From 803b1905400269f3b9e3a2190dea52641761619b Mon Sep 17 00:00:00 2001 From: Nathaniel van Diepen Date: Sun, 15 May 2022 14:32:25 -0600 Subject: [PATCH 01/28] Add crash reporting (#240) * Add sentry * Change build location * Also load libld * Get building * Setup sentry contexts * Fix #239 and add more debug code * Fix #239 * Add more debug code * Add more logging and switch to shared library * Switch to dynamic library * Update package * Fix release build * Fix building with/without feature flag * Start getting package build working * Begin adding liboxide * Begin moving shared code to a shared library * Merge device settings and dbus settings into liboxide * Move more into liboxide * Switch settings handling to liboxide * Change namespace * Add shared setings * Add shared settings access to rot * Add some documentation about telemetry * Flush out FAQ more * Fix machine-id handling * Remove unused include * Fix load order for running on device * Fix compile * Error if deprecated APIs are used in the shared lib * More build fixes * Switch to Type=dbus * Start flushing out sentry API more * Make adding settings easier and fix wifi loading * Fix dynamic property loading * Make telemetry opt-in and update docs * Fix settings file creation * Fix string comparison * Fix telemetry state handling * Always set initial transaction * Minor cleanup * Fix #243 * Fix #242 * Move signalhandler to liboxide * Move eventfilter into liboxide * Add opt-in screen * Start trying to fix group logic * Fix group handling * Move logic into library instead of header * Upgrade sentry library * Add more spans to sleep handling * Get spans working * Fix application recording * Switch to using EPFrameBuffer for save/recall * Add more analytics * Move application usage to it's own opt-in flag * Update package * Fix #244 * Fix #246 * Add analytics around update timings * Add application name as tag * Fix corrupt name * Add liboxide package * Update package * Upgrade sentry * Fix bug with qt type not being declared * Include all symlinks in install * Fix the right package * Fix crash on first launch * Default telemetry off on first launch --- Makefile | 131 +- applications/launcher/controller.cpp | 46 +- applications/launcher/controller.h | 39 +- applications/launcher/main.cpp | 11 +- applications/launcher/main.qml | 83 +- applications/launcher/notificationlist.h | 8 +- applications/launcher/oxide.pro | 47 +- .../launcher/widgets/CalendarMenu.qml | 5 +- .../launcher/widgets/NotificationsPopup.qml | 19 +- .../launcher/widgets/SettingsPopup.qml | 6 +- applications/launcher/widgets/WifiMenu.qml | 11 +- applications/lockscreen/controller.h | 61 +- applications/lockscreen/decay.pro | 38 +- applications/lockscreen/main.cpp | 7 +- applications/lockscreen/main.qml | 142 +- applications/process-manager/controller.h | 12 + applications/process-manager/erode.pro | 48 +- applications/process-manager/main.cpp | 8 +- applications/process-manager/main.qml | 66 +- applications/process-manager/tasklist.h | 2 +- applications/screenshot-tool/fret.pro | 26 +- applications/screenshot-tool/main.cpp | 7 +- applications/screenshot-viewer/anxiety.pro | 38 +- applications/screenshot-viewer/controller.h | 12 +- applications/screenshot-viewer/main.cpp | 8 +- applications/screenshot-viewer/main.qml | 28 +- applications/settings-manager/json.h | 130 + applications/settings-manager/main.cpp | 425 +- applications/settings-manager/rot.pro | 29 +- applications/settings-manager/slothandler.h | 80 + applications/system-service/apibase.h | 2 +- applications/system-service/application.cpp | 458 +- applications/system-service/application.h | 267 +- applications/system-service/appsapi.cpp | 178 +- applications/system-service/appsapi.h | 52 +- applications/system-service/bss.h | 2 +- applications/system-service/buttonhandler.cpp | 1 + applications/system-service/buttonhandler.h | 2 +- applications/system-service/dbusservice.h | 183 +- applications/system-service/dbussettings.h | 20 +- .../system-service/digitizerhandler.h | 2 +- applications/system-service/fifohandler.h | 3 +- applications/system-service/main.cpp | 6 +- applications/system-service/network.h | 2 +- applications/system-service/notification.h | 2 +- applications/system-service/notificationapi.h | 9 +- applications/system-service/powerapi.h | 76 +- applications/system-service/screenapi.h | 86 +- applications/system-service/screenshot.h | 3 +- applications/system-service/systemapi.cpp | 127 +- applications/system-service/systemapi.h | 134 +- applications/system-service/tarnish.pro | 49 +- applications/system-service/wifiapi.h | 243 +- applications/system-service/wlan.h | 3 +- applications/task-switcher/appitem.cpp | 2 +- applications/task-switcher/controller.h | 70 +- applications/task-switcher/corrupt.pro | 39 +- applications/task-switcher/main.cpp | 8 +- applications/task-switcher/main.qml | 23 +- assets/etc/systemd/system/tarnish.service | 2 + interfaces/application.xml | 1 + interfaces/wifiapi.xml | 1 + package | 42 +- shared/devicesettings.cpp | 127 - shared/devicesettings.h | 37 - shared/eventfilter.cpp | 143 - shared/eventfilter.h | 21 - shared/liboxide/.gitignore | 73 + shared/liboxide/eventfilter.cpp | 146 + shared/liboxide/eventfilter.h | 22 + shared/liboxide/liboxide.cpp | 563 + shared/liboxide/liboxide.h | 141 + shared/liboxide/liboxide.pro | 44 + shared/liboxide/liboxide_global.h | 12 + shared/liboxide/settingsfile.cpp | 131 + shared/liboxide/settingsfile.h | 121 + .../liboxide/signalhandler.cpp | 58 +- shared/liboxide/signalhandler.h | 36 + shared/sentry/.vscode/extensions.json | 9 + shared/sentry/.vscode/settings.json | 10 + shared/sentry/CHANGELOG.md | 620 + shared/sentry/CMakeLists.txt | 583 + shared/sentry/CONTRIBUTING.md | 141 + shared/sentry/LICENSE | 20 + shared/sentry/README.md | 320 + shared/sentry/examples/example.c | 264 + shared/sentry/external/CMakeLists.txt | 174 + shared/sentry/external/breakpad/.clang-format | 9 + .../.github/workflows/build-test-ci.yml | 73 + .../.github/workflows/close-pull-request.yml | 22 + .../breakpad/.github/workflows/coverity.yml | 43 + shared/sentry/external/breakpad/.gitignore | 92 + shared/sentry/external/breakpad/AUTHORS | 1 + shared/sentry/external/breakpad/ChangeLog | 0 shared/sentry/external/breakpad/DEPS | 84 + shared/sentry/external/breakpad/DIR_METADATA | 13 + shared/sentry/external/breakpad/INSTALL | 370 + shared/sentry/external/breakpad/LICENSE | 132 + shared/sentry/external/breakpad/Makefile.am | 1630 +++ shared/sentry/external/breakpad/Makefile.in | 10111 ++++++++++++++++ shared/sentry/external/breakpad/NEWS | 0 shared/sentry/external/breakpad/OWNERS | 13 + .../sentry/external/breakpad/README.ANDROID | 139 + shared/sentry/external/breakpad/README.md | 82 + shared/sentry/external/breakpad/aclocal.m4 | 1284 ++ .../breakpad/android/common-functions.sh | 372 + .../android/google_breakpad/Android.mk | 104 + .../external/breakpad/android/run-checks.sh | 555 + .../breakpad/android/sample_app/README | 32 + .../android/sample_app/jni/Android.mk | 44 + .../android/sample_app/jni/Application.mk | 32 + .../android/sample_app/jni/test_breakpad.cpp | 57 + .../external/breakpad/android/test-driver | 131 + .../external/breakpad/android/test-shell.sh | 131 + shared/sentry/external/breakpad/appveyor.yml | 42 + .../sentry/external/breakpad/autotools/ar-lib | 270 + .../external/breakpad/autotools/compile | 347 + .../external/breakpad/autotools/config.guess | 1465 +++ .../external/breakpad/autotools/config.sub | 1831 +++ .../external/breakpad/autotools/depcomp | 791 ++ .../external/breakpad/autotools/install-sh | 501 + .../external/breakpad/autotools/ltmain.sh | 8406 +++++++++++++ .../external/breakpad/autotools/missing | 215 + .../breakpad/autotools/root-test-driver | 3 + .../external/breakpad/autotools/test-driver | 148 + .../external/breakpad/breakpad-client.pc.in | 10 + .../sentry/external/breakpad/breakpad.pc.in | 10 + .../external/breakpad/codereview.settings | 3 + shared/sentry/external/breakpad/configure | 9260 ++++++++++++++ shared/sentry/external/breakpad/configure.ac | 272 + shared/sentry/external/breakpad/default.xml | 43 + shared/sentry/external/breakpad/docs/OWNERS | 1 + .../external/breakpad/docs/breakpad.png | Bin 0 -> 84153 bytes .../external/breakpad/docs/breakpad.svg | 1023 ++ .../external/breakpad/docs/client_design.md | 223 + .../breakpad/docs/contributing_to_breakpad.md | 35 + .../breakpad/docs/exception_handling.md | 128 + .../docs/getting_started_with_breakpad.md | 115 + .../breakpad/docs/linux_core_handler.md | 39 + .../breakpad/docs/linux_starter_guide.md | 110 + .../breakpad/docs/linux_system_calls.md | 47 + .../docs/mac_breakpad_starter_guide.md | 184 + .../breakpad/docs/mozilla_brown_bag_talk.md | 82 + .../breakpad/docs/processor_design.md | 231 + .../external/breakpad/docs/stack_walking.md | 144 + .../breakpad/docs/sym_upload_v2_protocol.md | 214 + .../external/breakpad/docs/symbol_files.md | 581 + .../docs/windows_client_integration.md | 70 + .../breakpad/m4/ax_append_compile_flags.m4 | 67 + .../external/breakpad/m4/ax_append_flag.m4 | 71 + .../breakpad/m4/ax_check_compile_flag.m4 | 74 + .../external/breakpad/m4/ax_check_define.m4 | 92 + .../breakpad/m4/ax_cxx_compile_stdcxx.m4 | 558 + .../sentry/external/breakpad/m4/ax_pthread.m4 | 283 + .../breakpad/m4/ax_require_defined.m4 | 37 + shared/sentry/external/breakpad/m4/libtool.m4 | 7377 +++++++++++ .../sentry/external/breakpad/m4/ltoptions.m4 | 368 + shared/sentry/external/breakpad/m4/ltsugar.m4 | 123 + .../sentry/external/breakpad/m4/ltversion.m4 | 23 + .../external/breakpad/m4/lt~obsolete.m4 | 92 + .../src/breakpad_googletest_includes.h | 57 + .../external/breakpad/src/build/all.gyp | 41 + .../external/breakpad/src/build/common.gypi | 1045 ++ .../breakpad/src/build/filename_rules.gypi | 57 + .../external/breakpad/src/build/gyp_breakpad | 67 + .../external/breakpad/src/build/testing.gyp | 90 + .../client/apple/Framework/BreakpadDefines.h | 73 + .../breakpad/src/client/ios/Breakpad.h | 260 + .../breakpad/src/client/ios/Breakpad.mm | 987 ++ .../ios/Breakpad.xcodeproj/project.pbxproj | 604 + .../src/client/ios/BreakpadController.h | 154 + .../src/client/ios/BreakpadController.mm | 374 + .../src/client/ios/Breakpad_Prefix.pch | 7 + .../client/ios/exception_handler_no_mach.cc | 265 + .../client/ios/exception_handler_no_mach.h | 179 + .../ios_exception_minidump_generator.h | 74 + .../ios_exception_minidump_generator.mm | 210 + .../linux/crash_generation/client_info.h | 53 + .../crash_generation_client.cc | 105 + .../crash_generation_client.h | 65 + .../crash_generation_server.cc | 333 + .../crash_generation_server.h | 135 + .../src/client/linux/data/linux-gate-amd.sym | 3 + .../client/linux/data/linux-gate-intel.sym | 3 + .../linux/dump_writer_common/mapping_info.h | 74 + .../dump_writer_common/raw_context_cpu.h | 53 + .../linux/dump_writer_common/thread_info.cc | 305 + .../linux/dump_writer_common/thread_info.h | 91 + .../dump_writer_common/ucontext_reader.cc | 259 + .../dump_writer_common/ucontext_reader.h | 65 + .../client/linux/handler/exception_handler.cc | 797 ++ .../client/linux/handler/exception_handler.h | 281 + .../handler/exception_handler_unittest.cc | 1287 ++ .../linux/handler/microdump_extra_info.h | 52 + .../linux/handler/minidump_descriptor.cc | 97 + .../linux/handler/minidump_descriptor.h | 199 + .../breakpad/src/client/linux/log/log.cc | 84 + .../breakpad/src/client/linux/log/log.h | 55 + .../microdump_writer/microdump_writer.cc | 664 + .../linux/microdump_writer/microdump_writer.h | 68 + .../microdump_writer_unittest.cc | 421 + .../client/linux/minidump_writer/cpu_set.h | 144 + .../linux/minidump_writer/cpu_set_unittest.cc | 164 + .../linux/minidump_writer/directory_reader.h | 106 + .../directory_reader_unittest.cc | 78 + .../linux/minidump_writer/line_reader.h | 131 + .../minidump_writer/line_reader_unittest.cc | 169 + .../minidump_writer/linux_core_dumper.cc | 312 + .../linux/minidump_writer/linux_core_dumper.h | 125 + .../linux_core_dumper_unittest.cc | 192 + .../linux/minidump_writer/linux_dumper.cc | 971 ++ .../linux/minidump_writer/linux_dumper.h | 326 + .../linux_dumper_unittest_helper.cc | 95 + .../minidump_writer/linux_ptrace_dumper.cc | 376 + .../minidump_writer/linux_ptrace_dumper.h | 101 + .../linux_ptrace_dumper_unittest.cc | 582 + .../linux/minidump_writer/minidump_writer.cc | 1509 +++ .../linux/minidump_writer/minidump_writer.h | 143 + .../minidump_writer_unittest.cc | 936 ++ .../minidump_writer_unittest_utils.cc | 66 + .../minidump_writer_unittest_utils.h | 49 + .../minidump_writer/proc_cpuinfo_reader.h | 130 + .../proc_cpuinfo_reader_unittest.cc | 199 + .../sender/google_crash_report_sender.cc | 105 + .../mac/Breakpad.xcodeproj/project.pbxproj | 2855 +++++ .../src/client/mac/Framework/Breakpad.h | 285 + .../src/client/mac/Framework/Breakpad.mm | 1043 ++ .../client/mac/Framework/Breakpad_Prefix.pch | 8 + .../src/client/mac/Framework/Info.plist | 26 + .../src/client/mac/Framework/OnDemandServer.h | 145 + .../client/mac/Framework/OnDemandServer.mm | 190 + .../src/client/mac/UnitTests-Info.plist | 20 + .../client/mac/crash_generation/ConfigFile.h | 83 + .../client/mac/crash_generation/ConfigFile.mm | 167 + .../client/mac/crash_generation/Inspector.h | 162 + .../client/mac/crash_generation/Inspector.mm | 362 + .../mac/crash_generation/InspectorMain.mm | 65 + .../client/mac/crash_generation/client_info.h | 47 + .../crash_generation_client.cc | 72 + .../crash_generation_client.h | 65 + .../crash_generation_server.cc | 166 + .../crash_generation_server.h | 150 + .../client/mac/handler/breakpad_nlist_64.cc | 399 + .../client/mac/handler/breakpad_nlist_64.h | 48 + .../src/client/mac/handler/dynamic_images.cc | 573 + .../src/client/mac/handler/dynamic_images.h | 320 + .../client/mac/handler/exception_handler.cc | 860 ++ .../client/mac/handler/exception_handler.h | 281 + .../src/client/mac/handler/mach_vm_compat.h | 48 + .../client/mac/handler/minidump_generator.cc | 1606 +++ .../client/mac/handler/minidump_generator.h | 253 + .../minidump_test.xcodeproj/project.pbxproj | 843 ++ .../mac/handler/minidump_tests32-Info.plist | 20 + .../mac/handler/minidump_tests64-Info.plist | 22 + .../mac/handler/obj-cTestCases-Info.plist | 20 + .../mac/handler/protected_memory_allocator.cc | 92 + .../mac/handler/protected_memory_allocator.h | 85 + .../handler/testcases/DynamicImagesTests.cc | 79 + .../handler/testcases/DynamicImagesTests.h | 52 + .../handler/testcases/breakpad_nlist_test.cc | 106 + .../handler/testcases/breakpad_nlist_test.h | 62 + .../client/mac/handler/testcases/dwarftests.h | 46 + .../mac/handler/testcases/dwarftests.mm | 60 + .../testcases/testdata/dump_syms_dwarf_data | Bin 0 -> 702795 bytes .../testdata/dump_syms_i386_breakpad.sym | 5300 ++++++++ .../src/client/mac/handler/ucontext_compat.h | 47 + .../src/client/mac/sender/Breakpad.xib | 1140 ++ .../sender/English.lproj/InfoPlist.strings | Bin 0 -> 156 bytes .../sender/English.lproj/Localizable.strings | Bin 0 -> 2428 bytes .../client/mac/sender/ReporterIcon.graffle | 2489 ++++ .../mac/sender/crash_report_sender-Info.plist | 32 + .../client/mac/sender/crash_report_sender.h | 117 + .../mac/sender/crash_report_sender.icns | Bin 0 -> 170816 bytes .../client/mac/sender/crash_report_sender.m | 755 ++ .../mac/sender/da.lproj/InfoPlist.strings | Bin 0 -> 156 bytes .../mac/sender/da.lproj/Localizable.strings | Bin 0 -> 2428 bytes .../mac/sender/de.lproj/InfoPlist.strings | Bin 0 -> 192 bytes .../mac/sender/de.lproj/Localizable.strings | Bin 0 -> 2746 bytes .../mac/sender/es.lproj/InfoPlist.strings | Bin 0 -> 184 bytes .../mac/sender/es.lproj/Localizable.strings | Bin 0 -> 2578 bytes .../mac/sender/fr.lproj/InfoPlist.strings | Bin 0 -> 156 bytes .../mac/sender/fr.lproj/Localizable.strings | Bin 0 -> 2694 bytes .../src/client/mac/sender/goArrow.png | Bin 0 -> 3591 bytes .../mac/sender/it.lproj/InfoPlist.strings | Bin 0 -> 156 bytes .../mac/sender/it.lproj/Localizable.strings | Bin 0 -> 2590 bytes .../mac/sender/ja.lproj/InfoPlist.strings | Bin 0 -> 156 bytes .../mac/sender/ja.lproj/Localizable.strings | Bin 0 -> 1792 bytes .../mac/sender/nl.lproj/InfoPlist.strings | Bin 0 -> 156 bytes .../mac/sender/nl.lproj/Localizable.strings | Bin 0 -> 2546 bytes .../mac/sender/no.lproj/InfoPlist.strings | Bin 0 -> 156 bytes .../mac/sender/no.lproj/Localizable.strings | Bin 0 -> 2484 bytes .../mac/sender/sl.lproj/InfoPlist.strings | Bin 0 -> 184 bytes .../mac/sender/sl.lproj/Localizable.strings | Bin 0 -> 2632 bytes .../mac/sender/sv.lproj/InfoPlist.strings | Bin 0 -> 156 bytes .../mac/sender/sv.lproj/Localizable.strings | Bin 0 -> 2588 bytes .../mac/sender/tr.lproj/InfoPlist.strings | Bin 0 -> 168 bytes .../mac/sender/tr.lproj/Localizable.strings | Bin 0 -> 2430 bytes .../breakpad/src/client/mac/sender/uploader.h | 106 + .../src/client/mac/sender/uploader.mm | 688 ++ .../src/client/mac/testapp/Controller.h | 65 + .../src/client/mac/testapp/Controller.m | 261 + .../testapp/English.lproj/InfoPlist.strings | Bin 0 -> 192 bytes .../mac/testapp/English.lproj/MainMenu.xib | 3748 ++++++ .../src/client/mac/testapp/Info.plist | 55 + .../src/client/mac/testapp/TestClass.h | 37 + .../src/client/mac/testapp/TestClass.mm | 95 + .../breakpad/src/client/mac/testapp/bomb.icns | Bin 0 -> 23659 bytes .../src/client/mac/testapp/crashInMain | Bin 0 -> 12588 bytes .../src/client/mac/testapp/crashduringload | Bin 0 -> 12588 bytes .../breakpad/src/client/mac/testapp/main.m | 34 + .../mac/tests/BreakpadFramework_Test.mm | 217 + .../mac/tests/crash_generation_server_test.cc | 398 + .../mac/tests/exception_handler_test.cc | 714 ++ .../mac/tests/minidump_generator_test.cc | 320 + .../tests/minidump_generator_test_helper.cc | 74 + .../client/mac/tests/spawn_child_process.h | 149 + .../src/client/mac/tests/testlogging.h | 9 + .../src/client/minidump_file_writer-inl.h | 97 + .../src/client/minidump_file_writer.cc | 350 + .../src/client/minidump_file_writer.h | 272 + .../client/minidump_file_writer_unittest.cc | 180 + .../src/client/solaris/handler/Makefile | 77 + .../solaris/handler/exception_handler.cc | 258 + .../solaris/handler/exception_handler.h | 201 + .../solaris/handler/exception_handler_test.cc | 119 + .../solaris/handler/minidump_generator.cc | 787 ++ .../solaris/handler/minidump_generator.h | 70 + .../client/solaris/handler/minidump_test.cc | 75 + .../src/client/solaris/handler/solaris_lwp.cc | 436 + .../src/client/solaris/handler/solaris_lwp.h | 160 + .../src/client/windows/breakpad_client.gyp | 66 + .../windows/common/auto_critical_section.h | 81 + .../src/client/windows/common/ipc_protocol.h | 181 + .../windows/crash_generation/ReadMe.txt | 58 + .../windows/crash_generation/client_info.cc | 223 + .../windows/crash_generation/client_info.h | 177 + .../crash_generation/crash_generation.gyp | 63 + .../crash_generation_client.cc | 405 + .../crash_generation_client.h | 182 + .../crash_generation_server.cc | 943 ++ .../crash_generation_server.h | 299 + .../crash_generation/minidump_generator.cc | 583 + .../crash_generation/minidump_generator.h | 203 + .../windows/handler/exception_handler.cc | 1082 ++ .../windows/handler/exception_handler.gyp | 47 + .../windows/handler/exception_handler.h | 524 + .../windows/sender/crash_report_sender.cc | 142 + .../windows/sender/crash_report_sender.gyp | 46 + .../windows/sender/crash_report_sender.h | 125 + .../crash_generation_app/abstract_class.cc | 53 + .../crash_generation_app/abstract_class.h | 57 + .../crash_generation_app.cc | 522 + .../crash_generation_app.gyp | 63 + .../crash_generation_app.h | 35 + .../crash_generation_app.ico | Bin 0 -> 23558 bytes .../crash_generation_app.rc | 144 + .../tests/crash_generation_app/resource.h | 73 + .../tests/crash_generation_app/small.ico | Bin 0 -> 23558 bytes .../client/windows/unittests/client_tests.gyp | 81 + .../unittests/crash_generation_server_test.cc | 303 + .../client/windows/unittests/dump_analysis.cc | 184 + .../client/windows/unittests/dump_analysis.h | 102 + .../unittests/exception_handler_death_test.cc | 587 + .../exception_handler_nesting_test.cc | 327 + .../unittests/exception_handler_test.cc | 503 + .../unittests/exception_handler_test.h | 61 + .../client/windows/unittests/minidump_test.cc | 332 + .../src/client/windows/unittests/testing.gyp | 90 + .../common/android/include/asm-mips/README.md | 9 + .../src/common/android/include/asm-mips/asm.h | 270 + .../android/include/asm-mips/fpregdef.h | 117 + .../common/android/include/asm-mips/regdef.h | 125 + .../breakpad/src/common/android/include/elf.h | 157 + .../src/common/android/include/link.h | 77 + .../src/common/android/include/stab.h | 100 + .../src/common/android/include/sys/procfs.h | 124 + .../src/common/android/include/sys/user.h | 69 + .../common/android/testing/include/wchar.h | 76 + .../src/common/android/testing/mkdtemp.h | 110 + .../common/android/testing/pthread_fixes.h | 94 + .../external/breakpad/src/common/basictypes.h | 58 + .../breakpad/src/common/byte_cursor.h | 266 + .../src/common/byte_cursor_unittest.cc | 776 ++ .../external/breakpad/src/common/common.gyp | 260 + .../breakpad/src/common/convert_UTF.cc | 591 + .../breakpad/src/common/convert_UTF.h | 159 + .../src/common/dwarf/bytereader-inl.h | 181 + .../breakpad/src/common/dwarf/bytereader.cc | 250 + .../breakpad/src/common/dwarf/bytereader.h | 320 + .../src/common/dwarf/bytereader_unittest.cc | 707 ++ .../src/common/dwarf/cfi_assembler.cc | 202 + .../breakpad/src/common/dwarf/cfi_assembler.h | 269 + .../src/common/dwarf/dwarf2diehandler.cc | 199 + .../src/common/dwarf/dwarf2diehandler.h | 368 + .../common/dwarf/dwarf2diehandler_unittest.cc | 528 + .../breakpad/src/common/dwarf/dwarf2enums.h | 744 ++ .../breakpad/src/common/dwarf/dwarf2reader.cc | 3423 ++++++ .../breakpad/src/common/dwarf/dwarf2reader.h | 1494 +++ .../common/dwarf/dwarf2reader_cfi_unittest.cc | 2584 ++++ .../common/dwarf/dwarf2reader_die_unittest.cc | 964 ++ .../dwarf/dwarf2reader_lineinfo_unittest.cc | 187 + .../dwarf2reader_splitfunctions_unittest.cc | 126 + .../common/dwarf/dwarf2reader_test_common.h | 164 + .../breakpad/src/common/dwarf/elf_reader.cc | 1274 ++ .../breakpad/src/common/dwarf/elf_reader.h | 167 + .../breakpad/src/common/dwarf/functioninfo.cc | 228 + .../breakpad/src/common/dwarf/functioninfo.h | 191 + .../src/common/dwarf/line_state_machine.h | 63 + .../breakpad/src/common/dwarf/types.h | 41 + .../src/common/dwarf_cfi_to_module.cc | 295 + .../breakpad/src/common/dwarf_cfi_to_module.h | 201 + .../common/dwarf_cfi_to_module_unittest.cc | 306 + .../breakpad/src/common/dwarf_cu_to_module.cc | 1515 +++ .../breakpad/src/common/dwarf_cu_to_module.h | 350 + .../src/common/dwarf_cu_to_module_unittest.cc | 1864 +++ .../src/common/dwarf_line_to_module.cc | 143 + .../src/common/dwarf_line_to_module.h | 191 + .../common/dwarf_line_to_module_unittest.cc | 410 + .../src/common/dwarf_range_list_handler.cc | 56 + .../src/common/dwarf_range_list_handler.h | 72 + .../external/breakpad/src/common/language.cc | 220 + .../external/breakpad/src/common/language.h | 105 + .../src/common/linux/breakpad_getcontext.S | 552 + .../src/common/linux/breakpad_getcontext.h | 56 + .../linux/breakpad_getcontext_unittest.cc | 194 + .../breakpad/src/common/linux/crc32.cc | 70 + .../breakpad/src/common/linux/crc32.h | 53 + .../breakpad/src/common/linux/dump_symbols.cc | 1148 ++ .../breakpad/src/common/linux/dump_symbols.h | 93 + .../src/common/linux/dump_symbols_unittest.cc | 208 + .../breakpad/src/common/linux/eintr_wrapper.h | 58 + .../src/common/linux/elf_core_dump.cc | 203 + .../breakpad/src/common/linux/elf_core_dump.h | 157 + .../common/linux/elf_core_dump_unittest.cc | 265 + .../src/common/linux/elf_gnu_compat.h | 51 + .../src/common/linux/elf_symbols_to_module.cc | 178 + .../src/common/linux/elf_symbols_to_module.h | 58 + .../linux/elf_symbols_to_module_unittest.cc | 370 + .../breakpad/src/common/linux/elfutils-inl.h | 74 + .../breakpad/src/common/linux/elfutils.cc | 243 + .../breakpad/src/common/linux/elfutils.h | 135 + .../breakpad/src/common/linux/file_id.cc | 203 + .../breakpad/src/common/linux/file_id.h | 90 + .../src/common/linux/file_id_unittest.cc | 383 + .../common/linux/google_crashdump_uploader.cc | 207 + .../common/linux/google_crashdump_uploader.h | 107 + .../linux/google_crashdump_uploader_test.cc | 170 + .../breakpad/src/common/linux/guid_creator.cc | 189 + .../breakpad/src/common/linux/guid_creator.h | 48 + .../breakpad/src/common/linux/http_upload.cc | 230 + .../breakpad/src/common/linux/http_upload.h | 90 + .../breakpad/src/common/linux/ignore_ret.h | 40 + .../src/common/linux/libcurl_wrapper.cc | 338 + .../src/common/linux/libcurl_wrapper.h | 119 + .../src/common/linux/linux_libc_support.cc | 237 + .../src/common/linux/linux_libc_support.h | 96 + .../linux/linux_libc_support_unittest.cc | 213 + .../src/common/linux/memory_mapped_file.cc | 108 + .../src/common/linux/memory_mapped_file.h | 87 + .../linux/memory_mapped_file_unittest.cc | 208 + .../src/common/linux/safe_readlink.cc | 53 + .../breakpad/src/common/linux/safe_readlink.h | 65 + .../common/linux/safe_readlink_unittest.cc | 89 + .../common/linux/symbol_collector_client.cc | 195 + .../common/linux/symbol_collector_client.h | 88 + .../src/common/linux/symbol_upload.cc | 284 + .../breakpad/src/common/linux/symbol_upload.h | 76 + .../breakpad/src/common/linux/synth_elf.cc | 263 + .../breakpad/src/common/linux/synth_elf.h | 197 + .../src/common/linux/synth_elf_unittest.cc | 413 + .../src/common/linux/tests/auto_testfile.h | 124 + .../src/common/linux/tests/crash_generator.cc | 329 + .../src/common/linux/tests/crash_generator.h | 117 + .../src/common/linux/ucontext_constants.h | 153 + .../src/common/long_string_dictionary.cc | 178 + .../src/common/long_string_dictionary.h | 87 + .../common/long_string_dictionary_unittest.cc | 301 + .../breakpad/src/common/mac/Breakpad.xcconfig | 52 + .../src/common/mac/BreakpadDebug.xcconfig | 32 + .../src/common/mac/BreakpadRelease.xcconfig | 34 + .../breakpad/src/common/mac/GTMDefines.h | 456 + .../breakpad/src/common/mac/GTMLogger.h | 504 + .../breakpad/src/common/mac/GTMLogger.m | 611 + .../breakpad/src/common/mac/HTTPGetRequest.h | 42 + .../breakpad/src/common/mac/HTTPGetRequest.m | 39 + .../src/common/mac/HTTPMultipartUpload.h | 63 + .../src/common/mac/HTTPMultipartUpload.m | 170 + .../breakpad/src/common/mac/HTTPPutRequest.h | 51 + .../breakpad/src/common/mac/HTTPPutRequest.m | 56 + .../breakpad/src/common/mac/HTTPRequest.h | 73 + .../breakpad/src/common/mac/HTTPRequest.m | 268 + .../src/common/mac/HTTPSimplePostRequest.h | 57 + .../src/common/mac/HTTPSimplePostRequest.m | 69 + .../breakpad/src/common/mac/MachIPC.h | 301 + .../breakpad/src/common/mac/MachIPC.mm | 306 + .../src/common/mac/SymbolCollectorClient.h | 104 + .../src/common/mac/SymbolCollectorClient.m | 272 + .../breakpad/src/common/mac/arch_utilities.cc | 269 + .../breakpad/src/common/mac/arch_utilities.h | 47 + .../src/common/mac/bootstrap_compat.cc | 42 + .../src/common/mac/bootstrap_compat.h | 54 + .../breakpad/src/common/mac/byteswap.h | 73 + .../breakpad/src/common/mac/dump_syms.cc | 699 ++ .../breakpad/src/common/mac/dump_syms.h | 207 + .../breakpad/src/common/mac/encoding_util.h | 41 + .../breakpad/src/common/mac/encoding_util.m | 47 + .../breakpad/src/common/mac/file_id.cc | 95 + .../breakpad/src/common/mac/file_id.h | 92 + .../src/common/mac/launch_reporter.cc | 84 + .../breakpad/src/common/mac/launch_reporter.h | 43 + .../breakpad/src/common/mac/macho_id.cc | 237 + .../breakpad/src/common/mac/macho_id.h | 107 + .../breakpad/src/common/mac/macho_reader.cc | 567 + .../breakpad/src/common/mac/macho_reader.h | 464 + .../src/common/mac/macho_reader_unittest.cc | 1947 +++ .../src/common/mac/macho_utilities.cc | 155 + .../breakpad/src/common/mac/macho_utilities.h | 95 + .../breakpad/src/common/mac/macho_walker.cc | 268 + .../breakpad/src/common/mac/macho_walker.h | 119 + .../breakpad/src/common/mac/minidump_upload.m | 135 + .../src/common/mac/scoped_task_suspend-inl.h | 56 + .../src/common/mac/string_utilities.cc | 84 + .../src/common/mac/string_utilities.h | 52 + .../breakpad/src/common/mac/super_fat_arch.h | 88 + .../src/common/mac/testing/GTMSenTestCase.h | 1110 ++ .../src/common/mac/testing/GTMSenTestCase.m | 428 + .../external/breakpad/src/common/macros.h | 45 + .../external/breakpad/src/common/md5.cc | 251 + .../sentry/external/breakpad/src/common/md5.h | 28 + .../breakpad/src/common/memory_allocator.h | 252 + .../src/common/memory_allocator_unittest.cc | 124 + .../breakpad/src/common/memory_range.h | 145 + .../src/common/memory_range_unittest.cc | 193 + .../src/common/minidump_type_helper.h | 56 + .../external/breakpad/src/common/module.cc | 464 + .../external/breakpad/src/common/module.h | 479 + .../breakpad/src/common/module_unittest.cc | 623 + .../breakpad/src/common/path_helper.cc | 55 + .../breakpad/src/common/path_helper.h | 44 + .../external/breakpad/src/common/scoped_ptr.h | 404 + .../src/common/simple_string_dictionary.cc | 45 + .../src/common/simple_string_dictionary.h | 279 + .../simple_string_dictionary_unittest.cc | 339 + .../src/common/solaris/dump_symbols.cc | 680 ++ .../src/common/solaris/dump_symbols.h | 49 + .../breakpad/src/common/solaris/file_id.cc | 200 + .../breakpad/src/common/solaris/file_id.h | 68 + .../src/common/solaris/guid_creator.cc | 84 + .../src/common/solaris/guid_creator.h | 50 + .../src/common/solaris/message_output.h | 54 + .../breakpad/src/common/stabs_reader.cc | 315 + .../breakpad/src/common/stabs_reader.h | 325 + .../src/common/stabs_reader_unittest.cc | 611 + .../breakpad/src/common/stabs_to_module.cc | 201 + .../breakpad/src/common/stabs_to_module.h | 143 + .../src/common/stabs_to_module_unittest.cc | 258 + .../breakpad/src/common/stdio_wrapper.h | 43 + .../breakpad/src/common/string_conversion.cc | 155 + .../breakpad/src/common/string_conversion.h | 68 + .../src/common/string_conversion_unittest.cc | 64 + .../breakpad/src/common/string_view.h | 114 + .../breakpad/src/common/symbol_data.h | 58 + .../breakpad/src/common/test_assembler.cc | 359 + .../breakpad/src/common/test_assembler.h | 484 + .../src/common/test_assembler_unittest.cc | 1662 +++ .../src/common/testdata/func-line-pairing.h | 676 ++ .../breakpad/src/common/tests/auto_tempdir.h | 100 + .../breakpad/src/common/tests/file_utils.cc | 157 + .../breakpad/src/common/tests/file_utils.h | 55 + .../external/breakpad/src/common/unordered.h | 56 + .../breakpad/src/common/using_std_string.h | 66 + .../src/common/windows/common_windows.gyp | 112 + .../breakpad/src/common/windows/dia_util.cc | 92 + .../breakpad/src/common/windows/dia_util.h | 64 + .../src/common/windows/guid_string.cc | 76 + .../breakpad/src/common/windows/guid_string.h | 58 + .../src/common/windows/http_upload.cc | 510 + .../breakpad/src/common/windows/http_upload.h | 126 + .../breakpad/src/common/windows/module_info.h | 75 + .../breakpad/src/common/windows/omap.cc | 716 ++ .../breakpad/src/common/windows/omap.h | 72 + .../src/common/windows/omap_internal.h | 140 + .../src/common/windows/omap_unittest.cc | 329 + .../common/windows/pdb_source_line_writer.cc | 1509 +++ .../common/windows/pdb_source_line_writer.h | 357 + .../common/windows/pe_source_line_writer.cc | 77 + .../common/windows/pe_source_line_writer.h | 69 + .../breakpad/src/common/windows/pe_util.cc | 412 + .../breakpad/src/common/windows/pe_util.h | 78 + .../src/common/windows/string_utils-inl.h | 142 + .../src/common/windows/string_utils.cc | 133 + .../common/windows/symbol_collector_client.cc | 159 + .../common/windows/symbol_collector_client.h | 92 + .../sentry/external/breakpad/src/config.h.in | 100 + .../google_breakpad/common/breakpad_types.h | 68 + .../common/minidump_cpu_amd64.h | 235 + .../google_breakpad/common/minidump_cpu_arm.h | 151 + .../common/minidump_cpu_arm64.h | 192 + .../common/minidump_cpu_mips.h | 176 + .../google_breakpad/common/minidump_cpu_ppc.h | 168 + .../common/minidump_cpu_ppc64.h | 134 + .../common/minidump_cpu_sparc.h | 163 + .../google_breakpad/common/minidump_cpu_x86.h | 174 + .../common/minidump_exception_fuchsia.h | 58 + .../common/minidump_exception_linux.h | 170 + .../common/minidump_exception_mac.h | 211 + .../common/minidump_exception_ps3.h | 67 + .../common/minidump_exception_solaris.h | 94 + .../common/minidump_exception_win32.h | 2269 ++++ .../google_breakpad/common/minidump_format.h | 1102 ++ .../google_breakpad/common/minidump_size.h | 113 + .../processor/basic_source_line_resolver.h | 184 + .../google_breakpad/processor/call_stack.h | 87 + .../google_breakpad/processor/code_module.h | 104 + .../google_breakpad/processor/code_modules.h | 108 + .../google_breakpad/processor/dump_context.h | 116 + .../google_breakpad/processor/dump_object.h | 53 + .../processor/exception_record.h | 124 + .../processor/exploitability.h | 82 + .../processor/fast_source_line_resolver.h | 102 + .../google_breakpad/processor/memory_region.h | 79 + .../src/google_breakpad/processor/microdump.h | 135 + .../processor/microdump_processor.h | 64 + .../src/google_breakpad/processor/minidump.h | 1349 +++ .../processor/minidump_processor.h | 147 + .../processor/proc_maps_linux.h | 60 + .../processor/process_result.h | 66 + .../google_breakpad/processor/process_state.h | 208 + .../processor/source_line_resolver_base.h | 133 + .../source_line_resolver_interface.h | 124 + .../google_breakpad/processor/stack_frame.h | 159 + .../processor/stack_frame_cpu.h | 408 + .../processor/stack_frame_symbolizer.h | 114 + .../google_breakpad/processor/stackwalker.h | 261 + .../processor/symbol_supplier.h | 99 + .../google_breakpad/processor/system_info.h | 106 + .../breakpad/src/third_party/curl/COPYING | 22 + .../breakpad/src/third_party/curl/curl.h | 1936 +++ .../breakpad/src/third_party/curl/curlbuild.h | 203 + .../breakpad/src/third_party/curl/curlrules.h | 249 + .../breakpad/src/third_party/curl/curlver.h | 70 + .../breakpad/src/third_party/curl/easy.h | 103 + .../breakpad/src/third_party/curl/mprintf.h | 82 + .../breakpad/src/third_party/curl/multi.h | 346 + .../src/third_party/curl/stdcheaders.h | 34 + .../src/third_party/curl/typecheck-gcc.h | 551 + .../breakpad/src/third_party/curl/types.h | 1 + .../src/third_party/libdisasm/LICENSE | 137 + .../src/third_party/libdisasm/Makefile.am | 43 + .../src/third_party/libdisasm/README.breakpad | 9 + .../breakpad/src/third_party/libdisasm/TODO | 43 + .../src/third_party/libdisasm/ia32_implicit.c | 422 + .../src/third_party/libdisasm/ia32_implicit.h | 13 + .../src/third_party/libdisasm/ia32_insn.c | 623 + .../src/third_party/libdisasm/ia32_insn.h | 506 + .../third_party/libdisasm/ia32_invariant.c | 313 + .../third_party/libdisasm/ia32_invariant.h | 11 + .../src/third_party/libdisasm/ia32_modrm.c | 310 + .../src/third_party/libdisasm/ia32_modrm.h | 13 + .../libdisasm/ia32_opcode_tables.c | 2939 +++++ .../libdisasm/ia32_opcode_tables.h | 57 + .../src/third_party/libdisasm/ia32_operand.c | 425 + .../src/third_party/libdisasm/ia32_operand.h | 11 + .../src/third_party/libdisasm/ia32_reg.c | 234 + .../src/third_party/libdisasm/ia32_reg.h | 41 + .../src/third_party/libdisasm/ia32_settings.c | 13 + .../src/third_party/libdisasm/ia32_settings.h | 27 + .../src/third_party/libdisasm/libdis.h | 832 ++ .../src/third_party/libdisasm/libdisasm.gyp | 67 + .../src/third_party/libdisasm/qword.h | 14 + .../src/third_party/libdisasm/swig/Makefile | 70 + .../src/third_party/libdisasm/swig/README | 128 + .../third_party/libdisasm/swig/libdisasm.i | 508 + .../libdisasm/swig/libdisasm_oop.i | 1114 ++ .../libdisasm/swig/perl/Makefile-swig | 65 + .../libdisasm/swig/perl/Makefile.PL | 7 + .../libdisasm/swig/python/Makefile-swig | 64 + .../libdisasm/swig/ruby/Makefile-swig | 68 + .../libdisasm/swig/ruby/extconf.rb | 4 + .../libdisasm/swig/tcl/Makefile-swig | 63 + .../src/third_party/libdisasm/x86_disasm.c | 210 + .../src/third_party/libdisasm/x86_format.c | 1430 +++ .../src/third_party/libdisasm/x86_imm.c | 70 + .../src/third_party/libdisasm/x86_imm.h | 18 + .../src/third_party/libdisasm/x86_insn.c | 182 + .../src/third_party/libdisasm/x86_misc.c | 71 + .../third_party/libdisasm/x86_operand_list.c | 191 + .../third_party/libdisasm/x86_operand_list.h | 8 + .../linux/include/gflags/gflags_completions.h | 121 + .../src/third_party/mac_headers/README | 2 + .../mac_headers/architecture/byte_order.h | 45 + .../src/third_party/mac_headers/i386/_types.h | 34 + .../src/third_party/mac_headers/mach-o/arch.h | 105 + .../src/third_party/mac_headers/mach-o/fat.h | 64 + .../third_party/mac_headers/mach-o/loader.h | 1402 +++ .../third_party/mac_headers/mach-o/nlist.h | 312 + .../third_party/mac_headers/mach/boolean.h | 88 + .../mac_headers/mach/i386/boolean.h | 74 + .../mac_headers/mach/i386/vm_param.h | 157 + .../mac_headers/mach/i386/vm_types.h | 140 + .../third_party/mac_headers/mach/machine.h | 347 + .../mac_headers/mach/machine/boolean.h | 40 + .../mac_headers/mach/machine/thread_state.h | 9 + .../mac_headers/mach/machine/thread_status.h | 1 + .../mac_headers/mach/machine/vm_types.h | 40 + .../mac_headers/mach/thread_status.h | 94 + .../third_party/mac_headers/mach/vm_prot.h | 140 + .../breakpad/src/third_party/musl/COPYRIGHT | 163 + .../breakpad/src/third_party/musl/README | 23 + .../src/third_party/musl/README.breakpad | 3 + .../breakpad/src/third_party/musl/VERSION | 1 + .../src/third_party/musl/include/elf.h | 2827 +++++ shared/sentry/external/crashpad/.clang-format | 19 + .../sentry/external/crashpad/.gitattributes | 59 + .../crashpad/.github/workflows/build.yml | 45 + shared/sentry/external/crashpad/.gitignore | 40 + shared/sentry/external/crashpad/.gitmodules | 9 + shared/sentry/external/crashpad/.gn | 16 + shared/sentry/external/crashpad/.style.yapf | 16 + shared/sentry/external/crashpad/.vpython | 32 + shared/sentry/external/crashpad/.vpython3 | 32 + shared/sentry/external/crashpad/AUTHORS | 14 + shared/sentry/external/crashpad/BUILD.gn | 187 + .../sentry/external/crashpad/CMakeLists.txt | 137 + shared/sentry/external/crashpad/CONTRIBUTORS | 15 + shared/sentry/external/crashpad/DEPS | 180 + shared/sentry/external/crashpad/LICENSE | 202 + .../external/crashpad/README.getsentry.md | 71 + shared/sentry/external/crashpad/README.md | 51 + .../sentry/external/crashpad/build/BUILD.gn | 68 + .../external/crashpad/build/BUILDCONFIG.gn | 99 + .../crashpad/build/crashpad_buildconfig.gni | 160 + .../crashpad/build/crashpad_fuzzer_test.gni | 58 + .../crashpad/build/install_linux_sysroot.py | 74 + .../external/crashpad/build/ios/Default.png | Bin 0 -> 1707 bytes .../crashpad/build/ios/Unittest-Info.plist | 10 + .../build/ios/convert_gn_xcodeproj.py | 549 + .../crashpad/build/ios/setup_ios_gn.config | 39 + .../crashpad/build/ios/setup_ios_gn.py | 405 + .../build/ios/xcodescheme-testable.template | 10 + .../crashpad/build/ios/xcodescheme.template | 80 + .../external/crashpad/build/run_tests.py | 480 + .../sentry/external/crashpad/build/test.gni | 49 + .../sentry/external/crashpad/client/BUILD.gn | 208 + .../external/crashpad/client/CMakeLists.txt | 120 + .../external/crashpad/client/annotation.cc | 43 + .../external/crashpad/client/annotation.h | 272 + .../crashpad/client/annotation_list.cc | 97 + .../crashpad/client/annotation_list.h | 112 + .../crashpad/client/annotation_list_test.cc | 183 + .../crashpad/client/annotation_test.cc | 135 + .../crashpad/client/client_argv_handling.cc | 80 + .../crashpad/client/client_argv_handling.h | 52 + .../crashpad/client/crash_report_database.cc | 215 + .../crashpad/client/crash_report_database.h | 457 + .../client/crash_report_database_generic.cc | 931 ++ .../client/crash_report_database_mac.mm | 876 ++ .../client/crash_report_database_test.cc | 905 ++ .../client/crash_report_database_win.cc | 1041 ++ .../crashpad/client/crashpad_client.h | 781 ++ .../client/crashpad_client_fuchsia.cc | 105 + .../crashpad/client/crashpad_client_ios.cc | 475 + .../client/crashpad_client_ios_test.mm | 154 + .../crashpad/client/crashpad_client_linux.cc | 747 ++ .../client/crashpad_client_linux_test.cc | 690 ++ .../crashpad/client/crashpad_client_mac.cc | 551 + .../crashpad/client/crashpad_client_win.cc | 1097 ++ .../client/crashpad_client_win_test.cc | 197 + .../external/crashpad/client/crashpad_info.cc | 138 + .../external/crashpad/client/crashpad_info.h | 279 + .../crashpad/client/crashpad_info_note.S | 68 + .../client/ios_handler/exception_processor.h | 89 + .../client/ios_handler/exception_processor.mm | 614 + .../ios_handler/exception_processor_test.mm | 47 + .../client/ios_handler/in_process_handler.cc | 474 + .../client/ios_handler/in_process_handler.h | 258 + .../ios_handler/in_process_handler_test.cc | 148 + .../in_process_intermediate_dump_handler.cc | 1285 ++ .../in_process_intermediate_dump_handler.h | 155 + ..._process_intermediate_dump_handler_test.cc | 252 + ...rmediate_dumps_and_crash_reports_thread.cc | 148 + ...ermediate_dumps_and_crash_reports_thread.h | 106 + .../crashpad/client/prune_crash_reports.cc | 131 + .../crashpad/client/prune_crash_reports.h | 156 + .../client/prune_crash_reports_test.cc | 263 + .../crashpad/client/pthread_create_linux.cc | 72 + .../external/crashpad/client/settings.cc | 407 + .../external/crashpad/client/settings.h | 252 + .../external/crashpad/client/settings_test.cc | 183 + .../client/simple_address_range_bag.h | 200 + .../client/simple_address_range_bag_test.cc | 108 + .../client/simple_string_dictionary.h | 288 + .../client/simple_string_dictionary_test.cc | 282 + .../external/crashpad/client/simulate_crash.h | 30 + .../crashpad/client/simulate_crash_ios.h | 58 + .../crashpad/client/simulate_crash_linux.h | 31 + .../crashpad/client/simulate_crash_mac.cc | 258 + .../crashpad/client/simulate_crash_mac.h | 60 + .../client/simulate_crash_mac_test.cc | 414 + .../crashpad/client/simulate_crash_win.h | 34 + .../external/crashpad/codereview.settings | 19 + .../sentry/external/crashpad/compat/BUILD.gn | 156 + .../external/crashpad/compat/CMakeLists.txt | 128 + .../crashpad/compat/android/dlfcn_internal.cc | 166 + .../crashpad/compat/android/dlfcn_internal.h | 35 + .../external/crashpad/compat/android/elf.h | 58 + .../crashpad/compat/android/linux/elf.h | 38 + .../crashpad/compat/android/linux/prctl.h | 25 + .../crashpad/compat/android/linux/ptrace.h | 25 + .../external/crashpad/compat/android/sched.h | 30 + .../crashpad/compat/android/sys/epoll.cc | 37 + .../crashpad/compat/android/sys/epoll.h | 50 + .../crashpad/compat/android/sys/mman.h | 43 + .../crashpad/compat/android/sys/mman_mmap.cc | 105 + .../crashpad/compat/android/sys/syscall.h | 42 + .../crashpad/compat/android/sys/user.h | 23 + .../crashpad/compat/ios/mach/exc.defs | 20 + .../crashpad/compat/ios/mach/mach_exc.defs | 20 + .../crashpad/compat/ios/mach/mach_types.defs | 20 + .../ios/mach/machine/machine_types.defs | 20 + .../crashpad/compat/ios/mach/std_types.defs | 20 + .../external/crashpad/compat/linux/signal.h | 65 + .../external/crashpad/compat/linux/sys/mman.h | 44 + .../compat/linux/sys/mman_memfd_create.cc | 36 + .../crashpad/compat/linux/sys/ptrace.h | 52 + .../external/crashpad/compat/linux/sys/user.h | 33 + .../crashpad/compat/mac/Availability.h | 28 + .../compat/mac/AvailabilityVersions.h | 96 + .../crashpad/compat/mac/kern/exc_resource.h | 76 + .../crashpad/compat/mac/mach-o/loader.h | 32 + .../compat/mac/mach/i386/thread_state.h | 28 + .../external/crashpad/compat/mac/mach/mach.h | 120 + .../crashpad/compat/mac/sys/resource.h | 26 + .../external/crashpad/compat/mingw/dbghelp.h | 334 + .../external/crashpad/compat/mingw/winnt.h | 60 + .../external/crashpad/compat/non_elf/elf.h | 20 + .../crashpad/compat/non_mac/mach-o/loader.h | 20 + .../crashpad/compat/non_mac/mach/mach.h | 44 + .../crashpad/compat/non_mac/mach/machine.h | 21 + .../crashpad/compat/non_mac/mach/vm_prot.h | 20 + .../crashpad/compat/non_win/dbghelp.h | 1105 ++ .../crashpad/compat/non_win/minwinbase.h | 49 + .../crashpad/compat/non_win/timezoneapi.h | 60 + .../crashpad/compat/non_win/verrsrc.h | 184 + .../crashpad/compat/non_win/windows.h | 17 + .../external/crashpad/compat/non_win/winnt.h | 250 + .../external/crashpad/compat/win/getopt.h | 20 + .../external/crashpad/compat/win/strings.cc | 25 + .../external/crashpad/compat/win/strings.h | 28 + .../external/crashpad/compat/win/sys/time.h | 20 + .../external/crashpad/compat/win/sys/types.h | 23 + .../external/crashpad/compat/win/time.cc | 40 + .../external/crashpad/compat/win/time.h | 37 + .../external/crashpad/compat/win/winbase.h | 27 + .../external/crashpad/compat/win/winnt.h | 60 + .../external/crashpad/compat/win/winternl.h | 25 + .../crashpad/crashpad-config.cmake.in | 10 + .../sentry/external/crashpad/doc/.gitignore | 1 + .../external/crashpad/doc/appengine/README | 51 + .../doc/appengine/src/crashpad-home/app.yaml | 20 + .../doc/appengine/src/crashpad-home/main.go | 161 + .../external/crashpad/doc/developing.md | 327 + .../sentry/external/crashpad/doc/favicon.ico | Bin 0 -> 8348 bytes .../crashpad/doc/ios_overview_design.md | 177 + .../sentry/external/crashpad/doc/layering.png | Bin 0 -> 23460 bytes shared/sentry/external/crashpad/doc/man.md | 34 + .../sentry/external/crashpad/doc/overview.png | Bin 0 -> 26517 bytes .../external/crashpad/doc/overview_design.md | 567 + shared/sentry/external/crashpad/doc/status.md | 44 + .../external/crashpad/doc/support/compat.sh | 37 + .../crashpad/doc/support/crashpad.doxy | 2510 ++++ .../crashpad/doc/support/crashpad.doxy.h | 34 + .../crashpad/doc/support/crashpad_doxygen.css | 60 + .../external/crashpad/doc/support/generate.sh | 41 + .../crashpad/doc/support/generate_doxygen.py | 48 + .../crashpad/doc/support/generate_git.sh | 85 + shared/sentry/external/crashpad/example.cpp | 81 + .../sentry/external/crashpad/handler/BUILD.gn | 356 + .../external/crashpad/handler/CMakeLists.txt | 124 + .../handler/crash_report_upload_thread.cc | 403 + .../handler/crash_report_upload_thread.h | 222 + .../crashpad/handler/crashpad_handler.md | 349 + .../crashpad/handler/crashpad_handler_main.cc | 29 + .../crashpad/handler/crashpad_handler_test.cc | 148 + .../crashpad_handler_test_extended_handler.cc | 70 + .../external/crashpad/handler/handler_main.cc | 1190 ++ .../external/crashpad/handler/handler_main.h | 50 + .../handler/linux/capture_snapshot.cc | 118 + .../crashpad/handler/linux/capture_snapshot.h | 67 + .../linux/crash_report_exception_handler.cc | 306 + .../linux/crash_report_exception_handler.h | 123 + .../cros_crash_report_exception_handler.cc | 289 + .../cros_crash_report_exception_handler.h | 103 + .../handler/linux/exception_handler_server.cc | 493 + .../handler/linux/exception_handler_server.h | 197 + .../linux/exception_handler_server_test.cc | 383 + .../handler/linux/handler_trampoline.cc | 47 + .../mac/crash_report_exception_handler.cc | 268 + .../mac/crash_report_exception_handler.h | 100 + .../handler/mac/exception_handler_server.cc | 244 + .../handler/mac/exception_handler_server.h | 83 + .../handler/mac/file_limit_annotation.cc | 132 + .../handler/mac/file_limit_annotation.h | 38 + .../sentry/external/crashpad/handler/main.cc | 53 + .../handler/minidump_to_upload_parameters.cc | 87 + .../handler/minidump_to_upload_parameters.h | 61 + .../minidump_to_upload_parameters_test.cc | 99 + .../handler/prune_crash_reports_thread.cc | 45 + .../handler/prune_crash_reports_thread.h | 80 + .../handler/user_stream_data_source.cc | 43 + .../handler/user_stream_data_source.h | 68 + .../crashpad/handler/win/.gitattributes | 21 + .../handler/win/crash_other_program.cc | 141 + .../win/crash_report_exception_handler.cc | 154 + .../win/crash_report_exception_handler.h | 91 + .../crashpad/handler/win/crashy_signal.cc | 89 + .../handler/win/crashy_test_program.cc | 265 + .../handler/win/crashy_test_z7_loader.cc | 68 + .../fake_handler_that_crashes_at_startup.cc | 20 + .../crashpad/handler/win/hanging_program.cc | 137 + .../crashpad/handler/win/loader_lock_dll.cc | 118 + .../win/self_destroying_test_program.cc | 91 + .../external/crashpad/handler/win/z7_test.cpp | 32 + .../external/crashpad/handler/win/z7_test.dll | Bin 0 -> 168264 bytes .../crashpad/infra/config/PRESUBMIT.py | 28 + .../infra/config/generated/commit-queue.cfg | 80 + .../infra/config/generated/cr-buildbucket.cfg | 984 ++ .../infra/config/generated/luci-logdog.cfg | 9 + .../infra/config/generated/luci-milo.cfg | 131 + .../infra/config/generated/luci-scheduler.cfg | 179 + .../infra/config/generated/project.cfg | 15 + .../infra/config/generated/realms.cfg | 65 + .../external/crashpad/infra/config/main.star | 265 + .../external/crashpad/libunwind/.clang-format | 2 + .../crashpad/libunwind/CMakeLists.txt | 369 + .../external/crashpad/libunwind/LICENSE.TXT | 311 + .../cmake/Modules/HandleCompilerRT.cmake | 64 + .../cmake/Modules/HandleLibunwindFlags.cmake | 283 + .../crashpad/libunwind/cmake/config-ix.cmake | 105 + .../libunwind/docs/BuildingLibunwind.rst | 168 + .../crashpad/libunwind/docs/CMakeLists.txt | 7 + .../crashpad/libunwind/docs/README.txt | 13 + .../external/crashpad/libunwind/docs/conf.py | 252 + .../crashpad/libunwind/docs/index.rst | 104 + .../libunwind/include/__libunwind_config.h | 176 + .../crashpad/libunwind/include/libunwind.h | 1186 ++ .../include/mach-o/compact_unwind_encoding.h | 477 + .../crashpad/libunwind/include/unwind.h | 207 + .../libunwind/include/unwind_arm_ehabi.h | 169 + .../libunwind/include/unwind_itanium.h | 76 + .../crashpad/libunwind/src/AddressSpace.hpp | 1072 ++ .../crashpad/libunwind/src/CMakeLists.txt | 211 + .../libunwind/src/CompactUnwinder.hpp | 787 ++ .../libunwind/src/DwarfInstructions.hpp | 838 ++ .../crashpad/libunwind/src/DwarfParser.hpp | 813 ++ .../crashpad/libunwind/src/EHHeaderParser.hpp | 169 + .../libunwind/src/FrameHeaderCache.hpp | 149 + .../crashpad/libunwind/src/RWMutex.hpp | 114 + .../crashpad/libunwind/src/Registers.hpp | 4509 +++++++ .../crashpad/libunwind/src/Unwind-EHABI.cpp | 1141 ++ .../crashpad/libunwind/src/Unwind-EHABI.h | 50 + .../crashpad/libunwind/src/Unwind-seh.cpp | 491 + .../crashpad/libunwind/src/Unwind-sjlj.c | 528 + .../crashpad/libunwind/src/UnwindCursor.hpp | 2165 ++++ .../libunwind/src/UnwindLevel1-gcc-ext.c | 317 + .../crashpad/libunwind/src/UnwindLevel1.c | 561 + .../libunwind/src/UnwindRegistersRestore.S | 1167 ++ .../libunwind/src/UnwindRegistersSave.S | 1117 ++ .../libunwind/src/Unwind_AppleExtras.cpp | 113 + .../crashpad/libunwind/src/assembly.h | 230 + .../crashpad/libunwind/src/cet_unwind.h | 41 + .../external/crashpad/libunwind/src/config.h | 241 + .../external/crashpad/libunwind/src/dwarf2.h | 239 + .../crashpad/libunwind/src/libunwind.cpp | 378 + .../crashpad/libunwind/src/libunwind_ext.h | 65 + .../crashpad/libunwind/test/CMakeLists.txt | 61 + .../libunwind/test/alignment.compile.pass.cpp | 24 + .../libunwind/test/floatregister.pass.cpp | 51 + .../libunwind/test/forceunwind.pass.cpp | 74 + .../test/frameheadercache_test.pass.cpp | 78 + .../libunwind/test/libunwind/__init__.py | 0 .../libunwind/test/libunwind/test/__init__.py | 0 .../libunwind/test/libunwind/test/config.py | 71 + .../libunwind/test/libunwind_01.pass.cpp | 147 + .../libunwind/test/libunwind_02.pass.cpp | 45 + .../crashpad/libunwind/test/lit.cfg.py | 10 + .../crashpad/libunwind/test/lit.site.cfg.in | 58 + .../test/remember_state_leak.pass.sh.s | 65 + .../libunwind/test/signal_frame.pass.cpp | 40 + .../libunwind/test/signal_unwind.pass.cpp | 52 + .../libunwind/test/unw_getcontext.pass.cpp | 12 + .../test/unwind_leaffunction.pass.cpp | 57 + .../external/crashpad/minidump/BUILD.gn | 189 + .../external/crashpad/minidump/CMakeLists.txt | 86 + .../minidump/minidump_annotation_writer.cc | 162 + .../minidump/minidump_annotation_writer.h | 114 + .../minidump_annotation_writer_test.cc | 192 + .../minidump/minidump_byte_array_writer.cc | 73 + .../minidump/minidump_byte_array_writer.h | 66 + .../minidump_byte_array_writer_test.cc | 81 + .../crashpad/minidump/minidump_context.h | 597 + .../minidump/minidump_context_writer.cc | 457 + .../minidump/minidump_context_writer.h | 335 + .../minidump/minidump_context_writer_test.cc | 218 + .../minidump/minidump_crashpad_info_writer.cc | 149 + .../minidump/minidump_crashpad_info_writer.h | 116 + .../minidump_crashpad_info_writer_test.cc | 317 + .../minidump/minidump_exception_writer.cc | 122 + .../minidump/minidump_exception_writer.h | 127 + .../minidump_exception_writer_test.cc | 281 + .../crashpad/minidump/minidump_extensions.cc | 22 + .../crashpad/minidump/minidump_extensions.h | 515 + .../crashpad/minidump/minidump_file_writer.cc | 340 + .../crashpad/minidump/minidump_file_writer.h | 173 + .../minidump/minidump_file_writer_test.cc | 650 + .../minidump/minidump_handle_writer.cc | 127 + .../minidump/minidump_handle_writer.h | 79 + .../minidump/minidump_handle_writer_test.cc | 202 + .../minidump/minidump_memory_info_writer.cc | 85 + .../minidump/minidump_memory_info_writer.h | 74 + .../minidump_memory_info_writer_test.cc | 141 + .../minidump/minidump_memory_writer.cc | 311 + .../minidump/minidump_memory_writer.h | 210 + .../minidump/minidump_memory_writer_test.cc | 692 ++ .../minidump/minidump_misc_info_writer.cc | 415 + .../minidump/minidump_misc_info_writer.h | 165 + .../minidump_misc_info_writer_test.cc | 819 ++ .../minidump_module_crashpad_info_writer.cc | 262 + .../minidump_module_crashpad_info_writer.h | 187 + ...nidump_module_crashpad_info_writer_test.cc | 607 + .../minidump/minidump_module_writer.cc | 496 + .../minidump/minidump_module_writer.h | 392 + .../minidump/minidump_module_writer_test.cc | 947 ++ .../minidump/minidump_rva_list_writer.cc | 100 + .../minidump/minidump_rva_list_writer.h | 79 + .../minidump/minidump_rva_list_writer_test.cc | 107 + ...inidump_simple_string_dictionary_writer.cc | 188 + ...minidump_simple_string_dictionary_writer.h | 154 + ...mp_simple_string_dictionary_writer_test.cc | 288 + .../minidump/minidump_stacktrace_writer.cc | 169 + .../minidump/minidump_stacktrace_writer.h | 88 + .../minidump/minidump_stream_writer.cc | 49 + .../minidump/minidump_stream_writer.h | 66 + .../minidump/minidump_string_writer.cc | 138 + .../minidump/minidump_string_writer.h | 188 + .../minidump/minidump_string_writer_test.cc | 258 + .../minidump/minidump_system_info_writer.cc | 323 + .../minidump/minidump_system_info_writer.h | 198 + .../minidump_system_info_writer_test.cc | 487 + .../minidump/minidump_thread_id_map.cc | 67 + .../minidump/minidump_thread_id_map.h | 52 + .../minidump/minidump_thread_id_map_test.cc | 193 + .../minidump/minidump_thread_writer.cc | 237 + .../minidump/minidump_thread_writer.h | 217 + .../minidump/minidump_thread_writer_test.cc | 724 ++ .../minidump_unloaded_module_writer.cc | 204 + .../minidump_unloaded_module_writer.h | 160 + .../minidump_unloaded_module_writer_test.cc | 164 + ...idump_user_extension_stream_data_source.cc | 26 + ...nidump_user_extension_stream_data_source.h | 87 + .../minidump/minidump_user_stream_writer.cc | 142 + .../minidump/minidump_user_stream_writer.h | 80 + .../minidump_user_stream_writer_test.cc | 149 + .../crashpad/minidump/minidump_writable.cc | 266 + .../crashpad/minidump/minidump_writable.h | 280 + .../minidump/minidump_writable_test.cc | 844 ++ .../crashpad/minidump/minidump_writer_util.cc | 61 + .../crashpad/minidump/minidump_writer_util.h | 89 + .../minidump_byte_array_writer_test_util.cc | 36 + .../minidump_byte_array_writer_test_util.h | 43 + .../test/minidump_context_test_util.cc | 597 + .../test/minidump_context_test_util.h | 93 + .../test/minidump_file_writer_test_util.cc | 59 + .../test/minidump_file_writer_test_util.h | 65 + .../test/minidump_memory_writer_test_util.cc | 82 + .../test/minidump_memory_writer_test_util.h | 98 + .../test/minidump_rva_list_test_util.cc | 50 + .../test/minidump_rva_list_test_util.h | 44 + .../test/minidump_string_writer_test_util.cc | 107 + .../test/minidump_string_writer_test_util.h | 102 + .../minidump_user_extension_stream_util.cc | 43 + .../minidump_user_extension_stream_util.h | 56 + .../test/minidump_writable_test_util.cc | 414 + .../test/minidump_writable_test_util.h | 297 + shared/sentry/external/crashpad/navbar.md | 25 + shared/sentry/external/crashpad/package.h | 29 + .../external/crashpad/snapshot/BUILD.gn | 639 + .../external/crashpad/snapshot/CMakeLists.txt | 248 + .../crashpad/snapshot/annotation_snapshot.cc | 32 + .../crashpad/snapshot/annotation_snapshot.h | 54 + .../crashpad/snapshot/capture_memory.cc | 146 + .../crashpad/snapshot/capture_memory.h | 99 + .../crashpad/snapshot/cpu_architecture.h | 51 + .../external/crashpad/snapshot/cpu_context.cc | 211 + .../external/crashpad/snapshot/cpu_context.h | 390 + .../crashpad/snapshot/cpu_context_test.cc | 348 + .../snapshot/crashpad_info_client_options.cc | 45 + .../snapshot/crashpad_info_client_options.h | 72 + .../crashpad_info_client_options_test.cc | 338 + ...rashpad_info_client_options_test_module.cc | 47 + .../crashpad_info_size_test_module.cc | 129 + .../snapshot/crashpad_info_size_test_note.S | 52 + .../crashpad_types/crashpad_info_reader.cc | 197 + .../crashpad_types/crashpad_info_reader.h | 76 + .../crashpad_info_reader_test.cc | 233 + .../crashpad_types/image_annotation_reader.cc | 153 + .../crashpad_types/image_annotation_reader.h | 76 + .../image_annotation_reader_test.cc | 197 + .../snapshot/elf/elf_dynamic_array_reader.cc | 80 + .../snapshot/elf/elf_dynamic_array_reader.h | 76 + .../crashpad/snapshot/elf/elf_image_reader.cc | 858 ++ .../crashpad/snapshot/elf/elf_image_reader.h | 296 + .../snapshot/elf/elf_image_reader_fuzzer.cc | 78 + .../.gitattributes | 17 + ...shpad_snapshot_test_both_dt_hash_styles.so | Bin 0 -> 8544 bytes .../elf/elf_image_reader_fuzzer_corpus/ret42 | Bin 0 -> 8264 bytes .../snapshot/elf/elf_image_reader_test.cc | 362 + .../snapshot/elf/elf_image_reader_test_note.S | 33 + .../snapshot/elf/elf_symbol_table_reader.cc | 93 + .../snapshot/elf/elf_symbol_table_reader.h | 90 + .../snapshot/elf/module_snapshot_elf.cc | 253 + .../snapshot/elf/module_snapshot_elf.h | 109 + .../snapshot/elf/test_exported_symbols.sym | 18 + .../crashpad/snapshot/exception_snapshot.h | 121 + .../snapshot/fuchsia/cpu_context_fuchsia.cc | 95 + .../snapshot/fuchsia/cpu_context_fuchsia.h | 62 + .../fuchsia/exception_snapshot_fuchsia.cc | 141 + .../fuchsia/exception_snapshot_fuchsia.h | 85 + .../snapshot/fuchsia/memory_map_fuchsia.cc | 85 + .../snapshot/fuchsia/memory_map_fuchsia.h | 64 + .../memory_map_region_snapshot_fuchsia.cc | 85 + .../memory_map_region_snapshot_fuchsia.h | 39 + .../fuchsia/process_reader_fuchsia.cc | 375 + .../snapshot/fuchsia/process_reader_fuchsia.h | 148 + .../fuchsia/process_reader_fuchsia_test.cc | 200 + .../fuchsia/process_snapshot_fuchsia.cc | 243 + .../fuchsia/process_snapshot_fuchsia.h | 155 + .../fuchsia/process_snapshot_fuchsia_test.cc | 240 + .../fuchsia/system_snapshot_fuchsia.cc | 213 + .../fuchsia/system_snapshot_fuchsia.h | 88 + .../fuchsia/thread_snapshot_fuchsia.cc | 106 + .../fuchsia/thread_snapshot_fuchsia.h | 82 + .../crashpad/snapshot/handle_snapshot.cc | 31 + .../crashpad/snapshot/handle_snapshot.h | 56 + .../crashpad/snapshot/hash_types_test.cc | 17 + ...xception_snapshot_ios_intermediate_dump.cc | 399 + ...exception_snapshot_ios_intermediate_dump.h | 115 + .../ios/intermediate_dump_reader_util.cc | 93 + .../ios/intermediate_dump_reader_util.h | 116 + .../memory_snapshot_ios_intermediate_dump.cc | 88 + .../memory_snapshot_ios_intermediate_dump.h | 73 + ...ory_snapshot_ios_intermediate_dump_test.cc | 161 + .../module_snapshot_ios_intermediate_dump.cc | 266 + .../module_snapshot_ios_intermediate_dump.h | 97 + .../process_snapshot_ios_intermediate_dump.cc | 313 + .../process_snapshot_ios_intermediate_dump.h | 130 + ...ess_snapshot_ios_intermediate_dump_test.cc | 679 ++ .../system_snapshot_ios_intermediate_dump.cc | 245 + .../system_snapshot_ios_intermediate_dump.h | 100 + ...h-1fa088dda0adb41459d063078a0f384a0bb8eefa | Bin 0 -> 565 bytes .../ios/testdata/crash-5726011582644224 | Bin 0 -> 71017 bytes .../thread_snapshot_ios_intermediate_dump.cc | 241 + .../thread_snapshot_ios_intermediate_dump.h | 80 + .../linux/capture_memory_delegate_linux.cc | 75 + .../linux/capture_memory_delegate_linux.h | 70 + .../snapshot/linux/cpu_context_linux.cc | 272 + .../snapshot/linux/cpu_context_linux.h | 180 + .../snapshot/linux/debug_rendezvous.cc | 159 + .../snapshot/linux/debug_rendezvous.h | 89 + .../snapshot/linux/debug_rendezvous_test.cc | 270 + .../linux/exception_snapshot_linux.cc | 500 + .../snapshot/linux/exception_snapshot_linux.h | 107 + .../linux/exception_snapshot_linux_test.cc | 502 + .../snapshot/linux/process_reader_linux.cc | 543 + .../snapshot/linux/process_reader_linux.h | 185 + .../linux/process_reader_linux_test.cc | 669 + .../snapshot/linux/process_snapshot_linux.cc | 323 + .../snapshot/linux/process_snapshot_linux.h | 161 + .../crashpad/snapshot/linux/signal_context.h | 434 + .../snapshot/linux/system_snapshot_linux.cc | 432 + .../snapshot/linux/system_snapshot_linux.h | 114 + .../linux/system_snapshot_linux_test.cc | 93 + .../crashpad/snapshot/linux/test_modules.cc | 262 + .../crashpad/snapshot/linux/test_modules.h | 37 + .../snapshot/linux/thread_snapshot_linux.cc | 315 + .../snapshot/linux/thread_snapshot_linux.h | 96 + .../crashpad/snapshot/mac/cpu_context_mac.cc | 594 + .../crashpad/snapshot/mac/cpu_context_mac.h | 156 + .../snapshot/mac/cpu_context_mac_test.cc | 421 + .../snapshot/mac/exception_snapshot_mac.cc | 263 + .../snapshot/mac/exception_snapshot_mac.h | 107 + .../mac/mach_o_image_annotations_reader.cc | 238 + .../mac/mach_o_image_annotations_reader.h | 103 + .../mach_o_image_annotations_reader_test.cc | 483 + ...s_reader_test_module_crashy_initializer.cc | 31 + ...h_o_image_annotations_reader_test_no_op.cc | 19 + .../snapshot/mac/mach_o_image_reader.cc | 733 ++ .../snapshot/mac/mach_o_image_reader.h | 357 + .../snapshot/mac/mach_o_image_reader_test.cc | 649 + .../mac/mach_o_image_segment_reader.cc | 310 + .../mac/mach_o_image_segment_reader.h | 301 + .../mac/mach_o_image_segment_reader_test.cc | 188 + .../mac/mach_o_image_symbol_table_reader.cc | 297 + .../mac/mach_o_image_symbol_table_reader.h | 134 + .../snapshot/mac/module_snapshot_mac.cc | 208 + .../snapshot/mac/module_snapshot_mac.h | 101 + .../snapshot/mac/process_reader_mac.cc | 756 ++ .../snapshot/mac/process_reader_mac.h | 273 + .../snapshot/mac/process_reader_mac_test.cc | 949 ++ .../snapshot/mac/process_snapshot_mac.cc | 250 + .../snapshot/mac/process_snapshot_mac.h | 160 + .../crashpad/snapshot/mac/process_types.cc | 377 + .../crashpad/snapshot/mac/process_types.h | 234 + .../snapshot/mac/process_types/all.proctype | 27 + .../mac/process_types/annotation.proctype | 31 + .../mac/process_types/crashpad_info.proctype | 71 + .../crashreporterclient.proctype | 60 + .../snapshot/mac/process_types/custom.cc | 247 + .../mac/process_types/dyld_images.proctype | 165 + .../snapshot/mac/process_types/flavors.h | 24 + .../snapshot/mac/process_types/internal.h | 36 + .../mac/process_types/loader.proctype | 140 + .../snapshot/mac/process_types/nlist.proctype | 30 + .../snapshot/mac/process_types/traits.h | 46 + .../snapshot/mac/process_types_test.cc | 453 + .../snapshot/mac/system_snapshot_mac.cc | 394 + .../snapshot/mac/system_snapshot_mac.h | 102 + .../snapshot/mac/system_snapshot_mac_test.cc | 145 + .../snapshot/mac/thread_snapshot_mac.cc | 162 + .../snapshot/mac/thread_snapshot_mac.h | 92 + .../snapshot/memory_map_region_snapshot.h | 35 + .../crashpad/snapshot/memory_snapshot.cc | 96 + .../crashpad/snapshot/memory_snapshot.h | 116 + .../snapshot/memory_snapshot_generic.h | 121 + .../crashpad/snapshot/memory_snapshot_test.cc | 151 + .../minidump/exception_snapshot_minidump.cc | 111 + .../minidump/exception_snapshot_minidump.h | 79 + .../minidump/memory_snapshot_minidump.cc | 111 + .../minidump/memory_snapshot_minidump.h | 64 + .../minidump/minidump_annotation_reader.cc | 119 + .../minidump/minidump_annotation_reader.h | 41 + .../minidump/minidump_context_converter.cc | 279 + .../minidump/minidump_context_converter.h | 47 + ...inidump_simple_string_dictionary_reader.cc | 88 + ...minidump_simple_string_dictionary_reader.h | 42 + .../snapshot/minidump/minidump_stream.h | 44 + .../minidump/minidump_string_list_reader.cc | 74 + .../minidump/minidump_string_list_reader.h | 42 + .../minidump/minidump_string_reader.cc | 83 + .../minidump/minidump_string_reader.h | 58 + .../minidump/module_snapshot_minidump.cc | 296 + .../minidump/module_snapshot_minidump.h | 114 + .../minidump/process_snapshot_minidump.cc | 662 + .../minidump/process_snapshot_minidump.h | 172 + .../process_snapshot_minidump_test.cc | 1438 +++ .../minidump/system_snapshot_minidump.cc | 199 + .../minidump/system_snapshot_minidump.h | 88 + .../minidump/thread_snapshot_minidump.cc | 114 + .../minidump/thread_snapshot_minidump.h | 82 + .../crashpad/snapshot/module_snapshot.h | 247 + .../crashpad/snapshot/posix/timezone.cc | 120 + .../crashpad/snapshot/posix/timezone.h | 55 + .../crashpad/snapshot/posix/timezone_test.cc | 195 + .../crashpad/snapshot/process_snapshot.h | 210 + .../sanitized/memory_snapshot_sanitized.cc | 108 + .../sanitized/memory_snapshot_sanitized.h | 75 + .../sanitized/module_snapshot_sanitized.cc | 136 + .../sanitized/module_snapshot_sanitized.h | 76 + .../sanitized/process_snapshot_sanitized.cc | 274 + .../sanitized/process_snapshot_sanitized.h | 115 + .../process_snapshot_sanitized_test.cc | 314 + .../sanitized/sanitization_information.cc | 116 + .../sanitized/sanitization_information.h | 101 + .../sanitization_information_test.cc | 78 + .../sanitized/thread_snapshot_sanitized.cc | 63 + .../sanitized/thread_snapshot_sanitized.h | 60 + .../crashpad/snapshot/snapshot_constants.h | 28 + .../crashpad/snapshot/system_snapshot.h | 282 + .../snapshot/test/test_cpu_context.cc | 296 + .../crashpad/snapshot/test/test_cpu_context.h | 71 + .../snapshot/test/test_exception_snapshot.cc | 67 + .../snapshot/test/test_exception_snapshot.h | 96 + .../test/test_memory_map_region_snapshot.cc | 37 + .../test/test_memory_map_region_snapshot.h | 49 + .../snapshot/test/test_memory_snapshot.cc | 65 + .../snapshot/test/test_memory_snapshot.h | 66 + .../snapshot/test/test_module_snapshot.cc | 113 + .../snapshot/test/test_module_snapshot.h | 138 + .../snapshot/test/test_process_snapshot.cc | 135 + .../snapshot/test/test_process_snapshot.h | 198 + .../snapshot/test/test_system_snapshot.cc | 134 + .../snapshot/test/test_system_snapshot.h | 149 + .../snapshot/test/test_thread_snapshot.cc | 67 + .../snapshot/test/test_thread_snapshot.h | 108 + .../crashpad/snapshot/thread_snapshot.h | 109 + .../snapshot/unloaded_module_snapshot.cc | 33 + .../snapshot/unloaded_module_snapshot.h | 61 + .../win/capture_memory_delegate_win.cc | 73 + .../win/capture_memory_delegate_win.h | 70 + .../crashpad/snapshot/win/cpu_context_win.cc | 233 + .../crashpad/snapshot/win/cpu_context_win.h | 58 + .../snapshot/win/cpu_context_win_test.cc | 149 + .../win/crashpad_snapshot_test_annotations.cc | 69 + .../crashpad_snapshot_test_crashing_child.cc | 53 + ...pad_snapshot_test_dump_without_crashing.cc | 54 + ...shpad_snapshot_test_extra_memory_ranges.cc | 54 + .../crashpad_snapshot_test_image_reader.cc | 90 + ...shpad_snapshot_test_image_reader_module.cc | 19 + .../crashpad/snapshot/win/end_to_end_test.py | 488 + .../snapshot/win/exception_snapshot_win.cc | 276 + .../snapshot/win/exception_snapshot_win.h | 114 + .../win/exception_snapshot_win_test.cc | 320 + .../snapshot/win/extra_memory_ranges_test.cc | 129 + .../win/memory_map_region_snapshot_win.cc | 41 + .../win/memory_map_region_snapshot_win.h | 37 + .../snapshot/win/module_snapshot_win.cc | 343 + .../snapshot/win/module_snapshot_win.h | 135 + .../snapshot/win/module_snapshot_win_test.cc | 171 + .../win/pe_image_annotations_reader.cc | 184 + .../win/pe_image_annotations_reader.h | 83 + .../crashpad/snapshot/win/pe_image_reader.cc | 447 + .../crashpad/snapshot/win/pe_image_reader.h | 204 + .../snapshot/win/pe_image_reader_test.cc | 193 + .../snapshot/win/pe_image_resource_reader.cc | 279 + .../snapshot/win/pe_image_resource_reader.h | 180 + .../snapshot/win/process_reader_win.cc | 484 + .../snapshot/win/process_reader_win.h | 150 + .../snapshot/win/process_reader_win_test.cc | 210 + .../snapshot/win/process_snapshot_win.cc | 570 + .../snapshot/win/process_snapshot_win.h | 201 + .../snapshot/win/process_snapshot_win_test.cc | 131 + .../snapshot/win/process_subrange_reader.cc | 106 + .../snapshot/win/process_subrange_reader.h | 115 + .../snapshot/win/system_snapshot_win.cc | 505 + .../snapshot/win/system_snapshot_win.h | 97 + .../snapshot/win/system_snapshot_win_test.cc | 171 + .../snapshot/win/thread_snapshot_win.cc | 147 + .../snapshot/win/thread_snapshot_win.h | 98 + .../crashpad/snapshot/x86/cpuid_reader.cc | 138 + .../crashpad/snapshot/x86/cpuid_reader.h | 74 + shared/sentry/external/crashpad/test/BUILD.gn | 263 + .../sentry/external/crashpad/test/errors.cc | 63 + shared/sentry/external/crashpad/test/errors.h | 82 + shared/sentry/external/crashpad/test/file.cc | 91 + shared/sentry/external/crashpad/test/file.h | 51 + .../external/crashpad/test/filesystem.cc | 218 + .../external/crashpad/test/filesystem.h | 75 + .../crashpad/test/fuchsia_crashpad_tests.cmx | 27 + .../external/crashpad/test/gtest_death.h | 133 + .../external/crashpad/test/gtest_main.cc | 126 + .../external/crashpad/test/hex_string.cc | 35 + .../external/crashpad/test/hex_string.h | 40 + .../external/crashpad/test/hex_string_test.cc | 35 + .../external/crashpad/test/ios/BUILD.gn | 83 + .../test/ios/cptest_google_test_runner.mm | 41 + .../ios/cptest_google_test_runner_delegate.h | 30 + .../crashpad/test/ios/crash_type_xctest.mm | 384 + .../crashpad/test/ios/google_test_setup.h | 33 + .../crashpad/test/ios/google_test_setup.mm | 134 + .../external/crashpad/test/ios/host/BUILD.gn | 74 + .../crashpad/test/ios/host/Info.plist | 112 + .../ios/host/cptest_application_delegate.h | 23 + .../ios/host/cptest_application_delegate.mm | 498 + .../ios/host/cptest_crash_view_controller.h | 23 + .../ios/host/cptest_crash_view_controller.mm | 64 + .../test/ios/host/cptest_shared_object.h | 120 + .../ios/host/handler_forbidden_allocators.cc | 295 + .../ios/host/handler_forbidden_allocators.h | 31 + .../external/crashpad/test/ios/host/main.mm | 30 + .../test/linux/fake_ptrace_connection.cc | 105 + .../test/linux/fake_ptrace_connection.h | 85 + .../external/crashpad/test/linux/get_tls.cc | 59 + .../external/crashpad/test/linux/get_tls.h | 29 + .../sentry/external/crashpad/test/mac/dyld.cc | 99 + .../sentry/external/crashpad/test/mac/dyld.h | 33 + .../crashpad/test/mac/exception_swallower.cc | 195 + .../crashpad/test/mac/exception_swallower.h | 87 + .../external/crashpad/test/mac/mach_errors.cc | 52 + .../external/crashpad/test/mac/mach_errors.h | 53 + .../crashpad/test/mac/mach_multiprocess.cc | 271 + .../crashpad/test/mac/mach_multiprocess.h | 120 + .../test/mac/mach_multiprocess_test.cc | 49 + .../external/crashpad/test/main_arguments.cc | 38 + .../external/crashpad/test/main_arguments.h | 49 + .../crashpad/test/main_arguments_test.cc | 34 + .../external/crashpad/test/multiprocess.h | 232 + .../crashpad/test/multiprocess_exec.cc | 75 + .../crashpad/test/multiprocess_exec.h | 154 + .../test/multiprocess_exec_fuchsia.cc | 203 + .../crashpad/test/multiprocess_exec_posix.cc | 165 + .../crashpad/test/multiprocess_exec_test.cc | 137 + .../test/multiprocess_exec_test_child.cc | 95 + .../crashpad/test/multiprocess_exec_win.cc | 178 + .../crashpad/test/multiprocess_posix.cc | 259 + .../crashpad/test/multiprocess_posix_test.cc | 295 + .../external/crashpad/test/process_type.cc | 41 + .../external/crashpad/test/process_type.h | 53 + .../crashpad/test/scoped_guarded_page.h | 51 + .../test/scoped_guarded_page_posix.cc | 47 + .../crashpad/test/scoped_guarded_page_test.cc | 38 + .../crashpad/test/scoped_guarded_page_win.cc | 39 + .../crashpad/test/scoped_module_handle.cc | 52 + .../crashpad/test/scoped_module_handle.h | 87 + .../external/crashpad/test/scoped_temp_dir.cc | 53 + .../external/crashpad/test/scoped_temp_dir.h | 65 + .../crashpad/test/scoped_temp_dir_posix.cc | 62 + .../crashpad/test/scoped_temp_dir_test.cc | 124 + .../crashpad/test/scoped_temp_dir_win.cc | 78 + .../external/crashpad/test/test_paths.cc | 245 + .../external/crashpad/test/test_paths.h | 151 + .../external/crashpad/test/test_paths_test.cc | 35 + .../test/test_paths_test_data_root.txt | 16 + .../crashpad/test/win/child_launcher.cc | 109 + .../crashpad/test/win/child_launcher.h | 77 + .../crashpad/test/win/win_child_process.cc | 239 + .../crashpad/test/win/win_child_process.h | 120 + .../test/win/win_child_process_test.cc | 87 + .../crashpad/test/win/win_multiprocess.cc | 78 + .../crashpad/test/win/win_multiprocess.h | 210 + .../test/win/win_multiprocess_test.cc | 90 + .../win/win_multiprocess_with_temp_dir.cc | 189 + .../test/win/win_multiprocess_with_temp_dir.h | 85 + .../crashpad/third_party/cpp-httplib/BUILD.gn | 20 + .../third_party/cpp-httplib/README.crashpad | 15 + .../cpp-httplib/cpp-httplib/LICENSE | 22 + .../cpp-httplib/cpp-httplib/README.md | 209 + .../cpp-httplib/cpp-httplib/httplib.h | 2335 ++++ .../crashpad/third_party/edo/BUILD.gn | 144 + .../crashpad/third_party/edo/README.crashpad | 10 + .../crashpad/third_party/fuchsia/BUILD.gn | 38 + .../third_party/fuchsia/README.crashpad | 3 + .../crashpad/third_party/fuchsia/runner.py | 20 + .../crashpad/third_party/getopt/BUILD.gn | 20 + .../third_party/getopt/CMakeLists.txt | 18 + .../crashpad/third_party/getopt/LICENSE | 5 + .../third_party/getopt/README.crashpad | 20 + .../crashpad/third_party/getopt/getopt.cc | 423 + .../crashpad/third_party/getopt/getopt.h | 63 + .../crashpad/third_party/glibc/BUILD.gn | 17 + .../crashpad/third_party/glibc/COPYING.LIB | 502 + .../third_party/glibc/README.crashpad | 16 + .../crashpad/third_party/glibc/elf/elf.h | 4003 ++++++ .../crashpad/third_party/googletest/BUILD.gn | 375 + .../third_party/googletest/README.crashpad | 15 + .../crashpad/third_party/gyp/README.crashpad | 13 + .../third_party/linux/README.crashpad | 3 + .../crashpad/third_party/lss/BUILD.gn | 31 + .../crashpad/third_party/lss/README.crashpad | 16 + .../external/crashpad/third_party/lss/lss.h | 28 + .../crashpad/third_party/lss/lss/.gitignore | 3 + .../crashpad/third_party/lss/lss/DIR_METADATA | 12 + .../crashpad/third_party/lss/lss/LICENSE | 28 + .../crashpad/third_party/lss/lss/OWNERS | 2 + .../crashpad/third_party/lss/lss/README.md | 137 + .../third_party/lss/lss/codereview.settings | 5 + .../lss/lss/linux_syscall_support.h | 4867 ++++++++ .../third_party/lss/lss/tests/.gitignore | 4 + .../third_party/lss/lss/tests/Makefile | 140 + .../third_party/lss/lss/tests/README.md | 52 + .../third_party/lss/lss/tests/fallocate.c | 69 + .../third_party/lss/lss/tests/getrandom.c | 59 + .../third_party/lss/lss/tests/lstat.c | 97 + .../third_party/lss/lss/tests/sigaction.c | 54 + .../third_party/lss/lss/tests/sigtimedwait.c | 58 + .../crashpad/third_party/lss/lss/tests/stat.c | 67 + .../third_party/lss/lss/tests/test_skel.h | 74 + .../third_party/lss/lss/tests/unlink.c | 48 + .../third_party/mini_chromium/BUILD.gn | 66 + .../third_party/mini_chromium/CMakeLists.txt | 212 + .../third_party/mini_chromium/README.crashpad | 17 + .../mini_chromium/build/chromeos_buildflags.h | 13 + .../mini_chromium/.gitattributes | 23 + .../mini_chromium/mini_chromium/.gitignore | 17 + .../mini_chromium/mini_chromium/.style.yapf | 7 + .../mini_chromium/mini_chromium/AUTHORS | 5 + .../mini_chromium/mini_chromium/BUILD.gn | 7 + .../mini_chromium/mini_chromium/LICENSE | 27 + .../mini_chromium/mini_chromium/README.md | 38 + .../mini_chromium/mini_chromium/base/BUILD.gn | 170 + .../mini_chromium/base/atomicops.h | 178 + .../atomicops_internals_atomicword_compat.h | 100 + .../base/atomicops_internals_portable.h | 227 + .../mini_chromium/base/auto_reset.h | 31 + .../mini_chromium/base/bit_cast.h | 98 + .../mini_chromium/mini_chromium/base/check.h | 26 + .../mini_chromium/base/check_op.h | 80 + .../mini_chromium/base/compiler_specific.h | 53 + .../mini_chromium/base/cxx17_backports.h | 19 + .../mini_chromium/base/debug/alias.cc | 24 + .../mini_chromium/base/debug/alias.h | 19 + .../mini_chromium/base/files/file_path.cc | 292 + .../mini_chromium/base/files/file_path.h | 243 + .../mini_chromium/base/files/file_util.h | 22 + .../base/files/file_util_posix.cc | 26 + .../mini_chromium/base/files/scoped_file.cc | 34 + .../mini_chromium/base/files/scoped_file.h | 41 + .../mini_chromium/base/format_macros.h | 101 + .../base/fuchsia/fuchsia_logging.cc | 27 + .../base/fuchsia/fuchsia_logging.h | 60 + .../mini_chromium/base/logging.cc | 535 + .../mini_chromium/base/logging.h | 317 + .../mini_chromium/base/mac/close_nocancel.cc | 70 + .../mini_chromium/base/mac/foundation_util.h | 188 + .../mini_chromium/base/mac/foundation_util.mm | 116 + .../mini_chromium/base/mac/mach_logging.cc | 89 + .../mini_chromium/base/mac/mach_logging.h | 163 + .../mini_chromium/base/mac/scoped_cftyperef.h | 34 + .../mini_chromium/base/mac/scoped_ioobject.h | 35 + .../base/mac/scoped_launch_data.h | 36 + .../base/mac/scoped_mach_port.cc | 32 + .../mini_chromium/base/mac/scoped_mach_port.h | 49 + .../mini_chromium/base/mac/scoped_mach_vm.cc | 33 + .../mini_chromium/base/mac/scoped_mach_vm.h | 92 + .../base/mac/scoped_nsautorelease_pool.h | 55 + .../base/mac/scoped_nsautorelease_pool.mm | 32 + .../mini_chromium/base/mac/scoped_nsobject.h | 70 + .../mini_chromium/base/mac/scoped_typeref.h | 85 + .../mini_chromium/base/memory/free_deleter.h | 25 + .../mini_chromium/base/memory/page_size.h | 16 + .../base/memory/page_size_posix.cc | 15 + .../base/memory/page_size_win.cc | 13 + .../mini_chromium/base/memory/scoped_policy.h | 25 + .../base/metrics/histogram_functions.h | 19 + .../base/metrics/histogram_macros.h | 70 + .../metrics/persistent_histogram_allocator.h | 40 + .../mini_chromium/base/notreached.h | 12 + .../base/numerics/checked_math.h | 387 + .../base/numerics/checked_math_impl.h | 567 + .../base/numerics/clamped_math.h | 262 + .../base/numerics/clamped_math_impl.h | 341 + .../base/numerics/safe_conversions.h | 353 + .../base/numerics/safe_conversions_arm_impl.h | 50 + .../base/numerics/safe_conversions_impl.h | 851 ++ .../mini_chromium/base/numerics/safe_math.h | 12 + .../base/numerics/safe_math_arm_impl.h | 123 + .../base/numerics/safe_math_clang_gcc_impl.h | 156 + .../base/numerics/safe_math_shared_impl.h | 235 + .../mini_chromium/base/posix/eintr_wrapper.h | 46 + .../mini_chromium/base/posix/safe_strerror.cc | 45 + .../mini_chromium/base/posix/safe_strerror.h | 17 + .../mini_chromium/base/process/memory.cc | 16 + .../mini_chromium/base/process/memory.h | 19 + .../mini_chromium/base/rand_util.cc | 142 + .../mini_chromium/base/rand_util.h | 27 + .../base/scoped_clear_last_error.h | 50 + .../base/scoped_clear_last_error_win.cc | 20 + .../mini_chromium/base/scoped_generic.h | 118 + .../base/strings/string_number_conversions.cc | 274 + .../base/strings/string_number_conversions.h | 28 + .../mini_chromium/base/strings/string_piece.h | 202 + .../mini_chromium/base/strings/string_util.cc | 40 + .../mini_chromium/base/strings/string_util.h | 41 + .../base/strings/string_util_posix.h | 29 + .../base/strings/string_util_win.cc | 24 + .../base/strings/string_util_win.h | 20 + .../base/strings/stringprintf.cc | 108 + .../mini_chromium/base/strings/stringprintf.h | 23 + .../base/strings/sys_string_conversions.h | 33 + .../strings/sys_string_conversions_mac.mm | 97 + .../strings/utf_string_conversion_utils.cc | 118 + .../strings/utf_string_conversion_utils.h | 39 + .../base/strings/utf_string_conversions.cc | 76 + .../base/strings/utf_string_conversions.h | 27 + .../base/synchronization/condition_variable.h | 108 + .../condition_variable_posix.cc | 48 + .../base/synchronization/lock.cc | 52 + .../mini_chromium/base/synchronization/lock.h | 149 + .../base/synchronization/lock_impl.h | 61 + .../base/synchronization/lock_impl_posix.cc | 54 + .../base/synchronization/lock_impl_win.cc | 36 + .../mini_chromium/base/sys_byteorder.h | 100 + .../mini_chromium/base/template_util.h | 128 + .../base/third_party/icu/LICENSE | 76 + .../base/third_party/icu/README.chromium | 14 + .../base/third_party/icu/icu_utf.cc | 131 + .../base/third_party/icu/icu_utf.h | 442 + .../base/threading/thread_local_storage.cc | 253 + .../base/threading/thread_local_storage.h | 143 + .../threading/thread_local_storage_posix.cc | 32 + .../threading/thread_local_storage_win.cc | 107 + .../mini_chromium/build/BUILD.gn | 37 + .../mini_chromium/build/build_config.h | 139 + .../mini_chromium/build/buildflag.h | 16 + .../mini_chromium/build/buildflag_header.gni | 56 + .../mini_chromium/build/compiler.gni | 7 + .../mini_chromium/build/config/BUILD.gn | 751 ++ .../mini_chromium/build/find_mac_sdk.py | 164 + .../build/ios/Application-Info.plist | 114 + .../mini_chromium/build/ios/BUILD.gn | 20 + .../mini_chromium/build/ios/BuildInfo.plist | 35 + .../mini_chromium/build/ios/Module-Info.plist | 26 + .../build/ios/XCTRunnerAddition+Info.plist | 12 + .../mini_chromium/build/ios/codesign.py | 691 ++ .../build/ios/entitlements.plist | 8 + .../build/ios/find_signing_identity.py | 89 + .../mini_chromium/build/ios/ios_sdk.gni | 116 + .../mini_chromium/build/ios/plist_util.py | 265 + .../mini_chromium/build/ios/rules.gni | 686 ++ .../mini_chromium/build/ios/sdk_info.py | 176 + .../mini_chromium/build/ios/strip_arm64e.py | 69 + .../mini_chromium/build/platform.gni | 38 + .../mini_chromium/build/sysroot.gni | 70 + .../mini_chromium/build/win_helper.py | 230 + .../build/write_buildflag_header.py | 104 + .../mini_chromium/codereview.settings | 9 + .../mini_chromium/testing/BUILD.gn | 8 + .../mini_chromium/testing/platform_test.h | 39 + .../utf_string_conversion_utils.mingw.cc | 105 + .../crashpad/third_party/xnu/APPLE_LICENSE | 367 + .../crashpad/third_party/xnu/BUILD.gn | 17 + .../xnu/EXTERNAL_HEADERS/mach-o/loader.h | 1574 +++ .../crashpad/third_party/xnu/README.crashpad | 25 + .../third_party/xnu/osfmk/mach/exc.defs | 133 + .../third_party/xnu/osfmk/mach/mach_exc.defs | 133 + .../xnu/osfmk/mach/mach_types.defs | 652 + .../xnu/osfmk/mach/machine/machine_types.defs | 130 + .../third_party/xnu/osfmk/mach/std_types.defs | 150 + .../crashpad/third_party/zlib/BUILD.gn | 145 + .../crashpad/third_party/zlib/CMakeLists.txt | 96 + .../crashpad/third_party/zlib/README.crashpad | 18 + .../crashpad/third_party/zlib/zlib/BUILD.gn | 161 + .../crashpad/third_party/zlib/zlib/LICENSE | 19 + .../crashpad/third_party/zlib/zlib/OWNERS | 3 + .../third_party/zlib/zlib/README.chromium | 28 + .../crashpad/third_party/zlib/zlib/adler32.c | 186 + .../crashpad/third_party/zlib/zlib/compress.c | 86 + .../zlib/zlib/contrib/minizip/ChangeLogUnzip | 67 + .../zlib/zlib/contrib/minizip/Makefile | 25 + .../zlib/zlib/contrib/minizip/crypt.h | 131 + .../zlib/zlib/contrib/minizip/ioapi.c | 247 + .../zlib/zlib/contrib/minizip/ioapi.h | 208 + .../zlib/zlib/contrib/minizip/iowin32.c | 469 + .../zlib/zlib/contrib/minizip/iowin32.h | 28 + .../zlib/zlib/contrib/minizip/miniunz.c | 660 + .../zlib/zlib/contrib/minizip/minizip.c | 520 + .../zlib/zlib/contrib/minizip/mztools.c | 291 + .../zlib/zlib/contrib/minizip/mztools.h | 37 + .../zlib/zlib/contrib/minizip/unzip.c | 2120 ++++ .../zlib/zlib/contrib/minizip/unzip.h | 437 + .../zlib/zlib/contrib/minizip/zip.c | 2007 +++ .../zlib/zlib/contrib/minizip/zip.h | 362 + .../crashpad/third_party/zlib/zlib/crc32.c | 469 + .../crashpad/third_party/zlib/zlib/crc32.h | 441 + .../third_party/zlib/zlib/crc_folding.c | 493 + .../crashpad/third_party/zlib/zlib/deflate.c | 2242 ++++ .../crashpad/third_party/zlib/zlib/deflate.h | 359 + .../third_party/zlib/zlib/fill_window_sse.c | 175 + .../third_party/zlib/zlib/google.patch | 399 + .../third_party/zlib/zlib/google/DEPS | 5 + .../third_party/zlib/zlib/google/OWNERS | 5 + .../zlib/zlib/google/compression_utils.cc | 165 + .../zlib/zlib/google/compression_utils.h | 22 + .../zlib/google/compression_utils_unittest.cc | 91 + .../zlib/google/test/data/create_test_zip.sh | 15 + .../zlib/zlib/google/test/data/evil.zip | Bin 0 -> 241 bytes .../test/data/evil_via_absolute_file_name.zip | Bin 0 -> 172 bytes .../test/data/evil_via_invalid_utf8.zip | Bin 0 -> 4585 bytes .../zlib/zlib/google/test/data/test.zip | Bin 0 -> 5065 bytes .../zlib/zlib/google/test/data/test/foo.txt | 1 + .../zlib/google/test/data/test/foo/bar.txt | 1 + .../google/test/data/test/foo/bar/.hidden | 1 + .../google/test/data/test/foo/bar/baz.txt | 1 + .../google/test/data/test/foo/bar/quux.txt | 39 + .../google/test/data/test_mismatch_size.zip | Bin 0 -> 886 bytes .../zlib/google/test/data/test_nocompress.zip | Bin 0 -> 14252 bytes .../third_party/zlib/zlib/google/zip.cc | 219 + .../third_party/zlib/zlib/google/zip.h | 59 + .../zlib/zlib/google/zip_internal.cc | 390 + .../zlib/zlib/google/zip_internal.h | 76 + .../zlib/zlib/google/zip_reader.cc | 533 + .../third_party/zlib/zlib/google/zip_reader.h | 281 + .../zlib/zlib/google/zip_reader_unittest.cc | 692 ++ .../zlib/zlib/google/zip_unittest.cc | 338 + .../crashpad/third_party/zlib/zlib/gzclose.c | 25 + .../crashpad/third_party/zlib/zlib/gzguts.h | 218 + .../crashpad/third_party/zlib/zlib/gzlib.c | 637 + .../crashpad/third_party/zlib/zlib/gzread.c | 658 + .../crashpad/third_party/zlib/zlib/gzwrite.c | 665 + .../crashpad/third_party/zlib/zlib/infback.c | 640 + .../crashpad/third_party/zlib/zlib/inffast.c | 323 + .../crashpad/third_party/zlib/zlib/inffast.h | 11 + .../crashpad/third_party/zlib/zlib/inffixed.h | 94 + .../crashpad/third_party/zlib/zlib/inflate.c | 1561 +++ .../crashpad/third_party/zlib/zlib/inflate.h | 125 + .../crashpad/third_party/zlib/zlib/inftrees.c | 304 + .../crashpad/third_party/zlib/zlib/inftrees.h | 62 + .../crashpad/third_party/zlib/zlib/names.h | 167 + .../crashpad/third_party/zlib/zlib/simd.patch | 1233 ++ .../third_party/zlib/zlib/simd_stub.c | 35 + .../crashpad/third_party/zlib/zlib/trees.c | 1203 ++ .../crashpad/third_party/zlib/zlib/trees.h | 128 + .../crashpad/third_party/zlib/zlib/uncompr.c | 93 + .../crashpad/third_party/zlib/zlib/x86.c | 92 + .../crashpad/third_party/zlib/zlib/x86.h | 15 + .../crashpad/third_party/zlib/zlib/zconf.h | 537 + .../crashpad/third_party/zlib/zlib/zlib.h | 1935 +++ .../crashpad/third_party/zlib/zlib/zutil.c | 325 + .../crashpad/third_party/zlib/zlib/zutil.h | 292 + .../crashpad/third_party/zlib/zlib_crashpad.h | 32 + .../sentry/external/crashpad/tools/BUILD.gn | 162 + .../external/crashpad/tools/CMakeLists.txt | 105 + .../external/crashpad/tools/base94_encoder.cc | 130 + .../external/crashpad/tools/base94_encoder.md | 100 + .../crashpad/tools/crashpad_database_util.cc | 632 + .../crashpad/tools/crashpad_database_util.md | 208 + .../crashpad/tools/crashpad_http_upload.cc | 219 + .../crashpad/tools/crashpad_http_upload.md | 132 + .../external/crashpad/tools/generate_dump.cc | 249 + .../external/crashpad/tools/generate_dump.md | 127 + .../tools/mac/catch_exception_tool.cc | 336 + .../tools/mac/catch_exception_tool.md | 140 + .../crashpad/tools/mac/exception_port_tool.cc | 594 + .../crashpad/tools/mac/exception_port_tool.md | 234 + .../tools/mac/on_demand_service_tool.md | 133 + .../tools/mac/on_demand_service_tool.mm | 198 + .../tools/mac/sectaskaccess_info.plist | 12 + .../crashpad/tools/run_with_crashpad.cc | 245 + .../crashpad/tools/run_with_crashpad.md | 147 + .../external/crashpad/tools/tool_support.cc | 113 + .../external/crashpad/tools/tool_support.h | 93 + shared/sentry/external/crashpad/util/BUILD.gn | 974 ++ .../external/crashpad/util/CMakeLists.txt | 533 + .../util/file/delimited_file_reader.cc | 110 + .../util/file/delimited_file_reader.h | 94 + .../util/file/delimited_file_reader_test.cc | 365 + .../crashpad/util/file/directory_reader.h | 88 + .../util/file/directory_reader_posix.cc | 79 + .../util/file/directory_reader_test.cc | 156 + .../util/file/directory_reader_win.cc | 76 + .../crashpad/util/file/file_helper.cc | 34 + .../external/crashpad/util/file/file_helper.h | 29 + .../external/crashpad/util/file/file_io.cc | 194 + .../external/crashpad/util/file/file_io.h | 560 + .../crashpad/util/file/file_io_posix.cc | 282 + .../crashpad/util/file/file_io_test.cc | 766 ++ .../crashpad/util/file/file_io_win.cc | 294 + .../crashpad/util/file/file_reader.cc | 113 + .../external/crashpad/util/file/file_reader.h | 149 + .../crashpad/util/file/file_reader_test.cc | 206 + .../crashpad/util/file/file_seeker.cc | 37 + .../external/crashpad/util/file/file_seeker.h | 54 + .../crashpad/util/file/file_writer.cc | 213 + .../external/crashpad/util/file/file_writer.h | 192 + .../external/crashpad/util/file/filesystem.h | 132 + .../crashpad/util/file/filesystem_posix.cc | 150 + .../crashpad/util/file/filesystem_test.cc | 535 + .../crashpad/util/file/filesystem_win.cc | 199 + .../util/file/output_stream_file_writer.cc | 69 + .../util/file/output_stream_file_writer.h | 63 + .../crashpad/util/file/scoped_remove_file.cc | 25 + .../crashpad/util/file/scoped_remove_file.h | 33 + .../crashpad/util/file/string_file.cc | 172 + .../external/crashpad/util/file/string_file.h | 82 + .../crashpad/util/file/string_file_test.cc | 505 + .../crashpad/util/fuchsia/koid_utilities.cc | 141 + .../crashpad/util/fuchsia/koid_utilities.h | 82 + .../util/fuchsia/scoped_task_suspend.cc | 68 + .../util/fuchsia/scoped_task_suspend.h | 50 + .../external/crashpad/util/fuchsia/traits.h | 32 + .../util/ios/ios_intermediate_dump_data.cc | 42 + .../util/ios/ios_intermediate_dump_data.h | 69 + .../util/ios/ios_intermediate_dump_format.h | 120 + .../ios/ios_intermediate_dump_interface.cc | 60 + .../ios/ios_intermediate_dump_interface.h | 67 + .../util/ios/ios_intermediate_dump_list.cc | 29 + .../util/ios/ios_intermediate_dump_list.h | 56 + .../util/ios/ios_intermediate_dump_map.cc | 68 + .../util/ios/ios_intermediate_dump_map.h | 71 + .../util/ios/ios_intermediate_dump_object.cc | 25 + .../util/ios/ios_intermediate_dump_object.h | 53 + .../util/ios/ios_intermediate_dump_reader.cc | 197 + .../util/ios/ios_intermediate_dump_reader.h | 77 + .../ios/ios_intermediate_dump_reader_test.cc | 264 + .../util/ios/ios_intermediate_dump_writer.cc | 128 + .../util/ios/ios_intermediate_dump_writer.h | 210 + .../ios/ios_intermediate_dump_writer_test.cc | 119 + .../util/ios/ios_system_data_collector.h | 88 + .../util/ios/ios_system_data_collector.mm | 229 + .../external/crashpad/util/ios/raw_logging.cc | 78 + .../external/crashpad/util/ios/raw_logging.h | 44 + .../util/ios/scoped_background_task.h | 40 + .../util/ios/scoped_background_task.mm | 58 + .../crashpad/util/ios/scoped_vm_read.cc | 62 + .../crashpad/util/ios/scoped_vm_read.h | 109 + .../crashpad/util/ios/scoped_vm_read_test.cc | 83 + .../crashpad/util/linux/address_types.h | 36 + .../crashpad/util/linux/auxiliary_vector.cc | 67 + .../crashpad/util/linux/auxiliary_vector.h | 75 + .../util/linux/auxiliary_vector_test.cc | 221 + .../util/linux/checked_linux_address_range.h | 37 + .../util/linux/direct_ptrace_connection.cc | 95 + .../util/linux/direct_ptrace_connection.h | 75 + .../util/linux/exception_handler_client.cc | 227 + .../util/linux/exception_handler_client.h | 87 + .../util/linux/exception_handler_protocol.cc | 37 + .../util/linux/exception_handler_protocol.h | 135 + .../util/linux/exception_information.h | 45 + .../util/linux/initial_signal_dispositions.cc | 79 + .../util/linux/initial_signal_dispositions.h | 41 + .../crashpad/util/linux/memory_map.cc | 463 + .../external/crashpad/util/linux/memory_map.h | 148 + .../crashpad/util/linux/memory_map_test.cc | 670 + .../crashpad/util/linux/proc_stat_reader.cc | 142 + .../crashpad/util/linux/proc_stat_reader.h | 84 + .../util/linux/proc_stat_reader_test.cc | 115 + .../crashpad/util/linux/proc_task_reader.cc | 60 + .../crashpad/util/linux/proc_task_reader.h | 35 + .../util/linux/proc_task_reader_test.cc | 161 + .../crashpad/util/linux/ptrace_broker.cc | 423 + .../crashpad/util/linux/ptrace_broker.h | 219 + .../crashpad/util/linux/ptrace_broker_test.cc | 289 + .../crashpad/util/linux/ptrace_client.cc | 380 + .../crashpad/util/linux/ptrace_client.h | 79 + .../crashpad/util/linux/ptrace_connection.h | 94 + .../external/crashpad/util/linux/ptracer.cc | 585 + .../external/crashpad/util/linux/ptracer.h | 107 + .../crashpad/util/linux/ptracer_test.cc | 83 + .../util/linux/scoped_pr_set_dumpable.cc | 41 + .../util/linux/scoped_pr_set_dumpable.h | 44 + .../util/linux/scoped_pr_set_ptracer.cc | 37 + .../util/linux/scoped_pr_set_ptracer.h | 51 + .../util/linux/scoped_ptrace_attach.cc | 77 + .../util/linux/scoped_ptrace_attach.h | 71 + .../util/linux/scoped_ptrace_attach_test.cc | 186 + .../external/crashpad/util/linux/socket.cc | 193 + .../external/crashpad/util/linux/socket.h | 96 + .../crashpad/util/linux/socket_test.cc | 137 + .../crashpad/util/linux/thread_info.cc | 38 + .../crashpad/util/linux/thread_info.h | 308 + .../external/crashpad/util/linux/traits.h | 50 + .../util/mac/checked_mach_address_range.h | 38 + .../mac/checked_mach_address_range_test.cc | 257 + .../external/crashpad/util/mac/launchd.h | 148 + .../external/crashpad/util/mac/launchd.mm | 142 + .../crashpad/util/mac/launchd_test.mm | 308 + .../external/crashpad/util/mac/mac_util.cc | 349 + .../external/crashpad/util/mac/mac_util.h | 83 + .../crashpad/util/mac/mac_util_test.mm | 156 + .../crashpad/util/mac/service_management.cc | 136 + .../crashpad/util/mac/service_management.h | 80 + .../util/mac/service_management_test.mm | 162 + .../external/crashpad/util/mac/sysctl.cc | 47 + .../external/crashpad/util/mac/sysctl.h | 36 + .../external/crashpad/util/mac/sysctl_test.cc | 35 + .../external/crashpad/util/mac/xattr.cc | 162 + .../sentry/external/crashpad/util/mac/xattr.h | 106 + .../external/crashpad/util/mac/xattr_test.cc | 137 + .../external/crashpad/util/mach/bootstrap.cc | 109 + .../external/crashpad/util/mach/bootstrap.h | 63 + .../crashpad/util/mach/bootstrap_test.cc | 70 + .../crashpad/util/mach/child_port.defs | 62 + .../util/mach/child_port_handshake.cc | 461 + .../crashpad/util/mach/child_port_handshake.h | 332 + .../util/mach/child_port_handshake_test.cc | 382 + .../crashpad/util/mach/child_port_server.cc | 105 + .../crashpad/util/mach/child_port_server.h | 81 + .../util/mach/child_port_server_test.cc | 138 + .../crashpad/util/mach/child_port_types.h | 26 + .../mach/composite_mach_message_server.cc | 87 + .../util/mach/composite_mach_message_server.h | 105 + .../composite_mach_message_server_test.cc | 309 + .../crashpad/util/mach/exc_client_variants.cc | 131 + .../crashpad/util/mach/exc_client_variants.h | 95 + .../util/mach/exc_client_variants_test.cc | 300 + .../crashpad/util/mach/exc_server_variants.cc | 714 ++ .../crashpad/util/mach/exc_server_variants.h | 235 + .../util/mach/exc_server_variants_test.cc | 1365 +++ .../crashpad/util/mach/exception_behaviors.cc | 39 + .../crashpad/util/mach/exception_behaviors.h | 94 + .../util/mach/exception_behaviors_test.cc | 74 + .../crashpad/util/mach/exception_ports.cc | 213 + .../crashpad/util/mach/exception_ports.h | 264 + .../util/mach/exception_ports_test.cc | 846 ++ .../crashpad/util/mach/exception_types.cc | 351 + .../crashpad/util/mach/exception_types.h | 129 + .../util/mach/exception_types_test.cc | 307 + .../crashpad/util/mach/mach_extensions.cc | 111 + .../crashpad/util/mach/mach_extensions.h | 123 + .../util/mach/mach_extensions_test.cc | 150 + .../crashpad/util/mach/mach_message.cc | 293 + .../crashpad/util/mach/mach_message.h | 208 + .../crashpad/util/mach/mach_message_server.cc | 281 + .../crashpad/util/mach/mach_message_server.h | 183 + .../util/mach/mach_message_server_test.cc | 860 ++ .../crashpad/util/mach/mach_message_test.cc | 209 + .../sentry/external/crashpad/util/mach/mig.py | 101 + .../external/crashpad/util/mach/mig_fix.py | 237 + .../external/crashpad/util/mach/mig_gen.py | 104 + .../crashpad/util/mach/notify_server.cc | 242 + .../crashpad/util/mach/notify_server.h | 242 + .../crashpad/util/mach/notify_server_test.cc | 571 + .../crashpad/util/mach/scoped_task_suspend.cc | 40 + .../crashpad/util/mach/scoped_task_suspend.h | 46 + .../util/mach/scoped_task_suspend_test.cc | 87 + .../util/mach/symbolic_constants_mach.cc | 550 + .../util/mach/symbolic_constants_mach.h | 120 + .../util/mach/symbolic_constants_mach_test.cc | 1062 ++ .../crashpad/util/mach/task_for_pid.cc | 166 + .../crashpad/util/mach/task_for_pid.h | 59 + .../crashpad/util/misc/address_sanitizer.h | 28 + .../crashpad/util/misc/address_types.h | 76 + .../crashpad/util/misc/arm64_pac_bti.S | 65 + .../external/crashpad/util/misc/arraysize.h | 43 + .../crashpad/util/misc/arraysize_test.cc | 64 + .../crashpad/util/misc/as_underlying_type.h | 34 + .../crashpad/util/misc/capture_context.h | 87 + .../util/misc/capture_context_broken.cc | 29 + .../util/misc/capture_context_fuchsia.S | 173 + .../util/misc/capture_context_linux.S | 430 + .../crashpad/util/misc/capture_context_mac.S | 288 + .../util/misc/capture_context_test.cc | 116 + .../util/misc/capture_context_test_util.h | 41 + .../misc/capture_context_test_util_fuchsia.cc | 58 + .../misc/capture_context_test_util_linux.cc | 70 + .../misc/capture_context_test_util_mac.cc | 105 + .../misc/capture_context_test_util_win.cc | 114 + .../util/misc/capture_context_win.asm | 528 + .../util/misc/capture_context_win_arm64.asm | 64 + .../util/misc/capture_context_win_arm64.obj | Bin 0 -> 614 bytes .../external/crashpad/util/misc/clock.h | 48 + .../external/crashpad/util/misc/clock_mac.cc | 45 + .../crashpad/util/misc/clock_posix.cc | 53 + .../external/crashpad/util/misc/clock_test.cc | 99 + .../external/crashpad/util/misc/clock_win.cc | 56 + .../crashpad/util/misc/elf_note_types.h | 34 + .../crashpad/util/misc/from_pointer_cast.h | 115 + .../util/misc/from_pointer_cast_test.cc | 262 + .../crashpad/util/misc/implicit_cast.h | 44 + .../crashpad/util/misc/initialization_state.h | 101 + .../util/misc/initialization_state_dcheck.cc | 41 + .../util/misc/initialization_state_dcheck.h | 195 + .../misc/initialization_state_dcheck_test.cc | 152 + .../util/misc/initialization_state_test.cc | 68 + .../external/crashpad/util/misc/lexing.cc | 75 + .../external/crashpad/util/misc/lexing.h | 44 + .../crashpad/util/misc/memory_sanitizer.h | 27 + .../external/crashpad/util/misc/metrics.cc | 136 + .../external/crashpad/util/misc/metrics.h | 223 + .../crashpad/util/misc/no_cfi_icall.h | 190 + .../crashpad/util/misc/no_cfi_icall_test.cc | 86 + .../external/crashpad/util/misc/paths.h | 41 + .../crashpad/util/misc/paths_fuchsia.cc | 33 + .../crashpad/util/misc/paths_linux.cc | 61 + .../external/crashpad/util/misc/paths_mac.cc | 44 + .../external/crashpad/util/misc/paths_test.cc | 37 + .../external/crashpad/util/misc/paths_win.cc | 42 + .../crashpad/util/misc/pdb_structures.cc | 23 + .../crashpad/util/misc/pdb_structures.h | 148 + .../crashpad/util/misc/random_string.cc | 29 + .../crashpad/util/misc/random_string.h | 31 + .../crashpad/util/misc/random_string_test.cc | 73 + .../external/crashpad/util/misc/range_set.cc | 55 + .../external/crashpad/util/misc/range_set.h | 52 + .../crashpad/util/misc/range_set_test.cc | 116 + .../crashpad/util/misc/reinterpret_bytes.cc | 70 + .../crashpad/util/misc/reinterpret_bytes.h | 48 + .../util/misc/reinterpret_bytes_test.cc | 104 + .../util/misc/scoped_forbid_return.cc | 27 + .../crashpad/util/misc/scoped_forbid_return.h | 55 + .../util/misc/scoped_forbid_return_test.cc | 66 + .../util/misc/symbolic_constants_common.h | 132 + .../external/crashpad/util/misc/time.cc | 54 + .../sentry/external/crashpad/util/misc/time.h | 84 + .../external/crashpad/util/misc/time_linux.cc | 38 + .../external/crashpad/util/misc/time_test.cc | 133 + .../external/crashpad/util/misc/time_win.cc | 93 + .../external/crashpad/util/misc/tri_state.h | 41 + .../external/crashpad/util/misc/uuid.cc | 153 + .../sentry/external/crashpad/util/misc/uuid.h | 104 + .../external/crashpad/util/misc/uuid_test.cc | 276 + .../external/crashpad/util/misc/zlib.cc | 39 + .../sentry/external/crashpad/util/misc/zlib.h | 42 + .../util/net/generate_test_server_key.py | 50 + .../external/crashpad/util/net/http_body.cc | 103 + .../external/crashpad/util/net/http_body.h | 125 + .../crashpad/util/net/http_body_gzip.cc | 126 + .../crashpad/util/net/http_body_gzip.h | 67 + .../crashpad/util/net/http_body_gzip_test.cc | 180 + .../crashpad/util/net/http_body_test.cc | 216 + .../crashpad/util/net/http_body_test_util.cc | 50 + .../crashpad/util/net/http_body_test_util.h | 49 + .../external/crashpad/util/net/http_headers.h | 37 + .../util/net/http_multipart_builder.cc | 215 + .../util/net/http_multipart_builder.h | 105 + .../util/net/http_multipart_builder_test.cc | 297 + .../crashpad/util/net/http_transport.cc | 59 + .../crashpad/util/net/http_transport.h | 121 + .../util/net/http_transport_libcurl.cc | 544 + .../crashpad/util/net/http_transport_mac.mm | 297 + .../util/net/http_transport_socket.cc | 608 + .../crashpad/util/net/http_transport_test.cc | 390 + .../util/net/http_transport_test_server.cc | 134 + .../crashpad/util/net/http_transport_win.cc | 418 + .../util/net/testdata/ascii_http_body.txt | 1 + .../util/net/testdata/binary_http_body.dat | 1 + .../net/testdata/crashpad_util_test_cert.pem | 21 + .../net/testdata/crashpad_util_test_key.pem | 30 + .../sentry/external/crashpad/util/net/tls.gni | 31 + .../sentry/external/crashpad/util/net/url.cc | 92 + .../sentry/external/crashpad/util/net/url.h | 50 + .../external/crashpad/util/net/url_test.cc | 132 + .../util/numeric/checked_address_range.cc | 141 + .../util/numeric/checked_address_range.h | 150 + .../numeric/checked_address_range_test.cc | 262 + .../crashpad/util/numeric/checked_range.h | 140 + .../util/numeric/checked_range_test.cc | 305 + .../util/numeric/checked_vm_address_range.h | 36 + .../crashpad/util/numeric/in_range_cast.h | 44 + .../util/numeric/in_range_cast_test.cc | 124 + .../external/crashpad/util/numeric/int128.h | 52 + .../crashpad/util/numeric/int128_test.cc | 44 + .../crashpad/util/numeric/safe_assignment.h | 44 + .../crashpad/util/posix/close_multiple.cc | 192 + .../crashpad/util/posix/close_multiple.h | 44 + .../crashpad/util/posix/close_stdio.cc | 54 + .../crashpad/util/posix/close_stdio.h | 42 + .../util/posix/double_fork_and_exec.cc | 166 + .../util/posix/double_fork_and_exec.h | 74 + .../crashpad/util/posix/drop_privileges.cc | 93 + .../crashpad/util/posix/drop_privileges.h | 40 + .../crashpad/util/posix/process_info.h | 201 + .../crashpad/util/posix/process_info_linux.cc | 296 + .../crashpad/util/posix/process_info_mac.cc | 243 + .../crashpad/util/posix/process_info_test.cc | 252 + .../crashpad/util/posix/scoped_dir.cc | 30 + .../external/crashpad/util/posix/scoped_dir.h | 39 + .../crashpad/util/posix/scoped_mmap.cc | 181 + .../crashpad/util/posix/scoped_mmap.h | 119 + .../crashpad/util/posix/scoped_mmap_test.cc | 423 + .../external/crashpad/util/posix/signals.cc | 334 + .../external/crashpad/util/posix/signals.h | 246 + .../crashpad/util/posix/signals_test.cc | 649 + .../util/posix/symbolic_constants_posix.cc | 206 + .../util/posix/symbolic_constants_posix.h | 48 + .../posix/symbolic_constants_posix_test.cc | 246 + .../crashpad/util/process/process_id.h | 54 + .../crashpad/util/process/process_memory.cc | 96 + .../crashpad/util/process/process_memory.h | 136 + .../util/process/process_memory_fuchsia.cc | 57 + .../util/process/process_memory_fuchsia.h | 60 + .../util/process/process_memory_linux.cc | 76 + .../util/process/process_memory_linux.h | 55 + .../util/process/process_memory_mac.cc | 136 + .../util/process/process_memory_mac.h | 137 + .../util/process/process_memory_mac_test.cc | 340 + .../util/process/process_memory_native.h | 42 + .../util/process/process_memory_range.cc | 93 + .../util/process/process_memory_range.h | 130 + .../util/process/process_memory_range_test.cc | 101 + .../util/process/process_memory_sanitized.cc | 65 + .../util/process/process_memory_sanitized.h | 63 + .../process/process_memory_sanitized_test.cc | 83 + .../util/process/process_memory_test.cc | 612 + .../util/process/process_memory_win.cc | 118 + .../util/process/process_memory_win.h | 67 + .../crashpad/util/stdlib/aligned_allocator.cc | 76 + .../crashpad/util/stdlib/aligned_allocator.h | 116 + .../util/stdlib/aligned_allocator_test.cc | 117 + .../crashpad/util/stdlib/map_insert.h | 56 + .../crashpad/util/stdlib/map_insert_test.cc | 54 + .../external/crashpad/util/stdlib/objc.h | 39 + .../util/stdlib/string_number_conversion.cc | 190 + .../util/stdlib/string_number_conversion.h | 67 + .../stdlib/string_number_conversion_test.cc | 406 + .../external/crashpad/util/stdlib/strlcpy.cc | 32 + .../external/crashpad/util/stdlib/strlcpy.h | 50 + .../crashpad/util/stdlib/strlcpy_test.cc | 104 + .../external/crashpad/util/stdlib/strnlen.cc | 49 + .../external/crashpad/util/stdlib/strnlen.h | 49 + .../crashpad/util/stdlib/strnlen_test.cc | 45 + .../crashpad/util/stdlib/thread_safe_vector.h | 64 + .../util/stdlib/thread_safe_vector_test.cc | 96 + .../util/stream/base94_output_stream.cc | 174 + .../util/stream/base94_output_stream.h | 80 + .../util/stream/base94_output_stream_test.cc | 290 + .../crashpad/util/stream/file_encoder.cc | 84 + .../crashpad/util/stream/file_encoder.h | 60 + .../crashpad/util/stream/file_encoder_test.cc | 111 + .../util/stream/file_output_stream.cc | 45 + .../crashpad/util/stream/file_output_stream.h | 47 + .../crashpad/util/stream/log_output_stream.cc | 103 + .../crashpad/util/stream/log_output_stream.h | 78 + .../util/stream/log_output_stream_test.cc | 125 + .../util/stream/output_stream_interface.h | 64 + .../util/stream/test_output_stream.cc | 48 + .../crashpad/util/stream/test_output_stream.h | 67 + .../util/stream/zlib_output_stream.cc | 141 + .../crashpad/util/stream/zlib_output_stream.h | 83 + .../util/stream/zlib_output_stream_test.cc | 187 + .../crashpad/util/string/split_string.cc | 59 + .../crashpad/util/string/split_string.h | 50 + .../crashpad/util/string/split_string_test.cc | 95 + .../crashpad/util/synchronization/semaphore.h | 94 + .../util/synchronization/semaphore_mac.cc | 54 + .../util/synchronization/semaphore_posix.cc | 110 + .../util/synchronization/semaphore_test.cc | 150 + .../util/synchronization/semaphore_win.cc | 58 + .../external/crashpad/util/thread/stoppable.h | 38 + .../external/crashpad/util/thread/thread.cc | 28 + .../external/crashpad/util/thread/thread.h | 76 + .../util/thread/thread_log_messages.cc | 88 + .../util/thread/thread_log_messages.h | 49 + .../util/thread/thread_log_messages_test.cc | 168 + .../crashpad/util/thread/thread_posix.cc | 55 + .../crashpad/util/thread/thread_test.cc | 103 + .../crashpad/util/thread/thread_win.cc | 44 + .../crashpad/util/thread/worker_thread.cc | 97 + .../crashpad/util/thread/worker_thread.h | 104 + .../util/thread/worker_thread_test.cc | 164 + .../crashpad/util/win/address_types.h | 32 + .../util/win/checked_win_address_range.h | 36 + .../crashpad/util/win/command_line.cc | 60 + .../external/crashpad/util/win/command_line.h | 38 + .../crashpad/util/win/command_line_test.cc | 168 + .../crashpad/util/win/context_wrappers.h | 40 + .../win/critical_section_with_debug_info.cc | 72 + .../win/critical_section_with_debug_info.h | 34 + .../critical_section_with_debug_info_test.cc | 34 + .../crashpad/util/win/exception_codes.h | 37 + .../util/win/exception_handler_server.cc | 578 + .../util/win/exception_handler_server.h | 140 + .../util/win/exception_handler_server_test.cc | 223 + .../crashpad/util/win/get_function.cc | 46 + .../external/crashpad/util/win/get_function.h | 121 + .../crashpad/util/win/get_function_test.cc | 78 + .../util/win/get_module_information.cc | 34 + .../util/win/get_module_information.h | 35 + .../external/crashpad/util/win/handle.cc | 37 + .../external/crashpad/util/win/handle.h | 65 + .../external/crashpad/util/win/handle_test.cc | 53 + .../crashpad/util/win/initial_client_data.cc | 113 + .../crashpad/util/win/initial_client_data.h | 116 + .../util/win/initial_client_data_test.cc | 73 + .../external/crashpad/util/win/loader_lock.cc | 52 + .../external/crashpad/util/win/loader_lock.h | 25 + .../crashpad/util/win/loader_lock_test.cc | 36 + .../crashpad/util/win/loader_lock_test_dll.cc | 41 + .../crashpad/util/win/module_version.cc | 57 + .../crashpad/util/win/module_version.h | 45 + .../crashpad/util/win/nt_internals.cc | 175 + .../external/crashpad/util/win/nt_internals.h | 99 + .../crashpad/util/win/ntstatus_logging.cc | 75 + .../crashpad/util/win/ntstatus_logging.h | 99 + .../crashpad/util/win/process_info.cc | 718 ++ .../external/crashpad/util/win/process_info.h | 222 + .../crashpad/util/win/process_info_test.cc | 651 + .../util/win/process_info_test_child.cc | 109 + .../crashpad/util/win/process_structs.h | 515 + .../util/win/registration_protocol_win.cc | 270 + .../util/win/registration_protocol_win.h | 176 + .../win/registration_protocol_win_test.cc | 158 + .../util/win/safe_terminate_process.asm | 74 + .../util/win/safe_terminate_process.h | 55 + .../util/win/safe_terminate_process_broken.cc | 34 + .../util/win/safe_terminate_process_test.cc | 187 + .../win/safe_terminate_process_test_child.cc | 22 + .../crashpad/util/win/scoped_handle.cc | 36 + .../crashpad/util/win/scoped_handle.h | 52 + .../crashpad/util/win/scoped_local_alloc.cc | 28 + .../crashpad/util/win/scoped_local_alloc.h | 38 + .../util/win/scoped_process_suspend.cc | 49 + .../util/win/scoped_process_suspend.h | 57 + .../util/win/scoped_process_suspend_test.cc | 121 + .../crashpad/util/win/scoped_registry_key.h | 34 + .../crashpad/util/win/scoped_set_event.cc | 41 + .../crashpad/util/win/scoped_set_event.h | 49 + .../crashpad/util/win/session_end_watcher.cc | 229 + .../crashpad/util/win/session_end_watcher.h | 79 + .../util/win/session_end_watcher_test.cc | 63 + .../crashpad/util/win/termination_codes.h | 37 + .../external/crashpad/util/win/traits.h | 32 + .../external/crashpad/util/win/xp_compat.h | 40 + .../.github/workflows/build.yml | 55 + .../external/libunwindstack-ndk/.gitignore | 2 + .../external/libunwindstack-ndk/ArmExidx.cpp | 862 ++ .../external/libunwindstack-ndk/ArmExidx.h | 126 + .../libunwindstack-ndk/AsmGetRegsX86.S | 62 + .../libunwindstack-ndk/AsmGetRegsX86_64.S | 62 + .../external/libunwindstack-ndk/Check.h | 34 + .../external/libunwindstack-ndk/DexFile.cpp | 146 + .../external/libunwindstack-ndk/DexFile.h | 71 + .../external/libunwindstack-ndk/DexFiles.cpp | 198 + .../external/libunwindstack-ndk/DwarfCfa.cpp | 768 ++ .../external/libunwindstack-ndk/DwarfCfa.h | 274 + .../libunwindstack-ndk/DwarfDebugFrame.h | 50 + .../libunwindstack-ndk/DwarfEhFrame.h | 49 + .../DwarfEhFrameWithHdr.cpp | 225 + .../libunwindstack-ndk/DwarfEhFrameWithHdr.h | 86 + .../libunwindstack-ndk/DwarfEncoding.h | 51 + .../libunwindstack-ndk/DwarfMemory.cpp | 252 + .../external/libunwindstack-ndk/DwarfOp.cpp | 1941 +++ .../external/libunwindstack-ndk/DwarfOp.h | 144 + .../libunwindstack-ndk/DwarfSection.cpp | 830 ++ .../external/libunwindstack-ndk/Elf.cpp | 421 + .../libunwindstack-ndk/ElfInterface.cpp | 702 ++ .../libunwindstack-ndk/ElfInterfaceArm.cpp | 185 + .../libunwindstack-ndk/ElfInterfaceArm.h | 98 + .../external/libunwindstack-ndk/Global.cpp | 99 + .../external/libunwindstack-ndk/JitDebug.cpp | 220 + .../external/libunwindstack-ndk/LICENSE | 232 + .../libunwindstack-ndk/LocalUnwinder.cpp | 147 + .../external/libunwindstack-ndk/Log.cpp | 59 + .../external/libunwindstack-ndk/MapInfo.cpp | 375 + .../external/libunwindstack-ndk/Maps.cpp | 213 + .../external/libunwindstack-ndk/Memory.cpp | 523 + .../libunwindstack-ndk/MemoryBuffer.h | 57 + .../external/libunwindstack-ndk/MemoryCache.h | 51 + .../libunwindstack-ndk/MemoryFileAtOffset.h | 47 + .../external/libunwindstack-ndk/MemoryLocal.h | 39 + .../external/libunwindstack-ndk/MemoryMte.cpp | 35 + .../libunwindstack-ndk/MemoryOffline.h | 60 + .../libunwindstack-ndk/MemoryOfflineBuffer.h | 43 + .../external/libunwindstack-ndk/MemoryRange.h | 66 + .../libunwindstack-ndk/MemoryRemote.h | 46 + .../sentry/external/libunwindstack-ndk/OWNERS | 1 + .../external/libunwindstack-ndk/README.md | 7 + .../external/libunwindstack-ndk/Regs.cpp | 162 + .../external/libunwindstack-ndk/RegsArm.cpp | 174 + .../external/libunwindstack-ndk/RegsArm64.cpp | 195 + .../external/libunwindstack-ndk/RegsInfo.h | 68 + .../external/libunwindstack-ndk/RegsX86.cpp | 179 + .../libunwindstack-ndk/RegsX86_64.cpp | 168 + .../external/libunwindstack-ndk/Symbols.cpp | 173 + .../external/libunwindstack-ndk/Symbols.h | 73 + .../libunwindstack-ndk/ThreadEntry.cpp | 102 + .../external/libunwindstack-ndk/ThreadEntry.h | 78 + .../libunwindstack-ndk/ThreadUnwinder.cpp | 182 + .../external/libunwindstack-ndk/Unwinder.cpp | 478 + .../libunwindstack-ndk/android-base/README.md | 3 + .../android-base/errno_restorer.h | 42 + .../libunwindstack-ndk/android-base/file.cpp | 397 + .../libunwindstack-ndk/android-base/file.h | 125 + .../android-base/log_main.h | 392 + .../libunwindstack-ndk/android-base/logging.h | 480 + .../libunwindstack-ndk/android-base/macros.h | 146 + .../libunwindstack-ndk/android-base/off64_t.h | 22 + .../android-base/parseint.h | 136 + .../android-base/stringprintf.cpp | 85 + .../android-base/stringprintf.h | 56 + .../android-base/strings.cpp | 139 + .../libunwindstack-ndk/android-base/strings.h | 94 + .../libunwindstack-ndk/android-base/threads.h | 30 + .../android-base/unique_fd.h | 272 + .../libunwindstack-ndk/android-base/utf8.h | 104 + .../libunwindstack-ndk/cmake/CMakeLists.txt | 104 + .../libunwindstack-ndk/compat/string.h | 12 + .../include/unwindstack/Arch.h | 44 + .../include/unwindstack/DexFiles.h | 79 + .../include/unwindstack/DwarfError.h | 44 + .../include/unwindstack/DwarfLocation.h | 54 + .../include/unwindstack/DwarfMemory.h | 76 + .../include/unwindstack/DwarfSection.h | 177 + .../include/unwindstack/DwarfStructs.h | 55 + .../include/unwindstack/Elf.h | 130 + .../include/unwindstack/ElfInterface.h | 223 + .../include/unwindstack/Error.h | 81 + .../include/unwindstack/Global.h | 63 + .../include/unwindstack/JitDebug.h | 71 + .../include/unwindstack/LocalUnwinder.h | 86 + .../include/unwindstack/Log.h | 30 + .../include/unwindstack/MachineArm.h | 50 + .../include/unwindstack/MachineArm64.h | 74 + .../include/unwindstack/MachineX86.h | 51 + .../include/unwindstack/MachineX86_64.h | 52 + .../include/unwindstack/MapInfo.h | 132 + .../include/unwindstack/Maps.h | 136 + .../include/unwindstack/Memory.h | 62 + .../include/unwindstack/Regs.h | 118 + .../include/unwindstack/RegsArm.h | 60 + .../include/unwindstack/RegsArm64.h | 75 + .../include/unwindstack/RegsGetLocal.h | 97 + .../include/unwindstack/RegsX86.h | 63 + .../include/unwindstack/RegsX86_64.h | 63 + .../include/unwindstack/UcontextArm.h | 63 + .../include/unwindstack/UcontextArm64.h | 69 + .../include/unwindstack/UcontextX86.h | 77 + .../include/unwindstack/UcontextX86_64.h | 82 + .../include/unwindstack/Unwinder.h | 207 + .../include/unwindstack/UserArm.h | 40 + .../include/unwindstack/UserArm64.h | 43 + .../include/unwindstack/UserX86.h | 56 + .../include/unwindstack/UserX86_64.h | 66 + .../libunwindstack-ndk/procinfo/README.md | 3 + .../libunwindstack-ndk/procinfo/process.h | 124 + .../libunwindstack-ndk/procinfo/process_map.h | 311 + .../external/libunwindstack-ndk/unistdfix.h | 7 + .../external/third_party/lss/.gitignore | 3 + .../sentry/external/third_party/lss/README.md | 137 + .../third_party/lss/codereview.settings | 5 + .../third_party/lss/linux_syscall_support.h | 4533 +++++++ .../external/third_party/lss/tests/.gitignore | 4 + .../external/third_party/lss/tests/Makefile | 131 + .../external/third_party/lss/tests/README.md | 52 + .../third_party/lss/tests/fallocate.c | 67 + .../third_party/lss/tests/sigtimedwait.c | 58 + .../third_party/lss/tests/test_skel.h | 70 + .../external/third_party/lss/tests/unlink.c | 48 + .../sentry/fuzzing-examples/macos-event.json | 1 + .../fuzzing-examples/macos-session.json | 1 + shared/sentry/include/sentry.h | 1745 +++ shared/sentry/sentry-config.cmake.in | 20 + shared/sentry/src/CMakeLists.txt | 155 + .../src/backends/sentry_backend_breakpad.cpp | 262 + .../src/backends/sentry_backend_crashpad.cpp | 477 + .../src/backends/sentry_backend_inproc.c | 343 + .../sentry/src/backends/sentry_backend_none.c | 7 + .../integrations/sentry_integration_qt.cpp | 59 + .../src/integrations/sentry_integration_qt.h | 15 + .../modulefinder/sentry_modulefinder_aix.c | 109 + .../modulefinder/sentry_modulefinder_apple.c | 169 + .../modulefinder/sentry_modulefinder_linux.c | 707 ++ .../modulefinder/sentry_modulefinder_linux.h | 54 + .../sentry_modulefinder_windows.c | 140 + shared/sentry/src/path/sentry_path.c | 48 + shared/sentry/src/path/sentry_path_unix.c | 505 + shared/sentry/src/path/sentry_path_windows.c | 538 + shared/sentry/src/sentry_alloc.c | 34 + shared/sentry/src/sentry_alloc.h | 11 + shared/sentry/src/sentry_backend.c | 13 + shared/sentry/src/sentry_backend.h | 43 + shared/sentry/src/sentry_boot.h | 43 + shared/sentry/src/sentry_core.c | 1027 ++ shared/sentry/src/sentry_core.h | 124 + shared/sentry/src/sentry_database.c | 295 + shared/sentry/src/sentry_database.h | 88 + shared/sentry/src/sentry_envelope.c | 505 + shared/sentry/src/sentry_envelope.h | 105 + shared/sentry/src/sentry_json.c | 560 + shared/sentry/src/sentry_json.h | 100 + shared/sentry/src/sentry_logger.c | 95 + shared/sentry/src/sentry_logger.h | 37 + shared/sentry/src/sentry_options.c | 427 + shared/sentry/src/sentry_options.h | 77 + shared/sentry/src/sentry_os.c | 228 + shared/sentry/src/sentry_os.h | 8 + shared/sentry/src/sentry_path.h | 229 + shared/sentry/src/sentry_random.c | 98 + shared/sentry/src/sentry_random.h | 11 + shared/sentry/src/sentry_ratelimiter.c | 117 + shared/sentry/src/sentry_ratelimiter.h | 59 + shared/sentry/src/sentry_scope.c | 361 + shared/sentry/src/sentry_scope.h | 102 + shared/sentry/src/sentry_session.c | 299 + shared/sentry/src/sentry_session.h | 78 + shared/sentry/src/sentry_slice.c | 95 + shared/sentry/src/sentry_slice.h | 92 + shared/sentry/src/sentry_string.c | 136 + shared/sentry/src/sentry_string.h | 189 + shared/sentry/src/sentry_symbolizer.h | 21 + shared/sentry/src/sentry_sync.c | 508 + shared/sentry/src/sentry_sync.h | 436 + shared/sentry/src/sentry_tracing.c | 520 + shared/sentry/src/sentry_tracing.h | 51 + shared/sentry/src/sentry_transport.c | 224 + shared/sentry/src/sentry_transport.h | 92 + shared/sentry/src/sentry_unix_pageallocator.c | 142 + shared/sentry/src/sentry_unix_pageallocator.h | 32 + shared/sentry/src/sentry_unix_spinlock.h | 27 + shared/sentry/src/sentry_utils.c | 531 + shared/sentry/src/sentry_utils.h | 201 + shared/sentry/src/sentry_uuid.c | 149 + shared/sentry/src/sentry_uuid.h | 23 + shared/sentry/src/sentry_value.c | 1211 ++ shared/sentry/src/sentry_value.h | 124 + shared/sentry/src/sentry_windows_dbghelp.c | 24 + shared/sentry/src/sentry_windows_dbghelp.h | 12 + .../src/symbolizer/sentry_symbolizer_unix.c | 217 + .../symbolizer/sentry_symbolizer_windows.c | 40 + .../src/transports/sentry_disk_transport.c | 28 + .../src/transports/sentry_disk_transport.h | 14 + .../transports/sentry_function_transport.c | 42 + .../src/transports/sentry_transport_curl.c | 274 + .../src/transports/sentry_transport_none.c | 8 + .../src/transports/sentry_transport_winhttp.c | 348 + shared/sentry/src/unwinder/sentry_unwinder.c | 46 + .../src/unwinder/sentry_unwinder_dbghelp.c | 73 + .../unwinder/sentry_unwinder_libbacktrace.c | 35 + .../sentry_unwinder_libunwindstack.cpp | 57 + shared/sentry/tests/__init__.py | 288 + shared/sentry/tests/assertions.py | 212 + shared/sentry/tests/cmake.py | 239 + shared/sentry/tests/conditions.py | 30 + shared/sentry/tests/conftest.py | 30 + shared/sentry/tests/fixtures/README.md | 24 + shared/sentry/tests/fixtures/libstdc++.so | Bin 0 -> 11408 bytes shared/sentry/tests/fixtures/sentry_example | Bin 0 -> 17016 bytes shared/sentry/tests/fixtures/test.c | 4 + shared/sentry/tests/fixtures/with-buildid.so | Bin 0 -> 15552 bytes .../sentry/tests/fixtures/without-buildid.so | Bin 0 -> 15448 bytes ...id-000000,sig-06,src-000560,op-havoc,rep-2 | 1 + ...,sig-11,src-000033+000005,op-splice,rep-16 | Bin 0 -> 1278 bytes ...id-000000,sig-11,src-000320,op-havoc,rep-2 | Bin 0 -> 238 bytes ...0,sig-11,src-000494+000073,op-splice,rep-8 | Bin 0 -> 59 bytes ...,sig-11,src-000052+000222,op-splice,rep-32 | Bin 0 -> 1747 bytes ...,sig-11,src-000215+000415,op-splice,rep-16 | 1 + ...,sig-11,src-000339+000110,op-splice,rep-16 | 1 + ...,sig-11,src-000447+000419,op-splice,rep-32 | Bin 0 -> 460 bytes ...,sig-11,src-000399+000468,op-splice,rep-32 | Bin 0 -> 312 bytes ...4,sig-11,src-000440+000369,op-splice,rep-2 | Bin 0 -> 146 bytes shared/sentry/tests/leaks.txt | 5 + shared/sentry/tests/requirements.txt | 3 + shared/sentry/tests/test_build_static.py | 67 + .../sentry/tests/test_integration_crashpad.py | 137 + shared/sentry/tests/test_integration_http.py | 474 + .../tests/test_integration_ratelimits.py | 56 + .../sentry/tests/test_integration_stdout.py | 162 + shared/sentry/tests/test_unit.py | 19 + shared/sentry/tests/unit/.gitattributes | 2 + shared/sentry/tests/unit/CMakeLists.txt | 113 + shared/sentry/tests/unit/fuzz.c | 75 + shared/sentry/tests/unit/main.c | 16 + shared/sentry/tests/unit/sentry_testsupport.h | 53 + shared/sentry/tests/unit/test_attachments.c | 93 + shared/sentry/tests/unit/test_basic.c | 167 + shared/sentry/tests/unit/test_concurrency.c | 124 + shared/sentry/tests/unit/test_consent.c | 55 + shared/sentry/tests/unit/test_envelopes.c | 169 + shared/sentry/tests/unit/test_failures.c | 28 + shared/sentry/tests/unit/test_fuzzfailures.c | 66 + shared/sentry/tests/unit/test_logger.c | 49 + shared/sentry/tests/unit/test_modulefinder.c | 206 + shared/sentry/tests/unit/test_mpack.c | 64 + shared/sentry/tests/unit/test_path.c | 198 + shared/sentry/tests/unit/test_ratelimiter.c | 36 + shared/sentry/tests/unit/test_sampling.c | 37 + shared/sentry/tests/unit/test_session.c | 146 + shared/sentry/tests/unit/test_slice.c | 20 + shared/sentry/tests/unit/test_symbolizer.c | 41 + shared/sentry/tests/unit/test_sync.c | 153 + shared/sentry/tests/unit/test_tracing.c | 811 ++ shared/sentry/tests/unit/test_uninit.c | 76 + shared/sentry/tests/unit/test_unwinder.c | 76 + shared/sentry/tests/unit/test_utils.c | 223 + shared/sentry/tests/unit/test_uuid.c | 48 + shared/sentry/tests/unit/test_value.c | 548 + shared/sentry/tests/unit/tests.inc | 91 + shared/sentry/tests/valgrind.txt | 29 + .../toolchains/msys2-mingw64-pkglist.txt | 5 + shared/sentry/toolchains/msys2.cmake | 33 + shared/sentry/vendor/acutest.h | 1671 +++ shared/sentry/vendor/jsmn.h | 471 + shared/sentry/vendor/mpack.c | 6440 ++++++++++ shared/sentry/vendor/mpack.h | 7152 +++++++++++ shared/sentry/vendor/stb_sprintf.c | 2 + shared/sentry/vendor/stb_sprintf.h | 1879 +++ shared/signalhandler.h | 104 - web/src/documentation/01_usage.rst | 18 +- web/src/documentation/api/01_general.rst | 2 +- web/src/documentation/api/02_apps.rst | 4 +- web/src/documentation/api/03_notification.rst | 6 +- web/src/documentation/api/04_screenshot.rst | 6 +- web/src/documentation/api/05_wifi.rst | 2 +- web/src/faq.rst | 101 + web/src/sitemap.rst | 1 + 2454 files changed, 537834 insertions(+), 1782 deletions(-) create mode 100644 applications/settings-manager/json.h create mode 100644 applications/settings-manager/slothandler.h delete mode 100644 shared/devicesettings.cpp delete mode 100644 shared/devicesettings.h delete mode 100644 shared/eventfilter.cpp delete mode 100644 shared/eventfilter.h create mode 100644 shared/liboxide/.gitignore create mode 100644 shared/liboxide/eventfilter.cpp create mode 100644 shared/liboxide/eventfilter.h create mode 100644 shared/liboxide/liboxide.cpp create mode 100644 shared/liboxide/liboxide.h create mode 100644 shared/liboxide/liboxide.pro create mode 100644 shared/liboxide/liboxide_global.h create mode 100644 shared/liboxide/settingsfile.cpp create mode 100644 shared/liboxide/settingsfile.h rename applications/task-switcher/signalhandler.h => shared/liboxide/signalhandler.cpp (72%) create mode 100644 shared/liboxide/signalhandler.h create mode 100644 shared/sentry/.vscode/extensions.json create mode 100644 shared/sentry/.vscode/settings.json create mode 100644 shared/sentry/CHANGELOG.md create mode 100644 shared/sentry/CMakeLists.txt create mode 100644 shared/sentry/CONTRIBUTING.md create mode 100644 shared/sentry/LICENSE create mode 100644 shared/sentry/README.md create mode 100644 shared/sentry/examples/example.c create mode 100644 shared/sentry/external/CMakeLists.txt create mode 100644 shared/sentry/external/breakpad/.clang-format create mode 100644 shared/sentry/external/breakpad/.github/workflows/build-test-ci.yml create mode 100644 shared/sentry/external/breakpad/.github/workflows/close-pull-request.yml create mode 100644 shared/sentry/external/breakpad/.github/workflows/coverity.yml create mode 100644 shared/sentry/external/breakpad/.gitignore create mode 100644 shared/sentry/external/breakpad/AUTHORS create mode 100644 shared/sentry/external/breakpad/ChangeLog create mode 100644 shared/sentry/external/breakpad/DEPS create mode 100644 shared/sentry/external/breakpad/DIR_METADATA create mode 100644 shared/sentry/external/breakpad/INSTALL create mode 100644 shared/sentry/external/breakpad/LICENSE create mode 100644 shared/sentry/external/breakpad/Makefile.am create mode 100644 shared/sentry/external/breakpad/Makefile.in create mode 100644 shared/sentry/external/breakpad/NEWS create mode 100644 shared/sentry/external/breakpad/OWNERS create mode 100644 shared/sentry/external/breakpad/README.ANDROID create mode 100644 shared/sentry/external/breakpad/README.md create mode 100644 shared/sentry/external/breakpad/aclocal.m4 create mode 100755 shared/sentry/external/breakpad/android/common-functions.sh create mode 100644 shared/sentry/external/breakpad/android/google_breakpad/Android.mk create mode 100755 shared/sentry/external/breakpad/android/run-checks.sh create mode 100644 shared/sentry/external/breakpad/android/sample_app/README create mode 100644 shared/sentry/external/breakpad/android/sample_app/jni/Android.mk create mode 100644 shared/sentry/external/breakpad/android/sample_app/jni/Application.mk create mode 100644 shared/sentry/external/breakpad/android/sample_app/jni/test_breakpad.cpp create mode 100755 shared/sentry/external/breakpad/android/test-driver create mode 100755 shared/sentry/external/breakpad/android/test-shell.sh create mode 100644 shared/sentry/external/breakpad/appveyor.yml create mode 100755 shared/sentry/external/breakpad/autotools/ar-lib create mode 100755 shared/sentry/external/breakpad/autotools/compile create mode 100755 shared/sentry/external/breakpad/autotools/config.guess create mode 100755 shared/sentry/external/breakpad/autotools/config.sub create mode 100755 shared/sentry/external/breakpad/autotools/depcomp create mode 100755 shared/sentry/external/breakpad/autotools/install-sh create mode 100755 shared/sentry/external/breakpad/autotools/ltmain.sh create mode 100755 shared/sentry/external/breakpad/autotools/missing create mode 100755 shared/sentry/external/breakpad/autotools/root-test-driver create mode 100755 shared/sentry/external/breakpad/autotools/test-driver create mode 100644 shared/sentry/external/breakpad/breakpad-client.pc.in create mode 100644 shared/sentry/external/breakpad/breakpad.pc.in create mode 100644 shared/sentry/external/breakpad/codereview.settings create mode 100755 shared/sentry/external/breakpad/configure create mode 100644 shared/sentry/external/breakpad/configure.ac create mode 100644 shared/sentry/external/breakpad/default.xml create mode 100644 shared/sentry/external/breakpad/docs/OWNERS create mode 100644 shared/sentry/external/breakpad/docs/breakpad.png create mode 100644 shared/sentry/external/breakpad/docs/breakpad.svg create mode 100644 shared/sentry/external/breakpad/docs/client_design.md create mode 100644 shared/sentry/external/breakpad/docs/contributing_to_breakpad.md create mode 100644 shared/sentry/external/breakpad/docs/exception_handling.md create mode 100644 shared/sentry/external/breakpad/docs/getting_started_with_breakpad.md create mode 100644 shared/sentry/external/breakpad/docs/linux_core_handler.md create mode 100644 shared/sentry/external/breakpad/docs/linux_starter_guide.md create mode 100644 shared/sentry/external/breakpad/docs/linux_system_calls.md create mode 100644 shared/sentry/external/breakpad/docs/mac_breakpad_starter_guide.md create mode 100644 shared/sentry/external/breakpad/docs/mozilla_brown_bag_talk.md create mode 100644 shared/sentry/external/breakpad/docs/processor_design.md create mode 100644 shared/sentry/external/breakpad/docs/stack_walking.md create mode 100644 shared/sentry/external/breakpad/docs/sym_upload_v2_protocol.md create mode 100644 shared/sentry/external/breakpad/docs/symbol_files.md create mode 100644 shared/sentry/external/breakpad/docs/windows_client_integration.md create mode 100644 shared/sentry/external/breakpad/m4/ax_append_compile_flags.m4 create mode 100644 shared/sentry/external/breakpad/m4/ax_append_flag.m4 create mode 100644 shared/sentry/external/breakpad/m4/ax_check_compile_flag.m4 create mode 100644 shared/sentry/external/breakpad/m4/ax_check_define.m4 create mode 100644 shared/sentry/external/breakpad/m4/ax_cxx_compile_stdcxx.m4 create mode 100644 shared/sentry/external/breakpad/m4/ax_pthread.m4 create mode 100644 shared/sentry/external/breakpad/m4/ax_require_defined.m4 create mode 100644 shared/sentry/external/breakpad/m4/libtool.m4 create mode 100644 shared/sentry/external/breakpad/m4/ltoptions.m4 create mode 100644 shared/sentry/external/breakpad/m4/ltsugar.m4 create mode 100644 shared/sentry/external/breakpad/m4/ltversion.m4 create mode 100644 shared/sentry/external/breakpad/m4/lt~obsolete.m4 create mode 100644 shared/sentry/external/breakpad/src/breakpad_googletest_includes.h create mode 100644 shared/sentry/external/breakpad/src/build/all.gyp create mode 100644 shared/sentry/external/breakpad/src/build/common.gypi create mode 100644 shared/sentry/external/breakpad/src/build/filename_rules.gypi create mode 100755 shared/sentry/external/breakpad/src/build/gyp_breakpad create mode 100644 shared/sentry/external/breakpad/src/build/testing.gyp create mode 100644 shared/sentry/external/breakpad/src/client/apple/Framework/BreakpadDefines.h create mode 100644 shared/sentry/external/breakpad/src/client/ios/Breakpad.h create mode 100644 shared/sentry/external/breakpad/src/client/ios/Breakpad.mm create mode 100644 shared/sentry/external/breakpad/src/client/ios/Breakpad.xcodeproj/project.pbxproj create mode 100644 shared/sentry/external/breakpad/src/client/ios/BreakpadController.h create mode 100644 shared/sentry/external/breakpad/src/client/ios/BreakpadController.mm create mode 100644 shared/sentry/external/breakpad/src/client/ios/Breakpad_Prefix.pch create mode 100644 shared/sentry/external/breakpad/src/client/ios/exception_handler_no_mach.cc create mode 100644 shared/sentry/external/breakpad/src/client/ios/exception_handler_no_mach.h create mode 100644 shared/sentry/external/breakpad/src/client/ios/handler/ios_exception_minidump_generator.h create mode 100644 shared/sentry/external/breakpad/src/client/ios/handler/ios_exception_minidump_generator.mm create mode 100644 shared/sentry/external/breakpad/src/client/linux/crash_generation/client_info.h create mode 100644 shared/sentry/external/breakpad/src/client/linux/crash_generation/crash_generation_client.cc create mode 100644 shared/sentry/external/breakpad/src/client/linux/crash_generation/crash_generation_client.h create mode 100644 shared/sentry/external/breakpad/src/client/linux/crash_generation/crash_generation_server.cc create mode 100644 shared/sentry/external/breakpad/src/client/linux/crash_generation/crash_generation_server.h create mode 100644 shared/sentry/external/breakpad/src/client/linux/data/linux-gate-amd.sym create mode 100644 shared/sentry/external/breakpad/src/client/linux/data/linux-gate-intel.sym create mode 100644 shared/sentry/external/breakpad/src/client/linux/dump_writer_common/mapping_info.h create mode 100644 shared/sentry/external/breakpad/src/client/linux/dump_writer_common/raw_context_cpu.h create mode 100644 shared/sentry/external/breakpad/src/client/linux/dump_writer_common/thread_info.cc create mode 100644 shared/sentry/external/breakpad/src/client/linux/dump_writer_common/thread_info.h create mode 100644 shared/sentry/external/breakpad/src/client/linux/dump_writer_common/ucontext_reader.cc create mode 100644 shared/sentry/external/breakpad/src/client/linux/dump_writer_common/ucontext_reader.h create mode 100644 shared/sentry/external/breakpad/src/client/linux/handler/exception_handler.cc create mode 100644 shared/sentry/external/breakpad/src/client/linux/handler/exception_handler.h create mode 100644 shared/sentry/external/breakpad/src/client/linux/handler/exception_handler_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/client/linux/handler/microdump_extra_info.h create mode 100644 shared/sentry/external/breakpad/src/client/linux/handler/minidump_descriptor.cc create mode 100644 shared/sentry/external/breakpad/src/client/linux/handler/minidump_descriptor.h create mode 100644 shared/sentry/external/breakpad/src/client/linux/log/log.cc create mode 100644 shared/sentry/external/breakpad/src/client/linux/log/log.h create mode 100644 shared/sentry/external/breakpad/src/client/linux/microdump_writer/microdump_writer.cc create mode 100644 shared/sentry/external/breakpad/src/client/linux/microdump_writer/microdump_writer.h create mode 100644 shared/sentry/external/breakpad/src/client/linux/microdump_writer/microdump_writer_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/client/linux/minidump_writer/cpu_set.h create mode 100644 shared/sentry/external/breakpad/src/client/linux/minidump_writer/cpu_set_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/client/linux/minidump_writer/directory_reader.h create mode 100644 shared/sentry/external/breakpad/src/client/linux/minidump_writer/directory_reader_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/client/linux/minidump_writer/line_reader.h create mode 100644 shared/sentry/external/breakpad/src/client/linux/minidump_writer/line_reader_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_core_dumper.cc create mode 100644 shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_core_dumper.h create mode 100644 shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_core_dumper_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_dumper.cc create mode 100644 shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_dumper.h create mode 100644 shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc create mode 100644 shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper.cc create mode 100644 shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper.h create mode 100644 shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/client/linux/minidump_writer/minidump_writer.cc create mode 100644 shared/sentry/external/breakpad/src/client/linux/minidump_writer/minidump_writer.h create mode 100644 shared/sentry/external/breakpad/src/client/linux/minidump_writer/minidump_writer_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc create mode 100644 shared/sentry/external/breakpad/src/client/linux/minidump_writer/minidump_writer_unittest_utils.h create mode 100644 shared/sentry/external/breakpad/src/client/linux/minidump_writer/proc_cpuinfo_reader.h create mode 100644 shared/sentry/external/breakpad/src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/client/linux/sender/google_crash_report_sender.cc create mode 100644 shared/sentry/external/breakpad/src/client/mac/Breakpad.xcodeproj/project.pbxproj create mode 100644 shared/sentry/external/breakpad/src/client/mac/Framework/Breakpad.h create mode 100644 shared/sentry/external/breakpad/src/client/mac/Framework/Breakpad.mm create mode 100644 shared/sentry/external/breakpad/src/client/mac/Framework/Breakpad_Prefix.pch create mode 100644 shared/sentry/external/breakpad/src/client/mac/Framework/Info.plist create mode 100644 shared/sentry/external/breakpad/src/client/mac/Framework/OnDemandServer.h create mode 100644 shared/sentry/external/breakpad/src/client/mac/Framework/OnDemandServer.mm create mode 100644 shared/sentry/external/breakpad/src/client/mac/UnitTests-Info.plist create mode 100644 shared/sentry/external/breakpad/src/client/mac/crash_generation/ConfigFile.h create mode 100644 shared/sentry/external/breakpad/src/client/mac/crash_generation/ConfigFile.mm create mode 100644 shared/sentry/external/breakpad/src/client/mac/crash_generation/Inspector.h create mode 100644 shared/sentry/external/breakpad/src/client/mac/crash_generation/Inspector.mm create mode 100644 shared/sentry/external/breakpad/src/client/mac/crash_generation/InspectorMain.mm create mode 100644 shared/sentry/external/breakpad/src/client/mac/crash_generation/client_info.h create mode 100644 shared/sentry/external/breakpad/src/client/mac/crash_generation/crash_generation_client.cc create mode 100644 shared/sentry/external/breakpad/src/client/mac/crash_generation/crash_generation_client.h create mode 100644 shared/sentry/external/breakpad/src/client/mac/crash_generation/crash_generation_server.cc create mode 100644 shared/sentry/external/breakpad/src/client/mac/crash_generation/crash_generation_server.h create mode 100644 shared/sentry/external/breakpad/src/client/mac/handler/breakpad_nlist_64.cc create mode 100644 shared/sentry/external/breakpad/src/client/mac/handler/breakpad_nlist_64.h create mode 100644 shared/sentry/external/breakpad/src/client/mac/handler/dynamic_images.cc create mode 100644 shared/sentry/external/breakpad/src/client/mac/handler/dynamic_images.h create mode 100644 shared/sentry/external/breakpad/src/client/mac/handler/exception_handler.cc create mode 100644 shared/sentry/external/breakpad/src/client/mac/handler/exception_handler.h create mode 100644 shared/sentry/external/breakpad/src/client/mac/handler/mach_vm_compat.h create mode 100644 shared/sentry/external/breakpad/src/client/mac/handler/minidump_generator.cc create mode 100644 shared/sentry/external/breakpad/src/client/mac/handler/minidump_generator.h create mode 100644 shared/sentry/external/breakpad/src/client/mac/handler/minidump_test.xcodeproj/project.pbxproj create mode 100644 shared/sentry/external/breakpad/src/client/mac/handler/minidump_tests32-Info.plist create mode 100644 shared/sentry/external/breakpad/src/client/mac/handler/minidump_tests64-Info.plist create mode 100644 shared/sentry/external/breakpad/src/client/mac/handler/obj-cTestCases-Info.plist create mode 100644 shared/sentry/external/breakpad/src/client/mac/handler/protected_memory_allocator.cc create mode 100644 shared/sentry/external/breakpad/src/client/mac/handler/protected_memory_allocator.h create mode 100644 shared/sentry/external/breakpad/src/client/mac/handler/testcases/DynamicImagesTests.cc create mode 100644 shared/sentry/external/breakpad/src/client/mac/handler/testcases/DynamicImagesTests.h create mode 100644 shared/sentry/external/breakpad/src/client/mac/handler/testcases/breakpad_nlist_test.cc create mode 100644 shared/sentry/external/breakpad/src/client/mac/handler/testcases/breakpad_nlist_test.h create mode 100644 shared/sentry/external/breakpad/src/client/mac/handler/testcases/dwarftests.h create mode 100644 shared/sentry/external/breakpad/src/client/mac/handler/testcases/dwarftests.mm create mode 100644 shared/sentry/external/breakpad/src/client/mac/handler/testcases/testdata/dump_syms_dwarf_data create mode 100644 shared/sentry/external/breakpad/src/client/mac/handler/testcases/testdata/dump_syms_i386_breakpad.sym create mode 100644 shared/sentry/external/breakpad/src/client/mac/handler/ucontext_compat.h create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/Breakpad.xib create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/English.lproj/InfoPlist.strings create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/English.lproj/Localizable.strings create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/ReporterIcon.graffle create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/crash_report_sender-Info.plist create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/crash_report_sender.h create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/crash_report_sender.icns create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/crash_report_sender.m create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/da.lproj/InfoPlist.strings create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/da.lproj/Localizable.strings create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/de.lproj/InfoPlist.strings create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/de.lproj/Localizable.strings create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/es.lproj/InfoPlist.strings create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/es.lproj/Localizable.strings create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/fr.lproj/InfoPlist.strings create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/fr.lproj/Localizable.strings create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/goArrow.png create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/it.lproj/InfoPlist.strings create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/it.lproj/Localizable.strings create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/ja.lproj/InfoPlist.strings create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/ja.lproj/Localizable.strings create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/nl.lproj/InfoPlist.strings create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/nl.lproj/Localizable.strings create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/no.lproj/InfoPlist.strings create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/no.lproj/Localizable.strings create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/sl.lproj/InfoPlist.strings create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/sl.lproj/Localizable.strings create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/sv.lproj/InfoPlist.strings create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/sv.lproj/Localizable.strings create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/tr.lproj/InfoPlist.strings create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/tr.lproj/Localizable.strings create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/uploader.h create mode 100644 shared/sentry/external/breakpad/src/client/mac/sender/uploader.mm create mode 100644 shared/sentry/external/breakpad/src/client/mac/testapp/Controller.h create mode 100644 shared/sentry/external/breakpad/src/client/mac/testapp/Controller.m create mode 100644 shared/sentry/external/breakpad/src/client/mac/testapp/English.lproj/InfoPlist.strings create mode 100644 shared/sentry/external/breakpad/src/client/mac/testapp/English.lproj/MainMenu.xib create mode 100644 shared/sentry/external/breakpad/src/client/mac/testapp/Info.plist create mode 100644 shared/sentry/external/breakpad/src/client/mac/testapp/TestClass.h create mode 100644 shared/sentry/external/breakpad/src/client/mac/testapp/TestClass.mm create mode 100644 shared/sentry/external/breakpad/src/client/mac/testapp/bomb.icns create mode 100755 shared/sentry/external/breakpad/src/client/mac/testapp/crashInMain create mode 100755 shared/sentry/external/breakpad/src/client/mac/testapp/crashduringload create mode 100644 shared/sentry/external/breakpad/src/client/mac/testapp/main.m create mode 100644 shared/sentry/external/breakpad/src/client/mac/tests/BreakpadFramework_Test.mm create mode 100644 shared/sentry/external/breakpad/src/client/mac/tests/crash_generation_server_test.cc create mode 100644 shared/sentry/external/breakpad/src/client/mac/tests/exception_handler_test.cc create mode 100644 shared/sentry/external/breakpad/src/client/mac/tests/minidump_generator_test.cc create mode 100644 shared/sentry/external/breakpad/src/client/mac/tests/minidump_generator_test_helper.cc create mode 100644 shared/sentry/external/breakpad/src/client/mac/tests/spawn_child_process.h create mode 100644 shared/sentry/external/breakpad/src/client/mac/tests/testlogging.h create mode 100644 shared/sentry/external/breakpad/src/client/minidump_file_writer-inl.h create mode 100644 shared/sentry/external/breakpad/src/client/minidump_file_writer.cc create mode 100644 shared/sentry/external/breakpad/src/client/minidump_file_writer.h create mode 100644 shared/sentry/external/breakpad/src/client/minidump_file_writer_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/client/solaris/handler/Makefile create mode 100644 shared/sentry/external/breakpad/src/client/solaris/handler/exception_handler.cc create mode 100644 shared/sentry/external/breakpad/src/client/solaris/handler/exception_handler.h create mode 100644 shared/sentry/external/breakpad/src/client/solaris/handler/exception_handler_test.cc create mode 100644 shared/sentry/external/breakpad/src/client/solaris/handler/minidump_generator.cc create mode 100644 shared/sentry/external/breakpad/src/client/solaris/handler/minidump_generator.h create mode 100644 shared/sentry/external/breakpad/src/client/solaris/handler/minidump_test.cc create mode 100644 shared/sentry/external/breakpad/src/client/solaris/handler/solaris_lwp.cc create mode 100644 shared/sentry/external/breakpad/src/client/solaris/handler/solaris_lwp.h create mode 100644 shared/sentry/external/breakpad/src/client/windows/breakpad_client.gyp create mode 100644 shared/sentry/external/breakpad/src/client/windows/common/auto_critical_section.h create mode 100644 shared/sentry/external/breakpad/src/client/windows/common/ipc_protocol.h create mode 100644 shared/sentry/external/breakpad/src/client/windows/crash_generation/ReadMe.txt create mode 100644 shared/sentry/external/breakpad/src/client/windows/crash_generation/client_info.cc create mode 100644 shared/sentry/external/breakpad/src/client/windows/crash_generation/client_info.h create mode 100644 shared/sentry/external/breakpad/src/client/windows/crash_generation/crash_generation.gyp create mode 100644 shared/sentry/external/breakpad/src/client/windows/crash_generation/crash_generation_client.cc create mode 100644 shared/sentry/external/breakpad/src/client/windows/crash_generation/crash_generation_client.h create mode 100644 shared/sentry/external/breakpad/src/client/windows/crash_generation/crash_generation_server.cc create mode 100644 shared/sentry/external/breakpad/src/client/windows/crash_generation/crash_generation_server.h create mode 100644 shared/sentry/external/breakpad/src/client/windows/crash_generation/minidump_generator.cc create mode 100644 shared/sentry/external/breakpad/src/client/windows/crash_generation/minidump_generator.h create mode 100644 shared/sentry/external/breakpad/src/client/windows/handler/exception_handler.cc create mode 100644 shared/sentry/external/breakpad/src/client/windows/handler/exception_handler.gyp create mode 100644 shared/sentry/external/breakpad/src/client/windows/handler/exception_handler.h create mode 100644 shared/sentry/external/breakpad/src/client/windows/sender/crash_report_sender.cc create mode 100644 shared/sentry/external/breakpad/src/client/windows/sender/crash_report_sender.gyp create mode 100644 shared/sentry/external/breakpad/src/client/windows/sender/crash_report_sender.h create mode 100644 shared/sentry/external/breakpad/src/client/windows/tests/crash_generation_app/abstract_class.cc create mode 100644 shared/sentry/external/breakpad/src/client/windows/tests/crash_generation_app/abstract_class.h create mode 100644 shared/sentry/external/breakpad/src/client/windows/tests/crash_generation_app/crash_generation_app.cc create mode 100644 shared/sentry/external/breakpad/src/client/windows/tests/crash_generation_app/crash_generation_app.gyp create mode 100644 shared/sentry/external/breakpad/src/client/windows/tests/crash_generation_app/crash_generation_app.h create mode 100644 shared/sentry/external/breakpad/src/client/windows/tests/crash_generation_app/crash_generation_app.ico create mode 100644 shared/sentry/external/breakpad/src/client/windows/tests/crash_generation_app/crash_generation_app.rc create mode 100644 shared/sentry/external/breakpad/src/client/windows/tests/crash_generation_app/resource.h create mode 100644 shared/sentry/external/breakpad/src/client/windows/tests/crash_generation_app/small.ico create mode 100644 shared/sentry/external/breakpad/src/client/windows/unittests/client_tests.gyp create mode 100644 shared/sentry/external/breakpad/src/client/windows/unittests/crash_generation_server_test.cc create mode 100644 shared/sentry/external/breakpad/src/client/windows/unittests/dump_analysis.cc create mode 100644 shared/sentry/external/breakpad/src/client/windows/unittests/dump_analysis.h create mode 100644 shared/sentry/external/breakpad/src/client/windows/unittests/exception_handler_death_test.cc create mode 100644 shared/sentry/external/breakpad/src/client/windows/unittests/exception_handler_nesting_test.cc create mode 100644 shared/sentry/external/breakpad/src/client/windows/unittests/exception_handler_test.cc create mode 100644 shared/sentry/external/breakpad/src/client/windows/unittests/exception_handler_test.h create mode 100644 shared/sentry/external/breakpad/src/client/windows/unittests/minidump_test.cc create mode 100644 shared/sentry/external/breakpad/src/client/windows/unittests/testing.gyp create mode 100644 shared/sentry/external/breakpad/src/common/android/include/asm-mips/README.md create mode 100644 shared/sentry/external/breakpad/src/common/android/include/asm-mips/asm.h create mode 100644 shared/sentry/external/breakpad/src/common/android/include/asm-mips/fpregdef.h create mode 100644 shared/sentry/external/breakpad/src/common/android/include/asm-mips/regdef.h create mode 100644 shared/sentry/external/breakpad/src/common/android/include/elf.h create mode 100644 shared/sentry/external/breakpad/src/common/android/include/link.h create mode 100644 shared/sentry/external/breakpad/src/common/android/include/stab.h create mode 100644 shared/sentry/external/breakpad/src/common/android/include/sys/procfs.h create mode 100644 shared/sentry/external/breakpad/src/common/android/include/sys/user.h create mode 100644 shared/sentry/external/breakpad/src/common/android/testing/include/wchar.h create mode 100644 shared/sentry/external/breakpad/src/common/android/testing/mkdtemp.h create mode 100644 shared/sentry/external/breakpad/src/common/android/testing/pthread_fixes.h create mode 100644 shared/sentry/external/breakpad/src/common/basictypes.h create mode 100644 shared/sentry/external/breakpad/src/common/byte_cursor.h create mode 100644 shared/sentry/external/breakpad/src/common/byte_cursor_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/common/common.gyp create mode 100644 shared/sentry/external/breakpad/src/common/convert_UTF.cc create mode 100644 shared/sentry/external/breakpad/src/common/convert_UTF.h create mode 100644 shared/sentry/external/breakpad/src/common/dwarf/bytereader-inl.h create mode 100644 shared/sentry/external/breakpad/src/common/dwarf/bytereader.cc create mode 100644 shared/sentry/external/breakpad/src/common/dwarf/bytereader.h create mode 100644 shared/sentry/external/breakpad/src/common/dwarf/bytereader_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/common/dwarf/cfi_assembler.cc create mode 100644 shared/sentry/external/breakpad/src/common/dwarf/cfi_assembler.h create mode 100644 shared/sentry/external/breakpad/src/common/dwarf/dwarf2diehandler.cc create mode 100644 shared/sentry/external/breakpad/src/common/dwarf/dwarf2diehandler.h create mode 100644 shared/sentry/external/breakpad/src/common/dwarf/dwarf2diehandler_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/common/dwarf/dwarf2enums.h create mode 100644 shared/sentry/external/breakpad/src/common/dwarf/dwarf2reader.cc create mode 100644 shared/sentry/external/breakpad/src/common/dwarf/dwarf2reader.h create mode 100644 shared/sentry/external/breakpad/src/common/dwarf/dwarf2reader_cfi_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/common/dwarf/dwarf2reader_die_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/common/dwarf/dwarf2reader_lineinfo_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/common/dwarf/dwarf2reader_splitfunctions_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/common/dwarf/dwarf2reader_test_common.h create mode 100644 shared/sentry/external/breakpad/src/common/dwarf/elf_reader.cc create mode 100644 shared/sentry/external/breakpad/src/common/dwarf/elf_reader.h create mode 100644 shared/sentry/external/breakpad/src/common/dwarf/functioninfo.cc create mode 100644 shared/sentry/external/breakpad/src/common/dwarf/functioninfo.h create mode 100644 shared/sentry/external/breakpad/src/common/dwarf/line_state_machine.h create mode 100644 shared/sentry/external/breakpad/src/common/dwarf/types.h create mode 100644 shared/sentry/external/breakpad/src/common/dwarf_cfi_to_module.cc create mode 100644 shared/sentry/external/breakpad/src/common/dwarf_cfi_to_module.h create mode 100644 shared/sentry/external/breakpad/src/common/dwarf_cfi_to_module_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/common/dwarf_cu_to_module.cc create mode 100644 shared/sentry/external/breakpad/src/common/dwarf_cu_to_module.h create mode 100644 shared/sentry/external/breakpad/src/common/dwarf_cu_to_module_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/common/dwarf_line_to_module.cc create mode 100644 shared/sentry/external/breakpad/src/common/dwarf_line_to_module.h create mode 100644 shared/sentry/external/breakpad/src/common/dwarf_line_to_module_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/common/dwarf_range_list_handler.cc create mode 100644 shared/sentry/external/breakpad/src/common/dwarf_range_list_handler.h create mode 100644 shared/sentry/external/breakpad/src/common/language.cc create mode 100644 shared/sentry/external/breakpad/src/common/language.h create mode 100644 shared/sentry/external/breakpad/src/common/linux/breakpad_getcontext.S create mode 100644 shared/sentry/external/breakpad/src/common/linux/breakpad_getcontext.h create mode 100644 shared/sentry/external/breakpad/src/common/linux/breakpad_getcontext_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/common/linux/crc32.cc create mode 100644 shared/sentry/external/breakpad/src/common/linux/crc32.h create mode 100644 shared/sentry/external/breakpad/src/common/linux/dump_symbols.cc create mode 100644 shared/sentry/external/breakpad/src/common/linux/dump_symbols.h create mode 100644 shared/sentry/external/breakpad/src/common/linux/dump_symbols_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/common/linux/eintr_wrapper.h create mode 100644 shared/sentry/external/breakpad/src/common/linux/elf_core_dump.cc create mode 100644 shared/sentry/external/breakpad/src/common/linux/elf_core_dump.h create mode 100644 shared/sentry/external/breakpad/src/common/linux/elf_core_dump_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/common/linux/elf_gnu_compat.h create mode 100644 shared/sentry/external/breakpad/src/common/linux/elf_symbols_to_module.cc create mode 100644 shared/sentry/external/breakpad/src/common/linux/elf_symbols_to_module.h create mode 100644 shared/sentry/external/breakpad/src/common/linux/elf_symbols_to_module_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/common/linux/elfutils-inl.h create mode 100644 shared/sentry/external/breakpad/src/common/linux/elfutils.cc create mode 100644 shared/sentry/external/breakpad/src/common/linux/elfutils.h create mode 100644 shared/sentry/external/breakpad/src/common/linux/file_id.cc create mode 100644 shared/sentry/external/breakpad/src/common/linux/file_id.h create mode 100644 shared/sentry/external/breakpad/src/common/linux/file_id_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/common/linux/google_crashdump_uploader.cc create mode 100644 shared/sentry/external/breakpad/src/common/linux/google_crashdump_uploader.h create mode 100644 shared/sentry/external/breakpad/src/common/linux/google_crashdump_uploader_test.cc create mode 100644 shared/sentry/external/breakpad/src/common/linux/guid_creator.cc create mode 100644 shared/sentry/external/breakpad/src/common/linux/guid_creator.h create mode 100644 shared/sentry/external/breakpad/src/common/linux/http_upload.cc create mode 100644 shared/sentry/external/breakpad/src/common/linux/http_upload.h create mode 100644 shared/sentry/external/breakpad/src/common/linux/ignore_ret.h create mode 100644 shared/sentry/external/breakpad/src/common/linux/libcurl_wrapper.cc create mode 100644 shared/sentry/external/breakpad/src/common/linux/libcurl_wrapper.h create mode 100644 shared/sentry/external/breakpad/src/common/linux/linux_libc_support.cc create mode 100644 shared/sentry/external/breakpad/src/common/linux/linux_libc_support.h create mode 100644 shared/sentry/external/breakpad/src/common/linux/linux_libc_support_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/common/linux/memory_mapped_file.cc create mode 100644 shared/sentry/external/breakpad/src/common/linux/memory_mapped_file.h create mode 100644 shared/sentry/external/breakpad/src/common/linux/memory_mapped_file_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/common/linux/safe_readlink.cc create mode 100644 shared/sentry/external/breakpad/src/common/linux/safe_readlink.h create mode 100644 shared/sentry/external/breakpad/src/common/linux/safe_readlink_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/common/linux/symbol_collector_client.cc create mode 100644 shared/sentry/external/breakpad/src/common/linux/symbol_collector_client.h create mode 100644 shared/sentry/external/breakpad/src/common/linux/symbol_upload.cc create mode 100644 shared/sentry/external/breakpad/src/common/linux/symbol_upload.h create mode 100644 shared/sentry/external/breakpad/src/common/linux/synth_elf.cc create mode 100644 shared/sentry/external/breakpad/src/common/linux/synth_elf.h create mode 100644 shared/sentry/external/breakpad/src/common/linux/synth_elf_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/common/linux/tests/auto_testfile.h create mode 100644 shared/sentry/external/breakpad/src/common/linux/tests/crash_generator.cc create mode 100644 shared/sentry/external/breakpad/src/common/linux/tests/crash_generator.h create mode 100644 shared/sentry/external/breakpad/src/common/linux/ucontext_constants.h create mode 100644 shared/sentry/external/breakpad/src/common/long_string_dictionary.cc create mode 100644 shared/sentry/external/breakpad/src/common/long_string_dictionary.h create mode 100644 shared/sentry/external/breakpad/src/common/long_string_dictionary_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/common/mac/Breakpad.xcconfig create mode 100644 shared/sentry/external/breakpad/src/common/mac/BreakpadDebug.xcconfig create mode 100644 shared/sentry/external/breakpad/src/common/mac/BreakpadRelease.xcconfig create mode 100644 shared/sentry/external/breakpad/src/common/mac/GTMDefines.h create mode 100644 shared/sentry/external/breakpad/src/common/mac/GTMLogger.h create mode 100644 shared/sentry/external/breakpad/src/common/mac/GTMLogger.m create mode 100644 shared/sentry/external/breakpad/src/common/mac/HTTPGetRequest.h create mode 100644 shared/sentry/external/breakpad/src/common/mac/HTTPGetRequest.m create mode 100644 shared/sentry/external/breakpad/src/common/mac/HTTPMultipartUpload.h create mode 100644 shared/sentry/external/breakpad/src/common/mac/HTTPMultipartUpload.m create mode 100644 shared/sentry/external/breakpad/src/common/mac/HTTPPutRequest.h create mode 100644 shared/sentry/external/breakpad/src/common/mac/HTTPPutRequest.m create mode 100644 shared/sentry/external/breakpad/src/common/mac/HTTPRequest.h create mode 100644 shared/sentry/external/breakpad/src/common/mac/HTTPRequest.m create mode 100644 shared/sentry/external/breakpad/src/common/mac/HTTPSimplePostRequest.h create mode 100644 shared/sentry/external/breakpad/src/common/mac/HTTPSimplePostRequest.m create mode 100644 shared/sentry/external/breakpad/src/common/mac/MachIPC.h create mode 100644 shared/sentry/external/breakpad/src/common/mac/MachIPC.mm create mode 100644 shared/sentry/external/breakpad/src/common/mac/SymbolCollectorClient.h create mode 100644 shared/sentry/external/breakpad/src/common/mac/SymbolCollectorClient.m create mode 100644 shared/sentry/external/breakpad/src/common/mac/arch_utilities.cc create mode 100644 shared/sentry/external/breakpad/src/common/mac/arch_utilities.h create mode 100644 shared/sentry/external/breakpad/src/common/mac/bootstrap_compat.cc create mode 100644 shared/sentry/external/breakpad/src/common/mac/bootstrap_compat.h create mode 100644 shared/sentry/external/breakpad/src/common/mac/byteswap.h create mode 100644 shared/sentry/external/breakpad/src/common/mac/dump_syms.cc create mode 100644 shared/sentry/external/breakpad/src/common/mac/dump_syms.h create mode 100644 shared/sentry/external/breakpad/src/common/mac/encoding_util.h create mode 100644 shared/sentry/external/breakpad/src/common/mac/encoding_util.m create mode 100644 shared/sentry/external/breakpad/src/common/mac/file_id.cc create mode 100644 shared/sentry/external/breakpad/src/common/mac/file_id.h create mode 100644 shared/sentry/external/breakpad/src/common/mac/launch_reporter.cc create mode 100644 shared/sentry/external/breakpad/src/common/mac/launch_reporter.h create mode 100644 shared/sentry/external/breakpad/src/common/mac/macho_id.cc create mode 100644 shared/sentry/external/breakpad/src/common/mac/macho_id.h create mode 100644 shared/sentry/external/breakpad/src/common/mac/macho_reader.cc create mode 100644 shared/sentry/external/breakpad/src/common/mac/macho_reader.h create mode 100644 shared/sentry/external/breakpad/src/common/mac/macho_reader_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/common/mac/macho_utilities.cc create mode 100644 shared/sentry/external/breakpad/src/common/mac/macho_utilities.h create mode 100644 shared/sentry/external/breakpad/src/common/mac/macho_walker.cc create mode 100644 shared/sentry/external/breakpad/src/common/mac/macho_walker.h create mode 100644 shared/sentry/external/breakpad/src/common/mac/minidump_upload.m create mode 100644 shared/sentry/external/breakpad/src/common/mac/scoped_task_suspend-inl.h create mode 100644 shared/sentry/external/breakpad/src/common/mac/string_utilities.cc create mode 100644 shared/sentry/external/breakpad/src/common/mac/string_utilities.h create mode 100644 shared/sentry/external/breakpad/src/common/mac/super_fat_arch.h create mode 100644 shared/sentry/external/breakpad/src/common/mac/testing/GTMSenTestCase.h create mode 100644 shared/sentry/external/breakpad/src/common/mac/testing/GTMSenTestCase.m create mode 100644 shared/sentry/external/breakpad/src/common/macros.h create mode 100644 shared/sentry/external/breakpad/src/common/md5.cc create mode 100644 shared/sentry/external/breakpad/src/common/md5.h create mode 100644 shared/sentry/external/breakpad/src/common/memory_allocator.h create mode 100644 shared/sentry/external/breakpad/src/common/memory_allocator_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/common/memory_range.h create mode 100644 shared/sentry/external/breakpad/src/common/memory_range_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/common/minidump_type_helper.h create mode 100644 shared/sentry/external/breakpad/src/common/module.cc create mode 100644 shared/sentry/external/breakpad/src/common/module.h create mode 100644 shared/sentry/external/breakpad/src/common/module_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/common/path_helper.cc create mode 100644 shared/sentry/external/breakpad/src/common/path_helper.h create mode 100644 shared/sentry/external/breakpad/src/common/scoped_ptr.h create mode 100644 shared/sentry/external/breakpad/src/common/simple_string_dictionary.cc create mode 100644 shared/sentry/external/breakpad/src/common/simple_string_dictionary.h create mode 100644 shared/sentry/external/breakpad/src/common/simple_string_dictionary_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/common/solaris/dump_symbols.cc create mode 100644 shared/sentry/external/breakpad/src/common/solaris/dump_symbols.h create mode 100644 shared/sentry/external/breakpad/src/common/solaris/file_id.cc create mode 100644 shared/sentry/external/breakpad/src/common/solaris/file_id.h create mode 100644 shared/sentry/external/breakpad/src/common/solaris/guid_creator.cc create mode 100644 shared/sentry/external/breakpad/src/common/solaris/guid_creator.h create mode 100644 shared/sentry/external/breakpad/src/common/solaris/message_output.h create mode 100644 shared/sentry/external/breakpad/src/common/stabs_reader.cc create mode 100644 shared/sentry/external/breakpad/src/common/stabs_reader.h create mode 100644 shared/sentry/external/breakpad/src/common/stabs_reader_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/common/stabs_to_module.cc create mode 100644 shared/sentry/external/breakpad/src/common/stabs_to_module.h create mode 100644 shared/sentry/external/breakpad/src/common/stabs_to_module_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/common/stdio_wrapper.h create mode 100644 shared/sentry/external/breakpad/src/common/string_conversion.cc create mode 100644 shared/sentry/external/breakpad/src/common/string_conversion.h create mode 100644 shared/sentry/external/breakpad/src/common/string_conversion_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/common/string_view.h create mode 100644 shared/sentry/external/breakpad/src/common/symbol_data.h create mode 100644 shared/sentry/external/breakpad/src/common/test_assembler.cc create mode 100644 shared/sentry/external/breakpad/src/common/test_assembler.h create mode 100644 shared/sentry/external/breakpad/src/common/test_assembler_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/common/testdata/func-line-pairing.h create mode 100644 shared/sentry/external/breakpad/src/common/tests/auto_tempdir.h create mode 100644 shared/sentry/external/breakpad/src/common/tests/file_utils.cc create mode 100644 shared/sentry/external/breakpad/src/common/tests/file_utils.h create mode 100644 shared/sentry/external/breakpad/src/common/unordered.h create mode 100644 shared/sentry/external/breakpad/src/common/using_std_string.h create mode 100644 shared/sentry/external/breakpad/src/common/windows/common_windows.gyp create mode 100644 shared/sentry/external/breakpad/src/common/windows/dia_util.cc create mode 100644 shared/sentry/external/breakpad/src/common/windows/dia_util.h create mode 100644 shared/sentry/external/breakpad/src/common/windows/guid_string.cc create mode 100644 shared/sentry/external/breakpad/src/common/windows/guid_string.h create mode 100644 shared/sentry/external/breakpad/src/common/windows/http_upload.cc create mode 100644 shared/sentry/external/breakpad/src/common/windows/http_upload.h create mode 100644 shared/sentry/external/breakpad/src/common/windows/module_info.h create mode 100644 shared/sentry/external/breakpad/src/common/windows/omap.cc create mode 100644 shared/sentry/external/breakpad/src/common/windows/omap.h create mode 100644 shared/sentry/external/breakpad/src/common/windows/omap_internal.h create mode 100644 shared/sentry/external/breakpad/src/common/windows/omap_unittest.cc create mode 100644 shared/sentry/external/breakpad/src/common/windows/pdb_source_line_writer.cc create mode 100644 shared/sentry/external/breakpad/src/common/windows/pdb_source_line_writer.h create mode 100644 shared/sentry/external/breakpad/src/common/windows/pe_source_line_writer.cc create mode 100644 shared/sentry/external/breakpad/src/common/windows/pe_source_line_writer.h create mode 100644 shared/sentry/external/breakpad/src/common/windows/pe_util.cc create mode 100644 shared/sentry/external/breakpad/src/common/windows/pe_util.h create mode 100644 shared/sentry/external/breakpad/src/common/windows/string_utils-inl.h create mode 100644 shared/sentry/external/breakpad/src/common/windows/string_utils.cc create mode 100644 shared/sentry/external/breakpad/src/common/windows/symbol_collector_client.cc create mode 100644 shared/sentry/external/breakpad/src/common/windows/symbol_collector_client.h create mode 100644 shared/sentry/external/breakpad/src/config.h.in create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/common/breakpad_types.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_amd64.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_arm.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_arm64.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_mips.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_ppc.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_ppc64.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_sparc.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_x86.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/common/minidump_exception_fuchsia.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/common/minidump_exception_linux.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/common/minidump_exception_mac.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/common/minidump_exception_ps3.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/common/minidump_exception_solaris.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/common/minidump_exception_win32.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/common/minidump_format.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/common/minidump_size.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/processor/basic_source_line_resolver.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/processor/call_stack.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/processor/code_module.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/processor/code_modules.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/processor/dump_context.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/processor/dump_object.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/processor/exception_record.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/processor/exploitability.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/processor/fast_source_line_resolver.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/processor/memory_region.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/processor/microdump.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/processor/microdump_processor.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/processor/minidump.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/processor/minidump_processor.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/processor/proc_maps_linux.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/processor/process_result.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/processor/process_state.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/processor/source_line_resolver_base.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/processor/source_line_resolver_interface.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/processor/stack_frame.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/processor/stack_frame_cpu.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/processor/stack_frame_symbolizer.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/processor/stackwalker.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/processor/symbol_supplier.h create mode 100644 shared/sentry/external/breakpad/src/google_breakpad/processor/system_info.h create mode 100644 shared/sentry/external/breakpad/src/third_party/curl/COPYING create mode 100644 shared/sentry/external/breakpad/src/third_party/curl/curl.h create mode 100644 shared/sentry/external/breakpad/src/third_party/curl/curlbuild.h create mode 100644 shared/sentry/external/breakpad/src/third_party/curl/curlrules.h create mode 100644 shared/sentry/external/breakpad/src/third_party/curl/curlver.h create mode 100644 shared/sentry/external/breakpad/src/third_party/curl/easy.h create mode 100644 shared/sentry/external/breakpad/src/third_party/curl/mprintf.h create mode 100644 shared/sentry/external/breakpad/src/third_party/curl/multi.h create mode 100644 shared/sentry/external/breakpad/src/third_party/curl/stdcheaders.h create mode 100644 shared/sentry/external/breakpad/src/third_party/curl/typecheck-gcc.h create mode 100644 shared/sentry/external/breakpad/src/third_party/curl/types.h create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/LICENSE create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/Makefile.am create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/README.breakpad create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/TODO create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_implicit.c create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_implicit.h create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_insn.c create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_insn.h create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_invariant.c create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_invariant.h create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_modrm.c create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_modrm.h create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_opcode_tables.c create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_opcode_tables.h create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_operand.c create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_operand.h create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_reg.c create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_reg.h create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_settings.c create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_settings.h create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/libdis.h create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/libdisasm.gyp create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/qword.h create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/swig/Makefile create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/swig/README create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/swig/libdisasm.i create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/swig/libdisasm_oop.i create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/swig/perl/Makefile-swig create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/swig/perl/Makefile.PL create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/swig/python/Makefile-swig create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/swig/ruby/Makefile-swig create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/swig/ruby/extconf.rb create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/swig/tcl/Makefile-swig create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/x86_disasm.c create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/x86_format.c create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/x86_imm.c create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/x86_imm.h create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/x86_insn.c create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/x86_misc.c create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/x86_operand_list.c create mode 100644 shared/sentry/external/breakpad/src/third_party/libdisasm/x86_operand_list.h create mode 100644 shared/sentry/external/breakpad/src/third_party/linux/include/gflags/gflags_completions.h create mode 100644 shared/sentry/external/breakpad/src/third_party/mac_headers/README create mode 100644 shared/sentry/external/breakpad/src/third_party/mac_headers/architecture/byte_order.h create mode 100644 shared/sentry/external/breakpad/src/third_party/mac_headers/i386/_types.h create mode 100644 shared/sentry/external/breakpad/src/third_party/mac_headers/mach-o/arch.h create mode 100644 shared/sentry/external/breakpad/src/third_party/mac_headers/mach-o/fat.h create mode 100644 shared/sentry/external/breakpad/src/third_party/mac_headers/mach-o/loader.h create mode 100644 shared/sentry/external/breakpad/src/third_party/mac_headers/mach-o/nlist.h create mode 100644 shared/sentry/external/breakpad/src/third_party/mac_headers/mach/boolean.h create mode 100644 shared/sentry/external/breakpad/src/third_party/mac_headers/mach/i386/boolean.h create mode 100644 shared/sentry/external/breakpad/src/third_party/mac_headers/mach/i386/vm_param.h create mode 100644 shared/sentry/external/breakpad/src/third_party/mac_headers/mach/i386/vm_types.h create mode 100644 shared/sentry/external/breakpad/src/third_party/mac_headers/mach/machine.h create mode 100644 shared/sentry/external/breakpad/src/third_party/mac_headers/mach/machine/boolean.h create mode 100644 shared/sentry/external/breakpad/src/third_party/mac_headers/mach/machine/thread_state.h create mode 100644 shared/sentry/external/breakpad/src/third_party/mac_headers/mach/machine/thread_status.h create mode 100644 shared/sentry/external/breakpad/src/third_party/mac_headers/mach/machine/vm_types.h create mode 100644 shared/sentry/external/breakpad/src/third_party/mac_headers/mach/thread_status.h create mode 100644 shared/sentry/external/breakpad/src/third_party/mac_headers/mach/vm_prot.h create mode 100644 shared/sentry/external/breakpad/src/third_party/musl/COPYRIGHT create mode 100644 shared/sentry/external/breakpad/src/third_party/musl/README create mode 100644 shared/sentry/external/breakpad/src/third_party/musl/README.breakpad create mode 100644 shared/sentry/external/breakpad/src/third_party/musl/VERSION create mode 100644 shared/sentry/external/breakpad/src/third_party/musl/include/elf.h create mode 100644 shared/sentry/external/crashpad/.clang-format create mode 100644 shared/sentry/external/crashpad/.gitattributes create mode 100644 shared/sentry/external/crashpad/.github/workflows/build.yml create mode 100644 shared/sentry/external/crashpad/.gitignore create mode 100644 shared/sentry/external/crashpad/.gitmodules create mode 100644 shared/sentry/external/crashpad/.gn create mode 100644 shared/sentry/external/crashpad/.style.yapf create mode 100644 shared/sentry/external/crashpad/.vpython create mode 100644 shared/sentry/external/crashpad/.vpython3 create mode 100644 shared/sentry/external/crashpad/AUTHORS create mode 100644 shared/sentry/external/crashpad/BUILD.gn create mode 100644 shared/sentry/external/crashpad/CMakeLists.txt create mode 100644 shared/sentry/external/crashpad/CONTRIBUTORS create mode 100644 shared/sentry/external/crashpad/DEPS create mode 100644 shared/sentry/external/crashpad/LICENSE create mode 100644 shared/sentry/external/crashpad/README.getsentry.md create mode 100644 shared/sentry/external/crashpad/README.md create mode 100644 shared/sentry/external/crashpad/build/BUILD.gn create mode 100644 shared/sentry/external/crashpad/build/BUILDCONFIG.gn create mode 100644 shared/sentry/external/crashpad/build/crashpad_buildconfig.gni create mode 100644 shared/sentry/external/crashpad/build/crashpad_fuzzer_test.gni create mode 100755 shared/sentry/external/crashpad/build/install_linux_sysroot.py create mode 100644 shared/sentry/external/crashpad/build/ios/Default.png create mode 100644 shared/sentry/external/crashpad/build/ios/Unittest-Info.plist create mode 100755 shared/sentry/external/crashpad/build/ios/convert_gn_xcodeproj.py create mode 100644 shared/sentry/external/crashpad/build/ios/setup_ios_gn.config create mode 100755 shared/sentry/external/crashpad/build/ios/setup_ios_gn.py create mode 100644 shared/sentry/external/crashpad/build/ios/xcodescheme-testable.template create mode 100644 shared/sentry/external/crashpad/build/ios/xcodescheme.template create mode 100755 shared/sentry/external/crashpad/build/run_tests.py create mode 100644 shared/sentry/external/crashpad/build/test.gni create mode 100644 shared/sentry/external/crashpad/client/BUILD.gn create mode 100644 shared/sentry/external/crashpad/client/CMakeLists.txt create mode 100644 shared/sentry/external/crashpad/client/annotation.cc create mode 100644 shared/sentry/external/crashpad/client/annotation.h create mode 100644 shared/sentry/external/crashpad/client/annotation_list.cc create mode 100644 shared/sentry/external/crashpad/client/annotation_list.h create mode 100644 shared/sentry/external/crashpad/client/annotation_list_test.cc create mode 100644 shared/sentry/external/crashpad/client/annotation_test.cc create mode 100644 shared/sentry/external/crashpad/client/client_argv_handling.cc create mode 100644 shared/sentry/external/crashpad/client/client_argv_handling.h create mode 100644 shared/sentry/external/crashpad/client/crash_report_database.cc create mode 100644 shared/sentry/external/crashpad/client/crash_report_database.h create mode 100644 shared/sentry/external/crashpad/client/crash_report_database_generic.cc create mode 100644 shared/sentry/external/crashpad/client/crash_report_database_mac.mm create mode 100644 shared/sentry/external/crashpad/client/crash_report_database_test.cc create mode 100644 shared/sentry/external/crashpad/client/crash_report_database_win.cc create mode 100644 shared/sentry/external/crashpad/client/crashpad_client.h create mode 100644 shared/sentry/external/crashpad/client/crashpad_client_fuchsia.cc create mode 100644 shared/sentry/external/crashpad/client/crashpad_client_ios.cc create mode 100644 shared/sentry/external/crashpad/client/crashpad_client_ios_test.mm create mode 100644 shared/sentry/external/crashpad/client/crashpad_client_linux.cc create mode 100644 shared/sentry/external/crashpad/client/crashpad_client_linux_test.cc create mode 100644 shared/sentry/external/crashpad/client/crashpad_client_mac.cc create mode 100644 shared/sentry/external/crashpad/client/crashpad_client_win.cc create mode 100644 shared/sentry/external/crashpad/client/crashpad_client_win_test.cc create mode 100644 shared/sentry/external/crashpad/client/crashpad_info.cc create mode 100644 shared/sentry/external/crashpad/client/crashpad_info.h create mode 100644 shared/sentry/external/crashpad/client/crashpad_info_note.S create mode 100644 shared/sentry/external/crashpad/client/ios_handler/exception_processor.h create mode 100644 shared/sentry/external/crashpad/client/ios_handler/exception_processor.mm create mode 100644 shared/sentry/external/crashpad/client/ios_handler/exception_processor_test.mm create mode 100644 shared/sentry/external/crashpad/client/ios_handler/in_process_handler.cc create mode 100644 shared/sentry/external/crashpad/client/ios_handler/in_process_handler.h create mode 100644 shared/sentry/external/crashpad/client/ios_handler/in_process_handler_test.cc create mode 100644 shared/sentry/external/crashpad/client/ios_handler/in_process_intermediate_dump_handler.cc create mode 100644 shared/sentry/external/crashpad/client/ios_handler/in_process_intermediate_dump_handler.h create mode 100644 shared/sentry/external/crashpad/client/ios_handler/in_process_intermediate_dump_handler_test.cc create mode 100644 shared/sentry/external/crashpad/client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.cc create mode 100644 shared/sentry/external/crashpad/client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.h create mode 100644 shared/sentry/external/crashpad/client/prune_crash_reports.cc create mode 100644 shared/sentry/external/crashpad/client/prune_crash_reports.h create mode 100644 shared/sentry/external/crashpad/client/prune_crash_reports_test.cc create mode 100644 shared/sentry/external/crashpad/client/pthread_create_linux.cc create mode 100644 shared/sentry/external/crashpad/client/settings.cc create mode 100644 shared/sentry/external/crashpad/client/settings.h create mode 100644 shared/sentry/external/crashpad/client/settings_test.cc create mode 100644 shared/sentry/external/crashpad/client/simple_address_range_bag.h create mode 100644 shared/sentry/external/crashpad/client/simple_address_range_bag_test.cc create mode 100644 shared/sentry/external/crashpad/client/simple_string_dictionary.h create mode 100644 shared/sentry/external/crashpad/client/simple_string_dictionary_test.cc create mode 100644 shared/sentry/external/crashpad/client/simulate_crash.h create mode 100644 shared/sentry/external/crashpad/client/simulate_crash_ios.h create mode 100644 shared/sentry/external/crashpad/client/simulate_crash_linux.h create mode 100644 shared/sentry/external/crashpad/client/simulate_crash_mac.cc create mode 100644 shared/sentry/external/crashpad/client/simulate_crash_mac.h create mode 100644 shared/sentry/external/crashpad/client/simulate_crash_mac_test.cc create mode 100644 shared/sentry/external/crashpad/client/simulate_crash_win.h create mode 100644 shared/sentry/external/crashpad/codereview.settings create mode 100644 shared/sentry/external/crashpad/compat/BUILD.gn create mode 100644 shared/sentry/external/crashpad/compat/CMakeLists.txt create mode 100644 shared/sentry/external/crashpad/compat/android/dlfcn_internal.cc create mode 100644 shared/sentry/external/crashpad/compat/android/dlfcn_internal.h create mode 100644 shared/sentry/external/crashpad/compat/android/elf.h create mode 100644 shared/sentry/external/crashpad/compat/android/linux/elf.h create mode 100644 shared/sentry/external/crashpad/compat/android/linux/prctl.h create mode 100644 shared/sentry/external/crashpad/compat/android/linux/ptrace.h create mode 100644 shared/sentry/external/crashpad/compat/android/sched.h create mode 100644 shared/sentry/external/crashpad/compat/android/sys/epoll.cc create mode 100644 shared/sentry/external/crashpad/compat/android/sys/epoll.h create mode 100644 shared/sentry/external/crashpad/compat/android/sys/mman.h create mode 100644 shared/sentry/external/crashpad/compat/android/sys/mman_mmap.cc create mode 100644 shared/sentry/external/crashpad/compat/android/sys/syscall.h create mode 100644 shared/sentry/external/crashpad/compat/android/sys/user.h create mode 100644 shared/sentry/external/crashpad/compat/ios/mach/exc.defs create mode 100644 shared/sentry/external/crashpad/compat/ios/mach/mach_exc.defs create mode 100644 shared/sentry/external/crashpad/compat/ios/mach/mach_types.defs create mode 100644 shared/sentry/external/crashpad/compat/ios/mach/machine/machine_types.defs create mode 100644 shared/sentry/external/crashpad/compat/ios/mach/std_types.defs create mode 100644 shared/sentry/external/crashpad/compat/linux/signal.h create mode 100644 shared/sentry/external/crashpad/compat/linux/sys/mman.h create mode 100644 shared/sentry/external/crashpad/compat/linux/sys/mman_memfd_create.cc create mode 100644 shared/sentry/external/crashpad/compat/linux/sys/ptrace.h create mode 100644 shared/sentry/external/crashpad/compat/linux/sys/user.h create mode 100644 shared/sentry/external/crashpad/compat/mac/Availability.h create mode 100644 shared/sentry/external/crashpad/compat/mac/AvailabilityVersions.h create mode 100644 shared/sentry/external/crashpad/compat/mac/kern/exc_resource.h create mode 100644 shared/sentry/external/crashpad/compat/mac/mach-o/loader.h create mode 100644 shared/sentry/external/crashpad/compat/mac/mach/i386/thread_state.h create mode 100644 shared/sentry/external/crashpad/compat/mac/mach/mach.h create mode 100644 shared/sentry/external/crashpad/compat/mac/sys/resource.h create mode 100644 shared/sentry/external/crashpad/compat/mingw/dbghelp.h create mode 100644 shared/sentry/external/crashpad/compat/mingw/winnt.h create mode 100644 shared/sentry/external/crashpad/compat/non_elf/elf.h create mode 100644 shared/sentry/external/crashpad/compat/non_mac/mach-o/loader.h create mode 100644 shared/sentry/external/crashpad/compat/non_mac/mach/mach.h create mode 100644 shared/sentry/external/crashpad/compat/non_mac/mach/machine.h create mode 100644 shared/sentry/external/crashpad/compat/non_mac/mach/vm_prot.h create mode 100644 shared/sentry/external/crashpad/compat/non_win/dbghelp.h create mode 100644 shared/sentry/external/crashpad/compat/non_win/minwinbase.h create mode 100644 shared/sentry/external/crashpad/compat/non_win/timezoneapi.h create mode 100644 shared/sentry/external/crashpad/compat/non_win/verrsrc.h create mode 100644 shared/sentry/external/crashpad/compat/non_win/windows.h create mode 100644 shared/sentry/external/crashpad/compat/non_win/winnt.h create mode 100644 shared/sentry/external/crashpad/compat/win/getopt.h create mode 100644 shared/sentry/external/crashpad/compat/win/strings.cc create mode 100644 shared/sentry/external/crashpad/compat/win/strings.h create mode 100644 shared/sentry/external/crashpad/compat/win/sys/time.h create mode 100644 shared/sentry/external/crashpad/compat/win/sys/types.h create mode 100644 shared/sentry/external/crashpad/compat/win/time.cc create mode 100644 shared/sentry/external/crashpad/compat/win/time.h create mode 100644 shared/sentry/external/crashpad/compat/win/winbase.h create mode 100644 shared/sentry/external/crashpad/compat/win/winnt.h create mode 100644 shared/sentry/external/crashpad/compat/win/winternl.h create mode 100644 shared/sentry/external/crashpad/crashpad-config.cmake.in create mode 100644 shared/sentry/external/crashpad/doc/.gitignore create mode 100644 shared/sentry/external/crashpad/doc/appengine/README create mode 100644 shared/sentry/external/crashpad/doc/appengine/src/crashpad-home/app.yaml create mode 100644 shared/sentry/external/crashpad/doc/appengine/src/crashpad-home/main.go create mode 100644 shared/sentry/external/crashpad/doc/developing.md create mode 100644 shared/sentry/external/crashpad/doc/favicon.ico create mode 100644 shared/sentry/external/crashpad/doc/ios_overview_design.md create mode 100644 shared/sentry/external/crashpad/doc/layering.png create mode 100644 shared/sentry/external/crashpad/doc/man.md create mode 100644 shared/sentry/external/crashpad/doc/overview.png create mode 100644 shared/sentry/external/crashpad/doc/overview_design.md create mode 100644 shared/sentry/external/crashpad/doc/status.md create mode 100644 shared/sentry/external/crashpad/doc/support/compat.sh create mode 100644 shared/sentry/external/crashpad/doc/support/crashpad.doxy create mode 100644 shared/sentry/external/crashpad/doc/support/crashpad.doxy.h create mode 100644 shared/sentry/external/crashpad/doc/support/crashpad_doxygen.css create mode 100755 shared/sentry/external/crashpad/doc/support/generate.sh create mode 100755 shared/sentry/external/crashpad/doc/support/generate_doxygen.py create mode 100755 shared/sentry/external/crashpad/doc/support/generate_git.sh create mode 100644 shared/sentry/external/crashpad/example.cpp create mode 100644 shared/sentry/external/crashpad/handler/BUILD.gn create mode 100644 shared/sentry/external/crashpad/handler/CMakeLists.txt create mode 100644 shared/sentry/external/crashpad/handler/crash_report_upload_thread.cc create mode 100644 shared/sentry/external/crashpad/handler/crash_report_upload_thread.h create mode 100644 shared/sentry/external/crashpad/handler/crashpad_handler.md create mode 100644 shared/sentry/external/crashpad/handler/crashpad_handler_main.cc create mode 100644 shared/sentry/external/crashpad/handler/crashpad_handler_test.cc create mode 100644 shared/sentry/external/crashpad/handler/crashpad_handler_test_extended_handler.cc create mode 100644 shared/sentry/external/crashpad/handler/handler_main.cc create mode 100644 shared/sentry/external/crashpad/handler/handler_main.h create mode 100644 shared/sentry/external/crashpad/handler/linux/capture_snapshot.cc create mode 100644 shared/sentry/external/crashpad/handler/linux/capture_snapshot.h create mode 100644 shared/sentry/external/crashpad/handler/linux/crash_report_exception_handler.cc create mode 100644 shared/sentry/external/crashpad/handler/linux/crash_report_exception_handler.h create mode 100644 shared/sentry/external/crashpad/handler/linux/cros_crash_report_exception_handler.cc create mode 100644 shared/sentry/external/crashpad/handler/linux/cros_crash_report_exception_handler.h create mode 100644 shared/sentry/external/crashpad/handler/linux/exception_handler_server.cc create mode 100644 shared/sentry/external/crashpad/handler/linux/exception_handler_server.h create mode 100644 shared/sentry/external/crashpad/handler/linux/exception_handler_server_test.cc create mode 100644 shared/sentry/external/crashpad/handler/linux/handler_trampoline.cc create mode 100644 shared/sentry/external/crashpad/handler/mac/crash_report_exception_handler.cc create mode 100644 shared/sentry/external/crashpad/handler/mac/crash_report_exception_handler.h create mode 100644 shared/sentry/external/crashpad/handler/mac/exception_handler_server.cc create mode 100644 shared/sentry/external/crashpad/handler/mac/exception_handler_server.h create mode 100644 shared/sentry/external/crashpad/handler/mac/file_limit_annotation.cc create mode 100644 shared/sentry/external/crashpad/handler/mac/file_limit_annotation.h create mode 100644 shared/sentry/external/crashpad/handler/main.cc create mode 100644 shared/sentry/external/crashpad/handler/minidump_to_upload_parameters.cc create mode 100644 shared/sentry/external/crashpad/handler/minidump_to_upload_parameters.h create mode 100644 shared/sentry/external/crashpad/handler/minidump_to_upload_parameters_test.cc create mode 100644 shared/sentry/external/crashpad/handler/prune_crash_reports_thread.cc create mode 100644 shared/sentry/external/crashpad/handler/prune_crash_reports_thread.h create mode 100644 shared/sentry/external/crashpad/handler/user_stream_data_source.cc create mode 100644 shared/sentry/external/crashpad/handler/user_stream_data_source.h create mode 100644 shared/sentry/external/crashpad/handler/win/.gitattributes create mode 100644 shared/sentry/external/crashpad/handler/win/crash_other_program.cc create mode 100644 shared/sentry/external/crashpad/handler/win/crash_report_exception_handler.cc create mode 100644 shared/sentry/external/crashpad/handler/win/crash_report_exception_handler.h create mode 100644 shared/sentry/external/crashpad/handler/win/crashy_signal.cc create mode 100644 shared/sentry/external/crashpad/handler/win/crashy_test_program.cc create mode 100644 shared/sentry/external/crashpad/handler/win/crashy_test_z7_loader.cc create mode 100644 shared/sentry/external/crashpad/handler/win/fake_handler_that_crashes_at_startup.cc create mode 100644 shared/sentry/external/crashpad/handler/win/hanging_program.cc create mode 100644 shared/sentry/external/crashpad/handler/win/loader_lock_dll.cc create mode 100644 shared/sentry/external/crashpad/handler/win/self_destroying_test_program.cc create mode 100644 shared/sentry/external/crashpad/handler/win/z7_test.cpp create mode 100755 shared/sentry/external/crashpad/handler/win/z7_test.dll create mode 100644 shared/sentry/external/crashpad/infra/config/PRESUBMIT.py create mode 100644 shared/sentry/external/crashpad/infra/config/generated/commit-queue.cfg create mode 100644 shared/sentry/external/crashpad/infra/config/generated/cr-buildbucket.cfg create mode 100644 shared/sentry/external/crashpad/infra/config/generated/luci-logdog.cfg create mode 100644 shared/sentry/external/crashpad/infra/config/generated/luci-milo.cfg create mode 100644 shared/sentry/external/crashpad/infra/config/generated/luci-scheduler.cfg create mode 100644 shared/sentry/external/crashpad/infra/config/generated/project.cfg create mode 100644 shared/sentry/external/crashpad/infra/config/generated/realms.cfg create mode 100755 shared/sentry/external/crashpad/infra/config/main.star create mode 100644 shared/sentry/external/crashpad/libunwind/.clang-format create mode 100644 shared/sentry/external/crashpad/libunwind/CMakeLists.txt create mode 100644 shared/sentry/external/crashpad/libunwind/LICENSE.TXT create mode 100644 shared/sentry/external/crashpad/libunwind/cmake/Modules/HandleCompilerRT.cmake create mode 100644 shared/sentry/external/crashpad/libunwind/cmake/Modules/HandleLibunwindFlags.cmake create mode 100644 shared/sentry/external/crashpad/libunwind/cmake/config-ix.cmake create mode 100644 shared/sentry/external/crashpad/libunwind/docs/BuildingLibunwind.rst create mode 100644 shared/sentry/external/crashpad/libunwind/docs/CMakeLists.txt create mode 100644 shared/sentry/external/crashpad/libunwind/docs/README.txt create mode 100644 shared/sentry/external/crashpad/libunwind/docs/conf.py create mode 100644 shared/sentry/external/crashpad/libunwind/docs/index.rst create mode 100644 shared/sentry/external/crashpad/libunwind/include/__libunwind_config.h create mode 100644 shared/sentry/external/crashpad/libunwind/include/libunwind.h create mode 100644 shared/sentry/external/crashpad/libunwind/include/mach-o/compact_unwind_encoding.h create mode 100644 shared/sentry/external/crashpad/libunwind/include/unwind.h create mode 100644 shared/sentry/external/crashpad/libunwind/include/unwind_arm_ehabi.h create mode 100644 shared/sentry/external/crashpad/libunwind/include/unwind_itanium.h create mode 100644 shared/sentry/external/crashpad/libunwind/src/AddressSpace.hpp create mode 100644 shared/sentry/external/crashpad/libunwind/src/CMakeLists.txt create mode 100644 shared/sentry/external/crashpad/libunwind/src/CompactUnwinder.hpp create mode 100644 shared/sentry/external/crashpad/libunwind/src/DwarfInstructions.hpp create mode 100644 shared/sentry/external/crashpad/libunwind/src/DwarfParser.hpp create mode 100644 shared/sentry/external/crashpad/libunwind/src/EHHeaderParser.hpp create mode 100644 shared/sentry/external/crashpad/libunwind/src/FrameHeaderCache.hpp create mode 100644 shared/sentry/external/crashpad/libunwind/src/RWMutex.hpp create mode 100644 shared/sentry/external/crashpad/libunwind/src/Registers.hpp create mode 100644 shared/sentry/external/crashpad/libunwind/src/Unwind-EHABI.cpp create mode 100644 shared/sentry/external/crashpad/libunwind/src/Unwind-EHABI.h create mode 100644 shared/sentry/external/crashpad/libunwind/src/Unwind-seh.cpp create mode 100644 shared/sentry/external/crashpad/libunwind/src/Unwind-sjlj.c create mode 100644 shared/sentry/external/crashpad/libunwind/src/UnwindCursor.hpp create mode 100644 shared/sentry/external/crashpad/libunwind/src/UnwindLevel1-gcc-ext.c create mode 100644 shared/sentry/external/crashpad/libunwind/src/UnwindLevel1.c create mode 100644 shared/sentry/external/crashpad/libunwind/src/UnwindRegistersRestore.S create mode 100644 shared/sentry/external/crashpad/libunwind/src/UnwindRegistersSave.S create mode 100644 shared/sentry/external/crashpad/libunwind/src/Unwind_AppleExtras.cpp create mode 100644 shared/sentry/external/crashpad/libunwind/src/assembly.h create mode 100644 shared/sentry/external/crashpad/libunwind/src/cet_unwind.h create mode 100644 shared/sentry/external/crashpad/libunwind/src/config.h create mode 100644 shared/sentry/external/crashpad/libunwind/src/dwarf2.h create mode 100644 shared/sentry/external/crashpad/libunwind/src/libunwind.cpp create mode 100644 shared/sentry/external/crashpad/libunwind/src/libunwind_ext.h create mode 100644 shared/sentry/external/crashpad/libunwind/test/CMakeLists.txt create mode 100644 shared/sentry/external/crashpad/libunwind/test/alignment.compile.pass.cpp create mode 100644 shared/sentry/external/crashpad/libunwind/test/floatregister.pass.cpp create mode 100644 shared/sentry/external/crashpad/libunwind/test/forceunwind.pass.cpp create mode 100644 shared/sentry/external/crashpad/libunwind/test/frameheadercache_test.pass.cpp create mode 100644 shared/sentry/external/crashpad/libunwind/test/libunwind/__init__.py create mode 100644 shared/sentry/external/crashpad/libunwind/test/libunwind/test/__init__.py create mode 100644 shared/sentry/external/crashpad/libunwind/test/libunwind/test/config.py create mode 100644 shared/sentry/external/crashpad/libunwind/test/libunwind_01.pass.cpp create mode 100644 shared/sentry/external/crashpad/libunwind/test/libunwind_02.pass.cpp create mode 100644 shared/sentry/external/crashpad/libunwind/test/lit.cfg.py create mode 100644 shared/sentry/external/crashpad/libunwind/test/lit.site.cfg.in create mode 100644 shared/sentry/external/crashpad/libunwind/test/remember_state_leak.pass.sh.s create mode 100644 shared/sentry/external/crashpad/libunwind/test/signal_frame.pass.cpp create mode 100644 shared/sentry/external/crashpad/libunwind/test/signal_unwind.pass.cpp create mode 100644 shared/sentry/external/crashpad/libunwind/test/unw_getcontext.pass.cpp create mode 100644 shared/sentry/external/crashpad/libunwind/test/unwind_leaffunction.pass.cpp create mode 100644 shared/sentry/external/crashpad/minidump/BUILD.gn create mode 100644 shared/sentry/external/crashpad/minidump/CMakeLists.txt create mode 100644 shared/sentry/external/crashpad/minidump/minidump_annotation_writer.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_annotation_writer.h create mode 100644 shared/sentry/external/crashpad/minidump/minidump_annotation_writer_test.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_byte_array_writer.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_byte_array_writer.h create mode 100644 shared/sentry/external/crashpad/minidump/minidump_byte_array_writer_test.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_context.h create mode 100644 shared/sentry/external/crashpad/minidump/minidump_context_writer.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_context_writer.h create mode 100644 shared/sentry/external/crashpad/minidump/minidump_context_writer_test.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_crashpad_info_writer.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_crashpad_info_writer.h create mode 100644 shared/sentry/external/crashpad/minidump/minidump_crashpad_info_writer_test.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_exception_writer.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_exception_writer.h create mode 100644 shared/sentry/external/crashpad/minidump/minidump_exception_writer_test.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_extensions.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_extensions.h create mode 100644 shared/sentry/external/crashpad/minidump/minidump_file_writer.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_file_writer.h create mode 100644 shared/sentry/external/crashpad/minidump/minidump_file_writer_test.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_handle_writer.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_handle_writer.h create mode 100644 shared/sentry/external/crashpad/minidump/minidump_handle_writer_test.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_memory_info_writer.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_memory_info_writer.h create mode 100644 shared/sentry/external/crashpad/minidump/minidump_memory_info_writer_test.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_memory_writer.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_memory_writer.h create mode 100644 shared/sentry/external/crashpad/minidump/minidump_memory_writer_test.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_misc_info_writer.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_misc_info_writer.h create mode 100644 shared/sentry/external/crashpad/minidump/minidump_misc_info_writer_test.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_module_crashpad_info_writer.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_module_crashpad_info_writer.h create mode 100644 shared/sentry/external/crashpad/minidump/minidump_module_crashpad_info_writer_test.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_module_writer.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_module_writer.h create mode 100644 shared/sentry/external/crashpad/minidump/minidump_module_writer_test.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_rva_list_writer.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_rva_list_writer.h create mode 100644 shared/sentry/external/crashpad/minidump/minidump_rva_list_writer_test.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_simple_string_dictionary_writer.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_simple_string_dictionary_writer.h create mode 100644 shared/sentry/external/crashpad/minidump/minidump_simple_string_dictionary_writer_test.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_stacktrace_writer.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_stacktrace_writer.h create mode 100644 shared/sentry/external/crashpad/minidump/minidump_stream_writer.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_stream_writer.h create mode 100644 shared/sentry/external/crashpad/minidump/minidump_string_writer.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_string_writer.h create mode 100644 shared/sentry/external/crashpad/minidump/minidump_string_writer_test.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_system_info_writer.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_system_info_writer.h create mode 100644 shared/sentry/external/crashpad/minidump/minidump_system_info_writer_test.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_thread_id_map.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_thread_id_map.h create mode 100644 shared/sentry/external/crashpad/minidump/minidump_thread_id_map_test.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_thread_writer.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_thread_writer.h create mode 100644 shared/sentry/external/crashpad/minidump/minidump_thread_writer_test.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_unloaded_module_writer.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_unloaded_module_writer.h create mode 100644 shared/sentry/external/crashpad/minidump/minidump_unloaded_module_writer_test.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_user_extension_stream_data_source.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_user_extension_stream_data_source.h create mode 100644 shared/sentry/external/crashpad/minidump/minidump_user_stream_writer.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_user_stream_writer.h create mode 100644 shared/sentry/external/crashpad/minidump/minidump_user_stream_writer_test.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_writable.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_writable.h create mode 100644 shared/sentry/external/crashpad/minidump/minidump_writable_test.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_writer_util.cc create mode 100644 shared/sentry/external/crashpad/minidump/minidump_writer_util.h create mode 100644 shared/sentry/external/crashpad/minidump/test/minidump_byte_array_writer_test_util.cc create mode 100644 shared/sentry/external/crashpad/minidump/test/minidump_byte_array_writer_test_util.h create mode 100644 shared/sentry/external/crashpad/minidump/test/minidump_context_test_util.cc create mode 100644 shared/sentry/external/crashpad/minidump/test/minidump_context_test_util.h create mode 100644 shared/sentry/external/crashpad/minidump/test/minidump_file_writer_test_util.cc create mode 100644 shared/sentry/external/crashpad/minidump/test/minidump_file_writer_test_util.h create mode 100644 shared/sentry/external/crashpad/minidump/test/minidump_memory_writer_test_util.cc create mode 100644 shared/sentry/external/crashpad/minidump/test/minidump_memory_writer_test_util.h create mode 100644 shared/sentry/external/crashpad/minidump/test/minidump_rva_list_test_util.cc create mode 100644 shared/sentry/external/crashpad/minidump/test/minidump_rva_list_test_util.h create mode 100644 shared/sentry/external/crashpad/minidump/test/minidump_string_writer_test_util.cc create mode 100644 shared/sentry/external/crashpad/minidump/test/minidump_string_writer_test_util.h create mode 100644 shared/sentry/external/crashpad/minidump/test/minidump_user_extension_stream_util.cc create mode 100644 shared/sentry/external/crashpad/minidump/test/minidump_user_extension_stream_util.h create mode 100644 shared/sentry/external/crashpad/minidump/test/minidump_writable_test_util.cc create mode 100644 shared/sentry/external/crashpad/minidump/test/minidump_writable_test_util.h create mode 100644 shared/sentry/external/crashpad/navbar.md create mode 100644 shared/sentry/external/crashpad/package.h create mode 100644 shared/sentry/external/crashpad/snapshot/BUILD.gn create mode 100644 shared/sentry/external/crashpad/snapshot/CMakeLists.txt create mode 100644 shared/sentry/external/crashpad/snapshot/annotation_snapshot.cc create mode 100644 shared/sentry/external/crashpad/snapshot/annotation_snapshot.h create mode 100644 shared/sentry/external/crashpad/snapshot/capture_memory.cc create mode 100644 shared/sentry/external/crashpad/snapshot/capture_memory.h create mode 100644 shared/sentry/external/crashpad/snapshot/cpu_architecture.h create mode 100644 shared/sentry/external/crashpad/snapshot/cpu_context.cc create mode 100644 shared/sentry/external/crashpad/snapshot/cpu_context.h create mode 100644 shared/sentry/external/crashpad/snapshot/cpu_context_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/crashpad_info_client_options.cc create mode 100644 shared/sentry/external/crashpad/snapshot/crashpad_info_client_options.h create mode 100644 shared/sentry/external/crashpad/snapshot/crashpad_info_client_options_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/crashpad_info_client_options_test_module.cc create mode 100644 shared/sentry/external/crashpad/snapshot/crashpad_info_size_test_module.cc create mode 100644 shared/sentry/external/crashpad/snapshot/crashpad_info_size_test_note.S create mode 100644 shared/sentry/external/crashpad/snapshot/crashpad_types/crashpad_info_reader.cc create mode 100644 shared/sentry/external/crashpad/snapshot/crashpad_types/crashpad_info_reader.h create mode 100644 shared/sentry/external/crashpad/snapshot/crashpad_types/crashpad_info_reader_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/crashpad_types/image_annotation_reader.cc create mode 100644 shared/sentry/external/crashpad/snapshot/crashpad_types/image_annotation_reader.h create mode 100644 shared/sentry/external/crashpad/snapshot/crashpad_types/image_annotation_reader_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/elf/elf_dynamic_array_reader.cc create mode 100644 shared/sentry/external/crashpad/snapshot/elf/elf_dynamic_array_reader.h create mode 100644 shared/sentry/external/crashpad/snapshot/elf/elf_image_reader.cc create mode 100644 shared/sentry/external/crashpad/snapshot/elf/elf_image_reader.h create mode 100644 shared/sentry/external/crashpad/snapshot/elf/elf_image_reader_fuzzer.cc create mode 100644 shared/sentry/external/crashpad/snapshot/elf/elf_image_reader_fuzzer_corpus/.gitattributes create mode 100755 shared/sentry/external/crashpad/snapshot/elf/elf_image_reader_fuzzer_corpus/crashpad_snapshot_test_both_dt_hash_styles.so create mode 100755 shared/sentry/external/crashpad/snapshot/elf/elf_image_reader_fuzzer_corpus/ret42 create mode 100644 shared/sentry/external/crashpad/snapshot/elf/elf_image_reader_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/elf/elf_image_reader_test_note.S create mode 100644 shared/sentry/external/crashpad/snapshot/elf/elf_symbol_table_reader.cc create mode 100644 shared/sentry/external/crashpad/snapshot/elf/elf_symbol_table_reader.h create mode 100644 shared/sentry/external/crashpad/snapshot/elf/module_snapshot_elf.cc create mode 100644 shared/sentry/external/crashpad/snapshot/elf/module_snapshot_elf.h create mode 100644 shared/sentry/external/crashpad/snapshot/elf/test_exported_symbols.sym create mode 100644 shared/sentry/external/crashpad/snapshot/exception_snapshot.h create mode 100644 shared/sentry/external/crashpad/snapshot/fuchsia/cpu_context_fuchsia.cc create mode 100644 shared/sentry/external/crashpad/snapshot/fuchsia/cpu_context_fuchsia.h create mode 100644 shared/sentry/external/crashpad/snapshot/fuchsia/exception_snapshot_fuchsia.cc create mode 100644 shared/sentry/external/crashpad/snapshot/fuchsia/exception_snapshot_fuchsia.h create mode 100644 shared/sentry/external/crashpad/snapshot/fuchsia/memory_map_fuchsia.cc create mode 100644 shared/sentry/external/crashpad/snapshot/fuchsia/memory_map_fuchsia.h create mode 100644 shared/sentry/external/crashpad/snapshot/fuchsia/memory_map_region_snapshot_fuchsia.cc create mode 100644 shared/sentry/external/crashpad/snapshot/fuchsia/memory_map_region_snapshot_fuchsia.h create mode 100644 shared/sentry/external/crashpad/snapshot/fuchsia/process_reader_fuchsia.cc create mode 100644 shared/sentry/external/crashpad/snapshot/fuchsia/process_reader_fuchsia.h create mode 100644 shared/sentry/external/crashpad/snapshot/fuchsia/process_reader_fuchsia_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/fuchsia/process_snapshot_fuchsia.cc create mode 100644 shared/sentry/external/crashpad/snapshot/fuchsia/process_snapshot_fuchsia.h create mode 100644 shared/sentry/external/crashpad/snapshot/fuchsia/process_snapshot_fuchsia_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/fuchsia/system_snapshot_fuchsia.cc create mode 100644 shared/sentry/external/crashpad/snapshot/fuchsia/system_snapshot_fuchsia.h create mode 100644 shared/sentry/external/crashpad/snapshot/fuchsia/thread_snapshot_fuchsia.cc create mode 100644 shared/sentry/external/crashpad/snapshot/fuchsia/thread_snapshot_fuchsia.h create mode 100644 shared/sentry/external/crashpad/snapshot/handle_snapshot.cc create mode 100644 shared/sentry/external/crashpad/snapshot/handle_snapshot.h create mode 100644 shared/sentry/external/crashpad/snapshot/hash_types_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/ios/exception_snapshot_ios_intermediate_dump.cc create mode 100644 shared/sentry/external/crashpad/snapshot/ios/exception_snapshot_ios_intermediate_dump.h create mode 100644 shared/sentry/external/crashpad/snapshot/ios/intermediate_dump_reader_util.cc create mode 100644 shared/sentry/external/crashpad/snapshot/ios/intermediate_dump_reader_util.h create mode 100644 shared/sentry/external/crashpad/snapshot/ios/memory_snapshot_ios_intermediate_dump.cc create mode 100644 shared/sentry/external/crashpad/snapshot/ios/memory_snapshot_ios_intermediate_dump.h create mode 100644 shared/sentry/external/crashpad/snapshot/ios/memory_snapshot_ios_intermediate_dump_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/ios/module_snapshot_ios_intermediate_dump.cc create mode 100644 shared/sentry/external/crashpad/snapshot/ios/module_snapshot_ios_intermediate_dump.h create mode 100644 shared/sentry/external/crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump.cc create mode 100644 shared/sentry/external/crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump.h create mode 100644 shared/sentry/external/crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/ios/system_snapshot_ios_intermediate_dump.cc create mode 100644 shared/sentry/external/crashpad/snapshot/ios/system_snapshot_ios_intermediate_dump.h create mode 100644 shared/sentry/external/crashpad/snapshot/ios/testdata/crash-1fa088dda0adb41459d063078a0f384a0bb8eefa create mode 100644 shared/sentry/external/crashpad/snapshot/ios/testdata/crash-5726011582644224 create mode 100644 shared/sentry/external/crashpad/snapshot/ios/thread_snapshot_ios_intermediate_dump.cc create mode 100644 shared/sentry/external/crashpad/snapshot/ios/thread_snapshot_ios_intermediate_dump.h create mode 100644 shared/sentry/external/crashpad/snapshot/linux/capture_memory_delegate_linux.cc create mode 100644 shared/sentry/external/crashpad/snapshot/linux/capture_memory_delegate_linux.h create mode 100644 shared/sentry/external/crashpad/snapshot/linux/cpu_context_linux.cc create mode 100644 shared/sentry/external/crashpad/snapshot/linux/cpu_context_linux.h create mode 100644 shared/sentry/external/crashpad/snapshot/linux/debug_rendezvous.cc create mode 100644 shared/sentry/external/crashpad/snapshot/linux/debug_rendezvous.h create mode 100644 shared/sentry/external/crashpad/snapshot/linux/debug_rendezvous_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/linux/exception_snapshot_linux.cc create mode 100644 shared/sentry/external/crashpad/snapshot/linux/exception_snapshot_linux.h create mode 100644 shared/sentry/external/crashpad/snapshot/linux/exception_snapshot_linux_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/linux/process_reader_linux.cc create mode 100644 shared/sentry/external/crashpad/snapshot/linux/process_reader_linux.h create mode 100644 shared/sentry/external/crashpad/snapshot/linux/process_reader_linux_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/linux/process_snapshot_linux.cc create mode 100644 shared/sentry/external/crashpad/snapshot/linux/process_snapshot_linux.h create mode 100644 shared/sentry/external/crashpad/snapshot/linux/signal_context.h create mode 100644 shared/sentry/external/crashpad/snapshot/linux/system_snapshot_linux.cc create mode 100644 shared/sentry/external/crashpad/snapshot/linux/system_snapshot_linux.h create mode 100644 shared/sentry/external/crashpad/snapshot/linux/system_snapshot_linux_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/linux/test_modules.cc create mode 100644 shared/sentry/external/crashpad/snapshot/linux/test_modules.h create mode 100644 shared/sentry/external/crashpad/snapshot/linux/thread_snapshot_linux.cc create mode 100644 shared/sentry/external/crashpad/snapshot/linux/thread_snapshot_linux.h create mode 100644 shared/sentry/external/crashpad/snapshot/mac/cpu_context_mac.cc create mode 100644 shared/sentry/external/crashpad/snapshot/mac/cpu_context_mac.h create mode 100644 shared/sentry/external/crashpad/snapshot/mac/cpu_context_mac_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/mac/exception_snapshot_mac.cc create mode 100644 shared/sentry/external/crashpad/snapshot/mac/exception_snapshot_mac.h create mode 100644 shared/sentry/external/crashpad/snapshot/mac/mach_o_image_annotations_reader.cc create mode 100644 shared/sentry/external/crashpad/snapshot/mac/mach_o_image_annotations_reader.h create mode 100644 shared/sentry/external/crashpad/snapshot/mac/mach_o_image_annotations_reader_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/mac/mach_o_image_annotations_reader_test_module_crashy_initializer.cc create mode 100644 shared/sentry/external/crashpad/snapshot/mac/mach_o_image_annotations_reader_test_no_op.cc create mode 100644 shared/sentry/external/crashpad/snapshot/mac/mach_o_image_reader.cc create mode 100644 shared/sentry/external/crashpad/snapshot/mac/mach_o_image_reader.h create mode 100644 shared/sentry/external/crashpad/snapshot/mac/mach_o_image_reader_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/mac/mach_o_image_segment_reader.cc create mode 100644 shared/sentry/external/crashpad/snapshot/mac/mach_o_image_segment_reader.h create mode 100644 shared/sentry/external/crashpad/snapshot/mac/mach_o_image_segment_reader_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/mac/mach_o_image_symbol_table_reader.cc create mode 100644 shared/sentry/external/crashpad/snapshot/mac/mach_o_image_symbol_table_reader.h create mode 100644 shared/sentry/external/crashpad/snapshot/mac/module_snapshot_mac.cc create mode 100644 shared/sentry/external/crashpad/snapshot/mac/module_snapshot_mac.h create mode 100644 shared/sentry/external/crashpad/snapshot/mac/process_reader_mac.cc create mode 100644 shared/sentry/external/crashpad/snapshot/mac/process_reader_mac.h create mode 100644 shared/sentry/external/crashpad/snapshot/mac/process_reader_mac_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/mac/process_snapshot_mac.cc create mode 100644 shared/sentry/external/crashpad/snapshot/mac/process_snapshot_mac.h create mode 100644 shared/sentry/external/crashpad/snapshot/mac/process_types.cc create mode 100644 shared/sentry/external/crashpad/snapshot/mac/process_types.h create mode 100644 shared/sentry/external/crashpad/snapshot/mac/process_types/all.proctype create mode 100644 shared/sentry/external/crashpad/snapshot/mac/process_types/annotation.proctype create mode 100644 shared/sentry/external/crashpad/snapshot/mac/process_types/crashpad_info.proctype create mode 100644 shared/sentry/external/crashpad/snapshot/mac/process_types/crashreporterclient.proctype create mode 100644 shared/sentry/external/crashpad/snapshot/mac/process_types/custom.cc create mode 100644 shared/sentry/external/crashpad/snapshot/mac/process_types/dyld_images.proctype create mode 100644 shared/sentry/external/crashpad/snapshot/mac/process_types/flavors.h create mode 100644 shared/sentry/external/crashpad/snapshot/mac/process_types/internal.h create mode 100644 shared/sentry/external/crashpad/snapshot/mac/process_types/loader.proctype create mode 100644 shared/sentry/external/crashpad/snapshot/mac/process_types/nlist.proctype create mode 100644 shared/sentry/external/crashpad/snapshot/mac/process_types/traits.h create mode 100644 shared/sentry/external/crashpad/snapshot/mac/process_types_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/mac/system_snapshot_mac.cc create mode 100644 shared/sentry/external/crashpad/snapshot/mac/system_snapshot_mac.h create mode 100644 shared/sentry/external/crashpad/snapshot/mac/system_snapshot_mac_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/mac/thread_snapshot_mac.cc create mode 100644 shared/sentry/external/crashpad/snapshot/mac/thread_snapshot_mac.h create mode 100644 shared/sentry/external/crashpad/snapshot/memory_map_region_snapshot.h create mode 100644 shared/sentry/external/crashpad/snapshot/memory_snapshot.cc create mode 100644 shared/sentry/external/crashpad/snapshot/memory_snapshot.h create mode 100644 shared/sentry/external/crashpad/snapshot/memory_snapshot_generic.h create mode 100644 shared/sentry/external/crashpad/snapshot/memory_snapshot_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/minidump/exception_snapshot_minidump.cc create mode 100644 shared/sentry/external/crashpad/snapshot/minidump/exception_snapshot_minidump.h create mode 100644 shared/sentry/external/crashpad/snapshot/minidump/memory_snapshot_minidump.cc create mode 100644 shared/sentry/external/crashpad/snapshot/minidump/memory_snapshot_minidump.h create mode 100644 shared/sentry/external/crashpad/snapshot/minidump/minidump_annotation_reader.cc create mode 100644 shared/sentry/external/crashpad/snapshot/minidump/minidump_annotation_reader.h create mode 100644 shared/sentry/external/crashpad/snapshot/minidump/minidump_context_converter.cc create mode 100644 shared/sentry/external/crashpad/snapshot/minidump/minidump_context_converter.h create mode 100644 shared/sentry/external/crashpad/snapshot/minidump/minidump_simple_string_dictionary_reader.cc create mode 100644 shared/sentry/external/crashpad/snapshot/minidump/minidump_simple_string_dictionary_reader.h create mode 100644 shared/sentry/external/crashpad/snapshot/minidump/minidump_stream.h create mode 100644 shared/sentry/external/crashpad/snapshot/minidump/minidump_string_list_reader.cc create mode 100644 shared/sentry/external/crashpad/snapshot/minidump/minidump_string_list_reader.h create mode 100644 shared/sentry/external/crashpad/snapshot/minidump/minidump_string_reader.cc create mode 100644 shared/sentry/external/crashpad/snapshot/minidump/minidump_string_reader.h create mode 100644 shared/sentry/external/crashpad/snapshot/minidump/module_snapshot_minidump.cc create mode 100644 shared/sentry/external/crashpad/snapshot/minidump/module_snapshot_minidump.h create mode 100644 shared/sentry/external/crashpad/snapshot/minidump/process_snapshot_minidump.cc create mode 100644 shared/sentry/external/crashpad/snapshot/minidump/process_snapshot_minidump.h create mode 100644 shared/sentry/external/crashpad/snapshot/minidump/process_snapshot_minidump_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/minidump/system_snapshot_minidump.cc create mode 100644 shared/sentry/external/crashpad/snapshot/minidump/system_snapshot_minidump.h create mode 100644 shared/sentry/external/crashpad/snapshot/minidump/thread_snapshot_minidump.cc create mode 100644 shared/sentry/external/crashpad/snapshot/minidump/thread_snapshot_minidump.h create mode 100644 shared/sentry/external/crashpad/snapshot/module_snapshot.h create mode 100644 shared/sentry/external/crashpad/snapshot/posix/timezone.cc create mode 100644 shared/sentry/external/crashpad/snapshot/posix/timezone.h create mode 100644 shared/sentry/external/crashpad/snapshot/posix/timezone_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/process_snapshot.h create mode 100644 shared/sentry/external/crashpad/snapshot/sanitized/memory_snapshot_sanitized.cc create mode 100644 shared/sentry/external/crashpad/snapshot/sanitized/memory_snapshot_sanitized.h create mode 100644 shared/sentry/external/crashpad/snapshot/sanitized/module_snapshot_sanitized.cc create mode 100644 shared/sentry/external/crashpad/snapshot/sanitized/module_snapshot_sanitized.h create mode 100644 shared/sentry/external/crashpad/snapshot/sanitized/process_snapshot_sanitized.cc create mode 100644 shared/sentry/external/crashpad/snapshot/sanitized/process_snapshot_sanitized.h create mode 100644 shared/sentry/external/crashpad/snapshot/sanitized/process_snapshot_sanitized_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/sanitized/sanitization_information.cc create mode 100644 shared/sentry/external/crashpad/snapshot/sanitized/sanitization_information.h create mode 100644 shared/sentry/external/crashpad/snapshot/sanitized/sanitization_information_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/sanitized/thread_snapshot_sanitized.cc create mode 100644 shared/sentry/external/crashpad/snapshot/sanitized/thread_snapshot_sanitized.h create mode 100644 shared/sentry/external/crashpad/snapshot/snapshot_constants.h create mode 100644 shared/sentry/external/crashpad/snapshot/system_snapshot.h create mode 100644 shared/sentry/external/crashpad/snapshot/test/test_cpu_context.cc create mode 100644 shared/sentry/external/crashpad/snapshot/test/test_cpu_context.h create mode 100644 shared/sentry/external/crashpad/snapshot/test/test_exception_snapshot.cc create mode 100644 shared/sentry/external/crashpad/snapshot/test/test_exception_snapshot.h create mode 100644 shared/sentry/external/crashpad/snapshot/test/test_memory_map_region_snapshot.cc create mode 100644 shared/sentry/external/crashpad/snapshot/test/test_memory_map_region_snapshot.h create mode 100644 shared/sentry/external/crashpad/snapshot/test/test_memory_snapshot.cc create mode 100644 shared/sentry/external/crashpad/snapshot/test/test_memory_snapshot.h create mode 100644 shared/sentry/external/crashpad/snapshot/test/test_module_snapshot.cc create mode 100644 shared/sentry/external/crashpad/snapshot/test/test_module_snapshot.h create mode 100644 shared/sentry/external/crashpad/snapshot/test/test_process_snapshot.cc create mode 100644 shared/sentry/external/crashpad/snapshot/test/test_process_snapshot.h create mode 100644 shared/sentry/external/crashpad/snapshot/test/test_system_snapshot.cc create mode 100644 shared/sentry/external/crashpad/snapshot/test/test_system_snapshot.h create mode 100644 shared/sentry/external/crashpad/snapshot/test/test_thread_snapshot.cc create mode 100644 shared/sentry/external/crashpad/snapshot/test/test_thread_snapshot.h create mode 100644 shared/sentry/external/crashpad/snapshot/thread_snapshot.h create mode 100644 shared/sentry/external/crashpad/snapshot/unloaded_module_snapshot.cc create mode 100644 shared/sentry/external/crashpad/snapshot/unloaded_module_snapshot.h create mode 100644 shared/sentry/external/crashpad/snapshot/win/capture_memory_delegate_win.cc create mode 100644 shared/sentry/external/crashpad/snapshot/win/capture_memory_delegate_win.h create mode 100644 shared/sentry/external/crashpad/snapshot/win/cpu_context_win.cc create mode 100644 shared/sentry/external/crashpad/snapshot/win/cpu_context_win.h create mode 100644 shared/sentry/external/crashpad/snapshot/win/cpu_context_win_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/win/crashpad_snapshot_test_annotations.cc create mode 100644 shared/sentry/external/crashpad/snapshot/win/crashpad_snapshot_test_crashing_child.cc create mode 100644 shared/sentry/external/crashpad/snapshot/win/crashpad_snapshot_test_dump_without_crashing.cc create mode 100644 shared/sentry/external/crashpad/snapshot/win/crashpad_snapshot_test_extra_memory_ranges.cc create mode 100644 shared/sentry/external/crashpad/snapshot/win/crashpad_snapshot_test_image_reader.cc create mode 100644 shared/sentry/external/crashpad/snapshot/win/crashpad_snapshot_test_image_reader_module.cc create mode 100755 shared/sentry/external/crashpad/snapshot/win/end_to_end_test.py create mode 100644 shared/sentry/external/crashpad/snapshot/win/exception_snapshot_win.cc create mode 100644 shared/sentry/external/crashpad/snapshot/win/exception_snapshot_win.h create mode 100644 shared/sentry/external/crashpad/snapshot/win/exception_snapshot_win_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/win/extra_memory_ranges_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/win/memory_map_region_snapshot_win.cc create mode 100644 shared/sentry/external/crashpad/snapshot/win/memory_map_region_snapshot_win.h create mode 100644 shared/sentry/external/crashpad/snapshot/win/module_snapshot_win.cc create mode 100644 shared/sentry/external/crashpad/snapshot/win/module_snapshot_win.h create mode 100644 shared/sentry/external/crashpad/snapshot/win/module_snapshot_win_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/win/pe_image_annotations_reader.cc create mode 100644 shared/sentry/external/crashpad/snapshot/win/pe_image_annotations_reader.h create mode 100644 shared/sentry/external/crashpad/snapshot/win/pe_image_reader.cc create mode 100644 shared/sentry/external/crashpad/snapshot/win/pe_image_reader.h create mode 100644 shared/sentry/external/crashpad/snapshot/win/pe_image_reader_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/win/pe_image_resource_reader.cc create mode 100644 shared/sentry/external/crashpad/snapshot/win/pe_image_resource_reader.h create mode 100644 shared/sentry/external/crashpad/snapshot/win/process_reader_win.cc create mode 100644 shared/sentry/external/crashpad/snapshot/win/process_reader_win.h create mode 100644 shared/sentry/external/crashpad/snapshot/win/process_reader_win_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/win/process_snapshot_win.cc create mode 100644 shared/sentry/external/crashpad/snapshot/win/process_snapshot_win.h create mode 100644 shared/sentry/external/crashpad/snapshot/win/process_snapshot_win_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/win/process_subrange_reader.cc create mode 100644 shared/sentry/external/crashpad/snapshot/win/process_subrange_reader.h create mode 100644 shared/sentry/external/crashpad/snapshot/win/system_snapshot_win.cc create mode 100644 shared/sentry/external/crashpad/snapshot/win/system_snapshot_win.h create mode 100644 shared/sentry/external/crashpad/snapshot/win/system_snapshot_win_test.cc create mode 100644 shared/sentry/external/crashpad/snapshot/win/thread_snapshot_win.cc create mode 100644 shared/sentry/external/crashpad/snapshot/win/thread_snapshot_win.h create mode 100644 shared/sentry/external/crashpad/snapshot/x86/cpuid_reader.cc create mode 100644 shared/sentry/external/crashpad/snapshot/x86/cpuid_reader.h create mode 100644 shared/sentry/external/crashpad/test/BUILD.gn create mode 100644 shared/sentry/external/crashpad/test/errors.cc create mode 100644 shared/sentry/external/crashpad/test/errors.h create mode 100644 shared/sentry/external/crashpad/test/file.cc create mode 100644 shared/sentry/external/crashpad/test/file.h create mode 100644 shared/sentry/external/crashpad/test/filesystem.cc create mode 100644 shared/sentry/external/crashpad/test/filesystem.h create mode 100644 shared/sentry/external/crashpad/test/fuchsia_crashpad_tests.cmx create mode 100644 shared/sentry/external/crashpad/test/gtest_death.h create mode 100644 shared/sentry/external/crashpad/test/gtest_main.cc create mode 100644 shared/sentry/external/crashpad/test/hex_string.cc create mode 100644 shared/sentry/external/crashpad/test/hex_string.h create mode 100644 shared/sentry/external/crashpad/test/hex_string_test.cc create mode 100644 shared/sentry/external/crashpad/test/ios/BUILD.gn create mode 100644 shared/sentry/external/crashpad/test/ios/cptest_google_test_runner.mm create mode 100644 shared/sentry/external/crashpad/test/ios/cptest_google_test_runner_delegate.h create mode 100644 shared/sentry/external/crashpad/test/ios/crash_type_xctest.mm create mode 100644 shared/sentry/external/crashpad/test/ios/google_test_setup.h create mode 100644 shared/sentry/external/crashpad/test/ios/google_test_setup.mm create mode 100644 shared/sentry/external/crashpad/test/ios/host/BUILD.gn create mode 100644 shared/sentry/external/crashpad/test/ios/host/Info.plist create mode 100644 shared/sentry/external/crashpad/test/ios/host/cptest_application_delegate.h create mode 100644 shared/sentry/external/crashpad/test/ios/host/cptest_application_delegate.mm create mode 100644 shared/sentry/external/crashpad/test/ios/host/cptest_crash_view_controller.h create mode 100644 shared/sentry/external/crashpad/test/ios/host/cptest_crash_view_controller.mm create mode 100644 shared/sentry/external/crashpad/test/ios/host/cptest_shared_object.h create mode 100644 shared/sentry/external/crashpad/test/ios/host/handler_forbidden_allocators.cc create mode 100644 shared/sentry/external/crashpad/test/ios/host/handler_forbidden_allocators.h create mode 100644 shared/sentry/external/crashpad/test/ios/host/main.mm create mode 100644 shared/sentry/external/crashpad/test/linux/fake_ptrace_connection.cc create mode 100644 shared/sentry/external/crashpad/test/linux/fake_ptrace_connection.h create mode 100644 shared/sentry/external/crashpad/test/linux/get_tls.cc create mode 100644 shared/sentry/external/crashpad/test/linux/get_tls.h create mode 100644 shared/sentry/external/crashpad/test/mac/dyld.cc create mode 100644 shared/sentry/external/crashpad/test/mac/dyld.h create mode 100644 shared/sentry/external/crashpad/test/mac/exception_swallower.cc create mode 100644 shared/sentry/external/crashpad/test/mac/exception_swallower.h create mode 100644 shared/sentry/external/crashpad/test/mac/mach_errors.cc create mode 100644 shared/sentry/external/crashpad/test/mac/mach_errors.h create mode 100644 shared/sentry/external/crashpad/test/mac/mach_multiprocess.cc create mode 100644 shared/sentry/external/crashpad/test/mac/mach_multiprocess.h create mode 100644 shared/sentry/external/crashpad/test/mac/mach_multiprocess_test.cc create mode 100644 shared/sentry/external/crashpad/test/main_arguments.cc create mode 100644 shared/sentry/external/crashpad/test/main_arguments.h create mode 100644 shared/sentry/external/crashpad/test/main_arguments_test.cc create mode 100644 shared/sentry/external/crashpad/test/multiprocess.h create mode 100644 shared/sentry/external/crashpad/test/multiprocess_exec.cc create mode 100644 shared/sentry/external/crashpad/test/multiprocess_exec.h create mode 100644 shared/sentry/external/crashpad/test/multiprocess_exec_fuchsia.cc create mode 100644 shared/sentry/external/crashpad/test/multiprocess_exec_posix.cc create mode 100644 shared/sentry/external/crashpad/test/multiprocess_exec_test.cc create mode 100644 shared/sentry/external/crashpad/test/multiprocess_exec_test_child.cc create mode 100644 shared/sentry/external/crashpad/test/multiprocess_exec_win.cc create mode 100644 shared/sentry/external/crashpad/test/multiprocess_posix.cc create mode 100644 shared/sentry/external/crashpad/test/multiprocess_posix_test.cc create mode 100644 shared/sentry/external/crashpad/test/process_type.cc create mode 100644 shared/sentry/external/crashpad/test/process_type.h create mode 100644 shared/sentry/external/crashpad/test/scoped_guarded_page.h create mode 100644 shared/sentry/external/crashpad/test/scoped_guarded_page_posix.cc create mode 100644 shared/sentry/external/crashpad/test/scoped_guarded_page_test.cc create mode 100644 shared/sentry/external/crashpad/test/scoped_guarded_page_win.cc create mode 100644 shared/sentry/external/crashpad/test/scoped_module_handle.cc create mode 100644 shared/sentry/external/crashpad/test/scoped_module_handle.h create mode 100644 shared/sentry/external/crashpad/test/scoped_temp_dir.cc create mode 100644 shared/sentry/external/crashpad/test/scoped_temp_dir.h create mode 100644 shared/sentry/external/crashpad/test/scoped_temp_dir_posix.cc create mode 100644 shared/sentry/external/crashpad/test/scoped_temp_dir_test.cc create mode 100644 shared/sentry/external/crashpad/test/scoped_temp_dir_win.cc create mode 100644 shared/sentry/external/crashpad/test/test_paths.cc create mode 100644 shared/sentry/external/crashpad/test/test_paths.h create mode 100644 shared/sentry/external/crashpad/test/test_paths_test.cc create mode 100644 shared/sentry/external/crashpad/test/test_paths_test_data_root.txt create mode 100644 shared/sentry/external/crashpad/test/win/child_launcher.cc create mode 100644 shared/sentry/external/crashpad/test/win/child_launcher.h create mode 100644 shared/sentry/external/crashpad/test/win/win_child_process.cc create mode 100644 shared/sentry/external/crashpad/test/win/win_child_process.h create mode 100644 shared/sentry/external/crashpad/test/win/win_child_process_test.cc create mode 100644 shared/sentry/external/crashpad/test/win/win_multiprocess.cc create mode 100644 shared/sentry/external/crashpad/test/win/win_multiprocess.h create mode 100644 shared/sentry/external/crashpad/test/win/win_multiprocess_test.cc create mode 100644 shared/sentry/external/crashpad/test/win/win_multiprocess_with_temp_dir.cc create mode 100644 shared/sentry/external/crashpad/test/win/win_multiprocess_with_temp_dir.h create mode 100644 shared/sentry/external/crashpad/third_party/cpp-httplib/BUILD.gn create mode 100644 shared/sentry/external/crashpad/third_party/cpp-httplib/README.crashpad create mode 100644 shared/sentry/external/crashpad/third_party/cpp-httplib/cpp-httplib/LICENSE create mode 100644 shared/sentry/external/crashpad/third_party/cpp-httplib/cpp-httplib/README.md create mode 100644 shared/sentry/external/crashpad/third_party/cpp-httplib/cpp-httplib/httplib.h create mode 100644 shared/sentry/external/crashpad/third_party/edo/BUILD.gn create mode 100644 shared/sentry/external/crashpad/third_party/edo/README.crashpad create mode 100644 shared/sentry/external/crashpad/third_party/fuchsia/BUILD.gn create mode 100644 shared/sentry/external/crashpad/third_party/fuchsia/README.crashpad create mode 100755 shared/sentry/external/crashpad/third_party/fuchsia/runner.py create mode 100644 shared/sentry/external/crashpad/third_party/getopt/BUILD.gn create mode 100644 shared/sentry/external/crashpad/third_party/getopt/CMakeLists.txt create mode 100644 shared/sentry/external/crashpad/third_party/getopt/LICENSE create mode 100644 shared/sentry/external/crashpad/third_party/getopt/README.crashpad create mode 100644 shared/sentry/external/crashpad/third_party/getopt/getopt.cc create mode 100644 shared/sentry/external/crashpad/third_party/getopt/getopt.h create mode 100644 shared/sentry/external/crashpad/third_party/glibc/BUILD.gn create mode 100644 shared/sentry/external/crashpad/third_party/glibc/COPYING.LIB create mode 100644 shared/sentry/external/crashpad/third_party/glibc/README.crashpad create mode 100644 shared/sentry/external/crashpad/third_party/glibc/elf/elf.h create mode 100644 shared/sentry/external/crashpad/third_party/googletest/BUILD.gn create mode 100644 shared/sentry/external/crashpad/third_party/googletest/README.crashpad create mode 100644 shared/sentry/external/crashpad/third_party/gyp/README.crashpad create mode 100644 shared/sentry/external/crashpad/third_party/linux/README.crashpad create mode 100644 shared/sentry/external/crashpad/third_party/lss/BUILD.gn create mode 100644 shared/sentry/external/crashpad/third_party/lss/README.crashpad create mode 100644 shared/sentry/external/crashpad/third_party/lss/lss.h create mode 100644 shared/sentry/external/crashpad/third_party/lss/lss/.gitignore create mode 100644 shared/sentry/external/crashpad/third_party/lss/lss/DIR_METADATA create mode 100644 shared/sentry/external/crashpad/third_party/lss/lss/LICENSE create mode 100644 shared/sentry/external/crashpad/third_party/lss/lss/OWNERS create mode 100644 shared/sentry/external/crashpad/third_party/lss/lss/README.md create mode 100644 shared/sentry/external/crashpad/third_party/lss/lss/codereview.settings create mode 100644 shared/sentry/external/crashpad/third_party/lss/lss/linux_syscall_support.h create mode 100644 shared/sentry/external/crashpad/third_party/lss/lss/tests/.gitignore create mode 100644 shared/sentry/external/crashpad/third_party/lss/lss/tests/Makefile create mode 100644 shared/sentry/external/crashpad/third_party/lss/lss/tests/README.md create mode 100644 shared/sentry/external/crashpad/third_party/lss/lss/tests/fallocate.c create mode 100644 shared/sentry/external/crashpad/third_party/lss/lss/tests/getrandom.c create mode 100644 shared/sentry/external/crashpad/third_party/lss/lss/tests/lstat.c create mode 100644 shared/sentry/external/crashpad/third_party/lss/lss/tests/sigaction.c create mode 100644 shared/sentry/external/crashpad/third_party/lss/lss/tests/sigtimedwait.c create mode 100644 shared/sentry/external/crashpad/third_party/lss/lss/tests/stat.c create mode 100644 shared/sentry/external/crashpad/third_party/lss/lss/tests/test_skel.h create mode 100644 shared/sentry/external/crashpad/third_party/lss/lss/tests/unlink.c create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/BUILD.gn create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/CMakeLists.txt create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/README.crashpad create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/build/chromeos_buildflags.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/.gitattributes create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/.gitignore create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/.style.yapf create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/AUTHORS create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/BUILD.gn create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/LICENSE create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/README.md create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/BUILD.gn create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/atomicops.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/atomicops_internals_atomicword_compat.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/atomicops_internals_portable.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/auto_reset.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/bit_cast.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/check.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/check_op.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/compiler_specific.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/cxx17_backports.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/debug/alias.cc create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/debug/alias.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/files/file_path.cc create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/files/file_path.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/files/file_util.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/files/file_util_posix.cc create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/files/scoped_file.cc create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/files/scoped_file.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/format_macros.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/fuchsia/fuchsia_logging.cc create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/fuchsia/fuchsia_logging.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/logging.cc create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/logging.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/close_nocancel.cc create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/foundation_util.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/foundation_util.mm create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/mach_logging.cc create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/mach_logging.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_cftyperef.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_ioobject.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_launch_data.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_mach_port.cc create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_mach_port.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_mach_vm.cc create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_mach_vm.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_nsautorelease_pool.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_nsautorelease_pool.mm create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_nsobject.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_typeref.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/memory/free_deleter.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/memory/page_size.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/memory/page_size_posix.cc create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/memory/page_size_win.cc create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/memory/scoped_policy.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/metrics/histogram_functions.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/metrics/histogram_macros.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/metrics/persistent_histogram_allocator.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/notreached.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/numerics/checked_math.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/numerics/checked_math_impl.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/numerics/clamped_math.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/numerics/clamped_math_impl.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/numerics/safe_conversions.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/numerics/safe_conversions_arm_impl.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/numerics/safe_conversions_impl.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/numerics/safe_math.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/numerics/safe_math_arm_impl.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/numerics/safe_math_clang_gcc_impl.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/numerics/safe_math_shared_impl.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/posix/eintr_wrapper.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/posix/safe_strerror.cc create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/posix/safe_strerror.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/process/memory.cc create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/process/memory.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/rand_util.cc create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/rand_util.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/scoped_clear_last_error.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/scoped_clear_last_error_win.cc create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/scoped_generic.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/strings/string_number_conversions.cc create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/strings/string_number_conversions.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/strings/string_piece.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/strings/string_util.cc create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/strings/string_util.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/strings/string_util_posix.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/strings/string_util_win.cc create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/strings/string_util_win.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/strings/stringprintf.cc create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/strings/stringprintf.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/strings/sys_string_conversions.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/strings/sys_string_conversions_mac.mm create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/strings/utf_string_conversion_utils.cc create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/strings/utf_string_conversion_utils.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/strings/utf_string_conversions.cc create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/strings/utf_string_conversions.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/synchronization/condition_variable.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/synchronization/condition_variable_posix.cc create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/synchronization/lock.cc create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/synchronization/lock.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/synchronization/lock_impl.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/synchronization/lock_impl_posix.cc create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/synchronization/lock_impl_win.cc create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/sys_byteorder.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/template_util.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/third_party/icu/LICENSE create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/third_party/icu/README.chromium create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/third_party/icu/icu_utf.cc create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/third_party/icu/icu_utf.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/threading/thread_local_storage.cc create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/threading/thread_local_storage.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/threading/thread_local_storage_posix.cc create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/threading/thread_local_storage_win.cc create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/build/BUILD.gn create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/build/build_config.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/build/buildflag.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/build/buildflag_header.gni create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/build/compiler.gni create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/build/config/BUILD.gn create mode 100755 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/build/find_mac_sdk.py create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/build/ios/Application-Info.plist create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/build/ios/BUILD.gn create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/build/ios/BuildInfo.plist create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/build/ios/Module-Info.plist create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/build/ios/XCTRunnerAddition+Info.plist create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/build/ios/codesign.py create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/build/ios/entitlements.plist create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/build/ios/find_signing_identity.py create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/build/ios/ios_sdk.gni create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/build/ios/plist_util.py create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/build/ios/rules.gni create mode 100755 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/build/ios/sdk_info.py create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/build/ios/strip_arm64e.py create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/build/platform.gni create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/build/sysroot.gni create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/build/win_helper.py create mode 100755 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/build/write_buildflag_header.py create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/codereview.settings create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/testing/BUILD.gn create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/testing/platform_test.h create mode 100644 shared/sentry/external/crashpad/third_party/mini_chromium/utf_string_conversion_utils.mingw.cc create mode 100644 shared/sentry/external/crashpad/third_party/xnu/APPLE_LICENSE create mode 100644 shared/sentry/external/crashpad/third_party/xnu/BUILD.gn create mode 100644 shared/sentry/external/crashpad/third_party/xnu/EXTERNAL_HEADERS/mach-o/loader.h create mode 100644 shared/sentry/external/crashpad/third_party/xnu/README.crashpad create mode 100644 shared/sentry/external/crashpad/third_party/xnu/osfmk/mach/exc.defs create mode 100644 shared/sentry/external/crashpad/third_party/xnu/osfmk/mach/mach_exc.defs create mode 100644 shared/sentry/external/crashpad/third_party/xnu/osfmk/mach/mach_types.defs create mode 100644 shared/sentry/external/crashpad/third_party/xnu/osfmk/mach/machine/machine_types.defs create mode 100644 shared/sentry/external/crashpad/third_party/xnu/osfmk/mach/std_types.defs create mode 100644 shared/sentry/external/crashpad/third_party/zlib/BUILD.gn create mode 100644 shared/sentry/external/crashpad/third_party/zlib/CMakeLists.txt create mode 100644 shared/sentry/external/crashpad/third_party/zlib/README.crashpad create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/BUILD.gn create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/LICENSE create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/OWNERS create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/README.chromium create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/adler32.c create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/compress.c create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/contrib/minizip/ChangeLogUnzip create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/contrib/minizip/Makefile create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/contrib/minizip/crypt.h create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/contrib/minizip/ioapi.c create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/contrib/minizip/ioapi.h create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/contrib/minizip/iowin32.c create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/contrib/minizip/iowin32.h create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/contrib/minizip/miniunz.c create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/contrib/minizip/minizip.c create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/contrib/minizip/mztools.c create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/contrib/minizip/mztools.h create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/contrib/minizip/unzip.c create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/contrib/minizip/unzip.h create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/contrib/minizip/zip.c create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/contrib/minizip/zip.h create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/crc32.c create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/crc32.h create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/crc_folding.c create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/deflate.c create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/deflate.h create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/fill_window_sse.c create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/google.patch create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/google/DEPS create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/google/OWNERS create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/google/compression_utils.cc create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/google/compression_utils.h create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/google/compression_utils_unittest.cc create mode 100755 shared/sentry/external/crashpad/third_party/zlib/zlib/google/test/data/create_test_zip.sh create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/google/test/data/evil.zip create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/google/test/data/evil_via_absolute_file_name.zip create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/google/test/data/evil_via_invalid_utf8.zip create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/google/test/data/test.zip create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/google/test/data/test/foo.txt create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/google/test/data/test/foo/bar.txt create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/google/test/data/test/foo/bar/.hidden create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/google/test/data/test/foo/bar/baz.txt create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/google/test/data/test/foo/bar/quux.txt create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/google/test/data/test_mismatch_size.zip create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/google/test/data/test_nocompress.zip create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/google/zip.cc create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/google/zip.h create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/google/zip_internal.cc create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/google/zip_internal.h create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/google/zip_reader.cc create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/google/zip_reader.h create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/google/zip_reader_unittest.cc create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/google/zip_unittest.cc create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/gzclose.c create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/gzguts.h create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/gzlib.c create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/gzread.c create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/gzwrite.c create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/infback.c create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/inffast.c create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/inffast.h create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/inffixed.h create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/inflate.c create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/inflate.h create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/inftrees.c create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/inftrees.h create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/names.h create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/simd.patch create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/simd_stub.c create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/trees.c create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/trees.h create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/uncompr.c create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/x86.c create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/x86.h create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/zconf.h create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/zlib.h create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/zutil.c create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib/zutil.h create mode 100644 shared/sentry/external/crashpad/third_party/zlib/zlib_crashpad.h create mode 100644 shared/sentry/external/crashpad/tools/BUILD.gn create mode 100644 shared/sentry/external/crashpad/tools/CMakeLists.txt create mode 100644 shared/sentry/external/crashpad/tools/base94_encoder.cc create mode 100644 shared/sentry/external/crashpad/tools/base94_encoder.md create mode 100644 shared/sentry/external/crashpad/tools/crashpad_database_util.cc create mode 100644 shared/sentry/external/crashpad/tools/crashpad_database_util.md create mode 100644 shared/sentry/external/crashpad/tools/crashpad_http_upload.cc create mode 100644 shared/sentry/external/crashpad/tools/crashpad_http_upload.md create mode 100644 shared/sentry/external/crashpad/tools/generate_dump.cc create mode 100644 shared/sentry/external/crashpad/tools/generate_dump.md create mode 100644 shared/sentry/external/crashpad/tools/mac/catch_exception_tool.cc create mode 100644 shared/sentry/external/crashpad/tools/mac/catch_exception_tool.md create mode 100644 shared/sentry/external/crashpad/tools/mac/exception_port_tool.cc create mode 100644 shared/sentry/external/crashpad/tools/mac/exception_port_tool.md create mode 100644 shared/sentry/external/crashpad/tools/mac/on_demand_service_tool.md create mode 100644 shared/sentry/external/crashpad/tools/mac/on_demand_service_tool.mm create mode 100644 shared/sentry/external/crashpad/tools/mac/sectaskaccess_info.plist create mode 100644 shared/sentry/external/crashpad/tools/run_with_crashpad.cc create mode 100644 shared/sentry/external/crashpad/tools/run_with_crashpad.md create mode 100644 shared/sentry/external/crashpad/tools/tool_support.cc create mode 100644 shared/sentry/external/crashpad/tools/tool_support.h create mode 100644 shared/sentry/external/crashpad/util/BUILD.gn create mode 100644 shared/sentry/external/crashpad/util/CMakeLists.txt create mode 100644 shared/sentry/external/crashpad/util/file/delimited_file_reader.cc create mode 100644 shared/sentry/external/crashpad/util/file/delimited_file_reader.h create mode 100644 shared/sentry/external/crashpad/util/file/delimited_file_reader_test.cc create mode 100644 shared/sentry/external/crashpad/util/file/directory_reader.h create mode 100644 shared/sentry/external/crashpad/util/file/directory_reader_posix.cc create mode 100644 shared/sentry/external/crashpad/util/file/directory_reader_test.cc create mode 100644 shared/sentry/external/crashpad/util/file/directory_reader_win.cc create mode 100644 shared/sentry/external/crashpad/util/file/file_helper.cc create mode 100644 shared/sentry/external/crashpad/util/file/file_helper.h create mode 100644 shared/sentry/external/crashpad/util/file/file_io.cc create mode 100644 shared/sentry/external/crashpad/util/file/file_io.h create mode 100644 shared/sentry/external/crashpad/util/file/file_io_posix.cc create mode 100644 shared/sentry/external/crashpad/util/file/file_io_test.cc create mode 100644 shared/sentry/external/crashpad/util/file/file_io_win.cc create mode 100644 shared/sentry/external/crashpad/util/file/file_reader.cc create mode 100644 shared/sentry/external/crashpad/util/file/file_reader.h create mode 100644 shared/sentry/external/crashpad/util/file/file_reader_test.cc create mode 100644 shared/sentry/external/crashpad/util/file/file_seeker.cc create mode 100644 shared/sentry/external/crashpad/util/file/file_seeker.h create mode 100644 shared/sentry/external/crashpad/util/file/file_writer.cc create mode 100644 shared/sentry/external/crashpad/util/file/file_writer.h create mode 100644 shared/sentry/external/crashpad/util/file/filesystem.h create mode 100644 shared/sentry/external/crashpad/util/file/filesystem_posix.cc create mode 100644 shared/sentry/external/crashpad/util/file/filesystem_test.cc create mode 100644 shared/sentry/external/crashpad/util/file/filesystem_win.cc create mode 100644 shared/sentry/external/crashpad/util/file/output_stream_file_writer.cc create mode 100644 shared/sentry/external/crashpad/util/file/output_stream_file_writer.h create mode 100644 shared/sentry/external/crashpad/util/file/scoped_remove_file.cc create mode 100644 shared/sentry/external/crashpad/util/file/scoped_remove_file.h create mode 100644 shared/sentry/external/crashpad/util/file/string_file.cc create mode 100644 shared/sentry/external/crashpad/util/file/string_file.h create mode 100644 shared/sentry/external/crashpad/util/file/string_file_test.cc create mode 100644 shared/sentry/external/crashpad/util/fuchsia/koid_utilities.cc create mode 100644 shared/sentry/external/crashpad/util/fuchsia/koid_utilities.h create mode 100644 shared/sentry/external/crashpad/util/fuchsia/scoped_task_suspend.cc create mode 100644 shared/sentry/external/crashpad/util/fuchsia/scoped_task_suspend.h create mode 100644 shared/sentry/external/crashpad/util/fuchsia/traits.h create mode 100644 shared/sentry/external/crashpad/util/ios/ios_intermediate_dump_data.cc create mode 100644 shared/sentry/external/crashpad/util/ios/ios_intermediate_dump_data.h create mode 100644 shared/sentry/external/crashpad/util/ios/ios_intermediate_dump_format.h create mode 100644 shared/sentry/external/crashpad/util/ios/ios_intermediate_dump_interface.cc create mode 100644 shared/sentry/external/crashpad/util/ios/ios_intermediate_dump_interface.h create mode 100644 shared/sentry/external/crashpad/util/ios/ios_intermediate_dump_list.cc create mode 100644 shared/sentry/external/crashpad/util/ios/ios_intermediate_dump_list.h create mode 100644 shared/sentry/external/crashpad/util/ios/ios_intermediate_dump_map.cc create mode 100644 shared/sentry/external/crashpad/util/ios/ios_intermediate_dump_map.h create mode 100644 shared/sentry/external/crashpad/util/ios/ios_intermediate_dump_object.cc create mode 100644 shared/sentry/external/crashpad/util/ios/ios_intermediate_dump_object.h create mode 100644 shared/sentry/external/crashpad/util/ios/ios_intermediate_dump_reader.cc create mode 100644 shared/sentry/external/crashpad/util/ios/ios_intermediate_dump_reader.h create mode 100644 shared/sentry/external/crashpad/util/ios/ios_intermediate_dump_reader_test.cc create mode 100644 shared/sentry/external/crashpad/util/ios/ios_intermediate_dump_writer.cc create mode 100644 shared/sentry/external/crashpad/util/ios/ios_intermediate_dump_writer.h create mode 100644 shared/sentry/external/crashpad/util/ios/ios_intermediate_dump_writer_test.cc create mode 100644 shared/sentry/external/crashpad/util/ios/ios_system_data_collector.h create mode 100644 shared/sentry/external/crashpad/util/ios/ios_system_data_collector.mm create mode 100644 shared/sentry/external/crashpad/util/ios/raw_logging.cc create mode 100644 shared/sentry/external/crashpad/util/ios/raw_logging.h create mode 100644 shared/sentry/external/crashpad/util/ios/scoped_background_task.h create mode 100644 shared/sentry/external/crashpad/util/ios/scoped_background_task.mm create mode 100644 shared/sentry/external/crashpad/util/ios/scoped_vm_read.cc create mode 100644 shared/sentry/external/crashpad/util/ios/scoped_vm_read.h create mode 100644 shared/sentry/external/crashpad/util/ios/scoped_vm_read_test.cc create mode 100644 shared/sentry/external/crashpad/util/linux/address_types.h create mode 100644 shared/sentry/external/crashpad/util/linux/auxiliary_vector.cc create mode 100644 shared/sentry/external/crashpad/util/linux/auxiliary_vector.h create mode 100644 shared/sentry/external/crashpad/util/linux/auxiliary_vector_test.cc create mode 100644 shared/sentry/external/crashpad/util/linux/checked_linux_address_range.h create mode 100644 shared/sentry/external/crashpad/util/linux/direct_ptrace_connection.cc create mode 100644 shared/sentry/external/crashpad/util/linux/direct_ptrace_connection.h create mode 100644 shared/sentry/external/crashpad/util/linux/exception_handler_client.cc create mode 100644 shared/sentry/external/crashpad/util/linux/exception_handler_client.h create mode 100644 shared/sentry/external/crashpad/util/linux/exception_handler_protocol.cc create mode 100644 shared/sentry/external/crashpad/util/linux/exception_handler_protocol.h create mode 100644 shared/sentry/external/crashpad/util/linux/exception_information.h create mode 100644 shared/sentry/external/crashpad/util/linux/initial_signal_dispositions.cc create mode 100644 shared/sentry/external/crashpad/util/linux/initial_signal_dispositions.h create mode 100644 shared/sentry/external/crashpad/util/linux/memory_map.cc create mode 100644 shared/sentry/external/crashpad/util/linux/memory_map.h create mode 100644 shared/sentry/external/crashpad/util/linux/memory_map_test.cc create mode 100644 shared/sentry/external/crashpad/util/linux/proc_stat_reader.cc create mode 100644 shared/sentry/external/crashpad/util/linux/proc_stat_reader.h create mode 100644 shared/sentry/external/crashpad/util/linux/proc_stat_reader_test.cc create mode 100644 shared/sentry/external/crashpad/util/linux/proc_task_reader.cc create mode 100644 shared/sentry/external/crashpad/util/linux/proc_task_reader.h create mode 100644 shared/sentry/external/crashpad/util/linux/proc_task_reader_test.cc create mode 100644 shared/sentry/external/crashpad/util/linux/ptrace_broker.cc create mode 100644 shared/sentry/external/crashpad/util/linux/ptrace_broker.h create mode 100644 shared/sentry/external/crashpad/util/linux/ptrace_broker_test.cc create mode 100644 shared/sentry/external/crashpad/util/linux/ptrace_client.cc create mode 100644 shared/sentry/external/crashpad/util/linux/ptrace_client.h create mode 100644 shared/sentry/external/crashpad/util/linux/ptrace_connection.h create mode 100644 shared/sentry/external/crashpad/util/linux/ptracer.cc create mode 100644 shared/sentry/external/crashpad/util/linux/ptracer.h create mode 100644 shared/sentry/external/crashpad/util/linux/ptracer_test.cc create mode 100644 shared/sentry/external/crashpad/util/linux/scoped_pr_set_dumpable.cc create mode 100644 shared/sentry/external/crashpad/util/linux/scoped_pr_set_dumpable.h create mode 100644 shared/sentry/external/crashpad/util/linux/scoped_pr_set_ptracer.cc create mode 100644 shared/sentry/external/crashpad/util/linux/scoped_pr_set_ptracer.h create mode 100644 shared/sentry/external/crashpad/util/linux/scoped_ptrace_attach.cc create mode 100644 shared/sentry/external/crashpad/util/linux/scoped_ptrace_attach.h create mode 100644 shared/sentry/external/crashpad/util/linux/scoped_ptrace_attach_test.cc create mode 100644 shared/sentry/external/crashpad/util/linux/socket.cc create mode 100644 shared/sentry/external/crashpad/util/linux/socket.h create mode 100644 shared/sentry/external/crashpad/util/linux/socket_test.cc create mode 100644 shared/sentry/external/crashpad/util/linux/thread_info.cc create mode 100644 shared/sentry/external/crashpad/util/linux/thread_info.h create mode 100644 shared/sentry/external/crashpad/util/linux/traits.h create mode 100644 shared/sentry/external/crashpad/util/mac/checked_mach_address_range.h create mode 100644 shared/sentry/external/crashpad/util/mac/checked_mach_address_range_test.cc create mode 100644 shared/sentry/external/crashpad/util/mac/launchd.h create mode 100644 shared/sentry/external/crashpad/util/mac/launchd.mm create mode 100644 shared/sentry/external/crashpad/util/mac/launchd_test.mm create mode 100644 shared/sentry/external/crashpad/util/mac/mac_util.cc create mode 100644 shared/sentry/external/crashpad/util/mac/mac_util.h create mode 100644 shared/sentry/external/crashpad/util/mac/mac_util_test.mm create mode 100644 shared/sentry/external/crashpad/util/mac/service_management.cc create mode 100644 shared/sentry/external/crashpad/util/mac/service_management.h create mode 100644 shared/sentry/external/crashpad/util/mac/service_management_test.mm create mode 100644 shared/sentry/external/crashpad/util/mac/sysctl.cc create mode 100644 shared/sentry/external/crashpad/util/mac/sysctl.h create mode 100644 shared/sentry/external/crashpad/util/mac/sysctl_test.cc create mode 100644 shared/sentry/external/crashpad/util/mac/xattr.cc create mode 100644 shared/sentry/external/crashpad/util/mac/xattr.h create mode 100644 shared/sentry/external/crashpad/util/mac/xattr_test.cc create mode 100644 shared/sentry/external/crashpad/util/mach/bootstrap.cc create mode 100644 shared/sentry/external/crashpad/util/mach/bootstrap.h create mode 100644 shared/sentry/external/crashpad/util/mach/bootstrap_test.cc create mode 100644 shared/sentry/external/crashpad/util/mach/child_port.defs create mode 100644 shared/sentry/external/crashpad/util/mach/child_port_handshake.cc create mode 100644 shared/sentry/external/crashpad/util/mach/child_port_handshake.h create mode 100644 shared/sentry/external/crashpad/util/mach/child_port_handshake_test.cc create mode 100644 shared/sentry/external/crashpad/util/mach/child_port_server.cc create mode 100644 shared/sentry/external/crashpad/util/mach/child_port_server.h create mode 100644 shared/sentry/external/crashpad/util/mach/child_port_server_test.cc create mode 100644 shared/sentry/external/crashpad/util/mach/child_port_types.h create mode 100644 shared/sentry/external/crashpad/util/mach/composite_mach_message_server.cc create mode 100644 shared/sentry/external/crashpad/util/mach/composite_mach_message_server.h create mode 100644 shared/sentry/external/crashpad/util/mach/composite_mach_message_server_test.cc create mode 100644 shared/sentry/external/crashpad/util/mach/exc_client_variants.cc create mode 100644 shared/sentry/external/crashpad/util/mach/exc_client_variants.h create mode 100644 shared/sentry/external/crashpad/util/mach/exc_client_variants_test.cc create mode 100644 shared/sentry/external/crashpad/util/mach/exc_server_variants.cc create mode 100644 shared/sentry/external/crashpad/util/mach/exc_server_variants.h create mode 100644 shared/sentry/external/crashpad/util/mach/exc_server_variants_test.cc create mode 100644 shared/sentry/external/crashpad/util/mach/exception_behaviors.cc create mode 100644 shared/sentry/external/crashpad/util/mach/exception_behaviors.h create mode 100644 shared/sentry/external/crashpad/util/mach/exception_behaviors_test.cc create mode 100644 shared/sentry/external/crashpad/util/mach/exception_ports.cc create mode 100644 shared/sentry/external/crashpad/util/mach/exception_ports.h create mode 100644 shared/sentry/external/crashpad/util/mach/exception_ports_test.cc create mode 100644 shared/sentry/external/crashpad/util/mach/exception_types.cc create mode 100644 shared/sentry/external/crashpad/util/mach/exception_types.h create mode 100644 shared/sentry/external/crashpad/util/mach/exception_types_test.cc create mode 100644 shared/sentry/external/crashpad/util/mach/mach_extensions.cc create mode 100644 shared/sentry/external/crashpad/util/mach/mach_extensions.h create mode 100644 shared/sentry/external/crashpad/util/mach/mach_extensions_test.cc create mode 100644 shared/sentry/external/crashpad/util/mach/mach_message.cc create mode 100644 shared/sentry/external/crashpad/util/mach/mach_message.h create mode 100644 shared/sentry/external/crashpad/util/mach/mach_message_server.cc create mode 100644 shared/sentry/external/crashpad/util/mach/mach_message_server.h create mode 100644 shared/sentry/external/crashpad/util/mach/mach_message_server_test.cc create mode 100644 shared/sentry/external/crashpad/util/mach/mach_message_test.cc create mode 100755 shared/sentry/external/crashpad/util/mach/mig.py create mode 100755 shared/sentry/external/crashpad/util/mach/mig_fix.py create mode 100755 shared/sentry/external/crashpad/util/mach/mig_gen.py create mode 100644 shared/sentry/external/crashpad/util/mach/notify_server.cc create mode 100644 shared/sentry/external/crashpad/util/mach/notify_server.h create mode 100644 shared/sentry/external/crashpad/util/mach/notify_server_test.cc create mode 100644 shared/sentry/external/crashpad/util/mach/scoped_task_suspend.cc create mode 100644 shared/sentry/external/crashpad/util/mach/scoped_task_suspend.h create mode 100644 shared/sentry/external/crashpad/util/mach/scoped_task_suspend_test.cc create mode 100644 shared/sentry/external/crashpad/util/mach/symbolic_constants_mach.cc create mode 100644 shared/sentry/external/crashpad/util/mach/symbolic_constants_mach.h create mode 100644 shared/sentry/external/crashpad/util/mach/symbolic_constants_mach_test.cc create mode 100644 shared/sentry/external/crashpad/util/mach/task_for_pid.cc create mode 100644 shared/sentry/external/crashpad/util/mach/task_for_pid.h create mode 100644 shared/sentry/external/crashpad/util/misc/address_sanitizer.h create mode 100644 shared/sentry/external/crashpad/util/misc/address_types.h create mode 100644 shared/sentry/external/crashpad/util/misc/arm64_pac_bti.S create mode 100644 shared/sentry/external/crashpad/util/misc/arraysize.h create mode 100644 shared/sentry/external/crashpad/util/misc/arraysize_test.cc create mode 100644 shared/sentry/external/crashpad/util/misc/as_underlying_type.h create mode 100644 shared/sentry/external/crashpad/util/misc/capture_context.h create mode 100644 shared/sentry/external/crashpad/util/misc/capture_context_broken.cc create mode 100644 shared/sentry/external/crashpad/util/misc/capture_context_fuchsia.S create mode 100644 shared/sentry/external/crashpad/util/misc/capture_context_linux.S create mode 100644 shared/sentry/external/crashpad/util/misc/capture_context_mac.S create mode 100644 shared/sentry/external/crashpad/util/misc/capture_context_test.cc create mode 100644 shared/sentry/external/crashpad/util/misc/capture_context_test_util.h create mode 100644 shared/sentry/external/crashpad/util/misc/capture_context_test_util_fuchsia.cc create mode 100644 shared/sentry/external/crashpad/util/misc/capture_context_test_util_linux.cc create mode 100644 shared/sentry/external/crashpad/util/misc/capture_context_test_util_mac.cc create mode 100644 shared/sentry/external/crashpad/util/misc/capture_context_test_util_win.cc create mode 100644 shared/sentry/external/crashpad/util/misc/capture_context_win.asm create mode 100644 shared/sentry/external/crashpad/util/misc/capture_context_win_arm64.asm create mode 100644 shared/sentry/external/crashpad/util/misc/capture_context_win_arm64.obj create mode 100644 shared/sentry/external/crashpad/util/misc/clock.h create mode 100644 shared/sentry/external/crashpad/util/misc/clock_mac.cc create mode 100644 shared/sentry/external/crashpad/util/misc/clock_posix.cc create mode 100644 shared/sentry/external/crashpad/util/misc/clock_test.cc create mode 100644 shared/sentry/external/crashpad/util/misc/clock_win.cc create mode 100644 shared/sentry/external/crashpad/util/misc/elf_note_types.h create mode 100644 shared/sentry/external/crashpad/util/misc/from_pointer_cast.h create mode 100644 shared/sentry/external/crashpad/util/misc/from_pointer_cast_test.cc create mode 100644 shared/sentry/external/crashpad/util/misc/implicit_cast.h create mode 100644 shared/sentry/external/crashpad/util/misc/initialization_state.h create mode 100644 shared/sentry/external/crashpad/util/misc/initialization_state_dcheck.cc create mode 100644 shared/sentry/external/crashpad/util/misc/initialization_state_dcheck.h create mode 100644 shared/sentry/external/crashpad/util/misc/initialization_state_dcheck_test.cc create mode 100644 shared/sentry/external/crashpad/util/misc/initialization_state_test.cc create mode 100644 shared/sentry/external/crashpad/util/misc/lexing.cc create mode 100644 shared/sentry/external/crashpad/util/misc/lexing.h create mode 100644 shared/sentry/external/crashpad/util/misc/memory_sanitizer.h create mode 100644 shared/sentry/external/crashpad/util/misc/metrics.cc create mode 100644 shared/sentry/external/crashpad/util/misc/metrics.h create mode 100644 shared/sentry/external/crashpad/util/misc/no_cfi_icall.h create mode 100644 shared/sentry/external/crashpad/util/misc/no_cfi_icall_test.cc create mode 100644 shared/sentry/external/crashpad/util/misc/paths.h create mode 100644 shared/sentry/external/crashpad/util/misc/paths_fuchsia.cc create mode 100644 shared/sentry/external/crashpad/util/misc/paths_linux.cc create mode 100644 shared/sentry/external/crashpad/util/misc/paths_mac.cc create mode 100644 shared/sentry/external/crashpad/util/misc/paths_test.cc create mode 100644 shared/sentry/external/crashpad/util/misc/paths_win.cc create mode 100644 shared/sentry/external/crashpad/util/misc/pdb_structures.cc create mode 100644 shared/sentry/external/crashpad/util/misc/pdb_structures.h create mode 100644 shared/sentry/external/crashpad/util/misc/random_string.cc create mode 100644 shared/sentry/external/crashpad/util/misc/random_string.h create mode 100644 shared/sentry/external/crashpad/util/misc/random_string_test.cc create mode 100644 shared/sentry/external/crashpad/util/misc/range_set.cc create mode 100644 shared/sentry/external/crashpad/util/misc/range_set.h create mode 100644 shared/sentry/external/crashpad/util/misc/range_set_test.cc create mode 100644 shared/sentry/external/crashpad/util/misc/reinterpret_bytes.cc create mode 100644 shared/sentry/external/crashpad/util/misc/reinterpret_bytes.h create mode 100644 shared/sentry/external/crashpad/util/misc/reinterpret_bytes_test.cc create mode 100644 shared/sentry/external/crashpad/util/misc/scoped_forbid_return.cc create mode 100644 shared/sentry/external/crashpad/util/misc/scoped_forbid_return.h create mode 100644 shared/sentry/external/crashpad/util/misc/scoped_forbid_return_test.cc create mode 100644 shared/sentry/external/crashpad/util/misc/symbolic_constants_common.h create mode 100644 shared/sentry/external/crashpad/util/misc/time.cc create mode 100644 shared/sentry/external/crashpad/util/misc/time.h create mode 100644 shared/sentry/external/crashpad/util/misc/time_linux.cc create mode 100644 shared/sentry/external/crashpad/util/misc/time_test.cc create mode 100644 shared/sentry/external/crashpad/util/misc/time_win.cc create mode 100644 shared/sentry/external/crashpad/util/misc/tri_state.h create mode 100644 shared/sentry/external/crashpad/util/misc/uuid.cc create mode 100644 shared/sentry/external/crashpad/util/misc/uuid.h create mode 100644 shared/sentry/external/crashpad/util/misc/uuid_test.cc create mode 100644 shared/sentry/external/crashpad/util/misc/zlib.cc create mode 100644 shared/sentry/external/crashpad/util/misc/zlib.h create mode 100755 shared/sentry/external/crashpad/util/net/generate_test_server_key.py create mode 100644 shared/sentry/external/crashpad/util/net/http_body.cc create mode 100644 shared/sentry/external/crashpad/util/net/http_body.h create mode 100644 shared/sentry/external/crashpad/util/net/http_body_gzip.cc create mode 100644 shared/sentry/external/crashpad/util/net/http_body_gzip.h create mode 100644 shared/sentry/external/crashpad/util/net/http_body_gzip_test.cc create mode 100644 shared/sentry/external/crashpad/util/net/http_body_test.cc create mode 100644 shared/sentry/external/crashpad/util/net/http_body_test_util.cc create mode 100644 shared/sentry/external/crashpad/util/net/http_body_test_util.h create mode 100644 shared/sentry/external/crashpad/util/net/http_headers.h create mode 100644 shared/sentry/external/crashpad/util/net/http_multipart_builder.cc create mode 100644 shared/sentry/external/crashpad/util/net/http_multipart_builder.h create mode 100644 shared/sentry/external/crashpad/util/net/http_multipart_builder_test.cc create mode 100644 shared/sentry/external/crashpad/util/net/http_transport.cc create mode 100644 shared/sentry/external/crashpad/util/net/http_transport.h create mode 100644 shared/sentry/external/crashpad/util/net/http_transport_libcurl.cc create mode 100644 shared/sentry/external/crashpad/util/net/http_transport_mac.mm create mode 100644 shared/sentry/external/crashpad/util/net/http_transport_socket.cc create mode 100644 shared/sentry/external/crashpad/util/net/http_transport_test.cc create mode 100644 shared/sentry/external/crashpad/util/net/http_transport_test_server.cc create mode 100644 shared/sentry/external/crashpad/util/net/http_transport_win.cc create mode 100644 shared/sentry/external/crashpad/util/net/testdata/ascii_http_body.txt create mode 100644 shared/sentry/external/crashpad/util/net/testdata/binary_http_body.dat create mode 100644 shared/sentry/external/crashpad/util/net/testdata/crashpad_util_test_cert.pem create mode 100644 shared/sentry/external/crashpad/util/net/testdata/crashpad_util_test_key.pem create mode 100644 shared/sentry/external/crashpad/util/net/tls.gni create mode 100644 shared/sentry/external/crashpad/util/net/url.cc create mode 100644 shared/sentry/external/crashpad/util/net/url.h create mode 100644 shared/sentry/external/crashpad/util/net/url_test.cc create mode 100644 shared/sentry/external/crashpad/util/numeric/checked_address_range.cc create mode 100644 shared/sentry/external/crashpad/util/numeric/checked_address_range.h create mode 100644 shared/sentry/external/crashpad/util/numeric/checked_address_range_test.cc create mode 100644 shared/sentry/external/crashpad/util/numeric/checked_range.h create mode 100644 shared/sentry/external/crashpad/util/numeric/checked_range_test.cc create mode 100644 shared/sentry/external/crashpad/util/numeric/checked_vm_address_range.h create mode 100644 shared/sentry/external/crashpad/util/numeric/in_range_cast.h create mode 100644 shared/sentry/external/crashpad/util/numeric/in_range_cast_test.cc create mode 100644 shared/sentry/external/crashpad/util/numeric/int128.h create mode 100644 shared/sentry/external/crashpad/util/numeric/int128_test.cc create mode 100644 shared/sentry/external/crashpad/util/numeric/safe_assignment.h create mode 100644 shared/sentry/external/crashpad/util/posix/close_multiple.cc create mode 100644 shared/sentry/external/crashpad/util/posix/close_multiple.h create mode 100644 shared/sentry/external/crashpad/util/posix/close_stdio.cc create mode 100644 shared/sentry/external/crashpad/util/posix/close_stdio.h create mode 100644 shared/sentry/external/crashpad/util/posix/double_fork_and_exec.cc create mode 100644 shared/sentry/external/crashpad/util/posix/double_fork_and_exec.h create mode 100644 shared/sentry/external/crashpad/util/posix/drop_privileges.cc create mode 100644 shared/sentry/external/crashpad/util/posix/drop_privileges.h create mode 100644 shared/sentry/external/crashpad/util/posix/process_info.h create mode 100644 shared/sentry/external/crashpad/util/posix/process_info_linux.cc create mode 100644 shared/sentry/external/crashpad/util/posix/process_info_mac.cc create mode 100644 shared/sentry/external/crashpad/util/posix/process_info_test.cc create mode 100644 shared/sentry/external/crashpad/util/posix/scoped_dir.cc create mode 100644 shared/sentry/external/crashpad/util/posix/scoped_dir.h create mode 100644 shared/sentry/external/crashpad/util/posix/scoped_mmap.cc create mode 100644 shared/sentry/external/crashpad/util/posix/scoped_mmap.h create mode 100644 shared/sentry/external/crashpad/util/posix/scoped_mmap_test.cc create mode 100644 shared/sentry/external/crashpad/util/posix/signals.cc create mode 100644 shared/sentry/external/crashpad/util/posix/signals.h create mode 100644 shared/sentry/external/crashpad/util/posix/signals_test.cc create mode 100644 shared/sentry/external/crashpad/util/posix/symbolic_constants_posix.cc create mode 100644 shared/sentry/external/crashpad/util/posix/symbolic_constants_posix.h create mode 100644 shared/sentry/external/crashpad/util/posix/symbolic_constants_posix_test.cc create mode 100644 shared/sentry/external/crashpad/util/process/process_id.h create mode 100644 shared/sentry/external/crashpad/util/process/process_memory.cc create mode 100644 shared/sentry/external/crashpad/util/process/process_memory.h create mode 100644 shared/sentry/external/crashpad/util/process/process_memory_fuchsia.cc create mode 100644 shared/sentry/external/crashpad/util/process/process_memory_fuchsia.h create mode 100644 shared/sentry/external/crashpad/util/process/process_memory_linux.cc create mode 100644 shared/sentry/external/crashpad/util/process/process_memory_linux.h create mode 100644 shared/sentry/external/crashpad/util/process/process_memory_mac.cc create mode 100644 shared/sentry/external/crashpad/util/process/process_memory_mac.h create mode 100644 shared/sentry/external/crashpad/util/process/process_memory_mac_test.cc create mode 100644 shared/sentry/external/crashpad/util/process/process_memory_native.h create mode 100644 shared/sentry/external/crashpad/util/process/process_memory_range.cc create mode 100644 shared/sentry/external/crashpad/util/process/process_memory_range.h create mode 100644 shared/sentry/external/crashpad/util/process/process_memory_range_test.cc create mode 100644 shared/sentry/external/crashpad/util/process/process_memory_sanitized.cc create mode 100644 shared/sentry/external/crashpad/util/process/process_memory_sanitized.h create mode 100644 shared/sentry/external/crashpad/util/process/process_memory_sanitized_test.cc create mode 100644 shared/sentry/external/crashpad/util/process/process_memory_test.cc create mode 100644 shared/sentry/external/crashpad/util/process/process_memory_win.cc create mode 100644 shared/sentry/external/crashpad/util/process/process_memory_win.h create mode 100644 shared/sentry/external/crashpad/util/stdlib/aligned_allocator.cc create mode 100644 shared/sentry/external/crashpad/util/stdlib/aligned_allocator.h create mode 100644 shared/sentry/external/crashpad/util/stdlib/aligned_allocator_test.cc create mode 100644 shared/sentry/external/crashpad/util/stdlib/map_insert.h create mode 100644 shared/sentry/external/crashpad/util/stdlib/map_insert_test.cc create mode 100644 shared/sentry/external/crashpad/util/stdlib/objc.h create mode 100644 shared/sentry/external/crashpad/util/stdlib/string_number_conversion.cc create mode 100644 shared/sentry/external/crashpad/util/stdlib/string_number_conversion.h create mode 100644 shared/sentry/external/crashpad/util/stdlib/string_number_conversion_test.cc create mode 100644 shared/sentry/external/crashpad/util/stdlib/strlcpy.cc create mode 100644 shared/sentry/external/crashpad/util/stdlib/strlcpy.h create mode 100644 shared/sentry/external/crashpad/util/stdlib/strlcpy_test.cc create mode 100644 shared/sentry/external/crashpad/util/stdlib/strnlen.cc create mode 100644 shared/sentry/external/crashpad/util/stdlib/strnlen.h create mode 100644 shared/sentry/external/crashpad/util/stdlib/strnlen_test.cc create mode 100644 shared/sentry/external/crashpad/util/stdlib/thread_safe_vector.h create mode 100644 shared/sentry/external/crashpad/util/stdlib/thread_safe_vector_test.cc create mode 100644 shared/sentry/external/crashpad/util/stream/base94_output_stream.cc create mode 100644 shared/sentry/external/crashpad/util/stream/base94_output_stream.h create mode 100644 shared/sentry/external/crashpad/util/stream/base94_output_stream_test.cc create mode 100644 shared/sentry/external/crashpad/util/stream/file_encoder.cc create mode 100644 shared/sentry/external/crashpad/util/stream/file_encoder.h create mode 100644 shared/sentry/external/crashpad/util/stream/file_encoder_test.cc create mode 100644 shared/sentry/external/crashpad/util/stream/file_output_stream.cc create mode 100644 shared/sentry/external/crashpad/util/stream/file_output_stream.h create mode 100644 shared/sentry/external/crashpad/util/stream/log_output_stream.cc create mode 100644 shared/sentry/external/crashpad/util/stream/log_output_stream.h create mode 100644 shared/sentry/external/crashpad/util/stream/log_output_stream_test.cc create mode 100644 shared/sentry/external/crashpad/util/stream/output_stream_interface.h create mode 100644 shared/sentry/external/crashpad/util/stream/test_output_stream.cc create mode 100644 shared/sentry/external/crashpad/util/stream/test_output_stream.h create mode 100644 shared/sentry/external/crashpad/util/stream/zlib_output_stream.cc create mode 100644 shared/sentry/external/crashpad/util/stream/zlib_output_stream.h create mode 100644 shared/sentry/external/crashpad/util/stream/zlib_output_stream_test.cc create mode 100644 shared/sentry/external/crashpad/util/string/split_string.cc create mode 100644 shared/sentry/external/crashpad/util/string/split_string.h create mode 100644 shared/sentry/external/crashpad/util/string/split_string_test.cc create mode 100644 shared/sentry/external/crashpad/util/synchronization/semaphore.h create mode 100644 shared/sentry/external/crashpad/util/synchronization/semaphore_mac.cc create mode 100644 shared/sentry/external/crashpad/util/synchronization/semaphore_posix.cc create mode 100644 shared/sentry/external/crashpad/util/synchronization/semaphore_test.cc create mode 100644 shared/sentry/external/crashpad/util/synchronization/semaphore_win.cc create mode 100644 shared/sentry/external/crashpad/util/thread/stoppable.h create mode 100644 shared/sentry/external/crashpad/util/thread/thread.cc create mode 100644 shared/sentry/external/crashpad/util/thread/thread.h create mode 100644 shared/sentry/external/crashpad/util/thread/thread_log_messages.cc create mode 100644 shared/sentry/external/crashpad/util/thread/thread_log_messages.h create mode 100644 shared/sentry/external/crashpad/util/thread/thread_log_messages_test.cc create mode 100644 shared/sentry/external/crashpad/util/thread/thread_posix.cc create mode 100644 shared/sentry/external/crashpad/util/thread/thread_test.cc create mode 100644 shared/sentry/external/crashpad/util/thread/thread_win.cc create mode 100644 shared/sentry/external/crashpad/util/thread/worker_thread.cc create mode 100644 shared/sentry/external/crashpad/util/thread/worker_thread.h create mode 100644 shared/sentry/external/crashpad/util/thread/worker_thread_test.cc create mode 100644 shared/sentry/external/crashpad/util/win/address_types.h create mode 100644 shared/sentry/external/crashpad/util/win/checked_win_address_range.h create mode 100644 shared/sentry/external/crashpad/util/win/command_line.cc create mode 100644 shared/sentry/external/crashpad/util/win/command_line.h create mode 100644 shared/sentry/external/crashpad/util/win/command_line_test.cc create mode 100644 shared/sentry/external/crashpad/util/win/context_wrappers.h create mode 100644 shared/sentry/external/crashpad/util/win/critical_section_with_debug_info.cc create mode 100644 shared/sentry/external/crashpad/util/win/critical_section_with_debug_info.h create mode 100644 shared/sentry/external/crashpad/util/win/critical_section_with_debug_info_test.cc create mode 100644 shared/sentry/external/crashpad/util/win/exception_codes.h create mode 100644 shared/sentry/external/crashpad/util/win/exception_handler_server.cc create mode 100644 shared/sentry/external/crashpad/util/win/exception_handler_server.h create mode 100644 shared/sentry/external/crashpad/util/win/exception_handler_server_test.cc create mode 100644 shared/sentry/external/crashpad/util/win/get_function.cc create mode 100644 shared/sentry/external/crashpad/util/win/get_function.h create mode 100644 shared/sentry/external/crashpad/util/win/get_function_test.cc create mode 100644 shared/sentry/external/crashpad/util/win/get_module_information.cc create mode 100644 shared/sentry/external/crashpad/util/win/get_module_information.h create mode 100644 shared/sentry/external/crashpad/util/win/handle.cc create mode 100644 shared/sentry/external/crashpad/util/win/handle.h create mode 100644 shared/sentry/external/crashpad/util/win/handle_test.cc create mode 100644 shared/sentry/external/crashpad/util/win/initial_client_data.cc create mode 100644 shared/sentry/external/crashpad/util/win/initial_client_data.h create mode 100644 shared/sentry/external/crashpad/util/win/initial_client_data_test.cc create mode 100644 shared/sentry/external/crashpad/util/win/loader_lock.cc create mode 100644 shared/sentry/external/crashpad/util/win/loader_lock.h create mode 100644 shared/sentry/external/crashpad/util/win/loader_lock_test.cc create mode 100644 shared/sentry/external/crashpad/util/win/loader_lock_test_dll.cc create mode 100644 shared/sentry/external/crashpad/util/win/module_version.cc create mode 100644 shared/sentry/external/crashpad/util/win/module_version.h create mode 100644 shared/sentry/external/crashpad/util/win/nt_internals.cc create mode 100644 shared/sentry/external/crashpad/util/win/nt_internals.h create mode 100644 shared/sentry/external/crashpad/util/win/ntstatus_logging.cc create mode 100644 shared/sentry/external/crashpad/util/win/ntstatus_logging.h create mode 100644 shared/sentry/external/crashpad/util/win/process_info.cc create mode 100644 shared/sentry/external/crashpad/util/win/process_info.h create mode 100644 shared/sentry/external/crashpad/util/win/process_info_test.cc create mode 100644 shared/sentry/external/crashpad/util/win/process_info_test_child.cc create mode 100644 shared/sentry/external/crashpad/util/win/process_structs.h create mode 100644 shared/sentry/external/crashpad/util/win/registration_protocol_win.cc create mode 100644 shared/sentry/external/crashpad/util/win/registration_protocol_win.h create mode 100644 shared/sentry/external/crashpad/util/win/registration_protocol_win_test.cc create mode 100644 shared/sentry/external/crashpad/util/win/safe_terminate_process.asm create mode 100644 shared/sentry/external/crashpad/util/win/safe_terminate_process.h create mode 100644 shared/sentry/external/crashpad/util/win/safe_terminate_process_broken.cc create mode 100644 shared/sentry/external/crashpad/util/win/safe_terminate_process_test.cc create mode 100644 shared/sentry/external/crashpad/util/win/safe_terminate_process_test_child.cc create mode 100644 shared/sentry/external/crashpad/util/win/scoped_handle.cc create mode 100644 shared/sentry/external/crashpad/util/win/scoped_handle.h create mode 100644 shared/sentry/external/crashpad/util/win/scoped_local_alloc.cc create mode 100644 shared/sentry/external/crashpad/util/win/scoped_local_alloc.h create mode 100644 shared/sentry/external/crashpad/util/win/scoped_process_suspend.cc create mode 100644 shared/sentry/external/crashpad/util/win/scoped_process_suspend.h create mode 100644 shared/sentry/external/crashpad/util/win/scoped_process_suspend_test.cc create mode 100644 shared/sentry/external/crashpad/util/win/scoped_registry_key.h create mode 100644 shared/sentry/external/crashpad/util/win/scoped_set_event.cc create mode 100644 shared/sentry/external/crashpad/util/win/scoped_set_event.h create mode 100644 shared/sentry/external/crashpad/util/win/session_end_watcher.cc create mode 100644 shared/sentry/external/crashpad/util/win/session_end_watcher.h create mode 100644 shared/sentry/external/crashpad/util/win/session_end_watcher_test.cc create mode 100644 shared/sentry/external/crashpad/util/win/termination_codes.h create mode 100644 shared/sentry/external/crashpad/util/win/traits.h create mode 100644 shared/sentry/external/crashpad/util/win/xp_compat.h create mode 100644 shared/sentry/external/libunwindstack-ndk/.github/workflows/build.yml create mode 100644 shared/sentry/external/libunwindstack-ndk/.gitignore create mode 100644 shared/sentry/external/libunwindstack-ndk/ArmExidx.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/ArmExidx.h create mode 100644 shared/sentry/external/libunwindstack-ndk/AsmGetRegsX86.S create mode 100644 shared/sentry/external/libunwindstack-ndk/AsmGetRegsX86_64.S create mode 100644 shared/sentry/external/libunwindstack-ndk/Check.h create mode 100644 shared/sentry/external/libunwindstack-ndk/DexFile.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/DexFile.h create mode 100644 shared/sentry/external/libunwindstack-ndk/DexFiles.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/DwarfCfa.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/DwarfCfa.h create mode 100644 shared/sentry/external/libunwindstack-ndk/DwarfDebugFrame.h create mode 100644 shared/sentry/external/libunwindstack-ndk/DwarfEhFrame.h create mode 100644 shared/sentry/external/libunwindstack-ndk/DwarfEhFrameWithHdr.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/DwarfEhFrameWithHdr.h create mode 100644 shared/sentry/external/libunwindstack-ndk/DwarfEncoding.h create mode 100644 shared/sentry/external/libunwindstack-ndk/DwarfMemory.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/DwarfOp.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/DwarfOp.h create mode 100644 shared/sentry/external/libunwindstack-ndk/DwarfSection.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/Elf.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/ElfInterface.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/ElfInterfaceArm.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/ElfInterfaceArm.h create mode 100644 shared/sentry/external/libunwindstack-ndk/Global.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/JitDebug.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/LICENSE create mode 100644 shared/sentry/external/libunwindstack-ndk/LocalUnwinder.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/Log.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/MapInfo.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/Maps.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/Memory.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/MemoryBuffer.h create mode 100644 shared/sentry/external/libunwindstack-ndk/MemoryCache.h create mode 100644 shared/sentry/external/libunwindstack-ndk/MemoryFileAtOffset.h create mode 100644 shared/sentry/external/libunwindstack-ndk/MemoryLocal.h create mode 100644 shared/sentry/external/libunwindstack-ndk/MemoryMte.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/MemoryOffline.h create mode 100644 shared/sentry/external/libunwindstack-ndk/MemoryOfflineBuffer.h create mode 100644 shared/sentry/external/libunwindstack-ndk/MemoryRange.h create mode 100644 shared/sentry/external/libunwindstack-ndk/MemoryRemote.h create mode 100644 shared/sentry/external/libunwindstack-ndk/OWNERS create mode 100644 shared/sentry/external/libunwindstack-ndk/README.md create mode 100644 shared/sentry/external/libunwindstack-ndk/Regs.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/RegsArm.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/RegsArm64.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/RegsInfo.h create mode 100644 shared/sentry/external/libunwindstack-ndk/RegsX86.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/RegsX86_64.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/Symbols.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/Symbols.h create mode 100644 shared/sentry/external/libunwindstack-ndk/ThreadEntry.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/ThreadEntry.h create mode 100644 shared/sentry/external/libunwindstack-ndk/ThreadUnwinder.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/Unwinder.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/android-base/README.md create mode 100644 shared/sentry/external/libunwindstack-ndk/android-base/errno_restorer.h create mode 100644 shared/sentry/external/libunwindstack-ndk/android-base/file.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/android-base/file.h create mode 100644 shared/sentry/external/libunwindstack-ndk/android-base/log_main.h create mode 100644 shared/sentry/external/libunwindstack-ndk/android-base/logging.h create mode 100644 shared/sentry/external/libunwindstack-ndk/android-base/macros.h create mode 100644 shared/sentry/external/libunwindstack-ndk/android-base/off64_t.h create mode 100644 shared/sentry/external/libunwindstack-ndk/android-base/parseint.h create mode 100644 shared/sentry/external/libunwindstack-ndk/android-base/stringprintf.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/android-base/stringprintf.h create mode 100644 shared/sentry/external/libunwindstack-ndk/android-base/strings.cpp create mode 100644 shared/sentry/external/libunwindstack-ndk/android-base/strings.h create mode 100644 shared/sentry/external/libunwindstack-ndk/android-base/threads.h create mode 100644 shared/sentry/external/libunwindstack-ndk/android-base/unique_fd.h create mode 100644 shared/sentry/external/libunwindstack-ndk/android-base/utf8.h create mode 100644 shared/sentry/external/libunwindstack-ndk/cmake/CMakeLists.txt create mode 100644 shared/sentry/external/libunwindstack-ndk/compat/string.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/Arch.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/DexFiles.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/DwarfError.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/DwarfLocation.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/DwarfMemory.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/DwarfSection.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/DwarfStructs.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/Elf.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/ElfInterface.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/Error.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/Global.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/JitDebug.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/LocalUnwinder.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/Log.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/MachineArm.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/MachineArm64.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/MachineX86.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/MachineX86_64.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/MapInfo.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/Maps.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/Memory.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/Regs.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/RegsArm.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/RegsArm64.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/RegsGetLocal.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/RegsX86.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/RegsX86_64.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/UcontextArm.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/UcontextArm64.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/UcontextX86.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/UcontextX86_64.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/Unwinder.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/UserArm.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/UserArm64.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/UserX86.h create mode 100644 shared/sentry/external/libunwindstack-ndk/include/unwindstack/UserX86_64.h create mode 100644 shared/sentry/external/libunwindstack-ndk/procinfo/README.md create mode 100644 shared/sentry/external/libunwindstack-ndk/procinfo/process.h create mode 100644 shared/sentry/external/libunwindstack-ndk/procinfo/process_map.h create mode 100644 shared/sentry/external/libunwindstack-ndk/unistdfix.h create mode 100644 shared/sentry/external/third_party/lss/.gitignore create mode 100644 shared/sentry/external/third_party/lss/README.md create mode 100644 shared/sentry/external/third_party/lss/codereview.settings create mode 100644 shared/sentry/external/third_party/lss/linux_syscall_support.h create mode 100644 shared/sentry/external/third_party/lss/tests/.gitignore create mode 100644 shared/sentry/external/third_party/lss/tests/Makefile create mode 100644 shared/sentry/external/third_party/lss/tests/README.md create mode 100644 shared/sentry/external/third_party/lss/tests/fallocate.c create mode 100644 shared/sentry/external/third_party/lss/tests/sigtimedwait.c create mode 100644 shared/sentry/external/third_party/lss/tests/test_skel.h create mode 100644 shared/sentry/external/third_party/lss/tests/unlink.c create mode 100644 shared/sentry/fuzzing-examples/macos-event.json create mode 100644 shared/sentry/fuzzing-examples/macos-session.json create mode 100644 shared/sentry/include/sentry.h create mode 100644 shared/sentry/sentry-config.cmake.in create mode 100644 shared/sentry/src/CMakeLists.txt create mode 100644 shared/sentry/src/backends/sentry_backend_breakpad.cpp create mode 100644 shared/sentry/src/backends/sentry_backend_crashpad.cpp create mode 100644 shared/sentry/src/backends/sentry_backend_inproc.c create mode 100644 shared/sentry/src/backends/sentry_backend_none.c create mode 100644 shared/sentry/src/integrations/sentry_integration_qt.cpp create mode 100644 shared/sentry/src/integrations/sentry_integration_qt.h create mode 100644 shared/sentry/src/modulefinder/sentry_modulefinder_aix.c create mode 100644 shared/sentry/src/modulefinder/sentry_modulefinder_apple.c create mode 100644 shared/sentry/src/modulefinder/sentry_modulefinder_linux.c create mode 100644 shared/sentry/src/modulefinder/sentry_modulefinder_linux.h create mode 100644 shared/sentry/src/modulefinder/sentry_modulefinder_windows.c create mode 100644 shared/sentry/src/path/sentry_path.c create mode 100644 shared/sentry/src/path/sentry_path_unix.c create mode 100644 shared/sentry/src/path/sentry_path_windows.c create mode 100644 shared/sentry/src/sentry_alloc.c create mode 100644 shared/sentry/src/sentry_alloc.h create mode 100644 shared/sentry/src/sentry_backend.c create mode 100644 shared/sentry/src/sentry_backend.h create mode 100644 shared/sentry/src/sentry_boot.h create mode 100644 shared/sentry/src/sentry_core.c create mode 100644 shared/sentry/src/sentry_core.h create mode 100644 shared/sentry/src/sentry_database.c create mode 100644 shared/sentry/src/sentry_database.h create mode 100644 shared/sentry/src/sentry_envelope.c create mode 100644 shared/sentry/src/sentry_envelope.h create mode 100644 shared/sentry/src/sentry_json.c create mode 100644 shared/sentry/src/sentry_json.h create mode 100644 shared/sentry/src/sentry_logger.c create mode 100644 shared/sentry/src/sentry_logger.h create mode 100644 shared/sentry/src/sentry_options.c create mode 100644 shared/sentry/src/sentry_options.h create mode 100644 shared/sentry/src/sentry_os.c create mode 100644 shared/sentry/src/sentry_os.h create mode 100644 shared/sentry/src/sentry_path.h create mode 100644 shared/sentry/src/sentry_random.c create mode 100644 shared/sentry/src/sentry_random.h create mode 100644 shared/sentry/src/sentry_ratelimiter.c create mode 100644 shared/sentry/src/sentry_ratelimiter.h create mode 100644 shared/sentry/src/sentry_scope.c create mode 100644 shared/sentry/src/sentry_scope.h create mode 100644 shared/sentry/src/sentry_session.c create mode 100644 shared/sentry/src/sentry_session.h create mode 100644 shared/sentry/src/sentry_slice.c create mode 100644 shared/sentry/src/sentry_slice.h create mode 100644 shared/sentry/src/sentry_string.c create mode 100644 shared/sentry/src/sentry_string.h create mode 100644 shared/sentry/src/sentry_symbolizer.h create mode 100644 shared/sentry/src/sentry_sync.c create mode 100644 shared/sentry/src/sentry_sync.h create mode 100644 shared/sentry/src/sentry_tracing.c create mode 100644 shared/sentry/src/sentry_tracing.h create mode 100644 shared/sentry/src/sentry_transport.c create mode 100644 shared/sentry/src/sentry_transport.h create mode 100644 shared/sentry/src/sentry_unix_pageallocator.c create mode 100644 shared/sentry/src/sentry_unix_pageallocator.h create mode 100644 shared/sentry/src/sentry_unix_spinlock.h create mode 100644 shared/sentry/src/sentry_utils.c create mode 100644 shared/sentry/src/sentry_utils.h create mode 100644 shared/sentry/src/sentry_uuid.c create mode 100644 shared/sentry/src/sentry_uuid.h create mode 100644 shared/sentry/src/sentry_value.c create mode 100644 shared/sentry/src/sentry_value.h create mode 100644 shared/sentry/src/sentry_windows_dbghelp.c create mode 100644 shared/sentry/src/sentry_windows_dbghelp.h create mode 100644 shared/sentry/src/symbolizer/sentry_symbolizer_unix.c create mode 100644 shared/sentry/src/symbolizer/sentry_symbolizer_windows.c create mode 100644 shared/sentry/src/transports/sentry_disk_transport.c create mode 100644 shared/sentry/src/transports/sentry_disk_transport.h create mode 100644 shared/sentry/src/transports/sentry_function_transport.c create mode 100644 shared/sentry/src/transports/sentry_transport_curl.c create mode 100644 shared/sentry/src/transports/sentry_transport_none.c create mode 100644 shared/sentry/src/transports/sentry_transport_winhttp.c create mode 100644 shared/sentry/src/unwinder/sentry_unwinder.c create mode 100644 shared/sentry/src/unwinder/sentry_unwinder_dbghelp.c create mode 100644 shared/sentry/src/unwinder/sentry_unwinder_libbacktrace.c create mode 100644 shared/sentry/src/unwinder/sentry_unwinder_libunwindstack.cpp create mode 100644 shared/sentry/tests/__init__.py create mode 100644 shared/sentry/tests/assertions.py create mode 100644 shared/sentry/tests/cmake.py create mode 100644 shared/sentry/tests/conditions.py create mode 100644 shared/sentry/tests/conftest.py create mode 100644 shared/sentry/tests/fixtures/README.md create mode 100644 shared/sentry/tests/fixtures/libstdc++.so create mode 100644 shared/sentry/tests/fixtures/sentry_example create mode 100644 shared/sentry/tests/fixtures/test.c create mode 100755 shared/sentry/tests/fixtures/with-buildid.so create mode 100755 shared/sentry/tests/fixtures/without-buildid.so create mode 100644 shared/sentry/tests/fuzzing-failures/id-000000,sig-06,src-000560,op-havoc,rep-2 create mode 100644 shared/sentry/tests/fuzzing-failures/id-000000,sig-11,src-000033+000005,op-splice,rep-16 create mode 100644 shared/sentry/tests/fuzzing-failures/id-000000,sig-11,src-000320,op-havoc,rep-2 create mode 100644 shared/sentry/tests/fuzzing-failures/id-000000,sig-11,src-000494+000073,op-splice,rep-8 create mode 100644 shared/sentry/tests/fuzzing-failures/id-000001,sig-11,src-000052+000222,op-splice,rep-32 create mode 100644 shared/sentry/tests/fuzzing-failures/id-000001,sig-11,src-000215+000415,op-splice,rep-16 create mode 100644 shared/sentry/tests/fuzzing-failures/id-000002,sig-11,src-000339+000110,op-splice,rep-16 create mode 100644 shared/sentry/tests/fuzzing-failures/id-000002,sig-11,src-000447+000419,op-splice,rep-32 create mode 100644 shared/sentry/tests/fuzzing-failures/id-000003,sig-11,src-000399+000468,op-splice,rep-32 create mode 100644 shared/sentry/tests/fuzzing-failures/id-000004,sig-11,src-000440+000369,op-splice,rep-2 create mode 100644 shared/sentry/tests/leaks.txt create mode 100644 shared/sentry/tests/requirements.txt create mode 100644 shared/sentry/tests/test_build_static.py create mode 100644 shared/sentry/tests/test_integration_crashpad.py create mode 100644 shared/sentry/tests/test_integration_http.py create mode 100644 shared/sentry/tests/test_integration_ratelimits.py create mode 100644 shared/sentry/tests/test_integration_stdout.py create mode 100644 shared/sentry/tests/test_unit.py create mode 100644 shared/sentry/tests/unit/.gitattributes create mode 100644 shared/sentry/tests/unit/CMakeLists.txt create mode 100644 shared/sentry/tests/unit/fuzz.c create mode 100644 shared/sentry/tests/unit/main.c create mode 100644 shared/sentry/tests/unit/sentry_testsupport.h create mode 100644 shared/sentry/tests/unit/test_attachments.c create mode 100644 shared/sentry/tests/unit/test_basic.c create mode 100644 shared/sentry/tests/unit/test_concurrency.c create mode 100644 shared/sentry/tests/unit/test_consent.c create mode 100644 shared/sentry/tests/unit/test_envelopes.c create mode 100644 shared/sentry/tests/unit/test_failures.c create mode 100644 shared/sentry/tests/unit/test_fuzzfailures.c create mode 100644 shared/sentry/tests/unit/test_logger.c create mode 100644 shared/sentry/tests/unit/test_modulefinder.c create mode 100644 shared/sentry/tests/unit/test_mpack.c create mode 100644 shared/sentry/tests/unit/test_path.c create mode 100644 shared/sentry/tests/unit/test_ratelimiter.c create mode 100644 shared/sentry/tests/unit/test_sampling.c create mode 100644 shared/sentry/tests/unit/test_session.c create mode 100644 shared/sentry/tests/unit/test_slice.c create mode 100644 shared/sentry/tests/unit/test_symbolizer.c create mode 100644 shared/sentry/tests/unit/test_sync.c create mode 100644 shared/sentry/tests/unit/test_tracing.c create mode 100644 shared/sentry/tests/unit/test_uninit.c create mode 100644 shared/sentry/tests/unit/test_unwinder.c create mode 100644 shared/sentry/tests/unit/test_utils.c create mode 100644 shared/sentry/tests/unit/test_uuid.c create mode 100644 shared/sentry/tests/unit/test_value.c create mode 100644 shared/sentry/tests/unit/tests.inc create mode 100644 shared/sentry/tests/valgrind.txt create mode 100644 shared/sentry/toolchains/msys2-mingw64-pkglist.txt create mode 100644 shared/sentry/toolchains/msys2.cmake create mode 100644 shared/sentry/vendor/acutest.h create mode 100644 shared/sentry/vendor/jsmn.h create mode 100644 shared/sentry/vendor/mpack.c create mode 100644 shared/sentry/vendor/mpack.h create mode 100644 shared/sentry/vendor/stb_sprintf.c create mode 100644 shared/sentry/vendor/stb_sprintf.h delete mode 100644 shared/signalhandler.h create mode 100644 web/src/faq.rst diff --git a/Makefile b/Makefile index 5b1642877..c1ee56421 100644 --- a/Makefile +++ b/Makefile @@ -1,67 +1,122 @@ +.PHONY: all clean release build liboxide erode tarnish rot fret oxide decay corrupt anxiety sentry + all: release +.NOTPARALLEL: + +# FEATURES += sentry + +ifneq ($(filter sentry,$(FEATURES)),) +OBJ += sentry +RELOBJ += release/opt/lib/libsentry.so +DEFINES += 'DEFINES+="SENTRY"' +endif + clean: rm -rf .build rm -rf release -release: clean - $(MAKE) build +release: clean build $(RELOBJ) mkdir -p release - INSTALL_ROOT=../../release $(MAKE) -C .build/process-manager install - INSTALL_ROOT=../../release $(MAKE) -C .build/system-service install - INSTALL_ROOT=../../release $(MAKE) -C .build/settings-manager install - INSTALL_ROOT=../../release $(MAKE) -C .build/screenshot-tool install - INSTALL_ROOT=../../release $(MAKE) -C .build/screenshot-viewer install - INSTALL_ROOT=../../release $(MAKE) -C .build/launcher install - INSTALL_ROOT=../../release $(MAKE) -C .build/lockscreen install - INSTALL_ROOT=../../release $(MAKE) -C .build/task-switcher install - -build: tarnish erode rot oxide decay corrupt fret anxiety - -erode: + INSTALL_ROOT=../../release $(MAKE) -j`nproc` -C .build/liboxide install + INSTALL_ROOT=../../release $(MAKE) -j`nproc` -C .build/process-manager install + INSTALL_ROOT=../../release $(MAKE) -j`nproc` -C .build/system-service install + INSTALL_ROOT=../../release $(MAKE) -j`nproc` -C .build/settings-manager install + INSTALL_ROOT=../../release $(MAKE) -j`nproc` -C .build/screenshot-tool install + INSTALL_ROOT=../../release $(MAKE) -j`nproc` -C .build/screenshot-viewer install + INSTALL_ROOT=../../release $(MAKE) -j`nproc` -C .build/launcher install + INSTALL_ROOT=../../release $(MAKE) -j`nproc` -C .build/lockscreen install + INSTALL_ROOT=../../release $(MAKE) -j`nproc` -C .build/task-switcher install + +build: liboxide tarnish erode rot oxide decay corrupt fret anxiety + +liboxide: $(OBJ) .build/liboxide/libliboxide.so + +.build/liboxide/libliboxide.so: + mkdir -p .build/liboxide + cp -r shared/liboxide/* .build/liboxide + cd .build/liboxide && qmake $(DEFINES) liboxide.pro + $(MAKE) -j`nproc` -C .build/liboxide all + +erode: liboxide .build/process-manager/erode + +.build/process-manager/erode: mkdir -p .build/process-manager cp -r applications/process-manager/* .build/process-manager - cd .build/process-manager && qmake erode.pro - $(MAKE) -C .build/process-manager all + cd .build/process-manager && qmake $(DEFINES) erode.pro + $(MAKE) -j`nproc` -C .build/process-manager all + +tarnish: liboxide .build/system-service/tarnish -tarnish: +.build/system-service/tarnish: mkdir -p .build/system-service cp -r applications/system-service/* .build/system-service - cd .build/system-service && qmake tarnish.pro - $(MAKE) -C .build/system-service all + cd .build/system-service && qmake $(DEFINES) tarnish.pro + $(MAKE) -j`nproc` -C .build/system-service all -rot: tarnish +rot: tarnish liboxide .build/settings-manager/rot + +.build/settings-manager/rot: mkdir -p .build/settings-manager cp -r applications/settings-manager/* .build/settings-manager - cd .build/settings-manager && qmake rot.pro - $(MAKE) -C .build/settings-manager all + cd .build/settings-manager && qmake $(DEFINES) rot.pro + $(MAKE) -j`nproc` -C .build/settings-manager all + +fret: tarnish liboxide .build/screenshot-tool/fret -fret: tarnish +.build/screenshot-tool/fret: mkdir -p .build/screenshot-tool cp -r applications/screenshot-tool/* .build/screenshot-tool - cd .build/screenshot-tool && qmake fret.pro - $(MAKE) -C .build/screenshot-tool all + cd .build/screenshot-tool && qmake $(DEFINES) fret.pro + $(MAKE) -j`nproc` -C .build/screenshot-tool all + +oxide: tarnish liboxide .build/launcher/oxide -oxide: tarnish +.build/launcher/oxide: mkdir -p .build/launcher cp -r applications/launcher/* .build/launcher - cd .build/launcher && qmake oxide.pro - $(MAKE) -C .build/launcher all + cd .build/launcher && qmake $(DEFINES) oxide.pro + $(MAKE) -j`nproc` -C .build/launcher all -decay: tarnish +decay: tarnish liboxide .build/lockscreen/decay + +.build/lockscreen/decay: mkdir -p .build/lockscreen cp -r applications/lockscreen/* .build/lockscreen - cd .build/lockscreen && qmake decay.pro - $(MAKE) -C .build/lockscreen all + cd .build/lockscreen && qmake $(DEFINES) decay.pro + $(MAKE) -j`nproc` -C .build/lockscreen all + +corrupt: tarnish liboxide .build/task-switcher/corrupt -corrupt: tarnish +.build/task-switcher/corrupt: mkdir -p .build/task-switcher cp -r applications/task-switcher/* .build/task-switcher - cd .build/task-switcher && qmake corrupt.pro - $(MAKE) -C .build/task-switcher all - -anxiety: tarnish + cd .build/task-switcher && qmake $(DEFINES) corrupt.pro + $(MAKE) -j`nproc` -C .build/task-switcher all + +anxiety: tarnish liboxide .build/screenshot-viewer/anxiety + +.build/screenshot-viewer/anxiety: mkdir -p .build/screenshot-viewer cp -r applications/screenshot-viewer/* .build/screenshot-viewer - cd .build/screenshot-viewer && qmake anxiety.pro - $(MAKE) -C .build/screenshot-viewer all + cd .build/screenshot-viewer && qmake $(DEFINES) anxiety.pro + $(MAKE) -j`nproc` -C .build/screenshot-viewer all + +sentry: .build/sentry/libsentry.so + +.build/sentry/libsentry.so: + cd shared/sentry && cmake -B ../../.build/sentry \ + -DBUILD_SHARED_LIBS=ON \ + -DSENTRY_INTEGRATION_QT=ON \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DSENTRY_PIC=OFF \ + -DSENTRY_BACKEND=breakpad \ + -DSENTRY_BREAKPAD_SYSTEM=OFF \ + -DSENTRY_EXPORT_SYMBOLS=ON \ + -DSENTRY_PTHREAD=ON + cd shared/sentry && cmake --build ../../.build/sentry --parallel + cd shared/sentry && cmake --install ../../.build/sentry --prefix ../../.build/sentry --config RelWithDebInfo + +release/opt/lib/libsentry.so: sentry + mkdir -p release/opt/lib + cp .build/sentry/libsentry.so release/opt/lib/ diff --git a/applications/launcher/controller.cpp b/applications/launcher/controller.cpp index 678802292..9d3156103 100644 --- a/applications/launcher/controller.cpp +++ b/applications/launcher/controller.cpp @@ -213,7 +213,7 @@ QList Controller::getApps(){ auto name = app.name(); auto appItem = getApplication(name); if(appItem == nullptr){ - qDebug() << name; + qDebug() << "New application:" << name; appItem = new AppItem(this); applications.append(appItem); } @@ -229,12 +229,12 @@ QList Controller::getApps(){ appItem->setProperty("running", running.contains(name)); auto icon = app.icon(); if(!icon.isEmpty() && QFile(icon).exists()){ - appItem->setProperty("imgFile", "file:" + icon); + appItem->setProperty("imgFile", "file:" + icon); } if(!appItem->ok()){ qDebug() << "Invalid item" << appItem->property("name").toString(); applications.removeAll(appItem); - delete appItem; + appItem->deleteLater(); } } // Sort by name @@ -273,11 +273,10 @@ void Controller::importDraftApps(){ continue; } QTextStream in(&file); - AppItem appItem(this); - QString onStop = ""; + QVariantMap properties; while (!in.atEnd()) { QString line = in.readLine(); - if(line.startsWith("#")){ + if(line.startsWith("#") || line.trimmed().isEmpty()){ continue; } QStringList parts = line.split("="); @@ -287,24 +286,30 @@ void Controller::importDraftApps(){ } QString lhs = parts.at(0); QString rhs = parts.at(1); - QSet known = { "name", "desc", "call" }; if(rhs != ":" && rhs != ""){ - if(known.contains(lhs)){ - appItem.setProperty(lhs.toUtf8(), rhs); + if(lhs == "name"){ + properties.insert("name", rhs); + }else if(lhs == "desc"){ + properties.insert("description", rhs); }else if(lhs == "imgFile"){ - appItem.setProperty(lhs.toUtf8(), configDirectoryPath + "/icons/" + rhs + ".png"); + auto icon = configDirectoryPath + "/icons/" + rhs + ".png"; + if(icon.startsWith("qrc:")){ + icon = ""; + } + properties.insert("icon", icon); + }else if(lhs == "call"){ + properties.insert("bin", rhs); }else if(lhs == "term"){ - onStop = rhs.trimmed(); + properties.insert("onStop", rhs.trimmed()); } } } file.close(); - auto name = appItem.property("name").toString(); - appItem.setProperty("displayName", name); + auto name = properties["name"].toString(); QDBusObjectPath path = appsApi->getApplicationPath(name); if(path.path() != "/"){ qDebug() << "Already exists" << name; - auto icon = appItem.property("imgFile").toString(); + auto icon = properties["icon"].toString(); if(icon.isEmpty()){ continue; } @@ -314,17 +319,8 @@ void Controller::importDraftApps(){ } continue; } - auto icon = appItem.property("imgFile").toString(); - if(icon.startsWith("qrc:")){ - icon = ""; - } - QVariantMap properties; - properties.insert("name", name); - properties.insert("displayName", appItem.property("displayName")); - properties.insert("description", appItem.property("desc")); - properties.insert("bin", appItem.property("call")); - properties.insert("icon", icon); - properties.insert("onStop", onStop); + qDebug() << "Not found, creating..."; + properties.insert("displayName", name); path = appsApi->registerApplication(properties); if(path.path() == "/"){ qDebug() << "Failed to import" << name; diff --git a/applications/launcher/controller.h b/applications/launcher/controller.h index 888817384..5a2fc9f15 100644 --- a/applications/launcher/controller.h +++ b/applications/launcher/controller.h @@ -3,14 +3,15 @@ #include #include #include +#include #include +#include +#include #include "appitem.h" -#include "eventfilter.h" #include "sysobject.h" #include "wifinetworklist.h" #include "notificationlist.h" -#include "devicesettings.h" #include "dbusservice_interface.h" #include "powerapi_interface.h" #include "wifiapi_interface.h" @@ -25,6 +26,8 @@ #define OXIDE_SERVICE_PATH "/codes/eeems/oxide1" using namespace codes::eeems::oxide1; +using namespace Oxide; +using namespace Oxide::Sentry; enum State { Normal, PowerSaving }; enum BatteryState { BatteryUnknown, BatteryCharging, BatteryDischarging, BatteryNotPresent }; @@ -65,7 +68,8 @@ class Controller : public QObject : QObject(parent), m_wifion(false), wifi("/sys/class/net/wlan0"), - applications() { + applications() + { networks = new WifiNetworkList(); notifications = new NotificationList(); @@ -250,6 +254,15 @@ class Controller : public QObject Q_INVOKABLE void reboot(); Q_INVOKABLE void suspend(); Q_INVOKABLE void lock(); + Q_INVOKABLE void breadcrumb(QString category, QString message, QString type = "default"){ +#ifdef SENTRY + sentry_breadcrumb(category.toStdString().c_str(), message.toStdString().c_str(), type.toStdString().c_str()); +#else + Q_UNUSED(category); + Q_UNUSED(message); + Q_UNUSED(type); +#endif + } void updateBatteryLevel(); bool automaticSleep() const { return m_automaticSleep; }; void setAutomaticSleep(bool); @@ -396,12 +409,7 @@ private slots: void notificationAdded(const QDBusObjectPath& path){ auto notification = new Notification(OXIDE_SERVICE, path.path(), QDBusConnection::systemBus(), this); notifications->append(notification); - emit notificationsChanged(notifications); - if(notifications->length() > 1){ - setNotification(QStringLiteral("%1 notifications").arg(notifications->length())); - return; - } - setNotification("1 notification"); + // Notification UI updates will be handled by notificationsUpdated } void notificationRemoved(const QDBusObjectPath& path){ auto notification = notifications->get(path); @@ -409,17 +417,8 @@ private slots: return; } notifications->removeAll(notification); - delete notification; - emit notificationsChanged(notifications); - if(notifications->empty()){ - clearNotification(); - return; - } - if(notifications->length() > 1){ - setNotification(QStringLiteral("%1 notifications").arg(notifications->length())); - return; - } - setNotification("1 notification"); + notification->deleteLater(); + // Notification UI updates will be handled by notificationsUpdated } void notificationChanged(const QDBusObjectPath& path){ Q_UNUSED(path); diff --git a/applications/launcher/main.cpp b/applications/launcher/main.cpp index ff46dc995..c60679912 100644 --- a/applications/launcher/main.cpp +++ b/applications/launcher/main.cpp @@ -16,16 +16,18 @@ #include #include #include +#include +#include #include "controller.h" -#include "eventfilter.h" -#include "devicesettings.h" #ifdef __arm__ Q_IMPORT_PLUGIN(QsgEpaperPlugin) #endif using namespace std; +using namespace Oxide; +using namespace Oxide::Sentry; const char *qt_version = qVersion(); @@ -33,10 +35,6 @@ function shutdown_handler; void signalHandler2(int signal) { shutdown_handler(signal); } int main(int argc, char *argv[]){ -// QSettings xochitlSettings("/home/root/.config/remarkable/xochitl.conf", QSettings::IniFormat); -// xochitlSettings.sync(); -// qDebug() << xochitlSettings.value("Password").toString(); - if (strcmp(qt_version, QT_VERSION_STR) != 0){ qDebug() << "Version mismatch, Runtime: " << qt_version << ", Build: " << QT_VERSION_STR; } @@ -49,6 +47,7 @@ int main(int argc, char *argv[]){ // qputenv("QT_DEBUG_BACKINGSTORE", "1"); #endif QGuiApplication app(argc, argv); + sentry_init("oxide", argv); auto filter = new EventFilter(&app); app.setOrganizationName("Eeems"); app.setOrganizationDomain(OXIDE_SERVICE); diff --git a/applications/launcher/main.qml b/applications/launcher/main.qml index 49a25ff77..28a0fb5f0 100755 --- a/applications/launcher/main.qml +++ b/applications/launcher/main.qml @@ -39,17 +39,25 @@ ApplicationWindow { font: iconFont.name width: 310 Action { text: qsTr(" Reload"); onTriggered: { + controller.breadcrumb("menu.reload", "click", "ui"); controller.startup(); appsView.model = controller.getApps(); }} Action { text: qsTr(" Import Apps"); onTriggered:{ + controller.breadcrumb("menu.import", "click", "ui"); controller.importDraftApps(); appsView.model = controller.getApps(); } } - Action { text: qsTr(" Options"); onTriggered: stateController.state = "settings" } + Action { + text: qsTr(" Options") + onTriggered: { + controller.breadcrumb("menu.options", "click", "ui"); + stateController.state = "settings"; + } + } } } StatusIcon { @@ -61,7 +69,10 @@ ApplicationWindow { MouseArea { anchors.fill: parent enabled: parent.visible - onClicked: stateController.state = "notifications" + onClicked: { + controller.breadcrumb("notifications", "click", "ui"); + stateController.state = "notifications"; + } } } } @@ -99,7 +110,10 @@ ApplicationWindow { text: controller.showWifiDb ? rssi + "dBm" : "" MouseArea { anchors.fill: parent - onClicked: stateController.state = "wifi" + onClicked: { + controller.breadcrumb("wifi", "click", "ui"); + stateController.state = "wifi"; + } } } StatusIcon { @@ -151,21 +165,33 @@ ApplicationWindow { Action { text: qsTr(" Suspend") enabled: !controller.sleepInhibited - onTriggered: controller.suspend(); + onTriggered: { + controller.breadcrumb("menu.suspend", "click", "ui"); + controller.suspend(); + } } Action { text: qsTr(" Reboot") enabled: !controller.powerOffInhibited - onTriggered: controller.reboot() + onTriggered: { + controller.breadcrumb("menu.reboot", "click", "ui"); + controller.reboot(); + } } Action { text: qsTr("î©– Shutdown") enabled: !controller.powerOffInhibited - onTriggered: controller.powerOff() + onTriggered: { + controller.breadcrumb("menu.shutdown", "click", "ui"); + controller.powerOff(); + } } Action { text: qsTr("î¦ Lock") - onTriggered: controller.lock() + onTriggered: { + controller.breadcrumb("menu.lock", "click", "ui"); + controller.lock(); + } } } } @@ -177,7 +203,10 @@ ApplicationWindow { color: "white" MouseArea { anchors.fill: parent - onClicked: stateController.state = "calendar" + onClicked: { + controller.breadcrumb("clock", "click", "ui"); + stateController.state = "calendar"; + } } } } @@ -224,10 +253,14 @@ ApplicationWindow { width: appsView.cellWidth height: appsView.cellHeight onLongPress: { + controller.breadcrumb("appItem", "longPress", "ui"); itemInfo.model = model.modelData; stateController.state = "itemInfo" } - onClicked: model.modelData.execute(); + onClicked: { + controller.breadcrumb("appItem", "click", "ui"); + model.modelData.execute(); + } } }, Popup { @@ -310,6 +343,7 @@ ApplicationWindow { MouseArea { anchors.fill: parent onClicked: { + controller.breadcrumb("appItem.autoStart", "click", "ui"); if(!itemInfo.model){ return } @@ -333,6 +367,7 @@ ApplicationWindow { MouseArea { anchors.fill: parent onClicked: { + controller.breadcrumb("appItem.kill", "click", "ui"); if(itemInfo.model){ itemInfo.model.stop(); appsView.model = controller.getApps(); @@ -384,7 +419,10 @@ ApplicationWindow { Transition { from: "*"; to: "settings" SequentialAnimation { - ScriptAction { script: stateController.previousState = "settings" } + ScriptAction { script: { + controller.breadcrumb("navigation", "settings", "navigation"); + stateController.previousState = "settings"; + } } ScriptAction { script: console.log("Opening settings") } PropertyAction { target: settings; property: "visible"; value: true } PropertyAction { target: wifi; property: "visible"; value: false } @@ -396,7 +434,10 @@ ApplicationWindow { Transition { from: "*"; to: "wifi" ParallelAnimation { - ScriptAction { script: stateController.previousState = "wifi" } + ScriptAction { script: { + controller.breadcrumb("navigation", "wifi", "navigation"); + stateController.previousState = "wifi"; + } } ScriptAction { script: console.log("Opening wifi menu") } PropertyAction { target: wifi; property: "visible"; value: true } PropertyAction { target: calendar; property: "visible"; value: false } @@ -408,7 +449,10 @@ ApplicationWindow { Transition { from: "*"; to: "calendar" SequentialAnimation { - ScriptAction { script: stateController.previousState = "calendar" } + ScriptAction { script: { + controller.breadcrumb("navigation", "calendar", "navigation"); + stateController.previousState = "calendar"; + } } ScriptAction { script: console.log("Opening calendar") } PropertyAction { target: calendar; property: "visible"; value: true } PropertyAction { target: settings; property: "visible"; value: false } @@ -420,7 +464,10 @@ ApplicationWindow { Transition { from: "*"; to: "notifications" SequentialAnimation { - ScriptAction { script: stateController.previousState = "notifications" } + ScriptAction { script: { + controller.breadcrumb("navigation", "notifications", "navigation"); + stateController.previousState = "notifications"; + } } ScriptAction { script: console.log("Opening notifications") } PropertyAction { target: notifications; property: "visible"; value: true } PropertyAction { target: calendar; property: "visible"; value: false } @@ -432,7 +479,10 @@ ApplicationWindow { Transition { from: "loaded"; to: "itemInfo" SequentialAnimation { - ScriptAction { script: console.log("Viewing item info") } + ScriptAction { script: { + controller.breadcrumb("navigation", "itemInfo", "navigation"); + console.log("Viewing item info"); + } } PropertyAction { target: itemInfo; property: "visible"; value: true } PropertyAction { target: menu; property: "focus"; value: false } } @@ -440,7 +490,10 @@ ApplicationWindow { Transition { from: "*"; to: "loaded" SequentialAnimation { - ScriptAction { script: console.log("Main display") } + ScriptAction { script: { + controller.breadcrumb("navigation", "main", "navigation"); + console.log("Main display"); + } } PropertyAction { target: calendar; property: "visible"; value: false } PropertyAction { target: settings; property: "visible"; value: false } PropertyAction { target: wifi; property: "visible"; value: false } diff --git a/applications/launcher/notificationlist.h b/applications/launcher/notificationlist.h index 0b507c89e..558c2cdb7 100644 --- a/applications/launcher/notificationlist.h +++ b/applications/launcher/notificationlist.h @@ -21,7 +21,7 @@ class NotificationItem : public QObject { } ~NotificationItem() { if(m_notification != nullptr){ - delete m_notification; + m_notification->deleteLater(); } } QString identifier() { return m_identifier; } @@ -121,7 +121,7 @@ class NotificationList : public QAbstractListModel for(auto notification : notifications){ notification->notification()->remove().waitForFinished(); if(notification != nullptr){ - delete notification; + notification->deleteLater(); } } notifications.clear(); @@ -136,7 +136,7 @@ class NotificationList : public QAbstractListModel beginRemoveRows(QModelIndex(), notifications.indexOf(notification), notifications.indexOf(notification)); i.remove(); notification->notification()->remove().waitForFinished(); - delete notification; + notification->deleteLater(); endRemoveRows(); } } @@ -151,7 +151,7 @@ class NotificationList : public QAbstractListModel beginRemoveRows(QModelIndex(), notifications.indexOf(item), notifications.indexOf(item)); i.remove(); item->notification()->remove().waitForFinished(); - delete item; + item->deleteLater(); endRemoveRows(); count++; } diff --git a/applications/launcher/oxide.pro b/applications/launcher/oxide.pro index 83de1abbb..99ec2bddb 100644 --- a/applications/launcher/oxide.pro +++ b/applications/launcher/oxide.pro @@ -4,34 +4,18 @@ QT += dbus CONFIG += c++11 CONFIG += qml_debug -# The following define makes your compiler emit warnings if you use -# any feature of Qt which as been marked deprecated (the exact warnings -# depend on your compiler). Please consult the documentation of the -# deprecated API in order to know how to port your code away from it. DEFINES += QT_DEPRECATED_WARNINGS - -# You can also make your code fail to compile if you use deprecated APIs. -# In order to do so, uncomment the following line. -# You can also select to disable deprecated APIs only up to a certain version of Qt. -#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ main.cpp \ controller.cpp \ appitem.cpp \ - sysobject.cpp \ - ../../shared/devicesettings.cpp \ - ../../shared/eventfilter.cpp + sysobject.cpp RESOURCES += qml.qrc -# Additional import path used to resolve QML modules in Qt Creator's code model -QML_IMPORT_PATH = - -# Additional import path used to resolve QML modules just for Qt Quick Designer -QML_DESIGNER_IMPORT_PATH = - # Default rules for deployment. target.path = /opt/bin !isEmpty(target.path): INSTALLS += target @@ -49,10 +33,6 @@ DBUS_INTERFACES += ../../interfaces/systemapi.xml DBUS_INTERFACES += ../../interfaces/notificationapi.xml DBUS_INTERFACES += ../../interfaces/notification.xml -linux-oe-g++ { - LIBS += -lqsgepaper -} - icons.files = ../../assets/etc/draft/icons/oxide-splash.png icons.path = /opt/etc/draft/icons INSTALLS += icons @@ -64,19 +44,34 @@ INSTALLS += configFile DISTFILES += \ ../../assets/etc/dbus-1/system.d/org.freedesktop.login1.conf \ ../../assets/etc/oxide.conf -ls + HEADERS += \ controller.h \ appitem.h \ mxcfb.h \ notificationlist.h \ sysobject.h \ - ../../shared/dbussettings.h \ - ../../shared/devicesettings.h \ - ../../shared/eventfilter.h \ wifinetworklist.h LIBS += -L$$PWD/../../shared/ -lqsgepaper INCLUDEPATH += $$PWD/../../shared DEPENDPATH += $$PWD/../../shared + +exists($$PWD/../../.build/sentry) { + LIBS += -L$$PWD/../../.build/sentry/lib -lsentry -ldl -lcurl -lbreakpad_client + INCLUDEPATH += $$PWD/../../.build/sentry/include + DEPENDPATH += $$PWD/../../.build/sentry/lib + + library.files = ../../.build/sentry/libsentry.so + library.path = /opt/lib + INSTALLS += library +} + +LIBS += -L$$PWD/../../.build/liboxide -lliboxide +INCLUDEPATH += $$PWD/../../shared/liboxide +DEPENDPATH += $$PWD/../../shared/liboxide + +QMAKE_RPATHDIR += /lib /usr/lib /opt/lib /opt/usr/lib + +VERSION = 2.3 diff --git a/applications/launcher/widgets/CalendarMenu.qml b/applications/launcher/widgets/CalendarMenu.qml index 72a2b6351..43b09c5a3 100644 --- a/applications/launcher/widgets/CalendarMenu.qml +++ b/applications/launcher/widgets/CalendarMenu.qml @@ -64,7 +64,10 @@ Item { BetterButton{ text: "Close" Layout.fillWidth: true - onClicked: calendar.close() + onClicked: { + controller.breadcrumb("calendar.close", "click", "ui"); + calendar.close(); + } } } } diff --git a/applications/launcher/widgets/NotificationsPopup.qml b/applications/launcher/widgets/NotificationsPopup.qml index d15673665..3ba862c8c 100644 --- a/applications/launcher/widgets/NotificationsPopup.qml +++ b/applications/launcher/widgets/NotificationsPopup.qml @@ -55,7 +55,10 @@ Item { Layout.preferredWidth: 64 MouseArea { anchors.fill: parent - onClicked: model.display && model.display.click() + onClicked: { + controller.breadcrumb("notifications.notification.icon", "click", "ui"); + model.display && model.display.click(); + } } } Label { @@ -65,7 +68,10 @@ Item { Layout.preferredWidth: parent.width - 50 - 64 MouseArea { anchors.fill: parent - onClicked: model.display && model.display.click() + onClicked: { + controller.breadcrumb("notifications.notification.text", "click", "ui"); + model.display && model.display.click(); + } } } Label { @@ -105,6 +111,7 @@ Item { anchors.fill: parent enabled: parent.enabled onClicked: { + controller.breadcrumb("notifications.notification.remove", "click", "ui"); if(!model.display){ return } @@ -145,12 +152,14 @@ Item { pageSize = (notifications.height / notifications.itemAt(0, 0).height).toFixed(0); } if(direction == "down"){ + controller.breadcrumb("notifications", "scroll.up", "ui"); console.log("Scroll up"); currentIndex = currentIndex - pageSize; if(currentIndex < 0){ currentIndex = 0; } }else if(direction == "up"){ + controller.breadcrumb("notifications", "scroll.down", "ui"); console.log("Scroll down"); currentIndex = currentIndex + pageSize; if(currentIndex > notifications.count){ @@ -168,6 +177,7 @@ Item { text: "Clear" Layout.fillWidth: true onClicked: { + controller.breadcrumb("notifications.clear", "click", "ui"); notifications.model.clear(); popup.close(); } @@ -175,7 +185,10 @@ Item { BetterButton{ text: "Close" Layout.fillWidth: true - onClicked: popup.close() + onClicked: { + controller.breadcrumb("notifications.close", "click", "ui"); + popup.close(); + } } } } diff --git a/applications/launcher/widgets/SettingsPopup.qml b/applications/launcher/widgets/SettingsPopup.qml index 566aa8e49..983141458 100644 --- a/applications/launcher/widgets/SettingsPopup.qml +++ b/applications/launcher/widgets/SettingsPopup.qml @@ -239,13 +239,17 @@ Item { text: "Reset" Layout.columnSpan: parent.columns Layout.fillWidth: true - onClicked: controller.loadSettings() + onClicked: { + controller.breadcrumb("settings.reset", "click", "ui"); + controller.loadSettings(); + } } BetterButton { text: "Close" Layout.columnSpan: parent.columns Layout.fillWidth: true onClicked: { + controller.breadcrumb("settings.close", "click", "ui"); controller.saveSettings(); settings.close(); } diff --git a/applications/launcher/widgets/WifiMenu.qml b/applications/launcher/widgets/WifiMenu.qml index 81192824a..b6aad2c1f 100644 --- a/applications/launcher/widgets/WifiMenu.qml +++ b/applications/launcher/widgets/WifiMenu.qml @@ -45,6 +45,7 @@ Item { Layout.fillWidth: true Layout.preferredWidth: wifi.width / 2 onClicked: { + controller.breadcrumb("wifi.toggle", "click", "ui"); if(controller.wifiOn){ controller.turnOffWifi(); text = "Turn wifi on"; @@ -61,6 +62,7 @@ Item { Layout.fillWidth: true Layout.preferredWidth: wifi.width / 2 onClicked: { + controller.breadcrumb("wifi.scan", "click", "ui"); networks.model.scan(true); networks.model.sort(); } @@ -103,6 +105,7 @@ Item { anchors.fill: parent enabled: parent.enabled onClicked: { + controller.breadcrumb("wifi.network.forget", "click", "ui"); if(!model.display){ return } @@ -120,6 +123,7 @@ Item { anchors.fill: parent enabled: parent.enabled onClicked: { + controller.breadcrumb("wifi.network.toggle", "click", "ui"); if(!model.display){ return } @@ -165,12 +169,14 @@ Item { pageSize = (networks.height / networks.itemAt(0, 0).height).toFixed(0); } if(direction == "down"){ + controller.breadcrumb("wifi", "scroll.up", "ui"); console.log("Scroll up"); currentIndex = currentIndex - pageSize; if(currentIndex < 0){ currentIndex = 0; } }else if(direction == "up"){ + controller.breadcrumb("wifi", "scroll.down", "ui"); console.log("Scroll down"); currentIndex = currentIndex + pageSize; if(currentIndex > networks.count){ @@ -186,7 +192,10 @@ Item { BetterButton{ text: "Close" Layout.fillWidth: true - onClicked: wifi.close(); + onClicked: { + controller.breadcrumb("wifi.close", "click", "ui"); + wifi.close(); + } } } } diff --git a/applications/lockscreen/controller.h b/applications/lockscreen/controller.h index 7a45bafc7..59c4ce10e 100644 --- a/applications/lockscreen/controller.h +++ b/applications/lockscreen/controller.h @@ -3,8 +3,7 @@ #include #include - -#include "dbussettings.h" +#include #include "dbusservice_interface.h" #include "systemapi_interface.h" @@ -14,6 +13,7 @@ #include "application_interface.h" using namespace codes::eeems::oxide1; +using namespace Oxide::Sentry; #define DECAY_SETTINGS_VERSION 1 @@ -26,6 +26,10 @@ class Controller : public QObject { Q_OBJECT Q_PROPERTY(bool powerOffInhibited READ powerOffInhibited NOTIFY powerOffInhibitedChanged) Q_PROPERTY(bool sleepInhibited READ sleepInhibited NOTIFY sleepInhibitedChanged) + Q_PROPERTY(bool firstLaunch READ firstLaunch WRITE setFirstLaunch NOTIFY firstLaunchChanged) + Q_PROPERTY(bool telemetry READ telemetry WRITE setTelemetry NOTIFY telemetryChanged) + Q_PROPERTY(bool applicationUsage READ applicationUsage WRITE setApplicationUsage NOTIFY applicationUsageChanged) + Q_PROPERTY(bool crashReport READ crashReport WRITE setCrashReport NOTIFY crashReportChanged) public: Controller(QObject* parent) : QObject(parent), confirmPin(), settings(this) { @@ -95,12 +99,25 @@ class Controller : public QObject { if(version < DECAY_SETTINGS_VERSION){ migrate(&settings, version); } + + connect(&sharedSettings, &Oxide::SharedSettings::firstLaunchChanged, this, &Controller::firstLaunchChanged); + connect(&sharedSettings, &Oxide::SharedSettings::telemetryChanged, this, &Controller::telemetryChanged); + connect(&sharedSettings, &Oxide::SharedSettings::applicationUsageChanged, this, &Controller::applicationUsageChanged); + connect(&sharedSettings, &Oxide::SharedSettings::crashReportChanged, this, &Controller::crashReportChanged); } ~Controller(){ if(clockTimer->isActive()){ clockTimer->stop(); } } + bool firstLaunch(){ return sharedSettings.firstLaunch(); } + void setFirstLaunch(bool firstLaunch){ sharedSettings.set_firstLaunch(firstLaunch); } + bool telemetry(){ return sharedSettings.telemetry(); } + void setTelemetry(bool telemetry){ sharedSettings.set_telemetry(telemetry); } + bool applicationUsage(){ return sharedSettings.applicationUsage(); } + void setApplicationUsage(bool applicationUsage){ sharedSettings.set_applicationUsage(applicationUsage); } + bool crashReport(){ return sharedSettings.crashReport(); } + void setCrashReport(bool crashReport){ sharedSettings.set_crashReport(crashReport); } Q_INVOKABLE void startup(){ if(!getBatteryUI() || !getWifiUI() || !getClockUI() || !getStateControllerUI()){ @@ -123,14 +140,22 @@ class Controller : public QObject { QObject::connect(clockTimer , &QTimer::timeout, this, &Controller::updateClock); clockTimer->start(); - if(!settings.contains("pin")){ + if(firstLaunch()){ qDebug() << "First launch"; QTimer::singleShot(100, [this]{ - stateControllerUI->setProperty("state", xochitlPin().isEmpty() ? "firstLaunch" : "import"); + stateControllerUI->setProperty("state", "telemetry"); }); return; } - + // There is no PIN configuration + if(!settings.contains("pin")){ + qDebug() << "No Pin"; + QTimer::singleShot(100, [this]{ + stateControllerUI->setProperty("state", xochitlPin().isEmpty() ? "pinPrompt" : "import"); + }); + return; + } + // There is a PIN set if(hasPin()){ qDebug() << "Prompting for PIN"; QTimer::singleShot(100, [this]{ @@ -138,6 +163,7 @@ class Controller : public QObject { }); return; } + // PIN is set explicitly to a blank value qDebug() << "No pin set"; QTimer::singleShot(100, [this]{ setState("noPin"); @@ -235,6 +261,7 @@ class Controller : public QObject { return true; } Q_INVOKABLE void importPin(){ + qDebug() << "Importing PIN from Xochitl"; setStoredPin(xochitlPin()); if(!storedPin().isEmpty()){ removeXochitlPin(); @@ -242,9 +269,19 @@ class Controller : public QObject { setState("loaded"); } Q_INVOKABLE void clearPin(){ + qDebug() << "Clearing PIN"; setStoredPin(""); startup(); } + Q_INVOKABLE void breadcrumb(QString category, QString message, QString type = "default"){ +#ifdef SENTRY + sentry_breadcrumb(category.toStdString().c_str(), message.toStdString().c_str(), type.toStdString().c_str()); +#else + Q_UNUSED(category); + Q_UNUSED(message); + Q_UNUSED(type); +#endif + } bool sleepInhibited(){ return systemApi->sleepInhibited(); } bool powerOffInhibited(){ return systemApi->powerOffInhibited(); } @@ -271,6 +308,10 @@ class Controller : public QObject { signals: void sleepInhibitedChanged(bool); void powerOffInhibitedChanged(bool); + void firstLaunchChanged(bool); + void telemetryChanged(bool); + void applicationUsageChanged(bool); + void crashReportChanged(bool); private slots: void deviceSuspending(){ @@ -280,7 +321,7 @@ private slots: } if(state() == "confirmPin"){ confirmPin = ""; - setState("firstLaunch"); + setState("pinPrompt"); }else{ setState("loading"); } @@ -479,14 +520,8 @@ private slots: settings->sync(); } - static QString xochitlPin(){ - QSettings xochitlSettings("/home/root/.config/remarkable/xochitl.conf", QSettings::IniFormat); - xochitlSettings.sync(); - return xochitlSettings.value("Passcode", "").toString(); - } + static QString xochitlPin(){ return xochitlSettings.passcode(); } static void removeXochitlPin(){ - QSettings xochitlSettings("/home/root/.config/remarkable/xochitl.conf", QSettings::IniFormat); - xochitlSettings.sync(); xochitlSettings.remove("Passcode"); xochitlSettings.sync(); } diff --git a/applications/lockscreen/decay.pro b/applications/lockscreen/decay.pro index 684c93fcc..9e1edfd7f 100644 --- a/applications/lockscreen/decay.pro +++ b/applications/lockscreen/decay.pro @@ -5,24 +5,11 @@ QT += dbus CONFIG += c++11 CONFIG += qml_debug -# The following define makes your compiler emit warnings if you use -# any feature of Qt which as been marked deprecated (the exact warnings -# depend on your compiler). Please consult the documentation of the -# deprecated API in order to know how to port your code away from it. DEFINES += QT_DEPRECATED_WARNINGS - -# You can make your code fail to compile if it uses deprecated APIs. -# In order to do so, uncomment the following line. -#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 - -linux-oe-g++ { - LIBS += -lqsgepaper -} +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ - main.cpp \ - ../../shared/devicesettings.cpp \ - ../../shared/eventfilter.cpp + main.cpp # Default rules for deployment. target.path = /opt/bin @@ -37,9 +24,6 @@ DBUS_INTERFACES += ../../interfaces/application.xml INCLUDEPATH += ../../shared HEADERS += \ - ../../shared/dbussettings.h \ - ../../shared/devicesettings.h \ - ../../shared/eventfilter.h \ controller.h RESOURCES += \ @@ -48,3 +32,21 @@ RESOURCES += \ LIBS += -L$$PWD/../../shared/ -lqsgepaper INCLUDEPATH += $$PWD/../../shared DEPENDPATH += $$PWD/../../shared + +exists($$PWD/../../.build/sentry) { + LIBS += -L$$PWD/../../.build/sentry/lib -lsentry -ldl -lcurl -lbreakpad_client + INCLUDEPATH += $$PWD/../../.build/sentry/include + DEPENDPATH += $$PWD/../../.build/sentry/lib + + library.files = ../../.build/sentry/libsentry.so + library.path = /opt/lib + INSTALLS += library +} + +LIBS += -L$$PWD/../../.build/liboxide -lliboxide +INCLUDEPATH += $$PWD/../../shared/liboxide +DEPENDPATH += $$PWD/../../shared/liboxide + +QMAKE_RPATHDIR += /lib /usr/lib /opt/lib /opt/usr/lib + +VERSION = 2.3 diff --git a/applications/lockscreen/main.cpp b/applications/lockscreen/main.cpp index e6ad5ecac..0135c5767 100644 --- a/applications/lockscreen/main.cpp +++ b/applications/lockscreen/main.cpp @@ -4,11 +4,9 @@ #include #include +#include -#include "dbussettings.h" -#include "devicesettings.h" #include "controller.h" -#include "eventfilter.h" #ifdef __arm__ Q_IMPORT_PLUGIN(QsgEpaperPlugin) @@ -17,6 +15,8 @@ Q_IMPORT_PLUGIN(QsgEpaperPlugin) #include "dbusservice_interface.h" using namespace codes::eeems::oxide1; +using namespace Oxide; +using namespace Oxide::Sentry; const char* qt_version = qVersion(); @@ -33,6 +33,7 @@ int main(int argc, char *argv[]){ // qputenv("QT_DEBUG_BACKINGSTORE", "1"); #endif QGuiApplication app(argc, argv); + sentry_init("decay", argv); auto filter = new EventFilter(&app); app.installEventFilter(filter); app.setOrganizationName("Eeems"); diff --git a/applications/lockscreen/main.qml b/applications/lockscreen/main.qml index a82fc3a28..a91431e06 100644 --- a/applications/lockscreen/main.qml +++ b/applications/lockscreen/main.qml @@ -99,17 +99,26 @@ ApplicationWindow { Action { text: qsTr(" Suspend") enabled: !controller.sleepInhibited - onTriggered: controller.suspend(); + onTriggered: { + controller.breadcrumb("menu.suspend", "clicked", "ui"); + controller.suspend(); + } } Action { text: qsTr(" Reboot") enabled: !controller.powerOffInhibited - onTriggered: controller.reboot() + onTriggered: { + controller.breadcrumb("menu.reboot", "clicked", "ui"); + controller.reboot(); + } } Action { text: qsTr("î©– Shutdown") enabled: !controller.powerOffInhibited - onTriggered: controller.powerOff() + onTriggered: { + controller.breadcrumb("menu.shutdown", "clicked", "ui"); + controller.powerOff(); + } } } } @@ -143,6 +152,7 @@ ApplicationWindow { } } onSubmit: { + controller.breadcrumb("pinEntry", "submit", "ui"); if(!controller.submitPin(pin)){ message = "Incorrect PIN"; value = ""; @@ -182,19 +192,25 @@ ApplicationWindow { text: "Cancel" width: height * 2 Layout.fillWidth: true - onClicked: stateController.state = "firstLaunch" + onClicked: { + controller.breadcrumb("cancel", "clicked", "ui"); + stateController.state = "pinPrompt"; + } } BetterButton { text: "Import" width: height * 2 Layout.fillWidth: true - onClicked: controller.importPin() + onClicked: { + controller.breadcrumb("import", "clicked", "ui"); + controller.importPin(); + } } } } }, Popup { - visible: stateController.state == "firstLaunch" + visible: stateController.state == "pinPrompt" x: (parent.width / 2) - (width / 2) y: (parent.height / 2) - (height / 2) width: 1000 @@ -219,13 +235,101 @@ ApplicationWindow { text: "No PIN" width: height * 2 Layout.fillWidth: true - onClicked: controller.clearPin() + onClicked: { + controller.breadcrumb("nopin", "clicked", "ui"); + controller.clearPin(); + } } BetterButton { text: "Create" width: height * 2 Layout.fillWidth: true - onClicked: stateController.state = "prompt" + onClicked: { + controller.breadcrumb("create", "clicked", "ui"); + stateController.state = "prompt"; + } + } + } + } + }, + Popup { + visible: stateController.state == "telemetry" + x: (parent.width / 2) - (width / 2) + y: (parent.height / 2) - (height / 2) + width: 1000 + clip: true + closePolicy: Popup.NoAutoClose + ColumnLayout { + anchors.fill: parent + RowLayout { + Item { Layout.fillWidth: true } + Label { + text: "Opt-in to telemetry?" + Layout.fillHeight: true + } + Item { Layout.fillWidth: true } + } + Label { + text: "Oxide has basic telemetry and crash reporting.\nWould you like to enable it?\nSee https://oxide.eeems.codes/faq.html for more\ninformation." + + Layout.fillWidth: true + } + Item { + Layout.rowSpan: 2 + Layout.fillHeight: true + } + ColumnLayout { + BetterButton { + text: "Full Telemetry" + width: height * 2 + Layout.fillWidth: true + onClicked: { + controller.breadcrumb("full-telemetry", "clicked", "ui"); + controller.telemetry = true; + controller.applicationUsage = true; + controller.crashReport = true; + controller.firstLaunch = false; + stateController.state = "loading"; + } + } + BetterButton { + text: "Basic Telemetry" + width: height * 2 + Layout.fillWidth: true + onClicked: { + controller.breadcrumb("basic-telemetry", "clicked", "ui"); + controller.telemetry = true; + controller.applicationUsage = false; + controller.crashReport = true; + controller.firstLaunch = false; + stateController.state = "loading"; + } + } + BetterButton { + text: "Crash Reports Only" + width: height * 2 + Layout.fillWidth: true + onClicked: { + controller.breadcrumb("crash-report-only", "clicked", "ui"); + controller.telemetry = false; + controller.applicationUsage = false; + controller.crashReport = true; + controller.firstLaunch = false; + stateController.state = "loading"; + } + } + BetterButton { + text: "No Telemetry" + width: height * 2 + Layout.fillWidth: true + onClicked: { + controller.breadcrumb("no-telemetry", "clicked", "ui"); + controller.telemetry = false; + controller.applicationUsage = false; + controller.crashReport = false; + controller.firstLaunch = false; + stateController.state = "loading"; + } } } } @@ -237,7 +341,8 @@ ApplicationWindow { state: "loading" states: [ State { name: "loaded" }, - State { name: "firstLaunch" }, + State { name: "pinPrompt" }, + State { name: "telemetry" }, State { name: "prompt" }, State { name: "confirmPin" }, State { name: "import" }, @@ -249,6 +354,7 @@ ApplicationWindow { from: "*"; to: "loaded" SequentialAnimation { ScriptAction { script: { + controller.breadcrumb("navigation", "main", "navigation"); console.log("PIN Entry"); pinEntry.value = ""; } } @@ -256,18 +362,30 @@ ApplicationWindow { } }, Transition { - from: "*"; to: "firstLaunch" + from: "*"; to: "pinPrompt" SequentialAnimation { PropertyAction { target: pinEntry; property: "visible"; value: false } ScriptAction { script: { + controller.breadcrumb("navigation", "pinPrompt", "navigation"); console.log("Prompt for PIN creation"); } } } }, + Transition { + from: "*"; to: "telemetry" + SequentialAnimation { + PropertyAction { target: pinEntry; property: "visible"; value: false } + ScriptAction { script: { + controller.breadcrumb("navigation", "telemetry", "navigation"); + console.log("Telemetry opt-in screen"); + } } + } + }, Transition { from: "*"; to: "confirmPin" SequentialAnimation { ScriptAction { script: { + controller.breadcrumb("navigation", "confirmPin", "navigation"); console.log("PIN Confirmation"); pinEntry.value = ""; } } @@ -278,6 +396,7 @@ ApplicationWindow { from: "*"; to: "import" SequentialAnimation { ScriptAction { script: { + controller.breadcrumb("navigation", "import", "navigation"); console.log("Import PIN"); } } PropertyAction { target: pinEntry; property: "visible"; value: false } @@ -287,6 +406,7 @@ ApplicationWindow { from: "*"; to: "prompt" SequentialAnimation { ScriptAction { script: { + controller.breadcrumb("navigation", "prompt", "navigation"); console.log("PIN Setup"); pinEntry.value = ""; } } @@ -296,6 +416,7 @@ ApplicationWindow { Transition { from: "*"; to: "noPin" SequentialAnimation { + ScriptAction { script: controller.breadcrumb("navigation", "noPin", "navigation"); } PropertyAction { target: pinEntry; property: "visible"; value: false } } }, @@ -304,6 +425,7 @@ ApplicationWindow { SequentialAnimation { PropertyAction { target: pinEntry; property: "visible"; value: false } ScriptAction { script: { + controller.breadcrumb("navigation", "loading", "navigation"); console.log("Loading display"); controller.startup(); } } diff --git a/applications/process-manager/controller.h b/applications/process-manager/controller.h index a86cab243..10575fb59 100755 --- a/applications/process-manager/controller.h +++ b/applications/process-manager/controller.h @@ -3,9 +3,12 @@ #include #include #include +#include #include "tasklist.h" +using namespace Oxide::Sentry; + class Controller : public QObject { Q_OBJECT @@ -21,6 +24,15 @@ class Controller : public QObject QString sortBy(){ return m_tasks->sortBy(); } void sortBy(QString key){ m_tasks->sort(key); } Q_INVOKABLE void reload(){ m_tasks->reload(); } + Q_INVOKABLE void breadcrumb(QString category, QString message, QString type = "default"){ +#ifdef SENTRY + sentry_breadcrumb(category.toStdString().c_str(), message.toStdString().c_str(), type.toStdString().c_str()); +#else + Q_UNUSED(category); + Q_UNUSED(message); + Q_UNUSED(type); +#endif + } TaskList* tasks(){ return m_tasks; } diff --git a/applications/process-manager/erode.pro b/applications/process-manager/erode.pro index 9daec3dbc..1882ab93a 100755 --- a/applications/process-manager/erode.pro +++ b/applications/process-manager/erode.pro @@ -1,29 +1,13 @@ QT += quick CONFIG += c++11 -# The following define makes your compiler emit warnings if you use -# any feature of Qt which as been marked deprecated (the exact warnings -# depend on your compiler). Please consult the documentation of the -# deprecated API in order to know how to port your code away from it. DEFINES += QT_DEPRECATED_WARNINGS +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 -# You can also make your code fail to compile if you use deprecated APIs. -# In order to do so, uncomment the following line. -# You can also select to disable deprecated APIs only up to a certain version of Qt. -#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 - -SOURCES += main.cpp \ - ../../shared/devicesettings.cpp\ - ../../shared/eventfilter.cpp +SOURCES += main.cpp RESOURCES += qml.qrc -# Additional import path used to resolve QML modules in Qt Creator's code model -QML_IMPORT_PATH = - -# Additional import path used to resolve QML modules just for Qt Quick Designer -QML_DESIGNER_IMPORT_PATH = - # Default rules for deployment. target.path = /opt/bin !isEmpty(target.path): INSTALLS += target @@ -37,22 +21,32 @@ INSTALLS += icons HEADERS += \ controller.h \ taskitem.h \ - ../../shared/dbussettings.h \ - ../../shared/devicesettings.h\ - ../../shared/eventfilter.h \ tasklist.h INCLUDEPATH += $$PWD/../../shared INCLUDEPATH += ../../shared DEPENDPATH += $$PWD/../../shared - -linux-oe-g++ { - LIBS += -lqsgepaper -} - -DISTFILES += +LIBS += -lsystemd LIBS += -L$$PWD/../../shared/ -lqsgepaper INCLUDEPATH += $$PWD/../../shared DEPENDPATH += $$PWD/../../shared + +exists($$PWD/../../.build/sentry) { + LIBS += -L$$PWD/../../.build/sentry/lib -lsentry -ldl -lcurl -lbreakpad_client + INCLUDEPATH += $$PWD/../../.build/sentry/include + DEPENDPATH += $$PWD/../../.build/sentry/lib + + library.files = ../../.build/sentry/libsentry.so + library.path = /opt/lib + INSTALLS += library +} + +LIBS += -L$$PWD/../../.build/liboxide -lliboxide +INCLUDEPATH += $$PWD/../../shared/liboxide +DEPENDPATH += $$PWD/../../shared/liboxide + +QMAKE_RPATHDIR += /lib /usr/lib /opt/lib /opt/usr/lib + +VERSION = 2.3 diff --git a/applications/process-manager/main.cpp b/applications/process-manager/main.cpp index 707ab0ae9..3da8741ab 100755 --- a/applications/process-manager/main.cpp +++ b/applications/process-manager/main.cpp @@ -7,11 +7,10 @@ #include #include #include +#include +#include #include "controller.h" -#include "eventfilter.h" -#include "dbussettings.h" -#include "devicesettings.h" #ifdef __arm__ @@ -19,6 +18,8 @@ Q_IMPORT_PLUGIN(QsgEpaperPlugin) #endif using namespace std; +using namespace Oxide; +using namespace Oxide::Sentry; const char *qt_version = qVersion(); @@ -35,6 +36,7 @@ int main(int argc, char *argv[]){ // qputenv("QT_DEBUG_BACKINGSTORE", "1"); #endif QGuiApplication app(argc, argv); + sentry_init("erode", argv); app.setOrganizationName("Eeems"); app.setOrganizationDomain(OXIDE_SERVICE); app.setApplicationName("tarnish"); diff --git a/applications/process-manager/main.qml b/applications/process-manager/main.qml index 96c6ebaf4..765f187af 100755 --- a/applications/process-manager/main.qml +++ b/applications/process-manager/main.qml @@ -19,7 +19,7 @@ ApplicationWindow { repeat: true triggeredOnStart: false running: true - onTriggered: controller.reload(); + onTriggered: controller.reload() } menuBar: ColumnLayout { @@ -35,7 +35,10 @@ ApplicationWindow { Timer { id: quitTimer interval: 1000 - onTriggered: Qt.quit() + onTriggered: { + controller.breadcrumb("back", "click", "ui"); + Qt.quit(); + } } } Item { Layout.fillWidth: true } @@ -58,7 +61,10 @@ ApplicationWindow { rightPadding: 10 leftPadding: 10 Layout.fillWidth: true - MouseArea { anchors.fill: parent; onClicked: controller.sortBy = "name" } + MouseArea { anchors.fill: parent; onClicked: { + controller.breadcrumb("tasksView.name", "sortBy", "ui"); + controller.sortBy = "name"; + } } } Label { id: pid @@ -69,7 +75,10 @@ ApplicationWindow { Layout.alignment: Qt.AlignLeft leftPadding: 20 Layout.preferredWidth: 180 - MouseArea { anchors.fill: parent; onClicked: controller.sortBy = "pid" } + MouseArea { anchors.fill: parent; onClicked: { + controller.breadcrumb("tasksView.pid", "sortBy", "ui"); + controller.sortBy = "pid"; + } } } Label { id: ppid @@ -80,7 +89,10 @@ ApplicationWindow { Layout.alignment: Qt.AlignLeft leftPadding: 20 Layout.preferredWidth: 180 - MouseArea { anchors.fill: parent; onClicked: controller.sortBy = "ppid" } + MouseArea { anchors.fill: parent; onClicked: { + controller.breadcrumb("tasksView.ppid", "sortBy", "ui"); + controller.sortBy = "ppid"; + } } } Label { id: cpu @@ -91,7 +103,10 @@ ApplicationWindow { Layout.alignment: Qt.AlignLeft leftPadding: 20 Layout.preferredWidth: 100 - MouseArea { anchors.fill: parent; onClicked: controller.sortBy = "cpu" } + MouseArea { anchors.fill: parent; onClicked: { + controller.breadcrumb("tasksView.cpu", "sortBy", "ui"); + controller.sortBy = "cpu"; + }} } Label { id: mem @@ -102,7 +117,10 @@ ApplicationWindow { Layout.alignment: Qt.AlignLeft leftPadding: 20 Layout.preferredWidth: 200 - MouseArea { anchors.fill: parent; onClicked: controller.sortBy = "mem" } + MouseArea { anchors.fill: parent; onClicked: { + controller.breadcrumb("tasksView.mem", "sortBy", "ui"); + controller.sortBy = "mem"; + }} } Item { width: scrollbar.width } } @@ -119,6 +137,7 @@ ApplicationWindow { backgroundColor: "white" borderColor: "white" onClicked: { + controller.breadcrumb("tasksView", "scroll.up", "ui"); console.log("Scroll up"); tasksView.currentIndex = tasksView.currentIndex - tasksView.pageSize(); if(tasksView.currentIndex < 0){ @@ -215,7 +234,10 @@ ApplicationWindow { } MouseArea { anchors.fill: parent - onClicked: model.display.killable && killPrompt.open() + onClicked: { + controller.breadcrumb("tasksView.tasksRow", "clicked", "ui"); + model.display.killable && killPrompt.open(); + } } Popup { id: killPrompt @@ -225,8 +247,14 @@ ApplicationWindow { y: Math.round((parent.height - height) / 2) focus: true closePolicy: Popup.CloseOnPressOutsideParent - onClosed: console.log("Closed") - onOpened: console.log("Opened") + onClosed: { + controller.breadcrumb("navigation", "main", "navigation"); + console.log("Closed"); + } + onOpened: { + controller.breadcrumb("navigation", "killPrompt", "navigation"); + console.log("Opened"); + } contentItem: ColumnLayout { Label { text: "Would you like to kill " + model.display.name + " (" + model.display.pid + ")" @@ -239,6 +267,7 @@ ApplicationWindow { MouseArea { anchors.fill: parent onClicked: { + controller.breadcrumb("killPrompt.forceQuit", "clicked", "ui"); model.display.signal(15); killPrompt.close() } @@ -250,6 +279,7 @@ ApplicationWindow { MouseArea { anchors.fill: parent onClicked: { + controller.breadcrumb("killPrompt.yes", "clicked", "ui"); model.display.signal(9); killPrompt.close() } @@ -261,7 +291,10 @@ ApplicationWindow { color: "black" MouseArea { anchors.fill: parent - onClicked: killPrompt.close() + onClicked: { + controller.breadcrumb("killPrompt.no", "clicked", "ui"); + killPrompt.close(); + } } } } @@ -291,6 +324,7 @@ ApplicationWindow { backgroundColor: "white" borderColor: "white" onClicked: { + controller.breadcrumb("tasksView", "scroll.down", "ui"); console.log("Scroll down"); tasksView.currentIndex = tasksView.currentIndex + tasksView.pageSize(); if(tasksView.currentIndex > tasksView.count){ @@ -323,7 +357,10 @@ ApplicationWindow { PropertyAction { target: window.contentItem; property: "visible"; value: false } PropertyAction { target: stateController; property: "state"; value: "loaded" } } - ScriptAction { script: console.log("loading...") } + ScriptAction { script: { + controller.breadcrumb("navigation", "loading", "navigation"); + console.log("loading..."); + } } } }, Transition { @@ -338,7 +375,10 @@ ApplicationWindow { PropertyAction { target: window; property: "visible"; value: true } PropertyAction { target: window.contentItem; property: "visible"; value: true } } - ScriptAction { script: console.log("loaded.") } + ScriptAction { script: { + controller.breadcrumb("navigation", "main", "navigation"); + console.log("loaded."); + } } } } ] diff --git a/applications/process-manager/tasklist.h b/applications/process-manager/tasklist.h index e9817e058..3c2e7de7c 100644 --- a/applications/process-manager/tasklist.h +++ b/applications/process-manager/tasklist.h @@ -171,7 +171,7 @@ class TaskList : public QAbstractListModel } beginRemoveRows(QModelIndex(), taskItems.indexOf(taskItem), taskItems.indexOf(taskItem)); i.remove(); - delete taskItem; + taskItem->deleteLater(); endRemoveRows(); } QDir directory("/proc"); diff --git a/applications/screenshot-tool/fret.pro b/applications/screenshot-tool/fret.pro index 7f2528b1b..e52d25a06 100644 --- a/applications/screenshot-tool/fret.pro +++ b/applications/screenshot-tool/fret.pro @@ -4,9 +4,8 @@ QT += dbus CONFIG += c++11 console CONFIG -= app_bundle -# You can make your code fail to compile if it uses deprecated APIs. -# In order to do so, uncomment the following line. -#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 +DEFINES += QT_DEPRECATED_WARNINGS +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ main.cpp @@ -22,11 +21,26 @@ DBUS_INTERFACES += ../../interfaces/screenshot.xml DBUS_INTERFACES += ../../interfaces/notificationapi.xml DBUS_INTERFACES += ../../interfaces/notification.xml - INCLUDEPATH += ../../shared -HEADERS += \ - ../../shared/dbussettings.h LIBS += -L$$PWD/../../shared/ -lqsgepaper INCLUDEPATH += $$PWD/../../shared DEPENDPATH += $$PWD/../../shared + +exists($$PWD/../../.build/sentry) { + LIBS += -L$$PWD/../../.build/sentry/lib -lsentry -ldl -lcurl -lbreakpad_client + INCLUDEPATH += $$PWD/../../.build/sentry/include + DEPENDPATH += $$PWD/../../.build/sentry/lib + + library.files = ../../.build/sentry/libsentry.so + library.path = /opt/lib + INSTALLS += library +} + +LIBS += -L$$PWD/../../.build/liboxide -lliboxide +INCLUDEPATH += $$PWD/../../shared/liboxide +DEPENDPATH += $$PWD/../../shared/liboxide + +QMAKE_RPATHDIR += /lib /usr/lib /opt/lib /opt/usr/lib + +VERSION = 2.3 diff --git a/applications/screenshot-tool/main.cpp b/applications/screenshot-tool/main.cpp index 46d598168..c0c093500 100644 --- a/applications/screenshot-tool/main.cpp +++ b/applications/screenshot-tool/main.cpp @@ -4,8 +4,7 @@ #include #include - -#include "dbussettings.h" +#include #include "dbusservice_interface.h" #include "systemapi_interface.h" @@ -15,6 +14,7 @@ #include "notification_interface.h" using namespace codes::eeems::oxide1; +using namespace Oxide::Sentry; void unixSignalHandler(int signal){ qDebug() << "Recieved signal" << signal; @@ -43,12 +43,13 @@ void addNotification(Notifications* notifications, QString text, QString icon = } int main(int argc, char *argv[]){ + QCoreApplication app(argc, argv); + sentry_init("fret", argv); atexit(onExit); signal(SIGTERM, unixSignalHandler); signal(SIGSEGV, unixSignalHandler); signal(SIGABRT, unixSignalHandler); signal(SIGSYS, unixSignalHandler); - QCoreApplication app(argc, argv); app.setOrganizationName("Eeems"); app.setOrganizationDomain(OXIDE_SERVICE); app.setApplicationName("fret"); diff --git a/applications/screenshot-viewer/anxiety.pro b/applications/screenshot-viewer/anxiety.pro index fc571ba1c..31a69ef3d 100644 --- a/applications/screenshot-viewer/anxiety.pro +++ b/applications/screenshot-viewer/anxiety.pro @@ -5,24 +5,11 @@ QT += dbus CONFIG += c++11 CONFIG += qml_debug -# The following define makes your compiler emit warnings if you use -# any feature of Qt which as been marked deprecated (the exact warnings -# depend on your compiler). Please consult the documentation of the -# deprecated API in order to know how to port your code away from it. DEFINES += QT_DEPRECATED_WARNINGS - -# You can make your code fail to compile if it uses deprecated APIs. -# In order to do so, uncomment the following line. -#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 - -linux-oe-g++ { - LIBS += -lqsgepaper -} +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ - main.cpp \ - ../../shared/devicesettings.cpp \ - ../../shared/eventfilter.cpp + main.cpp # Default rules for deployment. target.path = /opt/bin @@ -41,9 +28,6 @@ DBUS_INTERFACES += ../../interfaces/screenshot.xml INCLUDEPATH += ../../shared HEADERS += \ - ../../shared/dbussettings.h \ - ../../shared/devicesettings.h \ - ../../shared/eventfilter.h \ ../../shared/epframebuffer.h \ controller.h \ screenshotlist.h @@ -54,3 +38,21 @@ RESOURCES += \ LIBS += -L$$PWD/../../shared/ -lqsgepaper INCLUDEPATH += $$PWD/../../shared DEPENDPATH += $$PWD/../../shared + +exists($$PWD/../../.build/sentry) { + LIBS += -L$$PWD/../../.build/sentry/lib -lsentry -ldl -lcurl -lbreakpad_client + INCLUDEPATH += $$PWD/../../.build/sentry/include + DEPENDPATH += $$PWD/../../.build/sentry/lib + + library.files = ../../.build/sentry/libsentry.so + library.path = /opt/lib + INSTALLS += library +} + +LIBS += -L$$PWD/../../.build/liboxide -lliboxide +INCLUDEPATH += $$PWD/../../shared/liboxide +DEPENDPATH += $$PWD/../../shared/liboxide + +QMAKE_RPATHDIR += /lib /usr/lib /opt/lib /opt/usr/lib + +VERSION = 2.3 diff --git a/applications/screenshot-viewer/controller.h b/applications/screenshot-viewer/controller.h index a54796a36..ebae93f2e 100644 --- a/applications/screenshot-viewer/controller.h +++ b/applications/screenshot-viewer/controller.h @@ -4,9 +4,9 @@ #include #include #include +#include #include "epframebuffer.h" -#include "dbussettings.h" #include "dbusservice_interface.h" #include "screenapi_interface.h" @@ -15,6 +15,7 @@ #include "screenshotlist.h" using namespace codes::eeems::oxide1; +using namespace Oxide::Sentry; #define CORRUPT_SETTINGS_VERSION 1 @@ -71,6 +72,15 @@ class Controller : public QObject { setState("loaded"); }); } + Q_INVOKABLE void breadcrumb(QString category, QString message, QString type = "default"){ +#ifdef SENTRY + sentry_breadcrumb(category.toStdString().c_str(), message.toStdString().c_str(), type.toStdString().c_str()); +#else + Q_UNUSED(category); + Q_UNUSED(message); + Q_UNUSED(type); +#endif + } QString state() { if(!getStateControllerUI()){ return "loading"; diff --git a/applications/screenshot-viewer/main.cpp b/applications/screenshot-viewer/main.cpp index c22b7bc33..d1e77af14 100644 --- a/applications/screenshot-viewer/main.cpp +++ b/applications/screenshot-viewer/main.cpp @@ -5,11 +5,10 @@ #include #include +#include +#include -#include "dbussettings.h" -#include "devicesettings.h" #include "controller.h" -#include "eventfilter.h" #ifdef __arm__ Q_IMPORT_PLUGIN(QsgEpaperPlugin) @@ -18,6 +17,8 @@ Q_IMPORT_PLUGIN(QsgEpaperPlugin) #include "dbusservice_interface.h" using namespace std; +using namespace Oxide; +using namespace Oxide::Sentry; const char* qt_version = qVersion(); @@ -39,6 +40,7 @@ int main(int argc, char *argv[]){ // qputenv("QT_DEBUG_BACKINGSTORE", "1"); #endif QGuiApplication app(argc, argv); + sentry_init("anxiety", argv); auto filter = new EventFilter(&app); app.installEventFilter(filter); app.setOrganizationName("Eeems"); diff --git a/applications/screenshot-viewer/main.qml b/applications/screenshot-viewer/main.qml index 3d271cd8e..f2a00c506 100644 --- a/applications/screenshot-viewer/main.qml +++ b/applications/screenshot-viewer/main.qml @@ -37,6 +37,7 @@ ApplicationWindow { MouseArea { anchors.fill: parent onClicked: { + controller.breadcrumb("back", "click", "ui"); if(stateController.state !== "viewing"){ Qt.quit(); return; @@ -63,6 +64,7 @@ ApplicationWindow { MouseArea { anchors.fill: parent onClicked: { + controller.breadcrumb("menu.delete", "click", "ui"); controller.screenshots.remove(viewer.model.display.path); viewer.model = undefined; stateController.state = "loading"; @@ -77,15 +79,24 @@ ApplicationWindow { width: 310 Action { text: controller.columns === 4 ? "* Small" : "Small" - onTriggered: controller.columns = 4 + onTriggered: { + controller.breadcrumb("menu.small", "click", "ui"); + controller.columns = 4; + } } Action { text: controller.columns === 3 ? "* Medium" : "Medium" - onTriggered: controller.columns = 3 + onTriggered: { + controller.breadcrumb("menu.medium", "click", "ui"); + controller.columns = 3; + } } Action { text: controller.columns === 2 ? "* Large" : "Large" - onTriggered: controller.columns = 2 + onTriggered: { + controller.breadcrumb("menu.large", "click", "ui"); + controller.columns = 2; + } } } } @@ -105,6 +116,7 @@ ApplicationWindow { visible: !screenshots.atYBeginning Layout.fillWidth: true onClicked: { + controller.breadcrumb("screenshots", "scroll.up", "ui"); console.log("Scroll up"); screenshots.currentIndex = screenshots.currentIndex - screenshots.pageSize(); if(screenshots.currentIndex < 0){ @@ -130,13 +142,17 @@ ApplicationWindow { width: screenshots.cellWidth height: screenshots.cellHeight onClicked: { + controller.breadcrumb("screenshots.screenshot", "click", "ui"); if(!screenshots.flicking){ console.log("Opening " + model.display.path); viewer.model = model; stateController.state = "viewing"; } } - onLongPress: !screenshots.flicking && controller.screenshots.remove(model.display.path) + onLongPress: { + controller.breadcrumb("screenshots.screenshot", "longPress", "ui"); + !screenshots.flicking && controller.screenshots.remove(model.display.path); + } } interactive: false boundsBehavior: Flickable.StopAtBounds @@ -172,6 +188,7 @@ ApplicationWindow { Layout.fillWidth: true visible: !screenshots.atYEnd onClicked: { + controller.breadcrumb("screenshots", "scroll.down", "ui"); console.log("Scroll down"); screenshots.currentIndex = screenshots.currentIndex + screenshots.pageSize(); if(screenshots.currentIndex > screenshots.count){ @@ -219,6 +236,7 @@ ApplicationWindow { from: "*"; to: "loaded" SequentialAnimation { ScriptAction { script: { + controller.breadcrumb("navigation", "main", "navigation"); console.log("Display loaded"); } } } @@ -227,6 +245,7 @@ ApplicationWindow { from: "*"; to: "loading" SequentialAnimation { ScriptAction { script: { + controller.breadcrumb("navigation", "loading", "navigation"); console.log("Loading display"); controller.startup(); } } @@ -236,6 +255,7 @@ ApplicationWindow { from: "*"; to: "viewing" SequentialAnimation { ScriptAction { script: { + controller.breadcrumb("navigation", "viewing", "navigation"); console.log("Viewing Image"); } } } diff --git a/applications/settings-manager/json.h b/applications/settings-manager/json.h new file mode 100644 index 000000000..6e74ca809 --- /dev/null +++ b/applications/settings-manager/json.h @@ -0,0 +1,130 @@ +#ifndef JSON_H +#define JSON_H +#include +#include +#include +#include +#include +#include + +QVariant sanitizeForJson(QVariant value); +QVariant decodeDBusArgument(const QDBusArgument& arg){ + auto type = arg.currentType(); + if(type == QDBusArgument::BasicType || type == QDBusArgument::VariantType){ + return sanitizeForJson(arg.asVariant()); + } + if(type == QDBusArgument::ArrayType){ + QVariantList list; + arg.beginArray(); + while(!arg.atEnd()){ + list.append(decodeDBusArgument(arg)); + } + arg.endArray(); + return sanitizeForJson(list); + } + if(type == QDBusArgument::MapType){ + qDebug() << "Map Type"; + QMap map; + arg.beginMap(); + while(!arg.atEnd()){ + arg.beginMapEntry(); + auto key = decodeDBusArgument(arg); + auto value = decodeDBusArgument(arg); + arg.endMapEntry(); + map.insert(sanitizeForJson(key), sanitizeForJson(value)); + } + arg.endMap(); + return sanitizeForJson(QVariant::fromValue(map)); + } + qDebug() << "Unable to sanitize QDBusArgument as it is an unknown type"; + return QVariant(); +} +QVariant sanitizeForJson(QVariant value){ + auto userType = value.userType(); + if(userType == QMetaType::type("QDBusObjectPath")){ + return value.value().path(); + } + if(userType == QMetaType::type("QDBusSignature")){ + return value.value().signature(); + } + if(userType == QMetaType::type("QDBusVariant")){ + return value.value().variant(); + } + if(userType == QMetaType::type("QDBusArgument")){ + return decodeDBusArgument(value.value()); + } + if(userType == QMetaType::type("QList")){ + QVariantList list; + for(auto value : value.value>()){ + list.append(sanitizeForJson(value.variant())); + } + return list; + } + if(userType == QMetaType::type("QList")){ + QStringList list; + for(auto value : value.value>()){ + list.append(value.signature()); + } + return list; + } + if(userType == QMetaType::type("QList")){ + QStringList list; + for(auto value : value.value>()){ + list.append(value.path()); + } + return list; + } + if(userType == QMetaType::QByteArray){ + auto byteArray = value.toByteArray(); + QVariantList list; + for(auto byte : byteArray){ + list.append(byte); + } + return list; + } + if(userType == QMetaType::QVariantMap){ + QVariantMap map; + auto input = value.toMap(); + for(auto key : input.keys()){ + map.insert(key, sanitizeForJson(input[key])); + } + return map; + } + if(userType == QMetaType::QVariantList){ + QVariantList list = value.toList(); + QMutableListIterator i(list); + while(i.hasNext()){ + i.setValue(sanitizeForJson(i.next())); + } + return list; + } + return value; +} +QString toJson(QVariant value){ + if(value.isNull()){ + return "null"; + } + auto jsonVariant = QJsonValue::fromVariant(sanitizeForJson(value)); + if(jsonVariant.isNull()){ + return "null"; + } + if(jsonVariant.isUndefined()){ + return "undefined"; + } + QJsonArray jsonArray; + jsonArray.append(jsonVariant); + QJsonDocument doc(jsonArray); + auto json = doc.toJson(QJsonDocument::Compact); + return json.mid(1, json.length() - 2); +} +QVariant fromJson(QByteArray json){ + QJsonParseError error; + QJsonDocument doc = QJsonDocument::fromJson("[" + json + "]", &error); + if(error.error != QJsonParseError::NoError){ + qDebug() << "Unable to read json value" << error.errorString(); + qDebug() << "Value to parse" << json; + } + return doc.array().first().toVariant(); +} + +#endif // JSON_H diff --git a/applications/settings-manager/main.cpp b/applications/settings-manager/main.cpp index ee40c287b..c64f50ca4 100644 --- a/applications/settings-manager/main.cpp +++ b/applications/settings-manager/main.cpp @@ -4,7 +4,7 @@ #include #include -#include "dbussettings.h" +#include #include "dbusservice_interface.h" #include "powerapi_interface.h" @@ -19,184 +19,22 @@ #include "notificationapi_interface.h" #include "notification_interface.h" -using namespace codes::eeems::oxide1; +#include "json.h" +#include "slothandler.h" -static QTextStream qStdOut(stdout, QIODevice::WriteOnly); +using namespace codes::eeems::oxide1; +using namespace Oxide::Sentry; -QVariant sanitizeForJson(QVariant value); -QVariant decodeDBusArgument(const QDBusArgument& arg){ - auto type = arg.currentType(); - if(type == QDBusArgument::BasicType || type == QDBusArgument::VariantType){ - return sanitizeForJson(arg.asVariant()); - } - if(type == QDBusArgument::ArrayType){ - QVariantList list; - arg.beginArray(); - while(!arg.atEnd()){ - list.append(decodeDBusArgument(arg)); - } - arg.endArray(); - return sanitizeForJson(list); - } - if(type == QDBusArgument::MapType){ - qDebug() << "Map Type"; - QMap map; - arg.beginMap(); - while(!arg.atEnd()){ - arg.beginMapEntry(); - auto key = decodeDBusArgument(arg); - auto value = decodeDBusArgument(arg); - arg.endMapEntry(); - map.insert(sanitizeForJson(key), sanitizeForJson(value)); - } - arg.endMap(); - return sanitizeForJson(QVariant::fromValue(map)); - } - qDebug() << "Unable to sanitize QDBusArgument as it is an unknown type"; - return QVariant(); -} -QVariant sanitizeForJson(QVariant value){ - auto userType = value.userType(); - if(userType == QMetaType::type("QDBusObjectPath")){ - return value.value().path(); - } - if(userType == QMetaType::type("QDBusSignature")){ - return value.value().signature(); - } - if(userType == QMetaType::type("QDBusVariant")){ - return value.value().variant(); - } - if(userType == QMetaType::type("QDBusArgument")){ - return decodeDBusArgument(value.value()); - } - if(userType == QMetaType::type("QList")){ - QVariantList list; - for(auto value : value.value>()){ - list.append(sanitizeForJson(value.variant())); - } - return list; - } - if(userType == QMetaType::type("QList")){ - QStringList list; - for(auto value : value.value>()){ - list.append(value.signature()); - } - return list; - } - if(userType == QMetaType::type("QList")){ - QStringList list; - for(auto value : value.value>()){ - list.append(value.path()); - } - return list; - } - if(userType == QMetaType::QByteArray){ - auto byteArray = value.toByteArray(); - QVariantList list; - for(auto byte : byteArray){ - list.append(byte); - } - return list; - } - if(userType == QMetaType::QVariantMap){ - QVariantMap map; - auto input = value.toMap(); - for(auto key : input.keys()){ - map.insert(key, sanitizeForJson(input[key])); - } - return map; - } - if(userType == QMetaType::QVariantList){ - QVariantList list = value.toList(); - QMutableListIterator i(list); - while(i.hasNext()){ - i.setValue(sanitizeForJson(i.next())); - } - return list; - } - return value; -} -QString toJson(QVariant value){ - if(value.isNull()){ - return "null"; - } - auto jsonVariant = QJsonValue::fromVariant(sanitizeForJson(value)); - if(jsonVariant.isNull()){ - return "null"; - } - if(jsonVariant.isUndefined()){ - return "undefined"; - } - QJsonArray jsonArray; - jsonArray.append(jsonVariant); - QJsonDocument doc(jsonArray); - auto json = doc.toJson(QJsonDocument::Compact); - return json.mid(1, json.length() - 2); -} -QVariant fromJson(QByteArray json){ - QJsonParseError error; - QJsonDocument doc = QJsonDocument::fromJson("[" + json + "]", &error); - if(error.error != QJsonParseError::NoError){ - qDebug() << "Unable to read json value" << error.errorString(); - qDebug() << "Value to parse" << json; - } - return doc.array().first().toVariant(); +int qExit(int ret){ + QTimer::singleShot(0, [ret](){ + qApp->exit(ret); + }); + return qApp->exec(); } -class SlotHandler : public QObject { -public: - SlotHandler(QStringList parameters, bool once) : QObject(0), parameters(parameters), once(once){ - watcher = new QDBusServiceWatcher(OXIDE_SERVICE, QDBusConnection::systemBus(), QDBusServiceWatcher::WatchForUnregistration, this); - QObject::connect(watcher, &QDBusServiceWatcher::serviceUnregistered, this, &SlotHandler::serviceUnregistered); - } - ~SlotHandler() {}; - int qt_metacall(QMetaObject::Call call, int id, void **arguments){ - id = QObject::qt_metacall(call, id, arguments); - if (id < 0 || call != QMetaObject::InvokeMetaMethod) - return id; - Q_ASSERT(id < 1); - - handleSlot(sender(), arguments); - return -1; - } - bool connect(QObject* sender, int methodId){ - return QMetaObject::connect(sender, methodId, this, this->metaObject()->methodCount()); - } -public slots: - void serviceUnregistered(const QString& name){ - Q_UNUSED(name); - qDebug() << QDBusError(QDBusError::ServiceUnknown, "The name " OXIDE_SERVICE " is no longer registered"); - qApp->exit(); - } -private: - QStringList parameters; - bool once; - QDBusServiceWatcher* watcher; - - void handleSlot(QObject* api, void** arguments){ - Q_UNUSED(api); - QVariantList args; - for(int i = 0; i < parameters.length(); i++){ - auto typeId = QMetaType::type(parameters[i].toStdString().c_str()); - QMetaType type(typeId); - void* ptr = reinterpret_cast(arguments[i + 1]); - args << QVariant(typeId, ptr); - } - if(args.size() > 1){ - qStdOut << toJson(args).toStdString().c_str() << Qt::endl; - }else if(args.size() == 1 && !args.first().isNull()){ - qStdOut << toJson(args.first()).toStdString().c_str() << Qt::endl; - }else{ - qStdOut << "undefined" << Qt::endl; - } - if(once){ - qApp->quit(); - } - }; -}; - int main(int argc, char *argv[]){ QCoreApplication app(argc, argv); + sentry_init("rot", argv); app.setOrganizationName("Eeems"); app.setOrganizationDomain(OXIDE_SERVICE); app.setApplicationName("rot"); @@ -206,7 +44,7 @@ int main(int argc, char *argv[]){ parser.addHelpOption(); parser.applicationDescription(); parser.addVersionOption(); - parser.addPositionalArgument("api", "wifi\npower\napps\nsystem\nscreen\nnotification"); + parser.addPositionalArgument("api", "settings\nwifi\npower\napps\nsystem\nscreen\nnotification"); parser.addPositionalArgument("action","get\nset\nlisten\ncall"); QCommandLineOption objectOption( {"o", "object"}, @@ -224,18 +62,27 @@ int main(int argc, char *argv[]){ QStringList args = parser.positionalArguments(); if(args.isEmpty()){ +#ifdef SENTRY + sentry_breadcrumb("error", "No arguments"); +#endif parser.showHelp(EXIT_FAILURE); } auto apiName = args.at(0); - if(!(QSet {"power", "wifi", "apps", "system", "screen", "notification"}).contains(apiName)){ + if(!(QSet {"settings", "power", "wifi", "apps", "system", "screen", "notification"}).contains(apiName)){ qDebug() << "Unknown API" << apiName; - return EXIT_FAILURE; +#ifdef SENTRY + sentry_breadcrumb("error", "Unknown API"); +#endif + return qExit(EXIT_FAILURE); } if(args.length() < 2){ parser.showHelp(EXIT_FAILURE); } auto action = args.at(1); if(action == "get"){ +#ifdef SENTRY + sentry_breadcrumb("action", "get"); +#endif parser.addPositionalArgument("property", "Property to get."); parser.parse(app.arguments()); args = parser.positionalArguments(); @@ -243,59 +90,130 @@ int main(int argc, char *argv[]){ parser.showHelp(EXIT_FAILURE); } }else if(action == "set"){ +#ifdef SENTRY + sentry_breadcrumb("action", "set"); +#endif parser.addPositionalArgument("property", "Property to change."); parser.addPositionalArgument("value", "Value to set the property to."); parser.parse(app.arguments()); args = parser.positionalArguments(); if(args.size() < 4){ +#ifdef SENTRY + sentry_breadcrumb("action", "Missing arguments"); +#endif parser.showHelp(EXIT_FAILURE); } }else if(action == "listen"){ +#ifdef SENTRY + sentry_breadcrumb("action", "listen"); +#endif parser.addPositionalArgument("signal", "Signal to listen to."); parser.parse(app.arguments()); args = parser.positionalArguments(); if(args.size() < 3){ +#ifdef SENTRY + sentry_breadcrumb("action", "Missing arguments"); +#endif parser.showHelp(EXIT_FAILURE); } }else if(action == "call"){ +#ifdef SENTRY + sentry_breadcrumb("action", "call"); +#endif parser.addPositionalArgument("method", "Method to call"); parser.addPositionalArgument("arguments", "Arguments to pass to the method using the following format: :. e.g. QString:Test", "[arguments...]"); parser.parse(app.arguments()); args = parser.positionalArguments(); if(args.size() < 3){ +#ifdef SENTRY + sentry_breadcrumb("action", "Missing arguments"); +#endif parser.showHelp(EXIT_FAILURE); } }else{ +#ifdef SENTRY + sentry_breadcrumb("action", "unknown"); +#endif parser.showHelp(EXIT_FAILURE); } auto bus = QDBusConnection::systemBus(); if(!bus.isConnected()){ qDebug() << "Not able to connect to dbus"; - return EXIT_FAILURE; - } - General generalApi(OXIDE_SERVICE, OXIDE_SERVICE_PATH, bus); - auto reply = generalApi.requestAPI(apiName); - reply.waitForFinished(); - if(reply.isError()){ - qDebug() << reply.error(); - return EXIT_FAILURE; +#ifdef SENTRY + sentry_breadcrumb("error", "Unable to connect to dbus"); +#endif + return qExit(EXIT_FAILURE); } - auto path = ((QDBusObjectPath)reply).path(); - if(path == "/"){ - qDebug() << "API not available"; - return EXIT_FAILURE; + QString path = ""; + if(apiName != "settings"){ + General generalApi(OXIDE_SERVICE, OXIDE_SERVICE_PATH, bus); + auto reply = generalApi.requestAPI(apiName); + reply.waitForFinished(); + if(reply.isError()){ + qDebug() << reply.error(); +#ifdef SENTRY + sentry_breadcrumb("error", "Unable to request API"); +#endif + return qExit(EXIT_FAILURE); + } + path = ((QDBusObjectPath)reply).path(); + if(path == "/"){ + qDebug() << "API not available"; +#ifdef SENTRY + sentry_breadcrumb("error", "API not available"); +#endif + return qExit(EXIT_FAILURE); + } } - QDBusAbstractInterface* api; - if(apiName == "power"){ + QObject* api; + if(apiName == "settings"){ + if(action == "call"){ + if(args.length() == 3 && args.at(2) == "crash"){ + trigger_crash(); + }else if(args.length() == 3 && args.at(2) == "transaction"){ + sentry_transaction("settings", "transaction", [](Transaction* t){ + sentry_span(t, "span", "Transaction span", []{ + qDebug() << "Triggered transaction"; + }); + }); + return qExit(EXIT_SUCCESS); + } + qDebug() << "Call is not valid for the settings API"; +#ifdef SENTRY + sentry_breadcrumb("error", "invalid arguments"); +#endif + return qExit(EXIT_FAILURE); + } + if(parser.isSet("object")){ + qDebug() << "Paths are not valid for the settings API"; +#ifdef SENTRY + sentry_breadcrumb("error", "invalid arguments"); +#endif + return qExit(EXIT_FAILURE); + } + api = &sharedSettings; + }else if(apiName == "power"){ +#ifdef SENTRY + sentry_breadcrumb("api", "power"); +#endif api = new Power(OXIDE_SERVICE, path, bus); if(parser.isSet("object")){ qDebug() << "Paths are not valid for the power API"; - return EXIT_FAILURE; +#ifdef SENTRY + sentry_breadcrumb("error", "invalid arguments"); +#endif + return qExit(EXIT_FAILURE); } }else if(apiName == "wifi"){ +#ifdef SENTRY + sentry_breadcrumb("api", "wifi"); +#endif api = new Wifi(OXIDE_SERVICE, path, bus); if(parser.isSet("object")){ auto object = parser.value("object"); +#ifdef SENTRY + sentry_breadcrumb("object", object.toStdString().c_str()); +#endif auto type = object.mid(0, object.indexOf(":")); auto path = object.mid(object.indexOf(":") + 1); path = OXIDE_SERVICE_PATH + QString("/" + path); @@ -305,13 +223,22 @@ int main(int argc, char *argv[]){ api = new BSS(OXIDE_SERVICE, path, bus); }else{ qDebug() << "Unknown object type" << type; - return EXIT_FAILURE; +#ifdef SENTRY + sentry_breadcrumb("error", "Unknown object type"); +#endif + return qExit(EXIT_FAILURE); } } }else if(apiName == "apps"){ +#ifdef SENTRY + sentry_breadcrumb("api", "apps"); +#endif api = new Apps(OXIDE_SERVICE, path, bus); if(parser.isSet("object")){ auto object = parser.value("object"); +#ifdef SENTRY + sentry_breadcrumb("object", object.toStdString().c_str()); +#endif auto type = object.mid(0, object.indexOf(":")); auto path = object.mid(object.indexOf(":") + 1); path = OXIDE_SERVICE_PATH + QString("/" + path); @@ -319,19 +246,34 @@ int main(int argc, char *argv[]){ api = new Application(OXIDE_SERVICE, path, bus); }else{ qDebug() << "Unknown object type" << type; - return EXIT_FAILURE; +#ifdef SENTRY + sentry_breadcrumb("error", "Unknown object type"); +#endif + return qExit(EXIT_FAILURE); } } }else if(apiName == "system"){ +#ifdef SENTRY + sentry_breadcrumb("api", "system"); +#endif api = new System(OXIDE_SERVICE, path, bus); if(parser.isSet("object")){ qDebug() << "Paths are not valid for the system API"; - return EXIT_FAILURE; +#ifdef SENTRY + sentry_breadcrumb("error", "invalid arguments"); +#endif + return qExit(EXIT_FAILURE); } }else if(apiName == "screen"){ +#ifdef SENTRY + sentry_breadcrumb("api", "screen"); +#endif api = new Screen(OXIDE_SERVICE, path, bus); if(parser.isSet("object")){ auto object = parser.value("object"); +#ifdef SENTRY + sentry_breadcrumb("object", object.toStdString().c_str()); +#endif auto type = object.mid(0, object.indexOf(":")); auto path = object.mid(object.indexOf(":") + 1); path = OXIDE_SERVICE_PATH + QString("/" + path); @@ -339,13 +281,22 @@ int main(int argc, char *argv[]){ api = new Screenshot(OXIDE_SERVICE, path, bus); }else{ qDebug() << "Unknown object type" << type; - return EXIT_FAILURE; +#ifdef SENTRY + sentry_breadcrumb("error", "invalid arguments"); +#endif + return qExit(EXIT_FAILURE); } } }else if(apiName == "notification"){ +#ifdef SENTRY + sentry_breadcrumb("api", "notification"); +#endif api = new Notifications(OXIDE_SERVICE, path, bus); if(parser.isSet("object")){ auto object = parser.value("object"); +#ifdef SENTRY + sentry_breadcrumb("object", object.toStdString().c_str()); +#endif auto type = object.mid(0, object.indexOf(":")); auto path = object.mid(object.indexOf(":") + 1); path = OXIDE_SERVICE_PATH + QString("/" + path); @@ -353,26 +304,44 @@ int main(int argc, char *argv[]){ api = new Notification(OXIDE_SERVICE, path, bus); }else{ qDebug() << "Unknown object type" << type; - return EXIT_FAILURE; +#ifdef SENTRY + sentry_breadcrumb("error", "Unknown object type"); +#endif + return qExit(EXIT_FAILURE); } } }else{ qDebug() << "API not initialized? Please log a bug."; - return EXIT_FAILURE; +#ifdef SENTRY + sentry_breadcrumb("error", "Unknown API"); +#endif + return qExit(EXIT_FAILURE); } + QDBusAbstractInterface* iapi = qobject_cast(api); if(action == "get"){ auto value = api->property(args.at(2).toStdString().c_str()); - auto error = api->lastError(); - if(error.type() != QDBusError::NoError){ - qDebug() << "Failed to get value" << api->lastError(); - return EXIT_FAILURE; + if(iapi != nullptr){ + auto error = iapi->lastError(); + if(error.type() != QDBusError::NoError){ + qDebug() << "Failed to get value" << iapi->lastError(); +#ifdef SENTRY + sentry_breadcrumb("error", "Failed to get value"); +#endif + return qExit(EXIT_FAILURE); + } } - qStdOut << toJson(value).toStdString().c_str() << Qt::endl; + QTextStream(stdout, QIODevice::WriteOnly) << toJson(value).toStdString().c_str() << Qt::endl; }else if(action == "set"){ auto property = args.at(2).toStdString(); if(!api->setProperty(property.c_str(), args.at(3).toStdString().c_str())){ - qDebug() << "Failed to set value" << api->lastError(); - return EXIT_FAILURE; + qDebug() << "Failed to set value"; + if(iapi != nullptr){ + qDebug() << iapi->lastError(); + } +#ifdef SENTRY + sentry_breadcrumb("error", "Failed to get value"); +#endif + return qExit(EXIT_FAILURE); } }else if(action == "listen"){ auto metaObject = api->metaObject(); @@ -391,14 +360,19 @@ int main(int argc, char *argv[]){ if(!QMetaObject::checkConnectArgs(theSignal, theSlot)){ continue; } - auto slotHandler = new SlotHandler(parameters, parser.isSet("once")); + auto slotHandler = new SlotHandler(OXIDE_SERVICE, parameters, parser.isSet("once"), [=](){ + qApp->exit(EXIT_SUCCESS); + }); if(slotHandler->connect(api, methodId)){ return app.exec(); } } } - qDebug() << "Unable to listen to signal" << bus.interface()->lastError(); - return EXIT_FAILURE; + qDebug() << "Unable to listen to signal"; + if(iapi != nullptr){ + qDebug() << bus.interface()->lastError(); + } + return qExit(EXIT_FAILURE); }else if(action == "call"){ auto method = args.at(2); args = args.mid(3); @@ -407,14 +381,20 @@ int main(int argc, char *argv[]){ for(auto arg : args){ if(!arg.contains(":")){ qDebug() << "Arguments must be in the following format: :"; - return EXIT_FAILURE; +#ifdef SENTRY + sentry_breadcrumb("error", "Argument has bad format"); +#endif + return qExit(EXIT_FAILURE); } auto type = arg.mid(0, arg.indexOf(":")); auto value = arg.mid(arg.indexOf(":") + 1); int id = QMetaType::type(type.toStdString().c_str()); if(id == QMetaType::UnknownType){ qDebug() << "Unknown type" << type; - return EXIT_FAILURE; +#ifdef SENTRY + sentry_breadcrumb("error", "Unknown type"); +#endif + return qExit(EXIT_FAILURE); } QVariant variant = fromJson(value.toUtf8()); if(type == "QDBusObjectPath"){ @@ -425,30 +405,47 @@ int main(int argc, char *argv[]){ if(!variant.canConvert(id)){ qDebug() << "Unable to convert to type" << type; qDebug() << "Value" << variant; - return EXIT_FAILURE; +#ifdef SENTRY + sentry_breadcrumb("error", "Unable to convert to type"); +#endif + return qExit(EXIT_FAILURE); } variant.convert(id); if(!variant.isValid()){ qDebug() << "Failed converting to " << type; qDebug() << "Value" << variant; - return EXIT_FAILURE; +#ifdef SENTRY + sentry_breadcrumb("error", "Failed converting"); +#endif + return qExit(EXIT_FAILURE); } arguments.append(variant); } } - QDBusMessage reply = api->callWithArgumentList(QDBus::Block, method, arguments); + if(iapi == nullptr){ +#ifdef SENTRY + sentry_breadcrumb("error", "Cannot handle calls for non-dbus APIs"); +#endif + qDebug() << "Cannot handle calls for non-dbus APIs"; + return qExit(EXIT_SUCCESS); + } + QDBusMessage reply = iapi->callWithArgumentList(QDBus::Block, method, arguments); auto result = reply.arguments(); + QTextStream qStdOut(stdout, QIODevice::WriteOnly); if(result.size() > 1){ qStdOut << toJson(result).toStdString().c_str() << Qt::endl; }else if(!result.first().isNull()){ qStdOut << toJson(result.first()).toStdString().c_str() << Qt::endl; } if(!reply.errorName().isEmpty()){ - return EXIT_FAILURE; +#ifdef SENTRY + sentry_breadcrumb("error", reply.errorMessage().toStdString().c_str()); +#endif + return qExit(EXIT_SUCCESS); } if(apiName == "system" && (method == "inhibitSleep" || method == "inhibitPowerOff")){ return app.exec(); } } - return EXIT_SUCCESS; + return qExit(EXIT_SUCCESS); } diff --git a/applications/settings-manager/rot.pro b/applications/settings-manager/rot.pro index 8071fb23b..f9f4aae44 100644 --- a/applications/settings-manager/rot.pro +++ b/applications/settings-manager/rot.pro @@ -4,15 +4,7 @@ QT += dbus CONFIG += c++11 console CONFIG -= app_bundle -# The following define makes your compiler emit warnings if you use -# any Qt feature that has been marked deprecated (the exact warnings -# depend on your compiler). Please consult the documentation of the -# deprecated API in order to know how to port your code away from it. DEFINES += QT_DEPRECATED_WARNINGS - -# You can also make your code fail to compile if it uses deprecated APIs. -# In order to do so, uncomment the following line. -# You can also select to disable deprecated APIs only up to a certain version of Qt. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ @@ -37,5 +29,24 @@ DBUS_INTERFACES += ../../interfaces/notification.xml target.path = /opt/bin !isEmpty(target.path): INSTALLS += target +exists($$PWD/../../.build/sentry) { + LIBS += -L$$PWD/../../.build/sentry/lib -lsentry -ldl -lcurl -lbreakpad_client + INCLUDEPATH += $$PWD/../../.build/sentry/include + DEPENDPATH += $$PWD/../../.build/sentry/lib + + library.files = ../../.build/sentry/libsentry.so + library.path = /opt/lib + INSTALLS += library +} + +LIBS += -L$$PWD/../../.build/liboxide -lliboxide +INCLUDEPATH += $$PWD/../../shared/liboxide +DEPENDPATH += $$PWD/../../shared/liboxide + +QMAKE_RPATHDIR += /lib /usr/lib /opt/lib /opt/usr/lib + HEADERS += \ - ../../shared/dbussettings.h + json.h \ + slothandler.h + +VERSION = 2.3 diff --git a/applications/settings-manager/slothandler.h b/applications/settings-manager/slothandler.h new file mode 100644 index 000000000..9dafff1ae --- /dev/null +++ b/applications/settings-manager/slothandler.h @@ -0,0 +1,80 @@ +#ifndef SLOTHANDLER_H +#define SLOTHANDLER_H +#include +#include +#include +#include +#include + +#include "json.h" + +class SlotHandler : public QObject { +public: + SlotHandler(const QString& serviceName, QStringList parameters, bool once, std::function callback) + : QObject(0), + serviceName(serviceName), + parameters(parameters), + once(once), + m_disconnected(false), + qStdOut(stdout, QIODevice::WriteOnly), + callback(callback) + { + watcher = new QDBusServiceWatcher(serviceName, QDBusConnection::systemBus(), QDBusServiceWatcher::WatchForUnregistration, this); + QObject::connect(watcher, &QDBusServiceWatcher::serviceUnregistered, this, [=](const QString& name){ + Q_UNUSED(name); + if(!m_disconnected){ + qDebug() << QDBusError(QDBusError::ServiceUnknown, "The name " + serviceName + " is no longer registered"); + m_disconnected = true; + callback(); + } + }); + } + ~SlotHandler() {}; + int qt_metacall(QMetaObject::Call call, int id, void **arguments){ + id = QObject::qt_metacall(call, id, arguments); + if (id < 0 || call != QMetaObject::InvokeMetaMethod){ + return id; + } + Q_ASSERT(id < 1); + if(!m_disconnected){ + handleSlot(sender(), arguments); + } + return -1; + } + bool connect(QObject* sender, int methodId){ + return QMetaObject::connect(sender, methodId, this, this->metaObject()->methodCount()); + } + +private: + QString serviceName; + QStringList parameters; + bool once; + bool m_disconnected; + QDBusServiceWatcher* watcher; + QTextStream qStdOut; + std::function callback; + + void handleSlot(QObject* api, void** arguments){ + Q_UNUSED(api); + QVariantList args; + for(int i = 0; i < parameters.length(); i++){ + auto typeId = QMetaType::type(parameters[i].toStdString().c_str()); + QMetaType type(typeId); + void* ptr = reinterpret_cast(arguments[i + 1]); + args << QVariant(typeId, ptr); + } + if(args.size() > 1){ + qStdOut << toJson(args).toStdString().c_str() << Qt::endl; + }else if(args.size() == 1 && !args.first().isNull()){ + qStdOut << toJson(args.first()).toStdString().c_str() << Qt::endl; + }else{ + qStdOut << "undefined" << Qt::endl; + } + if(once){ + m_disconnected = true; + callback(); + } + } +}; + +#endif // SLOTHANDLER_H diff --git a/applications/system-service/apibase.h b/applications/system-service/apibase.h index a479ff7e6..1f74e0e51 100644 --- a/applications/system-service/apibase.h +++ b/applications/system-service/apibase.h @@ -9,7 +9,7 @@ #include -#include "dbussettings.h" +#include "../../shared/liboxide/liboxide.h" class APIBase : public QObject, protected QDBusContext { Q_OBJECT diff --git a/applications/system-service/application.cpp b/applications/system-service/application.cpp index 0354136ad..751670b1b 100644 --- a/applications/system-service/application.cpp +++ b/applications/system-service/application.cpp @@ -2,13 +2,13 @@ #include #include +#include #include "application.h" #include "appsapi.h" #include "systemapi.h" #include "buttonhandler.h" #include "digitizerhandler.h" -#include "devicesettings.h" const event_device touchScreen(deviceSettings.getTouchDevicePath(), O_WRONLY); @@ -22,28 +22,51 @@ void Application::launchNoSecurityCheck(){ if(m_process->processId()){ resumeNoSecurityCheck(); }else{ - appsAPI->recordPreviousApplication(); - qDebug() << "Launching " << path(); - appsAPI->pauseAll(); - if(!flags().contains("nosplash")){ - showSplashScreen(); - } - if(m_process->program() != bin()){ - m_process->setProgram(bin()); - } - updateEnvironment(); - umountAll(); - if(chroot()){ - mountAll(); - m_process->setChroot(chrootPath()); - }else{ - m_process->setChroot(""); + if(sharedSettings.applicationUsage()){ + transaction = Oxide::Sentry::start_transaction("application", "run"); +#ifdef SENTRY + if(transaction != nullptr){ + sentry_transaction_set_tag(transaction->inner, "application", name().toStdString().c_str()); + } +#endif + startSpan("starting", "Application is starting"); } - m_process->setWorkingDirectory(workingDirectory()); - m_process->setUser(user()); - m_process->setGroup(group()); - m_process->start(); - m_process->waitForStarted(); + Oxide::Sentry::sentry_transaction("application", "launch", [this](Oxide::Sentry::Transaction* t){ +#ifdef SENTRY + if(t != nullptr){ + sentry_transaction_set_tag(t->inner, "application", name().toStdString().c_str()); + } +#else + Q_UNUSED(t); +#endif + appsAPI->recordPreviousApplication(); + qDebug() << "Launching " << path(); + appsAPI->pauseAll(); + if(!flags().contains("nosplash")){ + showSplashScreen(); + } + if(m_process->program() != bin()){ + m_process->setProgram(bin()); + } + updateEnvironment(); + umountAll(); + if(chroot()){ + mountAll(); + m_process->setChroot(chrootPath()); + }else{ + m_process->setChroot(""); + } + m_process->setWorkingDirectory(workingDirectory()); + m_process->setUser(user()); + m_process->setGroup(group()); + m_process->start(); + m_process->waitForStarted(); + if(type() == AppsAPI::Background){ + startSpan("background", "Application is in the background"); + }else{ + startSpan("foreground", "Application is in the foreground"); + } + }); } } void Application::pause(bool startIfNone){ @@ -61,15 +84,24 @@ void Application::pauseNoSecurityCheck(bool startIfNone){ return; } qDebug() << "Pausing " << path(); - interruptApplication(); - if(!flags().contains("nosavescreen")){ - saveScreen(); - } - if(startIfNone){ - appsAPI->resumeIfNone(); - } - emit paused(); - emit appsAPI->applicationPaused(qPath()); + Oxide::Sentry::sentry_transaction("application", "pause", [this, startIfNone](Oxide::Sentry::Transaction* t){ +#ifdef SENTRY + if(t != nullptr){ + sentry_transaction_set_tag(t->inner, "application", name().toStdString().c_str()); + } +#else + Q_UNUSED(t); +#endif + interruptApplication(); + if(!flags().contains("nosavescreen")){ + saveScreen(); + } + if(startIfNone){ + appsAPI->resumeIfNone(); + } + emit paused(); + emit appsAPI->applicationPaused(qPath()); + }); qDebug() << "Paused " << path(); } void Application::interruptApplication(){ @@ -80,36 +112,53 @@ void Application::interruptApplication(){ ){ return; } - if(!onPause().isEmpty()){ - system(onPause().toStdString().c_str()); - } - switch(type()){ - case AppsAPI::Background: - // Already in the background. How did we get here? - return; - case AppsAPI::Backgroundable: - qDebug() << "Waiting for SIGUSR2 ack"; - appsAPI->connectSignals(this, 2); - kill(-m_process->processId(), SIGUSR2); - timer.restart(); - delayUpTo(1000); - appsAPI->disconnectSignals(this, 2); - if(stateNoSecurityCheck() == Inactive){ - qDebug() << "Application crashed while pausing"; - }else if(timer.isValid()){ - qDebug() << "Application took too long to background" << name(); - kill(-m_process->processId(), SIGSTOP); - waitForPause(); - }else{ - m_backgrounded = true; - qDebug() << "SIGUSR2 ack recieved"; + Oxide::Sentry::sentry_transaction("application", "interrupt", [this](Oxide::Sentry::Transaction* t){ +#ifdef SENTRY + if(t != nullptr){ + sentry_transaction_set_tag(t->inner, "application", name().toStdString().c_str()); + } +#else + Q_UNUSED(t); +#endif + if(!onPause().isEmpty()){ + Oxide::Sentry::sentry_span(t, "onPause", "Run onPause action", [this](){ + system(onPause().toStdString().c_str()); + }); + } + Oxide::Sentry::sentry_span(t, "background", "Background application", [this](){ + switch(type()){ + case AppsAPI::Background: + // Already in the background. How did we get here? + startSpan("background", "Application is in the background"); + return; + case AppsAPI::Backgroundable: + qDebug() << "Waiting for SIGUSR2 ack"; + appsAPI->connectSignals(this, 2); + kill(-m_process->processId(), SIGUSR2); + timer.restart(); + delayUpTo(1000); + appsAPI->disconnectSignals(this, 2); + if(stateNoSecurityCheck() == Inactive){ + qDebug() << "Application crashed while pausing"; + }else if(timer.isValid()){ + qDebug() << "Application took too long to background" << name(); + kill(-m_process->processId(), SIGSTOP); + waitForPause(); + startSpan("stopped", "Application is stopped"); + }else{ + m_backgrounded = true; + qDebug() << "SIGUSR2 ack recieved"; + startSpan("background", "Application is in the background"); + } + break; + case AppsAPI::Foreground: + default: + kill(-m_process->processId(), SIGSTOP); + waitForPause(); + startSpan("stopped", "Application is stopped"); } - break; - case AppsAPI::Foreground: - default: - kill(-m_process->processId(), SIGSTOP); - waitForPause(); - } + }); + }); } void Application::waitForPause(){ if(stateNoSecurityCheck() == Paused){ @@ -140,16 +189,25 @@ void Application::resumeNoSecurityCheck(){ qDebug() << "Can't Resume" << path() << "Already running!"; return; } - appsAPI->recordPreviousApplication(); - qDebug() << "Resuming " << path(); - appsAPI->pauseAll(); - if(!flags().contains("nosavescreen") && (type() != AppsAPI::Backgroundable || stateNoSecurityCheck() == Paused)){ - recallScreen(); - } - uninterruptApplication(); - waitForResume(); - emit resumed(); - emit appsAPI->applicationResumed(qPath()); + Oxide::Sentry::sentry_transaction("application", "resume", [this](Oxide::Sentry::Transaction* t){ +#ifdef SENTRY + if(t != nullptr){ + sentry_transaction_set_tag(t->inner, "application", name().toStdString().c_str()); + } +#else + Q_UNUSED(t); +#endif + appsAPI->recordPreviousApplication(); + qDebug() << "Resuming " << path(); + appsAPI->pauseAll(); + if(!flags().contains("nosavescreen") && (type() != AppsAPI::Backgroundable || stateNoSecurityCheck() == Paused)){ + recallScreen(); + } + uninterruptApplication(); + waitForResume(); + emit resumed(); + emit appsAPI->applicationResumed(qPath()); + }); qDebug() << "Resumed " << path(); } void Application::uninterruptApplication(){ @@ -160,34 +218,49 @@ void Application::uninterruptApplication(){ ){ return; } - if(!onResume().isEmpty()){ - system(onResume().toStdString().c_str()); - } - switch(type()){ - case AppsAPI::Background: - case AppsAPI::Backgroundable: - if(stateNoSecurityCheck() == Paused){ - touchHandler->clear_buffer(); - kill(-m_process->processId(), SIGCONT); - } - qDebug() << "Waiting for SIGUSR1 ack"; - appsAPI->connectSignals(this, 1); - kill(-m_process->processId(), SIGUSR1); - delayUpTo(1000); - appsAPI->disconnectSignals(this, 1); - if(timer.isValid()){ - // No need to fall through, we've just assumed it continued - qDebug() << "Warning: application took too long to forground" << name(); - }else{ - qDebug() << "SIGUSR1 ack recieved"; + Oxide::Sentry::sentry_transaction("application", "uninterrupt", [this](Oxide::Sentry::Transaction* t){ +#ifdef SENTRY + if(t != nullptr){ + sentry_transaction_set_tag(t->inner, "application", name().toStdString().c_str()); + } +#else + Q_UNUSED(t); +#endif + if(!onResume().isEmpty()){ + Oxide::Sentry::sentry_span(t, "onResume", "Run onResume action", [this](){ + system(onResume().toStdString().c_str()); + }); + } + Oxide::Sentry::sentry_span(t, "foreground", "Foreground application", [this](){ + switch(type()){ + case AppsAPI::Background: + case AppsAPI::Backgroundable: + if(stateNoSecurityCheck() == Paused){ + touchHandler->clear_buffer(); + kill(-m_process->processId(), SIGCONT); + } + qDebug() << "Waiting for SIGUSR1 ack"; + appsAPI->connectSignals(this, 1); + kill(-m_process->processId(), SIGUSR1); + delayUpTo(1000); + appsAPI->disconnectSignals(this, 1); + if(timer.isValid()){ + // No need to fall through, we've just assumed it continued + qDebug() << "Warning: application took too long to forground" << name(); + }else{ + qDebug() << "SIGUSR1 ack recieved"; + } + m_backgrounded = false; + startSpan("background", "Application is in the background"); + break; + case AppsAPI::Foreground: + default: + touchHandler->clear_buffer(); + kill(-m_process->processId(), SIGCONT); + startSpan("foreground", "Application is in the foreground"); } - m_backgrounded = false; - break; - case AppsAPI::Foreground: - default: - touchHandler->clear_buffer(); - kill(-m_process->processId(), SIGCONT); - } + }); + }); } void Application::stop(){ if(!hasPermission("apps")){ @@ -200,37 +273,53 @@ void Application::stopNoSecurityCheck(){ if(state == Inactive){ return; } - qDebug() << "Stopping " << path(); - if(!onStop().isEmpty()){ - QProcess::execute(onStop(), QStringList()); - } - Application* pausedApplication = nullptr; - if(state == Paused){ - touchHandler->clear_buffer(); - auto currentApplication = appsAPI->currentApplicationNoSecurityCheck(); - if(currentApplication.path() != path()){ - pausedApplication = appsAPI->getApplication(currentApplication); - if(pausedApplication != nullptr){ - if(pausedApplication->stateNoSecurityCheck() == Paused){ - pausedApplication = nullptr; - }else{ - appsAPI->forceRecordPreviousApplication(); - pausedApplication->interruptApplication(); - } - } + Oxide::Sentry::sentry_transaction("application", "stop", [this, state](Oxide::Sentry::Transaction* t){ +#ifdef SENTRY + if(t != nullptr){ + sentry_transaction_set_tag(t->inner, "application", name().toStdString().c_str()); } - kill(-m_process->processId(), SIGCONT); - } - kill(-m_process->processId(), SIGTERM); - // Try to wait for the application to stop normally before killing it - int tries = 0; - while(this->stateNoSecurityCheck() != Inactive){ - m_process->waitForFinished(100); - if(++tries == 5){ - kill(-m_process->processId(), SIGKILL); - break; +#else + Q_UNUSED(t); +#endif + qDebug() << "Stopping " << path(); + if(!onStop().isEmpty()){ + Oxide::Sentry::sentry_span(t, "onStop", "Run onStop action", [this](){ + QProcess::execute(onStop(), QStringList()); + }); } - } + Application* pausedApplication = nullptr; + if(state == Paused){ + Oxide::Sentry::sentry_span(t, "resume", "Resume paused application", [this, &pausedApplication](){ + touchHandler->clear_buffer(); + auto currentApplication = appsAPI->currentApplicationNoSecurityCheck(); + if(currentApplication.path() != path()){ + pausedApplication = appsAPI->getApplication(currentApplication); + if(pausedApplication != nullptr){ + if(pausedApplication->stateNoSecurityCheck() == Paused){ + pausedApplication = nullptr; + }else{ + appsAPI->forceRecordPreviousApplication(); + pausedApplication->interruptApplication(); + } + } + } + kill(-m_process->processId(), SIGCONT); + }); + } + Oxide::Sentry::sentry_span(t, "stop", "Stop application", [this](){ + kill(-m_process->processId(), SIGTERM); + // Try to wait for the application to stop normally before killing it + int tries = 0; + while(this->stateNoSecurityCheck() != Inactive){ + m_process->waitForFinished(100); + if(++tries == 5){ + kill(-m_process->processId(), SIGKILL); + break; + } + } + }); + appsAPI->removeFromPreviousApplications(name()); + }); } void Application::signal(int signal){ if(m_process->processId()){ @@ -322,50 +411,65 @@ bool Application::hasPermission(QString permission, const char* sender){ return void Application::showSplashScreen(){ auto frameBuffer = EPFrameBuffer::framebuffer(); qDebug() << "Waiting for other painting to finish..."; - while(frameBuffer->paintingActive()){ - EPFrameBuffer::waitForLastUpdate(); - } - qDebug() << "Displaying splashscreen for" << name(); - QPainter painter(frameBuffer); - auto fm = painter.fontMetrics(); - auto size = frameBuffer->size(); - painter.fillRect(frameBuffer->rect(), Qt::white); - QString splashPath = splash(); - if(splashPath.isEmpty() || !QFile::exists(splashPath)){ - splashPath = icon(); - } - if(!splashPath.isEmpty() && QFile::exists(splashPath)){ - qDebug() << "Using image" << splashPath; - int splashWidth = size.width() / 2; - QSize splashSize(splashWidth, splashWidth); - QImage splash = QImage(splashPath).scaled(splashSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); - QRect splashRect( - QPoint( - (size.width() / 2) - (splashWidth / 2), - (size.height() / 2) - (splashWidth / 2) - ), - splashSize - ); - painter.drawImage(splashRect, splash, splash.rect()); - EPFrameBuffer::sendUpdate(frameBuffer->rect(), EPFrameBuffer::HighQualityGrayscale, EPFrameBuffer::FullUpdate, true); - } - painter.setPen(Qt::black); - auto text = "Loading " + displayName() + "..."; - int padding = 10; - int textHeight = fm.height() + padding; - QRect textRect( - QPoint(0 + padding, size.height() - textHeight), - QSize(size.width() - padding * 2, textHeight) - ); - painter.drawText( - textRect, - Qt::AlignVCenter | Qt::AlignRight, - text - ); - EPFrameBuffer::sendUpdate(textRect, EPFrameBuffer::Grayscale, EPFrameBuffer::PartialUpdate, true); - painter.end(); - qDebug() << "Waitng for screen to finish..."; - EPFrameBuffer::waitForLastUpdate(); + Oxide::Sentry::sentry_transaction("application", "showSplashScreen", [this, frameBuffer](Oxide::Sentry::Transaction* t){ +#ifdef SENTRY + if(t != nullptr){ + sentry_transaction_set_tag(t->inner, "application", name().toStdString().c_str()); + } +#else + Q_UNUSED(t); +#endif + Oxide::Sentry::sentry_span(t, "wait", "Wait for screen to be ready", [frameBuffer](){ + while(frameBuffer->paintingActive()){ + EPFrameBuffer::waitForLastUpdate(); + } + }); + qDebug() << "Displaying splashscreen for" << name(); + Oxide::Sentry::sentry_span(t, "paint", "Draw splash screen", [this, frameBuffer](){ + QPainter painter(frameBuffer); + auto fm = painter.fontMetrics(); + auto size = frameBuffer->size(); + painter.fillRect(frameBuffer->rect(), Qt::white); + QString splashPath = splash(); + if(splashPath.isEmpty() || !QFile::exists(splashPath)){ + splashPath = icon(); + } + if(!splashPath.isEmpty() && QFile::exists(splashPath)){ + qDebug() << "Using image" << splashPath; + int splashWidth = size.width() / 2; + QSize splashSize(splashWidth, splashWidth); + QImage splash = QImage(splashPath).scaled(splashSize, Qt::KeepAspectRatio, Qt::SmoothTransformation); + QRect splashRect( + QPoint( + (size.width() / 2) - (splashWidth / 2), + (size.height() / 2) - (splashWidth / 2) + ), + splashSize + ); + painter.drawImage(splashRect, splash, splash.rect()); + EPFrameBuffer::sendUpdate(frameBuffer->rect(), EPFrameBuffer::HighQualityGrayscale, EPFrameBuffer::FullUpdate, true); + } + painter.setPen(Qt::black); + auto text = "Loading " + displayName() + "..."; + int padding = 10; + int textHeight = fm.height() + padding; + QRect textRect( + QPoint(0 + padding, size.height() - textHeight), + QSize(size.width() - padding * 2, textHeight) + ); + painter.drawText( + textRect, + Qt::AlignVCenter | Qt::AlignRight, + text + ); + EPFrameBuffer::sendUpdate(textRect, EPFrameBuffer::Grayscale, EPFrameBuffer::PartialUpdate, true); + painter.end(); + }); + qDebug() << "Waitng for screen to finish..."; + Oxide::Sentry::sentry_span(t, "wait", "Wait for screen finish updating", [](){ + EPFrameBuffer::waitForLastUpdate(); + }); + }); qDebug() << "Finished paining splash screen for" << name(); } void Application::powerStateDataRecieved(FifoHandler* handler, const QString& data){ @@ -380,3 +484,17 @@ void Application::powerStateDataRecieved(FifoHandler* handler, const QString& da qWarning() << "Unknown power state call: " << data; } } +void Application::startSpan(std::string operation, std::string description){ + if(!sharedSettings.applicationUsage()){ + return; + } + if(span != nullptr){ + Oxide::Sentry::stop_span(span); + delete span; + } + if(transaction == nullptr){ + span = nullptr; + return; + } + span = Oxide::Sentry::start_span(transaction, operation, description); +} diff --git a/applications/system-service/application.h b/applications/system-service/application.h index c12789905..6788e19a6 100644 --- a/applications/system-service/application.h +++ b/applications/system-service/application.h @@ -31,8 +31,9 @@ #include #include #include +#include -#include "dbussettings.h" +#include "../../shared/liboxide/liboxide.h" #include "mxcfb.h" #include "screenapi.h" #include "fifohandler.h" @@ -142,6 +143,7 @@ class Application : public QObject{ Q_PROPERTY(QString user READ user) Q_PROPERTY(QString group READ group) Q_PROPERTY(QStringList directories READ directories WRITE setDirectories NOTIFY directoriesChanged) + Q_PROPERTY(QByteArray screenCapture READ screenCapture) public: Application(QDBusObjectPath path, QObject* parent) : Application(path.path(), parent) {} Application(QString path, QObject* parent) : QObject(parent), m_path(path), m_backgrounded(false), fifos() { @@ -158,8 +160,8 @@ class Application : public QObject{ } ~Application() { unregisterPath(); - if(screenCapture != nullptr){ - delete screenCapture; + if(m_screenCapture != nullptr){ + delete m_screenCapture; } umountAll(); } @@ -319,57 +321,68 @@ class Application : public QObject{ setValue("directories", directories); emit directoriesChanged(directories); } + QByteArray screenCapture(){ + if(!hasPermission("permissions")){ + return QByteArray(); + } + return screenCaptureNoSecurityCheck(); + } + QByteArray screenCaptureNoSecurityCheck(){ return qUncompress(*m_screenCapture); } + const QVariantMap& getConfig(){ return m_config; } void setConfig(const QVariantMap& config); void saveScreen(){ - if(screenCapture != nullptr){ + if(m_screenCapture != nullptr){ return; } - qDebug() << "Saving screen..."; - int frameBufferHandle = open("/dev/fb0", O_RDWR); - char* frameBuffer = (char*)mmap(0, DISPLAYSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, frameBufferHandle, 0); - qDebug() << "Compressing data..."; - auto compressedData = qCompress(QByteArray(frameBuffer, DISPLAYSIZE)); - close(frameBufferHandle); - screenCapture = new QByteArray(compressedData); - qDebug() << "Screen saved."; + Oxide::Sentry::sentry_transaction("application", "saveScreen", [this](Oxide::Sentry::Transaction* t){ + qDebug() << "Saving screen..."; + QByteArray bytes; + Oxide::Sentry::sentry_span(t, "save", "Save the framebuffer", [&bytes]{ + QBuffer buffer(&bytes); + buffer.open(QIODevice::WriteOnly); + if(!EPFrameBuffer::framebuffer()->save(&buffer, "JPG", 100)){ + qWarning() << "Failed to save buffer"; + } + }); + qDebug() << "Compressing data..."; + Oxide::Sentry::sentry_span(t, "compress", "Compress the framebuffer", [this, bytes]{ + m_screenCapture = new QByteArray(qCompress(bytes)); + }); + qDebug() << "Screen saved " << m_screenCapture->size() << "bytes"; + }); } void recallScreen(){ - if(screenCapture == nullptr){ - return; - } - qDebug() << "Uncompressing screen..."; - auto uncompressedData = qUncompress(*screenCapture); - if(!uncompressedData.size()){ - qDebug() << "Screen capture was corrupt"; - qDebug() << screenCapture->size(); - delete screenCapture; + if(m_screenCapture == nullptr){ return; } - qDebug() << "Recalling screen..."; - int frameBufferHandle = open("/dev/fb0", O_RDWR); - auto frameBuffer = (char*)mmap(0, DISPLAYSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, frameBufferHandle, 0); - memcpy(frameBuffer, uncompressedData, DISPLAYSIZE); - - mxcfb_update_data update_data; - mxcfb_rect update_rect; - update_rect.top = 0; - update_rect.left = 0; - update_rect.width = DISPLAYWIDTH; - update_rect.height = DISPLAYHEIGHT; - update_data.update_marker = 0; - update_data.update_region = update_rect; - update_data.waveform_mode = WAVEFORM_MODE_AUTO; - update_data.update_mode = UPDATE_MODE_FULL; - update_data.dither_mode = EPDC_FLAG_USE_DITHERING_MAX; - update_data.temp = TEMP_USE_REMARKABLE_DRAW; - update_data.flags = 0; - ioctl(frameBufferHandle, MXCFB_SEND_UPDATE, &update_data); + Oxide::Sentry::sentry_transaction("application", "recallScreen", [this](Oxide::Sentry::Transaction* t){ + qDebug() << "Uncompressing screen..."; + QImage img; + Oxide::Sentry::sentry_span(t, "decompress", "Decompress the framebuffer", [this, &img]{ + img = QImage::fromData(screenCaptureNoSecurityCheck(), "JPG"); + }); + if(img.isNull()){ + qDebug() << "Screen capture was corrupt"; + qDebug() << m_screenCapture->size(); + delete m_screenCapture; + return; + } + qDebug() << "Recalling screen..."; + Oxide::Sentry::sentry_span(t, "recall", "Recall the screen", [this, img]{ + auto size = EPFrameBuffer::framebuffer()->size(); + QRect rect(0, 0, size.width(), size.height()); + QPainter painter(EPFrameBuffer::framebuffer()); + painter.drawImage(rect, img); + painter.end(); + EPFrameBuffer::sendUpdate(rect, EPFrameBuffer::HighQualityGrayscale, EPFrameBuffer::FullUpdate, true); + EPFrameBuffer::waitForLastUpdate(); - close(frameBufferHandle); - delete screenCapture; - screenCapture = nullptr; - qDebug() << "Screen recalled."; + delete m_screenCapture; + m_screenCapture = nullptr; + }); + qDebug() << "Screen recalled."; + }); } void waitForFinished(){ if(m_process->processId()){ @@ -441,6 +454,18 @@ private slots: break; case QProcess::NotRunning: qDebug() << "Application" << name() << "is not running."; + if(sharedSettings.applicationUsage()){ + if(span != nullptr){ + Oxide::Sentry::stop_span(span); + delete span; + span = nullptr; + } + if(transaction != nullptr){ + Oxide::Sentry::stop_transaction(transaction); + delete transaction; + transaction = nullptr; + } + } break; default: qDebug() << "Application" << name() << "unknown state" << state; @@ -453,10 +478,11 @@ private slots: QString m_path; SandBoxProcess* m_process; bool m_backgrounded; - QByteArray* screenCapture = nullptr; - size_t screenCaptureSize; + QByteArray* m_screenCapture = nullptr; QElapsedTimer timer; QMap fifos; + Oxide::Sentry::Transaction* transaction = nullptr; + Oxide::Sentry::Span* span = nullptr; bool hasPermission(QString permission, const char* sender = __builtin_FUNCTION()); void delayUpTo(int milliseconds){ @@ -593,66 +619,98 @@ private slots: const QString resourcePath() { return "/tmp/tarnish-chroot/" + name(); } const QString chrootPath() { return resourcePath() + "/chroot"; } void mountAll(){ - auto path = chrootPath(); - qDebug() << "Setting up chroot" << path; - // System tmpfs folders - bind("/dev", path + "/dev"); - bind("/proc", path + "/proc"); - sysfs(path + "/sys"); - // Folders required to run things - bind("/bin", path + "/bin", true); - bind("/sbin", path + "/sbin", true); - bind("/lib", path + "/lib", true); - bind("/usr/lib", path + "/usr/lib", true); - bind("/usr/bin", path + "/usr/bin", true); - bind("/usr/sbin", path + "/usr/sbin", true); - bind("/opt/bin", path + "/opt/bin", true); - bind("/opt/lib", path + "/opt/lib", true); - bind("/opt/usr/bin", path + "/opt/usr/bin", true); - bind("/opt/usr/lib", path + "/opt/usr/lib", true); - // tmpfs folders - mkdirs(path + "/tmp", 744); - if(!QFile::exists(path + "/run")){ - ramdisk(path + "/run"); - } - if(!QFile::exists(path + "/var/volatile")){ - ramdisk(path + "/var/volatile"); - } - // Configured folders - for(auto directory : directories()){ - bind(directory, path + directory); - } - // Fake sys devices - auto fifo = mkfifo("powerState", path + "/sys/power/state"); - connect(fifo, &FifoHandler::dataRecieved, this, &Application::powerStateDataRecieved); - // Missing symlinks - symlink(path + "/var/run", "../run"); - symlink(path + "/var/lock", "../run/lock"); - symlink(path + "/var/tmp", "volatile/tmp"); + Oxide::Sentry::sentry_transaction("application", "mount", [this](Oxide::Sentry::Transaction* t){ +#ifdef SENTRY + if(t != nullptr){ + sentry_transaction_set_tag(t->inner, "application", name().toStdString().c_str()); + } +#endif + auto path = chrootPath(); + qDebug() << "Setting up chroot" << path; + Oxide::Sentry::sentry_span(t, "bind", "Bind directories", [this, path]{ + // System tmpfs folders + bind("/dev", path + "/dev"); + bind("/proc", path + "/proc"); + sysfs(path + "/sys"); + // Folders required to run things + bind("/bin", path + "/bin", true); + bind("/sbin", path + "/sbin", true); + bind("/lib", path + "/lib", true); + bind("/usr/lib", path + "/usr/lib", true); + bind("/usr/bin", path + "/usr/bin", true); + bind("/usr/sbin", path + "/usr/sbin", true); + bind("/opt/bin", path + "/opt/bin", true); + bind("/opt/lib", path + "/opt/lib", true); + bind("/opt/usr/bin", path + "/opt/usr/bin", true); + bind("/opt/usr/lib", path + "/opt/usr/lib", true); + }); + Oxide::Sentry::sentry_span(t, "ramdisk", "Create ramdisks", [this, path]{ + // tmpfs folders + mkdirs(path + "/tmp", 744); + if(!QFile::exists(path + "/run")){ + ramdisk(path + "/run"); + } + if(!QFile::exists(path + "/var/volatile")){ + ramdisk(path + "/var/volatile"); + } + }); + Oxide::Sentry::sentry_span(t, "configured", "Bind configured directories", [this, path]{ + // Configured folders + for(auto directory : directories()){ + bind(directory, path + directory); + } + }); + Oxide::Sentry::sentry_span(t, "fifo", "Create fifos", [this, path]{ + // Fake sys devices + auto fifo = mkfifo("powerState", path + "/sys/power/state"); + connect(fifo, &FifoHandler::dataRecieved, this, &Application::powerStateDataRecieved); + }); + Oxide::Sentry::sentry_span(t, "symlink", "Create symlinks", [this, path]{ + // Missing symlinks + symlink(path + "/var/run", "../run"); + symlink(path + "/var/lock", "../run/lock"); + symlink(path + "/var/tmp", "volatile/tmp"); + }); + }); } void umountAll(){ - auto path = chrootPath(); - for(auto name : fifos.keys()){ - auto fifo = fifos.take(name); - fifo->quit(); - fifo->deleteLater(); - } - QDir dir(path); - if(!dir.exists()){ - return; - } - qDebug() << "Tearing down chroot" << path; - for(auto file : dir.entryList(QDir::Files)){ - QFile::remove(file); - } - for(auto mount : getActiveApplicationMounts()){ - umount(mount); - } - if(!getActiveApplicationMounts().isEmpty()){ - qDebug() << "Some items are still mounted in chroot" << path; - return; - } - dir.removeRecursively(); + Oxide::Sentry::sentry_transaction("application", "umount", [this](Oxide::Sentry::Transaction* t){ +#ifdef SENTRY + if(t != nullptr){ + sentry_transaction_set_tag(t->inner, "application", name().toStdString().c_str()); + } +#endif + auto path = chrootPath(); + Oxide::Sentry::sentry_span(t, "fifos", "Remove fifos", [this]{ + for(auto name : fifos.keys()){ + auto fifo = fifos.take(name); + fifo->quit(); + fifo->deleteLater(); + } + }); + QDir dir(path); + if(!dir.exists()){ + return; + } + qDebug() << "Tearing down chroot" << path; + Oxide::Sentry::sentry_span(t, "dirs", "Remove directories", [dir]{ + for(auto file : dir.entryList(QDir::Files)){ + QFile::remove(file); + } + }); + Oxide::Sentry::sentry_span(t, "umount", "Unmount all mounts", [this]{ + for(auto mount : getActiveApplicationMounts()){ + umount(mount); + } + }); + if(!getActiveApplicationMounts().isEmpty()){ + qDebug() << "Some items are still mounted in chroot" << path; + return; + } + Oxide::Sentry::sentry_span(t, "rm", "Remove final folder", [&dir]{ + dir.removeRecursively(); + }); + }); } bool isMounted(const QString& path){ return getActiveMounts().contains(path); } QStringList getActiveApplicationMounts(){ @@ -682,6 +740,7 @@ private slots: std::reverse(std::begin(activeMounts), std::end(activeMounts)); return activeMounts; } + void startSpan(std::string operation, std::string description); }; #endif // APPLICATION_H diff --git a/applications/system-service/appsapi.cpp b/applications/system-service/appsapi.cpp index 82db6414a..8197c3ddf 100644 --- a/applications/system-service/appsapi.cpp +++ b/applications/system-service/appsapi.cpp @@ -1,6 +1,10 @@ +#include + #include "appsapi.h" #include "notificationapi.h" +using namespace Oxide; + AppsAPI::AppsAPI(QObject* parent) : APIBase(parent), m_stopping(false), @@ -14,81 +18,113 @@ AppsAPI::AppsAPI(QObject* parent) m_processManagerApplication("/"), m_taskSwitcherApplication("/"), m_sleeping(false) { - singleton(this); - SignalHandler::setup_unix_signal_handlers(); - qDBusRegisterMetaType>(); - qDBusRegisterMetaType(); - settings.sync(); - auto version = settings.value("version", 0).toInt(); - if(version < OXIDE_SETTINGS_VERSION){ - migrate(&settings, version); - } - readApplications(); - - auto path = QDBusObjectPath(settings.value("lockscreenApplication").toString()); - auto app = getApplication(path); - if(app == nullptr){ - app = getApplication("codes.eeems.decay"); - if(app != nullptr){ - path = app->qPath(); - } - } - m_lockscreenApplication = path; - - path = QDBusObjectPath(settings.value("startupApplication").toString()); - app = getApplication(path); - if(app == nullptr){ - app = getApplication("codes.eeems.oxide"); - if(app != nullptr){ - path = app->qPath(); - } - } - m_startupApplication = path; - - path = QDBusObjectPath(settings.value("processManagerApplication").toString()); - app = getApplication(path); - if(app == nullptr){ - app = getApplication("codes.eeems.erode"); - if(app != nullptr){ - path = app->qPath(); - } - } - m_processManagerApplication= path; - - path = QDBusObjectPath(settings.value("processManagerApplication").toString()); - app = getApplication(path); - if(app == nullptr){ - app = getApplication("codes.eeems.corrupt"); - if(app != nullptr){ - path = app->qPath(); - } - } - m_taskSwitcherApplication= path; + Oxide::Sentry::sentry_transaction("apps", "init", [this](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_span(t, "start", "Launching initial application", [this](Oxide::Sentry::Span* s){ + Oxide::Sentry::sentry_span(s, "singleton", "Setup singleton", [this]{ + singleton(this); + }); + Oxide::Sentry::sentry_span(s, "signals", "Setup unix signal handlers", []{ + SignalHandler::setup_unix_signal_handlers(); + }); + qDBusRegisterMetaType>(); + qDBusRegisterMetaType(); + Oxide::Sentry::sentry_span(s, "sync", "Sync settings", [this]{ + settings.sync(); + }); + auto version = settings.value("version", 0).toInt(); + if(version < OXIDE_SETTINGS_VERSION){ + Oxide::Sentry::sentry_span(s, "migrate", "Migrate to latest version", [this, version]{ + migrate(&settings, version); + }); + } + }); + Oxide::Sentry::sentry_span(t, "application", "Read applications from disk", [this]{ + readApplications(); + }); + Oxide::Sentry::sentry_span(t, "setup", "Setup lockscreen, startup, process manager, task switcher", [this](Oxide::Sentry::Span* s){ + Oxide::Sentry::sentry_span(s, "lockscreenApplication", "Determine what the lockscreen application is", [this]{ + auto path = QDBusObjectPath(settings.value("lockscreenApplication").toString()); + auto app = getApplication(path); + if(app == nullptr){ + app = getApplication("codes.eeems.decay"); + if(app != nullptr){ + path = app->qPath(); + } + } + m_lockscreenApplication = path; + }); + Oxide::Sentry::sentry_span(s, "startupApplication", "Determine what the startup application is", [this]{ + auto path = QDBusObjectPath(settings.value("startupApplication").toString()); + auto app = getApplication(path); + if(app == nullptr){ + app = getApplication("codes.eeems.oxide"); + if(app != nullptr){ + path = app->qPath(); + } + } + m_startupApplication = path; + }); + Oxide::Sentry::sentry_span(s, "processManagerApplication", "Determine what the process manager application is", [this]{ + auto path = QDBusObjectPath(settings.value("processManagerApplication").toString()); + auto app = getApplication(path); + if(app == nullptr){ + app = getApplication("codes.eeems.erode"); + if(app != nullptr){ + path = app->qPath(); + } + } + m_processManagerApplication= path; + }); + Oxide::Sentry::sentry_span(s, "taskSwitcherApplication", "Determine what the task switcher application is", [this]{ + auto path = QDBusObjectPath(settings.value("taskSwitcherApplication").toString()); + auto app = getApplication(path); + if(app == nullptr){ + path = QDBusObjectPath(settings.value("processManagerApplication").toString()); + app = getApplication(path); + } + if(app == nullptr){ + app = getApplication("codes.eeems.corrupt"); + if(app != nullptr){ + path = app->qPath(); + } + } + m_taskSwitcherApplication= path; + }); + }); + }); } void AppsAPI::startup(){ - for(auto app : applications){ - if(app->autoStart()){ - qDebug() << "Auto starting" << app->name(); - app->launchNoSecurityCheck(); - if(app->type() == Backgroundable){ - qDebug() << " Pausing auto started app" << app->name(); - app->pauseNoSecurityCheck(); + Oxide::Sentry::sentry_transaction("apps", "startup", [this](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_span(t, "autoStart", "Launching auto start applications", [this](Oxide::Sentry::Span* s){ + for(auto app : applications){ + if(app->autoStart()){ + qDebug() << "Auto starting" << app->name(); + Oxide::Sentry::sentry_span(s, app->name().toStdString(), "Launching application", [app]{ + app->launchNoSecurityCheck(); + if(app->type() == Backgroundable){ + qDebug() << " Pausing auto started app" << app->name(); + app->pauseNoSecurityCheck(); + } + }); + } + } + }); + Oxide::Sentry::sentry_span(t, "start", "Launching initial application", [this]{ + auto app = getApplication(m_lockscreenApplication); + if(app == nullptr){ + qDebug() << "Could not find lockscreen application"; + app = getApplication(m_startupApplication); } - } - } - auto app = getApplication(m_lockscreenApplication); - if(app == nullptr){ - qDebug() << "Could not find lockscreen application"; - app = getApplication(m_startupApplication); - } - if(app == nullptr){ - qDebug() << "could not find startup application"; - return; - } - qDebug() << "Starting initial application" << app->name(); - app->launchNoSecurityCheck(); - m_starting = false; + if(app == nullptr){ + qDebug() << "could not find startup application"; + return; + } + qDebug() << "Starting initial application" << app->name(); + app->launchNoSecurityCheck(); + m_starting = false; + }); + }); } bool AppsAPI::locked(){ return notificationAPI->locked(); } diff --git a/applications/system-service/appsapi.h b/applications/system-service/appsapi.h index b1b25157f..a04bd09de 100644 --- a/applications/system-service/appsapi.h +++ b/applications/system-service/appsapi.h @@ -16,12 +16,13 @@ #include "apibase.h" #include "application.h" -#include "signalhandler.h" #define OXIDE_SETTINGS_VERSION 1 #define appsAPI AppsAPI::singleton() +using namespace Oxide; + class AppsAPI : public APIBase { Q_OBJECT Q_CLASSINFO("D-Bus Interface", OXIDE_APPS_INTERFACE) @@ -87,7 +88,7 @@ class AppsAPI : public APIBase { qDebug() << "Ensuring all applications have stopped..."; for(auto app : applications){ app->waitForFinished(); - delete app; + app->deleteLater(); } applications.clear(); qDebug() << "Displaying final quit message..."; @@ -137,16 +138,24 @@ class AppsAPI : public APIBase { qDebug() << "Invalid configuration: " << name << " has invalid bin" << bin; return QDBusObjectPath("/"); } + if(!QFileInfo(bin).isExecutable()){ + qDebug() << "Invalid configuration: " << name << " has bin that is not executable" << bin; + return QDBusObjectPath("/"); + } if(applications.contains(name)){ return applications[name]->qPath(); } - auto path = QDBusObjectPath(getPath(name)); - auto app = new Application(path, reinterpret_cast(this)); - auto displayName = properties.value("displayName", name).toString(); - app->setConfig(properties); - applications.insert(name, app); - app->registerPath(); - emit applicationRegistered(path); + QDBusObjectPath path; + Oxide::Sentry::sentry_transaction("apps", "registerApplication", [this, &path, name, properties](Oxide::Sentry::Transaction* t){ + Q_UNUSED(t); + path = QDBusObjectPath(getPath(name)); + auto app = new Application(path, reinterpret_cast(this)); + auto displayName = properties.value("displayName", name).toString(); + app->setConfig(properties); + applications.insert(name, app); + app->registerPath(); + emit applicationRegistered(path); + }); return path; } Q_INVOKABLE bool unregisterApplication(QDBusObjectPath path){ @@ -168,8 +177,11 @@ class AppsAPI : public APIBase { if(!hasPermission("apps")){ return; } - writeApplications(); - readApplications(); + Oxide::Sentry::sentry_transaction("apps", "reload", [this](Oxide::Sentry::Transaction* t){ + Q_UNUSED(t); + writeApplications(); + readApplications(); + }); } QDBusObjectPath startupApplication(){ @@ -290,12 +302,15 @@ class AppsAPI : public APIBase { } void unregisterApplication(Application* app){ - auto name = app->name(); - if(applications.contains(name)){ - applications.remove(name); - emit applicationUnregistered(app->qPath()); - app->deleteLater(); - } + Oxide::Sentry::sentry_transaction("apps", "unregisterApplication", [this, app](Oxide::Sentry::Transaction* t){ + Q_UNUSED(t); + auto name = app->name(); + if(applications.contains(name)){ + applications.remove(name); + emit applicationUnregistered(app->qPath()); + app->deleteLater(); + } + }); } void pauseAll(){ for(auto app : applications){ @@ -421,10 +436,11 @@ class AppsAPI : public APIBase { return; } auto name = currentApplication->name(); - previousApplications.removeAll(name); + removeFromPreviousApplications(name); previousApplications.append(name); qDebug() << "Previous Applications" << previousApplications; } + void removeFromPreviousApplications(QString name){ previousApplications.removeAll(name); } signals: void applicationRegistered(QDBusObjectPath); diff --git a/applications/system-service/bss.h b/applications/system-service/bss.h index a941d4d9e..072c12aee 100644 --- a/applications/system-service/bss.h +++ b/applications/system-service/bss.h @@ -4,9 +4,9 @@ #include #include +#include "../../shared/liboxide/liboxide.h" #include "supplicant.h" #include "network.h" -#include "dbussettings.h" class BSS : public QObject{ Q_OBJECT diff --git a/applications/system-service/buttonhandler.cpp b/applications/system-service/buttonhandler.cpp index da23c2fde..ee58e4d50 100644 --- a/applications/system-service/buttonhandler.cpp +++ b/applications/system-service/buttonhandler.cpp @@ -1,5 +1,6 @@ #include "buttonhandler.h" #include "dbusservice.h" +#include "liboxide.h" void button_exit_handler(){ // Release lock diff --git a/applications/system-service/buttonhandler.h b/applications/system-service/buttonhandler.h index 1c4972c0f..fc0058c31 100644 --- a/applications/system-service/buttonhandler.h +++ b/applications/system-service/buttonhandler.h @@ -22,9 +22,9 @@ #include #include #include +#include #include "event_device.h" -#include "devicesettings.h" using namespace std; diff --git a/applications/system-service/dbusservice.h b/applications/system-service/dbusservice.h index a158a5361..4c711f290 100644 --- a/applications/system-service/dbusservice.h +++ b/applications/system-service/dbusservice.h @@ -8,11 +8,10 @@ #include #include #include - #include #include -#include "dbussettings.h" +#include "../../shared/liboxide/liboxide.h" #include "powerapi.h" #include "wifiapi.h" #include "appsapi.h" @@ -25,6 +24,7 @@ #define dbusService DBusService::singleton() using namespace std; +using namespace Oxide::Sentry; struct APIEntry { QString path; @@ -55,6 +55,9 @@ class DBusService : public APIBase { }); auto bus = QDBusConnection::systemBus(); if(!bus.isConnected()){ +#ifdef SENTRY + sentry_breadcrumb("dbusservice", "Failed to connect to system bus.", "error"); +#endif qFatal("Failed to connect to system bus."); } QDBusConnectionInterface* interface = bus.interface(); @@ -63,10 +66,16 @@ class DBusService : public APIBase { bus.registerService(OXIDE_SERVICE); if(!reply.isValid()){ QDBusError ex = reply.error(); +#ifdef SENTRY + sentry_breadcrumb("dbusservice", "Unable to register service", "error"); +#endif qFatal("Unable to register service: %s", ex.message().toStdString().c_str()); } qDebug() << "Registering object..."; if(!bus.registerObject(OXIDE_SERVICE_PATH, instance, QDBusConnection::ExportAllContents)){ +#ifdef SENTRY + sentry_breadcrumb("dbusservice", "Unable to register interface", "error"); +#endif qFatal("Unable to register interface: %s", bus.lastError().message().toStdString().c_str()); } connect(bus.interface(), SIGNAL(serviceOwnerChanged(QString,QString,QString)), @@ -76,67 +85,106 @@ class DBusService : public APIBase { return instance; } DBusService(QObject* parent) : APIBase(parent), apis(){ - apis.insert("wifi", APIEntry{ - .path = QString(OXIDE_SERVICE_PATH) + "/wifi", - .dependants = new QStringList(), - .instance = new WifiAPI(this), - }); - apis.insert("system", APIEntry{ - .path = QString(OXIDE_SERVICE_PATH) + "/system", - .dependants = new QStringList(), - .instance = new SystemAPI(this), - }); - apis.insert("power", APIEntry{ - .path = QString(OXIDE_SERVICE_PATH) + "/power", - .dependants = new QStringList(), - .instance = new PowerAPI(this), - }); - apis.insert("screen", APIEntry{ - .path = QString(OXIDE_SERVICE_PATH) + "/screen", - .dependants = new QStringList(), - .instance = new ScreenAPI(this), - }); - apis.insert("apps", APIEntry{ - .path = QString(OXIDE_SERVICE_PATH) + "/apps", - .dependants = new QStringList(), - .instance = new AppsAPI(this), - }); - apis.insert("notification", APIEntry{ - .path = QString(OXIDE_SERVICE_PATH) + "/notification", - .dependants = new QStringList(), - .instance = new NotificationAPI(this), - }); - - connect(buttonHandler, &ButtonHandler::leftHeld, systemAPI, &SystemAPI::leftAction); - connect(buttonHandler, &ButtonHandler::homeHeld, systemAPI, &SystemAPI::homeAction); - connect(buttonHandler, &ButtonHandler::rightHeld, systemAPI, &SystemAPI::rightAction); - connect(buttonHandler, &ButtonHandler::powerHeld, systemAPI, &SystemAPI::powerAction); - connect(buttonHandler, &ButtonHandler::powerPress, systemAPI, &SystemAPI::suspend); - connect(buttonHandler, &ButtonHandler::activity, systemAPI, &SystemAPI::activity); - connect(powerAPI, &PowerAPI::chargerStateChanged, systemAPI, &SystemAPI::activity); - connect(systemAPI, &SystemAPI::leftAction, appsAPI, []{ - if(notificationAPI->locked()){ - return; - } - auto currentApplication = appsAPI->getApplication(appsAPI->currentApplicationNoSecurityCheck()); - if(currentApplication != nullptr && currentApplication->path() == appsAPI->lockscreenApplication().path()){ - qDebug() << "Left Action cancelled. On lockscreen"; - return; - } - if(!appsAPI->previousApplicationNoSecurityCheck()){ - appsAPI->openDefaultApplication(); - } +#ifdef SENTRY + sentry_breadcrumb("dbusservice", "Initializing APIs", "info"); +#endif + Oxide::Sentry::sentry_transaction("dbus", "init", [this](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_span(t, "apis", "Initialize APIs", [this](Oxide::Sentry::Span* s){ + Oxide::Sentry::sentry_span(s, "wifi", "Initialize wifi API", [this]{ + apis.insert("wifi", APIEntry{ + .path = QString(OXIDE_SERVICE_PATH) + "/wifi", + .dependants = new QStringList(), + .instance = new WifiAPI(this), + }); + }); + Oxide::Sentry::sentry_span(s, "system", "Initialize system API", [this]{ + apis.insert("system", APIEntry{ + .path = QString(OXIDE_SERVICE_PATH) + "/system", + .dependants = new QStringList(), + .instance = new SystemAPI(this), + }); + }); + Oxide::Sentry::sentry_span(s, "power", "Initialize power API", [this]{ + apis.insert("power", APIEntry{ + .path = QString(OXIDE_SERVICE_PATH) + "/power", + .dependants = new QStringList(), + .instance = new PowerAPI(this), + }); + }); + Oxide::Sentry::sentry_span(s, "screen", "Initialize screen API", [this]{ + apis.insert("screen", APIEntry{ + .path = QString(OXIDE_SERVICE_PATH) + "/screen", + .dependants = new QStringList(), + .instance = new ScreenAPI(this), + }); + }); + Oxide::Sentry::sentry_span(s, "apps", "Initialize apps API", [this]{ + apis.insert("apps", APIEntry{ + .path = QString(OXIDE_SERVICE_PATH) + "/apps", + .dependants = new QStringList(), + .instance = new AppsAPI(this), + }); + }); + Oxide::Sentry::sentry_span(s, "notification", "Initialize notification API", [this]{ + apis.insert("notification", APIEntry{ + .path = QString(OXIDE_SERVICE_PATH) + "/notification", + .dependants = new QStringList(), + .instance = new NotificationAPI(this), + }); + }); + }); +#ifdef SENTRY + sentry_breadcrumb("dbusservice", "Connecting button handler events", "info"); +#endif + Oxide::Sentry::sentry_span(t, "connect", "Connect events", []{ + connect(buttonHandler, &ButtonHandler::leftHeld, systemAPI, &SystemAPI::leftAction); + connect(buttonHandler, &ButtonHandler::homeHeld, systemAPI, &SystemAPI::homeAction); + connect(buttonHandler, &ButtonHandler::rightHeld, systemAPI, &SystemAPI::rightAction); + connect(buttonHandler, &ButtonHandler::powerHeld, systemAPI, &SystemAPI::powerAction); + connect(buttonHandler, &ButtonHandler::powerPress, systemAPI, &SystemAPI::suspend); + connect(buttonHandler, &ButtonHandler::activity, systemAPI, &SystemAPI::activity); +#ifdef SENTRY + sentry_breadcrumb("dbusservice", "Connecting power events", "info"); +#endif + connect(powerAPI, &PowerAPI::chargerStateChanged, systemAPI, &SystemAPI::activity); +#ifdef SENTRY + sentry_breadcrumb("dbusservice", "Connecting system events", "info"); +#endif + connect(systemAPI, &SystemAPI::leftAction, appsAPI, []{ + if(notificationAPI->locked()){ + return; + } + auto currentApplication = appsAPI->getApplication(appsAPI->currentApplicationNoSecurityCheck()); + if(currentApplication != nullptr && currentApplication->path() == appsAPI->lockscreenApplication().path()){ + qDebug() << "Left Action cancelled. On lockscreen"; + return; + } + if(!appsAPI->previousApplicationNoSecurityCheck()){ + appsAPI->openDefaultApplication(); + } + }); + connect(systemAPI, &SystemAPI::homeAction, appsAPI, &AppsAPI::openTaskManager); + connect(systemAPI, &SystemAPI::bottomAction, appsAPI, &AppsAPI::openTaskSwitcher); + connect(systemAPI, &SystemAPI::topAction, systemAPI, &SystemAPI::toggleSwipes); + }); +#ifdef SENTRY + sentry_breadcrumb("dbusservice", "Cleaning up", "info"); +#endif + Oxide::Sentry::sentry_span(t, "cleanup", "Cleanup", [this]{ + auto bus = QDBusConnection::systemBus(); + for(auto api : apis){ + bus.unregisterObject(api.path); + } + }); +#ifdef SENTRY + sentry_breadcrumb("dbusservice", "APIs initialized", "info"); +#endif }); - connect(systemAPI, &SystemAPI::homeAction, appsAPI, &AppsAPI::openTaskManager); - connect(systemAPI, &SystemAPI::bottomAction, appsAPI, &AppsAPI::openTaskSwitcher); - connect(systemAPI, &SystemAPI::topAction, systemAPI, &SystemAPI::toggleSwipes); - - auto bus = QDBusConnection::systemBus(); - for(auto api : apis){ - bus.unregisterObject(api.path); - } } ~DBusService(){ +#ifdef SENTRY + sentry_breadcrumb("dbusservice", "Disconnecting APIs", "info"); +#endif qDebug() << "Removing all APIs"; auto bus = QDBusConnection::systemBus(); for(auto api : apis){ @@ -147,6 +195,9 @@ class DBusService : public APIBase { delete api.dependants; } apis.clear(); +#ifdef SENTRY + sentry_breadcrumb("dbusservice", "APIs disconnected", "info"); +#endif } void setEnabled(bool enabled){ Q_UNUSED(enabled); }; @@ -161,6 +212,9 @@ class DBusService : public APIBase { public slots: QDBusObjectPath requestAPI(QString name, QDBusMessage message) { +#ifdef SENTRY + sentry_breadcrumb("dbusservice", ("requestAPI() " + name).toStdString().c_str(), "query"); +#endif if(!hasPermission(name)){ return QDBusObjectPath("/"); } @@ -181,6 +235,9 @@ public slots: return QDBusObjectPath(api.path); }; Q_NOREPLY void releaseAPI(QString name, QDBusMessage message) { +#ifdef SENTRY + sentry_breadcrumb("dbusservice", ("releaseAPI() " + name).toStdString().c_str(), "query"); +#endif if(!apis.contains(name)){ return; } @@ -195,6 +252,9 @@ public slots: } }; QVariantMap APIs(){ +#ifdef SENTRY + sentry_breadcrumb("dbusservice", "APIs()", "query"); +#endif QVariantMap result; for(auto key : apis.keys()){ auto api = apis[key]; @@ -206,6 +266,9 @@ public slots: }; void startup(){ +#ifdef SENTRY + sentry_breadcrumb("dbusservice", "startup", "navigation"); +#endif appsAPI->startup(); } diff --git a/applications/system-service/dbussettings.h b/applications/system-service/dbussettings.h index eabe60ef7..a38ffed81 100644 --- a/applications/system-service/dbussettings.h +++ b/applications/system-service/dbussettings.h @@ -1,24 +1,6 @@ #ifndef DBUSSETTINGS_H #define DBUSSETTINGS_H -#define WPA_SUPPLICANT_SERVICE "fi.w1.wpa_supplicant1" -#define WPA_SUPPLICANT_SERVICE_PATH "/fi/w1/wpa_supplicant1" - -#define OXIDE_SERVICE "codes.eeems.oxide1" -#define OXIDE_SERVICE_PATH "/codes/eeems/oxide1" -#define OXIDE_INTERFACE_VERSION "1.0.0" - -#define OXIDE_GENERAL_INTERFACE OXIDE_SERVICE ".General" -#define OXIDE_POWER_INTERFACE OXIDE_SERVICE ".Power" -#define OXIDE_WIFI_INTERFACE OXIDE_SERVICE ".Wifi" -#define OXIDE_NETWORK_INTERFACE OXIDE_SERVICE ".Network" -#define OXIDE_BSS_INTERFACE OXIDE_SERVICE ".BSS" -#define OXIDE_APPS_INTERFACE OXIDE_SERVICE ".Apps" -#define OXIDE_APPLICATION_INTERFACE OXIDE_SERVICE ".Application" -#define OXIDE_SYSTEM_INTERFACE OXIDE_SERVICE ".System" -#define OXIDE_SCREEN_INTERFACE OXIDE_SERVICE ".Screen" -#define OXIDE_NOTIFICATIONS_INTERFACE OXIDE_SERVICE ".Notifications" -#define OXIDE_NOTIFICATION_INTERFACE OXIDE_SERVICE ".Notification" -#define OXIDE_SCREENSHOT_INTERFACE OXIDE_SERVICE ".Screenshot" +#include #endif // DBUSSETTINGS_H diff --git a/applications/system-service/digitizerhandler.h b/applications/system-service/digitizerhandler.h index df3831c16..6377e9664 100644 --- a/applications/system-service/digitizerhandler.h +++ b/applications/system-service/digitizerhandler.h @@ -10,9 +10,9 @@ #include #include #include +#include #include "event_device.h" -#include "devicesettings.h" using namespace std; diff --git a/applications/system-service/fifohandler.h b/applications/system-service/fifohandler.h index 78cb74591..176b08ca6 100644 --- a/applications/system-service/fifohandler.h +++ b/applications/system-service/fifohandler.h @@ -6,8 +6,7 @@ #include #include - -#include "devicesettings.h" +#include class FifoHandler : public QObject { Q_OBJECT diff --git a/applications/system-service/main.cpp b/applications/system-service/main.cpp index 98a95ef8f..54f703d2b 100755 --- a/applications/system-service/main.cpp +++ b/applications/system-service/main.cpp @@ -2,11 +2,12 @@ #include #include +#include #include "dbusservice.h" -#include "signalhandler.h" using namespace std; +using namespace Oxide::Sentry; const char *qt_version = qVersion(); @@ -16,7 +17,7 @@ void sigHandler(int signal){ } int main(int argc, char *argv[]){ - if(deviceSettings.getDeviceType() == DeviceSettings::RM2 && getenv("RM2FB_ACTIVE") == nullptr){ + if(deviceSettings.getDeviceType() == Oxide::DeviceSettings::RM2 && getenv("RM2FB_ACTIVE") == nullptr){ qWarning() << "rm2fb not detected. Running xochitl instead!"; return QProcess::execute("/usr/bin/xochitl", QStringList()); } @@ -29,6 +30,7 @@ int main(int argc, char *argv[]){ qputenv("QT_QPA_PLATFORM", "epaper:enable_fonts"); #endif QGuiApplication app(argc, argv); + sentry_init("tarnish", argv); app.setOrganizationName("Eeems"); app.setOrganizationDomain(OXIDE_SERVICE); app.setApplicationName("tarnish"); diff --git a/applications/system-service/network.h b/applications/system-service/network.h index 0e0eb36af..4bbb6bad8 100644 --- a/applications/system-service/network.h +++ b/applications/system-service/network.h @@ -5,8 +5,8 @@ #include #include +#include "../../shared/liboxide/liboxide.h" #include "supplicant.h" -#include "dbussettings.h" class Network : public QObject { Q_OBJECT diff --git a/applications/system-service/notification.h b/applications/system-service/notification.h index 30e2fff57..01b468d2d 100644 --- a/applications/system-service/notification.h +++ b/applications/system-service/notification.h @@ -5,8 +5,8 @@ #include #include +#include "../../shared/liboxide/liboxide.h" #include "application.h" -#include "dbussettings.h" class Notification : public QObject{ Q_OBJECT diff --git a/applications/system-service/notificationapi.h b/applications/system-service/notificationapi.h index 87bce61a6..0ea578d12 100644 --- a/applications/system-service/notificationapi.h +++ b/applications/system-service/notificationapi.h @@ -5,7 +5,8 @@ #include #include -#include "dbussettings.h" +#include + #include "apibase.h" #include "notification.h" @@ -26,7 +27,11 @@ class NotificationAPI : public APIBase { return instance; } NotificationAPI(QObject* parent) : APIBase(parent), notificationDisplayQueue(), m_enabled(false), m_notifications(), m_lock() { - singleton(this); + Oxide::Sentry::sentry_transaction("apps", "init", [this](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_span(t, "singleton", "Setup singleton", [this]{ + singleton(this); + }); + }); } ~NotificationAPI(){} bool enabled(){ return m_enabled; } diff --git a/applications/system-service/powerapi.h b/applications/system-service/powerapi.h index 70c1a8be3..b33dc34d6 100644 --- a/applications/system-service/powerapi.h +++ b/applications/system-service/powerapi.h @@ -33,38 +33,50 @@ class PowerAPI : public APIBase { } PowerAPI(QObject* parent) : APIBase(parent), batteries(), chargers(), m_chargerState(ChargerUnknown){ - singleton(this); - QDir dir("/sys/class/power_supply"); - qDebug() << "Looking for batteries and chargers..."; - for(auto path : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable)){ - qDebug() << (" Checking " + path + "...").toStdString().c_str(); - SysObject item(dir.path() + "/" + path); - if(!item.hasProperty("type")){ - qDebug() << " Missing type property"; - continue; - } - if(item.hasProperty("present") && !item.intProperty("present")){ - qDebug() << " Either missing present property, or battery is not present"; - continue; - } - auto type = item.strProperty("type"); - if(type == "Battery"){ - qDebug() << " Found Battery!"; - batteries.append(item); - }else if(type == "USB" || type == "USB_CDP"){ - qDebug() << " Found Charger!"; - chargers.append(item); - }else{ - qDebug() << " Unknown type"; - } - } - update(); - timer = new QTimer(this); - timer->setSingleShot(false); - timer->setInterval(3 * 1000); // 3 seconds - timer->moveToThread(qApp->thread()); - connect(timer, &QTimer::timeout, this, QOverload<>::of(&PowerAPI::update)); - timer->start(); + Oxide::Sentry::sentry_transaction("power", "init", [this](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_span(t, "singleton", "Setup singleton", [this]{ + singleton(this); + }); + Oxide::Sentry::sentry_span(t, "sysfs", "Determine power devices from sysfs", [this](Oxide::Sentry::Span* s){ + QDir dir("/sys/class/power_supply"); + qDebug() << "Looking for batteries and chargers..."; + for(auto path : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable)){ + qDebug() << (" Checking " + path + "...").toStdString().c_str(); + SysObject item(dir.path() + "/" + path); + if(!item.hasProperty("type")){ + qDebug() << " Missing type property"; + continue; + } + if(item.hasProperty("present") && !item.intProperty("present")){ + qDebug() << " Either missing present property, or battery is not present"; + continue; + } + Oxide::Sentry::sentry_span(s, path.toStdString(), "Checking device", [this, &item]{ + auto type = item.strProperty("type"); + if(type == "Battery"){ + qDebug() << " Found Battery!"; + batteries.append(item); + }else if(type == "USB" || type == "USB_CDP"){ + qDebug() << " Found Charger!"; + chargers.append(item); + }else{ + qDebug() << " Unknown type"; + } + }); + } + }); + Oxide::Sentry::sentry_span(t, "update", "Update current state", [this]{ + update(); + }); + Oxide::Sentry::sentry_span(t, "timer", "Setup timer", [this]{ + timer = new QTimer(this); + timer->setSingleShot(false); + timer->setInterval(3 * 1000); // 3 seconds + timer->moveToThread(qApp->thread()); + connect(timer, &QTimer::timeout, this, QOverload<>::of(&PowerAPI::update)); + timer->start(); + }); + }); } ~PowerAPI(){ qDebug() << "Killing timer"; diff --git a/applications/system-service/screenapi.h b/applications/system-service/screenapi.h index b679ce09f..fad816fe4 100644 --- a/applications/system-service/screenapi.h +++ b/applications/system-service/screenapi.h @@ -13,11 +13,11 @@ #include #include #include +#include #include "apibase.h" #include "mxcfb.h" #include "screenshot.h" -#include "devicesettings.h" #define DISPLAYWIDTH 1404 #define DISPLAYHEIGHT 1872 @@ -41,14 +41,22 @@ class ScreenAPI : public APIBase { return instance; } ScreenAPI(QObject* parent) : APIBase(parent), m_screenshots(), m_enabled(false) { - qDBusRegisterMetaType>(); - mkdirs("/home/root/screenshots/"); - singleton(this); - QDir dir("/home/root/screenshots/"); - dir.setNameFilters(QStringList() << "*.png"); - for(auto entry : dir.entryInfoList()){ - addScreenshot(entry.filePath()); - } + Oxide::Sentry::sentry_transaction("screen", "init", [this](Oxide::Sentry::Transaction* t){ + qDBusRegisterMetaType>(); + Oxide::Sentry::sentry_span(t, "mkdirs", "Create screenshots directory", [this]{ + mkdirs("/home/root/screenshots/"); + }); + Oxide::Sentry::sentry_span(t, "singleton", "Setup singleton", [this]{ + singleton(this); + }); + Oxide::Sentry::sentry_span(t, "screenshots", "Load existing screenshots", [this]{ + QDir dir("/home/root/screenshots/"); + dir.setNameFilters(QStringList() << "*.png"); + for(auto entry : dir.entryInfoList()){ + addScreenshot(entry.filePath()); + } + }); + }); } ~ScreenAPI(){} void setEnabled(bool enabled){ @@ -92,13 +100,16 @@ class ScreenAPI : public APIBase { qDebug() << "Image data invalid" << path; return false; } - auto size = EPFrameBuffer::framebuffer()->size(); - QRect rect(0, 0, size.width(), size.height()); - QPainter painter(EPFrameBuffer::framebuffer()); - painter.drawImage(rect, img); - painter.end(); - EPFrameBuffer::sendUpdate(rect, EPFrameBuffer::HighQualityGrayscale, EPFrameBuffer::FullUpdate, true); - EPFrameBuffer::waitForLastUpdate(); + Oxide::Sentry::sentry_transaction("screen", "drawFullscrenImage", [img, path](Oxide::Sentry::Transaction* t){ + Q_UNUSED(t); + auto size = EPFrameBuffer::framebuffer()->size(); + QRect rect(0, 0, size.width(), size.height()); + QPainter painter(EPFrameBuffer::framebuffer()); + painter.drawImage(rect, img); + painter.end(); + EPFrameBuffer::sendUpdate(rect, EPFrameBuffer::HighQualityGrayscale, EPFrameBuffer::FullUpdate, true); + EPFrameBuffer::waitForLastUpdate(); + }); return true; } @@ -149,25 +160,32 @@ public slots: QMutex mutex; Screenshot* addScreenshot(QString filePath){ - auto path = QString(OXIDE_SERVICE_PATH "/screenshots/") + QFileInfo(filePath).completeBaseName().remove('-').remove('.'); - auto instance = new Screenshot(path, filePath, this); - m_screenshots.append(instance); - connect(instance, &Screenshot::removed, [=]{ - if(m_enabled){ - emit screenshotRemoved(instance->qPath()); - } - m_screenshots.removeAll(instance); - delete instance; - }); - connect(instance, &Screenshot::modified, [=]{ - if(m_enabled){ - emit screenshotModified(instance->qPath()); - } + Screenshot* instance; + Oxide::Sentry::sentry_transaction("screen", "addScreenshot", [this, filePath, &instance](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_span(t, "screenshot", "Create screenshot", [this, filePath, &instance]{ + auto path = QString(OXIDE_SERVICE_PATH "/screenshots/") + QFileInfo(filePath).completeBaseName().remove('-').remove('.'); + instance = new Screenshot(path, filePath, this); + m_screenshots.append(instance); + }); + Oxide::Sentry::sentry_span(t, "events", "Connect and emit events", [this, &instance]{ + connect(instance, &Screenshot::removed, [=]{ + if(m_enabled){ + emit screenshotRemoved(instance->qPath()); + } + m_screenshots.removeAll(instance); + delete instance; + }); + connect(instance, &Screenshot::modified, [=]{ + if(m_enabled){ + emit screenshotModified(instance->qPath()); + } + }); + if(m_enabled){ + instance->registerPath(); + emit screenshotAdded(instance->qPath()); + } + }); }); - if(m_enabled){ - instance->registerPath(); - emit screenshotAdded(instance->qPath()); - } return instance; } void mkdirs(const QString& path, mode_t mode = 0700){ diff --git a/applications/system-service/screenshot.h b/applications/system-service/screenshot.h index 93a6bcdc2..30934a2cf 100644 --- a/applications/system-service/screenshot.h +++ b/applications/system-service/screenshot.h @@ -7,8 +7,7 @@ #include #include - -#include "dbussettings.h" +#include "../../shared/liboxide/liboxide.h" class Screenshot : public QObject{ Q_OBJECT diff --git a/applications/system-service/systemapi.cpp b/applications/system-service/systemapi.cpp index 7658f40c7..0f599d39d 100644 --- a/applications/system-service/systemapi.cpp +++ b/applications/system-service/systemapi.cpp @@ -1,11 +1,11 @@ +#include + #include "systemapi.h" #include "appsapi.h" #include "powerapi.h" #include "wifiapi.h" #include "notificationapi.h" -#include "devicesettings.h" -#ifdef DEBUG QDebug operator<<(QDebug debug, const Touch& touch){ QDebugStateSaver saver(debug); debug.nospace() << touch.debugString().c_str(); @@ -16,64 +16,81 @@ QDebug operator<<(QDebug debug, Touch* touch){ debug.nospace() << touch->debugString().c_str(); return debug.maybeSpace(); } -#endif void SystemAPI::PrepareForSleep(bool suspending){ auto device = deviceSettings.getDeviceType(); if(suspending){ - qDebug() << "Preparing for suspend..."; - wifiAPI->stopUpdating(); - emit deviceSuspending(); - appsAPI->recordPreviousApplication(); - auto path = appsAPI->currentApplicationNoSecurityCheck(); - if(path.path() != "/"){ - resumeApp = appsAPI->getApplication(path); - resumeApp->pauseNoSecurityCheck(false); - }else{ - resumeApp = nullptr; - } - if(QFile::exists("/usr/share/remarkable/sleeping.png")){ - screenAPI->drawFullscreenImage("/usr/share/remarkable/sleeping.png"); - }else{ - screenAPI->drawFullscreenImage("/usr/share/remarkable/suspended.png"); - } - buttonHandler->setEnabled(false); - if(device == DeviceSettings::DeviceType::RM2){ - if(wifiAPI->state() != WifiAPI::State::Off){ - wifiWasOn = true; - wifiAPI->disable(); - } - system("rmmod brcmfmac"); - } - releaseSleepInhibitors(); - qDebug() << "Suspending..."; + Oxide::Sentry::sentry_transaction("system", "suspend", [this, device](Oxide::Sentry::Transaction* t){ + qDebug() << "Preparing for suspend..."; + Oxide::Sentry::sentry_span(t, "prepare", "Prepare for suspend", [this]{ + wifiAPI->stopUpdating(); + emit deviceSuspending(); + appsAPI->recordPreviousApplication(); + auto path = appsAPI->currentApplicationNoSecurityCheck(); + if(path.path() != "/"){ + resumeApp = appsAPI->getApplication(path); + resumeApp->pauseNoSecurityCheck(false); + }else{ + resumeApp = nullptr; + } + }); + Oxide::Sentry::sentry_span(t, "screen", "Update screen with suspend image", []{ + if(QFile::exists("/usr/share/remarkable/sleeping.png")){ + screenAPI->drawFullscreenImage("/usr/share/remarkable/sleeping.png"); + }else{ + screenAPI->drawFullscreenImage("/usr/share/remarkable/suspended.png"); + } + }); + Oxide::Sentry::sentry_span(t, "disable", "Disable various services", [this, device]{ + buttonHandler->setEnabled(false); + if(device == Oxide::DeviceSettings::DeviceType::RM2){ + if(wifiAPI->state() != WifiAPI::State::Off){ + wifiWasOn = true; + wifiAPI->disable(); + } + system("rmmod brcmfmac"); + } + releaseSleepInhibitors(); + }); + qDebug() << "Suspending..."; + }); }else{ - inhibitSleep(); - qDebug() << "Resuming..."; - QCoreApplication::processEvents(QEventLoop::AllEvents, 100); - auto lockscreenApp = appsAPI->getApplication(appsAPI->lockscreenApplication()); - if(lockscreenApp != nullptr){ - resumeApp = lockscreenApp; - } - if(resumeApp == nullptr){ - resumeApp = appsAPI->getApplication(appsAPI->startupApplication()); - } - if(resumeApp != nullptr){ - resumeApp->resumeNoSecurityCheck(); - } - buttonHandler->setEnabled(true); - emit deviceResuming(); - if(m_autoSleep && powerAPI->chargerState() != PowerAPI::ChargerConnected){ - qDebug() << "Suspend timer re-enabled due to resume"; - suspendTimer.start(m_autoSleep * 60 * 1000); - } - if(device == DeviceSettings::DeviceType::RM2){ - system("modprobe brcmfmac"); - if(wifiWasOn){ - wifiAPI->enable(); - } - } - wifiAPI->resumeUpdating(); + Oxide::Sentry::sentry_transaction("system", "resume", [this, device](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_span(t, "inhibit", "Inhibit sleep", [this]{ + inhibitSleep(); + }); + qDebug() << "Resuming..."; + Oxide::Sentry::sentry_span(t, "process", "Process events", []{ + QCoreApplication::processEvents(QEventLoop::AllEvents, 100); + }); + Oxide::Sentry::sentry_span(t, "resume", "Resume running application or go to lockscreen", [this]{ + auto lockscreenApp = appsAPI->getApplication(appsAPI->lockscreenApplication()); + if(lockscreenApp != nullptr){ + resumeApp = lockscreenApp; + } + if(resumeApp == nullptr){ + resumeApp = appsAPI->getApplication(appsAPI->startupApplication()); + } + if(resumeApp != nullptr){ + resumeApp->resumeNoSecurityCheck(); + } + }); + Oxide::Sentry::sentry_span(t, "enable", "Enable various services", [this, device]{ + buttonHandler->setEnabled(true); + emit deviceResuming(); + if(m_autoSleep && powerAPI->chargerState() != PowerAPI::ChargerConnected){ + qDebug() << "Suspend timer re-enabled due to resume"; + suspendTimer.start(m_autoSleep * 60 * 1000); + } + if(device == Oxide::DeviceSettings::DeviceType::RM2){ + system("modprobe brcmfmac"); + if(wifiWasOn){ + wifiAPI->enable(); + } + } + wifiAPI->resumeUpdating(); + }); + }); } } void SystemAPI::setAutoSleep(int autoSleep){ diff --git a/applications/system-service/systemapi.h b/applications/system-service/systemapi.h index a66cd17ae..daaf152a3 100644 --- a/applications/system-service/systemapi.h +++ b/applications/system-service/systemapi.h @@ -6,6 +6,7 @@ #include #include #include +#include #include "apibase.h" #include "buttonhandler.h" @@ -55,10 +56,8 @@ struct Touch { return ""; } }; -#ifdef DEBUG QDebug operator<<(QDebug debug, const Touch& touch); QDebug operator<<(QDebug debug, Touch* touch); -#endif Q_DECLARE_METATYPE(Touch) Q_DECLARE_METATYPE(input_event) @@ -88,53 +87,84 @@ class SystemAPI : public APIBase { touches(), swipeStates(), swipeLengths() { - for(short i = Right; i <= Down; i++){ - swipeStates[(SwipeDirection)i] = true; - } - settings.sync(); - singleton(this); - this->resumeApp = nullptr; - systemd = new Manager("org.freedesktop.login1", "/org/freedesktop/login1", QDBusConnection::systemBus(), this); - // Handle Systemd signals - connect(systemd, &Manager::PrepareForSleep, this, &SystemAPI::PrepareForSleep); - connect(&suspendTimer, &QTimer::timeout, this, &SystemAPI::timeout); - - auto autoSleep = settings.value("autoSleep", 1).toInt(); - m_autoSleep = autoSleep; - if(autoSleep < 0) { - m_autoSleep = 0; + Oxide::Sentry::sentry_transaction("system", "init", [this](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_span(t, "settings", "Sync settings", [this](Oxide::Sentry::Span* s){ + Oxide::Sentry::sentry_span(s, "swipes", "Default swipe values", [this]{ + for(short i = Right; i <= Down; i++){ + swipeStates[(SwipeDirection)i] = true; + } + }); + Oxide::Sentry::sentry_span(s, "sync", "Sync settings", [this]{ + settings.sync(); + }); + Oxide::Sentry::sentry_span(s, "singleton", "Instantiate singleton", [this]{ + singleton(this); + }); + resumeApp = nullptr; + }); + Oxide::Sentry::sentry_span(t, "systemd", "Connect to SystemD DBus", [this](Oxide::Sentry::Span* s){ + Oxide::Sentry::sentry_span(s, "manager", "Create manager object", [this]{ + systemd = new Manager("org.freedesktop.login1", "/org/freedesktop/login1", QDBusConnection::systemBus(), this); + }); + Oxide::Sentry::sentry_span(s, "connect", "Connect to signals", [this]{ + // Handle Systemd signals + connect(systemd, &Manager::PrepareForSleep, this, &SystemAPI::PrepareForSleep); + connect(&suspendTimer, &QTimer::timeout, this, &SystemAPI::timeout); + }); + }); + Oxide::Sentry::sentry_span(t, "autoSleep", "Setup automatic sleep", [this](Oxide::Sentry::Span* s){ + auto autoSleep = settings.value("autoSleep", 1).toInt(); + m_autoSleep = autoSleep; + if(autoSleep < 0) { + m_autoSleep = 0; - }else if(autoSleep > 10){ - m_autoSleep = 10; - } - if(autoSleep != m_autoSleep){ - m_autoSleep = autoSleep; - settings.setValue("autoSleep", autoSleep); - settings.sync(); - emit autoSleepChanged(autoSleep); - } - qDebug() << "Auto Sleep" << autoSleep; - if(m_autoSleep){ - suspendTimer.start(m_autoSleep * 60 * 1000); - }else if(!m_autoSleep){ - suspendTimer.stop(); - } - settings.beginReadArray("swipes"); - for(short i = Right; i <= Down; i++){ - settings.setArrayIndex(i); - swipeStates[(SwipeDirection)i] = settings.value("enabled", true).toBool(); - swipeLengths[(SwipeDirection)i] = settings.value("length", 30).toInt(); - } - settings.endArray(); - // Ask Systemd to tell us nicely when we are about to suspend or resume - inhibitSleep(); - inhibitPowerOff(); - qRegisterMetaType(); - connect(touchHandler, &DigitizerHandler::activity, this, &SystemAPI::activity); - connect(touchHandler, &DigitizerHandler::inputEvent, this, &SystemAPI::touchEvent); - connect(wacomHandler, &DigitizerHandler::activity, this, &SystemAPI::activity); - connect(wacomHandler, &DigitizerHandler::inputEvent, this, &SystemAPI::penEvent); - qDebug() << "System API ready to use"; + }else if(autoSleep > 10){ + m_autoSleep = 10; + } + if(autoSleep != m_autoSleep){ + Oxide::Sentry::sentry_span(s, "update", "Update value", [this, autoSleep]{ + m_autoSleep = autoSleep; + settings.setValue("autoSleep", autoSleep); + settings.sync(); + emit autoSleepChanged(autoSleep); + }); + } + qDebug() << "Auto Sleep" << autoSleep; + Oxide::Sentry::sentry_span(s, "timer", "Setup timer", [this]{ + if(m_autoSleep){ + suspendTimer.start(m_autoSleep * 60 * 1000); + }else if(!m_autoSleep){ + suspendTimer.stop(); + } + }); + }); + Oxide::Sentry::sentry_span(t, "swipes", "Load swipe settings", [this]{ + settings.beginReadArray("swipes"); + for(short i = Right; i <= Down; i++){ + settings.setArrayIndex(i); + swipeStates[(SwipeDirection)i] = settings.value("enabled", true).toBool(); + swipeLengths[(SwipeDirection)i] = settings.value("length", 30).toInt(); + } + settings.endArray(); + }); + // Ask Systemd to tell us nicely when we are about to suspend or resume + Oxide::Sentry::sentry_span(t, "inhibit", "Inhibit sleep and power off", [this](Oxide::Sentry::Span* s){ + Oxide::Sentry::sentry_span(s, "inhibitSleep", "Inhibit sleep", [this]{ + inhibitSleep(); + }); + Oxide::Sentry::sentry_span(s, "inhibitPowerOff", "Inhibit power off", [this]{ + inhibitPowerOff(); + }); + }); + qRegisterMetaType(); + Oxide::Sentry::sentry_span(t, "input", "Connect input events", [this]{ + connect(touchHandler, &DigitizerHandler::activity, this, &SystemAPI::activity); + connect(touchHandler, &DigitizerHandler::inputEvent, this, &SystemAPI::touchEvent); + connect(wacomHandler, &DigitizerHandler::activity, this, &SystemAPI::activity); + connect(wacomHandler, &DigitizerHandler::inputEvent, this, &SystemAPI::penEvent); + }); + qDebug() << "System API ready to use"; + }); } ~SystemAPI(){ qDebug() << "Removing all inhibitors"; @@ -560,7 +590,7 @@ private slots: return; } int offset = 20; - if(deviceSettings.getDeviceType() == DeviceSettings::RM2){ + if(deviceSettings.getDeviceType() == Oxide::DeviceSettings::RM2){ offset = 40; } if(touch->y <= offset){ @@ -568,13 +598,13 @@ private slots: }else if(touch->y > (deviceSettings.getTouchHeight() - offset)){ swipeDirection = Down; }else if(touch->x <= offset){ - if(deviceSettings.getDeviceType() == DeviceSettings::RM2){ + if(deviceSettings.getDeviceType() == Oxide::DeviceSettings::RM2){ swipeDirection = Right; }else{ swipeDirection = Left; } }else if(touch->x > (deviceSettings.getTouchWidth() - offset)){ - if(deviceSettings.getDeviceType() == DeviceSettings::RM2){ + if(deviceSettings.getDeviceType() == Oxide::DeviceSettings::RM2){ swipeDirection = Left; }else{ swipeDirection = Right; @@ -644,7 +674,7 @@ private slots: } emit topAction(); }else if(swipeDirection == Right || swipeDirection == Left){ - auto isRM2 = deviceSettings.getDeviceType() == DeviceSettings::RM2; + auto isRM2 = deviceSettings.getDeviceType() == Oxide::DeviceSettings::RM2; auto invalidLeft = !swipeStates[Left] || touch->x < location.x() || touch->x - startLocation.x() < swipeLengths[Left]; auto invalidRight = !swipeStates[Right] || touch->x > location.x() || startLocation.x() - touch->x < swipeLengths[Right]; if(swipeDirection == Right && (isRM2 ? invalidLeft : invalidRight)){ diff --git a/applications/system-service/tarnish.pro b/applications/system-service/tarnish.pro index 9b7fa7ff8..3be093e26 100644 --- a/applications/system-service/tarnish.pro +++ b/applications/system-service/tarnish.pro @@ -5,6 +5,9 @@ CONFIG += console CONFIG -= app_bundle CONFIG += precompile_header_c +DEFINES += QT_DEPRECATED_WARNINGS +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + QMAKE_CFLAGS += -std=c99 SOURCES += \ @@ -21,8 +24,7 @@ SOURCES += \ systemapi.cpp \ wlan.cpp \ wpa_supplicant.cpp \ - main.cpp \ - ../../shared/devicesettings.cpp + main.cpp TARGET=tarnish @@ -45,19 +47,14 @@ system(qdbusxml2cpp -N -p wpa_supplicant.h:wpa_supplicant.cpp fi.w1.wpa_supplica DBUS_INTERFACES += org.freedesktop.login1.xml -INCLUDEPATH += ../../shared -LIBS += -L$$PWD/../../shared/ -lqsgepaper -INCLUDEPATH += $$PWD/../../shared -DEPENDPATH += $$PWD/../../shared - HEADERS += \ + ../../shared/liboxide/liboxide.h \ apibase.h \ application.h \ appsapi.h \ bss.h \ buttonhandler.h \ dbusservice.h \ - dbussettings.h \ digitizerhandler.h \ event_device.h \ fifohandler.h \ @@ -73,17 +70,15 @@ HEADERS += \ systemapi.h \ wifiapi.h \ wlan.h \ - wpa_supplicant.h \ - ../../shared/devicesettings.h \ - ../../shared/signalhandler.h - -linux-oe-g++ { - LIBS += -lqsgepaper - LIBS += -lpng16 - LIBS += -lsystemd - LIBS += -lz -} + wpa_supplicant.h +LIBS += -lpng16 +LIBS += -lsystemd +LIBS += -lz + +LIBS += -L$$PWD/../../shared/ -lqsgepaper +INCLUDEPATH += $$PWD/../../shared +DEPENDPATH += $$PWD/../../shared QMAKE_POST_LINK += sh $$_PRO_FILE_PWD_/generate_xml.sh DISTFILES += \ @@ -93,4 +88,20 @@ DISTFILES += \ generate_xml.sh \ org.freedesktop.login1.xml -RESOURCES += +exists($$PWD/../../.build/sentry) { + LIBS += -L$$PWD/../../.build/sentry/lib -lsentry -ldl -lcurl -lbreakpad_client + INCLUDEPATH += $$PWD/../../.build/sentry/include + DEPENDPATH += $$PWD/../../.build/sentry/lib + + library.files = ../../.build/sentry/libsentry.so + library.path = /opt/lib + INSTALLS += library +} + +LIBS += -L$$PWD/../../.build/liboxide -lliboxide +INCLUDEPATH += $$PWD/../../shared/liboxide +DEPENDPATH += $$PWD/../../shared/liboxide + +QMAKE_RPATHDIR += /lib /usr/lib /opt/lib /opt/usr/lib + +VERSION = 2.3 diff --git a/applications/system-service/wifiapi.h b/applications/system-service/wifiapi.h index caac19430..227df0f82 100644 --- a/applications/system-service/wifiapi.h +++ b/applications/system-service/wifiapi.h @@ -20,7 +20,7 @@ class WifiAPI : public APIBase { Q_OBJECT Q_CLASSINFO("D-Bus Interface", OXIDE_WIFI_INTERFACE) Q_PROPERTY(int state READ state NOTIFY stateChanged) - Q_PROPERTY(QSet blobs READ blobs) + Q_PROPERTY(QStringList blobs READ blobs) Q_PROPERTY(QList bSSs READ bSSs) Q_PROPERTY(int link READ link NOTIFY linkChanged) Q_PROPERTY(int rssi READ rssi NOTIFY rssiChanged) @@ -38,7 +38,6 @@ class WifiAPI : public APIBase { WifiAPI(QObject* parent) : APIBase(parent), m_enabled(false), - settings("/home/root/.config/remarkable/xochitl.conf", QSettings::IniFormat), wlans(), networks(), m_state(Unknown), @@ -46,102 +45,133 @@ class WifiAPI : public APIBase { m_link(0), m_scanning(false) { - singleton(this); - QDir dir("/sys/class/net"); - qDebug() << "Looking for wireless devices..."; - for(auto path : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable)){ - qDebug() << (" Checking " + path + "...").toStdString().c_str(); - Wlan* item = new Wlan(dir.path() + "/" + path, this); - if(!item->hasDirectory("wireless")){ - qDebug() << " Not a wireless device"; - continue; - } - qDebug() << " Wireless device found!"; - wlans.append(item); - connect(item, &Wlan::BSSAdded, this, &WifiAPI::BSSAdded, Qt::QueuedConnection); - connect(item, &Wlan::BSSRemoved, this, &WifiAPI::BSSRemoved, Qt::QueuedConnection); - connect(item, &Wlan::BlobAdded, this, &WifiAPI::BlobAdded, Qt::QueuedConnection); - connect(item, &Wlan::BlobRemoved, this, &WifiAPI::BlobRemoved, Qt::QueuedConnection); - connect(item, &Wlan::NetworkAdded, this, &WifiAPI::NetworkAdded, Qt::QueuedConnection); - connect(item, &Wlan::NetworkRemoved, this, &WifiAPI::NetworkRemoved, Qt::QueuedConnection); - connect(item, &Wlan::NetworkSelected, this, &WifiAPI::NetworkSelected, Qt::QueuedConnection); - connect(item, &Wlan::PropertiesChanged, this, &WifiAPI::InterfacePropertiesChanged, Qt::QueuedConnection); - connect(item, &Wlan::ScanDone, this, &WifiAPI::ScanDone, Qt::QueuedConnection); - } - QDBusConnection bus = QDBusConnection::systemBus(); - if(!bus.isConnected()){ - qWarning("Failed to connect to system bus."); - throw QException(); - } - validateSupplicant(); - supplicant = new Wpa_Supplicant(WPA_SUPPLICANT_SERVICE, WPA_SUPPLICANT_SERVICE_PATH, bus); - connect(supplicant, &Wpa_Supplicant::InterfaceAdded, this, &WifiAPI::InterfaceAdded); - connect(supplicant, &Wpa_Supplicant::InterfaceRemoved, this, &WifiAPI::InterfaceRemoved); - connect(supplicant, &Wpa_Supplicant::PropertiesChanged, this, &WifiAPI::PropertiesChanged); - for(auto wlan : wlans){ - auto iface = wlan->iface(); - auto reply = (QDBusReply)supplicant->GetInterface(iface); - if(!reply.isValid() || reply.value().path() == "/"){ - QVariantMap args; - args.insert("Ifname", iface); - reply = supplicant->CreateInterface(args); - } - if(reply.isValid()){ - wlan->setInterface(reply.value().path()); - auto interface = wlan->interface(); - for(auto path : interface->networks()){ - bool found = false; - for(auto network : networks){ - if(network->path() == path.path()){ - found = true; - network->addNetwork(path.path(), interface); - break; - } - } - if(!found){ - INetwork inetwork(WPA_SUPPLICANT_SERVICE, path.path(), bus, interface); - auto properties = inetwork.properties(); - auto network = new Network(getPath("network", properties["ssid"].toString()), properties, this); - network->setEnabled(true); - network->addNetwork(path.path(), interface); - networks.append(network); + Oxide::Sentry::sentry_transaction("wifi", "init", [this](Oxide::Sentry::Transaction* t){ + Oxide::Sentry::sentry_span(t, "singleton", "Setup singleton", [this]{ + singleton(this); + }); + Oxide::Sentry::sentry_span(t, "sysfs", "Finding wireless devices", [this](Oxide::Sentry::Span* s){ + QDir dir("/sys/class/net"); + qDebug() << "Looking for wireless devices..."; + for(auto path : dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot | QDir::Readable)){ + qDebug() << (" Checking " + path + "...").toStdString().c_str(); + Wlan* item = new Wlan(dir.path() + "/" + path, this); + if(!item->hasDirectory("wireless")){ + qDebug() << " Not a wireless device"; + continue; } + qDebug() << " Wireless device found!"; + Oxide::Sentry::sentry_span(s, path.toStdString(), "Connect to wireless device", [this, item]{ + wlans.append(item); + connect(item, &Wlan::BSSAdded, this, &WifiAPI::BSSAdded, Qt::QueuedConnection); + connect(item, &Wlan::BSSRemoved, this, &WifiAPI::BSSRemoved, Qt::QueuedConnection); + connect(item, &Wlan::BlobAdded, this, &WifiAPI::BlobAdded, Qt::QueuedConnection); + connect(item, &Wlan::BlobRemoved, this, &WifiAPI::BlobRemoved, Qt::QueuedConnection); + connect(item, &Wlan::NetworkAdded, this, &WifiAPI::NetworkAdded, Qt::QueuedConnection); + connect(item, &Wlan::NetworkRemoved, this, &WifiAPI::NetworkRemoved, Qt::QueuedConnection); + connect(item, &Wlan::NetworkSelected, this, &WifiAPI::NetworkSelected, Qt::QueuedConnection); + connect(item, &Wlan::PropertiesChanged, this, &WifiAPI::InterfacePropertiesChanged, Qt::QueuedConnection); + connect(item, &Wlan::ScanDone, this, &WifiAPI::ScanDone, Qt::QueuedConnection); + }); } - for(auto path : wlan->interface()->bSSs()){ - auto ibss = new IBSS(WPA_SUPPLICANT_SERVICE, path.path(), bus, wlan->interface()); - bool found = false; - auto bssid = ibss->bSSID(); - for(auto bss : bsss){ - if(bss->bssid() == bssid){ - found = true; - bss->addBSS(path.path()); - break; - } + }); + Oxide::Sentry::sentry_span(t, "wpa_supplicant", "Connect to the wpa_supplicant", [this](Oxide::Sentry::Span* s){ + Oxide::Sentry::Span* span = Oxide::Sentry::start_span(s, "connect", "Connect to DBus interface"); + QDBusConnection bus = QDBusConnection::systemBus(); + if(!bus.isConnected()){ + qWarning("Failed to connect to system bus."); + throw QException(); + } + validateSupplicant(); + supplicant = new Wpa_Supplicant(WPA_SUPPLICANT_SERVICE, WPA_SUPPLICANT_SERVICE_PATH, bus); + connect(supplicant, &Wpa_Supplicant::InterfaceAdded, this, &WifiAPI::InterfaceAdded); + connect(supplicant, &Wpa_Supplicant::InterfaceRemoved, this, &WifiAPI::InterfaceRemoved); + connect(supplicant, &Wpa_Supplicant::PropertiesChanged, this, &WifiAPI::PropertiesChanged); + Oxide::Sentry::stop_span(span); + delete span; + Oxide::Sentry::sentry_span(s, "wlans", "Setting up wlan instances", [this, bus](Oxide::Sentry::Span* s){ + for(auto wlan : wlans){ + Oxide::Sentry::sentry_span(s, wlan->iface().toStdString(), "Load current networks and BSSs", [this, bus, wlan]{ + auto iface = wlan->iface(); + auto reply = (QDBusReply)supplicant->GetInterface(iface); + if(!reply.isValid() || reply.value().path() == "/"){ + QVariantMap args; + args.insert("Ifname", iface); + reply = supplicant->CreateInterface(args); + } + if(reply.isValid()){ + wlan->setInterface(reply.value().path()); + auto interface = wlan->interface(); + for(auto path : interface->networks()){ + bool found = false; + for(auto network : networks){ + if(network->path() == path.path()){ + found = true; + network->addNetwork(path.path(), interface); + break; + } + } + if(!found){ + INetwork inetwork(WPA_SUPPLICANT_SERVICE, path.path(), bus, interface); + auto properties = inetwork.properties(); + auto network = new Network(getPath("network", properties["ssid"].toString()), properties, this); + network->setEnabled(true); + network->addNetwork(path.path(), interface); + networks.append(network); + } + } + for(auto path : wlan->interface()->bSSs()){ + auto ibss = new IBSS(WPA_SUPPLICANT_SERVICE, path.path(), bus, wlan->interface()); + bool found = false; + auto bssid = ibss->bSSID(); + for(auto bss : bsss){ + if(bss->bssid() == bssid){ + found = true; + bss->addBSS(path.path()); + break; + } + } + if(!found){ + auto bss = new BSS(getPath("bss", bssid), ibss, this); + bsss.append(bss); + }else{ + ibss->deleteLater(); + } + } + } + }); } - if(!found){ - auto bss = new BSS(getPath("bss", bssid), ibss, this); - bsss.append(bss); + }); + }); + Oxide::Sentry::sentry_span(t, "load", "Load state", [this](Oxide::Sentry::Span* s){ + Oxide::Sentry::sentry_span(s, "timer", "Setup timer", [this]{ + timer = new QTimer(this); + timer->setSingleShot(false); + timer->setInterval(3 * 1000); // 3 seconds + timer->moveToThread(qApp->thread()); + connect(timer, &QTimer::timeout, this, QOverload<>::of(&WifiAPI::update)); + }); + Oxide::Sentry::sentry_span(s, "networks", "Load networks from disk", [this]{ + loadNetworks(); + }); + Oxide::Sentry::sentry_span(s, "state", "Syncronize state with settings", [this]{ + m_state = getCurrentState(); + if(xochitlSettings.wifion()){ + enable(); }else{ - ibss->deleteLater(); + disable(); } - } - } - } - timer = new QTimer(this); - timer->setSingleShot(false); - timer->setInterval(3 * 1000); // 3 seconds - timer->moveToThread(qApp->thread()); - connect(timer, &QTimer::timeout, this, QOverload<>::of(&WifiAPI::update)); - loadNetworks(); - m_state = getCurrentState(); - if(settings.value("wifion").toBool()){ - enable(); - }else{ - disable(); - } - update(); - setEnabled(m_enabled); - timer->start(); + }); + Oxide::Sentry::sentry_span(s, "update", "Update current state", [this]{ + update(); + }); + Oxide::Sentry::sentry_span(s, "setEnabled", "Enable/disable API objects", [this]{ + setEnabled(m_enabled); + }); + Oxide::Sentry::sentry_span(s, "timer", "Start timer", [this]{ + timer->start(); + }); + }); + }); } ~WifiAPI(){ qDebug() << "Unregistering all networks"; @@ -212,7 +242,7 @@ class WifiAPI : public APIBase { continue; } } - settings.setValue("wifion", true); + xochitlSettings.set_wifion(true); validateSupplicant(); auto state = getCurrentState(); m_state = state; @@ -234,8 +264,7 @@ class WifiAPI : public APIBase { } } flushBSSCache(0); - settings.setValue("wifion", false); - settings.sync(); + xochitlSettings.set_wifion(false); } Q_INVOKABLE QDBusObjectPath addNetwork(QString ssid, QVariantMap properties){ if(!hasPermission("wifi")){ @@ -406,15 +435,15 @@ class WifiAPI : public APIBase { } return QByteArray(); } - QSet blobs(){ + QStringList blobs(){ QSet result; if(!hasPermission("wifi")){ - return result; + return result.values(); } for(auto wlan : wlans){ result.unite(wlan->blobs()); } - return result; + return result.values(); } QList bSSs(){ QList result; @@ -445,6 +474,9 @@ class WifiAPI : public APIBase { } int result = -100; for(auto wlan : wlans){ + if(!wlan->isUp()){ + continue; + } int rssi = wlan->rssi(); if(result < rssi){ result = rssi; @@ -657,7 +689,6 @@ private slots: private: bool m_enabled; QTimer* timer; - QSettings settings; QList wlans; QList networks; int m_state; @@ -703,14 +734,7 @@ private slots: void loadNetworks(){ qDebug() << "Loading networks from settings..."; - settings.sync(); - settings.beginGroup("wifinetworks"); - QList registeredNetworks; - for(const QString& childKey : settings.allKeys()){ - QVariantMap network = settings.value(childKey).toMap(); - registeredNetworks.append(network); - } - settings.endGroup(); + QList> registeredNetworks = xochitlSettings.wifinetworks().values(); qDebug() << "Registering networks..."; for(auto registration : registeredNetworks){ bool found = false; @@ -750,9 +774,8 @@ private slots: } void update(){ - settings.sync(); auto state = getCurrentState(); - bool enabled = settings.value("wifion").toBool(); + bool enabled = xochitlSettings.wifion(); if(enabled && state == Off){ enable(); state = getCurrentState(); diff --git a/applications/system-service/wlan.h b/applications/system-service/wlan.h index eb4e282ee..f841a986c 100644 --- a/applications/system-service/wlan.h +++ b/applications/system-service/wlan.h @@ -3,7 +3,8 @@ #include -#include "dbussettings.h" +#include + #include "sysobject.h" #include "supplicant.h" diff --git a/applications/task-switcher/appitem.cpp b/applications/task-switcher/appitem.cpp index 8f8ccd8aa..df8f68e56 100755 --- a/applications/task-switcher/appitem.cpp +++ b/applications/task-switcher/appitem.cpp @@ -31,7 +31,7 @@ void AppItem::stop(){ qWarning() << "Application instance is not valid"; return; } - app->stop(); + QDBusPendingReply reply = app->stop(); } Application* AppItem::getApp(){ if(app != nullptr){ diff --git a/applications/task-switcher/controller.h b/applications/task-switcher/controller.h index 4ccb02fe0..cbc5f85ea 100644 --- a/applications/task-switcher/controller.h +++ b/applications/task-switcher/controller.h @@ -4,10 +4,12 @@ #include #include #include +#include +#include #include - -#include "dbussettings.h" +#include +#include #include "dbusservice_interface.h" #include "screenapi_interface.h" @@ -15,10 +17,11 @@ #include "application_interface.h" #include "screenprovider.h" -#include "signalhandler.h" #include "appitem.h" using namespace codes::eeems::oxide1; +using namespace Oxide::Sentry; +using namespace Oxide; #define CORRUPT_SETTINGS_VERSION 1 @@ -32,6 +35,7 @@ class Controller : public QObject { public: Controller(QObject* parent, ScreenProvider* screenProvider) : QObject(parent), settings(this), applications() { + blankImage = new QImage(qApp->primaryScreen()->geometry().size(), QImage::Format_Mono); this->screenProvider = screenProvider; auto bus = QDBusConnection::systemBus(); qDebug() << "Waiting for tarnish to start up..."; @@ -73,7 +77,7 @@ class Controller : public QObject { if(version < CORRUPT_SETTINGS_VERSION){ migrate(&settings, version); } - saveScreen(); + updateImage(); } ~Controller(){} @@ -148,7 +152,7 @@ class Controller : public QObject { if(!appItem->ok()){ qDebug() << "Invalid item" << appItem->property("name").toString(); applications.removeAll(appItem); - delete appItem; + appItem->deleteLater(); } } auto previousApplications = appsApi->previousApplications(); @@ -168,6 +172,15 @@ class Controller : public QObject { }); return applications; } + Q_INVOKABLE void breadcrumb(QString category, QString message, QString type = "default"){ +#ifdef SENTRY + sentry_breadcrumb(category.toStdString().c_str(), message.toStdString().c_str(), type.toStdString().c_str()); +#else + Q_UNUSED(category); + Q_UNUSED(message); + Q_UNUSED(type); +#endif + } AppItem* getApplication(QString name){ for(auto app : applications){ if(app->property("name").toString() == name){ @@ -188,9 +201,42 @@ class Controller : public QObject { } stateControllerUI->setProperty("state", state); } - void saveScreen(){ - qDebug() << "Saving screen for background..."; - screenProvider->updateImage(EPFrameBuffer::framebuffer()); + void updateImage(){ + qDebug() << "Updating background..."; + Oxide::Sentry::sentry_transaction("controller", "updateImage", [this](Oxide::Sentry::Transaction* t){ + QImage* img = nullptr; + Oxide::Sentry::sentry_span(t, "previousApplications", "Get image from previous application", [this, &img](Oxide::Sentry::Span* s){ + auto previousApplications = appsApi->previousApplications(); + while(img == nullptr && !previousApplications.isEmpty()){ + auto name = previousApplications.takeLast(); + Oxide::Sentry::sentry_span(s, name.toStdString(), "Load image from application", [this, &img, previousApplications, name]{ + auto path = ((QDBusObjectPath)appsApi->getApplicationPath(name)).path(); + if(path == "/"){ + qWarning() << "Unable to get save screen for" << name; + return; + } + auto bus = QDBusConnection::systemBus(); + Application app(OXIDE_SERVICE, path, bus, this); + auto data = app.screenCapture(); + auto image = QImage::fromData(data, "JPG"); + if(image.isNull()){ + qWarning() << "Image for " << name << " is corrupt, trying next application"; + return; + } + img = new QImage(image); + qDebug() << "Using save screen from " << name; + }); + } + }); + Oxide::Sentry::sentry_span(t, "update", "Update image", [this, img]{ + if(img != nullptr){ + screenProvider->updateImage(img); + return; + } + qWarning() << "No previous application. Using blank screen"; + screenProvider->updateImage(blankImage); + }); + }); } void setRoot(QObject* root){ this->root = root; } @@ -203,8 +249,8 @@ private slots: void sigUsr1(){ ::kill(tarnishPid(), SIGUSR1); qDebug() << "Sent to the foreground..."; - saveScreen(); setState("loading"); + updateImage(); } void sigUsr2(){ qDebug() << "Sent to the background..."; @@ -238,19 +284,15 @@ private slots: Apps* appsApi; QObject* root = nullptr; QObject* stateControllerUI = nullptr; - QObject* backgroundUI = nullptr; ScreenProvider* screenProvider; QList applications; + QImage* blankImage; int tarnishPid() { return api->tarnishPid(); } QObject* getStateControllerUI(){ stateControllerUI = root->findChild("stateController"); return stateControllerUI; } - QObject* getBackgroundUI(){ - backgroundUI = root->findChild("background"); - return backgroundUI; - } static void migrate(QSettings* settings, int fromVersion){ if(fromVersion != 0){ diff --git a/applications/task-switcher/corrupt.pro b/applications/task-switcher/corrupt.pro index 45a0a2c83..46ebd4ea9 100644 --- a/applications/task-switcher/corrupt.pro +++ b/applications/task-switcher/corrupt.pro @@ -5,25 +5,12 @@ QT += dbus CONFIG += c++11 CONFIG += qml_debug -# The following define makes your compiler emit warnings if you use -# any feature of Qt which as been marked deprecated (the exact warnings -# depend on your compiler). Please consult the documentation of the -# deprecated API in order to know how to port your code away from it. DEFINES += QT_DEPRECATED_WARNINGS - -# You can make your code fail to compile if it uses deprecated APIs. -# In order to do so, uncomment the following line. -#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 - -linux-oe-g++ { - LIBS += -lqsgepaper -} +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ appitem.cpp \ - main.cpp \ - ../../shared/devicesettings.cpp \ - ../../shared/eventfilter.cpp + main.cpp # Default rules for deployment. target.path = /opt/bin @@ -36,10 +23,6 @@ DBUS_INTERFACES += ../../interfaces/application.xml INCLUDEPATH += ../../shared HEADERS += \ - ../../shared/dbussettings.h \ - ../../shared/devicesettings.h \ - ../../shared/eventfilter.h \ - ../../shared/signalhandler.h \ appitem.h \ controller.h \ screenprovider.h @@ -50,3 +33,21 @@ RESOURCES += \ LIBS += -L$$PWD/../../shared/ -lqsgepaper INCLUDEPATH += $$PWD/../../shared DEPENDPATH += $$PWD/../../shared + +exists($$PWD/../../.build/sentry) { + LIBS += -L$$PWD/../../.build/sentry/lib -lsentry -ldl -lcurl -lbreakpad_client + INCLUDEPATH += $$PWD/../../.build/sentry/include + DEPENDPATH += $$PWD/../../.build/sentry/lib + + library.files = ../../.build/sentry/libsentry.so + library.path = /opt/lib + INSTALLS += library +} + +LIBS += -L$$PWD/../../.build/liboxide -lliboxide +INCLUDEPATH += $$PWD/../../shared/liboxide +DEPENDPATH += $$PWD/../../shared/liboxide + +QMAKE_RPATHDIR += /lib /usr/lib /opt/lib /opt/usr/lib + +VERSION = 2.3 diff --git a/applications/task-switcher/main.cpp b/applications/task-switcher/main.cpp index 8dd1ddedd..a01d65c12 100644 --- a/applications/task-switcher/main.cpp +++ b/applications/task-switcher/main.cpp @@ -5,11 +5,10 @@ #include #include +#include +#include -#include "dbussettings.h" -#include "devicesettings.h" #include "controller.h" -#include "eventfilter.h" #include "screenprovider.h" @@ -20,6 +19,8 @@ Q_IMPORT_PLUGIN(QsgEpaperPlugin) #include "dbusservice_interface.h" using namespace std; +using namespace Oxide; +using namespace Oxide::Sentry; const char* qt_version = qVersion(); @@ -41,6 +42,7 @@ int main(int argc, char *argv[]){ // qputenv("QT_DEBUG_BACKINGSTORE", "1"); #endif QGuiApplication app(argc, argv); + sentry_init("corrupt", argv); auto filter = new EventFilter(&app); app.installEventFilter(filter); app.setOrganizationName("Eeems"); diff --git a/applications/task-switcher/main.qml b/applications/task-switcher/main.qml index eb049e294..84aef9453 100644 --- a/applications/task-switcher/main.qml +++ b/applications/task-switcher/main.qml @@ -38,6 +38,7 @@ ApplicationWindow { visible: stateController.state === "loaded" property bool counter: false function reload(){ + controller.breadcrumb("background", "reload"); console.log("Reloading background"); counter = !counter source = "image://screen/image?id=" + counter @@ -48,7 +49,10 @@ ApplicationWindow { MouseArea { anchors.fill: parent enabled: stateController.state === "loaded" - onClicked: controller.previousApplication() + onClicked: { + controller.breadcrumb("background", "click", "ui"); + controller.previousApplication(); + } } ] footer: Rectangle { @@ -67,7 +71,10 @@ ApplicationWindow { text: "Home" source: "qrc:/img/home.svg" height: parent.height - onClicked: controller.launchStartupApp() + onClicked: { + controller.breadcrumb("appsView.home", "click", "ui"); + controller.launchStartupApp(); + } } AppItemSeperator {} AppItem { @@ -78,6 +85,7 @@ ApplicationWindow { height: parent.height width: height / 2 onClicked: { + controller.breadcrumb("appsView", "scroll.left", "ui"); console.log("Scroll left"); appsView.currentIndex = appsView.currentIndex - appsView.pageSize(); if(appsView.currentIndex < 0){ @@ -111,10 +119,14 @@ ApplicationWindow { height: visible ? appsView.height : 0 source: model.modelData.imgFile text: model.modelData.displayName - onClicked: model.modelData.execute() + onClicked: { + controller.breadcrumb("appsView.app", "click", "ui"); + model.modelData.execute(); + } onLongPress: { + controller.breadcrumb("appsView.app", "longPress", "ui"); model.modelData.stop(); - if(index == 0){ + if(index === 0){ background.source = ""; } } @@ -137,6 +149,7 @@ ApplicationWindow { height: parent.height width: height / 2 onClicked: { + controller.breadcrumb("appsView", "scroll.right", "ui"); console.log("Scroll right"); appsView.currentIndex = appsView.currentIndex + appsView.pageSize(); if(appsView.currentIndex > appsView.count){ @@ -165,6 +178,7 @@ ApplicationWindow { from: "*"; to: "loaded" SequentialAnimation { ScriptAction { script: { + controller.breadcrumb("navigation", "main", "navigation"); console.log("Display loaded"); } } } @@ -173,6 +187,7 @@ ApplicationWindow { from: "*"; to: "loading" SequentialAnimation { ScriptAction { script: { + controller.breadcrumb("navigation", "loading", "navigation"); console.log("Loading display"); controller.startup(); } } diff --git a/assets/etc/systemd/system/tarnish.service b/assets/etc/systemd/system/tarnish.service index 6592d2316..a2c591eeb 100644 --- a/assets/etc/systemd/system/tarnish.service +++ b/assets/etc/systemd/system/tarnish.service @@ -7,6 +7,8 @@ OnFailure=remarkable-fail.service Before=remarkable-reboot.service remarkable-shutdown.service [Service] +Type=dbus +BusName=codes.eeems.oxide1 ExecStart=/opt/bin/tarnish Restart=on-failure RestartSec=5 diff --git a/interfaces/application.xml b/interfaces/application.xml index 4e8b676bd..bebe68885 100644 --- a/interfaces/application.xml +++ b/interfaces/application.xml @@ -25,6 +25,7 @@ + diff --git a/interfaces/wifiapi.xml b/interfaces/wifiapi.xml index 05aad3507..c1b8845e8 100644 --- a/interfaces/wifiapi.xml +++ b/interfaces/wifiapi.xml @@ -2,6 +2,7 @@ + diff --git a/package b/package index 1af51945a..89db7af1b 100644 --- a/package +++ b/package @@ -2,7 +2,7 @@ # Copyright (c) 2020 The Toltec Contributors # SPDX-License-Identifier: MIT -pkgnames=(erode fret oxide rot tarnish decay corrupt anxiety) +pkgnames=(erode fret oxide rot tarnish decay corrupt anxiety liboxide libsentry) pkgver="2.3~~VERSION~" timestamp="$(date -u +%Y-%m-%dT%H:%MZ)" maintainer="Eeems " @@ -10,18 +10,18 @@ url=https://oxide.eeems.codes license=MIT source=(oxide.tar.gz) sha256sums=(SKIP) -image=qt:v2.1 +image=qt:latest build() { find . -name "*.pro" -type f -print0 \ | xargs -r -0 sed -i 's/linux-oe-g++/linux-arm-remarkable-g++/g' - make release + CMAKE_TOOLCHAIN_FILE="/usr/share/cmake/$CHOST.cmake" make FEATURES=sentry release } erode() { pkgdesc="Task manager" section=utils - installdepends=("tarnish=$pkgver") + installdepends=("tarnish=$pkgver" "liboxide=$pkgver" "libsentry=0.4.13") package() { install -D -m 755 -t "$pkgdir"/opt/bin "$srcdir"/release/opt/bin/erode @@ -34,7 +34,7 @@ erode() { fret() { pkgdesc="Take screenshots" section=utils - installdepends=("tarnish=$pkgver") + installdepends=("tarnish=$pkgver" "liboxide=$pkgver" "libsentry=0.4.13") package() { install -D -m 755 -t "$pkgdir"/opt/bin "$srcdir"/release/opt/bin/fret @@ -45,7 +45,7 @@ fret() { oxide() { pkgdesc="Launcher application" section=launchers - installdepends=("erode=$pkgver" "fret=$pkgver" "tarnish=$pkgver" "rot=$pkgver" "decay=$pkgver" "corrupt=$pkgver" display) + installdepends=("erode=$pkgver" "fret=$pkgver" "tarnish=$pkgver" "rot=$pkgver" "decay=$pkgver" "corrupt=$pkgver" "liboxide=$pkgver" display "libsentry=0.4.13") package() { install -D -m 755 -t "$pkgdir"/opt/bin "$srcdir"/release/opt/bin/oxide @@ -67,7 +67,7 @@ oxide() { rot() { pkgdesc="Manage Oxide settings through the command line" section=utils - installdepends=("tarnish=$pkgver") + installdepends=("tarnish=$pkgver" "liboxide=$pkgver" "libsentry=0.4.13") package() { install -D -m 755 -t "$pkgdir"/opt/bin "$srcdir"/release/opt/bin/rot @@ -77,6 +77,7 @@ rot() { tarnish() { pkgdesc="Service managing power states, connectivity and buttons" section=utils + installdepends=("liboxide=$pkgver" "libsentry=0.4.13") package() { install -D -m 644 -t "$pkgdir"/etc/dbus-1/system.d "$srcdir"/release/etc/dbus-1/system.d/codes.eeems.oxide.conf @@ -103,7 +104,7 @@ tarnish() { decay() { pkgdesc="Lockscreen application" section=utils - installdepends=("tarnish=$pkgver") + installdepends=("tarnish=$pkgver" "liboxide=$pkgver" "libsentry=0.4.13") package() { install -D -m 755 -t "$pkgdir"/opt/bin "$srcdir"/release/opt/bin/decay @@ -114,7 +115,7 @@ decay() { corrupt() { pkgdesc="Task Switcher for Oxide" section=utils - installdepends=("tarnish=$pkgver") + installdepends=("tarnish=$pkgver" "liboxide=$pkgver" "libsentry=0.4.13") package() { install -D -m 755 -t "$pkgdir"/opt/bin "$srcdir"/release/opt/bin/corrupt @@ -125,7 +126,7 @@ corrupt() { anxiety() { pkgdesc="Screenshot viewer for Oxide" section=utils - installdepends=("tarnish=$pkgver") + installdepends=("tarnish=$pkgver" "liboxide=$pkgver" "libsentry=0.4.13") package() { install -D -m 755 -t "$pkgdir"/opt/bin "$srcdir"/release/opt/bin/anxiety @@ -134,3 +135,24 @@ anxiety() { install -D -m 644 -t "$pkgdir"/opt/etc/draft/icons "$srcdir"/release/opt/etc/draft/icons/anxiety-splash.png } } + +liboxide() { + pkgdesc="Shared library for oxide applications" + section=devel + + package() { + install -D -m 755 -t "$pkgdir"/opt/usr/lib "$srcdir"/release/opt/usr/lib/libliboxide.so* + } +} + +libsentry() { + pkgdesc="Sentry SDK for C, C++ and native applications." + section=devel + url=https://github.com/getsentry/sentry-native + pkgver="0.4.13" + timestamp="2021-12-20T14:25:11Z" + + package() { + install -D -m 755 -t "$pkgdir"/opt/lib "$srcdir"/release/opt/lib/libsentry.so + } +} diff --git a/shared/devicesettings.cpp b/shared/devicesettings.cpp deleted file mode 100644 index 1cff49731..000000000 --- a/shared/devicesettings.cpp +++ /dev/null @@ -1,127 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include - -#include "devicesettings.h" - -#define BITS_PER_LONG (sizeof(long) * 8) -#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) -#define OFF(x) ((x)%BITS_PER_LONG) -#define LONG(x) ((x)/BITS_PER_LONG) -#define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1) - -DeviceSettings::DeviceSettings(): _deviceType(DeviceType::RM1) { - readDeviceType(); - - QDir dir("/dev/input"); - qDebug() << "Looking for input devices..."; - for(auto path : dir.entryList(QDir::Files | QDir::NoSymLinks | QDir::System)){ - qDebug() << (" Checking " + path + "...").toStdString().c_str(); - QString fullPath(dir.path() + "/" + path); - QFile device(fullPath); - device.open(QIODevice::ReadOnly); - int fd = device.handle(); - int version; - if (ioctl(fd, EVIOCGVERSION, &version)){ - qDebug() << " Invalid"; - continue; - } - unsigned long bit[EV_MAX]; - ioctl(fd, EVIOCGBIT(0, EV_MAX), bit); - if (test_bit(EV_KEY, bit)) { - if (checkBitSet(fd, EV_KEY, BTN_STYLUS) && test_bit(EV_ABS, bit)) { - qDebug() << " Wacom input device detected"; - wacomPath = fullPath.toStdString(); - continue; - } - if (checkBitSet(fd, EV_KEY, KEY_POWER)) { - qDebug() << " Buttons input device detected"; - buttonsPath = fullPath.toStdString(); - continue; - } - } - if (checkBitSet(fd, EV_ABS, ABS_MT_SLOT)) { - qDebug() << " Touch input device detected"; - touchPath = fullPath.toStdString(); - continue; - } - qDebug() << " Invalid"; - } - if (wacomPath.empty()) { - qWarning() << "Wacom input device not found"; - } - if (touchPath.empty()) { - qWarning() << "Touch input device not found"; - } - if (buttonsPath.empty()){ - qWarning() << "Buttons input device not found"; - } -} -bool DeviceSettings::checkBitSet(int fd, int type, int i) { - unsigned long bit[NBITS(KEY_MAX)]; - ioctl(fd, EVIOCGBIT(type, KEY_MAX), bit); - return test_bit(i, bit); -} - -void DeviceSettings::readDeviceType() { - QFile file("/sys/devices/soc0/machine"); - if(!file.exists() || !file.open(QIODevice::ReadOnly | QIODevice::Text)){ - qDebug() << "Couldn't open " << file.fileName(); - _deviceType = DeviceType::Unknown; - return; - } - QTextStream in(&file); - QString modelName = in.readLine(); - if (modelName.startsWith("reMarkable 2")) { - qDebug() << "RM2 detected..."; - _deviceType = DeviceType::RM2; - return; - } - qDebug() << "RM1 detected..."; - _deviceType = DeviceType::RM1; -} - -DeviceSettings::DeviceType DeviceSettings::getDeviceType() const { return _deviceType; } - -const char* DeviceSettings::getButtonsDevicePath() const { return buttonsPath.c_str(); } - -const char* DeviceSettings::getWacomDevicePath() const { return wacomPath.c_str(); } - -const char* DeviceSettings::getTouchDevicePath() const { return touchPath.c_str(); } - -const char* DeviceSettings::getTouchEnvSetting() const { - switch(getDeviceType()) { - case DeviceType::RM1: - return "rotate=180"; - case DeviceType::RM2: - return "rotate=180:invertx"; - default: - return ""; - } -} - -int DeviceSettings::getTouchWidth() const { - switch(getDeviceType()) { - case DeviceType::RM1: - return 767; - case DeviceType::RM2: - return 1403; - default: - return 0; - } -} -int DeviceSettings::getTouchHeight() const { - switch(getDeviceType()) { - case DeviceType::RM1: - return 1023; - case DeviceType::RM2: - return 1871; - default: - return 0; - } -} diff --git a/shared/devicesettings.h b/shared/devicesettings.h deleted file mode 100644 index acc5641d5..000000000 --- a/shared/devicesettings.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef DEVICESETTINGS_H -#define DEVICESETTINGS_H - -#include - -#define deviceSettings DeviceSettings::instance() - -#define DEBUG - -class DeviceSettings{ -public: - enum DeviceType { Unknown, RM1, RM2 }; - static DeviceSettings& instance() { - static DeviceSettings INSTANCE; - return INSTANCE; - } - const char* getButtonsDevicePath() const; - const char* getWacomDevicePath() const; - const char* getTouchDevicePath() const; - const char* getTouchEnvSetting() const; - DeviceType getDeviceType() const; - int getTouchWidth() const; - int getTouchHeight() const; - -private: - DeviceType _deviceType; - - DeviceSettings(); - ~DeviceSettings() {}; - void readDeviceType(); - bool checkBitSet(int fd, int type, int i); - std::string buttonsPath = ""; - std::string wacomPath = ""; - std::string touchPath = ""; -}; - -#endif // DEVICESETTINGS_H diff --git a/shared/eventfilter.cpp b/shared/eventfilter.cpp deleted file mode 100644 index bc00761c1..000000000 --- a/shared/eventfilter.cpp +++ /dev/null @@ -1,143 +0,0 @@ -#include "eventfilter.h" -#include -#include -#include -#include -#include - -#define DISPLAYWIDTH 1404 -#define DISPLAYHEIGHT 1872.0 -#define WACOM_X_SCALAR (float(DISPLAYWIDTH) / float(DISPLAYHEIGHT)) -#define WACOM_Y_SCALAR (float(DISPLAYHEIGHT) / float(DISPLAYWIDTH)) -//#define DEBUG_EVENTS - -EventFilter::EventFilter(QObject *parent) : QObject(parent), root(nullptr){} - -QPointF swap(QPointF pointF){ - return QPointF(pointF.y(), pointF.x()); -} - -QPointF transpose(QPointF pointF){ - pointF = swap(pointF); - // Handle scaling from wacom to screensize - pointF.setX(pointF.x() * WACOM_X_SCALAR); - pointF.setY((DISPLAYWIDTH - pointF.y()) * WACOM_Y_SCALAR); - return pointF; -} -QPointF globalPos(QQuickItem* obj){ - qreal x = obj->x(); - qreal y = obj->y(); - while(obj->parentItem() != nullptr){ - obj = obj->parentItem(); - x += obj->x(); - y += obj->y(); - } - return QPointF(x, y); -} -QMouseEvent* toMouseEvent(QEvent::Type type, QEvent* ev){ - auto tabletEvent = (QTabletEvent*)ev; - auto button = tabletEvent->pressure() > 0 || type == QMouseEvent::MouseButtonRelease ? Qt::LeftButton : Qt::NoButton; - return new QMouseEvent( - type, - transpose(tabletEvent->posF()), - transpose(tabletEvent->globalPosF()), - transpose(tabletEvent->globalPosF()), - button, - button, - tabletEvent->modifiers() - ); -} -bool isAt(QQuickItem* item, QPointF pos){ - auto itemPos = globalPos(item); - auto otherItemPos = QPointF(itemPos.x() + item->width(), itemPos.y() + item->height()); - return pos.x() >= itemPos.x() && pos.x() <= otherItemPos.x() && pos.y() >= itemPos.y() && pos.y() <= otherItemPos.y(); -} -QList widgetsAt(QQuickItem* root, QPointF pos){ - QList result; - auto children = root->findChildren(); - for(auto child : children){ - if(isAt(child, pos)){ - if(child->isVisible() && child->isEnabled() && child->acceptedMouseButtons() & Qt::LeftButton){ - if(!result.contains(child)){ - result.append((QObject*)child); - for(auto item : widgetsAt(child, pos)){ - if(!result.contains(item)){ - result.append(item); - } - } - } - } - } - } - return result; -} -int parentCount(QQuickItem* obj){ - int count = 0; - while(obj->parentItem()){ - count++; - obj = obj->parentItem(); - } - return count; -} -void postEvent(QEvent::Type type, QEvent* ev, QQuickItem* root){ - auto mouseEvent = toMouseEvent(type, ev); - auto pos = mouseEvent->globalPos(); - for(auto postWidget : widgetsAt(root, pos)){ - if(parentCount((QQuickItem*)postWidget)){ -#ifdef DEBUG_EVENTS - qDebug() << "postWidget: " << postWidget; -#endif - auto event = new QMouseEvent( - mouseEvent->type(), mouseEvent->localPos(), mouseEvent->windowPos(), - mouseEvent->screenPos(), mouseEvent->button(), mouseEvent->buttons(), - mouseEvent->modifiers() - ); - auto widgetPos = globalPos((QQuickItem*)postWidget); - auto localPos = event->localPos(); - localPos.setX(pos.x() - widgetPos.x()); - localPos.setY((pos.y()) - widgetPos.y()); - event->setLocalPos(localPos); - QGuiApplication::postEvent(postWidget, event); - } - } - delete mouseEvent; -} - -bool EventFilter::eventFilter(QObject* obj, QEvent* ev){ - auto type = ev->type(); - bool filtered = QObject::eventFilter(obj, ev); - if(!filtered){ - if(type == QEvent::TabletPress){ -#ifdef DEBUG_EVENTS - qDebug() << ev; -#endif - postEvent(QMouseEvent::MouseButtonPress, ev, root); - }else if(type == QEvent::TabletRelease){ -#ifdef DEBUG_EVENTS - qDebug() << ev; -#endif - postEvent(QMouseEvent::MouseButtonRelease, ev, root); - }else if(type == QEvent::TabletMove){ -#ifdef DEBUG_EVENTS - qDebug() << ev; -#endif - postEvent(QMouseEvent::MouseMove, ev, root); - } -#ifdef DEBUG_EVENTS - else if( - type == QEvent::MouseMove - || type == QEvent::MouseButtonPress - || type == QEvent::MouseButtonRelease - ){ - for(auto widget : widgetsAt(root, ((QMouseEvent*)ev)->globalPos())){ - if(parentCount((QQuickItem*)widget)){ - qDebug() << "postWidget: " << widget; - } - } - qDebug() << obj; - qDebug() << ev; - } -#endif - } - return filtered; -} diff --git a/shared/eventfilter.h b/shared/eventfilter.h deleted file mode 100644 index 3a3e3c835..000000000 --- a/shared/eventfilter.h +++ /dev/null @@ -1,21 +0,0 @@ -#ifndef EVENTFILTER_H -#define EVENTFILTER_H - -#include -#include -#include -#include - -class EventFilter : public QObject -{ - Q_OBJECT -public: - QQuickItem* root; - explicit EventFilter(QObject* parent = nullptr); -signals: - void suspend(); -protected: - bool eventFilter(QObject* obj, QEvent* ev); -}; - -#endif // EVENTFILTER_H diff --git a/shared/liboxide/.gitignore b/shared/liboxide/.gitignore new file mode 100644 index 000000000..fab7372d7 --- /dev/null +++ b/shared/liboxide/.gitignore @@ -0,0 +1,73 @@ +# This file is used to ignore files which are generated +# ---------------------------------------------------------------------------- + +*~ +*.autosave +*.a +*.core +*.moc +*.o +*.obj +*.orig +*.rej +*.so +*.so.* +*_pch.h.cpp +*_resource.rc +*.qm +.#* +*.*# +core +!core/ +tags +.DS_Store +.directory +*.debug +Makefile* +*.prl +*.app +moc_*.cpp +ui_*.h +qrc_*.cpp +Thumbs.db +*.res +*.rc +/.qmake.cache +/.qmake.stash + +# qtcreator generated files +*.pro.user* + +# xemacs temporary files +*.flc + +# Vim temporary files +.*.swp + +# Visual Studio generated files +*.ib_pdb_index +*.idb +*.ilk +*.pdb +*.sln +*.suo +*.vcproj +*vcproj.*.*.user +*.ncb +*.sdf +*.opensdf +*.vcxproj +*vcxproj.* + +# MinGW generated files +*.Debug +*.Release + +# Python byte code +*.pyc + +# Binaries +# -------- +*.dll +*.exe + diff --git a/shared/liboxide/eventfilter.cpp b/shared/liboxide/eventfilter.cpp new file mode 100644 index 000000000..5912c1b08 --- /dev/null +++ b/shared/liboxide/eventfilter.cpp @@ -0,0 +1,146 @@ +#include "eventfilter.h" +#include +#include +#include +#include +#include +#include + +#define DISPLAYWIDTH 1404 +#define DISPLAYHEIGHT 1872.0 +#define WACOM_X_SCALAR (float(DISPLAYWIDTH) / float(DISPLAYHEIGHT)) +#define WACOM_Y_SCALAR (float(DISPLAYHEIGHT) / float(DISPLAYWIDTH)) +//#define DEBUG_EVENTS + +namespace Oxide{ + EventFilter::EventFilter(QObject *parent) : QObject(parent), root(nullptr){} + + QPointF swap(QPointF pointF){ + return QPointF(pointF.y(), pointF.x()); + } + + QPointF transpose(QPointF pointF){ + pointF = swap(pointF); + // Handle scaling from wacom to screensize + pointF.setX(pointF.x() * WACOM_X_SCALAR); + pointF.setY((DISPLAYWIDTH - pointF.y()) * WACOM_Y_SCALAR); + return pointF; + } + QPointF globalPos(QQuickItem* obj){ + qreal x = obj->x(); + qreal y = obj->y(); + while(obj->parentItem() != nullptr){ + obj = obj->parentItem(); + x += obj->x(); + y += obj->y(); + } + return QPointF(x, y); + } + QMouseEvent* toMouseEvent(QEvent::Type type, QEvent* ev){ + auto tabletEvent = (QTabletEvent*)ev; + auto button = tabletEvent->pressure() > 0 || type == QMouseEvent::MouseButtonRelease ? Qt::LeftButton : Qt::NoButton; + return new QMouseEvent( + type, + transpose(tabletEvent->posF()), + transpose(tabletEvent->globalPosF()), + transpose(tabletEvent->globalPosF()), + button, + button, + tabletEvent->modifiers() + ); + } + bool isAt(QQuickItem* item, QPointF pos){ + auto itemPos = globalPos(item); + auto otherItemPos = QPointF(itemPos.x() + item->width(), itemPos.y() + item->height()); + return pos.x() >= itemPos.x() && pos.x() <= otherItemPos.x() && pos.y() >= itemPos.y() && pos.y() <= otherItemPos.y(); + } + QList widgetsAt(QQuickItem* root, QPointF pos){ + QList result; + auto children = root->findChildren(); + for(auto child : children){ + if(isAt(child, pos)){ + if(child->isVisible() && child->isEnabled() && child->acceptedMouseButtons() & Qt::LeftButton){ + if(!result.contains(child)){ + result.append((QObject*)child); + for(auto item : widgetsAt(child, pos)){ + if(!result.contains(item)){ + result.append(item); + } + } + } + } + } + } + return result; + } + int parentCount(QQuickItem* obj){ + int count = 0; + while(obj->parentItem()){ + count++; + obj = obj->parentItem(); + } + return count; + } + void postEvent(QEvent::Type type, QEvent* ev, QQuickItem* root){ + auto mouseEvent = toMouseEvent(type, ev); + auto pos = mouseEvent->globalPos(); + for(auto postWidget : widgetsAt(root, pos)){ + if(parentCount((QQuickItem*)postWidget)){ +#ifdef DEBUG_EVENTS + qDebug() << "postWidget: " << postWidget; +#endif + auto event = new QMouseEvent( + mouseEvent->type(), mouseEvent->localPos(), mouseEvent->windowPos(), + mouseEvent->screenPos(), mouseEvent->button(), mouseEvent->buttons(), + mouseEvent->modifiers() + ); + auto widgetPos = globalPos((QQuickItem*)postWidget); + auto localPos = event->localPos(); + localPos.setX(pos.x() - widgetPos.x()); + localPos.setY((pos.y()) - widgetPos.y()); + event->setLocalPos(localPos); + QGuiApplication::postEvent(postWidget, event); + } + } + delete mouseEvent; + } + + bool EventFilter::eventFilter(QObject* obj, QEvent* ev){ + auto type = ev->type(); + bool filtered = QObject::eventFilter(obj, ev); + if(!filtered){ + if(type == QEvent::TabletPress){ +#ifdef DEBUG_EVENTS + qDebug() << ev; +#endif + postEvent(QMouseEvent::MouseButtonPress, ev, root); + }else if(type == QEvent::TabletRelease){ +#ifdef DEBUG_EVENTS + qDebug() << ev; +#endif + postEvent(QMouseEvent::MouseButtonRelease, ev, root); + }else if(type == QEvent::TabletMove){ +#ifdef DEBUG_EVENTS + qDebug() << ev; +#endif + postEvent(QMouseEvent::MouseMove, ev, root); + } +#ifdef DEBUG_EVENTS + else if( + type == QEvent::MouseMove + || type == QEvent::MouseButtonPress + || type == QEvent::MouseButtonRelease + ){ + for(auto widget : widgetsAt(root, ((QMouseEvent*)ev)->globalPos())){ + if(parentCount((QQuickItem*)widget)){ + qDebug() << "postWidget: " << widget; + } + } + qDebug() << obj; + qDebug() << ev; + } +#endif + } + return filtered; + } +} diff --git a/shared/liboxide/eventfilter.h b/shared/liboxide/eventfilter.h new file mode 100644 index 000000000..232a0c85c --- /dev/null +++ b/shared/liboxide/eventfilter.h @@ -0,0 +1,22 @@ +#ifndef EVENTFILTER_H +#define EVENTFILTER_H + +#include +#include +#include + +namespace Oxide{ + class EventFilter : public QObject + { + Q_OBJECT + public: + QQuickItem* root; + explicit EventFilter(QObject* parent = nullptr); + signals: + void suspend(); + protected: + bool eventFilter(QObject* obj, QEvent* ev); + }; +} + +#endif // EVENTFILTER_H diff --git a/shared/liboxide/liboxide.cpp b/shared/liboxide/liboxide.cpp new file mode 100644 index 000000000..9877071a9 --- /dev/null +++ b/shared/liboxide/liboxide.cpp @@ -0,0 +1,563 @@ +#include "liboxide.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +// String: 5aa5ca39ee0b4f48927529ca17519524 +// UUID: 5aa5ca39-ee0b-4f48-9275-29ca17519524 +#define OXIDE_UID SD_ID128_MAKE(5a,a5,ca,39,ee,0b,4f,48,92,75,29,ca,17,51,95,24) + +#ifdef SENTRY +#define SAMPLE_RATE 1.0 +std::string readFile(const std::string path){ + std::ifstream t(path); + std::stringstream buffer; + buffer << t.rdbuf(); + return buffer.str(); +} +#endif +void sentry_setup_user(){ +#ifdef SENTRY + if(!sharedSettings.telemetry()){ + return; + } + sentry_value_t user = sentry_value_new_object(); + sentry_value_set_by_key(user, "id", sentry_value_new_string(readFile("/etc/machine-id").c_str())); + sentry_set_user(user); +#endif +} +void sentry_setup_context(){ +#ifdef SENTRY + if(!sharedSettings.telemetry()){ + return; + } + std::string version = readFile("/etc/version"); + sentry_set_tag("os.version", version.c_str()); + sentry_value_t device = sentry_value_new_object(); + sentry_value_set_by_key(device, "machine-id", sentry_value_new_string(readFile("/etc/machine-id").c_str())); + sentry_value_set_by_key(device, "version", sentry_value_new_string(readFile("/etc/version").c_str())); + sentry_set_context("device", device); +#endif +} + +#define BITS_PER_LONG (sizeof(long) * 8) +#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) +#define OFF(x) ((x)%BITS_PER_LONG) +#define LONG(x) ((x)/BITS_PER_LONG) +#define test_bit(bit, array) ((array[LONG(bit)] >> OFF(bit)) & 1) + +static void *invalid_mem = (void *)1; + +void logMachineIdError(int error, QString name, QString path){ + if(error == -ENOENT){ + qWarning() << "/etc/machine-id is missing"; + }else if(error == -ENOMEDIUM){ + qWarning() << path + " is empty or all zeros"; + }else if(error == -EIO){ + qWarning() << path + " has the incorrect format"; + }else if(error == -EPERM){ + qWarning() << path + " access denied"; + } if(error == -EINVAL){ + qWarning() << "Error while reading " + name + ": Buffer invalid"; + }else if(error == -ENXIO){ + qWarning() << "Error while reading " + name + ": No invocation ID is set"; + }else if(error == -EOPNOTSUPP){ + qWarning() << "Error while reading " + name + ": Operation not supported"; + }else{ + qWarning() << "Unexpected error code reading " + name + ":" << strerror(error); + } +} +std::string getAppSpecific(sd_id128_t base){ + QCryptographicHash hash(QCryptographicHash::Sha256); + char buf[SD_ID128_STRING_MAX]; + hash.addData(sd_id128_to_string(base, buf)); + hash.addData(sd_id128_to_string(OXIDE_UID, buf)); + auto r = hash.result(); + r[6] = (r.at(6) & 0x0F) | 0x40; + r[8] = (r.at(8) & 0x3F) | 0x80; + QUuid uid(r.at(0), r.at(1), r.at(2), r.at(3), r.at(4), r.at(5), r.at(6), r.at(7), r.at(8), r.at(9), r.at(10)); + return uid.toString((QUuid::Id128)).toStdString(); +} + +namespace Oxide { + void dispatchToMainThread(std::function callback){ + if(QThread::currentThread() == qApp->thread()){ + callback(); + return; + } + // any thread + QTimer* timer = new QTimer(); + timer->moveToThread(qApp->thread()); + timer->setSingleShot(true); + QObject::connect(timer, &QTimer::timeout, [=](){ + // main thread + callback(); + timer->deleteLater(); + }); + QMetaObject::invokeMethod(timer, "start", Qt::BlockingQueuedConnection, Q_ARG(int, 0)); + } + namespace Sentry{ +#ifdef SENTRY + static bool initialized = false; + Transaction::Transaction(sentry_transaction_t* t){ + inner = t; + } + Span::Span(sentry_span_t* s){ + inner = s; + } +#else + Transaction::Transaction(void* t){ + Q_UNUSED(t); + inner = nullptr; + } + Span::Span(void* s){ + Q_UNUSED(s); + inner = nullptr; + } +#endif + const char* bootId(){ + static std::string bootId(""); + if(!bootId.empty()){ + return bootId.c_str(); + } + sd_id128_t id; + int ret = sd_id128_get_boot(&id); + // TODO - eventually replace with the following when supported by the + // reMarkable kernel + // int ret = sd_id128_get_boot_app_specific(OXIDE_UID, &id); + if(ret == EXIT_SUCCESS){ + bootId = getAppSpecific(id); + // TODO - eventually replace with the following when supported by the + // reMarkable kernel + //char buf[SD_ID128_STRING_MAX]; + //bootId = sd_id128_to_string(id, buf); + return bootId.c_str(); + } + logMachineIdError(ret, "boot_id", "/proc/sys/kernel/random/boot_id"); + return ""; + } + const char* machineId(){ + static std::string machineId(""); + if(!machineId.empty()){ + return machineId.c_str(); + } + sd_id128_t id; + int ret = sd_id128_get_machine(&id); + // TODO - eventually replace with the following when supported by the + // reMarkable kernel + // int ret = sd_id128_get_machine_app_specific(OXIDE_UID, &id); + if(ret == EXIT_SUCCESS){ + machineId = getAppSpecific(id); + // TODO - eventually replace with the following when supported by the + // reMarkable kernel + //char buf[SD_ID128_STRING_MAX]; + //machineId = sd_id128_to_string(id, buf); + return machineId.c_str(); + } + logMachineIdError(ret, "machine-id", "/etc/machine-id"); + return ""; + } + bool enabled(){ + return sharedSettings.crashReport() || sharedSettings.telemetry(); + } + sentry_options_t* options = sentry_options_new(); + void sentry_init(const char* name, char* argv[], bool autoSessionTracking){ +#ifdef SENTRY + if(sharedSettings.crashReport()){ + sentry_options_set_sample_rate(options, SAMPLE_RATE); + }else{ + sentry_options_set_sample_rate(options, 0.0); + } + if(sharedSettings.telemetry()){ + sentry_options_set_traces_sample_rate(options, SAMPLE_RATE); + sentry_options_set_max_spans(options, 1000); + }else{ + sentry_options_set_traces_sample_rate(options, 0.0); + } + if(!sharedSettings.telemetry() && !sharedSettings.crashReport()){ + sentry_user_consent_revoke(); + }else{ + sentry_user_consent_give(); + } + sentry_options_set_auto_session_tracking(options, autoSessionTracking && sharedSettings.telemetry()); + if(initialized){ + return; + } + initialized = true; + // Setup options + sentry_options_set_dsn(options, "https://8d409799a9d640599cc66496fb87edf6@sentry.eeems.codes/2"); + sentry_options_set_symbolize_stacktraces(options, true); + if(QLibraryInfo::isDebugBuild()){ + sentry_options_set_environment(options, "debug"); + }else{ + sentry_options_set_environment(options, "release"); + } + sentry_options_set_debug(options, debugEnabled()); + sentry_options_set_database_path(options, "/home/root/.cache/Eeems/sentry"); + sentry_options_set_release(options, (std::string(name) + "@2.4").c_str()); + sentry_init(options); + + // Setup user + sentry_value_t user = sentry_value_new_object(); + sentry_value_set_by_key(user, "id", sentry_value_new_string(machineId())); + sentry_set_user(user); + // Setup context + std::string version = readFile("/etc/version"); + sentry_set_tag("os.version", version.c_str()); + sentry_set_tag("name", name); + sentry_value_t device = sentry_value_new_object(); + sentry_value_set_by_key(device, "machine-id", sentry_value_new_string(machineId())); + sentry_value_set_by_key(device, "version", sentry_value_new_string(readFile("/etc/version").c_str())); + sentry_set_context("device", device); + // Setup transaction + sentry_set_transaction(name); + // Add close guard + QObject::connect(qApp, &QCoreApplication::aboutToQuit, []() { + sentry_close(); + }); + // Handle settings changing + QObject::connect(&sharedSettings, &SharedSettings::telemetryChanged, [name, argv, autoSessionTracking](bool telemetry){ + Q_UNUSED(telemetry) + if(debugEnabled()){ + qDebug() << "Telemetry changed to" << telemetry; + } + sentry_init(name, argv, autoSessionTracking); + }); + QObject::connect(&sharedSettings, &SharedSettings::crashReportChanged, [name, argv, autoSessionTracking](bool crashReport){ + Q_UNUSED(crashReport) + if(debugEnabled()){ + qDebug() << "CrashReport changed to" << crashReport; + } + sentry_init(name, argv, autoSessionTracking); + }); +#else + Q_UNUSED(name); + Q_UNUSED(argv); + Q_UNUSED(autoSessionTracking); +#endif + } + void sentry_breadcrumb(const char* category, const char* message, const char* type, const char* level){ +#ifdef SENTRY + if(!sharedSettings.telemetry()){ + return; + } + sentry_value_t crumb = sentry_value_new_breadcrumb(type, message); + sentry_value_set_by_key(crumb, "category", sentry_value_new_string(category)); + sentry_value_set_by_key(crumb, "level", sentry_value_new_string(level)); + sentry_add_breadcrumb(crumb); +#else + Q_UNUSED(category); + Q_UNUSED(message); + Q_UNUSED(type); + Q_UNUSED(level); +#endif + } + Transaction* start_transaction(std::string name, std::string action){ +#ifdef SENTRY + sentry_transaction_context_t* context = sentry_transaction_context_new(name.c_str(), action.c_str()); + // Hack to force transactions to be reported even though SAMPLE_RATE is 100% + sentry_transaction_context_set_sampled(context, 1); + sentry_transaction_t* transaction = sentry_transaction_start(context, sentry_value_new_null()); + return new Transaction(transaction); +#else + Q_UNUSED(name); + Q_UNUSED(action); + return nullptr; +#endif + } + void stop_transaction(Transaction* transaction){ +#ifdef SENTRY + if(transaction != nullptr && transaction->inner != nullptr){ + sentry_transaction_finish(transaction->inner); + } +#else + Q_UNUSED(transaction); +#endif + } + void sentry_transaction(std::string name, std::string action, std::function callback){ +#ifdef SENTRY + if(!sharedSettings.telemetry()){ + callback(nullptr); + return; + } + Transaction* transaction = start_transaction(name, action); + auto scopeGuard = qScopeGuard([transaction] { + stop_transaction(transaction); + }); + callback(transaction); +#else + Q_UNUSED(name); + Q_UNUSED(action); + callback(nullptr); +#endif + } + Span* start_span(Transaction* transaction, std::string operation, std::string description){ +#ifdef SENTRY + if(transaction == nullptr){ + return nullptr; + } + return new Span(sentry_transaction_start_child(transaction->inner, (char*)operation.c_str(), (char*)description.c_str())); +#else + Q_UNUSED(transaction); + Q_UNUSED(operation); + Q_UNUSED(description); + return nullptr; +#endif + } + Span* start_span(Span* parent, std::string operation, std::string description){ +#ifdef SENTRY + if(parent == nullptr){ + return nullptr; + } + return new Span(sentry_span_start_child(parent->inner, (char*)operation.c_str(), (char*)description.c_str())); +#else + Q_UNUSED(parent); + Q_UNUSED(operation); + Q_UNUSED(description); + return nullptr; +#endif + } + void stop_span(Span* span){ +#ifdef SENTRY + if(span != nullptr && span->inner != nullptr){ + sentry_span_finish(span->inner); + } +#else + Q_UNUSED(span); +#endif + } + void sentry_span(Transaction* transaction, std::string operation, std::string description, std::function callback){ +#ifdef SENTRY + sentry_span(transaction, operation, description, [callback](Span* s){ + Q_UNUSED(s); + callback(); + }); +#else + Q_UNUSED(transaction); + Q_UNUSED(operation); + Q_UNUSED(description); + callback(); +#endif + } + void sentry_span(Transaction* transaction, std::string operation, std::string description, std::function callback){ +#ifdef SENTRY + if(!sharedSettings.telemetry() || transaction == nullptr || transaction->inner == nullptr){ + callback(nullptr); + return; + } + Span* span = start_span(transaction, operation, description); + auto scopeGuard = qScopeGuard([span] { + stop_span(span); + }); + callback(span); +#else + Q_UNUSED(transaction); + Q_UNUSED(operation); + Q_UNUSED(description); + callback(nullptr); +#endif + } + void sentry_span(Span* parent, std::string operation, std::string description, std::function callback){ +#ifdef SENTRY + sentry_span(parent, operation, description, [callback](Span* s){ + Q_UNUSED(s); + callback(); + }); +#else + Q_UNUSED(parent); + Q_UNUSED(operation); + Q_UNUSED(description); + callback(); +#endif + } + + void sentry_span(Span* parent, std::string operation, std::string description, std::function callback){ +#ifdef SENTRY + if(!sharedSettings.telemetry() || parent == nullptr || parent->inner == nullptr){ + callback(nullptr); + return; + } + Span* span = start_span(parent, operation, description); + auto scopeGuard = qScopeGuard([span] { + stop_span(span); + }); + callback(span); +#else + Q_UNUSED(parent); + Q_UNUSED(operation); + Q_UNUSED(description); + callback(nullptr); +#endif + } + void trigger_crash(){ memset((char *)invalid_mem, 1, 100); } + } + DeviceSettings& DeviceSettings::instance() { + static DeviceSettings INSTANCE; + return INSTANCE; + } + DeviceSettings::DeviceSettings(): _deviceType(DeviceType::RM1) { + readDeviceType(); + + QDir dir("/dev/input"); + qDebug() << "Looking for input devices..."; + for(auto path : dir.entryList(QDir::Files | QDir::NoSymLinks | QDir::System)){ + qDebug() << (" Checking " + path + "...").toStdString().c_str(); + QString fullPath(dir.path() + "/" + path); + QFile device(fullPath); + device.open(QIODevice::ReadOnly); + int fd = device.handle(); + int version; + if (ioctl(fd, EVIOCGVERSION, &version)){ + qDebug() << " Invalid"; + continue; + } + unsigned long bit[EV_MAX]; + ioctl(fd, EVIOCGBIT(0, EV_MAX), bit); + if (test_bit(EV_KEY, bit)) { + if (checkBitSet(fd, EV_KEY, BTN_STYLUS) && test_bit(EV_ABS, bit)) { + qDebug() << " Wacom input device detected"; + wacomPath = fullPath.toStdString(); + continue; + } + if (checkBitSet(fd, EV_KEY, KEY_POWER)) { + qDebug() << " Buttons input device detected"; + buttonsPath = fullPath.toStdString(); + continue; + } + } + if (checkBitSet(fd, EV_ABS, ABS_MT_SLOT)) { + qDebug() << " Touch input device detected"; + touchPath = fullPath.toStdString(); + continue; + } + qDebug() << " Invalid"; + } + if (wacomPath.empty()) { + qWarning() << "Wacom input device not found"; + } + if (touchPath.empty()) { + qWarning() << "Touch input device not found"; + } + if (buttonsPath.empty()){ + qWarning() << "Buttons input device not found"; + } + } + bool DeviceSettings::checkBitSet(int fd, int type, int i) { + unsigned long bit[NBITS(KEY_MAX)]; + ioctl(fd, EVIOCGBIT(type, KEY_MAX), bit); + return test_bit(i, bit); + } + + void DeviceSettings::readDeviceType() { + QFile file("/sys/devices/soc0/machine"); + if(!file.exists() || !file.open(QIODevice::ReadOnly | QIODevice::Text)){ + qDebug() << "Couldn't open " << file.fileName(); + _deviceType = DeviceType::Unknown; + return; + } + QTextStream in(&file); + QString modelName = in.readLine(); + if (modelName.startsWith("reMarkable 2")) { + qDebug() << "RM2 detected..."; + _deviceType = DeviceType::RM2; + return; + } + qDebug() << "RM1 detected..."; + _deviceType = DeviceType::RM1; + } + + DeviceSettings::DeviceType DeviceSettings::getDeviceType() const { return _deviceType; } + + const char* DeviceSettings::getButtonsDevicePath() const { return buttonsPath.c_str(); } + + const char* DeviceSettings::getWacomDevicePath() const { return wacomPath.c_str(); } + + const char* DeviceSettings::getTouchDevicePath() const { return touchPath.c_str(); } + + const char* DeviceSettings::getTouchEnvSetting() const { + switch(getDeviceType()) { + case DeviceType::RM1: + return "rotate=180"; + case DeviceType::RM2: + return "rotate=180:invertx"; + default: + return ""; + } + } + + int DeviceSettings::getTouchWidth() const { + switch(getDeviceType()) { + case DeviceType::RM1: + return 767; + case DeviceType::RM2: + return 1403; + default: + return 0; + } + } + int DeviceSettings::getTouchHeight() const { + switch(getDeviceType()) { + case DeviceType::RM1: + return 1023; + case DeviceType::RM2: + return 1871; + default: + return 0; + } + } + WifiNetworks XochitlSettings::wifinetworks(){ + beginGroup("wifinetworks"); + QMap wifinetworks; + for(const QString& key : allKeys()){ + QVariantMap network = value(key).toMap(); + wifinetworks[key] = network; + } + endGroup(); + return wifinetworks; + } + void XochitlSettings::setWifinetworks(const WifiNetworks& wifinetworks){ + beginGroup("wifinetworks"); + for(const QString& key : wifinetworks.keys()){ + setValue(key, wifinetworks.value(key)); + } + endGroup(); + sync(); + } + QVariantMap XochitlSettings::getWifiNetwork(const QString& name){ + beginGroup("wifinetworks"); + QVariantMap network = value(name).toMap(); + endGroup(); + return network; + } + void XochitlSettings::setWifiNetwork(const QString& name, QVariantMap properties){ + beginGroup("wifinetworks"); + setValue(name, properties); + endGroup(); + sync(); + } + void XochitlSettings::resetWifinetworks(){} + XochitlSettings::~XochitlSettings(){} + SharedSettings::~SharedSettings(){} + O_SETTINGS_PROPERTY_BODY(XochitlSettings, QString, General, passcode) + O_SETTINGS_PROPERTY_BODY(XochitlSettings, bool, General, wifion) + O_SETTINGS_PROPERTY_BODY(SharedSettings, int, General, version) + O_SETTINGS_PROPERTY_BODY(SharedSettings, bool, General, firstLaunch, true) + O_SETTINGS_PROPERTY_BODY(SharedSettings, bool, General, telemetry, false) + O_SETTINGS_PROPERTY_BODY(SharedSettings, bool, General, applicationUsage, false) + O_SETTINGS_PROPERTY_BODY(SharedSettings, bool, General, crashReport, true) +} diff --git a/shared/liboxide/liboxide.h b/shared/liboxide/liboxide.h new file mode 100644 index 000000000..d69c80e19 --- /dev/null +++ b/shared/liboxide/liboxide.h @@ -0,0 +1,141 @@ +#ifndef LIBOXIDE_H +#define LIBOXIDE_H + +#include "liboxide_global.h" + +#include "settingsfile.h" +#include "signalhandler.h" + +#include +#include +#include +#include +#include + +#define WPA_SUPPLICANT_SERVICE "fi.w1.wpa_supplicant1" +#define WPA_SUPPLICANT_SERVICE_PATH "/fi/w1/wpa_supplicant1" + +#define OXIDE_SERVICE "codes.eeems.oxide1" +#define OXIDE_SERVICE_PATH "/codes/eeems/oxide1" +#define OXIDE_INTERFACE_VERSION "1.0.0" + +#define OXIDE_GENERAL_INTERFACE OXIDE_SERVICE ".General" +#define OXIDE_POWER_INTERFACE OXIDE_SERVICE ".Power" +#define OXIDE_WIFI_INTERFACE OXIDE_SERVICE ".Wifi" +#define OXIDE_NETWORK_INTERFACE OXIDE_SERVICE ".Network" +#define OXIDE_BSS_INTERFACE OXIDE_SERVICE ".BSS" +#define OXIDE_APPS_INTERFACE OXIDE_SERVICE ".Apps" +#define OXIDE_APPLICATION_INTERFACE OXIDE_SERVICE ".Application" +#define OXIDE_SYSTEM_INTERFACE OXIDE_SERVICE ".System" +#define OXIDE_SCREEN_INTERFACE OXIDE_SERVICE ".Screen" +#define OXIDE_NOTIFICATIONS_INTERFACE OXIDE_SERVICE ".Notifications" +#define OXIDE_NOTIFICATION_INTERFACE OXIDE_SERVICE ".Notification" +#define OXIDE_SCREENSHOT_INTERFACE OXIDE_SERVICE ".Screenshot" + +#define deviceSettings Oxide::DeviceSettings::instance() +#define xochitlSettings Oxide::XochitlSettings::instance() +#define sharedSettings Oxide::SharedSettings::instance() + +#ifdef SENTRY +#include +#include +#endif + +typedef QMap WifiNetworks; +Q_DECLARE_METATYPE(WifiNetworks); + +namespace Oxide { + LIBOXIDE_EXPORT void dispatchToMainThread(std::function callback); + namespace Sentry{ + struct Transaction { +#ifdef SENTRY + sentry_transaction_t* inner; + Transaction(sentry_transaction_t* t); +#else + void* inner; + Transaction(void* t); +#endif + }; + struct Span { +#ifdef SENTRY + sentry_span_t* inner; + Span(sentry_span_t* s); +#else + void* inner; + Span(void* s); +#endif + }; + + LIBOXIDE_EXPORT const char* bootId(); + LIBOXIDE_EXPORT const char* machineId(); + LIBOXIDE_EXPORT void sentry_init(const char* name, char* argv[], bool autoSessionTracking = true); + LIBOXIDE_EXPORT void sentry_breadcrumb(const char* category, const char* message, const char* type = "default", const char* level = "info"); + LIBOXIDE_EXPORT Transaction* start_transaction(std::string name, std::string action); + LIBOXIDE_EXPORT void stop_transaction(Transaction* transaction); + LIBOXIDE_EXPORT void sentry_transaction(std::string name, std::string action, std::function callback); + LIBOXIDE_EXPORT Span* start_span(Transaction* transaction, std::string operation, std::string description); + LIBOXIDE_EXPORT Span* start_span(Span* parent, std::string operation, std::string description); + LIBOXIDE_EXPORT void stop_span(Span* span); + LIBOXIDE_EXPORT void sentry_span(Transaction* transaction, std::string operation, std::string description, std::function callback); + LIBOXIDE_EXPORT void sentry_span(Transaction* transaction, std::string operation, std::string description, std::function callback); + LIBOXIDE_EXPORT void sentry_span(Span* parent, std::string operation, std::string description, std::function callback); + LIBOXIDE_EXPORT void sentry_span(Span* parent, std::string operation, std::string description, std::function callback); + LIBOXIDE_EXPORT void trigger_crash(); + } + class LIBOXIDE_EXPORT DeviceSettings{ + public: + enum DeviceType { Unknown, RM1, RM2 }; + static DeviceSettings& instance(); + const char* getButtonsDevicePath() const; + const char* getWacomDevicePath() const; + const char* getTouchDevicePath() const; + const char* getTouchEnvSetting() const; + DeviceType getDeviceType() const; + int getTouchWidth() const; + int getTouchHeight() const; + + private: + DeviceType _deviceType; + + DeviceSettings(); + ~DeviceSettings() {}; + void readDeviceType(); + bool checkBitSet(int fd, int type, int i); + std::string buttonsPath = ""; + std::string wacomPath = ""; + std::string touchPath = ""; + }; + + class LIBOXIDE_EXPORT XochitlSettings : public SettingsFile { + Q_OBJECT + O_SETTINGS(XochitlSettings, "/home/root/.config/remarkable/xochitl.conf") + O_SETTINGS_PROPERTY(QString, General, passcode) + O_SETTINGS_PROPERTY(bool, General, wifion) + Q_PROPERTY(WifiNetworks wifinetworks MEMBER m_wifinetworks READ wifinetworks WRITE setWifinetworks RESET resetWifinetworks NOTIFY wifinetworksChanged) + public: + WifiNetworks wifinetworks(); + void setWifinetworks(const WifiNetworks& wifinetworks); + QVariantMap getWifiNetwork(const QString& name); + void setWifiNetwork(const QString& name, QVariantMap properties); + void resetWifinetworks(); + signals: + void wifinetworksChanged(WifiNetworks); + private: + ~XochitlSettings(); + WifiNetworks m_wifinetworks; + }; + + class LIBOXIDE_EXPORT SharedSettings : public SettingsFile { + Q_OBJECT + O_SETTINGS(SharedSettings, "/home/root/.config/Eeems/shared.conf") + O_SETTINGS_PROPERTY(int, General, version) + O_SETTINGS_PROPERTY(bool, General, firstLaunch, true) + O_SETTINGS_PROPERTY(bool, General, telemetry, false) + O_SETTINGS_PROPERTY(bool, General, applicationUsage, false) + O_SETTINGS_PROPERTY(bool, General, crashReport, true) + private: + ~SharedSettings(); + }; +} + +#endif // LIBOXIDE_H diff --git a/shared/liboxide/liboxide.pro b/shared/liboxide/liboxide.pro new file mode 100644 index 000000000..c64f163dd --- /dev/null +++ b/shared/liboxide/liboxide.pro @@ -0,0 +1,44 @@ +QT -= gui +QT += quick + +TEMPLATE = lib +DEFINES += LIBOXIDE_LIBRARY + +CONFIG += c++11 + +DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 + +SOURCES += \ + eventfilter.cpp \ + liboxide.cpp \ + settingsfile.cpp \ + signalhandler.cpp + +HEADERS += \ + eventfilter.h \ + liboxide_global.h \ + liboxide.h \ + settingsfile.h \ + signalhandler.h + +LIBS += -lsystemd + +INCLUDEPATH += ../../shared +LIBS += -L$$PWD/../../shared/ -lqsgepaper +INCLUDEPATH += $$PWD/../../shared +DEPENDPATH += $$PWD/../../shared + +exists($$PWD/../../.build/sentry) { + LIBS += -L$$PWD/../../.build/sentry/lib -lsentry -ldl -lcurl -lbreakpad_client + INCLUDEPATH += $$PWD/../../.build/sentry/include + DEPENDPATH += $$PWD/../../.build/sentry/lib + + library.files = ../../.build/sentry/libsentry.so + library.path = /opt/lib + INSTALLS += library +} + +target.path = /opt/usr/lib +!isEmpty(target.path): INSTALLS += target + +VERSION = 2.3 diff --git a/shared/liboxide/liboxide_global.h b/shared/liboxide/liboxide_global.h new file mode 100644 index 000000000..6e5781c58 --- /dev/null +++ b/shared/liboxide/liboxide_global.h @@ -0,0 +1,12 @@ +#ifndef LIBOXIDE_GLOBAL_H +#define LIBOXIDE_GLOBAL_H + +#include + +#if defined(LIBOXIDE_LIBRARY) +# define LIBOXIDE_EXPORT Q_DECL_EXPORT +#else +# define LIBOXIDE_EXPORT Q_DECL_IMPORT +#endif + +#endif // LIBOXIDE_GLOBAL_H diff --git a/shared/liboxide/settingsfile.cpp b/shared/liboxide/settingsfile.cpp new file mode 100644 index 000000000..eb6753be0 --- /dev/null +++ b/shared/liboxide/settingsfile.cpp @@ -0,0 +1,131 @@ +#include +#include + +#include "settingsfile.h" + +namespace Oxide { + bool debugEnabled(){ + if(getenv("DEBUG") == NULL){ + return false; + } + QString env = qgetenv("DEBUG"); + return !(QStringList() << "0" << "n" << "no" << "false").contains(env.toLower()); + } + SettingsFile::SettingsFile(QString path) + : QSettings(path, QSettings::IniFormat), + fileWatcher(QStringList() << path) + { + + } + SettingsFile::~SettingsFile(){ + + } + void SettingsFile::fileChanged(){ + if(!fileWatcher.files().contains(fileName()) && !fileWatcher.addPath(fileName())){ + qWarning() << "Unable to watch " << fileName(); + } + if(debugEnabled()){ + qDebug() << "Settings file" << fileName() << "changed!"; + } + // Load new values + sync(); + auto metaObj = metaObject(); + for (int i = metaObj->propertyOffset(); i < metaObj->propertyCount(); ++i) { + auto property = metaObj->property(i); + if(property.hasNotifySignal()){ + auto value = property.read(this); + auto value2 = this->value(property.name()); + if(value != value2){ + if(debugEnabled()){ + qDebug() << "Property" << property.name() << "changed"; + } + property.write(this, value2); + property.notifySignal().invoke(this, Qt::QueuedConnection, QGenericArgument(value2.typeName(), value2.data())); + } + } + } + if(debugEnabled()){ + qDebug() << "Settings file" << fileName() << "changes loaded"; + } + } + void SettingsFile::reloadProperty(const QString& name){ + auto metaObj = metaObject(); + + auto propertyName = "__META_GROUP_" + name; + auto idx = metaObj->indexOfProperty(propertyName.toStdString().c_str()); + if(idx == -1){ + O_SETTINGS_DEBUG("Group for " + name + " not found") + return; + } + auto groupName = property(propertyName.toStdString().c_str()).toString(); + O_SETTINGS_DEBUG((fileName() + " Reloading " + groupName + "." + name).toStdString().c_str()) + if(groupName != "General"){ + beginGroup(groupName); + }else{ + beginGroup(""); + } + if(contains(name)){ + O_SETTINGS_DEBUG(" Value exists") + auto value = this->value(name); + if(value.isValid()){ + O_SETTINGS_DEBUG(" Updated") + setProperty(name.toStdString().c_str(), value); + }else{ + O_SETTINGS_DEBUG(" Invalid value") + } + }else{ + O_SETTINGS_DEBUG(" No Value") + } + endGroup(); + O_SETTINGS_DEBUG(" Done") + } + void SettingsFile::resetProperty(const QString& name){ + auto metaObj = metaObject(); + auto idx = metaObj->indexOfProperty(name.toStdString().c_str()); + if(idx == -1){ + O_SETTINGS_DEBUG(fileName() + " Property" + name + " not found") + return; + } + auto property = metaObj->property(idx); + if(property.isResettable()){ + property.reset(this); + }else{ + reloadProperty(property.name()); + } + } + void SettingsFile::init(){ + if(initalized){ + return; + } + initalized = true; + if(!QFile::exists(fileName())){ + resetProperties(); + } + sync(); + reloadProperties(); + if(!fileWatcher.files().contains(fileName()) && !fileWatcher.addPath(fileName())){ + qWarning() << "Unable to watch " << fileName(); + } + connect(&fileWatcher, &QFileSystemWatcher::fileChanged, this, &SettingsFile::fileChanged); + } + void SettingsFile::reloadProperties(){ + auto metaObj = metaObject(); + for (int i = metaObj->propertyOffset(); i < metaObj->propertyCount(); ++i) { + auto property = metaObj->property(i); + if(property.isConstant()){ + continue; + } + reloadProperty(property.name()); + } + } + void SettingsFile::resetProperties(){ + auto metaObj = metaObject(); + for (int i = metaObj->propertyOffset(); i < metaObj->propertyCount(); ++i) { + auto property = metaObj->property(i); + if(property.isConstant()){ + continue; + } + resetProperty(property.name()); + } + } +} diff --git a/shared/liboxide/settingsfile.h b/shared/liboxide/settingsfile.h new file mode 100644 index 000000000..f3578903b --- /dev/null +++ b/shared/liboxide/settingsfile.h @@ -0,0 +1,121 @@ +#ifndef SETTINGSFILE_H +#define SETTINGSFILE_H + +#include "liboxide_global.h" + +#include +#include +#include +#include +#include +#include + +#include + +#ifdef DEBUG +#define O_SETTINGS_DEBUG(msg) if(debugEnabled()){ qDebug() << msg; } +#else +#define O_SETTINGS_DEBUG(msg) +#endif + +#define O_SETTINGS_PROPERTY_0(_type, member, _group) \ + Q_PROPERTY(QString __META_GROUP_##member READ __META_GROUP_##member CONSTANT FINAL) \ + public: \ + void set_##member(_type _arg_##member); \ + _type member() const; \ + void reload_##member(); \ + Q_SIGNALS: \ + void member##Changed(const _type&); \ + protected: \ + QString __META_GROUP_##member() const; \ + private: \ + _type m_##member; + +#define O_SETTINGS_PROPERTY_1(_type, group, member) \ + Q_PROPERTY(_type member MEMBER m_##member READ member WRITE set_##member NOTIFY member##Changed FINAL) \ + O_SETTINGS_PROPERTY_0(_type, member, group) + +#define O_SETTINGS_PROPERTY_2(_type, group, member, _default) \ + Q_PROPERTY(_type member MEMBER m_##member READ member WRITE set_##member NOTIFY member##Changed RESET reset_##member) \ + O_SETTINGS_PROPERTY_0(_type, member, group) \ + public: \ + void reset_##member(); + +#define O_SETTINGS_PROPERTY_BODY_0(_class, _type, member, _group) \ + void _class::set_##member(_type _arg_##member) { \ + O_SETTINGS_DEBUG(fileName() + " Setting " + #_group + "." + #member) \ + m_##member = _arg_##member; \ + if(std::strcmp("General", #_group) != 0){ \ + beginGroup(#_group); \ + }else{ \ + beginGroup(""); \ + } \ + setValue(#member, QVariant::fromValue<_type>(_arg_##member)); \ + endGroup(); \ + sync(); \ + } \ + _type _class::member() const { return m_##member; } \ + void _class::reload_##member() { reloadProperty(#member); } \ + QString _class::__META_GROUP_##member() const { return #_group; } + +#define O_SETTINGS_PROPERTY_BODY_1(_class, _type, group, member) \ + O_SETTINGS_PROPERTY_BODY_0(_class, _type, member, group) + +#define O_SETTINGS_PROPERTY_BODY_2(_class, _type, group, member, _default) \ + O_SETTINGS_PROPERTY_BODY_0(_class, _type, member, group) \ + void _class::reset_##member() { \ + O_SETTINGS_DEBUG(fileName() + " Resetting " + #group + "." + #member) \ + O_SETTINGS_DEBUG(_default) \ + setProperty(#member, _default); \ + O_SETTINGS_DEBUG(" Done") \ + } + +#define O_SETTINGS_PROPERTY_X_get_func(arg1, arg2, arg3, arg4, arg5, ...) arg5 +#define O_SETTINGS_PROPERTY_X(...) \ + O_SETTINGS_PROPERTY_X_get_func(__VA_ARGS__, \ + O_SETTINGS_PROPERTY_2, \ + O_SETTINGS_PROPERTY_1, \ + ) + +#define O_SETTINGS_PROPERTY_BODY_X_get_func(arg1, arg2, arg3, arg4, arg5, arg6, ...) arg6 +#define O_SETTINGS_PROPERTY_BODY_X(...) \ + O_SETTINGS_PROPERTY_BODY_X_get_func(__VA_ARGS__, \ + O_SETTINGS_PROPERTY_BODY_2, \ + O_SETTINGS_PROPERTY_BODY_1, \ + ) + +#define O_SETTINGS_PROPERTY(...) O_SETTINGS_PROPERTY_X(__VA_ARGS__)(__VA_ARGS__) +#define O_SETTINGS_PROPERTY_BODY(...) O_SETTINGS_PROPERTY_BODY_X(__VA_ARGS__)(__VA_ARGS__) + + +#define O_SETTINGS(_type, path) \ + public: \ + static _type& instance(){ \ + static _type INSTANCE(path); \ + INSTANCE.init(); \ + return INSTANCE; \ + } \ + private: \ + _type(QString _path) : SettingsFile(_path) {} + + +namespace Oxide { + LIBOXIDE_EXPORT bool debugEnabled(); + class LIBOXIDE_EXPORT SettingsFile : public QSettings { + Q_OBJECT + private slots: + void fileChanged(); + protected: + SettingsFile(QString path); + ~SettingsFile(); + void reloadProperty(const QString& name); + void resetProperty(const QString& name); + void init(); + void reloadProperties(); + void resetProperties(); + private: + QFileSystemWatcher fileWatcher; + bool initalized = false; + }; +} +#endif // SETTINGSFILE_H diff --git a/applications/task-switcher/signalhandler.h b/shared/liboxide/signalhandler.cpp similarity index 72% rename from applications/task-switcher/signalhandler.h rename to shared/liboxide/signalhandler.cpp index 3bbd77a29..3ea1059fd 100644 --- a/applications/task-switcher/signalhandler.h +++ b/shared/liboxide/signalhandler.cpp @@ -1,8 +1,5 @@ -#ifndef SIGNALHANDLER_H -#define SIGNALHANDLER_H +#include "signalhandler.h" -#include -#include #include #include @@ -10,23 +7,11 @@ #include #include -#define signalHandler SignalHandler::singleton() - static int sigUsr1Fd[2]; static int sigUsr2Fd[2]; -class SignalHandler : public QObject -{ - Q_OBJECT -public: - static SignalHandler* singleton(SignalHandler* self = nullptr){ - static SignalHandler* instance; - if(self != nullptr){ - instance = self; - } - return instance; - } - static int setup_unix_signal_handlers(){ +namespace Oxide { + int SignalHandler::setup_unix_signal_handlers(){ if(!signalHandler){ new SignalHandler(qApp); } @@ -50,7 +35,14 @@ class SignalHandler : public QObject return 0; } - SignalHandler(QObject *parent = 0) : QObject(parent){ + SignalHandler* SignalHandler::singleton(SignalHandler* self){ + static SignalHandler* instance; + if(self != nullptr){ + instance = self; + } + return instance; + } + SignalHandler::SignalHandler(QObject *parent) : QObject(parent){ singleton(this); if(::socketpair(AF_UNIX, SOCK_STREAM, 0, sigUsr1Fd)){ qFatal("Couldn't create USR1 socketpair"); @@ -60,45 +52,33 @@ class SignalHandler : public QObject } snUsr1 = new QSocketNotifier(sigUsr1Fd[1], QSocketNotifier::Read, this); - connect(snUsr1, &QSocketNotifier::activated, this, &SignalHandler::handleSigUsr1); + connect(snUsr1, &QSocketNotifier::activated, this, &SignalHandler::handleSigUsr1, Qt::QueuedConnection); snUsr2 = new QSocketNotifier(sigUsr2Fd[1], QSocketNotifier::Read, this); - connect(snUsr2, &QSocketNotifier::activated, this, &SignalHandler::handleSigUsr2); + connect(snUsr2, &QSocketNotifier::activated, this, &SignalHandler::handleSigUsr2, Qt::QueuedConnection); } - ~SignalHandler(){} - - static void usr1SignalHandler(int unused){ + SignalHandler::~SignalHandler(){} + void SignalHandler::usr1SignalHandler(int unused){ Q_UNUSED(unused) char a = 1; ::write(sigUsr1Fd[0], &a, sizeof(a)); } - static void usr2SignalHandler(int unused){ + void SignalHandler::usr2SignalHandler(int unused){ Q_UNUSED(unused) char a = 1; ::write(sigUsr2Fd[0], &a, sizeof(a)); } - -public slots: - void handleSigUsr1(){ + void SignalHandler::handleSigUsr1(){ snUsr1->setEnabled(false); char tmp; ::read(sigUsr1Fd[1], &tmp, sizeof(tmp)); emit sigUsr1(); snUsr1->setEnabled(true); } - void handleSigUsr2(){ + void SignalHandler::handleSigUsr2(){ snUsr2->setEnabled(false); char tmp; ::read(sigUsr2Fd[1], &tmp, sizeof(tmp)); emit sigUsr2(); snUsr2->setEnabled(true); } - -signals: - void sigUsr1(); - void sigUsr2(); - -private: - QSocketNotifier* snUsr1; - QSocketNotifier* snUsr2; -}; -#endif // SIGNALHANDLER_H +} diff --git a/shared/liboxide/signalhandler.h b/shared/liboxide/signalhandler.h new file mode 100644 index 000000000..37bc0167a --- /dev/null +++ b/shared/liboxide/signalhandler.h @@ -0,0 +1,36 @@ +#ifndef SIGNALHANDLER_H +#define SIGNALHANDLER_H + +#include "liboxide_global.h" + +#include +#include + +#define signalHandler Oxide::SignalHandler::singleton() + +namespace Oxide { + class LIBOXIDE_EXPORT SignalHandler : public QObject + { + Q_OBJECT + public: + static int setup_unix_signal_handlers(); + static SignalHandler* singleton(SignalHandler* self = nullptr); + SignalHandler(QObject *parent = 0); + ~SignalHandler(); + static void usr1SignalHandler(int unused); + static void usr2SignalHandler(int unused); + + public slots: + void handleSigUsr1(); + void handleSigUsr2(); + + signals: + void sigUsr1(); + void sigUsr2(); + + private: + QSocketNotifier* snUsr1; + QSocketNotifier* snUsr2; + }; +} +#endif // SIGNALHANDLER_H diff --git a/shared/sentry/.vscode/extensions.json b/shared/sentry/.vscode/extensions.json new file mode 100644 index 000000000..3b2ac6a0f --- /dev/null +++ b/shared/sentry/.vscode/extensions.json @@ -0,0 +1,9 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "ms-vscode.cpptools", + "ms-vscode.cmake-tools", + "twxs.cmake" + ] +} diff --git a/shared/sentry/.vscode/settings.json b/shared/sentry/.vscode/settings.json new file mode 100644 index 000000000..e4f9a033c --- /dev/null +++ b/shared/sentry/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "editor.formatOnPaste": true, + "editor.formatOnSave": true, + "editor.formatOnType": true, + "files.trimTrailingWhitespace": true, + "files.insertFinalNewline": true, + + // C++ Extension + "C_Cpp.autoAddFileAssociations": false +} diff --git a/shared/sentry/CHANGELOG.md b/shared/sentry/CHANGELOG.md new file mode 100644 index 000000000..17d6a9383 --- /dev/null +++ b/shared/sentry/CHANGELOG.md @@ -0,0 +1,620 @@ +# Changelog + +## 0.4.17 + +**Fixes**: + +- sentry-native now successfully builds when examples aren't included. ([#702](https://github.com/getsentry/sentry-native/pull/702)) + +**Thank you**: + +Features, fixes and improvements in this release have been contributed by: + +- [@AenBleidd](https://github.com/AenBleidd) + +## 0.4.16 + +**Features**: + +- Removed the `SENTRY_PERFORMANCE_MONITORING` compile flag requirement to access performance monitoring in the Sentry SDK. Performance monitoring is now available to everybody who has opted into the experimental API. +- New API to check whether the application has crashed in the previous run: `sentry_get_crashed_last_run()` and `sentry_clear_crashed_last_run()` ([#685](https://github.com/getsentry/sentry-native/pull/685)). +- Allow overriding the SDK name at build time - set the `SENTRY_SDK_NAME` CMake cache variable. +- More aggressively prune the Crashpad database. ([#698](https://github.com/getsentry/sentry-native/pull/698)) + +**Internal**: + +- Project IDs are now treated as opaque strings instead of integer values. ([#690](https://github.com/getsentry/sentry-native/pull/690)) +- Updated Breakpad and Crashpad backends to 2022-04-12. ([#696](https://github.com/getsentry/sentry-native/pull/696)) + +**Fixes**: + +- Updated CI as well as list of supported platforms to reflect Windows Server 2016, and therefore MSVC 2017 losing active support. +- Correctly free Windows Mutexes in Crashpad backend. + +**Thank you**: + +Features, fixes and improvements in this release have been contributed by: + +- [@zhaowq32](https://github.com/zhaowq32) + +## 0.4.15 + +**Fixes**: + +- Fix contexts from the scope not being attached to events correctly. +- Improve performance of event serialization. + +## 0.4.14 + +**Features**: + +- The Sentry SDK now has experimental support for performance monitoring. + The performance monitoring API allows manually creating transactions and instrumenting spans, and offers APIs for distributed tracing. + The API is currently disabled by default and needs to be enabled via a compile-time `SENTRY_PERFORMANCE_MONITORING` flag. + For more information, take a look at the more detailed [documentation of performance monitoring](https://docs.sentry.io/platforms/native/performance/). +- Sentry now has an explicit `sentry_flush` method that blocks the calling thread for the given time, waiting for the transport queue to be flushed. Custom transports need to implement a new `flush_hook` for this to work. + +**Fixes**: + +- Fix Sentry API deadlocking when the SDK was not initialized (or `sentry_init` failed). +- The rate limit handling of the default transports was updated to match the expected behavior. +- The Windows OS version is now read from the Registry and is more accurate. +- The `SENTRY_LIBRARY_TYPE` CMake option is now correctly honored. +- The Linux Modulefinder was once again improved to increase its memory safety and reliability. + +**Thank you**: + +Features, fixes and improvements in this release have been contributed by: + +- [@Mixaill](https://github.com/Mixaill) + +## 0.4.13 + +**Features**: + +- Add client-side stackwalking on Linux, Windows, and macOS (disabled by default). +- CMake: add ability to set solution folder name. +- Add AIX support. + +**Fixes**: + +- CMake: check whether libcurl was already found. +- Increment CXX standard version to 14 to allow crashpad to build. + +**Internal**: + +- Update Crashpad and Breakpad submodules to 2021-12-03. + +**Thank you**: + +Features, fixes and improvements in this release have been contributed by: + +- [@Mixaill](https://github.com/Mixaill) +- [@ladislavmacoun](https://github.com/ladislavmacoun) +- [@NattyNarwhal](https://github.com/NattyNarwhal) +- [@mjvankampen](https://github.com/mjvankampen) + +## 0.4.12 + +**Features**: + +- Make the shutdown timeout configurable via `sentry_options_set_shutdown_timeout`. + +**Fixes**: + +- The crashpad backend compiles with mingw again. +- Build System improvements. + +**Thank you**: + +Features, fixes and improvements in this release have been contributed by: + +- [@irov](https://github.com/irov) +- [@past-due](https://github.com/past-due) +- [@andrei-mu](https://github.com/andrei-mu) +- [@rpadaki](https://github.com/rpadaki) + +## 0.4.11 + +**Fixes**: + +- The crashpad backend now respects the `max_breadcrumbs` setting. +- Hanging HTTP requests will now be canceled on shutdown in the WinHTTP transport. +- The Modulefinder and Android unwinder now use safer memory access. +- Possible races and deadlocks have been fixed in `init`/`close`, and in API related to sessions. + +**Thank you**: + +Features, fixes and improvements in this release have been contributed by: + +- [@smibe](https://github.com/smibe) + +## 0.4.10 + +**Fixes**: + +- Fix a potential deadlock in macOS modulefinder. +- Lower Stack usage, to lower change of stack overflows. +- Avoid a double-free when parsing an invalid DSN. +- Improvements to Unity Builds and 32-bit Builds. +- Fix infinite recursion in signal handler by correctly cleaning up on shutdown. + +**Internal**: + +- Update Crashpad and Breakpad submodules to 2021-06-14. + +**Thank you**: + +Features, fixes and improvements in this release have been contributed by: + +- [@janisozaur](https://github.com/janisozaur) +- [@bschatt](https://github.com/bschatt) +- [@GenuineAster](https://github.com/GenuineAster) + +## 0.4.9 + +**Features**: + +- Rewrote the Linux modulefinder which should now work correctly when encountering gaps in the memory mapping of loaded libraries, and supports libraries loaded from a file offset, such as libraries loaded directly from `.apk` files on Android. +- Invoke the `before_send` hook at time of a hard crash when using the Windows or Linux Crashpad backend. +- Added the following new convenience functions: + - `sentry_value_new_exception` + - `sentry_value_new_thread` + - `sentry_value_new_stacktrace` + - `sentry_event_add_exception` + - `sentry_event_add_thread` + - The `sentry_event_value_add_stacktrace` is deprecated. +- Renamed `sentry_shutdown` to `sentry_close`, though the old function is still available. +- Updated Qt integration to Qt 6. + +**Fixes**: + +- Optimized and fixed bugs in the JSON parser/serializer. +- Build fixes for PPC and universal macOS. +- Fixes to build using musl libc. +- Correctness fixes around printf and strftime usage. +- Allow building and running on older macOS versions. + +**Internal**: + +- Update Crashpad and Breakpad submodules to 2021-04-12 + +**Thank you**: + +Features, fixes and improvements in this release have been contributed by: + +- [@mastertheknife](https://github.com/mastertheknife) +- [@torarnv](https://github.com/torarnv) +- [@encounter](https://github.com/encounter) + +## 0.4.8 + +**Features**: + +- The unwinder on Android was updated to a newer version. +- Experimental support for the Breakpad backend on Android and iOS. + +**Fixes**: + +- Fixed some memory leaks on Windows. +- Build System improvements. + +**Thank you**: + +Features, fixes and improvements in this release have been contributed by: + +- [@Mixaill](https://github.com/Mixaill) +- [@daxpedda](https://github.com/daxpedda) +- [@Amphaal](https://github.com/Amphaal) + +## 0.4.7 + +**Features**: + +- Events will automatically get an `os` context with OS version information. +- Added a new `max_breadcrumbs` option. + +**Fixes**: + +- Fixed some memory leaks related to bounded breadcrumbs. + +## 0.4.6 + +**Fixes**: + +- Restore compatibility with CMake 3.10 (as used in Android NDK Tools) + +**Internal**: + +- Update Crashpad and Breakpad submodules to 2021-01-25 + +## 0.4.5 + +**Features**: + +- The Breakpad backend is now supported on macOS, although the crashpad backend is recommended on that platform. +- Added a new `sentry_reinstall_backend` function which can be used in case a third-party library is overriding the signal/exception handler. +- Add a Qt integration that hooks into Qt logging (opt-in CMake option). +- Expose the sentry-native version via CMake. + +**Fixes**: + +- Install `.pdb` files correctly. +- Improve macOS runtime version detection. +- Fixed a potential segfault when doing concurrent scope modification. + +**Thank you**: + +Features, fixes and improvements in this release have been contributed by: + +- [@Mixaill](https://github.com/Mixaill) +- [@eakoli](https://github.com/eakoli) +- [@GenuineAster](https://github.com/GenuineAster) +- [@daxpedda](https://github.com/daxpedda) +- [@torarnv](https://github.com/torarnv) + +## 0.4.4 + +**Features**: + +- The `sentry_get_modules_list` function was made public, which will return a list of loaded libraries that will be sent to sentry with each event. +- A new `sentry_options_set_transport_thread_name` function was added to set an explicit name for sentries http transport thread. + +**Fixes**: + +- The session duration is now printed in a locale-independent way, avoiding invalid session payloads. +- Correctly clean up locks and pass the Windows Application Verifier. +- Build fixes for MinGW and better documentation for universal MacOS builds. +- Crashes captured by the `crashpad` backend _after_ calling `sentry_shutdown` will now have the full metadata. + +**Thank you**: + +Features, fixes and improvements in this release have been contributed by: + +- [@Mixaill](https://github.com/Mixaill) + +## 0.4.3 + +**Caution**: + +- The representation of `sentry_value_t` was changed to avoid problems with the newly introduced Memory Tagging Extension (MTE) on ARM / Android. + Implementation details of `sentry_value_t` were never considered public, and it should always be treated as an opaque type. + +**Fixes**: + +- Fix corrupted breadcrumb data when using the crashpad backend on Windows. +- Avoid sending empty envelopes when using the crashpad backend. +- Correctly encode the signal number when using the Windows inproc backend, avoiding a processing Error. +- Unwind from the local call-stack, fixing empty stacktraces when using the inproc backend on Linux. +- Improvements to the Build configuration. + +**Thank you**: + +Features, fixes and improvements in this release have been contributed by: + +- [@4diekmann](https://github.com/4diekmann) +- [@variar](https://github.com/variar) +- [@Mixaill](https://github.com/Mixaill) + +## 0.4.2 + +**Fixes**: + +- Sampled (discarded) events still contribute to a sessions `errors` count. +- Initialize all static data structures. + +## 0.4.1 + +**Fixes**: + +- Fix parsing rate limit headers with multiple categories. + +## 0.4.0 + +**Breaking Changes**: + +- The minimum CMake version required to build on windows was raised to `3.16.4` to avoid potential build failures on older versions. +- The `sentry_get_options` function was removed, as it was unsafe to use after a `sentry_shutdown` call. +- The `sentry_options_set_logger` function now accepts a `userdata` parameter. +- The `name` parameter of `sentry_options_add_attachment(w)` was removed, it will now be inferred from the filename of `path`. +- The transport startup hook that is set via `sentry_transport_set_startup_func` now needs to return an `int`, and a failure will propagate to `sentry_init`. +- The return value of the transport shutdown hook set via `sentry_transport_set_shutdown_func` was also changed to return an `int`. +- Both functions should return _0_ on success, and a non-zero error code on failure, as does `sentry_init`. +- Similarly, the return value of `sentry_shutdown` was also changed to an `int`, and will return _0_ on success and a non-zero error code on unclean shutdown. +- Documentation for custom transports was updated to highlight the ordering requirements of submitted envelopes, which is important for release health. + +```c +// before +sentry_options_set_logger(options, my_custom_logger); +sentry_options_add_attachment(options, "some-attachment", "/path/to/some-attachment.txt"); + +void transport_startup(sentry_options_t *options, void*state) { +} +sentry_transport_set_startup_func(transport, transport_startup); +bool transport_shutdown(uint64_t timeout, void*state) { + return true; +} +sentry_transport_set_shutdown_func(transport, transport_shutdown); + +// after +sentry_options_set_logger(options, my_custom_logger, NULL); +sentry_options_add_attachment(options, "/path/to/some-attachment.txt"); + +int transport_startup(sentry_options_t *options, void*state) { + return 0; +} +sentry_transport_set_startup_func(transport, transport_startup); +int transport_shutdown(uint64_t timeout, void*state) { + return 0; +} +sentry_transport_set_shutdown_func(transport, transport_shutdown); +``` + +**Features**: + +- [Release Health](https://docs.sentry.io/workflow/releases/health/) support is now stable and enabled by default. After the update, you will see the number of crash free sessions and crash free users on the Releases page in Sentry. To disable automatic session tracking, use `sentry_options_set_auto_session_tracking`. +- Breakpad support for Windows. This allows you to use `sentry-native` even on Windows XP! ([#278](https://github.com/getsentry/sentry-native/pull/278)) +- Add an in-process backend for Windows. As opposed to Breakpad, stack traces are generated on the device and sent to Sentry for symbolication. ([#287](https://github.com/getsentry/sentry-native/pull/287)) +- Support for the Crashpad backend was fixed and enabled for Linux. ([#320](https://github.com/getsentry/sentry-native/pull/320)) +- A new `SENTRY_BREAKPAD_SYSTEM` CMake option was added to link to the system-installed breakpad client instead of building it as part of sentry. + +**Fixes**: + +- Reworked thread synchronization code and logic in `sentry_shutdown`, avoiding an abort in case of an unclean shutdown. ([#323](https://github.com/getsentry/sentry-native/pull/323)) +- Similarly, reworked global options handling, avoiding thread safety issues. ([#333](https://github.com/getsentry/sentry-native/pull/333)) +- Fixed errors not being properly recorded in sessions. ([#317](https://github.com/getsentry/sentry-native/pull/317)) +- Fixed some potential memory leaks and other issues. ([#304](https://github.com/getsentry/sentry-native/pull/304) and others) + +**Thank you**: + +Features, fixes and improvements in this release have been contributed by: + +- [@eakoli](https://github.com/eakoli) +- [@Mixaill](https://github.com/Mixaill) +- [@irov](https://github.com/irov) +- [@jblazquez](https://github.com/jblazquez) +- [@daxpedda](https://github.com/daxpedda) + +## 0.3.4 + +**Fixes**: + +- Invalid memory access when `sentry_options_set_debug(1)` is set, leading to an application crash. This bug was introduced in version `0.3.3`. ([#310](https://github.com/getsentry/sentry-native/pull/310)). + +## 0.3.3 + +**Fixes**: + +- Fix a memory unsafety issue when calling `sentry_value_remove_by_key`. ([#297](https://github.com/getsentry/sentry-native/pull/297)) +- Improvements to internal logging. ([#301](https://github.com/getsentry/sentry-native/pull/301), [#302](https://github.com/getsentry/sentry-native/pull/302)) +- Better handling of timeouts. ([#284](https://github.com/getsentry/sentry-native/pull/284)) +- Better 32-bit build support. ([#291](https://github.com/getsentry/sentry-native/pull/291)) +- Run more checks on CI. ([#299](https://github.com/getsentry/sentry-native/pull/299)) + +**Thank you**: + +Fixes in this release have been contributed by: + +- [@eakoli](https://github.com/eakoli) + +## 0.3.2 + +**Features**: + +- Implement a new logger hook. ([#267](https://github.com/getsentry/sentry-native/pull/267)) + + This adds the new `sentry_options_set_logger` function, which can be used to customize the sentry-internal logging, for example to integrate into an app’s own logging system, or to stream logs to a file. + +- New CMake options: `SENTRY_LINK_PTHREAD`, `SENTRY_BUILD_RUNTIMESTATIC` and `SENTRY_EXPORT_SYMBOLS` along with other CMake improvements. + +**Fixes**: + +- Avoid memory unsafety when loading session from disk. ([#270](https://github.com/getsentry/sentry-native/pull/270)) +- Avoid Errors in Crashpad Backend without prior scope changes. ([#272](https://github.com/getsentry/sentry-native/pull/272)) +- Fix absolute paths on Windows, and allow using forward-slashes as directory separators. ([#266](https://github.com/getsentry/sentry-native/pull/266), [#289](https://github.com/getsentry/sentry-native/pull/289)) +- Various fixes uncovered by static analysis tools, notably excessive allocations by the page-allocator used inside signal handlers. +- Build fixes for MinGW and other compilers. + +**Thank you**: + +Features, fixes and improvements in this release have been contributed by: + +- [@Mixaill](https://github.com/Mixaill) +- [@blinkov](https://github.com/blinkov) +- [@eakoli](https://github.com/eakoli) + +## 0.3.1 + +- Add support for on-device symbolication, which is enabled by default on + Android. Use `sentry_options_set_symbolize_stacktraces` to customize. +- Enable gzip compressed crashpad minidumps on windows. +- Correctly 0-pad short `build-id`s. +- Fix build for 32bit Apple targets. + +## 0.3.0 + +- Always send the newer `x-sentry-envelope` format, which makes this + incompatible with older on-premise installations. +- Better document and handle non-ASCII paths. Users on windows should use the + `w` version of the appropriate APIs. +- Avoid segfaults due to failed sentry initialization. +- Avoid creating invalid sessions without a `release`. +- Make `sentry_transport_t` opaque, and instead expose APIs to configure it. + More functionality related to creating custom transports will be exposed in + future versions. + +### Breaking changes + +- The `sentry_backend_free` function was removed. +- The `sentry_backend_t` type was removed. +- The `sentry_transport_t` type is now opaque. Use the following new API to + create a custom transport. + +### New API + +- `sentry_transport_new` +- `sentry_transport_set_state` +- `sentry_transport_set_free_func` +- `sentry_transport_set_startup_func` +- `sentry_transport_set_shutdown_func` + +See `sentry.h` for more documentation. + +### Deprecations + +- `sentry_new_function_transport` has been deprecated in favor of the new + transport builder functions. + +## 0.2.6 + +- Avoid crash with invalid crashpad handler path. + +## 0.2.5 + +- Send sessions to the correct sentry endpoint and make sure they work. +- Smaller cleanups. + +## 0.2.4 + +- Avoid unsafe reads in the linux module finder. +- Update to latest crashpad snapshot. +- Yet more CMake improvements (thanks @madebr and @Amphaal). + +## 0.2.3 + +### Important upgrade notice + +All `0.2.x` versions prior to this one were affected by a bug that could +potentially lead to serious data-loss on Windows platforms. We encourage +everyone to update as quickly as possible. +See [#220](https://github.com/getsentry/sentry-native/issues/220) for details. + +### Deprecations + +- `sentry_transport_t` will be replaced by an opaque struct with setter methods + in a future release. +- `sentry_backend_free` and `sentry_backend_t` are deprecated and will be + removed in a future release. + +### Other changes + +- Further improvements to the cmake build system (huge thanks to @madebr + [#207](https://github.com/getsentry/sentry-native/pull/207)) +- Improved support for older Windows versions, as low as Windows XP SP3 (thanks + to @Mixaill [#203](https://github.com/getsentry/sentry-native/pull/203), + @cammm [#202](https://github.com/getsentry/sentry-native/pull/202) and + @jblazquez [#212](https://github.com/getsentry/sentry-native/pull/212)) +- Improved documentation +- Cleaned up sentry database handling +- Added new `sentry_handle_exception` function to explicitly capture a crash + (thanks @cammm [#201](https://github.com/getsentry/sentry-native/pull/201)) +- Added new `sentry_clear_modulecache` function to clear the list of loaded + modules. Use this function when dynamically loading libraries at runtime. + +## 0.2.2 + +- Implement experimental Session handling +- Implement more fine grained Rate Limiting for HTTP requests +- Implement `sample_rate` option +- In-process and Breakpad backend will not lose events queued for HTTP + submission on crash +- `sentry_shutdown` will better clean up after itself +- Add Experimental MinGW build support (thanks @Amphaal + [#189](https://github.com/getsentry/sentry-native/pull/189)) +- Various other fixes and improvements + +## 0.2.1 + +- Added Breakpad support on Linux +- Implemented fallback `debug-id` on Linux and Android for modules that are + built without a `build-id` +- Fixes issues and added CI for more platforms/compilers, including 32-bit Linux + and 32-bit VS2017 +- Further improvements to the CMake configuration (thanks @madebr + [#168](https://github.com/getsentry/sentry-native/pull/168)) +- Added a new `SENTRY_TRANSPORT` CMake option to customize the default HTTP transport + +## 0.2.0 + +- Complete rewrite in C +- Build system was switched to CMake +- Add attachment support +- Better support for custom transports +- The crashpad backend will automatically look for a `crashpad_handler` + executable next to the running program if no `handler_path` is set. + +### Breaking Changes + +- The `sentry_uuid_t` struct is now always a `char bytes[16]` instead of a + platform specific type. +- `sentry_remove_context`: The second parameter was removed. +- `sentry_options_set_transport`: + This function now takes a pointer to the new `sentry_transport_t` type. + Migrating from the old API can be done by wrapping with + `sentry_new_function_transport`, like this: + ```c + sentry_options_set_transport( + options, sentry_new_function_transport(send_envelope_func, &closure_data)); + ``` + +### Other API Additions + +- `size_t sentry_value_refcount(sentry_value_t value)` +- `void sentry_envelope_free(sentry_envelope_t *envelope)` +- `void sentry_backend_free(sentry_backend_t *backend)` + +## 0.1.4 + +- Add an option to enable the system crash reporter +- Fix compilation warnings + +## 0.1.3 + +- Stack unwinding on Android +- Fix UUID generation on Android +- Fix concurrently captured events leaking data in some cases +- Fix crashes when the database path contains both slashes and backslashes +- More robust error handling when creating the database folder +- Fix wrong initialization of CA info for the curl backend +- Disable the system crash handler on macOS for faster crashes + +## 0.1.2 + +- Fix SafeSEH builds on Win32 +- Fix a potential error when shutting down after unloading libsentry on macOS + +## 0.1.1 + +- Update Crashpad +- Fix compilation on Windows with VS 2019 +- Fix a bug in the JSON serializer causing invalid escapes +- Fix a bug in the Crashpad backend causing invalid events +- Reduce data event data sent along with minidumps +- Experimental support for Android NDK + +## 0.1.0 + +- Support for capturing messages +- Add an API to capture arbitrary contexts (`sentry_set_context`) +- Fix scope information being lost in some cases +- Experimental on-device unwinding support +- Experimental on-device symbolication support + +## 0.0.4 + +- Breakpad builds on all platforms +- Add builds for Windows (x86) +- Add builds for Linux + +## 0.0.3 + +- Fix debug information generation on macOS + +## 0.0.2 + +- Crashpad builds on macOS +- Crashpad builds on Windows (x64) + +## 0.0.1 + +Initial Release diff --git a/shared/sentry/CMakeLists.txt b/shared/sentry/CMakeLists.txt new file mode 100644 index 000000000..31861875a --- /dev/null +++ b/shared/sentry/CMakeLists.txt @@ -0,0 +1,583 @@ +if(WIN32) + cmake_minimum_required (VERSION 3.16.4) + + # enables support for CMAKE_MSVC_RUNTIME_LIBRARY + cmake_policy(SET CMP0091 NEW) +else() + # The Android tools ship with this ancient version, which we need to support. + cmake_minimum_required (VERSION 3.10) +endif() + +#read sentry-native version +file(READ "include/sentry.h" _SENTRY_HEADER_CONTENT) +string(REGEX MATCH "#define SENTRY_SDK_VERSION \"([0-9\.]+)\"" _SENTRY_VERSION_MATCH "${_SENTRY_HEADER_CONTENT}") +set(SENTRY_VERSION "${CMAKE_MATCH_1}") +unset(_SENTRY_HEADER_CONTENT) +unset(_SENTRY_VERSION_MATCH) + +project(Sentry-Native + LANGUAGES C CXX ASM + VERSION ${SENTRY_VERSION} +) + +set(SENTRY_MAIN_PROJECT OFF) +if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) + set(SENTRY_MAIN_PROJECT ON) +endif() + +if(NOT CMAKE_C_STANDARD) + set(CMAKE_C_STANDARD 11) +endif() + +if(NOT CMAKE_CXX_STANDARD) + set(CMAKE_CXX_STANDARD 17) +endif() + +include(GNUInstallDirs) +set(CMAKE_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/sentry") + +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(LINUX TRUE) +elseif(CMAKE_SYSTEM_NAME STREQUAL "AIX" OR CMAKE_SYSTEM_NAME STREQUAL "OS400") + set(AIX TRUE) +endif() + +#setup sentry library type +if(SENTRY_MAIN_PROJECT AND NOT DEFINED BUILD_SHARED_LIBS) + set(BUILD_SHARED_LIBS ON) +endif() +option(SENTRY_BUILD_SHARED_LIBS "Build shared libraries (.dll/.so) instead of static ones (.lib/.a)" ${BUILD_SHARED_LIBS}) +if(SENTRY_BUILD_SHARED_LIBS) + set(SENTRY_LIBRARY_TYPE SHARED) +else() + set(SENTRY_LIBRARY_TYPE STATIC) +endif() + +option(SENTRY_PIC "Build sentry (and dependent) libraries as position independent libraries" ON) + +option(SENTRY_BUILD_TESTS "Build sentry-native tests" "${SENTRY_MAIN_PROJECT}") +option(SENTRY_BUILD_EXAMPLES "Build sentry-native example(s)" "${SENTRY_MAIN_PROJECT}") + +option(SENTRY_LINK_PTHREAD "Link platform threads library" ON) +if(SENTRY_LINK_PTHREAD) + find_package(Threads REQUIRED) +endif() + +if(MSVC) + option(SENTRY_BUILD_RUNTIMESTATIC "Build sentry-native with static runtime" OFF) +endif() + +if(LINUX) + option(SENTRY_BUILD_FORCE32 "Force a 32bit compile on a 64bit host" OFF) + if(SENTRY_BUILD_FORCE32) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE") + set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} -m32 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE64_SOURCE") + set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS OFF) + endif() +endif() + +# CMAKE_POSITION_INDEPENDENT_CODE must be set BEFORE adding any libraries (including subprojects) +if(SENTRY_PIC) + set(CMAKE_POSITION_INDEPENDENT_CODE ON) +else() + set(CMAKE_POSITION_INDEPENDENT_CODE OFF) +endif() + +if(WIN32) + set(SENTRY_DEFAULT_TRANSPORT "winhttp") +elseif((APPLE AND NOT IOS) OR LINUX OR AIX) + set(SENTRY_DEFAULT_TRANSPORT "curl") +else() + set(SENTRY_DEFAULT_TRANSPORT "none") +endif() + +set(SENTRY_TRANSPORT ${SENTRY_DEFAULT_TRANSPORT} CACHE STRING + "The HTTP transport that sentry uses to submit events to the sentry server, can be either 'none', 'curl' or 'winhttp' on windows.") + +if(SENTRY_TRANSPORT STREQUAL "winhttp") + set(SENTRY_TRANSPORT_WINHTTP TRUE) +elseif(SENTRY_TRANSPORT STREQUAL "curl") + set(SENTRY_TRANSPORT_CURL TRUE) +elseif(SENTRY_TRANSPORT STREQUAL "none") + set(SENTRY_TRANSPORT_NONE TRUE) +else() + message(FATAL_ERROR "SENTRY_TRANSPORT must be one of 'none', 'curl' or 'winhttp'") +endif() + +if(SENTRY_TRANSPORT_WINHTTP AND NOT WIN32) + message(FATAL_ERROR "The winhttp transport is only supported on Windows.") +endif() + +if(SENTRY_BUILD_TESTS OR SENTRY_BUILD_EXAMPLES) + enable_testing() +endif() + +if("${CMAKE_SOURCE_DIR}" STREQUAL "${PROJECT_SOURCE_DIR}") + set(SENTRY_MAIN_PROJECT ON) +endif() + +option(SENTRY_ENABLE_INSTALL "Enable sentry installation" "${SENTRY_MAIN_PROJECT}") + +if(MSVC AND CMAKE_GENERATOR_TOOLSET MATCHES "_xp$") + message(WARNING "Crashpad is not supported for MSVC with XP toolset. Default backend was switched to 'breakpad'") + set(SENTRY_DEFAULT_BACKEND "breakpad") +elseif((APPLE AND NOT IOS) OR WIN32) + set(SENTRY_DEFAULT_BACKEND "crashpad") +elseif(LINUX) + set(SENTRY_DEFAULT_BACKEND "breakpad") +else() + set(SENTRY_DEFAULT_BACKEND "inproc") +endif() + +if(NOT DEFINED SENTRY_BACKEND) + set(SENTRY_BACKEND ${SENTRY_DEFAULT_BACKEND} CACHE STRING + "The sentry backend responsible for reporting crashes, can be either 'none', 'inproc', 'breakpad' or 'crashpad'.") +endif() + +if(SENTRY_BACKEND STREQUAL "crashpad") + set(SENTRY_BACKEND_CRASHPAD TRUE) +elseif(SENTRY_BACKEND STREQUAL "inproc") + set(SENTRY_BACKEND_INPROC TRUE) +elseif(SENTRY_BACKEND STREQUAL "breakpad") + set(SENTRY_BACKEND_BREAKPAD TRUE) +elseif(SENTRY_BACKEND STREQUAL "none") + set(SENTRY_BACKEND_NONE TRUE) +else() + message(FATAL_ERROR "SENTRY_BACKEND must be one of 'crashpad', 'inproc', 'breakpad' or 'none'") +endif() + +if(SENTRY_BACKEND_CRASHPAD AND ANDROID) + message(FATAL_ERROR "The Crashpad backend is not currently supported on Android") +endif() + +set(SENTRY_SDK_NAME "" CACHE STRING "The SDK name to report when sending events.") + +message(STATUS "SENTRY_TRANSPORT=${SENTRY_TRANSPORT}") +message(STATUS "SENTRY_BACKEND=${SENTRY_BACKEND}") +message(STATUS "SENTRY_LIBRARY_TYPE=${SENTRY_LIBRARY_TYPE}") +message(STATUS "SENTRY_SDK_NAME=${SENTRY_SDK_NAME}") + +if(ANDROID) + set(SENTRY_WITH_LIBUNWINDSTACK TRUE) +elseif(NOT WIN32) + set(SENTRY_WITH_LIBBACKTRACE TRUE) +endif() + +option(WITH_ASAN_OPTION "Build sentry-native with address sanitizer" OFF) +if(WITH_ASAN_OPTION) + add_compile_options(-g -fsanitize=address -fno-omit-frame-pointer) + link_libraries(-fsanitize=address) +endif() + +option(WITH_TSAN_OPTION "Build sentry-native with thread sanitizer" OFF) +if(WITH_TSAN_OPTION) + add_compile_options(-g -fsanitize=thread -fno-omit-frame-pointer) + link_libraries(-fsanitize=thread) +endif() + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "RelWithDebInfo") +endif() + +# use -O3 when doing `RelWithDebInfo` builds +if(NOT MSVC) + foreach(lang ASM C CXX) + string(REPLACE "-O2" "-O3" CMAKE_${lang}_FLAGS_RELWITHDEBINFO "${CMAKE_${lang}_FLAGS_RELWITHDEBINFO}") + endforeach() +endif() + +# https://gitlab.kitware.com/cmake/cmake/issues/20256 +if(APPLE) + find_program(DSYMUTIL_PROGRAM dsymutil) + if(DSYMUTIL_PROGRAM) + foreach(lang C CXX) + foreach(var LINK_EXECUTABLE CREATE_SHARED_LIBRARY) + set(CMAKE_${lang}_${var} "${CMAKE_${lang}_${var}}" "${DSYMUTIL_PROGRAM} ") + endforeach() + endforeach() + endif() +endif() + +function(sentry_install) + if(SENTRY_ENABLE_INSTALL) + install(${ARGN}) + endif() +endfunction() + +# helper function to add sources to existing TARGET prepended with ${CMAKE_CURRENT_SOURCE_DIR}/${SUBDIR} +function(sentry_target_sources_cwd TARGET) + cmake_parse_arguments(STSC "" "SUBDIR" "" ${ARGN}) + foreach(src ${STSC_UNPARSED_ARGUMENTS}) + if(IS_ABSOLUTE "${src}") + target_sources(${TARGET} PRIVATE ${src}) + else() + target_sources(${TARGET} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/${STSC_SUBDIR}/${src}") + endif() + endforeach() +endfunction() + +# ===== sentry library ===== + +add_library(sentry ${SENTRY_LIBRARY_TYPE} "${PROJECT_SOURCE_DIR}/vendor/mpack.c") +target_sources(sentry PRIVATE "${PROJECT_SOURCE_DIR}/include/sentry.h") +add_library(sentry::sentry ALIAS sentry) +add_subdirectory(src) + +if (NOT SENTRY_SDK_NAME STREQUAL "") + target_compile_definitions(sentry PRIVATE SENTRY_SDK_NAME="${SENTRY_SDK_NAME}") +endif() + +# we do not need this on android, only linux +if(LINUX) + target_sources(sentry PRIVATE + "${PROJECT_SOURCE_DIR}/vendor/stb_sprintf.c" + "${PROJECT_SOURCE_DIR}/vendor/stb_sprintf.h" + ) +endif() + +set_target_properties(sentry PROPERTIES PUBLIC_HEADER "include/sentry.h") + +if(DEFINED SENTRY_FOLDER) + set_target_properties(sentry PROPERTIES FOLDER ${SENTRY_FOLDER}) +endif() + +# check size type +include(CheckTypeSize) +check_type_size("long" CMAKE_SIZEOF_LONG) + +# https://gitlab.kitware.com/cmake/cmake/issues/18393 +if(SENTRY_BUILD_SHARED_LIBS) + if(APPLE) + sentry_install(FILES "$.dSYM" DESTINATION "${CMAKE_INSTALL_LIBDIR}") + elseif(MSVC) + sentry_install(FILES "$<$,$>:$>" + DESTINATION "${CMAKE_INSTALL_BINDIR}") + endif() +endif() + +if(SENTRY_BUILD_SHARED_LIBS) + target_compile_definitions(sentry PRIVATE SENTRY_BUILD_SHARED) +else() + target_compile_definitions(sentry PUBLIC SENTRY_BUILD_STATIC) +endif() +target_compile_definitions(sentry PRIVATE SIZEOF_LONG=${CMAKE_SIZEOF_LONG}) + +# AIX needs libm for isnan used in test suite +if(CMAKE_SYSTEM_NAME STREQUAL "AIX" OR CMAKE_SYSTEM_NAME STREQUAL "OS400") + target_link_libraries(sentry PRIVATE m) +endif() +# On IBM i PASE, flock is in libutil. Here because "sentry" exists now. +if(CMAKE_SYSTEM_NAME STREQUAL "OS400") + target_link_libraries(sentry PRIVATE util) +endif() + +if(SENTRY_TRANSPORT_CURL) + if(NOT CURL_FOUND) # Some other lib might bring libcurl already + find_package(CURL REQUIRED) + endif() + + if(TARGET CURL::libcurl) # Only available in cmake 3.12+ + target_link_libraries(sentry PRIVATE CURL::libcurl) + else() + # Needed for cmake < 3.12 support (cmake 3.12 introduced the target CURL::libcurl) + target_include_directories(sentry PRIVATE ${CURL_INCLUDE_DIR}) + # The exported sentry target must not contain any path of the build machine, therefore use generator expressions + string(REPLACE ";" "$" GENEX_CURL_LIBRARIES "${CURL_LIBRARIES}") + string(REPLACE ";" "$" GENEX_CURL_COMPILE_DEFINITIONS "${CURL_COMPILE_DEFINITIONS}") + target_link_libraries(sentry PRIVATE $) + target_compile_definitions(sentry PRIVATE $) + endif() +endif() + +set_property(TARGET sentry PROPERTY C_VISIBILITY_PRESET hidden) +if(MSVC) + if(CMAKE_SIZEOF_VOID_P EQUAL 4) + set(CMAKE_ASM_MASM_FLAGS "${CMAKE_ASM_MASM_FLAGS} /safeseh") + endif() + + # using `/Wall` is not feasible, as it spews tons of warnings from windows headers + # supress C5105, introduced in VS 16.8, which breaks on the Windows SDKs own `winbase.h` header + target_compile_options(sentry PRIVATE $) + # ignore all warnings for mpack + set_source_files_properties( + "${PROJECT_SOURCE_DIR}/vendor/mpack.c" + PROPERTIES + COMPILE_FLAGS + "/W0" + ) + + # set static runtime if enabled + if(SENTRY_BUILD_RUNTIMESTATIC) + set_property(TARGET sentry PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + endif() +else() + target_compile_options(sentry PRIVATE $) + # The crashpad and breakpad headers generate the following warnings that we + # ignore specifically + target_compile_options(sentry PRIVATE $) + # ignore all warnings for mpack + set_source_files_properties( + "${PROJECT_SOURCE_DIR}/vendor/mpack.c" + PROPERTIES + COMPILE_FLAGS + "-w" + ) +endif() + + +target_include_directories(sentry + PUBLIC + "$" + "$" + PRIVATE + "$" +) + +# The modulefinder and symbolizer need these two settings, and they are exported +# as `PUBLIC`, so libraries that depend on sentry get these too: +# `-E`: To have all symbols in the dynamic symbol table. +# `--build-id`: To have a build-id in the ELF object. +# FIXME: cmake 3.13 introduced target_link_options +option(SENTRY_EXPORT_SYMBOLS "Export symbols for modulefinder and symbolizer" ON) +if(SENTRY_EXPORT_SYMBOLS) + target_link_libraries(sentry PUBLIC + "$<$,$>:-Wl,-E,--build-id=sha1>") +endif() + +#respect CMAKE_SYSTEM_VERSION +if(WIN32) + if(MSVC AND CMAKE_GENERATOR_TOOLSET MATCHES "_xp$") + #force WINNT to 5.1 for Windows XP toolchain + target_compile_definitions(sentry PRIVATE "_WIN32_WINNT=0x0501") + elseif(${CMAKE_SYSTEM_VERSION} MATCHES "^10") + target_compile_definitions(sentry PRIVATE "_WIN32_WINNT=0x0A00") + elseif(${CMAKE_SYSTEM_VERSION} MATCHES "^6.3") + target_compile_definitions(sentry PRIVATE "_WIN32_WINNT=0x0603") + elseif(${CMAKE_SYSTEM_VERSION} MATCHES "^6.2") + target_compile_definitions(sentry PRIVATE "_WIN32_WINNT=0x0602") + elseif(${CMAKE_SYSTEM_VERSION} MATCHES "^6.1") + target_compile_definitions(sentry PRIVATE "_WIN32_WINNT=0x0601") + elseif(${CMAKE_SYSTEM_VERSION} MATCHES "^6.0") + target_compile_definitions(sentry PRIVATE "_WIN32_WINNT=0x0600") + elseif(${CMAKE_SYSTEM_VERSION} MATCHES "^5.2") + target_compile_definitions(sentry PRIVATE "_WIN32_WINNT=0x0502") + elseif(${CMAKE_SYSTEM_VERSION} MATCHES "^5.1") + target_compile_definitions(sentry PRIVATE "_WIN32_WINNT=0x0501") + endif() + + # crashpad does not support Windows XP toolset + if(MSVC AND CMAKE_GENERATOR_TOOLSET MATCHES "_xp$" AND SENTRY_BACKEND_CRASHPAD) + message(FATAL_ERROR "MSVC XP toolset does not support Crashpad") + endif() +endif() + +# handle platform libraries +if(ANDROID) + set(_SENTRY_PLATFORM_LIBS "dl" "log") +elseif(LINUX) + set(_SENTRY_PLATFORM_LIBS "dl" "rt") +elseif(WIN32) + set(_SENTRY_PLATFORM_LIBS "dbghelp" "shlwapi" "version") +endif() + +if(SENTRY_TRANSPORT_WINHTTP) + list(APPEND _SENTRY_PLATFORM_LIBS "winhttp") +endif() + +# handle platform threads library +if(SENTRY_LINK_PTHREAD) + list(APPEND _SENTRY_PLATFORM_LIBS "Threads::Threads") +endif() + +# apply platform libraries to sentry library +if(SENTRY_LIBRARY_TYPE STREQUAL "STATIC") + target_link_libraries(sentry PUBLIC ${_SENTRY_PLATFORM_LIBS}) +else() + target_link_libraries(sentry PRIVATE ${_SENTRY_PLATFORM_LIBS}) +endif() + +# suppress some errors and warnings for MinGW target +if(MINGW) + target_compile_options(sentry PRIVATE + -Wno-unused-variable + -Wno-unused-parameter + -Wno-format + -Wno-incompatible-pointer-types + -Wno-incompatible-function-pointer-types + ) +endif() + +if(SENTRY_WITH_LIBUNWINDSTACK) + target_include_directories(sentry PRIVATE + "$") + add_subdirectory("${PROJECT_SOURCE_DIR}/external/libunwindstack-ndk/cmake") + target_link_libraries(sentry PRIVATE unwindstack) + if(NOT SENTRY_BUILD_SHARED_LIBS) + sentry_install(TARGETS unwindstack EXPORT sentry + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + ) + endif() +endif() + +if(SENTRY_BACKEND_CRASHPAD) + option(SENTRY_CRASHPAD_SYSTEM "Use system crashpad" OFF) + if(SENTRY_CRASHPAD_SYSTEM) + find_package(crashpad REQUIRED) + target_link_libraries(sentry PUBLIC crashpad::client) + else() + # FIXME: required for cmake 3.12 and lower: + # - NEW behavior lets normal variable override option + cmake_policy(SET CMP0077 NEW) + if(SENTRY_BUILD_SHARED_LIBS) + set(CRASHPAD_ENABLE_INSTALL OFF CACHE BOOL "Enable crashpad installation" FORCE) + else() + set(CRASHPAD_ENABLE_INSTALL ON CACHE BOOL "Enable crashpad installation" FORCE) + endif() + add_subdirectory(external/crashpad crashpad_build) + + # set static runtime if enabled + if(SENTRY_BUILD_RUNTIMESTATIC AND MSVC) + set_property(TARGET crashpad_client PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + set_property(TARGET crashpad_compat PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + set_property(TARGET crashpad_getopt PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + set_property(TARGET crashpad_handler PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + set_property(TARGET crashpad_handler_lib PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + set_property(TARGET crashpad_minidump PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + set_property(TARGET crashpad_snapshot PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + set_property(TARGET crashpad_tools PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + set_property(TARGET crashpad_util PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + set_property(TARGET crashpad_zlib PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + set_property(TARGET mini_chromium PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + endif() + + if(DEFINED SENTRY_FOLDER) + set_target_properties(crashpad_client PROPERTIES FOLDER ${SENTRY_FOLDER}) + set_target_properties(crashpad_compat PROPERTIES FOLDER ${SENTRY_FOLDER}) + set_target_properties(crashpad_getopt PROPERTIES FOLDER ${SENTRY_FOLDER}) + set_target_properties(crashpad_handler PROPERTIES FOLDER ${SENTRY_FOLDER}) + set_target_properties(crashpad_handler_lib PROPERTIES FOLDER ${SENTRY_FOLDER}) + set_target_properties(crashpad_minidump PROPERTIES FOLDER ${SENTRY_FOLDER}) + set_target_properties(crashpad_snapshot PROPERTIES FOLDER ${SENTRY_FOLDER}) + set_target_properties(crashpad_tools PROPERTIES FOLDER ${SENTRY_FOLDER}) + set_target_properties(crashpad_util PROPERTIES FOLDER ${SENTRY_FOLDER}) + set_target_properties(crashpad_zlib PROPERTIES FOLDER ${SENTRY_FOLDER}) + set_target_properties(mini_chromium PROPERTIES FOLDER ${SENTRY_FOLDER}) + endif() + + target_link_libraries(sentry PRIVATE + $ + $ + ) + install(EXPORT crashpad_export NAMESPACE sentry_crashpad:: FILE sentry_crashpad-targets.cmake + DESTINATION "${CMAKE_INSTALL_CMAKEDIR}" + ) + if(WIN32 AND MSVC) + sentry_install(FILES $ + DESTINATION "${CMAKE_INSTALL_BINDIR}" OPTIONAL) + endif() + endif() + add_dependencies(sentry crashpad::handler) +elseif(SENTRY_BACKEND_BREAKPAD) + option(SENTRY_BREAKPAD_SYSTEM "Use system breakpad" OFF) + if(SENTRY_BREAKPAD_SYSTEM) + # system breakpad is using pkg-config, see `external/breakpad/breakpad-client.pc.in` + find_package(PkgConfig REQUIRED) + pkg_check_modules(BREAKPAD REQUIRED IMPORTED_TARGET breakpad-client) + target_link_libraries(sentry PUBLIC PkgConfig::BREAKPAD) + else() + add_subdirectory(external) + target_include_directories(sentry PRIVATE + "$" + ) + target_link_libraries(sentry PRIVATE + breakpad_client + ) + + if(DEFINED SENTRY_FOLDER) + set_target_properties(breakpad_client PROPERTIES FOLDER ${SENTRY_FOLDER}) + endif() + + if(NOT SENTRY_BUILD_SHARED_LIBS) + sentry_install(TARGETS breakpad_client EXPORT sentry + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + ) + endif() + endif() +elseif(SENTRY_BACKEND_INPROC) + target_compile_definitions(sentry PRIVATE SENTRY_WITH_INPROC_BACKEND) +endif() + +option(SENTRY_INTEGRATION_QT "Build Qt integration") +if(SENTRY_INTEGRATION_QT) + if(QT_DEFAULT_MAJOR_VERSION) + # Let user choose major version + set(Qt_VERSION_MAJOR ${QT_DEFAULT_MAJOR_VERSION}) + else() + # Find best match, prioritizing Qt 6 if available + find_package(Qt NAMES Qt6 Qt5 COMPONENTS Core REQUIRED) + endif() + find_package(Qt${Qt_VERSION_MAJOR} COMPONENTS Core REQUIRED) + message(STATUS "Found Qt: ${Qt${Qt_VERSION_MAJOR}_DIR} " + "(found version \"${Qt${Qt_VERSION_MAJOR}_VERSION}\")") + target_link_libraries(sentry PRIVATE Qt${Qt_VERSION_MAJOR}::Core) +endif() + +include(CMakePackageConfigHelpers) +configure_package_config_file(sentry-config.cmake.in sentry-config.cmake + INSTALL_DESTINATION "${CMAKE_INSTALL_CMAKEDIR}") + +# generate package version file +# We would have liked to use `SameMinorVersion`, but that is only supported on +# CMake >= 3.11. +write_basic_package_version_file(sentry-config-version.cmake + VERSION ${SENTRY_VERSION} + COMPATIBILITY SameMajorVersion) + +sentry_install(TARGETS sentry EXPORT sentry + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + PUBLIC_HEADER DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" +) +sentry_install(EXPORT sentry NAMESPACE sentry:: FILE sentry-targets.cmake + DESTINATION "${CMAKE_INSTALL_CMAKEDIR}") +sentry_install( + FILES + "${PROJECT_BINARY_DIR}/sentry-config.cmake" + "${PROJECT_BINARY_DIR}/sentry-config-version.cmake" + DESTINATION "${CMAKE_INSTALL_CMAKEDIR}") +if(WIN32 AND MSVC AND SENTRY_BUILD_SHARED_LIBS) + sentry_install(FILES $ + DESTINATION "${CMAKE_INSTALL_BINDIR}" OPTIONAL) +endif() + +# ===== tests ===== + +if(SENTRY_BUILD_TESTS) + add_subdirectory(tests/unit) +endif() + +# ===== example, also used as integration test ===== + +if(SENTRY_BUILD_EXAMPLES) + add_executable(sentry_example examples/example.c) + target_link_libraries(sentry_example PRIVATE sentry) + + if(MSVC) + target_compile_options(sentry_example PRIVATE $) + endif() + + # set static runtime if enabled + if(SENTRY_BUILD_RUNTIMESTATIC AND MSVC) + set_property(TARGET sentry_example PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + endif() + + if(DEFINED SENTRY_FOLDER) + set_target_properties(sentry_example PROPERTIES FOLDER ${SENTRY_FOLDER}) + endif() + + add_test(NAME sentry_example COMMAND sentry_example) +endif() diff --git a/shared/sentry/CONTRIBUTING.md b/shared/sentry/CONTRIBUTING.md new file mode 100644 index 000000000..655543465 --- /dev/null +++ b/shared/sentry/CONTRIBUTING.md @@ -0,0 +1,141 @@ +# Contribution guidelines + +We love and welcome contributions! + +In order to maintain a high quality, we run a number of checks on +different OS / Compiler combinations, and using different analysis tools. + +## Prerequisites + +Building and testing `sentry-native` currently requires the following tools: + +- **CMake** and a supported C/C++ compiler, to actually build the code. +- **python** and **pytest**, to run integration tests. +- **clang-format** and **black**, to format the C/C++ and python code respectively. +- **curl** and **zlib** libraries (e.g. on Ubuntu: libcurl4-openssl-dev, libz-dev) + +`pytest` and `black` are installed as virtualenv dependencies automatically. + +## Setting up Environment + + $ make setup + +This sets up both git, including a pre-commit hook and submodules, and installs +a python virtualenv which is used to run tests and formatting. + +## Formatting Code + + $ make format + +This should be done automatically as part of the pre-commit hook, but can also +be done manually. + + $ black tests + +## Running Tests + + $ make test + +Creates a python virtualenv, and runs all the tests through `pytest`. + +**Running integration tests manually**: + + $ pytest --verbose --maxfail=1 --capture=no tests/ + +When all the python dependencies have been installed, the integration test suite +can also be invoked directly. + +The `maxfail` parameter will abort after the first failure, and `capture=no` +will print the complete compiler output, and test log. + +**Running unit tests**: + + $ make test-unit + +Unit tests also have a dedicated `make` target, if they need to be run separately +from the integration tests. + +**Running unit tests manually**: + + $ cmake -B build -D CMAKE_RUNTIME_OUTPUT_DIRECTORY=$(pwd)/build + $ cmake --build build --target sentry_test_unit + $ ./build/sentry_test_unit + +The unit tests are a separate executable target and can be built and run on +their own. + +## How to interpret CI failures + +The way that tests are run unfortunately does not make it immediately obvious from +the summary what the actual failure is, especially for compile-time failures. +In such cases, it is good to scan the test output _from top to bottom_ and find +the offending compile error. + +When running tests locally, one can use the `--maxfail=1` / `-x` parameter to +abort after the first failure. + +## Integration Test Parameters + +The integration test suite runs the `sentry_example` target using a variety of +different compile-time parameters, and asserts different use-cases. + +Some of its behavior is controlled by env-variables: + +- `ERROR_ON_WARNINGS`: Turns on `-Werror` for gcc compatible compilers. + This is also the default for `MSVC` on windows. +- `RUN_ANALYZER`: Runs the code with/through one or more of the given analyzers. + This accepts a comma-separated list, and currently has support for: + - `asan`: Uses clangs AddressSanitizer and runs integration tests with the + `detect_leaks` flag. + - `scan-build`: Runs the build through the `scan-build` tool. + - `code-checker`: Uses the [`CodeChecker`](https://github.com/Ericsson/codechecker) + tool for builds. + - `kcov`: Uses [`kcov`](https://github.com/SimonKagstrom/kcov) to collect + code-coverage statistics. + - `valgrind`: Uses [`valgrind`](https://valgrind.org/) to check for memory + issues such as leaks. + - `gcc`: Use the `-fanalyzer` flag of `gcc > 10`. + This is currently not stable enough to use, as it leads to false positives + and internal compiler errors. +- `TEST_X86`: Passes flags to CMake to enable a 32-bit (cross-)compile. +- `ANDROID_API` / `ANDROID_NDK` / `ANDROID_ARCH`: Instructs the test runner to + build using the given Android `NDK` version, targeting the given `API` and + `ARCH`. The test runner assumes an already running simulator matching the + `ARCH`, and will run the tests on that. + +**Analyzer Requirements**: + +Some tools, such as `kcov` and `valgrind` have their own distribution packages. +Clang-based tools may require an up-to-date clang, and a separate `clang-tools` +packages. +`CodeChecker` has its own +[install instructions](https://github.com/Ericsson/codechecker#install-guide) +with a list of needed dependencies. + +**Running examples manually**: + + $ cmake -B build -D CMAKE_RUNTIME_OUTPUT_DIRECTORY=$(pwd)/build + $ cmake --build build --target sentry_example + $ ./build/sentry_example log capture-event + +The example can be run manually with a variety of commands to test different +scenarios. Additionally, it will use the `SENTRY_DSN` env-variable, and can thus +also be used to capture events/crashes directly to sentry. + +The example currently supports the following commends: + +- `capture-event`: Captures an event. +- `crash`: Triggers a crash to be captured. +- `log`: Enables debug logging. +- `release-env`: Uses the `SENTRY_RELEASE` env-variable for the release, + instead of a hardcoded value. +- `attachment`: Adds an attachment, which is currently defined as the + `CMakeCache.txt` file, which is part of the CMake build folder. +- `stdout`: Uses a custom transport which dumps all envelopes to `stdout`. +- `no-setup`: Skips all scope and breadcrumb initialization code. +- `start-session`: Starts a new release-health session. +- `overflow-breadcrumbs`: Creates a large number of breadcrumbs that overflow + the maximum allowed number. +- `capture-multiple`: Captures a number of events. +- `sleep`: Introduces a 10 second sleep. +- `add-stacktrace`: Adds the current thread stacktrace to the captured event. diff --git a/shared/sentry/LICENSE b/shared/sentry/LICENSE new file mode 100644 index 000000000..a9bdf9d55 --- /dev/null +++ b/shared/sentry/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2019 Sentry (https://sentry.io) and individual contributors. +All rights reserved. + +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. diff --git a/shared/sentry/README.md b/shared/sentry/README.md new file mode 100644 index 000000000..35071767b --- /dev/null +++ b/shared/sentry/README.md @@ -0,0 +1,320 @@ +

+ + + +
+

+ +# Official Sentry SDK for C/C++ + +The _Sentry Native SDK_ is an error and crash reporting client for native +applications, optimized for C and C++. Sentry allows to add tags, breadcrumbs +and arbitrary custom context to enrich error reports. Supports Sentry _20.6.0_ +and later. + +**Note**: This SDK is being actively developed and still in Beta. We recommend +to check for updates regularly to benefit from latest features and bug fixes. +Please see [Known Limitations](#known-limitations). + +## Resources + +- [Discord](https://discord.gg/ez5KZN7) server for project discussions. +- Follow [@getsentry](https://twitter.com/getsentry) on Twitter for updates + +## Table of Contents + +- [Downloads](#downloads) + - [What is Inside](#what-is-inside) +- [Platform and Feature Support](#platform-and-feature-support) +- [Building and Installation](#building-and-installation) + - [Compile-Time Options](#compile-time-options) + - [Build Targets](#build-targets) +- [Runtime Configuration](#runtime-configuration) +- [Known Limitations](#known-limitations) +- [Development](#development) + +## Downloads + +The SDK can be downloaded from the [Releases] page, which also lists the +changelog of every version. + +[releases]: https://github.com/getsentry/sentry-native/releases + +### What is Inside + +The SDK bundle contains the following folders: + +- `external`: These are external projects which are consumed via + `git submodules`. +- `include`: Contains the Sentry header file. Set the include path to this + directory or copy the header file to your source tree so that it is available + during the build. +- `src`: Sources of the Sentry SDK required for building. + +## Platform and Feature Support + +The SDK currently supports and is tested on the following OS/Compiler variations: + +- 64bit Linux with GCC 9 +- 64bit Linux with clang 9 +- 32bit Linux with GCC 7 (cross compiled from 64bit host) +- 32bit Windows with MSVC 2019 +- 64bit Windows with MSVC 2022 +- macOS Catalina with most recent Compiler toolchain +- Android API29 built by NDK21 toolchain +- Android API16 built by NDK19 toolchain + +Additionally, the SDK should support the following platforms, although they are +not automatically tested, so breakage may occur: + +- Windows Versions lower than Windows 10 / Windows Server 2016 +- Windows builds with the MSYS2 + MinGW + Clang toolchain + +The SDK supports different features on the target platform: + +- **HTTP Transport** is currently only supported on Windows and platforms that + have the `curl` library available. On other platforms, library users need to + implement their own transport, based on the `function transport` API. +- **Crashpad Backend** is currently only supported on Linux, Windows and macOS. +- **Client-side stackwalking** is currently only supported on Linux, Windows, and macOS. + +## Building and Installation + +The SDK is developed and shipped as a [CMake] project. +CMake will pick an appropriate compiler and buildsystem toolchain automatically +per platform, and can also be configured for cross-compilation. +System-wide installation of the resulting sentry library is also possible via +CMake. + +Building the Crashpad Backend requires a `C++14` compatible compiler. + +**Build example**: + +```sh +# configure the cmake build into the `build` directory, with crashpad (on macOS) +$ cmake -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo +# build the project +$ cmake --build build --parallel +# install the resulting artifacts into a specific prefix (use the correct config on windows) +$ cmake --install build --prefix install --config RelWithDebInfo +# which will result in the following (on macOS): +$ exa --tree install +install +├── bin +│ └── crashpad_handler +├── include +│ └── sentry.h +└── lib + ├── cmake + │ └── sentry + ├── libsentry.dylib + └── libsentry.dylib.dSYM +``` + +Please refer to the CMake Manual for more details. + +**Android**: + +The CMake project can also be configured to correctly work with the Android NDK, +see the dedicated [CMake Guide] for details on how to integrate it with gradle +or use it on the command line. + +[cmake]: https://cmake.org/cmake/help/latest/ +[cmake guide]: https://developer.android.com/ndk/guides/cmake + +**MinGW**: + +64-bits is the only platform supported for now. +LLVM + Clang are mandatory here : they are required to generate .pdb files, used by Crashpad for the report generation. + +For your application to generate the appropriate .pdb output, you need to activate CodeView file format generation on your application target. To do so, update your own CMakeLists.txt with something like `target_compile_options(${yourApplicationTarget} PRIVATE -gcodeview)`. + +If you use a MSYS2 environement to compile with MinGW, make sure to : + +- Create an environement variable `MINGW_ROOT` (ex : `C:/msys64/mingw64`) +- Run from `mingw64.exe` : `pacman -S --needed - < ./toolchains/msys2-mingw64-pkglist.txt` +- Build as : + +```sh +# Configure with Ninja as generator and use the MSYS2 toolchain file +$ cmake -GNinja -Bbuild -H. -DCMAKE_TOOLCHAIN_FILE=toolchains/msys2.cmake +# build with Ninja +$ ninja -C build +``` + +**MacOS**: + +Building universal binaries/libraries is possible out of the box when using the +[`CMAKE_OSX_ARCHITECTURES`](https://cmake.org/cmake/help/latest/variable/CMAKE_OSX_ARCHITECTURES.html) define, both with the `Xcode` generator as well +as the default generator: + +```sh +# using xcode generator: +$ cmake -B xcodebuild -GXcode -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" +$ xcodebuild build -project xcodebuild/Sentry-Native.xcodeproj +$ lipo -info xcodebuild/Debug/libsentry.dylib +Architectures in the fat file: xcodebuild/Debug/libsentry.dylib are: x86_64 arm64 + +# using default generator: +$ cmake -B defaultbuild -DCMAKE_OSX_ARCHITECTURES="arm64;x86_64" +$ cmake --build defaultbuild --parallel +$ lipo -info defaultbuild/libsentry.dylib +Architectures in the fat file: defaultbuild/libsentry.dylib are: x86_64 arm64 +``` + +Make sure that MacOSX SDK 11 or later is used. It is possible that this requires +manually overriding the `SDKROOT`: + +```sh +$ export SDKROOT=$(xcrun --sdk macosx --show-sdk-path) +``` + +### Compile-Time Options + +The following options can be set when running the cmake generator, for example +using `cmake -D BUILD_SHARED_LIBS=OFF ..`. + +- `SENTRY_BUILD_SHARED_LIBS` (Default: ON): + By default, `sentry` is built as a shared library. Setting this option to + `OFF` will build `sentry` as a static library instead. + If sentry is used as a subdirectory of another project, the value `BUILD_SHARED_LIBS` will be inherited by default. + + When using `sentry` as a static library, make sure to `#define SENTRY_BUILD_STATIC 1` before including the sentry header. + +- `SENTRY_PIC` (Default: ON): + By default, `sentry` is built as a position independent library. + +- `SENTRY_EXPORT_SYMBOLS` (Default: ON): + By default, `sentry` exposes all symbols in the dynamic symbol table. You might want to disable it in case the program intends to `dlopen` third-party shared libraries and avoid symbol collisions. + +- `SENTRY_BUILD_RUNTIMESTATIC` (Default: OFF): + Enables linking with the static MSVC runtime. Has no effect if the compiler is not MSVC. + +- `SENTRY_LINK_PTHREAD` (Default: ON): + Links platform threads library like `pthread` on unix targets. + +- `SENTRY_BUILD_FORCE32` (Default: OFF): + Forces cross-compilation from 64-bit host to 32-bit target. Only has an effect on Linux. + +- `CMAKE_SYSTEM_VERSION` (Default: depending on Windows SDK version): + Sets up a minimal version of Windows where sentry-native can be guaranteed to run. + Possible values: + + - `5.1` (Windows XP) + - `5.2` (Windows XP 64-bit / Server 2003 / Server 2003 R2) + - `6.0` (Windows Vista / Server 2008) + - `6.1` (Windows 7 / Server 2008 R2) + - `6.2` (Windows 8.0 / Server 2012) + - `6.3` (Windows 8.1 / Server 2012 R2) + - `10` (Windows 10 / Server 2016 / Server 2019) + + For Windows versions below than `6.0` it is also necessary to use XP toolchain + in case of MSVC compiler (pass `-T v141_xp` to CMake command line). + +- `SENTRY_TRANSPORT` (Default: depending on platform): + Sentry can use different http libraries to send reports to the server. + + - **curl**: This uses the `curl` library for HTTP handling. This requires + that the development version of the package is available. + - **winhttp**: This uses the `winhttp` system library, is only supported on + Windows and is the default there. + - **none**: Do not build any http transport. This should be used if users + want to handle uploads themselves + +- `SENTRY_BACKEND` (Default: depending on platform): + Sentry can use different backends depending on platform. + + - **crashpad**: This uses the out-of-process crashpad handler. It is currently + only supported on Desktop OSs, and used as the default on Windows and macOS. + - **breakpad**: This uses the in-process breakpad handler. It is currently + only supported on Desktop OSs, and used as the default on Linux. + - **inproc**: A small in-process handler which is supported on all platforms, + and is used as default on Android. + - **none**: This builds `sentry-native` without a backend, so it does not handle + crashes at all. It is primarily used for tests. + +- `SENTRY_INTEGRATION_QT` (Default: OFF): + Builds the Qt integration, which turns Qt log messages into breadcrumbs. + +- `SENTRY_BREAKPAD_SYSTEM` / `SENTRY_CRASHPAD_SYSTEM` (Default: OFF): + This instructs the build system to use system-installed breakpad or crashpad + libraries instead of using the in-tree version. This is generally not recommended + for crashpad, as sentry uses a patched version that has attachment support. + This is being worked on upstream as well, and a future version might work with + an unmodified crashpad version as well. + +| Feature | Windows | macOS | Linux | Android | iOS | +| ---------- | ------- | ----- | ----- | ------- | --- | +| Transports | | | | | | +| - curl | | ☑ | ☑ | (✓) | | +| - winhttp | ☑ | | | | | +| - none | ✓ | ✓ | ✓ | ☑ | ☑ | +| | | | | | | +| Backends | | | | | | +| - inproc | ✓ | ✓ | ✓ | ☑ | | +| - crashpad | ☑ | ☑ | ✓ | | | +| - breakpad | ✓ | ✓ | ☑ | (✓) | (✓) | +| - none | ✓ | ✓ | ✓ | ✓ | | + +Legend: + +- ☑ default +- ✓ supported +- unsupported + +- `SENTRY_FOLDER` (Default: not defined): + Sets the sentry-native projects folder name for generators which support project hierarchy (like Microsoft Visual Studio). + To use this feature you need to enable hierarchy via [`USE_FOLDERS` property](https://cmake.org/cmake/help/latest/prop_gbl/USE_FOLDERS.html) + +- `CRASHPAD_ENABLE_STACKTRACE` (Default: OFF): + This enables client-side stackwalking when using the crashpad backend. Stack unwinding will happen on the client's machine + and the result will be submitted to Sentry attached to the generated minidump. + Note that this feature is still experimental. + +- `SENTRY_SDK_NAME` (Default: sentry.native or sentry.native.android): + Sets the SDK name that should be included in the reported events. If you're overriding this, make sure to also define + the same value using `target_compile_definitions()` on your own targets that include `sentry.h`. + +### Build Targets + +- `sentry`: This is the main library and the only default build target. +- `crashpad_handler`: When configured with the `crashpad` backend, this is + the out of process crash handler, which will need to be installed along with + the projects executable. +- `sentry_test_unit`: These are the main unit-tests, which are conveniently built + also by the toplevel makefile. +- `sentry_example`: This is a small example program highlighting the API, which + can be controlled via command-line parameters, and is also used for + integration tests. + +## Runtime Configuration + +A minimal working example looks like this. For a more elaborate example see the [example.c](examples/example.c) file which is also used to run sentries integration tests. + +```c +sentry_options_t *options = sentry_options_new(); +sentry_options_set_dsn(options, "https://YOUR_KEY@oORG_ID.ingest.sentry.io/PROJECT_ID"); +sentry_init(options); + +// your application code … + +sentry_close(); +``` + +Other important configuration options include: + +- `sentry_options_set_database_path`: Sentry needs to persist some cache data across application restarts, especially for proper handling of release health sessions. It is recommended to set an explicit absolute path corresponding to the applications cache directory (equivalent to `AppData/Local` on Windows, and `XDG_CACHE_HOME` on Linux). Sentry should be given its own directory which is not shared with other application data, as the SDK will enumerate and possibly delete files in that directory. An example might be `$XDG_CACHE_HOME/your-app/sentry`. + When not set explicitly, sentry will create and use the `.sentry-native` directory inside of the current working directory. +- `sentry_options_set_handler_path`: When using the crashpad backend, sentry will look for a `crashpad_handler` executable in the same directory as the running executable. It is recommended to set this as an explicit absolute path based on the applications install location. +- `sentry_options_set_release`: Some features in sentry, including release health, need to have a release version set. This corresponds to the application’s version and needs to be set explicitly. See [Releases](https://docs.sentry.io/product/releases/) for more information. + +## Known Limitations + +- The crashpad backend on macOS currently has no support for notifying the crashing + process, and can thus not properly terminate sessions or call the registered + `before_send` hook. It will also lose any events that have been queued for + sending at time of crash. + +## Development + +Please see the [contribution guide](./CONTRIBUTING.md). diff --git a/shared/sentry/examples/example.c b/shared/sentry/examples/example.c new file mode 100644 index 000000000..de086ea2e --- /dev/null +++ b/shared/sentry/examples/example.c @@ -0,0 +1,264 @@ +#ifdef _WIN32 +# define WIN32_LEAN_AND_MEAN +# define NOMINMAX +# define _CRT_SECURE_NO_WARNINGS +#endif + +#include "sentry.h" +#include +#include +#include +#include +#ifdef NDEBUG +# undef NDEBUG +#endif +#include + +#ifdef SENTRY_PLATFORM_WINDOWS +# include +# define sleep_s(SECONDS) Sleep((SECONDS)*1000) +#else +# include +# include +# define sleep_s(SECONDS) sleep(SECONDS) +#endif + +static void +print_envelope(sentry_envelope_t *envelope, void *unused_state) +{ + (void)unused_state; + size_t size_out = 0; + char *s = sentry_envelope_serialize(envelope, &size_out); + printf("%s", s); + sentry_free(s); + sentry_envelope_free(envelope); +} + +static bool +has_arg(int argc, char **argv, const char *arg) +{ + for (int i = 1; i < argc; i++) { + if (strcmp(argv[i], arg) == 0) { + return true; + } + } + return false; +} + +#ifdef SENTRY_PLATFORM_AIX +// AIX has a null page mapped to the bottom of memory, which means null derefs +// don't segfault. try dereferencing the top of memory instead; the top nibble +// seems to be unusable. +static void *invalid_mem = (void *)0xFFFFFFFFFFFFFF9B; // -100 for memset +#else +static void *invalid_mem = (void *)1; +#endif + +static void +trigger_crash() +{ + memset((char *)invalid_mem, 1, 100); +} + +int +main(int argc, char **argv) +{ + sentry_options_t *options = sentry_options_new(); + + // this is an example. for real usage, make sure to set this explicitly to + // an app specific cache location. + sentry_options_set_database_path(options, ".sentry-native"); + + sentry_options_set_auto_session_tracking(options, false); + sentry_options_set_symbolize_stacktraces(options, true); + + sentry_options_set_environment(options, "development"); + // sentry defaults this to the `SENTRY_RELEASE` env variable + if (!has_arg(argc, argv, "release-env")) { + sentry_options_set_release(options, "test-example-release"); + } + + if (has_arg(argc, argv, "log")) { + sentry_options_set_debug(options, 1); + } + + if (has_arg(argc, argv, "attachment")) { + // assuming the example / test is run directly from the cmake build + // directory + sentry_options_add_attachment(options, "./CMakeCache.txt"); + } + + if (has_arg(argc, argv, "stdout")) { + sentry_options_set_transport( + options, sentry_transport_new(print_envelope)); + } + + if (has_arg(argc, argv, "capture-transaction")) { + sentry_options_set_traces_sample_rate(options, 1.0); + } + + if (has_arg(argc, argv, "child-spans")) { + sentry_options_set_max_spans(options, 5); + } + + sentry_init(options); + + if (!has_arg(argc, argv, "no-setup")) { + sentry_set_transaction("test-transaction"); + sentry_set_level(SENTRY_LEVEL_WARNING); + sentry_set_extra("extra stuff", sentry_value_new_string("some value")); + sentry_set_extra("…unicode key…", + // https://xkcd.com/1813/ :-) + sentry_value_new_string("őá…–🤮🚀¿ 한글 테스트")); + sentry_set_tag("expected-tag", "some value"); + sentry_set_tag("not-expected-tag", "some value"); + sentry_remove_tag("not-expected-tag"); + + sentry_value_t context = sentry_value_new_object(); + sentry_value_set_by_key( + context, "type", sentry_value_new_string("runtime")); + sentry_value_set_by_key( + context, "name", sentry_value_new_string("testing-runtime")); + sentry_set_context("runtime", context); + + sentry_value_t user = sentry_value_new_object(); + sentry_value_set_by_key(user, "id", sentry_value_new_int32(42)); + sentry_value_set_by_key( + user, "username", sentry_value_new_string("some_name")); + sentry_set_user(user); + + sentry_value_t default_crumb + = sentry_value_new_breadcrumb(NULL, "default level is info"); + sentry_add_breadcrumb(default_crumb); + + sentry_value_t debug_crumb + = sentry_value_new_breadcrumb("http", "debug crumb"); + sentry_value_set_by_key( + debug_crumb, "category", sentry_value_new_string("example!")); + sentry_value_set_by_key( + debug_crumb, "level", sentry_value_new_string("debug")); + sentry_add_breadcrumb(debug_crumb); + + sentry_value_t nl_crumb + = sentry_value_new_breadcrumb(NULL, "lf\ncrlf\r\nlf\n..."); + sentry_value_set_by_key( + nl_crumb, "category", sentry_value_new_string("something else")); + sentry_add_breadcrumb(nl_crumb); + } + + if (has_arg(argc, argv, "start-session")) { + sentry_start_session(); + } + + if (has_arg(argc, argv, "overflow-breadcrumbs")) { + for (size_t i = 0; i < 101; i++) { + char buffer[4]; + snprintf(buffer, 4, "%zu", i); + sentry_add_breadcrumb(sentry_value_new_breadcrumb(0, buffer)); + } + } + + if (has_arg(argc, argv, "capture-multiple")) { + for (size_t i = 0; i < 10; i++) { + char buffer[10]; + snprintf(buffer, 10, "Event #%zu", i); + + sentry_value_t event = sentry_value_new_message_event( + SENTRY_LEVEL_INFO, NULL, buffer); + sentry_capture_event(event); + } + } + + if (has_arg(argc, argv, "reinstall")) { + sentry_reinstall_backend(); + } + + if (has_arg(argc, argv, "sleep")) { + sleep_s(10); + } + + if (has_arg(argc, argv, "crash")) { + trigger_crash(); + } + if (has_arg(argc, argv, "assert")) { + assert(0); + } + if (has_arg(argc, argv, "abort")) { + abort(); + } +#ifdef SENTRY_PLATFORM_UNIX + if (has_arg(argc, argv, "raise")) { + raise(SIGSEGV); + } + if (has_arg(argc, argv, "kill")) { + kill(getpid(), SIGSEGV); + } +#endif + + if (has_arg(argc, argv, "capture-event")) { + sentry_value_t event = sentry_value_new_message_event( + SENTRY_LEVEL_INFO, "my-logger", "Hello World!"); + if (has_arg(argc, argv, "add-stacktrace")) { + sentry_event_value_add_stacktrace(event, NULL, 0); + } + sentry_capture_event(event); + } + if (has_arg(argc, argv, "capture-exception")) { + sentry_value_t exc = sentry_value_new_exception( + "ParseIntError", "invalid digit found in string"); + if (has_arg(argc, argv, "add-stacktrace")) { + sentry_value_t stacktrace = sentry_value_new_stacktrace(NULL, 0); + sentry_value_set_by_key(exc, "stacktrace", stacktrace); + } + sentry_value_t event = sentry_value_new_event(); + sentry_event_add_exception(event, exc); + + sentry_capture_event(event); + } + + if (has_arg(argc, argv, "capture-transaction")) { + sentry_transaction_context_t *tx_ctx + = sentry_transaction_context_new("little.teapot", + "Short and stout here is my handle and here is my spout"); + + if (has_arg(argc, argv, "unsample-tx")) { + sentry_transaction_context_set_sampled(tx_ctx, 0); + } + sentry_transaction_t *tx + = sentry_transaction_start(tx_ctx, sentry_value_new_null()); + + if (has_arg(argc, argv, "error-status")) { + sentry_transaction_set_status( + tx, SENTRY_SPAN_STATUS_INTERNAL_ERROR); + } + + if (has_arg(argc, argv, "child-spans")) { + sentry_span_t *child + = sentry_transaction_start_child(tx, "littler.teapot", NULL); + sentry_span_t *grandchild + = sentry_span_start_child(child, "littlest.teapot", NULL); + + if (has_arg(argc, argv, "error-status")) { + sentry_span_set_status(child, SENTRY_SPAN_STATUS_NOT_FOUND); + sentry_span_set_status( + grandchild, SENTRY_SPAN_STATUS_ALREADY_EXISTS); + } + + sentry_span_finish(grandchild); + sentry_span_finish(child); + } + + sentry_transaction_finish(tx); + } + + // make sure everything flushes + sentry_close(); + + if (has_arg(argc, argv, "sleep-after-shutdown")) { + sleep_s(1); + } + + if (has_arg(argc, argv, "crash-after-shutdown")) { + trigger_crash(); + } +} diff --git a/shared/sentry/external/CMakeLists.txt b/shared/sentry/external/CMakeLists.txt new file mode 100644 index 000000000..24bfe0186 --- /dev/null +++ b/shared/sentry/external/CMakeLists.txt @@ -0,0 +1,174 @@ +# The list of files is drived from: breakpad/Makefile.am + +set(BREAKPAD_SOURCES_COMMON + breakpad/src/common/convert_UTF.cc + breakpad/src/common/convert_UTF.h + breakpad/src/common/md5.cc + breakpad/src/common/md5.h + breakpad/src/common/string_conversion.cc + breakpad/src/common/string_conversion.h +) + +set(BREAKPAD_SOURCES_COMMON_LINUX + breakpad/src/common/linux/elf_core_dump.cc + breakpad/src/common/linux/elfutils.cc + breakpad/src/common/linux/elfutils.h + breakpad/src/common/linux/file_id.cc + breakpad/src/common/linux/file_id.h + breakpad/src/common/linux/guid_creator.cc + breakpad/src/common/linux/guid_creator.h + breakpad/src/common/linux/linux_libc_support.cc + breakpad/src/common/linux/memory_mapped_file.cc + breakpad/src/common/linux/safe_readlink.cc +) + +set(BREAKPAD_SOURCES_COMMON_LINUX_GETCONTEXT + breakpad/src/common/linux/breakpad_getcontext.S +) + +set(BREAKPAD_SOURCES_COMMON_ANDROID + breakpad/src/common/android/include/sys/procfs.h +) + +set(BREAKPAD_SOURCES_COMMON_WINDOWS + breakpad/src/common/windows/guid_string.cc + breakpad/src/common/windows/guid_string.h +) + +set(BREAKPAD_SOURCES_COMMON_APPLE + breakpad/src/common/mac/file_id.cc + breakpad/src/common/mac/file_id.h + breakpad/src/common/mac/macho_id.cc + breakpad/src/common/mac/macho_id.h + breakpad/src/common/mac/macho_utilities.cc + breakpad/src/common/mac/macho_utilities.h + breakpad/src/common/mac/macho_walker.cc + breakpad/src/common/mac/macho_walker.h + breakpad/src/common/mac/string_utilities.cc + breakpad/src/common/mac/string_utilities.h +) + +set(BREAKPAD_SOURCES_COMMON_MAC + breakpad/src/common/mac/MachIPC.mm + breakpad/src/common/mac/bootstrap_compat.cc + breakpad/src/common/mac/bootstrap_compat.h +) + +set(BREAKPAD_SOURCES_CLIENT_LINUX + breakpad/src/client/minidump_file_writer-inl.h + breakpad/src/client/minidump_file_writer.cc + breakpad/src/client/minidump_file_writer.h + breakpad/src/client/linux/crash_generation/crash_generation_client.cc + breakpad/src/client/linux/crash_generation/crash_generation_server.cc + breakpad/src/client/linux/dump_writer_common/thread_info.cc + breakpad/src/client/linux/dump_writer_common/ucontext_reader.cc + breakpad/src/client/linux/handler/exception_handler.cc + breakpad/src/client/linux/handler/exception_handler.h + breakpad/src/client/linux/handler/minidump_descriptor.cc + breakpad/src/client/linux/handler/minidump_descriptor.h + breakpad/src/client/linux/log/log.cc + breakpad/src/client/linux/log/log.h + breakpad/src/client/linux/microdump_writer/microdump_writer.cc + breakpad/src/client/linux/microdump_writer/microdump_writer.h + breakpad/src/client/linux/minidump_writer/linux_core_dumper.cc + breakpad/src/client/linux/minidump_writer/linux_dumper.cc + breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper.cc + breakpad/src/client/linux/minidump_writer/minidump_writer.cc +) + +set(BREAKPAD_SOURCES_CLIENT_WINDOWS + breakpad/src/client/windows/crash_generation/crash_generation_client.cc + breakpad/src/client/windows/crash_generation/crash_generation_client.h + breakpad/src/client/windows/handler/exception_handler.cc + breakpad/src/client/windows/handler/exception_handler.h +) + +set(BREAKPAD_SOURCES_CLIENT_APPLE + breakpad/src/client/minidump_file_writer-inl.h + breakpad/src/client/minidump_file_writer.cc + breakpad/src/client/minidump_file_writer.h + breakpad/src/client/mac/handler/breakpad_nlist_64.cc + breakpad/src/client/mac/handler/breakpad_nlist_64.h + breakpad/src/client/mac/handler/dynamic_images.cc + breakpad/src/client/mac/handler/dynamic_images.h + breakpad/src/client/mac/handler/minidump_generator.cc + breakpad/src/client/mac/handler/minidump_generator.h +) + +set(BREAKPAD_SOURCES_CLIENT_MAC + breakpad/src/client/mac/handler/exception_handler.cc + breakpad/src/client/mac/handler/exception_handler.h + breakpad/src/client/mac/crash_generation/crash_generation_client.cc + breakpad/src/client/mac/crash_generation/crash_generation_client.h +) + +set(BREAKPAD_SOURCES_CLIENT_IOS + breakpad/src/client/ios/exception_handler_no_mach.cc + breakpad/src/client/ios/exception_handler_no_mach.h + breakpad/src/client/ios/handler/ios_exception_minidump_generator.h + breakpad/src/client/ios/handler/ios_exception_minidump_generator.mm + breakpad/src/client/mac/crash_generation/ConfigFile.h + breakpad/src/client/mac/crash_generation/ConfigFile.mm + breakpad/src/client/mac/handler/mach_vm_compat.h + breakpad/src/client/mac/handler/protected_memory_allocator.cc + breakpad/src/client/mac/handler/protected_memory_allocator.h + breakpad/src/client/mac/handler/ucontext_compat.h +) + + +add_library(breakpad_client STATIC) +target_sources(breakpad_client PRIVATE ${BREAKPAD_SOURCES_COMMON}) + +if(LINUX OR ANDROID) + target_sources(breakpad_client PRIVATE ${BREAKPAD_SOURCES_COMMON_LINUX} ${BREAKPAD_SOURCES_CLIENT_LINUX}) + if(ANDROID) + target_sources(breakpad_client PRIVATE ${BREAKPAD_SOURCES_COMMON_ANDROID}) + target_include_directories(breakpad_client PRIVATE breakpad/src/common/android/include) + endif(ANDROID) + + include(CheckFunctionExists) + check_function_exists(getcontext HAVE_GETCONTEXT) + if(HAVE_GETCONTEXT) + target_compile_definitions(breakpad_client PRIVATE HAVE_GETCONTEXT) + else() + target_sources(breakpad_client PRIVATE ${BREAKPAD_SOURCES_COMMON_LINUX_GETCONTEXT}) + endif() + + set_property(TARGET breakpad_client PROPERTY POSITION_INDEPENDENT_CODE ON) +endif() + +if(APPLE) + target_sources(breakpad_client PRIVATE + ${BREAKPAD_SOURCES_COMMON_APPLE} + ${BREAKPAD_SOURCES_CLIENT_APPLE}) + if(NOT IOS) + target_sources(breakpad_client PRIVATE + ${BREAKPAD_SOURCES_COMMON_MAC} + ${BREAKPAD_SOURCES_CLIENT_MAC}) + else() + target_sources(breakpad_client PRIVATE + ${BREAKPAD_SOURCES_CLIENT_IOS}) + endif() + + target_link_libraries(breakpad_client PRIVATE "-framework CoreFoundation") +endif() + +if(WIN32) + target_sources(breakpad_client PRIVATE ${BREAKPAD_SOURCES_COMMON_WINDOWS} ${BREAKPAD_SOURCES_CLIENT_WINDOWS}) + target_compile_definitions(breakpad_client PRIVATE _UNICODE UNICODE) + + # set static runtime if enabled + if(SENTRY_BUILD_RUNTIMESTATIC AND MSVC) + set_property(TARGET breakpad_client PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + endif() +endif() + +# breakpad has includes directly to `third_party/lss/...`, +# which are being resolved correctly when we add the current directory to +# the include directories. A giant hack, yes, but it works +target_include_directories(breakpad_client + PRIVATE + "$" + PUBLIC + "$" +) diff --git a/shared/sentry/external/breakpad/.clang-format b/shared/sentry/external/breakpad/.clang-format new file mode 100644 index 000000000..d175ab754 --- /dev/null +++ b/shared/sentry/external/breakpad/.clang-format @@ -0,0 +1,9 @@ +# Defines the Chromium style for automatic reformatting. +# https://clang.llvm.org/docs/ClangFormatStyleOptions.html +BasedOnStyle: Chromium + +# This defaults to 'Auto'. Explicitly set it for a while, so that +# 'vector >' in existing files gets formatted to +# 'vector>'. ('Auto' means that clang-format will only use +# 'int>>' if the file already contains at least one such instance.) +Standard: Cpp11 diff --git a/shared/sentry/external/breakpad/.github/workflows/build-test-ci.yml b/shared/sentry/external/breakpad/.github/workflows/build-test-ci.yml new file mode 100644 index 000000000..2196d55f5 --- /dev/null +++ b/shared/sentry/external/breakpad/.github/workflows/build-test-ci.yml @@ -0,0 +1,73 @@ +# GitHub actions workflow. +# https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions + +name: Build+Test CI + +on: + push: + branches: [main] + + schedule: + # The GH mirroring from Google GoB does not trigger push actions. + # Fire it every other day to provide some coverage. This will run ~8 AM PT. + - cron: '39 3 */2 * *' + + # Allow for manual triggers from the web. + workflow_dispatch: + +jobs: + autotools: + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + cc: gcc + cxx: g++ + - os: ubuntu-latest + cc: clang + cxx: clang++ + - os: macos-latest + cc: clang + cxx: clang++ + runs-on: ${{ matrix.os }} + env: + CC: ${{ matrix.cc }} + CXX: ${{ matrix.cxx }} + + steps: + - name: System settings + run: | + set -x + $CC --version + $CXX --version + getconf _NPROCESSORS_ONLN + getconf _NPROCESSORS_CONF + true + + - name: Checkout depot_tools + run: git clone --depth=1 https://chromium.googlesource.com/chromium/tools/depot_tools.git ../depot_tools + + - name: Checkout breakpad + run: | + set -xe + PATH+=:$PWD/../depot_tools + gclient config --unmanaged --name=src https://github.com/${{ github.repository }} + gclient sync --no-history --nohooks + + # First build & test in-tree. + - run: ./configure --disable-silent-rules + working-directory: src + - run: make -j$(getconf _NPROCESSORS_CONF) + working-directory: src + - run: make -j$(getconf _NPROCESSORS_CONF) check VERBOSE=1 + working-directory: src + - run: make -j$(getconf _NPROCESSORS_CONF) distclean + working-directory: src + + # Then build & test out-of-tree. + - run: mkdir -p src/build/native + - run: ../../configure --disable-silent-rules + working-directory: src/build/native + - run: make -j$(getconf _NPROCESSORS_CONF) distcheck VERBOSE=1 + working-directory: src/build/native diff --git a/shared/sentry/external/breakpad/.github/workflows/close-pull-request.yml b/shared/sentry/external/breakpad/.github/workflows/close-pull-request.yml new file mode 100644 index 000000000..da11c4a6e --- /dev/null +++ b/shared/sentry/external/breakpad/.github/workflows/close-pull-request.yml @@ -0,0 +1,22 @@ +# GitHub actions workflow. +# https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions + +# https://github.com/superbrothers/close-pull-request +name: Close Pull Request + +on: + pull_request_target: + types: [opened] + +jobs: + run: + runs-on: ubuntu-latest + steps: + - uses: superbrothers/close-pull-request@v3 + with: + comment: > + Thanks for your contribution! + Unfortunately, we don't use GitHub pull requests to manage code + contributions to this repository. + Instead, please see [README.md](../blob/HEAD/README.md) which + provides full instructions on how to get involved. diff --git a/shared/sentry/external/breakpad/.github/workflows/coverity.yml b/shared/sentry/external/breakpad/.github/workflows/coverity.yml new file mode 100644 index 000000000..06f8e2763 --- /dev/null +++ b/shared/sentry/external/breakpad/.github/workflows/coverity.yml @@ -0,0 +1,43 @@ +# GitHub actions workflow. +# https://docs.github.com/en/actions/learn-github-actions/workflow-syntax-for-github-actions + +# https://scan.coverity.com/projects/google-breakpad +name: Coverity Scan + +on: + push: + branches: [main] + + schedule: + # The GH mirroring from Google GoB does not trigger push actions. + # Fire it once a week to provide some coverage. + - cron: '39 2 * * WED' + + # Allow for manual triggers from the web. + workflow_dispatch: + +jobs: + coverity: + runs-on: ubuntu-latest + env: + CC: clang + CXX: clang++ + steps: + - name: Checkout depot_tools + run: git clone --depth=1 https://chromium.googlesource.com/chromium/tools/depot_tools.git ../depot_tools + + - name: Checkout breakpad + run: | + set -xe + PATH+=:$PWD/../depot_tools + gclient config --unmanaged --name=src https://github.com/${{ github.repository }} + gclient sync --no-history --nohooks + + - run: ./configure --disable-silent-rules + working-directory: src + + - uses: vapier/coverity-scan-action@v1 + with: + command: make -C src -O -j$(getconf _NPROCESSORS_CONF) + email: ${{ secrets.COVERITY_SCAN_EMAIL }} + token: ${{ secrets.COVERITY_SCAN_TOKEN }} diff --git a/shared/sentry/external/breakpad/.gitignore b/shared/sentry/external/breakpad/.gitignore new file mode 100644 index 000000000..d6140d8c1 --- /dev/null +++ b/shared/sentry/external/breakpad/.gitignore @@ -0,0 +1,92 @@ +# 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. + +# Ignore other VCSs. +.repo/ +.svn/ + +# Ignore common compiled artifacts. +*~ +*.dwo +*.o +lib*.a +/breakpad.pc +/breakpad-client.pc +/src/client/linux/linux_client_unittest_shlib +/src/client/linux/linux_dumper_unittest_helper +/src/common/linux/google_crashdump_uploader_test +/src/processor/microdump_stackwalk +/src/processor/minidump_dump +/src/processor/minidump_stackwalk +/src/tools/linux/core2md/core2md +/src/tools/linux/core_handler/core_handler +/src/tools/linux/dump_syms/dump_syms +/src/tools/linux/md2core/minidump-2-core +/src/tools/linux/pid2md/pid2md +/src/tools/linux/symupload/minidump_upload +/src/tools/linux/symupload/sym_upload +/src/tools/mac/dump_syms/dump_syms +/src/tools/mac/dump_syms/dump_syms_mac + +# Ignore unit test artifacts. +*_unittest +*.log +*.trs + +# Ignore autotools generated artifacts. +.deps +.dirstamp +autom4te.cache/ +/config.cache +config.h +/config.log +/config.status +/Makefile +stamp-h1 + +# Ignore GYP generated Visual Studio artifacts. +*.filters +*.sdf +*.sln +*.suo +*.vcproj +*.vcxproj + +# Ignore GYP generated Makefiles +src/Makefile +*.Makefile +*.target.mk + +# Ignore compiled Python files. +*.pyc + +# Ignore directories gclient syncs. +src/testing +src/third_party/lss +src/third_party/protobuf +src/tools/gyp diff --git a/shared/sentry/external/breakpad/AUTHORS b/shared/sentry/external/breakpad/AUTHORS new file mode 100644 index 000000000..4858b377c --- /dev/null +++ b/shared/sentry/external/breakpad/AUTHORS @@ -0,0 +1 @@ +opensource@google.com diff --git a/shared/sentry/external/breakpad/ChangeLog b/shared/sentry/external/breakpad/ChangeLog new file mode 100644 index 000000000..e69de29bb diff --git a/shared/sentry/external/breakpad/DEPS b/shared/sentry/external/breakpad/DEPS new file mode 100644 index 000000000..a5b53aa9c --- /dev/null +++ b/shared/sentry/external/breakpad/DEPS @@ -0,0 +1,84 @@ +# Copyright 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 used to mimic the svn:externals mechanism for gclient (both Git and +# SVN) based checkouts of Breakpad. As such, its use is entirely optional. If +# using a manually managed SVN checkout as opposed to a gclient managed checkout +# you can still use the hooks mechanism for generating project files by calling +# 'gclient runhooks' rather than 'gclient sync'. + +deps = { + # Testing libraries and utilities. + "src/src/testing": + "https://github.com/google/googletest.git" + + "@release-1.11.0", + + # Protobuf. + "src/src/third_party/protobuf/protobuf": + "https://github.com/google/protobuf.git" + + "@cb6dd4ef5f82e41e06179dcd57d3b1d9246ad6ac", + + # GYP project generator. + "src/src/tools/gyp": + "https://chromium.googlesource.com/external/gyp/" + + "@324dd166b7c0b39d513026fa52d6280ac6d56770", + + # Linux syscall support. + "src/src/third_party/lss": + "https://chromium.googlesource.com/linux-syscall-support/" + + "@e1e7b0ad8ee99a875b272c8e33e308472e897660", +} + +hooks = [ + { + # Keep the manifest up to date. + "action": ["python", "src/src/tools/python/deps-to-manifest.py", + "src/DEPS", "src/default.xml"], + }, +] + +hooks_os = { + 'win': [ + { + # TODO(chrisha): Fix the GYP files so that they work without + # --no-circular-check. + "pattern": ".", + "action": ["python", + "src/src/tools/gyp/gyp_main.py", + "--no-circular-check", + "src/src/client/windows/breakpad_client.gyp"], + }, + { + # XXX: this and above should all be wired into build/all.gyp ? + "action": ["python", + "src/src/tools/gyp/gyp_main.py", + "--no-circular-check", + "src/src/tools/windows/tools_windows.gyp"], + }, + ], +} diff --git a/shared/sentry/external/breakpad/DIR_METADATA b/shared/sentry/external/breakpad/DIR_METADATA new file mode 100644 index 000000000..bc37756ef --- /dev/null +++ b/shared/sentry/external/breakpad/DIR_METADATA @@ -0,0 +1,13 @@ +# Metadata information for this directory. +# +# For more information on DIR_METADATA files, see: +# https://source.chromium.org/chromium/infra/infra/+/HEAD:go/src/infra/tools/dirmd/README.md +# +# For the schema of this file, see Metadata message: +# https://source.chromium.org/chromium/infra/infra/+/HEAD:go/src/infra/tools/dirmd/proto/dir_metadata.proto + +monorail { + project: "google-breakpad" +} + +team_email: "google-breakpad-dev@googlegroups.com" diff --git a/shared/sentry/external/breakpad/INSTALL b/shared/sentry/external/breakpad/INSTALL new file mode 100644 index 000000000..007e9396d --- /dev/null +++ b/shared/sentry/external/breakpad/INSTALL @@ -0,0 +1,370 @@ +Installation Instructions +************************* + +Copyright (C) 1994-1996, 1999-2002, 2004-2013 Free Software Foundation, +Inc. + + Copying and distribution of this file, with or without modification, +are permitted in any medium without royalty provided the copyright +notice and this notice are preserved. This file is offered as-is, +without warranty of any kind. + +Basic Installation +================== + + Briefly, the shell commands `./configure; make; make install' should +configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. Some packages provide this +`INSTALL' file but do not implement all of the features documented +below. The lack of an optional feature in a given package is not +necessarily a bug. More recommendations for GNU packages can be found +in *note Makefile Conventions: (standards)Makefile Conventions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. + + The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. + + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package, generally using the just-built uninstalled binaries. + + 4. Type `make install' to install the programs and any data files and + documentation. When installing into a prefix owned by root, it is + recommended that the package be configured and built as a regular + user, and only the `make install' phase executed with root + privileges. + + 5. Optionally, type `make installcheck' to repeat any self-tests, but + this time using the binaries in their final installed location. + This target does not install anything. Running this target as a + regular user, particularly if the prior `make install' required + root privileges, verifies that the installation completed + correctly. + + 6. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + 7. Often, you can also type `make uninstall' to remove the installed + files again. In practice, not all packages have tested that + uninstallation works correctly, even though it is required by the + GNU Coding Standards. + + 8. Some packages, particularly those that use Automake, provide `make + distcheck', which can by used by developers to test that all other + targets like `make install' and `make uninstall' work correctly. + This target is generally not run by end users. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. This +is known as a "VPATH" build. + + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + + On MacOS X 10.5 and later systems, you can create libraries and +executables that work on multiple system types--known as "fat" or +"universal" binaries--by specifying multiple `-arch' options to the +compiler but only a single `-arch' option to the preprocessor. Like +this: + + ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CPP="gcc -E" CXXCPP="g++ -E" + + This is not guaranteed to produce working output in all cases, you +may have to build one architecture at a time and combine the results +using the `lipo' tool if you have problems. + +Installation Names +================== + + By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX', where PREFIX must be an +absolute file name. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. In general, the +default for these options is expressed in terms of `${prefix}', so that +specifying just `--prefix' will affect all of the other directory +specifications that were not explicitly provided. + + The most portable way to affect installation locations is to pass the +correct locations to `configure'; however, many packages provide one or +both of the following shortcuts of passing variable assignments to the +`make install' command line to change installation locations without +having to reconfigure or recompile. + + The first method involves providing an override variable for each +affected directory. For example, `make install +prefix=/alternate/directory' will choose an alternate location for all +directory configuration variables that were expressed in terms of +`${prefix}'. Any directories that were specified during `configure', +but not in terms of `${prefix}', must each be overridden at install +time for the entire installation to be relocated. The approach of +makefile variable overrides for each directory variable is required by +the GNU Coding Standards, and ideally causes no recompilation. +However, some platforms have known limitations with the semantics of +shared libraries that end up requiring recompilation when using this +method, particularly noticeable in packages that use GNU Libtool. + + The second method involves providing the `DESTDIR' variable. For +example, `make install DESTDIR=/alternate/directory' will prepend +`/alternate/directory' before all installation names. The approach of +`DESTDIR' overrides is not required by the GNU Coding Standards, and +does not work on platforms that have drive letters. On the other hand, +it does better at avoiding recompilation issues, and works well even +when some directory options were not specified in terms of `${prefix}' +at `configure' time. + +Optional Features +================= + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + + Some packages offer the ability to configure how verbose the +execution of `make' will be. For these packages, running `./configure +--enable-silent-rules' sets the default to minimal output, which can be +overridden with `make V=1'; while running `./configure +--disable-silent-rules' sets the default to verbose, which can be +overridden with `make V=0'. + +Particular systems +================== + + On HP-UX, the default C compiler is not ANSI C compatible. If GNU +CC is not installed, it is recommended to use the following options in +order to use an ANSI C compiler: + + ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" + +and if that doesn't work, install pre-built binaries of GCC for HP-UX. + + HP-UX `make' updates targets which have the same time stamps as +their prerequisites, which makes it generally unusable when shipped +generated files such as `configure' are involved. Use GNU `make' +instead. + + On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot +parse its `' header file. The option `-nodtk' can be used as +a workaround. If GNU CC is not installed, it is therefore recommended +to try + + ./configure CC="cc" + +and if that doesn't work, try + + ./configure CC="cc -nodtk" + + On Solaris, don't put `/usr/ucb' early in your `PATH'. This +directory contains several dysfunctional programs; working variants of +these programs are available in `/usr/bin'. So, if you need `/usr/ucb' +in your `PATH', put it _after_ `/usr/bin'. + + On Haiku, software installed for all users goes in `/boot/common', +not `/usr/local'. It is recommended to use the following options: + + ./configure --prefix=/boot/common + +Specifying the System Type +========================== + + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS + KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf limitation. Until the limitation is lifted, you can use +this workaround: + + CONFIG_SHELL=/bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + + `configure' recognizes the following options to control how it +operates. + +`--help' +`-h' + Print a summary of all of the options to `configure', and exit. + +`--help=short' +`--help=recursive' + Print a summary of the options unique to this package's + `configure', and exit. The `short' variant lists options used + only in the top level, while the `recursive' variant lists options + also present in any nested packages. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--prefix=DIR' + Use DIR as the installation prefix. *note Installation Names:: + for more details, including other options available for fine-tuning + the installation locations. + +`--no-create' +`-n' + Run the configure checks, but stop before creating any output + files. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. diff --git a/shared/sentry/external/breakpad/LICENSE b/shared/sentry/external/breakpad/LICENSE new file mode 100644 index 000000000..a840b2647 --- /dev/null +++ b/shared/sentry/external/breakpad/LICENSE @@ -0,0 +1,132 @@ +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. + +-------------------------------------------------------------------- + +Copyright 2001-2004 Unicode, Inc. + +Disclaimer + +This source code is provided as is by Unicode, Inc. No claims are +made as to fitness for any particular purpose. No warranties of any +kind are expressed or implied. The recipient agrees to determine +applicability of information provided. If this file has been +purchased on magnetic or optical media from Unicode, Inc., the +sole remedy for any claim will be exchange of defective media +within 90 days of receipt. + +Limitations on Rights to Redistribute This Code + +Unicode, Inc. hereby grants the right to freely use the information +supplied in this file in the creation of products supporting the +Unicode Standard, and to make copies of this file in any form +for internal or external distribution as long as this notice +remains attached. + +-------------------------------------------------------------------- + +libunwind - a platform-independent unwind library + Copyright (C) 2008 Google, Inc + Contributed by Paul Pluzhnikov + Copyright (C) 2010 Konstantin Belousov + +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) 1999 Apple Computer, Inc. All rights reserved. + +@APPLE_LICENSE_HEADER_START@ + +This file contains Original Code and/or Modifications of Original Code +as defined in and that are subject to the Apple Public Source License +Version 2.0 (the 'License'). You may not use this file except in +compliance with the License. Please obtain a copy of the License at +http://www.opensource.apple.com/apsl/ and read it before using this +file. + +The Original Code and all software distributed under the License are +distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +Please see the License for the specific language governing rights and +limitations under the License. + +@APPLE_LICENSE_HEADER_END@ + +-------------------------------------------------------------------- + +Copyright (c) 1989, 1993 +The Regents of the University of California. All rights reserved. + +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. +3. All advertising materials mentioning features or use of this software + must display the following acknowledgement: + This product includes software developed by the University of + California, Berkeley and its contributors. +4. Neither the name of the University 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 REGENTS 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 REGENTS 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. diff --git a/shared/sentry/external/breakpad/Makefile.am b/shared/sentry/external/breakpad/Makefile.am new file mode 100644 index 000000000..e7dc06ac7 --- /dev/null +++ b/shared/sentry/external/breakpad/Makefile.am @@ -0,0 +1,1630 @@ +## Process this file with automake to produce Makefile.in + +# 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. + + +# This allows #includes to be relative to src/ +AM_CPPFLAGS = -I$(top_srcdir)/src +AM_CFLAGS = +AM_CXXFLAGS = + +if ANDROID_HOST +# This allows using fixed NDK headers when building for Android. +AM_CXXFLAGS += -I$(top_srcdir)/src/common/android/include +# This is only necessary for building the unit tests until GTest is upgraded +# to a future version. +AM_CXXFLAGS += -I$(top_srcdir)/src/common/android/testing/include +endif + +AM_CXXFLAGS += $(WARN_CXXFLAGS) + +if LINUX_HOST +# Build as PIC on Linux, for linux_client_unittest_shlib +AM_CFLAGS += -fPIC +AM_CXXFLAGS += -fPIC +endif + +# Specify include paths for ac macros +ACLOCAL_AMFLAGS = -I m4 + +# License file is called LICENSE not COPYING +AUTOMAKE_OPTIONS = foreign + +## Documentation +docdir = $(prefix)/share/doc/$(PACKAGE)-$(VERSION) + +dist_doc_DATA = \ + AUTHORS \ + ChangeLog \ + INSTALL \ + LICENSE \ + NEWS \ + README.md + +## Headers +if LINUX_HOST +includeclhdir = $(includedir)/$(PACKAGE)/client/linux/handler +includeclh_HEADERS = $(top_srcdir)/src/client/linux/handler/*.h + +includecldwcdir = $(includedir)/$(PACKAGE)/client/linux/dump_writer_common +includecldwc_HEADERS = $(top_srcdir)/src/client/linux/dump_writer_common/*.h + +includeclmdir = $(includedir)/$(PACKAGE)/client/linux/minidump_writer +includeclm_HEADERS = $(top_srcdir)/src/client/linux/minidump_writer/*.h + +includeclcdir = $(includedir)/$(PACKAGE)/client/linux/crash_generation +includeclc_HEADERS = $(top_srcdir)/src/client/linux/crash_generation/*.h + +includelssdir = $(includedir)/$(PACKAGE)/third_party/lss +includelss_HEADERS = $(top_srcdir)/src/third_party/lss/*.h + +includecldir = $(includedir)/$(PACKAGE)/common/linux +includecl_HEADERS = $(top_srcdir)/src/common/linux/*.h +endif + +includegbcdir = $(includedir)/$(PACKAGE)/google_breakpad/common +includegbc_HEADERS = $(top_srcdir)/src/google_breakpad/common/*.h + +includecdir = $(includedir)/$(PACKAGE)/common +includec_HEADERS = $(top_srcdir)/src/common/*.h + +includepdir = $(includedir)/$(PACKAGE)/processor +includep_HEADERS = $(top_srcdir)/src/processor/*.h + +## pkgconfig files +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = + +## Common test logic +if SYSTEM_TEST_LIBS +TEST_CFLAGS = $(GTEST_CFLAGS) $(GMOCK_CFLAGS) +TEST_LIBS = $(GTEST_LIBS) -lgtest_main $(GMOCK_LIBS) +TEST_DEPS = +else +TEST_CFLAGS = \ + -I$(top_srcdir)/src/testing/include \ + -I$(top_srcdir)/src/testing/googletest/include \ + -I$(top_srcdir)/src/testing/googletest \ + -I$(top_srcdir)/src/testing/googlemock/include \ + -I$(top_srcdir)/src/testing/googlemock \ + -I$(top_srcdir)/src/testing +TEST_LIBS = src/testing/libtesting.a +TEST_DEPS = $(TEST_LIBS) +endif + +## Libraries +check_LIBRARIES = +noinst_LIBRARIES = +lib_LIBRARIES = +libexec_PROGRAMS = +bin_PROGRAMS = +check_PROGRAMS = +EXTRA_PROGRAMS = +CLEANFILES = + +check_LIBRARIES += src/testing/libtesting.a + +if !SYSTEM_TEST_LIBS +src_testing_libtesting_a_SOURCES = \ + src/breakpad_googletest_includes.h \ + src/testing/googletest/src/gtest-all.cc \ + src/testing/googletest/src/gtest_main.cc \ + src/testing/googlemock/src/gmock-all.cc +src_testing_libtesting_a_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) +endif + +if !DISABLE_PROCESSOR +lib_LIBRARIES += src/libbreakpad.a +pkgconfig_DATA += breakpad.pc +noinst_LIBRARIES += src/third_party/libdisasm/libdisasm.a +endif + +if LINUX_HOST +lib_LIBRARIES += src/client/linux/libbreakpad_client.a +pkgconfig_DATA += breakpad-client.pc + +src_client_linux_libbreakpad_client_a_SOURCES = \ + src/client/linux/crash_generation/crash_generation_client.cc \ + src/client/linux/crash_generation/crash_generation_server.cc \ + src/client/linux/dump_writer_common/thread_info.cc \ + src/client/linux/dump_writer_common/ucontext_reader.cc \ + src/client/linux/handler/exception_handler.cc \ + src/client/linux/handler/exception_handler.h \ + src/client/linux/handler/minidump_descriptor.cc \ + src/client/linux/handler/minidump_descriptor.h \ + src/client/linux/log/log.cc \ + src/client/linux/log/log.h \ + src/client/linux/microdump_writer/microdump_writer.cc \ + src/client/linux/microdump_writer/microdump_writer.h \ + src/client/linux/minidump_writer/linux_core_dumper.cc \ + src/client/linux/minidump_writer/linux_dumper.cc \ + src/client/linux/minidump_writer/linux_ptrace_dumper.cc \ + src/client/linux/minidump_writer/minidump_writer.cc \ + src/client/minidump_file_writer-inl.h \ + src/client/minidump_file_writer.cc \ + src/client/minidump_file_writer.h \ + src/common/convert_UTF.cc \ + src/common/convert_UTF.h \ + src/common/md5.cc \ + src/common/md5.h \ + src/common/string_conversion.cc \ + src/common/string_conversion.h \ + src/common/linux/elf_core_dump.cc \ + src/common/linux/elfutils.cc \ + src/common/linux/elfutils.h \ + src/common/linux/file_id.cc \ + src/common/linux/file_id.h \ + src/common/linux/guid_creator.cc \ + src/common/linux/guid_creator.h \ + src/common/linux/linux_libc_support.cc \ + src/common/linux/memory_mapped_file.cc \ + src/common/linux/safe_readlink.cc +if !HAVE_GETCONTEXT +src_client_linux_libbreakpad_client_a_SOURCES += \ + src/common/linux/breakpad_getcontext.S +endif +endif LINUX_HOST + +if !DISABLE_PROCESSOR +src_libbreakpad_a_SOURCES = \ + src/google_breakpad/common/breakpad_types.h \ + src/google_breakpad/common/minidump_format.h \ + src/google_breakpad/common/minidump_size.h \ + src/google_breakpad/processor/basic_source_line_resolver.h \ + src/google_breakpad/processor/call_stack.h \ + src/google_breakpad/processor/code_module.h \ + src/google_breakpad/processor/code_modules.h \ + src/google_breakpad/processor/dump_context.h \ + src/google_breakpad/processor/dump_object.h \ + src/google_breakpad/processor/exploitability.h \ + src/google_breakpad/processor/fast_source_line_resolver.h \ + src/google_breakpad/processor/memory_region.h \ + src/google_breakpad/processor/microdump.h \ + src/google_breakpad/processor/microdump_processor.h \ + src/google_breakpad/processor/minidump.h \ + src/google_breakpad/processor/minidump_processor.h \ + src/google_breakpad/processor/process_result.h \ + src/google_breakpad/processor/process_state.h \ + src/google_breakpad/processor/proc_maps_linux.h \ + src/google_breakpad/processor/source_line_resolver_base.h \ + src/google_breakpad/processor/source_line_resolver_interface.h \ + src/google_breakpad/processor/stack_frame.h \ + src/google_breakpad/processor/stack_frame_cpu.h \ + src/google_breakpad/processor/stack_frame_symbolizer.h \ + src/google_breakpad/processor/stackwalker.h \ + src/google_breakpad/processor/symbol_supplier.h \ + src/google_breakpad/processor/system_info.h \ + src/processor/address_map-inl.h \ + src/processor/address_map.h \ + src/processor/basic_code_module.h \ + src/processor/basic_code_modules.cc \ + src/processor/basic_code_modules.h \ + src/processor/basic_source_line_resolver_types.h \ + src/processor/basic_source_line_resolver.cc \ + src/processor/call_stack.cc \ + src/processor/cfi_frame_info.cc \ + src/processor/cfi_frame_info.h \ + src/processor/contained_range_map-inl.h \ + src/processor/contained_range_map.h \ + src/processor/convert_old_arm64_context.cc \ + src/processor/convert_old_arm64_context.h \ + src/processor/disassembler_x86.h \ + src/processor/disassembler_x86.cc \ + src/processor/dump_context.cc \ + src/processor/dump_object.cc \ + src/processor/exploitability.cc \ + src/processor/exploitability_linux.h \ + src/processor/exploitability_linux.cc \ + src/processor/exploitability_win.h \ + src/processor/exploitability_win.cc \ + src/processor/fast_source_line_resolver_types.h \ + src/processor/fast_source_line_resolver.cc \ + src/processor/linked_ptr.h \ + src/processor/logging.h \ + src/processor/logging.cc \ + src/processor/map_serializers-inl.h \ + src/processor/map_serializers.h \ + src/processor/microdump.cc \ + src/processor/microdump_processor.cc \ + src/processor/minidump.cc \ + src/processor/minidump_processor.cc \ + src/processor/module_comparer.cc \ + src/processor/module_comparer.h \ + src/processor/module_factory.h \ + src/processor/module_serializer.cc \ + src/processor/module_serializer.h \ + src/processor/pathname_stripper.cc \ + src/processor/pathname_stripper.h \ + src/processor/postfix_evaluator-inl.h \ + src/processor/postfix_evaluator.h \ + src/processor/process_state.cc \ + src/processor/proc_maps_linux.cc \ + src/processor/range_map-inl.h \ + src/processor/range_map.h \ + src/processor/simple_serializer-inl.h \ + src/processor/simple_serializer.h \ + src/processor/simple_symbol_supplier.cc \ + src/processor/simple_symbol_supplier.h \ + src/processor/windows_frame_info.h \ + src/processor/source_line_resolver_base_types.h \ + src/processor/source_line_resolver_base.cc \ + src/processor/stack_frame_cpu.cc \ + src/processor/stack_frame_symbolizer.cc \ + src/processor/stackwalk_common.cc \ + src/processor/stackwalk_common.h \ + src/processor/stackwalker.cc \ + src/processor/stackwalker_amd64.cc \ + src/processor/stackwalker_amd64.h \ + src/processor/stackwalker_arm.cc \ + src/processor/stackwalker_arm.h \ + src/processor/stackwalker_arm64.cc \ + src/processor/stackwalker_arm64.h \ + src/processor/stackwalker_address_list.cc \ + src/processor/stackwalker_address_list.h \ + src/processor/stackwalker_mips.cc \ + src/processor/stackwalker_mips.h \ + src/processor/stackwalker_ppc.cc \ + src/processor/stackwalker_ppc.h \ + src/processor/stackwalker_ppc64.cc \ + src/processor/stackwalker_ppc64.h \ + src/processor/stackwalker_sparc.cc \ + src/processor/stackwalker_sparc.h \ + src/processor/stackwalker_x86.cc \ + src/processor/stackwalker_x86.h \ + src/processor/static_address_map-inl.h \ + src/processor/static_address_map.h \ + src/processor/static_contained_range_map-inl.h \ + src/processor/static_contained_range_map.h \ + src/processor/static_map_iterator-inl.h \ + src/processor/static_map_iterator.h \ + src/processor/static_map-inl.h \ + src/processor/static_map.h \ + src/processor/static_range_map-inl.h \ + src/processor/static_range_map.h \ + src/processor/symbolic_constants_win.cc \ + src/processor/symbolic_constants_win.h \ + src/processor/tokenize.cc \ + src/processor/tokenize.h + +src_third_party_libdisasm_libdisasm_a_SOURCES = \ + src/third_party/libdisasm/ia32_implicit.c \ + src/third_party/libdisasm/ia32_implicit.h \ + src/third_party/libdisasm/ia32_insn.c \ + src/third_party/libdisasm/ia32_insn.h \ + src/third_party/libdisasm/ia32_invariant.c \ + src/third_party/libdisasm/ia32_invariant.h \ + src/third_party/libdisasm/ia32_modrm.c \ + src/third_party/libdisasm/ia32_modrm.h \ + src/third_party/libdisasm/ia32_opcode_tables.c \ + src/third_party/libdisasm/ia32_opcode_tables.h \ + src/third_party/libdisasm/ia32_operand.c \ + src/third_party/libdisasm/ia32_operand.h \ + src/third_party/libdisasm/ia32_reg.c \ + src/third_party/libdisasm/ia32_reg.h \ + src/third_party/libdisasm/ia32_settings.c \ + src/third_party/libdisasm/ia32_settings.h \ + src/third_party/libdisasm/libdis.h \ + src/third_party/libdisasm/qword.h \ + src/third_party/libdisasm/x86_disasm.c \ + src/third_party/libdisasm/x86_format.c \ + src/third_party/libdisasm/x86_imm.c \ + src/third_party/libdisasm/x86_imm.h \ + src/third_party/libdisasm/x86_insn.c \ + src/third_party/libdisasm/x86_misc.c \ + src/third_party/libdisasm/x86_operand_list.c \ + src/third_party/libdisasm/x86_operand_list.h + +## Programs +bin_PROGRAMS += \ + src/processor/microdump_stackwalk \ + src/processor/minidump_dump \ + src/processor/minidump_stackwalk +endif !DISABLE_PROCESSOR + +if LINUX_HOST +EXTRA_PROGRAMS += \ + src/client/linux/linux_dumper_unittest_helper +CLEANFILES += \ + src/client/linux/linux_dumper_unittest_helper + +if !DISABLE_TOOLS +bin_PROGRAMS += \ + src/tools/linux/core2md/core2md \ + src/tools/linux/pid2md/pid2md \ + src/tools/linux/dump_syms/dump_syms \ + src/tools/linux/md2core/minidump-2-core \ + src/tools/linux/symupload/minidump_upload \ + src/tools/linux/symupload/sym_upload +if X86_HOST +bin_PROGRAMS += \ + src/tools/mac/dump_syms/dump_syms_mac +endif +if HAVE_MEMFD_CREATE +libexec_PROGRAMS += \ + src/tools/linux/core_handler/core_handler +endif +endif +endif LINUX_HOST + + +## Tests +if !DISABLE_PROCESSOR +check_PROGRAMS += \ + src/common/test_assembler_unittest \ + src/common/dwarf/dwarf2reader_lineinfo_unittest \ + src/common/dwarf/dwarf2reader_splitfunctions_unittest \ + src/processor/address_map_unittest \ + src/processor/basic_source_line_resolver_unittest \ + src/processor/cfi_frame_info_unittest \ + src/processor/contained_range_map_unittest \ + src/processor/disassembler_x86_unittest \ + src/processor/exploitability_unittest \ + src/processor/fast_source_line_resolver_unittest \ + src/processor/map_serializers_unittest \ + src/processor/microdump_processor_unittest \ + src/processor/minidump_processor_unittest \ + src/processor/minidump_unittest \ + src/processor/static_address_map_unittest \ + src/processor/static_contained_range_map_unittest \ + src/processor/static_map_unittest \ + src/processor/static_range_map_unittest \ + src/processor/pathname_stripper_unittest \ + src/processor/postfix_evaluator_unittest \ + src/processor/proc_maps_linux_unittest \ + src/processor/range_map_truncate_lower_unittest \ + src/processor/range_map_truncate_upper_unittest \ + src/processor/range_map_unittest \ + src/processor/stackwalker_amd64_unittest \ + src/processor/stackwalker_arm_unittest \ + src/processor/stackwalker_arm64_unittest \ + src/processor/stackwalker_address_list_unittest \ + src/processor/stackwalker_mips_unittest \ + src/processor/stackwalker_mips64_unittest \ + src/processor/stackwalker_x86_unittest \ + src/processor/synth_minidump_unittest +endif + +if LINUX_HOST +EXTRA_PROGRAMS += \ + src/client/linux/linux_client_unittest_shlib +CLEANFILES += \ + src/client/linux/linux_client_unittest_shlib + +check_PROGRAMS += \ + src/client/linux/linux_client_unittest \ + src/common/linux/google_crashdump_uploader_test + +if !DISABLE_TOOLS +check_PROGRAMS += \ + src/common/dumper_unittest \ + src/tools/linux/md2core/minidump_2_core_unittest +if X86_HOST +check_PROGRAMS += \ + src/common/mac/macho_reader_unittest +endif +endif +endif LINUX_HOST + +if !DISABLE_PROCESSOR +if SELFTEST +check_PROGRAMS += \ + src/processor/stackwalker_selftest +endif SELFTEST +endif !DISABLE_PROCESSOR + +if !DISABLE_PROCESSOR +check_SCRIPTS = \ + src/processor/microdump_stackwalk_test \ + src/processor/microdump_stackwalk_machine_readable_test \ + src/processor/minidump_dump_test \ + src/processor/minidump_stackwalk_test \ + src/processor/minidump_stackwalk_machine_readable_test +endif + +TESTS = $(check_PROGRAMS) $(check_SCRIPTS) + +if ANDROID_HOST +# Since Autotools 1.2, tests are run through a special "test driver" script. +# Unfortunately, it's not possible anymore to specify an alternative shell to +# run them on connected devices, so use a slightly modified version of the +# driver for Android. +LOG_DRIVER = $(top_srcdir)/android/test-driver +else +# The default Autotools test driver script. +if TESTS_AS_ROOT +LOG_DRIVER = $(top_srcdir)/autotools/root-test-driver $(top_srcdir)/autotools/test-driver +else +LOG_DRIVER = $(top_srcdir)/autotools/test-driver +endif !TESTS_AS_ROOT +endif !ANDROID_HOST + +if LINUX_HOST +src_client_linux_linux_dumper_unittest_helper_SOURCES = \ + src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc +src_client_linux_linux_dumper_unittest_helper_LDFLAGS=$(PTHREAD_CFLAGS) +src_client_linux_linux_dumper_unittest_helper_CC=$(PTHREAD_CC) +if ANDROID_HOST +# On Android PTHREAD_CFLAGS is empty, and adding src/common/android/include +# to the include path is necessary to build this program. +src_client_linux_linux_dumper_unittest_helper_CXXFLAGS=$(AM_CXXFLAGS) +else +src_client_linux_linux_dumper_unittest_helper_CXXFLAGS=$(PTHREAD_CFLAGS) +endif + +src_client_linux_linux_client_unittest_shlib_SOURCES = \ + $(src_testing_libtesting_a_SOURCES) \ + src/client/linux/handler/exception_handler_unittest.cc \ + src/client/linux/microdump_writer/microdump_writer_unittest.cc \ + src/client/linux/minidump_writer/directory_reader_unittest.cc \ + src/client/linux/minidump_writer/cpu_set_unittest.cc \ + src/client/linux/minidump_writer/line_reader_unittest.cc \ + src/client/linux/minidump_writer/linux_core_dumper.cc \ + src/client/linux/minidump_writer/linux_core_dumper_unittest.cc \ + src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc \ + src/client/linux/minidump_writer/minidump_writer_unittest.cc \ + src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc \ + src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc \ + src/common/linux/elf_core_dump.cc \ + src/common/linux/linux_libc_support_unittest.cc \ + src/common/linux/tests/auto_testfile.h \ + src/common/linux/tests/crash_generator.cc \ + src/common/memory_allocator_unittest.cc \ + src/common/tests/auto_tempdir.h \ + src/common/tests/file_utils.cc \ + src/common/tests/file_utils.h \ + src/processor/basic_code_modules.cc \ + src/processor/convert_old_arm64_context.cc \ + src/processor/dump_context.cc \ + src/processor/dump_object.cc \ + src/processor/logging.cc \ + src/processor/minidump.cc \ + src/processor/pathname_stripper.cc \ + src/processor/proc_maps_linux.cc +if !HAVE_GETCONTEXT +src_client_linux_linux_client_unittest_shlib_SOURCES += \ + src/common/linux/breakpad_getcontext.S +endif + +src_client_linux_linux_client_unittest_shlib_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) +src_client_linux_linux_client_unittest_shlib_LDFLAGS = \ + -shared \ + -Wl,-h,linux_client_unittest_shlib +src_client_linux_linux_client_unittest_shlib_LDADD = \ + src/client/linux/crash_generation/crash_generation_client.o \ + src/client/linux/dump_writer_common/thread_info.o \ + src/client/linux/dump_writer_common/ucontext_reader.o \ + src/client/linux/handler/exception_handler.o \ + src/client/linux/handler/minidump_descriptor.o \ + src/client/linux/log/log.o \ + src/client/linux/microdump_writer/microdump_writer.o \ + src/client/linux/minidump_writer/linux_dumper.o \ + src/client/linux/minidump_writer/linux_ptrace_dumper.o \ + src/client/linux/minidump_writer/minidump_writer.o \ + src/client/minidump_file_writer.o \ + src/common/convert_UTF.o \ + src/common/md5.o \ + src/common/linux/elfutils.o \ + src/common/linux/file_id.o \ + src/common/linux/guid_creator.o \ + src/common/linux/linux_libc_support.o \ + src/common/linux/memory_mapped_file.o \ + src/common/linux/safe_readlink.o \ + src/common/string_conversion.o \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) +if !HAVE_GETCONTEXT +src_client_linux_linux_client_unittest_shlib_SOURCES += \ + src/common/linux/breakpad_getcontext_unittest.cc +endif +if ANDROID_HOST +src_client_linux_linux_client_unittest_shlib_LDFLAGS += \ + -llog -lm +endif + +src_client_linux_linux_client_unittest_shlib_DEPENDENCIES = \ + src/client/linux/linux_dumper_unittest_helper \ + src/client/linux/libbreakpad_client.a \ + $(TEST_DEPS) \ + src/libbreakpad.a + +src_client_linux_linux_client_unittest_SOURCES = +# The extra-long build id is for a test in minidump_writer_unittest.cc. +src_client_linux_linux_client_unittest_LDFLAGS = \ + -Wl,-rpath,'$$ORIGIN' \ + -Wl,--build-id=0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f +if ANDROID_HOST +src_client_linux_linux_client_unittest_LDFLAGS += \ + -llog +endif + +src_client_linux_linux_client_unittest_LDADD = \ + src/client/linux/linux_client_unittest_shlib \ + $(TEST_LIBS) + +src_client_linux_linux_client_unittest_DEPENDENCIES = \ + src/client/linux/linux_client_unittest_shlib + +if !DISABLE_TOOLS +src_tools_linux_core2md_core2md_SOURCES = \ + src/tools/linux/core2md/core2md.cc + +src_tools_linux_core2md_core2md_LDADD = \ + src/client/linux/libbreakpad_client.a \ + src/common/path_helper.o + +if HAVE_MEMFD_CREATE +src_tools_linux_core_handler_core_handler_SOURCES = \ + src/tools/linux/core_handler/core_handler.cc + +src_tools_linux_core_handler_core_handler_LDADD = \ + src/client/linux/libbreakpad_client.a \ + src/common/path_helper.o +endif + +src_tools_linux_pid2md_pid2md_SOURCES = \ + src/tools/linux/pid2md/pid2md.cc + +src_tools_linux_pid2md_pid2md_LDADD = \ + src/client/linux/libbreakpad_client.a \ + src/common/path_helper.o + +src_tools_linux_dump_syms_dump_syms_SOURCES = \ + src/common/dwarf_cfi_to_module.cc \ + src/common/dwarf_cu_to_module.cc \ + src/common/dwarf_line_to_module.cc \ + src/common/dwarf_range_list_handler.cc \ + src/common/language.cc \ + src/common/module.cc \ + src/common/path_helper.cc \ + src/common/stabs_reader.cc \ + src/common/stabs_to_module.cc \ + src/common/dwarf/bytereader.cc \ + src/common/dwarf/dwarf2diehandler.cc \ + src/common/dwarf/dwarf2reader.cc \ + src/common/dwarf/elf_reader.cc \ + src/common/linux/crc32.cc \ + src/common/linux/dump_symbols.cc \ + src/common/linux/dump_symbols.h \ + src/common/linux/elf_symbols_to_module.cc \ + src/common/linux/elf_symbols_to_module.h \ + src/common/linux/elfutils.cc \ + src/common/linux/file_id.cc \ + src/common/linux/linux_libc_support.cc \ + src/common/linux/memory_mapped_file.cc \ + src/common/linux/safe_readlink.cc \ + src/tools/linux/dump_syms/dump_syms.cc +src_tools_linux_dump_syms_dump_syms_CXXFLAGS = \ + $(RUSTC_DEMANGLE_CFLAGS) +src_tools_linux_dump_syms_dump_syms_LDADD = \ + $(RUSTC_DEMANGLE_LIBS) + +src_tools_linux_md2core_minidump_2_core_SOURCES = \ + src/common/linux/memory_mapped_file.cc \ + src/common/path_helper.cc \ + src/tools/linux/md2core/minidump-2-core.cc \ + src/tools/linux/md2core/minidump_memory_range.h + +src_tools_linux_symupload_minidump_upload_SOURCES = \ + src/common/linux/http_upload.cc \ + src/common/path_helper.cc \ + src/tools/linux/symupload/minidump_upload.cc +src_tools_linux_symupload_minidump_upload_LDADD = -ldl + +src_tools_linux_symupload_sym_upload_SOURCES = \ + src/common/linux/http_upload.cc \ + src/common/linux/http_upload.h \ + src/common/linux/libcurl_wrapper.cc \ + src/common/linux/libcurl_wrapper.h \ + src/common/linux/symbol_collector_client.cc \ + src/common/linux/symbol_collector_client.h \ + src/common/linux/symbol_upload.cc \ + src/common/linux/symbol_upload.h \ + src/common/path_helper.cc \ + src/tools/linux/symupload/sym_upload.cc +src_tools_linux_symupload_sym_upload_LDADD = -ldl + +src_tools_mac_dump_syms_dump_syms_mac_SOURCES = \ + src/common/dwarf_cfi_to_module.cc \ + src/common/dwarf_cu_to_module.cc \ + src/common/dwarf_line_to_module.cc \ + src/common/dwarf_range_list_handler.cc \ + src/common/language.cc \ + src/common/md5.cc \ + src/common/module.cc \ + src/common/path_helper.cc \ + src/common/stabs_reader.cc \ + src/common/stabs_to_module.cc \ + src/common/dwarf/bytereader.cc \ + src/common/dwarf/dwarf2diehandler.cc \ + src/common/dwarf/dwarf2reader.cc \ + src/common/dwarf/elf_reader.cc \ + src/common/mac/arch_utilities.cc \ + src/common/mac/dump_syms.cc \ + src/common/mac/dump_syms.h \ + src/common/mac/file_id.cc \ + src/common/mac/file_id.h \ + src/common/mac/macho_id.cc \ + src/common/mac/macho_id.h \ + src/common/mac/macho_reader.cc \ + src/common/mac/macho_reader.h \ + src/common/mac/macho_utilities.cc \ + src/common/mac/macho_utilities.h \ + src/common/mac/macho_walker.cc \ + src/common/mac/macho_walker.h \ + src/tools/mac/dump_syms/dump_syms_tool.cc +src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS= \ + -I$(top_srcdir)/src/third_party/mac_headers \ + $(RUSTC_DEMANGLE_CFLAGS) \ + -DHAVE_MACH_O_NLIST_H +src_tools_mac_dump_syms_dump_syms_mac_LDADD= \ + $(RUSTC_DEMANGLE_LIBS) + +src_common_dumper_unittest_SOURCES = \ + src/common/byte_cursor_unittest.cc \ + src/common/convert_UTF.cc \ + src/common/dwarf_cfi_to_module.cc \ + src/common/dwarf_cfi_to_module_unittest.cc \ + src/common/dwarf_cu_to_module.cc \ + src/common/dwarf_cu_to_module_unittest.cc \ + src/common/dwarf_line_to_module.cc \ + src/common/dwarf_line_to_module_unittest.cc \ + src/common/dwarf_range_list_handler.cc \ + src/common/language.cc \ + src/common/memory_range_unittest.cc \ + src/common/module.cc \ + src/common/module_unittest.cc \ + src/common/path_helper.cc \ + src/common/stabs_reader.cc \ + src/common/stabs_reader_unittest.cc \ + src/common/stabs_to_module.cc \ + src/common/stabs_to_module_unittest.cc \ + src/common/string_conversion.cc \ + src/common/string_conversion_unittest.cc \ + src/common/test_assembler.cc \ + src/common/dwarf/bytereader.cc \ + src/common/dwarf/bytereader.h \ + src/common/dwarf/bytereader-inl.h \ + src/common/dwarf/bytereader_unittest.cc \ + src/common/dwarf/cfi_assembler.cc \ + src/common/dwarf/cfi_assembler.h \ + src/common/dwarf/dwarf2diehandler.cc \ + src/common/dwarf/dwarf2diehandler_unittest.cc \ + src/common/dwarf/dwarf2reader.cc \ + src/common/dwarf/dwarf2reader.h \ + src/common/dwarf/elf_reader.cc \ + src/common/dwarf/elf_reader.h \ + src/common/dwarf/dwarf2reader_cfi_unittest.cc \ + src/common/dwarf/dwarf2reader_die_unittest.cc \ + src/common/dwarf/dwarf2reader_test_common.h \ + src/common/linux/crc32.cc \ + src/common/linux/dump_symbols.cc \ + src/common/linux/dump_symbols_unittest.cc \ + src/common/linux/elf_core_dump.cc \ + src/common/linux/elf_core_dump_unittest.cc \ + src/common/linux/elf_symbols_to_module.cc \ + src/common/linux/elf_symbols_to_module_unittest.cc \ + src/common/linux/elfutils.cc \ + src/common/linux/file_id.cc \ + src/common/linux/file_id_unittest.cc \ + src/common/linux/linux_libc_support.cc \ + src/common/linux/memory_mapped_file.cc \ + src/common/linux/memory_mapped_file_unittest.cc \ + src/common/linux/safe_readlink.cc \ + src/common/linux/safe_readlink_unittest.cc \ + src/common/linux/synth_elf.cc \ + src/common/linux/synth_elf_unittest.cc \ + src/common/linux/tests/crash_generator.cc \ + src/common/linux/tests/crash_generator.h \ + src/common/testdata/func-line-pairing.h \ + src/common/tests/file_utils.cc +src_common_dumper_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) \ + $(RUSTC_DEMANGLE_CFLAGS) \ + $(PTHREAD_CFLAGS) +src_common_dumper_unittest_LDADD = \ + $(TEST_LIBS) \ + $(RUSTC_DEMANGLE_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +src_common_mac_macho_reader_unittest_SOURCES = \ + src/common/dwarf_cfi_to_module.cc \ + src/common/dwarf_cu_to_module.cc \ + src/common/dwarf_line_to_module.cc \ + src/common/language.cc \ + src/common/md5.cc \ + src/common/module.cc \ + src/common/path_helper.cc \ + src/common/stabs_reader.cc \ + src/common/stabs_to_module.cc \ + src/common/test_assembler.cc \ + src/common/dwarf/bytereader.cc \ + src/common/dwarf/cfi_assembler.cc \ + src/common/dwarf/dwarf2diehandler.cc \ + src/common/dwarf/dwarf2reader.cc \ + src/common/dwarf/elf_reader.cc \ + src/common/mac/arch_utilities.cc \ + src/common/mac/file_id.cc \ + src/common/mac/macho_id.cc \ + src/common/mac/macho_reader.cc \ + src/common/mac/macho_reader_unittest.cc \ + src/common/mac/macho_utilities.cc \ + src/common/mac/macho_walker.cc \ + src/common/tests/file_utils.cc +src_common_mac_macho_reader_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) \ + -I$(top_srcdir)/src/third_party/mac_headers \ + -DHAVE_MACH_O_NLIST_H \ + $(PTHREAD_CFLAGS) +src_common_mac_macho_reader_unittest_LDADD = \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) +endif + +src_common_linux_google_crashdump_uploader_test_SOURCES = \ + src/common/linux/google_crashdump_uploader.cc \ + src/common/linux/google_crashdump_uploader_test.cc \ + src/common/linux/libcurl_wrapper.cc +src_common_linux_google_crashdump_uploader_test_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) +src_common_linux_google_crashdump_uploader_test_LDADD = \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) \ + -ldl + +src_tools_linux_md2core_minidump_2_core_unittest_SOURCES = \ + src/tools/linux/md2core/minidump_memory_range_unittest.cc +src_tools_linux_md2core_minidump_2_core_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) +src_tools_linux_md2core_minidump_2_core_unittest_LDADD = \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +endif LINUX_HOST + +if !DISABLE_PROCESSOR +src_processor_address_map_unittest_SOURCES = \ + src/processor/address_map_unittest.cc +src_processor_address_map_unittest_LDADD = \ + src/processor/logging.o \ + src/processor/pathname_stripper.o + +src_processor_basic_source_line_resolver_unittest_SOURCES = \ + src/processor/basic_source_line_resolver_unittest.cc +src_processor_basic_source_line_resolver_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) +src_processor_basic_source_line_resolver_unittest_LDADD = \ + src/processor/basic_source_line_resolver.o \ + src/processor/cfi_frame_info.o \ + src/processor/pathname_stripper.o \ + src/processor/logging.o \ + src/processor/source_line_resolver_base.o \ + src/processor/tokenize.o \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +src_processor_cfi_frame_info_unittest_SOURCES = \ + src/processor/cfi_frame_info_unittest.cc +src_processor_cfi_frame_info_unittest_LDADD = \ + src/processor/cfi_frame_info.o \ + src/processor/logging.o \ + src/processor/pathname_stripper.o \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) +src_processor_cfi_frame_info_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) + +src_processor_contained_range_map_unittest_SOURCES = \ + src/processor/contained_range_map_unittest.cc +src_processor_contained_range_map_unittest_LDADD = \ + src/processor/logging.o \ + src/processor/pathname_stripper.o + +src_processor_exploitability_unittest_SOURCES = \ + src/processor/exploitability_unittest.cc +src_processor_exploitability_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) +src_processor_exploitability_unittest_LDADD = \ + src/processor/convert_old_arm64_context.o \ + src/processor/minidump_processor.o \ + src/processor/process_state.o \ + src/processor/disassembler_x86.o \ + src/processor/exploitability.o \ + src/processor/exploitability_linux.o \ + src/processor/exploitability_win.o \ + src/processor/basic_code_modules.o \ + src/processor/basic_source_line_resolver.o \ + src/processor/call_stack.o \ + src/processor/cfi_frame_info.o \ + src/processor/dump_context.o \ + src/processor/dump_object.o \ + src/processor/logging.o \ + src/processor/minidump.o \ + src/processor/pathname_stripper.o \ + src/processor/proc_maps_linux.o \ + src/processor/simple_symbol_supplier.o \ + src/processor/source_line_resolver_base.o \ + src/processor/stack_frame_cpu.o \ + src/processor/stack_frame_symbolizer.o \ + src/processor/stackwalker.o \ + src/processor/stackwalker_address_list.o \ + src/processor/stackwalker_amd64.o \ + src/processor/stackwalker_arm.o \ + src/processor/stackwalker_arm64.o \ + src/processor/stackwalker_mips.o \ + src/processor/stackwalker_ppc.o \ + src/processor/stackwalker_ppc64.o \ + src/processor/stackwalker_sparc.o \ + src/processor/stackwalker_x86.o \ + src/processor/symbolic_constants_win.o \ + src/processor/tokenize.o \ + src/third_party/libdisasm/libdisasm.a \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +src_processor_disassembler_x86_unittest_SOURCES = \ + src/processor/disassembler_x86_unittest.cc +src_processor_disassembler_x86_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) +src_processor_disassembler_x86_unittest_LDADD = \ + src/processor/disassembler_x86.o \ + src/third_party/libdisasm/libdisasm.a \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +src_processor_fast_source_line_resolver_unittest_SOURCES = \ + src/processor/fast_source_line_resolver_unittest.cc +src_processor_fast_source_line_resolver_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) +src_processor_fast_source_line_resolver_unittest_LDADD = \ + src/processor/fast_source_line_resolver.o \ + src/processor/basic_source_line_resolver.o \ + src/processor/cfi_frame_info.o \ + src/processor/module_comparer.o \ + src/processor/module_serializer.o \ + src/processor/pathname_stripper.o \ + src/processor/logging.o \ + src/processor/source_line_resolver_base.o \ + src/processor/tokenize.o \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +src_processor_map_serializers_unittest_SOURCES = \ + src/processor/map_serializers_unittest.cc +src_processor_map_serializers_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) +src_processor_map_serializers_unittest_LDADD = \ + src/processor/logging.o \ + src/processor/pathname_stripper.o \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +src_processor_microdump_processor_unittest_SOURCES = \ + src/processor/microdump_processor_unittest.cc +src_processor_microdump_processor_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) +src_processor_microdump_processor_unittest_LDADD = \ + src/processor/basic_code_modules.o \ + src/processor/basic_source_line_resolver.o \ + src/processor/call_stack.o \ + src/processor/convert_old_arm64_context.o \ + src/processor/cfi_frame_info.o \ + src/processor/dump_context.o \ + src/processor/dump_object.o \ + src/processor/logging.o \ + src/processor/microdump.o \ + src/processor/microdump_processor.o \ + src/processor/pathname_stripper.o \ + src/processor/process_state.o \ + src/processor/simple_symbol_supplier.o \ + src/processor/source_line_resolver_base.o \ + src/processor/stack_frame_symbolizer.o \ + src/processor/stackwalker.o \ + src/processor/stackwalker_address_list.o \ + src/processor/stackwalker_amd64.o \ + src/processor/stackwalker_arm.o \ + src/processor/stackwalker_arm64.o \ + src/processor/stackwalker_mips.o \ + src/processor/stackwalker_ppc.o \ + src/processor/stackwalker_ppc64.o \ + src/processor/stackwalker_sparc.o \ + src/processor/stackwalker_x86.o \ + src/processor/tokenize.o \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +src_processor_minidump_processor_unittest_SOURCES = \ + src/processor/minidump_processor_unittest.cc +src_processor_minidump_processor_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) +src_processor_minidump_processor_unittest_LDADD = \ + src/processor/basic_code_modules.o \ + src/processor/basic_source_line_resolver.o \ + src/processor/call_stack.o \ + src/processor/cfi_frame_info.o \ + src/processor/convert_old_arm64_context.o \ + src/processor/disassembler_x86.o \ + src/processor/dump_context.o \ + src/processor/dump_object.o \ + src/processor/exploitability.o \ + src/processor/exploitability_linux.o \ + src/processor/exploitability_win.o \ + src/processor/logging.o \ + src/processor/minidump_processor.o \ + src/processor/minidump.o \ + src/processor/pathname_stripper.o \ + src/processor/process_state.o \ + src/processor/proc_maps_linux.o \ + src/processor/source_line_resolver_base.o \ + src/processor/stack_frame_cpu.o \ + src/processor/stack_frame_symbolizer.o \ + src/processor/stackwalker.o \ + src/processor/stackwalker_address_list.o \ + src/processor/stackwalker_amd64.o \ + src/processor/stackwalker_arm.o \ + src/processor/stackwalker_arm64.o \ + src/processor/stackwalker_mips.o \ + src/processor/stackwalker_ppc.o \ + src/processor/stackwalker_ppc64.o \ + src/processor/stackwalker_sparc.o \ + src/processor/stackwalker_x86.o \ + src/processor/symbolic_constants_win.o \ + src/processor/tokenize.o \ + src/third_party/libdisasm/libdisasm.a \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +src_processor_minidump_unittest_SOURCES = \ + src/common/test_assembler.cc \ + src/processor/minidump_unittest.cc \ + src/processor/synth_minidump.cc +src_processor_minidump_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) +src_processor_minidump_unittest_LDADD = \ + src/processor/basic_code_modules.o \ + src/processor/convert_old_arm64_context.o \ + src/processor/dump_context.o \ + src/processor/dump_object.o \ + src/processor/logging.o \ + src/processor/minidump.o \ + src/processor/pathname_stripper.o \ + src/processor/proc_maps_linux.o \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +src_processor_proc_maps_linux_unittest_SOURCES = \ + src/processor/proc_maps_linux.cc \ + src/processor/proc_maps_linux_unittest.cc +src_processor_proc_maps_linux_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) +src_processor_proc_maps_linux_unittest_LDADD = \ + src/processor/logging.o \ + src/processor/pathname_stripper.o \ + src/third_party/libdisasm/libdisasm.a \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +src_processor_static_address_map_unittest_SOURCES = \ + src/processor/static_address_map_unittest.cc +src_processor_static_address_map_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) +src_processor_static_address_map_unittest_LDADD = \ + src/processor/logging.o \ + src/processor/pathname_stripper.o \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +src_processor_static_contained_range_map_unittest_SOURCES = \ + src/processor/static_contained_range_map_unittest.cc +src_processor_static_contained_range_map_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) +src_processor_static_contained_range_map_unittest_LDADD = \ + src/processor/logging.o \ + src/processor/pathname_stripper.o \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +src_processor_static_map_unittest_SOURCES = \ + src/processor/static_map_unittest.cc +src_processor_static_map_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) +src_processor_static_map_unittest_LDADD = \ + src/processor/logging.o \ + src/processor/pathname_stripper.o \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +src_processor_static_range_map_unittest_SOURCES = \ + src/processor/static_range_map_unittest.cc +src_processor_static_range_map_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) +src_processor_static_range_map_unittest_LDADD = \ + src/processor/logging.o \ + src/processor/pathname_stripper.o \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +src_processor_pathname_stripper_unittest_SOURCES = \ + src/processor/pathname_stripper_unittest.cc +src_processor_pathname_stripper_unittest_LDADD = \ + src/processor/pathname_stripper.o \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +src_processor_postfix_evaluator_unittest_SOURCES = \ + src/processor/postfix_evaluator_unittest.cc +src_processor_postfix_evaluator_unittest_LDADD = \ + src/processor/logging.o \ + src/processor/pathname_stripper.o \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +src_processor_range_map_truncate_lower_unittest_SOURCES = \ + src/processor/range_map_truncate_lower_unittest.cc +src_processor_range_map_truncate_lower_unittest_LDADD = \ + src/processor/logging.o \ + src/processor/pathname_stripper.o \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) +src_processor_range_map_truncate_lower_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) + +src_processor_range_map_truncate_upper_unittest_SOURCES = \ + src/processor/range_map_truncate_upper_unittest.cc +src_processor_range_map_truncate_upper_unittest_LDADD = \ + src/processor/logging.o \ + src/processor/pathname_stripper.o \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) +src_processor_range_map_truncate_upper_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) + +src_processor_range_map_unittest_SOURCES = \ + src/processor/range_map_unittest.cc +src_processor_range_map_unittest_LDADD = \ + src/processor/logging.o \ + src/processor/pathname_stripper.o \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +src_processor_stackwalker_selftest_SOURCES = \ + src/processor/stackwalker_selftest.cc +src_processor_stackwalker_selftest_LDADD = \ + src/processor/basic_code_modules.o \ + src/processor/basic_source_line_resolver.o \ + src/processor/call_stack.o \ + src/processor/disassembler_x86.o \ + src/processor/exploitability.o \ + src/processor/exploitability_linux.o \ + src/processor/exploitability_win.o \ + src/processor/logging.o \ + src/processor/minidump.o \ + src/processor/pathname_stripper.o \ + src/processor/proc_maps_linux.o \ + src/processor/source_line_resolver_base.o \ + src/processor/stack_frame_cpu.o \ + src/processor/stack_frame_symbolizer.o \ + src/processor/stackwalker.o \ + src/processor/stackwalker_address_list.o \ + src/processor/stackwalker_amd64.o \ + src/processor/stackwalker_arm.o \ + src/processor/stackwalker_arm64.o \ + src/processor/stackwalker_mips.o \ + src/processor/stackwalker_ppc.o \ + src/processor/stackwalker_ppc64.o \ + src/processor/stackwalker_sparc.o \ + src/processor/stackwalker_x86.o \ + src/processor/tokenize.o \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +src_processor_stackwalker_amd64_unittest_SOURCES = \ + src/common/test_assembler.cc \ + src/processor/stackwalker_amd64_unittest.cc +src_processor_stackwalker_amd64_unittest_LDADD = \ + src/libbreakpad.a \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) +src_processor_stackwalker_amd64_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) + +src_processor_stackwalker_arm_unittest_SOURCES = \ + src/common/test_assembler.cc \ + src/processor/stackwalker_arm_unittest.cc +src_processor_stackwalker_arm_unittest_LDADD = \ + src/libbreakpad.a \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) +src_processor_stackwalker_arm_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) + +src_processor_stackwalker_arm64_unittest_SOURCES = \ + src/common/test_assembler.cc \ + src/processor/stackwalker_arm64_unittest.cc +src_processor_stackwalker_arm64_unittest_LDADD = \ + src/libbreakpad.a \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) +src_processor_stackwalker_arm64_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) + +src_processor_stackwalker_address_list_unittest_SOURCES = \ + src/common/test_assembler.cc \ + src/processor/stackwalker_address_list_unittest.cc +src_processor_stackwalker_address_list_unittest_LDADD = \ + src/libbreakpad.a \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) +src_processor_stackwalker_address_list_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) + +src_processor_stackwalker_mips_unittest_SOURCES = \ + src/common/test_assembler.cc \ + src/processor/stackwalker_mips_unittest.cc +src_processor_stackwalker_mips_unittest_LDADD = \ + src/libbreakpad.a \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) +src_processor_stackwalker_mips_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) + +src_processor_stackwalker_mips64_unittest_SOURCES = \ + src/common/test_assembler.cc \ + src/processor/stackwalker_mips64_unittest.cc +src_processor_stackwalker_mips64_unittest_LDADD = \ + src/libbreakpad.a \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) +src_processor_stackwalker_mips64_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) + +src_processor_stackwalker_x86_unittest_SOURCES = \ + src/common/test_assembler.cc \ + src/processor/stackwalker_x86_unittest.cc +src_processor_stackwalker_x86_unittest_LDADD = \ + src/libbreakpad.a \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) +src_processor_stackwalker_x86_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) + +src_processor_synth_minidump_unittest_SOURCES = \ + src/common/test_assembler.cc \ + src/common/test_assembler.h \ + src/processor/synth_minidump_unittest.cc \ + src/processor/synth_minidump.cc \ + src/processor/synth_minidump.h +src_processor_synth_minidump_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) +src_processor_synth_minidump_unittest_LDADD = \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +src_common_test_assembler_unittest_SOURCES = \ + src/common/test_assembler.cc \ + src/common/test_assembler.h \ + src/common/test_assembler_unittest.cc +src_common_test_assembler_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) +src_common_test_assembler_unittest_LDADD = \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +src_common_dwarf_dwarf2reader_lineinfo_unittest_SOURCES = \ + src/common/dwarf/dwarf2reader.h \ + src/common/dwarf/dwarf2reader_lineinfo_unittest.cc +src_common_dwarf_dwarf2reader_lineinfo_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) +src_common_dwarf_dwarf2reader_lineinfo_unittest_LDADD = \ + src/common/dwarf/bytereader.o \ + src/common/dwarf/dwarf2reader.o \ + src/common/dwarf/elf_reader.o \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +src_common_dwarf_dwarf2reader_splitfunctions_unittest_SOURCES = \ + src/common/dwarf/dwarf2reader.h \ + src/common/dwarf/dwarf2reader_splitfunctions_unittest.cc +src_common_dwarf_dwarf2reader_splitfunctions_unittest_CPPFLAGS = \ + $(AM_CPPFLAGS) $(TEST_CFLAGS) +src_common_dwarf_dwarf2reader_splitfunctions_unittest_LDADD = \ + src/common/dwarf/bytereader.o \ + src/common/dwarf/dwarf2reader.o \ + src/common/dwarf/elf_reader.o \ + $(TEST_LIBS) \ + $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +## Non-installables +noinst_PROGRAMS = +noinst_SCRIPTS = $(check_SCRIPTS) + +src_processor_minidump_dump_SOURCES = \ + src/processor/minidump_dump.cc +src_processor_minidump_dump_LDADD = \ + src/common/path_helper.o \ + src/processor/basic_code_modules.o \ + src/processor/convert_old_arm64_context.o \ + src/processor/dump_context.o \ + src/processor/dump_object.o \ + src/processor/logging.o \ + src/processor/minidump.o \ + src/processor/pathname_stripper.o \ + src/processor/proc_maps_linux.o + +src_processor_microdump_stackwalk_SOURCES = \ + src/processor/microdump_stackwalk.cc +src_processor_microdump_stackwalk_LDADD = \ + src/common/path_helper.o \ + src/processor/basic_code_modules.o \ + src/processor/basic_source_line_resolver.o \ + src/processor/call_stack.o \ + src/processor/convert_old_arm64_context.o \ + src/processor/cfi_frame_info.o \ + src/processor/disassembler_x86.o \ + src/processor/dump_context.o \ + src/processor/dump_object.o \ + src/processor/logging.o \ + src/processor/microdump.o \ + src/processor/microdump_processor.o \ + src/processor/pathname_stripper.o \ + src/processor/process_state.o \ + src/processor/simple_symbol_supplier.o \ + src/processor/source_line_resolver_base.o \ + src/processor/stack_frame_cpu.o \ + src/processor/stack_frame_symbolizer.o \ + src/processor/stackwalk_common.o \ + src/processor/stackwalker.o \ + src/processor/stackwalker_address_list.o \ + src/processor/stackwalker_amd64.o \ + src/processor/stackwalker_arm.o \ + src/processor/stackwalker_arm64.o \ + src/processor/stackwalker_mips.o \ + src/processor/stackwalker_ppc.o \ + src/processor/stackwalker_ppc64.o \ + src/processor/stackwalker_sparc.o \ + src/processor/stackwalker_x86.o \ + src/processor/tokenize.o \ + src/third_party/libdisasm/libdisasm.a + +src_processor_minidump_stackwalk_SOURCES = \ + src/processor/minidump_stackwalk.cc +src_processor_minidump_stackwalk_LDADD = \ + src/common/path_helper.o \ + src/processor/basic_code_modules.o \ + src/processor/basic_source_line_resolver.o \ + src/processor/call_stack.o \ + src/processor/cfi_frame_info.o \ + src/processor/convert_old_arm64_context.o \ + src/processor/disassembler_x86.o \ + src/processor/dump_context.o \ + src/processor/dump_object.o \ + src/processor/exploitability.o \ + src/processor/exploitability_linux.o \ + src/processor/exploitability_win.o \ + src/processor/logging.o \ + src/processor/minidump.o \ + src/processor/minidump_processor.o \ + src/processor/pathname_stripper.o \ + src/processor/process_state.o \ + src/processor/proc_maps_linux.o \ + src/processor/simple_symbol_supplier.o \ + src/processor/source_line_resolver_base.o \ + src/processor/stack_frame_cpu.o \ + src/processor/stack_frame_symbolizer.o \ + src/processor/stackwalk_common.o \ + src/processor/stackwalker.o \ + src/processor/stackwalker_address_list.o \ + src/processor/stackwalker_amd64.o \ + src/processor/stackwalker_arm.o \ + src/processor/stackwalker_arm64.o \ + src/processor/stackwalker_mips.o \ + src/processor/stackwalker_ppc.o \ + src/processor/stackwalker_ppc64.o \ + src/processor/stackwalker_sparc.o \ + src/processor/stackwalker_x86.o \ + src/processor/symbolic_constants_win.o \ + src/processor/tokenize.o \ + src/third_party/libdisasm/libdisasm.a + +endif !DISABLE_PROCESSOR + +## Additional files to be included in a source distribution +## +## find src/client src/common src/processor/testdata src/tools \ +## -type f \! -path '*/.svn/*' -print | sort | \ +## sed -e s/'^\(.*\)$'/'\t\1 \\'/ +EXTRA_DIST = \ + $(SCRIPTS) \ + src/client/linux/data/linux-gate-amd.sym \ + src/client/linux/data/linux-gate-intel.sym \ + src/client/mac/handler/breakpad_nlist_64.cc \ + src/client/mac/handler/breakpad_nlist_64.h \ + src/client/mac/handler/dynamic_images.cc \ + src/client/mac/handler/dynamic_images.h \ + src/client/mac/handler/exception_handler.cc \ + src/client/mac/handler/exception_handler.h \ + src/client/mac/handler/mach_vm_compat.h \ + src/client/mac/handler/minidump_generator.cc \ + src/client/mac/handler/minidump_generator.h \ + src/client/mac/handler/minidump_test.xcodeproj/project.pbxproj \ + src/client/mac/handler/minidump_tests32-Info.plist \ + src/client/mac/handler/minidump_tests64-Info.plist \ + src/client/mac/handler/obj-cTestCases-Info.plist \ + src/client/mac/handler/protected_memory_allocator.cc \ + src/client/mac/handler/protected_memory_allocator.h \ + src/client/mac/handler/ucontext_compat.h \ + src/client/mac/handler/testcases/testdata/dump_syms_i386_breakpad.sym \ + src/client/mac/tests/BreakpadFramework_Test.mm \ + src/client/mac/tests/crash_generation_server_test.cc \ + src/client/mac/tests/exception_handler_test.cc \ + src/client/mac/tests/minidump_generator_test.cc \ + src/client/mac/tests/minidump_generator_test_helper.cc \ + src/client/mac/tests/spawn_child_process.h \ + src/client/mac/tests/testlogging.h \ + src/client/minidump_file_writer_unittest.cc \ + src/client/solaris/handler/Makefile \ + src/client/solaris/handler/exception_handler.cc \ + src/client/solaris/handler/exception_handler.h \ + src/client/solaris/handler/exception_handler_test.cc \ + src/client/solaris/handler/minidump_generator.cc \ + src/client/solaris/handler/minidump_generator.h \ + src/client/solaris/handler/minidump_test.cc \ + src/client/solaris/handler/solaris_lwp.cc \ + src/client/solaris/handler/solaris_lwp.h \ + src/client/windows/breakpad_client.gyp \ + src/client/windows/handler/exception_handler.cc \ + src/client/windows/handler/exception_handler.h \ + src/client/windows/handler/exception_handler.gyp \ + src/client/windows/sender/crash_report_sender.cc \ + src/client/windows/sender/crash_report_sender.h \ + src/client/windows/sender/crash_report_sender.gyp \ + src/common/dwarf/dwarf2diehandler.h \ + src/common/dwarf/dwarf2enums.h \ + src/common/dwarf/line_state_machine.h \ + src/common/dwarf/types.h \ + src/common/mac/arch_utilities.h \ + src/common/mac/byteswap.h \ + src/common/mac/HTTPMultipartUpload.h \ + src/common/mac/HTTPMultipartUpload.m \ + src/common/mac/string_utilities.cc \ + src/common/mac/string_utilities.h \ + src/common/mac/super_fat_arch.h \ + src/common/scoped_ptr.h \ + src/common/solaris/dump_symbols.cc \ + src/common/solaris/dump_symbols.h \ + src/common/solaris/file_id.cc \ + src/common/solaris/file_id.h \ + src/common/solaris/guid_creator.cc \ + src/common/solaris/guid_creator.h \ + src/common/solaris/message_output.h \ + src/common/windows/guid_string.cc \ + src/common/windows/guid_string.h \ + src/common/windows/http_upload.cc \ + src/common/windows/http_upload.h \ + src/common/windows/pdb_source_line_writer.cc \ + src/common/windows/pdb_source_line_writer.h \ + src/common/windows/string_utils-inl.h \ + src/common/windows/string_utils.cc \ + src/processor/microdump_stackwalk_test_vars \ + src/processor/stackwalk_common.cc \ + src/processor/stackwalk_common.h \ + src/processor/stackwalker_selftest_sol.s \ + src/processor/testdata/ascii_read_av_block_write.dmp \ + src/processor/testdata/ascii_read_av_clobber_write.dmp \ + src/processor/testdata/ascii_read_av_conditional.dmp \ + src/processor/testdata/ascii_read_av.dmp \ + src/processor/testdata/ascii_read_av_then_jmp.dmp \ + src/processor/testdata/ascii_read_av_xchg_write.dmp \ + src/processor/testdata/ascii_write_av_arg_to_call.dmp \ + src/processor/testdata/ascii_write_av.dmp \ + src/processor/testdata/exec_av_on_stack.dmp \ + src/processor/testdata/linux_divide_by_zero.dmp \ + src/processor/testdata/linux_executable_heap.dmp \ + src/processor/testdata/linux_executable_stack.dmp \ + src/processor/testdata/linux_inside_module_exe_region1.dmp \ + src/processor/testdata/linux_inside_module_exe_region2.dmp \ + src/processor/testdata/linux_jmp_to_0.dmp \ + src/processor/testdata/linux_jmp_to_module_not_exe_region.dmp \ + src/processor/testdata/linux_null_dereference.dmp \ + src/processor/testdata/linux_null_read_av.dmp \ + src/processor/testdata/linux_outside_module.dmp \ + src/processor/testdata/linux_overflow.dmp \ + src/processor/testdata/linux_raise_sigabrt.dmp \ + src/processor/testdata/linux_stack_pointer_in_module.dmp \ + src/processor/testdata/linux_stack_pointer_in_stack.dmp \ + src/processor/testdata/linux_stack_pointer_in_stack_alt_name.dmp \ + src/processor/testdata/linux_stacksmash.dmp \ + src/processor/testdata/linux_write_to_nonwritable_module.dmp \ + src/processor/testdata/linux_write_to_nonwritable_region_math.dmp \ + src/processor/testdata/linux_write_to_outside_module.dmp \ + src/processor/testdata/linux_write_to_outside_module_via_math.dmp \ + src/processor/testdata/linux_write_to_under_4k.dmp \ + src/processor/testdata/microdump-arm64.dmp \ + src/processor/testdata/microdump-arm.dmp \ + src/processor/testdata/microdump-mips32.dmp \ + src/processor/testdata/microdump-mips64.dmp \ + src/processor/testdata/microdump-multiple.dmp \ + src/processor/testdata/microdump.stackwalk-arm64.out \ + src/processor/testdata/microdump.stackwalk-arm.out \ + src/processor/testdata/microdump.stackwalk.machine_readable-arm64.out \ + src/processor/testdata/microdump.stackwalk.machine_readable-arm.out \ + src/processor/testdata/microdump-withcrashreason.dmp \ + src/processor/testdata/microdump-x86.dmp \ + src/processor/testdata/minidump_32bit_crash_addr.dmp \ + src/processor/testdata/minidump2.dmp \ + src/processor/testdata/minidump2.dump.out \ + src/processor/testdata/minidump2.stackwalk.machine_readable.out \ + src/processor/testdata/minidump2.stackwalk.out \ + src/processor/testdata/module0.out \ + src/processor/testdata/module1.out \ + src/processor/testdata/module2.out \ + src/processor/testdata/module3_bad.out \ + src/processor/testdata/module4_bad.out \ + src/processor/testdata/null_read_av.dmp \ + src/processor/testdata/null_write_av.dmp \ + src/processor/testdata/read_av_clobber_write.dmp \ + src/processor/testdata/read_av_conditional.dmp \ + src/processor/testdata/read_av_non_null.dmp \ + src/processor/testdata/stack_exhaustion.dmp \ + src/processor/testdata/write_av_non_null.dmp \ + src/processor/testdata/symbols/kernel32.pdb/BCE8785C57B44245A669896B6A19B9542/kernel32.sym \ + src/processor/testdata/symbols/ld-2.13.so/C32AD7E235EA6112E02A5B9D6219C4850/ld-2.13.so.sym \ + src/processor/testdata/symbols/libc-2.13.so/F4F8DFCD5A5FB5A7CE64717E9E6AE3890/libc-2.13.so.sym \ + src/processor/testdata/symbols/libgcc_s.so.1/18B180F90887D8F8B5C35D185444AF4C0/libgcc_s.so.1.sym \ + src/processor/testdata/symbols/microdump/breakpad_unittests/D6D1FEC9A15DE7F38A236898871A2E770/breakpad_unittests.sym \ + src/processor/testdata/symbols/microdump/breakpad_unittests/DA7778FB66018A4E9B4110ED06E730D00/breakpad_unittests.sym \ + src/processor/testdata/symbols/microdump/crash_example/6E72E2F1A5F59AB3D51356FDFE394D490/crash_example.sym \ + src/processor/testdata/symbols/microdump/crash_example/8F36148CC4647A8116CAF2A25F591F570/crash_example.sym \ + src/processor/testdata/symbols/null_read_av/7B7D1968FF0D47AE4366E9C3A7E1B6750/null_read_av.sym \ + src/processor/testdata/symbols/overflow/B0E1FC01EF48E39CAF5C881D2DF0C3840/overflow.sym \ + src/processor/testdata/symbols/test_app.pdb/5A9832E5287241C1838ED98914E9B7FF1/test_app.sym \ + src/processor/testdata/test_app.cc \ + src/testing/googletest/include/gtest/gtest.h \ + src/testing/googletest/include/gtest/gtest-death-test.h \ + src/testing/googletest/include/gtest/gtest-matchers.h \ + src/testing/googletest/include/gtest/gtest-message.h \ + src/testing/googletest/include/gtest/gtest-param-test.h \ + src/testing/googletest/include/gtest/gtest-printers.h \ + src/testing/googletest/include/gtest/gtest-spi.h \ + src/testing/googletest/include/gtest/gtest-test-part.h \ + src/testing/googletest/include/gtest/gtest-typed-test.h \ + src/testing/googletest/include/gtest/gtest_pred_impl.h \ + src/testing/googletest/include/gtest/gtest_prod.h \ + src/testing/googletest/include/gtest/internal/custom/gtest-port.h \ + src/testing/googletest/include/gtest/internal/custom/gtest-printers.h \ + src/testing/googletest/include/gtest/internal/custom/gtest.h \ + src/testing/googletest/include/gtest/internal/gtest-death-test-internal.h \ + src/testing/googletest/include/gtest/internal/gtest-filepath.h \ + src/testing/googletest/include/gtest/internal/gtest-internal.h \ + src/testing/googletest/include/gtest/internal/gtest-param-util-generated.h \ + src/testing/googletest/include/gtest/internal/gtest-param-util.h \ + src/testing/googletest/include/gtest/internal/gtest-port-arch.h \ + src/testing/googletest/include/gtest/internal/gtest-port.h \ + src/testing/googletest/include/gtest/internal/gtest-string.h \ + src/testing/googletest/include/gtest/internal/gtest-type-util.h \ + src/testing/googletest/src/gtest.cc \ + src/testing/googletest/src/gtest-death-test.cc \ + src/testing/googletest/src/gtest-filepath.cc \ + src/testing/googletest/src/gtest-internal-inl.h \ + src/testing/googletest/src/gtest-matchers.cc \ + src/testing/googletest/src/gtest-port.cc \ + src/testing/googletest/src/gtest-printers.cc \ + src/testing/googletest/src/gtest-test-part.cc \ + src/testing/googletest/src/gtest-typed-test.cc \ + src/testing/googlemock/include/gmock/gmock.h \ + src/testing/googlemock/include/gmock/gmock-actions.h \ + src/testing/googlemock/include/gmock/gmock-cardinalities.h \ + src/testing/googlemock/include/gmock/gmock-function-mocker.h \ + src/testing/googlemock/include/gmock/gmock-generated-actions.h \ + src/testing/googlemock/include/gmock/gmock-generated-function-mockers.h \ + src/testing/googlemock/include/gmock/gmock-generated-matchers.h \ + src/testing/googlemock/include/gmock/gmock-matchers.h \ + src/testing/googlemock/include/gmock/gmock-more-actions.h \ + src/testing/googlemock/include/gmock/gmock-more-matchers.h \ + src/testing/googlemock/include/gmock/gmock-nice-strict.h \ + src/testing/googlemock/include/gmock/gmock-spec-builders.h \ + src/testing/googlemock/include/gmock/internal/custom/gmock-generated-actions.h \ + src/testing/googlemock/include/gmock/internal/custom/gmock-matchers.h \ + src/testing/googlemock/include/gmock/internal/custom/gmock-port.h \ + src/testing/googlemock/include/gmock/internal/gmock-internal-utils.h \ + src/testing/googlemock/include/gmock/internal/gmock-port.h \ + src/testing/googlemock/include/gmock/internal/gmock-pp.h \ + src/testing/googlemock/src/gmock.cc \ + src/testing/googlemock/src/gmock-cardinalities.cc \ + src/testing/googlemock/src/gmock-internal-utils.cc \ + src/testing/googlemock/src/gmock-matchers.cc \ + src/testing/googlemock/src/gmock-spec-builders.cc \ + src/testing/googlemock/src/gmock_main.cc \ + src/third_party/curl/COPYING \ + src/third_party/curl/curlbuild.h \ + src/third_party/curl/curl.h \ + src/third_party/curl/curlrules.h \ + src/third_party/curl/curlver.h \ + src/third_party/curl/easy.h \ + src/third_party/curl/mprintf.h \ + src/third_party/curl/multi.h \ + src/third_party/curl/stdcheaders.h \ + src/third_party/curl/typecheck-gcc.h \ + src/third_party/curl/types.h \ + src/third_party/mac_headers/architecture/byte_order.h \ + src/third_party/mac_headers/i386/_types.h \ + src/third_party/mac_headers/mach/boolean.h \ + src/third_party/mac_headers/mach/i386/boolean.h \ + src/third_party/mac_headers/mach/i386/vm_param.h \ + src/third_party/mac_headers/mach/i386/vm_types.h \ + src/third_party/mac_headers/mach/machine/boolean.h \ + src/third_party/mac_headers/mach/machine.h \ + src/third_party/mac_headers/mach/machine/thread_state.h \ + src/third_party/mac_headers/mach/machine/thread_status.h \ + src/third_party/mac_headers/mach/machine/vm_types.h \ + src/third_party/mac_headers/mach-o/arch.h \ + src/third_party/mac_headers/mach-o/fat.h \ + src/third_party/mac_headers/mach-o/loader.h \ + src/third_party/mac_headers/mach-o/nlist.h \ + src/third_party/mac_headers/mach/thread_status.h \ + src/third_party/mac_headers/mach/vm_prot.h \ + src/third_party/mac_headers/README \ + src/third_party/musl/README \ + src/third_party/musl/COPYRIGHT \ + src/third_party/musl/README.breakpad \ + src/third_party/musl/VERSION \ + src/third_party/musl/include/elf.h \ + src/tools/mac/crash_report/crash_report.mm \ + src/tools/mac/crash_report/crash_report.xcodeproj/project.pbxproj \ + src/tools/mac/crash_report/on_demand_symbol_supplier.h \ + src/tools/mac/crash_report/on_demand_symbol_supplier.mm \ + src/tools/mac/dump_syms/dump_syms.xcodeproj/project.pbxproj \ + src/tools/mac/dump_syms/dump_syms_tool.cc \ + src/tools/mac/symupload/minidump_upload.m \ + src/tools/mac/symupload/symupload.m \ + src/tools/mac/symupload/symupload.xcodeproj/project.pbxproj \ + src/tools/solaris/dump_syms/Makefile \ + src/tools/solaris/dump_syms/dump_syms.cc \ + src/tools/solaris/dump_syms/run_regtest.sh \ + src/tools/solaris/dump_syms/testdata/dump_syms_regtest.cc \ + src/tools/solaris/dump_syms/testdata/dump_syms_regtest.o \ + src/tools/solaris/dump_syms/testdata/dump_syms_regtest.stabs \ + src/tools/solaris/dump_syms/testdata/dump_syms_regtest.sym \ + src/tools/windows/converter/ms_symbol_server_converter.cc \ + src/tools/windows/converter/ms_symbol_server_converter.h \ + src/tools/windows/converter/ms_symbol_server_converter.gyp \ + src/tools/windows/dump_syms/dump_syms.cc \ + src/tools/windows/dump_syms/dump_syms.gyp \ + src/tools/windows/dump_syms/run_regtest.sh \ + src/tools/windows/dump_syms/testdata/dump_syms_regtest.cc \ + src/tools/windows/dump_syms/testdata/dump_syms_regtest.pdb \ + src/tools/windows/dump_syms/testdata/dump_syms_regtest.sym \ + src/tools/windows/dump_syms/testdata/dump_syms_regtest64.sym \ + src/tools/windows/dump_syms/testdata/omap_reorder_bbs.sym \ + src/tools/windows/dump_syms/testdata/omap_reorder_funcs.sym \ + src/tools/windows/dump_syms/testdata/omap_stretched.sym \ + src/tools/windows/dump_syms/testdata/omap_stretched_filled.sym \ + src/tools/windows/symupload/symupload.cc \ + src/tools/windows/symupload/symupload.gyp + +mostlyclean-local: + -find src -name '*.dwo' -exec rm -f {} + diff --git a/shared/sentry/external/breakpad/Makefile.in b/shared/sentry/external/breakpad/Makefile.in new file mode 100644 index 000000000..87377f3ac --- /dev/null +++ b/shared/sentry/external/breakpad/Makefile.in @@ -0,0 +1,10111 @@ +# Makefile.in generated by automake 1.16.3 from Makefile.am. +# @configure_input@ + +# Copyright (C) 1994-2020 Free Software Foundation, Inc. + +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ + +# 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. + + + + + +VPATH = @srcdir@ +am__is_gnu_make = { \ + if test -z '$(MAKELEVEL)'; then \ + false; \ + elif test -n '$(MAKE_HOST)'; then \ + true; \ + elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \ + true; \ + else \ + false; \ + fi; \ +} +am__make_running_with_option = \ + case $${target_option-} in \ + ?) ;; \ + *) echo "am__make_running_with_option: internal error: invalid" \ + "target option '$${target_option-}' specified" >&2; \ + exit 1;; \ + esac; \ + has_opt=no; \ + sane_makeflags=$$MAKEFLAGS; \ + if $(am__is_gnu_make); then \ + sane_makeflags=$$MFLAGS; \ + else \ + case $$MAKEFLAGS in \ + *\\[\ \ ]*) \ + bs=\\; \ + sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \ + | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \ + esac; \ + fi; \ + skip_next=no; \ + strip_trailopt () \ + { \ + flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \ + }; \ + for flg in $$sane_makeflags; do \ + test $$skip_next = yes && { skip_next=no; continue; }; \ + case $$flg in \ + *=*|--*) continue;; \ + -*I) strip_trailopt 'I'; skip_next=yes;; \ + -*I?*) strip_trailopt 'I';; \ + -*O) strip_trailopt 'O'; skip_next=yes;; \ + -*O?*) strip_trailopt 'O';; \ + -*l) strip_trailopt 'l'; skip_next=yes;; \ + -*l?*) strip_trailopt 'l';; \ + -[dEDm]) skip_next=yes;; \ + -[JT]) skip_next=yes;; \ + esac; \ + case $$flg in \ + *$$target_option*) has_opt=yes; break;; \ + esac; \ + done; \ + test $$has_opt = yes +am__make_dryrun = (target_option=n; $(am__make_running_with_option)) +am__make_keepgoing = (target_option=k; $(am__make_running_with_option)) +pkgdatadir = $(datadir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkglibexecdir = $(libexecdir)/@PACKAGE@ +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_HEADER = $(INSTALL_DATA) +transform = $(program_transform_name) +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +build_triplet = @build@ +host_triplet = @host@ + +# This allows using fixed NDK headers when building for Android. +# This is only necessary for building the unit tests until GTest is upgraded +# to a future version. +@ANDROID_HOST_TRUE@am__append_1 = \ +@ANDROID_HOST_TRUE@ -I$(top_srcdir)/src/common/android/include \ +@ANDROID_HOST_TRUE@ -I$(top_srcdir)/src/common/android/testing/include + +# Build as PIC on Linux, for linux_client_unittest_shlib +@LINUX_HOST_TRUE@am__append_2 = -fPIC +@LINUX_HOST_TRUE@am__append_3 = -fPIC +libexec_PROGRAMS = $(am__EXEEXT_10) +bin_PROGRAMS = $(am__EXEEXT_2) $(am__EXEEXT_3) $(am__EXEEXT_4) +check_PROGRAMS = $(am__EXEEXT_5) $(am__EXEEXT_6) $(am__EXEEXT_7) \ + $(am__EXEEXT_8) $(am__EXEEXT_9) +EXTRA_PROGRAMS = $(am__EXEEXT_1) +@DISABLE_PROCESSOR_FALSE@am__append_4 = src/libbreakpad.a +@DISABLE_PROCESSOR_FALSE@am__append_5 = breakpad.pc +@DISABLE_PROCESSOR_FALSE@am__append_6 = src/third_party/libdisasm/libdisasm.a +@LINUX_HOST_TRUE@am__append_7 = src/client/linux/libbreakpad_client.a +@LINUX_HOST_TRUE@am__append_8 = breakpad-client.pc +@HAVE_GETCONTEXT_FALSE@@LINUX_HOST_TRUE@am__append_9 = \ +@HAVE_GETCONTEXT_FALSE@@LINUX_HOST_TRUE@ src/common/linux/breakpad_getcontext.S + +@DISABLE_PROCESSOR_FALSE@am__append_10 = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/microdump_stackwalk \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump_dump \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump_stackwalk + +@LINUX_HOST_TRUE@am__append_11 = src/client/linux/linux_dumper_unittest_helper \ +@LINUX_HOST_TRUE@ src/client/linux/linux_client_unittest_shlib +@LINUX_HOST_TRUE@am__append_12 = src/client/linux/linux_dumper_unittest_helper \ +@LINUX_HOST_TRUE@ src/client/linux/linux_client_unittest_shlib +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@am__append_13 = \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/tools/linux/core2md/core2md \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/tools/linux/pid2md/pid2md \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/tools/linux/dump_syms/dump_syms \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/tools/linux/md2core/minidump-2-core \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/tools/linux/symupload/minidump_upload \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/tools/linux/symupload/sym_upload + +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@@X86_HOST_TRUE@am__append_14 = \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@@X86_HOST_TRUE@ src/tools/mac/dump_syms/dump_syms_mac + +@DISABLE_TOOLS_FALSE@@HAVE_MEMFD_CREATE_TRUE@@LINUX_HOST_TRUE@am__append_15 = \ +@DISABLE_TOOLS_FALSE@@HAVE_MEMFD_CREATE_TRUE@@LINUX_HOST_TRUE@ src/tools/linux/core_handler/core_handler + +@DISABLE_PROCESSOR_FALSE@am__append_16 = \ +@DISABLE_PROCESSOR_FALSE@ src/common/test_assembler_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/common/dwarf/dwarf2reader_lineinfo_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/common/dwarf/dwarf2reader_splitfunctions_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/address_map_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_source_line_resolver_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/cfi_frame_info_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/contained_range_map_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/fast_source_line_resolver_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/map_serializers_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/microdump_processor_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump_processor_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/static_address_map_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/static_contained_range_map_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/static_map_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/static_range_map_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/postfix_evaluator_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/proc_maps_linux_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/range_map_truncate_lower_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/range_map_truncate_upper_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/range_map_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_amd64_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_address_list_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips64_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86_unittest \ +@DISABLE_PROCESSOR_FALSE@ src/processor/synth_minidump_unittest + +@LINUX_HOST_TRUE@am__append_17 = \ +@LINUX_HOST_TRUE@ src/client/linux/linux_client_unittest \ +@LINUX_HOST_TRUE@ src/common/linux/google_crashdump_uploader_test + +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@am__append_18 = \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dumper_unittest \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/tools/linux/md2core/minidump_2_core_unittest + +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@@X86_HOST_TRUE@am__append_19 = \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@@X86_HOST_TRUE@ src/common/mac/macho_reader_unittest + +@DISABLE_PROCESSOR_FALSE@@SELFTEST_TRUE@am__append_20 = \ +@DISABLE_PROCESSOR_FALSE@@SELFTEST_TRUE@ src/processor/stackwalker_selftest + +@HAVE_GETCONTEXT_FALSE@@LINUX_HOST_TRUE@am__append_21 = src/common/linux/breakpad_getcontext.S \ +@HAVE_GETCONTEXT_FALSE@@LINUX_HOST_TRUE@ src/common/linux/breakpad_getcontext_unittest.cc +@ANDROID_HOST_TRUE@@LINUX_HOST_TRUE@am__append_22 = \ +@ANDROID_HOST_TRUE@@LINUX_HOST_TRUE@ -llog -lm + +@ANDROID_HOST_TRUE@@LINUX_HOST_TRUE@am__append_23 = \ +@ANDROID_HOST_TRUE@@LINUX_HOST_TRUE@ -llog + +noinst_PROGRAMS = +subdir = . +ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +am__aclocal_m4_deps = $(top_srcdir)/m4/ax_append_compile_flags.m4 \ + $(top_srcdir)/m4/ax_append_flag.m4 \ + $(top_srcdir)/m4/ax_check_compile_flag.m4 \ + $(top_srcdir)/m4/ax_check_define.m4 \ + $(top_srcdir)/m4/ax_cxx_compile_stdcxx.m4 \ + $(top_srcdir)/m4/ax_pthread.m4 \ + $(top_srcdir)/m4/ax_require_defined.m4 \ + $(top_srcdir)/configure.ac +am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ + $(ACLOCAL_M4) +DIST_COMMON = $(srcdir)/Makefile.am $(top_srcdir)/configure \ + $(am__configure_deps) $(dist_doc_DATA) $(includec_HEADERS) \ + $(am__includecl_HEADERS_DIST) $(am__includeclc_HEADERS_DIST) \ + $(am__includecldwc_HEADERS_DIST) \ + $(am__includeclh_HEADERS_DIST) $(am__includeclm_HEADERS_DIST) \ + $(includegbc_HEADERS) $(am__includelss_HEADERS_DIST) \ + $(includep_HEADERS) $(am__DIST_COMMON) +am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \ + configure.lineno config.status.lineno +mkinstalldirs = $(install_sh) -d +CONFIG_HEADER = $(top_builddir)/src/config.h +CONFIG_CLEAN_FILES = breakpad.pc breakpad-client.pc +CONFIG_CLEAN_VPATH_FILES = +@LINUX_HOST_TRUE@am__EXEEXT_1 = src/client/linux/linux_dumper_unittest_helper$(EXEEXT) \ +@LINUX_HOST_TRUE@ src/client/linux/linux_client_unittest_shlib$(EXEEXT) +@DISABLE_PROCESSOR_FALSE@am__EXEEXT_2 = src/processor/microdump_stackwalk$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump_dump$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump_stackwalk$(EXEEXT) +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@am__EXEEXT_3 = src/tools/linux/core2md/core2md$(EXEEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/tools/linux/pid2md/pid2md$(EXEEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/tools/linux/dump_syms/dump_syms$(EXEEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/tools/linux/md2core/minidump-2-core$(EXEEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/tools/linux/symupload/minidump_upload$(EXEEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/tools/linux/symupload/sym_upload$(EXEEXT) +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@@X86_HOST_TRUE@am__EXEEXT_4 = src/tools/mac/dump_syms/dump_syms_mac$(EXEEXT) +am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libexecdir)" \ + "$(DESTDIR)$(libdir)" "$(DESTDIR)$(docdir)" \ + "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(includecdir)" \ + "$(DESTDIR)$(includecldir)" "$(DESTDIR)$(includeclcdir)" \ + "$(DESTDIR)$(includecldwcdir)" "$(DESTDIR)$(includeclhdir)" \ + "$(DESTDIR)$(includeclmdir)" "$(DESTDIR)$(includegbcdir)" \ + "$(DESTDIR)$(includelssdir)" "$(DESTDIR)$(includepdir)" +@DISABLE_PROCESSOR_FALSE@am__EXEEXT_5 = src/common/test_assembler_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/common/dwarf/dwarf2reader_lineinfo_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/common/dwarf/dwarf2reader_splitfunctions_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/address_map_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_source_line_resolver_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/cfi_frame_info_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/contained_range_map_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/fast_source_line_resolver_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/map_serializers_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/microdump_processor_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump_processor_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/static_address_map_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/static_contained_range_map_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/static_map_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/static_range_map_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/postfix_evaluator_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/proc_maps_linux_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/range_map_truncate_lower_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/range_map_truncate_upper_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/range_map_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_amd64_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_address_list_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips64_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86_unittest$(EXEEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/synth_minidump_unittest$(EXEEXT) +@LINUX_HOST_TRUE@am__EXEEXT_6 = src/client/linux/linux_client_unittest$(EXEEXT) \ +@LINUX_HOST_TRUE@ src/common/linux/google_crashdump_uploader_test$(EXEEXT) +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@am__EXEEXT_7 = src/common/dumper_unittest$(EXEEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/tools/linux/md2core/minidump_2_core_unittest$(EXEEXT) +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@@X86_HOST_TRUE@am__EXEEXT_8 = src/common/mac/macho_reader_unittest$(EXEEXT) +@DISABLE_PROCESSOR_FALSE@@SELFTEST_TRUE@am__EXEEXT_9 = src/processor/stackwalker_selftest$(EXEEXT) +@DISABLE_TOOLS_FALSE@@HAVE_MEMFD_CREATE_TRUE@@LINUX_HOST_TRUE@am__EXEEXT_10 = src/tools/linux/core_handler/core_handler$(EXEEXT) +PROGRAMS = $(bin_PROGRAMS) $(libexec_PROGRAMS) $(noinst_PROGRAMS) +am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +am__vpath_adj = case $$p in \ + $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ + *) f=$$p;; \ + esac; +am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +am__install_max = 40 +am__nobase_strip_setup = \ + srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +am__nobase_strip = \ + for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +am__nobase_list = $(am__nobase_strip_setup); \ + for p in $$list; do echo "$$p $$p"; done | \ + sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ + $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ + if (++n[$$2] == $(am__install_max)) \ + { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ + END { for (dir in files) print dir, files[dir] }' +am__base_list = \ + sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ + sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +am__uninstall_files_from_dir = { \ + test -z "$$files" \ + || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \ + || { echo " ( cd '$$dir' && rm -f" $$files ")"; \ + $(am__cd) "$$dir" && rm -f $$files; }; \ + } +LIBRARIES = $(lib_LIBRARIES) $(noinst_LIBRARIES) +ARFLAGS = cru +AM_V_AR = $(am__v_AR_@AM_V@) +am__v_AR_ = $(am__v_AR_@AM_DEFAULT_V@) +am__v_AR_0 = @echo " AR " $@; +am__v_AR_1 = +src_client_linux_libbreakpad_client_a_AR = $(AR) $(ARFLAGS) +src_client_linux_libbreakpad_client_a_LIBADD = +am__src_client_linux_libbreakpad_client_a_SOURCES_DIST = \ + src/client/linux/crash_generation/crash_generation_client.cc \ + src/client/linux/crash_generation/crash_generation_server.cc \ + src/client/linux/dump_writer_common/thread_info.cc \ + src/client/linux/dump_writer_common/ucontext_reader.cc \ + src/client/linux/handler/exception_handler.cc \ + src/client/linux/handler/exception_handler.h \ + src/client/linux/handler/minidump_descriptor.cc \ + src/client/linux/handler/minidump_descriptor.h \ + src/client/linux/log/log.cc src/client/linux/log/log.h \ + src/client/linux/microdump_writer/microdump_writer.cc \ + src/client/linux/microdump_writer/microdump_writer.h \ + src/client/linux/minidump_writer/linux_core_dumper.cc \ + src/client/linux/minidump_writer/linux_dumper.cc \ + src/client/linux/minidump_writer/linux_ptrace_dumper.cc \ + src/client/linux/minidump_writer/minidump_writer.cc \ + src/client/minidump_file_writer-inl.h \ + src/client/minidump_file_writer.cc \ + src/client/minidump_file_writer.h src/common/convert_UTF.cc \ + src/common/convert_UTF.h src/common/md5.cc src/common/md5.h \ + src/common/string_conversion.cc src/common/string_conversion.h \ + src/common/linux/elf_core_dump.cc src/common/linux/elfutils.cc \ + src/common/linux/elfutils.h src/common/linux/file_id.cc \ + src/common/linux/file_id.h src/common/linux/guid_creator.cc \ + src/common/linux/guid_creator.h \ + src/common/linux/linux_libc_support.cc \ + src/common/linux/memory_mapped_file.cc \ + src/common/linux/safe_readlink.cc \ + src/common/linux/breakpad_getcontext.S +am__dirstamp = $(am__leading_dot)dirstamp +@HAVE_GETCONTEXT_FALSE@@LINUX_HOST_TRUE@am__objects_1 = src/common/linux/breakpad_getcontext.$(OBJEXT) +@LINUX_HOST_TRUE@am_src_client_linux_libbreakpad_client_a_OBJECTS = src/client/linux/crash_generation/crash_generation_client.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/client/linux/crash_generation/crash_generation_server.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/client/linux/dump_writer_common/thread_info.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/client/linux/dump_writer_common/ucontext_reader.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/client/linux/handler/exception_handler.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/client/linux/handler/minidump_descriptor.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/client/linux/log/log.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/client/linux/microdump_writer/microdump_writer.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/linux_core_dumper.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/linux_dumper.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/linux_ptrace_dumper.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/minidump_writer.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/client/minidump_file_writer.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/common/convert_UTF.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/common/md5.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/common/string_conversion.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/common/linux/elf_core_dump.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/common/linux/elfutils.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/common/linux/file_id.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/common/linux/guid_creator.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/common/linux/linux_libc_support.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/common/linux/memory_mapped_file.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/common/linux/safe_readlink.$(OBJEXT) \ +@LINUX_HOST_TRUE@ $(am__objects_1) +src_client_linux_libbreakpad_client_a_OBJECTS = \ + $(am_src_client_linux_libbreakpad_client_a_OBJECTS) +src_libbreakpad_a_AR = $(AR) $(ARFLAGS) +src_libbreakpad_a_LIBADD = +am__src_libbreakpad_a_SOURCES_DIST = \ + src/google_breakpad/common/breakpad_types.h \ + src/google_breakpad/common/minidump_format.h \ + src/google_breakpad/common/minidump_size.h \ + src/google_breakpad/processor/basic_source_line_resolver.h \ + src/google_breakpad/processor/call_stack.h \ + src/google_breakpad/processor/code_module.h \ + src/google_breakpad/processor/code_modules.h \ + src/google_breakpad/processor/dump_context.h \ + src/google_breakpad/processor/dump_object.h \ + src/google_breakpad/processor/exploitability.h \ + src/google_breakpad/processor/fast_source_line_resolver.h \ + src/google_breakpad/processor/memory_region.h \ + src/google_breakpad/processor/microdump.h \ + src/google_breakpad/processor/microdump_processor.h \ + src/google_breakpad/processor/minidump.h \ + src/google_breakpad/processor/minidump_processor.h \ + src/google_breakpad/processor/process_result.h \ + src/google_breakpad/processor/process_state.h \ + src/google_breakpad/processor/proc_maps_linux.h \ + src/google_breakpad/processor/source_line_resolver_base.h \ + src/google_breakpad/processor/source_line_resolver_interface.h \ + src/google_breakpad/processor/stack_frame.h \ + src/google_breakpad/processor/stack_frame_cpu.h \ + src/google_breakpad/processor/stack_frame_symbolizer.h \ + src/google_breakpad/processor/stackwalker.h \ + src/google_breakpad/processor/symbol_supplier.h \ + src/google_breakpad/processor/system_info.h \ + src/processor/address_map-inl.h src/processor/address_map.h \ + src/processor/basic_code_module.h \ + src/processor/basic_code_modules.cc \ + src/processor/basic_code_modules.h \ + src/processor/basic_source_line_resolver_types.h \ + src/processor/basic_source_line_resolver.cc \ + src/processor/call_stack.cc src/processor/cfi_frame_info.cc \ + src/processor/cfi_frame_info.h \ + src/processor/contained_range_map-inl.h \ + src/processor/contained_range_map.h \ + src/processor/convert_old_arm64_context.cc \ + src/processor/convert_old_arm64_context.h \ + src/processor/disassembler_x86.h \ + src/processor/disassembler_x86.cc \ + src/processor/dump_context.cc src/processor/dump_object.cc \ + src/processor/exploitability.cc \ + src/processor/exploitability_linux.h \ + src/processor/exploitability_linux.cc \ + src/processor/exploitability_win.h \ + src/processor/exploitability_win.cc \ + src/processor/fast_source_line_resolver_types.h \ + src/processor/fast_source_line_resolver.cc \ + src/processor/linked_ptr.h src/processor/logging.h \ + src/processor/logging.cc src/processor/map_serializers-inl.h \ + src/processor/map_serializers.h src/processor/microdump.cc \ + src/processor/microdump_processor.cc src/processor/minidump.cc \ + src/processor/minidump_processor.cc \ + src/processor/module_comparer.cc \ + src/processor/module_comparer.h src/processor/module_factory.h \ + src/processor/module_serializer.cc \ + src/processor/module_serializer.h \ + src/processor/pathname_stripper.cc \ + src/processor/pathname_stripper.h \ + src/processor/postfix_evaluator-inl.h \ + src/processor/postfix_evaluator.h \ + src/processor/process_state.cc \ + src/processor/proc_maps_linux.cc src/processor/range_map-inl.h \ + src/processor/range_map.h \ + src/processor/simple_serializer-inl.h \ + src/processor/simple_serializer.h \ + src/processor/simple_symbol_supplier.cc \ + src/processor/simple_symbol_supplier.h \ + src/processor/windows_frame_info.h \ + src/processor/source_line_resolver_base_types.h \ + src/processor/source_line_resolver_base.cc \ + src/processor/stack_frame_cpu.cc \ + src/processor/stack_frame_symbolizer.cc \ + src/processor/stackwalk_common.cc \ + src/processor/stackwalk_common.h src/processor/stackwalker.cc \ + src/processor/stackwalker_amd64.cc \ + src/processor/stackwalker_amd64.h \ + src/processor/stackwalker_arm.cc \ + src/processor/stackwalker_arm.h \ + src/processor/stackwalker_arm64.cc \ + src/processor/stackwalker_arm64.h \ + src/processor/stackwalker_address_list.cc \ + src/processor/stackwalker_address_list.h \ + src/processor/stackwalker_mips.cc \ + src/processor/stackwalker_mips.h \ + src/processor/stackwalker_ppc.cc \ + src/processor/stackwalker_ppc.h \ + src/processor/stackwalker_ppc64.cc \ + src/processor/stackwalker_ppc64.h \ + src/processor/stackwalker_sparc.cc \ + src/processor/stackwalker_sparc.h \ + src/processor/stackwalker_x86.cc \ + src/processor/stackwalker_x86.h \ + src/processor/static_address_map-inl.h \ + src/processor/static_address_map.h \ + src/processor/static_contained_range_map-inl.h \ + src/processor/static_contained_range_map.h \ + src/processor/static_map_iterator-inl.h \ + src/processor/static_map_iterator.h \ + src/processor/static_map-inl.h src/processor/static_map.h \ + src/processor/static_range_map-inl.h \ + src/processor/static_range_map.h \ + src/processor/symbolic_constants_win.cc \ + src/processor/symbolic_constants_win.h \ + src/processor/tokenize.cc src/processor/tokenize.h +@DISABLE_PROCESSOR_FALSE@am_src_libbreakpad_a_OBJECTS = src/processor/basic_code_modules.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_source_line_resolver.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/call_stack.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/cfi_frame_info.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/convert_old_arm64_context.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_context.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_object.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_linux.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_win.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/fast_source_line_resolver.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/microdump.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/microdump_processor.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump_processor.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/module_comparer.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/module_serializer.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/process_state.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/proc_maps_linux.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/simple_symbol_supplier.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/source_line_resolver_base.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stack_frame_cpu.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stack_frame_symbolizer.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalk_common.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_amd64.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_address_list.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/symbolic_constants_win.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/tokenize.$(OBJEXT) +src_libbreakpad_a_OBJECTS = $(am_src_libbreakpad_a_OBJECTS) +src_testing_libtesting_a_AR = $(AR) $(ARFLAGS) +src_testing_libtesting_a_LIBADD = +am__src_testing_libtesting_a_SOURCES_DIST = \ + src/breakpad_googletest_includes.h \ + src/testing/googletest/src/gtest-all.cc \ + src/testing/googletest/src/gtest_main.cc \ + src/testing/googlemock/src/gmock-all.cc +@SYSTEM_TEST_LIBS_FALSE@am_src_testing_libtesting_a_OBJECTS = src/testing/googletest/src/libtesting_a-gtest-all.$(OBJEXT) \ +@SYSTEM_TEST_LIBS_FALSE@ src/testing/googletest/src/libtesting_a-gtest_main.$(OBJEXT) \ +@SYSTEM_TEST_LIBS_FALSE@ src/testing/googlemock/src/libtesting_a-gmock-all.$(OBJEXT) +src_testing_libtesting_a_OBJECTS = \ + $(am_src_testing_libtesting_a_OBJECTS) +src_third_party_libdisasm_libdisasm_a_AR = $(AR) $(ARFLAGS) +src_third_party_libdisasm_libdisasm_a_LIBADD = +am__src_third_party_libdisasm_libdisasm_a_SOURCES_DIST = \ + src/third_party/libdisasm/ia32_implicit.c \ + src/third_party/libdisasm/ia32_implicit.h \ + src/third_party/libdisasm/ia32_insn.c \ + src/third_party/libdisasm/ia32_insn.h \ + src/third_party/libdisasm/ia32_invariant.c \ + src/third_party/libdisasm/ia32_invariant.h \ + src/third_party/libdisasm/ia32_modrm.c \ + src/third_party/libdisasm/ia32_modrm.h \ + src/third_party/libdisasm/ia32_opcode_tables.c \ + src/third_party/libdisasm/ia32_opcode_tables.h \ + src/third_party/libdisasm/ia32_operand.c \ + src/third_party/libdisasm/ia32_operand.h \ + src/third_party/libdisasm/ia32_reg.c \ + src/third_party/libdisasm/ia32_reg.h \ + src/third_party/libdisasm/ia32_settings.c \ + src/third_party/libdisasm/ia32_settings.h \ + src/third_party/libdisasm/libdis.h \ + src/third_party/libdisasm/qword.h \ + src/third_party/libdisasm/x86_disasm.c \ + src/third_party/libdisasm/x86_format.c \ + src/third_party/libdisasm/x86_imm.c \ + src/third_party/libdisasm/x86_imm.h \ + src/third_party/libdisasm/x86_insn.c \ + src/third_party/libdisasm/x86_misc.c \ + src/third_party/libdisasm/x86_operand_list.c \ + src/third_party/libdisasm/x86_operand_list.h +@DISABLE_PROCESSOR_FALSE@am_src_third_party_libdisasm_libdisasm_a_OBJECTS = src/third_party/libdisasm/ia32_implicit.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/ia32_insn.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/ia32_invariant.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/ia32_modrm.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/ia32_opcode_tables.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/ia32_operand.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/ia32_reg.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/ia32_settings.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/x86_disasm.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/x86_format.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/x86_imm.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/x86_insn.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/x86_misc.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/x86_operand_list.$(OBJEXT) +src_third_party_libdisasm_libdisasm_a_OBJECTS = \ + $(am_src_third_party_libdisasm_libdisasm_a_OBJECTS) +am_src_client_linux_linux_client_unittest_OBJECTS = +src_client_linux_linux_client_unittest_OBJECTS = \ + $(am_src_client_linux_linux_client_unittest_OBJECTS) +am__DEPENDENCIES_1 = +@SYSTEM_TEST_LIBS_FALSE@am__DEPENDENCIES_2 = src/testing/libtesting.a +@SYSTEM_TEST_LIBS_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1) \ +@SYSTEM_TEST_LIBS_TRUE@ $(am__DEPENDENCIES_1) +src_client_linux_linux_client_unittest_LINK = $(CCLD) $(AM_CFLAGS) \ + $(CFLAGS) $(src_client_linux_linux_client_unittest_LDFLAGS) \ + $(LDFLAGS) -o $@ +am__src_client_linux_linux_client_unittest_shlib_SOURCES_DIST = \ + src/breakpad_googletest_includes.h \ + src/testing/googletest/src/gtest-all.cc \ + src/testing/googletest/src/gtest_main.cc \ + src/testing/googlemock/src/gmock-all.cc \ + src/client/linux/handler/exception_handler_unittest.cc \ + src/client/linux/microdump_writer/microdump_writer_unittest.cc \ + src/client/linux/minidump_writer/directory_reader_unittest.cc \ + src/client/linux/minidump_writer/cpu_set_unittest.cc \ + src/client/linux/minidump_writer/line_reader_unittest.cc \ + src/client/linux/minidump_writer/linux_core_dumper.cc \ + src/client/linux/minidump_writer/linux_core_dumper_unittest.cc \ + src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc \ + src/client/linux/minidump_writer/minidump_writer_unittest.cc \ + src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc \ + src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc \ + src/common/linux/elf_core_dump.cc \ + src/common/linux/linux_libc_support_unittest.cc \ + src/common/linux/tests/auto_testfile.h \ + src/common/linux/tests/crash_generator.cc \ + src/common/memory_allocator_unittest.cc \ + src/common/tests/auto_tempdir.h src/common/tests/file_utils.cc \ + src/common/tests/file_utils.h \ + src/processor/basic_code_modules.cc \ + src/processor/convert_old_arm64_context.cc \ + src/processor/dump_context.cc src/processor/dump_object.cc \ + src/processor/logging.cc src/processor/minidump.cc \ + src/processor/pathname_stripper.cc \ + src/processor/proc_maps_linux.cc \ + src/common/linux/breakpad_getcontext.S \ + src/common/linux/breakpad_getcontext_unittest.cc +@SYSTEM_TEST_LIBS_FALSE@am__objects_2 = src/testing/googletest/src/client_linux_linux_client_unittest_shlib-gtest-all.$(OBJEXT) \ +@SYSTEM_TEST_LIBS_FALSE@ src/testing/googletest/src/client_linux_linux_client_unittest_shlib-gtest_main.$(OBJEXT) \ +@SYSTEM_TEST_LIBS_FALSE@ src/testing/googlemock/src/client_linux_linux_client_unittest_shlib-gmock-all.$(OBJEXT) +@HAVE_GETCONTEXT_FALSE@@LINUX_HOST_TRUE@am__objects_3 = src/common/linux/client_linux_linux_client_unittest_shlib-breakpad_getcontext.$(OBJEXT) \ +@HAVE_GETCONTEXT_FALSE@@LINUX_HOST_TRUE@ src/common/linux/client_linux_linux_client_unittest_shlib-breakpad_getcontext_unittest.$(OBJEXT) +@LINUX_HOST_TRUE@am_src_client_linux_linux_client_unittest_shlib_OBJECTS = \ +@LINUX_HOST_TRUE@ $(am__objects_2) \ +@LINUX_HOST_TRUE@ src/client/linux/handler/linux_client_unittest_shlib-exception_handler_unittest.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/client/linux/microdump_writer/linux_client_unittest_shlib-microdump_writer_unittest.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/linux_client_unittest_shlib-directory_reader_unittest.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/linux_client_unittest_shlib-cpu_set_unittest.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/linux_client_unittest_shlib-line_reader_unittest.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_core_dumper.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_core_dumper_unittest.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_ptrace_dumper_unittest.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/linux_client_unittest_shlib-minidump_writer_unittest.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/linux_client_unittest_shlib-minidump_writer_unittest_utils.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/common/linux/client_linux_linux_client_unittest_shlib-elf_core_dump.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/common/linux/client_linux_linux_client_unittest_shlib-linux_libc_support_unittest.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/common/linux/tests/client_linux_linux_client_unittest_shlib-crash_generator.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/common/client_linux_linux_client_unittest_shlib-memory_allocator_unittest.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/common/tests/client_linux_linux_client_unittest_shlib-file_utils.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/processor/client_linux_linux_client_unittest_shlib-basic_code_modules.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/processor/client_linux_linux_client_unittest_shlib-convert_old_arm64_context.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/processor/client_linux_linux_client_unittest_shlib-dump_context.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/processor/client_linux_linux_client_unittest_shlib-dump_object.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/processor/client_linux_linux_client_unittest_shlib-logging.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/processor/client_linux_linux_client_unittest_shlib-minidump.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/processor/client_linux_linux_client_unittest_shlib-pathname_stripper.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/processor/client_linux_linux_client_unittest_shlib-proc_maps_linux.$(OBJEXT) \ +@LINUX_HOST_TRUE@ $(am__objects_3) +src_client_linux_linux_client_unittest_shlib_OBJECTS = \ + $(am_src_client_linux_linux_client_unittest_shlib_OBJECTS) +src_client_linux_linux_client_unittest_shlib_LINK = $(CXXLD) \ + $(AM_CXXFLAGS) $(CXXFLAGS) \ + $(src_client_linux_linux_client_unittest_shlib_LDFLAGS) \ + $(LDFLAGS) -o $@ +am__src_client_linux_linux_dumper_unittest_helper_SOURCES_DIST = src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc +@LINUX_HOST_TRUE@am_src_client_linux_linux_dumper_unittest_helper_OBJECTS = src/client/linux/minidump_writer/linux_dumper_unittest_helper-linux_dumper_unittest_helper.$(OBJEXT) +src_client_linux_linux_dumper_unittest_helper_OBJECTS = \ + $(am_src_client_linux_linux_dumper_unittest_helper_OBJECTS) +src_client_linux_linux_dumper_unittest_helper_LDADD = $(LDADD) +src_client_linux_linux_dumper_unittest_helper_LINK = $(CXXLD) \ + $(src_client_linux_linux_dumper_unittest_helper_CXXFLAGS) \ + $(CXXFLAGS) \ + $(src_client_linux_linux_dumper_unittest_helper_LDFLAGS) \ + $(LDFLAGS) -o $@ +am__src_common_dumper_unittest_SOURCES_DIST = \ + src/common/byte_cursor_unittest.cc src/common/convert_UTF.cc \ + src/common/dwarf_cfi_to_module.cc \ + src/common/dwarf_cfi_to_module_unittest.cc \ + src/common/dwarf_cu_to_module.cc \ + src/common/dwarf_cu_to_module_unittest.cc \ + src/common/dwarf_line_to_module.cc \ + src/common/dwarf_line_to_module_unittest.cc \ + src/common/dwarf_range_list_handler.cc src/common/language.cc \ + src/common/memory_range_unittest.cc src/common/module.cc \ + src/common/module_unittest.cc src/common/path_helper.cc \ + src/common/stabs_reader.cc src/common/stabs_reader_unittest.cc \ + src/common/stabs_to_module.cc \ + src/common/stabs_to_module_unittest.cc \ + src/common/string_conversion.cc \ + src/common/string_conversion_unittest.cc \ + src/common/test_assembler.cc src/common/dwarf/bytereader.cc \ + src/common/dwarf/bytereader.h \ + src/common/dwarf/bytereader-inl.h \ + src/common/dwarf/bytereader_unittest.cc \ + src/common/dwarf/cfi_assembler.cc \ + src/common/dwarf/cfi_assembler.h \ + src/common/dwarf/dwarf2diehandler.cc \ + src/common/dwarf/dwarf2diehandler_unittest.cc \ + src/common/dwarf/dwarf2reader.cc \ + src/common/dwarf/dwarf2reader.h src/common/dwarf/elf_reader.cc \ + src/common/dwarf/elf_reader.h \ + src/common/dwarf/dwarf2reader_cfi_unittest.cc \ + src/common/dwarf/dwarf2reader_die_unittest.cc \ + src/common/dwarf/dwarf2reader_test_common.h \ + src/common/linux/crc32.cc src/common/linux/dump_symbols.cc \ + src/common/linux/dump_symbols_unittest.cc \ + src/common/linux/elf_core_dump.cc \ + src/common/linux/elf_core_dump_unittest.cc \ + src/common/linux/elf_symbols_to_module.cc \ + src/common/linux/elf_symbols_to_module_unittest.cc \ + src/common/linux/elfutils.cc src/common/linux/file_id.cc \ + src/common/linux/file_id_unittest.cc \ + src/common/linux/linux_libc_support.cc \ + src/common/linux/memory_mapped_file.cc \ + src/common/linux/memory_mapped_file_unittest.cc \ + src/common/linux/safe_readlink.cc \ + src/common/linux/safe_readlink_unittest.cc \ + src/common/linux/synth_elf.cc \ + src/common/linux/synth_elf_unittest.cc \ + src/common/linux/tests/crash_generator.cc \ + src/common/linux/tests/crash_generator.h \ + src/common/testdata/func-line-pairing.h \ + src/common/tests/file_utils.cc +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@am_src_common_dumper_unittest_OBJECTS = src/common/dumper_unittest-byte_cursor_unittest.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dumper_unittest-convert_UTF.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dumper_unittest-dwarf_cfi_to_module.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dumper_unittest-dwarf_cfi_to_module_unittest.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dumper_unittest-dwarf_cu_to_module.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dumper_unittest-dwarf_cu_to_module_unittest.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dumper_unittest-dwarf_line_to_module.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dumper_unittest-dwarf_line_to_module_unittest.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dumper_unittest-dwarf_range_list_handler.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dumper_unittest-language.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dumper_unittest-memory_range_unittest.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dumper_unittest-module.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dumper_unittest-module_unittest.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dumper_unittest-path_helper.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dumper_unittest-stabs_reader.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dumper_unittest-stabs_reader_unittest.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dumper_unittest-stabs_to_module.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dumper_unittest-stabs_to_module_unittest.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dumper_unittest-string_conversion.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dumper_unittest-string_conversion_unittest.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dumper_unittest-test_assembler.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/dumper_unittest-bytereader.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/dumper_unittest-bytereader_unittest.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/dumper_unittest-cfi_assembler.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/dumper_unittest-dwarf2diehandler.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/dumper_unittest-dwarf2diehandler_unittest.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/dumper_unittest-dwarf2reader.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/dumper_unittest-elf_reader.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/dumper_unittest-dwarf2reader_cfi_unittest.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/dumper_unittest-dwarf2reader_die_unittest.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/dumper_unittest-crc32.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/dumper_unittest-dump_symbols.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/dumper_unittest-dump_symbols_unittest.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/dumper_unittest-elf_core_dump.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/dumper_unittest-elf_core_dump_unittest.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/dumper_unittest-elf_symbols_to_module.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/dumper_unittest-elf_symbols_to_module_unittest.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/dumper_unittest-elfutils.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/dumper_unittest-file_id.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/dumper_unittest-file_id_unittest.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/dumper_unittest-linux_libc_support.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/dumper_unittest-memory_mapped_file.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/dumper_unittest-memory_mapped_file_unittest.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/dumper_unittest-safe_readlink.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/dumper_unittest-safe_readlink_unittest.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/dumper_unittest-synth_elf.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/dumper_unittest-synth_elf_unittest.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/tests/dumper_unittest-crash_generator.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/tests/dumper_unittest-file_utils.$(OBJEXT) +src_common_dumper_unittest_OBJECTS = \ + $(am_src_common_dumper_unittest_OBJECTS) +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@src_common_dumper_unittest_DEPENDENCIES = \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ $(am__DEPENDENCIES_2) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ $(am__DEPENDENCIES_1) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ $(am__DEPENDENCIES_1) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ $(am__DEPENDENCIES_1) +am__src_common_dwarf_dwarf2reader_lineinfo_unittest_SOURCES_DIST = \ + src/common/dwarf/dwarf2reader.h \ + src/common/dwarf/dwarf2reader_lineinfo_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_common_dwarf_dwarf2reader_lineinfo_unittest_OBJECTS = src/common/dwarf/dwarf2reader_lineinfo_unittest-dwarf2reader_lineinfo_unittest.$(OBJEXT) +src_common_dwarf_dwarf2reader_lineinfo_unittest_OBJECTS = \ + $(am_src_common_dwarf_dwarf2reader_lineinfo_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_common_dwarf_dwarf2reader_lineinfo_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/common/dwarf/bytereader.o \ +@DISABLE_PROCESSOR_FALSE@ src/common/dwarf/dwarf2reader.o \ +@DISABLE_PROCESSOR_FALSE@ src/common/dwarf/elf_reader.o \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_common_dwarf_dwarf2reader_splitfunctions_unittest_SOURCES_DIST = \ + src/common/dwarf/dwarf2reader.h \ + src/common/dwarf/dwarf2reader_splitfunctions_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_common_dwarf_dwarf2reader_splitfunctions_unittest_OBJECTS = src/common/dwarf/dwarf2reader_splitfunctions_unittest-dwarf2reader_splitfunctions_unittest.$(OBJEXT) +src_common_dwarf_dwarf2reader_splitfunctions_unittest_OBJECTS = $(am_src_common_dwarf_dwarf2reader_splitfunctions_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_common_dwarf_dwarf2reader_splitfunctions_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/common/dwarf/bytereader.o \ +@DISABLE_PROCESSOR_FALSE@ src/common/dwarf/dwarf2reader.o \ +@DISABLE_PROCESSOR_FALSE@ src/common/dwarf/elf_reader.o \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_common_linux_google_crashdump_uploader_test_SOURCES_DIST = \ + src/common/linux/google_crashdump_uploader.cc \ + src/common/linux/google_crashdump_uploader_test.cc \ + src/common/linux/libcurl_wrapper.cc +@LINUX_HOST_TRUE@am_src_common_linux_google_crashdump_uploader_test_OBJECTS = src/common/linux/google_crashdump_uploader_test-google_crashdump_uploader.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/common/linux/google_crashdump_uploader_test-google_crashdump_uploader_test.$(OBJEXT) \ +@LINUX_HOST_TRUE@ src/common/linux/google_crashdump_uploader_test-libcurl_wrapper.$(OBJEXT) +src_common_linux_google_crashdump_uploader_test_OBJECTS = \ + $(am_src_common_linux_google_crashdump_uploader_test_OBJECTS) +@LINUX_HOST_TRUE@src_common_linux_google_crashdump_uploader_test_DEPENDENCIES = \ +@LINUX_HOST_TRUE@ $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \ +@LINUX_HOST_TRUE@ $(am__DEPENDENCIES_1) +am__src_common_mac_macho_reader_unittest_SOURCES_DIST = \ + src/common/dwarf_cfi_to_module.cc \ + src/common/dwarf_cu_to_module.cc \ + src/common/dwarf_line_to_module.cc src/common/language.cc \ + src/common/md5.cc src/common/module.cc \ + src/common/path_helper.cc src/common/stabs_reader.cc \ + src/common/stabs_to_module.cc src/common/test_assembler.cc \ + src/common/dwarf/bytereader.cc \ + src/common/dwarf/cfi_assembler.cc \ + src/common/dwarf/dwarf2diehandler.cc \ + src/common/dwarf/dwarf2reader.cc \ + src/common/dwarf/elf_reader.cc \ + src/common/mac/arch_utilities.cc src/common/mac/file_id.cc \ + src/common/mac/macho_id.cc src/common/mac/macho_reader.cc \ + src/common/mac/macho_reader_unittest.cc \ + src/common/mac/macho_utilities.cc \ + src/common/mac/macho_walker.cc src/common/tests/file_utils.cc +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@am_src_common_mac_macho_reader_unittest_OBJECTS = src/common/mac_macho_reader_unittest-dwarf_cfi_to_module.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac_macho_reader_unittest-dwarf_cu_to_module.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac_macho_reader_unittest-dwarf_line_to_module.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac_macho_reader_unittest-language.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac_macho_reader_unittest-md5.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac_macho_reader_unittest-module.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac_macho_reader_unittest-path_helper.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac_macho_reader_unittest-stabs_reader.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac_macho_reader_unittest-stabs_to_module.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac_macho_reader_unittest-test_assembler.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/mac_macho_reader_unittest-bytereader.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/mac_macho_reader_unittest-cfi_assembler.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/mac_macho_reader_unittest-dwarf2diehandler.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/mac_macho_reader_unittest-dwarf2reader.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/mac_macho_reader_unittest-elf_reader.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/macho_reader_unittest-arch_utilities.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/macho_reader_unittest-file_id.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/macho_reader_unittest-macho_id.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/macho_reader_unittest-macho_reader.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/macho_reader_unittest-macho_reader_unittest.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/macho_reader_unittest-macho_utilities.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/macho_reader_unittest-macho_walker.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/tests/mac_macho_reader_unittest-file_utils.$(OBJEXT) +src_common_mac_macho_reader_unittest_OBJECTS = \ + $(am_src_common_mac_macho_reader_unittest_OBJECTS) +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@src_common_mac_macho_reader_unittest_DEPENDENCIES = \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ $(am__DEPENDENCIES_2) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ $(am__DEPENDENCIES_1) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ $(am__DEPENDENCIES_1) +am__src_common_test_assembler_unittest_SOURCES_DIST = \ + src/common/test_assembler.cc src/common/test_assembler.h \ + src/common/test_assembler_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_common_test_assembler_unittest_OBJECTS = src/common/test_assembler_unittest-test_assembler.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/common/test_assembler_unittest-test_assembler_unittest.$(OBJEXT) +src_common_test_assembler_unittest_OBJECTS = \ + $(am_src_common_test_assembler_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_common_test_assembler_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_processor_address_map_unittest_SOURCES_DIST = \ + src/processor/address_map_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_address_map_unittest_OBJECTS = src/processor/address_map_unittest.$(OBJEXT) +src_processor_address_map_unittest_OBJECTS = \ + $(am_src_processor_address_map_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_address_map_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o +am__src_processor_basic_source_line_resolver_unittest_SOURCES_DIST = \ + src/processor/basic_source_line_resolver_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_basic_source_line_resolver_unittest_OBJECTS = src/processor/basic_source_line_resolver_unittest-basic_source_line_resolver_unittest.$(OBJEXT) +src_processor_basic_source_line_resolver_unittest_OBJECTS = $(am_src_processor_basic_source_line_resolver_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_basic_source_line_resolver_unittest_DEPENDENCIES = src/processor/basic_source_line_resolver.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/cfi_frame_info.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/source_line_resolver_base.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/tokenize.o \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_processor_cfi_frame_info_unittest_SOURCES_DIST = \ + src/processor/cfi_frame_info_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_cfi_frame_info_unittest_OBJECTS = src/processor/cfi_frame_info_unittest-cfi_frame_info_unittest.$(OBJEXT) +src_processor_cfi_frame_info_unittest_OBJECTS = \ + $(am_src_processor_cfi_frame_info_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_cfi_frame_info_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/cfi_frame_info.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_processor_contained_range_map_unittest_SOURCES_DIST = \ + src/processor/contained_range_map_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_contained_range_map_unittest_OBJECTS = src/processor/contained_range_map_unittest.$(OBJEXT) +src_processor_contained_range_map_unittest_OBJECTS = \ + $(am_src_processor_contained_range_map_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_contained_range_map_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o +am__src_processor_disassembler_x86_unittest_SOURCES_DIST = \ + src/processor/disassembler_x86_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_disassembler_x86_unittest_OBJECTS = src/processor/disassembler_x86_unittest-disassembler_x86_unittest.$(OBJEXT) +src_processor_disassembler_x86_unittest_OBJECTS = \ + $(am_src_processor_disassembler_x86_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_disassembler_x86_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86.o \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/libdisasm.a \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_processor_exploitability_unittest_SOURCES_DIST = \ + src/processor/exploitability_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_exploitability_unittest_OBJECTS = src/processor/exploitability_unittest-exploitability_unittest.$(OBJEXT) +src_processor_exploitability_unittest_OBJECTS = \ + $(am_src_processor_exploitability_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_exploitability_unittest_DEPENDENCIES = src/processor/convert_old_arm64_context.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump_processor.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/process_state.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_linux.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_win.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_code_modules.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_source_line_resolver.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/call_stack.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/cfi_frame_info.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_context.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_object.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/proc_maps_linux.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/simple_symbol_supplier.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/source_line_resolver_base.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stack_frame_cpu.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stack_frame_symbolizer.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_address_list.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_amd64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/symbolic_constants_win.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/tokenize.o \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/libdisasm.a \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_processor_fast_source_line_resolver_unittest_SOURCES_DIST = \ + src/processor/fast_source_line_resolver_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_fast_source_line_resolver_unittest_OBJECTS = src/processor/fast_source_line_resolver_unittest-fast_source_line_resolver_unittest.$(OBJEXT) +src_processor_fast_source_line_resolver_unittest_OBJECTS = $(am_src_processor_fast_source_line_resolver_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_fast_source_line_resolver_unittest_DEPENDENCIES = src/processor/fast_source_line_resolver.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_source_line_resolver.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/cfi_frame_info.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/module_comparer.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/module_serializer.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/source_line_resolver_base.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/tokenize.o \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_processor_map_serializers_unittest_SOURCES_DIST = \ + src/processor/map_serializers_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_map_serializers_unittest_OBJECTS = src/processor/map_serializers_unittest-map_serializers_unittest.$(OBJEXT) +src_processor_map_serializers_unittest_OBJECTS = \ + $(am_src_processor_map_serializers_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_map_serializers_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_processor_microdump_processor_unittest_SOURCES_DIST = \ + src/processor/microdump_processor_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_microdump_processor_unittest_OBJECTS = src/processor/microdump_processor_unittest-microdump_processor_unittest.$(OBJEXT) +src_processor_microdump_processor_unittest_OBJECTS = \ + $(am_src_processor_microdump_processor_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_microdump_processor_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_code_modules.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_source_line_resolver.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/call_stack.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/convert_old_arm64_context.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/cfi_frame_info.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_context.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_object.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/microdump.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/microdump_processor.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/process_state.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/simple_symbol_supplier.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/source_line_resolver_base.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stack_frame_symbolizer.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_address_list.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_amd64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/tokenize.o \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_processor_microdump_stackwalk_SOURCES_DIST = \ + src/processor/microdump_stackwalk.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_microdump_stackwalk_OBJECTS = src/processor/microdump_stackwalk.$(OBJEXT) +src_processor_microdump_stackwalk_OBJECTS = \ + $(am_src_processor_microdump_stackwalk_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_microdump_stackwalk_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/common/path_helper.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_code_modules.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_source_line_resolver.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/call_stack.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/convert_old_arm64_context.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/cfi_frame_info.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_context.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_object.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/microdump.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/microdump_processor.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/process_state.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/simple_symbol_supplier.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/source_line_resolver_base.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stack_frame_cpu.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stack_frame_symbolizer.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalk_common.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_address_list.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_amd64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/tokenize.o \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/libdisasm.a +am__src_processor_minidump_dump_SOURCES_DIST = \ + src/processor/minidump_dump.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_minidump_dump_OBJECTS = src/processor/minidump_dump.$(OBJEXT) +src_processor_minidump_dump_OBJECTS = \ + $(am_src_processor_minidump_dump_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_minidump_dump_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/common/path_helper.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_code_modules.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/convert_old_arm64_context.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_context.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_object.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/proc_maps_linux.o +am__src_processor_minidump_processor_unittest_SOURCES_DIST = \ + src/processor/minidump_processor_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_minidump_processor_unittest_OBJECTS = src/processor/minidump_processor_unittest-minidump_processor_unittest.$(OBJEXT) +src_processor_minidump_processor_unittest_OBJECTS = \ + $(am_src_processor_minidump_processor_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_minidump_processor_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_code_modules.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_source_line_resolver.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/call_stack.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/cfi_frame_info.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/convert_old_arm64_context.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_context.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_object.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_linux.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_win.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump_processor.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/process_state.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/proc_maps_linux.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/source_line_resolver_base.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stack_frame_cpu.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stack_frame_symbolizer.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_address_list.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_amd64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/symbolic_constants_win.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/tokenize.o \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/libdisasm.a \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_processor_minidump_stackwalk_SOURCES_DIST = \ + src/processor/minidump_stackwalk.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_minidump_stackwalk_OBJECTS = src/processor/minidump_stackwalk.$(OBJEXT) +src_processor_minidump_stackwalk_OBJECTS = \ + $(am_src_processor_minidump_stackwalk_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_minidump_stackwalk_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/common/path_helper.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_code_modules.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_source_line_resolver.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/call_stack.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/cfi_frame_info.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/convert_old_arm64_context.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_context.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_object.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_linux.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_win.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump_processor.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/process_state.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/proc_maps_linux.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/simple_symbol_supplier.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/source_line_resolver_base.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stack_frame_cpu.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stack_frame_symbolizer.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalk_common.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_address_list.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_amd64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/symbolic_constants_win.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/tokenize.o \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/libdisasm.a +am__src_processor_minidump_unittest_SOURCES_DIST = \ + src/common/test_assembler.cc \ + src/processor/minidump_unittest.cc \ + src/processor/synth_minidump.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_minidump_unittest_OBJECTS = src/common/processor_minidump_unittest-test_assembler.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump_unittest-minidump_unittest.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump_unittest-synth_minidump.$(OBJEXT) +src_processor_minidump_unittest_OBJECTS = \ + $(am_src_processor_minidump_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_minidump_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_code_modules.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/convert_old_arm64_context.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_context.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_object.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/proc_maps_linux.o \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_processor_pathname_stripper_unittest_SOURCES_DIST = \ + src/processor/pathname_stripper_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_pathname_stripper_unittest_OBJECTS = src/processor/pathname_stripper_unittest.$(OBJEXT) +src_processor_pathname_stripper_unittest_OBJECTS = \ + $(am_src_processor_pathname_stripper_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_pathname_stripper_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_processor_postfix_evaluator_unittest_SOURCES_DIST = \ + src/processor/postfix_evaluator_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_postfix_evaluator_unittest_OBJECTS = src/processor/postfix_evaluator_unittest.$(OBJEXT) +src_processor_postfix_evaluator_unittest_OBJECTS = \ + $(am_src_processor_postfix_evaluator_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_postfix_evaluator_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_processor_proc_maps_linux_unittest_SOURCES_DIST = \ + src/processor/proc_maps_linux.cc \ + src/processor/proc_maps_linux_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_proc_maps_linux_unittest_OBJECTS = src/processor/proc_maps_linux_unittest-proc_maps_linux.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/proc_maps_linux_unittest-proc_maps_linux_unittest.$(OBJEXT) +src_processor_proc_maps_linux_unittest_OBJECTS = \ + $(am_src_processor_proc_maps_linux_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_proc_maps_linux_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/libdisasm.a \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_processor_range_map_truncate_lower_unittest_SOURCES_DIST = \ + src/processor/range_map_truncate_lower_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_range_map_truncate_lower_unittest_OBJECTS = src/processor/range_map_truncate_lower_unittest-range_map_truncate_lower_unittest.$(OBJEXT) +src_processor_range_map_truncate_lower_unittest_OBJECTS = \ + $(am_src_processor_range_map_truncate_lower_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_range_map_truncate_lower_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_processor_range_map_truncate_upper_unittest_SOURCES_DIST = \ + src/processor/range_map_truncate_upper_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_range_map_truncate_upper_unittest_OBJECTS = src/processor/range_map_truncate_upper_unittest-range_map_truncate_upper_unittest.$(OBJEXT) +src_processor_range_map_truncate_upper_unittest_OBJECTS = \ + $(am_src_processor_range_map_truncate_upper_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_range_map_truncate_upper_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_processor_range_map_unittest_SOURCES_DIST = \ + src/processor/range_map_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_range_map_unittest_OBJECTS = src/processor/range_map_unittest.$(OBJEXT) +src_processor_range_map_unittest_OBJECTS = \ + $(am_src_processor_range_map_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_range_map_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_processor_stackwalker_address_list_unittest_SOURCES_DIST = \ + src/common/test_assembler.cc \ + src/processor/stackwalker_address_list_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_stackwalker_address_list_unittest_OBJECTS = src/common/processor_stackwalker_address_list_unittest-test_assembler.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_address_list_unittest-stackwalker_address_list_unittest.$(OBJEXT) +src_processor_stackwalker_address_list_unittest_OBJECTS = \ + $(am_src_processor_stackwalker_address_list_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_address_list_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/libbreakpad.a \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_processor_stackwalker_amd64_unittest_SOURCES_DIST = \ + src/common/test_assembler.cc \ + src/processor/stackwalker_amd64_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_stackwalker_amd64_unittest_OBJECTS = src/common/processor_stackwalker_amd64_unittest-test_assembler.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_amd64_unittest-stackwalker_amd64_unittest.$(OBJEXT) +src_processor_stackwalker_amd64_unittest_OBJECTS = \ + $(am_src_processor_stackwalker_amd64_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_amd64_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/libbreakpad.a \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_processor_stackwalker_arm64_unittest_SOURCES_DIST = \ + src/common/test_assembler.cc \ + src/processor/stackwalker_arm64_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_stackwalker_arm64_unittest_OBJECTS = src/common/processor_stackwalker_arm64_unittest-test_assembler.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64_unittest-stackwalker_arm64_unittest.$(OBJEXT) +src_processor_stackwalker_arm64_unittest_OBJECTS = \ + $(am_src_processor_stackwalker_arm64_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_arm64_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/libbreakpad.a \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_processor_stackwalker_arm_unittest_SOURCES_DIST = \ + src/common/test_assembler.cc \ + src/processor/stackwalker_arm_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_stackwalker_arm_unittest_OBJECTS = src/common/processor_stackwalker_arm_unittest-test_assembler.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm_unittest-stackwalker_arm_unittest.$(OBJEXT) +src_processor_stackwalker_arm_unittest_OBJECTS = \ + $(am_src_processor_stackwalker_arm_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_arm_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/libbreakpad.a \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_processor_stackwalker_mips64_unittest_SOURCES_DIST = \ + src/common/test_assembler.cc \ + src/processor/stackwalker_mips64_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_stackwalker_mips64_unittest_OBJECTS = src/common/processor_stackwalker_mips64_unittest-test_assembler.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips64_unittest-stackwalker_mips64_unittest.$(OBJEXT) +src_processor_stackwalker_mips64_unittest_OBJECTS = \ + $(am_src_processor_stackwalker_mips64_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_mips64_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/libbreakpad.a \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_processor_stackwalker_mips_unittest_SOURCES_DIST = \ + src/common/test_assembler.cc \ + src/processor/stackwalker_mips_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_stackwalker_mips_unittest_OBJECTS = src/common/processor_stackwalker_mips_unittest-test_assembler.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips_unittest-stackwalker_mips_unittest.$(OBJEXT) +src_processor_stackwalker_mips_unittest_OBJECTS = \ + $(am_src_processor_stackwalker_mips_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_mips_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/libbreakpad.a \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_processor_stackwalker_selftest_SOURCES_DIST = \ + src/processor/stackwalker_selftest.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_stackwalker_selftest_OBJECTS = src/processor/stackwalker_selftest.$(OBJEXT) +src_processor_stackwalker_selftest_OBJECTS = \ + $(am_src_processor_stackwalker_selftest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_selftest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_code_modules.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_source_line_resolver.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/call_stack.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_linux.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_win.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/proc_maps_linux.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/source_line_resolver_base.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stack_frame_cpu.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stack_frame_symbolizer.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_address_list.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_amd64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/tokenize.o \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_processor_stackwalker_x86_unittest_SOURCES_DIST = \ + src/common/test_assembler.cc \ + src/processor/stackwalker_x86_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_stackwalker_x86_unittest_OBJECTS = src/common/processor_stackwalker_x86_unittest-test_assembler.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86_unittest-stackwalker_x86_unittest.$(OBJEXT) +src_processor_stackwalker_x86_unittest_OBJECTS = \ + $(am_src_processor_stackwalker_x86_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_x86_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/libbreakpad.a \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_processor_static_address_map_unittest_SOURCES_DIST = \ + src/processor/static_address_map_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_static_address_map_unittest_OBJECTS = src/processor/static_address_map_unittest-static_address_map_unittest.$(OBJEXT) +src_processor_static_address_map_unittest_OBJECTS = \ + $(am_src_processor_static_address_map_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_static_address_map_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_processor_static_contained_range_map_unittest_SOURCES_DIST = \ + src/processor/static_contained_range_map_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_static_contained_range_map_unittest_OBJECTS = src/processor/static_contained_range_map_unittest-static_contained_range_map_unittest.$(OBJEXT) +src_processor_static_contained_range_map_unittest_OBJECTS = $(am_src_processor_static_contained_range_map_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_static_contained_range_map_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_processor_static_map_unittest_SOURCES_DIST = \ + src/processor/static_map_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_static_map_unittest_OBJECTS = src/processor/static_map_unittest-static_map_unittest.$(OBJEXT) +src_processor_static_map_unittest_OBJECTS = \ + $(am_src_processor_static_map_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_static_map_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_processor_static_range_map_unittest_SOURCES_DIST = \ + src/processor/static_range_map_unittest.cc +@DISABLE_PROCESSOR_FALSE@am_src_processor_static_range_map_unittest_OBJECTS = src/processor/static_range_map_unittest-static_range_map_unittest.$(OBJEXT) +src_processor_static_range_map_unittest_OBJECTS = \ + $(am_src_processor_static_range_map_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_static_range_map_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_processor_synth_minidump_unittest_SOURCES_DIST = \ + src/common/test_assembler.cc src/common/test_assembler.h \ + src/processor/synth_minidump_unittest.cc \ + src/processor/synth_minidump.cc src/processor/synth_minidump.h +@DISABLE_PROCESSOR_FALSE@am_src_processor_synth_minidump_unittest_OBJECTS = src/common/processor_synth_minidump_unittest-test_assembler.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/synth_minidump_unittest-synth_minidump_unittest.$(OBJEXT) \ +@DISABLE_PROCESSOR_FALSE@ src/processor/synth_minidump_unittest-synth_minidump.$(OBJEXT) +src_processor_synth_minidump_unittest_OBJECTS = \ + $(am_src_processor_synth_minidump_unittest_OBJECTS) +@DISABLE_PROCESSOR_FALSE@src_processor_synth_minidump_unittest_DEPENDENCIES = \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_2) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) \ +@DISABLE_PROCESSOR_FALSE@ $(am__DEPENDENCIES_1) +am__src_tools_linux_core2md_core2md_SOURCES_DIST = \ + src/tools/linux/core2md/core2md.cc +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@am_src_tools_linux_core2md_core2md_OBJECTS = src/tools/linux/core2md/core2md.$(OBJEXT) +src_tools_linux_core2md_core2md_OBJECTS = \ + $(am_src_tools_linux_core2md_core2md_OBJECTS) +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@src_tools_linux_core2md_core2md_DEPENDENCIES = src/client/linux/libbreakpad_client.a \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/path_helper.o +am__src_tools_linux_core_handler_core_handler_SOURCES_DIST = \ + src/tools/linux/core_handler/core_handler.cc +@DISABLE_TOOLS_FALSE@@HAVE_MEMFD_CREATE_TRUE@@LINUX_HOST_TRUE@am_src_tools_linux_core_handler_core_handler_OBJECTS = src/tools/linux/core_handler/core_handler.$(OBJEXT) +src_tools_linux_core_handler_core_handler_OBJECTS = \ + $(am_src_tools_linux_core_handler_core_handler_OBJECTS) +@DISABLE_TOOLS_FALSE@@HAVE_MEMFD_CREATE_TRUE@@LINUX_HOST_TRUE@src_tools_linux_core_handler_core_handler_DEPENDENCIES = src/client/linux/libbreakpad_client.a \ +@DISABLE_TOOLS_FALSE@@HAVE_MEMFD_CREATE_TRUE@@LINUX_HOST_TRUE@ src/common/path_helper.o +am__src_tools_linux_dump_syms_dump_syms_SOURCES_DIST = \ + src/common/dwarf_cfi_to_module.cc \ + src/common/dwarf_cu_to_module.cc \ + src/common/dwarf_line_to_module.cc \ + src/common/dwarf_range_list_handler.cc src/common/language.cc \ + src/common/module.cc src/common/path_helper.cc \ + src/common/stabs_reader.cc src/common/stabs_to_module.cc \ + src/common/dwarf/bytereader.cc \ + src/common/dwarf/dwarf2diehandler.cc \ + src/common/dwarf/dwarf2reader.cc \ + src/common/dwarf/elf_reader.cc src/common/linux/crc32.cc \ + src/common/linux/dump_symbols.cc \ + src/common/linux/dump_symbols.h \ + src/common/linux/elf_symbols_to_module.cc \ + src/common/linux/elf_symbols_to_module.h \ + src/common/linux/elfutils.cc src/common/linux/file_id.cc \ + src/common/linux/linux_libc_support.cc \ + src/common/linux/memory_mapped_file.cc \ + src/common/linux/safe_readlink.cc \ + src/tools/linux/dump_syms/dump_syms.cc +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@am_src_tools_linux_dump_syms_dump_syms_OBJECTS = src/common/tools_linux_dump_syms_dump_syms-dwarf_cfi_to_module.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/tools_linux_dump_syms_dump_syms-dwarf_cu_to_module.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/tools_linux_dump_syms_dump_syms-dwarf_line_to_module.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/tools_linux_dump_syms_dump_syms-dwarf_range_list_handler.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/tools_linux_dump_syms_dump_syms-language.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/tools_linux_dump_syms_dump_syms-module.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/tools_linux_dump_syms_dump_syms-path_helper.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/tools_linux_dump_syms_dump_syms-stabs_reader.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/tools_linux_dump_syms_dump_syms-stabs_to_module.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/tools_linux_dump_syms_dump_syms-bytereader.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/tools_linux_dump_syms_dump_syms-dwarf2diehandler.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/tools_linux_dump_syms_dump_syms-dwarf2reader.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/tools_linux_dump_syms_dump_syms-elf_reader.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/tools_linux_dump_syms_dump_syms-crc32.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/tools_linux_dump_syms_dump_syms-dump_symbols.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/tools_linux_dump_syms_dump_syms-elf_symbols_to_module.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/tools_linux_dump_syms_dump_syms-elfutils.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/tools_linux_dump_syms_dump_syms-file_id.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/tools_linux_dump_syms_dump_syms-linux_libc_support.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/tools_linux_dump_syms_dump_syms-memory_mapped_file.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/tools_linux_dump_syms_dump_syms-safe_readlink.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/tools/linux/dump_syms/dump_syms-dump_syms.$(OBJEXT) +src_tools_linux_dump_syms_dump_syms_OBJECTS = \ + $(am_src_tools_linux_dump_syms_dump_syms_OBJECTS) +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@src_tools_linux_dump_syms_dump_syms_DEPENDENCIES = \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ $(am__DEPENDENCIES_1) +src_tools_linux_dump_syms_dump_syms_LINK = $(CXXLD) \ + $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +am__src_tools_linux_md2core_minidump_2_core_SOURCES_DIST = \ + src/common/linux/memory_mapped_file.cc \ + src/common/path_helper.cc \ + src/tools/linux/md2core/minidump-2-core.cc \ + src/tools/linux/md2core/minidump_memory_range.h +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@am_src_tools_linux_md2core_minidump_2_core_OBJECTS = src/common/linux/memory_mapped_file.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/path_helper.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/tools/linux/md2core/minidump-2-core.$(OBJEXT) +src_tools_linux_md2core_minidump_2_core_OBJECTS = \ + $(am_src_tools_linux_md2core_minidump_2_core_OBJECTS) +src_tools_linux_md2core_minidump_2_core_LDADD = $(LDADD) +am__src_tools_linux_md2core_minidump_2_core_unittest_SOURCES_DIST = \ + src/tools/linux/md2core/minidump_memory_range_unittest.cc +@LINUX_HOST_TRUE@am_src_tools_linux_md2core_minidump_2_core_unittest_OBJECTS = src/tools/linux/md2core/minidump_2_core_unittest-minidump_memory_range_unittest.$(OBJEXT) +src_tools_linux_md2core_minidump_2_core_unittest_OBJECTS = $(am_src_tools_linux_md2core_minidump_2_core_unittest_OBJECTS) +@LINUX_HOST_TRUE@src_tools_linux_md2core_minidump_2_core_unittest_DEPENDENCIES = \ +@LINUX_HOST_TRUE@ $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \ +@LINUX_HOST_TRUE@ $(am__DEPENDENCIES_1) +am__src_tools_linux_pid2md_pid2md_SOURCES_DIST = \ + src/tools/linux/pid2md/pid2md.cc +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@am_src_tools_linux_pid2md_pid2md_OBJECTS = src/tools/linux/pid2md/pid2md.$(OBJEXT) +src_tools_linux_pid2md_pid2md_OBJECTS = \ + $(am_src_tools_linux_pid2md_pid2md_OBJECTS) +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@src_tools_linux_pid2md_pid2md_DEPENDENCIES = src/client/linux/libbreakpad_client.a \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/path_helper.o +am__src_tools_linux_symupload_minidump_upload_SOURCES_DIST = \ + src/common/linux/http_upload.cc src/common/path_helper.cc \ + src/tools/linux/symupload/minidump_upload.cc +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@am_src_tools_linux_symupload_minidump_upload_OBJECTS = src/common/linux/http_upload.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/path_helper.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/tools/linux/symupload/minidump_upload.$(OBJEXT) +src_tools_linux_symupload_minidump_upload_OBJECTS = \ + $(am_src_tools_linux_symupload_minidump_upload_OBJECTS) +src_tools_linux_symupload_minidump_upload_DEPENDENCIES = +am__src_tools_linux_symupload_sym_upload_SOURCES_DIST = \ + src/common/linux/http_upload.cc src/common/linux/http_upload.h \ + src/common/linux/libcurl_wrapper.cc \ + src/common/linux/libcurl_wrapper.h \ + src/common/linux/symbol_collector_client.cc \ + src/common/linux/symbol_collector_client.h \ + src/common/linux/symbol_upload.cc \ + src/common/linux/symbol_upload.h src/common/path_helper.cc \ + src/tools/linux/symupload/sym_upload.cc +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@am_src_tools_linux_symupload_sym_upload_OBJECTS = src/common/linux/http_upload.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/libcurl_wrapper.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/symbol_collector_client.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/symbol_upload.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/path_helper.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/tools/linux/symupload/sym_upload.$(OBJEXT) +src_tools_linux_symupload_sym_upload_OBJECTS = \ + $(am_src_tools_linux_symupload_sym_upload_OBJECTS) +src_tools_linux_symupload_sym_upload_DEPENDENCIES = +am__src_tools_mac_dump_syms_dump_syms_mac_SOURCES_DIST = \ + src/common/dwarf_cfi_to_module.cc \ + src/common/dwarf_cu_to_module.cc \ + src/common/dwarf_line_to_module.cc \ + src/common/dwarf_range_list_handler.cc src/common/language.cc \ + src/common/md5.cc src/common/module.cc \ + src/common/path_helper.cc src/common/stabs_reader.cc \ + src/common/stabs_to_module.cc src/common/dwarf/bytereader.cc \ + src/common/dwarf/dwarf2diehandler.cc \ + src/common/dwarf/dwarf2reader.cc \ + src/common/dwarf/elf_reader.cc \ + src/common/mac/arch_utilities.cc src/common/mac/dump_syms.cc \ + src/common/mac/dump_syms.h src/common/mac/file_id.cc \ + src/common/mac/file_id.h src/common/mac/macho_id.cc \ + src/common/mac/macho_id.h src/common/mac/macho_reader.cc \ + src/common/mac/macho_reader.h \ + src/common/mac/macho_utilities.cc \ + src/common/mac/macho_utilities.h \ + src/common/mac/macho_walker.cc src/common/mac/macho_walker.h \ + src/tools/mac/dump_syms/dump_syms_tool.cc +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@am_src_tools_mac_dump_syms_dump_syms_mac_OBJECTS = src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_cfi_to_module.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_cu_to_module.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_line_to_module.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_range_list_handler.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/tools_mac_dump_syms_dump_syms_mac-language.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/tools_mac_dump_syms_dump_syms_mac-md5.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/tools_mac_dump_syms_dump_syms_mac-module.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/tools_mac_dump_syms_dump_syms_mac-path_helper.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/tools_mac_dump_syms_dump_syms_mac-stabs_reader.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/tools_mac_dump_syms_dump_syms_mac-stabs_to_module.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-bytereader.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-dwarf2diehandler.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-dwarf2reader.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-elf_reader.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/tools_mac_dump_syms_dump_syms_mac-arch_utilities.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/tools_mac_dump_syms_dump_syms_mac-dump_syms.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/tools_mac_dump_syms_dump_syms_mac-file_id.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_id.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_reader.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_utilities.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_walker.$(OBJEXT) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/tools/mac/dump_syms/dump_syms_mac-dump_syms_tool.$(OBJEXT) +src_tools_mac_dump_syms_dump_syms_mac_OBJECTS = \ + $(am_src_tools_mac_dump_syms_dump_syms_mac_OBJECTS) +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@src_tools_mac_dump_syms_dump_syms_mac_DEPENDENCIES = \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ $(am__DEPENDENCIES_1) +src_tools_mac_dump_syms_dump_syms_mac_LINK = $(CXXLD) \ + $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +SCRIPTS = $(noinst_SCRIPTS) +AM_V_P = $(am__v_P_@AM_V@) +am__v_P_ = $(am__v_P_@AM_DEFAULT_V@) +am__v_P_0 = false +am__v_P_1 = : +AM_V_GEN = $(am__v_GEN_@AM_V@) +am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@) +am__v_GEN_0 = @echo " GEN " $@; +am__v_GEN_1 = +AM_V_at = $(am__v_at_@AM_V@) +am__v_at_ = $(am__v_at_@AM_DEFAULT_V@) +am__v_at_0 = @ +am__v_at_1 = +DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/src +depcomp = $(SHELL) $(top_srcdir)/autotools/depcomp +am__maybe_remake_depfiles = depfiles +am__depfiles_remade = src/client/$(DEPDIR)/minidump_file_writer.Po \ + src/client/linux/crash_generation/$(DEPDIR)/crash_generation_client.Po \ + src/client/linux/crash_generation/$(DEPDIR)/crash_generation_server.Po \ + src/client/linux/dump_writer_common/$(DEPDIR)/thread_info.Po \ + src/client/linux/dump_writer_common/$(DEPDIR)/ucontext_reader.Po \ + src/client/linux/handler/$(DEPDIR)/exception_handler.Po \ + src/client/linux/handler/$(DEPDIR)/linux_client_unittest_shlib-exception_handler_unittest.Po \ + src/client/linux/handler/$(DEPDIR)/minidump_descriptor.Po \ + src/client/linux/log/$(DEPDIR)/log.Po \ + src/client/linux/microdump_writer/$(DEPDIR)/linux_client_unittest_shlib-microdump_writer_unittest.Po \ + src/client/linux/microdump_writer/$(DEPDIR)/microdump_writer.Po \ + src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-cpu_set_unittest.Po \ + src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-directory_reader_unittest.Po \ + src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-line_reader_unittest.Po \ + src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-linux_core_dumper.Po \ + src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-linux_core_dumper_unittest.Po \ + src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-linux_ptrace_dumper_unittest.Po \ + src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-minidump_writer_unittest.Po \ + src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-minidump_writer_unittest_utils.Po \ + src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.Po \ + src/client/linux/minidump_writer/$(DEPDIR)/linux_core_dumper.Po \ + src/client/linux/minidump_writer/$(DEPDIR)/linux_dumper.Po \ + src/client/linux/minidump_writer/$(DEPDIR)/linux_dumper_unittest_helper-linux_dumper_unittest_helper.Po \ + src/client/linux/minidump_writer/$(DEPDIR)/linux_ptrace_dumper.Po \ + src/client/linux/minidump_writer/$(DEPDIR)/minidump_writer.Po \ + src/common/$(DEPDIR)/client_linux_linux_client_unittest_shlib-memory_allocator_unittest.Po \ + src/common/$(DEPDIR)/convert_UTF.Po \ + src/common/$(DEPDIR)/dumper_unittest-byte_cursor_unittest.Po \ + src/common/$(DEPDIR)/dumper_unittest-convert_UTF.Po \ + src/common/$(DEPDIR)/dumper_unittest-dwarf_cfi_to_module.Po \ + src/common/$(DEPDIR)/dumper_unittest-dwarf_cfi_to_module_unittest.Po \ + src/common/$(DEPDIR)/dumper_unittest-dwarf_cu_to_module.Po \ + src/common/$(DEPDIR)/dumper_unittest-dwarf_cu_to_module_unittest.Po \ + src/common/$(DEPDIR)/dumper_unittest-dwarf_line_to_module.Po \ + src/common/$(DEPDIR)/dumper_unittest-dwarf_line_to_module_unittest.Po \ + src/common/$(DEPDIR)/dumper_unittest-dwarf_range_list_handler.Po \ + src/common/$(DEPDIR)/dumper_unittest-language.Po \ + src/common/$(DEPDIR)/dumper_unittest-memory_range_unittest.Po \ + src/common/$(DEPDIR)/dumper_unittest-module.Po \ + src/common/$(DEPDIR)/dumper_unittest-module_unittest.Po \ + src/common/$(DEPDIR)/dumper_unittest-path_helper.Po \ + src/common/$(DEPDIR)/dumper_unittest-stabs_reader.Po \ + src/common/$(DEPDIR)/dumper_unittest-stabs_reader_unittest.Po \ + src/common/$(DEPDIR)/dumper_unittest-stabs_to_module.Po \ + src/common/$(DEPDIR)/dumper_unittest-stabs_to_module_unittest.Po \ + src/common/$(DEPDIR)/dumper_unittest-string_conversion.Po \ + src/common/$(DEPDIR)/dumper_unittest-string_conversion_unittest.Po \ + src/common/$(DEPDIR)/dumper_unittest-test_assembler.Po \ + src/common/$(DEPDIR)/mac_macho_reader_unittest-dwarf_cfi_to_module.Po \ + src/common/$(DEPDIR)/mac_macho_reader_unittest-dwarf_cu_to_module.Po \ + src/common/$(DEPDIR)/mac_macho_reader_unittest-dwarf_line_to_module.Po \ + src/common/$(DEPDIR)/mac_macho_reader_unittest-language.Po \ + src/common/$(DEPDIR)/mac_macho_reader_unittest-md5.Po \ + src/common/$(DEPDIR)/mac_macho_reader_unittest-module.Po \ + src/common/$(DEPDIR)/mac_macho_reader_unittest-path_helper.Po \ + src/common/$(DEPDIR)/mac_macho_reader_unittest-stabs_reader.Po \ + src/common/$(DEPDIR)/mac_macho_reader_unittest-stabs_to_module.Po \ + src/common/$(DEPDIR)/mac_macho_reader_unittest-test_assembler.Po \ + src/common/$(DEPDIR)/md5.Po \ + src/common/$(DEPDIR)/path_helper.Po \ + src/common/$(DEPDIR)/processor_minidump_unittest-test_assembler.Po \ + src/common/$(DEPDIR)/processor_stackwalker_address_list_unittest-test_assembler.Po \ + src/common/$(DEPDIR)/processor_stackwalker_amd64_unittest-test_assembler.Po \ + src/common/$(DEPDIR)/processor_stackwalker_arm64_unittest-test_assembler.Po \ + src/common/$(DEPDIR)/processor_stackwalker_arm_unittest-test_assembler.Po \ + src/common/$(DEPDIR)/processor_stackwalker_mips64_unittest-test_assembler.Po \ + src/common/$(DEPDIR)/processor_stackwalker_mips_unittest-test_assembler.Po \ + src/common/$(DEPDIR)/processor_stackwalker_x86_unittest-test_assembler.Po \ + src/common/$(DEPDIR)/processor_synth_minidump_unittest-test_assembler.Po \ + src/common/$(DEPDIR)/string_conversion.Po \ + src/common/$(DEPDIR)/test_assembler_unittest-test_assembler.Po \ + src/common/$(DEPDIR)/test_assembler_unittest-test_assembler_unittest.Po \ + src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_cfi_to_module.Po \ + src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_cu_to_module.Po \ + src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_line_to_module.Po \ + src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_range_list_handler.Po \ + src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-language.Po \ + src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-module.Po \ + src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-path_helper.Po \ + src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-stabs_reader.Po \ + src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-stabs_to_module.Po \ + src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_cfi_to_module.Po \ + src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_cu_to_module.Po \ + src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_line_to_module.Po \ + src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_range_list_handler.Po \ + src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-language.Po \ + src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-md5.Po \ + src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-module.Po \ + src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-path_helper.Po \ + src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-stabs_reader.Po \ + src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-stabs_to_module.Po \ + src/common/dwarf/$(DEPDIR)/dumper_unittest-bytereader.Po \ + src/common/dwarf/$(DEPDIR)/dumper_unittest-bytereader_unittest.Po \ + src/common/dwarf/$(DEPDIR)/dumper_unittest-cfi_assembler.Po \ + src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2diehandler.Po \ + src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2diehandler_unittest.Po \ + src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2reader.Po \ + src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2reader_cfi_unittest.Po \ + src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2reader_die_unittest.Po \ + src/common/dwarf/$(DEPDIR)/dumper_unittest-elf_reader.Po \ + src/common/dwarf/$(DEPDIR)/dwarf2reader_lineinfo_unittest-dwarf2reader_lineinfo_unittest.Po \ + src/common/dwarf/$(DEPDIR)/dwarf2reader_splitfunctions_unittest-dwarf2reader_splitfunctions_unittest.Po \ + src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-bytereader.Po \ + src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-cfi_assembler.Po \ + src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-dwarf2diehandler.Po \ + src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-dwarf2reader.Po \ + src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-elf_reader.Po \ + src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-bytereader.Po \ + src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf2diehandler.Po \ + src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf2reader.Po \ + src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-elf_reader.Po \ + src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-bytereader.Po \ + src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf2diehandler.Po \ + src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf2reader.Po \ + src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-elf_reader.Po \ + src/common/linux/$(DEPDIR)/breakpad_getcontext.Po \ + src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-breakpad_getcontext.Po \ + src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-breakpad_getcontext_unittest.Po \ + src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-elf_core_dump.Po \ + src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-linux_libc_support_unittest.Po \ + src/common/linux/$(DEPDIR)/dumper_unittest-crc32.Po \ + src/common/linux/$(DEPDIR)/dumper_unittest-dump_symbols.Po \ + src/common/linux/$(DEPDIR)/dumper_unittest-dump_symbols_unittest.Po \ + src/common/linux/$(DEPDIR)/dumper_unittest-elf_core_dump.Po \ + src/common/linux/$(DEPDIR)/dumper_unittest-elf_core_dump_unittest.Po \ + src/common/linux/$(DEPDIR)/dumper_unittest-elf_symbols_to_module.Po \ + src/common/linux/$(DEPDIR)/dumper_unittest-elf_symbols_to_module_unittest.Po \ + src/common/linux/$(DEPDIR)/dumper_unittest-elfutils.Po \ + src/common/linux/$(DEPDIR)/dumper_unittest-file_id.Po \ + src/common/linux/$(DEPDIR)/dumper_unittest-file_id_unittest.Po \ + src/common/linux/$(DEPDIR)/dumper_unittest-linux_libc_support.Po \ + src/common/linux/$(DEPDIR)/dumper_unittest-memory_mapped_file.Po \ + src/common/linux/$(DEPDIR)/dumper_unittest-memory_mapped_file_unittest.Po \ + src/common/linux/$(DEPDIR)/dumper_unittest-safe_readlink.Po \ + src/common/linux/$(DEPDIR)/dumper_unittest-safe_readlink_unittest.Po \ + src/common/linux/$(DEPDIR)/dumper_unittest-synth_elf.Po \ + src/common/linux/$(DEPDIR)/dumper_unittest-synth_elf_unittest.Po \ + src/common/linux/$(DEPDIR)/elf_core_dump.Po \ + src/common/linux/$(DEPDIR)/elfutils.Po \ + src/common/linux/$(DEPDIR)/file_id.Po \ + src/common/linux/$(DEPDIR)/google_crashdump_uploader_test-google_crashdump_uploader.Po \ + src/common/linux/$(DEPDIR)/google_crashdump_uploader_test-google_crashdump_uploader_test.Po \ + src/common/linux/$(DEPDIR)/google_crashdump_uploader_test-libcurl_wrapper.Po \ + src/common/linux/$(DEPDIR)/guid_creator.Po \ + src/common/linux/$(DEPDIR)/http_upload.Po \ + src/common/linux/$(DEPDIR)/libcurl_wrapper.Po \ + src/common/linux/$(DEPDIR)/linux_libc_support.Po \ + src/common/linux/$(DEPDIR)/memory_mapped_file.Po \ + src/common/linux/$(DEPDIR)/safe_readlink.Po \ + src/common/linux/$(DEPDIR)/symbol_collector_client.Po \ + src/common/linux/$(DEPDIR)/symbol_upload.Po \ + src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-crc32.Po \ + src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dump_symbols.Po \ + src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-elf_symbols_to_module.Po \ + src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-elfutils.Po \ + src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-file_id.Po \ + src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-linux_libc_support.Po \ + src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-memory_mapped_file.Po \ + src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-safe_readlink.Po \ + src/common/linux/tests/$(DEPDIR)/client_linux_linux_client_unittest_shlib-crash_generator.Po \ + src/common/linux/tests/$(DEPDIR)/dumper_unittest-crash_generator.Po \ + src/common/mac/$(DEPDIR)/macho_reader_unittest-arch_utilities.Po \ + src/common/mac/$(DEPDIR)/macho_reader_unittest-file_id.Po \ + src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_id.Po \ + src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_reader.Po \ + src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_reader_unittest.Po \ + src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_utilities.Po \ + src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_walker.Po \ + src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-arch_utilities.Po \ + src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dump_syms.Po \ + src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-file_id.Po \ + src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_id.Po \ + src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_reader.Po \ + src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_utilities.Po \ + src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_walker.Po \ + src/common/tests/$(DEPDIR)/client_linux_linux_client_unittest_shlib-file_utils.Po \ + src/common/tests/$(DEPDIR)/dumper_unittest-file_utils.Po \ + src/common/tests/$(DEPDIR)/mac_macho_reader_unittest-file_utils.Po \ + src/processor/$(DEPDIR)/address_map_unittest.Po \ + src/processor/$(DEPDIR)/basic_code_modules.Po \ + src/processor/$(DEPDIR)/basic_source_line_resolver.Po \ + src/processor/$(DEPDIR)/basic_source_line_resolver_unittest-basic_source_line_resolver_unittest.Po \ + src/processor/$(DEPDIR)/call_stack.Po \ + src/processor/$(DEPDIR)/cfi_frame_info.Po \ + src/processor/$(DEPDIR)/cfi_frame_info_unittest-cfi_frame_info_unittest.Po \ + src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-basic_code_modules.Po \ + src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-convert_old_arm64_context.Po \ + src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-dump_context.Po \ + src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-dump_object.Po \ + src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-logging.Po \ + src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-minidump.Po \ + src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-pathname_stripper.Po \ + src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-proc_maps_linux.Po \ + src/processor/$(DEPDIR)/contained_range_map_unittest.Po \ + src/processor/$(DEPDIR)/convert_old_arm64_context.Po \ + src/processor/$(DEPDIR)/disassembler_x86.Po \ + src/processor/$(DEPDIR)/disassembler_x86_unittest-disassembler_x86_unittest.Po \ + src/processor/$(DEPDIR)/dump_context.Po \ + src/processor/$(DEPDIR)/dump_object.Po \ + src/processor/$(DEPDIR)/exploitability.Po \ + src/processor/$(DEPDIR)/exploitability_linux.Po \ + src/processor/$(DEPDIR)/exploitability_unittest-exploitability_unittest.Po \ + src/processor/$(DEPDIR)/exploitability_win.Po \ + src/processor/$(DEPDIR)/fast_source_line_resolver.Po \ + src/processor/$(DEPDIR)/fast_source_line_resolver_unittest-fast_source_line_resolver_unittest.Po \ + src/processor/$(DEPDIR)/logging.Po \ + src/processor/$(DEPDIR)/map_serializers_unittest-map_serializers_unittest.Po \ + src/processor/$(DEPDIR)/microdump.Po \ + src/processor/$(DEPDIR)/microdump_processor.Po \ + src/processor/$(DEPDIR)/microdump_processor_unittest-microdump_processor_unittest.Po \ + src/processor/$(DEPDIR)/microdump_stackwalk.Po \ + src/processor/$(DEPDIR)/minidump.Po \ + src/processor/$(DEPDIR)/minidump_dump.Po \ + src/processor/$(DEPDIR)/minidump_processor.Po \ + src/processor/$(DEPDIR)/minidump_processor_unittest-minidump_processor_unittest.Po \ + src/processor/$(DEPDIR)/minidump_stackwalk.Po \ + src/processor/$(DEPDIR)/minidump_unittest-minidump_unittest.Po \ + src/processor/$(DEPDIR)/minidump_unittest-synth_minidump.Po \ + src/processor/$(DEPDIR)/module_comparer.Po \ + src/processor/$(DEPDIR)/module_serializer.Po \ + src/processor/$(DEPDIR)/pathname_stripper.Po \ + src/processor/$(DEPDIR)/pathname_stripper_unittest.Po \ + src/processor/$(DEPDIR)/postfix_evaluator_unittest.Po \ + src/processor/$(DEPDIR)/proc_maps_linux.Po \ + src/processor/$(DEPDIR)/proc_maps_linux_unittest-proc_maps_linux.Po \ + src/processor/$(DEPDIR)/proc_maps_linux_unittest-proc_maps_linux_unittest.Po \ + src/processor/$(DEPDIR)/process_state.Po \ + src/processor/$(DEPDIR)/range_map_truncate_lower_unittest-range_map_truncate_lower_unittest.Po \ + src/processor/$(DEPDIR)/range_map_truncate_upper_unittest-range_map_truncate_upper_unittest.Po \ + src/processor/$(DEPDIR)/range_map_unittest.Po \ + src/processor/$(DEPDIR)/simple_symbol_supplier.Po \ + src/processor/$(DEPDIR)/source_line_resolver_base.Po \ + src/processor/$(DEPDIR)/stack_frame_cpu.Po \ + src/processor/$(DEPDIR)/stack_frame_symbolizer.Po \ + src/processor/$(DEPDIR)/stackwalk_common.Po \ + src/processor/$(DEPDIR)/stackwalker.Po \ + src/processor/$(DEPDIR)/stackwalker_address_list.Po \ + src/processor/$(DEPDIR)/stackwalker_address_list_unittest-stackwalker_address_list_unittest.Po \ + src/processor/$(DEPDIR)/stackwalker_amd64.Po \ + src/processor/$(DEPDIR)/stackwalker_amd64_unittest-stackwalker_amd64_unittest.Po \ + src/processor/$(DEPDIR)/stackwalker_arm.Po \ + src/processor/$(DEPDIR)/stackwalker_arm64.Po \ + src/processor/$(DEPDIR)/stackwalker_arm64_unittest-stackwalker_arm64_unittest.Po \ + src/processor/$(DEPDIR)/stackwalker_arm_unittest-stackwalker_arm_unittest.Po \ + src/processor/$(DEPDIR)/stackwalker_mips.Po \ + src/processor/$(DEPDIR)/stackwalker_mips64_unittest-stackwalker_mips64_unittest.Po \ + src/processor/$(DEPDIR)/stackwalker_mips_unittest-stackwalker_mips_unittest.Po \ + src/processor/$(DEPDIR)/stackwalker_ppc.Po \ + src/processor/$(DEPDIR)/stackwalker_ppc64.Po \ + src/processor/$(DEPDIR)/stackwalker_selftest.Po \ + src/processor/$(DEPDIR)/stackwalker_sparc.Po \ + src/processor/$(DEPDIR)/stackwalker_x86.Po \ + src/processor/$(DEPDIR)/stackwalker_x86_unittest-stackwalker_x86_unittest.Po \ + src/processor/$(DEPDIR)/static_address_map_unittest-static_address_map_unittest.Po \ + src/processor/$(DEPDIR)/static_contained_range_map_unittest-static_contained_range_map_unittest.Po \ + src/processor/$(DEPDIR)/static_map_unittest-static_map_unittest.Po \ + src/processor/$(DEPDIR)/static_range_map_unittest-static_range_map_unittest.Po \ + src/processor/$(DEPDIR)/symbolic_constants_win.Po \ + src/processor/$(DEPDIR)/synth_minidump_unittest-synth_minidump.Po \ + src/processor/$(DEPDIR)/synth_minidump_unittest-synth_minidump_unittest.Po \ + src/processor/$(DEPDIR)/tokenize.Po \ + src/testing/googlemock/src/$(DEPDIR)/client_linux_linux_client_unittest_shlib-gmock-all.Po \ + src/testing/googlemock/src/$(DEPDIR)/libtesting_a-gmock-all.Po \ + src/testing/googletest/src/$(DEPDIR)/client_linux_linux_client_unittest_shlib-gtest-all.Po \ + src/testing/googletest/src/$(DEPDIR)/client_linux_linux_client_unittest_shlib-gtest_main.Po \ + src/testing/googletest/src/$(DEPDIR)/libtesting_a-gtest-all.Po \ + src/testing/googletest/src/$(DEPDIR)/libtesting_a-gtest_main.Po \ + src/third_party/libdisasm/$(DEPDIR)/ia32_implicit.Po \ + src/third_party/libdisasm/$(DEPDIR)/ia32_insn.Po \ + src/third_party/libdisasm/$(DEPDIR)/ia32_invariant.Po \ + src/third_party/libdisasm/$(DEPDIR)/ia32_modrm.Po \ + src/third_party/libdisasm/$(DEPDIR)/ia32_opcode_tables.Po \ + src/third_party/libdisasm/$(DEPDIR)/ia32_operand.Po \ + src/third_party/libdisasm/$(DEPDIR)/ia32_reg.Po \ + src/third_party/libdisasm/$(DEPDIR)/ia32_settings.Po \ + src/third_party/libdisasm/$(DEPDIR)/x86_disasm.Po \ + src/third_party/libdisasm/$(DEPDIR)/x86_format.Po \ + src/third_party/libdisasm/$(DEPDIR)/x86_imm.Po \ + src/third_party/libdisasm/$(DEPDIR)/x86_insn.Po \ + src/third_party/libdisasm/$(DEPDIR)/x86_misc.Po \ + src/third_party/libdisasm/$(DEPDIR)/x86_operand_list.Po \ + src/tools/linux/core2md/$(DEPDIR)/core2md.Po \ + src/tools/linux/core_handler/$(DEPDIR)/core_handler.Po \ + src/tools/linux/dump_syms/$(DEPDIR)/dump_syms-dump_syms.Po \ + src/tools/linux/md2core/$(DEPDIR)/minidump-2-core.Po \ + src/tools/linux/md2core/$(DEPDIR)/minidump_2_core_unittest-minidump_memory_range_unittest.Po \ + src/tools/linux/pid2md/$(DEPDIR)/pid2md.Po \ + src/tools/linux/symupload/$(DEPDIR)/minidump_upload.Po \ + src/tools/linux/symupload/$(DEPDIR)/sym_upload.Po \ + src/tools/mac/dump_syms/$(DEPDIR)/dump_syms_mac-dump_syms_tool.Po +am__mv = mv -f +AM_V_lt = $(am__v_lt_@AM_V@) +am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@) +am__v_lt_0 = --silent +am__v_lt_1 = +CPPASCOMPILE = $(CCAS) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CCASFLAGS) $(CCASFLAGS) +AM_V_CPPAS = $(am__v_CPPAS_@AM_V@) +am__v_CPPAS_ = $(am__v_CPPAS_@AM_DEFAULT_V@) +am__v_CPPAS_0 = @echo " CPPAS " $@; +am__v_CPPAS_1 = +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +AM_V_CC = $(am__v_CC_@AM_V@) +am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@) +am__v_CC_0 = @echo " CC " $@; +am__v_CC_1 = +CCLD = $(CC) +LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ +AM_V_CCLD = $(am__v_CCLD_@AM_V@) +am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@) +am__v_CCLD_0 = @echo " CCLD " $@; +am__v_CCLD_1 = +CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ + $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) +AM_V_CXX = $(am__v_CXX_@AM_V@) +am__v_CXX_ = $(am__v_CXX_@AM_DEFAULT_V@) +am__v_CXX_0 = @echo " CXX " $@; +am__v_CXX_1 = +CXXLD = $(CXX) +CXXLINK = $(CXXLD) $(AM_CXXFLAGS) $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) \ + -o $@ +AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) +am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) +am__v_CXXLD_0 = @echo " CXXLD " $@; +am__v_CXXLD_1 = +SOURCES = $(src_client_linux_libbreakpad_client_a_SOURCES) \ + $(src_libbreakpad_a_SOURCES) \ + $(src_testing_libtesting_a_SOURCES) \ + $(src_third_party_libdisasm_libdisasm_a_SOURCES) \ + $(src_client_linux_linux_client_unittest_SOURCES) \ + $(src_client_linux_linux_client_unittest_shlib_SOURCES) \ + $(src_client_linux_linux_dumper_unittest_helper_SOURCES) \ + $(src_common_dumper_unittest_SOURCES) \ + $(src_common_dwarf_dwarf2reader_lineinfo_unittest_SOURCES) \ + $(src_common_dwarf_dwarf2reader_splitfunctions_unittest_SOURCES) \ + $(src_common_linux_google_crashdump_uploader_test_SOURCES) \ + $(src_common_mac_macho_reader_unittest_SOURCES) \ + $(src_common_test_assembler_unittest_SOURCES) \ + $(src_processor_address_map_unittest_SOURCES) \ + $(src_processor_basic_source_line_resolver_unittest_SOURCES) \ + $(src_processor_cfi_frame_info_unittest_SOURCES) \ + $(src_processor_contained_range_map_unittest_SOURCES) \ + $(src_processor_disassembler_x86_unittest_SOURCES) \ + $(src_processor_exploitability_unittest_SOURCES) \ + $(src_processor_fast_source_line_resolver_unittest_SOURCES) \ + $(src_processor_map_serializers_unittest_SOURCES) \ + $(src_processor_microdump_processor_unittest_SOURCES) \ + $(src_processor_microdump_stackwalk_SOURCES) \ + $(src_processor_minidump_dump_SOURCES) \ + $(src_processor_minidump_processor_unittest_SOURCES) \ + $(src_processor_minidump_stackwalk_SOURCES) \ + $(src_processor_minidump_unittest_SOURCES) \ + $(src_processor_pathname_stripper_unittest_SOURCES) \ + $(src_processor_postfix_evaluator_unittest_SOURCES) \ + $(src_processor_proc_maps_linux_unittest_SOURCES) \ + $(src_processor_range_map_truncate_lower_unittest_SOURCES) \ + $(src_processor_range_map_truncate_upper_unittest_SOURCES) \ + $(src_processor_range_map_unittest_SOURCES) \ + $(src_processor_stackwalker_address_list_unittest_SOURCES) \ + $(src_processor_stackwalker_amd64_unittest_SOURCES) \ + $(src_processor_stackwalker_arm64_unittest_SOURCES) \ + $(src_processor_stackwalker_arm_unittest_SOURCES) \ + $(src_processor_stackwalker_mips64_unittest_SOURCES) \ + $(src_processor_stackwalker_mips_unittest_SOURCES) \ + $(src_processor_stackwalker_selftest_SOURCES) \ + $(src_processor_stackwalker_x86_unittest_SOURCES) \ + $(src_processor_static_address_map_unittest_SOURCES) \ + $(src_processor_static_contained_range_map_unittest_SOURCES) \ + $(src_processor_static_map_unittest_SOURCES) \ + $(src_processor_static_range_map_unittest_SOURCES) \ + $(src_processor_synth_minidump_unittest_SOURCES) \ + $(src_tools_linux_core2md_core2md_SOURCES) \ + $(src_tools_linux_core_handler_core_handler_SOURCES) \ + $(src_tools_linux_dump_syms_dump_syms_SOURCES) \ + $(src_tools_linux_md2core_minidump_2_core_SOURCES) \ + $(src_tools_linux_md2core_minidump_2_core_unittest_SOURCES) \ + $(src_tools_linux_pid2md_pid2md_SOURCES) \ + $(src_tools_linux_symupload_minidump_upload_SOURCES) \ + $(src_tools_linux_symupload_sym_upload_SOURCES) \ + $(src_tools_mac_dump_syms_dump_syms_mac_SOURCES) +DIST_SOURCES = \ + $(am__src_client_linux_libbreakpad_client_a_SOURCES_DIST) \ + $(am__src_libbreakpad_a_SOURCES_DIST) \ + $(am__src_testing_libtesting_a_SOURCES_DIST) \ + $(am__src_third_party_libdisasm_libdisasm_a_SOURCES_DIST) \ + $(src_client_linux_linux_client_unittest_SOURCES) \ + $(am__src_client_linux_linux_client_unittest_shlib_SOURCES_DIST) \ + $(am__src_client_linux_linux_dumper_unittest_helper_SOURCES_DIST) \ + $(am__src_common_dumper_unittest_SOURCES_DIST) \ + $(am__src_common_dwarf_dwarf2reader_lineinfo_unittest_SOURCES_DIST) \ + $(am__src_common_dwarf_dwarf2reader_splitfunctions_unittest_SOURCES_DIST) \ + $(am__src_common_linux_google_crashdump_uploader_test_SOURCES_DIST) \ + $(am__src_common_mac_macho_reader_unittest_SOURCES_DIST) \ + $(am__src_common_test_assembler_unittest_SOURCES_DIST) \ + $(am__src_processor_address_map_unittest_SOURCES_DIST) \ + $(am__src_processor_basic_source_line_resolver_unittest_SOURCES_DIST) \ + $(am__src_processor_cfi_frame_info_unittest_SOURCES_DIST) \ + $(am__src_processor_contained_range_map_unittest_SOURCES_DIST) \ + $(am__src_processor_disassembler_x86_unittest_SOURCES_DIST) \ + $(am__src_processor_exploitability_unittest_SOURCES_DIST) \ + $(am__src_processor_fast_source_line_resolver_unittest_SOURCES_DIST) \ + $(am__src_processor_map_serializers_unittest_SOURCES_DIST) \ + $(am__src_processor_microdump_processor_unittest_SOURCES_DIST) \ + $(am__src_processor_microdump_stackwalk_SOURCES_DIST) \ + $(am__src_processor_minidump_dump_SOURCES_DIST) \ + $(am__src_processor_minidump_processor_unittest_SOURCES_DIST) \ + $(am__src_processor_minidump_stackwalk_SOURCES_DIST) \ + $(am__src_processor_minidump_unittest_SOURCES_DIST) \ + $(am__src_processor_pathname_stripper_unittest_SOURCES_DIST) \ + $(am__src_processor_postfix_evaluator_unittest_SOURCES_DIST) \ + $(am__src_processor_proc_maps_linux_unittest_SOURCES_DIST) \ + $(am__src_processor_range_map_truncate_lower_unittest_SOURCES_DIST) \ + $(am__src_processor_range_map_truncate_upper_unittest_SOURCES_DIST) \ + $(am__src_processor_range_map_unittest_SOURCES_DIST) \ + $(am__src_processor_stackwalker_address_list_unittest_SOURCES_DIST) \ + $(am__src_processor_stackwalker_amd64_unittest_SOURCES_DIST) \ + $(am__src_processor_stackwalker_arm64_unittest_SOURCES_DIST) \ + $(am__src_processor_stackwalker_arm_unittest_SOURCES_DIST) \ + $(am__src_processor_stackwalker_mips64_unittest_SOURCES_DIST) \ + $(am__src_processor_stackwalker_mips_unittest_SOURCES_DIST) \ + $(am__src_processor_stackwalker_selftest_SOURCES_DIST) \ + $(am__src_processor_stackwalker_x86_unittest_SOURCES_DIST) \ + $(am__src_processor_static_address_map_unittest_SOURCES_DIST) \ + $(am__src_processor_static_contained_range_map_unittest_SOURCES_DIST) \ + $(am__src_processor_static_map_unittest_SOURCES_DIST) \ + $(am__src_processor_static_range_map_unittest_SOURCES_DIST) \ + $(am__src_processor_synth_minidump_unittest_SOURCES_DIST) \ + $(am__src_tools_linux_core2md_core2md_SOURCES_DIST) \ + $(am__src_tools_linux_core_handler_core_handler_SOURCES_DIST) \ + $(am__src_tools_linux_dump_syms_dump_syms_SOURCES_DIST) \ + $(am__src_tools_linux_md2core_minidump_2_core_SOURCES_DIST) \ + $(am__src_tools_linux_md2core_minidump_2_core_unittest_SOURCES_DIST) \ + $(am__src_tools_linux_pid2md_pid2md_SOURCES_DIST) \ + $(am__src_tools_linux_symupload_minidump_upload_SOURCES_DIST) \ + $(am__src_tools_linux_symupload_sym_upload_SOURCES_DIST) \ + $(am__src_tools_mac_dump_syms_dump_syms_mac_SOURCES_DIST) +am__can_run_installinfo = \ + case $$AM_UPDATE_INFO_DIR in \ + n|no|NO) false;; \ + *) (install-info --version) >/dev/null 2>&1;; \ + esac +DATA = $(dist_doc_DATA) $(pkgconfig_DATA) +am__includecl_HEADERS_DIST = $(top_srcdir)/src/common/linux/*.h +am__includeclc_HEADERS_DIST = \ + $(top_srcdir)/src/client/linux/crash_generation/*.h +am__includecldwc_HEADERS_DIST = \ + $(top_srcdir)/src/client/linux/dump_writer_common/*.h +am__includeclh_HEADERS_DIST = \ + $(top_srcdir)/src/client/linux/handler/*.h +am__includeclm_HEADERS_DIST = \ + $(top_srcdir)/src/client/linux/minidump_writer/*.h +am__includelss_HEADERS_DIST = $(top_srcdir)/src/third_party/lss/*.h +HEADERS = $(includec_HEADERS) $(includecl_HEADERS) \ + $(includeclc_HEADERS) $(includecldwc_HEADERS) \ + $(includeclh_HEADERS) $(includeclm_HEADERS) \ + $(includegbc_HEADERS) $(includelss_HEADERS) \ + $(includep_HEADERS) +am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP) +# Read a list of newline-separated strings from the standard input, +# and print each of them once, without duplicates. Input order is +# *not* preserved. +am__uniquify_input = $(AWK) '\ + BEGIN { nonempty = 0; } \ + { items[$$0] = 1; nonempty = 1; } \ + END { if (nonempty) { for (i in items) print i; }; } \ +' +# Make sure the list of sources is unique. This is necessary because, +# e.g., the same source file might be shared among _SOURCES variables +# for different programs/libraries. +am__define_uniq_tagged_files = \ + list='$(am__tagged_files)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | $(am__uniquify_input)` +ETAGS = etags +CTAGS = ctags +CSCOPE = cscope +AM_RECURSIVE_TARGETS = cscope check recheck +am__tty_colors_dummy = \ + mgn= red= grn= lgn= blu= brg= std=; \ + am__color_tests=no +am__tty_colors = { \ + $(am__tty_colors_dummy); \ + if test "X$(AM_COLOR_TESTS)" = Xno; then \ + am__color_tests=no; \ + elif test "X$(AM_COLOR_TESTS)" = Xalways; then \ + am__color_tests=yes; \ + elif test "X$$TERM" != Xdumb && { test -t 1; } 2>/dev/null; then \ + am__color_tests=yes; \ + fi; \ + if test $$am__color_tests = yes; then \ + red=''; \ + grn=''; \ + lgn=''; \ + blu=''; \ + mgn=''; \ + brg=''; \ + std=''; \ + fi; \ +} +am__recheck_rx = ^[ ]*:recheck:[ ]* +am__global_test_result_rx = ^[ ]*:global-test-result:[ ]* +am__copy_in_global_log_rx = ^[ ]*:copy-in-global-log:[ ]* +# A command that, given a newline-separated list of test names on the +# standard input, print the name of the tests that are to be re-run +# upon "make recheck". +am__list_recheck_tests = $(AWK) '{ \ + recheck = 1; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + { \ + if ((getline line2 < ($$0 ".log")) < 0) \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[nN][Oo]/) \ + { \ + recheck = 0; \ + break; \ + } \ + else if (line ~ /$(am__recheck_rx)[yY][eE][sS]/) \ + { \ + break; \ + } \ + }; \ + if (recheck) \ + print $$0; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# A command that, given a newline-separated list of test names on the +# standard input, create the global log from their .trs and .log files. +am__create_global_log = $(AWK) ' \ +function fatal(msg) \ +{ \ + print "fatal: making $@: " msg | "cat >&2"; \ + exit 1; \ +} \ +function rst_section(header) \ +{ \ + print header; \ + len = length(header); \ + for (i = 1; i <= len; i = i + 1) \ + printf "="; \ + printf "\n\n"; \ +} \ +{ \ + copy_in_global_log = 1; \ + global_test_result = "RUN"; \ + while ((rc = (getline line < ($$0 ".trs"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".trs"); \ + if (line ~ /$(am__global_test_result_rx)/) \ + { \ + sub("$(am__global_test_result_rx)", "", line); \ + sub("[ ]*$$", "", line); \ + global_test_result = line; \ + } \ + else if (line ~ /$(am__copy_in_global_log_rx)[nN][oO]/) \ + copy_in_global_log = 0; \ + }; \ + if (copy_in_global_log) \ + { \ + rst_section(global_test_result ": " $$0); \ + while ((rc = (getline line < ($$0 ".log"))) != 0) \ + { \ + if (rc < 0) \ + fatal("failed to read from " $$0 ".log"); \ + print line; \ + }; \ + printf "\n"; \ + }; \ + close ($$0 ".trs"); \ + close ($$0 ".log"); \ +}' +# Restructured Text title. +am__rst_title = { sed 's/.*/ & /;h;s/./=/g;p;x;s/ *$$//;p;g' && echo; } +# Solaris 10 'make', and several other traditional 'make' implementations, +# pass "-e" to $(SHELL), and POSIX 2008 even requires this. Work around it +# by disabling -e (using the XSI extension "set +e") if it's set. +am__sh_e_setup = case $$- in *e*) set +e;; esac +# Default flags passed to test drivers. +am__common_driver_flags = \ + --color-tests "$$am__color_tests" \ + --enable-hard-errors "$$am__enable_hard_errors" \ + --expect-failure "$$am__expect_failure" +# To be inserted before the command running the test. Creates the +# directory for the log if needed. Stores in $dir the directory +# containing $f, in $tst the test, in $log the log. Executes the +# developer- defined test setup AM_TESTS_ENVIRONMENT (if any), and +# passes TESTS_ENVIRONMENT. Set up options for the wrapper that +# will run the test scripts (or their associated LOG_COMPILER, if +# thy have one). +am__check_pre = \ +$(am__sh_e_setup); \ +$(am__vpath_adj_setup) $(am__vpath_adj) \ +$(am__tty_colors); \ +srcdir=$(srcdir); export srcdir; \ +case "$@" in \ + */*) am__odir=`echo "./$@" | sed 's|/[^/]*$$||'`;; \ + *) am__odir=.;; \ +esac; \ +test "x$$am__odir" = x"." || test -d "$$am__odir" \ + || $(MKDIR_P) "$$am__odir" || exit $$?; \ +if test -f "./$$f"; then dir=./; \ +elif test -f "$$f"; then dir=; \ +else dir="$(srcdir)/"; fi; \ +tst=$$dir$$f; log='$@'; \ +if test -n '$(DISABLE_HARD_ERRORS)'; then \ + am__enable_hard_errors=no; \ +else \ + am__enable_hard_errors=yes; \ +fi; \ +case " $(XFAIL_TESTS) " in \ + *[\ \ ]$$f[\ \ ]* | *[\ \ ]$$dir$$f[\ \ ]*) \ + am__expect_failure=yes;; \ + *) \ + am__expect_failure=no;; \ +esac; \ +$(AM_TESTS_ENVIRONMENT) $(TESTS_ENVIRONMENT) +# A shell command to get the names of the tests scripts with any registered +# extension removed (i.e., equivalently, the names of the test logs, with +# the '.log' extension removed). The result is saved in the shell variable +# '$bases'. This honors runtime overriding of TESTS and TEST_LOGS. Sadly, +# we cannot use something simpler, involving e.g., "$(TEST_LOGS:.log=)", +# since that might cause problem with VPATH rewrites for suffix-less tests. +# See also 'test-harness-vpath-rewrite.sh' and 'test-trs-basic.sh'. +am__set_TESTS_bases = \ + bases='$(TEST_LOGS)'; \ + bases=`for i in $$bases; do echo $$i; done | sed 's/\.log$$//'`; \ + bases=`echo $$bases` +AM_TESTSUITE_SUMMARY_HEADER = ' for $(PACKAGE_STRING)' +RECHECK_LOGS = $(TEST_LOGS) +TEST_SUITE_LOG = test-suite.log +TEST_EXTENSIONS = @EXEEXT@ .test +LOG_COMPILE = $(LOG_COMPILER) $(AM_LOG_FLAGS) $(LOG_FLAGS) +am__set_b = \ + case '$@' in \ + */*) \ + case '$*' in \ + */*) b='$*';; \ + *) b=`echo '$@' | sed 's/\.log$$//'`; \ + esac;; \ + *) \ + b='$*';; \ + esac +am__test_logs1 = $(TESTS:=.log) +am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) +TEST_LOGS = $(am__test_logs2:.test.log=.log) +TEST_LOG_DRIVER = $(SHELL) $(top_srcdir)/autotools/test-driver +TEST_LOG_COMPILE = $(TEST_LOG_COMPILER) $(AM_TEST_LOG_FLAGS) \ + $(TEST_LOG_FLAGS) +am__DIST_COMMON = $(srcdir)/Makefile.in \ + $(srcdir)/breakpad-client.pc.in $(srcdir)/breakpad.pc.in \ + $(top_srcdir)/autotools/ar-lib $(top_srcdir)/autotools/compile \ + $(top_srcdir)/autotools/config.guess \ + $(top_srcdir)/autotools/config.sub \ + $(top_srcdir)/autotools/depcomp \ + $(top_srcdir)/autotools/install-sh \ + $(top_srcdir)/autotools/missing \ + $(top_srcdir)/autotools/test-driver \ + $(top_srcdir)/src/config.h.in AUTHORS ChangeLog INSTALL NEWS \ + autotools/ar-lib autotools/compile autotools/config.guess \ + autotools/config.sub autotools/depcomp autotools/install-sh \ + autotools/ltmain.sh autotools/missing +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +distdir = $(PACKAGE)-$(VERSION) +top_distdir = $(distdir) +am__remove_distdir = \ + if test -d "$(distdir)"; then \ + find "$(distdir)" -type d ! -perm -200 -exec chmod u+w {} ';' \ + && rm -rf "$(distdir)" \ + || { sleep 5 && rm -rf "$(distdir)"; }; \ + else :; fi +am__post_remove_distdir = $(am__remove_distdir) +DIST_ARCHIVES = $(distdir).tar.gz +GZIP_ENV = --best +DIST_TARGETS = dist-gzip +# Exists only to be overridden by the user if desired. +AM_DISTCHECK_DVI_TARGET = dvi +distuninstallcheck_listfiles = find . -type f -print +am__distuninstallcheck_listfiles = $(distuninstallcheck_listfiles) \ + | sed 's|^\./|$(prefix)/|' | grep -v '$(infodir)/dir$$' +distcleancheck_listfiles = find . -type f -print +ACLOCAL = @ACLOCAL@ +AMTAR = @AMTAR@ +AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@ +AR = @AR@ +AUTOCONF = @AUTOCONF@ +AUTOHEADER = @AUTOHEADER@ +AUTOMAKE = @AUTOMAKE@ +AWK = @AWK@ +CC = @CC@ +CCAS = @CCAS@ +CCASDEPMODE = @CCASDEPMODE@ +CCASFLAGS = @CCASFLAGS@ +CCDEPMODE = @CCDEPMODE@ +CFLAGS = @CFLAGS@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +CXX = @CXX@ +CXXDEPMODE = @CXXDEPMODE@ +CXXFLAGS = @CXXFLAGS@ +CYGPATH_W = @CYGPATH_W@ +DEFS = @DEFS@ +DEPDIR = @DEPDIR@ +ECHO_C = @ECHO_C@ +ECHO_N = @ECHO_N@ +ECHO_T = @ECHO_T@ +EGREP = @EGREP@ +EXEEXT = @EXEEXT@ +GMOCK_CFLAGS = @GMOCK_CFLAGS@ +GMOCK_LIBS = @GMOCK_LIBS@ +GREP = @GREP@ +GTEST_CFLAGS = @GTEST_CFLAGS@ +GTEST_LIBS = @GTEST_LIBS@ +HAVE_CXX11 = @HAVE_CXX11@ +INSTALL = @INSTALL@ +INSTALL_DATA = @INSTALL_DATA@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LDFLAGS = @LDFLAGS@ +LIBOBJS = @LIBOBJS@ +LIBS = @LIBS@ +LTLIBOBJS = @LTLIBOBJS@ +MAINT = @MAINT@ +MAKEINFO = @MAKEINFO@ +MKDIR_P = @MKDIR_P@ +OBJEXT = @OBJEXT@ +PACKAGE = @PACKAGE@ +PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_STRING = @PACKAGE_STRING@ +PACKAGE_TARNAME = @PACKAGE_TARNAME@ +PACKAGE_URL = @PACKAGE_URL@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +PTHREAD_CC = @PTHREAD_CC@ +PTHREAD_CFLAGS = @PTHREAD_CFLAGS@ +PTHREAD_LIBS = @PTHREAD_LIBS@ +RANLIB = @RANLIB@ +RUSTC_DEMANGLE_CFLAGS = @RUSTC_DEMANGLE_CFLAGS@ +RUSTC_DEMANGLE_LIBS = @RUSTC_DEMANGLE_LIBS@ +SET_MAKE = @SET_MAKE@ +SHELL = @SHELL@ +STRIP = @STRIP@ +VERSION = @VERSION@ +WARN_CXXFLAGS = @WARN_CXXFLAGS@ +abs_builddir = @abs_builddir@ +abs_srcdir = @abs_srcdir@ +abs_top_builddir = @abs_top_builddir@ +abs_top_srcdir = @abs_top_srcdir@ +ac_ct_AR = @ac_ct_AR@ +ac_ct_CC = @ac_ct_CC@ +ac_ct_CXX = @ac_ct_CXX@ +am__include = @am__include@ +am__leading_dot = @am__leading_dot@ +am__quote = @am__quote@ +am__tar = @am__tar@ +am__untar = @am__untar@ +ax_pthread_config = @ax_pthread_config@ +bindir = @bindir@ +build = @build@ +build_alias = @build_alias@ +build_cpu = @build_cpu@ +build_os = @build_os@ +build_vendor = @build_vendor@ +builddir = @builddir@ +datadir = @datadir@ +datarootdir = @datarootdir@ +docdir = $(prefix)/share/doc/$(PACKAGE)-$(VERSION) +dvidir = @dvidir@ +exec_prefix = @exec_prefix@ +host = @host@ +host_alias = @host_alias@ +host_cpu = @host_cpu@ +host_os = @host_os@ +host_vendor = @host_vendor@ +htmldir = @htmldir@ +includedir = @includedir@ +infodir = @infodir@ +install_sh = @install_sh@ +libdir = @libdir@ +libexecdir = @libexecdir@ +localedir = @localedir@ +localstatedir = @localstatedir@ +mandir = @mandir@ +mkdir_p = @mkdir_p@ +oldincludedir = @oldincludedir@ +pdfdir = @pdfdir@ +prefix = @prefix@ +program_transform_name = @program_transform_name@ +psdir = @psdir@ +runstatedir = @runstatedir@ +sbindir = @sbindir@ +sharedstatedir = @sharedstatedir@ +srcdir = @srcdir@ +sysconfdir = @sysconfdir@ +target_alias = @target_alias@ +top_build_prefix = @top_build_prefix@ +top_builddir = @top_builddir@ +top_srcdir = @top_srcdir@ + +# This allows #includes to be relative to src/ +AM_CPPFLAGS = -I$(top_srcdir)/src +AM_CFLAGS = $(am__append_2) +AM_CXXFLAGS = $(am__append_1) $(WARN_CXXFLAGS) $(am__append_3) + +# Specify include paths for ac macros +ACLOCAL_AMFLAGS = -I m4 + +# License file is called LICENSE not COPYING +AUTOMAKE_OPTIONS = foreign +dist_doc_DATA = \ + AUTHORS \ + ChangeLog \ + INSTALL \ + LICENSE \ + NEWS \ + README.md + +@LINUX_HOST_TRUE@includeclhdir = $(includedir)/$(PACKAGE)/client/linux/handler +@LINUX_HOST_TRUE@includeclh_HEADERS = $(top_srcdir)/src/client/linux/handler/*.h +@LINUX_HOST_TRUE@includecldwcdir = $(includedir)/$(PACKAGE)/client/linux/dump_writer_common +@LINUX_HOST_TRUE@includecldwc_HEADERS = $(top_srcdir)/src/client/linux/dump_writer_common/*.h +@LINUX_HOST_TRUE@includeclmdir = $(includedir)/$(PACKAGE)/client/linux/minidump_writer +@LINUX_HOST_TRUE@includeclm_HEADERS = $(top_srcdir)/src/client/linux/minidump_writer/*.h +@LINUX_HOST_TRUE@includeclcdir = $(includedir)/$(PACKAGE)/client/linux/crash_generation +@LINUX_HOST_TRUE@includeclc_HEADERS = $(top_srcdir)/src/client/linux/crash_generation/*.h +@LINUX_HOST_TRUE@includelssdir = $(includedir)/$(PACKAGE)/third_party/lss +@LINUX_HOST_TRUE@includelss_HEADERS = $(top_srcdir)/src/third_party/lss/*.h +@LINUX_HOST_TRUE@includecldir = $(includedir)/$(PACKAGE)/common/linux +@LINUX_HOST_TRUE@includecl_HEADERS = $(top_srcdir)/src/common/linux/*.h +includegbcdir = $(includedir)/$(PACKAGE)/google_breakpad/common +includegbc_HEADERS = $(top_srcdir)/src/google_breakpad/common/*.h +includecdir = $(includedir)/$(PACKAGE)/common +includec_HEADERS = $(top_srcdir)/src/common/*.h +includepdir = $(includedir)/$(PACKAGE)/processor +includep_HEADERS = $(top_srcdir)/src/processor/*.h +pkgconfigdir = $(libdir)/pkgconfig +pkgconfig_DATA = $(am__append_5) $(am__append_8) +@SYSTEM_TEST_LIBS_FALSE@TEST_CFLAGS = \ +@SYSTEM_TEST_LIBS_FALSE@ -I$(top_srcdir)/src/testing/include \ +@SYSTEM_TEST_LIBS_FALSE@ -I$(top_srcdir)/src/testing/googletest/include \ +@SYSTEM_TEST_LIBS_FALSE@ -I$(top_srcdir)/src/testing/googletest \ +@SYSTEM_TEST_LIBS_FALSE@ -I$(top_srcdir)/src/testing/googlemock/include \ +@SYSTEM_TEST_LIBS_FALSE@ -I$(top_srcdir)/src/testing/googlemock \ +@SYSTEM_TEST_LIBS_FALSE@ -I$(top_srcdir)/src/testing + +@SYSTEM_TEST_LIBS_TRUE@TEST_CFLAGS = $(GTEST_CFLAGS) $(GMOCK_CFLAGS) +@SYSTEM_TEST_LIBS_FALSE@TEST_LIBS = src/testing/libtesting.a +@SYSTEM_TEST_LIBS_TRUE@TEST_LIBS = $(GTEST_LIBS) -lgtest_main $(GMOCK_LIBS) +@SYSTEM_TEST_LIBS_FALSE@TEST_DEPS = $(TEST_LIBS) +@SYSTEM_TEST_LIBS_TRUE@TEST_DEPS = +check_LIBRARIES = src/testing/libtesting.a +noinst_LIBRARIES = $(am__append_6) +lib_LIBRARIES = $(am__append_4) $(am__append_7) +CLEANFILES = $(am__append_12) +@SYSTEM_TEST_LIBS_FALSE@src_testing_libtesting_a_SOURCES = \ +@SYSTEM_TEST_LIBS_FALSE@ src/breakpad_googletest_includes.h \ +@SYSTEM_TEST_LIBS_FALSE@ src/testing/googletest/src/gtest-all.cc \ +@SYSTEM_TEST_LIBS_FALSE@ src/testing/googletest/src/gtest_main.cc \ +@SYSTEM_TEST_LIBS_FALSE@ src/testing/googlemock/src/gmock-all.cc + +@SYSTEM_TEST_LIBS_FALSE@src_testing_libtesting_a_CPPFLAGS = \ +@SYSTEM_TEST_LIBS_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@LINUX_HOST_TRUE@src_client_linux_libbreakpad_client_a_SOURCES = src/client/linux/crash_generation/crash_generation_client.cc \ +@LINUX_HOST_TRUE@ src/client/linux/crash_generation/crash_generation_server.cc \ +@LINUX_HOST_TRUE@ src/client/linux/dump_writer_common/thread_info.cc \ +@LINUX_HOST_TRUE@ src/client/linux/dump_writer_common/ucontext_reader.cc \ +@LINUX_HOST_TRUE@ src/client/linux/handler/exception_handler.cc \ +@LINUX_HOST_TRUE@ src/client/linux/handler/exception_handler.h \ +@LINUX_HOST_TRUE@ src/client/linux/handler/minidump_descriptor.cc \ +@LINUX_HOST_TRUE@ src/client/linux/handler/minidump_descriptor.h \ +@LINUX_HOST_TRUE@ src/client/linux/log/log.cc \ +@LINUX_HOST_TRUE@ src/client/linux/log/log.h \ +@LINUX_HOST_TRUE@ src/client/linux/microdump_writer/microdump_writer.cc \ +@LINUX_HOST_TRUE@ src/client/linux/microdump_writer/microdump_writer.h \ +@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/linux_core_dumper.cc \ +@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/linux_dumper.cc \ +@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/linux_ptrace_dumper.cc \ +@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/minidump_writer.cc \ +@LINUX_HOST_TRUE@ src/client/minidump_file_writer-inl.h \ +@LINUX_HOST_TRUE@ src/client/minidump_file_writer.cc \ +@LINUX_HOST_TRUE@ src/client/minidump_file_writer.h \ +@LINUX_HOST_TRUE@ src/common/convert_UTF.cc \ +@LINUX_HOST_TRUE@ src/common/convert_UTF.h src/common/md5.cc \ +@LINUX_HOST_TRUE@ src/common/md5.h \ +@LINUX_HOST_TRUE@ src/common/string_conversion.cc \ +@LINUX_HOST_TRUE@ src/common/string_conversion.h \ +@LINUX_HOST_TRUE@ src/common/linux/elf_core_dump.cc \ +@LINUX_HOST_TRUE@ src/common/linux/elfutils.cc \ +@LINUX_HOST_TRUE@ src/common/linux/elfutils.h \ +@LINUX_HOST_TRUE@ src/common/linux/file_id.cc \ +@LINUX_HOST_TRUE@ src/common/linux/file_id.h \ +@LINUX_HOST_TRUE@ src/common/linux/guid_creator.cc \ +@LINUX_HOST_TRUE@ src/common/linux/guid_creator.h \ +@LINUX_HOST_TRUE@ src/common/linux/linux_libc_support.cc \ +@LINUX_HOST_TRUE@ src/common/linux/memory_mapped_file.cc \ +@LINUX_HOST_TRUE@ src/common/linux/safe_readlink.cc \ +@LINUX_HOST_TRUE@ $(am__append_9) +@DISABLE_PROCESSOR_FALSE@src_libbreakpad_a_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/google_breakpad/common/breakpad_types.h \ +@DISABLE_PROCESSOR_FALSE@ src/google_breakpad/common/minidump_format.h \ +@DISABLE_PROCESSOR_FALSE@ src/google_breakpad/common/minidump_size.h \ +@DISABLE_PROCESSOR_FALSE@ src/google_breakpad/processor/basic_source_line_resolver.h \ +@DISABLE_PROCESSOR_FALSE@ src/google_breakpad/processor/call_stack.h \ +@DISABLE_PROCESSOR_FALSE@ src/google_breakpad/processor/code_module.h \ +@DISABLE_PROCESSOR_FALSE@ src/google_breakpad/processor/code_modules.h \ +@DISABLE_PROCESSOR_FALSE@ src/google_breakpad/processor/dump_context.h \ +@DISABLE_PROCESSOR_FALSE@ src/google_breakpad/processor/dump_object.h \ +@DISABLE_PROCESSOR_FALSE@ src/google_breakpad/processor/exploitability.h \ +@DISABLE_PROCESSOR_FALSE@ src/google_breakpad/processor/fast_source_line_resolver.h \ +@DISABLE_PROCESSOR_FALSE@ src/google_breakpad/processor/memory_region.h \ +@DISABLE_PROCESSOR_FALSE@ src/google_breakpad/processor/microdump.h \ +@DISABLE_PROCESSOR_FALSE@ src/google_breakpad/processor/microdump_processor.h \ +@DISABLE_PROCESSOR_FALSE@ src/google_breakpad/processor/minidump.h \ +@DISABLE_PROCESSOR_FALSE@ src/google_breakpad/processor/minidump_processor.h \ +@DISABLE_PROCESSOR_FALSE@ src/google_breakpad/processor/process_result.h \ +@DISABLE_PROCESSOR_FALSE@ src/google_breakpad/processor/process_state.h \ +@DISABLE_PROCESSOR_FALSE@ src/google_breakpad/processor/proc_maps_linux.h \ +@DISABLE_PROCESSOR_FALSE@ src/google_breakpad/processor/source_line_resolver_base.h \ +@DISABLE_PROCESSOR_FALSE@ src/google_breakpad/processor/source_line_resolver_interface.h \ +@DISABLE_PROCESSOR_FALSE@ src/google_breakpad/processor/stack_frame.h \ +@DISABLE_PROCESSOR_FALSE@ src/google_breakpad/processor/stack_frame_cpu.h \ +@DISABLE_PROCESSOR_FALSE@ src/google_breakpad/processor/stack_frame_symbolizer.h \ +@DISABLE_PROCESSOR_FALSE@ src/google_breakpad/processor/stackwalker.h \ +@DISABLE_PROCESSOR_FALSE@ src/google_breakpad/processor/symbol_supplier.h \ +@DISABLE_PROCESSOR_FALSE@ src/google_breakpad/processor/system_info.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/address_map-inl.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/address_map.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_code_module.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_code_modules.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_code_modules.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_source_line_resolver_types.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_source_line_resolver.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/call_stack.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/cfi_frame_info.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/cfi_frame_info.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/contained_range_map-inl.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/contained_range_map.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/convert_old_arm64_context.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/convert_old_arm64_context.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_context.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_object.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_linux.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_linux.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_win.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_win.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/fast_source_line_resolver_types.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/fast_source_line_resolver.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/linked_ptr.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/map_serializers-inl.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/map_serializers.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/microdump.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/microdump_processor.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump_processor.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/module_comparer.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/module_comparer.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/module_factory.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/module_serializer.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/module_serializer.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/postfix_evaluator-inl.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/postfix_evaluator.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/process_state.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/proc_maps_linux.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/range_map-inl.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/range_map.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/simple_serializer-inl.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/simple_serializer.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/simple_symbol_supplier.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/simple_symbol_supplier.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/windows_frame_info.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/source_line_resolver_base_types.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/source_line_resolver_base.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stack_frame_cpu.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stack_frame_symbolizer.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalk_common.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalk_common.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_amd64.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_amd64.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_address_list.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_address_list.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/static_address_map-inl.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/static_address_map.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/static_contained_range_map-inl.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/static_contained_range_map.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/static_map_iterator-inl.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/static_map_iterator.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/static_map-inl.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/static_map.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/static_range_map-inl.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/static_range_map.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/symbolic_constants_win.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/symbolic_constants_win.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/tokenize.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/tokenize.h + +@DISABLE_PROCESSOR_FALSE@src_third_party_libdisasm_libdisasm_a_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/ia32_implicit.c \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/ia32_implicit.h \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/ia32_insn.c \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/ia32_insn.h \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/ia32_invariant.c \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/ia32_invariant.h \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/ia32_modrm.c \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/ia32_modrm.h \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/ia32_opcode_tables.c \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/ia32_opcode_tables.h \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/ia32_operand.c \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/ia32_operand.h \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/ia32_reg.c \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/ia32_reg.h \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/ia32_settings.c \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/ia32_settings.h \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/libdis.h \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/qword.h \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/x86_disasm.c \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/x86_format.c \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/x86_imm.c \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/x86_imm.h \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/x86_insn.c \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/x86_misc.c \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/x86_operand_list.c \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/x86_operand_list.h + +@DISABLE_PROCESSOR_FALSE@check_SCRIPTS = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/microdump_stackwalk_test \ +@DISABLE_PROCESSOR_FALSE@ src/processor/microdump_stackwalk_machine_readable_test \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump_dump_test \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump_stackwalk_test \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump_stackwalk_machine_readable_test + +TESTS = $(check_PROGRAMS) $(check_SCRIPTS) +@ANDROID_HOST_FALSE@@TESTS_AS_ROOT_FALSE@LOG_DRIVER = $(top_srcdir)/autotools/test-driver +# The default Autotools test driver script. +@ANDROID_HOST_FALSE@@TESTS_AS_ROOT_TRUE@LOG_DRIVER = $(top_srcdir)/autotools/root-test-driver $(top_srcdir)/autotools/test-driver + +# Since Autotools 1.2, tests are run through a special "test driver" script. +# Unfortunately, it's not possible anymore to specify an alternative shell to +# run them on connected devices, so use a slightly modified version of the +# driver for Android. +@ANDROID_HOST_TRUE@LOG_DRIVER = $(top_srcdir)/android/test-driver +@LINUX_HOST_TRUE@src_client_linux_linux_dumper_unittest_helper_SOURCES = \ +@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc + +@LINUX_HOST_TRUE@src_client_linux_linux_dumper_unittest_helper_LDFLAGS = $(PTHREAD_CFLAGS) +@LINUX_HOST_TRUE@src_client_linux_linux_dumper_unittest_helper_CC = $(PTHREAD_CC) +@ANDROID_HOST_FALSE@@LINUX_HOST_TRUE@src_client_linux_linux_dumper_unittest_helper_CXXFLAGS = $(PTHREAD_CFLAGS) +# On Android PTHREAD_CFLAGS is empty, and adding src/common/android/include +# to the include path is necessary to build this program. +@ANDROID_HOST_TRUE@@LINUX_HOST_TRUE@src_client_linux_linux_dumper_unittest_helper_CXXFLAGS = $(AM_CXXFLAGS) +@LINUX_HOST_TRUE@src_client_linux_linux_client_unittest_shlib_SOURCES = \ +@LINUX_HOST_TRUE@ $(src_testing_libtesting_a_SOURCES) \ +@LINUX_HOST_TRUE@ src/client/linux/handler/exception_handler_unittest.cc \ +@LINUX_HOST_TRUE@ src/client/linux/microdump_writer/microdump_writer_unittest.cc \ +@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/directory_reader_unittest.cc \ +@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/cpu_set_unittest.cc \ +@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/line_reader_unittest.cc \ +@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/linux_core_dumper.cc \ +@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/linux_core_dumper_unittest.cc \ +@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc \ +@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/minidump_writer_unittest.cc \ +@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc \ +@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc \ +@LINUX_HOST_TRUE@ src/common/linux/elf_core_dump.cc \ +@LINUX_HOST_TRUE@ src/common/linux/linux_libc_support_unittest.cc \ +@LINUX_HOST_TRUE@ src/common/linux/tests/auto_testfile.h \ +@LINUX_HOST_TRUE@ src/common/linux/tests/crash_generator.cc \ +@LINUX_HOST_TRUE@ src/common/memory_allocator_unittest.cc \ +@LINUX_HOST_TRUE@ src/common/tests/auto_tempdir.h \ +@LINUX_HOST_TRUE@ src/common/tests/file_utils.cc \ +@LINUX_HOST_TRUE@ src/common/tests/file_utils.h \ +@LINUX_HOST_TRUE@ src/processor/basic_code_modules.cc \ +@LINUX_HOST_TRUE@ src/processor/convert_old_arm64_context.cc \ +@LINUX_HOST_TRUE@ src/processor/dump_context.cc \ +@LINUX_HOST_TRUE@ src/processor/dump_object.cc \ +@LINUX_HOST_TRUE@ src/processor/logging.cc \ +@LINUX_HOST_TRUE@ src/processor/minidump.cc \ +@LINUX_HOST_TRUE@ src/processor/pathname_stripper.cc \ +@LINUX_HOST_TRUE@ src/processor/proc_maps_linux.cc \ +@LINUX_HOST_TRUE@ $(am__append_21) +@LINUX_HOST_TRUE@src_client_linux_linux_client_unittest_shlib_CPPFLAGS = \ +@LINUX_HOST_TRUE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@LINUX_HOST_TRUE@src_client_linux_linux_client_unittest_shlib_LDFLAGS = \ +@LINUX_HOST_TRUE@ -shared -Wl,-h,linux_client_unittest_shlib \ +@LINUX_HOST_TRUE@ $(am__append_22) +@LINUX_HOST_TRUE@src_client_linux_linux_client_unittest_shlib_LDADD = \ +@LINUX_HOST_TRUE@ src/client/linux/crash_generation/crash_generation_client.o \ +@LINUX_HOST_TRUE@ src/client/linux/dump_writer_common/thread_info.o \ +@LINUX_HOST_TRUE@ src/client/linux/dump_writer_common/ucontext_reader.o \ +@LINUX_HOST_TRUE@ src/client/linux/handler/exception_handler.o \ +@LINUX_HOST_TRUE@ src/client/linux/handler/minidump_descriptor.o \ +@LINUX_HOST_TRUE@ src/client/linux/log/log.o \ +@LINUX_HOST_TRUE@ src/client/linux/microdump_writer/microdump_writer.o \ +@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/linux_dumper.o \ +@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/linux_ptrace_dumper.o \ +@LINUX_HOST_TRUE@ src/client/linux/minidump_writer/minidump_writer.o \ +@LINUX_HOST_TRUE@ src/client/minidump_file_writer.o \ +@LINUX_HOST_TRUE@ src/common/convert_UTF.o \ +@LINUX_HOST_TRUE@ src/common/md5.o \ +@LINUX_HOST_TRUE@ src/common/linux/elfutils.o \ +@LINUX_HOST_TRUE@ src/common/linux/file_id.o \ +@LINUX_HOST_TRUE@ src/common/linux/guid_creator.o \ +@LINUX_HOST_TRUE@ src/common/linux/linux_libc_support.o \ +@LINUX_HOST_TRUE@ src/common/linux/memory_mapped_file.o \ +@LINUX_HOST_TRUE@ src/common/linux/safe_readlink.o \ +@LINUX_HOST_TRUE@ src/common/string_conversion.o \ +@LINUX_HOST_TRUE@ $(TEST_LIBS) \ +@LINUX_HOST_TRUE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@LINUX_HOST_TRUE@src_client_linux_linux_client_unittest_shlib_DEPENDENCIES = \ +@LINUX_HOST_TRUE@ src/client/linux/linux_dumper_unittest_helper \ +@LINUX_HOST_TRUE@ src/client/linux/libbreakpad_client.a \ +@LINUX_HOST_TRUE@ $(TEST_DEPS) \ +@LINUX_HOST_TRUE@ src/libbreakpad.a + +@LINUX_HOST_TRUE@src_client_linux_linux_client_unittest_SOURCES = +# The extra-long build id is for a test in minidump_writer_unittest.cc. +@LINUX_HOST_TRUE@src_client_linux_linux_client_unittest_LDFLAGS = \ +@LINUX_HOST_TRUE@ -Wl,-rpath,'$$ORIGIN' \ +@LINUX_HOST_TRUE@ -Wl,--build-id=0x000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f \ +@LINUX_HOST_TRUE@ $(am__append_23) +@LINUX_HOST_TRUE@src_client_linux_linux_client_unittest_LDADD = \ +@LINUX_HOST_TRUE@ src/client/linux/linux_client_unittest_shlib \ +@LINUX_HOST_TRUE@ $(TEST_LIBS) + +@LINUX_HOST_TRUE@src_client_linux_linux_client_unittest_DEPENDENCIES = \ +@LINUX_HOST_TRUE@ src/client/linux/linux_client_unittest_shlib + +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@src_tools_linux_core2md_core2md_SOURCES = \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/tools/linux/core2md/core2md.cc + +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@src_tools_linux_core2md_core2md_LDADD = \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/client/linux/libbreakpad_client.a \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/path_helper.o + +@DISABLE_TOOLS_FALSE@@HAVE_MEMFD_CREATE_TRUE@@LINUX_HOST_TRUE@src_tools_linux_core_handler_core_handler_SOURCES = \ +@DISABLE_TOOLS_FALSE@@HAVE_MEMFD_CREATE_TRUE@@LINUX_HOST_TRUE@ src/tools/linux/core_handler/core_handler.cc + +@DISABLE_TOOLS_FALSE@@HAVE_MEMFD_CREATE_TRUE@@LINUX_HOST_TRUE@src_tools_linux_core_handler_core_handler_LDADD = \ +@DISABLE_TOOLS_FALSE@@HAVE_MEMFD_CREATE_TRUE@@LINUX_HOST_TRUE@ src/client/linux/libbreakpad_client.a \ +@DISABLE_TOOLS_FALSE@@HAVE_MEMFD_CREATE_TRUE@@LINUX_HOST_TRUE@ src/common/path_helper.o + +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@src_tools_linux_pid2md_pid2md_SOURCES = \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/tools/linux/pid2md/pid2md.cc + +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@src_tools_linux_pid2md_pid2md_LDADD = \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/client/linux/libbreakpad_client.a \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/path_helper.o + +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@src_tools_linux_dump_syms_dump_syms_SOURCES = \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf_cfi_to_module.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf_cu_to_module.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf_line_to_module.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf_range_list_handler.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/language.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/module.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/path_helper.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/stabs_reader.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/stabs_to_module.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/bytereader.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/dwarf2diehandler.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/dwarf2reader.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/elf_reader.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/crc32.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/dump_symbols.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/dump_symbols.h \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/elf_symbols_to_module.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/elf_symbols_to_module.h \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/elfutils.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/file_id.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/linux_libc_support.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/memory_mapped_file.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/safe_readlink.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/tools/linux/dump_syms/dump_syms.cc + +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@src_tools_linux_dump_syms_dump_syms_CXXFLAGS = \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ $(RUSTC_DEMANGLE_CFLAGS) + +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@src_tools_linux_dump_syms_dump_syms_LDADD = \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ $(RUSTC_DEMANGLE_LIBS) + +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@src_tools_linux_md2core_minidump_2_core_SOURCES = \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/memory_mapped_file.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/path_helper.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/tools/linux/md2core/minidump-2-core.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/tools/linux/md2core/minidump_memory_range.h + +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@src_tools_linux_symupload_minidump_upload_SOURCES = \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/http_upload.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/path_helper.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/tools/linux/symupload/minidump_upload.cc + +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@src_tools_linux_symupload_minidump_upload_LDADD = -ldl +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@src_tools_linux_symupload_sym_upload_SOURCES = \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/http_upload.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/http_upload.h \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/libcurl_wrapper.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/libcurl_wrapper.h \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/symbol_collector_client.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/symbol_collector_client.h \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/symbol_upload.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/symbol_upload.h \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/path_helper.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/tools/linux/symupload/sym_upload.cc + +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@src_tools_linux_symupload_sym_upload_LDADD = -ldl +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@src_tools_mac_dump_syms_dump_syms_mac_SOURCES = \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf_cfi_to_module.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf_cu_to_module.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf_line_to_module.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf_range_list_handler.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/language.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/md5.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/module.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/path_helper.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/stabs_reader.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/stabs_to_module.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/bytereader.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/dwarf2diehandler.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/dwarf2reader.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/elf_reader.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/arch_utilities.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/dump_syms.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/dump_syms.h \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/file_id.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/file_id.h \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/macho_id.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/macho_id.h \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/macho_reader.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/macho_reader.h \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/macho_utilities.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/macho_utilities.h \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/macho_walker.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/macho_walker.h \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/tools/mac/dump_syms/dump_syms_tool.cc + +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS = \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ -I$(top_srcdir)/src/third_party/mac_headers \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ $(RUSTC_DEMANGLE_CFLAGS) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ -DHAVE_MACH_O_NLIST_H + +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@src_tools_mac_dump_syms_dump_syms_mac_LDADD = \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ $(RUSTC_DEMANGLE_LIBS) + +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@src_common_dumper_unittest_SOURCES = \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/byte_cursor_unittest.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/convert_UTF.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf_cfi_to_module.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf_cfi_to_module_unittest.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf_cu_to_module.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf_cu_to_module_unittest.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf_line_to_module.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf_line_to_module_unittest.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf_range_list_handler.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/language.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/memory_range_unittest.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/module.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/module_unittest.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/path_helper.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/stabs_reader.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/stabs_reader_unittest.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/stabs_to_module.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/stabs_to_module_unittest.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/string_conversion.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/string_conversion_unittest.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/test_assembler.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/bytereader.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/bytereader.h \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/bytereader-inl.h \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/bytereader_unittest.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/cfi_assembler.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/cfi_assembler.h \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/dwarf2diehandler.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/dwarf2diehandler_unittest.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/dwarf2reader.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/dwarf2reader.h \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/elf_reader.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/elf_reader.h \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/dwarf2reader_cfi_unittest.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/dwarf2reader_die_unittest.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/dwarf2reader_test_common.h \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/crc32.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/dump_symbols.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/dump_symbols_unittest.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/elf_core_dump.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/elf_core_dump_unittest.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/elf_symbols_to_module.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/elf_symbols_to_module_unittest.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/elfutils.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/file_id.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/file_id_unittest.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/linux_libc_support.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/memory_mapped_file.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/memory_mapped_file_unittest.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/safe_readlink.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/safe_readlink_unittest.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/synth_elf.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/synth_elf_unittest.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/tests/crash_generator.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/linux/tests/crash_generator.h \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/testdata/func-line-pairing.h \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/tests/file_utils.cc + +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@src_common_dumper_unittest_CPPFLAGS = \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ $(RUSTC_DEMANGLE_CFLAGS) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ $(PTHREAD_CFLAGS) + +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@src_common_dumper_unittest_LDADD = \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ $(TEST_LIBS) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ $(RUSTC_DEMANGLE_LIBS) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@src_common_mac_macho_reader_unittest_SOURCES = \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf_cfi_to_module.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf_cu_to_module.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf_line_to_module.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/language.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/md5.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/module.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/path_helper.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/stabs_reader.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/stabs_to_module.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/test_assembler.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/bytereader.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/cfi_assembler.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/dwarf2diehandler.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/dwarf2reader.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/dwarf/elf_reader.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/arch_utilities.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/file_id.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/macho_id.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/macho_reader.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/macho_reader_unittest.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/macho_utilities.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/mac/macho_walker.cc \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ src/common/tests/file_utils.cc + +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@src_common_mac_macho_reader_unittest_CPPFLAGS = \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ -I$(top_srcdir)/src/third_party/mac_headers \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ -DHAVE_MACH_O_NLIST_H \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ $(PTHREAD_CFLAGS) + +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@src_common_mac_macho_reader_unittest_LDADD = \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ $(TEST_LIBS) \ +@DISABLE_TOOLS_FALSE@@LINUX_HOST_TRUE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@LINUX_HOST_TRUE@src_common_linux_google_crashdump_uploader_test_SOURCES = \ +@LINUX_HOST_TRUE@ src/common/linux/google_crashdump_uploader.cc \ +@LINUX_HOST_TRUE@ src/common/linux/google_crashdump_uploader_test.cc \ +@LINUX_HOST_TRUE@ src/common/linux/libcurl_wrapper.cc + +@LINUX_HOST_TRUE@src_common_linux_google_crashdump_uploader_test_CPPFLAGS = \ +@LINUX_HOST_TRUE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@LINUX_HOST_TRUE@src_common_linux_google_crashdump_uploader_test_LDADD = \ +@LINUX_HOST_TRUE@ $(TEST_LIBS) \ +@LINUX_HOST_TRUE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) \ +@LINUX_HOST_TRUE@ -ldl + +@LINUX_HOST_TRUE@src_tools_linux_md2core_minidump_2_core_unittest_SOURCES = \ +@LINUX_HOST_TRUE@ src/tools/linux/md2core/minidump_memory_range_unittest.cc + +@LINUX_HOST_TRUE@src_tools_linux_md2core_minidump_2_core_unittest_CPPFLAGS = \ +@LINUX_HOST_TRUE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@LINUX_HOST_TRUE@src_tools_linux_md2core_minidump_2_core_unittest_LDADD = \ +@LINUX_HOST_TRUE@ $(TEST_LIBS) \ +@LINUX_HOST_TRUE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_processor_address_map_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/address_map_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_address_map_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o + +@DISABLE_PROCESSOR_FALSE@src_processor_basic_source_line_resolver_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_source_line_resolver_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_basic_source_line_resolver_unittest_CPPFLAGS = \ +@DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@DISABLE_PROCESSOR_FALSE@src_processor_basic_source_line_resolver_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_source_line_resolver.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/cfi_frame_info.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/source_line_resolver_base.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/tokenize.o \ +@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_processor_cfi_frame_info_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/cfi_frame_info_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_cfi_frame_info_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/cfi_frame_info.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_processor_cfi_frame_info_unittest_CPPFLAGS = \ +@DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@DISABLE_PROCESSOR_FALSE@src_processor_contained_range_map_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/contained_range_map_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_contained_range_map_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o + +@DISABLE_PROCESSOR_FALSE@src_processor_exploitability_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_exploitability_unittest_CPPFLAGS = \ +@DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@DISABLE_PROCESSOR_FALSE@src_processor_exploitability_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/convert_old_arm64_context.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump_processor.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/process_state.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_linux.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_win.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_code_modules.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_source_line_resolver.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/call_stack.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/cfi_frame_info.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_context.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_object.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/proc_maps_linux.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/simple_symbol_supplier.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/source_line_resolver_base.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stack_frame_cpu.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stack_frame_symbolizer.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_address_list.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_amd64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/symbolic_constants_win.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/tokenize.o \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/libdisasm.a \ +@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_processor_disassembler_x86_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_disassembler_x86_unittest_CPPFLAGS = \ +@DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@DISABLE_PROCESSOR_FALSE@src_processor_disassembler_x86_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86.o \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/libdisasm.a \ +@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_processor_fast_source_line_resolver_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/fast_source_line_resolver_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_fast_source_line_resolver_unittest_CPPFLAGS = \ +@DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@DISABLE_PROCESSOR_FALSE@src_processor_fast_source_line_resolver_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/fast_source_line_resolver.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_source_line_resolver.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/cfi_frame_info.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/module_comparer.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/module_serializer.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/source_line_resolver_base.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/tokenize.o \ +@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_processor_map_serializers_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/map_serializers_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_map_serializers_unittest_CPPFLAGS = \ +@DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@DISABLE_PROCESSOR_FALSE@src_processor_map_serializers_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_processor_microdump_processor_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/microdump_processor_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_microdump_processor_unittest_CPPFLAGS = \ +@DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@DISABLE_PROCESSOR_FALSE@src_processor_microdump_processor_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_code_modules.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_source_line_resolver.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/call_stack.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/convert_old_arm64_context.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/cfi_frame_info.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_context.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_object.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/microdump.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/microdump_processor.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/process_state.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/simple_symbol_supplier.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/source_line_resolver_base.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stack_frame_symbolizer.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_address_list.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_amd64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/tokenize.o \ +@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_processor_minidump_processor_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump_processor_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_minidump_processor_unittest_CPPFLAGS = \ +@DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@DISABLE_PROCESSOR_FALSE@src_processor_minidump_processor_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_code_modules.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_source_line_resolver.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/call_stack.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/cfi_frame_info.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/convert_old_arm64_context.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_context.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_object.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_linux.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_win.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump_processor.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/process_state.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/proc_maps_linux.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/source_line_resolver_base.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stack_frame_cpu.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stack_frame_symbolizer.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_address_list.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_amd64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/symbolic_constants_win.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/tokenize.o \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/libdisasm.a \ +@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_processor_minidump_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/common/test_assembler.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump_unittest.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/synth_minidump.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_minidump_unittest_CPPFLAGS = \ +@DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@DISABLE_PROCESSOR_FALSE@src_processor_minidump_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_code_modules.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/convert_old_arm64_context.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_context.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_object.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/proc_maps_linux.o \ +@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_processor_proc_maps_linux_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/proc_maps_linux.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/proc_maps_linux_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_proc_maps_linux_unittest_CPPFLAGS = \ +@DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@DISABLE_PROCESSOR_FALSE@src_processor_proc_maps_linux_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/libdisasm.a \ +@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_processor_static_address_map_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/static_address_map_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_static_address_map_unittest_CPPFLAGS = \ +@DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@DISABLE_PROCESSOR_FALSE@src_processor_static_address_map_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_processor_static_contained_range_map_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/static_contained_range_map_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_static_contained_range_map_unittest_CPPFLAGS = \ +@DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@DISABLE_PROCESSOR_FALSE@src_processor_static_contained_range_map_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_processor_static_map_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/static_map_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_static_map_unittest_CPPFLAGS = \ +@DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@DISABLE_PROCESSOR_FALSE@src_processor_static_map_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_processor_static_range_map_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/static_range_map_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_static_range_map_unittest_CPPFLAGS = \ +@DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@DISABLE_PROCESSOR_FALSE@src_processor_static_range_map_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_processor_pathname_stripper_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_pathname_stripper_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_processor_postfix_evaluator_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/postfix_evaluator_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_postfix_evaluator_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_processor_range_map_truncate_lower_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/range_map_truncate_lower_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_range_map_truncate_lower_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_processor_range_map_truncate_lower_unittest_CPPFLAGS = \ +@DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@DISABLE_PROCESSOR_FALSE@src_processor_range_map_truncate_upper_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/range_map_truncate_upper_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_range_map_truncate_upper_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_processor_range_map_truncate_upper_unittest_CPPFLAGS = \ +@DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@DISABLE_PROCESSOR_FALSE@src_processor_range_map_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/range_map_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_range_map_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_selftest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_selftest.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_selftest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_code_modules.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_source_line_resolver.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/call_stack.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_linux.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_win.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/proc_maps_linux.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/source_line_resolver_base.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stack_frame_cpu.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stack_frame_symbolizer.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_address_list.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_amd64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/tokenize.o \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_amd64_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/common/test_assembler.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_amd64_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_amd64_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/libbreakpad.a \ +@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_amd64_unittest_CPPFLAGS = \ +@DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_arm_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/common/test_assembler.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_arm_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/libbreakpad.a \ +@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_arm_unittest_CPPFLAGS = \ +@DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_arm64_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/common/test_assembler.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_arm64_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/libbreakpad.a \ +@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_arm64_unittest_CPPFLAGS = \ +@DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_address_list_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/common/test_assembler.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_address_list_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_address_list_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/libbreakpad.a \ +@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_address_list_unittest_CPPFLAGS = \ +@DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_mips_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/common/test_assembler.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_mips_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/libbreakpad.a \ +@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_mips_unittest_CPPFLAGS = \ +@DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_mips64_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/common/test_assembler.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips64_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_mips64_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/libbreakpad.a \ +@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_mips64_unittest_CPPFLAGS = \ +@DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_x86_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/common/test_assembler.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_x86_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/libbreakpad.a \ +@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_processor_stackwalker_x86_unittest_CPPFLAGS = \ +@DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@DISABLE_PROCESSOR_FALSE@src_processor_synth_minidump_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/common/test_assembler.cc \ +@DISABLE_PROCESSOR_FALSE@ src/common/test_assembler.h \ +@DISABLE_PROCESSOR_FALSE@ src/processor/synth_minidump_unittest.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/synth_minidump.cc \ +@DISABLE_PROCESSOR_FALSE@ src/processor/synth_minidump.h + +@DISABLE_PROCESSOR_FALSE@src_processor_synth_minidump_unittest_CPPFLAGS = \ +@DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@DISABLE_PROCESSOR_FALSE@src_processor_synth_minidump_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_common_test_assembler_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/common/test_assembler.cc \ +@DISABLE_PROCESSOR_FALSE@ src/common/test_assembler.h \ +@DISABLE_PROCESSOR_FALSE@ src/common/test_assembler_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_common_test_assembler_unittest_CPPFLAGS = \ +@DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@DISABLE_PROCESSOR_FALSE@src_common_test_assembler_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_common_dwarf_dwarf2reader_lineinfo_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/common/dwarf/dwarf2reader.h \ +@DISABLE_PROCESSOR_FALSE@ src/common/dwarf/dwarf2reader_lineinfo_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_common_dwarf_dwarf2reader_lineinfo_unittest_CPPFLAGS = \ +@DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@DISABLE_PROCESSOR_FALSE@src_common_dwarf_dwarf2reader_lineinfo_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/common/dwarf/bytereader.o \ +@DISABLE_PROCESSOR_FALSE@ src/common/dwarf/dwarf2reader.o \ +@DISABLE_PROCESSOR_FALSE@ src/common/dwarf/elf_reader.o \ +@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@src_common_dwarf_dwarf2reader_splitfunctions_unittest_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/common/dwarf/dwarf2reader.h \ +@DISABLE_PROCESSOR_FALSE@ src/common/dwarf/dwarf2reader_splitfunctions_unittest.cc + +@DISABLE_PROCESSOR_FALSE@src_common_dwarf_dwarf2reader_splitfunctions_unittest_CPPFLAGS = \ +@DISABLE_PROCESSOR_FALSE@ $(AM_CPPFLAGS) $(TEST_CFLAGS) + +@DISABLE_PROCESSOR_FALSE@src_common_dwarf_dwarf2reader_splitfunctions_unittest_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/common/dwarf/bytereader.o \ +@DISABLE_PROCESSOR_FALSE@ src/common/dwarf/dwarf2reader.o \ +@DISABLE_PROCESSOR_FALSE@ src/common/dwarf/elf_reader.o \ +@DISABLE_PROCESSOR_FALSE@ $(TEST_LIBS) \ +@DISABLE_PROCESSOR_FALSE@ $(PTHREAD_CFLAGS) $(PTHREAD_LIBS) + +@DISABLE_PROCESSOR_FALSE@noinst_SCRIPTS = $(check_SCRIPTS) +@DISABLE_PROCESSOR_FALSE@src_processor_minidump_dump_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump_dump.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_minidump_dump_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/common/path_helper.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_code_modules.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/convert_old_arm64_context.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_context.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_object.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/proc_maps_linux.o + +@DISABLE_PROCESSOR_FALSE@src_processor_microdump_stackwalk_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/microdump_stackwalk.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_microdump_stackwalk_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/common/path_helper.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_code_modules.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_source_line_resolver.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/call_stack.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/convert_old_arm64_context.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/cfi_frame_info.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_context.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_object.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/microdump.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/microdump_processor.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/process_state.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/simple_symbol_supplier.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/source_line_resolver_base.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stack_frame_cpu.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stack_frame_symbolizer.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalk_common.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_address_list.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_amd64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/tokenize.o \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/libdisasm.a + +@DISABLE_PROCESSOR_FALSE@src_processor_minidump_stackwalk_SOURCES = \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump_stackwalk.cc + +@DISABLE_PROCESSOR_FALSE@src_processor_minidump_stackwalk_LDADD = \ +@DISABLE_PROCESSOR_FALSE@ src/common/path_helper.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_code_modules.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/basic_source_line_resolver.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/call_stack.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/cfi_frame_info.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/convert_old_arm64_context.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/disassembler_x86.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_context.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/dump_object.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_linux.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/exploitability_win.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/logging.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/minidump_processor.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/pathname_stripper.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/process_state.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/proc_maps_linux.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/simple_symbol_supplier.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/source_line_resolver_base.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stack_frame_cpu.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stack_frame_symbolizer.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalk_common.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_address_list.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_amd64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_arm64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_mips.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_ppc64.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_sparc.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/stackwalker_x86.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/symbolic_constants_win.o \ +@DISABLE_PROCESSOR_FALSE@ src/processor/tokenize.o \ +@DISABLE_PROCESSOR_FALSE@ src/third_party/libdisasm/libdisasm.a + +EXTRA_DIST = \ + $(SCRIPTS) \ + src/client/linux/data/linux-gate-amd.sym \ + src/client/linux/data/linux-gate-intel.sym \ + src/client/mac/handler/breakpad_nlist_64.cc \ + src/client/mac/handler/breakpad_nlist_64.h \ + src/client/mac/handler/dynamic_images.cc \ + src/client/mac/handler/dynamic_images.h \ + src/client/mac/handler/exception_handler.cc \ + src/client/mac/handler/exception_handler.h \ + src/client/mac/handler/mach_vm_compat.h \ + src/client/mac/handler/minidump_generator.cc \ + src/client/mac/handler/minidump_generator.h \ + src/client/mac/handler/minidump_test.xcodeproj/project.pbxproj \ + src/client/mac/handler/minidump_tests32-Info.plist \ + src/client/mac/handler/minidump_tests64-Info.plist \ + src/client/mac/handler/obj-cTestCases-Info.plist \ + src/client/mac/handler/protected_memory_allocator.cc \ + src/client/mac/handler/protected_memory_allocator.h \ + src/client/mac/handler/ucontext_compat.h \ + src/client/mac/handler/testcases/testdata/dump_syms_i386_breakpad.sym \ + src/client/mac/tests/BreakpadFramework_Test.mm \ + src/client/mac/tests/crash_generation_server_test.cc \ + src/client/mac/tests/exception_handler_test.cc \ + src/client/mac/tests/minidump_generator_test.cc \ + src/client/mac/tests/minidump_generator_test_helper.cc \ + src/client/mac/tests/spawn_child_process.h \ + src/client/mac/tests/testlogging.h \ + src/client/minidump_file_writer_unittest.cc \ + src/client/solaris/handler/Makefile \ + src/client/solaris/handler/exception_handler.cc \ + src/client/solaris/handler/exception_handler.h \ + src/client/solaris/handler/exception_handler_test.cc \ + src/client/solaris/handler/minidump_generator.cc \ + src/client/solaris/handler/minidump_generator.h \ + src/client/solaris/handler/minidump_test.cc \ + src/client/solaris/handler/solaris_lwp.cc \ + src/client/solaris/handler/solaris_lwp.h \ + src/client/windows/breakpad_client.gyp \ + src/client/windows/handler/exception_handler.cc \ + src/client/windows/handler/exception_handler.h \ + src/client/windows/handler/exception_handler.gyp \ + src/client/windows/sender/crash_report_sender.cc \ + src/client/windows/sender/crash_report_sender.h \ + src/client/windows/sender/crash_report_sender.gyp \ + src/common/dwarf/dwarf2diehandler.h \ + src/common/dwarf/dwarf2enums.h \ + src/common/dwarf/line_state_machine.h \ + src/common/dwarf/types.h \ + src/common/mac/arch_utilities.h \ + src/common/mac/byteswap.h \ + src/common/mac/HTTPMultipartUpload.h \ + src/common/mac/HTTPMultipartUpload.m \ + src/common/mac/string_utilities.cc \ + src/common/mac/string_utilities.h \ + src/common/mac/super_fat_arch.h \ + src/common/scoped_ptr.h \ + src/common/solaris/dump_symbols.cc \ + src/common/solaris/dump_symbols.h \ + src/common/solaris/file_id.cc \ + src/common/solaris/file_id.h \ + src/common/solaris/guid_creator.cc \ + src/common/solaris/guid_creator.h \ + src/common/solaris/message_output.h \ + src/common/windows/guid_string.cc \ + src/common/windows/guid_string.h \ + src/common/windows/http_upload.cc \ + src/common/windows/http_upload.h \ + src/common/windows/pdb_source_line_writer.cc \ + src/common/windows/pdb_source_line_writer.h \ + src/common/windows/string_utils-inl.h \ + src/common/windows/string_utils.cc \ + src/processor/microdump_stackwalk_test_vars \ + src/processor/stackwalk_common.cc \ + src/processor/stackwalk_common.h \ + src/processor/stackwalker_selftest_sol.s \ + src/processor/testdata/ascii_read_av_block_write.dmp \ + src/processor/testdata/ascii_read_av_clobber_write.dmp \ + src/processor/testdata/ascii_read_av_conditional.dmp \ + src/processor/testdata/ascii_read_av.dmp \ + src/processor/testdata/ascii_read_av_then_jmp.dmp \ + src/processor/testdata/ascii_read_av_xchg_write.dmp \ + src/processor/testdata/ascii_write_av_arg_to_call.dmp \ + src/processor/testdata/ascii_write_av.dmp \ + src/processor/testdata/exec_av_on_stack.dmp \ + src/processor/testdata/linux_divide_by_zero.dmp \ + src/processor/testdata/linux_executable_heap.dmp \ + src/processor/testdata/linux_executable_stack.dmp \ + src/processor/testdata/linux_inside_module_exe_region1.dmp \ + src/processor/testdata/linux_inside_module_exe_region2.dmp \ + src/processor/testdata/linux_jmp_to_0.dmp \ + src/processor/testdata/linux_jmp_to_module_not_exe_region.dmp \ + src/processor/testdata/linux_null_dereference.dmp \ + src/processor/testdata/linux_null_read_av.dmp \ + src/processor/testdata/linux_outside_module.dmp \ + src/processor/testdata/linux_overflow.dmp \ + src/processor/testdata/linux_raise_sigabrt.dmp \ + src/processor/testdata/linux_stack_pointer_in_module.dmp \ + src/processor/testdata/linux_stack_pointer_in_stack.dmp \ + src/processor/testdata/linux_stack_pointer_in_stack_alt_name.dmp \ + src/processor/testdata/linux_stacksmash.dmp \ + src/processor/testdata/linux_write_to_nonwritable_module.dmp \ + src/processor/testdata/linux_write_to_nonwritable_region_math.dmp \ + src/processor/testdata/linux_write_to_outside_module.dmp \ + src/processor/testdata/linux_write_to_outside_module_via_math.dmp \ + src/processor/testdata/linux_write_to_under_4k.dmp \ + src/processor/testdata/microdump-arm64.dmp \ + src/processor/testdata/microdump-arm.dmp \ + src/processor/testdata/microdump-mips32.dmp \ + src/processor/testdata/microdump-mips64.dmp \ + src/processor/testdata/microdump-multiple.dmp \ + src/processor/testdata/microdump.stackwalk-arm64.out \ + src/processor/testdata/microdump.stackwalk-arm.out \ + src/processor/testdata/microdump.stackwalk.machine_readable-arm64.out \ + src/processor/testdata/microdump.stackwalk.machine_readable-arm.out \ + src/processor/testdata/microdump-withcrashreason.dmp \ + src/processor/testdata/microdump-x86.dmp \ + src/processor/testdata/minidump_32bit_crash_addr.dmp \ + src/processor/testdata/minidump2.dmp \ + src/processor/testdata/minidump2.dump.out \ + src/processor/testdata/minidump2.stackwalk.machine_readable.out \ + src/processor/testdata/minidump2.stackwalk.out \ + src/processor/testdata/module0.out \ + src/processor/testdata/module1.out \ + src/processor/testdata/module2.out \ + src/processor/testdata/module3_bad.out \ + src/processor/testdata/module4_bad.out \ + src/processor/testdata/null_read_av.dmp \ + src/processor/testdata/null_write_av.dmp \ + src/processor/testdata/read_av_clobber_write.dmp \ + src/processor/testdata/read_av_conditional.dmp \ + src/processor/testdata/read_av_non_null.dmp \ + src/processor/testdata/stack_exhaustion.dmp \ + src/processor/testdata/write_av_non_null.dmp \ + src/processor/testdata/symbols/kernel32.pdb/BCE8785C57B44245A669896B6A19B9542/kernel32.sym \ + src/processor/testdata/symbols/ld-2.13.so/C32AD7E235EA6112E02A5B9D6219C4850/ld-2.13.so.sym \ + src/processor/testdata/symbols/libc-2.13.so/F4F8DFCD5A5FB5A7CE64717E9E6AE3890/libc-2.13.so.sym \ + src/processor/testdata/symbols/libgcc_s.so.1/18B180F90887D8F8B5C35D185444AF4C0/libgcc_s.so.1.sym \ + src/processor/testdata/symbols/microdump/breakpad_unittests/D6D1FEC9A15DE7F38A236898871A2E770/breakpad_unittests.sym \ + src/processor/testdata/symbols/microdump/breakpad_unittests/DA7778FB66018A4E9B4110ED06E730D00/breakpad_unittests.sym \ + src/processor/testdata/symbols/microdump/crash_example/6E72E2F1A5F59AB3D51356FDFE394D490/crash_example.sym \ + src/processor/testdata/symbols/microdump/crash_example/8F36148CC4647A8116CAF2A25F591F570/crash_example.sym \ + src/processor/testdata/symbols/null_read_av/7B7D1968FF0D47AE4366E9C3A7E1B6750/null_read_av.sym \ + src/processor/testdata/symbols/overflow/B0E1FC01EF48E39CAF5C881D2DF0C3840/overflow.sym \ + src/processor/testdata/symbols/test_app.pdb/5A9832E5287241C1838ED98914E9B7FF1/test_app.sym \ + src/processor/testdata/test_app.cc \ + src/testing/googletest/include/gtest/gtest.h \ + src/testing/googletest/include/gtest/gtest-death-test.h \ + src/testing/googletest/include/gtest/gtest-matchers.h \ + src/testing/googletest/include/gtest/gtest-message.h \ + src/testing/googletest/include/gtest/gtest-param-test.h \ + src/testing/googletest/include/gtest/gtest-printers.h \ + src/testing/googletest/include/gtest/gtest-spi.h \ + src/testing/googletest/include/gtest/gtest-test-part.h \ + src/testing/googletest/include/gtest/gtest-typed-test.h \ + src/testing/googletest/include/gtest/gtest_pred_impl.h \ + src/testing/googletest/include/gtest/gtest_prod.h \ + src/testing/googletest/include/gtest/internal/custom/gtest-port.h \ + src/testing/googletest/include/gtest/internal/custom/gtest-printers.h \ + src/testing/googletest/include/gtest/internal/custom/gtest.h \ + src/testing/googletest/include/gtest/internal/gtest-death-test-internal.h \ + src/testing/googletest/include/gtest/internal/gtest-filepath.h \ + src/testing/googletest/include/gtest/internal/gtest-internal.h \ + src/testing/googletest/include/gtest/internal/gtest-param-util-generated.h \ + src/testing/googletest/include/gtest/internal/gtest-param-util.h \ + src/testing/googletest/include/gtest/internal/gtest-port-arch.h \ + src/testing/googletest/include/gtest/internal/gtest-port.h \ + src/testing/googletest/include/gtest/internal/gtest-string.h \ + src/testing/googletest/include/gtest/internal/gtest-type-util.h \ + src/testing/googletest/src/gtest.cc \ + src/testing/googletest/src/gtest-death-test.cc \ + src/testing/googletest/src/gtest-filepath.cc \ + src/testing/googletest/src/gtest-internal-inl.h \ + src/testing/googletest/src/gtest-matchers.cc \ + src/testing/googletest/src/gtest-port.cc \ + src/testing/googletest/src/gtest-printers.cc \ + src/testing/googletest/src/gtest-test-part.cc \ + src/testing/googletest/src/gtest-typed-test.cc \ + src/testing/googlemock/include/gmock/gmock.h \ + src/testing/googlemock/include/gmock/gmock-actions.h \ + src/testing/googlemock/include/gmock/gmock-cardinalities.h \ + src/testing/googlemock/include/gmock/gmock-function-mocker.h \ + src/testing/googlemock/include/gmock/gmock-generated-actions.h \ + src/testing/googlemock/include/gmock/gmock-generated-function-mockers.h \ + src/testing/googlemock/include/gmock/gmock-generated-matchers.h \ + src/testing/googlemock/include/gmock/gmock-matchers.h \ + src/testing/googlemock/include/gmock/gmock-more-actions.h \ + src/testing/googlemock/include/gmock/gmock-more-matchers.h \ + src/testing/googlemock/include/gmock/gmock-nice-strict.h \ + src/testing/googlemock/include/gmock/gmock-spec-builders.h \ + src/testing/googlemock/include/gmock/internal/custom/gmock-generated-actions.h \ + src/testing/googlemock/include/gmock/internal/custom/gmock-matchers.h \ + src/testing/googlemock/include/gmock/internal/custom/gmock-port.h \ + src/testing/googlemock/include/gmock/internal/gmock-internal-utils.h \ + src/testing/googlemock/include/gmock/internal/gmock-port.h \ + src/testing/googlemock/include/gmock/internal/gmock-pp.h \ + src/testing/googlemock/src/gmock.cc \ + src/testing/googlemock/src/gmock-cardinalities.cc \ + src/testing/googlemock/src/gmock-internal-utils.cc \ + src/testing/googlemock/src/gmock-matchers.cc \ + src/testing/googlemock/src/gmock-spec-builders.cc \ + src/testing/googlemock/src/gmock_main.cc \ + src/third_party/curl/COPYING \ + src/third_party/curl/curlbuild.h \ + src/third_party/curl/curl.h \ + src/third_party/curl/curlrules.h \ + src/third_party/curl/curlver.h \ + src/third_party/curl/easy.h \ + src/third_party/curl/mprintf.h \ + src/third_party/curl/multi.h \ + src/third_party/curl/stdcheaders.h \ + src/third_party/curl/typecheck-gcc.h \ + src/third_party/curl/types.h \ + src/third_party/mac_headers/architecture/byte_order.h \ + src/third_party/mac_headers/i386/_types.h \ + src/third_party/mac_headers/mach/boolean.h \ + src/third_party/mac_headers/mach/i386/boolean.h \ + src/third_party/mac_headers/mach/i386/vm_param.h \ + src/third_party/mac_headers/mach/i386/vm_types.h \ + src/third_party/mac_headers/mach/machine/boolean.h \ + src/third_party/mac_headers/mach/machine.h \ + src/third_party/mac_headers/mach/machine/thread_state.h \ + src/third_party/mac_headers/mach/machine/thread_status.h \ + src/third_party/mac_headers/mach/machine/vm_types.h \ + src/third_party/mac_headers/mach-o/arch.h \ + src/third_party/mac_headers/mach-o/fat.h \ + src/third_party/mac_headers/mach-o/loader.h \ + src/third_party/mac_headers/mach-o/nlist.h \ + src/third_party/mac_headers/mach/thread_status.h \ + src/third_party/mac_headers/mach/vm_prot.h \ + src/third_party/mac_headers/README \ + src/third_party/musl/README \ + src/third_party/musl/COPYRIGHT \ + src/third_party/musl/README.breakpad \ + src/third_party/musl/VERSION \ + src/third_party/musl/include/elf.h \ + src/tools/mac/crash_report/crash_report.mm \ + src/tools/mac/crash_report/crash_report.xcodeproj/project.pbxproj \ + src/tools/mac/crash_report/on_demand_symbol_supplier.h \ + src/tools/mac/crash_report/on_demand_symbol_supplier.mm \ + src/tools/mac/dump_syms/dump_syms.xcodeproj/project.pbxproj \ + src/tools/mac/dump_syms/dump_syms_tool.cc \ + src/tools/mac/symupload/minidump_upload.m \ + src/tools/mac/symupload/symupload.m \ + src/tools/mac/symupload/symupload.xcodeproj/project.pbxproj \ + src/tools/solaris/dump_syms/Makefile \ + src/tools/solaris/dump_syms/dump_syms.cc \ + src/tools/solaris/dump_syms/run_regtest.sh \ + src/tools/solaris/dump_syms/testdata/dump_syms_regtest.cc \ + src/tools/solaris/dump_syms/testdata/dump_syms_regtest.o \ + src/tools/solaris/dump_syms/testdata/dump_syms_regtest.stabs \ + src/tools/solaris/dump_syms/testdata/dump_syms_regtest.sym \ + src/tools/windows/converter/ms_symbol_server_converter.cc \ + src/tools/windows/converter/ms_symbol_server_converter.h \ + src/tools/windows/converter/ms_symbol_server_converter.gyp \ + src/tools/windows/dump_syms/dump_syms.cc \ + src/tools/windows/dump_syms/dump_syms.gyp \ + src/tools/windows/dump_syms/run_regtest.sh \ + src/tools/windows/dump_syms/testdata/dump_syms_regtest.cc \ + src/tools/windows/dump_syms/testdata/dump_syms_regtest.pdb \ + src/tools/windows/dump_syms/testdata/dump_syms_regtest.sym \ + src/tools/windows/dump_syms/testdata/dump_syms_regtest64.sym \ + src/tools/windows/dump_syms/testdata/omap_reorder_bbs.sym \ + src/tools/windows/dump_syms/testdata/omap_reorder_funcs.sym \ + src/tools/windows/dump_syms/testdata/omap_stretched.sym \ + src/tools/windows/dump_syms/testdata/omap_stretched_filled.sym \ + src/tools/windows/symupload/symupload.cc \ + src/tools/windows/symupload/symupload.gyp + +all: all-am + +.SUFFIXES: +.SUFFIXES: .S .c .cc .log .o .obj .test .test$(EXEEXT) .trs +am--refresh: Makefile + @: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps) + @for dep in $?; do \ + case '$(am__configure_deps)' in \ + *$$dep*) \ + echo ' cd $(srcdir) && $(AUTOMAKE) --foreign'; \ + $(am__cd) $(srcdir) && $(AUTOMAKE) --foreign \ + && exit 0; \ + exit 1;; \ + esac; \ + done; \ + echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign Makefile'; \ + $(am__cd) $(top_srcdir) && \ + $(AUTOMAKE) --foreign Makefile +Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status + @case '$?' in \ + *config.status*) \ + echo ' $(SHELL) ./config.status'; \ + $(SHELL) ./config.status;; \ + *) \ + echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles)'; \ + cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles);; \ + esac; + +$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) + $(SHELL) ./config.status --recheck + +$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + $(am__cd) $(srcdir) && $(AUTOCONF) +$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps) + $(am__cd) $(srcdir) && $(ACLOCAL) $(ACLOCAL_AMFLAGS) +$(am__aclocal_m4_deps): + +src/config.h: src/stamp-h1 + @test -f $@ || rm -f src/stamp-h1 + @test -f $@ || $(MAKE) $(AM_MAKEFLAGS) src/stamp-h1 + +src/stamp-h1: $(top_srcdir)/src/config.h.in $(top_builddir)/config.status + @rm -f src/stamp-h1 + cd $(top_builddir) && $(SHELL) ./config.status src/config.h +$(top_srcdir)/src/config.h.in: @MAINTAINER_MODE_TRUE@ $(am__configure_deps) + ($(am__cd) $(top_srcdir) && $(AUTOHEADER)) + rm -f src/stamp-h1 + touch $@ + +distclean-hdr: + -rm -f src/config.h src/stamp-h1 +breakpad.pc: $(top_builddir)/config.status $(srcdir)/breakpad.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $@ +breakpad-client.pc: $(top_builddir)/config.status $(srcdir)/breakpad-client.pc.in + cd $(top_builddir) && $(SHELL) ./config.status $@ +install-binPROGRAMS: $(bin_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(bindir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-binPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(bindir)" && rm -f $$files + +clean-binPROGRAMS: + -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS) + +clean-checkPROGRAMS: + -test -z "$(check_PROGRAMS)" || rm -f $(check_PROGRAMS) +install-libexecPROGRAMS: $(libexec_PROGRAMS) + @$(NORMAL_INSTALL) + @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(libexecdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libexecdir)" || exit 1; \ + fi; \ + for p in $$list; do echo "$$p $$p"; done | \ + sed 's/$(EXEEXT)$$//' | \ + while read p p1; do if test -f $$p \ + ; then echo "$$p"; echo "$$p"; else :; fi; \ + done | \ + sed -e 'p;s,.*/,,;n;h' \ + -e 's|.*|.|' \ + -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ + sed 'N;N;N;s,\n, ,g' | \ + $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ + { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ + if ($$2 == $$4) files[d] = files[d] " " $$1; \ + else { print "f", $$3 "/" $$4, $$1; } } \ + END { for (d in files) print "f", d, files[d] }' | \ + while read type dir files; do \ + if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \ + test -z "$$files" || { \ + echo " $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(libexecdir)$$dir'"; \ + $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(libexecdir)$$dir" || exit $$?; \ + } \ + ; done + +uninstall-libexecPROGRAMS: + @$(NORMAL_UNINSTALL) + @list='$(libexec_PROGRAMS)'; test -n "$(libexecdir)" || list=; \ + files=`for p in $$list; do echo "$$p"; done | \ + sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ + -e 's/$$/$(EXEEXT)/' \ + `; \ + test -n "$$list" || exit 0; \ + echo " ( cd '$(DESTDIR)$(libexecdir)' && rm -f" $$files ")"; \ + cd "$(DESTDIR)$(libexecdir)" && rm -f $$files + +clean-libexecPROGRAMS: + -test -z "$(libexec_PROGRAMS)" || rm -f $(libexec_PROGRAMS) + +clean-noinstPROGRAMS: + -test -z "$(noinst_PROGRAMS)" || rm -f $(noinst_PROGRAMS) + +clean-checkLIBRARIES: + -test -z "$(check_LIBRARIES)" || rm -f $(check_LIBRARIES) +install-libLIBRARIES: $(lib_LIBRARIES) + @$(NORMAL_INSTALL) + @list='$(lib_LIBRARIES)'; test -n "$(libdir)" || list=; \ + list2=; for p in $$list; do \ + if test -f $$p; then \ + list2="$$list2 $$p"; \ + else :; fi; \ + done; \ + test -z "$$list2" || { \ + echo " $(MKDIR_P) '$(DESTDIR)$(libdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(libdir)" || exit 1; \ + echo " $(INSTALL_DATA) $$list2 '$(DESTDIR)$(libdir)'"; \ + $(INSTALL_DATA) $$list2 "$(DESTDIR)$(libdir)" || exit $$?; } + @$(POST_INSTALL) + @list='$(lib_LIBRARIES)'; test -n "$(libdir)" || list=; \ + for p in $$list; do \ + if test -f $$p; then \ + $(am__strip_dir) \ + echo " ( cd '$(DESTDIR)$(libdir)' && $(RANLIB) $$f )"; \ + ( cd "$(DESTDIR)$(libdir)" && $(RANLIB) $$f ) || exit $$?; \ + else :; fi; \ + done + +uninstall-libLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LIBRARIES)'; test -n "$(libdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(libdir)'; $(am__uninstall_files_from_dir) + +clean-libLIBRARIES: + -test -z "$(lib_LIBRARIES)" || rm -f $(lib_LIBRARIES) + +clean-noinstLIBRARIES: + -test -z "$(noinst_LIBRARIES)" || rm -f $(noinst_LIBRARIES) +src/client/linux/crash_generation/$(am__dirstamp): + @$(MKDIR_P) src/client/linux/crash_generation + @: > src/client/linux/crash_generation/$(am__dirstamp) +src/client/linux/crash_generation/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/client/linux/crash_generation/$(DEPDIR) + @: > src/client/linux/crash_generation/$(DEPDIR)/$(am__dirstamp) +src/client/linux/crash_generation/crash_generation_client.$(OBJEXT): \ + src/client/linux/crash_generation/$(am__dirstamp) \ + src/client/linux/crash_generation/$(DEPDIR)/$(am__dirstamp) +src/client/linux/crash_generation/crash_generation_server.$(OBJEXT): \ + src/client/linux/crash_generation/$(am__dirstamp) \ + src/client/linux/crash_generation/$(DEPDIR)/$(am__dirstamp) +src/client/linux/dump_writer_common/$(am__dirstamp): + @$(MKDIR_P) src/client/linux/dump_writer_common + @: > src/client/linux/dump_writer_common/$(am__dirstamp) +src/client/linux/dump_writer_common/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/client/linux/dump_writer_common/$(DEPDIR) + @: > src/client/linux/dump_writer_common/$(DEPDIR)/$(am__dirstamp) +src/client/linux/dump_writer_common/thread_info.$(OBJEXT): \ + src/client/linux/dump_writer_common/$(am__dirstamp) \ + src/client/linux/dump_writer_common/$(DEPDIR)/$(am__dirstamp) +src/client/linux/dump_writer_common/ucontext_reader.$(OBJEXT): \ + src/client/linux/dump_writer_common/$(am__dirstamp) \ + src/client/linux/dump_writer_common/$(DEPDIR)/$(am__dirstamp) +src/client/linux/handler/$(am__dirstamp): + @$(MKDIR_P) src/client/linux/handler + @: > src/client/linux/handler/$(am__dirstamp) +src/client/linux/handler/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/client/linux/handler/$(DEPDIR) + @: > src/client/linux/handler/$(DEPDIR)/$(am__dirstamp) +src/client/linux/handler/exception_handler.$(OBJEXT): \ + src/client/linux/handler/$(am__dirstamp) \ + src/client/linux/handler/$(DEPDIR)/$(am__dirstamp) +src/client/linux/handler/minidump_descriptor.$(OBJEXT): \ + src/client/linux/handler/$(am__dirstamp) \ + src/client/linux/handler/$(DEPDIR)/$(am__dirstamp) +src/client/linux/log/$(am__dirstamp): + @$(MKDIR_P) src/client/linux/log + @: > src/client/linux/log/$(am__dirstamp) +src/client/linux/log/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/client/linux/log/$(DEPDIR) + @: > src/client/linux/log/$(DEPDIR)/$(am__dirstamp) +src/client/linux/log/log.$(OBJEXT): \ + src/client/linux/log/$(am__dirstamp) \ + src/client/linux/log/$(DEPDIR)/$(am__dirstamp) +src/client/linux/microdump_writer/$(am__dirstamp): + @$(MKDIR_P) src/client/linux/microdump_writer + @: > src/client/linux/microdump_writer/$(am__dirstamp) +src/client/linux/microdump_writer/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/client/linux/microdump_writer/$(DEPDIR) + @: > src/client/linux/microdump_writer/$(DEPDIR)/$(am__dirstamp) +src/client/linux/microdump_writer/microdump_writer.$(OBJEXT): \ + src/client/linux/microdump_writer/$(am__dirstamp) \ + src/client/linux/microdump_writer/$(DEPDIR)/$(am__dirstamp) +src/client/linux/minidump_writer/$(am__dirstamp): + @$(MKDIR_P) src/client/linux/minidump_writer + @: > src/client/linux/minidump_writer/$(am__dirstamp) +src/client/linux/minidump_writer/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/client/linux/minidump_writer/$(DEPDIR) + @: > src/client/linux/minidump_writer/$(DEPDIR)/$(am__dirstamp) +src/client/linux/minidump_writer/linux_core_dumper.$(OBJEXT): \ + src/client/linux/minidump_writer/$(am__dirstamp) \ + src/client/linux/minidump_writer/$(DEPDIR)/$(am__dirstamp) +src/client/linux/minidump_writer/linux_dumper.$(OBJEXT): \ + src/client/linux/minidump_writer/$(am__dirstamp) \ + src/client/linux/minidump_writer/$(DEPDIR)/$(am__dirstamp) +src/client/linux/minidump_writer/linux_ptrace_dumper.$(OBJEXT): \ + src/client/linux/minidump_writer/$(am__dirstamp) \ + src/client/linux/minidump_writer/$(DEPDIR)/$(am__dirstamp) +src/client/linux/minidump_writer/minidump_writer.$(OBJEXT): \ + src/client/linux/minidump_writer/$(am__dirstamp) \ + src/client/linux/minidump_writer/$(DEPDIR)/$(am__dirstamp) +src/client/$(am__dirstamp): + @$(MKDIR_P) src/client + @: > src/client/$(am__dirstamp) +src/client/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/client/$(DEPDIR) + @: > src/client/$(DEPDIR)/$(am__dirstamp) +src/client/minidump_file_writer.$(OBJEXT): src/client/$(am__dirstamp) \ + src/client/$(DEPDIR)/$(am__dirstamp) +src/common/$(am__dirstamp): + @$(MKDIR_P) src/common + @: > src/common/$(am__dirstamp) +src/common/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/common/$(DEPDIR) + @: > src/common/$(DEPDIR)/$(am__dirstamp) +src/common/convert_UTF.$(OBJEXT): src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/md5.$(OBJEXT): src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/string_conversion.$(OBJEXT): src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/linux/$(am__dirstamp): + @$(MKDIR_P) src/common/linux + @: > src/common/linux/$(am__dirstamp) +src/common/linux/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/common/linux/$(DEPDIR) + @: > src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/elf_core_dump.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/elfutils.$(OBJEXT): src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/file_id.$(OBJEXT): src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/guid_creator.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/linux_libc_support.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/memory_mapped_file.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/safe_readlink.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/breakpad_getcontext.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/client/linux/$(am__dirstamp): + @$(MKDIR_P) src/client/linux + @: > src/client/linux/$(am__dirstamp) + +src/client/linux/libbreakpad_client.a: $(src_client_linux_libbreakpad_client_a_OBJECTS) $(src_client_linux_libbreakpad_client_a_DEPENDENCIES) $(EXTRA_src_client_linux_libbreakpad_client_a_DEPENDENCIES) src/client/linux/$(am__dirstamp) + $(AM_V_at)-rm -f src/client/linux/libbreakpad_client.a + $(AM_V_AR)$(src_client_linux_libbreakpad_client_a_AR) src/client/linux/libbreakpad_client.a $(src_client_linux_libbreakpad_client_a_OBJECTS) $(src_client_linux_libbreakpad_client_a_LIBADD) + $(AM_V_at)$(RANLIB) src/client/linux/libbreakpad_client.a +src/processor/$(am__dirstamp): + @$(MKDIR_P) src/processor + @: > src/processor/$(am__dirstamp) +src/processor/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/processor/$(DEPDIR) + @: > src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/basic_code_modules.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/basic_source_line_resolver.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/call_stack.$(OBJEXT): src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/cfi_frame_info.$(OBJEXT): src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/convert_old_arm64_context.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/disassembler_x86.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/dump_context.$(OBJEXT): src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/dump_object.$(OBJEXT): src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/exploitability.$(OBJEXT): src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/exploitability_linux.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/exploitability_win.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/fast_source_line_resolver.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/logging.$(OBJEXT): src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/microdump.$(OBJEXT): src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/microdump_processor.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/minidump.$(OBJEXT): src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/minidump_processor.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/module_comparer.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/module_serializer.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/pathname_stripper.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/process_state.$(OBJEXT): src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/proc_maps_linux.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/simple_symbol_supplier.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/source_line_resolver_base.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/stack_frame_cpu.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/stack_frame_symbolizer.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/stackwalk_common.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/stackwalker.$(OBJEXT): src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/stackwalker_amd64.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/stackwalker_arm.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/stackwalker_arm64.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/stackwalker_address_list.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/stackwalker_mips.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/stackwalker_ppc.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/stackwalker_ppc64.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/stackwalker_sparc.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/stackwalker_x86.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/symbolic_constants_win.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/tokenize.$(OBJEXT): src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/$(am__dirstamp): + @$(MKDIR_P) src + @: > src/$(am__dirstamp) + +src/libbreakpad.a: $(src_libbreakpad_a_OBJECTS) $(src_libbreakpad_a_DEPENDENCIES) $(EXTRA_src_libbreakpad_a_DEPENDENCIES) src/$(am__dirstamp) + $(AM_V_at)-rm -f src/libbreakpad.a + $(AM_V_AR)$(src_libbreakpad_a_AR) src/libbreakpad.a $(src_libbreakpad_a_OBJECTS) $(src_libbreakpad_a_LIBADD) + $(AM_V_at)$(RANLIB) src/libbreakpad.a +src/testing/googletest/src/$(am__dirstamp): + @$(MKDIR_P) src/testing/googletest/src + @: > src/testing/googletest/src/$(am__dirstamp) +src/testing/googletest/src/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/testing/googletest/src/$(DEPDIR) + @: > src/testing/googletest/src/$(DEPDIR)/$(am__dirstamp) +src/testing/googletest/src/libtesting_a-gtest-all.$(OBJEXT): \ + src/testing/googletest/src/$(am__dirstamp) \ + src/testing/googletest/src/$(DEPDIR)/$(am__dirstamp) +src/testing/googletest/src/libtesting_a-gtest_main.$(OBJEXT): \ + src/testing/googletest/src/$(am__dirstamp) \ + src/testing/googletest/src/$(DEPDIR)/$(am__dirstamp) +src/testing/googlemock/src/$(am__dirstamp): + @$(MKDIR_P) src/testing/googlemock/src + @: > src/testing/googlemock/src/$(am__dirstamp) +src/testing/googlemock/src/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/testing/googlemock/src/$(DEPDIR) + @: > src/testing/googlemock/src/$(DEPDIR)/$(am__dirstamp) +src/testing/googlemock/src/libtesting_a-gmock-all.$(OBJEXT): \ + src/testing/googlemock/src/$(am__dirstamp) \ + src/testing/googlemock/src/$(DEPDIR)/$(am__dirstamp) +src/testing/$(am__dirstamp): + @$(MKDIR_P) src/testing + @: > src/testing/$(am__dirstamp) + +src/testing/libtesting.a: $(src_testing_libtesting_a_OBJECTS) $(src_testing_libtesting_a_DEPENDENCIES) $(EXTRA_src_testing_libtesting_a_DEPENDENCIES) src/testing/$(am__dirstamp) + $(AM_V_at)-rm -f src/testing/libtesting.a + $(AM_V_AR)$(src_testing_libtesting_a_AR) src/testing/libtesting.a $(src_testing_libtesting_a_OBJECTS) $(src_testing_libtesting_a_LIBADD) + $(AM_V_at)$(RANLIB) src/testing/libtesting.a +src/third_party/libdisasm/$(am__dirstamp): + @$(MKDIR_P) src/third_party/libdisasm + @: > src/third_party/libdisasm/$(am__dirstamp) +src/third_party/libdisasm/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/third_party/libdisasm/$(DEPDIR) + @: > src/third_party/libdisasm/$(DEPDIR)/$(am__dirstamp) +src/third_party/libdisasm/ia32_implicit.$(OBJEXT): \ + src/third_party/libdisasm/$(am__dirstamp) \ + src/third_party/libdisasm/$(DEPDIR)/$(am__dirstamp) +src/third_party/libdisasm/ia32_insn.$(OBJEXT): \ + src/third_party/libdisasm/$(am__dirstamp) \ + src/third_party/libdisasm/$(DEPDIR)/$(am__dirstamp) +src/third_party/libdisasm/ia32_invariant.$(OBJEXT): \ + src/third_party/libdisasm/$(am__dirstamp) \ + src/third_party/libdisasm/$(DEPDIR)/$(am__dirstamp) +src/third_party/libdisasm/ia32_modrm.$(OBJEXT): \ + src/third_party/libdisasm/$(am__dirstamp) \ + src/third_party/libdisasm/$(DEPDIR)/$(am__dirstamp) +src/third_party/libdisasm/ia32_opcode_tables.$(OBJEXT): \ + src/third_party/libdisasm/$(am__dirstamp) \ + src/third_party/libdisasm/$(DEPDIR)/$(am__dirstamp) +src/third_party/libdisasm/ia32_operand.$(OBJEXT): \ + src/third_party/libdisasm/$(am__dirstamp) \ + src/third_party/libdisasm/$(DEPDIR)/$(am__dirstamp) +src/third_party/libdisasm/ia32_reg.$(OBJEXT): \ + src/third_party/libdisasm/$(am__dirstamp) \ + src/third_party/libdisasm/$(DEPDIR)/$(am__dirstamp) +src/third_party/libdisasm/ia32_settings.$(OBJEXT): \ + src/third_party/libdisasm/$(am__dirstamp) \ + src/third_party/libdisasm/$(DEPDIR)/$(am__dirstamp) +src/third_party/libdisasm/x86_disasm.$(OBJEXT): \ + src/third_party/libdisasm/$(am__dirstamp) \ + src/third_party/libdisasm/$(DEPDIR)/$(am__dirstamp) +src/third_party/libdisasm/x86_format.$(OBJEXT): \ + src/third_party/libdisasm/$(am__dirstamp) \ + src/third_party/libdisasm/$(DEPDIR)/$(am__dirstamp) +src/third_party/libdisasm/x86_imm.$(OBJEXT): \ + src/third_party/libdisasm/$(am__dirstamp) \ + src/third_party/libdisasm/$(DEPDIR)/$(am__dirstamp) +src/third_party/libdisasm/x86_insn.$(OBJEXT): \ + src/third_party/libdisasm/$(am__dirstamp) \ + src/third_party/libdisasm/$(DEPDIR)/$(am__dirstamp) +src/third_party/libdisasm/x86_misc.$(OBJEXT): \ + src/third_party/libdisasm/$(am__dirstamp) \ + src/third_party/libdisasm/$(DEPDIR)/$(am__dirstamp) +src/third_party/libdisasm/x86_operand_list.$(OBJEXT): \ + src/third_party/libdisasm/$(am__dirstamp) \ + src/third_party/libdisasm/$(DEPDIR)/$(am__dirstamp) + +src/third_party/libdisasm/libdisasm.a: $(src_third_party_libdisasm_libdisasm_a_OBJECTS) $(src_third_party_libdisasm_libdisasm_a_DEPENDENCIES) $(EXTRA_src_third_party_libdisasm_libdisasm_a_DEPENDENCIES) src/third_party/libdisasm/$(am__dirstamp) + $(AM_V_at)-rm -f src/third_party/libdisasm/libdisasm.a + $(AM_V_AR)$(src_third_party_libdisasm_libdisasm_a_AR) src/third_party/libdisasm/libdisasm.a $(src_third_party_libdisasm_libdisasm_a_OBJECTS) $(src_third_party_libdisasm_libdisasm_a_LIBADD) + $(AM_V_at)$(RANLIB) src/third_party/libdisasm/libdisasm.a + +src/client/linux/linux_client_unittest$(EXEEXT): $(src_client_linux_linux_client_unittest_OBJECTS) $(src_client_linux_linux_client_unittest_DEPENDENCIES) $(EXTRA_src_client_linux_linux_client_unittest_DEPENDENCIES) src/client/linux/$(am__dirstamp) + @rm -f src/client/linux/linux_client_unittest$(EXEEXT) + $(AM_V_CCLD)$(src_client_linux_linux_client_unittest_LINK) $(src_client_linux_linux_client_unittest_OBJECTS) $(src_client_linux_linux_client_unittest_LDADD) $(LIBS) +src/testing/googletest/src/client_linux_linux_client_unittest_shlib-gtest-all.$(OBJEXT): \ + src/testing/googletest/src/$(am__dirstamp) \ + src/testing/googletest/src/$(DEPDIR)/$(am__dirstamp) +src/testing/googletest/src/client_linux_linux_client_unittest_shlib-gtest_main.$(OBJEXT): \ + src/testing/googletest/src/$(am__dirstamp) \ + src/testing/googletest/src/$(DEPDIR)/$(am__dirstamp) +src/testing/googlemock/src/client_linux_linux_client_unittest_shlib-gmock-all.$(OBJEXT): \ + src/testing/googlemock/src/$(am__dirstamp) \ + src/testing/googlemock/src/$(DEPDIR)/$(am__dirstamp) +src/client/linux/handler/linux_client_unittest_shlib-exception_handler_unittest.$(OBJEXT): \ + src/client/linux/handler/$(am__dirstamp) \ + src/client/linux/handler/$(DEPDIR)/$(am__dirstamp) +src/client/linux/microdump_writer/linux_client_unittest_shlib-microdump_writer_unittest.$(OBJEXT): \ + src/client/linux/microdump_writer/$(am__dirstamp) \ + src/client/linux/microdump_writer/$(DEPDIR)/$(am__dirstamp) +src/client/linux/minidump_writer/linux_client_unittest_shlib-directory_reader_unittest.$(OBJEXT): \ + src/client/linux/minidump_writer/$(am__dirstamp) \ + src/client/linux/minidump_writer/$(DEPDIR)/$(am__dirstamp) +src/client/linux/minidump_writer/linux_client_unittest_shlib-cpu_set_unittest.$(OBJEXT): \ + src/client/linux/minidump_writer/$(am__dirstamp) \ + src/client/linux/minidump_writer/$(DEPDIR)/$(am__dirstamp) +src/client/linux/minidump_writer/linux_client_unittest_shlib-line_reader_unittest.$(OBJEXT): \ + src/client/linux/minidump_writer/$(am__dirstamp) \ + src/client/linux/minidump_writer/$(DEPDIR)/$(am__dirstamp) +src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_core_dumper.$(OBJEXT): \ + src/client/linux/minidump_writer/$(am__dirstamp) \ + src/client/linux/minidump_writer/$(DEPDIR)/$(am__dirstamp) +src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_core_dumper_unittest.$(OBJEXT): \ + src/client/linux/minidump_writer/$(am__dirstamp) \ + src/client/linux/minidump_writer/$(DEPDIR)/$(am__dirstamp) +src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_ptrace_dumper_unittest.$(OBJEXT): \ + src/client/linux/minidump_writer/$(am__dirstamp) \ + src/client/linux/minidump_writer/$(DEPDIR)/$(am__dirstamp) +src/client/linux/minidump_writer/linux_client_unittest_shlib-minidump_writer_unittest.$(OBJEXT): \ + src/client/linux/minidump_writer/$(am__dirstamp) \ + src/client/linux/minidump_writer/$(DEPDIR)/$(am__dirstamp) +src/client/linux/minidump_writer/linux_client_unittest_shlib-minidump_writer_unittest_utils.$(OBJEXT): \ + src/client/linux/minidump_writer/$(am__dirstamp) \ + src/client/linux/minidump_writer/$(DEPDIR)/$(am__dirstamp) +src/client/linux/minidump_writer/linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.$(OBJEXT): \ + src/client/linux/minidump_writer/$(am__dirstamp) \ + src/client/linux/minidump_writer/$(DEPDIR)/$(am__dirstamp) +src/common/linux/client_linux_linux_client_unittest_shlib-elf_core_dump.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/client_linux_linux_client_unittest_shlib-linux_libc_support_unittest.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/tests/$(am__dirstamp): + @$(MKDIR_P) src/common/linux/tests + @: > src/common/linux/tests/$(am__dirstamp) +src/common/linux/tests/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/common/linux/tests/$(DEPDIR) + @: > src/common/linux/tests/$(DEPDIR)/$(am__dirstamp) +src/common/linux/tests/client_linux_linux_client_unittest_shlib-crash_generator.$(OBJEXT): \ + src/common/linux/tests/$(am__dirstamp) \ + src/common/linux/tests/$(DEPDIR)/$(am__dirstamp) +src/common/client_linux_linux_client_unittest_shlib-memory_allocator_unittest.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/tests/$(am__dirstamp): + @$(MKDIR_P) src/common/tests + @: > src/common/tests/$(am__dirstamp) +src/common/tests/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/common/tests/$(DEPDIR) + @: > src/common/tests/$(DEPDIR)/$(am__dirstamp) +src/common/tests/client_linux_linux_client_unittest_shlib-file_utils.$(OBJEXT): \ + src/common/tests/$(am__dirstamp) \ + src/common/tests/$(DEPDIR)/$(am__dirstamp) +src/processor/client_linux_linux_client_unittest_shlib-basic_code_modules.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/client_linux_linux_client_unittest_shlib-convert_old_arm64_context.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/client_linux_linux_client_unittest_shlib-dump_context.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/client_linux_linux_client_unittest_shlib-dump_object.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/client_linux_linux_client_unittest_shlib-logging.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/client_linux_linux_client_unittest_shlib-minidump.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/client_linux_linux_client_unittest_shlib-pathname_stripper.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/client_linux_linux_client_unittest_shlib-proc_maps_linux.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/common/linux/client_linux_linux_client_unittest_shlib-breakpad_getcontext.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/client_linux_linux_client_unittest_shlib-breakpad_getcontext_unittest.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) + +src/client/linux/linux_client_unittest_shlib$(EXEEXT): $(src_client_linux_linux_client_unittest_shlib_OBJECTS) $(src_client_linux_linux_client_unittest_shlib_DEPENDENCIES) $(EXTRA_src_client_linux_linux_client_unittest_shlib_DEPENDENCIES) src/client/linux/$(am__dirstamp) + @rm -f src/client/linux/linux_client_unittest_shlib$(EXEEXT) + $(AM_V_CXXLD)$(src_client_linux_linux_client_unittest_shlib_LINK) $(src_client_linux_linux_client_unittest_shlib_OBJECTS) $(src_client_linux_linux_client_unittest_shlib_LDADD) $(LIBS) +src/client/linux/minidump_writer/linux_dumper_unittest_helper-linux_dumper_unittest_helper.$(OBJEXT): \ + src/client/linux/minidump_writer/$(am__dirstamp) \ + src/client/linux/minidump_writer/$(DEPDIR)/$(am__dirstamp) + +src/client/linux/linux_dumper_unittest_helper$(EXEEXT): $(src_client_linux_linux_dumper_unittest_helper_OBJECTS) $(src_client_linux_linux_dumper_unittest_helper_DEPENDENCIES) $(EXTRA_src_client_linux_linux_dumper_unittest_helper_DEPENDENCIES) src/client/linux/$(am__dirstamp) + @rm -f src/client/linux/linux_dumper_unittest_helper$(EXEEXT) + $(AM_V_CXXLD)$(src_client_linux_linux_dumper_unittest_helper_LINK) $(src_client_linux_linux_dumper_unittest_helper_OBJECTS) $(src_client_linux_linux_dumper_unittest_helper_LDADD) $(LIBS) +src/common/dumper_unittest-byte_cursor_unittest.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/dumper_unittest-convert_UTF.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/dumper_unittest-dwarf_cfi_to_module.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/dumper_unittest-dwarf_cfi_to_module_unittest.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/dumper_unittest-dwarf_cu_to_module.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/dumper_unittest-dwarf_cu_to_module_unittest.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/dumper_unittest-dwarf_line_to_module.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/dumper_unittest-dwarf_line_to_module_unittest.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/dumper_unittest-dwarf_range_list_handler.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/dumper_unittest-language.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/dumper_unittest-memory_range_unittest.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/dumper_unittest-module.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/dumper_unittest-module_unittest.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/dumper_unittest-path_helper.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/dumper_unittest-stabs_reader.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/dumper_unittest-stabs_reader_unittest.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/dumper_unittest-stabs_to_module.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/dumper_unittest-stabs_to_module_unittest.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/dumper_unittest-string_conversion.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/dumper_unittest-string_conversion_unittest.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/dumper_unittest-test_assembler.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/dwarf/$(am__dirstamp): + @$(MKDIR_P) src/common/dwarf + @: > src/common/dwarf/$(am__dirstamp) +src/common/dwarf/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/common/dwarf/$(DEPDIR) + @: > src/common/dwarf/$(DEPDIR)/$(am__dirstamp) +src/common/dwarf/dumper_unittest-bytereader.$(OBJEXT): \ + src/common/dwarf/$(am__dirstamp) \ + src/common/dwarf/$(DEPDIR)/$(am__dirstamp) +src/common/dwarf/dumper_unittest-bytereader_unittest.$(OBJEXT): \ + src/common/dwarf/$(am__dirstamp) \ + src/common/dwarf/$(DEPDIR)/$(am__dirstamp) +src/common/dwarf/dumper_unittest-cfi_assembler.$(OBJEXT): \ + src/common/dwarf/$(am__dirstamp) \ + src/common/dwarf/$(DEPDIR)/$(am__dirstamp) +src/common/dwarf/dumper_unittest-dwarf2diehandler.$(OBJEXT): \ + src/common/dwarf/$(am__dirstamp) \ + src/common/dwarf/$(DEPDIR)/$(am__dirstamp) +src/common/dwarf/dumper_unittest-dwarf2diehandler_unittest.$(OBJEXT): \ + src/common/dwarf/$(am__dirstamp) \ + src/common/dwarf/$(DEPDIR)/$(am__dirstamp) +src/common/dwarf/dumper_unittest-dwarf2reader.$(OBJEXT): \ + src/common/dwarf/$(am__dirstamp) \ + src/common/dwarf/$(DEPDIR)/$(am__dirstamp) +src/common/dwarf/dumper_unittest-elf_reader.$(OBJEXT): \ + src/common/dwarf/$(am__dirstamp) \ + src/common/dwarf/$(DEPDIR)/$(am__dirstamp) +src/common/dwarf/dumper_unittest-dwarf2reader_cfi_unittest.$(OBJEXT): \ + src/common/dwarf/$(am__dirstamp) \ + src/common/dwarf/$(DEPDIR)/$(am__dirstamp) +src/common/dwarf/dumper_unittest-dwarf2reader_die_unittest.$(OBJEXT): \ + src/common/dwarf/$(am__dirstamp) \ + src/common/dwarf/$(DEPDIR)/$(am__dirstamp) +src/common/linux/dumper_unittest-crc32.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/dumper_unittest-dump_symbols.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/dumper_unittest-dump_symbols_unittest.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/dumper_unittest-elf_core_dump.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/dumper_unittest-elf_core_dump_unittest.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/dumper_unittest-elf_symbols_to_module.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/dumper_unittest-elf_symbols_to_module_unittest.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/dumper_unittest-elfutils.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/dumper_unittest-file_id.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/dumper_unittest-file_id_unittest.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/dumper_unittest-linux_libc_support.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/dumper_unittest-memory_mapped_file.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/dumper_unittest-memory_mapped_file_unittest.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/dumper_unittest-safe_readlink.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/dumper_unittest-safe_readlink_unittest.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/dumper_unittest-synth_elf.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/dumper_unittest-synth_elf_unittest.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/tests/dumper_unittest-crash_generator.$(OBJEXT): \ + src/common/linux/tests/$(am__dirstamp) \ + src/common/linux/tests/$(DEPDIR)/$(am__dirstamp) +src/common/tests/dumper_unittest-file_utils.$(OBJEXT): \ + src/common/tests/$(am__dirstamp) \ + src/common/tests/$(DEPDIR)/$(am__dirstamp) + +src/common/dumper_unittest$(EXEEXT): $(src_common_dumper_unittest_OBJECTS) $(src_common_dumper_unittest_DEPENDENCIES) $(EXTRA_src_common_dumper_unittest_DEPENDENCIES) src/common/$(am__dirstamp) + @rm -f src/common/dumper_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_common_dumper_unittest_OBJECTS) $(src_common_dumper_unittest_LDADD) $(LIBS) +src/common/dwarf/dwarf2reader_lineinfo_unittest-dwarf2reader_lineinfo_unittest.$(OBJEXT): \ + src/common/dwarf/$(am__dirstamp) \ + src/common/dwarf/$(DEPDIR)/$(am__dirstamp) + +src/common/dwarf/dwarf2reader_lineinfo_unittest$(EXEEXT): $(src_common_dwarf_dwarf2reader_lineinfo_unittest_OBJECTS) $(src_common_dwarf_dwarf2reader_lineinfo_unittest_DEPENDENCIES) $(EXTRA_src_common_dwarf_dwarf2reader_lineinfo_unittest_DEPENDENCIES) src/common/dwarf/$(am__dirstamp) + @rm -f src/common/dwarf/dwarf2reader_lineinfo_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_common_dwarf_dwarf2reader_lineinfo_unittest_OBJECTS) $(src_common_dwarf_dwarf2reader_lineinfo_unittest_LDADD) $(LIBS) +src/common/dwarf/dwarf2reader_splitfunctions_unittest-dwarf2reader_splitfunctions_unittest.$(OBJEXT): \ + src/common/dwarf/$(am__dirstamp) \ + src/common/dwarf/$(DEPDIR)/$(am__dirstamp) + +src/common/dwarf/dwarf2reader_splitfunctions_unittest$(EXEEXT): $(src_common_dwarf_dwarf2reader_splitfunctions_unittest_OBJECTS) $(src_common_dwarf_dwarf2reader_splitfunctions_unittest_DEPENDENCIES) $(EXTRA_src_common_dwarf_dwarf2reader_splitfunctions_unittest_DEPENDENCIES) src/common/dwarf/$(am__dirstamp) + @rm -f src/common/dwarf/dwarf2reader_splitfunctions_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_common_dwarf_dwarf2reader_splitfunctions_unittest_OBJECTS) $(src_common_dwarf_dwarf2reader_splitfunctions_unittest_LDADD) $(LIBS) +src/common/linux/google_crashdump_uploader_test-google_crashdump_uploader.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/google_crashdump_uploader_test-google_crashdump_uploader_test.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/google_crashdump_uploader_test-libcurl_wrapper.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) + +src/common/linux/google_crashdump_uploader_test$(EXEEXT): $(src_common_linux_google_crashdump_uploader_test_OBJECTS) $(src_common_linux_google_crashdump_uploader_test_DEPENDENCIES) $(EXTRA_src_common_linux_google_crashdump_uploader_test_DEPENDENCIES) src/common/linux/$(am__dirstamp) + @rm -f src/common/linux/google_crashdump_uploader_test$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_common_linux_google_crashdump_uploader_test_OBJECTS) $(src_common_linux_google_crashdump_uploader_test_LDADD) $(LIBS) +src/common/mac_macho_reader_unittest-dwarf_cfi_to_module.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/mac_macho_reader_unittest-dwarf_cu_to_module.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/mac_macho_reader_unittest-dwarf_line_to_module.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/mac_macho_reader_unittest-language.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/mac_macho_reader_unittest-md5.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/mac_macho_reader_unittest-module.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/mac_macho_reader_unittest-path_helper.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/mac_macho_reader_unittest-stabs_reader.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/mac_macho_reader_unittest-stabs_to_module.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/mac_macho_reader_unittest-test_assembler.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/dwarf/mac_macho_reader_unittest-bytereader.$(OBJEXT): \ + src/common/dwarf/$(am__dirstamp) \ + src/common/dwarf/$(DEPDIR)/$(am__dirstamp) +src/common/dwarf/mac_macho_reader_unittest-cfi_assembler.$(OBJEXT): \ + src/common/dwarf/$(am__dirstamp) \ + src/common/dwarf/$(DEPDIR)/$(am__dirstamp) +src/common/dwarf/mac_macho_reader_unittest-dwarf2diehandler.$(OBJEXT): \ + src/common/dwarf/$(am__dirstamp) \ + src/common/dwarf/$(DEPDIR)/$(am__dirstamp) +src/common/dwarf/mac_macho_reader_unittest-dwarf2reader.$(OBJEXT): \ + src/common/dwarf/$(am__dirstamp) \ + src/common/dwarf/$(DEPDIR)/$(am__dirstamp) +src/common/dwarf/mac_macho_reader_unittest-elf_reader.$(OBJEXT): \ + src/common/dwarf/$(am__dirstamp) \ + src/common/dwarf/$(DEPDIR)/$(am__dirstamp) +src/common/mac/$(am__dirstamp): + @$(MKDIR_P) src/common/mac + @: > src/common/mac/$(am__dirstamp) +src/common/mac/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/common/mac/$(DEPDIR) + @: > src/common/mac/$(DEPDIR)/$(am__dirstamp) +src/common/mac/macho_reader_unittest-arch_utilities.$(OBJEXT): \ + src/common/mac/$(am__dirstamp) \ + src/common/mac/$(DEPDIR)/$(am__dirstamp) +src/common/mac/macho_reader_unittest-file_id.$(OBJEXT): \ + src/common/mac/$(am__dirstamp) \ + src/common/mac/$(DEPDIR)/$(am__dirstamp) +src/common/mac/macho_reader_unittest-macho_id.$(OBJEXT): \ + src/common/mac/$(am__dirstamp) \ + src/common/mac/$(DEPDIR)/$(am__dirstamp) +src/common/mac/macho_reader_unittest-macho_reader.$(OBJEXT): \ + src/common/mac/$(am__dirstamp) \ + src/common/mac/$(DEPDIR)/$(am__dirstamp) +src/common/mac/macho_reader_unittest-macho_reader_unittest.$(OBJEXT): \ + src/common/mac/$(am__dirstamp) \ + src/common/mac/$(DEPDIR)/$(am__dirstamp) +src/common/mac/macho_reader_unittest-macho_utilities.$(OBJEXT): \ + src/common/mac/$(am__dirstamp) \ + src/common/mac/$(DEPDIR)/$(am__dirstamp) +src/common/mac/macho_reader_unittest-macho_walker.$(OBJEXT): \ + src/common/mac/$(am__dirstamp) \ + src/common/mac/$(DEPDIR)/$(am__dirstamp) +src/common/tests/mac_macho_reader_unittest-file_utils.$(OBJEXT): \ + src/common/tests/$(am__dirstamp) \ + src/common/tests/$(DEPDIR)/$(am__dirstamp) + +src/common/mac/macho_reader_unittest$(EXEEXT): $(src_common_mac_macho_reader_unittest_OBJECTS) $(src_common_mac_macho_reader_unittest_DEPENDENCIES) $(EXTRA_src_common_mac_macho_reader_unittest_DEPENDENCIES) src/common/mac/$(am__dirstamp) + @rm -f src/common/mac/macho_reader_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_common_mac_macho_reader_unittest_OBJECTS) $(src_common_mac_macho_reader_unittest_LDADD) $(LIBS) +src/common/test_assembler_unittest-test_assembler.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/test_assembler_unittest-test_assembler_unittest.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) + +src/common/test_assembler_unittest$(EXEEXT): $(src_common_test_assembler_unittest_OBJECTS) $(src_common_test_assembler_unittest_DEPENDENCIES) $(EXTRA_src_common_test_assembler_unittest_DEPENDENCIES) src/common/$(am__dirstamp) + @rm -f src/common/test_assembler_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_common_test_assembler_unittest_OBJECTS) $(src_common_test_assembler_unittest_LDADD) $(LIBS) +src/processor/address_map_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/address_map_unittest$(EXEEXT): $(src_processor_address_map_unittest_OBJECTS) $(src_processor_address_map_unittest_DEPENDENCIES) $(EXTRA_src_processor_address_map_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/address_map_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_address_map_unittest_OBJECTS) $(src_processor_address_map_unittest_LDADD) $(LIBS) +src/processor/basic_source_line_resolver_unittest-basic_source_line_resolver_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/basic_source_line_resolver_unittest$(EXEEXT): $(src_processor_basic_source_line_resolver_unittest_OBJECTS) $(src_processor_basic_source_line_resolver_unittest_DEPENDENCIES) $(EXTRA_src_processor_basic_source_line_resolver_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/basic_source_line_resolver_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_basic_source_line_resolver_unittest_OBJECTS) $(src_processor_basic_source_line_resolver_unittest_LDADD) $(LIBS) +src/processor/cfi_frame_info_unittest-cfi_frame_info_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/cfi_frame_info_unittest$(EXEEXT): $(src_processor_cfi_frame_info_unittest_OBJECTS) $(src_processor_cfi_frame_info_unittest_DEPENDENCIES) $(EXTRA_src_processor_cfi_frame_info_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/cfi_frame_info_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_cfi_frame_info_unittest_OBJECTS) $(src_processor_cfi_frame_info_unittest_LDADD) $(LIBS) +src/processor/contained_range_map_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/contained_range_map_unittest$(EXEEXT): $(src_processor_contained_range_map_unittest_OBJECTS) $(src_processor_contained_range_map_unittest_DEPENDENCIES) $(EXTRA_src_processor_contained_range_map_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/contained_range_map_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_contained_range_map_unittest_OBJECTS) $(src_processor_contained_range_map_unittest_LDADD) $(LIBS) +src/processor/disassembler_x86_unittest-disassembler_x86_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/disassembler_x86_unittest$(EXEEXT): $(src_processor_disassembler_x86_unittest_OBJECTS) $(src_processor_disassembler_x86_unittest_DEPENDENCIES) $(EXTRA_src_processor_disassembler_x86_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/disassembler_x86_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_disassembler_x86_unittest_OBJECTS) $(src_processor_disassembler_x86_unittest_LDADD) $(LIBS) +src/processor/exploitability_unittest-exploitability_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/exploitability_unittest$(EXEEXT): $(src_processor_exploitability_unittest_OBJECTS) $(src_processor_exploitability_unittest_DEPENDENCIES) $(EXTRA_src_processor_exploitability_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/exploitability_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_exploitability_unittest_OBJECTS) $(src_processor_exploitability_unittest_LDADD) $(LIBS) +src/processor/fast_source_line_resolver_unittest-fast_source_line_resolver_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/fast_source_line_resolver_unittest$(EXEEXT): $(src_processor_fast_source_line_resolver_unittest_OBJECTS) $(src_processor_fast_source_line_resolver_unittest_DEPENDENCIES) $(EXTRA_src_processor_fast_source_line_resolver_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/fast_source_line_resolver_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_fast_source_line_resolver_unittest_OBJECTS) $(src_processor_fast_source_line_resolver_unittest_LDADD) $(LIBS) +src/processor/map_serializers_unittest-map_serializers_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/map_serializers_unittest$(EXEEXT): $(src_processor_map_serializers_unittest_OBJECTS) $(src_processor_map_serializers_unittest_DEPENDENCIES) $(EXTRA_src_processor_map_serializers_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/map_serializers_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_map_serializers_unittest_OBJECTS) $(src_processor_map_serializers_unittest_LDADD) $(LIBS) +src/processor/microdump_processor_unittest-microdump_processor_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/microdump_processor_unittest$(EXEEXT): $(src_processor_microdump_processor_unittest_OBJECTS) $(src_processor_microdump_processor_unittest_DEPENDENCIES) $(EXTRA_src_processor_microdump_processor_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/microdump_processor_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_microdump_processor_unittest_OBJECTS) $(src_processor_microdump_processor_unittest_LDADD) $(LIBS) +src/processor/microdump_stackwalk.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/microdump_stackwalk$(EXEEXT): $(src_processor_microdump_stackwalk_OBJECTS) $(src_processor_microdump_stackwalk_DEPENDENCIES) $(EXTRA_src_processor_microdump_stackwalk_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/microdump_stackwalk$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_microdump_stackwalk_OBJECTS) $(src_processor_microdump_stackwalk_LDADD) $(LIBS) +src/processor/minidump_dump.$(OBJEXT): src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/minidump_dump$(EXEEXT): $(src_processor_minidump_dump_OBJECTS) $(src_processor_minidump_dump_DEPENDENCIES) $(EXTRA_src_processor_minidump_dump_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/minidump_dump$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_minidump_dump_OBJECTS) $(src_processor_minidump_dump_LDADD) $(LIBS) +src/processor/minidump_processor_unittest-minidump_processor_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/minidump_processor_unittest$(EXEEXT): $(src_processor_minidump_processor_unittest_OBJECTS) $(src_processor_minidump_processor_unittest_DEPENDENCIES) $(EXTRA_src_processor_minidump_processor_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/minidump_processor_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_minidump_processor_unittest_OBJECTS) $(src_processor_minidump_processor_unittest_LDADD) $(LIBS) +src/processor/minidump_stackwalk.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/minidump_stackwalk$(EXEEXT): $(src_processor_minidump_stackwalk_OBJECTS) $(src_processor_minidump_stackwalk_DEPENDENCIES) $(EXTRA_src_processor_minidump_stackwalk_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/minidump_stackwalk$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_minidump_stackwalk_OBJECTS) $(src_processor_minidump_stackwalk_LDADD) $(LIBS) +src/common/processor_minidump_unittest-test_assembler.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/processor/minidump_unittest-minidump_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/minidump_unittest-synth_minidump.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/minidump_unittest$(EXEEXT): $(src_processor_minidump_unittest_OBJECTS) $(src_processor_minidump_unittest_DEPENDENCIES) $(EXTRA_src_processor_minidump_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/minidump_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_minidump_unittest_OBJECTS) $(src_processor_minidump_unittest_LDADD) $(LIBS) +src/processor/pathname_stripper_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/pathname_stripper_unittest$(EXEEXT): $(src_processor_pathname_stripper_unittest_OBJECTS) $(src_processor_pathname_stripper_unittest_DEPENDENCIES) $(EXTRA_src_processor_pathname_stripper_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/pathname_stripper_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_pathname_stripper_unittest_OBJECTS) $(src_processor_pathname_stripper_unittest_LDADD) $(LIBS) +src/processor/postfix_evaluator_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/postfix_evaluator_unittest$(EXEEXT): $(src_processor_postfix_evaluator_unittest_OBJECTS) $(src_processor_postfix_evaluator_unittest_DEPENDENCIES) $(EXTRA_src_processor_postfix_evaluator_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/postfix_evaluator_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_postfix_evaluator_unittest_OBJECTS) $(src_processor_postfix_evaluator_unittest_LDADD) $(LIBS) +src/processor/proc_maps_linux_unittest-proc_maps_linux.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/proc_maps_linux_unittest-proc_maps_linux_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/proc_maps_linux_unittest$(EXEEXT): $(src_processor_proc_maps_linux_unittest_OBJECTS) $(src_processor_proc_maps_linux_unittest_DEPENDENCIES) $(EXTRA_src_processor_proc_maps_linux_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/proc_maps_linux_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_proc_maps_linux_unittest_OBJECTS) $(src_processor_proc_maps_linux_unittest_LDADD) $(LIBS) +src/processor/range_map_truncate_lower_unittest-range_map_truncate_lower_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/range_map_truncate_lower_unittest$(EXEEXT): $(src_processor_range_map_truncate_lower_unittest_OBJECTS) $(src_processor_range_map_truncate_lower_unittest_DEPENDENCIES) $(EXTRA_src_processor_range_map_truncate_lower_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/range_map_truncate_lower_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_range_map_truncate_lower_unittest_OBJECTS) $(src_processor_range_map_truncate_lower_unittest_LDADD) $(LIBS) +src/processor/range_map_truncate_upper_unittest-range_map_truncate_upper_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/range_map_truncate_upper_unittest$(EXEEXT): $(src_processor_range_map_truncate_upper_unittest_OBJECTS) $(src_processor_range_map_truncate_upper_unittest_DEPENDENCIES) $(EXTRA_src_processor_range_map_truncate_upper_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/range_map_truncate_upper_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_range_map_truncate_upper_unittest_OBJECTS) $(src_processor_range_map_truncate_upper_unittest_LDADD) $(LIBS) +src/processor/range_map_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/range_map_unittest$(EXEEXT): $(src_processor_range_map_unittest_OBJECTS) $(src_processor_range_map_unittest_DEPENDENCIES) $(EXTRA_src_processor_range_map_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/range_map_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_range_map_unittest_OBJECTS) $(src_processor_range_map_unittest_LDADD) $(LIBS) +src/common/processor_stackwalker_address_list_unittest-test_assembler.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/processor/stackwalker_address_list_unittest-stackwalker_address_list_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/stackwalker_address_list_unittest$(EXEEXT): $(src_processor_stackwalker_address_list_unittest_OBJECTS) $(src_processor_stackwalker_address_list_unittest_DEPENDENCIES) $(EXTRA_src_processor_stackwalker_address_list_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/stackwalker_address_list_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_stackwalker_address_list_unittest_OBJECTS) $(src_processor_stackwalker_address_list_unittest_LDADD) $(LIBS) +src/common/processor_stackwalker_amd64_unittest-test_assembler.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/processor/stackwalker_amd64_unittest-stackwalker_amd64_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/stackwalker_amd64_unittest$(EXEEXT): $(src_processor_stackwalker_amd64_unittest_OBJECTS) $(src_processor_stackwalker_amd64_unittest_DEPENDENCIES) $(EXTRA_src_processor_stackwalker_amd64_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/stackwalker_amd64_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_stackwalker_amd64_unittest_OBJECTS) $(src_processor_stackwalker_amd64_unittest_LDADD) $(LIBS) +src/common/processor_stackwalker_arm64_unittest-test_assembler.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/processor/stackwalker_arm64_unittest-stackwalker_arm64_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/stackwalker_arm64_unittest$(EXEEXT): $(src_processor_stackwalker_arm64_unittest_OBJECTS) $(src_processor_stackwalker_arm64_unittest_DEPENDENCIES) $(EXTRA_src_processor_stackwalker_arm64_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/stackwalker_arm64_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_stackwalker_arm64_unittest_OBJECTS) $(src_processor_stackwalker_arm64_unittest_LDADD) $(LIBS) +src/common/processor_stackwalker_arm_unittest-test_assembler.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/processor/stackwalker_arm_unittest-stackwalker_arm_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/stackwalker_arm_unittest$(EXEEXT): $(src_processor_stackwalker_arm_unittest_OBJECTS) $(src_processor_stackwalker_arm_unittest_DEPENDENCIES) $(EXTRA_src_processor_stackwalker_arm_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/stackwalker_arm_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_stackwalker_arm_unittest_OBJECTS) $(src_processor_stackwalker_arm_unittest_LDADD) $(LIBS) +src/common/processor_stackwalker_mips64_unittest-test_assembler.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/processor/stackwalker_mips64_unittest-stackwalker_mips64_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/stackwalker_mips64_unittest$(EXEEXT): $(src_processor_stackwalker_mips64_unittest_OBJECTS) $(src_processor_stackwalker_mips64_unittest_DEPENDENCIES) $(EXTRA_src_processor_stackwalker_mips64_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/stackwalker_mips64_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_stackwalker_mips64_unittest_OBJECTS) $(src_processor_stackwalker_mips64_unittest_LDADD) $(LIBS) +src/common/processor_stackwalker_mips_unittest-test_assembler.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/processor/stackwalker_mips_unittest-stackwalker_mips_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/stackwalker_mips_unittest$(EXEEXT): $(src_processor_stackwalker_mips_unittest_OBJECTS) $(src_processor_stackwalker_mips_unittest_DEPENDENCIES) $(EXTRA_src_processor_stackwalker_mips_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/stackwalker_mips_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_stackwalker_mips_unittest_OBJECTS) $(src_processor_stackwalker_mips_unittest_LDADD) $(LIBS) +src/processor/stackwalker_selftest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/stackwalker_selftest$(EXEEXT): $(src_processor_stackwalker_selftest_OBJECTS) $(src_processor_stackwalker_selftest_DEPENDENCIES) $(EXTRA_src_processor_stackwalker_selftest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/stackwalker_selftest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_stackwalker_selftest_OBJECTS) $(src_processor_stackwalker_selftest_LDADD) $(LIBS) +src/common/processor_stackwalker_x86_unittest-test_assembler.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/processor/stackwalker_x86_unittest-stackwalker_x86_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/stackwalker_x86_unittest$(EXEEXT): $(src_processor_stackwalker_x86_unittest_OBJECTS) $(src_processor_stackwalker_x86_unittest_DEPENDENCIES) $(EXTRA_src_processor_stackwalker_x86_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/stackwalker_x86_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_stackwalker_x86_unittest_OBJECTS) $(src_processor_stackwalker_x86_unittest_LDADD) $(LIBS) +src/processor/static_address_map_unittest-static_address_map_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/static_address_map_unittest$(EXEEXT): $(src_processor_static_address_map_unittest_OBJECTS) $(src_processor_static_address_map_unittest_DEPENDENCIES) $(EXTRA_src_processor_static_address_map_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/static_address_map_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_static_address_map_unittest_OBJECTS) $(src_processor_static_address_map_unittest_LDADD) $(LIBS) +src/processor/static_contained_range_map_unittest-static_contained_range_map_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/static_contained_range_map_unittest$(EXEEXT): $(src_processor_static_contained_range_map_unittest_OBJECTS) $(src_processor_static_contained_range_map_unittest_DEPENDENCIES) $(EXTRA_src_processor_static_contained_range_map_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/static_contained_range_map_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_static_contained_range_map_unittest_OBJECTS) $(src_processor_static_contained_range_map_unittest_LDADD) $(LIBS) +src/processor/static_map_unittest-static_map_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/static_map_unittest$(EXEEXT): $(src_processor_static_map_unittest_OBJECTS) $(src_processor_static_map_unittest_DEPENDENCIES) $(EXTRA_src_processor_static_map_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/static_map_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_static_map_unittest_OBJECTS) $(src_processor_static_map_unittest_LDADD) $(LIBS) +src/processor/static_range_map_unittest-static_range_map_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/static_range_map_unittest$(EXEEXT): $(src_processor_static_range_map_unittest_OBJECTS) $(src_processor_static_range_map_unittest_DEPENDENCIES) $(EXTRA_src_processor_static_range_map_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/static_range_map_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_static_range_map_unittest_OBJECTS) $(src_processor_static_range_map_unittest_LDADD) $(LIBS) +src/common/processor_synth_minidump_unittest-test_assembler.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/processor/synth_minidump_unittest-synth_minidump_unittest.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) +src/processor/synth_minidump_unittest-synth_minidump.$(OBJEXT): \ + src/processor/$(am__dirstamp) \ + src/processor/$(DEPDIR)/$(am__dirstamp) + +src/processor/synth_minidump_unittest$(EXEEXT): $(src_processor_synth_minidump_unittest_OBJECTS) $(src_processor_synth_minidump_unittest_DEPENDENCIES) $(EXTRA_src_processor_synth_minidump_unittest_DEPENDENCIES) src/processor/$(am__dirstamp) + @rm -f src/processor/synth_minidump_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_processor_synth_minidump_unittest_OBJECTS) $(src_processor_synth_minidump_unittest_LDADD) $(LIBS) +src/tools/linux/core2md/$(am__dirstamp): + @$(MKDIR_P) src/tools/linux/core2md + @: > src/tools/linux/core2md/$(am__dirstamp) +src/tools/linux/core2md/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/tools/linux/core2md/$(DEPDIR) + @: > src/tools/linux/core2md/$(DEPDIR)/$(am__dirstamp) +src/tools/linux/core2md/core2md.$(OBJEXT): \ + src/tools/linux/core2md/$(am__dirstamp) \ + src/tools/linux/core2md/$(DEPDIR)/$(am__dirstamp) + +src/tools/linux/core2md/core2md$(EXEEXT): $(src_tools_linux_core2md_core2md_OBJECTS) $(src_tools_linux_core2md_core2md_DEPENDENCIES) $(EXTRA_src_tools_linux_core2md_core2md_DEPENDENCIES) src/tools/linux/core2md/$(am__dirstamp) + @rm -f src/tools/linux/core2md/core2md$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_tools_linux_core2md_core2md_OBJECTS) $(src_tools_linux_core2md_core2md_LDADD) $(LIBS) +src/tools/linux/core_handler/$(am__dirstamp): + @$(MKDIR_P) src/tools/linux/core_handler + @: > src/tools/linux/core_handler/$(am__dirstamp) +src/tools/linux/core_handler/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/tools/linux/core_handler/$(DEPDIR) + @: > src/tools/linux/core_handler/$(DEPDIR)/$(am__dirstamp) +src/tools/linux/core_handler/core_handler.$(OBJEXT): \ + src/tools/linux/core_handler/$(am__dirstamp) \ + src/tools/linux/core_handler/$(DEPDIR)/$(am__dirstamp) + +src/tools/linux/core_handler/core_handler$(EXEEXT): $(src_tools_linux_core_handler_core_handler_OBJECTS) $(src_tools_linux_core_handler_core_handler_DEPENDENCIES) $(EXTRA_src_tools_linux_core_handler_core_handler_DEPENDENCIES) src/tools/linux/core_handler/$(am__dirstamp) + @rm -f src/tools/linux/core_handler/core_handler$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_tools_linux_core_handler_core_handler_OBJECTS) $(src_tools_linux_core_handler_core_handler_LDADD) $(LIBS) +src/common/tools_linux_dump_syms_dump_syms-dwarf_cfi_to_module.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/tools_linux_dump_syms_dump_syms-dwarf_cu_to_module.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/tools_linux_dump_syms_dump_syms-dwarf_line_to_module.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/tools_linux_dump_syms_dump_syms-dwarf_range_list_handler.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/tools_linux_dump_syms_dump_syms-language.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/tools_linux_dump_syms_dump_syms-module.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/tools_linux_dump_syms_dump_syms-path_helper.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/tools_linux_dump_syms_dump_syms-stabs_reader.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/tools_linux_dump_syms_dump_syms-stabs_to_module.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/dwarf/tools_linux_dump_syms_dump_syms-bytereader.$(OBJEXT): \ + src/common/dwarf/$(am__dirstamp) \ + src/common/dwarf/$(DEPDIR)/$(am__dirstamp) +src/common/dwarf/tools_linux_dump_syms_dump_syms-dwarf2diehandler.$(OBJEXT): \ + src/common/dwarf/$(am__dirstamp) \ + src/common/dwarf/$(DEPDIR)/$(am__dirstamp) +src/common/dwarf/tools_linux_dump_syms_dump_syms-dwarf2reader.$(OBJEXT): \ + src/common/dwarf/$(am__dirstamp) \ + src/common/dwarf/$(DEPDIR)/$(am__dirstamp) +src/common/dwarf/tools_linux_dump_syms_dump_syms-elf_reader.$(OBJEXT): \ + src/common/dwarf/$(am__dirstamp) \ + src/common/dwarf/$(DEPDIR)/$(am__dirstamp) +src/common/linux/tools_linux_dump_syms_dump_syms-crc32.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/tools_linux_dump_syms_dump_syms-dump_symbols.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/tools_linux_dump_syms_dump_syms-elf_symbols_to_module.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/tools_linux_dump_syms_dump_syms-elfutils.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/tools_linux_dump_syms_dump_syms-file_id.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/tools_linux_dump_syms_dump_syms-linux_libc_support.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/tools_linux_dump_syms_dump_syms-memory_mapped_file.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/tools_linux_dump_syms_dump_syms-safe_readlink.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/tools/linux/dump_syms/$(am__dirstamp): + @$(MKDIR_P) src/tools/linux/dump_syms + @: > src/tools/linux/dump_syms/$(am__dirstamp) +src/tools/linux/dump_syms/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/tools/linux/dump_syms/$(DEPDIR) + @: > src/tools/linux/dump_syms/$(DEPDIR)/$(am__dirstamp) +src/tools/linux/dump_syms/dump_syms-dump_syms.$(OBJEXT): \ + src/tools/linux/dump_syms/$(am__dirstamp) \ + src/tools/linux/dump_syms/$(DEPDIR)/$(am__dirstamp) + +src/tools/linux/dump_syms/dump_syms$(EXEEXT): $(src_tools_linux_dump_syms_dump_syms_OBJECTS) $(src_tools_linux_dump_syms_dump_syms_DEPENDENCIES) $(EXTRA_src_tools_linux_dump_syms_dump_syms_DEPENDENCIES) src/tools/linux/dump_syms/$(am__dirstamp) + @rm -f src/tools/linux/dump_syms/dump_syms$(EXEEXT) + $(AM_V_CXXLD)$(src_tools_linux_dump_syms_dump_syms_LINK) $(src_tools_linux_dump_syms_dump_syms_OBJECTS) $(src_tools_linux_dump_syms_dump_syms_LDADD) $(LIBS) +src/common/path_helper.$(OBJEXT): src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/tools/linux/md2core/$(am__dirstamp): + @$(MKDIR_P) src/tools/linux/md2core + @: > src/tools/linux/md2core/$(am__dirstamp) +src/tools/linux/md2core/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/tools/linux/md2core/$(DEPDIR) + @: > src/tools/linux/md2core/$(DEPDIR)/$(am__dirstamp) +src/tools/linux/md2core/minidump-2-core.$(OBJEXT): \ + src/tools/linux/md2core/$(am__dirstamp) \ + src/tools/linux/md2core/$(DEPDIR)/$(am__dirstamp) + +src/tools/linux/md2core/minidump-2-core$(EXEEXT): $(src_tools_linux_md2core_minidump_2_core_OBJECTS) $(src_tools_linux_md2core_minidump_2_core_DEPENDENCIES) $(EXTRA_src_tools_linux_md2core_minidump_2_core_DEPENDENCIES) src/tools/linux/md2core/$(am__dirstamp) + @rm -f src/tools/linux/md2core/minidump-2-core$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_tools_linux_md2core_minidump_2_core_OBJECTS) $(src_tools_linux_md2core_minidump_2_core_LDADD) $(LIBS) +src/tools/linux/md2core/minidump_2_core_unittest-minidump_memory_range_unittest.$(OBJEXT): \ + src/tools/linux/md2core/$(am__dirstamp) \ + src/tools/linux/md2core/$(DEPDIR)/$(am__dirstamp) + +src/tools/linux/md2core/minidump_2_core_unittest$(EXEEXT): $(src_tools_linux_md2core_minidump_2_core_unittest_OBJECTS) $(src_tools_linux_md2core_minidump_2_core_unittest_DEPENDENCIES) $(EXTRA_src_tools_linux_md2core_minidump_2_core_unittest_DEPENDENCIES) src/tools/linux/md2core/$(am__dirstamp) + @rm -f src/tools/linux/md2core/minidump_2_core_unittest$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_tools_linux_md2core_minidump_2_core_unittest_OBJECTS) $(src_tools_linux_md2core_minidump_2_core_unittest_LDADD) $(LIBS) +src/tools/linux/pid2md/$(am__dirstamp): + @$(MKDIR_P) src/tools/linux/pid2md + @: > src/tools/linux/pid2md/$(am__dirstamp) +src/tools/linux/pid2md/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/tools/linux/pid2md/$(DEPDIR) + @: > src/tools/linux/pid2md/$(DEPDIR)/$(am__dirstamp) +src/tools/linux/pid2md/pid2md.$(OBJEXT): \ + src/tools/linux/pid2md/$(am__dirstamp) \ + src/tools/linux/pid2md/$(DEPDIR)/$(am__dirstamp) + +src/tools/linux/pid2md/pid2md$(EXEEXT): $(src_tools_linux_pid2md_pid2md_OBJECTS) $(src_tools_linux_pid2md_pid2md_DEPENDENCIES) $(EXTRA_src_tools_linux_pid2md_pid2md_DEPENDENCIES) src/tools/linux/pid2md/$(am__dirstamp) + @rm -f src/tools/linux/pid2md/pid2md$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_tools_linux_pid2md_pid2md_OBJECTS) $(src_tools_linux_pid2md_pid2md_LDADD) $(LIBS) +src/common/linux/http_upload.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/tools/linux/symupload/$(am__dirstamp): + @$(MKDIR_P) src/tools/linux/symupload + @: > src/tools/linux/symupload/$(am__dirstamp) +src/tools/linux/symupload/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/tools/linux/symupload/$(DEPDIR) + @: > src/tools/linux/symupload/$(DEPDIR)/$(am__dirstamp) +src/tools/linux/symupload/minidump_upload.$(OBJEXT): \ + src/tools/linux/symupload/$(am__dirstamp) \ + src/tools/linux/symupload/$(DEPDIR)/$(am__dirstamp) + +src/tools/linux/symupload/minidump_upload$(EXEEXT): $(src_tools_linux_symupload_minidump_upload_OBJECTS) $(src_tools_linux_symupload_minidump_upload_DEPENDENCIES) $(EXTRA_src_tools_linux_symupload_minidump_upload_DEPENDENCIES) src/tools/linux/symupload/$(am__dirstamp) + @rm -f src/tools/linux/symupload/minidump_upload$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_tools_linux_symupload_minidump_upload_OBJECTS) $(src_tools_linux_symupload_minidump_upload_LDADD) $(LIBS) +src/common/linux/libcurl_wrapper.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/symbol_collector_client.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/common/linux/symbol_upload.$(OBJEXT): \ + src/common/linux/$(am__dirstamp) \ + src/common/linux/$(DEPDIR)/$(am__dirstamp) +src/tools/linux/symupload/sym_upload.$(OBJEXT): \ + src/tools/linux/symupload/$(am__dirstamp) \ + src/tools/linux/symupload/$(DEPDIR)/$(am__dirstamp) + +src/tools/linux/symupload/sym_upload$(EXEEXT): $(src_tools_linux_symupload_sym_upload_OBJECTS) $(src_tools_linux_symupload_sym_upload_DEPENDENCIES) $(EXTRA_src_tools_linux_symupload_sym_upload_DEPENDENCIES) src/tools/linux/symupload/$(am__dirstamp) + @rm -f src/tools/linux/symupload/sym_upload$(EXEEXT) + $(AM_V_CXXLD)$(CXXLINK) $(src_tools_linux_symupload_sym_upload_OBJECTS) $(src_tools_linux_symupload_sym_upload_LDADD) $(LIBS) +src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_cfi_to_module.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_cu_to_module.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_line_to_module.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_range_list_handler.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/tools_mac_dump_syms_dump_syms_mac-language.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/tools_mac_dump_syms_dump_syms_mac-md5.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/tools_mac_dump_syms_dump_syms_mac-module.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/tools_mac_dump_syms_dump_syms_mac-path_helper.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/tools_mac_dump_syms_dump_syms_mac-stabs_reader.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/tools_mac_dump_syms_dump_syms_mac-stabs_to_module.$(OBJEXT): \ + src/common/$(am__dirstamp) \ + src/common/$(DEPDIR)/$(am__dirstamp) +src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-bytereader.$(OBJEXT): \ + src/common/dwarf/$(am__dirstamp) \ + src/common/dwarf/$(DEPDIR)/$(am__dirstamp) +src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-dwarf2diehandler.$(OBJEXT): \ + src/common/dwarf/$(am__dirstamp) \ + src/common/dwarf/$(DEPDIR)/$(am__dirstamp) +src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-dwarf2reader.$(OBJEXT): \ + src/common/dwarf/$(am__dirstamp) \ + src/common/dwarf/$(DEPDIR)/$(am__dirstamp) +src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-elf_reader.$(OBJEXT): \ + src/common/dwarf/$(am__dirstamp) \ + src/common/dwarf/$(DEPDIR)/$(am__dirstamp) +src/common/mac/tools_mac_dump_syms_dump_syms_mac-arch_utilities.$(OBJEXT): \ + src/common/mac/$(am__dirstamp) \ + src/common/mac/$(DEPDIR)/$(am__dirstamp) +src/common/mac/tools_mac_dump_syms_dump_syms_mac-dump_syms.$(OBJEXT): \ + src/common/mac/$(am__dirstamp) \ + src/common/mac/$(DEPDIR)/$(am__dirstamp) +src/common/mac/tools_mac_dump_syms_dump_syms_mac-file_id.$(OBJEXT): \ + src/common/mac/$(am__dirstamp) \ + src/common/mac/$(DEPDIR)/$(am__dirstamp) +src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_id.$(OBJEXT): \ + src/common/mac/$(am__dirstamp) \ + src/common/mac/$(DEPDIR)/$(am__dirstamp) +src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_reader.$(OBJEXT): \ + src/common/mac/$(am__dirstamp) \ + src/common/mac/$(DEPDIR)/$(am__dirstamp) +src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_utilities.$(OBJEXT): \ + src/common/mac/$(am__dirstamp) \ + src/common/mac/$(DEPDIR)/$(am__dirstamp) +src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_walker.$(OBJEXT): \ + src/common/mac/$(am__dirstamp) \ + src/common/mac/$(DEPDIR)/$(am__dirstamp) +src/tools/mac/dump_syms/$(am__dirstamp): + @$(MKDIR_P) src/tools/mac/dump_syms + @: > src/tools/mac/dump_syms/$(am__dirstamp) +src/tools/mac/dump_syms/$(DEPDIR)/$(am__dirstamp): + @$(MKDIR_P) src/tools/mac/dump_syms/$(DEPDIR) + @: > src/tools/mac/dump_syms/$(DEPDIR)/$(am__dirstamp) +src/tools/mac/dump_syms/dump_syms_mac-dump_syms_tool.$(OBJEXT): \ + src/tools/mac/dump_syms/$(am__dirstamp) \ + src/tools/mac/dump_syms/$(DEPDIR)/$(am__dirstamp) + +src/tools/mac/dump_syms/dump_syms_mac$(EXEEXT): $(src_tools_mac_dump_syms_dump_syms_mac_OBJECTS) $(src_tools_mac_dump_syms_dump_syms_mac_DEPENDENCIES) $(EXTRA_src_tools_mac_dump_syms_dump_syms_mac_DEPENDENCIES) src/tools/mac/dump_syms/$(am__dirstamp) + @rm -f src/tools/mac/dump_syms/dump_syms_mac$(EXEEXT) + $(AM_V_CXXLD)$(src_tools_mac_dump_syms_dump_syms_mac_LINK) $(src_tools_mac_dump_syms_dump_syms_mac_OBJECTS) $(src_tools_mac_dump_syms_dump_syms_mac_LDADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) + -rm -f src/client/*.$(OBJEXT) + -rm -f src/client/linux/crash_generation/*.$(OBJEXT) + -rm -f src/client/linux/dump_writer_common/*.$(OBJEXT) + -rm -f src/client/linux/handler/*.$(OBJEXT) + -rm -f src/client/linux/log/*.$(OBJEXT) + -rm -f src/client/linux/microdump_writer/*.$(OBJEXT) + -rm -f src/client/linux/minidump_writer/*.$(OBJEXT) + -rm -f src/common/*.$(OBJEXT) + -rm -f src/common/dwarf/*.$(OBJEXT) + -rm -f src/common/linux/*.$(OBJEXT) + -rm -f src/common/linux/tests/*.$(OBJEXT) + -rm -f src/common/mac/*.$(OBJEXT) + -rm -f src/common/tests/*.$(OBJEXT) + -rm -f src/processor/*.$(OBJEXT) + -rm -f src/testing/googlemock/src/*.$(OBJEXT) + -rm -f src/testing/googletest/src/*.$(OBJEXT) + -rm -f src/third_party/libdisasm/*.$(OBJEXT) + -rm -f src/tools/linux/core2md/*.$(OBJEXT) + -rm -f src/tools/linux/core_handler/*.$(OBJEXT) + -rm -f src/tools/linux/dump_syms/*.$(OBJEXT) + -rm -f src/tools/linux/md2core/*.$(OBJEXT) + -rm -f src/tools/linux/pid2md/*.$(OBJEXT) + -rm -f src/tools/linux/symupload/*.$(OBJEXT) + -rm -f src/tools/mac/dump_syms/*.$(OBJEXT) + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@src/client/$(DEPDIR)/minidump_file_writer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/crash_generation/$(DEPDIR)/crash_generation_client.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/crash_generation/$(DEPDIR)/crash_generation_server.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/dump_writer_common/$(DEPDIR)/thread_info.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/dump_writer_common/$(DEPDIR)/ucontext_reader.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/handler/$(DEPDIR)/exception_handler.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/handler/$(DEPDIR)/linux_client_unittest_shlib-exception_handler_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/handler/$(DEPDIR)/minidump_descriptor.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/log/$(DEPDIR)/log.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/microdump_writer/$(DEPDIR)/linux_client_unittest_shlib-microdump_writer_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/microdump_writer/$(DEPDIR)/microdump_writer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-cpu_set_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-directory_reader_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-line_reader_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-linux_core_dumper.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-linux_core_dumper_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-linux_ptrace_dumper_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-minidump_writer_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-minidump_writer_unittest_utils.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/minidump_writer/$(DEPDIR)/linux_core_dumper.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/minidump_writer/$(DEPDIR)/linux_dumper.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/minidump_writer/$(DEPDIR)/linux_dumper_unittest_helper-linux_dumper_unittest_helper.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/minidump_writer/$(DEPDIR)/linux_ptrace_dumper.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/client/linux/minidump_writer/$(DEPDIR)/minidump_writer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/client_linux_linux_client_unittest_shlib-memory_allocator_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/convert_UTF.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/dumper_unittest-byte_cursor_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/dumper_unittest-convert_UTF.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/dumper_unittest-dwarf_cfi_to_module.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/dumper_unittest-dwarf_cfi_to_module_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/dumper_unittest-dwarf_cu_to_module.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/dumper_unittest-dwarf_cu_to_module_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/dumper_unittest-dwarf_line_to_module.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/dumper_unittest-dwarf_line_to_module_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/dumper_unittest-dwarf_range_list_handler.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/dumper_unittest-language.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/dumper_unittest-memory_range_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/dumper_unittest-module.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/dumper_unittest-module_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/dumper_unittest-path_helper.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/dumper_unittest-stabs_reader.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/dumper_unittest-stabs_reader_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/dumper_unittest-stabs_to_module.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/dumper_unittest-stabs_to_module_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/dumper_unittest-string_conversion.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/dumper_unittest-string_conversion_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/dumper_unittest-test_assembler.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/mac_macho_reader_unittest-dwarf_cfi_to_module.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/mac_macho_reader_unittest-dwarf_cu_to_module.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/mac_macho_reader_unittest-dwarf_line_to_module.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/mac_macho_reader_unittest-language.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/mac_macho_reader_unittest-md5.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/mac_macho_reader_unittest-module.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/mac_macho_reader_unittest-path_helper.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/mac_macho_reader_unittest-stabs_reader.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/mac_macho_reader_unittest-stabs_to_module.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/mac_macho_reader_unittest-test_assembler.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/md5.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/path_helper.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/processor_minidump_unittest-test_assembler.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/processor_stackwalker_address_list_unittest-test_assembler.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/processor_stackwalker_amd64_unittest-test_assembler.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/processor_stackwalker_arm64_unittest-test_assembler.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/processor_stackwalker_arm_unittest-test_assembler.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/processor_stackwalker_mips64_unittest-test_assembler.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/processor_stackwalker_mips_unittest-test_assembler.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/processor_stackwalker_x86_unittest-test_assembler.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/processor_synth_minidump_unittest-test_assembler.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/string_conversion.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/test_assembler_unittest-test_assembler.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/test_assembler_unittest-test_assembler_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_cfi_to_module.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_cu_to_module.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_line_to_module.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_range_list_handler.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-language.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-module.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-path_helper.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-stabs_reader.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-stabs_to_module.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_cfi_to_module.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_cu_to_module.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_line_to_module.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_range_list_handler.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-language.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-md5.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-module.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-path_helper.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-stabs_reader.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-stabs_to_module.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/dwarf/$(DEPDIR)/dumper_unittest-bytereader.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/dwarf/$(DEPDIR)/dumper_unittest-bytereader_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/dwarf/$(DEPDIR)/dumper_unittest-cfi_assembler.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2diehandler.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2diehandler_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2reader.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2reader_cfi_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2reader_die_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/dwarf/$(DEPDIR)/dumper_unittest-elf_reader.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/dwarf/$(DEPDIR)/dwarf2reader_lineinfo_unittest-dwarf2reader_lineinfo_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/dwarf/$(DEPDIR)/dwarf2reader_splitfunctions_unittest-dwarf2reader_splitfunctions_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-bytereader.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-cfi_assembler.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-dwarf2diehandler.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-dwarf2reader.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-elf_reader.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-bytereader.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf2diehandler.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf2reader.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-elf_reader.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-bytereader.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf2diehandler.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf2reader.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-elf_reader.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/breakpad_getcontext.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-breakpad_getcontext.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-breakpad_getcontext_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-elf_core_dump.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-linux_libc_support_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/dumper_unittest-crc32.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/dumper_unittest-dump_symbols.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/dumper_unittest-dump_symbols_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/dumper_unittest-elf_core_dump.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/dumper_unittest-elf_core_dump_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/dumper_unittest-elf_symbols_to_module.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/dumper_unittest-elf_symbols_to_module_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/dumper_unittest-elfutils.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/dumper_unittest-file_id.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/dumper_unittest-file_id_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/dumper_unittest-linux_libc_support.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/dumper_unittest-memory_mapped_file.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/dumper_unittest-memory_mapped_file_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/dumper_unittest-safe_readlink.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/dumper_unittest-safe_readlink_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/dumper_unittest-synth_elf.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/dumper_unittest-synth_elf_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/elf_core_dump.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/elfutils.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/file_id.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/google_crashdump_uploader_test-google_crashdump_uploader.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/google_crashdump_uploader_test-google_crashdump_uploader_test.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/google_crashdump_uploader_test-libcurl_wrapper.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/guid_creator.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/http_upload.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/libcurl_wrapper.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/linux_libc_support.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/memory_mapped_file.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/safe_readlink.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/symbol_collector_client.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/symbol_upload.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-crc32.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dump_symbols.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-elf_symbols_to_module.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-elfutils.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-file_id.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-linux_libc_support.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-memory_mapped_file.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-safe_readlink.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/tests/$(DEPDIR)/client_linux_linux_client_unittest_shlib-crash_generator.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/linux/tests/$(DEPDIR)/dumper_unittest-crash_generator.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/mac/$(DEPDIR)/macho_reader_unittest-arch_utilities.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/mac/$(DEPDIR)/macho_reader_unittest-file_id.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_id.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_reader.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_reader_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_utilities.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_walker.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-arch_utilities.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dump_syms.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-file_id.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_id.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_reader.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_utilities.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_walker.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/tests/$(DEPDIR)/client_linux_linux_client_unittest_shlib-file_utils.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/tests/$(DEPDIR)/dumper_unittest-file_utils.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/common/tests/$(DEPDIR)/mac_macho_reader_unittest-file_utils.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/address_map_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/basic_code_modules.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/basic_source_line_resolver.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/basic_source_line_resolver_unittest-basic_source_line_resolver_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/call_stack.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/cfi_frame_info.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/cfi_frame_info_unittest-cfi_frame_info_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-basic_code_modules.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-convert_old_arm64_context.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-dump_context.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-dump_object.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-logging.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-minidump.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-pathname_stripper.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-proc_maps_linux.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/contained_range_map_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/convert_old_arm64_context.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/disassembler_x86.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/disassembler_x86_unittest-disassembler_x86_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/dump_context.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/dump_object.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/exploitability.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/exploitability_linux.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/exploitability_unittest-exploitability_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/exploitability_win.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/fast_source_line_resolver.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/fast_source_line_resolver_unittest-fast_source_line_resolver_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/logging.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/map_serializers_unittest-map_serializers_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/microdump.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/microdump_processor.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/microdump_processor_unittest-microdump_processor_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/microdump_stackwalk.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump_dump.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump_processor.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump_processor_unittest-minidump_processor_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump_stackwalk.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump_unittest-minidump_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/minidump_unittest-synth_minidump.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/module_comparer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/module_serializer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/pathname_stripper.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/pathname_stripper_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/postfix_evaluator_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/proc_maps_linux.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/proc_maps_linux_unittest-proc_maps_linux.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/proc_maps_linux_unittest-proc_maps_linux_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/process_state.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/range_map_truncate_lower_unittest-range_map_truncate_lower_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/range_map_truncate_upper_unittest-range_map_truncate_upper_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/range_map_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/simple_symbol_supplier.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/source_line_resolver_base.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stack_frame_cpu.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stack_frame_symbolizer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalk_common.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_address_list.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_address_list_unittest-stackwalker_address_list_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_amd64.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_amd64_unittest-stackwalker_amd64_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_arm.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_arm64.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_arm64_unittest-stackwalker_arm64_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_arm_unittest-stackwalker_arm_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_mips.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_mips64_unittest-stackwalker_mips64_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_mips_unittest-stackwalker_mips_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_ppc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_ppc64.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_selftest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_sparc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_x86.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/stackwalker_x86_unittest-stackwalker_x86_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/static_address_map_unittest-static_address_map_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/static_contained_range_map_unittest-static_contained_range_map_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/static_map_unittest-static_map_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/static_range_map_unittest-static_range_map_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/symbolic_constants_win.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/synth_minidump_unittest-synth_minidump.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/synth_minidump_unittest-synth_minidump_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/processor/$(DEPDIR)/tokenize.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/testing/googlemock/src/$(DEPDIR)/client_linux_linux_client_unittest_shlib-gmock-all.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/testing/googlemock/src/$(DEPDIR)/libtesting_a-gmock-all.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/testing/googletest/src/$(DEPDIR)/client_linux_linux_client_unittest_shlib-gtest-all.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/testing/googletest/src/$(DEPDIR)/client_linux_linux_client_unittest_shlib-gtest_main.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/testing/googletest/src/$(DEPDIR)/libtesting_a-gtest-all.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/testing/googletest/src/$(DEPDIR)/libtesting_a-gtest_main.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/third_party/libdisasm/$(DEPDIR)/ia32_implicit.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/third_party/libdisasm/$(DEPDIR)/ia32_insn.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/third_party/libdisasm/$(DEPDIR)/ia32_invariant.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/third_party/libdisasm/$(DEPDIR)/ia32_modrm.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/third_party/libdisasm/$(DEPDIR)/ia32_opcode_tables.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/third_party/libdisasm/$(DEPDIR)/ia32_operand.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/third_party/libdisasm/$(DEPDIR)/ia32_reg.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/third_party/libdisasm/$(DEPDIR)/ia32_settings.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/third_party/libdisasm/$(DEPDIR)/x86_disasm.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/third_party/libdisasm/$(DEPDIR)/x86_format.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/third_party/libdisasm/$(DEPDIR)/x86_imm.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/third_party/libdisasm/$(DEPDIR)/x86_insn.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/third_party/libdisasm/$(DEPDIR)/x86_misc.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/third_party/libdisasm/$(DEPDIR)/x86_operand_list.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/tools/linux/core2md/$(DEPDIR)/core2md.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/tools/linux/core_handler/$(DEPDIR)/core_handler.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/tools/linux/dump_syms/$(DEPDIR)/dump_syms-dump_syms.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/tools/linux/md2core/$(DEPDIR)/minidump-2-core.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/tools/linux/md2core/$(DEPDIR)/minidump_2_core_unittest-minidump_memory_range_unittest.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/tools/linux/pid2md/$(DEPDIR)/pid2md.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/tools/linux/symupload/$(DEPDIR)/minidump_upload.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/tools/linux/symupload/$(DEPDIR)/sym_upload.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@src/tools/mac/dump_syms/$(DEPDIR)/dump_syms_mac-dump_syms_tool.Po@am__quote@ # am--include-marker + +$(am__depfiles_remade): + @$(MKDIR_P) $(@D) + @echo '# dummy' >$@-t && $(am__mv) $@-t $@ + +am--depfiles: $(am__depfiles_remade) + +.S.o: +@am__fastdepCCAS_TRUE@ $(AM_V_CPPAS)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCCAS_TRUE@ $(CPPASCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCCAS_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCCAS_FALSE@ $(AM_V_CPPAS)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCCAS_FALSE@ DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCCAS_FALSE@ $(AM_V_CPPAS@am__nodep@)$(CPPASCOMPILE) -c -o $@ $< + +.S.obj: +@am__fastdepCCAS_TRUE@ $(AM_V_CPPAS)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCCAS_TRUE@ $(CPPASCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCCAS_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCCAS_FALSE@ $(AM_V_CPPAS)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCCAS_FALSE@ DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCCAS_FALSE@ $(AM_V_CPPAS@am__nodep@)$(CPPASCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +src/common/linux/client_linux_linux_client_unittest_shlib-breakpad_getcontext.o: src/common/linux/breakpad_getcontext.S +@am__fastdepCCAS_TRUE@ $(AM_V_CPPAS)$(CCAS) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CCASFLAGS) $(CCASFLAGS) -MT src/common/linux/client_linux_linux_client_unittest_shlib-breakpad_getcontext.o -MD -MP -MF src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-breakpad_getcontext.Tpo -c -o src/common/linux/client_linux_linux_client_unittest_shlib-breakpad_getcontext.o `test -f 'src/common/linux/breakpad_getcontext.S' || echo '$(srcdir)/'`src/common/linux/breakpad_getcontext.S +@am__fastdepCCAS_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-breakpad_getcontext.Tpo src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-breakpad_getcontext.Po +@AMDEP_TRUE@@am__fastdepCCAS_FALSE@ $(AM_V_CPPAS)source='src/common/linux/breakpad_getcontext.S' object='src/common/linux/client_linux_linux_client_unittest_shlib-breakpad_getcontext.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCCAS_FALSE@ DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCCAS_FALSE@ $(AM_V_CPPAS@am__nodep@)$(CCAS) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CCASFLAGS) $(CCASFLAGS) -c -o src/common/linux/client_linux_linux_client_unittest_shlib-breakpad_getcontext.o `test -f 'src/common/linux/breakpad_getcontext.S' || echo '$(srcdir)/'`src/common/linux/breakpad_getcontext.S + +src/common/linux/client_linux_linux_client_unittest_shlib-breakpad_getcontext.obj: src/common/linux/breakpad_getcontext.S +@am__fastdepCCAS_TRUE@ $(AM_V_CPPAS)$(CCAS) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CCASFLAGS) $(CCASFLAGS) -MT src/common/linux/client_linux_linux_client_unittest_shlib-breakpad_getcontext.obj -MD -MP -MF src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-breakpad_getcontext.Tpo -c -o src/common/linux/client_linux_linux_client_unittest_shlib-breakpad_getcontext.obj `if test -f 'src/common/linux/breakpad_getcontext.S'; then $(CYGPATH_W) 'src/common/linux/breakpad_getcontext.S'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/breakpad_getcontext.S'; fi` +@am__fastdepCCAS_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-breakpad_getcontext.Tpo src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-breakpad_getcontext.Po +@AMDEP_TRUE@@am__fastdepCCAS_FALSE@ $(AM_V_CPPAS)source='src/common/linux/breakpad_getcontext.S' object='src/common/linux/client_linux_linux_client_unittest_shlib-breakpad_getcontext.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCCAS_FALSE@ DEPDIR=$(DEPDIR) $(CCASDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCCAS_FALSE@ $(AM_V_CPPAS@am__nodep@)$(CCAS) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CCASFLAGS) $(CCASFLAGS) -c -o src/common/linux/client_linux_linux_client_unittest_shlib-breakpad_getcontext.obj `if test -f 'src/common/linux/breakpad_getcontext.S'; then $(CYGPATH_W) 'src/common/linux/breakpad_getcontext.S'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/breakpad_getcontext.S'; fi` + +.c.o: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $< + +.c.obj: +@am__fastdepCC_TRUE@ $(AM_V_CC)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCC_TRUE@ $(COMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCC_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +.cc.o: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ $< + +.cc.obj: +@am__fastdepCXX_TRUE@ $(AM_V_CXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.obj$$||'`;\ +@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ `$(CYGPATH_W) '$<'` &&\ +@am__fastdepCXX_TRUE@ $(am__mv) $$depbase.Tpo $$depbase.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'` + +src/testing/googletest/src/libtesting_a-gtest-all.o: src/testing/googletest/src/gtest-all.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_testing_libtesting_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/testing/googletest/src/libtesting_a-gtest-all.o -MD -MP -MF src/testing/googletest/src/$(DEPDIR)/libtesting_a-gtest-all.Tpo -c -o src/testing/googletest/src/libtesting_a-gtest-all.o `test -f 'src/testing/googletest/src/gtest-all.cc' || echo '$(srcdir)/'`src/testing/googletest/src/gtest-all.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/testing/googletest/src/$(DEPDIR)/libtesting_a-gtest-all.Tpo src/testing/googletest/src/$(DEPDIR)/libtesting_a-gtest-all.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/testing/googletest/src/gtest-all.cc' object='src/testing/googletest/src/libtesting_a-gtest-all.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_testing_libtesting_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/testing/googletest/src/libtesting_a-gtest-all.o `test -f 'src/testing/googletest/src/gtest-all.cc' || echo '$(srcdir)/'`src/testing/googletest/src/gtest-all.cc + +src/testing/googletest/src/libtesting_a-gtest-all.obj: src/testing/googletest/src/gtest-all.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_testing_libtesting_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/testing/googletest/src/libtesting_a-gtest-all.obj -MD -MP -MF src/testing/googletest/src/$(DEPDIR)/libtesting_a-gtest-all.Tpo -c -o src/testing/googletest/src/libtesting_a-gtest-all.obj `if test -f 'src/testing/googletest/src/gtest-all.cc'; then $(CYGPATH_W) 'src/testing/googletest/src/gtest-all.cc'; else $(CYGPATH_W) '$(srcdir)/src/testing/googletest/src/gtest-all.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/testing/googletest/src/$(DEPDIR)/libtesting_a-gtest-all.Tpo src/testing/googletest/src/$(DEPDIR)/libtesting_a-gtest-all.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/testing/googletest/src/gtest-all.cc' object='src/testing/googletest/src/libtesting_a-gtest-all.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_testing_libtesting_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/testing/googletest/src/libtesting_a-gtest-all.obj `if test -f 'src/testing/googletest/src/gtest-all.cc'; then $(CYGPATH_W) 'src/testing/googletest/src/gtest-all.cc'; else $(CYGPATH_W) '$(srcdir)/src/testing/googletest/src/gtest-all.cc'; fi` + +src/testing/googletest/src/libtesting_a-gtest_main.o: src/testing/googletest/src/gtest_main.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_testing_libtesting_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/testing/googletest/src/libtesting_a-gtest_main.o -MD -MP -MF src/testing/googletest/src/$(DEPDIR)/libtesting_a-gtest_main.Tpo -c -o src/testing/googletest/src/libtesting_a-gtest_main.o `test -f 'src/testing/googletest/src/gtest_main.cc' || echo '$(srcdir)/'`src/testing/googletest/src/gtest_main.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/testing/googletest/src/$(DEPDIR)/libtesting_a-gtest_main.Tpo src/testing/googletest/src/$(DEPDIR)/libtesting_a-gtest_main.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/testing/googletest/src/gtest_main.cc' object='src/testing/googletest/src/libtesting_a-gtest_main.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_testing_libtesting_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/testing/googletest/src/libtesting_a-gtest_main.o `test -f 'src/testing/googletest/src/gtest_main.cc' || echo '$(srcdir)/'`src/testing/googletest/src/gtest_main.cc + +src/testing/googletest/src/libtesting_a-gtest_main.obj: src/testing/googletest/src/gtest_main.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_testing_libtesting_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/testing/googletest/src/libtesting_a-gtest_main.obj -MD -MP -MF src/testing/googletest/src/$(DEPDIR)/libtesting_a-gtest_main.Tpo -c -o src/testing/googletest/src/libtesting_a-gtest_main.obj `if test -f 'src/testing/googletest/src/gtest_main.cc'; then $(CYGPATH_W) 'src/testing/googletest/src/gtest_main.cc'; else $(CYGPATH_W) '$(srcdir)/src/testing/googletest/src/gtest_main.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/testing/googletest/src/$(DEPDIR)/libtesting_a-gtest_main.Tpo src/testing/googletest/src/$(DEPDIR)/libtesting_a-gtest_main.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/testing/googletest/src/gtest_main.cc' object='src/testing/googletest/src/libtesting_a-gtest_main.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_testing_libtesting_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/testing/googletest/src/libtesting_a-gtest_main.obj `if test -f 'src/testing/googletest/src/gtest_main.cc'; then $(CYGPATH_W) 'src/testing/googletest/src/gtest_main.cc'; else $(CYGPATH_W) '$(srcdir)/src/testing/googletest/src/gtest_main.cc'; fi` + +src/testing/googlemock/src/libtesting_a-gmock-all.o: src/testing/googlemock/src/gmock-all.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_testing_libtesting_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/testing/googlemock/src/libtesting_a-gmock-all.o -MD -MP -MF src/testing/googlemock/src/$(DEPDIR)/libtesting_a-gmock-all.Tpo -c -o src/testing/googlemock/src/libtesting_a-gmock-all.o `test -f 'src/testing/googlemock/src/gmock-all.cc' || echo '$(srcdir)/'`src/testing/googlemock/src/gmock-all.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/testing/googlemock/src/$(DEPDIR)/libtesting_a-gmock-all.Tpo src/testing/googlemock/src/$(DEPDIR)/libtesting_a-gmock-all.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/testing/googlemock/src/gmock-all.cc' object='src/testing/googlemock/src/libtesting_a-gmock-all.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_testing_libtesting_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/testing/googlemock/src/libtesting_a-gmock-all.o `test -f 'src/testing/googlemock/src/gmock-all.cc' || echo '$(srcdir)/'`src/testing/googlemock/src/gmock-all.cc + +src/testing/googlemock/src/libtesting_a-gmock-all.obj: src/testing/googlemock/src/gmock-all.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_testing_libtesting_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/testing/googlemock/src/libtesting_a-gmock-all.obj -MD -MP -MF src/testing/googlemock/src/$(DEPDIR)/libtesting_a-gmock-all.Tpo -c -o src/testing/googlemock/src/libtesting_a-gmock-all.obj `if test -f 'src/testing/googlemock/src/gmock-all.cc'; then $(CYGPATH_W) 'src/testing/googlemock/src/gmock-all.cc'; else $(CYGPATH_W) '$(srcdir)/src/testing/googlemock/src/gmock-all.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/testing/googlemock/src/$(DEPDIR)/libtesting_a-gmock-all.Tpo src/testing/googlemock/src/$(DEPDIR)/libtesting_a-gmock-all.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/testing/googlemock/src/gmock-all.cc' object='src/testing/googlemock/src/libtesting_a-gmock-all.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_testing_libtesting_a_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/testing/googlemock/src/libtesting_a-gmock-all.obj `if test -f 'src/testing/googlemock/src/gmock-all.cc'; then $(CYGPATH_W) 'src/testing/googlemock/src/gmock-all.cc'; else $(CYGPATH_W) '$(srcdir)/src/testing/googlemock/src/gmock-all.cc'; fi` + +src/testing/googletest/src/client_linux_linux_client_unittest_shlib-gtest-all.o: src/testing/googletest/src/gtest-all.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/testing/googletest/src/client_linux_linux_client_unittest_shlib-gtest-all.o -MD -MP -MF src/testing/googletest/src/$(DEPDIR)/client_linux_linux_client_unittest_shlib-gtest-all.Tpo -c -o src/testing/googletest/src/client_linux_linux_client_unittest_shlib-gtest-all.o `test -f 'src/testing/googletest/src/gtest-all.cc' || echo '$(srcdir)/'`src/testing/googletest/src/gtest-all.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/testing/googletest/src/$(DEPDIR)/client_linux_linux_client_unittest_shlib-gtest-all.Tpo src/testing/googletest/src/$(DEPDIR)/client_linux_linux_client_unittest_shlib-gtest-all.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/testing/googletest/src/gtest-all.cc' object='src/testing/googletest/src/client_linux_linux_client_unittest_shlib-gtest-all.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/testing/googletest/src/client_linux_linux_client_unittest_shlib-gtest-all.o `test -f 'src/testing/googletest/src/gtest-all.cc' || echo '$(srcdir)/'`src/testing/googletest/src/gtest-all.cc + +src/testing/googletest/src/client_linux_linux_client_unittest_shlib-gtest-all.obj: src/testing/googletest/src/gtest-all.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/testing/googletest/src/client_linux_linux_client_unittest_shlib-gtest-all.obj -MD -MP -MF src/testing/googletest/src/$(DEPDIR)/client_linux_linux_client_unittest_shlib-gtest-all.Tpo -c -o src/testing/googletest/src/client_linux_linux_client_unittest_shlib-gtest-all.obj `if test -f 'src/testing/googletest/src/gtest-all.cc'; then $(CYGPATH_W) 'src/testing/googletest/src/gtest-all.cc'; else $(CYGPATH_W) '$(srcdir)/src/testing/googletest/src/gtest-all.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/testing/googletest/src/$(DEPDIR)/client_linux_linux_client_unittest_shlib-gtest-all.Tpo src/testing/googletest/src/$(DEPDIR)/client_linux_linux_client_unittest_shlib-gtest-all.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/testing/googletest/src/gtest-all.cc' object='src/testing/googletest/src/client_linux_linux_client_unittest_shlib-gtest-all.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/testing/googletest/src/client_linux_linux_client_unittest_shlib-gtest-all.obj `if test -f 'src/testing/googletest/src/gtest-all.cc'; then $(CYGPATH_W) 'src/testing/googletest/src/gtest-all.cc'; else $(CYGPATH_W) '$(srcdir)/src/testing/googletest/src/gtest-all.cc'; fi` + +src/testing/googletest/src/client_linux_linux_client_unittest_shlib-gtest_main.o: src/testing/googletest/src/gtest_main.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/testing/googletest/src/client_linux_linux_client_unittest_shlib-gtest_main.o -MD -MP -MF src/testing/googletest/src/$(DEPDIR)/client_linux_linux_client_unittest_shlib-gtest_main.Tpo -c -o src/testing/googletest/src/client_linux_linux_client_unittest_shlib-gtest_main.o `test -f 'src/testing/googletest/src/gtest_main.cc' || echo '$(srcdir)/'`src/testing/googletest/src/gtest_main.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/testing/googletest/src/$(DEPDIR)/client_linux_linux_client_unittest_shlib-gtest_main.Tpo src/testing/googletest/src/$(DEPDIR)/client_linux_linux_client_unittest_shlib-gtest_main.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/testing/googletest/src/gtest_main.cc' object='src/testing/googletest/src/client_linux_linux_client_unittest_shlib-gtest_main.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/testing/googletest/src/client_linux_linux_client_unittest_shlib-gtest_main.o `test -f 'src/testing/googletest/src/gtest_main.cc' || echo '$(srcdir)/'`src/testing/googletest/src/gtest_main.cc + +src/testing/googletest/src/client_linux_linux_client_unittest_shlib-gtest_main.obj: src/testing/googletest/src/gtest_main.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/testing/googletest/src/client_linux_linux_client_unittest_shlib-gtest_main.obj -MD -MP -MF src/testing/googletest/src/$(DEPDIR)/client_linux_linux_client_unittest_shlib-gtest_main.Tpo -c -o src/testing/googletest/src/client_linux_linux_client_unittest_shlib-gtest_main.obj `if test -f 'src/testing/googletest/src/gtest_main.cc'; then $(CYGPATH_W) 'src/testing/googletest/src/gtest_main.cc'; else $(CYGPATH_W) '$(srcdir)/src/testing/googletest/src/gtest_main.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/testing/googletest/src/$(DEPDIR)/client_linux_linux_client_unittest_shlib-gtest_main.Tpo src/testing/googletest/src/$(DEPDIR)/client_linux_linux_client_unittest_shlib-gtest_main.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/testing/googletest/src/gtest_main.cc' object='src/testing/googletest/src/client_linux_linux_client_unittest_shlib-gtest_main.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/testing/googletest/src/client_linux_linux_client_unittest_shlib-gtest_main.obj `if test -f 'src/testing/googletest/src/gtest_main.cc'; then $(CYGPATH_W) 'src/testing/googletest/src/gtest_main.cc'; else $(CYGPATH_W) '$(srcdir)/src/testing/googletest/src/gtest_main.cc'; fi` + +src/testing/googlemock/src/client_linux_linux_client_unittest_shlib-gmock-all.o: src/testing/googlemock/src/gmock-all.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/testing/googlemock/src/client_linux_linux_client_unittest_shlib-gmock-all.o -MD -MP -MF src/testing/googlemock/src/$(DEPDIR)/client_linux_linux_client_unittest_shlib-gmock-all.Tpo -c -o src/testing/googlemock/src/client_linux_linux_client_unittest_shlib-gmock-all.o `test -f 'src/testing/googlemock/src/gmock-all.cc' || echo '$(srcdir)/'`src/testing/googlemock/src/gmock-all.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/testing/googlemock/src/$(DEPDIR)/client_linux_linux_client_unittest_shlib-gmock-all.Tpo src/testing/googlemock/src/$(DEPDIR)/client_linux_linux_client_unittest_shlib-gmock-all.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/testing/googlemock/src/gmock-all.cc' object='src/testing/googlemock/src/client_linux_linux_client_unittest_shlib-gmock-all.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/testing/googlemock/src/client_linux_linux_client_unittest_shlib-gmock-all.o `test -f 'src/testing/googlemock/src/gmock-all.cc' || echo '$(srcdir)/'`src/testing/googlemock/src/gmock-all.cc + +src/testing/googlemock/src/client_linux_linux_client_unittest_shlib-gmock-all.obj: src/testing/googlemock/src/gmock-all.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/testing/googlemock/src/client_linux_linux_client_unittest_shlib-gmock-all.obj -MD -MP -MF src/testing/googlemock/src/$(DEPDIR)/client_linux_linux_client_unittest_shlib-gmock-all.Tpo -c -o src/testing/googlemock/src/client_linux_linux_client_unittest_shlib-gmock-all.obj `if test -f 'src/testing/googlemock/src/gmock-all.cc'; then $(CYGPATH_W) 'src/testing/googlemock/src/gmock-all.cc'; else $(CYGPATH_W) '$(srcdir)/src/testing/googlemock/src/gmock-all.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/testing/googlemock/src/$(DEPDIR)/client_linux_linux_client_unittest_shlib-gmock-all.Tpo src/testing/googlemock/src/$(DEPDIR)/client_linux_linux_client_unittest_shlib-gmock-all.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/testing/googlemock/src/gmock-all.cc' object='src/testing/googlemock/src/client_linux_linux_client_unittest_shlib-gmock-all.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/testing/googlemock/src/client_linux_linux_client_unittest_shlib-gmock-all.obj `if test -f 'src/testing/googlemock/src/gmock-all.cc'; then $(CYGPATH_W) 'src/testing/googlemock/src/gmock-all.cc'; else $(CYGPATH_W) '$(srcdir)/src/testing/googlemock/src/gmock-all.cc'; fi` + +src/client/linux/handler/linux_client_unittest_shlib-exception_handler_unittest.o: src/client/linux/handler/exception_handler_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/client/linux/handler/linux_client_unittest_shlib-exception_handler_unittest.o -MD -MP -MF src/client/linux/handler/$(DEPDIR)/linux_client_unittest_shlib-exception_handler_unittest.Tpo -c -o src/client/linux/handler/linux_client_unittest_shlib-exception_handler_unittest.o `test -f 'src/client/linux/handler/exception_handler_unittest.cc' || echo '$(srcdir)/'`src/client/linux/handler/exception_handler_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/client/linux/handler/$(DEPDIR)/linux_client_unittest_shlib-exception_handler_unittest.Tpo src/client/linux/handler/$(DEPDIR)/linux_client_unittest_shlib-exception_handler_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/client/linux/handler/exception_handler_unittest.cc' object='src/client/linux/handler/linux_client_unittest_shlib-exception_handler_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/client/linux/handler/linux_client_unittest_shlib-exception_handler_unittest.o `test -f 'src/client/linux/handler/exception_handler_unittest.cc' || echo '$(srcdir)/'`src/client/linux/handler/exception_handler_unittest.cc + +src/client/linux/handler/linux_client_unittest_shlib-exception_handler_unittest.obj: src/client/linux/handler/exception_handler_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/client/linux/handler/linux_client_unittest_shlib-exception_handler_unittest.obj -MD -MP -MF src/client/linux/handler/$(DEPDIR)/linux_client_unittest_shlib-exception_handler_unittest.Tpo -c -o src/client/linux/handler/linux_client_unittest_shlib-exception_handler_unittest.obj `if test -f 'src/client/linux/handler/exception_handler_unittest.cc'; then $(CYGPATH_W) 'src/client/linux/handler/exception_handler_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/client/linux/handler/exception_handler_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/client/linux/handler/$(DEPDIR)/linux_client_unittest_shlib-exception_handler_unittest.Tpo src/client/linux/handler/$(DEPDIR)/linux_client_unittest_shlib-exception_handler_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/client/linux/handler/exception_handler_unittest.cc' object='src/client/linux/handler/linux_client_unittest_shlib-exception_handler_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/client/linux/handler/linux_client_unittest_shlib-exception_handler_unittest.obj `if test -f 'src/client/linux/handler/exception_handler_unittest.cc'; then $(CYGPATH_W) 'src/client/linux/handler/exception_handler_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/client/linux/handler/exception_handler_unittest.cc'; fi` + +src/client/linux/microdump_writer/linux_client_unittest_shlib-microdump_writer_unittest.o: src/client/linux/microdump_writer/microdump_writer_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/client/linux/microdump_writer/linux_client_unittest_shlib-microdump_writer_unittest.o -MD -MP -MF src/client/linux/microdump_writer/$(DEPDIR)/linux_client_unittest_shlib-microdump_writer_unittest.Tpo -c -o src/client/linux/microdump_writer/linux_client_unittest_shlib-microdump_writer_unittest.o `test -f 'src/client/linux/microdump_writer/microdump_writer_unittest.cc' || echo '$(srcdir)/'`src/client/linux/microdump_writer/microdump_writer_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/client/linux/microdump_writer/$(DEPDIR)/linux_client_unittest_shlib-microdump_writer_unittest.Tpo src/client/linux/microdump_writer/$(DEPDIR)/linux_client_unittest_shlib-microdump_writer_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/client/linux/microdump_writer/microdump_writer_unittest.cc' object='src/client/linux/microdump_writer/linux_client_unittest_shlib-microdump_writer_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/client/linux/microdump_writer/linux_client_unittest_shlib-microdump_writer_unittest.o `test -f 'src/client/linux/microdump_writer/microdump_writer_unittest.cc' || echo '$(srcdir)/'`src/client/linux/microdump_writer/microdump_writer_unittest.cc + +src/client/linux/microdump_writer/linux_client_unittest_shlib-microdump_writer_unittest.obj: src/client/linux/microdump_writer/microdump_writer_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/client/linux/microdump_writer/linux_client_unittest_shlib-microdump_writer_unittest.obj -MD -MP -MF src/client/linux/microdump_writer/$(DEPDIR)/linux_client_unittest_shlib-microdump_writer_unittest.Tpo -c -o src/client/linux/microdump_writer/linux_client_unittest_shlib-microdump_writer_unittest.obj `if test -f 'src/client/linux/microdump_writer/microdump_writer_unittest.cc'; then $(CYGPATH_W) 'src/client/linux/microdump_writer/microdump_writer_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/client/linux/microdump_writer/microdump_writer_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/client/linux/microdump_writer/$(DEPDIR)/linux_client_unittest_shlib-microdump_writer_unittest.Tpo src/client/linux/microdump_writer/$(DEPDIR)/linux_client_unittest_shlib-microdump_writer_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/client/linux/microdump_writer/microdump_writer_unittest.cc' object='src/client/linux/microdump_writer/linux_client_unittest_shlib-microdump_writer_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/client/linux/microdump_writer/linux_client_unittest_shlib-microdump_writer_unittest.obj `if test -f 'src/client/linux/microdump_writer/microdump_writer_unittest.cc'; then $(CYGPATH_W) 'src/client/linux/microdump_writer/microdump_writer_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/client/linux/microdump_writer/microdump_writer_unittest.cc'; fi` + +src/client/linux/minidump_writer/linux_client_unittest_shlib-directory_reader_unittest.o: src/client/linux/minidump_writer/directory_reader_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/client/linux/minidump_writer/linux_client_unittest_shlib-directory_reader_unittest.o -MD -MP -MF src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-directory_reader_unittest.Tpo -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-directory_reader_unittest.o `test -f 'src/client/linux/minidump_writer/directory_reader_unittest.cc' || echo '$(srcdir)/'`src/client/linux/minidump_writer/directory_reader_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-directory_reader_unittest.Tpo src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-directory_reader_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/client/linux/minidump_writer/directory_reader_unittest.cc' object='src/client/linux/minidump_writer/linux_client_unittest_shlib-directory_reader_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-directory_reader_unittest.o `test -f 'src/client/linux/minidump_writer/directory_reader_unittest.cc' || echo '$(srcdir)/'`src/client/linux/minidump_writer/directory_reader_unittest.cc + +src/client/linux/minidump_writer/linux_client_unittest_shlib-directory_reader_unittest.obj: src/client/linux/minidump_writer/directory_reader_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/client/linux/minidump_writer/linux_client_unittest_shlib-directory_reader_unittest.obj -MD -MP -MF src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-directory_reader_unittest.Tpo -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-directory_reader_unittest.obj `if test -f 'src/client/linux/minidump_writer/directory_reader_unittest.cc'; then $(CYGPATH_W) 'src/client/linux/minidump_writer/directory_reader_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/client/linux/minidump_writer/directory_reader_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-directory_reader_unittest.Tpo src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-directory_reader_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/client/linux/minidump_writer/directory_reader_unittest.cc' object='src/client/linux/minidump_writer/linux_client_unittest_shlib-directory_reader_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-directory_reader_unittest.obj `if test -f 'src/client/linux/minidump_writer/directory_reader_unittest.cc'; then $(CYGPATH_W) 'src/client/linux/minidump_writer/directory_reader_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/client/linux/minidump_writer/directory_reader_unittest.cc'; fi` + +src/client/linux/minidump_writer/linux_client_unittest_shlib-cpu_set_unittest.o: src/client/linux/minidump_writer/cpu_set_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/client/linux/minidump_writer/linux_client_unittest_shlib-cpu_set_unittest.o -MD -MP -MF src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-cpu_set_unittest.Tpo -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-cpu_set_unittest.o `test -f 'src/client/linux/minidump_writer/cpu_set_unittest.cc' || echo '$(srcdir)/'`src/client/linux/minidump_writer/cpu_set_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-cpu_set_unittest.Tpo src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-cpu_set_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/client/linux/minidump_writer/cpu_set_unittest.cc' object='src/client/linux/minidump_writer/linux_client_unittest_shlib-cpu_set_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-cpu_set_unittest.o `test -f 'src/client/linux/minidump_writer/cpu_set_unittest.cc' || echo '$(srcdir)/'`src/client/linux/minidump_writer/cpu_set_unittest.cc + +src/client/linux/minidump_writer/linux_client_unittest_shlib-cpu_set_unittest.obj: src/client/linux/minidump_writer/cpu_set_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/client/linux/minidump_writer/linux_client_unittest_shlib-cpu_set_unittest.obj -MD -MP -MF src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-cpu_set_unittest.Tpo -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-cpu_set_unittest.obj `if test -f 'src/client/linux/minidump_writer/cpu_set_unittest.cc'; then $(CYGPATH_W) 'src/client/linux/minidump_writer/cpu_set_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/client/linux/minidump_writer/cpu_set_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-cpu_set_unittest.Tpo src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-cpu_set_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/client/linux/minidump_writer/cpu_set_unittest.cc' object='src/client/linux/minidump_writer/linux_client_unittest_shlib-cpu_set_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-cpu_set_unittest.obj `if test -f 'src/client/linux/minidump_writer/cpu_set_unittest.cc'; then $(CYGPATH_W) 'src/client/linux/minidump_writer/cpu_set_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/client/linux/minidump_writer/cpu_set_unittest.cc'; fi` + +src/client/linux/minidump_writer/linux_client_unittest_shlib-line_reader_unittest.o: src/client/linux/minidump_writer/line_reader_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/client/linux/minidump_writer/linux_client_unittest_shlib-line_reader_unittest.o -MD -MP -MF src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-line_reader_unittest.Tpo -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-line_reader_unittest.o `test -f 'src/client/linux/minidump_writer/line_reader_unittest.cc' || echo '$(srcdir)/'`src/client/linux/minidump_writer/line_reader_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-line_reader_unittest.Tpo src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-line_reader_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/client/linux/minidump_writer/line_reader_unittest.cc' object='src/client/linux/minidump_writer/linux_client_unittest_shlib-line_reader_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-line_reader_unittest.o `test -f 'src/client/linux/minidump_writer/line_reader_unittest.cc' || echo '$(srcdir)/'`src/client/linux/minidump_writer/line_reader_unittest.cc + +src/client/linux/minidump_writer/linux_client_unittest_shlib-line_reader_unittest.obj: src/client/linux/minidump_writer/line_reader_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/client/linux/minidump_writer/linux_client_unittest_shlib-line_reader_unittest.obj -MD -MP -MF src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-line_reader_unittest.Tpo -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-line_reader_unittest.obj `if test -f 'src/client/linux/minidump_writer/line_reader_unittest.cc'; then $(CYGPATH_W) 'src/client/linux/minidump_writer/line_reader_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/client/linux/minidump_writer/line_reader_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-line_reader_unittest.Tpo src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-line_reader_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/client/linux/minidump_writer/line_reader_unittest.cc' object='src/client/linux/minidump_writer/linux_client_unittest_shlib-line_reader_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-line_reader_unittest.obj `if test -f 'src/client/linux/minidump_writer/line_reader_unittest.cc'; then $(CYGPATH_W) 'src/client/linux/minidump_writer/line_reader_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/client/linux/minidump_writer/line_reader_unittest.cc'; fi` + +src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_core_dumper.o: src/client/linux/minidump_writer/linux_core_dumper.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_core_dumper.o -MD -MP -MF src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-linux_core_dumper.Tpo -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_core_dumper.o `test -f 'src/client/linux/minidump_writer/linux_core_dumper.cc' || echo '$(srcdir)/'`src/client/linux/minidump_writer/linux_core_dumper.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-linux_core_dumper.Tpo src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-linux_core_dumper.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/client/linux/minidump_writer/linux_core_dumper.cc' object='src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_core_dumper.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_core_dumper.o `test -f 'src/client/linux/minidump_writer/linux_core_dumper.cc' || echo '$(srcdir)/'`src/client/linux/minidump_writer/linux_core_dumper.cc + +src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_core_dumper.obj: src/client/linux/minidump_writer/linux_core_dumper.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_core_dumper.obj -MD -MP -MF src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-linux_core_dumper.Tpo -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_core_dumper.obj `if test -f 'src/client/linux/minidump_writer/linux_core_dumper.cc'; then $(CYGPATH_W) 'src/client/linux/minidump_writer/linux_core_dumper.cc'; else $(CYGPATH_W) '$(srcdir)/src/client/linux/minidump_writer/linux_core_dumper.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-linux_core_dumper.Tpo src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-linux_core_dumper.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/client/linux/minidump_writer/linux_core_dumper.cc' object='src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_core_dumper.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_core_dumper.obj `if test -f 'src/client/linux/minidump_writer/linux_core_dumper.cc'; then $(CYGPATH_W) 'src/client/linux/minidump_writer/linux_core_dumper.cc'; else $(CYGPATH_W) '$(srcdir)/src/client/linux/minidump_writer/linux_core_dumper.cc'; fi` + +src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_core_dumper_unittest.o: src/client/linux/minidump_writer/linux_core_dumper_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_core_dumper_unittest.o -MD -MP -MF src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-linux_core_dumper_unittest.Tpo -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_core_dumper_unittest.o `test -f 'src/client/linux/minidump_writer/linux_core_dumper_unittest.cc' || echo '$(srcdir)/'`src/client/linux/minidump_writer/linux_core_dumper_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-linux_core_dumper_unittest.Tpo src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-linux_core_dumper_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/client/linux/minidump_writer/linux_core_dumper_unittest.cc' object='src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_core_dumper_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_core_dumper_unittest.o `test -f 'src/client/linux/minidump_writer/linux_core_dumper_unittest.cc' || echo '$(srcdir)/'`src/client/linux/minidump_writer/linux_core_dumper_unittest.cc + +src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_core_dumper_unittest.obj: src/client/linux/minidump_writer/linux_core_dumper_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_core_dumper_unittest.obj -MD -MP -MF src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-linux_core_dumper_unittest.Tpo -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_core_dumper_unittest.obj `if test -f 'src/client/linux/minidump_writer/linux_core_dumper_unittest.cc'; then $(CYGPATH_W) 'src/client/linux/minidump_writer/linux_core_dumper_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/client/linux/minidump_writer/linux_core_dumper_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-linux_core_dumper_unittest.Tpo src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-linux_core_dumper_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/client/linux/minidump_writer/linux_core_dumper_unittest.cc' object='src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_core_dumper_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_core_dumper_unittest.obj `if test -f 'src/client/linux/minidump_writer/linux_core_dumper_unittest.cc'; then $(CYGPATH_W) 'src/client/linux/minidump_writer/linux_core_dumper_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/client/linux/minidump_writer/linux_core_dumper_unittest.cc'; fi` + +src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_ptrace_dumper_unittest.o: src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_ptrace_dumper_unittest.o -MD -MP -MF src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-linux_ptrace_dumper_unittest.Tpo -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_ptrace_dumper_unittest.o `test -f 'src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc' || echo '$(srcdir)/'`src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-linux_ptrace_dumper_unittest.Tpo src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-linux_ptrace_dumper_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc' object='src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_ptrace_dumper_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_ptrace_dumper_unittest.o `test -f 'src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc' || echo '$(srcdir)/'`src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc + +src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_ptrace_dumper_unittest.obj: src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_ptrace_dumper_unittest.obj -MD -MP -MF src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-linux_ptrace_dumper_unittest.Tpo -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_ptrace_dumper_unittest.obj `if test -f 'src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc'; then $(CYGPATH_W) 'src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-linux_ptrace_dumper_unittest.Tpo src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-linux_ptrace_dumper_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc' object='src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_ptrace_dumper_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-linux_ptrace_dumper_unittest.obj `if test -f 'src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc'; then $(CYGPATH_W) 'src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc'; fi` + +src/client/linux/minidump_writer/linux_client_unittest_shlib-minidump_writer_unittest.o: src/client/linux/minidump_writer/minidump_writer_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/client/linux/minidump_writer/linux_client_unittest_shlib-minidump_writer_unittest.o -MD -MP -MF src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-minidump_writer_unittest.Tpo -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-minidump_writer_unittest.o `test -f 'src/client/linux/minidump_writer/minidump_writer_unittest.cc' || echo '$(srcdir)/'`src/client/linux/minidump_writer/minidump_writer_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-minidump_writer_unittest.Tpo src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-minidump_writer_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/client/linux/minidump_writer/minidump_writer_unittest.cc' object='src/client/linux/minidump_writer/linux_client_unittest_shlib-minidump_writer_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-minidump_writer_unittest.o `test -f 'src/client/linux/minidump_writer/minidump_writer_unittest.cc' || echo '$(srcdir)/'`src/client/linux/minidump_writer/minidump_writer_unittest.cc + +src/client/linux/minidump_writer/linux_client_unittest_shlib-minidump_writer_unittest.obj: src/client/linux/minidump_writer/minidump_writer_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/client/linux/minidump_writer/linux_client_unittest_shlib-minidump_writer_unittest.obj -MD -MP -MF src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-minidump_writer_unittest.Tpo -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-minidump_writer_unittest.obj `if test -f 'src/client/linux/minidump_writer/minidump_writer_unittest.cc'; then $(CYGPATH_W) 'src/client/linux/minidump_writer/minidump_writer_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/client/linux/minidump_writer/minidump_writer_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-minidump_writer_unittest.Tpo src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-minidump_writer_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/client/linux/minidump_writer/minidump_writer_unittest.cc' object='src/client/linux/minidump_writer/linux_client_unittest_shlib-minidump_writer_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-minidump_writer_unittest.obj `if test -f 'src/client/linux/minidump_writer/minidump_writer_unittest.cc'; then $(CYGPATH_W) 'src/client/linux/minidump_writer/minidump_writer_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/client/linux/minidump_writer/minidump_writer_unittest.cc'; fi` + +src/client/linux/minidump_writer/linux_client_unittest_shlib-minidump_writer_unittest_utils.o: src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/client/linux/minidump_writer/linux_client_unittest_shlib-minidump_writer_unittest_utils.o -MD -MP -MF src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-minidump_writer_unittest_utils.Tpo -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-minidump_writer_unittest_utils.o `test -f 'src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc' || echo '$(srcdir)/'`src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-minidump_writer_unittest_utils.Tpo src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-minidump_writer_unittest_utils.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc' object='src/client/linux/minidump_writer/linux_client_unittest_shlib-minidump_writer_unittest_utils.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-minidump_writer_unittest_utils.o `test -f 'src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc' || echo '$(srcdir)/'`src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc + +src/client/linux/minidump_writer/linux_client_unittest_shlib-minidump_writer_unittest_utils.obj: src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/client/linux/minidump_writer/linux_client_unittest_shlib-minidump_writer_unittest_utils.obj -MD -MP -MF src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-minidump_writer_unittest_utils.Tpo -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-minidump_writer_unittest_utils.obj `if test -f 'src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc'; then $(CYGPATH_W) 'src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc'; else $(CYGPATH_W) '$(srcdir)/src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-minidump_writer_unittest_utils.Tpo src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-minidump_writer_unittest_utils.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc' object='src/client/linux/minidump_writer/linux_client_unittest_shlib-minidump_writer_unittest_utils.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-minidump_writer_unittest_utils.obj `if test -f 'src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc'; then $(CYGPATH_W) 'src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc'; else $(CYGPATH_W) '$(srcdir)/src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc'; fi` + +src/client/linux/minidump_writer/linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.o: src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/client/linux/minidump_writer/linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.o -MD -MP -MF src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.Tpo -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.o `test -f 'src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc' || echo '$(srcdir)/'`src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.Tpo src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc' object='src/client/linux/minidump_writer/linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.o `test -f 'src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc' || echo '$(srcdir)/'`src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc + +src/client/linux/minidump_writer/linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.obj: src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/client/linux/minidump_writer/linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.obj -MD -MP -MF src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.Tpo -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.obj `if test -f 'src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc'; then $(CYGPATH_W) 'src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.Tpo src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc' object='src/client/linux/minidump_writer/linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/client/linux/minidump_writer/linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.obj `if test -f 'src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc'; then $(CYGPATH_W) 'src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc'; fi` + +src/common/linux/client_linux_linux_client_unittest_shlib-elf_core_dump.o: src/common/linux/elf_core_dump.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/client_linux_linux_client_unittest_shlib-elf_core_dump.o -MD -MP -MF src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-elf_core_dump.Tpo -c -o src/common/linux/client_linux_linux_client_unittest_shlib-elf_core_dump.o `test -f 'src/common/linux/elf_core_dump.cc' || echo '$(srcdir)/'`src/common/linux/elf_core_dump.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-elf_core_dump.Tpo src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-elf_core_dump.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/elf_core_dump.cc' object='src/common/linux/client_linux_linux_client_unittest_shlib-elf_core_dump.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/client_linux_linux_client_unittest_shlib-elf_core_dump.o `test -f 'src/common/linux/elf_core_dump.cc' || echo '$(srcdir)/'`src/common/linux/elf_core_dump.cc + +src/common/linux/client_linux_linux_client_unittest_shlib-elf_core_dump.obj: src/common/linux/elf_core_dump.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/client_linux_linux_client_unittest_shlib-elf_core_dump.obj -MD -MP -MF src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-elf_core_dump.Tpo -c -o src/common/linux/client_linux_linux_client_unittest_shlib-elf_core_dump.obj `if test -f 'src/common/linux/elf_core_dump.cc'; then $(CYGPATH_W) 'src/common/linux/elf_core_dump.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/elf_core_dump.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-elf_core_dump.Tpo src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-elf_core_dump.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/elf_core_dump.cc' object='src/common/linux/client_linux_linux_client_unittest_shlib-elf_core_dump.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/client_linux_linux_client_unittest_shlib-elf_core_dump.obj `if test -f 'src/common/linux/elf_core_dump.cc'; then $(CYGPATH_W) 'src/common/linux/elf_core_dump.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/elf_core_dump.cc'; fi` + +src/common/linux/client_linux_linux_client_unittest_shlib-linux_libc_support_unittest.o: src/common/linux/linux_libc_support_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/client_linux_linux_client_unittest_shlib-linux_libc_support_unittest.o -MD -MP -MF src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-linux_libc_support_unittest.Tpo -c -o src/common/linux/client_linux_linux_client_unittest_shlib-linux_libc_support_unittest.o `test -f 'src/common/linux/linux_libc_support_unittest.cc' || echo '$(srcdir)/'`src/common/linux/linux_libc_support_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-linux_libc_support_unittest.Tpo src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-linux_libc_support_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/linux_libc_support_unittest.cc' object='src/common/linux/client_linux_linux_client_unittest_shlib-linux_libc_support_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/client_linux_linux_client_unittest_shlib-linux_libc_support_unittest.o `test -f 'src/common/linux/linux_libc_support_unittest.cc' || echo '$(srcdir)/'`src/common/linux/linux_libc_support_unittest.cc + +src/common/linux/client_linux_linux_client_unittest_shlib-linux_libc_support_unittest.obj: src/common/linux/linux_libc_support_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/client_linux_linux_client_unittest_shlib-linux_libc_support_unittest.obj -MD -MP -MF src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-linux_libc_support_unittest.Tpo -c -o src/common/linux/client_linux_linux_client_unittest_shlib-linux_libc_support_unittest.obj `if test -f 'src/common/linux/linux_libc_support_unittest.cc'; then $(CYGPATH_W) 'src/common/linux/linux_libc_support_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/linux_libc_support_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-linux_libc_support_unittest.Tpo src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-linux_libc_support_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/linux_libc_support_unittest.cc' object='src/common/linux/client_linux_linux_client_unittest_shlib-linux_libc_support_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/client_linux_linux_client_unittest_shlib-linux_libc_support_unittest.obj `if test -f 'src/common/linux/linux_libc_support_unittest.cc'; then $(CYGPATH_W) 'src/common/linux/linux_libc_support_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/linux_libc_support_unittest.cc'; fi` + +src/common/linux/tests/client_linux_linux_client_unittest_shlib-crash_generator.o: src/common/linux/tests/crash_generator.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/tests/client_linux_linux_client_unittest_shlib-crash_generator.o -MD -MP -MF src/common/linux/tests/$(DEPDIR)/client_linux_linux_client_unittest_shlib-crash_generator.Tpo -c -o src/common/linux/tests/client_linux_linux_client_unittest_shlib-crash_generator.o `test -f 'src/common/linux/tests/crash_generator.cc' || echo '$(srcdir)/'`src/common/linux/tests/crash_generator.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/tests/$(DEPDIR)/client_linux_linux_client_unittest_shlib-crash_generator.Tpo src/common/linux/tests/$(DEPDIR)/client_linux_linux_client_unittest_shlib-crash_generator.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/tests/crash_generator.cc' object='src/common/linux/tests/client_linux_linux_client_unittest_shlib-crash_generator.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/tests/client_linux_linux_client_unittest_shlib-crash_generator.o `test -f 'src/common/linux/tests/crash_generator.cc' || echo '$(srcdir)/'`src/common/linux/tests/crash_generator.cc + +src/common/linux/tests/client_linux_linux_client_unittest_shlib-crash_generator.obj: src/common/linux/tests/crash_generator.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/tests/client_linux_linux_client_unittest_shlib-crash_generator.obj -MD -MP -MF src/common/linux/tests/$(DEPDIR)/client_linux_linux_client_unittest_shlib-crash_generator.Tpo -c -o src/common/linux/tests/client_linux_linux_client_unittest_shlib-crash_generator.obj `if test -f 'src/common/linux/tests/crash_generator.cc'; then $(CYGPATH_W) 'src/common/linux/tests/crash_generator.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/tests/crash_generator.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/tests/$(DEPDIR)/client_linux_linux_client_unittest_shlib-crash_generator.Tpo src/common/linux/tests/$(DEPDIR)/client_linux_linux_client_unittest_shlib-crash_generator.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/tests/crash_generator.cc' object='src/common/linux/tests/client_linux_linux_client_unittest_shlib-crash_generator.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/tests/client_linux_linux_client_unittest_shlib-crash_generator.obj `if test -f 'src/common/linux/tests/crash_generator.cc'; then $(CYGPATH_W) 'src/common/linux/tests/crash_generator.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/tests/crash_generator.cc'; fi` + +src/common/client_linux_linux_client_unittest_shlib-memory_allocator_unittest.o: src/common/memory_allocator_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/client_linux_linux_client_unittest_shlib-memory_allocator_unittest.o -MD -MP -MF src/common/$(DEPDIR)/client_linux_linux_client_unittest_shlib-memory_allocator_unittest.Tpo -c -o src/common/client_linux_linux_client_unittest_shlib-memory_allocator_unittest.o `test -f 'src/common/memory_allocator_unittest.cc' || echo '$(srcdir)/'`src/common/memory_allocator_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/client_linux_linux_client_unittest_shlib-memory_allocator_unittest.Tpo src/common/$(DEPDIR)/client_linux_linux_client_unittest_shlib-memory_allocator_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/memory_allocator_unittest.cc' object='src/common/client_linux_linux_client_unittest_shlib-memory_allocator_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/client_linux_linux_client_unittest_shlib-memory_allocator_unittest.o `test -f 'src/common/memory_allocator_unittest.cc' || echo '$(srcdir)/'`src/common/memory_allocator_unittest.cc + +src/common/client_linux_linux_client_unittest_shlib-memory_allocator_unittest.obj: src/common/memory_allocator_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/client_linux_linux_client_unittest_shlib-memory_allocator_unittest.obj -MD -MP -MF src/common/$(DEPDIR)/client_linux_linux_client_unittest_shlib-memory_allocator_unittest.Tpo -c -o src/common/client_linux_linux_client_unittest_shlib-memory_allocator_unittest.obj `if test -f 'src/common/memory_allocator_unittest.cc'; then $(CYGPATH_W) 'src/common/memory_allocator_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/memory_allocator_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/client_linux_linux_client_unittest_shlib-memory_allocator_unittest.Tpo src/common/$(DEPDIR)/client_linux_linux_client_unittest_shlib-memory_allocator_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/memory_allocator_unittest.cc' object='src/common/client_linux_linux_client_unittest_shlib-memory_allocator_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/client_linux_linux_client_unittest_shlib-memory_allocator_unittest.obj `if test -f 'src/common/memory_allocator_unittest.cc'; then $(CYGPATH_W) 'src/common/memory_allocator_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/memory_allocator_unittest.cc'; fi` + +src/common/tests/client_linux_linux_client_unittest_shlib-file_utils.o: src/common/tests/file_utils.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/tests/client_linux_linux_client_unittest_shlib-file_utils.o -MD -MP -MF src/common/tests/$(DEPDIR)/client_linux_linux_client_unittest_shlib-file_utils.Tpo -c -o src/common/tests/client_linux_linux_client_unittest_shlib-file_utils.o `test -f 'src/common/tests/file_utils.cc' || echo '$(srcdir)/'`src/common/tests/file_utils.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/tests/$(DEPDIR)/client_linux_linux_client_unittest_shlib-file_utils.Tpo src/common/tests/$(DEPDIR)/client_linux_linux_client_unittest_shlib-file_utils.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/tests/file_utils.cc' object='src/common/tests/client_linux_linux_client_unittest_shlib-file_utils.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tests/client_linux_linux_client_unittest_shlib-file_utils.o `test -f 'src/common/tests/file_utils.cc' || echo '$(srcdir)/'`src/common/tests/file_utils.cc + +src/common/tests/client_linux_linux_client_unittest_shlib-file_utils.obj: src/common/tests/file_utils.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/tests/client_linux_linux_client_unittest_shlib-file_utils.obj -MD -MP -MF src/common/tests/$(DEPDIR)/client_linux_linux_client_unittest_shlib-file_utils.Tpo -c -o src/common/tests/client_linux_linux_client_unittest_shlib-file_utils.obj `if test -f 'src/common/tests/file_utils.cc'; then $(CYGPATH_W) 'src/common/tests/file_utils.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/tests/file_utils.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/tests/$(DEPDIR)/client_linux_linux_client_unittest_shlib-file_utils.Tpo src/common/tests/$(DEPDIR)/client_linux_linux_client_unittest_shlib-file_utils.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/tests/file_utils.cc' object='src/common/tests/client_linux_linux_client_unittest_shlib-file_utils.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tests/client_linux_linux_client_unittest_shlib-file_utils.obj `if test -f 'src/common/tests/file_utils.cc'; then $(CYGPATH_W) 'src/common/tests/file_utils.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/tests/file_utils.cc'; fi` + +src/processor/client_linux_linux_client_unittest_shlib-basic_code_modules.o: src/processor/basic_code_modules.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/client_linux_linux_client_unittest_shlib-basic_code_modules.o -MD -MP -MF src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-basic_code_modules.Tpo -c -o src/processor/client_linux_linux_client_unittest_shlib-basic_code_modules.o `test -f 'src/processor/basic_code_modules.cc' || echo '$(srcdir)/'`src/processor/basic_code_modules.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-basic_code_modules.Tpo src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-basic_code_modules.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/basic_code_modules.cc' object='src/processor/client_linux_linux_client_unittest_shlib-basic_code_modules.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/client_linux_linux_client_unittest_shlib-basic_code_modules.o `test -f 'src/processor/basic_code_modules.cc' || echo '$(srcdir)/'`src/processor/basic_code_modules.cc + +src/processor/client_linux_linux_client_unittest_shlib-basic_code_modules.obj: src/processor/basic_code_modules.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/client_linux_linux_client_unittest_shlib-basic_code_modules.obj -MD -MP -MF src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-basic_code_modules.Tpo -c -o src/processor/client_linux_linux_client_unittest_shlib-basic_code_modules.obj `if test -f 'src/processor/basic_code_modules.cc'; then $(CYGPATH_W) 'src/processor/basic_code_modules.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/basic_code_modules.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-basic_code_modules.Tpo src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-basic_code_modules.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/basic_code_modules.cc' object='src/processor/client_linux_linux_client_unittest_shlib-basic_code_modules.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/client_linux_linux_client_unittest_shlib-basic_code_modules.obj `if test -f 'src/processor/basic_code_modules.cc'; then $(CYGPATH_W) 'src/processor/basic_code_modules.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/basic_code_modules.cc'; fi` + +src/processor/client_linux_linux_client_unittest_shlib-convert_old_arm64_context.o: src/processor/convert_old_arm64_context.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/client_linux_linux_client_unittest_shlib-convert_old_arm64_context.o -MD -MP -MF src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-convert_old_arm64_context.Tpo -c -o src/processor/client_linux_linux_client_unittest_shlib-convert_old_arm64_context.o `test -f 'src/processor/convert_old_arm64_context.cc' || echo '$(srcdir)/'`src/processor/convert_old_arm64_context.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-convert_old_arm64_context.Tpo src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-convert_old_arm64_context.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/convert_old_arm64_context.cc' object='src/processor/client_linux_linux_client_unittest_shlib-convert_old_arm64_context.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/client_linux_linux_client_unittest_shlib-convert_old_arm64_context.o `test -f 'src/processor/convert_old_arm64_context.cc' || echo '$(srcdir)/'`src/processor/convert_old_arm64_context.cc + +src/processor/client_linux_linux_client_unittest_shlib-convert_old_arm64_context.obj: src/processor/convert_old_arm64_context.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/client_linux_linux_client_unittest_shlib-convert_old_arm64_context.obj -MD -MP -MF src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-convert_old_arm64_context.Tpo -c -o src/processor/client_linux_linux_client_unittest_shlib-convert_old_arm64_context.obj `if test -f 'src/processor/convert_old_arm64_context.cc'; then $(CYGPATH_W) 'src/processor/convert_old_arm64_context.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/convert_old_arm64_context.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-convert_old_arm64_context.Tpo src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-convert_old_arm64_context.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/convert_old_arm64_context.cc' object='src/processor/client_linux_linux_client_unittest_shlib-convert_old_arm64_context.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/client_linux_linux_client_unittest_shlib-convert_old_arm64_context.obj `if test -f 'src/processor/convert_old_arm64_context.cc'; then $(CYGPATH_W) 'src/processor/convert_old_arm64_context.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/convert_old_arm64_context.cc'; fi` + +src/processor/client_linux_linux_client_unittest_shlib-dump_context.o: src/processor/dump_context.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/client_linux_linux_client_unittest_shlib-dump_context.o -MD -MP -MF src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-dump_context.Tpo -c -o src/processor/client_linux_linux_client_unittest_shlib-dump_context.o `test -f 'src/processor/dump_context.cc' || echo '$(srcdir)/'`src/processor/dump_context.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-dump_context.Tpo src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-dump_context.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/dump_context.cc' object='src/processor/client_linux_linux_client_unittest_shlib-dump_context.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/client_linux_linux_client_unittest_shlib-dump_context.o `test -f 'src/processor/dump_context.cc' || echo '$(srcdir)/'`src/processor/dump_context.cc + +src/processor/client_linux_linux_client_unittest_shlib-dump_context.obj: src/processor/dump_context.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/client_linux_linux_client_unittest_shlib-dump_context.obj -MD -MP -MF src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-dump_context.Tpo -c -o src/processor/client_linux_linux_client_unittest_shlib-dump_context.obj `if test -f 'src/processor/dump_context.cc'; then $(CYGPATH_W) 'src/processor/dump_context.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/dump_context.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-dump_context.Tpo src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-dump_context.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/dump_context.cc' object='src/processor/client_linux_linux_client_unittest_shlib-dump_context.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/client_linux_linux_client_unittest_shlib-dump_context.obj `if test -f 'src/processor/dump_context.cc'; then $(CYGPATH_W) 'src/processor/dump_context.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/dump_context.cc'; fi` + +src/processor/client_linux_linux_client_unittest_shlib-dump_object.o: src/processor/dump_object.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/client_linux_linux_client_unittest_shlib-dump_object.o -MD -MP -MF src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-dump_object.Tpo -c -o src/processor/client_linux_linux_client_unittest_shlib-dump_object.o `test -f 'src/processor/dump_object.cc' || echo '$(srcdir)/'`src/processor/dump_object.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-dump_object.Tpo src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-dump_object.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/dump_object.cc' object='src/processor/client_linux_linux_client_unittest_shlib-dump_object.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/client_linux_linux_client_unittest_shlib-dump_object.o `test -f 'src/processor/dump_object.cc' || echo '$(srcdir)/'`src/processor/dump_object.cc + +src/processor/client_linux_linux_client_unittest_shlib-dump_object.obj: src/processor/dump_object.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/client_linux_linux_client_unittest_shlib-dump_object.obj -MD -MP -MF src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-dump_object.Tpo -c -o src/processor/client_linux_linux_client_unittest_shlib-dump_object.obj `if test -f 'src/processor/dump_object.cc'; then $(CYGPATH_W) 'src/processor/dump_object.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/dump_object.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-dump_object.Tpo src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-dump_object.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/dump_object.cc' object='src/processor/client_linux_linux_client_unittest_shlib-dump_object.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/client_linux_linux_client_unittest_shlib-dump_object.obj `if test -f 'src/processor/dump_object.cc'; then $(CYGPATH_W) 'src/processor/dump_object.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/dump_object.cc'; fi` + +src/processor/client_linux_linux_client_unittest_shlib-logging.o: src/processor/logging.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/client_linux_linux_client_unittest_shlib-logging.o -MD -MP -MF src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-logging.Tpo -c -o src/processor/client_linux_linux_client_unittest_shlib-logging.o `test -f 'src/processor/logging.cc' || echo '$(srcdir)/'`src/processor/logging.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-logging.Tpo src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-logging.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/logging.cc' object='src/processor/client_linux_linux_client_unittest_shlib-logging.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/client_linux_linux_client_unittest_shlib-logging.o `test -f 'src/processor/logging.cc' || echo '$(srcdir)/'`src/processor/logging.cc + +src/processor/client_linux_linux_client_unittest_shlib-logging.obj: src/processor/logging.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/client_linux_linux_client_unittest_shlib-logging.obj -MD -MP -MF src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-logging.Tpo -c -o src/processor/client_linux_linux_client_unittest_shlib-logging.obj `if test -f 'src/processor/logging.cc'; then $(CYGPATH_W) 'src/processor/logging.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/logging.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-logging.Tpo src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-logging.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/logging.cc' object='src/processor/client_linux_linux_client_unittest_shlib-logging.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/client_linux_linux_client_unittest_shlib-logging.obj `if test -f 'src/processor/logging.cc'; then $(CYGPATH_W) 'src/processor/logging.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/logging.cc'; fi` + +src/processor/client_linux_linux_client_unittest_shlib-minidump.o: src/processor/minidump.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/client_linux_linux_client_unittest_shlib-minidump.o -MD -MP -MF src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-minidump.Tpo -c -o src/processor/client_linux_linux_client_unittest_shlib-minidump.o `test -f 'src/processor/minidump.cc' || echo '$(srcdir)/'`src/processor/minidump.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-minidump.Tpo src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-minidump.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/minidump.cc' object='src/processor/client_linux_linux_client_unittest_shlib-minidump.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/client_linux_linux_client_unittest_shlib-minidump.o `test -f 'src/processor/minidump.cc' || echo '$(srcdir)/'`src/processor/minidump.cc + +src/processor/client_linux_linux_client_unittest_shlib-minidump.obj: src/processor/minidump.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/client_linux_linux_client_unittest_shlib-minidump.obj -MD -MP -MF src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-minidump.Tpo -c -o src/processor/client_linux_linux_client_unittest_shlib-minidump.obj `if test -f 'src/processor/minidump.cc'; then $(CYGPATH_W) 'src/processor/minidump.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/minidump.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-minidump.Tpo src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-minidump.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/minidump.cc' object='src/processor/client_linux_linux_client_unittest_shlib-minidump.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/client_linux_linux_client_unittest_shlib-minidump.obj `if test -f 'src/processor/minidump.cc'; then $(CYGPATH_W) 'src/processor/minidump.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/minidump.cc'; fi` + +src/processor/client_linux_linux_client_unittest_shlib-pathname_stripper.o: src/processor/pathname_stripper.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/client_linux_linux_client_unittest_shlib-pathname_stripper.o -MD -MP -MF src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-pathname_stripper.Tpo -c -o src/processor/client_linux_linux_client_unittest_shlib-pathname_stripper.o `test -f 'src/processor/pathname_stripper.cc' || echo '$(srcdir)/'`src/processor/pathname_stripper.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-pathname_stripper.Tpo src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-pathname_stripper.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/pathname_stripper.cc' object='src/processor/client_linux_linux_client_unittest_shlib-pathname_stripper.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/client_linux_linux_client_unittest_shlib-pathname_stripper.o `test -f 'src/processor/pathname_stripper.cc' || echo '$(srcdir)/'`src/processor/pathname_stripper.cc + +src/processor/client_linux_linux_client_unittest_shlib-pathname_stripper.obj: src/processor/pathname_stripper.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/client_linux_linux_client_unittest_shlib-pathname_stripper.obj -MD -MP -MF src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-pathname_stripper.Tpo -c -o src/processor/client_linux_linux_client_unittest_shlib-pathname_stripper.obj `if test -f 'src/processor/pathname_stripper.cc'; then $(CYGPATH_W) 'src/processor/pathname_stripper.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/pathname_stripper.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-pathname_stripper.Tpo src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-pathname_stripper.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/pathname_stripper.cc' object='src/processor/client_linux_linux_client_unittest_shlib-pathname_stripper.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/client_linux_linux_client_unittest_shlib-pathname_stripper.obj `if test -f 'src/processor/pathname_stripper.cc'; then $(CYGPATH_W) 'src/processor/pathname_stripper.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/pathname_stripper.cc'; fi` + +src/processor/client_linux_linux_client_unittest_shlib-proc_maps_linux.o: src/processor/proc_maps_linux.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/client_linux_linux_client_unittest_shlib-proc_maps_linux.o -MD -MP -MF src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-proc_maps_linux.Tpo -c -o src/processor/client_linux_linux_client_unittest_shlib-proc_maps_linux.o `test -f 'src/processor/proc_maps_linux.cc' || echo '$(srcdir)/'`src/processor/proc_maps_linux.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-proc_maps_linux.Tpo src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-proc_maps_linux.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/proc_maps_linux.cc' object='src/processor/client_linux_linux_client_unittest_shlib-proc_maps_linux.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/client_linux_linux_client_unittest_shlib-proc_maps_linux.o `test -f 'src/processor/proc_maps_linux.cc' || echo '$(srcdir)/'`src/processor/proc_maps_linux.cc + +src/processor/client_linux_linux_client_unittest_shlib-proc_maps_linux.obj: src/processor/proc_maps_linux.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/client_linux_linux_client_unittest_shlib-proc_maps_linux.obj -MD -MP -MF src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-proc_maps_linux.Tpo -c -o src/processor/client_linux_linux_client_unittest_shlib-proc_maps_linux.obj `if test -f 'src/processor/proc_maps_linux.cc'; then $(CYGPATH_W) 'src/processor/proc_maps_linux.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/proc_maps_linux.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-proc_maps_linux.Tpo src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-proc_maps_linux.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/proc_maps_linux.cc' object='src/processor/client_linux_linux_client_unittest_shlib-proc_maps_linux.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/client_linux_linux_client_unittest_shlib-proc_maps_linux.obj `if test -f 'src/processor/proc_maps_linux.cc'; then $(CYGPATH_W) 'src/processor/proc_maps_linux.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/proc_maps_linux.cc'; fi` + +src/common/linux/client_linux_linux_client_unittest_shlib-breakpad_getcontext_unittest.o: src/common/linux/breakpad_getcontext_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/client_linux_linux_client_unittest_shlib-breakpad_getcontext_unittest.o -MD -MP -MF src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-breakpad_getcontext_unittest.Tpo -c -o src/common/linux/client_linux_linux_client_unittest_shlib-breakpad_getcontext_unittest.o `test -f 'src/common/linux/breakpad_getcontext_unittest.cc' || echo '$(srcdir)/'`src/common/linux/breakpad_getcontext_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-breakpad_getcontext_unittest.Tpo src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-breakpad_getcontext_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/breakpad_getcontext_unittest.cc' object='src/common/linux/client_linux_linux_client_unittest_shlib-breakpad_getcontext_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/client_linux_linux_client_unittest_shlib-breakpad_getcontext_unittest.o `test -f 'src/common/linux/breakpad_getcontext_unittest.cc' || echo '$(srcdir)/'`src/common/linux/breakpad_getcontext_unittest.cc + +src/common/linux/client_linux_linux_client_unittest_shlib-breakpad_getcontext_unittest.obj: src/common/linux/breakpad_getcontext_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/client_linux_linux_client_unittest_shlib-breakpad_getcontext_unittest.obj -MD -MP -MF src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-breakpad_getcontext_unittest.Tpo -c -o src/common/linux/client_linux_linux_client_unittest_shlib-breakpad_getcontext_unittest.obj `if test -f 'src/common/linux/breakpad_getcontext_unittest.cc'; then $(CYGPATH_W) 'src/common/linux/breakpad_getcontext_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/breakpad_getcontext_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-breakpad_getcontext_unittest.Tpo src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-breakpad_getcontext_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/breakpad_getcontext_unittest.cc' object='src/common/linux/client_linux_linux_client_unittest_shlib-breakpad_getcontext_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_client_linux_linux_client_unittest_shlib_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/client_linux_linux_client_unittest_shlib-breakpad_getcontext_unittest.obj `if test -f 'src/common/linux/breakpad_getcontext_unittest.cc'; then $(CYGPATH_W) 'src/common/linux/breakpad_getcontext_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/breakpad_getcontext_unittest.cc'; fi` + +src/client/linux/minidump_writer/linux_dumper_unittest_helper-linux_dumper_unittest_helper.o: src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_client_linux_linux_dumper_unittest_helper_CXXFLAGS) $(CXXFLAGS) -MT src/client/linux/minidump_writer/linux_dumper_unittest_helper-linux_dumper_unittest_helper.o -MD -MP -MF src/client/linux/minidump_writer/$(DEPDIR)/linux_dumper_unittest_helper-linux_dumper_unittest_helper.Tpo -c -o src/client/linux/minidump_writer/linux_dumper_unittest_helper-linux_dumper_unittest_helper.o `test -f 'src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc' || echo '$(srcdir)/'`src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/client/linux/minidump_writer/$(DEPDIR)/linux_dumper_unittest_helper-linux_dumper_unittest_helper.Tpo src/client/linux/minidump_writer/$(DEPDIR)/linux_dumper_unittest_helper-linux_dumper_unittest_helper.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc' object='src/client/linux/minidump_writer/linux_dumper_unittest_helper-linux_dumper_unittest_helper.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_client_linux_linux_dumper_unittest_helper_CXXFLAGS) $(CXXFLAGS) -c -o src/client/linux/minidump_writer/linux_dumper_unittest_helper-linux_dumper_unittest_helper.o `test -f 'src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc' || echo '$(srcdir)/'`src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc + +src/client/linux/minidump_writer/linux_dumper_unittest_helper-linux_dumper_unittest_helper.obj: src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_client_linux_linux_dumper_unittest_helper_CXXFLAGS) $(CXXFLAGS) -MT src/client/linux/minidump_writer/linux_dumper_unittest_helper-linux_dumper_unittest_helper.obj -MD -MP -MF src/client/linux/minidump_writer/$(DEPDIR)/linux_dumper_unittest_helper-linux_dumper_unittest_helper.Tpo -c -o src/client/linux/minidump_writer/linux_dumper_unittest_helper-linux_dumper_unittest_helper.obj `if test -f 'src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc'; then $(CYGPATH_W) 'src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc'; else $(CYGPATH_W) '$(srcdir)/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/client/linux/minidump_writer/$(DEPDIR)/linux_dumper_unittest_helper-linux_dumper_unittest_helper.Tpo src/client/linux/minidump_writer/$(DEPDIR)/linux_dumper_unittest_helper-linux_dumper_unittest_helper.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc' object='src/client/linux/minidump_writer/linux_dumper_unittest_helper-linux_dumper_unittest_helper.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_client_linux_linux_dumper_unittest_helper_CXXFLAGS) $(CXXFLAGS) -c -o src/client/linux/minidump_writer/linux_dumper_unittest_helper-linux_dumper_unittest_helper.obj `if test -f 'src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc'; then $(CYGPATH_W) 'src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc'; else $(CYGPATH_W) '$(srcdir)/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc'; fi` + +src/common/dumper_unittest-byte_cursor_unittest.o: src/common/byte_cursor_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-byte_cursor_unittest.o -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-byte_cursor_unittest.Tpo -c -o src/common/dumper_unittest-byte_cursor_unittest.o `test -f 'src/common/byte_cursor_unittest.cc' || echo '$(srcdir)/'`src/common/byte_cursor_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-byte_cursor_unittest.Tpo src/common/$(DEPDIR)/dumper_unittest-byte_cursor_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/byte_cursor_unittest.cc' object='src/common/dumper_unittest-byte_cursor_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-byte_cursor_unittest.o `test -f 'src/common/byte_cursor_unittest.cc' || echo '$(srcdir)/'`src/common/byte_cursor_unittest.cc + +src/common/dumper_unittest-byte_cursor_unittest.obj: src/common/byte_cursor_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-byte_cursor_unittest.obj -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-byte_cursor_unittest.Tpo -c -o src/common/dumper_unittest-byte_cursor_unittest.obj `if test -f 'src/common/byte_cursor_unittest.cc'; then $(CYGPATH_W) 'src/common/byte_cursor_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/byte_cursor_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-byte_cursor_unittest.Tpo src/common/$(DEPDIR)/dumper_unittest-byte_cursor_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/byte_cursor_unittest.cc' object='src/common/dumper_unittest-byte_cursor_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-byte_cursor_unittest.obj `if test -f 'src/common/byte_cursor_unittest.cc'; then $(CYGPATH_W) 'src/common/byte_cursor_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/byte_cursor_unittest.cc'; fi` + +src/common/dumper_unittest-convert_UTF.o: src/common/convert_UTF.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-convert_UTF.o -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-convert_UTF.Tpo -c -o src/common/dumper_unittest-convert_UTF.o `test -f 'src/common/convert_UTF.cc' || echo '$(srcdir)/'`src/common/convert_UTF.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-convert_UTF.Tpo src/common/$(DEPDIR)/dumper_unittest-convert_UTF.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/convert_UTF.cc' object='src/common/dumper_unittest-convert_UTF.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-convert_UTF.o `test -f 'src/common/convert_UTF.cc' || echo '$(srcdir)/'`src/common/convert_UTF.cc + +src/common/dumper_unittest-convert_UTF.obj: src/common/convert_UTF.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-convert_UTF.obj -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-convert_UTF.Tpo -c -o src/common/dumper_unittest-convert_UTF.obj `if test -f 'src/common/convert_UTF.cc'; then $(CYGPATH_W) 'src/common/convert_UTF.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/convert_UTF.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-convert_UTF.Tpo src/common/$(DEPDIR)/dumper_unittest-convert_UTF.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/convert_UTF.cc' object='src/common/dumper_unittest-convert_UTF.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-convert_UTF.obj `if test -f 'src/common/convert_UTF.cc'; then $(CYGPATH_W) 'src/common/convert_UTF.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/convert_UTF.cc'; fi` + +src/common/dumper_unittest-dwarf_cfi_to_module.o: src/common/dwarf_cfi_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-dwarf_cfi_to_module.o -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-dwarf_cfi_to_module.Tpo -c -o src/common/dumper_unittest-dwarf_cfi_to_module.o `test -f 'src/common/dwarf_cfi_to_module.cc' || echo '$(srcdir)/'`src/common/dwarf_cfi_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-dwarf_cfi_to_module.Tpo src/common/$(DEPDIR)/dumper_unittest-dwarf_cfi_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_cfi_to_module.cc' object='src/common/dumper_unittest-dwarf_cfi_to_module.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-dwarf_cfi_to_module.o `test -f 'src/common/dwarf_cfi_to_module.cc' || echo '$(srcdir)/'`src/common/dwarf_cfi_to_module.cc + +src/common/dumper_unittest-dwarf_cfi_to_module.obj: src/common/dwarf_cfi_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-dwarf_cfi_to_module.obj -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-dwarf_cfi_to_module.Tpo -c -o src/common/dumper_unittest-dwarf_cfi_to_module.obj `if test -f 'src/common/dwarf_cfi_to_module.cc'; then $(CYGPATH_W) 'src/common/dwarf_cfi_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_cfi_to_module.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-dwarf_cfi_to_module.Tpo src/common/$(DEPDIR)/dumper_unittest-dwarf_cfi_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_cfi_to_module.cc' object='src/common/dumper_unittest-dwarf_cfi_to_module.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-dwarf_cfi_to_module.obj `if test -f 'src/common/dwarf_cfi_to_module.cc'; then $(CYGPATH_W) 'src/common/dwarf_cfi_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_cfi_to_module.cc'; fi` + +src/common/dumper_unittest-dwarf_cfi_to_module_unittest.o: src/common/dwarf_cfi_to_module_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-dwarf_cfi_to_module_unittest.o -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-dwarf_cfi_to_module_unittest.Tpo -c -o src/common/dumper_unittest-dwarf_cfi_to_module_unittest.o `test -f 'src/common/dwarf_cfi_to_module_unittest.cc' || echo '$(srcdir)/'`src/common/dwarf_cfi_to_module_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-dwarf_cfi_to_module_unittest.Tpo src/common/$(DEPDIR)/dumper_unittest-dwarf_cfi_to_module_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_cfi_to_module_unittest.cc' object='src/common/dumper_unittest-dwarf_cfi_to_module_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-dwarf_cfi_to_module_unittest.o `test -f 'src/common/dwarf_cfi_to_module_unittest.cc' || echo '$(srcdir)/'`src/common/dwarf_cfi_to_module_unittest.cc + +src/common/dumper_unittest-dwarf_cfi_to_module_unittest.obj: src/common/dwarf_cfi_to_module_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-dwarf_cfi_to_module_unittest.obj -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-dwarf_cfi_to_module_unittest.Tpo -c -o src/common/dumper_unittest-dwarf_cfi_to_module_unittest.obj `if test -f 'src/common/dwarf_cfi_to_module_unittest.cc'; then $(CYGPATH_W) 'src/common/dwarf_cfi_to_module_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_cfi_to_module_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-dwarf_cfi_to_module_unittest.Tpo src/common/$(DEPDIR)/dumper_unittest-dwarf_cfi_to_module_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_cfi_to_module_unittest.cc' object='src/common/dumper_unittest-dwarf_cfi_to_module_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-dwarf_cfi_to_module_unittest.obj `if test -f 'src/common/dwarf_cfi_to_module_unittest.cc'; then $(CYGPATH_W) 'src/common/dwarf_cfi_to_module_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_cfi_to_module_unittest.cc'; fi` + +src/common/dumper_unittest-dwarf_cu_to_module.o: src/common/dwarf_cu_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-dwarf_cu_to_module.o -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-dwarf_cu_to_module.Tpo -c -o src/common/dumper_unittest-dwarf_cu_to_module.o `test -f 'src/common/dwarf_cu_to_module.cc' || echo '$(srcdir)/'`src/common/dwarf_cu_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-dwarf_cu_to_module.Tpo src/common/$(DEPDIR)/dumper_unittest-dwarf_cu_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_cu_to_module.cc' object='src/common/dumper_unittest-dwarf_cu_to_module.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-dwarf_cu_to_module.o `test -f 'src/common/dwarf_cu_to_module.cc' || echo '$(srcdir)/'`src/common/dwarf_cu_to_module.cc + +src/common/dumper_unittest-dwarf_cu_to_module.obj: src/common/dwarf_cu_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-dwarf_cu_to_module.obj -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-dwarf_cu_to_module.Tpo -c -o src/common/dumper_unittest-dwarf_cu_to_module.obj `if test -f 'src/common/dwarf_cu_to_module.cc'; then $(CYGPATH_W) 'src/common/dwarf_cu_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_cu_to_module.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-dwarf_cu_to_module.Tpo src/common/$(DEPDIR)/dumper_unittest-dwarf_cu_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_cu_to_module.cc' object='src/common/dumper_unittest-dwarf_cu_to_module.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-dwarf_cu_to_module.obj `if test -f 'src/common/dwarf_cu_to_module.cc'; then $(CYGPATH_W) 'src/common/dwarf_cu_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_cu_to_module.cc'; fi` + +src/common/dumper_unittest-dwarf_cu_to_module_unittest.o: src/common/dwarf_cu_to_module_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-dwarf_cu_to_module_unittest.o -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-dwarf_cu_to_module_unittest.Tpo -c -o src/common/dumper_unittest-dwarf_cu_to_module_unittest.o `test -f 'src/common/dwarf_cu_to_module_unittest.cc' || echo '$(srcdir)/'`src/common/dwarf_cu_to_module_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-dwarf_cu_to_module_unittest.Tpo src/common/$(DEPDIR)/dumper_unittest-dwarf_cu_to_module_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_cu_to_module_unittest.cc' object='src/common/dumper_unittest-dwarf_cu_to_module_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-dwarf_cu_to_module_unittest.o `test -f 'src/common/dwarf_cu_to_module_unittest.cc' || echo '$(srcdir)/'`src/common/dwarf_cu_to_module_unittest.cc + +src/common/dumper_unittest-dwarf_cu_to_module_unittest.obj: src/common/dwarf_cu_to_module_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-dwarf_cu_to_module_unittest.obj -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-dwarf_cu_to_module_unittest.Tpo -c -o src/common/dumper_unittest-dwarf_cu_to_module_unittest.obj `if test -f 'src/common/dwarf_cu_to_module_unittest.cc'; then $(CYGPATH_W) 'src/common/dwarf_cu_to_module_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_cu_to_module_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-dwarf_cu_to_module_unittest.Tpo src/common/$(DEPDIR)/dumper_unittest-dwarf_cu_to_module_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_cu_to_module_unittest.cc' object='src/common/dumper_unittest-dwarf_cu_to_module_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-dwarf_cu_to_module_unittest.obj `if test -f 'src/common/dwarf_cu_to_module_unittest.cc'; then $(CYGPATH_W) 'src/common/dwarf_cu_to_module_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_cu_to_module_unittest.cc'; fi` + +src/common/dumper_unittest-dwarf_line_to_module.o: src/common/dwarf_line_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-dwarf_line_to_module.o -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-dwarf_line_to_module.Tpo -c -o src/common/dumper_unittest-dwarf_line_to_module.o `test -f 'src/common/dwarf_line_to_module.cc' || echo '$(srcdir)/'`src/common/dwarf_line_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-dwarf_line_to_module.Tpo src/common/$(DEPDIR)/dumper_unittest-dwarf_line_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_line_to_module.cc' object='src/common/dumper_unittest-dwarf_line_to_module.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-dwarf_line_to_module.o `test -f 'src/common/dwarf_line_to_module.cc' || echo '$(srcdir)/'`src/common/dwarf_line_to_module.cc + +src/common/dumper_unittest-dwarf_line_to_module.obj: src/common/dwarf_line_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-dwarf_line_to_module.obj -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-dwarf_line_to_module.Tpo -c -o src/common/dumper_unittest-dwarf_line_to_module.obj `if test -f 'src/common/dwarf_line_to_module.cc'; then $(CYGPATH_W) 'src/common/dwarf_line_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_line_to_module.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-dwarf_line_to_module.Tpo src/common/$(DEPDIR)/dumper_unittest-dwarf_line_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_line_to_module.cc' object='src/common/dumper_unittest-dwarf_line_to_module.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-dwarf_line_to_module.obj `if test -f 'src/common/dwarf_line_to_module.cc'; then $(CYGPATH_W) 'src/common/dwarf_line_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_line_to_module.cc'; fi` + +src/common/dumper_unittest-dwarf_line_to_module_unittest.o: src/common/dwarf_line_to_module_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-dwarf_line_to_module_unittest.o -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-dwarf_line_to_module_unittest.Tpo -c -o src/common/dumper_unittest-dwarf_line_to_module_unittest.o `test -f 'src/common/dwarf_line_to_module_unittest.cc' || echo '$(srcdir)/'`src/common/dwarf_line_to_module_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-dwarf_line_to_module_unittest.Tpo src/common/$(DEPDIR)/dumper_unittest-dwarf_line_to_module_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_line_to_module_unittest.cc' object='src/common/dumper_unittest-dwarf_line_to_module_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-dwarf_line_to_module_unittest.o `test -f 'src/common/dwarf_line_to_module_unittest.cc' || echo '$(srcdir)/'`src/common/dwarf_line_to_module_unittest.cc + +src/common/dumper_unittest-dwarf_line_to_module_unittest.obj: src/common/dwarf_line_to_module_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-dwarf_line_to_module_unittest.obj -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-dwarf_line_to_module_unittest.Tpo -c -o src/common/dumper_unittest-dwarf_line_to_module_unittest.obj `if test -f 'src/common/dwarf_line_to_module_unittest.cc'; then $(CYGPATH_W) 'src/common/dwarf_line_to_module_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_line_to_module_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-dwarf_line_to_module_unittest.Tpo src/common/$(DEPDIR)/dumper_unittest-dwarf_line_to_module_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_line_to_module_unittest.cc' object='src/common/dumper_unittest-dwarf_line_to_module_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-dwarf_line_to_module_unittest.obj `if test -f 'src/common/dwarf_line_to_module_unittest.cc'; then $(CYGPATH_W) 'src/common/dwarf_line_to_module_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_line_to_module_unittest.cc'; fi` + +src/common/dumper_unittest-dwarf_range_list_handler.o: src/common/dwarf_range_list_handler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-dwarf_range_list_handler.o -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-dwarf_range_list_handler.Tpo -c -o src/common/dumper_unittest-dwarf_range_list_handler.o `test -f 'src/common/dwarf_range_list_handler.cc' || echo '$(srcdir)/'`src/common/dwarf_range_list_handler.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-dwarf_range_list_handler.Tpo src/common/$(DEPDIR)/dumper_unittest-dwarf_range_list_handler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_range_list_handler.cc' object='src/common/dumper_unittest-dwarf_range_list_handler.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-dwarf_range_list_handler.o `test -f 'src/common/dwarf_range_list_handler.cc' || echo '$(srcdir)/'`src/common/dwarf_range_list_handler.cc + +src/common/dumper_unittest-dwarf_range_list_handler.obj: src/common/dwarf_range_list_handler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-dwarf_range_list_handler.obj -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-dwarf_range_list_handler.Tpo -c -o src/common/dumper_unittest-dwarf_range_list_handler.obj `if test -f 'src/common/dwarf_range_list_handler.cc'; then $(CYGPATH_W) 'src/common/dwarf_range_list_handler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_range_list_handler.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-dwarf_range_list_handler.Tpo src/common/$(DEPDIR)/dumper_unittest-dwarf_range_list_handler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_range_list_handler.cc' object='src/common/dumper_unittest-dwarf_range_list_handler.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-dwarf_range_list_handler.obj `if test -f 'src/common/dwarf_range_list_handler.cc'; then $(CYGPATH_W) 'src/common/dwarf_range_list_handler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_range_list_handler.cc'; fi` + +src/common/dumper_unittest-language.o: src/common/language.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-language.o -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-language.Tpo -c -o src/common/dumper_unittest-language.o `test -f 'src/common/language.cc' || echo '$(srcdir)/'`src/common/language.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-language.Tpo src/common/$(DEPDIR)/dumper_unittest-language.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/language.cc' object='src/common/dumper_unittest-language.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-language.o `test -f 'src/common/language.cc' || echo '$(srcdir)/'`src/common/language.cc + +src/common/dumper_unittest-language.obj: src/common/language.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-language.obj -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-language.Tpo -c -o src/common/dumper_unittest-language.obj `if test -f 'src/common/language.cc'; then $(CYGPATH_W) 'src/common/language.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/language.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-language.Tpo src/common/$(DEPDIR)/dumper_unittest-language.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/language.cc' object='src/common/dumper_unittest-language.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-language.obj `if test -f 'src/common/language.cc'; then $(CYGPATH_W) 'src/common/language.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/language.cc'; fi` + +src/common/dumper_unittest-memory_range_unittest.o: src/common/memory_range_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-memory_range_unittest.o -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-memory_range_unittest.Tpo -c -o src/common/dumper_unittest-memory_range_unittest.o `test -f 'src/common/memory_range_unittest.cc' || echo '$(srcdir)/'`src/common/memory_range_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-memory_range_unittest.Tpo src/common/$(DEPDIR)/dumper_unittest-memory_range_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/memory_range_unittest.cc' object='src/common/dumper_unittest-memory_range_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-memory_range_unittest.o `test -f 'src/common/memory_range_unittest.cc' || echo '$(srcdir)/'`src/common/memory_range_unittest.cc + +src/common/dumper_unittest-memory_range_unittest.obj: src/common/memory_range_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-memory_range_unittest.obj -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-memory_range_unittest.Tpo -c -o src/common/dumper_unittest-memory_range_unittest.obj `if test -f 'src/common/memory_range_unittest.cc'; then $(CYGPATH_W) 'src/common/memory_range_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/memory_range_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-memory_range_unittest.Tpo src/common/$(DEPDIR)/dumper_unittest-memory_range_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/memory_range_unittest.cc' object='src/common/dumper_unittest-memory_range_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-memory_range_unittest.obj `if test -f 'src/common/memory_range_unittest.cc'; then $(CYGPATH_W) 'src/common/memory_range_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/memory_range_unittest.cc'; fi` + +src/common/dumper_unittest-module.o: src/common/module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-module.o -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-module.Tpo -c -o src/common/dumper_unittest-module.o `test -f 'src/common/module.cc' || echo '$(srcdir)/'`src/common/module.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-module.Tpo src/common/$(DEPDIR)/dumper_unittest-module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/module.cc' object='src/common/dumper_unittest-module.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-module.o `test -f 'src/common/module.cc' || echo '$(srcdir)/'`src/common/module.cc + +src/common/dumper_unittest-module.obj: src/common/module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-module.obj -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-module.Tpo -c -o src/common/dumper_unittest-module.obj `if test -f 'src/common/module.cc'; then $(CYGPATH_W) 'src/common/module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/module.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-module.Tpo src/common/$(DEPDIR)/dumper_unittest-module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/module.cc' object='src/common/dumper_unittest-module.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-module.obj `if test -f 'src/common/module.cc'; then $(CYGPATH_W) 'src/common/module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/module.cc'; fi` + +src/common/dumper_unittest-module_unittest.o: src/common/module_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-module_unittest.o -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-module_unittest.Tpo -c -o src/common/dumper_unittest-module_unittest.o `test -f 'src/common/module_unittest.cc' || echo '$(srcdir)/'`src/common/module_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-module_unittest.Tpo src/common/$(DEPDIR)/dumper_unittest-module_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/module_unittest.cc' object='src/common/dumper_unittest-module_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-module_unittest.o `test -f 'src/common/module_unittest.cc' || echo '$(srcdir)/'`src/common/module_unittest.cc + +src/common/dumper_unittest-module_unittest.obj: src/common/module_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-module_unittest.obj -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-module_unittest.Tpo -c -o src/common/dumper_unittest-module_unittest.obj `if test -f 'src/common/module_unittest.cc'; then $(CYGPATH_W) 'src/common/module_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/module_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-module_unittest.Tpo src/common/$(DEPDIR)/dumper_unittest-module_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/module_unittest.cc' object='src/common/dumper_unittest-module_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-module_unittest.obj `if test -f 'src/common/module_unittest.cc'; then $(CYGPATH_W) 'src/common/module_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/module_unittest.cc'; fi` + +src/common/dumper_unittest-path_helper.o: src/common/path_helper.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-path_helper.o -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-path_helper.Tpo -c -o src/common/dumper_unittest-path_helper.o `test -f 'src/common/path_helper.cc' || echo '$(srcdir)/'`src/common/path_helper.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-path_helper.Tpo src/common/$(DEPDIR)/dumper_unittest-path_helper.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/path_helper.cc' object='src/common/dumper_unittest-path_helper.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-path_helper.o `test -f 'src/common/path_helper.cc' || echo '$(srcdir)/'`src/common/path_helper.cc + +src/common/dumper_unittest-path_helper.obj: src/common/path_helper.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-path_helper.obj -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-path_helper.Tpo -c -o src/common/dumper_unittest-path_helper.obj `if test -f 'src/common/path_helper.cc'; then $(CYGPATH_W) 'src/common/path_helper.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/path_helper.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-path_helper.Tpo src/common/$(DEPDIR)/dumper_unittest-path_helper.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/path_helper.cc' object='src/common/dumper_unittest-path_helper.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-path_helper.obj `if test -f 'src/common/path_helper.cc'; then $(CYGPATH_W) 'src/common/path_helper.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/path_helper.cc'; fi` + +src/common/dumper_unittest-stabs_reader.o: src/common/stabs_reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-stabs_reader.o -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-stabs_reader.Tpo -c -o src/common/dumper_unittest-stabs_reader.o `test -f 'src/common/stabs_reader.cc' || echo '$(srcdir)/'`src/common/stabs_reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-stabs_reader.Tpo src/common/$(DEPDIR)/dumper_unittest-stabs_reader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/stabs_reader.cc' object='src/common/dumper_unittest-stabs_reader.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-stabs_reader.o `test -f 'src/common/stabs_reader.cc' || echo '$(srcdir)/'`src/common/stabs_reader.cc + +src/common/dumper_unittest-stabs_reader.obj: src/common/stabs_reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-stabs_reader.obj -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-stabs_reader.Tpo -c -o src/common/dumper_unittest-stabs_reader.obj `if test -f 'src/common/stabs_reader.cc'; then $(CYGPATH_W) 'src/common/stabs_reader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/stabs_reader.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-stabs_reader.Tpo src/common/$(DEPDIR)/dumper_unittest-stabs_reader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/stabs_reader.cc' object='src/common/dumper_unittest-stabs_reader.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-stabs_reader.obj `if test -f 'src/common/stabs_reader.cc'; then $(CYGPATH_W) 'src/common/stabs_reader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/stabs_reader.cc'; fi` + +src/common/dumper_unittest-stabs_reader_unittest.o: src/common/stabs_reader_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-stabs_reader_unittest.o -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-stabs_reader_unittest.Tpo -c -o src/common/dumper_unittest-stabs_reader_unittest.o `test -f 'src/common/stabs_reader_unittest.cc' || echo '$(srcdir)/'`src/common/stabs_reader_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-stabs_reader_unittest.Tpo src/common/$(DEPDIR)/dumper_unittest-stabs_reader_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/stabs_reader_unittest.cc' object='src/common/dumper_unittest-stabs_reader_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-stabs_reader_unittest.o `test -f 'src/common/stabs_reader_unittest.cc' || echo '$(srcdir)/'`src/common/stabs_reader_unittest.cc + +src/common/dumper_unittest-stabs_reader_unittest.obj: src/common/stabs_reader_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-stabs_reader_unittest.obj -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-stabs_reader_unittest.Tpo -c -o src/common/dumper_unittest-stabs_reader_unittest.obj `if test -f 'src/common/stabs_reader_unittest.cc'; then $(CYGPATH_W) 'src/common/stabs_reader_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/stabs_reader_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-stabs_reader_unittest.Tpo src/common/$(DEPDIR)/dumper_unittest-stabs_reader_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/stabs_reader_unittest.cc' object='src/common/dumper_unittest-stabs_reader_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-stabs_reader_unittest.obj `if test -f 'src/common/stabs_reader_unittest.cc'; then $(CYGPATH_W) 'src/common/stabs_reader_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/stabs_reader_unittest.cc'; fi` + +src/common/dumper_unittest-stabs_to_module.o: src/common/stabs_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-stabs_to_module.o -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-stabs_to_module.Tpo -c -o src/common/dumper_unittest-stabs_to_module.o `test -f 'src/common/stabs_to_module.cc' || echo '$(srcdir)/'`src/common/stabs_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-stabs_to_module.Tpo src/common/$(DEPDIR)/dumper_unittest-stabs_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/stabs_to_module.cc' object='src/common/dumper_unittest-stabs_to_module.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-stabs_to_module.o `test -f 'src/common/stabs_to_module.cc' || echo '$(srcdir)/'`src/common/stabs_to_module.cc + +src/common/dumper_unittest-stabs_to_module.obj: src/common/stabs_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-stabs_to_module.obj -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-stabs_to_module.Tpo -c -o src/common/dumper_unittest-stabs_to_module.obj `if test -f 'src/common/stabs_to_module.cc'; then $(CYGPATH_W) 'src/common/stabs_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/stabs_to_module.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-stabs_to_module.Tpo src/common/$(DEPDIR)/dumper_unittest-stabs_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/stabs_to_module.cc' object='src/common/dumper_unittest-stabs_to_module.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-stabs_to_module.obj `if test -f 'src/common/stabs_to_module.cc'; then $(CYGPATH_W) 'src/common/stabs_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/stabs_to_module.cc'; fi` + +src/common/dumper_unittest-stabs_to_module_unittest.o: src/common/stabs_to_module_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-stabs_to_module_unittest.o -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-stabs_to_module_unittest.Tpo -c -o src/common/dumper_unittest-stabs_to_module_unittest.o `test -f 'src/common/stabs_to_module_unittest.cc' || echo '$(srcdir)/'`src/common/stabs_to_module_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-stabs_to_module_unittest.Tpo src/common/$(DEPDIR)/dumper_unittest-stabs_to_module_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/stabs_to_module_unittest.cc' object='src/common/dumper_unittest-stabs_to_module_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-stabs_to_module_unittest.o `test -f 'src/common/stabs_to_module_unittest.cc' || echo '$(srcdir)/'`src/common/stabs_to_module_unittest.cc + +src/common/dumper_unittest-stabs_to_module_unittest.obj: src/common/stabs_to_module_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-stabs_to_module_unittest.obj -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-stabs_to_module_unittest.Tpo -c -o src/common/dumper_unittest-stabs_to_module_unittest.obj `if test -f 'src/common/stabs_to_module_unittest.cc'; then $(CYGPATH_W) 'src/common/stabs_to_module_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/stabs_to_module_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-stabs_to_module_unittest.Tpo src/common/$(DEPDIR)/dumper_unittest-stabs_to_module_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/stabs_to_module_unittest.cc' object='src/common/dumper_unittest-stabs_to_module_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-stabs_to_module_unittest.obj `if test -f 'src/common/stabs_to_module_unittest.cc'; then $(CYGPATH_W) 'src/common/stabs_to_module_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/stabs_to_module_unittest.cc'; fi` + +src/common/dumper_unittest-string_conversion.o: src/common/string_conversion.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-string_conversion.o -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-string_conversion.Tpo -c -o src/common/dumper_unittest-string_conversion.o `test -f 'src/common/string_conversion.cc' || echo '$(srcdir)/'`src/common/string_conversion.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-string_conversion.Tpo src/common/$(DEPDIR)/dumper_unittest-string_conversion.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/string_conversion.cc' object='src/common/dumper_unittest-string_conversion.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-string_conversion.o `test -f 'src/common/string_conversion.cc' || echo '$(srcdir)/'`src/common/string_conversion.cc + +src/common/dumper_unittest-string_conversion.obj: src/common/string_conversion.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-string_conversion.obj -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-string_conversion.Tpo -c -o src/common/dumper_unittest-string_conversion.obj `if test -f 'src/common/string_conversion.cc'; then $(CYGPATH_W) 'src/common/string_conversion.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/string_conversion.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-string_conversion.Tpo src/common/$(DEPDIR)/dumper_unittest-string_conversion.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/string_conversion.cc' object='src/common/dumper_unittest-string_conversion.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-string_conversion.obj `if test -f 'src/common/string_conversion.cc'; then $(CYGPATH_W) 'src/common/string_conversion.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/string_conversion.cc'; fi` + +src/common/dumper_unittest-string_conversion_unittest.o: src/common/string_conversion_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-string_conversion_unittest.o -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-string_conversion_unittest.Tpo -c -o src/common/dumper_unittest-string_conversion_unittest.o `test -f 'src/common/string_conversion_unittest.cc' || echo '$(srcdir)/'`src/common/string_conversion_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-string_conversion_unittest.Tpo src/common/$(DEPDIR)/dumper_unittest-string_conversion_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/string_conversion_unittest.cc' object='src/common/dumper_unittest-string_conversion_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-string_conversion_unittest.o `test -f 'src/common/string_conversion_unittest.cc' || echo '$(srcdir)/'`src/common/string_conversion_unittest.cc + +src/common/dumper_unittest-string_conversion_unittest.obj: src/common/string_conversion_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-string_conversion_unittest.obj -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-string_conversion_unittest.Tpo -c -o src/common/dumper_unittest-string_conversion_unittest.obj `if test -f 'src/common/string_conversion_unittest.cc'; then $(CYGPATH_W) 'src/common/string_conversion_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/string_conversion_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-string_conversion_unittest.Tpo src/common/$(DEPDIR)/dumper_unittest-string_conversion_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/string_conversion_unittest.cc' object='src/common/dumper_unittest-string_conversion_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-string_conversion_unittest.obj `if test -f 'src/common/string_conversion_unittest.cc'; then $(CYGPATH_W) 'src/common/string_conversion_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/string_conversion_unittest.cc'; fi` + +src/common/dumper_unittest-test_assembler.o: src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-test_assembler.o -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-test_assembler.Tpo -c -o src/common/dumper_unittest-test_assembler.o `test -f 'src/common/test_assembler.cc' || echo '$(srcdir)/'`src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-test_assembler.Tpo src/common/$(DEPDIR)/dumper_unittest-test_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler.cc' object='src/common/dumper_unittest-test_assembler.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-test_assembler.o `test -f 'src/common/test_assembler.cc' || echo '$(srcdir)/'`src/common/test_assembler.cc + +src/common/dumper_unittest-test_assembler.obj: src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dumper_unittest-test_assembler.obj -MD -MP -MF src/common/$(DEPDIR)/dumper_unittest-test_assembler.Tpo -c -o src/common/dumper_unittest-test_assembler.obj `if test -f 'src/common/test_assembler.cc'; then $(CYGPATH_W) 'src/common/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/dumper_unittest-test_assembler.Tpo src/common/$(DEPDIR)/dumper_unittest-test_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler.cc' object='src/common/dumper_unittest-test_assembler.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dumper_unittest-test_assembler.obj `if test -f 'src/common/test_assembler.cc'; then $(CYGPATH_W) 'src/common/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler.cc'; fi` + +src/common/dwarf/dumper_unittest-bytereader.o: src/common/dwarf/bytereader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/dumper_unittest-bytereader.o -MD -MP -MF src/common/dwarf/$(DEPDIR)/dumper_unittest-bytereader.Tpo -c -o src/common/dwarf/dumper_unittest-bytereader.o `test -f 'src/common/dwarf/bytereader.cc' || echo '$(srcdir)/'`src/common/dwarf/bytereader.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/dumper_unittest-bytereader.Tpo src/common/dwarf/$(DEPDIR)/dumper_unittest-bytereader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/bytereader.cc' object='src/common/dwarf/dumper_unittest-bytereader.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/dumper_unittest-bytereader.o `test -f 'src/common/dwarf/bytereader.cc' || echo '$(srcdir)/'`src/common/dwarf/bytereader.cc + +src/common/dwarf/dumper_unittest-bytereader.obj: src/common/dwarf/bytereader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/dumper_unittest-bytereader.obj -MD -MP -MF src/common/dwarf/$(DEPDIR)/dumper_unittest-bytereader.Tpo -c -o src/common/dwarf/dumper_unittest-bytereader.obj `if test -f 'src/common/dwarf/bytereader.cc'; then $(CYGPATH_W) 'src/common/dwarf/bytereader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/bytereader.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/dumper_unittest-bytereader.Tpo src/common/dwarf/$(DEPDIR)/dumper_unittest-bytereader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/bytereader.cc' object='src/common/dwarf/dumper_unittest-bytereader.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/dumper_unittest-bytereader.obj `if test -f 'src/common/dwarf/bytereader.cc'; then $(CYGPATH_W) 'src/common/dwarf/bytereader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/bytereader.cc'; fi` + +src/common/dwarf/dumper_unittest-bytereader_unittest.o: src/common/dwarf/bytereader_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/dumper_unittest-bytereader_unittest.o -MD -MP -MF src/common/dwarf/$(DEPDIR)/dumper_unittest-bytereader_unittest.Tpo -c -o src/common/dwarf/dumper_unittest-bytereader_unittest.o `test -f 'src/common/dwarf/bytereader_unittest.cc' || echo '$(srcdir)/'`src/common/dwarf/bytereader_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/dumper_unittest-bytereader_unittest.Tpo src/common/dwarf/$(DEPDIR)/dumper_unittest-bytereader_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/bytereader_unittest.cc' object='src/common/dwarf/dumper_unittest-bytereader_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/dumper_unittest-bytereader_unittest.o `test -f 'src/common/dwarf/bytereader_unittest.cc' || echo '$(srcdir)/'`src/common/dwarf/bytereader_unittest.cc + +src/common/dwarf/dumper_unittest-bytereader_unittest.obj: src/common/dwarf/bytereader_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/dumper_unittest-bytereader_unittest.obj -MD -MP -MF src/common/dwarf/$(DEPDIR)/dumper_unittest-bytereader_unittest.Tpo -c -o src/common/dwarf/dumper_unittest-bytereader_unittest.obj `if test -f 'src/common/dwarf/bytereader_unittest.cc'; then $(CYGPATH_W) 'src/common/dwarf/bytereader_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/bytereader_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/dumper_unittest-bytereader_unittest.Tpo src/common/dwarf/$(DEPDIR)/dumper_unittest-bytereader_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/bytereader_unittest.cc' object='src/common/dwarf/dumper_unittest-bytereader_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/dumper_unittest-bytereader_unittest.obj `if test -f 'src/common/dwarf/bytereader_unittest.cc'; then $(CYGPATH_W) 'src/common/dwarf/bytereader_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/bytereader_unittest.cc'; fi` + +src/common/dwarf/dumper_unittest-cfi_assembler.o: src/common/dwarf/cfi_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/dumper_unittest-cfi_assembler.o -MD -MP -MF src/common/dwarf/$(DEPDIR)/dumper_unittest-cfi_assembler.Tpo -c -o src/common/dwarf/dumper_unittest-cfi_assembler.o `test -f 'src/common/dwarf/cfi_assembler.cc' || echo '$(srcdir)/'`src/common/dwarf/cfi_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/dumper_unittest-cfi_assembler.Tpo src/common/dwarf/$(DEPDIR)/dumper_unittest-cfi_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/cfi_assembler.cc' object='src/common/dwarf/dumper_unittest-cfi_assembler.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/dumper_unittest-cfi_assembler.o `test -f 'src/common/dwarf/cfi_assembler.cc' || echo '$(srcdir)/'`src/common/dwarf/cfi_assembler.cc + +src/common/dwarf/dumper_unittest-cfi_assembler.obj: src/common/dwarf/cfi_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/dumper_unittest-cfi_assembler.obj -MD -MP -MF src/common/dwarf/$(DEPDIR)/dumper_unittest-cfi_assembler.Tpo -c -o src/common/dwarf/dumper_unittest-cfi_assembler.obj `if test -f 'src/common/dwarf/cfi_assembler.cc'; then $(CYGPATH_W) 'src/common/dwarf/cfi_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/cfi_assembler.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/dumper_unittest-cfi_assembler.Tpo src/common/dwarf/$(DEPDIR)/dumper_unittest-cfi_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/cfi_assembler.cc' object='src/common/dwarf/dumper_unittest-cfi_assembler.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/dumper_unittest-cfi_assembler.obj `if test -f 'src/common/dwarf/cfi_assembler.cc'; then $(CYGPATH_W) 'src/common/dwarf/cfi_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/cfi_assembler.cc'; fi` + +src/common/dwarf/dumper_unittest-dwarf2diehandler.o: src/common/dwarf/dwarf2diehandler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/dumper_unittest-dwarf2diehandler.o -MD -MP -MF src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2diehandler.Tpo -c -o src/common/dwarf/dumper_unittest-dwarf2diehandler.o `test -f 'src/common/dwarf/dwarf2diehandler.cc' || echo '$(srcdir)/'`src/common/dwarf/dwarf2diehandler.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2diehandler.Tpo src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2diehandler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/dwarf2diehandler.cc' object='src/common/dwarf/dumper_unittest-dwarf2diehandler.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/dumper_unittest-dwarf2diehandler.o `test -f 'src/common/dwarf/dwarf2diehandler.cc' || echo '$(srcdir)/'`src/common/dwarf/dwarf2diehandler.cc + +src/common/dwarf/dumper_unittest-dwarf2diehandler.obj: src/common/dwarf/dwarf2diehandler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/dumper_unittest-dwarf2diehandler.obj -MD -MP -MF src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2diehandler.Tpo -c -o src/common/dwarf/dumper_unittest-dwarf2diehandler.obj `if test -f 'src/common/dwarf/dwarf2diehandler.cc'; then $(CYGPATH_W) 'src/common/dwarf/dwarf2diehandler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/dwarf2diehandler.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2diehandler.Tpo src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2diehandler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/dwarf2diehandler.cc' object='src/common/dwarf/dumper_unittest-dwarf2diehandler.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/dumper_unittest-dwarf2diehandler.obj `if test -f 'src/common/dwarf/dwarf2diehandler.cc'; then $(CYGPATH_W) 'src/common/dwarf/dwarf2diehandler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/dwarf2diehandler.cc'; fi` + +src/common/dwarf/dumper_unittest-dwarf2diehandler_unittest.o: src/common/dwarf/dwarf2diehandler_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/dumper_unittest-dwarf2diehandler_unittest.o -MD -MP -MF src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2diehandler_unittest.Tpo -c -o src/common/dwarf/dumper_unittest-dwarf2diehandler_unittest.o `test -f 'src/common/dwarf/dwarf2diehandler_unittest.cc' || echo '$(srcdir)/'`src/common/dwarf/dwarf2diehandler_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2diehandler_unittest.Tpo src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2diehandler_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/dwarf2diehandler_unittest.cc' object='src/common/dwarf/dumper_unittest-dwarf2diehandler_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/dumper_unittest-dwarf2diehandler_unittest.o `test -f 'src/common/dwarf/dwarf2diehandler_unittest.cc' || echo '$(srcdir)/'`src/common/dwarf/dwarf2diehandler_unittest.cc + +src/common/dwarf/dumper_unittest-dwarf2diehandler_unittest.obj: src/common/dwarf/dwarf2diehandler_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/dumper_unittest-dwarf2diehandler_unittest.obj -MD -MP -MF src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2diehandler_unittest.Tpo -c -o src/common/dwarf/dumper_unittest-dwarf2diehandler_unittest.obj `if test -f 'src/common/dwarf/dwarf2diehandler_unittest.cc'; then $(CYGPATH_W) 'src/common/dwarf/dwarf2diehandler_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/dwarf2diehandler_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2diehandler_unittest.Tpo src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2diehandler_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/dwarf2diehandler_unittest.cc' object='src/common/dwarf/dumper_unittest-dwarf2diehandler_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/dumper_unittest-dwarf2diehandler_unittest.obj `if test -f 'src/common/dwarf/dwarf2diehandler_unittest.cc'; then $(CYGPATH_W) 'src/common/dwarf/dwarf2diehandler_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/dwarf2diehandler_unittest.cc'; fi` + +src/common/dwarf/dumper_unittest-dwarf2reader.o: src/common/dwarf/dwarf2reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/dumper_unittest-dwarf2reader.o -MD -MP -MF src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2reader.Tpo -c -o src/common/dwarf/dumper_unittest-dwarf2reader.o `test -f 'src/common/dwarf/dwarf2reader.cc' || echo '$(srcdir)/'`src/common/dwarf/dwarf2reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2reader.Tpo src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2reader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/dwarf2reader.cc' object='src/common/dwarf/dumper_unittest-dwarf2reader.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/dumper_unittest-dwarf2reader.o `test -f 'src/common/dwarf/dwarf2reader.cc' || echo '$(srcdir)/'`src/common/dwarf/dwarf2reader.cc + +src/common/dwarf/dumper_unittest-dwarf2reader.obj: src/common/dwarf/dwarf2reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/dumper_unittest-dwarf2reader.obj -MD -MP -MF src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2reader.Tpo -c -o src/common/dwarf/dumper_unittest-dwarf2reader.obj `if test -f 'src/common/dwarf/dwarf2reader.cc'; then $(CYGPATH_W) 'src/common/dwarf/dwarf2reader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/dwarf2reader.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2reader.Tpo src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2reader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/dwarf2reader.cc' object='src/common/dwarf/dumper_unittest-dwarf2reader.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/dumper_unittest-dwarf2reader.obj `if test -f 'src/common/dwarf/dwarf2reader.cc'; then $(CYGPATH_W) 'src/common/dwarf/dwarf2reader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/dwarf2reader.cc'; fi` + +src/common/dwarf/dumper_unittest-elf_reader.o: src/common/dwarf/elf_reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/dumper_unittest-elf_reader.o -MD -MP -MF src/common/dwarf/$(DEPDIR)/dumper_unittest-elf_reader.Tpo -c -o src/common/dwarf/dumper_unittest-elf_reader.o `test -f 'src/common/dwarf/elf_reader.cc' || echo '$(srcdir)/'`src/common/dwarf/elf_reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/dumper_unittest-elf_reader.Tpo src/common/dwarf/$(DEPDIR)/dumper_unittest-elf_reader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/elf_reader.cc' object='src/common/dwarf/dumper_unittest-elf_reader.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/dumper_unittest-elf_reader.o `test -f 'src/common/dwarf/elf_reader.cc' || echo '$(srcdir)/'`src/common/dwarf/elf_reader.cc + +src/common/dwarf/dumper_unittest-elf_reader.obj: src/common/dwarf/elf_reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/dumper_unittest-elf_reader.obj -MD -MP -MF src/common/dwarf/$(DEPDIR)/dumper_unittest-elf_reader.Tpo -c -o src/common/dwarf/dumper_unittest-elf_reader.obj `if test -f 'src/common/dwarf/elf_reader.cc'; then $(CYGPATH_W) 'src/common/dwarf/elf_reader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/elf_reader.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/dumper_unittest-elf_reader.Tpo src/common/dwarf/$(DEPDIR)/dumper_unittest-elf_reader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/elf_reader.cc' object='src/common/dwarf/dumper_unittest-elf_reader.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/dumper_unittest-elf_reader.obj `if test -f 'src/common/dwarf/elf_reader.cc'; then $(CYGPATH_W) 'src/common/dwarf/elf_reader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/elf_reader.cc'; fi` + +src/common/dwarf/dumper_unittest-dwarf2reader_cfi_unittest.o: src/common/dwarf/dwarf2reader_cfi_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/dumper_unittest-dwarf2reader_cfi_unittest.o -MD -MP -MF src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2reader_cfi_unittest.Tpo -c -o src/common/dwarf/dumper_unittest-dwarf2reader_cfi_unittest.o `test -f 'src/common/dwarf/dwarf2reader_cfi_unittest.cc' || echo '$(srcdir)/'`src/common/dwarf/dwarf2reader_cfi_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2reader_cfi_unittest.Tpo src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2reader_cfi_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/dwarf2reader_cfi_unittest.cc' object='src/common/dwarf/dumper_unittest-dwarf2reader_cfi_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/dumper_unittest-dwarf2reader_cfi_unittest.o `test -f 'src/common/dwarf/dwarf2reader_cfi_unittest.cc' || echo '$(srcdir)/'`src/common/dwarf/dwarf2reader_cfi_unittest.cc + +src/common/dwarf/dumper_unittest-dwarf2reader_cfi_unittest.obj: src/common/dwarf/dwarf2reader_cfi_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/dumper_unittest-dwarf2reader_cfi_unittest.obj -MD -MP -MF src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2reader_cfi_unittest.Tpo -c -o src/common/dwarf/dumper_unittest-dwarf2reader_cfi_unittest.obj `if test -f 'src/common/dwarf/dwarf2reader_cfi_unittest.cc'; then $(CYGPATH_W) 'src/common/dwarf/dwarf2reader_cfi_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/dwarf2reader_cfi_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2reader_cfi_unittest.Tpo src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2reader_cfi_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/dwarf2reader_cfi_unittest.cc' object='src/common/dwarf/dumper_unittest-dwarf2reader_cfi_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/dumper_unittest-dwarf2reader_cfi_unittest.obj `if test -f 'src/common/dwarf/dwarf2reader_cfi_unittest.cc'; then $(CYGPATH_W) 'src/common/dwarf/dwarf2reader_cfi_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/dwarf2reader_cfi_unittest.cc'; fi` + +src/common/dwarf/dumper_unittest-dwarf2reader_die_unittest.o: src/common/dwarf/dwarf2reader_die_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/dumper_unittest-dwarf2reader_die_unittest.o -MD -MP -MF src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2reader_die_unittest.Tpo -c -o src/common/dwarf/dumper_unittest-dwarf2reader_die_unittest.o `test -f 'src/common/dwarf/dwarf2reader_die_unittest.cc' || echo '$(srcdir)/'`src/common/dwarf/dwarf2reader_die_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2reader_die_unittest.Tpo src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2reader_die_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/dwarf2reader_die_unittest.cc' object='src/common/dwarf/dumper_unittest-dwarf2reader_die_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/dumper_unittest-dwarf2reader_die_unittest.o `test -f 'src/common/dwarf/dwarf2reader_die_unittest.cc' || echo '$(srcdir)/'`src/common/dwarf/dwarf2reader_die_unittest.cc + +src/common/dwarf/dumper_unittest-dwarf2reader_die_unittest.obj: src/common/dwarf/dwarf2reader_die_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/dumper_unittest-dwarf2reader_die_unittest.obj -MD -MP -MF src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2reader_die_unittest.Tpo -c -o src/common/dwarf/dumper_unittest-dwarf2reader_die_unittest.obj `if test -f 'src/common/dwarf/dwarf2reader_die_unittest.cc'; then $(CYGPATH_W) 'src/common/dwarf/dwarf2reader_die_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/dwarf2reader_die_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2reader_die_unittest.Tpo src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2reader_die_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/dwarf2reader_die_unittest.cc' object='src/common/dwarf/dumper_unittest-dwarf2reader_die_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/dumper_unittest-dwarf2reader_die_unittest.obj `if test -f 'src/common/dwarf/dwarf2reader_die_unittest.cc'; then $(CYGPATH_W) 'src/common/dwarf/dwarf2reader_die_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/dwarf2reader_die_unittest.cc'; fi` + +src/common/linux/dumper_unittest-crc32.o: src/common/linux/crc32.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-crc32.o -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-crc32.Tpo -c -o src/common/linux/dumper_unittest-crc32.o `test -f 'src/common/linux/crc32.cc' || echo '$(srcdir)/'`src/common/linux/crc32.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-crc32.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-crc32.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/crc32.cc' object='src/common/linux/dumper_unittest-crc32.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-crc32.o `test -f 'src/common/linux/crc32.cc' || echo '$(srcdir)/'`src/common/linux/crc32.cc + +src/common/linux/dumper_unittest-crc32.obj: src/common/linux/crc32.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-crc32.obj -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-crc32.Tpo -c -o src/common/linux/dumper_unittest-crc32.obj `if test -f 'src/common/linux/crc32.cc'; then $(CYGPATH_W) 'src/common/linux/crc32.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/crc32.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-crc32.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-crc32.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/crc32.cc' object='src/common/linux/dumper_unittest-crc32.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-crc32.obj `if test -f 'src/common/linux/crc32.cc'; then $(CYGPATH_W) 'src/common/linux/crc32.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/crc32.cc'; fi` + +src/common/linux/dumper_unittest-dump_symbols.o: src/common/linux/dump_symbols.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-dump_symbols.o -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-dump_symbols.Tpo -c -o src/common/linux/dumper_unittest-dump_symbols.o `test -f 'src/common/linux/dump_symbols.cc' || echo '$(srcdir)/'`src/common/linux/dump_symbols.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-dump_symbols.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-dump_symbols.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/dump_symbols.cc' object='src/common/linux/dumper_unittest-dump_symbols.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-dump_symbols.o `test -f 'src/common/linux/dump_symbols.cc' || echo '$(srcdir)/'`src/common/linux/dump_symbols.cc + +src/common/linux/dumper_unittest-dump_symbols.obj: src/common/linux/dump_symbols.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-dump_symbols.obj -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-dump_symbols.Tpo -c -o src/common/linux/dumper_unittest-dump_symbols.obj `if test -f 'src/common/linux/dump_symbols.cc'; then $(CYGPATH_W) 'src/common/linux/dump_symbols.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/dump_symbols.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-dump_symbols.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-dump_symbols.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/dump_symbols.cc' object='src/common/linux/dumper_unittest-dump_symbols.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-dump_symbols.obj `if test -f 'src/common/linux/dump_symbols.cc'; then $(CYGPATH_W) 'src/common/linux/dump_symbols.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/dump_symbols.cc'; fi` + +src/common/linux/dumper_unittest-dump_symbols_unittest.o: src/common/linux/dump_symbols_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-dump_symbols_unittest.o -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-dump_symbols_unittest.Tpo -c -o src/common/linux/dumper_unittest-dump_symbols_unittest.o `test -f 'src/common/linux/dump_symbols_unittest.cc' || echo '$(srcdir)/'`src/common/linux/dump_symbols_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-dump_symbols_unittest.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-dump_symbols_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/dump_symbols_unittest.cc' object='src/common/linux/dumper_unittest-dump_symbols_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-dump_symbols_unittest.o `test -f 'src/common/linux/dump_symbols_unittest.cc' || echo '$(srcdir)/'`src/common/linux/dump_symbols_unittest.cc + +src/common/linux/dumper_unittest-dump_symbols_unittest.obj: src/common/linux/dump_symbols_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-dump_symbols_unittest.obj -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-dump_symbols_unittest.Tpo -c -o src/common/linux/dumper_unittest-dump_symbols_unittest.obj `if test -f 'src/common/linux/dump_symbols_unittest.cc'; then $(CYGPATH_W) 'src/common/linux/dump_symbols_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/dump_symbols_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-dump_symbols_unittest.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-dump_symbols_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/dump_symbols_unittest.cc' object='src/common/linux/dumper_unittest-dump_symbols_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-dump_symbols_unittest.obj `if test -f 'src/common/linux/dump_symbols_unittest.cc'; then $(CYGPATH_W) 'src/common/linux/dump_symbols_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/dump_symbols_unittest.cc'; fi` + +src/common/linux/dumper_unittest-elf_core_dump.o: src/common/linux/elf_core_dump.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-elf_core_dump.o -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-elf_core_dump.Tpo -c -o src/common/linux/dumper_unittest-elf_core_dump.o `test -f 'src/common/linux/elf_core_dump.cc' || echo '$(srcdir)/'`src/common/linux/elf_core_dump.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-elf_core_dump.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-elf_core_dump.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/elf_core_dump.cc' object='src/common/linux/dumper_unittest-elf_core_dump.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-elf_core_dump.o `test -f 'src/common/linux/elf_core_dump.cc' || echo '$(srcdir)/'`src/common/linux/elf_core_dump.cc + +src/common/linux/dumper_unittest-elf_core_dump.obj: src/common/linux/elf_core_dump.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-elf_core_dump.obj -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-elf_core_dump.Tpo -c -o src/common/linux/dumper_unittest-elf_core_dump.obj `if test -f 'src/common/linux/elf_core_dump.cc'; then $(CYGPATH_W) 'src/common/linux/elf_core_dump.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/elf_core_dump.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-elf_core_dump.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-elf_core_dump.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/elf_core_dump.cc' object='src/common/linux/dumper_unittest-elf_core_dump.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-elf_core_dump.obj `if test -f 'src/common/linux/elf_core_dump.cc'; then $(CYGPATH_W) 'src/common/linux/elf_core_dump.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/elf_core_dump.cc'; fi` + +src/common/linux/dumper_unittest-elf_core_dump_unittest.o: src/common/linux/elf_core_dump_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-elf_core_dump_unittest.o -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-elf_core_dump_unittest.Tpo -c -o src/common/linux/dumper_unittest-elf_core_dump_unittest.o `test -f 'src/common/linux/elf_core_dump_unittest.cc' || echo '$(srcdir)/'`src/common/linux/elf_core_dump_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-elf_core_dump_unittest.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-elf_core_dump_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/elf_core_dump_unittest.cc' object='src/common/linux/dumper_unittest-elf_core_dump_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-elf_core_dump_unittest.o `test -f 'src/common/linux/elf_core_dump_unittest.cc' || echo '$(srcdir)/'`src/common/linux/elf_core_dump_unittest.cc + +src/common/linux/dumper_unittest-elf_core_dump_unittest.obj: src/common/linux/elf_core_dump_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-elf_core_dump_unittest.obj -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-elf_core_dump_unittest.Tpo -c -o src/common/linux/dumper_unittest-elf_core_dump_unittest.obj `if test -f 'src/common/linux/elf_core_dump_unittest.cc'; then $(CYGPATH_W) 'src/common/linux/elf_core_dump_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/elf_core_dump_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-elf_core_dump_unittest.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-elf_core_dump_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/elf_core_dump_unittest.cc' object='src/common/linux/dumper_unittest-elf_core_dump_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-elf_core_dump_unittest.obj `if test -f 'src/common/linux/elf_core_dump_unittest.cc'; then $(CYGPATH_W) 'src/common/linux/elf_core_dump_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/elf_core_dump_unittest.cc'; fi` + +src/common/linux/dumper_unittest-elf_symbols_to_module.o: src/common/linux/elf_symbols_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-elf_symbols_to_module.o -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-elf_symbols_to_module.Tpo -c -o src/common/linux/dumper_unittest-elf_symbols_to_module.o `test -f 'src/common/linux/elf_symbols_to_module.cc' || echo '$(srcdir)/'`src/common/linux/elf_symbols_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-elf_symbols_to_module.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-elf_symbols_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/elf_symbols_to_module.cc' object='src/common/linux/dumper_unittest-elf_symbols_to_module.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-elf_symbols_to_module.o `test -f 'src/common/linux/elf_symbols_to_module.cc' || echo '$(srcdir)/'`src/common/linux/elf_symbols_to_module.cc + +src/common/linux/dumper_unittest-elf_symbols_to_module.obj: src/common/linux/elf_symbols_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-elf_symbols_to_module.obj -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-elf_symbols_to_module.Tpo -c -o src/common/linux/dumper_unittest-elf_symbols_to_module.obj `if test -f 'src/common/linux/elf_symbols_to_module.cc'; then $(CYGPATH_W) 'src/common/linux/elf_symbols_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/elf_symbols_to_module.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-elf_symbols_to_module.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-elf_symbols_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/elf_symbols_to_module.cc' object='src/common/linux/dumper_unittest-elf_symbols_to_module.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-elf_symbols_to_module.obj `if test -f 'src/common/linux/elf_symbols_to_module.cc'; then $(CYGPATH_W) 'src/common/linux/elf_symbols_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/elf_symbols_to_module.cc'; fi` + +src/common/linux/dumper_unittest-elf_symbols_to_module_unittest.o: src/common/linux/elf_symbols_to_module_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-elf_symbols_to_module_unittest.o -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-elf_symbols_to_module_unittest.Tpo -c -o src/common/linux/dumper_unittest-elf_symbols_to_module_unittest.o `test -f 'src/common/linux/elf_symbols_to_module_unittest.cc' || echo '$(srcdir)/'`src/common/linux/elf_symbols_to_module_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-elf_symbols_to_module_unittest.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-elf_symbols_to_module_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/elf_symbols_to_module_unittest.cc' object='src/common/linux/dumper_unittest-elf_symbols_to_module_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-elf_symbols_to_module_unittest.o `test -f 'src/common/linux/elf_symbols_to_module_unittest.cc' || echo '$(srcdir)/'`src/common/linux/elf_symbols_to_module_unittest.cc + +src/common/linux/dumper_unittest-elf_symbols_to_module_unittest.obj: src/common/linux/elf_symbols_to_module_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-elf_symbols_to_module_unittest.obj -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-elf_symbols_to_module_unittest.Tpo -c -o src/common/linux/dumper_unittest-elf_symbols_to_module_unittest.obj `if test -f 'src/common/linux/elf_symbols_to_module_unittest.cc'; then $(CYGPATH_W) 'src/common/linux/elf_symbols_to_module_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/elf_symbols_to_module_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-elf_symbols_to_module_unittest.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-elf_symbols_to_module_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/elf_symbols_to_module_unittest.cc' object='src/common/linux/dumper_unittest-elf_symbols_to_module_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-elf_symbols_to_module_unittest.obj `if test -f 'src/common/linux/elf_symbols_to_module_unittest.cc'; then $(CYGPATH_W) 'src/common/linux/elf_symbols_to_module_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/elf_symbols_to_module_unittest.cc'; fi` + +src/common/linux/dumper_unittest-elfutils.o: src/common/linux/elfutils.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-elfutils.o -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-elfutils.Tpo -c -o src/common/linux/dumper_unittest-elfutils.o `test -f 'src/common/linux/elfutils.cc' || echo '$(srcdir)/'`src/common/linux/elfutils.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-elfutils.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-elfutils.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/elfutils.cc' object='src/common/linux/dumper_unittest-elfutils.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-elfutils.o `test -f 'src/common/linux/elfutils.cc' || echo '$(srcdir)/'`src/common/linux/elfutils.cc + +src/common/linux/dumper_unittest-elfutils.obj: src/common/linux/elfutils.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-elfutils.obj -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-elfutils.Tpo -c -o src/common/linux/dumper_unittest-elfutils.obj `if test -f 'src/common/linux/elfutils.cc'; then $(CYGPATH_W) 'src/common/linux/elfutils.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/elfutils.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-elfutils.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-elfutils.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/elfutils.cc' object='src/common/linux/dumper_unittest-elfutils.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-elfutils.obj `if test -f 'src/common/linux/elfutils.cc'; then $(CYGPATH_W) 'src/common/linux/elfutils.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/elfutils.cc'; fi` + +src/common/linux/dumper_unittest-file_id.o: src/common/linux/file_id.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-file_id.o -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-file_id.Tpo -c -o src/common/linux/dumper_unittest-file_id.o `test -f 'src/common/linux/file_id.cc' || echo '$(srcdir)/'`src/common/linux/file_id.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-file_id.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-file_id.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/file_id.cc' object='src/common/linux/dumper_unittest-file_id.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-file_id.o `test -f 'src/common/linux/file_id.cc' || echo '$(srcdir)/'`src/common/linux/file_id.cc + +src/common/linux/dumper_unittest-file_id.obj: src/common/linux/file_id.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-file_id.obj -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-file_id.Tpo -c -o src/common/linux/dumper_unittest-file_id.obj `if test -f 'src/common/linux/file_id.cc'; then $(CYGPATH_W) 'src/common/linux/file_id.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/file_id.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-file_id.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-file_id.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/file_id.cc' object='src/common/linux/dumper_unittest-file_id.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-file_id.obj `if test -f 'src/common/linux/file_id.cc'; then $(CYGPATH_W) 'src/common/linux/file_id.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/file_id.cc'; fi` + +src/common/linux/dumper_unittest-file_id_unittest.o: src/common/linux/file_id_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-file_id_unittest.o -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-file_id_unittest.Tpo -c -o src/common/linux/dumper_unittest-file_id_unittest.o `test -f 'src/common/linux/file_id_unittest.cc' || echo '$(srcdir)/'`src/common/linux/file_id_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-file_id_unittest.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-file_id_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/file_id_unittest.cc' object='src/common/linux/dumper_unittest-file_id_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-file_id_unittest.o `test -f 'src/common/linux/file_id_unittest.cc' || echo '$(srcdir)/'`src/common/linux/file_id_unittest.cc + +src/common/linux/dumper_unittest-file_id_unittest.obj: src/common/linux/file_id_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-file_id_unittest.obj -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-file_id_unittest.Tpo -c -o src/common/linux/dumper_unittest-file_id_unittest.obj `if test -f 'src/common/linux/file_id_unittest.cc'; then $(CYGPATH_W) 'src/common/linux/file_id_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/file_id_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-file_id_unittest.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-file_id_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/file_id_unittest.cc' object='src/common/linux/dumper_unittest-file_id_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-file_id_unittest.obj `if test -f 'src/common/linux/file_id_unittest.cc'; then $(CYGPATH_W) 'src/common/linux/file_id_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/file_id_unittest.cc'; fi` + +src/common/linux/dumper_unittest-linux_libc_support.o: src/common/linux/linux_libc_support.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-linux_libc_support.o -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-linux_libc_support.Tpo -c -o src/common/linux/dumper_unittest-linux_libc_support.o `test -f 'src/common/linux/linux_libc_support.cc' || echo '$(srcdir)/'`src/common/linux/linux_libc_support.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-linux_libc_support.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-linux_libc_support.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/linux_libc_support.cc' object='src/common/linux/dumper_unittest-linux_libc_support.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-linux_libc_support.o `test -f 'src/common/linux/linux_libc_support.cc' || echo '$(srcdir)/'`src/common/linux/linux_libc_support.cc + +src/common/linux/dumper_unittest-linux_libc_support.obj: src/common/linux/linux_libc_support.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-linux_libc_support.obj -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-linux_libc_support.Tpo -c -o src/common/linux/dumper_unittest-linux_libc_support.obj `if test -f 'src/common/linux/linux_libc_support.cc'; then $(CYGPATH_W) 'src/common/linux/linux_libc_support.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/linux_libc_support.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-linux_libc_support.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-linux_libc_support.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/linux_libc_support.cc' object='src/common/linux/dumper_unittest-linux_libc_support.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-linux_libc_support.obj `if test -f 'src/common/linux/linux_libc_support.cc'; then $(CYGPATH_W) 'src/common/linux/linux_libc_support.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/linux_libc_support.cc'; fi` + +src/common/linux/dumper_unittest-memory_mapped_file.o: src/common/linux/memory_mapped_file.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-memory_mapped_file.o -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-memory_mapped_file.Tpo -c -o src/common/linux/dumper_unittest-memory_mapped_file.o `test -f 'src/common/linux/memory_mapped_file.cc' || echo '$(srcdir)/'`src/common/linux/memory_mapped_file.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-memory_mapped_file.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-memory_mapped_file.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/memory_mapped_file.cc' object='src/common/linux/dumper_unittest-memory_mapped_file.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-memory_mapped_file.o `test -f 'src/common/linux/memory_mapped_file.cc' || echo '$(srcdir)/'`src/common/linux/memory_mapped_file.cc + +src/common/linux/dumper_unittest-memory_mapped_file.obj: src/common/linux/memory_mapped_file.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-memory_mapped_file.obj -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-memory_mapped_file.Tpo -c -o src/common/linux/dumper_unittest-memory_mapped_file.obj `if test -f 'src/common/linux/memory_mapped_file.cc'; then $(CYGPATH_W) 'src/common/linux/memory_mapped_file.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/memory_mapped_file.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-memory_mapped_file.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-memory_mapped_file.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/memory_mapped_file.cc' object='src/common/linux/dumper_unittest-memory_mapped_file.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-memory_mapped_file.obj `if test -f 'src/common/linux/memory_mapped_file.cc'; then $(CYGPATH_W) 'src/common/linux/memory_mapped_file.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/memory_mapped_file.cc'; fi` + +src/common/linux/dumper_unittest-memory_mapped_file_unittest.o: src/common/linux/memory_mapped_file_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-memory_mapped_file_unittest.o -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-memory_mapped_file_unittest.Tpo -c -o src/common/linux/dumper_unittest-memory_mapped_file_unittest.o `test -f 'src/common/linux/memory_mapped_file_unittest.cc' || echo '$(srcdir)/'`src/common/linux/memory_mapped_file_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-memory_mapped_file_unittest.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-memory_mapped_file_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/memory_mapped_file_unittest.cc' object='src/common/linux/dumper_unittest-memory_mapped_file_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-memory_mapped_file_unittest.o `test -f 'src/common/linux/memory_mapped_file_unittest.cc' || echo '$(srcdir)/'`src/common/linux/memory_mapped_file_unittest.cc + +src/common/linux/dumper_unittest-memory_mapped_file_unittest.obj: src/common/linux/memory_mapped_file_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-memory_mapped_file_unittest.obj -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-memory_mapped_file_unittest.Tpo -c -o src/common/linux/dumper_unittest-memory_mapped_file_unittest.obj `if test -f 'src/common/linux/memory_mapped_file_unittest.cc'; then $(CYGPATH_W) 'src/common/linux/memory_mapped_file_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/memory_mapped_file_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-memory_mapped_file_unittest.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-memory_mapped_file_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/memory_mapped_file_unittest.cc' object='src/common/linux/dumper_unittest-memory_mapped_file_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-memory_mapped_file_unittest.obj `if test -f 'src/common/linux/memory_mapped_file_unittest.cc'; then $(CYGPATH_W) 'src/common/linux/memory_mapped_file_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/memory_mapped_file_unittest.cc'; fi` + +src/common/linux/dumper_unittest-safe_readlink.o: src/common/linux/safe_readlink.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-safe_readlink.o -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-safe_readlink.Tpo -c -o src/common/linux/dumper_unittest-safe_readlink.o `test -f 'src/common/linux/safe_readlink.cc' || echo '$(srcdir)/'`src/common/linux/safe_readlink.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-safe_readlink.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-safe_readlink.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/safe_readlink.cc' object='src/common/linux/dumper_unittest-safe_readlink.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-safe_readlink.o `test -f 'src/common/linux/safe_readlink.cc' || echo '$(srcdir)/'`src/common/linux/safe_readlink.cc + +src/common/linux/dumper_unittest-safe_readlink.obj: src/common/linux/safe_readlink.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-safe_readlink.obj -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-safe_readlink.Tpo -c -o src/common/linux/dumper_unittest-safe_readlink.obj `if test -f 'src/common/linux/safe_readlink.cc'; then $(CYGPATH_W) 'src/common/linux/safe_readlink.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/safe_readlink.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-safe_readlink.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-safe_readlink.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/safe_readlink.cc' object='src/common/linux/dumper_unittest-safe_readlink.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-safe_readlink.obj `if test -f 'src/common/linux/safe_readlink.cc'; then $(CYGPATH_W) 'src/common/linux/safe_readlink.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/safe_readlink.cc'; fi` + +src/common/linux/dumper_unittest-safe_readlink_unittest.o: src/common/linux/safe_readlink_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-safe_readlink_unittest.o -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-safe_readlink_unittest.Tpo -c -o src/common/linux/dumper_unittest-safe_readlink_unittest.o `test -f 'src/common/linux/safe_readlink_unittest.cc' || echo '$(srcdir)/'`src/common/linux/safe_readlink_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-safe_readlink_unittest.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-safe_readlink_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/safe_readlink_unittest.cc' object='src/common/linux/dumper_unittest-safe_readlink_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-safe_readlink_unittest.o `test -f 'src/common/linux/safe_readlink_unittest.cc' || echo '$(srcdir)/'`src/common/linux/safe_readlink_unittest.cc + +src/common/linux/dumper_unittest-safe_readlink_unittest.obj: src/common/linux/safe_readlink_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-safe_readlink_unittest.obj -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-safe_readlink_unittest.Tpo -c -o src/common/linux/dumper_unittest-safe_readlink_unittest.obj `if test -f 'src/common/linux/safe_readlink_unittest.cc'; then $(CYGPATH_W) 'src/common/linux/safe_readlink_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/safe_readlink_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-safe_readlink_unittest.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-safe_readlink_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/safe_readlink_unittest.cc' object='src/common/linux/dumper_unittest-safe_readlink_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-safe_readlink_unittest.obj `if test -f 'src/common/linux/safe_readlink_unittest.cc'; then $(CYGPATH_W) 'src/common/linux/safe_readlink_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/safe_readlink_unittest.cc'; fi` + +src/common/linux/dumper_unittest-synth_elf.o: src/common/linux/synth_elf.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-synth_elf.o -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-synth_elf.Tpo -c -o src/common/linux/dumper_unittest-synth_elf.o `test -f 'src/common/linux/synth_elf.cc' || echo '$(srcdir)/'`src/common/linux/synth_elf.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-synth_elf.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-synth_elf.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/synth_elf.cc' object='src/common/linux/dumper_unittest-synth_elf.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-synth_elf.o `test -f 'src/common/linux/synth_elf.cc' || echo '$(srcdir)/'`src/common/linux/synth_elf.cc + +src/common/linux/dumper_unittest-synth_elf.obj: src/common/linux/synth_elf.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-synth_elf.obj -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-synth_elf.Tpo -c -o src/common/linux/dumper_unittest-synth_elf.obj `if test -f 'src/common/linux/synth_elf.cc'; then $(CYGPATH_W) 'src/common/linux/synth_elf.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/synth_elf.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-synth_elf.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-synth_elf.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/synth_elf.cc' object='src/common/linux/dumper_unittest-synth_elf.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-synth_elf.obj `if test -f 'src/common/linux/synth_elf.cc'; then $(CYGPATH_W) 'src/common/linux/synth_elf.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/synth_elf.cc'; fi` + +src/common/linux/dumper_unittest-synth_elf_unittest.o: src/common/linux/synth_elf_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-synth_elf_unittest.o -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-synth_elf_unittest.Tpo -c -o src/common/linux/dumper_unittest-synth_elf_unittest.o `test -f 'src/common/linux/synth_elf_unittest.cc' || echo '$(srcdir)/'`src/common/linux/synth_elf_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-synth_elf_unittest.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-synth_elf_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/synth_elf_unittest.cc' object='src/common/linux/dumper_unittest-synth_elf_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-synth_elf_unittest.o `test -f 'src/common/linux/synth_elf_unittest.cc' || echo '$(srcdir)/'`src/common/linux/synth_elf_unittest.cc + +src/common/linux/dumper_unittest-synth_elf_unittest.obj: src/common/linux/synth_elf_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/dumper_unittest-synth_elf_unittest.obj -MD -MP -MF src/common/linux/$(DEPDIR)/dumper_unittest-synth_elf_unittest.Tpo -c -o src/common/linux/dumper_unittest-synth_elf_unittest.obj `if test -f 'src/common/linux/synth_elf_unittest.cc'; then $(CYGPATH_W) 'src/common/linux/synth_elf_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/synth_elf_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/dumper_unittest-synth_elf_unittest.Tpo src/common/linux/$(DEPDIR)/dumper_unittest-synth_elf_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/synth_elf_unittest.cc' object='src/common/linux/dumper_unittest-synth_elf_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/dumper_unittest-synth_elf_unittest.obj `if test -f 'src/common/linux/synth_elf_unittest.cc'; then $(CYGPATH_W) 'src/common/linux/synth_elf_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/synth_elf_unittest.cc'; fi` + +src/common/linux/tests/dumper_unittest-crash_generator.o: src/common/linux/tests/crash_generator.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/tests/dumper_unittest-crash_generator.o -MD -MP -MF src/common/linux/tests/$(DEPDIR)/dumper_unittest-crash_generator.Tpo -c -o src/common/linux/tests/dumper_unittest-crash_generator.o `test -f 'src/common/linux/tests/crash_generator.cc' || echo '$(srcdir)/'`src/common/linux/tests/crash_generator.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/tests/$(DEPDIR)/dumper_unittest-crash_generator.Tpo src/common/linux/tests/$(DEPDIR)/dumper_unittest-crash_generator.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/tests/crash_generator.cc' object='src/common/linux/tests/dumper_unittest-crash_generator.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/tests/dumper_unittest-crash_generator.o `test -f 'src/common/linux/tests/crash_generator.cc' || echo '$(srcdir)/'`src/common/linux/tests/crash_generator.cc + +src/common/linux/tests/dumper_unittest-crash_generator.obj: src/common/linux/tests/crash_generator.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/tests/dumper_unittest-crash_generator.obj -MD -MP -MF src/common/linux/tests/$(DEPDIR)/dumper_unittest-crash_generator.Tpo -c -o src/common/linux/tests/dumper_unittest-crash_generator.obj `if test -f 'src/common/linux/tests/crash_generator.cc'; then $(CYGPATH_W) 'src/common/linux/tests/crash_generator.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/tests/crash_generator.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/tests/$(DEPDIR)/dumper_unittest-crash_generator.Tpo src/common/linux/tests/$(DEPDIR)/dumper_unittest-crash_generator.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/tests/crash_generator.cc' object='src/common/linux/tests/dumper_unittest-crash_generator.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/tests/dumper_unittest-crash_generator.obj `if test -f 'src/common/linux/tests/crash_generator.cc'; then $(CYGPATH_W) 'src/common/linux/tests/crash_generator.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/tests/crash_generator.cc'; fi` + +src/common/tests/dumper_unittest-file_utils.o: src/common/tests/file_utils.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/tests/dumper_unittest-file_utils.o -MD -MP -MF src/common/tests/$(DEPDIR)/dumper_unittest-file_utils.Tpo -c -o src/common/tests/dumper_unittest-file_utils.o `test -f 'src/common/tests/file_utils.cc' || echo '$(srcdir)/'`src/common/tests/file_utils.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/tests/$(DEPDIR)/dumper_unittest-file_utils.Tpo src/common/tests/$(DEPDIR)/dumper_unittest-file_utils.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/tests/file_utils.cc' object='src/common/tests/dumper_unittest-file_utils.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tests/dumper_unittest-file_utils.o `test -f 'src/common/tests/file_utils.cc' || echo '$(srcdir)/'`src/common/tests/file_utils.cc + +src/common/tests/dumper_unittest-file_utils.obj: src/common/tests/file_utils.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/tests/dumper_unittest-file_utils.obj -MD -MP -MF src/common/tests/$(DEPDIR)/dumper_unittest-file_utils.Tpo -c -o src/common/tests/dumper_unittest-file_utils.obj `if test -f 'src/common/tests/file_utils.cc'; then $(CYGPATH_W) 'src/common/tests/file_utils.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/tests/file_utils.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/tests/$(DEPDIR)/dumper_unittest-file_utils.Tpo src/common/tests/$(DEPDIR)/dumper_unittest-file_utils.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/tests/file_utils.cc' object='src/common/tests/dumper_unittest-file_utils.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dumper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tests/dumper_unittest-file_utils.obj `if test -f 'src/common/tests/file_utils.cc'; then $(CYGPATH_W) 'src/common/tests/file_utils.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/tests/file_utils.cc'; fi` + +src/common/dwarf/dwarf2reader_lineinfo_unittest-dwarf2reader_lineinfo_unittest.o: src/common/dwarf/dwarf2reader_lineinfo_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dwarf_dwarf2reader_lineinfo_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/dwarf2reader_lineinfo_unittest-dwarf2reader_lineinfo_unittest.o -MD -MP -MF src/common/dwarf/$(DEPDIR)/dwarf2reader_lineinfo_unittest-dwarf2reader_lineinfo_unittest.Tpo -c -o src/common/dwarf/dwarf2reader_lineinfo_unittest-dwarf2reader_lineinfo_unittest.o `test -f 'src/common/dwarf/dwarf2reader_lineinfo_unittest.cc' || echo '$(srcdir)/'`src/common/dwarf/dwarf2reader_lineinfo_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/dwarf2reader_lineinfo_unittest-dwarf2reader_lineinfo_unittest.Tpo src/common/dwarf/$(DEPDIR)/dwarf2reader_lineinfo_unittest-dwarf2reader_lineinfo_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/dwarf2reader_lineinfo_unittest.cc' object='src/common/dwarf/dwarf2reader_lineinfo_unittest-dwarf2reader_lineinfo_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dwarf_dwarf2reader_lineinfo_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/dwarf2reader_lineinfo_unittest-dwarf2reader_lineinfo_unittest.o `test -f 'src/common/dwarf/dwarf2reader_lineinfo_unittest.cc' || echo '$(srcdir)/'`src/common/dwarf/dwarf2reader_lineinfo_unittest.cc + +src/common/dwarf/dwarf2reader_lineinfo_unittest-dwarf2reader_lineinfo_unittest.obj: src/common/dwarf/dwarf2reader_lineinfo_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dwarf_dwarf2reader_lineinfo_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/dwarf2reader_lineinfo_unittest-dwarf2reader_lineinfo_unittest.obj -MD -MP -MF src/common/dwarf/$(DEPDIR)/dwarf2reader_lineinfo_unittest-dwarf2reader_lineinfo_unittest.Tpo -c -o src/common/dwarf/dwarf2reader_lineinfo_unittest-dwarf2reader_lineinfo_unittest.obj `if test -f 'src/common/dwarf/dwarf2reader_lineinfo_unittest.cc'; then $(CYGPATH_W) 'src/common/dwarf/dwarf2reader_lineinfo_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/dwarf2reader_lineinfo_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/dwarf2reader_lineinfo_unittest-dwarf2reader_lineinfo_unittest.Tpo src/common/dwarf/$(DEPDIR)/dwarf2reader_lineinfo_unittest-dwarf2reader_lineinfo_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/dwarf2reader_lineinfo_unittest.cc' object='src/common/dwarf/dwarf2reader_lineinfo_unittest-dwarf2reader_lineinfo_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dwarf_dwarf2reader_lineinfo_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/dwarf2reader_lineinfo_unittest-dwarf2reader_lineinfo_unittest.obj `if test -f 'src/common/dwarf/dwarf2reader_lineinfo_unittest.cc'; then $(CYGPATH_W) 'src/common/dwarf/dwarf2reader_lineinfo_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/dwarf2reader_lineinfo_unittest.cc'; fi` + +src/common/dwarf/dwarf2reader_splitfunctions_unittest-dwarf2reader_splitfunctions_unittest.o: src/common/dwarf/dwarf2reader_splitfunctions_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dwarf_dwarf2reader_splitfunctions_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/dwarf2reader_splitfunctions_unittest-dwarf2reader_splitfunctions_unittest.o -MD -MP -MF src/common/dwarf/$(DEPDIR)/dwarf2reader_splitfunctions_unittest-dwarf2reader_splitfunctions_unittest.Tpo -c -o src/common/dwarf/dwarf2reader_splitfunctions_unittest-dwarf2reader_splitfunctions_unittest.o `test -f 'src/common/dwarf/dwarf2reader_splitfunctions_unittest.cc' || echo '$(srcdir)/'`src/common/dwarf/dwarf2reader_splitfunctions_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/dwarf2reader_splitfunctions_unittest-dwarf2reader_splitfunctions_unittest.Tpo src/common/dwarf/$(DEPDIR)/dwarf2reader_splitfunctions_unittest-dwarf2reader_splitfunctions_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/dwarf2reader_splitfunctions_unittest.cc' object='src/common/dwarf/dwarf2reader_splitfunctions_unittest-dwarf2reader_splitfunctions_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dwarf_dwarf2reader_splitfunctions_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/dwarf2reader_splitfunctions_unittest-dwarf2reader_splitfunctions_unittest.o `test -f 'src/common/dwarf/dwarf2reader_splitfunctions_unittest.cc' || echo '$(srcdir)/'`src/common/dwarf/dwarf2reader_splitfunctions_unittest.cc + +src/common/dwarf/dwarf2reader_splitfunctions_unittest-dwarf2reader_splitfunctions_unittest.obj: src/common/dwarf/dwarf2reader_splitfunctions_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dwarf_dwarf2reader_splitfunctions_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/dwarf2reader_splitfunctions_unittest-dwarf2reader_splitfunctions_unittest.obj -MD -MP -MF src/common/dwarf/$(DEPDIR)/dwarf2reader_splitfunctions_unittest-dwarf2reader_splitfunctions_unittest.Tpo -c -o src/common/dwarf/dwarf2reader_splitfunctions_unittest-dwarf2reader_splitfunctions_unittest.obj `if test -f 'src/common/dwarf/dwarf2reader_splitfunctions_unittest.cc'; then $(CYGPATH_W) 'src/common/dwarf/dwarf2reader_splitfunctions_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/dwarf2reader_splitfunctions_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/dwarf2reader_splitfunctions_unittest-dwarf2reader_splitfunctions_unittest.Tpo src/common/dwarf/$(DEPDIR)/dwarf2reader_splitfunctions_unittest-dwarf2reader_splitfunctions_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/dwarf2reader_splitfunctions_unittest.cc' object='src/common/dwarf/dwarf2reader_splitfunctions_unittest-dwarf2reader_splitfunctions_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_dwarf_dwarf2reader_splitfunctions_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/dwarf2reader_splitfunctions_unittest-dwarf2reader_splitfunctions_unittest.obj `if test -f 'src/common/dwarf/dwarf2reader_splitfunctions_unittest.cc'; then $(CYGPATH_W) 'src/common/dwarf/dwarf2reader_splitfunctions_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/dwarf2reader_splitfunctions_unittest.cc'; fi` + +src/common/linux/google_crashdump_uploader_test-google_crashdump_uploader.o: src/common/linux/google_crashdump_uploader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_linux_google_crashdump_uploader_test_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/google_crashdump_uploader_test-google_crashdump_uploader.o -MD -MP -MF src/common/linux/$(DEPDIR)/google_crashdump_uploader_test-google_crashdump_uploader.Tpo -c -o src/common/linux/google_crashdump_uploader_test-google_crashdump_uploader.o `test -f 'src/common/linux/google_crashdump_uploader.cc' || echo '$(srcdir)/'`src/common/linux/google_crashdump_uploader.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/google_crashdump_uploader_test-google_crashdump_uploader.Tpo src/common/linux/$(DEPDIR)/google_crashdump_uploader_test-google_crashdump_uploader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/google_crashdump_uploader.cc' object='src/common/linux/google_crashdump_uploader_test-google_crashdump_uploader.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_linux_google_crashdump_uploader_test_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/google_crashdump_uploader_test-google_crashdump_uploader.o `test -f 'src/common/linux/google_crashdump_uploader.cc' || echo '$(srcdir)/'`src/common/linux/google_crashdump_uploader.cc + +src/common/linux/google_crashdump_uploader_test-google_crashdump_uploader.obj: src/common/linux/google_crashdump_uploader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_linux_google_crashdump_uploader_test_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/google_crashdump_uploader_test-google_crashdump_uploader.obj -MD -MP -MF src/common/linux/$(DEPDIR)/google_crashdump_uploader_test-google_crashdump_uploader.Tpo -c -o src/common/linux/google_crashdump_uploader_test-google_crashdump_uploader.obj `if test -f 'src/common/linux/google_crashdump_uploader.cc'; then $(CYGPATH_W) 'src/common/linux/google_crashdump_uploader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/google_crashdump_uploader.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/google_crashdump_uploader_test-google_crashdump_uploader.Tpo src/common/linux/$(DEPDIR)/google_crashdump_uploader_test-google_crashdump_uploader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/google_crashdump_uploader.cc' object='src/common/linux/google_crashdump_uploader_test-google_crashdump_uploader.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_linux_google_crashdump_uploader_test_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/google_crashdump_uploader_test-google_crashdump_uploader.obj `if test -f 'src/common/linux/google_crashdump_uploader.cc'; then $(CYGPATH_W) 'src/common/linux/google_crashdump_uploader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/google_crashdump_uploader.cc'; fi` + +src/common/linux/google_crashdump_uploader_test-google_crashdump_uploader_test.o: src/common/linux/google_crashdump_uploader_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_linux_google_crashdump_uploader_test_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/google_crashdump_uploader_test-google_crashdump_uploader_test.o -MD -MP -MF src/common/linux/$(DEPDIR)/google_crashdump_uploader_test-google_crashdump_uploader_test.Tpo -c -o src/common/linux/google_crashdump_uploader_test-google_crashdump_uploader_test.o `test -f 'src/common/linux/google_crashdump_uploader_test.cc' || echo '$(srcdir)/'`src/common/linux/google_crashdump_uploader_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/google_crashdump_uploader_test-google_crashdump_uploader_test.Tpo src/common/linux/$(DEPDIR)/google_crashdump_uploader_test-google_crashdump_uploader_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/google_crashdump_uploader_test.cc' object='src/common/linux/google_crashdump_uploader_test-google_crashdump_uploader_test.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_linux_google_crashdump_uploader_test_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/google_crashdump_uploader_test-google_crashdump_uploader_test.o `test -f 'src/common/linux/google_crashdump_uploader_test.cc' || echo '$(srcdir)/'`src/common/linux/google_crashdump_uploader_test.cc + +src/common/linux/google_crashdump_uploader_test-google_crashdump_uploader_test.obj: src/common/linux/google_crashdump_uploader_test.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_linux_google_crashdump_uploader_test_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/google_crashdump_uploader_test-google_crashdump_uploader_test.obj -MD -MP -MF src/common/linux/$(DEPDIR)/google_crashdump_uploader_test-google_crashdump_uploader_test.Tpo -c -o src/common/linux/google_crashdump_uploader_test-google_crashdump_uploader_test.obj `if test -f 'src/common/linux/google_crashdump_uploader_test.cc'; then $(CYGPATH_W) 'src/common/linux/google_crashdump_uploader_test.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/google_crashdump_uploader_test.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/google_crashdump_uploader_test-google_crashdump_uploader_test.Tpo src/common/linux/$(DEPDIR)/google_crashdump_uploader_test-google_crashdump_uploader_test.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/google_crashdump_uploader_test.cc' object='src/common/linux/google_crashdump_uploader_test-google_crashdump_uploader_test.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_linux_google_crashdump_uploader_test_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/google_crashdump_uploader_test-google_crashdump_uploader_test.obj `if test -f 'src/common/linux/google_crashdump_uploader_test.cc'; then $(CYGPATH_W) 'src/common/linux/google_crashdump_uploader_test.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/google_crashdump_uploader_test.cc'; fi` + +src/common/linux/google_crashdump_uploader_test-libcurl_wrapper.o: src/common/linux/libcurl_wrapper.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_linux_google_crashdump_uploader_test_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/google_crashdump_uploader_test-libcurl_wrapper.o -MD -MP -MF src/common/linux/$(DEPDIR)/google_crashdump_uploader_test-libcurl_wrapper.Tpo -c -o src/common/linux/google_crashdump_uploader_test-libcurl_wrapper.o `test -f 'src/common/linux/libcurl_wrapper.cc' || echo '$(srcdir)/'`src/common/linux/libcurl_wrapper.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/google_crashdump_uploader_test-libcurl_wrapper.Tpo src/common/linux/$(DEPDIR)/google_crashdump_uploader_test-libcurl_wrapper.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/libcurl_wrapper.cc' object='src/common/linux/google_crashdump_uploader_test-libcurl_wrapper.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_linux_google_crashdump_uploader_test_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/google_crashdump_uploader_test-libcurl_wrapper.o `test -f 'src/common/linux/libcurl_wrapper.cc' || echo '$(srcdir)/'`src/common/linux/libcurl_wrapper.cc + +src/common/linux/google_crashdump_uploader_test-libcurl_wrapper.obj: src/common/linux/libcurl_wrapper.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_linux_google_crashdump_uploader_test_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/google_crashdump_uploader_test-libcurl_wrapper.obj -MD -MP -MF src/common/linux/$(DEPDIR)/google_crashdump_uploader_test-libcurl_wrapper.Tpo -c -o src/common/linux/google_crashdump_uploader_test-libcurl_wrapper.obj `if test -f 'src/common/linux/libcurl_wrapper.cc'; then $(CYGPATH_W) 'src/common/linux/libcurl_wrapper.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/libcurl_wrapper.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/google_crashdump_uploader_test-libcurl_wrapper.Tpo src/common/linux/$(DEPDIR)/google_crashdump_uploader_test-libcurl_wrapper.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/libcurl_wrapper.cc' object='src/common/linux/google_crashdump_uploader_test-libcurl_wrapper.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_linux_google_crashdump_uploader_test_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/google_crashdump_uploader_test-libcurl_wrapper.obj `if test -f 'src/common/linux/libcurl_wrapper.cc'; then $(CYGPATH_W) 'src/common/linux/libcurl_wrapper.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/libcurl_wrapper.cc'; fi` + +src/common/mac_macho_reader_unittest-dwarf_cfi_to_module.o: src/common/dwarf_cfi_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac_macho_reader_unittest-dwarf_cfi_to_module.o -MD -MP -MF src/common/$(DEPDIR)/mac_macho_reader_unittest-dwarf_cfi_to_module.Tpo -c -o src/common/mac_macho_reader_unittest-dwarf_cfi_to_module.o `test -f 'src/common/dwarf_cfi_to_module.cc' || echo '$(srcdir)/'`src/common/dwarf_cfi_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/mac_macho_reader_unittest-dwarf_cfi_to_module.Tpo src/common/$(DEPDIR)/mac_macho_reader_unittest-dwarf_cfi_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_cfi_to_module.cc' object='src/common/mac_macho_reader_unittest-dwarf_cfi_to_module.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac_macho_reader_unittest-dwarf_cfi_to_module.o `test -f 'src/common/dwarf_cfi_to_module.cc' || echo '$(srcdir)/'`src/common/dwarf_cfi_to_module.cc + +src/common/mac_macho_reader_unittest-dwarf_cfi_to_module.obj: src/common/dwarf_cfi_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac_macho_reader_unittest-dwarf_cfi_to_module.obj -MD -MP -MF src/common/$(DEPDIR)/mac_macho_reader_unittest-dwarf_cfi_to_module.Tpo -c -o src/common/mac_macho_reader_unittest-dwarf_cfi_to_module.obj `if test -f 'src/common/dwarf_cfi_to_module.cc'; then $(CYGPATH_W) 'src/common/dwarf_cfi_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_cfi_to_module.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/mac_macho_reader_unittest-dwarf_cfi_to_module.Tpo src/common/$(DEPDIR)/mac_macho_reader_unittest-dwarf_cfi_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_cfi_to_module.cc' object='src/common/mac_macho_reader_unittest-dwarf_cfi_to_module.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac_macho_reader_unittest-dwarf_cfi_to_module.obj `if test -f 'src/common/dwarf_cfi_to_module.cc'; then $(CYGPATH_W) 'src/common/dwarf_cfi_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_cfi_to_module.cc'; fi` + +src/common/mac_macho_reader_unittest-dwarf_cu_to_module.o: src/common/dwarf_cu_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac_macho_reader_unittest-dwarf_cu_to_module.o -MD -MP -MF src/common/$(DEPDIR)/mac_macho_reader_unittest-dwarf_cu_to_module.Tpo -c -o src/common/mac_macho_reader_unittest-dwarf_cu_to_module.o `test -f 'src/common/dwarf_cu_to_module.cc' || echo '$(srcdir)/'`src/common/dwarf_cu_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/mac_macho_reader_unittest-dwarf_cu_to_module.Tpo src/common/$(DEPDIR)/mac_macho_reader_unittest-dwarf_cu_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_cu_to_module.cc' object='src/common/mac_macho_reader_unittest-dwarf_cu_to_module.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac_macho_reader_unittest-dwarf_cu_to_module.o `test -f 'src/common/dwarf_cu_to_module.cc' || echo '$(srcdir)/'`src/common/dwarf_cu_to_module.cc + +src/common/mac_macho_reader_unittest-dwarf_cu_to_module.obj: src/common/dwarf_cu_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac_macho_reader_unittest-dwarf_cu_to_module.obj -MD -MP -MF src/common/$(DEPDIR)/mac_macho_reader_unittest-dwarf_cu_to_module.Tpo -c -o src/common/mac_macho_reader_unittest-dwarf_cu_to_module.obj `if test -f 'src/common/dwarf_cu_to_module.cc'; then $(CYGPATH_W) 'src/common/dwarf_cu_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_cu_to_module.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/mac_macho_reader_unittest-dwarf_cu_to_module.Tpo src/common/$(DEPDIR)/mac_macho_reader_unittest-dwarf_cu_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_cu_to_module.cc' object='src/common/mac_macho_reader_unittest-dwarf_cu_to_module.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac_macho_reader_unittest-dwarf_cu_to_module.obj `if test -f 'src/common/dwarf_cu_to_module.cc'; then $(CYGPATH_W) 'src/common/dwarf_cu_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_cu_to_module.cc'; fi` + +src/common/mac_macho_reader_unittest-dwarf_line_to_module.o: src/common/dwarf_line_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac_macho_reader_unittest-dwarf_line_to_module.o -MD -MP -MF src/common/$(DEPDIR)/mac_macho_reader_unittest-dwarf_line_to_module.Tpo -c -o src/common/mac_macho_reader_unittest-dwarf_line_to_module.o `test -f 'src/common/dwarf_line_to_module.cc' || echo '$(srcdir)/'`src/common/dwarf_line_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/mac_macho_reader_unittest-dwarf_line_to_module.Tpo src/common/$(DEPDIR)/mac_macho_reader_unittest-dwarf_line_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_line_to_module.cc' object='src/common/mac_macho_reader_unittest-dwarf_line_to_module.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac_macho_reader_unittest-dwarf_line_to_module.o `test -f 'src/common/dwarf_line_to_module.cc' || echo '$(srcdir)/'`src/common/dwarf_line_to_module.cc + +src/common/mac_macho_reader_unittest-dwarf_line_to_module.obj: src/common/dwarf_line_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac_macho_reader_unittest-dwarf_line_to_module.obj -MD -MP -MF src/common/$(DEPDIR)/mac_macho_reader_unittest-dwarf_line_to_module.Tpo -c -o src/common/mac_macho_reader_unittest-dwarf_line_to_module.obj `if test -f 'src/common/dwarf_line_to_module.cc'; then $(CYGPATH_W) 'src/common/dwarf_line_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_line_to_module.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/mac_macho_reader_unittest-dwarf_line_to_module.Tpo src/common/$(DEPDIR)/mac_macho_reader_unittest-dwarf_line_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_line_to_module.cc' object='src/common/mac_macho_reader_unittest-dwarf_line_to_module.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac_macho_reader_unittest-dwarf_line_to_module.obj `if test -f 'src/common/dwarf_line_to_module.cc'; then $(CYGPATH_W) 'src/common/dwarf_line_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_line_to_module.cc'; fi` + +src/common/mac_macho_reader_unittest-language.o: src/common/language.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac_macho_reader_unittest-language.o -MD -MP -MF src/common/$(DEPDIR)/mac_macho_reader_unittest-language.Tpo -c -o src/common/mac_macho_reader_unittest-language.o `test -f 'src/common/language.cc' || echo '$(srcdir)/'`src/common/language.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/mac_macho_reader_unittest-language.Tpo src/common/$(DEPDIR)/mac_macho_reader_unittest-language.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/language.cc' object='src/common/mac_macho_reader_unittest-language.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac_macho_reader_unittest-language.o `test -f 'src/common/language.cc' || echo '$(srcdir)/'`src/common/language.cc + +src/common/mac_macho_reader_unittest-language.obj: src/common/language.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac_macho_reader_unittest-language.obj -MD -MP -MF src/common/$(DEPDIR)/mac_macho_reader_unittest-language.Tpo -c -o src/common/mac_macho_reader_unittest-language.obj `if test -f 'src/common/language.cc'; then $(CYGPATH_W) 'src/common/language.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/language.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/mac_macho_reader_unittest-language.Tpo src/common/$(DEPDIR)/mac_macho_reader_unittest-language.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/language.cc' object='src/common/mac_macho_reader_unittest-language.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac_macho_reader_unittest-language.obj `if test -f 'src/common/language.cc'; then $(CYGPATH_W) 'src/common/language.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/language.cc'; fi` + +src/common/mac_macho_reader_unittest-md5.o: src/common/md5.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac_macho_reader_unittest-md5.o -MD -MP -MF src/common/$(DEPDIR)/mac_macho_reader_unittest-md5.Tpo -c -o src/common/mac_macho_reader_unittest-md5.o `test -f 'src/common/md5.cc' || echo '$(srcdir)/'`src/common/md5.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/mac_macho_reader_unittest-md5.Tpo src/common/$(DEPDIR)/mac_macho_reader_unittest-md5.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/md5.cc' object='src/common/mac_macho_reader_unittest-md5.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac_macho_reader_unittest-md5.o `test -f 'src/common/md5.cc' || echo '$(srcdir)/'`src/common/md5.cc + +src/common/mac_macho_reader_unittest-md5.obj: src/common/md5.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac_macho_reader_unittest-md5.obj -MD -MP -MF src/common/$(DEPDIR)/mac_macho_reader_unittest-md5.Tpo -c -o src/common/mac_macho_reader_unittest-md5.obj `if test -f 'src/common/md5.cc'; then $(CYGPATH_W) 'src/common/md5.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/md5.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/mac_macho_reader_unittest-md5.Tpo src/common/$(DEPDIR)/mac_macho_reader_unittest-md5.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/md5.cc' object='src/common/mac_macho_reader_unittest-md5.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac_macho_reader_unittest-md5.obj `if test -f 'src/common/md5.cc'; then $(CYGPATH_W) 'src/common/md5.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/md5.cc'; fi` + +src/common/mac_macho_reader_unittest-module.o: src/common/module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac_macho_reader_unittest-module.o -MD -MP -MF src/common/$(DEPDIR)/mac_macho_reader_unittest-module.Tpo -c -o src/common/mac_macho_reader_unittest-module.o `test -f 'src/common/module.cc' || echo '$(srcdir)/'`src/common/module.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/mac_macho_reader_unittest-module.Tpo src/common/$(DEPDIR)/mac_macho_reader_unittest-module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/module.cc' object='src/common/mac_macho_reader_unittest-module.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac_macho_reader_unittest-module.o `test -f 'src/common/module.cc' || echo '$(srcdir)/'`src/common/module.cc + +src/common/mac_macho_reader_unittest-module.obj: src/common/module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac_macho_reader_unittest-module.obj -MD -MP -MF src/common/$(DEPDIR)/mac_macho_reader_unittest-module.Tpo -c -o src/common/mac_macho_reader_unittest-module.obj `if test -f 'src/common/module.cc'; then $(CYGPATH_W) 'src/common/module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/module.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/mac_macho_reader_unittest-module.Tpo src/common/$(DEPDIR)/mac_macho_reader_unittest-module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/module.cc' object='src/common/mac_macho_reader_unittest-module.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac_macho_reader_unittest-module.obj `if test -f 'src/common/module.cc'; then $(CYGPATH_W) 'src/common/module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/module.cc'; fi` + +src/common/mac_macho_reader_unittest-path_helper.o: src/common/path_helper.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac_macho_reader_unittest-path_helper.o -MD -MP -MF src/common/$(DEPDIR)/mac_macho_reader_unittest-path_helper.Tpo -c -o src/common/mac_macho_reader_unittest-path_helper.o `test -f 'src/common/path_helper.cc' || echo '$(srcdir)/'`src/common/path_helper.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/mac_macho_reader_unittest-path_helper.Tpo src/common/$(DEPDIR)/mac_macho_reader_unittest-path_helper.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/path_helper.cc' object='src/common/mac_macho_reader_unittest-path_helper.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac_macho_reader_unittest-path_helper.o `test -f 'src/common/path_helper.cc' || echo '$(srcdir)/'`src/common/path_helper.cc + +src/common/mac_macho_reader_unittest-path_helper.obj: src/common/path_helper.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac_macho_reader_unittest-path_helper.obj -MD -MP -MF src/common/$(DEPDIR)/mac_macho_reader_unittest-path_helper.Tpo -c -o src/common/mac_macho_reader_unittest-path_helper.obj `if test -f 'src/common/path_helper.cc'; then $(CYGPATH_W) 'src/common/path_helper.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/path_helper.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/mac_macho_reader_unittest-path_helper.Tpo src/common/$(DEPDIR)/mac_macho_reader_unittest-path_helper.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/path_helper.cc' object='src/common/mac_macho_reader_unittest-path_helper.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac_macho_reader_unittest-path_helper.obj `if test -f 'src/common/path_helper.cc'; then $(CYGPATH_W) 'src/common/path_helper.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/path_helper.cc'; fi` + +src/common/mac_macho_reader_unittest-stabs_reader.o: src/common/stabs_reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac_macho_reader_unittest-stabs_reader.o -MD -MP -MF src/common/$(DEPDIR)/mac_macho_reader_unittest-stabs_reader.Tpo -c -o src/common/mac_macho_reader_unittest-stabs_reader.o `test -f 'src/common/stabs_reader.cc' || echo '$(srcdir)/'`src/common/stabs_reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/mac_macho_reader_unittest-stabs_reader.Tpo src/common/$(DEPDIR)/mac_macho_reader_unittest-stabs_reader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/stabs_reader.cc' object='src/common/mac_macho_reader_unittest-stabs_reader.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac_macho_reader_unittest-stabs_reader.o `test -f 'src/common/stabs_reader.cc' || echo '$(srcdir)/'`src/common/stabs_reader.cc + +src/common/mac_macho_reader_unittest-stabs_reader.obj: src/common/stabs_reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac_macho_reader_unittest-stabs_reader.obj -MD -MP -MF src/common/$(DEPDIR)/mac_macho_reader_unittest-stabs_reader.Tpo -c -o src/common/mac_macho_reader_unittest-stabs_reader.obj `if test -f 'src/common/stabs_reader.cc'; then $(CYGPATH_W) 'src/common/stabs_reader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/stabs_reader.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/mac_macho_reader_unittest-stabs_reader.Tpo src/common/$(DEPDIR)/mac_macho_reader_unittest-stabs_reader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/stabs_reader.cc' object='src/common/mac_macho_reader_unittest-stabs_reader.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac_macho_reader_unittest-stabs_reader.obj `if test -f 'src/common/stabs_reader.cc'; then $(CYGPATH_W) 'src/common/stabs_reader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/stabs_reader.cc'; fi` + +src/common/mac_macho_reader_unittest-stabs_to_module.o: src/common/stabs_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac_macho_reader_unittest-stabs_to_module.o -MD -MP -MF src/common/$(DEPDIR)/mac_macho_reader_unittest-stabs_to_module.Tpo -c -o src/common/mac_macho_reader_unittest-stabs_to_module.o `test -f 'src/common/stabs_to_module.cc' || echo '$(srcdir)/'`src/common/stabs_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/mac_macho_reader_unittest-stabs_to_module.Tpo src/common/$(DEPDIR)/mac_macho_reader_unittest-stabs_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/stabs_to_module.cc' object='src/common/mac_macho_reader_unittest-stabs_to_module.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac_macho_reader_unittest-stabs_to_module.o `test -f 'src/common/stabs_to_module.cc' || echo '$(srcdir)/'`src/common/stabs_to_module.cc + +src/common/mac_macho_reader_unittest-stabs_to_module.obj: src/common/stabs_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac_macho_reader_unittest-stabs_to_module.obj -MD -MP -MF src/common/$(DEPDIR)/mac_macho_reader_unittest-stabs_to_module.Tpo -c -o src/common/mac_macho_reader_unittest-stabs_to_module.obj `if test -f 'src/common/stabs_to_module.cc'; then $(CYGPATH_W) 'src/common/stabs_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/stabs_to_module.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/mac_macho_reader_unittest-stabs_to_module.Tpo src/common/$(DEPDIR)/mac_macho_reader_unittest-stabs_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/stabs_to_module.cc' object='src/common/mac_macho_reader_unittest-stabs_to_module.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac_macho_reader_unittest-stabs_to_module.obj `if test -f 'src/common/stabs_to_module.cc'; then $(CYGPATH_W) 'src/common/stabs_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/stabs_to_module.cc'; fi` + +src/common/mac_macho_reader_unittest-test_assembler.o: src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac_macho_reader_unittest-test_assembler.o -MD -MP -MF src/common/$(DEPDIR)/mac_macho_reader_unittest-test_assembler.Tpo -c -o src/common/mac_macho_reader_unittest-test_assembler.o `test -f 'src/common/test_assembler.cc' || echo '$(srcdir)/'`src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/mac_macho_reader_unittest-test_assembler.Tpo src/common/$(DEPDIR)/mac_macho_reader_unittest-test_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler.cc' object='src/common/mac_macho_reader_unittest-test_assembler.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac_macho_reader_unittest-test_assembler.o `test -f 'src/common/test_assembler.cc' || echo '$(srcdir)/'`src/common/test_assembler.cc + +src/common/mac_macho_reader_unittest-test_assembler.obj: src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac_macho_reader_unittest-test_assembler.obj -MD -MP -MF src/common/$(DEPDIR)/mac_macho_reader_unittest-test_assembler.Tpo -c -o src/common/mac_macho_reader_unittest-test_assembler.obj `if test -f 'src/common/test_assembler.cc'; then $(CYGPATH_W) 'src/common/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/mac_macho_reader_unittest-test_assembler.Tpo src/common/$(DEPDIR)/mac_macho_reader_unittest-test_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler.cc' object='src/common/mac_macho_reader_unittest-test_assembler.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac_macho_reader_unittest-test_assembler.obj `if test -f 'src/common/test_assembler.cc'; then $(CYGPATH_W) 'src/common/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler.cc'; fi` + +src/common/dwarf/mac_macho_reader_unittest-bytereader.o: src/common/dwarf/bytereader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/mac_macho_reader_unittest-bytereader.o -MD -MP -MF src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-bytereader.Tpo -c -o src/common/dwarf/mac_macho_reader_unittest-bytereader.o `test -f 'src/common/dwarf/bytereader.cc' || echo '$(srcdir)/'`src/common/dwarf/bytereader.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-bytereader.Tpo src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-bytereader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/bytereader.cc' object='src/common/dwarf/mac_macho_reader_unittest-bytereader.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/mac_macho_reader_unittest-bytereader.o `test -f 'src/common/dwarf/bytereader.cc' || echo '$(srcdir)/'`src/common/dwarf/bytereader.cc + +src/common/dwarf/mac_macho_reader_unittest-bytereader.obj: src/common/dwarf/bytereader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/mac_macho_reader_unittest-bytereader.obj -MD -MP -MF src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-bytereader.Tpo -c -o src/common/dwarf/mac_macho_reader_unittest-bytereader.obj `if test -f 'src/common/dwarf/bytereader.cc'; then $(CYGPATH_W) 'src/common/dwarf/bytereader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/bytereader.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-bytereader.Tpo src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-bytereader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/bytereader.cc' object='src/common/dwarf/mac_macho_reader_unittest-bytereader.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/mac_macho_reader_unittest-bytereader.obj `if test -f 'src/common/dwarf/bytereader.cc'; then $(CYGPATH_W) 'src/common/dwarf/bytereader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/bytereader.cc'; fi` + +src/common/dwarf/mac_macho_reader_unittest-cfi_assembler.o: src/common/dwarf/cfi_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/mac_macho_reader_unittest-cfi_assembler.o -MD -MP -MF src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-cfi_assembler.Tpo -c -o src/common/dwarf/mac_macho_reader_unittest-cfi_assembler.o `test -f 'src/common/dwarf/cfi_assembler.cc' || echo '$(srcdir)/'`src/common/dwarf/cfi_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-cfi_assembler.Tpo src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-cfi_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/cfi_assembler.cc' object='src/common/dwarf/mac_macho_reader_unittest-cfi_assembler.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/mac_macho_reader_unittest-cfi_assembler.o `test -f 'src/common/dwarf/cfi_assembler.cc' || echo '$(srcdir)/'`src/common/dwarf/cfi_assembler.cc + +src/common/dwarf/mac_macho_reader_unittest-cfi_assembler.obj: src/common/dwarf/cfi_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/mac_macho_reader_unittest-cfi_assembler.obj -MD -MP -MF src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-cfi_assembler.Tpo -c -o src/common/dwarf/mac_macho_reader_unittest-cfi_assembler.obj `if test -f 'src/common/dwarf/cfi_assembler.cc'; then $(CYGPATH_W) 'src/common/dwarf/cfi_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/cfi_assembler.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-cfi_assembler.Tpo src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-cfi_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/cfi_assembler.cc' object='src/common/dwarf/mac_macho_reader_unittest-cfi_assembler.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/mac_macho_reader_unittest-cfi_assembler.obj `if test -f 'src/common/dwarf/cfi_assembler.cc'; then $(CYGPATH_W) 'src/common/dwarf/cfi_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/cfi_assembler.cc'; fi` + +src/common/dwarf/mac_macho_reader_unittest-dwarf2diehandler.o: src/common/dwarf/dwarf2diehandler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/mac_macho_reader_unittest-dwarf2diehandler.o -MD -MP -MF src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-dwarf2diehandler.Tpo -c -o src/common/dwarf/mac_macho_reader_unittest-dwarf2diehandler.o `test -f 'src/common/dwarf/dwarf2diehandler.cc' || echo '$(srcdir)/'`src/common/dwarf/dwarf2diehandler.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-dwarf2diehandler.Tpo src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-dwarf2diehandler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/dwarf2diehandler.cc' object='src/common/dwarf/mac_macho_reader_unittest-dwarf2diehandler.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/mac_macho_reader_unittest-dwarf2diehandler.o `test -f 'src/common/dwarf/dwarf2diehandler.cc' || echo '$(srcdir)/'`src/common/dwarf/dwarf2diehandler.cc + +src/common/dwarf/mac_macho_reader_unittest-dwarf2diehandler.obj: src/common/dwarf/dwarf2diehandler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/mac_macho_reader_unittest-dwarf2diehandler.obj -MD -MP -MF src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-dwarf2diehandler.Tpo -c -o src/common/dwarf/mac_macho_reader_unittest-dwarf2diehandler.obj `if test -f 'src/common/dwarf/dwarf2diehandler.cc'; then $(CYGPATH_W) 'src/common/dwarf/dwarf2diehandler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/dwarf2diehandler.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-dwarf2diehandler.Tpo src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-dwarf2diehandler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/dwarf2diehandler.cc' object='src/common/dwarf/mac_macho_reader_unittest-dwarf2diehandler.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/mac_macho_reader_unittest-dwarf2diehandler.obj `if test -f 'src/common/dwarf/dwarf2diehandler.cc'; then $(CYGPATH_W) 'src/common/dwarf/dwarf2diehandler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/dwarf2diehandler.cc'; fi` + +src/common/dwarf/mac_macho_reader_unittest-dwarf2reader.o: src/common/dwarf/dwarf2reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/mac_macho_reader_unittest-dwarf2reader.o -MD -MP -MF src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-dwarf2reader.Tpo -c -o src/common/dwarf/mac_macho_reader_unittest-dwarf2reader.o `test -f 'src/common/dwarf/dwarf2reader.cc' || echo '$(srcdir)/'`src/common/dwarf/dwarf2reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-dwarf2reader.Tpo src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-dwarf2reader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/dwarf2reader.cc' object='src/common/dwarf/mac_macho_reader_unittest-dwarf2reader.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/mac_macho_reader_unittest-dwarf2reader.o `test -f 'src/common/dwarf/dwarf2reader.cc' || echo '$(srcdir)/'`src/common/dwarf/dwarf2reader.cc + +src/common/dwarf/mac_macho_reader_unittest-dwarf2reader.obj: src/common/dwarf/dwarf2reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/mac_macho_reader_unittest-dwarf2reader.obj -MD -MP -MF src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-dwarf2reader.Tpo -c -o src/common/dwarf/mac_macho_reader_unittest-dwarf2reader.obj `if test -f 'src/common/dwarf/dwarf2reader.cc'; then $(CYGPATH_W) 'src/common/dwarf/dwarf2reader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/dwarf2reader.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-dwarf2reader.Tpo src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-dwarf2reader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/dwarf2reader.cc' object='src/common/dwarf/mac_macho_reader_unittest-dwarf2reader.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/mac_macho_reader_unittest-dwarf2reader.obj `if test -f 'src/common/dwarf/dwarf2reader.cc'; then $(CYGPATH_W) 'src/common/dwarf/dwarf2reader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/dwarf2reader.cc'; fi` + +src/common/dwarf/mac_macho_reader_unittest-elf_reader.o: src/common/dwarf/elf_reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/mac_macho_reader_unittest-elf_reader.o -MD -MP -MF src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-elf_reader.Tpo -c -o src/common/dwarf/mac_macho_reader_unittest-elf_reader.o `test -f 'src/common/dwarf/elf_reader.cc' || echo '$(srcdir)/'`src/common/dwarf/elf_reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-elf_reader.Tpo src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-elf_reader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/elf_reader.cc' object='src/common/dwarf/mac_macho_reader_unittest-elf_reader.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/mac_macho_reader_unittest-elf_reader.o `test -f 'src/common/dwarf/elf_reader.cc' || echo '$(srcdir)/'`src/common/dwarf/elf_reader.cc + +src/common/dwarf/mac_macho_reader_unittest-elf_reader.obj: src/common/dwarf/elf_reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/mac_macho_reader_unittest-elf_reader.obj -MD -MP -MF src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-elf_reader.Tpo -c -o src/common/dwarf/mac_macho_reader_unittest-elf_reader.obj `if test -f 'src/common/dwarf/elf_reader.cc'; then $(CYGPATH_W) 'src/common/dwarf/elf_reader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/elf_reader.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-elf_reader.Tpo src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-elf_reader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/elf_reader.cc' object='src/common/dwarf/mac_macho_reader_unittest-elf_reader.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/mac_macho_reader_unittest-elf_reader.obj `if test -f 'src/common/dwarf/elf_reader.cc'; then $(CYGPATH_W) 'src/common/dwarf/elf_reader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/elf_reader.cc'; fi` + +src/common/mac/macho_reader_unittest-arch_utilities.o: src/common/mac/arch_utilities.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac/macho_reader_unittest-arch_utilities.o -MD -MP -MF src/common/mac/$(DEPDIR)/macho_reader_unittest-arch_utilities.Tpo -c -o src/common/mac/macho_reader_unittest-arch_utilities.o `test -f 'src/common/mac/arch_utilities.cc' || echo '$(srcdir)/'`src/common/mac/arch_utilities.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/mac/$(DEPDIR)/macho_reader_unittest-arch_utilities.Tpo src/common/mac/$(DEPDIR)/macho_reader_unittest-arch_utilities.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/mac/arch_utilities.cc' object='src/common/mac/macho_reader_unittest-arch_utilities.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac/macho_reader_unittest-arch_utilities.o `test -f 'src/common/mac/arch_utilities.cc' || echo '$(srcdir)/'`src/common/mac/arch_utilities.cc + +src/common/mac/macho_reader_unittest-arch_utilities.obj: src/common/mac/arch_utilities.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac/macho_reader_unittest-arch_utilities.obj -MD -MP -MF src/common/mac/$(DEPDIR)/macho_reader_unittest-arch_utilities.Tpo -c -o src/common/mac/macho_reader_unittest-arch_utilities.obj `if test -f 'src/common/mac/arch_utilities.cc'; then $(CYGPATH_W) 'src/common/mac/arch_utilities.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/mac/arch_utilities.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/mac/$(DEPDIR)/macho_reader_unittest-arch_utilities.Tpo src/common/mac/$(DEPDIR)/macho_reader_unittest-arch_utilities.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/mac/arch_utilities.cc' object='src/common/mac/macho_reader_unittest-arch_utilities.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac/macho_reader_unittest-arch_utilities.obj `if test -f 'src/common/mac/arch_utilities.cc'; then $(CYGPATH_W) 'src/common/mac/arch_utilities.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/mac/arch_utilities.cc'; fi` + +src/common/mac/macho_reader_unittest-file_id.o: src/common/mac/file_id.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac/macho_reader_unittest-file_id.o -MD -MP -MF src/common/mac/$(DEPDIR)/macho_reader_unittest-file_id.Tpo -c -o src/common/mac/macho_reader_unittest-file_id.o `test -f 'src/common/mac/file_id.cc' || echo '$(srcdir)/'`src/common/mac/file_id.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/mac/$(DEPDIR)/macho_reader_unittest-file_id.Tpo src/common/mac/$(DEPDIR)/macho_reader_unittest-file_id.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/mac/file_id.cc' object='src/common/mac/macho_reader_unittest-file_id.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac/macho_reader_unittest-file_id.o `test -f 'src/common/mac/file_id.cc' || echo '$(srcdir)/'`src/common/mac/file_id.cc + +src/common/mac/macho_reader_unittest-file_id.obj: src/common/mac/file_id.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac/macho_reader_unittest-file_id.obj -MD -MP -MF src/common/mac/$(DEPDIR)/macho_reader_unittest-file_id.Tpo -c -o src/common/mac/macho_reader_unittest-file_id.obj `if test -f 'src/common/mac/file_id.cc'; then $(CYGPATH_W) 'src/common/mac/file_id.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/mac/file_id.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/mac/$(DEPDIR)/macho_reader_unittest-file_id.Tpo src/common/mac/$(DEPDIR)/macho_reader_unittest-file_id.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/mac/file_id.cc' object='src/common/mac/macho_reader_unittest-file_id.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac/macho_reader_unittest-file_id.obj `if test -f 'src/common/mac/file_id.cc'; then $(CYGPATH_W) 'src/common/mac/file_id.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/mac/file_id.cc'; fi` + +src/common/mac/macho_reader_unittest-macho_id.o: src/common/mac/macho_id.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac/macho_reader_unittest-macho_id.o -MD -MP -MF src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_id.Tpo -c -o src/common/mac/macho_reader_unittest-macho_id.o `test -f 'src/common/mac/macho_id.cc' || echo '$(srcdir)/'`src/common/mac/macho_id.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_id.Tpo src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_id.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/mac/macho_id.cc' object='src/common/mac/macho_reader_unittest-macho_id.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac/macho_reader_unittest-macho_id.o `test -f 'src/common/mac/macho_id.cc' || echo '$(srcdir)/'`src/common/mac/macho_id.cc + +src/common/mac/macho_reader_unittest-macho_id.obj: src/common/mac/macho_id.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac/macho_reader_unittest-macho_id.obj -MD -MP -MF src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_id.Tpo -c -o src/common/mac/macho_reader_unittest-macho_id.obj `if test -f 'src/common/mac/macho_id.cc'; then $(CYGPATH_W) 'src/common/mac/macho_id.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/mac/macho_id.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_id.Tpo src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_id.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/mac/macho_id.cc' object='src/common/mac/macho_reader_unittest-macho_id.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac/macho_reader_unittest-macho_id.obj `if test -f 'src/common/mac/macho_id.cc'; then $(CYGPATH_W) 'src/common/mac/macho_id.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/mac/macho_id.cc'; fi` + +src/common/mac/macho_reader_unittest-macho_reader.o: src/common/mac/macho_reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac/macho_reader_unittest-macho_reader.o -MD -MP -MF src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_reader.Tpo -c -o src/common/mac/macho_reader_unittest-macho_reader.o `test -f 'src/common/mac/macho_reader.cc' || echo '$(srcdir)/'`src/common/mac/macho_reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_reader.Tpo src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_reader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/mac/macho_reader.cc' object='src/common/mac/macho_reader_unittest-macho_reader.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac/macho_reader_unittest-macho_reader.o `test -f 'src/common/mac/macho_reader.cc' || echo '$(srcdir)/'`src/common/mac/macho_reader.cc + +src/common/mac/macho_reader_unittest-macho_reader.obj: src/common/mac/macho_reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac/macho_reader_unittest-macho_reader.obj -MD -MP -MF src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_reader.Tpo -c -o src/common/mac/macho_reader_unittest-macho_reader.obj `if test -f 'src/common/mac/macho_reader.cc'; then $(CYGPATH_W) 'src/common/mac/macho_reader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/mac/macho_reader.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_reader.Tpo src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_reader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/mac/macho_reader.cc' object='src/common/mac/macho_reader_unittest-macho_reader.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac/macho_reader_unittest-macho_reader.obj `if test -f 'src/common/mac/macho_reader.cc'; then $(CYGPATH_W) 'src/common/mac/macho_reader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/mac/macho_reader.cc'; fi` + +src/common/mac/macho_reader_unittest-macho_reader_unittest.o: src/common/mac/macho_reader_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac/macho_reader_unittest-macho_reader_unittest.o -MD -MP -MF src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_reader_unittest.Tpo -c -o src/common/mac/macho_reader_unittest-macho_reader_unittest.o `test -f 'src/common/mac/macho_reader_unittest.cc' || echo '$(srcdir)/'`src/common/mac/macho_reader_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_reader_unittest.Tpo src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_reader_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/mac/macho_reader_unittest.cc' object='src/common/mac/macho_reader_unittest-macho_reader_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac/macho_reader_unittest-macho_reader_unittest.o `test -f 'src/common/mac/macho_reader_unittest.cc' || echo '$(srcdir)/'`src/common/mac/macho_reader_unittest.cc + +src/common/mac/macho_reader_unittest-macho_reader_unittest.obj: src/common/mac/macho_reader_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac/macho_reader_unittest-macho_reader_unittest.obj -MD -MP -MF src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_reader_unittest.Tpo -c -o src/common/mac/macho_reader_unittest-macho_reader_unittest.obj `if test -f 'src/common/mac/macho_reader_unittest.cc'; then $(CYGPATH_W) 'src/common/mac/macho_reader_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/mac/macho_reader_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_reader_unittest.Tpo src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_reader_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/mac/macho_reader_unittest.cc' object='src/common/mac/macho_reader_unittest-macho_reader_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac/macho_reader_unittest-macho_reader_unittest.obj `if test -f 'src/common/mac/macho_reader_unittest.cc'; then $(CYGPATH_W) 'src/common/mac/macho_reader_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/mac/macho_reader_unittest.cc'; fi` + +src/common/mac/macho_reader_unittest-macho_utilities.o: src/common/mac/macho_utilities.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac/macho_reader_unittest-macho_utilities.o -MD -MP -MF src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_utilities.Tpo -c -o src/common/mac/macho_reader_unittest-macho_utilities.o `test -f 'src/common/mac/macho_utilities.cc' || echo '$(srcdir)/'`src/common/mac/macho_utilities.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_utilities.Tpo src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_utilities.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/mac/macho_utilities.cc' object='src/common/mac/macho_reader_unittest-macho_utilities.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac/macho_reader_unittest-macho_utilities.o `test -f 'src/common/mac/macho_utilities.cc' || echo '$(srcdir)/'`src/common/mac/macho_utilities.cc + +src/common/mac/macho_reader_unittest-macho_utilities.obj: src/common/mac/macho_utilities.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac/macho_reader_unittest-macho_utilities.obj -MD -MP -MF src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_utilities.Tpo -c -o src/common/mac/macho_reader_unittest-macho_utilities.obj `if test -f 'src/common/mac/macho_utilities.cc'; then $(CYGPATH_W) 'src/common/mac/macho_utilities.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/mac/macho_utilities.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_utilities.Tpo src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_utilities.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/mac/macho_utilities.cc' object='src/common/mac/macho_reader_unittest-macho_utilities.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac/macho_reader_unittest-macho_utilities.obj `if test -f 'src/common/mac/macho_utilities.cc'; then $(CYGPATH_W) 'src/common/mac/macho_utilities.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/mac/macho_utilities.cc'; fi` + +src/common/mac/macho_reader_unittest-macho_walker.o: src/common/mac/macho_walker.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac/macho_reader_unittest-macho_walker.o -MD -MP -MF src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_walker.Tpo -c -o src/common/mac/macho_reader_unittest-macho_walker.o `test -f 'src/common/mac/macho_walker.cc' || echo '$(srcdir)/'`src/common/mac/macho_walker.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_walker.Tpo src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_walker.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/mac/macho_walker.cc' object='src/common/mac/macho_reader_unittest-macho_walker.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac/macho_reader_unittest-macho_walker.o `test -f 'src/common/mac/macho_walker.cc' || echo '$(srcdir)/'`src/common/mac/macho_walker.cc + +src/common/mac/macho_reader_unittest-macho_walker.obj: src/common/mac/macho_walker.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac/macho_reader_unittest-macho_walker.obj -MD -MP -MF src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_walker.Tpo -c -o src/common/mac/macho_reader_unittest-macho_walker.obj `if test -f 'src/common/mac/macho_walker.cc'; then $(CYGPATH_W) 'src/common/mac/macho_walker.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/mac/macho_walker.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_walker.Tpo src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_walker.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/mac/macho_walker.cc' object='src/common/mac/macho_reader_unittest-macho_walker.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac/macho_reader_unittest-macho_walker.obj `if test -f 'src/common/mac/macho_walker.cc'; then $(CYGPATH_W) 'src/common/mac/macho_walker.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/mac/macho_walker.cc'; fi` + +src/common/tests/mac_macho_reader_unittest-file_utils.o: src/common/tests/file_utils.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/tests/mac_macho_reader_unittest-file_utils.o -MD -MP -MF src/common/tests/$(DEPDIR)/mac_macho_reader_unittest-file_utils.Tpo -c -o src/common/tests/mac_macho_reader_unittest-file_utils.o `test -f 'src/common/tests/file_utils.cc' || echo '$(srcdir)/'`src/common/tests/file_utils.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/tests/$(DEPDIR)/mac_macho_reader_unittest-file_utils.Tpo src/common/tests/$(DEPDIR)/mac_macho_reader_unittest-file_utils.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/tests/file_utils.cc' object='src/common/tests/mac_macho_reader_unittest-file_utils.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tests/mac_macho_reader_unittest-file_utils.o `test -f 'src/common/tests/file_utils.cc' || echo '$(srcdir)/'`src/common/tests/file_utils.cc + +src/common/tests/mac_macho_reader_unittest-file_utils.obj: src/common/tests/file_utils.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/tests/mac_macho_reader_unittest-file_utils.obj -MD -MP -MF src/common/tests/$(DEPDIR)/mac_macho_reader_unittest-file_utils.Tpo -c -o src/common/tests/mac_macho_reader_unittest-file_utils.obj `if test -f 'src/common/tests/file_utils.cc'; then $(CYGPATH_W) 'src/common/tests/file_utils.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/tests/file_utils.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/tests/$(DEPDIR)/mac_macho_reader_unittest-file_utils.Tpo src/common/tests/$(DEPDIR)/mac_macho_reader_unittest-file_utils.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/tests/file_utils.cc' object='src/common/tests/mac_macho_reader_unittest-file_utils.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_mac_macho_reader_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tests/mac_macho_reader_unittest-file_utils.obj `if test -f 'src/common/tests/file_utils.cc'; then $(CYGPATH_W) 'src/common/tests/file_utils.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/tests/file_utils.cc'; fi` + +src/common/test_assembler_unittest-test_assembler.o: src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_test_assembler_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/test_assembler_unittest-test_assembler.o -MD -MP -MF src/common/$(DEPDIR)/test_assembler_unittest-test_assembler.Tpo -c -o src/common/test_assembler_unittest-test_assembler.o `test -f 'src/common/test_assembler.cc' || echo '$(srcdir)/'`src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/test_assembler_unittest-test_assembler.Tpo src/common/$(DEPDIR)/test_assembler_unittest-test_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler.cc' object='src/common/test_assembler_unittest-test_assembler.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_test_assembler_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/test_assembler_unittest-test_assembler.o `test -f 'src/common/test_assembler.cc' || echo '$(srcdir)/'`src/common/test_assembler.cc + +src/common/test_assembler_unittest-test_assembler.obj: src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_test_assembler_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/test_assembler_unittest-test_assembler.obj -MD -MP -MF src/common/$(DEPDIR)/test_assembler_unittest-test_assembler.Tpo -c -o src/common/test_assembler_unittest-test_assembler.obj `if test -f 'src/common/test_assembler.cc'; then $(CYGPATH_W) 'src/common/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/test_assembler_unittest-test_assembler.Tpo src/common/$(DEPDIR)/test_assembler_unittest-test_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler.cc' object='src/common/test_assembler_unittest-test_assembler.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_test_assembler_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/test_assembler_unittest-test_assembler.obj `if test -f 'src/common/test_assembler.cc'; then $(CYGPATH_W) 'src/common/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler.cc'; fi` + +src/common/test_assembler_unittest-test_assembler_unittest.o: src/common/test_assembler_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_test_assembler_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/test_assembler_unittest-test_assembler_unittest.o -MD -MP -MF src/common/$(DEPDIR)/test_assembler_unittest-test_assembler_unittest.Tpo -c -o src/common/test_assembler_unittest-test_assembler_unittest.o `test -f 'src/common/test_assembler_unittest.cc' || echo '$(srcdir)/'`src/common/test_assembler_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/test_assembler_unittest-test_assembler_unittest.Tpo src/common/$(DEPDIR)/test_assembler_unittest-test_assembler_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler_unittest.cc' object='src/common/test_assembler_unittest-test_assembler_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_test_assembler_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/test_assembler_unittest-test_assembler_unittest.o `test -f 'src/common/test_assembler_unittest.cc' || echo '$(srcdir)/'`src/common/test_assembler_unittest.cc + +src/common/test_assembler_unittest-test_assembler_unittest.obj: src/common/test_assembler_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_test_assembler_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/test_assembler_unittest-test_assembler_unittest.obj -MD -MP -MF src/common/$(DEPDIR)/test_assembler_unittest-test_assembler_unittest.Tpo -c -o src/common/test_assembler_unittest-test_assembler_unittest.obj `if test -f 'src/common/test_assembler_unittest.cc'; then $(CYGPATH_W) 'src/common/test_assembler_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/test_assembler_unittest-test_assembler_unittest.Tpo src/common/$(DEPDIR)/test_assembler_unittest-test_assembler_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler_unittest.cc' object='src/common/test_assembler_unittest-test_assembler_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_common_test_assembler_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/test_assembler_unittest-test_assembler_unittest.obj `if test -f 'src/common/test_assembler_unittest.cc'; then $(CYGPATH_W) 'src/common/test_assembler_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler_unittest.cc'; fi` + +src/processor/basic_source_line_resolver_unittest-basic_source_line_resolver_unittest.o: src/processor/basic_source_line_resolver_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_basic_source_line_resolver_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/basic_source_line_resolver_unittest-basic_source_line_resolver_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/basic_source_line_resolver_unittest-basic_source_line_resolver_unittest.Tpo -c -o src/processor/basic_source_line_resolver_unittest-basic_source_line_resolver_unittest.o `test -f 'src/processor/basic_source_line_resolver_unittest.cc' || echo '$(srcdir)/'`src/processor/basic_source_line_resolver_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/basic_source_line_resolver_unittest-basic_source_line_resolver_unittest.Tpo src/processor/$(DEPDIR)/basic_source_line_resolver_unittest-basic_source_line_resolver_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/basic_source_line_resolver_unittest.cc' object='src/processor/basic_source_line_resolver_unittest-basic_source_line_resolver_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_basic_source_line_resolver_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/basic_source_line_resolver_unittest-basic_source_line_resolver_unittest.o `test -f 'src/processor/basic_source_line_resolver_unittest.cc' || echo '$(srcdir)/'`src/processor/basic_source_line_resolver_unittest.cc + +src/processor/basic_source_line_resolver_unittest-basic_source_line_resolver_unittest.obj: src/processor/basic_source_line_resolver_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_basic_source_line_resolver_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/basic_source_line_resolver_unittest-basic_source_line_resolver_unittest.obj -MD -MP -MF src/processor/$(DEPDIR)/basic_source_line_resolver_unittest-basic_source_line_resolver_unittest.Tpo -c -o src/processor/basic_source_line_resolver_unittest-basic_source_line_resolver_unittest.obj `if test -f 'src/processor/basic_source_line_resolver_unittest.cc'; then $(CYGPATH_W) 'src/processor/basic_source_line_resolver_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/basic_source_line_resolver_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/basic_source_line_resolver_unittest-basic_source_line_resolver_unittest.Tpo src/processor/$(DEPDIR)/basic_source_line_resolver_unittest-basic_source_line_resolver_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/basic_source_line_resolver_unittest.cc' object='src/processor/basic_source_line_resolver_unittest-basic_source_line_resolver_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_basic_source_line_resolver_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/basic_source_line_resolver_unittest-basic_source_line_resolver_unittest.obj `if test -f 'src/processor/basic_source_line_resolver_unittest.cc'; then $(CYGPATH_W) 'src/processor/basic_source_line_resolver_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/basic_source_line_resolver_unittest.cc'; fi` + +src/processor/cfi_frame_info_unittest-cfi_frame_info_unittest.o: src/processor/cfi_frame_info_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_cfi_frame_info_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/cfi_frame_info_unittest-cfi_frame_info_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/cfi_frame_info_unittest-cfi_frame_info_unittest.Tpo -c -o src/processor/cfi_frame_info_unittest-cfi_frame_info_unittest.o `test -f 'src/processor/cfi_frame_info_unittest.cc' || echo '$(srcdir)/'`src/processor/cfi_frame_info_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/cfi_frame_info_unittest-cfi_frame_info_unittest.Tpo src/processor/$(DEPDIR)/cfi_frame_info_unittest-cfi_frame_info_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/cfi_frame_info_unittest.cc' object='src/processor/cfi_frame_info_unittest-cfi_frame_info_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_cfi_frame_info_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/cfi_frame_info_unittest-cfi_frame_info_unittest.o `test -f 'src/processor/cfi_frame_info_unittest.cc' || echo '$(srcdir)/'`src/processor/cfi_frame_info_unittest.cc + +src/processor/cfi_frame_info_unittest-cfi_frame_info_unittest.obj: src/processor/cfi_frame_info_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_cfi_frame_info_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/cfi_frame_info_unittest-cfi_frame_info_unittest.obj -MD -MP -MF src/processor/$(DEPDIR)/cfi_frame_info_unittest-cfi_frame_info_unittest.Tpo -c -o src/processor/cfi_frame_info_unittest-cfi_frame_info_unittest.obj `if test -f 'src/processor/cfi_frame_info_unittest.cc'; then $(CYGPATH_W) 'src/processor/cfi_frame_info_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/cfi_frame_info_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/cfi_frame_info_unittest-cfi_frame_info_unittest.Tpo src/processor/$(DEPDIR)/cfi_frame_info_unittest-cfi_frame_info_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/cfi_frame_info_unittest.cc' object='src/processor/cfi_frame_info_unittest-cfi_frame_info_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_cfi_frame_info_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/cfi_frame_info_unittest-cfi_frame_info_unittest.obj `if test -f 'src/processor/cfi_frame_info_unittest.cc'; then $(CYGPATH_W) 'src/processor/cfi_frame_info_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/cfi_frame_info_unittest.cc'; fi` + +src/processor/disassembler_x86_unittest-disassembler_x86_unittest.o: src/processor/disassembler_x86_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_disassembler_x86_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/disassembler_x86_unittest-disassembler_x86_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/disassembler_x86_unittest-disassembler_x86_unittest.Tpo -c -o src/processor/disassembler_x86_unittest-disassembler_x86_unittest.o `test -f 'src/processor/disassembler_x86_unittest.cc' || echo '$(srcdir)/'`src/processor/disassembler_x86_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/disassembler_x86_unittest-disassembler_x86_unittest.Tpo src/processor/$(DEPDIR)/disassembler_x86_unittest-disassembler_x86_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/disassembler_x86_unittest.cc' object='src/processor/disassembler_x86_unittest-disassembler_x86_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_disassembler_x86_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/disassembler_x86_unittest-disassembler_x86_unittest.o `test -f 'src/processor/disassembler_x86_unittest.cc' || echo '$(srcdir)/'`src/processor/disassembler_x86_unittest.cc + +src/processor/disassembler_x86_unittest-disassembler_x86_unittest.obj: src/processor/disassembler_x86_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_disassembler_x86_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/disassembler_x86_unittest-disassembler_x86_unittest.obj -MD -MP -MF src/processor/$(DEPDIR)/disassembler_x86_unittest-disassembler_x86_unittest.Tpo -c -o src/processor/disassembler_x86_unittest-disassembler_x86_unittest.obj `if test -f 'src/processor/disassembler_x86_unittest.cc'; then $(CYGPATH_W) 'src/processor/disassembler_x86_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/disassembler_x86_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/disassembler_x86_unittest-disassembler_x86_unittest.Tpo src/processor/$(DEPDIR)/disassembler_x86_unittest-disassembler_x86_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/disassembler_x86_unittest.cc' object='src/processor/disassembler_x86_unittest-disassembler_x86_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_disassembler_x86_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/disassembler_x86_unittest-disassembler_x86_unittest.obj `if test -f 'src/processor/disassembler_x86_unittest.cc'; then $(CYGPATH_W) 'src/processor/disassembler_x86_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/disassembler_x86_unittest.cc'; fi` + +src/processor/exploitability_unittest-exploitability_unittest.o: src/processor/exploitability_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_exploitability_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/exploitability_unittest-exploitability_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/exploitability_unittest-exploitability_unittest.Tpo -c -o src/processor/exploitability_unittest-exploitability_unittest.o `test -f 'src/processor/exploitability_unittest.cc' || echo '$(srcdir)/'`src/processor/exploitability_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/exploitability_unittest-exploitability_unittest.Tpo src/processor/$(DEPDIR)/exploitability_unittest-exploitability_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/exploitability_unittest.cc' object='src/processor/exploitability_unittest-exploitability_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_exploitability_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/exploitability_unittest-exploitability_unittest.o `test -f 'src/processor/exploitability_unittest.cc' || echo '$(srcdir)/'`src/processor/exploitability_unittest.cc + +src/processor/exploitability_unittest-exploitability_unittest.obj: src/processor/exploitability_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_exploitability_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/exploitability_unittest-exploitability_unittest.obj -MD -MP -MF src/processor/$(DEPDIR)/exploitability_unittest-exploitability_unittest.Tpo -c -o src/processor/exploitability_unittest-exploitability_unittest.obj `if test -f 'src/processor/exploitability_unittest.cc'; then $(CYGPATH_W) 'src/processor/exploitability_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/exploitability_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/exploitability_unittest-exploitability_unittest.Tpo src/processor/$(DEPDIR)/exploitability_unittest-exploitability_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/exploitability_unittest.cc' object='src/processor/exploitability_unittest-exploitability_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_exploitability_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/exploitability_unittest-exploitability_unittest.obj `if test -f 'src/processor/exploitability_unittest.cc'; then $(CYGPATH_W) 'src/processor/exploitability_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/exploitability_unittest.cc'; fi` + +src/processor/fast_source_line_resolver_unittest-fast_source_line_resolver_unittest.o: src/processor/fast_source_line_resolver_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_fast_source_line_resolver_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/fast_source_line_resolver_unittest-fast_source_line_resolver_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/fast_source_line_resolver_unittest-fast_source_line_resolver_unittest.Tpo -c -o src/processor/fast_source_line_resolver_unittest-fast_source_line_resolver_unittest.o `test -f 'src/processor/fast_source_line_resolver_unittest.cc' || echo '$(srcdir)/'`src/processor/fast_source_line_resolver_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/fast_source_line_resolver_unittest-fast_source_line_resolver_unittest.Tpo src/processor/$(DEPDIR)/fast_source_line_resolver_unittest-fast_source_line_resolver_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/fast_source_line_resolver_unittest.cc' object='src/processor/fast_source_line_resolver_unittest-fast_source_line_resolver_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_fast_source_line_resolver_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/fast_source_line_resolver_unittest-fast_source_line_resolver_unittest.o `test -f 'src/processor/fast_source_line_resolver_unittest.cc' || echo '$(srcdir)/'`src/processor/fast_source_line_resolver_unittest.cc + +src/processor/fast_source_line_resolver_unittest-fast_source_line_resolver_unittest.obj: src/processor/fast_source_line_resolver_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_fast_source_line_resolver_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/fast_source_line_resolver_unittest-fast_source_line_resolver_unittest.obj -MD -MP -MF src/processor/$(DEPDIR)/fast_source_line_resolver_unittest-fast_source_line_resolver_unittest.Tpo -c -o src/processor/fast_source_line_resolver_unittest-fast_source_line_resolver_unittest.obj `if test -f 'src/processor/fast_source_line_resolver_unittest.cc'; then $(CYGPATH_W) 'src/processor/fast_source_line_resolver_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/fast_source_line_resolver_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/fast_source_line_resolver_unittest-fast_source_line_resolver_unittest.Tpo src/processor/$(DEPDIR)/fast_source_line_resolver_unittest-fast_source_line_resolver_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/fast_source_line_resolver_unittest.cc' object='src/processor/fast_source_line_resolver_unittest-fast_source_line_resolver_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_fast_source_line_resolver_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/fast_source_line_resolver_unittest-fast_source_line_resolver_unittest.obj `if test -f 'src/processor/fast_source_line_resolver_unittest.cc'; then $(CYGPATH_W) 'src/processor/fast_source_line_resolver_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/fast_source_line_resolver_unittest.cc'; fi` + +src/processor/map_serializers_unittest-map_serializers_unittest.o: src/processor/map_serializers_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_map_serializers_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/map_serializers_unittest-map_serializers_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/map_serializers_unittest-map_serializers_unittest.Tpo -c -o src/processor/map_serializers_unittest-map_serializers_unittest.o `test -f 'src/processor/map_serializers_unittest.cc' || echo '$(srcdir)/'`src/processor/map_serializers_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/map_serializers_unittest-map_serializers_unittest.Tpo src/processor/$(DEPDIR)/map_serializers_unittest-map_serializers_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/map_serializers_unittest.cc' object='src/processor/map_serializers_unittest-map_serializers_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_map_serializers_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/map_serializers_unittest-map_serializers_unittest.o `test -f 'src/processor/map_serializers_unittest.cc' || echo '$(srcdir)/'`src/processor/map_serializers_unittest.cc + +src/processor/map_serializers_unittest-map_serializers_unittest.obj: src/processor/map_serializers_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_map_serializers_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/map_serializers_unittest-map_serializers_unittest.obj -MD -MP -MF src/processor/$(DEPDIR)/map_serializers_unittest-map_serializers_unittest.Tpo -c -o src/processor/map_serializers_unittest-map_serializers_unittest.obj `if test -f 'src/processor/map_serializers_unittest.cc'; then $(CYGPATH_W) 'src/processor/map_serializers_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/map_serializers_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/map_serializers_unittest-map_serializers_unittest.Tpo src/processor/$(DEPDIR)/map_serializers_unittest-map_serializers_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/map_serializers_unittest.cc' object='src/processor/map_serializers_unittest-map_serializers_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_map_serializers_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/map_serializers_unittest-map_serializers_unittest.obj `if test -f 'src/processor/map_serializers_unittest.cc'; then $(CYGPATH_W) 'src/processor/map_serializers_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/map_serializers_unittest.cc'; fi` + +src/processor/microdump_processor_unittest-microdump_processor_unittest.o: src/processor/microdump_processor_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_microdump_processor_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/microdump_processor_unittest-microdump_processor_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/microdump_processor_unittest-microdump_processor_unittest.Tpo -c -o src/processor/microdump_processor_unittest-microdump_processor_unittest.o `test -f 'src/processor/microdump_processor_unittest.cc' || echo '$(srcdir)/'`src/processor/microdump_processor_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/microdump_processor_unittest-microdump_processor_unittest.Tpo src/processor/$(DEPDIR)/microdump_processor_unittest-microdump_processor_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/microdump_processor_unittest.cc' object='src/processor/microdump_processor_unittest-microdump_processor_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_microdump_processor_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/microdump_processor_unittest-microdump_processor_unittest.o `test -f 'src/processor/microdump_processor_unittest.cc' || echo '$(srcdir)/'`src/processor/microdump_processor_unittest.cc + +src/processor/microdump_processor_unittest-microdump_processor_unittest.obj: src/processor/microdump_processor_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_microdump_processor_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/microdump_processor_unittest-microdump_processor_unittest.obj -MD -MP -MF src/processor/$(DEPDIR)/microdump_processor_unittest-microdump_processor_unittest.Tpo -c -o src/processor/microdump_processor_unittest-microdump_processor_unittest.obj `if test -f 'src/processor/microdump_processor_unittest.cc'; then $(CYGPATH_W) 'src/processor/microdump_processor_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/microdump_processor_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/microdump_processor_unittest-microdump_processor_unittest.Tpo src/processor/$(DEPDIR)/microdump_processor_unittest-microdump_processor_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/microdump_processor_unittest.cc' object='src/processor/microdump_processor_unittest-microdump_processor_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_microdump_processor_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/microdump_processor_unittest-microdump_processor_unittest.obj `if test -f 'src/processor/microdump_processor_unittest.cc'; then $(CYGPATH_W) 'src/processor/microdump_processor_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/microdump_processor_unittest.cc'; fi` + +src/processor/minidump_processor_unittest-minidump_processor_unittest.o: src/processor/minidump_processor_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_minidump_processor_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/minidump_processor_unittest-minidump_processor_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/minidump_processor_unittest-minidump_processor_unittest.Tpo -c -o src/processor/minidump_processor_unittest-minidump_processor_unittest.o `test -f 'src/processor/minidump_processor_unittest.cc' || echo '$(srcdir)/'`src/processor/minidump_processor_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/minidump_processor_unittest-minidump_processor_unittest.Tpo src/processor/$(DEPDIR)/minidump_processor_unittest-minidump_processor_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/minidump_processor_unittest.cc' object='src/processor/minidump_processor_unittest-minidump_processor_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_minidump_processor_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/minidump_processor_unittest-minidump_processor_unittest.o `test -f 'src/processor/minidump_processor_unittest.cc' || echo '$(srcdir)/'`src/processor/minidump_processor_unittest.cc + +src/processor/minidump_processor_unittest-minidump_processor_unittest.obj: src/processor/minidump_processor_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_minidump_processor_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/minidump_processor_unittest-minidump_processor_unittest.obj -MD -MP -MF src/processor/$(DEPDIR)/minidump_processor_unittest-minidump_processor_unittest.Tpo -c -o src/processor/minidump_processor_unittest-minidump_processor_unittest.obj `if test -f 'src/processor/minidump_processor_unittest.cc'; then $(CYGPATH_W) 'src/processor/minidump_processor_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/minidump_processor_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/minidump_processor_unittest-minidump_processor_unittest.Tpo src/processor/$(DEPDIR)/minidump_processor_unittest-minidump_processor_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/minidump_processor_unittest.cc' object='src/processor/minidump_processor_unittest-minidump_processor_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_minidump_processor_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/minidump_processor_unittest-minidump_processor_unittest.obj `if test -f 'src/processor/minidump_processor_unittest.cc'; then $(CYGPATH_W) 'src/processor/minidump_processor_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/minidump_processor_unittest.cc'; fi` + +src/common/processor_minidump_unittest-test_assembler.o: src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_minidump_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/processor_minidump_unittest-test_assembler.o -MD -MP -MF src/common/$(DEPDIR)/processor_minidump_unittest-test_assembler.Tpo -c -o src/common/processor_minidump_unittest-test_assembler.o `test -f 'src/common/test_assembler.cc' || echo '$(srcdir)/'`src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/processor_minidump_unittest-test_assembler.Tpo src/common/$(DEPDIR)/processor_minidump_unittest-test_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler.cc' object='src/common/processor_minidump_unittest-test_assembler.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_minidump_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/processor_minidump_unittest-test_assembler.o `test -f 'src/common/test_assembler.cc' || echo '$(srcdir)/'`src/common/test_assembler.cc + +src/common/processor_minidump_unittest-test_assembler.obj: src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_minidump_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/processor_minidump_unittest-test_assembler.obj -MD -MP -MF src/common/$(DEPDIR)/processor_minidump_unittest-test_assembler.Tpo -c -o src/common/processor_minidump_unittest-test_assembler.obj `if test -f 'src/common/test_assembler.cc'; then $(CYGPATH_W) 'src/common/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/processor_minidump_unittest-test_assembler.Tpo src/common/$(DEPDIR)/processor_minidump_unittest-test_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler.cc' object='src/common/processor_minidump_unittest-test_assembler.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_minidump_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/processor_minidump_unittest-test_assembler.obj `if test -f 'src/common/test_assembler.cc'; then $(CYGPATH_W) 'src/common/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler.cc'; fi` + +src/processor/minidump_unittest-minidump_unittest.o: src/processor/minidump_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_minidump_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/minidump_unittest-minidump_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/minidump_unittest-minidump_unittest.Tpo -c -o src/processor/minidump_unittest-minidump_unittest.o `test -f 'src/processor/minidump_unittest.cc' || echo '$(srcdir)/'`src/processor/minidump_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/minidump_unittest-minidump_unittest.Tpo src/processor/$(DEPDIR)/minidump_unittest-minidump_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/minidump_unittest.cc' object='src/processor/minidump_unittest-minidump_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_minidump_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/minidump_unittest-minidump_unittest.o `test -f 'src/processor/minidump_unittest.cc' || echo '$(srcdir)/'`src/processor/minidump_unittest.cc + +src/processor/minidump_unittest-minidump_unittest.obj: src/processor/minidump_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_minidump_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/minidump_unittest-minidump_unittest.obj -MD -MP -MF src/processor/$(DEPDIR)/minidump_unittest-minidump_unittest.Tpo -c -o src/processor/minidump_unittest-minidump_unittest.obj `if test -f 'src/processor/minidump_unittest.cc'; then $(CYGPATH_W) 'src/processor/minidump_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/minidump_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/minidump_unittest-minidump_unittest.Tpo src/processor/$(DEPDIR)/minidump_unittest-minidump_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/minidump_unittest.cc' object='src/processor/minidump_unittest-minidump_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_minidump_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/minidump_unittest-minidump_unittest.obj `if test -f 'src/processor/minidump_unittest.cc'; then $(CYGPATH_W) 'src/processor/minidump_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/minidump_unittest.cc'; fi` + +src/processor/minidump_unittest-synth_minidump.o: src/processor/synth_minidump.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_minidump_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/minidump_unittest-synth_minidump.o -MD -MP -MF src/processor/$(DEPDIR)/minidump_unittest-synth_minidump.Tpo -c -o src/processor/minidump_unittest-synth_minidump.o `test -f 'src/processor/synth_minidump.cc' || echo '$(srcdir)/'`src/processor/synth_minidump.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/minidump_unittest-synth_minidump.Tpo src/processor/$(DEPDIR)/minidump_unittest-synth_minidump.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/synth_minidump.cc' object='src/processor/minidump_unittest-synth_minidump.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_minidump_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/minidump_unittest-synth_minidump.o `test -f 'src/processor/synth_minidump.cc' || echo '$(srcdir)/'`src/processor/synth_minidump.cc + +src/processor/minidump_unittest-synth_minidump.obj: src/processor/synth_minidump.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_minidump_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/minidump_unittest-synth_minidump.obj -MD -MP -MF src/processor/$(DEPDIR)/minidump_unittest-synth_minidump.Tpo -c -o src/processor/minidump_unittest-synth_minidump.obj `if test -f 'src/processor/synth_minidump.cc'; then $(CYGPATH_W) 'src/processor/synth_minidump.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/synth_minidump.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/minidump_unittest-synth_minidump.Tpo src/processor/$(DEPDIR)/minidump_unittest-synth_minidump.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/synth_minidump.cc' object='src/processor/minidump_unittest-synth_minidump.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_minidump_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/minidump_unittest-synth_minidump.obj `if test -f 'src/processor/synth_minidump.cc'; then $(CYGPATH_W) 'src/processor/synth_minidump.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/synth_minidump.cc'; fi` + +src/processor/proc_maps_linux_unittest-proc_maps_linux.o: src/processor/proc_maps_linux.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_proc_maps_linux_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/proc_maps_linux_unittest-proc_maps_linux.o -MD -MP -MF src/processor/$(DEPDIR)/proc_maps_linux_unittest-proc_maps_linux.Tpo -c -o src/processor/proc_maps_linux_unittest-proc_maps_linux.o `test -f 'src/processor/proc_maps_linux.cc' || echo '$(srcdir)/'`src/processor/proc_maps_linux.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/proc_maps_linux_unittest-proc_maps_linux.Tpo src/processor/$(DEPDIR)/proc_maps_linux_unittest-proc_maps_linux.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/proc_maps_linux.cc' object='src/processor/proc_maps_linux_unittest-proc_maps_linux.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_proc_maps_linux_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/proc_maps_linux_unittest-proc_maps_linux.o `test -f 'src/processor/proc_maps_linux.cc' || echo '$(srcdir)/'`src/processor/proc_maps_linux.cc + +src/processor/proc_maps_linux_unittest-proc_maps_linux.obj: src/processor/proc_maps_linux.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_proc_maps_linux_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/proc_maps_linux_unittest-proc_maps_linux.obj -MD -MP -MF src/processor/$(DEPDIR)/proc_maps_linux_unittest-proc_maps_linux.Tpo -c -o src/processor/proc_maps_linux_unittest-proc_maps_linux.obj `if test -f 'src/processor/proc_maps_linux.cc'; then $(CYGPATH_W) 'src/processor/proc_maps_linux.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/proc_maps_linux.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/proc_maps_linux_unittest-proc_maps_linux.Tpo src/processor/$(DEPDIR)/proc_maps_linux_unittest-proc_maps_linux.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/proc_maps_linux.cc' object='src/processor/proc_maps_linux_unittest-proc_maps_linux.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_proc_maps_linux_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/proc_maps_linux_unittest-proc_maps_linux.obj `if test -f 'src/processor/proc_maps_linux.cc'; then $(CYGPATH_W) 'src/processor/proc_maps_linux.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/proc_maps_linux.cc'; fi` + +src/processor/proc_maps_linux_unittest-proc_maps_linux_unittest.o: src/processor/proc_maps_linux_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_proc_maps_linux_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/proc_maps_linux_unittest-proc_maps_linux_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/proc_maps_linux_unittest-proc_maps_linux_unittest.Tpo -c -o src/processor/proc_maps_linux_unittest-proc_maps_linux_unittest.o `test -f 'src/processor/proc_maps_linux_unittest.cc' || echo '$(srcdir)/'`src/processor/proc_maps_linux_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/proc_maps_linux_unittest-proc_maps_linux_unittest.Tpo src/processor/$(DEPDIR)/proc_maps_linux_unittest-proc_maps_linux_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/proc_maps_linux_unittest.cc' object='src/processor/proc_maps_linux_unittest-proc_maps_linux_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_proc_maps_linux_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/proc_maps_linux_unittest-proc_maps_linux_unittest.o `test -f 'src/processor/proc_maps_linux_unittest.cc' || echo '$(srcdir)/'`src/processor/proc_maps_linux_unittest.cc + +src/processor/proc_maps_linux_unittest-proc_maps_linux_unittest.obj: src/processor/proc_maps_linux_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_proc_maps_linux_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/proc_maps_linux_unittest-proc_maps_linux_unittest.obj -MD -MP -MF src/processor/$(DEPDIR)/proc_maps_linux_unittest-proc_maps_linux_unittest.Tpo -c -o src/processor/proc_maps_linux_unittest-proc_maps_linux_unittest.obj `if test -f 'src/processor/proc_maps_linux_unittest.cc'; then $(CYGPATH_W) 'src/processor/proc_maps_linux_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/proc_maps_linux_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/proc_maps_linux_unittest-proc_maps_linux_unittest.Tpo src/processor/$(DEPDIR)/proc_maps_linux_unittest-proc_maps_linux_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/proc_maps_linux_unittest.cc' object='src/processor/proc_maps_linux_unittest-proc_maps_linux_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_proc_maps_linux_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/proc_maps_linux_unittest-proc_maps_linux_unittest.obj `if test -f 'src/processor/proc_maps_linux_unittest.cc'; then $(CYGPATH_W) 'src/processor/proc_maps_linux_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/proc_maps_linux_unittest.cc'; fi` + +src/processor/range_map_truncate_lower_unittest-range_map_truncate_lower_unittest.o: src/processor/range_map_truncate_lower_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_range_map_truncate_lower_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/range_map_truncate_lower_unittest-range_map_truncate_lower_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/range_map_truncate_lower_unittest-range_map_truncate_lower_unittest.Tpo -c -o src/processor/range_map_truncate_lower_unittest-range_map_truncate_lower_unittest.o `test -f 'src/processor/range_map_truncate_lower_unittest.cc' || echo '$(srcdir)/'`src/processor/range_map_truncate_lower_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/range_map_truncate_lower_unittest-range_map_truncate_lower_unittest.Tpo src/processor/$(DEPDIR)/range_map_truncate_lower_unittest-range_map_truncate_lower_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/range_map_truncate_lower_unittest.cc' object='src/processor/range_map_truncate_lower_unittest-range_map_truncate_lower_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_range_map_truncate_lower_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/range_map_truncate_lower_unittest-range_map_truncate_lower_unittest.o `test -f 'src/processor/range_map_truncate_lower_unittest.cc' || echo '$(srcdir)/'`src/processor/range_map_truncate_lower_unittest.cc + +src/processor/range_map_truncate_lower_unittest-range_map_truncate_lower_unittest.obj: src/processor/range_map_truncate_lower_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_range_map_truncate_lower_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/range_map_truncate_lower_unittest-range_map_truncate_lower_unittest.obj -MD -MP -MF src/processor/$(DEPDIR)/range_map_truncate_lower_unittest-range_map_truncate_lower_unittest.Tpo -c -o src/processor/range_map_truncate_lower_unittest-range_map_truncate_lower_unittest.obj `if test -f 'src/processor/range_map_truncate_lower_unittest.cc'; then $(CYGPATH_W) 'src/processor/range_map_truncate_lower_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/range_map_truncate_lower_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/range_map_truncate_lower_unittest-range_map_truncate_lower_unittest.Tpo src/processor/$(DEPDIR)/range_map_truncate_lower_unittest-range_map_truncate_lower_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/range_map_truncate_lower_unittest.cc' object='src/processor/range_map_truncate_lower_unittest-range_map_truncate_lower_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_range_map_truncate_lower_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/range_map_truncate_lower_unittest-range_map_truncate_lower_unittest.obj `if test -f 'src/processor/range_map_truncate_lower_unittest.cc'; then $(CYGPATH_W) 'src/processor/range_map_truncate_lower_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/range_map_truncate_lower_unittest.cc'; fi` + +src/processor/range_map_truncate_upper_unittest-range_map_truncate_upper_unittest.o: src/processor/range_map_truncate_upper_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_range_map_truncate_upper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/range_map_truncate_upper_unittest-range_map_truncate_upper_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/range_map_truncate_upper_unittest-range_map_truncate_upper_unittest.Tpo -c -o src/processor/range_map_truncate_upper_unittest-range_map_truncate_upper_unittest.o `test -f 'src/processor/range_map_truncate_upper_unittest.cc' || echo '$(srcdir)/'`src/processor/range_map_truncate_upper_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/range_map_truncate_upper_unittest-range_map_truncate_upper_unittest.Tpo src/processor/$(DEPDIR)/range_map_truncate_upper_unittest-range_map_truncate_upper_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/range_map_truncate_upper_unittest.cc' object='src/processor/range_map_truncate_upper_unittest-range_map_truncate_upper_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_range_map_truncate_upper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/range_map_truncate_upper_unittest-range_map_truncate_upper_unittest.o `test -f 'src/processor/range_map_truncate_upper_unittest.cc' || echo '$(srcdir)/'`src/processor/range_map_truncate_upper_unittest.cc + +src/processor/range_map_truncate_upper_unittest-range_map_truncate_upper_unittest.obj: src/processor/range_map_truncate_upper_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_range_map_truncate_upper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/range_map_truncate_upper_unittest-range_map_truncate_upper_unittest.obj -MD -MP -MF src/processor/$(DEPDIR)/range_map_truncate_upper_unittest-range_map_truncate_upper_unittest.Tpo -c -o src/processor/range_map_truncate_upper_unittest-range_map_truncate_upper_unittest.obj `if test -f 'src/processor/range_map_truncate_upper_unittest.cc'; then $(CYGPATH_W) 'src/processor/range_map_truncate_upper_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/range_map_truncate_upper_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/range_map_truncate_upper_unittest-range_map_truncate_upper_unittest.Tpo src/processor/$(DEPDIR)/range_map_truncate_upper_unittest-range_map_truncate_upper_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/range_map_truncate_upper_unittest.cc' object='src/processor/range_map_truncate_upper_unittest-range_map_truncate_upper_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_range_map_truncate_upper_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/range_map_truncate_upper_unittest-range_map_truncate_upper_unittest.obj `if test -f 'src/processor/range_map_truncate_upper_unittest.cc'; then $(CYGPATH_W) 'src/processor/range_map_truncate_upper_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/range_map_truncate_upper_unittest.cc'; fi` + +src/common/processor_stackwalker_address_list_unittest-test_assembler.o: src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_address_list_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/processor_stackwalker_address_list_unittest-test_assembler.o -MD -MP -MF src/common/$(DEPDIR)/processor_stackwalker_address_list_unittest-test_assembler.Tpo -c -o src/common/processor_stackwalker_address_list_unittest-test_assembler.o `test -f 'src/common/test_assembler.cc' || echo '$(srcdir)/'`src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/processor_stackwalker_address_list_unittest-test_assembler.Tpo src/common/$(DEPDIR)/processor_stackwalker_address_list_unittest-test_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler.cc' object='src/common/processor_stackwalker_address_list_unittest-test_assembler.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_address_list_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/processor_stackwalker_address_list_unittest-test_assembler.o `test -f 'src/common/test_assembler.cc' || echo '$(srcdir)/'`src/common/test_assembler.cc + +src/common/processor_stackwalker_address_list_unittest-test_assembler.obj: src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_address_list_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/processor_stackwalker_address_list_unittest-test_assembler.obj -MD -MP -MF src/common/$(DEPDIR)/processor_stackwalker_address_list_unittest-test_assembler.Tpo -c -o src/common/processor_stackwalker_address_list_unittest-test_assembler.obj `if test -f 'src/common/test_assembler.cc'; then $(CYGPATH_W) 'src/common/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/processor_stackwalker_address_list_unittest-test_assembler.Tpo src/common/$(DEPDIR)/processor_stackwalker_address_list_unittest-test_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler.cc' object='src/common/processor_stackwalker_address_list_unittest-test_assembler.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_address_list_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/processor_stackwalker_address_list_unittest-test_assembler.obj `if test -f 'src/common/test_assembler.cc'; then $(CYGPATH_W) 'src/common/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler.cc'; fi` + +src/processor/stackwalker_address_list_unittest-stackwalker_address_list_unittest.o: src/processor/stackwalker_address_list_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_address_list_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/stackwalker_address_list_unittest-stackwalker_address_list_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/stackwalker_address_list_unittest-stackwalker_address_list_unittest.Tpo -c -o src/processor/stackwalker_address_list_unittest-stackwalker_address_list_unittest.o `test -f 'src/processor/stackwalker_address_list_unittest.cc' || echo '$(srcdir)/'`src/processor/stackwalker_address_list_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/stackwalker_address_list_unittest-stackwalker_address_list_unittest.Tpo src/processor/$(DEPDIR)/stackwalker_address_list_unittest-stackwalker_address_list_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/stackwalker_address_list_unittest.cc' object='src/processor/stackwalker_address_list_unittest-stackwalker_address_list_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_address_list_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/stackwalker_address_list_unittest-stackwalker_address_list_unittest.o `test -f 'src/processor/stackwalker_address_list_unittest.cc' || echo '$(srcdir)/'`src/processor/stackwalker_address_list_unittest.cc + +src/processor/stackwalker_address_list_unittest-stackwalker_address_list_unittest.obj: src/processor/stackwalker_address_list_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_address_list_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/stackwalker_address_list_unittest-stackwalker_address_list_unittest.obj -MD -MP -MF src/processor/$(DEPDIR)/stackwalker_address_list_unittest-stackwalker_address_list_unittest.Tpo -c -o src/processor/stackwalker_address_list_unittest-stackwalker_address_list_unittest.obj `if test -f 'src/processor/stackwalker_address_list_unittest.cc'; then $(CYGPATH_W) 'src/processor/stackwalker_address_list_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/stackwalker_address_list_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/stackwalker_address_list_unittest-stackwalker_address_list_unittest.Tpo src/processor/$(DEPDIR)/stackwalker_address_list_unittest-stackwalker_address_list_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/stackwalker_address_list_unittest.cc' object='src/processor/stackwalker_address_list_unittest-stackwalker_address_list_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_address_list_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/stackwalker_address_list_unittest-stackwalker_address_list_unittest.obj `if test -f 'src/processor/stackwalker_address_list_unittest.cc'; then $(CYGPATH_W) 'src/processor/stackwalker_address_list_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/stackwalker_address_list_unittest.cc'; fi` + +src/common/processor_stackwalker_amd64_unittest-test_assembler.o: src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_amd64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/processor_stackwalker_amd64_unittest-test_assembler.o -MD -MP -MF src/common/$(DEPDIR)/processor_stackwalker_amd64_unittest-test_assembler.Tpo -c -o src/common/processor_stackwalker_amd64_unittest-test_assembler.o `test -f 'src/common/test_assembler.cc' || echo '$(srcdir)/'`src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/processor_stackwalker_amd64_unittest-test_assembler.Tpo src/common/$(DEPDIR)/processor_stackwalker_amd64_unittest-test_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler.cc' object='src/common/processor_stackwalker_amd64_unittest-test_assembler.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_amd64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/processor_stackwalker_amd64_unittest-test_assembler.o `test -f 'src/common/test_assembler.cc' || echo '$(srcdir)/'`src/common/test_assembler.cc + +src/common/processor_stackwalker_amd64_unittest-test_assembler.obj: src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_amd64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/processor_stackwalker_amd64_unittest-test_assembler.obj -MD -MP -MF src/common/$(DEPDIR)/processor_stackwalker_amd64_unittest-test_assembler.Tpo -c -o src/common/processor_stackwalker_amd64_unittest-test_assembler.obj `if test -f 'src/common/test_assembler.cc'; then $(CYGPATH_W) 'src/common/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/processor_stackwalker_amd64_unittest-test_assembler.Tpo src/common/$(DEPDIR)/processor_stackwalker_amd64_unittest-test_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler.cc' object='src/common/processor_stackwalker_amd64_unittest-test_assembler.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_amd64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/processor_stackwalker_amd64_unittest-test_assembler.obj `if test -f 'src/common/test_assembler.cc'; then $(CYGPATH_W) 'src/common/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler.cc'; fi` + +src/processor/stackwalker_amd64_unittest-stackwalker_amd64_unittest.o: src/processor/stackwalker_amd64_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_amd64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/stackwalker_amd64_unittest-stackwalker_amd64_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/stackwalker_amd64_unittest-stackwalker_amd64_unittest.Tpo -c -o src/processor/stackwalker_amd64_unittest-stackwalker_amd64_unittest.o `test -f 'src/processor/stackwalker_amd64_unittest.cc' || echo '$(srcdir)/'`src/processor/stackwalker_amd64_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/stackwalker_amd64_unittest-stackwalker_amd64_unittest.Tpo src/processor/$(DEPDIR)/stackwalker_amd64_unittest-stackwalker_amd64_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/stackwalker_amd64_unittest.cc' object='src/processor/stackwalker_amd64_unittest-stackwalker_amd64_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_amd64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/stackwalker_amd64_unittest-stackwalker_amd64_unittest.o `test -f 'src/processor/stackwalker_amd64_unittest.cc' || echo '$(srcdir)/'`src/processor/stackwalker_amd64_unittest.cc + +src/processor/stackwalker_amd64_unittest-stackwalker_amd64_unittest.obj: src/processor/stackwalker_amd64_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_amd64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/stackwalker_amd64_unittest-stackwalker_amd64_unittest.obj -MD -MP -MF src/processor/$(DEPDIR)/stackwalker_amd64_unittest-stackwalker_amd64_unittest.Tpo -c -o src/processor/stackwalker_amd64_unittest-stackwalker_amd64_unittest.obj `if test -f 'src/processor/stackwalker_amd64_unittest.cc'; then $(CYGPATH_W) 'src/processor/stackwalker_amd64_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/stackwalker_amd64_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/stackwalker_amd64_unittest-stackwalker_amd64_unittest.Tpo src/processor/$(DEPDIR)/stackwalker_amd64_unittest-stackwalker_amd64_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/stackwalker_amd64_unittest.cc' object='src/processor/stackwalker_amd64_unittest-stackwalker_amd64_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_amd64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/stackwalker_amd64_unittest-stackwalker_amd64_unittest.obj `if test -f 'src/processor/stackwalker_amd64_unittest.cc'; then $(CYGPATH_W) 'src/processor/stackwalker_amd64_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/stackwalker_amd64_unittest.cc'; fi` + +src/common/processor_stackwalker_arm64_unittest-test_assembler.o: src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/processor_stackwalker_arm64_unittest-test_assembler.o -MD -MP -MF src/common/$(DEPDIR)/processor_stackwalker_arm64_unittest-test_assembler.Tpo -c -o src/common/processor_stackwalker_arm64_unittest-test_assembler.o `test -f 'src/common/test_assembler.cc' || echo '$(srcdir)/'`src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/processor_stackwalker_arm64_unittest-test_assembler.Tpo src/common/$(DEPDIR)/processor_stackwalker_arm64_unittest-test_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler.cc' object='src/common/processor_stackwalker_arm64_unittest-test_assembler.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/processor_stackwalker_arm64_unittest-test_assembler.o `test -f 'src/common/test_assembler.cc' || echo '$(srcdir)/'`src/common/test_assembler.cc + +src/common/processor_stackwalker_arm64_unittest-test_assembler.obj: src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/processor_stackwalker_arm64_unittest-test_assembler.obj -MD -MP -MF src/common/$(DEPDIR)/processor_stackwalker_arm64_unittest-test_assembler.Tpo -c -o src/common/processor_stackwalker_arm64_unittest-test_assembler.obj `if test -f 'src/common/test_assembler.cc'; then $(CYGPATH_W) 'src/common/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/processor_stackwalker_arm64_unittest-test_assembler.Tpo src/common/$(DEPDIR)/processor_stackwalker_arm64_unittest-test_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler.cc' object='src/common/processor_stackwalker_arm64_unittest-test_assembler.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/processor_stackwalker_arm64_unittest-test_assembler.obj `if test -f 'src/common/test_assembler.cc'; then $(CYGPATH_W) 'src/common/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler.cc'; fi` + +src/processor/stackwalker_arm64_unittest-stackwalker_arm64_unittest.o: src/processor/stackwalker_arm64_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/stackwalker_arm64_unittest-stackwalker_arm64_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/stackwalker_arm64_unittest-stackwalker_arm64_unittest.Tpo -c -o src/processor/stackwalker_arm64_unittest-stackwalker_arm64_unittest.o `test -f 'src/processor/stackwalker_arm64_unittest.cc' || echo '$(srcdir)/'`src/processor/stackwalker_arm64_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/stackwalker_arm64_unittest-stackwalker_arm64_unittest.Tpo src/processor/$(DEPDIR)/stackwalker_arm64_unittest-stackwalker_arm64_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/stackwalker_arm64_unittest.cc' object='src/processor/stackwalker_arm64_unittest-stackwalker_arm64_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/stackwalker_arm64_unittest-stackwalker_arm64_unittest.o `test -f 'src/processor/stackwalker_arm64_unittest.cc' || echo '$(srcdir)/'`src/processor/stackwalker_arm64_unittest.cc + +src/processor/stackwalker_arm64_unittest-stackwalker_arm64_unittest.obj: src/processor/stackwalker_arm64_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/stackwalker_arm64_unittest-stackwalker_arm64_unittest.obj -MD -MP -MF src/processor/$(DEPDIR)/stackwalker_arm64_unittest-stackwalker_arm64_unittest.Tpo -c -o src/processor/stackwalker_arm64_unittest-stackwalker_arm64_unittest.obj `if test -f 'src/processor/stackwalker_arm64_unittest.cc'; then $(CYGPATH_W) 'src/processor/stackwalker_arm64_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/stackwalker_arm64_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/stackwalker_arm64_unittest-stackwalker_arm64_unittest.Tpo src/processor/$(DEPDIR)/stackwalker_arm64_unittest-stackwalker_arm64_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/stackwalker_arm64_unittest.cc' object='src/processor/stackwalker_arm64_unittest-stackwalker_arm64_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/stackwalker_arm64_unittest-stackwalker_arm64_unittest.obj `if test -f 'src/processor/stackwalker_arm64_unittest.cc'; then $(CYGPATH_W) 'src/processor/stackwalker_arm64_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/stackwalker_arm64_unittest.cc'; fi` + +src/common/processor_stackwalker_arm_unittest-test_assembler.o: src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/processor_stackwalker_arm_unittest-test_assembler.o -MD -MP -MF src/common/$(DEPDIR)/processor_stackwalker_arm_unittest-test_assembler.Tpo -c -o src/common/processor_stackwalker_arm_unittest-test_assembler.o `test -f 'src/common/test_assembler.cc' || echo '$(srcdir)/'`src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/processor_stackwalker_arm_unittest-test_assembler.Tpo src/common/$(DEPDIR)/processor_stackwalker_arm_unittest-test_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler.cc' object='src/common/processor_stackwalker_arm_unittest-test_assembler.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/processor_stackwalker_arm_unittest-test_assembler.o `test -f 'src/common/test_assembler.cc' || echo '$(srcdir)/'`src/common/test_assembler.cc + +src/common/processor_stackwalker_arm_unittest-test_assembler.obj: src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/processor_stackwalker_arm_unittest-test_assembler.obj -MD -MP -MF src/common/$(DEPDIR)/processor_stackwalker_arm_unittest-test_assembler.Tpo -c -o src/common/processor_stackwalker_arm_unittest-test_assembler.obj `if test -f 'src/common/test_assembler.cc'; then $(CYGPATH_W) 'src/common/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/processor_stackwalker_arm_unittest-test_assembler.Tpo src/common/$(DEPDIR)/processor_stackwalker_arm_unittest-test_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler.cc' object='src/common/processor_stackwalker_arm_unittest-test_assembler.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/processor_stackwalker_arm_unittest-test_assembler.obj `if test -f 'src/common/test_assembler.cc'; then $(CYGPATH_W) 'src/common/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler.cc'; fi` + +src/processor/stackwalker_arm_unittest-stackwalker_arm_unittest.o: src/processor/stackwalker_arm_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/stackwalker_arm_unittest-stackwalker_arm_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/stackwalker_arm_unittest-stackwalker_arm_unittest.Tpo -c -o src/processor/stackwalker_arm_unittest-stackwalker_arm_unittest.o `test -f 'src/processor/stackwalker_arm_unittest.cc' || echo '$(srcdir)/'`src/processor/stackwalker_arm_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/stackwalker_arm_unittest-stackwalker_arm_unittest.Tpo src/processor/$(DEPDIR)/stackwalker_arm_unittest-stackwalker_arm_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/stackwalker_arm_unittest.cc' object='src/processor/stackwalker_arm_unittest-stackwalker_arm_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/stackwalker_arm_unittest-stackwalker_arm_unittest.o `test -f 'src/processor/stackwalker_arm_unittest.cc' || echo '$(srcdir)/'`src/processor/stackwalker_arm_unittest.cc + +src/processor/stackwalker_arm_unittest-stackwalker_arm_unittest.obj: src/processor/stackwalker_arm_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/stackwalker_arm_unittest-stackwalker_arm_unittest.obj -MD -MP -MF src/processor/$(DEPDIR)/stackwalker_arm_unittest-stackwalker_arm_unittest.Tpo -c -o src/processor/stackwalker_arm_unittest-stackwalker_arm_unittest.obj `if test -f 'src/processor/stackwalker_arm_unittest.cc'; then $(CYGPATH_W) 'src/processor/stackwalker_arm_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/stackwalker_arm_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/stackwalker_arm_unittest-stackwalker_arm_unittest.Tpo src/processor/$(DEPDIR)/stackwalker_arm_unittest-stackwalker_arm_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/stackwalker_arm_unittest.cc' object='src/processor/stackwalker_arm_unittest-stackwalker_arm_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_arm_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/stackwalker_arm_unittest-stackwalker_arm_unittest.obj `if test -f 'src/processor/stackwalker_arm_unittest.cc'; then $(CYGPATH_W) 'src/processor/stackwalker_arm_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/stackwalker_arm_unittest.cc'; fi` + +src/common/processor_stackwalker_mips64_unittest-test_assembler.o: src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_mips64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/processor_stackwalker_mips64_unittest-test_assembler.o -MD -MP -MF src/common/$(DEPDIR)/processor_stackwalker_mips64_unittest-test_assembler.Tpo -c -o src/common/processor_stackwalker_mips64_unittest-test_assembler.o `test -f 'src/common/test_assembler.cc' || echo '$(srcdir)/'`src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/processor_stackwalker_mips64_unittest-test_assembler.Tpo src/common/$(DEPDIR)/processor_stackwalker_mips64_unittest-test_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler.cc' object='src/common/processor_stackwalker_mips64_unittest-test_assembler.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_mips64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/processor_stackwalker_mips64_unittest-test_assembler.o `test -f 'src/common/test_assembler.cc' || echo '$(srcdir)/'`src/common/test_assembler.cc + +src/common/processor_stackwalker_mips64_unittest-test_assembler.obj: src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_mips64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/processor_stackwalker_mips64_unittest-test_assembler.obj -MD -MP -MF src/common/$(DEPDIR)/processor_stackwalker_mips64_unittest-test_assembler.Tpo -c -o src/common/processor_stackwalker_mips64_unittest-test_assembler.obj `if test -f 'src/common/test_assembler.cc'; then $(CYGPATH_W) 'src/common/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/processor_stackwalker_mips64_unittest-test_assembler.Tpo src/common/$(DEPDIR)/processor_stackwalker_mips64_unittest-test_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler.cc' object='src/common/processor_stackwalker_mips64_unittest-test_assembler.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_mips64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/processor_stackwalker_mips64_unittest-test_assembler.obj `if test -f 'src/common/test_assembler.cc'; then $(CYGPATH_W) 'src/common/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler.cc'; fi` + +src/processor/stackwalker_mips64_unittest-stackwalker_mips64_unittest.o: src/processor/stackwalker_mips64_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_mips64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/stackwalker_mips64_unittest-stackwalker_mips64_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/stackwalker_mips64_unittest-stackwalker_mips64_unittest.Tpo -c -o src/processor/stackwalker_mips64_unittest-stackwalker_mips64_unittest.o `test -f 'src/processor/stackwalker_mips64_unittest.cc' || echo '$(srcdir)/'`src/processor/stackwalker_mips64_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/stackwalker_mips64_unittest-stackwalker_mips64_unittest.Tpo src/processor/$(DEPDIR)/stackwalker_mips64_unittest-stackwalker_mips64_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/stackwalker_mips64_unittest.cc' object='src/processor/stackwalker_mips64_unittest-stackwalker_mips64_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_mips64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/stackwalker_mips64_unittest-stackwalker_mips64_unittest.o `test -f 'src/processor/stackwalker_mips64_unittest.cc' || echo '$(srcdir)/'`src/processor/stackwalker_mips64_unittest.cc + +src/processor/stackwalker_mips64_unittest-stackwalker_mips64_unittest.obj: src/processor/stackwalker_mips64_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_mips64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/stackwalker_mips64_unittest-stackwalker_mips64_unittest.obj -MD -MP -MF src/processor/$(DEPDIR)/stackwalker_mips64_unittest-stackwalker_mips64_unittest.Tpo -c -o src/processor/stackwalker_mips64_unittest-stackwalker_mips64_unittest.obj `if test -f 'src/processor/stackwalker_mips64_unittest.cc'; then $(CYGPATH_W) 'src/processor/stackwalker_mips64_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/stackwalker_mips64_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/stackwalker_mips64_unittest-stackwalker_mips64_unittest.Tpo src/processor/$(DEPDIR)/stackwalker_mips64_unittest-stackwalker_mips64_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/stackwalker_mips64_unittest.cc' object='src/processor/stackwalker_mips64_unittest-stackwalker_mips64_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_mips64_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/stackwalker_mips64_unittest-stackwalker_mips64_unittest.obj `if test -f 'src/processor/stackwalker_mips64_unittest.cc'; then $(CYGPATH_W) 'src/processor/stackwalker_mips64_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/stackwalker_mips64_unittest.cc'; fi` + +src/common/processor_stackwalker_mips_unittest-test_assembler.o: src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_mips_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/processor_stackwalker_mips_unittest-test_assembler.o -MD -MP -MF src/common/$(DEPDIR)/processor_stackwalker_mips_unittest-test_assembler.Tpo -c -o src/common/processor_stackwalker_mips_unittest-test_assembler.o `test -f 'src/common/test_assembler.cc' || echo '$(srcdir)/'`src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/processor_stackwalker_mips_unittest-test_assembler.Tpo src/common/$(DEPDIR)/processor_stackwalker_mips_unittest-test_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler.cc' object='src/common/processor_stackwalker_mips_unittest-test_assembler.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_mips_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/processor_stackwalker_mips_unittest-test_assembler.o `test -f 'src/common/test_assembler.cc' || echo '$(srcdir)/'`src/common/test_assembler.cc + +src/common/processor_stackwalker_mips_unittest-test_assembler.obj: src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_mips_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/processor_stackwalker_mips_unittest-test_assembler.obj -MD -MP -MF src/common/$(DEPDIR)/processor_stackwalker_mips_unittest-test_assembler.Tpo -c -o src/common/processor_stackwalker_mips_unittest-test_assembler.obj `if test -f 'src/common/test_assembler.cc'; then $(CYGPATH_W) 'src/common/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/processor_stackwalker_mips_unittest-test_assembler.Tpo src/common/$(DEPDIR)/processor_stackwalker_mips_unittest-test_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler.cc' object='src/common/processor_stackwalker_mips_unittest-test_assembler.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_mips_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/processor_stackwalker_mips_unittest-test_assembler.obj `if test -f 'src/common/test_assembler.cc'; then $(CYGPATH_W) 'src/common/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler.cc'; fi` + +src/processor/stackwalker_mips_unittest-stackwalker_mips_unittest.o: src/processor/stackwalker_mips_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_mips_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/stackwalker_mips_unittest-stackwalker_mips_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/stackwalker_mips_unittest-stackwalker_mips_unittest.Tpo -c -o src/processor/stackwalker_mips_unittest-stackwalker_mips_unittest.o `test -f 'src/processor/stackwalker_mips_unittest.cc' || echo '$(srcdir)/'`src/processor/stackwalker_mips_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/stackwalker_mips_unittest-stackwalker_mips_unittest.Tpo src/processor/$(DEPDIR)/stackwalker_mips_unittest-stackwalker_mips_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/stackwalker_mips_unittest.cc' object='src/processor/stackwalker_mips_unittest-stackwalker_mips_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_mips_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/stackwalker_mips_unittest-stackwalker_mips_unittest.o `test -f 'src/processor/stackwalker_mips_unittest.cc' || echo '$(srcdir)/'`src/processor/stackwalker_mips_unittest.cc + +src/processor/stackwalker_mips_unittest-stackwalker_mips_unittest.obj: src/processor/stackwalker_mips_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_mips_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/stackwalker_mips_unittest-stackwalker_mips_unittest.obj -MD -MP -MF src/processor/$(DEPDIR)/stackwalker_mips_unittest-stackwalker_mips_unittest.Tpo -c -o src/processor/stackwalker_mips_unittest-stackwalker_mips_unittest.obj `if test -f 'src/processor/stackwalker_mips_unittest.cc'; then $(CYGPATH_W) 'src/processor/stackwalker_mips_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/stackwalker_mips_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/stackwalker_mips_unittest-stackwalker_mips_unittest.Tpo src/processor/$(DEPDIR)/stackwalker_mips_unittest-stackwalker_mips_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/stackwalker_mips_unittest.cc' object='src/processor/stackwalker_mips_unittest-stackwalker_mips_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_mips_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/stackwalker_mips_unittest-stackwalker_mips_unittest.obj `if test -f 'src/processor/stackwalker_mips_unittest.cc'; then $(CYGPATH_W) 'src/processor/stackwalker_mips_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/stackwalker_mips_unittest.cc'; fi` + +src/common/processor_stackwalker_x86_unittest-test_assembler.o: src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_x86_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/processor_stackwalker_x86_unittest-test_assembler.o -MD -MP -MF src/common/$(DEPDIR)/processor_stackwalker_x86_unittest-test_assembler.Tpo -c -o src/common/processor_stackwalker_x86_unittest-test_assembler.o `test -f 'src/common/test_assembler.cc' || echo '$(srcdir)/'`src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/processor_stackwalker_x86_unittest-test_assembler.Tpo src/common/$(DEPDIR)/processor_stackwalker_x86_unittest-test_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler.cc' object='src/common/processor_stackwalker_x86_unittest-test_assembler.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_x86_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/processor_stackwalker_x86_unittest-test_assembler.o `test -f 'src/common/test_assembler.cc' || echo '$(srcdir)/'`src/common/test_assembler.cc + +src/common/processor_stackwalker_x86_unittest-test_assembler.obj: src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_x86_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/processor_stackwalker_x86_unittest-test_assembler.obj -MD -MP -MF src/common/$(DEPDIR)/processor_stackwalker_x86_unittest-test_assembler.Tpo -c -o src/common/processor_stackwalker_x86_unittest-test_assembler.obj `if test -f 'src/common/test_assembler.cc'; then $(CYGPATH_W) 'src/common/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/processor_stackwalker_x86_unittest-test_assembler.Tpo src/common/$(DEPDIR)/processor_stackwalker_x86_unittest-test_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler.cc' object='src/common/processor_stackwalker_x86_unittest-test_assembler.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_x86_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/processor_stackwalker_x86_unittest-test_assembler.obj `if test -f 'src/common/test_assembler.cc'; then $(CYGPATH_W) 'src/common/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler.cc'; fi` + +src/processor/stackwalker_x86_unittest-stackwalker_x86_unittest.o: src/processor/stackwalker_x86_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_x86_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/stackwalker_x86_unittest-stackwalker_x86_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/stackwalker_x86_unittest-stackwalker_x86_unittest.Tpo -c -o src/processor/stackwalker_x86_unittest-stackwalker_x86_unittest.o `test -f 'src/processor/stackwalker_x86_unittest.cc' || echo '$(srcdir)/'`src/processor/stackwalker_x86_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/stackwalker_x86_unittest-stackwalker_x86_unittest.Tpo src/processor/$(DEPDIR)/stackwalker_x86_unittest-stackwalker_x86_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/stackwalker_x86_unittest.cc' object='src/processor/stackwalker_x86_unittest-stackwalker_x86_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_x86_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/stackwalker_x86_unittest-stackwalker_x86_unittest.o `test -f 'src/processor/stackwalker_x86_unittest.cc' || echo '$(srcdir)/'`src/processor/stackwalker_x86_unittest.cc + +src/processor/stackwalker_x86_unittest-stackwalker_x86_unittest.obj: src/processor/stackwalker_x86_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_x86_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/stackwalker_x86_unittest-stackwalker_x86_unittest.obj -MD -MP -MF src/processor/$(DEPDIR)/stackwalker_x86_unittest-stackwalker_x86_unittest.Tpo -c -o src/processor/stackwalker_x86_unittest-stackwalker_x86_unittest.obj `if test -f 'src/processor/stackwalker_x86_unittest.cc'; then $(CYGPATH_W) 'src/processor/stackwalker_x86_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/stackwalker_x86_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/stackwalker_x86_unittest-stackwalker_x86_unittest.Tpo src/processor/$(DEPDIR)/stackwalker_x86_unittest-stackwalker_x86_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/stackwalker_x86_unittest.cc' object='src/processor/stackwalker_x86_unittest-stackwalker_x86_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_stackwalker_x86_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/stackwalker_x86_unittest-stackwalker_x86_unittest.obj `if test -f 'src/processor/stackwalker_x86_unittest.cc'; then $(CYGPATH_W) 'src/processor/stackwalker_x86_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/stackwalker_x86_unittest.cc'; fi` + +src/processor/static_address_map_unittest-static_address_map_unittest.o: src/processor/static_address_map_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_static_address_map_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/static_address_map_unittest-static_address_map_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/static_address_map_unittest-static_address_map_unittest.Tpo -c -o src/processor/static_address_map_unittest-static_address_map_unittest.o `test -f 'src/processor/static_address_map_unittest.cc' || echo '$(srcdir)/'`src/processor/static_address_map_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/static_address_map_unittest-static_address_map_unittest.Tpo src/processor/$(DEPDIR)/static_address_map_unittest-static_address_map_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/static_address_map_unittest.cc' object='src/processor/static_address_map_unittest-static_address_map_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_static_address_map_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/static_address_map_unittest-static_address_map_unittest.o `test -f 'src/processor/static_address_map_unittest.cc' || echo '$(srcdir)/'`src/processor/static_address_map_unittest.cc + +src/processor/static_address_map_unittest-static_address_map_unittest.obj: src/processor/static_address_map_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_static_address_map_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/static_address_map_unittest-static_address_map_unittest.obj -MD -MP -MF src/processor/$(DEPDIR)/static_address_map_unittest-static_address_map_unittest.Tpo -c -o src/processor/static_address_map_unittest-static_address_map_unittest.obj `if test -f 'src/processor/static_address_map_unittest.cc'; then $(CYGPATH_W) 'src/processor/static_address_map_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/static_address_map_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/static_address_map_unittest-static_address_map_unittest.Tpo src/processor/$(DEPDIR)/static_address_map_unittest-static_address_map_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/static_address_map_unittest.cc' object='src/processor/static_address_map_unittest-static_address_map_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_static_address_map_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/static_address_map_unittest-static_address_map_unittest.obj `if test -f 'src/processor/static_address_map_unittest.cc'; then $(CYGPATH_W) 'src/processor/static_address_map_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/static_address_map_unittest.cc'; fi` + +src/processor/static_contained_range_map_unittest-static_contained_range_map_unittest.o: src/processor/static_contained_range_map_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_static_contained_range_map_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/static_contained_range_map_unittest-static_contained_range_map_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/static_contained_range_map_unittest-static_contained_range_map_unittest.Tpo -c -o src/processor/static_contained_range_map_unittest-static_contained_range_map_unittest.o `test -f 'src/processor/static_contained_range_map_unittest.cc' || echo '$(srcdir)/'`src/processor/static_contained_range_map_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/static_contained_range_map_unittest-static_contained_range_map_unittest.Tpo src/processor/$(DEPDIR)/static_contained_range_map_unittest-static_contained_range_map_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/static_contained_range_map_unittest.cc' object='src/processor/static_contained_range_map_unittest-static_contained_range_map_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_static_contained_range_map_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/static_contained_range_map_unittest-static_contained_range_map_unittest.o `test -f 'src/processor/static_contained_range_map_unittest.cc' || echo '$(srcdir)/'`src/processor/static_contained_range_map_unittest.cc + +src/processor/static_contained_range_map_unittest-static_contained_range_map_unittest.obj: src/processor/static_contained_range_map_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_static_contained_range_map_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/static_contained_range_map_unittest-static_contained_range_map_unittest.obj -MD -MP -MF src/processor/$(DEPDIR)/static_contained_range_map_unittest-static_contained_range_map_unittest.Tpo -c -o src/processor/static_contained_range_map_unittest-static_contained_range_map_unittest.obj `if test -f 'src/processor/static_contained_range_map_unittest.cc'; then $(CYGPATH_W) 'src/processor/static_contained_range_map_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/static_contained_range_map_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/static_contained_range_map_unittest-static_contained_range_map_unittest.Tpo src/processor/$(DEPDIR)/static_contained_range_map_unittest-static_contained_range_map_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/static_contained_range_map_unittest.cc' object='src/processor/static_contained_range_map_unittest-static_contained_range_map_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_static_contained_range_map_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/static_contained_range_map_unittest-static_contained_range_map_unittest.obj `if test -f 'src/processor/static_contained_range_map_unittest.cc'; then $(CYGPATH_W) 'src/processor/static_contained_range_map_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/static_contained_range_map_unittest.cc'; fi` + +src/processor/static_map_unittest-static_map_unittest.o: src/processor/static_map_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_static_map_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/static_map_unittest-static_map_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/static_map_unittest-static_map_unittest.Tpo -c -o src/processor/static_map_unittest-static_map_unittest.o `test -f 'src/processor/static_map_unittest.cc' || echo '$(srcdir)/'`src/processor/static_map_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/static_map_unittest-static_map_unittest.Tpo src/processor/$(DEPDIR)/static_map_unittest-static_map_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/static_map_unittest.cc' object='src/processor/static_map_unittest-static_map_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_static_map_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/static_map_unittest-static_map_unittest.o `test -f 'src/processor/static_map_unittest.cc' || echo '$(srcdir)/'`src/processor/static_map_unittest.cc + +src/processor/static_map_unittest-static_map_unittest.obj: src/processor/static_map_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_static_map_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/static_map_unittest-static_map_unittest.obj -MD -MP -MF src/processor/$(DEPDIR)/static_map_unittest-static_map_unittest.Tpo -c -o src/processor/static_map_unittest-static_map_unittest.obj `if test -f 'src/processor/static_map_unittest.cc'; then $(CYGPATH_W) 'src/processor/static_map_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/static_map_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/static_map_unittest-static_map_unittest.Tpo src/processor/$(DEPDIR)/static_map_unittest-static_map_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/static_map_unittest.cc' object='src/processor/static_map_unittest-static_map_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_static_map_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/static_map_unittest-static_map_unittest.obj `if test -f 'src/processor/static_map_unittest.cc'; then $(CYGPATH_W) 'src/processor/static_map_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/static_map_unittest.cc'; fi` + +src/processor/static_range_map_unittest-static_range_map_unittest.o: src/processor/static_range_map_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_static_range_map_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/static_range_map_unittest-static_range_map_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/static_range_map_unittest-static_range_map_unittest.Tpo -c -o src/processor/static_range_map_unittest-static_range_map_unittest.o `test -f 'src/processor/static_range_map_unittest.cc' || echo '$(srcdir)/'`src/processor/static_range_map_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/static_range_map_unittest-static_range_map_unittest.Tpo src/processor/$(DEPDIR)/static_range_map_unittest-static_range_map_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/static_range_map_unittest.cc' object='src/processor/static_range_map_unittest-static_range_map_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_static_range_map_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/static_range_map_unittest-static_range_map_unittest.o `test -f 'src/processor/static_range_map_unittest.cc' || echo '$(srcdir)/'`src/processor/static_range_map_unittest.cc + +src/processor/static_range_map_unittest-static_range_map_unittest.obj: src/processor/static_range_map_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_static_range_map_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/static_range_map_unittest-static_range_map_unittest.obj -MD -MP -MF src/processor/$(DEPDIR)/static_range_map_unittest-static_range_map_unittest.Tpo -c -o src/processor/static_range_map_unittest-static_range_map_unittest.obj `if test -f 'src/processor/static_range_map_unittest.cc'; then $(CYGPATH_W) 'src/processor/static_range_map_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/static_range_map_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/static_range_map_unittest-static_range_map_unittest.Tpo src/processor/$(DEPDIR)/static_range_map_unittest-static_range_map_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/static_range_map_unittest.cc' object='src/processor/static_range_map_unittest-static_range_map_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_static_range_map_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/static_range_map_unittest-static_range_map_unittest.obj `if test -f 'src/processor/static_range_map_unittest.cc'; then $(CYGPATH_W) 'src/processor/static_range_map_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/static_range_map_unittest.cc'; fi` + +src/common/processor_synth_minidump_unittest-test_assembler.o: src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_synth_minidump_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/processor_synth_minidump_unittest-test_assembler.o -MD -MP -MF src/common/$(DEPDIR)/processor_synth_minidump_unittest-test_assembler.Tpo -c -o src/common/processor_synth_minidump_unittest-test_assembler.o `test -f 'src/common/test_assembler.cc' || echo '$(srcdir)/'`src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/processor_synth_minidump_unittest-test_assembler.Tpo src/common/$(DEPDIR)/processor_synth_minidump_unittest-test_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler.cc' object='src/common/processor_synth_minidump_unittest-test_assembler.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_synth_minidump_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/processor_synth_minidump_unittest-test_assembler.o `test -f 'src/common/test_assembler.cc' || echo '$(srcdir)/'`src/common/test_assembler.cc + +src/common/processor_synth_minidump_unittest-test_assembler.obj: src/common/test_assembler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_synth_minidump_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/common/processor_synth_minidump_unittest-test_assembler.obj -MD -MP -MF src/common/$(DEPDIR)/processor_synth_minidump_unittest-test_assembler.Tpo -c -o src/common/processor_synth_minidump_unittest-test_assembler.obj `if test -f 'src/common/test_assembler.cc'; then $(CYGPATH_W) 'src/common/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/processor_synth_minidump_unittest-test_assembler.Tpo src/common/$(DEPDIR)/processor_synth_minidump_unittest-test_assembler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/test_assembler.cc' object='src/common/processor_synth_minidump_unittest-test_assembler.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_synth_minidump_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/common/processor_synth_minidump_unittest-test_assembler.obj `if test -f 'src/common/test_assembler.cc'; then $(CYGPATH_W) 'src/common/test_assembler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/test_assembler.cc'; fi` + +src/processor/synth_minidump_unittest-synth_minidump_unittest.o: src/processor/synth_minidump_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_synth_minidump_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/synth_minidump_unittest-synth_minidump_unittest.o -MD -MP -MF src/processor/$(DEPDIR)/synth_minidump_unittest-synth_minidump_unittest.Tpo -c -o src/processor/synth_minidump_unittest-synth_minidump_unittest.o `test -f 'src/processor/synth_minidump_unittest.cc' || echo '$(srcdir)/'`src/processor/synth_minidump_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/synth_minidump_unittest-synth_minidump_unittest.Tpo src/processor/$(DEPDIR)/synth_minidump_unittest-synth_minidump_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/synth_minidump_unittest.cc' object='src/processor/synth_minidump_unittest-synth_minidump_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_synth_minidump_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/synth_minidump_unittest-synth_minidump_unittest.o `test -f 'src/processor/synth_minidump_unittest.cc' || echo '$(srcdir)/'`src/processor/synth_minidump_unittest.cc + +src/processor/synth_minidump_unittest-synth_minidump_unittest.obj: src/processor/synth_minidump_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_synth_minidump_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/synth_minidump_unittest-synth_minidump_unittest.obj -MD -MP -MF src/processor/$(DEPDIR)/synth_minidump_unittest-synth_minidump_unittest.Tpo -c -o src/processor/synth_minidump_unittest-synth_minidump_unittest.obj `if test -f 'src/processor/synth_minidump_unittest.cc'; then $(CYGPATH_W) 'src/processor/synth_minidump_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/synth_minidump_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/synth_minidump_unittest-synth_minidump_unittest.Tpo src/processor/$(DEPDIR)/synth_minidump_unittest-synth_minidump_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/synth_minidump_unittest.cc' object='src/processor/synth_minidump_unittest-synth_minidump_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_synth_minidump_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/synth_minidump_unittest-synth_minidump_unittest.obj `if test -f 'src/processor/synth_minidump_unittest.cc'; then $(CYGPATH_W) 'src/processor/synth_minidump_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/synth_minidump_unittest.cc'; fi` + +src/processor/synth_minidump_unittest-synth_minidump.o: src/processor/synth_minidump.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_synth_minidump_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/synth_minidump_unittest-synth_minidump.o -MD -MP -MF src/processor/$(DEPDIR)/synth_minidump_unittest-synth_minidump.Tpo -c -o src/processor/synth_minidump_unittest-synth_minidump.o `test -f 'src/processor/synth_minidump.cc' || echo '$(srcdir)/'`src/processor/synth_minidump.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/synth_minidump_unittest-synth_minidump.Tpo src/processor/$(DEPDIR)/synth_minidump_unittest-synth_minidump.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/synth_minidump.cc' object='src/processor/synth_minidump_unittest-synth_minidump.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_synth_minidump_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/synth_minidump_unittest-synth_minidump.o `test -f 'src/processor/synth_minidump.cc' || echo '$(srcdir)/'`src/processor/synth_minidump.cc + +src/processor/synth_minidump_unittest-synth_minidump.obj: src/processor/synth_minidump.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_synth_minidump_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/processor/synth_minidump_unittest-synth_minidump.obj -MD -MP -MF src/processor/$(DEPDIR)/synth_minidump_unittest-synth_minidump.Tpo -c -o src/processor/synth_minidump_unittest-synth_minidump.obj `if test -f 'src/processor/synth_minidump.cc'; then $(CYGPATH_W) 'src/processor/synth_minidump.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/synth_minidump.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/processor/$(DEPDIR)/synth_minidump_unittest-synth_minidump.Tpo src/processor/$(DEPDIR)/synth_minidump_unittest-synth_minidump.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/processor/synth_minidump.cc' object='src/processor/synth_minidump_unittest-synth_minidump.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_processor_synth_minidump_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/processor/synth_minidump_unittest-synth_minidump.obj `if test -f 'src/processor/synth_minidump.cc'; then $(CYGPATH_W) 'src/processor/synth_minidump.cc'; else $(CYGPATH_W) '$(srcdir)/src/processor/synth_minidump.cc'; fi` + +src/common/tools_linux_dump_syms_dump_syms-dwarf_cfi_to_module.o: src/common/dwarf_cfi_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_linux_dump_syms_dump_syms-dwarf_cfi_to_module.o -MD -MP -MF src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_cfi_to_module.Tpo -c -o src/common/tools_linux_dump_syms_dump_syms-dwarf_cfi_to_module.o `test -f 'src/common/dwarf_cfi_to_module.cc' || echo '$(srcdir)/'`src/common/dwarf_cfi_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_cfi_to_module.Tpo src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_cfi_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_cfi_to_module.cc' object='src/common/tools_linux_dump_syms_dump_syms-dwarf_cfi_to_module.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_linux_dump_syms_dump_syms-dwarf_cfi_to_module.o `test -f 'src/common/dwarf_cfi_to_module.cc' || echo '$(srcdir)/'`src/common/dwarf_cfi_to_module.cc + +src/common/tools_linux_dump_syms_dump_syms-dwarf_cfi_to_module.obj: src/common/dwarf_cfi_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_linux_dump_syms_dump_syms-dwarf_cfi_to_module.obj -MD -MP -MF src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_cfi_to_module.Tpo -c -o src/common/tools_linux_dump_syms_dump_syms-dwarf_cfi_to_module.obj `if test -f 'src/common/dwarf_cfi_to_module.cc'; then $(CYGPATH_W) 'src/common/dwarf_cfi_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_cfi_to_module.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_cfi_to_module.Tpo src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_cfi_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_cfi_to_module.cc' object='src/common/tools_linux_dump_syms_dump_syms-dwarf_cfi_to_module.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_linux_dump_syms_dump_syms-dwarf_cfi_to_module.obj `if test -f 'src/common/dwarf_cfi_to_module.cc'; then $(CYGPATH_W) 'src/common/dwarf_cfi_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_cfi_to_module.cc'; fi` + +src/common/tools_linux_dump_syms_dump_syms-dwarf_cu_to_module.o: src/common/dwarf_cu_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_linux_dump_syms_dump_syms-dwarf_cu_to_module.o -MD -MP -MF src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_cu_to_module.Tpo -c -o src/common/tools_linux_dump_syms_dump_syms-dwarf_cu_to_module.o `test -f 'src/common/dwarf_cu_to_module.cc' || echo '$(srcdir)/'`src/common/dwarf_cu_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_cu_to_module.Tpo src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_cu_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_cu_to_module.cc' object='src/common/tools_linux_dump_syms_dump_syms-dwarf_cu_to_module.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_linux_dump_syms_dump_syms-dwarf_cu_to_module.o `test -f 'src/common/dwarf_cu_to_module.cc' || echo '$(srcdir)/'`src/common/dwarf_cu_to_module.cc + +src/common/tools_linux_dump_syms_dump_syms-dwarf_cu_to_module.obj: src/common/dwarf_cu_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_linux_dump_syms_dump_syms-dwarf_cu_to_module.obj -MD -MP -MF src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_cu_to_module.Tpo -c -o src/common/tools_linux_dump_syms_dump_syms-dwarf_cu_to_module.obj `if test -f 'src/common/dwarf_cu_to_module.cc'; then $(CYGPATH_W) 'src/common/dwarf_cu_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_cu_to_module.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_cu_to_module.Tpo src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_cu_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_cu_to_module.cc' object='src/common/tools_linux_dump_syms_dump_syms-dwarf_cu_to_module.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_linux_dump_syms_dump_syms-dwarf_cu_to_module.obj `if test -f 'src/common/dwarf_cu_to_module.cc'; then $(CYGPATH_W) 'src/common/dwarf_cu_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_cu_to_module.cc'; fi` + +src/common/tools_linux_dump_syms_dump_syms-dwarf_line_to_module.o: src/common/dwarf_line_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_linux_dump_syms_dump_syms-dwarf_line_to_module.o -MD -MP -MF src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_line_to_module.Tpo -c -o src/common/tools_linux_dump_syms_dump_syms-dwarf_line_to_module.o `test -f 'src/common/dwarf_line_to_module.cc' || echo '$(srcdir)/'`src/common/dwarf_line_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_line_to_module.Tpo src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_line_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_line_to_module.cc' object='src/common/tools_linux_dump_syms_dump_syms-dwarf_line_to_module.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_linux_dump_syms_dump_syms-dwarf_line_to_module.o `test -f 'src/common/dwarf_line_to_module.cc' || echo '$(srcdir)/'`src/common/dwarf_line_to_module.cc + +src/common/tools_linux_dump_syms_dump_syms-dwarf_line_to_module.obj: src/common/dwarf_line_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_linux_dump_syms_dump_syms-dwarf_line_to_module.obj -MD -MP -MF src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_line_to_module.Tpo -c -o src/common/tools_linux_dump_syms_dump_syms-dwarf_line_to_module.obj `if test -f 'src/common/dwarf_line_to_module.cc'; then $(CYGPATH_W) 'src/common/dwarf_line_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_line_to_module.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_line_to_module.Tpo src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_line_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_line_to_module.cc' object='src/common/tools_linux_dump_syms_dump_syms-dwarf_line_to_module.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_linux_dump_syms_dump_syms-dwarf_line_to_module.obj `if test -f 'src/common/dwarf_line_to_module.cc'; then $(CYGPATH_W) 'src/common/dwarf_line_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_line_to_module.cc'; fi` + +src/common/tools_linux_dump_syms_dump_syms-dwarf_range_list_handler.o: src/common/dwarf_range_list_handler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_linux_dump_syms_dump_syms-dwarf_range_list_handler.o -MD -MP -MF src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_range_list_handler.Tpo -c -o src/common/tools_linux_dump_syms_dump_syms-dwarf_range_list_handler.o `test -f 'src/common/dwarf_range_list_handler.cc' || echo '$(srcdir)/'`src/common/dwarf_range_list_handler.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_range_list_handler.Tpo src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_range_list_handler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_range_list_handler.cc' object='src/common/tools_linux_dump_syms_dump_syms-dwarf_range_list_handler.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_linux_dump_syms_dump_syms-dwarf_range_list_handler.o `test -f 'src/common/dwarf_range_list_handler.cc' || echo '$(srcdir)/'`src/common/dwarf_range_list_handler.cc + +src/common/tools_linux_dump_syms_dump_syms-dwarf_range_list_handler.obj: src/common/dwarf_range_list_handler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_linux_dump_syms_dump_syms-dwarf_range_list_handler.obj -MD -MP -MF src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_range_list_handler.Tpo -c -o src/common/tools_linux_dump_syms_dump_syms-dwarf_range_list_handler.obj `if test -f 'src/common/dwarf_range_list_handler.cc'; then $(CYGPATH_W) 'src/common/dwarf_range_list_handler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_range_list_handler.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_range_list_handler.Tpo src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_range_list_handler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_range_list_handler.cc' object='src/common/tools_linux_dump_syms_dump_syms-dwarf_range_list_handler.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_linux_dump_syms_dump_syms-dwarf_range_list_handler.obj `if test -f 'src/common/dwarf_range_list_handler.cc'; then $(CYGPATH_W) 'src/common/dwarf_range_list_handler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_range_list_handler.cc'; fi` + +src/common/tools_linux_dump_syms_dump_syms-language.o: src/common/language.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_linux_dump_syms_dump_syms-language.o -MD -MP -MF src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-language.Tpo -c -o src/common/tools_linux_dump_syms_dump_syms-language.o `test -f 'src/common/language.cc' || echo '$(srcdir)/'`src/common/language.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-language.Tpo src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-language.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/language.cc' object='src/common/tools_linux_dump_syms_dump_syms-language.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_linux_dump_syms_dump_syms-language.o `test -f 'src/common/language.cc' || echo '$(srcdir)/'`src/common/language.cc + +src/common/tools_linux_dump_syms_dump_syms-language.obj: src/common/language.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_linux_dump_syms_dump_syms-language.obj -MD -MP -MF src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-language.Tpo -c -o src/common/tools_linux_dump_syms_dump_syms-language.obj `if test -f 'src/common/language.cc'; then $(CYGPATH_W) 'src/common/language.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/language.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-language.Tpo src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-language.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/language.cc' object='src/common/tools_linux_dump_syms_dump_syms-language.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_linux_dump_syms_dump_syms-language.obj `if test -f 'src/common/language.cc'; then $(CYGPATH_W) 'src/common/language.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/language.cc'; fi` + +src/common/tools_linux_dump_syms_dump_syms-module.o: src/common/module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_linux_dump_syms_dump_syms-module.o -MD -MP -MF src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-module.Tpo -c -o src/common/tools_linux_dump_syms_dump_syms-module.o `test -f 'src/common/module.cc' || echo '$(srcdir)/'`src/common/module.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-module.Tpo src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/module.cc' object='src/common/tools_linux_dump_syms_dump_syms-module.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_linux_dump_syms_dump_syms-module.o `test -f 'src/common/module.cc' || echo '$(srcdir)/'`src/common/module.cc + +src/common/tools_linux_dump_syms_dump_syms-module.obj: src/common/module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_linux_dump_syms_dump_syms-module.obj -MD -MP -MF src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-module.Tpo -c -o src/common/tools_linux_dump_syms_dump_syms-module.obj `if test -f 'src/common/module.cc'; then $(CYGPATH_W) 'src/common/module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/module.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-module.Tpo src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/module.cc' object='src/common/tools_linux_dump_syms_dump_syms-module.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_linux_dump_syms_dump_syms-module.obj `if test -f 'src/common/module.cc'; then $(CYGPATH_W) 'src/common/module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/module.cc'; fi` + +src/common/tools_linux_dump_syms_dump_syms-path_helper.o: src/common/path_helper.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_linux_dump_syms_dump_syms-path_helper.o -MD -MP -MF src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-path_helper.Tpo -c -o src/common/tools_linux_dump_syms_dump_syms-path_helper.o `test -f 'src/common/path_helper.cc' || echo '$(srcdir)/'`src/common/path_helper.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-path_helper.Tpo src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-path_helper.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/path_helper.cc' object='src/common/tools_linux_dump_syms_dump_syms-path_helper.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_linux_dump_syms_dump_syms-path_helper.o `test -f 'src/common/path_helper.cc' || echo '$(srcdir)/'`src/common/path_helper.cc + +src/common/tools_linux_dump_syms_dump_syms-path_helper.obj: src/common/path_helper.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_linux_dump_syms_dump_syms-path_helper.obj -MD -MP -MF src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-path_helper.Tpo -c -o src/common/tools_linux_dump_syms_dump_syms-path_helper.obj `if test -f 'src/common/path_helper.cc'; then $(CYGPATH_W) 'src/common/path_helper.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/path_helper.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-path_helper.Tpo src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-path_helper.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/path_helper.cc' object='src/common/tools_linux_dump_syms_dump_syms-path_helper.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_linux_dump_syms_dump_syms-path_helper.obj `if test -f 'src/common/path_helper.cc'; then $(CYGPATH_W) 'src/common/path_helper.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/path_helper.cc'; fi` + +src/common/tools_linux_dump_syms_dump_syms-stabs_reader.o: src/common/stabs_reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_linux_dump_syms_dump_syms-stabs_reader.o -MD -MP -MF src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-stabs_reader.Tpo -c -o src/common/tools_linux_dump_syms_dump_syms-stabs_reader.o `test -f 'src/common/stabs_reader.cc' || echo '$(srcdir)/'`src/common/stabs_reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-stabs_reader.Tpo src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-stabs_reader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/stabs_reader.cc' object='src/common/tools_linux_dump_syms_dump_syms-stabs_reader.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_linux_dump_syms_dump_syms-stabs_reader.o `test -f 'src/common/stabs_reader.cc' || echo '$(srcdir)/'`src/common/stabs_reader.cc + +src/common/tools_linux_dump_syms_dump_syms-stabs_reader.obj: src/common/stabs_reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_linux_dump_syms_dump_syms-stabs_reader.obj -MD -MP -MF src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-stabs_reader.Tpo -c -o src/common/tools_linux_dump_syms_dump_syms-stabs_reader.obj `if test -f 'src/common/stabs_reader.cc'; then $(CYGPATH_W) 'src/common/stabs_reader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/stabs_reader.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-stabs_reader.Tpo src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-stabs_reader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/stabs_reader.cc' object='src/common/tools_linux_dump_syms_dump_syms-stabs_reader.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_linux_dump_syms_dump_syms-stabs_reader.obj `if test -f 'src/common/stabs_reader.cc'; then $(CYGPATH_W) 'src/common/stabs_reader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/stabs_reader.cc'; fi` + +src/common/tools_linux_dump_syms_dump_syms-stabs_to_module.o: src/common/stabs_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_linux_dump_syms_dump_syms-stabs_to_module.o -MD -MP -MF src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-stabs_to_module.Tpo -c -o src/common/tools_linux_dump_syms_dump_syms-stabs_to_module.o `test -f 'src/common/stabs_to_module.cc' || echo '$(srcdir)/'`src/common/stabs_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-stabs_to_module.Tpo src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-stabs_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/stabs_to_module.cc' object='src/common/tools_linux_dump_syms_dump_syms-stabs_to_module.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_linux_dump_syms_dump_syms-stabs_to_module.o `test -f 'src/common/stabs_to_module.cc' || echo '$(srcdir)/'`src/common/stabs_to_module.cc + +src/common/tools_linux_dump_syms_dump_syms-stabs_to_module.obj: src/common/stabs_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_linux_dump_syms_dump_syms-stabs_to_module.obj -MD -MP -MF src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-stabs_to_module.Tpo -c -o src/common/tools_linux_dump_syms_dump_syms-stabs_to_module.obj `if test -f 'src/common/stabs_to_module.cc'; then $(CYGPATH_W) 'src/common/stabs_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/stabs_to_module.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-stabs_to_module.Tpo src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-stabs_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/stabs_to_module.cc' object='src/common/tools_linux_dump_syms_dump_syms-stabs_to_module.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_linux_dump_syms_dump_syms-stabs_to_module.obj `if test -f 'src/common/stabs_to_module.cc'; then $(CYGPATH_W) 'src/common/stabs_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/stabs_to_module.cc'; fi` + +src/common/dwarf/tools_linux_dump_syms_dump_syms-bytereader.o: src/common/dwarf/bytereader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/tools_linux_dump_syms_dump_syms-bytereader.o -MD -MP -MF src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-bytereader.Tpo -c -o src/common/dwarf/tools_linux_dump_syms_dump_syms-bytereader.o `test -f 'src/common/dwarf/bytereader.cc' || echo '$(srcdir)/'`src/common/dwarf/bytereader.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-bytereader.Tpo src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-bytereader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/bytereader.cc' object='src/common/dwarf/tools_linux_dump_syms_dump_syms-bytereader.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/tools_linux_dump_syms_dump_syms-bytereader.o `test -f 'src/common/dwarf/bytereader.cc' || echo '$(srcdir)/'`src/common/dwarf/bytereader.cc + +src/common/dwarf/tools_linux_dump_syms_dump_syms-bytereader.obj: src/common/dwarf/bytereader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/tools_linux_dump_syms_dump_syms-bytereader.obj -MD -MP -MF src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-bytereader.Tpo -c -o src/common/dwarf/tools_linux_dump_syms_dump_syms-bytereader.obj `if test -f 'src/common/dwarf/bytereader.cc'; then $(CYGPATH_W) 'src/common/dwarf/bytereader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/bytereader.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-bytereader.Tpo src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-bytereader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/bytereader.cc' object='src/common/dwarf/tools_linux_dump_syms_dump_syms-bytereader.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/tools_linux_dump_syms_dump_syms-bytereader.obj `if test -f 'src/common/dwarf/bytereader.cc'; then $(CYGPATH_W) 'src/common/dwarf/bytereader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/bytereader.cc'; fi` + +src/common/dwarf/tools_linux_dump_syms_dump_syms-dwarf2diehandler.o: src/common/dwarf/dwarf2diehandler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/tools_linux_dump_syms_dump_syms-dwarf2diehandler.o -MD -MP -MF src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf2diehandler.Tpo -c -o src/common/dwarf/tools_linux_dump_syms_dump_syms-dwarf2diehandler.o `test -f 'src/common/dwarf/dwarf2diehandler.cc' || echo '$(srcdir)/'`src/common/dwarf/dwarf2diehandler.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf2diehandler.Tpo src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf2diehandler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/dwarf2diehandler.cc' object='src/common/dwarf/tools_linux_dump_syms_dump_syms-dwarf2diehandler.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/tools_linux_dump_syms_dump_syms-dwarf2diehandler.o `test -f 'src/common/dwarf/dwarf2diehandler.cc' || echo '$(srcdir)/'`src/common/dwarf/dwarf2diehandler.cc + +src/common/dwarf/tools_linux_dump_syms_dump_syms-dwarf2diehandler.obj: src/common/dwarf/dwarf2diehandler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/tools_linux_dump_syms_dump_syms-dwarf2diehandler.obj -MD -MP -MF src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf2diehandler.Tpo -c -o src/common/dwarf/tools_linux_dump_syms_dump_syms-dwarf2diehandler.obj `if test -f 'src/common/dwarf/dwarf2diehandler.cc'; then $(CYGPATH_W) 'src/common/dwarf/dwarf2diehandler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/dwarf2diehandler.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf2diehandler.Tpo src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf2diehandler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/dwarf2diehandler.cc' object='src/common/dwarf/tools_linux_dump_syms_dump_syms-dwarf2diehandler.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/tools_linux_dump_syms_dump_syms-dwarf2diehandler.obj `if test -f 'src/common/dwarf/dwarf2diehandler.cc'; then $(CYGPATH_W) 'src/common/dwarf/dwarf2diehandler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/dwarf2diehandler.cc'; fi` + +src/common/dwarf/tools_linux_dump_syms_dump_syms-dwarf2reader.o: src/common/dwarf/dwarf2reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/tools_linux_dump_syms_dump_syms-dwarf2reader.o -MD -MP -MF src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf2reader.Tpo -c -o src/common/dwarf/tools_linux_dump_syms_dump_syms-dwarf2reader.o `test -f 'src/common/dwarf/dwarf2reader.cc' || echo '$(srcdir)/'`src/common/dwarf/dwarf2reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf2reader.Tpo src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf2reader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/dwarf2reader.cc' object='src/common/dwarf/tools_linux_dump_syms_dump_syms-dwarf2reader.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/tools_linux_dump_syms_dump_syms-dwarf2reader.o `test -f 'src/common/dwarf/dwarf2reader.cc' || echo '$(srcdir)/'`src/common/dwarf/dwarf2reader.cc + +src/common/dwarf/tools_linux_dump_syms_dump_syms-dwarf2reader.obj: src/common/dwarf/dwarf2reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/tools_linux_dump_syms_dump_syms-dwarf2reader.obj -MD -MP -MF src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf2reader.Tpo -c -o src/common/dwarf/tools_linux_dump_syms_dump_syms-dwarf2reader.obj `if test -f 'src/common/dwarf/dwarf2reader.cc'; then $(CYGPATH_W) 'src/common/dwarf/dwarf2reader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/dwarf2reader.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf2reader.Tpo src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf2reader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/dwarf2reader.cc' object='src/common/dwarf/tools_linux_dump_syms_dump_syms-dwarf2reader.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/tools_linux_dump_syms_dump_syms-dwarf2reader.obj `if test -f 'src/common/dwarf/dwarf2reader.cc'; then $(CYGPATH_W) 'src/common/dwarf/dwarf2reader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/dwarf2reader.cc'; fi` + +src/common/dwarf/tools_linux_dump_syms_dump_syms-elf_reader.o: src/common/dwarf/elf_reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/tools_linux_dump_syms_dump_syms-elf_reader.o -MD -MP -MF src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-elf_reader.Tpo -c -o src/common/dwarf/tools_linux_dump_syms_dump_syms-elf_reader.o `test -f 'src/common/dwarf/elf_reader.cc' || echo '$(srcdir)/'`src/common/dwarf/elf_reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-elf_reader.Tpo src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-elf_reader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/elf_reader.cc' object='src/common/dwarf/tools_linux_dump_syms_dump_syms-elf_reader.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/tools_linux_dump_syms_dump_syms-elf_reader.o `test -f 'src/common/dwarf/elf_reader.cc' || echo '$(srcdir)/'`src/common/dwarf/elf_reader.cc + +src/common/dwarf/tools_linux_dump_syms_dump_syms-elf_reader.obj: src/common/dwarf/elf_reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/tools_linux_dump_syms_dump_syms-elf_reader.obj -MD -MP -MF src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-elf_reader.Tpo -c -o src/common/dwarf/tools_linux_dump_syms_dump_syms-elf_reader.obj `if test -f 'src/common/dwarf/elf_reader.cc'; then $(CYGPATH_W) 'src/common/dwarf/elf_reader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/elf_reader.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-elf_reader.Tpo src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-elf_reader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/elf_reader.cc' object='src/common/dwarf/tools_linux_dump_syms_dump_syms-elf_reader.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/tools_linux_dump_syms_dump_syms-elf_reader.obj `if test -f 'src/common/dwarf/elf_reader.cc'; then $(CYGPATH_W) 'src/common/dwarf/elf_reader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/elf_reader.cc'; fi` + +src/common/linux/tools_linux_dump_syms_dump_syms-crc32.o: src/common/linux/crc32.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/tools_linux_dump_syms_dump_syms-crc32.o -MD -MP -MF src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-crc32.Tpo -c -o src/common/linux/tools_linux_dump_syms_dump_syms-crc32.o `test -f 'src/common/linux/crc32.cc' || echo '$(srcdir)/'`src/common/linux/crc32.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-crc32.Tpo src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-crc32.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/crc32.cc' object='src/common/linux/tools_linux_dump_syms_dump_syms-crc32.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/tools_linux_dump_syms_dump_syms-crc32.o `test -f 'src/common/linux/crc32.cc' || echo '$(srcdir)/'`src/common/linux/crc32.cc + +src/common/linux/tools_linux_dump_syms_dump_syms-crc32.obj: src/common/linux/crc32.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/tools_linux_dump_syms_dump_syms-crc32.obj -MD -MP -MF src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-crc32.Tpo -c -o src/common/linux/tools_linux_dump_syms_dump_syms-crc32.obj `if test -f 'src/common/linux/crc32.cc'; then $(CYGPATH_W) 'src/common/linux/crc32.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/crc32.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-crc32.Tpo src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-crc32.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/crc32.cc' object='src/common/linux/tools_linux_dump_syms_dump_syms-crc32.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/tools_linux_dump_syms_dump_syms-crc32.obj `if test -f 'src/common/linux/crc32.cc'; then $(CYGPATH_W) 'src/common/linux/crc32.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/crc32.cc'; fi` + +src/common/linux/tools_linux_dump_syms_dump_syms-dump_symbols.o: src/common/linux/dump_symbols.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/tools_linux_dump_syms_dump_syms-dump_symbols.o -MD -MP -MF src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dump_symbols.Tpo -c -o src/common/linux/tools_linux_dump_syms_dump_syms-dump_symbols.o `test -f 'src/common/linux/dump_symbols.cc' || echo '$(srcdir)/'`src/common/linux/dump_symbols.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dump_symbols.Tpo src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dump_symbols.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/dump_symbols.cc' object='src/common/linux/tools_linux_dump_syms_dump_syms-dump_symbols.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/tools_linux_dump_syms_dump_syms-dump_symbols.o `test -f 'src/common/linux/dump_symbols.cc' || echo '$(srcdir)/'`src/common/linux/dump_symbols.cc + +src/common/linux/tools_linux_dump_syms_dump_syms-dump_symbols.obj: src/common/linux/dump_symbols.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/tools_linux_dump_syms_dump_syms-dump_symbols.obj -MD -MP -MF src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dump_symbols.Tpo -c -o src/common/linux/tools_linux_dump_syms_dump_syms-dump_symbols.obj `if test -f 'src/common/linux/dump_symbols.cc'; then $(CYGPATH_W) 'src/common/linux/dump_symbols.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/dump_symbols.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dump_symbols.Tpo src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dump_symbols.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/dump_symbols.cc' object='src/common/linux/tools_linux_dump_syms_dump_syms-dump_symbols.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/tools_linux_dump_syms_dump_syms-dump_symbols.obj `if test -f 'src/common/linux/dump_symbols.cc'; then $(CYGPATH_W) 'src/common/linux/dump_symbols.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/dump_symbols.cc'; fi` + +src/common/linux/tools_linux_dump_syms_dump_syms-elf_symbols_to_module.o: src/common/linux/elf_symbols_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/tools_linux_dump_syms_dump_syms-elf_symbols_to_module.o -MD -MP -MF src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-elf_symbols_to_module.Tpo -c -o src/common/linux/tools_linux_dump_syms_dump_syms-elf_symbols_to_module.o `test -f 'src/common/linux/elf_symbols_to_module.cc' || echo '$(srcdir)/'`src/common/linux/elf_symbols_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-elf_symbols_to_module.Tpo src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-elf_symbols_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/elf_symbols_to_module.cc' object='src/common/linux/tools_linux_dump_syms_dump_syms-elf_symbols_to_module.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/tools_linux_dump_syms_dump_syms-elf_symbols_to_module.o `test -f 'src/common/linux/elf_symbols_to_module.cc' || echo '$(srcdir)/'`src/common/linux/elf_symbols_to_module.cc + +src/common/linux/tools_linux_dump_syms_dump_syms-elf_symbols_to_module.obj: src/common/linux/elf_symbols_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/tools_linux_dump_syms_dump_syms-elf_symbols_to_module.obj -MD -MP -MF src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-elf_symbols_to_module.Tpo -c -o src/common/linux/tools_linux_dump_syms_dump_syms-elf_symbols_to_module.obj `if test -f 'src/common/linux/elf_symbols_to_module.cc'; then $(CYGPATH_W) 'src/common/linux/elf_symbols_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/elf_symbols_to_module.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-elf_symbols_to_module.Tpo src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-elf_symbols_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/elf_symbols_to_module.cc' object='src/common/linux/tools_linux_dump_syms_dump_syms-elf_symbols_to_module.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/tools_linux_dump_syms_dump_syms-elf_symbols_to_module.obj `if test -f 'src/common/linux/elf_symbols_to_module.cc'; then $(CYGPATH_W) 'src/common/linux/elf_symbols_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/elf_symbols_to_module.cc'; fi` + +src/common/linux/tools_linux_dump_syms_dump_syms-elfutils.o: src/common/linux/elfutils.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/tools_linux_dump_syms_dump_syms-elfutils.o -MD -MP -MF src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-elfutils.Tpo -c -o src/common/linux/tools_linux_dump_syms_dump_syms-elfutils.o `test -f 'src/common/linux/elfutils.cc' || echo '$(srcdir)/'`src/common/linux/elfutils.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-elfutils.Tpo src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-elfutils.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/elfutils.cc' object='src/common/linux/tools_linux_dump_syms_dump_syms-elfutils.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/tools_linux_dump_syms_dump_syms-elfutils.o `test -f 'src/common/linux/elfutils.cc' || echo '$(srcdir)/'`src/common/linux/elfutils.cc + +src/common/linux/tools_linux_dump_syms_dump_syms-elfutils.obj: src/common/linux/elfutils.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/tools_linux_dump_syms_dump_syms-elfutils.obj -MD -MP -MF src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-elfutils.Tpo -c -o src/common/linux/tools_linux_dump_syms_dump_syms-elfutils.obj `if test -f 'src/common/linux/elfutils.cc'; then $(CYGPATH_W) 'src/common/linux/elfutils.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/elfutils.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-elfutils.Tpo src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-elfutils.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/elfutils.cc' object='src/common/linux/tools_linux_dump_syms_dump_syms-elfutils.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/tools_linux_dump_syms_dump_syms-elfutils.obj `if test -f 'src/common/linux/elfutils.cc'; then $(CYGPATH_W) 'src/common/linux/elfutils.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/elfutils.cc'; fi` + +src/common/linux/tools_linux_dump_syms_dump_syms-file_id.o: src/common/linux/file_id.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/tools_linux_dump_syms_dump_syms-file_id.o -MD -MP -MF src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-file_id.Tpo -c -o src/common/linux/tools_linux_dump_syms_dump_syms-file_id.o `test -f 'src/common/linux/file_id.cc' || echo '$(srcdir)/'`src/common/linux/file_id.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-file_id.Tpo src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-file_id.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/file_id.cc' object='src/common/linux/tools_linux_dump_syms_dump_syms-file_id.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/tools_linux_dump_syms_dump_syms-file_id.o `test -f 'src/common/linux/file_id.cc' || echo '$(srcdir)/'`src/common/linux/file_id.cc + +src/common/linux/tools_linux_dump_syms_dump_syms-file_id.obj: src/common/linux/file_id.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/tools_linux_dump_syms_dump_syms-file_id.obj -MD -MP -MF src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-file_id.Tpo -c -o src/common/linux/tools_linux_dump_syms_dump_syms-file_id.obj `if test -f 'src/common/linux/file_id.cc'; then $(CYGPATH_W) 'src/common/linux/file_id.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/file_id.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-file_id.Tpo src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-file_id.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/file_id.cc' object='src/common/linux/tools_linux_dump_syms_dump_syms-file_id.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/tools_linux_dump_syms_dump_syms-file_id.obj `if test -f 'src/common/linux/file_id.cc'; then $(CYGPATH_W) 'src/common/linux/file_id.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/file_id.cc'; fi` + +src/common/linux/tools_linux_dump_syms_dump_syms-linux_libc_support.o: src/common/linux/linux_libc_support.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/tools_linux_dump_syms_dump_syms-linux_libc_support.o -MD -MP -MF src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-linux_libc_support.Tpo -c -o src/common/linux/tools_linux_dump_syms_dump_syms-linux_libc_support.o `test -f 'src/common/linux/linux_libc_support.cc' || echo '$(srcdir)/'`src/common/linux/linux_libc_support.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-linux_libc_support.Tpo src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-linux_libc_support.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/linux_libc_support.cc' object='src/common/linux/tools_linux_dump_syms_dump_syms-linux_libc_support.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/tools_linux_dump_syms_dump_syms-linux_libc_support.o `test -f 'src/common/linux/linux_libc_support.cc' || echo '$(srcdir)/'`src/common/linux/linux_libc_support.cc + +src/common/linux/tools_linux_dump_syms_dump_syms-linux_libc_support.obj: src/common/linux/linux_libc_support.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/tools_linux_dump_syms_dump_syms-linux_libc_support.obj -MD -MP -MF src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-linux_libc_support.Tpo -c -o src/common/linux/tools_linux_dump_syms_dump_syms-linux_libc_support.obj `if test -f 'src/common/linux/linux_libc_support.cc'; then $(CYGPATH_W) 'src/common/linux/linux_libc_support.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/linux_libc_support.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-linux_libc_support.Tpo src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-linux_libc_support.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/linux_libc_support.cc' object='src/common/linux/tools_linux_dump_syms_dump_syms-linux_libc_support.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/tools_linux_dump_syms_dump_syms-linux_libc_support.obj `if test -f 'src/common/linux/linux_libc_support.cc'; then $(CYGPATH_W) 'src/common/linux/linux_libc_support.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/linux_libc_support.cc'; fi` + +src/common/linux/tools_linux_dump_syms_dump_syms-memory_mapped_file.o: src/common/linux/memory_mapped_file.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/tools_linux_dump_syms_dump_syms-memory_mapped_file.o -MD -MP -MF src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-memory_mapped_file.Tpo -c -o src/common/linux/tools_linux_dump_syms_dump_syms-memory_mapped_file.o `test -f 'src/common/linux/memory_mapped_file.cc' || echo '$(srcdir)/'`src/common/linux/memory_mapped_file.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-memory_mapped_file.Tpo src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-memory_mapped_file.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/memory_mapped_file.cc' object='src/common/linux/tools_linux_dump_syms_dump_syms-memory_mapped_file.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/tools_linux_dump_syms_dump_syms-memory_mapped_file.o `test -f 'src/common/linux/memory_mapped_file.cc' || echo '$(srcdir)/'`src/common/linux/memory_mapped_file.cc + +src/common/linux/tools_linux_dump_syms_dump_syms-memory_mapped_file.obj: src/common/linux/memory_mapped_file.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/tools_linux_dump_syms_dump_syms-memory_mapped_file.obj -MD -MP -MF src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-memory_mapped_file.Tpo -c -o src/common/linux/tools_linux_dump_syms_dump_syms-memory_mapped_file.obj `if test -f 'src/common/linux/memory_mapped_file.cc'; then $(CYGPATH_W) 'src/common/linux/memory_mapped_file.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/memory_mapped_file.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-memory_mapped_file.Tpo src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-memory_mapped_file.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/memory_mapped_file.cc' object='src/common/linux/tools_linux_dump_syms_dump_syms-memory_mapped_file.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/tools_linux_dump_syms_dump_syms-memory_mapped_file.obj `if test -f 'src/common/linux/memory_mapped_file.cc'; then $(CYGPATH_W) 'src/common/linux/memory_mapped_file.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/memory_mapped_file.cc'; fi` + +src/common/linux/tools_linux_dump_syms_dump_syms-safe_readlink.o: src/common/linux/safe_readlink.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/tools_linux_dump_syms_dump_syms-safe_readlink.o -MD -MP -MF src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-safe_readlink.Tpo -c -o src/common/linux/tools_linux_dump_syms_dump_syms-safe_readlink.o `test -f 'src/common/linux/safe_readlink.cc' || echo '$(srcdir)/'`src/common/linux/safe_readlink.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-safe_readlink.Tpo src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-safe_readlink.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/safe_readlink.cc' object='src/common/linux/tools_linux_dump_syms_dump_syms-safe_readlink.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/tools_linux_dump_syms_dump_syms-safe_readlink.o `test -f 'src/common/linux/safe_readlink.cc' || echo '$(srcdir)/'`src/common/linux/safe_readlink.cc + +src/common/linux/tools_linux_dump_syms_dump_syms-safe_readlink.obj: src/common/linux/safe_readlink.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/common/linux/tools_linux_dump_syms_dump_syms-safe_readlink.obj -MD -MP -MF src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-safe_readlink.Tpo -c -o src/common/linux/tools_linux_dump_syms_dump_syms-safe_readlink.obj `if test -f 'src/common/linux/safe_readlink.cc'; then $(CYGPATH_W) 'src/common/linux/safe_readlink.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/safe_readlink.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-safe_readlink.Tpo src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-safe_readlink.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/linux/safe_readlink.cc' object='src/common/linux/tools_linux_dump_syms_dump_syms-safe_readlink.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/common/linux/tools_linux_dump_syms_dump_syms-safe_readlink.obj `if test -f 'src/common/linux/safe_readlink.cc'; then $(CYGPATH_W) 'src/common/linux/safe_readlink.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/linux/safe_readlink.cc'; fi` + +src/tools/linux/dump_syms/dump_syms-dump_syms.o: src/tools/linux/dump_syms/dump_syms.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/tools/linux/dump_syms/dump_syms-dump_syms.o -MD -MP -MF src/tools/linux/dump_syms/$(DEPDIR)/dump_syms-dump_syms.Tpo -c -o src/tools/linux/dump_syms/dump_syms-dump_syms.o `test -f 'src/tools/linux/dump_syms/dump_syms.cc' || echo '$(srcdir)/'`src/tools/linux/dump_syms/dump_syms.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/tools/linux/dump_syms/$(DEPDIR)/dump_syms-dump_syms.Tpo src/tools/linux/dump_syms/$(DEPDIR)/dump_syms-dump_syms.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/tools/linux/dump_syms/dump_syms.cc' object='src/tools/linux/dump_syms/dump_syms-dump_syms.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/tools/linux/dump_syms/dump_syms-dump_syms.o `test -f 'src/tools/linux/dump_syms/dump_syms.cc' || echo '$(srcdir)/'`src/tools/linux/dump_syms/dump_syms.cc + +src/tools/linux/dump_syms/dump_syms-dump_syms.obj: src/tools/linux/dump_syms/dump_syms.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -MT src/tools/linux/dump_syms/dump_syms-dump_syms.obj -MD -MP -MF src/tools/linux/dump_syms/$(DEPDIR)/dump_syms-dump_syms.Tpo -c -o src/tools/linux/dump_syms/dump_syms-dump_syms.obj `if test -f 'src/tools/linux/dump_syms/dump_syms.cc'; then $(CYGPATH_W) 'src/tools/linux/dump_syms/dump_syms.cc'; else $(CYGPATH_W) '$(srcdir)/src/tools/linux/dump_syms/dump_syms.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/tools/linux/dump_syms/$(DEPDIR)/dump_syms-dump_syms.Tpo src/tools/linux/dump_syms/$(DEPDIR)/dump_syms-dump_syms.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/tools/linux/dump_syms/dump_syms.cc' object='src/tools/linux/dump_syms/dump_syms-dump_syms.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_linux_dump_syms_dump_syms_CXXFLAGS) $(CXXFLAGS) -c -o src/tools/linux/dump_syms/dump_syms-dump_syms.obj `if test -f 'src/tools/linux/dump_syms/dump_syms.cc'; then $(CYGPATH_W) 'src/tools/linux/dump_syms/dump_syms.cc'; else $(CYGPATH_W) '$(srcdir)/src/tools/linux/dump_syms/dump_syms.cc'; fi` + +src/tools/linux/md2core/minidump_2_core_unittest-minidump_memory_range_unittest.o: src/tools/linux/md2core/minidump_memory_range_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_tools_linux_md2core_minidump_2_core_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/tools/linux/md2core/minidump_2_core_unittest-minidump_memory_range_unittest.o -MD -MP -MF src/tools/linux/md2core/$(DEPDIR)/minidump_2_core_unittest-minidump_memory_range_unittest.Tpo -c -o src/tools/linux/md2core/minidump_2_core_unittest-minidump_memory_range_unittest.o `test -f 'src/tools/linux/md2core/minidump_memory_range_unittest.cc' || echo '$(srcdir)/'`src/tools/linux/md2core/minidump_memory_range_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/tools/linux/md2core/$(DEPDIR)/minidump_2_core_unittest-minidump_memory_range_unittest.Tpo src/tools/linux/md2core/$(DEPDIR)/minidump_2_core_unittest-minidump_memory_range_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/tools/linux/md2core/minidump_memory_range_unittest.cc' object='src/tools/linux/md2core/minidump_2_core_unittest-minidump_memory_range_unittest.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_tools_linux_md2core_minidump_2_core_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/tools/linux/md2core/minidump_2_core_unittest-minidump_memory_range_unittest.o `test -f 'src/tools/linux/md2core/minidump_memory_range_unittest.cc' || echo '$(srcdir)/'`src/tools/linux/md2core/minidump_memory_range_unittest.cc + +src/tools/linux/md2core/minidump_2_core_unittest-minidump_memory_range_unittest.obj: src/tools/linux/md2core/minidump_memory_range_unittest.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_tools_linux_md2core_minidump_2_core_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -MT src/tools/linux/md2core/minidump_2_core_unittest-minidump_memory_range_unittest.obj -MD -MP -MF src/tools/linux/md2core/$(DEPDIR)/minidump_2_core_unittest-minidump_memory_range_unittest.Tpo -c -o src/tools/linux/md2core/minidump_2_core_unittest-minidump_memory_range_unittest.obj `if test -f 'src/tools/linux/md2core/minidump_memory_range_unittest.cc'; then $(CYGPATH_W) 'src/tools/linux/md2core/minidump_memory_range_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/tools/linux/md2core/minidump_memory_range_unittest.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/tools/linux/md2core/$(DEPDIR)/minidump_2_core_unittest-minidump_memory_range_unittest.Tpo src/tools/linux/md2core/$(DEPDIR)/minidump_2_core_unittest-minidump_memory_range_unittest.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/tools/linux/md2core/minidump_memory_range_unittest.cc' object='src/tools/linux/md2core/minidump_2_core_unittest-minidump_memory_range_unittest.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(src_tools_linux_md2core_minidump_2_core_unittest_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS) -c -o src/tools/linux/md2core/minidump_2_core_unittest-minidump_memory_range_unittest.obj `if test -f 'src/tools/linux/md2core/minidump_memory_range_unittest.cc'; then $(CYGPATH_W) 'src/tools/linux/md2core/minidump_memory_range_unittest.cc'; else $(CYGPATH_W) '$(srcdir)/src/tools/linux/md2core/minidump_memory_range_unittest.cc'; fi` + +src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_cfi_to_module.o: src/common/dwarf_cfi_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_cfi_to_module.o -MD -MP -MF src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_cfi_to_module.Tpo -c -o src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_cfi_to_module.o `test -f 'src/common/dwarf_cfi_to_module.cc' || echo '$(srcdir)/'`src/common/dwarf_cfi_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_cfi_to_module.Tpo src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_cfi_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_cfi_to_module.cc' object='src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_cfi_to_module.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_cfi_to_module.o `test -f 'src/common/dwarf_cfi_to_module.cc' || echo '$(srcdir)/'`src/common/dwarf_cfi_to_module.cc + +src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_cfi_to_module.obj: src/common/dwarf_cfi_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_cfi_to_module.obj -MD -MP -MF src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_cfi_to_module.Tpo -c -o src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_cfi_to_module.obj `if test -f 'src/common/dwarf_cfi_to_module.cc'; then $(CYGPATH_W) 'src/common/dwarf_cfi_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_cfi_to_module.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_cfi_to_module.Tpo src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_cfi_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_cfi_to_module.cc' object='src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_cfi_to_module.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_cfi_to_module.obj `if test -f 'src/common/dwarf_cfi_to_module.cc'; then $(CYGPATH_W) 'src/common/dwarf_cfi_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_cfi_to_module.cc'; fi` + +src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_cu_to_module.o: src/common/dwarf_cu_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_cu_to_module.o -MD -MP -MF src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_cu_to_module.Tpo -c -o src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_cu_to_module.o `test -f 'src/common/dwarf_cu_to_module.cc' || echo '$(srcdir)/'`src/common/dwarf_cu_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_cu_to_module.Tpo src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_cu_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_cu_to_module.cc' object='src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_cu_to_module.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_cu_to_module.o `test -f 'src/common/dwarf_cu_to_module.cc' || echo '$(srcdir)/'`src/common/dwarf_cu_to_module.cc + +src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_cu_to_module.obj: src/common/dwarf_cu_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_cu_to_module.obj -MD -MP -MF src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_cu_to_module.Tpo -c -o src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_cu_to_module.obj `if test -f 'src/common/dwarf_cu_to_module.cc'; then $(CYGPATH_W) 'src/common/dwarf_cu_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_cu_to_module.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_cu_to_module.Tpo src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_cu_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_cu_to_module.cc' object='src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_cu_to_module.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_cu_to_module.obj `if test -f 'src/common/dwarf_cu_to_module.cc'; then $(CYGPATH_W) 'src/common/dwarf_cu_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_cu_to_module.cc'; fi` + +src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_line_to_module.o: src/common/dwarf_line_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_line_to_module.o -MD -MP -MF src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_line_to_module.Tpo -c -o src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_line_to_module.o `test -f 'src/common/dwarf_line_to_module.cc' || echo '$(srcdir)/'`src/common/dwarf_line_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_line_to_module.Tpo src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_line_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_line_to_module.cc' object='src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_line_to_module.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_line_to_module.o `test -f 'src/common/dwarf_line_to_module.cc' || echo '$(srcdir)/'`src/common/dwarf_line_to_module.cc + +src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_line_to_module.obj: src/common/dwarf_line_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_line_to_module.obj -MD -MP -MF src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_line_to_module.Tpo -c -o src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_line_to_module.obj `if test -f 'src/common/dwarf_line_to_module.cc'; then $(CYGPATH_W) 'src/common/dwarf_line_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_line_to_module.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_line_to_module.Tpo src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_line_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_line_to_module.cc' object='src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_line_to_module.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_line_to_module.obj `if test -f 'src/common/dwarf_line_to_module.cc'; then $(CYGPATH_W) 'src/common/dwarf_line_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_line_to_module.cc'; fi` + +src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_range_list_handler.o: src/common/dwarf_range_list_handler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_range_list_handler.o -MD -MP -MF src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_range_list_handler.Tpo -c -o src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_range_list_handler.o `test -f 'src/common/dwarf_range_list_handler.cc' || echo '$(srcdir)/'`src/common/dwarf_range_list_handler.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_range_list_handler.Tpo src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_range_list_handler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_range_list_handler.cc' object='src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_range_list_handler.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_range_list_handler.o `test -f 'src/common/dwarf_range_list_handler.cc' || echo '$(srcdir)/'`src/common/dwarf_range_list_handler.cc + +src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_range_list_handler.obj: src/common/dwarf_range_list_handler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_range_list_handler.obj -MD -MP -MF src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_range_list_handler.Tpo -c -o src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_range_list_handler.obj `if test -f 'src/common/dwarf_range_list_handler.cc'; then $(CYGPATH_W) 'src/common/dwarf_range_list_handler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_range_list_handler.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_range_list_handler.Tpo src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_range_list_handler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf_range_list_handler.cc' object='src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_range_list_handler.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_mac_dump_syms_dump_syms_mac-dwarf_range_list_handler.obj `if test -f 'src/common/dwarf_range_list_handler.cc'; then $(CYGPATH_W) 'src/common/dwarf_range_list_handler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf_range_list_handler.cc'; fi` + +src/common/tools_mac_dump_syms_dump_syms_mac-language.o: src/common/language.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_mac_dump_syms_dump_syms_mac-language.o -MD -MP -MF src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-language.Tpo -c -o src/common/tools_mac_dump_syms_dump_syms_mac-language.o `test -f 'src/common/language.cc' || echo '$(srcdir)/'`src/common/language.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-language.Tpo src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-language.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/language.cc' object='src/common/tools_mac_dump_syms_dump_syms_mac-language.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_mac_dump_syms_dump_syms_mac-language.o `test -f 'src/common/language.cc' || echo '$(srcdir)/'`src/common/language.cc + +src/common/tools_mac_dump_syms_dump_syms_mac-language.obj: src/common/language.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_mac_dump_syms_dump_syms_mac-language.obj -MD -MP -MF src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-language.Tpo -c -o src/common/tools_mac_dump_syms_dump_syms_mac-language.obj `if test -f 'src/common/language.cc'; then $(CYGPATH_W) 'src/common/language.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/language.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-language.Tpo src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-language.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/language.cc' object='src/common/tools_mac_dump_syms_dump_syms_mac-language.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_mac_dump_syms_dump_syms_mac-language.obj `if test -f 'src/common/language.cc'; then $(CYGPATH_W) 'src/common/language.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/language.cc'; fi` + +src/common/tools_mac_dump_syms_dump_syms_mac-md5.o: src/common/md5.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_mac_dump_syms_dump_syms_mac-md5.o -MD -MP -MF src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-md5.Tpo -c -o src/common/tools_mac_dump_syms_dump_syms_mac-md5.o `test -f 'src/common/md5.cc' || echo '$(srcdir)/'`src/common/md5.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-md5.Tpo src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-md5.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/md5.cc' object='src/common/tools_mac_dump_syms_dump_syms_mac-md5.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_mac_dump_syms_dump_syms_mac-md5.o `test -f 'src/common/md5.cc' || echo '$(srcdir)/'`src/common/md5.cc + +src/common/tools_mac_dump_syms_dump_syms_mac-md5.obj: src/common/md5.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_mac_dump_syms_dump_syms_mac-md5.obj -MD -MP -MF src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-md5.Tpo -c -o src/common/tools_mac_dump_syms_dump_syms_mac-md5.obj `if test -f 'src/common/md5.cc'; then $(CYGPATH_W) 'src/common/md5.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/md5.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-md5.Tpo src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-md5.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/md5.cc' object='src/common/tools_mac_dump_syms_dump_syms_mac-md5.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_mac_dump_syms_dump_syms_mac-md5.obj `if test -f 'src/common/md5.cc'; then $(CYGPATH_W) 'src/common/md5.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/md5.cc'; fi` + +src/common/tools_mac_dump_syms_dump_syms_mac-module.o: src/common/module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_mac_dump_syms_dump_syms_mac-module.o -MD -MP -MF src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-module.Tpo -c -o src/common/tools_mac_dump_syms_dump_syms_mac-module.o `test -f 'src/common/module.cc' || echo '$(srcdir)/'`src/common/module.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-module.Tpo src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/module.cc' object='src/common/tools_mac_dump_syms_dump_syms_mac-module.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_mac_dump_syms_dump_syms_mac-module.o `test -f 'src/common/module.cc' || echo '$(srcdir)/'`src/common/module.cc + +src/common/tools_mac_dump_syms_dump_syms_mac-module.obj: src/common/module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_mac_dump_syms_dump_syms_mac-module.obj -MD -MP -MF src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-module.Tpo -c -o src/common/tools_mac_dump_syms_dump_syms_mac-module.obj `if test -f 'src/common/module.cc'; then $(CYGPATH_W) 'src/common/module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/module.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-module.Tpo src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/module.cc' object='src/common/tools_mac_dump_syms_dump_syms_mac-module.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_mac_dump_syms_dump_syms_mac-module.obj `if test -f 'src/common/module.cc'; then $(CYGPATH_W) 'src/common/module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/module.cc'; fi` + +src/common/tools_mac_dump_syms_dump_syms_mac-path_helper.o: src/common/path_helper.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_mac_dump_syms_dump_syms_mac-path_helper.o -MD -MP -MF src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-path_helper.Tpo -c -o src/common/tools_mac_dump_syms_dump_syms_mac-path_helper.o `test -f 'src/common/path_helper.cc' || echo '$(srcdir)/'`src/common/path_helper.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-path_helper.Tpo src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-path_helper.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/path_helper.cc' object='src/common/tools_mac_dump_syms_dump_syms_mac-path_helper.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_mac_dump_syms_dump_syms_mac-path_helper.o `test -f 'src/common/path_helper.cc' || echo '$(srcdir)/'`src/common/path_helper.cc + +src/common/tools_mac_dump_syms_dump_syms_mac-path_helper.obj: src/common/path_helper.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_mac_dump_syms_dump_syms_mac-path_helper.obj -MD -MP -MF src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-path_helper.Tpo -c -o src/common/tools_mac_dump_syms_dump_syms_mac-path_helper.obj `if test -f 'src/common/path_helper.cc'; then $(CYGPATH_W) 'src/common/path_helper.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/path_helper.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-path_helper.Tpo src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-path_helper.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/path_helper.cc' object='src/common/tools_mac_dump_syms_dump_syms_mac-path_helper.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_mac_dump_syms_dump_syms_mac-path_helper.obj `if test -f 'src/common/path_helper.cc'; then $(CYGPATH_W) 'src/common/path_helper.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/path_helper.cc'; fi` + +src/common/tools_mac_dump_syms_dump_syms_mac-stabs_reader.o: src/common/stabs_reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_mac_dump_syms_dump_syms_mac-stabs_reader.o -MD -MP -MF src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-stabs_reader.Tpo -c -o src/common/tools_mac_dump_syms_dump_syms_mac-stabs_reader.o `test -f 'src/common/stabs_reader.cc' || echo '$(srcdir)/'`src/common/stabs_reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-stabs_reader.Tpo src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-stabs_reader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/stabs_reader.cc' object='src/common/tools_mac_dump_syms_dump_syms_mac-stabs_reader.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_mac_dump_syms_dump_syms_mac-stabs_reader.o `test -f 'src/common/stabs_reader.cc' || echo '$(srcdir)/'`src/common/stabs_reader.cc + +src/common/tools_mac_dump_syms_dump_syms_mac-stabs_reader.obj: src/common/stabs_reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_mac_dump_syms_dump_syms_mac-stabs_reader.obj -MD -MP -MF src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-stabs_reader.Tpo -c -o src/common/tools_mac_dump_syms_dump_syms_mac-stabs_reader.obj `if test -f 'src/common/stabs_reader.cc'; then $(CYGPATH_W) 'src/common/stabs_reader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/stabs_reader.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-stabs_reader.Tpo src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-stabs_reader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/stabs_reader.cc' object='src/common/tools_mac_dump_syms_dump_syms_mac-stabs_reader.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_mac_dump_syms_dump_syms_mac-stabs_reader.obj `if test -f 'src/common/stabs_reader.cc'; then $(CYGPATH_W) 'src/common/stabs_reader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/stabs_reader.cc'; fi` + +src/common/tools_mac_dump_syms_dump_syms_mac-stabs_to_module.o: src/common/stabs_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_mac_dump_syms_dump_syms_mac-stabs_to_module.o -MD -MP -MF src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-stabs_to_module.Tpo -c -o src/common/tools_mac_dump_syms_dump_syms_mac-stabs_to_module.o `test -f 'src/common/stabs_to_module.cc' || echo '$(srcdir)/'`src/common/stabs_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-stabs_to_module.Tpo src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-stabs_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/stabs_to_module.cc' object='src/common/tools_mac_dump_syms_dump_syms_mac-stabs_to_module.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_mac_dump_syms_dump_syms_mac-stabs_to_module.o `test -f 'src/common/stabs_to_module.cc' || echo '$(srcdir)/'`src/common/stabs_to_module.cc + +src/common/tools_mac_dump_syms_dump_syms_mac-stabs_to_module.obj: src/common/stabs_to_module.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/tools_mac_dump_syms_dump_syms_mac-stabs_to_module.obj -MD -MP -MF src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-stabs_to_module.Tpo -c -o src/common/tools_mac_dump_syms_dump_syms_mac-stabs_to_module.obj `if test -f 'src/common/stabs_to_module.cc'; then $(CYGPATH_W) 'src/common/stabs_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/stabs_to_module.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-stabs_to_module.Tpo src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-stabs_to_module.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/stabs_to_module.cc' object='src/common/tools_mac_dump_syms_dump_syms_mac-stabs_to_module.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/tools_mac_dump_syms_dump_syms_mac-stabs_to_module.obj `if test -f 'src/common/stabs_to_module.cc'; then $(CYGPATH_W) 'src/common/stabs_to_module.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/stabs_to_module.cc'; fi` + +src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-bytereader.o: src/common/dwarf/bytereader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-bytereader.o -MD -MP -MF src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-bytereader.Tpo -c -o src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-bytereader.o `test -f 'src/common/dwarf/bytereader.cc' || echo '$(srcdir)/'`src/common/dwarf/bytereader.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-bytereader.Tpo src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-bytereader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/bytereader.cc' object='src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-bytereader.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-bytereader.o `test -f 'src/common/dwarf/bytereader.cc' || echo '$(srcdir)/'`src/common/dwarf/bytereader.cc + +src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-bytereader.obj: src/common/dwarf/bytereader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-bytereader.obj -MD -MP -MF src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-bytereader.Tpo -c -o src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-bytereader.obj `if test -f 'src/common/dwarf/bytereader.cc'; then $(CYGPATH_W) 'src/common/dwarf/bytereader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/bytereader.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-bytereader.Tpo src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-bytereader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/bytereader.cc' object='src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-bytereader.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-bytereader.obj `if test -f 'src/common/dwarf/bytereader.cc'; then $(CYGPATH_W) 'src/common/dwarf/bytereader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/bytereader.cc'; fi` + +src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-dwarf2diehandler.o: src/common/dwarf/dwarf2diehandler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-dwarf2diehandler.o -MD -MP -MF src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf2diehandler.Tpo -c -o src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-dwarf2diehandler.o `test -f 'src/common/dwarf/dwarf2diehandler.cc' || echo '$(srcdir)/'`src/common/dwarf/dwarf2diehandler.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf2diehandler.Tpo src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf2diehandler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/dwarf2diehandler.cc' object='src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-dwarf2diehandler.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-dwarf2diehandler.o `test -f 'src/common/dwarf/dwarf2diehandler.cc' || echo '$(srcdir)/'`src/common/dwarf/dwarf2diehandler.cc + +src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-dwarf2diehandler.obj: src/common/dwarf/dwarf2diehandler.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-dwarf2diehandler.obj -MD -MP -MF src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf2diehandler.Tpo -c -o src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-dwarf2diehandler.obj `if test -f 'src/common/dwarf/dwarf2diehandler.cc'; then $(CYGPATH_W) 'src/common/dwarf/dwarf2diehandler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/dwarf2diehandler.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf2diehandler.Tpo src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf2diehandler.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/dwarf2diehandler.cc' object='src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-dwarf2diehandler.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-dwarf2diehandler.obj `if test -f 'src/common/dwarf/dwarf2diehandler.cc'; then $(CYGPATH_W) 'src/common/dwarf/dwarf2diehandler.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/dwarf2diehandler.cc'; fi` + +src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-dwarf2reader.o: src/common/dwarf/dwarf2reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-dwarf2reader.o -MD -MP -MF src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf2reader.Tpo -c -o src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-dwarf2reader.o `test -f 'src/common/dwarf/dwarf2reader.cc' || echo '$(srcdir)/'`src/common/dwarf/dwarf2reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf2reader.Tpo src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf2reader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/dwarf2reader.cc' object='src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-dwarf2reader.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-dwarf2reader.o `test -f 'src/common/dwarf/dwarf2reader.cc' || echo '$(srcdir)/'`src/common/dwarf/dwarf2reader.cc + +src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-dwarf2reader.obj: src/common/dwarf/dwarf2reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-dwarf2reader.obj -MD -MP -MF src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf2reader.Tpo -c -o src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-dwarf2reader.obj `if test -f 'src/common/dwarf/dwarf2reader.cc'; then $(CYGPATH_W) 'src/common/dwarf/dwarf2reader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/dwarf2reader.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf2reader.Tpo src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf2reader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/dwarf2reader.cc' object='src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-dwarf2reader.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-dwarf2reader.obj `if test -f 'src/common/dwarf/dwarf2reader.cc'; then $(CYGPATH_W) 'src/common/dwarf/dwarf2reader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/dwarf2reader.cc'; fi` + +src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-elf_reader.o: src/common/dwarf/elf_reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-elf_reader.o -MD -MP -MF src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-elf_reader.Tpo -c -o src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-elf_reader.o `test -f 'src/common/dwarf/elf_reader.cc' || echo '$(srcdir)/'`src/common/dwarf/elf_reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-elf_reader.Tpo src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-elf_reader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/elf_reader.cc' object='src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-elf_reader.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-elf_reader.o `test -f 'src/common/dwarf/elf_reader.cc' || echo '$(srcdir)/'`src/common/dwarf/elf_reader.cc + +src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-elf_reader.obj: src/common/dwarf/elf_reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-elf_reader.obj -MD -MP -MF src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-elf_reader.Tpo -c -o src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-elf_reader.obj `if test -f 'src/common/dwarf/elf_reader.cc'; then $(CYGPATH_W) 'src/common/dwarf/elf_reader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/elf_reader.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-elf_reader.Tpo src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-elf_reader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/dwarf/elf_reader.cc' object='src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-elf_reader.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/dwarf/tools_mac_dump_syms_dump_syms_mac-elf_reader.obj `if test -f 'src/common/dwarf/elf_reader.cc'; then $(CYGPATH_W) 'src/common/dwarf/elf_reader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/dwarf/elf_reader.cc'; fi` + +src/common/mac/tools_mac_dump_syms_dump_syms_mac-arch_utilities.o: src/common/mac/arch_utilities.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac/tools_mac_dump_syms_dump_syms_mac-arch_utilities.o -MD -MP -MF src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-arch_utilities.Tpo -c -o src/common/mac/tools_mac_dump_syms_dump_syms_mac-arch_utilities.o `test -f 'src/common/mac/arch_utilities.cc' || echo '$(srcdir)/'`src/common/mac/arch_utilities.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-arch_utilities.Tpo src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-arch_utilities.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/mac/arch_utilities.cc' object='src/common/mac/tools_mac_dump_syms_dump_syms_mac-arch_utilities.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac/tools_mac_dump_syms_dump_syms_mac-arch_utilities.o `test -f 'src/common/mac/arch_utilities.cc' || echo '$(srcdir)/'`src/common/mac/arch_utilities.cc + +src/common/mac/tools_mac_dump_syms_dump_syms_mac-arch_utilities.obj: src/common/mac/arch_utilities.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac/tools_mac_dump_syms_dump_syms_mac-arch_utilities.obj -MD -MP -MF src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-arch_utilities.Tpo -c -o src/common/mac/tools_mac_dump_syms_dump_syms_mac-arch_utilities.obj `if test -f 'src/common/mac/arch_utilities.cc'; then $(CYGPATH_W) 'src/common/mac/arch_utilities.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/mac/arch_utilities.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-arch_utilities.Tpo src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-arch_utilities.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/mac/arch_utilities.cc' object='src/common/mac/tools_mac_dump_syms_dump_syms_mac-arch_utilities.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac/tools_mac_dump_syms_dump_syms_mac-arch_utilities.obj `if test -f 'src/common/mac/arch_utilities.cc'; then $(CYGPATH_W) 'src/common/mac/arch_utilities.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/mac/arch_utilities.cc'; fi` + +src/common/mac/tools_mac_dump_syms_dump_syms_mac-dump_syms.o: src/common/mac/dump_syms.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac/tools_mac_dump_syms_dump_syms_mac-dump_syms.o -MD -MP -MF src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dump_syms.Tpo -c -o src/common/mac/tools_mac_dump_syms_dump_syms_mac-dump_syms.o `test -f 'src/common/mac/dump_syms.cc' || echo '$(srcdir)/'`src/common/mac/dump_syms.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dump_syms.Tpo src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dump_syms.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/mac/dump_syms.cc' object='src/common/mac/tools_mac_dump_syms_dump_syms_mac-dump_syms.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac/tools_mac_dump_syms_dump_syms_mac-dump_syms.o `test -f 'src/common/mac/dump_syms.cc' || echo '$(srcdir)/'`src/common/mac/dump_syms.cc + +src/common/mac/tools_mac_dump_syms_dump_syms_mac-dump_syms.obj: src/common/mac/dump_syms.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac/tools_mac_dump_syms_dump_syms_mac-dump_syms.obj -MD -MP -MF src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dump_syms.Tpo -c -o src/common/mac/tools_mac_dump_syms_dump_syms_mac-dump_syms.obj `if test -f 'src/common/mac/dump_syms.cc'; then $(CYGPATH_W) 'src/common/mac/dump_syms.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/mac/dump_syms.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dump_syms.Tpo src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dump_syms.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/mac/dump_syms.cc' object='src/common/mac/tools_mac_dump_syms_dump_syms_mac-dump_syms.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac/tools_mac_dump_syms_dump_syms_mac-dump_syms.obj `if test -f 'src/common/mac/dump_syms.cc'; then $(CYGPATH_W) 'src/common/mac/dump_syms.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/mac/dump_syms.cc'; fi` + +src/common/mac/tools_mac_dump_syms_dump_syms_mac-file_id.o: src/common/mac/file_id.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac/tools_mac_dump_syms_dump_syms_mac-file_id.o -MD -MP -MF src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-file_id.Tpo -c -o src/common/mac/tools_mac_dump_syms_dump_syms_mac-file_id.o `test -f 'src/common/mac/file_id.cc' || echo '$(srcdir)/'`src/common/mac/file_id.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-file_id.Tpo src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-file_id.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/mac/file_id.cc' object='src/common/mac/tools_mac_dump_syms_dump_syms_mac-file_id.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac/tools_mac_dump_syms_dump_syms_mac-file_id.o `test -f 'src/common/mac/file_id.cc' || echo '$(srcdir)/'`src/common/mac/file_id.cc + +src/common/mac/tools_mac_dump_syms_dump_syms_mac-file_id.obj: src/common/mac/file_id.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac/tools_mac_dump_syms_dump_syms_mac-file_id.obj -MD -MP -MF src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-file_id.Tpo -c -o src/common/mac/tools_mac_dump_syms_dump_syms_mac-file_id.obj `if test -f 'src/common/mac/file_id.cc'; then $(CYGPATH_W) 'src/common/mac/file_id.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/mac/file_id.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-file_id.Tpo src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-file_id.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/mac/file_id.cc' object='src/common/mac/tools_mac_dump_syms_dump_syms_mac-file_id.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac/tools_mac_dump_syms_dump_syms_mac-file_id.obj `if test -f 'src/common/mac/file_id.cc'; then $(CYGPATH_W) 'src/common/mac/file_id.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/mac/file_id.cc'; fi` + +src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_id.o: src/common/mac/macho_id.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_id.o -MD -MP -MF src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_id.Tpo -c -o src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_id.o `test -f 'src/common/mac/macho_id.cc' || echo '$(srcdir)/'`src/common/mac/macho_id.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_id.Tpo src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_id.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/mac/macho_id.cc' object='src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_id.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_id.o `test -f 'src/common/mac/macho_id.cc' || echo '$(srcdir)/'`src/common/mac/macho_id.cc + +src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_id.obj: src/common/mac/macho_id.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_id.obj -MD -MP -MF src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_id.Tpo -c -o src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_id.obj `if test -f 'src/common/mac/macho_id.cc'; then $(CYGPATH_W) 'src/common/mac/macho_id.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/mac/macho_id.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_id.Tpo src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_id.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/mac/macho_id.cc' object='src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_id.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_id.obj `if test -f 'src/common/mac/macho_id.cc'; then $(CYGPATH_W) 'src/common/mac/macho_id.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/mac/macho_id.cc'; fi` + +src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_reader.o: src/common/mac/macho_reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_reader.o -MD -MP -MF src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_reader.Tpo -c -o src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_reader.o `test -f 'src/common/mac/macho_reader.cc' || echo '$(srcdir)/'`src/common/mac/macho_reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_reader.Tpo src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_reader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/mac/macho_reader.cc' object='src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_reader.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_reader.o `test -f 'src/common/mac/macho_reader.cc' || echo '$(srcdir)/'`src/common/mac/macho_reader.cc + +src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_reader.obj: src/common/mac/macho_reader.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_reader.obj -MD -MP -MF src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_reader.Tpo -c -o src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_reader.obj `if test -f 'src/common/mac/macho_reader.cc'; then $(CYGPATH_W) 'src/common/mac/macho_reader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/mac/macho_reader.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_reader.Tpo src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_reader.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/mac/macho_reader.cc' object='src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_reader.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_reader.obj `if test -f 'src/common/mac/macho_reader.cc'; then $(CYGPATH_W) 'src/common/mac/macho_reader.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/mac/macho_reader.cc'; fi` + +src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_utilities.o: src/common/mac/macho_utilities.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_utilities.o -MD -MP -MF src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_utilities.Tpo -c -o src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_utilities.o `test -f 'src/common/mac/macho_utilities.cc' || echo '$(srcdir)/'`src/common/mac/macho_utilities.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_utilities.Tpo src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_utilities.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/mac/macho_utilities.cc' object='src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_utilities.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_utilities.o `test -f 'src/common/mac/macho_utilities.cc' || echo '$(srcdir)/'`src/common/mac/macho_utilities.cc + +src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_utilities.obj: src/common/mac/macho_utilities.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_utilities.obj -MD -MP -MF src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_utilities.Tpo -c -o src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_utilities.obj `if test -f 'src/common/mac/macho_utilities.cc'; then $(CYGPATH_W) 'src/common/mac/macho_utilities.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/mac/macho_utilities.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_utilities.Tpo src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_utilities.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/mac/macho_utilities.cc' object='src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_utilities.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_utilities.obj `if test -f 'src/common/mac/macho_utilities.cc'; then $(CYGPATH_W) 'src/common/mac/macho_utilities.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/mac/macho_utilities.cc'; fi` + +src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_walker.o: src/common/mac/macho_walker.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_walker.o -MD -MP -MF src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_walker.Tpo -c -o src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_walker.o `test -f 'src/common/mac/macho_walker.cc' || echo '$(srcdir)/'`src/common/mac/macho_walker.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_walker.Tpo src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_walker.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/mac/macho_walker.cc' object='src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_walker.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_walker.o `test -f 'src/common/mac/macho_walker.cc' || echo '$(srcdir)/'`src/common/mac/macho_walker.cc + +src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_walker.obj: src/common/mac/macho_walker.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_walker.obj -MD -MP -MF src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_walker.Tpo -c -o src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_walker.obj `if test -f 'src/common/mac/macho_walker.cc'; then $(CYGPATH_W) 'src/common/mac/macho_walker.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/mac/macho_walker.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_walker.Tpo src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_walker.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/common/mac/macho_walker.cc' object='src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_walker.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/common/mac/tools_mac_dump_syms_dump_syms_mac-macho_walker.obj `if test -f 'src/common/mac/macho_walker.cc'; then $(CYGPATH_W) 'src/common/mac/macho_walker.cc'; else $(CYGPATH_W) '$(srcdir)/src/common/mac/macho_walker.cc'; fi` + +src/tools/mac/dump_syms/dump_syms_mac-dump_syms_tool.o: src/tools/mac/dump_syms/dump_syms_tool.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/tools/mac/dump_syms/dump_syms_mac-dump_syms_tool.o -MD -MP -MF src/tools/mac/dump_syms/$(DEPDIR)/dump_syms_mac-dump_syms_tool.Tpo -c -o src/tools/mac/dump_syms/dump_syms_mac-dump_syms_tool.o `test -f 'src/tools/mac/dump_syms/dump_syms_tool.cc' || echo '$(srcdir)/'`src/tools/mac/dump_syms/dump_syms_tool.cc +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/tools/mac/dump_syms/$(DEPDIR)/dump_syms_mac-dump_syms_tool.Tpo src/tools/mac/dump_syms/$(DEPDIR)/dump_syms_mac-dump_syms_tool.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/tools/mac/dump_syms/dump_syms_tool.cc' object='src/tools/mac/dump_syms/dump_syms_mac-dump_syms_tool.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/tools/mac/dump_syms/dump_syms_mac-dump_syms_tool.o `test -f 'src/tools/mac/dump_syms/dump_syms_tool.cc' || echo '$(srcdir)/'`src/tools/mac/dump_syms/dump_syms_tool.cc + +src/tools/mac/dump_syms/dump_syms_mac-dump_syms_tool.obj: src/tools/mac/dump_syms/dump_syms_tool.cc +@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -MT src/tools/mac/dump_syms/dump_syms_mac-dump_syms_tool.obj -MD -MP -MF src/tools/mac/dump_syms/$(DEPDIR)/dump_syms_mac-dump_syms_tool.Tpo -c -o src/tools/mac/dump_syms/dump_syms_mac-dump_syms_tool.obj `if test -f 'src/tools/mac/dump_syms/dump_syms_tool.cc'; then $(CYGPATH_W) 'src/tools/mac/dump_syms/dump_syms_tool.cc'; else $(CYGPATH_W) '$(srcdir)/src/tools/mac/dump_syms/dump_syms_tool.cc'; fi` +@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) src/tools/mac/dump_syms/$(DEPDIR)/dump_syms_mac-dump_syms_tool.Tpo src/tools/mac/dump_syms/$(DEPDIR)/dump_syms_mac-dump_syms_tool.Po +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ $(AM_V_CXX)source='src/tools/mac/dump_syms/dump_syms_tool.cc' object='src/tools/mac/dump_syms/dump_syms_mac-dump_syms_tool.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(src_tools_mac_dump_syms_dump_syms_mac_CXXFLAGS) $(CXXFLAGS) -c -o src/tools/mac/dump_syms/dump_syms_mac-dump_syms_tool.obj `if test -f 'src/tools/mac/dump_syms/dump_syms_tool.cc'; then $(CYGPATH_W) 'src/tools/mac/dump_syms/dump_syms_tool.cc'; else $(CYGPATH_W) '$(srcdir)/src/tools/mac/dump_syms/dump_syms_tool.cc'; fi` +install-dist_docDATA: $(dist_doc_DATA) + @$(NORMAL_INSTALL) + @list='$(dist_doc_DATA)'; test -n "$(docdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(docdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(docdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(docdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(docdir)" || exit $$?; \ + done + +uninstall-dist_docDATA: + @$(NORMAL_UNINSTALL) + @list='$(dist_doc_DATA)'; test -n "$(docdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(docdir)'; $(am__uninstall_files_from_dir) +install-pkgconfigDATA: $(pkgconfig_DATA) + @$(NORMAL_INSTALL) + @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(pkgconfigdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(pkgconfigdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgconfigdir)'"; \ + $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgconfigdir)" || exit $$?; \ + done + +uninstall-pkgconfigDATA: + @$(NORMAL_UNINSTALL) + @list='$(pkgconfig_DATA)'; test -n "$(pkgconfigdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(pkgconfigdir)'; $(am__uninstall_files_from_dir) +install-includecHEADERS: $(includec_HEADERS) + @$(NORMAL_INSTALL) + @list='$(includec_HEADERS)'; test -n "$(includecdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(includecdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(includecdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includecdir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(includecdir)" || exit $$?; \ + done + +uninstall-includecHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(includec_HEADERS)'; test -n "$(includecdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(includecdir)'; $(am__uninstall_files_from_dir) +install-includeclHEADERS: $(includecl_HEADERS) + @$(NORMAL_INSTALL) + @list='$(includecl_HEADERS)'; test -n "$(includecldir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(includecldir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(includecldir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includecldir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(includecldir)" || exit $$?; \ + done + +uninstall-includeclHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(includecl_HEADERS)'; test -n "$(includecldir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(includecldir)'; $(am__uninstall_files_from_dir) +install-includeclcHEADERS: $(includeclc_HEADERS) + @$(NORMAL_INSTALL) + @list='$(includeclc_HEADERS)'; test -n "$(includeclcdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(includeclcdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(includeclcdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includeclcdir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(includeclcdir)" || exit $$?; \ + done + +uninstall-includeclcHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(includeclc_HEADERS)'; test -n "$(includeclcdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(includeclcdir)'; $(am__uninstall_files_from_dir) +install-includecldwcHEADERS: $(includecldwc_HEADERS) + @$(NORMAL_INSTALL) + @list='$(includecldwc_HEADERS)'; test -n "$(includecldwcdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(includecldwcdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(includecldwcdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includecldwcdir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(includecldwcdir)" || exit $$?; \ + done + +uninstall-includecldwcHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(includecldwc_HEADERS)'; test -n "$(includecldwcdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(includecldwcdir)'; $(am__uninstall_files_from_dir) +install-includeclhHEADERS: $(includeclh_HEADERS) + @$(NORMAL_INSTALL) + @list='$(includeclh_HEADERS)'; test -n "$(includeclhdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(includeclhdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(includeclhdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includeclhdir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(includeclhdir)" || exit $$?; \ + done + +uninstall-includeclhHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(includeclh_HEADERS)'; test -n "$(includeclhdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(includeclhdir)'; $(am__uninstall_files_from_dir) +install-includeclmHEADERS: $(includeclm_HEADERS) + @$(NORMAL_INSTALL) + @list='$(includeclm_HEADERS)'; test -n "$(includeclmdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(includeclmdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(includeclmdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includeclmdir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(includeclmdir)" || exit $$?; \ + done + +uninstall-includeclmHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(includeclm_HEADERS)'; test -n "$(includeclmdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(includeclmdir)'; $(am__uninstall_files_from_dir) +install-includegbcHEADERS: $(includegbc_HEADERS) + @$(NORMAL_INSTALL) + @list='$(includegbc_HEADERS)'; test -n "$(includegbcdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(includegbcdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(includegbcdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includegbcdir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(includegbcdir)" || exit $$?; \ + done + +uninstall-includegbcHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(includegbc_HEADERS)'; test -n "$(includegbcdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(includegbcdir)'; $(am__uninstall_files_from_dir) +install-includelssHEADERS: $(includelss_HEADERS) + @$(NORMAL_INSTALL) + @list='$(includelss_HEADERS)'; test -n "$(includelssdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(includelssdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(includelssdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includelssdir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(includelssdir)" || exit $$?; \ + done + +uninstall-includelssHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(includelss_HEADERS)'; test -n "$(includelssdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(includelssdir)'; $(am__uninstall_files_from_dir) +install-includepHEADERS: $(includep_HEADERS) + @$(NORMAL_INSTALL) + @list='$(includep_HEADERS)'; test -n "$(includepdir)" || list=; \ + if test -n "$$list"; then \ + echo " $(MKDIR_P) '$(DESTDIR)$(includepdir)'"; \ + $(MKDIR_P) "$(DESTDIR)$(includepdir)" || exit 1; \ + fi; \ + for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + echo "$$d$$p"; \ + done | $(am__base_list) | \ + while read files; do \ + echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(includepdir)'"; \ + $(INSTALL_HEADER) $$files "$(DESTDIR)$(includepdir)" || exit $$?; \ + done + +uninstall-includepHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(includep_HEADERS)'; test -n "$(includepdir)" || list=; \ + files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ + dir='$(DESTDIR)$(includepdir)'; $(am__uninstall_files_from_dir) + +ID: $(am__tagged_files) + $(am__define_uniq_tagged_files); mkid -fID $$unique +tags: tags-am +TAGS: tags + +tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + set x; \ + here=`pwd`; \ + $(am__define_uniq_tagged_files); \ + shift; \ + if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \ + test -n "$$unique" || unique=$$empty_fix; \ + if test $$# -gt 0; then \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + "$$@" $$unique; \ + else \ + $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$unique; \ + fi; \ + fi +ctags: ctags-am + +CTAGS: ctags +ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files) + $(am__define_uniq_tagged_files); \ + test -z "$(CTAGS_ARGS)$$unique" \ + || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \ + $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && $(am__cd) $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) "$$here" +cscope: cscope.files + test ! -s cscope.files \ + || $(CSCOPE) -b -q $(AM_CSCOPEFLAGS) $(CSCOPEFLAGS) -i cscope.files $(CSCOPE_ARGS) +clean-cscope: + -rm -f cscope.files +cscope.files: clean-cscope cscopelist +cscopelist: cscopelist-am + +cscopelist-am: $(am__tagged_files) + list='$(am__tagged_files)'; \ + case "$(srcdir)" in \ + [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \ + *) sdir=$(subdir)/$(srcdir) ;; \ + esac; \ + for i in $$list; do \ + if test -f "$$i"; then \ + echo "$(subdir)/$$i"; \ + else \ + echo "$$sdir/$$i"; \ + fi; \ + done >> $(top_builddir)/cscope.files + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags + -rm -f cscope.out cscope.in.out cscope.po.out cscope.files + +# Recover from deleted '.trs' file; this should ensure that +# "rm -f foo.log; make foo.trs" re-run 'foo.test', and re-create +# both 'foo.log' and 'foo.trs'. Break the recipe in two subshells +# to avoid problems with "make -n". +.log.trs: + rm -f $< $@ + $(MAKE) $(AM_MAKEFLAGS) $< + +# Leading 'am--fnord' is there to ensure the list of targets does not +# expand to empty, as could happen e.g. with make check TESTS=''. +am--fnord $(TEST_LOGS) $(TEST_LOGS:.log=.trs): $(am__force_recheck) +am--force-recheck: + @: + +$(TEST_SUITE_LOG): $(TEST_LOGS) + @$(am__set_TESTS_bases); \ + am__f_ok () { test -f "$$1" && test -r "$$1"; }; \ + redo_bases=`for i in $$bases; do \ + am__f_ok $$i.trs && am__f_ok $$i.log || echo $$i; \ + done`; \ + if test -n "$$redo_bases"; then \ + redo_logs=`for i in $$redo_bases; do echo $$i.log; done`; \ + redo_results=`for i in $$redo_bases; do echo $$i.trs; done`; \ + if $(am__make_dryrun); then :; else \ + rm -f $$redo_logs && rm -f $$redo_results || exit 1; \ + fi; \ + fi; \ + if test -n "$$am__remaking_logs"; then \ + echo "fatal: making $(TEST_SUITE_LOG): possible infinite" \ + "recursion detected" >&2; \ + elif test -n "$$redo_logs"; then \ + am__remaking_logs=yes $(MAKE) $(AM_MAKEFLAGS) $$redo_logs; \ + fi; \ + if $(am__make_dryrun); then :; else \ + st=0; \ + errmsg="fatal: making $(TEST_SUITE_LOG): failed to create"; \ + for i in $$redo_bases; do \ + test -f $$i.trs && test -r $$i.trs \ + || { echo "$$errmsg $$i.trs" >&2; st=1; }; \ + test -f $$i.log && test -r $$i.log \ + || { echo "$$errmsg $$i.log" >&2; st=1; }; \ + done; \ + test $$st -eq 0 || exit 1; \ + fi + @$(am__sh_e_setup); $(am__tty_colors); $(am__set_TESTS_bases); \ + ws='[ ]'; \ + results=`for b in $$bases; do echo $$b.trs; done`; \ + test -n "$$results" || results=/dev/null; \ + all=` grep "^$$ws*:test-result:" $$results | wc -l`; \ + pass=` grep "^$$ws*:test-result:$$ws*PASS" $$results | wc -l`; \ + fail=` grep "^$$ws*:test-result:$$ws*FAIL" $$results | wc -l`; \ + skip=` grep "^$$ws*:test-result:$$ws*SKIP" $$results | wc -l`; \ + xfail=`grep "^$$ws*:test-result:$$ws*XFAIL" $$results | wc -l`; \ + xpass=`grep "^$$ws*:test-result:$$ws*XPASS" $$results | wc -l`; \ + error=`grep "^$$ws*:test-result:$$ws*ERROR" $$results | wc -l`; \ + if test `expr $$fail + $$xpass + $$error` -eq 0; then \ + success=true; \ + else \ + success=false; \ + fi; \ + br='==================='; br=$$br$$br$$br$$br; \ + result_count () \ + { \ + if test x"$$1" = x"--maybe-color"; then \ + maybe_colorize=yes; \ + elif test x"$$1" = x"--no-color"; then \ + maybe_colorize=no; \ + else \ + echo "$@: invalid 'result_count' usage" >&2; exit 4; \ + fi; \ + shift; \ + desc=$$1 count=$$2; \ + if test $$maybe_colorize = yes && test $$count -gt 0; then \ + color_start=$$3 color_end=$$std; \ + else \ + color_start= color_end=; \ + fi; \ + echo "$${color_start}# $$desc $$count$${color_end}"; \ + }; \ + create_testsuite_report () \ + { \ + result_count $$1 "TOTAL:" $$all "$$brg"; \ + result_count $$1 "PASS: " $$pass "$$grn"; \ + result_count $$1 "SKIP: " $$skip "$$blu"; \ + result_count $$1 "XFAIL:" $$xfail "$$lgn"; \ + result_count $$1 "FAIL: " $$fail "$$red"; \ + result_count $$1 "XPASS:" $$xpass "$$red"; \ + result_count $$1 "ERROR:" $$error "$$mgn"; \ + }; \ + { \ + echo "$(PACKAGE_STRING): $(subdir)/$(TEST_SUITE_LOG)" | \ + $(am__rst_title); \ + create_testsuite_report --no-color; \ + echo; \ + echo ".. contents:: :depth: 2"; \ + echo; \ + for b in $$bases; do echo $$b; done \ + | $(am__create_global_log); \ + } >$(TEST_SUITE_LOG).tmp || exit 1; \ + mv $(TEST_SUITE_LOG).tmp $(TEST_SUITE_LOG); \ + if $$success; then \ + col="$$grn"; \ + else \ + col="$$red"; \ + test x"$$VERBOSE" = x || cat $(TEST_SUITE_LOG); \ + fi; \ + echo "$${col}$$br$${std}"; \ + echo "$${col}Testsuite summary"$(AM_TESTSUITE_SUMMARY_HEADER)"$${std}"; \ + echo "$${col}$$br$${std}"; \ + create_testsuite_report --maybe-color; \ + echo "$$col$$br$$std"; \ + if $$success; then :; else \ + echo "$${col}See $(subdir)/$(TEST_SUITE_LOG)$${std}"; \ + if test -n "$(PACKAGE_BUGREPORT)"; then \ + echo "$${col}Please report to $(PACKAGE_BUGREPORT)$${std}"; \ + fi; \ + echo "$$col$$br$$std"; \ + fi; \ + $$success || exit 1 + +check-TESTS: $(check_PROGRAMS) $(check_LIBRARIES) $(check_SCRIPTS) + @list='$(RECHECK_LOGS)'; test -z "$$list" || rm -f $$list + @list='$(RECHECK_LOGS:.log=.trs)'; test -z "$$list" || rm -f $$list + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + trs_list=`for i in $$bases; do echo $$i.trs; done`; \ + log_list=`echo $$log_list`; trs_list=`echo $$trs_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) TEST_LOGS="$$log_list"; \ + exit $$?; +recheck: all $(check_PROGRAMS) $(check_LIBRARIES) $(check_SCRIPTS) + @test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + @set +e; $(am__set_TESTS_bases); \ + bases=`for i in $$bases; do echo $$i; done \ + | $(am__list_recheck_tests)` || exit 1; \ + log_list=`for i in $$bases; do echo $$i.log; done`; \ + log_list=`echo $$log_list`; \ + $(MAKE) $(AM_MAKEFLAGS) $(TEST_SUITE_LOG) \ + am__force_recheck=am--force-recheck \ + TEST_LOGS="$$log_list"; \ + exit $$? +src/common/test_assembler_unittest.log: src/common/test_assembler_unittest$(EXEEXT) + @p='src/common/test_assembler_unittest$(EXEEXT)'; \ + b='src/common/test_assembler_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/common/dwarf/dwarf2reader_lineinfo_unittest.log: src/common/dwarf/dwarf2reader_lineinfo_unittest$(EXEEXT) + @p='src/common/dwarf/dwarf2reader_lineinfo_unittest$(EXEEXT)'; \ + b='src/common/dwarf/dwarf2reader_lineinfo_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/common/dwarf/dwarf2reader_splitfunctions_unittest.log: src/common/dwarf/dwarf2reader_splitfunctions_unittest$(EXEEXT) + @p='src/common/dwarf/dwarf2reader_splitfunctions_unittest$(EXEEXT)'; \ + b='src/common/dwarf/dwarf2reader_splitfunctions_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/address_map_unittest.log: src/processor/address_map_unittest$(EXEEXT) + @p='src/processor/address_map_unittest$(EXEEXT)'; \ + b='src/processor/address_map_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/basic_source_line_resolver_unittest.log: src/processor/basic_source_line_resolver_unittest$(EXEEXT) + @p='src/processor/basic_source_line_resolver_unittest$(EXEEXT)'; \ + b='src/processor/basic_source_line_resolver_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/cfi_frame_info_unittest.log: src/processor/cfi_frame_info_unittest$(EXEEXT) + @p='src/processor/cfi_frame_info_unittest$(EXEEXT)'; \ + b='src/processor/cfi_frame_info_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/contained_range_map_unittest.log: src/processor/contained_range_map_unittest$(EXEEXT) + @p='src/processor/contained_range_map_unittest$(EXEEXT)'; \ + b='src/processor/contained_range_map_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/disassembler_x86_unittest.log: src/processor/disassembler_x86_unittest$(EXEEXT) + @p='src/processor/disassembler_x86_unittest$(EXEEXT)'; \ + b='src/processor/disassembler_x86_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/exploitability_unittest.log: src/processor/exploitability_unittest$(EXEEXT) + @p='src/processor/exploitability_unittest$(EXEEXT)'; \ + b='src/processor/exploitability_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/fast_source_line_resolver_unittest.log: src/processor/fast_source_line_resolver_unittest$(EXEEXT) + @p='src/processor/fast_source_line_resolver_unittest$(EXEEXT)'; \ + b='src/processor/fast_source_line_resolver_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/map_serializers_unittest.log: src/processor/map_serializers_unittest$(EXEEXT) + @p='src/processor/map_serializers_unittest$(EXEEXT)'; \ + b='src/processor/map_serializers_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/microdump_processor_unittest.log: src/processor/microdump_processor_unittest$(EXEEXT) + @p='src/processor/microdump_processor_unittest$(EXEEXT)'; \ + b='src/processor/microdump_processor_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/minidump_processor_unittest.log: src/processor/minidump_processor_unittest$(EXEEXT) + @p='src/processor/minidump_processor_unittest$(EXEEXT)'; \ + b='src/processor/minidump_processor_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/minidump_unittest.log: src/processor/minidump_unittest$(EXEEXT) + @p='src/processor/minidump_unittest$(EXEEXT)'; \ + b='src/processor/minidump_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/static_address_map_unittest.log: src/processor/static_address_map_unittest$(EXEEXT) + @p='src/processor/static_address_map_unittest$(EXEEXT)'; \ + b='src/processor/static_address_map_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/static_contained_range_map_unittest.log: src/processor/static_contained_range_map_unittest$(EXEEXT) + @p='src/processor/static_contained_range_map_unittest$(EXEEXT)'; \ + b='src/processor/static_contained_range_map_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/static_map_unittest.log: src/processor/static_map_unittest$(EXEEXT) + @p='src/processor/static_map_unittest$(EXEEXT)'; \ + b='src/processor/static_map_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/static_range_map_unittest.log: src/processor/static_range_map_unittest$(EXEEXT) + @p='src/processor/static_range_map_unittest$(EXEEXT)'; \ + b='src/processor/static_range_map_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/pathname_stripper_unittest.log: src/processor/pathname_stripper_unittest$(EXEEXT) + @p='src/processor/pathname_stripper_unittest$(EXEEXT)'; \ + b='src/processor/pathname_stripper_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/postfix_evaluator_unittest.log: src/processor/postfix_evaluator_unittest$(EXEEXT) + @p='src/processor/postfix_evaluator_unittest$(EXEEXT)'; \ + b='src/processor/postfix_evaluator_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/proc_maps_linux_unittest.log: src/processor/proc_maps_linux_unittest$(EXEEXT) + @p='src/processor/proc_maps_linux_unittest$(EXEEXT)'; \ + b='src/processor/proc_maps_linux_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/range_map_truncate_lower_unittest.log: src/processor/range_map_truncate_lower_unittest$(EXEEXT) + @p='src/processor/range_map_truncate_lower_unittest$(EXEEXT)'; \ + b='src/processor/range_map_truncate_lower_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/range_map_truncate_upper_unittest.log: src/processor/range_map_truncate_upper_unittest$(EXEEXT) + @p='src/processor/range_map_truncate_upper_unittest$(EXEEXT)'; \ + b='src/processor/range_map_truncate_upper_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/range_map_unittest.log: src/processor/range_map_unittest$(EXEEXT) + @p='src/processor/range_map_unittest$(EXEEXT)'; \ + b='src/processor/range_map_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/stackwalker_amd64_unittest.log: src/processor/stackwalker_amd64_unittest$(EXEEXT) + @p='src/processor/stackwalker_amd64_unittest$(EXEEXT)'; \ + b='src/processor/stackwalker_amd64_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/stackwalker_arm_unittest.log: src/processor/stackwalker_arm_unittest$(EXEEXT) + @p='src/processor/stackwalker_arm_unittest$(EXEEXT)'; \ + b='src/processor/stackwalker_arm_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/stackwalker_arm64_unittest.log: src/processor/stackwalker_arm64_unittest$(EXEEXT) + @p='src/processor/stackwalker_arm64_unittest$(EXEEXT)'; \ + b='src/processor/stackwalker_arm64_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/stackwalker_address_list_unittest.log: src/processor/stackwalker_address_list_unittest$(EXEEXT) + @p='src/processor/stackwalker_address_list_unittest$(EXEEXT)'; \ + b='src/processor/stackwalker_address_list_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/stackwalker_mips_unittest.log: src/processor/stackwalker_mips_unittest$(EXEEXT) + @p='src/processor/stackwalker_mips_unittest$(EXEEXT)'; \ + b='src/processor/stackwalker_mips_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/stackwalker_mips64_unittest.log: src/processor/stackwalker_mips64_unittest$(EXEEXT) + @p='src/processor/stackwalker_mips64_unittest$(EXEEXT)'; \ + b='src/processor/stackwalker_mips64_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/stackwalker_x86_unittest.log: src/processor/stackwalker_x86_unittest$(EXEEXT) + @p='src/processor/stackwalker_x86_unittest$(EXEEXT)'; \ + b='src/processor/stackwalker_x86_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/synth_minidump_unittest.log: src/processor/synth_minidump_unittest$(EXEEXT) + @p='src/processor/synth_minidump_unittest$(EXEEXT)'; \ + b='src/processor/synth_minidump_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/client/linux/linux_client_unittest.log: src/client/linux/linux_client_unittest$(EXEEXT) + @p='src/client/linux/linux_client_unittest$(EXEEXT)'; \ + b='src/client/linux/linux_client_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/common/linux/google_crashdump_uploader_test.log: src/common/linux/google_crashdump_uploader_test$(EXEEXT) + @p='src/common/linux/google_crashdump_uploader_test$(EXEEXT)'; \ + b='src/common/linux/google_crashdump_uploader_test'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/common/dumper_unittest.log: src/common/dumper_unittest$(EXEEXT) + @p='src/common/dumper_unittest$(EXEEXT)'; \ + b='src/common/dumper_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/tools/linux/md2core/minidump_2_core_unittest.log: src/tools/linux/md2core/minidump_2_core_unittest$(EXEEXT) + @p='src/tools/linux/md2core/minidump_2_core_unittest$(EXEEXT)'; \ + b='src/tools/linux/md2core/minidump_2_core_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/common/mac/macho_reader_unittest.log: src/common/mac/macho_reader_unittest$(EXEEXT) + @p='src/common/mac/macho_reader_unittest$(EXEEXT)'; \ + b='src/common/mac/macho_reader_unittest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/stackwalker_selftest.log: src/processor/stackwalker_selftest$(EXEEXT) + @p='src/processor/stackwalker_selftest$(EXEEXT)'; \ + b='src/processor/stackwalker_selftest'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/microdump_stackwalk_test.log: src/processor/microdump_stackwalk_test + @p='src/processor/microdump_stackwalk_test'; \ + b='src/processor/microdump_stackwalk_test'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/microdump_stackwalk_machine_readable_test.log: src/processor/microdump_stackwalk_machine_readable_test + @p='src/processor/microdump_stackwalk_machine_readable_test'; \ + b='src/processor/microdump_stackwalk_machine_readable_test'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/minidump_dump_test.log: src/processor/minidump_dump_test + @p='src/processor/minidump_dump_test'; \ + b='src/processor/minidump_dump_test'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/minidump_stackwalk_test.log: src/processor/minidump_stackwalk_test + @p='src/processor/minidump_stackwalk_test'; \ + b='src/processor/minidump_stackwalk_test'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +src/processor/minidump_stackwalk_machine_readable_test.log: src/processor/minidump_stackwalk_machine_readable_test + @p='src/processor/minidump_stackwalk_machine_readable_test'; \ + b='src/processor/minidump_stackwalk_machine_readable_test'; \ + $(am__check_pre) $(LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_LOG_DRIVER_FLAGS) $(LOG_DRIVER_FLAGS) -- $(LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +.test.log: + @p='$<'; \ + $(am__set_b); \ + $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ + --log-file $$b.log --trs-file $$b.trs \ + $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ + "$$tst" $(AM_TESTS_FD_REDIRECT) +@am__EXEEXT_TRUE@.test$(EXEEXT).log: +@am__EXEEXT_TRUE@ @p='$<'; \ +@am__EXEEXT_TRUE@ $(am__set_b); \ +@am__EXEEXT_TRUE@ $(am__check_pre) $(TEST_LOG_DRIVER) --test-name "$$f" \ +@am__EXEEXT_TRUE@ --log-file $$b.log --trs-file $$b.trs \ +@am__EXEEXT_TRUE@ $(am__common_driver_flags) $(AM_TEST_LOG_DRIVER_FLAGS) $(TEST_LOG_DRIVER_FLAGS) -- $(TEST_LOG_COMPILE) \ +@am__EXEEXT_TRUE@ "$$tst" $(AM_TESTS_FD_REDIRECT) + +distdir: $(BUILT_SOURCES) + $(MAKE) $(AM_MAKEFLAGS) distdir-am + +distdir-am: $(DISTFILES) + $(am__remove_distdir) + test -d "$(distdir)" || mkdir "$(distdir)" + @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ + list='$(DISTFILES)'; \ + dist_files=`for file in $$list; do echo $$file; done | \ + sed -e "s|^$$srcdirstrip/||;t" \ + -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ + case $$dist_files in \ + */*) $(MKDIR_P) `echo "$$dist_files" | \ + sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ + sort -u` ;; \ + esac; \ + for file in $$dist_files; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + if test -d $$d/$$file; then \ + dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test -d "$(distdir)/$$file"; then \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ + find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ + fi; \ + cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ + else \ + test -f "$(distdir)/$$file" \ + || cp -p $$d/$$file "$(distdir)/$$file" \ + || exit 1; \ + fi; \ + done + -test -n "$(am__skip_mode_fix)" \ + || find "$(distdir)" -type d ! -perm -755 \ + -exec chmod u+rwx,go+rx {} \; -o \ + ! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -400 -exec chmod a+r {} \; -o \ + ! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \ + || chmod -R a+r "$(distdir)" +dist-gzip: distdir + tardir=$(distdir) && $(am__tar) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).tar.gz + $(am__post_remove_distdir) + +dist-bzip2: distdir + tardir=$(distdir) && $(am__tar) | BZIP2=$${BZIP2--9} bzip2 -c >$(distdir).tar.bz2 + $(am__post_remove_distdir) + +dist-lzip: distdir + tardir=$(distdir) && $(am__tar) | lzip -c $${LZIP_OPT--9} >$(distdir).tar.lz + $(am__post_remove_distdir) + +dist-xz: distdir + tardir=$(distdir) && $(am__tar) | XZ_OPT=$${XZ_OPT--e} xz -c >$(distdir).tar.xz + $(am__post_remove_distdir) + +dist-zstd: distdir + tardir=$(distdir) && $(am__tar) | zstd -c $${ZSTD_CLEVEL-$${ZSTD_OPT--19}} >$(distdir).tar.zst + $(am__post_remove_distdir) + +dist-tarZ: distdir + @echo WARNING: "Support for distribution archives compressed with" \ + "legacy program 'compress' is deprecated." >&2 + @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 + tardir=$(distdir) && $(am__tar) | compress -c >$(distdir).tar.Z + $(am__post_remove_distdir) + +dist-shar: distdir + @echo WARNING: "Support for shar distribution archives is" \ + "deprecated." >&2 + @echo WARNING: "It will be removed altogether in Automake 2.0" >&2 + shar $(distdir) | eval GZIP= gzip $(GZIP_ENV) -c >$(distdir).shar.gz + $(am__post_remove_distdir) + +dist-zip: distdir + -rm -f $(distdir).zip + zip -rq $(distdir).zip $(distdir) + $(am__post_remove_distdir) + +dist dist-all: + $(MAKE) $(AM_MAKEFLAGS) $(DIST_TARGETS) am__post_remove_distdir='@:' + $(am__post_remove_distdir) + +# This target untars the dist file and tries a VPATH configuration. Then +# it guarantees that the distribution is self-contained by making another +# tarfile. +distcheck: dist + case '$(DIST_ARCHIVES)' in \ + *.tar.gz*) \ + eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).tar.gz | $(am__untar) ;;\ + *.tar.bz2*) \ + bzip2 -dc $(distdir).tar.bz2 | $(am__untar) ;;\ + *.tar.lz*) \ + lzip -dc $(distdir).tar.lz | $(am__untar) ;;\ + *.tar.xz*) \ + xz -dc $(distdir).tar.xz | $(am__untar) ;;\ + *.tar.Z*) \ + uncompress -c $(distdir).tar.Z | $(am__untar) ;;\ + *.shar.gz*) \ + eval GZIP= gzip $(GZIP_ENV) -dc $(distdir).shar.gz | unshar ;;\ + *.zip*) \ + unzip $(distdir).zip ;;\ + *.tar.zst*) \ + zstd -dc $(distdir).tar.zst | $(am__untar) ;;\ + esac + chmod -R a-w $(distdir) + chmod u+w $(distdir) + mkdir $(distdir)/_build $(distdir)/_build/sub $(distdir)/_inst + chmod a-w $(distdir) + test -d $(distdir)/_build || exit 0; \ + dc_install_base=`$(am__cd) $(distdir)/_inst && pwd | sed -e 's,^[^:\\/]:[\\/],/,'` \ + && dc_destdir="$${TMPDIR-/tmp}/am-dc-$$$$/" \ + && am__cwd=`pwd` \ + && $(am__cd) $(distdir)/_build/sub \ + && ../../configure \ + $(AM_DISTCHECK_CONFIGURE_FLAGS) \ + $(DISTCHECK_CONFIGURE_FLAGS) \ + --srcdir=../.. --prefix="$$dc_install_base" \ + && $(MAKE) $(AM_MAKEFLAGS) \ + && $(MAKE) $(AM_MAKEFLAGS) $(AM_DISTCHECK_DVI_TARGET) \ + && $(MAKE) $(AM_MAKEFLAGS) check \ + && $(MAKE) $(AM_MAKEFLAGS) install \ + && $(MAKE) $(AM_MAKEFLAGS) installcheck \ + && $(MAKE) $(AM_MAKEFLAGS) uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) distuninstallcheck_dir="$$dc_install_base" \ + distuninstallcheck \ + && chmod -R a-w "$$dc_install_base" \ + && ({ \ + (cd ../.. && umask 077 && mkdir "$$dc_destdir") \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" install \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" uninstall \ + && $(MAKE) $(AM_MAKEFLAGS) DESTDIR="$$dc_destdir" \ + distuninstallcheck_dir="$$dc_destdir" distuninstallcheck; \ + } || { rm -rf "$$dc_destdir"; exit 1; }) \ + && rm -rf "$$dc_destdir" \ + && $(MAKE) $(AM_MAKEFLAGS) dist \ + && rm -rf $(DIST_ARCHIVES) \ + && $(MAKE) $(AM_MAKEFLAGS) distcleancheck \ + && cd "$$am__cwd" \ + || exit 1 + $(am__post_remove_distdir) + @(echo "$(distdir) archives ready for distribution: "; \ + list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \ + sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x' +distuninstallcheck: + @test -n '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: trying to run $@ with an empty' \ + '$$(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + $(am__cd) '$(distuninstallcheck_dir)' || { \ + echo 'ERROR: cannot chdir into $(distuninstallcheck_dir)' >&2; \ + exit 1; \ + }; \ + test `$(am__distuninstallcheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left after uninstall:" ; \ + if test -n "$(DESTDIR)"; then \ + echo " (check DESTDIR support)"; \ + fi ; \ + $(distuninstallcheck_listfiles) ; \ + exit 1; } >&2 +distcleancheck: distclean + @if test '$(srcdir)' = . ; then \ + echo "ERROR: distcleancheck can only run from a VPATH build" ; \ + exit 1 ; \ + fi + @test `$(distcleancheck_listfiles) | wc -l` -eq 0 \ + || { echo "ERROR: files left in build directory after distclean:" ; \ + $(distcleancheck_listfiles) ; \ + exit 1; } >&2 +check-am: all-am + $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS) $(check_LIBRARIES) \ + $(check_SCRIPTS) + $(MAKE) $(AM_MAKEFLAGS) check-TESTS +check: check-am +all-am: Makefile $(PROGRAMS) $(LIBRARIES) $(SCRIPTS) $(DATA) \ + $(HEADERS) +installdirs: + for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libexecdir)" "$(DESTDIR)$(libdir)" "$(DESTDIR)$(docdir)" "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(includecdir)" "$(DESTDIR)$(includecldir)" "$(DESTDIR)$(includeclcdir)" "$(DESTDIR)$(includecldwcdir)" "$(DESTDIR)$(includeclhdir)" "$(DESTDIR)$(includeclmdir)" "$(DESTDIR)$(includegbcdir)" "$(DESTDIR)$(includelssdir)" "$(DESTDIR)$(includepdir)"; do \ + test -z "$$dir" || $(MKDIR_P) "$$dir"; \ + done +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + if test -z '$(STRIP)'; then \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + install; \ + else \ + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ + "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \ + fi +mostlyclean-generic: + -test -z "$(TEST_LOGS)" || rm -f $(TEST_LOGS) + -test -z "$(TEST_LOGS:.log=.trs)" || rm -f $(TEST_LOGS:.log=.trs) + -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) + +clean-generic: + -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) + +distclean-generic: + -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) + -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) + -rm -f src/$(am__dirstamp) + -rm -f src/client/$(DEPDIR)/$(am__dirstamp) + -rm -f src/client/$(am__dirstamp) + -rm -f src/client/linux/$(am__dirstamp) + -rm -f src/client/linux/crash_generation/$(DEPDIR)/$(am__dirstamp) + -rm -f src/client/linux/crash_generation/$(am__dirstamp) + -rm -f src/client/linux/dump_writer_common/$(DEPDIR)/$(am__dirstamp) + -rm -f src/client/linux/dump_writer_common/$(am__dirstamp) + -rm -f src/client/linux/handler/$(DEPDIR)/$(am__dirstamp) + -rm -f src/client/linux/handler/$(am__dirstamp) + -rm -f src/client/linux/log/$(DEPDIR)/$(am__dirstamp) + -rm -f src/client/linux/log/$(am__dirstamp) + -rm -f src/client/linux/microdump_writer/$(DEPDIR)/$(am__dirstamp) + -rm -f src/client/linux/microdump_writer/$(am__dirstamp) + -rm -f src/client/linux/minidump_writer/$(DEPDIR)/$(am__dirstamp) + -rm -f src/client/linux/minidump_writer/$(am__dirstamp) + -rm -f src/common/$(DEPDIR)/$(am__dirstamp) + -rm -f src/common/$(am__dirstamp) + -rm -f src/common/dwarf/$(DEPDIR)/$(am__dirstamp) + -rm -f src/common/dwarf/$(am__dirstamp) + -rm -f src/common/linux/$(DEPDIR)/$(am__dirstamp) + -rm -f src/common/linux/$(am__dirstamp) + -rm -f src/common/linux/tests/$(DEPDIR)/$(am__dirstamp) + -rm -f src/common/linux/tests/$(am__dirstamp) + -rm -f src/common/mac/$(DEPDIR)/$(am__dirstamp) + -rm -f src/common/mac/$(am__dirstamp) + -rm -f src/common/tests/$(DEPDIR)/$(am__dirstamp) + -rm -f src/common/tests/$(am__dirstamp) + -rm -f src/processor/$(DEPDIR)/$(am__dirstamp) + -rm -f src/processor/$(am__dirstamp) + -rm -f src/testing/$(am__dirstamp) + -rm -f src/testing/googlemock/src/$(DEPDIR)/$(am__dirstamp) + -rm -f src/testing/googlemock/src/$(am__dirstamp) + -rm -f src/testing/googletest/src/$(DEPDIR)/$(am__dirstamp) + -rm -f src/testing/googletest/src/$(am__dirstamp) + -rm -f src/third_party/libdisasm/$(DEPDIR)/$(am__dirstamp) + -rm -f src/third_party/libdisasm/$(am__dirstamp) + -rm -f src/tools/linux/core2md/$(DEPDIR)/$(am__dirstamp) + -rm -f src/tools/linux/core2md/$(am__dirstamp) + -rm -f src/tools/linux/core_handler/$(DEPDIR)/$(am__dirstamp) + -rm -f src/tools/linux/core_handler/$(am__dirstamp) + -rm -f src/tools/linux/dump_syms/$(DEPDIR)/$(am__dirstamp) + -rm -f src/tools/linux/dump_syms/$(am__dirstamp) + -rm -f src/tools/linux/md2core/$(DEPDIR)/$(am__dirstamp) + -rm -f src/tools/linux/md2core/$(am__dirstamp) + -rm -f src/tools/linux/pid2md/$(DEPDIR)/$(am__dirstamp) + -rm -f src/tools/linux/pid2md/$(am__dirstamp) + -rm -f src/tools/linux/symupload/$(DEPDIR)/$(am__dirstamp) + -rm -f src/tools/linux/symupload/$(am__dirstamp) + -rm -f src/tools/mac/dump_syms/$(DEPDIR)/$(am__dirstamp) + -rm -f src/tools/mac/dump_syms/$(am__dirstamp) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-binPROGRAMS clean-checkLIBRARIES clean-checkPROGRAMS \ + clean-generic clean-libLIBRARIES clean-libexecPROGRAMS \ + clean-noinstLIBRARIES clean-noinstPROGRAMS mostlyclean-am + +distclean: distclean-am + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -f src/client/$(DEPDIR)/minidump_file_writer.Po + -rm -f src/client/linux/crash_generation/$(DEPDIR)/crash_generation_client.Po + -rm -f src/client/linux/crash_generation/$(DEPDIR)/crash_generation_server.Po + -rm -f src/client/linux/dump_writer_common/$(DEPDIR)/thread_info.Po + -rm -f src/client/linux/dump_writer_common/$(DEPDIR)/ucontext_reader.Po + -rm -f src/client/linux/handler/$(DEPDIR)/exception_handler.Po + -rm -f src/client/linux/handler/$(DEPDIR)/linux_client_unittest_shlib-exception_handler_unittest.Po + -rm -f src/client/linux/handler/$(DEPDIR)/minidump_descriptor.Po + -rm -f src/client/linux/log/$(DEPDIR)/log.Po + -rm -f src/client/linux/microdump_writer/$(DEPDIR)/linux_client_unittest_shlib-microdump_writer_unittest.Po + -rm -f src/client/linux/microdump_writer/$(DEPDIR)/microdump_writer.Po + -rm -f src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-cpu_set_unittest.Po + -rm -f src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-directory_reader_unittest.Po + -rm -f src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-line_reader_unittest.Po + -rm -f src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-linux_core_dumper.Po + -rm -f src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-linux_core_dumper_unittest.Po + -rm -f src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-linux_ptrace_dumper_unittest.Po + -rm -f src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-minidump_writer_unittest.Po + -rm -f src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-minidump_writer_unittest_utils.Po + -rm -f src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.Po + -rm -f src/client/linux/minidump_writer/$(DEPDIR)/linux_core_dumper.Po + -rm -f src/client/linux/minidump_writer/$(DEPDIR)/linux_dumper.Po + -rm -f src/client/linux/minidump_writer/$(DEPDIR)/linux_dumper_unittest_helper-linux_dumper_unittest_helper.Po + -rm -f src/client/linux/minidump_writer/$(DEPDIR)/linux_ptrace_dumper.Po + -rm -f src/client/linux/minidump_writer/$(DEPDIR)/minidump_writer.Po + -rm -f src/common/$(DEPDIR)/client_linux_linux_client_unittest_shlib-memory_allocator_unittest.Po + -rm -f src/common/$(DEPDIR)/convert_UTF.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-byte_cursor_unittest.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-convert_UTF.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-dwarf_cfi_to_module.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-dwarf_cfi_to_module_unittest.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-dwarf_cu_to_module.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-dwarf_cu_to_module_unittest.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-dwarf_line_to_module.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-dwarf_line_to_module_unittest.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-dwarf_range_list_handler.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-language.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-memory_range_unittest.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-module.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-module_unittest.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-path_helper.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-stabs_reader.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-stabs_reader_unittest.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-stabs_to_module.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-stabs_to_module_unittest.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-string_conversion.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-string_conversion_unittest.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-test_assembler.Po + -rm -f src/common/$(DEPDIR)/mac_macho_reader_unittest-dwarf_cfi_to_module.Po + -rm -f src/common/$(DEPDIR)/mac_macho_reader_unittest-dwarf_cu_to_module.Po + -rm -f src/common/$(DEPDIR)/mac_macho_reader_unittest-dwarf_line_to_module.Po + -rm -f src/common/$(DEPDIR)/mac_macho_reader_unittest-language.Po + -rm -f src/common/$(DEPDIR)/mac_macho_reader_unittest-md5.Po + -rm -f src/common/$(DEPDIR)/mac_macho_reader_unittest-module.Po + -rm -f src/common/$(DEPDIR)/mac_macho_reader_unittest-path_helper.Po + -rm -f src/common/$(DEPDIR)/mac_macho_reader_unittest-stabs_reader.Po + -rm -f src/common/$(DEPDIR)/mac_macho_reader_unittest-stabs_to_module.Po + -rm -f src/common/$(DEPDIR)/mac_macho_reader_unittest-test_assembler.Po + -rm -f src/common/$(DEPDIR)/md5.Po + -rm -f src/common/$(DEPDIR)/path_helper.Po + -rm -f src/common/$(DEPDIR)/processor_minidump_unittest-test_assembler.Po + -rm -f src/common/$(DEPDIR)/processor_stackwalker_address_list_unittest-test_assembler.Po + -rm -f src/common/$(DEPDIR)/processor_stackwalker_amd64_unittest-test_assembler.Po + -rm -f src/common/$(DEPDIR)/processor_stackwalker_arm64_unittest-test_assembler.Po + -rm -f src/common/$(DEPDIR)/processor_stackwalker_arm_unittest-test_assembler.Po + -rm -f src/common/$(DEPDIR)/processor_stackwalker_mips64_unittest-test_assembler.Po + -rm -f src/common/$(DEPDIR)/processor_stackwalker_mips_unittest-test_assembler.Po + -rm -f src/common/$(DEPDIR)/processor_stackwalker_x86_unittest-test_assembler.Po + -rm -f src/common/$(DEPDIR)/processor_synth_minidump_unittest-test_assembler.Po + -rm -f src/common/$(DEPDIR)/string_conversion.Po + -rm -f src/common/$(DEPDIR)/test_assembler_unittest-test_assembler.Po + -rm -f src/common/$(DEPDIR)/test_assembler_unittest-test_assembler_unittest.Po + -rm -f src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_cfi_to_module.Po + -rm -f src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_cu_to_module.Po + -rm -f src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_line_to_module.Po + -rm -f src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_range_list_handler.Po + -rm -f src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-language.Po + -rm -f src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-module.Po + -rm -f src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-path_helper.Po + -rm -f src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-stabs_reader.Po + -rm -f src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-stabs_to_module.Po + -rm -f src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_cfi_to_module.Po + -rm -f src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_cu_to_module.Po + -rm -f src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_line_to_module.Po + -rm -f src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_range_list_handler.Po + -rm -f src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-language.Po + -rm -f src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-md5.Po + -rm -f src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-module.Po + -rm -f src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-path_helper.Po + -rm -f src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-stabs_reader.Po + -rm -f src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-stabs_to_module.Po + -rm -f src/common/dwarf/$(DEPDIR)/dumper_unittest-bytereader.Po + -rm -f src/common/dwarf/$(DEPDIR)/dumper_unittest-bytereader_unittest.Po + -rm -f src/common/dwarf/$(DEPDIR)/dumper_unittest-cfi_assembler.Po + -rm -f src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2diehandler.Po + -rm -f src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2diehandler_unittest.Po + -rm -f src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2reader.Po + -rm -f src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2reader_cfi_unittest.Po + -rm -f src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2reader_die_unittest.Po + -rm -f src/common/dwarf/$(DEPDIR)/dumper_unittest-elf_reader.Po + -rm -f src/common/dwarf/$(DEPDIR)/dwarf2reader_lineinfo_unittest-dwarf2reader_lineinfo_unittest.Po + -rm -f src/common/dwarf/$(DEPDIR)/dwarf2reader_splitfunctions_unittest-dwarf2reader_splitfunctions_unittest.Po + -rm -f src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-bytereader.Po + -rm -f src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-cfi_assembler.Po + -rm -f src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-dwarf2diehandler.Po + -rm -f src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-dwarf2reader.Po + -rm -f src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-elf_reader.Po + -rm -f src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-bytereader.Po + -rm -f src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf2diehandler.Po + -rm -f src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf2reader.Po + -rm -f src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-elf_reader.Po + -rm -f src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-bytereader.Po + -rm -f src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf2diehandler.Po + -rm -f src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf2reader.Po + -rm -f src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-elf_reader.Po + -rm -f src/common/linux/$(DEPDIR)/breakpad_getcontext.Po + -rm -f src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-breakpad_getcontext.Po + -rm -f src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-breakpad_getcontext_unittest.Po + -rm -f src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-elf_core_dump.Po + -rm -f src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-linux_libc_support_unittest.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-crc32.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-dump_symbols.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-dump_symbols_unittest.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-elf_core_dump.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-elf_core_dump_unittest.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-elf_symbols_to_module.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-elf_symbols_to_module_unittest.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-elfutils.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-file_id.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-file_id_unittest.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-linux_libc_support.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-memory_mapped_file.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-memory_mapped_file_unittest.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-safe_readlink.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-safe_readlink_unittest.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-synth_elf.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-synth_elf_unittest.Po + -rm -f src/common/linux/$(DEPDIR)/elf_core_dump.Po + -rm -f src/common/linux/$(DEPDIR)/elfutils.Po + -rm -f src/common/linux/$(DEPDIR)/file_id.Po + -rm -f src/common/linux/$(DEPDIR)/google_crashdump_uploader_test-google_crashdump_uploader.Po + -rm -f src/common/linux/$(DEPDIR)/google_crashdump_uploader_test-google_crashdump_uploader_test.Po + -rm -f src/common/linux/$(DEPDIR)/google_crashdump_uploader_test-libcurl_wrapper.Po + -rm -f src/common/linux/$(DEPDIR)/guid_creator.Po + -rm -f src/common/linux/$(DEPDIR)/http_upload.Po + -rm -f src/common/linux/$(DEPDIR)/libcurl_wrapper.Po + -rm -f src/common/linux/$(DEPDIR)/linux_libc_support.Po + -rm -f src/common/linux/$(DEPDIR)/memory_mapped_file.Po + -rm -f src/common/linux/$(DEPDIR)/safe_readlink.Po + -rm -f src/common/linux/$(DEPDIR)/symbol_collector_client.Po + -rm -f src/common/linux/$(DEPDIR)/symbol_upload.Po + -rm -f src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-crc32.Po + -rm -f src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dump_symbols.Po + -rm -f src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-elf_symbols_to_module.Po + -rm -f src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-elfutils.Po + -rm -f src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-file_id.Po + -rm -f src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-linux_libc_support.Po + -rm -f src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-memory_mapped_file.Po + -rm -f src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-safe_readlink.Po + -rm -f src/common/linux/tests/$(DEPDIR)/client_linux_linux_client_unittest_shlib-crash_generator.Po + -rm -f src/common/linux/tests/$(DEPDIR)/dumper_unittest-crash_generator.Po + -rm -f src/common/mac/$(DEPDIR)/macho_reader_unittest-arch_utilities.Po + -rm -f src/common/mac/$(DEPDIR)/macho_reader_unittest-file_id.Po + -rm -f src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_id.Po + -rm -f src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_reader.Po + -rm -f src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_reader_unittest.Po + -rm -f src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_utilities.Po + -rm -f src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_walker.Po + -rm -f src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-arch_utilities.Po + -rm -f src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dump_syms.Po + -rm -f src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-file_id.Po + -rm -f src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_id.Po + -rm -f src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_reader.Po + -rm -f src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_utilities.Po + -rm -f src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_walker.Po + -rm -f src/common/tests/$(DEPDIR)/client_linux_linux_client_unittest_shlib-file_utils.Po + -rm -f src/common/tests/$(DEPDIR)/dumper_unittest-file_utils.Po + -rm -f src/common/tests/$(DEPDIR)/mac_macho_reader_unittest-file_utils.Po + -rm -f src/processor/$(DEPDIR)/address_map_unittest.Po + -rm -f src/processor/$(DEPDIR)/basic_code_modules.Po + -rm -f src/processor/$(DEPDIR)/basic_source_line_resolver.Po + -rm -f src/processor/$(DEPDIR)/basic_source_line_resolver_unittest-basic_source_line_resolver_unittest.Po + -rm -f src/processor/$(DEPDIR)/call_stack.Po + -rm -f src/processor/$(DEPDIR)/cfi_frame_info.Po + -rm -f src/processor/$(DEPDIR)/cfi_frame_info_unittest-cfi_frame_info_unittest.Po + -rm -f src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-basic_code_modules.Po + -rm -f src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-convert_old_arm64_context.Po + -rm -f src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-dump_context.Po + -rm -f src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-dump_object.Po + -rm -f src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-logging.Po + -rm -f src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-minidump.Po + -rm -f src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-pathname_stripper.Po + -rm -f src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-proc_maps_linux.Po + -rm -f src/processor/$(DEPDIR)/contained_range_map_unittest.Po + -rm -f src/processor/$(DEPDIR)/convert_old_arm64_context.Po + -rm -f src/processor/$(DEPDIR)/disassembler_x86.Po + -rm -f src/processor/$(DEPDIR)/disassembler_x86_unittest-disassembler_x86_unittest.Po + -rm -f src/processor/$(DEPDIR)/dump_context.Po + -rm -f src/processor/$(DEPDIR)/dump_object.Po + -rm -f src/processor/$(DEPDIR)/exploitability.Po + -rm -f src/processor/$(DEPDIR)/exploitability_linux.Po + -rm -f src/processor/$(DEPDIR)/exploitability_unittest-exploitability_unittest.Po + -rm -f src/processor/$(DEPDIR)/exploitability_win.Po + -rm -f src/processor/$(DEPDIR)/fast_source_line_resolver.Po + -rm -f src/processor/$(DEPDIR)/fast_source_line_resolver_unittest-fast_source_line_resolver_unittest.Po + -rm -f src/processor/$(DEPDIR)/logging.Po + -rm -f src/processor/$(DEPDIR)/map_serializers_unittest-map_serializers_unittest.Po + -rm -f src/processor/$(DEPDIR)/microdump.Po + -rm -f src/processor/$(DEPDIR)/microdump_processor.Po + -rm -f src/processor/$(DEPDIR)/microdump_processor_unittest-microdump_processor_unittest.Po + -rm -f src/processor/$(DEPDIR)/microdump_stackwalk.Po + -rm -f src/processor/$(DEPDIR)/minidump.Po + -rm -f src/processor/$(DEPDIR)/minidump_dump.Po + -rm -f src/processor/$(DEPDIR)/minidump_processor.Po + -rm -f src/processor/$(DEPDIR)/minidump_processor_unittest-minidump_processor_unittest.Po + -rm -f src/processor/$(DEPDIR)/minidump_stackwalk.Po + -rm -f src/processor/$(DEPDIR)/minidump_unittest-minidump_unittest.Po + -rm -f src/processor/$(DEPDIR)/minidump_unittest-synth_minidump.Po + -rm -f src/processor/$(DEPDIR)/module_comparer.Po + -rm -f src/processor/$(DEPDIR)/module_serializer.Po + -rm -f src/processor/$(DEPDIR)/pathname_stripper.Po + -rm -f src/processor/$(DEPDIR)/pathname_stripper_unittest.Po + -rm -f src/processor/$(DEPDIR)/postfix_evaluator_unittest.Po + -rm -f src/processor/$(DEPDIR)/proc_maps_linux.Po + -rm -f src/processor/$(DEPDIR)/proc_maps_linux_unittest-proc_maps_linux.Po + -rm -f src/processor/$(DEPDIR)/proc_maps_linux_unittest-proc_maps_linux_unittest.Po + -rm -f src/processor/$(DEPDIR)/process_state.Po + -rm -f src/processor/$(DEPDIR)/range_map_truncate_lower_unittest-range_map_truncate_lower_unittest.Po + -rm -f src/processor/$(DEPDIR)/range_map_truncate_upper_unittest-range_map_truncate_upper_unittest.Po + -rm -f src/processor/$(DEPDIR)/range_map_unittest.Po + -rm -f src/processor/$(DEPDIR)/simple_symbol_supplier.Po + -rm -f src/processor/$(DEPDIR)/source_line_resolver_base.Po + -rm -f src/processor/$(DEPDIR)/stack_frame_cpu.Po + -rm -f src/processor/$(DEPDIR)/stack_frame_symbolizer.Po + -rm -f src/processor/$(DEPDIR)/stackwalk_common.Po + -rm -f src/processor/$(DEPDIR)/stackwalker.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_address_list.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_address_list_unittest-stackwalker_address_list_unittest.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_amd64.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_amd64_unittest-stackwalker_amd64_unittest.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_arm.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_arm64.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_arm64_unittest-stackwalker_arm64_unittest.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_arm_unittest-stackwalker_arm_unittest.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_mips.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_mips64_unittest-stackwalker_mips64_unittest.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_mips_unittest-stackwalker_mips_unittest.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_ppc.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_ppc64.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_selftest.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_sparc.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_x86.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_x86_unittest-stackwalker_x86_unittest.Po + -rm -f src/processor/$(DEPDIR)/static_address_map_unittest-static_address_map_unittest.Po + -rm -f src/processor/$(DEPDIR)/static_contained_range_map_unittest-static_contained_range_map_unittest.Po + -rm -f src/processor/$(DEPDIR)/static_map_unittest-static_map_unittest.Po + -rm -f src/processor/$(DEPDIR)/static_range_map_unittest-static_range_map_unittest.Po + -rm -f src/processor/$(DEPDIR)/symbolic_constants_win.Po + -rm -f src/processor/$(DEPDIR)/synth_minidump_unittest-synth_minidump.Po + -rm -f src/processor/$(DEPDIR)/synth_minidump_unittest-synth_minidump_unittest.Po + -rm -f src/processor/$(DEPDIR)/tokenize.Po + -rm -f src/testing/googlemock/src/$(DEPDIR)/client_linux_linux_client_unittest_shlib-gmock-all.Po + -rm -f src/testing/googlemock/src/$(DEPDIR)/libtesting_a-gmock-all.Po + -rm -f src/testing/googletest/src/$(DEPDIR)/client_linux_linux_client_unittest_shlib-gtest-all.Po + -rm -f src/testing/googletest/src/$(DEPDIR)/client_linux_linux_client_unittest_shlib-gtest_main.Po + -rm -f src/testing/googletest/src/$(DEPDIR)/libtesting_a-gtest-all.Po + -rm -f src/testing/googletest/src/$(DEPDIR)/libtesting_a-gtest_main.Po + -rm -f src/third_party/libdisasm/$(DEPDIR)/ia32_implicit.Po + -rm -f src/third_party/libdisasm/$(DEPDIR)/ia32_insn.Po + -rm -f src/third_party/libdisasm/$(DEPDIR)/ia32_invariant.Po + -rm -f src/third_party/libdisasm/$(DEPDIR)/ia32_modrm.Po + -rm -f src/third_party/libdisasm/$(DEPDIR)/ia32_opcode_tables.Po + -rm -f src/third_party/libdisasm/$(DEPDIR)/ia32_operand.Po + -rm -f src/third_party/libdisasm/$(DEPDIR)/ia32_reg.Po + -rm -f src/third_party/libdisasm/$(DEPDIR)/ia32_settings.Po + -rm -f src/third_party/libdisasm/$(DEPDIR)/x86_disasm.Po + -rm -f src/third_party/libdisasm/$(DEPDIR)/x86_format.Po + -rm -f src/third_party/libdisasm/$(DEPDIR)/x86_imm.Po + -rm -f src/third_party/libdisasm/$(DEPDIR)/x86_insn.Po + -rm -f src/third_party/libdisasm/$(DEPDIR)/x86_misc.Po + -rm -f src/third_party/libdisasm/$(DEPDIR)/x86_operand_list.Po + -rm -f src/tools/linux/core2md/$(DEPDIR)/core2md.Po + -rm -f src/tools/linux/core_handler/$(DEPDIR)/core_handler.Po + -rm -f src/tools/linux/dump_syms/$(DEPDIR)/dump_syms-dump_syms.Po + -rm -f src/tools/linux/md2core/$(DEPDIR)/minidump-2-core.Po + -rm -f src/tools/linux/md2core/$(DEPDIR)/minidump_2_core_unittest-minidump_memory_range_unittest.Po + -rm -f src/tools/linux/pid2md/$(DEPDIR)/pid2md.Po + -rm -f src/tools/linux/symupload/$(DEPDIR)/minidump_upload.Po + -rm -f src/tools/linux/symupload/$(DEPDIR)/sym_upload.Po + -rm -f src/tools/mac/dump_syms/$(DEPDIR)/dump_syms_mac-dump_syms_tool.Po + -rm -f Makefile +distclean-am: clean-am distclean-compile distclean-generic \ + distclean-hdr distclean-tags + +dvi: dvi-am + +dvi-am: + +html: html-am + +html-am: + +info: info-am + +info-am: + +install-data-am: install-dist_docDATA install-includecHEADERS \ + install-includeclHEADERS install-includeclcHEADERS \ + install-includecldwcHEADERS install-includeclhHEADERS \ + install-includeclmHEADERS install-includegbcHEADERS \ + install-includelssHEADERS install-includepHEADERS \ + install-pkgconfigDATA + +install-dvi: install-dvi-am + +install-dvi-am: + +install-exec-am: install-binPROGRAMS install-libLIBRARIES \ + install-libexecPROGRAMS + +install-html: install-html-am + +install-html-am: + +install-info: install-info-am + +install-info-am: + +install-man: + +install-pdf: install-pdf-am + +install-pdf-am: + +install-ps: install-ps-am + +install-ps-am: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + -rm -f $(am__CONFIG_DISTCLEAN_FILES) + -rm -rf $(top_srcdir)/autom4te.cache + -rm -f src/client/$(DEPDIR)/minidump_file_writer.Po + -rm -f src/client/linux/crash_generation/$(DEPDIR)/crash_generation_client.Po + -rm -f src/client/linux/crash_generation/$(DEPDIR)/crash_generation_server.Po + -rm -f src/client/linux/dump_writer_common/$(DEPDIR)/thread_info.Po + -rm -f src/client/linux/dump_writer_common/$(DEPDIR)/ucontext_reader.Po + -rm -f src/client/linux/handler/$(DEPDIR)/exception_handler.Po + -rm -f src/client/linux/handler/$(DEPDIR)/linux_client_unittest_shlib-exception_handler_unittest.Po + -rm -f src/client/linux/handler/$(DEPDIR)/minidump_descriptor.Po + -rm -f src/client/linux/log/$(DEPDIR)/log.Po + -rm -f src/client/linux/microdump_writer/$(DEPDIR)/linux_client_unittest_shlib-microdump_writer_unittest.Po + -rm -f src/client/linux/microdump_writer/$(DEPDIR)/microdump_writer.Po + -rm -f src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-cpu_set_unittest.Po + -rm -f src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-directory_reader_unittest.Po + -rm -f src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-line_reader_unittest.Po + -rm -f src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-linux_core_dumper.Po + -rm -f src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-linux_core_dumper_unittest.Po + -rm -f src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-linux_ptrace_dumper_unittest.Po + -rm -f src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-minidump_writer_unittest.Po + -rm -f src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-minidump_writer_unittest_utils.Po + -rm -f src/client/linux/minidump_writer/$(DEPDIR)/linux_client_unittest_shlib-proc_cpuinfo_reader_unittest.Po + -rm -f src/client/linux/minidump_writer/$(DEPDIR)/linux_core_dumper.Po + -rm -f src/client/linux/minidump_writer/$(DEPDIR)/linux_dumper.Po + -rm -f src/client/linux/minidump_writer/$(DEPDIR)/linux_dumper_unittest_helper-linux_dumper_unittest_helper.Po + -rm -f src/client/linux/minidump_writer/$(DEPDIR)/linux_ptrace_dumper.Po + -rm -f src/client/linux/minidump_writer/$(DEPDIR)/minidump_writer.Po + -rm -f src/common/$(DEPDIR)/client_linux_linux_client_unittest_shlib-memory_allocator_unittest.Po + -rm -f src/common/$(DEPDIR)/convert_UTF.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-byte_cursor_unittest.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-convert_UTF.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-dwarf_cfi_to_module.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-dwarf_cfi_to_module_unittest.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-dwarf_cu_to_module.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-dwarf_cu_to_module_unittest.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-dwarf_line_to_module.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-dwarf_line_to_module_unittest.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-dwarf_range_list_handler.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-language.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-memory_range_unittest.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-module.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-module_unittest.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-path_helper.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-stabs_reader.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-stabs_reader_unittest.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-stabs_to_module.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-stabs_to_module_unittest.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-string_conversion.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-string_conversion_unittest.Po + -rm -f src/common/$(DEPDIR)/dumper_unittest-test_assembler.Po + -rm -f src/common/$(DEPDIR)/mac_macho_reader_unittest-dwarf_cfi_to_module.Po + -rm -f src/common/$(DEPDIR)/mac_macho_reader_unittest-dwarf_cu_to_module.Po + -rm -f src/common/$(DEPDIR)/mac_macho_reader_unittest-dwarf_line_to_module.Po + -rm -f src/common/$(DEPDIR)/mac_macho_reader_unittest-language.Po + -rm -f src/common/$(DEPDIR)/mac_macho_reader_unittest-md5.Po + -rm -f src/common/$(DEPDIR)/mac_macho_reader_unittest-module.Po + -rm -f src/common/$(DEPDIR)/mac_macho_reader_unittest-path_helper.Po + -rm -f src/common/$(DEPDIR)/mac_macho_reader_unittest-stabs_reader.Po + -rm -f src/common/$(DEPDIR)/mac_macho_reader_unittest-stabs_to_module.Po + -rm -f src/common/$(DEPDIR)/mac_macho_reader_unittest-test_assembler.Po + -rm -f src/common/$(DEPDIR)/md5.Po + -rm -f src/common/$(DEPDIR)/path_helper.Po + -rm -f src/common/$(DEPDIR)/processor_minidump_unittest-test_assembler.Po + -rm -f src/common/$(DEPDIR)/processor_stackwalker_address_list_unittest-test_assembler.Po + -rm -f src/common/$(DEPDIR)/processor_stackwalker_amd64_unittest-test_assembler.Po + -rm -f src/common/$(DEPDIR)/processor_stackwalker_arm64_unittest-test_assembler.Po + -rm -f src/common/$(DEPDIR)/processor_stackwalker_arm_unittest-test_assembler.Po + -rm -f src/common/$(DEPDIR)/processor_stackwalker_mips64_unittest-test_assembler.Po + -rm -f src/common/$(DEPDIR)/processor_stackwalker_mips_unittest-test_assembler.Po + -rm -f src/common/$(DEPDIR)/processor_stackwalker_x86_unittest-test_assembler.Po + -rm -f src/common/$(DEPDIR)/processor_synth_minidump_unittest-test_assembler.Po + -rm -f src/common/$(DEPDIR)/string_conversion.Po + -rm -f src/common/$(DEPDIR)/test_assembler_unittest-test_assembler.Po + -rm -f src/common/$(DEPDIR)/test_assembler_unittest-test_assembler_unittest.Po + -rm -f src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_cfi_to_module.Po + -rm -f src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_cu_to_module.Po + -rm -f src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_line_to_module.Po + -rm -f src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf_range_list_handler.Po + -rm -f src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-language.Po + -rm -f src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-module.Po + -rm -f src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-path_helper.Po + -rm -f src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-stabs_reader.Po + -rm -f src/common/$(DEPDIR)/tools_linux_dump_syms_dump_syms-stabs_to_module.Po + -rm -f src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_cfi_to_module.Po + -rm -f src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_cu_to_module.Po + -rm -f src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_line_to_module.Po + -rm -f src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf_range_list_handler.Po + -rm -f src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-language.Po + -rm -f src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-md5.Po + -rm -f src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-module.Po + -rm -f src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-path_helper.Po + -rm -f src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-stabs_reader.Po + -rm -f src/common/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-stabs_to_module.Po + -rm -f src/common/dwarf/$(DEPDIR)/dumper_unittest-bytereader.Po + -rm -f src/common/dwarf/$(DEPDIR)/dumper_unittest-bytereader_unittest.Po + -rm -f src/common/dwarf/$(DEPDIR)/dumper_unittest-cfi_assembler.Po + -rm -f src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2diehandler.Po + -rm -f src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2diehandler_unittest.Po + -rm -f src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2reader.Po + -rm -f src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2reader_cfi_unittest.Po + -rm -f src/common/dwarf/$(DEPDIR)/dumper_unittest-dwarf2reader_die_unittest.Po + -rm -f src/common/dwarf/$(DEPDIR)/dumper_unittest-elf_reader.Po + -rm -f src/common/dwarf/$(DEPDIR)/dwarf2reader_lineinfo_unittest-dwarf2reader_lineinfo_unittest.Po + -rm -f src/common/dwarf/$(DEPDIR)/dwarf2reader_splitfunctions_unittest-dwarf2reader_splitfunctions_unittest.Po + -rm -f src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-bytereader.Po + -rm -f src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-cfi_assembler.Po + -rm -f src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-dwarf2diehandler.Po + -rm -f src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-dwarf2reader.Po + -rm -f src/common/dwarf/$(DEPDIR)/mac_macho_reader_unittest-elf_reader.Po + -rm -f src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-bytereader.Po + -rm -f src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf2diehandler.Po + -rm -f src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dwarf2reader.Po + -rm -f src/common/dwarf/$(DEPDIR)/tools_linux_dump_syms_dump_syms-elf_reader.Po + -rm -f src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-bytereader.Po + -rm -f src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf2diehandler.Po + -rm -f src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dwarf2reader.Po + -rm -f src/common/dwarf/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-elf_reader.Po + -rm -f src/common/linux/$(DEPDIR)/breakpad_getcontext.Po + -rm -f src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-breakpad_getcontext.Po + -rm -f src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-breakpad_getcontext_unittest.Po + -rm -f src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-elf_core_dump.Po + -rm -f src/common/linux/$(DEPDIR)/client_linux_linux_client_unittest_shlib-linux_libc_support_unittest.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-crc32.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-dump_symbols.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-dump_symbols_unittest.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-elf_core_dump.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-elf_core_dump_unittest.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-elf_symbols_to_module.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-elf_symbols_to_module_unittest.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-elfutils.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-file_id.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-file_id_unittest.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-linux_libc_support.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-memory_mapped_file.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-memory_mapped_file_unittest.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-safe_readlink.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-safe_readlink_unittest.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-synth_elf.Po + -rm -f src/common/linux/$(DEPDIR)/dumper_unittest-synth_elf_unittest.Po + -rm -f src/common/linux/$(DEPDIR)/elf_core_dump.Po + -rm -f src/common/linux/$(DEPDIR)/elfutils.Po + -rm -f src/common/linux/$(DEPDIR)/file_id.Po + -rm -f src/common/linux/$(DEPDIR)/google_crashdump_uploader_test-google_crashdump_uploader.Po + -rm -f src/common/linux/$(DEPDIR)/google_crashdump_uploader_test-google_crashdump_uploader_test.Po + -rm -f src/common/linux/$(DEPDIR)/google_crashdump_uploader_test-libcurl_wrapper.Po + -rm -f src/common/linux/$(DEPDIR)/guid_creator.Po + -rm -f src/common/linux/$(DEPDIR)/http_upload.Po + -rm -f src/common/linux/$(DEPDIR)/libcurl_wrapper.Po + -rm -f src/common/linux/$(DEPDIR)/linux_libc_support.Po + -rm -f src/common/linux/$(DEPDIR)/memory_mapped_file.Po + -rm -f src/common/linux/$(DEPDIR)/safe_readlink.Po + -rm -f src/common/linux/$(DEPDIR)/symbol_collector_client.Po + -rm -f src/common/linux/$(DEPDIR)/symbol_upload.Po + -rm -f src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-crc32.Po + -rm -f src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-dump_symbols.Po + -rm -f src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-elf_symbols_to_module.Po + -rm -f src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-elfutils.Po + -rm -f src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-file_id.Po + -rm -f src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-linux_libc_support.Po + -rm -f src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-memory_mapped_file.Po + -rm -f src/common/linux/$(DEPDIR)/tools_linux_dump_syms_dump_syms-safe_readlink.Po + -rm -f src/common/linux/tests/$(DEPDIR)/client_linux_linux_client_unittest_shlib-crash_generator.Po + -rm -f src/common/linux/tests/$(DEPDIR)/dumper_unittest-crash_generator.Po + -rm -f src/common/mac/$(DEPDIR)/macho_reader_unittest-arch_utilities.Po + -rm -f src/common/mac/$(DEPDIR)/macho_reader_unittest-file_id.Po + -rm -f src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_id.Po + -rm -f src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_reader.Po + -rm -f src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_reader_unittest.Po + -rm -f src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_utilities.Po + -rm -f src/common/mac/$(DEPDIR)/macho_reader_unittest-macho_walker.Po + -rm -f src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-arch_utilities.Po + -rm -f src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-dump_syms.Po + -rm -f src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-file_id.Po + -rm -f src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_id.Po + -rm -f src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_reader.Po + -rm -f src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_utilities.Po + -rm -f src/common/mac/$(DEPDIR)/tools_mac_dump_syms_dump_syms_mac-macho_walker.Po + -rm -f src/common/tests/$(DEPDIR)/client_linux_linux_client_unittest_shlib-file_utils.Po + -rm -f src/common/tests/$(DEPDIR)/dumper_unittest-file_utils.Po + -rm -f src/common/tests/$(DEPDIR)/mac_macho_reader_unittest-file_utils.Po + -rm -f src/processor/$(DEPDIR)/address_map_unittest.Po + -rm -f src/processor/$(DEPDIR)/basic_code_modules.Po + -rm -f src/processor/$(DEPDIR)/basic_source_line_resolver.Po + -rm -f src/processor/$(DEPDIR)/basic_source_line_resolver_unittest-basic_source_line_resolver_unittest.Po + -rm -f src/processor/$(DEPDIR)/call_stack.Po + -rm -f src/processor/$(DEPDIR)/cfi_frame_info.Po + -rm -f src/processor/$(DEPDIR)/cfi_frame_info_unittest-cfi_frame_info_unittest.Po + -rm -f src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-basic_code_modules.Po + -rm -f src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-convert_old_arm64_context.Po + -rm -f src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-dump_context.Po + -rm -f src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-dump_object.Po + -rm -f src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-logging.Po + -rm -f src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-minidump.Po + -rm -f src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-pathname_stripper.Po + -rm -f src/processor/$(DEPDIR)/client_linux_linux_client_unittest_shlib-proc_maps_linux.Po + -rm -f src/processor/$(DEPDIR)/contained_range_map_unittest.Po + -rm -f src/processor/$(DEPDIR)/convert_old_arm64_context.Po + -rm -f src/processor/$(DEPDIR)/disassembler_x86.Po + -rm -f src/processor/$(DEPDIR)/disassembler_x86_unittest-disassembler_x86_unittest.Po + -rm -f src/processor/$(DEPDIR)/dump_context.Po + -rm -f src/processor/$(DEPDIR)/dump_object.Po + -rm -f src/processor/$(DEPDIR)/exploitability.Po + -rm -f src/processor/$(DEPDIR)/exploitability_linux.Po + -rm -f src/processor/$(DEPDIR)/exploitability_unittest-exploitability_unittest.Po + -rm -f src/processor/$(DEPDIR)/exploitability_win.Po + -rm -f src/processor/$(DEPDIR)/fast_source_line_resolver.Po + -rm -f src/processor/$(DEPDIR)/fast_source_line_resolver_unittest-fast_source_line_resolver_unittest.Po + -rm -f src/processor/$(DEPDIR)/logging.Po + -rm -f src/processor/$(DEPDIR)/map_serializers_unittest-map_serializers_unittest.Po + -rm -f src/processor/$(DEPDIR)/microdump.Po + -rm -f src/processor/$(DEPDIR)/microdump_processor.Po + -rm -f src/processor/$(DEPDIR)/microdump_processor_unittest-microdump_processor_unittest.Po + -rm -f src/processor/$(DEPDIR)/microdump_stackwalk.Po + -rm -f src/processor/$(DEPDIR)/minidump.Po + -rm -f src/processor/$(DEPDIR)/minidump_dump.Po + -rm -f src/processor/$(DEPDIR)/minidump_processor.Po + -rm -f src/processor/$(DEPDIR)/minidump_processor_unittest-minidump_processor_unittest.Po + -rm -f src/processor/$(DEPDIR)/minidump_stackwalk.Po + -rm -f src/processor/$(DEPDIR)/minidump_unittest-minidump_unittest.Po + -rm -f src/processor/$(DEPDIR)/minidump_unittest-synth_minidump.Po + -rm -f src/processor/$(DEPDIR)/module_comparer.Po + -rm -f src/processor/$(DEPDIR)/module_serializer.Po + -rm -f src/processor/$(DEPDIR)/pathname_stripper.Po + -rm -f src/processor/$(DEPDIR)/pathname_stripper_unittest.Po + -rm -f src/processor/$(DEPDIR)/postfix_evaluator_unittest.Po + -rm -f src/processor/$(DEPDIR)/proc_maps_linux.Po + -rm -f src/processor/$(DEPDIR)/proc_maps_linux_unittest-proc_maps_linux.Po + -rm -f src/processor/$(DEPDIR)/proc_maps_linux_unittest-proc_maps_linux_unittest.Po + -rm -f src/processor/$(DEPDIR)/process_state.Po + -rm -f src/processor/$(DEPDIR)/range_map_truncate_lower_unittest-range_map_truncate_lower_unittest.Po + -rm -f src/processor/$(DEPDIR)/range_map_truncate_upper_unittest-range_map_truncate_upper_unittest.Po + -rm -f src/processor/$(DEPDIR)/range_map_unittest.Po + -rm -f src/processor/$(DEPDIR)/simple_symbol_supplier.Po + -rm -f src/processor/$(DEPDIR)/source_line_resolver_base.Po + -rm -f src/processor/$(DEPDIR)/stack_frame_cpu.Po + -rm -f src/processor/$(DEPDIR)/stack_frame_symbolizer.Po + -rm -f src/processor/$(DEPDIR)/stackwalk_common.Po + -rm -f src/processor/$(DEPDIR)/stackwalker.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_address_list.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_address_list_unittest-stackwalker_address_list_unittest.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_amd64.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_amd64_unittest-stackwalker_amd64_unittest.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_arm.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_arm64.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_arm64_unittest-stackwalker_arm64_unittest.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_arm_unittest-stackwalker_arm_unittest.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_mips.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_mips64_unittest-stackwalker_mips64_unittest.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_mips_unittest-stackwalker_mips_unittest.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_ppc.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_ppc64.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_selftest.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_sparc.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_x86.Po + -rm -f src/processor/$(DEPDIR)/stackwalker_x86_unittest-stackwalker_x86_unittest.Po + -rm -f src/processor/$(DEPDIR)/static_address_map_unittest-static_address_map_unittest.Po + -rm -f src/processor/$(DEPDIR)/static_contained_range_map_unittest-static_contained_range_map_unittest.Po + -rm -f src/processor/$(DEPDIR)/static_map_unittest-static_map_unittest.Po + -rm -f src/processor/$(DEPDIR)/static_range_map_unittest-static_range_map_unittest.Po + -rm -f src/processor/$(DEPDIR)/symbolic_constants_win.Po + -rm -f src/processor/$(DEPDIR)/synth_minidump_unittest-synth_minidump.Po + -rm -f src/processor/$(DEPDIR)/synth_minidump_unittest-synth_minidump_unittest.Po + -rm -f src/processor/$(DEPDIR)/tokenize.Po + -rm -f src/testing/googlemock/src/$(DEPDIR)/client_linux_linux_client_unittest_shlib-gmock-all.Po + -rm -f src/testing/googlemock/src/$(DEPDIR)/libtesting_a-gmock-all.Po + -rm -f src/testing/googletest/src/$(DEPDIR)/client_linux_linux_client_unittest_shlib-gtest-all.Po + -rm -f src/testing/googletest/src/$(DEPDIR)/client_linux_linux_client_unittest_shlib-gtest_main.Po + -rm -f src/testing/googletest/src/$(DEPDIR)/libtesting_a-gtest-all.Po + -rm -f src/testing/googletest/src/$(DEPDIR)/libtesting_a-gtest_main.Po + -rm -f src/third_party/libdisasm/$(DEPDIR)/ia32_implicit.Po + -rm -f src/third_party/libdisasm/$(DEPDIR)/ia32_insn.Po + -rm -f src/third_party/libdisasm/$(DEPDIR)/ia32_invariant.Po + -rm -f src/third_party/libdisasm/$(DEPDIR)/ia32_modrm.Po + -rm -f src/third_party/libdisasm/$(DEPDIR)/ia32_opcode_tables.Po + -rm -f src/third_party/libdisasm/$(DEPDIR)/ia32_operand.Po + -rm -f src/third_party/libdisasm/$(DEPDIR)/ia32_reg.Po + -rm -f src/third_party/libdisasm/$(DEPDIR)/ia32_settings.Po + -rm -f src/third_party/libdisasm/$(DEPDIR)/x86_disasm.Po + -rm -f src/third_party/libdisasm/$(DEPDIR)/x86_format.Po + -rm -f src/third_party/libdisasm/$(DEPDIR)/x86_imm.Po + -rm -f src/third_party/libdisasm/$(DEPDIR)/x86_insn.Po + -rm -f src/third_party/libdisasm/$(DEPDIR)/x86_misc.Po + -rm -f src/third_party/libdisasm/$(DEPDIR)/x86_operand_list.Po + -rm -f src/tools/linux/core2md/$(DEPDIR)/core2md.Po + -rm -f src/tools/linux/core_handler/$(DEPDIR)/core_handler.Po + -rm -f src/tools/linux/dump_syms/$(DEPDIR)/dump_syms-dump_syms.Po + -rm -f src/tools/linux/md2core/$(DEPDIR)/minidump-2-core.Po + -rm -f src/tools/linux/md2core/$(DEPDIR)/minidump_2_core_unittest-minidump_memory_range_unittest.Po + -rm -f src/tools/linux/pid2md/$(DEPDIR)/pid2md.Po + -rm -f src/tools/linux/symupload/$(DEPDIR)/minidump_upload.Po + -rm -f src/tools/linux/symupload/$(DEPDIR)/sym_upload.Po + -rm -f src/tools/mac/dump_syms/$(DEPDIR)/dump_syms_mac-dump_syms_tool.Po + -rm -f Makefile +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-local + +pdf: pdf-am + +pdf-am: + +ps: ps-am + +ps-am: + +uninstall-am: uninstall-binPROGRAMS uninstall-dist_docDATA \ + uninstall-includecHEADERS uninstall-includeclHEADERS \ + uninstall-includeclcHEADERS uninstall-includecldwcHEADERS \ + uninstall-includeclhHEADERS uninstall-includeclmHEADERS \ + uninstall-includegbcHEADERS uninstall-includelssHEADERS \ + uninstall-includepHEADERS uninstall-libLIBRARIES \ + uninstall-libexecPROGRAMS uninstall-pkgconfigDATA + +.MAKE: check-am install-am install-strip + +.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles am--refresh check \ + check-TESTS check-am clean clean-binPROGRAMS \ + clean-checkLIBRARIES clean-checkPROGRAMS clean-cscope \ + clean-generic clean-libLIBRARIES clean-libexecPROGRAMS \ + clean-noinstLIBRARIES clean-noinstPROGRAMS cscope \ + cscopelist-am ctags ctags-am dist dist-all dist-bzip2 \ + dist-gzip dist-lzip dist-shar dist-tarZ dist-xz dist-zip \ + dist-zstd distcheck distclean distclean-compile \ + distclean-generic distclean-hdr distclean-tags distcleancheck \ + distdir distuninstallcheck dvi dvi-am html html-am info \ + info-am install install-am install-binPROGRAMS install-data \ + install-data-am install-dist_docDATA install-dvi \ + install-dvi-am install-exec install-exec-am install-html \ + install-html-am install-includecHEADERS \ + install-includeclHEADERS install-includeclcHEADERS \ + install-includecldwcHEADERS install-includeclhHEADERS \ + install-includeclmHEADERS install-includegbcHEADERS \ + install-includelssHEADERS install-includepHEADERS install-info \ + install-info-am install-libLIBRARIES install-libexecPROGRAMS \ + install-man install-pdf install-pdf-am install-pkgconfigDATA \ + install-ps install-ps-am install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-local pdf pdf-am ps ps-am \ + recheck tags tags-am uninstall uninstall-am \ + uninstall-binPROGRAMS uninstall-dist_docDATA \ + uninstall-includecHEADERS uninstall-includeclHEADERS \ + uninstall-includeclcHEADERS uninstall-includecldwcHEADERS \ + uninstall-includeclhHEADERS uninstall-includeclmHEADERS \ + uninstall-includegbcHEADERS uninstall-includelssHEADERS \ + uninstall-includepHEADERS uninstall-libLIBRARIES \ + uninstall-libexecPROGRAMS uninstall-pkgconfigDATA + +.PRECIOUS: Makefile + + +mostlyclean-local: + -find src -name '*.dwo' -exec rm -f {} + + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/shared/sentry/external/breakpad/NEWS b/shared/sentry/external/breakpad/NEWS new file mode 100644 index 000000000..e69de29bb diff --git a/shared/sentry/external/breakpad/OWNERS b/shared/sentry/external/breakpad/OWNERS new file mode 100644 index 000000000..2fde281f9 --- /dev/null +++ b/shared/sentry/external/breakpad/OWNERS @@ -0,0 +1,13 @@ +# Sorted alphabetically. +# Please see README.md for contact info. + +ivanpe@chromium.org +jperaza@chromium.org +mark@chromium.org +nbilling@google.com +primiano@chromium.org +saugustine@google.com +rsesek@chromium.org +ted@mielczarek.org +thestig@chromium.org +vapier@chromium.org diff --git a/shared/sentry/external/breakpad/README.ANDROID b/shared/sentry/external/breakpad/README.ANDROID new file mode 100644 index 000000000..30959ed3a --- /dev/null +++ b/shared/sentry/external/breakpad/README.ANDROID @@ -0,0 +1,139 @@ +Google Breakpad for Android +=========================== + +This document explains how to use the Google Breakpad client library +on Android, and later generate valid stack traces from the minidumps +it generates. + +This release supports ARM, x86 and MIPS based Android systems. +This release requires NDK release r11c or higher. + +I. Building the client library: +=============================== + +The Android client is built as a static library that you can +link into your own Android native code. There are two ways to +build it: + +I.1. Building with ndk-build: +----------------------------- + +If you're using the ndk-build build system, you can follow +these simple steps: + + 1/ Include android/google_breakpad/Android.mk from your own + project's Android.mk + + This can be done either directly, or using ndk-build's + import-module feature. + + 2/ Link the library to one of your modules by using: + + LOCAL_STATIC_LIBRARIES += breakpad_client + +NOTE: The client library requires a C++ STL implementation, + which you can select with APP_STL in your Application.mk + + It has been tested succesfully with both STLport and GNU libstdc++ + + +I.2. Building with a standalone Android toolchain: +-------------------------------------------------- + +All you need to do is configure your build with the right 'host' +value, and disable the processor and tools, as in: + + $GOOGLE_BREAKPAD_PATH/configure --host=arm-linux-androideabi \ + --disable-processor \ + --disable-tools + make -j4 + +The library will be under src/client/linux/libbreakpad_client.a + +You can also use 'make check' to run the test suite on a connected +Android device. This requires the Android 'adb' tool to be in your +path. + +II. Using the client library in Android: +======================================== + +The usage instructions are very similar to the Linux ones that are +found at https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/linux_starter_guide.md + +1/ You need to include "client/linux/handler/exception_handler.h" from a C++ + source file. + +2/ If you're not using ndk-build, you also need to: + + - add the following to your compiler include search paths: + $GOOGLE_BREAKPAD_PATH/src + $GOOGLE_BREAKPAD_PATH/src/common/android/include + + - add -llog to your linker flags + + Note that ndk-build does that for your automatically. + +3/ Keep in mind that there is no /tmp directory on Android. + + If you use the library from a regular Android applications, specify a + path under your app-specific storage directory. An alternative is to + store them on the SDCard, but this requires a specific permission. + +For a concrete example, see the sample test application under +android/sample_app. See its README for more information. + + +III. Getting a stack trace on the host: +======================================= + +This process is similar to other platforms, but here's a quick example: + +1/ Retrieve the minidumps on your development machine. + +2/ Dump the symbols for your native libraries with the 'dump_syms' tool. + This first requires building the host version of Google Breakpad, then + calling: + + dump_syms $PROJECT_PATH/obj/local/$ABI/libfoo.so > libfoo.so.sym + +3/ Create the symbol directory hierarchy. + + The first line of the generated libfoo.so.sym will have a "MODULE" + entry that carries a hexadecimal version number, e.g.: + + MODULE Linux arm D51B4A5504974FA6ECC1869CAEE3603B0 test_google_breakpad + + Note: The second field could be either 'Linux' or 'Android'. + + Extract the version number, and a 'symbol' directory, for example: + + $PROJECT_PATH/symbols/libfoo.so/$VERSION/ + + Copy/Move your libfoo.sym file there. + +4/ Invoke minidump_stackwalk to create the stack trace: + + minidump_stackwalk $MINIDUMP_FILE $PROJECT_PATH/symbols + +Note that various helper scripts can be found on the web to automate these +steps. + +IV. Verifying the Android build library: +======================================== + +If you modify Google Breakpad and want to check that it still works correctly +on Android, please run the android/run-checks.sh script which will do all +necessary verifications for you. This includes: + + - Rebuilding the full host binaries. + - Rebuilding the full Android binaries with configure/make. + - Rebuilding the client library unit tests, and running them on a device. + - Rebuilding the client library with ndk-build. + - Building, installing and running a test crasher program on a device. + - Extracting the corresponding minidump, dumping the test program symbols + and generating a stack trace. + - Checking the generated stack trace for valid source locations. + +For more details, please run: + + android/run-checks.sh --help-all diff --git a/shared/sentry/external/breakpad/README.md b/shared/sentry/external/breakpad/README.md new file mode 100644 index 000000000..a9094a27a --- /dev/null +++ b/shared/sentry/external/breakpad/README.md @@ -0,0 +1,82 @@ +# Breakpad + +Breakpad is a set of client and server components which implement a +crash-reporting system. + +* [Homepage](https://chromium.googlesource.com/breakpad/breakpad/) +* [Documentation](./docs/) +* [Bugs](https://bugs.chromium.org/p/google-breakpad/) +* Discussion/Questions: [google-breakpad-discuss@googlegroups.com](https://groups.google.com/d/forum/google-breakpad-discuss) +* Developer/Reviews: [google-breakpad-dev@googlegroups.com](https://groups.google.com/d/forum/google-breakpad-dev) +* Tests: [![Build+Test CI](https://github.com/google/breakpad/actions/workflows/build-test-ci.yml/badge.svg)](https://github.com/google/breakpad/actions/workflows/build-test-ci.yml) [![Build status](https://ci.appveyor.com/api/projects/status/eguv4emv2rhq68u2?svg=true)](https://ci.appveyor.com/project/vapier/breakpad) +* Coverage [![Coverity Status](https://scan.coverity.com/projects/9215/badge.svg)](https://scan.coverity.com/projects/google-breakpad) + +## Getting started (from main) + +1. First, [download depot_tools](http://dev.chromium.org/developers/how-tos/install-depot-tools) + and ensure that they’re in your `PATH`. + +2. Create a new directory for checking out the source code (it must be named + breakpad). + + ```sh + mkdir breakpad && cd breakpad + ``` + +3. Run the `fetch` tool from depot_tools to download all the source repos. + + ```sh + fetch breakpad + cd src + ``` + +4. Build the source. + + ```sh + ./configure && make + ``` + + You can also cd to another directory and run configure from there to build + outside the source tree. + + This will build the processor tools (`src/processor/minidump_stackwalk`, + `src/processor/minidump_dump`, etc), and when building on Linux it will + also build the client libraries and some tools + (`src/tools/linux/dump_syms/dump_syms`, + `src/tools/linux/md2core/minidump-2-core`, etc). + +5. Optionally, run tests. + + ```sh + make check + ``` + +6. Optionally, install the built libraries + + ```sh + make install + ``` + +If you need to reconfigure your build be sure to run `make distclean` first. + +To update an existing checkout to a newer revision, you can +`git pull` as usual, but then you should run `gclient sync` to ensure that the +dependent repos are up-to-date. + +## To request change review + +1. Follow the steps above to get the source and build it. + +2. Make changes. Build and test your changes. + For core code like processor use methods above. + For linux/mac/windows, there are test targets in each project file. + +3. Commit your changes to your local repo and upload them to the server. + http://dev.chromium.org/developers/contributing-code + e.g. `git commit ... && git cl upload ...` + You will be prompted for credential and a description. + +4. At https://chromium-review.googlesource.com/ you'll find your issue listed; + click on it, then “Add reviewerâ€, and enter in the code reviewer. Depending + on your settings, you may not see an email, but the reviewer has been + notified with google-breakpad-dev@googlegroups.com always CC’d. diff --git a/shared/sentry/external/breakpad/aclocal.m4 b/shared/sentry/external/breakpad/aclocal.m4 new file mode 100644 index 000000000..be219b421 --- /dev/null +++ b/shared/sentry/external/breakpad/aclocal.m4 @@ -0,0 +1,1284 @@ +# generated automatically by aclocal 1.16.3 -*- Autoconf -*- + +# Copyright (C) 1996-2020 Free Software Foundation, Inc. + +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +m4_ifndef([AC_CONFIG_MACRO_DIRS], [m4_defun([_AM_CONFIG_MACRO_DIRS], [])m4_defun([AC_CONFIG_MACRO_DIRS], [_AM_CONFIG_MACRO_DIRS($@)])]) +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +m4_if(m4_defn([AC_AUTOCONF_VERSION]), [2.69],, +[m4_warning([this file was generated for autoconf 2.69. +You have another version of autoconf. It may work, but is not guaranteed to. +If you have problems, you may need to regenerate the build system entirely. +To do so, use the procedure documented by the package, typically 'autoreconf'.])]) + +# Copyright (C) 2002-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_AUTOMAKE_VERSION(VERSION) +# ---------------------------- +# Automake X.Y traces this macro to ensure aclocal.m4 has been +# generated from the m4 files accompanying Automake X.Y. +# (This private macro should not be called outside this file.) +AC_DEFUN([AM_AUTOMAKE_VERSION], +[am__api_version='1.16' +dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to +dnl require some minimum version. Point them to the right macro. +m4_if([$1], [1.16.3], [], + [AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl +]) + +# _AM_AUTOCONF_VERSION(VERSION) +# ----------------------------- +# aclocal traces this macro to find the Autoconf version. +# This is a private macro too. Using m4_define simplifies +# the logic in aclocal, which can simply ignore this definition. +m4_define([_AM_AUTOCONF_VERSION], []) + +# AM_SET_CURRENT_AUTOMAKE_VERSION +# ------------------------------- +# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced. +# This function is AC_REQUIREd by AM_INIT_AUTOMAKE. +AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION], +[AM_AUTOMAKE_VERSION([1.16.3])dnl +m4_ifndef([AC_AUTOCONF_VERSION], + [m4_copy([m4_PACKAGE_VERSION], [AC_AUTOCONF_VERSION])])dnl +_AM_AUTOCONF_VERSION(m4_defn([AC_AUTOCONF_VERSION]))]) + +# Copyright (C) 2011-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_AR([ACT-IF-FAIL]) +# ------------------------- +# Try to determine the archiver interface, and trigger the ar-lib wrapper +# if it is needed. If the detection of archiver interface fails, run +# ACT-IF-FAIL (default is to abort configure with a proper error message). +AC_DEFUN([AM_PROG_AR], +[AC_BEFORE([$0], [LT_INIT])dnl +AC_BEFORE([$0], [AC_PROG_LIBTOOL])dnl +AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([ar-lib])dnl +AC_CHECK_TOOLS([AR], [ar lib "link -lib"], [false]) +: ${AR=ar} + +AC_CACHE_CHECK([the archiver ($AR) interface], [am_cv_ar_interface], + [AC_LANG_PUSH([C]) + am_cv_ar_interface=ar + AC_COMPILE_IFELSE([AC_LANG_SOURCE([[int some_variable = 0;]])], + [am_ar_try='$AR cru libconftest.a conftest.$ac_objext >&AS_MESSAGE_LOG_FD' + AC_TRY_EVAL([am_ar_try]) + if test "$ac_status" -eq 0; then + am_cv_ar_interface=ar + else + am_ar_try='$AR -NOLOGO -OUT:conftest.lib conftest.$ac_objext >&AS_MESSAGE_LOG_FD' + AC_TRY_EVAL([am_ar_try]) + if test "$ac_status" -eq 0; then + am_cv_ar_interface=lib + else + am_cv_ar_interface=unknown + fi + fi + rm -f conftest.lib libconftest.a + ]) + AC_LANG_POP([C])]) + +case $am_cv_ar_interface in +ar) + ;; +lib) + # Microsoft lib, so override with the ar-lib wrapper script. + # FIXME: It is wrong to rewrite AR. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__AR in this case, + # and then we could set am__AR="$am_aux_dir/ar-lib \$(AR)" or something + # similar. + AR="$am_aux_dir/ar-lib $AR" + ;; +unknown) + m4_default([$1], + [AC_MSG_ERROR([could not determine $AR interface])]) + ;; +esac +AC_SUBST([AR])dnl +]) + +# Figure out how to run the assembler. -*- Autoconf -*- + +# Copyright (C) 2001-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_AS +# ---------- +AC_DEFUN([AM_PROG_AS], +[# By default we simply use the C compiler to build assembly code. +AC_REQUIRE([AC_PROG_CC]) +test "${CCAS+set}" = set || CCAS=$CC +test "${CCASFLAGS+set}" = set || CCASFLAGS=$CFLAGS +AC_ARG_VAR([CCAS], [assembler compiler command (defaults to CC)]) +AC_ARG_VAR([CCASFLAGS], [assembler compiler flags (defaults to CFLAGS)]) +_AM_IF_OPTION([no-dependencies],, [_AM_DEPENDENCIES([CCAS])])dnl +]) + +# AM_AUX_DIR_EXPAND -*- Autoconf -*- + +# Copyright (C) 2001-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# For projects using AC_CONFIG_AUX_DIR([foo]), Autoconf sets +# $ac_aux_dir to '$srcdir/foo'. In other projects, it is set to +# '$srcdir', '$srcdir/..', or '$srcdir/../..'. +# +# Of course, Automake must honor this variable whenever it calls a +# tool from the auxiliary directory. The problem is that $srcdir (and +# therefore $ac_aux_dir as well) can be either absolute or relative, +# depending on how configure is run. This is pretty annoying, since +# it makes $ac_aux_dir quite unusable in subdirectories: in the top +# source directory, any form will work fine, but in subdirectories a +# relative path needs to be adjusted first. +# +# $ac_aux_dir/missing +# fails when called from a subdirectory if $ac_aux_dir is relative +# $top_srcdir/$ac_aux_dir/missing +# fails if $ac_aux_dir is absolute, +# fails when called from a subdirectory in a VPATH build with +# a relative $ac_aux_dir +# +# The reason of the latter failure is that $top_srcdir and $ac_aux_dir +# are both prefixed by $srcdir. In an in-source build this is usually +# harmless because $srcdir is '.', but things will broke when you +# start a VPATH build or use an absolute $srcdir. +# +# So we could use something similar to $top_srcdir/$ac_aux_dir/missing, +# iff we strip the leading $srcdir from $ac_aux_dir. That would be: +# am_aux_dir='\$(top_srcdir)/'`expr "$ac_aux_dir" : "$srcdir//*\(.*\)"` +# and then we would define $MISSING as +# MISSING="\${SHELL} $am_aux_dir/missing" +# This will work as long as MISSING is not called from configure, because +# unfortunately $(top_srcdir) has no meaning in configure. +# However there are other variables, like CC, which are often used in +# configure, and could therefore not use this "fixed" $ac_aux_dir. +# +# Another solution, used here, is to always expand $ac_aux_dir to an +# absolute PATH. The drawback is that using absolute paths prevent a +# configured tree to be moved without reconfiguration. + +AC_DEFUN([AM_AUX_DIR_EXPAND], +[AC_REQUIRE([AC_CONFIG_AUX_DIR_DEFAULT])dnl +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` +]) + +# AM_CONDITIONAL -*- Autoconf -*- + +# Copyright (C) 1997-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_CONDITIONAL(NAME, SHELL-CONDITION) +# ------------------------------------- +# Define a conditional. +AC_DEFUN([AM_CONDITIONAL], +[AC_PREREQ([2.52])dnl + m4_if([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])], + [$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl +AC_SUBST([$1_TRUE])dnl +AC_SUBST([$1_FALSE])dnl +_AM_SUBST_NOTMAKE([$1_TRUE])dnl +_AM_SUBST_NOTMAKE([$1_FALSE])dnl +m4_define([_AM_COND_VALUE_$1], [$2])dnl +if $2; then + $1_TRUE= + $1_FALSE='#' +else + $1_TRUE='#' + $1_FALSE= +fi +AC_CONFIG_COMMANDS_PRE( +[if test -z "${$1_TRUE}" && test -z "${$1_FALSE}"; then + AC_MSG_ERROR([[conditional "$1" was never defined. +Usually this means the macro was only invoked conditionally.]]) +fi])]) + +# Copyright (C) 1999-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + + +# There are a few dirty hacks below to avoid letting 'AC_PROG_CC' be +# written in clear, in which case automake, when reading aclocal.m4, +# will think it sees a *use*, and therefore will trigger all it's +# C support machinery. Also note that it means that autoscan, seeing +# CC etc. in the Makefile, will ask for an AC_PROG_CC use... + + +# _AM_DEPENDENCIES(NAME) +# ---------------------- +# See how the compiler implements dependency checking. +# NAME is "CC", "CXX", "OBJC", "OBJCXX", "UPC", or "GJC". +# We try a few techniques and use that to set a single cache variable. +# +# We don't AC_REQUIRE the corresponding AC_PROG_CC since the latter was +# modified to invoke _AM_DEPENDENCIES(CC); we would have a circular +# dependency, and given that the user is not expected to run this macro, +# just rely on AC_PROG_CC. +AC_DEFUN([_AM_DEPENDENCIES], +[AC_REQUIRE([AM_SET_DEPDIR])dnl +AC_REQUIRE([AM_OUTPUT_DEPENDENCY_COMMANDS])dnl +AC_REQUIRE([AM_MAKE_INCLUDE])dnl +AC_REQUIRE([AM_DEP_TRACK])dnl + +m4_if([$1], [CC], [depcc="$CC" am_compiler_list=], + [$1], [CXX], [depcc="$CXX" am_compiler_list=], + [$1], [OBJC], [depcc="$OBJC" am_compiler_list='gcc3 gcc'], + [$1], [OBJCXX], [depcc="$OBJCXX" am_compiler_list='gcc3 gcc'], + [$1], [UPC], [depcc="$UPC" am_compiler_list=], + [$1], [GCJ], [depcc="$GCJ" am_compiler_list='gcc3 gcc'], + [depcc="$$1" am_compiler_list=]) + +AC_CACHE_CHECK([dependency style of $depcc], + [am_cv_$1_dependencies_compiler_type], +[if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_$1_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n ['s/^#*\([a-zA-Z0-9]*\))$/\1/p'] < ./depcomp` + fi + am__universal=false + m4_case([$1], [CC], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac], + [CXX], + [case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac]) + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_$1_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_$1_dependencies_compiler_type=none +fi +]) +AC_SUBST([$1DEPMODE], [depmode=$am_cv_$1_dependencies_compiler_type]) +AM_CONDITIONAL([am__fastdep$1], [ + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_$1_dependencies_compiler_type" = gcc3]) +]) + + +# AM_SET_DEPDIR +# ------------- +# Choose a directory name for dependency files. +# This macro is AC_REQUIREd in _AM_DEPENDENCIES. +AC_DEFUN([AM_SET_DEPDIR], +[AC_REQUIRE([AM_SET_LEADING_DOT])dnl +AC_SUBST([DEPDIR], ["${am__leading_dot}deps"])dnl +]) + + +# AM_DEP_TRACK +# ------------ +AC_DEFUN([AM_DEP_TRACK], +[AC_ARG_ENABLE([dependency-tracking], [dnl +AS_HELP_STRING( + [--enable-dependency-tracking], + [do not reject slow dependency extractors]) +AS_HELP_STRING( + [--disable-dependency-tracking], + [speeds up one-time build])]) +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi +AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno]) +AC_SUBST([AMDEPBACKSLASH])dnl +_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl +AC_SUBST([am__nodep])dnl +_AM_SUBST_NOTMAKE([am__nodep])dnl +]) + +# Generate code to set up dependency tracking. -*- Autoconf -*- + +# Copyright (C) 1999-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_OUTPUT_DEPENDENCY_COMMANDS +# ------------------------------ +AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS], +[{ + # Older Autoconf quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + # TODO: see whether this extra hack can be removed once we start + # requiring Autoconf 2.70 or later. + AS_CASE([$CONFIG_FILES], + [*\'*], [eval set x "$CONFIG_FILES"], + [*], [set x $CONFIG_FILES]) + shift + # Used to flag and report bootstrapping failures. + am_rc=0 + for am_mf + do + # Strip MF so we end up with the name of the file. + am_mf=`AS_ECHO(["$am_mf"]) | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile which includes + # dependency-tracking related rules and includes. + # Grep'ing the whole file directly is not great: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ + || continue + am_dirpart=`AS_DIRNAME(["$am_mf"])` + am_filepart=`AS_BASENAME(["$am_mf"])` + AM_RUN_LOG([cd "$am_dirpart" \ + && sed -e '/# am--include-marker/d' "$am_filepart" \ + | $MAKE -f - am--depfiles]) || am_rc=$? + done + if test $am_rc -ne 0; then + AC_MSG_FAILURE([Something went wrong bootstrapping makefile fragments + for automatic dependency tracking. If GNU make was not used, consider + re-running the configure script with MAKE="gmake" (or whatever is + necessary). You can also try re-running configure with the + '--disable-dependency-tracking' option to at least be able to build + the package (albeit without support for automatic dependency tracking).]) + fi + AS_UNSET([am_dirpart]) + AS_UNSET([am_filepart]) + AS_UNSET([am_mf]) + AS_UNSET([am_rc]) + rm -f conftest-deps.mk +} +])# _AM_OUTPUT_DEPENDENCY_COMMANDS + + +# AM_OUTPUT_DEPENDENCY_COMMANDS +# ----------------------------- +# This macro should only be invoked once -- use via AC_REQUIRE. +# +# This code is only required when automatic dependency tracking is enabled. +# This creates each '.Po' and '.Plo' makefile fragment that we'll need in +# order to bootstrap the dependency handling code. +AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS], +[AC_CONFIG_COMMANDS([depfiles], + [test x"$AMDEP_TRUE" != x"" || _AM_OUTPUT_DEPENDENCY_COMMANDS], + [AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}"])]) + +# Do all the work for Automake. -*- Autoconf -*- + +# Copyright (C) 1996-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This macro actually does too much. Some checks are only needed if +# your package does certain things. But this isn't really a big deal. + +dnl Redefine AC_PROG_CC to automatically invoke _AM_PROG_CC_C_O. +m4_define([AC_PROG_CC], +m4_defn([AC_PROG_CC]) +[_AM_PROG_CC_C_O +]) + +# AM_INIT_AUTOMAKE(PACKAGE, VERSION, [NO-DEFINE]) +# AM_INIT_AUTOMAKE([OPTIONS]) +# ----------------------------------------------- +# The call with PACKAGE and VERSION arguments is the old style +# call (pre autoconf-2.50), which is being phased out. PACKAGE +# and VERSION should now be passed to AC_INIT and removed from +# the call to AM_INIT_AUTOMAKE. +# We support both call styles for the transition. After +# the next Automake release, Autoconf can make the AC_INIT +# arguments mandatory, and then we can depend on a new Autoconf +# release and drop the old call support. +AC_DEFUN([AM_INIT_AUTOMAKE], +[AC_PREREQ([2.65])dnl +dnl Autoconf wants to disallow AM_ names. We explicitly allow +dnl the ones we care about. +m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl +AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl +AC_REQUIRE([AC_PROG_INSTALL])dnl +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + AC_MSG_ERROR([source directory already configured; run "make distclean" there first]) + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi +AC_SUBST([CYGPATH_W]) + +# Define the identity of the package. +dnl Distinguish between old-style and new-style calls. +m4_ifval([$2], +[AC_DIAGNOSE([obsolete], + [$0: two- and three-arguments forms are deprecated.]) +m4_ifval([$3], [_AM_SET_OPTION([no-define])])dnl + AC_SUBST([PACKAGE], [$1])dnl + AC_SUBST([VERSION], [$2])], +[_AM_SET_OPTIONS([$1])dnl +dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT. +m4_if( + m4_ifdef([AC_PACKAGE_NAME], [ok]):m4_ifdef([AC_PACKAGE_VERSION], [ok]), + [ok:ok],, + [m4_fatal([AC_INIT should be called with package and version arguments])])dnl + AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl + AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl + +_AM_IF_OPTION([no-define],, +[AC_DEFINE_UNQUOTED([PACKAGE], ["$PACKAGE"], [Name of package]) + AC_DEFINE_UNQUOTED([VERSION], ["$VERSION"], [Version number of package])])dnl + +# Some tools Automake needs. +AC_REQUIRE([AM_SANITY_CHECK])dnl +AC_REQUIRE([AC_ARG_PROGRAM])dnl +AM_MISSING_PROG([ACLOCAL], [aclocal-${am__api_version}]) +AM_MISSING_PROG([AUTOCONF], [autoconf]) +AM_MISSING_PROG([AUTOMAKE], [automake-${am__api_version}]) +AM_MISSING_PROG([AUTOHEADER], [autoheader]) +AM_MISSING_PROG([MAKEINFO], [makeinfo]) +AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +AC_REQUIRE([AM_PROG_INSTALL_STRIP])dnl +AC_REQUIRE([AC_PROG_MKDIR_P])dnl +# For better backward compatibility. To be removed once Automake 1.9.x +# dies out for good. For more background, see: +# +# +AC_SUBST([mkdir_p], ['$(MKDIR_P)']) +# We need awk for the "check" target (and possibly the TAP driver). The +# system "awk" is bad on some platforms. +AC_REQUIRE([AC_PROG_AWK])dnl +AC_REQUIRE([AC_PROG_MAKE_SET])dnl +AC_REQUIRE([AM_SET_LEADING_DOT])dnl +_AM_IF_OPTION([tar-ustar], [_AM_PROG_TAR([ustar])], + [_AM_IF_OPTION([tar-pax], [_AM_PROG_TAR([pax])], + [_AM_PROG_TAR([v7])])]) +_AM_IF_OPTION([no-dependencies],, +[AC_PROVIDE_IFELSE([AC_PROG_CC], + [_AM_DEPENDENCIES([CC])], + [m4_define([AC_PROG_CC], + m4_defn([AC_PROG_CC])[_AM_DEPENDENCIES([CC])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_CXX], + [_AM_DEPENDENCIES([CXX])], + [m4_define([AC_PROG_CXX], + m4_defn([AC_PROG_CXX])[_AM_DEPENDENCIES([CXX])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJC], + [_AM_DEPENDENCIES([OBJC])], + [m4_define([AC_PROG_OBJC], + m4_defn([AC_PROG_OBJC])[_AM_DEPENDENCIES([OBJC])])])dnl +AC_PROVIDE_IFELSE([AC_PROG_OBJCXX], + [_AM_DEPENDENCIES([OBJCXX])], + [m4_define([AC_PROG_OBJCXX], + m4_defn([AC_PROG_OBJCXX])[_AM_DEPENDENCIES([OBJCXX])])])dnl +]) +AC_REQUIRE([AM_SILENT_RULES])dnl +dnl The testsuite driver may need to know about EXEEXT, so add the +dnl 'am__EXEEXT' conditional if _AM_COMPILER_EXEEXT was seen. This +dnl macro is hooked onto _AC_COMPILER_EXEEXT early, see below. +AC_CONFIG_COMMANDS_PRE(dnl +[m4_provide_if([_AM_COMPILER_EXEEXT], + [AM_CONDITIONAL([am__EXEEXT], [test -n "$EXEEXT"])])])dnl + +# POSIX will say in a future version that running "rm -f" with no argument +# is OK; and we want to be able to make that assumption in our Makefile +# recipes. So use an aggressive probe to check that the usage we want is +# actually supported "in the wild" to an acceptable degree. +# See automake bug#10828. +# To make any issue more visible, cause the running configure to be aborted +# by default if the 'rm' program in use doesn't match our expectations; the +# user can still override this though. +if rm -f && rm -fr && rm -rf; then : OK; else + cat >&2 <<'END' +Oops! + +Your 'rm' program seems unable to run without file operands specified +on the command line, even when the '-f' option is present. This is contrary +to the behaviour of most rm programs out there, and not conforming with +the upcoming POSIX standard: + +Please tell bug-automake@gnu.org about your system, including the value +of your $PATH and any error possibly output before this message. This +can help us improve future automake versions. + +END + if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then + echo 'Configuration will proceed anyway, since you have set the' >&2 + echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 + echo >&2 + else + cat >&2 <<'END' +Aborting the configuration process, to ensure you take notice of the issue. + +You can download and install GNU coreutils to get an 'rm' implementation +that behaves properly: . + +If you want to complete the configuration process using your problematic +'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM +to "yes", and re-run configure. + +END + AC_MSG_ERROR([Your 'rm' program is bad, sorry.]) + fi +fi +dnl The trailing newline in this macro's definition is deliberate, for +dnl backward compatibility and to allow trailing 'dnl'-style comments +dnl after the AM_INIT_AUTOMAKE invocation. See automake bug#16841. +]) + +dnl Hook into '_AC_COMPILER_EXEEXT' early to learn its expansion. Do not +dnl add the conditional right here, as _AC_COMPILER_EXEEXT may be further +dnl mangled by Autoconf and run in a shell conditional statement. +m4_define([_AC_COMPILER_EXEEXT], +m4_defn([_AC_COMPILER_EXEEXT])[m4_provide([_AM_COMPILER_EXEEXT])]) + +# When config.status generates a header, we must update the stamp-h file. +# This file resides in the same directory as the config header +# that is generated. The stamp files are numbered to have different names. + +# Autoconf calls _AC_AM_CONFIG_HEADER_HOOK (when defined) in the +# loop where config.status creates the headers, so we can generate +# our stamp files there. +AC_DEFUN([_AC_AM_CONFIG_HEADER_HOOK], +[# Compute $1's index in $config_headers. +_am_arg=$1 +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`AS_DIRNAME(["$_am_arg"])`/stamp-h[]$_am_stamp_count]) + +# Copyright (C) 2001-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_SH +# ------------------ +# Define $install_sh. +AC_DEFUN([AM_PROG_INSTALL_SH], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +if test x"${install_sh+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi +AC_SUBST([install_sh])]) + +# Copyright (C) 2003-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# Check whether the underlying file-system supports filenames +# with a leading dot. For instance MS-DOS doesn't. +AC_DEFUN([AM_SET_LEADING_DOT], +[rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null +AC_SUBST([am__leading_dot])]) + +# Add --enable-maintainer-mode option to configure. -*- Autoconf -*- +# From Jim Meyering + +# Copyright (C) 1996-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_MAINTAINER_MODE([DEFAULT-MODE]) +# ---------------------------------- +# Control maintainer-specific portions of Makefiles. +# Default is to disable them, unless 'enable' is passed literally. +# For symmetry, 'disable' may be passed as well. Anyway, the user +# can override the default with the --enable/--disable switch. +AC_DEFUN([AM_MAINTAINER_MODE], +[m4_case(m4_default([$1], [disable]), + [enable], [m4_define([am_maintainer_other], [disable])], + [disable], [m4_define([am_maintainer_other], [enable])], + [m4_define([am_maintainer_other], [enable]) + m4_warn([syntax], [unexpected argument to AM@&t@_MAINTAINER_MODE: $1])]) +AC_MSG_CHECKING([whether to enable maintainer-specific portions of Makefiles]) + dnl maintainer-mode's default is 'disable' unless 'enable' is passed + AC_ARG_ENABLE([maintainer-mode], + [AS_HELP_STRING([--]am_maintainer_other[-maintainer-mode], + am_maintainer_other[ make rules and dependencies not useful + (and sometimes confusing) to the casual installer])], + [USE_MAINTAINER_MODE=$enableval], + [USE_MAINTAINER_MODE=]m4_if(am_maintainer_other, [enable], [no], [yes])) + AC_MSG_RESULT([$USE_MAINTAINER_MODE]) + AM_CONDITIONAL([MAINTAINER_MODE], [test $USE_MAINTAINER_MODE = yes]) + MAINT=$MAINTAINER_MODE_TRUE + AC_SUBST([MAINT])dnl +] +) + +# Check to see how 'make' treats includes. -*- Autoconf -*- + +# Copyright (C) 2001-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_MAKE_INCLUDE() +# ----------------- +# Check whether make has an 'include' directive that can support all +# the idioms we need for our automatic dependency tracking code. +AC_DEFUN([AM_MAKE_INCLUDE], +[AC_MSG_CHECKING([whether ${MAKE-make} supports the include directive]) +cat > confinc.mk << 'END' +am__doit: + @echo this is the am__doit target >confinc.out +.PHONY: am__doit +END +am__include="#" +am__quote= +# BSD make does it like this. +echo '.include "confinc.mk" # ignored' > confmf.BSD +# Other make implementations (GNU, Solaris 10, AIX) do it like this. +echo 'include confinc.mk # ignored' > confmf.GNU +_am_result=no +for s in GNU BSD; do + AM_RUN_LOG([${MAKE-make} -f confmf.$s && cat confinc.out]) + AS_CASE([$?:`cat confinc.out 2>/dev/null`], + ['0:this is the am__doit target'], + [AS_CASE([$s], + [BSD], [am__include='.include' am__quote='"'], + [am__include='include' am__quote=''])]) + if test "$am__include" != "#"; then + _am_result="yes ($s style)" + break + fi +done +rm -f confinc.* confmf.* +AC_MSG_RESULT([${_am_result}]) +AC_SUBST([am__include])]) +AC_SUBST([am__quote])]) + +# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*- + +# Copyright (C) 1997-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_MISSING_PROG(NAME, PROGRAM) +# ------------------------------ +AC_DEFUN([AM_MISSING_PROG], +[AC_REQUIRE([AM_MISSING_HAS_RUN]) +$1=${$1-"${am_missing_run}$2"} +AC_SUBST($1)]) + +# AM_MISSING_HAS_RUN +# ------------------ +# Define MISSING if not defined so far and test if it is modern enough. +# If it is, set am_missing_run to use it, otherwise, to nothing. +AC_DEFUN([AM_MISSING_HAS_RUN], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([missing])dnl +if test x"${MISSING+set}" != xset; then + MISSING="\${SHELL} '$am_aux_dir/missing'" +fi +# Use eval to expand $SHELL +if eval "$MISSING --is-lightweight"; then + am_missing_run="$MISSING " +else + am_missing_run= + AC_MSG_WARN(['missing' script is too old or missing]) +fi +]) + +# -*- Autoconf -*- +# Obsolete and "removed" macros, that must however still report explicit +# error messages when used, to smooth transition. +# +# Copyright (C) 1996-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +AC_DEFUN([AM_CONFIG_HEADER], +[AC_DIAGNOSE([obsolete], +['$0': this macro is obsolete. +You should use the 'AC][_CONFIG_HEADERS' macro instead.])dnl +AC_CONFIG_HEADERS($@)]) + +AC_DEFUN([AM_PROG_CC_STDC], +[AC_PROG_CC +am_cv_prog_cc_stdc=$ac_cv_prog_cc_stdc +AC_DIAGNOSE([obsolete], +['$0': this macro is obsolete. +You should simply use the 'AC][_PROG_CC' macro instead. +Also, your code should no longer depend upon 'am_cv_prog_cc_stdc', +but upon 'ac_cv_prog_cc_stdc'.])]) + +AC_DEFUN([AM_C_PROTOTYPES], + [AC_FATAL([automatic de-ANSI-fication support has been removed])]) +AU_DEFUN([fp_C_PROTOTYPES], [AM_C_PROTOTYPES]) + +# Helper functions for option handling. -*- Autoconf -*- + +# Copyright (C) 2001-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_MANGLE_OPTION(NAME) +# ----------------------- +AC_DEFUN([_AM_MANGLE_OPTION], +[[_AM_OPTION_]m4_bpatsubst($1, [[^a-zA-Z0-9_]], [_])]) + +# _AM_SET_OPTION(NAME) +# -------------------- +# Set option NAME. Presently that only means defining a flag for this option. +AC_DEFUN([_AM_SET_OPTION], +[m4_define(_AM_MANGLE_OPTION([$1]), [1])]) + +# _AM_SET_OPTIONS(OPTIONS) +# ------------------------ +# OPTIONS is a space-separated list of Automake options. +AC_DEFUN([_AM_SET_OPTIONS], +[m4_foreach_w([_AM_Option], [$1], [_AM_SET_OPTION(_AM_Option)])]) + +# _AM_IF_OPTION(OPTION, IF-SET, [IF-NOT-SET]) +# ------------------------------------------- +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +AC_DEFUN([_AM_IF_OPTION], +[m4_ifset(_AM_MANGLE_OPTION([$1]), [$2], [$3])]) + +# Copyright (C) 1999-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_PROG_CC_C_O +# --------------- +# Like AC_PROG_CC_C_O, but changed for automake. We rewrite AC_PROG_CC +# to automatically call this. +AC_DEFUN([_AM_PROG_CC_C_O], +[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl +AC_REQUIRE_AUX_FILE([compile])dnl +AC_LANG_PUSH([C])dnl +AC_CACHE_CHECK( + [whether $CC understands -c and -o together], + [am_cv_prog_cc_c_o], + [AC_LANG_CONFTEST([AC_LANG_PROGRAM([])]) + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if AM_RUN_LOG([$CC -c conftest.$ac_ext -o conftest2.$ac_objext]) \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i]) +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +AC_LANG_POP([C])]) + +# For backward compatibility. +AC_DEFUN_ONCE([AM_PROG_CC_C_O], [AC_REQUIRE([AC_PROG_CC])]) + +# Copyright (C) 2001-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_RUN_LOG(COMMAND) +# ------------------- +# Run COMMAND, save the exit status in ac_status, and log it. +# (This has been adapted from Autoconf's _AC_RUN_LOG macro.) +AC_DEFUN([AM_RUN_LOG], +[{ echo "$as_me:$LINENO: $1" >&AS_MESSAGE_LOG_FD + ($1) >&AS_MESSAGE_LOG_FD 2>&AS_MESSAGE_LOG_FD + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + (exit $ac_status); }]) + +# Check to make sure that the build environment is sane. -*- Autoconf -*- + +# Copyright (C) 1996-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_SANITY_CHECK +# --------------- +AC_DEFUN([AM_SANITY_CHECK], +[AC_MSG_CHECKING([whether build environment is sane]) +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[[\\\"\#\$\&\'\`$am_lf]]*) + AC_MSG_ERROR([unsafe absolute working directory name]);; +esac +case $srcdir in + *[[\\\"\#\$\&\'\`$am_lf\ \ ]]*) + AC_MSG_ERROR([unsafe srcdir value: '$srcdir']);; +esac + +# Do 'set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$[*]" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$[*]" != "X $srcdir/configure conftest.file" \ + && test "$[*]" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + AC_MSG_ERROR([ls -t appears to fail. Make sure there is not a broken + alias in your environment]) + fi + if test "$[2]" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done + test "$[2]" = conftest.file + ) +then + # Ok. + : +else + AC_MSG_ERROR([newly created file is older than distributed files! +Check your system clock]) +fi +AC_MSG_RESULT([yes]) +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi +AC_CONFIG_COMMANDS_PRE( + [AC_MSG_CHECKING([that generated files are newer than configure]) + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + AC_MSG_RESULT([done])]) +rm -f conftest.file +]) + +# Copyright (C) 2009-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_SILENT_RULES([DEFAULT]) +# -------------------------- +# Enable less verbose build rules; with the default set to DEFAULT +# ("yes" being less verbose, "no" or empty being verbose). +AC_DEFUN([AM_SILENT_RULES], +[AC_ARG_ENABLE([silent-rules], [dnl +AS_HELP_STRING( + [--enable-silent-rules], + [less verbose build output (undo: "make V=1")]) +AS_HELP_STRING( + [--disable-silent-rules], + [verbose build output (undo: "make V=0")])dnl +]) +case $enable_silent_rules in @%:@ ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=m4_if([$1], [yes], [0], [1]);; +esac +dnl +dnl A few 'make' implementations (e.g., NonStop OS and NextStep) +dnl do not support nested variable expansions. +dnl See automake bug#9928 and bug#10237. +am_make=${MAKE-make} +AC_CACHE_CHECK([whether $am_make supports nested variables], + [am_cv_make_support_nested_variables], + [if AS_ECHO([['TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit']]) | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi]) +if test $am_cv_make_support_nested_variables = yes; then + dnl Using '$V' instead of '$(V)' breaks IRIX make. + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AC_SUBST([AM_V])dnl +AM_SUBST_NOTMAKE([AM_V])dnl +AC_SUBST([AM_DEFAULT_V])dnl +AM_SUBST_NOTMAKE([AM_DEFAULT_V])dnl +AC_SUBST([AM_DEFAULT_VERBOSITY])dnl +AM_BACKSLASH='\' +AC_SUBST([AM_BACKSLASH])dnl +_AM_SUBST_NOTMAKE([AM_BACKSLASH])dnl +]) + +# Copyright (C) 2001-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# AM_PROG_INSTALL_STRIP +# --------------------- +# One issue with vendor 'install' (even GNU) is that you can't +# specify the program used to strip binaries. This is especially +# annoying in cross-compiling environments, where the build's strip +# is unlikely to handle the host's binaries. +# Fortunately install-sh will honor a STRIPPROG variable, so we +# always use install-sh in "make install-strip", and initialize +# STRIPPROG with the value of the STRIP variable (set by the user). +AC_DEFUN([AM_PROG_INSTALL_STRIP], +[AC_REQUIRE([AM_PROG_INSTALL_SH])dnl +# Installed binaries are usually stripped using 'strip' when the user +# run "make install-strip". However 'strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the 'STRIP' environment variable to overrule this program. +dnl Don't test for $cross_compiling = yes, because it might be 'maybe'. +if test "$cross_compiling" != no; then + AC_CHECK_TOOL([STRIP], [strip], :) +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" +AC_SUBST([INSTALL_STRIP_PROGRAM])]) + +# Copyright (C) 2006-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_SUBST_NOTMAKE(VARIABLE) +# --------------------------- +# Prevent Automake from outputting VARIABLE = @VARIABLE@ in Makefile.in. +# This macro is traced by Automake. +AC_DEFUN([_AM_SUBST_NOTMAKE]) + +# AM_SUBST_NOTMAKE(VARIABLE) +# -------------------------- +# Public sister of _AM_SUBST_NOTMAKE. +AC_DEFUN([AM_SUBST_NOTMAKE], [_AM_SUBST_NOTMAKE($@)]) + +# Check how to create a tarball. -*- Autoconf -*- + +# Copyright (C) 2004-2020 Free Software Foundation, Inc. +# +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# _AM_PROG_TAR(FORMAT) +# -------------------- +# Check how to create a tarball in format FORMAT. +# FORMAT should be one of 'v7', 'ustar', or 'pax'. +# +# Substitute a variable $(am__tar) that is a command +# writing to stdout a FORMAT-tarball containing the directory +# $tardir. +# tardir=directory && $(am__tar) > result.tar +# +# Substitute a variable $(am__untar) that extract such +# a tarball read from stdin. +# $(am__untar) < result.tar +# +AC_DEFUN([_AM_PROG_TAR], +[# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AC_SUBST([AMTAR], ['$${TAR-tar}']) + +# We'll loop over all known methods to create a tar archive until one works. +_am_tools='gnutar m4_if([$1], [ustar], [plaintar]) pax cpio none' + +m4_if([$1], [v7], + [am__tar='$${TAR-tar} chof - "$$tardir"' am__untar='$${TAR-tar} xf -'], + + [m4_case([$1], + [ustar], + [# The POSIX 1988 'ustar' format is defined with fixed-size fields. + # There is notably a 21 bits limit for the UID and the GID. In fact, + # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 + # and bug#13588). + am_max_uid=2097151 # 2^21 - 1 + am_max_gid=$am_max_uid + # The $UID and $GID variables are not portable, so we need to resort + # to the POSIX-mandated id(1) utility. Errors in the 'id' calls + # below are definitely unexpected, so allow the users to see them + # (that is, avoid stderr redirection). + am_uid=`id -u || echo unknown` + am_gid=`id -g || echo unknown` + AC_MSG_CHECKING([whether UID '$am_uid' is supported by ustar format]) + if test $am_uid -le $am_max_uid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi + AC_MSG_CHECKING([whether GID '$am_gid' is supported by ustar format]) + if test $am_gid -le $am_max_gid; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + _am_tools=none + fi], + + [pax], + [], + + [m4_fatal([Unknown tar format])]) + + AC_MSG_CHECKING([how to create a $1 tar archive]) + + # Go ahead even if we have the value already cached. We do so because we + # need to set the values for the 'am__tar' and 'am__untar' variables. + _am_tools=${am_cv_prog_tar_$1-$_am_tools} + + for _am_tool in $_am_tools; do + case $_am_tool in + gnutar) + for _am_tar in tar gnutar gtar; do + AM_RUN_LOG([$_am_tar --version]) && break + done + am__tar="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$$tardir"' + am__tar_="$_am_tar --format=m4_if([$1], [pax], [posix], [$1]) -chf - "'"$tardir"' + am__untar="$_am_tar -xf -" + ;; + plaintar) + # Must skip GNU tar: if it does not support --format= it doesn't create + # ustar tarball either. + (tar --version) >/dev/null 2>&1 && continue + am__tar='tar chf - "$$tardir"' + am__tar_='tar chf - "$tardir"' + am__untar='tar xf -' + ;; + pax) + am__tar='pax -L -x $1 -w "$$tardir"' + am__tar_='pax -L -x $1 -w "$tardir"' + am__untar='pax -r' + ;; + cpio) + am__tar='find "$$tardir" -print | cpio -o -H $1 -L' + am__tar_='find "$tardir" -print | cpio -o -H $1 -L' + am__untar='cpio -i -H $1 -d' + ;; + none) + am__tar=false + am__tar_=false + am__untar=false + ;; + esac + + # If the value was cached, stop now. We just wanted to have am__tar + # and am__untar set. + test -n "${am_cv_prog_tar_$1}" && break + + # tar/untar a dummy directory, and stop if the command works. + rm -rf conftest.dir + mkdir conftest.dir + echo GrepMe > conftest.dir/file + AM_RUN_LOG([tardir=conftest.dir && eval $am__tar_ >conftest.tar]) + rm -rf conftest.dir + if test -s conftest.tar; then + AM_RUN_LOG([$am__untar /dev/null 2>&1 && break + fi + done + rm -rf conftest.dir + + AC_CACHE_VAL([am_cv_prog_tar_$1], [am_cv_prog_tar_$1=$_am_tool]) + AC_MSG_RESULT([$am_cv_prog_tar_$1])]) + +AC_SUBST([am__tar]) +AC_SUBST([am__untar]) +]) # _AM_PROG_TAR + +m4_include([m4/ax_append_compile_flags.m4]) +m4_include([m4/ax_append_flag.m4]) +m4_include([m4/ax_check_compile_flag.m4]) +m4_include([m4/ax_check_define.m4]) +m4_include([m4/ax_cxx_compile_stdcxx.m4]) +m4_include([m4/ax_pthread.m4]) +m4_include([m4/ax_require_defined.m4]) diff --git a/shared/sentry/external/breakpad/android/common-functions.sh b/shared/sentry/external/breakpad/android/common-functions.sh new file mode 100755 index 000000000..c00e34f99 --- /dev/null +++ b/shared/sentry/external/breakpad/android/common-functions.sh @@ -0,0 +1,372 @@ +# 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. + +# Collection of common shell functions for 'run-checks.sh' et 'test-shell.sh' + +# All internal variables and functions use an underscore as a prefix +# (e.g. _VERBOSE, _ALL_CLEANUPS, etc..). + +# Sanitize the environment +export LANG=C +export LC_ALL=C + +if [ "$BASH_VERSION" ]; then + set -o posix +fi + +# Utility functions + +_ALL_CLEANUPS= + +# Register a function to be called when the script exits, even in case of +# Ctrl-C, logout, etc. +# $1: function name. +atexit () { + if [ -z "$_ALL_CLEANUPS" ]; then + _ALL_CLEANUPS=$1 + # Ensure a clean exit when the script is: + # - Exiting normally (EXIT) + # - Interrupted by Ctrl-C (INT) + # - Interrupted by log out (HUP) + # - Being asked to quit nicely (TERM) + # - Being asked to quit and dump core (QUIT) + trap "_exit_cleanups \$?" EXIT INT HUP QUIT TERM + else + _ALL_CLEANUPS="$_ALL_CLEANUPS $1" + fi +} + +# Called on exit if at least one function was registered with atexit +# $1: final exit status code +_exit_cleanups () { + local CLEANUP CLEANUPS + # Ignore calls to atexit during cleanups + CLEANUPS=$_ALL_CLEANUPS + _ALL_CLEANUPS= + for CLEANUP in $CLEANUPS; do + ($CLEANUP) + done + exit "$@" +} + + + + +# Dump a panic message then exit. +# $1+: message +panic () { + echo "ERROR: $@" >&2 + exit 1 +} + +# If the previous command failed, dump a panic message then exit. +# $1+: message. +fail_panic () { + if [ $? != 0 ]; then + panic "$@" + fi; +} + +_VERBOSE=0 + +# Increase verbosity for dump/log/run/run2 functions +increase_verbosity () { + _VERBOSE=$(( $_VERBOSE + 1 )) +} + +# Decrease verbosity +decrease_verbosity () { + _VERBOSE=$(( $_VERBOSE - 1 )) +} + +# Returns success iff verbosity level is higher than a specific value +# $1: verbosity level +verbosity_is_higher_than () { + [ "$_VERBOSE" -gt "$1" ] +} + +# Returns success iff verbosity level is lower than a specific value +# $1: verbosity level +verbosity_is_lower_than () { + [ "$_VERBOSE" -le "$1" ] +} + +# Dump message to stdout, unless verbosity is < 0, i.e. --quiet was called +# $1+: message +dump () { + if [ "$_VERBOSE" -ge 0 ]; then + printf "%s\n" "$*" + fi +} + +# If --verbose was used, dump a message to stdout. +# $1+: message +log () { + if [ "$_VERBOSE" -ge 1 ]; then + printf "%s\n" "$*" + fi +} + +_RUN_LOG= + +# Set a run log file that can be used to collect the output of commands that +# are not displayed. +set_run_log () { + _RUN_LOG=$1 +} + +# Run a command. Output depends on $_VERBOSE: +# $_VERBOSE <= 0: Run command, store output into the run log +# $_VERBOSE >= 1: Dump command, run it, output goest to stdout +# Note: Ideally, the command's output would go to the run log for $_VERBOSE >= 1 +# but the 'tee' tool doesn't preserve the status code of its input pipe +# in case of error. +run () { + local LOGILE + if [ "$_RUN_LOG" ]; then + LOGFILE=$_RUN_LOG + else + LOGFILE=/dev/null + fi + + if [ "$_VERBOSE" -ge 1 ]; then + echo "COMMAND: $@" + "$@" + else + "$@" >>$LOGFILE 2>&1 + fi +} + +# Same as run(), but only dump command output for $_VERBOSE >= 2 +run2 () { + local LOGILE + if [ "$_RUN_LOG" ]; then + LOGFILE=$_RUN_LOG + else + LOGFILE=/dev/null + fi + + if [ "$_VERBOSE" -ge 1 ]; then + echo "COMMAND: $@" + fi + if [ "$_VERBOSE" -ge 2 ]; then + "$@" + else + "$@" >>$LOGFILE 2>&1 + fi +} + +# Extract number of cores to speed up the builds +# Out: number of CPU cores +get_core_count () { + case $(uname -s) in + Linux) + grep -c -e '^processor' /proc/cpuinfo + ;; + Darwin) + sysctl -n hw.ncpu + ;; + CYGWIN*|*_NT-*) + echo $NUMBER_OF_PROCESSORS + ;; + *) + echo 1 + ;; + esac +} + + +# Check for the Android ADB program. +# +# On success, return nothing, but updates internal variables so later calls to +# adb_shell, adb_push, etc.. will work. You can get the path to the ADB program +# with adb_get_program if needed. +# +# On failure, returns 1, and updates the internal adb error message, which can +# be retrieved with adb_get_error. +# +# $1: optional ADB program path. +# Return: success or failure. +_ADB= +_ADB_STATUS= +_ADB_ERROR= + +adb_check () { + # First, try to find the executable in the path, or the SDK install dir. + _ADB=$1 + if [ -z "$_ADB" ]; then + _ADB=$(which adb 2>/dev/null) + if [ -z "$_ADB" -a "$ANDROID_SDK_ROOT" ]; then + _ADB=$ANDROID_SDK_ROOT/platform-tools/adb + if [ ! -f "$_ADB" ]; then + _ADB= + fi + fi + if [ -z "$_ADB" ]; then + _ADB_STATUS=1 + _ADB_ERROR="The Android 'adb' tool is not in your path." + return 1 + fi + fi + + log "Found ADB program: $_ADB" + + # Check that it works correctly + local ADB_VERSION + ADB_VERSION=$("$_ADB" version 2>/dev/null) + case $ADB_VERSION in + "Android Debug Bridge "*) # Pass + log "Found ADB version: $ADB_VERSION" + ;; + *) # Fail + _ADB_ERROR="Your ADB binary reports a bad version ($ADB_VERSION): $_ADB" + _ADB_STATUS=1 + return 1 + esac + + _ADB_STATUS=0 + return 0 +} + + +# Return the path to the Android ADB program, if correctly detected. +# On failure, return the empty string. +# Out: ADB program path (or empty on failure) +# Return: success or failure. +adb_get_program () { + # Return cached value as soon as possible. + if [ -z "$_ADB_STATUS" ]; then + adb_check $1 + fi + echo "$_ADB" + return $_ADB_STATUS +} + +# Return the error corresponding to the last ADB function failure. +adb_get_error () { + echo "$_ADB_ERROR" +} + +# Check that there is one device connected through ADB. +# In case of failure, use adb_get_error to know why this failed. +# $1: Optional adb program path +# Return: success or failure. +_ADB_DEVICE= +_ADB_DEVICE_STATUS= +adb_check_device () { + if [ "$_ADB_DEVICE_STATUS" ]; then + return $_ADB_DEVICE_STATUS + fi + + # Check for ADB. + if ! adb_check $1; then + _ADB_DEVICE_STATUS=$_ADB_STATUS + return 1 + fi + + local ADB_DEVICES NUM_DEVICES FINGERPRINT + + # Count the number of connected devices. + ADB_DEVICES=$("$_ADB" devices 2>/dev/null | awk '$2 == "device" { print $1; }') + NUM_DEVICES=$(echo "$ADB_DEVICES" | wc -l) + case $NUM_DEVICES in + 0) + _ADB_ERROR="No Android device connected. Please connect one to your machine." + _ADB_DEVICE_STATUS=1 + return 1 + ;; + 1) # Pass + # Ensure the same device will be called in later adb_shell calls. + export ANDROID_SERIAL=$ADB_DEVICES + ;; + *) # 2 or more devices. + if [ "$ANDROID_SERIAL" ]; then + ADB_DEVICES=$ANDROID_SERIAL + NUM_DEVICES=1 + else + _ADB_ERROR="More than one Android device connected. \ +Please define ANDROID_SERIAL in your environment" + _ADB_DEVICE_STATUS=1 + return 1 + fi + ;; + esac + + _ADB_DEVICE_STATUS=0 + _ADB_DEVICE=$ADB_DEVICES + + FINGERPRINT=$(adb_shell getprop ro.build.fingerprint) + log "Using ADB device: $ANDROID_SERIAL ($FINGERPRINT)" + return 0 +} + +# The 'adb shell' command is pretty hopeless, try to make sense of it by: +# 1/ Removing trailing \r from line endings. +# 2/ Ensuring the function returns the command's status code. +# +# $1+: Command +# Out: command output (stdout + stderr combined) +# Return: command exit status +adb_shell () { + local RET ADB_LOG + # Check for ADB device. + adb_check_device || return 1 + ADB_LOG=$(mktemp "${TMPDIR:-/tmp}/adb-XXXXXXXX") + "$_ADB" shell "$@" ";" echo \$? > "$ADB_LOG" 2>&1 + sed -i -e 's![[:cntrl:]]!!g' "$ADB_LOG" # Remove \r. + RET=$(sed -e '$!d' "$ADB_LOG") # Last line contains status code. + sed -e '$d' "$ADB_LOG" # Print everything except last line. + rm -f "$ADB_LOG" + return $RET +} + +# Push a file to a device. +# $1: source file path +# $2: device target file path +# Return: success or failure. +adb_push () { + adb_check_device || return 1 + run "$_ADB" push "$1" "$2" +} + +# Pull a file from a device +# $1: device file path +# $2: target host file path +# Return: success or failure. +adb_pull () { + adb_check_device || return 1 + run "$_ADB" pull "$1" "$2" +} + +# Same as adb_push, but will panic if the operations didn't succeed. +adb_install () { + adb_push "$@" + fail_panic "Failed to install $1 to the Android device at $2" +} + diff --git a/shared/sentry/external/breakpad/android/google_breakpad/Android.mk b/shared/sentry/external/breakpad/android/google_breakpad/Android.mk new file mode 100644 index 000000000..861849277 --- /dev/null +++ b/shared/sentry/external/breakpad/android/google_breakpad/Android.mk @@ -0,0 +1,104 @@ +# 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. + +# ndk-build module definition for the Google Breakpad client library +# +# To use this file, do the following: +# +# 1/ Include this file from your own Android.mk, either directly +# or with through the NDK's import-module function. +# +# 2/ Use the client static library in your project with: +# +# LOCAL_STATIC_LIBRARIES += breakpad_client +# +# 3/ In your source code, include "src/client/linux/exception_handler.h" +# and use the Linux instructions to use it. +# +# This module works with either the STLport or GNU libstdc++, but you need +# to select one in your Application.mk +# + +# The top Google Breakpad directory. +# We assume this Android.mk to be under 'android/google_breakpad' + +LOCAL_PATH := $(call my-dir)/../.. + +# Defube the client library module, as a simple static library that +# exports the right include path / linker flags to its users. + +include $(CLEAR_VARS) + +LOCAL_MODULE := breakpad_client + +LOCAL_CPP_EXTENSION := .cc + +# Breakpad uses inline ARM assembly that requires the library +# to be built in ARM mode. Otherwise, the build will fail with +# cryptic assembler messages like: +# Compile++ thumb : google_breakpad_client <= crash_generation_client.cc +# /tmp/cc8aMSoD.s: Assembler messages: +# /tmp/cc8aMSoD.s:132: Error: invalid immediate: 288 is out of range +# /tmp/cc8aMSoD.s:244: Error: invalid immediate: 296 is out of range +LOCAL_ARM_MODE := arm + +# List of client source files, directly taken from Makefile.am +LOCAL_SRC_FILES := \ + src/client/linux/crash_generation/crash_generation_client.cc \ + src/client/linux/dump_writer_common/thread_info.cc \ + src/client/linux/dump_writer_common/ucontext_reader.cc \ + src/client/linux/handler/exception_handler.cc \ + src/client/linux/handler/minidump_descriptor.cc \ + src/client/linux/log/log.cc \ + src/client/linux/microdump_writer/microdump_writer.cc \ + src/client/linux/minidump_writer/linux_dumper.cc \ + src/client/linux/minidump_writer/linux_ptrace_dumper.cc \ + src/client/linux/minidump_writer/minidump_writer.cc \ + src/client/minidump_file_writer.cc \ + src/common/convert_UTF.cc \ + src/common/md5.cc \ + src/common/string_conversion.cc \ + src/common/linux/breakpad_getcontext.S \ + src/common/linux/elfutils.cc \ + src/common/linux/file_id.cc \ + src/common/linux/guid_creator.cc \ + src/common/linux/linux_libc_support.cc \ + src/common/linux/memory_mapped_file.cc \ + src/common/linux/safe_readlink.cc + +LOCAL_C_INCLUDES := $(LOCAL_PATH)/src/common/android/include \ + $(LOCAL_PATH)/src \ + $(LSS_PATH) + +LOCAL_EXPORT_C_INCLUDES := $(LOCAL_C_INCLUDES) +LOCAL_EXPORT_LDLIBS := -llog + +include $(BUILD_STATIC_LIBRARY) + +# Done. diff --git a/shared/sentry/external/breakpad/android/run-checks.sh b/shared/sentry/external/breakpad/android/run-checks.sh new file mode 100755 index 000000000..51d2d5023 --- /dev/null +++ b/shared/sentry/external/breakpad/android/run-checks.sh @@ -0,0 +1,555 @@ +#!/bin/sh +# 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. + +# Sanitize the environment +export LANG=C +export LC_ALL=C + +if [ "$BASH_VERSION" ]; then + set -o posix +fi + +PROGDIR=$(dirname "$0") +PROGDIR=$(cd "$PROGDIR" && pwd) +PROGNAME=$(basename "$0") + +. $PROGDIR/common-functions.sh + +DEFAULT_ABI="armeabi" +VALID_ABIS="armeabi armeabi-v7a x86 mips" + +ABI= +ADB= +ALL_TESTS= +ENABLE_M32= +HELP= +HELP_ALL= +NDK_DIR= +NO_CLEANUP= +NO_DEVICE= +NUM_JOBS=$(get_core_count) +TMPDIR= + +for opt do + # The following extracts the value if the option is like --name=. + optarg=$(expr -- $opt : '^--[^=]*=\(.*\)$') + case $opt in + --abi=*) ABI=$optarg;; + --adb=*) ADB=$optarg;; + --all-tests) ALL_TESTS=true;; + --enable-m32) ENABLE_M32=true;; + --help|-h|-?) HELP=TRUE;; + --help-all) HELP_ALL=true;; + --jobs=*) NUM_JOBS=$optarg;; + --ndk-dir=*) NDK_DIR=$optarg;; + --tmp-dir=*) TMPDIR=$optarg;; + --no-cleanup) NO_CLEANUP=true;; + --no-device) NO_DEVICE=true;; + --quiet) decrease_verbosity;; + --verbose) increase_verbosity;; + -*) panic "Invalid option '$opt', see --help for details.";; + *) panic "This script doesn't take any parameters. See --help for details." + ;; + esac +done + +if [ "$HELP" -o "$HELP_ALL" ]; then + echo "\ + Usage: $PROGNAME [options] + + This script is used to check that your Google Breakpad source tree can + be properly built for Android, and that the client library and host tools + work properly together. +" + if [ "$HELP_ALL" ]; then + echo "\ + In more details, this script will: + + - Rebuild the host version of Google Breakpad in a temporary + directory (with the Auto-tools based build system). + + - Rebuild the Android client library with the Google Breakpad build + system (using autotools/configure). This requires that you define + ANDROID_NDK_ROOT in your environment to point to a valid Android NDK + installation directory, or use the --ndk-dir= option. + + - Rebuild the Android client library and a test crashing program with the + Android NDK build system (ndk-build). + + - Require an Android device connected to your machine, and the 'adb' + tool in your path. They are used to: + + - Install and run a test crashing program. + - Extract the corresponding minidump from the device. + - Dump the symbols from the test program on the host with 'dump_syms' + - Generate a stack trace with 'minidump_stackwalk' + - Check the stack trace content for valid source file locations. + + You can however skip this requirement and only test the builds by using + the --no-device flag. + + By default, all generated files will be created in a temporary directory + that is removed when the script completion. If you want to inspect the + files, use the --no-cleanup option. + + Finally, use --verbose to increase the verbosity level, this will help + you see which exact commands are being issues and their result. Use the + flag twice for even more output. Use --quiet to decrease verbosity + instead and run the script silently. + + If you have a device connected, the script will probe it to determine + its primary CPU ABI, and build the test program for it. You can however + use the --abi= option to override this (this can be useful to check + the secondary ABI, e.g. using --abi=armeabi to check that such a program + works correctly on an ARMv7-A device). + + If you don't have a device connected, the test program will be built (but + not run) with the default '$DEFAULT_ABI' ABI. Again, you can use + --abi= to override this. Valid ABI names are: + + $VALID_ABIS + + The script will only run the client library unit test on the device + by default. You can use --all-tests to also build and run the unit + tests for the Breakpad tools and processor, but be warned that this + adds several minutes of testing time. --all-tests will also run the + host unit tests suite. +" + + fi # HELP_ALL + + echo "\ + Valid options: + + --help|-h|-? Display this message. + --help-all Display extended help. + --enable-m32 Build 32-bit version of host tools. + --abi= Specify target CPU ABI [auto-detected]. + --jobs= Run build tasks in parallel [$NUM_JOBS]. + --ndk-dir= Specify NDK installation directory. + --tmp-dir= Specify temporary directory (will be wiped-out). + --adb= Specify adb program path. + --no-cleanup Don't remove temporary directory after completion. + --no-device Do not try to detect devices, nor run crash test. + --all-tests Run all unit tests (i.e. tools and processor ones too). + --verbose Increase verbosity. + --quiet Decrease verbosity." + + exit 0 +fi + +TESTAPP_DIR=$PROGDIR/sample_app + +# Select NDK install directory. +if [ -z "$NDK_DIR" ]; then + if [ -z "$ANDROID_NDK_ROOT" ]; then + panic "Please define ANDROID_NDK_ROOT in your environment, or use \ +--ndk-dir=." + fi + NDK_DIR="$ANDROID_NDK_ROOT" + log "Found NDK directory: $NDK_DIR" +else + log "Using NDK directory: $NDK_DIR" +fi +# Small sanity check. +NDK_BUILD="$NDK_DIR/ndk-build" +if [ ! -f "$NDK_BUILD" ]; then + panic "Your NDK directory is not valid (missing ndk-build): $NDK_DIR" +fi + +# Ensure the temporary directory is deleted on exit, except if the --no-cleanup +# option is used. + +clean_tmpdir () { + if [ "$TMPDIR" ]; then + if [ -z "$NO_CLEANUP" ]; then + log "Cleaning up: $TMPDIR" + rm -rf "$TMPDIR" + else + dump "Temporary directory contents preserved: $TMPDIR" + fi + fi + exit "$@" +} + +atexit clean_tmpdir + +# If --tmp-dir= is not used, create a temporary directory. +# Otherwise, start by cleaning up the user-provided path. +if [ -z "$TMPDIR" ]; then + TMPDIR=$(mktemp -d /tmp/$PROGNAME.XXXXXXXX) + fail_panic "Can't create temporary directory!" + log "Using temporary directory: $TMPDIR" +else + if [ ! -d "$TMPDIR" ]; then + mkdir -p "$TMPDIR" + fail_panic "Can't create temporary directory: $TMPDIR" + else + log "Cleaning up temporary directory: $TMPDIR" + rm -rf "$TMPDIR"/* + fail_panic "Cannot cleanup temporary directory!" + fi +fi + +if [ -z "$NO_DEVICE" ]; then + if ! adb_check_device $ADB; then + echo "$(adb_get_error)" + echo "Use --no-device to build the code without running any tests." + exit 1 + fi +fi + +BUILD_LOG="$TMPDIR/build.log" +RUN_LOG="$TMPDIR/run.log" +CRASH_LOG="$TMPDIR/crash.log" + +set_run_log "$RUN_LOG" + +TMPHOST="$TMPDIR/host-local" + +cd "$TMPDIR" + +# Build host version of the tools +dump "Building host binaries." +CONFIGURE_FLAGS= +if [ "$ENABLE_M32" ]; then + CONFIGURE_FLAGS="$CONFIGURE_FLAGS --enable-m32" +fi +( + run mkdir "$TMPDIR/build-host" && + run cd "$TMPDIR/build-host" && + run2 "$PROGDIR/../configure" --prefix="$TMPHOST" $CONFIGURE_FLAGS && + run2 make -j$NUM_JOBS install +) +fail_panic "Can't build host binaries!" + +if [ "$ALL_TESTS" ]; then + dump "Running host unit tests." + ( + run cd "$TMPDIR/build-host" && + run2 make -j$NUM_JOBS check + ) + fail_panic "Host unit tests failed!!" +fi + +TMPBIN=$TMPHOST/bin + +# Generate a stand-alone NDK toolchain + +# Extract CPU ABI and architecture from device, if any. +if adb_check_device; then + DEVICE_ABI=$(adb_shell getprop ro.product.cpu.abi) + DEVICE_ABI2=$(adb_shell getprop ro.product.cpu.abi2) + if [ -z "$DEVICE_ABI" ]; then + panic "Can't extract ABI from connected device!" + fi + if [ "$DEVICE_ABI2" ]; then + dump "Found device ABIs: $DEVICE_ABI $DEVICE_ABI2" + else + dump "Found device ABI: $DEVICE_ABI" + DEVICE_ABI2=$DEVICE_ABI + fi + + # If --abi= is used, check that the device supports it. + if [ "$ABI" -a "$DEVICE_ABI" != "$ABI" -a "$DEVICE_ABI2" != "$ABI" ]; then + dump "ERROR: Device ABI(s) do not match --abi command-line value ($ABI)!" + panic "Please use --no-device to skip device tests." + fi + + if [ -z "$ABI" ]; then + ABI=$DEVICE_ABI + dump "Using CPU ABI: $ABI (device)" + else + dump "Using CPU ABI: $ABI (command-line)" + fi +else + if [ -z "$ABI" ]; then + # No device connected, choose default ABI + ABI=$DEFAULT_ABI + dump "Using CPU ABI: $ABI (default)" + else + dump "Using CPU ABI: $ABI (command-line)" + fi +fi + +# Check the ABI value +VALID= +for VALID_ABI in $VALID_ABIS; do + if [ "$ABI" = "$VALID_ABI" ]; then + VALID=true + break + fi +done + +if [ -z "$VALID" ]; then + panic "Unknown CPU ABI '$ABI'. Valid values are: $VALID_ABIS" +fi + +# Extract architecture name from ABI +case $ABI in + armeabi*) ARCH=arm;; + *) ARCH=$ABI;; +esac + +# Extract GNU configuration name +case $ARCH in + arm) + GNU_CONFIG=arm-linux-androideabi + ;; + x86) + GNU_CONFIG=i686-linux-android + ;; + mips) + GNU_CONFIG=mipsel-linux-android + ;; + *) + GNU_CONFIG="$ARCH-linux-android" + ;; +esac + +# Generate standalone NDK toolchain installation +NDK_STANDALONE="$TMPDIR/ndk-$ARCH-toolchain" +echo "Generating NDK standalone toolchain installation" +mkdir -p "$NDK_STANDALONE" +# NOTE: The --platform=android-9 is required to provide for GTest. +run "$NDK_DIR/build/tools/make-standalone-toolchain.sh" \ + --arch="$ARCH" \ + --platform=android-9 \ + --install-dir="$NDK_STANDALONE" +fail_panic "Can't generate standalone NDK toolchain installation!" + +# Rebuild the client library, processor and tools with the auto-tools based +# build system. Even though it's not going to be used, this checks that this +# still works correctly. +echo "Building full Android binaries with configure/make" +TMPTARGET="$TMPDIR/target-local" +( + PATH="$NDK_STANDALONE/bin:$PATH" + run mkdir "$TMPTARGET" && + run mkdir "$TMPDIR"/build-target && + run cd "$TMPDIR"/build-target && + run2 "$PROGDIR"/../configure --prefix="$TMPTARGET" \ + --host="$GNU_CONFIG" && + run2 make -j$NUM_JOBS install +) +fail_panic "Could not rebuild Android binaries!" + +# Build and/or run unit test suite. +# If --no-device is used, only rebuild it, otherwise, run in on the +# connected device. +if [ "$NO_DEVICE" ]; then + ACTION="Building" + # This is a trick to force the Makefile to ignore running the scripts. + TESTS_ENVIRONMENT="TESTS_ENVIRONMENT=true" +else + ACTION="Running" + TESTS_ENVIRONMENT= +fi + +( + PATH="$NDK_STANDALONE/bin:$PATH" + run cd "$TMPDIR"/build-target && + # Reconfigure to only run the client unit test suite. + # This one should _never_ fail. + dump "$ACTION Android client library unit tests." + run2 "$PROGDIR"/../configure --prefix="$TMPTARGET" \ + --host="$GNU_CONFIG" \ + --disable-tools \ + --disable-processor && + run make -j$NUM_JOBS check $TESTS_ENVIRONMENT || exit $? + + if [ "$ALL_TESTS" ]; then + dump "$ACTION Tools and processor unit tests." + # Reconfigure to run the processor and tools tests. + # Most of these fail for now, so do not worry about it. + run2 "$PROGDIR"/../configure --prefix="$TMPTARGET" \ + --host="$GNU_CONFIG" && + run make -j$NUM_JOBS check $TESTS_ENVIRONMENT + if [ $? != 0 ]; then + dump "Tools and processor unit tests failed as expected. \ +Use --verbose for results." + fi + fi +) +fail_panic "Client library unit test suite failed!" + +# Copy sources to temporary directory +PROJECT_DIR=$TMPDIR/project +dump "Copying test program sources to: $PROJECT_DIR" +run cp -r "$TESTAPP_DIR" "$PROJECT_DIR" && +run rm -rf "$PROJECT_DIR/obj" && +run rm -rf "$PROJECT_DIR/libs" +fail_panic "Could not copy test program sources to: $PROJECT_DIR" + +# Build the test program with ndk-build. +dump "Building test program with ndk-build" +export NDK_MODULE_PATH="$PROGDIR" +NDK_BUILD_FLAGS="-j$NUM_JOBS" +if verbosity_is_higher_than 1; then + NDK_BUILD_FLAGS="$NDK_BUILD_FLAGS NDK_LOG=1 V=1" +fi +run "$NDK_DIR/ndk-build" -C "$PROJECT_DIR" $NDK_BUILD_FLAGS APP_ABI=$ABI +fail_panic "Can't build test program!" + +# Unless --no-device was used, stop right here if ADB isn't in the path, +# or there is no connected device. +if [ "$NO_DEVICE" ]; then + dump "Done. Please connect a device to run all tests!" + clean_exit 0 +fi + +# Push the program to the device. +TESTAPP=test_google_breakpad +TESTAPP_FILE="$PROJECT_DIR/libs/$ABI/test_google_breakpad" +if [ ! -f "$TESTAPP_FILE" ]; then + panic "Device requires '$ABI' binaries. None found!" +fi + +# Run the program there +dump "Installing test program on device" +DEVICE_TMP=/data/local/tmp +adb_push "$TESTAPP_FILE" "$DEVICE_TMP/" +fail_panic "Cannot push test program to device!" + +dump "Running test program on device" +adb_shell cd "$DEVICE_TMP" "&&" ./$TESTAPP > "$CRASH_LOG" 2>/dev/null +if [ $? = 0 ]; then + panic "Test program did *not* crash as expected!" +fi +if verbosity_is_higher_than 0; then + echo -n "Crash log: " + cat "$CRASH_LOG" +fi + +# Extract minidump from device +MINIDUMP_NAME=$(awk '$1 == "Dump" && $2 == "path:" { print $3; }' "$CRASH_LOG") +MINIDUMP_NAME=$(basename "$MINIDUMP_NAME") +if [ -z "$MINIDUMP_NAME" ]; then + panic "Test program didn't write minidump properly!" +fi + +dump "Extracting minidump: $MINIDUMP_NAME" +adb_pull "$DEVICE_TMP/$MINIDUMP_NAME" . +fail_panic "Can't extract minidump!" + +dump "Parsing test program symbols" +if verbosity_is_higher_than 1; then + log "COMMAND: $TMPBIN/dump_syms \ + $PROJECT_DIR/obj/local/$ABI/$TESTAPP >$TESTAPP.sym" +fi +"$TMPBIN/dump_syms" "$PROJECT_DIR/obj/local/$ABI/$TESTAPP" > $TESTAPP.sym +fail_panic "dump_syms doesn't work!" + +VERSION=$(awk '$1 == "MODULE" { print $4; }' $TESTAPP.sym) +dump "Found module version: $VERSION" +if [ -z "$VERSION" ]; then + echo "ERROR: Can't find proper module version from symbol dump!" + head -n5 $TESTAPP.sym + clean_exit 1 +fi + +run mkdir -p "$TMPDIR/symbols/$TESTAPP/$VERSION" +run mv $TESTAPP.sym "$TMPDIR/symbols/$TESTAPP/$VERSION/" + +dump "Generating stack trace" +# Don't use 'run' to be able to send stdout and stderr to two different files. +log "COMMAND: $TMPBIN/minidump_stackwalk $MINIDUMP_NAME symbols" +"$TMPBIN/minidump_stackwalk" $MINIDUMP_NAME \ + "$TMPDIR/symbols" \ + > "$BUILD_LOG" 2>>"$RUN_LOG" +fail_panic "minidump_stackwalk doesn't work!" + +dump "Checking stack trace content" + +if verbosity_is_higher_than 1; then + cat "$BUILD_LOG" +fi + +# The generated stack trace should look like the following: +# +# Thread 0 (crashed) +# 0 test_google_breakpad!crash [test_breakpad.cpp : 17 + 0x4] +# r4 = 0x00015530 r5 = 0xbea2cbe4 r6 = 0xffffff38 r7 = 0xbea2cb5c +# r8 = 0x00000000 r9 = 0x00000000 r10 = 0x00000000 fp = 0x00000000 +# sp = 0xbea2cb50 lr = 0x00009025 pc = 0x00008f84 +# Found by: given as instruction pointer in context +# 1 test_google_breakpad!main [test_breakpad.cpp : 25 + 0x3] +# r4 = 0x00015530 r5 = 0xbea2cbe4 r6 = 0xffffff38 r7 = 0xbea2cb5c +# r8 = 0x00000000 r9 = 0x00000000 r10 = 0x00000000 fp = 0x00000000 +# sp = 0xbea2cb50 pc = 0x00009025 +# Found by: call frame info +# 2 libc.so + 0x164e5 +# r4 = 0x00008f64 r5 = 0xbea2cc34 r6 = 0x00000001 r7 = 0xbea2cc3c +# r8 = 0x00000000 r9 = 0x00000000 r10 = 0x00000000 fp = 0x00000000 +# sp = 0xbea2cc18 pc = 0x400c34e7 +# Found by: call frame info +# ... +# +# The most important part for us is ensuring that the source location could +# be extracted, so look at the 'test_breakpad.cpp' references here. +# +# First, extract all the lines with test_google_breakpad! in them, and +# dump the corresponding crash location. +# +# Note that if the source location can't be extracted, the second field +# will only be 'test_google_breakpad' without the exclamation mark. +# +LOCATIONS=$(awk '$2 ~ "^test_google_breakpad!.*" { print $3; }' "$BUILD_LOG") + +if [ -z "$LOCATIONS" ]; then + if verbosity_is_lower_than 1; then + cat "$BUILD_LOG" + fi + panic "No source location found in stack trace!" +fi + +# Now check that they all match "[" +BAD_LOCATIONS= +for LOCATION in $LOCATIONS; do + case $LOCATION in + # Escape the opening bracket, or some shells like Dash will not + # match them properly. + \[*.cpp|\[*.cc|\[*.h) # These are valid source locations in our executable + ;; + *) # Everything else is not! + BAD_LOCATIONS="$BAD_LOCATIONS $LOCATION" + ;; + esac +done + +if [ "$BAD_LOCATIONS" ]; then + dump "ERROR: Generated stack trace doesn't contain valid source locations:" + cat "$BUILD_LOG" + echo "Bad locations are: $BAD_LOCATIONS" + exit 1 +fi + +echo "All clear! Congratulations." + diff --git a/shared/sentry/external/breakpad/android/sample_app/README b/shared/sentry/external/breakpad/android/sample_app/README new file mode 100644 index 000000000..aa19dbb44 --- /dev/null +++ b/shared/sentry/external/breakpad/android/sample_app/README @@ -0,0 +1,32 @@ +This is a sample Android executable that can be used to test the +Google Breakpad client library on Android. + +Its purpose is simply to crash and generate a minidump under /data/local/tmp. + +Build instructions: + + cd android/sample_app + $NDK/ndk-build + + Where $NDK points to a valid Android NDK installation. + +Usage instructions: + + After buildind the test program, send it to a device, then run it as + the shell UID: + + adb push libs/armeabi/test_google_breakpad /data/local/tmp + adb shell /data/local/tmp/test_google_breakpad + + This will simply crash after dumping the name of the generated minidump + file. + + See jni/test_breakpad.cpp for details. + + Use 'armeabi-v7a' instead of 'armeabi' above to test the ARMv7-A version + of the binary. + +Note: + If you plan to use the library in a regular Android application, store + the minidump files either to your app-specific directory, or to the SDCard + (the latter requiring a specific permission). diff --git a/shared/sentry/external/breakpad/android/sample_app/jni/Android.mk b/shared/sentry/external/breakpad/android/sample_app/jni/Android.mk new file mode 100644 index 000000000..61487b52c --- /dev/null +++ b/shared/sentry/external/breakpad/android/sample_app/jni/Android.mk @@ -0,0 +1,44 @@ +# 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. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_MODULE := test_google_breakpad +LOCAL_SRC_FILES := test_breakpad.cpp +LOCAL_STATIC_LIBRARIES += breakpad_client +include $(BUILD_EXECUTABLE) + +# If NDK_MODULE_PATH is defined, import the module, otherwise do a direct +# includes. This allows us to build in all scenarios easily. +ifneq ($(NDK_MODULE_PATH),) + $(call import-module,google_breakpad) +else + include $(LOCAL_PATH)/../../google_breakpad/Android.mk +endif diff --git a/shared/sentry/external/breakpad/android/sample_app/jni/Application.mk b/shared/sentry/external/breakpad/android/sample_app/jni/Application.mk new file mode 100644 index 000000000..9728017d3 --- /dev/null +++ b/shared/sentry/external/breakpad/android/sample_app/jni/Application.mk @@ -0,0 +1,32 @@ +# 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. + +APP_STL := stlport_static +APP_ABI := all +APP_CXXFLAGS := -std=c++11 -D__STDC_LIMIT_MACROS diff --git a/shared/sentry/external/breakpad/android/sample_app/jni/test_breakpad.cpp b/shared/sentry/external/breakpad/android/sample_app/jni/test_breakpad.cpp new file mode 100644 index 000000000..9c4ebbb14 --- /dev/null +++ b/shared/sentry/external/breakpad/android/sample_app/jni/test_breakpad.cpp @@ -0,0 +1,57 @@ +// 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 + +#include "client/linux/handler/exception_handler.h" +#include "client/linux/handler/minidump_descriptor.h" + +namespace { + +bool DumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, + void* context, + bool succeeded) { + printf("Dump path: %s\n", descriptor.path()); + return succeeded; +} + +void Crash() { + volatile int* a = reinterpret_cast(NULL); + *a = 1; +} + +} // namespace + +int main(int argc, char* argv[]) { + google_breakpad::MinidumpDescriptor descriptor("."); + google_breakpad::ExceptionHandler eh(descriptor, NULL, DumpCallback, + NULL, true, -1); + Crash(); + return 0; +} diff --git a/shared/sentry/external/breakpad/android/test-driver b/shared/sentry/external/breakpad/android/test-driver new file mode 100755 index 000000000..eaaac6b29 --- /dev/null +++ b/shared/sentry/external/breakpad/android/test-driver @@ -0,0 +1,131 @@ +#! /bin/sh +# test-driver - basic testsuite driver script. + +# Slightly modified for Android, see ANDROID comment below. + +scriptversion=2012-06-27.10; # UTC + +# Copyright (C) 2011-2013 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +# Make unconditional expansion of undefined variables an error. This +# helps a lot in preventing typo-related bugs. +set -u + +usage_error () +{ + echo "$0: $*" >&2 + print_usage >&2 + exit 2 +} + +print_usage () +{ + cat < $log_file 2>&1 +progdir=$(dirname "$0") +"$progdir/test-shell.sh" "$@" > $log_file 2>&1 +estatus=$? +if test $enable_hard_errors = no && test $estatus -eq 99; then + estatus=1 +fi + +case $estatus:$expect_failure in + 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;; + 0:*) col=$grn res=PASS recheck=no gcopy=no;; + 77:*) col=$blu res=SKIP recheck=no gcopy=yes;; + 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;; + *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;; + *:*) col=$red res=FAIL recheck=yes gcopy=yes;; +esac + +# Report outcome to console. +echo "${col}${res}${std}: $test_name" + +# Register the test result, and other relevant metadata. +echo ":test-result: $res" > $trs_file +echo ":global-test-result: $res" >> $trs_file +echo ":recheck: $recheck" >> $trs_file +echo ":copy-in-global-log: $gcopy" >> $trs_file + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/shared/sentry/external/breakpad/android/test-shell.sh b/shared/sentry/external/breakpad/android/test-shell.sh new file mode 100755 index 000000000..3677d8755 --- /dev/null +++ b/shared/sentry/external/breakpad/android/test-shell.sh @@ -0,0 +1,131 @@ +#!/bin/sh +# +# 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 special shell wrapper that can be used to run the Google Breakpad unit +# tests on a connected Android device. +# +# This is designed to be called from the Makefile during 'make check' +# + +PROGDIR=$(dirname "$0") +PROGNAME=$(basename "$0") +. $PROGDIR/common-functions.sh + +# Extract test program name first. +TEST_PROGRAM=$1 +shift + +if [ -z "$TEST_PROGRAM" ]; then + panic "No test program/script name on the command-line!" +fi + +if [ ! -f "$TEST_PROGRAM" ]; then + panic "Can't find test program/script: $TEST_PROGRAM" +fi + +# Create test directory on the device +TEST_DIR=/data/local/tmp/test-google-breakpad-$$ +adb_shell mkdir "$TEST_DIR" || + panic "Can't create test directory on device: $TEST_DIR" + +# Ensure that it is always removed when the script exits. +clean_test_dir () { + # Don't care about success/failure, use '$ADB shell' directly. + adb_shell rm -r "$TEST_DIR" +} + +atexit clean_test_dir + +TEST_PROGRAM_NAME=$(basename "$TEST_PROGRAM") +TEST_PROGRAM_DIR=$(dirname "$TEST_PROGRAM") + +# Handle special case(s) here. +DATA_FILES= +case $TEST_PROGRAM_NAME in + linux_client_unittest) + # linux_client_unittest will call another executable at runtime, ensure + # it is installed too. + adb_install "$TEST_PROGRAM_DIR/linux_dumper_unittest_helper" "$TEST_DIR" + # linux_client_unittest loads a shared library at runtime, ensure it is + # installed too. + adb_install "$TEST_PROGRAM_DIR/linux_client_unittest_shlib" "$TEST_DIR" + ;; + basic_source_line_resolver_unittest) + DATA_FILES="module1.out \ + module2.out \ + module3_bad.out \ + module4_bad.out" + ;; + exploitability_unittest) + DATA_FILES="scii_read_av.dmp \ + ascii_read_av_block_write.dmp \ + ascii_read_av_clobber_write.dmp \ + ascii_read_av_conditional.dmp \ + ascii_read_av_non_null.dmp \ + ascii_read_av_then_jmp.dmp \ + ascii_read_av_xchg_write.dmp \ + ascii_write_av.dmp \ + ascii_write_av_arg_to_call.dmp \ + exec_av_on_stack.dmp \ + null_read_av.dmp \ + null_write_av.dmp \ + read_av.dmp \ + null_read_av.dmp \ + write_av_non_null.dmp" + ;; + fast_source_line_resolver_unittest) + DATA_FILES="module0.out \ + module1.out \ + module2.out \ + module3_bad.out \ + module4_bad.out" + ;; + minidump_processor_unittest|minidump_unittest) + DATA_FILES="src/processor/testdata/minidump2.dmp" + ;; +esac + +# Install the data files, their path is relative to the environment +# variable 'srcdir' +for FILE in $DATA_FILES; do + FILEDIR=src/processor/testdata/$(dirname "$FILE") + adb_shell mkdir -p "$TEST_DIR/$FILEDIR" + adb_install "${srcdir:-.}/$FILE" "$TEST_DIR"/"$FILE" +done + +# Copy test program to device +adb_install "$TEST_PROGRAM" "$TEST_DIR" + +# Run it +adb_shell "cd $TEST_DIR && LD_LIBRARY_PATH=. ./$TEST_PROGRAM_NAME $@" + +# Note: exiting here will call cleanup_exit which will remove the temporary +# files from the device. diff --git a/shared/sentry/external/breakpad/appveyor.yml b/shared/sentry/external/breakpad/appveyor.yml new file mode 100644 index 000000000..3cf5ba427 --- /dev/null +++ b/shared/sentry/external/breakpad/appveyor.yml @@ -0,0 +1,42 @@ +version: '{build}' + +environment: + GYP_MSVS_VERSION: 2013 + +platform: + - Win32 + +configuration: + - Debug + - Release + +# Use the source dir expected by gclient. +clone_folder: c:\projects\breakpad\src + +# Before checkout. +init: + - cd %APPVEYOR_BUILD_FOLDER%\..\.. + - appveyor DownloadFile https://storage.googleapis.com/chrome-infra/depot_tools.zip + - 7z -bd x depot_tools.zip -odepot_tools + - depot_tools\update_depot_tools + - cd %APPVEYOR_BUILD_FOLDER% + +# After checkout. +install: + - PATH C:\projects\depot_tools;%PATH% + - cd %APPVEYOR_BUILD_FOLDER%\.. + - gclient config https://%APPVEYOR_REPO_PROVIDER%.com/%APPVEYOR_REPO_NAME% --unmanaged --name=src + - gclient sync + +build_script: + - cd %APPVEYOR_BUILD_FOLDER% + - msbuild src\client\windows\breakpad_client.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" /m /verbosity:normal + - msbuild src\tools\windows\tools_windows.sln /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" /m /verbosity:normal + +test_script: + - src\client\windows\%CONFIGURATION%\client_tests.exe + - src\tools\windows\%CONFIGURATION%\dump_syms_unittest.exe + +artifacts: + - path: '**\*.exe' + - path: '**\*.lib' diff --git a/shared/sentry/external/breakpad/autotools/ar-lib b/shared/sentry/external/breakpad/autotools/ar-lib new file mode 100755 index 000000000..463b9ec02 --- /dev/null +++ b/shared/sentry/external/breakpad/autotools/ar-lib @@ -0,0 +1,270 @@ +#! /bin/sh +# Wrapper for Microsoft lib.exe + +me=ar-lib +scriptversion=2012-03-01.08; # UTC + +# Copyright (C) 2010-2014 Free Software Foundation, Inc. +# Written by Peter Rosin . +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + + +# func_error message +func_error () +{ + echo "$me: $1" 1>&2 + exit 1 +} + +file_conv= + +# func_file_conv build_file +# Convert a $build file to $host form and store it in $file +# Currently only supports Windows hosts. +func_file_conv () +{ + file=$1 + case $file in + / | /[!/]*) # absolute file, and not a UNC file + if test -z "$file_conv"; then + # lazily determine how to convert abs files + case `uname -s` in + MINGW*) + file_conv=mingw + ;; + CYGWIN*) + file_conv=cygwin + ;; + *) + file_conv=wine + ;; + esac + fi + case $file_conv in + mingw) + file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` + ;; + cygwin) + file=`cygpath -m "$file" || echo "$file"` + ;; + wine) + file=`winepath -w "$file" || echo "$file"` + ;; + esac + ;; + esac +} + +# func_at_file at_file operation archive +# Iterate over all members in AT_FILE performing OPERATION on ARCHIVE +# for each of them. +# When interpreting the content of the @FILE, do NOT use func_file_conv, +# since the user would need to supply preconverted file names to +# binutils ar, at least for MinGW. +func_at_file () +{ + operation=$2 + archive=$3 + at_file_contents=`cat "$1"` + eval set x "$at_file_contents" + shift + + for member + do + $AR -NOLOGO $operation:"$member" "$archive" || exit $? + done +} + +case $1 in + '') + func_error "no command. Try '$0 --help' for more information." + ;; + -h | --h*) + cat <. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +nl=' +' + +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent tools from complaining about whitespace usage. +IFS=" "" $nl" + +file_conv= + +# func_file_conv build_file lazy +# Convert a $build file to $host form and store it in $file +# Currently only supports Windows hosts. If the determined conversion +# type is listed in (the comma separated) LAZY, no conversion will +# take place. +func_file_conv () +{ + file=$1 + case $file in + / | /[!/]*) # absolute file, and not a UNC file + if test -z "$file_conv"; then + # lazily determine how to convert abs files + case `uname -s` in + MINGW*) + file_conv=mingw + ;; + CYGWIN*) + file_conv=cygwin + ;; + *) + file_conv=wine + ;; + esac + fi + case $file_conv/,$2, in + *,$file_conv,*) + ;; + mingw/*) + file=`cmd //C echo "$file " | sed -e 's/"\(.*\) " *$/\1/'` + ;; + cygwin/*) + file=`cygpath -m "$file" || echo "$file"` + ;; + wine/*) + file=`winepath -w "$file" || echo "$file"` + ;; + esac + ;; + esac +} + +# func_cl_dashL linkdir +# Make cl look for libraries in LINKDIR +func_cl_dashL () +{ + func_file_conv "$1" + if test -z "$lib_path"; then + lib_path=$file + else + lib_path="$lib_path;$file" + fi + linker_opts="$linker_opts -LIBPATH:$file" +} + +# func_cl_dashl library +# Do a library search-path lookup for cl +func_cl_dashl () +{ + lib=$1 + found=no + save_IFS=$IFS + IFS=';' + for dir in $lib_path $LIB + do + IFS=$save_IFS + if $shared && test -f "$dir/$lib.dll.lib"; then + found=yes + lib=$dir/$lib.dll.lib + break + fi + if test -f "$dir/$lib.lib"; then + found=yes + lib=$dir/$lib.lib + break + fi + if test -f "$dir/lib$lib.a"; then + found=yes + lib=$dir/lib$lib.a + break + fi + done + IFS=$save_IFS + + if test "$found" != yes; then + lib=$lib.lib + fi +} + +# func_cl_wrapper cl arg... +# Adjust compile command to suit cl +func_cl_wrapper () +{ + # Assume a capable shell + lib_path= + shared=: + linker_opts= + for arg + do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + eat=1 + case $2 in + *.o | *.[oO][bB][jJ]) + func_file_conv "$2" + set x "$@" -Fo"$file" + shift + ;; + *) + func_file_conv "$2" + set x "$@" -Fe"$file" + shift + ;; + esac + ;; + -I) + eat=1 + func_file_conv "$2" mingw + set x "$@" -I"$file" + shift + ;; + -I*) + func_file_conv "${1#-I}" mingw + set x "$@" -I"$file" + shift + ;; + -l) + eat=1 + func_cl_dashl "$2" + set x "$@" "$lib" + shift + ;; + -l*) + func_cl_dashl "${1#-l}" + set x "$@" "$lib" + shift + ;; + -L) + eat=1 + func_cl_dashL "$2" + ;; + -L*) + func_cl_dashL "${1#-L}" + ;; + -static) + shared=false + ;; + -Wl,*) + arg=${1#-Wl,} + save_ifs="$IFS"; IFS=',' + for flag in $arg; do + IFS="$save_ifs" + linker_opts="$linker_opts $flag" + done + IFS="$save_ifs" + ;; + -Xlinker) + eat=1 + linker_opts="$linker_opts $2" + ;; + -*) + set x "$@" "$1" + shift + ;; + *.cc | *.CC | *.cxx | *.CXX | *.[cC]++) + func_file_conv "$1" + set x "$@" -Tp"$file" + shift + ;; + *.c | *.cpp | *.CPP | *.lib | *.LIB | *.Lib | *.OBJ | *.obj | *.[oO]) + func_file_conv "$1" mingw + set x "$@" "$file" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift + done + if test -n "$linker_opts"; then + linker_opts="-link$linker_opts" + fi + exec "$@" $linker_opts + exit 1 +} + +eat= + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: compile [--help] [--version] PROGRAM [ARGS] + +Wrapper for compilers which do not understand '-c -o'. +Remove '-o dest.o' from ARGS, run PROGRAM with the remaining +arguments, and rename the output as expected. + +If you are trying to build a whole package this is not the +right script to run: please start by reading the file 'INSTALL'. + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "compile $scriptversion" + exit $? + ;; + cl | *[/\\]cl | cl.exe | *[/\\]cl.exe ) + func_cl_wrapper "$@" # Doesn't return... + ;; +esac + +ofile= +cfile= + +for arg +do + if test -n "$eat"; then + eat= + else + case $1 in + -o) + # configure might choose to run compile as 'compile cc -o foo foo.c'. + # So we strip '-o arg' only if arg is an object. + eat=1 + case $2 in + *.o | *.obj) + ofile=$2 + ;; + *) + set x "$@" -o "$2" + shift + ;; + esac + ;; + *.c) + cfile=$1 + set x "$@" "$1" + shift + ;; + *) + set x "$@" "$1" + shift + ;; + esac + fi + shift +done + +if test -z "$ofile" || test -z "$cfile"; then + # If no '-o' option was seen then we might have been invoked from a + # pattern rule where we don't need one. That is ok -- this is a + # normal compilation that the losing compiler can handle. If no + # '.c' file was seen then we are probably linking. That is also + # ok. + exec "$@" +fi + +# Name of file we expect compiler to create. +cofile=`echo "$cfile" | sed 's|^.*[\\/]||; s|^[a-zA-Z]:||; s/\.c$/.o/'` + +# Create the lock directory. +# Note: use '[/\\:.-]' here to ensure that we don't use the same name +# that we are using for the .o file. Also, base the name on the expected +# object file name, since that is what matters with a parallel build. +lockdir=`echo "$cofile" | sed -e 's|[/\\:.-]|_|g'`.d +while true; do + if mkdir "$lockdir" >/dev/null 2>&1; then + break + fi + sleep 1 +done +# FIXME: race condition here if user kills between mkdir and trap. +trap "rmdir '$lockdir'; exit 1" 1 2 15 + +# Run the compile. +"$@" +ret=$? + +if test -f "$cofile"; then + test "$cofile" = "$ofile" || mv "$cofile" "$ofile" +elif test -f "${cofile}bj"; then + test "${cofile}bj" = "$ofile" || mv "${cofile}bj" "$ofile" +fi + +rmdir "$lockdir" +exit $ret + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/shared/sentry/external/breakpad/autotools/config.guess b/shared/sentry/external/breakpad/autotools/config.guess new file mode 100755 index 000000000..1000e2bd9 --- /dev/null +++ b/shared/sentry/external/breakpad/autotools/config.guess @@ -0,0 +1,1465 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright 1992-2017 Free Software Foundation, Inc. + +timestamp='2017-02-07' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see . +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). +# +# Originally written by Per Bothner; maintained since 2000 by Ben Elliston. +# +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess +# +# Please send patches to . + + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright 1992-2017 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ; set_cc_for_build= ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +case "${UNAME_SYSTEM}" in +Linux|GNU|GNU/*) + # If the system lacks a compiler, then just pick glibc. + # We could probably try harder. + LIBC=gnu + + eval $set_cc_for_build + cat <<-EOF > $dummy.c + #include + #if defined(__UCLIBC__) + LIBC=uclibc + #elif defined(__dietlibc__) + LIBC=dietlibc + #else + LIBC=gnu + #endif + EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` + ;; +esac + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ + /sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || \ + echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + sh5el) machine=sh5le-unknown ;; + earmv*) + arch=`echo ${UNAME_MACHINE_ARCH} | sed -e 's,^e\(armv[0-9]\).*$,\1,'` + endian=`echo ${UNAME_MACHINE_ARCH} | sed -ne 's,^.*\(eb\)$,\1,p'` + machine=${arch}${endian}-unknown + ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently (or will in the future) and ABI. + case "${UNAME_MACHINE_ARCH}" in + earm*) + os=netbsdelf + ;; + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ELF__ + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # Determine ABI tags. + case "${UNAME_MACHINE_ARCH}" in + earm*) + expr='s/^earmv[0-9]/-eabi/;s/eb$//' + abi=`echo ${UNAME_MACHINE_ARCH} | sed -e "$expr"` + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE} | sed -e 's/[-_].*//' | cut -d. -f1,2` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}${abi}" + exit ;; + *:Bitrig:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} + exit ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} + exit ;; + *:LibertyBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-libertybsd${UNAME_RELEASE} + exit ;; + *:ekkoBSD:*:*) + echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + exit ;; + *:SolidBSD:*:*) + echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} + exit ;; + macppc:MirBSD:*:*) + echo powerpc-unknown-mirbsd${UNAME_RELEASE} + exit ;; + *:MirBSD:*:*) + echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + exit ;; + *:Sortix:*:*) + echo ${UNAME_MACHINE}-unknown-sortix + exit ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE=alpha ;; + "EV4.5 (21064)") + UNAME_MACHINE=alpha ;; + "LCA4 (21066/21068)") + UNAME_MACHINE=alpha ;; + "EV5 (21164)") + UNAME_MACHINE=alphaev5 ;; + "EV5.6 (21164A)") + UNAME_MACHINE=alphaev56 ;; + "EV5.6 (21164PC)") + UNAME_MACHINE=alphapca56 ;; + "EV5.7 (21164PC)") + UNAME_MACHINE=alphapca57 ;; + "EV6 (21264)") + UNAME_MACHINE=alphaev6 ;; + "EV6.7 (21264A)") + UNAME_MACHINE=alphaev67 ;; + "EV6.8CB (21264C)") + UNAME_MACHINE=alphaev68 ;; + "EV6.8AL (21264B)") + UNAME_MACHINE=alphaev68 ;; + "EV6.8CX (21264D)") + UNAME_MACHINE=alphaev68 ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE=alphaev69 ;; + "EV7 (21364)") + UNAME_MACHINE=alphaev7 ;; + "EV7.9 (21364A)") + UNAME_MACHINE=alphaev79 ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + # Reset EXIT trap before exiting to avoid spurious non-zero exit code. + exitcode=$? + trap '' 0 + exit $exitcode ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit ;; + *:z/VM:*:*) + echo s390-ibm-zvmoe + exit ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit ;; + arm*:riscos:*:*|arm*:RISCOS:*:*) + echo arm-unknown-riscos + exit ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7; exit ;; + esac ;; + s390x:SunOS:*:*) + echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) + echo i386-pc-auroraux${UNAME_RELEASE} + exit ;; + i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) + eval $set_cc_for_build + SUN_ARCH=i386 + # If there is a compiler, see if it is configured for 64-bit objects. + # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. + # This test works for both compilers. + if [ "$CC_FOR_BUILD" != no_compiler_found ]; then + if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + SUN_ARCH=x86_64 + fi + fi + echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = x && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit ;; + m68k:machten:*:*) + echo m68k-apple-machten${UNAME_RELEASE} + exit ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && + dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`$dummy $dummyarg` && + { echo "$SYSTEM_NAME"; exit; } + echo mips-mips-riscos${UNAME_RELEASE} + exit ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` + then + echo "$SYSTEM_NAME" + else + echo rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit ;; + *:AIX:*:[4567]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/lslpp ] ; then + IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | + awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 + 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH=hppa2.0n ;; + 64) HP_ARCH=hppa2.0w ;; + '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS="" $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = hppa2.0w ] + then + eval $set_cc_for_build + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | + grep -q __LP64__ + then + HP_ARCH=hppa2.0w + else + HP_ARCH=hppa64 + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + echo unknown-hitachi-hiuxwe2 + exit ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + *:UNICOS/mp:*:*) + echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:FreeBSD:*:*) + UNAME_PROCESSOR=`/usr/bin/uname -p` + case ${UNAME_PROCESSOR} in + amd64) + echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + *) + echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + esac + exit ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit ;; + *:MINGW64*:*) + echo ${UNAME_MACHINE}-pc-mingw64 + exit ;; + *:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit ;; + *:MSYS*:*) + echo ${UNAME_MACHINE}-pc-msys + exit ;; + i*:windows32*:*) + # uname -m includes "-pc" on this system. + echo ${UNAME_MACHINE}-mingw32 + exit ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit ;; + *:Interix*:*) + case ${UNAME_MACHINE} in + x86) + echo i586-pc-interix${UNAME_RELEASE} + exit ;; + authenticamd | genuineintel | EM64T) + echo x86_64-unknown-interix${UNAME_RELEASE} + exit ;; + IA64) + echo ia64-unknown-interix${UNAME_RELEASE} + exit ;; + esac ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit ;; + 8664:Windows_NT:*) + echo x86_64-pc-mks + exit ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit ;; + amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) + echo x86_64-unknown-cygwin + exit ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + *:GNU:*:*) + # the GNU system + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} + exit ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit ;; + aarch64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + aarch64_be:Linux:*:*) + UNAME_MACHINE=aarch64_be + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC=gnulibc1 ; fi + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + arc:Linux:*:* | arceb:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + arm*:Linux:*:*) + eval $set_cc_for_build + if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_EABI__ + then + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + else + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi + else + echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf + fi + fi + exit ;; + avr32*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + cris:Linux:*:*) + echo ${UNAME_MACHINE}-axis-linux-${LIBC} + exit ;; + crisv32:Linux:*:*) + echo ${UNAME_MACHINE}-axis-linux-${LIBC} + exit ;; + e2k:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + frv:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + hexagon:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + i*86:Linux:*:*) + echo ${UNAME_MACHINE}-pc-linux-${LIBC} + exit ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + k1om:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + m32r*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + mips:Linux:*:* | mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef ${UNAME_MACHINE} + #undef ${UNAME_MACHINE}el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=${UNAME_MACHINE}el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=${UNAME_MACHINE} + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } + ;; + mips64el:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + openrisc*:Linux:*:*) + echo or1k-unknown-linux-${LIBC} + exit ;; + or32:Linux:*:* | or1k*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + padre:Linux:*:*) + echo sparc-unknown-linux-${LIBC} + exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-${LIBC} + exit ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; + PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; + *) echo hppa-unknown-linux-${LIBC} ;; + esac + exit ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-${LIBC} + exit ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-${LIBC} + exit ;; + ppc64le:Linux:*:*) + echo powerpc64le-unknown-linux-${LIBC} + exit ;; + ppcle:Linux:*:*) + echo powerpcle-unknown-linux-${LIBC} + exit ;; + riscv32:Linux:*:* | riscv64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux-${LIBC} + exit ;; + sh64*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + tile*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + vax:Linux:*:*) + echo ${UNAME_MACHINE}-dec-linux-${LIBC} + exit ;; + x86_64:Linux:*:*) + echo ${UNAME_MACHINE}-pc-linux-${LIBC} + exit ;; + xtensa*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit ;; + i*86:syllable:*:*) + echo ${UNAME_MACHINE}-pc-syllable + exit ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i586. + # Note: whatever this is, it MUST be the same as what config.sub + # prints for the "djgpp" host, or else GDB configure will decide that + # this is a cross-build. + echo i586-pc-msdosdjgpp + exit ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + NCR*:*:4.2:* | MPRAS*:*:4.2:*) + OS_REL='.3' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says + echo i586-unisys-sysv4 + exit ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + echo ${UNAME_MACHINE}-stratus-vos + exit ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit ;; + BePC:Haiku:*:*) # Haiku running on Intel PC compatible. + echo i586-pc-haiku + exit ;; + x86_64:Haiku:*:*) + echo x86_64-unknown-haiku + exit ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit ;; + SX-7:SUPER-UX:*:*) + echo sx7-nec-superux${UNAME_RELEASE} + exit ;; + SX-8:SUPER-UX:*:*) + echo sx8-nec-superux${UNAME_RELEASE} + exit ;; + SX-8R:SUPER-UX:*:*) + echo sx8r-nec-superux${UNAME_RELEASE} + exit ;; + SX-ACE:SUPER-UX:*:*) + echo sxace-nec-superux${UNAME_RELEASE} + exit ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown + eval $set_cc_for_build + if test "$UNAME_PROCESSOR" = unknown ; then + UNAME_PROCESSOR=powerpc + fi + if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then + if [ "$CC_FOR_BUILD" != no_compiler_found ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi + fi + elif test "$UNAME_PROCESSOR" = i386 ; then + # Avoid executing cc on OS X 10.9, as it ships with a stub + # that puts up a graphical alert prompting to install + # developer tools. Any system running Mac OS X 10.7 or + # later (Darwin 11 and later) is required to have a 64-bit + # processor. This is not true of the ARM version of Darwin + # that Apple uses in portable devices. + UNAME_PROCESSOR=x86_64 + fi + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = x86; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit ;; + NEO-?:NONSTOP_KERNEL:*:*) + echo neo-tandem-nsk${UNAME_RELEASE} + exit ;; + NSE-*:NONSTOP_KERNEL:*:*) + echo nse-tandem-nsk${UNAME_RELEASE} + exit ;; + NSR-?:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit ;; + NSX-?:NONSTOP_KERNEL:*:*) + echo nsx-tandem-nsk${UNAME_RELEASE} + exit ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = 386; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux${UNAME_RELEASE} + exit ;; + *:DragonFly:*:*) + echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "${UNAME_MACHINE}" in + A*) echo alpha-dec-vms ; exit ;; + I*) echo ia64-dec-vms ; exit ;; + V*) echo vax-dec-vms ; exit ;; + esac ;; + *:XENIX:*:SysV) + echo i386-pc-xenix + exit ;; + i*86:skyos:*:*) + echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE} | sed -e 's/ .*$//'` + exit ;; + i*86:rdos:*:*) + echo ${UNAME_MACHINE}-pc-rdos + exit ;; + i*86:AROS:*:*) + echo ${UNAME_MACHINE}-pc-aros + exit ;; + x86_64:VMkernel:*:*) + echo ${UNAME_MACHINE}-unknown-esx + exit ;; + amd64:Isilon\ OneFS:*:*) + echo x86_64-unknown-onefs + exit ;; +esac + +cat >&2 </dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/shared/sentry/external/breakpad/autotools/config.sub b/shared/sentry/external/breakpad/autotools/config.sub new file mode 100755 index 000000000..87abeab6c --- /dev/null +++ b/shared/sentry/external/breakpad/autotools/config.sub @@ -0,0 +1,1831 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright 1992-2017 Free Software Foundation, Inc. + +timestamp='2017-02-07' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see . +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). + + +# Please send patches to . +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright 1992-2017 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ + linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ + knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \ + kopensolaris*-gnu* | cloudabi*-eabi* | \ + storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + android-linux) + os=-linux-android + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis | -knuth | -cray | -microblaze*) + os= + basic_machine=$1 + ;; + -bluegene*) + os=-cnk + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco6) + os=-sco5v6 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*178) + os=-lynxos178 + ;; + -lynx*5) + os=-lynxos5 + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | aarch64 | aarch64_be \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arceb \ + | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ + | avr | avr32 \ + | ba \ + | be32 | be64 \ + | bfin \ + | c4x | c8051 | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | e2k | epiphany \ + | fido | fr30 | frv | ft32 \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | hexagon \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ + | k1om \ + | le32 | le64 \ + | lm32 \ + | m32c | m32r | m32rle | m68000 | m68k | m88k \ + | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64octeon | mips64octeonel \ + | mips64orion | mips64orionel \ + | mips64r5900 | mips64r5900el \ + | mips64vr | mips64vrel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mips64vr5900 | mips64vr5900el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa32r6 | mipsisa32r6el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64r6 | mipsisa64r6el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipsr5900 | mipsr5900el \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | moxie \ + | mt \ + | msp430 \ + | nds32 | nds32le | nds32be \ + | nios | nios2 | nios2eb | nios2el \ + | ns16k | ns32k \ + | open8 | or1k | or1knd | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle \ + | pru \ + | pyramid \ + | riscv32 | riscv64 \ + | rl78 | rx \ + | score \ + | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ + | spu \ + | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ + | ubicom32 \ + | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ + | visium \ + | we32k \ + | x86 | xc16x | xstormy16 | xtensa \ + | z8k | z80) + basic_machine=$basic_machine-unknown + ;; + c54x) + basic_machine=tic54x-unknown + ;; + c55x) + basic_machine=tic55x-unknown + ;; + c6x) + basic_machine=tic6x-unknown + ;; + leon|leon[3-9]) + basic_machine=sparc-$basic_machine + ;; + m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + ms1) + basic_machine=mt-unknown + ;; + + strongarm | thumb | xscale) + basic_machine=arm-unknown + ;; + xgate) + basic_machine=$basic_machine-unknown + os=-none + ;; + xscaleeb) + basic_machine=armeb-unknown + ;; + + xscaleel) + basic_machine=armel-unknown + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | aarch64-* | aarch64_be-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* | avr32-* \ + | ba-* \ + | be32-* | be64-* \ + | bfin-* | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* \ + | c8051-* | clipper-* | craynv-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | e2k-* | elxsi-* \ + | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | hexagon-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ + | k1om-* \ + | le32-* | le64-* \ + | lm32-* \ + | m32c-* | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ + | microblaze-* | microblazeel-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64octeon-* | mips64octeonel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64r5900-* | mips64r5900el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mips64vr5900-* | mips64vr5900el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa32r6-* | mipsisa32r6el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64r6-* | mipsisa64r6el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipsr5900-* | mipsr5900el-* \ + | mipstx39-* | mipstx39el-* \ + | mmix-* \ + | mt-* \ + | msp430-* \ + | nds32-* | nds32le-* | nds32be-* \ + | nios-* | nios2-* | nios2eb-* | nios2el-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | open8-* \ + | or1k*-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ + | pru-* \ + | pyramid-* \ + | riscv32-* | riscv64-* \ + | rl78-* | romp-* | rs6000-* | rx-* \ + | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ + | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \ + | tahoe-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ + | tile*-* \ + | tron-* \ + | ubicom32-* \ + | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ + | vax-* \ + | visium-* \ + | we32k-* \ + | x86-* | x86_64-* | xc16x-* | xps100-* \ + | xstormy16-* | xtensa*-* \ + | ymp-* \ + | z8k-* | z80-*) + ;; + # Recognize the basic CPU types without company name, with glob match. + xtensa*) + basic_machine=$basic_machine-unknown + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + abacus) + basic_machine=abacus-unknown + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amd64-*) + basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aros) + basic_machine=i386-pc + os=-aros + ;; + asmjs) + basic_machine=asmjs-unknown + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + blackfin) + basic_machine=bfin-unknown + os=-linux + ;; + blackfin-*) + basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + bluegene*) + basic_machine=powerpc-ibm + os=-cnk + ;; + c54x-*) + basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c55x-*) + basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c6x-*) + basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + cegcc) + basic_machine=arm-unknown + os=-cegcc + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + craynv) + basic_machine=craynv-cray + os=-unicosmp + ;; + cr16 | cr16-*) + basic_machine=cr16-unknown + os=-elf + ;; + crds | unos) + basic_machine=m68k-crds + ;; + crisv32 | crisv32-* | etraxfs*) + basic_machine=crisv32-axis + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + crx) + basic_machine=crx-unknown + os=-elf + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dicos) + basic_machine=i686-pc + os=-dicos + ;; + djgpp) + basic_machine=i586-pc + os=-msdosdjgpp + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + e500v[12]) + basic_machine=powerpc-unknown + os=$os"spe" + ;; + e500v[12]-*) + basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + os=$os"spe" + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + leon-*|leon[3-9]-*) + basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'` + ;; + m68knommu) + basic_machine=m68k-unknown + os=-linux + ;; + m68knommu-*) + basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + microblaze*) + basic_machine=microblaze-xilinx + ;; + mingw64) + basic_machine=x86_64-pc + os=-mingw64 + ;; + mingw32) + basic_machine=i686-pc + os=-mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + os=-mingw32ce + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + moxiebox) + basic_machine=moxie-unknown + os=-moxiebox + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + ms1-*) + basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` + ;; + msys) + basic_machine=i686-pc + os=-msys + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + nacl) + basic_machine=le32-unknown + os=-nacl + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + neo-tandem) + basic_machine=neo-tandem + ;; + nse-tandem) + basic_machine=nse-tandem + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + nsx-tandem) + basic_machine=nsx-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + openrisc | openrisc-*) + basic_machine=or32-unknown + ;; + os400) + basic_machine=powerpc-ibm + os=-os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + parisc) + basic_machine=hppa-unknown + os=-linux + ;; + parisc-*) + basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pc98) + basic_machine=i386-pc + ;; + pc98-*) + basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc + ;; + pentium4) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium4-*) + basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc | ppcbe) basic_machine=powerpc-unknown + ;; + ppc-* | ppcbe-*) + basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rdos | rdos64) + basic_machine=x86_64-pc + os=-rdos + ;; + rdos32) + basic_machine=i386-pc + os=-rdos + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sde) + basic_machine=mipsisa32-sde + os=-elf + ;; + sei) + basic_machine=mips-sei + os=-seiux + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sh5el) + basic_machine=sh5le-unknown + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + strongarm-* | thumb-*) + basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tile*) + basic_machine=$basic_machine-unknown + os=-linux-gnu + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + tpf) + basic_machine=s390x-ibm + os=-tpf + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xbox) + basic_machine=i686-pc + os=-mingw32 + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + xscale-* | xscalee[bl]-*) + basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + z80-*-coff) + basic_machine=z80-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + mmix) + basic_machine=mmix-knuth + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -auroraux) + os=-auroraux + ;; + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ + | -sym* | -kopensolaris* | -plan9* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* | -aros* | -cloudabi* | -sortix* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ + | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \ + | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ + | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* | -cegcc* | -glidix* \ + | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -midipix* | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ + | -linux-newlib* | -linux-musl* | -linux-uclibc* \ + | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ + | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \ + | -onefs* | -tirtos* | -phoenix* | -fuchsia* | -redox*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux-dietlibc) + os=-linux-dietlibc + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -os400*) + os=-os400 + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -syllable*) + os=-syllable + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -tpf*) + os=-tpf + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -aros*) + os=-aros + ;; + -zvmoe) + os=-zvmoe + ;; + -dicos*) + os=-dicos + ;; + -nacl*) + ;; + -ios) + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + score-*) + os=-elf + ;; + spu-*) + os=-elf + ;; + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + c4x-* | tic4x-*) + os=-coff + ;; + c8051-*) + os=-elf + ;; + hexagon-*) + os=-elf + ;; + tic54x-*) + os=-coff + ;; + tic55x-*) + os=-coff + ;; + tic6x-*) + os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + ;; + m68*-cisco) + os=-aout + ;; + mep-*) + os=-elf + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + pru-*) + os=-elf + ;; + *-be) + os=-beos + ;; + *-haiku) + os=-haiku + ;; + *-ibm) + os=-aix + ;; + *-knuth) + os=-mmixware + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -cnk*|-aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -os400*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -tpf*) + vendor=ibm + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/shared/sentry/external/breakpad/autotools/depcomp b/shared/sentry/external/breakpad/autotools/depcomp new file mode 100755 index 000000000..fc98710e2 --- /dev/null +++ b/shared/sentry/external/breakpad/autotools/depcomp @@ -0,0 +1,791 @@ +#! /bin/sh +# depcomp - compile a program generating dependencies as side-effects + +scriptversion=2013-05-30.07; # UTC + +# Copyright (C) 1999-2014 Free Software Foundation, Inc. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# Originally written by Alexandre Oliva . + +case $1 in + '') + echo "$0: No command. Try '$0 --help' for more information." 1>&2 + exit 1; + ;; + -h | --h*) + cat <<\EOF +Usage: depcomp [--help] [--version] PROGRAM [ARGS] + +Run PROGRAMS ARGS to compile a file, generating dependencies +as side-effects. + +Environment variables: + depmode Dependency tracking mode. + source Source file read by 'PROGRAMS ARGS'. + object Object file output by 'PROGRAMS ARGS'. + DEPDIR directory where to store dependencies. + depfile Dependency file to output. + tmpdepfile Temporary file to use when outputting dependencies. + libtool Whether libtool is used (yes/no). + +Report bugs to . +EOF + exit $? + ;; + -v | --v*) + echo "depcomp $scriptversion" + exit $? + ;; +esac + +# Get the directory component of the given path, and save it in the +# global variables '$dir'. Note that this directory component will +# be either empty or ending with a '/' character. This is deliberate. +set_dir_from () +{ + case $1 in + */*) dir=`echo "$1" | sed -e 's|/[^/]*$|/|'`;; + *) dir=;; + esac +} + +# Get the suffix-stripped basename of the given path, and save it the +# global variable '$base'. +set_base_from () +{ + base=`echo "$1" | sed -e 's|^.*/||' -e 's/\.[^.]*$//'` +} + +# If no dependency file was actually created by the compiler invocation, +# we still have to create a dummy depfile, to avoid errors with the +# Makefile "include basename.Plo" scheme. +make_dummy_depfile () +{ + echo "#dummy" > "$depfile" +} + +# Factor out some common post-processing of the generated depfile. +# Requires the auxiliary global variable '$tmpdepfile' to be set. +aix_post_process_depfile () +{ + # If the compiler actually managed to produce a dependency file, + # post-process it. + if test -f "$tmpdepfile"; then + # Each line is of the form 'foo.o: dependency.h'. + # Do two passes, one to just change these to + # $object: dependency.h + # and one to simply output + # dependency.h: + # which is needed to avoid the deleted-header problem. + { sed -e "s,^.*\.[$lower]*:,$object:," < "$tmpdepfile" + sed -e "s,^.*\.[$lower]*:[$tab ]*,," -e 's,$,:,' < "$tmpdepfile" + } > "$depfile" + rm -f "$tmpdepfile" + else + make_dummy_depfile + fi +} + +# A tabulation character. +tab=' ' +# A newline character. +nl=' +' +# Character ranges might be problematic outside the C locale. +# These definitions help. +upper=ABCDEFGHIJKLMNOPQRSTUVWXYZ +lower=abcdefghijklmnopqrstuvwxyz +digits=0123456789 +alpha=${upper}${lower} + +if test -z "$depmode" || test -z "$source" || test -z "$object"; then + echo "depcomp: Variables source, object and depmode must be set" 1>&2 + exit 1 +fi + +# Dependencies for sub/bar.o or sub/bar.obj go into sub/.deps/bar.Po. +depfile=${depfile-`echo "$object" | + sed 's|[^\\/]*$|'${DEPDIR-.deps}'/&|;s|\.\([^.]*\)$|.P\1|;s|Pobj$|Po|'`} +tmpdepfile=${tmpdepfile-`echo "$depfile" | sed 's/\.\([^.]*\)$/.T\1/'`} + +rm -f "$tmpdepfile" + +# Avoid interferences from the environment. +gccflag= dashmflag= + +# Some modes work just like other modes, but use different flags. We +# parameterize here, but still list the modes in the big case below, +# to make depend.m4 easier to write. Note that we *cannot* use a case +# here, because this file can only contain one case statement. +if test "$depmode" = hp; then + # HP compiler uses -M and no extra arg. + gccflag=-M + depmode=gcc +fi + +if test "$depmode" = dashXmstdout; then + # This is just like dashmstdout with a different argument. + dashmflag=-xM + depmode=dashmstdout +fi + +cygpath_u="cygpath -u -f -" +if test "$depmode" = msvcmsys; then + # This is just like msvisualcpp but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvisualcpp +fi + +if test "$depmode" = msvc7msys; then + # This is just like msvc7 but w/o cygpath translation. + # Just convert the backslash-escaped backslashes to single forward + # slashes to satisfy depend.m4 + cygpath_u='sed s,\\\\,/,g' + depmode=msvc7 +fi + +if test "$depmode" = xlc; then + # IBM C/C++ Compilers xlc/xlC can output gcc-like dependency information. + gccflag=-qmakedep=gcc,-MF + depmode=gcc +fi + +case "$depmode" in +gcc3) +## gcc 3 implements dependency tracking that does exactly what +## we want. Yay! Note: for some reason libtool 1.4 doesn't like +## it if -MD -MP comes after the -MF stuff. Hmm. +## Unfortunately, FreeBSD c89 acceptance of flags depends upon +## the command line argument order; so add the flags where they +## appear in depend2.am. Note that the slowdown incurred here +## affects only configure: in makefiles, %FASTDEP% shortcuts this. + for arg + do + case $arg in + -c) set fnord "$@" -MT "$object" -MD -MP -MF "$tmpdepfile" "$arg" ;; + *) set fnord "$@" "$arg" ;; + esac + shift # fnord + shift # $arg + done + "$@" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + mv "$tmpdepfile" "$depfile" + ;; + +gcc) +## Note that this doesn't just cater to obsosete pre-3.x GCC compilers. +## but also to in-use compilers like IMB xlc/xlC and the HP C compiler. +## (see the conditional assignment to $gccflag above). +## There are various ways to get dependency output from gcc. Here's +## why we pick this rather obscure method: +## - Don't want to use -MD because we'd like the dependencies to end +## up in a subdir. Having to rename by hand is ugly. +## (We might end up doing this anyway to support other compilers.) +## - The DEPENDENCIES_OUTPUT environment variable makes gcc act like +## -MM, not -M (despite what the docs say). Also, it might not be +## supported by the other compilers which use the 'gcc' depmode. +## - Using -M directly means running the compiler twice (even worse +## than renaming). + if test -z "$gccflag"; then + gccflag=-MD, + fi + "$@" -Wp,"$gccflag$tmpdepfile" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + # The second -e expression handles DOS-style file names with drive + # letters. + sed -e 's/^[^:]*: / /' \ + -e 's/^['$alpha']:\/[^:]*: / /' < "$tmpdepfile" >> "$depfile" +## This next piece of magic avoids the "deleted header file" problem. +## The problem is that when a header file which appears in a .P file +## is deleted, the dependency causes make to die (because there is +## typically no way to rebuild the header). We avoid this by adding +## dummy dependencies for each header file. Too bad gcc doesn't do +## this for us directly. +## Some versions of gcc put a space before the ':'. On the theory +## that the space means something, we add a space to the output as +## well. hp depmode also adds that space, but also prefixes the VPATH +## to the object. Take care to not repeat it in the output. +## Some versions of the HPUX 10.20 sed can't process this invocation +## correctly. Breaking it into two sed invocations is a workaround. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^\\$//' -e '/^$/d' -e "s|.*$object$||" -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +sgi) + if test "$libtool" = yes; then + "$@" "-Wp,-MDupdate,$tmpdepfile" + else + "$@" -MDupdate "$tmpdepfile" + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + + if test -f "$tmpdepfile"; then # yes, the sourcefile depend on other files + echo "$object : \\" > "$depfile" + # Clip off the initial element (the dependent). Don't try to be + # clever and replace this with sed code, as IRIX sed won't handle + # lines with more than a fixed number of characters (4096 in + # IRIX 6.2 sed, 8192 in IRIX 6.5). We also remove comment lines; + # the IRIX cc adds comments like '#:fec' to the end of the + # dependency line. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' \ + | tr "$nl" ' ' >> "$depfile" + echo >> "$depfile" + # The second pass generates a dummy entry for each header file. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^.*\.o://' -e 's/#.*$//' -e '/^$/ d' -e 's/$/:/' \ + >> "$depfile" + else + make_dummy_depfile + fi + rm -f "$tmpdepfile" + ;; + +xlc) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +aix) + # The C for AIX Compiler uses -M and outputs the dependencies + # in a .u file. In older versions, this file always lives in the + # current directory. Also, the AIX compiler puts '$object:' at the + # start of each line; $object doesn't have directory information. + # Version 6 uses the directory in both cases. + set_dir_from "$object" + set_base_from "$object" + if test "$libtool" = yes; then + tmpdepfile1=$dir$base.u + tmpdepfile2=$base.u + tmpdepfile3=$dir.libs/$base.u + "$@" -Wc,-M + else + tmpdepfile1=$dir$base.u + tmpdepfile2=$dir$base.u + tmpdepfile3=$dir$base.u + "$@" -M + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + do + test -f "$tmpdepfile" && break + done + aix_post_process_depfile + ;; + +tcc) + # tcc (Tiny C Compiler) understand '-MD -MF file' since version 0.9.26 + # FIXME: That version still under development at the moment of writing. + # Make that this statement remains true also for stable, released + # versions. + # It will wrap lines (doesn't matter whether long or short) with a + # trailing '\', as in: + # + # foo.o : \ + # foo.c \ + # foo.h \ + # + # It will put a trailing '\' even on the last line, and will use leading + # spaces rather than leading tabs (at least since its commit 0394caf7 + # "Emit spaces for -MD"). + "$@" -MD -MF "$tmpdepfile" + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each non-empty line is of the form 'foo.o : \' or ' dep.h \'. + # We have to change lines of the first kind to '$object: \'. + sed -e "s|.*:|$object :|" < "$tmpdepfile" > "$depfile" + # And for each line of the second kind, we have to emit a 'dep.h:' + # dummy dependency, to avoid the deleted-header problem. + sed -n -e 's|^ *\(.*\) *\\$|\1:|p' < "$tmpdepfile" >> "$depfile" + rm -f "$tmpdepfile" + ;; + +## The order of this option in the case statement is important, since the +## shell code in configure will try each of these formats in the order +## listed in this file. A plain '-MD' option would be understood by many +## compilers, so we must ensure this comes after the gcc and icc options. +pgcc) + # Portland's C compiler understands '-MD'. + # Will always output deps to 'file.d' where file is the root name of the + # source file under compilation, even if file resides in a subdirectory. + # The object file name does not affect the name of the '.d' file. + # pgcc 10.2 will output + # foo.o: sub/foo.c sub/foo.h + # and will wrap long lines using '\' : + # foo.o: sub/foo.c ... \ + # sub/foo.h ... \ + # ... + set_dir_from "$object" + # Use the source, not the object, to determine the base name, since + # that's sadly what pgcc will do too. + set_base_from "$source" + tmpdepfile=$base.d + + # For projects that build the same source file twice into different object + # files, the pgcc approach of using the *source* file root name can cause + # problems in parallel builds. Use a locking strategy to avoid stomping on + # the same $tmpdepfile. + lockdir=$base.d-lock + trap " + echo '$0: caught signal, cleaning up...' >&2 + rmdir '$lockdir' + exit 1 + " 1 2 13 15 + numtries=100 + i=$numtries + while test $i -gt 0; do + # mkdir is a portable test-and-set. + if mkdir "$lockdir" 2>/dev/null; then + # This process acquired the lock. + "$@" -MD + stat=$? + # Release the lock. + rmdir "$lockdir" + break + else + # If the lock is being held by a different process, wait + # until the winning process is done or we timeout. + while test -d "$lockdir" && test $i -gt 0; do + sleep 1 + i=`expr $i - 1` + done + fi + i=`expr $i - 1` + done + trap - 1 2 13 15 + if test $i -le 0; then + echo "$0: failed to acquire lock after $numtries attempts" >&2 + echo "$0: check lockdir '$lockdir'" >&2 + exit 1 + fi + + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + # Each line is of the form `foo.o: dependent.h', + # or `foo.o: dep1.h dep2.h \', or ` dep3.h dep4.h \'. + # Do two passes, one to just change these to + # `$object: dependent.h' and one to simply `dependent.h:'. + sed "s,^[^:]*:,$object :," < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed 's,^[^:]*: \(.*\)$,\1,;s/^\\$//;/^$/d;/:$/d' < "$tmpdepfile" \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +hp2) + # The "hp" stanza above does not work with aCC (C++) and HP's ia64 + # compilers, which have integrated preprocessors. The correct option + # to use with these is +Maked; it writes dependencies to a file named + # 'foo.d', which lands next to the object file, wherever that + # happens to be. + # Much of this is similar to the tru64 case; see comments there. + set_dir_from "$object" + set_base_from "$object" + if test "$libtool" = yes; then + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir.libs/$base.d + "$@" -Wc,+Maked + else + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir$base.d + "$@" +Maked + fi + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" + do + test -f "$tmpdepfile" && break + done + if test -f "$tmpdepfile"; then + sed -e "s,^.*\.[$lower]*:,$object:," "$tmpdepfile" > "$depfile" + # Add 'dependent.h:' lines. + sed -ne '2,${ + s/^ *// + s/ \\*$// + s/$/:/ + p + }' "$tmpdepfile" >> "$depfile" + else + make_dummy_depfile + fi + rm -f "$tmpdepfile" "$tmpdepfile2" + ;; + +tru64) + # The Tru64 compiler uses -MD to generate dependencies as a side + # effect. 'cc -MD -o foo.o ...' puts the dependencies into 'foo.o.d'. + # At least on Alpha/Redhat 6.1, Compaq CCC V6.2-504 seems to put + # dependencies in 'foo.d' instead, so we check for that too. + # Subdirectories are respected. + set_dir_from "$object" + set_base_from "$object" + + if test "$libtool" = yes; then + # Libtool generates 2 separate objects for the 2 libraries. These + # two compilations output dependencies in $dir.libs/$base.o.d and + # in $dir$base.o.d. We have to check for both files, because + # one of the two compilations can be disabled. We should prefer + # $dir$base.o.d over $dir.libs/$base.o.d because the latter is + # automatically cleaned when .libs/ is deleted, while ignoring + # the former would cause a distcleancheck panic. + tmpdepfile1=$dir$base.o.d # libtool 1.5 + tmpdepfile2=$dir.libs/$base.o.d # Likewise. + tmpdepfile3=$dir.libs/$base.d # Compaq CCC V6.2-504 + "$@" -Wc,-MD + else + tmpdepfile1=$dir$base.d + tmpdepfile2=$dir$base.d + tmpdepfile3=$dir$base.d + "$@" -MD + fi + + stat=$? + if test $stat -ne 0; then + rm -f "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + exit $stat + fi + + for tmpdepfile in "$tmpdepfile1" "$tmpdepfile2" "$tmpdepfile3" + do + test -f "$tmpdepfile" && break + done + # Same post-processing that is required for AIX mode. + aix_post_process_depfile + ;; + +msvc7) + if test "$libtool" = yes; then + showIncludes=-Wc,-showIncludes + else + showIncludes=-showIncludes + fi + "$@" $showIncludes > "$tmpdepfile" + stat=$? + grep -v '^Note: including file: ' "$tmpdepfile" + if test $stat -ne 0; then + rm -f "$tmpdepfile" + exit $stat + fi + rm -f "$depfile" + echo "$object : \\" > "$depfile" + # The first sed program below extracts the file names and escapes + # backslashes for cygpath. The second sed program outputs the file + # name when reading, but also accumulates all include files in the + # hold buffer in order to output them again at the end. This only + # works with sed implementations that can handle large buffers. + sed < "$tmpdepfile" -n ' +/^Note: including file: *\(.*\)/ { + s//\1/ + s/\\/\\\\/g + p +}' | $cygpath_u | sort -u | sed -n ' +s/ /\\ /g +s/\(.*\)/'"$tab"'\1 \\/p +s/.\(.*\) \\/\1:/ +H +$ { + s/.*/'"$tab"'/ + G + p +}' >> "$depfile" + echo >> "$depfile" # make sure the fragment doesn't end with a backslash + rm -f "$tmpdepfile" + ;; + +msvc7msys) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +#nosideeffect) + # This comment above is used by automake to tell side-effect + # dependency tracking mechanisms from slower ones. + +dashmstdout) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout, regardless of -o. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + # Remove '-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + test -z "$dashmflag" && dashmflag=-M + # Require at least two characters before searching for ':' + # in the target name. This is to cope with DOS-style filenames: + # a dependency such as 'c:/foo/bar' could be seen as target 'c' otherwise. + "$@" $dashmflag | + sed "s|^[$tab ]*[^:$tab ][^:][^:]*:[$tab ]*|$object: |" > "$tmpdepfile" + rm -f "$depfile" + cat < "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process this sed invocation + # correctly. Breaking it into two sed invocations is a workaround. + tr ' ' "$nl" < "$tmpdepfile" \ + | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +dashXmstdout) + # This case only exists to satisfy depend.m4. It is never actually + # run, as this mode is specially recognized in the preamble. + exit 1 + ;; + +makedepend) + "$@" || exit $? + # Remove any Libtool call + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + # X makedepend + shift + cleared=no eat=no + for arg + do + case $cleared in + no) + set ""; shift + cleared=yes ;; + esac + if test $eat = yes; then + eat=no + continue + fi + case "$arg" in + -D*|-I*) + set fnord "$@" "$arg"; shift ;; + # Strip any option that makedepend may not understand. Remove + # the object too, otherwise makedepend will parse it as a source file. + -arch) + eat=yes ;; + -*|$object) + ;; + *) + set fnord "$@" "$arg"; shift ;; + esac + done + obj_suffix=`echo "$object" | sed 's/^.*\././'` + touch "$tmpdepfile" + ${MAKEDEPEND-makedepend} -o"$obj_suffix" -f"$tmpdepfile" "$@" + rm -f "$depfile" + # makedepend may prepend the VPATH from the source file name to the object. + # No need to regex-escape $object, excess matching of '.' is harmless. + sed "s|^.*\($object *:\)|\1|" "$tmpdepfile" > "$depfile" + # Some versions of the HPUX 10.20 sed can't process the last invocation + # correctly. Breaking it into two sed invocations is a workaround. + sed '1,2d' "$tmpdepfile" \ + | tr ' ' "$nl" \ + | sed -e 's/^\\$//' -e '/^$/d' -e '/:$/d' \ + | sed -e 's/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" "$tmpdepfile".bak + ;; + +cpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + # Remove '-o $object'. + IFS=" " + for arg + do + case $arg in + -o) + shift + ;; + $object) + shift + ;; + *) + set fnord "$@" "$arg" + shift # fnord + shift # $arg + ;; + esac + done + + "$@" -E \ + | sed -n -e '/^# [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + -e '/^#line [0-9][0-9]* "\([^"]*\)".*/ s:: \1 \\:p' \ + | sed '$ s: \\$::' > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + cat < "$tmpdepfile" >> "$depfile" + sed < "$tmpdepfile" '/^$/d;s/^ //;s/ \\$//;s/$/ :/' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvisualcpp) + # Important note: in order to support this mode, a compiler *must* + # always write the preprocessed file to stdout. + "$@" || exit $? + + # Remove the call to Libtool. + if test "$libtool" = yes; then + while test "X$1" != 'X--mode=compile'; do + shift + done + shift + fi + + IFS=" " + for arg + do + case "$arg" in + -o) + shift + ;; + $object) + shift + ;; + "-Gm"|"/Gm"|"-Gi"|"/Gi"|"-ZI"|"/ZI") + set fnord "$@" + shift + shift + ;; + *) + set fnord "$@" "$arg" + shift + shift + ;; + esac + done + "$@" -E 2>/dev/null | + sed -n '/^#line [0-9][0-9]* "\([^"]*\)"/ s::\1:p' | $cygpath_u | sort -u > "$tmpdepfile" + rm -f "$depfile" + echo "$object : \\" > "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::'"$tab"'\1 \\:p' >> "$depfile" + echo "$tab" >> "$depfile" + sed < "$tmpdepfile" -n -e 's% %\\ %g' -e '/^\(.*\)$/ s::\1\::p' >> "$depfile" + rm -f "$tmpdepfile" + ;; + +msvcmsys) + # This case exists only to let depend.m4 do its work. It works by + # looking at the text of this script. This case will never be run, + # since it is checked for above. + exit 1 + ;; + +none) + exec "$@" + ;; + +*) + echo "Unknown depmode $depmode" 1>&2 + exit 1 + ;; +esac + +exit 0 + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/shared/sentry/external/breakpad/autotools/install-sh b/shared/sentry/external/breakpad/autotools/install-sh new file mode 100755 index 000000000..0b0fdcbba --- /dev/null +++ b/shared/sentry/external/breakpad/autotools/install-sh @@ -0,0 +1,501 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2013-12-25.23; # UTC + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# 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 +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# 'make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. + +tab=' ' +nl=' +' +IFS=" $tab$nl" + +# Set DOITPROG to "echo" to test this script. + +doit=${DOITPROG-} +doit_exec=${doit:-exec} + +# Put in absolute file names if you don't have them in your path; +# or use environment vars. + +chgrpprog=${CHGRPPROG-chgrp} +chmodprog=${CHMODPROG-chmod} +chownprog=${CHOWNPROG-chown} +cmpprog=${CMPPROG-cmp} +cpprog=${CPPROG-cp} +mkdirprog=${MKDIRPROG-mkdir} +mvprog=${MVPROG-mv} +rmprog=${RMPROG-rm} +stripprog=${STRIPPROG-strip} + +posix_mkdir= + +# Desired mode of installed file. +mode=0755 + +chgrpcmd= +chmodcmd=$chmodprog +chowncmd= +mvcmd=$mvprog +rmcmd="$rmprog -f" +stripcmd= + +src= +dst= +dir_arg= +dst_arg= + +copy_on_change=false +is_target_a_directory=possibly + +usage="\ +Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: + --help display this help and exit. + --version display version info and exit. + + -c (ignored) + -C install only if different (preserve the last data modification time) + -d create directories instead of installing files. + -g GROUP $chgrpprog installed files to GROUP. + -m MODE $chmodprog installed files to MODE. + -o USER $chownprog installed files to USER. + -s $stripprog installed files. + -t DIRECTORY install into DIRECTORY. + -T report an error if DSTFILE is a directory. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG + RMPROG STRIPPROG +" + +while test $# -ne 0; do + case $1 in + -c) ;; + + -C) copy_on_change=true;; + + -d) dir_arg=true;; + + -g) chgrpcmd="$chgrpprog $2" + shift;; + + --help) echo "$usage"; exit $?;; + + -m) mode=$2 + case $mode in + *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) + echo "$0: invalid mode: $mode" >&2 + exit 1;; + esac + shift;; + + -o) chowncmd="$chownprog $2" + shift;; + + -s) stripcmd=$stripprog;; + + -t) + is_target_a_directory=always + dst_arg=$2 + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + shift;; + + -T) is_target_a_directory=never;; + + --version) echo "$0 $scriptversion"; exit $?;; + + --) shift + break;; + + -*) echo "$0: invalid option: $1" >&2 + exit 1;; + + *) break;; + esac + shift +done + +# We allow the use of options -d and -T together, by making -d +# take the precedence; this is for compatibility with GNU install. + +if test -n "$dir_arg"; then + if test -n "$dst_arg"; then + echo "$0: target directory not allowed when installing a directory." >&2 + exit 1 + fi +fi + +if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then + # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dst_arg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dst_arg" + shift # fnord + fi + shift # arg + dst_arg=$arg + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + done +fi + +if test $# -eq 0; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call 'install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +if test -z "$dir_arg"; then + if test $# -gt 1 || test "$is_target_a_directory" = always; then + if test ! -d "$dst_arg"; then + echo "$0: $dst_arg: Is not a directory." >&2 + exit 1 + fi + fi +fi + +if test -z "$dir_arg"; then + do_exit='(exit $ret); exit $ret' + trap "ret=129; $do_exit" 1 + trap "ret=130; $do_exit" 2 + trap "ret=141; $do_exit" 13 + trap "ret=143; $do_exit" 15 + + # Set umask so as not to create temps with too-generous modes. + # However, 'strip' requires both read and write access to temps. + case $mode in + # Optimize common cases. + *644) cp_umask=133;; + *755) cp_umask=22;; + + *[0-7]) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw='% 200' + fi + cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; + *) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw=,u+rw + fi + cp_umask=$mode$u_plus_rw;; + esac +fi + +for src +do + # Protect names problematic for 'test' and other utilities. + case $src in + -* | [=\(\)!]) src=./$src;; + esac + + if test -n "$dir_arg"; then + dst=$src + dstdir=$dst + test -d "$dstdir" + dstdir_status=$? + else + + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dst_arg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + dst=$dst_arg + + # If destination is a directory, append the input filename; won't work + # if double slashes aren't ignored. + if test -d "$dst"; then + if test "$is_target_a_directory" = never; then + echo "$0: $dst_arg: Is a directory" >&2 + exit 1 + fi + dstdir=$dst + dst=$dstdir/`basename "$src"` + dstdir_status=0 + else + dstdir=`dirname "$dst"` + test -d "$dstdir" + dstdir_status=$? + fi + fi + + obsolete_mkdir_used=false + + if test $dstdir_status != 0; then + case $posix_mkdir in + '') + # Create intermediate dirs using mode 755 as modified by the umask. + # This is like FreeBSD 'install' as of 1997-10-28. + umask=`umask` + case $stripcmd.$umask in + # Optimize common cases. + *[2367][2367]) mkdir_umask=$umask;; + .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; + + *[0-7]) + mkdir_umask=`expr $umask + 22 \ + - $umask % 100 % 40 + $umask % 20 \ + - $umask % 10 % 4 + $umask % 2 + `;; + *) mkdir_umask=$umask,go-w;; + esac + + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi + + posix_mkdir=false + case $umask in + *[123567][0-7][0-7]) + # POSIX mkdir -p sets u+wx bits regardless of umask, which + # is incompatible with FreeBSD 'install' when (umask & 300) != 0. + ;; + *) + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 + + if (umask $mkdir_umask && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + ls_ld_tmpdir=`ls -ld "$tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/d" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null + fi + trap '' 0;; + esac;; + esac + + if + $posix_mkdir && ( + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + ) + then : + else + + # The umask is ridiculous, or mkdir does not conform to POSIX, + # or it failed possibly due to a race condition. Create the + # directory the slow way, step by step, checking for races as we go. + + case $dstdir in + /*) prefix='/';; + [-=\(\)!]*) prefix='./';; + *) prefix='';; + esac + + oIFS=$IFS + IFS=/ + set -f + set fnord $dstdir + shift + set +f + IFS=$oIFS + + prefixes= + + for d + do + test X"$d" = X && continue + + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask=$mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ + done + + if test -n "$prefixes"; then + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true + fi + fi + fi + + if test -n "$dir_arg"; then + { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && + { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || + test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 + else + + # Make a couple of temp file names in the proper directory. + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + + # Copy the file name to the temp name. + (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && + { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && + { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && + + # If -C, don't bother to copy if it wouldn't change the file. + if $copy_on_change && + old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && + new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && + set -f && + set X $old && old=:$2:$4:$5:$6 && + set X $new && new=:$2:$4:$5:$6 && + set +f && + test "$old" = "$new" && + $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 + then + rm -f "$dsttmp" + else + # Rename the file to the real destination. + $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || + + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + { + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + test ! -f "$dst" || + $doit $rmcmd -f "$dst" 2>/dev/null || + { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && + { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } + } || + { echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" + } + fi || exit 1 + + trap '' 0 + fi +done + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/shared/sentry/external/breakpad/autotools/ltmain.sh b/shared/sentry/external/breakpad/autotools/ltmain.sh new file mode 100755 index 000000000..a72f2fd78 --- /dev/null +++ b/shared/sentry/external/breakpad/autotools/ltmain.sh @@ -0,0 +1,8406 @@ +# Generated from ltmain.m4sh. + +# ltmain.sh (GNU libtool) 2.2.6b +# Written by Gordon Matzigkeit , 1996 + +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007 2008 Free Software Foundation, Inc. +# This is free software; see the source for copying conditions. There is NO +# warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + +# GNU Libtool is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# As a special exception to the GNU General Public License, +# if you distribute this file as part of a program or library that +# is built using GNU Libtool, you may include this file under the +# same distribution terms that you use for the rest of that program. +# +# GNU Libtool is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Libtool; see the file COPYING. If not, a copy +# can be downloaded from http://www.gnu.org/licenses/gpl.html, +# or obtained by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +# Usage: $progname [OPTION]... [MODE-ARG]... +# +# Provide generalized library-building support services. +# +# --config show all configuration variables +# --debug enable verbose shell tracing +# -n, --dry-run display commands without modifying any files +# --features display basic configuration information and exit +# --mode=MODE use operation mode MODE +# --preserve-dup-deps don't remove duplicate dependency libraries +# --quiet, --silent don't print informational messages +# --tag=TAG use configuration variables from tag TAG +# -v, --verbose print informational messages (default) +# --version print version information +# -h, --help print short or long help message +# +# MODE must be one of the following: +# +# clean remove files from the build directory +# compile compile a source file into a libtool object +# execute automatically set library path, then run a program +# finish complete the installation of libtool libraries +# install install libraries or executables +# link create a library or an executable +# uninstall remove libraries from an installed directory +# +# MODE-ARGS vary depending on the MODE. +# Try `$progname --help --mode=MODE' for a more detailed description of MODE. +# +# When reporting a bug, please describe a test case to reproduce it and +# include the following information: +# +# host-triplet: $host +# shell: $SHELL +# compiler: $LTCC +# compiler flags: $LTCFLAGS +# linker: $LD (gnu? $with_gnu_ld) +# $progname: (GNU libtool) 2.2.6b +# automake: $automake_version +# autoconf: $autoconf_version +# +# Report bugs to . + +PROGRAM=ltmain.sh +PACKAGE=libtool +VERSION=2.2.6b +TIMESTAMP="" +package_revision=1.3017 + +# Be Bourne compatible +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +# NLS nuisances: We save the old values to restore during execute mode. +# Only set LANG and LC_ALL to C if already set. +# These must not be set unconditionally because not all systems understand +# e.g. LANG=C (notably SCO). +lt_user_locale= +lt_safe_locale= +for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES +do + eval "if test \"\${$lt_var+set}\" = set; then + save_$lt_var=\$$lt_var + $lt_var=C + export $lt_var + lt_user_locale=\"$lt_var=\\\$save_\$lt_var; \$lt_user_locale\" + lt_safe_locale=\"$lt_var=C; \$lt_safe_locale\" + fi" +done + +$lt_unset CDPATH + + + + + +: ${CP="cp -f"} +: ${ECHO="echo"} +: ${EGREP="/bin/grep -E"} +: ${FGREP="/bin/grep -F"} +: ${GREP="/bin/grep"} +: ${LN_S="ln -s"} +: ${MAKE="make"} +: ${MKDIR="mkdir"} +: ${MV="mv -f"} +: ${RM="rm -f"} +: ${SED="/bin/sed"} +: ${SHELL="${CONFIG_SHELL-/bin/sh}"} +: ${Xsed="$SED -e 1s/^X//"} + +# Global variables: +EXIT_SUCCESS=0 +EXIT_FAILURE=1 +EXIT_MISMATCH=63 # $? = 63 is used to indicate version mismatch to missing. +EXIT_SKIP=77 # $? = 77 is used to indicate a skipped test to automake. + +exit_status=$EXIT_SUCCESS + +# Make sure IFS has a sensible default +lt_nl=' +' +IFS=" $lt_nl" + +dirname="s,/[^/]*$,," +basename="s,^.*/,," + +# func_dirname_and_basename file append nondir_replacement +# perform func_basename and func_dirname in a single function +# call: +# dirname: Compute the dirname of FILE. If nonempty, +# add APPEND to the result, otherwise set result +# to NONDIR_REPLACEMENT. +# value returned in "$func_dirname_result" +# basename: Compute filename of FILE. +# value retuned in "$func_basename_result" +# Implementation must be kept synchronized with func_dirname +# and func_basename. For efficiency, we do not delegate to +# those functions but instead duplicate the functionality here. +func_dirname_and_basename () +{ + # Extract subdirectory from the argument. + func_dirname_result=`$ECHO "X${1}" | $Xsed -e "$dirname"` + if test "X$func_dirname_result" = "X${1}"; then + func_dirname_result="${3}" + else + func_dirname_result="$func_dirname_result${2}" + fi + func_basename_result=`$ECHO "X${1}" | $Xsed -e "$basename"` +} + +# Generated shell functions inserted here. + +# Work around backward compatibility issue on IRIX 6.5. On IRIX 6.4+, sh +# is ksh but when the shell is invoked as "sh" and the current value of +# the _XPG environment variable is not equal to 1 (one), the special +# positional parameter $0, within a function call, is the name of the +# function. +progpath="$0" + +# The name of this program: +# In the unlikely event $progname began with a '-', it would play havoc with +# func_echo (imagine progname=-n), so we prepend ./ in that case: +func_dirname_and_basename "$progpath" +progname=$func_basename_result +case $progname in + -*) progname=./$progname ;; +esac + +# Make sure we have an absolute path for reexecution: +case $progpath in + [\\/]*|[A-Za-z]:\\*) ;; + *[\\/]*) + progdir=$func_dirname_result + progdir=`cd "$progdir" && pwd` + progpath="$progdir/$progname" + ;; + *) + save_IFS="$IFS" + IFS=: + for progdir in $PATH; do + IFS="$save_IFS" + test -x "$progdir/$progname" && break + done + IFS="$save_IFS" + test -n "$progdir" || progdir=`pwd` + progpath="$progdir/$progname" + ;; +esac + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +Xsed="${SED}"' -e 1s/^X//' +sed_quote_subst='s/\([`"$\\]\)/\\\1/g' + +# Same as above, but do not quote variable references. +double_quote_subst='s/\(["`\\]\)/\\\1/g' + +# Re-`\' parameter expansions in output of double_quote_subst that were +# `\'-ed in input to the same. If an odd number of `\' preceded a '$' +# in input to double_quote_subst, that '$' was protected from expansion. +# Since each input `\' is now two `\'s, look for any number of runs of +# four `\'s followed by two `\'s and then a '$'. `\' that '$'. +bs='\\' +bs2='\\\\' +bs4='\\\\\\\\' +dollar='\$' +sed_double_backslash="\ + s/$bs4/&\\ +/g + s/^$bs2$dollar/$bs&/ + s/\\([^$bs]\\)$bs2$dollar/\\1$bs2$bs$dollar/g + s/\n//g" + +# Standard options: +opt_dry_run=false +opt_help=false +opt_quiet=false +opt_verbose=false +opt_warning=: + +# func_echo arg... +# Echo program name prefixed message, along with the current mode +# name if it has been set yet. +func_echo () +{ + $ECHO "$progname${mode+: }$mode: $*" +} + +# func_verbose arg... +# Echo program name prefixed message in verbose mode only. +func_verbose () +{ + $opt_verbose && func_echo ${1+"$@"} + + # A bug in bash halts the script if the last line of a function + # fails when set -e is in force, so we need another command to + # work around that: + : +} + +# func_error arg... +# Echo program name prefixed message to standard error. +func_error () +{ + $ECHO "$progname${mode+: }$mode: "${1+"$@"} 1>&2 +} + +# func_warning arg... +# Echo program name prefixed warning message to standard error. +func_warning () +{ + $opt_warning && $ECHO "$progname${mode+: }$mode: warning: "${1+"$@"} 1>&2 + + # bash bug again: + : +} + +# func_fatal_error arg... +# Echo program name prefixed message to standard error, and exit. +func_fatal_error () +{ + func_error ${1+"$@"} + exit $EXIT_FAILURE +} + +# func_fatal_help arg... +# Echo program name prefixed message to standard error, followed by +# a help hint, and exit. +func_fatal_help () +{ + func_error ${1+"$@"} + func_fatal_error "$help" +} +help="Try \`$progname --help' for more information." ## default + + +# func_grep expression filename +# Check whether EXPRESSION matches any line of FILENAME, without output. +func_grep () +{ + $GREP "$1" "$2" >/dev/null 2>&1 +} + + +# func_mkdir_p directory-path +# Make sure the entire path to DIRECTORY-PATH is available. +func_mkdir_p () +{ + my_directory_path="$1" + my_dir_list= + + if test -n "$my_directory_path" && test "$opt_dry_run" != ":"; then + + # Protect directory names starting with `-' + case $my_directory_path in + -*) my_directory_path="./$my_directory_path" ;; + esac + + # While some portion of DIR does not yet exist... + while test ! -d "$my_directory_path"; do + # ...make a list in topmost first order. Use a colon delimited + # list incase some portion of path contains whitespace. + my_dir_list="$my_directory_path:$my_dir_list" + + # If the last portion added has no slash in it, the list is done + case $my_directory_path in */*) ;; *) break ;; esac + + # ...otherwise throw away the child directory and loop + my_directory_path=`$ECHO "X$my_directory_path" | $Xsed -e "$dirname"` + done + my_dir_list=`$ECHO "X$my_dir_list" | $Xsed -e 's,:*$,,'` + + save_mkdir_p_IFS="$IFS"; IFS=':' + for my_dir in $my_dir_list; do + IFS="$save_mkdir_p_IFS" + # mkdir can fail with a `File exist' error if two processes + # try to create one of the directories concurrently. Don't + # stop in that case! + $MKDIR "$my_dir" 2>/dev/null || : + done + IFS="$save_mkdir_p_IFS" + + # Bail out if we (or some other process) failed to create a directory. + test -d "$my_directory_path" || \ + func_fatal_error "Failed to create \`$1'" + fi +} + + +# func_mktempdir [string] +# Make a temporary directory that won't clash with other running +# libtool processes, and avoids race conditions if possible. If +# given, STRING is the basename for that directory. +func_mktempdir () +{ + my_template="${TMPDIR-/tmp}/${1-$progname}" + + if test "$opt_dry_run" = ":"; then + # Return a directory name, but don't create it in dry-run mode + my_tmpdir="${my_template}-$$" + else + + # If mktemp works, use that first and foremost + my_tmpdir=`mktemp -d "${my_template}-XXXXXXXX" 2>/dev/null` + + if test ! -d "$my_tmpdir"; then + # Failing that, at least try and use $RANDOM to avoid a race + my_tmpdir="${my_template}-${RANDOM-0}$$" + + save_mktempdir_umask=`umask` + umask 0077 + $MKDIR "$my_tmpdir" + umask $save_mktempdir_umask + fi + + # If we're not in dry-run mode, bomb out on failure + test -d "$my_tmpdir" || \ + func_fatal_error "cannot create temporary directory \`$my_tmpdir'" + fi + + $ECHO "X$my_tmpdir" | $Xsed +} + + +# func_quote_for_eval arg +# Aesthetically quote ARG to be evaled later. +# This function returns two values: FUNC_QUOTE_FOR_EVAL_RESULT +# is double-quoted, suitable for a subsequent eval, whereas +# FUNC_QUOTE_FOR_EVAL_UNQUOTED_RESULT has merely all characters +# which are still active within double quotes backslashified. +func_quote_for_eval () +{ + case $1 in + *[\\\`\"\$]*) + func_quote_for_eval_unquoted_result=`$ECHO "X$1" | $Xsed -e "$sed_quote_subst"` ;; + *) + func_quote_for_eval_unquoted_result="$1" ;; + esac + + case $func_quote_for_eval_unquoted_result in + # Double-quote args containing shell metacharacters to delay + # word splitting, command substitution and and variable + # expansion for a subsequent eval. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + func_quote_for_eval_result="\"$func_quote_for_eval_unquoted_result\"" + ;; + *) + func_quote_for_eval_result="$func_quote_for_eval_unquoted_result" + esac +} + + +# func_quote_for_expand arg +# Aesthetically quote ARG to be evaled later; same as above, +# but do not quote variable references. +func_quote_for_expand () +{ + case $1 in + *[\\\`\"]*) + my_arg=`$ECHO "X$1" | $Xsed \ + -e "$double_quote_subst" -e "$sed_double_backslash"` ;; + *) + my_arg="$1" ;; + esac + + case $my_arg in + # Double-quote args containing shell metacharacters to delay + # word splitting and command substitution for a subsequent eval. + # Many Bourne shells cannot handle close brackets correctly + # in scan sets, so we specify it separately. + *[\[\~\#\^\&\*\(\)\{\}\|\;\<\>\?\'\ \ ]*|*]*|"") + my_arg="\"$my_arg\"" + ;; + esac + + func_quote_for_expand_result="$my_arg" +} + + +# func_show_eval cmd [fail_exp] +# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is +# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP +# is given, then evaluate it. +func_show_eval () +{ + my_cmd="$1" + my_fail_exp="${2-:}" + + ${opt_silent-false} || { + func_quote_for_expand "$my_cmd" + eval "func_echo $func_quote_for_expand_result" + } + + if ${opt_dry_run-false}; then :; else + eval "$my_cmd" + my_status=$? + if test "$my_status" -eq 0; then :; else + eval "(exit $my_status); $my_fail_exp" + fi + fi +} + + +# func_show_eval_locale cmd [fail_exp] +# Unless opt_silent is true, then output CMD. Then, if opt_dryrun is +# not true, evaluate CMD. If the evaluation of CMD fails, and FAIL_EXP +# is given, then evaluate it. Use the saved locale for evaluation. +func_show_eval_locale () +{ + my_cmd="$1" + my_fail_exp="${2-:}" + + ${opt_silent-false} || { + func_quote_for_expand "$my_cmd" + eval "func_echo $func_quote_for_expand_result" + } + + if ${opt_dry_run-false}; then :; else + eval "$lt_user_locale + $my_cmd" + my_status=$? + eval "$lt_safe_locale" + if test "$my_status" -eq 0; then :; else + eval "(exit $my_status); $my_fail_exp" + fi + fi +} + + + + + +# func_version +# Echo version message to standard output and exit. +func_version () +{ + $SED -n '/^# '$PROGRAM' (GNU /,/# warranty; / { + s/^# // + s/^# *$// + s/\((C)\)[ 0-9,-]*\( [1-9][0-9]*\)/\1\2/ + p + }' < "$progpath" + exit $? +} + +# func_usage +# Echo short help message to standard output and exit. +func_usage () +{ + $SED -n '/^# Usage:/,/# -h/ { + s/^# // + s/^# *$// + s/\$progname/'$progname'/ + p + }' < "$progpath" + $ECHO + $ECHO "run \`$progname --help | more' for full usage" + exit $? +} + +# func_help +# Echo long help message to standard output and exit. +func_help () +{ + $SED -n '/^# Usage:/,/# Report bugs to/ { + s/^# // + s/^# *$// + s*\$progname*'$progname'* + s*\$host*'"$host"'* + s*\$SHELL*'"$SHELL"'* + s*\$LTCC*'"$LTCC"'* + s*\$LTCFLAGS*'"$LTCFLAGS"'* + s*\$LD*'"$LD"'* + s/\$with_gnu_ld/'"$with_gnu_ld"'/ + s/\$automake_version/'"`(automake --version) 2>/dev/null |$SED 1q`"'/ + s/\$autoconf_version/'"`(autoconf --version) 2>/dev/null |$SED 1q`"'/ + p + }' < "$progpath" + exit $? +} + +# func_missing_arg argname +# Echo program name prefixed message to standard error and set global +# exit_cmd. +func_missing_arg () +{ + func_error "missing argument for $1" + exit_cmd=exit +} + +exit_cmd=: + + + + + +# Check that we have a working $ECHO. +if test "X$1" = X--no-reexec; then + # Discard the --no-reexec flag, and continue. + shift +elif test "X$1" = X--fallback-echo; then + # Avoid inline document here, it may be left over + : +elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t'; then + # Yippee, $ECHO works! + : +else + # Restart under the correct shell, and then maybe $ECHO will work. + exec $SHELL "$progpath" --no-reexec ${1+"$@"} +fi + +if test "X$1" = X--fallback-echo; then + # used as fallback echo + shift + cat </dev/null 2>&1; then + taglist="$taglist $tagname" + + # Evaluate the configuration. Be careful to quote the path + # and the sed script, to avoid splitting on whitespace, but + # also don't use non-portable quotes within backquotes within + # quotes we have to do it in 2 steps: + extractedcf=`$SED -n -e "$sed_extractcf" < "$progpath"` + eval "$extractedcf" + else + func_error "ignoring unknown tag $tagname" + fi + ;; + esac +} + +# Parse options once, thoroughly. This comes as soon as possible in +# the script to make things like `libtool --version' happen quickly. +{ + + # Shorthand for --mode=foo, only valid as the first argument + case $1 in + clean|clea|cle|cl) + shift; set dummy --mode clean ${1+"$@"}; shift + ;; + compile|compil|compi|comp|com|co|c) + shift; set dummy --mode compile ${1+"$@"}; shift + ;; + execute|execut|execu|exec|exe|ex|e) + shift; set dummy --mode execute ${1+"$@"}; shift + ;; + finish|finis|fini|fin|fi|f) + shift; set dummy --mode finish ${1+"$@"}; shift + ;; + install|instal|insta|inst|ins|in|i) + shift; set dummy --mode install ${1+"$@"}; shift + ;; + link|lin|li|l) + shift; set dummy --mode link ${1+"$@"}; shift + ;; + uninstall|uninstal|uninsta|uninst|unins|unin|uni|un|u) + shift; set dummy --mode uninstall ${1+"$@"}; shift + ;; + esac + + # Parse non-mode specific arguments: + while test "$#" -gt 0; do + opt="$1" + shift + + case $opt in + --config) func_config ;; + + --debug) preserve_args="$preserve_args $opt" + func_echo "enabling shell trace mode" + opt_debug='set -x' + $opt_debug + ;; + + -dlopen) test "$#" -eq 0 && func_missing_arg "$opt" && break + execute_dlfiles="$execute_dlfiles $1" + shift + ;; + + --dry-run | -n) opt_dry_run=: ;; + --features) func_features ;; + --finish) mode="finish" ;; + + --mode) test "$#" -eq 0 && func_missing_arg "$opt" && break + case $1 in + # Valid mode arguments: + clean) ;; + compile) ;; + execute) ;; + finish) ;; + install) ;; + link) ;; + relink) ;; + uninstall) ;; + + # Catch anything else as an error + *) func_error "invalid argument for $opt" + exit_cmd=exit + break + ;; + esac + + mode="$1" + shift + ;; + + --preserve-dup-deps) + opt_duplicate_deps=: ;; + + --quiet|--silent) preserve_args="$preserve_args $opt" + opt_silent=: + ;; + + --verbose| -v) preserve_args="$preserve_args $opt" + opt_silent=false + ;; + + --tag) test "$#" -eq 0 && func_missing_arg "$opt" && break + preserve_args="$preserve_args $opt $1" + func_enable_tag "$1" # tagname is set here + shift + ;; + + # Separate optargs to long options: + -dlopen=*|--mode=*|--tag=*) + func_opt_split "$opt" + set dummy "$func_opt_split_opt" "$func_opt_split_arg" ${1+"$@"} + shift + ;; + + -\?|-h) func_usage ;; + --help) opt_help=: ;; + --version) func_version ;; + + -*) func_fatal_help "unrecognized option \`$opt'" ;; + + *) nonopt="$opt" + break + ;; + esac + done + + + case $host in + *cygwin* | *mingw* | *pw32* | *cegcc*) + # don't eliminate duplications in $postdeps and $predeps + opt_duplicate_compiler_generated_deps=: + ;; + *) + opt_duplicate_compiler_generated_deps=$opt_duplicate_deps + ;; + esac + + # Having warned about all mis-specified options, bail out if + # anything was wrong. + $exit_cmd $EXIT_FAILURE +} + +# func_check_version_match +# Ensure that we are using m4 macros, and libtool script from the same +# release of libtool. +func_check_version_match () +{ + if test "$package_revision" != "$macro_revision"; then + if test "$VERSION" != "$macro_version"; then + if test -z "$macro_version"; then + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, but the +$progname: definition of this LT_INIT comes from an older release. +$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION +$progname: and run autoconf again. +_LT_EOF + else + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, but the +$progname: definition of this LT_INIT comes from $PACKAGE $macro_version. +$progname: You should recreate aclocal.m4 with macros from $PACKAGE $VERSION +$progname: and run autoconf again. +_LT_EOF + fi + else + cat >&2 <<_LT_EOF +$progname: Version mismatch error. This is $PACKAGE $VERSION, revision $package_revision, +$progname: but the definition of this LT_INIT comes from revision $macro_revision. +$progname: You should recreate aclocal.m4 with macros from revision $package_revision +$progname: of $PACKAGE $VERSION and run autoconf again. +_LT_EOF + fi + + exit $EXIT_MISMATCH + fi +} + + +## ----------- ## +## Main. ## +## ----------- ## + +$opt_help || { + # Sanity checks first: + func_check_version_match + + if test "$build_libtool_libs" != yes && test "$build_old_libs" != yes; then + func_fatal_configuration "not configured to build any kind of library" + fi + + test -z "$mode" && func_fatal_error "error: you must specify a MODE." + + + # Darwin sucks + eval std_shrext=\"$shrext_cmds\" + + + # Only execute mode is allowed to have -dlopen flags. + if test -n "$execute_dlfiles" && test "$mode" != execute; then + func_error "unrecognized option \`-dlopen'" + $ECHO "$help" 1>&2 + exit $EXIT_FAILURE + fi + + # Change the help message to a mode-specific one. + generic_help="$help" + help="Try \`$progname --help --mode=$mode' for more information." +} + + +# func_lalib_p file +# True iff FILE is a libtool `.la' library or `.lo' object file. +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_lalib_p () +{ + test -f "$1" && + $SED -e 4q "$1" 2>/dev/null \ + | $GREP "^# Generated by .*$PACKAGE" > /dev/null 2>&1 +} + +# func_lalib_unsafe_p file +# True iff FILE is a libtool `.la' library or `.lo' object file. +# This function implements the same check as func_lalib_p without +# resorting to external programs. To this end, it redirects stdin and +# closes it afterwards, without saving the original file descriptor. +# As a safety measure, use it only where a negative result would be +# fatal anyway. Works if `file' does not exist. +func_lalib_unsafe_p () +{ + lalib_p=no + if test -f "$1" && test -r "$1" && exec 5<&0 <"$1"; then + for lalib_p_l in 1 2 3 4 + do + read lalib_p_line + case "$lalib_p_line" in + \#\ Generated\ by\ *$PACKAGE* ) lalib_p=yes; break;; + esac + done + exec 0<&5 5<&- + fi + test "$lalib_p" = yes +} + +# func_ltwrapper_script_p file +# True iff FILE is a libtool wrapper script +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_script_p () +{ + func_lalib_p "$1" +} + +# func_ltwrapper_executable_p file +# True iff FILE is a libtool wrapper executable +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_executable_p () +{ + func_ltwrapper_exec_suffix= + case $1 in + *.exe) ;; + *) func_ltwrapper_exec_suffix=.exe ;; + esac + $GREP "$magic_exe" "$1$func_ltwrapper_exec_suffix" >/dev/null 2>&1 +} + +# func_ltwrapper_scriptname file +# Assumes file is an ltwrapper_executable +# uses $file to determine the appropriate filename for a +# temporary ltwrapper_script. +func_ltwrapper_scriptname () +{ + func_ltwrapper_scriptname_result="" + if func_ltwrapper_executable_p "$1"; then + func_dirname_and_basename "$1" "" "." + func_stripname '' '.exe' "$func_basename_result" + func_ltwrapper_scriptname_result="$func_dirname_result/$objdir/${func_stripname_result}_ltshwrapper" + fi +} + +# func_ltwrapper_p file +# True iff FILE is a libtool wrapper script or wrapper executable +# This function is only a basic sanity check; it will hardly flush out +# determined imposters. +func_ltwrapper_p () +{ + func_ltwrapper_script_p "$1" || func_ltwrapper_executable_p "$1" +} + + +# func_execute_cmds commands fail_cmd +# Execute tilde-delimited COMMANDS. +# If FAIL_CMD is given, eval that upon failure. +# FAIL_CMD may read-access the current command in variable CMD! +func_execute_cmds () +{ + $opt_debug + save_ifs=$IFS; IFS='~' + for cmd in $1; do + IFS=$save_ifs + eval cmd=\"$cmd\" + func_show_eval "$cmd" "${2-:}" + done + IFS=$save_ifs +} + + +# func_source file +# Source FILE, adding directory component if necessary. +# Note that it is not necessary on cygwin/mingw to append a dot to +# FILE even if both FILE and FILE.exe exist: automatic-append-.exe +# behavior happens only for exec(3), not for open(2)! Also, sourcing +# `FILE.' does not work on cygwin managed mounts. +func_source () +{ + $opt_debug + case $1 in + */* | *\\*) . "$1" ;; + *) . "./$1" ;; + esac +} + + +# func_infer_tag arg +# Infer tagged configuration to use if any are available and +# if one wasn't chosen via the "--tag" command line option. +# Only attempt this if the compiler in the base compile +# command doesn't match the default compiler. +# arg is usually of the form 'gcc ...' +func_infer_tag () +{ + $opt_debug + if test -n "$available_tags" && test -z "$tagname"; then + CC_quoted= + for arg in $CC; do + func_quote_for_eval "$arg" + CC_quoted="$CC_quoted $func_quote_for_eval_result" + done + case $@ in + # Blanks in the command may have been stripped by the calling shell, + # but not from the CC environment variable when configure was run. + " $CC "* | "$CC "* | " `$ECHO $CC` "* | "`$ECHO $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$ECHO $CC_quoted` "* | "`$ECHO $CC_quoted` "*) ;; + # Blanks at the start of $base_compile will cause this to fail + # if we don't check for them as well. + *) + for z in $available_tags; do + if $GREP "^# ### BEGIN LIBTOOL TAG CONFIG: $z$" < "$progpath" > /dev/null; then + # Evaluate the configuration. + eval "`${SED} -n -e '/^# ### BEGIN LIBTOOL TAG CONFIG: '$z'$/,/^# ### END LIBTOOL TAG CONFIG: '$z'$/p' < $progpath`" + CC_quoted= + for arg in $CC; do + # Double-quote args containing other shell metacharacters. + func_quote_for_eval "$arg" + CC_quoted="$CC_quoted $func_quote_for_eval_result" + done + case "$@ " in + " $CC "* | "$CC "* | " `$ECHO $CC` "* | "`$ECHO $CC` "* | " $CC_quoted"* | "$CC_quoted "* | " `$ECHO $CC_quoted` "* | "`$ECHO $CC_quoted` "*) + # The compiler in the base compile command matches + # the one in the tagged configuration. + # Assume this is the tagged configuration we want. + tagname=$z + break + ;; + esac + fi + done + # If $tagname still isn't set, then no tagged configuration + # was found and let the user know that the "--tag" command + # line option must be used. + if test -z "$tagname"; then + func_echo "unable to infer tagged configuration" + func_fatal_error "specify a tag with \`--tag'" +# else +# func_verbose "using $tagname tagged configuration" + fi + ;; + esac + fi +} + + + +# func_write_libtool_object output_name pic_name nonpic_name +# Create a libtool object file (analogous to a ".la" file), +# but don't create it if we're doing a dry run. +func_write_libtool_object () +{ + write_libobj=${1} + if test "$build_libtool_libs" = yes; then + write_lobj=\'${2}\' + else + write_lobj=none + fi + + if test "$build_old_libs" = yes; then + write_oldobj=\'${3}\' + else + write_oldobj=none + fi + + $opt_dry_run || { + cat >${write_libobj}T <?"'"'"' &()|`$[]' \ + && func_warning "libobj name \`$libobj' may not contain shell special characters." + func_dirname_and_basename "$obj" "/" "" + objname="$func_basename_result" + xdir="$func_dirname_result" + lobj=${xdir}$objdir/$objname + + test -z "$base_compile" && \ + func_fatal_help "you must specify a compilation command" + + # Delete any leftover library objects. + if test "$build_old_libs" = yes; then + removelist="$obj $lobj $libobj ${libobj}T" + else + removelist="$lobj $libobj ${libobj}T" + fi + + # On Cygwin there's no "real" PIC flag so we must build both object types + case $host_os in + cygwin* | mingw* | pw32* | os2* | cegcc*) + pic_mode=default + ;; + esac + if test "$pic_mode" = no && test "$deplibs_check_method" != pass_all; then + # non-PIC code in shared libraries is not supported + pic_mode=default + fi + + # Calculate the filename of the output object if compiler does + # not support -o with -c + if test "$compiler_c_o" = no; then + output_obj=`$ECHO "X$srcfile" | $Xsed -e 's%^.*/%%' -e 's%\.[^.]*$%%'`.${objext} + lockfile="$output_obj.lock" + else + output_obj= + need_locks=no + lockfile= + fi + + # Lock this critical section if it is needed + # We use this script file to make the link, it avoids creating a new file + if test "$need_locks" = yes; then + until $opt_dry_run || ln "$progpath" "$lockfile" 2>/dev/null; do + func_echo "Waiting for $lockfile to be removed" + sleep 2 + done + elif test "$need_locks" = warn; then + if test -f "$lockfile"; then + $ECHO "\ +*** ERROR, $lockfile exists and contains: +`cat $lockfile 2>/dev/null` + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $opt_dry_run || $RM $removelist + exit $EXIT_FAILURE + fi + removelist="$removelist $output_obj" + $ECHO "$srcfile" > "$lockfile" + fi + + $opt_dry_run || $RM $removelist + removelist="$removelist $lockfile" + trap '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' 1 2 15 + + if test -n "$fix_srcfile_path"; then + eval srcfile=\"$fix_srcfile_path\" + fi + func_quote_for_eval "$srcfile" + qsrcfile=$func_quote_for_eval_result + + # Only build a PIC object if we are building libtool libraries. + if test "$build_libtool_libs" = yes; then + # Without this assignment, base_compile gets emptied. + fbsd_hideous_sh_bug=$base_compile + + if test "$pic_mode" != no; then + command="$base_compile $qsrcfile $pic_flag" + else + # Don't build PIC code + command="$base_compile $qsrcfile" + fi + + func_mkdir_p "$xdir$objdir" + + if test -z "$output_obj"; then + # Place PIC objects in $objdir + command="$command -o $lobj" + fi + + func_show_eval_locale "$command" \ + 'test -n "$output_obj" && $RM $removelist; exit $EXIT_FAILURE' + + if test "$need_locks" = warn && + test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then + $ECHO "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $opt_dry_run || $RM $removelist + exit $EXIT_FAILURE + fi + + # Just move the object if needed, then go on to compile the next one + if test -n "$output_obj" && test "X$output_obj" != "X$lobj"; then + func_show_eval '$MV "$output_obj" "$lobj"' \ + 'error=$?; $opt_dry_run || $RM $removelist; exit $error' + fi + + # Allow error messages only from the first compilation. + if test "$suppress_opt" = yes; then + suppress_output=' >/dev/null 2>&1' + fi + fi + + # Only build a position-dependent object if we build old libraries. + if test "$build_old_libs" = yes; then + if test "$pic_mode" != yes; then + # Don't build PIC code + command="$base_compile $qsrcfile$pie_flag" + else + command="$base_compile $qsrcfile $pic_flag" + fi + if test "$compiler_c_o" = yes; then + command="$command -o $obj" + fi + + # Suppress compiler output if we already did a PIC compilation. + command="$command$suppress_output" + func_show_eval_locale "$command" \ + '$opt_dry_run || $RM $removelist; exit $EXIT_FAILURE' + + if test "$need_locks" = warn && + test "X`cat $lockfile 2>/dev/null`" != "X$srcfile"; then + $ECHO "\ +*** ERROR, $lockfile contains: +`cat $lockfile 2>/dev/null` + +but it should contain: +$srcfile + +This indicates that another process is trying to use the same +temporary object file, and libtool could not work around it because +your compiler does not support \`-c' and \`-o' together. If you +repeat this compilation, it may succeed, by chance, but you had better +avoid parallel builds (make -j) in this platform, or get a better +compiler." + + $opt_dry_run || $RM $removelist + exit $EXIT_FAILURE + fi + + # Just move the object if needed + if test -n "$output_obj" && test "X$output_obj" != "X$obj"; then + func_show_eval '$MV "$output_obj" "$obj"' \ + 'error=$?; $opt_dry_run || $RM $removelist; exit $error' + fi + fi + + $opt_dry_run || { + func_write_libtool_object "$libobj" "$objdir/$objname" "$objname" + + # Unlock the critical section if it was locked + if test "$need_locks" != no; then + removelist=$lockfile + $RM "$lockfile" + fi + } + + exit $EXIT_SUCCESS +} + +$opt_help || { +test "$mode" = compile && func_mode_compile ${1+"$@"} +} + +func_mode_help () +{ + # We need to display help for each of the modes. + case $mode in + "") + # Generic help is extracted from the usage comments + # at the start of this file. + func_help + ;; + + clean) + $ECHO \ +"Usage: $progname [OPTION]... --mode=clean RM [RM-OPTION]... FILE... + +Remove files from the build directory. + +RM is the name of the program to use to delete files associated with each FILE +(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed +to RM. + +If FILE is a libtool library, object or program, all the files associated +with it are deleted. Otherwise, only FILE itself is deleted using RM." + ;; + + compile) + $ECHO \ +"Usage: $progname [OPTION]... --mode=compile COMPILE-COMMAND... SOURCEFILE + +Compile a source file into a libtool library object. + +This mode accepts the following additional options: + + -o OUTPUT-FILE set the output file name to OUTPUT-FILE + -no-suppress do not suppress compiler output for multiple passes + -prefer-pic try to building PIC objects only + -prefer-non-pic try to building non-PIC objects only + -shared do not build a \`.o' file suitable for static linking + -static only build a \`.o' file suitable for static linking + +COMPILE-COMMAND is a command to be used in creating a \`standard' object file +from the given SOURCEFILE. + +The output file name is determined by removing the directory component from +SOURCEFILE, then substituting the C source code suffix \`.c' with the +library object suffix, \`.lo'." + ;; + + execute) + $ECHO \ +"Usage: $progname [OPTION]... --mode=execute COMMAND [ARGS]... + +Automatically set library path, then run a program. + +This mode accepts the following additional options: + + -dlopen FILE add the directory containing FILE to the library path + +This mode sets the library path environment variable according to \`-dlopen' +flags. + +If any of the ARGS are libtool executable wrappers, then they are translated +into their corresponding uninstalled binary, and any of their required library +directories are added to the library path. + +Then, COMMAND is executed, with ARGS as arguments." + ;; + + finish) + $ECHO \ +"Usage: $progname [OPTION]... --mode=finish [LIBDIR]... + +Complete the installation of libtool libraries. + +Each LIBDIR is a directory that contains libtool libraries. + +The commands that this mode executes may require superuser privileges. Use +the \`--dry-run' option if you just want to see what would be executed." + ;; + + install) + $ECHO \ +"Usage: $progname [OPTION]... --mode=install INSTALL-COMMAND... + +Install executables or libraries. + +INSTALL-COMMAND is the installation command. The first component should be +either the \`install' or \`cp' program. + +The following components of INSTALL-COMMAND are treated specially: + + -inst-prefix PREFIX-DIR Use PREFIX-DIR as a staging area for installation + +The rest of the components are interpreted as arguments to that command (only +BSD-compatible install options are recognized)." + ;; + + link) + $ECHO \ +"Usage: $progname [OPTION]... --mode=link LINK-COMMAND... + +Link object files or libraries together to form another library, or to +create an executable program. + +LINK-COMMAND is a command using the C compiler that you would use to create +a program from several object files. + +The following components of LINK-COMMAND are treated specially: + + -all-static do not do any dynamic linking at all + -avoid-version do not add a version suffix if possible + -dlopen FILE \`-dlpreopen' FILE if it cannot be dlopened at runtime + -dlpreopen FILE link in FILE and add its symbols to lt_preloaded_symbols + -export-dynamic allow symbols from OUTPUT-FILE to be resolved with dlsym(3) + -export-symbols SYMFILE + try to export only the symbols listed in SYMFILE + -export-symbols-regex REGEX + try to export only the symbols matching REGEX + -LLIBDIR search LIBDIR for required installed libraries + -lNAME OUTPUT-FILE requires the installed library libNAME + -module build a library that can dlopened + -no-fast-install disable the fast-install mode + -no-install link a not-installable executable + -no-undefined declare that a library does not refer to external symbols + -o OUTPUT-FILE create OUTPUT-FILE from the specified objects + -objectlist FILE Use a list of object files found in FILE to specify objects + -precious-files-regex REGEX + don't remove output files matching REGEX + -release RELEASE specify package release information + -rpath LIBDIR the created library will eventually be installed in LIBDIR + -R[ ]LIBDIR add LIBDIR to the runtime path of programs and libraries + -shared only do dynamic linking of libtool libraries + -shrext SUFFIX override the standard shared library file extension + -static do not do any dynamic linking of uninstalled libtool libraries + -static-libtool-libs + do not do any dynamic linking of libtool libraries + -version-info CURRENT[:REVISION[:AGE]] + specify library version info [each variable defaults to 0] + -weak LIBNAME declare that the target provides the LIBNAME interface + +All other options (arguments beginning with \`-') are ignored. + +Every other argument is treated as a filename. Files ending in \`.la' are +treated as uninstalled libtool libraries, other files are standard or library +object files. + +If the OUTPUT-FILE ends in \`.la', then a libtool library is created, +only library objects (\`.lo' files) may be specified, and \`-rpath' is +required, except when creating a convenience library. + +If OUTPUT-FILE ends in \`.a' or \`.lib', then a standard library is created +using \`ar' and \`ranlib', or on Windows using \`lib'. + +If OUTPUT-FILE ends in \`.lo' or \`.${objext}', then a reloadable object file +is created, otherwise an executable program is created." + ;; + + uninstall) + $ECHO \ +"Usage: $progname [OPTION]... --mode=uninstall RM [RM-OPTION]... FILE... + +Remove libraries from an installation directory. + +RM is the name of the program to use to delete files associated with each FILE +(typically \`/bin/rm'). RM-OPTIONS are options (such as \`-f') to be passed +to RM. + +If FILE is a libtool library, all the files associated with it are deleted. +Otherwise, only FILE itself is deleted using RM." + ;; + + *) + func_fatal_help "invalid operation mode \`$mode'" + ;; + esac + + $ECHO + $ECHO "Try \`$progname --help' for more information about other modes." + + exit $? +} + + # Now that we've collected a possible --mode arg, show help if necessary + $opt_help && func_mode_help + + +# func_mode_execute arg... +func_mode_execute () +{ + $opt_debug + # The first argument is the command name. + cmd="$nonopt" + test -z "$cmd" && \ + func_fatal_help "you must specify a COMMAND" + + # Handle -dlopen flags immediately. + for file in $execute_dlfiles; do + test -f "$file" \ + || func_fatal_help "\`$file' is not a file" + + dir= + case $file in + *.la) + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$file" \ + || func_fatal_help "\`$lib' is not a valid libtool archive" + + # Read the libtool library. + dlname= + library_names= + func_source "$file" + + # Skip this library if it cannot be dlopened. + if test -z "$dlname"; then + # Warn if it was a shared library. + test -n "$library_names" && \ + func_warning "\`$file' was not linked with \`-export-dynamic'" + continue + fi + + func_dirname "$file" "" "." + dir="$func_dirname_result" + + if test -f "$dir/$objdir/$dlname"; then + dir="$dir/$objdir" + else + if test ! -f "$dir/$dlname"; then + func_fatal_error "cannot find \`$dlname' in \`$dir' or \`$dir/$objdir'" + fi + fi + ;; + + *.lo) + # Just add the directory containing the .lo file. + func_dirname "$file" "" "." + dir="$func_dirname_result" + ;; + + *) + func_warning "\`-dlopen' is ignored for non-libtool libraries and objects" + continue + ;; + esac + + # Get the absolute pathname. + absdir=`cd "$dir" && pwd` + test -n "$absdir" && dir="$absdir" + + # Now add the directory to shlibpath_var. + if eval "test -z \"\$$shlibpath_var\""; then + eval "$shlibpath_var=\"\$dir\"" + else + eval "$shlibpath_var=\"\$dir:\$$shlibpath_var\"" + fi + done + + # This variable tells wrapper scripts just to set shlibpath_var + # rather than running their programs. + libtool_execute_magic="$magic" + + # Check if any of the arguments is a wrapper script. + args= + for file + do + case $file in + -*) ;; + *) + # Do a test to see if this is really a libtool program. + if func_ltwrapper_script_p "$file"; then + func_source "$file" + # Transform arg to wrapped name. + file="$progdir/$program" + elif func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + func_source "$func_ltwrapper_scriptname_result" + # Transform arg to wrapped name. + file="$progdir/$program" + fi + ;; + esac + # Quote arguments (to preserve shell metacharacters). + func_quote_for_eval "$file" + args="$args $func_quote_for_eval_result" + done + + if test "X$opt_dry_run" = Xfalse; then + if test -n "$shlibpath_var"; then + # Export the shlibpath_var. + eval "export $shlibpath_var" + fi + + # Restore saved environment variables + for lt_var in LANG LANGUAGE LC_ALL LC_CTYPE LC_COLLATE LC_MESSAGES + do + eval "if test \"\${save_$lt_var+set}\" = set; then + $lt_var=\$save_$lt_var; export $lt_var + else + $lt_unset $lt_var + fi" + done + + # Now prepare to actually exec the command. + exec_cmd="\$cmd$args" + else + # Display what would be done. + if test -n "$shlibpath_var"; then + eval "\$ECHO \"\$shlibpath_var=\$$shlibpath_var\"" + $ECHO "export $shlibpath_var" + fi + $ECHO "$cmd$args" + exit $EXIT_SUCCESS + fi +} + +test "$mode" = execute && func_mode_execute ${1+"$@"} + + +# func_mode_finish arg... +func_mode_finish () +{ + $opt_debug + libdirs="$nonopt" + admincmds= + + if test -n "$finish_cmds$finish_eval" && test -n "$libdirs"; then + for dir + do + libdirs="$libdirs $dir" + done + + for libdir in $libdirs; do + if test -n "$finish_cmds"; then + # Do each command in the finish commands. + func_execute_cmds "$finish_cmds" 'admincmds="$admincmds +'"$cmd"'"' + fi + if test -n "$finish_eval"; then + # Do the single finish_eval. + eval cmds=\"$finish_eval\" + $opt_dry_run || eval "$cmds" || admincmds="$admincmds + $cmds" + fi + done + fi + + # Exit here if they wanted silent mode. + $opt_silent && exit $EXIT_SUCCESS + + $ECHO "X----------------------------------------------------------------------" | $Xsed + $ECHO "Libraries have been installed in:" + for libdir in $libdirs; do + $ECHO " $libdir" + done + $ECHO + $ECHO "If you ever happen to want to link against installed libraries" + $ECHO "in a given directory, LIBDIR, you must either use libtool, and" + $ECHO "specify the full pathname of the library, or use the \`-LLIBDIR'" + $ECHO "flag during linking and do at least one of the following:" + if test -n "$shlibpath_var"; then + $ECHO " - add LIBDIR to the \`$shlibpath_var' environment variable" + $ECHO " during execution" + fi + if test -n "$runpath_var"; then + $ECHO " - add LIBDIR to the \`$runpath_var' environment variable" + $ECHO " during linking" + fi + if test -n "$hardcode_libdir_flag_spec"; then + libdir=LIBDIR + eval flag=\"$hardcode_libdir_flag_spec\" + + $ECHO " - use the \`$flag' linker flag" + fi + if test -n "$admincmds"; then + $ECHO " - have your system administrator run these commands:$admincmds" + fi + if test -f /etc/ld.so.conf; then + $ECHO " - have your system administrator add LIBDIR to \`/etc/ld.so.conf'" + fi + $ECHO + + $ECHO "See any operating system documentation about shared libraries for" + case $host in + solaris2.[6789]|solaris2.1[0-9]) + $ECHO "more information, such as the ld(1), crle(1) and ld.so(8) manual" + $ECHO "pages." + ;; + *) + $ECHO "more information, such as the ld(1) and ld.so(8) manual pages." + ;; + esac + $ECHO "X----------------------------------------------------------------------" | $Xsed + exit $EXIT_SUCCESS +} + +test "$mode" = finish && func_mode_finish ${1+"$@"} + + +# func_mode_install arg... +func_mode_install () +{ + $opt_debug + # There may be an optional sh(1) argument at the beginning of + # install_prog (especially on Windows NT). + if test "$nonopt" = "$SHELL" || test "$nonopt" = /bin/sh || + # Allow the use of GNU shtool's install command. + $ECHO "X$nonopt" | $GREP shtool >/dev/null; then + # Aesthetically quote it. + func_quote_for_eval "$nonopt" + install_prog="$func_quote_for_eval_result " + arg=$1 + shift + else + install_prog= + arg=$nonopt + fi + + # The real first argument should be the name of the installation program. + # Aesthetically quote it. + func_quote_for_eval "$arg" + install_prog="$install_prog$func_quote_for_eval_result" + + # We need to accept at least all the BSD install flags. + dest= + files= + opts= + prev= + install_type= + isdir=no + stripme= + for arg + do + if test -n "$dest"; then + files="$files $dest" + dest=$arg + continue + fi + + case $arg in + -d) isdir=yes ;; + -f) + case " $install_prog " in + *[\\\ /]cp\ *) ;; + *) prev=$arg ;; + esac + ;; + -g | -m | -o) + prev=$arg + ;; + -s) + stripme=" -s" + continue + ;; + -*) + ;; + *) + # If the previous option needed an argument, then skip it. + if test -n "$prev"; then + prev= + else + dest=$arg + continue + fi + ;; + esac + + # Aesthetically quote the argument. + func_quote_for_eval "$arg" + install_prog="$install_prog $func_quote_for_eval_result" + done + + test -z "$install_prog" && \ + func_fatal_help "you must specify an install program" + + test -n "$prev" && \ + func_fatal_help "the \`$prev' option requires an argument" + + if test -z "$files"; then + if test -z "$dest"; then + func_fatal_help "no file or destination specified" + else + func_fatal_help "you must specify a destination" + fi + fi + + # Strip any trailing slash from the destination. + func_stripname '' '/' "$dest" + dest=$func_stripname_result + + # Check to see that the destination is a directory. + test -d "$dest" && isdir=yes + if test "$isdir" = yes; then + destdir="$dest" + destname= + else + func_dirname_and_basename "$dest" "" "." + destdir="$func_dirname_result" + destname="$func_basename_result" + + # Not a directory, so check to see that there is only one file specified. + set dummy $files; shift + test "$#" -gt 1 && \ + func_fatal_help "\`$dest' is not a directory" + fi + case $destdir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + for file in $files; do + case $file in + *.lo) ;; + *) + func_fatal_help "\`$destdir' must be an absolute directory name" + ;; + esac + done + ;; + esac + + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic="$magic" + + staticlibs= + future_libdirs= + current_libdirs= + for file in $files; do + + # Do each installation. + case $file in + *.$libext) + # Do the static libraries later. + staticlibs="$staticlibs $file" + ;; + + *.la) + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$file" \ + || func_fatal_help "\`$file' is not a valid libtool archive" + + library_names= + old_library= + relink_command= + func_source "$file" + + # Add the libdir to current_libdirs if it is the destination. + if test "X$destdir" = "X$libdir"; then + case "$current_libdirs " in + *" $libdir "*) ;; + *) current_libdirs="$current_libdirs $libdir" ;; + esac + else + # Note the libdir as a future libdir. + case "$future_libdirs " in + *" $libdir "*) ;; + *) future_libdirs="$future_libdirs $libdir" ;; + esac + fi + + func_dirname "$file" "/" "" + dir="$func_dirname_result" + dir="$dir$objdir" + + if test -n "$relink_command"; then + # Determine the prefix the user has applied to our future dir. + inst_prefix_dir=`$ECHO "X$destdir" | $Xsed -e "s%$libdir\$%%"` + + # Don't allow the user to place us outside of our expected + # location b/c this prevents finding dependent libraries that + # are installed to the same prefix. + # At present, this check doesn't affect windows .dll's that + # are installed into $libdir/../bin (currently, that works fine) + # but it's something to keep an eye on. + test "$inst_prefix_dir" = "$destdir" && \ + func_fatal_error "error: cannot install \`$file' to a directory not ending in $libdir" + + if test -n "$inst_prefix_dir"; then + # Stick the inst_prefix_dir data into the link command. + relink_command=`$ECHO "X$relink_command" | $Xsed -e "s%@inst_prefix_dir@%-inst-prefix-dir $inst_prefix_dir%"` + else + relink_command=`$ECHO "X$relink_command" | $Xsed -e "s%@inst_prefix_dir@%%"` + fi + + func_warning "relinking \`$file'" + func_show_eval "$relink_command" \ + 'func_fatal_error "error: relink \`$file'\'' with the above command before installing it"' + fi + + # See the names of the shared library. + set dummy $library_names; shift + if test -n "$1"; then + realname="$1" + shift + + srcname="$realname" + test -n "$relink_command" && srcname="$realname"T + + # Install the shared library and build the symlinks. + func_show_eval "$install_prog $dir/$srcname $destdir/$realname" \ + 'exit $?' + tstripme="$stripme" + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + case $realname in + *.dll.a) + tstripme="" + ;; + esac + ;; + esac + if test -n "$tstripme" && test -n "$striplib"; then + func_show_eval "$striplib $destdir/$realname" 'exit $?' + fi + + if test "$#" -gt 0; then + # Delete the old symlinks, and create new ones. + # Try `ln -sf' first, because the `ln' binary might depend on + # the symlink we replace! Solaris /bin/ln does not understand -f, + # so we also need to try rm && ln -s. + for linkname + do + test "$linkname" != "$realname" \ + && func_show_eval "(cd $destdir && { $LN_S -f $realname $linkname || { $RM $linkname && $LN_S $realname $linkname; }; })" + done + fi + + # Do each command in the postinstall commands. + lib="$destdir/$realname" + func_execute_cmds "$postinstall_cmds" 'exit $?' + fi + + # Install the pseudo-library for information purposes. + func_basename "$file" + name="$func_basename_result" + instname="$dir/$name"i + func_show_eval "$install_prog $instname $destdir/$name" 'exit $?' + + # Maybe install the static library, too. + test -n "$old_library" && staticlibs="$staticlibs $dir/$old_library" + ;; + + *.lo) + # Install (i.e. copy) a libtool object. + + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile="$destdir/$destname" + else + func_basename "$file" + destfile="$func_basename_result" + destfile="$destdir/$destfile" + fi + + # Deduce the name of the destination old-style object file. + case $destfile in + *.lo) + func_lo2o "$destfile" + staticdest=$func_lo2o_result + ;; + *.$objext) + staticdest="$destfile" + destfile= + ;; + *) + func_fatal_help "cannot copy a libtool object to \`$destfile'" + ;; + esac + + # Install the libtool object if requested. + test -n "$destfile" && \ + func_show_eval "$install_prog $file $destfile" 'exit $?' + + # Install the old object if enabled. + if test "$build_old_libs" = yes; then + # Deduce the name of the old-style object file. + func_lo2o "$file" + staticobj=$func_lo2o_result + func_show_eval "$install_prog \$staticobj \$staticdest" 'exit $?' + fi + exit $EXIT_SUCCESS + ;; + + *) + # Figure out destination file name, if it wasn't already specified. + if test -n "$destname"; then + destfile="$destdir/$destname" + else + func_basename "$file" + destfile="$func_basename_result" + destfile="$destdir/$destfile" + fi + + # If the file is missing, and there is a .exe on the end, strip it + # because it is most likely a libtool script we actually want to + # install + stripped_ext="" + case $file in + *.exe) + if test ! -f "$file"; then + func_stripname '' '.exe' "$file" + file=$func_stripname_result + stripped_ext=".exe" + fi + ;; + esac + + # Do a test to see if this is really a libtool program. + case $host in + *cygwin* | *mingw*) + if func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + wrapper=$func_ltwrapper_scriptname_result + else + func_stripname '' '.exe' "$file" + wrapper=$func_stripname_result + fi + ;; + *) + wrapper=$file + ;; + esac + if func_ltwrapper_script_p "$wrapper"; then + notinst_deplibs= + relink_command= + + func_source "$wrapper" + + # Check the variables that should have been set. + test -z "$generated_by_libtool_version" && \ + func_fatal_error "invalid libtool wrapper script \`$wrapper'" + + finalize=yes + for lib in $notinst_deplibs; do + # Check to see that each library is installed. + libdir= + if test -f "$lib"; then + func_source "$lib" + fi + libfile="$libdir/"`$ECHO "X$lib" | $Xsed -e 's%^.*/%%g'` ### testsuite: skip nested quoting test + if test -n "$libdir" && test ! -f "$libfile"; then + func_warning "\`$lib' has not been installed in \`$libdir'" + finalize=no + fi + done + + relink_command= + func_source "$wrapper" + + outputname= + if test "$fast_install" = no && test -n "$relink_command"; then + $opt_dry_run || { + if test "$finalize" = yes; then + tmpdir=`func_mktempdir` + func_basename "$file$stripped_ext" + file="$func_basename_result" + outputname="$tmpdir/$file" + # Replace the output file specification. + relink_command=`$ECHO "X$relink_command" | $Xsed -e 's%@OUTPUT@%'"$outputname"'%g'` + + $opt_silent || { + func_quote_for_expand "$relink_command" + eval "func_echo $func_quote_for_expand_result" + } + if eval "$relink_command"; then : + else + func_error "error: relink \`$file' with the above command before installing it" + $opt_dry_run || ${RM}r "$tmpdir" + continue + fi + file="$outputname" + else + func_warning "cannot relink \`$file'" + fi + } + else + # Install the binary that we compiled earlier. + file=`$ECHO "X$file$stripped_ext" | $Xsed -e "s%\([^/]*\)$%$objdir/\1%"` + fi + fi + + # remove .exe since cygwin /usr/bin/install will append another + # one anyway + case $install_prog,$host in + */usr/bin/install*,*cygwin*) + case $file:$destfile in + *.exe:*.exe) + # this is ok + ;; + *.exe:*) + destfile=$destfile.exe + ;; + *:*.exe) + func_stripname '' '.exe' "$destfile" + destfile=$func_stripname_result + ;; + esac + ;; + esac + func_show_eval "$install_prog\$stripme \$file \$destfile" 'exit $?' + $opt_dry_run || if test -n "$outputname"; then + ${RM}r "$tmpdir" + fi + ;; + esac + done + + for file in $staticlibs; do + func_basename "$file" + name="$func_basename_result" + + # Set up the ranlib parameters. + oldlib="$destdir/$name" + + func_show_eval "$install_prog \$file \$oldlib" 'exit $?' + + if test -n "$stripme" && test -n "$old_striplib"; then + func_show_eval "$old_striplib $oldlib" 'exit $?' + fi + + # Do each command in the postinstall commands. + func_execute_cmds "$old_postinstall_cmds" 'exit $?' + done + + test -n "$future_libdirs" && \ + func_warning "remember to run \`$progname --finish$future_libdirs'" + + if test -n "$current_libdirs"; then + # Maybe just do a dry run. + $opt_dry_run && current_libdirs=" -n$current_libdirs" + exec_cmd='$SHELL $progpath $preserve_args --finish$current_libdirs' + else + exit $EXIT_SUCCESS + fi +} + +test "$mode" = install && func_mode_install ${1+"$@"} + + +# func_generate_dlsyms outputname originator pic_p +# Extract symbols from dlprefiles and create ${outputname}S.o with +# a dlpreopen symbol table. +func_generate_dlsyms () +{ + $opt_debug + my_outputname="$1" + my_originator="$2" + my_pic_p="${3-no}" + my_prefix=`$ECHO "$my_originator" | sed 's%[^a-zA-Z0-9]%_%g'` + my_dlsyms= + + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + if test -n "$NM" && test -n "$global_symbol_pipe"; then + my_dlsyms="${my_outputname}S.c" + else + func_error "not configured to extract global symbols from dlpreopened files" + fi + fi + + if test -n "$my_dlsyms"; then + case $my_dlsyms in + "") ;; + *.c) + # Discover the nlist of each of the dlfiles. + nlist="$output_objdir/${my_outputname}.nm" + + func_show_eval "$RM $nlist ${nlist}S ${nlist}T" + + # Parse the name list into a source file. + func_verbose "creating $output_objdir/$my_dlsyms" + + $opt_dry_run || $ECHO > "$output_objdir/$my_dlsyms" "\ +/* $my_dlsyms - symbol resolution table for \`$my_outputname' dlsym emulation. */ +/* Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION */ + +#ifdef __cplusplus +extern \"C\" { +#endif + +/* External symbol declarations for the compiler. */\ +" + + if test "$dlself" = yes; then + func_verbose "generating symbol list for \`$output'" + + $opt_dry_run || echo ': @PROGRAM@ ' > "$nlist" + + # Add our own program objects to the symbol list. + progfiles=`$ECHO "X$objs$old_deplibs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + for progfile in $progfiles; do + func_verbose "extracting global C symbols from \`$progfile'" + $opt_dry_run || eval "$NM $progfile | $global_symbol_pipe >> '$nlist'" + done + + if test -n "$exclude_expsyms"; then + $opt_dry_run || { + eval '$EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + } + fi + + if test -n "$export_symbols_regex"; then + $opt_dry_run || { + eval '$EGREP -e "$export_symbols_regex" "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + } + fi + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + export_symbols="$output_objdir/$outputname.exp" + $opt_dry_run || { + $RM $export_symbols + eval "${SED} -n -e '/^: @PROGRAM@ $/d' -e 's/^.* \(.*\)$/\1/p' "'< "$nlist" > "$export_symbols"' + case $host in + *cygwin* | *mingw* | *cegcc* ) + eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' + eval 'cat "$export_symbols" >> "$output_objdir/$outputname.def"' + ;; + esac + } + else + $opt_dry_run || { + eval "${SED} -e 's/\([].[*^$]\)/\\\\\1/g' -e 's/^/ /' -e 's/$/$/'"' < "$export_symbols" > "$output_objdir/$outputname.exp"' + eval '$GREP -f "$output_objdir/$outputname.exp" < "$nlist" > "$nlist"T' + eval '$MV "$nlist"T "$nlist"' + case $host in + *cygwin | *mingw* | *cegcc* ) + eval "echo EXPORTS "'> "$output_objdir/$outputname.def"' + eval 'cat "$nlist" >> "$output_objdir/$outputname.def"' + ;; + esac + } + fi + fi + + for dlprefile in $dlprefiles; do + func_verbose "extracting global C symbols from \`$dlprefile'" + func_basename "$dlprefile" + name="$func_basename_result" + $opt_dry_run || { + eval '$ECHO ": $name " >> "$nlist"' + eval "$NM $dlprefile 2>/dev/null | $global_symbol_pipe >> '$nlist'" + } + done + + $opt_dry_run || { + # Make sure we have at least an empty file. + test -f "$nlist" || : > "$nlist" + + if test -n "$exclude_expsyms"; then + $EGREP -v " ($exclude_expsyms)$" "$nlist" > "$nlist"T + $MV "$nlist"T "$nlist" + fi + + # Try sorting and uniquifying the output. + if $GREP -v "^: " < "$nlist" | + if sort -k 3 /dev/null 2>&1; then + sort -k 3 + else + sort +2 + fi | + uniq > "$nlist"S; then + : + else + $GREP -v "^: " < "$nlist" > "$nlist"S + fi + + if test -f "$nlist"S; then + eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"' + else + $ECHO '/* NONE */' >> "$output_objdir/$my_dlsyms" + fi + + $ECHO >> "$output_objdir/$my_dlsyms" "\ + +/* The mapping between symbol names and symbols. */ +typedef struct { + const char *name; + void *address; +} lt_dlsymlist; +" + case $host in + *cygwin* | *mingw* | *cegcc* ) + $ECHO >> "$output_objdir/$my_dlsyms" "\ +/* DATA imports from DLLs on WIN32 con't be const, because + runtime relocations are performed -- see ld's documentation + on pseudo-relocs. */" + lt_dlsym_const= ;; + *osf5*) + echo >> "$output_objdir/$my_dlsyms" "\ +/* This system does not cope well with relocations in const data */" + lt_dlsym_const= ;; + *) + lt_dlsym_const=const ;; + esac + + $ECHO >> "$output_objdir/$my_dlsyms" "\ +extern $lt_dlsym_const lt_dlsymlist +lt_${my_prefix}_LTX_preloaded_symbols[]; +$lt_dlsym_const lt_dlsymlist +lt_${my_prefix}_LTX_preloaded_symbols[] = +{\ + { \"$my_originator\", (void *) 0 }," + + case $need_lib_prefix in + no) + eval "$global_symbol_to_c_name_address" < "$nlist" >> "$output_objdir/$my_dlsyms" + ;; + *) + eval "$global_symbol_to_c_name_address_lib_prefix" < "$nlist" >> "$output_objdir/$my_dlsyms" + ;; + esac + $ECHO >> "$output_objdir/$my_dlsyms" "\ + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt_${my_prefix}_LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif\ +" + } # !$opt_dry_run + + pic_flag_for_symtable= + case "$compile_command " in + *" -static "*) ;; + *) + case $host in + # compiling the symbol table file with pic_flag works around + # a FreeBSD bug that causes programs to crash when -lm is + # linked before any other PIC object. But we must not use + # pic_flag when linking with -static. The problem exists in + # FreeBSD 2.2.6 and is fixed in FreeBSD 3.1. + *-*-freebsd2*|*-*-freebsd3.0*|*-*-freebsdelf3.0*) + pic_flag_for_symtable=" $pic_flag -DFREEBSD_WORKAROUND" ;; + *-*-hpux*) + pic_flag_for_symtable=" $pic_flag" ;; + *) + if test "X$my_pic_p" != Xno; then + pic_flag_for_symtable=" $pic_flag" + fi + ;; + esac + ;; + esac + symtab_cflags= + for arg in $LTCFLAGS; do + case $arg in + -pie | -fpie | -fPIE) ;; + *) symtab_cflags="$symtab_cflags $arg" ;; + esac + done + + # Now compile the dynamic symbol file. + func_show_eval '(cd $output_objdir && $LTCC$symtab_cflags -c$no_builtin_flag$pic_flag_for_symtable "$my_dlsyms")' 'exit $?' + + # Clean up the generated files. + func_show_eval '$RM "$output_objdir/$my_dlsyms" "$nlist" "${nlist}S" "${nlist}T"' + + # Transform the symbol file into the correct name. + symfileobj="$output_objdir/${my_outputname}S.$objext" + case $host in + *cygwin* | *mingw* | *cegcc* ) + if test -f "$output_objdir/$my_outputname.def"; then + compile_command=`$ECHO "X$compile_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` + finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$output_objdir/$my_outputname.def $symfileobj%"` + else + compile_command=`$ECHO "X$compile_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"` + finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"` + fi + ;; + *) + compile_command=`$ECHO "X$compile_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"` + finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s%@SYMFILE@%$symfileobj%"` + ;; + esac + ;; + *) + func_fatal_error "unknown suffix for \`$my_dlsyms'" + ;; + esac + else + # We keep going just in case the user didn't refer to + # lt_preloaded_symbols. The linker will fail if global_symbol_pipe + # really was required. + + # Nullify the symbol file. + compile_command=`$ECHO "X$compile_command" | $Xsed -e "s% @SYMFILE@%%"` + finalize_command=`$ECHO "X$finalize_command" | $Xsed -e "s% @SYMFILE@%%"` + fi +} + +# func_win32_libid arg +# return the library type of file 'arg' +# +# Need a lot of goo to handle *both* DLLs and import libs +# Has to be a shell function in order to 'eat' the argument +# that is supplied when $file_magic_command is called. +func_win32_libid () +{ + $opt_debug + win32_libid_type="unknown" + win32_fileres=`file -L $1 2>/dev/null` + case $win32_fileres in + *ar\ archive\ import\ library*) # definitely import + win32_libid_type="x86 archive import" + ;; + *ar\ archive*) # could be an import, or static + if eval $OBJDUMP -f $1 | $SED -e '10q' 2>/dev/null | + $EGREP 'file format pe-i386(.*architecture: i386)?' >/dev/null ; then + win32_nmres=`eval $NM -f posix -A $1 | + $SED -n -e ' + 1,100{ + / I /{ + s,.*,import, + p + q + } + }'` + case $win32_nmres in + import*) win32_libid_type="x86 archive import";; + *) win32_libid_type="x86 archive static";; + esac + fi + ;; + *DLL*) + win32_libid_type="x86 DLL" + ;; + *executable*) # but shell scripts are "executable" too... + case $win32_fileres in + *MS\ Windows\ PE\ Intel*) + win32_libid_type="x86 DLL" + ;; + esac + ;; + esac + $ECHO "$win32_libid_type" +} + + + +# func_extract_an_archive dir oldlib +func_extract_an_archive () +{ + $opt_debug + f_ex_an_ar_dir="$1"; shift + f_ex_an_ar_oldlib="$1" + func_show_eval "(cd \$f_ex_an_ar_dir && $AR x \"\$f_ex_an_ar_oldlib\")" 'exit $?' + if ($AR t "$f_ex_an_ar_oldlib" | sort | sort -uc >/dev/null 2>&1); then + : + else + func_fatal_error "object name conflicts in archive: $f_ex_an_ar_dir/$f_ex_an_ar_oldlib" + fi +} + + +# func_extract_archives gentop oldlib ... +func_extract_archives () +{ + $opt_debug + my_gentop="$1"; shift + my_oldlibs=${1+"$@"} + my_oldobjs="" + my_xlib="" + my_xabs="" + my_xdir="" + + for my_xlib in $my_oldlibs; do + # Extract the objects. + case $my_xlib in + [\\/]* | [A-Za-z]:[\\/]*) my_xabs="$my_xlib" ;; + *) my_xabs=`pwd`"/$my_xlib" ;; + esac + func_basename "$my_xlib" + my_xlib="$func_basename_result" + my_xlib_u=$my_xlib + while :; do + case " $extracted_archives " in + *" $my_xlib_u "*) + func_arith $extracted_serial + 1 + extracted_serial=$func_arith_result + my_xlib_u=lt$extracted_serial-$my_xlib ;; + *) break ;; + esac + done + extracted_archives="$extracted_archives $my_xlib_u" + my_xdir="$my_gentop/$my_xlib_u" + + func_mkdir_p "$my_xdir" + + case $host in + *-darwin*) + func_verbose "Extracting $my_xabs" + # Do not bother doing anything if just a dry run + $opt_dry_run || { + darwin_orig_dir=`pwd` + cd $my_xdir || exit $? + darwin_archive=$my_xabs + darwin_curdir=`pwd` + darwin_base_archive=`basename "$darwin_archive"` + darwin_arches=`$LIPO -info "$darwin_archive" 2>/dev/null | $GREP Architectures 2>/dev/null || true` + if test -n "$darwin_arches"; then + darwin_arches=`$ECHO "$darwin_arches" | $SED -e 's/.*are://'` + darwin_arch= + func_verbose "$darwin_base_archive has multiple architectures $darwin_arches" + for darwin_arch in $darwin_arches ; do + func_mkdir_p "unfat-$$/${darwin_base_archive}-${darwin_arch}" + $LIPO -thin $darwin_arch -output "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" "${darwin_archive}" + cd "unfat-$$/${darwin_base_archive}-${darwin_arch}" + func_extract_an_archive "`pwd`" "${darwin_base_archive}" + cd "$darwin_curdir" + $RM "unfat-$$/${darwin_base_archive}-${darwin_arch}/${darwin_base_archive}" + done # $darwin_arches + ## Okay now we've a bunch of thin objects, gotta fatten them up :) + darwin_filelist=`find unfat-$$ -type f -name \*.o -print -o -name \*.lo -print | $SED -e "$basename" | sort -u` + darwin_file= + darwin_files= + for darwin_file in $darwin_filelist; do + darwin_files=`find unfat-$$ -name $darwin_file -print | $NL2SP` + $LIPO -create -output "$darwin_file" $darwin_files + done # $darwin_filelist + $RM -rf unfat-$$ + cd "$darwin_orig_dir" + else + cd $darwin_orig_dir + func_extract_an_archive "$my_xdir" "$my_xabs" + fi # $darwin_arches + } # !$opt_dry_run + ;; + *) + func_extract_an_archive "$my_xdir" "$my_xabs" + ;; + esac + my_oldobjs="$my_oldobjs "`find $my_xdir -name \*.$objext -print -o -name \*.lo -print | $NL2SP` + done + + func_extract_archives_result="$my_oldobjs" +} + + + +# func_emit_wrapper_part1 [arg=no] +# +# Emit the first part of a libtool wrapper script on stdout. +# For more information, see the description associated with +# func_emit_wrapper(), below. +func_emit_wrapper_part1 () +{ + func_emit_wrapper_part1_arg1=no + if test -n "$1" ; then + func_emit_wrapper_part1_arg1=$1 + fi + + $ECHO "\ +#! $SHELL + +# $output - temporary wrapper script for $objdir/$outputname +# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION +# +# The $output program cannot be directly executed until all the libtool +# libraries that it depends on are installed. +# +# This wrapper script should never be moved out of the build directory. +# If it is, it will not operate correctly. + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +Xsed='${SED} -e 1s/^X//' +sed_quote_subst='$sed_quote_subst' + +# Be Bourne compatible +if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then + emulate sh + NULLCMD=: + # Zsh 3.x and 4.x performs word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in *posix*) set -o posix;; esac +fi +BIN_SH=xpg4; export BIN_SH # for Tru64 +DUALCASE=1; export DUALCASE # for MKS sh + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +relink_command=\"$relink_command\" + +# This environment variable determines our operation mode. +if test \"\$libtool_install_magic\" = \"$magic\"; then + # install mode needs the following variables: + generated_by_libtool_version='$macro_version' + notinst_deplibs='$notinst_deplibs' +else + # When we are sourced in execute mode, \$file and \$ECHO are already set. + if test \"\$libtool_execute_magic\" != \"$magic\"; then + ECHO=\"$qecho\" + file=\"\$0\" + # Make sure echo works. + if test \"X\$1\" = X--no-reexec; then + # Discard the --no-reexec flag, and continue. + shift + elif test \"X\`{ \$ECHO '\t'; } 2>/dev/null\`\" = 'X\t'; then + # Yippee, \$ECHO works! + : + else + # Restart under the correct shell, and then maybe \$ECHO will work. + exec $SHELL \"\$0\" --no-reexec \${1+\"\$@\"} + fi + fi\ +" + $ECHO "\ + + # Find the directory that this script lives in. + thisdir=\`\$ECHO \"X\$file\" | \$Xsed -e 's%/[^/]*$%%'\` + test \"x\$thisdir\" = \"x\$file\" && thisdir=. + + # Follow symbolic links until we get to the real thisdir. + file=\`ls -ld \"\$file\" | ${SED} -n 's/.*-> //p'\` + while test -n \"\$file\"; do + destdir=\`\$ECHO \"X\$file\" | \$Xsed -e 's%/[^/]*\$%%'\` + + # If there was a directory component, then change thisdir. + if test \"x\$destdir\" != \"x\$file\"; then + case \"\$destdir\" in + [\\\\/]* | [A-Za-z]:[\\\\/]*) thisdir=\"\$destdir\" ;; + *) thisdir=\"\$thisdir/\$destdir\" ;; + esac + fi + + file=\`\$ECHO \"X\$file\" | \$Xsed -e 's%^.*/%%'\` + file=\`ls -ld \"\$thisdir/\$file\" | ${SED} -n 's/.*-> //p'\` + done +" +} +# end: func_emit_wrapper_part1 + +# func_emit_wrapper_part2 [arg=no] +# +# Emit the second part of a libtool wrapper script on stdout. +# For more information, see the description associated with +# func_emit_wrapper(), below. +func_emit_wrapper_part2 () +{ + func_emit_wrapper_part2_arg1=no + if test -n "$1" ; then + func_emit_wrapper_part2_arg1=$1 + fi + + $ECHO "\ + + # Usually 'no', except on cygwin/mingw when embedded into + # the cwrapper. + WRAPPER_SCRIPT_BELONGS_IN_OBJDIR=$func_emit_wrapper_part2_arg1 + if test \"\$WRAPPER_SCRIPT_BELONGS_IN_OBJDIR\" = \"yes\"; then + # special case for '.' + if test \"\$thisdir\" = \".\"; then + thisdir=\`pwd\` + fi + # remove .libs from thisdir + case \"\$thisdir\" in + *[\\\\/]$objdir ) thisdir=\`\$ECHO \"X\$thisdir\" | \$Xsed -e 's%[\\\\/][^\\\\/]*$%%'\` ;; + $objdir ) thisdir=. ;; + esac + fi + + # Try to get the absolute directory name. + absdir=\`cd \"\$thisdir\" && pwd\` + test -n \"\$absdir\" && thisdir=\"\$absdir\" +" + + if test "$fast_install" = yes; then + $ECHO "\ + program=lt-'$outputname'$exeext + progdir=\"\$thisdir/$objdir\" + + if test ! -f \"\$progdir/\$program\" || + { file=\`ls -1dt \"\$progdir/\$program\" \"\$progdir/../\$program\" 2>/dev/null | ${SED} 1q\`; \\ + test \"X\$file\" != \"X\$progdir/\$program\"; }; then + + file=\"\$\$-\$program\" + + if test ! -d \"\$progdir\"; then + $MKDIR \"\$progdir\" + else + $RM \"\$progdir/\$file\" + fi" + + $ECHO "\ + + # relink executable if necessary + if test -n \"\$relink_command\"; then + if relink_command_output=\`eval \$relink_command 2>&1\`; then : + else + $ECHO \"\$relink_command_output\" >&2 + $RM \"\$progdir/\$file\" + exit 1 + fi + fi + + $MV \"\$progdir/\$file\" \"\$progdir/\$program\" 2>/dev/null || + { $RM \"\$progdir/\$program\"; + $MV \"\$progdir/\$file\" \"\$progdir/\$program\"; } + $RM \"\$progdir/\$file\" + fi" + else + $ECHO "\ + program='$outputname' + progdir=\"\$thisdir/$objdir\" +" + fi + + $ECHO "\ + + if test -f \"\$progdir/\$program\"; then" + + # Export our shlibpath_var if we have one. + if test "$shlibpath_overrides_runpath" = yes && test -n "$shlibpath_var" && test -n "$temp_rpath"; then + $ECHO "\ + # Add our own library path to $shlibpath_var + $shlibpath_var=\"$temp_rpath\$$shlibpath_var\" + + # Some systems cannot cope with colon-terminated $shlibpath_var + # The second colon is a workaround for a bug in BeOS R4 sed + $shlibpath_var=\`\$ECHO \"X\$$shlibpath_var\" | \$Xsed -e 's/::*\$//'\` + + export $shlibpath_var +" + fi + + # fixup the dll searchpath if we need to. + if test -n "$dllsearchpath"; then + $ECHO "\ + # Add the dll search path components to the executable PATH + PATH=$dllsearchpath:\$PATH +" + fi + + $ECHO "\ + if test \"\$libtool_execute_magic\" != \"$magic\"; then + # Run the actual program with our arguments. +" + case $host in + # Backslashes separate directories on plain windows + *-*-mingw | *-*-os2* | *-cegcc*) + $ECHO "\ + exec \"\$progdir\\\\\$program\" \${1+\"\$@\"} +" + ;; + + *) + $ECHO "\ + exec \"\$progdir/\$program\" \${1+\"\$@\"} +" + ;; + esac + $ECHO "\ + \$ECHO \"\$0: cannot exec \$program \$*\" 1>&2 + exit 1 + fi + else + # The program doesn't exist. + \$ECHO \"\$0: error: \\\`\$progdir/\$program' does not exist\" 1>&2 + \$ECHO \"This script is just a wrapper for \$program.\" 1>&2 + $ECHO \"See the $PACKAGE documentation for more information.\" 1>&2 + exit 1 + fi +fi\ +" +} +# end: func_emit_wrapper_part2 + + +# func_emit_wrapper [arg=no] +# +# Emit a libtool wrapper script on stdout. +# Don't directly open a file because we may want to +# incorporate the script contents within a cygwin/mingw +# wrapper executable. Must ONLY be called from within +# func_mode_link because it depends on a number of variables +# set therein. +# +# ARG is the value that the WRAPPER_SCRIPT_BELONGS_IN_OBJDIR +# variable will take. If 'yes', then the emitted script +# will assume that the directory in which it is stored is +# the $objdir directory. This is a cygwin/mingw-specific +# behavior. +func_emit_wrapper () +{ + func_emit_wrapper_arg1=no + if test -n "$1" ; then + func_emit_wrapper_arg1=$1 + fi + + # split this up so that func_emit_cwrapperexe_src + # can call each part independently. + func_emit_wrapper_part1 "${func_emit_wrapper_arg1}" + func_emit_wrapper_part2 "${func_emit_wrapper_arg1}" +} + + +# func_to_host_path arg +# +# Convert paths to host format when used with build tools. +# Intended for use with "native" mingw (where libtool itself +# is running under the msys shell), or in the following cross- +# build environments: +# $build $host +# mingw (msys) mingw [e.g. native] +# cygwin mingw +# *nix + wine mingw +# where wine is equipped with the `winepath' executable. +# In the native mingw case, the (msys) shell automatically +# converts paths for any non-msys applications it launches, +# but that facility isn't available from inside the cwrapper. +# Similar accommodations are necessary for $host mingw and +# $build cygwin. Calling this function does no harm for other +# $host/$build combinations not listed above. +# +# ARG is the path (on $build) that should be converted to +# the proper representation for $host. The result is stored +# in $func_to_host_path_result. +func_to_host_path () +{ + func_to_host_path_result="$1" + if test -n "$1" ; then + case $host in + *mingw* ) + lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' + case $build in + *mingw* ) # actually, msys + # awkward: cmd appends spaces to result + lt_sed_strip_trailing_spaces="s/[ ]*\$//" + func_to_host_path_tmp1=`( cmd //c echo "$1" |\ + $SED -e "$lt_sed_strip_trailing_spaces" ) 2>/dev/null || echo ""` + func_to_host_path_result=`echo "$func_to_host_path_tmp1" |\ + $SED -e "$lt_sed_naive_backslashify"` + ;; + *cygwin* ) + func_to_host_path_tmp1=`cygpath -w "$1"` + func_to_host_path_result=`echo "$func_to_host_path_tmp1" |\ + $SED -e "$lt_sed_naive_backslashify"` + ;; + * ) + # Unfortunately, winepath does not exit with a non-zero + # error code, so we are forced to check the contents of + # stdout. On the other hand, if the command is not + # found, the shell will set an exit code of 127 and print + # *an error message* to stdout. So we must check for both + # error code of zero AND non-empty stdout, which explains + # the odd construction: + func_to_host_path_tmp1=`winepath -w "$1" 2>/dev/null` + if test "$?" -eq 0 && test -n "${func_to_host_path_tmp1}"; then + func_to_host_path_result=`echo "$func_to_host_path_tmp1" |\ + $SED -e "$lt_sed_naive_backslashify"` + else + # Allow warning below. + func_to_host_path_result="" + fi + ;; + esac + if test -z "$func_to_host_path_result" ; then + func_error "Could not determine host path corresponding to" + func_error " '$1'" + func_error "Continuing, but uninstalled executables may not work." + # Fallback: + func_to_host_path_result="$1" + fi + ;; + esac + fi +} +# end: func_to_host_path + +# func_to_host_pathlist arg +# +# Convert pathlists to host format when used with build tools. +# See func_to_host_path(), above. This function supports the +# following $build/$host combinations (but does no harm for +# combinations not listed here): +# $build $host +# mingw (msys) mingw [e.g. native] +# cygwin mingw +# *nix + wine mingw +# +# Path separators are also converted from $build format to +# $host format. If ARG begins or ends with a path separator +# character, it is preserved (but converted to $host format) +# on output. +# +# ARG is a pathlist (on $build) that should be converted to +# the proper representation on $host. The result is stored +# in $func_to_host_pathlist_result. +func_to_host_pathlist () +{ + func_to_host_pathlist_result="$1" + if test -n "$1" ; then + case $host in + *mingw* ) + lt_sed_naive_backslashify='s|\\\\*|\\|g;s|/|\\|g;s|\\|\\\\|g' + # Remove leading and trailing path separator characters from + # ARG. msys behavior is inconsistent here, cygpath turns them + # into '.;' and ';.', and winepath ignores them completely. + func_to_host_pathlist_tmp2="$1" + # Once set for this call, this variable should not be + # reassigned. It is used in tha fallback case. + func_to_host_pathlist_tmp1=`echo "$func_to_host_pathlist_tmp2" |\ + $SED -e 's|^:*||' -e 's|:*$||'` + case $build in + *mingw* ) # Actually, msys. + # Awkward: cmd appends spaces to result. + lt_sed_strip_trailing_spaces="s/[ ]*\$//" + func_to_host_pathlist_tmp2=`( cmd //c echo "$func_to_host_pathlist_tmp1" |\ + $SED -e "$lt_sed_strip_trailing_spaces" ) 2>/dev/null || echo ""` + func_to_host_pathlist_result=`echo "$func_to_host_pathlist_tmp2" |\ + $SED -e "$lt_sed_naive_backslashify"` + ;; + *cygwin* ) + func_to_host_pathlist_tmp2=`cygpath -w -p "$func_to_host_pathlist_tmp1"` + func_to_host_pathlist_result=`echo "$func_to_host_pathlist_tmp2" |\ + $SED -e "$lt_sed_naive_backslashify"` + ;; + * ) + # unfortunately, winepath doesn't convert pathlists + func_to_host_pathlist_result="" + func_to_host_pathlist_oldIFS=$IFS + IFS=: + for func_to_host_pathlist_f in $func_to_host_pathlist_tmp1 ; do + IFS=$func_to_host_pathlist_oldIFS + if test -n "$func_to_host_pathlist_f" ; then + func_to_host_path "$func_to_host_pathlist_f" + if test -n "$func_to_host_path_result" ; then + if test -z "$func_to_host_pathlist_result" ; then + func_to_host_pathlist_result="$func_to_host_path_result" + else + func_to_host_pathlist_result="$func_to_host_pathlist_result;$func_to_host_path_result" + fi + fi + fi + IFS=: + done + IFS=$func_to_host_pathlist_oldIFS + ;; + esac + if test -z "$func_to_host_pathlist_result" ; then + func_error "Could not determine the host path(s) corresponding to" + func_error " '$1'" + func_error "Continuing, but uninstalled executables may not work." + # Fallback. This may break if $1 contains DOS-style drive + # specifications. The fix is not to complicate the expression + # below, but for the user to provide a working wine installation + # with winepath so that path translation in the cross-to-mingw + # case works properly. + lt_replace_pathsep_nix_to_dos="s|:|;|g" + func_to_host_pathlist_result=`echo "$func_to_host_pathlist_tmp1" |\ + $SED -e "$lt_replace_pathsep_nix_to_dos"` + fi + # Now, add the leading and trailing path separators back + case "$1" in + :* ) func_to_host_pathlist_result=";$func_to_host_pathlist_result" + ;; + esac + case "$1" in + *: ) func_to_host_pathlist_result="$func_to_host_pathlist_result;" + ;; + esac + ;; + esac + fi +} +# end: func_to_host_pathlist + +# func_emit_cwrapperexe_src +# emit the source code for a wrapper executable on stdout +# Must ONLY be called from within func_mode_link because +# it depends on a number of variable set therein. +func_emit_cwrapperexe_src () +{ + cat < +#include +#ifdef _MSC_VER +# include +# include +# include +# define setmode _setmode +#else +# include +# include +# ifdef __CYGWIN__ +# include +# define HAVE_SETENV +# ifdef __STRICT_ANSI__ +char *realpath (const char *, char *); +int putenv (char *); +int setenv (const char *, const char *, int); +# endif +# endif +#endif +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(PATH_MAX) +# define LT_PATHMAX PATH_MAX +#elif defined(MAXPATHLEN) +# define LT_PATHMAX MAXPATHLEN +#else +# define LT_PATHMAX 1024 +#endif + +#ifndef S_IXOTH +# define S_IXOTH 0 +#endif +#ifndef S_IXGRP +# define S_IXGRP 0 +#endif + +#ifdef _MSC_VER +# define S_IXUSR _S_IEXEC +# define stat _stat +# ifndef _INTPTR_T_DEFINED +# define intptr_t int +# endif +#endif + +#ifndef DIR_SEPARATOR +# define DIR_SEPARATOR '/' +# define PATH_SEPARATOR ':' +#endif + +#if defined (_WIN32) || defined (__MSDOS__) || defined (__DJGPP__) || \ + defined (__OS2__) +# define HAVE_DOS_BASED_FILE_SYSTEM +# define FOPEN_WB "wb" +# ifndef DIR_SEPARATOR_2 +# define DIR_SEPARATOR_2 '\\' +# endif +# ifndef PATH_SEPARATOR_2 +# define PATH_SEPARATOR_2 ';' +# endif +#endif + +#ifndef DIR_SEPARATOR_2 +# define IS_DIR_SEPARATOR(ch) ((ch) == DIR_SEPARATOR) +#else /* DIR_SEPARATOR_2 */ +# define IS_DIR_SEPARATOR(ch) \ + (((ch) == DIR_SEPARATOR) || ((ch) == DIR_SEPARATOR_2)) +#endif /* DIR_SEPARATOR_2 */ + +#ifndef PATH_SEPARATOR_2 +# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR) +#else /* PATH_SEPARATOR_2 */ +# define IS_PATH_SEPARATOR(ch) ((ch) == PATH_SEPARATOR_2) +#endif /* PATH_SEPARATOR_2 */ + +#ifdef __CYGWIN__ +# define FOPEN_WB "wb" +#endif + +#ifndef FOPEN_WB +# define FOPEN_WB "w" +#endif +#ifndef _O_BINARY +# define _O_BINARY 0 +#endif + +#define XMALLOC(type, num) ((type *) xmalloc ((num) * sizeof(type))) +#define XFREE(stale) do { \ + if (stale) { free ((void *) stale); stale = 0; } \ +} while (0) + +#undef LTWRAPPER_DEBUGPRINTF +#if defined DEBUGWRAPPER +# define LTWRAPPER_DEBUGPRINTF(args) ltwrapper_debugprintf args +static void +ltwrapper_debugprintf (const char *fmt, ...) +{ + va_list args; + va_start (args, fmt); + (void) vfprintf (stderr, fmt, args); + va_end (args); +} +#else +# define LTWRAPPER_DEBUGPRINTF(args) +#endif + +const char *program_name = NULL; + +void *xmalloc (size_t num); +char *xstrdup (const char *string); +const char *base_name (const char *name); +char *find_executable (const char *wrapper); +char *chase_symlinks (const char *pathspec); +int make_executable (const char *path); +int check_executable (const char *path); +char *strendzap (char *str, const char *pat); +void lt_fatal (const char *message, ...); +void lt_setenv (const char *name, const char *value); +char *lt_extend_str (const char *orig_value, const char *add, int to_end); +void lt_opt_process_env_set (const char *arg); +void lt_opt_process_env_prepend (const char *arg); +void lt_opt_process_env_append (const char *arg); +int lt_split_name_value (const char *arg, char** name, char** value); +void lt_update_exe_path (const char *name, const char *value); +void lt_update_lib_path (const char *name, const char *value); + +static const char *script_text_part1 = +EOF + + func_emit_wrapper_part1 yes | + $SED -e 's/\([\\"]\)/\\\1/g' \ + -e 's/^/ "/' -e 's/$/\\n"/' + echo ";" + cat <"))); + for (i = 0; i < newargc; i++) + { + LTWRAPPER_DEBUGPRINTF (("(main) newargz[%d] : %s\n", i, (newargz[i] ? newargz[i] : ""))); + } + +EOF + + case $host_os in + mingw*) + cat <<"EOF" + /* execv doesn't actually work on mingw as expected on unix */ + rval = _spawnv (_P_WAIT, lt_argv_zero, (const char * const *) newargz); + if (rval == -1) + { + /* failed to start process */ + LTWRAPPER_DEBUGPRINTF (("(main) failed to launch target \"%s\": errno = %d\n", lt_argv_zero, errno)); + return 127; + } + return rval; +EOF + ;; + *) + cat <<"EOF" + execv (lt_argv_zero, newargz); + return rval; /* =127, but avoids unused variable warning */ +EOF + ;; + esac + + cat <<"EOF" +} + +void * +xmalloc (size_t num) +{ + void *p = (void *) malloc (num); + if (!p) + lt_fatal ("Memory exhausted"); + + return p; +} + +char * +xstrdup (const char *string) +{ + return string ? strcpy ((char *) xmalloc (strlen (string) + 1), + string) : NULL; +} + +const char * +base_name (const char *name) +{ + const char *base; + +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + /* Skip over the disk name in MSDOS pathnames. */ + if (isalpha ((unsigned char) name[0]) && name[1] == ':') + name += 2; +#endif + + for (base = name; *name; name++) + if (IS_DIR_SEPARATOR (*name)) + base = name + 1; + return base; +} + +int +check_executable (const char *path) +{ + struct stat st; + + LTWRAPPER_DEBUGPRINTF (("(check_executable) : %s\n", + path ? (*path ? path : "EMPTY!") : "NULL!")); + if ((!path) || (!*path)) + return 0; + + if ((stat (path, &st) >= 0) + && (st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH))) + return 1; + else + return 0; +} + +int +make_executable (const char *path) +{ + int rval = 0; + struct stat st; + + LTWRAPPER_DEBUGPRINTF (("(make_executable) : %s\n", + path ? (*path ? path : "EMPTY!") : "NULL!")); + if ((!path) || (!*path)) + return 0; + + if (stat (path, &st) >= 0) + { + rval = chmod (path, st.st_mode | S_IXOTH | S_IXGRP | S_IXUSR); + } + return rval; +} + +/* Searches for the full path of the wrapper. Returns + newly allocated full path name if found, NULL otherwise + Does not chase symlinks, even on platforms that support them. +*/ +char * +find_executable (const char *wrapper) +{ + int has_slash = 0; + const char *p; + const char *p_next; + /* static buffer for getcwd */ + char tmp[LT_PATHMAX + 1]; + int tmp_len; + char *concat_name; + + LTWRAPPER_DEBUGPRINTF (("(find_executable) : %s\n", + wrapper ? (*wrapper ? wrapper : "EMPTY!") : "NULL!")); + + if ((wrapper == NULL) || (*wrapper == '\0')) + return NULL; + + /* Absolute path? */ +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + if (isalpha ((unsigned char) wrapper[0]) && wrapper[1] == ':') + { + concat_name = xstrdup (wrapper); + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } + else + { +#endif + if (IS_DIR_SEPARATOR (wrapper[0])) + { + concat_name = xstrdup (wrapper); + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } +#if defined (HAVE_DOS_BASED_FILE_SYSTEM) + } +#endif + + for (p = wrapper; *p; p++) + if (*p == '/') + { + has_slash = 1; + break; + } + if (!has_slash) + { + /* no slashes; search PATH */ + const char *path = getenv ("PATH"); + if (path != NULL) + { + for (p = path; *p; p = p_next) + { + const char *q; + size_t p_len; + for (q = p; *q; q++) + if (IS_PATH_SEPARATOR (*q)) + break; + p_len = q - p; + p_next = (*q == '\0' ? q : q + 1); + if (p_len == 0) + { + /* empty path: current directory */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal ("getcwd failed"); + tmp_len = strlen (tmp); + concat_name = + XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, tmp, tmp_len); + concat_name[tmp_len] = '/'; + strcpy (concat_name + tmp_len + 1, wrapper); + } + else + { + concat_name = + XMALLOC (char, p_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, p, p_len); + concat_name[p_len] = '/'; + strcpy (concat_name + p_len + 1, wrapper); + } + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + } + } + /* not found in PATH; assume curdir */ + } + /* Relative path | not found in path: prepend cwd */ + if (getcwd (tmp, LT_PATHMAX) == NULL) + lt_fatal ("getcwd failed"); + tmp_len = strlen (tmp); + concat_name = XMALLOC (char, tmp_len + 1 + strlen (wrapper) + 1); + memcpy (concat_name, tmp, tmp_len); + concat_name[tmp_len] = '/'; + strcpy (concat_name + tmp_len + 1, wrapper); + + if (check_executable (concat_name)) + return concat_name; + XFREE (concat_name); + return NULL; +} + +char * +chase_symlinks (const char *pathspec) +{ +#ifndef S_ISLNK + return xstrdup (pathspec); +#else + char buf[LT_PATHMAX]; + struct stat s; + char *tmp_pathspec = xstrdup (pathspec); + char *p; + int has_symlinks = 0; + while (strlen (tmp_pathspec) && !has_symlinks) + { + LTWRAPPER_DEBUGPRINTF (("checking path component for symlinks: %s\n", + tmp_pathspec)); + if (lstat (tmp_pathspec, &s) == 0) + { + if (S_ISLNK (s.st_mode) != 0) + { + has_symlinks = 1; + break; + } + + /* search backwards for last DIR_SEPARATOR */ + p = tmp_pathspec + strlen (tmp_pathspec) - 1; + while ((p > tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) + p--; + if ((p == tmp_pathspec) && (!IS_DIR_SEPARATOR (*p))) + { + /* no more DIR_SEPARATORS left */ + break; + } + *p = '\0'; + } + else + { + char *errstr = strerror (errno); + lt_fatal ("Error accessing file %s (%s)", tmp_pathspec, errstr); + } + } + XFREE (tmp_pathspec); + + if (!has_symlinks) + { + return xstrdup (pathspec); + } + + tmp_pathspec = realpath (pathspec, buf); + if (tmp_pathspec == 0) + { + lt_fatal ("Could not follow symlinks for %s", pathspec); + } + return xstrdup (tmp_pathspec); +#endif +} + +char * +strendzap (char *str, const char *pat) +{ + size_t len, patlen; + + assert (str != NULL); + assert (pat != NULL); + + len = strlen (str); + patlen = strlen (pat); + + if (patlen <= len) + { + str += len - patlen; + if (strcmp (str, pat) == 0) + *str = '\0'; + } + return str; +} + +static void +lt_error_core (int exit_status, const char *mode, + const char *message, va_list ap) +{ + fprintf (stderr, "%s: %s: ", program_name, mode); + vfprintf (stderr, message, ap); + fprintf (stderr, ".\n"); + + if (exit_status >= 0) + exit (exit_status); +} + +void +lt_fatal (const char *message, ...) +{ + va_list ap; + va_start (ap, message); + lt_error_core (EXIT_FAILURE, "FATAL", message, ap); + va_end (ap); +} + +void +lt_setenv (const char *name, const char *value) +{ + LTWRAPPER_DEBUGPRINTF (("(lt_setenv) setting '%s' to '%s'\n", + (name ? name : ""), + (value ? value : ""))); + { +#ifdef HAVE_SETENV + /* always make a copy, for consistency with !HAVE_SETENV */ + char *str = xstrdup (value); + setenv (name, str, 1); +#else + int len = strlen (name) + 1 + strlen (value) + 1; + char *str = XMALLOC (char, len); + sprintf (str, "%s=%s", name, value); + if (putenv (str) != EXIT_SUCCESS) + { + XFREE (str); + } +#endif + } +} + +char * +lt_extend_str (const char *orig_value, const char *add, int to_end) +{ + char *new_value; + if (orig_value && *orig_value) + { + int orig_value_len = strlen (orig_value); + int add_len = strlen (add); + new_value = XMALLOC (char, add_len + orig_value_len + 1); + if (to_end) + { + strcpy (new_value, orig_value); + strcpy (new_value + orig_value_len, add); + } + else + { + strcpy (new_value, add); + strcpy (new_value + add_len, orig_value); + } + } + else + { + new_value = xstrdup (add); + } + return new_value; +} + +int +lt_split_name_value (const char *arg, char** name, char** value) +{ + const char *p; + int len; + if (!arg || !*arg) + return 1; + + p = strchr (arg, (int)'='); + + if (!p) + return 1; + + *value = xstrdup (++p); + + len = strlen (arg) - strlen (*value); + *name = XMALLOC (char, len); + strncpy (*name, arg, len-1); + (*name)[len - 1] = '\0'; + + return 0; +} + +void +lt_opt_process_env_set (const char *arg) +{ + char *name = NULL; + char *value = NULL; + + if (lt_split_name_value (arg, &name, &value) != 0) + { + XFREE (name); + XFREE (value); + lt_fatal ("bad argument for %s: '%s'", env_set_opt, arg); + } + + lt_setenv (name, value); + XFREE (name); + XFREE (value); +} + +void +lt_opt_process_env_prepend (const char *arg) +{ + char *name = NULL; + char *value = NULL; + char *new_value = NULL; + + if (lt_split_name_value (arg, &name, &value) != 0) + { + XFREE (name); + XFREE (value); + lt_fatal ("bad argument for %s: '%s'", env_prepend_opt, arg); + } + + new_value = lt_extend_str (getenv (name), value, 0); + lt_setenv (name, new_value); + XFREE (new_value); + XFREE (name); + XFREE (value); +} + +void +lt_opt_process_env_append (const char *arg) +{ + char *name = NULL; + char *value = NULL; + char *new_value = NULL; + + if (lt_split_name_value (arg, &name, &value) != 0) + { + XFREE (name); + XFREE (value); + lt_fatal ("bad argument for %s: '%s'", env_append_opt, arg); + } + + new_value = lt_extend_str (getenv (name), value, 1); + lt_setenv (name, new_value); + XFREE (new_value); + XFREE (name); + XFREE (value); +} + +void +lt_update_exe_path (const char *name, const char *value) +{ + LTWRAPPER_DEBUGPRINTF (("(lt_update_exe_path) modifying '%s' by prepending '%s'\n", + (name ? name : ""), + (value ? value : ""))); + + if (name && *name && value && *value) + { + char *new_value = lt_extend_str (getenv (name), value, 0); + /* some systems can't cope with a ':'-terminated path #' */ + int len = strlen (new_value); + while (((len = strlen (new_value)) > 0) && IS_PATH_SEPARATOR (new_value[len-1])) + { + new_value[len-1] = '\0'; + } + lt_setenv (name, new_value); + XFREE (new_value); + } +} + +void +lt_update_lib_path (const char *name, const char *value) +{ + LTWRAPPER_DEBUGPRINTF (("(lt_update_lib_path) modifying '%s' by prepending '%s'\n", + (name ? name : ""), + (value ? value : ""))); + + if (name && *name && value && *value) + { + char *new_value = lt_extend_str (getenv (name), value, 0); + lt_setenv (name, new_value); + XFREE (new_value); + } +} + + +EOF +} +# end: func_emit_cwrapperexe_src + +# func_mode_link arg... +func_mode_link () +{ + $opt_debug + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + # It is impossible to link a dll without this setting, and + # we shouldn't force the makefile maintainer to figure out + # which system we are compiling for in order to pass an extra + # flag for every libtool invocation. + # allow_undefined=no + + # FIXME: Unfortunately, there are problems with the above when trying + # to make a dll which has undefined symbols, in which case not + # even a static library is built. For now, we need to specify + # -no-undefined on the libtool link line when we can be certain + # that all symbols are satisfied, otherwise we get a static library. + allow_undefined=yes + ;; + *) + allow_undefined=yes + ;; + esac + libtool_args=$nonopt + base_compile="$nonopt $@" + compile_command=$nonopt + finalize_command=$nonopt + + compile_rpath= + finalize_rpath= + compile_shlibpath= + finalize_shlibpath= + convenience= + old_convenience= + deplibs= + old_deplibs= + compiler_flags= + linker_flags= + dllsearchpath= + lib_search_path=`pwd` + inst_prefix_dir= + new_inherited_linker_flags= + + avoid_version=no + dlfiles= + dlprefiles= + dlself=no + export_dynamic=no + export_symbols= + export_symbols_regex= + generated= + libobjs= + ltlibs= + module=no + no_install=no + objs= + non_pic_objects= + precious_files_regex= + prefer_static_libs=no + preload=no + prev= + prevarg= + release= + rpath= + xrpath= + perm_rpath= + temp_rpath= + thread_safe=no + vinfo= + vinfo_number=no + weak_libs= + single_module="${wl}-single_module" + func_infer_tag $base_compile + + # We need to know -static, to get the right output filenames. + for arg + do + case $arg in + -shared) + test "$build_libtool_libs" != yes && \ + func_fatal_configuration "can not build a shared library" + build_old_libs=no + break + ;; + -all-static | -static | -static-libtool-libs) + case $arg in + -all-static) + if test "$build_libtool_libs" = yes && test -z "$link_static_flag"; then + func_warning "complete static linking is impossible in this configuration" + fi + if test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=yes + ;; + -static) + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=built + ;; + -static-libtool-libs) + if test -z "$pic_flag" && test -n "$link_static_flag"; then + dlopen_self=$dlopen_self_static + fi + prefer_static_libs=yes + ;; + esac + build_libtool_libs=no + build_old_libs=yes + break + ;; + esac + done + + # See if our shared archives depend on static archives. + test -n "$old_archive_from_new_cmds" && build_old_libs=yes + + # Go through the arguments, transforming them on the way. + while test "$#" -gt 0; do + arg="$1" + shift + func_quote_for_eval "$arg" + qarg=$func_quote_for_eval_unquoted_result + func_append libtool_args " $func_quote_for_eval_result" + + # If the previous option needs an argument, assign it. + if test -n "$prev"; then + case $prev in + output) + func_append compile_command " @OUTPUT@" + func_append finalize_command " @OUTPUT@" + ;; + esac + + case $prev in + dlfiles|dlprefiles) + if test "$preload" = no; then + # Add the symbol object into the linking commands. + func_append compile_command " @SYMFILE@" + func_append finalize_command " @SYMFILE@" + preload=yes + fi + case $arg in + *.la | *.lo) ;; # We handle these cases below. + force) + if test "$dlself" = no; then + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + self) + if test "$prev" = dlprefiles; then + dlself=yes + elif test "$prev" = dlfiles && test "$dlopen_self" != yes; then + dlself=yes + else + dlself=needless + export_dynamic=yes + fi + prev= + continue + ;; + *) + if test "$prev" = dlfiles; then + dlfiles="$dlfiles $arg" + else + dlprefiles="$dlprefiles $arg" + fi + prev= + continue + ;; + esac + ;; + expsyms) + export_symbols="$arg" + test -f "$arg" \ + || func_fatal_error "symbol file \`$arg' does not exist" + prev= + continue + ;; + expsyms_regex) + export_symbols_regex="$arg" + prev= + continue + ;; + framework) + case $host in + *-*-darwin*) + case "$deplibs " in + *" $qarg.ltframework "*) ;; + *) deplibs="$deplibs $qarg.ltframework" # this is fixed later + ;; + esac + ;; + esac + prev= + continue + ;; + inst_prefix) + inst_prefix_dir="$arg" + prev= + continue + ;; + objectlist) + if test -f "$arg"; then + save_arg=$arg + moreargs= + for fil in `cat "$save_arg"` + do +# moreargs="$moreargs $fil" + arg=$fil + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if func_lalib_unsafe_p "$arg"; then + pic_object= + non_pic_object= + + # Read the .lo file + func_source "$arg" + + if test -z "$pic_object" || + test -z "$non_pic_object" || + test "$pic_object" = none && + test "$non_pic_object" = none; then + func_fatal_error "cannot find name of object for \`$arg'" + fi + + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + if test "$pic_object" != none; then + # Prepend the subdirectory the object is found in. + pic_object="$xdir$pic_object" + + if test "$prev" = dlfiles; then + if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then + dlfiles="$dlfiles $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test "$prev" = dlprefiles; then + # Preload the old-style object. + dlprefiles="$dlprefiles $pic_object" + prev= + fi + + # A PIC object. + func_append libobjs " $pic_object" + arg="$pic_object" + fi + + # Non-PIC object. + if test "$non_pic_object" != none; then + # Prepend the subdirectory the object is found in. + non_pic_object="$xdir$non_pic_object" + + # A standard non-PIC object + func_append non_pic_objects " $non_pic_object" + if test -z "$pic_object" || test "$pic_object" = none ; then + arg="$non_pic_object" + fi + else + # If the PIC object exists, use it instead. + # $xdir was prepended to $pic_object above. + non_pic_object="$pic_object" + func_append non_pic_objects " $non_pic_object" + fi + else + # Only an error if not doing a dry-run. + if $opt_dry_run; then + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + func_lo2o "$arg" + pic_object=$xdir$objdir/$func_lo2o_result + non_pic_object=$xdir$func_lo2o_result + func_append libobjs " $pic_object" + func_append non_pic_objects " $non_pic_object" + else + func_fatal_error "\`$arg' is not a valid libtool object" + fi + fi + done + else + func_fatal_error "link input file \`$arg' does not exist" + fi + arg=$save_arg + prev= + continue + ;; + precious_regex) + precious_files_regex="$arg" + prev= + continue + ;; + release) + release="-$arg" + prev= + continue + ;; + rpath | xrpath) + # We need an absolute path. + case $arg in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + func_fatal_error "only absolute run-paths are allowed" + ;; + esac + if test "$prev" = rpath; then + case "$rpath " in + *" $arg "*) ;; + *) rpath="$rpath $arg" ;; + esac + else + case "$xrpath " in + *" $arg "*) ;; + *) xrpath="$xrpath $arg" ;; + esac + fi + prev= + continue + ;; + shrext) + shrext_cmds="$arg" + prev= + continue + ;; + weak) + weak_libs="$weak_libs $arg" + prev= + continue + ;; + xcclinker) + linker_flags="$linker_flags $qarg" + compiler_flags="$compiler_flags $qarg" + prev= + func_append compile_command " $qarg" + func_append finalize_command " $qarg" + continue + ;; + xcompiler) + compiler_flags="$compiler_flags $qarg" + prev= + func_append compile_command " $qarg" + func_append finalize_command " $qarg" + continue + ;; + xlinker) + linker_flags="$linker_flags $qarg" + compiler_flags="$compiler_flags $wl$qarg" + prev= + func_append compile_command " $wl$qarg" + func_append finalize_command " $wl$qarg" + continue + ;; + *) + eval "$prev=\"\$arg\"" + prev= + continue + ;; + esac + fi # test -n "$prev" + + prevarg="$arg" + + case $arg in + -all-static) + if test -n "$link_static_flag"; then + # See comment for -static flag below, for more details. + func_append compile_command " $link_static_flag" + func_append finalize_command " $link_static_flag" + fi + continue + ;; + + -allow-undefined) + # FIXME: remove this flag sometime in the future. + func_fatal_error "\`-allow-undefined' must not be used because it is the default" + ;; + + -avoid-version) + avoid_version=yes + continue + ;; + + -dlopen) + prev=dlfiles + continue + ;; + + -dlpreopen) + prev=dlprefiles + continue + ;; + + -export-dynamic) + export_dynamic=yes + continue + ;; + + -export-symbols | -export-symbols-regex) + if test -n "$export_symbols" || test -n "$export_symbols_regex"; then + func_fatal_error "more than one -exported-symbols argument is not allowed" + fi + if test "X$arg" = "X-export-symbols"; then + prev=expsyms + else + prev=expsyms_regex + fi + continue + ;; + + -framework) + prev=framework + continue + ;; + + -inst-prefix-dir) + prev=inst_prefix + continue + ;; + + # The native IRIX linker understands -LANG:*, -LIST:* and -LNO:* + # so, if we see these flags be careful not to treat them like -L + -L[A-Z][A-Z]*:*) + case $with_gcc/$host in + no/*-*-irix* | /*-*-irix*) + func_append compile_command " $arg" + func_append finalize_command " $arg" + ;; + esac + continue + ;; + + -L*) + func_stripname '-L' '' "$arg" + dir=$func_stripname_result + if test -z "$dir"; then + if test "$#" -gt 0; then + func_fatal_error "require no space between \`-L' and \`$1'" + else + func_fatal_error "need path for \`-L' option" + fi + fi + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + absdir=`cd "$dir" && pwd` + test -z "$absdir" && \ + func_fatal_error "cannot determine absolute directory name of \`$dir'" + dir="$absdir" + ;; + esac + case "$deplibs " in + *" -L$dir "*) ;; + *) + deplibs="$deplibs -L$dir" + lib_search_path="$lib_search_path $dir" + ;; + esac + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + testbindir=`$ECHO "X$dir" | $Xsed -e 's*/lib$*/bin*'` + case :$dllsearchpath: in + *":$dir:"*) ;; + ::) dllsearchpath=$dir;; + *) dllsearchpath="$dllsearchpath:$dir";; + esac + case :$dllsearchpath: in + *":$testbindir:"*) ;; + ::) dllsearchpath=$testbindir;; + *) dllsearchpath="$dllsearchpath:$testbindir";; + esac + ;; + esac + continue + ;; + + -l*) + if test "X$arg" = "X-lc" || test "X$arg" = "X-lm"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-beos* | *-cegcc*) + # These systems don't actually have a C or math library (as such) + continue + ;; + *-*-os2*) + # These systems don't actually have a C library (as such) + test "X$arg" = "X-lc" && continue + ;; + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc due to us having libc/libc_r. + test "X$arg" = "X-lc" && continue + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C and math libraries are in the System framework + deplibs="$deplibs System.ltframework" + continue + ;; + *-*-sco3.2v5* | *-*-sco5v6*) + # Causes problems with __ctype + test "X$arg" = "X-lc" && continue + ;; + *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) + # Compiler inserts libc in the correct place for threads to work + test "X$arg" = "X-lc" && continue + ;; + esac + elif test "X$arg" = "X-lc_r"; then + case $host in + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc_r directly, use -pthread flag. + continue + ;; + esac + fi + deplibs="$deplibs $arg" + continue + ;; + + -module) + module=yes + continue + ;; + + # Tru64 UNIX uses -model [arg] to determine the layout of C++ + # classes, name mangling, and exception handling. + # Darwin uses the -arch flag to determine output architecture. + -model|-arch|-isysroot) + compiler_flags="$compiler_flags $arg" + func_append compile_command " $arg" + func_append finalize_command " $arg" + prev=xcompiler + continue + ;; + + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe|-threads) + compiler_flags="$compiler_flags $arg" + func_append compile_command " $arg" + func_append finalize_command " $arg" + case "$new_inherited_linker_flags " in + *" $arg "*) ;; + * ) new_inherited_linker_flags="$new_inherited_linker_flags $arg" ;; + esac + continue + ;; + + -multi_module) + single_module="${wl}-multi_module" + continue + ;; + + -no-fast-install) + fast_install=no + continue + ;; + + -no-install) + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-darwin* | *-cegcc*) + # The PATH hackery in wrapper scripts is required on Windows + # and Darwin in order for the loader to find any dlls it needs. + func_warning "\`-no-install' is ignored for $host" + func_warning "assuming \`-no-fast-install' instead" + fast_install=no + ;; + *) no_install=yes ;; + esac + continue + ;; + + -no-undefined) + allow_undefined=no + continue + ;; + + -objectlist) + prev=objectlist + continue + ;; + + -o) prev=output ;; + + -precious-files-regex) + prev=precious_regex + continue + ;; + + -release) + prev=release + continue + ;; + + -rpath) + prev=rpath + continue + ;; + + -R) + prev=xrpath + continue + ;; + + -R*) + func_stripname '-R' '' "$arg" + dir=$func_stripname_result + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) ;; + *) + func_fatal_error "only absolute run-paths are allowed" + ;; + esac + case "$xrpath " in + *" $dir "*) ;; + *) xrpath="$xrpath $dir" ;; + esac + continue + ;; + + -shared) + # The effects of -shared are defined in a previous loop. + continue + ;; + + -shrext) + prev=shrext + continue + ;; + + -static | -static-libtool-libs) + # The effects of -static are defined in a previous loop. + # We used to do the same as -all-static on platforms that + # didn't have a PIC flag, but the assumption that the effects + # would be equivalent was wrong. It would break on at least + # Digital Unix and AIX. + continue + ;; + + -thread-safe) + thread_safe=yes + continue + ;; + + -version-info) + prev=vinfo + continue + ;; + + -version-number) + prev=vinfo + vinfo_number=yes + continue + ;; + + -weak) + prev=weak + continue + ;; + + -Wc,*) + func_stripname '-Wc,' '' "$arg" + args=$func_stripname_result + arg= + save_ifs="$IFS"; IFS=',' + for flag in $args; do + IFS="$save_ifs" + func_quote_for_eval "$flag" + arg="$arg $wl$func_quote_for_eval_result" + compiler_flags="$compiler_flags $func_quote_for_eval_result" + done + IFS="$save_ifs" + func_stripname ' ' '' "$arg" + arg=$func_stripname_result + ;; + + -Wl,*) + func_stripname '-Wl,' '' "$arg" + args=$func_stripname_result + arg= + save_ifs="$IFS"; IFS=',' + for flag in $args; do + IFS="$save_ifs" + func_quote_for_eval "$flag" + arg="$arg $wl$func_quote_for_eval_result" + compiler_flags="$compiler_flags $wl$func_quote_for_eval_result" + linker_flags="$linker_flags $func_quote_for_eval_result" + done + IFS="$save_ifs" + func_stripname ' ' '' "$arg" + arg=$func_stripname_result + ;; + + -Xcompiler) + prev=xcompiler + continue + ;; + + -Xlinker) + prev=xlinker + continue + ;; + + -XCClinker) + prev=xcclinker + continue + ;; + + # -msg_* for osf cc + -msg_*) + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + ;; + + # -64, -mips[0-9] enable 64-bit mode on the SGI compiler + # -r[0-9][0-9]* specifies the processor on the SGI compiler + # -xarch=*, -xtarget=* enable 64-bit mode on the Sun compiler + # +DA*, +DD* enable 64-bit mode on the HP compiler + # -q* pass through compiler args for the IBM compiler + # -m*, -t[45]*, -txscale* pass through architecture-specific + # compiler args for GCC + # -F/path gives path to uninstalled frameworks, gcc on darwin + # -p, -pg, --coverage, -fprofile-* pass through profiling flag for GCC + # @file GCC response files + -64|-mips[0-9]|-r[0-9][0-9]*|-xarch=*|-xtarget=*|+DA*|+DD*|-q*|-m*| \ + -t[45]*|-txscale*|-p|-pg|--coverage|-fprofile-*|-F*|@*) + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + func_append compile_command " $arg" + func_append finalize_command " $arg" + compiler_flags="$compiler_flags $arg" + continue + ;; + + # Some other compiler flag. + -* | +*) + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + ;; + + *.$objext) + # A standard object. + objs="$objs $arg" + ;; + + *.lo) + # A libtool-controlled object. + + # Check to see that this really is a libtool object. + if func_lalib_unsafe_p "$arg"; then + pic_object= + non_pic_object= + + # Read the .lo file + func_source "$arg" + + if test -z "$pic_object" || + test -z "$non_pic_object" || + test "$pic_object" = none && + test "$non_pic_object" = none; then + func_fatal_error "cannot find name of object for \`$arg'" + fi + + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + if test "$pic_object" != none; then + # Prepend the subdirectory the object is found in. + pic_object="$xdir$pic_object" + + if test "$prev" = dlfiles; then + if test "$build_libtool_libs" = yes && test "$dlopen_support" = yes; then + dlfiles="$dlfiles $pic_object" + prev= + continue + else + # If libtool objects are unsupported, then we need to preload. + prev=dlprefiles + fi + fi + + # CHECK ME: I think I busted this. -Ossama + if test "$prev" = dlprefiles; then + # Preload the old-style object. + dlprefiles="$dlprefiles $pic_object" + prev= + fi + + # A PIC object. + func_append libobjs " $pic_object" + arg="$pic_object" + fi + + # Non-PIC object. + if test "$non_pic_object" != none; then + # Prepend the subdirectory the object is found in. + non_pic_object="$xdir$non_pic_object" + + # A standard non-PIC object + func_append non_pic_objects " $non_pic_object" + if test -z "$pic_object" || test "$pic_object" = none ; then + arg="$non_pic_object" + fi + else + # If the PIC object exists, use it instead. + # $xdir was prepended to $pic_object above. + non_pic_object="$pic_object" + func_append non_pic_objects " $non_pic_object" + fi + else + # Only an error if not doing a dry-run. + if $opt_dry_run; then + # Extract subdirectory from the argument. + func_dirname "$arg" "/" "" + xdir="$func_dirname_result" + + func_lo2o "$arg" + pic_object=$xdir$objdir/$func_lo2o_result + non_pic_object=$xdir$func_lo2o_result + func_append libobjs " $pic_object" + func_append non_pic_objects " $non_pic_object" + else + func_fatal_error "\`$arg' is not a valid libtool object" + fi + fi + ;; + + *.$libext) + # An archive. + deplibs="$deplibs $arg" + old_deplibs="$old_deplibs $arg" + continue + ;; + + *.la) + # A libtool-controlled library. + + if test "$prev" = dlfiles; then + # This library was specified with -dlopen. + dlfiles="$dlfiles $arg" + prev= + elif test "$prev" = dlprefiles; then + # The library was specified with -dlpreopen. + dlprefiles="$dlprefiles $arg" + prev= + else + deplibs="$deplibs $arg" + fi + continue + ;; + + # Some other compiler argument. + *) + # Unknown arguments in both finalize_command and compile_command need + # to be aesthetically quoted because they are evaled later. + func_quote_for_eval "$arg" + arg="$func_quote_for_eval_result" + ;; + esac # arg + + # Now actually substitute the argument into the commands. + if test -n "$arg"; then + func_append compile_command " $arg" + func_append finalize_command " $arg" + fi + done # argument parsing loop + + test -n "$prev" && \ + func_fatal_help "the \`$prevarg' option requires an argument" + + if test "$export_dynamic" = yes && test -n "$export_dynamic_flag_spec"; then + eval arg=\"$export_dynamic_flag_spec\" + func_append compile_command " $arg" + func_append finalize_command " $arg" + fi + + oldlibs= + # calculate the name of the file, without its directory + func_basename "$output" + outputname="$func_basename_result" + libobjs_save="$libobjs" + + if test -n "$shlibpath_var"; then + # get the directories listed in $shlibpath_var + eval shlib_search_path=\`\$ECHO \"X\${$shlibpath_var}\" \| \$Xsed -e \'s/:/ /g\'\` + else + shlib_search_path= + fi + eval sys_lib_search_path=\"$sys_lib_search_path_spec\" + eval sys_lib_dlsearch_path=\"$sys_lib_dlsearch_path_spec\" + + func_dirname "$output" "/" "" + output_objdir="$func_dirname_result$objdir" + # Create the object directory. + func_mkdir_p "$output_objdir" + + # Determine the type of output + case $output in + "") + func_fatal_help "you must specify an output file" + ;; + *.$libext) linkmode=oldlib ;; + *.lo | *.$objext) linkmode=obj ;; + *.la) linkmode=lib ;; + *) linkmode=prog ;; # Anything else should be a program. + esac + + specialdeplibs= + + libs= + # Find all interdependent deplibs by searching for libraries + # that are linked more than once (e.g. -la -lb -la) + for deplib in $deplibs; do + if $opt_duplicate_deps ; then + case "$libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + libs="$libs $deplib" + done + + if test "$linkmode" = lib; then + libs="$predeps $libs $compiler_lib_search_path $postdeps" + + # Compute libraries that are listed more than once in $predeps + # $postdeps and mark them as special (i.e., whose duplicates are + # not to be eliminated). + pre_post_deps= + if $opt_duplicate_compiler_generated_deps; then + for pre_post_dep in $predeps $postdeps; do + case "$pre_post_deps " in + *" $pre_post_dep "*) specialdeplibs="$specialdeplibs $pre_post_deps" ;; + esac + pre_post_deps="$pre_post_deps $pre_post_dep" + done + fi + pre_post_deps= + fi + + deplibs= + newdependency_libs= + newlib_search_path= + need_relink=no # whether we're linking any uninstalled libtool libraries + notinst_deplibs= # not-installed libtool libraries + notinst_path= # paths that contain not-installed libtool libraries + + case $linkmode in + lib) + passes="conv dlpreopen link" + for file in $dlfiles $dlprefiles; do + case $file in + *.la) ;; + *) + func_fatal_help "libraries can \`-dlopen' only libtool libraries: $file" + ;; + esac + done + ;; + prog) + compile_deplibs= + finalize_deplibs= + alldeplibs=no + newdlfiles= + newdlprefiles= + passes="conv scan dlopen dlpreopen link" + ;; + *) passes="conv" + ;; + esac + + for pass in $passes; do + # The preopen pass in lib mode reverses $deplibs; put it back here + # so that -L comes before libs that need it for instance... + if test "$linkmode,$pass" = "lib,link"; then + ## FIXME: Find the place where the list is rebuilt in the wrong + ## order, and fix it there properly + tmp_deplibs= + for deplib in $deplibs; do + tmp_deplibs="$deplib $tmp_deplibs" + done + deplibs="$tmp_deplibs" + fi + + if test "$linkmode,$pass" = "lib,link" || + test "$linkmode,$pass" = "prog,scan"; then + libs="$deplibs" + deplibs= + fi + if test "$linkmode" = prog; then + case $pass in + dlopen) libs="$dlfiles" ;; + dlpreopen) libs="$dlprefiles" ;; + link) libs="$deplibs %DEPLIBS% $dependency_libs" ;; + esac + fi + if test "$linkmode,$pass" = "lib,dlpreopen"; then + # Collect and forward deplibs of preopened libtool libs + for lib in $dlprefiles; do + # Ignore non-libtool-libs + dependency_libs= + case $lib in + *.la) func_source "$lib" ;; + esac + + # Collect preopened libtool deplibs, except any this library + # has declared as weak libs + for deplib in $dependency_libs; do + deplib_base=`$ECHO "X$deplib" | $Xsed -e "$basename"` + case " $weak_libs " in + *" $deplib_base "*) ;; + *) deplibs="$deplibs $deplib" ;; + esac + done + done + libs="$dlprefiles" + fi + if test "$pass" = dlopen; then + # Collect dlpreopened libraries + save_deplibs="$deplibs" + deplibs= + fi + + for deplib in $libs; do + lib= + found=no + case $deplib in + -mt|-mthreads|-kthread|-Kthread|-pthread|-pthreads|--thread-safe|-threads) + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + compiler_flags="$compiler_flags $deplib" + if test "$linkmode" = lib ; then + case "$new_inherited_linker_flags " in + *" $deplib "*) ;; + * ) new_inherited_linker_flags="$new_inherited_linker_flags $deplib" ;; + esac + fi + fi + continue + ;; + -l*) + if test "$linkmode" != lib && test "$linkmode" != prog; then + func_warning "\`-l' is ignored for archives/objects" + continue + fi + func_stripname '-l' '' "$deplib" + name=$func_stripname_result + if test "$linkmode" = lib; then + searchdirs="$newlib_search_path $lib_search_path $compiler_lib_search_dirs $sys_lib_search_path $shlib_search_path" + else + searchdirs="$newlib_search_path $lib_search_path $sys_lib_search_path $shlib_search_path" + fi + for searchdir in $searchdirs; do + for search_ext in .la $std_shrext .so .a; do + # Search the libtool library + lib="$searchdir/lib${name}${search_ext}" + if test -f "$lib"; then + if test "$search_ext" = ".la"; then + found=yes + else + found=no + fi + break 2 + fi + done + done + if test "$found" != yes; then + # deplib doesn't seem to be a libtool library + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" + fi + continue + else # deplib is a libtool library + # If $allow_libtool_libs_with_static_runtimes && $deplib is a stdlib, + # We need to do some special things here, and not later. + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $deplib "*) + if func_lalib_p "$lib"; then + library_names= + old_library= + func_source "$lib" + for l in $old_library $library_names; do + ll="$l" + done + if test "X$ll" = "X$old_library" ; then # only static version available + found=no + func_dirname "$lib" "" "." + ladir="$func_dirname_result" + lib=$ladir/$old_library + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + test "$linkmode" = lib && newdependency_libs="$deplib $newdependency_libs" + fi + continue + fi + fi + ;; + *) ;; + esac + fi + fi + ;; # -l + *.ltframework) + if test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + deplibs="$deplib $deplibs" + if test "$linkmode" = lib ; then + case "$new_inherited_linker_flags " in + *" $deplib "*) ;; + * ) new_inherited_linker_flags="$new_inherited_linker_flags $deplib" ;; + esac + fi + fi + continue + ;; + -L*) + case $linkmode in + lib) + deplibs="$deplib $deplibs" + test "$pass" = conv && continue + newdependency_libs="$deplib $newdependency_libs" + func_stripname '-L' '' "$deplib" + newlib_search_path="$newlib_search_path $func_stripname_result" + ;; + prog) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + if test "$pass" = scan; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + func_stripname '-L' '' "$deplib" + newlib_search_path="$newlib_search_path $func_stripname_result" + ;; + *) + func_warning "\`-L' is ignored for archives/objects" + ;; + esac # linkmode + continue + ;; # -L + -R*) + if test "$pass" = link; then + func_stripname '-R' '' "$deplib" + dir=$func_stripname_result + # Make sure the xrpath contains only unique directories. + case "$xrpath " in + *" $dir "*) ;; + *) xrpath="$xrpath $dir" ;; + esac + fi + deplibs="$deplib $deplibs" + continue + ;; + *.la) lib="$deplib" ;; + *.$libext) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + continue + fi + case $linkmode in + lib) + # Linking convenience modules into shared libraries is allowed, + # but linking other static libraries is non-portable. + case " $dlpreconveniencelibs " in + *" $deplib "*) ;; + *) + valid_a_lib=no + case $deplibs_check_method in + match_pattern*) + set dummy $deplibs_check_method; shift + match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` + if eval "\$ECHO \"X$deplib\"" 2>/dev/null | $Xsed -e 10q \ + | $EGREP "$match_pattern_regex" > /dev/null; then + valid_a_lib=yes + fi + ;; + pass_all) + valid_a_lib=yes + ;; + esac + if test "$valid_a_lib" != yes; then + $ECHO + $ECHO "*** Warning: Trying to link with static lib archive $deplib." + $ECHO "*** I have the capability to make that library automatically link in when" + $ECHO "*** you link to this library. But I can only do this if you have a" + $ECHO "*** shared version of the library, which you do not appear to have" + $ECHO "*** because the file extensions .$libext of this argument makes me believe" + $ECHO "*** that it is just a static archive that I should not use here." + else + $ECHO + $ECHO "*** Warning: Linking the shared library $output against the" + $ECHO "*** static library $deplib is not portable!" + deplibs="$deplib $deplibs" + fi + ;; + esac + continue + ;; + prog) + if test "$pass" != link; then + deplibs="$deplib $deplibs" + else + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + fi + continue + ;; + esac # linkmode + ;; # *.$libext + *.lo | *.$objext) + if test "$pass" = conv; then + deplibs="$deplib $deplibs" + elif test "$linkmode" = prog; then + if test "$pass" = dlpreopen || test "$dlopen_support" != yes || test "$build_libtool_libs" = no; then + # If there is no dlopen support or we're linking statically, + # we need to preload. + newdlprefiles="$newdlprefiles $deplib" + compile_deplibs="$deplib $compile_deplibs" + finalize_deplibs="$deplib $finalize_deplibs" + else + newdlfiles="$newdlfiles $deplib" + fi + fi + continue + ;; + %DEPLIBS%) + alldeplibs=yes + continue + ;; + esac # case $deplib + + if test "$found" = yes || test -f "$lib"; then : + else + func_fatal_error "cannot find the library \`$lib' or unhandled argument \`$deplib'" + fi + + # Check to see that this really is a libtool archive. + func_lalib_unsafe_p "$lib" \ + || func_fatal_error "\`$lib' is not a valid libtool archive" + + func_dirname "$lib" "" "." + ladir="$func_dirname_result" + + dlname= + dlopen= + dlpreopen= + libdir= + library_names= + old_library= + inherited_linker_flags= + # If the library was installed with an old release of libtool, + # it will not redefine variables installed, or shouldnotlink + installed=yes + shouldnotlink=no + avoidtemprpath= + + + # Read the .la file + func_source "$lib" + + # Convert "-framework foo" to "foo.ltframework" + if test -n "$inherited_linker_flags"; then + tmp_inherited_linker_flags=`$ECHO "X$inherited_linker_flags" | $Xsed -e 's/-framework \([^ $]*\)/\1.ltframework/g'` + for tmp_inherited_linker_flag in $tmp_inherited_linker_flags; do + case " $new_inherited_linker_flags " in + *" $tmp_inherited_linker_flag "*) ;; + *) new_inherited_linker_flags="$new_inherited_linker_flags $tmp_inherited_linker_flag";; + esac + done + fi + dependency_libs=`$ECHO "X $dependency_libs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` + if test "$linkmode,$pass" = "lib,link" || + test "$linkmode,$pass" = "prog,scan" || + { test "$linkmode" != prog && test "$linkmode" != lib; }; then + test -n "$dlopen" && dlfiles="$dlfiles $dlopen" + test -n "$dlpreopen" && dlprefiles="$dlprefiles $dlpreopen" + fi + + if test "$pass" = conv; then + # Only check for convenience libraries + deplibs="$lib $deplibs" + if test -z "$libdir"; then + if test -z "$old_library"; then + func_fatal_error "cannot find name of link library for \`$lib'" + fi + # It is a libtool convenience library, so add in its objects. + convenience="$convenience $ladir/$objdir/$old_library" + old_convenience="$old_convenience $ladir/$objdir/$old_library" + elif test "$linkmode" != prog && test "$linkmode" != lib; then + func_fatal_error "\`$lib' is not a convenience library" + fi + tmp_libs= + for deplib in $dependency_libs; do + deplibs="$deplib $deplibs" + if $opt_duplicate_deps ; then + case "$tmp_libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + tmp_libs="$tmp_libs $deplib" + done + continue + fi # $pass = conv + + + # Get the name of the library we link against. + linklib= + for l in $old_library $library_names; do + linklib="$l" + done + if test -z "$linklib"; then + func_fatal_error "cannot find name of link library for \`$lib'" + fi + + # This library was specified with -dlopen. + if test "$pass" = dlopen; then + if test -z "$libdir"; then + func_fatal_error "cannot -dlopen a convenience library: \`$lib'" + fi + if test -z "$dlname" || + test "$dlopen_support" != yes || + test "$build_libtool_libs" = no; then + # If there is no dlname, no dlopen support or we're linking + # statically, we need to preload. We also need to preload any + # dependent libraries so libltdl's deplib preloader doesn't + # bomb out in the load deplibs phase. + dlprefiles="$dlprefiles $lib $dependency_libs" + else + newdlfiles="$newdlfiles $lib" + fi + continue + fi # $pass = dlopen + + # We need an absolute path. + case $ladir in + [\\/]* | [A-Za-z]:[\\/]*) abs_ladir="$ladir" ;; + *) + abs_ladir=`cd "$ladir" && pwd` + if test -z "$abs_ladir"; then + func_warning "cannot determine absolute directory name of \`$ladir'" + func_warning "passing it literally to the linker, although it might fail" + abs_ladir="$ladir" + fi + ;; + esac + func_basename "$lib" + laname="$func_basename_result" + + # Find the relevant object directory and library name. + if test "X$installed" = Xyes; then + if test ! -f "$libdir/$linklib" && test -f "$abs_ladir/$linklib"; then + func_warning "library \`$lib' was moved." + dir="$ladir" + absdir="$abs_ladir" + libdir="$abs_ladir" + else + dir="$libdir" + absdir="$libdir" + fi + test "X$hardcode_automatic" = Xyes && avoidtemprpath=yes + else + if test ! -f "$ladir/$objdir/$linklib" && test -f "$abs_ladir/$linklib"; then + dir="$ladir" + absdir="$abs_ladir" + # Remove this search path later + notinst_path="$notinst_path $abs_ladir" + else + dir="$ladir/$objdir" + absdir="$abs_ladir/$objdir" + # Remove this search path later + notinst_path="$notinst_path $abs_ladir" + fi + fi # $installed = yes + func_stripname 'lib' '.la' "$laname" + name=$func_stripname_result + + # This library was specified with -dlpreopen. + if test "$pass" = dlpreopen; then + if test -z "$libdir" && test "$linkmode" = prog; then + func_fatal_error "only libraries may -dlpreopen a convenience library: \`$lib'" + fi + # Prefer using a static library (so that no silly _DYNAMIC symbols + # are required to link). + if test -n "$old_library"; then + newdlprefiles="$newdlprefiles $dir/$old_library" + # Keep a list of preopened convenience libraries to check + # that they are being used correctly in the link pass. + test -z "$libdir" && \ + dlpreconveniencelibs="$dlpreconveniencelibs $dir/$old_library" + # Otherwise, use the dlname, so that lt_dlopen finds it. + elif test -n "$dlname"; then + newdlprefiles="$newdlprefiles $dir/$dlname" + else + newdlprefiles="$newdlprefiles $dir/$linklib" + fi + fi # $pass = dlpreopen + + if test -z "$libdir"; then + # Link the convenience library + if test "$linkmode" = lib; then + deplibs="$dir/$old_library $deplibs" + elif test "$linkmode,$pass" = "prog,link"; then + compile_deplibs="$dir/$old_library $compile_deplibs" + finalize_deplibs="$dir/$old_library $finalize_deplibs" + else + deplibs="$lib $deplibs" # used for prog,scan pass + fi + continue + fi + + + if test "$linkmode" = prog && test "$pass" != link; then + newlib_search_path="$newlib_search_path $ladir" + deplibs="$lib $deplibs" + + linkalldeplibs=no + if test "$link_all_deplibs" != no || test -z "$library_names" || + test "$build_libtool_libs" = no; then + linkalldeplibs=yes + fi + + tmp_libs= + for deplib in $dependency_libs; do + case $deplib in + -L*) func_stripname '-L' '' "$deplib" + newlib_search_path="$newlib_search_path $func_stripname_result" + ;; + esac + # Need to link against all dependency_libs? + if test "$linkalldeplibs" = yes; then + deplibs="$deplib $deplibs" + else + # Need to hardcode shared library paths + # or/and link against static libraries + newdependency_libs="$deplib $newdependency_libs" + fi + if $opt_duplicate_deps ; then + case "$tmp_libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + tmp_libs="$tmp_libs $deplib" + done # for deplib + continue + fi # $linkmode = prog... + + if test "$linkmode,$pass" = "prog,link"; then + if test -n "$library_names" && + { { test "$prefer_static_libs" = no || + test "$prefer_static_libs,$installed" = "built,yes"; } || + test -z "$old_library"; }; then + # We need to hardcode the library path + if test -n "$shlibpath_var" && test -z "$avoidtemprpath" ; then + # Make sure the rpath contains only unique directories. + case "$temp_rpath:" in + *"$absdir:"*) ;; + *) temp_rpath="$temp_rpath$absdir:" ;; + esac + fi + + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) compile_rpath="$compile_rpath $absdir" + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" + esac + ;; + esac + fi # $linkmode,$pass = prog,link... + + if test "$alldeplibs" = yes && + { test "$deplibs_check_method" = pass_all || + { test "$build_libtool_libs" = yes && + test -n "$library_names"; }; }; then + # We only need to search for static libraries + continue + fi + fi + + link_static=no # Whether the deplib will be linked statically + use_static_libs=$prefer_static_libs + if test "$use_static_libs" = built && test "$installed" = yes; then + use_static_libs=no + fi + if test -n "$library_names" && + { test "$use_static_libs" = no || test -z "$old_library"; }; then + case $host in + *cygwin* | *mingw* | *cegcc*) + # No point in relinking DLLs because paths are not encoded + notinst_deplibs="$notinst_deplibs $lib" + need_relink=no + ;; + *) + if test "$installed" = no; then + notinst_deplibs="$notinst_deplibs $lib" + need_relink=yes + fi + ;; + esac + # This is a shared library + + # Warn about portability, can't link against -module's on some + # systems (darwin). Don't bleat about dlopened modules though! + dlopenmodule="" + for dlpremoduletest in $dlprefiles; do + if test "X$dlpremoduletest" = "X$lib"; then + dlopenmodule="$dlpremoduletest" + break + fi + done + if test -z "$dlopenmodule" && test "$shouldnotlink" = yes && test "$pass" = link; then + $ECHO + if test "$linkmode" = prog; then + $ECHO "*** Warning: Linking the executable $output against the loadable module" + else + $ECHO "*** Warning: Linking the shared library $output against the loadable module" + fi + $ECHO "*** $linklib is not portable!" + fi + if test "$linkmode" = lib && + test "$hardcode_into_libs" = yes; then + # Hardcode the library path. + # Skip directories that are in the system default run-time + # search path. + case " $sys_lib_dlsearch_path " in + *" $absdir "*) ;; + *) + case "$compile_rpath " in + *" $absdir "*) ;; + *) compile_rpath="$compile_rpath $absdir" + esac + ;; + esac + case " $sys_lib_dlsearch_path " in + *" $libdir "*) ;; + *) + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" + esac + ;; + esac + fi + + if test -n "$old_archive_from_expsyms_cmds"; then + # figure out the soname + set dummy $library_names + shift + realname="$1" + shift + libname=`eval "\\$ECHO \"$libname_spec\""` + # use dlname if we got it. it's perfectly good, no? + if test -n "$dlname"; then + soname="$dlname" + elif test -n "$soname_spec"; then + # bleh windows + case $host in + *cygwin* | mingw* | *cegcc*) + func_arith $current - $age + major=$func_arith_result + versuffix="-$major" + ;; + esac + eval soname=\"$soname_spec\" + else + soname="$realname" + fi + + # Make a new name for the extract_expsyms_cmds to use + soroot="$soname" + func_basename "$soroot" + soname="$func_basename_result" + func_stripname 'lib' '.dll' "$soname" + newlib=libimp-$func_stripname_result.a + + # If the library has no export list, then create one now + if test -f "$output_objdir/$soname-def"; then : + else + func_verbose "extracting exported symbol list from \`$soname'" + func_execute_cmds "$extract_expsyms_cmds" 'exit $?' + fi + + # Create $newlib + if test -f "$output_objdir/$newlib"; then :; else + func_verbose "generating import library for \`$soname'" + func_execute_cmds "$old_archive_from_expsyms_cmds" 'exit $?' + fi + # make sure the library variables are pointing to the new library + dir=$output_objdir + linklib=$newlib + fi # test -n "$old_archive_from_expsyms_cmds" + + if test "$linkmode" = prog || test "$mode" != relink; then + add_shlibpath= + add_dir= + add= + lib_linked=yes + case $hardcode_action in + immediate | unsupported) + if test "$hardcode_direct" = no; then + add="$dir/$linklib" + case $host in + *-*-sco3.2v5.0.[024]*) add_dir="-L$dir" ;; + *-*-sysv4*uw2*) add_dir="-L$dir" ;; + *-*-sysv5OpenUNIX* | *-*-sysv5UnixWare7.[01].[10]* | \ + *-*-unixware7*) add_dir="-L$dir" ;; + *-*-darwin* ) + # if the lib is a (non-dlopened) module then we can not + # link against it, someone is ignoring the earlier warnings + if /usr/bin/file -L $add 2> /dev/null | + $GREP ": [^:]* bundle" >/dev/null ; then + if test "X$dlopenmodule" != "X$lib"; then + $ECHO "*** Warning: lib $linklib is a module, not a shared library" + if test -z "$old_library" ; then + $ECHO + $ECHO "*** And there doesn't seem to be a static archive available" + $ECHO "*** The link will probably fail, sorry" + else + add="$dir/$old_library" + fi + elif test -n "$old_library"; then + add="$dir/$old_library" + fi + fi + esac + elif test "$hardcode_minus_L" = no; then + case $host in + *-*-sunos*) add_shlibpath="$dir" ;; + esac + add_dir="-L$dir" + add="-l$name" + elif test "$hardcode_shlibpath_var" = no; then + add_shlibpath="$dir" + add="-l$name" + else + lib_linked=no + fi + ;; + relink) + if test "$hardcode_direct" = yes && + test "$hardcode_direct_absolute" = no; then + add="$dir/$linklib" + elif test "$hardcode_minus_L" = yes; then + add_dir="-L$dir" + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case $libdir in + [\\/]*) + add_dir="$add_dir -L$inst_prefix_dir$libdir" + ;; + esac + fi + add="-l$name" + elif test "$hardcode_shlibpath_var" = yes; then + add_shlibpath="$dir" + add="-l$name" + else + lib_linked=no + fi + ;; + *) lib_linked=no ;; + esac + + if test "$lib_linked" != yes; then + func_fatal_configuration "unsupported hardcode properties" + fi + + if test -n "$add_shlibpath"; then + case :$compile_shlibpath: in + *":$add_shlibpath:"*) ;; + *) compile_shlibpath="$compile_shlibpath$add_shlibpath:" ;; + esac + fi + if test "$linkmode" = prog; then + test -n "$add_dir" && compile_deplibs="$add_dir $compile_deplibs" + test -n "$add" && compile_deplibs="$add $compile_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + if test "$hardcode_direct" != yes && + test "$hardcode_minus_L" != yes && + test "$hardcode_shlibpath_var" = yes; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;; + esac + fi + fi + fi + + if test "$linkmode" = prog || test "$mode" = relink; then + add_shlibpath= + add_dir= + add= + # Finalize command for both is simple: just hardcode it. + if test "$hardcode_direct" = yes && + test "$hardcode_direct_absolute" = no; then + add="$libdir/$linklib" + elif test "$hardcode_minus_L" = yes; then + add_dir="-L$libdir" + add="-l$name" + elif test "$hardcode_shlibpath_var" = yes; then + case :$finalize_shlibpath: in + *":$libdir:"*) ;; + *) finalize_shlibpath="$finalize_shlibpath$libdir:" ;; + esac + add="-l$name" + elif test "$hardcode_automatic" = yes; then + if test -n "$inst_prefix_dir" && + test -f "$inst_prefix_dir$libdir/$linklib" ; then + add="$inst_prefix_dir$libdir/$linklib" + else + add="$libdir/$linklib" + fi + else + # We cannot seem to hardcode it, guess we'll fake it. + add_dir="-L$libdir" + # Try looking first in the location we're being installed to. + if test -n "$inst_prefix_dir"; then + case $libdir in + [\\/]*) + add_dir="$add_dir -L$inst_prefix_dir$libdir" + ;; + esac + fi + add="-l$name" + fi + + if test "$linkmode" = prog; then + test -n "$add_dir" && finalize_deplibs="$add_dir $finalize_deplibs" + test -n "$add" && finalize_deplibs="$add $finalize_deplibs" + else + test -n "$add_dir" && deplibs="$add_dir $deplibs" + test -n "$add" && deplibs="$add $deplibs" + fi + fi + elif test "$linkmode" = prog; then + # Here we assume that one of hardcode_direct or hardcode_minus_L + # is not unsupported. This is valid on all known static and + # shared platforms. + if test "$hardcode_direct" != unsupported; then + test -n "$old_library" && linklib="$old_library" + compile_deplibs="$dir/$linklib $compile_deplibs" + finalize_deplibs="$dir/$linklib $finalize_deplibs" + else + compile_deplibs="-l$name -L$dir $compile_deplibs" + finalize_deplibs="-l$name -L$dir $finalize_deplibs" + fi + elif test "$build_libtool_libs" = yes; then + # Not a shared library + if test "$deplibs_check_method" != pass_all; then + # We're trying link a shared library against a static one + # but the system doesn't support it. + + # Just print a warning and add the library to dependency_libs so + # that the program can be linked against the static library. + $ECHO + $ECHO "*** Warning: This system can not link to static lib archive $lib." + $ECHO "*** I have the capability to make that library automatically link in when" + $ECHO "*** you link to this library. But I can only do this if you have a" + $ECHO "*** shared version of the library, which you do not appear to have." + if test "$module" = yes; then + $ECHO "*** But as you try to build a module library, libtool will still create " + $ECHO "*** a static module, that should work as long as the dlopening application" + $ECHO "*** is linked with the -dlopen flag to resolve symbols at runtime." + if test -z "$global_symbol_pipe"; then + $ECHO + $ECHO "*** However, this would only work if libtool was able to extract symbol" + $ECHO "*** lists from a program, using \`nm' or equivalent, but libtool could" + $ECHO "*** not find such a program. So, this module is probably useless." + $ECHO "*** \`nm' from GNU binutils and a full rebuild may help." + fi + if test "$build_old_libs" = no; then + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + fi + else + deplibs="$dir/$old_library $deplibs" + link_static=yes + fi + fi # link shared/static library? + + if test "$linkmode" = lib; then + if test -n "$dependency_libs" && + { test "$hardcode_into_libs" != yes || + test "$build_old_libs" = yes || + test "$link_static" = yes; }; then + # Extract -R from dependency_libs + temp_deplibs= + for libdir in $dependency_libs; do + case $libdir in + -R*) func_stripname '-R' '' "$libdir" + temp_xrpath=$func_stripname_result + case " $xrpath " in + *" $temp_xrpath "*) ;; + *) xrpath="$xrpath $temp_xrpath";; + esac;; + *) temp_deplibs="$temp_deplibs $libdir";; + esac + done + dependency_libs="$temp_deplibs" + fi + + newlib_search_path="$newlib_search_path $absdir" + # Link against this library + test "$link_static" = no && newdependency_libs="$abs_ladir/$laname $newdependency_libs" + # ... and its dependency_libs + tmp_libs= + for deplib in $dependency_libs; do + newdependency_libs="$deplib $newdependency_libs" + if $opt_duplicate_deps ; then + case "$tmp_libs " in + *" $deplib "*) specialdeplibs="$specialdeplibs $deplib" ;; + esac + fi + tmp_libs="$tmp_libs $deplib" + done + + if test "$link_all_deplibs" != no; then + # Add the search paths of all dependency libraries + for deplib in $dependency_libs; do + case $deplib in + -L*) path="$deplib" ;; + *.la) + func_dirname "$deplib" "" "." + dir="$func_dirname_result" + # We need an absolute path. + case $dir in + [\\/]* | [A-Za-z]:[\\/]*) absdir="$dir" ;; + *) + absdir=`cd "$dir" && pwd` + if test -z "$absdir"; then + func_warning "cannot determine absolute directory name of \`$dir'" + absdir="$dir" + fi + ;; + esac + if $GREP "^installed=no" $deplib > /dev/null; then + case $host in + *-*-darwin*) + depdepl= + eval deplibrary_names=`${SED} -n -e 's/^library_names=\(.*\)$/\1/p' $deplib` + if test -n "$deplibrary_names" ; then + for tmp in $deplibrary_names ; do + depdepl=$tmp + done + if test -f "$absdir/$objdir/$depdepl" ; then + depdepl="$absdir/$objdir/$depdepl" + darwin_install_name=`${OTOOL} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` + if test -z "$darwin_install_name"; then + darwin_install_name=`${OTOOL64} -L $depdepl | awk '{if (NR == 2) {print $1;exit}}'` + fi + compiler_flags="$compiler_flags ${wl}-dylib_file ${wl}${darwin_install_name}:${depdepl}" + linker_flags="$linker_flags -dylib_file ${darwin_install_name}:${depdepl}" + path= + fi + fi + ;; + *) + path="-L$absdir/$objdir" + ;; + esac + else + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` + test -z "$libdir" && \ + func_fatal_error "\`$deplib' is not a valid libtool archive" + test "$absdir" != "$libdir" && \ + func_warning "\`$deplib' seems to be moved" + + path="-L$absdir" + fi + ;; + esac + case " $deplibs " in + *" $path "*) ;; + *) deplibs="$path $deplibs" ;; + esac + done + fi # link_all_deplibs != no + fi # linkmode = lib + done # for deplib in $libs + if test "$pass" = link; then + if test "$linkmode" = "prog"; then + compile_deplibs="$new_inherited_linker_flags $compile_deplibs" + finalize_deplibs="$new_inherited_linker_flags $finalize_deplibs" + else + compiler_flags="$compiler_flags "`$ECHO "X $new_inherited_linker_flags" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` + fi + fi + dependency_libs="$newdependency_libs" + if test "$pass" = dlpreopen; then + # Link the dlpreopened libraries before other libraries + for deplib in $save_deplibs; do + deplibs="$deplib $deplibs" + done + fi + if test "$pass" != dlopen; then + if test "$pass" != conv; then + # Make sure lib_search_path contains only unique directories. + lib_search_path= + for dir in $newlib_search_path; do + case "$lib_search_path " in + *" $dir "*) ;; + *) lib_search_path="$lib_search_path $dir" ;; + esac + done + newlib_search_path= + fi + + if test "$linkmode,$pass" != "prog,link"; then + vars="deplibs" + else + vars="compile_deplibs finalize_deplibs" + fi + for var in $vars dependency_libs; do + # Add libraries to $var in reverse order + eval tmp_libs=\"\$$var\" + new_libs= + for deplib in $tmp_libs; do + # FIXME: Pedantically, this is the right thing to do, so + # that some nasty dependency loop isn't accidentally + # broken: + #new_libs="$deplib $new_libs" + # Pragmatically, this seems to cause very few problems in + # practice: + case $deplib in + -L*) new_libs="$deplib $new_libs" ;; + -R*) ;; + *) + # And here is the reason: when a library appears more + # than once as an explicit dependence of a library, or + # is implicitly linked in more than once by the + # compiler, it is considered special, and multiple + # occurrences thereof are not removed. Compare this + # with having the same library being listed as a + # dependency of multiple other libraries: in this case, + # we know (pedantically, we assume) the library does not + # need to be listed more than once, so we keep only the + # last copy. This is not always right, but it is rare + # enough that we require users that really mean to play + # such unportable linking tricks to link the library + # using -Wl,-lname, so that libtool does not consider it + # for duplicate removal. + case " $specialdeplibs " in + *" $deplib "*) new_libs="$deplib $new_libs" ;; + *) + case " $new_libs " in + *" $deplib "*) ;; + *) new_libs="$deplib $new_libs" ;; + esac + ;; + esac + ;; + esac + done + tmp_libs= + for deplib in $new_libs; do + case $deplib in + -L*) + case " $tmp_libs " in + *" $deplib "*) ;; + *) tmp_libs="$tmp_libs $deplib" ;; + esac + ;; + *) tmp_libs="$tmp_libs $deplib" ;; + esac + done + eval $var=\"$tmp_libs\" + done # for var + fi + # Last step: remove runtime libs from dependency_libs + # (they stay in deplibs) + tmp_libs= + for i in $dependency_libs ; do + case " $predeps $postdeps $compiler_lib_search_path " in + *" $i "*) + i="" + ;; + esac + if test -n "$i" ; then + tmp_libs="$tmp_libs $i" + fi + done + dependency_libs=$tmp_libs + done # for pass + if test "$linkmode" = prog; then + dlfiles="$newdlfiles" + fi + if test "$linkmode" = prog || test "$linkmode" = lib; then + dlprefiles="$newdlprefiles" + fi + + case $linkmode in + oldlib) + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + func_warning "\`-dlopen' is ignored for archives" + fi + + case " $deplibs" in + *\ -l* | *\ -L*) + func_warning "\`-l' and \`-L' are ignored for archives" ;; + esac + + test -n "$rpath" && \ + func_warning "\`-rpath' is ignored for archives" + + test -n "$xrpath" && \ + func_warning "\`-R' is ignored for archives" + + test -n "$vinfo" && \ + func_warning "\`-version-info/-version-number' is ignored for archives" + + test -n "$release" && \ + func_warning "\`-release' is ignored for archives" + + test -n "$export_symbols$export_symbols_regex" && \ + func_warning "\`-export-symbols' is ignored for archives" + + # Now set the variables for building old libraries. + build_libtool_libs=no + oldlibs="$output" + objs="$objs$old_deplibs" + ;; + + lib) + # Make sure we only generate libraries of the form `libNAME.la'. + case $outputname in + lib*) + func_stripname 'lib' '.la' "$outputname" + name=$func_stripname_result + eval shared_ext=\"$shrext_cmds\" + eval libname=\"$libname_spec\" + ;; + *) + test "$module" = no && \ + func_fatal_help "libtool library \`$output' must begin with \`lib'" + + if test "$need_lib_prefix" != no; then + # Add the "lib" prefix for modules if required + func_stripname '' '.la' "$outputname" + name=$func_stripname_result + eval shared_ext=\"$shrext_cmds\" + eval libname=\"$libname_spec\" + else + func_stripname '' '.la' "$outputname" + libname=$func_stripname_result + fi + ;; + esac + + if test -n "$objs"; then + if test "$deplibs_check_method" != pass_all; then + func_fatal_error "cannot build libtool library \`$output' from non-libtool objects on this host:$objs" + else + $ECHO + $ECHO "*** Warning: Linking the shared library $output against the non-libtool" + $ECHO "*** objects $objs is not portable!" + libobjs="$libobjs $objs" + fi + fi + + test "$dlself" != no && \ + func_warning "\`-dlopen self' is ignored for libtool libraries" + + set dummy $rpath + shift + test "$#" -gt 1 && \ + func_warning "ignoring multiple \`-rpath's for a libtool library" + + install_libdir="$1" + + oldlibs= + if test -z "$rpath"; then + if test "$build_libtool_libs" = yes; then + # Building a libtool convenience library. + # Some compilers have problems with a `.al' extension so + # convenience libraries should have the same extension an + # archive normally would. + oldlibs="$output_objdir/$libname.$libext $oldlibs" + build_libtool_libs=convenience + build_old_libs=yes + fi + + test -n "$vinfo" && \ + func_warning "\`-version-info/-version-number' is ignored for convenience libraries" + + test -n "$release" && \ + func_warning "\`-release' is ignored for convenience libraries" + else + + # Parse the version information argument. + save_ifs="$IFS"; IFS=':' + set dummy $vinfo 0 0 0 + shift + IFS="$save_ifs" + + test -n "$7" && \ + func_fatal_help "too many parameters to \`-version-info'" + + # convert absolute version numbers to libtool ages + # this retains compatibility with .la files and attempts + # to make the code below a bit more comprehensible + + case $vinfo_number in + yes) + number_major="$1" + number_minor="$2" + number_revision="$3" + # + # There are really only two kinds -- those that + # use the current revision as the major version + # and those that subtract age and use age as + # a minor version. But, then there is irix + # which has an extra 1 added just for fun + # + case $version_type in + darwin|linux|osf|windows|none) + func_arith $number_major + $number_minor + current=$func_arith_result + age="$number_minor" + revision="$number_revision" + ;; + freebsd-aout|freebsd-elf|sunos) + current="$number_major" + revision="$number_minor" + age="0" + ;; + irix|nonstopux) + func_arith $number_major + $number_minor + current=$func_arith_result + age="$number_minor" + revision="$number_minor" + lt_irix_increment=no + ;; + esac + ;; + no) + current="$1" + revision="$2" + age="$3" + ;; + esac + + # Check that each of the things are valid numbers. + case $current in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "CURRENT \`$current' must be a nonnegative integer" + func_fatal_error "\`$vinfo' is not valid version information" + ;; + esac + + case $revision in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "REVISION \`$revision' must be a nonnegative integer" + func_fatal_error "\`$vinfo' is not valid version information" + ;; + esac + + case $age in + 0|[1-9]|[1-9][0-9]|[1-9][0-9][0-9]|[1-9][0-9][0-9][0-9]|[1-9][0-9][0-9][0-9][0-9]) ;; + *) + func_error "AGE \`$age' must be a nonnegative integer" + func_fatal_error "\`$vinfo' is not valid version information" + ;; + esac + + if test "$age" -gt "$current"; then + func_error "AGE \`$age' is greater than the current interface number \`$current'" + func_fatal_error "\`$vinfo' is not valid version information" + fi + + # Calculate the version variables. + major= + versuffix= + verstring= + case $version_type in + none) ;; + + darwin) + # Like Linux, but with the current version available in + # verstring for coding it into the library header + func_arith $current - $age + major=.$func_arith_result + versuffix="$major.$age.$revision" + # Darwin ld doesn't like 0 for these options... + func_arith $current + 1 + minor_current=$func_arith_result + xlcverstring="${wl}-compatibility_version ${wl}$minor_current ${wl}-current_version ${wl}$minor_current.$revision" + verstring="-compatibility_version $minor_current -current_version $minor_current.$revision" + ;; + + freebsd-aout) + major=".$current" + versuffix=".$current.$revision"; + ;; + + freebsd-elf) + major=".$current" + versuffix=".$current" + ;; + + irix | nonstopux) + if test "X$lt_irix_increment" = "Xno"; then + func_arith $current - $age + else + func_arith $current - $age + 1 + fi + major=$func_arith_result + + case $version_type in + nonstopux) verstring_prefix=nonstopux ;; + *) verstring_prefix=sgi ;; + esac + verstring="$verstring_prefix$major.$revision" + + # Add in all the interfaces that we are compatible with. + loop=$revision + while test "$loop" -ne 0; do + func_arith $revision - $loop + iface=$func_arith_result + func_arith $loop - 1 + loop=$func_arith_result + verstring="$verstring_prefix$major.$iface:$verstring" + done + + # Before this point, $major must not contain `.'. + major=.$major + versuffix="$major.$revision" + ;; + + linux) + func_arith $current - $age + major=.$func_arith_result + versuffix="$major.$age.$revision" + ;; + + osf) + func_arith $current - $age + major=.$func_arith_result + versuffix=".$current.$age.$revision" + verstring="$current.$age.$revision" + + # Add in all the interfaces that we are compatible with. + loop=$age + while test "$loop" -ne 0; do + func_arith $current - $loop + iface=$func_arith_result + func_arith $loop - 1 + loop=$func_arith_result + verstring="$verstring:${iface}.0" + done + + # Make executables depend on our current version. + verstring="$verstring:${current}.0" + ;; + + qnx) + major=".$current" + versuffix=".$current" + ;; + + sunos) + major=".$current" + versuffix=".$current.$revision" + ;; + + windows) + # Use '-' rather than '.', since we only want one + # extension on DOS 8.3 filesystems. + func_arith $current - $age + major=$func_arith_result + versuffix="-$major" + ;; + + *) + func_fatal_configuration "unknown library version type \`$version_type'" + ;; + esac + + # Clear the version info if we defaulted, and they specified a release. + if test -z "$vinfo" && test -n "$release"; then + major= + case $version_type in + darwin) + # we can't check for "0.0" in archive_cmds due to quoting + # problems, so we reset it completely + verstring= + ;; + *) + verstring="0.0" + ;; + esac + if test "$need_version" = no; then + versuffix= + else + versuffix=".0.0" + fi + fi + + # Remove version info from name if versioning should be avoided + if test "$avoid_version" = yes && test "$need_version" = no; then + major= + versuffix= + verstring="" + fi + + # Check to see if the archive will have undefined symbols. + if test "$allow_undefined" = yes; then + if test "$allow_undefined_flag" = unsupported; then + func_warning "undefined symbols not allowed in $host shared libraries" + build_libtool_libs=no + build_old_libs=yes + fi + else + # Don't allow undefined symbols. + allow_undefined_flag="$no_undefined_flag" + fi + + fi + + func_generate_dlsyms "$libname" "$libname" "yes" + libobjs="$libobjs $symfileobj" + test "X$libobjs" = "X " && libobjs= + + if test "$mode" != relink; then + # Remove our outputs, but don't remove object files since they + # may have been created when compiling PIC objects. + removelist= + tempremovelist=`$ECHO "$output_objdir/*"` + for p in $tempremovelist; do + case $p in + *.$objext | *.gcno) + ;; + $output_objdir/$outputname | $output_objdir/$libname.* | $output_objdir/${libname}${release}.*) + if test "X$precious_files_regex" != "X"; then + if $ECHO "$p" | $EGREP -e "$precious_files_regex" >/dev/null 2>&1 + then + continue + fi + fi + removelist="$removelist $p" + ;; + *) ;; + esac + done + test -n "$removelist" && \ + func_show_eval "${RM}r \$removelist" + fi + + # Now set the variables for building old libraries. + if test "$build_old_libs" = yes && test "$build_libtool_libs" != convenience ; then + oldlibs="$oldlibs $output_objdir/$libname.$libext" + + # Transform .lo files to .o files. + oldobjs="$objs "`$ECHO "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}'$/d' -e "$lo2o" | $NL2SP` + fi + + # Eliminate all temporary directories. + #for path in $notinst_path; do + # lib_search_path=`$ECHO "X$lib_search_path " | $Xsed -e "s% $path % %g"` + # deplibs=`$ECHO "X$deplibs " | $Xsed -e "s% -L$path % %g"` + # dependency_libs=`$ECHO "X$dependency_libs " | $Xsed -e "s% -L$path % %g"` + #done + + if test -n "$xrpath"; then + # If the user specified any rpath flags, then add them. + temp_xrpath= + for libdir in $xrpath; do + temp_xrpath="$temp_xrpath -R$libdir" + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" ;; + esac + done + if test "$hardcode_into_libs" != yes || test "$build_old_libs" = yes; then + dependency_libs="$temp_xrpath $dependency_libs" + fi + fi + + # Make sure dlfiles contains only unique files that won't be dlpreopened + old_dlfiles="$dlfiles" + dlfiles= + for lib in $old_dlfiles; do + case " $dlprefiles $dlfiles " in + *" $lib "*) ;; + *) dlfiles="$dlfiles $lib" ;; + esac + done + + # Make sure dlprefiles contains only unique files + old_dlprefiles="$dlprefiles" + dlprefiles= + for lib in $old_dlprefiles; do + case "$dlprefiles " in + *" $lib "*) ;; + *) dlprefiles="$dlprefiles $lib" ;; + esac + done + + if test "$build_libtool_libs" = yes; then + if test -n "$rpath"; then + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-*-beos* | *-cegcc*) + # these systems don't actually have a c library (as such)! + ;; + *-*-rhapsody* | *-*-darwin1.[012]) + # Rhapsody C library is in the System framework + deplibs="$deplibs System.ltframework" + ;; + *-*-netbsd*) + # Don't link with libc until the a.out ld.so is fixed. + ;; + *-*-openbsd* | *-*-freebsd* | *-*-dragonfly*) + # Do not include libc due to us having libc/libc_r. + ;; + *-*-sco3.2v5* | *-*-sco5v6*) + # Causes problems with __ctype + ;; + *-*-sysv4.2uw2* | *-*-sysv5* | *-*-unixware* | *-*-OpenUNIX*) + # Compiler inserts libc in the correct place for threads to work + ;; + *) + # Add libc to deplibs on all other systems if necessary. + if test "$build_libtool_need_lc" = "yes"; then + deplibs="$deplibs -lc" + fi + ;; + esac + fi + + # Transform deplibs into only deplibs that can be linked in shared. + name_save=$name + libname_save=$libname + release_save=$release + versuffix_save=$versuffix + major_save=$major + # I'm not sure if I'm treating the release correctly. I think + # release should show up in the -l (ie -lgmp5) so we don't want to + # add it in twice. Is that correct? + release="" + versuffix="" + major="" + newdeplibs= + droppeddeps=no + case $deplibs_check_method in + pass_all) + # Don't check for shared/static. Everything works. + # This might be a little naive. We might want to check + # whether the library exists or not. But this is on + # osf3 & osf4 and I'm not really sure... Just + # implementing what was already the behavior. + newdeplibs=$deplibs + ;; + test_compile) + # This code stresses the "libraries are programs" paradigm to its + # limits. Maybe even breaks it. We compile a program, linking it + # against the deplibs as a proxy for the library. Then we can check + # whether they linked in statically or dynamically with ldd. + $opt_dry_run || $RM conftest.c + cat > conftest.c </dev/null` + for potent_lib in $potential_libs; do + # Follow soft links. + if ls -lLd "$potent_lib" 2>/dev/null | + $GREP " -> " >/dev/null; then + continue + fi + # The statement above tries to avoid entering an + # endless loop below, in case of cyclic links. + # We might still enter an endless loop, since a link + # loop can be closed while we follow links, + # but so what? + potlib="$potent_lib" + while test -h "$potlib" 2>/dev/null; do + potliblink=`ls -ld $potlib | ${SED} 's/.* -> //'` + case $potliblink in + [\\/]* | [A-Za-z]:[\\/]*) potlib="$potliblink";; + *) potlib=`$ECHO "X$potlib" | $Xsed -e 's,[^/]*$,,'`"$potliblink";; + esac + done + if eval $file_magic_cmd \"\$potlib\" 2>/dev/null | + $SED -e 10q | + $EGREP "$file_magic_regex" > /dev/null; then + newdeplibs="$newdeplibs $a_deplib" + a_deplib="" + break 2 + fi + done + done + fi + if test -n "$a_deplib" ; then + droppeddeps=yes + $ECHO + $ECHO "*** Warning: linker path does not have real file for library $a_deplib." + $ECHO "*** I have the capability to make that library automatically link in when" + $ECHO "*** you link to this library. But I can only do this if you have a" + $ECHO "*** shared version of the library, which you do not appear to have" + $ECHO "*** because I did check the linker path looking for a file starting" + if test -z "$potlib" ; then + $ECHO "*** with $libname but no candidates were found. (...for file magic test)" + else + $ECHO "*** with $libname and none of the candidates passed a file format test" + $ECHO "*** using a file magic. Last file checked: $potlib" + fi + fi + ;; + *) + # Add a -L argument. + newdeplibs="$newdeplibs $a_deplib" + ;; + esac + done # Gone through all deplibs. + ;; + match_pattern*) + set dummy $deplibs_check_method; shift + match_pattern_regex=`expr "$deplibs_check_method" : "$1 \(.*\)"` + for a_deplib in $deplibs; do + case $a_deplib in + -l*) + func_stripname -l '' "$a_deplib" + name=$func_stripname_result + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + case " $predeps $postdeps " in + *" $a_deplib "*) + newdeplibs="$newdeplibs $a_deplib" + a_deplib="" + ;; + esac + fi + if test -n "$a_deplib" ; then + libname=`eval "\\$ECHO \"$libname_spec\""` + for i in $lib_search_path $sys_lib_search_path $shlib_search_path; do + potential_libs=`ls $i/$libname[.-]* 2>/dev/null` + for potent_lib in $potential_libs; do + potlib="$potent_lib" # see symlink-check above in file_magic test + if eval "\$ECHO \"X$potent_lib\"" 2>/dev/null | $Xsed -e 10q | \ + $EGREP "$match_pattern_regex" > /dev/null; then + newdeplibs="$newdeplibs $a_deplib" + a_deplib="" + break 2 + fi + done + done + fi + if test -n "$a_deplib" ; then + droppeddeps=yes + $ECHO + $ECHO "*** Warning: linker path does not have real file for library $a_deplib." + $ECHO "*** I have the capability to make that library automatically link in when" + $ECHO "*** you link to this library. But I can only do this if you have a" + $ECHO "*** shared version of the library, which you do not appear to have" + $ECHO "*** because I did check the linker path looking for a file starting" + if test -z "$potlib" ; then + $ECHO "*** with $libname but no candidates were found. (...for regex pattern test)" + else + $ECHO "*** with $libname and none of the candidates passed a file format test" + $ECHO "*** using a regex pattern. Last file checked: $potlib" + fi + fi + ;; + *) + # Add a -L argument. + newdeplibs="$newdeplibs $a_deplib" + ;; + esac + done # Gone through all deplibs. + ;; + none | unknown | *) + newdeplibs="" + tmp_deplibs=`$ECHO "X $deplibs" | $Xsed \ + -e 's/ -lc$//' -e 's/ -[LR][^ ]*//g'` + if test "X$allow_libtool_libs_with_static_runtimes" = "Xyes" ; then + for i in $predeps $postdeps ; do + # can't use Xsed below, because $i might contain '/' + tmp_deplibs=`$ECHO "X $tmp_deplibs" | $Xsed -e "s,$i,,"` + done + fi + if $ECHO "X $tmp_deplibs" | $Xsed -e 's/[ ]//g' | + $GREP . >/dev/null; then + $ECHO + if test "X$deplibs_check_method" = "Xnone"; then + $ECHO "*** Warning: inter-library dependencies are not supported in this platform." + else + $ECHO "*** Warning: inter-library dependencies are not known to be supported." + fi + $ECHO "*** All declared inter-library dependencies are being dropped." + droppeddeps=yes + fi + ;; + esac + versuffix=$versuffix_save + major=$major_save + release=$release_save + libname=$libname_save + name=$name_save + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library with the System framework + newdeplibs=`$ECHO "X $newdeplibs" | $Xsed -e 's/ -lc / System.ltframework /'` + ;; + esac + + if test "$droppeddeps" = yes; then + if test "$module" = yes; then + $ECHO + $ECHO "*** Warning: libtool could not satisfy all declared inter-library" + $ECHO "*** dependencies of module $libname. Therefore, libtool will create" + $ECHO "*** a static module, that should work as long as the dlopening" + $ECHO "*** application is linked with the -dlopen flag." + if test -z "$global_symbol_pipe"; then + $ECHO + $ECHO "*** However, this would only work if libtool was able to extract symbol" + $ECHO "*** lists from a program, using \`nm' or equivalent, but libtool could" + $ECHO "*** not find such a program. So, this module is probably useless." + $ECHO "*** \`nm' from GNU binutils and a full rebuild may help." + fi + if test "$build_old_libs" = no; then + oldlibs="$output_objdir/$libname.$libext" + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + else + $ECHO "*** The inter-library dependencies that have been dropped here will be" + $ECHO "*** automatically added whenever a program is linked with this library" + $ECHO "*** or is declared to -dlopen it." + + if test "$allow_undefined" = no; then + $ECHO + $ECHO "*** Since this library must not contain undefined symbols," + $ECHO "*** because either the platform does not support them or" + $ECHO "*** it was explicitly requested with -no-undefined," + $ECHO "*** libtool will only create a static version of it." + if test "$build_old_libs" = no; then + oldlibs="$output_objdir/$libname.$libext" + build_libtool_libs=module + build_old_libs=yes + else + build_libtool_libs=no + fi + fi + fi + fi + # Done checking deplibs! + deplibs=$newdeplibs + fi + # Time to change all our "foo.ltframework" stuff back to "-framework foo" + case $host in + *-*-darwin*) + newdeplibs=`$ECHO "X $newdeplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` + new_inherited_linker_flags=`$ECHO "X $new_inherited_linker_flags" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` + deplibs=`$ECHO "X $deplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` + ;; + esac + + # move library search paths that coincide with paths to not yet + # installed libraries to the beginning of the library search list + new_libs= + for path in $notinst_path; do + case " $new_libs " in + *" -L$path/$objdir "*) ;; + *) + case " $deplibs " in + *" -L$path/$objdir "*) + new_libs="$new_libs -L$path/$objdir" ;; + esac + ;; + esac + done + for deplib in $deplibs; do + case $deplib in + -L*) + case " $new_libs " in + *" $deplib "*) ;; + *) new_libs="$new_libs $deplib" ;; + esac + ;; + *) new_libs="$new_libs $deplib" ;; + esac + done + deplibs="$new_libs" + + # All the library-specific variables (install_libdir is set above). + library_names= + old_library= + dlname= + + # Test again, we may have decided not to build it any more + if test "$build_libtool_libs" = yes; then + if test "$hardcode_into_libs" = yes; then + # Hardcode the library paths + hardcode_libdirs= + dep_rpath= + rpath="$finalize_rpath" + test "$mode" != relink && rpath="$compile_rpath$rpath" + for libdir in $rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + dep_rpath="$dep_rpath $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) perm_rpath="$perm_rpath $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + if test -n "$hardcode_libdir_flag_spec_ld"; then + eval dep_rpath=\"$hardcode_libdir_flag_spec_ld\" + else + eval dep_rpath=\"$hardcode_libdir_flag_spec\" + fi + fi + if test -n "$runpath_var" && test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + rpath="$rpath$dir:" + done + eval "$runpath_var='$rpath\$$runpath_var'; export $runpath_var" + fi + test -n "$dep_rpath" && deplibs="$dep_rpath $deplibs" + fi + + shlibpath="$finalize_shlibpath" + test "$mode" != relink && shlibpath="$compile_shlibpath$shlibpath" + if test -n "$shlibpath"; then + eval "$shlibpath_var='$shlibpath\$$shlibpath_var'; export $shlibpath_var" + fi + + # Get the real and link names of the library. + eval shared_ext=\"$shrext_cmds\" + eval library_names=\"$library_names_spec\" + set dummy $library_names + shift + realname="$1" + shift + + if test -n "$soname_spec"; then + eval soname=\"$soname_spec\" + else + soname="$realname" + fi + if test -z "$dlname"; then + dlname=$soname + fi + + lib="$output_objdir/$realname" + linknames= + for link + do + linknames="$linknames $link" + done + + # Use standard objects if they are pic + test -z "$pic_flag" && libobjs=`$ECHO "X$libobjs" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + test "X$libobjs" = "X " && libobjs= + + delfiles= + if test -n "$export_symbols" && test -n "$include_expsyms"; then + $opt_dry_run || cp "$export_symbols" "$output_objdir/$libname.uexp" + export_symbols="$output_objdir/$libname.uexp" + delfiles="$delfiles $export_symbols" + fi + + orig_export_symbols= + case $host_os in + cygwin* | mingw* | cegcc*) + if test -n "$export_symbols" && test -z "$export_symbols_regex"; then + # exporting using user supplied symfile + if test "x`$SED 1q $export_symbols`" != xEXPORTS; then + # and it's NOT already a .def file. Must figure out + # which of the given symbols are data symbols and tag + # them as such. So, trigger use of export_symbols_cmds. + # export_symbols gets reassigned inside the "prepare + # the list of exported symbols" if statement, so the + # include_expsyms logic still works. + orig_export_symbols="$export_symbols" + export_symbols= + always_export_symbols=yes + fi + fi + ;; + esac + + # Prepare the list of exported symbols + if test -z "$export_symbols"; then + if test "$always_export_symbols" = yes || test -n "$export_symbols_regex"; then + func_verbose "generating symbol list for \`$libname.la'" + export_symbols="$output_objdir/$libname.exp" + $opt_dry_run || $RM $export_symbols + cmds=$export_symbols_cmds + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + func_len " $cmd" + len=$func_len_result + if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then + func_show_eval "$cmd" 'exit $?' + skipped_export=false + else + # The command line is too long to execute in one step. + func_verbose "using reloadable object file for export list..." + skipped_export=: + # Break out early, otherwise skipped_export may be + # set to false by a later but shorter cmd. + break + fi + done + IFS="$save_ifs" + if test -n "$export_symbols_regex" && test "X$skipped_export" != "X:"; then + func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' + func_show_eval '$MV "${export_symbols}T" "$export_symbols"' + fi + fi + fi + + if test -n "$export_symbols" && test -n "$include_expsyms"; then + tmp_export_symbols="$export_symbols" + test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" + $opt_dry_run || eval '$ECHO "X$include_expsyms" | $Xsed | $SP2NL >> "$tmp_export_symbols"' + fi + + if test "X$skipped_export" != "X:" && test -n "$orig_export_symbols"; then + # The given exports_symbols file has to be filtered, so filter it. + func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" + # FIXME: $output_objdir/$libname.filter potentially contains lots of + # 's' commands which not all seds can handle. GNU sed should be fine + # though. Also, the filter scales superlinearly with the number of + # global variables. join(1) would be nice here, but unfortunately + # isn't a blessed tool. + $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter + delfiles="$delfiles $export_symbols $output_objdir/$libname.filter" + export_symbols=$output_objdir/$libname.def + $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols + fi + + tmp_deplibs= + for test_deplib in $deplibs; do + case " $convenience " in + *" $test_deplib "*) ;; + *) + tmp_deplibs="$tmp_deplibs $test_deplib" + ;; + esac + done + deplibs="$tmp_deplibs" + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec" && + test "$compiler_needs_object" = yes && + test -z "$libobjs"; then + # extract the archives, so we have objects to list. + # TODO: could optimize this to just extract one archive. + whole_archive_flag_spec= + fi + if test -n "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + test "X$libobjs" = "X " && libobjs= + else + gentop="$output_objdir/${outputname}x" + generated="$generated $gentop" + + func_extract_archives $gentop $convenience + libobjs="$libobjs $func_extract_archives_result" + test "X$libobjs" = "X " && libobjs= + fi + fi + + if test "$thread_safe" = yes && test -n "$thread_safe_flag_spec"; then + eval flag=\"$thread_safe_flag_spec\" + linker_flags="$linker_flags $flag" + fi + + # Make a backup of the uninstalled library when relinking + if test "$mode" = relink; then + $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}U && $MV $realname ${realname}U)' || exit $? + fi + + # Do each of the archive commands. + if test "$module" = yes && test -n "$module_cmds" ; then + if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then + eval test_cmds=\"$module_expsym_cmds\" + cmds=$module_expsym_cmds + else + eval test_cmds=\"$module_cmds\" + cmds=$module_cmds + fi + else + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + eval test_cmds=\"$archive_expsym_cmds\" + cmds=$archive_expsym_cmds + else + eval test_cmds=\"$archive_cmds\" + cmds=$archive_cmds + fi + fi + + if test "X$skipped_export" != "X:" && + func_len " $test_cmds" && + len=$func_len_result && + test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then + : + else + # The command line is too long to link in one step, link piecewise + # or, if using GNU ld and skipped_export is not :, use a linker + # script. + + # Save the value of $output and $libobjs because we want to + # use them later. If we have whole_archive_flag_spec, we + # want to use save_libobjs as it was before + # whole_archive_flag_spec was expanded, because we can't + # assume the linker understands whole_archive_flag_spec. + # This may have to be revisited, in case too many + # convenience libraries get linked in and end up exceeding + # the spec. + if test -z "$convenience" || test -z "$whole_archive_flag_spec"; then + save_libobjs=$libobjs + fi + save_output=$output + output_la=`$ECHO "X$output" | $Xsed -e "$basename"` + + # Clear the reloadable object creation command queue and + # initialize k to one. + test_cmds= + concat_cmds= + objlist= + last_robj= + k=1 + + if test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "$with_gnu_ld" = yes; then + output=${output_objdir}/${output_la}.lnkscript + func_verbose "creating GNU ld script: $output" + $ECHO 'INPUT (' > $output + for obj in $save_libobjs + do + $ECHO "$obj" >> $output + done + $ECHO ')' >> $output + delfiles="$delfiles $output" + elif test -n "$save_libobjs" && test "X$skipped_export" != "X:" && test "X$file_list_spec" != X; then + output=${output_objdir}/${output_la}.lnk + func_verbose "creating linker input file list: $output" + : > $output + set x $save_libobjs + shift + firstobj= + if test "$compiler_needs_object" = yes; then + firstobj="$1 " + shift + fi + for obj + do + $ECHO "$obj" >> $output + done + delfiles="$delfiles $output" + output=$firstobj\"$file_list_spec$output\" + else + if test -n "$save_libobjs"; then + func_verbose "creating reloadable object files..." + output=$output_objdir/$output_la-${k}.$objext + eval test_cmds=\"$reload_cmds\" + func_len " $test_cmds" + len0=$func_len_result + len=$len0 + + # Loop over the list of objects to be linked. + for obj in $save_libobjs + do + func_len " $obj" + func_arith $len + $func_len_result + len=$func_arith_result + if test "X$objlist" = X || + test "$len" -lt "$max_cmd_len"; then + func_append objlist " $obj" + else + # The command $test_cmds is almost too long, add a + # command to the queue. + if test "$k" -eq 1 ; then + # The first file doesn't have a previous command to add. + eval concat_cmds=\"$reload_cmds $objlist $last_robj\" + else + # All subsequent reloadable object files will link in + # the last one created. + eval concat_cmds=\"\$concat_cmds~$reload_cmds $objlist $last_robj~\$RM $last_robj\" + fi + last_robj=$output_objdir/$output_la-${k}.$objext + func_arith $k + 1 + k=$func_arith_result + output=$output_objdir/$output_la-${k}.$objext + objlist=$obj + func_len " $last_robj" + func_arith $len0 + $func_len_result + len=$func_arith_result + fi + done + # Handle the remaining objects by creating one last + # reloadable object file. All subsequent reloadable object + # files will link in the last one created. + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\${concat_cmds}$reload_cmds $objlist $last_robj\" + if test -n "$last_robj"; then + eval concat_cmds=\"\${concat_cmds}~\$RM $last_robj\" + fi + delfiles="$delfiles $output" + + else + output= + fi + + if ${skipped_export-false}; then + func_verbose "generating symbol list for \`$libname.la'" + export_symbols="$output_objdir/$libname.exp" + $opt_dry_run || $RM $export_symbols + libobjs=$output + # Append the command to create the export file. + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\$concat_cmds$export_symbols_cmds\" + if test -n "$last_robj"; then + eval concat_cmds=\"\$concat_cmds~\$RM $last_robj\" + fi + fi + + test -n "$save_libobjs" && + func_verbose "creating a temporary reloadable object file: $output" + + # Loop through the commands generated above and execute them. + save_ifs="$IFS"; IFS='~' + for cmd in $concat_cmds; do + IFS="$save_ifs" + $opt_silent || { + func_quote_for_expand "$cmd" + eval "func_echo $func_quote_for_expand_result" + } + $opt_dry_run || eval "$cmd" || { + lt_exit=$? + + # Restore the uninstalled library and exit + if test "$mode" = relink; then + ( cd "$output_objdir" && \ + $RM "${realname}T" && \ + $MV "${realname}U" "$realname" ) + fi + + exit $lt_exit + } + done + IFS="$save_ifs" + + if test -n "$export_symbols_regex" && ${skipped_export-false}; then + func_show_eval '$EGREP -e "$export_symbols_regex" "$export_symbols" > "${export_symbols}T"' + func_show_eval '$MV "${export_symbols}T" "$export_symbols"' + fi + fi + + if ${skipped_export-false}; then + if test -n "$export_symbols" && test -n "$include_expsyms"; then + tmp_export_symbols="$export_symbols" + test -n "$orig_export_symbols" && tmp_export_symbols="$orig_export_symbols" + $opt_dry_run || eval '$ECHO "X$include_expsyms" | $Xsed | $SP2NL >> "$tmp_export_symbols"' + fi + + if test -n "$orig_export_symbols"; then + # The given exports_symbols file has to be filtered, so filter it. + func_verbose "filter symbol list for \`$libname.la' to tag DATA exports" + # FIXME: $output_objdir/$libname.filter potentially contains lots of + # 's' commands which not all seds can handle. GNU sed should be fine + # though. Also, the filter scales superlinearly with the number of + # global variables. join(1) would be nice here, but unfortunately + # isn't a blessed tool. + $opt_dry_run || $SED -e '/[ ,]DATA/!d;s,\(.*\)\([ \,].*\),s|^\1$|\1\2|,' < $export_symbols > $output_objdir/$libname.filter + delfiles="$delfiles $export_symbols $output_objdir/$libname.filter" + export_symbols=$output_objdir/$libname.def + $opt_dry_run || $SED -f $output_objdir/$libname.filter < $orig_export_symbols > $export_symbols + fi + fi + + libobjs=$output + # Restore the value of output. + output=$save_output + + if test -n "$convenience" && test -n "$whole_archive_flag_spec"; then + eval libobjs=\"\$libobjs $whole_archive_flag_spec\" + test "X$libobjs" = "X " && libobjs= + fi + # Expand the library linking commands again to reset the + # value of $libobjs for piecewise linking. + + # Do each of the archive commands. + if test "$module" = yes && test -n "$module_cmds" ; then + if test -n "$export_symbols" && test -n "$module_expsym_cmds"; then + cmds=$module_expsym_cmds + else + cmds=$module_cmds + fi + else + if test -n "$export_symbols" && test -n "$archive_expsym_cmds"; then + cmds=$archive_expsym_cmds + else + cmds=$archive_cmds + fi + fi + fi + + if test -n "$delfiles"; then + # Append the command to remove temporary files to $cmds. + eval cmds=\"\$cmds~\$RM $delfiles\" + fi + + # Add any objects from preloaded convenience libraries + if test -n "$dlprefiles"; then + gentop="$output_objdir/${outputname}x" + generated="$generated $gentop" + + func_extract_archives $gentop $dlprefiles + libobjs="$libobjs $func_extract_archives_result" + test "X$libobjs" = "X " && libobjs= + fi + + save_ifs="$IFS"; IFS='~' + for cmd in $cmds; do + IFS="$save_ifs" + eval cmd=\"$cmd\" + $opt_silent || { + func_quote_for_expand "$cmd" + eval "func_echo $func_quote_for_expand_result" + } + $opt_dry_run || eval "$cmd" || { + lt_exit=$? + + # Restore the uninstalled library and exit + if test "$mode" = relink; then + ( cd "$output_objdir" && \ + $RM "${realname}T" && \ + $MV "${realname}U" "$realname" ) + fi + + exit $lt_exit + } + done + IFS="$save_ifs" + + # Restore the uninstalled library and exit + if test "$mode" = relink; then + $opt_dry_run || eval '(cd $output_objdir && $RM ${realname}T && $MV $realname ${realname}T && $MV ${realname}U $realname)' || exit $? + + if test -n "$convenience"; then + if test -z "$whole_archive_flag_spec"; then + func_show_eval '${RM}r "$gentop"' + fi + fi + + exit $EXIT_SUCCESS + fi + + # Create links to the real library. + for linkname in $linknames; do + if test "$realname" != "$linkname"; then + func_show_eval '(cd "$output_objdir" && $RM "$linkname" && $LN_S "$realname" "$linkname")' 'exit $?' + fi + done + + # If -module or -export-dynamic was specified, set the dlname. + if test "$module" = yes || test "$export_dynamic" = yes; then + # On all known operating systems, these are identical. + dlname="$soname" + fi + fi + ;; + + obj) + if test -n "$dlfiles$dlprefiles" || test "$dlself" != no; then + func_warning "\`-dlopen' is ignored for objects" + fi + + case " $deplibs" in + *\ -l* | *\ -L*) + func_warning "\`-l' and \`-L' are ignored for objects" ;; + esac + + test -n "$rpath" && \ + func_warning "\`-rpath' is ignored for objects" + + test -n "$xrpath" && \ + func_warning "\`-R' is ignored for objects" + + test -n "$vinfo" && \ + func_warning "\`-version-info' is ignored for objects" + + test -n "$release" && \ + func_warning "\`-release' is ignored for objects" + + case $output in + *.lo) + test -n "$objs$old_deplibs" && \ + func_fatal_error "cannot build library object \`$output' from non-libtool objects" + + libobj=$output + func_lo2o "$libobj" + obj=$func_lo2o_result + ;; + *) + libobj= + obj="$output" + ;; + esac + + # Delete the old objects. + $opt_dry_run || $RM $obj $libobj + + # Objects from convenience libraries. This assumes + # single-version convenience libraries. Whenever we create + # different ones for PIC/non-PIC, this we'll have to duplicate + # the extraction. + reload_conv_objs= + gentop= + # reload_cmds runs $LD directly, so let us get rid of + # -Wl from whole_archive_flag_spec and hope we can get by with + # turning comma into space.. + wl= + + if test -n "$convenience"; then + if test -n "$whole_archive_flag_spec"; then + eval tmp_whole_archive_flags=\"$whole_archive_flag_spec\" + reload_conv_objs=$reload_objs\ `$ECHO "X$tmp_whole_archive_flags" | $Xsed -e 's|,| |g'` + else + gentop="$output_objdir/${obj}x" + generated="$generated $gentop" + + func_extract_archives $gentop $convenience + reload_conv_objs="$reload_objs $func_extract_archives_result" + fi + fi + + # Create the old-style object. + reload_objs="$objs$old_deplibs "`$ECHO "X$libobjs" | $SP2NL | $Xsed -e '/\.'${libext}$'/d' -e '/\.lib$/d' -e "$lo2o" | $NL2SP`" $reload_conv_objs" ### testsuite: skip nested quoting test + + output="$obj" + func_execute_cmds "$reload_cmds" 'exit $?' + + # Exit if we aren't doing a library object file. + if test -z "$libobj"; then + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + exit $EXIT_SUCCESS + fi + + if test "$build_libtool_libs" != yes; then + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + # Create an invalid libtool object if no PIC, so that we don't + # accidentally link it into a program. + # $show "echo timestamp > $libobj" + # $opt_dry_run || eval "echo timestamp > $libobj" || exit $? + exit $EXIT_SUCCESS + fi + + if test -n "$pic_flag" || test "$pic_mode" != default; then + # Only do commands if we really have different PIC objects. + reload_objs="$libobjs $reload_conv_objs" + output="$libobj" + func_execute_cmds "$reload_cmds" 'exit $?' + fi + + if test -n "$gentop"; then + func_show_eval '${RM}r "$gentop"' + fi + + exit $EXIT_SUCCESS + ;; + + prog) + case $host in + *cygwin*) func_stripname '' '.exe' "$output" + output=$func_stripname_result.exe;; + esac + test -n "$vinfo" && \ + func_warning "\`-version-info' is ignored for programs" + + test -n "$release" && \ + func_warning "\`-release' is ignored for programs" + + test "$preload" = yes \ + && test "$dlopen_support" = unknown \ + && test "$dlopen_self" = unknown \ + && test "$dlopen_self_static" = unknown && \ + func_warning "\`LT_INIT([dlopen])' not used. Assuming no dlopen support." + + case $host in + *-*-rhapsody* | *-*-darwin1.[012]) + # On Rhapsody replace the C library is the System framework + compile_deplibs=`$ECHO "X $compile_deplibs" | $Xsed -e 's/ -lc / System.ltframework /'` + finalize_deplibs=`$ECHO "X $finalize_deplibs" | $Xsed -e 's/ -lc / System.ltframework /'` + ;; + esac + + case $host in + *-*-darwin*) + # Don't allow lazy linking, it breaks C++ global constructors + # But is supposedly fixed on 10.4 or later (yay!). + if test "$tagname" = CXX ; then + case ${MACOSX_DEPLOYMENT_TARGET-10.0} in + 10.[0123]) + compile_command="$compile_command ${wl}-bind_at_load" + finalize_command="$finalize_command ${wl}-bind_at_load" + ;; + esac + fi + # Time to change all our "foo.ltframework" stuff back to "-framework foo" + compile_deplibs=`$ECHO "X $compile_deplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` + finalize_deplibs=`$ECHO "X $finalize_deplibs" | $Xsed -e 's% \([^ $]*\).ltframework% -framework \1%g'` + ;; + esac + + + # move library search paths that coincide with paths to not yet + # installed libraries to the beginning of the library search list + new_libs= + for path in $notinst_path; do + case " $new_libs " in + *" -L$path/$objdir "*) ;; + *) + case " $compile_deplibs " in + *" -L$path/$objdir "*) + new_libs="$new_libs -L$path/$objdir" ;; + esac + ;; + esac + done + for deplib in $compile_deplibs; do + case $deplib in + -L*) + case " $new_libs " in + *" $deplib "*) ;; + *) new_libs="$new_libs $deplib" ;; + esac + ;; + *) new_libs="$new_libs $deplib" ;; + esac + done + compile_deplibs="$new_libs" + + + compile_command="$compile_command $compile_deplibs" + finalize_command="$finalize_command $finalize_deplibs" + + if test -n "$rpath$xrpath"; then + # If the user specified any rpath flags, then add them. + for libdir in $rpath $xrpath; do + # This is the magic to use -rpath. + case "$finalize_rpath " in + *" $libdir "*) ;; + *) finalize_rpath="$finalize_rpath $libdir" ;; + esac + done + fi + + # Now hardcode the library paths + rpath= + hardcode_libdirs= + for libdir in $compile_rpath $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + rpath="$rpath $flag" + fi + elif test -n "$runpath_var"; then + case "$perm_rpath " in + *" $libdir "*) ;; + *) perm_rpath="$perm_rpath $libdir" ;; + esac + fi + case $host in + *-*-cygwin* | *-*-mingw* | *-*-pw32* | *-*-os2* | *-cegcc*) + testbindir=`${ECHO} "$libdir" | ${SED} -e 's*/lib$*/bin*'` + case :$dllsearchpath: in + *":$libdir:"*) ;; + ::) dllsearchpath=$libdir;; + *) dllsearchpath="$dllsearchpath:$libdir";; + esac + case :$dllsearchpath: in + *":$testbindir:"*) ;; + ::) dllsearchpath=$testbindir;; + *) dllsearchpath="$dllsearchpath:$testbindir";; + esac + ;; + esac + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + compile_rpath="$rpath" + + rpath= + hardcode_libdirs= + for libdir in $finalize_rpath; do + if test -n "$hardcode_libdir_flag_spec"; then + if test -n "$hardcode_libdir_separator"; then + if test -z "$hardcode_libdirs"; then + hardcode_libdirs="$libdir" + else + # Just accumulate the unique libdirs. + case $hardcode_libdir_separator$hardcode_libdirs$hardcode_libdir_separator in + *"$hardcode_libdir_separator$libdir$hardcode_libdir_separator"*) + ;; + *) + hardcode_libdirs="$hardcode_libdirs$hardcode_libdir_separator$libdir" + ;; + esac + fi + else + eval flag=\"$hardcode_libdir_flag_spec\" + rpath="$rpath $flag" + fi + elif test -n "$runpath_var"; then + case "$finalize_perm_rpath " in + *" $libdir "*) ;; + *) finalize_perm_rpath="$finalize_perm_rpath $libdir" ;; + esac + fi + done + # Substitute the hardcoded libdirs into the rpath. + if test -n "$hardcode_libdir_separator" && + test -n "$hardcode_libdirs"; then + libdir="$hardcode_libdirs" + eval rpath=\" $hardcode_libdir_flag_spec\" + fi + finalize_rpath="$rpath" + + if test -n "$libobjs" && test "$build_old_libs" = yes; then + # Transform all the library objects into standard objects. + compile_command=`$ECHO "X$compile_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + finalize_command=`$ECHO "X$finalize_command" | $SP2NL | $Xsed -e "$lo2o" | $NL2SP` + fi + + func_generate_dlsyms "$outputname" "@PROGRAM@" "no" + + # template prelinking step + if test -n "$prelink_cmds"; then + func_execute_cmds "$prelink_cmds" 'exit $?' + fi + + wrappers_required=yes + case $host in + *cygwin* | *mingw* ) + if test "$build_libtool_libs" != yes; then + wrappers_required=no + fi + ;; + *cegcc) + # Disable wrappers for cegcc, we are cross compiling anyway. + wrappers_required=no + ;; + *) + if test "$need_relink" = no || test "$build_libtool_libs" != yes; then + wrappers_required=no + fi + ;; + esac + if test "$wrappers_required" = no; then + # Replace the output file specification. + compile_command=`$ECHO "X$compile_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'` + link_command="$compile_command$compile_rpath" + + # We have no uninstalled library dependencies, so finalize right now. + exit_status=0 + func_show_eval "$link_command" 'exit_status=$?' + + # Delete the generated files. + if test -f "$output_objdir/${outputname}S.${objext}"; then + func_show_eval '$RM "$output_objdir/${outputname}S.${objext}"' + fi + + exit $exit_status + fi + + if test -n "$compile_shlibpath$finalize_shlibpath"; then + compile_command="$shlibpath_var=\"$compile_shlibpath$finalize_shlibpath\$$shlibpath_var\" $compile_command" + fi + if test -n "$finalize_shlibpath"; then + finalize_command="$shlibpath_var=\"$finalize_shlibpath\$$shlibpath_var\" $finalize_command" + fi + + compile_var= + finalize_var= + if test -n "$runpath_var"; then + if test -n "$perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $perm_rpath; do + rpath="$rpath$dir:" + done + compile_var="$runpath_var=\"$rpath\$$runpath_var\" " + fi + if test -n "$finalize_perm_rpath"; then + # We should set the runpath_var. + rpath= + for dir in $finalize_perm_rpath; do + rpath="$rpath$dir:" + done + finalize_var="$runpath_var=\"$rpath\$$runpath_var\" " + fi + fi + + if test "$no_install" = yes; then + # We don't need to create a wrapper script. + link_command="$compile_var$compile_command$compile_rpath" + # Replace the output file specification. + link_command=`$ECHO "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output"'%g'` + # Delete the old output file. + $opt_dry_run || $RM $output + # Link the executable and exit + func_show_eval "$link_command" 'exit $?' + exit $EXIT_SUCCESS + fi + + if test "$hardcode_action" = relink; then + # Fast installation is not supported + link_command="$compile_var$compile_command$compile_rpath" + relink_command="$finalize_var$finalize_command$finalize_rpath" + + func_warning "this platform does not like uninstalled shared libraries" + func_warning "\`$output' will be relinked during installation" + else + if test "$fast_install" != no; then + link_command="$finalize_var$compile_command$finalize_rpath" + if test "$fast_install" = yes; then + relink_command=`$ECHO "X$compile_var$compile_command$compile_rpath" | $Xsed -e 's%@OUTPUT@%\$progdir/\$file%g'` + else + # fast_install is set to needless + relink_command= + fi + else + link_command="$compile_var$compile_command$compile_rpath" + relink_command="$finalize_var$finalize_command$finalize_rpath" + fi + fi + + # Replace the output file specification. + link_command=`$ECHO "X$link_command" | $Xsed -e 's%@OUTPUT@%'"$output_objdir/$outputname"'%g'` + + # Delete the old output files. + $opt_dry_run || $RM $output $output_objdir/$outputname $output_objdir/lt-$outputname + + func_show_eval "$link_command" 'exit $?' + + # Now create the wrapper script. + func_verbose "creating $output" + + # Quote the relink command for shipping. + if test -n "$relink_command"; then + # Preserve any variables that may affect compiler behavior + for var in $variables_saved_for_relink; do + if eval test -z \"\${$var+set}\"; then + relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" + elif eval var_value=\$$var; test -z "$var_value"; then + relink_command="$var=; export $var; $relink_command" + else + func_quote_for_eval "$var_value" + relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" + fi + done + relink_command="(cd `pwd`; $relink_command)" + relink_command=`$ECHO "X$relink_command" | $Xsed -e "$sed_quote_subst"` + fi + + # Quote $ECHO for shipping. + if test "X$ECHO" = "X$SHELL $progpath --fallback-echo"; then + case $progpath in + [\\/]* | [A-Za-z]:[\\/]*) qecho="$SHELL $progpath --fallback-echo";; + *) qecho="$SHELL `pwd`/$progpath --fallback-echo";; + esac + qecho=`$ECHO "X$qecho" | $Xsed -e "$sed_quote_subst"` + else + qecho=`$ECHO "X$ECHO" | $Xsed -e "$sed_quote_subst"` + fi + + # Only actually do things if not in dry run mode. + $opt_dry_run || { + # win32 will think the script is a binary if it has + # a .exe suffix, so we strip it off here. + case $output in + *.exe) func_stripname '' '.exe' "$output" + output=$func_stripname_result ;; + esac + # test for cygwin because mv fails w/o .exe extensions + case $host in + *cygwin*) + exeext=.exe + func_stripname '' '.exe' "$outputname" + outputname=$func_stripname_result ;; + *) exeext= ;; + esac + case $host in + *cygwin* | *mingw* ) + func_dirname_and_basename "$output" "" "." + output_name=$func_basename_result + output_path=$func_dirname_result + cwrappersource="$output_path/$objdir/lt-$output_name.c" + cwrapper="$output_path/$output_name.exe" + $RM $cwrappersource $cwrapper + trap "$RM $cwrappersource $cwrapper; exit $EXIT_FAILURE" 1 2 15 + + func_emit_cwrapperexe_src > $cwrappersource + + # The wrapper executable is built using the $host compiler, + # because it contains $host paths and files. If cross- + # compiling, it, like the target executable, must be + # executed on the $host or under an emulation environment. + $opt_dry_run || { + $LTCC $LTCFLAGS -o $cwrapper $cwrappersource + $STRIP $cwrapper + } + + # Now, create the wrapper script for func_source use: + func_ltwrapper_scriptname $cwrapper + $RM $func_ltwrapper_scriptname_result + trap "$RM $func_ltwrapper_scriptname_result; exit $EXIT_FAILURE" 1 2 15 + $opt_dry_run || { + # note: this script will not be executed, so do not chmod. + if test "x$build" = "x$host" ; then + $cwrapper --lt-dump-script > $func_ltwrapper_scriptname_result + else + func_emit_wrapper no > $func_ltwrapper_scriptname_result + fi + } + ;; + * ) + $RM $output + trap "$RM $output; exit $EXIT_FAILURE" 1 2 15 + + func_emit_wrapper no > $output + chmod +x $output + ;; + esac + } + exit $EXIT_SUCCESS + ;; + esac + + # See if we need to build an old-fashioned archive. + for oldlib in $oldlibs; do + + if test "$build_libtool_libs" = convenience; then + oldobjs="$libobjs_save $symfileobj" + addlibs="$convenience" + build_libtool_libs=no + else + if test "$build_libtool_libs" = module; then + oldobjs="$libobjs_save" + build_libtool_libs=no + else + oldobjs="$old_deplibs $non_pic_objects" + if test "$preload" = yes && test -f "$symfileobj"; then + oldobjs="$oldobjs $symfileobj" + fi + fi + addlibs="$old_convenience" + fi + + if test -n "$addlibs"; then + gentop="$output_objdir/${outputname}x" + generated="$generated $gentop" + + func_extract_archives $gentop $addlibs + oldobjs="$oldobjs $func_extract_archives_result" + fi + + # Do each command in the archive commands. + if test -n "$old_archive_from_new_cmds" && test "$build_libtool_libs" = yes; then + cmds=$old_archive_from_new_cmds + else + + # Add any objects from preloaded convenience libraries + if test -n "$dlprefiles"; then + gentop="$output_objdir/${outputname}x" + generated="$generated $gentop" + + func_extract_archives $gentop $dlprefiles + oldobjs="$oldobjs $func_extract_archives_result" + fi + + # POSIX demands no paths to be encoded in archives. We have + # to avoid creating archives with duplicate basenames if we + # might have to extract them afterwards, e.g., when creating a + # static archive out of a convenience library, or when linking + # the entirety of a libtool archive into another (currently + # not supported by libtool). + if (for obj in $oldobjs + do + func_basename "$obj" + $ECHO "$func_basename_result" + done | sort | sort -uc >/dev/null 2>&1); then + : + else + $ECHO "copying selected object files to avoid basename conflicts..." + gentop="$output_objdir/${outputname}x" + generated="$generated $gentop" + func_mkdir_p "$gentop" + save_oldobjs=$oldobjs + oldobjs= + counter=1 + for obj in $save_oldobjs + do + func_basename "$obj" + objbase="$func_basename_result" + case " $oldobjs " in + " ") oldobjs=$obj ;; + *[\ /]"$objbase "*) + while :; do + # Make sure we don't pick an alternate name that also + # overlaps. + newobj=lt$counter-$objbase + func_arith $counter + 1 + counter=$func_arith_result + case " $oldobjs " in + *[\ /]"$newobj "*) ;; + *) if test ! -f "$gentop/$newobj"; then break; fi ;; + esac + done + func_show_eval "ln $obj $gentop/$newobj || cp $obj $gentop/$newobj" + oldobjs="$oldobjs $gentop/$newobj" + ;; + *) oldobjs="$oldobjs $obj" ;; + esac + done + fi + eval cmds=\"$old_archive_cmds\" + + func_len " $cmds" + len=$func_len_result + if test "$len" -lt "$max_cmd_len" || test "$max_cmd_len" -le -1; then + cmds=$old_archive_cmds + else + # the command line is too long to link in one step, link in parts + func_verbose "using piecewise archive linking..." + save_RANLIB=$RANLIB + RANLIB=: + objlist= + concat_cmds= + save_oldobjs=$oldobjs + oldobjs= + # Is there a better way of finding the last object in the list? + for obj in $save_oldobjs + do + last_oldobj=$obj + done + eval test_cmds=\"$old_archive_cmds\" + func_len " $test_cmds" + len0=$func_len_result + len=$len0 + for obj in $save_oldobjs + do + func_len " $obj" + func_arith $len + $func_len_result + len=$func_arith_result + func_append objlist " $obj" + if test "$len" -lt "$max_cmd_len"; then + : + else + # the above command should be used before it gets too long + oldobjs=$objlist + if test "$obj" = "$last_oldobj" ; then + RANLIB=$save_RANLIB + fi + test -z "$concat_cmds" || concat_cmds=$concat_cmds~ + eval concat_cmds=\"\${concat_cmds}$old_archive_cmds\" + objlist= + len=$len0 + fi + done + RANLIB=$save_RANLIB + oldobjs=$objlist + if test "X$oldobjs" = "X" ; then + eval cmds=\"\$concat_cmds\" + else + eval cmds=\"\$concat_cmds~\$old_archive_cmds\" + fi + fi + fi + func_execute_cmds "$cmds" 'exit $?' + done + + test -n "$generated" && \ + func_show_eval "${RM}r$generated" + + # Now create the libtool archive. + case $output in + *.la) + old_library= + test "$build_old_libs" = yes && old_library="$libname.$libext" + func_verbose "creating $output" + + # Preserve any variables that may affect compiler behavior + for var in $variables_saved_for_relink; do + if eval test -z \"\${$var+set}\"; then + relink_command="{ test -z \"\${$var+set}\" || $lt_unset $var || { $var=; export $var; }; }; $relink_command" + elif eval var_value=\$$var; test -z "$var_value"; then + relink_command="$var=; export $var; $relink_command" + else + func_quote_for_eval "$var_value" + relink_command="$var=$func_quote_for_eval_result; export $var; $relink_command" + fi + done + # Quote the link command for shipping. + relink_command="(cd `pwd`; $SHELL $progpath $preserve_args --mode=relink $libtool_args @inst_prefix_dir@)" + relink_command=`$ECHO "X$relink_command" | $Xsed -e "$sed_quote_subst"` + if test "$hardcode_automatic" = yes ; then + relink_command= + fi + + # Only create the output if not a dry run. + $opt_dry_run || { + for installed in no yes; do + if test "$installed" = yes; then + if test -z "$install_libdir"; then + break + fi + output="$output_objdir/$outputname"i + # Replace all uninstalled libtool libraries with the installed ones + newdependency_libs= + for deplib in $dependency_libs; do + case $deplib in + *.la) + func_basename "$deplib" + name="$func_basename_result" + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $deplib` + test -z "$libdir" && \ + func_fatal_error "\`$deplib' is not a valid libtool archive" + newdependency_libs="$newdependency_libs $libdir/$name" + ;; + *) newdependency_libs="$newdependency_libs $deplib" ;; + esac + done + dependency_libs="$newdependency_libs" + newdlfiles= + + for lib in $dlfiles; do + case $lib in + *.la) + func_basename "$lib" + name="$func_basename_result" + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + test -z "$libdir" && \ + func_fatal_error "\`$lib' is not a valid libtool archive" + newdlfiles="$newdlfiles $libdir/$name" + ;; + *) newdlfiles="$newdlfiles $lib" ;; + esac + done + dlfiles="$newdlfiles" + newdlprefiles= + for lib in $dlprefiles; do + case $lib in + *.la) + # Only pass preopened files to the pseudo-archive (for + # eventual linking with the app. that links it) if we + # didn't already link the preopened objects directly into + # the library: + func_basename "$lib" + name="$func_basename_result" + eval libdir=`${SED} -n -e 's/^libdir=\(.*\)$/\1/p' $lib` + test -z "$libdir" && \ + func_fatal_error "\`$lib' is not a valid libtool archive" + newdlprefiles="$newdlprefiles $libdir/$name" + ;; + esac + done + dlprefiles="$newdlprefiles" + else + newdlfiles= + for lib in $dlfiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; + *) abs=`pwd`"/$lib" ;; + esac + newdlfiles="$newdlfiles $abs" + done + dlfiles="$newdlfiles" + newdlprefiles= + for lib in $dlprefiles; do + case $lib in + [\\/]* | [A-Za-z]:[\\/]*) abs="$lib" ;; + *) abs=`pwd`"/$lib" ;; + esac + newdlprefiles="$newdlprefiles $abs" + done + dlprefiles="$newdlprefiles" + fi + $RM $output + # place dlname in correct position for cygwin + tdlname=$dlname + case $host,$output,$installed,$module,$dlname in + *cygwin*,*lai,yes,no,*.dll | *mingw*,*lai,yes,no,*.dll | *cegcc*,*lai,yes,no,*.dll) tdlname=../bin/$dlname ;; + esac + $ECHO > $output "\ +# $outputname - a libtool library file +# Generated by $PROGRAM (GNU $PACKAGE$TIMESTAMP) $VERSION +# +# Please DO NOT delete this file! +# It is necessary for linking the library. + +# The name that we can dlopen(3). +dlname='$tdlname' + +# Names of this library. +library_names='$library_names' + +# The name of the static archive. +old_library='$old_library' + +# Linker flags that can not go in dependency_libs. +inherited_linker_flags='$new_inherited_linker_flags' + +# Libraries that this one depends upon. +dependency_libs='$dependency_libs' + +# Names of additional weak libraries provided by this library +weak_library_names='$weak_libs' + +# Version information for $libname. +current=$current +age=$age +revision=$revision + +# Is this an already installed library? +installed=$installed + +# Should we warn about portability when linking against -modules? +shouldnotlink=$module + +# Files to dlopen/dlpreopen +dlopen='$dlfiles' +dlpreopen='$dlprefiles' + +# Directory that this library needs to be installed in: +libdir='$install_libdir'" + if test "$installed" = no && test "$need_relink" = yes; then + $ECHO >> $output "\ +relink_command=\"$relink_command\"" + fi + done + } + + # Do a symbolic link so that the libtool archive can be found in + # LD_LIBRARY_PATH before the program is installed. + func_show_eval '( cd "$output_objdir" && $RM "$outputname" && $LN_S "../$outputname" "$outputname" )' 'exit $?' + ;; + esac + exit $EXIT_SUCCESS +} + +{ test "$mode" = link || test "$mode" = relink; } && + func_mode_link ${1+"$@"} + + +# func_mode_uninstall arg... +func_mode_uninstall () +{ + $opt_debug + RM="$nonopt" + files= + rmforce= + exit_status=0 + + # This variable tells wrapper scripts just to set variables rather + # than running their programs. + libtool_install_magic="$magic" + + for arg + do + case $arg in + -f) RM="$RM $arg"; rmforce=yes ;; + -*) RM="$RM $arg" ;; + *) files="$files $arg" ;; + esac + done + + test -z "$RM" && \ + func_fatal_help "you must specify an RM program" + + rmdirs= + + origobjdir="$objdir" + for file in $files; do + func_dirname "$file" "" "." + dir="$func_dirname_result" + if test "X$dir" = X.; then + objdir="$origobjdir" + else + objdir="$dir/$origobjdir" + fi + func_basename "$file" + name="$func_basename_result" + test "$mode" = uninstall && objdir="$dir" + + # Remember objdir for removal later, being careful to avoid duplicates + if test "$mode" = clean; then + case " $rmdirs " in + *" $objdir "*) ;; + *) rmdirs="$rmdirs $objdir" ;; + esac + fi + + # Don't error if the file doesn't exist and rm -f was used. + if { test -L "$file"; } >/dev/null 2>&1 || + { test -h "$file"; } >/dev/null 2>&1 || + test -f "$file"; then + : + elif test -d "$file"; then + exit_status=1 + continue + elif test "$rmforce" = yes; then + continue + fi + + rmfiles="$file" + + case $name in + *.la) + # Possibly a libtool archive, so verify it. + if func_lalib_p "$file"; then + func_source $dir/$name + + # Delete the libtool libraries and symlinks. + for n in $library_names; do + rmfiles="$rmfiles $objdir/$n" + done + test -n "$old_library" && rmfiles="$rmfiles $objdir/$old_library" + + case "$mode" in + clean) + case " $library_names " in + # " " in the beginning catches empty $dlname + *" $dlname "*) ;; + *) rmfiles="$rmfiles $objdir/$dlname" ;; + esac + test -n "$libdir" && rmfiles="$rmfiles $objdir/$name $objdir/${name}i" + ;; + uninstall) + if test -n "$library_names"; then + # Do each command in the postuninstall commands. + func_execute_cmds "$postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' + fi + + if test -n "$old_library"; then + # Do each command in the old_postuninstall commands. + func_execute_cmds "$old_postuninstall_cmds" 'test "$rmforce" = yes || exit_status=1' + fi + # FIXME: should reinstall the best remaining shared library. + ;; + esac + fi + ;; + + *.lo) + # Possibly a libtool object, so verify it. + if func_lalib_p "$file"; then + + # Read the .lo file + func_source $dir/$name + + # Add PIC object to the list of files to remove. + if test -n "$pic_object" && + test "$pic_object" != none; then + rmfiles="$rmfiles $dir/$pic_object" + fi + + # Add non-PIC object to the list of files to remove. + if test -n "$non_pic_object" && + test "$non_pic_object" != none; then + rmfiles="$rmfiles $dir/$non_pic_object" + fi + fi + ;; + + *) + if test "$mode" = clean ; then + noexename=$name + case $file in + *.exe) + func_stripname '' '.exe' "$file" + file=$func_stripname_result + func_stripname '' '.exe' "$name" + noexename=$func_stripname_result + # $file with .exe has already been added to rmfiles, + # add $file without .exe + rmfiles="$rmfiles $file" + ;; + esac + # Do a test to see if this is a libtool program. + if func_ltwrapper_p "$file"; then + if func_ltwrapper_executable_p "$file"; then + func_ltwrapper_scriptname "$file" + relink_command= + func_source $func_ltwrapper_scriptname_result + rmfiles="$rmfiles $func_ltwrapper_scriptname_result" + else + relink_command= + func_source $dir/$noexename + fi + + # note $name still contains .exe if it was in $file originally + # as does the version of $file that was added into $rmfiles + rmfiles="$rmfiles $objdir/$name $objdir/${name}S.${objext}" + if test "$fast_install" = yes && test -n "$relink_command"; then + rmfiles="$rmfiles $objdir/lt-$name" + fi + if test "X$noexename" != "X$name" ; then + rmfiles="$rmfiles $objdir/lt-${noexename}.c" + fi + fi + fi + ;; + esac + func_show_eval "$RM $rmfiles" 'exit_status=1' + done + objdir="$origobjdir" + + # Try to remove the ${objdir}s in the directories where we deleted files + for dir in $rmdirs; do + if test -d "$dir"; then + func_show_eval "rmdir $dir >/dev/null 2>&1" + fi + done + + exit $exit_status +} + +{ test "$mode" = uninstall || test "$mode" = clean; } && + func_mode_uninstall ${1+"$@"} + +test -z "$mode" && { + help="$generic_help" + func_fatal_help "you must specify a MODE" +} + +test -z "$exec_cmd" && \ + func_fatal_help "invalid operation mode \`$mode'" + +if test -n "$exec_cmd"; then + eval exec "$exec_cmd" + exit $EXIT_FAILURE +fi + +exit $exit_status + + +# The TAGs below are defined such that we never get into a situation +# in which we disable both kinds of libraries. Given conflicting +# choices, we go for a static library, that is the most portable, +# since we can't tell whether shared libraries were disabled because +# the user asked for that or because the platform doesn't support +# them. This is particularly important on AIX, because we don't +# support having both static and shared libraries enabled at the same +# time on that platform, so we default to a shared-only configuration. +# If a disable-shared tag is given, we'll fallback to a static-only +# configuration. But we'll never go from static-only to shared-only. + +# ### BEGIN LIBTOOL TAG CONFIG: disable-shared +build_libtool_libs=no +build_old_libs=yes +# ### END LIBTOOL TAG CONFIG: disable-shared + +# ### BEGIN LIBTOOL TAG CONFIG: disable-static +build_old_libs=`case $build_libtool_libs in yes) echo no;; *) echo yes;; esac` +# ### END LIBTOOL TAG CONFIG: disable-static + +# Local Variables: +# mode:shell-script +# sh-indentation:2 +# End: +# vi:sw=2 + diff --git a/shared/sentry/external/breakpad/autotools/missing b/shared/sentry/external/breakpad/autotools/missing new file mode 100755 index 000000000..f62bbae30 --- /dev/null +++ b/shared/sentry/external/breakpad/autotools/missing @@ -0,0 +1,215 @@ +#! /bin/sh +# Common wrapper for a few potentially missing GNU programs. + +scriptversion=2013-10-28.13; # UTC + +# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# Originally written by Fran,cois Pinard , 1996. + +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. + +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +if test $# -eq 0; then + echo 1>&2 "Try '$0 --help' for more information" + exit 1 +fi + +case $1 in + + --is-lightweight) + # Used by our autoconf macros to check whether the available missing + # script is modern enough. + exit 0 + ;; + + --run) + # Back-compat with the calling convention used by older automake. + shift + ;; + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Run 'PROGRAM [ARGUMENT]...', returning a proper advice when this fails due +to PROGRAM being missing or too old. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + +Supported PROGRAM values: + aclocal autoconf autoheader autom4te automake makeinfo + bison yacc flex lex help2man + +Version suffixes to PROGRAM as well as the prefixes 'gnu-', 'gnu', and +'g' are ignored when checking the name. + +Send bug reports to ." + exit $? + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing $scriptversion (GNU Automake)" + exit $? + ;; + + -*) + echo 1>&2 "$0: unknown '$1' option" + echo 1>&2 "Try '$0 --help' for more information" + exit 1 + ;; + +esac + +# Run the given program, remember its exit status. +"$@"; st=$? + +# If it succeeded, we are done. +test $st -eq 0 && exit 0 + +# Also exit now if we it failed (or wasn't found), and '--version' was +# passed; such an option is passed most likely to detect whether the +# program is present and works. +case $2 in --version|--help) exit $st;; esac + +# Exit code 63 means version mismatch. This often happens when the user +# tries to use an ancient version of a tool on a file that requires a +# minimum version. +if test $st -eq 63; then + msg="probably too old" +elif test $st -eq 127; then + # Program was missing. + msg="missing on your system" +else + # Program was found and executed, but failed. Give up. + exit $st +fi + +perl_URL=http://www.perl.org/ +flex_URL=http://flex.sourceforge.net/ +gnu_software_URL=http://www.gnu.org/software + +program_details () +{ + case $1 in + aclocal|automake) + echo "The '$1' program is part of the GNU Automake package:" + echo "<$gnu_software_URL/automake>" + echo "It also requires GNU Autoconf, GNU m4 and Perl in order to run:" + echo "<$gnu_software_URL/autoconf>" + echo "<$gnu_software_URL/m4/>" + echo "<$perl_URL>" + ;; + autoconf|autom4te|autoheader) + echo "The '$1' program is part of the GNU Autoconf package:" + echo "<$gnu_software_URL/autoconf/>" + echo "It also requires GNU m4 and Perl in order to run:" + echo "<$gnu_software_URL/m4/>" + echo "<$perl_URL>" + ;; + esac +} + +give_advice () +{ + # Normalize program name to check for. + normalized_program=`echo "$1" | sed ' + s/^gnu-//; t + s/^gnu//; t + s/^g//; t'` + + printf '%s\n' "'$1' is $msg." + + configure_deps="'configure.ac' or m4 files included by 'configure.ac'" + case $normalized_program in + autoconf*) + echo "You should only need it if you modified 'configure.ac'," + echo "or m4 files included by it." + program_details 'autoconf' + ;; + autoheader*) + echo "You should only need it if you modified 'acconfig.h' or" + echo "$configure_deps." + program_details 'autoheader' + ;; + automake*) + echo "You should only need it if you modified 'Makefile.am' or" + echo "$configure_deps." + program_details 'automake' + ;; + aclocal*) + echo "You should only need it if you modified 'acinclude.m4' or" + echo "$configure_deps." + program_details 'aclocal' + ;; + autom4te*) + echo "You might have modified some maintainer files that require" + echo "the 'autom4te' program to be rebuilt." + program_details 'autom4te' + ;; + bison*|yacc*) + echo "You should only need it if you modified a '.y' file." + echo "You may want to install the GNU Bison package:" + echo "<$gnu_software_URL/bison/>" + ;; + lex*|flex*) + echo "You should only need it if you modified a '.l' file." + echo "You may want to install the Fast Lexical Analyzer package:" + echo "<$flex_URL>" + ;; + help2man*) + echo "You should only need it if you modified a dependency" \ + "of a man page." + echo "You may want to install the GNU Help2man package:" + echo "<$gnu_software_URL/help2man/>" + ;; + makeinfo*) + echo "You should only need it if you modified a '.texi' file, or" + echo "any other file indirectly affecting the aspect of the manual." + echo "You might want to install the Texinfo package:" + echo "<$gnu_software_URL/texinfo/>" + echo "The spurious makeinfo call might also be the consequence of" + echo "using a buggy 'make' (AIX, DU, IRIX), in which case you might" + echo "want to install GNU make:" + echo "<$gnu_software_URL/make/>" + ;; + *) + echo "You might have modified some files without having the proper" + echo "tools for further handling them. Check the 'README' file, it" + echo "often tells you about the needed prerequisites for installing" + echo "this package. You may also peek at any GNU archive site, in" + echo "case some other package contains this missing '$1' program." + ;; + esac +} + +give_advice "$1" | sed -e '1s/^/WARNING: /' \ + -e '2,$s/^/ /' >&2 + +# Propagate the correct exit status (expected to be 127 for a program +# not found, 63 for a program that failed due to version mismatch). +exit $st + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/shared/sentry/external/breakpad/autotools/root-test-driver b/shared/sentry/external/breakpad/autotools/root-test-driver new file mode 100755 index 000000000..66bf7ebb1 --- /dev/null +++ b/shared/sentry/external/breakpad/autotools/root-test-driver @@ -0,0 +1,3 @@ +#!/bin/sh +# -E to keep the environment variables needed to run the tests. +exec sudo -E "$@" diff --git a/shared/sentry/external/breakpad/autotools/test-driver b/shared/sentry/external/breakpad/autotools/test-driver new file mode 100755 index 000000000..8e575b017 --- /dev/null +++ b/shared/sentry/external/breakpad/autotools/test-driver @@ -0,0 +1,148 @@ +#! /bin/sh +# test-driver - basic testsuite driver script. + +scriptversion=2013-07-13.22; # UTC + +# Copyright (C) 2011-2014 Free Software Foundation, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +# Make unconditional expansion of undefined variables an error. This +# helps a lot in preventing typo-related bugs. +set -u + +usage_error () +{ + echo "$0: $*" >&2 + print_usage >&2 + exit 2 +} + +print_usage () +{ + cat <$log_file 2>&1 +estatus=$? + +if test $enable_hard_errors = no && test $estatus -eq 99; then + tweaked_estatus=1 +else + tweaked_estatus=$estatus +fi + +case $tweaked_estatus:$expect_failure in + 0:yes) col=$red res=XPASS recheck=yes gcopy=yes;; + 0:*) col=$grn res=PASS recheck=no gcopy=no;; + 77:*) col=$blu res=SKIP recheck=no gcopy=yes;; + 99:*) col=$mgn res=ERROR recheck=yes gcopy=yes;; + *:yes) col=$lgn res=XFAIL recheck=no gcopy=yes;; + *:*) col=$red res=FAIL recheck=yes gcopy=yes;; +esac + +# Report the test outcome and exit status in the logs, so that one can +# know whether the test passed or failed simply by looking at the '.log' +# file, without the need of also peaking into the corresponding '.trs' +# file (automake bug#11814). +echo "$res $test_name (exit status: $estatus)" >>$log_file + +# Report outcome to console. +echo "${col}${res}${std}: $test_name" + +# Register the test result, and other relevant metadata. +echo ":test-result: $res" > $trs_file +echo ":global-test-result: $res" >> $trs_file +echo ":recheck: $recheck" >> $trs_file +echo ":copy-in-global-log: $gcopy" >> $trs_file + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/shared/sentry/external/breakpad/breakpad-client.pc.in b/shared/sentry/external/breakpad/breakpad-client.pc.in new file mode 100644 index 000000000..fcd2fa27e --- /dev/null +++ b/shared/sentry/external/breakpad/breakpad-client.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@/@PACKAGE_NAME@ + +Name: google-breakpad-client +Description: An open-source multi-platform crash reporting system +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lbreakpad_client @PTHREAD_LIBS@ +Cflags: -I${includedir} @PTHREAD_CFLAGS@ diff --git a/shared/sentry/external/breakpad/breakpad.pc.in b/shared/sentry/external/breakpad/breakpad.pc.in new file mode 100644 index 000000000..9aec9f8cb --- /dev/null +++ b/shared/sentry/external/breakpad/breakpad.pc.in @@ -0,0 +1,10 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@/@PACKAGE_NAME@ + +Name: google-breakpad +Description: An open-source multi-platform crash reporting system +Version: @PACKAGE_VERSION@ +Libs: -L${libdir} -lbreakpad @PTHREAD_LIBS@ +Cflags: -I${includedir} @PTHREAD_CFLAGS@ diff --git a/shared/sentry/external/breakpad/codereview.settings b/shared/sentry/external/breakpad/codereview.settings new file mode 100644 index 000000000..3f93733e4 --- /dev/null +++ b/shared/sentry/external/breakpad/codereview.settings @@ -0,0 +1,3 @@ +GERRIT_HOST: True +CODE_REVIEW_SERVER: chromium-review.googlesource.com +VIEW_VC: https://chromium.googlesource.com/breakpad/breakpad/+/ diff --git a/shared/sentry/external/breakpad/configure b/shared/sentry/external/breakpad/configure new file mode 100755 index 000000000..b6a86c72d --- /dev/null +++ b/shared/sentry/external/breakpad/configure @@ -0,0 +1,9260 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.69 for breakpad 0.1. +# +# Report bugs to . +# +# +# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +as_fn_exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1 +test -x / || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org and +$0: google-breakpad-dev@googlegroups.com about your system, +$0: including any error possibly output before this +$0: message. Then install a modern shell, or manually run +$0: the script under such a shell if you do have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +test -n "$DJDIR" || exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME='breakpad' +PACKAGE_TARNAME='breakpad' +PACKAGE_VERSION='0.1' +PACKAGE_STRING='breakpad 0.1' +PACKAGE_BUGREPORT='google-breakpad-dev@googlegroups.com' +PACKAGE_URL='' + +ac_unique_file="README.md" +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + +ac_subst_vars='am__EXEEXT_FALSE +am__EXEEXT_TRUE +LTLIBOBJS +LIBOBJS +TESTS_AS_ROOT_FALSE +TESTS_AS_ROOT_TRUE +RUSTC_DEMANGLE_LIBS +RUSTC_DEMANGLE_CFLAGS +SELFTEST_FALSE +SELFTEST_TRUE +GTEST_LIBS +GTEST_CFLAGS +GMOCK_LIBS +GMOCK_CFLAGS +SYSTEM_TEST_LIBS_FALSE +SYSTEM_TEST_LIBS_TRUE +DISABLE_TOOLS_FALSE +DISABLE_TOOLS_TRUE +DISABLE_PROCESSOR_FALSE +DISABLE_PROCESSOR_TRUE +X86_HOST_FALSE +X86_HOST_TRUE +ANDROID_HOST_FALSE +ANDROID_HOST_TRUE +LINUX_HOST_FALSE +LINUX_HOST_TRUE +WARN_CXXFLAGS +HAVE_CXX11 +HAVE_MEMFD_CREATE_FALSE +HAVE_MEMFD_CREATE_TRUE +HAVE_GETCONTEXT_FALSE +HAVE_GETCONTEXT_TRUE +PTHREAD_CFLAGS +PTHREAD_LIBS +PTHREAD_CC +ax_pthread_config +EGREP +GREP +RANLIB +am__fastdepCXX_FALSE +am__fastdepCXX_TRUE +CXXDEPMODE +ac_ct_CXX +CXXFLAGS +CXX +CPP +am__fastdepCCAS_FALSE +am__fastdepCCAS_TRUE +CCASDEPMODE +CCASFLAGS +CCAS +am__fastdepCC_FALSE +am__fastdepCC_TRUE +CCDEPMODE +am__nodep +AMDEPBACKSLASH +AMDEP_FALSE +AMDEP_TRUE +am__include +DEPDIR +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +ac_ct_AR +AR +MAINT +MAINTAINER_MODE_FALSE +MAINTAINER_MODE_TRUE +AM_BACKSLASH +AM_DEFAULT_VERBOSITY +AM_DEFAULT_V +AM_V +am__untar +am__tar +AMTAR +am__leading_dot +SET_MAKE +AWK +mkdir_p +MKDIR_P +INSTALL_STRIP_PROGRAM +STRIP +install_sh +MAKEINFO +AUTOHEADER +AUTOMAKE +AUTOCONF +ACLOCAL +VERSION +PACKAGE +CYGPATH_W +am__isrc +INSTALL_DATA +INSTALL_SCRIPT +INSTALL_PROGRAM +host_os +host_vendor +host_cpu +host +build_os +build_vendor +build_cpu +build +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +runstatedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL +am__quote' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +enable_silent_rules +enable_maintainer_mode +enable_dependency_tracking +enable_m32 +enable_largefile +enable_processor +enable_tools +enable_system_test_libs +enable_selftest +with_rustc_demangle +with_tests_as_root +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CCAS +CCASFLAGS +CPP +CXX +CXXFLAGS +CCC +GMOCK_CFLAGS +GMOCK_LIBS +GTEST_CFLAGS +GTEST_LIBS +RUSTC_DEMANGLE_CFLAGS +RUSTC_DEMANGLE_LIBS' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +runstatedir='${localstatedir}/run' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -runstatedir | --runstatedir | --runstatedi | --runstated \ + | --runstate | --runstat | --runsta | --runst | --runs \ + | --run | --ru | --r) + ac_prev=runstatedir ;; + -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ + | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ + | --run=* | --ru=* | --r=*) + runstatedir=$ac_optarg ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir runstatedir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures breakpad 0.1 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/breakpad] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF + +Program names: + --program-prefix=PREFIX prepend PREFIX to installed program names + --program-suffix=SUFFIX append SUFFIX to installed program names + --program-transform-name=PROGRAM run sed PROGRAM on installed program names + +System types: + --build=BUILD configure for building on BUILD [guessed] + --host=HOST cross-compile to build programs to run on HOST [BUILD] +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of breakpad 0.1:";; + esac + cat <<\_ACEOF + +Optional Features: + --disable-option-checking ignore unrecognized --enable/--with options + --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) + --enable-FEATURE[=ARG] include FEATURE [ARG=yes] + --enable-silent-rules less verbose build output (undo: "make V=1") + --disable-silent-rules verbose build output (undo: "make V=0") + --enable-maintainer-mode + enable make rules and dependencies not useful (and + sometimes confusing) to the casual installer + --enable-dependency-tracking + do not reject slow dependency extractors + --disable-dependency-tracking + speeds up one-time build + --enable-m32 Compile/build with -m32 (default is no) + --disable-largefile omit support for large files + --disable-processor Don't build processor library (default is no) + --disable-tools Don't build tool binaries (default is no) + --enable-system-test-libs + Use gtest/gmock/etc... from the system instead of + the local copies (default is local) + --enable-selftest Run extra tests with "make check" (may conflict with + optimizations) (default is no) + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --with-rustc-demangle=/path/to/rustc-demangle + Link against the rustc-demangle library to demangle + Rust language symbols during symbol dumping (default + is no) Pass the path to the crate root. + --with-tests-as-root Run the tests as root. Use this on platforms like + travis-ci.org that require root privileges to use + ptrace (default is no) + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + CCAS assembler compiler command (defaults to CC) + CCASFLAGS assembler compiler flags (defaults to CFLAGS) + CPP C preprocessor + CXX C++ compiler command + CXXFLAGS C++ compiler flags + GMOCK_CFLAGS + Compiler flags for gmock + GMOCK_LIBS Linker flags for gmock + GTEST_CFLAGS + Compiler flags for gtest + GTEST_LIBS Linker flags for gtest + RUSTC_DEMANGLE_CFLAGS + Compiler flags for rustc-demangle + RUSTC_DEMANGLE_LIBS + Linker flags for rustc-demangle + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to . +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +breakpad configure 0.1 +generated by GNU Autoconf 2.69 + +Copyright (C) 2012 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp + +# ac_fn_cxx_try_compile LINENO +# ---------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_cxx_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_cxx_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_cxx_try_compile + +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_run + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + +# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists, giving a warning if it cannot be compiled using +# the include files in INCLUDES and setting the cache variable VAR +# accordingly. +ac_fn_c_check_header_mongrel () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if eval \${$3+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 +$as_echo_n "checking $2 usability... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_header_compiler=yes +else + ac_header_compiler=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 +$as_echo_n "checking $2 presence... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <$2> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + ac_header_preproc=yes +else + ac_header_preproc=no +fi +rm -f conftest.err conftest.i conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( + yes:no: ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; + no:yes:* ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} +( $as_echo "## --------------------------------------------------- ## +## Report this to google-breakpad-dev@googlegroups.com ## +## --------------------------------------------------- ##" + ) | sed "s/^/$as_me: WARNING: /" >&2 + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=\$ac_header_compiler" +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_mongrel + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_compile + +# ac_fn_c_check_func LINENO FUNC VAR +# ---------------------------------- +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_c_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case declares $2. + For example, HP-UX 11i declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. + Prefer to if __STDC__ is defined, since + exists even on freestanding compilers. */ + +#ifdef __STDC__ +# include +#else +# include +#endif + +#undef $2 + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$2 || defined __stub___$2 +choke me +#endif + +int +main () +{ +return $2 (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_func +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by breakpad $as_me 0.1, which was +generated by GNU Autoconf 2.69. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + $as_echo "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + $as_echo "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + $as_echo "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + $as_echo "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + +ac_aux_dir= +for ac_dir in autotools "$srcdir"/autotools; do + if test -f "$ac_dir/install-sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install-sh -c" + break + elif test -f "$ac_dir/install.sh"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/install.sh -c" + break + elif test -f "$ac_dir/shtool"; then + ac_aux_dir=$ac_dir + ac_install_sh="$ac_aux_dir/shtool install -c" + break + fi +done +if test -z "$ac_aux_dir"; then + as_fn_error $? "cannot find install-sh, install.sh, or shtool in autotools \"$srcdir\"/autotools" "$LINENO" 5 +fi + +# These three variables are undocumented and unsupported, +# and are intended to be withdrawn in a future Autoconf release. +# They can cause serious problems if a builder's source tree is in a directory +# whose full name contains unusual characters. +ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. +ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. +ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. + + + +# Make sure we can run config.sub. +$SHELL "$ac_aux_dir/config.sub" sun4 >/dev/null 2>&1 || + as_fn_error $? "cannot run $SHELL $ac_aux_dir/config.sub" "$LINENO" 5 + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking build system type" >&5 +$as_echo_n "checking build system type... " >&6; } +if ${ac_cv_build+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_build_alias=$build_alias +test "x$ac_build_alias" = x && + ac_build_alias=`$SHELL "$ac_aux_dir/config.guess"` +test "x$ac_build_alias" = x && + as_fn_error $? "cannot guess build type; you must specify one" "$LINENO" 5 +ac_cv_build=`$SHELL "$ac_aux_dir/config.sub" $ac_build_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $ac_build_alias failed" "$LINENO" 5 + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_build" >&5 +$as_echo "$ac_cv_build" >&6; } +case $ac_cv_build in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical build" "$LINENO" 5;; +esac +build=$ac_cv_build +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_build +shift +build_cpu=$1 +build_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +build_os=$* +IFS=$ac_save_IFS +case $build_os in *\ *) build_os=`echo "$build_os" | sed 's/ /-/g'`;; esac + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking host system type" >&5 +$as_echo_n "checking host system type... " >&6; } +if ${ac_cv_host+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test "x$host_alias" = x; then + ac_cv_host=$ac_cv_build +else + ac_cv_host=`$SHELL "$ac_aux_dir/config.sub" $host_alias` || + as_fn_error $? "$SHELL $ac_aux_dir/config.sub $host_alias failed" "$LINENO" 5 +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_host" >&5 +$as_echo "$ac_cv_host" >&6; } +case $ac_cv_host in +*-*-*) ;; +*) as_fn_error $? "invalid value of canonical host" "$LINENO" 5;; +esac +host=$ac_cv_host +ac_save_IFS=$IFS; IFS='-' +set x $ac_cv_host +shift +host_cpu=$1 +host_vendor=$2 +shift; shift +# Remember, the first character of IFS is used to create $*, +# except with old shells: +host_os=$* +IFS=$ac_save_IFS +case $host_os in *\ *) host_os=`echo "$host_os" | sed 's/ /-/g'`;; esac + + + +am__api_version='1.16' + +# Find a good install program. We prefer a C program (faster), +# so one script is as good as another. But avoid the broken or +# incompatible versions: +# SysV /etc/install, /usr/sbin/install +# SunOS /usr/etc/install +# IRIX /sbin/install +# AIX /bin/install +# AmigaOS /C/install, which installs bootblocks on floppy discs +# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag +# AFS /usr/afsws/bin/install, which mishandles nonexistent args +# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" +# OS/2's system install, which has a completely different semantic +# ./install, which can be erroneously created by make from ./install.sh. +# Reject install programs that cannot install multiple files. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 +$as_echo_n "checking for a BSD-compatible install... " >&6; } +if test -z "$INSTALL"; then +if ${ac_cv_path_install+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + # Account for people who put trailing slashes in PATH elements. +case $as_dir/ in #(( + ./ | .// | /[cC]/* | \ + /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ + ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ + /usr/ucb/* ) ;; + *) + # OSF1 and SCO ODT 3.0 have their own names for install. + # Don't use installbsd from OSF since it installs stuff as root + # by default. + for ac_prog in ginstall scoinst install; do + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext"; then + if test $ac_prog = install && + grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # AIX install. It has an incompatible calling convention. + : + elif test $ac_prog = install && + grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then + # program-specific install script used by HP pwplus--don't use. + : + else + rm -rf conftest.one conftest.two conftest.dir + echo one > conftest.one + echo two > conftest.two + mkdir conftest.dir + if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && + test -s conftest.one && test -s conftest.two && + test -s conftest.dir/conftest.one && + test -s conftest.dir/conftest.two + then + ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" + break 3 + fi + fi + fi + done + done + ;; +esac + + done +IFS=$as_save_IFS + +rm -rf conftest.one conftest.two conftest.dir + +fi + if test "${ac_cv_path_install+set}" = set; then + INSTALL=$ac_cv_path_install + else + # As a last resort, use the slow shell script. Don't cache a + # value for INSTALL within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + INSTALL=$ac_install_sh + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 +$as_echo "$INSTALL" >&6; } + +# Use test -z because SunOS4 sh mishandles braces in ${var-val}. +# It thinks the first close brace ends the variable substitution. +test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' + +test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' + +test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether build environment is sane" >&5 +$as_echo_n "checking whether build environment is sane... " >&6; } +# Reject unsafe characters in $srcdir or the absolute working directory +# name. Accept space and tab only in the latter. +am_lf=' +' +case `pwd` in + *[\\\"\#\$\&\'\`$am_lf]*) + as_fn_error $? "unsafe absolute working directory name" "$LINENO" 5;; +esac +case $srcdir in + *[\\\"\#\$\&\'\`$am_lf\ \ ]*) + as_fn_error $? "unsafe srcdir value: '$srcdir'" "$LINENO" 5;; +esac + +# Do 'set' in a subshell so we don't clobber the current shell's +# arguments. Must try -L first in case configure is actually a +# symlink; some systems play weird games with the mod time of symlinks +# (eg FreeBSD returns the mod time of the symlink's containing +# directory). +if ( + am_has_slept=no + for am_try in 1 2; do + echo "timestamp, slept: $am_has_slept" > conftest.file + set X `ls -Lt "$srcdir/configure" conftest.file 2> /dev/null` + if test "$*" = "X"; then + # -L didn't work. + set X `ls -t "$srcdir/configure" conftest.file` + fi + if test "$*" != "X $srcdir/configure conftest.file" \ + && test "$*" != "X conftest.file $srcdir/configure"; then + + # If neither matched, then we have a broken ls. This can happen + # if, for instance, CONFIG_SHELL is bash and it inherits a + # broken ls alias from the environment. This has actually + # happened. Such a system could not be considered "sane". + as_fn_error $? "ls -t appears to fail. Make sure there is not a broken + alias in your environment" "$LINENO" 5 + fi + if test "$2" = conftest.file || test $am_try -eq 2; then + break + fi + # Just in case. + sleep 1 + am_has_slept=yes + done + test "$2" = conftest.file + ) +then + # Ok. + : +else + as_fn_error $? "newly created file is older than distributed files! +Check your system clock" "$LINENO" 5 +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +# If we didn't sleep, we still need to ensure time stamps of config.status and +# generated files are strictly newer. +am_sleep_pid= +if grep 'slept: no' conftest.file >/dev/null 2>&1; then + ( sleep 1 ) & + am_sleep_pid=$! +fi + +rm -f conftest.file + +test "$program_prefix" != NONE && + program_transform_name="s&^&$program_prefix&;$program_transform_name" +# Use a double $ so make ignores it. +test "$program_suffix" != NONE && + program_transform_name="s&\$&$program_suffix&;$program_transform_name" +# Double any \ or $. +# By default was `s,x,x', remove it if useless. +ac_script='s/[\\$]/&&/g;s/;s,x,x,$//' +program_transform_name=`$as_echo "$program_transform_name" | sed "$ac_script"` + +# Expand $ac_aux_dir to an absolute path. +am_aux_dir=`cd "$ac_aux_dir" && pwd` + +if test x"${MISSING+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + MISSING="\${SHELL} \"$am_aux_dir/missing\"" ;; + *) + MISSING="\${SHELL} $am_aux_dir/missing" ;; + esac +fi +# Use eval to expand $SHELL +if eval "$MISSING --is-lightweight"; then + am_missing_run="$MISSING " +else + am_missing_run= + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: 'missing' script is too old or missing" >&5 +$as_echo "$as_me: WARNING: 'missing' script is too old or missing" >&2;} +fi + +if test x"${install_sh+set}" != xset; then + case $am_aux_dir in + *\ * | *\ *) + install_sh="\${SHELL} '$am_aux_dir/install-sh'" ;; + *) + install_sh="\${SHELL} $am_aux_dir/install-sh" + esac +fi + +# Installed binaries are usually stripped using 'strip' when the user +# run "make install-strip". However 'strip' might not be the right +# tool to use in cross-compilation environments, therefore Automake +# will honor the 'STRIP' environment variable to overrule this program. +if test "$cross_compiling" != no; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}strip", so it can be a program name with args. +set dummy ${ac_tool_prefix}strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$STRIP"; then + ac_cv_prog_STRIP="$STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_STRIP="${ac_tool_prefix}strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +STRIP=$ac_cv_prog_STRIP +if test -n "$STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $STRIP" >&5 +$as_echo "$STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_STRIP"; then + ac_ct_STRIP=$STRIP + # Extract the first word of "strip", so it can be a program name with args. +set dummy strip; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_STRIP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_STRIP"; then + ac_cv_prog_ac_ct_STRIP="$ac_ct_STRIP" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_STRIP="strip" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_STRIP=$ac_cv_prog_ac_ct_STRIP +if test -n "$ac_ct_STRIP"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_STRIP" >&5 +$as_echo "$ac_ct_STRIP" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_STRIP" = x; then + STRIP=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + STRIP=$ac_ct_STRIP + fi +else + STRIP="$ac_cv_prog_STRIP" +fi + +fi +INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for a thread-safe mkdir -p" >&5 +$as_echo_n "checking for a thread-safe mkdir -p... " >&6; } +if test -z "$MKDIR_P"; then + if ${ac_cv_path_mkdir+:} false; then : + $as_echo_n "(cached) " >&6 +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/opt/sfw/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in mkdir gmkdir; do + for ac_exec_ext in '' $ac_executable_extensions; do + as_fn_executable_p "$as_dir/$ac_prog$ac_exec_ext" || continue + case `"$as_dir/$ac_prog$ac_exec_ext" --version 2>&1` in #( + 'mkdir (GNU coreutils) '* | \ + 'mkdir (coreutils) '* | \ + 'mkdir (fileutils) '4.1*) + ac_cv_path_mkdir=$as_dir/$ac_prog$ac_exec_ext + break 3;; + esac + done + done + done +IFS=$as_save_IFS + +fi + + test -d ./--version && rmdir ./--version + if test "${ac_cv_path_mkdir+set}" = set; then + MKDIR_P="$ac_cv_path_mkdir -p" + else + # As a last resort, use the slow shell script. Don't cache a + # value for MKDIR_P within a source directory, because that will + # break other packages using the cache if that directory is + # removed, or if the value is a relative name. + MKDIR_P="$ac_install_sh -d" + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDIR_P" >&5 +$as_echo "$MKDIR_P" >&6; } + +for ac_prog in gawk mawk nawk awk +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AWK+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AWK"; then + ac_cv_prog_AWK="$AWK" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AWK="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AWK=$ac_cv_prog_AWK +if test -n "$AWK"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AWK" >&5 +$as_echo "$AWK" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AWK" && break +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} sets \$(MAKE)" >&5 +$as_echo_n "checking whether ${MAKE-make} sets \$(MAKE)... " >&6; } +set x ${MAKE-make} +ac_make=`$as_echo "$2" | sed 's/+/p/g; s/[^a-zA-Z0-9_]/_/g'` +if eval \${ac_cv_prog_make_${ac_make}_set+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat >conftest.make <<\_ACEOF +SHELL = /bin/sh +all: + @echo '@@@%%%=$(MAKE)=@@@%%%' +_ACEOF +# GNU make sometimes prints "make[1]: Entering ...", which would confuse us. +case `${MAKE-make} -f conftest.make 2>/dev/null` in + *@@@%%%=?*=@@@%%%*) + eval ac_cv_prog_make_${ac_make}_set=yes;; + *) + eval ac_cv_prog_make_${ac_make}_set=no;; +esac +rm -f conftest.make +fi +if eval test \$ac_cv_prog_make_${ac_make}_set = yes; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + SET_MAKE= +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + SET_MAKE="MAKE=${MAKE-make}" +fi + +rm -rf .tst 2>/dev/null +mkdir .tst 2>/dev/null +if test -d .tst; then + am__leading_dot=. +else + am__leading_dot=_ +fi +rmdir .tst 2>/dev/null + +# Check whether --enable-silent-rules was given. +if test "${enable_silent_rules+set}" = set; then : + enableval=$enable_silent_rules; +fi + +case $enable_silent_rules in # ((( + yes) AM_DEFAULT_VERBOSITY=0;; + no) AM_DEFAULT_VERBOSITY=1;; + *) AM_DEFAULT_VERBOSITY=1;; +esac +am_make=${MAKE-make} +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $am_make supports nested variables" >&5 +$as_echo_n "checking whether $am_make supports nested variables... " >&6; } +if ${am_cv_make_support_nested_variables+:} false; then : + $as_echo_n "(cached) " >&6 +else + if $as_echo 'TRUE=$(BAR$(V)) +BAR0=false +BAR1=true +V=1 +am__doit: + @$(TRUE) +.PHONY: am__doit' | $am_make -f - >/dev/null 2>&1; then + am_cv_make_support_nested_variables=yes +else + am_cv_make_support_nested_variables=no +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_make_support_nested_variables" >&5 +$as_echo "$am_cv_make_support_nested_variables" >&6; } +if test $am_cv_make_support_nested_variables = yes; then + AM_V='$(V)' + AM_DEFAULT_V='$(AM_DEFAULT_VERBOSITY)' +else + AM_V=$AM_DEFAULT_VERBOSITY + AM_DEFAULT_V=$AM_DEFAULT_VERBOSITY +fi +AM_BACKSLASH='\' + +if test "`cd $srcdir && pwd`" != "`pwd`"; then + # Use -I$(srcdir) only when $(srcdir) != ., so that make's output + # is not polluted with repeated "-I." + am__isrc=' -I$(srcdir)' + # test to see if srcdir already configured + if test -f $srcdir/config.status; then + as_fn_error $? "source directory already configured; run \"make distclean\" there first" "$LINENO" 5 + fi +fi + +# test whether we have cygpath +if test -z "$CYGPATH_W"; then + if (cygpath --version) >/dev/null 2>/dev/null; then + CYGPATH_W='cygpath -w' + else + CYGPATH_W=echo + fi +fi + + +# Define the identity of the package. + PACKAGE='breakpad' + VERSION='0.1' + + +cat >>confdefs.h <<_ACEOF +#define PACKAGE "$PACKAGE" +_ACEOF + + +cat >>confdefs.h <<_ACEOF +#define VERSION "$VERSION" +_ACEOF + +# Some tools Automake needs. + +ACLOCAL=${ACLOCAL-"${am_missing_run}aclocal-${am__api_version}"} + + +AUTOCONF=${AUTOCONF-"${am_missing_run}autoconf"} + + +AUTOMAKE=${AUTOMAKE-"${am_missing_run}automake-${am__api_version}"} + + +AUTOHEADER=${AUTOHEADER-"${am_missing_run}autoheader"} + + +MAKEINFO=${MAKEINFO-"${am_missing_run}makeinfo"} + +# For better backward compatibility. To be removed once Automake 1.9.x +# dies out for good. For more background, see: +# +# +mkdir_p='$(MKDIR_P)' + +# We need awk for the "check" target (and possibly the TAP driver). The +# system "awk" is bad on some platforms. +# Always define AMTAR for backward compatibility. Yes, it's still used +# in the wild :-( We should find a proper way to deprecate it ... +AMTAR='$${TAR-tar}' + + +# We'll loop over all known methods to create a tar archive until one works. +_am_tools='gnutar plaintar pax cpio none' + +# The POSIX 1988 'ustar' format is defined with fixed-size fields. + # There is notably a 21 bits limit for the UID and the GID. In fact, + # the 'pax' utility can hang on bigger UID/GID (see automake bug#8343 + # and bug#13588). + am_max_uid=2097151 # 2^21 - 1 + am_max_gid=$am_max_uid + # The $UID and $GID variables are not portable, so we need to resort + # to the POSIX-mandated id(1) utility. Errors in the 'id' calls + # below are definitely unexpected, so allow the users to see them + # (that is, avoid stderr redirection). + am_uid=`id -u || echo unknown` + am_gid=`id -g || echo unknown` + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether UID '$am_uid' is supported by ustar format" >&5 +$as_echo_n "checking whether UID '$am_uid' is supported by ustar format... " >&6; } + if test $am_uid -le $am_max_uid; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + _am_tools=none + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether GID '$am_gid' is supported by ustar format" >&5 +$as_echo_n "checking whether GID '$am_gid' is supported by ustar format... " >&6; } + if test $am_gid -le $am_max_gid; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + _am_tools=none + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to create a ustar tar archive" >&5 +$as_echo_n "checking how to create a ustar tar archive... " >&6; } + + # Go ahead even if we have the value already cached. We do so because we + # need to set the values for the 'am__tar' and 'am__untar' variables. + _am_tools=${am_cv_prog_tar_ustar-$_am_tools} + + for _am_tool in $_am_tools; do + case $_am_tool in + gnutar) + for _am_tar in tar gnutar gtar; do + { echo "$as_me:$LINENO: $_am_tar --version" >&5 + ($_am_tar --version) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } && break + done + am__tar="$_am_tar --format=ustar -chf - "'"$$tardir"' + am__tar_="$_am_tar --format=ustar -chf - "'"$tardir"' + am__untar="$_am_tar -xf -" + ;; + plaintar) + # Must skip GNU tar: if it does not support --format= it doesn't create + # ustar tarball either. + (tar --version) >/dev/null 2>&1 && continue + am__tar='tar chf - "$$tardir"' + am__tar_='tar chf - "$tardir"' + am__untar='tar xf -' + ;; + pax) + am__tar='pax -L -x ustar -w "$$tardir"' + am__tar_='pax -L -x ustar -w "$tardir"' + am__untar='pax -r' + ;; + cpio) + am__tar='find "$$tardir" -print | cpio -o -H ustar -L' + am__tar_='find "$tardir" -print | cpio -o -H ustar -L' + am__untar='cpio -i -H ustar -d' + ;; + none) + am__tar=false + am__tar_=false + am__untar=false + ;; + esac + + # If the value was cached, stop now. We just wanted to have am__tar + # and am__untar set. + test -n "${am_cv_prog_tar_ustar}" && break + + # tar/untar a dummy directory, and stop if the command works. + rm -rf conftest.dir + mkdir conftest.dir + echo GrepMe > conftest.dir/file + { echo "$as_me:$LINENO: tardir=conftest.dir && eval $am__tar_ >conftest.tar" >&5 + (tardir=conftest.dir && eval $am__tar_ >conftest.tar) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + rm -rf conftest.dir + if test -s conftest.tar; then + { echo "$as_me:$LINENO: $am__untar &5 + ($am__untar &5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + { echo "$as_me:$LINENO: cat conftest.dir/file" >&5 + (cat conftest.dir/file) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + grep GrepMe conftest.dir/file >/dev/null 2>&1 && break + fi + done + rm -rf conftest.dir + + if ${am_cv_prog_tar_ustar+:} false; then : + $as_echo_n "(cached) " >&6 +else + am_cv_prog_tar_ustar=$_am_tool +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_tar_ustar" >&5 +$as_echo "$am_cv_prog_tar_ustar" >&6; } + + + + + + +# POSIX will say in a future version that running "rm -f" with no argument +# is OK; and we want to be able to make that assumption in our Makefile +# recipes. So use an aggressive probe to check that the usage we want is +# actually supported "in the wild" to an acceptable degree. +# See automake bug#10828. +# To make any issue more visible, cause the running configure to be aborted +# by default if the 'rm' program in use doesn't match our expectations; the +# user can still override this though. +if rm -f && rm -fr && rm -rf; then : OK; else + cat >&2 <<'END' +Oops! + +Your 'rm' program seems unable to run without file operands specified +on the command line, even when the '-f' option is present. This is contrary +to the behaviour of most rm programs out there, and not conforming with +the upcoming POSIX standard: + +Please tell bug-automake@gnu.org about your system, including the value +of your $PATH and any error possibly output before this message. This +can help us improve future automake versions. + +END + if test x"$ACCEPT_INFERIOR_RM_PROGRAM" = x"yes"; then + echo 'Configuration will proceed anyway, since you have set the' >&2 + echo 'ACCEPT_INFERIOR_RM_PROGRAM variable to "yes"' >&2 + echo >&2 + else + cat >&2 <<'END' +Aborting the configuration process, to ensure you take notice of the issue. + +You can download and install GNU coreutils to get an 'rm' implementation +that behaves properly: . + +If you want to complete the configuration process using your problematic +'rm' anyway, export the environment variable ACCEPT_INFERIOR_RM_PROGRAM +to "yes", and re-run configure. + +END + as_fn_error $? "Your 'rm' program is bad, sorry." "$LINENO" 5 + fi +fi + +ac_config_headers="$ac_config_headers src/config.h" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to enable maintainer-specific portions of Makefiles" >&5 +$as_echo_n "checking whether to enable maintainer-specific portions of Makefiles... " >&6; } + # Check whether --enable-maintainer-mode was given. +if test "${enable_maintainer_mode+set}" = set; then : + enableval=$enable_maintainer_mode; USE_MAINTAINER_MODE=$enableval +else + USE_MAINTAINER_MODE=no +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $USE_MAINTAINER_MODE" >&5 +$as_echo "$USE_MAINTAINER_MODE" >&6; } + if test $USE_MAINTAINER_MODE = yes; then + MAINTAINER_MODE_TRUE= + MAINTAINER_MODE_FALSE='#' +else + MAINTAINER_MODE_TRUE='#' + MAINTAINER_MODE_FALSE= +fi + + MAINT=$MAINTAINER_MODE_TRUE + + + +DEPDIR="${am__leading_dot}deps" + +ac_config_commands="$ac_config_commands depfiles" + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether ${MAKE-make} supports the include directive" >&5 +$as_echo_n "checking whether ${MAKE-make} supports the include directive... " >&6; } +cat > confinc.mk << 'END' +am__doit: + @echo this is the am__doit target >confinc.out +.PHONY: am__doit +END +am__include="#" +am__quote= +# BSD make does it like this. +echo '.include "confinc.mk" # ignored' > confmf.BSD +# Other make implementations (GNU, Solaris 10, AIX) do it like this. +echo 'include confinc.mk # ignored' > confmf.GNU +_am_result=no +for s in GNU BSD; do + { echo "$as_me:$LINENO: ${MAKE-make} -f confmf.$s && cat confinc.out" >&5 + (${MAKE-make} -f confmf.$s && cat confinc.out) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } + case $?:`cat confinc.out 2>/dev/null` in #( + '0:this is the am__doit target') : + case $s in #( + BSD) : + am__include='.include' am__quote='"' ;; #( + *) : + am__include='include' am__quote='' ;; +esac ;; #( + *) : + ;; +esac + if test "$am__include" != "#"; then + _am_result="yes ($s style)" + break + fi +done +rm -f confinc.* confmf.* +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: ${_am_result}" >&5 +$as_echo "${_am_result}" >&6; } + +# Check whether --enable-dependency-tracking was given. +if test "${enable_dependency_tracking+set}" = set; then : + enableval=$enable_dependency_tracking; +fi + +if test "x$enable_dependency_tracking" != xno; then + am_depcomp="$ac_aux_dir/depcomp" + AMDEPBACKSLASH='\' + am__nodep='_no' +fi + if test "x$enable_dependency_tracking" != xno; then + AMDEP_TRUE= + AMDEP_FALSE='#' +else + AMDEP_TRUE='#' + AMDEP_FALSE= +fi + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +if test -z "$ac_file"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if ${ac_cv_objext+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 +$as_echo_n "checking whether $CC understands -c and -o together... " >&6; } +if ${am_cv_prog_cc_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 + ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 +$as_echo "$am_cv_prog_cc_c_o" >&6; } +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +depcc="$CC" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CC_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + + +if test -n "$ac_tool_prefix"; then + for ac_prog in ar lib "link -lib" + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$AR"; then + ac_cv_prog_AR="$AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_AR="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +AR=$ac_cv_prog_AR +if test -n "$AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $AR" >&5 +$as_echo "$AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$AR" && break + done +fi +if test -z "$AR"; then + ac_ct_AR=$AR + for ac_prog in ar lib "link -lib" +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_AR+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_AR"; then + ac_cv_prog_ac_ct_AR="$ac_ct_AR" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_AR="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_AR=$ac_cv_prog_ac_ct_AR +if test -n "$ac_ct_AR"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_AR" >&5 +$as_echo "$ac_ct_AR" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_AR" && break +done + + if test "x$ac_ct_AR" = x; then + AR="false" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + AR=$ac_ct_AR + fi +fi + +: ${AR=ar} + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking the archiver ($AR) interface" >&5 +$as_echo_n "checking the archiver ($AR) interface... " >&6; } +if ${am_cv_ar_interface+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + am_cv_ar_interface=ar + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +int some_variable = 0; +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + am_ar_try='$AR cru libconftest.a conftest.$ac_objext >&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$am_ar_try\""; } >&5 + (eval $am_ar_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if test "$ac_status" -eq 0; then + am_cv_ar_interface=ar + else + am_ar_try='$AR -NOLOGO -OUT:conftest.lib conftest.$ac_objext >&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$am_ar_try\""; } >&5 + (eval $am_ar_try) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if test "$ac_status" -eq 0; then + am_cv_ar_interface=lib + else + am_cv_ar_interface=unknown + fi + fi + rm -f conftest.lib libconftest.a + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_ar_interface" >&5 +$as_echo "$am_cv_ar_interface" >&6; } + +case $am_cv_ar_interface in +ar) + ;; +lib) + # Microsoft lib, so override with the ar-lib wrapper script. + # FIXME: It is wrong to rewrite AR. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__AR in this case, + # and then we could set am__AR="$am_aux_dir/ar-lib \$(AR)" or something + # similar. + AR="$am_aux_dir/ar-lib $AR" + ;; +unknown) + as_fn_error $? "could not determine $AR interface" "$LINENO" 5 + ;; +esac + +# By default we simply use the C compiler to build assembly code. + +test "${CCAS+set}" = set || CCAS=$CC +test "${CCASFLAGS+set}" = set || CCASFLAGS=$CFLAGS + + + +depcc="$CCAS" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CCAS_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CCAS_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CCAS_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CCAS_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CCAS_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CCAS_dependencies_compiler_type" >&6; } +CCASDEPMODE=depmode=$am_cv_CCAS_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CCAS_dependencies_compiler_type" = gcc3; then + am__fastdepCCAS_TRUE= + am__fastdepCCAS_FALSE='#' +else + am__fastdepCCAS_TRUE='#' + am__fastdepCCAS_FALSE= +fi + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC understands -c and -o together" >&5 +$as_echo_n "checking whether $CC understands -c and -o together... " >&6; } +if ${am_cv_prog_cc_c_o+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF + # Make sure it works both with $CC and with simple cc. + # Following AC_PROG_CC_C_O, we do the test twice because some + # compilers refuse to overwrite an existing .o file with -o, + # though they will create one. + am_cv_prog_cc_c_o=yes + for am_i in 1 2; do + if { echo "$as_me:$LINENO: $CC -c conftest.$ac_ext -o conftest2.$ac_objext" >&5 + ($CC -c conftest.$ac_ext -o conftest2.$ac_objext) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } \ + && test -f conftest2.$ac_objext; then + : OK + else + am_cv_prog_cc_c_o=no + break + fi + done + rm -f core conftest* + unset am_i +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_prog_cc_c_o" >&5 +$as_echo "$am_cv_prog_cc_c_o" >&6; } +if test "$am_cv_prog_cc_c_o" != yes; then + # Losing compiler, so override with the script. + # FIXME: It is wrong to rewrite CC. + # But if we don't then we get into trouble of one sort or another. + # A longer-term fix would be to have automake use am__CC in this case, + # and then we could set am__CC="\$(top_srcdir)/compile \$(CC)" + CC="$am_aux_dir/compile $CC" +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +depcc="$CC" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CC_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CC_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CC_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CC_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CC_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CC_dependencies_compiler_type" >&6; } +CCDEPMODE=depmode=$am_cv_CC_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CC_dependencies_compiler_type" = gcc3; then + am__fastdepCC_TRUE= + am__fastdepCC_FALSE='#' +else + am__fastdepCC_TRUE='#' + am__fastdepCC_FALSE= +fi + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu +if test -z "$CXX"; then + if test -n "$CCC"; then + CXX=$CCC + else + if test -n "$ac_tool_prefix"; then + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CXX"; then + ac_cv_prog_CXX="$CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CXX="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CXX=$ac_cv_prog_CXX +if test -n "$CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CXX" >&5 +$as_echo "$CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CXX" && break + done +fi +if test -z "$CXX"; then + ac_ct_CXX=$CXX + for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CXX+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CXX"; then + ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CXX="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CXX=$ac_cv_prog_ac_ct_CXX +if test -n "$ac_ct_CXX"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CXX" >&5 +$as_echo "$ac_ct_CXX" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CXX" && break +done + + if test "x$ac_ct_CXX" = x; then + CXX="g++" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CXX=$ac_ct_CXX + fi +fi + + fi +fi +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C++ compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C++ compiler" >&5 +$as_echo_n "checking whether we are using the GNU C++ compiler... " >&6; } +if ${ac_cv_cxx_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_cxx_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_cxx_compiler_gnu" >&5 +$as_echo "$ac_cv_cxx_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GXX=yes +else + GXX= +fi +ac_test_CXXFLAGS=${CXXFLAGS+set} +ac_save_CXXFLAGS=$CXXFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX accepts -g" >&5 +$as_echo_n "checking whether $CXX accepts -g... " >&6; } +if ${ac_cv_prog_cxx_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_cxx_werror_flag=$ac_cxx_werror_flag + ac_cxx_werror_flag=yes + ac_cv_prog_cxx_g=no + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +else + CXXFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + +else + ac_cxx_werror_flag=$ac_save_cxx_werror_flag + CXXFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ac_cv_prog_cxx_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cxx_werror_flag=$ac_save_cxx_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cxx_g" >&5 +$as_echo "$ac_cv_prog_cxx_g" >&6; } +if test "$ac_test_CXXFLAGS" = set; then + CXXFLAGS=$ac_save_CXXFLAGS +elif test $ac_cv_prog_cxx_g = yes; then + if test "$GXX" = yes; then + CXXFLAGS="-g -O2" + else + CXXFLAGS="-g" + fi +else + if test "$GXX" = yes; then + CXXFLAGS="-O2" + else + CXXFLAGS= + fi +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +depcc="$CXX" am_compiler_list= + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking dependency style of $depcc" >&5 +$as_echo_n "checking dependency style of $depcc... " >&6; } +if ${am_cv_CXX_dependencies_compiler_type+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$AMDEP_TRUE" && test -f "$am_depcomp"; then + # We make a subdir and do the tests there. Otherwise we can end up + # making bogus files that we don't know about and never remove. For + # instance it was reported that on HP-UX the gcc test will end up + # making a dummy file named 'D' -- because '-MD' means "put the output + # in D". + rm -rf conftest.dir + mkdir conftest.dir + # Copy depcomp to subdir because otherwise we won't find it if we're + # using a relative directory. + cp "$am_depcomp" conftest.dir + cd conftest.dir + # We will build objects and dependencies in a subdirectory because + # it helps to detect inapplicable dependency modes. For instance + # both Tru64's cc and ICC support -MD to output dependencies as a + # side effect of compilation, but ICC will put the dependencies in + # the current directory while Tru64 will put them in the object + # directory. + mkdir sub + + am_cv_CXX_dependencies_compiler_type=none + if test "$am_compiler_list" = ""; then + am_compiler_list=`sed -n 's/^#*\([a-zA-Z0-9]*\))$/\1/p' < ./depcomp` + fi + am__universal=false + case " $depcc " in #( + *\ -arch\ *\ -arch\ *) am__universal=true ;; + esac + + for depmode in $am_compiler_list; do + # Setup a source with many dependencies, because some compilers + # like to wrap large dependency lists on column 80 (with \), and + # we should not choose a depcomp mode which is confused by this. + # + # We need to recreate these files for each test, as the compiler may + # overwrite some of them when testing with obscure command lines. + # This happens at least with the AIX C compiler. + : > sub/conftest.c + for i in 1 2 3 4 5 6; do + echo '#include "conftst'$i'.h"' >> sub/conftest.c + # Using ": > sub/conftst$i.h" creates only sub/conftst1.h with + # Solaris 10 /bin/sh. + echo '/* dummy */' > sub/conftst$i.h + done + echo "${am__include} ${am__quote}sub/conftest.Po${am__quote}" > confmf + + # We check with '-c' and '-o' for the sake of the "dashmstdout" + # mode. It turns out that the SunPro C++ compiler does not properly + # handle '-M -o', and we need to detect this. Also, some Intel + # versions had trouble with output in subdirs. + am__obj=sub/conftest.${OBJEXT-o} + am__minus_obj="-o $am__obj" + case $depmode in + gcc) + # This depmode causes a compiler race in universal mode. + test "$am__universal" = false || continue + ;; + nosideeffect) + # After this tag, mechanisms are not by side-effect, so they'll + # only be used when explicitly requested. + if test "x$enable_dependency_tracking" = xyes; then + continue + else + break + fi + ;; + msvc7 | msvc7msys | msvisualcpp | msvcmsys) + # This compiler won't grok '-c -o', but also, the minuso test has + # not run yet. These depmodes are late enough in the game, and + # so weak that their functioning should not be impacted. + am__obj=conftest.${OBJEXT-o} + am__minus_obj= + ;; + none) break ;; + esac + if depmode=$depmode \ + source=sub/conftest.c object=$am__obj \ + depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \ + $SHELL ./depcomp $depcc -c $am__minus_obj sub/conftest.c \ + >/dev/null 2>conftest.err && + grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 && + grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 && + grep $am__obj sub/conftest.Po > /dev/null 2>&1 && + ${MAKE-make} -s -f confmf > /dev/null 2>&1; then + # icc doesn't choke on unknown options, it will just issue warnings + # or remarks (even with -Werror). So we grep stderr for any message + # that says an option was ignored or not supported. + # When given -MP, icc 7.0 and 7.1 complain thusly: + # icc: Command line warning: ignoring option '-M'; no argument required + # The diagnosis changed in icc 8.0: + # icc: Command line remark: option '-MP' not supported + if (grep 'ignoring option' conftest.err || + grep 'not supported' conftest.err) >/dev/null 2>&1; then :; else + am_cv_CXX_dependencies_compiler_type=$depmode + break + fi + fi + done + + cd .. + rm -rf conftest.dir +else + am_cv_CXX_dependencies_compiler_type=none +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $am_cv_CXX_dependencies_compiler_type" >&5 +$as_echo "$am_cv_CXX_dependencies_compiler_type" >&6; } +CXXDEPMODE=depmode=$am_cv_CXX_dependencies_compiler_type + + if + test "x$enable_dependency_tracking" != xno \ + && test "$am_cv_CXX_dependencies_compiler_type" = gcc3; then + am__fastdepCXX_TRUE= + am__fastdepCXX_FALSE='#' +else + am__fastdepCXX_TRUE='#' + am__fastdepCXX_FALSE= +fi + + +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. +set dummy ${ac_tool_prefix}ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$RANLIB"; then + ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +RANLIB=$ac_cv_prog_RANLIB +if test -n "$RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 +$as_echo "$RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_RANLIB"; then + ac_ct_RANLIB=$RANLIB + # Extract the first word of "ranlib", so it can be a program name with args. +set dummy ranlib; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_RANLIB"; then + ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_RANLIB="ranlib" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB +if test -n "$ac_ct_RANLIB"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 +$as_echo "$ac_ct_RANLIB" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_RANLIB" = x; then + RANLIB=":" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + RANLIB=$ac_ct_RANLIB + fi +else + RANLIB="$ac_cv_prog_RANLIB" +fi + + +# Check whether --enable-m32 was given. +if test "${enable_m32+set}" = set; then : + enableval=$enable_m32; case "${enableval}" in + yes) + CFLAGS="${CFLAGS} -m32" + CXXFLAGS="${CXXFLAGS} -m32" + usem32=true + ;; + no) + usem32=false + ;; + *) + as_fn_error $? "bad value ${enableval} for --enable-m32" "$LINENO" 5 + ;; + esac +else + usem32=false +fi + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_GREP" || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_EGREP" || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +# Check whether --enable-largefile was given. +if test "${enable_largefile+set}" = set; then : + enableval=$enable_largefile; +fi + +if test "$enable_largefile" != no; then + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5 +$as_echo_n "checking for special C compiler options needed for large files... " >&6; } +if ${ac_cv_sys_largefile_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_sys_largefile_CC=no + if test "$GCC" != yes; then + ac_save_CC=$CC + while :; do + # IRIX 6.2 and later do not support large files by default, + # so use the C compiler's -n32 option if that helps. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF + if ac_fn_c_try_compile "$LINENO"; then : + break +fi +rm -f core conftest.err conftest.$ac_objext + CC="$CC -n32" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_largefile_CC=' -n32'; break +fi +rm -f core conftest.err conftest.$ac_objext + break + done + CC=$ac_save_CC + rm -f conftest.$ac_ext + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5 +$as_echo "$ac_cv_sys_largefile_CC" >&6; } + if test "$ac_cv_sys_largefile_CC" != no; then + CC=$CC$ac_cv_sys_largefile_CC + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5 +$as_echo_n "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; } +if ${ac_cv_sys_file_offset_bits+:} false; then : + $as_echo_n "(cached) " >&6 +else + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_file_offset_bits=no; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _FILE_OFFSET_BITS 64 +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_file_offset_bits=64; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cv_sys_file_offset_bits=unknown + break +done +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5 +$as_echo "$ac_cv_sys_file_offset_bits" >&6; } +case $ac_cv_sys_file_offset_bits in #( + no | unknown) ;; + *) +cat >>confdefs.h <<_ACEOF +#define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits +_ACEOF +;; +esac +rm -rf conftest* + if test $ac_cv_sys_file_offset_bits = unknown; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5 +$as_echo_n "checking for _LARGE_FILES value needed for large files... " >&6; } +if ${ac_cv_sys_large_files+:} false; then : + $as_echo_n "(cached) " >&6 +else + while :; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_large_files=no; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#define _LARGE_FILES 1 +#include + /* Check that off_t can represent 2**63 - 1 correctly. + We can't simply define LARGE_OFF_T to be 9223372036854775807, + since some C++ compilers masquerading as C compilers + incorrectly reject 9223372036854775807. */ +#define LARGE_OFF_T ((((off_t) 1 << 31) << 31) - 1 + (((off_t) 1 << 31) << 31)) + int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 + && LARGE_OFF_T % 2147483647 == 1) + ? 1 : -1]; +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_sys_large_files=1; break +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_cv_sys_large_files=unknown + break +done +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5 +$as_echo "$ac_cv_sys_large_files" >&6; } +case $ac_cv_sys_large_files in #( + no | unknown) ;; + *) +cat >>confdefs.h <<_ACEOF +#define _LARGE_FILES $ac_cv_sys_large_files +_ACEOF +;; +esac +rm -rf conftest* + fi + + +fi + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +ax_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on True64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS" >&5 +$as_echo_n "checking for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS... " >&6; } + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char pthread_join (); +int +main () +{ +return pthread_join (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ax_pthread_ok=yes +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_ok" >&5 +$as_echo "$ax_pthread_ok" >&6; } + if test x"$ax_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all, and "pthread-config" +# which is a program returning the flags for the Pth emulation library. + +ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# ... -mt is also the pthreads flag for HP/aCC +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case "${host_cpu}-${host_os}" in + *solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthreads/-mt/ + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + + ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" + ;; + + *-darwin*) + acx_pthread_flags="-pthread $acx_pthread_flags" + ;; +esac + +if test x"$ax_pthread_ok" = xno; then +for flag in $ax_pthread_flags; do + + case $flag in + none) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work without any flags" >&5 +$as_echo_n "checking whether pthreads work without any flags... " >&6; } + ;; + + -*) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether pthreads work with $flag" >&5 +$as_echo_n "checking whether pthreads work with $flag... " >&6; } + PTHREAD_CFLAGS="$flag" + ;; + + pthread-config) + # Extract the first word of "pthread-config", so it can be a program name with args. +set dummy pthread-config; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ax_pthread_config+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ax_pthread_config"; then + ac_cv_prog_ax_pthread_config="$ax_pthread_config" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ax_pthread_config="yes" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + + test -z "$ac_cv_prog_ax_pthread_config" && ac_cv_prog_ax_pthread_config="no" +fi +fi +ax_pthread_config=$ac_cv_prog_ax_pthread_config +if test -n "$ax_pthread_config"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_config" >&5 +$as_echo "$ax_pthread_config" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + if test x"$ax_pthread_config" = xno; then continue; fi + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for the pthreads library -l$flag" >&5 +$as_echo_n "checking for the pthreads library -l$flag... " >&6; } + PTHREAD_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + static void routine(void* a) {a=0;} + static void* start_routine(void* a) {return a;} +int +main () +{ +pthread_t th; pthread_attr_t attr; + pthread_join(th, 0); + pthread_attr_init(&attr); + pthread_cleanup_push(routine, 0); + pthread_create(&th,0,start_routine,0); + pthread_cleanup_pop(0); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ax_pthread_ok=yes +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_pthread_ok" >&5 +$as_echo "$ax_pthread_ok" >&6; } + if test "x$ax_pthread_ok" = xyes; then + break; + fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$ax_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for joinable pthread attribute" >&5 +$as_echo_n "checking for joinable pthread attribute... " >&6; } + attr_name=unknown + for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ +int attr=$attr; return attr; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + attr_name=$attr; break +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $attr_name" >&5 +$as_echo "$attr_name" >&6; } + if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then + +cat >>confdefs.h <<_ACEOF +#define PTHREAD_CREATE_JOINABLE $attr_name +_ACEOF + + fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking if more special flags are required for pthreads" >&5 +$as_echo_n "checking if more special flags are required for pthreads... " >&6; } + flag=no + case "${host_cpu}-${host_os}" in + *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; + *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; + esac + { $as_echo "$as_me:${as_lineno-$LINENO}: result: ${flag}" >&5 +$as_echo "${flag}" >&6; } + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + # More AIX lossage: must compile with xlc_r or cc_r + if test x"$GCC" != xyes; then + for ac_prog in xlc_r cc_r +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_PTHREAD_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$PTHREAD_CC"; then + ac_cv_prog_PTHREAD_CC="$PTHREAD_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_PTHREAD_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +PTHREAD_CC=$ac_cv_prog_PTHREAD_CC +if test -n "$PTHREAD_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PTHREAD_CC" >&5 +$as_echo "$PTHREAD_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$PTHREAD_CC" && break +done +test -n "$PTHREAD_CC" || PTHREAD_CC="${CC}" + + else + PTHREAD_CC=$CC + fi +else + PTHREAD_CC="$CC" +fi + + + + + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$ax_pthread_ok" = xyes; then + +$as_echo "#define HAVE_PTHREAD 1" >>confdefs.h + + : +else + ax_pthread_ok=no + +fi +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +for ac_header in a.out.h sys/mman.h sys/random.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + +for ac_func in arc4random getcontext getrandom memfd_create +do : + as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` +ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" +if eval test \"x\$"$as_ac_var"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 +_ACEOF + +fi +done + + if test "x$ac_cv_func_getcontext" = xyes; then + HAVE_GETCONTEXT_TRUE= + HAVE_GETCONTEXT_FALSE='#' +else + HAVE_GETCONTEXT_TRUE='#' + HAVE_GETCONTEXT_FALSE= +fi + + if test "x$ac_cv_func_memfd_create" = xyes; then + HAVE_MEMFD_CREATE_TRUE= + HAVE_MEMFD_CREATE_FALSE='#' +else + HAVE_MEMFD_CREATE_TRUE='#' + HAVE_MEMFD_CREATE_FALSE= +fi + + + + ax_cxx_compile_cxx11_required=true + ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + ac_success=no + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features by default" >&5 +$as_echo_n "checking whether $CXX supports C++11 features by default... " >&6; } +if ${ax_cv_cxx_compile_cxx11+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +// If the compiler admits that it is not ready for C++11, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201103L + +#error "This is not a C++11 compiler" + +#else + +namespace cxx11 +{ + + namespace test_static_assert + { + + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + } + + namespace test_final_override + { + + struct Base + { + virtual void f() {} + }; + + struct Derived : public Base + { + virtual void f() override {} + }; + + } + + namespace test_double_right_angle_brackets + { + + template < typename T > + struct check {}; + + typedef check single_type; + typedef check> double_type; + typedef check>> triple_type; + typedef check>>> quadruple_type; + + } + + namespace test_decltype + { + + int + f() + { + int a = 1; + decltype(a) b = 2; + return a + b; + } + + } + + namespace test_type_deduction + { + + template < typename T1, typename T2 > + struct is_same + { + static const bool value = false; + }; + + template < typename T > + struct is_same + { + static const bool value = true; + }; + + template < typename T1, typename T2 > + auto + add(T1 a1, T2 a2) -> decltype(a1 + a2) + { + return a1 + a2; + } + + int + test(const int c, volatile int v) + { + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == false, ""); + auto ac = c; + auto av = v; + auto sumi = ac + av + 'x'; + auto sumf = ac + av + 1.0; + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == true, ""); + return (sumf > 0.0) ? sumi : add(c, v); + } + + } + + namespace test_noexcept + { + + int f() { return 0; } + int g() noexcept { return 0; } + + static_assert(noexcept(f()) == false, ""); + static_assert(noexcept(g()) == true, ""); + + } + + namespace test_constexpr + { + + template < typename CharT > + unsigned long constexpr + strlen_c_r(const CharT *const s, const unsigned long acc) noexcept + { + return *s ? strlen_c_r(s + 1, acc + 1) : acc; + } + + template < typename CharT > + unsigned long constexpr + strlen_c(const CharT *const s) noexcept + { + return strlen_c_r(s, 0UL); + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("1") == 1UL, ""); + static_assert(strlen_c("example") == 7UL, ""); + static_assert(strlen_c("another\0example") == 7UL, ""); + + } + + namespace test_rvalue_references + { + + template < int N > + struct answer + { + static constexpr int value = N; + }; + + answer<1> f(int&) { return answer<1>(); } + answer<2> f(const int&) { return answer<2>(); } + answer<3> f(int&&) { return answer<3>(); } + + void + test() + { + int i = 0; + const int c = 0; + static_assert(decltype(f(i))::value == 1, ""); + static_assert(decltype(f(c))::value == 2, ""); + static_assert(decltype(f(0))::value == 3, ""); + } + + } + + namespace test_uniform_initialization + { + + struct test + { + static const int zero {}; + static const int one {1}; + }; + + static_assert(test::zero == 0, ""); + static_assert(test::one == 1, ""); + + } + + namespace test_lambdas + { + + void + test1() + { + auto lambda1 = [](){}; + auto lambda2 = lambda1; + lambda1(); + lambda2(); + } + + int + test2() + { + auto a = [](int i, int j){ return i + j; }(1, 2); + auto b = []() -> int { return '0'; }(); + auto c = [=](){ return a + b; }(); + auto d = [&](){ return c; }(); + auto e = [a, &b](int x) mutable { + const auto identity = [](int y){ return y; }; + for (auto i = 0; i < a; ++i) + a += b--; + return x + identity(a + b); + }(0); + return a + b + c + d + e; + } + + int + test3() + { + const auto nullary = [](){ return 0; }; + const auto unary = [](int x){ return x; }; + using nullary_t = decltype(nullary); + using unary_t = decltype(unary); + const auto higher1st = [](nullary_t f){ return f(); }; + const auto higher2nd = [unary](nullary_t f1){ + return [unary, f1](unary_t f2){ return f2(unary(f1())); }; + }; + return higher1st(nullary) + higher2nd(nullary)(unary); + } + + } + + namespace test_variadic_templates + { + + template + struct sum; + + template + struct sum + { + static constexpr auto value = N0 + sum::value; + }; + + template <> + struct sum<> + { + static constexpr auto value = 0; + }; + + static_assert(sum<>::value == 0, ""); + static_assert(sum<1>::value == 1, ""); + static_assert(sum<23>::value == 23, ""); + static_assert(sum<1, 2>::value == 3, ""); + static_assert(sum<5, 5, 11>::value == 21, ""); + static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); + + } + + // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae + // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function + // because of this. + namespace test_template_alias_sfinae + { + + struct foo {}; + + template + using member = typename T::member_type; + + template + void func(...) {} + + template + void func(member*) {} + + void test(); + + void test() { func(0); } + + } + +} // namespace cxx11 + +#endif // __cplusplus >= 201103L + + + +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ax_cv_cxx_compile_cxx11=yes +else + ax_cv_cxx_compile_cxx11=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_cxx_compile_cxx11" >&5 +$as_echo "$ax_cv_cxx_compile_cxx11" >&6; } + if test x$ax_cv_cxx_compile_cxx11 = xyes; then + ac_success=yes + fi + + + + if test x$ac_success = xno; then + for switch in -std=c++11 -std=c++0x +std=c++11 "-h std=c++11"; do + cachevar=`$as_echo "ax_cv_cxx_compile_cxx11_$switch" | $as_tr_sh` + { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CXX supports C++11 features with $switch" >&5 +$as_echo_n "checking whether $CXX supports C++11 features with $switch... " >&6; } +if eval \${$cachevar+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $switch" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + +// If the compiler admits that it is not ready for C++11, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201103L + +#error "This is not a C++11 compiler" + +#else + +namespace cxx11 +{ + + namespace test_static_assert + { + + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + } + + namespace test_final_override + { + + struct Base + { + virtual void f() {} + }; + + struct Derived : public Base + { + virtual void f() override {} + }; + + } + + namespace test_double_right_angle_brackets + { + + template < typename T > + struct check {}; + + typedef check single_type; + typedef check> double_type; + typedef check>> triple_type; + typedef check>>> quadruple_type; + + } + + namespace test_decltype + { + + int + f() + { + int a = 1; + decltype(a) b = 2; + return a + b; + } + + } + + namespace test_type_deduction + { + + template < typename T1, typename T2 > + struct is_same + { + static const bool value = false; + }; + + template < typename T > + struct is_same + { + static const bool value = true; + }; + + template < typename T1, typename T2 > + auto + add(T1 a1, T2 a2) -> decltype(a1 + a2) + { + return a1 + a2; + } + + int + test(const int c, volatile int v) + { + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == false, ""); + auto ac = c; + auto av = v; + auto sumi = ac + av + 'x'; + auto sumf = ac + av + 1.0; + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == true, ""); + return (sumf > 0.0) ? sumi : add(c, v); + } + + } + + namespace test_noexcept + { + + int f() { return 0; } + int g() noexcept { return 0; } + + static_assert(noexcept(f()) == false, ""); + static_assert(noexcept(g()) == true, ""); + + } + + namespace test_constexpr + { + + template < typename CharT > + unsigned long constexpr + strlen_c_r(const CharT *const s, const unsigned long acc) noexcept + { + return *s ? strlen_c_r(s + 1, acc + 1) : acc; + } + + template < typename CharT > + unsigned long constexpr + strlen_c(const CharT *const s) noexcept + { + return strlen_c_r(s, 0UL); + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("1") == 1UL, ""); + static_assert(strlen_c("example") == 7UL, ""); + static_assert(strlen_c("another\0example") == 7UL, ""); + + } + + namespace test_rvalue_references + { + + template < int N > + struct answer + { + static constexpr int value = N; + }; + + answer<1> f(int&) { return answer<1>(); } + answer<2> f(const int&) { return answer<2>(); } + answer<3> f(int&&) { return answer<3>(); } + + void + test() + { + int i = 0; + const int c = 0; + static_assert(decltype(f(i))::value == 1, ""); + static_assert(decltype(f(c))::value == 2, ""); + static_assert(decltype(f(0))::value == 3, ""); + } + + } + + namespace test_uniform_initialization + { + + struct test + { + static const int zero {}; + static const int one {1}; + }; + + static_assert(test::zero == 0, ""); + static_assert(test::one == 1, ""); + + } + + namespace test_lambdas + { + + void + test1() + { + auto lambda1 = [](){}; + auto lambda2 = lambda1; + lambda1(); + lambda2(); + } + + int + test2() + { + auto a = [](int i, int j){ return i + j; }(1, 2); + auto b = []() -> int { return '0'; }(); + auto c = [=](){ return a + b; }(); + auto d = [&](){ return c; }(); + auto e = [a, &b](int x) mutable { + const auto identity = [](int y){ return y; }; + for (auto i = 0; i < a; ++i) + a += b--; + return x + identity(a + b); + }(0); + return a + b + c + d + e; + } + + int + test3() + { + const auto nullary = [](){ return 0; }; + const auto unary = [](int x){ return x; }; + using nullary_t = decltype(nullary); + using unary_t = decltype(unary); + const auto higher1st = [](nullary_t f){ return f(); }; + const auto higher2nd = [unary](nullary_t f1){ + return [unary, f1](unary_t f2){ return f2(unary(f1())); }; + }; + return higher1st(nullary) + higher2nd(nullary)(unary); + } + + } + + namespace test_variadic_templates + { + + template + struct sum; + + template + struct sum + { + static constexpr auto value = N0 + sum::value; + }; + + template <> + struct sum<> + { + static constexpr auto value = 0; + }; + + static_assert(sum<>::value == 0, ""); + static_assert(sum<1>::value == 1, ""); + static_assert(sum<23>::value == 23, ""); + static_assert(sum<1, 2>::value == 3, ""); + static_assert(sum<5, 5, 11>::value == 21, ""); + static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); + + } + + // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae + // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function + // because of this. + namespace test_template_alias_sfinae + { + + struct foo {}; + + template + using member = typename T::member_type; + + template + void func(...) {} + + template + void func(member*) {} + + void test(); + + void test() { func(0); } + + } + +} // namespace cxx11 + +#endif // __cplusplus >= 201103L + + + +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + eval $cachevar=yes +else + eval $cachevar=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CXXFLAGS="$ac_save_CXXFLAGS" +fi +eval ac_res=\$$cachevar + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + if eval test x\$$cachevar = xyes; then + CXXFLAGS="$CXXFLAGS $switch" + ac_success=yes + break + fi + done + fi + ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + if test x$ax_cxx_compile_cxx11_required = xtrue; then + if test x$ac_success = xno; then + as_fn_error $? "*** A compiler with support for C++11 language features is required." "$LINENO" 5 + fi + else + if test x$ac_success = xno; then + HAVE_CXX11=0 + { $as_echo "$as_me:${as_lineno-$LINENO}: No compiler with C++11 support was found" >&5 +$as_echo "$as_me: No compiler with C++11 support was found" >&6;} + else + HAVE_CXX11=1 + +$as_echo "#define HAVE_CXX11 1" >>confdefs.h + + fi + + + fi + + +WARN_CXXFLAGS= +ac_ext=cpp +ac_cpp='$CXXCPP $CPPFLAGS' +ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_cxx_compiler_gnu + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts -Werror=unknown-warning-option" >&5 +$as_echo_n "checking whether C++ compiler accepts -Werror=unknown-warning-option... " >&6; } +if ${ax_cv_check_cxxflags___Werror_unknown_warning_option+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CXXFLAGS + CXXFLAGS="$CXXFLAGS -Werror=unknown-warning-option" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + ax_cv_check_cxxflags___Werror_unknown_warning_option=yes +else + ax_cv_check_cxxflags___Werror_unknown_warning_option=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CXXFLAGS=$ax_check_save_flags +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ax_cv_check_cxxflags___Werror_unknown_warning_option" >&5 +$as_echo "$ax_cv_check_cxxflags___Werror_unknown_warning_option" >&6; } +if test "x$ax_cv_check_cxxflags___Werror_unknown_warning_option" = xyes; then : + + ax_compiler_flags_test="-Werror=unknown-warning-option" + +else + + ax_compiler_flags_test="" + +fi + + + + + +for flag in -Wmissing-braces -Wnon-virtual-dtor -Woverloaded-virtual -Wreorder -Wsign-compare -Wunused-local-typedefs -Wunused-variable -Wvla ; do + as_CACHEVAR=`$as_echo "ax_cv_check_cxxflags_${ax_compiler_flags_test}_$flag" | $as_tr_sh` +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether C++ compiler accepts $flag" >&5 +$as_echo_n "checking whether C++ compiler accepts $flag... " >&6; } +if eval \${$as_CACHEVAR+:} false; then : + $as_echo_n "(cached) " >&6 +else + + ax_check_save_flags=$CXXFLAGS + CXXFLAGS="$CXXFLAGS ${ax_compiler_flags_test} $flag" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_cxx_try_compile "$LINENO"; then : + eval "$as_CACHEVAR=yes" +else + eval "$as_CACHEVAR=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + CXXFLAGS=$ax_check_save_flags +fi +eval ac_res=\$$as_CACHEVAR + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +if eval test \"x\$"$as_CACHEVAR"\" = x"yes"; then : + +if ${WARN_CXXFLAGS+:} false; then : + + case " $WARN_CXXFLAGS " in #( + *" $flag "*) : + { { $as_echo "$as_me:${as_lineno-$LINENO}: : WARN_CXXFLAGS already contains \$flag"; } >&5 + (: WARN_CXXFLAGS already contains $flag) 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } ;; #( + *) : + + as_fn_append WARN_CXXFLAGS " $flag" + { { $as_echo "$as_me:${as_lineno-$LINENO}: : WARN_CXXFLAGS=\"\$WARN_CXXFLAGS\""; } >&5 + (: WARN_CXXFLAGS="$WARN_CXXFLAGS") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + ;; +esac + +else + + WARN_CXXFLAGS=$flag + { { $as_echo "$as_me:${as_lineno-$LINENO}: : WARN_CXXFLAGS=\"\$WARN_CXXFLAGS\""; } >&5 + (: WARN_CXXFLAGS="$WARN_CXXFLAGS") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + +fi + +else + : +fi + +done + +as_fn_append WARN_CXXFLAGS " -Werror" +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for O_CLOEXEC defined in fcntl.h" >&5 +$as_echo_n "checking for O_CLOEXEC defined in fcntl.h... " >&6; } +if ${ac_cv_defined_O_CLOEXEC_fcntl_h+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main () +{ + + #ifdef O_CLOEXEC + int ok; + #else + choke me + #endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_defined_O_CLOEXEC_fcntl_h=yes +else + ac_cv_defined_O_CLOEXEC_fcntl_h=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_defined_O_CLOEXEC_fcntl_h" >&5 +$as_echo "$ac_cv_defined_O_CLOEXEC_fcntl_h" >&6; } +if test $ac_cv_defined_O_CLOEXEC_fcntl_h != "no"; then : + +else + +$as_echo "#define O_CLOEXEC 0" >>confdefs.h + +fi + +# Only build Linux client libs when compiling for Linux +case $host in + *-*-linux* | *-android* ) + LINUX_HOST=true + ;; +esac + if test x$LINUX_HOST = xtrue; then + LINUX_HOST_TRUE= + LINUX_HOST_FALSE='#' +else + LINUX_HOST_TRUE='#' + LINUX_HOST_FALSE= +fi + + +# Only use Android support headers when compiling for Android +case $host in + *-android*) + ANDROID_HOST=true + ;; +esac + if test x$ANDROID_HOST = xtrue; then + ANDROID_HOST_TRUE= + ANDROID_HOST_FALSE='#' +else + ANDROID_HOST_TRUE='#' + ANDROID_HOST_FALSE= +fi + + +# Some tools (like mac ones) only support x86 currently. +case $host_cpu in + i?86|x86_64) + X86_HOST=true + ;; +esac + if test x$X86_HOST = xtrue; then + X86_HOST_TRUE= + X86_HOST_FALSE='#' +else + X86_HOST_TRUE='#' + X86_HOST_FALSE= +fi + + +# Check whether --enable-processor was given. +if test "${enable_processor+set}" = set; then : + enableval=$enable_processor; case "${enableval}" in + yes) + disable_processor=false + ;; + no) + disable_processor=true + ;; + *) + as_fn_error $? "bad value ${enableval} for --disable-processor" "$LINENO" 5 + ;; + esac +else + disable_processor=false +fi + + if test x$disable_processor = xtrue; then + DISABLE_PROCESSOR_TRUE= + DISABLE_PROCESSOR_FALSE='#' +else + DISABLE_PROCESSOR_TRUE='#' + DISABLE_PROCESSOR_FALSE= +fi + + +# Check whether --enable-tools was given. +if test "${enable_tools+set}" = set; then : + enableval=$enable_tools; case "${enableval}" in + yes) + disable_tools=false + ;; + no) + disable_tools=true + ;; + *) + as_fn_error $? "bad value ${enableval} for --disable-tools" "$LINENO" 5 + ;; + esac +else + disable_tools=false +fi + + if test x$disable_tools = xtrue; then + DISABLE_TOOLS_TRUE= + DISABLE_TOOLS_FALSE='#' +else + DISABLE_TOOLS_TRUE='#' + DISABLE_TOOLS_FALSE= +fi + + +if test x$LINUX_HOST = xfalse -a x$disable_processor = xtrue -a x$disable_tools = xtrue; then + as_fn_error $? "--disable-processor and --disable-tools were specified, and not building for Linux. Nothing to build!" "$LINENO" 5 +fi + +# Check whether --enable-system-test-libs was given. +if test "${enable_system_test_libs+set}" = set; then : + enableval=$enable_system_test_libs; case "${enableval}" in + yes) + system_test_libs=true + ;; + no) + system_test_libs=false + ;; + *) + as_fn_error $? "bad value ${enableval} for --enable-system-test-libs" "$LINENO" 5 + ;; + esac +else + system_test_libs=false +fi + + if test x$system_test_libs = xtrue; then + SYSTEM_TEST_LIBS_TRUE= + SYSTEM_TEST_LIBS_FALSE='#' +else + SYSTEM_TEST_LIBS_TRUE='#' + SYSTEM_TEST_LIBS_FALSE= +fi + + + + + + +if test x$system_test_libs = xtrue; then + : "${GMOCK_CFLAGS:=-pthread}" + : "${GMOCK_LIBS:=-lgmock -lgtest -pthread -lpthread}" + : "${GTEST_CFLAGS:=-pthread}" + : "${GTEST_LIBS:=-lgtest -pthread -lpthread}" +fi + +# Check whether --enable-selftest was given. +if test "${enable_selftest+set}" = set; then : + enableval=$enable_selftest; case "${enableval}" in + yes) + selftest=true + ;; + no) + selftest=false + ;; + *) + as_fn_error $? "bad value ${enableval} for --enable-selftest" "$LINENO" 5 + ;; + esac +else + selftest=false +fi + + if test x$selftest = xtrue; then + SELFTEST_TRUE= + SELFTEST_FALSE='#' +else + SELFTEST_TRUE='#' + SELFTEST_FALSE= +fi + + + +# Check whether --with-rustc-demangle was given. +if test "${with_rustc_demangle+set}" = set; then : + withval=$with_rustc_demangle; case "${withval}" in + yes) + as_fn_error $? "You must pass the path to the rustc-demangle crate for --with-rustc-demangle" "$LINENO" 5 + ;; + no) + rustc_demangle=false + ;; + *) + if ! test -f "${withval}/Cargo.toml"; then + as_fn_error $? "You must pass the path to the rustc-demangle crate for --with-rustc-demangle" "$LINENO" 5 + fi + RUSTC_DEMANGLE_CFLAGS="-DHAVE_RUSTC_DEMANGLE -I${withval}/crates/capi/include" + RUSTC_DEMANGLE_LIBS="-L${withval}/target/release -lrustc_demangle -lpthread -ldl" + ;; + esac +else + rustc_demangle=false +fi + + + + + +# Check whether --with-tests-as-root was given. +if test "${with_tests_as_root+set}" = set; then : + withval=$with_tests_as_root; case "${withval}" in + yes) + tests_as_root=true + ;; + no) + tests_as_root=false + ;; + *) + as_fn_error $? "--with-tests-as-root can only be \"yes\" or \"no\"" "$LINENO" 5 + ;; + esac +else + tests_as_root=false +fi + + if test x$tests_as_root = xtrue; then + TESTS_AS_ROOT_TRUE= + TESTS_AS_ROOT_FALSE='#' +else + TESTS_AS_ROOT_TRUE='#' + TESTS_AS_ROOT_FALSE= +fi + + +ac_config_files="$ac_config_files breakpad.pc breakpad-client.pc Makefile" + + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking that generated files are newer than configure" >&5 +$as_echo_n "checking that generated files are newer than configure... " >&6; } + if test -n "$am_sleep_pid"; then + # Hide warnings about reused PIDs. + wait $am_sleep_pid 2>/dev/null + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: result: done" >&5 +$as_echo "done" >&6; } + if test -n "$EXEEXT"; then + am__EXEEXT_TRUE= + am__EXEEXT_FALSE='#' +else + am__EXEEXT_TRUE='#' + am__EXEEXT_FALSE= +fi + +if test -z "${MAINTAINER_MODE_TRUE}" && test -z "${MAINTAINER_MODE_FALSE}"; then + as_fn_error $? "conditional \"MAINTAINER_MODE\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${AMDEP_TRUE}" && test -z "${AMDEP_FALSE}"; then + as_fn_error $? "conditional \"AMDEP\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCCAS_TRUE}" && test -z "${am__fastdepCCAS_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCCAS\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCC_TRUE}" && test -z "${am__fastdepCC_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCC\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${am__fastdepCXX_TRUE}" && test -z "${am__fastdepCXX_FALSE}"; then + as_fn_error $? "conditional \"am__fastdepCXX\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${HAVE_GETCONTEXT_TRUE}" && test -z "${HAVE_GETCONTEXT_FALSE}"; then + as_fn_error $? "conditional \"HAVE_GETCONTEXT\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${HAVE_MEMFD_CREATE_TRUE}" && test -z "${HAVE_MEMFD_CREATE_FALSE}"; then + as_fn_error $? "conditional \"HAVE_MEMFD_CREATE\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${LINUX_HOST_TRUE}" && test -z "${LINUX_HOST_FALSE}"; then + as_fn_error $? "conditional \"LINUX_HOST\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${ANDROID_HOST_TRUE}" && test -z "${ANDROID_HOST_FALSE}"; then + as_fn_error $? "conditional \"ANDROID_HOST\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${X86_HOST_TRUE}" && test -z "${X86_HOST_FALSE}"; then + as_fn_error $? "conditional \"X86_HOST\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${DISABLE_PROCESSOR_TRUE}" && test -z "${DISABLE_PROCESSOR_FALSE}"; then + as_fn_error $? "conditional \"DISABLE_PROCESSOR\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${DISABLE_TOOLS_TRUE}" && test -z "${DISABLE_TOOLS_FALSE}"; then + as_fn_error $? "conditional \"DISABLE_TOOLS\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${SYSTEM_TEST_LIBS_TRUE}" && test -z "${SYSTEM_TEST_LIBS_FALSE}"; then + as_fn_error $? "conditional \"SYSTEM_TEST_LIBS\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${SELFTEST_TRUE}" && test -z "${SELFTEST_FALSE}"; then + as_fn_error $? "conditional \"SELFTEST\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi +if test -z "${TESTS_AS_ROOT_TRUE}" && test -z "${TESTS_AS_ROOT_FALSE}"; then + as_fn_error $? "conditional \"TESTS_AS_ROOT\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by breakpad $as_me 0.1, which was +generated by GNU Autoconf 2.69. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" +config_commands="$ac_config_commands" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Configuration commands: +$config_commands + +Report bugs to ." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +breakpad config.status 0.1 +configured by $0, generated by GNU Autoconf 2.69, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2012 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +INSTALL='$INSTALL' +MKDIR_P='$MKDIR_P' +AWK='$AWK' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# +# INIT-COMMANDS +# +AMDEP_TRUE="$AMDEP_TRUE" MAKE="${MAKE-make}" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "src/config.h") CONFIG_HEADERS="$CONFIG_HEADERS src/config.h" ;; + "depfiles") CONFIG_COMMANDS="$CONFIG_COMMANDS depfiles" ;; + "breakpad.pc") CONFIG_FILES="$CONFIG_FILES breakpad.pc" ;; + "breakpad-client.pc") CONFIG_FILES="$CONFIG_FILES breakpad-client.pc" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files + test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers + test "${CONFIG_COMMANDS+set}" = set || CONFIG_COMMANDS=$config_commands +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS :C $CONFIG_COMMANDS" +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + + case $INSTALL in + [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; + *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; + esac + ac_MKDIR_P=$MKDIR_P + case $MKDIR_P in + [\\/$]* | ?:[\\/]* ) ;; + */*) ac_MKDIR_P=$ac_top_build_prefix$MKDIR_P ;; + esac +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +s&@INSTALL@&$ac_INSTALL&;t t +s&@MKDIR_P@&$ac_MKDIR_P&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +$as_echo "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + fi + else + $as_echo "/* $configure_input */" \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 + fi +# Compute "$ac_file"'s index in $config_headers. +_am_arg="$ac_file" +_am_stamp_count=1 +for _am_header in $config_headers :; do + case $_am_header in + $_am_arg | $_am_arg:* ) + break ;; + * ) + _am_stamp_count=`expr $_am_stamp_count + 1` ;; + esac +done +echo "timestamp for $_am_arg" >`$as_dirname -- "$_am_arg" || +$as_expr X"$_am_arg" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$_am_arg" : 'X\(//\)[^/]' \| \ + X"$_am_arg" : 'X\(//\)$' \| \ + X"$_am_arg" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$_am_arg" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'`/stamp-h$_am_stamp_count + ;; + + :C) { $as_echo "$as_me:${as_lineno-$LINENO}: executing $ac_file commands" >&5 +$as_echo "$as_me: executing $ac_file commands" >&6;} + ;; + esac + + + case $ac_file$ac_mode in + "depfiles":C) test x"$AMDEP_TRUE" != x"" || { + # Older Autoconf quotes --file arguments for eval, but not when files + # are listed without --file. Let's play safe and only enable the eval + # if we detect the quoting. + # TODO: see whether this extra hack can be removed once we start + # requiring Autoconf 2.70 or later. + case $CONFIG_FILES in #( + *\'*) : + eval set x "$CONFIG_FILES" ;; #( + *) : + set x $CONFIG_FILES ;; #( + *) : + ;; +esac + shift + # Used to flag and report bootstrapping failures. + am_rc=0 + for am_mf + do + # Strip MF so we end up with the name of the file. + am_mf=`$as_echo "$am_mf" | sed -e 's/:.*$//'` + # Check whether this is an Automake generated Makefile which includes + # dependency-tracking related rules and includes. + # Grep'ing the whole file directly is not great: AIX grep has a line + # limit of 2048, but all sed's we know have understand at least 4000. + sed -n 's,^am--depfiles:.*,X,p' "$am_mf" | grep X >/dev/null 2>&1 \ + || continue + am_dirpart=`$as_dirname -- "$am_mf" || +$as_expr X"$am_mf" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$am_mf" : 'X\(//\)[^/]' \| \ + X"$am_mf" : 'X\(//\)$' \| \ + X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$am_mf" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + am_filepart=`$as_basename -- "$am_mf" || +$as_expr X/"$am_mf" : '.*/\([^/][^/]*\)/*$' \| \ + X"$am_mf" : 'X\(//\)$' \| \ + X"$am_mf" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$am_mf" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + { echo "$as_me:$LINENO: cd "$am_dirpart" \ + && sed -e '/# am--include-marker/d' "$am_filepart" \ + | $MAKE -f - am--depfiles" >&5 + (cd "$am_dirpart" \ + && sed -e '/# am--include-marker/d' "$am_filepart" \ + | $MAKE -f - am--depfiles) >&5 2>&5 + ac_status=$? + echo "$as_me:$LINENO: \$? = $ac_status" >&5 + (exit $ac_status); } || am_rc=$? + done + if test $am_rc -ne 0; then + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "Something went wrong bootstrapping makefile fragments + for automatic dependency tracking. If GNU make was not used, consider + re-running the configure script with MAKE=\"gmake\" (or whatever is + necessary). You can also try re-running configure with the + '--disable-dependency-tracking' option to at least be able to build + the package (albeit without support for automatic dependency tracking). +See \`config.log' for more details" "$LINENO" 5; } + fi + { am_dirpart=; unset am_dirpart;} + { am_filepart=; unset am_filepart;} + { am_mf=; unset am_mf;} + { am_rc=; unset am_rc;} + rm -f conftest-deps.mk +} + ;; + + esac +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + diff --git a/shared/sentry/external/breakpad/configure.ac b/shared/sentry/external/breakpad/configure.ac new file mode 100644 index 000000000..20fb07532 --- /dev/null +++ b/shared/sentry/external/breakpad/configure.ac @@ -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. + + +AC_PREREQ(2.64) + +AC_INIT(breakpad, 0.1, google-breakpad-dev@googlegroups.com) +dnl Sanity check: the argument is just a file that should exist. +AC_CONFIG_SRCDIR(README.md) +AC_CONFIG_AUX_DIR(autotools) +AC_CONFIG_MACRO_DIR([m4]) +AC_CANONICAL_HOST + +AM_INIT_AUTOMAKE(subdir-objects tar-ustar 1.11.1) +AM_CONFIG_HEADER(src/config.h) +AM_MAINTAINER_MODE + +AM_PROG_AR +AM_PROG_AS +AC_PROG_CC +AM_PROG_CC_C_O +AC_PROG_CPP +AC_PROG_CXX +AC_PROG_RANLIB + +dnl This must come before all the feature tests below. +AC_ARG_ENABLE(m32, + AS_HELP_STRING([--enable-m32], + [Compile/build with -m32] + [(default is no)]), + [case "${enableval}" in + yes) + CFLAGS="${CFLAGS} -m32" + CXXFLAGS="${CXXFLAGS} -m32" + usem32=true + ;; + no) + usem32=false + ;; + *) + AC_MSG_ERROR(bad value ${enableval} for --enable-m32) + ;; + esac], + [usem32=false]) + +AC_HEADER_STDC +AC_SYS_LARGEFILE +AX_PTHREAD +AC_CHECK_HEADERS([a.out.h sys/mman.h sys/random.h]) +AC_CHECK_FUNCS([arc4random getcontext getrandom memfd_create]) +AM_CONDITIONAL([HAVE_GETCONTEXT], [test "x$ac_cv_func_getcontext" = xyes]) +AM_CONDITIONAL([HAVE_MEMFD_CREATE], [test "x$ac_cv_func_memfd_create" = xyes]) + +AX_CXX_COMPILE_STDCXX(11, noext, mandatory) + +dnl Test supported warning flags. +WARN_CXXFLAGS= +dnl This warning flag is used by clang. Its default behavior is to warn when +dnl given an unknown flag rather than error out. +AC_LANG_PUSH([C++]) +AX_CHECK_COMPILE_FLAG([-Werror=unknown-warning-option],[ + ax_compiler_flags_test="-Werror=unknown-warning-option" +],[ + ax_compiler_flags_test="" +]) +AX_APPEND_COMPILE_FLAGS(m4_flatten([ + -Wmissing-braces + -Wnon-virtual-dtor + -Woverloaded-virtual + -Wreorder + -Wsign-compare + -Wunused-local-typedefs + -Wunused-variable + -Wvla +]), [WARN_CXXFLAGS], [${ax_compiler_flags_test}]) +AS_VAR_APPEND([WARN_CXXFLAGS], " -Werror") +AC_LANG_POP([C++]) +AC_SUBST([WARN_CXXFLAGS]) + +dnl Test support for O_CLOEXEC +AX_CHECK_DEFINE([fcntl.h], [O_CLOEXEC], [], + [AC_DEFINE([O_CLOEXEC], [0], [Fallback definition for old systems])]) + +# Only build Linux client libs when compiling for Linux +case $host in + *-*-linux* | *-android* ) + LINUX_HOST=true + ;; +esac +AM_CONDITIONAL(LINUX_HOST, test x$LINUX_HOST = xtrue) + +# Only use Android support headers when compiling for Android +case $host in + *-android*) + ANDROID_HOST=true + ;; +esac +AM_CONDITIONAL(ANDROID_HOST, test x$ANDROID_HOST = xtrue) + +# Some tools (like mac ones) only support x86 currently. +case $host_cpu in + i?86|x86_64) + X86_HOST=true + ;; +esac +AM_CONDITIONAL(X86_HOST, test x$X86_HOST = xtrue) + +AC_ARG_ENABLE(processor, + AS_HELP_STRING([--disable-processor], + [Don't build processor library] + [(default is no)]), + [case "${enableval}" in + yes) + disable_processor=false + ;; + no) + disable_processor=true + ;; + *) + AC_MSG_ERROR(bad value ${enableval} for --disable-processor) + ;; + esac], + [disable_processor=false]) +AM_CONDITIONAL(DISABLE_PROCESSOR, test x$disable_processor = xtrue) + +AC_ARG_ENABLE(tools, + AS_HELP_STRING([--disable-tools], + [Don't build tool binaries] + [(default is no)]), + [case "${enableval}" in + yes) + disable_tools=false + ;; + no) + disable_tools=true + ;; + *) + AC_MSG_ERROR(bad value ${enableval} for --disable-tools) + ;; + esac], + [disable_tools=false]) +AM_CONDITIONAL(DISABLE_TOOLS, test x$disable_tools = xtrue) + +if test x$LINUX_HOST = xfalse -a x$disable_processor = xtrue -a x$disable_tools = xtrue; then + AC_MSG_ERROR([--disable-processor and --disable-tools were specified, and not building for Linux. Nothing to build!]) +fi + +AC_ARG_ENABLE(system-test-libs, + AS_HELP_STRING([--enable-system-test-libs], + [Use gtest/gmock/etc... from the system instead ] + [of the local copies (default is local)]), + [case "${enableval}" in + yes) + system_test_libs=true + ;; + no) + system_test_libs=false + ;; + *) + AC_MSG_ERROR(bad value ${enableval} for --enable-system-test-libs) + ;; + esac], + [system_test_libs=false]) +AM_CONDITIONAL(SYSTEM_TEST_LIBS, test x$system_test_libs = xtrue) + +AC_ARG_VAR([GMOCK_CFLAGS], [Compiler flags for gmock]) +AC_ARG_VAR([GMOCK_LIBS], [Linker flags for gmock]) +AC_ARG_VAR([GTEST_CFLAGS], [Compiler flags for gtest]) +AC_ARG_VAR([GTEST_LIBS], [Linker flags for gtest]) +if test x$system_test_libs = xtrue; then + : "${GMOCK_CFLAGS:=-pthread}" + : "${GMOCK_LIBS:=-lgmock -lgtest -pthread -lpthread}" + : "${GTEST_CFLAGS:=-pthread}" + : "${GTEST_LIBS:=-lgtest -pthread -lpthread}" +fi + +AC_ARG_ENABLE(selftest, + AS_HELP_STRING([--enable-selftest], + [Run extra tests with "make check" ] + [(may conflict with optimizations) ] + [(default is no)]), + [case "${enableval}" in + yes) + selftest=true + ;; + no) + selftest=false + ;; + *) + AC_MSG_ERROR(bad value ${enableval} for --enable-selftest) + ;; + esac], + [selftest=false]) +AM_CONDITIONAL(SELFTEST, test x$selftest = xtrue) + +AC_ARG_WITH(rustc-demangle, + AS_HELP_STRING([--with-rustc-demangle=/path/to/rustc-demangle], + [Link against the rustc-demangle library] + [to demangle Rust language symbols during] + [symbol dumping (default is no)] + [Pass the path to the crate root.]), + [case "${withval}" in + yes) + AC_MSG_ERROR(You must pass the path to the rustc-demangle crate for --with-rustc-demangle) + ;; + no) + rustc_demangle=false + ;; + *) + if ! test -f "${withval}/Cargo.toml"; then + AC_MSG_ERROR(You must pass the path to the rustc-demangle crate for --with-rustc-demangle) + fi + RUSTC_DEMANGLE_CFLAGS="-DHAVE_RUSTC_DEMANGLE -I${withval}/crates/capi/include" + RUSTC_DEMANGLE_LIBS="-L${withval}/target/release -lrustc_demangle -lpthread -ldl" + ;; + esac], + [rustc_demangle=false]) +AC_ARG_VAR([RUSTC_DEMANGLE_CFLAGS], [Compiler flags for rustc-demangle]) +AC_ARG_VAR([RUSTC_DEMANGLE_LIBS], [Linker flags for rustc-demangle]) + +AC_ARG_WITH(tests-as-root, + AS_HELP_STRING([--with-tests-as-root], + [Run the tests as root. Use this on platforms] + [like travis-ci.org that require root privileges] + [to use ptrace (default is no)]), + [case "${withval}" in + yes) + tests_as_root=true + ;; + no) + tests_as_root=false + ;; + *) + AC_MSG_ERROR(--with-tests-as-root can only be "yes" or "no") + ;; + esac], + [tests_as_root=false]) +AM_CONDITIONAL(TESTS_AS_ROOT, test x$tests_as_root = xtrue) + +AC_CONFIG_FILES(m4_flatten([ + breakpad.pc + breakpad-client.pc + Makefile +])) + +AC_OUTPUT diff --git a/shared/sentry/external/breakpad/default.xml b/shared/sentry/external/breakpad/default.xml new file mode 100644 index 000000000..3c157012a --- /dev/null +++ b/shared/sentry/external/breakpad/default.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/shared/sentry/external/breakpad/docs/OWNERS b/shared/sentry/external/breakpad/docs/OWNERS new file mode 100644 index 000000000..72e8ffc0d --- /dev/null +++ b/shared/sentry/external/breakpad/docs/OWNERS @@ -0,0 +1 @@ +* diff --git a/shared/sentry/external/breakpad/docs/breakpad.png b/shared/sentry/external/breakpad/docs/breakpad.png new file mode 100644 index 0000000000000000000000000000000000000000..20bc7566ce853d7d6bbe4e8af820a43e09095c9f GIT binary patch literal 84153 zcmY(r1yq||&@CKXin~joXmNK7TBJyeyA^kLDFk=70tJdgafcGDSPK+)OL2F*;r;G+ z*Z;F%k%T-CGiTyQY(syO*h}CBVzei`~Z2&duD^ z*^=GK)jH=`lnel%0Vsl`w7hc<^St~CJno0qQdU{faBNY#h^3fWXhOdR?^J!ApA7W9 zzOWXW^z>cz@wFZb1nMvWuS25=k@gJrKGV`7DyUuG4DqDot=PX;_`y8+aJ4vhJmzn` zo2BBp%QGoD;4DbNi-ggS3MU=v5Lv_&J@r*ESV>;9O@|pk5`l}E3vfqXgePsj!Jwh+ zDs6FR3865_xLb7aTSNEXau$r|P34j^7aGb86+lOo38XJ{AOWsWXlk}AdfUe~2x z%l#FE-iBmz>l_*0y}6LBG<5_jQQtlIFut%osGg`jE33r?1nLSG`UU2lK0#}gdxI`^^1QzYMX ziDdk6nsarkOoyKYU<*>jKY3WH=@>c;QpXNRVhjA_wQJAj7hc z+|LONISB3nFkf31M)ZL_k1~_f2DaG^|0N}Fx{yc1g>y>zAvYI-)C`XOt;P0{n}sAjKDXJGHJiW<30YaA29OU_x2-)QEmb3NDvY zPVuVtR?p12^S$1+;P`9Hp+?8?t1uKdTPue0KVGni;2c_h5&zQ}Q%`N>88%~cP< z#@zv-FsD9Y!chTg_%OC=dzB;m)sH>G0>2Bu1~&o@lEV?e4)dxAG3EmeDOdo5Zf(Pv z0BPxKX}Bp#d9O(C=Z8nfkr7c&S9FZ#;DM>)^ zO^C9cQO>;1F{yYi>-`m-@>XzK1cdY7>Wh8*wOH6ki5|#J!Bbj4(#QK7optMe&QjRT zOT?QulZ0Q?_%Y~+6l4bKUeiJ_3Q>DT(#AS-P#5GV*> z$l!O0nUD+~^@`FTCe5EZujX@qiA<2lpLb@%DdENX`Lmc*=s8fefU5xf9nsE{_q5cM z1Uqx1BgCPS9fLdUNHeL*NLHOd)&`wKvO*W@@+-qKT7;?So5w%d6UxdKt25=7YVY3^ zeN-~HhNcgsW*`yeueJAoC^FYLuwT>dV&FMiq|SW!MR4vWpxbZ)FAPVF{3EWI2x2AF z)Yv|s{$JQa5NVb zBDmnZ$FlSPlwotkQ`gr2RO*ctKXPfm5^J#aU9BujSuh78#8z`mbg`3G{P*f=zdy?%C?5M81A*J<4%@SW*y_;NqH-$Yu3!FrhZnV#b-0 ztc0|1;%_E6t>qJIVX*Si3zrhYQo6EKrXLs;t>~F$J>!<0g^TyAb-G~JWc9>;>9+h3 z&qx5n{9FHq&W1x*@tvBQ9JdE-&JdKeC4MH6i;2dDtuMGb0D3rgWrg(>X3fT?A5#`w z($Yc>i}uO-9t~ClaBndBPsvb?D_=bOIXB1RSYPAs*00oPcb%zw>jWnTF#Fk`CE&qg zL&j4zL(pH>qK*8us52Zv=p!aC6RViAtSs{e?v?TKTNdH0X$~WkTJb+<-jf(N)pBu2 z^`<21V6WE!RnMkFp6V@r+o~`Lr&`Zol+76;wYMk5D{oUlPw!zdIZ;qqioJ6}V?Bu; zxOR%`*ARqca(AO<9v+^YJXw*+UBI&F*#?#+f+!!znFm6L?9+n>V>@q@wUyu-?T%=inNrEarc+7cg7(fZ6Xoy5XEUKlGQEmOaGxpJUnU@*S7R z)X!N&fhF%!hgr^8189f;29k9-Io*)rm$&(KN$rU=AnWr^|D53M0FwKmaC*w5fBe7s zPwxAk+f=*FL(d30 zN8{11LHs(INSf<9!7AUQ9@)RD+NNH24$bmEpV*nTU{Bm}7R1uyvE8Iq1r0d6ZK#+? zVh&~iP(oA`j30y$wPOu2mTd!95puUp$EZWdA7jhe8bZ2vp^PEWedi?rUC3T;dkJ=~dwND$9nW&My;6C@~e3c3eG$=j-0!{G;$hY$xt zaqE|VgEkx2ayYRqlpUfP97Kw+T5{k-F=Ek7s^Hw^1IOmzZUAlvzlmhkbLmMU)aMP_ z=Sd@yVyDY{jpbu$Sd{hAndFE3I8-hd%8#e?=Tep{p0izV`%%$$ZlkbyUj9}j#*xE9 zJ+IU0ty&roQi_1CZHioNq6 z!1L?sorlR2>)+smFqR0m_UnA5rTC#Aeh&#W@vCl0j9t%2^}1GU6-I1Yp^CVtLE`E1 zG^-mEV6XXS)F9yb{L$Gxcg+_Z5(Lp({is;991?3TX_Ij~eF|J^K%7_JW zOT;*%uu}a{5)8P+(dSccaZ-ZdX;&=oL32 z>NV}XpM-;|yVIAcqi$BMYTlYXU(IeuMVQhHQN;TfgzAvZe?77#hyjB9n+8asWHI2D zIr87}dApTu&do<|%+6@519c*k@CYG_yY3?Nq$ktD4XWw{F$^5wkQ6RT!vOszAdfm7 zFs}5lh~vWi-enap-2U<=90S{hZm}^PrEd5P~Z#R*&J0 zP(%OQ*)R5Mk4UtI8(N=I;eOX#FfrslX%wGg>04ao+oG8z)L9-G#jGtg$<0%~v>qG$(3c*c;sMUs5A=mtAEc^g*kV?*RbAAnGFIF3 zr>)1z0y6N04t^wXVT9e)=1Ix=Y(+UAE?QRvxE7g`;6AHPuz(5GQrlGg>>PZ>fGlv` z@K(rmM}>k1DB3i9kt?JqW1JGy@HJ>A9nXTu%*LLg8eTIKL?%RLK|Yj+qwx{r#~x0qQk$u4jvyB8TRMvJdiwzCm?V&d*Jk2A&NSCScDoAzt=tD|vVIqPz4NRN z`pzJXC)Wc1RGYdPOTOKaQq7~!l_V-92=1kj|0!`BbQ6#tM`7;Na)y%BzM8N_))EjR zw7T+EBmR*?EMhj^u>u6A(&V-xsf;3bkVz8Jbfq;Tr*5nICyBz-{yF)*(~6{PrQt-$=ILA41WR3nCjZT65z0jG5ylCr?Kwf+KpC8_t`U5pS zvElUDLXE}K9UylBA@nnyq`my|bOO{wwZ0@gXytjihvp(9!zvT9;LlHg9PV%oDlK*9 z2g(H0+z?NhoJ$j)hG<1Iqgc>6g6FE2Wm$dcM6KA6S&GRZn!>h#E zkWBF)FB~vT=|t5AXK zMm=MaTL8h%VY&>tb(OSvvJITEHUaqdTyKbx0k;%v9AXcm(W9sJNM1Q$noC#M?r%~=G#^3X3+VoeEw_jPZpk;LS|F#i>-y^f6&VS3NXyh;F_pG} zW9}q;vfLHo5FfkHawa}T<|bCtjXzb-CGBe#<39wL*nbtnQaIEk%gYR(`!I#HTeMf@ z3Z-_2noHqmNz?u!zi`j@Jp(XbM&wP>Z*UrzNfkL>7No^$fWN*U{Q^*%FGZnMVHTHk zw`98vm%t)k=2qflz-^(o+NN;C0w4j=B4cx;(0yN+>Pk+S!}5Y2EUUq z-aCP4U8;{lrkw-apBTES{|ZZ zLzfdNR4x)nT_V~DXsJxRxa_S-H|qoDb5-FdQ1Y)E3TnTzM;wjyeZ+B+G6M(1wY=F> z*1{2x&90!SImfXZ4%eV|KQ{V%@#UiCnID{fJ0xcDGvl8_g@kv>vm)ZN!ix8O=$8)d zW$mK00dx>}7G+04HY_D^=&1WA=~PndY!I9T?TF*n*-Oq-@AaK`?y82D9vq#+^zo`` zV#GVoDbB;Zt|FfvL|T zH7N{FNMZiFpV>5IoiM-R@4{)L%ZOv5iS z!}piEQ-g=1_ZeVx{4(C3B>&{apIPEDYO(~abs<#h_AaEduW!J`9SpIhkE1GNV_Tq z9|$>L4PssM{~i>U7+o11z9TnQnvNCwXaF;JsG=R}U>5LXKv=T-&eu*DhWlx&9fHIQ zyeMDMqgHEytdk0KC96!5?!X!!-znjqZal}UsQh_8E!z#;8!m;4CohT|)zfGeRV!zv za#Brvupimniazw0Pp5^!JR!n$o%rGk5s#k>gT%61)W;%{M)5OLqYE9r_me?CgDTSM z1?L16`x#~3Qh%nU!jsJb{1SCry*K4;;^Kf13XGzGD$(O|d^il{LptO4tBA9&Z$VzF z8TT%+nbN9F`lwp>K_;e#*+afB#9{kTcckg&kda$B>Q^*(v*TkpZLy=pJ?49#r?s$! zt}XdRPf_;y(oeW%t&g!Xu$d5|5j8UwKp0hS5Z-{+KLEyY2-tr`mU9$rJ5o@*x;9c*&#j+)uJECIPRKsD7X1pXTXX5`!XT}wa!|Bv0 z>?sz7$+~7b{GA*Ii$EH-L`W#jd@NFj@hv1~D&IBzlfrl#>NpeW%SuB#m@1-ZJaN2J zlI|@;l5sroH1@|PMp_2}OZ7vz^4SMV^?s3WlF-RlZ{ck|ZH~y~*e<%DY;ry0F>4ZS zncAz=YYU^v+VZ2^yPhEaV~f{Q_ctYpXxAYe017t~QZ@|Mxul7@E5pFq>Y*`!iGySJ zcVAwk?X*B9!@VrNaO&bY*N4ystZKiT_Q6;`3~SCVv1|VRof*;QW*jtaMM#s>z-Q5+ zSw|Tbxl5ihMqrc)(KYf3b=K0&fRn=5*_lGX{#om%le%GP(O*y?)po(7{T%T%`L%0& z4OX*Xj`i!_naF+5hpeE}O=yWfP)S-t9*c5$q^PAdrNxO7Hwo=q>BDFj7CQ?#ZBZl2 zVoYDURg==8g_c_kt6jzGdtW+DmOlA&R3**vuKA&KZm2hghHouu z6vD9?Ih4)dXC8X)21O88OF-2fSv*CVtUUdg8OHgyLjL*DN(Y{n5*V}dnYps+TVqk^ z{ORt(Mb&yQf47b^!jW)7QR_dzfGocQK znjXb;Df|nwbW=fe6!w@KIO7C@ipOn?fwR<0Njf^Lh`q*5XqCbWyg(-64Knt6a>ehJ z%xU1cTY+_-ZDgnc{kK6|*8;$DI2gKEw|3oDJ*OUBG()QGe~xzIv^rKT6X|-v6{7X7 ziWK|t6hY!}b^IsKgo$RLc(K*E*~lFg z&kqKH4D`sb=K6XAk0C9xp{&@Pkkj&lJ^Ocv0{(ea$Gi@;kH1*V@_AyuWc^R%!DyUe- zXpUboF71x@HyJrP2!(owtI0~+zeA=N;6LIT<(jvxM3$sTe$0@&+E$WP7jfmAOdwfbJFT z8$pyz!%Sl;?a1+HA~&I zxxZ0>B}&%oORJEEd2m7nUWuOC^l{pfQ~EV}chc%&aJ5%fH9S%nq?i3dXs6iGD!8kX zw;yB@tOY+nIB^-fwbTN=y_R=FKcAfRyFMLGkT}1DUB%by^AhAcR}=mv{B)+a$93s< z;dq7AFMYLyZ~s5WZIb9O1|dYrfh3wRK~i1l(DBiz;WkJ? z5#c#vB24!SWS#DDA`zaQR~@YHec>?IX>~tUcW;OF^}Df zFtL>X3bcR+NEf!|>gsLx!-wkTTX-QYpCW@Z?XSlez^Mwd=*kllJEQa{}k1zT}9HrknbGKIgQ-TsmWn5&DZrF8yYD4qi zDEObOyiY&9zQ5WgBIaQd%VQjjH4XNOebZvUaddUi=J}h(QMWVm@57Co#!&n+kH27m zzRxe(2&i_|S2r`D_NCW}EA|ZrwW_xQs$V=sIUeN1ad|jQ;;}aZ0cBW8X|t48rnObA zpu&TnO0bu&AZP*>(ciS<`63aRmCY*yB3`Z!bH)w)0vREDd%1HBjM~_O*^w@MTZgPY zUw>%0&ITE`fSn}TW1^s4$Mx+Xe=`*gFEV7ztPdFU)2nNhlo~|`s*o_71a-q`X!;lxtu7iZ~OhHfVq?Knc!pdoE!FM za?l}S`cmnU%S;IUbhW$wjgrLH1D(<5m^JwM$Xdivs6#2#&}QBf92;&@dhi4^N}ep2Eh!>QZU zA38CXC!99(?=wT!4b=mB3F<`K8{%Y5EyzI%!`UJvih_l0XK_*j#YEv?)?liIW23N8 z)b;VqT}_pt_B*)XTJWTx_Wn}yxx=0mdik%9wJcZK#Ycz^wpI@I*x40fz z7jPAOIaw8Wl96LktaGgSwjZLs|Ir{+AK;gO`9W_8atg`h!mwd0d4xJGLKfZ-{TAX% z@+ws}HPIu>2zunItuivadWy^Ci)m_Wdo>_;sTgH0;8`f=JP*k;aJefdWp|90l{u<~ zQEfMKwz}=Ever9rAlmk!mLplCK|%3AQAtnD7duNzqq61|N3yW~s@&DDJp~WWF{_I~ zltiDqLb`|HYA$}*mn>aADM zGq}IzMWacMUe7wg^uKhWiAzbD=eol$nuTpul;>SW(H9S_vWtNa=7^_6>*Z_E7I5So zdf&f2zZ(^E&!f@37?R(-Zg+o=1!!0n?6Up}>1(*CQkZ)m%zRqv+q1G9%R*mt71G$2+^ zrnEea%Ih=i8enL1p-kJqe(bKruBYF$6Gx;vd{YlWw;?Wh!e^_g%z)gfHXf|?=aU}j zrg(t2)t#$rbo6}SPMSM7qjS%wy!_ELGNmAypFuFT4Z*|rh53gb)`UPg>K_Z7WBwtij5lk;@(LJs4y^RvKr~QwvH73 zzPY(Q>cGWrBOFY|68+r$PU^V4c-?TOCN!d`QE*kKBVD|_-2aCIInC-4Y+PsCJ=jCN zKj-k%g9L*dQ@DBHbh1)p4s_ax#mkF%u$KS~R#xZzj%s-udGL{#MI^zgSuC0_WJ%q1&g|zIznZ3ZG6FJ4IP2u?D?di3HKPFjPOU1n}u(ZE{Vny>v2m?{~=DTvg%M#?-Wjn`sfgCMAq8 z{|wKQi>WR@(v?D)G;LBEmUpp_$$00{F^W;q=XRrik~_RG3&y*$53AR>op1SKvrlPo z)zSRB{(0_-N~FJc)P3)+EF&vBO93mbD$Md!UX#+I?$4WYPIBR}Ela0Dg}}K^RJiiY z$EIk>JL=8)N7kAv*TVagElQBGp%%91p(N<61UwvjEw=o{m0s$QdZtQImcI^Tu1Do- zQ>2u%f*viEj@QOsIl{8hx{LqNaUkIGhe}x{hnjZ23>XkZ!c1y?lj^q*ZXqwfs;cjg z#sk=0oZot!7-RqhUjf8nxz^35mViQsH-&-C+YygoaFO|3+21B18Vm7zWe$J6bXPr|iQ@3OWog`^ z@A($~p&wT9ibfx66czpJ26Pdbn7Mz*edO>h{Z=<|8Q-D#Nq1yk$SpHBZQv>lSzSY2 zO1tG`Fnfae-S!6?=9Of_m`!?Fxha-KpR9RfEy#pScoL*d5@waiN|K=d28k2tJx@g3iNMi;raxh~nic{+Y20F=>%?x26G%D(#7 ziFd}`6Nfs@4VuZd(?5<%2)^32c4}Ea*^I?yJL4YQbb#apFi$zKO*dCz+#)M3-v7+K z5m&{wHeqfGQICTB?EW%?oDYWJF=~pf1`)gB*Fzc^acXNAXEzBTNWIpuO)Wf{hYwa; zmJo6|m8qdF3odRaz9Lm_+)Yo$KoD;2c}eq8@J+mrY;WA~8q}Y@uQtxXhw7Yn(`~!s zO8dZCXyQe}_@Qaog}kS4Ns@fp*>|Glmh3xI-*z0I9T*eadWlfGl{ZzV)$&aWbYkb? zXsI%JvwLL1Q}+fc=EuauR?;4&9&W zVJL=&$llid$lc=;yAge1QCM&2-m*l$f)Y6aYvy~{13WVlcW^?PjK65ZY&C4c(LgTI zYez@ToeD25Q*nuhV#NbmJB@oPd|;*hG4RynqnTL%kL}O3e?sg2v5EWG$gfdkVI`N3 z5_I}bHS|f#>Ej`w`=+5w?6#IE(V-jOl4H}xpXAK5ly;Lj zVSjbR&>I`S{0$Zz@Fu`bm_U&th9`sYL)VkL18P6%L};7D%j9iE!y7c?IG#NQjj1QDDo@HS4L9A)=h_E;cD2 zEjHyU?`!Gkio+pv{S$moi;+sLWg9$8LouZz*uE>R)Rsv!^fjl~dzyU8Dxxew0| zodO=VA6sQ$o1%oLN1i-9yo#Sn43<{s&%5Kx{$pIQ_*fg8(mFZK_qM9$fl)CnVex%2 z?9QEonF*^(OqEsJdpo0s*8xf&;7dT#kQ! z!LV7$Rzbn4>-i4O!e`Km;z`^k&*JFpg)KVW%k^P>)%p)dzSe*jQjzqLi`}HV&6wQz zZZT?ym0vIyGZ&j!39(S3U@)K4kH4RXTc^_;B`Ycl6dvI^*Rpp0czgCuUS3l}y>F+$ z|7W%1Lkt==bfn(A&G`?od%ipG*~Z4d(}T5G#9gq$qVX1tN|firU_Dl#c9UUb_Q6*e z4Hx&Z(;7Reuy8C(v~6E*%c)q7%j(*`eIj=RGNvrP(i{f?qh=tsz45);OimErEVmpT zW@Q1sqYv-1!lQldf|`uw3S9;r@eR8F>xJ!a=6z3T3i1Lxs}uUDX=zPCaw1~S17CdX zjUL2dPaF3*W9#aeJRKh5&(F<}DZB^^O_Q#y-W!^Dx7%@J(4n6tGDlDOXQAcV2d+lY z*D*)qk%&;PF2~0a^mASVs^L71JqR7lx-z;KUH;+ZTwIX;tug$VTbBphIn{}CB$zKY zn*LgDyL|tC>pbu&6V|>;!$L`Eci$k(KKnISFY=9&%#ecD|>el)2u)4W2m4E-LZOdYv zneQlq@?I&kD^LuE*}uLw1H$A&QhJScEF&G{0_PX`VvR8|Te(zX^V&KJwps+FR`ZJL zK0>f{%v{Bh-NDQ}Yqc_CvZsFFHA}|QGzcpGZx=ss?hLZT5laW;&X0n2%YnXmLq;rPC9ne8v)8LS(c znnqPv^^G04w~tK-iJszC81g&XX8i7Ro>jzz4oGSEQK5(92c+Xqm$*Jrv-n>2V_V6M zhO6WatUq^g73qaTMx^*KhR+#`!C>3W2pSLBD?JvNKIHXV1EKrw6zg>WPdWwj z1@g(1boWd>onAsRs1Eu{`7Xj7@{M8a@Bh5JsT8KA*^ve@_Yxch4g>_Qr2eGzkVJ06Gy<8v3z_X&oRpI z2-$j%{-MX~t%mJGqlv7HK(HzAsvg#BW(gk!Pe|Hbq@j}Z@&02%MTy8PcpqYH*BZ#2 zh;YbfY&YifY9qX)jSF1Q(=7$v;L6ac%Z=9f36BM`U7$}KHgwEnt;Vv^Q2_EF7a9xm z8#e;572+bazlep{(!?|`}JDNYRdRpecG?di?|XG0tWWdIdIH$n|cQJC%P^>|vz)9v`Uq?3!5 zFO|*qqxZ6@M8v$dTp0VbpY+(vdl7oekKdT}_Y0MJ*l}-e4ufOp_*l^UKWF3O!}y_% zwH~EZkT-~8D9>FxZnl<*W8+l^N^r=)VJ-G5Y2=7Ua71F4?jF7KFmHM_Hx(5P&HU#A z$Jt(oHf)Q+m)D_2!z&u?KrU|D|I3TSdHEv&<$~Iuq$dD9G&Q6#Vj)BpT zeN7c@CM2;GK@u`*o#Wy+YQ6I`y5C-BA)hqNK)lT8RBbwL-c*=r$yu2s7Ae`(Lu&q+ zFon%w!!tH2X0p(0{^D`MFx0N9;D?>|PV;$Axn?`4d8>v#23@%3gwL1n|5zsdwAYtu zvR`kEh%0X?Tc?6bqEexmEdoV2f}1ioYqk9MnkM0w1CFlL3FheLOP400@5=wRlL?3A?5&SjIay8EA$^iY+-e$pJ7VPX`}D}_^Dl8 z*rH%l>i@D}yimWxTB{-eQ`d#N-s>uN91qdx{3D0;Psg)TIA4Ym9G22&xwrx& zSUqGvos?gX4E~K*fZd>6lOm1q-OF)LF6Y^ak;w12u!OoQx|wg-V!S?lYztw#Oh7j) z5t<&lcUFQjnzkj1{;)~2%mmrP86mA41fi8mIvO(VACye!JX*;O|JDTc7 zQ#e+CQDHoQTWQ)=VSv9s=h5dR2xD`LS_RFgS9j@4-D!DwL)q*kPK6xjnopRR#T8uj zZjgVxbB0x+@hleHbX;v>I1pe?xv5U*C&Pp8E>9JN_t+ZJ+}cE#b+aGY#pWlQ%xucH*j5)MQ`}xg*J$q zo$g~#6|BFTNoz1#EjK2Aa=1tSBfSP|alO_PS@r$!#^xtQyQMWtBQ0fgbuMP9A55J8 zue8b!M7mjh@c!&1X)d34j#IsaSz{ez8}V9>^jOLeS~|wv04Tv8y%)Xr5&Y55%grkHx z{8TRtZN2XN;+=3@JruBvatS|C-6y=)>%T5#Vk$QOjcGhiJA`9p7ZYI>t_H5g&N&9= zZ8ph+C)>q>GaZ{bjpf_Og&r?L8wV5|!o20+**jPTXAJ_dBTm5odWv;G*q&(+W8Mw4 zl_uF_`}4CSTEj08-WAuWUX~nE*@-j^ACK^Ct-p#+VjHiEad`9Ns4qCp_DSTF^25eZ zKEYSEy18XXujy|8uLW&galIT`5?-Xu0iUw3gPa1J@vY(yY!@I!QK+E}iS zNV`AWAzSV7`$G%lf~Xb>Yt_on*R(NU1VyZ7G(r23G| zEiDI<)=}KS7Iq=@-5y_Iq`Rdq_MXextHZU1Y^2(Pk^f!Me+$2Sr|~S|;{^9orRBv! z$XTMN9{*u}gFji=HH`C)FF?pgjCMiav>Bc*Vgh|8>n&5jsCfH?zm zP@_=Dne__HH)kMrzQJf*ILo;U(4pl@8lG-&8{_)KH3JRq*?JiSsS|MgS79O_0-94` z>Uq2rp0?v|gmC#mzOR-ce{Nam<~X8y>FL!i@HR*4|HXQ9OR`5)J6y1F{J~k*x^R=v zg)yC9v2hX%E+goOhv5*ZB8lP*m^h@_+q)e3;VOJrkBNCw_SpN2a41W(^6V2|fMTQtyxi>J$lK-tF89iA;jj z@FcFC1O?akJ~exkWtqQ3EqmV}J!AiaA3HVEw+3NQ119?Uns04EmzXHN00AJhX2{;0 zT?%we2mWw~{YZEhr`8hJG1>TFx5 z+otfj_dQsrxpT0VR(e)*Q_p(4B{LG-`xZk027`;AM9UIzk?Cr2P77uY$JfgAf6K zipnA;@$cW6cA&M-6SZs4IYrVww*>eZsxRiX7W4l^P!m2cC*`sY$WTOsH^h1shrjH@ zZ-as&g?8b$eHB$B+r^(&WT%a4=s>)WEo1i`&=DxEm?zf;aoy;yWAgmlnPe>u!hIZ zTuR`CcG-~Mh`Z+YkL_&?gV8n|&fNCSEA9yy-Rx7+*z zb$BalBV8qvJa*_G{^&U&<(OKXxhylh6%<22FS-u8*onFxKH;arT`Eo@|Epp0Hot%h zQx0)eWBc(8OEtuyektIsNrpoK4YZqeEZ|yp!l7s5{-Nb(v8V$_B4nc@smzj?hSpeC zfiNFxf9?N;0!qKVH@BxiGED5abe=v&p1aV<(61qqfPRig3wfpLpW!#CcIRx@2)0qDuJ#%~nR}xn+x^hkSUY*z& z{yrKLaM(M%U^1w`=Lp>Af6?FGtY8{XQ^V!>_VlOIZ*W-3Z54lb8effLlmV7#hr3Zo=j(iFk2{+|`CDP`-s>Mkg=V7{EK0i{1^K-wTaY#eW#$Fq zB-mhWT2e=Z$BxU19-VAnZl(jFLs7$+5c+NZ?b>WLmD8($!+z#)F-pzkyW-c7Gj!1Hn4pV#KTIQ0>q6{i!yHNiNf9QG8t|h=b7;l4&duQv^-+bVv>qQl zt;5J&B~@F+^qXzlIcr02`?_dcMhCxUR5V`DLjPfr?&so4<));PB(sv+Kzr26p1E>c zTPL+^*yo@)*>L;DJF+j3Ly0)i@(NJrVE%+uPtMY?EX0bsVrUydSY4NoH}K>~IV;w9 zb%q*o&&;&f?=t0Rs4Uc=DzfU2I0f@mu&smPQg>zZJHG=wEaJl=he%VZS$ok&da2wv zix1cc>@uJ;O9hP>33#^J-I0DXLvJi^9pnl5(I}>2wO>@x5(u@-r?+E&t{)v_rxjQooycT1B(N2@b(oS0lZwV$jh@ZA2OQ5eA zq!mI~&HNIsx*U5D=Yf}eYwb{*V{>Emt=WYc7~HWmt0)Pv<;6RQ5qj3&_7@!Hzt(fh z>1L7*zmF3f_{_vP^=L0~!Y0?bl9BardDAtU8sEC>jUg?19+Tud7@71t#VrxW^l8zz zY+E*_mKM!Kf6pYir*cEPFv|Lw?t%BDYmhmVtaiNnB=9V0l@J9P>>#eSHE2vF*<2k~ zJhg&PFFX)_^zJDaJZ)-(3)FcPmG+>G4Cfp@)v~KwRsj!h_G% zr7?q;EM9c5i3jiOvqQtUo7%(J&%Xy23jr84X#8TQ?~(~i$~UTiLA+Xb5xuuO=qH}x zK%iYZs_7^22gvduD_HUY?QR8ImWBH<9Ce?({5c2dZe=Y>6tkTgt)$oBhQI{=ys=aO zc<(3^?#!jZXNrtx+w^xCioM&ul@v{&mb2DpH!Sp-()C$Us)EblXlve!wheK5Y-`?@ z%8z0*t-0W)3bC^R$5d}_OgcG!Qu>_F7r5c*wKOOi?XTtp%#))_bX)c64Lay!khS58 z71<&++nieHv4#QQhlZd@rRbRR*He>n1^2Bln21^FKrkjG1~-mAQYg`!+A+{mtq2Tp z^P&s7lu{l3v9G?d)c7r*1Q)O-!G zmE|VTW_|o9lQ{(l&$ih)TUth&SH%?gqRnwb9$A1@9l*C6RiHH$1%neU&~Hcia~v!h zD3kGWlz&D)E^Zfri=emdem#^J9_l&9TqvDtSXmNzIQ#IL z346`Rn2-W^K`S?J|2f2;@%q-;NkB2W$eWg)H&?w(A*2Sz<~?6Z(!ywesb&^uy=WSv zIQr(?1Pgs@I-}6f-!?-D<4t^`jAb_Ctmm91VsKB!1{6f|b_$(qFYL-F&ZNa6!IOLn z1};LI10;^*4}i^aZ}PSeAX={OQQXPMyo|)#fyyNO00FnG0dhVcBy#`iqsK zG0{@%Bo_RG#F%mU;hT@!Tk`}ZTn*SS8#12Fn<8!3ZjTwv`k<>H#yLrX25+1S5i7;U zmJ-74z57E@caR(}1b_l7p6BJOV8jaT^-~bd5LIZZ^#hgmAIJK>{?2}*>z+Y}d-)|O z^l4r_!uxxGPq`S3V=KC)@j4-6VG;GvBG#Dp?dS8`%J+Z{bm9hV}a)ve%*6G{l|*m&xVQMQ6qw;u-`_lwjllholYD&!AUF0Q#Vk~ywj}=|4dCoKc#6K|L+Y0;67~)ITkG+XAWylQaI>8HXzj&#(u6l zxAI?9RlT#`s_+__TvGP!=rfwNr}%%AL436K%il?J4MUj9B}{%*(jc@YqALyo#-AAC zN6eV2$l4||EQy!VG{yHPBIbO!a0A~tz8u;ul(g~_|3@jm1uN%qB}AVm+g{*naz{nk zQ6m0FDwHr-(ENSdPP;jq@-J>^L8-YLd@TV|&wgu|ln%N0Zg@fS%KXrZnJ3O!N(5Ya zy<$Gt$xzJC>f^9OTi!r|a<^p^+}^ISn{dEvHON*FnpJDyCOntRE^W(@k#j;wRVv2D zD$sJY>Z_-&hc|=}Bx_sR;We*CAd5!U(HtQ47TlM?+Iua z+Tjpgj+*AkU;Ccp2D4K%XR(QtD$M&mBSO#La@js}98Ls5OfV<({}A>TKyfxffav1E z-7PGOTY%se2(AeP*RV)%cMt9sELcLY;1XbQ2pR~G;JPdj+}+`Q{CD@N?&{S&YAJS$ zneLgM_U><%6f6)yo?Xq=&8zm73iq#K>q&H1zH5EP-=W3Av-Xy%X_?NSkQImWR_~W1Nb^fs#FpUd7$mPUK<-HR1 zPX}c%RPGC9LM7c6oC=yNR|e15RSL_C+i%yWHtL!cL-QLI7kq&{3@ea!ab6052UzJY z&RsFm8m2rC{GPt6@1QL>mBt|b?;U~)B1r*}yktZKMG5Jpx|7He&2Y=MbnCmE;v>y` z&L2a0I(ne8@mACS1s#Q1$3d~9=wQ73sQzuRh@wY&_V++7DG=e-0S;)dDzhU>Q3Azg z|3UNxF5yLa(pru~!ZrWuec+Ul7Y^6}1NoG2pi9<_*;(u;U8Jg^xefOSZB?WbB#a?U zOZYe4;nyIp&R$LiNl2}B#Ek5@d3ILDnM+lD~L-B`#%(sV&K;o%^s+wb#1CF3kBsn^yfR1ruJL5hDcYX;g)PqblHppkAd=I#LL!^C@vWF%Wak#fFE5>PKu=jf|X>pwIgD0I-^JYNar1n~i;4RMT zlCfU-HzLvJ140Wqa9r_-s0m(0yLIxUnwklhyxmt}CsSP*8)y7otx?bO5+TFzuy+Mn z&ng55BtGUqtyje#^%nf-y#Fj#1L^WNHcFM*i70yxni0rr$*B+A2$APxS7I;5Y3iv< zx|WdtW#%h9w@hqdAP=uj&it7e;(6|gDll-u9uQ*`hX?X{PQIh?7Em$=??4vrRHJ9q zf$!%%r1*SKIx8&kpPNigN3$7k&06AsfD~gdi_ZOC-m|e@ zwg_q2jR66P=hT+MXk5al3>#}CMQMX00d)&4*-O)0sChC&TzCqycUg+FQ??5*amdSp zRFUZdXZ#F~*jdb^7Va=}GLnEEIy{u|vHU(;pf-50>y zQiF^hF!$cl*xw+Jfw_uk2Wut5nETmccp5>yOpPTCb5=hdYdB`>JQ;vmO4LBy)V|Oe z-;12}#&6KdLH2nDlfT#?nI7wUhZtu3@uBd9@m&eXes(KPWtnl*HrjYvyM%AxY1}_qosd|@1Nte##mKr ztfxGtp|2e&j}^89l$+?cBJ5S;cR?1qA%_Fm>6cdRk(PEzwj{q$$B6v|i7p7y+ajY* zPoB>eu;wYs3{mtm{4x+&f8mPKGJRp7F(NlD3;9KY@c*}Q>$Pu;yzdV;JlP|Uls zm-Rc~`X!&AI#>`sWuN^#K}GT4_cs4V^0X%iG*SJSm6hmO^yA+fY6$O0{&Tovhc%K^ z3)e0MqSg{snEMT%#P4164kv}S-=vDLK4QnU)uhhr1JOn$Kzv6(Y=49a%KRl7&YNy% z6XHcxAXpBb5W6QydGQU%fmjF6Bn_b-N14@0wssQzUZ38aR?Ioz-5CCfwAZT*seY{- z>w|?fh)RdC^~dt%+(LR7po7$;Q)XE?IQ{|8+4Qn_lp1_h8PnPh+N1xqBhftH&ndlF zyQ`EV=pgu)cQjH}q*eEgf$K8}{d;xL@|cyZxXFivrcnX5^Z>z_5omPZcF6w6Fx7jy zE}(hpMT(+5elCpUnC8)Z9!B!r{5{Vc*~di-_gAjupAbZr0Z-i$(aFi(KLF;p7e{>J z0IJM-?PqBS%86j0JTlM12(VTaZu(`h#CT1QIgvPW<7W%Q~Iq{Yq(mSg<&y{|-EZydh{IhWr{?r*-v@AXvLvQ+J zQCx{`wzFG~03|N(PUGj#0%sOx!Q8Jb@S)=p@i;=Fow~h}YPwp<4n;Et{B=cv90da2 zYj#bZJKuluVxxi=5{2T6Ya6qu2=$$gcAxLf_XA0H(fkDXx5L7(_2{HexZ(0d$k@rY zq_2|*`1j2y{mTrZmaf(_x_`X3DYS4hmbsW_>K0_AkS%{+C1RL_{WDy#2y+*bkE$w1 z=OTC(9Ymq|IEiEwX(tFPcq!zXT&R1iFLw4g*Lrx>Sa$okR=t+o%eXLVVnN8K$3WmY z>vxD-^ag9W@zqxp<)BwKUr`KLxh&J(>U{m3loUoNuQ+HPw%1O~(wOSqud;bL&|GI} z*tp(%G36~qk1yfw;o)(pP{$PDejt*Mf>Yp`(njx?WLP7AR0QjFr2fOh#Tcu_?Y!10 z>bX7UVtpTfJn$G%-RU3aWG$)waAAo<;VV;#Qc_$TiThz|>y)D7QSN+t(>`b5CXGIH z_wf$QowbB96Vpne_qWTM;Ft)HKL!#zTYEDd4Q=v zy1Z11){m{q$!RGhaM<~med&yI^{tb$k~YH;zEM%sm+LMjm@N(d+70ifZ=w&cMeBMg zer=R2B~$N+ZbG^6v4RoDAP0=6+0eO!i{AF?Yh?ksqE_Qa}C-~}wPyXGCxc}stP3sg~4P{{pe`L)yo)EQn z=6_`sxpF0Otcyf(~1AmH>qk!V~bf8==4X&h(lR>utnFUL^3EuB?bld z5JQfPZNUAHxo99QopeSvx<#8xh&m`94B>qm zUaRP>_CGz+-q5$tmpyipZ?4z6d99{N7}wTY6`N9*S}bfhE$q#p_FAxk<4MwQ$JpE$Sd-!mf`+SuwZ<@_77IZI}n8n`;pbXvSUUZFQ#yCuZ(v1%sp^tfvqd>%+;5&A$EK1 zb69zcHul{M0<*1Bw{5r^#D;RN(E8%Jgtsw&vwS%lz_#<*LN%^`#k#dq|Avu*!5vyM zG}%ByoWY||)$f$Bj7W~wKFdQRxdF*se>|32f7h`{&TJUOo1e5`iwX6#BSEAS;W?)LXlycvwfQYo#XpIUV^-MOnQ_D?`y*%8`5)*lW(1j#Fc}>nC|9nFz`Jti&}~@=A8m+n8fg*GQ!yfb z{K~1g&BZud_3eD~f|OhETXS+h7AArCd%TvE*r1oW zyFs-df%jl<-Ca68S=rBp05GF}#^oM*h1WwaTy?r24Rl?dbn=PvWnop#1#JW%D91f)Tt48oR!Ql5cZ;baS&}o8qg=ic%#0hN-1RY~e*O27t{7DD$PcQ#Gv^y{d-JT!{ zh|CEI^MM`Hl6%h_t{Ak)76M7z2W0pCBzSJ>=p#AM+BaKPT@+Mxu5$;?j>6#Qlmg<8_0UjC!_Vy9F6*XXLp#=?DXX$PaCjB{;& zbIe@#;d8&B;qp#MRFvYk$w*;AcU1XRE0f##C%&Ds12M@hD5;FurlNQ%?heiAw#zBo z8f@*%!G3u-qK+zH)<#7gej*r%ceQ_{5sgCOZ=UsjBKyh4j&;k-er~bHI>{R z8y@$&bnP2T1(`DT>aP?@kmR7V9b|3b3WSpwCl&J%%{*?KP6o3=_=?S3KYr+;Z-@Wtsro(_Ao zU;Mq40$)&agVNCs#R0v3c>}V`&_FYk8j!d+lj1cSJO-gZLojc^ysGh8iO}r3>fA;W z3^Gy{Q;O+l2Rq7!?xcJ)Yu=MvhFezp#uem%V_aiC-@|@#g;&xoz=5@!Z+@mlEkh2B z9EULV{^gksf)QmNx^dPPi(+!jRfWMS)Ba8Qj(oEOXXSP!2FHekhWza^1ffLay{Zme z+mZNESLf6IskqaNW9-obCw8+_@%bA!tDg|2`ooY6okOx2+d$fyOMO_>7`rmN_j%jn zWJ{a#D*w~(Z$DQ&L4xb(Bt(a6hJ#aI6yxbA{=+~Fe!4*Wipka9L*n&WM`HGQCVr4W zc!qjVf;N5ChoaI8Rc#+q5$r~yQfDcv6W#UW9iPYO=tDDL^@jvTk1L14n)%RZfhkOv zhca=gHAUex%QO;)swr9q10wU_L>FsA*N}@Yd?dWwqjF#B4aa++(NUCPPhDB%=c9(l zFg&9zN6!D~MnL>_fWiNM9s2Iwr=-pRwtV$A3D}%5Oxh!@i>piHVuvzXHX3>$nP4Y* z!!YB2_L|#wpReD@^Z99h)^N#X;6JYqein;~0~5Dhl7W$LRg@fgIZC)UXZ;ea|6PB( z$16!+-S2DjINzPJe#Y?}g>TVj)_F2R6}H-vHc&)Bj&-F-#+&xp%hDBXd=%ECi42LR?e9*O{FS;(W zH*JfLNr{;kYZ6$~KnAv>^Ka@_tceV_EH*H--Okc|LK;v{)61m^9G;o z;n;V%F18!eSN)&N8_~|eI{l89OT1slIgl|OJYAC&KnUhMkB2+O}P|d?Xoqp z#^~FgC-q>H(0uE?Ate#{a|8{Q9T`W|@kPnqy1Q$~@=rtEPNXgl>(0TqoxD5BDHly;~`Q8GiQvCQ8Mb0V= zfELiy*HP*`MxpTdOHurI@ftY|NmZFh;r>KVKBt`?)U48U3eZ6Fla{P&tA(a$;ftGb z6ydXt;>%8?;O5SJ~kNdApFl#8uL9!*<@gh1t}%SA`TDI&h;ko$u7Q}5dp z!{24!8*Uop>|~74+Hg?GM*Q-Ws*U7bR>BJbSf;|@3+``njeOD&wI@cU|Y6Uy^=CqgN~-|Rb&f3@mj} z*QYa6p!FZ47&B?s`||1yjPi^4CM76$ce!hw^JuSW5m`0m7#@NgbxP;%871tUWyOktg&!+RLA>clww;AtZ?@AW{gkb5??_#fJ@0CH|m@y||yfJupD)+;88xn&h zh*E(Xi2q_gNa&40j*6TQAqROo_2LhXKTJO{vVQ7n`@v_)qbUMrg4v34`%YqYfBQDj{-5um3w)A|WdfW_UjVzeY+ z6K`sX1XL$)0q6zK$2r9_DgRfM3ttDmmz%9|`^If6wI&6&EJa%X?a=b`tSa9o&uMV- zY#;E%_Tg6gr37f2sldQ}J9@8t%uvjC=z2#dP5yV;Hf@0`!-On%a)wy7NGgxV4f%%4 zF88y|jtZ3?U&o}1UfasvI#r1th6JmR$TcA}rZ)1OlHUY%P32oo{UG%hYGJ`J3aA-l z=*vGTa4m-Oik$Vhka5q&p_PZ!hu8&Mmu^odVb?eedjT05hCUYA)TuRv41UIdg?)wTq3rJ(@H$?luSU)X7gm9DePm*e&IMJ|OyLwrgdq%|QG@J6+p6 zB~?-R`_cjVXFZ)X?Ucoi3tr7}Dvb`!2`Y8R@*#F5b+t0md?h|elT%e2qlFM!ACcJ5Uq`xKi7r|%g)Zs$ z^I?~_1c-6wMpBz9uI})Z#YT?1ll$RiP3^aRQEm#ze`+-pQZ!QlLz=lWe8eyRM%zTr zpy|iXfJ0_;$8_LyKu7vTuGqUK*mR|2jmv2N&N??rD^Y2w`L0lQ`OH4Np;gCWZ4{-o z&gCM);CA-irl*@b-E3OQA`!rc)g~F8b;s;fTwk^F)$OmX+$t&cPt!LwQw(J`Lz43f zZ4Dk`#03Kz^6K*?^PvZ#3HPJaa#q#94emctPtV7 z+v-VrGYQHckKnD;PUghaHLA@HL>wiYmm-B%Z*=tf3k~6x_i;OmJeiRG9@c(+SUom& zaKZShQ3Y-=H7a=pLt1FI(V7oJb`jK~w5Px8$V9;D{;Mug86Bi8mI5QpRQGTMa+}IInB#ayH%$x zyKJz!Vb@XDSO3D^74vIY@BA;8yz$n~fhX-`IN`7@NBP_+L0lYh%pdaH4#n)p2f5HM3ck@Pb zf_@i#uw!6q?)x3E%JIUlu~QLO%1#RKgRCU@LYG=SwP4M`B9EDJ1~V_wY3ns&X;}c( z0XT#)P0jk}Ls&zZ=Yu%92UP{0nnsjXAS1w|e63E^Kkh|V?{a|-gp>BGM1o^WdiT)p zTxj``*7Yr_vQ1_zRB7+${nlZa6mX0iopkM!mFnV}fM0@m&D%ACA`--jja)-3Ypuo8 zJ0<9MxR;^>q0Mk6R_Ynw)}m2Qtu_^vOswBMYD1@th-9+)YQ^(M2Ro+T)BMjpcO^!c zSDsn>gH(S*Qy=|BfwdU4t6i?>)3{x_{JsJ%1-;(XeFsQnO5P9~e!jGFB*{?h5R&lK zjf=nk=V<)eF$Xuypw$D0zpk273VRCeBZH z6K6@B@g!BZ>iZ(cU$u6p9+#+XmL{HzQ}Lo;G~h$Mg*7DoB~N6qTek1;KGG7sI8e+8 zF}7L$E^)YgCb$;lX;Jww33{@eT0xRo(NezI7rFCV0Kart?+;|ETsYMVGisNFd&kzk z#~P)EiXtSh`b>F111s{~3U=FNdi(@eLIT8jNqK3bu1WSngXbQ@& zFXG|zp_2z&97lY^(Ciq>c_jGeBemJ)BTb{`j6C%L_RAPbXkgrKde0!Xyc=*w%`Md! zU`u})*Tolpg48LT<1Dw`K;cd$pHAyR#cotKeS2=2l)qiWGItr;Uu1 zafa~y*rvZOj$I?Y9bkTQ{@pHnd^G8iRb)|wij%7 zK-jq?=9Rk0owXn9Hn_JG&5hD>ba$-bHp!D{x%Is#8~)3Lb)g}9U#013PzB*!<@6t| zmL9b-z`y`SuAAxvhXV^z)XD-yltyz^6f~WHqbmgmXG4l=M68YZ3>p!d*$@ z?a6Al%x^zBnl=3U+E~gr4_i#R0Bs{o+?62Tl(M0RkrTSh;VTX^jn0=i!dw=Y$_FB{ z_XytGKet~baq$)v-}NoT>Ob}w0BSP=&|6cv-K8Wj(yOlgt<_Qg#X{PWwNZQVEhk|j z7K8R-SvU8s#gIS`cD%`jVihWs1e#gIJ&;^4faa^eKlHnd5E4NQg;My0>{>mAKtOoP zi(qZ)^vJJ%4^ORcK57`=;w$9(xe4banI_ROyFO51zA(SonyC&tI!|8Y0 zFeYVa);mydPzDN=iMhBiVMH+8(#tAUWhHRCpD4h++kcoU;i1HGUkYLXRS$SJY#jDA zlYa+bCs2+B?z%AN7gdiZf{UQ`#;neFVA5EwUW^rVN+8Y9EkJLwyn&K6KYl~Gdv*4Z zsI>c8{tNr7?tWt$*R`)SXQ8iy5#A}6zw|R-ZwBVhH3YJ3>u={gT-NtA+RK~I^)mgw zS;%nTn@T=OagAvrb(_o`xEfc`p}Op6s*b>`6F0_maW{-BmlH>yU3cR=W=EOPIK_B% zWrc1tXoN`g_Q)&JRvkFk??|6zd!NB-&dZ?w^PKSj$+=r4;}6tbBPi4s1qdJO{bn3n z@W;TFcK7HBbwBxBI~J7X6LDH__d8~+F&w(;9Nyz;lWJGdIS*X;HAva`gd1Y{Z2#E& zC^Q_*>5&y>@*QRYBVzqvZaC-G2a{GwTrpTt@7BiJQZZ`E-Ai7AO&5KKv13#1&E3F3 zf#KywU&Hh+Vj2X^;(YLDa3clstJI^ygnu-;2=9wERv*a!B~nfKt_#&K7@pU#BR#yC zH82E{y}mRN*&BkwMpCmu-Yfz=>Aq_Q2j6_y7xlEGcMDFKjtrlp0gq%7&$#B4{&GQc zd^MwU)>Bc*gye+O*`+rAH~c8oAA;l{wx8Ks=XFH1+AyK(Hv33>;3f=qs8*M`mj9qB2jO zDid0{gsdF(ZA78#xsfK#UhumJm56#5%S>4aFDKeOiqx?qJ&Eh`Hq5uKenFHq8v78H zob16%x}7aaZZ$pp(By{MeWbM;4e%)}soq3!Go#MbUzAxB2dW4dWB)M zd|>9V=m~eg-qKQm)T;Jt%V6T|wBhO!7KM7<#yaB@*~B$?k{`V}y0|OZ)HE?VfXad^ zdEQW(0Z;nM=t{h=G$wER8YbNGNi+3~*>9$s=O@pzT!^kLQ8zivk+^#-1iMrzB4cfo zHF_^v;aHjF0IokIG&XW6LX$k*uoZvEHv2}R=rz*OtF3#0LSqo4%cwVfrH?g=VD_I( z9n#%ndwhudMtp_U@P^zD7av%)+zYkbWq@oAVn(Q30`g>GEAhSKr0U7pD22-uWwgeL{C~M0zmrq6Wxn zP3kCL^TzINvp}-!Ip#U>Dtnu(yoH9oU3su6h7*sE9btKK%k~QF|VAoD}s3=l4Y8+fA?W;5joIFdOeG6o| zwGMNP$`4)~*~1*t2oy?eV6M**1|Q!!szGW4sOpU3hANeYTd4G&z0ueC^~rF#1kz-J z@>x-pjfdO%1V|M%mfsZKS|P;k94uk`PXMweRm&?~?ZAALUX9O;rZCeCQ^}x}T&SZ+ zCmuTTKw53P zVWf2N^N$yH`?d)17oz9Z-DHw2cI?`g$}vohKqmglIjOkySKHdzt!y}@^2;Gw-8ygc zy8o0f9i{k=6}fRWkS6G#8z)goMZ-jhVQ7kH=^xqY==me` zy0GX0oq0U?oPYXcI<30i?zP8|MaZ(AU3J~G{N(;**up5q1k#{mt)ivIc|U0q86grZ z{sr24A2~JSeL=i-eL%rtKE*)xv{}h+U@dt7*H%Lye0xOpugyMM>Rno#WG&5)nLJEw-oPD zUm#Dl_$j4V7+#sx5i^$vTy^*PuElEvG}<)nxSV>=&Va{5NfEkR_h+KD@S5r`{1-a9 z-|#}n)QG=7X40CmAX04U_h@z^rwHWRHTWeP>~a`#*ikzPL34vT-&ejOce-|(jC=dx z!~GnrWx&uSAj2*BOPEqrXdLe*2p@@2X2y$tIyYc!J~1GHTBhV7_a0My)!?l>5z;$Y7 zEDO(Zu4p)q;0mbwB%Fr0T_O@Mi+Zm}qTD{m?K7*Nl-sr@N2JJ4b{>%&)sgwiAzkrlZ zzQnsUpWi{Ycv4PSu0j>(O@~8+1^K5zBT1U!-&_?rk}{ev8433DxTzEUU9UznvDx@LBg8wpvUDH z-p?^EABb}|Z)k#^^N%YLt*YOHaHvNuS8}4&3DvoF@@l*Yq-fWil>EPks1ZDGU=fZ1{i* zYec)CjQvC~^FHoZmxq`*hHz+-s{`2Y3+M`zfvV9s-MG_+6FqOaDv4-8#Gg&RbEFqb zH;toK7@%;(bj0q5{Pp^id`9aIiKM%&Utm)=slTba(@2PZS9IOB?;8Js?;i zBp+e$a@aZAm`#AI^mrrSOK6inHrFZi+_cLck#TWvFSWWvKE}jT($dsB<~+q|a>gT?Y#@pXPe;X7UoPgizfB0_;FQ&p}LKF6Lxz9NafTmpl6 zxsxS-+@K_`pvO4>3`T16-N-5|f|xV<4BO3oCEheTCu_%`Au~$*DAw?K-Si!Jig|4m zXaDEaV(y`nu)_w(NWWRSE3(o%XTT+mRXMoiZLTX}G;j1Lf@Mq#5YEA3)(mhrg9R~9 z&ql#(S7pD^jvX#O(F?q)osTM#vIX5Gu=G_n$pOMdD+Ie|PU7t`1DHsQh>3wNe3w58 z>A`PW-+lMb*JFi2x?g*fkrO=k8e3(cKM{k7>P)nYd|rKfd<8I1|aa5;WVw+^avd>CR>=P1C#MrGOz_p;WcL z0;|^@OIFqO$|8CNMK{b4m_{Ri?iaQ)!C*foLZa2tuG((uOWLn4CJrlusfYOOKJ4t( zj1Gq{Z6(*H-|%CG|ES7ewf#e7)CaU!lx94`JjV1cQNcf$OMP$LdPAdbzBPA9(Be){ z)Ru)uvknzb$z)zDhCYz#QVS5Kz16ZTsEALv_8dH^5fm+s8Z=P~3%mM<_=(nYHn*4_ z5icoqw($$sfn{8I8We;v+V4TDC<`t9&c|CBpCCf)4a$6_IO0W^{2?8{8`vGrkB)x< z)N%Q}dnvyPDcq1oM*aAXWyG~Q6^H=_6MdIu{h3W`M6uXhpYH<68C|ly2mNyVAeOA- zj9PVg<7tNHShgbj17G0&a%Q|G)$L_3XgYE&CQ6nQZWuLjB%f|wTs&TDESBG+#7Rl( zC#_a#&D1-dj)z{=H$|V1u!B5&j@f1B#TC-BHWVSx#N$LhuV_*e>SgRxFNuwfB@8JZV0UR?SnoG2XOvw~F=+XUA~wI^>rhyf;-r=gC;0dSvU7M- zTS%{3R0%R|+qz+Jw1fL%YDlDv`IQGXt*)#^A}a$KVRHI3(%?zD*fk~4Far5R5UVKu z`J)|WM6Uxa`eT`DR8%hsQ!kh(@tx(*w@+O+&;G}xVs>JHx&IrknTDyL%`7Vm{sQ<0 ztAA17St36*e?Y*>QF5|{Mc;uH{eQqn_AX>6PIy8f`|7Fdc9Pm`py3x^*7AYA=J_*;>!X=iBeS20*DpO2CU=%fN`v- z%}nzj>frw$)M~G^nSmET*e9aM8hi$brKtKug8v;xv_cu*%73U)|9?<_KrZ7$`j4=L z0Aa!7<4?pgSN;j~+YM?P>=ubB0=_7MTHp(Zz zVdIecwsntJX-(n;iV3IZX~E~|3U3)PSTEB3Kv`UEfwF_O;{a7%`wa|(ig;;*_Zsm_ z`f!noZ)wBciBan9coC!!&=nRBhh5oWIe%N0{_!zhaFM(;s<#l*iSohnIB7CAs<#fS z6NP}#M!7Bjr#hZ~BTVyE^Q(|1?%2-{4!D(LD4u;+H=u(7>7zsAxrk$&YB!@~!L&tg zN+=E}_r{|i(z^aJzO_bM@clHo6Hjqn?|Sj}9A#l~3G$m1LshxjCP|Lm$v98Hc1Z!o zwDG&oRR&=2Uv4<@Y9+&PdC1xw79=D?w&9l%KmE_< zU0V~uB{JPndK2jKibAEpmwxMjD?ggro&^l3Gl5kJhS9F64m7=W>*tsi_yVCj?(#A~ zmt8>u5y{hhr4Pq&nF@B|hbP`5B#gE_&Lb!v3VVDyMPStD7C@K)A(n}~Bs15}I?8_% zE+GDiy$qwUEmO6Z!K|f6N6CV_7UhR1%6zBef75LE=;=^dS21m8a-d5C1}Dc{auY9v zusb!g96$PhQ==02<&0J{JfH`uwa4cs+58~xV8{VCq49OgHgy36mmScfKtGIg^y~$5 zV_Lc{7EaGS#WuXR5zO`d=HBF!)|NF5=64Ij$}sQp|2cu!JXq)+?|F9`(r$aKqTw%0 z?thiKzZ-UW7_l)X8jj25W6SNAH z_n^ZwQ&VIpUH0_ABazXHU`TIn@NKYumzd;0iXzSt4mYn4=GKOjaEh67Fl9HjT}ky* zhe6%Sum=z?c4set9pVENGZ6y=Hxf7K200*j?H-rqeGaC@O3Qj2@}w+_gM)$d`(wk{ z8l+uoPE2Sw$8tFQZm?PyXcc4x%&0OV2!7(b$R=(Rz!A*Twm$QBm)pUK?+vKNZ66OP ztqua^(B(3G-IRJWl_M*(qx8fTfMA@2KtI($@G^)vSR)`F;=+kmb;O-`OA`wzHtv%8 zD6}ELn;m1G*6YBfZK?e`(B5jRjNJKrSx&aVqyW-;-4#2jit`tZV%t>MW&gQZXVQWx zsLYQk@LH&b(#T=arZcCYin!9^CV)O1&x8~8;Q%TO29Kn$V!D*R?~+}7RV^twK;#5G zNrLQzQgTBR`wXT`e2GkEb|2U9+y5$7p8omB(A|8z=XxJLxpU41H`r=b~HC^^E-G)}> zwvTqsfHI)$pT8-=kN=ch?M|y%)W6ZquC@!p3P1aV|mf zNb9q0zVy#|KrIA=@54_splX_gXFPrv%Z;V|NvG!h#!~?* z#ly7XYg?&FL&89aRH|Nn24EOg6~$S4G1kz5__(TY1%F4?$j++R>sH3rK&S+HDTD@L zYiniXOE-gs#huKZuy$?(GbosY2XybyNVrs|cHqwdgV$!Kw3!3dDyn&VUdOhsO$T!QNx+B^DQMo|eMy5L;UiKSu3y4!-=gL` zTSTJScc7od-oiTTBF)05!f-!JPhIm5V0(f3EKlPguKe(Gs9k_5(jDpSN5#EN{G7;L zEq{!v8`XxGRaYSWVLd=4B@>Ie8=~MGkEe_!2ifSelkpd+;@M>emqThPVYHXN?|gPO zozG)JiLPG`W-CGuuJWRg>fZ7@YFK1eDjEYvYRrJgzSb*%1JG zWp_x_rMe|b$b+@j>^T7U5PE5IF2d3`sHK7sZ zZ*Ribeqg8IKiG?-EaT3{LN$mxCb31ATf$ zLU&v+X&NVlEmmw))T0h!$Q0eV0j?m};qyDH=um+$`jt4Ow>>1*`MB(`CQ8vg3+aw} z=EL!`V_t@L75-8&ieak^3>{QLA^ojgqWMsucl+i6wW7a)V0p)llZ!#;36o4b&^i8s zQ6I~>w(0I;{!{01sOOqhH8w~H<@T0()TI0fXot7a2Zj|nkoDz>5ej{S^C77J0~awe zpf;V1e3v%M)u&@q86S?$NN5v*PHcXNOTXXwk>|3?^jH83(DA)JUCL$@Rei!_eKOGG zsFzw=T0#n^)SW!eIF8_irU2k;;AB7RM3f7@4KJ7EonCgnP<$E06EjE`vkpZ2MtAfe zvH!+^Pr0g0{E3dp&C7RWK^c>=ia9py4BRZD3KPsI8Q#OyPo8kp%LT?RDzj$-DBqyc z--TS#qI$FBfi=a`$5-}7g4%-{x;TSg1Jtl813m0z~h8&^`vozg^QSNRYhwT}Vr@?hRAa%Qwjgwusc zdjC6Qxj+mCa3xDY7*O1hK6B3%9%;?nu_FO|JR-W2s_Gt({3v;`DPc~cWv~MS`iwFS zZvM0GFuf%vSy}VtrY?)cu9gM=1g0EUJBODZ)FjC|1TMhpDv_}adD9i+P|=Znx{Uri zuJmK;#V-x9A<_>Knu$A$v5?@69|WN)QTG)p{wZD(xVq1cpFpxB)D|JMGdI-ATt(9c zYP}38pwDRphIS~gxF0-ng%?{N220ieH+k24y0zn(;w3+U22;|Z7Xf%&@biKU4QEf| zH&SyOWif=A(eM!seF6^{TxpGYP~Xnz+%Z;*8oY19m=pyGwqgN7x_R^Y4yj{lm^8Uc zR282yuxGx!%c|;Q{%6m}3=Fs3aKPgc7px2ydxZ`xd(S*GGM6F-NC=Y~`OyZEF3|dK zNqW@nDy<5Mm0tMe5cmmDp1Qb=H-#f2*K{4+#R0F)Fs*ACe$pUG(>ZIf>m|TMcpNgf zV7}I$X#u1j2AUdv^o;l{(+O$i(g$P$QOCKZmK*cYGc!AC+9rzGuiII2L=$lLEKu1l zu@j2&g=7vo+ATiM>tBGhi7zB%molQk3gQ!*Ck(QyE%YER`yMhlX1U@9PBjY*@P-?F zz#s11Y~ST@j&`xb)Jc6Kgqe&w1IBboYG@3e{;nlH)cVjEehKKEzt(h)bqSod=+VCIyYKgHBe^y1_gpibzh4`wSRE>QI56D2Iyie01Cj3H5WDc@( zKy!R9E9oaS0Ts8BG4(FoE1bm{_U-0uRzpE>DBbxw9kN?#RU|CRU;w4F#Tw@@?fLAw-UeO@t z5`BzkCX~!H(2eA=BgF|$(fhc>tj=)aNQ`8pwqj%!+t8@k&L~^RR;fSA%vtT|Liola zj`!7PukBY?s1ma>ZKV!i;M`ejpXtR?EAY`>)rIV{hiD#Z7NhFPel8y&UVQ`q5<&E( zS0!P=hnLAOgTxHAib)>`3Kdtga29cL@ccjSq7(XlKSFnnjoPbi&ueKGpoHFlvYqcc zef^Z`qkyVcsEbE*lYyPzEp1#>FN>*R6yuA*Ruhydt|7p92yakB1wr@myo{9%R2qP( zs2V|^+W#sIKR2L<0N2;4cOC z5%U-os`?|yDOwRH<^n(c|D8{-ZzqJz5nR-p0V@Pnq5IiZcfTf@j%id0YaHsiKFNygXR$0h_teZ>)8IZ=nJa6}^1R`65cmEAEe+o&F1-N>@`9IB0 zc#4jrF{kHg%*6i8QA{iY%Yp%gt3aTu^VCDi3U;eytkClDuMnVr;hY? zaUxB9-^xYc$Nic0GLEM9aUyLE2`u0g#AG6X%Lu@=-@dyv;=icWLFfuK``IwmwuYRozVUL>L4EVX&GeVmrgDAsjUaMl>jYKY3 zXAlKcGS;k>o(j4_2{k!)d|aS7bsW4W+*h!0D!ih7#rW};ubGpld1XKmnz@brU zJ)kbRVx4iB7~^@Brsvyl5|RvVkn{my_D=b@eEsf74@jyQCMPewO0&6K}EM>su_J#nb-GT`IlDZ%GSFalF9BInVPBg*+au+x9O6 z`rEAH^miLd7Y#2Pg!;uDj7%@S#q*j{K=Y2Lt*N08j&b@Mw9q`>jk@BzP{o(E(`3lI z!FXK+`8~<)LUbQ679A>c|5b|N3y*guK?wWov1@rZ-XSE+IF7`t#*W)x1?H*^Xx_%3 zgSc06kyTOgPy<75<$QCSI^(;(b{Uw3UcyjZCc^3BZ;Ni4n*wQ^U*$sSaE&nA(0`+k zqUG`TpEXRQ-|{D7v_$&Yu7z~KPpr&ho7#q2<9UnmkJ6f!AZFCgj7KQY-z(Gea_=vw z-N_DSF5bgT*W7}!Lw@yVmdhAS4xrd{O~D>h@!O#E=>9nWkg8WrE;&N9;&>JG>@xfo za_&WuW-&5xIL`qXByYl0?!4AKbYJuv;kDs#g7VP$+Gl~|0q$}O+?9CHGwM8s z!!MSRgg_I)MvGs5ccNqihjnLfeE-+w?|%dc^Ca-BAl6PHhqIpuhF( z_Aeq#?EfFezB;O{sELyhtT;uALj@^LgS)pti%+h_s;z0PUfx%>^`XDOecEWs9zYTj=|;|A6h0H z9zf{b(-rG(^VS2xLw`0pYUiYXDF?_nNB=}n;1mpV9Vc0>%N6K_i<0&eJBr}X zpVYrmdp;ujVvegDs@{vinKCy+4Z=c6e#e3n`l*G?{?%BxjH@WCGnsDk$Eqag>+ofw z$L9hTdFK61nM1K>P3pJIQBOSS zt3>b!|Jjed;W7dGn3XNsMeP9SfiHka1KY^1?jK)ozKD~eu7_(83Bj{pf*Xup@h$7$ z_%7PfuJ?#dwlu}^LkdvWXFa~5+_r1SP)Gl1V%Q|BRHP!UWm)t90ncn53~Y7?YV`Y* z8n}es#=+!(_PU-Upm?SDvk=ZB>-0M)K`BX-Z+Sy3P!1uvS>{dkh8X-nTlr$+0%G4X zD38cnf4u&TS$yNlcNTVYR7Ekwx2~Ou+d!bDCn_J#SPmEH$ z0rx9+(hv3F2S1@Bl^BA{MA|TU*ZQ02hNaNYD4fxkpHf+1CXUkj+#0J!ycjs%^H|H4 zy3@1%CiV{`=sIrfroQRa_<>rjHzX*85qQh-u64`3-TlT9T`lXF4THiX_J$B;E=rO( zW3aGwQT6mjKh}0T>HU@{54O^zr_say;uAd2gN*D8%lo}xpNCM-;aJaD0%NHQ!~@A? zTh-kPPA!u7DpkjGo<-P2!pOyX|8a>CYv{WE1S5L38fa7@{DN%nY~cE)0sN56@4e~f zWelWdhVBNoB`Gry>_d5$#)ro%?u`jykH{F=xRAddC}EaMr$mg;pG>cbciRH2;M4Vg zCn*qfn1;`0NQATa3NbJJ5&C-!;+M9!nL-=N8ff~` zvAhw0u%XU?_v?*HMa_YV?ZN){`Hn*d-mvbZAX}DxV&~gG^uel^4M=H99e+VYHrak7PPCj~~lZx(W^=-G-{C?AGT4oZIPFL^Ge!J-u z>86g`krW!ypC8os)*_I={ZUV0ofp0rV6w^qaP5F5n&A3bx7xh=lSJT4dN-3l$W zQ1C3uqLB%ny$&Wmxd35KQy)>3YopWO7Mi{1U#t%ohBRZLXvFbQ>8{v(-&5|P7}8jW zFqWQ0hl&HqM%n&$R;Pw_V@n4j#sZ}iW^?!~EEjgum~b74t~2BxD=oZ!`E2T=;8Ws^ zk5^)FUxJhwB$Fc(4rerfrkAfCt|GNocAc9o);u>J>yM{js}Ail8;l!CN-~V4r*{bi zmB+GJFOKb!Tu-5UqXV?IWp$fR8-h^WzuT}INv7{7C3<_Z={#pdH9 zA3mDH$^S(2Wib7%(wHb?VQ;KBCn~j%$bQ|gQ>z?U5$ES}8p1u2q>`V`DnbtS>w77F= z;3!?Lj;e2>oBnqE9QpcVc-;lEQ7M`(ia%RAN6W$TK?iKhSypGi`tzVGioe%uz7>%6 z*(ULz6*I}M%Vi3WA8T50mD7HC8K4XB7yd(y6yGE=MAeRtgbq3`3dyy^ya1x~Z4(V_ zRUOgMIRV>)!KM559@jhZW;J?B7z<*~6*1?u*F1JdM!n}ckKYEVDXVL*L>$&4sPt`_ zV6J5?i_{a@KeoVV@DOfGh{4`~&`~%MF+}wl!L$)`O^CT@#GJ``bI2mqXFXEu7%R1J zbaDGeiD6B=3(Yo&xdun>>;_;FEFFeV4ly;P4k7#VgeUaP-b>+g;YX}|wvGi=a=e*q zQjHOvl9l0?D$u>C@2;+y_Tosfg)p6?P_?bbvJ)*`<3Am)Va_w*Ru2km5*0PLUbf`D~8#F zn0p=g8cH%07HAN~A1ioFAoaehvy|@8sKR=!>dukrj0hZSyOU;7MwCv9RiT(8es1uV zE`nvDdCKgN0RIo;p&%nw^*bhePv&!j;IK8nS>ZnF3G(UP6P$9v{BZ+h@5YPlk*34h zFf$mj?ZNQh{ZVhmMfoocy(~3*rFHHY?j9yN6&USkr|#|0@+Oh(0j}#sV#r>@^a_bk zMNfXvG;~wPe$mR1>GH;*qVCPxN<;#kc>?gc<0IQfP!JHhm&S*}KJR9MuGY_P82?7@d&&lk~!rP9 z>m1w*+jot4jN)+*BTK$v%J1Xt==}MN9(adOo!%IHg_S4Jlq0JI_E_ zxwXBPYdbS946D}uqW}A+bvzLW)`U;+^$4o3R~SVjekeD=J?c(_i~m z=S51OJO5F;}Atf79log#EQ##JLl!H+EzF@S?4CJE%`={ z^#1g{$I0xKOV~s!^GqHB6`&Pb(^&QhoM5}=P9{qnaQ ztAaOmkt4DXpZZzyuU7qf*&~{rcPt+tRlE^&N8wBQgkiQTZ!(+25dTh8D4zD_Qz7W% zefq1`V(UZssrwj9QA;kn7;8jM&}d#UcVG>^;q_nx>7_k|Vy}{BtviL{bj~Z2_Goc- z1M>PCpv&6#`vs&@(3BeSo^O=YL{QgeyJQZ#7{CbO9oD&nYt5Ur{Gf{_DDnQ?7D2#- z21#$|Q&O%lY?<~WaT+==BmW2<3uAJrid@Y@Bi=JlmklS=8}mt5J&&QRT^u4F!!UDwWm`+7sAU%;KS(A#{ zbk~;1%2Hx{YStf|OsBdZR#(25YUpjs^FaR4uEta4C zq8zmCDheLgTfM5Y!swx4?~BnSStqeJYAby-)$7Y!dP?QM=kWL_Y|=}4 zMcGREauydBuIN+ltP#-%-cUilmG6LP*+4V%=YKRr12s8h-#cx2uAmE9E3oU`ik#oO zDY9{+@)0*g*eZ$Hya5|EV0ypTds!*nj^jd;ATPBg+XfVbF*rbP{9z6GMU(rin5R9abHvXU%k>a{h z^0*I~+^Z*is1eRffKnrFIJ0Y?;J-0>-QlUa)Mi3(bC0*Z*zdhVch7`m(Y<~pXAb=M zK5wjZ#F%70<7d0w2RA#&Qnf{=TU%PgtvyV+$o5Ary_RFmUsY zTR6q49-M8!VHVS!V&0*?<*E7zvsuB(m$lTFhBmWe+XEYm;0L>Fozw<6-RPGtC)_d< z%BsVeVPX+%CkoZ$(0n+OF_lo?6|cG%d9ZEW_W6?i3gNb+vxwe zPqwMm3#K`&{b*>V**qsgA8G$~*U}#N#C`RYj^u2_K#KKYu%O%D`yIP|<&czyV%YuS z41_5HPh!lXZ&U2XTPabng|aVndEQCJxk*ZX*@qKFlurK#wk)P+$&UXjF^eK{EgInXqEB)ScUb!o_WlnTnGXYQ` zH!m<^PY{ygxBCs)^%D~Tmrj!{_dF^^DY0U}@pPr2F|2p0om*#w_!>;A{!E%}0gb#W zk>51K#%flNTYlSI-264r{n9^cJQXfbcAyAjS{J#OLmU*L>z57zhGSht%MEX=l%T^8 z(d(D6$&;Y5z=JH2mi#u7{d}E?`;_3Y+f1hGa$xoL^^-{g!2j9(vOb!F6%{Ot^aom5f=R z$1g>Ry0}_Sh{P}r-pm#klCRxQRwo=S8HcZ5-c6ne20pFYEhbkSqhg;i2`WY1(B{MGDckGbYEN9<9$3hAEEH$5{tNZ%_qMecFIt`@ zrJ#6V;o&>Y0P`1%GD{Igk3z*_*5zxys6z`CWauwWS$++k6YdJ?0p6)NsnYizL=y^u zI8`VVQ?n-X3f4uIvTDv%E8CxRwMAS@MdWOMnKVd|4rrpVYkERW(|fF+w^R?#E&f|= ztNiHYlNp|}_FUtb>WKAA^r1?O7*atz(~_Z>$8>{Gdyp-ja$QjQuoPJ52SjrolSoXw zoN#{Wadh0J;mZa)#5S5ewPFSej)RYj6kGY2<=t%=iiuTlxJnrZ@Z*PL3zn5?kr-Z{ z<9h#)@_HlM-s4QwR=b)|ouQZH<{F#9sCAKy@2=i(cxkhOrU39=y?_Ld9ru#ljXqgY z=5U9dCDhMKV?Qbeu?L|Ws7I%4>J9-MgO!dhWI>-@MObQ*lLZslBaG!;{ieUIUv8Gp zn?veKY}o$dJr7ux&1*_N>1K%miY`}Sq+$h!)@Z7(NA8W+q)5? zpVakAc0+U?Pcf3VlO5`gplLyLqUlLf!O9=JWp1gC9`QYmcxLh#1gfe1JrLZ^1TTGE zEXGV+_broHT^L@?jc)D=3A<@auJ44~7;EEpcWA9H0nmi=SU^b^{r}<3<|(t^s|9gp zAyAC~?!VsZNJ-fwyXij7;e$XWLIhn<;78Ejk2luMQ|N>Eq+oBeXsFTt!M>XDM+k(7 zIU*-;`^LRmwhlWU_t%;NOi2Xr=yeaxJUX4d9QNI4F-9-S#wk_iv^V!s34nadS<)J= z5c19*bXn~+5#yOL+p;KtBZcCSj6Vfd;zwB59n1`d5MdT7apztY5{SqNVHe3*5Arg5 zx>y=w*ekD{*A)*w5gvPkoGty1cgIwFWrB8%$g<-ANyy=idKIZco`@WDCE!qidEnDu zU(S$stA44E?MC1>^7f{Dd_oa9tE&-OU6DE8&>~XQ6rfMSh{C}c^9ao5e3#@}PQ$>Y z1SIdQsx=Ww7EE>5)CHw-s3=M*nCw zvbb9+jKPQasGAQCnGl`Dl(E2869m!AFm(Uf{4{qpUsU5&#LTHI=kRN;N10q-5e_sz zjYpIPi2QyNb$Gbvy<%pH&rz{n`v|0}0P91)Wx4qM{i7YLE=^vKuHPXp^5ZYp@n1ml z$g=UJyXaoBl&J#jsHY?d^WJq_VGlfjqzG>XhoxnZTP&OB!J^RRjp2|9m zz<{yn_p=4t0U|yfQJ6Rh6{3nwPZyByEKU-JO6$)GLP^%lOb4kAl}h!L+j7|)_&K3c z>?~!6v9A@eyR&O7u!@hq*I5P#2mXNR&@}YGi?x`t{(42oGoD%bR4-E=J{97uFfNSC z=IOP55pnR8@he2SnCbx!bz^~^vn3GjPl-!Y7G_x|cxk)~KLQIuFUmAzTHjlwm?QX} zP4;UvXnzqS`ltD^tn-De$}+9uZKYO_13C?Y+&uZ@+#e#;hj6YHdbG>jr=Bc=F;n$3 z`jstd|H@`F*C4`7hTcgjRJV}Z=$Q3LfMXa5%uIIuWR)~q#t~8*^W#ICje6NNUgHl# zjWm}CE(~G?z$uibcCeI8AUmt^P;F^m5+e-oa%#cc(YH}r482f-xv4dyR_8*+b(?f#&abtT!q6mRyaC#uxZL*VLx zYN(RIbv8L-Br8)wQn^$MVO~MKahzRE)R#2ZlFL)Wd+<`1>+E&iMRsb^uphxw86uJd zjn5?RWJ4xLxO6CST7p!{QD66F?7^)q4d1KEv9B_TaAKr%Nsk% zXth2`uzG3RL+4ZM&sq4J- zAlqe_hJJO9+(<3%c>JHTZSkj77>Nd;!GRU7iq?2JF{}O8b`+uQB$OuYv@?%p5)663 zK3V8L7I7cX^rfx+_f6J_8AqDJ~GJ1RvS4uZ-v;%bJp6c{r zd^axrrBRT_HSk~O8Zd>jHuVMt*?epMPlrn1n`6^+9jw@Tbh3a!Qp(r&Cw8&(0X zkf|JX3PtFe$Z!R@@%b~C?~HM-@+#XP7})B}k+3ux$HWD~RcYI37c>5*w63Y}@2P*= zMDEgma{)RJazCe<>pa#%6JQF{c&|x8gcTa^%tZ9reC4>5&J3dZE^@u`@B5^b!gg84 zX$%t?=pWTlmth7l_W3h(R9;zcxx>NiE`R}DDy28zG{6F6_X9kly+*BPtDCHbQ!Ybp zwD467|FI=NJ=j2yOS+I?FBW}Bp=jL~??G?CG~gkujcK>&w6E0A=D-7YJ2ET9GJmgJ zeAKk1BtUWuAi0dUA`W{>aY-0jTg&GM!}vo&-v^Vb^UgaxCXrfM1n)94NqojuiZy6g ztuW#9XRWdH?Mq8$;&UIKJc~^UxC?{^4hEQLU8R&m@-)KDO$FU0qF$%twPWXpSnL7k zgL?_AB`+QL_+Ckr*KNKlvN+Y+t8eh3sI>NwAi1>Owe-$%iDpRx>kBd==ceA;=_IoM z0Tt>T+mCv`WvWHs@F%Is-Wz*qLY0<%A|K)zsfG60Q=L@p4=k8#XenH>(z=gfN<@0e z%bMdn7Bo;Q!upfXxcs_2Wo)2u`qE-$3wA*HB@rxGf><{SduA$<(wAdQ^m>Q9fB&U7 zVC(zt_`}%%=jnu9z1iZA=LD^-^?AUxbD@Wiv6ySMB+K6DO|v^5aud6Abgh7ECs#?0 z5*D;;lJUMgI!wQpe4y@*Q4IU?y@!wL?(y|D#~eBkcD=0TrH1=s&oN!oe9$`^iKW4& z>@-^f-MkBQ)i9MH^AO{bW)KmQ@WU~B393nRAC%N^IaxSK7Fn5J` z#T}NtJutldeorF(A?H^zg<5R!pH0;O=dM#vn6gZETxQP!S7y-e!j`x2XMa*!Ei*{j zIVP3G;76PmC&(W%cQ#ToLsJGP+5%E?fIg{zp=S^InJ=pAh@1AKXS;kO37j z&JH-1S^4QO5+NVzu$&!Mz00R7%MmiRF1#^{rVq<LvU3JvpCPBu=vSkCaI6)WVD)SJ8Bt?JD~5 z@y?+ew ztC@vjNPz#`5#kWz#lFr(($^^IaQ8xU1m*Z7a71`KoyLa6YY^YaXoL#-0-Oa00xZ)1maLF;2eU3O6~FQ&=sx{$J3Mx~|! z=^TDFA^5He6t^3yO!QmCyXAo0GAB#6!q|7lEw*Mn@?xl%8}i3$MRp~zw!CPb9VZLm z;Ey$20lPPTCuvM0QjK{dQ1iGD72sj}9=PMCKO92BVOHl#lJfw|Ggo2F3fF*_o=Ano z1cqh(jEl}U)MgbMC%h%RKX&~p?=3KYNxkn6xcqFIcy#F@z#oyUUF5~|!4iTf#3^7@A1tMe-iw=Dmrbtg zgh#BEYk_L6U8tMPZ*jpwFXWZuA88CIut1e~-fiaMBhkI~p$7x@wV7lV;V$7I$9NK) znmmP3@HQzqITYoPSE(@6qrWzB@tv z;evn`rI(3*$P2^`>0Op1cSSpEP6NScGRcCqN(X{`pc41;S3m6H!^1c7;TON@A=<1nLL+sZOjL>dF3A7u!qB2q8x5P^ z{6`XTC2t%5wdwzF8``>Q7`$|;X#fk3V@w=tWYGzq+x*WU1Z_yugcxR6a(!C{g26wQy!X5g@9=lpH9|#?N2>r|W@t;P#L_0P<0!M10cKs3mB%%2q>IItt@m=du|1{-4 zLj1p(D#-0WCu~o*kbkwoK@QQOK*t~;=S5`Bp{M`faw&R>xu%={E*IIf1zC)G%Vk*o zX=mM%>5hCv`aUt@!23AoYNz0N01f4Q(?13Lu`9~Vm?rTp@GA0&<;<^LQeH58jV0Na zSW`z_$X25P<9VPTtO@qml;_Ch(o@>>?x@i?kryXFq&G5Pug@1LN$W>ksZ5j53kzF4 zMlVCfim+<1aL!w)5U1@@v3-ozAFj{gbH{%4y%wSdQ8`w4MdWAr&5?!!!4`WIEB0XF z&eE@tZpT|?MD%uq*Wpt&5@t{GOM%^|Z`#gM0*zN4C3)2o1alc9dniATbZy|o!@K_} z8UB>B%q_oz8QOKUJJsFN(u4BYtBaz%IYxyg++;o{{BWd;DR6IJ5F@09e2`F4twyCVh4KkkRFTkTGLsNXz=zzybzK_!0Y^rY(8!voD^M2GpMbf%EX zt|D=MmmV{)@N;K69fvDP>81%l6`1I_oBn?)=x$S(2fKrZhXh08v#xr9;)y`n)njWB zyG}Hd#Tt^h-OmG%A-xsr0U`vMN*0+iJDmTN7+$(7f>u-XMXN7~B2_PNjek_qI`MCc zpuf9K2;daWpV`)LHNZ2u^rB_I)H7$4r?D3ca)Hx~W~re6K| z+rQt`dso!rqss2_He7$K(F-Wwxvp54xCAdH>-o2!is_67=i-wy<)E6`C#VDy zs73Ipu?aB}KFy;s8i>=A&;M^L+ERS~JCc;?CnjFvmIQW0D+DO$2_XNXYDGo=Ci82 z=iZX=MAtSWR$8~6&L-NNWneEbGV;m=ro=Df31~6H>8wO}Ov%_w=IfNxrLS3!sou}C=_e{^%USsH@g*1Y_Xyf<;b1eimRkYOr znffl~%3sMWanHi2ZKuNx$vO#B#aQycl{}QXBTnFQ(=g;>VW6^)zRWcfACO(S%b-;@*t@Rd?!2{1?@Rlz7ZyXu-yUPzCK5?)jx35 zGz&kESy!+AqM$4AQ6>$k3VR8AyW$OdH!D=Yy8n%*ix}fRR7$!=AAtwpFWeor{K*!S z$k2Hqz3{_A$o{M%u-rhz{i(X~Ew#Gg3H9;MJjF0eVCL((up!!H;2JH6l%X*+bgTCV zr`8!k78C8$TRN94;5lnUaPO1vsV+H;i49qC!BsHkmA<3Xy8gU0F3fuDrLIypK@}Oo zBf*u_?Msrv29Nc1A@f^S@^2|Q7be#5I}e_U#cTXnlUsJ@f??%{ZKD-unCfgTdbt!~ zE|>Mr$+D-fWc*Ip;rBQXI{;#+`ghfb5j!lpvh|S%krnZv?$boJTV_7oOPV5@D!9#u z=h)pCEx@hsx?ii2-`lv_7bBvUKVbrUi9fC+1)k9@NmGgmC$JDC@@?hL!ZRdNcgUsx z4z(LzD#{Z+q8MO^Dj^ zFn>ApiEnl%*rha%Hso8Nqs{Ih0l#A5IOr4;v&&ugl!yMKk_rDJiu^UGlDGM$^?cAsw5O~Szd zd%l)sYD)eVt8QL?|5rGz1J{xHiu?Ba#N_0ntwJ2J*zf3Z1Z1`_6opVunr+=aSkr%1FHzIy$r+;TZLG6J3KAW)e6QXr#e&|1kRd z!xbCC0K+?bvdbEsKPGLJntNO1K!+(2!s=FqZ}U4`R0hhpOc0w1uYQWQJvyU->=<_@ zHnXuB`Xf_?wTr=(J`9tQUL2LI#Z>3&zleXi3E=CI$m)Ksm)Ej2k-e$X0PRYM6M5j~ z^_G%l_)}gI;CBiuun;|{@SJ}iO|aFdl)}Sq?f0@?RuY}^&MkShEW`5d6qiAtM({m} zBk#&I`|BNjm)SwI?WyL+i@$vvB?m3%%qeEjIz>Eiq+IS`3eedELzL_=FXVNFadN7^ z_5wz-k=`2LuQ-Y;tNFOZ$wgTAdHpd=9up$Jc@-c>Zmzb*K7AMo-s(@od7agI%f5f= zDWIwP)ApWVa=n$1HnN7w;qmU&{j=RN#wg5l{Wo>RbkURYx7eoUA84LFz{lWl!j&%{ z0&{iQ_P`@VO1gG&^IMv+NFRJ%J}>Neg0v?Y_Te1bhKf8>mQ=eT>Cu>)8V=sH_-%2f z)J~G4T_+cDSY0i^%se|jLI=?%Q)&U*y~si~vDrT;&B|0pkL31|=cC?tQcaW5H0*i} z_%^>7*QNsOLwebA!8>R%oJ7mVy*huY8!<7kE;&bBY(RHDcTaW+C>HwRA!NT8-*tyU z@pj8z{3UHAUY_QQidpJ&_~DdV%P^LXOnT^5htpa%=HTILX#WuVk=jL*&CFH`JNGj? zmPv8Rm-biYjiuI#f^sB|pcLzJl4dMNb@WOOs^Lf=bhFuU;O0!+3S`PL9tWQIB}gCl z_n}Wuu2f&L6DlhSq>shfTnv0qt~BbmyP@&xD?b@gCzGCOx?G&aTSflf??<-^6e16} z*!%JX`_AmND~a~}O*Bqrn6%kpmz=O>cHQ1YX0(IxTv=ux@NO0yFiwU*euAR9?v4w& zjJ@j>f=UyHZbdBL1EYwrvd!K=@PBGYm9*UZZ zekgi&FrlY7GfMu)dYLWd7wLg`?H-8t)mSZztG@z$H#q0ev=yR z6{?=oIJ?$Hlot61p9PXjT!q_pY7p3>@v?p-2X?{mOZC>90-o^=bHaxSU>P!Rsd>#( z7UizNlHvhg7+VYk3XFpO1NZeUV!puz>0JfU>t?I9#q#*O4`DDew92Qx^te9*HbTq zWICMrwVaTl9H#p*Uw5N5w0{<`r}ehJjHqD^_2rR?XFX@M{#$Bxrexz^`Q zC3vYOQxp1#N=T6#De;3qoC&y7;Ma#ro?SYYyWGrD^zkxHvBS=eO4;2Pfg4WWs*_k9 znK{}|q-gW>i(MzKf2NMH+2NGgzS5a{BS8I8CDiT>p^TP`T8^bZZr3PAMo0+br+g-yKXI?NVj zjTU+ltFBiKTccY?Jv?73KKO;H41Eal9kYDD-JQ+Z7N&Ch!Vk-Ey35Y(_ak4&*A@$5 z;OCC$J-~q7qA(BlU*fl72eH?NG>uf)zqT+*vVNrR<)(x^!Fc_>#7NJ+oEf$BY*N*a z?+7Sxc{Cn1r#-SH2OZh01|6}vyh8cVpv)2MY4ZQ5y=0c+SulBf)6~_jV2v9#+I%)} z%Kq(MIE4X%dR517tDa4pYOR~Sw)J`~ppo7XIA2=d7&cQ1m{OH#)ogrMa5p$Xhmnfm zpKZq=7l7)tCBgiYOFybTMiG&S(UQ~D{#vb&6;L6}JIe3%^u#^V+EzgQF5Yv{9jjhmm-A3I(@ZSWTUCH)TRPBp z)KZG!&PakBF^hjr8f0V73tT{ipguzEh903dTdU0%r@um?TYe1NVicTX`-MV+S{mY9 z)s@zz?&s2R^zDih14NQzZ}FXE4cd%4@0P`8hGV<9t7JW-81nSsvQRiWr<0xCiv(MH z9+)Zr@J2Uex+3VkK#C?Q2!Op{09@{yN4a+jF)D%%=|l3jA*<1~RCc{3(a)#Ou=Imk3-gs%F7JQT zZbh;u8OJYR^f8Q42F3+E1fSyo8?shTT`3F?Bl6)A_(OOKmsgbA?#Dh>>s1tqtRTY8 zw2Cml%ykV1A7=bYg7gg^u~yUz<}Y&X(?NadQS?z?@1gxfZS%l%m|ZaD)|0XLNb&&Q z5LC02m?y^F!Py@)!oFkJ;?;FguxX!zT4UU(F)b;1&rZzTA zbXhQKHxIv;4gv$VJlfDM28}7;DALc)G;;JXbE{08;`}JzS<79iW1YoHWj~qS^hOe& z0wouCBE>#>i_^q-Q?<=}MuU&rt;~1Jj-U4Aw_iP*SjhwHHP@f4k<0zji>7Um5%Cb6 zzq7~UtL*?!(x!k*3W`2P`NOP6UI)KGq0Jlo-3}@GykrH~v|P)DoGzZ2vy9b<=eE5W z@%2q3=_V%f^&tvMCAK=p!nGG&T;M~D-TfqImeo<0`3CW)kh9m4$*1Ca1(NLr=aZ54Su5K=C#%7QHuBB-3cP3P zBI0%XH|%|Hd4Wdv%DgR4j9xMQ)EA<|KEJBqMr0J?ccL`Dx4*VRzMZ@07RU_%F??bO zTNIn`>6_2uddHG89dFRYfVq8tL8r${eCJD^L5$aL!RP3cnTN5O{~0l=(2lQTB4bpk}zwY?~urTSM}UlnpRe#!#Lo^M&tv zOMj-vS0WX4JLIqzeIha~lUUx6cb>Mbn=^Ppk|iFI~VVy0cCfo7B-8*Vx zhdztGX;Gv3j~z^?mRRX@8O5<}>hxymq+`{O=GWnoT;sXCD550Scy!7-HKmdsKlCQM z_({k(v2mWOz1Rlk3}x5d=_-Qh+^Zt=y|m5?l(;hS&ycUnVPiM;BObqk5~SJg=szU? zrX~}kZj12Z82<^77|b#@8AXcvx7}R4{|YY^Kuh4y`nJlt*Y;&FTX58U_-gy+CH{?=U`2Y6 zWn#d!D_ztVpK^L$2Ibd9z`7p z_qlfJ(M);CEPVC)mVFwiX`l5W%)*-P(+eR5|1*E3#BK7cjv3Nls;vz8E5-^P>!6LV z@&V$bOxIhQlws>#_4hLK*DXx0<9-?$)-TySlA(5^_ zNvt2kcr{GLo?sV`j5eb{{8S9o8Tb{V8TFY%z1YUW!V5|vTZ5s~_EbyL1ludMA9n?n z?Y*-PT!mznt25Qbz!lGdeeZsbFIgt>oYw}Fzk_{*ND5QlA&HdOW}L;07n`m5cB4Eo z^R~Eg(*nN^g4y|e@Gm)3rf%q(T*72)rF4ji_uz8Nbnj-wGQL_HXuO@u$3GCRYu;C#rN79jY?PQqX{>}ECY6rrCW8`gl5(aCB-*3 z*}*IJUV#J3l@|6DL#A}7A5)&k21>{cf}h?#a`776&YD`%ec3cPUS$ZRa*?|+08gui+g*7W&6kK1w zPNk3PMZ}!=vt^a|D66a%ZF@gVP%x(5Tc4$)o;$6_-iO(opf)Zgn6TIRcd?g=&Z!^1 z8`7@Yk5a+oe$C!lNPNqY4j)dMeJp}{Bq%wh(KJ` zlaH}f-C31A+SGOb^jJl=KXb@I;9BxYs?f1SbMY9b{nhkXzkD{mEvTe)18XLpbzsj; zzW{@E=i?4V-=`x(OV?~afrp)8ZIIm|HQ>bOA%>?VO>RrH z8JT0;F{EKy^HeM{owR<8;-UfTT!oFLDnY$2TbvRQBa`^CSBd*Iu@+d^YYWjcqj;uU z9%fyy3UU0rW$x_8$y8wiE^h|kWx{>g)Ka{LZMOrU!cT}I_N?nKyOvs- z0hgJW6@%_V1D4HHcc*8v&|qnKz0Jcl_Dd%EV9>mLD$FpX< z2JY6&_``U!Y8s8%t=>XJRNfN`DdHlMSGRT~R@&b;Ub<&*J{fm|f%y+*EW3Jkt)w6D z<_(@~PP7H$ROBtHANmSslmBoZ18n$j+*grsSKeK%)>%7i#NKA5qeqfU6_I*^%TQz0R%P<6GkIRBh;$+>3w_%MSf z=Nj!Q^PD&Z<8HMe_6Nyypfu$j_m@x}vJ~)0)40N&H(eeTxl`6s*|1USC(C@TRtuDLpSN-Xk|pZDc)=iAM7U{ZXaic5BpsxcVP zO|AcaVOhlahqE?4RMZaN&U~rmzrb6K>tqq)OMJuYw_u^DWo*7gb_R(SzJUm@(T>~55o}XLlvFriG^L{{L)vsP0=cU%0N?pyUof+&2q;_ z2eXczlUojtu>86A z=^am<#H?EL!j8KpVHF$g#4%UXb;pkJ+UcnB_?N=ldEe|i6K36b4(sWMlQ{;OjsaB8bM3e)@x@PFaG{UeMRoD-mjRGV-|o+b@+4*^fft zrNfgpGHGu+=BHB1&|$zH4~Yf)U+7fo(q}e;%xXN+erf;7t2P#8u2=D-8ZsM=dn8*L zX5*@1^oldl8@=~bN}nMkI5e%e9G^kmF|f%XFq>#msN?+S&!*>nKaJrT4T2yemIRQ}3wvoj`Qg{AqAg3Gp zUp5~IeQ4%}lZ~S+`n8we5E9N0qNq5imNHgheVBvGSclw|5^~6j$@bVyYi~+)YH(ZJ zv?6Svu(Bx`>@G=l$UvGp)ud1KEsk-`OtHM3DJ2@RK3V;)tGeVAA;~lpFhOsOqQ@t- zSCl<+f|0G(1!>}UFs#;DX3>0poExax)r>-aQpobAi?0^PHlGh?$xPQWYE+co#99`}3>Lrret-DO`*m-}P}4IK6NxzypwST`Amit|ImAYqy=&V7(kPHZ>$OP%4%| z!?cP0vp*j`PXSOGn>9CF7AHHS4>yo0blM~)|0~wH+ETC=6%Rlv4D*k<=1PLd%iH}| z6!3KV`iIwIjJ4Ca3|SAnt!~K5cBlqy29qAs2Y6na@!iXgpv>F&CM)aV{R)%S=n86V zd$+>%s5xB|CQA0s%#-jMddpQ&=KE4YOZWbS$`mu@@hJ&RYWu~i>6z$=JR36`&^8N0 zjGws`#gqjI%9PDq^@Cw!U)Vtq$t)XcbHayKil7p^I(-&^fa~dXVNQp9dZ{>#2Ve$A zM3`$H6T59Mduc+vvpy8Voyy}*cW(CL_vF2aQXn0BA43@C%5U5&w-q*a5|omFsX6Db zugB|?LR*-bO0v5f#7nxMBlk&8LVN|3Y~I&43@!!e_;r`d8;}7>f^^O!o?HCUmQCbO{6A>Aroc#>rW-q%*!IS@y|J;;#wkEQQEcxs_5bTHxabyV4*iA^Q-}&4UWx9;c@K3;`j~qyF4&45gI6*vmJkkU&GPA* z?>(IVbC>;l`oP=j$&YL8f@=pq4dEIW#WlI&se3gWK4Y5kcMYLd=Wl0Ci>C(|@qn(} zS2bLJwaARa0Mv)13KglIjmd}KD%LAm^rfm%+l+XV{?70`1xIi^9~L-E9bWjSK;o=# zVEGgK9UVpqOn9>10XVf!>6~JV0#KKK4P!m(_ zYwaTG-xsAcF>dH5u1;#fa?~^60=kPFnd#s>-fs0+nNe6>MgjizD`;N7ut9%)$$dg* zLAqNt^-MgrO3N|vHdI^!dP$lw|2v$JR?mO^4jW3`Cu3p1XYV$i{je?7Bs1?BiUL<_ z=hpZ1)WtmLDk^3G^uj-s;}+WbXKw?XH-3U}=CmUJE<}9H5a2&SYW9gPEBx#k3B?$Lj5ok{RBzM z7|2OXgmK{)Y>F@{>2dqn!ELDE@xP z`Cd_!GB@-|{JFb0I5iCynhYjy-MwnysyZa!BT)D6KlL<;LcJdi229XwhZ${ONuM;1 z5zHCb1$WB$sK~(fT+VcfM$c#-N4;OhZ4UWVsig;A?gs=z3iQUN%Y^-*MBBNL0#x8$c3+6&eIgUK; zD&nNEDg92ErU}OJjO6iq54GKSB>J_gDi3`PX^`efa-Z)-*WpIbJt*X%0pLEUVlJ!y zZ#&XDxThHP>^xMHF<7x~(1BAiHY?FTY>^!LBO-javHB3`EA}ECZyWTll*Xqx)FU_b zl_3!40Y3!)-9mILoUcqAqIje$g1uc4BzZ4omFPhem$nJ{w@NR%ux!NUh}DVKB90=2 zgQKoq$Vb$ZIjuRVD=)fJ#sif<&yb_>u|n-0ZWdKc+U%)%bN$;feyI`5%%IaKKbyCl z?qEdjfC5zWijuzH4Im3y#EU7IG-UXv?8oEseE$3LcK4adHeoW}?`#XWU|L>cWTGCu zcC6JVtv;&uqr{Ctu>x}4tBj5;>;am`n6DZj2Z4{)Yy4pk1KSfal%LGd<4waOEmoNT#FQ`3G;K- z3%BEjAw}&y{rhMFdF^P%nzL$|zR>VN z&!6gQ^Dnp4%4we-9Gx;A@F+FO63@yTr6%WX8UX7~t|1q7$~FK>rA-kN>TzjE!6U6x zR(ji;&%tdq&L`>_z36Om#PQebPfK4hhAXexh{L4+D)+)!p{mLs#M?gZ za&O308r|YT`rTpfW!8%?=lpi7Z%d5=ERz7mE9SXvf;q`!<`eAYE?e9yuU}qbXd3}^ zE$89C&kvUxzrtr^8|=S81s$I!_!F?7`#$8faGh%V;v_cxyQI7@PdNL&{vU=3SWHok zP;~?7=J+Id;=@O59IE2ujV_Z<$6mf7+S=cGJ1qrrBPgAzeh;lL(ccl4t-BLDO(Rj* zxVrM(s2!koUxu+?{w)4}$KR|v&^F>ugVKHHF*_CaKOR^7WWz3mwxRB8PFyW2T>MA# zs)X5Zi^$cOl~3xqs(wzW{gdu=S2-{tY+t-|-|>rMsG|3FGQu#Oe`+q`_gB|XIa?6B z)|peJrhagY(0A-fC~%Zh2*6A8J!j?NpJeF2ua(Lh9QVDd;cmZ{*{W|#ERB26=U7&)OhJT+5Z%N)UQ3>I-jh>D};K)SPZkQhtq!3|NfF23YWuK=8O}+ ztPwqD#}27;A^_tzb^a{d3uV=iE0>~@t>nJu4;0Ei0L0horasUTN&Y3**^?*M0ed6s zQntepwl9*!!ds*ab!mJKeVLeMy-`py5KBDAoW_mQS*`QC=F{i3)F-|xt%2ZA;HpjP zMYMaqsbxbQLh`axtSjlW$$dyhU)D&tT-Af}fM6m&`paqAlJGQ@3~tV)pHVK+IJ3>P zuZc)Su7LppWUA?#kzk1Kib0tK-nwkMFKf*CyT=AI687N2r*C;-pr}Id4gFC@j<*1; z`gbmrz)xHgf@0vkuXYmpl2QQ6HR3n$NNA0#UdhNg|l@$yR<)slW;Z>}I3}4CC{JPgi^M>`X>qLat zFnlHHYM;jV%s0$piQgbL#E;MxezB;Sn8}bfqMx7;6rM*bmG6V17|+ZOPXC>nUa1Af zFte|r>>z71hrpmnt;*TiGt7I-t<2DV!HF*V?gqQ0^u}W>{O2UnKT1zpaCBC~x5(Hf zji84bC8O{^FGbjsI|IBR1n&9Qe3S2n4kaRI|K%OyY#CJ$5KFW%`OnTSdK)RTR(GVu z`Xk5W&Qn$+H&0J;a-Su@nxDnFF}5{}pUQSqZye!Xfcn457u+*iWURvhlf`_K`ttjb z;|c6oJV3#NPkR7RMTfy1sY>AsF~O&~j{5d}oBFqb-xjhXbKhG`=lpV{zX?$W zSN`;>;}04(w~ql~F-xq3&5S&fh@XV~0DsKM&Qv%+mnH%+_n_od)S6DB;RrH$JP0_`sglLq$=vRomcenp=9(0YU?;1)1c^{SS zkNB(7aq6e1(iMpIleb~CIJ4ocs*A&X>aP>uO304wOEa1|sI=bZ%y<^a`0b^M-_+!r zZXfd^s(2EUp2&72*iu*b0Uxt3oW49pJv;r4iI_yYTNc83smCvlgJv%c)m|;$5l||G z#&EdYN)Afy4K3|`ghM0)Q)C!gRg^IwfH7gw$U8|PvH|Ho=t-u*%ov}ygEfth!L=D7 z*O#T;?|;dP#)Ut0JKMhN`awnt$Nm7x(5NLVu_gmuY!;{?*6C3c|2ip@&_SzX=wePQ z4K0Ln?6|W_Ag>oII@H^W3{URMr&04yTV4^3`KM*d{IsX;)1KIa(G`Yev0~r!mfAI= zNd5s|CSH@oBpOu5;4;91`k-k=4QlN_tc)0Ut~?*-&|a|e;dzbEHh{33Ybst!n?s${ z4$Pp{-&N8z>t*LY+&|O$?K1c1}ow|%dU`EOhP29 z(i4Sdnp#Yw`!$1Xvy?1LN5AJ0GrU~68yRo37aCH+9-X!&zyGq%J3UpE5D-(F#wYPp zwidUbFKnc3Q~3;;YNQ0%E>4{CsHWXfQqeQA4>SZ~HNURd1=)OZb=>cgrkQwqq=(P+cUw5 zxGT>)LIOlfGJg*M2fZdU4!@qy#jBajkbOS6bCunt&FMDfsJn;`#Y!^LncMuOsN_JN zt}r>D5;AyUVQRkg15Hm1iViJ1z^9qJ3SRFk8~)(NT*G+6RrJ%xFH`UwrCxH=vfVN6 zCG@(75cTBhR()M|!gN+eQ-YGZTXUyRdi^}g-Cofdu5Yw zI#pHWt!)%=lLlrNCVU_2*dYo#jH(n!?;YjbgG%w?jLZbgRaJ6tnvj&vVK-X3?s=|V7a=}7JDInmD_*e=mU&_Q~o z1&+x<4LR!dUvMJNVe=Aa27d1CqH5e+XExevGqlQQ4r6PL_ht^&kPBLsyr98c%7vHV zyuv2yeFsgJ2n`fUhBEheF+%m?_+n;lgkL1f|G8G!(b?hTFlCD0Vb1t<;! z>Av8COaIs}m$SPH6X&J2@?NVRLPVCUkiuwTA&mSXt1#>|f*zX#G(%C|rb*@TBOh+J zn8Cn%f-D(nS!WCo#o`sZqV(3LX`YrSy|aoMQIyvc6m^ zd6{l*uYc>UkOG5&=&OjLG1T`CEG(Dfq>%kDTePwkW9i3mhlnACmxtHZPK3lFy*T}+ zXA}c7uciv1;EQwtwnRHE6Z4~cpZ|S=hqap7<@uMXY+k{9={ynrFjeaj=<4VV;@x1p z@7b;!6o+Kuk>$<3@dauYPP%DwlT^;Laf}s^&L1RUw|&8>B7)}C*18m{L%&v86PF2~ z9boRbZF3EJ+%ep|vVbtfA5im62S+0Khtj7!LFeeyW~j(tS%G9xnfaZarqPpk!hOhhV-bUM zSzb^lkPD(l4!ezqtTT#4E>;K6UW?P>^3|u0k!_3iZx{j3fd2S0e}HwYl;F2SdzBp{ zPh0+sl7pP!A@C4{a*3L>`q2? z(m`t1EZehc8|ZbFrD@@De35q!*lP%sKQT$ZH1bT}!Cf-!7Qbe{_wl2wdT|r%cv*t9 zRY{9osgqk21cmmJGHG0KHQ))QoQzlXw13LKXCMrm1-f9&_O&%vv}aAKTkDEcDBHrs zQNY$i6BPaFif3xRg0_`d761kZ?L0TVHtjLO4~TbrLyPyg+5KG$Tne2}Z>rj*E z$taY)YATSi{r%JkIa$mv&s*I-G)x1+=J%Dv+1zGo#@{a9RI&u$$$(0Xv$z|RMSb(q z^GoYcfKr2-ZKlDG8XJ2ys{zrW+II?}%5$_$p!l&}BPbUW4K}aSw6N?CM?XGu+e?)_74JzMl5h#> z67rjPfDPG^ZuaA@FxNL9{;Fn)RH;yER%c_!`C$XgRF~99*1GsErO7=_&s6+X7}hd% z#z?82GIbv!m0s~+hMze3mO|XwmIbe!GwB|xN!@~9x9@~!Ut?-ALa4w)9Ak%vbd&) zdWX7*XK1}~#aG2m}LgbH`AI^A0fPQJu7 z^1%=(Q>F%1^<-+uO@^1R3C$>{BEGBhQD#egYM5tnbJ$M|*dzSvUC!PnJNTsYM>n>fEbLWX4jWU_g&0_E)!Hw8 z@fxZo09R&Lsun|{I;uvPtqT0&*L63ETsG0@`4+wZCY>nN|5Ugbk2Qb2arXNi?Bw{^ zv2yW(gQm@8af&M9apm33C*~-aiC8rlUU_fr zPQML$kYS*qvXXX`7KXPaUZw9o^s5^F=OxX7Hdu+AtaH~3u(Brvh)7#5H+Y@^VM?HE zSEmMRM~4~^1Y{C_cri4fjxz29TK*HG7!K!=!+;kG2=e1`Snshv{2{C5d(=zyCI(TA ztIKvEl?QR>h!ZGV?#c7Z)qr1aZRb1nY;SzEcfBGf>z~}yylZ>cftvxUZb!+mBkEnH z9dbz)VFF@mr3}VU-d9)qGyA9~3rzH_bx7~f;_~U23pb|rp&$KFJk0m5SF!M4`I0-R z923KC&;~i|e-c%y!4?Tn6o@o&q#k;!rI8)0>`*RrRT#+&7()4c)B;yja7Nj$HieJg zZW3TJOR4Tq4&olYi7&=^d!fn^m?r%`{upOHFP^=2J|ET0APp43GmH({-iXo8_ zLy_Zztb-Fd`m`fUxVSE2ma7hysgceQ)y8w_T?wossz{h-1zGeYItz3F6?AQ6sF*5g zhKRMFs}H+us(d0wO&5w431fwK)w-3iQfMDceOhMi@w82n<(m?NhJgkQ*GnF1L)cU1 zuUQR^UgRPX%*yYZ@eDF>+b#BbbTMtNCQ9=Pf5^PR8VlycjY+f62&LbTfVk%8MgSOlCIP{4K8L zwmGA#uKGKZY<=(5&)X~rJEjQQt3tOvg9WYdejAEYxEO1XHRr|)+VP8jtg*SG9`rrq zRk5w`SfeJ^=p+skJnimk{Y*{@UC_Yzhvz2c^U04SfY;HTAIk;1%%_stcV}j)mQl!s zd*x0B2YX_+3#vp~qQGR1sR-g0;t3Gs2w1+&4t#tZ1_s6(0*v`rF%r3dmpno?U1b(V;pQ?HCepb81- zjSZ(TdNda^-`Up3riOLkg|>&%i%T+WLxnm`W*lsL32sN3q(eDOmv8;hX@2UPCFv)@ zU1G4lfu~?p*F|st1pSGlgiN5teFdH)W1``+AK5hv7e@q>tWneu7t|_g!EB2g*V)ki z0>NbadwvxR8A&Q8Mvx04m0p{grjULh3XmMaVK>AipC<|56m3j2lR(}L(^3JN-rnZA zP8k#~G&-5;vKNjwfv=Dzb~xa+Dn1aOzO`J8RvqeG{z*R(eg~lEO5x%(Gf#{M!o8>v zd#SD99dStJ<&n9o>~L`3(fU6xGWBY& ze&_zAVBP(PtdrUouiiWE`GuCny)@FLaf&FNF|3`fhCM3vt1{;3o;;wJO!7IjVvAH< z&hGGq5)i09^G=c06liQ@W)y`&78HR77Yqz0>v{9_;DrBT?HlsGfs)kTHq>(}@!j7( zKyO!?D15Vo|Nd|GL8+;Q210y01s7^|DD(By75jfuY~U0GCkrLa2OQ%sz?boFoyhAjDU z2xXf!1-_jgdVD_xDV~eQ!3*!X>803 zy5mlLQJqsVFPtHx69XO z=+-{2lEyhk?NQNh_uduW%xA{1Z*BO=HiYQNI72sf7|H`*K;5&H6c8Crm3Kv)qOYAM zd|R*aNONRp`$ite)GW6RLHSk@If3K%KXFxJ4^EbDN%%dYu~&UYT4K_&+3xF&C$e&B zNtHuhbq#b}Cp_@vQt)O8or7?07<;n$W&q&!MbDeJJm)sSl38qhp-#Q z_R8!AK}866C7MJ$H$Uy|suD>bpw;%c%#?vWNZ41un|=({bd(V4QY!rgjYbb*d6t7Z zD|NdPjORWKZ2+ zoRm3f0U*v+%5!@SDsdmn-6QWGqxY82E_&P@rB@)Rtv~iYq^6JYNc*WVt_fxG zZbK!H_I38@n+Ec1i1D9`t29m@gOq=5xQnGuFZ{5;-Ou$Q8*a@$p8%1$+Lq{E$T+Af zNl=%|GlqCy#H^fwJwI8Y4ja$xw8eGtn8O5ICW9!0D+SOr&*=$4$ZltpwO#v7*UuId80e+)Fc=k^ z)x^_}f8kKj;<%(_1sS*|-2L^)d7h-;YLSXQ#0IVJ4eNU{3BN4jPH49Sef4g=3~&;JDq#L!eIKe4O;pP%-3^M6kw9Rn4oJ^#at~_1K`feiS3gz7FsF$YoX-dvh&?b-$lTSfTghb zyO940Xj}<|f!)uGcY>GhI`vLBZ|;lb{*!|M^gGr-i5kX~SQ_f9F&8=V-eUE}Z+ya}DpfL+KvVf+ z+cg&UW~wn&O3TUd@*wH0gRs z8*HH{+Z+Q?_d6jqf@(R}!!nz1O(<|V;n$-8(1 zahE|XW_5+3kMc;LQ9uev?HW=U~90{{psIm9!%mncGj(A@6fhmH%h3;MdoBDG+XnK(GXC=#wRJlC}?I^{e(90_-r4l

Dn+UmXymPW z^{=y;F*S2-{H_9u{Kxp6rJ=2L7nf4K_0_4#e(={^*j?kcaZ!a}kH6P2OxdCcYvb!9 zSKS~{d(hlVfyUWqxX??odc=JJqr8L5aZqv-i1Fa#n%tjKVCngdz4Yvsc4GAzYFDJr zZ>Cpt4tCe^ey({u8x&IqV(usJKb|>uh~5-zRdi>)H7?}dA+4u9U6UK6L=qwYrvICI zY!YNyk^VH}cDVV}7t?>>zJoSkm6*6m8}k+^U&>UUSsZ;y=?-c2Pu+ycFU3;H7DO(c z!Nr&H%Y8FFdWEH(7DpRlryWu0$<@CUjZzRhD~ftlnl!W~$B+i}4-=7wS#pf}k?6Kq z6vdYiKA0?Y=zy;7y$%7=t4P>eZtu1*V#~F_E2)oxS1AsJqLYaNztWS$(oC=e7cO)! z|A)3%16a`St)MP`T9X?aG5ay3OJ5_whOtqcJ2pM@GN?WhzX$~6d(~5?b_+L-Y0ICi z3AZ05Mh#b=fVUBLW^SxV+myVwlySw1 zD6S2I_$J0!tf^EB>ya{>4?+PGr9a#G^}{3uf;^GyLrCU#@D$K&l#E}d&1fei|IvHe zBqXK0{TO>o6EDVSPHYOK#Exi_z3`z}&g6UF^4r4bkbrQ*O5PsrA614HWvsKa!dQj_ zna^_ykrl||3htxr=O>@r zxw_-XfK1God-pDk4pgGVpL+91-pqo#2YLDUnf`DxuPz^QXLBF-MI=mM88oG$*tDd5nH>?f z`HFH;siG>$p9%nyr7}+tM7)b zO2MBacoJN=&zRysrtw6njJox4Z^Z!^ToD4}#<}D`yPp~{w^tu=QRm+Bm!x+)OC8f<>YKa9LYp& zcufJH5csj$;p5d$kU*S*A9!##)oj~j7s>JfZ&!zkSM)4&>dVXe!4c{z<-7F#wS}buID=!lpxqyK@qcj@Aqk>pdrnBZDQzIBD_nS|Z;2j)iw>VzeDBVke-Sq0UF?WSbwtx}QNFp(=b40J z^Q^YYS5M7+FU+X&ca%}$SKne3gZlMeDdUw5wbZdYB6V*9+=uX5id9fb@~v}U*e#?D zU>f10pV@(T@OE!|N;SshaW*nuGb2X)t^{UlpF0h>$8@GdM=~jhqv7>swTY?Yepo*f z+y!Di&i~c&vynkN(qlIcX{!t6U7VyYBzGQDAGk>yB2N>q^3%on0EK)&dOQ&w(AfTt zj=a;}x}ER;DiFXfK6;)f-|*Af7m~ShLXyA#<>xi({E?^ytcM--OixdZ)IzQIJJ5gm z;$1`ynwb=uq-;%apFj9AT`VWihYyy+z{3kIk{|x2$fj?^i(v8CDAXoe1Ebn~X@G=M zY&Qa#I(}TvCd>PnERE2<3{$gYn5Eqhk7y$f%`ci72c-34_G1J^VWgL>0eN12GcP%E zhIl0oIp>{A^dWHJe}n{+NB8X{VH_Ny)?pH>NF(dK(DDua1cGKoW(W)9+xSlFFCoz< zcbjFuSPMnKp1~`(aJurK9-Qv_(d%dQm>GD)4w$Qu4h)!l`9WFDr|#8_x!SEeQp9GG zMBe#4==_J$=)s=o^Vu}v`U4AZ=ZV{!eK4kojw#2JVR!RX?z9FSA^gFOqKcRJ8W@s4 zk8iM^#PDZOqDcxcdUbky-gCRwD*vYX-tY9*{cKDZvazhjfn++1!qME8c5c(Bt*Hx5 zgYeRBVMG+a3YXo+GA0=w^gn2TU9IZ-Yk`UP<^KLQc*560kuLgjr*w&vsqoYD%cmG^ zVO_0g;I$SVHvytRkT1DSg6>37ApyLAyS(@!0en^$%NWS}!DGAEFYjIf+0gsuc3}u5 z%H`kwdI+xjU9OeLty@IP3vQb08^xIU!dfM5@XR5d+e(=tJTJ~*%C~+)q2<3)cn7E9 zn&y$RDSwgom}5O0E$(h;^onuR%j&W!yps_hHx!-CXxi@flkb4RLzv?1Dm9hZLl%%u ze?yql^&7Yc8Eq3p?_ih@vCcH;{GRUj9+nCAJj1c`GD(xq)-Z?UF`G?5`leuA0_19! zmWD{QG(33*(*50wt4exQ!3FidTju03f%~OaS+h%>Sc8@lV8iq7cDq?osO|oG&o46J zxjE13EbM>tdXW>*?r2GF=)-!@JW2y|+A8@Ep9JzMTdG*~{NP?ewsVYzI1hd%GT;{8 zpAcQ|GgrXew~O3_T(AWwe#w56s)dRjX9g^dejyV6iZH@%quc0U%5HMJ0%>rV0vRGr zr_yyaBx&OKjyc|U?+#_f5$$&Ll+u9@o(OU{f@aXK|5Q@H_}t!Zm=i4?v7oE#78my} zlZ3OF;Ls9A5HRSv-p#`&bp01oZOXb|HF)cHwIJmjAm8(@)`zIyEi`s;7XNR1Wb*wg z^XB>&yhxzEWb?A0o~PFD?+k;sq@MDOTT1Z)feL|~ zXmefR(R2=BHSEaPNL3P}bK9rvlgwr-*bi2q+4}z&Da3k8Vw`J5yFZ-ZNhZTs^sy|p zvgKAr@-+JKLdK7O7FrdVHi(Ta@Sc8#Nm|`NXZ6rZMB|gwL!i)-bjjO5j@>Hil}vt{P?Wf_$82>OP8 zzP&Q|v0?jq>C|20VU&@)Hm}xYP5zhnZVu9Faa{@4admJKvYLXo+t@z-QzB`v0hG6H zE;VeHQDy=MRg~NC&j=I>VW*~sUJve&gF8=PiUN!D2S)V8&T-2)o^k=s&Nj~8s-0~P zEr|x?k5`<2NYtW9e9aT+rgIpOq`D+g?al>2f|Sakx)J8KZ8EeNy{YL`v5(7MG`x;; zzKZ(XqUele>S|Q#yQj2)UJ}CJ(TyS%EdayS<#A2V?a69`HEg6j`h&+GZY!PO5plMJ ztFWzR%3z!2x{hcsx?MJ#ps$YLCR#3OI1|0m<3taA@C{$OG|utnMK0tu=UeKTpsKV{ z@z@hvHM(=DRv~BUM8{&f-R5*Tdlo6l2!sxX7#|pCxnJQwG>O!cCT6X*xdN3;m4Sv# zkQ`-}ZPn=@tN0@VhL;G@u!hZ+B;m7CG}_x=R%rHMu1n`A&EFe!V@R++ZrKx-a))6^Ox&@lX<>(j2at5_G&%^gGRnFjGk^}csFYA&4JOhj4-Uxr)aV#kRZyNd}|Aj{tQw?lsh%FKZKhyNHhrqRe zzPrDPtuP$VATDs1`*Oi zBcUoGKA(DFV*t4d>UU6W;Jxixv8g<}4{717G(`q?dsimI4CByocpmC z96-jIuAwWFsX;k^%v&_jab8SeQA)rH@E~e)k*;?b#o63OP`D~RicgNQ z^(T52mcmqb=zoZ61Iw6}4cL4cTE$*Gudk;a1)OF@3O=jCAi3n*)xbdeg}4tzM+%u&2r~1Eo)>T{^t1Rr_ewA1 zS7MZ6H9bl@5BC3XtLI5434xrE6z^IRGq-ML^?u zYf#gBI?3}%BlS1lueA*>%D;Aoxn;e68y8l$Hx!VZ;&3l<@?3;4=G(CBC(aDD$_ z`05K@C{b=Jlk~W7urdPs6D`$7t}WE@O5nrH3jahTl+mZWQn2hWh2l#?@B@!3Zog}Q zN4=*-&P5gQ4|)(I!zGA`yB;6P@NhlWFoJt~M<$DXGC6GQmQ{**uSs25`Q9uE*Jp&Z z{hvi`I=CSwlPF3PNisSNrFB5yNj}`8UoP`VIfZ2Ye)}(1);VHi-CtQZ=I`x~538%= z1^&%zntbvNM4p=;#QRe{WDd-6B%m!4vJ8zgLPcTXkA*8x;Tc~&ZZm=7PNN8#Lh;VR zNrOU=DLzRpCSXKR=);wo>oomZl>#FSmEE45hnY=@MlF~OgW=|g zQhUCPyVM-WcFRM9F|31Z)rSecDNZi3{81fzxq?pHW5YkP8vQ)kD`BWQ^B52ul{?48BT$ATfz*5GtK;Mn@ zB+Ma7lNfj4oTg%_UrwSq$s;HE2E)W1WJyLUYz!%a{Am5W`4@1+`EvEuU4_pz*obgv zu1hhiO=O_H9j2Ywmyf%aOVd)|=qQtoKE~Lx-o2Uv`MfaOAnWe^zjW$aD)*NH zKeUsB!?9PzO~I5dsCwv6nC%Ju{WSJwcx;Vub>t)UXFKsmTc_9>AuV9p7jk|;**Vb( z=^gX;@UwU5nTC~;tKO+~1X}9o;^+$Aen9_}!0|~`FgGi(V@#GB(?(c)0(*|Ypv=1B zi3kOFG0*!#mIYK~?i%-Tx?{K|<86BbNUa8tPh4Wp)|NvVkiM3DE@am6p-Zi@&1M%S zS4nkqzc#C31)N+9q$lL=!(MTKxn*NrNQ zmMD=?G8yUwWJu*e$|pELJ@dWJ7qLe!H{-0u-anLeCP4U7< zYdb3^D6=7W<}5$(CHwGNvWSMb^^+VOD|&(LLQdde0hG#nPdiXY!(XBiR+{R28N-4b zA`MV%O0gw`?+zatB-U00EACj*9!TdM~CPu$#$W0KqsBjAMt8N%vvG?ki1w08`$Ro&U36 zq*CAzk0fVFmbD@m?U%0X{CmUrU<#o1q;N~xokJG&mh8A#GlE=vOG0k_pv2`-_8Rt*DwGk^|CLhSUy+Wc>m*TZR1{ z3Ni)J&L%f&=pBkiF=2?3b!erj@a_EAEk&Ob|71Ef1CwQp6>8XIqiNVy?mJNfE0G(9FUfrUai^Db<+eV zlIGfB`(5h`f}lHv_An!PYvBuFM0jCxw0kLCP+{JM7+irQbsu`;Z}|LK;ukis6oBtj zs)TIGtPnLYs^(Fz<8RZpU;(lWdU%M$WdB|fh^asu&_J6B4--_QJHMI6*Y~%|psE0Io=?ER)r(4A zIayd#&)EW~uIa8*WF{a!wS^DMSYt!W?Oh++X6yR_c>}{7a?p$|Xbt;-+6}+)(IhG2 z#DZ~BD%N2`8}?+=Q*s&p=Yw;8K3H$W&xO-n^6P_9iU5w>m}w33i`W7la(+rm5;%y3 zUN?eW_3laTLemQKWNBfrRg}vDQ=jPB!5~5|Vd38-%8zb>Eg!kQK3HNiw=OXvB1_19 zcw0febpx|bWGD(1XNcE^z)0tk&!*iKa#3SmL0m5hns#PnreURJ{uKh+_Vttru7fUS>2|PHO3RV61jTL2ArY~WRve5QHgzbYNmbcq@$*-^ilk+cJfx8zh6FyLd&xk zL~--iPPbz!HJbThH#FQKm>C&iHyS>a81=OB!-dyf^e-M*STQAMd<$Oe`^N#CyI zq4DSOH+)|&aa%qpd6dOP#(M`-`nzE|s3TaGdP-V1D9Y?_shIE1#@Zf@zgzy;ub3dc z|IR01|K4!I?Wnfru!P%@NZTdnd?Zv4n+kHVt&;c{FLosKm;?9eEPad$JN=D%ibxXY zu@CaFBvWn4m)mNhnNWo359L*Ed)(NAqH+E^lv2%q`_Y_3_Z;J%ZOiyxXhvudWf0?_ zy6n1p`5wgcMS;E3f4++HSX5_%L`w=`ky3usa3H!gZlTJv&08xaZtPWo3nGx^LKJ|7 zS0_v2;zX19V_HE|xY)qB#-f92h2(Se>5^yhjsx#KExod!G>`d-{xyjk1G$4)-M{g0 ztm+M5`SqP|yf-&*H8=uxyA_Jf`$!sOj)=PiCb=$YsuOIaT^;dx$nltQ&{&D%Wkffs z?A%Fa#2KW%H>jX8yYzUp!w^Z`>*$p!-1o=jXonKjE{qPWkS=`3!>#n~SuFQryc^9v zA?_-@yEDGqPbC?`o8g>n8}{o0np7Ux1-1M0B@+ru){Xfp-*1(a3u_^RS0X@H<6LT73zjlp+wG zNoF#u#YMS7{53(J?rk!I646h&vWoNuXgc(1sYVzpC&I+A+9WASlW_I8M&?vHKb8qv z_55sG48_z(9&GnQ>KkDME3PM^*f~|kdKXV%My`s&kZ1(VwKevmR!_=79Rmf`O%N>h zD?H=eH8L#>yS&u3P8u_qj~0nv;J8u-fRH;$eE)nU_3^jKc+|({dpqH?U~rOVnwLC) z8H@w^6edq+gdN&H>70e^mfFGj7BE$Sk3--i32bv z?EC6B!K;u=b77Cv!219}_>MHVxrZ8w;8naC_iXNT;a48X)ezVh(cp++hTO^Pxv9FK zpk@V{t-pHQ%z_@uUls{ux6@V~(YS8wupvY%nKPaVart5OEK28t|7+|m!{Tb1KVjT0 zxDM{_5Zs;M?mEFOxVyUqNstKy3GVJN1P=sv5AH5Ixu5&_?|Z!;c9)A!XS%CTRaaN_ zsrps-b1f5C(^T(AWs0cIXOK-^=noyJXE%FSMV*9?sIYkt(u_E^wDbO6q`EzWh`YtJ z$!GhB3$H(syjuH#_kEP)t?H!c$00v5phlB}Av>H^eV%c33Pb1gSLSPdLl8w5Yl`)S zec$h$auBmy+Ci9q=z8q?x5QGt;pftqoWkH%R<*zt%oOV@ihcLaaE*FA=pdQ79hmg5 zC)QM9eUU{SbEu$+6kq_4A;n$1iK^k6IKB}-*_Wmp_lXz5lx{q7H*)=8mY*j3&xN~K zh4vy?F+bR&^FMy$ugR}aseYF&*8+<@@I^h@{2<3m_95-5=xm#t%3UF9iI?0scZtFf z#r;~cwIC|FO@cc5xtpDyZ$@6_Af6e0V_a^Z+s-O5b$l=wv~+GIr*`#VpE$3bhjlj50{1fNeHF_w84+!EaH}Sk7_Y;#ip2nL`Qc)# zQQA;aUK6!FED?TI&$5c6K!9)f5--aci!7!li}UkJ82_PmUgsuuTiqQ0Um#B!37p9n zquz%PS(p2VgtQ${33bn?cs|e@WAA)~eGTlWt;Z)8dLC2Dx7XsW{W;q%F?t$C8&0g?>|T`@B+fAg9&9p*gC;tX3;Ek~kd z6;NJAsiA;C$)9SHxW6Pi?W{w-6}xS|6-TPS?SK#m0~uW0{B{C0z9=-oC8UP`dj3t< zFU1WB*{8;CuInNQ-j!*rJ+s*$wZpWot(-J`dQY7F5%zq6ei!ZVaQE$2NmN6}B*qX; zs^6W6`!LC~MWc|IkrSulQXc}pyajfo}a?H zXoWgb42|!o4|lF&FEXwlc=d2i{bhTFZXU~Roapr0pK{dO1Y~gV+Hvm)uMzkv{wU!S zIX!hw-JBIzRVEgz9erxWlv4eC$FQv6)khS@M|Bq+l+re;)^@VO+K|lf=1YNu8K^da z$DUPNMN^XW2)(=Wy4ZN?wPX24RTu0&?OuF+bg&ytL&UOm9kghJ#bK8u!SMTg%6m-9 z&=wS&fj^pf#>No0g)nH+XWRW_qiCGcs3@hbzGp<()fPDoa*Vo4-WZPH2A9n&S6I8B&xKKiR#v>d zDYIppc6nScoU5;gTqY@nu#uiRvR9%mWd5rl2` zZrRz;YwnfJNe_(V772yxtpd9Vlj^VEx|Lv!BBAai%bOSkjq|#_OBzo5AOLR(JdEJ_ zg4VNZfHz1!K+yF^OIHZ-29sN@ysa%3H>t!f+(FNjd_0auLoCl^Wbs2fm*2~Yx;$|68;eE{keR78whP9N1^?xFPa{?N}P zz3(^Z(urACu|0wcEHErxLv8;WwcrU8Sto*r3>kDHzMvc+{TH4^FoH1P1Hc#QR6+0f)>}1dbk-8T0*6p~6@U^)h1ly`gaqG#0p)bQXjPgL{f~+_!OMx^j zru9=-*Ke`Sa3O!9oM*5GoW1s`@<>sSB1+oL&y3s_r4}w`N1tK%M0o$^!`729@nQlw zp35Huo-1!*r;BAA5hGfqD~%X{$-azAH}p(c?*>z8T7&&+^4s|y%8^G52NH`ZfmKg2 ziT=BzVIh))*kXfoTELOIL~i*Q4oHmE){KCi0DyHX5W|I}rSUdD05p59q3Ef_G{~H= zdTy9`;T2w!B;eaPTf@=z7oc`mC|{N0TqNXtZa9zT#lFJHwg<`r3B(}gJ&e=RT@BE(r*jpg zH8Xu?|4C7k`Bn36!mlRR! zntj7r1nL`6+9czWMpF0!n&9T?uxE*8NvUlSBTd9>vg!c^`Rg z%-2Z$#g%OB5~Q6D8Z+!$8|`!PX_`+%xj3H_0d|2>i=-=kcXwtSKrfw5dLO(R=Va}c zrZ^ZC*=(QFaPSxoWL1FzqATC*SCkPaMm?zO;YF$u<<3QaVg>~k_0rpc4ii{2NX788FVidY0`DE z@ka|sI-T%ZnG0Uzf3!tZ8iJc*>sLZ|y4o&~sNy!6`iiTAygFYK0alU^Scg9}j1xKF zblZ3d>{Ig3QD?t=y0zxf@Wk&~Qjw5d)9(9bALkH0=2DO5%{`OAQAdC?P{u86*9x}_ z4#;bp^lN{o?NISk_~JSo#kZN=QmSkf>o7!PN0*dcGn(L`uuaBI_gbXUwM3`tp^Gt( zJ-BN$fuUHZqii61tI~^oK2rVy6pswAu4x{61FwFBifrhXo6^-%DLvQy`=HlcJ9Ok_ zjhKb}lOy&d#W_Z7Mro4SZ1fn8dnSUwWmoFK51hF-UZ?k|{4Olp8Fb@wmD~Ttr)?cIh1;gEMh|v)-%|~qw=k4WbgX&PnIZ=H*!f9V6}p&@>q_&aA&)BQHa zHGG6w|cC9S=8T5xMgt3gS3C5=1x zM&#kN8^b_oIl%QPkiw*VK{g3_YnBkY17;=I=`efe6VEY#-Mc4L(Jf4|?{y$DnZ zZ!OZ*#H<>U_FD(DUFS^@q}b~7IGHLhmlT$ESq`9a%?T;eoCm=~qIh{#@6DD_HUWZV zy~T#806vonsdTR#q>L;f#87Q7My*i+X8>V+5)*=X)G1>l;(q0M+w}fSG2!$I^_s}_ zAw9Ld)gHZ{qX@a+2X-~6Kn&7aX&&KW-7C{|TAmzdU~%%Ur&|q@K;eiV8bC<**>Y6I z;`c0pwzX<|r$3aZ%wIqWL6Gs^lbcb3AnzoLUzk$23(1qGsOvzTS+~8Dy|oAx@FL9r zgFpz$21PSwI|Zk>zQ?H|GW#~EOY@<#>5eS{GHrIvxFqYS~wngi%A)a1j0gO z3JxTbwpq|i=a_nNvZ7cr)=2maJl3^@kRp^-<*2F8l2c^`dvFu;%y;PwrMKdH;wC^! zBOvr3|HQm>(Gbr55|>SARsP!tLQpxIU?X^$&$^8WJx58CVDIH8 z2?$s&`;QHwel)VhKYi=IkcXskL1Ipp690sNB^>@4yr~}hp6B1S_?NJ`mC$N%%0HoE zG7ip}1R%p_2xJUGfqMB5di>9Sbd^)WDB@qLZlNY89i$?&-&DBho^?jDy7f2Ad2TBp zg+!1Z^au0P{j9?~wtwFdvSHwsAVp*z`{xxQ61MwSHjo)6cF+EYS8Dpt0zy3#^9IFj zR?l5r$$#j0IJ#eyUNuJc!ex`ESdUA5sx?Xdv1D z{o=@^U;oHj5v7G7Ll~3l{cm^G4;y`dyL%Jj3~2wiHT}Wr+9-0r@)FJVj49KDLnJ&v z*0P)U9~ruZijoN%`GsqjD-AVPXJk_(hW%e=$)A_Ir%Bgg?C;b<04?0B;RrGRBf3wt z5`lVVp_6~r^IzGo!ywxH)yKO3=;MD>%E4BIL*$=1LM`Y1E7|{_PW(#{4Ww@QA052)~h+eIz!W*FA=x4R)R*LN6@=E|*~SuV}l8w%ReF}^k9zuE-^?!a~l9CeCI zh%^^dZqwhyo5R!02fzp0)*Gf5|5rRD7ceK$xULc6sJ5b*z}>Ms*}2JN@aZOt9BPlS z8KRk?MTCvLt#RR1V4B^ME*#IN3C@L2!sJtkgvn;zo;@4|N}+e{(943uNkefzdZh9^ zhd{+wfw?>5!Z(&oub=mYx~G#(Oh->;aLK_CX^v zQySnrEOjt84bL!@L?V2O)c>v;L>iNOC5|8 z%nPaPve}pxa6ChZcdc5a+kecH#n|`dL;cTMLr;+k87(S-{Ut2xyrF5vzTw;h5a%}I zr}=D1;fjXsAD0rn3QGuYB>zq4F?S|1AjB}lyKCFGdR(P>y2 z9)8%Xyc18DHH5F?_-A?lwE*ZT7FIVNA7WOZuxpX^RiNo$B&;qf#pgCZiX-m7EGI|* zke*v80W0S1zhCp~p|X8-w|ars&uxFNX*++bl$_B-+A2T_B^)~7DtOLuxk?uNV^At2 zD=)xM=;UONg&fL11dgu>$S(IS=kQM*6Z(VEBo% zx9bV!&2vu|DoWrgsHN#o{BsO>()$u}ClG6?S@N9zc9Li(Tz;G(H zV|#eya71_AQzKG`!H+U#-S>}qcK8O@N)#?#n6$9@=5YYs5j(&U9uQ|AhX-cAtFHPa zmHfL&&@DkP!AO{oLPDso@r1^z(`k>TS+;A6(iiFs8^7hEYA`U|SiDIR#z%6NFNnhQ zklOg@RvbEJFhpMU$&5o!|HbCd_Jy|r z#Zi(uVHr1Wv6A7t#x7WF-$JuG3p0T_kH8p>@=wwkrv?H?t0A^Ruwn&fLTZr_+TaiK zNn9V$B(#^;WQuC3f5O$WUNU1emLVbkjzJccqNh;}N;Fs}iCI;oaais8>6m=)5E8nd z2}4hHbc{Qj^$h>;5(qyg9TLQyT(S&kwPKNeoiAj0=LIbUBUTts@}lmt>Zd+$LtLjC zc8Pfw!E$ki^9R$I%pUUjlEc$9De#ZE`^Q(z(&^Jjk4ODuWdA=V^Z&)V>H+>U6K)9s z>W>W;T4>wo7Md-;5&U3w8Q=}L-or-G`UD{}p;%ZU{a5#}F+oNh`Gs!7^iTw_avR%|FV%^}P7RdC(TbN8dVyr=5IsbLhRW&#+985yOi;b`P8;Tox zwhu1*FXMOvWtSx9Uh|h(JxZk;QPWTy|7|`6-O4c*Z4ks<4n(ZOXogdT_AjMcf!ZJ! zC)gm^u-w3ze_e*A2h~k&tK+qMTgN9s`NG^4VuSy<;V@-C2dN5umHB<8>bmp{a(D8& z>Ff;dE~*=Ih9z&C5JAdtatdQQOel6TlB)y!>y4;&@B4fL05LN<6PG+2fwqC~)ey!I z@TtYN2||Q7KAO?it$4aNk)w6Q51bG8eC+Ao&U>QSK+)oArL!R1S#!;wDOoJdIj`^G z+Hb@ngq$SF#419php7z1CRh9wx=Au{Bv7m9L3~4DpHLRphVnnV5nVm>W5j3F79s|5 zn0ucMcq7Rc* z^bU4?%99iT#fk*kC=BjY!L`X0ZUxE#8BGR%Glby)gGQ0u(@_l8x)!OHeF~#hjioZ; z_JiB_TeuOur+B_K!xT)b?+$E02#N7>(2jDUXAmTl?;FH9)ri0J+5Phjhss8_NeOmkGdRkrQ-i|w(60g=$z;5rc zTS$<%Easow)y)i}b-*tL3H8sSiiMs;KP_sd(3b&8Y035j7d>I}Ed+vRmGC;0cQIHm zDfZFi#U4-ZT8+X>czgN`984$vyA3wlgtyY}gpb+woVW}C-)#8JH;w1b>x!@)o%hMND8OFyoqeu}%q>JAN(8Vzzes&i>vEySO2u9z;OilAFwl)NV_ z{U~SIUSK;+b%8QQ7R8&9n9CRUyv6W1`;Ft~^qOT^sxCQbmudfm0nccHyzKz^j>PiQ z7lIa8i|0?W4IP2cc2gAK7nV`u}_5{(st}{l8e8SsmJpr5%s?XW?|0HaZ9Bco){7NTx1A zbOLq`G(r@IFebD{I41Shk-<{5U0pp^&wlwRn;yy@EUl0VLA7AT?kZ_($8LC0sGnT3 zLy<$G;+VFt{2Os-O|BvBysj)>`Nf z#VWSxH|S^*xd)L|NuU%{J}2!-+QQtEX~%HarTt(Yo5o@tkUg0~yRWbpl<&tKP?=7k zUrnqgZ~NFCY1hbqo7-$I?N021x1nU2Ki5;-BhgdVllmT*rS6^sg$dg~v*OIW;iENl z=j$N`@!1e1qE5RHp%4B7ntO|6=|)w5dB6w%`i-+%YS`W+(&y7zz(!&)47n%rqT>vcy@;&rD4j8dbXhv1M)_yrT_>sA$JaO6nn=A?BkOi?8{Wv4k zeVEbY3Vp8ve)GF9tnbwG4fcl(#j?>+Apu_=_G^yT3Ls|QDm5(zx1TwfIrxI6$!rBF z5AHeb9P9iYxYNdem8X8<5LqhyRv(n2hqetl^Zc3|t!3^l!i6F|MesFA zp0fi>ACOPq+P|UumTZ95t!g6$R;fqzb^jA;#Y4{ zBhcblDN<1LM^BsP|a z0pDQ)nui5PMa>AV4c#=uUo8s)UE7X2SJlK#Fs}`jajy@hy6@f{h|Oca@Z$^3oK5(= zDPM6nNOkwCnXV0e>R2olTTb8Ol)^3aaG+Kb(~o|=x=J>6^5||5@Oo8v1oWF|9pdUcRhHwgPLcyEA5)gbO|Mlp>4Oj3P zY9yd{f7@Tb6qO-=+;*AqbrMh^bkN^kZO%i{`Hs(6JD$%_3?1ZpU^g3UR+&Jw>O?7{K zSB*++*gea6{Cr-=?9O4ox~FLTam{x&7)r ziZ|X1njZQ|oHzt!_Fm)nnIgmu|aYPqySWiIvJka(E z+W`?p*lXRkO{hAHVmwU|<486P*+QhZQ=)}Ro$EbJGsU|HJ>KOuoOpT=dMpee_Er8! za{-YibO?KyvmIHV3A<4GJ5hvJ_}rKXBmLdl;?6RDGHly@VK<=#dl zCXU+RmU(6R=ri;}J>}&aDK21bd8ofQrnM6L2KTFX2U6vdk9FkQcctnv2eb)iYGIQh7TibM0gXc_EFR#C| z&w`yAqTan$=7mb{?!~r~tNnUQz9}VYlf8Ql^0Ni7=xXm@eCWP|p?n(3gn~v!Ns$ki zl(W&3){?hlu?j`{$zaJChGrQPde9QjYbl7r)&hmY5$zEAP6B__mF5R_v;;IPv@%#8 zCY&ZYAmD8EVcRe1E^pcE=;p>vRNMHE>blp{?TLHfzE_~VH~P(i%N15toD1Ir%7xa- z(igF$PB9dHBS*+x3bP}J><3;8V$Uq?-&U-FVV4^BzBrHQ60t>OlXtLG%+Da?`TLJX z*4|i-bYI2IDG{mouN{ikPo%BTg}(U5EyPIQZG@b+zaTp@siKK3=Xr5Gt|~|M(lR*g zCjlva{k%IteRp!81nrdZqVEE7119?s^1Pv#8%+ISz{FvKBiLkYbLRdeXfIGrR$+4{e0$b%Bp1bAL63ejOU)$Fz{`40`5ER7IBp`a*UB^f=|?`lz)ks82tdl6^J#}TC+ceK-ItB!+1FFmEBKD_bu7Ljy#1Elnc*qG$wX0>+)6)9fT2q z&j^fG%oWe^fgWFEYx3ja0y1lwD*IvOgO~E+SwN8$(_4UMIO8a|q}sX!8PvU`zWLl3 z+$|8JZuAVX`XM$j4e`97=svIbMx1U0XNT!R{Kf@F!h z@LmF&NX`{_ZqF!*#S~6eJ~%F%{x-JqQzh=zE8+l&QS1Bgk8Em_(=Snk;Kt@+C$$ zlKR%8DsFD0u-3@G#T1vTE?_Q9V*%i@2_R3%7v)(~xnB#|g!Kq=h*$tW3s_F;HD_!d zrSQ+`nugJOlD%U$k}i?LwkXX`TGw(lqb@}z^{BIk@`}c{S;AX-p{^NC z!6$>s`RQk!J%-TD{Bmt%n$cZmB`LU%zGhQ8E23CH=1U9Z~i1nFq@t z8v0Yw_~iwVdOO2B&Pg}=#42$f?40EXYp}3SP2iTtD4)V@Bojz?fUxp;MV$d8l!WM- z5wGZzcHpZPutV!Tmepehn7sxv(k*GPhb%IQ5xB<0B*9wy-G&0-CXz~7e-CDo&}K9*%g0m zrSx=nQmFUb_+8*umXmd;Y1%>Vr$Q!T2_hqgP`{k;B2P?l?R*+`V(tDe!sS`-hnPL< z>HAS;a5cCF?EBSr0Zc6_m-8<731Q{N7W#CLGA5(N<#92xL|k^yf&S<_V79q+tnQMMt~wHtt9m=Ec-9Qb9$40DfS2LM8oSOqQt$S z=dOg0=!r6O8Q;Ql0l0h|IIYdfC5{GYT4DwsDhk<}YQLmYsq77-_|r)7E!LYsQ&qyF ztG?Dlei;_1$H803Tw+pp;DJXngCfL&Fl`?|po!=^&y-5eNxyk)ka{!7uC;nVF z^=i2hYSgAdR5k0p*Qomam$CuuYW*M>Co02~MnC3DzRxbjFIhzjNgzgRWUeJy<#IF=q^ZB8>a5( z1aFS58gPJ-q3lSwp|tqZ;*pHdi)m`7P;)=st*T%XBCR;#DWwl|?0WNA!NQ%nK{rSRi)X|P+G18@LTV&42?LFlE5Eu;qxH)4R?O@ zpgz^&rsGI(Vq53b=DEsK^1>!ItIr>=IcIvT%{WC#n57`~5M@$t(stvg!;|m4rqRca z;8Gn|1AiBQb3$h#f8NhNQBU^E&XR=BATlWkZ$S}8-8Rhv@AMVk$nd&MqFGEw8FfU5 zRhtZwD$I7xKTKQ*4m*|HGNA=YE9N6p5{7i$$uLx%nR_^YrEL#%1VO^mBMHU+Vz_ra zB(2xStIyI_oq{n|yALVn{zDOhac&i-LM}(eB|a#D@kW|Mik!U9VlzLsIC*T!Rp7=f za&G)_G7{MR3Mbrmbq-aTix|9#NA0Dzy~ePNr7A7El1e$JNRl%y$Y|z!6R!+$m7Fc4 zhN;CvRXGj{V53bKD3hM|~sQP!-*zf5c{??Dc_A1mPnz5_NC3FPo-DmY? z`n4CLM{+VP>{m7*D_0IY;cB_9Zx5@|wx?)P&lM*YLOdsQLw`F=v%|E2mP{XRmT8jF zm{-BVYC|}lP~sU!`lczmPiVDC*dsO#ZesJRGmXZ3+o&hhz81;fr3 z2ClB2y@-a(@Vs(4xv5O*VOxUlv#3PSe?0#rRKQ~%z;0GDhJ{ily2A6L>6g=C_{Y7 z+$cMRc3XT~8!mA(eFu@MDxIY?{}~h+BMddd`qgy_&DL` zWtvEw>Q)`CnB_kdak>!wfw5utaNY+cNV6gnMEWw;jo0urlAHj3nKz@J%Ito%vu3-* z&K^!b?nfPNxuyItOZ9EapOA=V>*qPer4=5l@hx`hOg^;;LLl1Gvq>DwPkn_Zsqv$h zOIr5XG9;W7QvU8gA1uz6T}LyTgc6zE5*EDYukt_|U-{e78v`ayzjNv1u9xnv=G%B- zJ1{>^jkEv4D^4Slmb~-*rq)RSzu28|d8zhPU{JRX60UV*R&VJ)EA6UKt0&lR?;Ezd zUR{r|XxvJ(M>|)snpR*+G)*TeO$2Q!e#_JS`K1)9%UJwXEp_`FKmulshHZi>)<+0g zSD~Y_$M#Ixa>GylL4b&sw(-UA7wKf(MOjwNQwb)9UF6!qA?@}1*P`ywsbl3B-l5u!2cTT7}^$2=V% z2or&s7g9m-HOXkJ$=U`s1wE)bcV?P_TJ{ZU!la#orfD2*osBXX}@nV^)IV~my8a(KUY&| zct1tI&-xP44lPPBQfI)2m9WK06+yUD_@slrAE!$dJzvwT)UHijWKQ0lHWWTT=x%#R z3wv^A{H=>og`z!qaH|3s zQlr#yy@kT$qJ(jxcg}BdHp%!Dw>4ndP(lmPj=w+4*~M;~@ePDx%Hl?7 z?$Qo0xRAu7LU3rq5R!W1c&A6?O>55ME3KBMBMxyYy?iY@p z>;~ch{+lN{^pWz_Ah3u2icJ_P9uFaaQ zvJ`81u5~*7Z9|4(Gn0!*dU23%TaBOkN+8dHuh0d0x!6Pgn6UkL>>X~i%U!`9sVOG; z#}kDk8O&2JF}f!gPYUKlk;Sd`PVCGrwqW;ClbXS5Y9$KcSP}w$68+c&S_guvU!=B7 zrvallM)t{gOHDt1yR9GZ;mc_Xf33oFJRG}MJp45~?hz`9rf%=kuUl3{y@9(DuI<9_ z*=Xh!;TQYtJD+T-Xoq{+F~m!1H@yoT2L;8R$LFAt->5V zoZ=?=GG4Gq`4c zMZShs&dnA?m*FEH-HTN{cb{Yji8F4GuA86{w^w-(-vzqlBQ zRVBY6Ua~q-yK)EQM|XDP>Voq>1lpX_XuXcrgkRec+zccJh{pKa+mfp%C@{R8W?c3| zoq7jSyc?xxpV1z1V!nGRLa$!PFCr0@R<+PJkLtb>;po{R%>xswd zX;(Ta8nu$N~y~o zq;O(6F597(MIUwy)~XP;r>St=wRXOLe1U%pbk?H#WwNf5LK^hMRb}ysr)ruahDbi9 zeo|e*Jw|aMWSR3^dWAHV=27j*m%74BZ4KEI9_3U0q#x;`Kf>MDJs&w7I=5W)&1XN{9%8`qmYHCzhK$G8*3iu^+FH;oD zYZ{qhPf=M34Ng~(DB@HUa`RRvuySodik_-;|!o-cFs`f5CoWf_{(z~5y zy25hTr=qMbKQX{DDfleGIxZ0u_#^Z~arJ%E&V9gl3fLP}2a03U7tWn~M$O!>&*!$b z;8ArYzOSTP4T}L@nH^y?C$H>YaTWP!N}^|_{*2F@3U~>>2^v)k^}^S8d(l}L-}(cL zryGnT55A@pdOlz)Z&u?E?JYo8y1Ry#m@;L`@SHZtpxc8++>RtAjrm#RD7gxL4W+x* z`sN+4Bru6%Wthb-$0;Do0Z3v2-WA#5*!SO+6vj9+FEyv;0qc;EEw3N}#-6~O(!L(J zZ_1q{Uf7c$y^(5P7cv7EW#CBebf1?PlUD=r>I|&9pVe~TH3c?PL%V;DMOh9ZNlwKUOPyP~6ZC0`C5u zO?r<7^KF*KS8?cFJT=j%Ljq@+Iiuyr8>n(JyQ&~;jkbK0b;B_HkU#0o>93+UaquU& z2tZY$Ht3vrq(A%&vtw+ueDM0Q-O$g-`WK!>4?!FwxouHu0k^(xB6R|{REzwW`bPli zktRRt!A%;f#RlSpkM3`5;wbZ6Md$hC1qpTPnMo3O#PKZrS6gN@UMDxFp2W7(Kj;Z4 z7cJNq2u5VRfBK-EHC=_=!acJ305O zES*&9Mw*jUc>+t(JCzep-?R^?ug%e*IobqW?>Es)kMYO}I!wB=6bcB#FYB4IQW1 zw*9drDv9T`q{GjEdJ+1W`EbFCg(ConZ8JC|nw0+Hs|VFY$$Zudu5>mGrPE+(b)l`p z5Z3M8Uz2H-G-_xYxMCRk^;)US|AQq^)mOLJTAql1`&odh;;cA5OZJh=0O=Tg8?CP2 zeNWSbe;IZ;#|pfl;4T!l7R(p>7DJLGfJRH&&HE>brAj8Z+t=_BAa}K)xp@Rgmrl== zZy;o(agWJaCXMJ?ua2>h=chQjMVGF+SRF}fG>*p|_bi~y;g*GhLhMqIme3-njWiB* zqj!+;pn5>I$(<5mDtb1Jn3EJ*v9r6EM=fkYtYjSD0PsY1sqj?a$|$-LD@}WkaQPd> zb~Tgb3MX7E%D*s3Ulpd`<;#-rPCeStORt4h2#7-a!~LNig5m`b@%<8JWT&wI)KRgi zHe;897{P&44oq0H!7d=%@Jn8lwXdmK~%}Jf{b~N?5f%2|QSPW)@ud zQZ8;k!84JVR_hHDAm=Zdk##4}eZ=_1vdE9ib?am$@{BNu&4`$i>cdn=wKo#Z+S2`T z`t08E8=+NWsd3oO_@B)yW^mRdCiUng+0JA!B9ZQ31n1P)DrwUgm?l87+w_eySV*qoW3V=QWvS+# z2G?AQMj21`+^HTv46VeN^hJl|)PxH)@%e`$WUaV5Pb@d+lPIHjzXC2gcB(;3&fkMD zs&egjBMfNfyc#Z9QS&P(rx7FIK45Ck5JmH$;`IwTsMggy(gYXh&c^Bn;&tXormX_; zPrligrE_2~x(-nVFrv_xM*ac&nw=W5x0zN|w9b#iUn zqiN`vQV^Ru5{&+)fictOwpdjx*1&wIOd=WY1v;H1?fpIls#Z#EU*F+!Q_aSufM0^l z?vbVz0(d$yTRJ$&*yy*IR3ZD0tG=<(cj=>-I#n#{>8n#Y-ZUurIl<}0HwPxK0DVS# zV@nJPlp8A^PrgF*aVih?+*&>>PI%*WEe~?O0^VDA8rTX)eSZOB&=b{S+q;DPb#s{Vc zrP+p6jpoHXp6MgA0)fd4&rCE_vVCM8SSIpqjvAf;CJvsIYS~xVeDLFvzk>GyE#vqJ z118mGUQt{L-`7&Us3m=8t=-c-!Zo)CGNa-7`id0(@xH`w;l{Q zjJlEJ;*B~YtHY6$$3+eBVzqzI!(X;Bz>PDRFsl3W}r0=D$^pCb?f#{`o4M~=j=Q6)|FG{@x;Q-;KR7y(;iwz*kGY%2C(6W#OQ6yS~Ks>y@l6?c- zErgsMk#r$;<(#62NLOVOA%#}<3>9`^Ku%69OHL?!J|uL*vpAPr zh73<@7)r6gGfSWzjNahdt_hjmPYxj1Q8|2#Xeqvp3=7BufP-vIFY5=rKel93oKh4^43pZZ)V!=uM&S|xStQGR|4@NPCeGnLB z&LBlO|B9O+(M^Fx1Yv;|(+PqyjPFsP%wIW?h4n0XvO(kW$>f9+|MZ54xne45$9~wi zu3RG;8bXI1d!=hRb$FY|&Xdx7^)@q3tw|Nmll;&gG9bVV5hd;5fU85Bc>s#`hJp++ zzI+)EqFEoT9b|Tc-qV$`rdMuXtV)UiCa3D)BMLvo`>TNHBJQi{Q*xb2IXd>z{x;_H zEU2?1TUM{vVoH#$gQ%K!&iEr=*{mkPVG-8bjaBh47c&;ocnB{%iN-yV6i97FTKWvS z^c(JcklyZu;wH_Rm78lwhvitX;F)2hZ-h?+SPjqR6o0{&`VT;$I5U6FE;xcyE%L`q57t(tSCDmMQ!b z5I)MSnNcL{pHM^^fAK^tJg8;h5l8+QQouaJzV?2fC1LfDAchcPY{Ef6uN9wA*qikc zQXMC=AOZ9^zvQ!!$AzfC_Qh-#M_wPpaQ_7*9w12$PP))`xlNFT3bh&wve-XFneVzR wm^8Q0PcV-GKGl-dU^Q0%Dj6ezu_Sh%a3`73I+L7kWrPck~9nbUrwO4;Q#;t literal 0 HcmV?d00001 diff --git a/shared/sentry/external/breakpad/docs/breakpad.svg b/shared/sentry/external/breakpad/docs/breakpad.svg new file mode 100644 index 000000000..e91e72cb2 --- /dev/null +++ b/shared/sentry/external/breakpad/docs/breakpad.svg @@ -0,0 +1,1023 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + Breakpad Client + Debugging Information + Application Code + + Build System + User's System + Crash Collector + + + + + + + + + + + + + + + + + + + + + Minidump + + + + Minidump + + + + + + + + + + + Human-readableStack Trace + + strip debuginfo + + Breakpad symboldumper + + distribute program to users + + + copy symbol file + + Crash! Breakpad clientwrites minidump... + + ...and submits itto crash collector + Breakpadminidumpprocessor + + + + + + + diff --git a/shared/sentry/external/breakpad/docs/client_design.md b/shared/sentry/external/breakpad/docs/client_design.md new file mode 100644 index 000000000..9f85655d3 --- /dev/null +++ b/shared/sentry/external/breakpad/docs/client_design.md @@ -0,0 +1,223 @@ +# Breakpad Client Libraries + +## Objective + +The Breakpad client libraries are responsible for monitoring an application for +crashes (exceptions), handling them when they occur by generating a dump, and +providing a means to upload dumps to a crash reporting server. These tasks are +divided between the “handler†(short for “exception handlerâ€) library linked in +to an application being monitored for crashes, and the “sender†library, +intended to be linked in to a separate external program. + +## Background + +As one of the chief tasks of the client handler is to generate a dump, an +understanding of [dump files](processor_design.md) will aid in understanding the +handler. + +## Overview + +Breakpad provides client libraries for each of its target platforms. Currently, +these exist for Windows on x86 and Mac OS X on both x86 and PowerPC. A Linux +implementation has been written and is currently under review. + +Because the mechanisms for catching exceptions and the methods for obtaining the +information that a dump contains vary between operating systems, each target +operating system requires a completely different handler implementation. Where +multiple CPUs are supported for a single operating system, the handler +implementation will likely also require separate code for each processor type to +extract CPU-specific information. One of the goals of the Breakpad handler is to +provide a prepackaged cross-platform system that masks many of these +system-level differences and quirks from the application developer. Although the +underlying implementations differ, the handler library for each system follows +the same set of principles and exposes a similar interface. + +Code that wishes to take advantage of Breakpad should be linked against the +handler library, and should, at an appropriate time, install a Breakpad handler. +For applications, it is generally desirable to install the handler as early in +the start-up process as possible. Developers of library code using Breakpad to +monitor itself may wish to install a Breakpad handler when the library is +loaded, or may only want to install a handler when calls are made in to the +library. + +The handler can be triggered to generate a dump either by catching an exception +or at the request of the application itself. The latter case may be useful in +debugging assertions or other conditions where developers want to know how a +program got in to a specific non-crash state. After generating a dump, the +handler calls a user-specified callback function. The callback function may +collect additional data about the program’s state, quit the program, launch a +crash reporter application, or perform other tasks. Allowing for this +functionality to be dictated by a callback function preserves flexibility. + +The sender library is also has a separate implementation for each supported +platform, because of the varying interfaces for accessing network resources on +different operating systems. The sender transmits a dump along with other +application-defined information to a crash report server via HTTP. Because dumps +may contain sensitive data, the sender allows for the use of HTTPS. + +The canonical example of the entire client system would be for a monitored +application to link against the handler library, install a Breakpad handler from +its main function, and provide a callback to launch a small crash reporter +program. The crash reporter program would be linked against the sender library, +and would send the crash dump when launched. A separate process is recommended +for this function because of the unreliability inherent in doing any significant +amount of work from a crashed process. + +## Detailed Design + +### Exception Handler Installation + +The mechanisms for installing an exception handler vary between operating +systems. On Windows, it’s a relatively simple matter of making one call to +register a [top-level exception +filter](http://msdn.microsoft.com/library/en-us/debug/base/setunhandledexceptionfilter.asp) +callback function. On most Unix-like systems such as Linux, processes are +informed of exceptions by the delivery of a signal, so an exception handler +takes the form of a signal handler. The native mechanism to catch exceptions on +Mac OS X requires a large amount of code to set up a Mach port, identify it as +the exception port, and assign a thread to listen for an exception on that port. +Just as the preparation of exception handlers differ, the manner in which they +are called differs as well. On Windows and most Unix-like systems, the handler +is called on the thread that caused the exception. On Mac OS X, the thread +listening to the exception port is notified that an exception has occurred. The +different implementations of the Breakpad handler libraries perform these tasks +in the appropriate ways on each platform, while exposing a similar interface on +each. + +A Breakpad handler is embodied in an `ExceptionHandler` object. Because it’s a +C++ object, `ExceptionHandler`s may be created as local variables, allowing them +to be installed and removed as functions are called and return. This provides +one possible way for a developer to monitor only a portion of an application for +crashes. + +### Exception Basics + +Once an application encounters an exception, it is in an indeterminate and +possibly hazardous state. Consequently, any code that runs after an exception +occurs must take extreme care to avoid performing operations that might fail, +hang, or cause additional exceptions. This task is not at all straightforward, +and the Breakpad handler library seeks to do it properly, accounting for all of +the minute details while allowing other application developers, even those with +little systems programming experience, to reap the benefits. All of the Breakpad +handler code that executes after an exception occurs has been written according +to the following guidelines for safety at exception time: + +* Use of the application heap is forbidden. The heap may be corrupt or + otherwise unusable, and allocators may not function. +* Resource allocation must be severely limited. The handler may create a new + file to contain the dump, and it may attempt to launch a process to continue + handling the crash. +* Execution on the thread that caused the exception is significantly limited. + The only code permitted to execute on this thread is the code necessary to + transition handling to a dedicated preallocated handler thread, and the code + to return from the exception handler. +* Handlers shouldn’t handle crashes by attempting to walk stacks themselves, + as stacks may be in inconsistent states. Dump generation should be performed + by interfacing with the operating system’s memory manager and code module + manager. +* Library code, including runtime library code, must be avoided unless it + provably meets the above guidelines. For example, this means that the STL + string class may not be used, because it performs operations that attempt to + allocate and use heap memory. It also means that many C runtime functions + must be avoided, particularly on Windows, because of heap operations that + they may perform. + +A dedicated handler thread is used to preserve the state of the exception thread +when an exception occurs: during dump generation, it is difficult if not +impossible for a thread to accurately capture its own state. Performing all +exception-handling functions on a separate thread is also critical when handling +stack-limit-exceeded exceptions. It would be hazardous to run out of stack space +while attempting to handle an exception. Because of the rule against allocating +resources at exception time, the Breakpad handler library creates its handler +thread when it installs its exception handler. On Mac OS X, this handler thread +is created during the normal setup of the exception handler, and the handler +thread will be signaled directly in the event of an exception. On Windows and +Linux, the handler thread is signaled by a small amount of code that executes on +the exception thread. Because the code that executes on the exception thread in +this case is small and safe, this does not pose a problem. Even when an +exception is caused by exceeding stack size limits, this code is sufficiently +compact to execute entirely within the stack’s guard page without causing an +exception. + +The handler thread may also be triggered directly by a user call, even when no +exception occurs, to allow dumps to be generated at any point deemed +interesting. + +### Filter Callback + +When the handler thread begins handling an exception, it calls an optional +user-defined filter callback function, which is responsible for judging whether +Breakpad’s handler should continue handling the exception or not. This mechanism +is provided for the benefit of library or plug-in code, whose developers may not +be interested in reports of crashes that occur outside of their modules but +within processes hosting their code. If the filter callback indicates that it is +not interested in the exception, the Breakpad handler arranges for it to be +delivered to any previously-installed handler. + +### Dump Generation + +Assuming that the filter callback approves (or does not exist), the handler +writes a dump in a directory specified by the application developer when the +handler was installed, using a previously generated unique identifier to avoid +name collisions. The mechanics of dump generation also vary between platforms, +but in general, the process involves enumerating each thread of execution, and +capturing its state, including processor context and the active portion of its +stack area. The dump also includes a list of the code modules loaded in to the +application, and an indicator of which thread generated the exception or +requested the dump. In order to avoid allocating memory during this process, the +dump is written in place on disk. + +### Post-Dump Behavior + +Upon completion of writing the dump, a second callback function is called. This +callback may be used to launch a separate crash reporting program or to collect +additional data from the application. The callback may also be used to influence +whether Breakpad will treat the exception as handled or unhandled. Even after a +dump is successfully generated, Breakpad can be made to behave as though it +didn’t actually handle an exception. This function may be useful for developers +who want to test their applications with Breakpad enabled but still retain the +ability to use traditional debugging techniques. It also allows a +Breakpad-enabled application to coexist with a platform’s native crash reporting +system, such as Mac OS X’ [CrashReporter](http://developer.apple.com/technotes/tn2004/tn2123.html) +and [Windows Error Reporting](http://msdn.microsoft.com/isv/resources/wer/). + +Typically, when Breakpad handles an exception fully and no debuggers are +involved, the crashed process will terminate. + +Authors of both callback functions that execute within a Breakpad handler are +cautioned that their code will be run at exception time, and that as a result, +they should observe the same programming practices that the Breakpad handler +itself adheres to. Notably, if a callback is to be used to collect additional +data from an application, it should take care to read only “safe†data. This +might involve accessing only static memory locations that are updated +periodically during the course of normal program execution. + +### Sender Library + +The Breakpad sender library provides a single function to send a crash report to +a crash server. It accepts a crash server’s URL, a map of key-value parameters +that will accompany the dump, and the path to a dump file itself. Each of the +key-value parameters and the dump file are sent as distinct parts of a multipart +HTTP POST request to the specified URL using the platform’s native HTTP +facilities. On Linux, [libcurl](http://curl.haxx.se/) is used for this function, +as it is the closest thing to a standard HTTP library available on that +platform. + +## Future Plans + +Although we’ve had great success with in-process dump generation by following +our guidelines for safe code at exception time, we are exploring options for +allowing dumps to be generated in a separate process, to further enhance the +handler library’s robustness. + +On Windows, we intend to offer tools to make it easier for Breakpad’s settings +to be managed by the native group policy management system. + +We also plan to offer tools that many developers would find desirable in the +context of handling crashes, such as a mechanism to determine at launch if the +program last terminated in a crash, and a way to calculate “crashiness†in terms +of crashes over time or the number of application launches between crashes. + +We are also investigating methods to capture crashes that occur early in an +application’s launch sequence, including crashes that occur before a program’s +main function begins executing. diff --git a/shared/sentry/external/breakpad/docs/contributing_to_breakpad.md b/shared/sentry/external/breakpad/docs/contributing_to_breakpad.md new file mode 100644 index 000000000..b8d261e99 --- /dev/null +++ b/shared/sentry/external/breakpad/docs/contributing_to_breakpad.md @@ -0,0 +1,35 @@ +# Introduction + +Thanks for thinking of contributing to Breakpad! Unfortunately there are some +pesky legal issues to get out of the way, but they're quick and painless. + +## Legal + +If you're doing work individually, not as part of any employment, you'll need to +sign the Individual +Contributor License Agreement. This agreement can be completed +electronically. + +If you're contributing to Breakpad as part of your employment with another +organization, you'll need to sign a Corporate +Contributor License Agreement. Once completed this document will need to be +faxed. + +**_IMPORTANT_**: The authors(you!) of the contributions will maintain all +copyrights; the agreements you sign will grant rights to Google to use your +work. + +Thanks, and if you have any questions let me know and I'll loop in the legal guy +here to get you an answer. + +## Technical + +Once you have signed the agreement you can be added to our contributors list and +have write access to code. For full details on getting started see our trunk +`README`. + +## List of people who have signed contributor agreements + +None so far. diff --git a/shared/sentry/external/breakpad/docs/exception_handling.md b/shared/sentry/external/breakpad/docs/exception_handling.md new file mode 100644 index 000000000..e48a52ae2 --- /dev/null +++ b/shared/sentry/external/breakpad/docs/exception_handling.md @@ -0,0 +1,128 @@ +The goal of this document is to give an overview of the exception handling +options in breakpad. + +# Basics + +Exception handling is a mechanism designed to handle the occurrence of +exceptions, special conditions that change the normal flow of program execution. + +`SetUnhandledExceptionFilter` replaces all unhandled exceptions when Breakpad is +enabled. TODO: More on first and second change and vectored v. try/catch. + +There are two main types of exceptions across all platforms: in-process and +out-of-process. + +# In-Process + +In process exception handling is relatively simple since the crashing process +handles crash reporting. It is generally considered unsafe to write a minidump +from a crashed process. For example, key data structures could be corrupted or +the stack on which the exception handler runs could have been overwritten. For +this reason all platforms also support some level of out-of-process exception +handling. + +## Windows + +In-process exception handling Breakpad creates a 'handler head' that waits +infinitely on a semaphore at start up. When this thread is woken it writes the +minidump and signals to the excepting thread that it may continue. A filter will +tell the OS to kill the process if the minidump is written successfully. +Otherwise it continues. + +# Out-of-Process + +Out-of-process exception handling is more complicated than in-process exception +handling because of the need to set up a separate process that can read the +state of the crashing process. + +## Windows + +Breakpad uses two abstractions around the exception handler to make things work: +`CrashGenerationServer` and `CrashGenerationClient`. The constructor for these +takes a named pipe name. + +During server start up a named pipe and registers callbacks for client +connections are created. The named pipe is used for registration and all IO on +the pipe is done asynchronously. `OnPipeConnected` is called when a client +attempts to connect (call `CreateFile` on the pipe). `OnPipeConnected` does the +state machine transition from `Initial` to `Connecting` and on through +`Reading`, `Reading_Done`, `Writing`, `Writing_Done`, `Reading_ACK`, and +`Disconnecting`. + +When registering callbacks, the client passes in two pointers to pointers: 1. A +pointer to the `EXCEPTION_INFO` pointer 1. A pointer to the `MDRawAssertionInfo` +which handles various non-exception failures like assertions + +The essence of registration is adding a "`ClientInfo`" object that contains +handles used for synchronization with the crashing process to an array +maintained by the server. This is how we can keep track of all the clients on +the system that have registered for minidumps. These handles are: * +`server_died(mutex)` * `dump_requested(Event)` * `dump_generated(Event)` + +The server registers asynchronous waits on these events with the `ClientInfo` +object as the callback context. When the `dump_requested` event is set by the +client, the `OnDumpRequested()` callback is called. The server uses the handles +inside `ClientInfo` to communicate with the child process. Once the child sets +the event, it waits for two objects: 1. the `dump_generated` event 1. the +`server_died` mutex + +In the end handles are "duped" into the client process, and the clients use +`SetEvent` to request events, wait on the other event, or the `server_died` +mutex. + +## Linux + +### Current Status + +As of July 2011, Linux had a minidump generator that is not entirely +out-of-process. The minidump was generated from a separate process, but one that +shared an address space, file descriptors, signal handles and much else with the +crashing process. It worked by using the `clone()` system call to duplicate the +crashing process, and then uses `ptrace()` and the `/proc` file system to +retrieve the information required to write the minidump. Since then Breakpad has +updated Linux exception handling to provide more benefits of out-of-process +report generation. + +### Proposed Design + +#### Overview + +Breakpad would use a per-user daemon to write out a minidump that does not have, +interact with or depend on the crashing process. We don't want to start a new +separate process every time a user launches a Breakpad-enabled process. Doing +one daemon per machine is unacceptable for security concerns around one user +being able to initiate a minidump generation for another user's process. + +#### Client/Server Communication + +On Breakpad initialization in a process, the initializer would check if the +daemon is running and, if not, start it. The race condition between the check +and the initialization is not a problem because multiple daemons can check if +the IPC endpoint already exists and if a server is listening. Even if multiple +copies of the daemon try to `bind()` the filesystem to name the socket, all but +one will fail and can terminate. + +This point is relevant for error handling conditions. Linux does not clean the +file system representation of a UNIX domain socket even if both endpoints +terminate, so checking for existence is not strong enough. However checking the +process list or sending a ping on the socket can handle this. + +Breakpad uses UNIX domain sockets since they support full duplex communication +(unlike Windows, named pipes on Linux are half) and the kernal automatically +creates a private channel between the client and server once the client calls +`connect()`. + +#### Minidump Generation + +Breakpad could use the current system with `ptrace()` and `/proc` within the +daemon executable. + +Overall the operations look like: 1. Signal from OS indicating crash 1. Signal +Handler suspends all threads except itself 1. Signal Handler sends +`CRASH_DUMP_REQUEST` message to server and waits for response 1. Server inspects +1. Minidump is asynchronously written to disk by the server 1. Server responds +indicating inspection is done + +## Mac OSX + +Out-of-process exception handling is fully supported on Mac. diff --git a/shared/sentry/external/breakpad/docs/getting_started_with_breakpad.md b/shared/sentry/external/breakpad/docs/getting_started_with_breakpad.md new file mode 100644 index 000000000..b827d401f --- /dev/null +++ b/shared/sentry/external/breakpad/docs/getting_started_with_breakpad.md @@ -0,0 +1,115 @@ +# Introduction + +Breakpad is a library and tool suite that allows you to distribute an +application to users with compiler-provided debugging information removed, +record crashes in compact "minidump" files, send them back to your server, and +produce C and C++ stack traces from these minidumps. Breakpad can also write +minidumps on request for programs that have not crashed. + +Breakpad is currently used by Google Chrome, Firefox, Google Picasa, Camino, +Google Earth, and other projects. + +![Workflow](breakpad.png) + +Breakpad has three main components: + +* The **client** is a library that you include in your application. It can + write minidump files capturing the current threads' state and the identities + of the currently loaded executable and shared libraries. You can configure + the client to write a minidump when a crash occurs, or when explicitly + requested. + +* The **symbol dumper** is a program that reads the debugging information + produced by the compiler and produces a **symbol file**, in [Breakpad's own + format](symbol_files.md). + +* The **processor** is a program that reads a minidump file, finds the + appropriate symbol files for the versions of the executables and shared + libraries the minidump mentions, and produces a human-readable C/C++ stack + trace. + +# The minidump file format + +The minidump file format is similar to core files but was developed by Microsoft +for its crash-uploading facility. A minidump file contains: + +* A list of the executable and shared libraries that were loaded in the + process at the time the dump was created. This list includes both file names + and identifiers for the particular versions of those files that were loaded. + +* A list of threads present in the process. For each thread, the minidump + includes the state of the processor registers, and the contents of the + threads' stack memory. These data are uninterpreted byte streams, as the + Breakpad client generally has no debugging information available to produce + function names or line numbers, or even identify stack frame boundaries. + +* Other information about the system on which the dump was collected: + processor and operating system versions, the reason for the dump, and so on. + +Breakpad uses Windows minidump files on all platforms, instead of the +traditional core files, for several reasons: + +* Core files can be very large, making them impractical to send across a + network to the collector for processing. Minidumps are smaller, as they were + designed to be used this way. + +* The core file format is poorly documented. For example, the Linux Standards + Base does not describe how registers are stored in `PT_NOTE` segments. + +* It is harder to persuade a Windows machine to produce a core dump file than + it is to persuade other machines to write a minidump file. + +* It simplifies the Breakpad processor to support only one file format. + +# Overview/Life of a minidump + +A minidump is generated via calls into the Breakpad library. By default, +initializing Breakpad installs an exception/signal handler that writes a +minidump to disk at exception time. On Windows, this is done via +`SetUnhandledExceptionFilter()`; on OS X, this is done by creating a thread that +waits on the Mach exception port; and on Linux, this is done by installing a +signal handler for various exceptions like `SIGILL, SIGSEGV` etc. + +Once the minidump is generated, each platform has a slightly different way of +uploading the crash dump. On Windows & Linux, a separate library of functions is +provided that can be called into to do the upload. On OS X, a separate process +is spawned that prompts the user for permission, if configured to do so, and +sends the file. + +# Terminology + +**In-process vs. out-of-process exception handling** - it's generally considered +that writing the minidump from within the crashed process is unsafe - key +process data structures could be corrupted, or the stack on which the exception +handler runs could have been overwritten, etc. All 3 platforms support what's +known as "out-of-process" exception handling. + +# Integration overview + +## Breakpad Code Overview + +All the client-side code is found by visiting the Google Project at +https://chromium.googlesource.com/breakpad/breakpad. The following directory structure is +present in the `src` directory: + +* `processor` Contains minidump-processing code that is used on the server + side and isn't of use on the client side +* `client` Contains client minidump-generation libraries for all platforms +* `tools` Contains source code & projects for building various tools on each + platform. + +(Among other directories) + +* [Windows Integration Guide](windows_client_integration.md) +* [Mac Integration Guide](mac_breakpad_starter_guide.md) +* [Linux Integration Guide](linux_starter_guide.md) + +## Build process specifics(symbol generation) + +This applies to all platforms. Inside `src/tools/{platform}/dump_syms` is a tool +that can read debugging information for each platform (e.g. for OS X/Linux, +DWARF and STABS, and for Windows, PDB files) and generate a Breakpad symbol +file. This tool should be run on your binary before it's stripped(in the case of +OS X/Linux) and the symbol files need to be stored somewhere that the minidump +processor can find. There is another tool, `symupload`, that can be used to +upload symbol files if you have written a server that can accept them. diff --git a/shared/sentry/external/breakpad/docs/linux_core_handler.md b/shared/sentry/external/breakpad/docs/linux_core_handler.md new file mode 100644 index 000000000..558940f2b --- /dev/null +++ b/shared/sentry/external/breakpad/docs/linux_core_handler.md @@ -0,0 +1,39 @@ +# How To Use Breakpad As a Coredump Handler on Linux + +This document presents a way to use Breakpad in order to generate +minidumps system wide on Linux. + +Please refer to [Linux starter guide](./linux_starter_guide.md) if +instead you want to integrate breakpad into your application. + +## Motivation + +When working on an embedded system, disk and memory space is often +limited and when a process crashes it must be restarted as soon as +possible. Sometime saving a full coredump takes to much time or +consumes too much space. + +## Breakpad Core Handler + +In such case the program `core_handler` can be use to generate +minidumps instead of coredumps. `core_handler` reads the firsts +sections of the coredump (where the various threads are described) +generated by Linux from the standard input and then directly reads +`/proc//mem` to reconstruct the stacktraces. + +One can test it with: + +``` +# echo "|/usr/libexec/core_handler %P /var/lib/minidump/%e-%i.md" > + /proc/sys/kernel/core_pattern +# echo 1 > /proc/sys/kernel/core_pipe_limit +``` + +Be aware that a real world integration would likely require further +customization and so `core_handler` can be wrapped into a script (for +example to change the permission of the minidump file or to signal the +presence of the minidump to another service). + +Please refer to +[core(5)](https://man7.org/linux/man-pages/man5/core.5.html) for more +details. diff --git a/shared/sentry/external/breakpad/docs/linux_starter_guide.md b/shared/sentry/external/breakpad/docs/linux_starter_guide.md new file mode 100644 index 000000000..41e1162f4 --- /dev/null +++ b/shared/sentry/external/breakpad/docs/linux_starter_guide.md @@ -0,0 +1,110 @@ +# How To Add Breakpad To Your Linux Application + +This document is an overview of using the Breakpad client libraries on Linux. + +## Building the Breakpad libraries + +Breakpad provides an Autotools build system that will build both the Linux +client libraries and the processor libraries. Running `./configure && make` in +the Breakpad source directory will produce +**src/client/linux/libbreakpad\_client.a**, which contains all the code +necessary to produce minidumps from an application. + +## Integrating Breakpad into your Application + +First, configure your build process to link **libbreakpad\_client.a** into your +binary, and set your include paths to include the **src** directory in the +**google-breakpad** source tree. Next, include the exception handler header: + +```cpp +#include "client/linux/handler/exception_handler.h" +``` + +Now you can instantiate an `ExceptionHandler` object. Exception handling is active for the lifetime of the `ExceptionHandler` object, so you should instantiate it as early as possible in your application's startup process, and keep it alive for as close to shutdown as possible. To do anything useful, the `ExceptionHandler` constructor requires a path where it can write minidumps, as well as a callback function to receive information about minidumps that were written: + +```cpp +static bool dumpCallback(const google_breakpad::MinidumpDescriptor& descriptor, +void* context, bool succeeded) { + printf("Dump path: %s\n", descriptor.path()); + return succeeded; +} + +void crash() { volatile int* a = (int*)(NULL); *a = 1; } + +int main(int argc, char* argv[]) { + google_breakpad::MinidumpDescriptor descriptor("/tmp"); + google_breakpad::ExceptionHandler eh(descriptor, NULL, dumpCallback, NULL, true, -1); + crash(); + return 0; +} +``` + +Compiling and running this example should produce a minidump file in /tmp, and +it should print the minidump filename before exiting. You can read more about +the other parameters to the `ExceptionHandler` constructor [in the exception_handler.h source file][1]. + +[1]: https://chromium.googlesource.com/breakpad/breakpad/+/master/src/client/linux/handler/exception_handler.h + +**Note**: You should do as little work as possible in the callback function. +Your application is in an unsafe state. It may not be safe to allocate memory or +call functions from other shared libraries. The safest thing to do is `fork` and +`exec` a new process to do any work you need to do. If you must do some work in +the callback, the Breakpad source contains [some simple reimplementations of libc functions][2], to avoid calling directly into +libc, as well as [a header file for making Linux system calls][3] (in **src/third\_party/lss**) to avoid calling into other shared libraries. + +[2]: https://chromium.googlesource.com/breakpad/breakpad/+/master/src/common/linux/linux_libc_support.h +[3]: https://chromium.googlesource.com/linux-syscall-support/+/master + +## Sending the minidump file + +In a real application, you would want to handle the minidump in some way, likely +by sending it to a server for analysis. The Breakpad source tree contains [some +HTTP upload source][4] that you might find useful, as well as [a minidump upload tool][5]. + +[4]: https://chromium.googlesource.com/breakpad/breakpad/+/master/src/common/linux/http_upload.h +[5]: https://chromium.googlesource.com/breakpad/breakpad/+/master/src/tools/linux/symupload/minidump_upload.cc + +## Producing symbols for your application + +To produce useful stack traces, Breakpad requires you to convert the debugging +symbols in your binaries to [text-format symbol files][6]. First, ensure that you've compiled your binaries with `-g` to +include debugging symbols. Next, compile the `dump_syms` tool by running +`configure && make` in the Breakpad source directory. Next, run `dump_syms` on +your binaries to produce the text-format symbols. For example, if your main +binary was named `test`: + +[6]: https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/symbol_files.md + +``` +$ google-breakpad/src/tools/linux/dump_syms/dump_syms ./test > test.sym +``` + +In order to use these symbols with the `minidump_stackwalk` tool, you will need +to place them in a specific directory structure. The first line of the symbol +file contains the information you need to produce this directory structure, for +example (your output will vary): + +``` +$ head -n1 test.sym MODULE Linux x86_64 6EDC6ACDB282125843FD59DA9C81BD830 test +$ mkdir -p ./symbols/test/6EDC6ACDB282125843FD59DA9C81BD830 +$ mv test.sym ./symbols/test/6EDC6ACDB282125843FD59DA9C81BD830 +``` + +You may also find the [symbolstore.py][7] script in the Mozilla repository useful, as it encapsulates these steps. + +[7]: https://dxr.mozilla.org/mozilla-central/source/toolkit/crashreporter/tools/symbolstore.py + +## Processing the minidump to produce a stack trace + +Breakpad includes a tool called `minidump_stackwalk` which can take a minidump +plus its corresponding text-format symbols and produce a symbolized stacktrace. +It should be in the **google-breakpad/src/processor** directory if you compiled +the Breakpad source using the directions above. Simply pass it the minidump and +the symbol path as commandline parameters: + +``` +$ google-breakpad/src/processor/minidump_stackwalk minidump.dmp ./symbols +``` + +It produces verbose output on stderr, and the stacktrace on stdout, so you may +want to redirect stderr. diff --git a/shared/sentry/external/breakpad/docs/linux_system_calls.md b/shared/sentry/external/breakpad/docs/linux_system_calls.md new file mode 100644 index 000000000..17ada7e03 --- /dev/null +++ b/shared/sentry/external/breakpad/docs/linux_system_calls.md @@ -0,0 +1,47 @@ +# Introduction + +Linux implements its userland-to-kernel transition using a special library +called linux-gate.so that is mapped by the kernel into every process. For more +information, see + +http://www.trilithium.com/johan/2005/08/linux-gate/ + +In a nutshell, the problem is that the system call gate function, +kernel\_vsyscall does not use EBP to point to the frame pointer. + +However, the Breakpad processor supports special frames like this via STACK +lines in the symbol file. If you look in src/client/linux/data you will see +symbol files for linux-gate.so for both Intel & AMD(the implementation of +kernel\_vsyscall changes depending on the CPU manufacturer). When processing +minidumps from Linux 2.6, having these symbol files is necessary for walking the +stack for crashes that happen while a thread is in a system call. + +If you're just interested in processing minidumps, those two symbol files should +be all you need! + +# Details + +The particular details of understanding the linux-gate.so symbol files can be +found by reading about STACK lines inside +src/common/windows/pdb\_source\_line\_writer.cc, and the above link. To +summarize briefly, we just have to inform the processor how to get to the +previous frame when the EIP is inside kernel\_vsyscall, and we do that by +telling the processor how many bytes kernel\_vsyscall has pushed onto the stack +in it's prologue. For example, one of the symbol files looks somewhat like the +following: + +MODULE Linux x86 random\_debug\_id linux-gate.so PUBLIC 400 0 kernel\_vsyscall +STACK WIN 4 100 1 1 0 0 0 0 0 1 + +The PUBLIC line indicates that kernel\_vsyscall is at offset 400 (in bytes) from +the beginning of linux-gate.so. The STACK line indicates the size of the +function(100), how many bytes it pushes(1), and how many bytes it pops(1). The +last 1 indicates that EBP is pushed onto the stack before being used by the +function. + +# Warnings + +These functions might change significantly depending on kernel version. In my +opinion, the actual function stack information is unlikely to change frequently, +but the Linux kernel might change the address of kernel\_vsyscall w.r.t the +beginning of linux-gate.so, which would cause these symbol files to be invalid. diff --git a/shared/sentry/external/breakpad/docs/mac_breakpad_starter_guide.md b/shared/sentry/external/breakpad/docs/mac_breakpad_starter_guide.md new file mode 100644 index 000000000..6e0bdb0ed --- /dev/null +++ b/shared/sentry/external/breakpad/docs/mac_breakpad_starter_guide.md @@ -0,0 +1,184 @@ +# How To Add Breakpad To Your Mac Client Application + +This document is a step-by-step recipe to get your Mac client app to build with +Breakpad. + +## Preparing a binary build of Breakpad for use in your tree + +You can either check in a binary build of the Breakpad framework & tools or +build it as a dependency of your project. The former is recommended, and +detailed here, since building dependencies through other projects is +problematic(matching up configuration names), and the Breakpad code doesn't +change nearly often enough as your application's will. + +## Building the requisite targets + +All directories are relative to the `src` directory of the Breakpad checkout. + +* Build the 'All' target of `client/mac/Breakpad.xcodeproj` in Release mode. +* Execute `cp -R client/mac/build/Release/Breakpad.framework ` +* Inside `tools/mac/dump_syms` directory, build dump\_syms.xcodeproj, and copy + tools/mac/dump\_syms/build/Release/dump\_syms to a safe location where it + can be run during the build process. + +## Adding Breakpad.framework + +Inside your application's framework, add the Breakpad.Framework to your +project's framework settings. When you select it from the file chooser, it will +let you pick a target to add it to; go ahead and check the one that's relevant +to your application. + +## Copy Breakpad into your Application Package + +Copy Breakpad into your Application Package, so it will be around at run time. + +Go to the Targets section of your Xcode Project window. Hit the disclosure +triangle to reveal the build phases of your application. Add a new Copy Files +phase using the Contextual menu (Control Click). On the General panel of the new +'Get Info' of this new phase, set the destination to 'Frameworks' Close the +'Info' panel. Use the Contextual Menu to Rename your new phase 'Copy Frameworks' +Now drag Breakpad again into this Copy Frameworks phase. Drag it from whereever +it appears in the project file tree. + +## Add a New Run Script build phase + +Near the end of the build phases, add a new Run Script build phase. This will be +run before Xcode calls /usr/bin/strip on your project. This is where you'll be +calling dump\_sym to output the symbols for each architecture of your build. In +my case, the relevant lines read: + +``` +#!/bin/sh +$TOOL_DIR= + +"$TOOL_DIR/dump_syms" -a ppc "$PROD" > "$TARGET_NAME ppc.breakpad" + +"$TOOL_DIR/dump_syms" -a i386 "$PROD" > "$TARGET_NAME i386.breakpad" +``` + +## Adjust the Project Settings + +* Turn on Separate Strip, +* Set the Strip Style to Non-Global Symbols. + +## Write Code! + +You'll need to have an object that acts as the delegate for NSApplication. +Inside this object's header, you'll need to add + +1. add an ivar for Breakpad and +2. a declaration for the applicationShouldTerminate:(NSApplication`*` sender) + message. + +``` +#import + +@interface BreakpadTest : NSObject { + . + . + . + BreakpadRef breakpad; + . + . + . +} +. +. +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender; +. +. +@end +``` + +Inside your object's implementation file, + +1. add the following method InitBreakpad +2. modify your awakeFromNib method to look like the one below, +3. modify/add your application's delegate method to look like the one below + +``` +static BreakpadRef InitBreakpad(void) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + BreakpadRef breakpad = 0; + NSDictionary *plist = [[NSBundle mainBundle] infoDictionary]; + if (plist) { + // Note: version 1.0.0.4 of the framework changed the type of the argument + // from CFDictionaryRef to NSDictionary * on the next line: + breakpad = BreakpadCreate(plist); + } + [pool release]; + return breakpad; +} + +- (void)awakeFromNib { + breakpad = InitBreakpad(); +} + +- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender { + BreakpadRelease(breakpad); + return NSTerminateNow; +} +``` + +## Configure Breakpad + +Configure Breakpad for your application. + +1. Take a look inside the Breakpad.framework at the Breakpad.h file for the + keys, default values, and descriptions to be passed to BreakpadCreate(). +2. Add/Edit the Breakpad specific entries in the dictionary passed to + BreakpadCreate() -- typically your application's info plist. + +Example from the Notifier Info.plist: +`BreakpadProductGoogle_Notifier_Mac +BreakpadProductDisplay${PRODUCT_NAME} +` + +## Build Your Application + +Almost done! + +## Verify + +Double-check: + +Your app should have in its package contents: +myApp.app/Contents/Frameworks/Breakpad.framework. + +The symbol files have reasonable contents (you can look at them with a text +editor.) + +Look again at the Copy Frameworks phase of your project. Are you leaking .h +files? Select them and delete them. (If you drag a bunch of files into your +project, Xcode often wants to copy your .h files into the build, revealing +Google secrets. Be vigilant!) + +## Upload the symbol file + +You'll need to configure your build process to store symbols in a location that +is accessible by the minidump processor. There is a tool in tools/mac/symupload +that can be used to send the symbol file via HTTP post. + +1. Test + +Configure breakpad to send reports to a URL by adding to your app's Info.plist: + +``` +BreakpadURL +upload URL +BreakpadReportInterval +30 +``` + +## Final Notes + +Breakpad checks whether it is being run under a debugger, and if so, normally +does nothing. But, you can force Breakpad to function under a debugger by +setting the Unix shell variable BREAKPAD\_IGNORE\_DEBUGGER to a non-zero value. +You can bracket the source code in the above Write The Code step with #if DEBUG +to completely eliminate it from Debug builds. See +//depot/googlemac/GoogleNotifier/main.m for an example. FYI, when your process +forks(), exception handlers are reset to the default for child processes. So +they must reinitialize Breakpad, otherwise exceptions will be handled by Apple's +Crash Reporter. diff --git a/shared/sentry/external/breakpad/docs/mozilla_brown_bag_talk.md b/shared/sentry/external/breakpad/docs/mozilla_brown_bag_talk.md new file mode 100644 index 000000000..82461b3d0 --- /dev/null +++ b/shared/sentry/external/breakpad/docs/mozilla_brown_bag_talk.md @@ -0,0 +1,82 @@ +# Breakpad Crash Reporting for Mozilla + +* January 24, 2007 + * Links updated February 14, 2007 +* Mozilla HQ +* Mark Mentovai +* Brian Ryner + +## What is a crash reporter? + +* Enables developers to analyze crashes that occur in the wild +* Produces stack backtraces that help identify how a program failed +* Offers higher-level data aggregation (topcrashes, MTBF statistics) + +## Motivation + +* Talkback is proprietary and unmaintained +* Smaller open-source projects have few options +* Larger projects need flexibility and scalability + +## Design Options + +* Stackwalking done on client + * Apple CrashReporter + * GNOME BugBuddy +* Client sends memory dump + * Talkback + * Windows Error Reporting + * Breakpad + +## Goals + +* Provide libraries around which systems can be based +* Open-source +* Cross-platform + * Mac OS X x86, PowerPC + * Linux x86 + * Windows x86 +* No requirement to distribute symbols + +## Client Libraries + +* Exception handler installed at application startup + * Spawns a separate thread +* Minidump file written at crash time + * Format used by Windows debuggers +* Separate application invoked to send + * HTTP[S](S.md) POST, can include additional parameters + +## Symbols + +* Cross-platform symbol file format +* Contents + * Function names + * Source file names and line numbers + * Windows: Frame pointer omission data + * Future: parameters and local variables +* Symbol conversion methods + +## Processor + +* Examines minidump file and invokes stackwalker +* Symbol files requested from a SymbolSupplier +* Produces stack trace +* Output may be placed where convenient + +## Intergation + +* Breakpad client present in Gran Paradiso Alpha 1 for Windows + * Disabled by default + * Enable with `MOZ_AIRBAG` +* Proof-of-concept collector + * http://mavra.perilith.com/~luser/airbag-collector/list.pl +* Other platforms coming soon + +## More Information + +* Project home: https://chromium.googlesource.com/breakpad/breakpad +* Mailing lists + * [google-breakpad-dev@googlegroups.com](http://groups.google.com/group/google-breakpad-dev/) + * [google-breakpad-discuss@googlegroups.com](http://groups.google.com/group/google-breakpad-discuss/) +* Ask me (irc.mozilla.org: mento) diff --git a/shared/sentry/external/breakpad/docs/processor_design.md b/shared/sentry/external/breakpad/docs/processor_design.md new file mode 100644 index 000000000..7b9f244d9 --- /dev/null +++ b/shared/sentry/external/breakpad/docs/processor_design.md @@ -0,0 +1,231 @@ +# Breakpad Processor Library + +## Objective + +The Breakpad processor library is an open-source framework to access the the +information contained within crash dumps for multiple platforms, and to use that +information to produce stack traces showing the call chain of each thread in a +process. After processing, this data is made available to users of the library. + +## Background + +The Breakpad processor is intended to sit at the core of a comprehensive +crash-reporting system that does not require debugging information to be +provided to those running applications being monitored. Some existing +crash-reporting systems, such as [GNOME](http://www.gnome.org/)’s Bug-Buddy and +[Apple](http://www.apple.com/)’s +[CrashReporter](http://developer.apple.com/technotes/tn2004/tn2123.html), +require symbolic +information to be present on the end user’s computer; in the case of +CrashReporter, the reports are transmitted only to Apple, not to third-party +developers. Other systems, such as [Microsoft](http://www.microsoft.com/)’s +[Windows Error Reporting](http://msdn.microsoft.com/isv/resources/wer/) and +SupportSoft’s Talkback, transmit only a snapshot of a crashed process’ state, +which can later be combined with symbolic debugging information without the need +for it to be present on end users’ computers. Because symbolic debugging +information consumes a large amount of space and is otherwise not needed during +the normal operation of software, and because some developers are reluctant to +release debugging symbols to their customers, Breakpad follows the latter +approach. + +We know of no currently-maintained crash-reporting systems that meet our +requirements, which are to: * allow for symbols to be separate from the +application, * handle crash reports from multiple platforms, * allow developers +to operate their own crash-reporting platform, and to * be open-source. Windows +Error Reporting only functions for Microsoft products, and requires the +involvement of Microsoft’s servers. Talkback, while cross-platform, has not been +maintained and at this point does not support Mac OS X on x86, which we consider +to be a significant platform. Talkback is also closed-source commercial +software, and has very specific requirements for its server platform. + +We are aware of Windows-only crash-reporting systems that leverage Microsoft’s +debugging interfaces. Such systems, even if extended to support dumps from other +platforms, are tied to using Windows for at least a portion of the processor +platform. + +## Overview + +The Breakpad processor itself is written in standard C++ and will work on a +variety of platforms. The dumps it accepts may also have been created on a +variety of systems. The library is able to combine dumps with symbolic debugging +information to create stack traces that include function signatures. The +processor library includes simple command-line tools to examine dumps and +process them, producing stack traces. It also exposes several layers of APIs +enabling crash-reporting systems to be built around the Breakpad processor. + +## Detailed Design + +### Dump Files + +In the processor, the dump data is of primary significance. Dumps typically +contain: + +* CPU context (register data) as it was at the time the crash occurred, and an + indication of which thread caused the crash. General-purpose registers are + included, as are special-purpose registers such as the instruction pointer + (program counter). +* Information about each thread of execution within a crashed process, + including: + * The memory region used for each thread’s stack. + * CPU context for each thread, which for various reasons is not the same + as the crash context in the case of the crashed thread. +* A list of loaded code segments (or modules), including: + * The name of the file (`.so`, `.exe`, `.dll`, etc.) which provides the + code. + * The boundaries of the memory region in which the code segment is visible + to the process. + * A reference to the debugging information for the code module, when such + information is available. + +Ordinarily, dumps are produced as a result of a crash, but other triggers may be +set to produce dumps at any time a developer deems appropriate. The Breakpad +processor can handle dumps in the minidump format, either generated by an +[Breakpad client “handlerâ€](client_design.md) implementation, or by another +implementation that produces dumps in this format. The +[DbgHelp.dll!MiniDumpWriteDump](http://msdn2.microsoft.com/en-us/library/ms680360.aspx) +function on Windows +produces dumps in this format, and is the basis for the Breakpad handler +implementation on that platform. + +The [minidump format](http://msdn.microsoft.com/en-us/library/ms679293%28VS.85%29.aspx) is +essentially a simple container format, organized as a series of streams. Each +stream contains some type of data relevant to the crash. A typical “normal†+minidump contains streams for the thread list, the module list, the CPU context +at the time of the crash, and various bits of additional system information. +Other types of minidump can be generated, such as a full-memory minidump, which +in addition to stack memory contains snapshots of all of a process’ mapped +memory regions. + +The minidump format was chosen as Breakpad’s dump format because it has an +established track record on Windows, and it can be adapted to meet the needs of +the other platforms that Breakpad supports. Most other operating systems use +“core†files as their native dump formats, but the capabilities of core files +vary across platforms, and because core files are usually presented in a +platform’s native executable format, there are complications involved in +accessing the data contained therein without the benefit of the header files +that define an executable format’s entire structure. Because minidumps are +leaner than a typical executable format, a redefinition of the format in a +cross-platform header file, `minidump_format.h`, was a straightforward task. +Similarly, the capabilities of the minidump format are understood, and because +it provides an extensible container, any of Breakpad’s needs that could not be +met directly by the standard minidump format could likely be met by extending it +as needed. Finally, using this format means that the dump file is compatible +with native debugging tools at least on Windows. A possible future avenue for +exploration is the conversion of minidumps to core files, to enable this same +benefit on other platforms. + +We have already provided an extension to the minidump format that allows it to +carry dumps generated on systems with PowerPC processors. The format already +allows for variable CPUs, so our work in this area was limited to defining a +context structure sufficient to represent the execution state of a PowerPC. We +have also defined an extension that allows minidumps to indicate which thread of +execution requested a dump be produced for non-crash dumps. + +Often, the information contained within a dump alone is sufficient to produce a +full stack backtrace for each thread. Certain optimizations that compilers +employ in producing code frustrate this process. Specifically, the “frame +pointer omission†optimization of x86 compilers can make it impossible to +produce useful stack traces given only a stack snapshot and CPU context. In +these cases, however, compiler-emitted debugging information can aid in +producing useful stack traces. The Breakpad processor is able to take advantage +of this debugging information as supplied by Microsoft’s C/C++ compiler, the +only compiler to apply such optimizations by default. As a result, the Breakpad +processor can produce useful stack traces even from code with frame pointer +omission optimizations as produced by this compiler. + +### Symbol Files + +The [symbol files](symbol_files.md) that the Breakpad processor accepts allow +for frame pointer omission data, but this is only one of their capabilities. +Each symbol file also includes information about the functions, source files, +and source code line numbers for a single module of code. A module is an +individually-loadble chunk of code: these can be executables containing a main +program (`exe` files on Windows) or shared libraries (`.so` files on Linux, +`.dylib` files, frameworks, and bundles on Mac OS X, and `.dll` files on +Windows). Dumps contain information about which of these modules were loaded at +the time the dump was produced, and given this information, the Breakpad +processor attempts to locate debugging symbols for the module through a +user-supplied function embodied in a “symbol supplier.†Breakpad includes a +sample symbol supplier, called `SimpleSymbolSupplier`, that is used by its +command-line tools; this supplier locates symbol files by pathname. +`SimpleSymbolSupplier` is also available to other users of the Breakpad +processor library. This allows for the use of a simple reference implementation, +but preserves flexibility for users who may have more demanding symbol file +storage needs. + +Breakpad’s symbol file format is text-based, and was defined to be fairly +human-readable and to encompass the needs of multiple platforms. The Breakpad +processor itself does not operate directly with native symbol formats +([DWARF](http://dwarf.freestandards.org/) and +[STABS](http://sourceware.org/gdb/current/onlinedocs/stabs.html) +on most Unix-like systems, +[.pdb files](http://msdn2.microsoft.com/en-us/library/yd4f8bd1(VS.80).aspx) +on Windows), +because of the complications in accessing potentially complex symbol formats +with slight variations between platforms, stored within different types of +binary formats. In the case of `.pdb` files, the debugging format is not even +documented. Instead, Breakpad’s symbol files are produced on each platform, +using specific debugging APIs where available, to convert native symbols to +Breakpad’s cross-platform format. + +### Processing + +Most commonly, a developer will enable an application to use Breakpad by +building it with a platform-specific [client “handlerâ€](client_design.md) +library. After building the application, the developer will create symbol files +for Breakpad’s use using the included `dump_syms` or `symupload` tools, or +another suitable tool, and place the symbol files where the processor’s symbol +supplier will be able to locate them. + +When a dump file is given to the processor’s `MinidumpProcessor` class, it will +read it using its included minidump reader, contained in the `Minidump` family +of classes. It will collect information about the operating system and CPU that +produced the dump, and determine whether the dump was produced as a result of a +crash or at the direct request of the application itself. It then loops over all +of the threads in a process, attempting to walk the stack associated with each +thread. This process is achieved by the processor’s `Stackwalker` components, of +which there are a slightly different implementations for each CPU type that the +processor is able to handle dumps from. Beginning with a thread’s context, and +possibly using debugging data, the stackwalker produces a list of stack frames, +containing each instruction executed in the chain. These instructions are +matched up with the modules that contributed them to a process, and the +`SymbolSupplier` is invoked to locate a symbol file. The symbol file is given to +a `SourceLineResolver`, which matches the instruction up with a specific +function name, source file, and line number, resulting in a representation of a +stack frame that can easily be used to identify which code was executing. + +The results of processing are made available in a `ProcessState` object, which +contains a vector of threads, each containing a vector of stack frames. + +For small-scale use of the Breakpad processor, and for testing and debugging, +the `minidump_stackwalk` tool is provided. It invokes the processor and displays +the full results of processing, optionally allowing symbols to be provided to +the processor by a pathname-based symbol supplier, `SimpleSymbolSupplier`. + +For lower-level testing and debugging, the processor library also includes a +`minidump_dump` tool, which walks through an entire minidump file and displays +its contents in somewhat readable form. + +### Platform Support + +The Breakpad processor library is able to process dumps produced on Mac OS X +systems running on x86, x86-64, and PowerPC processors, on Windows and Linux +systems running on x86 or x86-64 processors, and on Android systems running ARM +or x86 processors. The processor library itself is written in standard C++, and +should function properly in most Unix-like environments. It has been tested on +Linux and Mac OS X. + +## Future Plans + +There are currently no firm plans or timetables to implement any of these +features, although they are possible avenues for future exploration. + +The symbol file format can be extended to carry information about the locations +of parameters and local variables as stored in stack frames and registers, and +the processor can use this information to provide enhanced stack traces showing +function arguments and variable values. + +On Mac OS X and Linux, we can provide tools to convert files from the minidump +format into the native core format. This will enable developers to open dump +files in a native debugger, just as they are presently able to do with minidumps +on Windows. diff --git a/shared/sentry/external/breakpad/docs/stack_walking.md b/shared/sentry/external/breakpad/docs/stack_walking.md new file mode 100644 index 000000000..941115946 --- /dev/null +++ b/shared/sentry/external/breakpad/docs/stack_walking.md @@ -0,0 +1,144 @@ +# Introduction + +This page aims to provide a detailed description of how Breakpad produces stack +traces from the information contained within a minidump file. + +# Details + +## Starting the Process + +Typically the stack walking process is initiated by instantiating the +[MinidumpProcessor](../src/processor/minidump_processor.cc) +class and calling the [MinidumpProcessor::Process](../src/processor/minidump_processor.cc#61) +method, providing it a minidump file to process. To produce a useful stack +trace, the MinidumpProcessor requires two other objects which are passed in its +constructor: a [SymbolSupplier](../src/google_breakpad/processor/symbol_supplier.h) +and a [SourceLineResolverInterface](../src/google_breakpad/processor/source_line_resolver_interface.h). +The SymbolSupplier object is responsible for locating and providing SymbolFiles +that match modules from the minidump. The SourceLineResolverInterface is +responsible for loading the symbol files and using the information contained +within to provide function and source information for stack frames, as well as +information on how to unwind from a stack frame to its caller. More detail will +be provided on these interactions later. + +A number of data streams are extracted from the minidump to begin stack walking: +the list of threads from the process +([MinidumpThreadList](../src/google_breakpad/processor/minidump.h#335)), +the list of modules loaded in the process +([MinidumpModuleList](../src/google_breakpad/processor/minidump.h#501)), +and information about the exception that caused the process to crash +([MinidumpException](../src/google_breakpad/processor/minidump.h#615)). + +## Enumerating Threads + +For each thread in the thread list +([MinidumpThread](../src/google_breakpad/processor/minidump.h#299)), +the thread memory containing the stack for the thread +([MinidumpMemoryRegion](../src/google_breakpad/processor/minidump.h#236)) +and the CPU context representing the CPU state of the thread at the time the +dump was written ([MinidumpContext](../src/google_breakpad/processor/minidump.h#171)) +are extracted from the minidump. If the thread being processed is the thread +that produced the exception then a CPU context is obtained from the +MinidumpException object instead, which represents the CPU state of the thread +at the point of the exception. A stack walker is then instantiated by calling +the [Stackwalker::StackwalkerForCPU](../src/google_breakpad/processor/stackwalker.h#77) +method and passing it the CPU context, the thread memory, the module list, as +well as the SymbolSupplier and SourceLineResolverInterface. This method selects +the specific !Stackwalker subclass based on the CPU architecture of the provided +CPU context and returns an instance of that subclass. + +## Walking a thread's stack + +Once a !Stackwalker instance has been obtained, the processor calls the +[Stackwalker::Walk](../src/google_breakpad/processor/source_line_resolver_interface.h) +method to obtain a list of frames representing the stack of this thread. The +!Stackwalker starts by calling the GetContextFrame method which returns a +StackFrame representing the top of the stack, with CPU state provided by the +initial CPU context. From there, the stack walker repeats the following steps +for each frame in turn: + +### Finding the Module + +The address of the instruction pointer of the current frame is used to determine +which module contains the current frame by calling the module list's +[GetModuleForAddress](../src/google_breakpad/processor/code_modules.h#56) method. + +### Locating Symbols + +If a module is located, the SymbolSupplier is asked to locate symbols +corresponding to the module by calling its +[GetCStringSymbolData](../src/google_breakpad/processor/symbol_supplier.h#87) +method. Typically this is implemented by using the module's debug filename (the +PDB filename for Windows dumps) and debug identifier (a GUID plus one extra +digit) as a lookup key. The [SimpleSymbolSupplier](../src/processor/simple_symbol_supplier.cc) +class simply uses these as parts of a file path to locate a flat file on disk. + +### Loading Symbols + +If a symbol file is located, the SourceLineResolverInterface is then asked to +load the symbol file by calling its +[LoadModuleUsingMemoryBuffer](../src/google_breakpad/processor/source_line_resolver_interface.h#71) +method. The [BasicSourceLineResolver](../src/processor/basic_source_line_resolver.cc) +implementation parses the text-format [symbol file](symbol_files.md) into +in-memory data structures to make lookups by address of function names, source +line information, and unwind information easy. + +### Getting source line information + +If a symbol file has been successfully loaded, the SourceLineResolverInterface's +[FillSourceLineInfo](../src/google_breakpad/processor/source_line_resolver_interface.h#89) +method is called to provide a function name and source line information for the +current frame. This is done by subtracting the base address of the module +containing the current frame from the instruction pointer of the current frame +to obtain a relative virtual address (RVA), which is a code offset relative to +the start of the module. This RVA is then used as a lookup into a table of +functions ([FUNC lines](SymbolFiles#FUNC_records.md) from the symbol file), each +of which has an associated address range (function start address, function +size). If a function is found whose address range contains the RVA, then its +name is used. The RVA is then used as a lookup into a table of source lines +([line records](SymbolFiles#Line_records.md) from the symbol file), each of +which also has an associated address range. If a match is found it will provide +the file name and source line associated with the current frame. If no match was +found in the function table, another table of publicly exported symbols may be +consulted ([PUBLIC lines](SymbolFiles#PUBLIC_records.md) from the symbol file). +Public symbols contain only a start address, so the lookup simply looks for the +nearest symbol that is less than the provided RVA. + +### Finding the caller frame + +To find the next frame in the stack, the !Stackwalker calls its +[GetCallerFrame](../src/google_breakpad/processor/stackwalker.h#186) +method, passing in the current frame. Each !Stackwalker subclass implements +GetCallerFrame differently, but there are common patterns. + +Typically the first step is to query the SourceLineResolverInterface for the +presence of detailed unwind information. This is done using its +[FindWindowsFrameInfo](../src/google_breakpad/processor/source_line_resolver_interface.h#96) +and [FindCFIFrameInfo](../src/google_breakpad/processor/source_line_resolver_interface.h#102) +methods. These methods look for Windows unwind info extracted from a PDB file +([STACK WIN](SymbolFiles#STACK_WIN_records.md) lines from the symbol file), or +DWARF CFI extracted from a binary ([STACK CFI](SymbolFiles#STACK_CFI_records.md) +lines from the symbol file) respectively. The information covers address ranges, +so the RVA of the current frame is used for lookup as with function and source +line information. + +If unwind info is found it provides a set of rules to recover the register state +of the caller frame given the current register state as well as the thread's +stack memory. The rules are evaluated to produce the caller frame. + +If unwind info is not found then the !Stackwalker may resort to other methods. +Typically on architectures which specify a frame pointer unwinding by +dereferencing the frame pointer is tried next. If that is successful it is used +to produce the caller frame. + +If no caller frame was found by any other method most !Stackwalker +implementations resort to stack scanning by looking at each word on the stack +down to a fixed depth (implemented in the +[Stackwalker::ScanForReturnAddress](../src/google_breakpad/processor/stackwalker.h#131) +method) and using a heuristic to attempt to find a reasonable return address +(implemented in the +[Stackwalker::InstructionAddressSeemsValid](../src/google_breakpad/processor/stackwalker.h#111) method). + +If no caller frame is found or the caller frame seems invalid, stack walking +stops. If a caller frame was found then these steps repeat using the new frame +as the current frame. diff --git a/shared/sentry/external/breakpad/docs/sym_upload_v2_protocol.md b/shared/sentry/external/breakpad/docs/sym_upload_v2_protocol.md new file mode 100644 index 000000000..d56a4c836 --- /dev/null +++ b/shared/sentry/external/breakpad/docs/sym_upload_v2_protocol.md @@ -0,0 +1,214 @@ +# Introduction + +The `sym_upload` tool is able to operate in `sym-upload-v2` protocol mode, in +addition to the legacy protocol (which will be referred to as `sym-upload-v1` +for the rest of this document). For now `sym-upload-v2` is HTTP/REST-based but +it could be extended to operate over gRPC instead, in the future. + +# Table of Contents +* [Why](#why) +* [How](#how) + * [Uploading](#uploading) + * [Uploading with `sym_upload`](#uploading-with-sym_upload) + * [Uploading with curl](#uploading-with-curl) + * [Serving the `sym-upload-v2` protocol](#serving-the-sym-upload-v2-protocol) + * [Authenticate using `key`](#authenticate-using-key) + * [Symbol `checkStatus`](#symbol-checkstatus) + * [Upload `create`](#upload-create) + * [Uploading the symbol file](#uploading-the-symbol-file) + * [Upload complete](#upload-complete) + + +# Why + +Using `sym_upload` in `sym-upload-v2` protocol mode has the following features +beyond `sym-upload-v1`: + * Authentication via `key` (arbitrary secret). + * Symbol identifier (product of `debug_file` and `debug_id`, as recorded in +output from `dump_syms`) can be checked against existing symbol information on +server. If it's present, then the upload is skipped entirely. + +# How + +## Uploading + +### Uploading with `sym_upload` + +Uploading in `sym-upload-v2` protocol mode is easy. Invoke `sym_upload` like +``` +$ ./sym_upload -p sym-upload-v2 [-k ] +``` + +Where `symbol-file` is a symbol file created by `dump_syms`, `API-URL` is the +URL of your `sym-upload-v2` API service (see next section for details), and +`API-key` is a secret known to your uploader and server. + +For more options see `sym_upload --help`. + +### Uploading with curl + +As an example, if: + * Your API's URL was "https://sym-upload-api". + * Your service has assigned you `key` "myfancysecret123". + * You wanted to upload the symbol file at "path/to/file_name", with +`debug_file` being "file_name" and `debug_id` being +"123123123123123123123123123". Normally you would read these values from +"path/to/file_name", which in turn was generated by `dump_syms`. + +Then you might run: +``` +$ curl https://sym-upload-api/symbols/file_name/123123123123123123123123123:checkStatus?key=myfancysecret123 +``` + +And, upon seeing that this `debug_file`/`debug_id` combo is missing from symbol +storage then you could run: +``` +$ curl --request POST https://sym-upload-api/uploads:create?key=myfancysecret123 +``` + +Which returns `upload_url` "https://upload-server/42?creds=shhhhh" and +`upload_key` "42". Next you upload the file directly like: +``` +$ curl -T path/to/file_name "https://upload-server/42?creds=shhhhh" +``` + +Once the HTTP PUT is complete, run: +``` +$ curl --header "Content-Type: application/json" \ + --request POST \ + --data '{symbol_id:{"debugFile":"file_name",'\ + '"debugId":"123123123123123123123123123"}}' \ + https://sym-upload-api/uploads/42:complete?key=myfancysecret123 +``` + +### Serving the `sym-upload-v2` Protocol + +The protocol is currently defined only in HTTP/REST. There are three necessary +REST operations to implement in your service: +* `/symbols//:checkStatus?key=` +* `/uploads:create?key=` +* `/uploads/:complete?key=` + +#### Authenticate Using `key` + +The query string arg `key` contains some secret that both the uploader and +server understand. It is up to the service implementer to decide on what +constitutes a valid `key`, how the uploader acquires one, and how to handle +requests made with invalid ones. + +#### Symbol `checkStatus` + +``` +/symbols//:checkStatus?key= +``` + +This operation expects an empty (or no) JSON payload in the request. + +This operation should return the status of the symbol file uniquely identified +by the given `debug_file` and `debug_id`. JSON schema: +``` +{ + "type": object", + "properties": { + "status": { + "type": "string", + "enum": ["STATUS_UNSPECIFIED", "MISING", "FOUND"], + "required": true + } + } +} +``` + +Where `MISSING` denotes that the symbol file does not exist on the server and +`FOUND` denotes that the symbol file exists on the server. + +#### Upload `create` + +``` +/uploads:create?key= +``` + +This operation expects an empty (or no) JSON payload in the request. + +This operation should return a URL that uploader can HTTP PUT their symbol file +to, along with an "upload key" that can be used to notify the service once the +file upload is completed. JSON schema: +``` +{ + "type": "object", + "properties": { + "upload_url": { + "type: "string", + "required": true + }, + "upload_key": { + "type": "string", + "required": true + } + } +} +``` + +Since this REST API operation can be authenticated via the `key` query string +arg, the service can return a URL that encodes permission delegation to the +upload endpoint resource and thereby constrain the ability to upload to those +with valid `key`s. + +#### Uploading the Symbol File + +Note that the actual symbol upload step is _not_ part of the REST API. The +upload URL obtained in the above operation is meant to be used as the endpoint +for a normal HTTP PUT request for the contents of the symbol file. Once that +HTTP PUT request is completed use the upload `complete` operation. + +#### Upload `complete` + +``` +/uploads/:complete?key= +``` + +This operation expects a JSON payload in the HTTP request body with the +following schema: +``` +{ + "type": "object", + "properties": { + "symbol_id": { + "type": "object", + "properties": { + "debug_file": { + "type": "string", + "required": true + }, + "debug_id": { + "type": "string", + "required": true + } + } + } + } +} +``` + +This operation should cause the symbol storage back-end (however implemented) +to consume the symbol file identified by `upload_key`. It is up to the service +implementation to decide how uploads are assigned `upload_key`s and how to +retrieve a completed upload by its `upload_key`. If the symbol file cannot be +found, is malformed, or the operation cannot be completed for any other reason +then an HTTP error will be returned. JSON schema of non-error responses: +``` +{ + "type": "object", + "properties": { + "result": { + "type": string, + "enum": ["RESULT_UNSPECIFIED", "OK", "DUPLICATE_DATA"], + "required": true + } + } +} +``` + +Where `OK` denotes that the symbol storage was updated with the new symbol file +and `DUPLICATE_DATA` denotes that the symbol file data was identical to data +already in symbol storage and therefore nothing changed. diff --git a/shared/sentry/external/breakpad/docs/symbol_files.md b/shared/sentry/external/breakpad/docs/symbol_files.md new file mode 100644 index 000000000..e05b455bb --- /dev/null +++ b/shared/sentry/external/breakpad/docs/symbol_files.md @@ -0,0 +1,581 @@ +# Introduction + +Given a minidump file, the Breakpad processor produces stack traces that include +function names and source locations. However, minidump files contain only the +byte-by-byte contents of threads' registers and stacks, without function names +or machine-code-to-source mapping data. The processor consults Breakpad symbol +files for the information it needs to produce human-readable stack traces from +the binary-only minidump file. + +The platform-specific symbol dumping tools parse the debugging information the +compiler provides (whether as DWARF or STABS sections in an ELF file or as +stand-alone PDB files), and write that information back out in the Breakpad +symbol file format. This format is much simpler and less detailed than compiler +debugging information, and values legibility over compactness. + +# Overview + +Breakpad symbol files are ASCII text files, with lines delimited as appropriate +for the host platform. Each line is a _record_, divided into fields by single +spaces; in some cases, the last field of the record can contain spaces. The +first field is a string indicating what sort of record the line represents +(except for line records; these are very common, making them the default saves +space). Some fields hold decimal or hexadecimal numbers; hexadecimal numbers +have no "0x" prefix, and use lower-case letters. + +Breakpad symbol files contain the following record types. With some +restrictions, these may appear in any order. + +* A `MODULE` record describes the executable file or shared library from which + this data was derived, for use by symbol suppliers. A `MODULE' record should + be the first record in the file. + +* A `FILE` record gives a source file name, and assigns it a number by which + other records can refer to it. + +* An `INLINE_ORIGIN` record holds an inline function name for `INLINE` records + to refer to. + +* A `FUNC` record describes a function present in the source code. + +* An `INLINE` record describes the inline function's nest level, call site + line and call site source file to which the given ranges of machine code + should be attributed. + +* A line record indicates to which source file and line a given range of + machine code should be attributed. The line is attributed to the function + defined by the most recent `FUNC` record. + +* A `PUBLIC` record gives the address of a linker symbol. + +* A `STACK` record provides information necessary to produce stack traces. + +# `MODULE` records + +A `MODULE` record provides meta-information about the module the symbol file +describes. It has the form: + +> `MODULE` _operatingsystem_ _architecture_ _id_ _name_ + +For example: `MODULE Linux x86 D3096ED481217FD4C16B29CD9BC208BA0 firefox-bin +` These records provide meta-information about the executable or shared library +from which this symbol file was generated. A symbol supplier might use this +information to find the correct symbol files to use to interpret a given +minidump, or to perform other sorts of validation. If present, a `MODULE` record +should be the first line in the file. + +The fields are separated by spaces, and cannot contain spaces themselves, except +for _name_. + +* The _operatingsystem_ field names the operating system on which the + executable or shared library was intended to run. This field should have one + of the following values: + + | **Value** | **Meaning** | + |:----------|:--------------------| + | Linux | Linux | + | mac | Macintosh OSX | + | windows | Microsoft Windows | + +* The _architecture_ field indicates what processor architecture the + executable or shared library contains machine code for. This field should + have one of the following values: + + | **Value** | **Instruction Set Architecture** | + |:----------|:---------------------------------| + | x86 | Intel IA-32 | + | x86\_64 | AMD64/Intel 64 | + | ppc | 32-bit PowerPC | + | ppc64 | 64-bit PowerPC | + | unknown | unknown | + +* The _id_ field is a sequence of hexadecimal digits that identifies the exact + executable or library whose contents the symbol file describes. The way in + which it is computed varies from platform to platform. + +* The _name_ field contains the base name (the final component of the + directory path) of the executable or library. It may contain spaces, and + extends to the end of the line. + +# `FILE` records + +A `FILE` record holds a source file name for other records to refer to. It has +the form: + +> `FILE` _number_ _name_ + +For example: `FILE 2 /home/jimb/mc/in/browser/app/nsBrowserApp.cpp +` + +A `FILE` record provides the name of a source file, and assigns it a number +which other records (line records, in particular) can use to refer to that file +name. The _number_ field is a decimal number. The _name_ field is the name of +the file; it may contain spaces. + +# `INLINE_ORIGIN` records + +An `INLINE_ORIGIN` record holds an inline function name for `INLINE` records to +refer to. It has the form: + +> `INLINE_ORIGIN` _number_ _name_ + +For example: `INLINE_ORIGIN 2 nsQueryInterfaceWithError::operator()(nsID const&, +void**) const +` + +An `INLINE_ORIGIN` record provides the name of an inline function, and assigns +it a number which other records (`INLINE` records, in particular) can use to +refer to that function name. The _number_ field is a decimal number. The _name_ +field is the name of the inline function; it may contain spaces. + +# `FUNC` records + +A `FUNC` record describes a source-language function. It has the form: + +> `FUNC` _[m]_ _address_ _size_ _parameter\_size_ _name_ + +For example: `FUNC m c184 30 0 nsQueryInterfaceWithError::operator()(nsID const&, +void**) const +` + +The _m_ field is optional. If present it indicates that multiple symbols +reference this function's instructions. (In which case, only one symbol name is +mentioned within the breakpad file.) Multiple symbols referencing the same +instructions may occur due to identical code folding by the linker. + +The _address_ and _size_ fields are hexadecimal numbers indicating the start +address and length in bytes of the machine code instructions the function +occupies. (Breakpad symbol files cannot accurately describe functions whose code +is not contiguous.) The start address is relative to the module's load address. + +The _parameter\_size_ field is a hexadecimal number indicating the size, in +bytes, of the arguments pushed on the stack for this function. Some calling +conventions, like the Microsoft Windows `stdcall` convention, require the called +function to pop parameters passed to it on the stack from its caller before +returning. The stack walker uses this value, along with data from `STACK` +records, to step from the called function's frame to the caller's frame. + +The _name_ field is the name of the function. In languages that use linker +symbol name mangling like C++, this should be the source language name (the +"unmangled" form). This field may contain spaces. + +# `INLINE` records + +An `INLINE` record describes the inline function's nest level, call site line +and call site source file to which the given ranges of machine code should be +attributed. It has the form: + +> `INLINE` _inline_nest_level_ _call_site_line_ _call_site_file_num_ +> _origin_num_ [_address_ _size_]+ + +For example: `INLINE 0 10 3 4 d30 2a fa1 b +` + +The _inline_nest_level_ field is a decimal number that means it's inlined at the +function described by a previous `INLINE` record which has _inline_nest_level_ +one less than its. In the example below, first and third `INLINE` records have +_inline_nest_level_ 0, which means they are inlined inside the function +described by the `FUNC` record. The second `INLINE` record has +_inline_nest_level_ 1 means that it's inlined at the inline function described +by first `INLINE` record. +``` +FUNC ... +INLINE 0 ... +INLINE 1 ... +INLINE 0 ... +``` + +The _call_site_line_ and _call_site_file_num_ fields are decimal numbers +indicating where this inline function being called at. + +The _origin_num_ field refers to an `INLINE_ORIGIN` record that has the name +of the inline function. + +The _address_ and _size_ fields are hexadecimal numbers indicating the start +address and length in bytes of the machine code. The address is relative to the +module's load address. There could be more than one [_address_ _size_] range +pair, since inline functions could have discontinuous address ranges. The ranges +of an `INLINE` record are always inside the ranges described by its parent +record (a `FUNC` record or an `INLINE` record). + +The `INLINE` record is assumed to belong to the function described by the last +preceding `FUNC` record. `INLINE` records may not appear before the first `FUNC` +record. + +# Line records + +A line record describes the source file and line number to which a given range +of machine code should be attributed. It has the form: + +> _address_ _size_ _line_ _filenum_ + +For example: `c184 7 59 4 +` + +Because they are so common, line records do not begin with a string indicating +the record type. All other record types' names use upper-case letters; +hexadecimal numbers, like a line record's _address_, use lower-case letters. + +The _address_ and _size_ fields are hexadecimal numbers indicating the start +address and length in bytes of the machine code. The address is relative to the +module's load address. + +The _line_ field is the line number to which the machine code should be +attributed, in decimal; the first line of the source file is line number 1. The +_filenum_ field is a decimal number appearing in a prior `FILE` record; the name +given in that record is the source file name for the machine code. + +The line is assumed to belong to the function described by the last preceding +`FUNC` record. Line records may not appear before the first `FUNC' record. + +No two line records in a symbol file cover the same range of addresses. However, +there may be many line records with identical line and file numbers, as a given +source line may contribute many non-contiguous blocks of machine code. + +# `PUBLIC` records + +A `PUBLIC` record describes a publicly visible linker symbol, such as that used +to identify an assembly language entry point or region of memory. It has the +form: + +> PUBLIC _[m]_ _address_ _parameter\_size_ _name_ + +For example: `PUBLIC m 2160 0 Public2_1 +` + +The Breakpad processor essentially treats a `PUBLIC` record as defining a +function with no line number data and an indeterminate size: the code extends to +the next address mentioned. If a given address is covered by both a `PUBLIC` +record and a `FUNC` record, the processor uses the `FUNC` data. + +The _m_ field is optional. If present it indicates that multiple symbols +reference this function's instructions. (In which case, only one symbol name is +mentioned within the breakpad file.) Multiple symbols referencing the same +instructions may occur due to identical code folding by the linker. + +The _address_ field is a hexadecimal number indicating the symbol's address, +relative to the module's load address. + +The _parameter\_size_ field is a hexadecimal number indicating the size of the +parameters passed to the code whose entry point the symbol marks, if known. This +field has the same meaning as the _parameter\_size_ field of a `FUNC` record; +see that description for more details. + +The _name_ field is the name of the symbol. In languages that use linker symbol +name mangling like C++, this should be the source language name (the "unmangled" +form). This field may contain spaces. + +# `STACK WIN` records + +Given a stack frame, a `STACK WIN` record indicates how to find the frame that +called it. It has the form: + +> STACK WIN _type_ _rva_ _code\_size_ _prologue\_size_ _epilogue\_size_ +> _parameter\_size_ _saved\_register\_size_ _local\_size_ _max\_stack\_size_ +> _has\_program\_string_ _program\_string\_OR\_allocates\_base\_pointer_ + +For example: `STACK WIN 4 2170 14 1 0 0 0 0 0 1 $eip 4 + ^ = $esp $ebp 8 + = +$ebp $ebp ^ = +` + +All fields of a `STACK WIN` record, except for the last, are hexadecimal +numbers. + +The _type_ field indicates what sort of stack frame data this record holds. Its +value should be one of the values of the +[StackFrameTypeEnum](http://msdn.microsoft.com/en-us/library/bc5207xw%28VS.100%29.aspx) +type in Microsoft's +[Debug Interface Access (DIA)](http://msdn.microsoft.com/en-us/library/x93ctkx8%28VS.100%29.aspx) API. +Breakpad uses only records of type 4 (`FrameTypeFrameData`) and 0 +(`FrameTypeFPO`); it ignores others. These types differ only in whether the last +field is an _allocates\_base\_pointer_ flag (`FrameTypeFPO`) or a program string +(`FrameTypeFrameData`). If more than one record covers a given address, Breakpad +prefers `FrameTypeFrameData` records over `FrameTypeFPO` records. + +The _rva_ and _code\_size_ fields give the starting address and length in bytes +of the machine code covered by this record. The starting address is relative to +the module's load address. + +The _prologue\_size_ and _epilogue\_size_ fields give the length, in bytes, of +the prologue and epilogue machine code within the record's range. Breakpad does +not use these values. + +The _parameter\_size_ field gives the number of argument bytes this function +expects to have been passed. This field has the same meaning as the +_parameter\_size_ field of a `FUNC` record; see that description for more +details. + +The _saved\_register\_size_ field gives the number of bytes in the stack frame +dedicated to preserving the values of any callee-saves registers used by this +function. + +The _local\_size_ field gives the number of bytes in the stack frame dedicated +to holding the function's local variables and temporary values. + +The _max\_stack\_size_ field gives the maximum number of bytes pushed on the +stack in the frame. Breakpad does not use this value. + +If the _has\_program\_string_ field is zero, then the `STACK WIN` record's final +field is an _allocates\_base\_pointer_ flag, as a hexadecimal number; this is +expected for records whose _type_ is 0. Otherwise, the final field is a program +string. + +## Interpreting a `STACK WIN` record + +Given the register values for a frame F, we can find the calling frame as +follows: + +* If the _has\_program\_string_ field of a `STACK WIN` record is zero, then + the final field is _allocates\_base\_pointer_, a flag indicating whether the + frame uses the frame pointer register, `%ebp`, as a general-purpose + register. + * If _allocates\_base\_pointer_ is true, then `%ebp` does not point to the + frame's base address. Instead, + * Let _next\_parameter\_size_ be the parameter size of the function + frame F called (**not** this record's _parameter\_size_ field), or + zero if F is the youngest frame on the stack. You must find this + value in F's callee's `FUNC`, `STACK WIN`, or `PUBLIC` records. + * Let _frame\_size_ be the sum of the _local\_size_ field, the + _saved\_register\_size_ field, and _next\_parameter\_size_. > > With + those definitions in place, we can recover the calling frame as + follows: + * F's return address is at `%esp +`_frame\_size_, + * the caller's value of `%ebp` is saved at `%esp + +`_next\_parameter\_size_`+`_saved\_register\_size_`- 8`, and + * the caller's value of `%esp` just before the call instruction was + `%esp +`_frame\_size_`+ 4`. > > (Why do we include + _next\_parameter\_size_ in the sum when computing _frame\_size_ and + the address of the saved `%ebp`? When a function A has called a + function B, the arguments that A pushed for B are considered part of + A's stack frame: A's value for `%esp` points at the last argument + pushed for B. Thus, we must include the size of those arguments + (given by the debugging info for B) along with the size of A's + register save area and local variable area (given by the debugging + info for A) when computing the overall size of A's frame.) + * If _allocates\_base\_pointer_ is false, then F's function doesn't use + `%ebp` at all. You may recover the calling frame as above, except that + the caller's value of `%ebp` is the same as F's value for `%ebp`, so no + steps are necessary to recover it. +* If the _has\_program\_string_ field of a `STACK WIN` record is not zero, + then the record's final field is a string containing a program to be + interpreted to recover the caller's frame. The comments in the + [postfix\_evaluator.h](../src/processor/postfix_evaluator.h#40) + header file explain the language in which the program is written. You should + place the following variables in the dictionary before interpreting the + program: + * `$ebp` and `$esp` should be the values of the `%ebp` and `%esp` + registers in F. + * `.cbParams`, `.cbSavedRegs`, and `.cbLocals`, should be the values of + the `STACK WIN` record's _parameter\_size_, _saved\_register\_size_, and + _local\_size_ fields. + * `.raSearchStart` should be set to the address on the stack to begin + scanning for a return address, if necessary. The Breakpad processor sets + this to the value of `%esp` in F, plus the _frame\_size_ value mentioned + above. + +> If the program stores values for `$eip`, `$esp`, `$ebp`, `$ebx`, `$esi`, or +> `$edi`, then those are the values of the given registers in the caller. If the +> value of `$eip` is zero, that indicates that the end of the stack has been +> reached. + +The Breakpad processor checks that the value yielded by the above for the +calling frame's instruction address refers to known code; if the address seems +to be bogus, then it uses a heuristic search to find F's return address and +stack base. + +# `STACK CFI` records + +`STACK CFI` ("Call Frame Information") records describe how to walk the stack +when execution is at a given machine instruction. These records take one of two +forms: + +> `STACK CFI INIT` _address_ _size_ _register1_: +> _expression1_ _register2_: _expression2_ ... +> +> `STACK CFI` _address_ _register1_: _expression1_ +> _register2_: _expression2_ ... + +For example: + +``` +STACK CFI INIT 804c4b0 40 .cfa: $esp 4 + $eip: .cfa 4 - ^ +STACK CFI 804c4b1 .cfa: $esp 8 + $ebp: .cfa 8 - ^ +``` + +The _address_ and _size_ fields are hexadecimal numbers. Each +_register_i is the name of a register or pseudoregister. Each +_expression_ is a Breakpad postfix expression, which may contain spaces, but +never ends with a colon. (The appropriate register names for a given +architecture are determined when `STACK CFI` records are first enabled for that +architecture, and should be documented in the appropriate +`stackwalker_`_architecture_`.cc` source file.) + +STACK CFI records describe, at each machine instruction in a given function, how +to recover the values the machine registers had in the function's caller. +Naturally, some registers' values are simply lost, but there are three cases in +which they can be recovered: + +* You can always recover the program counter, because that's the function's + return address. If the function is ever going to return, the PC must be + saved somewhere. + +* You can always recover the stack pointer. The function is responsible for + popping its stack frame before it returns to the caller, so it must be able + to restore this, as well. + +* You should be able to recover the values of callee-saves registers. These + are registers whose values the callee must preserve, either by saving them + in its own stack frame before using them and re-loading them before + returning, or by not using them at all. + +(As an exception, note that functions which never return may not save any of +this data. It may not be possible to walk the stack past such functions' stack +frames.) + +Given rules for recovering the values of a function's caller's registers, we can +walk up the stack. Starting with the current set of registers --- the PC of the +instruction we're currently executing, the current stack pointer, etc. --- we +use CFI to recover the values those registers had in the caller of the current +frame. This gives us a PC in the caller whose CFI we can look up; we apply the +process again to find that function's caller; and so on. + +Concretely, CFI records represent a table with a row for each machine +instruction address and a column for each register. The table entry for a given +address and register contains a rule describing how, when the PC is at that +address, to restore the value that register had in the caller. + +There are some special columns: + +* A column named `.cfa`, for "Canonical Frame Address", tells how to compute + the base address of the frame; other entries can refer to the CFA in their + rules. + +* A column named `.ra` represents the return address. + +For example, suppose we have a machine with 32-bit registers, one-byte +instructions, a stack that grows downwards, and an assembly language that +resembles C. Suppose further that we have a function whose machine code looks +like this: + +``` +func: ; entry point; return address at sp +func+0: sp -= 16 ; allocate space for stack frame +func+1: sp[12] = r0 ; save 4-byte r0 at sp+12 + ... ; stuff that doesn't affect stack +func+10: sp -= 4; *sp = x ; push some 4-byte x on the stack + ... ; stuff that doesn't affect stack +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 +``` + +The following table would describe the function above: + +| **code address** | **.cfa** | **r0 (on Google Code)** | **r1 (on Google Code)** | ... | **.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 + execute the first instruction, which allocates the stack frame. In the next + row, the formula for computing the CFA has changed, reflecting the + 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, to + find the caller's value for r0 (on Google Code) at func+2, we would first + compute the CFA by adding 16 to the sp, and then subtract four from that to + find the address at which r0 (on Google Code) was saved. + +* Although the example doesn't show this, most calling conventions designate + "callee-saves" and "caller-saves" registers. The callee must restore the + values of "callee-saves" registers before returning (if it uses them at + all), whereas the callee is free to use "caller-saves" registers without + restoring their values. A function that uses caller-saves registers + typically does not save their original values at all; in this case, the CFI + marks such registers' values as "unrecoverable". + +* Exactly where the CFA points in the frame --- at the return address? below + it? At some fixed point within the frame? --- is a question of definition + that depends on the architecture and ABI in use. But by definition, the CFA + remains constant throughout the lifetime of the frame. It's up to + architecture- specific code to know what significance to assign the CFA, if + any. + +To save space, the most common type of CFI record only mentions the table +entries at which changes take place. So for the above, the CFI data would only +actually mention the non-blank entries here: + +| **insn** | **cfa** | **r0 (on Google Code)** | **r1 (on Google Code)** | ... | **ra** | +|:---------|:--------|:------------------------|:------------------------|:----|:---------| +| func+0 | sp | | | | `cfa[0]` | +| func+1 | sp+16 | | | | | +| func+2 | | `cfa[-4]` | | | | +| func+11 | sp+20 | | | | | +| func+21 | | r0 (on Google Code) | | | | +| func+22 | sp | | | | | + +A `STACK CFI INIT` record indicates that, at the machine instruction at +_address_, belonging to some function, the value that _registern_ had +in that function's caller can be recovered by evaluating +_expressionn_. The values of any callee-saves registers not mentioned +are assumed to be unchanged. (`STACK CFI` records never mention caller-saves +registers.) These rules apply starting at _address_ and continue up to, but not +including, the address given in the next `STACK CFI` record. The _size_ field is +the total number of bytes of machine code covered by this record and any +subsequent `STACK CFI` records (until the next `STACK CFI INIT` record). The +_address_ field is relative to the module's load address. + +A `STACK CFI` record (no `INIT`) is the same, except that it mentions only those +registers whose recovery rules have changed from the previous CFI record. There +must be a prior `STACK CFI INIT` or `STACK CFI` record in the symbol file. The +_address_ field of this record must be greater than that of the previous record, +and it must not be at or beyond the end of the range given by the most recent +`STACK CFI INIT` record. The address is relative to the module's load address. + +Each expression is a breakpad-style postfix expression. Expressions may contain +spaces, but their tokens may not end with colons. When an expression mentions a +register, it refers to the value of that register in the callee, even if a prior +name/expression pair gives that register's value in the caller. The exception is +`.cfa`, which refers to the canonical frame address computed by the .cfa rule in +force at the current instruction. + +The special expression `.undef` indicates that the given register's value cannot +be recovered. + +The register names preceding the expressions are always followed by colons. The +expressions themselves never contain tokens ending with colons. + +There are two special register names: + +* `.cfa` ("Canonical Frame Address") is the base address of the stack frame. + Other registers' rules may refer to this. If no rule is provided for the + stack pointer, the value of `.cfa` is the caller's stack pointer. + +* `.ra` is the return address. This is the value of the restored program + counter. We use `.ra` instead of the architecture-specific name for the + program counter. + +The Breakpad stack walker requires that there be rules in force for `.cfa` and +`.ra` at every code address from which it unwinds. If those rules are not +present, the stack walker will ignore the `STACK CFI` data, and try to use a +different strategy. + +So the CFI for the example function above would be as follows, if `func` were at +address 0x1000 (relative to the module's load address): + +``` +STACK CFI INIT 1000 .cfa: $sp .ra: .cfa ^ +STACK CFI 1001 .cfa: $sp 16 + +STACK CFI 1002 $r0: .cfa 4 - ^ +STACK CFI 100b .cfa: $sp 20 + +STACK CFI 1015 $r0: $r0 +STACK CFI 1016 .cfa: $sp +``` diff --git a/shared/sentry/external/breakpad/docs/windows_client_integration.md b/shared/sentry/external/breakpad/docs/windows_client_integration.md new file mode 100644 index 000000000..99a84926d --- /dev/null +++ b/shared/sentry/external/breakpad/docs/windows_client_integration.md @@ -0,0 +1,70 @@ +# Windows Integration overview + +## Windows Client Code + +The Windows client code is in the `src/client/windows` directory of the tree. +Since the header files are fairly well commented some specifics are purposely +omitted from this document. + +## Integration of minidump-generation + +Once you build the solution inside `src/client/windows`, an output file of +`exception_handler.lib` will be generated. You can either check this into your +project's directory or build directly from the source, as the project itself +does. + +Enabling Breakpad in your application requires you to `#include +"exception_handler.h"` and instantiate the `ExceptionHandler` object like so: + +``` + handler = new ExceptionHandler(const wstring& dump_path, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + int handler_types, + MINIDUMP_TYPE dump_type, + const wchar_t* pipe_name, + const CustomClientInfo* custom_info); +``` + +The parameters, in order, are: + +* pathname for minidumps to be written to - this is ignored if OOP dump + generation is used +* A callback that is called when the exception is first handled - you can + return true/false here to continue/stop exception processing +* A callback that is called after minidumps have been written +* Context for the callbacks +* Which exceptions to handle - see `HandlerType` enumeration in + exception\_handler.h +* The type of minidump to generate, using the `MINIDUMP_TYPE` definitions in + `DbgHelp.h` +* A pipe name that can be used to communicate with a crash generation server +* A pointer to a CustomClientInfo class that can be used to send custom data + along with the minidump when using OOP generation + +You can also see `src/client/windows/tests/crash_generation_app/*` for a sample +app that uses OOP generation. + +## OOP Minidump Generation + +For out of process minidump generation, more work is needed. If you look inside +`src/client/windows/crash_generation`, you will see a file called +`crash_generation_server.h`. This file is the interface for a crash generation +server, which must be instantiated with the same pipe name that is passed to the +client above. The logistics of running a separate process that instantiates the +crash generation server is left up to you, however. + +## Build process specifics(symbol generation, upload) + +The symbol creation step is talked about in the general overview doc, since it +doesn't vary much by platform. You'll need to make sure that the symbols are +available wherever minidumps are uploaded to for processing. + +## Out in the field - uploading the minidump + +Inside `src/client/windows/sender` is a class implementation called +`CrashReportSender`. This class can be compiled into a separate standalone CLI +or in the crash generation server and used to upload the report; it can know +when to do so via one of the callbacks provided by the `CrashGenerationServer` +or the `ExceptionHandler` object for in-process generation. diff --git a/shared/sentry/external/breakpad/m4/ax_append_compile_flags.m4 b/shared/sentry/external/breakpad/m4/ax_append_compile_flags.m4 new file mode 100644 index 000000000..2bb27ef2b --- /dev/null +++ b/shared/sentry/external/breakpad/m4/ax_append_compile_flags.m4 @@ -0,0 +1,67 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_append_compile_flags.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_APPEND_COMPILE_FLAGS([FLAG1 FLAG2 ...], [FLAGS-VARIABLE], [EXTRA-FLAGS], [INPUT]) +# +# DESCRIPTION +# +# For every FLAG1, FLAG2 it is checked whether the compiler works with the +# flag. If it does, the flag is added FLAGS-VARIABLE +# +# If FLAGS-VARIABLE is not specified, the current language's flags (e.g. +# CFLAGS) is used. During the check the flag is always added to the +# current language's flags. +# +# If EXTRA-FLAGS is defined, it is added to the current language's default +# flags (e.g. CFLAGS) when the check is done. The check is thus made with +# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to +# force the compiler to issue an error when a bad flag is given. +# +# INPUT gives an alternative input source to AC_COMPILE_IFELSE. +# +# NOTE: This macro depends on the AX_APPEND_FLAG and +# AX_CHECK_COMPILE_FLAG. Please keep this macro in sync with +# AX_APPEND_LINK_FLAGS. +# +# LICENSE +# +# Copyright (c) 2011 Maarten Bosmans +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 5 + +AC_DEFUN([AX_APPEND_COMPILE_FLAGS], +[AX_REQUIRE_DEFINED([AX_CHECK_COMPILE_FLAG]) +AX_REQUIRE_DEFINED([AX_APPEND_FLAG]) +for flag in $1; do + AX_CHECK_COMPILE_FLAG([$flag], [AX_APPEND_FLAG([$flag], [$2])], [], [$3], [$4]) +done +])dnl AX_APPEND_COMPILE_FLAGS diff --git a/shared/sentry/external/breakpad/m4/ax_append_flag.m4 b/shared/sentry/external/breakpad/m4/ax_append_flag.m4 new file mode 100644 index 000000000..08f2e07ec --- /dev/null +++ b/shared/sentry/external/breakpad/m4/ax_append_flag.m4 @@ -0,0 +1,71 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_append_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_APPEND_FLAG(FLAG, [FLAGS-VARIABLE]) +# +# DESCRIPTION +# +# FLAG is appended to the FLAGS-VARIABLE shell variable, with a space +# added in between. +# +# If FLAGS-VARIABLE is not specified, the current language's flags (e.g. +# CFLAGS) is used. FLAGS-VARIABLE is not changed if it already contains +# FLAG. If FLAGS-VARIABLE is unset in the shell, it is set to exactly +# FLAG. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 6 + +AC_DEFUN([AX_APPEND_FLAG], +[dnl +AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_SET_IF +AS_VAR_PUSHDEF([FLAGS], [m4_default($2,_AC_LANG_PREFIX[FLAGS])]) +AS_VAR_SET_IF(FLAGS,[ + AS_CASE([" AS_VAR_GET(FLAGS) "], + [*" $1 "*], [AC_RUN_LOG([: FLAGS already contains $1])], + [ + AS_VAR_APPEND(FLAGS,[" $1"]) + AC_RUN_LOG([: FLAGS="$FLAGS"]) + ]) + ], + [ + AS_VAR_SET(FLAGS,[$1]) + AC_RUN_LOG([: FLAGS="$FLAGS"]) + ]) +AS_VAR_POPDEF([FLAGS])dnl +])dnl AX_APPEND_FLAG diff --git a/shared/sentry/external/breakpad/m4/ax_check_compile_flag.m4 b/shared/sentry/external/breakpad/m4/ax_check_compile_flag.m4 new file mode 100644 index 000000000..ca3639715 --- /dev/null +++ b/shared/sentry/external/breakpad/m4/ax_check_compile_flag.m4 @@ -0,0 +1,74 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) +# +# DESCRIPTION +# +# Check whether the given FLAG works with the current language's compiler +# or gives an error. (Warnings, however, are ignored) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# If EXTRA-FLAGS is defined, it is added to the current language's default +# flags (e.g. CFLAGS) when the check is done. The check is thus made with +# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to +# force the compiler to issue an error when a bad flag is given. +# +# INPUT gives an alternative input source to AC_COMPILE_IFELSE. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this +# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 4 + +AC_DEFUN([AX_CHECK_COMPILE_FLAG], +[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF +AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl +AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ + ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS + _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" + AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], + [AS_VAR_SET(CACHEVAR,[yes])], + [AS_VAR_SET(CACHEVAR,[no])]) + _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) +AS_VAR_IF(CACHEVAR,yes, + [m4_default([$2], :)], + [m4_default([$3], :)]) +AS_VAR_POPDEF([CACHEVAR])dnl +])dnl AX_CHECK_COMPILE_FLAGS diff --git a/shared/sentry/external/breakpad/m4/ax_check_define.m4 b/shared/sentry/external/breakpad/m4/ax_check_define.m4 new file mode 100644 index 000000000..20ba80868 --- /dev/null +++ b/shared/sentry/external/breakpad/m4/ax_check_define.m4 @@ -0,0 +1,92 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_check_define.html +# =========================================================================== +# +# SYNOPSIS +# +# AC_CHECK_DEFINE([symbol], [ACTION-IF-FOUND], [ACTION-IF-NOT]) +# AX_CHECK_DEFINE([includes],[symbol], [ACTION-IF-FOUND], [ACTION-IF-NOT]) +# +# DESCRIPTION +# +# Complements AC_CHECK_FUNC but it does not check for a function but for a +# define to exist. Consider a usage like: +# +# AC_CHECK_DEFINE(__STRICT_ANSI__, CFLAGS="$CFLAGS -D_XOPEN_SOURCE=500") +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# +# This program is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 9 + +AU_ALIAS([AC_CHECK_DEFINED], [AC_CHECK_DEFINE]) +AC_DEFUN([AC_CHECK_DEFINE],[ +AS_VAR_PUSHDEF([ac_var],[ac_cv_defined_$1])dnl +AC_CACHE_CHECK([for $1 defined], ac_var, +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[]], [[ + #ifdef $1 + int ok; + #else + choke me + #endif +]])],[AS_VAR_SET(ac_var, yes)],[AS_VAR_SET(ac_var, no)])) +AS_IF([test AS_VAR_GET(ac_var) != "no"], [$2], [$3])dnl +AS_VAR_POPDEF([ac_var])dnl +]) + +AU_ALIAS([AX_CHECK_DEFINED], [AX_CHECK_DEFINE]) +AC_DEFUN([AX_CHECK_DEFINE],[ +AS_VAR_PUSHDEF([ac_var],[ac_cv_defined_$2_$1])dnl +AC_CACHE_CHECK([for $2 defined in $1], ac_var, +AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include <$1>]], [[ + #ifdef $2 + int ok; + #else + choke me + #endif +]])],[AS_VAR_SET(ac_var, yes)],[AS_VAR_SET(ac_var, no)])) +AS_IF([test AS_VAR_GET(ac_var) != "no"], [$3], [$4])dnl +AS_VAR_POPDEF([ac_var])dnl +]) + +AC_DEFUN([AX_CHECK_FUNC], +[AS_VAR_PUSHDEF([ac_var], [ac_cv_func_$2])dnl +AC_CACHE_CHECK([for $2], ac_var, +dnl AC_LANG_FUNC_LINK_TRY +[AC_LINK_IFELSE([AC_LANG_PROGRAM([$1 + #undef $2 + char $2 ();],[ + char (*f) () = $2; + return f != $2; ])], + [AS_VAR_SET(ac_var, yes)], + [AS_VAR_SET(ac_var, no)])]) +AS_IF([test AS_VAR_GET(ac_var) = yes], [$3], [$4])dnl +AS_VAR_POPDEF([ac_var])dnl +])# AC_CHECK_FUNC diff --git a/shared/sentry/external/breakpad/m4/ax_cxx_compile_stdcxx.m4 b/shared/sentry/external/breakpad/m4/ax_cxx_compile_stdcxx.m4 new file mode 100644 index 000000000..079e17d2a --- /dev/null +++ b/shared/sentry/external/breakpad/m4/ax_cxx_compile_stdcxx.m4 @@ -0,0 +1,558 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_cxx_compile_stdcxx.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CXX_COMPILE_STDCXX(VERSION, [ext|noext], [mandatory|optional]) +# +# DESCRIPTION +# +# Check for baseline language coverage in the compiler for the specified +# version of the C++ standard. If necessary, add switches to CXXFLAGS to +# enable support. VERSION may be '11' (for the C++11 standard) or '14' +# (for the C++14 standard). +# +# The second argument, if specified, indicates whether you insist on an +# extended mode (e.g. -std=gnu++11) or a strict conformance mode (e.g. +# -std=c++11). If neither is specified, you get whatever works, with +# preference for an extended mode. +# +# The third argument, if specified 'mandatory' or if left unspecified, +# indicates that baseline support for the specified C++ standard is +# required and that the macro should error out if no mode with that +# support is found. If specified 'optional', then configuration proceeds +# regardless, after defining HAVE_CXX${VERSION} if and only if a +# supporting mode is found. +# +# LICENSE +# +# Copyright (c) 2008 Benjamin Kosnik +# Copyright (c) 2012 Zack Weinberg +# Copyright (c) 2013 Roy Stogner +# Copyright (c) 2014, 2015 Google Inc.; contributed by Alexey Sokolov +# Copyright (c) 2015 Paul Norman +# Copyright (c) 2015 Moritz Klammler +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 1 + +dnl This macro is based on the code from the AX_CXX_COMPILE_STDCXX_11 macro +dnl (serial version number 13). + +AC_DEFUN([AX_CXX_COMPILE_STDCXX], [dnl + m4_if([$1], [11], [], + [$1], [14], [], + [$1], [17], [m4_fatal([support for C++17 not yet implemented in AX_CXX_COMPILE_STDCXX])], + [m4_fatal([invalid first argument `$1' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$2], [], [], + [$2], [ext], [], + [$2], [noext], [], + [m4_fatal([invalid second argument `$2' to AX_CXX_COMPILE_STDCXX])])dnl + m4_if([$3], [], [ax_cxx_compile_cxx$1_required=true], + [$3], [mandatory], [ax_cxx_compile_cxx$1_required=true], + [$3], [optional], [ax_cxx_compile_cxx$1_required=false], + [m4_fatal([invalid third argument `$3' to AX_CXX_COMPILE_STDCXX])]) + AC_LANG_PUSH([C++])dnl + ac_success=no + AC_CACHE_CHECK(whether $CXX supports C++$1 features by default, + ax_cv_cxx_compile_cxx$1, + [AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [ax_cv_cxx_compile_cxx$1=yes], + [ax_cv_cxx_compile_cxx$1=no])]) + if test x$ax_cv_cxx_compile_cxx$1 = xyes; then + ac_success=yes + fi + + m4_if([$2], [noext], [], [dnl + if test x$ac_success = xno; then + for switch in -std=gnu++$1 -std=gnu++0x; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXXFLAGS="$ac_save_CXXFLAGS"]) + if eval test x\$$cachevar = xyes; then + CXXFLAGS="$CXXFLAGS $switch" + ac_success=yes + break + fi + done + fi]) + + m4_if([$2], [ext], [], [dnl + if test x$ac_success = xno; then + dnl HP's aCC needs +std=c++11 according to: + dnl http://h21007.www2.hp.com/portal/download/files/unprot/aCxx/PDF_Release_Notes/769149-001.pdf + dnl Cray's crayCC needs "-h std=c++11" + for switch in -std=c++$1 -std=c++0x +std=c++$1 "-h std=c++$1"; do + cachevar=AS_TR_SH([ax_cv_cxx_compile_cxx$1_$switch]) + AC_CACHE_CHECK(whether $CXX supports C++$1 features with $switch, + $cachevar, + [ac_save_CXXFLAGS="$CXXFLAGS" + CXXFLAGS="$CXXFLAGS $switch" + AC_COMPILE_IFELSE([AC_LANG_SOURCE([_AX_CXX_COMPILE_STDCXX_testbody_$1])], + [eval $cachevar=yes], + [eval $cachevar=no]) + CXXFLAGS="$ac_save_CXXFLAGS"]) + if eval test x\$$cachevar = xyes; then + CXXFLAGS="$CXXFLAGS $switch" + ac_success=yes + break + fi + done + fi]) + AC_LANG_POP([C++]) + if test x$ax_cxx_compile_cxx$1_required = xtrue; then + if test x$ac_success = xno; then + AC_MSG_ERROR([*** A compiler with support for C++$1 language features is required.]) + fi + else + if test x$ac_success = xno; then + HAVE_CXX$1=0 + AC_MSG_NOTICE([No compiler with C++$1 support was found]) + else + HAVE_CXX$1=1 + AC_DEFINE(HAVE_CXX$1,1, + [define if the compiler supports basic C++$1 syntax]) + fi + + AC_SUBST(HAVE_CXX$1) + fi +]) + + +dnl Test body for checking C++11 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_11], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 +) + + +dnl Test body for checking C++14 support + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_14], + _AX_CXX_COMPILE_STDCXX_testbody_new_in_11 + _AX_CXX_COMPILE_STDCXX_testbody_new_in_14 +) + + +dnl Tests for new features in C++11 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_11], [[ + +// If the compiler admits that it is not ready for C++11, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201103L + +#error "This is not a C++11 compiler" + +#else + +namespace cxx11 +{ + + namespace test_static_assert + { + + template + struct check + { + static_assert(sizeof(int) <= sizeof(T), "not big enough"); + }; + + } + + namespace test_final_override + { + + struct Base + { + virtual void f() {} + }; + + struct Derived : public Base + { + virtual void f() override {} + }; + + } + + namespace test_double_right_angle_brackets + { + + template < typename T > + struct check {}; + + typedef check single_type; + typedef check> double_type; + typedef check>> triple_type; + typedef check>>> quadruple_type; + + } + + namespace test_decltype + { + + int + f() + { + int a = 1; + decltype(a) b = 2; + return a + b; + } + + } + + namespace test_type_deduction + { + + template < typename T1, typename T2 > + struct is_same + { + static const bool value = false; + }; + + template < typename T > + struct is_same + { + static const bool value = true; + }; + + template < typename T1, typename T2 > + auto + add(T1 a1, T2 a2) -> decltype(a1 + a2) + { + return a1 + a2; + } + + int + test(const int c, volatile int v) + { + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == false, ""); + auto ac = c; + auto av = v; + auto sumi = ac + av + 'x'; + auto sumf = ac + av + 1.0; + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == true, ""); + static_assert(is_same::value == false, ""); + static_assert(is_same::value == true, ""); + return (sumf > 0.0) ? sumi : add(c, v); + } + + } + + namespace test_noexcept + { + + int f() { return 0; } + int g() noexcept { return 0; } + + static_assert(noexcept(f()) == false, ""); + static_assert(noexcept(g()) == true, ""); + + } + + namespace test_constexpr + { + + template < typename CharT > + unsigned long constexpr + strlen_c_r(const CharT *const s, const unsigned long acc) noexcept + { + return *s ? strlen_c_r(s + 1, acc + 1) : acc; + } + + template < typename CharT > + unsigned long constexpr + strlen_c(const CharT *const s) noexcept + { + return strlen_c_r(s, 0UL); + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("1") == 1UL, ""); + static_assert(strlen_c("example") == 7UL, ""); + static_assert(strlen_c("another\0example") == 7UL, ""); + + } + + namespace test_rvalue_references + { + + template < int N > + struct answer + { + static constexpr int value = N; + }; + + answer<1> f(int&) { return answer<1>(); } + answer<2> f(const int&) { return answer<2>(); } + answer<3> f(int&&) { return answer<3>(); } + + void + test() + { + int i = 0; + const int c = 0; + static_assert(decltype(f(i))::value == 1, ""); + static_assert(decltype(f(c))::value == 2, ""); + static_assert(decltype(f(0))::value == 3, ""); + } + + } + + namespace test_uniform_initialization + { + + struct test + { + static const int zero {}; + static const int one {1}; + }; + + static_assert(test::zero == 0, ""); + static_assert(test::one == 1, ""); + + } + + namespace test_lambdas + { + + void + test1() + { + auto lambda1 = [](){}; + auto lambda2 = lambda1; + lambda1(); + lambda2(); + } + + int + test2() + { + auto a = [](int i, int j){ return i + j; }(1, 2); + auto b = []() -> int { return '0'; }(); + auto c = [=](){ return a + b; }(); + auto d = [&](){ return c; }(); + auto e = [a, &b](int x) mutable { + const auto identity = [](int y){ return y; }; + for (auto i = 0; i < a; ++i) + a += b--; + return x + identity(a + b); + }(0); + return a + b + c + d + e; + } + + int + test3() + { + const auto nullary = [](){ return 0; }; + const auto unary = [](int x){ return x; }; + using nullary_t = decltype(nullary); + using unary_t = decltype(unary); + const auto higher1st = [](nullary_t f){ return f(); }; + const auto higher2nd = [unary](nullary_t f1){ + return [unary, f1](unary_t f2){ return f2(unary(f1())); }; + }; + return higher1st(nullary) + higher2nd(nullary)(unary); + } + + } + + namespace test_variadic_templates + { + + template + struct sum; + + template + struct sum + { + static constexpr auto value = N0 + sum::value; + }; + + template <> + struct sum<> + { + static constexpr auto value = 0; + }; + + static_assert(sum<>::value == 0, ""); + static_assert(sum<1>::value == 1, ""); + static_assert(sum<23>::value == 23, ""); + static_assert(sum<1, 2>::value == 3, ""); + static_assert(sum<5, 5, 11>::value == 21, ""); + static_assert(sum<2, 3, 5, 7, 11, 13>::value == 41, ""); + + } + + // http://stackoverflow.com/questions/13728184/template-aliases-and-sfinae + // Clang 3.1 fails with headers of libstd++ 4.8.3 when using std::function + // because of this. + namespace test_template_alias_sfinae + { + + struct foo {}; + + template + using member = typename T::member_type; + + template + void func(...) {} + + template + void func(member*) {} + + void test(); + + void test() { func(0); } + + } + +} // namespace cxx11 + +#endif // __cplusplus >= 201103L + +]]) + + +dnl Tests for new features in C++14 + +m4_define([_AX_CXX_COMPILE_STDCXX_testbody_new_in_14], [[ + +// If the compiler admits that it is not ready for C++14, why torture it? +// Hopefully, this will speed up the test. + +#ifndef __cplusplus + +#error "This is not a C++ compiler" + +#elif __cplusplus < 201402L + +#error "This is not a C++14 compiler" + +#else + +namespace cxx14 +{ + + namespace test_polymorphic_lambdas + { + + int + test() + { + const auto lambda = [](auto&&... args){ + const auto istiny = [](auto x){ + return (sizeof(x) == 1UL) ? 1 : 0; + }; + const int aretiny[] = { istiny(args)... }; + return aretiny[0]; + }; + return lambda(1, 1L, 1.0f, '1'); + } + + } + + namespace test_binary_literals + { + + constexpr auto ivii = 0b0000000000101010; + static_assert(ivii == 42, "wrong value"); + + } + + namespace test_generalized_constexpr + { + + template < typename CharT > + constexpr unsigned long + strlen_c(const CharT *const s) noexcept + { + auto length = 0UL; + for (auto p = s; *p; ++p) + ++length; + return length; + } + + static_assert(strlen_c("") == 0UL, ""); + static_assert(strlen_c("x") == 1UL, ""); + static_assert(strlen_c("test") == 4UL, ""); + static_assert(strlen_c("another\0test") == 7UL, ""); + + } + + namespace test_lambda_init_capture + { + + int + test() + { + auto x = 0; + const auto lambda1 = [a = x](int b){ return a + b; }; + const auto lambda2 = [a = lambda1(x)](){ return a; }; + return lambda2(); + } + + } + + namespace test_digit_seperators + { + + constexpr auto ten_million = 100'000'000; + static_assert(ten_million == 100000000, ""); + + } + + namespace test_return_type_deduction + { + + auto f(int& x) { return x; } + decltype(auto) g(int& x) { return x; } + + template < typename T1, typename T2 > + struct is_same + { + static constexpr auto value = false; + }; + + template < typename T > + struct is_same + { + static constexpr auto value = true; + }; + + int + test() + { + auto x = 0; + static_assert(is_same::value, ""); + static_assert(is_same::value, ""); + return x; + } + + } + +} // namespace cxx14 + +#endif // __cplusplus >= 201402L + +]]) diff --git a/shared/sentry/external/breakpad/m4/ax_pthread.m4 b/shared/sentry/external/breakpad/m4/ax_pthread.m4 new file mode 100644 index 000000000..77ae09c3f --- /dev/null +++ b/shared/sentry/external/breakpad/m4/ax_pthread.m4 @@ -0,0 +1,283 @@ +# =========================================================================== +# http://www.nongnu.org/autoconf-archive/ax_pthread.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PTHREAD([ACTION-IF-FOUND[, ACTION-IF-NOT-FOUND]]) +# +# DESCRIPTION +# +# This macro figures out how to build C programs using POSIX threads. It +# sets the PTHREAD_LIBS output variable to the threads library and linker +# flags, and the PTHREAD_CFLAGS output variable to any special C compiler +# flags that are needed. (The user can also force certain compiler +# flags/libs to be tested by setting these environment variables.) +# +# Also sets PTHREAD_CC to any special C compiler that is needed for +# multi-threaded programs (defaults to the value of CC otherwise). (This +# is necessary on AIX to use the special cc_r compiler alias.) +# +# NOTE: You are assumed to not only compile your program with these flags, +# but also link it with them as well. e.g. you should link with +# $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS +# +# If you are only building threads programs, you may wish to use these +# variables in your default LIBS, CFLAGS, and CC: +# +# LIBS="$PTHREAD_LIBS $LIBS" +# CFLAGS="$CFLAGS $PTHREAD_CFLAGS" +# CC="$PTHREAD_CC" +# +# In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant +# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name +# (e.g. PTHREAD_CREATE_UNDETACHED on AIX). +# +# ACTION-IF-FOUND is a list of shell commands to run if a threads library +# is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it +# is not found. If ACTION-IF-FOUND is not specified, the default action +# will define HAVE_PTHREAD. +# +# Please let the authors know if this macro fails on any platform, or if +# you have any other suggestions or comments. This macro was based on work +# by SGJ on autoconf scripts for FFTW (http://www.fftw.org/) (with help +# from M. Frigo), as well as ac_pthread and hb_pthread macros posted by +# Alejandro Forero Cuervo to the autoconf macro repository. We are also +# grateful for the helpful feedback of numerous users. +# +# LICENSE +# +# Copyright (c) 2008 Steven G. Johnson +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 6 + +AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) +AC_DEFUN([AX_PTHREAD], [ +AC_REQUIRE([AC_CANONICAL_HOST]) +AC_LANG_SAVE +AC_LANG_C +ax_pthread_ok=no + +# We used to check for pthread.h first, but this fails if pthread.h +# requires special compiler flags (e.g. on True64 or Sequent). +# It gets checked for in the link test anyway. + +# First of all, check if the user has set any of the PTHREAD_LIBS, +# etcetera environment variables, and if threads linking works using +# them: +if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) + AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes) + AC_MSG_RESULT($ax_pthread_ok) + if test x"$ax_pthread_ok" = xno; then + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" + fi + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" +fi + +# We must check for the threads library under a number of different +# names; the ordering is very important because some systems +# (e.g. DEC) have both -lpthread and -lpthreads, where one of the +# libraries is broken (non-POSIX). + +# Create a list of thread flags to try. Items starting with a "-" are +# C compiler flags, and other items are library names, except for "none" +# which indicates that we try without any flags at all, and "pthread-config" +# which is a program returning the flags for the Pth emulation library. + +ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" + +# The ordering *is* (sometimes) important. Some notes on the +# individual items follow: + +# pthreads: AIX (must check this before -lpthread) +# none: in case threads are in libc; should be tried before -Kthread and +# other compiler flags to prevent continual compiler warnings +# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) +# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) +# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) +# -pthreads: Solaris/gcc +# -mthreads: Mingw32/gcc, Lynx/gcc +# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it +# doesn't hurt to check since this sometimes defines pthreads too; +# also defines -D_REENTRANT) +# ... -mt is also the pthreads flag for HP/aCC +# pthread: Linux, etcetera +# --thread-safe: KAI C++ +# pthread-config: use pthread-config program (for GNU Pth library) + +case "${host_cpu}-${host_os}" in + *solaris*) + + # On Solaris (at least, for some versions), libc contains stubbed + # (non-functional) versions of the pthreads routines, so link-based + # tests will erroneously succeed. (We need to link with -pthreads/-mt/ + # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather + # a function called by this macro, so we could check for that, but + # who knows whether they'll stub that too in a future libc.) So, + # we'll just look for -pthreads and -lpthread first: + + ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" + ;; + + *-darwin*) + acx_pthread_flags="-pthread $acx_pthread_flags" + ;; +esac + +if test x"$ax_pthread_ok" = xno; then +for flag in $ax_pthread_flags; do + + case $flag in + none) + AC_MSG_CHECKING([whether pthreads work without any flags]) + ;; + + -*) + AC_MSG_CHECKING([whether pthreads work with $flag]) + PTHREAD_CFLAGS="$flag" + ;; + + pthread-config) + AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no) + if test x"$ax_pthread_config" = xno; then continue; fi + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; + + *) + AC_MSG_CHECKING([for the pthreads library -l$flag]) + PTHREAD_LIBS="-l$flag" + ;; + esac + + save_LIBS="$LIBS" + save_CFLAGS="$CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Check for various functions. We must include pthread.h, + # since some functions may be macros. (On the Sequent, we + # need a special flag -Kthread to make this header compile.) + # We check for pthread_join because it is in -lpthread on IRIX + # while pthread_create is in libc. We check for pthread_attr_init + # due to DEC craziness with -lpthreads. We check for + # pthread_cleanup_push because it is one of the few pthread + # functions on Solaris that doesn't have a non-functional libc stub. + # We try pthread_create on general principles. + AC_TRY_LINK([#include + static void routine(void* a) {a=0;} + static void* start_routine(void* a) {return a;}], + [pthread_t th; pthread_attr_t attr; + pthread_join(th, 0); + pthread_attr_init(&attr); + pthread_cleanup_push(routine, 0); + pthread_create(&th,0,start_routine,0); + pthread_cleanup_pop(0); ], + [ax_pthread_ok=yes]) + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + AC_MSG_RESULT($ax_pthread_ok) + if test "x$ax_pthread_ok" = xyes; then + break; + fi + + PTHREAD_LIBS="" + PTHREAD_CFLAGS="" +done +fi + +# Various other checks: +if test "x$ax_pthread_ok" = xyes; then + save_LIBS="$LIBS" + LIBS="$PTHREAD_LIBS $LIBS" + save_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + + # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. + AC_MSG_CHECKING([for joinable pthread attribute]) + attr_name=unknown + for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + AC_TRY_LINK([#include ], [int attr=$attr; return attr;], + [attr_name=$attr; break]) + done + AC_MSG_RESULT($attr_name) + if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then + AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, + [Define to necessary symbol if this constant + uses a non-standard name on your system.]) + fi + + AC_MSG_CHECKING([if more special flags are required for pthreads]) + flag=no + case "${host_cpu}-${host_os}" in + *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; + *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; + esac + AC_MSG_RESULT(${flag}) + if test "x$flag" != xno; then + PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" + fi + + LIBS="$save_LIBS" + CFLAGS="$save_CFLAGS" + + # More AIX lossage: must compile with xlc_r or cc_r + if test x"$GCC" != xyes; then + AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC}) + else + PTHREAD_CC=$CC + fi +else + PTHREAD_CC="$CC" +fi + +AC_SUBST(PTHREAD_LIBS) +AC_SUBST(PTHREAD_CFLAGS) +AC_SUBST(PTHREAD_CC) + +# Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: +if test x"$ax_pthread_ok" = xyes; then + ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) + : +else + ax_pthread_ok=no + $2 +fi +AC_LANG_RESTORE +])dnl AX_PTHREAD diff --git a/shared/sentry/external/breakpad/m4/ax_require_defined.m4 b/shared/sentry/external/breakpad/m4/ax_require_defined.m4 new file mode 100644 index 000000000..cae11112d --- /dev/null +++ b/shared/sentry/external/breakpad/m4/ax_require_defined.m4 @@ -0,0 +1,37 @@ +# =========================================================================== +# http://www.gnu.org/software/autoconf-archive/ax_require_defined.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_REQUIRE_DEFINED(MACRO) +# +# DESCRIPTION +# +# AX_REQUIRE_DEFINED is a simple helper for making sure other macros have +# been defined and thus are available for use. This avoids random issues +# where a macro isn't expanded. Instead the configure script emits a +# non-fatal: +# +# ./configure: line 1673: AX_CFLAGS_WARN_ALL: command not found +# +# It's like AC_REQUIRE except it doesn't expand the required macro. +# +# Here's an example: +# +# AX_REQUIRE_DEFINED([AX_CHECK_LINK_FLAG]) +# +# LICENSE +# +# Copyright (c) 2014 Mike Frysinger +# +# Copying and distribution of this file, with or without modification, are +# permitted in any medium without royalty provided the copyright notice +# and this notice are preserved. This file is offered as-is, without any +# warranty. + +#serial 1 + +AC_DEFUN([AX_REQUIRE_DEFINED], [dnl + m4_ifndef([$1], [m4_fatal([macro ]$1[ is not defined; is a m4 file missing?])]) +])dnl AX_REQUIRE_DEFINED diff --git a/shared/sentry/external/breakpad/m4/libtool.m4 b/shared/sentry/external/breakpad/m4/libtool.m4 new file mode 100644 index 000000000..a3fee5360 --- /dev/null +++ b/shared/sentry/external/breakpad/m4/libtool.m4 @@ -0,0 +1,7377 @@ +# libtool.m4 - Configure libtool for the host system. -*-Autoconf-*- +# +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, +# 2006, 2007, 2008 Free Software Foundation, Inc. +# Written by Gordon Matzigkeit, 1996 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +m4_define([_LT_COPYING], [dnl +# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, +# 2006, 2007, 2008 Free Software Foundation, Inc. +# Written by Gordon Matzigkeit, 1996 +# +# This file is part of GNU Libtool. +# +# GNU Libtool is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# As a special exception to the GNU General Public License, +# if you distribute this file as part of a program or library that +# is built using GNU Libtool, you may include this file under the +# same distribution terms that you use for the rest of that program. +# +# GNU Libtool is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GNU Libtool; see the file COPYING. If not, a copy +# can be downloaded from http://www.gnu.org/licenses/gpl.html, or +# obtained by writing to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +]) + +# serial 56 LT_INIT + + +# LT_PREREQ(VERSION) +# ------------------ +# Complain and exit if this libtool version is less that VERSION. +m4_defun([LT_PREREQ], +[m4_if(m4_version_compare(m4_defn([LT_PACKAGE_VERSION]), [$1]), -1, + [m4_default([$3], + [m4_fatal([Libtool version $1 or higher is required], + 63)])], + [$2])]) + + +# _LT_CHECK_BUILDDIR +# ------------------ +# Complain if the absolute build directory name contains unusual characters +m4_defun([_LT_CHECK_BUILDDIR], +[case `pwd` in + *\ * | *\ *) + AC_MSG_WARN([Libtool does not cope well with whitespace in `pwd`]) ;; +esac +]) + + +# LT_INIT([OPTIONS]) +# ------------------ +AC_DEFUN([LT_INIT], +[AC_PREREQ([2.58])dnl We use AC_INCLUDES_DEFAULT +AC_BEFORE([$0], [LT_LANG])dnl +AC_BEFORE([$0], [LT_OUTPUT])dnl +AC_BEFORE([$0], [LTDL_INIT])dnl +m4_require([_LT_CHECK_BUILDDIR])dnl + +dnl Autoconf doesn't catch unexpanded LT_ macros by default: +m4_pattern_forbid([^_?LT_[A-Z_]+$])dnl +m4_pattern_allow([^(_LT_EOF|LT_DLGLOBAL|LT_DLLAZY_OR_NOW|LT_MULTI_MODULE)$])dnl +dnl aclocal doesn't pull ltoptions.m4, ltsugar.m4, or ltversion.m4 +dnl unless we require an AC_DEFUNed macro: +AC_REQUIRE([LTOPTIONS_VERSION])dnl +AC_REQUIRE([LTSUGAR_VERSION])dnl +AC_REQUIRE([LTVERSION_VERSION])dnl +AC_REQUIRE([LTOBSOLETE_VERSION])dnl +m4_require([_LT_PROG_LTMAIN])dnl + +dnl Parse OPTIONS +_LT_SET_OPTIONS([$0], [$1]) + +# This can be used to rebuild libtool when needed +LIBTOOL_DEPS="$ltmain" + +# Always use our own libtool. +LIBTOOL='$(SHELL) $(top_builddir)/libtool' +AC_SUBST(LIBTOOL)dnl + +_LT_SETUP + +# Only expand once: +m4_define([LT_INIT]) +])# LT_INIT + +# Old names: +AU_ALIAS([AC_PROG_LIBTOOL], [LT_INIT]) +AU_ALIAS([AM_PROG_LIBTOOL], [LT_INIT]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_PROG_LIBTOOL], []) +dnl AC_DEFUN([AM_PROG_LIBTOOL], []) + + +# _LT_CC_BASENAME(CC) +# ------------------- +# Calculate cc_basename. Skip known compiler wrappers and cross-prefix. +m4_defun([_LT_CC_BASENAME], +[for cc_temp in $1""; do + case $cc_temp in + compile | *[[\\/]]compile | ccache | *[[\\/]]ccache ) ;; + distcc | *[[\\/]]distcc | purify | *[[\\/]]purify ) ;; + \-*) ;; + *) break;; + esac +done +cc_basename=`$ECHO "X$cc_temp" | $Xsed -e 's%.*/%%' -e "s%^$host_alias-%%"` +]) + + +# _LT_FILEUTILS_DEFAULTS +# ---------------------- +# It is okay to use these file commands and assume they have been set +# sensibly after `m4_require([_LT_FILEUTILS_DEFAULTS])'. +m4_defun([_LT_FILEUTILS_DEFAULTS], +[: ${CP="cp -f"} +: ${MV="mv -f"} +: ${RM="rm -f"} +])# _LT_FILEUTILS_DEFAULTS + + +# _LT_SETUP +# --------- +m4_defun([_LT_SETUP], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +_LT_DECL([], [host_alias], [0], [The host system])dnl +_LT_DECL([], [host], [0])dnl +_LT_DECL([], [host_os], [0])dnl +dnl +_LT_DECL([], [build_alias], [0], [The build system])dnl +_LT_DECL([], [build], [0])dnl +_LT_DECL([], [build_os], [0])dnl +dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([LT_PATH_LD])dnl +AC_REQUIRE([LT_PATH_NM])dnl +dnl +AC_REQUIRE([AC_PROG_LN_S])dnl +test -z "$LN_S" && LN_S="ln -s" +_LT_DECL([], [LN_S], [1], [Whether we need soft or hard links])dnl +dnl +AC_REQUIRE([LT_CMD_MAX_LEN])dnl +_LT_DECL([objext], [ac_objext], [0], [Object file suffix (normally "o")])dnl +_LT_DECL([], [exeext], [0], [Executable file suffix (normally "")])dnl +dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_CHECK_SHELL_FEATURES])dnl +m4_require([_LT_CMD_RELOAD])dnl +m4_require([_LT_CHECK_MAGIC_METHOD])dnl +m4_require([_LT_CMD_OLD_ARCHIVE])dnl +m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl + +_LT_CONFIG_LIBTOOL_INIT([ +# See if we are running on zsh, and set the options which allow our +# commands through without removal of \ escapes INIT. +if test -n "\${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi +]) +if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST +fi + +_LT_CHECK_OBJDIR + +m4_require([_LT_TAG_COMPILER])dnl +_LT_PROG_ECHO_BACKSLASH + +case $host_os in +aix3*) + # AIX sometimes has problems with the GCC collect2 program. For some + # reason, if we set the COLLECT_NAMES environment variable, the problems + # vanish in a puff of smoke. + if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES + fi + ;; +esac + +# Sed substitution that helps us do robust quoting. It backslashifies +# metacharacters that are still active within double-quoted strings. +sed_quote_subst='s/\([["`$\\]]\)/\\\1/g' + +# Same as above, but do not quote variable references. +double_quote_subst='s/\([["`\\]]\)/\\\1/g' + +# Sed substitution to delay expansion of an escaped shell variable in a +# double_quote_subst'ed string. +delay_variable_subst='s/\\\\\\\\\\\$/\\\\\\$/g' + +# Sed substitution to delay expansion of an escaped single quote. +delay_single_quote_subst='s/'\''/'\'\\\\\\\'\''/g' + +# Sed substitution to avoid accidental globbing in evaled expressions +no_glob_subst='s/\*/\\\*/g' + +# Global variables: +ofile=libtool +can_build_shared=yes + +# All known linkers require a `.a' archive for static linking (except MSVC, +# which needs '.lib'). +libext=a + +with_gnu_ld="$lt_cv_prog_gnu_ld" + +old_CC="$CC" +old_CFLAGS="$CFLAGS" + +# Set sane defaults for various variables +test -z "$CC" && CC=cc +test -z "$LTCC" && LTCC=$CC +test -z "$LTCFLAGS" && LTCFLAGS=$CFLAGS +test -z "$LD" && LD=ld +test -z "$ac_objext" && ac_objext=o + +_LT_CC_BASENAME([$compiler]) + +# Only perform the check for file, if the check method requires it +test -z "$MAGIC_CMD" && MAGIC_CMD=file +case $deplibs_check_method in +file_magic*) + if test "$file_magic_cmd" = '$MAGIC_CMD'; then + _LT_PATH_MAGIC + fi + ;; +esac + +# Use C for the default configuration in the libtool script +LT_SUPPORTED_TAG([CC]) +_LT_LANG_C_CONFIG +_LT_LANG_DEFAULT_CONFIG +_LT_CONFIG_COMMANDS +])# _LT_SETUP + + +# _LT_PROG_LTMAIN +# --------------- +# Note that this code is called both from `configure', and `config.status' +# now that we use AC_CONFIG_COMMANDS to generate libtool. Notably, +# `config.status' has no value for ac_aux_dir unless we are using Automake, +# so we pass a copy along to make sure it has a sensible value anyway. +m4_defun([_LT_PROG_LTMAIN], +[m4_ifdef([AC_REQUIRE_AUX_FILE], [AC_REQUIRE_AUX_FILE([ltmain.sh])])dnl +_LT_CONFIG_LIBTOOL_INIT([ac_aux_dir='$ac_aux_dir']) +ltmain="$ac_aux_dir/ltmain.sh" +])# _LT_PROG_LTMAIN + + +## ------------------------------------- ## +## Accumulate code for creating libtool. ## +## ------------------------------------- ## + +# So that we can recreate a full libtool script including additional +# tags, we accumulate the chunks of code to send to AC_CONFIG_COMMANDS +# in macros and then make a single call at the end using the `libtool' +# label. + + +# _LT_CONFIG_LIBTOOL_INIT([INIT-COMMANDS]) +# ---------------------------------------- +# Register INIT-COMMANDS to be passed to AC_CONFIG_COMMANDS later. +m4_define([_LT_CONFIG_LIBTOOL_INIT], +[m4_ifval([$1], + [m4_append([_LT_OUTPUT_LIBTOOL_INIT], + [$1 +])])]) + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_INIT]) + + +# _LT_CONFIG_LIBTOOL([COMMANDS]) +# ------------------------------ +# Register COMMANDS to be passed to AC_CONFIG_COMMANDS later. +m4_define([_LT_CONFIG_LIBTOOL], +[m4_ifval([$1], + [m4_append([_LT_OUTPUT_LIBTOOL_COMMANDS], + [$1 +])])]) + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS]) + + +# _LT_CONFIG_SAVE_COMMANDS([COMMANDS], [INIT_COMMANDS]) +# ----------------------------------------------------- +m4_defun([_LT_CONFIG_SAVE_COMMANDS], +[_LT_CONFIG_LIBTOOL([$1]) +_LT_CONFIG_LIBTOOL_INIT([$2]) +]) + + +# _LT_FORMAT_COMMENT([COMMENT]) +# ----------------------------- +# Add leading comment marks to the start of each line, and a trailing +# full-stop to the whole comment if one is not present already. +m4_define([_LT_FORMAT_COMMENT], +[m4_ifval([$1], [ +m4_bpatsubst([m4_bpatsubst([$1], [^ *], [# ])], + [['`$\]], [\\\&])]m4_bmatch([$1], [[!?.]$], [], [.]) +)]) + + + +## ------------------------ ## +## FIXME: Eliminate VARNAME ## +## ------------------------ ## + + +# _LT_DECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION], [IS-TAGGED?]) +# ------------------------------------------------------------------- +# CONFIGNAME is the name given to the value in the libtool script. +# VARNAME is the (base) name used in the configure script. +# VALUE may be 0, 1 or 2 for a computed quote escaped value based on +# VARNAME. Any other value will be used directly. +m4_define([_LT_DECL], +[lt_if_append_uniq([lt_decl_varnames], [$2], [, ], + [lt_dict_add_subkey([lt_decl_dict], [$2], [libtool_name], + [m4_ifval([$1], [$1], [$2])]) + lt_dict_add_subkey([lt_decl_dict], [$2], [value], [$3]) + m4_ifval([$4], + [lt_dict_add_subkey([lt_decl_dict], [$2], [description], [$4])]) + lt_dict_add_subkey([lt_decl_dict], [$2], + [tagged?], [m4_ifval([$5], [yes], [no])])]) +]) + + +# _LT_TAGDECL([CONFIGNAME], VARNAME, VALUE, [DESCRIPTION]) +# -------------------------------------------------------- +m4_define([_LT_TAGDECL], [_LT_DECL([$1], [$2], [$3], [$4], [yes])]) + + +# lt_decl_tag_varnames([SEPARATOR], [VARNAME1...]) +# ------------------------------------------------ +m4_define([lt_decl_tag_varnames], +[_lt_decl_filter([tagged?], [yes], $@)]) + + +# _lt_decl_filter(SUBKEY, VALUE, [SEPARATOR], [VARNAME1..]) +# --------------------------------------------------------- +m4_define([_lt_decl_filter], +[m4_case([$#], + [0], [m4_fatal([$0: too few arguments: $#])], + [1], [m4_fatal([$0: too few arguments: $#: $1])], + [2], [lt_dict_filter([lt_decl_dict], [$1], [$2], [], lt_decl_varnames)], + [3], [lt_dict_filter([lt_decl_dict], [$1], [$2], [$3], lt_decl_varnames)], + [lt_dict_filter([lt_decl_dict], $@)])[]dnl +]) + + +# lt_decl_quote_varnames([SEPARATOR], [VARNAME1...]) +# -------------------------------------------------- +m4_define([lt_decl_quote_varnames], +[_lt_decl_filter([value], [1], $@)]) + + +# lt_decl_dquote_varnames([SEPARATOR], [VARNAME1...]) +# --------------------------------------------------- +m4_define([lt_decl_dquote_varnames], +[_lt_decl_filter([value], [2], $@)]) + + +# lt_decl_varnames_tagged([SEPARATOR], [VARNAME1...]) +# --------------------------------------------------- +m4_define([lt_decl_varnames_tagged], +[m4_assert([$# <= 2])dnl +_$0(m4_quote(m4_default([$1], [[, ]])), + m4_ifval([$2], [[$2]], [m4_dquote(lt_decl_tag_varnames)]), + m4_split(m4_normalize(m4_quote(_LT_TAGS)), [ ]))]) +m4_define([_lt_decl_varnames_tagged], +[m4_ifval([$3], [lt_combine([$1], [$2], [_], $3)])]) + + +# lt_decl_all_varnames([SEPARATOR], [VARNAME1...]) +# ------------------------------------------------ +m4_define([lt_decl_all_varnames], +[_$0(m4_quote(m4_default([$1], [[, ]])), + m4_if([$2], [], + m4_quote(lt_decl_varnames), + m4_quote(m4_shift($@))))[]dnl +]) +m4_define([_lt_decl_all_varnames], +[lt_join($@, lt_decl_varnames_tagged([$1], + lt_decl_tag_varnames([[, ]], m4_shift($@))))dnl +]) + + +# _LT_CONFIG_STATUS_DECLARE([VARNAME]) +# ------------------------------------ +# Quote a variable value, and forward it to `config.status' so that its +# declaration there will have the same value as in `configure'. VARNAME +# must have a single quote delimited value for this to work. +m4_define([_LT_CONFIG_STATUS_DECLARE], +[$1='`$ECHO "X$][$1" | $Xsed -e "$delay_single_quote_subst"`']) + + +# _LT_CONFIG_STATUS_DECLARATIONS +# ------------------------------ +# We delimit libtool config variables with single quotes, so when +# we write them to config.status, we have to be sure to quote all +# embedded single quotes properly. In configure, this macro expands +# each variable declared with _LT_DECL (and _LT_TAGDECL) into: +# +# ='`$ECHO "X$" | $Xsed -e "$delay_single_quote_subst"`' +m4_defun([_LT_CONFIG_STATUS_DECLARATIONS], +[m4_foreach([_lt_var], m4_quote(lt_decl_all_varnames), + [m4_n([_LT_CONFIG_STATUS_DECLARE(_lt_var)])])]) + + +# _LT_LIBTOOL_TAGS +# ---------------- +# Output comment and list of tags supported by the script +m4_defun([_LT_LIBTOOL_TAGS], +[_LT_FORMAT_COMMENT([The names of the tagged configurations supported by this script])dnl +available_tags="_LT_TAGS"dnl +]) + + +# _LT_LIBTOOL_DECLARE(VARNAME, [TAG]) +# ----------------------------------- +# Extract the dictionary values for VARNAME (optionally with TAG) and +# expand to a commented shell variable setting: +# +# # Some comment about what VAR is for. +# visible_name=$lt_internal_name +m4_define([_LT_LIBTOOL_DECLARE], +[_LT_FORMAT_COMMENT(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], + [description])))[]dnl +m4_pushdef([_libtool_name], + m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [libtool_name])))[]dnl +m4_case(m4_quote(lt_dict_fetch([lt_decl_dict], [$1], [value])), + [0], [_libtool_name=[$]$1], + [1], [_libtool_name=$lt_[]$1], + [2], [_libtool_name=$lt_[]$1], + [_libtool_name=lt_dict_fetch([lt_decl_dict], [$1], [value])])[]dnl +m4_ifval([$2], [_$2])[]m4_popdef([_libtool_name])[]dnl +]) + + +# _LT_LIBTOOL_CONFIG_VARS +# ----------------------- +# Produce commented declarations of non-tagged libtool config variables +# suitable for insertion in the LIBTOOL CONFIG section of the `libtool' +# script. Tagged libtool config variables (even for the LIBTOOL CONFIG +# section) are produced by _LT_LIBTOOL_TAG_VARS. +m4_defun([_LT_LIBTOOL_CONFIG_VARS], +[m4_foreach([_lt_var], + m4_quote(_lt_decl_filter([tagged?], [no], [], lt_decl_varnames)), + [m4_n([_LT_LIBTOOL_DECLARE(_lt_var)])])]) + + +# _LT_LIBTOOL_TAG_VARS(TAG) +# ------------------------- +m4_define([_LT_LIBTOOL_TAG_VARS], +[m4_foreach([_lt_var], m4_quote(lt_decl_tag_varnames), + [m4_n([_LT_LIBTOOL_DECLARE(_lt_var, [$1])])])]) + + +# _LT_TAGVAR(VARNAME, [TAGNAME]) +# ------------------------------ +m4_define([_LT_TAGVAR], [m4_ifval([$2], [$1_$2], [$1])]) + + +# _LT_CONFIG_COMMANDS +# ------------------- +# Send accumulated output to $CONFIG_STATUS. Thanks to the lists of +# variables for single and double quote escaping we saved from calls +# to _LT_DECL, we can put quote escaped variables declarations +# into `config.status', and then the shell code to quote escape them in +# for loops in `config.status'. Finally, any additional code accumulated +# from calls to _LT_CONFIG_LIBTOOL_INIT is expanded. +m4_defun([_LT_CONFIG_COMMANDS], +[AC_PROVIDE_IFELSE([LT_OUTPUT], + dnl If the libtool generation code has been placed in $CONFIG_LT, + dnl instead of duplicating it all over again into config.status, + dnl then we will have config.status run $CONFIG_LT later, so it + dnl needs to know what name is stored there: + [AC_CONFIG_COMMANDS([libtool], + [$SHELL $CONFIG_LT || AS_EXIT(1)], [CONFIG_LT='$CONFIG_LT'])], + dnl If the libtool generation code is destined for config.status, + dnl expand the accumulated commands and init code now: + [AC_CONFIG_COMMANDS([libtool], + [_LT_OUTPUT_LIBTOOL_COMMANDS], [_LT_OUTPUT_LIBTOOL_COMMANDS_INIT])]) +])#_LT_CONFIG_COMMANDS + + +# Initialize. +m4_define([_LT_OUTPUT_LIBTOOL_COMMANDS_INIT], +[ + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +sed_quote_subst='$sed_quote_subst' +double_quote_subst='$double_quote_subst' +delay_variable_subst='$delay_variable_subst' +_LT_CONFIG_STATUS_DECLARATIONS +LTCC='$LTCC' +LTCFLAGS='$LTCFLAGS' +compiler='$compiler_DEFAULT' + +# Quote evaled strings. +for var in lt_decl_all_varnames([[ \ +]], lt_decl_quote_varnames); do + case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in + *[[\\\\\\\`\\"\\\$]]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$sed_quote_subst\\"\\\`\\\\\\"" + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +# Double-quote double-evaled strings. +for var in lt_decl_all_varnames([[ \ +]], lt_decl_dquote_varnames); do + case \`eval \\\\\$ECHO "X\\\\\$\$var"\` in + *[[\\\\\\\`\\"\\\$]]*) + eval "lt_\$var=\\\\\\"\\\`\\\$ECHO \\"X\\\$\$var\\" | \\\$Xsed -e \\"\\\$double_quote_subst\\" -e \\"\\\$sed_quote_subst\\" -e \\"\\\$delay_variable_subst\\"\\\`\\\\\\"" + ;; + *) + eval "lt_\$var=\\\\\\"\\\$\$var\\\\\\"" + ;; + esac +done + +# Fix-up fallback echo if it was mangled by the above quoting rules. +case \$lt_ECHO in +*'\\\[$]0 --fallback-echo"')dnl " + lt_ECHO=\`\$ECHO "X\$lt_ECHO" | \$Xsed -e 's/\\\\\\\\\\\\\\\[$]0 --fallback-echo"\[$]/\[$]0 --fallback-echo"/'\` + ;; +esac + +_LT_OUTPUT_LIBTOOL_INIT +]) + + +# LT_OUTPUT +# --------- +# This macro allows early generation of the libtool script (before +# AC_OUTPUT is called), incase it is used in configure for compilation +# tests. +AC_DEFUN([LT_OUTPUT], +[: ${CONFIG_LT=./config.lt} +AC_MSG_NOTICE([creating $CONFIG_LT]) +cat >"$CONFIG_LT" <<_LTEOF +#! $SHELL +# Generated by $as_me. +# Run this file to recreate a libtool stub with the current configuration. + +lt_cl_silent=false +SHELL=\${CONFIG_SHELL-$SHELL} +_LTEOF + +cat >>"$CONFIG_LT" <<\_LTEOF +AS_SHELL_SANITIZE +_AS_PREPARE + +exec AS_MESSAGE_FD>&1 +exec AS_MESSAGE_LOG_FD>>config.log +{ + echo + AS_BOX([Running $as_me.]) +} >&AS_MESSAGE_LOG_FD + +lt_cl_help="\ +\`$as_me' creates a local libtool stub from the current configuration, +for use in further configure time tests before the real libtool is +generated. + +Usage: $[0] [[OPTIONS]] + + -h, --help print this help, then exit + -V, --version print version number, then exit + -q, --quiet do not print progress messages + -d, --debug don't remove temporary files + +Report bugs to ." + +lt_cl_version="\ +m4_ifset([AC_PACKAGE_NAME], [AC_PACKAGE_NAME ])config.lt[]dnl +m4_ifset([AC_PACKAGE_VERSION], [ AC_PACKAGE_VERSION]) +configured by $[0], generated by m4_PACKAGE_STRING. + +Copyright (C) 2008 Free Software Foundation, Inc. +This config.lt script is free software; the Free Software Foundation +gives unlimited permision to copy, distribute and modify it." + +while test $[#] != 0 +do + case $[1] in + --version | --v* | -V ) + echo "$lt_cl_version"; exit 0 ;; + --help | --h* | -h ) + echo "$lt_cl_help"; exit 0 ;; + --debug | --d* | -d ) + debug=: ;; + --quiet | --q* | --silent | --s* | -q ) + lt_cl_silent=: ;; + + -*) AC_MSG_ERROR([unrecognized option: $[1] +Try \`$[0] --help' for more information.]) ;; + + *) AC_MSG_ERROR([unrecognized argument: $[1] +Try \`$[0] --help' for more information.]) ;; + esac + shift +done + +if $lt_cl_silent; then + exec AS_MESSAGE_FD>/dev/null +fi +_LTEOF + +cat >>"$CONFIG_LT" <<_LTEOF +_LT_OUTPUT_LIBTOOL_COMMANDS_INIT +_LTEOF + +cat >>"$CONFIG_LT" <<\_LTEOF +AC_MSG_NOTICE([creating $ofile]) +_LT_OUTPUT_LIBTOOL_COMMANDS +AS_EXIT(0) +_LTEOF +chmod +x "$CONFIG_LT" + +# configure is writing to config.log, but config.lt does its own redirection, +# appending to config.log, which fails on DOS, as config.log is still kept +# open by configure. Here we exec the FD to /dev/null, effectively closing +# config.log, so it can be properly (re)opened and appended to by config.lt. +if test "$no_create" != yes; then + lt_cl_success=: + test "$silent" = yes && + lt_config_lt_args="$lt_config_lt_args --quiet" + exec AS_MESSAGE_LOG_FD>/dev/null + $SHELL "$CONFIG_LT" $lt_config_lt_args || lt_cl_success=false + exec AS_MESSAGE_LOG_FD>>config.log + $lt_cl_success || AS_EXIT(1) +fi +])# LT_OUTPUT + + +# _LT_CONFIG(TAG) +# --------------- +# If TAG is the built-in tag, create an initial libtool script with a +# default configuration from the untagged config vars. Otherwise add code +# to config.status for appending the configuration named by TAG from the +# matching tagged config vars. +m4_defun([_LT_CONFIG], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +_LT_CONFIG_SAVE_COMMANDS([ + m4_define([_LT_TAG], m4_if([$1], [], [C], [$1]))dnl + m4_if(_LT_TAG, [C], [ + # See if we are running on zsh, and set the options which allow our + # commands through without removal of \ escapes. + if test -n "${ZSH_VERSION+set}" ; then + setopt NO_GLOB_SUBST + fi + + cfgfile="${ofile}T" + trap "$RM \"$cfgfile\"; exit 1" 1 2 15 + $RM "$cfgfile" + + cat <<_LT_EOF >> "$cfgfile" +#! $SHELL + +# `$ECHO "$ofile" | sed 's%^.*/%%'` - Provide generalized library-building support services. +# Generated automatically by $as_me ($PACKAGE$TIMESTAMP) $VERSION +# Libtool was configured on host `(hostname || uname -n) 2>/dev/null | sed 1q`: +# NOTE: Changes made to this file will be lost: look at ltmain.sh. +# +_LT_COPYING +_LT_LIBTOOL_TAGS + +# ### BEGIN LIBTOOL CONFIG +_LT_LIBTOOL_CONFIG_VARS +_LT_LIBTOOL_TAG_VARS +# ### END LIBTOOL CONFIG + +_LT_EOF + + case $host_os in + aix3*) + cat <<\_LT_EOF >> "$cfgfile" +# AIX sometimes has problems with the GCC collect2 program. For some +# reason, if we set the COLLECT_NAMES environment variable, the problems +# vanish in a puff of smoke. +if test "X${COLLECT_NAMES+set}" != Xset; then + COLLECT_NAMES= + export COLLECT_NAMES +fi +_LT_EOF + ;; + esac + + _LT_PROG_LTMAIN + + # We use sed instead of cat because bash on DJGPP gets confused if + # if finds mixed CR/LF and LF-only lines. Since sed operates in + # text mode, it properly converts lines to CR/LF. This bash problem + # is reportedly fixed, but why not run on old versions too? + sed '/^# Generated shell functions inserted here/q' "$ltmain" >> "$cfgfile" \ + || (rm -f "$cfgfile"; exit 1) + + _LT_PROG_XSI_SHELLFNS + + sed -n '/^# Generated shell functions inserted here/,$p' "$ltmain" >> "$cfgfile" \ + || (rm -f "$cfgfile"; exit 1) + + mv -f "$cfgfile" "$ofile" || + (rm -f "$ofile" && cp "$cfgfile" "$ofile" && rm -f "$cfgfile") + chmod +x "$ofile" +], +[cat <<_LT_EOF >> "$ofile" + +dnl Unfortunately we have to use $1 here, since _LT_TAG is not expanded +dnl in a comment (ie after a #). +# ### BEGIN LIBTOOL TAG CONFIG: $1 +_LT_LIBTOOL_TAG_VARS(_LT_TAG) +# ### END LIBTOOL TAG CONFIG: $1 +_LT_EOF +])dnl /m4_if +], +[m4_if([$1], [], [ + PACKAGE='$PACKAGE' + VERSION='$VERSION' + TIMESTAMP='$TIMESTAMP' + RM='$RM' + ofile='$ofile'], []) +])dnl /_LT_CONFIG_SAVE_COMMANDS +])# _LT_CONFIG + + +# LT_SUPPORTED_TAG(TAG) +# --------------------- +# Trace this macro to discover what tags are supported by the libtool +# --tag option, using: +# autoconf --trace 'LT_SUPPORTED_TAG:$1' +AC_DEFUN([LT_SUPPORTED_TAG], []) + + +# C support is built-in for now +m4_define([_LT_LANG_C_enabled], []) +m4_define([_LT_TAGS], []) + + +# LT_LANG(LANG) +# ------------- +# Enable libtool support for the given language if not already enabled. +AC_DEFUN([LT_LANG], +[AC_BEFORE([$0], [LT_OUTPUT])dnl +m4_case([$1], + [C], [_LT_LANG(C)], + [C++], [_LT_LANG(CXX)], + [Java], [_LT_LANG(GCJ)], + [Fortran 77], [_LT_LANG(F77)], + [Fortran], [_LT_LANG(FC)], + [Windows Resource], [_LT_LANG(RC)], + [m4_ifdef([_LT_LANG_]$1[_CONFIG], + [_LT_LANG($1)], + [m4_fatal([$0: unsupported language: "$1"])])])dnl +])# LT_LANG + + +# _LT_LANG(LANGNAME) +# ------------------ +m4_defun([_LT_LANG], +[m4_ifdef([_LT_LANG_]$1[_enabled], [], + [LT_SUPPORTED_TAG([$1])dnl + m4_append([_LT_TAGS], [$1 ])dnl + m4_define([_LT_LANG_]$1[_enabled], [])dnl + _LT_LANG_$1_CONFIG($1)])dnl +])# _LT_LANG + + +# _LT_LANG_DEFAULT_CONFIG +# ----------------------- +m4_defun([_LT_LANG_DEFAULT_CONFIG], +[AC_PROVIDE_IFELSE([AC_PROG_CXX], + [LT_LANG(CXX)], + [m4_define([AC_PROG_CXX], defn([AC_PROG_CXX])[LT_LANG(CXX)])]) + +AC_PROVIDE_IFELSE([AC_PROG_F77], + [LT_LANG(F77)], + [m4_define([AC_PROG_F77], defn([AC_PROG_F77])[LT_LANG(F77)])]) + +AC_PROVIDE_IFELSE([AC_PROG_FC], + [LT_LANG(FC)], + [m4_define([AC_PROG_FC], defn([AC_PROG_FC])[LT_LANG(FC)])]) + +dnl The call to [A][M_PROG_GCJ] is quoted like that to stop aclocal +dnl pulling things in needlessly. +AC_PROVIDE_IFELSE([AC_PROG_GCJ], + [LT_LANG(GCJ)], + [AC_PROVIDE_IFELSE([A][M_PROG_GCJ], + [LT_LANG(GCJ)], + [AC_PROVIDE_IFELSE([LT_PROG_GCJ], + [LT_LANG(GCJ)], + [m4_ifdef([AC_PROG_GCJ], + [m4_define([AC_PROG_GCJ], defn([AC_PROG_GCJ])[LT_LANG(GCJ)])]) + m4_ifdef([A][M_PROG_GCJ], + [m4_define([A][M_PROG_GCJ], defn([A][M_PROG_GCJ])[LT_LANG(GCJ)])]) + m4_ifdef([LT_PROG_GCJ], + [m4_define([LT_PROG_GCJ], defn([LT_PROG_GCJ])[LT_LANG(GCJ)])])])])]) + +AC_PROVIDE_IFELSE([LT_PROG_RC], + [LT_LANG(RC)], + [m4_define([LT_PROG_RC], defn([LT_PROG_RC])[LT_LANG(RC)])]) +])# _LT_LANG_DEFAULT_CONFIG + +# Obsolete macros: +AU_DEFUN([AC_LIBTOOL_CXX], [LT_LANG(C++)]) +AU_DEFUN([AC_LIBTOOL_F77], [LT_LANG(Fortran 77)]) +AU_DEFUN([AC_LIBTOOL_FC], [LT_LANG(Fortran)]) +AU_DEFUN([AC_LIBTOOL_GCJ], [LT_LANG(Java)]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_CXX], []) +dnl AC_DEFUN([AC_LIBTOOL_F77], []) +dnl AC_DEFUN([AC_LIBTOOL_FC], []) +dnl AC_DEFUN([AC_LIBTOOL_GCJ], []) + + +# _LT_TAG_COMPILER +# ---------------- +m4_defun([_LT_TAG_COMPILER], +[AC_REQUIRE([AC_PROG_CC])dnl + +_LT_DECL([LTCC], [CC], [1], [A C compiler])dnl +_LT_DECL([LTCFLAGS], [CFLAGS], [1], [LTCC compiler flags])dnl +_LT_TAGDECL([CC], [compiler], [1], [A language specific compiler])dnl +_LT_TAGDECL([with_gcc], [GCC], [0], [Is the compiler the GNU compiler?])dnl + +# If no C compiler was specified, use CC. +LTCC=${LTCC-"$CC"} + +# If no C compiler flags were specified, use CFLAGS. +LTCFLAGS=${LTCFLAGS-"$CFLAGS"} + +# Allow CC to be a program name with arguments. +compiler=$CC +])# _LT_TAG_COMPILER + + +# _LT_COMPILER_BOILERPLATE +# ------------------------ +# Check for compiler boilerplate output or warnings with +# the simple compiler test code. +m4_defun([_LT_COMPILER_BOILERPLATE], +[m4_require([_LT_DECL_SED])dnl +ac_outfile=conftest.$ac_objext +echo "$lt_simple_compile_test_code" >conftest.$ac_ext +eval "$ac_compile" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_compiler_boilerplate=`cat conftest.err` +$RM conftest* +])# _LT_COMPILER_BOILERPLATE + + +# _LT_LINKER_BOILERPLATE +# ---------------------- +# Check for linker boilerplate output or warnings with +# the simple link test code. +m4_defun([_LT_LINKER_BOILERPLATE], +[m4_require([_LT_DECL_SED])dnl +ac_outfile=conftest.$ac_objext +echo "$lt_simple_link_test_code" >conftest.$ac_ext +eval "$ac_link" 2>&1 >/dev/null | $SED '/^$/d; /^ *+/d' >conftest.err +_lt_linker_boilerplate=`cat conftest.err` +$RM -r conftest* +])# _LT_LINKER_BOILERPLATE + +# _LT_REQUIRED_DARWIN_CHECKS +# ------------------------- +m4_defun_once([_LT_REQUIRED_DARWIN_CHECKS],[ + case $host_os in + rhapsody* | darwin*) + AC_CHECK_TOOL([DSYMUTIL], [dsymutil], [:]) + AC_CHECK_TOOL([NMEDIT], [nmedit], [:]) + AC_CHECK_TOOL([LIPO], [lipo], [:]) + AC_CHECK_TOOL([OTOOL], [otool], [:]) + AC_CHECK_TOOL([OTOOL64], [otool64], [:]) + _LT_DECL([], [DSYMUTIL], [1], + [Tool to manipulate archived DWARF debug symbol files on Mac OS X]) + _LT_DECL([], [NMEDIT], [1], + [Tool to change global to local symbols on Mac OS X]) + _LT_DECL([], [LIPO], [1], + [Tool to manipulate fat objects and archives on Mac OS X]) + _LT_DECL([], [OTOOL], [1], + [ldd/readelf like tool for Mach-O binaries on Mac OS X]) + _LT_DECL([], [OTOOL64], [1], + [ldd/readelf like tool for 64 bit Mach-O binaries on Mac OS X 10.4]) + + AC_CACHE_CHECK([for -single_module linker flag],[lt_cv_apple_cc_single_mod], + [lt_cv_apple_cc_single_mod=no + if test -z "${LT_MULTI_MODULE}"; then + # By default we will add the -single_module flag. You can override + # by either setting the environment variable LT_MULTI_MODULE + # non-empty at configure time, or by adding -multi_module to the + # link flags. + rm -rf libconftest.dylib* + echo "int foo(void){return 1;}" > conftest.c + echo "$LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ +-dynamiclib -Wl,-single_module conftest.c" >&AS_MESSAGE_LOG_FD + $LTCC $LTCFLAGS $LDFLAGS -o libconftest.dylib \ + -dynamiclib -Wl,-single_module conftest.c 2>conftest.err + _lt_result=$? + if test -f libconftest.dylib && test ! -s conftest.err && test $_lt_result = 0; then + lt_cv_apple_cc_single_mod=yes + else + cat conftest.err >&AS_MESSAGE_LOG_FD + fi + rm -rf libconftest.dylib* + rm -f conftest.* + fi]) + AC_CACHE_CHECK([for -exported_symbols_list linker flag], + [lt_cv_ld_exported_symbols_list], + [lt_cv_ld_exported_symbols_list=no + save_LDFLAGS=$LDFLAGS + echo "_main" > conftest.sym + LDFLAGS="$LDFLAGS -Wl,-exported_symbols_list,conftest.sym" + AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], + [lt_cv_ld_exported_symbols_list=yes], + [lt_cv_ld_exported_symbols_list=no]) + LDFLAGS="$save_LDFLAGS" + ]) + case $host_os in + rhapsody* | darwin1.[[012]]) + _lt_dar_allow_undefined='${wl}-undefined ${wl}suppress' ;; + darwin1.*) + _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; + darwin*) # darwin 5.x on + # if running on 10.5 or later, the deployment target defaults + # to the OS version, if on x86, and 10.4, the deployment + # target defaults to 10.4. Don't you love it? + case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in + 10.0,*86*-darwin8*|10.0,*-darwin[[91]]*) + _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; + 10.[[012]]*) + _lt_dar_allow_undefined='${wl}-flat_namespace ${wl}-undefined ${wl}suppress' ;; + 10.*) + _lt_dar_allow_undefined='${wl}-undefined ${wl}dynamic_lookup' ;; + esac + ;; + esac + if test "$lt_cv_apple_cc_single_mod" = "yes"; then + _lt_dar_single_mod='$single_module' + fi + if test "$lt_cv_ld_exported_symbols_list" = "yes"; then + _lt_dar_export_syms=' ${wl}-exported_symbols_list,$output_objdir/${libname}-symbols.expsym' + else + _lt_dar_export_syms='~$NMEDIT -s $output_objdir/${libname}-symbols.expsym ${lib}' + fi + if test "$DSYMUTIL" != ":"; then + _lt_dsymutil='~$DSYMUTIL $lib || :' + else + _lt_dsymutil= + fi + ;; + esac +]) + + +# _LT_DARWIN_LINKER_FEATURES +# -------------------------- +# Checks for linker and compiler features on darwin +m4_defun([_LT_DARWIN_LINKER_FEATURES], +[ + m4_require([_LT_REQUIRED_DARWIN_CHECKS]) + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_automatic, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + _LT_TAGVAR(whole_archive_flag_spec, $1)='' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)="$_lt_dar_allow_undefined" + case $cc_basename in + ifort*) _lt_dar_can_shared=yes ;; + *) _lt_dar_can_shared=$GCC ;; + esac + if test "$_lt_dar_can_shared" = "yes"; then + output_verbose_link_cmd=echo + _LT_TAGVAR(archive_cmds, $1)="\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring $_lt_dar_single_mod${_lt_dsymutil}" + _LT_TAGVAR(module_cmds, $1)="\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dsymutil}" + _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \$libobjs \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring ${_lt_dar_single_mod}${_lt_dar_export_syms}${_lt_dsymutil}" + _LT_TAGVAR(module_expsym_cmds, $1)="sed -e 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC \$allow_undefined_flag -o \$lib -bundle \$libobjs \$deplibs \$compiler_flags${_lt_dar_export_syms}${_lt_dsymutil}" + m4_if([$1], [CXX], +[ if test "$lt_cv_apple_cc_single_mod" != "yes"; then + _LT_TAGVAR(archive_cmds, $1)="\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dsymutil}" + _LT_TAGVAR(archive_expsym_cmds, $1)="sed 's,^,_,' < \$export_symbols > \$output_objdir/\${libname}-symbols.expsym~\$CC -r -keep_private_externs -nostdlib -o \${lib}-master.o \$libobjs~\$CC -dynamiclib \$allow_undefined_flag -o \$lib \${lib}-master.o \$deplibs \$compiler_flags -install_name \$rpath/\$soname \$verstring${_lt_dar_export_syms}${_lt_dsymutil}" + fi +],[]) + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi +]) + +# _LT_SYS_MODULE_PATH_AIX +# ----------------------- +# Links a minimal program and checks the executable +# for the system default hardcoded library path. In most cases, +# this is /usr/lib:/lib, but when the MPI compilers are used +# the location of the communication and MPI libs are included too. +# If we don't find anything, use the default library path according +# to the aix ld manual. +m4_defun([_LT_SYS_MODULE_PATH_AIX], +[m4_require([_LT_DECL_SED])dnl +AC_LINK_IFELSE(AC_LANG_PROGRAM,[ +lt_aix_libpath_sed=' + /Import File Strings/,/^$/ { + /^0/ { + s/^0 *\(.*\)$/\1/ + p + } + }' +aix_libpath=`dump -H conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` +# Check for a 64-bit object if we didn't find anything. +if test -z "$aix_libpath"; then + aix_libpath=`dump -HX64 conftest$ac_exeext 2>/dev/null | $SED -n -e "$lt_aix_libpath_sed"` +fi],[]) +if test -z "$aix_libpath"; then aix_libpath="/usr/lib:/lib"; fi +])# _LT_SYS_MODULE_PATH_AIX + + +# _LT_SHELL_INIT(ARG) +# ------------------- +m4_define([_LT_SHELL_INIT], +[ifdef([AC_DIVERSION_NOTICE], + [AC_DIVERT_PUSH(AC_DIVERSION_NOTICE)], + [AC_DIVERT_PUSH(NOTICE)]) +$1 +AC_DIVERT_POP +])# _LT_SHELL_INIT + + +# _LT_PROG_ECHO_BACKSLASH +# ----------------------- +# Add some code to the start of the generated configure script which +# will find an echo command which doesn't interpret backslashes. +m4_defun([_LT_PROG_ECHO_BACKSLASH], +[_LT_SHELL_INIT([ +# Check that we are running under the correct shell. +SHELL=${CONFIG_SHELL-/bin/sh} + +case X$lt_ECHO in +X*--fallback-echo) + # Remove one level of quotation (which was required for Make). + ECHO=`echo "$lt_ECHO" | sed 's,\\\\\[$]\\[$]0,'[$]0','` + ;; +esac + +ECHO=${lt_ECHO-echo} +if test "X[$]1" = X--no-reexec; then + # Discard the --no-reexec flag, and continue. + shift +elif test "X[$]1" = X--fallback-echo; then + # Avoid inline document here, it may be left over + : +elif test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' ; then + # Yippee, $ECHO works! + : +else + # Restart under the correct shell. + exec $SHELL "[$]0" --no-reexec ${1+"[$]@"} +fi + +if test "X[$]1" = X--fallback-echo; then + # used as fallback echo + shift + cat <<_LT_EOF +[$]* +_LT_EOF + exit 0 +fi + +# The HP-UX ksh and POSIX shell print the target directory to stdout +# if CDPATH is set. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +if test -z "$lt_ECHO"; then + if test "X${echo_test_string+set}" != Xset; then + # find a string as large as possible, as long as the shell can cope with it + for cmd in 'sed 50q "[$]0"' 'sed 20q "[$]0"' 'sed 10q "[$]0"' 'sed 2q "[$]0"' 'echo test'; do + # expected sizes: less than 2Kb, 1Kb, 512 bytes, 16 bytes, ... + if { echo_test_string=`eval $cmd`; } 2>/dev/null && + { test "X$echo_test_string" = "X$echo_test_string"; } 2>/dev/null + then + break + fi + done + fi + + if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' && + echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + : + else + # The Solaris, AIX, and Digital Unix default echo programs unquote + # backslashes. This makes it impossible to quote backslashes using + # echo "$something" | sed 's/\\/\\\\/g' + # + # So, first we look for a working echo in the user's PATH. + + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for dir in $PATH /usr/ucb; do + IFS="$lt_save_ifs" + if (test -f $dir/echo || test -f $dir/echo$ac_exeext) && + test "X`($dir/echo '\t') 2>/dev/null`" = 'X\t' && + echo_testing_string=`($dir/echo "$echo_test_string") 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + ECHO="$dir/echo" + break + fi + done + IFS="$lt_save_ifs" + + if test "X$ECHO" = Xecho; then + # We didn't find a better echo, so look for alternatives. + if test "X`{ print -r '\t'; } 2>/dev/null`" = 'X\t' && + echo_testing_string=`{ print -r "$echo_test_string"; } 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + # This shell has a builtin print -r that does the trick. + ECHO='print -r' + elif { test -f /bin/ksh || test -f /bin/ksh$ac_exeext; } && + test "X$CONFIG_SHELL" != X/bin/ksh; then + # If we have ksh, try running configure again with it. + ORIGINAL_CONFIG_SHELL=${CONFIG_SHELL-/bin/sh} + export ORIGINAL_CONFIG_SHELL + CONFIG_SHELL=/bin/ksh + export CONFIG_SHELL + exec $CONFIG_SHELL "[$]0" --no-reexec ${1+"[$]@"} + else + # Try using printf. + ECHO='printf %s\n' + if test "X`{ $ECHO '\t'; } 2>/dev/null`" = 'X\t' && + echo_testing_string=`{ $ECHO "$echo_test_string"; } 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + # Cool, printf works + : + elif echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` && + test "X$echo_testing_string" = 'X\t' && + echo_testing_string=`($ORIGINAL_CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + CONFIG_SHELL=$ORIGINAL_CONFIG_SHELL + export CONFIG_SHELL + SHELL="$CONFIG_SHELL" + export SHELL + ECHO="$CONFIG_SHELL [$]0 --fallback-echo" + elif echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo '\t') 2>/dev/null` && + test "X$echo_testing_string" = 'X\t' && + echo_testing_string=`($CONFIG_SHELL "[$]0" --fallback-echo "$echo_test_string") 2>/dev/null` && + test "X$echo_testing_string" = "X$echo_test_string"; then + ECHO="$CONFIG_SHELL [$]0 --fallback-echo" + else + # maybe with a smaller string... + prev=: + + for cmd in 'echo test' 'sed 2q "[$]0"' 'sed 10q "[$]0"' 'sed 20q "[$]0"' 'sed 50q "[$]0"'; do + if { test "X$echo_test_string" = "X`eval $cmd`"; } 2>/dev/null + then + break + fi + prev="$cmd" + done + + if test "$prev" != 'sed 50q "[$]0"'; then + echo_test_string=`eval $prev` + export echo_test_string + exec ${ORIGINAL_CONFIG_SHELL-${CONFIG_SHELL-/bin/sh}} "[$]0" ${1+"[$]@"} + else + # Oops. We lost completely, so just stick with echo. + ECHO=echo + fi + fi + fi + fi + fi +fi + +# Copy echo and quote the copy suitably for passing to libtool from +# the Makefile, instead of quoting the original, which is used later. +lt_ECHO=$ECHO +if test "X$lt_ECHO" = "X$CONFIG_SHELL [$]0 --fallback-echo"; then + lt_ECHO="$CONFIG_SHELL \\\$\[$]0 --fallback-echo" +fi + +AC_SUBST(lt_ECHO) +]) +_LT_DECL([], [SHELL], [1], [Shell to use when invoking shell scripts]) +_LT_DECL([], [ECHO], [1], + [An echo program that does not interpret backslashes]) +])# _LT_PROG_ECHO_BACKSLASH + + +# _LT_ENABLE_LOCK +# --------------- +m4_defun([_LT_ENABLE_LOCK], +[AC_ARG_ENABLE([libtool-lock], + [AS_HELP_STRING([--disable-libtool-lock], + [avoid locking (might break parallel builds)])]) +test "x$enable_libtool_lock" != xno && enable_libtool_lock=yes + +# Some flags need to be propagated to the compiler or linker for good +# libtool support. +case $host in +ia64-*-hpux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.$ac_objext` in + *ELF-32*) + HPUX_IA64_MODE="32" + ;; + *ELF-64*) + HPUX_IA64_MODE="64" + ;; + esac + fi + rm -rf conftest* + ;; +*-*-irix6*) + # Find out which ABI we are using. + echo '[#]line __oline__ "configure"' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + if test "$lt_cv_prog_gnu_ld" = yes; then + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -melf32bsmip" + ;; + *N32*) + LD="${LD-ld} -melf32bmipn32" + ;; + *64-bit*) + LD="${LD-ld} -melf64bmip" + ;; + esac + else + case `/usr/bin/file conftest.$ac_objext` in + *32-bit*) + LD="${LD-ld} -32" + ;; + *N32*) + LD="${LD-ld} -n32" + ;; + *64-bit*) + LD="${LD-ld} -64" + ;; + esac + fi + fi + rm -rf conftest* + ;; + +x86_64-*kfreebsd*-gnu|x86_64-*linux*|ppc*-*linux*|powerpc*-*linux*| \ +s390*-*linux*|s390*-*tpf*|sparc*-*linux*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.o` in + *32-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_i386_fbsd" + ;; + x86_64-*linux*) + LD="${LD-ld} -m elf_i386" + ;; + ppc64-*linux*|powerpc64-*linux*) + LD="${LD-ld} -m elf32ppclinux" + ;; + s390x-*linux*) + LD="${LD-ld} -m elf_s390" + ;; + sparc64-*linux*) + LD="${LD-ld} -m elf32_sparc" + ;; + esac + ;; + *64-bit*) + case $host in + x86_64-*kfreebsd*-gnu) + LD="${LD-ld} -m elf_x86_64_fbsd" + ;; + x86_64-*linux*) + LD="${LD-ld} -m elf_x86_64" + ;; + ppc*-*linux*|powerpc*-*linux*) + LD="${LD-ld} -m elf64ppc" + ;; + s390*-*linux*|s390*-*tpf*) + LD="${LD-ld} -m elf64_s390" + ;; + sparc*-*linux*) + LD="${LD-ld} -m elf64_sparc" + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; + +*-*-sco3.2v5*) + # On SCO OpenServer 5, we need -belf to get full-featured binaries. + SAVE_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS -belf" + AC_CACHE_CHECK([whether the C compiler needs -belf], lt_cv_cc_needs_belf, + [AC_LANG_PUSH(C) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[]],[[]])],[lt_cv_cc_needs_belf=yes],[lt_cv_cc_needs_belf=no]) + AC_LANG_POP]) + if test x"$lt_cv_cc_needs_belf" != x"yes"; then + # this is probably gcc 2.8.0, egcs 1.0 or newer; no need for -belf + CFLAGS="$SAVE_CFLAGS" + fi + ;; +sparc*-*solaris*) + # Find out which ABI we are using. + echo 'int i;' > conftest.$ac_ext + if AC_TRY_EVAL(ac_compile); then + case `/usr/bin/file conftest.o` in + *64-bit*) + case $lt_cv_prog_gnu_ld in + yes*) LD="${LD-ld} -m elf64_sparc" ;; + *) + if ${LD-ld} -64 -r -o conftest2.o conftest.o >/dev/null 2>&1; then + LD="${LD-ld} -64" + fi + ;; + esac + ;; + esac + fi + rm -rf conftest* + ;; +esac + +need_locks="$enable_libtool_lock" +])# _LT_ENABLE_LOCK + + +# _LT_CMD_OLD_ARCHIVE +# ------------------- +m4_defun([_LT_CMD_OLD_ARCHIVE], +[AC_CHECK_TOOL(AR, ar, false) +test -z "$AR" && AR=ar +test -z "$AR_FLAGS" && AR_FLAGS=cru +_LT_DECL([], [AR], [1], [The archiver]) +_LT_DECL([], [AR_FLAGS], [1]) + +AC_CHECK_TOOL(STRIP, strip, :) +test -z "$STRIP" && STRIP=: +_LT_DECL([], [STRIP], [1], [A symbol stripping program]) + +AC_CHECK_TOOL(RANLIB, ranlib, :) +test -z "$RANLIB" && RANLIB=: +_LT_DECL([], [RANLIB], [1], + [Commands used to install an old-style archive]) + +# Determine commands to create old-style static archives. +old_archive_cmds='$AR $AR_FLAGS $oldlib$oldobjs' +old_postinstall_cmds='chmod 644 $oldlib' +old_postuninstall_cmds= + +if test -n "$RANLIB"; then + case $host_os in + openbsd*) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB -t \$oldlib" + ;; + *) + old_postinstall_cmds="$old_postinstall_cmds~\$RANLIB \$oldlib" + ;; + esac + old_archive_cmds="$old_archive_cmds~\$RANLIB \$oldlib" +fi +_LT_DECL([], [old_postinstall_cmds], [2]) +_LT_DECL([], [old_postuninstall_cmds], [2]) +_LT_TAGDECL([], [old_archive_cmds], [2], + [Commands used to build an old-style archive]) +])# _LT_CMD_OLD_ARCHIVE + + +# _LT_COMPILER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, +# [OUTPUT-FILE], [ACTION-SUCCESS], [ACTION-FAILURE]) +# ---------------------------------------------------------------- +# Check whether the given compiler option works +AC_DEFUN([_LT_COMPILER_OPTION], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_SED])dnl +AC_CACHE_CHECK([$1], [$2], + [$2=no + m4_if([$4], , [ac_outfile=conftest.$ac_objext], [ac_outfile=$4]) + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + lt_compiler_flag="$3" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + # The option is referenced via a variable to avoid confusing sed. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$lt_compile" 2>conftest.err) + ac_status=$? + cat conftest.err >&AS_MESSAGE_LOG_FD + echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + if (exit $ac_status) && test -s "$ac_outfile"; then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings other than the usual output. + $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' >conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if test ! -s conftest.er2 || diff conftest.exp conftest.er2 >/dev/null; then + $2=yes + fi + fi + $RM conftest* +]) + +if test x"[$]$2" = xyes; then + m4_if([$5], , :, [$5]) +else + m4_if([$6], , :, [$6]) +fi +])# _LT_COMPILER_OPTION + +# Old name: +AU_ALIAS([AC_LIBTOOL_COMPILER_OPTION], [_LT_COMPILER_OPTION]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_COMPILER_OPTION], []) + + +# _LT_LINKER_OPTION(MESSAGE, VARIABLE-NAME, FLAGS, +# [ACTION-SUCCESS], [ACTION-FAILURE]) +# ---------------------------------------------------- +# Check whether the given linker option works +AC_DEFUN([_LT_LINKER_OPTION], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_SED])dnl +AC_CACHE_CHECK([$1], [$2], + [$2=no + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS $3" + echo "$lt_simple_link_test_code" > conftest.$ac_ext + if (eval $ac_link 2>conftest.err) && test -s conftest$ac_exeext; then + # The linker can only warn and ignore the option if not recognized + # So say no if there are warnings + if test -s conftest.err; then + # Append any errors to the config.log. + cat conftest.err 1>&AS_MESSAGE_LOG_FD + $ECHO "X$_lt_linker_boilerplate" | $Xsed -e '/^$/d' > conftest.exp + $SED '/^$/d; /^ *+/d' conftest.err >conftest.er2 + if diff conftest.exp conftest.er2 >/dev/null; then + $2=yes + fi + else + $2=yes + fi + fi + $RM -r conftest* + LDFLAGS="$save_LDFLAGS" +]) + +if test x"[$]$2" = xyes; then + m4_if([$4], , :, [$4]) +else + m4_if([$5], , :, [$5]) +fi +])# _LT_LINKER_OPTION + +# Old name: +AU_ALIAS([AC_LIBTOOL_LINKER_OPTION], [_LT_LINKER_OPTION]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_LINKER_OPTION], []) + + +# LT_CMD_MAX_LEN +#--------------- +AC_DEFUN([LT_CMD_MAX_LEN], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +# find the maximum length of command line arguments +AC_MSG_CHECKING([the maximum length of command line arguments]) +AC_CACHE_VAL([lt_cv_sys_max_cmd_len], [dnl + i=0 + teststring="ABCD" + + case $build_os in + msdosdjgpp*) + # On DJGPP, this test can blow up pretty badly due to problems in libc + # (any single argument exceeding 2000 bytes causes a buffer overrun + # during glob expansion). Even if it were fixed, the result of this + # check would be larger than it should be. + lt_cv_sys_max_cmd_len=12288; # 12K is about right + ;; + + gnu*) + # Under GNU Hurd, this test is not required because there is + # no limit to the length of command line arguments. + # Libtool will interpret -1 as no limit whatsoever + lt_cv_sys_max_cmd_len=-1; + ;; + + cygwin* | mingw* | cegcc*) + # On Win9x/ME, this test blows up -- it succeeds, but takes + # about 5 minutes as the teststring grows exponentially. + # Worse, since 9x/ME are not pre-emptively multitasking, + # you end up with a "frozen" computer, even though with patience + # the test eventually succeeds (with a max line length of 256k). + # Instead, let's just punt: use the minimum linelength reported by + # all of the supported platforms: 8192 (on NT/2K/XP). + lt_cv_sys_max_cmd_len=8192; + ;; + + amigaos*) + # On AmigaOS with pdksh, this test takes hours, literally. + # So we just punt and use a minimum line length of 8192. + lt_cv_sys_max_cmd_len=8192; + ;; + + netbsd* | freebsd* | openbsd* | darwin* | dragonfly*) + # This has been around since 386BSD, at least. Likely further. + if test -x /sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/sbin/sysctl -n kern.argmax` + elif test -x /usr/sbin/sysctl; then + lt_cv_sys_max_cmd_len=`/usr/sbin/sysctl -n kern.argmax` + else + lt_cv_sys_max_cmd_len=65536 # usable default for all BSDs + fi + # And add a safety zone + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + ;; + + interix*) + # We know the value 262144 and hardcode it with a safety zone (like BSD) + lt_cv_sys_max_cmd_len=196608 + ;; + + osf*) + # Dr. Hans Ekkehard Plesser reports seeing a kernel panic running configure + # due to this test when exec_disable_arg_limit is 1 on Tru64. It is not + # nice to cause kernel panics so lets avoid the loop below. + # First set a reasonable default. + lt_cv_sys_max_cmd_len=16384 + # + if test -x /sbin/sysconfig; then + case `/sbin/sysconfig -q proc exec_disable_arg_limit` in + *1*) lt_cv_sys_max_cmd_len=-1 ;; + esac + fi + ;; + sco3.2v5*) + lt_cv_sys_max_cmd_len=102400 + ;; + sysv5* | sco5v6* | sysv4.2uw2*) + kargmax=`grep ARG_MAX /etc/conf/cf.d/stune 2>/dev/null` + if test -n "$kargmax"; then + lt_cv_sys_max_cmd_len=`echo $kargmax | sed 's/.*[[ ]]//'` + else + lt_cv_sys_max_cmd_len=32768 + fi + ;; + *) + lt_cv_sys_max_cmd_len=`(getconf ARG_MAX) 2> /dev/null` + if test -n "$lt_cv_sys_max_cmd_len"; then + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 4` + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \* 3` + else + # Make teststring a little bigger before we do anything with it. + # a 1K string should be a reasonable start. + for i in 1 2 3 4 5 6 7 8 ; do + teststring=$teststring$teststring + done + SHELL=${SHELL-${CONFIG_SHELL-/bin/sh}} + # If test is not a shell built-in, we'll probably end up computing a + # maximum length that is only half of the actual maximum length, but + # we can't tell. + while { test "X"`$SHELL [$]0 --fallback-echo "X$teststring$teststring" 2>/dev/null` \ + = "XX$teststring$teststring"; } >/dev/null 2>&1 && + test $i != 17 # 1/2 MB should be enough + do + i=`expr $i + 1` + teststring=$teststring$teststring + done + # Only check the string length outside the loop. + lt_cv_sys_max_cmd_len=`expr "X$teststring" : ".*" 2>&1` + teststring= + # Add a significant safety factor because C++ compilers can tack on + # massive amounts of additional arguments before passing them to the + # linker. It appears as though 1/2 is a usable value. + lt_cv_sys_max_cmd_len=`expr $lt_cv_sys_max_cmd_len \/ 2` + fi + ;; + esac +]) +if test -n $lt_cv_sys_max_cmd_len ; then + AC_MSG_RESULT($lt_cv_sys_max_cmd_len) +else + AC_MSG_RESULT(none) +fi +max_cmd_len=$lt_cv_sys_max_cmd_len +_LT_DECL([], [max_cmd_len], [0], + [What is the maximum length of a command?]) +])# LT_CMD_MAX_LEN + +# Old name: +AU_ALIAS([AC_LIBTOOL_SYS_MAX_CMD_LEN], [LT_CMD_MAX_LEN]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_SYS_MAX_CMD_LEN], []) + + +# _LT_HEADER_DLFCN +# ---------------- +m4_defun([_LT_HEADER_DLFCN], +[AC_CHECK_HEADERS([dlfcn.h], [], [], [AC_INCLUDES_DEFAULT])dnl +])# _LT_HEADER_DLFCN + + +# _LT_TRY_DLOPEN_SELF (ACTION-IF-TRUE, ACTION-IF-TRUE-W-USCORE, +# ACTION-IF-FALSE, ACTION-IF-CROSS-COMPILING) +# ---------------------------------------------------------------- +m4_defun([_LT_TRY_DLOPEN_SELF], +[m4_require([_LT_HEADER_DLFCN])dnl +if test "$cross_compiling" = yes; then : + [$4] +else + lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2 + lt_status=$lt_dlunknown + cat > conftest.$ac_ext <<_LT_EOF +[#line __oline__ "configure" +#include "confdefs.h" + +#if HAVE_DLFCN_H +#include +#endif + +#include + +#ifdef RTLD_GLOBAL +# define LT_DLGLOBAL RTLD_GLOBAL +#else +# ifdef DL_GLOBAL +# define LT_DLGLOBAL DL_GLOBAL +# else +# define LT_DLGLOBAL 0 +# endif +#endif + +/* We may have to define LT_DLLAZY_OR_NOW in the command line if we + find out it does not work in some platform. */ +#ifndef LT_DLLAZY_OR_NOW +# ifdef RTLD_LAZY +# define LT_DLLAZY_OR_NOW RTLD_LAZY +# else +# ifdef DL_LAZY +# define LT_DLLAZY_OR_NOW DL_LAZY +# else +# ifdef RTLD_NOW +# define LT_DLLAZY_OR_NOW RTLD_NOW +# else +# ifdef DL_NOW +# define LT_DLLAZY_OR_NOW DL_NOW +# else +# define LT_DLLAZY_OR_NOW 0 +# endif +# endif +# endif +# endif +#endif + +void fnord() { int i=42;} +int main () +{ + void *self = dlopen (0, LT_DLGLOBAL|LT_DLLAZY_OR_NOW); + int status = $lt_dlunknown; + + if (self) + { + if (dlsym (self,"fnord")) status = $lt_dlno_uscore; + else if (dlsym( self,"_fnord")) status = $lt_dlneed_uscore; + /* dlclose (self); */ + } + else + puts (dlerror ()); + + return status; +}] +_LT_EOF + if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext} 2>/dev/null; then + (./conftest; exit; ) >&AS_MESSAGE_LOG_FD 2>/dev/null + lt_status=$? + case x$lt_status in + x$lt_dlno_uscore) $1 ;; + x$lt_dlneed_uscore) $2 ;; + x$lt_dlunknown|x*) $3 ;; + esac + else : + # compilation failed + $3 + fi +fi +rm -fr conftest* +])# _LT_TRY_DLOPEN_SELF + + +# LT_SYS_DLOPEN_SELF +# ------------------ +AC_DEFUN([LT_SYS_DLOPEN_SELF], +[m4_require([_LT_HEADER_DLFCN])dnl +if test "x$enable_dlopen" != xyes; then + enable_dlopen=unknown + enable_dlopen_self=unknown + enable_dlopen_self_static=unknown +else + lt_cv_dlopen=no + lt_cv_dlopen_libs= + + case $host_os in + beos*) + lt_cv_dlopen="load_add_on" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ;; + + mingw* | pw32* | cegcc*) + lt_cv_dlopen="LoadLibrary" + lt_cv_dlopen_libs= + ;; + + cygwin*) + lt_cv_dlopen="dlopen" + lt_cv_dlopen_libs= + ;; + + darwin*) + # if libdl is installed we need to link against it + AC_CHECK_LIB([dl], [dlopen], + [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"],[ + lt_cv_dlopen="dyld" + lt_cv_dlopen_libs= + lt_cv_dlopen_self=yes + ]) + ;; + + *) + AC_CHECK_FUNC([shl_load], + [lt_cv_dlopen="shl_load"], + [AC_CHECK_LIB([dld], [shl_load], + [lt_cv_dlopen="shl_load" lt_cv_dlopen_libs="-ldld"], + [AC_CHECK_FUNC([dlopen], + [lt_cv_dlopen="dlopen"], + [AC_CHECK_LIB([dl], [dlopen], + [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-ldl"], + [AC_CHECK_LIB([svld], [dlopen], + [lt_cv_dlopen="dlopen" lt_cv_dlopen_libs="-lsvld"], + [AC_CHECK_LIB([dld], [dld_link], + [lt_cv_dlopen="dld_link" lt_cv_dlopen_libs="-ldld"]) + ]) + ]) + ]) + ]) + ]) + ;; + esac + + if test "x$lt_cv_dlopen" != xno; then + enable_dlopen=yes + else + enable_dlopen=no + fi + + case $lt_cv_dlopen in + dlopen) + save_CPPFLAGS="$CPPFLAGS" + test "x$ac_cv_header_dlfcn_h" = xyes && CPPFLAGS="$CPPFLAGS -DHAVE_DLFCN_H" + + save_LDFLAGS="$LDFLAGS" + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $export_dynamic_flag_spec\" + + save_LIBS="$LIBS" + LIBS="$lt_cv_dlopen_libs $LIBS" + + AC_CACHE_CHECK([whether a program can dlopen itself], + lt_cv_dlopen_self, [dnl + _LT_TRY_DLOPEN_SELF( + lt_cv_dlopen_self=yes, lt_cv_dlopen_self=yes, + lt_cv_dlopen_self=no, lt_cv_dlopen_self=cross) + ]) + + if test "x$lt_cv_dlopen_self" = xyes; then + wl=$lt_prog_compiler_wl eval LDFLAGS=\"\$LDFLAGS $lt_prog_compiler_static\" + AC_CACHE_CHECK([whether a statically linked program can dlopen itself], + lt_cv_dlopen_self_static, [dnl + _LT_TRY_DLOPEN_SELF( + lt_cv_dlopen_self_static=yes, lt_cv_dlopen_self_static=yes, + lt_cv_dlopen_self_static=no, lt_cv_dlopen_self_static=cross) + ]) + fi + + CPPFLAGS="$save_CPPFLAGS" + LDFLAGS="$save_LDFLAGS" + LIBS="$save_LIBS" + ;; + esac + + case $lt_cv_dlopen_self in + yes|no) enable_dlopen_self=$lt_cv_dlopen_self ;; + *) enable_dlopen_self=unknown ;; + esac + + case $lt_cv_dlopen_self_static in + yes|no) enable_dlopen_self_static=$lt_cv_dlopen_self_static ;; + *) enable_dlopen_self_static=unknown ;; + esac +fi +_LT_DECL([dlopen_support], [enable_dlopen], [0], + [Whether dlopen is supported]) +_LT_DECL([dlopen_self], [enable_dlopen_self], [0], + [Whether dlopen of programs is supported]) +_LT_DECL([dlopen_self_static], [enable_dlopen_self_static], [0], + [Whether dlopen of statically linked programs is supported]) +])# LT_SYS_DLOPEN_SELF + +# Old name: +AU_ALIAS([AC_LIBTOOL_DLOPEN_SELF], [LT_SYS_DLOPEN_SELF]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_DLOPEN_SELF], []) + + +# _LT_COMPILER_C_O([TAGNAME]) +# --------------------------- +# Check to see if options -c and -o are simultaneously supported by compiler. +# This macro does not hard code the compiler like AC_PROG_CC_C_O. +m4_defun([_LT_COMPILER_C_O], +[m4_require([_LT_DECL_SED])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_TAG_COMPILER])dnl +AC_CACHE_CHECK([if $compiler supports -c -o file.$ac_objext], + [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)], + [_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=no + $RM -r conftest 2>/dev/null + mkdir conftest + cd conftest + mkdir out + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + lt_compiler_flag="-o out/conftest2.$ac_objext" + # Insert the option either (1) after the last *FLAGS variable, or + # (2) before a word containing "conftest.", or (3) at the end. + # Note that $ac_compile itself does not contain backslashes and begins + # with a dollar sign (not a hyphen), so the echo should work correctly. + lt_compile=`echo "$ac_compile" | $SED \ + -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \ + -e 's: [[^ ]]*conftest\.: $lt_compiler_flag&:; t' \ + -e 's:$: $lt_compiler_flag:'` + (eval echo "\"\$as_me:__oline__: $lt_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$lt_compile" 2>out/conftest.err) + ac_status=$? + cat out/conftest.err >&AS_MESSAGE_LOG_FD + echo "$as_me:__oline__: \$? = $ac_status" >&AS_MESSAGE_LOG_FD + if (exit $ac_status) && test -s out/conftest2.$ac_objext + then + # The compiler can only warn and ignore the option if not recognized + # So say no if there are warnings + $ECHO "X$_lt_compiler_boilerplate" | $Xsed -e '/^$/d' > out/conftest.exp + $SED '/^$/d; /^ *+/d' out/conftest.err >out/conftest.er2 + if test ! -s out/conftest.er2 || diff out/conftest.exp out/conftest.er2 >/dev/null; then + _LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes + fi + fi + chmod u+w . 2>&AS_MESSAGE_LOG_FD + $RM conftest* + # SGI C++ compiler will create directory out/ii_files/ for + # template instantiation + test -d out/ii_files && $RM out/ii_files/* && rmdir out/ii_files + $RM out/* && rmdir out + cd .. + $RM -r conftest + $RM conftest* +]) +_LT_TAGDECL([compiler_c_o], [lt_cv_prog_compiler_c_o], [1], + [Does compiler simultaneously support -c and -o options?]) +])# _LT_COMPILER_C_O + + +# _LT_COMPILER_FILE_LOCKS([TAGNAME]) +# ---------------------------------- +# Check to see if we can do hard links to lock some files if needed +m4_defun([_LT_COMPILER_FILE_LOCKS], +[m4_require([_LT_ENABLE_LOCK])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +_LT_COMPILER_C_O([$1]) + +hard_links="nottested" +if test "$_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)" = no && test "$need_locks" != no; then + # do not overwrite the value of need_locks provided by the user + AC_MSG_CHECKING([if we can lock with hard links]) + hard_links=yes + $RM conftest* + ln conftest.a conftest.b 2>/dev/null && hard_links=no + touch conftest.a + ln conftest.a conftest.b 2>&5 || hard_links=no + ln conftest.a conftest.b 2>/dev/null && hard_links=no + AC_MSG_RESULT([$hard_links]) + if test "$hard_links" = no; then + AC_MSG_WARN([`$CC' does not support `-c -o', so `make -j' may be unsafe]) + need_locks=warn + fi +else + need_locks=no +fi +_LT_DECL([], [need_locks], [1], [Must we lock files when doing compilation?]) +])# _LT_COMPILER_FILE_LOCKS + + +# _LT_CHECK_OBJDIR +# ---------------- +m4_defun([_LT_CHECK_OBJDIR], +[AC_CACHE_CHECK([for objdir], [lt_cv_objdir], +[rm -f .libs 2>/dev/null +mkdir .libs 2>/dev/null +if test -d .libs; then + lt_cv_objdir=.libs +else + # MS-DOS does not allow filenames that begin with a dot. + lt_cv_objdir=_libs +fi +rmdir .libs 2>/dev/null]) +objdir=$lt_cv_objdir +_LT_DECL([], [objdir], [0], + [The name of the directory that contains temporary libtool files])dnl +m4_pattern_allow([LT_OBJDIR])dnl +AC_DEFINE_UNQUOTED(LT_OBJDIR, "$lt_cv_objdir/", + [Define to the sub-directory in which libtool stores uninstalled libraries.]) +])# _LT_CHECK_OBJDIR + + +# _LT_LINKER_HARDCODE_LIBPATH([TAGNAME]) +# -------------------------------------- +# Check hardcoding attributes. +m4_defun([_LT_LINKER_HARDCODE_LIBPATH], +[AC_MSG_CHECKING([how to hardcode library paths into programs]) +_LT_TAGVAR(hardcode_action, $1)= +if test -n "$_LT_TAGVAR(hardcode_libdir_flag_spec, $1)" || + test -n "$_LT_TAGVAR(runpath_var, $1)" || + test "X$_LT_TAGVAR(hardcode_automatic, $1)" = "Xyes" ; then + + # We can hardcode non-existent directories. + if test "$_LT_TAGVAR(hardcode_direct, $1)" != no && + # If the only mechanism to avoid hardcoding is shlibpath_var, we + # have to relink, otherwise we might link with an installed library + # when we should be linking with a yet-to-be-installed one + ## test "$_LT_TAGVAR(hardcode_shlibpath_var, $1)" != no && + test "$_LT_TAGVAR(hardcode_minus_L, $1)" != no; then + # Linking always hardcodes the temporary library directory. + _LT_TAGVAR(hardcode_action, $1)=relink + else + # We can link without hardcoding, and we can hardcode nonexisting dirs. + _LT_TAGVAR(hardcode_action, $1)=immediate + fi +else + # We cannot hardcode anything, or else we can only hardcode existing + # directories. + _LT_TAGVAR(hardcode_action, $1)=unsupported +fi +AC_MSG_RESULT([$_LT_TAGVAR(hardcode_action, $1)]) + +if test "$_LT_TAGVAR(hardcode_action, $1)" = relink || + test "$_LT_TAGVAR(inherit_rpath, $1)" = yes; then + # Fast installation is not supported + enable_fast_install=no +elif test "$shlibpath_overrides_runpath" = yes || + test "$enable_shared" = no; then + # Fast installation is not necessary + enable_fast_install=needless +fi +_LT_TAGDECL([], [hardcode_action], [0], + [How to hardcode a shared library path into an executable]) +])# _LT_LINKER_HARDCODE_LIBPATH + + +# _LT_CMD_STRIPLIB +# ---------------- +m4_defun([_LT_CMD_STRIPLIB], +[m4_require([_LT_DECL_EGREP]) +striplib= +old_striplib= +AC_MSG_CHECKING([whether stripping libraries is possible]) +if test -n "$STRIP" && $STRIP -V 2>&1 | $GREP "GNU strip" >/dev/null; then + test -z "$old_striplib" && old_striplib="$STRIP --strip-debug" + test -z "$striplib" && striplib="$STRIP --strip-unneeded" + AC_MSG_RESULT([yes]) +else +# FIXME - insert some real tests, host_os isn't really good enough + case $host_os in + darwin*) + if test -n "$STRIP" ; then + striplib="$STRIP -x" + old_striplib="$STRIP -S" + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + fi + ;; + *) + AC_MSG_RESULT([no]) + ;; + esac +fi +_LT_DECL([], [old_striplib], [1], [Commands to strip libraries]) +_LT_DECL([], [striplib], [1]) +])# _LT_CMD_STRIPLIB + + +# _LT_SYS_DYNAMIC_LINKER([TAG]) +# ----------------------------- +# PORTME Fill in your ld.so characteristics +m4_defun([_LT_SYS_DYNAMIC_LINKER], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_OBJDUMP])dnl +m4_require([_LT_DECL_SED])dnl +AC_MSG_CHECKING([dynamic linker characteristics]) +m4_if([$1], + [], [ +if test "$GCC" = yes; then + case $host_os in + darwin*) lt_awk_arg="/^libraries:/,/LR/" ;; + *) lt_awk_arg="/^libraries:/" ;; + esac + lt_search_path_spec=`$CC -print-search-dirs | awk $lt_awk_arg | $SED -e "s/^libraries://" -e "s,=/,/,g"` + if $ECHO "$lt_search_path_spec" | $GREP ';' >/dev/null ; then + # if the path contains ";" then we assume it to be the separator + # otherwise default to the standard path separator (i.e. ":") - it is + # assumed that no part of a normal pathname contains ";" but that should + # okay in the real world where ";" in dirpaths is itself problematic. + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e 's/;/ /g'` + else + lt_search_path_spec=`$ECHO "$lt_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + # Ok, now we have the path, separated by spaces, we can step through it + # and add multilib dir if necessary. + lt_tmp_lt_search_path_spec= + lt_multi_os_dir=`$CC $CPPFLAGS $CFLAGS $LDFLAGS -print-multi-os-directory 2>/dev/null` + for lt_sys_path in $lt_search_path_spec; do + if test -d "$lt_sys_path/$lt_multi_os_dir"; then + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path/$lt_multi_os_dir" + else + test -d "$lt_sys_path" && \ + lt_tmp_lt_search_path_spec="$lt_tmp_lt_search_path_spec $lt_sys_path" + fi + done + lt_search_path_spec=`$ECHO $lt_tmp_lt_search_path_spec | awk ' +BEGIN {RS=" "; FS="/|\n";} { + lt_foo=""; + lt_count=0; + for (lt_i = NF; lt_i > 0; lt_i--) { + if ($lt_i != "" && $lt_i != ".") { + if ($lt_i == "..") { + lt_count++; + } else { + if (lt_count == 0) { + lt_foo="/" $lt_i lt_foo; + } else { + lt_count--; + } + } + } + } + if (lt_foo != "") { lt_freq[[lt_foo]]++; } + if (lt_freq[[lt_foo]] == 1) { print lt_foo; } +}'` + sys_lib_search_path_spec=`$ECHO $lt_search_path_spec` +else + sys_lib_search_path_spec="/lib /usr/lib /usr/local/lib" +fi]) +library_names_spec= +libname_spec='lib$name' +soname_spec= +shrext_cmds=".so" +postinstall_cmds= +postuninstall_cmds= +finish_cmds= +finish_eval= +shlibpath_var= +shlibpath_overrides_runpath=unknown +version_type=none +dynamic_linker="$host_os ld.so" +sys_lib_dlsearch_path_spec="/lib /usr/lib" +need_lib_prefix=unknown +hardcode_into_libs=no + +# when you set need_version to no, make sure it does not cause -set_version +# flags to be left without arguments +need_version=unknown + +case $host_os in +aix3*) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname.a' + shlibpath_var=LIBPATH + + # AIX 3 has no versioning support, so we append a major version to the name. + soname_spec='${libname}${release}${shared_ext}$major' + ;; + +aix[[4-9]]*) + version_type=linux + need_lib_prefix=no + need_version=no + hardcode_into_libs=yes + if test "$host_cpu" = ia64; then + # AIX 5 supports IA64 + library_names_spec='${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext}$versuffix $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + else + # With GCC up to 2.95.x, collect2 would create an import file + # for dependence libraries. The import file would start with + # the line `#! .'. This would cause the generated library to + # depend on `.', always an invalid library. This was fixed in + # development snapshots of GCC prior to 3.0. + case $host_os in + aix4 | aix4.[[01]] | aix4.[[01]].*) + if { echo '#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 97)' + echo ' yes ' + echo '#endif'; } | ${CC} -E - | $GREP yes > /dev/null; then + : + else + can_build_shared=no + fi + ;; + esac + # AIX (on Power*) has no versioning support, so currently we can not hardcode correct + # soname into executable. Probably we can add versioning support to + # collect2, so additional links can be useful in future. + if test "$aix_use_runtimelinking" = yes; then + # If using run time linking (on AIX 4.2 or later) use lib.so + # instead of lib.a to let people know that these are not + # typical AIX shared libraries. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + else + # We preserve .a as extension for shared libraries through AIX4.2 + # and later when we are not doing run time linking. + library_names_spec='${libname}${release}.a $libname.a' + soname_spec='${libname}${release}${shared_ext}$major' + fi + shlibpath_var=LIBPATH + fi + ;; + +amigaos*) + case $host_cpu in + powerpc) + # Since July 2007 AmigaOS4 officially supports .so libraries. + # When compiling the executable, add -use-dynld -Lsobjs: to the compileline. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + ;; + m68k) + library_names_spec='$libname.ixlibrary $libname.a' + # Create ${libname}_ixlibrary.a entries in /sys/libs. + finish_eval='for lib in `ls $libdir/*.ixlibrary 2>/dev/null`; do libname=`$ECHO "X$lib" | $Xsed -e '\''s%^.*/\([[^/]]*\)\.ixlibrary$%\1%'\''`; test $RM /sys/libs/${libname}_ixlibrary.a; $show "cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a"; cd /sys/libs && $LN_S $lib ${libname}_ixlibrary.a || exit 1; done' + ;; + esac + ;; + +beos*) + library_names_spec='${libname}${shared_ext}' + dynamic_linker="$host_os ld.so" + shlibpath_var=LIBRARY_PATH + ;; + +bsdi[[45]]*) + version_type=linux + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/shlib /usr/lib /usr/X11/lib /usr/contrib/lib /lib /usr/local/lib" + sys_lib_dlsearch_path_spec="/shlib /usr/lib /usr/local/lib" + # the default ld.so.conf also contains /usr/contrib/lib and + # /usr/X11R6/lib (/usr/X11 is a link to /usr/X11R6), but let us allow + # libtool to hard-code these into programs + ;; + +cygwin* | mingw* | pw32* | cegcc*) + version_type=windows + shrext_cmds=".dll" + need_version=no + need_lib_prefix=no + + case $GCC,$host_os in + yes,cygwin* | yes,mingw* | yes,pw32* | yes,cegcc*) + library_names_spec='$libname.dll.a' + # DLL is installed to $(libdir)/../bin by postinstall_cmds + postinstall_cmds='base_file=`basename \${file}`~ + dlpath=`$SHELL 2>&1 -c '\''. $dir/'\''\${base_file}'\''i; echo \$dlname'\''`~ + dldir=$destdir/`dirname \$dlpath`~ + test -d \$dldir || mkdir -p \$dldir~ + $install_prog $dir/$dlname \$dldir/$dlname~ + chmod a+x \$dldir/$dlname~ + if test -n '\''$stripme'\'' && test -n '\''$striplib'\''; then + eval '\''$striplib \$dldir/$dlname'\'' || exit \$?; + fi' + postuninstall_cmds='dldll=`$SHELL 2>&1 -c '\''. $file; echo \$dlname'\''`~ + dlpath=$dir/\$dldll~ + $RM \$dlpath' + shlibpath_overrides_runpath=yes + + case $host_os in + cygwin*) + # Cygwin DLLs use 'cyg' prefix rather than 'lib' + soname_spec='`echo ${libname} | sed -e 's/^lib/cyg/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' + sys_lib_search_path_spec="/usr/lib /lib/w32api /lib /usr/local/lib" + ;; + mingw* | cegcc*) + # MinGW DLLs use traditional 'lib' prefix + soname_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' + sys_lib_search_path_spec=`$CC -print-search-dirs | $GREP "^libraries:" | $SED -e "s/^libraries://" -e "s,=/,/,g"` + if $ECHO "$sys_lib_search_path_spec" | [$GREP ';[c-zC-Z]:/' >/dev/null]; then + # It is most probably a Windows format PATH printed by + # mingw gcc, but we are running on Cygwin. Gcc prints its search + # path with ; separators, and with drive letters. We can handle the + # drive letters (cygwin fileutils understands them), so leave them, + # especially as we might pass files found there to a mingw objdump, + # which wouldn't understand a cygwinified path. Ahh. + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e 's/;/ /g'` + else + sys_lib_search_path_spec=`$ECHO "$sys_lib_search_path_spec" | $SED -e "s/$PATH_SEPARATOR/ /g"` + fi + ;; + pw32*) + # pw32 DLLs use 'pw' prefix rather than 'lib' + library_names_spec='`echo ${libname} | sed -e 's/^lib/pw/'``echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext}' + ;; + esac + ;; + + *) + library_names_spec='${libname}`echo ${release} | $SED -e 's/[[.]]/-/g'`${versuffix}${shared_ext} $libname.lib' + ;; + esac + dynamic_linker='Win32 ld.exe' + # FIXME: first we should search . and the directory the executable is in + shlibpath_var=PATH + ;; + +darwin* | rhapsody*) + dynamic_linker="$host_os dyld" + version_type=darwin + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${major}$shared_ext ${libname}$shared_ext' + soname_spec='${libname}${release}${major}$shared_ext' + shlibpath_overrides_runpath=yes + shlibpath_var=DYLD_LIBRARY_PATH + shrext_cmds='`test .$module = .yes && echo .so || echo .dylib`' +m4_if([$1], [],[ + sys_lib_search_path_spec="$sys_lib_search_path_spec /usr/local/lib"]) + sys_lib_dlsearch_path_spec='/usr/local/lib /lib /usr/lib' + ;; + +dgux*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname$shared_ext' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +freebsd1*) + dynamic_linker=no + ;; + +freebsd* | dragonfly*) + # DragonFly does not have aout. When/if they implement a new + # versioning mechanism, adjust this. + if test -x /usr/bin/objformat; then + objformat=`/usr/bin/objformat` + else + case $host_os in + freebsd[[123]]*) objformat=aout ;; + *) objformat=elf ;; + esac + fi + version_type=freebsd-$objformat + case $version_type in + freebsd-elf*) + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + need_version=no + need_lib_prefix=no + ;; + freebsd-*) + library_names_spec='${libname}${release}${shared_ext}$versuffix $libname${shared_ext}$versuffix' + need_version=yes + ;; + esac + shlibpath_var=LD_LIBRARY_PATH + case $host_os in + freebsd2*) + shlibpath_overrides_runpath=yes + ;; + freebsd3.[[01]]* | freebsdelf3.[[01]]*) + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + freebsd3.[[2-9]]* | freebsdelf3.[[2-9]]* | \ + freebsd4.[[0-5]] | freebsdelf4.[[0-5]] | freebsd4.1.1 | freebsdelf4.1.1) + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + *) # from 4.6 on, and DragonFly + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + esac + ;; + +gnu*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}${major} ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + hardcode_into_libs=yes + ;; + +hpux9* | hpux10* | hpux11*) + # Give a soname corresponding to the major version so that dld.sl refuses to + # link against other versions. + version_type=sunos + need_lib_prefix=no + need_version=no + case $host_cpu in + ia64*) + shrext_cmds='.so' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.so" + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + if test "X$HPUX_IA64_MODE" = X32; then + sys_lib_search_path_spec="/usr/lib/hpux32 /usr/local/lib/hpux32 /usr/local/lib" + else + sys_lib_search_path_spec="/usr/lib/hpux64 /usr/local/lib/hpux64" + fi + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + hppa*64*) + shrext_cmds='.sl' + hardcode_into_libs=yes + dynamic_linker="$host_os dld.sl" + shlibpath_var=LD_LIBRARY_PATH # How should we handle SHLIB_PATH + shlibpath_overrides_runpath=yes # Unless +noenvvar is specified. + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + sys_lib_search_path_spec="/usr/lib/pa20_64 /usr/ccs/lib/pa20_64" + sys_lib_dlsearch_path_spec=$sys_lib_search_path_spec + ;; + *) + shrext_cmds='.sl' + dynamic_linker="$host_os dld.sl" + shlibpath_var=SHLIB_PATH + shlibpath_overrides_runpath=no # +s is required to enable SHLIB_PATH + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + ;; + esac + # HP-UX runs *really* slowly unless shared libraries are mode 555. + postinstall_cmds='chmod 555 $lib' + ;; + +interix[[3-9]]*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='Interix 3.x ld.so.1 (PE, like ELF)' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +irix5* | irix6* | nonstopux*) + case $host_os in + nonstopux*) version_type=nonstopux ;; + *) + if test "$lt_cv_prog_gnu_ld" = yes; then + version_type=linux + else + version_type=irix + fi ;; + esac + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${release}${shared_ext} $libname${shared_ext}' + case $host_os in + irix5* | nonstopux*) + libsuff= shlibsuff= + ;; + *) + case $LD in # libtool.m4 will add one of these switches to LD + *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") + libsuff= shlibsuff= libmagic=32-bit;; + *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") + libsuff=32 shlibsuff=N32 libmagic=N32;; + *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") + libsuff=64 shlibsuff=64 libmagic=64-bit;; + *) libsuff= shlibsuff= libmagic=never-match;; + esac + ;; + esac + shlibpath_var=LD_LIBRARY${shlibsuff}_PATH + shlibpath_overrides_runpath=no + sys_lib_search_path_spec="/usr/lib${libsuff} /lib${libsuff} /usr/local/lib${libsuff}" + sys_lib_dlsearch_path_spec="/usr/lib${libsuff} /lib${libsuff}" + hardcode_into_libs=yes + ;; + +# No shared lib support for Linux oldld, aout, or coff. +linux*oldld* | linux*aout* | linux*coff*) + dynamic_linker=no + ;; + +# This must be Linux ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -n $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + # Some binutils ld are patched to set DT_RUNPATH + save_LDFLAGS=$LDFLAGS + save_libdir=$libdir + eval "libdir=/foo; wl=\"$_LT_TAGVAR(lt_prog_compiler_wl, $1)\"; \ + LDFLAGS=\"\$LDFLAGS $_LT_TAGVAR(hardcode_libdir_flag_spec, $1)\"" + AC_LINK_IFELSE([AC_LANG_PROGRAM([],[])], + [AS_IF([ ($OBJDUMP -p conftest$ac_exeext) 2>/dev/null | grep "RUNPATH.*$libdir" >/dev/null], + [shlibpath_overrides_runpath=yes])]) + LDFLAGS=$save_LDFLAGS + libdir=$save_libdir + + # This implies no fast_install, which is unacceptable. + # Some rework will be needed to allow for fast_install + # before this can be enabled. + hardcode_into_libs=yes + + # Append ld.so.conf contents to the search path + if test -f /etc/ld.so.conf; then + lt_ld_extra=`awk '/^include / { system(sprintf("cd /etc; cat %s 2>/dev/null", \[$]2)); skip = 1; } { if (!skip) print \[$]0; skip = 0; }' < /etc/ld.so.conf | $SED -e 's/#.*//;/^[ ]*hwcap[ ]/d;s/[:, ]/ /g;s/=[^=]*$//;s/=[^= ]* / /g;/^$/d' | tr '\n' ' '` + sys_lib_dlsearch_path_spec="/lib /usr/lib $lt_ld_extra" + fi + + # We used to test for /lib/ld.so.1 and disable shared libraries on + # powerpc, because MkLinux only supported shared libraries with the + # GNU dynamic linker. Since this was broken with cross compilers, + # most powerpc-linux boxes support dynamic linking these days and + # people can always --disable-shared, the test was removed, and we + # assume the GNU/Linux dynamic linker is in use. + dynamic_linker='GNU/Linux ld.so' + ;; + +netbsdelf*-gnu) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='NetBSD ld.elf_so' + ;; + +netbsd*) + version_type=sunos + need_lib_prefix=no + need_version=no + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + dynamic_linker='NetBSD (a.out) ld.so' + else + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major ${libname}${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + dynamic_linker='NetBSD ld.elf_so' + fi + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + ;; + +newsos6) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + ;; + +*nto* | *qnx*) + version_type=qnx + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + dynamic_linker='ldqnx.so' + ;; + +openbsd*) + version_type=sunos + sys_lib_dlsearch_path_spec="/usr/lib" + need_lib_prefix=no + # Some older versions of OpenBSD (3.3 at least) *do* need versioned libs. + case $host_os in + openbsd3.3 | openbsd3.3.*) need_version=yes ;; + *) need_version=no ;; + esac + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/sbin" ldconfig -m $libdir' + shlibpath_var=LD_LIBRARY_PATH + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + case $host_os in + openbsd2.[[89]] | openbsd2.[[89]].*) + shlibpath_overrides_runpath=no + ;; + *) + shlibpath_overrides_runpath=yes + ;; + esac + else + shlibpath_overrides_runpath=yes + fi + ;; + +os2*) + libname_spec='$name' + shrext_cmds=".dll" + need_lib_prefix=no + library_names_spec='$libname${shared_ext} $libname.a' + dynamic_linker='OS/2 ld.exe' + shlibpath_var=LIBPATH + ;; + +osf3* | osf4* | osf5*) + version_type=osf + need_lib_prefix=no + need_version=no + soname_spec='${libname}${release}${shared_ext}$major' + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + sys_lib_search_path_spec="/usr/shlib /usr/ccs/lib /usr/lib/cmplrs/cc /usr/lib /usr/local/lib /var/shlib" + sys_lib_dlsearch_path_spec="$sys_lib_search_path_spec" + ;; + +rdos*) + dynamic_linker=no + ;; + +solaris*) + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + # ldd complains unless libraries are executable + postinstall_cmds='chmod +x $lib' + ;; + +sunos4*) + version_type=sunos + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${shared_ext}$versuffix' + finish_cmds='PATH="\$PATH:/usr/etc" ldconfig $libdir' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + if test "$with_gnu_ld" = yes; then + need_lib_prefix=no + fi + need_version=yes + ;; + +sysv4 | sysv4.3*) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + case $host_vendor in + sni) + shlibpath_overrides_runpath=no + need_lib_prefix=no + runpath_var=LD_RUN_PATH + ;; + siemens) + need_lib_prefix=no + ;; + motorola) + need_lib_prefix=no + need_version=no + shlibpath_overrides_runpath=no + sys_lib_search_path_spec='/lib /usr/lib /usr/ccs/lib' + ;; + esac + ;; + +sysv4*MP*) + if test -d /usr/nec ;then + version_type=linux + library_names_spec='$libname${shared_ext}.$versuffix $libname${shared_ext}.$major $libname${shared_ext}' + soname_spec='$libname${shared_ext}.$major' + shlibpath_var=LD_LIBRARY_PATH + fi + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + version_type=freebsd-elf + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext} $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=yes + hardcode_into_libs=yes + if test "$with_gnu_ld" = yes; then + sys_lib_search_path_spec='/usr/local/lib /usr/gnu/lib /usr/ccs/lib /usr/lib /lib' + else + sys_lib_search_path_spec='/usr/ccs/lib /usr/lib' + case $host_os in + sco3.2v5*) + sys_lib_search_path_spec="$sys_lib_search_path_spec /lib" + ;; + esac + fi + sys_lib_dlsearch_path_spec='/usr/lib' + ;; + +tpf*) + # TPF is a cross-target only. Preferred cross-host = GNU/Linux. + version_type=linux + need_lib_prefix=no + need_version=no + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + shlibpath_var=LD_LIBRARY_PATH + shlibpath_overrides_runpath=no + hardcode_into_libs=yes + ;; + +uts4*) + version_type=linux + library_names_spec='${libname}${release}${shared_ext}$versuffix ${libname}${release}${shared_ext}$major $libname${shared_ext}' + soname_spec='${libname}${release}${shared_ext}$major' + shlibpath_var=LD_LIBRARY_PATH + ;; + +*) + dynamic_linker=no + ;; +esac +AC_MSG_RESULT([$dynamic_linker]) +test "$dynamic_linker" = no && can_build_shared=no + +variables_saved_for_relink="PATH $shlibpath_var $runpath_var" +if test "$GCC" = yes; then + variables_saved_for_relink="$variables_saved_for_relink GCC_EXEC_PREFIX COMPILER_PATH LIBRARY_PATH" +fi + +if test "${lt_cv_sys_lib_search_path_spec+set}" = set; then + sys_lib_search_path_spec="$lt_cv_sys_lib_search_path_spec" +fi +if test "${lt_cv_sys_lib_dlsearch_path_spec+set}" = set; then + sys_lib_dlsearch_path_spec="$lt_cv_sys_lib_dlsearch_path_spec" +fi + +_LT_DECL([], [variables_saved_for_relink], [1], + [Variables whose values should be saved in libtool wrapper scripts and + restored at link time]) +_LT_DECL([], [need_lib_prefix], [0], + [Do we need the "lib" prefix for modules?]) +_LT_DECL([], [need_version], [0], [Do we need a version for libraries?]) +_LT_DECL([], [version_type], [0], [Library versioning type]) +_LT_DECL([], [runpath_var], [0], [Shared library runtime path variable]) +_LT_DECL([], [shlibpath_var], [0],[Shared library path variable]) +_LT_DECL([], [shlibpath_overrides_runpath], [0], + [Is shlibpath searched before the hard-coded library search path?]) +_LT_DECL([], [libname_spec], [1], [Format of library name prefix]) +_LT_DECL([], [library_names_spec], [1], + [[List of archive names. First name is the real one, the rest are links. + The last name is the one that the linker finds with -lNAME]]) +_LT_DECL([], [soname_spec], [1], + [[The coded name of the library, if different from the real name]]) +_LT_DECL([], [postinstall_cmds], [2], + [Command to use after installation of a shared archive]) +_LT_DECL([], [postuninstall_cmds], [2], + [Command to use after uninstallation of a shared archive]) +_LT_DECL([], [finish_cmds], [2], + [Commands used to finish a libtool library installation in a directory]) +_LT_DECL([], [finish_eval], [1], + [[As "finish_cmds", except a single script fragment to be evaled but + not shown]]) +_LT_DECL([], [hardcode_into_libs], [0], + [Whether we should hardcode library paths into libraries]) +_LT_DECL([], [sys_lib_search_path_spec], [2], + [Compile-time system search path for libraries]) +_LT_DECL([], [sys_lib_dlsearch_path_spec], [2], + [Run-time system search path for libraries]) +])# _LT_SYS_DYNAMIC_LINKER + + +# _LT_PATH_TOOL_PREFIX(TOOL) +# -------------------------- +# find a file program which can recognize shared library +AC_DEFUN([_LT_PATH_TOOL_PREFIX], +[m4_require([_LT_DECL_EGREP])dnl +AC_MSG_CHECKING([for $1]) +AC_CACHE_VAL(lt_cv_path_MAGIC_CMD, +[case $MAGIC_CMD in +[[\\/*] | ?:[\\/]*]) + lt_cv_path_MAGIC_CMD="$MAGIC_CMD" # Let the user override the test with a path. + ;; +*) + lt_save_MAGIC_CMD="$MAGIC_CMD" + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR +dnl $ac_dummy forces splitting on constant user-supplied paths. +dnl POSIX.2 word splitting is done only on the output of word expansions, +dnl not every word. This closes a longstanding sh security hole. + ac_dummy="m4_if([$2], , $PATH, [$2])" + for ac_dir in $ac_dummy; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f $ac_dir/$1; then + lt_cv_path_MAGIC_CMD="$ac_dir/$1" + if test -n "$file_magic_test_file"; then + case $deplibs_check_method in + "file_magic "*) + file_magic_regex=`expr "$deplibs_check_method" : "file_magic \(.*\)"` + MAGIC_CMD="$lt_cv_path_MAGIC_CMD" + if eval $file_magic_cmd \$file_magic_test_file 2> /dev/null | + $EGREP "$file_magic_regex" > /dev/null; then + : + else + cat <<_LT_EOF 1>&2 + +*** Warning: the command libtool uses to detect shared libraries, +*** $file_magic_cmd, produces output that libtool cannot recognize. +*** The result is that libtool may fail to recognize shared libraries +*** as such. This will affect the creation of libtool libraries that +*** depend on shared libraries, but programs linked with such libtool +*** libraries will work regardless of this problem. Nevertheless, you +*** may want to report the problem to your system manager and/or to +*** bug-libtool@gnu.org + +_LT_EOF + fi ;; + esac + fi + break + fi + done + IFS="$lt_save_ifs" + MAGIC_CMD="$lt_save_MAGIC_CMD" + ;; +esac]) +MAGIC_CMD="$lt_cv_path_MAGIC_CMD" +if test -n "$MAGIC_CMD"; then + AC_MSG_RESULT($MAGIC_CMD) +else + AC_MSG_RESULT(no) +fi +_LT_DECL([], [MAGIC_CMD], [0], + [Used to examine libraries when file_magic_cmd begins with "file"])dnl +])# _LT_PATH_TOOL_PREFIX + +# Old name: +AU_ALIAS([AC_PATH_TOOL_PREFIX], [_LT_PATH_TOOL_PREFIX]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_PATH_TOOL_PREFIX], []) + + +# _LT_PATH_MAGIC +# -------------- +# find a file program which can recognize a shared library +m4_defun([_LT_PATH_MAGIC], +[_LT_PATH_TOOL_PREFIX(${ac_tool_prefix}file, /usr/bin$PATH_SEPARATOR$PATH) +if test -z "$lt_cv_path_MAGIC_CMD"; then + if test -n "$ac_tool_prefix"; then + _LT_PATH_TOOL_PREFIX(file, /usr/bin$PATH_SEPARATOR$PATH) + else + MAGIC_CMD=: + fi +fi +])# _LT_PATH_MAGIC + + +# LT_PATH_LD +# ---------- +# find the pathname to the GNU or non-GNU linker +AC_DEFUN([LT_PATH_LD], +[AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_CANONICAL_BUILD])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_DECL_EGREP])dnl + +AC_ARG_WITH([gnu-ld], + [AS_HELP_STRING([--with-gnu-ld], + [assume the C compiler uses GNU ld @<:@default=no@:>@])], + [test "$withval" = no || with_gnu_ld=yes], + [with_gnu_ld=no])dnl + +ac_prog=ld +if test "$GCC" = yes; then + # Check if gcc -print-prog-name=ld gives a path. + AC_MSG_CHECKING([for ld used by $CC]) + case $host in + *-*-mingw*) + # gcc leaves a trailing carriage return which upsets mingw + ac_prog=`($CC -print-prog-name=ld) 2>&5 | tr -d '\015'` ;; + *) + ac_prog=`($CC -print-prog-name=ld) 2>&5` ;; + esac + case $ac_prog in + # Accept absolute paths. + [[\\/]]* | ?:[[\\/]]*) + re_direlt='/[[^/]][[^/]]*/\.\./' + # Canonicalize the pathname of ld + ac_prog=`$ECHO "$ac_prog"| $SED 's%\\\\%/%g'` + while $ECHO "$ac_prog" | $GREP "$re_direlt" > /dev/null 2>&1; do + ac_prog=`$ECHO $ac_prog| $SED "s%$re_direlt%/%"` + done + test -z "$LD" && LD="$ac_prog" + ;; + "") + # If it fails, then pretend we aren't using GCC. + ac_prog=ld + ;; + *) + # If it is relative, then search for the first ld in PATH. + with_gnu_ld=unknown + ;; + esac +elif test "$with_gnu_ld" = yes; then + AC_MSG_CHECKING([for GNU ld]) +else + AC_MSG_CHECKING([for non-GNU ld]) +fi +AC_CACHE_VAL(lt_cv_path_LD, +[if test -z "$LD"; then + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + if test -f "$ac_dir/$ac_prog" || test -f "$ac_dir/$ac_prog$ac_exeext"; then + lt_cv_path_LD="$ac_dir/$ac_prog" + # Check to see if the program is GNU ld. I'd rather use --version, + # but apparently some variants of GNU ld only accept -v. + # Break only if it was the GNU/non-GNU ld that we prefer. + case `"$lt_cv_path_LD" -v 2>&1 &1 /dev/null 2>&1; then + lt_cv_deplibs_check_method='file_magic ^x86 archive import|^x86 DLL' + lt_cv_file_magic_cmd='func_win32_libid' + else + lt_cv_deplibs_check_method='file_magic file format pei*-i386(.*architecture: i386)?' + lt_cv_file_magic_cmd='$OBJDUMP -f' + fi + ;; + +cegcc) + # use the weaker test based on 'objdump'. See mingw*. + lt_cv_deplibs_check_method='file_magic file format pe-arm-.*little(.*architecture: arm)?' + lt_cv_file_magic_cmd='$OBJDUMP -f' + ;; + +darwin* | rhapsody*) + lt_cv_deplibs_check_method=pass_all + ;; + +freebsd* | dragonfly*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + case $host_cpu in + i*86 ) + # Not sure whether the presence of OpenBSD here was a mistake. + # Let's accept both of them until this is cleared up. + lt_cv_deplibs_check_method='file_magic (FreeBSD|OpenBSD|DragonFly)/i[[3-9]]86 (compact )?demand paged shared library' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so.*` + ;; + esac + else + lt_cv_deplibs_check_method=pass_all + fi + ;; + +gnu*) + lt_cv_deplibs_check_method=pass_all + ;; + +hpux10.20* | hpux11*) + lt_cv_file_magic_cmd=/usr/bin/file + case $host_cpu in + ia64*) + lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|ELF-[[0-9]][[0-9]]) shared object file - IA64' + lt_cv_file_magic_test_file=/usr/lib/hpux32/libc.so + ;; + hppa*64*) + [lt_cv_deplibs_check_method='file_magic (s[0-9][0-9][0-9]|ELF-[0-9][0-9]) shared object file - PA-RISC [0-9].[0-9]'] + lt_cv_file_magic_test_file=/usr/lib/pa20_64/libc.sl + ;; + *) + lt_cv_deplibs_check_method='file_magic (s[[0-9]][[0-9]][[0-9]]|PA-RISC[[0-9]].[[0-9]]) shared library' + lt_cv_file_magic_test_file=/usr/lib/libc.sl + ;; + esac + ;; + +interix[[3-9]]*) + # PIC code is broken on Interix 3.x, that's why |\.a not |_pic\.a here + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|\.a)$' + ;; + +irix5* | irix6* | nonstopux*) + case $LD in + *-32|*"-32 ") libmagic=32-bit;; + *-n32|*"-n32 ") libmagic=N32;; + *-64|*"-64 ") libmagic=64-bit;; + *) libmagic=never-match;; + esac + lt_cv_deplibs_check_method=pass_all + ;; + +# This must be Linux ELF. +linux* | k*bsd*-gnu | kopensolaris*-gnu) + lt_cv_deplibs_check_method=pass_all + ;; + +netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ > /dev/null; then + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so|_pic\.a)$' + fi + ;; + +newos6*) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (executable|dynamic lib)' + lt_cv_file_magic_cmd=/usr/bin/file + lt_cv_file_magic_test_file=/usr/lib/libnls.so + ;; + +*nto* | *qnx*) + lt_cv_deplibs_check_method=pass_all + ;; + +openbsd*) + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|\.so|_pic\.a)$' + else + lt_cv_deplibs_check_method='match_pattern /lib[[^/]]+(\.so\.[[0-9]]+\.[[0-9]]+|_pic\.a)$' + fi + ;; + +osf3* | osf4* | osf5*) + lt_cv_deplibs_check_method=pass_all + ;; + +rdos*) + lt_cv_deplibs_check_method=pass_all + ;; + +solaris*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*) + lt_cv_deplibs_check_method=pass_all + ;; + +sysv4 | sysv4.3*) + case $host_vendor in + motorola) + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[ML]]SB (shared object|dynamic lib) M[[0-9]][[0-9]]* Version [[0-9]]' + lt_cv_file_magic_test_file=`echo /usr/lib/libc.so*` + ;; + ncr) + lt_cv_deplibs_check_method=pass_all + ;; + sequent) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method='file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB (shared object|dynamic lib )' + ;; + sni) + lt_cv_file_magic_cmd='/bin/file' + lt_cv_deplibs_check_method="file_magic ELF [[0-9]][[0-9]]*-bit [[LM]]SB dynamic lib" + lt_cv_file_magic_test_file=/lib/libc.so + ;; + siemens) + lt_cv_deplibs_check_method=pass_all + ;; + pc) + lt_cv_deplibs_check_method=pass_all + ;; + esac + ;; + +tpf*) + lt_cv_deplibs_check_method=pass_all + ;; +esac +]) +file_magic_cmd=$lt_cv_file_magic_cmd +deplibs_check_method=$lt_cv_deplibs_check_method +test -z "$deplibs_check_method" && deplibs_check_method=unknown + +_LT_DECL([], [deplibs_check_method], [1], + [Method to check whether dependent libraries are shared objects]) +_LT_DECL([], [file_magic_cmd], [1], + [Command to use when deplibs_check_method == "file_magic"]) +])# _LT_CHECK_MAGIC_METHOD + + +# LT_PATH_NM +# ---------- +# find the pathname to a BSD- or MS-compatible name lister +AC_DEFUN([LT_PATH_NM], +[AC_REQUIRE([AC_PROG_CC])dnl +AC_CACHE_CHECK([for BSD- or MS-compatible name lister (nm)], lt_cv_path_NM, +[if test -n "$NM"; then + # Let the user override the test. + lt_cv_path_NM="$NM" +else + lt_nm_to_check="${ac_tool_prefix}nm" + if test -n "$ac_tool_prefix" && test "$build" = "$host"; then + lt_nm_to_check="$lt_nm_to_check nm" + fi + for lt_tmp_nm in $lt_nm_to_check; do + lt_save_ifs="$IFS"; IFS=$PATH_SEPARATOR + for ac_dir in $PATH /usr/ccs/bin/elf /usr/ccs/bin /usr/ucb /bin; do + IFS="$lt_save_ifs" + test -z "$ac_dir" && ac_dir=. + tmp_nm="$ac_dir/$lt_tmp_nm" + if test -f "$tmp_nm" || test -f "$tmp_nm$ac_exeext" ; then + # Check to see if the nm accepts a BSD-compat flag. + # Adding the `sed 1q' prevents false positives on HP-UX, which says: + # nm: unknown option "B" ignored + # Tru64's nm complains that /dev/null is an invalid object file + case `"$tmp_nm" -B /dev/null 2>&1 | sed '1q'` in + */dev/null* | *'Invalid file or object type'*) + lt_cv_path_NM="$tmp_nm -B" + break + ;; + *) + case `"$tmp_nm" -p /dev/null 2>&1 | sed '1q'` in + */dev/null*) + lt_cv_path_NM="$tmp_nm -p" + break + ;; + *) + lt_cv_path_NM=${lt_cv_path_NM="$tmp_nm"} # keep the first match, but + continue # so that we can try to find one that supports BSD flags + ;; + esac + ;; + esac + fi + done + IFS="$lt_save_ifs" + done + : ${lt_cv_path_NM=no} +fi]) +if test "$lt_cv_path_NM" != "no"; then + NM="$lt_cv_path_NM" +else + # Didn't find any BSD compatible name lister, look for dumpbin. + AC_CHECK_TOOLS(DUMPBIN, ["dumpbin -symbols" "link -dump -symbols"], :) + AC_SUBST([DUMPBIN]) + if test "$DUMPBIN" != ":"; then + NM="$DUMPBIN" + fi +fi +test -z "$NM" && NM=nm +AC_SUBST([NM]) +_LT_DECL([], [NM], [1], [A BSD- or MS-compatible name lister])dnl + +AC_CACHE_CHECK([the name lister ($NM) interface], [lt_cv_nm_interface], + [lt_cv_nm_interface="BSD nm" + echo "int some_variable = 0;" > conftest.$ac_ext + (eval echo "\"\$as_me:__oline__: $ac_compile\"" >&AS_MESSAGE_LOG_FD) + (eval "$ac_compile" 2>conftest.err) + cat conftest.err >&AS_MESSAGE_LOG_FD + (eval echo "\"\$as_me:__oline__: $NM \\\"conftest.$ac_objext\\\"\"" >&AS_MESSAGE_LOG_FD) + (eval "$NM \"conftest.$ac_objext\"" 2>conftest.err > conftest.out) + cat conftest.err >&AS_MESSAGE_LOG_FD + (eval echo "\"\$as_me:__oline__: output\"" >&AS_MESSAGE_LOG_FD) + cat conftest.out >&AS_MESSAGE_LOG_FD + if $GREP 'External.*some_variable' conftest.out > /dev/null; then + lt_cv_nm_interface="MS dumpbin" + fi + rm -f conftest*]) +])# LT_PATH_NM + +# Old names: +AU_ALIAS([AM_PROG_NM], [LT_PATH_NM]) +AU_ALIAS([AC_PROG_NM], [LT_PATH_NM]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_PROG_NM], []) +dnl AC_DEFUN([AC_PROG_NM], []) + + +# LT_LIB_M +# -------- +# check for math library +AC_DEFUN([LT_LIB_M], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +LIBM= +case $host in +*-*-beos* | *-*-cygwin* | *-*-pw32* | *-*-darwin*) + # These system don't have libm, or don't need it + ;; +*-ncr-sysv4.3*) + AC_CHECK_LIB(mw, _mwvalidcheckl, LIBM="-lmw") + AC_CHECK_LIB(m, cos, LIBM="$LIBM -lm") + ;; +*) + AC_CHECK_LIB(m, cos, LIBM="-lm") + ;; +esac +AC_SUBST([LIBM]) +])# LT_LIB_M + +# Old name: +AU_ALIAS([AC_CHECK_LIBM], [LT_LIB_M]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_CHECK_LIBM], []) + + +# _LT_COMPILER_NO_RTTI([TAGNAME]) +# ------------------------------- +m4_defun([_LT_COMPILER_NO_RTTI], +[m4_require([_LT_TAG_COMPILER])dnl + +_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= + +if test "$GCC" = yes; then + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' + + _LT_COMPILER_OPTION([if $compiler supports -fno-rtti -fno-exceptions], + lt_cv_prog_compiler_rtti_exceptions, + [-fno-rtti -fno-exceptions], [], + [_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)="$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1) -fno-rtti -fno-exceptions"]) +fi +_LT_TAGDECL([no_builtin_flag], [lt_prog_compiler_no_builtin_flag], [1], + [Compiler flag to turn off builtin functions]) +])# _LT_COMPILER_NO_RTTI + + +# _LT_CMD_GLOBAL_SYMBOLS +# ---------------------- +m4_defun([_LT_CMD_GLOBAL_SYMBOLS], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +AC_REQUIRE([AC_PROG_CC])dnl +AC_REQUIRE([LT_PATH_NM])dnl +AC_REQUIRE([LT_PATH_LD])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_TAG_COMPILER])dnl + +# Check for command to grab the raw symbol name followed by C symbol from nm. +AC_MSG_CHECKING([command to parse $NM output from $compiler object]) +AC_CACHE_VAL([lt_cv_sys_global_symbol_pipe], +[ +# These are sane defaults that work on at least a few old systems. +# [They come from Ultrix. What could be older than Ultrix?!! ;)] + +# Character class describing NM global symbol codes. +symcode='[[BCDEGRST]]' + +# Regexp to match symbols that can be accessed directly from C. +sympat='\([[_A-Za-z]][[_A-Za-z0-9]]*\)' + +# Define system-specific variables. +case $host_os in +aix*) + symcode='[[BCDT]]' + ;; +cygwin* | mingw* | pw32* | cegcc*) + symcode='[[ABCDGISTW]]' + ;; +hpux*) + if test "$host_cpu" = ia64; then + symcode='[[ABCDEGRST]]' + fi + ;; +irix* | nonstopux*) + symcode='[[BCDEGRST]]' + ;; +osf*) + symcode='[[BCDEGQRST]]' + ;; +solaris*) + symcode='[[BDRT]]' + ;; +sco3.2v5*) + symcode='[[DT]]' + ;; +sysv4.2uw2*) + symcode='[[DT]]' + ;; +sysv5* | sco5v6* | unixware* | OpenUNIX*) + symcode='[[ABDT]]' + ;; +sysv4) + symcode='[[DFNSTU]]' + ;; +esac + +# If we're using GNU nm, then use its standard symbol codes. +case `$NM -V 2>&1` in +*GNU* | *'with BFD'*) + symcode='[[ABCDGIRSTW]]' ;; +esac + +# Transform an extracted symbol line into a proper C declaration. +# Some systems (esp. on ia64) link data and code symbols differently, +# so use this general approach. +lt_cv_sys_global_symbol_to_cdecl="sed -n -e 's/^T .* \(.*\)$/extern int \1();/p' -e 's/^$symcode* .* \(.*\)$/extern char \1;/p'" + +# Transform an extracted symbol line into symbol name and symbol address +lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p'" +lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([[^ ]]*\) $/ {\\\"\1\\\", (void *) 0},/p' -e 's/^$symcode* \([[^ ]]*\) \(lib[[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^$symcode* \([[^ ]]*\) \([[^ ]]*\)$/ {\"lib\2\", (void *) \&\2},/p'" + +# Handle CRLF in mingw tool chain +opt_cr= +case $build_os in +mingw*) + opt_cr=`$ECHO 'x\{0,1\}' | tr x '\015'` # option cr in regexp + ;; +esac + +# Try without a prefix underscore, then with it. +for ac_symprfx in "" "_"; do + + # Transform symcode, sympat, and symprfx into a raw symbol and a C symbol. + symxfrm="\\1 $ac_symprfx\\2 \\2" + + # Write the raw and C identifiers. + if test "$lt_cv_nm_interface" = "MS dumpbin"; then + # Fake it for dumpbin and say T for any non-static function + # and D for any global variable. + # Also find C++ and __fastcall symbols from MSVC++, + # which start with @ or ?. + lt_cv_sys_global_symbol_pipe="$AWK ['"\ +" {last_section=section; section=\$ 3};"\ +" /Section length .*#relocs.*(pick any)/{hide[last_section]=1};"\ +" \$ 0!~/External *\|/{next};"\ +" / 0+ UNDEF /{next}; / UNDEF \([^|]\)*()/{next};"\ +" {if(hide[section]) next};"\ +" {f=0}; \$ 0~/\(\).*\|/{f=1}; {printf f ? \"T \" : \"D \"};"\ +" {split(\$ 0, a, /\||\r/); split(a[2], s)};"\ +" s[1]~/^[@?]/{print s[1], s[1]; next};"\ +" s[1]~prfx {split(s[1],t,\"@\"); print t[1], substr(t[1],length(prfx))}"\ +" ' prfx=^$ac_symprfx]" + else + lt_cv_sys_global_symbol_pipe="sed -n -e 's/^.*[[ ]]\($symcode$symcode*\)[[ ]][[ ]]*$ac_symprfx$sympat$opt_cr$/$symxfrm/p'" + fi + + # Check to see that the pipe works correctly. + pipe_works=no + + rm -f conftest* + cat > conftest.$ac_ext <<_LT_EOF +#ifdef __cplusplus +extern "C" { +#endif +char nm_test_var; +void nm_test_func(void); +void nm_test_func(void){} +#ifdef __cplusplus +} +#endif +int main(){nm_test_var='a';nm_test_func();return(0);} +_LT_EOF + + if AC_TRY_EVAL(ac_compile); then + # Now try to grab the symbols. + nlist=conftest.nm + if AC_TRY_EVAL(NM conftest.$ac_objext \| $lt_cv_sys_global_symbol_pipe \> $nlist) && test -s "$nlist"; then + # Try sorting and uniquifying the output. + if sort "$nlist" | uniq > "$nlist"T; then + mv -f "$nlist"T "$nlist" + else + rm -f "$nlist"T + fi + + # Make sure that we snagged all the symbols we need. + if $GREP ' nm_test_var$' "$nlist" >/dev/null; then + if $GREP ' nm_test_func$' "$nlist" >/dev/null; then + cat <<_LT_EOF > conftest.$ac_ext +#ifdef __cplusplus +extern "C" { +#endif + +_LT_EOF + # Now generate the symbol file. + eval "$lt_cv_sys_global_symbol_to_cdecl"' < "$nlist" | $GREP -v main >> conftest.$ac_ext' + + cat <<_LT_EOF >> conftest.$ac_ext + +/* The mapping between symbol names and symbols. */ +const struct { + const char *name; + void *address; +} +lt__PROGRAM__LTX_preloaded_symbols[[]] = +{ + { "@PROGRAM@", (void *) 0 }, +_LT_EOF + $SED "s/^$symcode$symcode* \(.*\) \(.*\)$/ {\"\2\", (void *) \&\2},/" < "$nlist" | $GREP -v main >> conftest.$ac_ext + cat <<\_LT_EOF >> conftest.$ac_ext + {0, (void *) 0} +}; + +/* This works around a problem in FreeBSD linker */ +#ifdef FREEBSD_WORKAROUND +static const void *lt_preloaded_setup() { + return lt__PROGRAM__LTX_preloaded_symbols; +} +#endif + +#ifdef __cplusplus +} +#endif +_LT_EOF + # Now try linking the two files. + mv conftest.$ac_objext conftstm.$ac_objext + lt_save_LIBS="$LIBS" + lt_save_CFLAGS="$CFLAGS" + LIBS="conftstm.$ac_objext" + CFLAGS="$CFLAGS$_LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)" + if AC_TRY_EVAL(ac_link) && test -s conftest${ac_exeext}; then + pipe_works=yes + fi + LIBS="$lt_save_LIBS" + CFLAGS="$lt_save_CFLAGS" + else + echo "cannot find nm_test_func in $nlist" >&AS_MESSAGE_LOG_FD + fi + else + echo "cannot find nm_test_var in $nlist" >&AS_MESSAGE_LOG_FD + fi + else + echo "cannot run $lt_cv_sys_global_symbol_pipe" >&AS_MESSAGE_LOG_FD + fi + else + echo "$progname: failed program was:" >&AS_MESSAGE_LOG_FD + cat conftest.$ac_ext >&5 + fi + rm -rf conftest* conftst* + + # Do not use the global_symbol_pipe unless it works. + if test "$pipe_works" = yes; then + break + else + lt_cv_sys_global_symbol_pipe= + fi +done +]) +if test -z "$lt_cv_sys_global_symbol_pipe"; then + lt_cv_sys_global_symbol_to_cdecl= +fi +if test -z "$lt_cv_sys_global_symbol_pipe$lt_cv_sys_global_symbol_to_cdecl"; then + AC_MSG_RESULT(failed) +else + AC_MSG_RESULT(ok) +fi + +_LT_DECL([global_symbol_pipe], [lt_cv_sys_global_symbol_pipe], [1], + [Take the output of nm and produce a listing of raw symbols and C names]) +_LT_DECL([global_symbol_to_cdecl], [lt_cv_sys_global_symbol_to_cdecl], [1], + [Transform the output of nm in a proper C declaration]) +_LT_DECL([global_symbol_to_c_name_address], + [lt_cv_sys_global_symbol_to_c_name_address], [1], + [Transform the output of nm in a C name address pair]) +_LT_DECL([global_symbol_to_c_name_address_lib_prefix], + [lt_cv_sys_global_symbol_to_c_name_address_lib_prefix], [1], + [Transform the output of nm in a C name address pair when lib prefix is needed]) +]) # _LT_CMD_GLOBAL_SYMBOLS + + +# _LT_COMPILER_PIC([TAGNAME]) +# --------------------------- +m4_defun([_LT_COMPILER_PIC], +[m4_require([_LT_TAG_COMPILER])dnl +_LT_TAGVAR(lt_prog_compiler_wl, $1)= +_LT_TAGVAR(lt_prog_compiler_pic, $1)= +_LT_TAGVAR(lt_prog_compiler_static, $1)= + +AC_MSG_CHECKING([for $compiler option to produce PIC]) +m4_if([$1], [CXX], [ + # C++ specific cases for pic, static, wl, etc. + if test "$GXX" = yes; then + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + mingw* | cygwin* | os2* | pw32* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + ;; + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + ;; + *djgpp*) + # DJGPP does not support shared libraries at all + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + ;; + interix[[3-9]]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic + fi + ;; + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + else + case $host_os in + aix[[4-9]]*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + else + _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' + fi + ;; + chorus*) + case $cc_basename in + cxch68*) + # Green Hills C++ Compiler + # _LT_TAGVAR(lt_prog_compiler_static, $1)="--no_auto_instantiation -u __main -u __premain -u _abort -r $COOL_DIR/lib/libOrb.a $MVME_DIR/lib/CC/libC.a $MVME_DIR/lib/classix/libcx.s.a" + ;; + esac + ;; + dgux*) + case $cc_basename in + ec++*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + ;; + ghcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + *) + ;; + esac + ;; + freebsd* | dragonfly*) + # FreeBSD uses GNU C++ + ;; + hpux9* | hpux10* | hpux11*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' + if test "$host_cpu" != ia64; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + fi + ;; + aCC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + ;; + esac + ;; + *) + ;; + esac + ;; + interix*) + # This is c89, which is MS Visual C++ (no shared libs) + # Anyone wants to do a port? + ;; + irix5* | irix6* | nonstopux*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + # CC pic flag -KPIC is the default. + ;; + *) + ;; + esac + ;; + linux* | k*bsd*-gnu | kopensolaris*-gnu) + case $cc_basename in + KCC*) + # KAI C++ Compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + ecpc* ) + # old Intel C++ for x86_64 which still supported -KPIC. + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + icpc* ) + # Intel C++, used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + cxx*) + # Compaq C++ + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + xlc* | xlC*) + # IBM XL 8.0 on PPC + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + esac + ;; + esac + ;; + lynxos*) + ;; + m88k*) + ;; + mvs*) + case $cc_basename in + cxx*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-W c,exportall' + ;; + *) + ;; + esac + ;; + netbsd* | netbsdelf*-gnu) + ;; + *qnx* | *nto*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='--backend -Wl,' + ;; + RCC*) + # Rational C++ 2.4.1 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + cxx*) + # Digital/Compaq C++ + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # Make sure the PIC flag is empty. It appears that all Alpha + # Linux and Compaq Tru64 Unix objects are PIC. + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + *) + ;; + esac + ;; + psos*) + ;; + solaris*) + case $cc_basename in + CC*) + # Sun C++ 4.2, 5.x and Centerline C++ + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + ;; + gcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + ;; + *) + ;; + esac + ;; + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + lcc*) + # Lucid + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + ;; + *) + ;; + esac + ;; + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + case $cc_basename in + CC*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + esac + ;; + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + ;; + *) + ;; + esac + ;; + vxworks*) + ;; + *) + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + esac + fi +], +[ + if test "$GCC" = yes; then + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + + case $host_os in + aix*) + # All AIX code is PIC. + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + m68k) + # FIXME: we need at least 68020 code to build shared libraries, but + # adding the `-m68020' flag to GCC prevents building anything better, + # like `-m68040'. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-m68020 -resident32 -malways-restore-a4' + ;; + esac + ;; + + beos* | irix5* | irix6* | nonstopux* | osf3* | osf4* | osf5*) + # PIC is the default for these OSes. + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + # Although the cygwin gcc ignores -fPIC, still need this for old-style + # (--disable-auto-import) libraries + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + ;; + + darwin* | rhapsody*) + # PIC is the default on this platform + # Common symbols not allowed in MH_DYLIB files + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fno-common' + ;; + + hpux*) + # PIC is the default for 64-bit PA HP-UX, but not for 32-bit + # PA HP-UX. On IA64 HP-UX, PIC is the default but the pic flag + # sets the default TLS model and affects inlining. + case $host_cpu in + hppa*64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + ;; + + interix[[3-9]]*) + # Interix 3.x gcc -fpic/-fPIC options generate broken code. + # Instead, we relocate shared libraries at runtime. + ;; + + msdosdjgpp*) + # Just because we use GCC doesn't mean we suddenly get shared libraries + # on systems that don't support them. + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + enable_shared=no + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(lt_prog_compiler_pic, $1)=-Kconform_pic + fi + ;; + + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + ;; + esac + else + # PORTME Check for flag to pass linker flags through the system compiler. + case $host_os in + aix*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + if test "$host_cpu" = ia64; then + # AIX 5 now supports IA64 processor + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + else + _LT_TAGVAR(lt_prog_compiler_static, $1)='-bnso -bI:/lib/syscalls.exp' + fi + ;; + + mingw* | cygwin* | pw32* | os2* | cegcc*) + # This hack is so that the source file can tell whether it is being + # built for inclusion in a dll (and should export symbols for example). + m4_if([$1], [GCJ], [], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)='-DDLL_EXPORT']) + ;; + + hpux9* | hpux10* | hpux11*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # PIC is the default for IA64 HP-UX and 64-bit HP-UX, but + # not for PA HP-UX. + case $host_cpu in + hppa*64*|ia64*) + # +Z the default + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='+Z' + ;; + esac + # Is there a better lt_prog_compiler_static that works with the bundled CC? + _LT_TAGVAR(lt_prog_compiler_static, $1)='${wl}-a ${wl}archive' + ;; + + irix5* | irix6* | nonstopux*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # PIC (with -KPIC) is the default. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu) + case $cc_basename in + # old Intel for x86_64 which still supported -KPIC. + ecc*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + # icc used to be incompatible with GCC. + # ICC 10 doesn't accept -KPIC any more. + icc* | ifort*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-static' + ;; + # Lahey Fortran 8.1. + lf95*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='--shared' + _LT_TAGVAR(lt_prog_compiler_static, $1)='--static' + ;; + pgcc* | pgf77* | pgf90* | pgf95*) + # Portland Group compilers (*not* the Pentium gcc compiler, + # which looks to be a dead project) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + ccc*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # All Alpha code is PIC. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + xl*) + # IBM XL C 8.0/Fortran 10.1 on PPC + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-qpic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-qstaticlink' + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C 5.9 + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + ;; + *Sun\ F*) + # Sun Fortran 8.3 passes all unrecognized flags to the linker + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + _LT_TAGVAR(lt_prog_compiler_wl, $1)='' + ;; + esac + ;; + esac + ;; + + newsos6) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + *nto* | *qnx*) + # QNX uses GNU C++, but need to define -shared option too, otherwise + # it will coredump. + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-fPIC -shared' + ;; + + osf3* | osf4* | osf5*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + # All OSF/1 code is PIC. + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + rdos*) + _LT_TAGVAR(lt_prog_compiler_static, $1)='-non_shared' + ;; + + solaris*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + case $cc_basename in + f77* | f90* | f95*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ';; + *) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,';; + esac + ;; + + sunos4*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Qoption ld ' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-PIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + sysv4 | sysv4.2uw2* | sysv4.3*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + sysv4*MP*) + if test -d /usr/nec ;then + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-Kconform_pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + fi + ;; + + sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-KPIC' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + unicos*) + _LT_TAGVAR(lt_prog_compiler_wl, $1)='-Wl,' + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + + uts4*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)='-pic' + _LT_TAGVAR(lt_prog_compiler_static, $1)='-Bstatic' + ;; + + *) + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no + ;; + esac + fi +]) +case $host_os in + # For platforms which do not support PIC, -DPIC is meaningless: + *djgpp*) + _LT_TAGVAR(lt_prog_compiler_pic, $1)= + ;; + *) + _LT_TAGVAR(lt_prog_compiler_pic, $1)="$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])" + ;; +esac +AC_MSG_RESULT([$_LT_TAGVAR(lt_prog_compiler_pic, $1)]) +_LT_TAGDECL([wl], [lt_prog_compiler_wl], [1], + [How to pass a linker flag through the compiler]) + +# +# Check to make sure the PIC flag actually works. +# +if test -n "$_LT_TAGVAR(lt_prog_compiler_pic, $1)"; then + _LT_COMPILER_OPTION([if $compiler PIC flag $_LT_TAGVAR(lt_prog_compiler_pic, $1) works], + [_LT_TAGVAR(lt_cv_prog_compiler_pic_works, $1)], + [$_LT_TAGVAR(lt_prog_compiler_pic, $1)@&t@m4_if([$1],[],[ -DPIC],[m4_if([$1],[CXX],[ -DPIC],[])])], [], + [case $_LT_TAGVAR(lt_prog_compiler_pic, $1) in + "" | " "*) ;; + *) _LT_TAGVAR(lt_prog_compiler_pic, $1)=" $_LT_TAGVAR(lt_prog_compiler_pic, $1)" ;; + esac], + [_LT_TAGVAR(lt_prog_compiler_pic, $1)= + _LT_TAGVAR(lt_prog_compiler_can_build_shared, $1)=no]) +fi +_LT_TAGDECL([pic_flag], [lt_prog_compiler_pic], [1], + [Additional compiler flags for building library objects]) + +# +# Check to make sure the static flag actually works. +# +wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) eval lt_tmp_static_flag=\"$_LT_TAGVAR(lt_prog_compiler_static, $1)\" +_LT_LINKER_OPTION([if $compiler static flag $lt_tmp_static_flag works], + _LT_TAGVAR(lt_cv_prog_compiler_static_works, $1), + $lt_tmp_static_flag, + [], + [_LT_TAGVAR(lt_prog_compiler_static, $1)=]) +_LT_TAGDECL([link_static_flag], [lt_prog_compiler_static], [1], + [Compiler flag to prevent dynamic linking]) +])# _LT_COMPILER_PIC + + +# _LT_LINKER_SHLIBS([TAGNAME]) +# ---------------------------- +# See if the linker supports building shared libraries. +m4_defun([_LT_LINKER_SHLIBS], +[AC_REQUIRE([LT_PATH_LD])dnl +AC_REQUIRE([LT_PATH_NM])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_EGREP])dnl +m4_require([_LT_DECL_SED])dnl +m4_require([_LT_CMD_GLOBAL_SYMBOLS])dnl +m4_require([_LT_TAG_COMPILER])dnl +AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) +m4_if([$1], [CXX], [ + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + case $host_os in + aix[[4-9]]*) + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + else + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + fi + ;; + pw32*) + _LT_TAGVAR(export_symbols_cmds, $1)="$ltdll_cmds" + ;; + cygwin* | mingw* | cegcc*) + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/;/^.*[[ ]]__nm__/s/^.*[[ ]]__nm__\([[^ ]]*\)[[ ]][[^ ]]*/\1 DATA/;/^I[[ ]]/d;/^[[AITW]][[ ]]/s/.* //'\'' | sort | uniq > $export_symbols' + ;; + linux* | k*bsd*-gnu) + _LT_TAGVAR(link_all_deplibs, $1)=no + ;; + *) + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + ;; + esac + _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] +], [ + runpath_var= + _LT_TAGVAR(allow_undefined_flag, $1)= + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(archive_cmds, $1)= + _LT_TAGVAR(archive_expsym_cmds, $1)= + _LT_TAGVAR(compiler_needs_object, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + _LT_TAGVAR(export_dynamic_flag_spec, $1)= + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED '\''s/.* //'\'' | sort | uniq > $export_symbols' + _LT_TAGVAR(hardcode_automatic, $1)=no + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= + _LT_TAGVAR(hardcode_libdir_separator, $1)= + _LT_TAGVAR(hardcode_minus_L, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported + _LT_TAGVAR(inherit_rpath, $1)=no + _LT_TAGVAR(link_all_deplibs, $1)=unknown + _LT_TAGVAR(module_cmds, $1)= + _LT_TAGVAR(module_expsym_cmds, $1)= + _LT_TAGVAR(old_archive_from_new_cmds, $1)= + _LT_TAGVAR(old_archive_from_expsyms_cmds, $1)= + _LT_TAGVAR(thread_safe_flag_spec, $1)= + _LT_TAGVAR(whole_archive_flag_spec, $1)= + # include_expsyms should be a list of space-separated symbols to be *always* + # included in the symbol list + _LT_TAGVAR(include_expsyms, $1)= + # exclude_expsyms can be an extended regexp of symbols to exclude + # it will be wrapped by ` (' and `)$', so one must not match beginning or + # end of line. Example: `a|bc|.*d.*' will exclude the symbols `a' and `bc', + # as well as any symbol that contains `d'. + _LT_TAGVAR(exclude_expsyms, $1)=['_GLOBAL_OFFSET_TABLE_|_GLOBAL__F[ID]_.*'] + # Although _GLOBAL_OFFSET_TABLE_ is a valid symbol C name, most a.out + # platforms (ab)use it in PIC code, but their linkers get confused if + # the symbol is explicitly referenced. Since portable code cannot + # rely on this symbol name, it's probably fine to never include it in + # preloaded symbol tables. + # Exclude shared library initialization/finalization symbols. +dnl Note also adjust exclude_expsyms for C++ above. + extract_expsyms_cmds= + + case $host_os in + cygwin* | mingw* | pw32* | cegcc*) + # FIXME: the MSVC++ port hasn't been tested in a loooong time + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + if test "$GCC" != yes; then + with_gnu_ld=no + fi + ;; + interix*) + # we just hope/assume this is gcc and not c89 (= MSVC++) + with_gnu_ld=yes + ;; + openbsd*) + with_gnu_ld=no + ;; + linux* | k*bsd*-gnu) + _LT_TAGVAR(link_all_deplibs, $1)=no + ;; + esac + + _LT_TAGVAR(ld_shlibs, $1)=yes + if test "$with_gnu_ld" = yes; then + # If archive_cmds runs LD, not CC, wlarc should be empty + wlarc='${wl}' + + # Set some defaults for GNU ld with shared library support. These + # are reset later if shared libraries are not supported. Putting them + # here allows them to be overridden if necessary. + runpath_var=LD_RUN_PATH + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + # ancient GNU ld didn't support --whole-archive et. al. + if $LD --help 2>&1 | $GREP 'no-whole-archive' > /dev/null; then + _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + supports_anon_versioning=no + case `$LD -v 2>&1` in + *GNU\ gold*) supports_anon_versioning=yes ;; + *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.10.*) ;; # catch versions < 2.11 + *\ 2.11.93.0.2\ *) supports_anon_versioning=yes ;; # RH7.3 ... + *\ 2.11.92.0.12\ *) supports_anon_versioning=yes ;; # Mandrake 8.2 ... + *\ 2.11.*) ;; # other 2.11 versions + *) supports_anon_versioning=yes ;; + esac + + # See if GNU ld supports shared libraries. + case $host_os in + aix[[3-9]]*) + # On AIX/PPC, the GNU linker is very broken + if test "$host_cpu" != ia64; then + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: the GNU linker, at least up to release 2.9.1, is reported +*** to be unable to reliably create shared libraries on AIX. +*** Therefore, libtool is disabling shared libraries support. If you +*** really care for shared libraries, you may want to modify your PATH +*** so that a non-GNU linker is found, and then restart. + +_LT_EOF + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='' + ;; + m68k) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, + # as there is no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + _LT_TAGVAR(export_symbols_cmds, $1)='$NM $libobjs $convenience | $global_symbol_pipe | $SED -e '\''/^[[BCDGRS]][[ ]]/s/.*[[ ]]\([[^ ]]*\)/\1 DATA/'\'' | $SED -e '\''/^[[AITW]][[ ]]/s/.*[[ ]]//'\'' | sort | uniq > $export_symbols' + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared $output_objdir/$soname.def $libobjs $deplibs $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + interix[[3-9]]*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + + gnu* | linux* | tpf* | k*bsd*-gnu | kopensolaris*-gnu) + tmp_diet=no + if test "$host_os" = linux-dietlibc; then + case $cc_basename in + diet\ *) tmp_diet=yes;; # linux-dietlibc with static linking (!diet-dyn) + esac + fi + if $LD --help 2>&1 | $EGREP ': supported targets:.* elf' > /dev/null \ + && test "$tmp_diet" = no + then + tmp_addflag= + tmp_sharedflag='-shared' + case $cc_basename,$host_cpu in + pgcc*) # Portland Group C compiler + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' + tmp_addflag=' $pic_flag' + ;; + pgf77* | pgf90* | pgf95*) # Portland Group f77 and f90 compilers + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' + tmp_addflag=' $pic_flag -Mnomain' ;; + ecc*,ia64* | icc*,ia64*) # Intel C compiler on ia64 + tmp_addflag=' -i_dynamic' ;; + efc*,ia64* | ifort*,ia64*) # Intel Fortran compiler on ia64 + tmp_addflag=' -i_dynamic -nofor_main' ;; + ifc* | ifort*) # Intel Fortran compiler + tmp_addflag=' -nofor_main' ;; + lf95*) # Lahey Fortran 8.1 + _LT_TAGVAR(whole_archive_flag_spec, $1)= + tmp_sharedflag='--shared' ;; + xl[[cC]]*) # IBM XL C 8.0 on PPC (deal with xlf below) + tmp_sharedflag='-qmkshrobj' + tmp_addflag= ;; + esac + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) # Sun C 5.9 + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + tmp_sharedflag='-G' ;; + *Sun\ F*) # Sun Fortran 8.3 + tmp_sharedflag='-G' ;; + esac + _LT_TAGVAR(archive_cmds, $1)='$CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + + if test "x$supports_anon_versioning" = xyes; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC '"$tmp_sharedflag""$tmp_addflag"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + fi + + case $cc_basename in + xlf*) + # IBM XL Fortran 10.1 on PPC cannot create shared libs itself + _LT_TAGVAR(whole_archive_flag_spec, $1)='--whole-archive$convenience --no-whole-archive' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='-rpath $libdir' + _LT_TAGVAR(archive_cmds, $1)='$LD -shared $libobjs $deplibs $compiler_flags -soname $soname -o $lib' + if test "x$supports_anon_versioning" = xyes; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $LD -shared $libobjs $deplibs $compiler_flags -soname $soname -version-script $output_objdir/$libname.ver -o $lib' + fi + ;; + esac + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable $libobjs $deplibs $linker_flags -o $lib' + wlarc= + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + fi + ;; + + solaris*) + if $LD -v 2>&1 | $GREP 'BFD 2\.8' > /dev/null; then + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: The releases 2.8.* of the GNU linker cannot reliably +*** create shared libraries on Solaris systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.9.1 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + elif $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*) + case `$LD -v 2>&1` in + *\ [[01]].* | *\ 2.[[0-9]].* | *\ 2.1[[0-5]].*) + _LT_TAGVAR(ld_shlibs, $1)=no + cat <<_LT_EOF 1>&2 + +*** Warning: Releases of the GNU linker prior to 2.16.91.0.3 can not +*** reliably create shared libraries on SCO systems. Therefore, libtool +*** is disabling shared libraries support. We urge you to upgrade GNU +*** binutils to release 2.16.91.0.3 or newer. Another option is to modify +*** your PATH or compiler configuration so that the native linker is +*** used, and then restart. + +_LT_EOF + ;; + *) + # For security reasons, it is highly recommended that you always + # use absolute paths for naming shared libraries, and exclude the + # DT_RUNPATH tag from executables and libraries. But doing so + # requires that you compile everything twice, which is a pain. + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + sunos4*) + _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bshareable -o $lib $libobjs $deplibs $linker_flags' + wlarc= + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + + if test "$_LT_TAGVAR(ld_shlibs, $1)" = no; then + runpath_var= + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)= + _LT_TAGVAR(export_dynamic_flag_spec, $1)= + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + else + # PORTME fill in a description of your system's linker (not GNU ld) + case $host_os in + aix3*) + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=yes + _LT_TAGVAR(archive_expsym_cmds, $1)='$LD -o $output_objdir/$soname $libobjs $deplibs $linker_flags -bE:$export_symbols -T512 -H512 -bM:SRE~$AR $AR_FLAGS $lib $output_objdir/$soname' + # Note: this linker hardcodes the directories in LIBPATH if there + # are no directories specified by -L. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + if test "$GCC" = yes && test -z "$lt_prog_compiler_static"; then + # Neither direct hardcoding nor static linking is supported with a + # broken collect2. + _LT_TAGVAR(hardcode_direct, $1)=unsupported + fi + ;; + + aix[[4-9]]*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + # If we're using GNU nm, then we don't want the "-C" option. + # -C means demangle to AIX nm, but means don't demangle with GNU nm + if $NM -V 2>&1 | $GREP 'GNU' > /dev/null; then + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -Bpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + else + _LT_TAGVAR(export_symbols_cmds, $1)='$NM -BCpg $libobjs $convenience | awk '\''{ if (((\$ 2 == "T") || (\$ 2 == "D") || (\$ 2 == "B")) && ([substr](\$ 3,1,1) != ".")) { print \$ 3 } }'\'' | sort -u > $export_symbols' + fi + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) + for ld_flag in $LDFLAGS; do + if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then + aix_use_runtimelinking=yes + break + fi + done + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + _LT_TAGVAR(archive_cmds, $1)='' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' + + if test "$GCC" = yes; then + case $host_os in aix4.[[012]]|aix4.[[012]].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + _LT_TAGVAR(hardcode_direct, $1)=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)= + fi + ;; + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then + shared_flag="$shared_flag "'${wl}-G' + fi + _LT_TAGVAR(link_all_deplibs, $1)=no + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to export. + _LT_TAGVAR(always_export_symbols, $1)=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(allow_undefined_flag, $1)='-berok' + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' + _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' + # Exported symbols can be pulled into shared objects from archives + _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + # This is similar to how AIX traditionally builds its shared libraries. + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + + amigaos*) + case $host_cpu in + powerpc) + # see comment about AmigaOS4 .so support + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='' + ;; + m68k) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/a2ixlibrary.data~$ECHO "#define NAME $libname" > $output_objdir/a2ixlibrary.data~$ECHO "#define LIBRARY_ID 1" >> $output_objdir/a2ixlibrary.data~$ECHO "#define VERSION $major" >> $output_objdir/a2ixlibrary.data~$ECHO "#define REVISION $revision" >> $output_objdir/a2ixlibrary.data~$AR $AR_FLAGS $lib $libobjs~$RANLIB $lib~(cd $output_objdir && a2ixlibrary -32)' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + ;; + + bsdi[[45]]*) + _LT_TAGVAR(export_dynamic_flag_spec, $1)=-rdynamic + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # When not using gcc, we currently assume that we are using + # Microsoft Visual C++. + # hardcode_libdir_flag_spec is actually meaningless, as there is + # no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)=' ' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Tell ltmain to make .lib files, not .a files. + libext=lib + # Tell ltmain to make .dll files, not .so files. + shrext_cmds=".dll" + # FIXME: Setting linknames here is a bad hack. + _LT_TAGVAR(archive_cmds, $1)='$CC -o $lib $libobjs $compiler_flags `$ECHO "X$deplibs" | $Xsed -e '\''s/ -lc$//'\''` -link -dll~linknames=' + # The linker will automatically build a .lib file if we build a DLL. + _LT_TAGVAR(old_archive_from_new_cmds, $1)='true' + # FIXME: Should let the user specify the lib program. + _LT_TAGVAR(old_archive_cmds, $1)='lib -OUT:$oldlib$oldobjs$old_deplibs' + _LT_TAGVAR(fix_srcfile_path, $1)='`cygpath -w "$srcfile"`' + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + ;; + + darwin* | rhapsody*) + _LT_DARWIN_LINKER_FEATURES($1) + ;; + + dgux*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + freebsd1*) + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + # FreeBSD 2.2.[012] allows us to include c++rt0.o to get C++ constructor + # support. Future versions do this automatically, but an explicit c++rt0.o + # does not break anything, and helps significantly (at the cost of a little + # extra space). + freebsd2.2*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags /usr/lib/c++rt0.o' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # Unfortunately, older versions of FreeBSD 2 do not have this feature. + freebsd2*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + # FreeBSD 3 and greater uses gcc -shared to do shared libraries. + freebsd* | dragonfly*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + hpux9*) + if test "$GCC" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $libobjs $deplibs $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$LD -b +b $install_libdir -o $output_objdir/$soname $libobjs $deplibs $linker_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_direct, $1)=yes + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + ;; + + hpux10*) + if test "$GCC" = yes -a "$with_gnu_ld" = no; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$LD -b +h $soname +b $install_libdir -o $lib $libobjs $deplibs $linker_flags' + fi + if test "$with_gnu_ld" = no; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)='+b $libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + fi + ;; + + hpux11*) + if test "$GCC" = yes -a "$with_gnu_ld" = no; then + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + else + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + fi + if test "$with_gnu_ld" = no; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + case $host_cpu in + hppa*64*|ia64*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + *) + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + + # hardcode_minus_L: Not really in the search PATH, + # but as the default location of the library. + _LT_TAGVAR(hardcode_minus_L, $1)=yes + ;; + esac + fi + ;; + + irix5* | irix6* | nonstopux*) + if test "$GCC" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + # Try to use the -exported_symbol ld option, if it does not + # work, assume that -exports_file does not work either and + # implicitly export all symbols. + save_LDFLAGS="$LDFLAGS" + LDFLAGS="$LDFLAGS -shared ${wl}-exported_symbol ${wl}foo ${wl}-update_registry ${wl}/dev/null" + AC_LINK_IFELSE(int foo(void) {}, + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations ${wl}-exports_file ${wl}$export_symbols -o $lib' + ) + LDFLAGS="$save_LDFLAGS" + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -exports_file $export_symbols -o $lib' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(inherit_rpath, $1)=yes + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + netbsd* | netbsdelf*-gnu) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' # a.out + else + _LT_TAGVAR(archive_cmds, $1)='$LD -shared -o $lib $libobjs $deplibs $linker_flags' # ELF + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + newsos6) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *nto* | *qnx*) + ;; + + openbsd*) + if test -f /usr/libexec/ld.so; then + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + if test -z "`echo __ELF__ | $CC -E - | $GREP __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags ${wl}-retain-symbols-file,$export_symbols' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + else + case $host_os in + openbsd[[01]].* | openbsd2.[[0-7]] | openbsd2.[[0-7]].*) + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + ;; + esac + fi + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + os2*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(archive_cmds, $1)='$ECHO "LIBRARY $libname INITINSTANCE" > $output_objdir/$libname.def~$ECHO "DESCRIPTION \"$libname\"" >> $output_objdir/$libname.def~$ECHO DATA >> $output_objdir/$libname.def~$ECHO " SINGLE NONSHARED" >> $output_objdir/$libname.def~$ECHO EXPORTS >> $output_objdir/$libname.def~emxexp $libobjs >> $output_objdir/$libname.def~$CC -Zdll -Zcrtdll -o $lib $libobjs $deplibs $compiler_flags $output_objdir/$libname.def' + _LT_TAGVAR(old_archive_from_new_cmds, $1)='emximp -o $output_objdir/$libname.a $output_objdir/$libname.def' + ;; + + osf3*) + if test "$GCC" = yes; then + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + + osf4* | osf5*) # as osf3* with the addition of -msym flag + if test "$GCC" = yes; then + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + else + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $libobjs $deplibs $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done; printf "%s\\n" "-hidden">> $lib.exp~ + $CC -shared${allow_undefined_flag} ${wl}-input ${wl}$lib.exp $compiler_flags $libobjs $deplibs -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~$RM $lib.exp' + + # Both c and cxx compiler support -rpath directly + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + fi + _LT_TAGVAR(archive_cmds_need_lc, $1)='no' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + ;; + + solaris*) + _LT_TAGVAR(no_undefined_flag, $1)=' -z defs' + if test "$GCC" = yes; then + wlarc='${wl}' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-z ${wl}text ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared ${wl}-z ${wl}text ${wl}-M ${wl}$lib.exp ${wl}-h ${wl}$soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + else + case `$CC -V 2>&1` in + *"Compilers 5.0"*) + wlarc='' + _LT_TAGVAR(archive_cmds, $1)='$LD -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $LD -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $linker_flags~$RM $lib.exp' + ;; + *) + wlarc='${wl}' + _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h $soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G${allow_undefined_flag} -M $lib.exp -h $soname -o $lib $libobjs $deplibs $compiler_flags~$RM $lib.exp' + ;; + esac + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands `-z linker_flag'. GCC discards it without `$wl', + # but is careful enough not to reorder. + # Supported since Solaris 2.6 (maybe 2.5.1?) + if test "$GCC" = yes; then + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' + fi + ;; + esac + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + + sunos4*) + if test "x$host_vendor" = xsequent; then + # Use $CC to link under sequent, because it throws in some extra .o + # files that make .init and .fini sections work. + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h $soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$LD -assert pure-text -Bstatic -o $lib $libobjs $deplibs $linker_flags' + fi + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + sysv4) + case $host_vendor in + sni) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=yes # is this really true??? + ;; + siemens) + ## LD is ld it makes a PLAMLIB + ## CC just makes a GrossModule. + _LT_TAGVAR(archive_cmds, $1)='$LD -G -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(reload_cmds, $1)='$CC -r -o $output$reload_objs' + _LT_TAGVAR(hardcode_direct, $1)=no + ;; + motorola) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_direct, $1)=no #Motorola manual says yes, but my tests say they lie + ;; + esac + runpath_var='LD_RUN_PATH' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + sysv4.3*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(export_dynamic_flag_spec, $1)='-Bexport' + ;; + + sysv4*MP*) + if test -d /usr/nec; then + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var=LD_RUN_PATH + hardcode_runpath_var=yes + _LT_TAGVAR(ld_shlibs, $1)=yes + fi + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) + _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var='LD_RUN_PATH' + + if test "$GCC" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We can NOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' + _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' + runpath_var='LD_RUN_PATH' + + if test "$GCC" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + fi + ;; + + uts4*) + _LT_TAGVAR(archive_cmds, $1)='$LD -G -h $soname -o $lib $libobjs $deplibs $linker_flags' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + + *) + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + + if test x$host_vendor = xsni; then + case $host in + sysv4 | sysv4.2uw2* | sysv4.3* | sysv5*) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Blargedynsym' + ;; + esac + fi + fi +]) +AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) +test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no + +_LT_TAGVAR(with_gnu_ld, $1)=$with_gnu_ld + +_LT_DECL([], [libext], [0], [Old archive suffix (normally "a")])dnl +_LT_DECL([], [shrext_cmds], [1], [Shared library suffix (normally ".so")])dnl +_LT_DECL([], [extract_expsyms_cmds], [2], + [The commands to extract the exported symbol list from a shared archive]) + +# +# Do we need to explicitly link libc? +# +case "x$_LT_TAGVAR(archive_cmds_need_lc, $1)" in +x|xyes) + # Assume -lc should be added + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + + if test "$enable_shared" = yes && test "$GCC" = yes; then + case $_LT_TAGVAR(archive_cmds, $1) in + *'~'*) + # FIXME: we may have to deal with multi-command sequences. + ;; + '$CC '*) + # Test whether the compiler implicitly links with -lc since on some + # systems, -lgcc has to come before -lc. If gcc already passes -lc + # to ld, don't add -lc before -lgcc. + AC_MSG_CHECKING([whether -lc should be explicitly linked in]) + $RM conftest* + echo "$lt_simple_compile_test_code" > conftest.$ac_ext + + if AC_TRY_EVAL(ac_compile) 2>conftest.err; then + soname=conftest + lib=conftest + libobjs=conftest.$ac_objext + deplibs= + wl=$_LT_TAGVAR(lt_prog_compiler_wl, $1) + pic_flag=$_LT_TAGVAR(lt_prog_compiler_pic, $1) + compiler_flags=-v + linker_flags=-v + verstring= + output_objdir=. + libname=conftest + lt_save_allow_undefined_flag=$_LT_TAGVAR(allow_undefined_flag, $1) + _LT_TAGVAR(allow_undefined_flag, $1)= + if AC_TRY_EVAL(_LT_TAGVAR(archive_cmds, $1) 2\>\&1 \| $GREP \" -lc \" \>/dev/null 2\>\&1) + then + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + else + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + fi + _LT_TAGVAR(allow_undefined_flag, $1)=$lt_save_allow_undefined_flag + else + cat conftest.err 1>&5 + fi + $RM conftest* + AC_MSG_RESULT([$_LT_TAGVAR(archive_cmds_need_lc, $1)]) + ;; + esac + fi + ;; +esac + +_LT_TAGDECL([build_libtool_need_lc], [archive_cmds_need_lc], [0], + [Whether or not to add -lc for building shared libraries]) +_LT_TAGDECL([allow_libtool_libs_with_static_runtimes], + [enable_shared_with_static_runtimes], [0], + [Whether or not to disallow shared libs when runtime libs are static]) +_LT_TAGDECL([], [export_dynamic_flag_spec], [1], + [Compiler flag to allow reflexive dlopens]) +_LT_TAGDECL([], [whole_archive_flag_spec], [1], + [Compiler flag to generate shared objects directly from archives]) +_LT_TAGDECL([], [compiler_needs_object], [1], + [Whether the compiler copes with passing no objects directly]) +_LT_TAGDECL([], [old_archive_from_new_cmds], [2], + [Create an old-style archive from a shared archive]) +_LT_TAGDECL([], [old_archive_from_expsyms_cmds], [2], + [Create a temporary old-style archive to link instead of a shared archive]) +_LT_TAGDECL([], [archive_cmds], [2], [Commands used to build a shared archive]) +_LT_TAGDECL([], [archive_expsym_cmds], [2]) +_LT_TAGDECL([], [module_cmds], [2], + [Commands used to build a loadable module if different from building + a shared archive.]) +_LT_TAGDECL([], [module_expsym_cmds], [2]) +_LT_TAGDECL([], [with_gnu_ld], [1], + [Whether we are building with GNU ld or not]) +_LT_TAGDECL([], [allow_undefined_flag], [1], + [Flag that allows shared libraries with undefined symbols to be built]) +_LT_TAGDECL([], [no_undefined_flag], [1], + [Flag that enforces no undefined symbols]) +_LT_TAGDECL([], [hardcode_libdir_flag_spec], [1], + [Flag to hardcode $libdir into a binary during linking. + This must work even if $libdir does not exist]) +_LT_TAGDECL([], [hardcode_libdir_flag_spec_ld], [1], + [[If ld is used when linking, flag to hardcode $libdir into a binary + during linking. This must work even if $libdir does not exist]]) +_LT_TAGDECL([], [hardcode_libdir_separator], [1], + [Whether we need a single "-rpath" flag with a separated argument]) +_LT_TAGDECL([], [hardcode_direct], [0], + [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes + DIR into the resulting binary]) +_LT_TAGDECL([], [hardcode_direct_absolute], [0], + [Set to "yes" if using DIR/libNAME${shared_ext} during linking hardcodes + DIR into the resulting binary and the resulting library dependency is + "absolute", i.e impossible to change by setting ${shlibpath_var} if the + library is relocated]) +_LT_TAGDECL([], [hardcode_minus_L], [0], + [Set to "yes" if using the -LDIR flag during linking hardcodes DIR + into the resulting binary]) +_LT_TAGDECL([], [hardcode_shlibpath_var], [0], + [Set to "yes" if using SHLIBPATH_VAR=DIR during linking hardcodes DIR + into the resulting binary]) +_LT_TAGDECL([], [hardcode_automatic], [0], + [Set to "yes" if building a shared library automatically hardcodes DIR + into the library and all subsequent libraries and executables linked + against it]) +_LT_TAGDECL([], [inherit_rpath], [0], + [Set to yes if linker adds runtime paths of dependent libraries + to runtime path list]) +_LT_TAGDECL([], [link_all_deplibs], [0], + [Whether libtool must link a program against all its dependency libraries]) +_LT_TAGDECL([], [fix_srcfile_path], [1], + [Fix the shell variable $srcfile for the compiler]) +_LT_TAGDECL([], [always_export_symbols], [0], + [Set to "yes" if exported symbols are required]) +_LT_TAGDECL([], [export_symbols_cmds], [2], + [The commands to list exported symbols]) +_LT_TAGDECL([], [exclude_expsyms], [1], + [Symbols that should not be listed in the preloaded symbols]) +_LT_TAGDECL([], [include_expsyms], [1], + [Symbols that must always be exported]) +_LT_TAGDECL([], [prelink_cmds], [2], + [Commands necessary for linking programs (against libraries) with templates]) +_LT_TAGDECL([], [file_list_spec], [1], + [Specify filename containing input files]) +dnl FIXME: Not yet implemented +dnl _LT_TAGDECL([], [thread_safe_flag_spec], [1], +dnl [Compiler flag to generate thread safe objects]) +])# _LT_LINKER_SHLIBS + + +# _LT_LANG_C_CONFIG([TAG]) +# ------------------------ +# Ensure that the configuration variables for a C compiler are suitably +# defined. These variables are subsequently used by _LT_CONFIG to write +# the compiler configuration to `libtool'. +m4_defun([_LT_LANG_C_CONFIG], +[m4_require([_LT_DECL_EGREP])dnl +lt_save_CC="$CC" +AC_LANG_PUSH(C) + +# Source file extension for C test sources. +ac_ext=c + +# Object file extension for compiled C test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="int some_variable = 0;" + +# Code to be used in simple link tests +lt_simple_link_test_code='int main(){return(0);}' + +_LT_TAG_COMPILER +# Save the default compiler, since it gets overwritten when the other +# tags are being tested, and _LT_TAGVAR(compiler, []) is a NOP. +compiler_DEFAULT=$CC + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + LT_SYS_DLOPEN_SELF + _LT_CMD_STRIPLIB + + # Report which library types will actually be built + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test "$can_build_shared" = "no" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + + aix[[4-9]]*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test "$enable_shared" = yes || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_CONFIG($1) +fi +AC_LANG_POP +CC="$lt_save_CC" +])# _LT_LANG_C_CONFIG + + +# _LT_PROG_CXX +# ------------ +# Since AC_PROG_CXX is broken, in that it returns g++ if there is no c++ +# compiler, we have our own version here. +m4_defun([_LT_PROG_CXX], +[ +pushdef([AC_MSG_ERROR], [_lt_caught_CXX_error=yes]) +AC_PROG_CXX +if test -n "$CXX" && ( test "X$CXX" != "Xno" && + ( (test "X$CXX" = "Xg++" && `g++ -v >/dev/null 2>&1` ) || + (test "X$CXX" != "Xg++"))) ; then + AC_PROG_CXXCPP +else + _lt_caught_CXX_error=yes +fi +popdef([AC_MSG_ERROR]) +])# _LT_PROG_CXX + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([_LT_PROG_CXX], []) + + +# _LT_LANG_CXX_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for a C++ compiler are suitably +# defined. These variables are subsequently used by _LT_CONFIG to write +# the compiler configuration to `libtool'. +m4_defun([_LT_LANG_CXX_CONFIG], +[AC_REQUIRE([_LT_PROG_CXX])dnl +m4_require([_LT_FILEUTILS_DEFAULTS])dnl +m4_require([_LT_DECL_EGREP])dnl + +AC_LANG_PUSH(C++) +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(compiler_needs_object, $1)=no +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_shlibpath_var, $1)=unsupported +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for C++ test sources. +ac_ext=cpp + +# Object file extension for compiled C++ test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the CXX compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test "$_lt_caught_CXX_error" != yes; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="int some_variable = 0;" + + # Code to be used in simple link tests + lt_simple_link_test_code='int main(int, char *[[]]) { return(0); }' + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC=$CC + lt_save_LD=$LD + lt_save_GCC=$GCC + GCC=$GXX + lt_save_with_gnu_ld=$with_gnu_ld + lt_save_path_LD=$lt_cv_path_LD + if test -n "${lt_cv_prog_gnu_ldcxx+set}"; then + lt_cv_prog_gnu_ld=$lt_cv_prog_gnu_ldcxx + else + $as_unset lt_cv_prog_gnu_ld + fi + if test -n "${lt_cv_path_LDCXX+set}"; then + lt_cv_path_LD=$lt_cv_path_LDCXX + else + $as_unset lt_cv_path_LD + fi + test -z "${LDCXX+set}" || LD=$LDCXX + CC=${CXX-"c++"} + compiler=$CC + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + + if test -n "$compiler"; then + # We don't want -fno-exception when compiling C++ code, so set the + # no_builtin_flag separately + if test "$GXX" = yes; then + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)=' -fno-builtin' + else + _LT_TAGVAR(lt_prog_compiler_no_builtin_flag, $1)= + fi + + if test "$GXX" = yes; then + # Set up default GNU C++ configuration + + LT_PATH_LD + + # Check if GNU C++ uses GNU ld as the underlying linker, since the + # archiving commands below assume that GNU ld is being used. + if test "$with_gnu_ld" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + + # If archive_cmds runs LD, not CC, wlarc should be empty + # XXX I think wlarc can be eliminated in ltcf-cxx, but I need to + # investigate it a little bit more. (MM) + wlarc='${wl}' + + # ancient GNU ld didn't support --whole-archive et. al. + if eval "`$CC -print-prog-name=ld` --help 2>&1" | + $GREP 'no-whole-archive' > /dev/null; then + _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + else + _LT_TAGVAR(whole_archive_flag_spec, $1)= + fi + else + with_gnu_ld=no + wlarc= + + # A generic and very simple default shared library creation + # command for GNU C++ for the case where it uses the native + # linker, instead of GNU ld. If possible, this setting should + # overridden to take advantage of the native linker features on + # the platform it is being used on. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + fi + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' + + else + GXX=no + with_gnu_ld=no + wlarc= + fi + + # PORTME: fill in a description of your system's C++ link characteristics + AC_MSG_CHECKING([whether the $compiler linker ($LD) supports shared libraries]) + _LT_TAGVAR(ld_shlibs, $1)=yes + case $host_os in + aix3*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aix[[4-9]]*) + if test "$host_cpu" = ia64; then + # On IA64, the linker does run time linking by default, so we don't + # have to do anything special. + aix_use_runtimelinking=no + exp_sym_flag='-Bexport' + no_entry_flag="" + else + aix_use_runtimelinking=no + + # Test if we are trying to use run time linking or normal + # AIX style linking. If -brtl is somewhere in LDFLAGS, we + # need to do runtime linking. + case $host_os in aix4.[[23]]|aix4.[[23]].*|aix[[5-9]]*) + for ld_flag in $LDFLAGS; do + case $ld_flag in + *-brtl*) + aix_use_runtimelinking=yes + break + ;; + esac + done + ;; + esac + + exp_sym_flag='-bexport' + no_entry_flag='-bnoentry' + fi + + # When large executables or shared objects are built, AIX ld can + # have problems creating the table of contents. If linking a library + # or program results in "error TOC overflow" add -mminimal-toc to + # CXXFLAGS/CFLAGS for g++/gcc. In the cases where that is not + # enough to fix the problem, add -Wl,-bbigtoc to LDFLAGS. + + _LT_TAGVAR(archive_cmds, $1)='' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(file_list_spec, $1)='${wl}-f,' + + if test "$GXX" = yes; then + case $host_os in aix4.[[012]]|aix4.[[012]].*) + # We only want to do this on AIX 4.2 and lower, the check + # below for broken collect2 doesn't work under 4.3+ + collect2name=`${CC} -print-prog-name=collect2` + if test -f "$collect2name" && + strings "$collect2name" | $GREP resolve_lib_name >/dev/null + then + # We have reworked collect2 + : + else + # We have old collect2 + _LT_TAGVAR(hardcode_direct, $1)=unsupported + # It fails to find uninstalled libraries when the uninstalled + # path is not listed in the libpath. Setting hardcode_minus_L + # to unsupported forces relinking + _LT_TAGVAR(hardcode_minus_L, $1)=yes + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)= + fi + esac + shared_flag='-shared' + if test "$aix_use_runtimelinking" = yes; then + shared_flag="$shared_flag "'${wl}-G' + fi + else + # not using gcc + if test "$host_cpu" = ia64; then + # VisualAge C++, Version 5.5 for AIX 5L for IA-64, Beta 3 Release + # chokes on -Wl,-G. The following line is correct: + shared_flag='-G' + else + if test "$aix_use_runtimelinking" = yes; then + shared_flag='${wl}-G' + else + shared_flag='${wl}-bM:SRE' + fi + fi + fi + + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-bexpall' + # It seems that -bexpall does not export symbols beginning with + # underscore (_), so it is better to generate a list of symbols to + # export. + _LT_TAGVAR(always_export_symbols, $1)=yes + if test "$aix_use_runtimelinking" = yes; then + # Warning - without using the other runtime loading flags (-brtl), + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(allow_undefined_flag, $1)='-berok' + # Determine the default libpath from the value encoded in an empty + # executable. + _LT_SYS_MODULE_PATH_AIX + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags `if test "x${allow_undefined_flag}" != "x"; then $ECHO "X${wl}${allow_undefined_flag}" | $Xsed; else :; fi` '"\${wl}$exp_sym_flag:\$export_symbols $shared_flag" + else + if test "$host_cpu" = ia64; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $libdir:/usr/lib:/lib' + _LT_TAGVAR(allow_undefined_flag, $1)="-z nodefs" + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs '"\${wl}$no_entry_flag"' $compiler_flags ${wl}${allow_undefined_flag} '"\${wl}$exp_sym_flag:\$export_symbols" + else + # Determine the default libpath from the value encoded in an + # empty executable. + _LT_SYS_MODULE_PATH_AIX + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-blibpath:$libdir:'"$aix_libpath" + # Warning - without using the other run time loading flags, + # -berok will link without error, but may produce a broken library. + _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-bernotok' + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-berok' + # Exported symbols can be pulled into shared objects from archives + _LT_TAGVAR(whole_archive_flag_spec, $1)='$convenience' + _LT_TAGVAR(archive_cmds_need_lc, $1)=yes + # This is similar to how AIX traditionally builds its shared + # libraries. + _LT_TAGVAR(archive_expsym_cmds, $1)="\$CC $shared_flag"' -o $output_objdir/$soname $libobjs $deplibs ${wl}-bnoentry $compiler_flags ${wl}-bE:$export_symbols${allow_undefined_flag}~$AR $AR_FLAGS $output_objdir/$libname$release.a $output_objdir/$soname' + fi + fi + ;; + + beos*) + if $LD --help 2>&1 | $GREP ': supported targets:.* elf' > /dev/null; then + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + # Joseph Beckenbach says some releases of gcc + # support --undefined. This deserves some investigation. FIXME + _LT_TAGVAR(archive_cmds, $1)='$CC -nostart $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + chorus*) + case $cc_basename in + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + cygwin* | mingw* | pw32* | cegcc*) + # _LT_TAGVAR(hardcode_libdir_flag_spec, $1) is actually meaningless, + # as there is no search path for DLLs. + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-L$libdir' + _LT_TAGVAR(allow_undefined_flag, $1)=unsupported + _LT_TAGVAR(always_export_symbols, $1)=no + _LT_TAGVAR(enable_shared_with_static_runtimes, $1)=yes + + if $LD --help 2>&1 | $GREP 'auto-import' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + # If the export-symbols file already is a .def file (1st line + # is EXPORTS), use it as is; otherwise, prepend... + _LT_TAGVAR(archive_expsym_cmds, $1)='if test "x`$SED 1q $export_symbols`" = xEXPORTS; then + cp $export_symbols $output_objdir/$soname.def; + else + echo EXPORTS > $output_objdir/$soname.def; + cat $export_symbols >> $output_objdir/$soname.def; + fi~ + $CC -shared -nostdlib $output_objdir/$soname.def $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $output_objdir/$soname ${wl}--enable-auto-image-base -Xlinker --out-implib -Xlinker $lib' + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + darwin* | rhapsody*) + _LT_DARWIN_LINKER_FEATURES($1) + ;; + + dgux*) + case $cc_basename in + ec++*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + ghcx*) + # Green Hills C++ Compiler + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + freebsd[[12]]*) + # C++ shared libraries reported to be fairly broken before + # switch to ELF + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + freebsd-elf*) + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + ;; + + freebsd* | dragonfly*) + # FreeBSD 3 and later use GNU C++ and GNU ld with standard ELF + # conventions + _LT_TAGVAR(ld_shlibs, $1)=yes + ;; + + gnu*) + ;; + + hpux9*) + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, + # but as the default + # location of the library. + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aCC*) + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -b ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $EGREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' + ;; + *) + if test "$GXX" = yes; then + _LT_TAGVAR(archive_cmds, $1)='$RM $output_objdir/$soname~$CC -shared -nostdlib -fPIC ${wl}+b ${wl}$install_libdir -o $output_objdir/$soname $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~test $output_objdir/$soname = $lib || mv $output_objdir/$soname $lib' + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + hpux10*|hpux11*) + if test $with_gnu_ld = no; then + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}+b ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + case $host_cpu in + hppa*64*|ia64*) + ;; + *) + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + ;; + esac + fi + case $host_cpu in + hppa*64*|ia64*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + ;; + *) + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(hardcode_minus_L, $1)=yes # Not in the search PATH, + # but as the default + # location of the library. + ;; + esac + + case $cc_basename in + CC*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + aCC*) + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -b ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`($CC -b $CFLAGS -v conftest.$objext 2>&1) | $GREP "\-L"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' + ;; + *) + if test "$GXX" = yes; then + if test $with_gnu_ld = no; then + case $host_cpu in + hppa*64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + ia64*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+nodefaultrpath -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib -fPIC ${wl}+h ${wl}$soname ${wl}+b ${wl}$install_libdir -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + ;; + esac + fi + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + interix[[3-9]]*) + _LT_TAGVAR(hardcode_direct, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + # Hack: On Interix 3.x, we cannot compile PIC because of a broken gcc. + # Instead, shared libraries are loaded at an image base (0x10000000 by + # default) and relocated if they conflict, which is a slow very memory + # consuming and fragmenting process. To avoid this, we pick a random, + # 256 KiB-aligned image base between 0x50000000 and 0x6FFC0000 at link + # time. Moving up from 0x10000000 also allows more sbrk(2) space. + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='sed "s,^,_," $export_symbols >$output_objdir/$soname.expsym~$CC -shared $pic_flag $libobjs $deplibs $compiler_flags ${wl}-h,$soname ${wl}--retain-symbols-file,$output_objdir/$soname.expsym ${wl}--image-base,`expr ${RANDOM-$$} % 4096 / 2 \* 262144 + 1342177280` -o $lib' + ;; + irix5* | irix6*) + case $cc_basename in + CC*) + # SGI C++ + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -all -multigot $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' + + # Archives containing C++ object files must be created using + # "CC -ar", where "CC" is the IRIX C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -ar -WR,-u -o $oldlib $oldobjs' + ;; + *) + if test "$GXX" = yes; then + if test "$with_gnu_ld" = no; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + else + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` -o $lib' + fi + fi + _LT_TAGVAR(link_all_deplibs, $1)=yes + ;; + esac + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + _LT_TAGVAR(inherit_rpath, $1)=yes + ;; + + linux* | k*bsd*-gnu | kopensolaris*-gnu) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo $lib | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib ${wl}-retain-symbols-file,$export_symbols; mv \$templib $lib' + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 | $GREP "ld"`; rm -f libconftest$shared_ext; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + + # Archives containing C++ object files must be created using + # "CC -Bstatic", where "CC" is the KAI C++ compiler. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' + ;; + icpc* | ecpc* ) + # Intel C++ + with_gnu_ld=yes + # version 8.0 and above of icpc choke on multiply defined symbols + # if we add $predep_objects and $postdep_objects, however 7.1 and + # earlier do not add the objects themselves. + case `$CC -V 2>&1` in + *"Version 7."*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + ;; + *) # Version 8.0 or newer + tmp_idyn= + case $host_cpu in + ia64*) tmp_idyn=' -i_dynamic';; + esac + _LT_TAGVAR(archive_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared'"$tmp_idyn"' $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-retain-symbols-file $wl$export_symbols -o $lib' + ;; + esac + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive$convenience ${wl}--no-whole-archive' + ;; + pgCC* | pgcpp*) + # Portland Group C++ compiler + case `$CC -V` in + *pgCC\ [[1-5]]* | *pgcpp\ [[1-5]]*) + _LT_TAGVAR(prelink_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $objs $libobjs $compile_deplibs~ + compile_command="$compile_command `find $tpldir -name \*.o | $NL2SP`"' + _LT_TAGVAR(old_archive_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $oldobjs$old_deplibs~ + $AR $AR_FLAGS $oldlib$oldobjs$old_deplibs `find $tpldir -name \*.o | $NL2SP`~ + $RANLIB $oldlib' + _LT_TAGVAR(archive_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='tpldir=Template.dir~ + rm -rf $tpldir~ + $CC --prelink_objects --instantiation_dir $tpldir $predep_objects $libobjs $deplibs $convenience $postdep_objects~ + $CC -shared $pic_flag $predep_objects $libobjs $deplibs `find $tpldir -name \*.o | $NL2SP` $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' + ;; + *) # Version 6 will use weak symbols + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname ${wl}-retain-symbols-file ${wl}$export_symbols -o $lib' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}--rpath ${wl}$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`for conv in $convenience\"\"; do test -n \"$conv\" && new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' + ;; + cxx*) + # Compaq C++ + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $wl$soname -o $lib ${wl}-retain-symbols-file $wl$export_symbols' + + runpath_var=LD_RUN_PATH + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld"`; templist=`$ECHO "X$templist" | $Xsed -e "s/\(^.*ld.*\)\( .*ld .*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' + ;; + xl*) + # IBM XL 8.0 on PPC, with GNU ld + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}--export-dynamic' + _LT_TAGVAR(archive_cmds, $1)='$CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname -o $lib' + if test "x$supports_anon_versioning" = xyes; then + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $output_objdir/$libname.ver~ + cat $export_symbols | sed -e "s/\(.*\)/\1;/" >> $output_objdir/$libname.ver~ + echo "local: *; };" >> $output_objdir/$libname.ver~ + $CC -qmkshrobj $libobjs $deplibs $compiler_flags ${wl}-soname $wl$soname ${wl}-version-script ${wl}$output_objdir/$libname.ver -o $lib' + fi + ;; + *) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' + _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file ${wl}$export_symbols' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}--whole-archive`new_convenience=; for conv in $convenience\"\"; do test -z \"$conv\" || new_convenience=\"$new_convenience,$conv\"; done; $ECHO \"$new_convenience\"` ${wl}--no-whole-archive' + _LT_TAGVAR(compiler_needs_object, $1)=yes + + # Not sure whether something based on + # $CC $CFLAGS -v conftest.$objext -o libconftest$shared_ext 2>&1 + # would be better. + output_verbose_link_cmd='echo' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' + ;; + esac + ;; + esac + ;; + + lynxos*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + m88k*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + mvs*) + case $cc_basename in + cxx*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + netbsd*) + if echo __ELF__ | $CC -E - | $GREP __ELF__ >/dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$LD -Bshareable -o $lib $predep_objects $libobjs $deplibs $postdep_objects $linker_flags' + wlarc= + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + fi + # Workaround some broken pre-1.5 toolchains + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP conftest.$objext | $SED -e "s:-lgcc -lc -lgcc::"' + ;; + + *nto* | *qnx*) + _LT_TAGVAR(ld_shlibs, $1)=yes + ;; + + openbsd2*) + # C++ shared libraries are fairly broken + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + openbsd*) + if test -f /usr/libexec/ld.so; then + _LT_TAGVAR(hardcode_direct, $1)=yes + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_direct_absolute, $1)=yes + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared $pic_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-retain-symbols-file,$export_symbols -o $lib' + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-E' + _LT_TAGVAR(whole_archive_flag_spec, $1)="$wlarc"'--whole-archive$convenience '"$wlarc"'--no-whole-archive' + fi + output_verbose_link_cmd=echo + else + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + + osf3* | osf4* | osf5*) + case $cc_basename in + KCC*) + # Kuck and Associates, Inc. (KAI) C++ Compiler + + # KCC will only create a shared library if the output file + # ends with ".so" (or ".sl" for HP-UX), so rename the library + # to its proper name (with version) after linking. + _LT_TAGVAR(archive_cmds, $1)='tempext=`echo $shared_ext | $SED -e '\''s/\([[^()0-9A-Za-z{}]]\)/\\\\\1/g'\''`; templib=`echo "$lib" | $SED -e "s/\${tempext}\..*/.so/"`; $CC $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags --soname $soname -o \$templib; mv \$templib $lib' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Archives containing C++ object files must be created using + # the KAI C++ compiler. + case $host in + osf3*) _LT_TAGVAR(old_archive_cmds, $1)='$CC -Bstatic -o $oldlib $oldobjs' ;; + *) _LT_TAGVAR(old_archive_cmds, $1)='$CC -o $oldlib $oldobjs' ;; + esac + ;; + RCC*) + # Rational C++ 2.4.1 + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + cxx*) + case $host in + osf3*) + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname $soname `test -n "$verstring" && $ECHO "X${wl}-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + ;; + *) + _LT_TAGVAR(allow_undefined_flag, $1)=' -expect_unresolved \*' + _LT_TAGVAR(archive_cmds, $1)='$CC -shared${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='for i in `cat $export_symbols`; do printf "%s %s\\n" -exported_symbol "\$i" >> $lib.exp; done~ + echo "-hidden">> $lib.exp~ + $CC -shared$allow_undefined_flag $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags -msym -soname $soname ${wl}-input ${wl}$lib.exp `test -n "$verstring" && $ECHO "X-set_version $verstring" | $Xsed` -update_registry ${output_objdir}/so_locations -o $lib~ + $RM $lib.exp' + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-rpath $libdir' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + # + # There doesn't appear to be a way to prevent this compiler from + # explicitly linking system object files so we need to strip them + # from the output so that they don't get included in the library + # dependencies. + output_verbose_link_cmd='templist=`$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "ld" | $GREP -v "ld:"`; templist=`$ECHO "X$templist" | $Xsed -e "s/\(^.*ld.*\)\( .*ld.*$\)/\1/"`; list=""; for z in $templist; do case $z in conftest.$objext) list="$list $z";; *.$objext);; *) list="$list $z";;esac; done; $ECHO "X$list" | $Xsed' + ;; + *) + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + _LT_TAGVAR(allow_undefined_flag, $1)=' ${wl}-expect_unresolved ${wl}\*' + case $host in + osf3*) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "X${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib ${allow_undefined_flag} $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-msym ${wl}-soname ${wl}$soname `test -n "$verstring" && $ECHO "${wl}-set_version ${wl}$verstring" | $Xsed` ${wl}-update_registry ${wl}${output_objdir}/so_locations -o $lib' + ;; + esac + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-rpath ${wl}$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=: + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' + + else + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + fi + ;; + esac + ;; + + psos*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + sunos4*) + case $cc_basename in + CC*) + # Sun C++ 4.x + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + lcc*) + # Lucid + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + solaris*) + case $cc_basename in + CC*) + # Sun C++ 4.2, 5.x and Centerline C++ + _LT_TAGVAR(archive_cmds_need_lc,$1)=yes + _LT_TAGVAR(no_undefined_flag, $1)=' -zdefs' + _LT_TAGVAR(archive_cmds, $1)='$CC -G${allow_undefined_flag} -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G${allow_undefined_flag} ${wl}-M ${wl}$lib.exp -h$soname -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='-R$libdir' + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + # The compiler driver will combine and reorder linker options, + # but understands `-z linker_flag'. + # Supported since Solaris 2.6 (maybe 2.5.1?) + _LT_TAGVAR(whole_archive_flag_spec, $1)='-z allextract$convenience -z defaultextract' + ;; + esac + _LT_TAGVAR(link_all_deplibs, $1)=yes + + output_verbose_link_cmd='echo' + + # Archives containing C++ object files must be created using + # "CC -xar", where "CC" is the Sun C++ compiler. This is + # necessary to make sure instantiated templates are included + # in the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC -xar -o $oldlib $oldobjs' + ;; + gcx*) + # Green Hills C++ Compiler + _LT_TAGVAR(archive_cmds, $1)='$CC -shared $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + + # The C++ compiler must be used to create the archive. + _LT_TAGVAR(old_archive_cmds, $1)='$CC $LDFLAGS -archive -o $oldlib $oldobjs' + ;; + *) + # GNU C++ compiler with Solaris linker + if test "$GXX" = yes && test "$with_gnu_ld" = no; then + _LT_TAGVAR(no_undefined_flag, $1)=' ${wl}-z ${wl}defs' + if $CC --version | $GREP -v '^2\.7' > /dev/null; then + _LT_TAGVAR(archive_cmds, $1)='$CC -shared -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -shared -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -shared $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' + else + # g++ 2.7 appears to require `-G' NOT `-shared' on this + # platform. + _LT_TAGVAR(archive_cmds, $1)='$CC -G -nostdlib $LDFLAGS $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags ${wl}-h $wl$soname -o $lib' + _LT_TAGVAR(archive_expsym_cmds, $1)='echo "{ global:" > $lib.exp~cat $export_symbols | $SED -e "s/\(.*\)/\1;/" >> $lib.exp~echo "local: *; };" >> $lib.exp~ + $CC -G -nostdlib ${wl}-M $wl$lib.exp -o $lib $predep_objects $libobjs $deplibs $postdep_objects $compiler_flags~$RM $lib.exp' + + # Commands to make compiler produce verbose output that lists + # what "hidden" libraries, object files and flags are used when + # linking a shared library. + output_verbose_link_cmd='$CC -G $CFLAGS -v conftest.$objext 2>&1 | $GREP "\-L"' + fi + + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R $wl$libdir' + case $host_os in + solaris2.[[0-5]] | solaris2.[[0-5]].*) ;; + *) + _LT_TAGVAR(whole_archive_flag_spec, $1)='${wl}-z ${wl}allextract$convenience ${wl}-z ${wl}defaultextract' + ;; + esac + fi + ;; + esac + ;; + + sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[[01]].[[10]]* | unixware7* | sco3.2v5.0.[[024]]*) + _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + sysv5* | sco3.2v5* | sco5v6*) + # Note: We can NOT use -z defs as we might desire, because we do not + # link with -lc, and that would cause any symbols used from libc to + # always be unresolved, which means just about no library would + # ever link correctly. If we're not using GNU ld we use -z text + # though, which does catch some bad symbols but isn't as heavy-handed + # as -z defs. + _LT_TAGVAR(no_undefined_flag, $1)='${wl}-z,text' + _LT_TAGVAR(allow_undefined_flag, $1)='${wl}-z,nodefs' + _LT_TAGVAR(archive_cmds_need_lc, $1)=no + _LT_TAGVAR(hardcode_shlibpath_var, $1)=no + _LT_TAGVAR(hardcode_libdir_flag_spec, $1)='${wl}-R,$libdir' + _LT_TAGVAR(hardcode_libdir_separator, $1)=':' + _LT_TAGVAR(link_all_deplibs, $1)=yes + _LT_TAGVAR(export_dynamic_flag_spec, $1)='${wl}-Bexport' + runpath_var='LD_RUN_PATH' + + case $cc_basename in + CC*) + _LT_TAGVAR(archive_cmds, $1)='$CC -G ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -G ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + *) + _LT_TAGVAR(archive_cmds, $1)='$CC -shared ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + _LT_TAGVAR(archive_expsym_cmds, $1)='$CC -shared ${wl}-Bexport:$export_symbols ${wl}-h,$soname -o $lib $libobjs $deplibs $compiler_flags' + ;; + esac + ;; + + tandem*) + case $cc_basename in + NCC*) + # NonStop-UX NCC 3.20 + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + ;; + + vxworks*) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + + *) + # FIXME: insert proper C++ library support + _LT_TAGVAR(ld_shlibs, $1)=no + ;; + esac + + AC_MSG_RESULT([$_LT_TAGVAR(ld_shlibs, $1)]) + test "$_LT_TAGVAR(ld_shlibs, $1)" = no && can_build_shared=no + + _LT_TAGVAR(GCC, $1)="$GXX" + _LT_TAGVAR(LD, $1)="$LD" + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_SYS_HIDDEN_LIBDEPS($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + CC=$lt_save_CC + LDCXX=$LD + LD=$lt_save_LD + GCC=$lt_save_GCC + with_gnu_ld=$lt_save_with_gnu_ld + lt_cv_path_LDCXX=$lt_cv_path_LD + lt_cv_path_LD=$lt_save_path_LD + lt_cv_prog_gnu_ldcxx=$lt_cv_prog_gnu_ld + lt_cv_prog_gnu_ld=$lt_save_with_gnu_ld +fi # test "$_lt_caught_CXX_error" != yes + +AC_LANG_POP +])# _LT_LANG_CXX_CONFIG + + +# _LT_SYS_HIDDEN_LIBDEPS([TAGNAME]) +# --------------------------------- +# Figure out "hidden" library dependencies from verbose +# compiler output when linking a shared library. +# Parse the compiler output and extract the necessary +# objects, libraries and library flags. +m4_defun([_LT_SYS_HIDDEN_LIBDEPS], +[m4_require([_LT_FILEUTILS_DEFAULTS])dnl +# Dependencies to place before and after the object being linked: +_LT_TAGVAR(predep_objects, $1)= +_LT_TAGVAR(postdep_objects, $1)= +_LT_TAGVAR(predeps, $1)= +_LT_TAGVAR(postdeps, $1)= +_LT_TAGVAR(compiler_lib_search_path, $1)= + +dnl we can't use the lt_simple_compile_test_code here, +dnl because it contains code intended for an executable, +dnl not a library. It's possible we should let each +dnl tag define a new lt_????_link_test_code variable, +dnl but it's only used here... +m4_if([$1], [], [cat > conftest.$ac_ext <<_LT_EOF +int a; +void foo (void) { a = 0; } +_LT_EOF +], [$1], [CXX], [cat > conftest.$ac_ext <<_LT_EOF +class Foo +{ +public: + Foo (void) { a = 0; } +private: + int a; +}; +_LT_EOF +], [$1], [F77], [cat > conftest.$ac_ext <<_LT_EOF + subroutine foo + implicit none + integer*4 a + a=0 + return + end +_LT_EOF +], [$1], [FC], [cat > conftest.$ac_ext <<_LT_EOF + subroutine foo + implicit none + integer a + a=0 + return + end +_LT_EOF +], [$1], [GCJ], [cat > conftest.$ac_ext <<_LT_EOF +public class foo { + private int a; + public void bar (void) { + a = 0; + } +}; +_LT_EOF +]) +dnl Parse the compiler output and extract the necessary +dnl objects, libraries and library flags. +if AC_TRY_EVAL(ac_compile); then + # Parse the compiler output and extract the necessary + # objects, libraries and library flags. + + # Sentinel used to keep track of whether or not we are before + # the conftest object file. + pre_test_object_deps_done=no + + for p in `eval "$output_verbose_link_cmd"`; do + case $p in + + -L* | -R* | -l*) + # Some compilers place space between "-{L,R}" and the path. + # Remove the space. + if test $p = "-L" || + test $p = "-R"; then + prev=$p + continue + else + prev= + fi + + if test "$pre_test_object_deps_done" = no; then + case $p in + -L* | -R*) + # Internal compiler library paths should come after those + # provided the user. The postdeps already come after the + # user supplied libs so there is no need to process them. + if test -z "$_LT_TAGVAR(compiler_lib_search_path, $1)"; then + _LT_TAGVAR(compiler_lib_search_path, $1)="${prev}${p}" + else + _LT_TAGVAR(compiler_lib_search_path, $1)="${_LT_TAGVAR(compiler_lib_search_path, $1)} ${prev}${p}" + fi + ;; + # The "-l" case would never come before the object being + # linked, so don't bother handling this case. + esac + else + if test -z "$_LT_TAGVAR(postdeps, $1)"; then + _LT_TAGVAR(postdeps, $1)="${prev}${p}" + else + _LT_TAGVAR(postdeps, $1)="${_LT_TAGVAR(postdeps, $1)} ${prev}${p}" + fi + fi + ;; + + *.$objext) + # This assumes that the test object file only shows up + # once in the compiler output. + if test "$p" = "conftest.$objext"; then + pre_test_object_deps_done=yes + continue + fi + + if test "$pre_test_object_deps_done" = no; then + if test -z "$_LT_TAGVAR(predep_objects, $1)"; then + _LT_TAGVAR(predep_objects, $1)="$p" + else + _LT_TAGVAR(predep_objects, $1)="$_LT_TAGVAR(predep_objects, $1) $p" + fi + else + if test -z "$_LT_TAGVAR(postdep_objects, $1)"; then + _LT_TAGVAR(postdep_objects, $1)="$p" + else + _LT_TAGVAR(postdep_objects, $1)="$_LT_TAGVAR(postdep_objects, $1) $p" + fi + fi + ;; + + *) ;; # Ignore the rest. + + esac + done + + # Clean up. + rm -f a.out a.exe +else + echo "libtool.m4: error: problem compiling $1 test program" +fi + +$RM -f confest.$objext + +# PORTME: override above test on systems where it is broken +m4_if([$1], [CXX], +[case $host_os in +interix[[3-9]]*) + # Interix 3.5 installs completely hosed .la files for C++, so rather than + # hack all around it, let's just trust "g++" to DTRT. + _LT_TAGVAR(predep_objects,$1)= + _LT_TAGVAR(postdep_objects,$1)= + _LT_TAGVAR(postdeps,$1)= + ;; + +linux*) + case `$CC -V 2>&1 | sed 5q` in + *Sun\ C*) + # Sun C++ 5.9 + + # The more standards-conforming stlport4 library is + # incompatible with the Cstd library. Avoid specifying + # it if it's in CXXFLAGS. Ignore libCrun as + # -library=stlport4 depends on it. + case " $CXX $CXXFLAGS " in + *" -library=stlport4 "*) + solaris_use_stlport4=yes + ;; + esac + + if test "$solaris_use_stlport4" != yes; then + _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' + fi + ;; + esac + ;; + +solaris*) + case $cc_basename in + CC*) + # The more standards-conforming stlport4 library is + # incompatible with the Cstd library. Avoid specifying + # it if it's in CXXFLAGS. Ignore libCrun as + # -library=stlport4 depends on it. + case " $CXX $CXXFLAGS " in + *" -library=stlport4 "*) + solaris_use_stlport4=yes + ;; + esac + + # Adding this requires a known-good setup of shared libraries for + # Sun compiler versions before 5.6, else PIC objects from an old + # archive will be linked into the output, leading to subtle bugs. + if test "$solaris_use_stlport4" != yes; then + _LT_TAGVAR(postdeps,$1)='-library=Cstd -library=Crun' + fi + ;; + esac + ;; +esac +]) + +case " $_LT_TAGVAR(postdeps, $1) " in +*" -lc "*) _LT_TAGVAR(archive_cmds_need_lc, $1)=no ;; +esac + _LT_TAGVAR(compiler_lib_search_dirs, $1)= +if test -n "${_LT_TAGVAR(compiler_lib_search_path, $1)}"; then + _LT_TAGVAR(compiler_lib_search_dirs, $1)=`echo " ${_LT_TAGVAR(compiler_lib_search_path, $1)}" | ${SED} -e 's! -L! !g' -e 's!^ !!'` +fi +_LT_TAGDECL([], [compiler_lib_search_dirs], [1], + [The directories searched by this compiler when creating a shared library]) +_LT_TAGDECL([], [predep_objects], [1], + [Dependencies to place before and after the objects being linked to + create a shared library]) +_LT_TAGDECL([], [postdep_objects], [1]) +_LT_TAGDECL([], [predeps], [1]) +_LT_TAGDECL([], [postdeps], [1]) +_LT_TAGDECL([], [compiler_lib_search_path], [1], + [The library search path used internally by the compiler when linking + a shared library]) +])# _LT_SYS_HIDDEN_LIBDEPS + + +# _LT_PROG_F77 +# ------------ +# Since AC_PROG_F77 is broken, in that it returns the empty string +# if there is no fortran compiler, we have our own version here. +m4_defun([_LT_PROG_F77], +[ +pushdef([AC_MSG_ERROR], [_lt_disable_F77=yes]) +AC_PROG_F77 +if test -z "$F77" || test "X$F77" = "Xno"; then + _lt_disable_F77=yes +fi +popdef([AC_MSG_ERROR]) +])# _LT_PROG_F77 + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([_LT_PROG_F77], []) + + +# _LT_LANG_F77_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for a Fortran 77 compiler are +# suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_F77_CONFIG], +[AC_REQUIRE([_LT_PROG_F77])dnl +AC_LANG_PUSH(Fortran 77) + +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for f77 test sources. +ac_ext=f + +# Object file extension for compiled f77 test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the F77 compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test "$_lt_disable_F77" != yes; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="\ + subroutine t + return + end +" + + # Code to be used in simple link tests + lt_simple_link_test_code="\ + program t + end +" + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC="$CC" + lt_save_GCC=$GCC + CC=${F77-"f77"} + compiler=$CC + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + GCC=$G77 + if test -n "$compiler"; then + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test "$can_build_shared" = "no" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + aix[[4-9]]*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test "$enable_shared" = yes || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_TAGVAR(GCC, $1)="$G77" + _LT_TAGVAR(LD, $1)="$LD" + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + GCC=$lt_save_GCC + CC="$lt_save_CC" +fi # test "$_lt_disable_F77" != yes + +AC_LANG_POP +])# _LT_LANG_F77_CONFIG + + +# _LT_PROG_FC +# ----------- +# Since AC_PROG_FC is broken, in that it returns the empty string +# if there is no fortran compiler, we have our own version here. +m4_defun([_LT_PROG_FC], +[ +pushdef([AC_MSG_ERROR], [_lt_disable_FC=yes]) +AC_PROG_FC +if test -z "$FC" || test "X$FC" = "Xno"; then + _lt_disable_FC=yes +fi +popdef([AC_MSG_ERROR]) +])# _LT_PROG_FC + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([_LT_PROG_FC], []) + + +# _LT_LANG_FC_CONFIG([TAG]) +# ------------------------- +# Ensure that the configuration variables for a Fortran compiler are +# suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_FC_CONFIG], +[AC_REQUIRE([_LT_PROG_FC])dnl +AC_LANG_PUSH(Fortran) + +_LT_TAGVAR(archive_cmds_need_lc, $1)=no +_LT_TAGVAR(allow_undefined_flag, $1)= +_LT_TAGVAR(always_export_symbols, $1)=no +_LT_TAGVAR(archive_expsym_cmds, $1)= +_LT_TAGVAR(export_dynamic_flag_spec, $1)= +_LT_TAGVAR(hardcode_direct, $1)=no +_LT_TAGVAR(hardcode_direct_absolute, $1)=no +_LT_TAGVAR(hardcode_libdir_flag_spec, $1)= +_LT_TAGVAR(hardcode_libdir_flag_spec_ld, $1)= +_LT_TAGVAR(hardcode_libdir_separator, $1)= +_LT_TAGVAR(hardcode_minus_L, $1)=no +_LT_TAGVAR(hardcode_automatic, $1)=no +_LT_TAGVAR(inherit_rpath, $1)=no +_LT_TAGVAR(module_cmds, $1)= +_LT_TAGVAR(module_expsym_cmds, $1)= +_LT_TAGVAR(link_all_deplibs, $1)=unknown +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds +_LT_TAGVAR(no_undefined_flag, $1)= +_LT_TAGVAR(whole_archive_flag_spec, $1)= +_LT_TAGVAR(enable_shared_with_static_runtimes, $1)=no + +# Source file extension for fc test sources. +ac_ext=${ac_fc_srcext-f} + +# Object file extension for compiled fc test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# No sense in running all these tests if we already determined that +# the FC compiler isn't working. Some variables (like enable_shared) +# are currently assumed to apply to all compilers on this platform, +# and will be corrupted by setting them based on a non-working compiler. +if test "$_lt_disable_FC" != yes; then + # Code to be used in simple compile tests + lt_simple_compile_test_code="\ + subroutine t + return + end +" + + # Code to be used in simple link tests + lt_simple_link_test_code="\ + program t + end +" + + # ltmain only uses $CC for tagged configurations so make sure $CC is set. + _LT_TAG_COMPILER + + # save warnings/boilerplate of simple test code + _LT_COMPILER_BOILERPLATE + _LT_LINKER_BOILERPLATE + + # Allow CC to be a program name with arguments. + lt_save_CC="$CC" + lt_save_GCC=$GCC + CC=${FC-"f95"} + compiler=$CC + GCC=$ac_cv_fc_compiler_gnu + + _LT_TAGVAR(compiler, $1)=$CC + _LT_CC_BASENAME([$compiler]) + + if test -n "$compiler"; then + AC_MSG_CHECKING([if libtool supports shared libraries]) + AC_MSG_RESULT([$can_build_shared]) + + AC_MSG_CHECKING([whether to build shared libraries]) + test "$can_build_shared" = "no" && enable_shared=no + + # On AIX, shared libraries and static libraries use the same namespace, and + # are all built from PIC. + case $host_os in + aix3*) + test "$enable_shared" = yes && enable_static=no + if test -n "$RANLIB"; then + archive_cmds="$archive_cmds~\$RANLIB \$lib" + postinstall_cmds='$RANLIB $lib' + fi + ;; + aix[[4-9]]*) + if test "$host_cpu" != ia64 && test "$aix_use_runtimelinking" = no ; then + test "$enable_shared" = yes && enable_static=no + fi + ;; + esac + AC_MSG_RESULT([$enable_shared]) + + AC_MSG_CHECKING([whether to build static libraries]) + # Make sure either enable_shared or enable_static is yes. + test "$enable_shared" = yes || enable_static=yes + AC_MSG_RESULT([$enable_static]) + + _LT_TAGVAR(GCC, $1)="$ac_cv_fc_compiler_gnu" + _LT_TAGVAR(LD, $1)="$LD" + + ## CAVEAT EMPTOR: + ## There is no encapsulation within the following macros, do not change + ## the running order or otherwise move them around unless you know exactly + ## what you are doing... + _LT_SYS_HIDDEN_LIBDEPS($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_SYS_DYNAMIC_LINKER($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) + fi # test -n "$compiler" + + GCC=$lt_save_GCC + CC="$lt_save_CC" +fi # test "$_lt_disable_FC" != yes + +AC_LANG_POP +])# _LT_LANG_FC_CONFIG + + +# _LT_LANG_GCJ_CONFIG([TAG]) +# -------------------------- +# Ensure that the configuration variables for the GNU Java Compiler compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_GCJ_CONFIG], +[AC_REQUIRE([LT_PROG_GCJ])dnl +AC_LANG_SAVE + +# Source file extension for Java test sources. +ac_ext=java + +# Object file extension for compiled Java test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code="class foo {}" + +# Code to be used in simple link tests +lt_simple_link_test_code='public class conftest { public static void main(String[[]] argv) {}; }' + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC="$CC" +lt_save_GCC=$GCC +GCC=yes +CC=${GCJ-"gcj"} +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_TAGVAR(LD, $1)="$LD" +_LT_CC_BASENAME([$compiler]) + +# GCJ did not exist at the time GCC didn't implicitly link libc in. +_LT_TAGVAR(archive_cmds_need_lc, $1)=no + +_LT_TAGVAR(old_archive_cmds, $1)=$old_archive_cmds + +## CAVEAT EMPTOR: +## There is no encapsulation within the following macros, do not change +## the running order or otherwise move them around unless you know exactly +## what you are doing... +if test -n "$compiler"; then + _LT_COMPILER_NO_RTTI($1) + _LT_COMPILER_PIC($1) + _LT_COMPILER_C_O($1) + _LT_COMPILER_FILE_LOCKS($1) + _LT_LINKER_SHLIBS($1) + _LT_LINKER_HARDCODE_LIBPATH($1) + + _LT_CONFIG($1) +fi + +AC_LANG_RESTORE + +GCC=$lt_save_GCC +CC="$lt_save_CC" +])# _LT_LANG_GCJ_CONFIG + + +# _LT_LANG_RC_CONFIG([TAG]) +# ------------------------- +# Ensure that the configuration variables for the Windows resource compiler +# are suitably defined. These variables are subsequently used by _LT_CONFIG +# to write the compiler configuration to `libtool'. +m4_defun([_LT_LANG_RC_CONFIG], +[AC_REQUIRE([LT_PROG_RC])dnl +AC_LANG_SAVE + +# Source file extension for RC test sources. +ac_ext=rc + +# Object file extension for compiled RC test sources. +objext=o +_LT_TAGVAR(objext, $1)=$objext + +# Code to be used in simple compile tests +lt_simple_compile_test_code='sample MENU { MENUITEM "&Soup", 100, CHECKED }' + +# Code to be used in simple link tests +lt_simple_link_test_code="$lt_simple_compile_test_code" + +# ltmain only uses $CC for tagged configurations so make sure $CC is set. +_LT_TAG_COMPILER + +# save warnings/boilerplate of simple test code +_LT_COMPILER_BOILERPLATE +_LT_LINKER_BOILERPLATE + +# Allow CC to be a program name with arguments. +lt_save_CC="$CC" +lt_save_GCC=$GCC +GCC= +CC=${RC-"windres"} +compiler=$CC +_LT_TAGVAR(compiler, $1)=$CC +_LT_CC_BASENAME([$compiler]) +_LT_TAGVAR(lt_cv_prog_compiler_c_o, $1)=yes + +if test -n "$compiler"; then + : + _LT_CONFIG($1) +fi + +GCC=$lt_save_GCC +AC_LANG_RESTORE +CC="$lt_save_CC" +])# _LT_LANG_RC_CONFIG + + +# LT_PROG_GCJ +# ----------- +AC_DEFUN([LT_PROG_GCJ], +[m4_ifdef([AC_PROG_GCJ], [AC_PROG_GCJ], + [m4_ifdef([A][M_PROG_GCJ], [A][M_PROG_GCJ], + [AC_CHECK_TOOL(GCJ, gcj,) + test "x${GCJFLAGS+set}" = xset || GCJFLAGS="-g -O2" + AC_SUBST(GCJFLAGS)])])[]dnl +]) + +# Old name: +AU_ALIAS([LT_AC_PROG_GCJ], [LT_PROG_GCJ]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_GCJ], []) + + +# LT_PROG_RC +# ---------- +AC_DEFUN([LT_PROG_RC], +[AC_CHECK_TOOL(RC, windres,) +]) + +# Old name: +AU_ALIAS([LT_AC_PROG_RC], [LT_PROG_RC]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_RC], []) + + +# _LT_DECL_EGREP +# -------------- +# If we don't have a new enough Autoconf to choose the best grep +# available, choose the one first in the user's PATH. +m4_defun([_LT_DECL_EGREP], +[AC_REQUIRE([AC_PROG_EGREP])dnl +AC_REQUIRE([AC_PROG_FGREP])dnl +test -z "$GREP" && GREP=grep +_LT_DECL([], [GREP], [1], [A grep program that handles long lines]) +_LT_DECL([], [EGREP], [1], [An ERE matcher]) +_LT_DECL([], [FGREP], [1], [A literal string matcher]) +dnl Non-bleeding-edge autoconf doesn't subst GREP, so do it here too +AC_SUBST([GREP]) +]) + + +# _LT_DECL_OBJDUMP +# -------------- +# If we don't have a new enough Autoconf to choose the best objdump +# available, choose the one first in the user's PATH. +m4_defun([_LT_DECL_OBJDUMP], +[AC_CHECK_TOOL(OBJDUMP, objdump, false) +test -z "$OBJDUMP" && OBJDUMP=objdump +_LT_DECL([], [OBJDUMP], [1], [An object symbol dumper]) +AC_SUBST([OBJDUMP]) +]) + + +# _LT_DECL_SED +# ------------ +# Check for a fully-functional sed program, that truncates +# as few characters as possible. Prefer GNU sed if found. +m4_defun([_LT_DECL_SED], +[AC_PROG_SED +test -z "$SED" && SED=sed +Xsed="$SED -e 1s/^X//" +_LT_DECL([], [SED], [1], [A sed program that does not truncate output]) +_LT_DECL([], [Xsed], ["\$SED -e 1s/^X//"], + [Sed that helps us avoid accidentally triggering echo(1) options like -n]) +])# _LT_DECL_SED + +m4_ifndef([AC_PROG_SED], [ +############################################################ +# NOTE: This macro has been submitted for inclusion into # +# GNU Autoconf as AC_PROG_SED. When it is available in # +# a released version of Autoconf we should remove this # +# macro and use it instead. # +############################################################ + +m4_defun([AC_PROG_SED], +[AC_MSG_CHECKING([for a sed that does not truncate output]) +AC_CACHE_VAL(lt_cv_path_SED, +[# Loop through the user's path and test for sed and gsed. +# Then use that list of sed's as ones to test for truncation. +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for lt_ac_prog in sed gsed; do + for ac_exec_ext in '' $ac_executable_extensions; do + if $as_executable_p "$as_dir/$lt_ac_prog$ac_exec_ext"; then + lt_ac_sed_list="$lt_ac_sed_list $as_dir/$lt_ac_prog$ac_exec_ext" + fi + done + done +done +IFS=$as_save_IFS +lt_ac_max=0 +lt_ac_count=0 +# Add /usr/xpg4/bin/sed as it is typically found on Solaris +# along with /bin/sed that truncates output. +for lt_ac_sed in $lt_ac_sed_list /usr/xpg4/bin/sed; do + test ! -f $lt_ac_sed && continue + cat /dev/null > conftest.in + lt_ac_count=0 + echo $ECHO_N "0123456789$ECHO_C" >conftest.in + # Check for GNU sed and select it if it is found. + if "$lt_ac_sed" --version 2>&1 < /dev/null | grep 'GNU' > /dev/null; then + lt_cv_path_SED=$lt_ac_sed + break + fi + while true; do + cat conftest.in conftest.in >conftest.tmp + mv conftest.tmp conftest.in + cp conftest.in conftest.nl + echo >>conftest.nl + $lt_ac_sed -e 's/a$//' < conftest.nl >conftest.out || break + cmp -s conftest.out conftest.nl || break + # 10000 chars as input seems more than enough + test $lt_ac_count -gt 10 && break + lt_ac_count=`expr $lt_ac_count + 1` + if test $lt_ac_count -gt $lt_ac_max; then + lt_ac_max=$lt_ac_count + lt_cv_path_SED=$lt_ac_sed + fi + done +done +]) +SED=$lt_cv_path_SED +AC_SUBST([SED]) +AC_MSG_RESULT([$SED]) +])#AC_PROG_SED +])#m4_ifndef + +# Old name: +AU_ALIAS([LT_AC_PROG_SED], [AC_PROG_SED]) +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([LT_AC_PROG_SED], []) + + +# _LT_CHECK_SHELL_FEATURES +# ------------------------ +# Find out whether the shell is Bourne or XSI compatible, +# or has some other useful features. +m4_defun([_LT_CHECK_SHELL_FEATURES], +[AC_MSG_CHECKING([whether the shell understands some XSI constructs]) +# Try some XSI features +xsi_shell=no +( _lt_dummy="a/b/c" + test "${_lt_dummy##*/},${_lt_dummy%/*},"${_lt_dummy%"$_lt_dummy"}, \ + = c,a/b,, \ + && eval 'test $(( 1 + 1 )) -eq 2 \ + && test "${#_lt_dummy}" -eq 5' ) >/dev/null 2>&1 \ + && xsi_shell=yes +AC_MSG_RESULT([$xsi_shell]) +_LT_CONFIG_LIBTOOL_INIT([xsi_shell='$xsi_shell']) + +AC_MSG_CHECKING([whether the shell understands "+="]) +lt_shell_append=no +( foo=bar; set foo baz; eval "$[1]+=\$[2]" && test "$foo" = barbaz ) \ + >/dev/null 2>&1 \ + && lt_shell_append=yes +AC_MSG_RESULT([$lt_shell_append]) +_LT_CONFIG_LIBTOOL_INIT([lt_shell_append='$lt_shell_append']) + +if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then + lt_unset=unset +else + lt_unset=false +fi +_LT_DECL([], [lt_unset], [0], [whether the shell understands "unset"])dnl + +# test EBCDIC or ASCII +case `echo X|tr X '\101'` in + A) # ASCII based system + # \n is not interpreted correctly by Solaris 8 /usr/ucb/tr + lt_SP2NL='tr \040 \012' + lt_NL2SP='tr \015\012 \040\040' + ;; + *) # EBCDIC based system + lt_SP2NL='tr \100 \n' + lt_NL2SP='tr \r\n \100\100' + ;; +esac +_LT_DECL([SP2NL], [lt_SP2NL], [1], [turn spaces into newlines])dnl +_LT_DECL([NL2SP], [lt_NL2SP], [1], [turn newlines into spaces])dnl +])# _LT_CHECK_SHELL_FEATURES + + +# _LT_PROG_XSI_SHELLFNS +# --------------------- +# Bourne and XSI compatible variants of some useful shell functions. +m4_defun([_LT_PROG_XSI_SHELLFNS], +[case $xsi_shell in + yes) + cat << \_LT_EOF >> "$cfgfile" + +# func_dirname file append nondir_replacement +# Compute the dirname of FILE. If nonempty, add APPEND to the result, +# otherwise set result to NONDIR_REPLACEMENT. +func_dirname () +{ + case ${1} in + */*) func_dirname_result="${1%/*}${2}" ;; + * ) func_dirname_result="${3}" ;; + esac +} + +# func_basename file +func_basename () +{ + func_basename_result="${1##*/}" +} + +# func_dirname_and_basename file append nondir_replacement +# perform func_basename and func_dirname in a single function +# call: +# dirname: Compute the dirname of FILE. If nonempty, +# add APPEND to the result, otherwise set result +# to NONDIR_REPLACEMENT. +# value returned in "$func_dirname_result" +# basename: Compute filename of FILE. +# value retuned in "$func_basename_result" +# Implementation must be kept synchronized with func_dirname +# and func_basename. For efficiency, we do not delegate to +# those functions but instead duplicate the functionality here. +func_dirname_and_basename () +{ + case ${1} in + */*) func_dirname_result="${1%/*}${2}" ;; + * ) func_dirname_result="${3}" ;; + esac + func_basename_result="${1##*/}" +} + +# func_stripname prefix suffix name +# strip PREFIX and SUFFIX off of NAME. +# PREFIX and SUFFIX must not contain globbing or regex special +# characters, hashes, percent signs, but SUFFIX may contain a leading +# dot (in which case that matches only a dot). +func_stripname () +{ + # pdksh 5.2.14 does not do ${X%$Y} correctly if both X and Y are + # positional parameters, so assign one to ordinary parameter first. + func_stripname_result=${3} + func_stripname_result=${func_stripname_result#"${1}"} + func_stripname_result=${func_stripname_result%"${2}"} +} + +# func_opt_split +func_opt_split () +{ + func_opt_split_opt=${1%%=*} + func_opt_split_arg=${1#*=} +} + +# func_lo2o object +func_lo2o () +{ + case ${1} in + *.lo) func_lo2o_result=${1%.lo}.${objext} ;; + *) func_lo2o_result=${1} ;; + esac +} + +# func_xform libobj-or-source +func_xform () +{ + func_xform_result=${1%.*}.lo +} + +# func_arith arithmetic-term... +func_arith () +{ + func_arith_result=$(( $[*] )) +} + +# func_len string +# STRING may not start with a hyphen. +func_len () +{ + func_len_result=${#1} +} + +_LT_EOF + ;; + *) # Bourne compatible functions. + cat << \_LT_EOF >> "$cfgfile" + +# func_dirname file append nondir_replacement +# Compute the dirname of FILE. If nonempty, add APPEND to the result, +# otherwise set result to NONDIR_REPLACEMENT. +func_dirname () +{ + # Extract subdirectory from the argument. + func_dirname_result=`$ECHO "X${1}" | $Xsed -e "$dirname"` + if test "X$func_dirname_result" = "X${1}"; then + func_dirname_result="${3}" + else + func_dirname_result="$func_dirname_result${2}" + fi +} + +# func_basename file +func_basename () +{ + func_basename_result=`$ECHO "X${1}" | $Xsed -e "$basename"` +} + +dnl func_dirname_and_basename +dnl A portable version of this function is already defined in general.m4sh +dnl so there is no need for it here. + +# func_stripname prefix suffix name +# strip PREFIX and SUFFIX off of NAME. +# PREFIX and SUFFIX must not contain globbing or regex special +# characters, hashes, percent signs, but SUFFIX may contain a leading +# dot (in which case that matches only a dot). +# func_strip_suffix prefix name +func_stripname () +{ + case ${2} in + .*) func_stripname_result=`$ECHO "X${3}" \ + | $Xsed -e "s%^${1}%%" -e "s%\\\\${2}\$%%"`;; + *) func_stripname_result=`$ECHO "X${3}" \ + | $Xsed -e "s%^${1}%%" -e "s%${2}\$%%"`;; + esac +} + +# sed scripts: +my_sed_long_opt='1s/^\(-[[^=]]*\)=.*/\1/;q' +my_sed_long_arg='1s/^-[[^=]]*=//' + +# func_opt_split +func_opt_split () +{ + func_opt_split_opt=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_opt"` + func_opt_split_arg=`$ECHO "X${1}" | $Xsed -e "$my_sed_long_arg"` +} + +# func_lo2o object +func_lo2o () +{ + func_lo2o_result=`$ECHO "X${1}" | $Xsed -e "$lo2o"` +} + +# func_xform libobj-or-source +func_xform () +{ + func_xform_result=`$ECHO "X${1}" | $Xsed -e 's/\.[[^.]]*$/.lo/'` +} + +# func_arith arithmetic-term... +func_arith () +{ + func_arith_result=`expr "$[@]"` +} + +# func_len string +# STRING may not start with a hyphen. +func_len () +{ + func_len_result=`expr "$[1]" : ".*" 2>/dev/null || echo $max_cmd_len` +} + +_LT_EOF +esac + +case $lt_shell_append in + yes) + cat << \_LT_EOF >> "$cfgfile" + +# func_append var value +# Append VALUE to the end of shell variable VAR. +func_append () +{ + eval "$[1]+=\$[2]" +} +_LT_EOF + ;; + *) + cat << \_LT_EOF >> "$cfgfile" + +# func_append var value +# Append VALUE to the end of shell variable VAR. +func_append () +{ + eval "$[1]=\$$[1]\$[2]" +} + +_LT_EOF + ;; + esac +]) diff --git a/shared/sentry/external/breakpad/m4/ltoptions.m4 b/shared/sentry/external/breakpad/m4/ltoptions.m4 new file mode 100644 index 000000000..34151a3ba --- /dev/null +++ b/shared/sentry/external/breakpad/m4/ltoptions.m4 @@ -0,0 +1,368 @@ +# Helper functions for option handling. -*- Autoconf -*- +# +# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc. +# Written by Gary V. Vaughan, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 6 ltoptions.m4 + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTOPTIONS_VERSION], [m4_if([1])]) + + +# _LT_MANGLE_OPTION(MACRO-NAME, OPTION-NAME) +# ------------------------------------------ +m4_define([_LT_MANGLE_OPTION], +[[_LT_OPTION_]m4_bpatsubst($1__$2, [[^a-zA-Z0-9_]], [_])]) + + +# _LT_SET_OPTION(MACRO-NAME, OPTION-NAME) +# --------------------------------------- +# Set option OPTION-NAME for macro MACRO-NAME, and if there is a +# matching handler defined, dispatch to it. Other OPTION-NAMEs are +# saved as a flag. +m4_define([_LT_SET_OPTION], +[m4_define(_LT_MANGLE_OPTION([$1], [$2]))dnl +m4_ifdef(_LT_MANGLE_DEFUN([$1], [$2]), + _LT_MANGLE_DEFUN([$1], [$2]), + [m4_warning([Unknown $1 option `$2'])])[]dnl +]) + + +# _LT_IF_OPTION(MACRO-NAME, OPTION-NAME, IF-SET, [IF-NOT-SET]) +# ------------------------------------------------------------ +# Execute IF-SET if OPTION is set, IF-NOT-SET otherwise. +m4_define([_LT_IF_OPTION], +[m4_ifdef(_LT_MANGLE_OPTION([$1], [$2]), [$3], [$4])]) + + +# _LT_UNLESS_OPTIONS(MACRO-NAME, OPTION-LIST, IF-NOT-SET) +# ------------------------------------------------------- +# Execute IF-NOT-SET unless all options in OPTION-LIST for MACRO-NAME +# are set. +m4_define([_LT_UNLESS_OPTIONS], +[m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), + [m4_ifdef(_LT_MANGLE_OPTION([$1], _LT_Option), + [m4_define([$0_found])])])[]dnl +m4_ifdef([$0_found], [m4_undefine([$0_found])], [$3 +])[]dnl +]) + + +# _LT_SET_OPTIONS(MACRO-NAME, OPTION-LIST) +# ---------------------------------------- +# OPTION-LIST is a space-separated list of Libtool options associated +# with MACRO-NAME. If any OPTION has a matching handler declared with +# LT_OPTION_DEFINE, dispatch to that macro; otherwise complain about +# the unknown option and exit. +m4_defun([_LT_SET_OPTIONS], +[# Set options +m4_foreach([_LT_Option], m4_split(m4_normalize([$2])), + [_LT_SET_OPTION([$1], _LT_Option)]) + +m4_if([$1],[LT_INIT],[ + dnl + dnl Simply set some default values (i.e off) if boolean options were not + dnl specified: + _LT_UNLESS_OPTIONS([LT_INIT], [dlopen], [enable_dlopen=no + ]) + _LT_UNLESS_OPTIONS([LT_INIT], [win32-dll], [enable_win32_dll=no + ]) + dnl + dnl If no reference was made to various pairs of opposing options, then + dnl we run the default mode handler for the pair. For example, if neither + dnl `shared' nor `disable-shared' was passed, we enable building of shared + dnl archives by default: + _LT_UNLESS_OPTIONS([LT_INIT], [shared disable-shared], [_LT_ENABLE_SHARED]) + _LT_UNLESS_OPTIONS([LT_INIT], [static disable-static], [_LT_ENABLE_STATIC]) + _LT_UNLESS_OPTIONS([LT_INIT], [pic-only no-pic], [_LT_WITH_PIC]) + _LT_UNLESS_OPTIONS([LT_INIT], [fast-install disable-fast-install], + [_LT_ENABLE_FAST_INSTALL]) + ]) +])# _LT_SET_OPTIONS + + +## --------------------------------- ## +## Macros to handle LT_INIT options. ## +## --------------------------------- ## + +# _LT_MANGLE_DEFUN(MACRO-NAME, OPTION-NAME) +# ----------------------------------------- +m4_define([_LT_MANGLE_DEFUN], +[[_LT_OPTION_DEFUN_]m4_bpatsubst(m4_toupper([$1__$2]), [[^A-Z0-9_]], [_])]) + + +# LT_OPTION_DEFINE(MACRO-NAME, OPTION-NAME, CODE) +# ----------------------------------------------- +m4_define([LT_OPTION_DEFINE], +[m4_define(_LT_MANGLE_DEFUN([$1], [$2]), [$3])[]dnl +])# LT_OPTION_DEFINE + + +# dlopen +# ------ +LT_OPTION_DEFINE([LT_INIT], [dlopen], [enable_dlopen=yes +]) + +AU_DEFUN([AC_LIBTOOL_DLOPEN], +[_LT_SET_OPTION([LT_INIT], [dlopen]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the `dlopen' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_DLOPEN], []) + + +# win32-dll +# --------- +# Declare package support for building win32 dll's. +LT_OPTION_DEFINE([LT_INIT], [win32-dll], +[enable_win32_dll=yes + +case $host in +*-*-cygwin* | *-*-mingw* | *-*-pw32* | *-cegcc*) + AC_CHECK_TOOL(AS, as, false) + AC_CHECK_TOOL(DLLTOOL, dlltool, false) + AC_CHECK_TOOL(OBJDUMP, objdump, false) + ;; +esac + +test -z "$AS" && AS=as +_LT_DECL([], [AS], [0], [Assembler program])dnl + +test -z "$DLLTOOL" && DLLTOOL=dlltool +_LT_DECL([], [DLLTOOL], [0], [DLL creation program])dnl + +test -z "$OBJDUMP" && OBJDUMP=objdump +_LT_DECL([], [OBJDUMP], [0], [Object dumper program])dnl +])# win32-dll + +AU_DEFUN([AC_LIBTOOL_WIN32_DLL], +[AC_REQUIRE([AC_CANONICAL_HOST])dnl +_LT_SET_OPTION([LT_INIT], [win32-dll]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the `win32-dll' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_WIN32_DLL], []) + + +# _LT_ENABLE_SHARED([DEFAULT]) +# ---------------------------- +# implement the --enable-shared flag, and supports the `shared' and +# `disable-shared' LT_INIT options. +# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. +m4_define([_LT_ENABLE_SHARED], +[m4_define([_LT_ENABLE_SHARED_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([shared], + [AS_HELP_STRING([--enable-shared@<:@=PKGS@:>@], + [build shared libraries @<:@default=]_LT_ENABLE_SHARED_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_shared=yes ;; + no) enable_shared=no ;; + *) + enable_shared=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_shared=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac], + [enable_shared=]_LT_ENABLE_SHARED_DEFAULT) + + _LT_DECL([build_libtool_libs], [enable_shared], [0], + [Whether or not to build shared libraries]) +])# _LT_ENABLE_SHARED + +LT_OPTION_DEFINE([LT_INIT], [shared], [_LT_ENABLE_SHARED([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-shared], [_LT_ENABLE_SHARED([no])]) + +# Old names: +AC_DEFUN([AC_ENABLE_SHARED], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[shared]) +]) + +AC_DEFUN([AC_DISABLE_SHARED], +[_LT_SET_OPTION([LT_INIT], [disable-shared]) +]) + +AU_DEFUN([AM_ENABLE_SHARED], [AC_ENABLE_SHARED($@)]) +AU_DEFUN([AM_DISABLE_SHARED], [AC_DISABLE_SHARED($@)]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_ENABLE_SHARED], []) +dnl AC_DEFUN([AM_DISABLE_SHARED], []) + + + +# _LT_ENABLE_STATIC([DEFAULT]) +# ---------------------------- +# implement the --enable-static flag, and support the `static' and +# `disable-static' LT_INIT options. +# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. +m4_define([_LT_ENABLE_STATIC], +[m4_define([_LT_ENABLE_STATIC_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([static], + [AS_HELP_STRING([--enable-static@<:@=PKGS@:>@], + [build static libraries @<:@default=]_LT_ENABLE_STATIC_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_static=yes ;; + no) enable_static=no ;; + *) + enable_static=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_static=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac], + [enable_static=]_LT_ENABLE_STATIC_DEFAULT) + + _LT_DECL([build_old_libs], [enable_static], [0], + [Whether or not to build static libraries]) +])# _LT_ENABLE_STATIC + +LT_OPTION_DEFINE([LT_INIT], [static], [_LT_ENABLE_STATIC([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-static], [_LT_ENABLE_STATIC([no])]) + +# Old names: +AC_DEFUN([AC_ENABLE_STATIC], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[static]) +]) + +AC_DEFUN([AC_DISABLE_STATIC], +[_LT_SET_OPTION([LT_INIT], [disable-static]) +]) + +AU_DEFUN([AM_ENABLE_STATIC], [AC_ENABLE_STATIC($@)]) +AU_DEFUN([AM_DISABLE_STATIC], [AC_DISABLE_STATIC($@)]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AM_ENABLE_STATIC], []) +dnl AC_DEFUN([AM_DISABLE_STATIC], []) + + + +# _LT_ENABLE_FAST_INSTALL([DEFAULT]) +# ---------------------------------- +# implement the --enable-fast-install flag, and support the `fast-install' +# and `disable-fast-install' LT_INIT options. +# DEFAULT is either `yes' or `no'. If omitted, it defaults to `yes'. +m4_define([_LT_ENABLE_FAST_INSTALL], +[m4_define([_LT_ENABLE_FAST_INSTALL_DEFAULT], [m4_if($1, no, no, yes)])dnl +AC_ARG_ENABLE([fast-install], + [AS_HELP_STRING([--enable-fast-install@<:@=PKGS@:>@], + [optimize for fast installation @<:@default=]_LT_ENABLE_FAST_INSTALL_DEFAULT[@:>@])], + [p=${PACKAGE-default} + case $enableval in + yes) enable_fast_install=yes ;; + no) enable_fast_install=no ;; + *) + enable_fast_install=no + # Look at the argument we got. We use all the common list separators. + lt_save_ifs="$IFS"; IFS="${IFS}$PATH_SEPARATOR," + for pkg in $enableval; do + IFS="$lt_save_ifs" + if test "X$pkg" = "X$p"; then + enable_fast_install=yes + fi + done + IFS="$lt_save_ifs" + ;; + esac], + [enable_fast_install=]_LT_ENABLE_FAST_INSTALL_DEFAULT) + +_LT_DECL([fast_install], [enable_fast_install], [0], + [Whether or not to optimize for fast installation])dnl +])# _LT_ENABLE_FAST_INSTALL + +LT_OPTION_DEFINE([LT_INIT], [fast-install], [_LT_ENABLE_FAST_INSTALL([yes])]) +LT_OPTION_DEFINE([LT_INIT], [disable-fast-install], [_LT_ENABLE_FAST_INSTALL([no])]) + +# Old names: +AU_DEFUN([AC_ENABLE_FAST_INSTALL], +[_LT_SET_OPTION([LT_INIT], m4_if([$1], [no], [disable-])[fast-install]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you put +the `fast-install' option into LT_INIT's first parameter.]) +]) + +AU_DEFUN([AC_DISABLE_FAST_INSTALL], +[_LT_SET_OPTION([LT_INIT], [disable-fast-install]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you put +the `disable-fast-install' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_ENABLE_FAST_INSTALL], []) +dnl AC_DEFUN([AM_DISABLE_FAST_INSTALL], []) + + +# _LT_WITH_PIC([MODE]) +# -------------------- +# implement the --with-pic flag, and support the `pic-only' and `no-pic' +# LT_INIT options. +# MODE is either `yes' or `no'. If omitted, it defaults to `both'. +m4_define([_LT_WITH_PIC], +[AC_ARG_WITH([pic], + [AS_HELP_STRING([--with-pic], + [try to use only PIC/non-PIC objects @<:@default=use both@:>@])], + [pic_mode="$withval"], + [pic_mode=default]) + +test -z "$pic_mode" && pic_mode=m4_default([$1], [default]) + +_LT_DECL([], [pic_mode], [0], [What type of objects to build])dnl +])# _LT_WITH_PIC + +LT_OPTION_DEFINE([LT_INIT], [pic-only], [_LT_WITH_PIC([yes])]) +LT_OPTION_DEFINE([LT_INIT], [no-pic], [_LT_WITH_PIC([no])]) + +# Old name: +AU_DEFUN([AC_LIBTOOL_PICMODE], +[_LT_SET_OPTION([LT_INIT], [pic-only]) +AC_DIAGNOSE([obsolete], +[$0: Remove this warning and the call to _LT_SET_OPTION when you +put the `pic-only' option into LT_INIT's first parameter.]) +]) + +dnl aclocal-1.4 backwards compatibility: +dnl AC_DEFUN([AC_LIBTOOL_PICMODE], []) + +## ----------------- ## +## LTDL_INIT Options ## +## ----------------- ## + +m4_define([_LTDL_MODE], []) +LT_OPTION_DEFINE([LTDL_INIT], [nonrecursive], + [m4_define([_LTDL_MODE], [nonrecursive])]) +LT_OPTION_DEFINE([LTDL_INIT], [recursive], + [m4_define([_LTDL_MODE], [recursive])]) +LT_OPTION_DEFINE([LTDL_INIT], [subproject], + [m4_define([_LTDL_MODE], [subproject])]) + +m4_define([_LTDL_TYPE], []) +LT_OPTION_DEFINE([LTDL_INIT], [installable], + [m4_define([_LTDL_TYPE], [installable])]) +LT_OPTION_DEFINE([LTDL_INIT], [convenience], + [m4_define([_LTDL_TYPE], [convenience])]) diff --git a/shared/sentry/external/breakpad/m4/ltsugar.m4 b/shared/sentry/external/breakpad/m4/ltsugar.m4 new file mode 100644 index 000000000..9000a057d --- /dev/null +++ b/shared/sentry/external/breakpad/m4/ltsugar.m4 @@ -0,0 +1,123 @@ +# ltsugar.m4 -- libtool m4 base layer. -*-Autoconf-*- +# +# Copyright (C) 2004, 2005, 2007, 2008 Free Software Foundation, Inc. +# Written by Gary V. Vaughan, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 6 ltsugar.m4 + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTSUGAR_VERSION], [m4_if([0.1])]) + + +# lt_join(SEP, ARG1, [ARG2...]) +# ----------------------------- +# Produce ARG1SEPARG2...SEPARGn, omitting [] arguments and their +# associated separator. +# Needed until we can rely on m4_join from Autoconf 2.62, since all earlier +# versions in m4sugar had bugs. +m4_define([lt_join], +[m4_if([$#], [1], [], + [$#], [2], [[$2]], + [m4_if([$2], [], [], [[$2]_])$0([$1], m4_shift(m4_shift($@)))])]) +m4_define([_lt_join], +[m4_if([$#$2], [2], [], + [m4_if([$2], [], [], [[$1$2]])$0([$1], m4_shift(m4_shift($@)))])]) + + +# lt_car(LIST) +# lt_cdr(LIST) +# ------------ +# Manipulate m4 lists. +# These macros are necessary as long as will still need to support +# Autoconf-2.59 which quotes differently. +m4_define([lt_car], [[$1]]) +m4_define([lt_cdr], +[m4_if([$#], 0, [m4_fatal([$0: cannot be called without arguments])], + [$#], 1, [], + [m4_dquote(m4_shift($@))])]) +m4_define([lt_unquote], $1) + + +# lt_append(MACRO-NAME, STRING, [SEPARATOR]) +# ------------------------------------------ +# Redefine MACRO-NAME to hold its former content plus `SEPARATOR'`STRING'. +# Note that neither SEPARATOR nor STRING are expanded; they are appended +# to MACRO-NAME as is (leaving the expansion for when MACRO-NAME is invoked). +# No SEPARATOR is output if MACRO-NAME was previously undefined (different +# than defined and empty). +# +# This macro is needed until we can rely on Autoconf 2.62, since earlier +# versions of m4sugar mistakenly expanded SEPARATOR but not STRING. +m4_define([lt_append], +[m4_define([$1], + m4_ifdef([$1], [m4_defn([$1])[$3]])[$2])]) + + + +# lt_combine(SEP, PREFIX-LIST, INFIX, SUFFIX1, [SUFFIX2...]) +# ---------------------------------------------------------- +# Produce a SEP delimited list of all paired combinations of elements of +# PREFIX-LIST with SUFFIX1 through SUFFIXn. Each element of the list +# has the form PREFIXmINFIXSUFFIXn. +# Needed until we can rely on m4_combine added in Autoconf 2.62. +m4_define([lt_combine], +[m4_if(m4_eval([$# > 3]), [1], + [m4_pushdef([_Lt_sep], [m4_define([_Lt_sep], m4_defn([lt_car]))])]]dnl +[[m4_foreach([_Lt_prefix], [$2], + [m4_foreach([_Lt_suffix], + ]m4_dquote(m4_dquote(m4_shift(m4_shift(m4_shift($@)))))[, + [_Lt_sep([$1])[]m4_defn([_Lt_prefix])[$3]m4_defn([_Lt_suffix])])])])]) + + +# lt_if_append_uniq(MACRO-NAME, VARNAME, [SEPARATOR], [UNIQ], [NOT-UNIQ]) +# ----------------------------------------------------------------------- +# Iff MACRO-NAME does not yet contain VARNAME, then append it (delimited +# by SEPARATOR if supplied) and expand UNIQ, else NOT-UNIQ. +m4_define([lt_if_append_uniq], +[m4_ifdef([$1], + [m4_if(m4_index([$3]m4_defn([$1])[$3], [$3$2$3]), [-1], + [lt_append([$1], [$2], [$3])$4], + [$5])], + [lt_append([$1], [$2], [$3])$4])]) + + +# lt_dict_add(DICT, KEY, VALUE) +# ----------------------------- +m4_define([lt_dict_add], +[m4_define([$1($2)], [$3])]) + + +# lt_dict_add_subkey(DICT, KEY, SUBKEY, VALUE) +# -------------------------------------------- +m4_define([lt_dict_add_subkey], +[m4_define([$1($2:$3)], [$4])]) + + +# lt_dict_fetch(DICT, KEY, [SUBKEY]) +# ---------------------------------- +m4_define([lt_dict_fetch], +[m4_ifval([$3], + m4_ifdef([$1($2:$3)], [m4_defn([$1($2:$3)])]), + m4_ifdef([$1($2)], [m4_defn([$1($2)])]))]) + + +# lt_if_dict_fetch(DICT, KEY, [SUBKEY], VALUE, IF-TRUE, [IF-FALSE]) +# ----------------------------------------------------------------- +m4_define([lt_if_dict_fetch], +[m4_if(lt_dict_fetch([$1], [$2], [$3]), [$4], + [$5], + [$6])]) + + +# lt_dict_filter(DICT, [SUBKEY], VALUE, [SEPARATOR], KEY, [...]) +# -------------------------------------------------------------- +m4_define([lt_dict_filter], +[m4_if([$5], [], [], + [lt_join(m4_quote(m4_default([$4], [[, ]])), + lt_unquote(m4_split(m4_normalize(m4_foreach(_Lt_key, lt_car([m4_shiftn(4, $@)]), + [lt_if_dict_fetch([$1], _Lt_key, [$2], [$3], [_Lt_key ])])))))])[]dnl +]) diff --git a/shared/sentry/external/breakpad/m4/ltversion.m4 b/shared/sentry/external/breakpad/m4/ltversion.m4 new file mode 100644 index 000000000..f3c530980 --- /dev/null +++ b/shared/sentry/external/breakpad/m4/ltversion.m4 @@ -0,0 +1,23 @@ +# ltversion.m4 -- version numbers -*- Autoconf -*- +# +# Copyright (C) 2004 Free Software Foundation, Inc. +# Written by Scott James Remnant, 2004 +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# Generated from ltversion.in. + +# serial 3017 ltversion.m4 +# This file is part of GNU Libtool + +m4_define([LT_PACKAGE_VERSION], [2.2.6b]) +m4_define([LT_PACKAGE_REVISION], [1.3017]) + +AC_DEFUN([LTVERSION_VERSION], +[macro_version='2.2.6b' +macro_revision='1.3017' +_LT_DECL(, macro_version, 0, [Which release of libtool.m4 was used?]) +_LT_DECL(, macro_revision, 0) +]) diff --git a/shared/sentry/external/breakpad/m4/lt~obsolete.m4 b/shared/sentry/external/breakpad/m4/lt~obsolete.m4 new file mode 100644 index 000000000..637bb2066 --- /dev/null +++ b/shared/sentry/external/breakpad/m4/lt~obsolete.m4 @@ -0,0 +1,92 @@ +# lt~obsolete.m4 -- aclocal satisfying obsolete definitions. -*-Autoconf-*- +# +# Copyright (C) 2004, 2005, 2007 Free Software Foundation, Inc. +# Written by Scott James Remnant, 2004. +# +# This file is free software; the Free Software Foundation gives +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 4 lt~obsolete.m4 + +# These exist entirely to fool aclocal when bootstrapping libtool. +# +# In the past libtool.m4 has provided macros via AC_DEFUN (or AU_DEFUN) +# which have later been changed to m4_define as they aren't part of the +# exported API, or moved to Autoconf or Automake where they belong. +# +# The trouble is, aclocal is a bit thick. It'll see the old AC_DEFUN +# in /usr/share/aclocal/libtool.m4 and remember it, then when it sees us +# using a macro with the same name in our local m4/libtool.m4 it'll +# pull the old libtool.m4 in (it doesn't see our shiny new m4_define +# and doesn't know about Autoconf macros at all.) +# +# So we provide this file, which has a silly filename so it's always +# included after everything else. This provides aclocal with the +# AC_DEFUNs it wants, but when m4 processes it, it doesn't do anything +# because those macros already exist, or will be overwritten later. +# We use AC_DEFUN over AU_DEFUN for compatibility with aclocal-1.6. +# +# Anytime we withdraw an AC_DEFUN or AU_DEFUN, remember to add it here. +# Yes, that means every name once taken will need to remain here until +# we give up compatibility with versions before 1.7, at which point +# we need to keep only those names which we still refer to. + +# This is to help aclocal find these macros, as it can't see m4_define. +AC_DEFUN([LTOBSOLETE_VERSION], [m4_if([1])]) + +m4_ifndef([AC_LIBTOOL_LINKER_OPTION], [AC_DEFUN([AC_LIBTOOL_LINKER_OPTION])]) +m4_ifndef([AC_PROG_EGREP], [AC_DEFUN([AC_PROG_EGREP])]) +m4_ifndef([_LT_AC_PROG_ECHO_BACKSLASH], [AC_DEFUN([_LT_AC_PROG_ECHO_BACKSLASH])]) +m4_ifndef([_LT_AC_SHELL_INIT], [AC_DEFUN([_LT_AC_SHELL_INIT])]) +m4_ifndef([_LT_AC_SYS_LIBPATH_AIX], [AC_DEFUN([_LT_AC_SYS_LIBPATH_AIX])]) +m4_ifndef([_LT_PROG_LTMAIN], [AC_DEFUN([_LT_PROG_LTMAIN])]) +m4_ifndef([_LT_AC_TAGVAR], [AC_DEFUN([_LT_AC_TAGVAR])]) +m4_ifndef([AC_LTDL_ENABLE_INSTALL], [AC_DEFUN([AC_LTDL_ENABLE_INSTALL])]) +m4_ifndef([AC_LTDL_PREOPEN], [AC_DEFUN([AC_LTDL_PREOPEN])]) +m4_ifndef([_LT_AC_SYS_COMPILER], [AC_DEFUN([_LT_AC_SYS_COMPILER])]) +m4_ifndef([_LT_AC_LOCK], [AC_DEFUN([_LT_AC_LOCK])]) +m4_ifndef([AC_LIBTOOL_SYS_OLD_ARCHIVE], [AC_DEFUN([AC_LIBTOOL_SYS_OLD_ARCHIVE])]) +m4_ifndef([_LT_AC_TRY_DLOPEN_SELF], [AC_DEFUN([_LT_AC_TRY_DLOPEN_SELF])]) +m4_ifndef([AC_LIBTOOL_PROG_CC_C_O], [AC_DEFUN([AC_LIBTOOL_PROG_CC_C_O])]) +m4_ifndef([AC_LIBTOOL_SYS_HARD_LINK_LOCKS], [AC_DEFUN([AC_LIBTOOL_SYS_HARD_LINK_LOCKS])]) +m4_ifndef([AC_LIBTOOL_OBJDIR], [AC_DEFUN([AC_LIBTOOL_OBJDIR])]) +m4_ifndef([AC_LTDL_OBJDIR], [AC_DEFUN([AC_LTDL_OBJDIR])]) +m4_ifndef([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH], [AC_DEFUN([AC_LIBTOOL_PROG_LD_HARDCODE_LIBPATH])]) +m4_ifndef([AC_LIBTOOL_SYS_LIB_STRIP], [AC_DEFUN([AC_LIBTOOL_SYS_LIB_STRIP])]) +m4_ifndef([AC_PATH_MAGIC], [AC_DEFUN([AC_PATH_MAGIC])]) +m4_ifndef([AC_PROG_LD_GNU], [AC_DEFUN([AC_PROG_LD_GNU])]) +m4_ifndef([AC_PROG_LD_RELOAD_FLAG], [AC_DEFUN([AC_PROG_LD_RELOAD_FLAG])]) +m4_ifndef([AC_DEPLIBS_CHECK_METHOD], [AC_DEFUN([AC_DEPLIBS_CHECK_METHOD])]) +m4_ifndef([AC_LIBTOOL_PROG_COMPILER_NO_RTTI], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_NO_RTTI])]) +m4_ifndef([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE], [AC_DEFUN([AC_LIBTOOL_SYS_GLOBAL_SYMBOL_PIPE])]) +m4_ifndef([AC_LIBTOOL_PROG_COMPILER_PIC], [AC_DEFUN([AC_LIBTOOL_PROG_COMPILER_PIC])]) +m4_ifndef([AC_LIBTOOL_PROG_LD_SHLIBS], [AC_DEFUN([AC_LIBTOOL_PROG_LD_SHLIBS])]) +m4_ifndef([AC_LIBTOOL_POSTDEP_PREDEP], [AC_DEFUN([AC_LIBTOOL_POSTDEP_PREDEP])]) +m4_ifndef([LT_AC_PROG_EGREP], [AC_DEFUN([LT_AC_PROG_EGREP])]) +m4_ifndef([LT_AC_PROG_SED], [AC_DEFUN([LT_AC_PROG_SED])]) +m4_ifndef([_LT_CC_BASENAME], [AC_DEFUN([_LT_CC_BASENAME])]) +m4_ifndef([_LT_COMPILER_BOILERPLATE], [AC_DEFUN([_LT_COMPILER_BOILERPLATE])]) +m4_ifndef([_LT_LINKER_BOILERPLATE], [AC_DEFUN([_LT_LINKER_BOILERPLATE])]) +m4_ifndef([_AC_PROG_LIBTOOL], [AC_DEFUN([_AC_PROG_LIBTOOL])]) +m4_ifndef([AC_LIBTOOL_SETUP], [AC_DEFUN([AC_LIBTOOL_SETUP])]) +m4_ifndef([_LT_AC_CHECK_DLFCN], [AC_DEFUN([_LT_AC_CHECK_DLFCN])]) +m4_ifndef([AC_LIBTOOL_SYS_DYNAMIC_LINKER], [AC_DEFUN([AC_LIBTOOL_SYS_DYNAMIC_LINKER])]) +m4_ifndef([_LT_AC_TAGCONFIG], [AC_DEFUN([_LT_AC_TAGCONFIG])]) +m4_ifndef([AC_DISABLE_FAST_INSTALL], [AC_DEFUN([AC_DISABLE_FAST_INSTALL])]) +m4_ifndef([_LT_AC_LANG_CXX], [AC_DEFUN([_LT_AC_LANG_CXX])]) +m4_ifndef([_LT_AC_LANG_F77], [AC_DEFUN([_LT_AC_LANG_F77])]) +m4_ifndef([_LT_AC_LANG_GCJ], [AC_DEFUN([_LT_AC_LANG_GCJ])]) +m4_ifndef([AC_LIBTOOL_RC], [AC_DEFUN([AC_LIBTOOL_RC])]) +m4_ifndef([AC_LIBTOOL_LANG_C_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_C_CONFIG])]) +m4_ifndef([_LT_AC_LANG_C_CONFIG], [AC_DEFUN([_LT_AC_LANG_C_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_CXX_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_CXX_CONFIG])]) +m4_ifndef([_LT_AC_LANG_CXX_CONFIG], [AC_DEFUN([_LT_AC_LANG_CXX_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_F77_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_F77_CONFIG])]) +m4_ifndef([_LT_AC_LANG_F77_CONFIG], [AC_DEFUN([_LT_AC_LANG_F77_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_GCJ_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_GCJ_CONFIG])]) +m4_ifndef([_LT_AC_LANG_GCJ_CONFIG], [AC_DEFUN([_LT_AC_LANG_GCJ_CONFIG])]) +m4_ifndef([AC_LIBTOOL_LANG_RC_CONFIG], [AC_DEFUN([AC_LIBTOOL_LANG_RC_CONFIG])]) +m4_ifndef([_LT_AC_LANG_RC_CONFIG], [AC_DEFUN([_LT_AC_LANG_RC_CONFIG])]) +m4_ifndef([AC_LIBTOOL_CONFIG], [AC_DEFUN([AC_LIBTOOL_CONFIG])]) +m4_ifndef([_LT_AC_FILE_LTDLL_C], [AC_DEFUN([_LT_AC_FILE_LTDLL_C])]) diff --git a/shared/sentry/external/breakpad/src/breakpad_googletest_includes.h b/shared/sentry/external/breakpad/src/breakpad_googletest_includes.h new file mode 100644 index 000000000..19a3e9807 --- /dev/null +++ b/shared/sentry/external/breakpad/src/breakpad_googletest_includes.h @@ -0,0 +1,57 @@ +// 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 BREAKPAD_GOOGLETEST_INCLUDES_H__ +#define BREAKPAD_GOOGLETEST_INCLUDES_H__ + +#include "gtest/gtest.h" +#include "gmock/gmock.h" + +// If AddressSanitizer is used, NULL pointer dereferences generate SIGILL +// (illegal instruction) instead of SIGSEGV (segmentation fault). Also, +// the number of memory regions differs, so there is no point in running +// this test if AddressSanitizer is used. +// +// Ideally we'd use this attribute to disable ASAN on a per-func basis, +// but this doesn't seem to actually work, and it's changed names over +// time. So just stick with disabling the actual tests. +// http://crbug.com/304575 +//#define NO_ASAN __attribute__((no_sanitize_address)) +#if defined(__clang__) && defined(__has_feature) +// Have to keep this check sep from above as newer gcc will barf on it. +# if __has_feature(address_sanitizer) +# define ADDRESS_SANITIZER +# endif +#elif defined(__GNUC__) && defined(__SANITIZE_ADDRESS__) +# define ADDRESS_SANITIZER +#else +# undef ADDRESS_SANITIZER +#endif + +#endif // BREAKPAD_GOOGLETEST_INCLUDES_H__ diff --git a/shared/sentry/external/breakpad/src/build/all.gyp b/shared/sentry/external/breakpad/src/build/all.gyp new file mode 100644 index 000000000..4b59d917b --- /dev/null +++ b/shared/sentry/external/breakpad/src/build/all.gyp @@ -0,0 +1,41 @@ +# 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. + +{ + 'targets': [ + { + 'target_name': 'All', + 'type': 'none', + 'dependencies': [ + '../common/common.gyp:*', + '../processor/processor.gyp:*', + '../tools/tools.gyp:*', + ], + }, + ], +} diff --git a/shared/sentry/external/breakpad/src/build/common.gypi b/shared/sentry/external/breakpad/src/build/common.gypi new file mode 100644 index 000000000..29990c659 --- /dev/null +++ b/shared/sentry/external/breakpad/src/build/common.gypi @@ -0,0 +1,1045 @@ +# Copyright 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. + +# IMPORTANT: +# Please don't directly include this file if you are building via gyp_chromium, +# since gyp_chromium is automatically forcing its inclusion. +{ + 'variables': { + # Variables expected to be overriden on the GYP command line (-D) or by + # ~/.gyp/include.gypi. + + # Putting a variables dict inside another variables dict looks kind of + # weird. This is necessary to get these variables defined for the conditions + # within this variables dict that operate on these variables. + 'variables': { + 'variables': { + # Compute the architecture that we're building on. + 'conditions': [ + [ 'OS=="linux" or OS=="freebsd" or OS=="openbsd"', { + # This handles the Linux platforms we generally deal with. Anything + # else gets passed through, which probably won't work very well; such + # hosts should pass an explicit target_arch to gyp. + 'host_arch%': + '. + # Additional documentation on these macros is available at + # http://developer.apple.com/mac/library/technotes/tn2002/tn2064.html#SECTION3 + # Chrome normally builds with the Mac OS X 10.5 SDK and sets the + # deployment target to 10.5. Other projects, such as O3D, may override + # these defaults. + 'mac_sdk%': '10.5', + 'mac_deployment_target%': '10.5', + + # Set to 1 to enable code coverage. In addition to build changes + # (e.g. extra CFLAGS), also creates a new target in the src/chrome + # project file called "coverage". + # Currently ignored on Windows. + 'coverage%': 0, + + # Although base/allocator lets you select a heap library via an + # environment variable, the libcmt shim it uses sometimes gets in + # the way. To disable it entirely, and switch to normal msvcrt, do e.g. + # 'win_use_allocator_shim': 0, + # 'win_release_RuntimeLibrary': 2 + # to ~/.gyp/include.gypi, gclient runhooks --force, and do a release build. + 'win_use_allocator_shim%': 1, # 0 = shim allocator via libcmt; 1 = msvcrt + + # Whether usage of OpenMAX is enabled. + 'enable_openmax%': 0, + + # TODO(bradnelson): eliminate this when possible. + # To allow local gyp files to prevent release.vsprops from being included. + # Yes(1) means include release.vsprops. + # Once all vsprops settings are migrated into gyp, this can go away. + 'msvs_use_common_release%': 1, + + # TODO(bradnelson): eliminate this when possible. + # To allow local gyp files to override additional linker options for msvs. + # Yes(1) means set use the common linker options. + 'msvs_use_common_linker_extras%': 1, + + # TODO(sgk): eliminate this if possible. + # It would be nicer to support this via a setting in 'target_defaults' + # in chrome/app/locales/locales.gypi overriding the setting in the + # 'Debug' configuration in the 'target_defaults' dict below, + # but that doesn't work as we'd like. + 'msvs_debug_link_incremental%': '2', + + # This is the location of the sandbox binary. Chrome looks for this before + # running the zygote process. If found, and SUID, it will be used to + # sandbox the zygote process and, thus, all renderer processes. + 'linux_sandbox_path%': '', + + # Set this to true to enable SELinux support. + 'selinux%': 0, + + # Strip the binary after dumping symbols. + 'linux_strip_binary%': 0, + + # Enable TCMalloc. + 'linux_use_tcmalloc%': 1, + + # Disable TCMalloc's debugallocation. + 'linux_use_debugallocation%': 0, + + # Disable TCMalloc's heapchecker. + 'linux_use_heapchecker%': 0, + + # Set to 1 to turn on seccomp sandbox by default. + # (Note: this is ignored for official builds.) + 'linux_use_seccomp_sandbox%': 0, + + # Set to select the Title Case versions of strings in GRD files. + 'use_titlecase_in_grd%': 0, + + # Used to disable Native Client at compile time, for platforms where it + # isn't supported + 'disable_nacl%': 0, + + # Set Thumb compilation flags. + 'arm_thumb%': 0, + + # Set ARM fpu compilation flags (only meaningful if arm_version==7 and + # arm_neon==0). + 'arm_fpu%': 'vfpv3', + + # Enable new NPDevice API. + 'enable_new_npdevice_api%': 0, + + 'conditions': [ + # Whether to use multiple cores to compile with visual studio. This is + # optional because it sometimes causes corruption on VS 2005. + # It is on by default on VS 2008 and off on VS 2005. + ['OS=="win"', { + 'conditions': [ + ['MSVS_VERSION=="2005"', { + 'msvs_multi_core_compile%': 0, + },{ + 'msvs_multi_core_compile%': 1, + }], + # Don't do incremental linking for large modules on 32-bit. + ['MSVS_OS_BITS==32', { + 'msvs_large_module_debug_link_mode%': '1', # No + },{ + 'msvs_large_module_debug_link_mode%': '2', # Yes + }], + ], + 'nacl_win64_defines': [ + # This flag is used to minimize dependencies when building + # Native Client loader for 64-bit Windows. + 'NACL_WIN64', + ], + }], + ], + + # NOTE: When these end up in the Mac bundle, we need to replace '-' for '_' + # so Cocoa is happy (http://crbug.com/20441). + 'locales': [ + 'am', 'ar', 'bg', 'bn', 'ca', 'cs', 'da', 'de', 'el', 'en-GB', + 'en-US', 'es-419', 'es', 'et', 'fi', 'fil', 'fr', 'gu', 'he', + 'hi', 'hr', 'hu', 'id', 'it', 'ja', 'kn', 'ko', 'lt', 'lv', + 'ml', 'mr', 'nb', 'nl', 'pl', 'pt-BR', 'pt-PT', 'ro', 'ru', + 'sk', 'sl', 'sr', 'sv', 'sw', 'ta', 'te', 'th', 'tr', 'uk', + 'vi', 'zh-CN', 'zh-TW', + ], + }, + 'target_defaults': { + 'includes': [ + 'filename_rules.gypi', + ], + 'variables': { + # See http://gcc.gnu.org/onlinedocs/gcc-4.4.2/gcc/Optimize-Options.html + 'mac_release_optimization%': '3', # Use -O3 unless overridden + 'mac_debug_optimization%': '0', # Use -O0 unless overridden + # See http://msdn.microsoft.com/en-us/library/aa652360(VS.71).aspx + 'win_release_Optimization%': '2', # 2 = /Os + 'win_debug_Optimization%': '0', # 0 = /Od + # See http://msdn.microsoft.com/en-us/library/aa652367(VS.71).aspx + 'win_release_RuntimeLibrary%': '0', # 0 = /MT (nondebug static) + 'win_debug_RuntimeLibrary%': '1', # 1 = /MTd (debug static) + + 'release_extra_cflags%': '', + 'debug_extra_cflags%': '', + 'release_valgrind_build%': 0, + }, + 'conditions': [ + ['selinux==1', { + 'defines': ['CHROMIUM_SELINUX=1'], + }], + ['win_use_allocator_shim==0', { + 'conditions': [ + ['OS=="win"', { + 'defines': ['NO_TCMALLOC'], + }], + ], + }], + ['coverage!=0', { + 'conditions': [ + ['OS=="mac"', { + 'xcode_settings': { + 'GCC_INSTRUMENT_PROGRAM_FLOW_ARCS': 'YES', # -fprofile-arcs + 'GCC_GENERATE_TEST_COVERAGE_FILES': 'YES', # -ftest-coverage + }, + # Add -lgcov for types executable, shared_library, and + # loadable_module; not for static_library. + # This is a delayed conditional. + 'target_conditions': [ + ['_type!="static_library"', { + 'xcode_settings': { 'OTHER_LDFLAGS': [ '-lgcov' ] }, + }], + ], + }], + # Linux gyp (into scons) doesn't like target_conditions? + # TODO(???): track down why 'target_conditions' doesn't work + # on Linux gyp into scons like it does on Mac gyp into xcodeproj. + ['OS=="linux"', { + 'cflags': [ '-ftest-coverage', + '-fprofile-arcs' ], + 'link_settings': { 'libraries': [ '-lgcov' ] }, + }], + # Finally, for Windows, we simply turn on profiling. + ['OS=="win"', { + 'msvs_settings': { + 'VCLinkerTool': { + 'Profile': 'true', + }, + 'VCCLCompilerTool': { + # /Z7, not /Zi, so coverage is happyb + 'DebugInformationFormat': '1', + 'AdditionalOptions': ['/Yd'], + } + } + }], # OS==win + ], # conditions for coverage + }], # coverage!=0 + ], # conditions for 'target_defaults' + 'target_conditions': [ + [ 'OS=="linux" or OS=="freebsd" or OS=="openbsd"', { + 'cflags!': [ + '-Wall', + '-Wextra', + '-Werror', + ], + }], + [ 'OS=="win"', { + 'defines': [ + '_CRT_SECURE_NO_DEPRECATE', + '_CRT_NONSTDC_NO_WARNINGS', + '_CRT_NONSTDC_NO_DEPRECATE', + # This is required for ATL to use XP-safe versions of its functions. + '_USING_V110_SDK71_', + ], + 'msvs_disabled_warnings': [4800], + 'msvs_settings': { + 'VCCLCompilerTool': { + 'WarnAsError': 'true', + 'Detect64BitPortabilityProblems': 'false', + }, + }, + }], + [ 'OS=="mac"', { + 'xcode_settings': { + 'GCC_TREAT_WARNINGS_AS_ERRORS': 'NO', + 'WARNING_CFLAGS!': ['-Wall'], + }, + }], + ], # target_conditions for 'target_defaults' + 'default_configuration': 'Debug', + 'configurations': { + # VCLinkerTool LinkIncremental values below: + # 0 == default + # 1 == /INCREMENTAL:NO + # 2 == /INCREMENTAL + # Debug links incremental, Release does not. + # + # Abstract base configurations to cover common + # attributes. + # + 'Common_Base': { + 'abstract': 1, + 'msvs_configuration_attributes': { + 'OutputDirectory': '$(SolutionDir)$(ConfigurationName)', + 'IntermediateDirectory': '$(OutDir)\\obj\\$(ProjectName)', + 'CharacterSet': '1', + }, + }, + 'x86_Base': { + 'abstract': 1, + 'msvs_settings': { + 'VCLinkerTool': { + 'MinimumRequiredVersion': '5.01', # XP. + 'TargetMachine': '1', + }, + }, + 'msvs_configuration_platform': 'Win32', + }, + 'x64_Base': { + 'abstract': 1, + 'msvs_configuration_platform': 'x64', + 'msvs_settings': { + 'VCLinkerTool': { + 'TargetMachine': '17', # x86 - 64 + 'AdditionalLibraryDirectories!': + ['<(DEPTH)/third_party/platformsdk_win7/files/Lib'], + 'AdditionalLibraryDirectories': + ['<(DEPTH)/third_party/platformsdk_win7/files/Lib/x64'], + }, + 'VCLibrarianTool': { + 'AdditionalLibraryDirectories!': + ['<(DEPTH)/third_party/platformsdk_win7/files/Lib'], + 'AdditionalLibraryDirectories': + ['<(DEPTH)/third_party/platformsdk_win7/files/Lib/x64'], + }, + }, + 'defines': [ + # Not sure if tcmalloc works on 64-bit Windows. + 'NO_TCMALLOC', + ], + }, + 'Debug_Base': { + 'abstract': 1, + 'xcode_settings': { + 'COPY_PHASE_STRIP': 'NO', + 'GCC_OPTIMIZATION_LEVEL': '<(mac_debug_optimization)', + 'OTHER_CFLAGS': [ '<@(debug_extra_cflags)', ], + }, + 'msvs_settings': { + 'VCCLCompilerTool': { + 'Optimization': '<(win_debug_Optimization)', + 'PreprocessorDefinitions': ['_DEBUG'], + 'BasicRuntimeChecks': '3', + 'RuntimeLibrary': '<(win_debug_RuntimeLibrary)', + }, + 'VCLinkerTool': { + 'LinkIncremental': '<(msvs_debug_link_incremental)', + }, + 'VCResourceCompilerTool': { + 'PreprocessorDefinitions': ['_DEBUG'], + }, + }, + 'conditions': [ + ['OS=="linux"', { + 'cflags': [ + '<@(debug_extra_cflags)', + ], + }], + ], + }, + 'Release_Base': { + 'abstract': 1, + 'defines': [ + 'NDEBUG', + ], + 'xcode_settings': { + 'DEAD_CODE_STRIPPING': 'YES', # -Wl,-dead_strip + 'GCC_OPTIMIZATION_LEVEL': '<(mac_release_optimization)', + 'OTHER_CFLAGS': [ '<@(release_extra_cflags)', ], + }, + 'msvs_settings': { + 'VCCLCompilerTool': { + 'Optimization': '<(win_release_Optimization)', + 'RuntimeLibrary': '<(win_release_RuntimeLibrary)', + }, + 'VCLinkerTool': { + 'LinkIncremental': '1', + }, + }, + 'conditions': [ + ['release_valgrind_build==0', { + 'defines': ['NVALGRIND'], + }], + ['win_use_allocator_shim==0', { + 'defines': ['NO_TCMALLOC'], + }], + ['win_release_RuntimeLibrary==2', { + # Visual C++ 2008 barfs when building anything with /MD (msvcrt): + # VC\include\typeinfo(139) : warning C4275: non dll-interface + # class 'stdext::exception' used as base for dll-interface + # class 'std::bad_cast' + 'msvs_disabled_warnings': [4275], + }], + ['OS=="linux"', { + 'cflags': [ + '<@(release_extra_cflags)', + ], + }], + ], + }, + 'Purify_Base': { + 'abstract': 1, + 'defines': [ + 'PURIFY', + 'NO_TCMALLOC', + ], + 'msvs_settings': { + 'VCCLCompilerTool': { + 'Optimization': '0', + 'RuntimeLibrary': '0', + 'BufferSecurityCheck': 'false', + }, + 'VCLinkerTool': { + 'EnableCOMDATFolding': '1', + 'LinkIncremental': '1', + }, + }, + }, + # + # Concrete configurations + # + 'Debug': { + 'inherit_from': ['Common_Base', 'x86_Base', 'Debug_Base'], + }, + 'Release': { + 'inherit_from': ['Common_Base', 'x86_Base', 'Release_Base'], + 'conditions': [ + ['msvs_use_common_release', { + 'defines': ['OFFICIAL_BUILD'], + 'msvs_settings': { + 'VCCLCompilerTool': { + 'Optimization': '3', + 'StringPooling': 'true', + 'OmitFramePointers': 'true', + 'InlineFunctionExpansion': '2', + 'EnableIntrinsicFunctions': 'true', + 'FavorSizeOrSpeed': '2', + 'OmitFramePointers': 'true', + 'EnableFiberSafeOptimizations': 'true', + 'WholeProgramOptimization': 'true', + }, + 'VCLibrarianTool': { + 'AdditionalOptions': ['/ltcg', '/expectedoutputsize:120000000'], + }, + 'VCLinkerTool': { + 'LinkIncremental': '1', + 'OptimizeReferences': '2', + 'EnableCOMDATFolding': '2', + 'OptimizeForWindows98': '1', + 'LinkTimeCodeGeneration': '1', + }, + }, + }], + ] + }, + 'conditions': [ + [ 'OS=="win"', { + # TODO(bradnelson): add a gyp mechanism to make this more graceful. + 'Purify': { + 'inherit_from': ['Common_Base', 'x86_Base', 'Release_Base', 'Purify'], + }, + 'Debug_x64': { + 'inherit_from': ['Common_Base', 'x64_Base', 'Debug_Base'], + }, + 'Release_x64': { + 'inherit_from': ['Common_Base', 'x64_Base', 'Release_Base'], + }, + 'Purify_x64': { + 'inherit_from': ['Common_Base', 'x64_Base', 'Release_Base', 'Purify_Base'], + }, + }], + ], + }, + }, + 'conditions': [ + ['OS=="linux" or OS=="freebsd" or OS=="openbsd" or OS=="solaris"', { + 'target_defaults': { + # Enable -Werror by default, but put it in a variable so it can + # be disabled in ~/.gyp/include.gypi on the valgrind builders. + 'variables': { + # Use -fno-strict-aliasing by default since gcc 4.4 has periodic + # issues that slip through the cracks. We could do this just for + # gcc 4.4 but it makes more sense to be consistent on all + # compilers in use. TODO(Craig): turn this off again when + # there is some 4.4 test infrastructure in place and existing + # aliasing issues have been fixed. + 'no_strict_aliasing%': 1, + 'conditions': [['OS=="linux"', {'werror%': '-Werror',}], + ['OS=="freebsd"', {'werror%': '',}], + ['OS=="openbsd"', {'werror%': '',}], + ], + }, + 'cflags': [ + '<(werror)', # See note above about the werror variable. + '-pthread', + '-fno-exceptions', + '-Wall', + # TODO(evan): turn this back on once all the builds work. + # '-Wextra', + # Don't warn about unused function params. We use those everywhere. + '-Wno-unused-parameter', + # Don't warn about the "struct foo f = {0};" initialization pattern. + '-Wno-missing-field-initializers', + '-D_FILE_OFFSET_BITS=64', + # Don't export any symbols (for example, to plugins we dlopen()). + # Note: this is *required* to make some plugins work. + '-fvisibility=hidden', + ], + 'cflags_cc': [ + '-frtti', + '-fno-threadsafe-statics', + # Make inline functions have hidden visiblity by default. + # Surprisingly, not covered by -fvisibility=hidden. + '-fvisibility-inlines-hidden', + ], + 'ldflags': [ + '-pthread', '-Wl,-z,noexecstack', + ], + 'scons_variable_settings': { + 'LIBPATH': ['$LIB_DIR'], + # Linking of large files uses lots of RAM, so serialize links + # using the handy flock command from util-linux. + 'FLOCK_LINK': ['flock', '$TOP_BUILDDIR/linker.lock', '$LINK'], + 'FLOCK_SHLINK': ['flock', '$TOP_BUILDDIR/linker.lock', '$SHLINK'], + 'FLOCK_LDMODULE': ['flock', '$TOP_BUILDDIR/linker.lock', '$LDMODULE'], + + # We have several cases where archives depend on each other in + # a cyclic fashion. Since the GNU linker does only a single + # pass over the archives we surround the libraries with + # --start-group and --end-group (aka -( and -) ). That causes + # ld to loop over the group until no more undefined symbols + # are found. In an ideal world we would only make groups from + # those libraries which we knew to be in cycles. However, + # that's tough with SCons, so we bodge it by making all the + # archives a group by redefining the linking command here. + # + # TODO: investigate whether we still have cycles that + # require --{start,end}-group. There has been a lot of + # refactoring since this was first coded, which might have + # eliminated the circular dependencies. + # + # Note: $_LIBDIRFLAGS comes before ${LINK,SHLINK,LDMODULE}FLAGS + # so that we prefer our own built libraries (e.g. -lpng) to + # system versions of libraries that pkg-config might turn up. + # TODO(sgk): investigate handling this not by re-ordering the + # flags this way, but by adding a hook to use the SCons + # ParseFlags() option on the output from pkg-config. + 'LINKCOM': [['$FLOCK_LINK', '-o', '$TARGET', + '$_LIBDIRFLAGS', '$LINKFLAGS', '$SOURCES', + '-Wl,--start-group', '$_LIBFLAGS', '-Wl,--end-group']], + 'SHLINKCOM': [['$FLOCK_SHLINK', '-o', '$TARGET', + '$_LIBDIRFLAGS', '$SHLINKFLAGS', '$SOURCES', + '-Wl,--start-group', '$_LIBFLAGS', '-Wl,--end-group']], + 'LDMODULECOM': [['$FLOCK_LDMODULE', '-o', '$TARGET', + '$_LIBDIRFLAGS', '$LDMODULEFLAGS', '$SOURCES', + '-Wl,--start-group', '$_LIBFLAGS', '-Wl,--end-group']], + 'IMPLICIT_COMMAND_DEPENDENCIES': 0, + }, + 'scons_import_variables': [ + 'AS', + 'CC', + 'CXX', + 'LINK', + ], + 'scons_propagate_variables': [ + 'AS', + 'CC', + 'CCACHE_DIR', + 'CXX', + 'DISTCC_DIR', + 'DISTCC_HOSTS', + 'HOME', + 'INCLUDE_SERVER_ARGS', + 'INCLUDE_SERVER_PORT', + 'LINK', + 'CHROME_BUILD_TYPE', + 'CHROMIUM_BUILD', + 'OFFICIAL_BUILD', + ], + 'configurations': { + 'Debug_Base': { + 'variables': { + 'debug_optimize%': '0', + }, + 'defines': [ + '_DEBUG', + ], + 'cflags': [ + '-O>(debug_optimize)', + '-g', + # One can use '-gstabs' to enable building the debugging + # information in STABS format for breakpad's dumpsyms. + ], + 'ldflags': [ + '-rdynamic', # Allows backtrace to resolve symbols. + ], + }, + 'Release_Base': { + 'variables': { + 'release_optimize%': '2', + }, + 'cflags': [ + '-O>(release_optimize)', + # Don't emit the GCC version ident directives, they just end up + # in the .comment section taking up binary size. + '-fno-ident', + # Put data and code in their own sections, so that unused symbols + # can be removed at link time with --gc-sections. + '-fdata-sections', + '-ffunction-sections', + ], + 'ldflags': [ + '-Wl,--gc-sections', + ], + }, + }, + 'variants': { + 'coverage': { + 'cflags': ['-fprofile-arcs', '-ftest-coverage'], + 'ldflags': ['-fprofile-arcs'], + }, + 'profile': { + 'cflags': ['-pg', '-g'], + 'ldflags': ['-pg'], + }, + 'symbols': { + 'cflags': ['-g'], + }, + }, + 'conditions': [ + [ 'target_arch=="ia32"', { + 'asflags': [ + # Needed so that libs with .s files (e.g. libicudata.a) + # are compatible with the general 32-bit-ness. + '-32', + ], + # All floating-point computations on x87 happens in 80-bit + # precision. Because the C and C++ language standards allow + # the compiler to keep the floating-point values in higher + # precision than what's specified in the source and doing so + # is more efficient than constantly rounding up to 64-bit or + # 32-bit precision as specified in the source, the compiler, + # especially in the optimized mode, tries very hard to keep + # values in x87 floating-point stack (in 80-bit precision) + # as long as possible. This has important side effects, that + # the real value used in computation may change depending on + # how the compiler did the optimization - that is, the value + # kept in 80-bit is different than the value rounded down to + # 64-bit or 32-bit. There are possible compiler options to make + # this behavior consistent (e.g. -ffloat-store would keep all + # floating-values in the memory, thus force them to be rounded + # to its original precision) but they have significant runtime + # performance penalty. + # + # -mfpmath=sse -msse2 makes the compiler use SSE instructions + # which keep floating-point values in SSE registers in its + # native precision (32-bit for single precision, and 64-bit for + # double precision values). This means the floating-point value + # used during computation does not change depending on how the + # compiler optimized the code, since the value is always kept + # in its specified precision. + 'conditions': [ + ['disable_sse2==0', { + 'cflags': [ + '-march=pentium4', + '-msse2', + '-mfpmath=sse', + ], + }], + ], + # -mmmx allows mmintrin.h to be used for mmx intrinsics. + # video playback is mmx and sse2 optimized. + 'cflags': [ + '-m32', + '-mmmx', + ], + 'ldflags': [ + '-m32', + ], + }], + ['target_arch=="arm"', { + 'target_conditions': [ + ['_toolset=="target"', { + 'cflags_cc': [ + # The codesourcery arm-2009q3 toolchain warns at that the ABI + # has changed whenever it encounters a varargs function. This + # silences those warnings, as they are not helpful and + # clutter legitimate warnings. + '-Wno-abi', + ], + 'conditions': [ + ['arm_thumb == 1', { + 'cflags': [ + '-mthumb', + # TODO(piman): -Wa,-mimplicit-it=thumb is needed for + # inline assembly that uses condition codes but it's + # suboptimal. Better would be to #ifdef __thumb__ at the + # right place and have a separate thumb path. + '-Wa,-mimplicit-it=thumb', + ] + }], + ['arm_version==7', { + 'cflags': [ + '-march=armv7-a', + '-mtune=cortex-a8', + '-mfloat-abi=softfp', + ], + 'conditions': [ + ['arm_neon==1', { + 'cflags': [ '-mfpu=neon', ], + }, { + 'cflags': [ '-mfpu=<(arm_fpu)', ], + }] + ], + }], + ], + }], + ], + }], + ['linux_fpic==1', { + 'cflags': [ + '-fPIC', + ], + }], + ['sysroot!=""', { + 'target_conditions': [ + ['_toolset=="target"', { + 'cflags': [ + '--sysroot=<(sysroot)', + ], + 'ldflags': [ + '--sysroot=<(sysroot)', + ], + }]] + }], + ['no_strict_aliasing==1', { + 'cflags': [ + '-fno-strict-aliasing', + ], + }], + ['linux_use_heapchecker==1', { + 'variables': {'linux_use_tcmalloc%': 1}, + }], + ['linux_use_tcmalloc==0', { + 'defines': ['NO_TCMALLOC'], + }], + ['linux_use_heapchecker==0', { + 'defines': ['NO_HEAPCHECKER'], + }], + ], + }, + }], + # FreeBSD-specific options; note that most FreeBSD options are set above, + # with Linux. + ['OS=="freebsd"', { + 'target_defaults': { + 'ldflags': [ + '-Wl,--no-keep-memory', + ], + }, + }], + ['OS=="solaris"', { + 'cflags!': ['-fvisibility=hidden'], + 'cflags_cc!': ['-fvisibility-inlines-hidden'], + }], + ['OS=="mac"', { + 'target_defaults': { + 'variables': { + # This should be 'mac_real_dsym%', but there seems to be a bug + # with % in variables that are intended to be set to different + # values in different targets, like this one. + 'mac_real_dsym': 0, # Fake .dSYMs are fine in most cases. + }, + 'mac_bundle': 0, + 'xcode_settings': { + 'ALWAYS_SEARCH_USER_PATHS': 'NO', + 'GCC_C_LANGUAGE_STANDARD': 'c99', # -std=c99 + 'GCC_CW_ASM_SYNTAX': 'NO', # No -fasm-blocks + 'GCC_DYNAMIC_NO_PIC': 'NO', # No -mdynamic-no-pic + # (Equivalent to -fPIC) + 'GCC_ENABLE_CPP_EXCEPTIONS': 'NO', # -fno-exceptions + 'GCC_ENABLE_CPP_RTTI': 'YES', # -frtti + 'GCC_ENABLE_PASCAL_STRINGS': 'NO', # No -mpascal-strings + # GCC_INLINES_ARE_PRIVATE_EXTERN maps to -fvisibility-inlines-hidden + 'GCC_INLINES_ARE_PRIVATE_EXTERN': 'YES', + 'GCC_OBJC_CALL_CXX_CDTORS': 'YES', # -fobjc-call-cxx-cdtors + 'GCC_SYMBOLS_PRIVATE_EXTERN': 'YES', # -fvisibility=hidden + 'GCC_THREADSAFE_STATICS': 'NO', # -fno-threadsafe-statics + 'GCC_TREAT_WARNINGS_AS_ERRORS': 'YES', # -Werror + 'GCC_VERSION': '4.2', + 'GCC_WARN_ABOUT_MISSING_NEWLINE': 'YES', # -Wnewline-eof + # MACOSX_DEPLOYMENT_TARGET maps to -mmacosx-version-min + 'MACOSX_DEPLOYMENT_TARGET': '<(mac_deployment_target)', + 'PREBINDING': 'NO', # No -Wl,-prebind + 'USE_HEADERMAP': 'NO', + 'WARNING_CFLAGS': ['-Wall', '-Wendif-labels'], + 'conditions': [ + ['chromium_mac_pch', {'GCC_PRECOMPILE_PREFIX_HEADER': 'YES'}, + {'GCC_PRECOMPILE_PREFIX_HEADER': 'NO'} + ], + ], + }, + 'target_conditions': [ + ['_type!="static_library"', { + 'xcode_settings': {'OTHER_LDFLAGS': ['-Wl,-search_paths_first']}, + }], + ['_mac_bundle', { + 'xcode_settings': {'OTHER_LDFLAGS': ['-Wl,-ObjC']}, + }], + ], # target_conditions + }, # target_defaults + }], # OS=="mac" + ['OS=="win"', { + 'target_defaults': { + 'defines': [ + '_WIN32_WINNT=0x0600', + 'WINVER=0x0600', + 'WIN32', + '_WINDOWS', + '_HAS_EXCEPTIONS=0', + 'NOMINMAX', + '_CRT_RAND_S', + 'CERT_CHAIN_PARA_HAS_EXTRA_FIELDS', + 'WIN32_LEAN_AND_MEAN', + '_SECURE_ATL', + '_HAS_TR1=0', + ], + 'msvs_system_include_dirs': [ + '<(DEPTH)/third_party/platformsdk_win7/files/Include', + '$(VSInstallDir)/VC/atlmfc/include', + ], + 'msvs_cygwin_dirs': ['<(DEPTH)/third_party/cygwin'], + 'msvs_disabled_warnings': [ + 4091, 4100, 4127, 4366, 4396, 4503, 4512, 4819, 4995, 4702 + ], + 'msvs_settings': { + 'VCCLCompilerTool': { + 'MinimalRebuild': 'false', + 'ExceptionHandling': '0', + 'BufferSecurityCheck': 'true', + 'EnableFunctionLevelLinking': 'true', + 'RuntimeTypeInfo': 'false', + 'WarningLevel': '4', + 'WarnAsError': 'true', + 'DebugInformationFormat': '3', + 'conditions': [ + [ 'msvs_multi_core_compile', { + 'AdditionalOptions': ['/MP'], + }], + ], + }, + 'VCLibrarianTool': { + 'AdditionalOptions': ['/ignore:4221'], + 'AdditionalLibraryDirectories': + ['<(DEPTH)/third_party/platformsdk_win7/files/Lib'], + }, + 'VCLinkerTool': { + 'AdditionalDependencies': [ + 'wininet.lib', + 'version.lib', + 'msimg32.lib', + 'ws2_32.lib', + 'usp10.lib', + 'psapi.lib', + 'dbghelp.lib', + ], + 'AdditionalLibraryDirectories': + ['<(DEPTH)/third_party/platformsdk_win7/files/Lib'], + 'GenerateDebugInformation': 'true', + 'MapFileName': '$(OutDir)\\$(TargetName).map', + 'ImportLibrary': '$(OutDir)\\lib\\$(TargetName).lib', + 'FixedBaseAddress': '1', + # SubSystem values: + # 0 == not set + # 1 == /SUBSYSTEM:CONSOLE + # 2 == /SUBSYSTEM:WINDOWS + # Most of the executables we'll ever create are tests + # and utilities with console output. + 'SubSystem': '1', + }, + 'VCMIDLTool': { + 'GenerateStublessProxies': 'true', + 'TypeLibraryName': '$(InputName).tlb', + 'OutputDirectory': '$(IntDir)', + 'HeaderFileName': '$(InputName).h', + 'DLLDataFileName': 'dlldata.c', + 'InterfaceIdentifierFileName': '$(InputName)_i.c', + 'ProxyFileName': '$(InputName)_p.c', + }, + 'VCResourceCompilerTool': { + 'Culture' : '1033', + 'AdditionalIncludeDirectories': ['<(DEPTH)'], + }, + }, + }, + }], + ['disable_nacl==1 or OS=="freebsd" or OS=="openbsd" or OS=="solaris"', { + 'target_defaults': { + 'defines': [ + 'DISABLE_NACL', + ], + }, + }], + ['OS=="win" and msvs_use_common_linker_extras', { + 'target_defaults': { + 'msvs_settings': { + 'VCLinkerTool': { + 'DelayLoadDLLs': [ + 'dbghelp.dll', + 'dwmapi.dll', + 'uxtheme.dll', + ], + }, + }, + 'configurations': { + 'x86_Base': { + 'msvs_settings': { + 'VCLinkerTool': { + 'AdditionalOptions': [ + '/safeseh', + '/dynamicbase', + '/ignore:4199', + '/ignore:4221', + '/nxcompat', + ], + }, + }, + }, + 'x64_Base': { + 'msvs_settings': { + 'VCLinkerTool': { + 'AdditionalOptions': [ + # safeseh is not compatible with x64 + '/dynamicbase', + '/ignore:4199', + '/ignore:4221', + '/nxcompat', + ], + }, + }, + }, + }, + }, + }], + ['enable_new_npdevice_api==1', { + 'target_defaults': { + 'defines': [ + 'ENABLE_NEW_NPDEVICE_API', + ], + }, + }], + ], + 'scons_settings': { + 'sconsbuild_dir': '<(DEPTH)/sconsbuild', + 'tools': ['ar', 'as', 'gcc', 'g++', 'gnulink', 'chromium_builders'], + }, + 'xcode_settings': { + # DON'T ADD ANYTHING NEW TO THIS BLOCK UNLESS YOU REALLY REALLY NEED IT! + # This block adds *project-wide* configuration settings to each project + # file. It's almost always wrong to put things here. Specify your + # custom xcode_settings in target_defaults to add them to targets instead. + + # In an Xcode Project Info window, the "Base SDK for All Configurations" + # setting sets the SDK on a project-wide basis. In order to get the + # configured SDK to show properly in the Xcode UI, SDKROOT must be set + # here at the project level. + 'SDKROOT': 'macosx<(mac_sdk)', # -isysroot + + # The Xcode generator will look for an xcode_settings section at the root + # of each dict and use it to apply settings on a file-wide basis. Most + # settings should not be here, they should be in target-specific + # xcode_settings sections, or better yet, should use non-Xcode-specific + # settings in target dicts. SYMROOT is a special case, because many other + # Xcode variables depend on it, including variables such as + # PROJECT_DERIVED_FILE_DIR. When a source group corresponding to something + # like PROJECT_DERIVED_FILE_DIR is added to a project, in order for the + # files to appear (when present) in the UI as actual files and not red + # red "missing file" proxies, the correct path to PROJECT_DERIVED_FILE_DIR, + # and therefore SYMROOT, needs to be set at the project level. + 'SYMROOT': '<(DEPTH)/xcodebuild', + }, +} diff --git a/shared/sentry/external/breakpad/src/build/filename_rules.gypi b/shared/sentry/external/breakpad/src/build/filename_rules.gypi new file mode 100644 index 000000000..78cd1808a --- /dev/null +++ b/shared/sentry/external/breakpad/src/build/filename_rules.gypi @@ -0,0 +1,57 @@ +# 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_conditions': [ + ['OS!="win"', { + 'sources/': [ + ['exclude', '(^|/)windows/'], + ], + }], + ['OS!="linux"', { + 'sources/': [ + ['exclude', '(^|/)linux/'], + ], + }], + ['OS!="mac"', { + 'sources/': [ + ['exclude', '(^|/)mac/'], + ], + }], + ['OS!="android"', { + 'sources/': [ + ['exclude', '(^|/)android/'], + ], + }], + ['OS!="solaris"', { + 'sources/': [ + ['exclude', '(^|/)solaris/'], + ], + }], + ], +} diff --git a/shared/sentry/external/breakpad/src/build/gyp_breakpad b/shared/sentry/external/breakpad/src/build/gyp_breakpad new file mode 100755 index 000000000..0b8077d2f --- /dev/null +++ b/shared/sentry/external/breakpad/src/build/gyp_breakpad @@ -0,0 +1,67 @@ +#!/usr/bin/env python + +# 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. + +import os +import platform +import sys + +script_dir = os.path.dirname(os.path.realpath(__file__)) +breakpad_root = os.path.abspath(os.path.join(script_dir, os.pardir)) + +sys.path.insert(0, os.path.join(breakpad_root, 'tools', 'gyp', 'pylib')) +import gyp + +def run_gyp(args): + rc = gyp.main(args) + if rc != 0: + print 'Error running GYP' + sys.exit(rc) + + +def main(): + args = sys.argv[1:] + args.append(os.path.join(script_dir, 'all.gyp')) + + args.append('-I') + args.append(os.path.join(breakpad_root, 'build', 'common.gypi')) + + args.extend(['-D', 'gyp_output_dir=out']) + + # Set the GYP DEPTH variable to the root of the project. + args.append('--depth=' + os.path.relpath(breakpad_root)) + + print 'Updating projects from gyp files...' + sys.stdout.flush() + + run_gyp(args) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/shared/sentry/external/breakpad/src/build/testing.gyp b/shared/sentry/external/breakpad/src/build/testing.gyp new file mode 100644 index 000000000..6a459a646 --- /dev/null +++ b/shared/sentry/external/breakpad/src/build/testing.gyp @@ -0,0 +1,90 @@ +# 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. + +{ + 'targets': [ + { + 'target_name': 'gtest', + 'type': 'static_library', + 'sources': [ + '../testing/googletest/src/gtest-all.cc', + ], + 'include_dirs': [ + '../testing/googletest', + '../testing/googletest/include', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '../testing/googletest/include', + ], + }, + }, + { + 'target_name': 'gtest_main', + 'type': 'static_library', + 'dependencies': [ + 'gtest', + ], + 'sources': [ + '../testing/googletest/src/gtest_main.cc', + ], + }, + { + 'target_name': 'gmock', + 'type': 'static_library', + 'dependencies': [ + 'gtest', + ], + 'sources': [ + '../testing/googlemock/src/gmock-all.cc', + ], + 'include_dirs': [ + '../testing/googlemock', + '../testing/googlemock/include', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '../testing/googlemock/include', + ], + }, + 'export_dependent_settings': [ + 'gtest', + ], + }, + { + 'target_name': 'gmock_main', + 'type': 'static_library', + 'dependencies': [ + 'gmock', + ], + 'sources': [ + '../testing/googlemock/src/gmock_main.cc', + ], + }, + ], +} diff --git a/shared/sentry/external/breakpad/src/client/apple/Framework/BreakpadDefines.h b/shared/sentry/external/breakpad/src/client/apple/Framework/BreakpadDefines.h new file mode 100644 index 000000000..410a5a6f3 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/apple/Framework/BreakpadDefines.h @@ -0,0 +1,73 @@ +// 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. + +// Keys for configuration file +#define kReporterMinidumpDirectoryKey "MinidumpDir" +#define kReporterMinidumpIDKey "MinidumpID" + +// Filename for recording uploaded IDs +#define kReporterLogFilename "uploads.log" + +// The default subdirectory of the Library to put crash dumps in +// The subdirectory is +// ~/Library// +#define kDefaultLibrarySubdirectory "Breakpad" + +// Specify some special keys to be used in the configuration file that is +// generated by Breakpad and consumed by the crash_sender. +#define BREAKPAD_PRODUCT "BreakpadProduct" +#define BREAKPAD_PRODUCT_DISPLAY "BreakpadProductDisplay" +#define BREAKPAD_VERSION "BreakpadVersion" +#define BREAKPAD_VENDOR "BreakpadVendor" +#define BREAKPAD_URL "BreakpadURL" +#define BREAKPAD_REPORT_INTERVAL "BreakpadReportInterval" +#define BREAKPAD_SKIP_CONFIRM "BreakpadSkipConfirm" +#define BREAKPAD_CONFIRM_TIMEOUT "BreakpadConfirmTimeout" +#define BREAKPAD_SEND_AND_EXIT "BreakpadSendAndExit" +#define BREAKPAD_DUMP_DIRECTORY "BreakpadMinidumpLocation" +#define BREAKPAD_INSPECTOR_LOCATION "BreakpadInspectorLocation" +#define BREAKPAD_REPORTER_EXE_LOCATION \ + "BreakpadReporterExeLocation" +#define BREAKPAD_LOGFILES "BreakpadLogFiles" +#define BREAKPAD_LOGFILE_UPLOAD_SIZE "BreakpadLogFileTailSize" +#define BREAKPAD_REQUEST_COMMENTS "BreakpadRequestComments" +#define BREAKPAD_COMMENTS "BreakpadComments" +#define BREAKPAD_REQUEST_EMAIL "BreakpadRequestEmail" +#define BREAKPAD_EMAIL "BreakpadEmail" +#define BREAKPAD_SERVER_TYPE "BreakpadServerType" +#define BREAKPAD_SERVER_PARAMETER_DICT "BreakpadServerParameters" +#define BREAKPAD_IN_PROCESS "BreakpadInProcess" + +// The keys below are NOT user supplied, and are used internally. +#define BREAKPAD_PROCESS_START_TIME "BreakpadProcStartTime" +#define BREAKPAD_PROCESS_UP_TIME "BreakpadProcessUpTime" +#define BREAKPAD_PROCESS_CRASH_TIME "BreakpadProcessCrashTime" +#define BREAKPAD_LOGFILE_KEY_PREFIX "BreakpadAppLogFile" +#define BREAKPAD_SERVER_PARAMETER_PREFIX "BreakpadServerParameterPrefix_" +#define BREAKPAD_ON_DEMAND "BreakpadOnDemand" diff --git a/shared/sentry/external/breakpad/src/client/ios/Breakpad.h b/shared/sentry/external/breakpad/src/client/ios/Breakpad.h new file mode 100644 index 000000000..6c9b8bd64 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/ios/Breakpad.h @@ -0,0 +1,260 @@ +// 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. + +// Framework to provide a simple C API to crash reporting for +// applications. By default, if any machine-level exception (e.g., +// EXC_BAD_ACCESS) occurs, it will be handled by the BreakpadRef +// object as follows: +// +// 1. Create a minidump file (see Breakpad for details) +// 2. Create a config file. +// +// These files can then be uploaded to a server. + +typedef void* BreakpadRef; + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#include + +// The keys in the dictionary returned by |BreakpadGenerateReport|. +#define BREAKPAD_OUTPUT_DUMP_FILE "BreakpadDumpFile" +#define BREAKPAD_OUTPUT_CONFIG_FILE "BreakpadConfigFile" + +// Optional user-defined function to decide if we should handle this crash or +// forward it along. +// Return true if you want Breakpad to handle it. +// Return false if you want Breakpad to skip it +// The exception handler always returns false, as if SEND_AND_EXIT were false +// (which means the next exception handler will take the exception) +typedef bool (*BreakpadFilterCallback)(int exception_type, + int exception_code, + mach_port_t crashing_thread, + void* context); + +// Optional user-defined function that will be called after a network upload +// of a crash report. +// |report_id| will be the id returned by the server, or "ERR" if an error +// occurred. +// |error| will contain the error, or nil if no error occured. +typedef void (*BreakpadUploadCompletionCallback)(NSString* report_id, + NSError* error); + +// Create a new BreakpadRef object and install it as an exception +// handler. The |parameters| will typically be the contents of your +// bundle's Info.plist. +// +// You can also specify these additional keys for customizable behavior: +// Key: Value: +// BREAKPAD_PRODUCT Product name (e.g., "MyAwesomeProduct") +// This one is used as the key to identify +// the product when uploading. Falls back to +// CFBundleName if not specified. +// REQUIRED +// +// BREAKPAD_PRODUCT_DISPLAY This is the display name, e.g. a pretty +// name for the product when the crash_sender +// pops up UI for the user. Falls back first to +// CFBundleDisplayName and then to +// BREAKPAD_PRODUCT if not specified. +// +// BREAKPAD_VERSION Product version (e.g., 1.2.3), used +// as metadata for crash report. Falls back to +// CFBundleVersion if not specified. +// REQUIRED +// +// BREAKPAD_VENDOR Vendor name, used in UI (e.g. "A report has +// been created that you can send to ") +// +// BREAKPAD_URL URL destination for reporting +// REQUIRED +// +// BREAKPAD_DUMP_DIRECTORY The directory to store crash-dumps +// in. By default, we use +// ~/Library/Cache/Breakpad/ +// The path you specify here is tilde-expanded. +// +// BREAKPAD_SERVER_TYPE A parameter that tells Breakpad how to +// rewrite the upload parameters for a specific +// server type. The currently valid values are +// 'socorro' or 'google'. If you want to add +// other types, see the function in +// crash_report_sender.m that maps parameters to +// URL parameters. Defaults to 'google'. +// +// BREAKPAD_SERVER_PARAMETER_DICT A plist dictionary of static +// parameters that are uploaded to the +// server. The parameters are sent as +// is to the crash server. Their +// content isn't added to the minidump +// but pass as URL parameters when +// uploading theminidump to the crash +// server. +//============================================================================= +// The BREAKPAD_PRODUCT, BREAKPAD_VERSION and BREAKPAD_URL are +// required to have non-NULL values. By default, the BREAKPAD_PRODUCT +// will be the CFBundleName and the BREAKPAD_VERSION will be the +// CFBundleVersion when these keys are present in the bundle's +// Info.plist, which is usually passed in to BreakpadCreate() as an +// NSDictionary (you could also pass in another dictionary that had +// the same keys configured). If the BREAKPAD_PRODUCT or +// BREAKPAD_VERSION are ultimately undefined, BreakpadCreate() will +// fail. You have been warned. +// +// If you are running in a debugger, Breakpad will not install, unless the +// BREAKPAD_IGNORE_DEBUGGER envionment variable is set and/or non-zero. +// +//============================================================================= +// The following are NOT user-supplied but are documented here for +// completeness. They are calculated by Breakpad during initialization & +// crash-dump generation, or entered in by the user. +// +// BREAKPAD_PROCESS_START_TIME The time, in seconds since the Epoch, the +// process started +// +// BREAKPAD_PROCESS_CRASH_TIME The time, in seconds since the Epoch, the +// process crashed. +// +// BREAKPAD_PROCESS_UP_TIME The total time in milliseconds the process +// has been running. This parameter is not +// set until the crash-dump-generation phase. +// +// BREAKPAD_SERVER_PARAMETER_PREFIX This prefix is used by Breakpad +// internally, because Breakpad uses +// the same dictionary internally to +// track both its internal +// configuration parameters and +// parameters meant to be uploaded +// to the server. This string is +// used internally by Breakpad to +// prefix user-supplied parameter +// names so those can be sent to the +// server without leaking Breakpad's +// internal values. + +// Returns a new BreakpadRef object on success, NULL otherwise. +BreakpadRef BreakpadCreate(NSDictionary* parameters); + +// Uninstall and release the data associated with |ref|. +void BreakpadRelease(BreakpadRef ref); + +// User defined key and value string storage. Generally this is used +// to configure Breakpad's internal operation, such as whether the +// crash_sender should prompt the user, or the filesystem location for +// the minidump file. See Breakpad.h for some parameters that can be +// set. Anything longer than 255 bytes will be truncated. Note that +// the string is converted to UTF8 before truncation, so any multibyte +// character that straddles the 255(256 - 1 for terminator) byte limit +// will be mangled. +// +// A maximum number of 64 key/value pairs are supported. An assert() +// will fire if more than this number are set. Unfortunately, right +// now, the same dictionary is used for both Breakpad's parameters AND +// the Upload parameters. +// +// TODO (nealsid): Investigate how necessary this is if we don't +// automatically upload parameters to the server anymore. +// TODO (nealsid): separate server parameter dictionary from the +// dictionary used to configure Breakpad, and document limits for each +// independently. +void BreakpadSetKeyValue(BreakpadRef ref, NSString* key, NSString* value); +NSString* BreakpadKeyValue(BreakpadRef ref, NSString* key); +void BreakpadRemoveKeyValue(BreakpadRef ref, NSString* key); + +// You can use this method to specify parameters that will be uploaded +// to the crash server. They will be automatically encoded as +// necessary. Note that as mentioned above there are limits on both +// the number of keys and their length. +void BreakpadAddUploadParameter(BreakpadRef ref, NSString* key, + NSString* value); + +// This method will remove a previously-added parameter from the +// upload parameter set. +void BreakpadRemoveUploadParameter(BreakpadRef ref, NSString* key); + +// Method to handle uploading data to the server + +// Returns the number of crash reports waiting to send to the server. +int BreakpadGetCrashReportCount(BreakpadRef ref); + +// Returns the next upload configuration. The report file is deleted. +NSDictionary* BreakpadGetNextReportConfiguration(BreakpadRef ref); + +// Returns the date of the most recent crash report. +NSDate* BreakpadGetDateOfMostRecentCrashReport(BreakpadRef ref); + +// Upload next report to the server. +void BreakpadUploadNextReport(BreakpadRef ref); + +// Upload next report to the server. +// |server_parameters| is additional server parameters to send. +void BreakpadUploadNextReportWithParameters( + BreakpadRef ref, + NSDictionary* server_parameters, + BreakpadUploadCompletionCallback callback); + +// Upload a report to the server. +// |server_parameters| is additional server parameters to send. +// |configuration| is the configuration of the breakpad report to send. +void BreakpadUploadReportWithParametersAndConfiguration( + BreakpadRef ref, + NSDictionary* server_parameters, + NSDictionary* configuration, + BreakpadUploadCompletionCallback callback); + +// Handles the network response of a breakpad upload. This function is needed if +// the actual upload is done by the Breakpad client. +// |configuration| is the configuration of the upload. It must contain the same +// fields as the configuration passed to +// BreakpadUploadReportWithParametersAndConfiguration. +// |data| and |error| contain the network response. +void BreakpadHandleNetworkResponse(BreakpadRef ref, + NSDictionary* configuration, + NSData* data, + NSError* error); + +// Upload a file to the server. |data| is the content of the file to sent. +// |server_parameters| is additional server parameters to send. +void BreakpadUploadData(BreakpadRef ref, NSData* data, NSString* name, + NSDictionary* server_parameters); + +// Generate a breakpad minidump and configuration file in the dump directory. +// The report will be available for uploading. The paths of the created files +// are returned in the dictionary. |server_parameters| is additional server +// parameters to add in the config file. +NSDictionary* BreakpadGenerateReport(BreakpadRef ref, + NSDictionary* server_parameters); + +#ifdef __cplusplus +} +#endif diff --git a/shared/sentry/external/breakpad/src/client/ios/Breakpad.mm b/shared/sentry/external/breakpad/src/client/ios/Breakpad.mm new file mode 100644 index 000000000..11c517466 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/ios/Breakpad.mm @@ -0,0 +1,987 @@ +// 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. + +#define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER" + +#import "client/ios/Breakpad.h" + +#include +#import +#include +#include +#include +#include + +#include + +#import "client/ios/handler/ios_exception_minidump_generator.h" +#import "client/mac/crash_generation/ConfigFile.h" +#import "client/mac/handler/minidump_generator.h" +#import "client/mac/handler/protected_memory_allocator.h" +#import "client/mac/sender/uploader.h" +#import "common/long_string_dictionary.h" + +#if !TARGET_OS_TV && !TARGET_OS_WATCH +#import "client/mac/handler/exception_handler.h" +#else +#import "client/ios/exception_handler_no_mach.h" +#endif // !TARGET_OS_TV && !TARGET_OS_WATCH + +#if !defined(__EXCEPTIONS) || (__clang__ && !__has_feature(cxx_exceptions)) +// This file uses C++ try/catch (but shouldn't). Duplicate the macros from +// allowing this file to work properly with +// exceptions disabled even when other C++ libraries are used. #undef the try +// and catch macros first in case libstdc++ is in use and has already provided +// its own definitions. +#undef try +#define try if (true) +#undef catch +#define catch(X) if (false) +#endif // __EXCEPTIONS + +using google_breakpad::ConfigFile; +using google_breakpad::EnsureDirectoryPathExists; +using google_breakpad::LongStringDictionary; + +//============================================================================= +// We want any memory allocations which are used by breakpad during the +// exception handling process (after a crash has happened) to be read-only +// to prevent them from being smashed before a crash occurs. Unfortunately +// we cannot protect against smashes to our exception handling thread's +// stack. +// +// NOTE: Any memory allocations which are not used during the exception +// handling process may be allocated in the normal ways. +// +// The ProtectedMemoryAllocator class provides an Allocate() method which +// we'll using in conjunction with placement operator new() to control +// allocation of C++ objects. Note that we don't use operator delete() +// but instead call the objects destructor directly: object->~ClassName(); +// +ProtectedMemoryAllocator* gMasterAllocator = NULL; +ProtectedMemoryAllocator* gKeyValueAllocator = NULL; +ProtectedMemoryAllocator* gBreakpadAllocator = NULL; + +// Mutex for thread-safe access to the key/value dictionary used by breakpad. +// It's a global instead of an instance variable of Breakpad +// since it can't live in a protected memory area. +pthread_mutex_t gDictionaryMutex; + +//============================================================================= +// Stack-based object for thread-safe access to a memory-protected region. +// It's assumed that normally the memory block (allocated by the allocator) +// is protected (read-only). Creating a stack-based instance of +// ProtectedMemoryLocker will unprotect this block after taking the lock. +// Its destructor will first re-protect the memory then release the lock. +class ProtectedMemoryLocker { + public: + ProtectedMemoryLocker(pthread_mutex_t* mutex, + ProtectedMemoryAllocator* allocator) + : mutex_(mutex), + allocator_(allocator) { + // Lock the mutex + __attribute__((unused)) int rv = pthread_mutex_lock(mutex_); + assert(rv == 0); + + // Unprotect the memory + allocator_->Unprotect(); + } + + ~ProtectedMemoryLocker() { + // First protect the memory + allocator_->Protect(); + + // Then unlock the mutex + __attribute__((unused)) int rv = pthread_mutex_unlock(mutex_); + assert(rv == 0); + } + + private: + ProtectedMemoryLocker(); + ProtectedMemoryLocker(const ProtectedMemoryLocker&); + ProtectedMemoryLocker& operator=(const ProtectedMemoryLocker&); + + pthread_mutex_t* mutex_; + ProtectedMemoryAllocator* allocator_; +}; + +//============================================================================= +class Breakpad { + public: + // factory method + static Breakpad* Create(NSDictionary* parameters) { + // Allocate from our special allocation pool + Breakpad* breakpad = + new (gBreakpadAllocator->Allocate(sizeof(Breakpad))) + Breakpad(); + + if (!breakpad) + return NULL; + + if (!breakpad->Initialize(parameters)) { + // Don't use operator delete() here since we allocated from special pool + breakpad->~Breakpad(); + return NULL; + } + + return breakpad; + } + + ~Breakpad(); + + void SetKeyValue(NSString* key, NSString* value); + NSString* KeyValue(NSString* key); + void RemoveKeyValue(NSString* key); + NSArray* CrashReportsToUpload(); + NSString* NextCrashReportToUpload(); + NSDictionary* NextCrashReportConfiguration(); + NSDictionary* FixedUpCrashReportConfiguration(NSDictionary* configuration); + NSDate* DateOfMostRecentCrashReport(); + void UploadNextReport(NSDictionary* server_parameters); + void UploadReportWithConfiguration(NSDictionary* configuration, + NSDictionary* server_parameters, + BreakpadUploadCompletionCallback callback); + void UploadData(NSData* data, NSString* name, + NSDictionary* server_parameters); + void HandleNetworkResponse(NSDictionary* configuration, + NSData* data, + NSError* error); + NSDictionary* GenerateReport(NSDictionary* server_parameters); + + private: + Breakpad() + : handler_(NULL), + config_params_(NULL) {} + + bool Initialize(NSDictionary* parameters); + + bool ExtractParameters(NSDictionary* parameters); + + // Dispatches to HandleMinidump() + static bool HandleMinidumpCallback(const char* dump_dir, + const char* minidump_id, + void* context, bool succeeded); + + bool HandleMinidump(const char* dump_dir, + const char* minidump_id); + + // NSException handler + static void UncaughtExceptionHandler(NSException* exception); + + // Handle an uncaught NSException. + void HandleUncaughtException(NSException* exception); + + // Since ExceptionHandler (w/o namespace) is defined as typedef in OSX's + // MachineExceptions.h, we have to explicitly name the handler. + google_breakpad::ExceptionHandler* handler_; // The actual handler (STRONG) + + LongStringDictionary* config_params_; // Create parameters (STRONG) + + ConfigFile config_file_; + + // A static reference to the current Breakpad instance. Used for handling + // NSException. + static Breakpad* current_breakpad_; +}; + +Breakpad* Breakpad::current_breakpad_ = NULL; + +#pragma mark - +#pragma mark Helper functions + +//============================================================================= +// Helper functions + +//============================================================================= +static BOOL IsDebuggerActive() { + BOOL result = NO; + NSUserDefaults* stdDefaults = [NSUserDefaults standardUserDefaults]; + + // We check both defaults and the environment variable here + + BOOL ignoreDebugger = [stdDefaults boolForKey:@IGNORE_DEBUGGER]; + + if (!ignoreDebugger) { + char* ignoreDebuggerStr = getenv(IGNORE_DEBUGGER); + ignoreDebugger = + (ignoreDebuggerStr ? strtol(ignoreDebuggerStr, NULL, 10) : 0) != 0; + } + + if (!ignoreDebugger) { + pid_t pid = getpid(); + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; + int mibSize = sizeof(mib) / sizeof(int); + size_t actualSize; + + if (sysctl(mib, mibSize, NULL, &actualSize, NULL, 0) == 0) { + struct kinfo_proc* info = (struct kinfo_proc*)malloc(actualSize); + + if (info) { + // This comes from looking at the Darwin xnu Kernel + if (sysctl(mib, mibSize, info, &actualSize, NULL, 0) == 0) + result = (info->kp_proc.p_flag & P_TRACED) ? YES : NO; + + free(info); + } + } + } + + return result; +} + +//============================================================================= +bool Breakpad::HandleMinidumpCallback(const char* dump_dir, + const char* minidump_id, + void* context, bool succeeded) { + Breakpad* breakpad = (Breakpad*)context; + + // If our context is damaged or something, just return false to indicate that + // the handler should continue without us. + if (!breakpad || !succeeded) + return false; + + return breakpad->HandleMinidump(dump_dir, minidump_id); +} + +//============================================================================= +void Breakpad::UncaughtExceptionHandler(NSException* exception) { + NSSetUncaughtExceptionHandler(NULL); + if (current_breakpad_) { + current_breakpad_->HandleUncaughtException(exception); + BreakpadRelease(current_breakpad_); + } +} + +//============================================================================= +#pragma mark - + +//============================================================================= +bool Breakpad::Initialize(NSDictionary* parameters) { + // Initialize + current_breakpad_ = this; + config_params_ = NULL; + handler_ = NULL; + + // Gather any user specified parameters + if (!ExtractParameters(parameters)) { + return false; + } + + // Check for debugger + if (IsDebuggerActive()) { + return true; + } + + // Create the handler (allocating it in our special protected pool) + handler_ = + new (gBreakpadAllocator->Allocate( + sizeof(google_breakpad::ExceptionHandler))) + google_breakpad::ExceptionHandler( + config_params_->GetValueForKey(BREAKPAD_DUMP_DIRECTORY), + 0, &HandleMinidumpCallback, this, true, 0); + NSSetUncaughtExceptionHandler(&Breakpad::UncaughtExceptionHandler); + return true; +} + +//============================================================================= +Breakpad::~Breakpad() { + NSSetUncaughtExceptionHandler(NULL); + current_breakpad_ = NULL; + // Note that we don't use operator delete() on these pointers, + // since they were allocated by ProtectedMemoryAllocator objects. + // + if (config_params_) { + config_params_->~LongStringDictionary(); + } + + if (handler_) + handler_->~ExceptionHandler(); +} + +//============================================================================= +bool Breakpad::ExtractParameters(NSDictionary* parameters) { + NSString* serverType = [parameters objectForKey:@BREAKPAD_SERVER_TYPE]; + NSString* display = [parameters objectForKey:@BREAKPAD_PRODUCT_DISPLAY]; + NSString* product = [parameters objectForKey:@BREAKPAD_PRODUCT]; + NSString* version = [parameters objectForKey:@BREAKPAD_VERSION]; + NSString* urlStr = [parameters objectForKey:@BREAKPAD_URL]; + NSString* vendor = + [parameters objectForKey:@BREAKPAD_VENDOR]; + // We check both parameters and the environment variable here. + char* envVarDumpSubdirectory = getenv(BREAKPAD_DUMP_DIRECTORY); + NSString* dumpSubdirectory = envVarDumpSubdirectory ? + [NSString stringWithUTF8String:envVarDumpSubdirectory] : + [parameters objectForKey:@BREAKPAD_DUMP_DIRECTORY]; + + NSDictionary* serverParameters = + [parameters objectForKey:@BREAKPAD_SERVER_PARAMETER_DICT]; + + if (!product) + product = [parameters objectForKey:@"CFBundleName"]; + + if (!display) { + display = [parameters objectForKey:@"CFBundleDisplayName"]; + if (!display) { + display = product; + } + } + + if (!version.length) // Default nil or empty string to CFBundleVersion + version = [parameters objectForKey:@"CFBundleVersion"]; + + if (!vendor) { + vendor = @"Vendor not specified"; + } + + if (!dumpSubdirectory) { + NSString* cachePath = + [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, + NSUserDomainMask, + YES) + objectAtIndex:0]; + dumpSubdirectory = + [cachePath stringByAppendingPathComponent:@kDefaultLibrarySubdirectory]; + + EnsureDirectoryPathExists(dumpSubdirectory); + } + + // The product, version, and URL are required values. + if (![product length]) { + return false; + } + + if (![version length]) { + return false; + } + + if (![urlStr length]) { + return false; + } + + config_params_ = + new (gKeyValueAllocator->Allocate(sizeof(LongStringDictionary))) + LongStringDictionary(); + + LongStringDictionary& dictionary = *config_params_; + + dictionary.SetKeyValue(BREAKPAD_SERVER_TYPE, [serverType UTF8String]); + dictionary.SetKeyValue(BREAKPAD_PRODUCT_DISPLAY, [display UTF8String]); + dictionary.SetKeyValue(BREAKPAD_PRODUCT, [product UTF8String]); + dictionary.SetKeyValue(BREAKPAD_VERSION, [version UTF8String]); + dictionary.SetKeyValue(BREAKPAD_URL, [urlStr UTF8String]); + dictionary.SetKeyValue(BREAKPAD_VENDOR, [vendor UTF8String]); + dictionary.SetKeyValue(BREAKPAD_DUMP_DIRECTORY, + [dumpSubdirectory UTF8String]); + + struct timeval tv; + gettimeofday(&tv, NULL); + char timeStartedString[32]; + sprintf(timeStartedString, "%zd", tv.tv_sec); + dictionary.SetKeyValue(BREAKPAD_PROCESS_START_TIME, timeStartedString); + + if (serverParameters) { + // For each key-value pair, call BreakpadAddUploadParameter() + NSEnumerator* keyEnumerator = [serverParameters keyEnumerator]; + NSString* aParameter; + while ((aParameter = [keyEnumerator nextObject])) { + BreakpadAddUploadParameter(this, aParameter, + [serverParameters objectForKey:aParameter]); + } + } + return true; +} + +//============================================================================= +void Breakpad::SetKeyValue(NSString* key, NSString* value) { + // We allow nil values. This is the same as removing the keyvalue. + if (!config_params_ || !key) + return; + + config_params_->SetKeyValue([key UTF8String], [value UTF8String]); +} + +//============================================================================= +NSString* Breakpad::KeyValue(NSString* key) { + if (!config_params_ || !key) + return nil; + + const std::string value = config_params_->GetValueForKey([key UTF8String]); + return value.empty() ? nil : [NSString stringWithUTF8String:value.c_str()]; +} + +//============================================================================= +void Breakpad::RemoveKeyValue(NSString* key) { + if (!config_params_ || !key) return; + + config_params_->RemoveKey([key UTF8String]); +} + +//============================================================================= +NSArray* Breakpad::CrashReportsToUpload() { + NSString* directory = KeyValue(@BREAKPAD_DUMP_DIRECTORY); + if (!directory) + return nil; + NSArray* dirContents = [[NSFileManager defaultManager] + contentsOfDirectoryAtPath:directory error:nil]; + NSArray* configs = [dirContents filteredArrayUsingPredicate:[NSPredicate + predicateWithFormat:@"self BEGINSWITH 'Config-'"]]; + return configs; +} + +//============================================================================= +NSString* Breakpad::NextCrashReportToUpload() { + NSString* directory = KeyValue(@BREAKPAD_DUMP_DIRECTORY); + if (!directory) + return nil; + NSString* config = [CrashReportsToUpload() lastObject]; + if (!config) + return nil; + return [NSString stringWithFormat:@"%@/%@", directory, config]; +} + +//============================================================================= +NSDictionary* Breakpad::NextCrashReportConfiguration() { + NSDictionary* configuration = [Uploader readConfigurationDataFromFile:NextCrashReportToUpload()]; + return FixedUpCrashReportConfiguration(configuration); +} + +//============================================================================= +NSDictionary* Breakpad::FixedUpCrashReportConfiguration(NSDictionary* configuration) { + NSMutableDictionary* fixedConfiguration = [[configuration mutableCopy] autorelease]; + // kReporterMinidumpDirectoryKey can become stale because the app's data container path includes + // an UUID that is not guaranteed to stay the same over time. + [fixedConfiguration setObject:KeyValue(@BREAKPAD_DUMP_DIRECTORY) + forKey:@kReporterMinidumpDirectoryKey]; + return fixedConfiguration; +} + +//============================================================================= +NSDate* Breakpad::DateOfMostRecentCrashReport() { + NSString* directory = KeyValue(@BREAKPAD_DUMP_DIRECTORY); + if (!directory) { + return nil; + } + NSFileManager* fileManager = [NSFileManager defaultManager]; + NSArray* dirContents = [fileManager contentsOfDirectoryAtPath:directory error:nil]; + NSArray* dumps = [dirContents filteredArrayUsingPredicate:[NSPredicate + predicateWithFormat:@"self ENDSWITH '.dmp'"]]; + NSDate* mostRecentCrashReportDate = nil; + for (NSString* dump in dumps) { + NSString* filePath = [directory stringByAppendingPathComponent:dump]; + NSDate* crashReportDate = + [[fileManager attributesOfItemAtPath:filePath error:nil] fileCreationDate]; + if (!mostRecentCrashReportDate) { + mostRecentCrashReportDate = crashReportDate; + } else if (crashReportDate) { + mostRecentCrashReportDate = [mostRecentCrashReportDate laterDate:crashReportDate]; + } + } + return mostRecentCrashReportDate; +} + +//============================================================================= +void Breakpad::HandleNetworkResponse(NSDictionary* configuration, + NSData* data, + NSError* error) { + Uploader* uploader = [[[Uploader alloc] + initWithConfig:configuration] autorelease]; + [uploader handleNetworkResponse:data withError:error]; +} + +//============================================================================= +void Breakpad::UploadReportWithConfiguration( + NSDictionary* configuration, + NSDictionary* server_parameters, + BreakpadUploadCompletionCallback callback) { + Uploader* uploader = [[[Uploader alloc] + initWithConfig:configuration] autorelease]; + if (!uploader) + return; + for (NSString* key in server_parameters) { + [uploader addServerParameter:[server_parameters objectForKey:key] + forKey:key]; + } + if (callback) { + [uploader setUploadCompletionBlock:^(NSString* report_id, NSError* error) { + dispatch_async(dispatch_get_main_queue(), ^{ + callback(report_id, error); + }); + }]; + } + [uploader report]; +} + +//============================================================================= +void Breakpad::UploadNextReport(NSDictionary* server_parameters) { + NSDictionary* configuration = NextCrashReportConfiguration(); + if (configuration) { + return UploadReportWithConfiguration(configuration, server_parameters, + nullptr); + } +} + +//============================================================================= +void Breakpad::UploadData(NSData* data, NSString* name, + NSDictionary* server_parameters) { + NSMutableDictionary* config = [NSMutableDictionary dictionary]; + + LongStringDictionary::Iterator it(*config_params_); + while (const LongStringDictionary::Entry* next = it.Next()) { + [config setValue:[NSString stringWithUTF8String:next->value] + forKey:[NSString stringWithUTF8String:next->key]]; + } + + Uploader* uploader = + [[[Uploader alloc] initWithConfig:config] autorelease]; + for (NSString* key in server_parameters) { + [uploader addServerParameter:[server_parameters objectForKey:key] + forKey:key]; + } + [uploader uploadData:data name:name]; +} + +//============================================================================= +NSDictionary* Breakpad::GenerateReport(NSDictionary* server_parameters) { + NSString* dumpDirAsNSString = KeyValue(@BREAKPAD_DUMP_DIRECTORY); + if (!dumpDirAsNSString) + return nil; + const char* dumpDir = [dumpDirAsNSString UTF8String]; + + google_breakpad::MinidumpGenerator generator(mach_task_self(), + MACH_PORT_NULL); + std::string dumpId; + std::string dumpFilename = generator.UniqueNameInDirectory(dumpDir, &dumpId); + bool success = generator.Write(dumpFilename.c_str()); + if (!success) + return nil; + + LongStringDictionary params = *config_params_; + for (NSString* key in server_parameters) { + params.SetKeyValue([key UTF8String], + [[server_parameters objectForKey:key] UTF8String]); + } + ConfigFile config_file; + config_file.WriteFile(dumpDir, ¶ms, dumpDir, dumpId.c_str()); + + // Handle results. + NSMutableDictionary* result = [NSMutableDictionary dictionary]; + NSString* dumpFullPath = [NSString stringWithUTF8String:dumpFilename.c_str()]; + [result setValue:dumpFullPath + forKey:@BREAKPAD_OUTPUT_DUMP_FILE]; + [result setValue:[NSString stringWithUTF8String:config_file.GetFilePath()] + forKey:@BREAKPAD_OUTPUT_CONFIG_FILE]; + return result; +} + +//============================================================================= +bool Breakpad::HandleMinidump(const char* dump_dir, + const char* minidump_id) { + config_file_.WriteFile(dump_dir, + config_params_, + dump_dir, + minidump_id); + + // Return true here to indicate that we've processed things as much as we + // want. + return true; +} + +//============================================================================= +void Breakpad::HandleUncaughtException(NSException* exception) { + // Generate the minidump. + google_breakpad::IosExceptionMinidumpGenerator generator(exception); + const std::string minidump_path = + config_params_->GetValueForKey(BREAKPAD_DUMP_DIRECTORY); + std::string minidump_id; + std::string minidump_filename = generator.UniqueNameInDirectory(minidump_path, + &minidump_id); + generator.Write(minidump_filename.c_str()); + + // Copy the config params and our custom parameter. This is necessary for 2 + // reasons: + // 1- config_params_ is protected. + // 2- If the application crash while trying to handle this exception, a usual + // report will be generated. This report must not contain these special + // keys. + LongStringDictionary params = *config_params_; + params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "type", "exception"); + params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "exceptionName", + [[exception name] UTF8String]); + params.SetKeyValue(BREAKPAD_SERVER_PARAMETER_PREFIX "exceptionReason", + [[exception reason] UTF8String]); + + // And finally write the config file. + ConfigFile config_file; + config_file.WriteFile(minidump_path.c_str(), + ¶ms, + minidump_path.c_str(), + minidump_id.c_str()); +} + +//============================================================================= + +#pragma mark - +#pragma mark Public API + +//============================================================================= +BreakpadRef BreakpadCreate(NSDictionary* parameters) { + try { + // This is confusing. Our two main allocators for breakpad memory are: + // - gKeyValueAllocator for the key/value memory + // - gBreakpadAllocator for the Breakpad, ExceptionHandler, and other + // breakpad allocations which are accessed at exception handling time. + // + // But in order to avoid these two allocators themselves from being smashed, + // we'll protect them as well by allocating them with gMasterAllocator. + // + // gMasterAllocator itself will NOT be protected, but this doesn't matter, + // since once it does its allocations and locks the memory, smashes to + // itself don't affect anything we care about. + gMasterAllocator = + new ProtectedMemoryAllocator(sizeof(ProtectedMemoryAllocator) * 2); + + gKeyValueAllocator = + new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator))) + ProtectedMemoryAllocator(sizeof(LongStringDictionary)); + + // Create a mutex for use in accessing the LongStringDictionary + int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL); + if (mutexResult == 0) { + + // With the current compiler, gBreakpadAllocator is allocating 1444 bytes. + // Let's round up to the nearest page size. + // + int breakpad_pool_size = 4096; + + /* + sizeof(Breakpad) + + sizeof(google_breakpad::ExceptionHandler) + + sizeof( STUFF ALLOCATED INSIDE ExceptionHandler ) + */ + + gBreakpadAllocator = + new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator))) + ProtectedMemoryAllocator(breakpad_pool_size); + + // Stack-based autorelease pool for Breakpad::Create() obj-c code. + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + Breakpad* breakpad = Breakpad::Create(parameters); + + if (breakpad) { + // Make read-only to protect against memory smashers + gMasterAllocator->Protect(); + gKeyValueAllocator->Protect(); + gBreakpadAllocator->Protect(); + // Can uncomment this line to figure out how much space was actually + // allocated using this allocator + // printf("gBreakpadAllocator allocated size = %d\n", + // gBreakpadAllocator->GetAllocatedSize() ); + [pool release]; + return (BreakpadRef)breakpad; + } + + [pool release]; + } + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadCreate() : error\n"); + } + + if (gKeyValueAllocator) { + gKeyValueAllocator->~ProtectedMemoryAllocator(); + gKeyValueAllocator = NULL; + } + + if (gBreakpadAllocator) { + gBreakpadAllocator->~ProtectedMemoryAllocator(); + gBreakpadAllocator = NULL; + } + + delete gMasterAllocator; + gMasterAllocator = NULL; + + return NULL; +} + +//============================================================================= +void BreakpadRelease(BreakpadRef ref) { + try { + Breakpad* breakpad = (Breakpad*)ref; + + if (gMasterAllocator) { + gMasterAllocator->Unprotect(); + gKeyValueAllocator->Unprotect(); + gBreakpadAllocator->Unprotect(); + + breakpad->~Breakpad(); + + // Unfortunately, it's not possible to deallocate this stuff + // because the exception handling thread is still finishing up + // asynchronously at this point... OK, it could be done with + // locks, etc. But since BreakpadRelease() should usually only + // be called right before the process exits, it's not worth + // deallocating this stuff. +#if 0 + gKeyValueAllocator->~ProtectedMemoryAllocator(); + gBreakpadAllocator->~ProtectedMemoryAllocator(); + delete gMasterAllocator; + + gMasterAllocator = NULL; + gKeyValueAllocator = NULL; + gBreakpadAllocator = NULL; +#endif + + pthread_mutex_destroy(&gDictionaryMutex); + } + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadRelease() : error\n"); + } +} + +//============================================================================= +void BreakpadSetKeyValue(BreakpadRef ref, NSString* key, NSString* value) { + try { + // Not called at exception time + Breakpad* breakpad = (Breakpad*)ref; + + if (breakpad && key && gKeyValueAllocator) { + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); + + breakpad->SetKeyValue(key, value); + } + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadSetKeyValue() : error\n"); + } +} + +void BreakpadAddUploadParameter(BreakpadRef ref, + NSString* key, + NSString* value) { + // The only difference, internally, between an upload parameter and + // a key value one that is set with BreakpadSetKeyValue is that we + // prepend the keyname with a special prefix. This informs the + // crash sender that the parameter should be sent along with the + // POST of the crash dump upload. + try { + Breakpad* breakpad = (Breakpad*)ref; + + if (breakpad && key && gKeyValueAllocator) { + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); + + NSString* prefixedKey = [@BREAKPAD_SERVER_PARAMETER_PREFIX + stringByAppendingString:key]; + breakpad->SetKeyValue(prefixedKey, value); + } + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadSetKeyValue() : error\n"); + } +} + +void BreakpadRemoveUploadParameter(BreakpadRef ref, + NSString* key) { + try { + // Not called at exception time + Breakpad* breakpad = (Breakpad*)ref; + + if (breakpad && key && gKeyValueAllocator) { + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); + + NSString* prefixedKey = [NSString stringWithFormat:@"%@%@", + @BREAKPAD_SERVER_PARAMETER_PREFIX, key]; + breakpad->RemoveKeyValue(prefixedKey); + } + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadRemoveKeyValue() : error\n"); + } +} +//============================================================================= +NSString* BreakpadKeyValue(BreakpadRef ref, NSString* key) { + NSString* value = nil; + + try { + // Not called at exception time + Breakpad* breakpad = (Breakpad*)ref; + + if (!breakpad || !key || !gKeyValueAllocator) + return nil; + + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); + + value = breakpad->KeyValue(key); + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadKeyValue() : error\n"); + } + + return value; +} + +//============================================================================= +void BreakpadRemoveKeyValue(BreakpadRef ref, NSString* key) { + try { + // Not called at exception time + Breakpad* breakpad = (Breakpad*)ref; + + if (breakpad && key && gKeyValueAllocator) { + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); + + breakpad->RemoveKeyValue(key); + } + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadRemoveKeyValue() : error\n"); + } +} + +//============================================================================= +int BreakpadGetCrashReportCount(BreakpadRef ref) { + try { + // Not called at exception time + Breakpad* breakpad = (Breakpad*)ref; + + if (breakpad) { + return static_cast([breakpad->CrashReportsToUpload() count]); + } + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadGetCrashReportCount() : error\n"); + } + return false; +} + +//============================================================================= +void BreakpadUploadNextReport(BreakpadRef ref) { + BreakpadUploadNextReportWithParameters(ref, nil, nullptr); +} + +//============================================================================= +NSDictionary* BreakpadGetNextReportConfiguration(BreakpadRef ref) { + try { + Breakpad* breakpad = (Breakpad*)ref; + if (breakpad) + return breakpad->NextCrashReportConfiguration(); + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadGetNextReportConfiguration() : error\n"); + } + return nil; +} + +//============================================================================= +NSDate* BreakpadGetDateOfMostRecentCrashReport(BreakpadRef ref) { + try { + Breakpad* breakpad = (Breakpad*)ref; + if (breakpad) { + return breakpad->DateOfMostRecentCrashReport(); + } + } catch (...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadGetDateOfMostRecentCrashReport() : error\n"); + } + return nil; +} + +//============================================================================= +void BreakpadUploadReportWithParametersAndConfiguration( + BreakpadRef ref, + NSDictionary* server_parameters, + NSDictionary* configuration, + BreakpadUploadCompletionCallback callback) { + try { + Breakpad* breakpad = (Breakpad*)ref; + if (!breakpad || !configuration) + return; + breakpad->UploadReportWithConfiguration(configuration, server_parameters, + callback); + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, + "BreakpadUploadReportWithParametersAndConfiguration() : error\n"); + } +} + +//============================================================================= +void BreakpadUploadNextReportWithParameters( + BreakpadRef ref, + NSDictionary* server_parameters, + BreakpadUploadCompletionCallback callback) { + try { + Breakpad* breakpad = (Breakpad*)ref; + if (!breakpad) + return; + NSDictionary* configuration = breakpad->NextCrashReportConfiguration(); + if (!configuration) + return; + return BreakpadUploadReportWithParametersAndConfiguration( + ref, server_parameters, configuration, callback); + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadUploadNextReportWithParameters() : error\n"); + } +} + +void BreakpadHandleNetworkResponse(BreakpadRef ref, + NSDictionary* configuration, + NSData* data, + NSError* error) { + try { + // Not called at exception time + Breakpad* breakpad = (Breakpad*)ref; + if (breakpad && configuration) + breakpad->HandleNetworkResponse(configuration,data, error); + + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadHandleNetworkResponse() : error\n"); + } +} + +//============================================================================= +void BreakpadUploadData(BreakpadRef ref, NSData* data, NSString* name, + NSDictionary* server_parameters) { + try { + // Not called at exception time + Breakpad* breakpad = (Breakpad*)ref; + + if (breakpad) { + breakpad->UploadData(data, name, server_parameters); + } + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadUploadData() : error\n"); + } +} + +//============================================================================= +NSDictionary* BreakpadGenerateReport(BreakpadRef ref, + NSDictionary* server_parameters) { + try { + // Not called at exception time + Breakpad* breakpad = (Breakpad*)ref; + + if (breakpad) { + return breakpad->GenerateReport(server_parameters); + } else { + return nil; + } + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadGenerateReport() : error\n"); + return nil; + } +} diff --git a/shared/sentry/external/breakpad/src/client/ios/Breakpad.xcodeproj/project.pbxproj b/shared/sentry/external/breakpad/src/client/ios/Breakpad.xcodeproj/project.pbxproj new file mode 100644 index 000000000..ca5f1f058 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/ios/Breakpad.xcodeproj/project.pbxproj @@ -0,0 +1,604 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 06D561E62700974500F9F2E8 /* encoding_util.h in Headers */ = {isa = PBXBuildFile; fileRef = 06D561E42700974500F9F2E8 /* encoding_util.h */; }; + 06D561E72700974500F9F2E8 /* encoding_util.m in Sources */ = {isa = PBXBuildFile; fileRef = 06D561E52700974500F9F2E8 /* encoding_util.m */; }; + 14569321182CE29F0029C465 /* ucontext_compat.h in Headers */ = {isa = PBXBuildFile; fileRef = 14569320182CE29F0029C465 /* ucontext_compat.h */; }; + 14569323182CE2C10029C465 /* mach_vm_compat.h in Headers */ = {isa = PBXBuildFile; fileRef = 14569322182CE2C10029C465 /* mach_vm_compat.h */; }; + 16BFA67014E195E9009704F8 /* ios_exception_minidump_generator.h in Headers */ = {isa = PBXBuildFile; fileRef = 16BFA66E14E195E9009704F8 /* ios_exception_minidump_generator.h */; }; + 16BFA67214E1965A009704F8 /* ios_exception_minidump_generator.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16BFA67114E1965A009704F8 /* ios_exception_minidump_generator.mm */; }; + 16C7CCCB147D4A4300776EAD /* BreakpadDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7C968147D4A4200776EAD /* BreakpadDefines.h */; }; + 16C7CCCC147D4A4300776EAD /* Breakpad.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7C96A147D4A4200776EAD /* Breakpad.h */; }; + 16C7CCCD147D4A4300776EAD /* Breakpad.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16C7C96B147D4A4200776EAD /* Breakpad.mm */; }; + 16C7CDE8147D4A4300776EAD /* ConfigFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CB9E147D4A4300776EAD /* ConfigFile.h */; }; + 16C7CDE9147D4A4300776EAD /* ConfigFile.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CB9F147D4A4300776EAD /* ConfigFile.mm */; }; + 16C7CDF5147D4A4300776EAD /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CBAD147D4A4300776EAD /* breakpad_nlist_64.cc */; }; + 16C7CDF6147D4A4300776EAD /* breakpad_nlist_64.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CBAE147D4A4300776EAD /* breakpad_nlist_64.h */; }; + 16C7CDF7147D4A4300776EAD /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CBAF147D4A4300776EAD /* dynamic_images.cc */; }; + 16C7CDF8147D4A4300776EAD /* dynamic_images.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CBB0147D4A4300776EAD /* dynamic_images.h */; }; + 16C7CDF9147D4A4300776EAD /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CBB1147D4A4300776EAD /* exception_handler.cc */; }; + 16C7CDFA147D4A4300776EAD /* exception_handler.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CBB2147D4A4300776EAD /* exception_handler.h */; }; + 16C7CDFC147D4A4300776EAD /* minidump_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CBB4147D4A4300776EAD /* minidump_generator.cc */; }; + 16C7CDFD147D4A4300776EAD /* minidump_generator.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CBB5147D4A4300776EAD /* minidump_generator.h */; }; + 16C7CDFE147D4A4300776EAD /* protected_memory_allocator.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CBBC147D4A4300776EAD /* protected_memory_allocator.cc */; }; + 16C7CDFF147D4A4300776EAD /* protected_memory_allocator.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CBBD147D4A4300776EAD /* protected_memory_allocator.h */; }; + 16C7CE08147D4A4300776EAD /* uploader.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CBEA147D4A4300776EAD /* uploader.h */; }; + 16C7CE09147D4A4300776EAD /* uploader.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CBEB147D4A4300776EAD /* uploader.mm */; }; + 16C7CE18147D4A4300776EAD /* minidump_file_writer-inl.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC04147D4A4300776EAD /* minidump_file_writer-inl.h */; }; + 16C7CE19147D4A4300776EAD /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC05147D4A4300776EAD /* minidump_file_writer.cc */; }; + 16C7CE1A147D4A4300776EAD /* minidump_file_writer.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC06147D4A4300776EAD /* minidump_file_writer.h */; }; + 16C7CE40147D4A4300776EAD /* convert_UTF.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC4A147D4A4300776EAD /* convert_UTF.cc */; }; + 16C7CE41147D4A4300776EAD /* convert_UTF.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC4B147D4A4300776EAD /* convert_UTF.h */; }; + 16C7CE78147D4A4300776EAD /* GTMLogger.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC88147D4A4300776EAD /* GTMLogger.h */; }; + 16C7CE79147D4A4300776EAD /* GTMLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC89147D4A4300776EAD /* GTMLogger.m */; }; + 16C7CE7A147D4A4300776EAD /* HTTPMultipartUpload.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC8A147D4A4300776EAD /* HTTPMultipartUpload.h */; }; + 16C7CE7B147D4A4300776EAD /* HTTPMultipartUpload.m in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC8B147D4A4300776EAD /* HTTPMultipartUpload.m */; }; + 16C7CE83147D4A4300776EAD /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC93147D4A4300776EAD /* file_id.cc */; }; + 16C7CE84147D4A4300776EAD /* file_id.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC94147D4A4300776EAD /* file_id.h */; }; + 16C7CE85147D4A4300776EAD /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC95147D4A4300776EAD /* macho_id.cc */; }; + 16C7CE86147D4A4300776EAD /* macho_id.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC96147D4A4300776EAD /* macho_id.h */; }; + 16C7CE8A147D4A4300776EAD /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC9A147D4A4300776EAD /* macho_utilities.cc */; }; + 16C7CE8B147D4A4300776EAD /* macho_utilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC9B147D4A4300776EAD /* macho_utilities.h */; }; + 16C7CE8C147D4A4300776EAD /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC9C147D4A4300776EAD /* macho_walker.cc */; }; + 16C7CE8D147D4A4300776EAD /* macho_walker.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CC9D147D4A4300776EAD /* macho_walker.h */; }; + 16C7CE8F147D4A4300776EAD /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CC9F147D4A4300776EAD /* string_utilities.cc */; }; + 16C7CE90147D4A4300776EAD /* string_utilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CCA0147D4A4300776EAD /* string_utilities.h */; }; + 16C7CE93147D4A4300776EAD /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CCA4147D4A4300776EAD /* md5.cc */; }; + 16C7CE94147D4A4300776EAD /* md5.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CCA5147D4A4300776EAD /* md5.h */; }; + 16C7CEA7147D4A4300776EAD /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = 16C7CCB9147D4A4300776EAD /* string_conversion.cc */; }; + 16C7CEA8147D4A4300776EAD /* string_conversion.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7CCBA147D4A4300776EAD /* string_conversion.h */; }; + 16C92FAD150DF8330053D7BA /* BreakpadController.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C92FAB150DF8330053D7BA /* BreakpadController.h */; }; + 16C92FAE150DF8330053D7BA /* BreakpadController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16C92FAC150DF8330053D7BA /* BreakpadController.mm */; }; + 1EEEB60F1720821900F7E689 /* simple_string_dictionary.cc in Sources */ = {isa = PBXBuildFile; fileRef = 1EEEB60C1720821900F7E689 /* simple_string_dictionary.cc */; }; + 1EEEB6101720821900F7E689 /* simple_string_dictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EEEB60D1720821900F7E689 /* simple_string_dictionary.h */; }; + AA747D9F0F9514B9006C5449 /* Breakpad_Prefix.pch in Headers */ = {isa = PBXBuildFile; fileRef = AA747D9E0F9514B9006C5449 /* Breakpad_Prefix.pch */; }; + AACBBE4A0F95108600F1A2B1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AACBBE490F95108600F1A2B1 /* Foundation.framework */; }; + CF6D547D1F9E6FFE00E95174 /* long_string_dictionary.cc in Sources */ = {isa = PBXBuildFile; fileRef = CF6D547C1F9E6FFE00E95174 /* long_string_dictionary.cc */; }; + CF706DC11F7C6EFB002C54C7 /* long_string_dictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = CF706DC01F7C6EFB002C54C7 /* long_string_dictionary.h */; }; + E69213D8265202570071B04F /* HTTPRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = E69213D6265202570071B04F /* HTTPRequest.h */; }; + E69213D9265202570071B04F /* HTTPRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = E69213D7265202570071B04F /* HTTPRequest.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 06D561E42700974500F9F2E8 /* encoding_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = encoding_util.h; sourceTree = ""; }; + 06D561E52700974500F9F2E8 /* encoding_util.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = encoding_util.m; sourceTree = ""; }; + 14569320182CE29F0029C465 /* ucontext_compat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ucontext_compat.h; sourceTree = ""; }; + 14569322182CE2C10029C465 /* mach_vm_compat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mach_vm_compat.h; sourceTree = ""; }; + 16BFA66E14E195E9009704F8 /* ios_exception_minidump_generator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ios_exception_minidump_generator.h; sourceTree = ""; }; + 16BFA67114E1965A009704F8 /* ios_exception_minidump_generator.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ios_exception_minidump_generator.mm; sourceTree = ""; }; + 16C7C968147D4A4200776EAD /* BreakpadDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BreakpadDefines.h; sourceTree = ""; }; + 16C7C96A147D4A4200776EAD /* Breakpad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Breakpad.h; sourceTree = ""; }; + 16C7C96B147D4A4200776EAD /* Breakpad.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = Breakpad.mm; sourceTree = ""; }; + 16C7CB9E147D4A4300776EAD /* ConfigFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConfigFile.h; sourceTree = ""; }; + 16C7CB9F147D4A4300776EAD /* ConfigFile.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = ConfigFile.mm; sourceTree = ""; }; + 16C7CBAD147D4A4300776EAD /* breakpad_nlist_64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = breakpad_nlist_64.cc; sourceTree = ""; }; + 16C7CBAE147D4A4300776EAD /* breakpad_nlist_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = breakpad_nlist_64.h; sourceTree = ""; }; + 16C7CBAF147D4A4300776EAD /* dynamic_images.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dynamic_images.cc; sourceTree = ""; }; + 16C7CBB0147D4A4300776EAD /* dynamic_images.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dynamic_images.h; sourceTree = ""; }; + 16C7CBB1147D4A4300776EAD /* exception_handler.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = exception_handler.cc; sourceTree = ""; }; + 16C7CBB2147D4A4300776EAD /* exception_handler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = exception_handler.h; sourceTree = ""; }; + 16C7CBB4147D4A4300776EAD /* minidump_generator.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = minidump_generator.cc; sourceTree = ""; }; + 16C7CBB5147D4A4300776EAD /* minidump_generator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = minidump_generator.h; sourceTree = ""; }; + 16C7CBBC147D4A4300776EAD /* protected_memory_allocator.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = protected_memory_allocator.cc; sourceTree = ""; }; + 16C7CBBD147D4A4300776EAD /* protected_memory_allocator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = protected_memory_allocator.h; sourceTree = ""; }; + 16C7CBEA147D4A4300776EAD /* uploader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = uploader.h; sourceTree = ""; }; + 16C7CBEB147D4A4300776EAD /* uploader.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = uploader.mm; sourceTree = ""; }; + 16C7CC04147D4A4300776EAD /* minidump_file_writer-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "minidump_file_writer-inl.h"; sourceTree = ""; }; + 16C7CC05147D4A4300776EAD /* minidump_file_writer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = minidump_file_writer.cc; sourceTree = ""; }; + 16C7CC06147D4A4300776EAD /* minidump_file_writer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = minidump_file_writer.h; sourceTree = ""; }; + 16C7CC07147D4A4300776EAD /* minidump_file_writer_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = minidump_file_writer_unittest.cc; sourceTree = ""; }; + 16C7CC4A147D4A4300776EAD /* convert_UTF.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = convert_UTF.cc; sourceTree = ""; }; + 16C7CC4B147D4A4300776EAD /* convert_UTF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = convert_UTF.h; sourceTree = ""; }; + 16C7CC88147D4A4300776EAD /* GTMLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = GTMLogger.h; sourceTree = ""; }; + 16C7CC89147D4A4300776EAD /* GTMLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GTMLogger.m; sourceTree = ""; }; + 16C7CC8A147D4A4300776EAD /* HTTPMultipartUpload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTTPMultipartUpload.h; sourceTree = ""; }; + 16C7CC8B147D4A4300776EAD /* HTTPMultipartUpload.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTTPMultipartUpload.m; sourceTree = ""; }; + 16C7CC93147D4A4300776EAD /* file_id.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = file_id.cc; sourceTree = ""; }; + 16C7CC94147D4A4300776EAD /* file_id.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = file_id.h; sourceTree = ""; }; + 16C7CC95147D4A4300776EAD /* macho_id.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = macho_id.cc; sourceTree = ""; }; + 16C7CC96147D4A4300776EAD /* macho_id.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = macho_id.h; sourceTree = ""; }; + 16C7CC9A147D4A4300776EAD /* macho_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = macho_utilities.cc; sourceTree = ""; }; + 16C7CC9B147D4A4300776EAD /* macho_utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = macho_utilities.h; sourceTree = ""; }; + 16C7CC9C147D4A4300776EAD /* macho_walker.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = macho_walker.cc; sourceTree = ""; }; + 16C7CC9D147D4A4300776EAD /* macho_walker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = macho_walker.h; sourceTree = ""; }; + 16C7CC9F147D4A4300776EAD /* string_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = string_utilities.cc; sourceTree = ""; }; + 16C7CCA0147D4A4300776EAD /* string_utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = string_utilities.h; sourceTree = ""; }; + 16C7CCA4147D4A4300776EAD /* md5.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = md5.cc; sourceTree = ""; }; + 16C7CCA5147D4A4300776EAD /* md5.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = md5.h; sourceTree = ""; }; + 16C7CCB9147D4A4300776EAD /* string_conversion.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = string_conversion.cc; sourceTree = ""; }; + 16C7CCBA147D4A4300776EAD /* string_conversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = string_conversion.h; sourceTree = ""; }; + 16C92FAB150DF8330053D7BA /* BreakpadController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BreakpadController.h; sourceTree = ""; }; + 16C92FAC150DF8330053D7BA /* BreakpadController.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = BreakpadController.mm; sourceTree = ""; }; + 1EEEB60C1720821900F7E689 /* simple_string_dictionary.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = simple_string_dictionary.cc; sourceTree = ""; }; + 1EEEB60D1720821900F7E689 /* simple_string_dictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = simple_string_dictionary.h; sourceTree = ""; }; + AA747D9E0F9514B9006C5449 /* Breakpad_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Breakpad_Prefix.pch; sourceTree = SOURCE_ROOT; }; + AACBBE490F95108600F1A2B1 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + CF6D547C1F9E6FFE00E95174 /* long_string_dictionary.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = long_string_dictionary.cc; sourceTree = ""; }; + CF706DC01F7C6EFB002C54C7 /* long_string_dictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = long_string_dictionary.h; sourceTree = ""; }; + D2AAC07E0554694100DB518D /* libBreakpad.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libBreakpad.a; sourceTree = BUILT_PRODUCTS_DIR; }; + E69213D6265202570071B04F /* HTTPRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HTTPRequest.h; sourceTree = ""; }; + E69213D7265202570071B04F /* HTTPRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = HTTPRequest.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + D2AAC07C0554694100DB518D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + AACBBE4A0F95108600F1A2B1 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 034768DFFF38A50411DB9C8B /* Products */ = { + isa = PBXGroup; + children = ( + D2AAC07E0554694100DB518D /* libBreakpad.a */, + ); + name = Products; + sourceTree = ""; + }; + 0867D691FE84028FC02AAC07 /* Breakpad */ = { + isa = PBXGroup; + children = ( + 08FB77AEFE84172EC02AAC07 /* Classes */, + 32C88DFF0371C24200C91783 /* Other Sources */, + 0867D69AFE84028FC02AAC07 /* Frameworks */, + 034768DFFF38A50411DB9C8B /* Products */, + ); + name = Breakpad; + sourceTree = ""; + }; + 0867D69AFE84028FC02AAC07 /* Frameworks */ = { + isa = PBXGroup; + children = ( + AACBBE490F95108600F1A2B1 /* Foundation.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 08FB77AEFE84172EC02AAC07 /* Classes */ = { + isa = PBXGroup; + children = ( + 16C7C965147D4A4200776EAD /* client */, + 16C7CC47147D4A4300776EAD /* common */, + ); + name = Classes; + sourceTree = ""; + }; + 16BFA66A14E195E9009704F8 /* handler */ = { + isa = PBXGroup; + children = ( + 16BFA67114E1965A009704F8 /* ios_exception_minidump_generator.mm */, + 16BFA66E14E195E9009704F8 /* ios_exception_minidump_generator.h */, + ); + path = handler; + sourceTree = ""; + }; + 16C7C965147D4A4200776EAD /* client */ = { + isa = PBXGroup; + children = ( + 16C7C966147D4A4200776EAD /* apple */, + 16C7C969147D4A4200776EAD /* ios */, + 16C7C99E147D4A4200776EAD /* mac */, + 16C7CC04147D4A4300776EAD /* minidump_file_writer-inl.h */, + 16C7CC05147D4A4300776EAD /* minidump_file_writer.cc */, + 16C7CC06147D4A4300776EAD /* minidump_file_writer.h */, + 16C7CC07147D4A4300776EAD /* minidump_file_writer_unittest.cc */, + ); + name = client; + path = ..; + sourceTree = SOURCE_ROOT; + }; + 16C7C966147D4A4200776EAD /* apple */ = { + isa = PBXGroup; + children = ( + 16C7C967147D4A4200776EAD /* Framework */, + ); + path = apple; + sourceTree = ""; + }; + 16C7C967147D4A4200776EAD /* Framework */ = { + isa = PBXGroup; + children = ( + 16C7C968147D4A4200776EAD /* BreakpadDefines.h */, + ); + path = Framework; + sourceTree = ""; + }; + 16C7C969147D4A4200776EAD /* ios */ = { + isa = PBXGroup; + children = ( + 16C92FAB150DF8330053D7BA /* BreakpadController.h */, + 16C92FAC150DF8330053D7BA /* BreakpadController.mm */, + 16BFA66A14E195E9009704F8 /* handler */, + 16C7C96A147D4A4200776EAD /* Breakpad.h */, + 16C7C96B147D4A4200776EAD /* Breakpad.mm */, + ); + path = ios; + sourceTree = ""; + }; + 16C7C99E147D4A4200776EAD /* mac */ = { + isa = PBXGroup; + children = ( + 16C7CB9D147D4A4300776EAD /* crash_generation */, + 16C7CBAA147D4A4300776EAD /* handler */, + 16C7CBC8147D4A4300776EAD /* sender */, + ); + path = mac; + sourceTree = ""; + }; + 16C7CB9D147D4A4300776EAD /* crash_generation */ = { + isa = PBXGroup; + children = ( + 16C7CB9E147D4A4300776EAD /* ConfigFile.h */, + 16C7CB9F147D4A4300776EAD /* ConfigFile.mm */, + ); + path = crash_generation; + sourceTree = ""; + }; + 16C7CBAA147D4A4300776EAD /* handler */ = { + isa = PBXGroup; + children = ( + 16C7CBAD147D4A4300776EAD /* breakpad_nlist_64.cc */, + 16C7CBAE147D4A4300776EAD /* breakpad_nlist_64.h */, + 16C7CBAF147D4A4300776EAD /* dynamic_images.cc */, + 16C7CBB0147D4A4300776EAD /* dynamic_images.h */, + 16C7CBB1147D4A4300776EAD /* exception_handler.cc */, + 16C7CBB2147D4A4300776EAD /* exception_handler.h */, + 14569322182CE2C10029C465 /* mach_vm_compat.h */, + 16C7CBB4147D4A4300776EAD /* minidump_generator.cc */, + 16C7CBB5147D4A4300776EAD /* minidump_generator.h */, + 16C7CBBC147D4A4300776EAD /* protected_memory_allocator.cc */, + 16C7CBBD147D4A4300776EAD /* protected_memory_allocator.h */, + 14569320182CE29F0029C465 /* ucontext_compat.h */, + ); + path = handler; + sourceTree = ""; + }; + 16C7CBC8147D4A4300776EAD /* sender */ = { + isa = PBXGroup; + children = ( + 16C7CBEA147D4A4300776EAD /* uploader.h */, + 16C7CBEB147D4A4300776EAD /* uploader.mm */, + ); + path = sender; + sourceTree = ""; + }; + 16C7CC47147D4A4300776EAD /* common */ = { + isa = PBXGroup; + children = ( + CF706DC01F7C6EFB002C54C7 /* long_string_dictionary.h */, + CF6D547C1F9E6FFE00E95174 /* long_string_dictionary.cc */, + 1EEEB60C1720821900F7E689 /* simple_string_dictionary.cc */, + 1EEEB60D1720821900F7E689 /* simple_string_dictionary.h */, + 16C7CC4A147D4A4300776EAD /* convert_UTF.cc */, + 16C7CC4B147D4A4300776EAD /* convert_UTF.h */, + 16C7CC82147D4A4300776EAD /* mac */, + 16C7CCA4147D4A4300776EAD /* md5.cc */, + 16C7CCA5147D4A4300776EAD /* md5.h */, + 16C7CCB9147D4A4300776EAD /* string_conversion.cc */, + 16C7CCBA147D4A4300776EAD /* string_conversion.h */, + ); + name = common; + path = ../../common; + sourceTree = SOURCE_ROOT; + }; + 16C7CC82147D4A4300776EAD /* mac */ = { + isa = PBXGroup; + children = ( + 06D561E42700974500F9F2E8 /* encoding_util.h */, + 06D561E52700974500F9F2E8 /* encoding_util.m */, + 16C7CC88147D4A4300776EAD /* GTMLogger.h */, + 16C7CC89147D4A4300776EAD /* GTMLogger.m */, + E69213D6265202570071B04F /* HTTPRequest.h */, + E69213D7265202570071B04F /* HTTPRequest.m */, + 16C7CC8A147D4A4300776EAD /* HTTPMultipartUpload.h */, + 16C7CC8B147D4A4300776EAD /* HTTPMultipartUpload.m */, + 16C7CC93147D4A4300776EAD /* file_id.cc */, + 16C7CC94147D4A4300776EAD /* file_id.h */, + 16C7CC95147D4A4300776EAD /* macho_id.cc */, + 16C7CC96147D4A4300776EAD /* macho_id.h */, + 16C7CC9A147D4A4300776EAD /* macho_utilities.cc */, + 16C7CC9B147D4A4300776EAD /* macho_utilities.h */, + 16C7CC9C147D4A4300776EAD /* macho_walker.cc */, + 16C7CC9D147D4A4300776EAD /* macho_walker.h */, + 16C7CC9F147D4A4300776EAD /* string_utilities.cc */, + 16C7CCA0147D4A4300776EAD /* string_utilities.h */, + ); + path = mac; + sourceTree = ""; + }; + 32C88DFF0371C24200C91783 /* Other Sources */ = { + isa = PBXGroup; + children = ( + AA747D9E0F9514B9006C5449 /* Breakpad_Prefix.pch */, + ); + name = "Other Sources"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + D2AAC07A0554694100DB518D /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + AA747D9F0F9514B9006C5449 /* Breakpad_Prefix.pch in Headers */, + 16C7CCCB147D4A4300776EAD /* BreakpadDefines.h in Headers */, + 16C7CCCC147D4A4300776EAD /* Breakpad.h in Headers */, + 16C7CDE8147D4A4300776EAD /* ConfigFile.h in Headers */, + 14569321182CE29F0029C465 /* ucontext_compat.h in Headers */, + 16C7CDF6147D4A4300776EAD /* breakpad_nlist_64.h in Headers */, + 16C7CDF8147D4A4300776EAD /* dynamic_images.h in Headers */, + 16C7CDFA147D4A4300776EAD /* exception_handler.h in Headers */, + 16C7CDFD147D4A4300776EAD /* minidump_generator.h in Headers */, + 16C7CDFF147D4A4300776EAD /* protected_memory_allocator.h in Headers */, + 16C7CE08147D4A4300776EAD /* uploader.h in Headers */, + 16C7CE18147D4A4300776EAD /* minidump_file_writer-inl.h in Headers */, + 16C7CE1A147D4A4300776EAD /* minidump_file_writer.h in Headers */, + 06D561E62700974500F9F2E8 /* encoding_util.h in Headers */, + 16C7CE41147D4A4300776EAD /* convert_UTF.h in Headers */, + 16C7CE78147D4A4300776EAD /* GTMLogger.h in Headers */, + E69213D8265202570071B04F /* HTTPRequest.h in Headers */, + 16C7CE7A147D4A4300776EAD /* HTTPMultipartUpload.h in Headers */, + 16C7CE84147D4A4300776EAD /* file_id.h in Headers */, + 16C7CE86147D4A4300776EAD /* macho_id.h in Headers */, + 16C7CE8B147D4A4300776EAD /* macho_utilities.h in Headers */, + 16C7CE8D147D4A4300776EAD /* macho_walker.h in Headers */, + 16C7CE90147D4A4300776EAD /* string_utilities.h in Headers */, + 16C7CE94147D4A4300776EAD /* md5.h in Headers */, + 16C7CEA8147D4A4300776EAD /* string_conversion.h in Headers */, + 16BFA67014E195E9009704F8 /* ios_exception_minidump_generator.h in Headers */, + 16C92FAD150DF8330053D7BA /* BreakpadController.h in Headers */, + CF706DC11F7C6EFB002C54C7 /* long_string_dictionary.h in Headers */, + 1EEEB6101720821900F7E689 /* simple_string_dictionary.h in Headers */, + 14569323182CE2C10029C465 /* mach_vm_compat.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + D2AAC07D0554694100DB518D /* Breakpad */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1DEB921E08733DC00010E9CD /* Build configuration list for PBXNativeTarget "Breakpad" */; + buildPhases = ( + D2AAC07A0554694100DB518D /* Headers */, + D2AAC07B0554694100DB518D /* Sources */, + D2AAC07C0554694100DB518D /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Breakpad; + productName = Breakpad; + productReference = D2AAC07E0554694100DB518D /* libBreakpad.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 0867D690FE84028FC02AAC07 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0510; + }; + buildConfigurationList = 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "Breakpad" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + English, + Japanese, + French, + German, + da, + de, + es, + fr, + it, + ja, + nl, + no, + sl, + sv, + tr, + ); + mainGroup = 0867D691FE84028FC02AAC07 /* Breakpad */; + productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + D2AAC07D0554694100DB518D /* Breakpad */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXSourcesBuildPhase section */ + D2AAC07B0554694100DB518D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 16C7CCCD147D4A4300776EAD /* Breakpad.mm in Sources */, + E69213D9265202570071B04F /* HTTPRequest.m in Sources */, + 16C7CDE9147D4A4300776EAD /* ConfigFile.mm in Sources */, + 16C7CDF5147D4A4300776EAD /* breakpad_nlist_64.cc in Sources */, + 16C7CDF7147D4A4300776EAD /* dynamic_images.cc in Sources */, + 16C7CDF9147D4A4300776EAD /* exception_handler.cc in Sources */, + 16C7CDFC147D4A4300776EAD /* minidump_generator.cc in Sources */, + 16C7CDFE147D4A4300776EAD /* protected_memory_allocator.cc in Sources */, + 16C7CE09147D4A4300776EAD /* uploader.mm in Sources */, + CF6D547D1F9E6FFE00E95174 /* long_string_dictionary.cc in Sources */, + 16C7CE19147D4A4300776EAD /* minidump_file_writer.cc in Sources */, + 16C7CE40147D4A4300776EAD /* convert_UTF.cc in Sources */, + 16C7CE79147D4A4300776EAD /* GTMLogger.m in Sources */, + 06D561E72700974500F9F2E8 /* encoding_util.m in Sources */, + 16C7CE7B147D4A4300776EAD /* HTTPMultipartUpload.m in Sources */, + 16C7CE83147D4A4300776EAD /* file_id.cc in Sources */, + 16C7CE85147D4A4300776EAD /* macho_id.cc in Sources */, + 16C7CE8A147D4A4300776EAD /* macho_utilities.cc in Sources */, + 16C7CE8C147D4A4300776EAD /* macho_walker.cc in Sources */, + 16C7CE8F147D4A4300776EAD /* string_utilities.cc in Sources */, + 16C7CE93147D4A4300776EAD /* md5.cc in Sources */, + 16C7CEA7147D4A4300776EAD /* string_conversion.cc in Sources */, + 16BFA67214E1965A009704F8 /* ios_exception_minidump_generator.mm in Sources */, + 16C92FAE150DF8330053D7BA /* BreakpadController.mm in Sources */, + 1EEEB60F1720821900F7E689 /* simple_string_dictionary.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1DEB921F08733DC00010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LIBRARY = "libc++"; + COPY_PHASE_STRIP = NO; + DSTROOT = /tmp/Breakpad.dst; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/../mac/build/Debug\"", + ); + GCC_DYNAMIC_NO_PIC = NO; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = Breakpad_Prefix.pch; + INSTALL_PATH = /usr/local/lib; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/Breakpad.build/Objects-normal/i386\"", + "\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/Breakpad.build/Objects-normal/x86_64\"", + "\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/breakpadUtilities.build/Objects-normal/i386\"", + "\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/breakpadUtilities.build/Objects-normal/x86_64\"", + "\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/gtest.build/Objects-normal/i386\"", + "\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/gtest.build/Objects-normal/x86_64\"", + "\"$(SRCROOT)/../mac/build/Debug\"", + "\"$(SRCROOT)/../mac/gcov\"", + ); + PRODUCT_NAME = Breakpad; + }; + name = Debug; + }; + 1DEB922008733DC00010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LIBRARY = "libc++"; + DSTROOT = /tmp/Breakpad.dst; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/../mac/build/Debug\"", + ); + GCC_MODEL_TUNING = G5; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = Breakpad_Prefix.pch; + INSTALL_PATH = /usr/local/lib; + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/Breakpad.build/Objects-normal/i386\"", + "\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/Breakpad.build/Objects-normal/x86_64\"", + "\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/breakpadUtilities.build/Objects-normal/i386\"", + "\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/breakpadUtilities.build/Objects-normal/x86_64\"", + "\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/gtest.build/Objects-normal/i386\"", + "\"$(SRCROOT)/../mac/build/Breakpad.build/Debug/gtest.build/Objects-normal/x86_64\"", + "\"$(SRCROOT)/../mac/build/Debug\"", + "\"$(SRCROOT)/../mac/gcov\"", + ); + PRODUCT_NAME = Breakpad; + }; + name = Release; + }; + 1DEB922308733DC00010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNKNOWN_PRAGMAS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + ../../, + ../../client/apple/Framework, + ../../common/mac, + ); + IPHONEOS_DEPLOYMENT_TARGET = 5.0; + ONLY_ACTIVE_ARCH = YES; + OTHER_LDFLAGS = "-ObjC"; + SDKROOT = iphoneos; + WARNING_CFLAGS = "-Wundef"; + }; + name = Debug; + }; + 1DEB922408733DC00010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++0x"; + CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_SHADOW = YES; + GCC_WARN_SIGN_COMPARE = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNKNOWN_PRAGMAS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_LABEL = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + HEADER_SEARCH_PATHS = ( + ../../, + ../../client/apple/Framework, + ../../common/mac, + ); + IPHONEOS_DEPLOYMENT_TARGET = 5.0; + OTHER_LDFLAGS = "-ObjC"; + SDKROOT = iphoneos; + WARNING_CFLAGS = "-Wundef"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1DEB921E08733DC00010E9CD /* Build configuration list for PBXNativeTarget "Breakpad" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB921F08733DC00010E9CD /* Debug */, + 1DEB922008733DC00010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1DEB922208733DC00010E9CD /* Build configuration list for PBXProject "Breakpad" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB922308733DC00010E9CD /* Debug */, + 1DEB922408733DC00010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 0867D690FE84028FC02AAC07 /* Project object */; +} diff --git a/shared/sentry/external/breakpad/src/client/ios/BreakpadController.h b/shared/sentry/external/breakpad/src/client/ios/BreakpadController.h new file mode 100644 index 000000000..6c70c202f --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/ios/BreakpadController.h @@ -0,0 +1,154 @@ +// 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 CLIENT_IOS_HANDLER_IOS_BREAKPAD_CONTROLLER_H_ +#define CLIENT_IOS_HANDLER_IOS_BREAKPAD_CONTROLLER_H_ + +#import + +#import "client/ios/Breakpad.h" + +// This class is used to offer a higher level API around BreakpadRef. It +// configures it, ensures thread-safety, and sends crash reports back to the +// collecting server. By default, no crash reports are sent, the user must call +// |setUploadingEnabled:YES| to start the uploading. +@interface BreakpadController : NSObject { + @private + // The dispatch queue that will own the breakpad reference. + dispatch_queue_t queue_; + + // Instance of Breakpad crash reporter. This is owned by the queue, but can + // be created on the main thread at startup. + BreakpadRef breakpadRef_; + + // The dictionary that contains configuration for breakpad. Modifying it + // should only happen when the controller is not started. The initial value + // is the infoDictionary of the bundle of the application. + NSMutableDictionary* configuration_; + + // Whether or not crash reports should be uploaded. + BOOL enableUploads_; + + // Whether the controller has been started on the main thread. This is only + // used to assert the initialization order is correct. + BOOL started_; + + // The interval to wait between two uploads. Value is 0 if no upload must be + // done. + int uploadIntervalInSeconds_; + + // The dictionary that contains additional server parameters to send when + // uploading crash reports. + NSDictionary* uploadTimeParameters_; + + // The callback to call on report upload completion. + BreakpadUploadCompletionCallback uploadCompleteCallback_; +} + +// Singleton. ++ (BreakpadController*)sharedInstance; + +// Update the controller configuration. Merges its old configuration with the +// new one. Merge is done by replacing the old values by the new values. +- (void)updateConfiguration:(NSDictionary*)configuration; + +// Reset the controller configuration to its initial value, which is the +// infoDictionary of the bundle of the application. +- (void)resetConfiguration; + +// Configure the URL to upload the report to. This must be called at least once +// if the URL is not in the bundle information. +- (void)setUploadingURL:(NSString*)url; + +// Set the minimal interval between two uploads in seconds. This must be called +// at least once if the interval is not in the bundle information. A value of 0 +// will prevent uploads. +- (void)setUploadInterval:(int)intervalInSeconds; + +// Set additional server parameters to send when uploading crash reports. +- (void)setParametersToAddAtUploadTime:(NSDictionary*)uploadTimeParameters; + +// Specify an upload parameter that will be added to the crash report when a +// crash report is generated. See |BreakpadAddUploadParameter|. +- (void)addUploadParameter:(NSString*)value forKey:(NSString*)key; + +// Sets the callback to be called after uploading a crash report to the server. +// Only the latest callback registered will be called. +- (void)setUploadCallback:(BreakpadUploadCompletionCallback)callback; + +// Remove a previously-added parameter from the upload parameter set. See +// |BreakpadRemoveUploadParameter|. +- (void)removeUploadParameterForKey:(NSString*)key; + +// Access the underlying BreakpadRef. This method is asynchronous, and will be +// executed on the thread owning the BreakpadRef variable. Moreover, if the +// controller is not started, the block will be called with a NULL parameter. +- (void)withBreakpadRef:(void(^)(BreakpadRef))callback; + +// Starts the BreakpadController by registering crash handlers. If +// |onCurrentThread| is YES, all setup is done on the current thread, otherwise +// it is done on a private queue. +- (void)start:(BOOL)onCurrentThread; + +// Unregisters the crash handlers. +- (void)stop; + +// Returns whether or not the controller is started. +- (BOOL)isStarted; + +// Enables or disables uploading of crash reports, but does not stop the +// BreakpadController. +- (void)setUploadingEnabled:(BOOL)enabled; + +// Check if there is currently a crash report to upload. +- (void)hasReportToUpload:(void(^)(BOOL))callback; + +// Get the number of crash reports waiting to upload. +- (void)getCrashReportCount:(void(^)(int))callback; + +// Get the next report to upload. +// - If upload is disabled, callback will be called with (nil, -1). +// - If a delay is to be waited before sending, callback will be called with +// (nil, n), with n (> 0) being the number of seconds to wait. +// - if no delay is needed, callback will be called with (0, configuration), +// configuration being next report to upload, or nil if none is pending. +- (void)getNextReportConfigurationOrSendDelay: + (void(^)(NSDictionary*, int))callback; + +// Get the date of the most recent crash report. +- (void)getDateOfMostRecentCrashReport:(void(^)(NSDate *))callback; + +// Sends synchronously the report specified by |configuration|. This method is +// NOT thread safe and must be called from the breakpad thread. +- (void)threadUnsafeSendReportWithConfiguration:(NSDictionary*)configuration + withBreakpadRef:(BreakpadRef)ref; + +@end + +#endif // CLIENT_IOS_HANDLER_IOS_BREAKPAD_CONTROLLER_H_ diff --git a/shared/sentry/external/breakpad/src/client/ios/BreakpadController.mm b/shared/sentry/external/breakpad/src/client/ios/BreakpadController.mm new file mode 100644 index 000000000..01fb5f13a --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/ios/BreakpadController.mm @@ -0,0 +1,374 @@ +// 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. + +#import "BreakpadController.h" + +#import +#include +#include +#include +#include +#include + +#include + +#pragma mark - +#pragma mark Private Methods + +@interface BreakpadController () + +// Init the singleton instance. +- (id)initSingleton; + +// Load a crash report and send it to the server. +- (void)sendStoredCrashReports; + +// Returns when a report can be sent. |-1| means never, |0| means that a report +// can be sent immediately, a positive number is the number of seconds to wait +// before being allowed to upload a report. +- (int)sendDelay; + +// Notifies that a report will be sent, and update the last sending time +// accordingly. +- (void)reportWillBeSent; + +@end + +#pragma mark - +#pragma mark Anonymous namespace + +namespace { + +// The name of the user defaults key for the last submission to the crash +// server. +NSString* const kLastSubmission = @"com.google.Breakpad.LastSubmission"; + +// Returns a NSString describing the current platform. +NSString* GetPlatform() { + // Name of the system call for getting the platform. + static const char kHwMachineSysctlName[] = "hw.machine"; + + NSString* result = nil; + + size_t size = 0; + if (sysctlbyname(kHwMachineSysctlName, NULL, &size, NULL, 0) || size == 0) + return nil; + google_breakpad::scoped_array machine(new char[size]); + if (sysctlbyname(kHwMachineSysctlName, machine.get(), &size, NULL, 0) == 0) + result = [NSString stringWithUTF8String:machine.get()]; + return result; +} + +} // namespace + +#pragma mark - +#pragma mark BreakpadController Implementation + +@implementation BreakpadController + ++ (BreakpadController*)sharedInstance { + static dispatch_once_t onceToken; + static BreakpadController* sharedInstance ; + dispatch_once(&onceToken, ^{ + sharedInstance = [[BreakpadController alloc] initSingleton]; + }); + return sharedInstance; +} + +- (id)init { + return nil; +} + +- (id)initSingleton { + self = [super init]; + if (self) { + queue_ = dispatch_queue_create("com.google.BreakpadQueue", NULL); + enableUploads_ = NO; + started_ = NO; + [self resetConfiguration]; + } + return self; +} + +// Since this class is a singleton, this method is not expected to be called. +- (void)dealloc { + assert(!breakpadRef_); + dispatch_release(queue_); + [configuration_ release]; + [uploadTimeParameters_ release]; + [super dealloc]; +} + +#pragma mark - + +- (void)start:(BOOL)onCurrentThread { + if (started_) + return; + started_ = YES; + void(^startBlock)() = ^{ + assert(!breakpadRef_); + breakpadRef_ = BreakpadCreate(configuration_); + if (breakpadRef_) { + BreakpadAddUploadParameter(breakpadRef_, @"platform", GetPlatform()); + } + }; + if (onCurrentThread) + startBlock(); + else + dispatch_async(queue_, startBlock); +} + +- (void)stop { + if (!started_) + return; + started_ = NO; + dispatch_sync(queue_, ^{ + if (breakpadRef_) { + BreakpadRelease(breakpadRef_); + breakpadRef_ = NULL; + } + }); +} + +- (BOOL)isStarted { + return started_; +} + +// This method must be called from the breakpad queue. +- (void)threadUnsafeSendReportWithConfiguration:(NSDictionary*)configuration + withBreakpadRef:(BreakpadRef)ref { + NSAssert(started_, @"The controller must be started before " + "threadUnsafeSendReportWithConfiguration is called"); + if (breakpadRef_) { + BreakpadUploadReportWithParametersAndConfiguration( + breakpadRef_, uploadTimeParameters_, configuration, + uploadCompleteCallback_); + } +} + +- (void)setUploadingEnabled:(BOOL)enabled { + NSAssert(started_, + @"The controller must be started before setUploadingEnabled is called"); + dispatch_async(queue_, ^{ + if (enabled == enableUploads_) + return; + if (enabled) { + // Set this before calling doSendStoredCrashReport, because that + // calls sendDelay, which in turn checks this flag. + enableUploads_ = YES; + [self sendStoredCrashReports]; + } else { + // disable the enableUpload_ flag. + // sendDelay checks this flag and disables the upload of logs by sendStoredCrashReports + enableUploads_ = NO; + } + }); +} + +- (void)updateConfiguration:(NSDictionary*)configuration { + NSAssert(!started_, + @"The controller must not be started when updateConfiguration is called"); + [configuration_ addEntriesFromDictionary:configuration]; + NSString *uploadInterval = + [configuration_ valueForKey:@BREAKPAD_REPORT_INTERVAL]; + if (uploadInterval) + [self setUploadInterval:[uploadInterval intValue]]; +} + +- (void)resetConfiguration { + NSAssert(!started_, + @"The controller must not be started when resetConfiguration is called"); + [configuration_ autorelease]; + configuration_ = [[[NSBundle mainBundle] infoDictionary] mutableCopy]; + NSString *uploadInterval = + [configuration_ valueForKey:@BREAKPAD_REPORT_INTERVAL]; + [self setUploadInterval:[uploadInterval intValue]]; + [self setParametersToAddAtUploadTime:nil]; +} + +- (void)setUploadingURL:(NSString*)url { + NSAssert(!started_, + @"The controller must not be started when setUploadingURL is called"); + [configuration_ setValue:url forKey:@BREAKPAD_URL]; +} + +- (void)setUploadInterval:(int)intervalInSeconds { + NSAssert(!started_, + @"The controller must not be started when setUploadInterval is called"); + [configuration_ removeObjectForKey:@BREAKPAD_REPORT_INTERVAL]; + uploadIntervalInSeconds_ = intervalInSeconds; + if (uploadIntervalInSeconds_ < 0) + uploadIntervalInSeconds_ = 0; +} + +- (void)setParametersToAddAtUploadTime:(NSDictionary*)uploadTimeParameters { + NSAssert(!started_, @"The controller must not be started when " + "setParametersToAddAtUploadTime is called"); + [uploadTimeParameters_ autorelease]; + uploadTimeParameters_ = [uploadTimeParameters copy]; +} + +- (void)addUploadParameter:(NSString*)value forKey:(NSString*)key { + NSAssert(started_, + @"The controller must be started before addUploadParameter is called"); + dispatch_async(queue_, ^{ + if (breakpadRef_) + BreakpadAddUploadParameter(breakpadRef_, key, value); + }); +} + +- (void)setUploadCallback:(BreakpadUploadCompletionCallback)callback { + NSAssert(started_, + @"The controller must not be started before setUploadCallback is " + "called"); + dispatch_async(queue_, ^{ + uploadCompleteCallback_ = callback; + }); +} + +- (void)removeUploadParameterForKey:(NSString*)key { + NSAssert(started_, @"The controller must be started before " + "removeUploadParameterForKey is called"); + dispatch_async(queue_, ^{ + if (breakpadRef_) + BreakpadRemoveUploadParameter(breakpadRef_, key); + }); +} + +- (void)withBreakpadRef:(void(^)(BreakpadRef))callback { + dispatch_async(queue_, ^{ + callback(started_ ? breakpadRef_ : NULL); + }); +} + +- (void)hasReportToUpload:(void(^)(BOOL))callback { + NSAssert(started_, @"The controller must be started before " + "hasReportToUpload is called"); + dispatch_async(queue_, ^{ + callback(breakpadRef_ && (BreakpadGetCrashReportCount(breakpadRef_) > 0)); + }); +} + +- (void)getCrashReportCount:(void(^)(int))callback { + NSAssert(started_, @"The controller must be started before " + "getCrashReportCount is called"); + dispatch_async(queue_, ^{ + callback(breakpadRef_ ? BreakpadGetCrashReportCount(breakpadRef_) : 0); + }); +} + +- (void)getNextReportConfigurationOrSendDelay: + (void(^)(NSDictionary*, int))callback { + NSAssert(started_, @"The controller must be started before " + "getNextReportConfigurationOrSendDelay is called"); + dispatch_async(queue_, ^{ + if (!breakpadRef_) { + callback(nil, -1); + return; + } + int delay = [self sendDelay]; + if (delay != 0) { + callback(nil, delay); + return; + } + [self reportWillBeSent]; + callback(BreakpadGetNextReportConfiguration(breakpadRef_), 0); + }); +} + +- (void)getDateOfMostRecentCrashReport:(void(^)(NSDate *))callback { + NSAssert(started_, @"The controller must be started before " + "getDateOfMostRecentCrashReport is called"); + dispatch_async(queue_, ^{ + if (!breakpadRef_) { + callback(nil); + return; + } + callback(BreakpadGetDateOfMostRecentCrashReport(breakpadRef_)); + }); +} + +#pragma mark - + +- (int)sendDelay { + if (!breakpadRef_ || uploadIntervalInSeconds_ <= 0 || !enableUploads_) + return -1; + + // To prevent overloading the crash server, crashes are not sent than one + // report every |uploadIntervalInSeconds_|. A value in the user defaults is + // used to keep the time of the last upload. + NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; + NSNumber *lastTimeNum = [userDefaults objectForKey:kLastSubmission]; + NSTimeInterval lastTime = lastTimeNum ? [lastTimeNum floatValue] : 0; + NSTimeInterval spanSeconds = CFAbsoluteTimeGetCurrent() - lastTime; + + if (spanSeconds >= uploadIntervalInSeconds_) + return 0; + return uploadIntervalInSeconds_ - static_cast(spanSeconds); +} + +- (void)reportWillBeSent { + NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults]; + [userDefaults setObject:[NSNumber numberWithDouble:CFAbsoluteTimeGetCurrent()] + forKey:kLastSubmission]; + [userDefaults synchronize]; +} + +// This method must be called from the breakpad queue. +- (void)sendStoredCrashReports { + if (BreakpadGetCrashReportCount(breakpadRef_) == 0) + return; + + int timeToWait = [self sendDelay]; + + // Unable to ever send report. + if (timeToWait == -1) + return; + + // A report can be sent now. + if (timeToWait == 0) { + [self reportWillBeSent]; + BreakpadUploadNextReportWithParameters(breakpadRef_, uploadTimeParameters_, + uploadCompleteCallback_); + + // If more reports must be sent, make sure this method is called again. + if (BreakpadGetCrashReportCount(breakpadRef_) > 0) + timeToWait = uploadIntervalInSeconds_; + } + + // A report must be sent later. + if (timeToWait > 0) { + dispatch_time_t delay = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(timeToWait * NSEC_PER_SEC)); + dispatch_after(delay, queue_, ^{ + [self sendStoredCrashReports]; + }); + } +} + +@end diff --git a/shared/sentry/external/breakpad/src/client/ios/Breakpad_Prefix.pch b/shared/sentry/external/breakpad/src/client/ios/Breakpad_Prefix.pch new file mode 100644 index 000000000..bfb739423 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/ios/Breakpad_Prefix.pch @@ -0,0 +1,7 @@ +// +// Prefix header for all source files of the 'CocoaTouchStaticLibrary' target in the 'CocoaTouchStaticLibrary' project. +// + +#ifdef __OBJC__ + #import +#endif diff --git a/shared/sentry/external/breakpad/src/client/ios/exception_handler_no_mach.cc b/shared/sentry/external/breakpad/src/client/ios/exception_handler_no_mach.cc new file mode 100644 index 000000000..a716a5263 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/ios/exception_handler_no_mach.cc @@ -0,0 +1,265 @@ +// 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 +#include + +#include "client/mac/handler/minidump_generator.h" +#include "client/ios/exception_handler_no_mach.h" + +#ifndef USE_PROTECTED_ALLOCATIONS +#if TARGET_OS_TV +#define USE_PROTECTED_ALLOCATIONS 1 +#else +#define USE_PROTECTED_ALLOCATIONS 0 +#endif +#endif + +// If USE_PROTECTED_ALLOCATIONS is activated then the +// gBreakpadAllocator needs to be setup in other code +// ahead of time. Please see ProtectedMemoryAllocator.h +// for more details. +#if USE_PROTECTED_ALLOCATIONS + #include "client/mac/handler/protected_memory_allocator.h" + extern ProtectedMemoryAllocator* gBreakpadAllocator; +#endif + +namespace google_breakpad { + +const int kExceptionSignals[] = { + // Core-generating signals. + SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGQUIT, SIGSEGV, SIGSYS, SIGTRAP, SIGEMT, + SIGXCPU, SIGXFSZ, + // Non-core-generating but terminating signals. + SIGALRM, SIGHUP, SIGINT, SIGPIPE, SIGPROF, SIGTERM, SIGUSR1, SIGUSR2, + SIGVTALRM, SIGXCPU, SIGXFSZ, SIGIO, +}; +const int kNumHandledSignals = + sizeof(kExceptionSignals) / sizeof(kExceptionSignals[0]); +struct scoped_ptr old_handlers[kNumHandledSignals]; + +static union { +#if USE_PROTECTED_ALLOCATIONS +#if defined PAGE_MAX_SIZE + char protected_buffer[PAGE_MAX_SIZE] __attribute__((aligned(PAGE_MAX_SIZE))); +#else + char protected_buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); +#endif // defined PAGE_MAX_SIZE +#endif // USE_PROTECTED_ALLOCATIONS + google_breakpad::ExceptionHandler* handler; +} gProtectedData; + +ExceptionHandler::ExceptionHandler(const string& dump_path, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + bool install_handler, + const char* port_name) + : dump_path_(), + filter_(filter), + callback_(callback), + callback_context_(callback_context), + directCallback_(NULL), + installed_exception_handler_(false), + is_in_teardown_(false) { + // This will update to the ID and C-string pointers + set_dump_path(dump_path); + MinidumpGenerator::GatherSystemInformation(); + Setup(); +} + +// special constructor if we want to bypass minidump writing and +// simply get a callback with the exception information +ExceptionHandler::ExceptionHandler(DirectCallback callback, + void* callback_context, + bool install_handler) + : dump_path_(), + filter_(NULL), + callback_(NULL), + callback_context_(callback_context), + directCallback_(callback), + installed_exception_handler_(false), + is_in_teardown_(false) { + MinidumpGenerator::GatherSystemInformation(); + Setup(); +} + +ExceptionHandler::~ExceptionHandler() { + Teardown(); +} + +bool ExceptionHandler::WriteMinidumpWithException( + int exception_type, + int exception_code, + int exception_subcode, + breakpad_ucontext_t* task_context, + mach_port_t thread_name, + bool exit_after_write, + bool report_current_thread) { + bool result = false; + +#if !TARGET_OS_TV + exit_after_write = false; +#endif // !TARGET_OS_TV + + if (directCallback_) { + if (directCallback_(callback_context_, + exception_type, + exception_code, + exception_subcode, + thread_name) ) { + if (exit_after_write) + _exit(exception_type); + } + } else { + string minidump_id; + + // Putting the MinidumpGenerator in its own context will ensure that the + // destructor is executed, closing the newly created minidump file. + if (!dump_path_.empty()) { + MinidumpGenerator md(mach_task_self(), + report_current_thread ? MACH_PORT_NULL : + mach_thread_self()); + md.SetTaskContext(task_context); + if (exception_type && exception_code) { + // If this is a real exception, give the filter (if any) a chance to + // decide if this should be sent. + if (filter_ && !filter_(callback_context_)) + return false; + + md.SetExceptionInformation(exception_type, exception_code, + exception_subcode, thread_name); + } + + result = md.Write(next_minidump_path_c_); + } + + // Call user specified callback (if any) + if (callback_) { + // If the user callback returned true and we're handling an exception + // (rather than just writing out the file), then we should exit without + // forwarding the exception to the next handler. + if (callback_(dump_path_c_, next_minidump_id_c_, callback_context_, + result)) { + if (exit_after_write) + _exit(exception_type); + } + } + } + + return result; +} + +// static +void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) { +#if USE_PROTECTED_ALLOCATIONS + if (gBreakpadAllocator) + gBreakpadAllocator->Unprotect(); +#endif + gProtectedData.handler->WriteMinidumpWithException( + EXC_SOFTWARE, + MD_EXCEPTION_CODE_MAC_ABORT, + 0, + static_cast(uc), + mach_thread_self(), + true, + true); +#if USE_PROTECTED_ALLOCATIONS + if (gBreakpadAllocator) + gBreakpadAllocator->Protect(); +#endif + // uninstall our own crash handler so that when the signal is re-raised, the + // default handler takes over. + gProtectedData.handler->UninstallHandlers(); +} + +bool ExceptionHandler::InstallHandlers() { + // If a handler is already installed, something is really wrong. + if (gProtectedData.handler != NULL) + return false; + for (int i = 0; i < kNumHandledSignals; ++i) { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + sigaddset(&sa.sa_mask, kExceptionSignals[i]); + sa.sa_sigaction = ExceptionHandler::SignalHandler; + sa.sa_flags = SA_ONSTACK | SA_SIGINFO; + + if (sigaction(kExceptionSignals[i], &sa, old_handlers[i].get()) == -1) { + return false; + } + } + gProtectedData.handler = this; +#if USE_PROTECTED_ALLOCATIONS + assert(((size_t)(gProtectedData.protected_buffer) & PAGE_MASK) == 0); + mprotect(gProtectedData.protected_buffer, PAGE_SIZE, PROT_READ); +#endif // USE_PROTECTED_ALLOCATIONS + installed_exception_handler_ = true; + return true; +} + +bool ExceptionHandler::UninstallHandlers() { + for (int i = 0; i < kNumHandledSignals; ++i) { + if (old_handlers[i].get()) { + sigaction(kExceptionSignals[i], old_handlers[i].get(), NULL); + old_handlers[i].reset(); + } + } +#if USE_PROTECTED_ALLOCATIONS + mprotect(gProtectedData.protected_buffer, PAGE_SIZE, PROT_READ | PROT_WRITE); +#endif // USE_PROTECTED_ALLOCATIONS + gProtectedData.handler = NULL; + installed_exception_handler_ = false; + return true; +} + +bool ExceptionHandler::Setup() { + if (!InstallHandlers()) + return false; + return true; +} + +bool ExceptionHandler::Teardown() { + is_in_teardown_ = true; + + if (!UninstallHandlers()) + return false; + + return true; +} + +void ExceptionHandler::UpdateNextID() { + next_minidump_path_ = + (MinidumpGenerator::UniqueNameInDirectory(dump_path_, &next_minidump_id_)); + + next_minidump_path_c_ = next_minidump_path_.c_str(); + next_minidump_id_c_ = next_minidump_id_.c_str(); +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/ios/exception_handler_no_mach.h b/shared/sentry/external/breakpad/src/client/ios/exception_handler_no_mach.h new file mode 100644 index 000000000..ec598dcf3 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/ios/exception_handler_no_mach.h @@ -0,0 +1,179 @@ +// 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 CLIENT_IOS_HANDLER_EXCEPTION_HANDLER_NO_MACH_H__ +#define CLIENT_IOS_HANDLER_EXCEPTION_HANDLER_NO_MACH_H__ + +#include +#include + +#include + +#include "client/mac/handler/ucontext_compat.h" +#include "common/scoped_ptr.h" + +namespace google_breakpad { + +using std::string; + +class ExceptionHandler { + public: + // A callback function to run before Breakpad performs any substantial + // processing of an exception. A FilterCallback is called before writing + // a minidump. context is the parameter supplied by the user as + // callback_context when the handler was created. + // + // If a FilterCallback returns true, Breakpad will continue processing, + // attempting to write a minidump. If a FilterCallback returns false, Breakpad + // will immediately report the exception as unhandled without writing a + // minidump, allowing another handler the opportunity to handle it. + typedef bool (*FilterCallback)(void* context); + + // A callback function to run after the minidump has been written. + // |minidump_id| is a unique id for the dump, so the minidump + // file is /.dmp. + // |context| is the value passed into the constructor. + // |succeeded| indicates whether a minidump file was successfully written. + // Return true if the exception was fully handled and breakpad should exit. + // Return false to allow any other exception handlers to process the + // exception. + typedef bool (*MinidumpCallback)(const char* dump_dir, + const char* minidump_id, + void* context, bool succeeded); + + // A callback function which will be called directly if an exception occurs. + // This bypasses the minidump file writing and simply gives the client + // the exception information. + typedef bool (*DirectCallback)(void* context, + int exception_type, + int exception_code, + int exception_subcode, + mach_port_t thread_name); + + // Creates a new ExceptionHandler instance to handle writing minidumps. + // Minidump files will be written to dump_path, and the optional callback + // is called after writing the dump file, as described above. + // If install_handler is true, then a minidump will be written whenever + // an unhandled exception occurs. If it is false, minidumps will only + // be written when WriteMinidump is called. + // If port_name is non-NULL, attempt to perform out-of-process dump generation + // If port_name is NULL, in-process dump generation will be used. + ExceptionHandler(const string& dump_path, + FilterCallback filter, MinidumpCallback callback, + void* callback_context, bool install_handler, + const char* port_name); + + // A special constructor if we want to bypass minidump writing and + // simply get a callback with the exception information. + ExceptionHandler(DirectCallback callback, + void* callback_context, + bool install_handler); + + ~ExceptionHandler(); + + // Get and set the minidump path. + string dump_path() const { return dump_path_; } + void set_dump_path(const string& dump_path) { + dump_path_ = dump_path; + dump_path_c_ = dump_path_.c_str(); + UpdateNextID(); // Necessary to put dump_path_ in next_minidump_path_. + } + + private: + // Install the SIG exception handlers. + bool InstallHandlers(); + + // Uninstall the SIG exception handlers. + bool UninstallHandlers(); + + // Setup the handler thread, and if |install_handler| is true, install the + // mach exception port handler + bool Setup(); + + // Uninstall the mach exception handler (if any) and terminate the helper + // thread + bool Teardown(); + + // All minidump writing goes through this one routine. + // |task_context| can be NULL. If not, it will be used to retrieve the + // context of the current thread, instead of using |thread_get_state|. + bool WriteMinidumpWithException(int exception_type, + int exception_code, + int exception_subcode, + breakpad_ucontext_t* task_context, + mach_port_t thread_name, + bool exit_after_write, + bool report_current_thread); + + // Signal handler for SIG exceptions. + static void SignalHandler(int sig, siginfo_t* info, void* uc); + + // disallow copy ctor and operator= + explicit ExceptionHandler(const ExceptionHandler&); + void operator=(const ExceptionHandler&); + + // Generates a new ID and stores it in next_minidump_id_, and stores the + // path of the next minidump to be written in next_minidump_path_. + void UpdateNextID(); + + // The destination directory for the minidump + string dump_path_; + + // The basename of the next minidump w/o extension + string next_minidump_id_; + + // The full path to the next minidump to be written, including extension + string next_minidump_path_; + + // Pointers to the UTF-8 versions of above + const char* dump_path_c_; + const char* next_minidump_id_c_; + const char* next_minidump_path_c_; + + // The callback function and pointer to be passed back after the minidump + // has been written + FilterCallback filter_; + MinidumpCallback callback_; + void* callback_context_; + + // The callback function to be passed back when we don't want a minidump + // file to be written + DirectCallback directCallback_; + + // True, if we've installed the exception handler + bool installed_exception_handler_; + + // True, if we're in the process of uninstalling the exception handler and + // the thread. + bool is_in_teardown_; +}; + +} // namespace google_breakpad + +#endif // CLIENT_IOS_HANDLER_EXCEPTION_HANDLER_NO_MACH_H__ diff --git a/shared/sentry/external/breakpad/src/client/ios/handler/ios_exception_minidump_generator.h b/shared/sentry/external/breakpad/src/client/ios/handler/ios_exception_minidump_generator.h new file mode 100644 index 000000000..e48444a72 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/ios/handler/ios_exception_minidump_generator.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. + +// ios_exception_minidump_generator.h: Create a fake minidump from a +// NSException. + +#ifndef CLIENT_IOS_HANDLER_IOS_EXCEPTION_MINIDUMP_GENERATOR_H_ +#define CLIENT_IOS_HANDLER_IOS_EXCEPTION_MINIDUMP_GENERATOR_H_ + +#include + +#include "client/mac/handler/minidump_generator.h" + +namespace google_breakpad { + +class IosExceptionMinidumpGenerator : public MinidumpGenerator { + public: + explicit IosExceptionMinidumpGenerator(NSException* exception); + virtual ~IosExceptionMinidumpGenerator(); + + protected: + virtual bool WriteExceptionStream(MDRawDirectory* exception_stream); + virtual bool WriteThreadStream(mach_port_t thread_id, MDRawThread* thread); + + private: + + // Get the crashing program counter from the exception. + uintptr_t GetPCFromException(); + + // Get the crashing link register from the exception. + uintptr_t GetLRFromException(); + + // Write a virtual thread context for the crashing site. + bool WriteCrashingContext(MDLocationDescriptor* register_location); + // Per-CPU implementations of the above method. +#ifdef HAS_ARM_SUPPORT + bool WriteCrashingContextARM(MDLocationDescriptor* register_location); +#endif +#ifdef HAS_ARM64_SUPPORT + bool WriteCrashingContextARM64(MDLocationDescriptor* register_location); +#endif + + NSArray* return_addresses_; +}; + +} // namespace google_breakpad + +#endif // CLIENT_IOS_HANDLER_IOS_EXCEPTION_MINIDUMP_GENERATOR_H_ diff --git a/shared/sentry/external/breakpad/src/client/ios/handler/ios_exception_minidump_generator.mm b/shared/sentry/external/breakpad/src/client/ios/handler/ios_exception_minidump_generator.mm new file mode 100644 index 000000000..2a5d76d7b --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/ios/handler/ios_exception_minidump_generator.mm @@ -0,0 +1,210 @@ +// 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 "client/ios/handler/ios_exception_minidump_generator.h" + +#include + +#include "google_breakpad/common/minidump_cpu_arm.h" +#include "google_breakpad/common/minidump_cpu_arm64.h" +#include "google_breakpad/common/minidump_exception_mac.h" +#include "client/minidump_file_writer-inl.h" +#include "common/scoped_ptr.h" + +#if defined(HAS_ARM_SUPPORT) && defined(HAS_ARM64_SUPPORT) +#error "This file should be compiled for only one architecture at a time" +#endif + +namespace { + +const int kExceptionType = EXC_SOFTWARE; +const int kExceptionCode = MD_EXCEPTION_CODE_MAC_NS_EXCEPTION; + +#if defined(HAS_ARM_SUPPORT) || defined(HAS_ARM64_SUPPORT) +const uintptr_t kExpectedFinalFp = sizeof(uintptr_t); +const uintptr_t kExpectedFinalSp = 0; + +// Append the given value to the sp position of the stack represented +// by memory. +void AppendToMemory(uint8_t* memory, uintptr_t sp, uintptr_t data) { + memcpy(memory + sp, &data, sizeof(data)); +} +#endif + +} // namespace + +namespace google_breakpad { + +IosExceptionMinidumpGenerator::IosExceptionMinidumpGenerator( + NSException* exception) + : MinidumpGenerator(mach_task_self(), 0) { + return_addresses_ = [[exception callStackReturnAddresses] retain]; + SetExceptionInformation(kExceptionType, + kExceptionCode, + 0, + pthread_mach_thread_np(pthread_self())); +} + +IosExceptionMinidumpGenerator::~IosExceptionMinidumpGenerator() { + [return_addresses_ release]; +} + +bool IosExceptionMinidumpGenerator::WriteCrashingContext( + MDLocationDescriptor* register_location) { +#ifdef HAS_ARM_SUPPORT + return WriteCrashingContextARM(register_location); +#elif defined(HAS_ARM64_SUPPORT) + return WriteCrashingContextARM64(register_location); +#else + assert(false); + return false; +#endif +} + +#ifdef HAS_ARM_SUPPORT +bool IosExceptionMinidumpGenerator::WriteCrashingContextARM( + MDLocationDescriptor* register_location) { + TypedMDRVA context(&writer_); + if (!context.Allocate()) + return false; + *register_location = context.location(); + MDRawContextARM* context_ptr = context.get(); + memset(context_ptr, 0, sizeof(MDRawContextARM)); + context_ptr->context_flags = MD_CONTEXT_ARM_FULL; + context_ptr->iregs[MD_CONTEXT_ARM_REG_IOS_FP] = kExpectedFinalFp; // FP + context_ptr->iregs[MD_CONTEXT_ARM_REG_SP] = kExpectedFinalSp; // SP + context_ptr->iregs[MD_CONTEXT_ARM_REG_LR] = GetLRFromException(); // LR + context_ptr->iregs[MD_CONTEXT_ARM_REG_PC] = GetPCFromException(); // PC + return true; +} +#endif + +#ifdef HAS_ARM64_SUPPORT +bool IosExceptionMinidumpGenerator::WriteCrashingContextARM64( + MDLocationDescriptor* register_location) { + TypedMDRVA context(&writer_); + if (!context.Allocate()) + return false; + *register_location = context.location(); + MDRawContextARM64_Old* context_ptr = context.get(); + memset(context_ptr, 0, sizeof(*context_ptr)); + context_ptr->context_flags = MD_CONTEXT_ARM64_FULL_OLD; + context_ptr->iregs[MD_CONTEXT_ARM64_REG_FP] = kExpectedFinalFp; // FP + context_ptr->iregs[MD_CONTEXT_ARM64_REG_SP] = kExpectedFinalSp; // SP + context_ptr->iregs[MD_CONTEXT_ARM64_REG_LR] = GetLRFromException(); // LR + context_ptr->iregs[MD_CONTEXT_ARM64_REG_PC] = GetPCFromException(); // PC + return true; +} +#endif + +uintptr_t IosExceptionMinidumpGenerator::GetPCFromException() { + return [[return_addresses_ objectAtIndex:0] unsignedIntegerValue]; +} + +uintptr_t IosExceptionMinidumpGenerator::GetLRFromException() { + return [[return_addresses_ objectAtIndex:1] unsignedIntegerValue]; +} + +bool IosExceptionMinidumpGenerator::WriteExceptionStream( + MDRawDirectory* exception_stream) { +#if defined(HAS_ARM_SUPPORT) || defined(HAS_ARM64_SUPPORT) + TypedMDRVA exception(&writer_); + + if (!exception.Allocate()) + return false; + + exception_stream->stream_type = MD_EXCEPTION_STREAM; + exception_stream->location = exception.location(); + MDRawExceptionStream* exception_ptr = exception.get(); + exception_ptr->thread_id = pthread_mach_thread_np(pthread_self()); + + // This naming is confusing, but it is the proper translation from + // mach naming to minidump naming. + exception_ptr->exception_record.exception_code = kExceptionType; + exception_ptr->exception_record.exception_flags = kExceptionCode; + + if (!WriteCrashingContext(&exception_ptr->thread_context)) + return false; + + exception_ptr->exception_record.exception_address = GetPCFromException(); + return true; +#else + return MinidumpGenerator::WriteExceptionStream(exception_stream); +#endif +} + +bool IosExceptionMinidumpGenerator::WriteThreadStream(mach_port_t thread_id, + MDRawThread* thread) { +#if defined(HAS_ARM_SUPPORT) || defined(HAS_ARM64_SUPPORT) + if (pthread_mach_thread_np(pthread_self()) != thread_id) + return MinidumpGenerator::WriteThreadStream(thread_id, thread); + + size_t frame_count = [return_addresses_ count]; + if (frame_count == 0) + return false; + UntypedMDRVA memory(&writer_); + size_t pointer_size = sizeof(uintptr_t); + size_t frame_record_size = 2 * pointer_size; + size_t stack_size = frame_record_size * (frame_count - 1) + pointer_size; + if (!memory.Allocate(stack_size)) + return false; + scoped_array stack_memory(new uint8_t[stack_size]); + uintptr_t sp = stack_size - pointer_size; + uintptr_t fp = 0; + uintptr_t lr = 0; + for (size_t current_frame = frame_count - 1; + current_frame > 0; + --current_frame) { + AppendToMemory(stack_memory.get(), sp, lr); + sp -= pointer_size; + AppendToMemory(stack_memory.get(), sp, fp); + fp = sp; + sp -= pointer_size; + lr = [[return_addresses_ objectAtIndex:current_frame] unsignedIntegerValue]; + } + if (!memory.Copy(stack_memory.get(), stack_size)) + return false; + assert(sp == kExpectedFinalSp); + assert(fp == kExpectedFinalFp); + assert(lr == GetLRFromException()); + thread->stack.start_of_memory_range = sp; + thread->stack.memory = memory.location(); + memory_blocks_.push_back(thread->stack); + + if (!WriteCrashingContext(&thread->thread_context)) + return false; + + thread->thread_id = thread_id; + return true; +#else + return MinidumpGenerator::WriteThreadStream(thread_id, thread); +#endif +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/linux/crash_generation/client_info.h b/shared/sentry/external/breakpad/src/client/linux/crash_generation/client_info.h new file mode 100644 index 000000000..d0a184a63 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/crash_generation/client_info.h @@ -0,0 +1,53 @@ +// 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 CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_ +#define CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_ + +namespace google_breakpad { + +class CrashGenerationServer; + +class ClientInfo { + public: + ClientInfo(pid_t pid, CrashGenerationServer* crash_server) + : crash_server_(crash_server), + pid_(pid) {} + + CrashGenerationServer* crash_server() const { return crash_server_; } + pid_t pid() const { return pid_; } + + private: + CrashGenerationServer* crash_server_; + pid_t pid_; +}; + +} + +#endif // CLIENT_LINUX_CRASH_GENERATION_CLIENT_INFO_H_ diff --git a/shared/sentry/external/breakpad/src/client/linux/crash_generation/crash_generation_client.cc b/shared/sentry/external/breakpad/src/client/linux/crash_generation/crash_generation_client.cc new file mode 100644 index 000000000..d8bfbbad2 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/crash_generation/crash_generation_client.cc @@ -0,0 +1,105 @@ +// 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 "client/linux/crash_generation/crash_generation_client.h" + +#include +#include +#include + +#include + +#include "common/linux/eintr_wrapper.h" +#include "common/linux/ignore_ret.h" +#include "third_party/lss/linux_syscall_support.h" + +namespace google_breakpad { + +namespace { + +class CrashGenerationClientImpl : public CrashGenerationClient { + public: + explicit CrashGenerationClientImpl(int server_fd) : server_fd_(server_fd) {} + virtual ~CrashGenerationClientImpl() {} + + virtual bool RequestDump(const void* blob, size_t blob_size) { + int fds[2]; + if (sys_pipe(fds) < 0) + return false; + static const unsigned kControlMsgSize = CMSG_SPACE(sizeof(int)); + + struct kernel_iovec iov; + iov.iov_base = const_cast(blob); + iov.iov_len = blob_size; + + struct kernel_msghdr msg = { 0 }; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + char cmsg[kControlMsgSize] = ""; + msg.msg_control = cmsg; + msg.msg_controllen = sizeof(cmsg); + + struct cmsghdr* hdr = CMSG_FIRSTHDR(&msg); + hdr->cmsg_level = SOL_SOCKET; + hdr->cmsg_type = SCM_RIGHTS; + hdr->cmsg_len = CMSG_LEN(sizeof(int)); + int* p = reinterpret_cast(CMSG_DATA(hdr)); + *p = fds[1]; + + ssize_t ret = HANDLE_EINTR(sys_sendmsg(server_fd_, &msg, 0)); + sys_close(fds[1]); + if (ret < 0) { + sys_close(fds[0]); + return false; + } + + // Wait for an ACK from the server. + char b; + IGNORE_RET(HANDLE_EINTR(sys_read(fds[0], &b, 1))); + sys_close(fds[0]); + + return true; + } + + private: + int server_fd_; + + DISALLOW_COPY_AND_ASSIGN(CrashGenerationClientImpl); +}; + +} // namespace + +// static +CrashGenerationClient* CrashGenerationClient::TryCreate(int server_fd) { + if (server_fd < 0) + return NULL; + return new CrashGenerationClientImpl(server_fd); +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/linux/crash_generation/crash_generation_client.h b/shared/sentry/external/breakpad/src/client/linux/crash_generation/crash_generation_client.h new file mode 100644 index 000000000..4e68424ae --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/crash_generation/crash_generation_client.h @@ -0,0 +1,65 @@ +// 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 CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ +#define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ + +#include "common/basictypes.h" + +#include + +namespace google_breakpad { + +// CrashGenerationClient is an interface for implementing out-of-process crash +// dumping. The default implementation, accessed via the TryCreate() factory, +// works in conjunction with the CrashGenerationServer to generate a minidump +// via a remote process. +class CrashGenerationClient { + public: + CrashGenerationClient() {} + virtual ~CrashGenerationClient() {} + + // Request the crash server to generate a dump. |blob| is an opaque + // CrashContext pointer from exception_handler.h. + // Returns true if the dump was successful; false otherwise. + virtual bool RequestDump(const void* blob, size_t blob_size) = 0; + + // Returns a new CrashGenerationClient if |server_fd| is valid and + // connects to a CrashGenerationServer. Otherwise, return NULL. + // The returned CrashGenerationClient* is owned by the caller of + // this function. + static CrashGenerationClient* TryCreate(int server_fd); + + private: + DISALLOW_COPY_AND_ASSIGN(CrashGenerationClient); +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ diff --git a/shared/sentry/external/breakpad/src/client/linux/crash_generation/crash_generation_server.cc b/shared/sentry/external/breakpad/src/client/linux/crash_generation/crash_generation_server.cc new file mode 100644 index 000000000..8332f59d4 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/crash_generation/crash_generation_server.cc @@ -0,0 +1,333 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "client/linux/crash_generation/crash_generation_server.h" +#include "client/linux/crash_generation/client_info.h" +#include "client/linux/handler/exception_handler.h" +#include "client/linux/minidump_writer/minidump_writer.h" +#include "common/linux/eintr_wrapper.h" +#include "common/linux/guid_creator.h" +#include "common/linux/safe_readlink.h" + +static const char kCommandQuit = 'x'; + +namespace google_breakpad { + +CrashGenerationServer::CrashGenerationServer( + const int listen_fd, + OnClientDumpRequestCallback dump_callback, + void* dump_context, + OnClientExitingCallback exit_callback, + void* exit_context, + bool generate_dumps, + const string* dump_path) : + server_fd_(listen_fd), + dump_callback_(dump_callback), + dump_context_(dump_context), + exit_callback_(exit_callback), + exit_context_(exit_context), + generate_dumps_(generate_dumps), + started_(false) +{ + if (dump_path) + dump_dir_ = *dump_path; + else + dump_dir_ = "/tmp"; +} + +CrashGenerationServer::~CrashGenerationServer() +{ + if (started_) + Stop(); +} + +bool +CrashGenerationServer::Start() +{ + if (started_ || 0 > server_fd_) + return false; + + int control_pipe[2]; + if (pipe(control_pipe)) + return false; + + if (fcntl(control_pipe[0], F_SETFD, FD_CLOEXEC)) + return false; + if (fcntl(control_pipe[1], F_SETFD, FD_CLOEXEC)) + return false; + + if (fcntl(control_pipe[0], F_SETFL, O_NONBLOCK)) + return false; + + control_pipe_in_ = control_pipe[0]; + control_pipe_out_ = control_pipe[1]; + + if (pthread_create(&thread_, NULL, + ThreadMain, reinterpret_cast(this))) + return false; + + started_ = true; + return true; +} + +void +CrashGenerationServer::Stop() +{ + assert(pthread_self() != thread_); + + if (!started_) + return; + + HANDLE_EINTR(write(control_pipe_out_, &kCommandQuit, 1)); + + void* dummy; + pthread_join(thread_, &dummy); + + close(control_pipe_in_); + close(control_pipe_out_); + + started_ = false; +} + +//static +bool +CrashGenerationServer::CreateReportChannel(int* server_fd, int* client_fd) +{ + int fds[2]; + + if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, fds)) + return false; + + static const int on = 1; + // Enable passcred on the server end of the socket + if (setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on))) + return false; + + if (fcntl(fds[1], F_SETFL, O_NONBLOCK)) + return false; + if (fcntl(fds[1], F_SETFD, FD_CLOEXEC)) + return false; + + *client_fd = fds[0]; + *server_fd = fds[1]; + return true; +} + +// The following methods/functions execute on the server thread + +void +CrashGenerationServer::Run() +{ + struct pollfd pollfds[2]; + memset(&pollfds, 0, sizeof(pollfds)); + + pollfds[0].fd = server_fd_; + pollfds[0].events = POLLIN; + + pollfds[1].fd = control_pipe_in_; + pollfds[1].events = POLLIN; + + while (true) { + // infinite timeout + int nevents = poll(pollfds, sizeof(pollfds)/sizeof(pollfds[0]), -1); + if (-1 == nevents) { + if (EINTR == errno) { + continue; + } else { + return; + } + } + + if (pollfds[0].revents && !ClientEvent(pollfds[0].revents)) + return; + + if (pollfds[1].revents && !ControlEvent(pollfds[1].revents)) + return; + } +} + +bool +CrashGenerationServer::ClientEvent(short revents) +{ + if (POLLHUP & revents) + return false; + assert(POLLIN & revents); + + // A process has crashed and has signaled us by writing a datagram + // to the death signal socket. The datagram contains the crash context needed + // for writing the minidump as well as a file descriptor and a credentials + // block so that they can't lie about their pid. + + // The length of the control message: + static const unsigned kControlMsgSize = + CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred)); + // The length of the regular payload: + static const unsigned kCrashContextSize = + sizeof(google_breakpad::ExceptionHandler::CrashContext); + + struct msghdr msg = {0}; + struct iovec iov[1]; + char crash_context[kCrashContextSize]; + char control[kControlMsgSize]; + const ssize_t expected_msg_size = sizeof(crash_context); + + iov[0].iov_base = crash_context; + iov[0].iov_len = sizeof(crash_context); + msg.msg_iov = iov; + msg.msg_iovlen = sizeof(iov)/sizeof(iov[0]); + msg.msg_control = control; + msg.msg_controllen = kControlMsgSize; + + const ssize_t msg_size = HANDLE_EINTR(recvmsg(server_fd_, &msg, 0)); + if (msg_size != expected_msg_size) + return true; + + if (msg.msg_controllen != kControlMsgSize || + msg.msg_flags & ~MSG_TRUNC) + return true; + + // Walk the control payload and extract the file descriptor and validated pid. + pid_t crashing_pid = -1; + int signal_fd = -1; + for (struct cmsghdr* hdr = CMSG_FIRSTHDR(&msg); hdr; + hdr = CMSG_NXTHDR(&msg, hdr)) { + if (hdr->cmsg_level != SOL_SOCKET) + continue; + if (hdr->cmsg_type == SCM_RIGHTS) { + const unsigned len = hdr->cmsg_len - + (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr); + assert(len % sizeof(int) == 0u); + const unsigned num_fds = len / sizeof(int); + if (num_fds > 1 || num_fds == 0) { + // A nasty process could try and send us too many descriptors and + // force a leak. + for (unsigned i = 0; i < num_fds; ++i) + close(reinterpret_cast(CMSG_DATA(hdr))[i]); + return true; + } else { + signal_fd = reinterpret_cast(CMSG_DATA(hdr))[0]; + } + } else if (hdr->cmsg_type == SCM_CREDENTIALS) { + const struct ucred* cred = + reinterpret_cast(CMSG_DATA(hdr)); + crashing_pid = cred->pid; + } + } + + if (crashing_pid == -1 || signal_fd == -1) { + if (signal_fd != -1) + close(signal_fd); + return true; + } + + string minidump_filename; + if (!MakeMinidumpFilename(minidump_filename)) + return true; + + if (!google_breakpad::WriteMinidump(minidump_filename.c_str(), + crashing_pid, crash_context, + kCrashContextSize)) { + close(signal_fd); + return true; + } + + if (dump_callback_) { + ClientInfo info(crashing_pid, this); + + dump_callback_(dump_context_, &info, &minidump_filename); + } + + // Send the done signal to the process: it can exit now. + // (Closing this will make the child's sys_read unblock and return 0.) + close(signal_fd); + + return true; +} + +bool +CrashGenerationServer::ControlEvent(short revents) +{ + if (POLLHUP & revents) + return false; + assert(POLLIN & revents); + + char command; + if (read(control_pipe_in_, &command, 1)) + return false; + + switch (command) { + case kCommandQuit: + return false; + default: + assert(0); + } + + return true; +} + +bool +CrashGenerationServer::MakeMinidumpFilename(string& outFilename) +{ + GUID guid; + char guidString[kGUIDStringLength+1]; + + if (!(CreateGUID(&guid) + && GUIDToString(&guid, guidString, sizeof(guidString)))) + return false; + + char path[PATH_MAX]; + snprintf(path, sizeof(path), "%s/%s.dmp", dump_dir_.c_str(), guidString); + + outFilename = path; + return true; +} + +// static +void* +CrashGenerationServer::ThreadMain(void* arg) +{ + reinterpret_cast(arg)->Run(); + return NULL; +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/linux/crash_generation/crash_generation_server.h b/shared/sentry/external/breakpad/src/client/linux/crash_generation/crash_generation_server.h new file mode 100644 index 000000000..483fb709b --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/crash_generation/crash_generation_server.h @@ -0,0 +1,135 @@ +// 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 CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_ +#define CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_ + +#include + +#include + +#include "common/using_std_string.h" + +namespace google_breakpad { + +class ClientInfo; + +class CrashGenerationServer { +public: + // WARNING: callbacks may be invoked on a different thread + // than that which creates the CrashGenerationServer. They must + // be thread safe. + typedef void (*OnClientDumpRequestCallback)(void* context, + const ClientInfo* client_info, + const string* file_path); + + typedef void (*OnClientExitingCallback)(void* context, + const ClientInfo* client_info); + + // Create an instance with the given parameters. + // + // Parameter listen_fd: The server fd created by CreateReportChannel(). + // Parameter dump_callback: Callback for a client crash dump request. + // Parameter dump_context: Context for client crash dump request callback. + // Parameter exit_callback: Callback for client process exit. + // Parameter exit_context: Context for client exit callback. + // Parameter generate_dumps: Whether to automatically generate dumps. + // Client code of this class might want to generate dumps explicitly + // in the crash dump request callback. In that case, false can be + // passed for this parameter. + // Parameter dump_path: Path for generating dumps; required only if true is + // passed for generateDumps parameter; NULL can be passed otherwise. + CrashGenerationServer(const int listen_fd, + OnClientDumpRequestCallback dump_callback, + void* dump_context, + OnClientExitingCallback exit_callback, + void* exit_context, + bool generate_dumps, + const string* dump_path); + + ~CrashGenerationServer(); + + // Perform initialization steps needed to start listening to clients. + // + // Return true if initialization is successful; false otherwise. + bool Start(); + + // Stop the server. + void Stop(); + + // Create a "channel" that can be used by clients to report crashes + // to a CrashGenerationServer. |*server_fd| should be passed to + // this class's constructor, and |*client_fd| should be passed to + // the ExceptionHandler constructor in the client process. + static bool CreateReportChannel(int* server_fd, int* client_fd); + +private: + // Run the server's event loop + void Run(); + + // Invoked when an child process (client) event occurs + // Returning true => "keep running", false => "exit loop" + bool ClientEvent(short revents); + + // Invoked when the controlling thread (main) event occurs + // Returning true => "keep running", false => "exit loop" + bool ControlEvent(short revents); + + // Return a unique filename at which a minidump can be written + bool MakeMinidumpFilename(string& outFilename); + + // Trampoline to |Run()| + static void* ThreadMain(void* arg); + + int server_fd_; + + OnClientDumpRequestCallback dump_callback_; + void* dump_context_; + + OnClientExitingCallback exit_callback_; + void* exit_context_; + + bool generate_dumps_; + + string dump_dir_; + + bool started_; + + pthread_t thread_; + int control_pipe_in_; + int control_pipe_out_; + + // disable these + CrashGenerationServer(const CrashGenerationServer&); + CrashGenerationServer& operator=(const CrashGenerationServer&); +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_ diff --git a/shared/sentry/external/breakpad/src/client/linux/data/linux-gate-amd.sym b/shared/sentry/external/breakpad/src/client/linux/data/linux-gate-amd.sym new file mode 100644 index 000000000..e042a5ec4 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/data/linux-gate-amd.sym @@ -0,0 +1,3 @@ +MODULE Linux x86 B8CFDE93002D54DA1900A40AA1BD67690 linux-gate.so +PUBLIC 400 0 __kernel_vsyscall +STACK WIN 4 400 100 1 1 0 0 0 0 0 1 diff --git a/shared/sentry/external/breakpad/src/client/linux/data/linux-gate-intel.sym b/shared/sentry/external/breakpad/src/client/linux/data/linux-gate-intel.sym new file mode 100644 index 000000000..c209c2375 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/data/linux-gate-intel.sym @@ -0,0 +1,3 @@ +MODULE Linux x86 4FBDA58B5A1DF5A379E3CF19A235EA090 linux-gate.so +PUBLIC 400 0 __kernel_vsyscall +STACK WIN 4 400 200 3 3 0 0 0 0 0 1 \ No newline at end of file diff --git a/shared/sentry/external/breakpad/src/client/linux/dump_writer_common/mapping_info.h b/shared/sentry/external/breakpad/src/client/linux/dump_writer_common/mapping_info.h new file mode 100644 index 000000000..c09e48ab0 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/dump_writer_common/mapping_info.h @@ -0,0 +1,74 @@ +// 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 CLIENT_LINUX_DUMP_WRITER_COMMON_MAPPING_INFO_H_ +#define CLIENT_LINUX_DUMP_WRITER_COMMON_MAPPING_INFO_H_ + +#include +#include +#include + +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +// One of these is produced for each mapping in the process (i.e. line in +// /proc/$x/maps). +struct MappingInfo { + // On Android, relocation packing can mean that the reported start + // address of the mapping must be adjusted by a bias in order to + // compensate for the compression of the relocation section. The + // following two members hold (after LateInit) the adjusted mapping + // range. See crbug.com/606972 for more information. + uintptr_t start_addr; + size_t size; + // When Android relocation packing causes |start_addr| and |size| to + // be modified with a load bias, we need to remember the unbiased + // address range. The following structure holds the original mapping + // address range as reported by the operating system. + struct { + uintptr_t start_addr; + uintptr_t end_addr; + } system_mapping_info; + size_t offset; // offset into the backed file. + bool exec; // true if the mapping has the execute bit set. + char name[NAME_MAX]; +}; + +struct MappingEntry { + MappingInfo first; + uint8_t second[sizeof(MDGUID)]; +}; + +// A list of +typedef std::list MappingList; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_MAPPING_INFO_H_ diff --git a/shared/sentry/external/breakpad/src/client/linux/dump_writer_common/raw_context_cpu.h b/shared/sentry/external/breakpad/src/client/linux/dump_writer_common/raw_context_cpu.h new file mode 100644 index 000000000..07d9171a0 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/dump_writer_common/raw_context_cpu.h @@ -0,0 +1,53 @@ +// 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 CLIENT_LINUX_DUMP_WRITER_COMMON_RAW_CONTEXT_CPU_H +#define CLIENT_LINUX_DUMP_WRITER_COMMON_RAW_CONTEXT_CPU_H + +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +#if defined(__i386__) +typedef MDRawContextX86 RawContextCPU; +#elif defined(__x86_64) +typedef MDRawContextAMD64 RawContextCPU; +#elif defined(__ARM_EABI__) +typedef MDRawContextARM RawContextCPU; +#elif defined(__aarch64__) +typedef MDRawContextARM64_Old RawContextCPU; +#elif defined(__mips__) +typedef MDRawContextMIPS RawContextCPU; +#else +#error "This code has not been ported to your platform yet." +#endif + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_RAW_CONTEXT_CPU_H diff --git a/shared/sentry/external/breakpad/src/client/linux/dump_writer_common/thread_info.cc b/shared/sentry/external/breakpad/src/client/linux/dump_writer_common/thread_info.cc new file mode 100644 index 000000000..aae1dc13b --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/dump_writer_common/thread_info.cc @@ -0,0 +1,305 @@ +// 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 "client/linux/dump_writer_common/thread_info.h" + +#include +#include + +#include "common/linux/linux_libc_support.h" +#include "google_breakpad/common/minidump_format.h" + +namespace { + +#if defined(__i386__) +// Write a uint16_t to memory +// out: memory location to write to +// v: value to write. +void U16(void* out, uint16_t v) { + my_memcpy(out, &v, sizeof(v)); +} + +// Write a uint32_t to memory +// out: memory location to write to +// v: value to write. +void U32(void* out, uint32_t v) { + my_memcpy(out, &v, sizeof(v)); +} +#endif + +} + +namespace google_breakpad { + +#if defined(__i386__) + +uintptr_t ThreadInfo::GetInstructionPointer() const { + return regs.eip; +} + +void ThreadInfo::FillCPUContext(RawContextCPU* out) const { + out->context_flags = MD_CONTEXT_X86_ALL; + + out->dr0 = dregs[0]; + out->dr1 = dregs[1]; + out->dr2 = dregs[2]; + out->dr3 = dregs[3]; + // 4 and 5 deliberatly omitted because they aren't included in the minidump + // format. + out->dr6 = dregs[6]; + out->dr7 = dregs[7]; + + out->gs = regs.xgs; + out->fs = regs.xfs; + out->es = regs.xes; + out->ds = regs.xds; + + out->edi = regs.edi; + out->esi = regs.esi; + out->ebx = regs.ebx; + out->edx = regs.edx; + out->ecx = regs.ecx; + out->eax = regs.eax; + + out->ebp = regs.ebp; + out->eip = regs.eip; + out->cs = regs.xcs; + out->eflags = regs.eflags; + out->esp = regs.esp; + out->ss = regs.xss; + + out->float_save.control_word = fpregs.cwd; + out->float_save.status_word = fpregs.swd; + out->float_save.tag_word = fpregs.twd; + out->float_save.error_offset = fpregs.fip; + out->float_save.error_selector = fpregs.fcs; + out->float_save.data_offset = fpregs.foo; + out->float_save.data_selector = fpregs.fos; + + // 8 registers * 10 bytes per register. + my_memcpy(out->float_save.register_area, fpregs.st_space, 10 * 8); + + // This matches the Intel fpsave format. + U16(out->extended_registers + 0, fpregs.cwd); + U16(out->extended_registers + 2, fpregs.swd); + U16(out->extended_registers + 4, fpregs.twd); + U16(out->extended_registers + 6, fpxregs.fop); + U32(out->extended_registers + 8, fpxregs.fip); + U16(out->extended_registers + 12, fpxregs.fcs); + U32(out->extended_registers + 16, fpregs.foo); + U16(out->extended_registers + 20, fpregs.fos); + U32(out->extended_registers + 24, fpxregs.mxcsr); + + my_memcpy(out->extended_registers + 32, &fpxregs.st_space, 128); + my_memcpy(out->extended_registers + 160, &fpxregs.xmm_space, 128); +} + +#elif defined(__x86_64) + +uintptr_t ThreadInfo::GetInstructionPointer() const { + return regs.rip; +} + +void ThreadInfo::FillCPUContext(RawContextCPU* out) const { + out->context_flags = MD_CONTEXT_AMD64_FULL | + MD_CONTEXT_AMD64_SEGMENTS; + + out->cs = regs.cs; + + out->ds = regs.ds; + out->es = regs.es; + out->fs = regs.fs; + out->gs = regs.gs; + + out->ss = regs.ss; + out->eflags = regs.eflags; + + out->dr0 = dregs[0]; + out->dr1 = dregs[1]; + out->dr2 = dregs[2]; + out->dr3 = dregs[3]; + // 4 and 5 deliberatly omitted because they aren't included in the minidump + // format. + out->dr6 = dregs[6]; + out->dr7 = dregs[7]; + + out->rax = regs.rax; + out->rcx = regs.rcx; + out->rdx = regs.rdx; + out->rbx = regs.rbx; + + out->rsp = regs.rsp; + + out->rbp = regs.rbp; + out->rsi = regs.rsi; + out->rdi = regs.rdi; + out->r8 = regs.r8; + out->r9 = regs.r9; + out->r10 = regs.r10; + out->r11 = regs.r11; + out->r12 = regs.r12; + out->r13 = regs.r13; + out->r14 = regs.r14; + out->r15 = regs.r15; + + out->rip = regs.rip; + + out->flt_save.control_word = fpregs.cwd; + out->flt_save.status_word = fpregs.swd; + out->flt_save.tag_word = fpregs.ftw; + out->flt_save.error_opcode = fpregs.fop; + out->flt_save.error_offset = fpregs.rip; + out->flt_save.error_selector = 0; // We don't have this. + out->flt_save.data_offset = fpregs.rdp; + out->flt_save.data_selector = 0; // We don't have this. + out->flt_save.mx_csr = fpregs.mxcsr; + out->flt_save.mx_csr_mask = fpregs.mxcr_mask; + + my_memcpy(&out->flt_save.float_registers, &fpregs.st_space, 8 * 16); + my_memcpy(&out->flt_save.xmm_registers, &fpregs.xmm_space, 16 * 16); +} + +#elif defined(__ARM_EABI__) + +uintptr_t ThreadInfo::GetInstructionPointer() const { + return regs.uregs[15]; +} + +void ThreadInfo::FillCPUContext(RawContextCPU* out) const { + out->context_flags = MD_CONTEXT_ARM_FULL; + + for (int i = 0; i < MD_CONTEXT_ARM_GPR_COUNT; ++i) + out->iregs[i] = regs.uregs[i]; + // No CPSR register in ThreadInfo(it's not accessible via ptrace) + out->cpsr = 0; +#if !defined(__ANDROID__) + out->float_save.fpscr = fpregs.fpsr | + (static_cast(fpregs.fpcr) << 32); + // TODO: sort this out, actually collect floating point registers + my_memset(&out->float_save.regs, 0, sizeof(out->float_save.regs)); + my_memset(&out->float_save.extra, 0, sizeof(out->float_save.extra)); +#endif +} + +#elif defined(__aarch64__) + +uintptr_t ThreadInfo::GetInstructionPointer() const { + return regs.pc; +} + +void ThreadInfo::FillCPUContext(RawContextCPU* out) const { + out->context_flags = MD_CONTEXT_ARM64_FULL_OLD; + + out->cpsr = static_cast(regs.pstate); + for (int i = 0; i < MD_CONTEXT_ARM64_REG_SP; ++i) + out->iregs[i] = regs.regs[i]; + out->iregs[MD_CONTEXT_ARM64_REG_SP] = regs.sp; + out->iregs[MD_CONTEXT_ARM64_REG_PC] = regs.pc; + + out->float_save.fpsr = fpregs.fpsr; + out->float_save.fpcr = fpregs.fpcr; + my_memcpy(&out->float_save.regs, &fpregs.vregs, + MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT * 16); +} + +#elif defined(__mips__) + +uintptr_t ThreadInfo::GetInstructionPointer() const { + return mcontext.pc; +} + +void ThreadInfo::FillCPUContext(RawContextCPU* out) const { +#if _MIPS_SIM == _ABI64 + out->context_flags = MD_CONTEXT_MIPS64_FULL; +#elif _MIPS_SIM == _ABIO32 + out->context_flags = MD_CONTEXT_MIPS_FULL; +#else +# error "This mips ABI is currently not supported (n32)" +#endif + + for (int i = 0; i < MD_CONTEXT_MIPS_GPR_COUNT; ++i) + out->iregs[i] = mcontext.gregs[i]; + + out->mdhi = mcontext.mdhi; + out->mdlo = mcontext.mdlo; + out->dsp_control = mcontext.dsp; + + out->hi[0] = mcontext.hi1; + out->lo[0] = mcontext.lo1; + out->hi[1] = mcontext.hi2; + out->lo[1] = mcontext.lo2; + out->hi[2] = mcontext.hi3; + out->lo[2] = mcontext.lo3; + + out->epc = mcontext.pc; + out->badvaddr = 0; // Not stored in mcontext + out->status = 0; // Not stored in mcontext + out->cause = 0; // Not stored in mcontext + + for (int i = 0; i < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT; ++i) + out->float_save.regs[i] = mcontext.fpregs.fp_r.fp_fregs[i]._fp_fregs; + + out->float_save.fpcsr = mcontext.fpc_csr; +#if _MIPS_SIM == _ABIO32 + out->float_save.fir = mcontext.fpc_eir; +#endif +} +#endif // __mips__ + +void ThreadInfo::GetGeneralPurposeRegisters(void** gp_regs, size_t* size) { + assert(gp_regs || size); +#if defined(__mips__) + if (gp_regs) + *gp_regs = mcontext.gregs; + if (size) + *size = sizeof(mcontext.gregs); +#else + if (gp_regs) + *gp_regs = ®s; + if (size) + *size = sizeof(regs); +#endif +} + +void ThreadInfo::GetFloatingPointRegisters(void** fp_regs, size_t* size) { + assert(fp_regs || size); +#if defined(__mips__) + if (fp_regs) + *fp_regs = &mcontext.fpregs; + if (size) + *size = sizeof(mcontext.fpregs); +#else + if (fp_regs) + *fp_regs = &fpregs; + if (size) + *size = sizeof(fpregs); +#endif +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/linux/dump_writer_common/thread_info.h b/shared/sentry/external/breakpad/src/client/linux/dump_writer_common/thread_info.h new file mode 100644 index 000000000..fb216fa6d --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/dump_writer_common/thread_info.h @@ -0,0 +1,91 @@ +// 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 CLIENT_LINUX_DUMP_WRITER_COMMON_THREAD_INFO_H_ +#define CLIENT_LINUX_DUMP_WRITER_COMMON_THREAD_INFO_H_ + +#include +#include + +#include "client/linux/dump_writer_common/raw_context_cpu.h" +#include "common/memory_allocator.h" +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +#if defined(__i386) || defined(__x86_64) +typedef __typeof__(((struct user*) 0)->u_debugreg[0]) debugreg_t; +#endif + +// We produce one of these structures for each thread in the crashed process. +struct ThreadInfo { + pid_t tgid; // thread group id + pid_t ppid; // parent process + + uintptr_t stack_pointer; // thread stack pointer + + +#if defined(__i386) || defined(__x86_64) + user_regs_struct regs; + user_fpregs_struct fpregs; + static const unsigned kNumDebugRegisters = 8; + debugreg_t dregs[8]; +#if defined(__i386) + user_fpxregs_struct fpxregs; +#endif // defined(__i386) + +#elif defined(__ARM_EABI__) + // Mimicking how strace does this(see syscall.c, search for GETREGS) + struct user_regs regs; + struct user_fpregs fpregs; +#elif defined(__aarch64__) + // Use the structures defined in + struct user_regs_struct regs; + struct user_fpsimd_struct fpregs; +#elif defined(__mips__) + // Use the structure defined in . + mcontext_t mcontext; +#endif + + // Returns the instruction pointer (platform-dependent impl.). + uintptr_t GetInstructionPointer() const; + + // Fills a RawContextCPU using the context in the ThreadInfo object. + void FillCPUContext(RawContextCPU* out) const; + + // Returns the pointer and size of general purpose register area. + void GetGeneralPurposeRegisters(void** gp_regs, size_t* size); + + // Returns the pointer and size of float point register area. + void GetFloatingPointRegisters(void** fp_regs, size_t* size); +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_THREAD_INFO_H_ diff --git a/shared/sentry/external/breakpad/src/client/linux/dump_writer_common/ucontext_reader.cc b/shared/sentry/external/breakpad/src/client/linux/dump_writer_common/ucontext_reader.cc new file mode 100644 index 000000000..6eec1be24 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/dump_writer_common/ucontext_reader.cc @@ -0,0 +1,259 @@ +// 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 "client/linux/dump_writer_common/ucontext_reader.h" + +#include "common/linux/linux_libc_support.h" +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +// Minidump defines register structures which are different from the raw +// structures which we get from the kernel. These are platform specific +// functions to juggle the ucontext_t and user structures into minidump format. + +#if defined(__i386__) + +uintptr_t UContextReader::GetStackPointer(const ucontext_t* uc) { + return uc->uc_mcontext.gregs[REG_ESP]; +} + +uintptr_t UContextReader::GetInstructionPointer(const ucontext_t* uc) { + return uc->uc_mcontext.gregs[REG_EIP]; +} + +void UContextReader::FillCPUContext(RawContextCPU* out, const ucontext_t* uc, + const fpstate_t* fp) { + const greg_t* regs = uc->uc_mcontext.gregs; + + out->context_flags = MD_CONTEXT_X86_FULL | + MD_CONTEXT_X86_FLOATING_POINT; + + out->gs = regs[REG_GS]; + out->fs = regs[REG_FS]; + out->es = regs[REG_ES]; + out->ds = regs[REG_DS]; + + out->edi = regs[REG_EDI]; + out->esi = regs[REG_ESI]; + out->ebx = regs[REG_EBX]; + out->edx = regs[REG_EDX]; + out->ecx = regs[REG_ECX]; + out->eax = regs[REG_EAX]; + + out->ebp = regs[REG_EBP]; + out->eip = regs[REG_EIP]; + out->cs = regs[REG_CS]; + out->eflags = regs[REG_EFL]; + out->esp = regs[REG_UESP]; + out->ss = regs[REG_SS]; + + out->float_save.control_word = fp->cw; + out->float_save.status_word = fp->sw; + out->float_save.tag_word = fp->tag; + out->float_save.error_offset = fp->ipoff; + out->float_save.error_selector = fp->cssel; + out->float_save.data_offset = fp->dataoff; + out->float_save.data_selector = fp->datasel; + + // 8 registers * 10 bytes per register. + my_memcpy(out->float_save.register_area, fp->_st, 10 * 8); +} + +#elif defined(__x86_64) + +uintptr_t UContextReader::GetStackPointer(const ucontext_t* uc) { + return uc->uc_mcontext.gregs[REG_RSP]; +} + +uintptr_t UContextReader::GetInstructionPointer(const ucontext_t* uc) { + return uc->uc_mcontext.gregs[REG_RIP]; +} + +void UContextReader::FillCPUContext(RawContextCPU* out, const ucontext_t* uc, + const fpstate_t* fpregs) { + const greg_t* regs = uc->uc_mcontext.gregs; + + out->context_flags = MD_CONTEXT_AMD64_FULL; + + out->cs = regs[REG_CSGSFS] & 0xffff; + + out->fs = (regs[REG_CSGSFS] >> 32) & 0xffff; + out->gs = (regs[REG_CSGSFS] >> 16) & 0xffff; + + out->eflags = regs[REG_EFL]; + + out->rax = regs[REG_RAX]; + out->rcx = regs[REG_RCX]; + out->rdx = regs[REG_RDX]; + out->rbx = regs[REG_RBX]; + + out->rsp = regs[REG_RSP]; + out->rbp = regs[REG_RBP]; + out->rsi = regs[REG_RSI]; + out->rdi = regs[REG_RDI]; + out->r8 = regs[REG_R8]; + out->r9 = regs[REG_R9]; + out->r10 = regs[REG_R10]; + out->r11 = regs[REG_R11]; + out->r12 = regs[REG_R12]; + out->r13 = regs[REG_R13]; + out->r14 = regs[REG_R14]; + out->r15 = regs[REG_R15]; + + out->rip = regs[REG_RIP]; + + out->flt_save.control_word = fpregs->cwd; + out->flt_save.status_word = fpregs->swd; + out->flt_save.tag_word = fpregs->ftw; + out->flt_save.error_opcode = fpregs->fop; + out->flt_save.error_offset = fpregs->rip; + out->flt_save.data_offset = fpregs->rdp; + out->flt_save.error_selector = 0; // We don't have this. + out->flt_save.data_selector = 0; // We don't have this. + out->flt_save.mx_csr = fpregs->mxcsr; + out->flt_save.mx_csr_mask = fpregs->mxcr_mask; + my_memcpy(&out->flt_save.float_registers, &fpregs->_st, 8 * 16); + my_memcpy(&out->flt_save.xmm_registers, &fpregs->_xmm, 16 * 16); +} + +#elif defined(__ARM_EABI__) + +uintptr_t UContextReader::GetStackPointer(const ucontext_t* uc) { + return uc->uc_mcontext.arm_sp; +} + +uintptr_t UContextReader::GetInstructionPointer(const ucontext_t* uc) { + return uc->uc_mcontext.arm_pc; +} + +void UContextReader::FillCPUContext(RawContextCPU* out, const ucontext_t* uc) { + out->context_flags = MD_CONTEXT_ARM_FULL; + + out->iregs[0] = uc->uc_mcontext.arm_r0; + out->iregs[1] = uc->uc_mcontext.arm_r1; + out->iregs[2] = uc->uc_mcontext.arm_r2; + out->iregs[3] = uc->uc_mcontext.arm_r3; + out->iregs[4] = uc->uc_mcontext.arm_r4; + out->iregs[5] = uc->uc_mcontext.arm_r5; + out->iregs[6] = uc->uc_mcontext.arm_r6; + out->iregs[7] = uc->uc_mcontext.arm_r7; + out->iregs[8] = uc->uc_mcontext.arm_r8; + out->iregs[9] = uc->uc_mcontext.arm_r9; + out->iregs[10] = uc->uc_mcontext.arm_r10; + + out->iregs[11] = uc->uc_mcontext.arm_fp; + out->iregs[12] = uc->uc_mcontext.arm_ip; + out->iregs[13] = uc->uc_mcontext.arm_sp; + out->iregs[14] = uc->uc_mcontext.arm_lr; + out->iregs[15] = uc->uc_mcontext.arm_pc; + + out->cpsr = uc->uc_mcontext.arm_cpsr; + + // TODO: fix this after fixing ExceptionHandler + out->float_save.fpscr = 0; + my_memset(&out->float_save.regs, 0, sizeof(out->float_save.regs)); + my_memset(&out->float_save.extra, 0, sizeof(out->float_save.extra)); +} + +#elif defined(__aarch64__) + +uintptr_t UContextReader::GetStackPointer(const ucontext_t* uc) { + return uc->uc_mcontext.sp; +} + +uintptr_t UContextReader::GetInstructionPointer(const ucontext_t* uc) { + return uc->uc_mcontext.pc; +} + +void UContextReader::FillCPUContext(RawContextCPU* out, const ucontext_t* uc, + const struct fpsimd_context* fpregs) { + out->context_flags = MD_CONTEXT_ARM64_FULL_OLD; + + out->cpsr = static_cast(uc->uc_mcontext.pstate); + for (int i = 0; i < MD_CONTEXT_ARM64_REG_SP; ++i) + out->iregs[i] = uc->uc_mcontext.regs[i]; + out->iregs[MD_CONTEXT_ARM64_REG_SP] = uc->uc_mcontext.sp; + out->iregs[MD_CONTEXT_ARM64_REG_PC] = uc->uc_mcontext.pc; + + out->float_save.fpsr = fpregs->fpsr; + out->float_save.fpcr = fpregs->fpcr; + my_memcpy(&out->float_save.regs, &fpregs->vregs, + MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT * 16); +} + +#elif defined(__mips__) + +uintptr_t UContextReader::GetStackPointer(const ucontext_t* uc) { + return uc->uc_mcontext.gregs[MD_CONTEXT_MIPS_REG_SP]; +} + +uintptr_t UContextReader::GetInstructionPointer(const ucontext_t* uc) { + return uc->uc_mcontext.pc; +} + +void UContextReader::FillCPUContext(RawContextCPU* out, const ucontext_t* uc) { +#if _MIPS_SIM == _ABI64 + out->context_flags = MD_CONTEXT_MIPS64_FULL; +#elif _MIPS_SIM == _ABIO32 + out->context_flags = MD_CONTEXT_MIPS_FULL; +#else +#error "This mips ABI is currently not supported (n32)" +#endif + + for (int i = 0; i < MD_CONTEXT_MIPS_GPR_COUNT; ++i) + out->iregs[i] = uc->uc_mcontext.gregs[i]; + + out->mdhi = uc->uc_mcontext.mdhi; + out->mdlo = uc->uc_mcontext.mdlo; + + out->hi[0] = uc->uc_mcontext.hi1; + out->hi[1] = uc->uc_mcontext.hi2; + out->hi[2] = uc->uc_mcontext.hi3; + out->lo[0] = uc->uc_mcontext.lo1; + out->lo[1] = uc->uc_mcontext.lo2; + out->lo[2] = uc->uc_mcontext.lo3; + out->dsp_control = uc->uc_mcontext.dsp; + + out->epc = uc->uc_mcontext.pc; + out->badvaddr = 0; // Not reported in signal context. + out->status = 0; // Not reported in signal context. + out->cause = 0; // Not reported in signal context. + + for (int i = 0; i < MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT; ++i) + out->float_save.regs[i] = uc->uc_mcontext.fpregs.fp_r.fp_dregs[i]; + + out->float_save.fpcsr = uc->uc_mcontext.fpc_csr; +#if _MIPS_SIM == _ABIO32 + out->float_save.fir = uc->uc_mcontext.fpc_eir; // Unused. +#endif +} +#endif + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/linux/dump_writer_common/ucontext_reader.h b/shared/sentry/external/breakpad/src/client/linux/dump_writer_common/ucontext_reader.h new file mode 100644 index 000000000..7d4100881 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/dump_writer_common/ucontext_reader.h @@ -0,0 +1,65 @@ +// 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 CLIENT_LINUX_DUMP_WRITER_COMMON_UCONTEXT_READER_H +#define CLIENT_LINUX_DUMP_WRITER_COMMON_UCONTEXT_READER_H + +#include +#include + +#include "client/linux/dump_writer_common/raw_context_cpu.h" +#include "client/linux/minidump_writer/minidump_writer.h" +#include "common/memory_allocator.h" +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +// Wraps platform-dependent implementations of accessors to ucontext_t structs. +struct UContextReader { + static uintptr_t GetStackPointer(const ucontext_t* uc); + + static uintptr_t GetInstructionPointer(const ucontext_t* uc); + + // Juggle a arch-specific ucontext_t into a minidump format + // out: the minidump structure + // info: the collection of register structures. +#if defined(__i386__) || defined(__x86_64) + static void FillCPUContext(RawContextCPU* out, const ucontext_t* uc, + const fpstate_t* fp); +#elif defined(__aarch64__) + static void FillCPUContext(RawContextCPU* out, const ucontext_t* uc, + const struct fpsimd_context* fpregs); +#else + static void FillCPUContext(RawContextCPU* out, const ucontext_t* uc); +#endif +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_DUMP_WRITER_COMMON_UCONTEXT_READER_H diff --git a/shared/sentry/external/breakpad/src/client/linux/handler/exception_handler.cc b/shared/sentry/external/breakpad/src/client/linux/handler/exception_handler.cc new file mode 100644 index 000000000..499be0a98 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/handler/exception_handler.cc @@ -0,0 +1,797 @@ +// 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. + +// The ExceptionHandler object installs signal handlers for a number of +// signals. We rely on the signal handler running on the thread which crashed +// in order to identify it. This is true of the synchronous signals (SEGV etc), +// but not true of ABRT. Thus, if you send ABRT to yourself in a program which +// uses ExceptionHandler, you need to use tgkill to direct it to the current +// thread. +// +// The signal flow looks like this: +// +// SignalHandler (uses a global stack of ExceptionHandler objects to find +// | one to handle the signal. If the first rejects it, try +// | the second etc...) +// V +// HandleSignal ----------------------------| (clones a new process which +// | | shares an address space with +// (wait for cloned | the crashed process. This +// process) | allows us to ptrace the crashed +// | | process) +// V V +// (set signal handler to ThreadEntry (static function to bounce +// SIG_DFL and rethrow, | back into the object) +// killing the crashed | +// process) V +// DoDump (writes minidump) +// | +// V +// sys_exit +// + +// This code is a little fragmented. Different functions of the ExceptionHandler +// class run in a number of different contexts. Some of them run in a normal +// context and are easy to code, others run in a compromised context and the +// restrictions at the top of minidump_writer.cc apply: no libc and use the +// alternative malloc. Each function should have comment above it detailing the +// context which it runs in. + +#include "client/linux/handler/exception_handler.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#include "common/basictypes.h" +#include "common/linux/breakpad_getcontext.h" +#include "common/linux/linux_libc_support.h" +#include "common/memory_allocator.h" +#include "client/linux/log/log.h" +#include "client/linux/microdump_writer/microdump_writer.h" +#include "client/linux/minidump_writer/linux_dumper.h" +#include "client/linux/minidump_writer/minidump_writer.h" +#include "common/linux/eintr_wrapper.h" +#include "third_party/lss/linux_syscall_support.h" + +#if defined(__ANDROID__) +#include "linux/sched.h" +#endif + +#ifndef PR_SET_PTRACER +#define PR_SET_PTRACER 0x59616d61 +#endif + +namespace google_breakpad { + +namespace { +// The list of signals which we consider to be crashes. The default action for +// all these signals must be Core (see man 7 signal) because we rethrow the +// signal after handling it and expect that it'll be fatal. +const int kExceptionSignals[] = { + SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, SIGTRAP +}; +const int kNumHandledSignals = + sizeof(kExceptionSignals) / sizeof(kExceptionSignals[0]); +struct sigaction old_handlers[kNumHandledSignals]; +bool handlers_installed = false; + +// InstallAlternateStackLocked will store the newly installed stack in new_stack +// and (if it exists) the previously installed stack in old_stack. +stack_t old_stack; +stack_t new_stack; +bool stack_installed = false; + +// Create an alternative stack to run the signal handlers on. This is done since +// the signal might have been caused by a stack overflow. +// Runs before crashing: normal context. +void InstallAlternateStackLocked() { + if (stack_installed) + return; + + memset(&old_stack, 0, sizeof(old_stack)); + memset(&new_stack, 0, sizeof(new_stack)); + + // SIGSTKSZ may be too small to prevent the signal handlers from overrunning + // the alternative stack. Ensure that the size of the alternative stack is + // large enough. + const unsigned kSigStackSize = std::max(16384, SIGSTKSZ); + + // Only set an alternative stack if there isn't already one, or if the current + // one is too small. + if (sys_sigaltstack(NULL, &old_stack) == -1 || !old_stack.ss_sp || + old_stack.ss_size < kSigStackSize) { + new_stack.ss_sp = calloc(1, kSigStackSize); + new_stack.ss_size = kSigStackSize; + + if (sys_sigaltstack(&new_stack, NULL) == -1) { + free(new_stack.ss_sp); + return; + } + stack_installed = true; + } +} + +// Runs before crashing: normal context. +void RestoreAlternateStackLocked() { + if (!stack_installed) + return; + + stack_t current_stack; + if (sys_sigaltstack(NULL, ¤t_stack) == -1) + return; + + // Only restore the old_stack if the current alternative stack is the one + // installed by the call to InstallAlternateStackLocked. + if (current_stack.ss_sp == new_stack.ss_sp) { + if (old_stack.ss_sp) { + if (sys_sigaltstack(&old_stack, NULL) == -1) + return; + } else { + stack_t disable_stack; + disable_stack.ss_flags = SS_DISABLE; + if (sys_sigaltstack(&disable_stack, NULL) == -1) + return; + } + } + + free(new_stack.ss_sp); + stack_installed = false; +} + +void InstallDefaultHandler(int sig) { +#if defined(__ANDROID__) + // Android L+ expose signal and sigaction symbols that override the system + // ones. There is a bug in these functions where a request to set the handler + // to SIG_DFL is ignored. In that case, an infinite loop is entered as the + // signal is repeatedly sent to breakpad's signal handler. + // To work around this, directly call the system's sigaction. + struct kernel_sigaction sa; + memset(&sa, 0, sizeof(sa)); + sys_sigemptyset(&sa.sa_mask); + sa.sa_handler_ = SIG_DFL; + sa.sa_flags = SA_RESTART; + sys_rt_sigaction(sig, &sa, NULL, sizeof(kernel_sigset_t)); +#else + signal(sig, SIG_DFL); +#endif +} + +// The global exception handler stack. This is needed because there may exist +// multiple ExceptionHandler instances in a process. Each will have itself +// registered in this stack. +std::vector* g_handler_stack_ = NULL; +pthread_mutex_t g_handler_stack_mutex_ = PTHREAD_MUTEX_INITIALIZER; + +// sizeof(CrashContext) can be too big w.r.t the size of alternatate stack +// for SignalHandler(). Keep the crash context as a .bss field. Exception +// handlers are serialized by the |g_handler_stack_mutex_| and at most one at a +// time can use |g_crash_context_|. +ExceptionHandler::CrashContext g_crash_context_; + +FirstChanceHandler g_first_chance_handler_ = nullptr; +} // namespace + +// Runs before crashing: normal context. +ExceptionHandler::ExceptionHandler(const MinidumpDescriptor& descriptor, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + bool install_handler, + const int server_fd) + : filter_(filter), + callback_(callback), + callback_context_(callback_context), + minidump_descriptor_(descriptor), + crash_handler_(NULL) { + if (server_fd >= 0) + crash_generation_client_.reset(CrashGenerationClient::TryCreate(server_fd)); + + if (!IsOutOfProcess() && !minidump_descriptor_.IsFD() && + !minidump_descriptor_.IsMicrodumpOnConsole()) + minidump_descriptor_.UpdatePath(); + +#if defined(__ANDROID__) + if (minidump_descriptor_.IsMicrodumpOnConsole()) + logger::initializeCrashLogWriter(); +#endif + + pthread_mutex_lock(&g_handler_stack_mutex_); + + // Pre-fault the crash context struct. This is to avoid failing due to OOM + // if handling an exception when the process ran out of virtual memory. + memset(&g_crash_context_, 0, sizeof(g_crash_context_)); + + if (!g_handler_stack_) + g_handler_stack_ = new std::vector; + if (install_handler) { + InstallAlternateStackLocked(); + InstallHandlersLocked(); + } + g_handler_stack_->push_back(this); + pthread_mutex_unlock(&g_handler_stack_mutex_); +} + +// Runs before crashing: normal context. +ExceptionHandler::~ExceptionHandler() { + pthread_mutex_lock(&g_handler_stack_mutex_); + std::vector::iterator handler = + std::find(g_handler_stack_->begin(), g_handler_stack_->end(), this); + g_handler_stack_->erase(handler); + if (g_handler_stack_->empty()) { + delete g_handler_stack_; + g_handler_stack_ = NULL; + RestoreAlternateStackLocked(); + RestoreHandlersLocked(); + } + pthread_mutex_unlock(&g_handler_stack_mutex_); +} + +// Runs before crashing: normal context. +// static +bool ExceptionHandler::InstallHandlersLocked() { + if (handlers_installed) + return false; + + // Fail if unable to store all the old handlers. + for (int i = 0; i < kNumHandledSignals; ++i) { + if (sigaction(kExceptionSignals[i], NULL, &old_handlers[i]) == -1) + return false; + } + + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + + // Mask all exception signals when we're handling one of them. + for (int i = 0; i < kNumHandledSignals; ++i) + sigaddset(&sa.sa_mask, kExceptionSignals[i]); + + sa.sa_sigaction = SignalHandler; + sa.sa_flags = SA_ONSTACK | SA_SIGINFO; + + for (int i = 0; i < kNumHandledSignals; ++i) { + if (sigaction(kExceptionSignals[i], &sa, NULL) == -1) { + // At this point it is impractical to back out changes, and so failure to + // install a signal is intentionally ignored. + } + } + handlers_installed = true; + return true; +} + +// This function runs in a compromised context: see the top of the file. +// Runs on the crashing thread. +// static +void ExceptionHandler::RestoreHandlersLocked() { + if (!handlers_installed) + return; + + for (int i = 0; i < kNumHandledSignals; ++i) { + if (sigaction(kExceptionSignals[i], &old_handlers[i], NULL) == -1) { + InstallDefaultHandler(kExceptionSignals[i]); + } + } + handlers_installed = false; +} + +// void ExceptionHandler::set_crash_handler(HandlerCallback callback) { +// crash_handler_ = callback; +// } + +// This function runs in a compromised context: see the top of the file. +// Runs on the crashing thread. +// static +void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) { + + // Give the first chance handler a chance to recover from this signal + // + // This is primarily used by V8. V8 uses guard regions to guarantee memory + // safety in WebAssembly. This means some signals might be expected if they + // originate from Wasm code while accessing the guard region. We give V8 the + // chance to handle and recover from these signals first. + if (g_first_chance_handler_ != nullptr && + g_first_chance_handler_(sig, info, uc)) { + return; + } + + // All the exception signals are blocked at this point. + pthread_mutex_lock(&g_handler_stack_mutex_); + + // Sometimes, Breakpad runs inside a process where some other buggy code + // saves and restores signal handlers temporarily with 'signal' + // instead of 'sigaction'. This loses the SA_SIGINFO flag associated + // with this function. As a consequence, the values of 'info' and 'uc' + // become totally bogus, generally inducing a crash. + // + // The following code tries to detect this case. When it does, it + // resets the signal handlers with sigaction + SA_SIGINFO and returns. + // This forces the signal to be thrown again, but this time the kernel + // will call the function with the right arguments. + struct sigaction cur_handler; + if (sigaction(sig, NULL, &cur_handler) == 0 && + cur_handler.sa_sigaction == SignalHandler && + (cur_handler.sa_flags & SA_SIGINFO) == 0) { + // Reset signal handler with the right flags. + sigemptyset(&cur_handler.sa_mask); + sigaddset(&cur_handler.sa_mask, sig); + + cur_handler.sa_sigaction = SignalHandler; + cur_handler.sa_flags = SA_ONSTACK | SA_SIGINFO; + + if (sigaction(sig, &cur_handler, NULL) == -1) { + // When resetting the handler fails, try to reset the + // default one to avoid an infinite loop here. + InstallDefaultHandler(sig); + } + pthread_mutex_unlock(&g_handler_stack_mutex_); + return; + } + + bool handled = false; + for (int i = g_handler_stack_->size() - 1; !handled && i >= 0; --i) { + handled = (*g_handler_stack_)[i]->HandleSignal(sig, info, uc); + } + + // Upon returning from this signal handler, sig will become unmasked and then + // it will be retriggered. If one of the ExceptionHandlers handled it + // successfully, restore the default handler. Otherwise, restore the + // previously installed handler. Then, when the signal is retriggered, it will + // be delivered to the appropriate handler. + if (handled) { + InstallDefaultHandler(sig); + } else { + RestoreHandlersLocked(); + } + + pthread_mutex_unlock(&g_handler_stack_mutex_); + + // info->si_code <= 0 iff SI_FROMUSER (SI_FROMKERNEL otherwise). + if (info->si_code <= 0 || sig == SIGABRT) { + // This signal was triggered by somebody sending us the signal with kill(). + // In order to retrigger it, we have to queue a new signal by calling + // kill() ourselves. The special case (si_pid == 0 && sig == SIGABRT) is + // due to the kernel sending a SIGABRT from a user request via SysRQ. + if (sys_tgkill(getpid(), syscall(__NR_gettid), sig) < 0) { + // If we failed to kill ourselves (e.g. because a sandbox disallows us + // to do so), we instead resort to terminating our process. This will + // result in an incorrect exit code. + _exit(1); + } + } else { + // This was a synchronous signal triggered by a hard fault (e.g. SIGSEGV). + // No need to reissue the signal. It will automatically trigger again, + // when we return from the signal handler. + } +} + +struct ThreadArgument { + pid_t pid; // the crashing process + const MinidumpDescriptor* minidump_descriptor; + ExceptionHandler* handler; + const void* context; // a CrashContext structure + size_t context_size; +}; + +// This is the entry function for the cloned process. We are in a compromised +// context here: see the top of the file. +// static +int ExceptionHandler::ThreadEntry(void* arg) { + const ThreadArgument* thread_arg = reinterpret_cast(arg); + + // Close the write end of the pipe. This allows us to fail if the parent dies + // while waiting for the continue signal. + sys_close(thread_arg->handler->fdes[1]); + + // Block here until the crashing process unblocks us when + // we're allowed to use ptrace + thread_arg->handler->WaitForContinueSignal(); + sys_close(thread_arg->handler->fdes[0]); + + return thread_arg->handler->DoDump(thread_arg->pid, thread_arg->context, + thread_arg->context_size) == false; +} + +// This function runs in a compromised context: see the top of the file. +// Runs on the crashing thread. +bool ExceptionHandler::HandleSignal(int /*sig*/, siginfo_t* info, void* uc) { + if (filter_ && !filter_(callback_context_)) + return false; + + // Allow ourselves to be dumped if the signal is trusted. + bool signal_trusted = info->si_code > 0; + bool signal_pid_trusted = info->si_code == SI_USER || + info->si_code == SI_TKILL; + if (signal_trusted || (signal_pid_trusted && info->si_pid == getpid())) { + sys_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); + } + + // Fill in all the holes in the struct to make Valgrind happy. + memset(&g_crash_context_, 0, sizeof(g_crash_context_)); + memcpy(&g_crash_context_.siginfo, info, sizeof(siginfo_t)); + memcpy(&g_crash_context_.context, uc, sizeof(ucontext_t)); +#if defined(__aarch64__) + ucontext_t* uc_ptr = (ucontext_t*)uc; + struct fpsimd_context* fp_ptr = + (struct fpsimd_context*)&uc_ptr->uc_mcontext.__reserved; + if (fp_ptr->head.magic == FPSIMD_MAGIC) { + memcpy(&g_crash_context_.float_state, fp_ptr, + sizeof(g_crash_context_.float_state)); + } +#elif !defined(__ARM_EABI__) && !defined(__mips__) + // FP state is not part of user ABI on ARM Linux. + // In case of MIPS Linux FP state is already part of ucontext_t + // and 'float_state' is not a member of CrashContext. + ucontext_t* uc_ptr = (ucontext_t*)uc; + if (uc_ptr->uc_mcontext.fpregs) { + memcpy(&g_crash_context_.float_state, uc_ptr->uc_mcontext.fpregs, + sizeof(g_crash_context_.float_state)); + } +#endif + g_crash_context_.tid = syscall(__NR_gettid); + if (crash_handler_ != NULL) { + if (crash_handler_(&g_crash_context_, sizeof(g_crash_context_), + callback_context_)) { + return true; + } + } + return GenerateDump(&g_crash_context_); +} + +// This is a public interface to HandleSignal that allows the client to +// generate a crash dump. This function may run in a compromised context. +bool ExceptionHandler::SimulateSignalDelivery(int sig) { + siginfo_t siginfo = {}; + // Mimic a trusted signal to allow tracing the process (see + // ExceptionHandler::HandleSignal(). + siginfo.si_code = SI_USER; + siginfo.si_pid = getpid(); + ucontext_t context; + getcontext(&context); + return HandleSignal(sig, &siginfo, &context); +} + +// This function may run in a compromised context: see the top of the file. +bool ExceptionHandler::GenerateDump(CrashContext* context) { + if (IsOutOfProcess()) + return crash_generation_client_->RequestDump(context, sizeof(*context)); + + // Allocating too much stack isn't a problem, and better to err on the side + // of caution than smash it into random locations. + static const unsigned kChildStackSize = 16000; + PageAllocator allocator; + uint8_t* stack = reinterpret_cast(allocator.Alloc(kChildStackSize)); + if (!stack) + return false; + // clone() needs the top-most address. (scrub just to be safe) + stack += kChildStackSize; + my_memset(stack - 16, 0, 16); + + ThreadArgument thread_arg; + thread_arg.handler = this; + thread_arg.minidump_descriptor = &minidump_descriptor_; + thread_arg.pid = getpid(); + thread_arg.context = context; + thread_arg.context_size = sizeof(*context); + + // We need to explicitly enable ptrace of parent processes on some + // kernels, but we need to know the PID of the cloned process before we + // can do this. Create a pipe here which we can use to block the + // cloned process after creating it, until we have explicitly enabled ptrace + if (sys_pipe(fdes) == -1) { + // Creating the pipe failed. We'll log an error but carry on anyway, + // as we'll probably still get a useful crash report. All that will happen + // is the write() and read() calls will fail with EBADF + static const char no_pipe_msg[] = "ExceptionHandler::GenerateDump " + "sys_pipe failed:"; + logger::write(no_pipe_msg, sizeof(no_pipe_msg) - 1); + logger::write(strerror(errno), strlen(strerror(errno))); + logger::write("\n", 1); + + // Ensure fdes[0] and fdes[1] are invalid file descriptors. + fdes[0] = fdes[1] = -1; + } + + const pid_t child = sys_clone( + ThreadEntry, stack, CLONE_FS | CLONE_UNTRACED, &thread_arg, NULL, NULL, + NULL); + if (child == -1) { + sys_close(fdes[0]); + sys_close(fdes[1]); + return false; + } + + // Close the read end of the pipe. + sys_close(fdes[0]); + // Allow the child to ptrace us + sys_prctl(PR_SET_PTRACER, child, 0, 0, 0); + SendContinueSignalToChild(); + int status = 0; + const int r = HANDLE_EINTR(sys_waitpid(child, &status, __WALL)); + + sys_close(fdes[1]); + + if (r == -1) { + static const char msg[] = "ExceptionHandler::GenerateDump waitpid failed:"; + logger::write(msg, sizeof(msg) - 1); + logger::write(strerror(errno), strlen(strerror(errno))); + logger::write("\n", 1); + } + + bool success = r != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0; + if (callback_) + success = callback_(minidump_descriptor_, callback_context_, success); + return success; +} + +// This function runs in a compromised context: see the top of the file. +void ExceptionHandler::SendContinueSignalToChild() { + static const char okToContinueMessage = 'a'; + int r; + r = HANDLE_EINTR(sys_write(fdes[1], &okToContinueMessage, sizeof(char))); + if (r == -1) { + static const char msg[] = "ExceptionHandler::SendContinueSignalToChild " + "sys_write failed:"; + logger::write(msg, sizeof(msg) - 1); + logger::write(strerror(errno), strlen(strerror(errno))); + logger::write("\n", 1); + } +} + +// This function runs in a compromised context: see the top of the file. +// Runs on the cloned process. +void ExceptionHandler::WaitForContinueSignal() { + int r; + char receivedMessage; + r = HANDLE_EINTR(sys_read(fdes[0], &receivedMessage, sizeof(char))); + if (r == -1) { + static const char msg[] = "ExceptionHandler::WaitForContinueSignal " + "sys_read failed:"; + logger::write(msg, sizeof(msg) - 1); + logger::write(strerror(errno), strlen(strerror(errno))); + logger::write("\n", 1); + } +} + +// This function runs in a compromised context: see the top of the file. +// Runs on the cloned process. +bool ExceptionHandler::DoDump(pid_t crashing_process, const void* context, + size_t context_size) { + const bool may_skip_dump = + minidump_descriptor_.skip_dump_if_principal_mapping_not_referenced(); + const uintptr_t principal_mapping_address = + minidump_descriptor_.address_within_principal_mapping(); + const bool sanitize_stacks = minidump_descriptor_.sanitize_stacks(); + if (minidump_descriptor_.IsMicrodumpOnConsole()) { + return google_breakpad::WriteMicrodump( + crashing_process, + context, + context_size, + mapping_list_, + may_skip_dump, + principal_mapping_address, + sanitize_stacks, + *minidump_descriptor_.microdump_extra_info()); + } + if (minidump_descriptor_.IsFD()) { + return google_breakpad::WriteMinidump(minidump_descriptor_.fd(), + minidump_descriptor_.size_limit(), + crashing_process, + context, + context_size, + mapping_list_, + app_memory_list_, + may_skip_dump, + principal_mapping_address, + sanitize_stacks); + } + return google_breakpad::WriteMinidump(minidump_descriptor_.path(), + minidump_descriptor_.size_limit(), + crashing_process, + context, + context_size, + mapping_list_, + app_memory_list_, + may_skip_dump, + principal_mapping_address, + sanitize_stacks); +} + +// static +bool ExceptionHandler::WriteMinidump(const string& dump_path, + MinidumpCallback callback, + void* callback_context) { + MinidumpDescriptor descriptor(dump_path); + ExceptionHandler eh(descriptor, NULL, callback, callback_context, false, -1); + return eh.WriteMinidump(); +} + +// In order to making using EBP to calculate the desired value for ESP +// a valid operation, ensure that this function is compiled with a +// frame pointer using the following attribute. This attribute +// is supported on GCC but not on clang. +#if defined(__i386__) && defined(__GNUC__) && !defined(__clang__) +__attribute__((optimize("no-omit-frame-pointer"))) +#endif +bool ExceptionHandler::WriteMinidump() { + if (!IsOutOfProcess() && !minidump_descriptor_.IsFD() && + !minidump_descriptor_.IsMicrodumpOnConsole()) { + // Update the path of the minidump so that this can be called multiple times + // and new files are created for each minidump. This is done before the + // generation happens, as clients may want to access the MinidumpDescriptor + // after this call to find the exact path to the minidump file. + minidump_descriptor_.UpdatePath(); + } else if (minidump_descriptor_.IsFD()) { + // Reposition the FD to its beginning and resize it to get rid of the + // previous minidump info. + lseek(minidump_descriptor_.fd(), 0, SEEK_SET); + ignore_result(ftruncate(minidump_descriptor_.fd(), 0)); + } + + // Allow this process to be dumped. + sys_prctl(PR_SET_DUMPABLE, 1, 0, 0, 0); + + CrashContext context; + int getcontext_result = getcontext(&context.context); + if (getcontext_result) + return false; + +#if defined(__i386__) + // In CPUFillFromUContext in minidumpwriter.cc the stack pointer is retrieved + // from REG_UESP instead of from REG_ESP. REG_UESP is the user stack pointer + // and it only makes sense when running in kernel mode with a different stack + // pointer. When WriteMiniDump is called during normal processing REG_UESP is + // zero which leads to bad minidump files. + if (!context.context.uc_mcontext.gregs[REG_UESP]) { + // If REG_UESP is set to REG_ESP then that includes the stack space for the + // CrashContext object in this function, which is about 128 KB. Since the + // Linux dumper only records 32 KB of stack this would mean that nothing + // useful would be recorded. A better option is to set REG_UESP to REG_EBP, + // perhaps with a small negative offset in case there is any code that + // objects to them being equal. + context.context.uc_mcontext.gregs[REG_UESP] = + context.context.uc_mcontext.gregs[REG_EBP] - 16; + // The stack saving is based off of REG_ESP so it must be set to match the + // new REG_UESP. + context.context.uc_mcontext.gregs[REG_ESP] = + context.context.uc_mcontext.gregs[REG_UESP]; + } +#endif + +#if !defined(__ARM_EABI__) && !defined(__aarch64__) && !defined(__mips__) + // FPU state is not part of ARM EABI ucontext_t. + memcpy(&context.float_state, context.context.uc_mcontext.fpregs, + sizeof(context.float_state)); +#endif + context.tid = sys_gettid(); + + // Add an exception stream to the minidump for better reporting. + memset(&context.siginfo, 0, sizeof(context.siginfo)); + context.siginfo.si_signo = MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED; +#if defined(__i386__) + context.siginfo.si_addr = + reinterpret_cast(context.context.uc_mcontext.gregs[REG_EIP]); +#elif defined(__x86_64__) + context.siginfo.si_addr = + reinterpret_cast(context.context.uc_mcontext.gregs[REG_RIP]); +#elif defined(__arm__) + context.siginfo.si_addr = + reinterpret_cast(context.context.uc_mcontext.arm_pc); +#elif defined(__aarch64__) + context.siginfo.si_addr = + reinterpret_cast(context.context.uc_mcontext.pc); +#elif defined(__mips__) + context.siginfo.si_addr = + reinterpret_cast(context.context.uc_mcontext.pc); +#else +#error "This code has not been ported to your platform yet." +#endif + + return GenerateDump(&context); +} + +void ExceptionHandler::AddMappingInfo(const string& name, + const uint8_t identifier[sizeof(MDGUID)], + uintptr_t start_address, + size_t mapping_size, + size_t file_offset) { + MappingInfo info; + info.start_addr = start_address; + info.size = mapping_size; + info.offset = file_offset; + strncpy(info.name, name.c_str(), sizeof(info.name) - 1); + info.name[sizeof(info.name) - 1] = '\0'; + + MappingEntry mapping; + mapping.first = info; + memcpy(mapping.second, identifier, sizeof(MDGUID)); + mapping_list_.push_back(mapping); +} + +void ExceptionHandler::RegisterAppMemory(void* ptr, size_t length) { + AppMemoryList::iterator iter = + std::find(app_memory_list_.begin(), app_memory_list_.end(), ptr); + if (iter != app_memory_list_.end()) { + // Don't allow registering the same pointer twice. + return; + } + + AppMemory app_memory; + app_memory.ptr = ptr; + app_memory.length = length; + app_memory_list_.push_back(app_memory); +} + +void ExceptionHandler::UnregisterAppMemory(void* ptr) { + AppMemoryList::iterator iter = + std::find(app_memory_list_.begin(), app_memory_list_.end(), ptr); + if (iter != app_memory_list_.end()) { + app_memory_list_.erase(iter); + } +} + +// static +bool ExceptionHandler::WriteMinidumpForChild(pid_t child, + pid_t child_blamed_thread, + const string& dump_path, + MinidumpCallback callback, + void* callback_context) { + // This function is not run in a compromised context. + MinidumpDescriptor descriptor(dump_path); + descriptor.UpdatePath(); + if (!google_breakpad::WriteMinidump(descriptor.path(), + child, + child_blamed_thread)) + return false; + + return callback ? callback(descriptor, callback_context, true) : true; +} + +void SetFirstChanceExceptionHandler(FirstChanceHandler callback) { + g_first_chance_handler_ = callback; +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/linux/handler/exception_handler.h b/shared/sentry/external/breakpad/src/client/linux/handler/exception_handler.h new file mode 100644 index 000000000..f80843ea7 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/handler/exception_handler.h @@ -0,0 +1,281 @@ +// 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 CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_ +#define CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_ + +#include +#include +#include +#include + +#include + +#include "client/linux/crash_generation/crash_generation_client.h" +#include "client/linux/handler/minidump_descriptor.h" +#include "client/linux/minidump_writer/minidump_writer.h" +#include "common/scoped_ptr.h" +#include "common/using_std_string.h" +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +// ExceptionHandler +// +// ExceptionHandler can write a minidump file when an exception occurs, +// or when WriteMinidump() is called explicitly by your program. +// +// To have the exception handler write minidumps when an uncaught exception +// (crash) occurs, you should create an instance early in the execution +// of your program, and keep it around for the entire time you want to +// have crash handling active (typically, until shutdown). +// (NOTE): There should be only be one this kind of exception handler +// object per process. +// +// If you want to write minidumps without installing the exception handler, +// you can create an ExceptionHandler with install_handler set to false, +// then call WriteMinidump. You can also use this technique if you want to +// use different minidump callbacks for different call sites. +// +// In either case, a callback function is called when a minidump is written, +// which receives the full path or file descriptor of the minidump. The +// caller can collect and write additional application state to that minidump, +// and launch an external crash-reporting application. +// +// Caller should try to make the callbacks as crash-friendly as possible, +// it should avoid use heap memory allocation as much as possible. + +class ExceptionHandler { + public: + // A callback function to run before Breakpad performs any substantial + // processing of an exception. A FilterCallback is called before writing + // a minidump. |context| is the parameter supplied by the user as + // callback_context when the handler was created. + // + // If a FilterCallback returns true, Breakpad will continue processing, + // attempting to write a minidump. If a FilterCallback returns false, + // Breakpad will immediately report the exception as unhandled without + // writing a minidump, allowing another handler the opportunity to handle it. + typedef bool (*FilterCallback)(void* context); + + // A callback function to run after the minidump has been written. + // |descriptor| contains the file descriptor or file path containing the + // minidump. |context| is the parameter supplied by the user as + // callback_context when the handler was created. |succeeded| indicates + // whether a minidump file was successfully written. + // + // If an exception occurred and the callback returns true, Breakpad will + // treat the exception as fully-handled, suppressing any other handlers from + // being notified of the exception. If the callback returns false, Breakpad + // will treat the exception as unhandled, and allow another handler to handle + // it. If there are no other handlers, Breakpad will report the exception to + // the system as unhandled, allowing a debugger or native crash dialog the + // opportunity to handle the exception. Most callback implementations + // should normally return the value of |succeeded|, or when they wish to + // not report an exception of handled, false. Callbacks will rarely want to + // return true directly (unless |succeeded| is true). + typedef bool (*MinidumpCallback)(const MinidumpDescriptor& descriptor, + void* context, + bool succeeded); + + // In certain cases, a user may wish to handle the generation of the minidump + // themselves. In this case, they can install a handler callback which is + // called when a crash has occurred. If this function returns true, no other + // processing of occurs and the process will shortly be crashed. If this + // returns false, the normal processing continues. + typedef bool (*HandlerCallback)(const void* crash_context, + size_t crash_context_size, + void* context); + + // Creates a new ExceptionHandler instance to handle writing minidumps. + // Before writing a minidump, the optional |filter| callback will be called. + // Its return value determines whether or not Breakpad should write a + // minidump. The minidump content will be written to the file path or file + // descriptor from |descriptor|, and the optional |callback| is called after + // writing the dump file, as described above. + // If install_handler is true, then a minidump will be written whenever + // an unhandled exception occurs. If it is false, minidumps will only + // be written when WriteMinidump is called. + // If |server_fd| is valid, the minidump is generated out-of-process. If it + // is -1, in-process generation will always be used. + ExceptionHandler(const MinidumpDescriptor& descriptor, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + bool install_handler, + const int server_fd); + ~ExceptionHandler(); + + const MinidumpDescriptor& minidump_descriptor() const { + return minidump_descriptor_; + } + + void set_minidump_descriptor(const MinidumpDescriptor& descriptor) { + minidump_descriptor_ = descriptor; + } + + void set_crash_handler(HandlerCallback callback) { + crash_handler_ = callback; + } + + void set_crash_generation_client(CrashGenerationClient* client) { + crash_generation_client_.reset(client); + } + + // Writes a minidump immediately. This can be used to capture the execution + // state independently of a crash. + // Returns true on success. + // If the ExceptionHandler has been created with a path, a new file is + // generated for each minidump. The file path can be retrieved in the + // MinidumpDescriptor passed to the MinidumpCallback or by accessing the + // MinidumpDescriptor directly from the ExceptionHandler (with + // minidump_descriptor()). + // If the ExceptionHandler has been created with a file descriptor, the file + // descriptor is repositioned to its beginning and the previous generated + // minidump is overwritten. + // Note that this method is not supposed to be called from a compromised + // context as it uses the heap. + bool WriteMinidump(); + + // Convenience form of WriteMinidump which does not require an + // ExceptionHandler instance. + static bool WriteMinidump(const string& dump_path, + MinidumpCallback callback, + void* callback_context); + + // Write a minidump of |child| immediately. This can be used to + // capture the execution state of |child| independently of a crash. + // Pass a meaningful |child_blamed_thread| to make that thread in + // the child process the one from which a crash signature is + // extracted. + // + // WARNING: the return of this function *must* happen before + // the code that will eventually reap |child| executes. + // Otherwise there's a pernicious race condition in which |child| + // exits, is reaped, another process created with its pid, then that + // new process dumped. + static bool WriteMinidumpForChild(pid_t child, + pid_t child_blamed_thread, + const string& dump_path, + MinidumpCallback callback, + void* callback_context); + + // This structure is passed to minidump_writer.h:WriteMinidump via an opaque + // blob. It shouldn't be needed in any user code. + struct CrashContext { + siginfo_t siginfo; + pid_t tid; // the crashing thread. + ucontext_t context; +#if !defined(__ARM_EABI__) && !defined(__mips__) + // #ifdef this out because FP state is not part of user ABI for Linux ARM. + // In case of MIPS Linux FP state is already part of ucontext_t so + // 'float_state' is not required. + fpstate_t float_state; +#endif + }; + + // Returns whether out-of-process dump generation is used or not. + bool IsOutOfProcess() const { + return crash_generation_client_.get() != NULL; + } + + // Add information about a memory mapping. This can be used if + // a custom library loader is used that maps things in a way + // that the linux dumper can't handle by reading the maps file. + void AddMappingInfo(const string& name, + const uint8_t identifier[sizeof(MDGUID)], + uintptr_t start_address, + size_t mapping_size, + size_t file_offset); + + // Register a block of memory of length bytes starting at address ptr + // to be copied to the minidump when a crash happens. + void RegisterAppMemory(void* ptr, size_t length); + + // Unregister a block of memory that was registered with RegisterAppMemory. + void UnregisterAppMemory(void* ptr); + + // Force signal handling for the specified signal. + bool SimulateSignalDelivery(int sig); + + // Report a crash signal from an SA_SIGINFO signal handler. + bool HandleSignal(int sig, siginfo_t* info, void* uc); + + private: + // Save the old signal handlers and install new ones. + static bool InstallHandlersLocked(); + // Restore the old signal handlers. + static void RestoreHandlersLocked(); + + void PreresolveSymbols(); + bool GenerateDump(CrashContext* context); + void SendContinueSignalToChild(); + void WaitForContinueSignal(); + + static void SignalHandler(int sig, siginfo_t* info, void* uc); + static int ThreadEntry(void* arg); + bool DoDump(pid_t crashing_process, const void* context, + size_t context_size); + + const FilterCallback filter_; + const MinidumpCallback callback_; + void* const callback_context_; + + scoped_ptr crash_generation_client_; + + MinidumpDescriptor minidump_descriptor_; + + // Must be volatile. The compiler is unaware of the code which runs in + // the signal handler which reads this variable. Without volatile the + // compiler is free to optimise away writes to this variable which it + // believes are never read. + volatile HandlerCallback crash_handler_; + + // We need to explicitly enable ptrace of parent processes on some + // kernels, but we need to know the PID of the cloned process before we + // can do this. We create a pipe which we can use to block the + // cloned process after creating it, until we have explicitly enabled + // ptrace. This is used to store the file descriptors for the pipe + int fdes[2] = {-1, -1}; + + // Callers can add extra info about mappings for cases where the + // dumper code cannot extract enough information from /proc//maps. + MappingList mapping_list_; + + // Callers can request additional memory regions to be included in + // the dump. + AppMemoryList app_memory_list_; +}; + +typedef bool (*FirstChanceHandler)(int, siginfo_t*, void*); +void SetFirstChanceExceptionHandler(FirstChanceHandler callback); + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_HANDLER_EXCEPTION_HANDLER_H_ diff --git a/shared/sentry/external/breakpad/src/client/linux/handler/exception_handler_unittest.cc b/shared/sentry/external/breakpad/src/client/linux/handler/exception_handler_unittest.cc new file mode 100644 index 000000000..35dcbfd4d --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/handler/exception_handler_unittest.cc @@ -0,0 +1,1287 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#if defined(__mips__) +#include +#endif + +#include + +#include "breakpad_googletest_includes.h" +#include "client/linux/handler/exception_handler.h" +#include "client/linux/minidump_writer/minidump_writer.h" +#include "common/linux/eintr_wrapper.h" +#include "common/linux/ignore_ret.h" +#include "common/linux/linux_libc_support.h" +#include "common/tests/auto_tempdir.h" +#include "common/using_std_string.h" +#include "third_party/lss/linux_syscall_support.h" +#include "google_breakpad/processor/minidump.h" + +using namespace google_breakpad; + +namespace { + +// Flush the instruction cache for a given memory range. +// Only required on ARM and mips. +void FlushInstructionCache(const char* memory, uint32_t memory_size) { +#if defined(__arm__) + long begin = reinterpret_cast(memory); + long end = begin + static_cast(memory_size); +# if defined(__ANDROID__) + // Provided by Android's + cacheflush(begin, end, 0); +# elif defined(__linux__) + // GLibc/ARM doesn't provide a wrapper for it, do a direct syscall. +# ifndef __ARM_NR_cacheflush +# define __ARM_NR_cacheflush 0xf0002 +# endif + syscall(__ARM_NR_cacheflush, begin, end, 0); +# else +# error "Your operating system is not supported yet" +# endif +#elif defined(__mips__) +# if defined(__ANDROID__) + // Provided by Android's + long begin = reinterpret_cast(memory); + long end = begin + static_cast(memory_size); +#if _MIPS_SIM == _ABIO32 + cacheflush(begin, end, 0); +#else + syscall(__NR_cacheflush, begin, end, ICACHE); +#endif +# elif defined(__linux__) + // See http://www.linux-mips.org/wiki/Cacheflush_Syscall. + cacheflush(const_cast(memory), memory_size, ICACHE); +# else +# error "Your operating system is not supported yet" +# endif +#endif +} + +void sigchld_handler(int signo) { } + +int CreateTMPFile(const string& dir, string* path) { + string file = dir + "/exception-handler-unittest.XXXXXX"; + const char* c_file = file.c_str(); + // Copy that string, mkstemp needs a C string it can modify. + char* c_path = strdup(c_file); + const int fd = mkstemp(c_path); + if (fd >= 0) + *path = c_path; + free(c_path); + return fd; +} + +class ExceptionHandlerTest : public ::testing::Test { + protected: + void SetUp() { + // We need to be able to wait for children, so SIGCHLD cannot be SIG_IGN. + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = sigchld_handler; + ASSERT_NE(sigaction(SIGCHLD, &sa, &old_action), -1); + } + + void TearDown() { + sigaction(SIGCHLD, &old_action, NULL); + } + + struct sigaction old_action; +}; + + +void WaitForProcessToTerminate(pid_t process_id, int expected_status) { + int status; + ASSERT_NE(HANDLE_EINTR(waitpid(process_id, &status, 0)), -1); + ASSERT_TRUE(WIFSIGNALED(status)); + ASSERT_EQ(expected_status, WTERMSIG(status)); +} + +// Reads the minidump path sent over the pipe |fd| and sets it in |path|. +void ReadMinidumpPathFromPipe(int fd, string* path) { + struct pollfd pfd; + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fd; + pfd.events = POLLIN | POLLERR; + + const int r = HANDLE_EINTR(poll(&pfd, 1, 0)); + ASSERT_EQ(1, r); + ASSERT_TRUE(pfd.revents & POLLIN); + + int32_t len; + ASSERT_EQ(static_cast(sizeof(len)), read(fd, &len, sizeof(len))); + ASSERT_LT(len, 2048); + char* filename = static_cast(malloc(len + 1)); + ASSERT_EQ(len, read(fd, filename, len)); + filename[len] = 0; + close(fd); + *path = filename; + free(filename); +} + +} // namespace + +TEST(ExceptionHandlerTest, SimpleWithPath) { + AutoTempDir temp_dir; + ExceptionHandler handler( + MinidumpDescriptor(temp_dir.path()), NULL, NULL, NULL, true, -1); + EXPECT_EQ(temp_dir.path(), handler.minidump_descriptor().directory()); + string temp_subdir = temp_dir.path() + "/subdir"; + handler.set_minidump_descriptor(MinidumpDescriptor(temp_subdir)); + EXPECT_EQ(temp_subdir, handler.minidump_descriptor().directory()); +} + +TEST(ExceptionHandlerTest, SimpleWithFD) { + AutoTempDir temp_dir; + string path; + const int fd = CreateTMPFile(temp_dir.path(), &path); + ExceptionHandler handler(MinidumpDescriptor(fd), NULL, NULL, NULL, true, -1); + close(fd); +} + +static bool DoneCallback(const MinidumpDescriptor& descriptor, + void* context, + bool succeeded) { + if (!succeeded) + return false; + + if (!descriptor.IsFD()) { + int fd = reinterpret_cast(context); + uint32_t len = 0; + len = my_strlen(descriptor.path()); + IGNORE_RET(HANDLE_EINTR(sys_write(fd, &len, sizeof(len)))); + IGNORE_RET(HANDLE_EINTR(sys_write(fd, descriptor.path(), len))); + } + return true; +} + +#ifndef ADDRESS_SANITIZER + +// This is a replacement for "*reinterpret_cast(NULL) = 0;" +// It is needed because GCC is allowed to assume that the program will +// not execute any undefined behavior (UB) operation. Further, when GCC +// observes that UB statement is reached, it can assume that all statements +// leading to the UB one are never executed either, and can completely +// optimize them out. In the case of ExceptionHandlerTest::ExternalDumper, +// GCC-4.9 optimized out the entire set up of ExceptionHandler, causing +// test failure. +volatile int* p_null; // external linkage, so GCC can't tell that it + // remains NULL. Volatile just for a good measure. +static void DoNullPointerDereference() { + *p_null = 1; +} + +void ChildCrash(bool use_fd) { + AutoTempDir temp_dir; + int fds[2] = {0}; + int minidump_fd = -1; + string minidump_path; + if (use_fd) { + minidump_fd = CreateTMPFile(temp_dir.path(), &minidump_path); + } else { + ASSERT_NE(pipe(fds), -1); + } + + const pid_t child = fork(); + if (child == 0) { + { + google_breakpad::scoped_ptr handler; + if (use_fd) { + handler.reset(new ExceptionHandler(MinidumpDescriptor(minidump_fd), + NULL, NULL, NULL, true, -1)); + } else { + close(fds[0]); // Close the reading end. + void* fd_param = reinterpret_cast(fds[1]); + handler.reset(new ExceptionHandler(MinidumpDescriptor(temp_dir.path()), + NULL, DoneCallback, fd_param, + true, -1)); + } + // Crash with the exception handler in scope. + DoNullPointerDereference(); + } + } + if (!use_fd) + close(fds[1]); // Close the writting end. + + ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV)); + + if (!use_fd) + ASSERT_NO_FATAL_FAILURE(ReadMinidumpPathFromPipe(fds[0], &minidump_path)); + + struct stat st; + ASSERT_EQ(0, stat(minidump_path.c_str(), &st)); + ASSERT_GT(st.st_size, 0); + unlink(minidump_path.c_str()); +} + +TEST(ExceptionHandlerTest, ChildCrashWithPath) { + ASSERT_NO_FATAL_FAILURE(ChildCrash(false)); +} + +TEST(ExceptionHandlerTest, ChildCrashWithFD) { + ASSERT_NO_FATAL_FAILURE(ChildCrash(true)); +} + +#if !defined(__ANDROID_API__) || __ANDROID_API__ >= __ANDROID_API_N__ +static void* SleepFunction(void* unused) { + while (true) usleep(1000000); + return NULL; +} + +static void* CrashFunction(void* b_ptr) { + pthread_barrier_t* b = reinterpret_cast(b_ptr); + pthread_barrier_wait(b); + DoNullPointerDereference(); + return NULL; +} + +// Tests that concurrent crashes do not enter a loop by alternately triggering +// the signal handler. +TEST(ExceptionHandlerTest, ParallelChildCrashesDontHang) { + AutoTempDir temp_dir; + const pid_t child = fork(); + if (child == 0) { + google_breakpad::scoped_ptr handler( + new ExceptionHandler(MinidumpDescriptor(temp_dir.path()), NULL, NULL, + NULL, true, -1)); + + // We start a number of threads to make sure handling the signal takes + // enough time for the second thread to enter the signal handler. + int num_sleep_threads = 100; + google_breakpad::scoped_array sleep_threads( + new pthread_t[num_sleep_threads]); + for (int i = 0; i < num_sleep_threads; ++i) { + ASSERT_EQ(0, pthread_create(&sleep_threads[i], NULL, SleepFunction, + NULL)); + } + + int num_crash_threads = 2; + google_breakpad::scoped_array crash_threads( + new pthread_t[num_crash_threads]); + // Barrier to synchronize crashing both threads at the same time. + pthread_barrier_t b; + ASSERT_EQ(0, pthread_barrier_init(&b, NULL, num_crash_threads + 1)); + for (int i = 0; i < num_crash_threads; ++i) { + ASSERT_EQ(0, pthread_create(&crash_threads[i], NULL, CrashFunction, &b)); + } + pthread_barrier_wait(&b); + for (int i = 0; i < num_crash_threads; ++i) { + ASSERT_EQ(0, pthread_join(crash_threads[i], NULL)); + } + } + + // Wait a while until the child should have crashed. + usleep(1000000); + // Kill the child if it is still running. + kill(child, SIGKILL); + + // If the child process terminated by itself, it will have returned SIGSEGV. + // If however it got stuck in a loop, it will have been killed by the + // SIGKILL. + ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV)); +} +#endif // !defined(__ANDROID_API__) || __ANDROID_API__ >= __ANDROID_API_N__ + +static bool DoneCallbackReturnFalse(const MinidumpDescriptor& descriptor, + void* context, + bool succeeded) { + return false; +} + +static bool DoneCallbackReturnTrue(const MinidumpDescriptor& descriptor, + void* context, + bool succeeded) { + return true; +} + +static bool DoneCallbackRaiseSIGKILL(const MinidumpDescriptor& descriptor, + void* context, + bool succeeded) { + raise(SIGKILL); + return true; +} + +static bool FilterCallbackReturnFalse(void* context) { + return false; +} + +static bool FilterCallbackReturnTrue(void* context) { + return true; +} + +// SIGKILL cannot be blocked and a handler cannot be installed for it. In the +// following tests, if the child dies with signal SIGKILL, then the signal was +// redelivered to this handler. If the child dies with SIGSEGV then it wasn't. +static void RaiseSIGKILL(int sig) { + raise(SIGKILL); +} + +static bool InstallRaiseSIGKILL() { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = RaiseSIGKILL; + return sigaction(SIGSEGV, &sa, NULL) != -1; +} + +static void CrashWithCallbacks(ExceptionHandler::FilterCallback filter, + ExceptionHandler::MinidumpCallback done, + string path) { + ExceptionHandler handler( + MinidumpDescriptor(path), filter, done, NULL, true, -1); + // Crash with the exception handler in scope. + DoNullPointerDereference(); +} + +TEST(ExceptionHandlerTest, RedeliveryOnFilterCallbackFalse) { + AutoTempDir temp_dir; + + const pid_t child = fork(); + if (child == 0) { + ASSERT_TRUE(InstallRaiseSIGKILL()); + CrashWithCallbacks(FilterCallbackReturnFalse, NULL, temp_dir.path()); + } + + ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL)); +} + +TEST(ExceptionHandlerTest, RedeliveryOnDoneCallbackFalse) { + AutoTempDir temp_dir; + + const pid_t child = fork(); + if (child == 0) { + ASSERT_TRUE(InstallRaiseSIGKILL()); + CrashWithCallbacks(NULL, DoneCallbackReturnFalse, temp_dir.path()); + } + + ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL)); +} + +TEST(ExceptionHandlerTest, NoRedeliveryOnDoneCallbackTrue) { + AutoTempDir temp_dir; + + const pid_t child = fork(); + if (child == 0) { + ASSERT_TRUE(InstallRaiseSIGKILL()); + CrashWithCallbacks(NULL, DoneCallbackReturnTrue, temp_dir.path()); + } + + ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV)); +} + +TEST(ExceptionHandlerTest, NoRedeliveryOnFilterCallbackTrue) { + AutoTempDir temp_dir; + + const pid_t child = fork(); + if (child == 0) { + ASSERT_TRUE(InstallRaiseSIGKILL()); + CrashWithCallbacks(FilterCallbackReturnTrue, NULL, temp_dir.path()); + } + + ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV)); +} + +TEST(ExceptionHandlerTest, RedeliveryToDefaultHandler) { + AutoTempDir temp_dir; + + const pid_t child = fork(); + if (child == 0) { + // Custom signal handlers, which may have been installed by a test launcher, + // are undesirable in this child. + signal(SIGSEGV, SIG_DFL); + + CrashWithCallbacks(FilterCallbackReturnFalse, NULL, temp_dir.path()); + } + + // As RaiseSIGKILL wasn't installed, the redelivery should just kill the child + // with SIGSEGV. + ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV)); +} + +// Check that saving and restoring the signal handler with 'signal' +// instead of 'sigaction' doesn't make the Breakpad signal handler +// crash. See comments in ExceptionHandler::SignalHandler for full +// details. +TEST(ExceptionHandlerTest, RedeliveryOnBadSignalHandlerFlag) { + AutoTempDir temp_dir; + const pid_t child = fork(); + if (child == 0) { + // Install the RaiseSIGKILL handler for SIGSEGV. + ASSERT_TRUE(InstallRaiseSIGKILL()); + + // Create a new exception handler, this installs a new SIGSEGV + // handler, after saving the old one. + ExceptionHandler handler( + MinidumpDescriptor(temp_dir.path()), NULL, + DoneCallbackReturnFalse, NULL, true, -1); + + // Install the default SIGSEGV handler, saving the current one. + // Then re-install the current one with 'signal', this loses the + // SA_SIGINFO flag associated with the Breakpad handler. + sighandler_t old_handler = signal(SIGSEGV, SIG_DFL); + ASSERT_NE(reinterpret_cast(old_handler), + reinterpret_cast(SIG_ERR)); + ASSERT_NE(reinterpret_cast(signal(SIGSEGV, old_handler)), + reinterpret_cast(SIG_ERR)); + + // Crash with the exception handler in scope. + DoNullPointerDereference(); + } + // SIGKILL means Breakpad's signal handler didn't crash. + ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL)); +} + +TEST(ExceptionHandlerTest, StackedHandlersDeliveredToTop) { + AutoTempDir temp_dir; + + const pid_t child = fork(); + if (child == 0) { + ExceptionHandler bottom(MinidumpDescriptor(temp_dir.path()), + NULL, + NULL, + NULL, + true, + -1); + CrashWithCallbacks(NULL, DoneCallbackRaiseSIGKILL, temp_dir.path()); + } + ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL)); +} + +TEST(ExceptionHandlerTest, StackedHandlersNotDeliveredToBottom) { + AutoTempDir temp_dir; + + const pid_t child = fork(); + if (child == 0) { + ExceptionHandler bottom(MinidumpDescriptor(temp_dir.path()), + NULL, + DoneCallbackRaiseSIGKILL, + NULL, + true, + -1); + CrashWithCallbacks(NULL, NULL, temp_dir.path()); + } + ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV)); +} + +TEST(ExceptionHandlerTest, StackedHandlersFilteredToBottom) { + AutoTempDir temp_dir; + + const pid_t child = fork(); + if (child == 0) { + ExceptionHandler bottom(MinidumpDescriptor(temp_dir.path()), + NULL, + DoneCallbackRaiseSIGKILL, + NULL, + true, + -1); + CrashWithCallbacks(FilterCallbackReturnFalse, NULL, temp_dir.path()); + } + ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL)); +} + +TEST(ExceptionHandlerTest, StackedHandlersUnhandledToBottom) { + AutoTempDir temp_dir; + + const pid_t child = fork(); + if (child == 0) { + ExceptionHandler bottom(MinidumpDescriptor(temp_dir.path()), + NULL, + DoneCallbackRaiseSIGKILL, + NULL, + true, + -1); + CrashWithCallbacks(NULL, DoneCallbackReturnFalse, temp_dir.path()); + } + ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGKILL)); +} + +namespace { +const int kSimpleFirstChanceReturnStatus = 42; +bool SimpleFirstChanceHandler(int, siginfo_t*, void*) { + _exit(kSimpleFirstChanceReturnStatus); +} +} + +TEST(ExceptionHandlerTest, FirstChanceHandlerRuns) { + AutoTempDir temp_dir; + + const pid_t child = fork(); + if (child == 0) { + ExceptionHandler handler( + MinidumpDescriptor(temp_dir.path()), NULL, NULL, NULL, true, -1); + google_breakpad::SetFirstChanceExceptionHandler(SimpleFirstChanceHandler); + DoNullPointerDereference(); + } + int status; + ASSERT_NE(HANDLE_EINTR(waitpid(child, &status, 0)), -1); + ASSERT_TRUE(WIFEXITED(status)); + ASSERT_EQ(kSimpleFirstChanceReturnStatus, WEXITSTATUS(status)); +} + +#endif // !ADDRESS_SANITIZER + +const unsigned char kIllegalInstruction[] = { +#if defined(__mips__) + // mfc2 zero,Impl - usually illegal in userspace. + 0x48, 0x00, 0x00, 0x48 +#else + // This crashes with SIGILL on x86/x86-64/arm. + 0xff, 0xff, 0xff, 0xff +#endif +}; + +// Test that memory around the instruction pointer is written +// to the dump as a MinidumpMemoryRegion. +TEST(ExceptionHandlerTest, InstructionPointerMemory) { + AutoTempDir temp_dir; + int fds[2]; + ASSERT_NE(pipe(fds), -1); + + // These are defined here so the parent can use them to check the + // data from the minidump afterwards. + const uint32_t kMemorySize = 256; // bytes + const int kOffset = kMemorySize / 2; + + const pid_t child = fork(); + if (child == 0) { + close(fds[0]); + ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL, + DoneCallback, reinterpret_cast(fds[1]), + true, -1); + // Get some executable memory. + char* memory = + reinterpret_cast(mmap(NULL, + kMemorySize, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANON, + -1, + 0)); + if (!memory) + exit(0); + + // Write some instructions that will crash. Put them in the middle + // of the block of memory, because the minidump should contain 128 + // bytes on either side of the instruction pointer. + memcpy(memory + kOffset, kIllegalInstruction, sizeof(kIllegalInstruction)); + FlushInstructionCache(memory, kMemorySize); + + // Now execute the instructions, which should crash. + typedef void (*void_function)(void); + void_function memory_function = + reinterpret_cast(memory + kOffset); + memory_function(); + } + close(fds[1]); + + ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGILL)); + + string minidump_path; + ASSERT_NO_FATAL_FAILURE(ReadMinidumpPathFromPipe(fds[0], &minidump_path)); + + struct stat st; + ASSERT_EQ(0, stat(minidump_path.c_str(), &st)); + ASSERT_GT(st.st_size, 0); + + // Read the minidump. Locate the exception record and the + // memory list, and then ensure that there is a memory region + // in the memory list that covers the instruction pointer from + // the exception record. + Minidump minidump(minidump_path); + ASSERT_TRUE(minidump.Read()); + + MinidumpException* exception = minidump.GetException(); + MinidumpMemoryList* memory_list = minidump.GetMemoryList(); + ASSERT_TRUE(exception); + ASSERT_TRUE(memory_list); + ASSERT_LT(0U, memory_list->region_count()); + + MinidumpContext* context = exception->GetContext(); + ASSERT_TRUE(context); + + uint64_t instruction_pointer; + ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer)); + + MinidumpMemoryRegion* region = + memory_list->GetMemoryRegionForAddress(instruction_pointer); + ASSERT_TRUE(region); + + EXPECT_EQ(kMemorySize, region->GetSize()); + const uint8_t* bytes = region->GetMemory(); + ASSERT_TRUE(bytes); + + uint8_t prefix_bytes[kOffset]; + uint8_t suffix_bytes[kMemorySize - kOffset - sizeof(kIllegalInstruction)]; + memset(prefix_bytes, 0, sizeof(prefix_bytes)); + memset(suffix_bytes, 0, sizeof(suffix_bytes)); + EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0); + EXPECT_TRUE(memcmp(bytes + kOffset, kIllegalInstruction, + sizeof(kIllegalInstruction)) == 0); + EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(kIllegalInstruction), + suffix_bytes, sizeof(suffix_bytes)) == 0); + + unlink(minidump_path.c_str()); +} + +// Test that the memory region around the instruction pointer is +// bounded correctly on the low end. +TEST(ExceptionHandlerTest, InstructionPointerMemoryMinBound) { + AutoTempDir temp_dir; + int fds[2]; + ASSERT_NE(pipe(fds), -1); + + // These are defined here so the parent can use them to check the + // data from the minidump afterwards. + const uint32_t kMemorySize = 256; // bytes + const int kOffset = 0; + + const pid_t child = fork(); + if (child == 0) { + close(fds[0]); + ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL, + DoneCallback, reinterpret_cast(fds[1]), + true, -1); + // Get some executable memory. + char* memory = + reinterpret_cast(mmap(NULL, + kMemorySize, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANON, + -1, + 0)); + if (!memory) + exit(0); + + // Write some instructions that will crash. Put them in the middle + // of the block of memory, because the minidump should contain 128 + // bytes on either side of the instruction pointer. + memcpy(memory + kOffset, kIllegalInstruction, sizeof(kIllegalInstruction)); + FlushInstructionCache(memory, kMemorySize); + + // Now execute the instructions, which should crash. + typedef void (*void_function)(void); + void_function memory_function = + reinterpret_cast(memory + kOffset); + memory_function(); + } + close(fds[1]); + + ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGILL)); + + string minidump_path; + ASSERT_NO_FATAL_FAILURE(ReadMinidumpPathFromPipe(fds[0], &minidump_path)); + + struct stat st; + ASSERT_EQ(0, stat(minidump_path.c_str(), &st)); + ASSERT_GT(st.st_size, 0); + + // Read the minidump. Locate the exception record and the + // memory list, and then ensure that there is a memory region + // in the memory list that covers the instruction pointer from + // the exception record. + Minidump minidump(minidump_path); + ASSERT_TRUE(minidump.Read()); + + MinidumpException* exception = minidump.GetException(); + MinidumpMemoryList* memory_list = minidump.GetMemoryList(); + ASSERT_TRUE(exception); + ASSERT_TRUE(memory_list); + ASSERT_LT(0U, memory_list->region_count()); + + MinidumpContext* context = exception->GetContext(); + ASSERT_TRUE(context); + + uint64_t instruction_pointer; + ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer)); + + MinidumpMemoryRegion* region = + memory_list->GetMemoryRegionForAddress(instruction_pointer); + ASSERT_TRUE(region); + + EXPECT_EQ(kMemorySize / 2, region->GetSize()); + const uint8_t* bytes = region->GetMemory(); + ASSERT_TRUE(bytes); + + uint8_t suffix_bytes[kMemorySize / 2 - sizeof(kIllegalInstruction)]; + memset(suffix_bytes, 0, sizeof(suffix_bytes)); + EXPECT_TRUE(memcmp(bytes + kOffset, kIllegalInstruction, + sizeof(kIllegalInstruction)) == 0); + EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(kIllegalInstruction), + suffix_bytes, sizeof(suffix_bytes)) == 0); + unlink(minidump_path.c_str()); +} + +// Test that the memory region around the instruction pointer is +// bounded correctly on the high end. +TEST(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) { + AutoTempDir temp_dir; + int fds[2]; + ASSERT_NE(pipe(fds), -1); + + // These are defined here so the parent can use them to check the + // data from the minidump afterwards. + // Use 4k here because the OS will hand out a single page even + // if a smaller size is requested, and this test wants to + // test the upper bound of the memory range. + const uint32_t kMemorySize = 4096; // bytes + const int kOffset = kMemorySize - sizeof(kIllegalInstruction); + + const pid_t child = fork(); + if (child == 0) { + close(fds[0]); + ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL, + DoneCallback, reinterpret_cast(fds[1]), + true, -1); + // Get some executable memory. + char* memory = + reinterpret_cast(mmap(NULL, + kMemorySize, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANON, + -1, + 0)); + if (!memory) + exit(0); + + // Write some instructions that will crash. Put them in the middle + // of the block of memory, because the minidump should contain 128 + // bytes on either side of the instruction pointer. + memcpy(memory + kOffset, kIllegalInstruction, sizeof(kIllegalInstruction)); + FlushInstructionCache(memory, kMemorySize); + + // Now execute the instructions, which should crash. + typedef void (*void_function)(void); + void_function memory_function = + reinterpret_cast(memory + kOffset); + memory_function(); + } + close(fds[1]); + + ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGILL)); + + string minidump_path; + ASSERT_NO_FATAL_FAILURE(ReadMinidumpPathFromPipe(fds[0], &minidump_path)); + + struct stat st; + ASSERT_EQ(0, stat(minidump_path.c_str(), &st)); + ASSERT_GT(st.st_size, 0); + + // Read the minidump. Locate the exception record and the memory list, and + // then ensure that there is a memory region in the memory list that covers + // the instruction pointer from the exception record. + Minidump minidump(minidump_path); + ASSERT_TRUE(minidump.Read()); + + MinidumpException* exception = minidump.GetException(); + MinidumpMemoryList* memory_list = minidump.GetMemoryList(); + ASSERT_TRUE(exception); + ASSERT_TRUE(memory_list); + ASSERT_LT(0U, memory_list->region_count()); + + MinidumpContext* context = exception->GetContext(); + ASSERT_TRUE(context); + + uint64_t instruction_pointer; + ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer)); + + MinidumpMemoryRegion* region = + memory_list->GetMemoryRegionForAddress(instruction_pointer); + ASSERT_TRUE(region); + + const size_t kPrefixSize = 128; // bytes + EXPECT_EQ(kPrefixSize + sizeof(kIllegalInstruction), region->GetSize()); + const uint8_t* bytes = region->GetMemory(); + ASSERT_TRUE(bytes); + + uint8_t prefix_bytes[kPrefixSize]; + memset(prefix_bytes, 0, sizeof(prefix_bytes)); + EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0); + EXPECT_TRUE(memcmp(bytes + kPrefixSize, + kIllegalInstruction, sizeof(kIllegalInstruction)) == 0); + + unlink(minidump_path.c_str()); +} + +#ifndef ADDRESS_SANITIZER + +// Ensure that an extra memory block doesn't get added when the instruction +// pointer is not in mapped memory. +TEST(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) { + AutoTempDir temp_dir; + int fds[2]; + ASSERT_NE(pipe(fds), -1); + + const pid_t child = fork(); + if (child == 0) { + close(fds[0]); + ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL, + DoneCallback, reinterpret_cast(fds[1]), + true, -1); + // Try calling a NULL pointer. + typedef void (*void_function)(void); + // Volatile markings are needed to keep Clang from generating invalid + // opcodes. See http://crbug.com/498354 for details. + volatile void_function memory_function = + reinterpret_cast(NULL); + memory_function(); + // not reached + exit(1); + } + close(fds[1]); + + ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV)); + + string minidump_path; + ASSERT_NO_FATAL_FAILURE(ReadMinidumpPathFromPipe(fds[0], &minidump_path)); + + struct stat st; + ASSERT_EQ(0, stat(minidump_path.c_str(), &st)); + ASSERT_GT(st.st_size, 0); + + // Read the minidump. Locate the exception record and the + // memory list, and then ensure that there is no memory region + // in the memory list that covers the instruction pointer from + // the exception record. + Minidump minidump(minidump_path); + ASSERT_TRUE(minidump.Read()); + + MinidumpException* exception = minidump.GetException(); + ASSERT_TRUE(exception); + + MinidumpContext* exception_context = exception->GetContext(); + ASSERT_TRUE(exception_context); + + uint64_t instruction_pointer; + ASSERT_TRUE(exception_context->GetInstructionPointer(&instruction_pointer)); + EXPECT_EQ(instruction_pointer, 0u); + + MinidumpMemoryList* memory_list = minidump.GetMemoryList(); + ASSERT_TRUE(memory_list); + + unsigned int region_count = memory_list->region_count(); + ASSERT_GE(region_count, 1u); + + for (unsigned int region_index = 0; + region_index < region_count; + ++region_index) { + MinidumpMemoryRegion* region = + memory_list->GetMemoryRegionAtIndex(region_index); + uint64_t region_base = region->GetBase(); + EXPECT_FALSE(instruction_pointer >= region_base && + instruction_pointer < region_base + region->GetSize()); + } + + unlink(minidump_path.c_str()); +} + +#endif // !ADDRESS_SANITIZER + +// Test that anonymous memory maps can be annotated with names and IDs. +TEST(ExceptionHandlerTest, ModuleInfo) { + // These are defined here so the parent can use them to check the + // data from the minidump afterwards. + const uint32_t kMemorySize = sysconf(_SC_PAGESIZE); + const char* kMemoryName = "a fake module"; + const uint8_t kModuleGUID[sizeof(MDGUID)] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF + }; + const string module_identifier = "33221100554477668899AABBCCDDEEFF0"; + + // Get some memory. + char* memory = + reinterpret_cast(mmap(NULL, + kMemorySize, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, + -1, + 0)); + const uintptr_t kMemoryAddress = reinterpret_cast(memory); + ASSERT_TRUE(memory); + + AutoTempDir temp_dir; + ExceptionHandler handler( + MinidumpDescriptor(temp_dir.path()), NULL, NULL, NULL, true, -1); + + // Add info about the anonymous memory mapping. + handler.AddMappingInfo(kMemoryName, + kModuleGUID, + kMemoryAddress, + kMemorySize, + 0); + ASSERT_TRUE(handler.WriteMinidump()); + + const MinidumpDescriptor& minidump_desc = handler.minidump_descriptor(); + // Read the minidump. Load the module list, and ensure that the mmap'ed + // |memory| is listed with the given module name and debug ID. + Minidump minidump(minidump_desc.path()); + ASSERT_TRUE(minidump.Read()); + + MinidumpModuleList* module_list = minidump.GetModuleList(); + ASSERT_TRUE(module_list); + const MinidumpModule* module = + module_list->GetModuleForAddress(kMemoryAddress); + ASSERT_TRUE(module); + + EXPECT_EQ(kMemoryAddress, module->base_address()); + EXPECT_EQ(kMemorySize, module->size()); + EXPECT_EQ(kMemoryName, module->code_file()); + EXPECT_EQ(module_identifier, module->debug_identifier()); + + unlink(minidump_desc.path()); +} + +#ifndef ADDRESS_SANITIZER + +static const unsigned kControlMsgSize = + CMSG_SPACE(sizeof(int)) + CMSG_SPACE(sizeof(struct ucred)); + +static bool +CrashHandler(const void* crash_context, size_t crash_context_size, + void* context) { + const int fd = (intptr_t) context; + int fds[2]; + if (pipe(fds) == -1) { + // There doesn't seem to be any way to reliably handle + // this failure without the parent process hanging + // At least make sure that this process doesn't access + // unexpected file descriptors + fds[0] = -1; + fds[1] = -1; + } + struct kernel_msghdr msg = {0}; + struct kernel_iovec iov; + iov.iov_base = const_cast(crash_context); + iov.iov_len = crash_context_size; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + char cmsg[kControlMsgSize]; + memset(cmsg, 0, kControlMsgSize); + msg.msg_control = cmsg; + msg.msg_controllen = sizeof(cmsg); + + struct cmsghdr* hdr = CMSG_FIRSTHDR(&msg); + hdr->cmsg_level = SOL_SOCKET; + hdr->cmsg_type = SCM_RIGHTS; + hdr->cmsg_len = CMSG_LEN(sizeof(int)); + *((int*) CMSG_DATA(hdr)) = fds[1]; + hdr = CMSG_NXTHDR((struct msghdr*) &msg, hdr); + hdr->cmsg_level = SOL_SOCKET; + hdr->cmsg_type = SCM_CREDENTIALS; + hdr->cmsg_len = CMSG_LEN(sizeof(struct ucred)); + struct ucred* cred = reinterpret_cast(CMSG_DATA(hdr)); + cred->uid = getuid(); + cred->gid = getgid(); + cred->pid = getpid(); + + ssize_t ret = HANDLE_EINTR(sys_sendmsg(fd, &msg, 0)); + sys_close(fds[1]); + if (ret <= 0) + return false; + + char b; + IGNORE_RET(HANDLE_EINTR(sys_read(fds[0], &b, 1))); + + return true; +} + +TEST(ExceptionHandlerTest, ExternalDumper) { + int fds[2]; + ASSERT_NE(socketpair(AF_UNIX, SOCK_DGRAM, 0, fds), -1); + static const int on = 1; + setsockopt(fds[0], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); + setsockopt(fds[1], SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)); + + const pid_t child = fork(); + if (child == 0) { + close(fds[0]); + ExceptionHandler handler(MinidumpDescriptor("/tmp1"), NULL, NULL, + reinterpret_cast(fds[1]), true, -1); + handler.set_crash_handler(CrashHandler); + DoNullPointerDereference(); + } + close(fds[1]); + struct msghdr msg = {0}; + struct iovec iov; + static const unsigned kCrashContextSize = + sizeof(ExceptionHandler::CrashContext); + char context[kCrashContextSize]; + char control[kControlMsgSize]; + iov.iov_base = context; + iov.iov_len = kCrashContextSize; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = control; + msg.msg_controllen = kControlMsgSize; + + const ssize_t n = HANDLE_EINTR(recvmsg(fds[0], &msg, 0)); + ASSERT_EQ(static_cast(kCrashContextSize), n); + ASSERT_EQ(kControlMsgSize, msg.msg_controllen); + ASSERT_EQ(static_cast<__typeof__(msg.msg_flags)>(0), msg.msg_flags); + ASSERT_EQ(0, close(fds[0])); + + pid_t crashing_pid = -1; + int signal_fd = -1; + for (struct cmsghdr* hdr = CMSG_FIRSTHDR(&msg); hdr; + hdr = CMSG_NXTHDR(&msg, hdr)) { + if (hdr->cmsg_level != SOL_SOCKET) + continue; + if (hdr->cmsg_type == SCM_RIGHTS) { + const unsigned len = hdr->cmsg_len - + (((uint8_t*)CMSG_DATA(hdr)) - (uint8_t*)hdr); + ASSERT_EQ(sizeof(int), len); + signal_fd = *(reinterpret_cast(CMSG_DATA(hdr))); + } else if (hdr->cmsg_type == SCM_CREDENTIALS) { + const struct ucred* cred = + reinterpret_cast(CMSG_DATA(hdr)); + crashing_pid = cred->pid; + } + } + + ASSERT_NE(crashing_pid, -1); + ASSERT_NE(signal_fd, -1); + + AutoTempDir temp_dir; + string templ = temp_dir.path() + "/exception-handler-unittest"; + ASSERT_TRUE(WriteMinidump(templ.c_str(), crashing_pid, context, + kCrashContextSize)); + static const char b = 0; + ASSERT_EQ(1, (HANDLE_EINTR(write(signal_fd, &b, 1)))); + ASSERT_EQ(0, close(signal_fd)); + + ASSERT_NO_FATAL_FAILURE(WaitForProcessToTerminate(child, SIGSEGV)); + + struct stat st; + ASSERT_EQ(0, stat(templ.c_str(), &st)); + ASSERT_GT(st.st_size, 0); + unlink(templ.c_str()); +} + +#endif // !ADDRESS_SANITIZER + +TEST(ExceptionHandlerTest, WriteMinidumpExceptionStream) { + AutoTempDir temp_dir; + ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL, NULL, + NULL, false, -1); + ASSERT_TRUE(handler.WriteMinidump()); + + string minidump_path = handler.minidump_descriptor().path(); + + // Read the minidump and check the exception stream. + Minidump minidump(minidump_path); + ASSERT_TRUE(minidump.Read()); + MinidumpException* exception = minidump.GetException(); + ASSERT_TRUE(exception); + const MDRawExceptionStream* raw = exception->exception(); + ASSERT_TRUE(raw); + EXPECT_EQ(MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED, + raw->exception_record.exception_code); +} + +TEST(ExceptionHandlerTest, GenerateMultipleDumpsWithFD) { + AutoTempDir temp_dir; + string path; + const int fd = CreateTMPFile(temp_dir.path(), &path); + ExceptionHandler handler(MinidumpDescriptor(fd), NULL, NULL, NULL, false, -1); + ASSERT_TRUE(handler.WriteMinidump()); + // Check by the size of the data written to the FD that a minidump was + // generated. + off_t size = lseek(fd, 0, SEEK_CUR); + ASSERT_GT(size, 0); + + // Generate another minidump. + ASSERT_TRUE(handler.WriteMinidump()); + size = lseek(fd, 0, SEEK_CUR); + ASSERT_GT(size, 0); +} + +TEST(ExceptionHandlerTest, GenerateMultipleDumpsWithPath) { + AutoTempDir temp_dir; + ExceptionHandler handler(MinidumpDescriptor(temp_dir.path()), NULL, NULL, + NULL, false, -1); + ASSERT_TRUE(handler.WriteMinidump()); + + const MinidumpDescriptor& minidump_1 = handler.minidump_descriptor(); + struct stat st; + ASSERT_EQ(0, stat(minidump_1.path(), &st)); + ASSERT_GT(st.st_size, 0); + string minidump_1_path(minidump_1.path()); + // Check it is a valid minidump. + Minidump minidump1(minidump_1_path); + ASSERT_TRUE(minidump1.Read()); + unlink(minidump_1.path()); + + // Generate another minidump, it should go to a different file. + ASSERT_TRUE(handler.WriteMinidump()); + const MinidumpDescriptor& minidump_2 = handler.minidump_descriptor(); + ASSERT_EQ(0, stat(minidump_2.path(), &st)); + ASSERT_GT(st.st_size, 0); + string minidump_2_path(minidump_2.path()); + // Check it is a valid minidump. + Minidump minidump2(minidump_2_path); + ASSERT_TRUE(minidump2.Read()); + unlink(minidump_2.path()); + + // 2 distinct files should be produced. + ASSERT_STRNE(minidump_1_path.c_str(), minidump_2_path.c_str()); +} + +// Test that an additional memory region can be added to the minidump. +TEST(ExceptionHandlerTest, AdditionalMemory) { + const uint32_t kMemorySize = sysconf(_SC_PAGESIZE); + + // Get some heap memory. + uint8_t* memory = new uint8_t[kMemorySize]; + const uintptr_t kMemoryAddress = reinterpret_cast(memory); + ASSERT_TRUE(memory); + + // Stick some data into the memory so the contents can be verified. + for (uint32_t i = 0; i < kMemorySize; ++i) { + memory[i] = i % 255; + } + + AutoTempDir temp_dir; + ExceptionHandler handler( + MinidumpDescriptor(temp_dir.path()), NULL, NULL, NULL, true, -1); + + // Add the memory region to the list of memory to be included. + handler.RegisterAppMemory(memory, kMemorySize); + handler.WriteMinidump(); + + const MinidumpDescriptor& minidump_desc = handler.minidump_descriptor(); + + // Read the minidump. Ensure that the memory region is present + Minidump minidump(minidump_desc.path()); + ASSERT_TRUE(minidump.Read()); + + MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList(); + ASSERT_TRUE(dump_memory_list); + const MinidumpMemoryRegion* region = + dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress); + ASSERT_TRUE(region); + + EXPECT_EQ(kMemoryAddress, region->GetBase()); + EXPECT_EQ(kMemorySize, region->GetSize()); + + // Verify memory contents. + EXPECT_EQ(0, memcmp(region->GetMemory(), memory, kMemorySize)); + + delete[] memory; +} + +// Test that a memory region that was previously registered +// can be unregistered. +TEST(ExceptionHandlerTest, AdditionalMemoryRemove) { + const uint32_t kMemorySize = sysconf(_SC_PAGESIZE); + + // Get some heap memory. + uint8_t* memory = new uint8_t[kMemorySize]; + const uintptr_t kMemoryAddress = reinterpret_cast(memory); + ASSERT_TRUE(memory); + + AutoTempDir temp_dir; + ExceptionHandler handler( + MinidumpDescriptor(temp_dir.path()), NULL, NULL, NULL, true, -1); + + // Add the memory region to the list of memory to be included. + handler.RegisterAppMemory(memory, kMemorySize); + + // ...and then remove it + handler.UnregisterAppMemory(memory); + handler.WriteMinidump(); + + const MinidumpDescriptor& minidump_desc = handler.minidump_descriptor(); + + // Read the minidump. Ensure that the memory region is not present. + Minidump minidump(minidump_desc.path()); + ASSERT_TRUE(minidump.Read()); + + MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList(); + ASSERT_TRUE(dump_memory_list); + const MinidumpMemoryRegion* region = + dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress); + EXPECT_FALSE(region); + + delete[] memory; +} + +static bool SimpleCallback(const MinidumpDescriptor& descriptor, + void* context, + bool succeeded) { + string* filename = reinterpret_cast(context); + *filename = descriptor.path(); + return true; +} + +TEST(ExceptionHandlerTest, WriteMinidumpForChild) { + int fds[2]; + ASSERT_NE(-1, pipe(fds)); + + const pid_t child = fork(); + if (child == 0) { + close(fds[1]); + char b; + HANDLE_EINTR(read(fds[0], &b, sizeof(b))); + close(fds[0]); + syscall(__NR_exit); + } + close(fds[0]); + + AutoTempDir temp_dir; + string minidump_filename; + ASSERT_TRUE( + ExceptionHandler::WriteMinidumpForChild(child, child, + temp_dir.path(), SimpleCallback, + (void*)&minidump_filename)); + + Minidump minidump(minidump_filename); + ASSERT_TRUE(minidump.Read()); + // Check that the crashing thread is the main thread of |child| + MinidumpException* exception = minidump.GetException(); + ASSERT_TRUE(exception); + uint32_t thread_id; + ASSERT_TRUE(exception->GetThreadID(&thread_id)); + EXPECT_EQ(child, static_cast(thread_id)); + + const MDRawExceptionStream* raw = exception->exception(); + ASSERT_TRUE(raw); + EXPECT_EQ(MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED, + raw->exception_record.exception_code); + + close(fds[1]); + unlink(minidump_filename.c_str()); +} diff --git a/shared/sentry/external/breakpad/src/client/linux/handler/microdump_extra_info.h b/shared/sentry/external/breakpad/src/client/linux/handler/microdump_extra_info.h new file mode 100644 index 000000000..bf01f0c7b --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/handler/microdump_extra_info.h @@ -0,0 +1,52 @@ +// Copyright 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. + +#ifndef CLIENT_LINUX_HANDLER_MICRODUMP_EXTRA_INFO_H_ +#define CLIENT_LINUX_HANDLER_MICRODUMP_EXTRA_INFO_H_ + +namespace google_breakpad { + +struct MicrodumpExtraInfo { + // Strings pointed to by this struct are not copied, and are + // expected to remain valid for the lifetime of the process. + const char* build_fingerprint; + const char* product_info; + const char* gpu_fingerprint; + const char* process_type; + + MicrodumpExtraInfo() + : build_fingerprint(NULL), + product_info(NULL), + gpu_fingerprint(NULL), + process_type(NULL) {} +}; + +} + +#endif // CLIENT_LINUX_HANDLER_MICRODUMP_EXTRA_INFO_H_ diff --git a/shared/sentry/external/breakpad/src/client/linux/handler/minidump_descriptor.cc b/shared/sentry/external/breakpad/src/client/linux/handler/minidump_descriptor.cc new file mode 100644 index 000000000..bd94474e9 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/handler/minidump_descriptor.cc @@ -0,0 +1,97 @@ +// 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 + +#include "client/linux/handler/minidump_descriptor.h" + +#include "common/linux/guid_creator.h" + +namespace google_breakpad { + +//static +const MinidumpDescriptor::MicrodumpOnConsole + MinidumpDescriptor::kMicrodumpOnConsole = {}; + +MinidumpDescriptor::MinidumpDescriptor(const MinidumpDescriptor& descriptor) + : mode_(descriptor.mode_), + fd_(descriptor.fd_), + directory_(descriptor.directory_), + c_path_(NULL), + size_limit_(descriptor.size_limit_), + address_within_principal_mapping_( + descriptor.address_within_principal_mapping_), + skip_dump_if_principal_mapping_not_referenced_( + descriptor.skip_dump_if_principal_mapping_not_referenced_), + sanitize_stacks_(descriptor.sanitize_stacks_), + microdump_extra_info_(descriptor.microdump_extra_info_) { + // The copy constructor is not allowed to be called on a MinidumpDescriptor + // with a valid path_, as getting its c_path_ would require the heap which + // can cause problems in compromised environments. + assert(descriptor.path_.empty()); +} + +MinidumpDescriptor& MinidumpDescriptor::operator=( + const MinidumpDescriptor& descriptor) { + assert(descriptor.path_.empty()); + + mode_ = descriptor.mode_; + fd_ = descriptor.fd_; + directory_ = descriptor.directory_; + path_.clear(); + if (c_path_) { + // This descriptor already had a path set, so generate a new one. + c_path_ = NULL; + UpdatePath(); + } + size_limit_ = descriptor.size_limit_; + address_within_principal_mapping_ = + descriptor.address_within_principal_mapping_; + skip_dump_if_principal_mapping_not_referenced_ = + descriptor.skip_dump_if_principal_mapping_not_referenced_; + sanitize_stacks_ = descriptor.sanitize_stacks_; + microdump_extra_info_ = descriptor.microdump_extra_info_; + return *this; +} + +void MinidumpDescriptor::UpdatePath() { + assert(mode_ == kWriteMinidumpToFile && !directory_.empty()); + + GUID guid; + char guid_str[kGUIDStringLength + 1]; + if (!CreateGUID(&guid) || !GUIDToString(&guid, guid_str, sizeof(guid_str))) { + assert(false); + } + + path_.clear(); + path_ = directory_ + "/" + guid_str + ".dmp"; + c_path_ = path_.c_str(); +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/linux/handler/minidump_descriptor.h b/shared/sentry/external/breakpad/src/client/linux/handler/minidump_descriptor.h new file mode 100644 index 000000000..c7e4f2b37 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/handler/minidump_descriptor.h @@ -0,0 +1,199 @@ +// 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 CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_ +#define CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_ + +#include +#include + +#include + +#include "client/linux/handler/microdump_extra_info.h" +#include "common/using_std_string.h" + +// This class describes how a crash dump should be generated, either: +// - Writing a full minidump to a file in a given directory (the actual path, +// inside the directory, is determined by this class). +// - Writing a full minidump to a given fd. +// - Writing a reduced microdump to the console (logcat on Android). +namespace google_breakpad { + +class MinidumpDescriptor { + public: + struct MicrodumpOnConsole {}; + static const MicrodumpOnConsole kMicrodumpOnConsole; + + MinidumpDescriptor() + : mode_(kUninitialized), + fd_(-1), + size_limit_(-1), + address_within_principal_mapping_(0), + skip_dump_if_principal_mapping_not_referenced_(false) {} + + explicit MinidumpDescriptor(const string& directory) + : mode_(kWriteMinidumpToFile), + fd_(-1), + directory_(directory), + c_path_(NULL), + size_limit_(-1), + address_within_principal_mapping_(0), + skip_dump_if_principal_mapping_not_referenced_(false), + sanitize_stacks_(false) { + assert(!directory.empty()); + } + + explicit MinidumpDescriptor(int fd) + : mode_(kWriteMinidumpToFd), + fd_(fd), + c_path_(NULL), + size_limit_(-1), + address_within_principal_mapping_(0), + skip_dump_if_principal_mapping_not_referenced_(false), + sanitize_stacks_(false) { + assert(fd != -1); + } + + explicit MinidumpDescriptor(const MicrodumpOnConsole&) + : mode_(kWriteMicrodumpToConsole), + fd_(-1), + size_limit_(-1), + address_within_principal_mapping_(0), + skip_dump_if_principal_mapping_not_referenced_(false), + sanitize_stacks_(false) {} + + explicit MinidumpDescriptor(const MinidumpDescriptor& descriptor); + MinidumpDescriptor& operator=(const MinidumpDescriptor& descriptor); + + static MinidumpDescriptor getMicrodumpDescriptor(); + + bool IsFD() const { return mode_ == kWriteMinidumpToFd; } + + int fd() const { return fd_; } + + string directory() const { return directory_; } + + const char* path() const { return c_path_; } + + bool IsMicrodumpOnConsole() const { + return mode_ == kWriteMicrodumpToConsole; + } + + // Updates the path so it is unique. + // Should be called from a normal context: this methods uses the heap. + void UpdatePath(); + + off_t size_limit() const { return size_limit_; } + void set_size_limit(off_t limit) { size_limit_ = limit; } + + uintptr_t address_within_principal_mapping() const { + return address_within_principal_mapping_; + } + void set_address_within_principal_mapping( + uintptr_t address_within_principal_mapping) { + address_within_principal_mapping_ = address_within_principal_mapping; + } + + bool skip_dump_if_principal_mapping_not_referenced() { + return skip_dump_if_principal_mapping_not_referenced_; + } + void set_skip_dump_if_principal_mapping_not_referenced( + bool skip_dump_if_principal_mapping_not_referenced) { + skip_dump_if_principal_mapping_not_referenced_ = + skip_dump_if_principal_mapping_not_referenced; + } + + bool sanitize_stacks() const { return sanitize_stacks_; } + void set_sanitize_stacks(bool sanitize_stacks) { + sanitize_stacks_ = sanitize_stacks; + } + + MicrodumpExtraInfo* microdump_extra_info() { + assert(IsMicrodumpOnConsole()); + return µdump_extra_info_; + } + + private: + enum DumpMode { + kUninitialized = 0, + kWriteMinidumpToFile, + kWriteMinidumpToFd, + kWriteMicrodumpToConsole + }; + + // Specifies the dump mode (see DumpMode). + DumpMode mode_; + + // The file descriptor where the minidump is generated. + int fd_; + + // The directory where the minidump should be generated. + string directory_; + + // The full path to the generated minidump. + string path_; + + // The C string of |path_|. Precomputed so it can be access from a compromised + // context. + const char* c_path_; + + off_t size_limit_; + + // This member points somewhere into the main module for this + // process (the module that is considerered interesting for the + // purposes of debugging crashes). + uintptr_t address_within_principal_mapping_; + + // If set, threads that do not reference the address range + // associated with |address_within_principal_mapping_| will not have their + // stacks logged. + bool skip_dump_if_principal_mapping_not_referenced_; + + // If set, stacks are sanitized to remove PII. This involves + // overwriting any pointer-aligned words that are not either + // pointers into a process mapping or small integers (+/-4096). This + // leaves enough information to unwind stacks, and preserve some + // register values, but elides strings and other program data. + bool sanitize_stacks_; + + // The extra microdump data (e.g. product name/version, build + // fingerprint, gpu fingerprint) that should be appended to the dump + // (microdump only). Microdumps don't have the ability of appending + // extra metadata after the dump is generated (as opposite to + // minidumps MIME fields), therefore the extra data must be provided + // upfront. Any memory pointed to by members of the + // MicrodumpExtraInfo struct must be valid for the lifetime of the + // process (read: the caller has to guarantee that it is stored in + // global static storage.) + MicrodumpExtraInfo microdump_extra_info_; +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_HANDLER_MINIDUMP_DESCRIPTOR_H_ diff --git a/shared/sentry/external/breakpad/src/client/linux/log/log.cc b/shared/sentry/external/breakpad/src/client/linux/log/log.cc new file mode 100644 index 000000000..318794095 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/log/log.cc @@ -0,0 +1,84 @@ +// 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 "client/linux/log/log.h" + +#if defined(__ANDROID__) +#include +#include +#else +#include "third_party/lss/linux_syscall_support.h" +#endif + +namespace logger { + +#if defined(__ANDROID__) +namespace { + +// __android_log_buf_write() is not exported in the NDK and is being used by +// dynamic runtime linking. Its declaration is taken from Android's +// system/core/include/log/log.h. +using AndroidLogBufferWriteFunc = int (*)(int bufID, int prio, const char* tag, + const char* text); +const int kAndroidCrashLogId = 4; // From LOG_ID_CRASH in log.h. +const char kAndroidLogTag[] = "google-breakpad"; + +bool g_crash_log_initialized = false; +AndroidLogBufferWriteFunc g_android_log_buf_write = nullptr; + +} // namespace + +void initializeCrashLogWriter() { + if (g_crash_log_initialized) + return; + g_android_log_buf_write = reinterpret_cast( + dlsym(RTLD_DEFAULT, "__android_log_buf_write")); + g_crash_log_initialized = true; +} + +int writeToCrashLog(const char* buf) { + // Try writing to the crash log ring buffer. If not available, fall back to + // the standard log buffer. + if (g_android_log_buf_write) { + return g_android_log_buf_write(kAndroidCrashLogId, ANDROID_LOG_FATAL, + kAndroidLogTag, buf); + } + return __android_log_write(ANDROID_LOG_FATAL, kAndroidLogTag, buf); +} +#endif + +int write(const char* buf, size_t nbytes) { +#if defined(__ANDROID__) + return __android_log_write(ANDROID_LOG_WARN, kAndroidLogTag, buf); +#else + return sys_write(2, buf, nbytes); +#endif +} + +} // namespace logger diff --git a/shared/sentry/external/breakpad/src/client/linux/log/log.h b/shared/sentry/external/breakpad/src/client/linux/log/log.h new file mode 100644 index 000000000..f94bbd5fb --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/log/log.h @@ -0,0 +1,55 @@ +// 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 CLIENT_LINUX_LOG_LOG_H_ +#define CLIENT_LINUX_LOG_LOG_H_ + +#include + +namespace logger { + +int write(const char* buf, size_t nbytes); + +// In the case of Android the log can be written to the default system log +// (default behavior of write() above, or to the crash log (see +// writeToCrashLog() below). +#if defined(__ANDROID__) + +// The logger must be initialized in a non-compromised context. +void initializeCrashLogWriter(); + +// Once initialized, writeToCrashLog is safe to use in a compromised context, +// even if the initialization failed, in which case this will silently fall +// back on write(). +int writeToCrashLog(const char* buf); +#endif + +} // namespace logger + +#endif // CLIENT_LINUX_LOG_LOG_H_ diff --git a/shared/sentry/external/breakpad/src/client/linux/microdump_writer/microdump_writer.cc b/shared/sentry/external/breakpad/src/client/linux/microdump_writer/microdump_writer.cc new file mode 100644 index 000000000..22edb1b8d --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/microdump_writer/microdump_writer.cc @@ -0,0 +1,664 @@ +// 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. + +// This translation unit generates microdumps into the console (logcat on +// Android). See crbug.com/410294 for more info and design docs. + +#include "client/linux/microdump_writer/microdump_writer.h" + +#include + +#include + +#include "client/linux/dump_writer_common/thread_info.h" +#include "client/linux/dump_writer_common/ucontext_reader.h" +#include "client/linux/handler/exception_handler.h" +#include "client/linux/handler/microdump_extra_info.h" +#include "client/linux/log/log.h" +#include "client/linux/minidump_writer/linux_ptrace_dumper.h" +#include "common/linux/file_id.h" +#include "common/linux/linux_libc_support.h" +#include "common/memory_allocator.h" + +namespace { + +using google_breakpad::auto_wasteful_vector; +using google_breakpad::elf::kDefaultBuildIdSize; +using google_breakpad::ExceptionHandler; +using google_breakpad::LinuxDumper; +using google_breakpad::LinuxPtraceDumper; +using google_breakpad::MappingInfo; +using google_breakpad::MappingList; +using google_breakpad::MicrodumpExtraInfo; +using google_breakpad::RawContextCPU; +using google_breakpad::ThreadInfo; +using google_breakpad::UContextReader; + +const size_t kLineBufferSize = 2048; + +#if !defined(__LP64__) +// The following are only used by DumpFreeSpace, so need to be compiled +// in conditionally in the same way. + +template +Dst saturated_cast(Src src) { + if (src >= std::numeric_limits::max()) + return std::numeric_limits::max(); + if (src <= std::numeric_limits::min()) + return std::numeric_limits::min(); + return static_cast(src); +} + +int Log2Floor(uint64_t n) { + // Copied from chromium src/base/bits.h + if (n == 0) + return -1; + int log = 0; + uint64_t value = n; + for (int i = 5; i >= 0; --i) { + int shift = (1 << i); + uint64_t x = value >> shift; + if (x != 0) { + value = x; + log += shift; + } + } + assert(value == 1u); + return log; +} + +bool MappingsAreAdjacent(const MappingInfo& a, const MappingInfo& b) { + // Because of load biasing, we can end up with a situation where two + // mappings actually overlap. So we will define adjacency to also include a + // b start address that lies within a's address range (including starting + // immediately after a). + // Because load biasing only ever moves the start address backwards, the end + // address should still increase. + return a.start_addr <= b.start_addr && a.start_addr + a.size >= b.start_addr; +} + +bool MappingLessThan(const MappingInfo* a, const MappingInfo* b) { + // Return true if mapping a is before mapping b. + // For the same reason (load biasing) we compare end addresses, which - unlike + // start addresses - will not have been modified. + return a->start_addr + a->size < b->start_addr + b->size; +} + +size_t NextOrderedMapping( + const google_breakpad::wasteful_vector& mappings, + size_t curr) { + // Find the mapping that directly follows mappings[curr]. + // If no such mapping exists, return |invalid| to indicate this. + const size_t invalid = std::numeric_limits::max(); + size_t best = invalid; + for (size_t next = 0; next < mappings.size(); ++next) { + if (MappingLessThan(mappings[curr], mappings[next]) && + (best == invalid || MappingLessThan(mappings[next], mappings[best]))) { + best = next; + } + } + return best; +} + +#endif // !__LP64__ + +class MicrodumpWriter { + public: + MicrodumpWriter(const ExceptionHandler::CrashContext* context, + const MappingList& mappings, + bool skip_dump_if_principal_mapping_not_referenced, + uintptr_t address_within_principal_mapping, + bool sanitize_stack, + const MicrodumpExtraInfo& microdump_extra_info, + LinuxDumper* dumper) + : ucontext_(context ? &context->context : NULL), +#if !defined(__ARM_EABI__) && !defined(__mips__) + float_state_(context ? &context->float_state : NULL), +#endif + dumper_(dumper), + mapping_list_(mappings), + skip_dump_if_principal_mapping_not_referenced_( + skip_dump_if_principal_mapping_not_referenced), + address_within_principal_mapping_(address_within_principal_mapping), + sanitize_stack_(sanitize_stack), + microdump_extra_info_(microdump_extra_info), + log_line_(NULL), + stack_copy_(NULL), + stack_len_(0), + stack_lower_bound_(0), + stack_pointer_(0) { + log_line_ = reinterpret_cast(Alloc(kLineBufferSize)); + if (log_line_) + log_line_[0] = '\0'; // Clear out the log line buffer. + } + + ~MicrodumpWriter() { dumper_->ThreadsResume(); } + + bool Init() { + // In the exceptional case where the system was out of memory and there + // wasn't even room to allocate the line buffer, bail out. There is nothing + // useful we can possibly achieve without the ability to Log. At least let's + // try to not crash. + if (!dumper_->Init() || !log_line_) + return false; + return dumper_->ThreadsSuspend() && dumper_->LateInit(); + } + + void Dump() { + CaptureResult stack_capture_result = CaptureCrashingThreadStack(-1); + if (stack_capture_result == CAPTURE_UNINTERESTING) { + LogLine("Microdump skipped (uninteresting)"); + return; + } + + LogLine("-----BEGIN BREAKPAD MICRODUMP-----"); + DumpProductInformation(); + DumpOSInformation(); + DumpProcessType(); + DumpCrashReason(); + DumpGPUInformation(); +#if !defined(__LP64__) + DumpFreeSpace(); +#endif + if (stack_capture_result == CAPTURE_OK) + DumpThreadStack(); + DumpCPUState(); + DumpMappings(); + LogLine("-----END BREAKPAD MICRODUMP-----"); + } + + private: + enum CaptureResult { CAPTURE_OK, CAPTURE_FAILED, CAPTURE_UNINTERESTING }; + + // Writes one line to the system log. + void LogLine(const char* msg) { +#if defined(__ANDROID__) + logger::writeToCrashLog(msg); +#else + logger::write(msg, my_strlen(msg)); + logger::write("\n", 1); +#endif + } + + // Stages the given string in the current line buffer. + void LogAppend(const char* str) { + my_strlcat(log_line_, str, kLineBufferSize); + } + + // As above (required to take precedence over template specialization below). + void LogAppend(char* str) { + LogAppend(const_cast(str)); + } + + // Stages the hex repr. of the given int type in the current line buffer. + template + void LogAppend(T value) { + // Make enough room to hex encode the largest int type + NUL. + static const char HEX[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', + 'A', 'B', 'C', 'D', 'E', 'F'}; + char hexstr[sizeof(T) * 2 + 1]; + for (int i = sizeof(T) * 2 - 1; i >= 0; --i, value >>= 4) + hexstr[i] = HEX[static_cast(value) & 0x0F]; + hexstr[sizeof(T) * 2] = '\0'; + LogAppend(hexstr); + } + + // Stages the buffer content hex-encoded in the current line buffer. + void LogAppend(const void* buf, size_t length) { + const uint8_t* ptr = reinterpret_cast(buf); + for (size_t i = 0; i < length; ++i, ++ptr) + LogAppend(*ptr); + } + + // Writes out the current line buffer on the system log. + void LogCommitLine() { + LogLine(log_line_); + log_line_[0] = 0; + } + + CaptureResult CaptureCrashingThreadStack(int max_stack_len) { + stack_pointer_ = UContextReader::GetStackPointer(ucontext_); + + if (!dumper_->GetStackInfo(reinterpret_cast(&stack_lower_bound_), + &stack_len_, stack_pointer_)) { + return CAPTURE_FAILED; + } + + if (max_stack_len >= 0 && + stack_len_ > static_cast(max_stack_len)) { + stack_len_ = max_stack_len; + } + + stack_copy_ = reinterpret_cast(Alloc(stack_len_)); + dumper_->CopyFromProcess(stack_copy_, dumper_->crash_thread(), + reinterpret_cast(stack_lower_bound_), + stack_len_); + + if (!skip_dump_if_principal_mapping_not_referenced_) return CAPTURE_OK; + + const MappingInfo* principal_mapping = + dumper_->FindMappingNoBias(address_within_principal_mapping_); + if (!principal_mapping) return CAPTURE_UNINTERESTING; + + uintptr_t low_addr = principal_mapping->system_mapping_info.start_addr; + uintptr_t high_addr = principal_mapping->system_mapping_info.end_addr; + uintptr_t pc = UContextReader::GetInstructionPointer(ucontext_); + if (low_addr <= pc && pc <= high_addr) return CAPTURE_OK; + + if (dumper_->StackHasPointerToMapping(stack_copy_, stack_len_, + stack_pointer_ - stack_lower_bound_, + *principal_mapping)) { + return CAPTURE_OK; + } + return CAPTURE_UNINTERESTING; + } + + void DumpProductInformation() { + LogAppend("V "); + if (microdump_extra_info_.product_info) { + LogAppend(microdump_extra_info_.product_info); + } else { + LogAppend("UNKNOWN:0.0.0.0"); + } + LogCommitLine(); + } + + void DumpProcessType() { + LogAppend("P "); + if (microdump_extra_info_.process_type) { + LogAppend(microdump_extra_info_.process_type); + } else { + LogAppend("UNKNOWN"); + } + LogCommitLine(); + } + + void DumpCrashReason() { + LogAppend("R "); + LogAppend(dumper_->crash_signal()); + LogAppend(" "); + LogAppend(dumper_->GetCrashSignalString()); + LogAppend(" "); + LogAppend(dumper_->crash_address()); + LogCommitLine(); + } + + void DumpOSInformation() { + const uint8_t n_cpus = static_cast(sysconf(_SC_NPROCESSORS_CONF)); + +#if defined(__ANDROID__) + const char kOSId[] = "A"; +#else + const char kOSId[] = "L"; +#endif + +// Dump the runtime architecture. On multiarch devices it might not match the +// hw architecture (the one returned by uname()), for instance in the case of +// a 32-bit app running on a aarch64 device. +#if defined(__aarch64__) + const char kArch[] = "arm64"; +#elif defined(__ARMEL__) + const char kArch[] = "arm"; +#elif defined(__x86_64__) + const char kArch[] = "x86_64"; +#elif defined(__i386__) + const char kArch[] = "x86"; +#elif defined(__mips__) +# if _MIPS_SIM == _ABIO32 + const char kArch[] = "mips"; +# elif _MIPS_SIM == _ABI64 + const char kArch[] = "mips64"; +# else +# error "This mips ABI is currently not supported (n32)" +#endif +#else +#error "This code has not been ported to your platform yet" +#endif + + LogAppend("O "); + LogAppend(kOSId); + LogAppend(" "); + LogAppend(kArch); + LogAppend(" "); + LogAppend(n_cpus); + LogAppend(" "); + + // Dump the HW architecture (e.g., armv7l, aarch64). + struct utsname uts; + const bool has_uts_info = (uname(&uts) == 0); + const char* hwArch = has_uts_info ? uts.machine : "unknown_hw_arch"; + LogAppend(hwArch); + LogAppend(" "); + + // If the client has attached a build fingerprint to the MinidumpDescriptor + // use that one. Otherwise try to get some basic info from uname(). + if (microdump_extra_info_.build_fingerprint) { + LogAppend(microdump_extra_info_.build_fingerprint); + } else if (has_uts_info) { + LogAppend(uts.release); + LogAppend(" "); + LogAppend(uts.version); + } else { + LogAppend("no build fingerprint available"); + } + LogCommitLine(); + } + + void DumpGPUInformation() { + LogAppend("G "); + if (microdump_extra_info_.gpu_fingerprint) { + LogAppend(microdump_extra_info_.gpu_fingerprint); + } else { + LogAppend("UNKNOWN"); + } + LogCommitLine(); + } + + void DumpThreadStack() { + if (sanitize_stack_) { + dumper_->SanitizeStackCopy(stack_copy_, stack_len_, stack_pointer_, + stack_pointer_ - stack_lower_bound_); + } + + LogAppend("S 0 "); + LogAppend(stack_pointer_); + LogAppend(" "); + LogAppend(stack_lower_bound_); + LogAppend(" "); + LogAppend(stack_len_); + LogCommitLine(); + + const size_t STACK_DUMP_CHUNK_SIZE = 384; + for (size_t stack_off = 0; stack_off < stack_len_; + stack_off += STACK_DUMP_CHUNK_SIZE) { + LogAppend("S "); + LogAppend(stack_lower_bound_ + stack_off); + LogAppend(" "); + LogAppend(stack_copy_ + stack_off, + std::min(STACK_DUMP_CHUNK_SIZE, stack_len_ - stack_off)); + LogCommitLine(); + } + } + + void DumpCPUState() { + RawContextCPU cpu; + my_memset(&cpu, 0, sizeof(RawContextCPU)); +#if !defined(__ARM_EABI__) && !defined(__mips__) + UContextReader::FillCPUContext(&cpu, ucontext_, float_state_); +#else + UContextReader::FillCPUContext(&cpu, ucontext_); +#endif + LogAppend("C "); + LogAppend(&cpu, sizeof(cpu)); + LogCommitLine(); + } + + // If there is caller-provided information about this mapping + // in the mapping_list_ list, return true. Otherwise, return false. + bool HaveMappingInfo(const MappingInfo& mapping) { + for (MappingList::const_iterator iter = mapping_list_.begin(); + iter != mapping_list_.end(); + ++iter) { + // Ignore any mappings that are wholly contained within + // mappings in the mapping_info_ list. + if (mapping.start_addr >= iter->first.start_addr && + (mapping.start_addr + mapping.size) <= + (iter->first.start_addr + iter->first.size)) { + return true; + } + } + return false; + } + + // Dump information about the provided |mapping|. If |identifier| is non-NULL, + // use it instead of calculating a file ID from the mapping. + void DumpModule(const MappingInfo& mapping, + bool member, + unsigned int mapping_id, + const uint8_t* identifier) { + + auto_wasteful_vector identifier_bytes( + dumper_->allocator()); + + if (identifier) { + // GUID was provided by caller. + identifier_bytes.insert(identifier_bytes.end(), + identifier, + identifier + sizeof(MDGUID)); + } else { + dumper_->ElfFileIdentifierForMapping( + mapping, + member, + mapping_id, + identifier_bytes); + } + + // Copy as many bytes of |identifier| as will fit into a MDGUID + MDGUID module_identifier = {0}; + memcpy(&module_identifier, &identifier_bytes[0], + std::min(sizeof(MDGUID), identifier_bytes.size())); + + char file_name[NAME_MAX]; + char file_path[NAME_MAX]; + dumper_->GetMappingEffectiveNameAndPath( + mapping, file_path, sizeof(file_path), file_name, sizeof(file_name)); + + LogAppend("M "); + LogAppend(static_cast(mapping.start_addr)); + LogAppend(" "); + LogAppend(mapping.offset); + LogAppend(" "); + LogAppend(mapping.size); + LogAppend(" "); + LogAppend(module_identifier.data1); + LogAppend(module_identifier.data2); + LogAppend(module_identifier.data3); + LogAppend(module_identifier.data4[0]); + LogAppend(module_identifier.data4[1]); + LogAppend(module_identifier.data4[2]); + LogAppend(module_identifier.data4[3]); + LogAppend(module_identifier.data4[4]); + LogAppend(module_identifier.data4[5]); + LogAppend(module_identifier.data4[6]); + LogAppend(module_identifier.data4[7]); + LogAppend("0 "); // Age is always 0 on Linux. + LogAppend(file_name); + LogCommitLine(); + } + +#if !defined(__LP64__) + void DumpFreeSpace() { + const MappingInfo* stack_mapping = nullptr; + ThreadInfo info; + if (dumper_->GetThreadInfoByIndex(dumper_->GetMainThreadIndex(), &info)) { + stack_mapping = dumper_->FindMappingNoBias(info.stack_pointer); + } + + const google_breakpad::wasteful_vector& mappings = + dumper_->mappings(); + if (mappings.size() == 0) return; + + // This is complicated by the fact that mappings is not in order. It should + // be mostly in order, however the mapping that contains the entry point for + // the process is always at the front of the vector. + + static const int HBITS = sizeof(size_t) * 8; + size_t hole_histogram[HBITS]; + my_memset(hole_histogram, 0, sizeof(hole_histogram)); + + // Find the lowest address mapping. + size_t curr = 0; + for (size_t i = 1; i < mappings.size(); ++i) { + if (mappings[i]->start_addr < mappings[curr]->start_addr) curr = i; + } + + uintptr_t lo_addr = mappings[curr]->start_addr; + + size_t hole_cnt = 0; + size_t hole_max = 0; + size_t hole_sum = 0; + + while (true) { + // Skip to the end of an adjacent run of mappings. This is an optimization + // for the fact that mappings is mostly sorted. + while (curr != mappings.size() - 1 && + MappingsAreAdjacent(*mappings[curr], *mappings[curr + 1])) { + ++curr; + } + + if (mappings[curr] == stack_mapping) { + // Because we can't determine the top of userspace mappable + // memory we treat the start of the process stack as the top + // of the allocatable address space. Once we reach + // |stack_mapping| we are done scanning for free space regions. + break; + } + + size_t next = NextOrderedMapping(mappings, curr); + if (next == std::numeric_limits::max()) + break; + + uintptr_t hole_lo = mappings[curr]->start_addr + mappings[curr]->size; + uintptr_t hole_hi = mappings[next]->start_addr; + + if (hole_hi > hole_lo) { + size_t hole_sz = hole_hi - hole_lo; + hole_sum += hole_sz; + hole_max = std::max(hole_sz, hole_max); + ++hole_cnt; + ++hole_histogram[Log2Floor(hole_sz)]; + } + curr = next; + } + + uintptr_t hi_addr = mappings[curr]->start_addr + mappings[curr]->size; + + LogAppend("H "); + LogAppend(lo_addr); + LogAppend(" "); + LogAppend(hi_addr); + LogAppend(" "); + LogAppend(saturated_cast(hole_cnt)); + LogAppend(" "); + LogAppend(hole_max); + LogAppend(" "); + LogAppend(hole_sum); + for (unsigned int i = 0; i < HBITS; ++i) { + if (!hole_histogram[i]) continue; + LogAppend(" "); + LogAppend(saturated_cast(i)); + LogAppend(":"); + LogAppend(saturated_cast(hole_histogram[i])); + } + LogCommitLine(); + } +#endif + + // Write information about the mappings in effect. + void DumpMappings() { + // First write all the mappings from the dumper + for (unsigned i = 0; i < dumper_->mappings().size(); ++i) { + const MappingInfo& mapping = *dumper_->mappings()[i]; + if (mapping.name[0] == 0 || // only want modules with filenames. + !mapping.exec || // only want executable mappings. + mapping.size < 4096 || // too small to get a signature for. + HaveMappingInfo(mapping)) { + continue; + } + + DumpModule(mapping, true, i, NULL); + } + // Next write all the mappings provided by the caller + for (MappingList::const_iterator iter = mapping_list_.begin(); + iter != mapping_list_.end(); + ++iter) { + DumpModule(iter->first, false, 0, iter->second); + } + } + + void* Alloc(unsigned bytes) { return dumper_->allocator()->Alloc(bytes); } + + const ucontext_t* const ucontext_; +#if !defined(__ARM_EABI__) && !defined(__mips__) + const google_breakpad::fpstate_t* const float_state_; +#endif + LinuxDumper* dumper_; + const MappingList& mapping_list_; + bool skip_dump_if_principal_mapping_not_referenced_; + uintptr_t address_within_principal_mapping_; + bool sanitize_stack_; + const MicrodumpExtraInfo microdump_extra_info_; + char* log_line_; + + // The local copy of crashed process stack memory, beginning at + // |stack_lower_bound_|. + uint8_t* stack_copy_; + + // The length of crashed process stack copy. + size_t stack_len_; + + // The address of the page containing the stack pointer in the + // crashed process. |stack_lower_bound_| <= |stack_pointer_| + uintptr_t stack_lower_bound_; + + // The stack pointer of the crashed thread. + uintptr_t stack_pointer_; +}; +} // namespace + +namespace google_breakpad { + +bool WriteMicrodump(pid_t crashing_process, + const void* blob, + size_t blob_size, + const MappingList& mappings, + bool skip_dump_if_principal_mapping_not_referenced, + uintptr_t address_within_principal_mapping, + bool sanitize_stack, + const MicrodumpExtraInfo& microdump_extra_info) { + LinuxPtraceDumper dumper(crashing_process); + const ExceptionHandler::CrashContext* context = NULL; + if (blob) { + if (blob_size != sizeof(ExceptionHandler::CrashContext)) + return false; + context = reinterpret_cast(blob); + dumper.SetCrashInfoFromSigInfo(context->siginfo); + dumper.set_crash_thread(context->tid); + } + MicrodumpWriter writer(context, mappings, + skip_dump_if_principal_mapping_not_referenced, + address_within_principal_mapping, sanitize_stack, + microdump_extra_info, &dumper); + if (!writer.Init()) + return false; + writer.Dump(); + return true; +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/linux/microdump_writer/microdump_writer.h b/shared/sentry/external/breakpad/src/client/linux/microdump_writer/microdump_writer.h new file mode 100644 index 000000000..a1e53df62 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/microdump_writer/microdump_writer.h @@ -0,0 +1,68 @@ +// 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 CLIENT_LINUX_MINIDUMP_WRITER_MICRODUMP_WRITER_H_ +#define CLIENT_LINUX_MINIDUMP_WRITER_MICRODUMP_WRITER_H_ + +#include +#include + +#include "client/linux/dump_writer_common/mapping_info.h" + +namespace google_breakpad { + +struct MicrodumpExtraInfo; + +// Writes a microdump (a reduced dump containing only the state of the crashing +// thread) on the console (logcat on Android). These functions do not malloc nor +// use libc functions which may. Thus, it can be used in contexts where the +// state of the heap may be corrupt. +// Args: +// crashing_process: the pid of the crashing process. This must be trusted. +// blob: a blob of data from the crashing process. See exception_handler.h +// blob_size: the length of |blob| in bytes. +// mappings: a list of additional mappings provided by the application. +// build_fingerprint: a (optional) C string which determines the OS +// build fingerprint (e.g., aosp/occam/mako:5.1.1/LMY47W/1234:eng/dev-keys). +// product_info: a (optional) C string which determines the product name and +// version (e.g., WebView:42.0.2311.136). +// +// Returns true iff successful. +bool WriteMicrodump(pid_t crashing_process, + const void* blob, + size_t blob_size, + const MappingList& mappings, + bool skip_dump_if_main_module_not_referenced, + uintptr_t address_within_main_module, + bool sanitize_stack, + const MicrodumpExtraInfo& microdump_extra_info); + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_MINIDUMP_WRITER_MICRODUMP_WRITER_H_ diff --git a/shared/sentry/external/breakpad/src/client/linux/microdump_writer/microdump_writer_unittest.cc b/shared/sentry/external/breakpad/src/client/linux/microdump_writer/microdump_writer_unittest.cc new file mode 100644 index 000000000..6339ac0cd --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/microdump_writer/microdump_writer_unittest.cc @@ -0,0 +1,421 @@ +// 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 +#include +#include +#include +#include + +#include +#include + +#include "breakpad_googletest_includes.h" +#include "client/linux/handler/exception_handler.h" +#include "client/linux/handler/microdump_extra_info.h" +#include "client/linux/microdump_writer/microdump_writer.h" +#include "common/linux/breakpad_getcontext.h" +#include "common/linux/eintr_wrapper.h" +#include "common/linux/ignore_ret.h" +#include "common/scoped_ptr.h" +#include "common/tests/auto_tempdir.h" +#include "common/using_std_string.h" + +using namespace google_breakpad; + +extern "C" { +extern char __executable_start; +extern char __etext; +} + +namespace { + +typedef testing::Test MicrodumpWriterTest; + +MicrodumpExtraInfo MakeMicrodumpExtraInfo( + const char* build_fingerprint, + const char* product_info, + const char* gpu_fingerprint) { + MicrodumpExtraInfo info; + info.build_fingerprint = build_fingerprint; + info.product_info = product_info; + info.gpu_fingerprint = gpu_fingerprint; + info.process_type = "Browser"; + return info; +} + +bool ContainsMicrodump(const std::string& buf) { + return std::string::npos != buf.find("-----BEGIN BREAKPAD MICRODUMP-----") && + std::string::npos != buf.find("-----END BREAKPAD MICRODUMP-----"); +} + +const char kIdentifiableString[] = "_IDENTIFIABLE_"; +const uintptr_t kCrashAddress = 0xdeaddeadu; + +void CrashAndGetMicrodump(const MappingList& mappings, + const MicrodumpExtraInfo& microdump_extra_info, + std::string* microdump, + bool skip_dump_if_principal_mapping_not_referenced = false, + uintptr_t address_within_principal_mapping = 0, + bool sanitize_stack = false) { + int fds[2]; + ASSERT_NE(-1, pipe(fds)); + + AutoTempDir temp_dir; + string stderr_file = temp_dir.path() + "/stderr.log"; + int err_fd = open(stderr_file.c_str(), O_CREAT | O_RDWR, S_IRUSR | S_IWUSR); + ASSERT_NE(-1, err_fd); + + char identifiable_string[sizeof(kIdentifiableString)]; + + // This string should not appear in the resulting microdump if it + // has been sanitized. + strcpy(identifiable_string, kIdentifiableString); + // Force the strcpy to not be optimized away. + IGNORE_RET(write(STDOUT_FILENO, identifiable_string, 0)); + + const pid_t child = fork(); + if (child == 0) { + close(fds[1]); + char b; + IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b)))); + close(fds[0]); + syscall(__NR_exit); + } + close(fds[0]); + + ExceptionHandler::CrashContext context; + memset(&context, 0, sizeof(context)); + // Pretend the current context is the child context (which is + // approximately right) so that we have a valid stack pointer, and + // can fetch child stack data via ptrace. + getcontext(&context.context); + // Set a non-zero tid to avoid tripping asserts. + context.tid = child; + context.siginfo.si_signo = MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED; + context.siginfo.si_addr = reinterpret_cast(kCrashAddress); + + // Redirect temporarily stderr to the stderr.log file. + int save_err = dup(STDERR_FILENO); + ASSERT_NE(-1, save_err); + ASSERT_NE(-1, dup2(err_fd, STDERR_FILENO)); + + ASSERT_TRUE(WriteMicrodump(child, &context, sizeof(context), mappings, + skip_dump_if_principal_mapping_not_referenced, + address_within_principal_mapping, sanitize_stack, + microdump_extra_info)); + + // Revert stderr back to the console. + dup2(save_err, STDERR_FILENO); + close(save_err); + + // Read back the stderr file and check for the microdump marker. + fsync(err_fd); + lseek(err_fd, 0, SEEK_SET); + + microdump->clear(); + char buf[1024]; + + while (true) { + int bytes_read = IGNORE_EINTR(read(err_fd, buf, 1024)); + if (bytes_read <= 0) break; + microdump->append(buf, buf + bytes_read); + } + close(err_fd); + close(fds[1]); +} + +void ExtractMicrodumpStackContents(const string& microdump_content, + string* result) { + std::istringstream iss(microdump_content); + result->clear(); + for (string line; std::getline(iss, line);) { + if (line.find("S ") == 0) { + std::istringstream stack_data(line); + std::string key; + std::string addr; + std::string data; + stack_data >> key >> addr >> data; + EXPECT_TRUE((data.size() & 1u) == 0u); + result->reserve(result->size() + data.size() / 2); + for (size_t i = 0; i < data.size(); i += 2) { + std::string byte = data.substr(i, 2); + result->push_back(static_cast(strtoul(byte.c_str(), NULL, 16))); + } + } + } +} + +void CheckMicrodumpContents(const string& microdump_content, + const MicrodumpExtraInfo& expected_info) { + std::istringstream iss(microdump_content); + bool did_find_os_info = false; + bool did_find_product_info = false; + bool did_find_process_type = false; + bool did_find_crash_reason = false; + bool did_find_gpu_info = false; + for (string line; std::getline(iss, line);) { + if (line.find("O ") == 0) { + std::istringstream os_info_tokens(line); + string token; + os_info_tokens.ignore(2); // Ignore the "O " preamble. + // Check the OS descriptor char (L=Linux, A=Android). + os_info_tokens >> token; + ASSERT_TRUE(token == "L" || token == "A"); + + os_info_tokens >> token; // HW architecture. + os_info_tokens >> token; // Number of cpus. + for (size_t i = 0; i < token.size(); ++i) + ASSERT_TRUE(isxdigit(token[i])); + os_info_tokens >> token; // SW architecture. + + // Check that the build fingerprint is in the right place. + os_info_tokens >> token; + ASSERT_FALSE(os_info_tokens.fail()); + if (expected_info.build_fingerprint) + ASSERT_EQ(expected_info.build_fingerprint, token); + did_find_os_info = true; + } else if (line.find("P ") == 0) { + if (expected_info.process_type) + ASSERT_EQ(string("P ") + expected_info.process_type, line); + did_find_process_type = true; + } else if (line.find("R ") == 0) { + std::istringstream crash_reason_tokens(line); + string token; + unsigned crash_reason; + string crash_reason_str; + uintptr_t crash_address; + crash_reason_tokens.ignore(2); // Ignore the "R " preamble. + crash_reason_tokens >> std::hex >> crash_reason >> crash_reason_str >> + crash_address; + ASSERT_FALSE(crash_reason_tokens.fail()); + ASSERT_EQ(MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED, crash_reason); + ASSERT_EQ("DUMP_REQUESTED", crash_reason_str); + ASSERT_EQ(kCrashAddress, crash_address); + did_find_crash_reason = true; + } else if (line.find("V ") == 0) { + if (expected_info.product_info) + ASSERT_EQ(string("V ") + expected_info.product_info, line); + did_find_product_info = true; + } else if (line.find("G ") == 0) { + if (expected_info.gpu_fingerprint) + ASSERT_EQ(string("G ") + expected_info.gpu_fingerprint, line); + did_find_gpu_info = true; + } + } + ASSERT_TRUE(did_find_os_info); + ASSERT_TRUE(did_find_product_info); + ASSERT_TRUE(did_find_process_type); + ASSERT_TRUE(did_find_crash_reason); + ASSERT_TRUE(did_find_gpu_info); +} + +bool MicrodumpStackContains(const string& microdump_content, + const string& expected_content) { + string result; + ExtractMicrodumpStackContents(microdump_content, &result); + return result.find(kIdentifiableString) != string::npos; +} + +void CheckMicrodumpContents(const string& microdump_content, + const string& expected_fingerprint, + const string& expected_product_info, + const string& expected_gpu_fingerprint) { + CheckMicrodumpContents( + microdump_content, + MakeMicrodumpExtraInfo(expected_fingerprint.c_str(), + expected_product_info.c_str(), + expected_gpu_fingerprint.c_str())); +} + +TEST(MicrodumpWriterTest, BasicWithMappings) { + // Push some extra mapping to check the MappingList logic. + const uint32_t memory_size = sysconf(_SC_PAGESIZE); + const char* kMemoryName = "libfoo.so"; + const uint8_t kModuleGUID[sizeof(MDGUID)] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF + }; + + MappingInfo info; + info.start_addr = memory_size; + info.size = memory_size; + info.offset = 42; + strcpy(info.name, kMemoryName); + + MappingList mappings; + MappingEntry mapping; + mapping.first = info; + memcpy(mapping.second, kModuleGUID, sizeof(MDGUID)); + mappings.push_back(mapping); + + std::string buf; + CrashAndGetMicrodump(mappings, MicrodumpExtraInfo(), &buf); + ASSERT_TRUE(ContainsMicrodump(buf)); + +#ifdef __LP64__ + ASSERT_NE(std::string::npos, + buf.find("M 0000000000001000 000000000000002A 0000000000001000 " + "33221100554477668899AABBCCDDEEFF0 libfoo.so")); +#else + ASSERT_NE(std::string::npos, + buf.find("M 00001000 0000002A 00001000 " + "33221100554477668899AABBCCDDEEFF0 libfoo.so")); +#endif + + // In absence of a product info in the minidump, the writer should just write + // an unknown marker. + ASSERT_NE(std::string::npos, buf.find("V UNKNOWN:0.0.0.0")); +} + +// Ensure that no output occurs if the interest region is set, but +// doesn't overlap anything on the stack. +TEST(MicrodumpWriterTest, NoOutputIfUninteresting) { + const char kProductInfo[] = "MockProduct:42.0.2311.99"; + const char kBuildFingerprint[] = + "aosp/occam/mako:5.1.1/LMY47W/12345678:userdegbug/dev-keys"; + const char kGPUFingerprint[] = + "Qualcomm;Adreno (TM) 330;OpenGL ES 3.0 V@104.0 AU@ (GIT@Id3510ff6dc)"; + const MicrodumpExtraInfo kMicrodumpExtraInfo( + MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, kGPUFingerprint)); + + std::string buf; + MappingList no_mappings; + + CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf, true, 0); + ASSERT_FALSE(ContainsMicrodump(buf)); +} + +// Ensure that stack content does not contain an identifiable string if the +// stack is sanitized. +TEST(MicrodumpWriterTest, StringRemovedBySanitization) { + const char kProductInfo[] = "MockProduct:42.0.2311.99"; + const char kBuildFingerprint[] = + "aosp/occam/mako:5.1.1/LMY47W/12345678:userdegbug/dev-keys"; + const char kGPUFingerprint[] = + "Qualcomm;Adreno (TM) 330;OpenGL ES 3.0 V@104.0 AU@ (GIT@Id3510ff6dc)"; + + const MicrodumpExtraInfo kMicrodumpExtraInfo( + MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, kGPUFingerprint)); + + std::string buf; + MappingList no_mappings; + + CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf, false, 0u, true); + ASSERT_TRUE(ContainsMicrodump(buf)); + ASSERT_FALSE(MicrodumpStackContains(buf, kIdentifiableString)); +} + +// Ensure that stack content does contain an identifiable string if the +// stack is not sanitized. +TEST(MicrodumpWriterTest, StringPresentIfNotSanitized) { + const char kProductInfo[] = "MockProduct:42.0.2311.99"; + const char kBuildFingerprint[] = + "aosp/occam/mako:5.1.1/LMY47W/12345678:userdegbug/dev-keys"; + const char kGPUFingerprint[] = + "Qualcomm;Adreno (TM) 330;OpenGL ES 3.0 V@104.0 AU@ (GIT@Id3510ff6dc)"; + + const MicrodumpExtraInfo kMicrodumpExtraInfo( + MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, kGPUFingerprint)); + + std::string buf; + MappingList no_mappings; + + CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf, false, 0u, false); + ASSERT_TRUE(ContainsMicrodump(buf)); + ASSERT_TRUE(MicrodumpStackContains(buf, kIdentifiableString)); +} + +// Ensure that output occurs if the interest region is set, and +// does overlap something on the stack. +TEST(MicrodumpWriterTest, OutputIfInteresting) { + const char kProductInfo[] = "MockProduct:42.0.2311.99"; + const char kBuildFingerprint[] = + "aosp/occam/mako:5.1.1/LMY47W/12345678:userdegbug/dev-keys"; + const char kGPUFingerprint[] = + "Qualcomm;Adreno (TM) 330;OpenGL ES 3.0 V@104.0 AU@ (GIT@Id3510ff6dc)"; + + const MicrodumpExtraInfo kMicrodumpExtraInfo( + MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, kGPUFingerprint)); + + std::string buf; + MappingList no_mappings; + + CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf, true, + reinterpret_cast(CrashAndGetMicrodump)); + ASSERT_TRUE(ContainsMicrodump(buf)); +} + +// Ensure that the product info and build fingerprint metadata show up in the +// final microdump if present. +TEST(MicrodumpWriterTest, BuildFingerprintAndProductInfo) { + const char kProductInfo[] = "MockProduct:42.0.2311.99"; + const char kBuildFingerprint[] = + "aosp/occam/mako:5.1.1/LMY47W/12345678:userdegbug/dev-keys"; + const char kGPUFingerprint[] = + "Qualcomm;Adreno (TM) 330;OpenGL ES 3.0 V@104.0 AU@ (GIT@Id3510ff6dc)"; + const MicrodumpExtraInfo kMicrodumpExtraInfo( + MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, kGPUFingerprint)); + std::string buf; + MappingList no_mappings; + + CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfo, &buf); + ASSERT_TRUE(ContainsMicrodump(buf)); + CheckMicrodumpContents(buf, kMicrodumpExtraInfo); +} + +TEST(MicrodumpWriterTest, NoProductInfo) { + const char kBuildFingerprint[] = "foobar"; + const char kGPUFingerprint[] = "bazqux"; + std::string buf; + MappingList no_mappings; + + const MicrodumpExtraInfo kMicrodumpExtraInfoNoProductInfo( + MakeMicrodumpExtraInfo(kBuildFingerprint, NULL, kGPUFingerprint)); + + CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfoNoProductInfo, &buf); + ASSERT_TRUE(ContainsMicrodump(buf)); + CheckMicrodumpContents(buf, kBuildFingerprint, "UNKNOWN:0.0.0.0", + kGPUFingerprint); +} + +TEST(MicrodumpWriterTest, NoGPUInfo) { + const char kProductInfo[] = "bazqux"; + const char kBuildFingerprint[] = "foobar"; + std::string buf; + MappingList no_mappings; + + const MicrodumpExtraInfo kMicrodumpExtraInfoNoGPUInfo( + MakeMicrodumpExtraInfo(kBuildFingerprint, kProductInfo, NULL)); + + CrashAndGetMicrodump(no_mappings, kMicrodumpExtraInfoNoGPUInfo, &buf); + ASSERT_TRUE(ContainsMicrodump(buf)); + CheckMicrodumpContents(buf, kBuildFingerprint, kProductInfo, "UNKNOWN"); +} +} // namespace diff --git a/shared/sentry/external/breakpad/src/client/linux/minidump_writer/cpu_set.h b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/cpu_set.h new file mode 100644 index 000000000..1cca9aa5a --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/cpu_set.h @@ -0,0 +1,144 @@ +// 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 CLIENT_LINUX_MINIDUMP_WRITER_CPU_SET_H_ +#define CLIENT_LINUX_MINIDUMP_WRITER_CPU_SET_H_ + +#include +#include +#include + +#include "common/linux/linux_libc_support.h" +#include "third_party/lss/linux_syscall_support.h" + +namespace google_breakpad { + +// Helper class used to model a set of CPUs, as read from sysfs +// files like /sys/devices/system/cpu/present +// See See http://www.kernel.org/doc/Documentation/cputopology.txt +class CpuSet { +public: + // The maximum number of supported CPUs. + static const size_t kMaxCpus = 1024; + + CpuSet() { + my_memset(mask_, 0, sizeof(mask_)); + } + + // Parse a sysfs file to extract the corresponding CPU set. + bool ParseSysFile(int fd) { + char buffer[512]; + int ret = sys_read(fd, buffer, sizeof(buffer)-1); + if (ret < 0) + return false; + + buffer[ret] = '\0'; + + // Expected format: comma-separated list of items, where each + // item can be a decimal integer, or two decimal integers separated + // by a dash. + // E.g.: + // 0 + // 0,1,2,3 + // 0-3 + // 1,10-23 + const char* p = buffer; + const char* p_end = p + ret; + while (p < p_end) { + // Skip leading space, if any + while (p < p_end && my_isspace(*p)) + p++; + + // Find start and size of current item. + const char* item = p; + size_t item_len = static_cast(p_end - p); + const char* item_next = + static_cast(my_memchr(p, ',', item_len)); + if (item_next != NULL) { + p = item_next + 1; + item_len = static_cast(item_next - item); + } else { + p = p_end; + item_next = p_end; + } + + // Ignore trailing spaces. + while (item_next > item && my_isspace(item_next[-1])) + item_next--; + + // skip empty items. + if (item_next == item) + continue; + + // read first decimal value. + uintptr_t start = 0; + const char* next = my_read_decimal_ptr(&start, item); + uintptr_t end = start; + if (*next == '-') + my_read_decimal_ptr(&end, next+1); + + while (start <= end) + SetBit(start++); + } + return true; + } + + // Intersect this CPU set with another one. + void IntersectWith(const CpuSet& other) { + for (size_t nn = 0; nn < kMaskWordCount; ++nn) + mask_[nn] &= other.mask_[nn]; + } + + // Return the number of CPUs in this set. + int GetCount() { + int result = 0; + for (size_t nn = 0; nn < kMaskWordCount; ++nn) { + result += __builtin_popcount(mask_[nn]); + } + return result; + } + +private: + void SetBit(uintptr_t index) { + size_t nn = static_cast(index); + if (nn < kMaxCpus) + mask_[nn / kMaskWordBits] |= (1U << (nn % kMaskWordBits)); + } + + typedef uint32_t MaskWordType; + static const size_t kMaskWordBits = 8*sizeof(MaskWordType); + static const size_t kMaskWordCount = + (kMaxCpus + kMaskWordBits - 1) / kMaskWordBits; + + MaskWordType mask_[kMaskWordCount]; +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_MINIDUMP_WRITER_CPU_SET_H_ diff --git a/shared/sentry/external/breakpad/src/client/linux/minidump_writer/cpu_set_unittest.cc b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/cpu_set_unittest.cc new file mode 100644 index 000000000..e2274bd17 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/cpu_set_unittest.cc @@ -0,0 +1,164 @@ +// 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. + +#include +#include +#include +#include +#include +#include + +#include "breakpad_googletest_includes.h" +#include "client/linux/minidump_writer/cpu_set.h" +#include "common/linux/tests/auto_testfile.h" + +using namespace google_breakpad; + +namespace { + +typedef testing::Test CpuSetTest; + +// Helper class to write test text file to a temporary file and return +// its file descriptor. +class ScopedTestFile : public AutoTestFile { +public: + explicit ScopedTestFile(const char* text) + : AutoTestFile("cpu_set", text) { + } +}; + +} + +TEST(CpuSetTest, EmptyCount) { + CpuSet set; + ASSERT_EQ(0, set.GetCount()); +} + +TEST(CpuSetTest, OneCpu) { + ScopedTestFile file("10"); + ASSERT_TRUE(file.IsOk()); + + CpuSet set; + ASSERT_TRUE(set.ParseSysFile(file.GetFd())); + ASSERT_EQ(1, set.GetCount()); +} + +TEST(CpuSetTest, OneCpuTerminated) { + ScopedTestFile file("10\n"); + ASSERT_TRUE(file.IsOk()); + + CpuSet set; + ASSERT_TRUE(set.ParseSysFile(file.GetFd())); + ASSERT_EQ(1, set.GetCount()); +} + +TEST(CpuSetTest, TwoCpusWithComma) { + ScopedTestFile file("1,10"); + ASSERT_TRUE(file.IsOk()); + + CpuSet set; + ASSERT_TRUE(set.ParseSysFile(file.GetFd())); + ASSERT_EQ(2, set.GetCount()); +} + +TEST(CpuSetTest, TwoCpusWithRange) { + ScopedTestFile file("1-2"); + ASSERT_TRUE(file.IsOk()); + + CpuSet set; + ASSERT_TRUE(set.ParseSysFile(file.GetFd())); + ASSERT_EQ(2, set.GetCount()); +} + +TEST(CpuSetTest, TenCpusWithRange) { + ScopedTestFile file("9-18"); + ASSERT_TRUE(file.IsOk()); + + CpuSet set; + ASSERT_TRUE(set.ParseSysFile(file.GetFd())); + ASSERT_EQ(10, set.GetCount()); +} + +TEST(CpuSetTest, MultiItems) { + ScopedTestFile file("0, 2-4, 128"); + ASSERT_TRUE(file.IsOk()); + + CpuSet set; + ASSERT_TRUE(set.ParseSysFile(file.GetFd())); + ASSERT_EQ(5, set.GetCount()); +} + +TEST(CpuSetTest, IntersectWith) { + ScopedTestFile file1("9-19"); + ASSERT_TRUE(file1.IsOk()); + CpuSet set1; + ASSERT_TRUE(set1.ParseSysFile(file1.GetFd())); + ASSERT_EQ(11, set1.GetCount()); + + ScopedTestFile file2("16-24"); + ASSERT_TRUE(file2.IsOk()); + CpuSet set2; + ASSERT_TRUE(set2.ParseSysFile(file2.GetFd())); + ASSERT_EQ(9, set2.GetCount()); + + set1.IntersectWith(set2); + ASSERT_EQ(4, set1.GetCount()); + ASSERT_EQ(9, set2.GetCount()); +} + +TEST(CpuSetTest, SelfIntersection) { + ScopedTestFile file1("9-19"); + ASSERT_TRUE(file1.IsOk()); + CpuSet set1; + ASSERT_TRUE(set1.ParseSysFile(file1.GetFd())); + ASSERT_EQ(11, set1.GetCount()); + + set1.IntersectWith(set1); + ASSERT_EQ(11, set1.GetCount()); +} + +TEST(CpuSetTest, EmptyIntersection) { + ScopedTestFile file1("0-19"); + ASSERT_TRUE(file1.IsOk()); + CpuSet set1; + ASSERT_TRUE(set1.ParseSysFile(file1.GetFd())); + ASSERT_EQ(20, set1.GetCount()); + + ScopedTestFile file2("20-39"); + ASSERT_TRUE(file2.IsOk()); + CpuSet set2; + ASSERT_TRUE(set2.ParseSysFile(file2.GetFd())); + ASSERT_EQ(20, set2.GetCount()); + + set1.IntersectWith(set2); + ASSERT_EQ(0, set1.GetCount()); + + ASSERT_EQ(20, set2.GetCount()); +} + diff --git a/shared/sentry/external/breakpad/src/client/linux/minidump_writer/directory_reader.h b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/directory_reader.h new file mode 100644 index 000000000..a4bde1803 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/directory_reader.h @@ -0,0 +1,106 @@ +// 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 CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_ +#define CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_ + +#include +#include +#include +#include +#include +#include + +#include "common/linux/linux_libc_support.h" +#include "third_party/lss/linux_syscall_support.h" + +namespace google_breakpad { + +// A class for enumerating a directory without using diropen/readdir or other +// functions which may allocate memory. +class DirectoryReader { + public: + DirectoryReader(int fd) + : fd_(fd), + buf_used_(0) { + } + + // Return the next entry from the directory + // name: (output) the NUL terminated entry name + // + // Returns true iff successful (false on EOF). + // + // After calling this, one must call |PopEntry| otherwise you'll get the same + // entry over and over. + bool GetNextEntry(const char** name) { + struct kernel_dirent* const dent = + reinterpret_cast(buf_); + + if (buf_used_ == 0) { + // need to read more entries. + const int n = sys_getdents(fd_, dent, sizeof(buf_)); + if (n < 0) { + return false; + } else if (n == 0) { + hit_eof_ = true; + } else { + buf_used_ += n; + } + } + + if (buf_used_ == 0 && hit_eof_) + return false; + + assert(buf_used_ > 0); + + *name = dent->d_name; + return true; + } + + void PopEntry() { + if (!buf_used_) + return; + + const struct kernel_dirent* const dent = + reinterpret_cast(buf_); + + buf_used_ -= dent->d_reclen; + my_memmove(buf_, buf_ + dent->d_reclen, buf_used_); + } + + private: + const int fd_; + bool hit_eof_; + unsigned buf_used_; + uint8_t buf_[sizeof(struct kernel_dirent) + NAME_MAX + 1]; +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_MINIDUMP_WRITER_DIRECTORY_READER_H_ diff --git a/shared/sentry/external/breakpad/src/client/linux/minidump_writer/directory_reader_unittest.cc b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/directory_reader_unittest.cc new file mode 100644 index 000000000..7292a6368 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/directory_reader_unittest.cc @@ -0,0 +1,78 @@ +// 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 +#include + +#include +#include +#include + +#include "client/linux/minidump_writer/directory_reader.h" +#include "common/using_std_string.h" +#include "breakpad_googletest_includes.h" + +using namespace google_breakpad; + +namespace { +typedef testing::Test DirectoryReaderTest; +} + +TEST(DirectoryReaderTest, CompareResults) { + std::set dent_set; + + DIR* const dir = opendir("/proc/self"); + ASSERT_TRUE(dir != NULL); + + struct dirent* dent; + while ((dent = readdir(dir))) + dent_set.insert(dent->d_name); + + closedir(dir); + + const int fd = open("/proc/self", O_DIRECTORY | O_RDONLY); + ASSERT_GE(fd, 0); + + DirectoryReader dir_reader(fd); + unsigned seen = 0; + + const char* name; + while (dir_reader.GetNextEntry(&name)) { + ASSERT_TRUE(dent_set.find(name) != dent_set.end()); + seen++; + dir_reader.PopEntry(); + } + + ASSERT_TRUE(dent_set.find("status") != dent_set.end()); + ASSERT_TRUE(dent_set.find("stat") != dent_set.end()); + ASSERT_TRUE(dent_set.find("cmdline") != dent_set.end()); + + ASSERT_EQ(dent_set.size(), seen); + close(fd); +} diff --git a/shared/sentry/external/breakpad/src/client/linux/minidump_writer/line_reader.h b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/line_reader.h new file mode 100644 index 000000000..9fc4b7cc8 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/line_reader.h @@ -0,0 +1,131 @@ +// 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 CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_ +#define CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_ + +#include +#include +#include + +#include "common/linux/linux_libc_support.h" +#include "third_party/lss/linux_syscall_support.h" + +namespace google_breakpad { + +// A class for reading a file, line by line, without using fopen/fgets or other +// functions which may allocate memory. +class LineReader { + public: + LineReader(int fd) + : fd_(fd), + hit_eof_(false), + buf_used_(0) { + } + + // The maximum length of a line. + static const size_t kMaxLineLen = 512; + + // Return the next line from the file. + // line: (output) a pointer to the start of the line. The line is NUL + // terminated. + // len: (output) the length of the line (not inc the NUL byte) + // + // Returns true iff successful (false on EOF). + // + // One must call |PopLine| after this function, otherwise you'll continue to + // get the same line over and over. + bool GetNextLine(const char** line, unsigned* len) { + for (;;) { + if (buf_used_ == 0 && hit_eof_) + return false; + + for (unsigned i = 0; i < buf_used_; ++i) { + if (buf_[i] == '\n' || buf_[i] == 0) { + buf_[i] = 0; + *len = i; + *line = buf_; + return true; + } + } + + if (buf_used_ == sizeof(buf_)) { + // we scanned the whole buffer and didn't find an end-of-line marker. + // This line is too long to process. + return false; + } + + // We didn't find any end-of-line terminators in the buffer. However, if + // this is the last line in the file it might not have one: + if (hit_eof_) { + assert(buf_used_); + // There's room for the NUL because of the buf_used_ == sizeof(buf_) + // check above. + buf_[buf_used_] = 0; + *len = buf_used_; + buf_used_ += 1; // since we appended the NUL. + *line = buf_; + return true; + } + + // Otherwise, we should pull in more data from the file + const ssize_t n = sys_read(fd_, buf_ + buf_used_, + sizeof(buf_) - buf_used_); + if (n < 0) { + return false; + } else if (n == 0) { + hit_eof_ = true; + } else { + buf_used_ += n; + } + + // At this point, we have either set the hit_eof_ flag, or we have more + // data to process... + } + } + + void PopLine(unsigned len) { + // len doesn't include the NUL byte at the end. + + assert(buf_used_ >= len + 1); + buf_used_ -= len + 1; + my_memmove(buf_, buf_ + len + 1, buf_used_); + } + + private: + const int fd_; + + bool hit_eof_; + unsigned buf_used_; + char buf_[kMaxLineLen]; +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_MINIDUMP_WRITER_LINE_READER_H_ diff --git a/shared/sentry/external/breakpad/src/client/linux/minidump_writer/line_reader_unittest.cc b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/line_reader_unittest.cc new file mode 100644 index 000000000..e1ac445ae --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/line_reader_unittest.cc @@ -0,0 +1,169 @@ +// 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 +#include +#include + +#include "client/linux/minidump_writer/line_reader.h" +#include "breakpad_googletest_includes.h" +#include "common/linux/tests/auto_testfile.h" + +using namespace google_breakpad; + +namespace { + +typedef testing::Test LineReaderTest; + +class ScopedTestFile : public AutoTestFile { +public: + explicit ScopedTestFile(const char* text) + : AutoTestFile("line_reader", text) { + } + + ScopedTestFile(const char* text, size_t text_len) + : AutoTestFile("line_reader", text, text_len) { + } +}; + +} + +TEST(LineReaderTest, EmptyFile) { + ScopedTestFile file(""); + ASSERT_TRUE(file.IsOk()); + LineReader reader(file.GetFd()); + + const char* line; + unsigned len; + ASSERT_FALSE(reader.GetNextLine(&line, &len)); +} + +TEST(LineReaderTest, OneLineTerminated) { + ScopedTestFile file("a\n"); + ASSERT_TRUE(file.IsOk()); + LineReader reader(file.GetFd()); + + const char* line; + unsigned int len; + ASSERT_TRUE(reader.GetNextLine(&line, &len)); + ASSERT_EQ((unsigned int)1, len); + ASSERT_EQ('a', line[0]); + ASSERT_EQ('\0', line[1]); + reader.PopLine(len); + + ASSERT_FALSE(reader.GetNextLine(&line, &len)); +} + +TEST(LineReaderTest, OneLine) { + ScopedTestFile file("a"); + ASSERT_TRUE(file.IsOk()); + LineReader reader(file.GetFd()); + + const char* line; + unsigned len; + ASSERT_TRUE(reader.GetNextLine(&line, &len)); + ASSERT_EQ((unsigned)1, len); + ASSERT_EQ('a', line[0]); + ASSERT_EQ('\0', line[1]); + reader.PopLine(len); + + ASSERT_FALSE(reader.GetNextLine(&line, &len)); +} + +TEST(LineReaderTest, TwoLinesTerminated) { + ScopedTestFile file("a\nb\n"); + ASSERT_TRUE(file.IsOk()); + LineReader reader(file.GetFd()); + + const char* line; + unsigned len; + ASSERT_TRUE(reader.GetNextLine(&line, &len)); + ASSERT_EQ((unsigned)1, len); + ASSERT_EQ('a', line[0]); + ASSERT_EQ('\0', line[1]); + reader.PopLine(len); + + ASSERT_TRUE(reader.GetNextLine(&line, &len)); + ASSERT_EQ((unsigned)1, len); + ASSERT_EQ('b', line[0]); + ASSERT_EQ('\0', line[1]); + reader.PopLine(len); + + ASSERT_FALSE(reader.GetNextLine(&line, &len)); +} + +TEST(LineReaderTest, TwoLines) { + ScopedTestFile file("a\nb"); + ASSERT_TRUE(file.IsOk()); + LineReader reader(file.GetFd()); + + const char* line; + unsigned len; + ASSERT_TRUE(reader.GetNextLine(&line, &len)); + ASSERT_EQ((unsigned)1, len); + ASSERT_EQ('a', line[0]); + ASSERT_EQ('\0', line[1]); + reader.PopLine(len); + + ASSERT_TRUE(reader.GetNextLine(&line, &len)); + ASSERT_EQ((unsigned)1, len); + ASSERT_EQ('b', line[0]); + ASSERT_EQ('\0', line[1]); + reader.PopLine(len); + + ASSERT_FALSE(reader.GetNextLine(&line, &len)); +} + +TEST(LineReaderTest, MaxLength) { + char l[LineReader::kMaxLineLen-1]; + memset(l, 'a', sizeof(l)); + ScopedTestFile file(l, sizeof(l)); + ASSERT_TRUE(file.IsOk()); + LineReader reader(file.GetFd()); + + const char* line; + unsigned len; + ASSERT_TRUE(reader.GetNextLine(&line, &len)); + ASSERT_EQ(sizeof(l), len); + ASSERT_TRUE(memcmp(l, line, sizeof(l)) == 0); + ASSERT_EQ('\0', line[len]); +} + +TEST(LineReaderTest, TooLong) { + // Note: this writes kMaxLineLen 'a' chars in the test file. + char l[LineReader::kMaxLineLen]; + memset(l, 'a', sizeof(l)); + ScopedTestFile file(l, sizeof(l)); + ASSERT_TRUE(file.IsOk()); + LineReader reader(file.GetFd()); + + const char* line; + unsigned len; + ASSERT_FALSE(reader.GetNextLine(&line, &len)); +} diff --git a/shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_core_dumper.cc b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_core_dumper.cc new file mode 100644 index 000000000..92e3a8444 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_core_dumper.cc @@ -0,0 +1,312 @@ +// 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. + +// linux_core_dumper.cc: Implement google_breakpad::LinuxCoreDumper. +// See linux_core_dumper.h for details. + +#include "client/linux/minidump_writer/linux_core_dumper.h" + +#include +#include +#include +#include +#include +#include +#if defined(__mips__) && defined(__ANDROID__) +// To get register definitions. +#include +#endif + +#include "common/linux/elf_gnu_compat.h" +#include "common/linux/linux_libc_support.h" + +namespace google_breakpad { + +LinuxCoreDumper::LinuxCoreDumper(pid_t pid, + const char* core_path, + const char* procfs_path, + const char* root_prefix) + : LinuxDumper(pid, root_prefix), + core_path_(core_path), + procfs_path_(procfs_path), + thread_infos_(&allocator_, 8) { + assert(core_path_); +} + +bool LinuxCoreDumper::BuildProcPath(char* path, pid_t pid, + const char* node) const { + if (!path || !node) + return false; + + size_t node_len = my_strlen(node); + if (node_len == 0) + return false; + + size_t procfs_path_len = my_strlen(procfs_path_); + size_t total_length = procfs_path_len + 1 + node_len; + if (total_length >= NAME_MAX) + return false; + + memcpy(path, procfs_path_, procfs_path_len); + path[procfs_path_len] = '/'; + memcpy(path + procfs_path_len + 1, node, node_len); + path[total_length] = '\0'; + return true; +} + +bool LinuxCoreDumper::CopyFromProcess(void* dest, pid_t child, + const void* src, size_t length) { + ElfCoreDump::Addr virtual_address = reinterpret_cast(src); + // TODO(benchan): Investigate whether the data to be copied could span + // across multiple segments in the core dump file. ElfCoreDump::CopyData + // and this method do not handle that case yet. + if (!core_.CopyData(dest, virtual_address, length)) { + // If the data segment is not found in the core dump, fill the result + // with marker characters. + memset(dest, 0xab, length); + return false; + } + return true; +} + +bool LinuxCoreDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) { + if (index >= thread_infos_.size()) + return false; + + *info = thread_infos_[index]; + const uint8_t* stack_pointer; +#if defined(__i386) + memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp)); +#elif defined(__x86_64) + memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp)); +#elif defined(__ARM_EABI__) + memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp)); +#elif defined(__aarch64__) + memcpy(&stack_pointer, &info->regs.sp, sizeof(info->regs.sp)); +#elif defined(__mips__) + stack_pointer = + reinterpret_cast(info->mcontext.gregs[MD_CONTEXT_MIPS_REG_SP]); +#else +#error "This code hasn't been ported to your platform yet." +#endif + info->stack_pointer = reinterpret_cast(stack_pointer); + return true; +} + +bool LinuxCoreDumper::IsPostMortem() const { + return true; +} + +bool LinuxCoreDumper::ThreadsSuspend() { + return true; +} + +bool LinuxCoreDumper::ThreadsResume() { + return true; +} + +bool LinuxCoreDumper::EnumerateThreads() { + if (!mapped_core_file_.Map(core_path_, 0)) { + fprintf(stderr, "Could not map core dump file into memory\n"); + return false; + } + + char proc_mem_path[NAME_MAX]; + if (BuildProcPath(proc_mem_path, pid_, "mem")) { + int fd = open(proc_mem_path, O_RDONLY | O_LARGEFILE | O_CLOEXEC); + if (fd != -1) { + core_.SetProcMem(fd); + } else { + fprintf(stderr, "Cannot open %s (%s)\n", proc_mem_path, strerror(errno)); + } + } + + core_.SetContent(mapped_core_file_.content()); + if (!core_.IsValid()) { + fprintf(stderr, "Invalid core dump file\n"); + return false; + } + + ElfCoreDump::Note note = core_.GetFirstNote(); + if (!note.IsValid()) { + fprintf(stderr, "PT_NOTE section not found\n"); + return false; + } + + bool first_thread = true; + do { + ElfCoreDump::Word type = note.GetType(); + MemoryRange name = note.GetName(); + MemoryRange description = note.GetDescription(); + + if (type == 0 || name.IsEmpty() || description.IsEmpty()) { + fprintf(stderr, "Could not found a valid PT_NOTE.\n"); + return false; + } + + // 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_SIGINFO + // 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 + // + // The following code only works if notes are ordered as expected. + switch (type) { + case NT_PRSTATUS: { + if (description.length() != sizeof(elf_prstatus)) { + fprintf(stderr, "Found NT_PRSTATUS descriptor of unexpected size\n"); + return false; + } + + const elf_prstatus* status = + reinterpret_cast(description.data()); + pid_t pid = status->pr_pid; + ThreadInfo info; + memset(&info, 0, sizeof(ThreadInfo)); + info.tgid = status->pr_pgrp; + info.ppid = status->pr_ppid; +#if defined(__mips__) +#if defined(__ANDROID__) + for (int i = EF_R0; i <= EF_R31; i++) + info.mcontext.gregs[i - EF_R0] = status->pr_reg[i]; +#else // __ANDROID__ + for (int i = EF_REG0; i <= EF_REG31; i++) + info.mcontext.gregs[i - EF_REG0] = status->pr_reg[i]; +#endif // __ANDROID__ + info.mcontext.mdlo = status->pr_reg[EF_LO]; + info.mcontext.mdhi = status->pr_reg[EF_HI]; + info.mcontext.pc = status->pr_reg[EF_CP0_EPC]; +#else // __mips__ + memcpy(&info.regs, status->pr_reg, sizeof(info.regs)); +#endif // __mips__ + if (first_thread) { + crash_thread_ = pid; + crash_signal_ = status->pr_info.si_signo; + crash_signal_code_ = status->pr_info.si_code; + } + first_thread = false; + threads_.push_back(pid); + thread_infos_.push_back(info); + break; + } + case NT_SIGINFO: { + if (description.length() != sizeof(siginfo_t)) { + fprintf(stderr, "Found NT_SIGINFO descriptor of unexpected size\n"); + return false; + } + + const siginfo_t* info = + reinterpret_cast(description.data()); + + // Set crash_address when si_addr is valid for the signal. + switch (info->si_signo) { + case MD_EXCEPTION_CODE_LIN_SIGBUS: + case MD_EXCEPTION_CODE_LIN_SIGFPE: + case MD_EXCEPTION_CODE_LIN_SIGILL: + case MD_EXCEPTION_CODE_LIN_SIGSEGV: + case MD_EXCEPTION_CODE_LIN_SIGSYS: + case MD_EXCEPTION_CODE_LIN_SIGTRAP: + crash_address_ = reinterpret_cast(info->si_addr); + break; + } + + // Set crash_exception_info for common signals. Since exception info is + // unsigned, but some of these fields might be signed, we always cast. + switch (info->si_signo) { + case MD_EXCEPTION_CODE_LIN_SIGKILL: + set_crash_exception_info({ + static_cast(info->si_pid), + static_cast(info->si_uid), + }); + break; + case MD_EXCEPTION_CODE_LIN_SIGSYS: +#ifdef si_syscall + set_crash_exception_info({ + static_cast(info->si_syscall), + static_cast(info->si_arch), + }); +#endif + break; + } + break; + } +#if defined(__i386) || defined(__x86_64) + case NT_FPREGSET: { + if (thread_infos_.empty()) + return false; + + ThreadInfo* info = &thread_infos_.back(); + if (description.length() != sizeof(info->fpregs)) { + fprintf(stderr, "Found NT_FPREGSET descriptor of unexpected size\n"); + return false; + } + + memcpy(&info->fpregs, description.data(), sizeof(info->fpregs)); + break; + } +#endif +#if defined(__i386) + case NT_PRXFPREG: { + if (thread_infos_.empty()) + return false; + + ThreadInfo* info = &thread_infos_.back(); + if (description.length() != sizeof(info->fpxregs)) { + fprintf(stderr, "Found NT_PRXFPREG descriptor of unexpected size\n"); + return false; + } + + memcpy(&info->fpxregs, description.data(), sizeof(info->fpxregs)); + break; + } +#endif + } + note = note.GetNextNote(); + } while (note.IsValid()); + + return true; +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_core_dumper.h b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_core_dumper.h new file mode 100644 index 000000000..8a7c924b6 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_core_dumper.h @@ -0,0 +1,125 @@ +// 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. + +// linux_core_dumper.h: Define the google_breakpad::LinuxCoreDumper +// class, which is derived from google_breakpad::LinuxDumper to extract +// information from a crashed process via its core dump and proc files. + +#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_CORE_DUMPER_H_ +#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_CORE_DUMPER_H_ + +#include "client/linux/minidump_writer/linux_dumper.h" +#include "common/linux/elf_core_dump.h" +#include "common/linux/memory_mapped_file.h" + +namespace google_breakpad { + +class LinuxCoreDumper : public LinuxDumper { + public: + // Constructs a dumper for extracting information of a given process + // with a process ID of |pid| via its core dump file at |core_path| and + // its proc files at |procfs_path|. If |procfs_path| is a copy of + // /proc/, it should contain the following files: + // auxv, cmdline, environ, exe, maps, status + // See LinuxDumper for the purpose of |root_prefix|. + LinuxCoreDumper(pid_t pid, const char* core_path, const char* procfs_path, + const char* root_prefix = ""); + + // Implements LinuxDumper::BuildProcPath(). + // Builds a proc path for a certain pid for a node (/proc//). + // |path| is a character array of at least NAME_MAX bytes to return the + // result.|node| is the final node without any slashes. Return true on + // success. + // + // As this dumper performs a post-mortem dump and makes use of a copy + // of the proc files of the crashed process, this derived method does + // not actually make use of |pid| and always returns a subpath of + // |procfs_path_| regardless of whether |pid| corresponds to the main + // process or a thread of the process, i.e. assuming both the main process + // and its threads have the following proc files with the same content: + // auxv, cmdline, environ, exe, maps, status + virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const; + + // Implements LinuxDumper::CopyFromProcess(). + // Copies content of |length| bytes from a given process |child|, + // starting from |src|, into |dest|. This method extracts the content + // the core dump and fills |dest| with a sequence of marker bytes + // if the expected data is not found in the core dump. Returns true if + // the expected data is found in the core dump. + virtual bool CopyFromProcess(void* dest, pid_t child, const void* src, + size_t length); + + // Implements LinuxDumper::GetThreadInfoByIndex(). + // Reads information about the |index|-th thread of |threads_|. + // Returns true on success. One must have called |ThreadsSuspend| first. + virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info); + + // Implements LinuxDumper::IsPostMortem(). + // Always returns true to indicate that this dumper performs a + // post-mortem dump of a crashed process via a core dump file. + virtual bool IsPostMortem() const; + + // Implements LinuxDumper::ThreadsSuspend(). + // As the dumper performs a post-mortem dump via a core dump file, + // there is no threads to suspend. This method does nothing and + // always returns true. + virtual bool ThreadsSuspend(); + + // Implements LinuxDumper::ThreadsResume(). + // As the dumper performs a post-mortem dump via a core dump file, + // there is no threads to resume. This method does nothing and + // always returns true. + virtual bool ThreadsResume(); + + protected: + // Implements LinuxDumper::EnumerateThreads(). + // Enumerates all threads of the given process into |threads_|. + virtual bool EnumerateThreads(); + + private: + // Path of the core dump file. + const char* core_path_; + + // Path of the directory containing the proc files of the given process, + // which is usually a copy of /proc/. + const char* procfs_path_; + + // Memory-mapped core dump file at |core_path_|. + MemoryMappedFile mapped_core_file_; + + // Content of the core dump file. + ElfCoreDump core_; + + // Thread info found in the core dump file. + wasteful_vector thread_infos_; +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_HANDLER_LINUX_CORE_DUMPER_H_ diff --git a/shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_core_dumper_unittest.cc b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_core_dumper_unittest.cc new file mode 100644 index 000000000..774480319 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_core_dumper_unittest.cc @@ -0,0 +1,192 @@ +// 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. + +// linux_core_dumper_unittest.cc: +// Unit tests for google_breakpad::LinuxCoreDumoer. + +#include + +#include "breakpad_googletest_includes.h" +#include "client/linux/minidump_writer/linux_core_dumper.h" +#include "common/linux/tests/crash_generator.h" +#include "common/using_std_string.h" + +using namespace google_breakpad; + +TEST(LinuxCoreDumperTest, GetMappingAbsolutePath) { + const LinuxCoreDumper dumper(getpid(), "core", "/tmp", "/mnt/root"); + const MappingInfo mapping = {0, 0, {0, 0}, 0, false, "/usr/lib/libc.so"}; + + char path[PATH_MAX]; + dumper.GetMappingAbsolutePath(mapping, path); + + EXPECT_STREQ("/mnt/root/usr/lib/libc.so", path); +} + +TEST(LinuxCoreDumperTest, BuildProcPath) { + const pid_t pid = getpid(); + const char procfs_path[] = "/procfs_copy"; + LinuxCoreDumper dumper(getpid(), "core_file", procfs_path); + + char maps_path[NAME_MAX] = ""; + char maps_path_expected[NAME_MAX]; + snprintf(maps_path_expected, sizeof(maps_path_expected), + "%s/maps", procfs_path); + EXPECT_TRUE(dumper.BuildProcPath(maps_path, pid, "maps")); + EXPECT_STREQ(maps_path_expected, maps_path); + + EXPECT_FALSE(dumper.BuildProcPath(NULL, pid, "maps")); + EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, "")); + EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, NULL)); + + char long_node[NAME_MAX]; + size_t long_node_len = NAME_MAX - strlen(procfs_path) - 1; + memset(long_node, 'a', long_node_len); + long_node[long_node_len] = '\0'; + EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, long_node)); +} + +TEST(LinuxCoreDumperTest, VerifyDumpWithMultipleThreads) { + CrashGenerator crash_generator; + if (!crash_generator.HasDefaultCorePattern()) { + fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test " + "is skipped due to non-default core pattern\n"); + return; + } + + const unsigned kNumOfThreads = 3; + const unsigned kCrashThread = 1; + const int kCrashSignal = SIGABRT; + pid_t child_pid; + ASSERT_TRUE(crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread, + kCrashSignal, &child_pid)); + + const string core_file = crash_generator.GetCoreFilePath(); + const string procfs_path = crash_generator.GetDirectoryOfProcFilesCopy(); + +#if defined(__ANDROID__) + struct stat st; + if (stat(core_file.c_str(), &st) != 0) { + fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test is " + "skipped due to no core file being generated\n"); + return; + } +#endif + + LinuxCoreDumper dumper(child_pid, core_file.c_str(), procfs_path.c_str()); + + EXPECT_TRUE(dumper.Init()); + + EXPECT_TRUE(dumper.IsPostMortem()); + + // These are no-ops and should always return true. + EXPECT_TRUE(dumper.ThreadsSuspend()); + EXPECT_TRUE(dumper.ThreadsResume()); + + // Linux does not set the crash address with SIGABRT, so make sure it always + // sets the crash address to 0. + EXPECT_EQ(0U, dumper.crash_address()); + EXPECT_EQ(kCrashSignal, dumper.crash_signal()); + EXPECT_EQ(crash_generator.GetThreadId(kCrashThread), + dumper.crash_thread()); + +#if defined(THREAD_SANITIZER) + EXPECT_GE(dumper.threads().size(), kNumOfThreads); +#else + EXPECT_EQ(dumper.threads().size(), kNumOfThreads); +#endif + for (unsigned i = 0; i < kNumOfThreads; ++i) { + ThreadInfo info; + EXPECT_TRUE(dumper.GetThreadInfoByIndex(i, &info)); + const void* stack; + size_t stack_len; + EXPECT_TRUE(dumper.GetStackInfo(&stack, &stack_len, info.stack_pointer)); + EXPECT_EQ(getpid(), info.ppid); + } +} + +TEST(LinuxCoreDumperTest, VerifyExceptionDetails) { + CrashGenerator crash_generator; + if (!crash_generator.HasDefaultCorePattern()) { + fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test " + "is skipped due to non-default core pattern\n"); + return; + } + +#ifndef si_syscall + fprintf(stderr, "LinuxCoreDumperTest.VerifyDumpWithMultipleThreads test is " + "skipped due to old kernel/C library headers\n"); + return; +#endif + + const unsigned kNumOfThreads = 2; + const unsigned kCrashThread = 1; + const int kCrashSignal = SIGSYS; + pid_t child_pid; + ASSERT_TRUE(crash_generator.CreateChildCrash(kNumOfThreads, kCrashThread, + kCrashSignal, &child_pid)); + + const string core_file = crash_generator.GetCoreFilePath(); + const string procfs_path = crash_generator.GetDirectoryOfProcFilesCopy(); + +#if defined(__ANDROID__) + struct stat st; + if (stat(core_file.c_str(), &st) != 0) { + fprintf(stderr, "LinuxCoreDumperTest.VerifyExceptionDetails test is " + "skipped due to no core file being generated\n"); + return; + } +#endif + + LinuxCoreDumper dumper(child_pid, core_file.c_str(), procfs_path.c_str()); + + EXPECT_TRUE(dumper.Init()); + + EXPECT_TRUE(dumper.IsPostMortem()); + +#if defined(__ANDROID__) + // TODO: For some reason, Android doesn't seem to pass this. + if (!dumper.crash_address()) { + fprintf(stderr, "LinuxCoreDumperTest.VerifyExceptionDetails test is " + "skipped due to missing signal details on Android\n"); + return; + } +#endif + + // Check the exception details. + EXPECT_NE(0U, dumper.crash_address()); + EXPECT_EQ(kCrashSignal, dumper.crash_signal()); + EXPECT_EQ(crash_generator.GetThreadId(kCrashThread), + dumper.crash_thread()); + + // We check the length, but not the actual fields. We sent SIGSYS ourselves + // instead of the kernel, so the extended fields are garbage. + const std::vector info(dumper.crash_exception_info()); + EXPECT_EQ(2U, info.size()); +} diff --git a/shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_dumper.cc b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_dumper.cc new file mode 100644 index 000000000..44430c4e9 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_dumper.cc @@ -0,0 +1,971 @@ +// 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. + +// linux_dumper.cc: Implement google_breakpad::LinuxDumper. +// See linux_dumper.h for details. + +// This code deals with the mechanics of getting information about a crashed +// process. Since this code may run in a compromised address space, the same +// rules apply as detailed at the top of minidump_writer.h: no libc calls and +// use the alternative allocator. + +#include "client/linux/minidump_writer/linux_dumper.h" + +#include +#include +#include +#include +#include +#include + +#include "client/linux/minidump_writer/line_reader.h" +#include "common/linux/elfutils.h" +#include "common/linux/file_id.h" +#include "common/linux/linux_libc_support.h" +#include "common/linux/memory_mapped_file.h" +#include "common/linux/safe_readlink.h" +#include "google_breakpad/common/minidump_exception_linux.h" +#include "third_party/lss/linux_syscall_support.h" + +using google_breakpad::elf::FileID; + +#if defined(__ANDROID__) + +// Android packed relocations definitions are not yet available from the +// NDK header files, so we have to provide them manually here. +#ifndef DT_LOOS +#define DT_LOOS 0x6000000d +#endif +#ifndef DT_ANDROID_REL +static const int DT_ANDROID_REL = DT_LOOS + 2; +#endif +#ifndef DT_ANDROID_RELA +static const int DT_ANDROID_RELA = DT_LOOS + 4; +#endif + +#endif // __ANDROID __ + +static const char kMappedFileUnsafePrefix[] = "/dev/"; +static const char kDeletedSuffix[] = " (deleted)"; + +inline static bool IsMappedFileOpenUnsafe( + const google_breakpad::MappingInfo& mapping) { + // It is unsafe to attempt to open a mapped file that lives under /dev, + // because the semantics of the open may be driver-specific so we'd risk + // hanging the crash dumper. And a file in /dev/ almost certainly has no + // ELF file identifier anyways. + return my_strncmp(mapping.name, + kMappedFileUnsafePrefix, + sizeof(kMappedFileUnsafePrefix) - 1) == 0; +} + +namespace google_breakpad { + +namespace { + +bool MappingContainsAddress(const MappingInfo& mapping, uintptr_t address) { + return mapping.system_mapping_info.start_addr <= address && + address < mapping.system_mapping_info.end_addr; +} + +#if defined(__CHROMEOS__) + +// Recover memory mappings before writing dump on ChromeOS +// +// On Linux, breakpad relies on /proc/[pid]/maps to associate symbols from +// addresses. ChromeOS' hugepage implementation replaces some segments with +// anonymous private pages, which is a restriction of current implementation +// in Linux kernel at the time of writing. Thus, breakpad can no longer +// symbolize addresses from those text segments replaced with hugepages. +// +// This postprocess tries to recover the mappings. Because hugepages are always +// inserted in between some .text sections, it tries to infer the names and +// offsets of the segments, by looking at segments immediately precede and +// succeed them. +// +// For example, a text segment before hugepage optimization +// 02001000-03002000 r-xp /opt/google/chrome/chrome +// +// can be broken into +// 02001000-02200000 r-xp /opt/google/chrome/chrome +// 02200000-03000000 r-xp +// 03000000-03002000 r-xp /opt/google/chrome/chrome +// +// For more details, see: +// crbug.com/628040 ChromeOS' use of hugepages confuses crash symbolization + +// Copied from CrOS' hugepage implementation, which is unlikely to change. +// The hugepage size is 2M. +const unsigned int kHpageShift = 21; +const size_t kHpageSize = (1 << kHpageShift); +const size_t kHpageMask = (~(kHpageSize - 1)); + +// Find and merge anonymous r-xp segments with surrounding named segments. +// There are two cases: + +// Case 1: curr, next +// curr is anonymous +// curr is r-xp +// curr.size >= 2M +// curr.size is a multiple of 2M. +// next is backed by some file. +// curr and next are contiguous. +// offset(next) == sizeof(curr) +void TryRecoverMappings(MappingInfo* curr, MappingInfo* next) { + // Merged segments are marked with size = 0. + if (curr->size == 0 || next->size == 0) + return; + + if (curr->size >= kHpageSize && + curr->exec && + (curr->size & kHpageMask) == curr->size && + (curr->start_addr & kHpageMask) == curr->start_addr && + curr->name[0] == '\0' && + next->name[0] != '\0' && + curr->start_addr + curr->size == next->start_addr && + curr->size == next->offset) { + + // matched + my_strlcpy(curr->name, next->name, NAME_MAX); + if (next->exec) { + // (curr, next) + curr->size += next->size; + next->size = 0; + } + } +} + +// Case 2: prev, curr, next +// curr is anonymous +// curr is r-xp +// curr.size >= 2M +// curr.size is a multiple of 2M. +// next and prev are backed by the same file. +// prev, curr and next are contiguous. +// offset(next) == offset(prev) + sizeof(prev) + sizeof(curr) +void TryRecoverMappings(MappingInfo* prev, MappingInfo* curr, + MappingInfo* next) { + // Merged segments are marked with size = 0. + if (prev->size == 0 || curr->size == 0 || next->size == 0) + return; + + if (curr->size >= kHpageSize && + curr->exec && + (curr->size & kHpageMask) == curr->size && + (curr->start_addr & kHpageMask) == curr->start_addr && + curr->name[0] == '\0' && + next->name[0] != '\0' && + curr->start_addr + curr->size == next->start_addr && + prev->start_addr + prev->size == curr->start_addr && + my_strncmp(prev->name, next->name, NAME_MAX) == 0 && + next->offset == prev->offset + prev->size + curr->size) { + + // matched + my_strlcpy(curr->name, prev->name, NAME_MAX); + if (prev->exec) { + curr->offset = prev->offset; + curr->start_addr = prev->start_addr; + if (next->exec) { + // (prev, curr, next) + curr->size += prev->size + next->size; + prev->size = 0; + next->size = 0; + } else { + // (prev, curr), next + curr->size += prev->size; + prev->size = 0; + } + } else { + curr->offset = prev->offset + prev->size; + if (next->exec) { + // prev, (curr, next) + curr->size += next->size; + next->size = 0; + } else { + // prev, curr, next + } + } + } +} + +// mappings_ is sorted excepted for the first entry. +// This function tries to merge segemnts into the first entry, +// then check for other sorted entries. +// See LinuxDumper::EnumerateMappings(). +void CrOSPostProcessMappings(wasteful_vector& mappings) { + // Find the candidate "next" to first segment, which is the only one that + // could be out-of-order. + size_t l = 1; + size_t r = mappings.size(); + size_t next = mappings.size(); + while (l < r) { + int m = (l + r) / 2; + if (mappings[m]->start_addr > mappings[0]->start_addr) + r = next = m; + else + l = m + 1; + } + + // Shows the range that contains the entry point is + // [first_start_addr, first_end_addr) + size_t first_start_addr = mappings[0]->start_addr; + size_t first_end_addr = mappings[0]->start_addr + mappings[0]->size; + + // Put the out-of-order segment in order. + std::rotate(mappings.begin(), mappings.begin() + 1, mappings.begin() + next); + + // Iterate through normal, sorted cases. + // Normal case 1. + for (size_t i = 0; i < mappings.size() - 1; i++) + TryRecoverMappings(mappings[i], mappings[i + 1]); + + // Normal case 2. + for (size_t i = 0; i < mappings.size() - 2; i++) + TryRecoverMappings(mappings[i], mappings[i + 1], mappings[i + 2]); + + // Collect merged (size == 0) segments. + size_t f, e; + for (f = e = 0; e < mappings.size(); e++) + if (mappings[e]->size > 0) + mappings[f++] = mappings[e]; + mappings.resize(f); + + // The entry point is in the first mapping. We want to find the location + // of the entry point after merging segment. To do this, we want to find + // the mapping that covers the first mapping from the original mapping list. + // If the mapping is not in the beginning, we move it to the begining via + // a right rotate by using reverse iterators. + for (l = 0; l < mappings.size(); l++) { + if (mappings[l]->start_addr <= first_start_addr + && (mappings[l]->start_addr + mappings[l]->size >= first_end_addr)) + break; + } + if (l > 0) { + r = mappings.size(); + std::rotate(mappings.rbegin() + r - l - 1, mappings.rbegin() + r - l, + mappings.rend()); + } +} + +#endif // __CHROMEOS__ + +} // namespace + +// All interesting auvx entry types are below AT_SYSINFO_EHDR +#define AT_MAX AT_SYSINFO_EHDR + +LinuxDumper::LinuxDumper(pid_t pid, const char* root_prefix) + : pid_(pid), + root_prefix_(root_prefix), + crash_address_(0), + crash_signal_(0), + crash_signal_code_(0), + crash_thread_(pid), + threads_(&allocator_, 8), + mappings_(&allocator_), + auxv_(&allocator_, AT_MAX + 1) { + assert(root_prefix_ && my_strlen(root_prefix_) < PATH_MAX); + // The passed-in size to the constructor (above) is only a hint. + // Must call .resize() to do actual initialization of the elements. + auxv_.resize(AT_MAX + 1); +} + +LinuxDumper::~LinuxDumper() { +} + +bool LinuxDumper::Init() { + return ReadAuxv() && EnumerateThreads() && EnumerateMappings(); +} + +bool LinuxDumper::LateInit() { +#if defined(__ANDROID__) + LatePostprocessMappings(); +#endif + +#if defined(__CHROMEOS__) + CrOSPostProcessMappings(mappings_); +#endif + + return true; +} + +bool +LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping, + bool member, + unsigned int mapping_id, + wasteful_vector& identifier) { + assert(!member || mapping_id < mappings_.size()); + if (IsMappedFileOpenUnsafe(mapping)) + return false; + + // Special-case linux-gate because it's not a real file. + if (my_strcmp(mapping.name, kLinuxGateLibraryName) == 0) { + void* linux_gate = NULL; + if (pid_ == sys_getpid()) { + linux_gate = reinterpret_cast(mapping.start_addr); + } else { + linux_gate = allocator_.Alloc(mapping.size); + CopyFromProcess(linux_gate, pid_, + reinterpret_cast(mapping.start_addr), + mapping.size); + } + return FileID::ElfFileIdentifierFromMappedFile(linux_gate, identifier); + } + + char filename[PATH_MAX]; + if (!GetMappingAbsolutePath(mapping, filename)) + return false; + bool filename_modified = HandleDeletedFileInMapping(filename); + + MemoryMappedFile mapped_file(filename, mapping.offset); + if (!mapped_file.data() || mapped_file.size() < SELFMAG) + return false; + + bool success = + FileID::ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier); + if (success && member && filename_modified) { + mappings_[mapping_id]->name[my_strlen(mapping.name) - + sizeof(kDeletedSuffix) + 1] = '\0'; + } + + return success; +} + +void LinuxDumper::SetCrashInfoFromSigInfo(const siginfo_t& siginfo) { + set_crash_address(reinterpret_cast(siginfo.si_addr)); + set_crash_signal(siginfo.si_signo); + set_crash_signal_code(siginfo.si_code); +} + +const char* LinuxDumper::GetCrashSignalString() const { + switch (static_cast(crash_signal_)) { + case MD_EXCEPTION_CODE_LIN_SIGHUP: + return "SIGHUP"; + case MD_EXCEPTION_CODE_LIN_SIGINT: + return "SIGINT"; + case MD_EXCEPTION_CODE_LIN_SIGQUIT: + return "SIGQUIT"; + case MD_EXCEPTION_CODE_LIN_SIGILL: + return "SIGILL"; + case MD_EXCEPTION_CODE_LIN_SIGTRAP: + return "SIGTRAP"; + case MD_EXCEPTION_CODE_LIN_SIGABRT: + return "SIGABRT"; + case MD_EXCEPTION_CODE_LIN_SIGBUS: + return "SIGBUS"; + case MD_EXCEPTION_CODE_LIN_SIGFPE: + return "SIGFPE"; + case MD_EXCEPTION_CODE_LIN_SIGKILL: + return "SIGKILL"; + case MD_EXCEPTION_CODE_LIN_SIGUSR1: + return "SIGUSR1"; + case MD_EXCEPTION_CODE_LIN_SIGSEGV: + return "SIGSEGV"; + case MD_EXCEPTION_CODE_LIN_SIGUSR2: + return "SIGUSR2"; + case MD_EXCEPTION_CODE_LIN_SIGPIPE: + return "SIGPIPE"; + case MD_EXCEPTION_CODE_LIN_SIGALRM: + return "SIGALRM"; + case MD_EXCEPTION_CODE_LIN_SIGTERM: + return "SIGTERM"; + case MD_EXCEPTION_CODE_LIN_SIGSTKFLT: + return "SIGSTKFLT"; + case MD_EXCEPTION_CODE_LIN_SIGCHLD: + return "SIGCHLD"; + case MD_EXCEPTION_CODE_LIN_SIGCONT: + return "SIGCONT"; + case MD_EXCEPTION_CODE_LIN_SIGSTOP: + return "SIGSTOP"; + case MD_EXCEPTION_CODE_LIN_SIGTSTP: + return "SIGTSTP"; + case MD_EXCEPTION_CODE_LIN_SIGTTIN: + return "SIGTTIN"; + case MD_EXCEPTION_CODE_LIN_SIGTTOU: + return "SIGTTOU"; + case MD_EXCEPTION_CODE_LIN_SIGURG: + return "SIGURG"; + case MD_EXCEPTION_CODE_LIN_SIGXCPU: + return "SIGXCPU"; + case MD_EXCEPTION_CODE_LIN_SIGXFSZ: + return "SIGXFSZ"; + case MD_EXCEPTION_CODE_LIN_SIGVTALRM: + return "SIGVTALRM"; + case MD_EXCEPTION_CODE_LIN_SIGPROF: + return "SIGPROF"; + case MD_EXCEPTION_CODE_LIN_SIGWINCH: + return "SIGWINCH"; + case MD_EXCEPTION_CODE_LIN_SIGIO: + return "SIGIO"; + case MD_EXCEPTION_CODE_LIN_SIGPWR: + return "SIGPWR"; + case MD_EXCEPTION_CODE_LIN_SIGSYS: + return "SIGSYS"; + case MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED: + return "DUMP_REQUESTED"; + default: + return "UNKNOWN"; + } +} + +bool LinuxDumper::GetMappingAbsolutePath(const MappingInfo& mapping, + char path[PATH_MAX]) const { + return my_strlcpy(path, root_prefix_, PATH_MAX) < PATH_MAX && + my_strlcat(path, mapping.name, PATH_MAX) < PATH_MAX; +} + +namespace { +// Find the shared object name (SONAME) by examining the ELF information +// for |mapping|. If the SONAME is found copy it into the passed buffer +// |soname| and return true. The size of the buffer is |soname_size|. +// The SONAME will be truncated if it is too long to fit in the buffer. +bool ElfFileSoName(const LinuxDumper& dumper, + const MappingInfo& mapping, char* soname, size_t soname_size) { + if (IsMappedFileOpenUnsafe(mapping)) { + // Not safe + return false; + } + + char filename[PATH_MAX]; + if (!dumper.GetMappingAbsolutePath(mapping, filename)) + return false; + + MemoryMappedFile mapped_file(filename, mapping.offset); + if (!mapped_file.data() || mapped_file.size() < SELFMAG) { + // mmap failed + return false; + } + + return ElfFileSoNameFromMappedFile(mapped_file.data(), soname, soname_size); +} + +} // namespace + + +void LinuxDumper::GetMappingEffectiveNameAndPath(const MappingInfo& mapping, + char* file_path, + size_t file_path_size, + char* file_name, + size_t file_name_size) { + my_strlcpy(file_path, mapping.name, file_path_size); + + // Tools such as minidump_stackwalk use the name of the module to look up + // symbols produced by dump_syms. dump_syms will prefer to use a module's + // DT_SONAME as the module name, if one exists, and will fall back to the + // filesystem name of the module. + + // Just use the filesystem name if no SONAME is present. + if (!ElfFileSoName(*this, mapping, file_name, file_name_size)) { + // file_path := /path/to/libname.so + // file_name := libname.so + const char* basename = my_strrchr(file_path, '/'); + basename = basename == NULL ? file_path : (basename + 1); + my_strlcpy(file_name, basename, file_name_size); + return; + } + + if (mapping.exec && mapping.offset != 0) { + // If an executable is mapped from a non-zero offset, this is likely because + // the executable was loaded directly from inside an archive file (e.g., an + // apk on Android). + // In this case, we append the file_name to the mapped archive path: + // file_name := libname.so + // file_path := /path/to/ARCHIVE.APK/libname.so + if (my_strlen(file_path) + 1 + my_strlen(file_name) < file_path_size) { + my_strlcat(file_path, "/", file_path_size); + my_strlcat(file_path, file_name, file_path_size); + } + } else { + // Otherwise, replace the basename with the SONAME. + char* basename = const_cast(my_strrchr(file_path, '/')); + if (basename) { + my_strlcpy(basename + 1, file_name, + file_path_size - my_strlen(file_path) + + my_strlen(basename + 1)); + } else { + my_strlcpy(file_path, file_name, file_path_size); + } + } +} + +bool LinuxDumper::ReadAuxv() { + char auxv_path[NAME_MAX]; + if (!BuildProcPath(auxv_path, pid_, "auxv")) { + return false; + } + + int fd = sys_open(auxv_path, O_RDONLY, 0); + if (fd < 0) { + return false; + } + + elf_aux_entry one_aux_entry; + bool res = false; + while (sys_read(fd, + &one_aux_entry, + sizeof(elf_aux_entry)) == sizeof(elf_aux_entry) && + one_aux_entry.a_type != AT_NULL) { + if (one_aux_entry.a_type <= AT_MAX) { + auxv_[one_aux_entry.a_type] = one_aux_entry.a_un.a_val; + res = true; + } + } + sys_close(fd); + return res; +} + +bool LinuxDumper::EnumerateMappings() { + char maps_path[NAME_MAX]; + if (!BuildProcPath(maps_path, pid_, "maps")) + return false; + + // linux_gate_loc is the beginning of the kernel's mapping of + // linux-gate.so in the process. It doesn't actually show up in the + // maps list as a filename, but it can be found using the AT_SYSINFO_EHDR + // aux vector entry, which gives the information necessary to special + // case its entry when creating the list of mappings. + // See http://www.trilithium.com/johan/2005/08/linux-gate/ for more + // information. + const void* linux_gate_loc = + reinterpret_cast(auxv_[AT_SYSINFO_EHDR]); + // Although the initial executable is usually the first mapping, it's not + // guaranteed (see http://crosbug.com/25355); therefore, try to use the + // actual entry point to find the mapping. + const void* entry_point_loc = reinterpret_cast(auxv_[AT_ENTRY]); + + const int fd = sys_open(maps_path, O_RDONLY, 0); + if (fd < 0) + return false; + LineReader* const line_reader = new(allocator_) LineReader(fd); + + const char* line; + unsigned line_len; + while (line_reader->GetNextLine(&line, &line_len)) { + uintptr_t start_addr, end_addr, offset; + + const char* i1 = my_read_hex_ptr(&start_addr, line); + if (*i1 == '-') { + const char* i2 = my_read_hex_ptr(&end_addr, i1 + 1); + if (*i2 == ' ') { + bool exec = (*(i2 + 3) == 'x'); + const char* i3 = my_read_hex_ptr(&offset, i2 + 6 /* skip ' rwxp ' */); + if (*i3 == ' ') { + const char* name = NULL; + // Only copy name if the name is a valid path name, or if + // it's the VDSO image. + if (((name = my_strchr(line, '/')) == NULL) && + linux_gate_loc && + reinterpret_cast(start_addr) == linux_gate_loc) { + name = kLinuxGateLibraryName; + offset = 0; + } + // Merge adjacent mappings into one module, assuming they're a single + // library mapped by the dynamic linker. Do this only if their name + // matches and either they have the same +x protection flag, or if the + // previous mapping is not executable and the new one is, to handle + // lld's output (see crbug.com/716484). + if (name && !mappings_.empty()) { + MappingInfo* module = mappings_.back(); + if ((start_addr == module->start_addr + module->size) && + (my_strlen(name) == my_strlen(module->name)) && + (my_strncmp(name, module->name, my_strlen(name)) == 0) && + ((exec == module->exec) || (!module->exec && exec))) { + module->system_mapping_info.end_addr = end_addr; + module->size = end_addr - module->start_addr; + module->exec |= exec; + line_reader->PopLine(line_len); + continue; + } + } + MappingInfo* const module = new(allocator_) MappingInfo; + mappings_.push_back(module); + my_memset(module, 0, sizeof(MappingInfo)); + module->system_mapping_info.start_addr = start_addr; + module->system_mapping_info.end_addr = end_addr; + module->start_addr = start_addr; + module->size = end_addr - start_addr; + module->offset = offset; + module->exec = exec; + if (name != NULL) { + const unsigned l = my_strlen(name); + if (l < sizeof(module->name)) + my_memcpy(module->name, name, l); + } + } + } + } + line_reader->PopLine(line_len); + } + + if (entry_point_loc) { + for (size_t i = 0; i < mappings_.size(); ++i) { + MappingInfo* module = mappings_[i]; + + // If this module contains the entry-point, and it's not already the first + // one, then we need to make it be first. This is because the minidump + // format assumes the first module is the one that corresponds to the main + // executable (as codified in + // processor/minidump.cc:MinidumpModuleList::GetMainModule()). + if ((entry_point_loc >= reinterpret_cast(module->start_addr)) && + (entry_point_loc < + reinterpret_cast(module->start_addr + module->size))) { + for (size_t j = i; j > 0; j--) + mappings_[j] = mappings_[j - 1]; + mappings_[0] = module; + break; + } + } + } + + sys_close(fd); + + return !mappings_.empty(); +} + +#if defined(__ANDROID__) + +bool LinuxDumper::GetLoadedElfHeader(uintptr_t start_addr, ElfW(Ehdr)* ehdr) { + CopyFromProcess(ehdr, pid_, + reinterpret_cast(start_addr), + sizeof(*ehdr)); + return my_memcmp(&ehdr->e_ident, ELFMAG, SELFMAG) == 0; +} + +void LinuxDumper::ParseLoadedElfProgramHeaders(ElfW(Ehdr)* ehdr, + uintptr_t start_addr, + uintptr_t* min_vaddr_ptr, + uintptr_t* dyn_vaddr_ptr, + size_t* dyn_count_ptr) { + uintptr_t phdr_addr = start_addr + ehdr->e_phoff; + + const uintptr_t max_addr = UINTPTR_MAX; + uintptr_t min_vaddr = max_addr; + uintptr_t dyn_vaddr = 0; + size_t dyn_count = 0; + + for (size_t i = 0; i < ehdr->e_phnum; ++i) { + ElfW(Phdr) phdr; + CopyFromProcess(&phdr, pid_, + reinterpret_cast(phdr_addr), + sizeof(phdr)); + if (phdr.p_type == PT_LOAD && phdr.p_vaddr < min_vaddr) { + min_vaddr = phdr.p_vaddr; + } + if (phdr.p_type == PT_DYNAMIC) { + dyn_vaddr = phdr.p_vaddr; + dyn_count = phdr.p_memsz / sizeof(ElfW(Dyn)); + } + phdr_addr += sizeof(phdr); + } + + *min_vaddr_ptr = min_vaddr; + *dyn_vaddr_ptr = dyn_vaddr; + *dyn_count_ptr = dyn_count; +} + +bool LinuxDumper::HasAndroidPackedRelocations(uintptr_t load_bias, + uintptr_t dyn_vaddr, + size_t dyn_count) { + uintptr_t dyn_addr = load_bias + dyn_vaddr; + for (size_t i = 0; i < dyn_count; ++i) { + ElfW(Dyn) dyn; + CopyFromProcess(&dyn, pid_, + reinterpret_cast(dyn_addr), + sizeof(dyn)); + if (dyn.d_tag == DT_ANDROID_REL || dyn.d_tag == DT_ANDROID_RELA) { + return true; + } + dyn_addr += sizeof(dyn); + } + return false; +} + +uintptr_t LinuxDumper::GetEffectiveLoadBias(ElfW(Ehdr)* ehdr, + uintptr_t start_addr) { + uintptr_t min_vaddr = 0; + uintptr_t dyn_vaddr = 0; + size_t dyn_count = 0; + ParseLoadedElfProgramHeaders(ehdr, start_addr, + &min_vaddr, &dyn_vaddr, &dyn_count); + // If |min_vaddr| is non-zero and we find Android packed relocation tags, + // return the effective load bias. + if (min_vaddr != 0) { + const uintptr_t load_bias = start_addr - min_vaddr; + if (HasAndroidPackedRelocations(load_bias, dyn_vaddr, dyn_count)) { + return load_bias; + } + } + // Either |min_vaddr| is zero, or it is non-zero but we did not find the + // expected Android packed relocations tags. + return start_addr; +} + +void LinuxDumper::LatePostprocessMappings() { + for (size_t i = 0; i < mappings_.size(); ++i) { + // Only consider exec mappings that indicate a file path was mapped, and + // where the ELF header indicates a mapped shared library. + MappingInfo* mapping = mappings_[i]; + if (!(mapping->exec && mapping->name[0] == '/')) { + continue; + } + ElfW(Ehdr) ehdr; + if (!GetLoadedElfHeader(mapping->start_addr, &ehdr)) { + continue; + } + if (ehdr.e_type == ET_DYN) { + // Compute the effective load bias for this mapped library, and update + // the mapping to hold that rather than |start_addr|, at the same time + // adjusting |size| to account for the change in |start_addr|. Where + // the library does not contain Android packed relocations, + // GetEffectiveLoadBias() returns |start_addr| and the mapping entry + // is not changed. + const uintptr_t load_bias = GetEffectiveLoadBias(&ehdr, + mapping->start_addr); + mapping->size += mapping->start_addr - load_bias; + mapping->start_addr = load_bias; + } + } +} + +#endif // __ANDROID__ + +// Get information about the stack, given the stack pointer. We don't try to +// walk the stack since we might not have all the information needed to do +// unwind. So we just grab, up to, 32k of stack. +bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len, + uintptr_t int_stack_pointer) { + // Move the stack pointer to the bottom of the page that it's in. + const uintptr_t page_size = getpagesize(); + + uint8_t* const stack_pointer = + reinterpret_cast(int_stack_pointer & ~(page_size - 1)); + + // The number of bytes of stack which we try to capture. + static const ptrdiff_t kStackToCapture = 32 * 1024; + + const MappingInfo* mapping = FindMapping(stack_pointer); + if (!mapping) + return false; + const ptrdiff_t offset = stack_pointer - + reinterpret_cast(mapping->start_addr); + const ptrdiff_t distance_to_end = + static_cast(mapping->size) - offset; + *stack_len = distance_to_end > kStackToCapture ? + kStackToCapture : distance_to_end; + *stack = stack_pointer; + return true; +} + +void LinuxDumper::SanitizeStackCopy(uint8_t* stack_copy, size_t stack_len, + uintptr_t stack_pointer, + uintptr_t sp_offset) { + // We optimize the search for containing mappings in three ways: + // 1) We expect that pointers into the stack mapping will be common, so + // we cache that address range. + // 2) The last referenced mapping is a reasonable predictor for the next + // referenced mapping, so we test that first. + // 3) We precompute a bitfield based upon bits 32:32-n of the start and + // stop addresses, and use that to short circuit any values that can + // not be pointers. (n=11) + const uintptr_t defaced = +#if defined(__LP64__) + 0x0defaced0defaced; +#else + 0x0defaced; +#endif + // the bitfield length is 2^test_bits long. + const unsigned int test_bits = 11; + // byte length of the corresponding array. + const unsigned int array_size = 1 << (test_bits - 3); + const unsigned int array_mask = array_size - 1; + // The amount to right shift pointers by. This captures the top bits + // on 32 bit architectures. On 64 bit architectures this would be + // uninformative so we take the same range of bits. + const unsigned int shift = 32 - 11; + const MappingInfo* last_hit_mapping = nullptr; + const MappingInfo* hit_mapping = nullptr; + const MappingInfo* stack_mapping = FindMappingNoBias(stack_pointer); + // The magnitude below which integers are considered to be to be + // 'small', and not constitute a PII risk. These are included to + // avoid eliding useful register values. + const ssize_t small_int_magnitude = 4096; + + char could_hit_mapping[array_size]; + my_memset(could_hit_mapping, 0, array_size); + + // Initialize the bitfield such that if the (pointer >> shift)'th + // bit, modulo the bitfield size, is not set then there does not + // exist a mapping in mappings_ that would contain that pointer. + for (size_t i = 0; i < mappings_.size(); ++i) { + if (!mappings_[i]->exec) continue; + // For each mapping, work out the (unmodulo'ed) range of bits to + // set. + uintptr_t start = mappings_[i]->start_addr; + uintptr_t end = start + mappings_[i]->size; + start >>= shift; + end >>= shift; + for (size_t bit = start; bit <= end; ++bit) { + // Set each bit in the range, applying the modulus. + could_hit_mapping[(bit >> 3) & array_mask] |= 1 << (bit & 7); + } + } + + // Zero memory that is below the current stack pointer. + const uintptr_t offset = + (sp_offset + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1); + if (offset) { + my_memset(stack_copy, 0, offset); + } + + // Apply sanitization to each complete pointer-aligned word in the + // stack. + uint8_t* sp; + for (sp = stack_copy + offset; + sp <= stack_copy + stack_len - sizeof(uintptr_t); + sp += sizeof(uintptr_t)) { + uintptr_t addr; + my_memcpy(&addr, sp, sizeof(uintptr_t)); + if (static_cast(addr) <= small_int_magnitude && + static_cast(addr) >= -small_int_magnitude) { + continue; + } + if (stack_mapping && MappingContainsAddress(*stack_mapping, addr)) { + continue; + } + if (last_hit_mapping && MappingContainsAddress(*last_hit_mapping, addr)) { + continue; + } + uintptr_t test = addr >> shift; + if (could_hit_mapping[(test >> 3) & array_mask] & (1 << (test & 7)) && + (hit_mapping = FindMappingNoBias(addr)) != nullptr && + hit_mapping->exec) { + last_hit_mapping = hit_mapping; + continue; + } + my_memcpy(sp, &defaced, sizeof(uintptr_t)); + } + // Zero any partial word at the top of the stack, if alignment is + // such that that is required. + if (sp < stack_copy + stack_len) { + my_memset(sp, 0, stack_copy + stack_len - sp); + } +} + +bool LinuxDumper::StackHasPointerToMapping(const uint8_t* stack_copy, + size_t stack_len, + uintptr_t sp_offset, + const MappingInfo& mapping) { + // Loop over all stack words that would have been on the stack in + // the target process (i.e. are word aligned, and at addresses >= + // the stack pointer). Regardless of the alignment of |stack_copy|, + // the memory starting at |stack_copy| + |offset| represents an + // aligned word in the target process. + const uintptr_t low_addr = mapping.system_mapping_info.start_addr; + const uintptr_t high_addr = mapping.system_mapping_info.end_addr; + const uintptr_t offset = + (sp_offset + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1); + + for (const uint8_t* sp = stack_copy + offset; + sp <= stack_copy + stack_len - sizeof(uintptr_t); + sp += sizeof(uintptr_t)) { + uintptr_t addr; + my_memcpy(&addr, sp, sizeof(uintptr_t)); + if (low_addr <= addr && addr <= high_addr) + return true; + } + return false; +} + +// Find the mapping which the given memory address falls in. +const MappingInfo* LinuxDumper::FindMapping(const void* address) const { + const uintptr_t addr = (uintptr_t) address; + + for (size_t i = 0; i < mappings_.size(); ++i) { + const uintptr_t start = static_cast(mappings_[i]->start_addr); + if (addr >= start && addr - start < mappings_[i]->size) + return mappings_[i]; + } + + return NULL; +} + +// Find the mapping which the given memory address falls in. Uses the +// unadjusted mapping address range from the kernel, rather than the +// biased range. +const MappingInfo* LinuxDumper::FindMappingNoBias(uintptr_t address) const { + for (size_t i = 0; i < mappings_.size(); ++i) { + if (address >= mappings_[i]->system_mapping_info.start_addr && + address < mappings_[i]->system_mapping_info.end_addr) { + return mappings_[i]; + } + } + return NULL; +} + +bool LinuxDumper::HandleDeletedFileInMapping(char* path) const { + static const size_t kDeletedSuffixLen = sizeof(kDeletedSuffix) - 1; + + // Check for ' (deleted)' in |path|. + // |path| has to be at least as long as "/x (deleted)". + const size_t path_len = my_strlen(path); + if (path_len < kDeletedSuffixLen + 2) + return false; + if (my_strncmp(path + path_len - kDeletedSuffixLen, kDeletedSuffix, + kDeletedSuffixLen) != 0) { + return false; + } + + // Check |path| against the /proc/pid/exe 'symlink'. + char exe_link[NAME_MAX]; + if (!BuildProcPath(exe_link, pid_, "exe")) + return false; + MappingInfo new_mapping = {0}; + if (!SafeReadLink(exe_link, new_mapping.name)) + return false; + char new_path[PATH_MAX]; + if (!GetMappingAbsolutePath(new_mapping, new_path)) + return false; + if (my_strcmp(path, new_path) != 0) + return false; + + // Check to see if someone actually named their executable 'foo (deleted)'. + struct kernel_stat exe_stat; + struct kernel_stat new_path_stat; + if (sys_stat(exe_link, &exe_stat) == 0 && + sys_stat(new_path, &new_path_stat) == 0 && + exe_stat.st_dev == new_path_stat.st_dev && + exe_stat.st_ino == new_path_stat.st_ino) { + return false; + } + + my_memcpy(path, exe_link, NAME_MAX); + return true; +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_dumper.h b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_dumper.h new file mode 100644 index 000000000..7bee160f1 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_dumper.h @@ -0,0 +1,326 @@ +// 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. + +// linux_dumper.h: Define the google_breakpad::LinuxDumper class, which +// is a base class for extracting information of a crashed process. It +// was originally a complete implementation using the ptrace API, but +// has been refactored to allow derived implementations supporting both +// ptrace and core dump. A portion of the original implementation is now +// in google_breakpad::LinuxPtraceDumper (see linux_ptrace_dumper.h for +// details). + +#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_ +#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_DUMPER_H_ + +#include +#include +#if defined(__ANDROID__) +#include +#endif +#include +#include +#include +#include + +#include + +#include "client/linux/dump_writer_common/mapping_info.h" +#include "client/linux/dump_writer_common/thread_info.h" +#include "common/linux/file_id.h" +#include "common/memory_allocator.h" +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +// Typedef for our parsing of the auxv variables in /proc/pid/auxv. +#if defined(__i386) || defined(__ARM_EABI__) || \ + (defined(__mips__) && _MIPS_SIM == _ABIO32) +typedef Elf32_auxv_t elf_aux_entry; +#elif defined(__x86_64) || defined(__aarch64__) || \ + (defined(__mips__) && _MIPS_SIM != _ABIO32) +typedef Elf64_auxv_t elf_aux_entry; +#endif + +typedef __typeof__(((elf_aux_entry*) 0)->a_un.a_val) elf_aux_val_t; + +// When we find the VDSO mapping in the process's address space, this +// is the name we use for it when writing it to the minidump. +// This should always be less than NAME_MAX! +const char kLinuxGateLibraryName[] = "linux-gate.so"; + +class LinuxDumper { + public: + // The |root_prefix| is prepended to mapping paths before opening them, which + // is useful if the crash originates from a chroot. + explicit LinuxDumper(pid_t pid, const char* root_prefix = ""); + + virtual ~LinuxDumper(); + + // Parse the data for |threads| and |mappings|. + virtual bool Init(); + + // Take any actions that could not be taken in Init(). LateInit() is + // called after all other caller's initialization is complete, and in + // particular after it has called ThreadsSuspend(), so that ptrace is + // available. + virtual bool LateInit(); + + // Return true if the dumper performs a post-mortem dump. + virtual bool IsPostMortem() const = 0; + + // Suspend/resume all threads in the given process. + virtual bool ThreadsSuspend() = 0; + virtual bool ThreadsResume() = 0; + + // Read information about the |index|-th thread of |threads_|. + // Returns true on success. One must have called |ThreadsSuspend| first. + virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info) = 0; + + size_t GetMainThreadIndex() const { + for (size_t i = 0; i < threads_.size(); ++i) { + if (threads_[i] == pid_) return i; + } + return -1u; + } + + // These are only valid after a call to |Init|. + const wasteful_vector& threads() { return threads_; } + const wasteful_vector& mappings() { return mappings_; } + const MappingInfo* FindMapping(const void* address) const; + // Find the mapping which the given memory address falls in. Unlike + // FindMapping, this method uses the unadjusted mapping address + // ranges from the kernel, rather than the ranges that have had the + // load bias applied. + const MappingInfo* FindMappingNoBias(uintptr_t address) const; + const wasteful_vector& auxv() { return auxv_; } + + // Find a block of memory to take as the stack given the top of stack pointer. + // stack: (output) the lowest address in the memory area + // stack_len: (output) the length of the memory area + // stack_top: the current top of the stack + bool GetStackInfo(const void** stack, size_t* stack_len, uintptr_t stack_top); + + // Sanitize a copy of the stack by overwriting words that are not + // pointers with a sentinel (0x0defaced). + // stack_copy: a copy of the stack to sanitize. |stack_copy| might + // not be word aligned, but it represents word aligned + // data copied from another location. + // stack_len: the length of the allocation pointed to by |stack_copy|. + // stack_pointer: the address of the stack pointer (used to locate + // the stack mapping, as an optimization). + // sp_offset: the offset relative to stack_copy that reflects the + // current value of the stack pointer. + void SanitizeStackCopy(uint8_t* stack_copy, size_t stack_len, + uintptr_t stack_pointer, uintptr_t sp_offset); + + // Test whether |stack_copy| contains a pointer-aligned word that + // could be an address within a given mapping. + // stack_copy: a copy of the stack to check. |stack_copy| might + // not be word aligned, but it represents word aligned + // data copied from another location. + // stack_len: the length of the allocation pointed to by |stack_copy|. + // sp_offset: the offset relative to stack_copy that reflects the + // current value of the stack pointer. + // mapping: the mapping against which to test stack words. + bool StackHasPointerToMapping(const uint8_t* stack_copy, size_t stack_len, + uintptr_t sp_offset, + const MappingInfo& mapping); + + PageAllocator* allocator() { return &allocator_; } + + // Copy content of |length| bytes from a given process |child|, + // starting from |src|, into |dest|. Returns true on success. + virtual bool CopyFromProcess(void* dest, pid_t child, const void* src, + size_t length) = 0; + + // Builds a proc path for a certain pid for a node (/proc//). + // |path| is a character array of at least NAME_MAX bytes to return the + // result.|node| is the final node without any slashes. Returns true on + // success. + virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const = 0; + + // Generate a File ID from the .text section of a mapped entry. + // If not a member, mapping_id is ignored. This method can also manipulate the + // |mapping|.name to truncate "(deleted)" from the file name if necessary. + bool ElfFileIdentifierForMapping(const MappingInfo& mapping, + bool member, + unsigned int mapping_id, + wasteful_vector& identifier); + + void SetCrashInfoFromSigInfo(const siginfo_t& siginfo); + + uintptr_t crash_address() const { return crash_address_; } + void set_crash_address(uintptr_t crash_address) { + crash_address_ = crash_address; + } + + int crash_signal() const { return crash_signal_; } + void set_crash_signal(int crash_signal) { crash_signal_ = crash_signal; } + const char* GetCrashSignalString() const; + + void set_crash_signal_code(int code) { crash_signal_code_ = code; } + int crash_signal_code() const { return crash_signal_code_; } + + void set_crash_exception_info(const std::vector& exception_info) { + assert(exception_info.size() <= MD_EXCEPTION_MAXIMUM_PARAMETERS); + crash_exception_info_ = exception_info; + } + const std::vector& crash_exception_info() const { + return crash_exception_info_; + } + + pid_t crash_thread() const { return crash_thread_; } + void set_crash_thread(pid_t crash_thread) { crash_thread_ = crash_thread; } + + // Concatenates the |root_prefix_| and |mapping| path. Writes into |path| and + // returns true unless the string is too long. + bool GetMappingAbsolutePath(const MappingInfo& mapping, + char path[PATH_MAX]) const; + + // Extracts the effective path and file name of from |mapping|. In most cases + // the effective name/path are just the mapping's path and basename. In some + // other cases, however, a library can be mapped from an archive (e.g., when + // loading .so libs from an apk on Android) and this method is able to + // reconstruct the original file name. + void GetMappingEffectiveNameAndPath(const MappingInfo& mapping, + char* file_path, + size_t file_path_size, + char* file_name, + size_t file_name_size); + + protected: + bool ReadAuxv(); + + virtual bool EnumerateMappings(); + + virtual bool EnumerateThreads() = 0; + + // For the case where a running program has been deleted, it'll show up in + // /proc/pid/maps as "/path/to/program (deleted)". If this is the case, then + // see if '/path/to/program (deleted)' matches /proc/pid/exe and return + // /proc/pid/exe in |path| so ELF identifier generation works correctly. This + // also checks to see if '/path/to/program (deleted)' exists, so it does not + // get fooled by a poorly named binary. + // For programs that don't end with ' (deleted)', this is a no-op. + // This assumes |path| is a buffer with length NAME_MAX. + // Returns true if |path| is modified. + bool HandleDeletedFileInMapping(char* path) const; + + // ID of the crashed process. + const pid_t pid_; + + // Path of the root directory to which mapping paths are relative. + const char* const root_prefix_; + + // Virtual address at which the process crashed. + uintptr_t crash_address_; + + // Signal that terminated the crashed process. + int crash_signal_; + + // The code associated with |crash_signal_|. + int crash_signal_code_; + + // The additional fields associated with |crash_signal_|. + std::vector crash_exception_info_; + + // ID of the crashed thread. + pid_t crash_thread_; + + mutable PageAllocator allocator_; + + // IDs of all the threads. + wasteful_vector threads_; + + // Info from /proc//maps. + wasteful_vector mappings_; + + // Info from /proc//auxv + wasteful_vector auxv_; + +#if defined(__ANDROID__) + private: + // Android M and later support packed ELF relocations in shared libraries. + // Packing relocations changes the vaddr of the LOAD segments, such that + // the effective load bias is no longer the same as the start address of + // the memory mapping containing the executable parts of the library. The + // packing is applied to the stripped library run on the target, but not to + // any other library, and in particular not to the library used to generate + // breakpad symbols. As a result, we need to adjust the |start_addr| for + // any mapping that results from a shared library that contains Android + // packed relocations, so that it properly represents the effective library + // load bias. The following functions support this adjustment. + + // Check that a given mapping at |start_addr| is for an ELF shared library. + // If it is, place the ELF header in |ehdr| and return true. + // The first LOAD segment in an ELF shared library has offset zero, so the + // ELF file header is at the start of this map entry, and in already mapped + // memory. + bool GetLoadedElfHeader(uintptr_t start_addr, ElfW(Ehdr)* ehdr); + + // For the ELF file mapped at |start_addr|, iterate ELF program headers to + // find the min vaddr of all program header LOAD segments, the vaddr for + // the DYNAMIC segment, and a count of DYNAMIC entries. Return values in + // |min_vaddr_ptr|, |dyn_vaddr_ptr|, and |dyn_count_ptr|. + // The program header table is also in already mapped memory. + void ParseLoadedElfProgramHeaders(ElfW(Ehdr)* ehdr, + uintptr_t start_addr, + uintptr_t* min_vaddr_ptr, + uintptr_t* dyn_vaddr_ptr, + size_t* dyn_count_ptr); + + // Search the DYNAMIC tags for the ELF file with the given |load_bias|, and + // return true if the tags indicate that the file contains Android packed + // relocations. Dynamic tags are found at |dyn_vaddr| past the |load_bias|. + bool HasAndroidPackedRelocations(uintptr_t load_bias, + uintptr_t dyn_vaddr, + size_t dyn_count); + + // If the ELF file mapped at |start_addr| contained Android packed + // relocations, return the load bias that the system linker (or Chromium + // crazy linker) will have used. If the file did not contain Android + // packed relocations, returns |start_addr|, indicating that no adjustment + // is necessary. + // The effective load bias is |start_addr| adjusted downwards by the + // min vaddr in the library LOAD segments. + uintptr_t GetEffectiveLoadBias(ElfW(Ehdr)* ehdr, uintptr_t start_addr); + + // Called from LateInit(). Iterates |mappings_| and rewrites the |start_addr| + // field of any that represent ELF shared libraries with Android packed + // relocations, so that |start_addr| is the load bias that the system linker + // (or Chromium crazy linker) used. This value matches the addresses produced + // when the non-relocation-packed library is used for breakpad symbol + // generation. + void LatePostprocessMappings(); +#endif // __ANDROID__ +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_HANDLER_LINUX_DUMPER_H_ diff --git a/shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc new file mode 100644 index 000000000..331f4bb34 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_dumper_unittest_helper.cc @@ -0,0 +1,95 @@ +// 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. +// +// Helper program for the linux_dumper class, which creates a bunch of +// threads. The first word of each thread's stack is set to the thread +// id. + +#include +#include +#include +#include +#include +#include + +#include "common/scoped_ptr.h" +#include "third_party/lss/linux_syscall_support.h" + +#if defined(__ARM_EABI__) +#define TID_PTR_REGISTER "r3" +#elif defined(__aarch64__) +#define TID_PTR_REGISTER "x3" +#elif defined(__i386) +#define TID_PTR_REGISTER "ecx" +#elif defined(__x86_64) +#define TID_PTR_REGISTER "rcx" +#elif defined(__mips__) +#define TID_PTR_REGISTER "$1" +#else +#error This test has not been ported to this platform. +#endif + +void* thread_function(void* data) { + int pipefd = *static_cast(data); + volatile pid_t* thread_id = new pid_t; + *thread_id = syscall(__NR_gettid); + // Signal parent that a thread has started. + uint8_t byte = 1; + if (write(pipefd, &byte, sizeof(byte)) != sizeof(byte)) { + perror("ERROR: parent notification failed"); + return NULL; + } + register volatile pid_t* thread_id_ptr asm(TID_PTR_REGISTER) = thread_id; + while (true) + asm volatile ("" : : "r" (thread_id_ptr)); + return NULL; +} + +int main(int argc, char* argv[]) { + if (argc < 3) { + fprintf(stderr, + "usage: linux_dumper_unittest_helper <# of threads>\n"); + return 1; + } + int pipefd = atoi(argv[1]); + int num_threads = atoi(argv[2]); + if (num_threads < 1) { + fprintf(stderr, "ERROR: number of threads is 0"); + return 1; + } + google_breakpad::scoped_array threads(new pthread_t[num_threads]); + pthread_attr_t thread_attributes; + pthread_attr_init(&thread_attributes); + pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_DETACHED); + for (int i = 1; i < num_threads; i++) { + pthread_create(&threads[i], &thread_attributes, &thread_function, &pipefd); + } + thread_function(&pipefd); + return 0; +} diff --git a/shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper.cc b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper.cc new file mode 100644 index 000000000..e3ddb81a6 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper.cc @@ -0,0 +1,376 @@ +// 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. + +// linux_ptrace_dumper.cc: Implement google_breakpad::LinuxPtraceDumper. +// See linux_ptrace_dumper.h for detals. +// This class was originally splitted from google_breakpad::LinuxDumper. + +// This code deals with the mechanics of getting information about a crashed +// process. Since this code may run in a compromised address space, the same +// rules apply as detailed at the top of minidump_writer.h: no libc calls and +// use the alternative allocator. + +#include "client/linux/minidump_writer/linux_ptrace_dumper.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__i386) +#include +#endif + +#include "client/linux/minidump_writer/directory_reader.h" +#include "client/linux/minidump_writer/line_reader.h" +#include "common/linux/linux_libc_support.h" +#include "third_party/lss/linux_syscall_support.h" + +// Suspends a thread by attaching to it. +static bool SuspendThread(pid_t pid) { + // This may fail if the thread has just died or debugged. + errno = 0; + if (sys_ptrace(PTRACE_ATTACH, pid, NULL, NULL) != 0 && + errno != 0) { + return false; + } + while (sys_waitpid(pid, NULL, __WALL) < 0) { + if (errno != EINTR) { + sys_ptrace(PTRACE_DETACH, pid, NULL, NULL); + return false; + } + } +#if defined(__i386) || defined(__x86_64) + // On x86, the stack pointer is NULL or -1, when executing trusted code in + // the seccomp sandbox. Not only does this cause difficulties down the line + // when trying to dump the thread's stack, it also results in the minidumps + // containing information about the trusted threads. This information is + // generally completely meaningless and just pollutes the minidumps. + // We thus test the stack pointer and exclude any threads that are part of + // the seccomp sandbox's trusted code. + user_regs_struct regs; + if (sys_ptrace(PTRACE_GETREGS, pid, NULL, ®s) == -1 || +#if defined(__i386) + !regs.esp +#elif defined(__x86_64) + !regs.rsp +#endif + ) { + sys_ptrace(PTRACE_DETACH, pid, NULL, NULL); + return false; + } +#endif + return true; +} + +// Resumes a thread by detaching from it. +static bool ResumeThread(pid_t pid) { + return sys_ptrace(PTRACE_DETACH, pid, NULL, NULL) >= 0; +} + +namespace google_breakpad { + +LinuxPtraceDumper::LinuxPtraceDumper(pid_t pid) + : LinuxDumper(pid), + threads_suspended_(false) { +} + +bool LinuxPtraceDumper::BuildProcPath(char* path, pid_t pid, + const char* node) const { + if (!path || !node || pid <= 0) + return false; + + size_t node_len = my_strlen(node); + if (node_len == 0) + return false; + + const unsigned pid_len = my_uint_len(pid); + const size_t total_length = 6 + pid_len + 1 + node_len; + if (total_length >= NAME_MAX) + return false; + + my_memcpy(path, "/proc/", 6); + my_uitos(path + 6, pid, pid_len); + path[6 + pid_len] = '/'; + my_memcpy(path + 6 + pid_len + 1, node, node_len); + path[total_length] = '\0'; + return true; +} + +bool LinuxPtraceDumper::CopyFromProcess(void* dest, pid_t child, + const void* src, size_t length) { + unsigned long tmp = 55; + size_t done = 0; + static const size_t word_size = sizeof(tmp); + uint8_t* const local = (uint8_t*) dest; + uint8_t* const remote = (uint8_t*) src; + + while (done < length) { + const size_t l = (length - done > word_size) ? word_size : (length - done); + if (sys_ptrace(PTRACE_PEEKDATA, child, remote + done, &tmp) == -1) { + tmp = 0; + } + my_memcpy(local + done, &tmp, l); + done += l; + } + return true; +} + +bool LinuxPtraceDumper::ReadRegisterSet(ThreadInfo* info, pid_t tid) +{ +#ifdef PTRACE_GETREGSET + struct iovec io; + info->GetGeneralPurposeRegisters(&io.iov_base, &io.iov_len); + if (sys_ptrace(PTRACE_GETREGSET, tid, (void*)NT_PRSTATUS, (void*)&io) == -1) { + return false; + } + + info->GetFloatingPointRegisters(&io.iov_base, &io.iov_len); + if (sys_ptrace(PTRACE_GETREGSET, tid, (void*)NT_FPREGSET, (void*)&io) == -1) { + return false; + } + return true; +#else + return false; +#endif +} + +bool LinuxPtraceDumper::ReadRegisters(ThreadInfo* info, pid_t tid) { +#ifdef PTRACE_GETREGS + void* gp_addr; + info->GetGeneralPurposeRegisters(&gp_addr, NULL); + if (sys_ptrace(PTRACE_GETREGS, tid, NULL, gp_addr) == -1) { + return false; + } + +#if !(defined(__ANDROID__) && defined(__ARM_EABI__)) + // When running an arm build on an arm64 device, attempting to get the + // floating point registers fails. On Android, the floating point registers + // aren't written to the cpu context anyway, so just don't get them here. + // See http://crbug.com/508324 + void* fp_addr; + info->GetFloatingPointRegisters(&fp_addr, NULL); + if (sys_ptrace(PTRACE_GETFPREGS, tid, NULL, fp_addr) == -1) { + return false; + } +#endif // !(defined(__ANDROID__) && defined(__ARM_EABI__)) + return true; +#else // PTRACE_GETREGS + return false; +#endif +} + +// Read thread info from /proc/$pid/status. +// Fill out the |tgid|, |ppid| and |pid| members of |info|. If unavailable, +// these members are set to -1. Returns true iff all three members are +// available. +bool LinuxPtraceDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) { + if (index >= threads_.size()) + return false; + + pid_t tid = threads_[index]; + + assert(info != NULL); + char status_path[NAME_MAX]; + if (!BuildProcPath(status_path, tid, "status")) + return false; + + const int fd = sys_open(status_path, O_RDONLY, 0); + if (fd < 0) + return false; + + LineReader* const line_reader = new(allocator_) LineReader(fd); + const char* line; + unsigned line_len; + + info->ppid = info->tgid = -1; + + while (line_reader->GetNextLine(&line, &line_len)) { + if (my_strncmp("Tgid:\t", line, 6) == 0) { + my_strtoui(&info->tgid, line + 6); + } else if (my_strncmp("PPid:\t", line, 6) == 0) { + my_strtoui(&info->ppid, line + 6); + } + + line_reader->PopLine(line_len); + } + sys_close(fd); + + if (info->ppid == -1 || info->tgid == -1) + return false; + + if (!ReadRegisterSet(info, tid)) { + if (!ReadRegisters(info, tid)) { + return false; + } + } + +#if defined(__i386) +#if !defined(bit_FXSAVE) // e.g. Clang +#define bit_FXSAVE bit_FXSR +#endif + // Detect if the CPU supports the FXSAVE/FXRSTOR instructions + int eax, ebx, ecx, edx; + __cpuid(1, eax, ebx, ecx, edx); + if (edx & bit_FXSAVE) { + if (sys_ptrace(PTRACE_GETFPXREGS, tid, NULL, &info->fpxregs) == -1) { + return false; + } + } else { + memset(&info->fpxregs, 0, sizeof(info->fpxregs)); + } +#endif // defined(__i386) + +#if defined(__i386) || defined(__x86_64) + for (unsigned i = 0; i < ThreadInfo::kNumDebugRegisters; ++i) { + if (sys_ptrace( + PTRACE_PEEKUSER, tid, + reinterpret_cast (offsetof(struct user, + u_debugreg[0]) + i * + sizeof(debugreg_t)), + &info->dregs[i]) == -1) { + return false; + } + } +#endif + +#if defined(__mips__) + sys_ptrace(PTRACE_PEEKUSER, tid, + reinterpret_cast(PC), &info->mcontext.pc); + sys_ptrace(PTRACE_PEEKUSER, tid, + reinterpret_cast(DSP_BASE), &info->mcontext.hi1); + sys_ptrace(PTRACE_PEEKUSER, tid, + reinterpret_cast(DSP_BASE + 1), &info->mcontext.lo1); + sys_ptrace(PTRACE_PEEKUSER, tid, + reinterpret_cast(DSP_BASE + 2), &info->mcontext.hi2); + sys_ptrace(PTRACE_PEEKUSER, tid, + reinterpret_cast(DSP_BASE + 3), &info->mcontext.lo2); + sys_ptrace(PTRACE_PEEKUSER, tid, + reinterpret_cast(DSP_BASE + 4), &info->mcontext.hi3); + sys_ptrace(PTRACE_PEEKUSER, tid, + reinterpret_cast(DSP_BASE + 5), &info->mcontext.lo3); + sys_ptrace(PTRACE_PEEKUSER, tid, + reinterpret_cast(DSP_CONTROL), &info->mcontext.dsp); +#endif + + const uint8_t* stack_pointer; +#if defined(__i386) + my_memcpy(&stack_pointer, &info->regs.esp, sizeof(info->regs.esp)); +#elif defined(__x86_64) + my_memcpy(&stack_pointer, &info->regs.rsp, sizeof(info->regs.rsp)); +#elif defined(__ARM_EABI__) + my_memcpy(&stack_pointer, &info->regs.ARM_sp, sizeof(info->regs.ARM_sp)); +#elif defined(__aarch64__) + my_memcpy(&stack_pointer, &info->regs.sp, sizeof(info->regs.sp)); +#elif defined(__mips__) + stack_pointer = + reinterpret_cast(info->mcontext.gregs[MD_CONTEXT_MIPS_REG_SP]); +#else +#error "This code hasn't been ported to your platform yet." +#endif + info->stack_pointer = reinterpret_cast(stack_pointer); + + return true; +} + +bool LinuxPtraceDumper::IsPostMortem() const { + return false; +} + +bool LinuxPtraceDumper::ThreadsSuspend() { + if (threads_suspended_) + return true; + for (size_t i = 0; i < threads_.size(); ++i) { + if (!SuspendThread(threads_[i])) { + // If the thread either disappeared before we could attach to it, or if + // it was part of the seccomp sandbox's trusted code, it is OK to + // silently drop it from the minidump. + if (i < threads_.size() - 1) { + my_memmove(&threads_[i], &threads_[i + 1], + (threads_.size() - i - 1) * sizeof(threads_[i])); + } + threads_.resize(threads_.size() - 1); + --i; + } + } + threads_suspended_ = true; + return threads_.size() > 0; +} + +bool LinuxPtraceDumper::ThreadsResume() { + if (!threads_suspended_) + return false; + bool good = true; + for (size_t i = 0; i < threads_.size(); ++i) + good &= ResumeThread(threads_[i]); + threads_suspended_ = false; + return good; +} + +// Parse /proc/$pid/task to list all the threads of the process identified by +// pid. +bool LinuxPtraceDumper::EnumerateThreads() { + char task_path[NAME_MAX]; + if (!BuildProcPath(task_path, pid_, "task")) + return false; + + const int fd = sys_open(task_path, O_RDONLY | O_DIRECTORY, 0); + if (fd < 0) + return false; + DirectoryReader* dir_reader = new(allocator_) DirectoryReader(fd); + + // The directory may contain duplicate entries which we filter by assuming + // that they are consecutive. + int last_tid = -1; + const char* dent_name; + while (dir_reader->GetNextEntry(&dent_name)) { + if (my_strcmp(dent_name, ".") && + my_strcmp(dent_name, "..")) { + int tid = 0; + if (my_strtoui(&tid, dent_name) && + last_tid != tid) { + last_tid = tid; + threads_.push_back(tid); + } + } + dir_reader->PopEntry(); + } + + sys_close(fd); + return true; +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper.h b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper.h new file mode 100644 index 000000000..cee581784 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper.h @@ -0,0 +1,101 @@ +// 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. + +// linux_ptrace_dumper.h: Define the google_breakpad::LinuxPtraceDumper +// class, which is derived from google_breakpad::LinuxDumper to extract +// information from a crashed process via ptrace. +// This class was originally splitted from google_breakpad::LinuxDumper. + +#ifndef CLIENT_LINUX_MINIDUMP_WRITER_LINUX_PTRACE_DUMPER_H_ +#define CLIENT_LINUX_MINIDUMP_WRITER_LINUX_PTRACE_DUMPER_H_ + +#include "client/linux/minidump_writer/linux_dumper.h" + +namespace google_breakpad { + +class LinuxPtraceDumper : public LinuxDumper { + public: + // Constructs a dumper for extracting information of a given process + // with a process ID of |pid|. + explicit LinuxPtraceDumper(pid_t pid); + + // Implements LinuxDumper::BuildProcPath(). + // Builds a proc path for a certain pid for a node (/proc//). + // |path| is a character array of at least NAME_MAX bytes to return the + // result. |node| is the final node without any slashes. Returns true on + // success. + virtual bool BuildProcPath(char* path, pid_t pid, const char* node) const; + + // Implements LinuxDumper::CopyFromProcess(). + // Copies content of |length| bytes from a given process |child|, + // starting from |src|, into |dest|. This method uses ptrace to extract + // the content from the target process. Always returns true. + virtual bool CopyFromProcess(void* dest, pid_t child, const void* src, + size_t length); + + // Implements LinuxDumper::GetThreadInfoByIndex(). + // Reads information about the |index|-th thread of |threads_|. + // Returns true on success. One must have called |ThreadsSuspend| first. + virtual bool GetThreadInfoByIndex(size_t index, ThreadInfo* info); + + // Implements LinuxDumper::IsPostMortem(). + // Always returns false to indicate this dumper performs a dump of + // a crashed process via ptrace. + virtual bool IsPostMortem() const; + + // Implements LinuxDumper::ThreadsSuspend(). + // Suspends all threads in the given process. Returns true on success. + virtual bool ThreadsSuspend(); + + // Implements LinuxDumper::ThreadsResume(). + // Resumes all threads in the given process. Returns true on success. + virtual bool ThreadsResume(); + + protected: + // Implements LinuxDumper::EnumerateThreads(). + // Enumerates all threads of the given process into |threads_|. + virtual bool EnumerateThreads(); + + private: + // Set to true if all threads of the crashed process are suspended. + bool threads_suspended_; + + // Read the tracee's registers on kernel with PTRACE_GETREGSET support. + // Returns false if PTRACE_GETREGSET is not defined. + // Returns true on success. + bool ReadRegisterSet(ThreadInfo* info, pid_t tid); + + // Read the tracee's registers on kernel with PTRACE_GETREGS support. + // Returns true on success. + bool ReadRegisters(ThreadInfo* info, pid_t tid); +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_HANDLER_LINUX_PTRACE_DUMPER_H_ diff --git a/shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc new file mode 100644 index 000000000..11c392d83 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/linux_ptrace_dumper_unittest.cc @@ -0,0 +1,582 @@ +// 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. + +// linux_ptrace_dumper_unittest.cc: +// Unit tests for google_breakpad::LinuxPtraceDumper. +// +// This file was renamed from linux_dumper_unittest.cc and modified due +// to LinuxDumper being splitted into two classes. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "breakpad_googletest_includes.h" +#include "client/linux/minidump_writer/linux_ptrace_dumper.h" +#include "client/linux/minidump_writer/minidump_writer_unittest_utils.h" +#include "common/linux/eintr_wrapper.h" +#include "common/linux/file_id.h" +#include "common/linux/ignore_ret.h" +#include "common/linux/safe_readlink.h" +#include "common/memory_allocator.h" +#include "common/using_std_string.h" + +#ifndef PR_SET_PTRACER +#define PR_SET_PTRACER 0x59616d61 +#endif + +using namespace google_breakpad; +using google_breakpad::elf::FileID; +using google_breakpad::elf::kDefaultBuildIdSize; + +namespace { + +pid_t SetupChildProcess(int number_of_threads) { + char kNumberOfThreadsArgument[2]; + sprintf(kNumberOfThreadsArgument, "%d", number_of_threads); + + int fds[2]; + EXPECT_NE(-1, pipe(fds)); + + pid_t child_pid = fork(); + if (child_pid == 0) { + // In child process. + close(fds[0]); + + string helper_path(GetHelperBinary()); + if (helper_path.empty()) { + fprintf(stderr, "Couldn't find helper binary\n"); + _exit(1); + } + + // Pass the pipe fd and the number of threads as arguments. + char pipe_fd_string[8]; + sprintf(pipe_fd_string, "%d", fds[1]); + execl(helper_path.c_str(), + "linux_dumper_unittest_helper", + pipe_fd_string, + kNumberOfThreadsArgument, + NULL); + // Kill if we get here. + printf("Errno from exec: %d", errno); + std::string err_str = "Exec of " + helper_path + " failed"; + perror(err_str.c_str()); + _exit(1); + } + close(fds[1]); + + // Wait for all child threads to indicate that they have started + for (int threads = 0; threads < number_of_threads; threads++) { + struct pollfd pfd; + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fds[0]; + pfd.events = POLLIN | POLLERR; + + const int r = HANDLE_EINTR(poll(&pfd, 1, 1000)); + EXPECT_EQ(1, r); + EXPECT_TRUE(pfd.revents & POLLIN); + uint8_t junk; + EXPECT_EQ(read(fds[0], &junk, sizeof(junk)), + static_cast(sizeof(junk))); + } + close(fds[0]); + + // There is a race here because we may stop a child thread before + // it is actually running the busy loop. Empirically this sleep + // is sufficient to avoid the race. + usleep(100000); + return child_pid; +} + +typedef wasteful_vector id_vector; +typedef testing::Test LinuxPtraceDumperTest; + +/* Fixture for running tests in a child process. */ +class LinuxPtraceDumperChildTest : public testing::Test { + protected: + virtual void SetUp() { + child_pid_ = fork(); +#ifndef __ANDROID__ + prctl(PR_SET_PTRACER, child_pid_); +#endif + } + + /* Gtest is calling TestBody from this class, which sets up a child + * process in which the RealTestBody virtual member is called. + * As such, TestBody is not supposed to be overridden in derived classes. + */ + virtual void TestBody() /* final */ { + if (child_pid_ == 0) { + // child process + RealTestBody(); + _exit(HasFatalFailure() ? kFatalFailure : + (HasNonfatalFailure() ? kNonFatalFailure : 0)); + } + + ASSERT_TRUE(child_pid_ > 0); + int status; + waitpid(child_pid_, &status, 0); + if (WEXITSTATUS(status) == kFatalFailure) { + GTEST_FATAL_FAILURE_("Test failed in child process"); + } else if (WEXITSTATUS(status) == kNonFatalFailure) { + GTEST_NONFATAL_FAILURE_("Test failed in child process"); + } + } + + /* Gtest defines TestBody functions through its macros, but classes + * derived from this one need to define RealTestBody instead. + * This is achieved by defining a TestBody macro further below. + */ + virtual void RealTestBody() = 0; + + id_vector make_vector() { + return id_vector(&allocator, kDefaultBuildIdSize); + } + + private: + static const int kFatalFailure = 1; + static const int kNonFatalFailure = 2; + + pid_t child_pid_; + PageAllocator allocator; +}; + +} // namespace + +/* Replace TestBody declarations within TEST*() with RealTestBody + * declarations */ +#define TestBody RealTestBody + +TEST_F(LinuxPtraceDumperChildTest, Setup) { + LinuxPtraceDumper dumper(getppid()); +} + +TEST_F(LinuxPtraceDumperChildTest, FindMappings) { + LinuxPtraceDumper dumper(getppid()); + ASSERT_TRUE(dumper.Init()); + + ASSERT_TRUE(dumper.FindMapping(reinterpret_cast(getpid))); + ASSERT_TRUE(dumper.FindMapping(reinterpret_cast(printf))); + ASSERT_FALSE(dumper.FindMapping(NULL)); +} + +TEST_F(LinuxPtraceDumperChildTest, ThreadList) { + LinuxPtraceDumper dumper(getppid()); + ASSERT_TRUE(dumper.Init()); + + ASSERT_GE(dumper.threads().size(), (size_t)1); + bool found = false; + for (size_t i = 0; i < dumper.threads().size(); ++i) { + if (dumper.threads()[i] == getppid()) { + ASSERT_FALSE(found); + found = true; + } + } + ASSERT_TRUE(found); +} + +// Helper stack class to close a file descriptor and unmap +// a mmap'ed mapping. +class StackHelper { + public: + StackHelper() + : fd_(-1), mapping_(NULL), size_(0) {} + ~StackHelper() { + if (size_) + munmap(mapping_, size_); + if (fd_ >= 0) + close(fd_); + } + void Init(int fd, char* mapping, size_t size) { + fd_ = fd; + mapping_ = mapping; + size_ = size; + } + + char* mapping() const { return mapping_; } + size_t size() const { return size_; } + + private: + int fd_; + char* mapping_; + size_t size_; +}; + +class LinuxPtraceDumperMappingsTest : public LinuxPtraceDumperChildTest { + protected: + virtual void SetUp(); + + string helper_path_; + size_t page_size_; + StackHelper helper_; +}; + +void LinuxPtraceDumperMappingsTest::SetUp() { + helper_path_ = GetHelperBinary(); + if (helper_path_.empty()) { + FAIL() << "Couldn't find helper binary"; + _exit(1); + } + + // mmap two segments out of the helper binary, one + // enclosed in the other, but with different protections. + page_size_ = sysconf(_SC_PAGESIZE); + const size_t kMappingSize = 3 * page_size_; + int fd = open(helper_path_.c_str(), O_RDONLY); + ASSERT_NE(-1, fd) << "Failed to open file: " << helper_path_ + << ", Error: " << strerror(errno); + char* mapping = + reinterpret_cast(mmap(NULL, + kMappingSize, + PROT_READ, + MAP_SHARED, + fd, + 0)); + ASSERT_TRUE(mapping); + + // Ensure that things get cleaned up. + helper_.Init(fd, mapping, kMappingSize); + + // Carve a page out of the first mapping with different permissions. + char* inside_mapping = reinterpret_cast( + mmap(mapping + 2 * page_size_, + page_size_, + PROT_NONE, + MAP_SHARED | MAP_FIXED, + fd, + // Map a different offset just to + // better test real-world conditions. + page_size_)); + ASSERT_TRUE(inside_mapping); + + LinuxPtraceDumperChildTest::SetUp(); +} + +TEST_F(LinuxPtraceDumperMappingsTest, MergedMappings) { + // Now check that LinuxPtraceDumper interpreted the mappings properly. + LinuxPtraceDumper dumper(getppid()); + ASSERT_TRUE(dumper.Init()); + int mapping_count = 0; + for (unsigned i = 0; i < dumper.mappings().size(); ++i) { + const MappingInfo& mapping = *dumper.mappings()[i]; + if (strcmp(mapping.name, this->helper_path_.c_str()) == 0) { + // This mapping should encompass the entire original mapped + // range. + EXPECT_EQ(reinterpret_cast(this->helper_.mapping()), + mapping.start_addr); + EXPECT_EQ(this->helper_.size(), mapping.size); + EXPECT_EQ(0U, mapping.offset); + mapping_count++; + } + } + EXPECT_EQ(1, mapping_count); +} + +TEST_F(LinuxPtraceDumperChildTest, BuildProcPath) { + const pid_t pid = getppid(); + LinuxPtraceDumper dumper(pid); + + char maps_path[NAME_MAX] = ""; + char maps_path_expected[NAME_MAX]; + snprintf(maps_path_expected, sizeof(maps_path_expected), + "/proc/%d/maps", pid); + EXPECT_TRUE(dumper.BuildProcPath(maps_path, pid, "maps")); + EXPECT_STREQ(maps_path_expected, maps_path); + + EXPECT_FALSE(dumper.BuildProcPath(NULL, pid, "maps")); + EXPECT_FALSE(dumper.BuildProcPath(maps_path, 0, "maps")); + EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, "")); + EXPECT_FALSE(dumper.BuildProcPath(maps_path, pid, NULL)); + + char long_node[NAME_MAX]; + size_t long_node_len = NAME_MAX - strlen("/proc/123") - 1; + memset(long_node, 'a', long_node_len); + long_node[long_node_len] = '\0'; + EXPECT_FALSE(dumper.BuildProcPath(maps_path, 123, long_node)); +} + +#if !defined(__ARM_EABI__) && !defined(__mips__) +// Ensure that the linux-gate VDSO is included in the mapping list. +TEST_F(LinuxPtraceDumperChildTest, MappingsIncludeLinuxGate) { + LinuxPtraceDumper dumper(getppid()); + ASSERT_TRUE(dumper.Init()); + + void* linux_gate_loc = + reinterpret_cast(dumper.auxv()[AT_SYSINFO_EHDR]); + ASSERT_TRUE(linux_gate_loc); + bool found_linux_gate = false; + + const wasteful_vector mappings = dumper.mappings(); + const MappingInfo* mapping; + for (unsigned i = 0; i < mappings.size(); ++i) { + mapping = mappings[i]; + if (!strcmp(mapping->name, kLinuxGateLibraryName)) { + found_linux_gate = true; + break; + } + } + EXPECT_TRUE(found_linux_gate); + EXPECT_EQ(linux_gate_loc, reinterpret_cast(mapping->start_addr)); + EXPECT_EQ(0, memcmp(linux_gate_loc, ELFMAG, SELFMAG)); +} + +// Ensure that the linux-gate VDSO can generate a non-zeroed File ID. +TEST_F(LinuxPtraceDumperChildTest, LinuxGateMappingID) { + LinuxPtraceDumper dumper(getppid()); + ASSERT_TRUE(dumper.Init()); + + bool found_linux_gate = false; + const wasteful_vector mappings = dumper.mappings(); + unsigned index = 0; + for (unsigned i = 0; i < mappings.size(); ++i) { + if (!strcmp(mappings[i]->name, kLinuxGateLibraryName)) { + found_linux_gate = true; + index = i; + break; + } + } + ASSERT_TRUE(found_linux_gate); + + // Need to suspend the child so ptrace actually works. + ASSERT_TRUE(dumper.ThreadsSuspend()); + id_vector identifier(make_vector()); + ASSERT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[index], + true, + index, + identifier)); + + id_vector empty_identifier(make_vector()); + empty_identifier.resize(kDefaultBuildIdSize, 0); + EXPECT_NE(empty_identifier, identifier); + EXPECT_TRUE(dumper.ThreadsResume()); +} +#endif + +TEST_F(LinuxPtraceDumperChildTest, FileIDsMatch) { + // Calculate the File ID of our binary using both + // FileID::ElfFileIdentifier and LinuxDumper::ElfFileIdentifierForMapping + // and ensure that we get the same result from both. + char exe_name[PATH_MAX]; + ASSERT_TRUE(SafeReadLink("/proc/self/exe", exe_name)); + + LinuxPtraceDumper dumper(getppid()); + ASSERT_TRUE(dumper.Init()); + const wasteful_vector mappings = dumper.mappings(); + bool found_exe = false; + unsigned i; + for (i = 0; i < mappings.size(); ++i) { + const MappingInfo* mapping = mappings[i]; + if (!strcmp(mapping->name, exe_name)) { + found_exe = true; + break; + } + } + ASSERT_TRUE(found_exe); + + id_vector identifier1(make_vector()); + id_vector identifier2(make_vector()); + EXPECT_TRUE(dumper.ElfFileIdentifierForMapping(*mappings[i], true, i, + identifier1)); + FileID fileid(exe_name); + EXPECT_TRUE(fileid.ElfFileIdentifier(identifier2)); + + string identifier_string1 = + FileID::ConvertIdentifierToUUIDString(identifier1); + string identifier_string2 = + FileID::ConvertIdentifierToUUIDString(identifier2); + EXPECT_EQ(identifier_string1, identifier_string2); +} + +/* Get back to normal behavior of TEST*() macros wrt TestBody. */ +#undef TestBody + +TEST(LinuxPtraceDumperTest, VerifyStackReadWithMultipleThreads) { + static const size_t kNumberOfThreadsInHelperProgram = 5; + + pid_t child_pid = SetupChildProcess(kNumberOfThreadsInHelperProgram); + ASSERT_NE(child_pid, -1); + + // Children are ready now. + LinuxPtraceDumper dumper(child_pid); + ASSERT_TRUE(dumper.Init()); +#if defined(THREAD_SANITIZER) + EXPECT_GE(dumper.threads().size(), (size_t)kNumberOfThreadsInHelperProgram); +#else + EXPECT_EQ(dumper.threads().size(), (size_t)kNumberOfThreadsInHelperProgram); +#endif + EXPECT_TRUE(dumper.ThreadsSuspend()); + + ThreadInfo one_thread; + size_t matching_threads = 0; + for (size_t i = 0; i < dumper.threads().size(); ++i) { + EXPECT_TRUE(dumper.GetThreadInfoByIndex(i, &one_thread)); + const void* stack; + size_t stack_len; + EXPECT_TRUE(dumper.GetStackInfo(&stack, &stack_len, + one_thread.stack_pointer)); + // In the helper program, we stored a pointer to the thread id in a + // specific register. Check that we can recover its value. +#if defined(__ARM_EABI__) + pid_t* process_tid_location = (pid_t*)(one_thread.regs.uregs[3]); +#elif defined(__aarch64__) + pid_t* process_tid_location = (pid_t*)(one_thread.regs.regs[3]); +#elif defined(__i386) + pid_t* process_tid_location = (pid_t*)(one_thread.regs.ecx); +#elif defined(__x86_64) + pid_t* process_tid_location = (pid_t*)(one_thread.regs.rcx); +#elif defined(__mips__) + pid_t* process_tid_location = + reinterpret_cast(one_thread.mcontext.gregs[1]); +#else +#error This test has not been ported to this platform. +#endif + pid_t one_thread_id; + dumper.CopyFromProcess(&one_thread_id, + dumper.threads()[i], + process_tid_location, + 4); + matching_threads += (dumper.threads()[i] == one_thread_id) ? 1 : 0; + } + EXPECT_EQ(matching_threads, kNumberOfThreadsInHelperProgram); + EXPECT_TRUE(dumper.ThreadsResume()); + kill(child_pid, SIGKILL); + + // Reap child + int status; + ASSERT_NE(-1, HANDLE_EINTR(waitpid(child_pid, &status, 0))); + ASSERT_TRUE(WIFSIGNALED(status)); + ASSERT_EQ(SIGKILL, WTERMSIG(status)); +} + +TEST_F(LinuxPtraceDumperTest, SanitizeStackCopy) { + static const size_t kNumberOfThreadsInHelperProgram = 1; + + pid_t child_pid = SetupChildProcess(kNumberOfThreadsInHelperProgram); + ASSERT_NE(child_pid, -1); + + LinuxPtraceDumper dumper(child_pid); + ASSERT_TRUE(dumper.Init()); + EXPECT_TRUE(dumper.ThreadsSuspend()); + + ThreadInfo thread_info; + EXPECT_TRUE(dumper.GetThreadInfoByIndex(0, &thread_info)); + + const uintptr_t defaced = +#if defined(__LP64__) + 0x0defaced0defaced; +#else + 0x0defaced; +#endif + + uintptr_t simulated_stack[2]; + + // Pointers into the stack shouldn't be sanitized. + memset(simulated_stack, 0xff, sizeof(simulated_stack)); + simulated_stack[1] = thread_info.stack_pointer; + dumper.SanitizeStackCopy(reinterpret_cast(&simulated_stack), + sizeof(simulated_stack), thread_info.stack_pointer, + sizeof(uintptr_t)); + ASSERT_NE(simulated_stack[1], defaced); + + // Memory prior to the stack pointer should be cleared. + ASSERT_EQ(simulated_stack[0], 0u); + + // Small integers should not be sanitized. + for (int i = -4096; i <= 4096; ++i) { + memset(simulated_stack, 0, sizeof(simulated_stack)); + simulated_stack[0] = static_cast(i); + dumper.SanitizeStackCopy(reinterpret_cast(&simulated_stack), + sizeof(simulated_stack), thread_info.stack_pointer, + 0u); + ASSERT_NE(simulated_stack[0], defaced); + } + + // The instruction pointer definitely should point into an executable mapping. + const MappingInfo* mapping_info = dumper.FindMappingNoBias( + reinterpret_cast(thread_info.GetInstructionPointer())); + ASSERT_NE(mapping_info, nullptr); + ASSERT_TRUE(mapping_info->exec); + + // Pointers to code shouldn't be sanitized. + memset(simulated_stack, 0, sizeof(simulated_stack)); + simulated_stack[1] = thread_info.GetInstructionPointer(); + dumper.SanitizeStackCopy(reinterpret_cast(&simulated_stack), + sizeof(simulated_stack), thread_info.stack_pointer, + 0u); + ASSERT_NE(simulated_stack[0], defaced); + + // String fragments should be sanitized. + memcpy(simulated_stack, "abcdefghijklmnop", sizeof(simulated_stack)); + dumper.SanitizeStackCopy(reinterpret_cast(&simulated_stack), + sizeof(simulated_stack), thread_info.stack_pointer, + 0u); + ASSERT_EQ(simulated_stack[0], defaced); + ASSERT_EQ(simulated_stack[1], defaced); + + // Heap pointers should be sanititzed. +#if defined(__ARM_EABI__) + uintptr_t heap_addr = thread_info.regs.uregs[3]; +#elif defined(__aarch64__) + uintptr_t heap_addr = thread_info.regs.regs[3]; +#elif defined(__i386) + uintptr_t heap_addr = thread_info.regs.ecx; +#elif defined(__x86_64) + uintptr_t heap_addr = thread_info.regs.rcx; +#elif defined(__mips__) + uintptr_t heap_addr = thread_info.mcontext.gregs[1]; +#else +#error This test has not been ported to this platform. +#endif + memset(simulated_stack, 0, sizeof(simulated_stack)); + simulated_stack[0] = heap_addr; + dumper.SanitizeStackCopy(reinterpret_cast(&simulated_stack), + sizeof(simulated_stack), thread_info.stack_pointer, + 0u); + ASSERT_EQ(simulated_stack[0], defaced); + + EXPECT_TRUE(dumper.ThreadsResume()); + kill(child_pid, SIGKILL); + + // Reap child. + int status; + ASSERT_NE(-1, HANDLE_EINTR(waitpid(child_pid, &status, 0))); + ASSERT_TRUE(WIFSIGNALED(status)); + ASSERT_EQ(SIGKILL, WTERMSIG(status)); +} diff --git a/shared/sentry/external/breakpad/src/client/linux/minidump_writer/minidump_writer.cc b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/minidump_writer.cc new file mode 100644 index 000000000..72a921664 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/minidump_writer.cc @@ -0,0 +1,1509 @@ +// 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 code writes out minidump files: +// http://msdn.microsoft.com/en-us/library/ms680378(VS.85,loband).aspx +// +// Minidumps are a Microsoft format which Breakpad uses for recording crash +// dumps. This code has to run in a compromised environment (the address space +// may have received SIGSEGV), thus the following rules apply: +// * You may not enter the dynamic linker. This means that we cannot call +// any symbols in a shared library (inc libc). Because of this we replace +// libc functions in linux_libc_support.h. +// * You may not call syscalls via the libc wrappers. This rule is a subset +// of the first rule but it bears repeating. We have direct wrappers +// around the system calls in linux_syscall_support.h. +// * You may not malloc. There's an alternative allocator in memory.h and +// a canonical instance in the LinuxDumper object. We use the placement +// new form to allocate objects and we don't delete them. + +#include "client/linux/handler/minidump_descriptor.h" +#include "client/linux/minidump_writer/minidump_writer.h" +#include "client/minidump_file_writer-inl.h" + +#include +#include +#include +#include +#include +#if defined(__ANDROID__) +#include +#endif +#include +#include +#include +#include +#include +#include + +#include + +#include "client/linux/dump_writer_common/thread_info.h" +#include "client/linux/dump_writer_common/ucontext_reader.h" +#include "client/linux/handler/exception_handler.h" +#include "client/linux/minidump_writer/cpu_set.h" +#include "client/linux/minidump_writer/line_reader.h" +#include "client/linux/minidump_writer/linux_dumper.h" +#include "client/linux/minidump_writer/linux_ptrace_dumper.h" +#include "client/linux/minidump_writer/proc_cpuinfo_reader.h" +#include "client/minidump_file_writer.h" +#include "common/linux/file_id.h" +#include "common/linux/linux_libc_support.h" +#include "common/minidump_type_helper.h" +#include "google_breakpad/common/minidump_format.h" +#include "third_party/lss/linux_syscall_support.h" + +namespace { + +using google_breakpad::AppMemoryList; +using google_breakpad::auto_wasteful_vector; +using google_breakpad::elf::kDefaultBuildIdSize; +using google_breakpad::ExceptionHandler; +using google_breakpad::CpuSet; +using google_breakpad::LineReader; +using google_breakpad::LinuxDumper; +using google_breakpad::LinuxPtraceDumper; +using google_breakpad::MDTypeHelper; +using google_breakpad::MappingEntry; +using google_breakpad::MappingInfo; +using google_breakpad::MappingList; +using google_breakpad::MinidumpFileWriter; +using google_breakpad::PageAllocator; +using google_breakpad::ProcCpuInfoReader; +using google_breakpad::RawContextCPU; +using google_breakpad::ThreadInfo; +using google_breakpad::TypedMDRVA; +using google_breakpad::UContextReader; +using google_breakpad::UntypedMDRVA; +using google_breakpad::wasteful_vector; + +typedef MDTypeHelper::MDRawDebug MDRawDebug; +typedef MDTypeHelper::MDRawLinkMap MDRawLinkMap; + +class MinidumpWriter { + public: + // The following kLimit* constants are for when minidump_size_limit_ is set + // and the minidump size might exceed it. + // + // Estimate for how big each thread's stack will be (in bytes). + static const unsigned kLimitAverageThreadStackLength = 8 * 1024; + // Number of threads whose stack size we don't want to limit. These base + // threads will simply be the first N threads returned by the dumper (although + // the crashing thread will never be limited). Threads beyond this count are + // the extra threads. + static const unsigned kLimitBaseThreadCount = 20; + // Maximum stack size to dump for any extra thread (in bytes). + static const unsigned kLimitMaxExtraThreadStackLen = 2 * 1024; + // Make sure this number of additional bytes can fit in the minidump + // (exclude the stack data). + static const unsigned kLimitMinidumpFudgeFactor = 64 * 1024; + + MinidumpWriter(const char* minidump_path, + int minidump_fd, + const ExceptionHandler::CrashContext* context, + const MappingList& mappings, + const AppMemoryList& appmem, + bool skip_stacks_if_mapping_unreferenced, + uintptr_t principal_mapping_address, + bool sanitize_stacks, + LinuxDumper* dumper) + : fd_(minidump_fd), + path_(minidump_path), + ucontext_(context ? &context->context : NULL), +#if !defined(__ARM_EABI__) && !defined(__mips__) + float_state_(context ? &context->float_state : NULL), +#endif + dumper_(dumper), + minidump_size_limit_(-1), + memory_blocks_(dumper_->allocator()), + mapping_list_(mappings), + app_memory_list_(appmem), + skip_stacks_if_mapping_unreferenced_( + skip_stacks_if_mapping_unreferenced), + principal_mapping_address_(principal_mapping_address), + principal_mapping_(nullptr), + sanitize_stacks_(sanitize_stacks) { + // Assert there should be either a valid fd or a valid path, not both. + assert(fd_ != -1 || minidump_path); + assert(fd_ == -1 || !minidump_path); + } + + bool Init() { + if (!dumper_->Init()) + return false; + + if (!dumper_->ThreadsSuspend() || !dumper_->LateInit()) + return false; + + if (skip_stacks_if_mapping_unreferenced_) { + principal_mapping_ = + dumper_->FindMappingNoBias(principal_mapping_address_); + if (!CrashingThreadReferencesPrincipalMapping()) + return false; + } + + if (fd_ != -1) + minidump_writer_.SetFile(fd_); + else if (!minidump_writer_.Open(path_)) + return false; + + return true; + } + + ~MinidumpWriter() { + // Don't close the file descriptor when it's been provided explicitly. + // Callers might still need to use it. + if (fd_ == -1) + minidump_writer_.Close(); + dumper_->ThreadsResume(); + } + + bool CrashingThreadReferencesPrincipalMapping() { + if (!ucontext_ || !principal_mapping_) + return false; + + const uintptr_t low_addr = + principal_mapping_->system_mapping_info.start_addr; + const uintptr_t high_addr = + principal_mapping_->system_mapping_info.end_addr; + + const uintptr_t stack_pointer = UContextReader::GetStackPointer(ucontext_); + const uintptr_t pc = UContextReader::GetInstructionPointer(ucontext_); + + if (pc >= low_addr && pc < high_addr) + return true; + + uint8_t* stack_copy; + const void* stack; + size_t stack_len; + + if (!dumper_->GetStackInfo(&stack, &stack_len, stack_pointer)) + return false; + + stack_copy = reinterpret_cast(Alloc(stack_len)); + dumper_->CopyFromProcess(stack_copy, GetCrashThread(), stack, stack_len); + + uintptr_t stack_pointer_offset = + stack_pointer - reinterpret_cast(stack); + + return dumper_->StackHasPointerToMapping( + stack_copy, stack_len, stack_pointer_offset, *principal_mapping_); + } + + bool Dump() { + // A minidump file contains a number of tagged streams. This is the number + // of stream which we write. + unsigned kNumWriters = 13; + + TypedMDRVA dir(&minidump_writer_); + { + // Ensure the header gets flushed, as that happens in the destructor. + // If a crash occurs somewhere below, at least the header will be + // intact. + TypedMDRVA header(&minidump_writer_); + if (!header.Allocate()) + return false; + + if (!dir.AllocateArray(kNumWriters)) + return false; + + my_memset(header.get(), 0, sizeof(MDRawHeader)); + + header.get()->signature = MD_HEADER_SIGNATURE; + header.get()->version = MD_HEADER_VERSION; + header.get()->time_date_stamp = time(NULL); + header.get()->stream_count = kNumWriters; + header.get()->stream_directory_rva = dir.position(); + } + + unsigned dir_index = 0; + MDRawDirectory dirent; + + if (!WriteThreadListStream(&dirent)) + return false; + dir.CopyIndex(dir_index++, &dirent); + + if (!WriteMappings(&dirent)) + return false; + dir.CopyIndex(dir_index++, &dirent); + + if (!WriteAppMemory()) + return false; + + if (!WriteMemoryListStream(&dirent)) + return false; + dir.CopyIndex(dir_index++, &dirent); + + if (!WriteExceptionStream(&dirent)) + return false; + dir.CopyIndex(dir_index++, &dirent); + + if (!WriteSystemInfoStream(&dirent)) + return false; + dir.CopyIndex(dir_index++, &dirent); + + dirent.stream_type = MD_LINUX_CPU_INFO; + if (!WriteFile(&dirent.location, "/proc/cpuinfo")) + NullifyDirectoryEntry(&dirent); + dir.CopyIndex(dir_index++, &dirent); + + dirent.stream_type = MD_LINUX_PROC_STATUS; + if (!WriteProcFile(&dirent.location, GetCrashThread(), "status")) + NullifyDirectoryEntry(&dirent); + dir.CopyIndex(dir_index++, &dirent); + + dirent.stream_type = MD_LINUX_LSB_RELEASE; + if (!WriteFile(&dirent.location, "/etc/lsb-release")) + NullifyDirectoryEntry(&dirent); + dir.CopyIndex(dir_index++, &dirent); + + dirent.stream_type = MD_LINUX_CMD_LINE; + if (!WriteProcFile(&dirent.location, GetCrashThread(), "cmdline")) + NullifyDirectoryEntry(&dirent); + dir.CopyIndex(dir_index++, &dirent); + + dirent.stream_type = MD_LINUX_ENVIRON; + if (!WriteProcFile(&dirent.location, GetCrashThread(), "environ")) + NullifyDirectoryEntry(&dirent); + dir.CopyIndex(dir_index++, &dirent); + + dirent.stream_type = MD_LINUX_AUXV; + if (!WriteProcFile(&dirent.location, GetCrashThread(), "auxv")) + NullifyDirectoryEntry(&dirent); + dir.CopyIndex(dir_index++, &dirent); + + dirent.stream_type = MD_LINUX_MAPS; + if (!WriteProcFile(&dirent.location, GetCrashThread(), "maps")) + NullifyDirectoryEntry(&dirent); + dir.CopyIndex(dir_index++, &dirent); + + dirent.stream_type = MD_LINUX_DSO_DEBUG; + if (!WriteDSODebugStream(&dirent)) + NullifyDirectoryEntry(&dirent); + dir.CopyIndex(dir_index++, &dirent); + + // If you add more directory entries, don't forget to update kNumWriters, + // above. + + dumper_->ThreadsResume(); + return true; + } + + bool FillThreadStack(MDRawThread* thread, uintptr_t stack_pointer, + uintptr_t pc, int max_stack_len, uint8_t** stack_copy) { + *stack_copy = NULL; + const void* stack; + size_t stack_len; + + thread->stack.start_of_memory_range = stack_pointer; + thread->stack.memory.data_size = 0; + thread->stack.memory.rva = minidump_writer_.position(); + + if (dumper_->GetStackInfo(&stack, &stack_len, stack_pointer)) { + if (max_stack_len >= 0 && + stack_len > static_cast(max_stack_len)) { + stack_len = max_stack_len; + // Skip empty chunks of length max_stack_len. + uintptr_t int_stack = reinterpret_cast(stack); + if (max_stack_len > 0) { + while (int_stack + max_stack_len < stack_pointer) { + int_stack += max_stack_len; + } + } + stack = reinterpret_cast(int_stack); + } + *stack_copy = reinterpret_cast(Alloc(stack_len)); + dumper_->CopyFromProcess(*stack_copy, thread->thread_id, stack, + stack_len); + + uintptr_t stack_pointer_offset = + stack_pointer - reinterpret_cast(stack); + if (skip_stacks_if_mapping_unreferenced_) { + if (!principal_mapping_) { + return true; + } + uintptr_t low_addr = principal_mapping_->system_mapping_info.start_addr; + uintptr_t high_addr = principal_mapping_->system_mapping_info.end_addr; + if ((pc < low_addr || pc > high_addr) && + !dumper_->StackHasPointerToMapping(*stack_copy, stack_len, + stack_pointer_offset, + *principal_mapping_)) { + return true; + } + } + + if (sanitize_stacks_) { + dumper_->SanitizeStackCopy(*stack_copy, stack_len, stack_pointer, + stack_pointer_offset); + } + + UntypedMDRVA memory(&minidump_writer_); + if (!memory.Allocate(stack_len)) + return false; + memory.Copy(*stack_copy, stack_len); + thread->stack.start_of_memory_range = reinterpret_cast(stack); + thread->stack.memory = memory.location(); + memory_blocks_.push_back(thread->stack); + } + return true; + } + + // Write information about the threads. + bool WriteThreadListStream(MDRawDirectory* dirent) { + const unsigned num_threads = dumper_->threads().size(); + + TypedMDRVA list(&minidump_writer_); + if (!list.AllocateObjectAndArray(num_threads, sizeof(MDRawThread))) + return false; + + dirent->stream_type = MD_THREAD_LIST_STREAM; + dirent->location = list.location(); + + *list.get() = num_threads; + + // If there's a minidump size limit, check if it might be exceeded. Since + // most of the space is filled with stack data, just check against that. + // If this expects to exceed the limit, set extra_thread_stack_len such + // that any thread beyond the first kLimitBaseThreadCount threads will + // have only kLimitMaxExtraThreadStackLen bytes dumped. + int extra_thread_stack_len = -1; // default to no maximum + if (minidump_size_limit_ >= 0) { + const unsigned estimated_total_stack_size = num_threads * + kLimitAverageThreadStackLength; + const off_t estimated_minidump_size = minidump_writer_.position() + + estimated_total_stack_size + kLimitMinidumpFudgeFactor; + if (estimated_minidump_size > minidump_size_limit_) + extra_thread_stack_len = kLimitMaxExtraThreadStackLen; + } + + for (unsigned i = 0; i < num_threads; ++i) { + MDRawThread thread; + my_memset(&thread, 0, sizeof(thread)); + thread.thread_id = dumper_->threads()[i]; + + // We have a different source of information for the crashing thread. If + // we used the actual state of the thread we would find it running in the + // signal handler with the alternative stack, which would be deeply + // unhelpful. + if (static_cast(thread.thread_id) == GetCrashThread() && + ucontext_ && + !dumper_->IsPostMortem()) { + uint8_t* stack_copy; + const uintptr_t stack_ptr = UContextReader::GetStackPointer(ucontext_); + if (!FillThreadStack(&thread, stack_ptr, + UContextReader::GetInstructionPointer(ucontext_), + -1, &stack_copy)) + return false; + + // Copy 256 bytes around crashing instruction pointer to minidump. + const size_t kIPMemorySize = 256; + uint64_t ip = UContextReader::GetInstructionPointer(ucontext_); + // Bound it to the upper and lower bounds of the memory map + // it's contained within. If it's not in mapped memory, + // don't bother trying to write it. + bool ip_is_mapped = false; + MDMemoryDescriptor ip_memory_d; + for (unsigned j = 0; j < dumper_->mappings().size(); ++j) { + const MappingInfo& mapping = *dumper_->mappings()[j]; + if (ip >= mapping.start_addr && + ip < mapping.start_addr + mapping.size) { + ip_is_mapped = true; + // Try to get 128 bytes before and after the IP, but + // settle for whatever's available. + ip_memory_d.start_of_memory_range = + std::max(mapping.start_addr, + uintptr_t(ip - (kIPMemorySize / 2))); + uintptr_t end_of_range = + std::min(uintptr_t(ip + (kIPMemorySize / 2)), + uintptr_t(mapping.start_addr + mapping.size)); + ip_memory_d.memory.data_size = + end_of_range - ip_memory_d.start_of_memory_range; + break; + } + } + + if (ip_is_mapped) { + UntypedMDRVA ip_memory(&minidump_writer_); + if (!ip_memory.Allocate(ip_memory_d.memory.data_size)) + return false; + uint8_t* memory_copy = + reinterpret_cast(Alloc(ip_memory_d.memory.data_size)); + dumper_->CopyFromProcess( + memory_copy, + thread.thread_id, + reinterpret_cast(ip_memory_d.start_of_memory_range), + ip_memory_d.memory.data_size); + ip_memory.Copy(memory_copy, ip_memory_d.memory.data_size); + ip_memory_d.memory = ip_memory.location(); + memory_blocks_.push_back(ip_memory_d); + } + + TypedMDRVA cpu(&minidump_writer_); + if (!cpu.Allocate()) + return false; + my_memset(cpu.get(), 0, sizeof(RawContextCPU)); +#if !defined(__ARM_EABI__) && !defined(__mips__) + UContextReader::FillCPUContext(cpu.get(), ucontext_, float_state_); +#else + UContextReader::FillCPUContext(cpu.get(), ucontext_); +#endif + thread.thread_context = cpu.location(); + crashing_thread_context_ = cpu.location(); + } else { + ThreadInfo info; + if (!dumper_->GetThreadInfoByIndex(i, &info)) + return false; + + uint8_t* stack_copy; + int max_stack_len = -1; // default to no maximum for this thread + if (minidump_size_limit_ >= 0 && i >= kLimitBaseThreadCount) + max_stack_len = extra_thread_stack_len; + if (!FillThreadStack(&thread, info.stack_pointer, + info.GetInstructionPointer(), max_stack_len, + &stack_copy)) + return false; + + TypedMDRVA cpu(&minidump_writer_); + if (!cpu.Allocate()) + return false; + my_memset(cpu.get(), 0, sizeof(RawContextCPU)); + info.FillCPUContext(cpu.get()); + thread.thread_context = cpu.location(); + if (dumper_->threads()[i] == GetCrashThread()) { + crashing_thread_context_ = cpu.location(); + if (!dumper_->IsPostMortem()) { + // This is the crashing thread of a live process, but + // no context was provided, so set the crash address + // while the instruction pointer is already here. + dumper_->set_crash_address(info.GetInstructionPointer()); + } + } + } + + list.CopyIndexAfterObject(i, &thread, sizeof(thread)); + } + + return true; + } + + // Write application-provided memory regions. + bool WriteAppMemory() { + for (AppMemoryList::const_iterator iter = app_memory_list_.begin(); + iter != app_memory_list_.end(); + ++iter) { + uint8_t* data_copy = + reinterpret_cast(dumper_->allocator()->Alloc(iter->length)); + dumper_->CopyFromProcess(data_copy, GetCrashThread(), iter->ptr, + iter->length); + + UntypedMDRVA memory(&minidump_writer_); + if (!memory.Allocate(iter->length)) { + return false; + } + memory.Copy(data_copy, iter->length); + MDMemoryDescriptor desc; + desc.start_of_memory_range = reinterpret_cast(iter->ptr); + desc.memory = memory.location(); + memory_blocks_.push_back(desc); + } + + return true; + } + + static bool ShouldIncludeMapping(const MappingInfo& mapping) { + if (mapping.name[0] == 0 || // only want modules with filenames. + // Only want to include one mapping per shared lib. + // Avoid filtering executable mappings. + (mapping.offset != 0 && !mapping.exec) || + mapping.size < 4096) { // too small to get a signature for. + return false; + } + + return true; + } + + // If there is caller-provided information about this mapping + // in the mapping_list_ list, return true. Otherwise, return false. + bool HaveMappingInfo(const MappingInfo& mapping) { + for (MappingList::const_iterator iter = mapping_list_.begin(); + iter != mapping_list_.end(); + ++iter) { + // Ignore any mappings that are wholly contained within + // mappings in the mapping_info_ list. + if (mapping.start_addr >= iter->first.start_addr && + (mapping.start_addr + mapping.size) <= + (iter->first.start_addr + iter->first.size)) { + return true; + } + } + return false; + } + + // Write information about the mappings in effect. Because we are using the + // minidump format, the information about the mappings is pretty limited. + // Because of this, we also include the full, unparsed, /proc/$x/maps file in + // another stream in the file. + bool WriteMappings(MDRawDirectory* dirent) { + const unsigned num_mappings = dumper_->mappings().size(); + unsigned num_output_mappings = mapping_list_.size(); + + for (unsigned i = 0; i < dumper_->mappings().size(); ++i) { + const MappingInfo& mapping = *dumper_->mappings()[i]; + if (ShouldIncludeMapping(mapping) && !HaveMappingInfo(mapping)) + num_output_mappings++; + } + + TypedMDRVA list(&minidump_writer_); + if (num_output_mappings) { + if (!list.AllocateObjectAndArray(num_output_mappings, MD_MODULE_SIZE)) + return false; + } else { + // Still create the module list stream, although it will have zero + // modules. + if (!list.Allocate()) + return false; + } + + dirent->stream_type = MD_MODULE_LIST_STREAM; + dirent->location = list.location(); + *list.get() = num_output_mappings; + + // First write all the mappings from the dumper + unsigned int j = 0; + for (unsigned i = 0; i < num_mappings; ++i) { + const MappingInfo& mapping = *dumper_->mappings()[i]; + if (!ShouldIncludeMapping(mapping) || HaveMappingInfo(mapping)) + continue; + + MDRawModule mod; + if (!FillRawModule(mapping, true, i, &mod, NULL)) + return false; + list.CopyIndexAfterObject(j++, &mod, MD_MODULE_SIZE); + } + // Next write all the mappings provided by the caller + for (MappingList::const_iterator iter = mapping_list_.begin(); + iter != mapping_list_.end(); + ++iter) { + MDRawModule mod; + if (!FillRawModule(iter->first, false, 0, &mod, iter->second)) + return false; + list.CopyIndexAfterObject(j++, &mod, MD_MODULE_SIZE); + } + + return true; + } + + // Fill the MDRawModule |mod| with information about the provided + // |mapping|. If |identifier| is non-NULL, use it instead of calculating + // a file ID from the mapping. + bool FillRawModule(const MappingInfo& mapping, + bool member, + unsigned int mapping_id, + MDRawModule* mod, + const uint8_t* identifier) { + my_memset(mod, 0, MD_MODULE_SIZE); + + mod->base_of_image = mapping.start_addr; + mod->size_of_image = mapping.size; + + auto_wasteful_vector identifier_bytes( + dumper_->allocator()); + + if (identifier) { + // GUID was provided by caller. + identifier_bytes.insert(identifier_bytes.end(), + identifier, + identifier + sizeof(MDGUID)); + } else { + // Note: ElfFileIdentifierForMapping() can manipulate the |mapping.name|. + dumper_->ElfFileIdentifierForMapping(mapping, + member, + mapping_id, + identifier_bytes); + } + + if (!identifier_bytes.empty()) { + UntypedMDRVA cv(&minidump_writer_); + if (!cv.Allocate(MDCVInfoELF_minsize + identifier_bytes.size())) + return false; + + const uint32_t cv_signature = MD_CVINFOELF_SIGNATURE; + cv.Copy(&cv_signature, sizeof(cv_signature)); + cv.Copy(cv.position() + sizeof(cv_signature), &identifier_bytes[0], + identifier_bytes.size()); + + mod->cv_record = cv.location(); + } + + char file_name[NAME_MAX]; + char file_path[NAME_MAX]; + dumper_->GetMappingEffectiveNameAndPath( + mapping, file_path, sizeof(file_path), file_name, sizeof(file_name)); + + MDLocationDescriptor ld; + if (!minidump_writer_.WriteString(file_path, my_strlen(file_path), &ld)) + return false; + mod->module_name_rva = ld.rva; + return true; + } + + bool WriteMemoryListStream(MDRawDirectory* dirent) { + TypedMDRVA list(&minidump_writer_); + if (memory_blocks_.size()) { + if (!list.AllocateObjectAndArray(memory_blocks_.size(), + sizeof(MDMemoryDescriptor))) + return false; + } else { + // Still create the memory list stream, although it will have zero + // memory blocks. + if (!list.Allocate()) + return false; + } + + dirent->stream_type = MD_MEMORY_LIST_STREAM; + dirent->location = list.location(); + + *list.get() = memory_blocks_.size(); + + for (size_t i = 0; i < memory_blocks_.size(); ++i) { + list.CopyIndexAfterObject(i, &memory_blocks_[i], + sizeof(MDMemoryDescriptor)); + } + return true; + } + + bool WriteExceptionStream(MDRawDirectory* dirent) { + TypedMDRVA exc(&minidump_writer_); + if (!exc.Allocate()) + return false; + + MDRawExceptionStream* stream = exc.get(); + my_memset(stream, 0, sizeof(MDRawExceptionStream)); + + dirent->stream_type = MD_EXCEPTION_STREAM; + dirent->location = exc.location(); + + stream->thread_id = GetCrashThread(); + stream->exception_record.exception_code = dumper_->crash_signal(); + stream->exception_record.exception_flags = dumper_->crash_signal_code(); + stream->exception_record.exception_address = dumper_->crash_address(); + const std::vector crash_exception_info = + dumper_->crash_exception_info(); + stream->exception_record.number_parameters = crash_exception_info.size(); + memcpy(stream->exception_record.exception_information, + crash_exception_info.data(), + sizeof(uint64_t) * crash_exception_info.size()); + stream->thread_context = crashing_thread_context_; + + return true; + } + + bool WriteSystemInfoStream(MDRawDirectory* dirent) { + TypedMDRVA si(&minidump_writer_); + if (!si.Allocate()) + return false; + my_memset(si.get(), 0, sizeof(MDRawSystemInfo)); + + dirent->stream_type = MD_SYSTEM_INFO_STREAM; + dirent->location = si.location(); + + WriteCPUInformation(si.get()); + WriteOSInformation(si.get()); + + return true; + } + + bool WriteDSODebugStream(MDRawDirectory* dirent) { + ElfW(Phdr)* phdr = reinterpret_cast(dumper_->auxv()[AT_PHDR]); + char* base; + int phnum = dumper_->auxv()[AT_PHNUM]; + if (!phnum || !phdr) + return false; + + // Assume the program base is at the beginning of the same page as the PHDR + base = reinterpret_cast(reinterpret_cast(phdr) & ~0xfff); + + // Search for the program PT_DYNAMIC segment + ElfW(Addr) dyn_addr = 0; + for (; phnum >= 0; phnum--, phdr++) { + ElfW(Phdr) ph; + if (!dumper_->CopyFromProcess(&ph, GetCrashThread(), phdr, sizeof(ph))) + return false; + + // Adjust base address with the virtual address of the PT_LOAD segment + // corresponding to offset 0 + if (ph.p_type == PT_LOAD && ph.p_offset == 0) { + base -= ph.p_vaddr; + } + if (ph.p_type == PT_DYNAMIC) { + dyn_addr = ph.p_vaddr; + } + } + if (!dyn_addr) + return false; + + ElfW(Dyn)* dynamic = reinterpret_cast(dyn_addr + base); + + // The dynamic linker makes information available that helps gdb find all + // DSOs loaded into the program. If this information is indeed available, + // dump it to a MD_LINUX_DSO_DEBUG stream. + struct r_debug* r_debug = NULL; + uint32_t dynamic_length = 0; + + for (int i = 0; ; ++i) { + ElfW(Dyn) dyn; + dynamic_length += sizeof(dyn); + if (!dumper_->CopyFromProcess(&dyn, GetCrashThread(), dynamic + i, + sizeof(dyn))) { + return false; + } + +#ifdef __mips__ + const int32_t debug_tag = DT_MIPS_RLD_MAP; +#else + const int32_t debug_tag = DT_DEBUG; +#endif + if (dyn.d_tag == debug_tag) { + r_debug = reinterpret_cast(dyn.d_un.d_ptr); + continue; + } else if (dyn.d_tag == DT_NULL) { + break; + } + } + + // The "r_map" field of that r_debug struct contains a linked list of all + // loaded DSOs. + // Our list of DSOs potentially is different from the ones in the crashing + // process. So, we have to be careful to never dereference pointers + // directly. Instead, we use CopyFromProcess() everywhere. + // See for a more detailed discussion of the how the dynamic + // loader communicates with debuggers. + + // Count the number of loaded DSOs + int dso_count = 0; + struct r_debug debug_entry; + if (!dumper_->CopyFromProcess(&debug_entry, GetCrashThread(), r_debug, + sizeof(debug_entry))) { + return false; + } + for (struct link_map* ptr = debug_entry.r_map; ptr; ) { + struct link_map map; + if (!dumper_->CopyFromProcess(&map, GetCrashThread(), ptr, sizeof(map))) + return false; + + ptr = map.l_next; + dso_count++; + } + + MDRVA linkmap_rva = minidump_writer_.kInvalidMDRVA; + if (dso_count > 0) { + // If we have at least one DSO, create an array of MDRawLinkMap + // entries in the minidump file. + TypedMDRVA linkmap(&minidump_writer_); + if (!linkmap.AllocateArray(dso_count)) + return false; + linkmap_rva = linkmap.location().rva; + int idx = 0; + + // Iterate over DSOs and write their information to mini dump + for (struct link_map* ptr = debug_entry.r_map; ptr; ) { + struct link_map map; + if (!dumper_->CopyFromProcess(&map, GetCrashThread(), ptr, sizeof(map))) + return false; + + ptr = map.l_next; + char filename[257] = { 0 }; + if (map.l_name) { + dumper_->CopyFromProcess(filename, GetCrashThread(), map.l_name, + sizeof(filename) - 1); + } + MDLocationDescriptor location; + if (!minidump_writer_.WriteString(filename, 0, &location)) + return false; + MDRawLinkMap entry; + entry.name = location.rva; + entry.addr = map.l_addr; + entry.ld = reinterpret_cast(map.l_ld); + linkmap.CopyIndex(idx++, &entry); + } + } + + // Write MD_LINUX_DSO_DEBUG record + TypedMDRVA debug(&minidump_writer_); + if (!debug.AllocateObjectAndArray(1, dynamic_length)) + return false; + my_memset(debug.get(), 0, sizeof(MDRawDebug)); + dirent->stream_type = MD_LINUX_DSO_DEBUG; + dirent->location = debug.location(); + + debug.get()->version = debug_entry.r_version; + debug.get()->map = linkmap_rva; + debug.get()->dso_count = dso_count; + debug.get()->brk = debug_entry.r_brk; + debug.get()->ldbase = debug_entry.r_ldbase; + debug.get()->dynamic = reinterpret_cast(dynamic); + + wasteful_vector dso_debug_data(dumper_->allocator(), dynamic_length); + // The passed-in size to the constructor (above) is only a hint. + // Must call .resize() to do actual initialization of the elements. + dso_debug_data.resize(dynamic_length); + dumper_->CopyFromProcess(&dso_debug_data[0], GetCrashThread(), dynamic, + dynamic_length); + debug.CopyIndexAfterObject(0, &dso_debug_data[0], dynamic_length); + + return true; + } + + void set_minidump_size_limit(off_t limit) { minidump_size_limit_ = limit; } + + private: + void* Alloc(unsigned bytes) { + return dumper_->allocator()->Alloc(bytes); + } + + pid_t GetCrashThread() const { + return dumper_->crash_thread(); + } + + void NullifyDirectoryEntry(MDRawDirectory* dirent) { + dirent->stream_type = 0; + dirent->location.data_size = 0; + dirent->location.rva = 0; + } + +#if defined(__i386__) || defined(__x86_64__) || defined(__mips__) + bool WriteCPUInformation(MDRawSystemInfo* sys_info) { + char vendor_id[sizeof(sys_info->cpu.x86_cpu_info.vendor_id) + 1] = {0}; + static const char vendor_id_name[] = "vendor_id"; + + struct CpuInfoEntry { + const char* info_name; + int value; + bool found; + } cpu_info_table[] = { + { "processor", -1, false }, +#if defined(__i386__) || defined(__x86_64__) + { "model", 0, false }, + { "stepping", 0, false }, + { "cpu family", 0, false }, +#endif + }; + + // processor_architecture should always be set, do this first + sys_info->processor_architecture = +#if defined(__mips__) +# if _MIPS_SIM == _ABIO32 + MD_CPU_ARCHITECTURE_MIPS; +# elif _MIPS_SIM == _ABI64 + MD_CPU_ARCHITECTURE_MIPS64; +# else +# error "This mips ABI is currently not supported (n32)" +#endif +#elif defined(__i386__) + MD_CPU_ARCHITECTURE_X86; +#else + MD_CPU_ARCHITECTURE_AMD64; +#endif + + const int fd = sys_open("/proc/cpuinfo", O_RDONLY, 0); + if (fd < 0) + return false; + + { + PageAllocator allocator; + ProcCpuInfoReader* const reader = new(allocator) ProcCpuInfoReader(fd); + const char* field; + while (reader->GetNextField(&field)) { + bool is_first_entry = true; + for (CpuInfoEntry& entry : cpu_info_table) { + if (!is_first_entry && entry.found) { + // except for the 'processor' field, ignore repeated values. + continue; + } + is_first_entry = false; + if (!my_strcmp(field, entry.info_name)) { + size_t value_len; + const char* value = reader->GetValueAndLen(&value_len); + if (value_len == 0) + continue; + + uintptr_t val; + if (my_read_decimal_ptr(&val, value) == value) + continue; + + entry.value = static_cast(val); + entry.found = true; + } + } + + // special case for vendor_id + if (!my_strcmp(field, vendor_id_name)) { + size_t value_len; + const char* value = reader->GetValueAndLen(&value_len); + if (value_len > 0) + my_strlcpy(vendor_id, value, sizeof(vendor_id)); + } + } + sys_close(fd); + } + + // make sure we got everything we wanted + for (const CpuInfoEntry& entry : cpu_info_table) { + if (!entry.found) { + return false; + } + } + // cpu_info_table[0] holds the last cpu id listed in /proc/cpuinfo, + // assuming this is the highest id, change it to the number of CPUs + // by adding one. + cpu_info_table[0].value++; + + sys_info->number_of_processors = cpu_info_table[0].value; +#if defined(__i386__) || defined(__x86_64__) + sys_info->processor_level = cpu_info_table[3].value; + sys_info->processor_revision = cpu_info_table[1].value << 8 | + cpu_info_table[2].value; +#endif + + if (vendor_id[0] != '\0') { + my_memcpy(sys_info->cpu.x86_cpu_info.vendor_id, vendor_id, + sizeof(sys_info->cpu.x86_cpu_info.vendor_id)); + } + return true; + } +#elif defined(__arm__) || defined(__aarch64__) + bool WriteCPUInformation(MDRawSystemInfo* sys_info) { + // The CPUID value is broken up in several entries in /proc/cpuinfo. + // This table is used to rebuild it from the entries. + const struct CpuIdEntry { + const char* field; + char format; + char bit_lshift; + char bit_length; + } cpu_id_entries[] = { + { "CPU implementer", 'x', 24, 8 }, + { "CPU variant", 'x', 20, 4 }, + { "CPU part", 'x', 4, 12 }, + { "CPU revision", 'd', 0, 4 }, + }; + + // The ELF hwcaps are listed in the "Features" entry as textual tags. + // This table is used to rebuild them. + const struct CpuFeaturesEntry { + const char* tag; + uint32_t hwcaps; + } cpu_features_entries[] = { +#if defined(__arm__) + { "swp", MD_CPU_ARM_ELF_HWCAP_SWP }, + { "half", MD_CPU_ARM_ELF_HWCAP_HALF }, + { "thumb", MD_CPU_ARM_ELF_HWCAP_THUMB }, + { "26bit", MD_CPU_ARM_ELF_HWCAP_26BIT }, + { "fastmult", MD_CPU_ARM_ELF_HWCAP_FAST_MULT }, + { "fpa", MD_CPU_ARM_ELF_HWCAP_FPA }, + { "vfp", MD_CPU_ARM_ELF_HWCAP_VFP }, + { "edsp", MD_CPU_ARM_ELF_HWCAP_EDSP }, + { "java", MD_CPU_ARM_ELF_HWCAP_JAVA }, + { "iwmmxt", MD_CPU_ARM_ELF_HWCAP_IWMMXT }, + { "crunch", MD_CPU_ARM_ELF_HWCAP_CRUNCH }, + { "thumbee", MD_CPU_ARM_ELF_HWCAP_THUMBEE }, + { "neon", MD_CPU_ARM_ELF_HWCAP_NEON }, + { "vfpv3", MD_CPU_ARM_ELF_HWCAP_VFPv3 }, + { "vfpv3d16", MD_CPU_ARM_ELF_HWCAP_VFPv3D16 }, + { "tls", MD_CPU_ARM_ELF_HWCAP_TLS }, + { "vfpv4", MD_CPU_ARM_ELF_HWCAP_VFPv4 }, + { "idiva", MD_CPU_ARM_ELF_HWCAP_IDIVA }, + { "idivt", MD_CPU_ARM_ELF_HWCAP_IDIVT }, + { "idiv", MD_CPU_ARM_ELF_HWCAP_IDIVA | MD_CPU_ARM_ELF_HWCAP_IDIVT }, +#elif defined(__aarch64__) + // No hwcaps on aarch64. +#endif + }; + + // processor_architecture should always be set, do this first + sys_info->processor_architecture = +#if defined(__aarch64__) + MD_CPU_ARCHITECTURE_ARM64_OLD; +#else + MD_CPU_ARCHITECTURE_ARM; +#endif + + // /proc/cpuinfo is not readable under various sandboxed environments + // (e.g. Android services with the android:isolatedProcess attribute) + // prepare for this by setting default values now, which will be + // returned when this happens. + // + // Note: Bogus values are used to distinguish between failures (to + // read /sys and /proc files) and really badly configured kernels. + sys_info->number_of_processors = 0; + sys_info->processor_level = 1U; // There is no ARMv1 + sys_info->processor_revision = 42; + sys_info->cpu.arm_cpu_info.cpuid = 0; + sys_info->cpu.arm_cpu_info.elf_hwcaps = 0; + + // Counting the number of CPUs involves parsing two sysfs files, + // because the content of /proc/cpuinfo will only mirror the number + // of 'online' cores, and thus will vary with time. + // See http://www.kernel.org/doc/Documentation/cputopology.txt + { + CpuSet cpus_present; + CpuSet cpus_possible; + + int fd = sys_open("/sys/devices/system/cpu/present", O_RDONLY, 0); + if (fd >= 0) { + cpus_present.ParseSysFile(fd); + sys_close(fd); + + fd = sys_open("/sys/devices/system/cpu/possible", O_RDONLY, 0); + if (fd >= 0) { + cpus_possible.ParseSysFile(fd); + sys_close(fd); + + cpus_present.IntersectWith(cpus_possible); + int cpu_count = cpus_present.GetCount(); + if (cpu_count > 255) + cpu_count = 255; + sys_info->number_of_processors = static_cast(cpu_count); + } + } + } + + // Parse /proc/cpuinfo to reconstruct the CPUID value, as well + // as the ELF hwcaps field. For the latter, it would be easier to + // read /proc/self/auxv but unfortunately, this file is not always + // readable from regular Android applications on later versions + // (>= 4.1) of the Android platform. + const int fd = sys_open("/proc/cpuinfo", O_RDONLY, 0); + if (fd < 0) { + // Do not return false here to allow the minidump generation + // to happen properly. + return true; + } + + { + PageAllocator allocator; + ProcCpuInfoReader* const reader = + new(allocator) ProcCpuInfoReader(fd); + const char* field; + while (reader->GetNextField(&field)) { + for (const CpuIdEntry& entry : cpu_id_entries) { + if (my_strcmp(entry.field, field) != 0) + continue; + uintptr_t result = 0; + const char* value = reader->GetValue(); + const char* p = value; + if (value[0] == '0' && value[1] == 'x') { + p = my_read_hex_ptr(&result, value+2); + } else if (entry.format == 'x') { + p = my_read_hex_ptr(&result, value); + } else { + p = my_read_decimal_ptr(&result, value); + } + if (p == value) + continue; + + result &= (1U << entry.bit_length)-1; + result <<= entry.bit_lshift; + sys_info->cpu.arm_cpu_info.cpuid |= + static_cast(result); + } +#if defined(__arm__) + // Get the architecture version from the "Processor" field. + // Note that it is also available in the "CPU architecture" field, + // however, some existing kernels are misconfigured and will report + // invalid values here (e.g. 6, while the CPU is ARMv7-A based). + // The "Processor" field doesn't have this issue. + if (!my_strcmp(field, "Processor")) { + size_t value_len; + const char* value = reader->GetValueAndLen(&value_len); + // Expected format: (v) + // Where is some text like "ARMv7 Processor rev 2" + // and is a decimal corresponding to the ARM + // architecture number. is either 'l' or 'b' + // and corresponds to the endianess, it is ignored here. + while (value_len > 0 && my_isspace(value[value_len-1])) + value_len--; + + size_t nn = value_len; + while (nn > 0 && value[nn-1] != '(') + nn--; + if (nn > 0 && value[nn] == 'v') { + uintptr_t arch_level = 5; + my_read_decimal_ptr(&arch_level, value + nn + 1); + sys_info->processor_level = static_cast(arch_level); + } + } +#elif defined(__aarch64__) + // The aarch64 architecture does not provide the architecture level + // in the Processor field, so we instead check the "CPU architecture" + // field. + if (!my_strcmp(field, "CPU architecture")) { + uintptr_t arch_level = 0; + const char* value = reader->GetValue(); + const char* p = value; + p = my_read_decimal_ptr(&arch_level, value); + if (p == value) + continue; + sys_info->processor_level = static_cast(arch_level); + } +#endif + // Rebuild the ELF hwcaps from the 'Features' field. + if (!my_strcmp(field, "Features")) { + size_t value_len; + const char* value = reader->GetValueAndLen(&value_len); + + // Parse each space-separated tag. + while (value_len > 0) { + const char* tag = value; + size_t tag_len = value_len; + const char* p = my_strchr(tag, ' '); + if (p) { + tag_len = static_cast(p - tag); + value += tag_len + 1; + value_len -= tag_len + 1; + } else { + tag_len = strlen(tag); + value_len = 0; + } + for (const CpuFeaturesEntry& entry : cpu_features_entries) { + if (tag_len == strlen(entry.tag) && + !memcmp(tag, entry.tag, tag_len)) { + sys_info->cpu.arm_cpu_info.elf_hwcaps |= entry.hwcaps; + break; + } + } + } + } + } + sys_close(fd); + } + + return true; + } +#else +# error "Unsupported CPU" +#endif + + bool WriteFile(MDLocationDescriptor* result, const char* filename) { + const int fd = sys_open(filename, O_RDONLY, 0); + if (fd < 0) + return false; + + // We can't stat the files because several of the files that we want to + // read are kernel seqfiles, which always have a length of zero. So we have + // to read as much as we can into a buffer. + static const unsigned kBufSize = 1024 - 2*sizeof(void*); + struct Buffers { + Buffers* next; + size_t len; + uint8_t data[kBufSize]; + }* buffers = reinterpret_cast(Alloc(sizeof(Buffers))); + buffers->next = NULL; + buffers->len = 0; + + size_t total = 0; + for (Buffers* bufptr = buffers;;) { + ssize_t r; + do { + r = sys_read(fd, &bufptr->data[bufptr->len], kBufSize - bufptr->len); + } while (r == -1 && errno == EINTR); + + if (r < 1) + break; + + total += r; + bufptr->len += r; + if (bufptr->len == kBufSize) { + bufptr->next = reinterpret_cast(Alloc(sizeof(Buffers))); + bufptr = bufptr->next; + bufptr->next = NULL; + bufptr->len = 0; + } + } + sys_close(fd); + + if (!total) + return false; + + UntypedMDRVA memory(&minidump_writer_); + if (!memory.Allocate(total)) + return false; + for (MDRVA pos = memory.position(); buffers; buffers = buffers->next) { + // Check for special case of a zero-length buffer. This should only + // occur if a file's size happens to be a multiple of the buffer's + // size, in which case the final sys_read() will have resulted in + // zero bytes being read after the final buffer was just allocated. + if (buffers->len == 0) { + // This can only occur with final buffer. + assert(buffers->next == NULL); + continue; + } + memory.Copy(pos, &buffers->data, buffers->len); + pos += buffers->len; + } + *result = memory.location(); + return true; + } + + bool WriteOSInformation(MDRawSystemInfo* sys_info) { +#if defined(__ANDROID__) + sys_info->platform_id = MD_OS_ANDROID; +#else + sys_info->platform_id = MD_OS_LINUX; +#endif + + struct utsname uts; + if (uname(&uts)) + return false; + + static const size_t buf_len = 512; + char buf[buf_len] = {0}; + size_t space_left = buf_len - 1; + const char* info_table[] = { + uts.sysname, + uts.release, + uts.version, + uts.machine, + NULL + }; + bool first_item = true; + for (const char** cur_info = info_table; *cur_info; cur_info++) { + static const char separator[] = " "; + size_t separator_len = sizeof(separator) - 1; + size_t info_len = my_strlen(*cur_info); + if (info_len == 0) + continue; + + if (space_left < info_len + (first_item ? 0 : separator_len)) + break; + + if (!first_item) { + my_strlcat(buf, separator, sizeof(buf)); + space_left -= separator_len; + } + + first_item = false; + my_strlcat(buf, *cur_info, sizeof(buf)); + space_left -= info_len; + } + + MDLocationDescriptor location; + if (!minidump_writer_.WriteString(buf, 0, &location)) + return false; + sys_info->csd_version_rva = location.rva; + + return true; + } + + bool WriteProcFile(MDLocationDescriptor* result, pid_t pid, + const char* filename) { + char buf[NAME_MAX]; + if (!dumper_->BuildProcPath(buf, pid, filename)) + return false; + return WriteFile(result, buf); + } + + // Only one of the 2 member variables below should be set to a valid value. + const int fd_; // File descriptor where the minidum should be written. + const char* path_; // Path to the file where the minidum should be written. + + const ucontext_t* const ucontext_; // also from the signal handler +#if !defined(__ARM_EABI__) && !defined(__mips__) + const google_breakpad::fpstate_t* const float_state_; // ditto +#endif + LinuxDumper* dumper_; + MinidumpFileWriter minidump_writer_; + off_t minidump_size_limit_; + MDLocationDescriptor crashing_thread_context_; + // Blocks of memory written to the dump. These are all currently + // written while writing the thread list stream, but saved here + // so a memory list stream can be written afterwards. + wasteful_vector memory_blocks_; + // Additional information about some mappings provided by the caller. + const MappingList& mapping_list_; + // Additional memory regions to be included in the dump, + // provided by the caller. + const AppMemoryList& app_memory_list_; + // If set, skip recording any threads that do not reference the + // mapping containing principal_mapping_address_. + bool skip_stacks_if_mapping_unreferenced_; + uintptr_t principal_mapping_address_; + const MappingInfo* principal_mapping_; + // If true, apply stack sanitization to stored stack data. + bool sanitize_stacks_; +}; + + +bool WriteMinidumpImpl(const char* minidump_path, + int minidump_fd, + off_t minidump_size_limit, + pid_t crashing_process, + const void* blob, size_t blob_size, + const MappingList& mappings, + const AppMemoryList& appmem, + bool skip_stacks_if_mapping_unreferenced, + uintptr_t principal_mapping_address, + bool sanitize_stacks) { + LinuxPtraceDumper dumper(crashing_process); + const ExceptionHandler::CrashContext* context = NULL; + if (blob) { + if (blob_size != sizeof(ExceptionHandler::CrashContext)) + return false; + context = reinterpret_cast(blob); + dumper.SetCrashInfoFromSigInfo(context->siginfo); + dumper.set_crash_thread(context->tid); + } + MinidumpWriter writer(minidump_path, minidump_fd, context, mappings, + appmem, skip_stacks_if_mapping_unreferenced, + principal_mapping_address, sanitize_stacks, &dumper); + // Set desired limit for file size of minidump (-1 means no limit). + writer.set_minidump_size_limit(minidump_size_limit); + if (!writer.Init()) + return false; + return writer.Dump(); +} + +} // namespace + +namespace google_breakpad { + +bool WriteMinidump(const char* minidump_path, pid_t crashing_process, + const void* blob, size_t blob_size, + bool skip_stacks_if_mapping_unreferenced, + uintptr_t principal_mapping_address, + bool sanitize_stacks) { + return WriteMinidumpImpl(minidump_path, -1, -1, + crashing_process, blob, blob_size, + MappingList(), AppMemoryList(), + skip_stacks_if_mapping_unreferenced, + principal_mapping_address, + sanitize_stacks); +} + +bool WriteMinidump(int minidump_fd, pid_t crashing_process, + const void* blob, size_t blob_size, + bool skip_stacks_if_mapping_unreferenced, + uintptr_t principal_mapping_address, + bool sanitize_stacks) { + return WriteMinidumpImpl(NULL, minidump_fd, -1, + crashing_process, blob, blob_size, + MappingList(), AppMemoryList(), + skip_stacks_if_mapping_unreferenced, + principal_mapping_address, + sanitize_stacks); +} + +bool WriteMinidump(const char* minidump_path, pid_t process, + pid_t process_blamed_thread) { + LinuxPtraceDumper dumper(process); + // MinidumpWriter will set crash address + dumper.set_crash_signal(MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED); + dumper.set_crash_thread(process_blamed_thread); + MappingList mapping_list; + AppMemoryList app_memory_list; + MinidumpWriter writer(minidump_path, -1, NULL, mapping_list, + app_memory_list, false, 0, false, &dumper); + if (!writer.Init()) + return false; + return writer.Dump(); +} + +bool WriteMinidump(const char* minidump_path, pid_t crashing_process, + const void* blob, size_t blob_size, + const MappingList& mappings, + const AppMemoryList& appmem, + bool skip_stacks_if_mapping_unreferenced, + uintptr_t principal_mapping_address, + bool sanitize_stacks) { + return WriteMinidumpImpl(minidump_path, -1, -1, crashing_process, + blob, blob_size, + mappings, appmem, + skip_stacks_if_mapping_unreferenced, + principal_mapping_address, + sanitize_stacks); +} + +bool WriteMinidump(int minidump_fd, pid_t crashing_process, + const void* blob, size_t blob_size, + const MappingList& mappings, + const AppMemoryList& appmem, + bool skip_stacks_if_mapping_unreferenced, + uintptr_t principal_mapping_address, + bool sanitize_stacks) { + return WriteMinidumpImpl(NULL, minidump_fd, -1, crashing_process, + blob, blob_size, + mappings, appmem, + skip_stacks_if_mapping_unreferenced, + principal_mapping_address, + sanitize_stacks); +} + +bool WriteMinidump(const char* minidump_path, off_t minidump_size_limit, + pid_t crashing_process, + const void* blob, size_t blob_size, + const MappingList& mappings, + const AppMemoryList& appmem, + bool skip_stacks_if_mapping_unreferenced, + uintptr_t principal_mapping_address, + bool sanitize_stacks) { + return WriteMinidumpImpl(minidump_path, -1, minidump_size_limit, + crashing_process, blob, blob_size, + mappings, appmem, + skip_stacks_if_mapping_unreferenced, + principal_mapping_address, + sanitize_stacks); +} + +bool WriteMinidump(int minidump_fd, off_t minidump_size_limit, + pid_t crashing_process, + const void* blob, size_t blob_size, + const MappingList& mappings, + const AppMemoryList& appmem, + bool skip_stacks_if_mapping_unreferenced, + uintptr_t principal_mapping_address, + bool sanitize_stacks) { + return WriteMinidumpImpl(NULL, minidump_fd, minidump_size_limit, + crashing_process, blob, blob_size, + mappings, appmem, + skip_stacks_if_mapping_unreferenced, + principal_mapping_address, + sanitize_stacks); +} + +bool WriteMinidump(const char* filename, + const MappingList& mappings, + const AppMemoryList& appmem, + LinuxDumper* dumper) { + MinidumpWriter writer(filename, -1, NULL, mappings, appmem, + false, 0, false, dumper); + if (!writer.Init()) + return false; + return writer.Dump(); +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/linux/minidump_writer/minidump_writer.h b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/minidump_writer.h new file mode 100644 index 000000000..e3b0b16da --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/minidump_writer.h @@ -0,0 +1,143 @@ +// 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 CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_ +#define CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_ + +#include +#include +#include +#include + +#include +#include +#include + +#include "client/linux/minidump_writer/linux_dumper.h" +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +class ExceptionHandler; + +#if defined(__aarch64__) +typedef struct fpsimd_context fpstate_t; +#elif !defined(__ARM_EABI__) && !defined(__mips__) +typedef std::remove_pointer::type fpstate_t; +#endif + +// These entries store a list of memory regions that the client wants included +// in the minidump. +struct AppMemory { + void* ptr; + size_t length; + + bool operator==(const struct AppMemory& other) const { + return ptr == other.ptr; + } + + bool operator==(const void* other) const { + return ptr == other; + } +}; +typedef std::list AppMemoryList; + +// Writes a minidump to the filesystem. These functions do not malloc nor use +// libc functions which may. Thus, it can be used in contexts where the state +// of the heap may be corrupt. +// minidump_path: the path to the file to write to. This is opened O_EXCL and +// fails open fails. +// crashing_process: the pid of the crashing process. This must be trusted. +// blob: a blob of data from the crashing process. See exception_handler.h +// blob_size: the length of |blob|, in bytes +// +// Returns true iff successful. +bool WriteMinidump(const char* minidump_path, pid_t crashing_process, + const void* blob, size_t blob_size, + bool skip_stacks_if_mapping_unreferenced = false, + uintptr_t principal_mapping_address = 0, + bool sanitize_stacks = false); +// Same as above but takes an open file descriptor instead of a path. +bool WriteMinidump(int minidump_fd, pid_t crashing_process, + const void* blob, size_t blob_size, + bool skip_stacks_if_mapping_unreferenced = false, + uintptr_t principal_mapping_address = 0, + bool sanitize_stacks = false); + +// Alternate form of WriteMinidump() that works with processes that +// are not expected to have crashed. If |process_blamed_thread| is +// meaningful, it will be the one from which a crash signature is +// extracted. It is not expected that this function will be called +// from a compromised context, but it is safe to do so. +bool WriteMinidump(const char* minidump_path, pid_t process, + pid_t process_blamed_thread); + +// These overloads also allow passing a list of known mappings and +// a list of additional memory regions to be included in the minidump. +bool WriteMinidump(const char* minidump_path, pid_t crashing_process, + const void* blob, size_t blob_size, + const MappingList& mappings, + const AppMemoryList& appdata, + bool skip_stacks_if_mapping_unreferenced = false, + uintptr_t principal_mapping_address = 0, + bool sanitize_stacks = false); +bool WriteMinidump(int minidump_fd, pid_t crashing_process, + const void* blob, size_t blob_size, + const MappingList& mappings, + const AppMemoryList& appdata, + bool skip_stacks_if_mapping_unreferenced = false, + uintptr_t principal_mapping_address = 0, + bool sanitize_stacks = false); + +// These overloads also allow passing a file size limit for the minidump. +bool WriteMinidump(const char* minidump_path, off_t minidump_size_limit, + pid_t crashing_process, + const void* blob, size_t blob_size, + const MappingList& mappings, + const AppMemoryList& appdata, + bool skip_stacks_if_mapping_unreferenced = false, + uintptr_t principal_mapping_address = 0, + bool sanitize_stacks = false); +bool WriteMinidump(int minidump_fd, off_t minidump_size_limit, + pid_t crashing_process, + const void* blob, size_t blob_size, + const MappingList& mappings, + const AppMemoryList& appdata, + bool skip_stacks_if_mapping_unreferenced = false, + uintptr_t principal_mapping_address = 0, + bool sanitize_stacks = false); + +bool WriteMinidump(const char* filename, + const MappingList& mappings, + const AppMemoryList& appdata, + LinuxDumper* dumper); + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_H_ diff --git a/shared/sentry/external/breakpad/src/client/linux/minidump_writer/minidump_writer_unittest.cc b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/minidump_writer_unittest.cc new file mode 100644 index 000000000..b7a2c61e1 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/minidump_writer_unittest.cc @@ -0,0 +1,936 @@ +// 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. + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "breakpad_googletest_includes.h" +#include "client/linux/handler/exception_handler.h" +#include "client/linux/minidump_writer/linux_dumper.h" +#include "client/linux/minidump_writer/minidump_writer.h" +#include "client/linux/minidump_writer/minidump_writer_unittest_utils.h" +#include "common/linux/breakpad_getcontext.h" +#include "common/linux/eintr_wrapper.h" +#include "common/linux/file_id.h" +#include "common/linux/ignore_ret.h" +#include "common/linux/safe_readlink.h" +#include "common/scoped_ptr.h" +#include "common/tests/auto_tempdir.h" +#include "common/tests/file_utils.h" +#include "common/using_std_string.h" +#include "google_breakpad/processor/minidump.h" + +using namespace google_breakpad; +using google_breakpad::elf::FileID; +using google_breakpad::elf::kDefaultBuildIdSize; + +namespace { + +typedef testing::Test MinidumpWriterTest; + +const char kMDWriterUnitTestFileName[] = "/minidump-writer-unittest"; + +TEST(MinidumpWriterTest, SetupWithPath) { + int fds[2]; + ASSERT_NE(-1, pipe(fds)); + + const pid_t child = fork(); + if (child == 0) { + close(fds[1]); + char b; + IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b)))); + close(fds[0]); + syscall(__NR_exit_group); + } + close(fds[0]); + + ExceptionHandler::CrashContext context; + memset(&context, 0, sizeof(context)); + + AutoTempDir temp_dir; + string templ = temp_dir.path() + kMDWriterUnitTestFileName; + // Set a non-zero tid to avoid tripping asserts. + context.tid = child; + ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context))); + struct stat st; + ASSERT_EQ(0, stat(templ.c_str(), &st)); + ASSERT_GT(st.st_size, 0); + + close(fds[1]); + IGNORE_EINTR(waitpid(child, nullptr, 0)); +} + +TEST(MinidumpWriterTest, SetupWithFD) { + int fds[2]; + ASSERT_NE(-1, pipe(fds)); + + const pid_t child = fork(); + if (child == 0) { + close(fds[1]); + char b; + HANDLE_EINTR(read(fds[0], &b, sizeof(b))); + close(fds[0]); + syscall(__NR_exit_group); + } + close(fds[0]); + + ExceptionHandler::CrashContext context; + memset(&context, 0, sizeof(context)); + + AutoTempDir temp_dir; + string templ = temp_dir.path() + kMDWriterUnitTestFileName; + int fd = open(templ.c_str(), O_CREAT | O_WRONLY, S_IRWXU); + // Set a non-zero tid to avoid tripping asserts. + context.tid = child; + ASSERT_TRUE(WriteMinidump(fd, child, &context, sizeof(context))); + struct stat st; + ASSERT_EQ(0, stat(templ.c_str(), &st)); + ASSERT_GT(st.st_size, 0); + + close(fds[1]); + IGNORE_EINTR(waitpid(child, nullptr, 0)); +} + +// Test that mapping info can be specified when writing a minidump, +// and that it ends up in the module list of the minidump. +TEST(MinidumpWriterTest, MappingInfo) { + int fds[2]; + ASSERT_NE(-1, pipe(fds)); + + // These are defined here so the parent can use them to check the + // data from the minidump afterwards. + const uint32_t memory_size = sysconf(_SC_PAGESIZE); + const char* kMemoryName = "a fake module"; + const uint8_t kModuleGUID[sizeof(MDGUID)] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF + }; + const string module_identifier = "33221100554477668899AABBCCDDEEFF0"; + + // Get some memory. + char* memory = + reinterpret_cast(mmap(NULL, + memory_size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANON, + -1, + 0)); + const uintptr_t kMemoryAddress = reinterpret_cast(memory); + ASSERT_TRUE(memory); + + const pid_t child = fork(); + if (child == 0) { + close(fds[1]); + char b; + IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b)))); + close(fds[0]); + syscall(__NR_exit_group); + } + close(fds[0]); + + ExceptionHandler::CrashContext context; + memset(&context, 0, sizeof(context)); + ASSERT_EQ(0, getcontext(&context.context)); + context.tid = child; + + AutoTempDir temp_dir; + string templ = temp_dir.path() + kMDWriterUnitTestFileName; + + // Add information about the mapped memory. + MappingInfo info; + info.start_addr = kMemoryAddress; + info.size = memory_size; + info.offset = 0; + info.exec = false; + strcpy(info.name, kMemoryName); + + MappingList mappings; + AppMemoryList memory_list; + MappingEntry mapping; + mapping.first = info; + memcpy(mapping.second, kModuleGUID, sizeof(MDGUID)); + mappings.push_back(mapping); + ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context), + mappings, memory_list, false, 0, false)); + + // Read the minidump. Load the module list, and ensure that + // the mmap'ed |memory| is listed with the given module name + // and debug ID. + Minidump minidump(templ); + ASSERT_TRUE(minidump.Read()); + + MinidumpModuleList* module_list = minidump.GetModuleList(); + ASSERT_TRUE(module_list); + const MinidumpModule* module = + module_list->GetModuleForAddress(kMemoryAddress); + ASSERT_TRUE(module); + + EXPECT_EQ(kMemoryAddress, module->base_address()); + EXPECT_EQ(memory_size, module->size()); + EXPECT_EQ(kMemoryName, module->code_file()); + EXPECT_EQ(module_identifier, module->debug_identifier()); + + uint32_t len; + // These streams are expected to be there + EXPECT_TRUE(minidump.SeekToStreamType(MD_THREAD_LIST_STREAM, &len)); + EXPECT_TRUE(minidump.SeekToStreamType(MD_MEMORY_LIST_STREAM, &len)); + EXPECT_TRUE(minidump.SeekToStreamType(MD_EXCEPTION_STREAM, &len)); + EXPECT_TRUE(minidump.SeekToStreamType(MD_SYSTEM_INFO_STREAM, &len)); + EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_CPU_INFO, &len)); + EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_PROC_STATUS, &len)); + EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_CMD_LINE, &len)); + EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_ENVIRON, &len)); + EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_AUXV, &len)); + EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_MAPS, &len)); + EXPECT_TRUE(minidump.SeekToStreamType(MD_LINUX_DSO_DEBUG, &len)); + + close(fds[1]); + IGNORE_EINTR(waitpid(child, nullptr, 0)); +} + +// Test that minidumping is skipped while writing minidumps if principal mapping +// is not referenced. +TEST(MinidumpWriterTest, MinidumpSkippedIfRequested) { + int fds[2]; + ASSERT_NE(-1, pipe(fds)); + + const pid_t child = fork(); + if (child == 0) { + close(fds[1]); + char b; + IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b)))); + close(fds[0]); + syscall(__NR_exit_group); + } + close(fds[0]); + + ExceptionHandler::CrashContext context; + memset(&context, 0, sizeof(context)); + ASSERT_EQ(0, getcontext(&context.context)); + context.tid = child; + + AutoTempDir temp_dir; + string templ = temp_dir.path() + kMDWriterUnitTestFileName; + + // pass an invalid principal mapping address, which will force + // WriteMinidump to not write a minidump. + ASSERT_FALSE(WriteMinidump(templ.c_str(), child, &context, sizeof(context), + true, static_cast(0x0102030405060708ull), + false)); + close(fds[1]); + IGNORE_EINTR(waitpid(child, nullptr, 0)); +} + +// Test that minidumping is skipped while writing minidumps if principal mapping +// is not referenced. +TEST(MinidumpWriterTest, MinidumpStacksSkippedIfRequested) { + int fds[2]; + ASSERT_NE(-1, pipe(fds)); + + const pid_t child = fork(); + if (child == 0) { + close(fds[1]); + + // Create a thread that does not return, and only references libc (not the + // current executable). This thread should not be captured in the minidump. + pthread_t thread; + pthread_attr_t thread_attributes; + pthread_attr_init(&thread_attributes); + pthread_attr_setdetachstate(&thread_attributes, PTHREAD_CREATE_DETACHED); + sigset_t sigset; + sigemptyset(&sigset); + pthread_create(&thread, &thread_attributes, + reinterpret_cast(&sigsuspend), &sigset); + + char b; + IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b)))); + close(fds[0]); + syscall(__NR_exit_group); + } + close(fds[0]); + + ExceptionHandler::CrashContext context; + memset(&context, 0, sizeof(context)); + ASSERT_EQ(0, getcontext(&context.context)); + context.tid = child; + + AutoTempDir temp_dir; + string templ = temp_dir.path() + kMDWriterUnitTestFileName; + + // Pass an invalid principal mapping address, which will force + // WriteMinidump to not dump any thread stacks. + ASSERT_TRUE(WriteMinidump( + templ.c_str(), child, &context, sizeof(context), true, + reinterpret_cast(google_breakpad::WriteFile), false)); + + // Read the minidump. And ensure that thread memory was dumped only for the + // main thread. + Minidump minidump(templ); + ASSERT_TRUE(minidump.Read()); + + MinidumpThreadList* threads = minidump.GetThreadList(); + int threads_with_stacks = 0; + for (unsigned int i = 0; i < threads->thread_count(); ++i) { + MinidumpThread* thread = threads->GetThreadAtIndex(i); + if (thread->GetMemory()) { + ++threads_with_stacks; + } + } +#if defined(THREAD_SANITIZER) || defined(ADDRESS_SANITIZER) + ASSERT_GE(threads_with_stacks, 1); +#else + ASSERT_EQ(threads_with_stacks, 1); +#endif + close(fds[1]); + IGNORE_EINTR(waitpid(child, nullptr, 0)); +} + +// Test that stacks can be sanitized while writing minidumps. +TEST(MinidumpWriterTest, StacksAreSanitizedIfRequested) { + int fds[2]; + ASSERT_NE(-1, pipe(fds)); + + const pid_t child = fork(); + if (child == 0) { + close(fds[1]); + char b; + IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b)))); + close(fds[0]); + syscall(__NR_exit_group); + } + close(fds[0]); + + ExceptionHandler::CrashContext context; + memset(&context, 0, sizeof(context)); + ASSERT_EQ(0, getcontext(&context.context)); + context.tid = child; + + AutoTempDir temp_dir; + string templ = temp_dir.path() + kMDWriterUnitTestFileName; + // pass an invalid principal mapping address, which will force + // WriteMinidump to not dump any thread stacks. + ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context), + false, 0, true)); + + // Read the minidump. And ensure that thread memory contains a defaced value. + Minidump minidump(templ); + ASSERT_TRUE(minidump.Read()); + + const uintptr_t defaced = +#if defined(__LP64__) + 0x0defaced0defaced; +#else + 0x0defaced; +#endif + MinidumpThreadList* threads = minidump.GetThreadList(); + for (unsigned int i = 0; i < threads->thread_count(); ++i) { + MinidumpThread* thread = threads->GetThreadAtIndex(i); + MinidumpMemoryRegion* mem = thread->GetMemory(); + ASSERT_TRUE(mem != nullptr); + uint32_t sz = mem->GetSize(); + const uint8_t* data = mem->GetMemory(); + ASSERT_TRUE(memmem(data, sz, &defaced, sizeof(defaced)) != nullptr); + } + close(fds[1]); + IGNORE_EINTR(waitpid(child, nullptr, 0)); +} + +// Test that a binary with a longer-than-usual build id note +// makes its way all the way through to the minidump unscathed. +// The linux_client_unittest is linked with an explicit --build-id +// in Makefile.am. +TEST(MinidumpWriterTest, BuildIDLong) { + int fds[2]; + ASSERT_NE(-1, pipe(fds)); + + const pid_t child = fork(); + if (child == 0) { + close(fds[1]); + char b; + IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b)))); + close(fds[0]); + syscall(__NR_exit_group); + } + close(fds[0]); + + ExceptionHandler::CrashContext context; + memset(&context, 0, sizeof(context)); + ASSERT_EQ(0, getcontext(&context.context)); + context.tid = child; + + AutoTempDir temp_dir; + const string dump_path = temp_dir.path() + kMDWriterUnitTestFileName; + + EXPECT_TRUE(WriteMinidump(dump_path.c_str(), + child, &context, sizeof(context))); + close(fds[1]); + + // Read the minidump. Load the module list, and ensure that + // the main module has the correct debug id and code id. + Minidump minidump(dump_path); + ASSERT_TRUE(minidump.Read()); + + MinidumpModuleList* module_list = minidump.GetModuleList(); + ASSERT_TRUE(module_list); + const MinidumpModule* module = module_list->GetMainModule(); + ASSERT_TRUE(module); + const string module_identifier = "030201000504070608090A0B0C0D0E0F0"; + // This is passed explicitly to the linker in Makefile.am + const string build_id = + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"; + EXPECT_EQ(module_identifier, module->debug_identifier()); + EXPECT_EQ(build_id, module->code_identifier()); + + IGNORE_EINTR(waitpid(child, nullptr, 0)); +} + +// Test that mapping info can be specified, and that it overrides +// existing mappings that are wholly contained within the specified +// range. +TEST(MinidumpWriterTest, MappingInfoContained) { + int fds[2]; + ASSERT_NE(-1, pipe(fds)); + + // These are defined here so the parent can use them to check the + // data from the minidump afterwards. + const int32_t memory_size = sysconf(_SC_PAGESIZE); + const char* kMemoryName = "a fake module"; + const uint8_t kModuleGUID[sizeof(MDGUID)] = { + 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, + 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF + }; + const string module_identifier = "33221100554477668899AABBCCDDEEFF0"; + + // mmap a file + AutoTempDir temp_dir; + string tempfile = temp_dir.path() + "/minidump-writer-unittest-temp"; + int fd = open(tempfile.c_str(), O_RDWR | O_CREAT, 0); + ASSERT_NE(-1, fd); + unlink(tempfile.c_str()); + // fill with zeros + google_breakpad::scoped_array buffer(new char[memory_size]); + memset(buffer.get(), 0, memory_size); + ASSERT_EQ(memory_size, write(fd, buffer.get(), memory_size)); + lseek(fd, 0, SEEK_SET); + + char* memory = + reinterpret_cast(mmap(NULL, + memory_size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE, + fd, + 0)); + const uintptr_t kMemoryAddress = reinterpret_cast(memory); + ASSERT_TRUE(memory); + close(fd); + + const pid_t child = fork(); + if (child == 0) { + close(fds[1]); + char b; + IGNORE_RET(HANDLE_EINTR(read(fds[0], &b, sizeof(b)))); + close(fds[0]); + syscall(__NR_exit_group); + } + close(fds[0]); + + ExceptionHandler::CrashContext context; + memset(&context, 0, sizeof(context)); + context.tid = 1; + + string dumpfile = temp_dir.path() + kMDWriterUnitTestFileName; + + // Add information about the mapped memory. Report it as being larger than + // it actually is. + MappingInfo info; + info.start_addr = kMemoryAddress - memory_size; + info.size = memory_size * 3; + info.offset = 0; + info.exec = false; + strcpy(info.name, kMemoryName); + + MappingList mappings; + AppMemoryList memory_list; + MappingEntry mapping; + mapping.first = info; + memcpy(mapping.second, kModuleGUID, sizeof(MDGUID)); + mappings.push_back(mapping); + ASSERT_TRUE(WriteMinidump(dumpfile.c_str(), child, &context, sizeof(context), + mappings, memory_list)); + + // Read the minidump. Load the module list, and ensure that + // the mmap'ed |memory| is listed with the given module name + // and debug ID. + Minidump minidump(dumpfile); + ASSERT_TRUE(minidump.Read()); + + MinidumpModuleList* module_list = minidump.GetModuleList(); + ASSERT_TRUE(module_list); + const MinidumpModule* module = + module_list->GetModuleForAddress(kMemoryAddress); + ASSERT_TRUE(module); + + EXPECT_EQ(info.start_addr, module->base_address()); + EXPECT_EQ(info.size, module->size()); + EXPECT_EQ(kMemoryName, module->code_file()); + EXPECT_EQ(module_identifier, module->debug_identifier()); + + close(fds[1]); + IGNORE_EINTR(waitpid(child, nullptr, 0)); +} + +TEST(MinidumpWriterTest, DeletedBinary) { + const string kNumberOfThreadsArgument = "1"; + const string helper_path(GetHelperBinary()); + if (helper_path.empty()) { + FAIL() << "Couldn't find helper binary"; + exit(1); + } + + // Copy binary to a temp file. + AutoTempDir temp_dir; + string binpath = temp_dir.path() + "/linux-dumper-unittest-helper"; + ASSERT_TRUE(CopyFile(helper_path, binpath)) + << "Failed to copy " << helper_path << " to " << binpath; + ASSERT_EQ(0, chmod(binpath.c_str(), 0755)); + + int fds[2]; + ASSERT_NE(-1, pipe(fds)); + + pid_t child_pid = fork(); + if (child_pid == 0) { + // In child process. + close(fds[0]); + + // Pass the pipe fd and the number of threads as arguments. + char pipe_fd_string[8]; + sprintf(pipe_fd_string, "%d", fds[1]); + execl(binpath.c_str(), + binpath.c_str(), + pipe_fd_string, + kNumberOfThreadsArgument.c_str(), + NULL); + } + close(fds[1]); + // Wait for the child process to signal that it's ready. + struct pollfd pfd; + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fds[0]; + pfd.events = POLLIN | POLLERR; + + const int r = HANDLE_EINTR(poll(&pfd, 1, 1000)); + ASSERT_EQ(1, r); + ASSERT_TRUE(pfd.revents & POLLIN); + uint8_t junk; + const int nr = HANDLE_EINTR(read(fds[0], &junk, sizeof(junk))); + ASSERT_EQ(static_cast(sizeof(junk)), nr); + close(fds[0]); + + // Child is ready now. + // Unlink the test binary. + unlink(binpath.c_str()); + + ExceptionHandler::CrashContext context; + memset(&context, 0, sizeof(context)); + + string templ = temp_dir.path() + kMDWriterUnitTestFileName; + // Set a non-zero tid to avoid tripping asserts. + context.tid = child_pid; + ASSERT_TRUE(WriteMinidump(templ.c_str(), child_pid, &context, + sizeof(context))); + kill(child_pid, SIGKILL); + + struct stat st; + ASSERT_EQ(0, stat(templ.c_str(), &st)); + ASSERT_GT(st.st_size, 0); + + Minidump minidump(templ); + ASSERT_TRUE(minidump.Read()); + + // Check that the main module filename is correct. + MinidumpModuleList* module_list = minidump.GetModuleList(); + ASSERT_TRUE(module_list); + const MinidumpModule* module = module_list->GetMainModule(); + EXPECT_STREQ(binpath.c_str(), module->code_file().c_str()); + // Check that the file ID is correct. + FileID fileid(helper_path.c_str()); + PageAllocator allocator; + wasteful_vector identifier(&allocator, kDefaultBuildIdSize); + EXPECT_TRUE(fileid.ElfFileIdentifier(identifier)); + string identifier_string = FileID::ConvertIdentifierToUUIDString(identifier); + string module_identifier(identifier_string); + // Strip out dashes + size_t pos; + while ((pos = module_identifier.find('-')) != string::npos) { + module_identifier.erase(pos, 1); + } + // And append a zero, because module IDs include an "age" field + // which is always zero on Linux. + module_identifier += "0"; + EXPECT_EQ(module_identifier, module->debug_identifier()); + + IGNORE_EINTR(waitpid(child_pid, nullptr, 0)); +} + +// Test that an additional memory region can be added to the minidump. +TEST(MinidumpWriterTest, AdditionalMemory) { + int fds[2]; + ASSERT_NE(-1, pipe(fds)); + + // These are defined here so the parent can use them to check the + // data from the minidump afterwards. + const uint32_t kMemorySize = sysconf(_SC_PAGESIZE); + + // Get some heap memory. + uint8_t* memory = new uint8_t[kMemorySize]; + const uintptr_t kMemoryAddress = reinterpret_cast(memory); + ASSERT_TRUE(memory); + + // Stick some data into the memory so the contents can be verified. + for (uint32_t i = 0; i < kMemorySize; ++i) { + memory[i] = i % 255; + } + + const pid_t child = fork(); + if (child == 0) { + close(fds[1]); + char b; + HANDLE_EINTR(read(fds[0], &b, sizeof(b))); + close(fds[0]); + syscall(__NR_exit_group); + } + close(fds[0]); + + ExceptionHandler::CrashContext context; + + // This needs a valid context for minidump writing to work, but getting + // a useful one from the child is too much work, so just use one from + // the parent since the child is just a forked copy anyway. + ASSERT_EQ(0, getcontext(&context.context)); + context.tid = child; + + AutoTempDir temp_dir; + string templ = temp_dir.path() + kMDWriterUnitTestFileName; + unlink(templ.c_str()); + + MappingList mappings; + AppMemoryList memory_list; + + // Add the memory region to the list of memory to be included. + AppMemory app_memory; + app_memory.ptr = memory; + app_memory.length = kMemorySize; + memory_list.push_back(app_memory); + ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context), + mappings, memory_list)); + + // Read the minidump. Ensure that the memory region is present + Minidump minidump(templ); + ASSERT_TRUE(minidump.Read()); + + MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList(); + ASSERT_TRUE(dump_memory_list); + const MinidumpMemoryRegion* region = + dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress); + ASSERT_TRUE(region); + + EXPECT_EQ(kMemoryAddress, region->GetBase()); + EXPECT_EQ(kMemorySize, region->GetSize()); + + // Verify memory contents. + EXPECT_EQ(0, memcmp(region->GetMemory(), memory, kMemorySize)); + + delete[] memory; + close(fds[1]); + IGNORE_EINTR(waitpid(child, nullptr, 0)); +} + +// Test that an invalid thread stack pointer still results in a minidump. +TEST(MinidumpWriterTest, InvalidStackPointer) { + int fds[2]; + ASSERT_NE(-1, pipe(fds)); + + const pid_t child = fork(); + if (child == 0) { + close(fds[1]); + char b; + HANDLE_EINTR(read(fds[0], &b, sizeof(b))); + close(fds[0]); + syscall(__NR_exit_group); + } + close(fds[0]); + + ExceptionHandler::CrashContext context; + + // This needs a valid context for minidump writing to work, but getting + // a useful one from the child is too much work, so just use one from + // the parent since the child is just a forked copy anyway. + ASSERT_EQ(0, getcontext(&context.context)); + context.tid = child; + + // Fake the child's stack pointer for its crashing thread. NOTE: This must + // be an invalid memory address for the child process (stack or otherwise). + // Try 1MB below the current stack. + uintptr_t invalid_stack_pointer = + reinterpret_cast(&context) - 1024*1024; +#if defined(__i386) + context.context.uc_mcontext.gregs[REG_ESP] = invalid_stack_pointer; +#elif defined(__x86_64) + context.context.uc_mcontext.gregs[REG_RSP] = invalid_stack_pointer; +#elif defined(__ARM_EABI__) + context.context.uc_mcontext.arm_sp = invalid_stack_pointer; +#elif defined(__aarch64__) + context.context.uc_mcontext.sp = invalid_stack_pointer; +#elif defined(__mips__) + context.context.uc_mcontext.gregs[MD_CONTEXT_MIPS_REG_SP] = + invalid_stack_pointer; +#else +# error "This code has not been ported to your platform yet." +#endif + + AutoTempDir temp_dir; + string templ = temp_dir.path() + kMDWriterUnitTestFileName; + // NOTE: In previous versions of Breakpad, WriteMinidump() would fail if + // presented with an invalid stack pointer. + ASSERT_TRUE(WriteMinidump(templ.c_str(), child, &context, sizeof(context))); + + // Read the minidump. Ensure that the memory region is present + Minidump minidump(templ); + ASSERT_TRUE(minidump.Read()); + + // TODO(ted.mielczarek,mkrebs): Enable this part of the test once + // https://breakpad.appspot.com/413002/ is committed. +#if 0 + // Make sure there's a thread without a stack. NOTE: It's okay if + // GetThreadList() shows the error: "ERROR: MinidumpThread has a memory + // region problem". + MinidumpThreadList* dump_thread_list = minidump.GetThreadList(); + ASSERT_TRUE(dump_thread_list); + bool found_empty_stack = false; + for (int i = 0; i < dump_thread_list->thread_count(); i++) { + MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i); + ASSERT_TRUE(thread->thread() != NULL); + // When the stack size is zero bytes, GetMemory() returns NULL. + if (thread->GetMemory() == NULL) { + found_empty_stack = true; + break; + } + } + // NOTE: If you fail this, first make sure that "invalid_stack_pointer" + // above is indeed set to an invalid address. + ASSERT_TRUE(found_empty_stack); +#endif + + close(fds[1]); + IGNORE_EINTR(waitpid(child, nullptr, 0)); +} + +// Test that limiting the size of the minidump works. +TEST(MinidumpWriterTest, MinidumpSizeLimit) { + static const int kNumberOfThreadsInHelperProgram = 40; + + char number_of_threads_arg[3]; + sprintf(number_of_threads_arg, "%d", kNumberOfThreadsInHelperProgram); + + string helper_path(GetHelperBinary()); + if (helper_path.empty()) { + FAIL() << "Couldn't find helper binary"; + exit(1); + } + + int fds[2]; + ASSERT_NE(-1, pipe(fds)); + + pid_t child_pid = fork(); + if (child_pid == 0) { + // In child process. + close(fds[0]); + + // Pass the pipe fd and the number of threads as arguments. + char pipe_fd_string[8]; + sprintf(pipe_fd_string, "%d", fds[1]); + execl(helper_path.c_str(), + helper_path.c_str(), + pipe_fd_string, + number_of_threads_arg, + NULL); + } + close(fds[1]); + + // Wait for all child threads to indicate that they have started + for (int threads = 0; threads < kNumberOfThreadsInHelperProgram; threads++) { + struct pollfd pfd; + memset(&pfd, 0, sizeof(pfd)); + pfd.fd = fds[0]; + pfd.events = POLLIN | POLLERR; + + const int r = HANDLE_EINTR(poll(&pfd, 1, 1000)); + ASSERT_EQ(1, r); + ASSERT_TRUE(pfd.revents & POLLIN); + uint8_t junk; + ASSERT_EQ(read(fds[0], &junk, sizeof(junk)), + static_cast(sizeof(junk))); + } + close(fds[0]); + + // There is a race here because we may stop a child thread before + // it is actually running the busy loop. Empirically this sleep + // is sufficient to avoid the race. + usleep(100000); + + // Child and its threads are ready now. + + + off_t normal_file_size; + int total_normal_stack_size = 0; + AutoTempDir temp_dir; + + // First, write a minidump with no size limit. + { + string normal_dump = temp_dir.path() + + "/minidump-writer-unittest.dmp"; + ASSERT_TRUE(WriteMinidump(normal_dump.c_str(), -1, + child_pid, NULL, 0, + MappingList(), AppMemoryList())); + struct stat st; + ASSERT_EQ(0, stat(normal_dump.c_str(), &st)); + ASSERT_GT(st.st_size, 0); + normal_file_size = st.st_size; + + Minidump minidump(normal_dump); + ASSERT_TRUE(minidump.Read()); + MinidumpThreadList* dump_thread_list = minidump.GetThreadList(); + ASSERT_TRUE(dump_thread_list); + for (unsigned int i = 0; i < dump_thread_list->thread_count(); i++) { + MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i); + ASSERT_TRUE(thread->thread() != NULL); + // When the stack size is zero bytes, GetMemory() returns NULL. + MinidumpMemoryRegion* memory = thread->GetMemory(); + ASSERT_TRUE(memory != NULL); + total_normal_stack_size += memory->GetSize(); + } + } + + // Second, write a minidump with a size limit big enough to not trigger + // anything. + { + // Set size limit arbitrarily 1MB larger than the normal file size -- such + // that the limiting code will not kick in. + const off_t minidump_size_limit = normal_file_size + 1024*1024; + + string same_dump = temp_dir.path() + + "/minidump-writer-unittest-same.dmp"; + ASSERT_TRUE(WriteMinidump(same_dump.c_str(), minidump_size_limit, + child_pid, NULL, 0, + MappingList(), AppMemoryList())); + struct stat st; + ASSERT_EQ(0, stat(same_dump.c_str(), &st)); + // Make sure limiting wasn't actually triggered. NOTE: If you fail this, + // first make sure that "minidump_size_limit" above is indeed set to a + // large enough value -- the limit-checking code in minidump_writer.cc + // does just a rough estimate. + ASSERT_EQ(normal_file_size, st.st_size); + } + + // Third, write a minidump with a size limit small enough to be triggered. + { + // Set size limit to some arbitrary amount, such that the limiting code + // will kick in. The equation used to set this value was determined by + // simply reversing the size-limit logic a little bit in order to pick a + // size we know will trigger it. The definition of + // kLimitAverageThreadStackLength here was copied from class + // MinidumpWriter in minidump_writer.cc. + static const unsigned kLimitAverageThreadStackLength = 8 * 1024; + off_t minidump_size_limit = kNumberOfThreadsInHelperProgram * + kLimitAverageThreadStackLength; + // If, in reality, each of the threads' stack is *smaller* than + // kLimitAverageThreadStackLength, the normal file size could very well be + // smaller than the arbitrary limit that was just set. In that case, + // either of these numbers should trigger the size-limiting code, but we + // might as well pick the smallest. + if (normal_file_size < minidump_size_limit) + minidump_size_limit = normal_file_size; + + string limit_dump = temp_dir.path() + + "/minidump-writer-unittest-limit.dmp"; + ASSERT_TRUE(WriteMinidump(limit_dump.c_str(), minidump_size_limit, + child_pid, NULL, 0, + MappingList(), AppMemoryList())); + struct stat st; + ASSERT_EQ(0, stat(limit_dump.c_str(), &st)); + ASSERT_GT(st.st_size, 0); + // Make sure the file size is at least smaller than the original. If this + // fails because it's the same size, then the size-limit logic didn't kick + // in like it was supposed to. + EXPECT_LT(st.st_size, normal_file_size); + + Minidump minidump(limit_dump); + ASSERT_TRUE(minidump.Read()); + MinidumpThreadList* dump_thread_list = minidump.GetThreadList(); + ASSERT_TRUE(dump_thread_list); + int total_limit_stack_size = 0; + for (unsigned int i = 0; i < dump_thread_list->thread_count(); i++) { + MinidumpThread* thread = dump_thread_list->GetThreadAtIndex(i); + ASSERT_TRUE(thread->thread() != NULL); + // When the stack size is zero bytes, GetMemory() returns NULL. + MinidumpMemoryRegion* memory = thread->GetMemory(); + ASSERT_TRUE(memory != NULL); + total_limit_stack_size += memory->GetSize(); + } + + // Make sure stack size shrunk by at least 1KB per extra thread. The + // definition of kLimitBaseThreadCount here was copied from class + // MinidumpWriter in minidump_writer.cc. + // Note: The 1KB is arbitrary, and assumes that the thread stacks are big + // enough to shrink by that much. For example, if each thread stack was + // originally only 2KB, the current size-limit logic wouldn't actually + // shrink them because that's the size to which it tries to shrink. If + // you fail this part of the test due to something like that, the test + // logic should probably be improved to account for your situation. + const unsigned kLimitBaseThreadCount = 20; + const unsigned kMinPerExtraThreadStackReduction = 1024; + const int min_expected_reduction = (kNumberOfThreadsInHelperProgram - + kLimitBaseThreadCount) * kMinPerExtraThreadStackReduction; + EXPECT_LT(total_limit_stack_size, + total_normal_stack_size - min_expected_reduction); + } + + // Kill the helper program. + kill(child_pid, SIGKILL); + IGNORE_EINTR(waitpid(child_pid, nullptr, 0)); +} + +} // namespace diff --git a/shared/sentry/external/breakpad/src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc new file mode 100644 index 000000000..8e2319e76 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/minidump_writer_unittest_utils.cc @@ -0,0 +1,66 @@ +// 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. + +// minidump_writer_unittest_utils.cc: +// Shared routines used by unittests under client/linux/minidump_writer. + +#include +#include + +#include "client/linux/minidump_writer/minidump_writer_unittest_utils.h" +#include "common/linux/safe_readlink.h" +#include "common/using_std_string.h" + +namespace google_breakpad { + +string GetHelperBinary() { + string helper_path; + char* bindir = getenv("bindir"); + if (bindir) { + helper_path = string(bindir) + "/"; + } else { + // Locate helper binary next to the current binary. + char self_path[PATH_MAX]; + if (!SafeReadLink("/proc/self/exe", self_path)) { + return ""; + } + helper_path = string(self_path); + size_t pos = helper_path.rfind('/'); + if (pos == string::npos) { + return ""; + } + helper_path.erase(pos + 1); + } + + helper_path += "linux_dumper_unittest_helper"; + + return helper_path; +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/linux/minidump_writer/minidump_writer_unittest_utils.h b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/minidump_writer_unittest_utils.h new file mode 100644 index 000000000..f16cc086b --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/minidump_writer_unittest_utils.h @@ -0,0 +1,49 @@ +// 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. + +// minidump_writer_unittest_utils.h: +// Shared routines used by unittests under client/linux/minidump_writer. + +#ifndef CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_UNITTEST_UTILS_H_ +#define CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_UNITTEST_UTILS_H_ + +#include + +#include "common/using_std_string.h" + +namespace google_breakpad { + +// Returns the full path to linux_dumper_unittest_helper. The full path is +// discovered either by using the environment variable "bindir" or by using +// the location of the main module of the currently running process. +string GetHelperBinary(); + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_MINIDUMP_WRITER_MINIDUMP_WRITER_UNITTEST_UTILS_H_ diff --git a/shared/sentry/external/breakpad/src/client/linux/minidump_writer/proc_cpuinfo_reader.h b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/proc_cpuinfo_reader.h new file mode 100644 index 000000000..d9461bf30 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/proc_cpuinfo_reader.h @@ -0,0 +1,130 @@ +// 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 CLIENT_LINUX_MINIDUMP_WRITER_PROC_CPUINFO_READER_H_ +#define CLIENT_LINUX_MINIDUMP_WRITER_PROC_CPUINFO_READER_H_ + +#include +#include +#include + +#include "client/linux/minidump_writer/line_reader.h" +#include "common/linux/linux_libc_support.h" +#include "third_party/lss/linux_syscall_support.h" + +namespace google_breakpad { + +// A class for reading /proc/cpuinfo without using fopen/fgets or other +// functions which may allocate memory. +class ProcCpuInfoReader { +public: + ProcCpuInfoReader(int fd) + : line_reader_(fd), pop_count_(-1) { + } + + // Return the next field name, or NULL in case of EOF. + // field: (output) Pointer to zero-terminated field name. + // Returns true on success, or false on EOF or error (line too long). + bool GetNextField(const char** field) { + for (;;) { + const char* line; + unsigned line_len; + + // Try to read next line. + if (pop_count_ >= 0) { + line_reader_.PopLine(pop_count_); + pop_count_ = -1; + } + + if (!line_reader_.GetNextLine(&line, &line_len)) + return false; + + pop_count_ = static_cast(line_len); + + const char* line_end = line + line_len; + + // Expected format: + ':' + // Note that: + // - empty lines happen. + // - can contain spaces. + // - some fields have an empty + char* sep = static_cast(my_memchr(line, ':', line_len)); + if (sep == NULL) + continue; + + // Record the value. Skip leading space after the column to get + // its start. + const char* val = sep+1; + while (val < line_end && my_isspace(*val)) + val++; + + value_ = val; + value_len_ = static_cast(line_end - val); + + // Remove trailing spaces before the column to properly 0-terminate + // the field name. + while (sep > line && my_isspace(sep[-1])) + sep--; + + if (sep == line) + continue; + + // zero-terminate field name. + *sep = '\0'; + + *field = line; + return true; + } + } + + // Return the field value. This must be called after a succesful + // call to GetNextField(). + const char* GetValue() { + assert(value_); + return value_; + } + + // Same as GetValue(), but also returns the length in characters of + // the value. + const char* GetValueAndLen(size_t* length) { + assert(value_); + *length = value_len_; + return value_; + } + +private: + LineReader line_reader_; + int pop_count_; + const char* value_; + size_t value_len_; +}; + +} // namespace google_breakpad + +#endif // CLIENT_LINUX_MINIDUMP_WRITER_PROC_CPUINFO_READER_H_ diff --git a/shared/sentry/external/breakpad/src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc new file mode 100644 index 000000000..d9b1203e4 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/minidump_writer/proc_cpuinfo_reader_unittest.cc @@ -0,0 +1,199 @@ +// 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. + +#include +#include +#include +#include +#include +#include + +#include "client/linux/minidump_writer/proc_cpuinfo_reader.h" +#include "breakpad_googletest_includes.h" +#include "common/linux/tests/auto_testfile.h" + +using namespace google_breakpad; + +#if !defined(__ANDROID__) +#define TEMPDIR "/tmp" +#else +#define TEMPDIR "/data/local/tmp" +#endif + + +namespace { + +typedef testing::Test ProcCpuInfoReaderTest; + +class ScopedTestFile : public AutoTestFile { +public: + explicit ScopedTestFile(const char* text) + : AutoTestFile("proc_cpuinfo_reader", text) { + } +}; + +} + +TEST(ProcCpuInfoReaderTest, EmptyFile) { + ScopedTestFile file(""); + ASSERT_TRUE(file.IsOk()); + ProcCpuInfoReader reader(file.GetFd()); + + const char* field; + ASSERT_FALSE(reader.GetNextField(&field)); +} + +TEST(ProcCpuInfoReaderTest, OneLineTerminated) { + ScopedTestFile file("foo : bar\n"); + ASSERT_TRUE(file.IsOk()); + ProcCpuInfoReader reader(file.GetFd()); + + const char* field; + ASSERT_TRUE(reader.GetNextField(&field)); + ASSERT_STREQ("foo", field); + ASSERT_STREQ("bar", reader.GetValue()); + + ASSERT_FALSE(reader.GetNextField(&field)); +} + +TEST(ProcCpuInfoReaderTest, OneLine) { + ScopedTestFile file("foo : bar"); + ASSERT_TRUE(file.IsOk()); + ProcCpuInfoReader reader(file.GetFd()); + + const char* field; + size_t value_len; + ASSERT_TRUE(reader.GetNextField(&field)); + ASSERT_STREQ("foo", field); + ASSERT_STREQ("bar", reader.GetValueAndLen(&value_len)); + ASSERT_EQ(3U, value_len); + + ASSERT_FALSE(reader.GetNextField(&field)); +} + +TEST(ProcCpuInfoReaderTest, TwoLinesTerminated) { + ScopedTestFile file("foo : bar\nzoo : tut\n"); + ASSERT_TRUE(file.IsOk()); + ProcCpuInfoReader reader(file.GetFd()); + + const char* field; + ASSERT_TRUE(reader.GetNextField(&field)); + ASSERT_STREQ("foo", field); + ASSERT_STREQ("bar", reader.GetValue()); + + ASSERT_TRUE(reader.GetNextField(&field)); + ASSERT_STREQ("zoo", field); + ASSERT_STREQ("tut", reader.GetValue()); + + ASSERT_FALSE(reader.GetNextField(&field)); +} + +TEST(ProcCpuInfoReaderTest, SkipMalformedLine) { + ScopedTestFile file("this line should have a column\nfoo : bar\n"); + ASSERT_TRUE(file.IsOk()); + ProcCpuInfoReader reader(file.GetFd()); + + const char* field; + ASSERT_TRUE(reader.GetNextField(&field)); + ASSERT_STREQ("foo", field); + ASSERT_STREQ("bar", reader.GetValue()); + + ASSERT_FALSE(reader.GetNextField(&field)); +} + +TEST(ProcCpuInfoReaderTest, SkipOneEmptyLine) { + ScopedTestFile file("\n\nfoo : bar\n"); + ASSERT_TRUE(file.IsOk()); + ProcCpuInfoReader reader(file.GetFd()); + + const char* field; + ASSERT_TRUE(reader.GetNextField(&field)); + ASSERT_STREQ("foo", field); + ASSERT_STREQ("bar", reader.GetValue()); + + ASSERT_FALSE(reader.GetNextField(&field)); +} + +TEST(ProcCpuInfoReaderTest, SkipEmptyField) { + ScopedTestFile file(" : bar\nzoo : tut\n"); + ASSERT_TRUE(file.IsOk()); + ProcCpuInfoReader reader(file.GetFd()); + + const char* field; + ASSERT_TRUE(reader.GetNextField(&field)); + ASSERT_STREQ("zoo", field); + ASSERT_STREQ("tut", reader.GetValue()); + + ASSERT_FALSE(reader.GetNextField(&field)); +} + +TEST(ProcCpuInfoReaderTest, SkipTwoEmptyLines) { + ScopedTestFile file("foo : bar\n\n\nfoo : bar\n"); + ASSERT_TRUE(file.IsOk()); + ProcCpuInfoReader reader(file.GetFd()); + + const char* field; + ASSERT_TRUE(reader.GetNextField(&field)); + ASSERT_STREQ("foo", field); + ASSERT_STREQ("bar", reader.GetValue()); + + ASSERT_TRUE(reader.GetNextField(&field)); + ASSERT_STREQ("foo", field); + ASSERT_STREQ("bar", reader.GetValue()); + + ASSERT_FALSE(reader.GetNextField(&field)); +} + +TEST(ProcCpuInfoReaderTest, FieldWithSpaces) { + ScopedTestFile file("foo bar : zoo\n"); + ASSERT_TRUE(file.IsOk()); + ProcCpuInfoReader reader(file.GetFd()); + + const char* field; + ASSERT_TRUE(reader.GetNextField(&field)); + ASSERT_STREQ("foo bar", field); + ASSERT_STREQ("zoo", reader.GetValue()); + + ASSERT_FALSE(reader.GetNextField(&field)); +} + +TEST(ProcCpuInfoReaderTest, EmptyValue) { + ScopedTestFile file("foo :\n"); + ASSERT_TRUE(file.IsOk()); + ProcCpuInfoReader reader(file.GetFd()); + + const char* field; + ASSERT_TRUE(reader.GetNextField(&field)); + ASSERT_STREQ("foo", field); + size_t value_len; + ASSERT_STREQ("", reader.GetValueAndLen(&value_len)); + ASSERT_EQ(0U, value_len); + + ASSERT_FALSE(reader.GetNextField(&field)); +} diff --git a/shared/sentry/external/breakpad/src/client/linux/sender/google_crash_report_sender.cc b/shared/sentry/external/breakpad/src/client/linux/sender/google_crash_report_sender.cc new file mode 100644 index 000000000..6bf337a8d --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/linux/sender/google_crash_report_sender.cc @@ -0,0 +1,105 @@ +// 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 +#include +#include +#include + +#include "common/using_std_string.h" + +DEFINE_string(crash_server, "https://clients2.google.com/cr", + "The crash server to upload minidumps to."); +DEFINE_string(product_name, "", + "The product name that the minidump corresponds to."); +DEFINE_string(product_version, "", + "The version of the product that produced the minidump."); +DEFINE_string(client_id, "", + "The client GUID"); +DEFINE_string(minidump_path, "", + "The path of the minidump file."); +DEFINE_string(ptime, "", + "The process uptime in milliseconds."); +DEFINE_string(ctime, "", + "The cumulative process uptime in milliseconds."); +DEFINE_string(email, "", + "The user's email address."); +DEFINE_string(comments, "", + "Extra user comments"); +DEFINE_string(proxy_host, "", + "Proxy host"); +DEFINE_string(proxy_userpasswd, "", + "Proxy username/password in user:pass format."); + + +bool CheckForRequiredFlagsOrDie() { + string error_text = ""; + if (FLAGS_product_name.empty()) { + error_text.append("\nProduct name must be specified."); + } + + if (FLAGS_product_version.empty()) { + error_text.append("\nProduct version must be specified."); + } + + if (FLAGS_client_id.empty()) { + error_text.append("\nClient ID must be specified."); + } + + if (FLAGS_minidump_path.empty()) { + error_text.append("\nMinidump pathname must be specified."); + } + + if (!error_text.empty()) { + std::cout << error_text; + return false; + } + return true; +} + +int main(int argc, char* argv[]) { + google::InitGoogleLogging(argv[0]); + google::ParseCommandLineFlags(&argc, &argv, true); + if (!CheckForRequiredFlagsOrDie()) { + return 1; + } + google_breakpad::GoogleCrashdumpUploader g(FLAGS_product_name, + FLAGS_product_version, + FLAGS_client_id, + FLAGS_ptime, + FLAGS_ctime, + FLAGS_email, + FLAGS_comments, + FLAGS_minidump_path, + FLAGS_crash_server, + FLAGS_proxy_host, + FLAGS_proxy_userpasswd); + g.Upload(NULL, NULL, NULL); +} diff --git a/shared/sentry/external/breakpad/src/client/mac/Breakpad.xcodeproj/project.pbxproj b/shared/sentry/external/breakpad/src/client/mac/Breakpad.xcodeproj/project.pbxproj new file mode 100644 index 000000000..ed782c918 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/Breakpad.xcodeproj/project.pbxproj @@ -0,0 +1,2855 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 45; + objects = { + +/* Begin PBXAggregateTarget section */ + F94585840F782326009A47BF /* All */ = { + isa = PBXAggregateTarget; + buildConfigurationList = F94585930F78235C009A47BF /* Build configuration list for PBXAggregateTarget "All" */; + buildPhases = ( + ); + dependencies = ( + F94585880F78232B009A47BF /* PBXTargetDependency */, + F945858A0F78232E009A47BF /* PBXTargetDependency */, + F945858C0F782330009A47BF /* PBXTargetDependency */, + F945858E0F782333009A47BF /* PBXTargetDependency */, + F94585900F782336009A47BF /* PBXTargetDependency */, + F93DE3A70F830D1D00608B94 /* PBXTargetDependency */, + F95BB8B3101F94D300AA053B /* PBXTargetDependency */, + F95BB8B5101F94D300AA053B /* PBXTargetDependency */, + F95BB8B7101F94D300AA053B /* PBXTargetDependency */, + 8B31023911F0CF0600FCF3E4 /* PBXTargetDependency */, + 8B31051711F1010E00FCF3E4 /* PBXTargetDependency */, + 8B31051911F1010E00FCF3E4 /* PBXTargetDependency */, + 8B31051B11F1010E00FCF3E4 /* PBXTargetDependency */, + 8B31051D11F1010E00FCF3E4 /* PBXTargetDependency */, + 8B31051F11F1010E00FCF3E4 /* PBXTargetDependency */, + ); + name = All; + productName = All; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 162F64F2161C577500CD68D5 /* arch_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = 162F64F0161C577500CD68D5 /* arch_utilities.cc */; }; + 162F64F3161C577500CD68D5 /* arch_utilities.h in Headers */ = {isa = PBXBuildFile; fileRef = 162F64F1161C577500CD68D5 /* arch_utilities.h */; }; + 162F64F4161C579B00CD68D5 /* arch_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = 162F64F0161C577500CD68D5 /* arch_utilities.cc */; }; + 162F64F5161C579B00CD68D5 /* arch_utilities.h in Sources */ = {isa = PBXBuildFile; fileRef = 162F64F1161C577500CD68D5 /* arch_utilities.h */; }; + 163201D61443019E00C4DBF5 /* ConfigFile.h in Headers */ = {isa = PBXBuildFile; fileRef = 163201D41443019E00C4DBF5 /* ConfigFile.h */; }; + 163201D71443019E00C4DBF5 /* ConfigFile.mm in Sources */ = {isa = PBXBuildFile; fileRef = 163201D51443019E00C4DBF5 /* ConfigFile.mm */; }; + 163201E31443029300C4DBF5 /* ConfigFile.mm in Sources */ = {isa = PBXBuildFile; fileRef = 163201D51443019E00C4DBF5 /* ConfigFile.mm */; }; + 16C7C918147D45AE00776EAD /* BreakpadDefines.h in Headers */ = {isa = PBXBuildFile; fileRef = 16C7C917147D45AE00776EAD /* BreakpadDefines.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 16E02DB8147410F0008C604D /* uploader.mm in Sources */ = {isa = PBXBuildFile; fileRef = 16E02DB4147410D4008C604D /* uploader.mm */; }; + 1EEEB6231720829E00F7E689 /* simple_string_dictionary.cc in Sources */ = {isa = PBXBuildFile; fileRef = 1EEEB6211720829E00F7E689 /* simple_string_dictionary.cc */; }; + 1EEEB6241720829E00F7E689 /* simple_string_dictionary.h in Headers */ = {isa = PBXBuildFile; fileRef = 1EEEB6221720829E00F7E689 /* simple_string_dictionary.h */; }; + 1EEEB6271720831E00F7E689 /* BreakpadFramework_Test.mm in Sources */ = {isa = PBXBuildFile; fileRef = F91AF5CF0FD60393009D8BE2 /* BreakpadFramework_Test.mm */; }; + 1EEEB62A1720859200F7E689 /* simple_string_dictionary_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 1EEEB6251720830600F7E689 /* simple_string_dictionary_unittest.cc */; }; + 1EEEB62B1720868C00F7E689 /* simple_string_dictionary.cc in Sources */ = {isa = PBXBuildFile; fileRef = 1EEEB6211720829E00F7E689 /* simple_string_dictionary.cc */; }; + 3329D4ED0FA16D820007BBC5 /* Breakpad.xib in Resources */ = {isa = PBXBuildFile; fileRef = 3329D4EC0FA16D820007BBC5 /* Breakpad.xib */; }; + 33880C800F9E097100817F82 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 33880C7E0F9E097100817F82 /* InfoPlist.strings */; }; + 4084699D0F5D9CF900FDCA37 /* crash_report_sender.icns in Resources */ = {isa = PBXBuildFile; fileRef = 4084699C0F5D9CF900FDCA37 /* crash_report_sender.icns */; }; + 421BC5BC21110C0300B8042E /* convert_old_arm64_context.cc in Sources */ = {isa = PBXBuildFile; fileRef = 421BC5AD21110C0300B8042E /* convert_old_arm64_context.cc */; }; + 421BC5BD21110C0300B8042E /* convert_old_arm64_context.h in Headers */ = {isa = PBXBuildFile; fileRef = 421BC5BB21110C0300B8042E /* convert_old_arm64_context.h */; }; + 421BC5BE21110C1000B8042E /* convert_old_arm64_context.cc in Sources */ = {isa = PBXBuildFile; fileRef = 421BC5AD21110C0300B8042E /* convert_old_arm64_context.cc */; }; + 4247E6412110D7A300482558 /* memory_allocator_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = D244540A12439BA0009BBCE0 /* memory_allocator_unittest.cc */; }; + 4D61A25F14F43CFC002D5862 /* bootstrap_compat.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D61A25D14F43CFC002D5862 /* bootstrap_compat.cc */; }; + 4D61A26B14F43D3C002D5862 /* bootstrap_compat.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D61A25D14F43CFC002D5862 /* bootstrap_compat.cc */; }; + 4D61A26C14F43D42002D5862 /* bootstrap_compat.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D61A25D14F43CFC002D5862 /* bootstrap_compat.cc */; }; + 4D61A26D14F43D43002D5862 /* bootstrap_compat.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D61A25D14F43CFC002D5862 /* bootstrap_compat.cc */; }; + 4D61A26E14F43D45002D5862 /* bootstrap_compat.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D61A25D14F43CFC002D5862 /* bootstrap_compat.cc */; }; + 4D61A26F14F43D48002D5862 /* bootstrap_compat.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D61A25D14F43CFC002D5862 /* bootstrap_compat.cc */; }; + 4D72CA0E13DFAD5C006CABE3 /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D72CA0D13DFAD5C006CABE3 /* md5.cc */; }; + 4D72CA2513DFAE1C006CABE3 /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D72CA0D13DFAD5C006CABE3 /* md5.cc */; }; + 4D72CA2F13DFAE65006CABE3 /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D72CA0D13DFAD5C006CABE3 /* md5.cc */; }; + 4D72CA3813DFAE91006CABE3 /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D72CA0D13DFAD5C006CABE3 /* md5.cc */; }; + 4D72CA3913DFAE92006CABE3 /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = 4D72CA0D13DFAD5C006CABE3 /* md5.cc */; }; + 4DBE49A6134A4F200072546A /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DBE4769134A4F080072546A /* CoreServices.framework */; }; + 4DBE49A7134A4F280072546A /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DBE4769134A4F080072546A /* CoreServices.framework */; }; + 4DBE49A8134A4F380072546A /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DBE4769134A4F080072546A /* CoreServices.framework */; }; + 4DBE49A9134A4F460072546A /* CoreServices.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4DBE4769134A4F080072546A /* CoreServices.framework */; }; + 8B3101C611F0CD9F00FCF3E4 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D6A5FE840307C02AAC07 /* AppKit.framework */; }; + 8B3101C711F0CD9F00FCF3E4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; }; + 8B3101CA11F0CDB000FCF3E4 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D6A5FE840307C02AAC07 /* AppKit.framework */; }; + 8B3101CB11F0CDB000FCF3E4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; }; + 8B3101EA11F0CDE300FCF3E4 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8B3101E911F0CDE300FCF3E4 /* SenTestingKit.framework */; }; + 8B31029411F0D54300FCF3E4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; }; + 8B3102E611F0D74C00FCF3E4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; }; + 8B3102EB11F0D78000FCF3E4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; }; + 8B31FC8211EFD2B800FCF3E4 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; }; + 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; }; + D23F4B2E12A7E13200686C8D /* minidump_generator_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D23F4B2C12A7E13200686C8D /* minidump_generator_test.cc */; }; + D23F4B3312A7E17700686C8D /* libgtest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D2F9A41512131EF0002747C1 /* libgtest.a */; }; + D23F4BB112A868CB00686C8D /* minidump_generator_test_helper.cc in Sources */ = {isa = PBXBuildFile; fileRef = D23F4B9A12A8688800686C8D /* minidump_generator_test_helper.cc */; }; + D23F4BB812A868F700686C8D /* MachIPC.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C53790ECCE635009BE4BA /* MachIPC.mm */; }; + D244536A12426F00009BBCE0 /* logging.cc in Sources */ = {isa = PBXBuildFile; fileRef = D244535112426EBB009BBCE0 /* logging.cc */; }; + D244536B12426F00009BBCE0 /* minidump.cc in Sources */ = {isa = PBXBuildFile; fileRef = D244535212426EBB009BBCE0 /* minidump.cc */; }; + D244536C12426F00009BBCE0 /* pathname_stripper.cc in Sources */ = {isa = PBXBuildFile; fileRef = D244535312426EBB009BBCE0 /* pathname_stripper.cc */; }; + D244536D12426F00009BBCE0 /* basic_code_modules.cc in Sources */ = {isa = PBXBuildFile; fileRef = D244534F12426E98009BBCE0 /* basic_code_modules.cc */; }; + D246417012BAA40E005170D0 /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536D0ECCE3FD009BE4BA /* exception_handler.cc */; }; + D246417112BAA41C005170D0 /* crash_generation_client.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F9A4C6121336C7002747C1 /* crash_generation_client.cc */; }; + D246417512BAA438005170D0 /* minidump_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536F0ECCE3FD009BE4BA /* minidump_generator.cc */; }; + D246417612BAA43F005170D0 /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536B0ECCE3FD009BE4BA /* dynamic_images.cc */; }; + D246417712BAA444005170D0 /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53690ECCE3FD009BE4BA /* breakpad_nlist_64.cc */; }; + D246418412BAA4BA005170D0 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; }; + D246418812BAA4E3005170D0 /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53820ECCE635009BE4BA /* string_utilities.cc */; }; + D246418C12BAA508005170D0 /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */; }; + D246419012BAA52A005170D0 /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53850ECCE6AD009BE4BA /* string_conversion.cc */; }; + D246419112BAA52F005170D0 /* convert_UTF.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53870ECCE6C0009BE4BA /* convert_UTF.cc */; }; + D246419512BAA54C005170D0 /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53740ECCE635009BE4BA /* file_id.cc */; }; + D246419612BAA55A005170D0 /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537A0ECCE635009BE4BA /* macho_id.cc */; }; + D24641A012BAA67F005170D0 /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537E0ECCE635009BE4BA /* macho_walker.cc */; }; + D24641AF12BAA82D005170D0 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537C0ECCE635009BE4BA /* macho_utilities.cc */; }; + D24641EC12BAC6FB005170D0 /* logging.cc in Sources */ = {isa = PBXBuildFile; fileRef = D244535112426EBB009BBCE0 /* logging.cc */; }; + D24641ED12BAC6FB005170D0 /* minidump.cc in Sources */ = {isa = PBXBuildFile; fileRef = D244535212426EBB009BBCE0 /* minidump.cc */; }; + D24641EE12BAC6FB005170D0 /* pathname_stripper.cc in Sources */ = {isa = PBXBuildFile; fileRef = D244535312426EBB009BBCE0 /* pathname_stripper.cc */; }; + D24641EF12BAC6FB005170D0 /* basic_code_modules.cc in Sources */ = {isa = PBXBuildFile; fileRef = D244534F12426E98009BBCE0 /* basic_code_modules.cc */; }; + D24BBBFD121050F000F3D417 /* breakpadUtilities.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F92C563C0ECD10B3009BE4BA /* breakpadUtilities.dylib */; }; + D24BBD291211EDB100F3D417 /* MachIPC.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C53790ECCE635009BE4BA /* MachIPC.mm */; }; + D24BBD321212CACF00F3D417 /* MachIPC.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C53790ECCE635009BE4BA /* MachIPC.mm */; }; + D2A5DD301188633800081F03 /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53690ECCE3FD009BE4BA /* breakpad_nlist_64.cc */; }; + D2A5DD401188640400081F03 /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53690ECCE3FD009BE4BA /* breakpad_nlist_64.cc */; }; + D2A5DD411188642E00081F03 /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53690ECCE3FD009BE4BA /* breakpad_nlist_64.cc */; }; + D2C1DBE412AFC270006917BD /* logging.cc in Sources */ = {isa = PBXBuildFile; fileRef = D244535112426EBB009BBCE0 /* logging.cc */; }; + D2C1DBE512AFC270006917BD /* minidump.cc in Sources */ = {isa = PBXBuildFile; fileRef = D244535212426EBB009BBCE0 /* minidump.cc */; }; + D2C1DBE612AFC270006917BD /* pathname_stripper.cc in Sources */ = {isa = PBXBuildFile; fileRef = D244535312426EBB009BBCE0 /* pathname_stripper.cc */; }; + D2C1DBE712AFC270006917BD /* basic_code_modules.cc in Sources */ = {isa = PBXBuildFile; fileRef = D244534F12426E98009BBCE0 /* basic_code_modules.cc */; }; + D2F9A3D51212F87C002747C1 /* exception_handler_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F9A3D41212F87C002747C1 /* exception_handler_test.cc */; }; + D2F9A43D12131F55002747C1 /* gmock-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F9A43C12131F55002747C1 /* gmock-all.cc */; }; + D2F9A44012131F65002747C1 /* gtest_main.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F9A43E12131F65002747C1 /* gtest_main.cc */; }; + D2F9A44112131F65002747C1 /* gtest-all.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F9A43F12131F65002747C1 /* gtest-all.cc */; }; + D2F9A44412131F84002747C1 /* libgtest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D2F9A41512131EF0002747C1 /* libgtest.a */; }; + D2F9A4C9121336C7002747C1 /* client_info.h in Headers */ = {isa = PBXBuildFile; fileRef = D2F9A4C4121336C7002747C1 /* client_info.h */; }; + D2F9A4CA121336C7002747C1 /* crash_generation_client.h in Headers */ = {isa = PBXBuildFile; fileRef = D2F9A4C5121336C7002747C1 /* crash_generation_client.h */; }; + D2F9A4CB121336C7002747C1 /* crash_generation_client.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F9A4C6121336C7002747C1 /* crash_generation_client.cc */; }; + D2F9A4CC121336C7002747C1 /* crash_generation_server.h in Headers */ = {isa = PBXBuildFile; fileRef = D2F9A4C7121336C7002747C1 /* crash_generation_server.h */; }; + D2F9A4CD121336C7002747C1 /* crash_generation_server.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F9A4C8121336C7002747C1 /* crash_generation_server.cc */; }; + D2F9A4DF12133AD9002747C1 /* crash_generation_client.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F9A4C6121336C7002747C1 /* crash_generation_client.cc */; }; + D2F9A4E012133AD9002747C1 /* crash_generation_server.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F9A4C8121336C7002747C1 /* crash_generation_server.cc */; }; + D2F9A4E112133AE2002747C1 /* crash_generation_client.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F9A4C6121336C7002747C1 /* crash_generation_client.cc */; }; + D2F9A4E212133AE2002747C1 /* crash_generation_server.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F9A4C8121336C7002747C1 /* crash_generation_server.cc */; }; + D2F9A52E121383A1002747C1 /* crash_generation_client.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F9A4C6121336C7002747C1 /* crash_generation_client.cc */; }; + D2F9A52F121383A1002747C1 /* crash_generation_server.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F9A4C8121336C7002747C1 /* crash_generation_server.cc */; }; + D2F9A530121383A1002747C1 /* MachIPC.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C53790ECCE635009BE4BA /* MachIPC.mm */; }; + D2F9A531121383A1002747C1 /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53690ECCE3FD009BE4BA /* breakpad_nlist_64.cc */; }; + D2F9A532121383A1002747C1 /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536B0ECCE3FD009BE4BA /* dynamic_images.cc */; }; + D2F9A533121383A1002747C1 /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536D0ECCE3FD009BE4BA /* exception_handler.cc */; }; + D2F9A534121383A1002747C1 /* minidump_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536F0ECCE3FD009BE4BA /* minidump_generator.cc */; }; + D2F9A535121383A1002747C1 /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */; }; + D2F9A536121383A1002747C1 /* convert_UTF.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53870ECCE6C0009BE4BA /* convert_UTF.cc */; }; + D2F9A537121383A1002747C1 /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53850ECCE6AD009BE4BA /* string_conversion.cc */; }; + D2F9A538121383A1002747C1 /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53740ECCE635009BE4BA /* file_id.cc */; }; + D2F9A539121383A1002747C1 /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537A0ECCE635009BE4BA /* macho_id.cc */; }; + D2F9A53A121383A1002747C1 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537C0ECCE635009BE4BA /* macho_utilities.cc */; }; + D2F9A53B121383A1002747C1 /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537E0ECCE635009BE4BA /* macho_walker.cc */; }; + D2F9A53C121383A1002747C1 /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53820ECCE635009BE4BA /* string_utilities.cc */; }; + D2F9A53F121383A1002747C1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; }; + D2F9A541121383A1002747C1 /* libgtest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = D2F9A41512131EF0002747C1 /* libgtest.a */; }; + D2F9A553121383DC002747C1 /* crash_generation_server_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F9A4CE121336F7002747C1 /* crash_generation_server_test.cc */; }; + EB9CF8B924F01E1D00F9B6D1 /* encoding_util.m in Sources */ = {isa = PBXBuildFile; fileRef = EB9CF89F24F01E1D00F9B6D1 /* encoding_util.m */; }; + EB9CF8BA24F01E1D00F9B6D1 /* minidump_upload.m in Sources */ = {isa = PBXBuildFile; fileRef = EB9CF8AD24F01E1D00F9B6D1 /* minidump_upload.m */; }; + EB9CF8BB24F01E1D00F9B6D1 /* encoding_util.h in Headers */ = {isa = PBXBuildFile; fileRef = EB9CF8AE24F01E1D00F9B6D1 /* encoding_util.h */; }; + EB9CF8BC24F01E1D00F9B6D1 /* HTTPSimplePostRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = EB9CF8AF24F01E1D00F9B6D1 /* HTTPSimplePostRequest.h */; }; + EB9CF8BD24F01E1D00F9B6D1 /* HTTPRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = EB9CF8B024F01E1D00F9B6D1 /* HTTPRequest.h */; }; + EB9CF8BE24F01E1D00F9B6D1 /* HTTPPutRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = EB9CF8B124F01E1D00F9B6D1 /* HTTPPutRequest.m */; }; + EB9CF8BF24F01E1D00F9B6D1 /* HTTPRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = EB9CF8B224F01E1D00F9B6D1 /* HTTPRequest.m */; }; + EB9CF8C024F01E1D00F9B6D1 /* SymbolCollectorClient.m in Sources */ = {isa = PBXBuildFile; fileRef = EB9CF8B324F01E1D00F9B6D1 /* SymbolCollectorClient.m */; }; + EB9CF8C124F01E1D00F9B6D1 /* HTTPGetRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = EB9CF8B424F01E1D00F9B6D1 /* HTTPGetRequest.h */; }; + EB9CF8C224F01E1D00F9B6D1 /* HTTPGetRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = EB9CF8B524F01E1D00F9B6D1 /* HTTPGetRequest.m */; }; + EB9CF8C324F01E1D00F9B6D1 /* HTTPSimplePostRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = EB9CF8B624F01E1D00F9B6D1 /* HTTPSimplePostRequest.m */; }; + EB9CF8C424F01E1D00F9B6D1 /* SymbolCollectorClient.h in Headers */ = {isa = PBXBuildFile; fileRef = EB9CF8B724F01E1D00F9B6D1 /* SymbolCollectorClient.h */; }; + EB9CF8C524F01E1D00F9B6D1 /* HTTPPutRequest.h in Headers */ = {isa = PBXBuildFile; fileRef = EB9CF8B824F01E1D00F9B6D1 /* HTTPPutRequest.h */; }; + EB9CF8C624F01F1100F9B6D1 /* HTTPRequest.m in Sources */ = {isa = PBXBuildFile; fileRef = EB9CF8B224F01E1D00F9B6D1 /* HTTPRequest.m */; }; + EB9CF8C724F01F7600F9B6D1 /* encoding_util.m in Sources */ = {isa = PBXBuildFile; fileRef = EB9CF89F24F01E1D00F9B6D1 /* encoding_util.m */; }; + EB9CF8C824F01FB900F9B6D1 /* HTTPMultipartUpload.m in Sources */ = {isa = PBXBuildFile; fileRef = F92C53770ECCE635009BE4BA /* HTTPMultipartUpload.m */; }; + F4DAB1DD19F1027100A5A838 /* launch_reporter.cc in Sources */ = {isa = PBXBuildFile; fileRef = F4DAB1DB19F1027100A5A838 /* launch_reporter.cc */; }; + F4DAB1DE19F1027100A5A838 /* launch_reporter.h in Headers */ = {isa = PBXBuildFile; fileRef = F4DAB1DC19F1027100A5A838 /* launch_reporter.h */; }; + F4F916B619F10FFC00B83BE4 /* launch_reporter.cc in Sources */ = {isa = PBXBuildFile; fileRef = F4DAB1DB19F1027100A5A838 /* launch_reporter.cc */; }; + F91AF6210FD60784009D8BE2 /* Breakpad.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* Breakpad.framework */; }; + F9286B3A0F7EB25800A4DCC8 /* InspectorMain.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9286B390F7EB25800A4DCC8 /* InspectorMain.mm */; }; + F92C53B80ECCE7B3009BE4BA /* Inspector.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C53B70ECCE7B3009BE4BA /* Inspector.mm */; }; + F92C554C0ECCF534009BE4BA /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0867D69BFE84028FC02AAC07 /* Foundation.framework */; }; + F92C55D00ECD0064009BE4BA /* Breakpad.h in Headers */ = {isa = PBXBuildFile; fileRef = F92C55CE0ECD0064009BE4BA /* Breakpad.h */; settings = {ATTRIBUTES = (Public, ); }; }; + F92C55D10ECD0064009BE4BA /* Breakpad.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C55CF0ECD0064009BE4BA /* Breakpad.mm */; }; + F92C56330ECD0DF1009BE4BA /* OnDemandServer.h in Headers */ = {isa = PBXBuildFile; fileRef = F92C56310ECD0DF1009BE4BA /* OnDemandServer.h */; }; + F92C56340ECD0DF1009BE4BA /* OnDemandServer.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C56320ECD0DF1009BE4BA /* OnDemandServer.mm */; }; + F92C563F0ECD10CA009BE4BA /* convert_UTF.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53870ECCE6C0009BE4BA /* convert_UTF.cc */; }; + F92C56400ECD10CA009BE4BA /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536B0ECCE3FD009BE4BA /* dynamic_images.cc */; }; + F92C56410ECD10CA009BE4BA /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53740ECCE635009BE4BA /* file_id.cc */; }; + F92C56420ECD10CA009BE4BA /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537A0ECCE635009BE4BA /* macho_id.cc */; }; + F92C56430ECD10CA009BE4BA /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537C0ECCE635009BE4BA /* macho_utilities.cc */; }; + F92C56440ECD10CA009BE4BA /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537E0ECCE635009BE4BA /* macho_walker.cc */; }; + F92C56450ECD10CA009BE4BA /* MachIPC.mm in Sources */ = {isa = PBXBuildFile; fileRef = F92C53790ECCE635009BE4BA /* MachIPC.mm */; }; + F92C56460ECD10CA009BE4BA /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */; }; + F92C56470ECD10CA009BE4BA /* minidump_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536F0ECCE3FD009BE4BA /* minidump_generator.cc */; }; + F92C56490ECD10CA009BE4BA /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53820ECCE635009BE4BA /* string_utilities.cc */; }; + F92C564A0ECD10CA009BE4BA /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53850ECCE6AD009BE4BA /* string_conversion.cc */; }; + F92C564C0ECD10DD009BE4BA /* breakpadUtilities.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F92C563C0ECD10B3009BE4BA /* breakpadUtilities.dylib */; }; + F92C56570ECD113E009BE4BA /* Carbon.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F92C554A0ECCF530009BE4BA /* Carbon.framework */; }; + F92C565C0ECD1158009BE4BA /* breakpadUtilities.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = F92C563C0ECD10B3009BE4BA /* breakpadUtilities.dylib */; }; + F92C565F0ECD116B009BE4BA /* protected_memory_allocator.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53720ECCE3FD009BE4BA /* protected_memory_allocator.cc */; }; + F92C56630ECD1179009BE4BA /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536D0ECCE3FD009BE4BA /* exception_handler.cc */; }; + F92C56650ECD1185009BE4BA /* breakpadUtilities.dylib in Resources */ = {isa = PBXBuildFile; fileRef = F92C563C0ECD10B3009BE4BA /* breakpadUtilities.dylib */; }; + F92C568A0ECD15F9009BE4BA /* Inspector in Resources */ = {isa = PBXBuildFile; fileRef = F92C53540ECCE349009BE4BA /* Inspector */; }; + F92C56A90ECE04C5009BE4BA /* crash_report_sender.m in Sources */ = {isa = PBXBuildFile; fileRef = F92C56A80ECE04C5009BE4BA /* crash_report_sender.m */; }; + F93803CD0F8083B7004D428B /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536B0ECCE3FD009BE4BA /* dynamic_images.cc */; }; + F93803CE0F8083B7004D428B /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536D0ECCE3FD009BE4BA /* exception_handler.cc */; }; + F93803CF0F8083B7004D428B /* minidump_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536F0ECCE3FD009BE4BA /* minidump_generator.cc */; }; + F93803D00F8083B7004D428B /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */; }; + F93803D10F8083B7004D428B /* convert_UTF.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53870ECCE6C0009BE4BA /* convert_UTF.cc */; }; + F93803D20F8083B7004D428B /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53850ECCE6AD009BE4BA /* string_conversion.cc */; }; + F93803D30F8083B7004D428B /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53740ECCE635009BE4BA /* file_id.cc */; }; + F93803D40F8083B7004D428B /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537A0ECCE635009BE4BA /* macho_id.cc */; }; + F93803D50F8083B7004D428B /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537C0ECCE635009BE4BA /* macho_utilities.cc */; }; + F93803D60F8083B7004D428B /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537E0ECCE635009BE4BA /* macho_walker.cc */; }; + F93803D70F8083B7004D428B /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53820ECCE635009BE4BA /* string_utilities.cc */; }; + F93DE2D80F82A70E00608B94 /* minidump_file_writer_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = F93DE2D70F82A70E00608B94 /* minidump_file_writer_unittest.cc */; }; + F93DE2D90F82A73500608B94 /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */; }; + F93DE2DA0F82A73500608B94 /* convert_UTF.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53870ECCE6C0009BE4BA /* convert_UTF.cc */; }; + F93DE2DB0F82A73500608B94 /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53850ECCE6AD009BE4BA /* string_conversion.cc */; }; + F93DE3350F82C66B00608B94 /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536B0ECCE3FD009BE4BA /* dynamic_images.cc */; }; + F93DE3360F82C66B00608B94 /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536D0ECCE3FD009BE4BA /* exception_handler.cc */; }; + F93DE3370F82C66B00608B94 /* minidump_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C536F0ECCE3FD009BE4BA /* minidump_generator.cc */; }; + F93DE3380F82C66B00608B94 /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */; }; + F93DE3390F82C66B00608B94 /* convert_UTF.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53870ECCE6C0009BE4BA /* convert_UTF.cc */; }; + F93DE33A0F82C66B00608B94 /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53850ECCE6AD009BE4BA /* string_conversion.cc */; }; + F93DE33B0F82C66B00608B94 /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53740ECCE635009BE4BA /* file_id.cc */; }; + F93DE33C0F82C66B00608B94 /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537A0ECCE635009BE4BA /* macho_id.cc */; }; + F93DE33D0F82C66B00608B94 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537C0ECCE635009BE4BA /* macho_utilities.cc */; }; + F93DE33E0F82C66B00608B94 /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C537E0ECCE635009BE4BA /* macho_walker.cc */; }; + F93DE33F0F82C66B00608B94 /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = F92C53820ECCE635009BE4BA /* string_utilities.cc */; }; + F945849E0F280E3C009A47BF /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = F945849C0F280E3C009A47BF /* Localizable.strings */; }; + F9B630A0100FF96B00D0F4AC /* goArrow.png in Resources */ = {isa = PBXBuildFile; fileRef = F9B6309F100FF96B00D0F4AC /* goArrow.png */; }; + F9C44DB20EF07288003AEBAA /* Controller.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C44DAC0EF07288003AEBAA /* Controller.m */; }; + F9C44DB30EF07288003AEBAA /* crashduringload in Resources */ = {isa = PBXBuildFile; fileRef = F9C44DAD0EF07288003AEBAA /* crashduringload */; }; + F9C44DB40EF07288003AEBAA /* crashInMain in Resources */ = {isa = PBXBuildFile; fileRef = F9C44DAE0EF07288003AEBAA /* crashInMain */; }; + F9C44DB60EF07288003AEBAA /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C44DB00EF07288003AEBAA /* main.m */; }; + F9C44DB70EF07288003AEBAA /* TestClass.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9C44DB10EF07288003AEBAA /* TestClass.mm */; }; + F9C44DBC0EF072A0003AEBAA /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = F9C44DB80EF072A0003AEBAA /* InfoPlist.strings */; }; + F9C44DBD0EF072A0003AEBAA /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = F9C44DBA0EF072A0003AEBAA /* MainMenu.xib */; }; + F9C44E000EF077CD003AEBAA /* Breakpad.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* Breakpad.framework */; }; + F9C44E3C0EF08B12003AEBAA /* Breakpad.framework in Copy Frameworks */ = {isa = PBXBuildFile; fileRef = 8DC2EF5B0486A6940098B216 /* Breakpad.framework */; }; + F9C44E980EF09F56003AEBAA /* crash_report_sender.app in Resources */ = {isa = PBXBuildFile; fileRef = F92C56A00ECE04A7009BE4BA /* crash_report_sender.app */; }; + F9C44EA20EF09F93003AEBAA /* HTTPMultipartUpload.m in Sources */ = {isa = PBXBuildFile; fileRef = F92C53770ECCE635009BE4BA /* HTTPMultipartUpload.m */; }; + F9C44EE50EF0A006003AEBAA /* SystemConfiguration.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9C44EE40EF0A006003AEBAA /* SystemConfiguration.framework */; }; + F9C44EE90EF0A3C1003AEBAA /* GTMLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C44EE80EF0A3C1003AEBAA /* GTMLogger.m */; }; + F9C77E130F7DDF810045F7DB /* GTMSenTestCase.m in Sources */ = {isa = PBXBuildFile; fileRef = F9C77E120F7DDF810045F7DB /* GTMSenTestCase.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 8B31023811F0CF0600FCF3E4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = B88FAFC9116BDCAD00407530; + remoteInfo = all_unittests; + }; + 8B31051611F1010E00FCF3E4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F93803BD0F80820F004D428B; + remoteInfo = generator_test; + }; + 8B31051811F1010E00FCF3E4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F93DE2D00F82A67300608B94; + remoteInfo = minidump_file_writer_unittest; + }; + 8B31051A11F1010E00FCF3E4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F93DE32B0F82C55600608B94; + remoteInfo = handler_test; + }; + 8B31051C11F1010E00FCF3E4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = B89E0E731166575200DD08C9; + remoteInfo = macho_dump; + }; + 8B31051E11F1010E00FCF3E4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB894101F94C000AA053B /* symupload.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 9BD835FA0B0544950055103E; + remoteInfo = minidump_upload; + }; + 8B31F7A011EF9A8700FCF3E4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = B89E0E741166575200DD08C9; + remoteInfo = macho_dump; + }; + 8B31F7A211EF9A8700FCF3E4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = B88FB024116BDFFF00407530; + remoteInfo = gtestmockall; + }; + 8B31F7A411EF9A8700FCF3E4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = B88FB14B116CF4A700407530; + remoteInfo = byte_cursor_unittest; + }; + 8B31F7A611EF9A8700FCF3E4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = B89E0E9511665A6400DD08C9; + remoteInfo = macho_reader_unittest; + }; + 8B31F7A811EF9A8700FCF3E4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = B88FB007116BDE8300407530; + remoteInfo = stabs_reader_unittest; + }; + 8B31F7AA11EF9A8700FCF3E4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = B88FB135116CF30F00407530; + remoteInfo = bytereader_unittest; + }; + 8B31F7AC11EF9A8700FCF3E4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = B88FAF2F116A591E00407530; + remoteInfo = dwarf2reader_cfi_unittest; + }; + 8B31F7AE11EF9A8700FCF3E4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = B88FB0DF116CEEA800407530; + remoteInfo = dwarf2diehandler_unittest; + }; + 8B31F7B011EF9A8700FCF3E4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = B88FB10A116CF07900407530; + remoteInfo = dwarf_cu_to_module_unittest; + }; + 8B31F7B211EF9A8700FCF3E4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = B88FB0F2116CEF1900407530; + remoteInfo = dwarf_line_to_module_unittest; + }; + 8B31F7B411EF9A8700FCF3E4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = B88FB11F116CF27F00407530; + remoteInfo = dwarf_cfi_to_module_unittest; + }; + 8B31F7B611EF9A8700FCF3E4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = B84A91F4116CF784006C210E; + remoteInfo = stabs_to_module_unittest; + }; + 8B31F7B811EF9A8700FCF3E4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = B88FB0B9116CEABF00407530; + remoteInfo = module_unittest; + }; + 8B31F7BA11EF9A8700FCF3E4 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = D21F97D211CBA0F200239E38; + remoteInfo = test_assembler_unittest; + }; + D23F4B2F12A7E16200686C8D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = D2F9A41412131EF0002747C1; + remoteInfo = gtest; + }; + D23F4BB912A8694C00686C8D /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = D23F4BAA12A868A500686C8D; + remoteInfo = minidump_generator_test_helper; + }; + D2F9A44212131F80002747C1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = D2F9A41412131EF0002747C1; + remoteInfo = gtest; + }; + D2F9A52C121383A1002747C1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = D2F9A41412131EF0002747C1; + remoteInfo = gtest; + }; + D2F9A5DE12142A6A002747C1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = D2F9A52A121383A1002747C1; + remoteInfo = crash_generation_server_test; + }; + F91AF6370FD60A74009D8BE2 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8DC2EF4F0486A6940098B216; + remoteInfo = Breakpad; + }; + F92C564D0ECD10E5009BE4BA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F92C563B0ECD10B3009BE4BA; + remoteInfo = breakpadUtilities; + }; + F92C56850ECD15EF009BE4BA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F92C563B0ECD10B3009BE4BA; + remoteInfo = breakpadUtilities; + }; + F92C56870ECD15F1009BE4BA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F92C53530ECCE349009BE4BA; + remoteInfo = Inspector; + }; + F93DE2FB0F82C3C600608B94 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F93803BD0F80820F004D428B; + remoteInfo = generator_test; + }; + F93DE36F0F82CC1300608B94 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F93DE32B0F82C55600608B94; + remoteInfo = handler_test; + }; + F93DE3A60F830D1D00608B94 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9C77DD90F7DD5CF0045F7DB; + remoteInfo = UnitTests; + }; + F94585870F78232B009A47BF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8DC2EF4F0486A6940098B216; + remoteInfo = Breakpad; + }; + F94585890F78232E009A47BF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F92C53530ECCE349009BE4BA; + remoteInfo = Inspector; + }; + F945858B0F782330009A47BF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F92C563B0ECD10B3009BE4BA; + remoteInfo = breakpadUtilities; + }; + F945858D0F782333009A47BF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F92C569F0ECE04A7009BE4BA; + remoteInfo = crash_report_sender; + }; + F945858F0F782336009A47BF /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F9C44DA40EF060A8003AEBAA; + remoteInfo = BreakpadTest; + }; + F95BB884101F949F00AA053B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB87C101F949F00AA053B /* crash_report.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 8DD76FA10486AA7600D96B5E; + remoteInfo = crash_report; + }; + F95BB891101F94AC00AA053B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 8DD76FA10486AA7600D96B5E; + remoteInfo = dump_syms; + }; + F95BB89E101F94C000AA053B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB894101F94C000AA053B /* symupload.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 8DD76FA10486AA7600D96B5E; + remoteInfo = symupload; + }; + F95BB8A0101F94C000AA053B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB894101F94C000AA053B /* symupload.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 9BD835FB0B0544950055103E; + remoteInfo = minidump_upload; + }; + F95BB8B2101F94D300AA053B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = B8C5B5101166531A00D34F4E; + remoteInfo = dump_syms; + }; + F95BB8B4101F94D300AA053B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB894101F94C000AA053B /* symupload.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 8DD76F960486AA7600D96B5E; + remoteInfo = symupload; + }; + F95BB8B6101F94D300AA053B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F95BB87C101F949F00AA053B /* crash_report.xcodeproj */; + proxyType = 1; + remoteGlobalIDString = 8DD76F960486AA7600D96B5E; + remoteInfo = crash_report; + }; + F9C44E190EF0790F003AEBAA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8DC2EF4F0486A6940098B216; + remoteInfo = Breakpad; + }; + F9C44E960EF09F4B003AEBAA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 0867D690FE84028FC02AAC07 /* Project object */; + proxyType = 1; + remoteGlobalIDString = F92C569F0ECE04A7009BE4BA; + remoteInfo = crash_report_sender; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + F9C44E410EF08B17003AEBAA /* Copy Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + F9C44E3C0EF08B12003AEBAA /* Breakpad.framework in Copy Frameworks */, + ); + name = "Copy Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 0867D69BFE84028FC02AAC07 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; + 0867D6A5FE840307C02AAC07 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; }; + 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; + 162F64F0161C577500CD68D5 /* arch_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = arch_utilities.cc; path = ../../common/mac/arch_utilities.cc; sourceTree = ""; }; + 162F64F1161C577500CD68D5 /* arch_utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = arch_utilities.h; path = ../../common/mac/arch_utilities.h; sourceTree = ""; }; + 163201D41443019E00C4DBF5 /* ConfigFile.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ConfigFile.h; path = crash_generation/ConfigFile.h; sourceTree = ""; }; + 163201D51443019E00C4DBF5 /* ConfigFile.mm */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.objcpp; fileEncoding = 4; name = ConfigFile.mm; path = crash_generation/ConfigFile.mm; sourceTree = ""; }; + 163202431443201300C4DBF5 /* uploader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = uploader.h; path = sender/uploader.h; sourceTree = ""; }; + 16C7C917147D45AE00776EAD /* BreakpadDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BreakpadDefines.h; sourceTree = ""; }; + 16E02DB4147410D4008C604D /* uploader.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = uploader.mm; path = sender/uploader.mm; sourceTree = ""; }; + 1EEEB6211720829E00F7E689 /* simple_string_dictionary.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = simple_string_dictionary.cc; path = ../../common/simple_string_dictionary.cc; sourceTree = ""; }; + 1EEEB6221720829E00F7E689 /* simple_string_dictionary.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = simple_string_dictionary.h; path = ../../common/simple_string_dictionary.h; sourceTree = ""; }; + 1EEEB6251720830600F7E689 /* simple_string_dictionary_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = simple_string_dictionary_unittest.cc; path = ../../common/simple_string_dictionary_unittest.cc; sourceTree = ""; }; + 32DBCF5E0370ADEE00C91783 /* Breakpad_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Breakpad_Prefix.pch; path = Framework/Breakpad_Prefix.pch; sourceTree = ""; }; + 3329D4EC0FA16D820007BBC5 /* Breakpad.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Breakpad.xib; path = sender/Breakpad.xib; sourceTree = ""; }; + 33880C7F0F9E097100817F82 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = sender/English.lproj/InfoPlist.strings; sourceTree = ""; }; + 4084699C0F5D9CF900FDCA37 /* crash_report_sender.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; name = crash_report_sender.icns; path = sender/crash_report_sender.icns; sourceTree = ""; }; + 421BC5AD21110C0300B8042E /* convert_old_arm64_context.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = convert_old_arm64_context.cc; path = ../../processor/convert_old_arm64_context.cc; sourceTree = ""; }; + 421BC5BB21110C0300B8042E /* convert_old_arm64_context.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = convert_old_arm64_context.h; path = ../../processor/convert_old_arm64_context.h; sourceTree = ""; }; + 4D61A25D14F43CFC002D5862 /* bootstrap_compat.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bootstrap_compat.cc; path = ../../common/mac/bootstrap_compat.cc; sourceTree = SOURCE_ROOT; }; + 4D61A25E14F43CFC002D5862 /* bootstrap_compat.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = bootstrap_compat.h; path = ../../common/mac/bootstrap_compat.h; sourceTree = SOURCE_ROOT; }; + 4D72CA0D13DFAD5C006CABE3 /* md5.cc */ = {isa = PBXFileReference; explicitFileType = sourcecode.cpp.cpp; fileEncoding = 4; name = md5.cc; path = ../../common/md5.cc; sourceTree = SOURCE_ROOT; }; + 4DBE4769134A4F080072546A /* CoreServices.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreServices.framework; path = System/Library/Frameworks/CoreServices.framework; sourceTree = SDKROOT; }; + 8B31007011F0CD3C00FCF3E4 /* GTMDefines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTMDefines.h; path = ../../common/mac/GTMDefines.h; sourceTree = SOURCE_ROOT; }; + 8B3101E911F0CDE300FCF3E4 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; }; + 8B31027711F0D3AF00FCF3E4 /* BreakpadDebug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = BreakpadDebug.xcconfig; path = ../../common/mac/BreakpadDebug.xcconfig; sourceTree = SOURCE_ROOT; }; + 8B31027811F0D3AF00FCF3E4 /* BreakpadRelease.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = BreakpadRelease.xcconfig; path = ../../common/mac/BreakpadRelease.xcconfig; sourceTree = SOURCE_ROOT; }; + 8B31FFF611F0C90500FCF3E4 /* Breakpad.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Breakpad.xcconfig; path = ../../common/mac/Breakpad.xcconfig; sourceTree = SOURCE_ROOT; }; + 8DC2EF5B0486A6940098B216 /* Breakpad.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Breakpad.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D23F4B2C12A7E13200686C8D /* minidump_generator_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_generator_test.cc; path = tests/minidump_generator_test.cc; sourceTree = ""; }; + D23F4B9A12A8688800686C8D /* minidump_generator_test_helper.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_generator_test_helper.cc; path = tests/minidump_generator_test_helper.cc; sourceTree = ""; }; + D23F4BAB12A868A500686C8D /* minidump_generator_test_helper */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = minidump_generator_test_helper; sourceTree = BUILT_PRODUCTS_DIR; }; + D244534F12426E98009BBCE0 /* basic_code_modules.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = basic_code_modules.cc; path = ../../processor/basic_code_modules.cc; sourceTree = SOURCE_ROOT; }; + D244535112426EBB009BBCE0 /* logging.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = logging.cc; path = ../../processor/logging.cc; sourceTree = SOURCE_ROOT; }; + D244535212426EBB009BBCE0 /* minidump.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump.cc; path = ../../processor/minidump.cc; sourceTree = SOURCE_ROOT; }; + D244535312426EBB009BBCE0 /* pathname_stripper.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = pathname_stripper.cc; path = ../../processor/pathname_stripper.cc; sourceTree = SOURCE_ROOT; }; + D244540A12439BA0009BBCE0 /* memory_allocator_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = memory_allocator_unittest.cc; path = ../../common/memory_allocator_unittest.cc; sourceTree = SOURCE_ROOT; }; + D2F9A3D41212F87C002747C1 /* exception_handler_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = exception_handler_test.cc; path = tests/exception_handler_test.cc; sourceTree = ""; }; + D2F9A41512131EF0002747C1 /* libgtest.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libgtest.a; sourceTree = BUILT_PRODUCTS_DIR; }; + D2F9A43C12131F55002747C1 /* gmock-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gmock-all.cc"; path = "../../testing/googlemock/src/gmock-all.cc"; sourceTree = SOURCE_ROOT; }; + D2F9A43E12131F65002747C1 /* gtest_main.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = gtest_main.cc; path = ../../testing/googletest/src/gtest_main.cc; sourceTree = ""; }; + D2F9A43F12131F65002747C1 /* gtest-all.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = "gtest-all.cc"; path = "../../testing/googletest/src/gtest-all.cc"; sourceTree = ""; }; + D2F9A4C4121336C7002747C1 /* client_info.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = client_info.h; path = crash_generation/client_info.h; sourceTree = ""; }; + D2F9A4C5121336C7002747C1 /* crash_generation_client.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = crash_generation_client.h; path = crash_generation/crash_generation_client.h; sourceTree = ""; }; + D2F9A4C6121336C7002747C1 /* crash_generation_client.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = crash_generation_client.cc; path = crash_generation/crash_generation_client.cc; sourceTree = ""; }; + D2F9A4C7121336C7002747C1 /* crash_generation_server.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = crash_generation_server.h; path = crash_generation/crash_generation_server.h; sourceTree = ""; }; + D2F9A4C8121336C7002747C1 /* crash_generation_server.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = crash_generation_server.cc; path = crash_generation/crash_generation_server.cc; sourceTree = ""; }; + D2F9A4CE121336F7002747C1 /* crash_generation_server_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = crash_generation_server_test.cc; path = tests/crash_generation_server_test.cc; sourceTree = ""; }; + D2F9A546121383A1002747C1 /* crash_generation_server_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = crash_generation_server_test; sourceTree = BUILT_PRODUCTS_DIR; }; + DE43467411C72855004F095F /* da */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = da; path = sender/da.lproj/Localizable.strings; sourceTree = ""; }; + DE43467511C72857004F095F /* de */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = de; path = sender/de.lproj/Localizable.strings; sourceTree = ""; }; + DE43467611C7285B004F095F /* es */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = es; path = sender/es.lproj/Localizable.strings; sourceTree = ""; }; + DE43467711C72862004F095F /* fr */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = fr; path = sender/fr.lproj/Localizable.strings; sourceTree = ""; }; + DE43467811C72869004F095F /* it */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = it; path = sender/it.lproj/Localizable.strings; sourceTree = ""; }; + DE43467911C7286D004F095F /* nl */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = nl; path = sender/nl.lproj/Localizable.strings; sourceTree = ""; }; + DE43467A11C72873004F095F /* no */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = no; path = sender/no.lproj/Localizable.strings; sourceTree = ""; }; + DE43467B11C72877004F095F /* sl */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = sl; path = sender/sl.lproj/Localizable.strings; sourceTree = ""; }; + DE43467C11C7287A004F095F /* sv */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = sv; path = sender/sv.lproj/Localizable.strings; sourceTree = ""; }; + DE43467E11C728DC004F095F /* ja */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = ja; path = sender/ja.lproj/Localizable.strings; sourceTree = ""; }; + DE43467F11C728E1004F095F /* tr */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = tr; path = sender/tr.lproj/Localizable.strings; sourceTree = ""; }; + DE43468611C72958004F095F /* de */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = de; path = sender/de.lproj/InfoPlist.strings; sourceTree = ""; }; + DE43468711C7295D004F095F /* da */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = da; path = sender/da.lproj/InfoPlist.strings; sourceTree = ""; }; + DE43468811C7295F004F095F /* es */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = es; path = sender/es.lproj/InfoPlist.strings; sourceTree = ""; }; + DE43468911C72964004F095F /* fr */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = fr; path = sender/fr.lproj/InfoPlist.strings; sourceTree = ""; }; + DE43468A11C72967004F095F /* it */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = it; path = sender/it.lproj/InfoPlist.strings; sourceTree = ""; }; + DE43468B11C7296B004F095F /* ja */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = ja; path = sender/ja.lproj/InfoPlist.strings; sourceTree = ""; }; + DE43468C11C7296D004F095F /* nl */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = nl; path = sender/nl.lproj/InfoPlist.strings; sourceTree = ""; }; + DE43468D11C7296F004F095F /* no */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = no; path = sender/no.lproj/InfoPlist.strings; sourceTree = ""; }; + DE43468E11C72971004F095F /* sl */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = sl; path = sender/sl.lproj/InfoPlist.strings; sourceTree = ""; }; + DE43468F11C72973004F095F /* sv */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = sv; path = sender/sv.lproj/InfoPlist.strings; sourceTree = ""; }; + DE43469011C72976004F095F /* tr */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = tr; path = sender/tr.lproj/InfoPlist.strings; sourceTree = ""; }; + EB9CF89F24F01E1D00F9B6D1 /* encoding_util.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = encoding_util.m; path = ../../common/mac/encoding_util.m; sourceTree = ""; }; + EB9CF8AD24F01E1D00F9B6D1 /* minidump_upload.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = minidump_upload.m; path = ../../common/mac/minidump_upload.m; sourceTree = ""; }; + EB9CF8AE24F01E1D00F9B6D1 /* encoding_util.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = encoding_util.h; path = ../../common/mac/encoding_util.h; sourceTree = ""; }; + EB9CF8AF24F01E1D00F9B6D1 /* HTTPSimplePostRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HTTPSimplePostRequest.h; path = ../../common/mac/HTTPSimplePostRequest.h; sourceTree = ""; }; + EB9CF8B024F01E1D00F9B6D1 /* HTTPRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HTTPRequest.h; path = ../../common/mac/HTTPRequest.h; sourceTree = ""; }; + EB9CF8B124F01E1D00F9B6D1 /* HTTPPutRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HTTPPutRequest.m; path = ../../common/mac/HTTPPutRequest.m; sourceTree = ""; }; + EB9CF8B224F01E1D00F9B6D1 /* HTTPRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HTTPRequest.m; path = ../../common/mac/HTTPRequest.m; sourceTree = ""; }; + EB9CF8B324F01E1D00F9B6D1 /* SymbolCollectorClient.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = SymbolCollectorClient.m; path = ../../common/mac/SymbolCollectorClient.m; sourceTree = ""; }; + EB9CF8B424F01E1D00F9B6D1 /* HTTPGetRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HTTPGetRequest.h; path = ../../common/mac/HTTPGetRequest.h; sourceTree = ""; }; + EB9CF8B524F01E1D00F9B6D1 /* HTTPGetRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HTTPGetRequest.m; path = ../../common/mac/HTTPGetRequest.m; sourceTree = ""; }; + EB9CF8B624F01E1D00F9B6D1 /* HTTPSimplePostRequest.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HTTPSimplePostRequest.m; path = ../../common/mac/HTTPSimplePostRequest.m; sourceTree = ""; }; + EB9CF8B724F01E1D00F9B6D1 /* SymbolCollectorClient.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SymbolCollectorClient.h; path = ../../common/mac/SymbolCollectorClient.h; sourceTree = ""; }; + EB9CF8B824F01E1D00F9B6D1 /* HTTPPutRequest.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HTTPPutRequest.h; path = ../../common/mac/HTTPPutRequest.h; sourceTree = ""; }; + F4DAB1DB19F1027100A5A838 /* launch_reporter.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = launch_reporter.cc; path = ../../common/mac/launch_reporter.cc; sourceTree = SOURCE_ROOT; }; + F4DAB1DC19F1027100A5A838 /* launch_reporter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = launch_reporter.h; path = ../../common/mac/launch_reporter.h; sourceTree = SOURCE_ROOT; }; + F91AF5CF0FD60393009D8BE2 /* BreakpadFramework_Test.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = BreakpadFramework_Test.mm; path = tests/BreakpadFramework_Test.mm; sourceTree = ""; }; + F9286B380F7EB25800A4DCC8 /* Inspector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Inspector.h; path = crash_generation/Inspector.h; sourceTree = ""; }; + F9286B390F7EB25800A4DCC8 /* InspectorMain.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = InspectorMain.mm; path = crash_generation/InspectorMain.mm; sourceTree = ""; }; + F92C53540ECCE349009BE4BA /* Inspector */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = Inspector; sourceTree = BUILT_PRODUCTS_DIR; }; + F92C53670ECCE3FD009BE4BA /* breakpad_exc_server.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = breakpad_exc_server.c; path = handler/breakpad_exc_server.c; sourceTree = SOURCE_ROOT; }; + F92C53680ECCE3FD009BE4BA /* breakpad_exc_server.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = breakpad_exc_server.h; path = handler/breakpad_exc_server.h; sourceTree = SOURCE_ROOT; }; + F92C53690ECCE3FD009BE4BA /* breakpad_nlist_64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = breakpad_nlist_64.cc; path = handler/breakpad_nlist_64.cc; sourceTree = SOURCE_ROOT; }; + F92C536A0ECCE3FD009BE4BA /* breakpad_nlist_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = breakpad_nlist_64.h; path = handler/breakpad_nlist_64.h; sourceTree = SOURCE_ROOT; }; + F92C536B0ECCE3FD009BE4BA /* dynamic_images.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dynamic_images.cc; path = handler/dynamic_images.cc; sourceTree = SOURCE_ROOT; }; + F92C536C0ECCE3FD009BE4BA /* dynamic_images.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dynamic_images.h; path = handler/dynamic_images.h; sourceTree = SOURCE_ROOT; }; + F92C536D0ECCE3FD009BE4BA /* exception_handler.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = exception_handler.cc; path = handler/exception_handler.cc; sourceTree = SOURCE_ROOT; }; + F92C536E0ECCE3FD009BE4BA /* exception_handler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = exception_handler.h; path = handler/exception_handler.h; sourceTree = SOURCE_ROOT; }; + F92C536F0ECCE3FD009BE4BA /* minidump_generator.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_generator.cc; path = handler/minidump_generator.cc; sourceTree = SOURCE_ROOT; }; + F92C53700ECCE3FD009BE4BA /* minidump_generator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = minidump_generator.h; path = handler/minidump_generator.h; sourceTree = SOURCE_ROOT; }; + F92C53720ECCE3FD009BE4BA /* protected_memory_allocator.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = protected_memory_allocator.cc; path = handler/protected_memory_allocator.cc; sourceTree = SOURCE_ROOT; }; + F92C53730ECCE3FD009BE4BA /* protected_memory_allocator.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = protected_memory_allocator.h; path = handler/protected_memory_allocator.h; sourceTree = SOURCE_ROOT; }; + F92C53740ECCE635009BE4BA /* file_id.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = file_id.cc; path = ../../common/mac/file_id.cc; sourceTree = SOURCE_ROOT; }; + F92C53750ECCE635009BE4BA /* file_id.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = file_id.h; path = ../../common/mac/file_id.h; sourceTree = SOURCE_ROOT; }; + F92C53760ECCE635009BE4BA /* HTTPMultipartUpload.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = HTTPMultipartUpload.h; path = ../../common/mac/HTTPMultipartUpload.h; sourceTree = SOURCE_ROOT; }; + F92C53770ECCE635009BE4BA /* HTTPMultipartUpload.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = HTTPMultipartUpload.m; path = ../../common/mac/HTTPMultipartUpload.m; sourceTree = SOURCE_ROOT; }; + F92C53780ECCE635009BE4BA /* MachIPC.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = MachIPC.h; path = ../../common/mac/MachIPC.h; sourceTree = SOURCE_ROOT; }; + F92C53790ECCE635009BE4BA /* MachIPC.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = MachIPC.mm; path = ../../common/mac/MachIPC.mm; sourceTree = SOURCE_ROOT; }; + F92C537A0ECCE635009BE4BA /* macho_id.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = macho_id.cc; path = ../../common/mac/macho_id.cc; sourceTree = SOURCE_ROOT; }; + F92C537B0ECCE635009BE4BA /* macho_id.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = macho_id.h; path = ../../common/mac/macho_id.h; sourceTree = SOURCE_ROOT; }; + F92C537C0ECCE635009BE4BA /* macho_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = macho_utilities.cc; path = ../../common/mac/macho_utilities.cc; sourceTree = SOURCE_ROOT; }; + F92C537D0ECCE635009BE4BA /* macho_utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = macho_utilities.h; path = ../../common/mac/macho_utilities.h; sourceTree = SOURCE_ROOT; }; + F92C537E0ECCE635009BE4BA /* macho_walker.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = macho_walker.cc; path = ../../common/mac/macho_walker.cc; sourceTree = SOURCE_ROOT; }; + F92C537F0ECCE635009BE4BA /* macho_walker.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = macho_walker.h; path = ../../common/mac/macho_walker.h; sourceTree = SOURCE_ROOT; }; + F92C53820ECCE635009BE4BA /* string_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = string_utilities.cc; path = ../../common/mac/string_utilities.cc; sourceTree = SOURCE_ROOT; }; + F92C53830ECCE635009BE4BA /* string_utilities.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = string_utilities.h; path = ../../common/mac/string_utilities.h; sourceTree = SOURCE_ROOT; }; + F92C53850ECCE6AD009BE4BA /* string_conversion.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = string_conversion.cc; path = ../../common/string_conversion.cc; sourceTree = SOURCE_ROOT; }; + F92C53860ECCE6AD009BE4BA /* string_conversion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = string_conversion.h; path = ../../common/string_conversion.h; sourceTree = SOURCE_ROOT; }; + F92C53870ECCE6C0009BE4BA /* convert_UTF.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = convert_UTF.cc; path = ../../common/convert_UTF.cc; sourceTree = SOURCE_ROOT; }; + F92C53880ECCE6C0009BE4BA /* convert_UTF.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = convert_UTF.h; path = ../../common/convert_UTF.h; sourceTree = SOURCE_ROOT; }; + F92C538E0ECCE70A009BE4BA /* minidump_file_writer-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "minidump_file_writer-inl.h"; path = "../minidump_file_writer-inl.h"; sourceTree = SOURCE_ROOT; }; + F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_file_writer.cc; path = ../minidump_file_writer.cc; sourceTree = SOURCE_ROOT; }; + F92C53900ECCE70A009BE4BA /* minidump_file_writer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = minidump_file_writer.h; path = ../minidump_file_writer.h; sourceTree = SOURCE_ROOT; }; + F92C53B70ECCE7B3009BE4BA /* Inspector.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Inspector.mm; path = crash_generation/Inspector.mm; sourceTree = SOURCE_ROOT; }; + F92C554A0ECCF530009BE4BA /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; }; + F92C55CE0ECD0064009BE4BA /* Breakpad.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Breakpad.h; path = Framework/Breakpad.h; sourceTree = ""; }; + F92C55CF0ECD0064009BE4BA /* Breakpad.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Breakpad.mm; path = Framework/Breakpad.mm; sourceTree = ""; }; + F92C56310ECD0DF1009BE4BA /* OnDemandServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OnDemandServer.h; path = Framework/OnDemandServer.h; sourceTree = ""; }; + F92C56320ECD0DF1009BE4BA /* OnDemandServer.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = OnDemandServer.mm; path = Framework/OnDemandServer.mm; sourceTree = ""; }; + F92C563C0ECD10B3009BE4BA /* breakpadUtilities.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = breakpadUtilities.dylib; sourceTree = BUILT_PRODUCTS_DIR; }; + F92C56A00ECE04A7009BE4BA /* crash_report_sender.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = crash_report_sender.app; sourceTree = BUILT_PRODUCTS_DIR; }; + F92C56A20ECE04A7009BE4BA /* crash_report_sender-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = "crash_report_sender-Info.plist"; path = "sender/crash_report_sender-Info.plist"; sourceTree = ""; }; + F92C56A70ECE04C5009BE4BA /* crash_report_sender.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = crash_report_sender.h; path = sender/crash_report_sender.h; sourceTree = ""; }; + F92C56A80ECE04C5009BE4BA /* crash_report_sender.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = crash_report_sender.m; path = sender/crash_report_sender.m; sourceTree = ""; }; + F93803BE0F80820F004D428B /* generator_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = generator_test; sourceTree = BUILT_PRODUCTS_DIR; }; + F93DE2D10F82A67300608B94 /* minidump_file_writer_unittest */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = minidump_file_writer_unittest; sourceTree = BUILT_PRODUCTS_DIR; }; + F93DE2D70F82A70E00608B94 /* minidump_file_writer_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_file_writer_unittest.cc; path = ../minidump_file_writer_unittest.cc; sourceTree = SOURCE_ROOT; }; + F93DE32C0F82C55600608B94 /* handler_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = handler_test; sourceTree = BUILT_PRODUCTS_DIR; }; + F945849D0F280E3C009A47BF /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = sender/English.lproj/Localizable.strings; sourceTree = ""; }; + F945859D0F78241E009A47BF /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = Framework/Info.plist; sourceTree = ""; }; + F95BB87C101F949F00AA053B /* crash_report.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = crash_report.xcodeproj; path = ../../tools/mac/crash_report/crash_report.xcodeproj; sourceTree = SOURCE_ROOT; }; + F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = dump_syms.xcodeproj; path = ../../tools/mac/dump_syms/dump_syms.xcodeproj; sourceTree = SOURCE_ROOT; }; + F95BB894101F94C000AA053B /* symupload.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = symupload.xcodeproj; path = ../../tools/mac/symupload/symupload.xcodeproj; sourceTree = SOURCE_ROOT; }; + F9B6309F100FF96B00D0F4AC /* goArrow.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; name = goArrow.png; path = sender/goArrow.png; sourceTree = ""; }; + F9C44DA50EF060A8003AEBAA /* BreakpadTest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = BreakpadTest.app; sourceTree = BUILT_PRODUCTS_DIR; }; + F9C44DAC0EF07288003AEBAA /* Controller.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = Controller.m; path = testapp/Controller.m; sourceTree = ""; }; + F9C44DAD0EF07288003AEBAA /* crashduringload */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = crashduringload; path = testapp/crashduringload; sourceTree = ""; }; + F9C44DAE0EF07288003AEBAA /* crashInMain */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; name = crashInMain; path = testapp/crashInMain; sourceTree = ""; }; + F9C44DAF0EF07288003AEBAA /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = testapp/Info.plist; sourceTree = ""; }; + F9C44DB00EF07288003AEBAA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = testapp/main.m; sourceTree = ""; }; + F9C44DB10EF07288003AEBAA /* TestClass.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = TestClass.mm; path = testapp/TestClass.mm; sourceTree = ""; }; + F9C44DB90EF072A0003AEBAA /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = testapp/English.lproj/InfoPlist.strings; sourceTree = ""; }; + F9C44DBB0EF072A0003AEBAA /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = testapp/English.lproj/MainMenu.xib; sourceTree = ""; }; + F9C44DBF0EF0778F003AEBAA /* Controller.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Controller.h; path = testapp/Controller.h; sourceTree = ""; }; + F9C44DC00EF0778F003AEBAA /* TestClass.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = TestClass.h; path = testapp/TestClass.h; sourceTree = ""; }; + F9C44EE40EF0A006003AEBAA /* SystemConfiguration.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SystemConfiguration.framework; path = System/Library/Frameworks/SystemConfiguration.framework; sourceTree = SDKROOT; }; + F9C44EE70EF0A3C1003AEBAA /* GTMLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTMLogger.h; path = ../../common/mac/GTMLogger.h; sourceTree = SOURCE_ROOT; }; + F9C44EE80EF0A3C1003AEBAA /* GTMLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTMLogger.m; path = ../../common/mac/GTMLogger.m; sourceTree = SOURCE_ROOT; }; + F9C77DDA0F7DD5CF0045F7DB /* UnitTests.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = UnitTests.octest; sourceTree = BUILT_PRODUCTS_DIR; }; + F9C77DDB0F7DD5CF0045F7DB /* UnitTests-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "UnitTests-Info.plist"; sourceTree = ""; }; + F9C77E110F7DDF810045F7DB /* GTMSenTestCase.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = GTMSenTestCase.h; path = ../../common/mac/testing/GTMSenTestCase.h; sourceTree = SOURCE_ROOT; }; + F9C77E120F7DDF810045F7DB /* GTMSenTestCase.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = GTMSenTestCase.m; path = ../../common/mac/testing/GTMSenTestCase.m; sourceTree = SOURCE_ROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8DC2EF560486A6940098B216 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F92C565C0ECD1158009BE4BA /* breakpadUtilities.dylib in Frameworks */, + 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D23F4BA912A868A500686C8D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D246418412BAA4BA005170D0 /* Foundation.framework in Frameworks */, + 4DBE49A6134A4F200072546A /* CoreServices.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D2F9A41312131EF0002747C1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D2F9A53E121383A1002747C1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D2F9A53F121383A1002747C1 /* Foundation.framework in Frameworks */, + D2F9A541121383A1002747C1 /* libgtest.a in Frameworks */, + 4DBE49A9134A4F460072546A /* CoreServices.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F92C53520ECCE349009BE4BA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F92C564C0ECD10DD009BE4BA /* breakpadUtilities.dylib in Frameworks */, + F92C554C0ECCF534009BE4BA /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F92C563A0ECD10B3009BE4BA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8B31FC8211EFD2B800FCF3E4 /* Foundation.framework in Frameworks */, + F92C56570ECD113E009BE4BA /* Carbon.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F92C569E0ECE04A7009BE4BA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F9C44EE50EF0A006003AEBAA /* SystemConfiguration.framework in Frameworks */, + 8B3101C611F0CD9F00FCF3E4 /* AppKit.framework in Frameworks */, + 8B3101C711F0CD9F00FCF3E4 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F93803BC0F80820F004D428B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8B31029411F0D54300FCF3E4 /* Foundation.framework in Frameworks */, + D23F4B3312A7E17700686C8D /* libgtest.a in Frameworks */, + 4DBE49A7134A4F280072546A /* CoreServices.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F93DE2CF0F82A67300608B94 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F93DE32A0F82C55600608B94 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8B3102E611F0D74C00FCF3E4 /* Foundation.framework in Frameworks */, + D2F9A44412131F84002747C1 /* libgtest.a in Frameworks */, + 4DBE49A8134A4F380072546A /* CoreServices.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9C44DA30EF060A8003AEBAA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F9C44E000EF077CD003AEBAA /* Breakpad.framework in Frameworks */, + 8B3101CA11F0CDB000FCF3E4 /* AppKit.framework in Frameworks */, + 8B3101CB11F0CDB000FCF3E4 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9C77DD70F7DD5CF0045F7DB /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F91AF6210FD60784009D8BE2 /* Breakpad.framework in Frameworks */, + 8B3101EA11F0CDE300FCF3E4 /* SenTestingKit.framework in Frameworks */, + 8B3102EB11F0D78000FCF3E4 /* Foundation.framework in Frameworks */, + D24BBBFD121050F000F3D417 /* breakpadUtilities.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 034768DFFF38A50411DB9C8B /* Products */ = { + isa = PBXGroup; + children = ( + 8DC2EF5B0486A6940098B216 /* Breakpad.framework */, + F92C53540ECCE349009BE4BA /* Inspector */, + F92C563C0ECD10B3009BE4BA /* breakpadUtilities.dylib */, + F92C56A00ECE04A7009BE4BA /* crash_report_sender.app */, + F9C44DA50EF060A8003AEBAA /* BreakpadTest.app */, + F9C77DDA0F7DD5CF0045F7DB /* UnitTests.octest */, + F93803BE0F80820F004D428B /* generator_test */, + F93DE2D10F82A67300608B94 /* minidump_file_writer_unittest */, + F93DE32C0F82C55600608B94 /* handler_test */, + D2F9A41512131EF0002747C1 /* libgtest.a */, + D2F9A546121383A1002747C1 /* crash_generation_server_test */, + D23F4BAB12A868A500686C8D /* minidump_generator_test_helper */, + ); + name = Products; + sourceTree = ""; + }; + 0867D691FE84028FC02AAC07 /* Breakpad */ = { + isa = PBXGroup; + children = ( + D2F9A43812131F3B002747C1 /* gtest */, + 8B31FFF611F0C90500FCF3E4 /* Breakpad.xcconfig */, + 8B31027711F0D3AF00FCF3E4 /* BreakpadDebug.xcconfig */, + 8B31027811F0D3AF00FCF3E4 /* BreakpadRelease.xcconfig */, + F95BB8A3101F94C300AA053B /* Tools */, + 32DBCF5E0370ADEE00C91783 /* Breakpad_Prefix.pch */, + F92C538D0ECCE6F2009BE4BA /* client */, + F92C53600ECCE3D6009BE4BA /* common */, + D244536912426EE7009BBCE0 /* processor */, + 0867D69AFE84028FC02AAC07 /* Frameworks */, + 034768DFFF38A50411DB9C8B /* Products */, + F9C77DDB0F7DD5CF0045F7DB /* UnitTests-Info.plist */, + ); + name = Breakpad; + sourceTree = ""; + }; + 0867D69AFE84028FC02AAC07 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 8B3101E911F0CDE300FCF3E4 /* SenTestingKit.framework */, + F9C44EE40EF0A006003AEBAA /* SystemConfiguration.framework */, + F92C554A0ECCF530009BE4BA /* Carbon.framework */, + 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */, + 0867D6A5FE840307C02AAC07 /* AppKit.framework */, + 0867D69BFE84028FC02AAC07 /* Foundation.framework */, + 4DBE4769134A4F080072546A /* CoreServices.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 16C7C915147D45AE00776EAD /* apple */ = { + isa = PBXGroup; + children = ( + 16C7C916147D45AE00776EAD /* Framework */, + ); + name = apple; + path = ../apple; + sourceTree = SOURCE_ROOT; + }; + 16C7C916147D45AE00776EAD /* Framework */ = { + isa = PBXGroup; + children = ( + 16C7C917147D45AE00776EAD /* BreakpadDefines.h */, + ); + path = Framework; + sourceTree = ""; + }; + D244536912426EE7009BBCE0 /* processor */ = { + isa = PBXGroup; + children = ( + 421BC5AD21110C0300B8042E /* convert_old_arm64_context.cc */, + 421BC5BB21110C0300B8042E /* convert_old_arm64_context.h */, + D244535112426EBB009BBCE0 /* logging.cc */, + D244535212426EBB009BBCE0 /* minidump.cc */, + D244535312426EBB009BBCE0 /* pathname_stripper.cc */, + D244534F12426E98009BBCE0 /* basic_code_modules.cc */, + ); + name = processor; + sourceTree = ""; + }; + D2F9A43812131F3B002747C1 /* gtest */ = { + isa = PBXGroup; + children = ( + D2F9A43E12131F65002747C1 /* gtest_main.cc */, + D2F9A43F12131F65002747C1 /* gtest-all.cc */, + D2F9A43C12131F55002747C1 /* gmock-all.cc */, + ); + name = gtest; + sourceTree = ""; + }; + F92C53590ECCE3BB009BE4BA /* handler */ = { + isa = PBXGroup; + children = ( + F92C53670ECCE3FD009BE4BA /* breakpad_exc_server.c */, + F92C53680ECCE3FD009BE4BA /* breakpad_exc_server.h */, + F92C53690ECCE3FD009BE4BA /* breakpad_nlist_64.cc */, + F92C536A0ECCE3FD009BE4BA /* breakpad_nlist_64.h */, + F92C536B0ECCE3FD009BE4BA /* dynamic_images.cc */, + F92C536C0ECCE3FD009BE4BA /* dynamic_images.h */, + F92C536D0ECCE3FD009BE4BA /* exception_handler.cc */, + F92C536E0ECCE3FD009BE4BA /* exception_handler.h */, + F92C536F0ECCE3FD009BE4BA /* minidump_generator.cc */, + F92C53700ECCE3FD009BE4BA /* minidump_generator.h */, + F92C53720ECCE3FD009BE4BA /* protected_memory_allocator.cc */, + F92C53730ECCE3FD009BE4BA /* protected_memory_allocator.h */, + ); + name = handler; + sourceTree = ""; + }; + F92C53600ECCE3D6009BE4BA /* common */ = { + isa = PBXGroup; + children = ( + D244540A12439BA0009BBCE0 /* memory_allocator_unittest.cc */, + F92C53870ECCE6C0009BE4BA /* convert_UTF.cc */, + F92C53880ECCE6C0009BE4BA /* convert_UTF.h */, + 4D72CA0D13DFAD5C006CABE3 /* md5.cc */, + 1EEEB6211720829E00F7E689 /* simple_string_dictionary.cc */, + 1EEEB6221720829E00F7E689 /* simple_string_dictionary.h */, + F92C53850ECCE6AD009BE4BA /* string_conversion.cc */, + F92C53860ECCE6AD009BE4BA /* string_conversion.h */, + F92C53840ECCE68D009BE4BA /* mac */, + ); + name = common; + sourceTree = ""; + }; + F92C53840ECCE68D009BE4BA /* mac */ = { + isa = PBXGroup; + children = ( + EB9CF8AE24F01E1D00F9B6D1 /* encoding_util.h */, + EB9CF89F24F01E1D00F9B6D1 /* encoding_util.m */, + EB9CF8B424F01E1D00F9B6D1 /* HTTPGetRequest.h */, + EB9CF8B524F01E1D00F9B6D1 /* HTTPGetRequest.m */, + EB9CF8B824F01E1D00F9B6D1 /* HTTPPutRequest.h */, + EB9CF8B124F01E1D00F9B6D1 /* HTTPPutRequest.m */, + EB9CF8B024F01E1D00F9B6D1 /* HTTPRequest.h */, + EB9CF8B224F01E1D00F9B6D1 /* HTTPRequest.m */, + EB9CF8AF24F01E1D00F9B6D1 /* HTTPSimplePostRequest.h */, + EB9CF8B624F01E1D00F9B6D1 /* HTTPSimplePostRequest.m */, + EB9CF8AD24F01E1D00F9B6D1 /* minidump_upload.m */, + EB9CF8B724F01E1D00F9B6D1 /* SymbolCollectorClient.h */, + EB9CF8B324F01E1D00F9B6D1 /* SymbolCollectorClient.m */, + 162F64F0161C577500CD68D5 /* arch_utilities.cc */, + 162F64F1161C577500CD68D5 /* arch_utilities.h */, + 8B31007011F0CD3C00FCF3E4 /* GTMDefines.h */, + F9C77E0F0F7DDF650045F7DB /* testing */, + F9C44EE70EF0A3C1003AEBAA /* GTMLogger.h */, + F9C44EE80EF0A3C1003AEBAA /* GTMLogger.m */, + F92C53740ECCE635009BE4BA /* file_id.cc */, + F92C53750ECCE635009BE4BA /* file_id.h */, + F92C53760ECCE635009BE4BA /* HTTPMultipartUpload.h */, + F92C53770ECCE635009BE4BA /* HTTPMultipartUpload.m */, + F4DAB1DB19F1027100A5A838 /* launch_reporter.cc */, + F4DAB1DC19F1027100A5A838 /* launch_reporter.h */, + F92C53780ECCE635009BE4BA /* MachIPC.h */, + F92C53790ECCE635009BE4BA /* MachIPC.mm */, + 4D61A25D14F43CFC002D5862 /* bootstrap_compat.cc */, + 4D61A25E14F43CFC002D5862 /* bootstrap_compat.h */, + F92C537A0ECCE635009BE4BA /* macho_id.cc */, + F92C537B0ECCE635009BE4BA /* macho_id.h */, + F92C537C0ECCE635009BE4BA /* macho_utilities.cc */, + F92C537D0ECCE635009BE4BA /* macho_utilities.h */, + F92C537E0ECCE635009BE4BA /* macho_walker.cc */, + F92C537F0ECCE635009BE4BA /* macho_walker.h */, + F92C53820ECCE635009BE4BA /* string_utilities.cc */, + F92C53830ECCE635009BE4BA /* string_utilities.h */, + ); + name = mac; + sourceTree = ""; + }; + F92C538D0ECCE6F2009BE4BA /* client */ = { + isa = PBXGroup; + children = ( + 16C7C915147D45AE00776EAD /* apple */, + F92C53990ECCE78E009BE4BA /* mac */, + F92C538E0ECCE70A009BE4BA /* minidump_file_writer-inl.h */, + F92C538F0ECCE70A009BE4BA /* minidump_file_writer.cc */, + F92C53900ECCE70A009BE4BA /* minidump_file_writer.h */, + F93DE2D70F82A70E00608B94 /* minidump_file_writer_unittest.cc */, + ); + name = client; + sourceTree = ""; + }; + F92C53990ECCE78E009BE4BA /* mac */ = { + isa = PBXGroup; + children = ( + F9C77DDF0F7DD7CF0045F7DB /* tests */, + F9C44DAB0EF0726F003AEBAA /* testapp */, + F92C56A60ECE04B6009BE4BA /* sender */, + F92C55CD0ECD0053009BE4BA /* Framework */, + F92C53B50ECCE799009BE4BA /* crash_generation */, + F92C53590ECCE3BB009BE4BA /* handler */, + ); + name = mac; + sourceTree = ""; + }; + F92C53B50ECCE799009BE4BA /* crash_generation */ = { + isa = PBXGroup; + children = ( + 163201D41443019E00C4DBF5 /* ConfigFile.h */, + 163201D51443019E00C4DBF5 /* ConfigFile.mm */, + D2F9A4C4121336C7002747C1 /* client_info.h */, + D2F9A4C5121336C7002747C1 /* crash_generation_client.h */, + D2F9A4C6121336C7002747C1 /* crash_generation_client.cc */, + D2F9A4C7121336C7002747C1 /* crash_generation_server.h */, + D2F9A4C8121336C7002747C1 /* crash_generation_server.cc */, + F9286B380F7EB25800A4DCC8 /* Inspector.h */, + F9286B390F7EB25800A4DCC8 /* InspectorMain.mm */, + F92C53B70ECCE7B3009BE4BA /* Inspector.mm */, + ); + name = crash_generation; + sourceTree = ""; + }; + F92C55CD0ECD0053009BE4BA /* Framework */ = { + isa = PBXGroup; + children = ( + F945859D0F78241E009A47BF /* Info.plist */, + F92C56310ECD0DF1009BE4BA /* OnDemandServer.h */, + F92C56320ECD0DF1009BE4BA /* OnDemandServer.mm */, + F92C55CE0ECD0064009BE4BA /* Breakpad.h */, + F92C55CF0ECD0064009BE4BA /* Breakpad.mm */, + ); + name = Framework; + sourceTree = ""; + }; + F92C56A60ECE04B6009BE4BA /* sender */ = { + isa = PBXGroup; + children = ( + 16E02DB4147410D4008C604D /* uploader.mm */, + 163202431443201300C4DBF5 /* uploader.h */, + F9B6309F100FF96B00D0F4AC /* goArrow.png */, + F92C56A70ECE04C5009BE4BA /* crash_report_sender.h */, + F92C56A80ECE04C5009BE4BA /* crash_report_sender.m */, + F945849C0F280E3C009A47BF /* Localizable.strings */, + 33880C7E0F9E097100817F82 /* InfoPlist.strings */, + 3329D4EC0FA16D820007BBC5 /* Breakpad.xib */, + 4084699C0F5D9CF900FDCA37 /* crash_report_sender.icns */, + F92C56A20ECE04A7009BE4BA /* crash_report_sender-Info.plist */, + ); + name = sender; + sourceTree = ""; + }; + F95BB87D101F949F00AA053B /* Products */ = { + isa = PBXGroup; + children = ( + F95BB885101F949F00AA053B /* crash_report */, + ); + name = Products; + sourceTree = ""; + }; + F95BB88A101F94AC00AA053B /* Products */ = { + isa = PBXGroup; + children = ( + F95BB892101F94AC00AA053B /* dump_syms */, + 8B31F7A111EF9A8700FCF3E4 /* macho_dump */, + 8B31F7A311EF9A8700FCF3E4 /* libgtestmockall.a */, + 8B31F7A511EF9A8700FCF3E4 /* byte_cursor_unittest */, + 8B31F7A711EF9A8700FCF3E4 /* macho_reader_unittest */, + 8B31F7A911EF9A8700FCF3E4 /* stabs_reader_unittest */, + 8B31F7AB11EF9A8700FCF3E4 /* bytereader_unittest */, + 8B31F7AD11EF9A8700FCF3E4 /* dwarf2reader_cfi_unittest */, + 8B31F7AF11EF9A8700FCF3E4 /* dwarf2diehandler_unittest */, + 8B31F7B111EF9A8700FCF3E4 /* dwarf_cu_to_module_unittest */, + 8B31F7B311EF9A8700FCF3E4 /* dwarf_line_to_module_unittest */, + 8B31F7B511EF9A8700FCF3E4 /* dwarf_cfi_to_module_unittest */, + 8B31F7B711EF9A8700FCF3E4 /* stabs_to_module_unittest */, + 8B31F7B911EF9A8700FCF3E4 /* module_unittest */, + 8B31F7BB11EF9A8700FCF3E4 /* test_assembler_unittest */, + ); + name = Products; + sourceTree = ""; + }; + F95BB895101F94C000AA053B /* Products */ = { + isa = PBXGroup; + children = ( + F95BB89F101F94C000AA053B /* symupload */, + F95BB8A1101F94C000AA053B /* minidump_upload */, + ); + name = Products; + sourceTree = ""; + }; + F95BB8A3101F94C300AA053B /* Tools */ = { + isa = PBXGroup; + children = ( + F95BB894101F94C000AA053B /* symupload.xcodeproj */, + F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */, + F95BB87C101F949F00AA053B /* crash_report.xcodeproj */, + ); + name = Tools; + sourceTree = ""; + }; + F9C44DAB0EF0726F003AEBAA /* testapp */ = { + isa = PBXGroup; + children = ( + F9C44DBF0EF0778F003AEBAA /* Controller.h */, + F9C44DC00EF0778F003AEBAA /* TestClass.h */, + F9C44DB80EF072A0003AEBAA /* InfoPlist.strings */, + F9C44DBA0EF072A0003AEBAA /* MainMenu.xib */, + F9C44DAC0EF07288003AEBAA /* Controller.m */, + F9C44DAD0EF07288003AEBAA /* crashduringload */, + F9C44DAE0EF07288003AEBAA /* crashInMain */, + F9C44DAF0EF07288003AEBAA /* Info.plist */, + F9C44DB00EF07288003AEBAA /* main.m */, + F9C44DB10EF07288003AEBAA /* TestClass.mm */, + ); + name = testapp; + sourceTree = ""; + }; + F9C77DDF0F7DD7CF0045F7DB /* tests */ = { + isa = PBXGroup; + children = ( + 1EEEB6251720830600F7E689 /* simple_string_dictionary_unittest.cc */, + D23F4B9A12A8688800686C8D /* minidump_generator_test_helper.cc */, + D23F4B2C12A7E13200686C8D /* minidump_generator_test.cc */, + D2F9A4CE121336F7002747C1 /* crash_generation_server_test.cc */, + D2F9A3D41212F87C002747C1 /* exception_handler_test.cc */, + F91AF5CF0FD60393009D8BE2 /* BreakpadFramework_Test.mm */, + ); + name = tests; + sourceTree = ""; + }; + F9C77E0F0F7DDF650045F7DB /* testing */ = { + isa = PBXGroup; + children = ( + F9C77E110F7DDF810045F7DB /* GTMSenTestCase.h */, + F9C77E120F7DDF810045F7DB /* GTMSenTestCase.m */, + ); + name = testing; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 8DC2EF500486A6940098B216 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + F92C55D00ECD0064009BE4BA /* Breakpad.h in Headers */, + EB9CF8BC24F01E1D00F9B6D1 /* HTTPSimplePostRequest.h in Headers */, + EB9CF8BB24F01E1D00F9B6D1 /* encoding_util.h in Headers */, + F92C56330ECD0DF1009BE4BA /* OnDemandServer.h in Headers */, + EB9CF8BD24F01E1D00F9B6D1 /* HTTPRequest.h in Headers */, + D2F9A4C9121336C7002747C1 /* client_info.h in Headers */, + EB9CF8C524F01E1D00F9B6D1 /* HTTPPutRequest.h in Headers */, + D2F9A4CA121336C7002747C1 /* crash_generation_client.h in Headers */, + D2F9A4CC121336C7002747C1 /* crash_generation_server.h in Headers */, + 163201D61443019E00C4DBF5 /* ConfigFile.h in Headers */, + EB9CF8C424F01E1D00F9B6D1 /* SymbolCollectorClient.h in Headers */, + 16C7C918147D45AE00776EAD /* BreakpadDefines.h in Headers */, + 421BC5BD21110C0300B8042E /* convert_old_arm64_context.h in Headers */, + EB9CF8C124F01E1D00F9B6D1 /* HTTPGetRequest.h in Headers */, + 162F64F3161C577500CD68D5 /* arch_utilities.h in Headers */, + F4DAB1DE19F1027100A5A838 /* launch_reporter.h in Headers */, + 1EEEB6241720829E00F7E689 /* simple_string_dictionary.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D2F9A41112131EF0002747C1 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F92C56380ECD10B3009BE4BA /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 8DC2EF4F0486A6940098B216 /* Breakpad */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "Breakpad" */; + buildPhases = ( + F97A0E850ED4EC15008784D3 /* Change install name of breakpadUtilities */, + 8DC2EF500486A6940098B216 /* Headers */, + 8DC2EF520486A6940098B216 /* Resources */, + 8DC2EF540486A6940098B216 /* Sources */, + 8DC2EF560486A6940098B216 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + F92C56860ECD15EF009BE4BA /* PBXTargetDependency */, + F92C56880ECD15F1009BE4BA /* PBXTargetDependency */, + F9C44E970EF09F4B003AEBAA /* PBXTargetDependency */, + ); + name = Breakpad; + productInstallPath = "$(HOME)/Library/Frameworks"; + productName = Breakpad; + productReference = 8DC2EF5B0486A6940098B216 /* Breakpad.framework */; + productType = "com.apple.product-type.framework"; + }; + D23F4BAA12A868A500686C8D /* minidump_generator_test_helper */ = { + isa = PBXNativeTarget; + buildConfigurationList = D23F4BB012A868C400686C8D /* Build configuration list for PBXNativeTarget "minidump_generator_test_helper" */; + buildPhases = ( + D23F4BA812A868A500686C8D /* Sources */, + D23F4BA912A868A500686C8D /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = minidump_generator_test_helper; + productName = minidump_generator_test_helper; + productReference = D23F4BAB12A868A500686C8D /* minidump_generator_test_helper */; + productType = "com.apple.product-type.tool"; + }; + D2F9A41412131EF0002747C1 /* gtest */ = { + isa = PBXNativeTarget; + buildConfigurationList = D2F9A42D12131F0E002747C1 /* Build configuration list for PBXNativeTarget "gtest" */; + buildPhases = ( + D2F9A41112131EF0002747C1 /* Headers */, + D2F9A41212131EF0002747C1 /* Sources */, + D2F9A41312131EF0002747C1 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = gtest; + productName = gtest; + productReference = D2F9A41512131EF0002747C1 /* libgtest.a */; + productType = "com.apple.product-type.library.static"; + }; + D2F9A52A121383A1002747C1 /* crash_generation_server_test */ = { + isa = PBXNativeTarget; + buildConfigurationList = D2F9A542121383A1002747C1 /* Build configuration list for PBXNativeTarget "crash_generation_server_test" */; + buildPhases = ( + D2F9A52D121383A1002747C1 /* Sources */, + D2F9A53E121383A1002747C1 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + D2F9A52B121383A1002747C1 /* PBXTargetDependency */, + ); + name = crash_generation_server_test; + productName = handler_test; + productReference = D2F9A546121383A1002747C1 /* crash_generation_server_test */; + productType = "com.apple.product-type.tool"; + }; + F92C53530ECCE349009BE4BA /* Inspector */ = { + isa = PBXNativeTarget; + buildConfigurationList = F92C53580ECCE36D009BE4BA /* Build configuration list for PBXNativeTarget "Inspector" */; + buildPhases = ( + F94584840F27FB40009A47BF /* Change install name of breakpadUtilities */, + F92C53510ECCE349009BE4BA /* Sources */, + F92C53520ECCE349009BE4BA /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + F92C564E0ECD10E5009BE4BA /* PBXTargetDependency */, + ); + name = Inspector; + productName = Inspector; + productReference = F92C53540ECCE349009BE4BA /* Inspector */; + productType = "com.apple.product-type.tool"; + }; + F92C563B0ECD10B3009BE4BA /* breakpadUtilities */ = { + isa = PBXNativeTarget; + buildConfigurationList = F92C56670ECD11A3009BE4BA /* Build configuration list for PBXNativeTarget "breakpadUtilities" */; + buildPhases = ( + F92C56380ECD10B3009BE4BA /* Headers */, + F92C56390ECD10B3009BE4BA /* Sources */, + F92C563A0ECD10B3009BE4BA /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = breakpadUtilities; + productName = breakpadUtilities; + productReference = F92C563C0ECD10B3009BE4BA /* breakpadUtilities.dylib */; + productType = "com.apple.product-type.library.dynamic"; + }; + F92C569F0ECE04A7009BE4BA /* crash_report_sender */ = { + isa = PBXNativeTarget; + buildConfigurationList = F92C56A50ECE04A8009BE4BA /* Build configuration list for PBXNativeTarget "crash_report_sender" */; + buildPhases = ( + F92C569C0ECE04A7009BE4BA /* Resources */, + F92C569D0ECE04A7009BE4BA /* Sources */, + F92C569E0ECE04A7009BE4BA /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = crash_report_sender; + productName = crash_report_sender; + productReference = F92C56A00ECE04A7009BE4BA /* crash_report_sender.app */; + productType = "com.apple.product-type.application"; + }; + F93803BD0F80820F004D428B /* generator_test */ = { + isa = PBXNativeTarget; + buildConfigurationList = F93803C40F80822E004D428B /* Build configuration list for PBXNativeTarget "generator_test" */; + buildPhases = ( + F93803BB0F80820F004D428B /* Sources */, + F93803BC0F80820F004D428B /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + D23F4B3012A7E16200686C8D /* PBXTargetDependency */, + D23F4BBA12A8694C00686C8D /* PBXTargetDependency */, + ); + name = generator_test; + productName = generator_test; + productReference = F93803BE0F80820F004D428B /* generator_test */; + productType = "com.apple.product-type.tool"; + }; + F93DE2D00F82A67300608B94 /* minidump_file_writer_unittest */ = { + isa = PBXNativeTarget; + buildConfigurationList = F93DE2D60F82A67700608B94 /* Build configuration list for PBXNativeTarget "minidump_file_writer_unittest" */; + buildPhases = ( + F93DE2CE0F82A67300608B94 /* Sources */, + F93DE2CF0F82A67300608B94 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = minidump_file_writer_unittest; + productName = minidump_file_writer_unittest; + productReference = F93DE2D10F82A67300608B94 /* minidump_file_writer_unittest */; + productType = "com.apple.product-type.tool"; + }; + F93DE32B0F82C55600608B94 /* handler_test */ = { + isa = PBXNativeTarget; + buildConfigurationList = F93DE3320F82C5D800608B94 /* Build configuration list for PBXNativeTarget "handler_test" */; + buildPhases = ( + F93DE3290F82C55600608B94 /* Sources */, + F93DE32A0F82C55600608B94 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + D2F9A44312131F80002747C1 /* PBXTargetDependency */, + ); + name = handler_test; + productName = handler_test; + productReference = F93DE32C0F82C55600608B94 /* handler_test */; + productType = "com.apple.product-type.tool"; + }; + F9C44DA40EF060A8003AEBAA /* BreakpadTest */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9C44DAA0EF060A9003AEBAA /* Build configuration list for PBXNativeTarget "BreakpadTest" */; + buildPhases = ( + F9C44DA10EF060A8003AEBAA /* Resources */, + F9C44DA20EF060A8003AEBAA /* Sources */, + F9C44DA30EF060A8003AEBAA /* Frameworks */, + F9C44E410EF08B17003AEBAA /* Copy Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + F9C44E1A0EF0790F003AEBAA /* PBXTargetDependency */, + ); + name = BreakpadTest; + productName = BreakpadTest; + productReference = F9C44DA50EF060A8003AEBAA /* BreakpadTest.app */; + productType = "com.apple.product-type.application"; + }; + F9C77DD90F7DD5CF0045F7DB /* UnitTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9C77DDE0F7DD5D00045F7DB /* Build configuration list for PBXNativeTarget "UnitTests" */; + buildPhases = ( + F9C77DD50F7DD5CF0045F7DB /* Resources */, + F9C77DD60F7DD5CF0045F7DB /* Sources */, + F9C77DD70F7DD5CF0045F7DB /* Frameworks */, + F9C77DD80F7DD5CF0045F7DB /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + F93DE2FC0F82C3C600608B94 /* PBXTargetDependency */, + F93DE3700F82CC1300608B94 /* PBXTargetDependency */, + F91AF6380FD60A74009D8BE2 /* PBXTargetDependency */, + D2F9A5DF12142A6A002747C1 /* PBXTargetDependency */, + ); + name = UnitTests; + productName = UnitTests; + productReference = F9C77DDA0F7DD5CF0045F7DB /* UnitTests.octest */; + productType = "com.apple.product-type.bundle"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 0867D690FE84028FC02AAC07 /* Project object */ = { + isa = PBXProject; + attributes = { + }; + buildConfigurationList = 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Breakpad" */; + compatibilityVersion = "Xcode 3.1"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + English, + da, + de, + es, + fr, + it, + ja, + nl, + no, + sl, + sv, + tr, + ); + mainGroup = 0867D691FE84028FC02AAC07 /* Breakpad */; + productRefGroup = 034768DFFF38A50411DB9C8B /* Products */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = F95BB87D101F949F00AA053B /* Products */; + ProjectRef = F95BB87C101F949F00AA053B /* crash_report.xcodeproj */; + }, + { + ProductGroup = F95BB88A101F94AC00AA053B /* Products */; + ProjectRef = F95BB889101F94AC00AA053B /* dump_syms.xcodeproj */; + }, + { + ProductGroup = F95BB895101F94C000AA053B /* Products */; + ProjectRef = F95BB894101F94C000AA053B /* symupload.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 8DC2EF4F0486A6940098B216 /* Breakpad */, + F92C53530ECCE349009BE4BA /* Inspector */, + F92C563B0ECD10B3009BE4BA /* breakpadUtilities */, + F92C569F0ECE04A7009BE4BA /* crash_report_sender */, + F9C44DA40EF060A8003AEBAA /* BreakpadTest */, + F94585840F782326009A47BF /* All */, + F9C77DD90F7DD5CF0045F7DB /* UnitTests */, + F93803BD0F80820F004D428B /* generator_test */, + F93DE2D00F82A67300608B94 /* minidump_file_writer_unittest */, + F93DE32B0F82C55600608B94 /* handler_test */, + D2F9A41412131EF0002747C1 /* gtest */, + D2F9A52A121383A1002747C1 /* crash_generation_server_test */, + D23F4BAA12A868A500686C8D /* minidump_generator_test_helper */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + 8B31F7A111EF9A8700FCF3E4 /* macho_dump */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = macho_dump; + remoteRef = 8B31F7A011EF9A8700FCF3E4 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 8B31F7A311EF9A8700FCF3E4 /* libgtestmockall.a */ = { + isa = PBXReferenceProxy; + fileType = archive.ar; + path = libgtestmockall.a; + remoteRef = 8B31F7A211EF9A8700FCF3E4 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 8B31F7A511EF9A8700FCF3E4 /* byte_cursor_unittest */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = byte_cursor_unittest; + remoteRef = 8B31F7A411EF9A8700FCF3E4 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 8B31F7A711EF9A8700FCF3E4 /* macho_reader_unittest */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = macho_reader_unittest; + remoteRef = 8B31F7A611EF9A8700FCF3E4 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 8B31F7A911EF9A8700FCF3E4 /* stabs_reader_unittest */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = stabs_reader_unittest; + remoteRef = 8B31F7A811EF9A8700FCF3E4 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 8B31F7AB11EF9A8700FCF3E4 /* bytereader_unittest */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = bytereader_unittest; + remoteRef = 8B31F7AA11EF9A8700FCF3E4 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 8B31F7AD11EF9A8700FCF3E4 /* dwarf2reader_cfi_unittest */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = dwarf2reader_cfi_unittest; + remoteRef = 8B31F7AC11EF9A8700FCF3E4 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 8B31F7AF11EF9A8700FCF3E4 /* dwarf2diehandler_unittest */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = dwarf2diehandler_unittest; + remoteRef = 8B31F7AE11EF9A8700FCF3E4 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 8B31F7B111EF9A8700FCF3E4 /* dwarf_cu_to_module_unittest */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = dwarf_cu_to_module_unittest; + remoteRef = 8B31F7B011EF9A8700FCF3E4 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 8B31F7B311EF9A8700FCF3E4 /* dwarf_line_to_module_unittest */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = dwarf_line_to_module_unittest; + remoteRef = 8B31F7B211EF9A8700FCF3E4 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 8B31F7B511EF9A8700FCF3E4 /* dwarf_cfi_to_module_unittest */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = dwarf_cfi_to_module_unittest; + remoteRef = 8B31F7B411EF9A8700FCF3E4 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 8B31F7B711EF9A8700FCF3E4 /* stabs_to_module_unittest */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = stabs_to_module_unittest; + remoteRef = 8B31F7B611EF9A8700FCF3E4 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 8B31F7B911EF9A8700FCF3E4 /* module_unittest */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = module_unittest; + remoteRef = 8B31F7B811EF9A8700FCF3E4 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + 8B31F7BB11EF9A8700FCF3E4 /* test_assembler_unittest */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = test_assembler_unittest; + remoteRef = 8B31F7BA11EF9A8700FCF3E4 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + F95BB885101F949F00AA053B /* crash_report */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = crash_report; + remoteRef = F95BB884101F949F00AA053B /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + F95BB892101F94AC00AA053B /* dump_syms */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = dump_syms; + remoteRef = F95BB891101F94AC00AA053B /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + F95BB89F101F94C000AA053B /* symupload */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = symupload; + remoteRef = F95BB89E101F94C000AA053B /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; + F95BB8A1101F94C000AA053B /* minidump_upload */ = { + isa = PBXReferenceProxy; + fileType = "compiled.mach-o.executable"; + path = minidump_upload; + remoteRef = F95BB8A0101F94C000AA053B /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + 8DC2EF520486A6940098B216 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9C44E980EF09F56003AEBAA /* crash_report_sender.app in Resources */, + F92C568A0ECD15F9009BE4BA /* Inspector in Resources */, + F92C56650ECD1185009BE4BA /* breakpadUtilities.dylib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F92C569C0ECE04A7009BE4BA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F945849E0F280E3C009A47BF /* Localizable.strings in Resources */, + 4084699D0F5D9CF900FDCA37 /* crash_report_sender.icns in Resources */, + 33880C800F9E097100817F82 /* InfoPlist.strings in Resources */, + 3329D4ED0FA16D820007BBC5 /* Breakpad.xib in Resources */, + F9B630A0100FF96B00D0F4AC /* goArrow.png in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9C44DA10EF060A8003AEBAA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9C44DB30EF07288003AEBAA /* crashduringload in Resources */, + F9C44DB40EF07288003AEBAA /* crashInMain in Resources */, + F9C44DBC0EF072A0003AEBAA /* InfoPlist.strings in Resources */, + F9C44DBD0EF072A0003AEBAA /* MainMenu.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9C77DD50F7DD5CF0045F7DB /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + F94584840F27FB40009A47BF /* Change install name of breakpadUtilities */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Change install name of breakpadUtilities"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "install_name_tool -id \"@executable_path/../Resources/breakpadUtilities.dylib\" \"${BUILT_PRODUCTS_DIR}/breakpadUtilities.dylib\"\n"; + }; + F97A0E850ED4EC15008784D3 /* Change install name of breakpadUtilities */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Change install name of breakpadUtilities"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "#!/bin/bash\ninstall_name_tool -id \"@executable_path/../Frameworks/Breakpad.framework/Resources/breakpadUtilities.dylib\" \"${BUILT_PRODUCTS_DIR}/breakpadUtilities.dylib\"\n"; + }; + F9C77DD80F7DD5CF0045F7DB /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n\necho running minidump generator tests...\n\"${BUILT_PRODUCTS_DIR}/generator_test\"\necho Running exception handler tests...\n\"${BUILT_PRODUCTS_DIR}/handler_test\"\necho Running crash generation server tests...\n\"${BUILT_PRODUCTS_DIR}/crash_generation_server_test\"\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8DC2EF540486A6940098B216 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + EB9CF8C824F01FB900F9B6D1 /* HTTPMultipartUpload.m in Sources */, + EB9CF8C224F01E1D00F9B6D1 /* HTTPGetRequest.m in Sources */, + F92C565F0ECD116B009BE4BA /* protected_memory_allocator.cc in Sources */, + EB9CF8BA24F01E1D00F9B6D1 /* minidump_upload.m in Sources */, + F92C56630ECD1179009BE4BA /* exception_handler.cc in Sources */, + EB9CF8BE24F01E1D00F9B6D1 /* HTTPPutRequest.m in Sources */, + F92C55D10ECD0064009BE4BA /* Breakpad.mm in Sources */, + F4DAB1DD19F1027100A5A838 /* launch_reporter.cc in Sources */, + F92C56340ECD0DF1009BE4BA /* OnDemandServer.mm in Sources */, + D2F9A4CB121336C7002747C1 /* crash_generation_client.cc in Sources */, + D2F9A4CD121336C7002747C1 /* crash_generation_server.cc in Sources */, + 163201D71443019E00C4DBF5 /* ConfigFile.mm in Sources */, + 162F64F2161C577500CD68D5 /* arch_utilities.cc in Sources */, + 1EEEB6231720829E00F7E689 /* simple_string_dictionary.cc in Sources */, + EB9CF8C324F01E1D00F9B6D1 /* HTTPSimplePostRequest.m in Sources */, + EB9CF8B924F01E1D00F9B6D1 /* encoding_util.m in Sources */, + EB9CF8BF24F01E1D00F9B6D1 /* HTTPRequest.m in Sources */, + EB9CF8C024F01E1D00F9B6D1 /* SymbolCollectorClient.m in Sources */, + 421BC5BC21110C0300B8042E /* convert_old_arm64_context.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D23F4BA812A868A500686C8D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D23F4BB112A868CB00686C8D /* minidump_generator_test_helper.cc in Sources */, + D23F4BB812A868F700686C8D /* MachIPC.mm in Sources */, + D246417012BAA40E005170D0 /* exception_handler.cc in Sources */, + D246417112BAA41C005170D0 /* crash_generation_client.cc in Sources */, + D246417512BAA438005170D0 /* minidump_generator.cc in Sources */, + D246417612BAA43F005170D0 /* dynamic_images.cc in Sources */, + D246417712BAA444005170D0 /* breakpad_nlist_64.cc in Sources */, + D246418812BAA4E3005170D0 /* string_utilities.cc in Sources */, + D246418C12BAA508005170D0 /* minidump_file_writer.cc in Sources */, + D246419012BAA52A005170D0 /* string_conversion.cc in Sources */, + D246419112BAA52F005170D0 /* convert_UTF.cc in Sources */, + D246419512BAA54C005170D0 /* file_id.cc in Sources */, + D246419612BAA55A005170D0 /* macho_id.cc in Sources */, + D24641A012BAA67F005170D0 /* macho_walker.cc in Sources */, + D24641AF12BAA82D005170D0 /* macho_utilities.cc in Sources */, + 4D72CA2513DFAE1C006CABE3 /* md5.cc in Sources */, + 4D61A26C14F43D42002D5862 /* bootstrap_compat.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D2F9A41212131EF0002747C1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D2F9A43D12131F55002747C1 /* gmock-all.cc in Sources */, + D2F9A44012131F65002747C1 /* gtest_main.cc in Sources */, + D2F9A44112131F65002747C1 /* gtest-all.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D2F9A52D121383A1002747C1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D2F9A553121383DC002747C1 /* crash_generation_server_test.cc in Sources */, + D2F9A52E121383A1002747C1 /* crash_generation_client.cc in Sources */, + D2F9A52F121383A1002747C1 /* crash_generation_server.cc in Sources */, + D2F9A530121383A1002747C1 /* MachIPC.mm in Sources */, + D2F9A531121383A1002747C1 /* breakpad_nlist_64.cc in Sources */, + D2F9A532121383A1002747C1 /* dynamic_images.cc in Sources */, + D2F9A533121383A1002747C1 /* exception_handler.cc in Sources */, + D2F9A534121383A1002747C1 /* minidump_generator.cc in Sources */, + D2F9A535121383A1002747C1 /* minidump_file_writer.cc in Sources */, + D2F9A536121383A1002747C1 /* convert_UTF.cc in Sources */, + D2F9A537121383A1002747C1 /* string_conversion.cc in Sources */, + D2F9A538121383A1002747C1 /* file_id.cc in Sources */, + D2F9A539121383A1002747C1 /* macho_id.cc in Sources */, + D2F9A53A121383A1002747C1 /* macho_utilities.cc in Sources */, + D2F9A53B121383A1002747C1 /* macho_walker.cc in Sources */, + D2F9A53C121383A1002747C1 /* string_utilities.cc in Sources */, + D24641EC12BAC6FB005170D0 /* logging.cc in Sources */, + D24641ED12BAC6FB005170D0 /* minidump.cc in Sources */, + D24641EE12BAC6FB005170D0 /* pathname_stripper.cc in Sources */, + D24641EF12BAC6FB005170D0 /* basic_code_modules.cc in Sources */, + 4D72CA3913DFAE92006CABE3 /* md5.cc in Sources */, + 4D61A26F14F43D48002D5862 /* bootstrap_compat.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F92C53510ECCE349009BE4BA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F92C53B80ECCE7B3009BE4BA /* Inspector.mm in Sources */, + F9286B3A0F7EB25800A4DCC8 /* InspectorMain.mm in Sources */, + 163201E31443029300C4DBF5 /* ConfigFile.mm in Sources */, + 4D61A26B14F43D3C002D5862 /* bootstrap_compat.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F92C56390ECD10B3009BE4BA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F4F916B619F10FFC00B83BE4 /* launch_reporter.cc in Sources */, + 162F64F4161C579B00CD68D5 /* arch_utilities.cc in Sources */, + 162F64F5161C579B00CD68D5 /* arch_utilities.h in Sources */, + D2A5DD301188633800081F03 /* breakpad_nlist_64.cc in Sources */, + F92C563F0ECD10CA009BE4BA /* convert_UTF.cc in Sources */, + F92C56400ECD10CA009BE4BA /* dynamic_images.cc in Sources */, + F92C56410ECD10CA009BE4BA /* file_id.cc in Sources */, + F92C56420ECD10CA009BE4BA /* macho_id.cc in Sources */, + F92C56430ECD10CA009BE4BA /* macho_utilities.cc in Sources */, + F92C56440ECD10CA009BE4BA /* macho_walker.cc in Sources */, + F92C56450ECD10CA009BE4BA /* MachIPC.mm in Sources */, + 4D72CA0E13DFAD5C006CABE3 /* md5.cc in Sources */, + F92C56460ECD10CA009BE4BA /* minidump_file_writer.cc in Sources */, + F92C56470ECD10CA009BE4BA /* minidump_generator.cc in Sources */, + F92C56490ECD10CA009BE4BA /* string_utilities.cc in Sources */, + F92C564A0ECD10CA009BE4BA /* string_conversion.cc in Sources */, + 4D61A25F14F43CFC002D5862 /* bootstrap_compat.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F92C569D0ECE04A7009BE4BA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + EB9CF8C724F01F7600F9B6D1 /* encoding_util.m in Sources */, + EB9CF8C624F01F1100F9B6D1 /* HTTPRequest.m in Sources */, + F9C44EA20EF09F93003AEBAA /* HTTPMultipartUpload.m in Sources */, + F92C56A90ECE04C5009BE4BA /* crash_report_sender.m in Sources */, + F9C44EE90EF0A3C1003AEBAA /* GTMLogger.m in Sources */, + 16E02DB8147410F0008C604D /* uploader.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F93803BB0F80820F004D428B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + D2C1DBE412AFC270006917BD /* logging.cc in Sources */, + D2C1DBE512AFC270006917BD /* minidump.cc in Sources */, + D2C1DBE612AFC270006917BD /* pathname_stripper.cc in Sources */, + D2C1DBE712AFC270006917BD /* basic_code_modules.cc in Sources */, + D2F9A4DF12133AD9002747C1 /* crash_generation_client.cc in Sources */, + D2F9A4E012133AD9002747C1 /* crash_generation_server.cc in Sources */, + D24BBD291211EDB100F3D417 /* MachIPC.mm in Sources */, + D2A5DD401188640400081F03 /* breakpad_nlist_64.cc in Sources */, + F93803CD0F8083B7004D428B /* dynamic_images.cc in Sources */, + F93803CE0F8083B7004D428B /* exception_handler.cc in Sources */, + F93803CF0F8083B7004D428B /* minidump_generator.cc in Sources */, + F93803D00F8083B7004D428B /* minidump_file_writer.cc in Sources */, + F93803D10F8083B7004D428B /* convert_UTF.cc in Sources */, + F93803D20F8083B7004D428B /* string_conversion.cc in Sources */, + F93803D30F8083B7004D428B /* file_id.cc in Sources */, + F93803D40F8083B7004D428B /* macho_id.cc in Sources */, + F93803D50F8083B7004D428B /* macho_utilities.cc in Sources */, + F93803D60F8083B7004D428B /* macho_walker.cc in Sources */, + F93803D70F8083B7004D428B /* string_utilities.cc in Sources */, + D23F4B2E12A7E13200686C8D /* minidump_generator_test.cc in Sources */, + 4D72CA2F13DFAE65006CABE3 /* md5.cc in Sources */, + 4D61A26D14F43D43002D5862 /* bootstrap_compat.cc in Sources */, + 1EEEB62B1720868C00F7E689 /* simple_string_dictionary.cc in Sources */, + 1EEEB62A1720859200F7E689 /* simple_string_dictionary_unittest.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F93DE2CE0F82A67300608B94 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F93DE2D90F82A73500608B94 /* minidump_file_writer.cc in Sources */, + F93DE2DA0F82A73500608B94 /* convert_UTF.cc in Sources */, + F93DE2DB0F82A73500608B94 /* string_conversion.cc in Sources */, + F93DE2D80F82A70E00608B94 /* minidump_file_writer_unittest.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F93DE3290F82C55600608B94 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 421BC5BE21110C1000B8042E /* convert_old_arm64_context.cc in Sources */, + 4247E6412110D7A300482558 /* memory_allocator_unittest.cc in Sources */, + D244536A12426F00009BBCE0 /* logging.cc in Sources */, + D244536B12426F00009BBCE0 /* minidump.cc in Sources */, + D244536C12426F00009BBCE0 /* pathname_stripper.cc in Sources */, + D244536D12426F00009BBCE0 /* basic_code_modules.cc in Sources */, + D2F9A4E112133AE2002747C1 /* crash_generation_client.cc in Sources */, + D2F9A4E212133AE2002747C1 /* crash_generation_server.cc in Sources */, + D24BBD321212CACF00F3D417 /* MachIPC.mm in Sources */, + D2A5DD411188642E00081F03 /* breakpad_nlist_64.cc in Sources */, + F93DE3350F82C66B00608B94 /* dynamic_images.cc in Sources */, + F93DE3360F82C66B00608B94 /* exception_handler.cc in Sources */, + F93DE3370F82C66B00608B94 /* minidump_generator.cc in Sources */, + F93DE3380F82C66B00608B94 /* minidump_file_writer.cc in Sources */, + F93DE3390F82C66B00608B94 /* convert_UTF.cc in Sources */, + F93DE33A0F82C66B00608B94 /* string_conversion.cc in Sources */, + F93DE33B0F82C66B00608B94 /* file_id.cc in Sources */, + F93DE33C0F82C66B00608B94 /* macho_id.cc in Sources */, + F93DE33D0F82C66B00608B94 /* macho_utilities.cc in Sources */, + F93DE33E0F82C66B00608B94 /* macho_walker.cc in Sources */, + F93DE33F0F82C66B00608B94 /* string_utilities.cc in Sources */, + D2F9A3D51212F87C002747C1 /* exception_handler_test.cc in Sources */, + 4D72CA3813DFAE91006CABE3 /* md5.cc in Sources */, + 4D61A26E14F43D45002D5862 /* bootstrap_compat.cc in Sources */, + 1EEEB6271720831E00F7E689 /* BreakpadFramework_Test.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9C44DA20EF060A8003AEBAA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9C44DB20EF07288003AEBAA /* Controller.m in Sources */, + F9C44DB60EF07288003AEBAA /* main.m in Sources */, + F9C44DB70EF07288003AEBAA /* TestClass.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9C77DD60F7DD5CF0045F7DB /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9C77E130F7DDF810045F7DB /* GTMSenTestCase.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 8B31023911F0CF0600FCF3E4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = all_unittests; + targetProxy = 8B31023811F0CF0600FCF3E4 /* PBXContainerItemProxy */; + }; + 8B31051711F1010E00FCF3E4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F93803BD0F80820F004D428B /* generator_test */; + targetProxy = 8B31051611F1010E00FCF3E4 /* PBXContainerItemProxy */; + }; + 8B31051911F1010E00FCF3E4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F93DE2D00F82A67300608B94 /* minidump_file_writer_unittest */; + targetProxy = 8B31051811F1010E00FCF3E4 /* PBXContainerItemProxy */; + }; + 8B31051B11F1010E00FCF3E4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F93DE32B0F82C55600608B94 /* handler_test */; + targetProxy = 8B31051A11F1010E00FCF3E4 /* PBXContainerItemProxy */; + }; + 8B31051D11F1010E00FCF3E4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = macho_dump; + targetProxy = 8B31051C11F1010E00FCF3E4 /* PBXContainerItemProxy */; + }; + 8B31051F11F1010E00FCF3E4 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = minidump_upload; + targetProxy = 8B31051E11F1010E00FCF3E4 /* PBXContainerItemProxy */; + }; + D23F4B3012A7E16200686C8D /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D2F9A41412131EF0002747C1 /* gtest */; + targetProxy = D23F4B2F12A7E16200686C8D /* PBXContainerItemProxy */; + }; + D23F4BBA12A8694C00686C8D /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D23F4BAA12A868A500686C8D /* minidump_generator_test_helper */; + targetProxy = D23F4BB912A8694C00686C8D /* PBXContainerItemProxy */; + }; + D2F9A44312131F80002747C1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D2F9A41412131EF0002747C1 /* gtest */; + targetProxy = D2F9A44212131F80002747C1 /* PBXContainerItemProxy */; + }; + D2F9A52B121383A1002747C1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D2F9A41412131EF0002747C1 /* gtest */; + targetProxy = D2F9A52C121383A1002747C1 /* PBXContainerItemProxy */; + }; + D2F9A5DF12142A6A002747C1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = D2F9A52A121383A1002747C1 /* crash_generation_server_test */; + targetProxy = D2F9A5DE12142A6A002747C1 /* PBXContainerItemProxy */; + }; + F91AF6380FD60A74009D8BE2 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8DC2EF4F0486A6940098B216 /* Breakpad */; + targetProxy = F91AF6370FD60A74009D8BE2 /* PBXContainerItemProxy */; + }; + F92C564E0ECD10E5009BE4BA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F92C563B0ECD10B3009BE4BA /* breakpadUtilities */; + targetProxy = F92C564D0ECD10E5009BE4BA /* PBXContainerItemProxy */; + }; + F92C56860ECD15EF009BE4BA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F92C563B0ECD10B3009BE4BA /* breakpadUtilities */; + targetProxy = F92C56850ECD15EF009BE4BA /* PBXContainerItemProxy */; + }; + F92C56880ECD15F1009BE4BA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F92C53530ECCE349009BE4BA /* Inspector */; + targetProxy = F92C56870ECD15F1009BE4BA /* PBXContainerItemProxy */; + }; + F93DE2FC0F82C3C600608B94 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F93803BD0F80820F004D428B /* generator_test */; + targetProxy = F93DE2FB0F82C3C600608B94 /* PBXContainerItemProxy */; + }; + F93DE3700F82CC1300608B94 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F93DE32B0F82C55600608B94 /* handler_test */; + targetProxy = F93DE36F0F82CC1300608B94 /* PBXContainerItemProxy */; + }; + F93DE3A70F830D1D00608B94 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9C77DD90F7DD5CF0045F7DB /* UnitTests */; + targetProxy = F93DE3A60F830D1D00608B94 /* PBXContainerItemProxy */; + }; + F94585880F78232B009A47BF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8DC2EF4F0486A6940098B216 /* Breakpad */; + targetProxy = F94585870F78232B009A47BF /* PBXContainerItemProxy */; + }; + F945858A0F78232E009A47BF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F92C53530ECCE349009BE4BA /* Inspector */; + targetProxy = F94585890F78232E009A47BF /* PBXContainerItemProxy */; + }; + F945858C0F782330009A47BF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F92C563B0ECD10B3009BE4BA /* breakpadUtilities */; + targetProxy = F945858B0F782330009A47BF /* PBXContainerItemProxy */; + }; + F945858E0F782333009A47BF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F92C569F0ECE04A7009BE4BA /* crash_report_sender */; + targetProxy = F945858D0F782333009A47BF /* PBXContainerItemProxy */; + }; + F94585900F782336009A47BF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F9C44DA40EF060A8003AEBAA /* BreakpadTest */; + targetProxy = F945858F0F782336009A47BF /* PBXContainerItemProxy */; + }; + F95BB8B3101F94D300AA053B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = dump_syms; + targetProxy = F95BB8B2101F94D300AA053B /* PBXContainerItemProxy */; + }; + F95BB8B5101F94D300AA053B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = symupload; + targetProxy = F95BB8B4101F94D300AA053B /* PBXContainerItemProxy */; + }; + F95BB8B7101F94D300AA053B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = crash_report; + targetProxy = F95BB8B6101F94D300AA053B /* PBXContainerItemProxy */; + }; + F9C44E1A0EF0790F003AEBAA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 8DC2EF4F0486A6940098B216 /* Breakpad */; + targetProxy = F9C44E190EF0790F003AEBAA /* PBXContainerItemProxy */; + }; + F9C44E970EF09F4B003AEBAA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = F92C569F0ECE04A7009BE4BA /* crash_report_sender */; + targetProxy = F9C44E960EF09F4B003AEBAA /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 33880C7E0F9E097100817F82 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 33880C7F0F9E097100817F82 /* English */, + DE43468711C7295D004F095F /* da */, + DE43468611C72958004F095F /* de */, + DE43468811C7295F004F095F /* es */, + DE43468911C72964004F095F /* fr */, + DE43468A11C72967004F095F /* it */, + DE43468B11C7296B004F095F /* ja */, + DE43468C11C7296D004F095F /* nl */, + DE43468D11C7296F004F095F /* no */, + DE43468E11C72971004F095F /* sl */, + DE43468F11C72973004F095F /* sv */, + DE43469011C72976004F095F /* tr */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + F945849C0F280E3C009A47BF /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + F945849D0F280E3C009A47BF /* English */, + DE43467411C72855004F095F /* da */, + DE43467511C72857004F095F /* de */, + DE43467611C7285B004F095F /* es */, + DE43467711C72862004F095F /* fr */, + DE43467811C72869004F095F /* it */, + DE43467E11C728DC004F095F /* ja */, + DE43467911C7286D004F095F /* nl */, + DE43467A11C72873004F095F /* no */, + DE43467B11C72877004F095F /* sl */, + DE43467C11C7287A004F095F /* sv */, + DE43467F11C728E1004F095F /* tr */, + ); + name = Localizable.strings; + sourceTree = ""; + }; + F9C44DB80EF072A0003AEBAA /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + F9C44DB90EF072A0003AEBAA /* English */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + F9C44DBA0EF072A0003AEBAA /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + F9C44DBB0EF072A0003AEBAA /* English */, + ); + name = MainMenu.xib; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 1DEB91AE08733DA50010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = Framework/Breakpad_Prefix.pch; + HEADER_SEARCH_PATHS = ../..; + INFOPLIST_FILE = Framework/Info.plist; + INSTALL_PATH = "@executable_path/../Frameworks"; + PRODUCT_NAME = Breakpad; + WRAPPER_EXTENSION = framework; + }; + name = Debug; + }; + 1DEB91AF08733DA50010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = Framework/Breakpad_Prefix.pch; + HEADER_SEARCH_PATHS = ../..; + INFOPLIST_FILE = Framework/Info.plist; + INSTALL_PATH = "@executable_path/../Frameworks"; + PRODUCT_NAME = Breakpad; + WRAPPER_EXTENSION = framework; + }; + name = Release; + }; + 1DEB91B208733DA50010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8B31027711F0D3AF00FCF3E4 /* BreakpadDebug.xcconfig */; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++17"; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + SDKROOT = macosx; + }; + name = Debug; + }; + 1DEB91B308733DA50010E9CD /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8B31027811F0D3AF00FCF3E4 /* BreakpadRelease.xcconfig */; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++17"; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + }; + name = Release; + }; + D23F4BAD12A868A600686C8D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + HEADER_SEARCH_PATHS = ../..; + INSTALL_PATH = /usr/local/bin; + PREBINDING = NO; + PRODUCT_NAME = minidump_generator_test_helper; + }; + name = Debug; + }; + D23F4BAE12A868A600686C8D /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + HEADER_SEARCH_PATHS = ../..; + INSTALL_PATH = /usr/local/bin; + PREBINDING = NO; + PRODUCT_NAME = minidump_generator_test_helper; + }; + name = "Debug With Code Coverage"; + }; + D23F4BAF12A868A600686C8D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + GCC_MODEL_TUNING = G5; + HEADER_SEARCH_PATHS = ../..; + INSTALL_PATH = /usr/local/bin; + PREBINDING = NO; + PRODUCT_NAME = minidump_generator_test_helper; + ZERO_LINK = NO; + }; + name = Release; + }; + D2F9A41612131EF0002747C1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_OPTIMIZATION_LEVEL = 0; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + ../../testing/googlemock, + ../../testing/googlemock/include, + ../../testing/googletest, + ../../testing/googletest/include, + ); + PREBINDING = NO; + PRODUCT_NAME = gtest; + }; + name = Debug; + }; + D2F9A41712131EF0002747C1 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + ../../testing/googlemock, + ../../testing/googlemock/include, + ../../testing/googletest, + ../../testing/googletest/include, + ); + PREBINDING = NO; + PRODUCT_NAME = gtest; + }; + name = "Debug With Code Coverage"; + }; + D2F9A41812131EF0002747C1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = YES; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_ENABLE_FIX_AND_CONTINUE = NO; + HEADER_SEARCH_PATHS = ( + "$(inherited)", + ../../testing/googlemock, + ../../testing/googlemock/include, + ../../testing/googletest, + ../../testing/googletest/include, + ); + PREBINDING = NO; + PRODUCT_NAME = gtest; + ZERO_LINK = NO; + }; + name = Release; + }; + D2F9A543121383A1002747C1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_INLINES_ARE_PRIVATE_EXTERN = NO; + GCC_PREPROCESSOR_DEFINITIONS = "BP_LOGGING_INCLUDE=\\\"client/mac/tests/testlogging.h\\\""; + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + HEADER_SEARCH_PATHS = ( + ../.., + ../../testing/googlemock, + ../../testing/googlemock/include, + ../../testing/googletest, + ../../testing/googletest/include, + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/build/Debug\"", + ); + PRODUCT_NAME = crash_generation_server_test; + }; + name = Debug; + }; + D2F9A544121383A1002747C1 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = ( + ../.., + ../../testing/googlemock, + ../../testing/googlemock/include, + ../../testing/googletest, + ../../testing/googletest/include, + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\\\"$(SRCROOT)/build/Debug\\\"", + ); + PRODUCT_NAME = crash_generation_server_test; + }; + name = "Debug With Code Coverage"; + }; + D2F9A545121383A1002747C1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = ( + ../.., + ../../testing/googlemock, + ../../testing/googlemock/include, + ../../testing/googletest, + ../../testing/googletest/include, + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\\\"$(SRCROOT)/build/Debug\\\"", + ); + PRODUCT_NAME = crash_generation_server_test; + }; + name = Release; + }; + F92C53560ECCE34A009BE4BA /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = ../..; + PRODUCT_NAME = Inspector; + }; + name = Debug; + }; + F92C53570ECCE34A009BE4BA /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = ../..; + PRODUCT_NAME = Inspector; + }; + name = Release; + }; + F92C563D0ECD10B3009BE4BA /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + HEADER_SEARCH_PATHS = ../..; + LD_DYLIB_INSTALL_NAME = "@executable_path/../Resources/$(EXECUTABLE_PATH)"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-headerpad_max_install_names", + ); + PRODUCT_NAME = breakpadUtilities; + }; + name = Debug; + }; + F92C563E0ECD10B3009BE4BA /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + HEADER_SEARCH_PATHS = ../..; + LD_DYLIB_INSTALL_NAME = "@executable_path/../Resources/$(EXECUTABLE_PATH)"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-headerpad_max_install_names", + ); + PRODUCT_NAME = breakpadUtilities; + }; + name = Release; + }; + F92C56A30ECE04A8009BE4BA /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = ../..; + INFOPLIST_FILE = "sender/crash_report_sender-Info.plist"; + PRODUCT_NAME = crash_report_sender; + }; + name = Debug; + }; + F92C56A40ECE04A8009BE4BA /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = ../..; + INFOPLIST_FILE = "sender/crash_report_sender-Info.plist"; + PRODUCT_NAME = crash_report_sender; + }; + name = Release; + }; + F93803C00F808210004D428B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + GCC_PREPROCESSOR_DEFINITIONS = "BP_LOGGING_INCLUDE=\\\"client/mac/tests/testlogging.h\\\""; + HEADER_SEARCH_PATHS = ( + ../.., + ../../.., + ../../testing/googlemock/include, + ../../testing/googletest/include, + ); + PRODUCT_NAME = generator_test; + }; + name = Debug; + }; + F93803C10F808210004D428B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = ( + ../.., + ../../.., + ../../testing/googlemock/include, + ../../testing/googletest/include, + ); + PRODUCT_NAME = generator_test; + }; + name = Release; + }; + F93DE2D30F82A67400608B94 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = ../..; + PRODUCT_NAME = minidump_file_writer_unittest; + }; + name = Debug; + }; + F93DE2D40F82A67400608B94 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = ../..; + PRODUCT_NAME = minidump_file_writer_unittest; + }; + name = Release; + }; + F93DE32E0F82C55700608B94 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_INLINES_ARE_PRIVATE_EXTERN = NO; + GCC_PREPROCESSOR_DEFINITIONS = "BP_LOGGING_INCLUDE=\\\"client/mac/tests/testlogging.h\\\""; + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + HEADER_SEARCH_PATHS = ( + ../../.., + ../.., + ../../testing/googletest, + ../../testing/googletest/include, + ../../testing/googlemock, + ../../testing/googlemock/include, + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/build/Debug\"", + ); + PRODUCT_NAME = handler_test; + }; + name = Debug; + }; + F93DE32F0F82C55700608B94 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = ( + ../../.., + ../.., + ../../testing/googletest, + ../../testing/googletest/include, + ../../testing/googlemock, + ../../testing/googlemock/include, + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/build/Debug\"", + ); + PRODUCT_NAME = handler_test; + }; + name = Release; + }; + F93DE3B90F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8B31027711F0D3AF00FCF3E4 /* BreakpadDebug.xcconfig */; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++17"; + GCC_TREAT_WARNINGS_AS_ERRORS = NO; + }; + name = "Debug With Code Coverage"; + }; + F93DE3BA0F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + FRAMEWORK_VERSION = A; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = Framework/Breakpad_Prefix.pch; + HEADER_SEARCH_PATHS = ../..; + INFOPLIST_FILE = Framework/Info.plist; + INSTALL_PATH = "@executable_path/../Frameworks"; + PRODUCT_NAME = Breakpad; + WRAPPER_EXTENSION = framework; + }; + name = "Debug With Code Coverage"; + }; + F93DE3BB0F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = ../..; + PRODUCT_NAME = Inspector; + }; + name = "Debug With Code Coverage"; + }; + F93DE3BC0F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + HEADER_SEARCH_PATHS = ../..; + LD_DYLIB_INSTALL_NAME = "@executable_path/../Resources/$(EXECUTABLE_PATH)"; + OTHER_LDFLAGS = ( + "$(inherited)", + "-headerpad_max_install_names", + ); + PRODUCT_NAME = breakpadUtilities; + }; + name = "Debug With Code Coverage"; + }; + F93DE3BD0F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = ../..; + INFOPLIST_FILE = "sender/crash_report_sender-Info.plist"; + PRODUCT_NAME = crash_report_sender; + }; + name = "Debug With Code Coverage"; + }; + F93DE3BE0F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/build/$(CONFIGURATION)"; + INFOPLIST_FILE = testapp/Info.plist; + PRODUCT_NAME = BreakpadTest; + }; + name = "Debug With Code Coverage"; + }; + F93DE3BF0F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = All; + }; + name = "Debug With Code Coverage"; + }; + F93DE3C00F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"$(DEVELOPER_FRAMEWORKS_DIR)\"", + ); + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + HEADER_SEARCH_PATHS = ../..; + INFOPLIST_FILE = "UnitTests-Info.plist"; + PRODUCT_NAME = UnitTests; + WRAPPER_EXTENSION = octest; + }; + name = "Debug With Code Coverage"; + }; + F93DE3C10F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = ( + ../.., + ../../.., + ../../testing/googlemock/include, + ../../testing/googletest/include, + ); + PRODUCT_NAME = generator_test; + }; + name = "Debug With Code Coverage"; + }; + F93DE3C20F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = ../..; + PRODUCT_NAME = minidump_file_writer_unittest; + }; + name = "Debug With Code Coverage"; + }; + F93DE3C30F830E7000608B94 /* Debug With Code Coverage */ = { + isa = XCBuildConfiguration; + buildSettings = { + HEADER_SEARCH_PATHS = ( + ../../.., + ../.., + ../../testing/googletest, + ../../testing/googletest/include, + ../../testing/googlemock, + ../../testing/googlemock/include, + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + "\"$(SRCROOT)/build/Debug\"", + ); + PRODUCT_NAME = handler_test; + }; + name = "Debug With Code Coverage"; + }; + F94585850F782326009A47BF /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = All; + }; + name = Debug; + }; + F94585860F782326009A47BF /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = All; + }; + name = Release; + }; + F9C44DA80EF060A8003AEBAA /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/build/$(CONFIGURATION)"; + INFOPLIST_FILE = testapp/Info.plist; + PRODUCT_NAME = BreakpadTest; + }; + name = Debug; + }; + F9C44DA90EF060A8003AEBAA /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = "$(SRCROOT)/build/$(CONFIGURATION)"; + INFOPLIST_FILE = testapp/Info.plist; + PRODUCT_NAME = BreakpadTest; + }; + name = Release; + }; + F9C77DDC0F7DD5D00045F7DB /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"$(DEVELOPER_FRAMEWORKS_DIR)\"", + ); + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + INFOPLIST_FILE = "UnitTests-Info.plist"; + PRODUCT_NAME = UnitTests; + WRAPPER_EXTENSION = octest; + }; + name = Debug; + }; + F9C77DDD0F7DD5D00045F7DB /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"$(DEVELOPER_FRAMEWORKS_DIR)\"", + ); + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + INFOPLIST_FILE = "UnitTests-Info.plist"; + PRODUCT_NAME = UnitTests; + WRAPPER_EXTENSION = octest; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1DEB91AD08733DA50010E9CD /* Build configuration list for PBXNativeTarget "Breakpad" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB91AE08733DA50010E9CD /* Debug */, + F93DE3BA0F830E7000608B94 /* Debug With Code Coverage */, + 1DEB91AF08733DA50010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1DEB91B108733DA50010E9CD /* Build configuration list for PBXProject "Breakpad" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB91B208733DA50010E9CD /* Debug */, + F93DE3B90F830E7000608B94 /* Debug With Code Coverage */, + 1DEB91B308733DA50010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D23F4BB012A868C400686C8D /* Build configuration list for PBXNativeTarget "minidump_generator_test_helper" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D23F4BAD12A868A600686C8D /* Debug */, + D23F4BAE12A868A600686C8D /* Debug With Code Coverage */, + D23F4BAF12A868A600686C8D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D2F9A42D12131F0E002747C1 /* Build configuration list for PBXNativeTarget "gtest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D2F9A41612131EF0002747C1 /* Debug */, + D2F9A41712131EF0002747C1 /* Debug With Code Coverage */, + D2F9A41812131EF0002747C1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + D2F9A542121383A1002747C1 /* Build configuration list for PBXNativeTarget "crash_generation_server_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + D2F9A543121383A1002747C1 /* Debug */, + D2F9A544121383A1002747C1 /* Debug With Code Coverage */, + D2F9A545121383A1002747C1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F92C53580ECCE36D009BE4BA /* Build configuration list for PBXNativeTarget "Inspector" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F92C53560ECCE34A009BE4BA /* Debug */, + F93DE3BB0F830E7000608B94 /* Debug With Code Coverage */, + F92C53570ECCE34A009BE4BA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F92C56670ECD11A3009BE4BA /* Build configuration list for PBXNativeTarget "breakpadUtilities" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F92C563D0ECD10B3009BE4BA /* Debug */, + F93DE3BC0F830E7000608B94 /* Debug With Code Coverage */, + F92C563E0ECD10B3009BE4BA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F92C56A50ECE04A8009BE4BA /* Build configuration list for PBXNativeTarget "crash_report_sender" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F92C56A30ECE04A8009BE4BA /* Debug */, + F93DE3BD0F830E7000608B94 /* Debug With Code Coverage */, + F92C56A40ECE04A8009BE4BA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F93803C40F80822E004D428B /* Build configuration list for PBXNativeTarget "generator_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F93803C00F808210004D428B /* Debug */, + F93DE3C10F830E7000608B94 /* Debug With Code Coverage */, + F93803C10F808210004D428B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F93DE2D60F82A67700608B94 /* Build configuration list for PBXNativeTarget "minidump_file_writer_unittest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F93DE2D30F82A67400608B94 /* Debug */, + F93DE3C20F830E7000608B94 /* Debug With Code Coverage */, + F93DE2D40F82A67400608B94 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F93DE3320F82C5D800608B94 /* Build configuration list for PBXNativeTarget "handler_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F93DE32E0F82C55700608B94 /* Debug */, + F93DE3C30F830E7000608B94 /* Debug With Code Coverage */, + F93DE32F0F82C55700608B94 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F94585930F78235C009A47BF /* Build configuration list for PBXAggregateTarget "All" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F94585850F782326009A47BF /* Debug */, + F93DE3BF0F830E7000608B94 /* Debug With Code Coverage */, + F94585860F782326009A47BF /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9C44DAA0EF060A9003AEBAA /* Build configuration list for PBXNativeTarget "BreakpadTest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9C44DA80EF060A8003AEBAA /* Debug */, + F93DE3BE0F830E7000608B94 /* Debug With Code Coverage */, + F9C44DA90EF060A8003AEBAA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9C77DDE0F7DD5D00045F7DB /* Build configuration list for PBXNativeTarget "UnitTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9C77DDC0F7DD5D00045F7DB /* Debug */, + F93DE3C00F830E7000608B94 /* Debug With Code Coverage */, + F9C77DDD0F7DD5D00045F7DB /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 0867D690FE84028FC02AAC07 /* Project object */; +} \ No newline at end of file diff --git a/shared/sentry/external/breakpad/src/client/mac/Framework/Breakpad.h b/shared/sentry/external/breakpad/src/client/mac/Framework/Breakpad.h new file mode 100644 index 000000000..9e191ce27 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/Framework/Breakpad.h @@ -0,0 +1,285 @@ +// 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. +// +// Framework to provide a simple C API to crash reporting for +// applications. By default, if any machine-level exception (e.g., +// EXC_BAD_ACCESS) occurs, it will be handled by the BreakpadRef +// object as follows: +// +// 1. Create a minidump file (see Breakpad for details) +// 2. Prompt the user (using CFUserNotification) +// 3. Invoke a command line reporting tool to send the minidump to a +// server +// +// By specifying parameters to the BreakpadCreate function, you can +// modify the default behavior to suit your needs and wants and +// desires. + +// A service name associated with the original bootstrap parent port, saved in +// OnDemandServer and restored in Inspector. +#define BREAKPAD_BOOTSTRAP_PARENT_PORT "com.Breakpad.BootstrapParent" + +typedef void* BreakpadRef; + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +#include "BreakpadDefines.h" + +// Optional user-defined function to dec to decide if we should handle +// this crash or forward it along. +// Return true if you want Breakpad to handle it. +// Return false if you want Breakpad to skip it +// The exception handler always returns false, as if SEND_AND_EXIT were false +// (which means the next exception handler will take the exception) +typedef bool (*BreakpadFilterCallback)(int exception_type, + int exception_code, + mach_port_t crashing_thread, + void* context); + +// Create a new BreakpadRef object and install it as an exception +// handler. The |parameters| will typically be the contents of your +// bundle's Info.plist. +// +// You can also specify these additional keys for customizable behavior: +// Key: Value: +// BREAKPAD_PRODUCT Product name (e.g., "MyAwesomeProduct") +// This one is used as the key to identify +// the product when uploading. Falls back to +// CFBundleName if not specified. +// REQUIRED +// +// BREAKPAD_PRODUCT_DISPLAY This is the display name, e.g. a pretty +// name for the product when the crash_sender +// pops up UI for the user. Falls back first to +// CFBundleDisplayName and then to +// BREAKPAD_PRODUCT if not specified. +// +// BREAKPAD_VERSION Product version (e.g., 1.2.3), used +// as metadata for crash report. Falls back to +// CFBundleVersion if not specified. +// REQUIRED +// +// BREAKPAD_VENDOR Vendor name, used in UI (e.g. "A report has +// been created that you can send to ") +// +// BREAKPAD_URL URL destination for reporting +// REQUIRED +// +// BREAKPAD_REPORT_INTERVAL # of seconds between sending +// reports. If an additional report is +// generated within this time, it will +// be ignored. Default: 3600sec. +// Specify 0 to send all reports. +// +// BREAKPAD_SKIP_CONFIRM If true, the reporter will send the report +// without any user intervention. +// Defaults to NO +// +// BREAKPAD_CONFIRM_TIMEOUT Number of seconds before the upload +// confirmation dialog will be automatically +// dismissed (cancelling the upload). +// Default: 300 seconds (min of 60). +// Specify 0 to prevent timeout. +// +// BREAKPAD_SEND_AND_EXIT If true, the handler will exit after sending. +// This will prevent any other handler (e.g., +// CrashReporter) from getting the crash. +// Defaults TO YES +// +// BREAKPAD_DUMP_DIRECTORY The directory to store crash-dumps +// in. By default, we use +// ~/Library/Breakpad/ +// The path you specify here is tilde-expanded. +// +// BREAKPAD_INSPECTOR_LOCATION The full path to the Inspector executable. +// Defaults to /Inspector +// +// BREAKPAD_REPORTER_EXE_LOCATION The full path to the Reporter/sender +// executable. +// Default: +// /crash_report_sender.app +// +// BREAKPAD_LOGFILES Indicates an array of log file paths that +// should be uploaded at crash time. +// +// BREAKPAD_REQUEST_COMMENTS If true, the message dialog will have a +// text box for the user to enter comments. +// Default: NO +// +// BREAKPAD_REQUEST_EMAIL If true and BREAKPAD_REQUEST_COMMENTS is also +// true, the message dialog will have a text +// box for the user to enter their email address. +// Default: NO +// +// BREAKPAD_SERVER_TYPE A parameter that tells Breakpad how to +// rewrite the upload parameters for a specific +// server type. The currently valid values are +// 'socorro' or 'google'. If you want to add +// other types, see the function in +// crash_report_sender.m that maps parameters to +// URL parameters. Defaults to 'google'. +// +// BREAKPAD_SERVER_PARAMETER_DICT A plist dictionary of static +// parameters that are uploaded to the +// server. The parameters are sent as +// is to the crash server. Their +// content isn't added to the minidump +// but pass as URL parameters when +// uploading theminidump to the crash +// server. +// +// BREAKPAD_IN_PROCESS A boolean NSNumber value. If YES, Breakpad +// will write the dump file in-process and then +// launch the reporter executable as a child +// process. +//============================================================================= +// The BREAKPAD_PRODUCT, BREAKPAD_VERSION and BREAKPAD_URL are +// required to have non-NULL values. By default, the BREAKPAD_PRODUCT +// will be the CFBundleName and the BREAKPAD_VERSION will be the +// CFBundleVersion when these keys are present in the bundle's +// Info.plist, which is usually passed in to BreakpadCreate() as an +// NSDictionary (you could also pass in another dictionary that had +// the same keys configured). If the BREAKPAD_PRODUCT or +// BREAKPAD_VERSION are ultimately undefined, BreakpadCreate() will +// fail. You have been warned. +// +// If you are running in a debugger, Breakpad will not install, unless the +// BREAKPAD_IGNORE_DEBUGGER envionment variable is set and/or non-zero. +// +// The BREAKPAD_SKIP_CONFIRM and BREAKPAD_SEND_AND_EXIT default +// values are NO and YES. However, they can be controlled by setting their +// values in a user or global plist. +// +// It's easiest to use Breakpad via the Framework, but if you're compiling the +// code in directly, BREAKPAD_INSPECTOR_LOCATION and +// BREAKPAD_REPORTER_EXE_LOCATION allow you to specify custom paths +// to the helper executables. +// +//============================================================================= +// The following are NOT user-supplied but are documented here for +// completeness. They are calculated by Breakpad during initialization & +// crash-dump generation, or entered in by the user. +// +// BREAKPAD_PROCESS_START_TIME The time, in seconds since the Epoch, the +// process started +// +// BREAKPAD_PROCESS_CRASH_TIME The time, in seconds since the Epoch, the +// process crashed. +// +// BREAKPAD_PROCESS_UP_TIME The total time in milliseconds the process +// has been running. This parameter is not +// set until the crash-dump-generation phase. +// +// BREAKPAD_LOGFILE_KEY_PREFIX Used to find out which parameters in the +// parameter dictionary correspond to log +// file paths. +// +// BREAKPAD_SERVER_PARAMETER_PREFIX This prefix is used by Breakpad +// internally, because Breakpad uses +// the same dictionary internally to +// track both its internal +// configuration parameters and +// parameters meant to be uploaded +// to the server. This string is +// used internally by Breakpad to +// prefix user-supplied parameter +// names so those can be sent to the +// server without leaking Breakpad's +// internal values. +// +// BREAKPAD_ON_DEMAND Used internally to indicate to the +// Reporter that we're sending on-demand, +// not as result of a crash. +// +// BREAKPAD_COMMENTS The text the user provided as comments. +// Only used in crash_report_sender. + +// Returns a new BreakpadRef object on success, NULL otherwise. +BreakpadRef BreakpadCreate(NSDictionary* parameters); + +// Uninstall and release the data associated with |ref|. +void BreakpadRelease(BreakpadRef ref); + +// Clients may set an optional callback which gets called when a crash +// occurs. The callback function should return |true| if we should +// handle the crash, generate a crash report, etc. or |false| if we +// should ignore it and forward the crash (normally to CrashReporter). +// Context is a pointer to arbitrary data to make the callback with. +void BreakpadSetFilterCallback(BreakpadRef ref, + BreakpadFilterCallback callback, + void* context); + +// User defined key and value string storage. Generally this is used +// to configure Breakpad's internal operation, such as whether the +// crash_sender should prompt the user, or the filesystem location for +// the minidump file. See Breakpad.h for some parameters that can be +// set. Anything longer than 255 bytes will be truncated. Note that +// the string is converted to UTF8 before truncation, so any multibyte +// character that straddles the 255(256 - 1 for terminator) byte limit +// will be mangled. +// +// A maximum number of 64 key/value pairs are supported. An assert() +// will fire if more than this number are set. Unfortunately, right +// now, the same dictionary is used for both Breakpad's parameters AND +// the Upload parameters. +// +// TODO (nealsid): Investigate how necessary this is if we don't +// automatically upload parameters to the server anymore. +// TODO (nealsid): separate server parameter dictionary from the +// dictionary used to configure Breakpad, and document limits for each +// independently. +void BreakpadSetKeyValue(BreakpadRef ref, NSString* key, NSString* value); +NSString* BreakpadKeyValue(BreakpadRef ref, NSString* key); +void BreakpadRemoveKeyValue(BreakpadRef ref, NSString* key); + +// You can use this method to specify parameters that will be uploaded +// to the crash server. They will be automatically encoded as +// necessary. Note that as mentioned above there are limits on both +// the number of keys and their length. +void BreakpadAddUploadParameter(BreakpadRef ref, NSString* key, + NSString* value); + +// This method will remove a previously-added parameter from the +// upload parameter set. +void BreakpadRemoveUploadParameter(BreakpadRef ref, NSString* key); + +// Add a log file for Breakpad to read and send upon crash dump +void BreakpadAddLogFile(BreakpadRef ref, NSString* logPathname); + +// Generate a minidump and send +void BreakpadGenerateAndSendReport(BreakpadRef ref); + +#ifdef __cplusplus +} +#endif diff --git a/shared/sentry/external/breakpad/src/client/mac/Framework/Breakpad.mm b/shared/sentry/external/breakpad/src/client/mac/Framework/Breakpad.mm new file mode 100644 index 000000000..1a46b5977 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/Framework/Breakpad.mm @@ -0,0 +1,1043 @@ +// 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. +// + + +#define IGNORE_DEBUGGER "BREAKPAD_IGNORE_DEBUGGER" + +#import "client/mac/Framework/Breakpad.h" + +#include +#import +#include +#include +#include + +#import "client/mac/crash_generation/Inspector.h" +#import "client/mac/handler/exception_handler.h" +#import "client/mac/Framework/Breakpad.h" +#import "client/mac/Framework/OnDemandServer.h" +#import "client/mac/handler/protected_memory_allocator.h" +#include "common/mac/launch_reporter.h" +#import "common/mac/MachIPC.h" +#import "common/simple_string_dictionary.h" + +#if !defined(__EXCEPTIONS) || (__clang__ && !__has_feature(cxx_exceptions)) +// This file uses C++ try/catch (but shouldn't). Duplicate the macros from +// allowing this file to work properly with +// exceptions disabled even when other C++ libraries are used. #undef the try +// and catch macros first in case libstdc++ is in use and has already provided +// its own definitions. +#undef try +#define try if (true) +#undef catch +#define catch(X) if (false) +#endif // __EXCEPTIONS + +using google_breakpad::MachPortSender; +using google_breakpad::MachReceiveMessage; +using google_breakpad::MachSendMessage; +using google_breakpad::ReceivePort; +using google_breakpad::SimpleStringDictionary; + +//============================================================================= +// We want any memory allocations which are used by breakpad during the +// exception handling process (after a crash has happened) to be read-only +// to prevent them from being smashed before a crash occurs. Unfortunately +// we cannot protect against smashes to our exception handling thread's +// stack. +// +// NOTE: Any memory allocations which are not used during the exception +// handling process may be allocated in the normal ways. +// +// The ProtectedMemoryAllocator class provides an Allocate() method which +// we'll using in conjunction with placement operator new() to control +// allocation of C++ objects. Note that we don't use operator delete() +// but instead call the objects destructor directly: object->~ClassName(); +// +ProtectedMemoryAllocator* gMasterAllocator = NULL; +ProtectedMemoryAllocator* gKeyValueAllocator = NULL; +ProtectedMemoryAllocator* gBreakpadAllocator = NULL; + +// Mutex for thread-safe access to the key/value dictionary used by breakpad. +// It's a global instead of an instance variable of Breakpad +// since it can't live in a protected memory area. +pthread_mutex_t gDictionaryMutex; + +//============================================================================= +// Stack-based object for thread-safe access to a memory-protected region. +// It's assumed that normally the memory block (allocated by the allocator) +// is protected (read-only). Creating a stack-based instance of +// ProtectedMemoryLocker will unprotect this block after taking the lock. +// Its destructor will first re-protect the memory then release the lock. +class ProtectedMemoryLocker { + public: + ProtectedMemoryLocker(pthread_mutex_t* mutex, + ProtectedMemoryAllocator* allocator) + : mutex_(mutex), + allocator_(allocator) { + // Lock the mutex + __attribute__((unused)) int rv = pthread_mutex_lock(mutex_); + assert(rv == 0); + + // Unprotect the memory + allocator_->Unprotect(); + } + + ~ProtectedMemoryLocker() { + // First protect the memory + allocator_->Protect(); + + // Then unlock the mutex + __attribute__((unused)) int rv = pthread_mutex_unlock(mutex_); + assert(rv == 0); + } + + private: + ProtectedMemoryLocker(); + ProtectedMemoryLocker(const ProtectedMemoryLocker&); + ProtectedMemoryLocker& operator=(const ProtectedMemoryLocker&); + + pthread_mutex_t* mutex_; + ProtectedMemoryAllocator* allocator_; +}; + +//============================================================================= +class Breakpad { + public: + // factory method + static Breakpad* Create(NSDictionary* parameters) { + // Allocate from our special allocation pool + Breakpad* breakpad = + new (gBreakpadAllocator->Allocate(sizeof(Breakpad))) + Breakpad(); + + if (!breakpad) + return NULL; + + if (!breakpad->Initialize(parameters)) { + // Don't use operator delete() here since we allocated from special pool + breakpad->~Breakpad(); + return NULL; + } + + return breakpad; + } + + ~Breakpad(); + + void SetKeyValue(NSString* key, NSString* value); + NSString* KeyValue(NSString* key); + void RemoveKeyValue(NSString* key); + + void GenerateAndSendReport(); + + void SetFilterCallback(BreakpadFilterCallback callback, void* context) { + filter_callback_ = callback; + filter_callback_context_ = context; + } + + private: + Breakpad() + : handler_(NULL), + config_params_(NULL), + send_and_exit_(true), + filter_callback_(NULL), + filter_callback_context_(NULL) { + inspector_path_[0] = 0; + } + + bool Initialize(NSDictionary* parameters); + bool InitializeInProcess(NSDictionary* parameters); + bool InitializeOutOfProcess(NSDictionary* parameters); + + bool ExtractParameters(NSDictionary* parameters); + + // Dispatches to HandleException() + static bool ExceptionHandlerDirectCallback(void* context, + int exception_type, + int exception_code, + int exception_subcode, + mach_port_t crashing_thread); + + bool HandleException(int exception_type, + int exception_code, + int exception_subcode, + mach_port_t crashing_thread); + + // Dispatches to HandleMinidump(). + // This gets called instead of ExceptionHandlerDirectCallback when running + // with the BREAKPAD_IN_PROCESS option. + static bool HandleMinidumpCallback(const char* dump_dir, + const char* minidump_id, + void* context, + bool succeeded); + + // This is only used when BREAKPAD_IN_PROCESS is YES. + bool HandleMinidump(const char* dump_dir, const char* minidump_id); + + // Since ExceptionHandler (w/o namespace) is defined as typedef in OSX's + // MachineExceptions.h, we have to explicitly name the handler. + google_breakpad::ExceptionHandler* handler_; // The actual handler (STRONG) + + char inspector_path_[PATH_MAX]; // Path to inspector tool + + SimpleStringDictionary* config_params_; // Create parameters (STRONG) + + OnDemandServer inspector_; + + bool send_and_exit_; // Exit after sending, if true + + BreakpadFilterCallback filter_callback_; + void* filter_callback_context_; +}; + +#pragma mark - +#pragma mark Helper functions + +//============================================================================= +// Helper functions + +//============================================================================= +static BOOL IsDebuggerActive() { + BOOL result = NO; + NSUserDefaults* stdDefaults = [NSUserDefaults standardUserDefaults]; + + // We check both defaults and the environment variable here + + BOOL ignoreDebugger = [stdDefaults boolForKey:@IGNORE_DEBUGGER]; + + if (!ignoreDebugger) { + char* ignoreDebuggerStr = getenv(IGNORE_DEBUGGER); + ignoreDebugger = (ignoreDebuggerStr ? strtol(ignoreDebuggerStr, NULL, 10) : 0) != 0; + } + + if (!ignoreDebugger) { + pid_t pid = getpid(); + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, pid}; + int mibSize = sizeof(mib) / sizeof(int); + size_t actualSize; + + if (sysctl(mib, mibSize, NULL, &actualSize, NULL, 0) == 0) { + struct kinfo_proc* info = (struct kinfo_proc*)malloc(actualSize); + + if (info) { + // This comes from looking at the Darwin xnu Kernel + if (sysctl(mib, mibSize, info, &actualSize, NULL, 0) == 0) + result = (info->kp_proc.p_flag & P_TRACED) ? YES : NO; + + free(info); + } + } + } + + return result; +} + +//============================================================================= +bool Breakpad::ExceptionHandlerDirectCallback(void* context, + int exception_type, + int exception_code, + int exception_subcode, + mach_port_t crashing_thread) { + Breakpad* breakpad = (Breakpad*)context; + + // If our context is damaged or something, just return false to indicate that + // the handler should continue without us. + if (!breakpad) + return false; + + return breakpad->HandleException( exception_type, + exception_code, + exception_subcode, + crashing_thread); +} + +//============================================================================= +bool Breakpad::HandleMinidumpCallback(const char* dump_dir, + const char* minidump_id, + void* context, + bool succeeded) { + Breakpad* breakpad = (Breakpad*)context; + + // If our context is damaged or something, just return false to indicate that + // the handler should continue without us. + if (!breakpad || !succeeded) + return false; + + return breakpad->HandleMinidump(dump_dir, minidump_id); +} + +//============================================================================= +#pragma mark - + +#include + +//============================================================================= +// Returns the pathname to the Resources directory for this version of +// Breakpad which we are now running. +// +// Don't make the function static, since _dyld_lookup_and_bind_fully needs a +// simple non-static C name +// +extern "C" { +NSString* GetResourcePath(); +NSString* GetResourcePath() { + NSString* resourcePath = nil; + + // If there are multiple breakpads installed then calling bundleWithIdentifier + // will not work properly, so only use that as a backup plan. + // We want to find the bundle containing the code where this function lives + // and work from there + // + + // Get the pathname to the code which contains this function + Dl_info info; + if (dladdr((const void*)GetResourcePath, &info) != 0) { + NSFileManager* filemgr = [NSFileManager defaultManager]; + NSString* filePath = + [filemgr stringWithFileSystemRepresentation:info.dli_fname + length:strlen(info.dli_fname)]; + NSString* bundlePath = [filePath stringByDeletingLastPathComponent]; + // The "Resources" directory should be in the same directory as the + // executable code, since that's how the Breakpad framework is built. + resourcePath = [bundlePath stringByAppendingPathComponent:@"Resources/"]; + } else { + // fallback plan + NSBundle* bundle = + [NSBundle bundleWithIdentifier:@"com.Google.BreakpadFramework"]; + resourcePath = [bundle resourcePath]; + } + + return resourcePath; +} +} // extern "C" + +//============================================================================= +bool Breakpad::Initialize(NSDictionary* parameters) { + // Initialize + config_params_ = NULL; + handler_ = NULL; + + // Check for debugger + if (IsDebuggerActive()) { + return true; + } + + // Gather any user specified parameters + if (!ExtractParameters(parameters)) { + return false; + } + + if ([[parameters objectForKey:@BREAKPAD_IN_PROCESS] boolValue]) + return InitializeInProcess(parameters); + else + return InitializeOutOfProcess(parameters); +} + +//============================================================================= +bool Breakpad::InitializeInProcess(NSDictionary* parameters) { + handler_ = + new (gBreakpadAllocator->Allocate( + sizeof(google_breakpad::ExceptionHandler))) + google_breakpad::ExceptionHandler( + config_params_->GetValueForKey(BREAKPAD_DUMP_DIRECTORY), + 0, &HandleMinidumpCallback, this, true, 0); + return true; +} + +//============================================================================= +bool Breakpad::InitializeOutOfProcess(NSDictionary* parameters) { + // Get path to Inspector executable. + NSString* inspectorPathString = KeyValue(@BREAKPAD_INSPECTOR_LOCATION); + + // Standardize path (resolve symlinkes, etc.) and escape spaces + inspectorPathString = [inspectorPathString stringByStandardizingPath]; + inspectorPathString = [[inspectorPathString componentsSeparatedByString:@" "] + componentsJoinedByString:@"\\ "]; + + // Create an on-demand server object representing the Inspector. + // In case of a crash, we simply need to call the LaunchOnDemand() + // method on it, then send a mach message to its service port. + // It will then launch and perform a process inspection of our crashed state. + // See the HandleException() method for the details. +#define RECEIVE_PORT_NAME "com.Breakpad.Inspector" + + name_t portName; + snprintf(portName, sizeof(name_t), "%s%d", RECEIVE_PORT_NAME, getpid()); + + // Save the location of the Inspector + strlcpy(inspector_path_, [inspectorPathString fileSystemRepresentation], + sizeof(inspector_path_)); + + // Append a single command-line argument to the Inspector path + // representing the bootstrap name of the launch-on-demand receive port. + // When the Inspector is launched, it can use this to lookup the port + // by calling bootstrap_check_in(). + strlcat(inspector_path_, " ", sizeof(inspector_path_)); + strlcat(inspector_path_, portName, sizeof(inspector_path_)); + + kern_return_t kr = inspector_.Initialize(inspector_path_, + portName, + true); // shutdown on exit + + if (kr != KERN_SUCCESS) { + return false; + } + + // Create the handler (allocating it in our special protected pool) + handler_ = + new (gBreakpadAllocator->Allocate( + sizeof(google_breakpad::ExceptionHandler))) + google_breakpad::ExceptionHandler( + Breakpad::ExceptionHandlerDirectCallback, this, true); + return true; +} + +//============================================================================= +Breakpad::~Breakpad() { + // Note that we don't use operator delete() on these pointers, + // since they were allocated by ProtectedMemoryAllocator objects. + // + if (config_params_) { + config_params_->~SimpleStringDictionary(); + } + + if (handler_) + handler_->~ExceptionHandler(); +} + +//============================================================================= +bool Breakpad::ExtractParameters(NSDictionary* parameters) { + NSUserDefaults* stdDefaults = [NSUserDefaults standardUserDefaults]; + NSString* skipConfirm = [stdDefaults stringForKey:@BREAKPAD_SKIP_CONFIRM]; + NSString* sendAndExit = [stdDefaults stringForKey:@BREAKPAD_SEND_AND_EXIT]; + + NSString* serverType = [parameters objectForKey:@BREAKPAD_SERVER_TYPE]; + NSString* display = [parameters objectForKey:@BREAKPAD_PRODUCT_DISPLAY]; + NSString* product = [parameters objectForKey:@BREAKPAD_PRODUCT]; + NSString* version = [parameters objectForKey:@BREAKPAD_VERSION]; + NSString* urlStr = [parameters objectForKey:@BREAKPAD_URL]; + NSString* interval = [parameters objectForKey:@BREAKPAD_REPORT_INTERVAL]; + NSString* inspectorPathString = + [parameters objectForKey:@BREAKPAD_INSPECTOR_LOCATION]; + NSString* reporterPathString = + [parameters objectForKey:@BREAKPAD_REPORTER_EXE_LOCATION]; + NSString* timeout = [parameters objectForKey:@BREAKPAD_CONFIRM_TIMEOUT]; + NSArray* logFilePaths = [parameters objectForKey:@BREAKPAD_LOGFILES]; + NSString* logFileTailSize = + [parameters objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE]; + NSString* requestUserText = + [parameters objectForKey:@BREAKPAD_REQUEST_COMMENTS]; + NSString* requestEmail = [parameters objectForKey:@BREAKPAD_REQUEST_EMAIL]; + NSString* vendor = + [parameters objectForKey:@BREAKPAD_VENDOR]; + NSString* dumpSubdirectory = + [parameters objectForKey:@BREAKPAD_DUMP_DIRECTORY]; + + NSDictionary* serverParameters = + [parameters objectForKey:@BREAKPAD_SERVER_PARAMETER_DICT]; + + // These may have been set above as user prefs, which take priority. + if (!skipConfirm) { + skipConfirm = [parameters objectForKey:@BREAKPAD_SKIP_CONFIRM]; + } + if (!sendAndExit) { + sendAndExit = [parameters objectForKey:@BREAKPAD_SEND_AND_EXIT]; + } + + if (!product) + product = [parameters objectForKey:@"CFBundleName"]; + + if (!display) { + display = [parameters objectForKey:@"CFBundleDisplayName"]; + if (!display) { + display = product; + } + } + + if (!version) + version = [parameters objectForKey:@"CFBundleVersion"]; + + if (!interval) + interval = @"3600"; + + if (!timeout) + timeout = @"300"; + + if (!logFileTailSize) + logFileTailSize = @"200000"; + + if (!vendor) { + vendor = @"Vendor not specified"; + } + + // Normalize the values. + if (skipConfirm) { + skipConfirm = [skipConfirm uppercaseString]; + + if ([skipConfirm isEqualToString:@"YES"] || + [skipConfirm isEqualToString:@"TRUE"] || + [skipConfirm isEqualToString:@"1"]) + skipConfirm = @"YES"; + else + skipConfirm = @"NO"; + } else { + skipConfirm = @"NO"; + } + + send_and_exit_ = true; + if (sendAndExit) { + sendAndExit = [sendAndExit uppercaseString]; + + if ([sendAndExit isEqualToString:@"NO"] || + [sendAndExit isEqualToString:@"FALSE"] || + [sendAndExit isEqualToString:@"0"]) + send_and_exit_ = false; + } + + if (requestUserText) { + requestUserText = [requestUserText uppercaseString]; + + if ([requestUserText isEqualToString:@"YES"] || + [requestUserText isEqualToString:@"TRUE"] || + [requestUserText isEqualToString:@"1"]) + requestUserText = @"YES"; + else + requestUserText = @"NO"; + } else { + requestUserText = @"NO"; + } + + // Find the helper applications if not specified in user config. + NSString* resourcePath = nil; + if (!inspectorPathString || !reporterPathString) { + resourcePath = GetResourcePath(); + if (!resourcePath) { + return false; + } + } + + // Find Inspector. + if (!inspectorPathString) { + inspectorPathString = + [resourcePath stringByAppendingPathComponent:@"Inspector"]; + } + + // Verify that there is an Inspector tool. + if (![[NSFileManager defaultManager] fileExistsAtPath:inspectorPathString]) { + return false; + } + + // Find Reporter. + if (!reporterPathString) { + reporterPathString = + [resourcePath + stringByAppendingPathComponent:@"crash_report_sender.app"]; + reporterPathString = + [[NSBundle bundleWithPath:reporterPathString] executablePath]; + } + + // Verify that there is a Reporter application. + if (![[NSFileManager defaultManager] + fileExistsAtPath:reporterPathString]) { + return false; + } + + if (!dumpSubdirectory) { + dumpSubdirectory = @""; + } + + // The product, version, and URL are required values. + if (![product length]) { + return false; + } + + if (![version length]) { + return false; + } + + if (![urlStr length]) { + return false; + } + + config_params_ = + new (gKeyValueAllocator->Allocate(sizeof(SimpleStringDictionary)) ) + SimpleStringDictionary(); + + SimpleStringDictionary& dictionary = *config_params_; + + dictionary.SetKeyValue(BREAKPAD_SERVER_TYPE, [serverType UTF8String]); + dictionary.SetKeyValue(BREAKPAD_PRODUCT_DISPLAY, [display UTF8String]); + dictionary.SetKeyValue(BREAKPAD_PRODUCT, [product UTF8String]); + dictionary.SetKeyValue(BREAKPAD_VERSION, [version UTF8String]); + dictionary.SetKeyValue(BREAKPAD_URL, [urlStr UTF8String]); + dictionary.SetKeyValue(BREAKPAD_REPORT_INTERVAL, [interval UTF8String]); + dictionary.SetKeyValue(BREAKPAD_SKIP_CONFIRM, [skipConfirm UTF8String]); + dictionary.SetKeyValue(BREAKPAD_CONFIRM_TIMEOUT, [timeout UTF8String]); + dictionary.SetKeyValue(BREAKPAD_INSPECTOR_LOCATION, + [inspectorPathString fileSystemRepresentation]); + dictionary.SetKeyValue(BREAKPAD_REPORTER_EXE_LOCATION, + [reporterPathString fileSystemRepresentation]); + dictionary.SetKeyValue(BREAKPAD_LOGFILE_UPLOAD_SIZE, + [logFileTailSize UTF8String]); + dictionary.SetKeyValue(BREAKPAD_REQUEST_COMMENTS, + [requestUserText UTF8String]); + dictionary.SetKeyValue(BREAKPAD_REQUEST_EMAIL, [requestEmail UTF8String]); + dictionary.SetKeyValue(BREAKPAD_VENDOR, [vendor UTF8String]); + dictionary.SetKeyValue(BREAKPAD_DUMP_DIRECTORY, + [dumpSubdirectory UTF8String]); + + struct timeval tv; + gettimeofday(&tv, NULL); + char timeStartedString[32]; + sprintf(timeStartedString, "%zd", tv.tv_sec); + dictionary.SetKeyValue(BREAKPAD_PROCESS_START_TIME, + timeStartedString); + + if (logFilePaths) { + char logFileKey[255]; + for(unsigned int i = 0; i < [logFilePaths count]; i++) { + sprintf(logFileKey,"%s%d", BREAKPAD_LOGFILE_KEY_PREFIX, i); + dictionary.SetKeyValue(logFileKey, + [[logFilePaths objectAtIndex:i] + fileSystemRepresentation]); + } + } + + if (serverParameters) { + // For each key-value pair, call BreakpadAddUploadParameter() + NSEnumerator* keyEnumerator = [serverParameters keyEnumerator]; + NSString* aParameter; + while ((aParameter = [keyEnumerator nextObject])) { + BreakpadAddUploadParameter(this, aParameter, + [serverParameters objectForKey:aParameter]); + } + } + return true; +} + +//============================================================================= +void Breakpad::SetKeyValue(NSString* key, NSString* value) { + // We allow nil values. This is the same as removing the keyvalue. + if (!config_params_ || !key) + return; + + config_params_->SetKeyValue([key UTF8String], [value UTF8String]); +} + +//============================================================================= +NSString* Breakpad::KeyValue(NSString* key) { + if (!config_params_ || !key) + return nil; + + const char* value = config_params_->GetValueForKey([key UTF8String]); + return value ? [NSString stringWithUTF8String:value] : nil; +} + +//============================================================================= +void Breakpad::RemoveKeyValue(NSString* key) { + if (!config_params_ || !key) return; + + config_params_->RemoveKey([key UTF8String]); +} + +//============================================================================= +void Breakpad::GenerateAndSendReport() { + config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "YES"); + HandleException(0, 0, 0, mach_thread_self()); + config_params_->SetKeyValue(BREAKPAD_ON_DEMAND, "NO"); +} + +//============================================================================= +bool Breakpad::HandleException(int exception_type, + int exception_code, + int exception_subcode, + mach_port_t crashing_thread) { + if (filter_callback_) { + bool should_handle = filter_callback_(exception_type, + exception_code, + crashing_thread, + filter_callback_context_); + if (!should_handle) return false; + } + + // We need to reset the memory protections to be read/write, + // since LaunchOnDemand() requires changing state. + gBreakpadAllocator->Unprotect(); + // Configure the server to launch when we message the service port. + // The reason we do this here, rather than at startup, is that we + // can leak a bootstrap service entry if this method is called and + // there never ends up being a crash. + inspector_.LaunchOnDemand(); + gBreakpadAllocator->Protect(); + + // The Inspector should send a message to this port to verify it + // received our information and has finished the inspection. + ReceivePort acknowledge_port; + + // Send initial information to the Inspector. + MachSendMessage message(kMsgType_InspectorInitialInfo); + message.AddDescriptor(mach_task_self()); // our task + message.AddDescriptor(crashing_thread); // crashing thread + message.AddDescriptor(mach_thread_self()); // exception-handling thread + message.AddDescriptor(acknowledge_port.GetPort());// message receive port + + InspectorInfo info; + info.exception_type = exception_type; + info.exception_code = exception_code; + info.exception_subcode = exception_subcode; + info.parameter_count = config_params_->GetCount(); + message.SetData(&info, sizeof(info)); + + MachPortSender sender(inspector_.GetServicePort()); + + kern_return_t result = sender.SendMessage(message, 2000); + + if (result == KERN_SUCCESS) { + // Now, send a series of key-value pairs to the Inspector. + const SimpleStringDictionary::Entry* entry = NULL; + SimpleStringDictionary::Iterator iter(*config_params_); + + while ( (entry = iter.Next()) ) { + KeyValueMessageData keyvalue_data(*entry); + + MachSendMessage keyvalue_message(kMsgType_InspectorKeyValuePair); + keyvalue_message.SetData(&keyvalue_data, sizeof(keyvalue_data)); + + result = sender.SendMessage(keyvalue_message, 2000); + + if (result != KERN_SUCCESS) { + break; + } + } + + if (result == KERN_SUCCESS) { + // Wait for acknowledgement that the inspection has finished. + MachReceiveMessage acknowledge_messsage; + result = acknowledge_port.WaitForMessage(&acknowledge_messsage, 5000); + } + } + +#if VERBOSE + PRINT_MACH_RESULT(result, "Breakpad: SendMessage "); + printf("Breakpad: Inspector service port = %#x\n", + inspector_.GetServicePort()); +#endif + + // If we don't want any forwarding, return true here to indicate that we've + // processed things as much as we want. + if (send_and_exit_) return true; + + return false; +} + +//============================================================================= +bool Breakpad::HandleMinidump(const char* dump_dir, const char* minidump_id) { + google_breakpad::ConfigFile config_file; + config_file.WriteFile(dump_dir, config_params_, dump_dir, minidump_id); + google_breakpad::LaunchReporter( + config_params_->GetValueForKey(BREAKPAD_REPORTER_EXE_LOCATION), + config_file.GetFilePath()); + return true; +} + +//============================================================================= +//============================================================================= + +#pragma mark - +#pragma mark Public API + +//============================================================================= +BreakpadRef BreakpadCreate(NSDictionary* parameters) { + try { + // This is confusing. Our two main allocators for breakpad memory are: + // - gKeyValueAllocator for the key/value memory + // - gBreakpadAllocator for the Breakpad, ExceptionHandler, and other + // breakpad allocations which are accessed at exception handling time. + // + // But in order to avoid these two allocators themselves from being smashed, + // we'll protect them as well by allocating them with gMasterAllocator. + // + // gMasterAllocator itself will NOT be protected, but this doesn't matter, + // since once it does its allocations and locks the memory, smashes to itself + // don't affect anything we care about. + gMasterAllocator = + new ProtectedMemoryAllocator(sizeof(ProtectedMemoryAllocator) * 2); + + gKeyValueAllocator = + new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator))) + ProtectedMemoryAllocator(sizeof(SimpleStringDictionary)); + + // Create a mutex for use in accessing the SimpleStringDictionary + int mutexResult = pthread_mutex_init(&gDictionaryMutex, NULL); + if (mutexResult == 0) { + + // With the current compiler, gBreakpadAllocator is allocating 1444 bytes. + // Let's round up to the nearest page size. + // + int breakpad_pool_size = 4096; + + /* + sizeof(Breakpad) + + sizeof(google_breakpad::ExceptionHandler) + + sizeof( STUFF ALLOCATED INSIDE ExceptionHandler ) + */ + + gBreakpadAllocator = + new (gMasterAllocator->Allocate(sizeof(ProtectedMemoryAllocator))) + ProtectedMemoryAllocator(breakpad_pool_size); + + // Stack-based autorelease pool for Breakpad::Create() obj-c code. + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + Breakpad* breakpad = Breakpad::Create(parameters); + + if (breakpad) { + // Make read-only to protect against memory smashers + gMasterAllocator->Protect(); + gKeyValueAllocator->Protect(); + gBreakpadAllocator->Protect(); + // Can uncomment this line to figure out how much space was actually + // allocated using this allocator + // printf("gBreakpadAllocator allocated size = %d\n", + // gBreakpadAllocator->GetAllocatedSize() ); + [pool release]; + return (BreakpadRef)breakpad; + } + + [pool release]; + } + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadCreate() : error\n"); + } + + if (gKeyValueAllocator) { + gKeyValueAllocator->~ProtectedMemoryAllocator(); + gKeyValueAllocator = NULL; + } + + if (gBreakpadAllocator) { + gBreakpadAllocator->~ProtectedMemoryAllocator(); + gBreakpadAllocator = NULL; + } + + delete gMasterAllocator; + gMasterAllocator = NULL; + + return NULL; +} + +//============================================================================= +void BreakpadRelease(BreakpadRef ref) { + try { + Breakpad* breakpad = (Breakpad*)ref; + + if (gMasterAllocator) { + gMasterAllocator->Unprotect(); + gKeyValueAllocator->Unprotect(); + gBreakpadAllocator->Unprotect(); + + breakpad->~Breakpad(); + + // Unfortunately, it's not possible to deallocate this stuff + // because the exception handling thread is still finishing up + // asynchronously at this point... OK, it could be done with + // locks, etc. But since BreakpadRelease() should usually only + // be called right before the process exits, it's not worth + // deallocating this stuff. +#if 0 + gKeyValueAllocator->~ProtectedMemoryAllocator(); + gBreakpadAllocator->~ProtectedMemoryAllocator(); + delete gMasterAllocator; + + gMasterAllocator = NULL; + gKeyValueAllocator = NULL; + gBreakpadAllocator = NULL; +#endif + + pthread_mutex_destroy(&gDictionaryMutex); + } + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadRelease() : error\n"); + } +} + +//============================================================================= +void BreakpadSetKeyValue(BreakpadRef ref, NSString* key, NSString* value) { + try { + // Not called at exception time + Breakpad* breakpad = (Breakpad*)ref; + + if (breakpad && key && gKeyValueAllocator) { + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); + + breakpad->SetKeyValue(key, value); + } + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadSetKeyValue() : error\n"); + } +} + +void BreakpadAddUploadParameter(BreakpadRef ref, + NSString* key, + NSString* value) { + // The only difference, internally, between an upload parameter and + // a key value one that is set with BreakpadSetKeyValue is that we + // prepend the keyname with a special prefix. This informs the + // crash sender that the parameter should be sent along with the + // POST of the crash dump upload. + try { + Breakpad* breakpad = (Breakpad*)ref; + + if (breakpad && key && gKeyValueAllocator) { + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); + + NSString* prefixedKey = [@BREAKPAD_SERVER_PARAMETER_PREFIX + stringByAppendingString:key]; + breakpad->SetKeyValue(prefixedKey, value); + } + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadSetKeyValue() : error\n"); + } +} + +void BreakpadRemoveUploadParameter(BreakpadRef ref, + NSString* key) { + try { + // Not called at exception time + Breakpad* breakpad = (Breakpad*)ref; + + if (breakpad && key && gKeyValueAllocator) { + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); + + NSString* prefixedKey = [NSString stringWithFormat:@"%@%@", + @BREAKPAD_SERVER_PARAMETER_PREFIX, key]; + breakpad->RemoveKeyValue(prefixedKey); + } + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadRemoveKeyValue() : error\n"); + } +} +//============================================================================= +NSString* BreakpadKeyValue(BreakpadRef ref, NSString* key) { + NSString* value = nil; + + try { + // Not called at exception time + Breakpad* breakpad = (Breakpad*)ref; + + if (!breakpad || !key || !gKeyValueAllocator) + return nil; + + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); + + value = breakpad->KeyValue(key); + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadKeyValue() : error\n"); + } + + return value; +} + +//============================================================================= +void BreakpadRemoveKeyValue(BreakpadRef ref, NSString* key) { + try { + // Not called at exception time + Breakpad* breakpad = (Breakpad*)ref; + + if (breakpad && key && gKeyValueAllocator) { + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); + + breakpad->RemoveKeyValue(key); + } + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadRemoveKeyValue() : error\n"); + } +} + +//============================================================================= +void BreakpadGenerateAndSendReport(BreakpadRef ref) { + try { + Breakpad* breakpad = (Breakpad*)ref; + + if (breakpad && gKeyValueAllocator) { + ProtectedMemoryLocker locker(&gDictionaryMutex, gKeyValueAllocator); + + gBreakpadAllocator->Unprotect(); + breakpad->GenerateAndSendReport(); + gBreakpadAllocator->Protect(); + } + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadGenerateAndSendReport() : error\n"); + } +} + +//============================================================================= +void BreakpadSetFilterCallback(BreakpadRef ref, + BreakpadFilterCallback callback, + void* context) { + + try { + Breakpad* breakpad = (Breakpad*)ref; + + if (breakpad && gBreakpadAllocator) { + // share the dictionary mutex here (we really don't need a mutex) + ProtectedMemoryLocker locker(&gDictionaryMutex, gBreakpadAllocator); + + breakpad->SetFilterCallback(callback, context); + } + } catch(...) { // don't let exceptions leave this C API + fprintf(stderr, "BreakpadSetFilterCallback() : error\n"); + } +} + +//============================================================================ +void BreakpadAddLogFile(BreakpadRef ref, NSString* logPathname) { + int logFileCounter = 0; + + NSString* logFileKey = [NSString stringWithFormat:@"%@%d", + @BREAKPAD_LOGFILE_KEY_PREFIX, + logFileCounter]; + + NSString* existingLogFilename = nil; + existingLogFilename = BreakpadKeyValue(ref, logFileKey); + // Find the first log file key that we can use by testing for existence + while (existingLogFilename) { + if ([existingLogFilename isEqualToString:logPathname]) { + return; + } + logFileCounter++; + logFileKey = [NSString stringWithFormat:@"%@%d", + @BREAKPAD_LOGFILE_KEY_PREFIX, + logFileCounter]; + existingLogFilename = BreakpadKeyValue(ref, logFileKey); + } + + BreakpadSetKeyValue(ref, logFileKey, logPathname); +} diff --git a/shared/sentry/external/breakpad/src/client/mac/Framework/Breakpad_Prefix.pch b/shared/sentry/external/breakpad/src/client/mac/Framework/Breakpad_Prefix.pch new file mode 100644 index 000000000..729866635 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/Framework/Breakpad_Prefix.pch @@ -0,0 +1,8 @@ +// +// Prefix header for all source files of the 'Breakpad' target in the +// 'Breakpad' project. +// + +#ifdef __OBJC__ + #import +#endif diff --git a/shared/sentry/external/breakpad/src/client/mac/Framework/Info.plist b/shared/sentry/external/breakpad/src/client/mac/Framework/Info.plist new file mode 100644 index 000000000..e43baddc0 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/Framework/Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleName + ${PRODUCT_NAME} + CFBundleIconFile + + CFBundleIdentifier + com.googlecode.google-breakpad + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + FMWK + CFBundleSignature + ???? + CFBundleVersion + 1.0 + NSPrincipalClass + + + diff --git a/shared/sentry/external/breakpad/src/client/mac/Framework/OnDemandServer.h b/shared/sentry/external/breakpad/src/client/mac/Framework/OnDemandServer.h new file mode 100644 index 000000000..be0d2b79a --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/Framework/OnDemandServer.h @@ -0,0 +1,145 @@ +// 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 +#include +#include +#include +#include +#include + +//============================================================================== +// class OnDemandServer : +// A basic on-demand server launcher supporting a single named service port +// +// Example Usage : +// +// kern_return_t result; +// OnDemandServer* server = OnDemandServer::Create("/tmp/myserver", +// "com.MyCompany.MyServiceName", +// true, +// &result); +// +// if (server) { +// server->LaunchOnDemand(); +// mach_port_t service_port = GetServicePort(); +// +// // Send a mach message to service_port and "myserver" will be launched +// } +// +// +// ---- Now in the server code ---- +// +// // "myserver" should get the service port and read the message which +// // launched it: +// mach_port_t service_rcv_port_; +// kern_return_t kr = bootstrap_check_in(bootstrap_port, +// "com.MyCompany.MyServiceName", +// &service_rcv_port_); +// // mach_msg() read service_rcv_port_ .... +// +// .... +// +// // Later "myserver" may want to unregister the service if it doesn't +// // want its bootstrap service to stick around after it exits. +// +// // DO NOT use mach_port_deallocate() here -- it will fail and the +// // following bootstrap_register() will also fail leaving our service +// // name hanging around forever (until reboot) +// kern_return_t kr = mach_port_destroy(mach_task_self(), service_rcv_port_); +// +// kr = bootstrap_register(bootstrap_port, +// "com.MyCompany.MyServiceName", +// MACH_PORT_NULL); + +class OnDemandServer { + public: + // must call Initialize() to be useful + OnDemandServer() + : server_port_(MACH_PORT_NULL), + service_port_(MACH_PORT_NULL), + unregister_on_cleanup_(true) { + } + + // Creates the bootstrap server and service + kern_return_t Initialize(const char* server_command, + const char* service_name, + bool unregister_on_cleanup); + + // Returns an OnDemandServer object if successful, or NULL if there's + // an error. The error result will be returned in out_result. + // + // server_command : the full path name including optional command-line + // arguments to the executable representing the server + // + // service_name : represents service name + // something like "com.company.ServiceName" + // + // unregister_on_cleanup : if true, unregisters the service name + // when the OnDemandServer is deleted -- unregistering will + // ONLY be possible if LaunchOnDemand() has NOT been called. + // If false, then the service will continue to be registered + // even after the current process quits. + // + // out_result : if non-NULL, returns the result + // this value will be KERN_SUCCESS if Create() returns non-NULL + // + static OnDemandServer* Create(const char *server_command, + const char* service_name, + bool unregister_on_cleanup, + kern_return_t* out_result); + + // Cleans up and if LaunchOnDemand() has not yet been called then + // the bootstrap service will be unregistered. + ~OnDemandServer(); + + // This must be called if we intend to commit to launching the server + // by sending a mach message to our service port. Do not call it otherwise + // or it will be difficult (impossible?) to unregister the service name. + void LaunchOnDemand(); + + // This is the port we need to send a mach message to after calling + // LaunchOnDemand(). Sending a message causing an immediate launch + // of the server + mach_port_t GetServicePort() { return service_port_; } + + private: + // Disallow copy constructor + OnDemandServer(const OnDemandServer&); + + // Cleans up and if LaunchOnDemand() has not yet been called then + // the bootstrap service will be unregistered. + void Unregister(); + + name_t service_name_; + + mach_port_t server_port_; + mach_port_t service_port_; + bool unregister_on_cleanup_; +}; diff --git a/shared/sentry/external/breakpad/src/client/mac/Framework/OnDemandServer.mm b/shared/sentry/external/breakpad/src/client/mac/Framework/OnDemandServer.mm new file mode 100644 index 000000000..ee934ec93 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/Framework/OnDemandServer.mm @@ -0,0 +1,190 @@ +// 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. + +#import "OnDemandServer.h" + +#import "Breakpad.h" +#include "common/mac/bootstrap_compat.h" + +#if DEBUG + #define PRINT_MACH_RESULT(result_, message_) \ + printf(message_"%s (%d)\n", mach_error_string(result_), result_ ); +#if defined(MAC_OS_X_VERSION_10_5) && \ + MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + #define PRINT_BOOTSTRAP_RESULT(result_, message_) \ + printf(message_"%s (%d)\n", bootstrap_strerror(result_), result_ ); +#else + #define PRINT_BOOTSTRAP_RESULT(result_, message_) \ + PRINT_MACH_RESULT(result_, message_) +#endif +#else + #define PRINT_MACH_RESULT(result_, message_) + #define PRINT_BOOTSTRAP_RESULT(result_, message_) +#endif + +//============================================================================== +OnDemandServer* OnDemandServer::Create(const char* server_command, + const char* service_name, + bool unregister_on_cleanup, + kern_return_t* out_result) { + OnDemandServer* server = new OnDemandServer(); + + if (!server) return NULL; + + kern_return_t result = server->Initialize(server_command, + service_name, + unregister_on_cleanup); + + if (out_result) { + *out_result = result; + } + + if (result == KERN_SUCCESS) { + return server; + } + + delete server; + return NULL; +} + +//============================================================================== +kern_return_t OnDemandServer::Initialize(const char* server_command, + const char* service_name, + bool unregister_on_cleanup) { + unregister_on_cleanup_ = unregister_on_cleanup; + + mach_port_t self_task = mach_task_self(); + + mach_port_t self_bootstrap_port; + kern_return_t kr = task_get_bootstrap_port(self_task, &self_bootstrap_port); + if (kr != KERN_SUCCESS) { + PRINT_MACH_RESULT(kr, "task_get_bootstrap_port(): "); + return kr; + } + + mach_port_t bootstrap_subset_port; + kr = bootstrap_subset(self_bootstrap_port, self_task, &bootstrap_subset_port); + if (kr != BOOTSTRAP_SUCCESS) { + PRINT_BOOTSTRAP_RESULT(kr, "bootstrap_subset(): "); + return kr; + } + + // The inspector will be invoked with its bootstrap port set to the subset, + // but the sender will need access to the original bootstrap port. Although + // the original port is the subset's parent, bootstrap_parent can't be used + // because it requires extra privileges. Stash the original bootstrap port + // in the subset by registering it under a known name. The inspector will + // recover this port and set it as its own bootstrap port in Inspector.mm + // Inspector::ResetBootstrapPort. + kr = breakpad::BootstrapRegister( + bootstrap_subset_port, + const_cast(BREAKPAD_BOOTSTRAP_PARENT_PORT), + self_bootstrap_port); + if (kr != BOOTSTRAP_SUCCESS) { + PRINT_BOOTSTRAP_RESULT(kr, "bootstrap_register(): "); + return kr; + } + + kr = bootstrap_create_server(bootstrap_subset_port, + const_cast(server_command), + geteuid(), // server uid + true, + &server_port_); + if (kr != BOOTSTRAP_SUCCESS) { + PRINT_BOOTSTRAP_RESULT(kr, "bootstrap_create_server(): "); + return kr; + } + + strlcpy(service_name_, service_name, sizeof(service_name_)); + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + // Create a service called service_name, and return send rights to + // that port in service_port_. + kr = bootstrap_create_service(server_port_, + const_cast(service_name), + &service_port_); +#pragma clang diagnostic pop + if (kr != BOOTSTRAP_SUCCESS) { + PRINT_BOOTSTRAP_RESULT(kr, "bootstrap_create_service(): "); + + // perhaps the service has already been created - try to look it up + kr = bootstrap_look_up(self_bootstrap_port, (char*)service_name, + &service_port_); + + if (kr != BOOTSTRAP_SUCCESS) { + PRINT_BOOTSTRAP_RESULT(kr, "bootstrap_look_up(): "); + Unregister(); // clean up server port + return kr; + } + } + + return KERN_SUCCESS; +} + +//============================================================================== +OnDemandServer::~OnDemandServer() { + if (unregister_on_cleanup_) { + Unregister(); + } +} + +//============================================================================== +void OnDemandServer::LaunchOnDemand() { + // We need to do this, since the launched server is another process + // and holding on to this port delays launching until the current process + // exits! + mach_port_deallocate(mach_task_self(), server_port_); + server_port_ = MACH_PORT_DEAD; + + // Now, the service is still registered and all we need to do is send + // a mach message to the service port in order to launch the server. +} + +//============================================================================== +void OnDemandServer::Unregister() { + if (service_port_ != MACH_PORT_NULL) { + mach_port_deallocate(mach_task_self(), service_port_); + service_port_ = MACH_PORT_NULL; + } + + if (server_port_ != MACH_PORT_NULL) { + // unregister the service + kern_return_t kr = breakpad::BootstrapRegister(server_port_, + service_name_, + MACH_PORT_NULL); + + if (kr != KERN_SUCCESS) { + PRINT_MACH_RESULT(kr, "Breakpad UNREGISTER : bootstrap_register() : "); + } + + mach_port_deallocate(mach_task_self(), server_port_); + server_port_ = MACH_PORT_NULL; + } +} diff --git a/shared/sentry/external/breakpad/src/client/mac/UnitTests-Info.plist b/shared/sentry/external/breakpad/src/client/mac/UnitTests-Info.plist new file mode 100644 index 000000000..65013556d --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/UnitTests-Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.yourcompany.${PRODUCT_NAME:identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + BNDL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + + diff --git a/shared/sentry/external/breakpad/src/client/mac/crash_generation/ConfigFile.h b/shared/sentry/external/breakpad/src/client/mac/crash_generation/ConfigFile.h new file mode 100644 index 000000000..11bc2e434 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/crash_generation/ConfigFile.h @@ -0,0 +1,83 @@ +// 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 that can persist a SimpleStringDictionary to disk. + +#import + +#include "common/simple_string_dictionary.h" + +namespace google_breakpad { + +BOOL EnsureDirectoryPathExists(NSString* dirPath); + +//============================================================================= +class ConfigFile { + public: + ConfigFile() { + config_file_ = -1; + config_file_path_[0] = 0; + has_created_file_ = false; + } + + ~ConfigFile() { + } + + void WriteFile(const char* directory, + const SimpleStringDictionary* configurationParameters, + const char* dump_dir, + const char* minidump_id); + + const char* GetFilePath() { return config_file_path_; } + + void Unlink() { + if (config_file_ != -1) + unlink(config_file_path_); + + config_file_ = -1; + } + + private: + BOOL WriteData(const void* data, size_t length); + + BOOL AppendConfigData(const char* key, + const void* data, + size_t length); + + BOOL AppendConfigString(const char* key, + const char* value); + + BOOL AppendCrashTimeParameters(const char* processStartTimeString); + + int config_file_; // descriptor for config file + char config_file_path_[PATH_MAX]; // Path to configuration file + bool has_created_file_; +}; + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/mac/crash_generation/ConfigFile.mm b/shared/sentry/external/breakpad/src/client/mac/crash_generation/ConfigFile.mm new file mode 100644 index 000000000..57d07590a --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/crash_generation/ConfigFile.mm @@ -0,0 +1,167 @@ +// 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 that can persist a SimpleStringDictionary to disk. + +#import "client/mac/crash_generation/ConfigFile.h" + +#import +#include +#include + +#import "client/apple/Framework/BreakpadDefines.h" +#import "common/mac/GTMDefines.h" + + +namespace google_breakpad { + +//============================================================================= +BOOL EnsureDirectoryPathExists(NSString* dirPath) { + NSFileManager* mgr = [NSFileManager defaultManager]; + + NSDictionary* attrs = + [NSDictionary dictionaryWithObject:[NSNumber numberWithUnsignedLong:0750] + forKey:NSFilePosixPermissions]; + + return [mgr createDirectoryAtPath:dirPath + withIntermediateDirectories:YES + attributes:attrs + error:nil]; +} + +//============================================================================= +BOOL ConfigFile::WriteData(const void* data, size_t length) { + size_t result = write(config_file_, data, length); + + return result == length; +} + +//============================================================================= +BOOL ConfigFile::AppendConfigData(const char* key, + const void* data, size_t length) { + assert(config_file_ != -1); + + if (!key) { + return NO; + } + + if (!data) { + return NO; + } + + // Write the key, \n, length of data (ascii integer), \n, data + char buffer[16]; + char nl = '\n'; + BOOL result = WriteData(key, strlen(key)); + + snprintf(buffer, sizeof(buffer) - 1, "\n%lu\n", length); + result &= WriteData(buffer, strlen(buffer)); + result &= WriteData(data, length); + result &= WriteData(&nl, 1); + return result; +} + +//============================================================================= +BOOL ConfigFile::AppendConfigString(const char* key, + const char* value) { + return AppendConfigData(key, value, strlen(value)); +} + +//============================================================================= +BOOL ConfigFile::AppendCrashTimeParameters(const char* processStartTimeString) { + // Set process uptime parameter + struct timeval tv; + gettimeofday(&tv, NULL); + + char processUptimeString[32], processCrashtimeString[32]; + // Set up time if we've received the start time. + if (processStartTimeString) { + time_t processStartTime = strtol(processStartTimeString, NULL, 10); + time_t processUptime = tv.tv_sec - processStartTime; + // Store the uptime in milliseconds. + sprintf(processUptimeString, "%llu", + static_cast(processUptime) * 1000); + if (!AppendConfigString(BREAKPAD_PROCESS_UP_TIME, processUptimeString)) + return false; + } + + sprintf(processCrashtimeString, "%zd", tv.tv_sec); + return AppendConfigString(BREAKPAD_PROCESS_CRASH_TIME, + processCrashtimeString); +} + +//============================================================================= +void ConfigFile::WriteFile(const char* directory, + const SimpleStringDictionary* configurationParameters, + const char* dump_dir, + const char* minidump_id) { + + assert(config_file_ == -1); + + // Open and write out configuration file preamble + if (directory) { + snprintf(config_file_path_, sizeof(config_file_path_), "%s/Config-XXXXXX", + directory); + } else { + strlcpy(config_file_path_, "/tmp/Config-XXXXXX", + sizeof(config_file_path_)); + } + config_file_ = mkstemp(config_file_path_); + + if (config_file_ == -1) { + return; + } + + has_created_file_ = true; + + // Add the minidump dir + AppendConfigString(kReporterMinidumpDirectoryKey, dump_dir); + AppendConfigString(kReporterMinidumpIDKey, minidump_id); + + // Write out the configuration parameters + BOOL result = YES; + const SimpleStringDictionary& dictionary = *configurationParameters; + + const SimpleStringDictionary::Entry* entry = NULL; + SimpleStringDictionary::Iterator iter(dictionary); + + while ((entry = iter.Next())) { + result = AppendConfigString(entry->key, entry->value); + + if (!result) + break; + } + AppendCrashTimeParameters( + configurationParameters->GetValueForKey(BREAKPAD_PROCESS_START_TIME)); + + close(config_file_); + config_file_ = -1; +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/mac/crash_generation/Inspector.h b/shared/sentry/external/breakpad/src/client/mac/crash_generation/Inspector.h new file mode 100644 index 000000000..c96711363 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/crash_generation/Inspector.h @@ -0,0 +1,162 @@ +// 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. +// +// Interface file between the Breakpad.framework and +// the Inspector process. + +#include "common/simple_string_dictionary.h" + +#import +#include + +#import "client/mac/crash_generation/ConfigFile.h" +#import "client/mac/handler/minidump_generator.h" + + +// Types of mach messsages (message IDs) +enum { + kMsgType_InspectorInitialInfo = 0, // data is InspectorInfo + kMsgType_InspectorKeyValuePair = 1, // data is KeyValueMessageData + kMsgType_InspectorAcknowledgement = 2 // no data sent +}; + +// Initial information sent from the crashed process by +// Breakpad.framework to the Inspector process +// The mach message with this struct as data will also include +// several descriptors for sending mach port rights to the crashed +// task, etc. +struct InspectorInfo { + int exception_type; + int exception_code; + int exception_subcode; + unsigned int parameter_count; // key-value pairs +}; + +// Key/value message data to be sent to the Inspector +struct KeyValueMessageData { + public: + KeyValueMessageData() {} + explicit KeyValueMessageData( + const google_breakpad::SimpleStringDictionary::Entry& inEntry) { + strlcpy(key, inEntry.key, sizeof(key) ); + strlcpy(value, inEntry.value, sizeof(value) ); + } + + char key[google_breakpad::SimpleStringDictionary::key_size]; + char value[google_breakpad::SimpleStringDictionary::value_size]; +}; + +using std::string; +using google_breakpad::MinidumpGenerator; + +namespace google_breakpad { + +//============================================================================= +class MinidumpLocation { + public: + MinidumpLocation(NSString* minidumpDir) { + // Ensure that the path exists. Fallback to /tmp if unable to locate path. + assert(minidumpDir); + if (!EnsureDirectoryPathExists(minidumpDir)) { + minidumpDir = @"/tmp"; + } + + strlcpy(minidump_dir_path_, [minidumpDir fileSystemRepresentation], + sizeof(minidump_dir_path_)); + + // now generate a unique ID + string dump_path(minidump_dir_path_); + string next_minidump_id; + + string next_minidump_path_ = + (MinidumpGenerator::UniqueNameInDirectory(dump_path, &next_minidump_id)); + + strlcpy(minidump_id_, next_minidump_id.c_str(), sizeof(minidump_id_)); + } + + const char* GetPath() { return minidump_dir_path_; } + const char* GetID() { return minidump_id_; } + + private: + char minidump_dir_path_[PATH_MAX]; // Path to minidump directory + char minidump_id_[128]; +}; + +//============================================================================= +class Inspector { + public: + Inspector() {} + + // given a bootstrap service name, receives mach messages + // from a crashed process, then inspects it, creates a minidump file + // and asks the user if he wants to upload it to a server. + void Inspect(const char* receive_port_name); + + private: + // The Inspector is invoked with its bootstrap port set to the bootstrap + // subset established in OnDemandServer.mm OnDemandServer::Initialize. + // For proper communication with the system, the sender (which will inherit + // the Inspector's bootstrap port) needs the per-session bootstrap namespace + // available directly in its bootstrap port. OnDemandServer stashed this + // port into the subset namespace under a special name. ResetBootstrapPort + // recovers this port and switches this task to use it as its own bootstrap + // (ensuring that children like the sender will inherit it), and saves the + // subset in bootstrap_subset_port_ for use by ServiceCheckIn and + // ServiceCheckOut. + kern_return_t ResetBootstrapPort(); + + kern_return_t ServiceCheckIn(const char* receive_port_name); + kern_return_t ServiceCheckOut(const char* receive_port_name); + + kern_return_t ReadMessages(); + + bool InspectTask(); + kern_return_t SendAcknowledgement(); + + // The bootstrap port in which the inspector is registered and into which it + // must check in. + mach_port_t bootstrap_subset_port_; + + mach_port_t service_rcv_port_; + + int exception_type_; + int exception_code_; + int exception_subcode_; + mach_port_t remote_task_; + mach_port_t crashing_thread_; + mach_port_t handler_thread_; + mach_port_t ack_port_; + + SimpleStringDictionary config_params_; + + ConfigFile config_file_; +}; + + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/mac/crash_generation/Inspector.mm b/shared/sentry/external/breakpad/src/client/mac/crash_generation/Inspector.mm new file mode 100644 index 000000000..d5fc29e02 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/crash_generation/Inspector.mm @@ -0,0 +1,362 @@ +// 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. +// +// Utility that can inspect another process and write a crash dump + +#include +#include +#include +#include +#include +#include + +#import "client/mac/crash_generation/Inspector.h" + +#import "client/mac/Framework/Breakpad.h" +#import "client/mac/handler/minidump_generator.h" + +#import "common/mac/MachIPC.h" +#include "common/mac/bootstrap_compat.h" +#include "common/mac/launch_reporter.h" + +#import "GTMDefines.h" + +#import + +namespace google_breakpad { + +//============================================================================= +void Inspector::Inspect(const char* receive_port_name) { + kern_return_t result = ResetBootstrapPort(); + if (result != KERN_SUCCESS) { + return; + } + + result = ServiceCheckIn(receive_port_name); + + if (result == KERN_SUCCESS) { + result = ReadMessages(); + + if (result == KERN_SUCCESS) { + // Inspect the task and write a minidump file. + bool wrote_minidump = InspectTask(); + + // Send acknowledgement to the crashed process that the inspection + // has finished. It will then be able to cleanly exit. + // The return value is ignored because failure isn't fatal. If the process + // didn't get the message there's nothing we can do, and we still want to + // send the report. + SendAcknowledgement(); + + if (wrote_minidump) { + // Ask the user if he wants to upload the crash report to a server, + // and do so if he agrees. + LaunchReporter( + config_params_.GetValueForKey(BREAKPAD_REPORTER_EXE_LOCATION), + config_file_.GetFilePath()); + } else { + fprintf(stderr, "Inspection of crashed process failed\n"); + } + + // Now that we're done reading messages, cleanup the service, but only + // if there was an actual exception + // Otherwise, it means the dump was generated on demand and the process + // lives on, and we might be needed again in the future. + if (exception_code_) { + ServiceCheckOut(receive_port_name); + } + } else { + PRINT_MACH_RESULT(result, "Inspector: WaitForMessage()"); + } + } +} + +//============================================================================= +kern_return_t Inspector::ResetBootstrapPort() { + // A reasonable default, in case anything fails. + bootstrap_subset_port_ = bootstrap_port; + + mach_port_t self_task = mach_task_self(); + + kern_return_t kr = task_get_bootstrap_port(self_task, + &bootstrap_subset_port_); + if (kr != KERN_SUCCESS) { + NSLog(@"ResetBootstrapPort: task_get_bootstrap_port failed: %s (%d)", + mach_error_string(kr), kr); + return kr; + } + + mach_port_t bootstrap_parent_port; + kr = bootstrap_look_up(bootstrap_subset_port_, + const_cast(BREAKPAD_BOOTSTRAP_PARENT_PORT), + &bootstrap_parent_port); + if (kr != BOOTSTRAP_SUCCESS) { + NSLog(@"ResetBootstrapPort: bootstrap_look_up failed: %s (%d)", +#if defined(MAC_OS_X_VERSION_10_5) && \ + MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5 + bootstrap_strerror(kr), +#else + mach_error_string(kr), +#endif + kr); + return kr; + } + + kr = task_set_bootstrap_port(self_task, bootstrap_parent_port); + if (kr != KERN_SUCCESS) { + NSLog(@"ResetBootstrapPort: task_set_bootstrap_port failed: %s (%d)", + mach_error_string(kr), kr); + return kr; + } + + // Some things access the bootstrap port through this global variable + // instead of calling task_get_bootstrap_port. + bootstrap_port = bootstrap_parent_port; + + return KERN_SUCCESS; +} + +//============================================================================= +kern_return_t Inspector::ServiceCheckIn(const char* receive_port_name) { + // We need to get the mach port representing this service, so we can + // get information from the crashed process. + kern_return_t kr = bootstrap_check_in(bootstrap_subset_port_, + (char*)receive_port_name, + &service_rcv_port_); + + if (kr != KERN_SUCCESS) { +#if VERBOSE + PRINT_MACH_RESULT(kr, "Inspector: bootstrap_check_in()"); +#endif + } + + return kr; +} + +//============================================================================= +kern_return_t Inspector::ServiceCheckOut(const char* receive_port_name) { + // We're done receiving mach messages from the crashed process, + // so clean up a bit. + kern_return_t kr; + + // DO NOT use mach_port_deallocate() here -- it will fail and the + // following bootstrap_register() will also fail leaving our service + // name hanging around forever (until reboot) + kr = mach_port_destroy(mach_task_self(), service_rcv_port_); + + if (kr != KERN_SUCCESS) { + PRINT_MACH_RESULT(kr, + "Inspector: UNREGISTERING: service_rcv_port mach_port_deallocate()"); + return kr; + } + + // Unregister the service associated with the receive port. + kr = breakpad::BootstrapRegister(bootstrap_subset_port_, + (char*)receive_port_name, + MACH_PORT_NULL); + + if (kr != KERN_SUCCESS) { + PRINT_MACH_RESULT(kr, "Inspector: UNREGISTERING: bootstrap_register()"); + } + + return kr; +} + +//============================================================================= +kern_return_t Inspector::ReadMessages() { + // Wait for an initial message from the crashed process containing basic + // information about the crash. + ReceivePort receive_port(service_rcv_port_); + + MachReceiveMessage message; + kern_return_t result = receive_port.WaitForMessage(&message, 1000); + + if (result == KERN_SUCCESS) { + InspectorInfo& info = (InspectorInfo&)*message.GetData(); + exception_type_ = info.exception_type; + exception_code_ = info.exception_code; + exception_subcode_ = info.exception_subcode; + +#if VERBOSE + printf("message ID = %d\n", message.GetMessageID()); +#endif + + remote_task_ = message.GetTranslatedPort(0); + crashing_thread_ = message.GetTranslatedPort(1); + handler_thread_ = message.GetTranslatedPort(2); + ack_port_ = message.GetTranslatedPort(3); + +#if VERBOSE + printf("exception_type = %d\n", exception_type_); + printf("exception_code = %d\n", exception_code_); + printf("exception_subcode = %d\n", exception_subcode_); + printf("remote_task = %d\n", remote_task_); + printf("crashing_thread = %d\n", crashing_thread_); + printf("handler_thread = %d\n", handler_thread_); + printf("ack_port_ = %d\n", ack_port_); + printf("parameter count = %d\n", info.parameter_count); +#endif + + // In certain situations where multiple crash requests come + // through quickly, we can end up with the mach IPC messages not + // coming through correctly. Since we don't know what parameters + // we've missed, we can't do much besides abort the crash dump + // situation in this case. + unsigned int parameters_read = 0; + // The initial message contains the number of key value pairs that + // we are expected to read. + // Read each key/value pair, one mach message per key/value pair. + for (unsigned int i = 0; i < info.parameter_count; ++i) { + MachReceiveMessage parameter_message; + result = receive_port.WaitForMessage(¶meter_message, 1000); + + if(result == KERN_SUCCESS) { + KeyValueMessageData& key_value_data = + (KeyValueMessageData&)*parameter_message.GetData(); + // If we get a blank key, make sure we don't increment the + // parameter count; in some cases (notably on-demand generation + // many times in a short period of time) caused the Mach IPC + // messages to not come through correctly. + if (strlen(key_value_data.key) == 0) { + continue; + } + parameters_read++; + + config_params_.SetKeyValue(key_value_data.key, key_value_data.value); + } else { + PRINT_MACH_RESULT(result, "Inspector: key/value message"); + break; + } + } + if (parameters_read != info.parameter_count) { + return KERN_FAILURE; + } + } + + return result; +} + +//============================================================================= +bool Inspector::InspectTask() { + // keep the task quiet while we're looking at it + task_suspend(remote_task_); + + NSString* minidumpDir; + + const char* minidumpDirectory = + config_params_.GetValueForKey(BREAKPAD_DUMP_DIRECTORY); + + // If the client app has not specified a minidump directory, + // use a default of Library// + if (!minidumpDirectory || 0 == strlen(minidumpDirectory)) { + NSArray* libraryDirectories = + NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, + NSUserDomainMask, + YES); + + NSString* applicationSupportDirectory = + [libraryDirectories objectAtIndex:0]; + NSString* library_subdirectory = [NSString + stringWithUTF8String:kDefaultLibrarySubdirectory]; + NSString* breakpad_product = [NSString + stringWithUTF8String:config_params_.GetValueForKey(BREAKPAD_PRODUCT)]; + + NSArray* path_components = [NSArray + arrayWithObjects:applicationSupportDirectory, + library_subdirectory, + breakpad_product, + nil]; + + minidumpDir = [NSString pathWithComponents:path_components]; + } else { + minidumpDir = [[NSString stringWithUTF8String:minidumpDirectory] + stringByExpandingTildeInPath]; + } + + MinidumpLocation minidumpLocation(minidumpDir); + + // Obscure bug alert: + // Don't use [NSString stringWithFormat] to build up the path here since it + // assumes system encoding and in RTL locales will prepend an LTR override + // character for paths beginning with '/' which fileSystemRepresentation does + // not remove. Filed as rdar://6889706 . + NSString* path_ns = [NSString + stringWithUTF8String:minidumpLocation.GetPath()]; + NSString* pathid_ns = [NSString + stringWithUTF8String:minidumpLocation.GetID()]; + NSString* minidumpPath = [path_ns stringByAppendingPathComponent:pathid_ns]; + minidumpPath = [minidumpPath + stringByAppendingPathExtension:@"dmp"]; + + config_file_.WriteFile( 0, + &config_params_, + minidumpLocation.GetPath(), + minidumpLocation.GetID()); + + + MinidumpGenerator generator(remote_task_, handler_thread_); + + if (exception_type_ && exception_code_) { + generator.SetExceptionInformation(exception_type_, + exception_code_, + exception_subcode_, + crashing_thread_); + } + + + bool result = generator.Write([minidumpPath fileSystemRepresentation]); + + // let the task continue + task_resume(remote_task_); + + return result; +} + +//============================================================================= +// The crashed task needs to be told that the inspection has finished. +// It will wait on a mach port (with timeout) until we send acknowledgement. +kern_return_t Inspector::SendAcknowledgement() { + if (ack_port_ != MACH_PORT_DEAD) { + MachPortSender sender(ack_port_); + MachSendMessage ack_message(kMsgType_InspectorAcknowledgement); + + kern_return_t result = sender.SendMessage(ack_message, 2000); + +#if VERBOSE + PRINT_MACH_RESULT(result, "Inspector: sent acknowledgement"); +#endif + + return result; + } + + return KERN_INVALID_NAME; +} + +} // namespace google_breakpad + diff --git a/shared/sentry/external/breakpad/src/client/mac/crash_generation/InspectorMain.mm b/shared/sentry/external/breakpad/src/client/mac/crash_generation/InspectorMain.mm new file mode 100644 index 000000000..137c6a1e1 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/crash_generation/InspectorMain.mm @@ -0,0 +1,65 @@ +// 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. +// +// Main driver for Inspector + +#import "client/mac/crash_generation/Inspector.h" +#import + +namespace google_breakpad { + +//============================================================================= +extern "C" { + +int main(int argc, char *const argv[]) { +#if DEBUG + // Since we're launched on-demand, this is necessary to see debugging + // output in the console window. + freopen("/dev/console", "w", stdout); + freopen("/dev/console", "w", stderr); +#endif + + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + + if (argc != 2) { + exit(0); + } + // Our first command-line argument contains the name of the service + // that we're providing. + google_breakpad::Inspector inspector; + inspector.Inspect(argv[1]); + + [pool release]; + + return 0; +} + +} // extern "C" + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/mac/crash_generation/client_info.h b/shared/sentry/external/breakpad/src/client/mac/crash_generation/client_info.h new file mode 100644 index 000000000..a3a95dcac --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/crash_generation/client_info.h @@ -0,0 +1,47 @@ +// 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 CLIENT_MAC_CRASH_GENERATION_CLIENT_INFO_H_ +#define CLIENT_MAC_CRASH_GENERATION_CLIENT_INFO_H_ + +namespace google_breakpad { + +class ClientInfo { + public: + explicit ClientInfo(pid_t pid) : pid_(pid) {} + + pid_t pid() const { return pid_; } + + private: + pid_t pid_; +}; + +} // namespace google_breakpad + +#endif // CLIENT_MAC_CRASH_GENERATION_CLIENT_INFO_H_ diff --git a/shared/sentry/external/breakpad/src/client/mac/crash_generation/crash_generation_client.cc b/shared/sentry/external/breakpad/src/client/mac/crash_generation/crash_generation_client.cc new file mode 100644 index 000000000..ceeb3b32a --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/crash_generation/crash_generation_client.cc @@ -0,0 +1,72 @@ +// 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 "client/mac/crash_generation/crash_generation_client.h" + +#include "client/mac/crash_generation/crash_generation_server.h" +#include "common/mac/MachIPC.h" + +namespace google_breakpad { + +bool CrashGenerationClient::RequestDumpForException( + int exception_type, + int exception_code, + int exception_subcode, + mach_port_t crashing_thread) { + // The server will send a message to this port indicating that it + // has finished its work. + ReceivePort acknowledge_port; + + MachSendMessage message(kDumpRequestMessage); + message.AddDescriptor(mach_task_self()); // this task + message.AddDescriptor(crashing_thread); // crashing thread + message.AddDescriptor(mach_thread_self()); // handler thread + message.AddDescriptor(acknowledge_port.GetPort()); // message receive port + + ExceptionInfo info; + info.exception_type = exception_type; + info.exception_code = exception_code; + info.exception_subcode = exception_subcode; + message.SetData(&info, sizeof(info)); + + const mach_msg_timeout_t kSendTimeoutMs = 2 * 1000; + kern_return_t result = sender_.SendMessage(message, kSendTimeoutMs); + if (result != KERN_SUCCESS) + return false; + + // Give the server slightly longer to reply since it has to + // inspect this task and write the minidump. + const mach_msg_timeout_t kReceiveTimeoutMs = 5 * 1000; + MachReceiveMessage acknowledge_message; + result = acknowledge_port.WaitForMessage(&acknowledge_message, + kReceiveTimeoutMs); + return result == KERN_SUCCESS; +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/mac/crash_generation/crash_generation_client.h b/shared/sentry/external/breakpad/src/client/mac/crash_generation/crash_generation_client.h new file mode 100644 index 000000000..527f577a5 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/crash_generation/crash_generation_client.h @@ -0,0 +1,65 @@ +// 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 GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ +#define GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ + +#include "common/mac/MachIPC.h" + +namespace google_breakpad { + +class CrashGenerationClient { + public: + explicit CrashGenerationClient(const char* mach_port_name) + : sender_(mach_port_name) { + } + + // Request the crash server to generate a dump. + // + // Return true if the dump was successful; false otherwise. + bool RequestDumpForException(int exception_type, + int exception_code, + int exception_subcode, + mach_port_t crashing_thread); + + bool RequestDump() { + return RequestDumpForException(0, 0, 0, MACH_PORT_NULL); + } + + private: + MachPortSender sender_; + + // Prevent copy construction and assignment. + CrashGenerationClient(const CrashGenerationClient&); + CrashGenerationClient& operator=(const CrashGenerationClient&); +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ diff --git a/shared/sentry/external/breakpad/src/client/mac/crash_generation/crash_generation_server.cc b/shared/sentry/external/breakpad/src/client/mac/crash_generation/crash_generation_server.cc new file mode 100644 index 000000000..ae44e8bf8 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/crash_generation/crash_generation_server.cc @@ -0,0 +1,166 @@ +// 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 "client/mac/crash_generation/crash_generation_server.h" + +#include + +#include "client/mac/crash_generation/client_info.h" +#include "client/mac/handler/minidump_generator.h" +#include "common/mac/scoped_task_suspend-inl.h" + +namespace google_breakpad { + +CrashGenerationServer::CrashGenerationServer( + const char* mach_port_name, + FilterCallback filter, + void* filter_context, + OnClientDumpRequestCallback dump_callback, + void* dump_context, + OnClientExitingCallback exit_callback, + void* exit_context, + bool generate_dumps, + const std::string& dump_path) + : filter_(filter), + filter_context_(filter_context), + dump_callback_(dump_callback), + dump_context_(dump_context), + exit_callback_(exit_callback), + exit_context_(exit_context), + generate_dumps_(generate_dumps), + dump_dir_(dump_path.empty() ? "/tmp" : dump_path), + started_(false), + receive_port_(mach_port_name), + mach_port_name_(mach_port_name) { +} + +CrashGenerationServer::~CrashGenerationServer() { + if (started_) + Stop(); +} + +bool CrashGenerationServer::Start() { + int thread_create_result = pthread_create(&server_thread_, NULL, + &WaitForMessages, this); + started_ = thread_create_result == 0; + return started_; +} + +bool CrashGenerationServer::Stop() { + if (!started_) + return false; + + // Send a quit message to the background thread, and then join it. + MachPortSender sender(mach_port_name_.c_str()); + MachSendMessage quit_message(kQuitMessage); + const mach_msg_timeout_t kSendTimeoutMs = 2 * 1000; + kern_return_t result = sender.SendMessage(quit_message, kSendTimeoutMs); + if (result == KERN_SUCCESS) { + int thread_join_result = pthread_join(server_thread_, NULL); + started_ = thread_join_result != 0; + } + + return !started_; +} + +// static +void* CrashGenerationServer::WaitForMessages(void* server) { + CrashGenerationServer* self = + reinterpret_cast(server); + while (self->WaitForOneMessage()) {} + return NULL; +} + +bool CrashGenerationServer::WaitForOneMessage() { + MachReceiveMessage message; + kern_return_t result = receive_port_.WaitForMessage(&message, + MACH_MSG_TIMEOUT_NONE); + if (result == KERN_SUCCESS) { + switch (message.GetMessageID()) { + case kDumpRequestMessage: { + ExceptionInfo& info = (ExceptionInfo&)*message.GetData(); + + mach_port_t remote_task = message.GetTranslatedPort(0); + mach_port_t crashing_thread = message.GetTranslatedPort(1); + mach_port_t handler_thread = message.GetTranslatedPort(2); + mach_port_t ack_port = message.GetTranslatedPort(3); + pid_t remote_pid = -1; + pid_for_task(remote_task, &remote_pid); + ClientInfo client(remote_pid); + + bool result; + std::string dump_path; + if (generate_dumps_ && (!filter_ || filter_(filter_context_))) { + ScopedTaskSuspend suspend(remote_task); + + MinidumpGenerator generator(remote_task, handler_thread); + dump_path = generator.UniqueNameInDirectory(dump_dir_, NULL); + + if (info.exception_type && info.exception_code) { + generator.SetExceptionInformation(info.exception_type, + info.exception_code, + info.exception_subcode, + crashing_thread); + } + result = generator.Write(dump_path.c_str()); + } else { + result = true; + } + + if (result && dump_callback_) { + dump_callback_(dump_context_, client, dump_path); + } + + // TODO(ted): support a way for the client to send additional data, + // perhaps with a callback so users of the server can read the data + // themselves? + + if (ack_port != MACH_PORT_DEAD && ack_port != MACH_PORT_NULL) { + MachPortSender sender(ack_port); + MachSendMessage ack_message(kAcknowledgementMessage); + const mach_msg_timeout_t kSendTimeoutMs = 2 * 1000; + + sender.SendMessage(ack_message, kSendTimeoutMs); + } + + if (exit_callback_) { + exit_callback_(exit_context_, client); + } + break; + } + case kQuitMessage: + return false; + } + } else { // result != KERN_SUCCESS + return false; + } + return true; +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/mac/crash_generation/crash_generation_server.h b/shared/sentry/external/breakpad/src/client/mac/crash_generation/crash_generation_server.h new file mode 100644 index 000000000..82fef146e --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/crash_generation/crash_generation_server.h @@ -0,0 +1,150 @@ +// 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 GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_ +#define GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_ + +#include + +#include + +#include "common/mac/MachIPC.h" + +namespace google_breakpad { + +class ClientInfo; + +// Messages the server can read via its mach port +enum { + kDumpRequestMessage = 1, + kAcknowledgementMessage = 2, + kQuitMessage = 3 +}; + +// Exception details sent by the client when requesting a dump. +struct ExceptionInfo { + int32_t exception_type; + int32_t exception_code; + int32_t exception_subcode; +}; + +class CrashGenerationServer { + public: + // WARNING: callbacks may be invoked on a different thread + // than that which creates the CrashGenerationServer. They must + // be thread safe. + typedef void (*OnClientDumpRequestCallback)(void* context, + const ClientInfo& client_info, + const std::string& file_path); + + typedef void (*OnClientExitingCallback)(void* context, + const ClientInfo& client_info); + // If a FilterCallback returns false, the dump will not be written. + typedef bool (*FilterCallback)(void* context); + + // Create an instance with the given parameters. + // + // mach_port_name: Named server port to listen on. + // filter: Callback for a client to cancel writing a dump. + // filter_context: Context for the filter callback. + // dump_callback: Callback for a client crash dump request. + // dump_context: Context for client crash dump request callback. + // exit_callback: Callback for client process exit. + // exit_context: Context for client exit callback. + // generate_dumps: Whether to automatically generate dumps. + // Client code of this class might want to generate dumps explicitly + // in the crash dump request callback. In that case, false can be + // passed for this parameter. + // dump_path: Path for generating dumps; required only if true is + // passed for generateDumps parameter; NULL can be passed otherwise. + CrashGenerationServer(const char* mach_port_name, + FilterCallback filter, + void* filter_context, + OnClientDumpRequestCallback dump_callback, + void* dump_context, + OnClientExitingCallback exit_callback, + void* exit_context, + bool generate_dumps, + const std::string& dump_path); + + ~CrashGenerationServer(); + + // Perform initialization steps needed to start listening to clients. + // + // Return true if initialization is successful; false otherwise. + bool Start(); + + // Stop the server. + bool Stop(); + + private: + // Return a unique filename at which a minidump can be written. + bool MakeMinidumpFilename(std::string& outFilename); + + // Loop reading client messages and responding to them until + // a quit message is received. + static void* WaitForMessages(void* server); + + // Wait for a single client message and respond to it. Returns false + // if a quit message was received or if an error occurred. + bool WaitForOneMessage(); + + FilterCallback filter_; + void* filter_context_; + + OnClientDumpRequestCallback dump_callback_; + void* dump_context_; + + OnClientExitingCallback exit_callback_; + void* exit_context_; + + bool generate_dumps_; + + std::string dump_dir_; + + bool started_; + + // The mach port that receives requests to dump from child processes. + ReceivePort receive_port_; + + // The name of the mach port. Stored so the Stop method can message + // the background thread to shut it down. + std::string mach_port_name_; + + // The thread that waits on the receive port. + pthread_t server_thread_; + + // Disable copy constructor and operator=. + CrashGenerationServer(const CrashGenerationServer&); + CrashGenerationServer& operator=(const CrashGenerationServer&); +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_CLIENT_MAC_CRASH_GENERATION_CRASH_GENERATION_SERVER_H_ diff --git a/shared/sentry/external/breakpad/src/client/mac/handler/breakpad_nlist_64.cc b/shared/sentry/external/breakpad/src/client/mac/handler/breakpad_nlist_64.cc new file mode 100644 index 000000000..b4f04c917 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/handler/breakpad_nlist_64.cc @@ -0,0 +1,399 @@ +/* + * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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 was copied from libc/gen/nlist.c from Darwin's source code + * The version of nlist used as a base is from 10.5.2, libc-498 + * http://www.opensource.apple.com/darwinsource/10.5.2/Libc-498/gen/nlist.c + * + * The full tarball is at: + * http://www.opensource.apple.com/darwinsource/tarballs/apsl/Libc-498.tar.gz + * + * I've modified it to be compatible with 64-bit images. +*/ + +#include "breakpad_nlist_64.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Stuff lifted from and since they are gone */ +/* + * Header prepended to each a.out file. + */ +struct exec { + unsigned short a_machtype; /* machine type */ + unsigned short a_magic; /* magic number */ + unsigned long a_text; /* size of text segment */ + unsigned long a_data; /* size of initialized data */ + unsigned long a_bss; /* size of uninitialized data */ + unsigned long a_syms; /* size of symbol table */ + unsigned long a_entry; /* entry point */ + unsigned long a_trsize; /* size of text relocation */ + unsigned long a_drsize; /* size of data relocation */ +}; + +#define OMAGIC 0407 /* old impure format */ +#define NMAGIC 0410 /* read-only text */ +#define ZMAGIC 0413 /* demand load format */ + +#define N_BADMAG(x) \ + (((x).a_magic)!=OMAGIC && ((x).a_magic)!=NMAGIC && ((x).a_magic)!=ZMAGIC) +#define N_TXTOFF(x) \ + ((x).a_magic==ZMAGIC ? 0 : sizeof (struct exec)) +#define N_SYMOFF(x) \ + (N_TXTOFF(x) + (x).a_text+(x).a_data + (x).a_trsize+(x).a_drsize) + +// Traits structs for specializing function templates to handle +// 32-bit/64-bit Mach-O files. +template +struct MachBits {}; + +typedef struct nlist nlist32; +typedef struct nlist_64 nlist64; + +template<> +struct MachBits { + typedef mach_header mach_header_type; + typedef uint32_t word_type; + static const uint32_t magic = MH_MAGIC; +}; + +template<> +struct MachBits { + typedef mach_header_64 mach_header_type; + typedef uint64_t word_type; + static const uint32_t magic = MH_MAGIC_64; +}; + +template +int +__breakpad_fdnlist(int fd, nlist_type* list, const char** symbolNames, + cpu_type_t cpu_type); + +/* + * nlist - retreive attributes from name list (string table version) + */ + +template +int breakpad_nlist_common(const char* name, + nlist_type* list, + const char** symbolNames, + cpu_type_t cpu_type) { + int fd = open(name, O_RDONLY, 0); + if (fd < 0) + return -1; + int n = __breakpad_fdnlist(fd, list, symbolNames, cpu_type); + close(fd); + return n; +} + +int breakpad_nlist(const char* name, + struct nlist* list, + const char** symbolNames, + cpu_type_t cpu_type) { + return breakpad_nlist_common(name, list, symbolNames, cpu_type); +} + +int breakpad_nlist(const char* name, + struct nlist_64* list, + const char** symbolNames, + cpu_type_t cpu_type) { + return breakpad_nlist_common(name, list, symbolNames, cpu_type); +} + +/* Note: __fdnlist() is called from kvm_nlist in libkvm's kvm.c */ + +template +int __breakpad_fdnlist(int fd, nlist_type* list, const char** symbolNames, + cpu_type_t cpu_type) { + typedef typename MachBits::mach_header_type mach_header_type; + typedef typename MachBits::word_type word_type; + + const uint32_t magic = MachBits::magic; + + int maxlen = 500; + int nreq = 0; + for (nlist_type* q = list; + symbolNames[q-list] && symbolNames[q-list][0]; + q++, nreq++) { + + q->n_type = 0; + q->n_value = 0; + q->n_desc = 0; + q->n_sect = 0; + q->n_un.n_strx = 0; + } + + struct exec buf; + if (read(fd, (char*)&buf, sizeof(buf)) != sizeof(buf) || + (N_BADMAG(buf) && *((uint32_t*)&buf) != magic && + CFSwapInt32BigToHost(*((uint32_t*)&buf)) != FAT_MAGIC && + /* The following is the big-endian ppc64 check */ + (*((uint32_t*)&buf)) != FAT_MAGIC)) { + return -1; + } + + /* Deal with fat file if necessary */ + unsigned arch_offset = 0; + if (CFSwapInt32BigToHost(*((uint32_t*)&buf)) == FAT_MAGIC || + /* The following is the big-endian ppc64 check */ + *((unsigned int*)&buf) == FAT_MAGIC) { + /* Read in the fat header */ + struct fat_header fh; + if (lseek(fd, 0, SEEK_SET) == -1) { + return -1; + } + if (read(fd, (char*)&fh, sizeof(fh)) != sizeof(fh)) { + return -1; + } + + /* Convert fat_narchs to host byte order */ + fh.nfat_arch = CFSwapInt32BigToHost(fh.nfat_arch); + + /* Read in the fat archs */ + struct fat_arch* fat_archs = + (struct fat_arch*)malloc(fh.nfat_arch * sizeof(struct fat_arch)); + if (fat_archs == NULL) { + return -1; + } + if (read(fd, (char*)fat_archs, + sizeof(struct fat_arch) * fh.nfat_arch) != + (ssize_t)(sizeof(struct fat_arch) * fh.nfat_arch)) { + free(fat_archs); + return -1; + } + + /* + * Convert archs to host byte ordering (a constraint of + * cpusubtype_getbestarch() + */ + for (unsigned i = 0; i < fh.nfat_arch; i++) { + fat_archs[i].cputype = + CFSwapInt32BigToHost(fat_archs[i].cputype); + fat_archs[i].cpusubtype = + CFSwapInt32BigToHost(fat_archs[i].cpusubtype); + fat_archs[i].offset = + CFSwapInt32BigToHost(fat_archs[i].offset); + fat_archs[i].size = + CFSwapInt32BigToHost(fat_archs[i].size); + fat_archs[i].align = + CFSwapInt32BigToHost(fat_archs[i].align); + } + + struct fat_arch* fap = NULL; + for (unsigned i = 0; i < fh.nfat_arch; i++) { + if (fat_archs[i].cputype == cpu_type) { + fap = &fat_archs[i]; + break; + } + } + + if (!fap) { + free(fat_archs); + return -1; + } + arch_offset = fap->offset; + free(fat_archs); + + /* Read in the beginning of the architecture-specific file */ + if (lseek(fd, arch_offset, SEEK_SET) == -1) { + return -1; + } + if (read(fd, (char*)&buf, sizeof(buf)) != sizeof(buf)) { + return -1; + } + } + + off_t sa; /* symbol address */ + off_t ss; /* start of strings */ + register_t n; + if (*((unsigned int*)&buf) == magic) { + if (lseek(fd, arch_offset, SEEK_SET) == -1) { + return -1; + } + mach_header_type mh; + if (read(fd, (char*)&mh, sizeof(mh)) != sizeof(mh)) { + return -1; + } + + struct load_command* load_commands = + (struct load_command*)malloc(mh.sizeofcmds); + if (load_commands == NULL) { + return -1; + } + if (read(fd, (char*)load_commands, mh.sizeofcmds) != + (ssize_t)mh.sizeofcmds) { + free(load_commands); + return -1; + } + struct symtab_command* stp = NULL; + struct load_command* lcp = load_commands; + // iterate through all load commands, looking for + // LC_SYMTAB load command + for (uint32_t i = 0; i < mh.ncmds; i++) { + if (lcp->cmdsize % sizeof(word_type) != 0 || + lcp->cmdsize <= 0 || + (char*)lcp + lcp->cmdsize > (char*)load_commands + mh.sizeofcmds) { + free(load_commands); + return -1; + } + if (lcp->cmd == LC_SYMTAB) { + if (lcp->cmdsize != sizeof(struct symtab_command)) { + free(load_commands); + return -1; + } + stp = (struct symtab_command*)lcp; + break; + } + lcp = (struct load_command*)((char*)lcp + lcp->cmdsize); + } + if (stp == NULL) { + free(load_commands); + return -1; + } + // sa points to the beginning of the symbol table + sa = stp->symoff + arch_offset; + // ss points to the beginning of the string table + ss = stp->stroff + arch_offset; + // n is the number of bytes in the symbol table + // each symbol table entry is an nlist structure + n = stp->nsyms * sizeof(nlist_type); + free(load_commands); + } else { + sa = N_SYMOFF(buf) + arch_offset; + ss = sa + buf.a_syms + arch_offset; + n = buf.a_syms; + } + + if (lseek(fd, sa, SEEK_SET) == -1) { + return -1; + } + + // the algorithm here is to read the nlist entries in m-sized + // chunks into q. q is then iterated over. for each entry in q, + // use the string table index(q->n_un.n_strx) to read the symbol + // name, then scan the nlist entries passed in by the user(via p), + // and look for a match + while (n) { + nlist_type space[BUFSIZ/sizeof (nlist_type)]; + register_t m = sizeof (space); + + if (n < m) + m = n; + if (read(fd, (char*)space, m) != m) + break; + n -= m; + off_t savpos = lseek(fd, 0, SEEK_CUR); + if (savpos == -1) { + return -1; + } + for (nlist_type* q = space; (m -= sizeof(nlist_type)) >= 0; q++) { + char nambuf[BUFSIZ]; + + if (q->n_un.n_strx == 0 || q->n_type & N_STAB) + continue; + + // seek to the location in the binary where the symbol + // name is stored & read it into memory + if (lseek(fd, ss+q->n_un.n_strx, SEEK_SET) == -1) { + return -1; + } + if (read(fd, nambuf, maxlen+1) == -1) { + return -1; + } + const char* s2 = nambuf; + for (nlist_type* p = list; + symbolNames[p-list] && symbolNames[p-list][0]; + p++) { + // get the symbol name the user has passed in that + // corresponds to the nlist entry that we're looking at + const char* s1 = symbolNames[p - list]; + while (*s1) { + if (*s1++ != *s2++) + goto cont; + } + if (*s2) + goto cont; + + p->n_value = q->n_value; + p->n_type = q->n_type; + p->n_desc = q->n_desc; + p->n_sect = q->n_sect; + p->n_un.n_strx = q->n_un.n_strx; + if (--nreq == 0) + return nreq; + + break; + cont: ; + } + } + if (lseek(fd, savpos, SEEK_SET) == -1) { + return -1; + } + } + return nreq; +} diff --git a/shared/sentry/external/breakpad/src/client/mac/handler/breakpad_nlist_64.h b/shared/sentry/external/breakpad/src/client/mac/handler/breakpad_nlist_64.h new file mode 100644 index 000000000..a1a3e83c9 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/handler/breakpad_nlist_64.h @@ -0,0 +1,48 @@ +// 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. + +// breakpad_nlist.h +// +// This file is meant to provide a header for clients of the modified +// nlist function implemented to work on 64-bit. + +#ifndef CLIENT_MAC_HANDLER_BREAKPAD_NLIST_H__ +#define CLIENT_MAC_HANDLER_BREAKPAD_NLIST_H__ + +#include + +int breakpad_nlist(const char* name, + struct nlist* list, + const char** symbolNames, + cpu_type_t cpu_type); +int breakpad_nlist(const char* name, + struct nlist_64* list, + const char** symbolNames, + cpu_type_t cpu_type); + +#endif /* CLIENT_MAC_HANDLER_BREAKPAD_NLIST_H__ */ diff --git a/shared/sentry/external/breakpad/src/client/mac/handler/dynamic_images.cc b/shared/sentry/external/breakpad/src/client/mac/handler/dynamic_images.cc new file mode 100644 index 000000000..b78c20877 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/handler/dynamic_images.cc @@ -0,0 +1,573 @@ +// 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 "client/mac/handler/dynamic_images.h" + +extern "C" { // needed to compile on Leopard + #include + #include + #include +} + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "breakpad_nlist_64.h" + +#if !TARGET_OS_IPHONE +#include + +#ifndef MAC_OS_X_VERSION_10_6 +#define MAC_OS_X_VERSION_10_6 1060 +#endif + +#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6 + +// Fallback declarations for TASK_DYLD_INFO and friends, introduced in +// in the Mac OS X 10.6 SDK. +#define TASK_DYLD_INFO 17 +struct task_dyld_info { + mach_vm_address_t all_image_info_addr; + mach_vm_size_t all_image_info_size; +}; +typedef struct task_dyld_info task_dyld_info_data_t; +typedef struct task_dyld_info* task_dyld_info_t; +#define TASK_DYLD_INFO_COUNT (sizeof(task_dyld_info_data_t) / sizeof(natural_t)) + +#endif + +#endif // !TARGET_OS_IPHONE + +namespace google_breakpad { + +using std::string; +using std::vector; + +//============================================================================== +// Returns the size of the memory region containing |address| and the +// number of bytes from |address| to the end of the region. +// We potentially, will extend the size of the original +// region by the size of the following region if it's contiguous with the +// first in order to handle cases when we're reading strings and they +// straddle two vm regions. +// +static mach_vm_size_t GetMemoryRegionSize(task_port_t target_task, + const uint64_t address, + mach_vm_size_t* size_to_end) { + mach_vm_address_t region_base = (mach_vm_address_t)address; + mach_vm_size_t region_size; + natural_t nesting_level = 0; + vm_region_submap_info_64 submap_info; + mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64; + + // Get information about the vm region containing |address| + vm_region_recurse_info_t region_info; + region_info = reinterpret_cast(&submap_info); + + kern_return_t result = + mach_vm_region_recurse(target_task, + ®ion_base, + ®ion_size, + &nesting_level, + region_info, + &info_count); + + if (result == KERN_SUCCESS) { + // Get distance from |address| to the end of this region + *size_to_end = region_base + region_size -(mach_vm_address_t)address; + + // If we want to handle strings as long as 4096 characters we may need + // to check if there's a vm region immediately following the first one. + // If so, we need to extend |*size_to_end| to go all the way to the end + // of the second region. + if (*size_to_end < 4096) { + // Second region starts where the first one ends + mach_vm_address_t region_base2 = + (mach_vm_address_t)(region_base + region_size); + mach_vm_size_t region_size2; + + // Get information about the following vm region + result = + mach_vm_region_recurse(target_task, + ®ion_base2, + ®ion_size2, + &nesting_level, + region_info, + &info_count); + + // Extend region_size to go all the way to the end of the 2nd region + if (result == KERN_SUCCESS + && region_base2 == region_base + region_size) { + region_size += region_size2; + } + } + + *size_to_end = region_base + region_size -(mach_vm_address_t)address; + } else { + region_size = 0; + *size_to_end = 0; + } + + return region_size; +} + +#define kMaxStringLength 8192 +//============================================================================== +// Reads a NULL-terminated string from another task. +// +// Warning! This will not read any strings longer than kMaxStringLength-1 +// +static string ReadTaskString(task_port_t target_task, + const uint64_t address) { + // The problem is we don't know how much to read until we know how long + // the string is. And we don't know how long the string is, until we've read + // the memory! So, we'll try to read kMaxStringLength bytes + // (or as many bytes as we can until we reach the end of the vm region). + mach_vm_size_t size_to_end; + GetMemoryRegionSize(target_task, address, &size_to_end); + + if (size_to_end > 0) { + mach_vm_size_t size_to_read = + size_to_end > kMaxStringLength ? kMaxStringLength : size_to_end; + + vector bytes; + if (ReadTaskMemory(target_task, address, (size_t)size_to_read, bytes) != + KERN_SUCCESS) + return string(); + + return string(reinterpret_cast(&bytes[0])); + } + + return string(); +} + +//============================================================================== +// Reads an address range from another task. The bytes read will be returned +// in bytes, which will be resized as necessary. +kern_return_t ReadTaskMemory(task_port_t target_task, + const uint64_t address, + size_t length, + vector& bytes) { + int systemPageSize = getpagesize(); + + // use the negative of the page size for the mask to find the page address + mach_vm_address_t page_address = address & (-systemPageSize); + + mach_vm_address_t last_page_address = + (address + length + (systemPageSize - 1)) & (-systemPageSize); + + mach_vm_size_t page_size = last_page_address - page_address; + uint8_t* local_start; + uint32_t local_length; + + kern_return_t r = mach_vm_read(target_task, + page_address, + page_size, + reinterpret_cast(&local_start), + &local_length); + + if (r != KERN_SUCCESS) + return r; + + bytes.resize(length); + memcpy(&bytes[0], + &local_start[(mach_vm_address_t)address - page_address], + length); + mach_vm_deallocate(mach_task_self(), (uintptr_t)local_start, local_length); + return KERN_SUCCESS; +} + +#pragma mark - + +//============================================================================== +// Traits structs for specializing function templates to handle +// 32-bit/64-bit Mach-O files. +struct MachO32 { + typedef mach_header mach_header_type; + typedef segment_command mach_segment_command_type; + typedef dyld_image_info32 dyld_image_info; + typedef dyld_all_image_infos32 dyld_all_image_infos; + typedef struct nlist nlist_type; + static const uint32_t magic = MH_MAGIC; + static const uint32_t segment_load_command = LC_SEGMENT; +}; + +struct MachO64 { + typedef mach_header_64 mach_header_type; + typedef segment_command_64 mach_segment_command_type; + typedef dyld_image_info64 dyld_image_info; + typedef dyld_all_image_infos64 dyld_all_image_infos; + typedef struct nlist_64 nlist_type; + static const uint32_t magic = MH_MAGIC_64; + static const uint32_t segment_load_command = LC_SEGMENT_64; +}; + +template +bool FindTextSection(DynamicImage& image) { + typedef typename MachBits::mach_header_type mach_header_type; + typedef typename MachBits::mach_segment_command_type + mach_segment_command_type; + + const mach_header_type* header = + reinterpret_cast(&image.header_[0]); + + if(header->magic != MachBits::magic) { + return false; + } + + const struct load_command* cmd = + reinterpret_cast(header + 1); + + bool found_text_section = false; + bool found_dylib_id_command = false; + for (unsigned int i = 0; cmd && (i < header->ncmds); ++i) { + if (!found_text_section) { + if (cmd->cmd == MachBits::segment_load_command) { + const mach_segment_command_type* seg = + reinterpret_cast(cmd); + + if (!strcmp(seg->segname, "__TEXT")) { + image.vmaddr_ = static_cast(seg->vmaddr); + image.vmsize_ = static_cast(seg->vmsize); + image.slide_ = 0; + + if (seg->fileoff == 0 && seg->filesize != 0) { + image.slide_ = + (uintptr_t)image.GetLoadAddress() - (uintptr_t)seg->vmaddr; + } + found_text_section = true; + } + } + } + + if (!found_dylib_id_command) { + if (cmd->cmd == LC_ID_DYLIB) { + const struct dylib_command* dc = + reinterpret_cast(cmd); + + image.version_ = dc->dylib.current_version; + found_dylib_id_command = true; + } + } + + if (found_dylib_id_command && found_text_section) { + return true; + } + + cmd = reinterpret_cast + (reinterpret_cast(cmd) + cmd->cmdsize); + } + + return false; +} + +//============================================================================== +// Initializes vmaddr_, vmsize_, and slide_ +void DynamicImage::CalculateMemoryAndVersionInfo() { + // unless we can process the header, ensure that calls to + // IsValid() will return false + vmaddr_ = 0; + vmsize_ = 0; + slide_ = 0; + version_ = 0; + + // The function template above does all the real work. + if (Is64Bit()) + FindTextSection(*this); + else + FindTextSection(*this); +} + +//============================================================================== +// The helper function template abstracts the 32/64-bit differences. +template +uint32_t GetFileTypeFromHeader(DynamicImage& image) { + typedef typename MachBits::mach_header_type mach_header_type; + + const mach_header_type* header = + reinterpret_cast(&image.header_[0]); + return header->filetype; +} + +uint32_t DynamicImage::GetFileType() { + if (Is64Bit()) + return GetFileTypeFromHeader(*this); + + return GetFileTypeFromHeader(*this); +} + +#pragma mark - + +//============================================================================== +// Loads information about dynamically loaded code in the given task. +DynamicImages::DynamicImages(mach_port_t task) + : task_(task), + cpu_type_(DetermineTaskCPUType(task)), + image_list_() { + ReadImageInfoForTask(); +} + +template +static uint64_t LookupSymbol(const char* symbol_name, + const char* filename, + cpu_type_t cpu_type) { + typedef typename MachBits::nlist_type nlist_type; + + nlist_type symbol_info[8] = {}; + const char* symbolNames[2] = { symbol_name, "\0" }; + nlist_type& list = symbol_info[0]; + int invalidEntriesCount = breakpad_nlist(filename, + &list, + symbolNames, + cpu_type); + + if(invalidEntriesCount != 0) { + return 0; + } + + assert(list.n_value); + return list.n_value; +} + +#if TARGET_OS_IPHONE || MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6 +static bool HasTaskDyldInfo() { + return true; +} +#else +static SInt32 GetOSVersionInternal() { + SInt32 os_version = 0; + Gestalt(gestaltSystemVersion, &os_version); + return os_version; +} + +static SInt32 GetOSVersion() { + static SInt32 os_version = GetOSVersionInternal(); + return os_version; +} + +static bool HasTaskDyldInfo() { + return GetOSVersion() >= 0x1060; +} +#endif // TARGET_OS_IPHONE || MAC_OS_X_VERSION_MIN_REQUIRED >= 10_6 + +uint64_t DynamicImages::GetDyldAllImageInfosPointer() { + if (HasTaskDyldInfo()) { + task_dyld_info_data_t task_dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + if (task_info(task_, TASK_DYLD_INFO, (task_info_t)&task_dyld_info, + &count) != KERN_SUCCESS) { + return 0; + } + + return (uint64_t)task_dyld_info.all_image_info_addr; + } else { + const char* imageSymbolName = "_dyld_all_image_infos"; + const char* dyldPath = "/usr/lib/dyld"; + + if (Is64Bit()) + return LookupSymbol(imageSymbolName, dyldPath, cpu_type_); + return LookupSymbol(imageSymbolName, dyldPath, cpu_type_); + } +} + +//============================================================================== +// This code was written using dyld_debug.c (from Darwin) as a guide. + +template +void ReadImageInfo(DynamicImages& images, + uint64_t image_list_address) { + typedef typename MachBits::dyld_image_info dyld_image_info; + typedef typename MachBits::dyld_all_image_infos dyld_all_image_infos; + typedef typename MachBits::mach_header_type mach_header_type; + + // Read the structure inside of dyld that contains information about + // loaded images. We're reading from the desired task's address space. + + // Here we make the assumption that dyld loaded at the same address in + // the crashed process vs. this one. This is an assumption made in + // "dyld_debug.c" and is said to be nearly always valid. + vector dyld_all_info_bytes; + if (ReadTaskMemory(images.task_, + image_list_address, + sizeof(dyld_all_image_infos), + dyld_all_info_bytes) != KERN_SUCCESS) + return; + + dyld_all_image_infos* dyldInfo = + reinterpret_cast(&dyld_all_info_bytes[0]); + + // number of loaded images + int count = dyldInfo->infoArrayCount; + + // Read an array of dyld_image_info structures each containing + // information about a loaded image. + vector dyld_info_array_bytes; + if (ReadTaskMemory(images.task_, + dyldInfo->infoArray, + count * sizeof(dyld_image_info), + dyld_info_array_bytes) != KERN_SUCCESS) + return; + + dyld_image_info* infoArray = + reinterpret_cast(&dyld_info_array_bytes[0]); + images.image_list_.reserve(count); + + for (int i = 0; i < count; ++i) { + dyld_image_info& info = infoArray[i]; + + // First read just the mach_header from the image in the task. + vector mach_header_bytes; + if (ReadTaskMemory(images.task_, + info.load_address_, + sizeof(mach_header_type), + mach_header_bytes) != KERN_SUCCESS) + continue; // bail on this dynamic image + + mach_header_type* header = + reinterpret_cast(&mach_header_bytes[0]); + + // Now determine the total amount necessary to read the header + // plus all of the load commands. + size_t header_size = + sizeof(mach_header_type) + header->sizeofcmds; + + if (ReadTaskMemory(images.task_, + info.load_address_, + header_size, + mach_header_bytes) != KERN_SUCCESS) + continue; + + // Read the file name from the task's memory space. + string file_path; + if (info.file_path_) { + // Although we're reading kMaxStringLength bytes, it's copied in the + // the DynamicImage constructor below with the correct string length, + // so it's not really wasting memory. + file_path = ReadTaskString(images.task_, info.file_path_); + } + + // Create an object representing this image and add it to our list. + DynamicImage* new_image; + new_image = new DynamicImage(&mach_header_bytes[0], + header_size, + info.load_address_, + file_path, + static_cast(info.file_mod_date_), + images.task_, + images.cpu_type_); + + if (new_image->IsValid()) { + images.image_list_.push_back(DynamicImageRef(new_image)); + } else { + delete new_image; + } + } + + // sorts based on loading address + sort(images.image_list_.begin(), images.image_list_.end()); + // remove duplicates - this happens in certain strange cases + // You can see it in DashboardClient when Google Gadgets plugin + // is installed. Apple's crash reporter log and gdb "info shared" + // both show the same library multiple times at the same address + + vector::iterator it = unique(images.image_list_.begin(), + images.image_list_.end()); + images.image_list_.erase(it, images.image_list_.end()); +} + +void DynamicImages::ReadImageInfoForTask() { + uint64_t imageList = GetDyldAllImageInfosPointer(); + + if (imageList) { + if (Is64Bit()) + ReadImageInfo(*this, imageList); + else + ReadImageInfo(*this, imageList); + } +} + +//============================================================================== +DynamicImage* DynamicImages::GetExecutableImage() { + int executable_index = GetExecutableImageIndex(); + + if (executable_index >= 0) { + return GetImage(executable_index); + } + + return NULL; +} + +//============================================================================== +// returns -1 if failure to find executable +int DynamicImages::GetExecutableImageIndex() { + int image_count = GetImageCount(); + + for (int i = 0; i < image_count; ++i) { + DynamicImage* image = GetImage(i); + if (image->GetFileType() == MH_EXECUTE) { + return i; + } + } + + return -1; +} + +//============================================================================== +// static +cpu_type_t DynamicImages::DetermineTaskCPUType(task_t task) { + if (task == mach_task_self()) + return GetNativeCPUType(); + + int mib[CTL_MAXNAME]; + size_t mibLen = CTL_MAXNAME; + int err = sysctlnametomib("sysctl.proc_cputype", mib, &mibLen); + if (err == 0) { + assert(mibLen < CTL_MAXNAME); + pid_for_task(task, &mib[mibLen]); + mibLen += 1; + + cpu_type_t cpu_type; + size_t cpuTypeSize = sizeof(cpu_type); + sysctl(mib, static_cast(mibLen), &cpu_type, &cpuTypeSize, 0, 0); + return cpu_type; + } + + return GetNativeCPUType(); +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/mac/handler/dynamic_images.h b/shared/sentry/external/breakpad/src/client/mac/handler/dynamic_images.h new file mode 100644 index 000000000..01f3a0e91 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/handler/dynamic_images.h @@ -0,0 +1,320 @@ +// 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. + +// dynamic_images.h +// +// Implements most of the function of the dyld API, but allowing an +// arbitrary task to be introspected, unlike the dyld API which +// only allows operation on the current task. The current implementation +// is limited to use by 32-bit tasks. + +#ifndef CLIENT_MAC_HANDLER_DYNAMIC_IMAGES_H__ +#define CLIENT_MAC_HANDLER_DYNAMIC_IMAGES_H__ + +#include +#include +#include +#include + +#include +#include + +#include "mach_vm_compat.h" + +namespace google_breakpad { + +using std::string; +using std::vector; + +//============================================================================== +// The memory layout of this struct matches the dyld_image_info struct +// defined in "dyld_gdb.h" in the darwin source. +typedef struct dyld_image_info32 { + uint32_t load_address_; // struct mach_header* + uint32_t file_path_; // char* + uint32_t file_mod_date_; +} dyld_image_info32; + +typedef struct dyld_image_info64 { + uint64_t load_address_; // struct mach_header* + uint64_t file_path_; // char* + uint64_t file_mod_date_; +} dyld_image_info64; + +//============================================================================== +// This is as defined in "dyld_gdb.h" in the darwin source. +// _dyld_all_image_infos (in dyld) is a structure of this type +// which will be used to determine which dynamic code has been loaded. +typedef struct dyld_all_image_infos32 { + uint32_t version; // == 1 in Mac OS X 10.4 + uint32_t infoArrayCount; + uint32_t infoArray; // const struct dyld_image_info* + uint32_t notification; + bool processDetachedFromSharedRegion; +} dyld_all_image_infos32; + +typedef struct dyld_all_image_infos64 { + uint32_t version; // == 1 in Mac OS X 10.4 + uint32_t infoArrayCount; + uint64_t infoArray; // const struct dyld_image_info* + uint64_t notification; + bool processDetachedFromSharedRegion; +} dyld_all_image_infos64; + +// some typedefs to isolate 64/32 bit differences +#ifdef __LP64__ +typedef mach_header_64 breakpad_mach_header; +typedef segment_command_64 breakpad_mach_segment_command; +#else +typedef mach_header breakpad_mach_header; +typedef segment_command breakpad_mach_segment_command; +#endif + +// Helper functions to deal with 32-bit/64-bit Mach-O differences. +class DynamicImage; +template +bool FindTextSection(DynamicImage& image); + +template +uint32_t GetFileTypeFromHeader(DynamicImage& image); + +//============================================================================== +// Represents a single dynamically loaded mach-o image +class DynamicImage { + public: + DynamicImage(uint8_t* header, // data is copied + size_t header_size, // includes load commands + uint64_t load_address, + string file_path, + uintptr_t image_mod_date, + mach_port_t task, + cpu_type_t cpu_type) + : header_(header, header + header_size), + header_size_(header_size), + load_address_(load_address), + vmaddr_(0), + vmsize_(0), + slide_(0), + version_(0), + file_path_(file_path), + file_mod_date_(image_mod_date), + task_(task), + cpu_type_(cpu_type) { + CalculateMemoryAndVersionInfo(); + } + + // Size of mach_header plus load commands + size_t GetHeaderSize() const {return header_.size();} + + // Full path to mach-o binary + string GetFilePath() {return file_path_;} + + uint64_t GetModDate() const {return file_mod_date_;} + + // Actual address where the image was loaded + uint64_t GetLoadAddress() const {return load_address_;} + + // Address where the image should be loaded + mach_vm_address_t GetVMAddr() const {return vmaddr_;} + + // Difference between GetLoadAddress() and GetVMAddr() + ptrdiff_t GetVMAddrSlide() const {return slide_;} + + // Size of the image + mach_vm_size_t GetVMSize() const {return vmsize_;} + + // Task owning this loaded image + mach_port_t GetTask() {return task_;} + + // CPU type of the task + cpu_type_t GetCPUType() {return cpu_type_;} + + // filetype from the Mach-O header. + uint32_t GetFileType(); + + // Return true if the task is a 64-bit architecture. + bool Is64Bit() { return (GetCPUType() & CPU_ARCH_ABI64) == CPU_ARCH_ABI64; } + + uint32_t GetVersion() {return version_;} + // For sorting + bool operator<(const DynamicImage& inInfo) { + return GetLoadAddress() < inInfo.GetLoadAddress(); + } + + // Sanity checking + bool IsValid() {return GetVMSize() != 0;} + + private: + DynamicImage(const DynamicImage&); + DynamicImage& operator=(const DynamicImage&); + + friend class DynamicImages; + template + friend bool FindTextSection(DynamicImage& image); + template + friend uint32_t GetFileTypeFromHeader(DynamicImage& image); + + // Initializes vmaddr_, vmsize_, and slide_ + void CalculateMemoryAndVersionInfo(); + + const vector header_; // our local copy of the header + size_t header_size_; // mach_header plus load commands + uint64_t load_address_; // base address image is mapped into + mach_vm_address_t vmaddr_; + mach_vm_size_t vmsize_; + ptrdiff_t slide_; + uint32_t version_; // Dylib version + string file_path_; // path dyld used to load the image + uintptr_t file_mod_date_; // time_t of image file + + mach_port_t task_; + cpu_type_t cpu_type_; // CPU type of task_ +}; + +//============================================================================== +// DynamicImageRef is just a simple wrapper for a pointer to +// DynamicImage. The reason we use it instead of a simple typedef is so +// that we can use stl::sort() on a vector of DynamicImageRefs +// and simple class pointers can't implement operator<(). +// +class DynamicImageRef { + public: + explicit DynamicImageRef(DynamicImage* inP) : p(inP) {} + // The copy constructor is required by STL + DynamicImageRef(const DynamicImageRef& inRef) = default; + DynamicImageRef& operator=(const DynamicImageRef& inRef) = default; + + bool operator<(const DynamicImageRef& inRef) const { + return (*const_cast(this)->p) + < (*const_cast(inRef).p); + } + + bool operator==(const DynamicImageRef& inInfo) const { + return (*const_cast(this)->p).GetLoadAddress() == + (*const_cast(inInfo)).GetLoadAddress(); + } + + // Be just like DynamicImage* + DynamicImage* operator->() {return p;} + operator DynamicImage*() {return p;} + + private: + DynamicImage* p; +}; + +// Helper function to deal with 32-bit/64-bit Mach-O differences. +class DynamicImages; +template +void ReadImageInfo(DynamicImages& images, uint64_t image_list_address); + +//============================================================================== +// An object of type DynamicImages may be created to allow introspection of +// an arbitrary task's dynamically loaded mach-o binaries. This makes the +// assumption that the current task has send rights to the target task. +class DynamicImages { + public: + explicit DynamicImages(mach_port_t task); + + ~DynamicImages() { + for (int i = 0; i < GetImageCount(); ++i) { + delete image_list_[i]; + } + } + + // Returns the number of dynamically loaded mach-o images. + int GetImageCount() const {return static_cast(image_list_.size());} + + // Returns an individual image. + DynamicImage* GetImage(int i) { + if (i < (int)image_list_.size()) { + return image_list_[i]; + } + return NULL; + } + + // Returns the image corresponding to the main executable. + DynamicImage* GetExecutableImage(); + int GetExecutableImageIndex(); + + // Returns the task which we're looking at. + mach_port_t GetTask() const {return task_;} + + // CPU type of the task + cpu_type_t GetCPUType() {return cpu_type_;} + + // Return true if the task is a 64-bit architecture. + bool Is64Bit() { return (GetCPUType() & CPU_ARCH_ABI64) == CPU_ARCH_ABI64; } + + // Determine the CPU type of the task being dumped. + static cpu_type_t DetermineTaskCPUType(task_t task); + + // Get the native CPU type of this task. + static cpu_type_t GetNativeCPUType() { +#if defined(__i386__) + return CPU_TYPE_I386; +#elif defined(__x86_64__) + return CPU_TYPE_X86_64; +#elif defined(__ppc__) + return CPU_TYPE_POWERPC; +#elif defined(__ppc64__) + return CPU_TYPE_POWERPC64; +#elif defined(__arm__) + return CPU_TYPE_ARM; +#elif defined(__aarch64__) + return CPU_TYPE_ARM64; +#else +#error "GetNativeCPUType not implemented for this architecture" +#endif + } + + private: + template + friend void ReadImageInfo(DynamicImages& images, uint64_t image_list_address); + + bool IsOurTask() {return task_ == mach_task_self();} + + // Initialization + void ReadImageInfoForTask(); + uint64_t GetDyldAllImageInfosPointer(); + + mach_port_t task_; + cpu_type_t cpu_type_; // CPU type of task_ + vector image_list_; +}; + +// Fill bytes with the contents of memory at a particular +// location in another task. +kern_return_t ReadTaskMemory(task_port_t target_task, + const uint64_t address, + size_t length, + vector& bytes); + +} // namespace google_breakpad + +#endif // CLIENT_MAC_HANDLER_DYNAMIC_IMAGES_H__ diff --git a/shared/sentry/external/breakpad/src/client/mac/handler/exception_handler.cc b/shared/sentry/external/breakpad/src/client/mac/handler/exception_handler.cc new file mode 100644 index 000000000..287fe1bec --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/handler/exception_handler.cc @@ -0,0 +1,860 @@ +// 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 +#include +#include +#include +#include + +#include + +#include "client/mac/handler/exception_handler.h" +#include "client/mac/handler/minidump_generator.h" +#include "common/mac/macho_utilities.h" +#include "common/mac/scoped_task_suspend-inl.h" +#include "google_breakpad/common/minidump_exception_mac.h" + +#ifndef __EXCEPTIONS +// This file uses C++ try/catch (but shouldn't). Duplicate the macros from +// allowing this file to work properly with +// exceptions disabled even when other C++ libraries are used. #undef the try +// and catch macros first in case libstdc++ is in use and has already provided +// its own definitions. +#undef try +#define try if (true) +#undef catch +#define catch(X) if (false) +#endif // __EXCEPTIONS + +#ifndef USE_PROTECTED_ALLOCATIONS +#if TARGET_OS_IPHONE +#define USE_PROTECTED_ALLOCATIONS 1 +#else +#define USE_PROTECTED_ALLOCATIONS 0 +#endif +#endif + +// If USE_PROTECTED_ALLOCATIONS is activated then the +// gBreakpadAllocator needs to be setup in other code +// ahead of time. Please see ProtectedMemoryAllocator.h +// for more details. +#if USE_PROTECTED_ALLOCATIONS + #include "protected_memory_allocator.h" + extern ProtectedMemoryAllocator *gBreakpadAllocator; +#endif + +namespace google_breakpad { + +static union { +#if USE_PROTECTED_ALLOCATIONS +#if defined PAGE_MAX_SIZE + char protected_buffer[PAGE_MAX_SIZE] __attribute__((aligned(PAGE_MAX_SIZE))); +#else + char protected_buffer[PAGE_SIZE] __attribute__((aligned(PAGE_SIZE))); +#endif // defined PAGE_MAX_SIZE +#endif // USE_PROTECTED_ALLOCATIONS + google_breakpad::ExceptionHandler *handler; +} gProtectedData; + +using std::map; + +// These structures and techniques are illustrated in +// Mac OS X Internals, Amit Singh, ch 9.7 +struct ExceptionMessage { + mach_msg_header_t header; + mach_msg_body_t body; + mach_msg_port_descriptor_t thread; + mach_msg_port_descriptor_t task; + NDR_record_t ndr; + exception_type_t exception; + mach_msg_type_number_t code_count; + integer_t code[EXCEPTION_CODE_MAX]; + char padding[512]; +}; + +struct ExceptionParameters { + ExceptionParameters() : count(0) {} + mach_msg_type_number_t count; + exception_mask_t masks[EXC_TYPES_COUNT]; + mach_port_t ports[EXC_TYPES_COUNT]; + exception_behavior_t behaviors[EXC_TYPES_COUNT]; + thread_state_flavor_t flavors[EXC_TYPES_COUNT]; +}; + +struct ExceptionReplyMessage { + mach_msg_header_t header; + NDR_record_t ndr; + kern_return_t return_code; +}; + +// Only catch these three exceptions. The other ones are nebulously defined +// and may result in treating a non-fatal exception as fatal. +exception_mask_t s_exception_mask = EXC_MASK_BAD_ACCESS | +EXC_MASK_BAD_INSTRUCTION | EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT; + +#if !TARGET_OS_IPHONE +extern "C" { + // Forward declarations for functions that need "C" style compilation + boolean_t exc_server(mach_msg_header_t* request, + mach_msg_header_t* reply); + + // This symbol must be visible to dlsym() - see + // https://bugs.chromium.org/p/google-breakpad/issues/detail?id=345 for details. + kern_return_t catch_exception_raise(mach_port_t target_port, + mach_port_t failed_thread, + mach_port_t task, + exception_type_t exception, + exception_data_t code, + mach_msg_type_number_t code_count) + __attribute__((visibility("default"))); +} +#endif + +kern_return_t ForwardException(mach_port_t task, + mach_port_t failed_thread, + exception_type_t exception, + exception_data_t code, + mach_msg_type_number_t code_count); + +#if TARGET_OS_IPHONE +// Implementation is based on the implementation generated by mig. +boolean_t breakpad_exc_server(mach_msg_header_t* InHeadP, + mach_msg_header_t* OutHeadP) { + OutHeadP->msgh_bits = + MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(InHeadP->msgh_bits), 0); + OutHeadP->msgh_remote_port = InHeadP->msgh_remote_port; + /* Minimal size: routine() will update it if different */ + OutHeadP->msgh_size = (mach_msg_size_t)sizeof(mig_reply_error_t); + OutHeadP->msgh_local_port = MACH_PORT_NULL; + OutHeadP->msgh_id = InHeadP->msgh_id + 100; + + if (InHeadP->msgh_id != 2401) { + ((mig_reply_error_t*)OutHeadP)->NDR = NDR_record; + ((mig_reply_error_t*)OutHeadP)->RetCode = MIG_BAD_ID; + return FALSE; + } + +#ifdef __MigPackStructs +#pragma pack(4) +#endif + typedef struct { + mach_msg_header_t Head; + /* start of the kernel processed data */ + mach_msg_body_t msgh_body; + mach_msg_port_descriptor_t thread; + mach_msg_port_descriptor_t task; + /* end of the kernel processed data */ + NDR_record_t NDR; + exception_type_t exception; + mach_msg_type_number_t codeCnt; + integer_t code[2]; + mach_msg_trailer_t trailer; + } Request; + + typedef struct { + mach_msg_header_t Head; + NDR_record_t NDR; + kern_return_t RetCode; + } Reply; +#ifdef __MigPackStructs +#pragma pack() +#endif + + Request* In0P = (Request*)InHeadP; + Reply* OutP = (Reply*)OutHeadP; + + if (In0P->task.name != mach_task_self()) { + return FALSE; + } + OutP->RetCode = ForwardException(In0P->task.name, + In0P->thread.name, + In0P->exception, + In0P->code, + In0P->codeCnt); + OutP->NDR = NDR_record; + return TRUE; +} +#else +boolean_t breakpad_exc_server(mach_msg_header_t* request, + mach_msg_header_t* reply) { + return exc_server(request, reply); +} + +// Callback from exc_server() +kern_return_t catch_exception_raise(mach_port_t port, mach_port_t failed_thread, + mach_port_t task, + exception_type_t exception, + exception_data_t code, + mach_msg_type_number_t code_count) { + if (task != mach_task_self()) { + return KERN_FAILURE; + } + return ForwardException(task, failed_thread, exception, code, code_count); +} +#endif + +ExceptionHandler::ExceptionHandler(const string& dump_path, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + bool install_handler, + const char* port_name) + : dump_path_(), + filter_(filter), + callback_(callback), + callback_context_(callback_context), + directCallback_(NULL), + handler_thread_(NULL), + handler_port_(MACH_PORT_NULL), + previous_(NULL), + installed_exception_handler_(false), + is_in_teardown_(false), + last_minidump_write_result_(false), + use_minidump_write_mutex_(false) { + // This will update to the ID and C-string pointers + set_dump_path(dump_path); + MinidumpGenerator::GatherSystemInformation(); +#if !TARGET_OS_IPHONE + if (port_name) + crash_generation_client_.reset(new CrashGenerationClient(port_name)); +#endif + Setup(install_handler); +} + +// special constructor if we want to bypass minidump writing and +// simply get a callback with the exception information +ExceptionHandler::ExceptionHandler(DirectCallback callback, + void* callback_context, + bool install_handler) + : dump_path_(), + filter_(NULL), + callback_(NULL), + callback_context_(callback_context), + directCallback_(callback), + handler_thread_(NULL), + handler_port_(MACH_PORT_NULL), + previous_(NULL), + installed_exception_handler_(false), + is_in_teardown_(false), + last_minidump_write_result_(false), + use_minidump_write_mutex_(false) { + MinidumpGenerator::GatherSystemInformation(); + Setup(install_handler); +} + +ExceptionHandler::~ExceptionHandler() { + Teardown(); +} + +bool ExceptionHandler::WriteMinidump(bool write_exception_stream) { + // If we're currently writing, just return + if (use_minidump_write_mutex_) + return false; + + use_minidump_write_mutex_ = true; + last_minidump_write_result_ = false; + + // Lock the mutex. Since we just created it, this will return immediately. + if (pthread_mutex_lock(&minidump_write_mutex_) == 0) { + // Send an empty message to the handle port so that a minidump will + // be written + bool result = SendMessageToHandlerThread(write_exception_stream ? + kWriteDumpWithExceptionMessage : + kWriteDumpMessage); + if (!result) { + pthread_mutex_unlock(&minidump_write_mutex_); + return false; + } + + // Wait for the minidump writer to complete its writing. It will unlock + // the mutex when completed + pthread_mutex_lock(&minidump_write_mutex_); + } + + use_minidump_write_mutex_ = false; + UpdateNextID(); + return last_minidump_write_result_; +} + +// static +bool ExceptionHandler::WriteMinidump(const string& dump_path, + bool write_exception_stream, + MinidumpCallback callback, + void* callback_context) { + ExceptionHandler handler(dump_path, NULL, callback, callback_context, false, + NULL); + return handler.WriteMinidump(write_exception_stream); +} + +// static +bool ExceptionHandler::WriteMinidumpForChild(mach_port_t child, + mach_port_t child_blamed_thread, + const string& dump_path, + MinidumpCallback callback, + void* callback_context) { + ScopedTaskSuspend suspend(child); + + MinidumpGenerator generator(child, MACH_PORT_NULL); + string dump_id; + string dump_filename = generator.UniqueNameInDirectory(dump_path, &dump_id); + + generator.SetExceptionInformation(EXC_BREAKPOINT, +#if defined(__i386__) || defined(__x86_64__) + EXC_I386_BPT, +#elif defined(__ppc__) || defined(__ppc64__) + EXC_PPC_BREAKPOINT, +#elif defined(__arm__) || defined(__aarch64__) + EXC_ARM_BREAKPOINT, +#else +#error architecture not supported +#endif + 0, + child_blamed_thread); + bool result = generator.Write(dump_filename.c_str()); + + if (callback) { + return callback(dump_path.c_str(), dump_id.c_str(), + callback_context, result); + } + return result; +} + +bool ExceptionHandler::WriteMinidumpWithException( + int exception_type, + int exception_code, + int exception_subcode, + breakpad_ucontext_t* task_context, + mach_port_t thread_name, + bool exit_after_write, + bool report_current_thread) { + bool result = false; + +#if TARGET_OS_IPHONE + // _exit() should never be called on iOS. + exit_after_write = false; +#endif + + if (directCallback_) { + if (directCallback_(callback_context_, + exception_type, + exception_code, + exception_subcode, + thread_name) ) { + if (exit_after_write) + _exit(exception_type); + } +#if !TARGET_OS_IPHONE + } else if (IsOutOfProcess()) { + if (exception_type && exception_code) { + // If this is a real exception, give the filter (if any) a chance to + // decide if this should be sent. + if (filter_ && !filter_(callback_context_)) + return false; + result = crash_generation_client_->RequestDumpForException( + exception_type, + exception_code, + exception_subcode, + thread_name); + if (result && exit_after_write) { + _exit(exception_type); + } + } +#endif + } else { + string minidump_id; + + // Putting the MinidumpGenerator in its own context will ensure that the + // destructor is executed, closing the newly created minidump file. + if (!dump_path_.empty()) { + MinidumpGenerator md(mach_task_self(), + report_current_thread ? MACH_PORT_NULL : + mach_thread_self()); + md.SetTaskContext(task_context); + if (exception_type && exception_code) { + // If this is a real exception, give the filter (if any) a chance to + // decide if this should be sent. + if (filter_ && !filter_(callback_context_)) + return false; + + md.SetExceptionInformation(exception_type, exception_code, + exception_subcode, thread_name); + } + + result = md.Write(next_minidump_path_c_); + } + + // Call user specified callback (if any) + if (callback_) { + // If the user callback returned true and we're handling an exception + // (rather than just writing out the file), then we should exit without + // forwarding the exception to the next handler. + if (callback_(dump_path_c_, next_minidump_id_c_, callback_context_, + result)) { + if (exit_after_write) + _exit(exception_type); + } + } + } + + return result; +} + +kern_return_t ForwardException(mach_port_t task, mach_port_t failed_thread, + exception_type_t exception, + exception_data_t code, + mach_msg_type_number_t code_count) { + // At this time, we should have called Uninstall() on the exception handler + // so that the current exception ports are the ones that we should be + // forwarding to. + ExceptionParameters current; + + current.count = EXC_TYPES_COUNT; + mach_port_t current_task = mach_task_self(); + task_get_exception_ports(current_task, + s_exception_mask, + current.masks, + ¤t.count, + current.ports, + current.behaviors, + current.flavors); + + // Find the first exception handler that matches the exception + unsigned int found; + for (found = 0; found < current.count; ++found) { + if (current.masks[found] & (1 << exception)) { + break; + } + } + + // Nothing to forward + if (found == current.count) { + fprintf(stderr, "** No previous ports for forwarding!! \n"); + exit(KERN_FAILURE); + } + + mach_port_t target_port = current.ports[found]; + exception_behavior_t target_behavior = current.behaviors[found]; + + kern_return_t result; + // TODO: Handle the case where |target_behavior| has MACH_EXCEPTION_CODES + // set. https://bugs.chromium.org/p/google-breakpad/issues/detail?id=551 + switch (target_behavior) { + case EXCEPTION_DEFAULT: + result = exception_raise(target_port, failed_thread, task, exception, + code, code_count); + break; + default: + fprintf(stderr, "** Unknown exception behavior: %d\n", target_behavior); + result = KERN_FAILURE; + break; + } + + return result; +} + +// static +void* ExceptionHandler::WaitForMessage(void* exception_handler_class) { + ExceptionHandler* self = + reinterpret_cast(exception_handler_class); + ExceptionMessage receive; + + // Wait for the exception info + while (1) { + receive.header.msgh_local_port = self->handler_port_; + receive.header.msgh_size = static_cast(sizeof(receive)); + kern_return_t result = mach_msg(&(receive.header), + MACH_RCV_MSG | MACH_RCV_LARGE, 0, + receive.header.msgh_size, + self->handler_port_, + MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + + + if (result == KERN_SUCCESS) { + // Uninstall our handler so that we don't get in a loop if the process of + // writing out a minidump causes an exception. However, if the exception + // was caused by a fork'd process, don't uninstall things + + // If the actual exception code is zero, then we're calling this handler + // in a way that indicates that we want to either exit this thread or + // generate a minidump + // + // While reporting, all threads (except this one) must be suspended + // to avoid misleading stacks. If appropriate they will be resumed + // afterwards. + if (!receive.exception) { + // Don't touch self, since this message could have been sent + // from its destructor. + if (receive.header.msgh_id == kShutdownMessage) + return NULL; + + self->SuspendThreads(); + +#if USE_PROTECTED_ALLOCATIONS + if (gBreakpadAllocator) + gBreakpadAllocator->Unprotect(); +#endif + + mach_port_t thread = MACH_PORT_NULL; + int exception_type = 0; + int exception_code = 0; + if (receive.header.msgh_id == kWriteDumpWithExceptionMessage) { + thread = receive.thread.name; + exception_type = EXC_BREAKPOINT; +#if defined(__i386__) || defined(__x86_64__) + exception_code = EXC_I386_BPT; +#elif defined(__ppc__) || defined(__ppc64__) + exception_code = EXC_PPC_BREAKPOINT; +#elif defined(__arm__) || defined(__aarch64__) + exception_code = EXC_ARM_BREAKPOINT; +#else +#error architecture not supported +#endif + } + + // Write out the dump and save the result for later retrieval + self->last_minidump_write_result_ = + self->WriteMinidumpWithException(exception_type, exception_code, + 0, NULL, thread, + false, false); + +#if USE_PROTECTED_ALLOCATIONS + if (gBreakpadAllocator) + gBreakpadAllocator->Protect(); +#endif + + self->ResumeThreads(); + + if (self->use_minidump_write_mutex_) + pthread_mutex_unlock(&self->minidump_write_mutex_); + } else { + // When forking a child process with the exception handler installed, + // if the child crashes, it will send the exception back to the parent + // process. The check for task == self_task() ensures that only + // exceptions that occur in the parent process are caught and + // processed. If the exception was not caused by this task, we + // still need to call into the exception server and have it return + // KERN_FAILURE (see catch_exception_raise) in order for the kernel + // to move onto the host exception handler for the child task + if (receive.task.name == mach_task_self()) { + self->SuspendThreads(); + +#if USE_PROTECTED_ALLOCATIONS + if (gBreakpadAllocator) + gBreakpadAllocator->Unprotect(); +#endif + + int subcode = 0; + if (receive.exception == EXC_BAD_ACCESS && receive.code_count > 1) + subcode = receive.code[1]; + + // Generate the minidump with the exception data. + self->WriteMinidumpWithException(receive.exception, receive.code[0], + subcode, NULL, receive.thread.name, + true, false); + +#if USE_PROTECTED_ALLOCATIONS + // This may have become protected again within + // WriteMinidumpWithException, but it needs to be unprotected for + // UninstallHandler. + if (gBreakpadAllocator) + gBreakpadAllocator->Unprotect(); +#endif + + self->UninstallHandler(true); + +#if USE_PROTECTED_ALLOCATIONS + if (gBreakpadAllocator) + gBreakpadAllocator->Protect(); +#endif + } + // Pass along the exception to the server, which will setup the + // message and call catch_exception_raise() and put the return + // code into the reply. + ExceptionReplyMessage reply; + if (!breakpad_exc_server(&receive.header, &reply.header)) + exit(1); + + // Send a reply and exit + mach_msg(&(reply.header), MACH_SEND_MSG, + reply.header.msgh_size, 0, MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + } + } + } + + return NULL; +} + +// static +void ExceptionHandler::SignalHandler(int sig, siginfo_t* info, void* uc) { +#if USE_PROTECTED_ALLOCATIONS + if (gBreakpadAllocator) + gBreakpadAllocator->Unprotect(); +#endif + gProtectedData.handler->WriteMinidumpWithException( + EXC_SOFTWARE, + MD_EXCEPTION_CODE_MAC_ABORT, + 0, + static_cast(uc), + mach_thread_self(), + true, + true); +#if USE_PROTECTED_ALLOCATIONS + if (gBreakpadAllocator) + gBreakpadAllocator->Protect(); +#endif +} + +bool ExceptionHandler::InstallHandler() { + // If a handler is already installed, something is really wrong. + if (gProtectedData.handler != NULL) { + return false; + } + if (!IsOutOfProcess()) { + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sigemptyset(&sa.sa_mask); + sigaddset(&sa.sa_mask, SIGABRT); + sa.sa_sigaction = ExceptionHandler::SignalHandler; + sa.sa_flags = SA_SIGINFO; + + scoped_ptr old(new struct sigaction); + if (sigaction(SIGABRT, &sa, old.get()) == -1) { + return false; + } + old_handler_.swap(old); + gProtectedData.handler = this; +#if USE_PROTECTED_ALLOCATIONS + assert(((size_t)(gProtectedData.protected_buffer) & PAGE_MASK) == 0); + mprotect(gProtectedData.protected_buffer, PAGE_SIZE, PROT_READ); +#endif + } + + try { +#if USE_PROTECTED_ALLOCATIONS + previous_ = new (gBreakpadAllocator->Allocate(sizeof(ExceptionParameters)) ) + ExceptionParameters(); +#else + previous_ = new ExceptionParameters(); +#endif + } + catch (std::bad_alloc) { + return false; + } + + // Save the current exception ports so that we can forward to them + previous_->count = EXC_TYPES_COUNT; + mach_port_t current_task = mach_task_self(); + kern_return_t result = task_get_exception_ports(current_task, + s_exception_mask, + previous_->masks, + &previous_->count, + previous_->ports, + previous_->behaviors, + previous_->flavors); + + // Setup the exception ports on this task + if (result == KERN_SUCCESS) + result = task_set_exception_ports(current_task, s_exception_mask, + handler_port_, EXCEPTION_DEFAULT, + THREAD_STATE_NONE); + + installed_exception_handler_ = (result == KERN_SUCCESS); + + return installed_exception_handler_; +} + +bool ExceptionHandler::UninstallHandler(bool in_exception) { + kern_return_t result = KERN_SUCCESS; + + if (old_handler_.get()) { + sigaction(SIGABRT, old_handler_.get(), NULL); +#if USE_PROTECTED_ALLOCATIONS + mprotect(gProtectedData.protected_buffer, PAGE_SIZE, + PROT_READ | PROT_WRITE); +#endif + old_handler_.reset(); + gProtectedData.handler = NULL; + } + + if (installed_exception_handler_) { + mach_port_t current_task = mach_task_self(); + + // Restore the previous ports + for (unsigned int i = 0; i < previous_->count; ++i) { + result = task_set_exception_ports(current_task, previous_->masks[i], + previous_->ports[i], + previous_->behaviors[i], + previous_->flavors[i]); + if (result != KERN_SUCCESS) + return false; + } + + // this delete should NOT happen if an exception just occurred! + if (!in_exception) { +#if USE_PROTECTED_ALLOCATIONS + previous_->~ExceptionParameters(); +#else + delete previous_; +#endif + } + + previous_ = NULL; + installed_exception_handler_ = false; + } + + return result == KERN_SUCCESS; +} + +bool ExceptionHandler::Setup(bool install_handler) { + if (pthread_mutex_init(&minidump_write_mutex_, NULL)) + return false; + + // Create a receive right + mach_port_t current_task = mach_task_self(); + kern_return_t result = mach_port_allocate(current_task, + MACH_PORT_RIGHT_RECEIVE, + &handler_port_); + // Add send right + if (result == KERN_SUCCESS) + result = mach_port_insert_right(current_task, handler_port_, handler_port_, + MACH_MSG_TYPE_MAKE_SEND); + + if (install_handler && result == KERN_SUCCESS) + if (!InstallHandler()) + return false; + + if (result == KERN_SUCCESS) { + // Install the handler in its own thread, detached as we won't be joining. + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + int thread_create_result = pthread_create(&handler_thread_, &attr, + &WaitForMessage, this); + pthread_attr_destroy(&attr); + result = thread_create_result ? KERN_FAILURE : KERN_SUCCESS; + } + + return result == KERN_SUCCESS; +} + +bool ExceptionHandler::Teardown() { + kern_return_t result = KERN_SUCCESS; + is_in_teardown_ = true; + + if (!UninstallHandler(false)) + return false; + + // Send an empty message so that the handler_thread exits + if (SendMessageToHandlerThread(kShutdownMessage)) { + mach_port_t current_task = mach_task_self(); + result = mach_port_deallocate(current_task, handler_port_); + if (result != KERN_SUCCESS) + return false; + } else { + return false; + } + + handler_thread_ = NULL; + handler_port_ = MACH_PORT_NULL; + pthread_mutex_destroy(&minidump_write_mutex_); + + return result == KERN_SUCCESS; +} + +bool ExceptionHandler::SendMessageToHandlerThread( + HandlerThreadMessage message_id) { + ExceptionMessage msg; + memset(&msg, 0, sizeof(msg)); + msg.header.msgh_id = message_id; + if (message_id == kWriteDumpMessage || + message_id == kWriteDumpWithExceptionMessage) { + // Include this thread's port. + msg.thread.name = mach_thread_self(); + msg.thread.disposition = MACH_MSG_TYPE_PORT_SEND; + msg.thread.type = MACH_MSG_PORT_DESCRIPTOR; + } + msg.header.msgh_size = sizeof(msg) - sizeof(msg.padding); + msg.header.msgh_remote_port = handler_port_; + msg.header.msgh_bits = MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, + MACH_MSG_TYPE_MAKE_SEND_ONCE); + kern_return_t result = mach_msg(&(msg.header), + MACH_SEND_MSG | MACH_SEND_TIMEOUT, + msg.header.msgh_size, 0, 0, + MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL); + + return result == KERN_SUCCESS; +} + +void ExceptionHandler::UpdateNextID() { + next_minidump_path_ = + (MinidumpGenerator::UniqueNameInDirectory(dump_path_, &next_minidump_id_)); + + next_minidump_path_c_ = next_minidump_path_.c_str(); + next_minidump_id_c_ = next_minidump_id_.c_str(); +} + +bool ExceptionHandler::SuspendThreads() { + thread_act_port_array_t threads_for_task; + mach_msg_type_number_t thread_count; + + if (task_threads(mach_task_self(), &threads_for_task, &thread_count)) + return false; + + // suspend all of the threads except for this one + for (unsigned int i = 0; i < thread_count; ++i) { + if (threads_for_task[i] != mach_thread_self()) { + if (thread_suspend(threads_for_task[i])) + return false; + } + } + + return true; +} + +bool ExceptionHandler::ResumeThreads() { + thread_act_port_array_t threads_for_task; + mach_msg_type_number_t thread_count; + + if (task_threads(mach_task_self(), &threads_for_task, &thread_count)) + return false; + + // resume all of the threads except for this one + for (unsigned int i = 0; i < thread_count; ++i) { + if (threads_for_task[i] != mach_thread_self()) { + if (thread_resume(threads_for_task[i])) + return false; + } + } + + return true; +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/mac/handler/exception_handler.h b/shared/sentry/external/breakpad/src/client/mac/handler/exception_handler.h new file mode 100644 index 000000000..fe7491fd4 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/handler/exception_handler.h @@ -0,0 +1,281 @@ +// 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. + +// exception_handler.h: MacOS exception handler +// This class can install a Mach exception port handler to trap most common +// programming errors. If an exception occurs, a minidump file will be +// generated which contains detailed information about the process and the +// exception. + +#ifndef CLIENT_MAC_HANDLER_EXCEPTION_HANDLER_H__ +#define CLIENT_MAC_HANDLER_EXCEPTION_HANDLER_H__ + +#include +#include + +#include + +#include "client/mac/handler/ucontext_compat.h" +#include "common/scoped_ptr.h" + +#if !TARGET_OS_IPHONE +#include "client/mac/crash_generation/crash_generation_client.h" +#endif + +namespace google_breakpad { + +using std::string; + +struct ExceptionParameters; + +enum HandlerThreadMessage { + // Message ID telling the handler thread to write a dump. + kWriteDumpMessage = 0, + // Message ID telling the handler thread to write a dump and include + // an exception stream. + kWriteDumpWithExceptionMessage = 1, + // Message ID telling the handler thread to quit. + kShutdownMessage = 2 +}; + +class ExceptionHandler { + public: + // A callback function to run before Breakpad performs any substantial + // processing of an exception. A FilterCallback is called before writing + // a minidump. context is the parameter supplied by the user as + // callback_context when the handler was created. + // + // If a FilterCallback returns true, Breakpad will continue processing, + // attempting to write a minidump. If a FilterCallback returns false, Breakpad + // will immediately report the exception as unhandled without writing a + // minidump, allowing another handler the opportunity to handle it. + typedef bool (*FilterCallback)(void* context); + + // A callback function to run after the minidump has been written. + // |minidump_id| is a unique id for the dump, so the minidump + // file is /.dmp. + // |context| is the value passed into the constructor. + // |succeeded| indicates whether a minidump file was successfully written. + // Return true if the exception was fully handled and breakpad should exit. + // Return false to allow any other exception handlers to process the + // exception. + typedef bool (*MinidumpCallback)(const char* dump_dir, + const char* minidump_id, + void* context, bool succeeded); + + // A callback function which will be called directly if an exception occurs. + // This bypasses the minidump file writing and simply gives the client + // the exception information. + typedef bool (*DirectCallback)(void* context, + int exception_type, + int exception_code, + int exception_subcode, + mach_port_t thread_name); + + // Creates a new ExceptionHandler instance to handle writing minidumps. + // Minidump files will be written to dump_path, and the optional callback + // is called after writing the dump file, as described above. + // If install_handler is true, then a minidump will be written whenever + // an unhandled exception occurs. If it is false, minidumps will only + // be written when WriteMinidump is called. + // If port_name is non-NULL, attempt to perform out-of-process dump generation + // If port_name is NULL, in-process dump generation will be used. + ExceptionHandler(const string& dump_path, + FilterCallback filter, MinidumpCallback callback, + void* callback_context, bool install_handler, + const char* port_name); + + // A special constructor if we want to bypass minidump writing and + // simply get a callback with the exception information. + ExceptionHandler(DirectCallback callback, + void* callback_context, + bool install_handler); + + ~ExceptionHandler(); + + // Get and set the minidump path. + string dump_path() const { return dump_path_; } + void set_dump_path(const string& dump_path) { + dump_path_ = dump_path; + dump_path_c_ = dump_path_.c_str(); + UpdateNextID(); // Necessary to put dump_path_ in next_minidump_path_. + } + + // Writes a minidump immediately. This can be used to capture the + // execution state independently of a crash. Returns true on success. + bool WriteMinidump() { + return WriteMinidump(false); + } + + bool WriteMinidump(bool write_exception_stream); + + // Convenience form of WriteMinidump which does not require an + // ExceptionHandler instance. + static bool WriteMinidump(const string& dump_path, MinidumpCallback callback, + void* callback_context) { + return WriteMinidump(dump_path, false, callback, callback_context); + } + + static bool WriteMinidump(const string& dump_path, + bool write_exception_stream, + MinidumpCallback callback, + void* callback_context); + + // Write a minidump of child immediately. This can be used to capture + // the execution state of a child process independently of a crash. + static bool WriteMinidumpForChild(mach_port_t child, + mach_port_t child_blamed_thread, + const std::string& dump_path, + MinidumpCallback callback, + void* callback_context); + + // Returns whether out-of-process dump generation is used or not. + bool IsOutOfProcess() const { +#if TARGET_OS_IPHONE + return false; +#else + return crash_generation_client_.get() != NULL; +#endif + } + + private: + // Install the mach exception handler + bool InstallHandler(); + + // Uninstall the mach exception handler (if any) + bool UninstallHandler(bool in_exception); + + // Setup the handler thread, and if |install_handler| is true, install the + // mach exception port handler + bool Setup(bool install_handler); + + // Uninstall the mach exception handler (if any) and terminate the helper + // thread + bool Teardown(); + + // Send a mach message to the exception handler. Return true on + // success, false otherwise. + bool SendMessageToHandlerThread(HandlerThreadMessage message_id); + + // All minidump writing goes through this one routine. + // |task_context| can be NULL. If not, it will be used to retrieve the + // context of the current thread, instead of using |thread_get_state|. + bool WriteMinidumpWithException(int exception_type, + int exception_code, + int exception_subcode, + breakpad_ucontext_t* task_context, + mach_port_t thread_name, + bool exit_after_write, + bool report_current_thread); + + // When installed, this static function will be call from a newly created + // pthread with |this| as the argument + static void* WaitForMessage(void* exception_handler_class); + + // Signal handler for SIGABRT. + static void SignalHandler(int sig, siginfo_t* info, void* uc); + + // disallow copy ctor and operator= + explicit ExceptionHandler(const ExceptionHandler&); + void operator=(const ExceptionHandler&); + + // Generates a new ID and stores it in next_minidump_id_, and stores the + // path of the next minidump to be written in next_minidump_path_. + void UpdateNextID(); + + // These functions will suspend/resume all threads except for the + // reporting thread + bool SuspendThreads(); + bool ResumeThreads(); + + // The destination directory for the minidump + string dump_path_; + + // The basename of the next minidump w/o extension + string next_minidump_id_; + + // The full path to the next minidump to be written, including extension + string next_minidump_path_; + + // Pointers to the UTF-8 versions of above + const char* dump_path_c_; + const char* next_minidump_id_c_; + const char* next_minidump_path_c_; + + // The callback function and pointer to be passed back after the minidump + // has been written + FilterCallback filter_; + MinidumpCallback callback_; + void* callback_context_; + + // The callback function to be passed back when we don't want a minidump + // file to be written + DirectCallback directCallback_; + + // The thread that is created for the handler + pthread_t handler_thread_; + + // The port that is waiting on an exception message to be sent, if the + // handler is installed + mach_port_t handler_port_; + + // These variables save the previous exception handler's data so that it + // can be re-installed when this handler is uninstalled + ExceptionParameters* previous_; + + // True, if we've installed the exception handler + bool installed_exception_handler_; + + // True, if we're in the process of uninstalling the exception handler and + // the thread. + bool is_in_teardown_; + + // Save the last result of the last minidump + bool last_minidump_write_result_; + + // A mutex for use when writing out a minidump that was requested on a + // thread other than the exception handler. + pthread_mutex_t minidump_write_mutex_; + + // True, if we're using the mutext to indicate when mindump writing occurs + bool use_minidump_write_mutex_; + + // Old signal handler for SIGABRT. Used to be able to restore it when + // uninstalling. + scoped_ptr old_handler_; + +#if !TARGET_OS_IPHONE + // Client for out-of-process dump generation. + scoped_ptr crash_generation_client_; +#endif +}; + +} // namespace google_breakpad + +#endif // CLIENT_MAC_HANDLER_EXCEPTION_HANDLER_H__ diff --git a/shared/sentry/external/breakpad/src/client/mac/handler/mach_vm_compat.h b/shared/sentry/external/breakpad/src/client/mac/handler/mach_vm_compat.h new file mode 100644 index 000000000..9e9028b92 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/handler/mach_vm_compat.h @@ -0,0 +1,48 @@ +// 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 CLIENT_MAC_GENERATOR_MACH_VM_COMPAT_H_ +#define CLIENT_MAC_GENERATOR_MACH_VM_COMPAT_H_ + +#include + +// On iOS 5 and higher, mach/mach_vm.h is not supported. Use the corresponding +// vm_map functions instead. +#if TARGET_OS_IPHONE +#include +#define mach_vm_address_t vm_address_t +#define mach_vm_deallocate vm_deallocate +#define mach_vm_read vm_read +#define mach_vm_region_recurse vm_region_recurse_64 +#define mach_vm_size_t vm_size_t +#else +#include +#endif // TARGET_OS_IPHONE + +#endif // CLIENT_MAC_GENERATOR_MACH_VM_COMPAT_H_ diff --git a/shared/sentry/external/breakpad/src/client/mac/handler/minidump_generator.cc b/shared/sentry/external/breakpad/src/client/mac/handler/minidump_generator.cc new file mode 100644 index 000000000..05d91b08e --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/handler/minidump_generator.cc @@ -0,0 +1,1606 @@ +// 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 +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "client/mac/handler/minidump_generator.h" + +#if defined(HAS_ARM_SUPPORT) || defined(HAS_ARM64_SUPPORT) +#include +#endif +#ifdef HAS_PPC_SUPPORT +#include +#endif +#ifdef HAS_X86_SUPPORT +#include +#endif + +#include "client/minidump_file_writer-inl.h" +#include "common/mac/file_id.h" +#include "common/mac/macho_id.h" +#include "common/mac/string_utilities.h" + +using MacStringUtils::ConvertToString; +using MacStringUtils::IntegerValueAtIndex; + +namespace google_breakpad { + +using mach_o::FileID; + +#if defined(__LP64__) && __LP64__ +#define LC_SEGMENT_ARCH LC_SEGMENT_64 +#else +#define LC_SEGMENT_ARCH LC_SEGMENT +#endif + +// constructor when generating from within the crashed process +MinidumpGenerator::MinidumpGenerator() + : writer_(), + exception_type_(0), + exception_code_(0), + exception_subcode_(0), + exception_thread_(0), + crashing_task_(mach_task_self()), + handler_thread_(mach_thread_self()), + cpu_type_(DynamicImages::GetNativeCPUType()), + task_context_(NULL), + dynamic_images_(NULL), + memory_blocks_(&allocator_) { + GatherSystemInformation(); +} + +// constructor when generating from a different process than the +// crashed process +MinidumpGenerator::MinidumpGenerator(mach_port_t crashing_task, + mach_port_t handler_thread) + : writer_(), + exception_type_(0), + exception_code_(0), + exception_subcode_(0), + exception_thread_(0), + crashing_task_(crashing_task), + handler_thread_(handler_thread), + cpu_type_(DynamicImages::GetNativeCPUType()), + task_context_(NULL), + dynamic_images_(NULL), + memory_blocks_(&allocator_) { + if (crashing_task != mach_task_self()) { + dynamic_images_ = new DynamicImages(crashing_task_); + cpu_type_ = dynamic_images_->GetCPUType(); + } else { + dynamic_images_ = NULL; + cpu_type_ = DynamicImages::GetNativeCPUType(); + } + + GatherSystemInformation(); +} + +MinidumpGenerator::~MinidumpGenerator() { + delete dynamic_images_; +} + +char MinidumpGenerator::build_string_[16]; +int MinidumpGenerator::os_major_version_ = 0; +int MinidumpGenerator::os_minor_version_ = 0; +int MinidumpGenerator::os_build_number_ = 0; + +// static +void MinidumpGenerator::GatherSystemInformation() { + // If this is non-zero, then we've already gathered the information + if (os_major_version_) + return; + + // This code extracts the version and build information from the OS + CFStringRef vers_path = + CFSTR("/System/Library/CoreServices/SystemVersion.plist"); + CFURLRef sys_vers = + CFURLCreateWithFileSystemPath(NULL, + vers_path, + kCFURLPOSIXPathStyle, + false); + CFReadStreamRef read_stream = CFReadStreamCreateWithFile(NULL, sys_vers); + CFRelease(sys_vers); + if (!read_stream) { + return; + } + if (!CFReadStreamOpen(read_stream)) { + CFRelease(read_stream); + return; + } + CFMutableDataRef data = NULL; + while (true) { + // Actual data file tests: Mac at 480 bytes and iOS at 413 bytes. + const CFIndex kMaxBufferLength = 1024; + UInt8 data_bytes[kMaxBufferLength]; + CFIndex num_bytes_read = + CFReadStreamRead(read_stream, data_bytes, kMaxBufferLength); + if (num_bytes_read < 0) { + if (data) { + CFRelease(data); + data = NULL; + } + break; + } else if (num_bytes_read == 0) { + break; + } else if (!data) { + data = CFDataCreateMutable(NULL, 0); + } + CFDataAppendBytes(data, data_bytes, num_bytes_read); + } + CFReadStreamClose(read_stream); + CFRelease(read_stream); + if (!data) { + return; + } + CFDictionaryRef list = + static_cast(CFPropertyListCreateWithData( + NULL, data, kCFPropertyListImmutable, NULL, NULL)); + CFRelease(data); + if (!list) { + return; + } + CFStringRef build_version = static_cast + (CFDictionaryGetValue(list, CFSTR("ProductBuildVersion"))); + CFStringRef product_version = static_cast + (CFDictionaryGetValue(list, CFSTR("ProductVersion"))); + string build_str = ConvertToString(build_version); + string product_str = ConvertToString(product_version); + + CFRelease(list); + + strlcpy(build_string_, build_str.c_str(), sizeof(build_string_)); + + // Parse the string that looks like "10.4.8" + os_major_version_ = IntegerValueAtIndex(product_str, 0); + os_minor_version_ = IntegerValueAtIndex(product_str, 1); + os_build_number_ = IntegerValueAtIndex(product_str, 2); +} + +void MinidumpGenerator::SetTaskContext(breakpad_ucontext_t* task_context) { + task_context_ = task_context; +} + +string MinidumpGenerator::UniqueNameInDirectory(const string& dir, + string* unique_name) { + CFUUIDRef uuid = CFUUIDCreate(NULL); + CFStringRef uuid_cfstr = CFUUIDCreateString(NULL, uuid); + CFRelease(uuid); + string file_name(ConvertToString(uuid_cfstr)); + CFRelease(uuid_cfstr); + string path(dir); + + // Ensure that the directory (if non-empty) has a trailing slash so that + // we can append the file name and have a valid pathname. + if (!dir.empty()) { + if (dir.at(dir.size() - 1) != '/') + path.append(1, '/'); + } + + path.append(file_name); + path.append(".dmp"); + + if (unique_name) + *unique_name = file_name; + + return path; +} + +bool MinidumpGenerator::Write(const char* path) { + WriteStreamFN writers[] = { + &MinidumpGenerator::WriteThreadListStream, + &MinidumpGenerator::WriteMemoryListStream, + &MinidumpGenerator::WriteSystemInfoStream, + &MinidumpGenerator::WriteModuleListStream, + &MinidumpGenerator::WriteMiscInfoStream, + &MinidumpGenerator::WriteBreakpadInfoStream, + // Exception stream needs to be the last entry in this array as it may + // be omitted in the case where the minidump is written without an + // exception. + &MinidumpGenerator::WriteExceptionStream, + }; + bool result = false; + + // If opening was successful, create the header, directory, and call each + // writer. The destructor for the TypedMDRVAs will cause the data to be + // flushed. The destructor for the MinidumpFileWriter will close the file. + if (writer_.Open(path)) { + TypedMDRVA header(&writer_); + TypedMDRVA dir(&writer_); + + if (!header.Allocate()) + return false; + + int writer_count = static_cast(sizeof(writers) / sizeof(writers[0])); + + // If we don't have exception information, don't write out the + // exception stream + if (!exception_thread_ && !exception_type_) + --writer_count; + + // Add space for all writers + if (!dir.AllocateArray(writer_count)) + return false; + + MDRawHeader* header_ptr = header.get(); + header_ptr->signature = MD_HEADER_SIGNATURE; + header_ptr->version = MD_HEADER_VERSION; + time(reinterpret_cast(&(header_ptr->time_date_stamp))); + header_ptr->stream_count = writer_count; + header_ptr->stream_directory_rva = dir.position(); + + MDRawDirectory local_dir; + result = true; + for (int i = 0; (result) && (i < writer_count); ++i) { + result = (this->*writers[i])(&local_dir); + + if (result) + dir.CopyIndex(i, &local_dir); + } + } + return result; +} + +size_t MinidumpGenerator::CalculateStackSize(mach_vm_address_t start_addr) { + mach_vm_address_t stack_region_base = start_addr; + mach_vm_size_t stack_region_size; + natural_t nesting_level = 0; + vm_region_submap_info_64 submap_info; + mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64; + + vm_region_recurse_info_t region_info; + region_info = reinterpret_cast(&submap_info); + + if (start_addr == 0) { + return 0; + } + + kern_return_t result = + mach_vm_region_recurse(crashing_task_, &stack_region_base, + &stack_region_size, &nesting_level, + region_info, &info_count); + + if (result != KERN_SUCCESS || start_addr < stack_region_base) { + // Failure or stack corruption, since mach_vm_region had to go + // higher in the process address space to find a valid region. + return 0; + } + + unsigned int tag = submap_info.user_tag; + + // If the user tag is VM_MEMORY_STACK, look for more readable regions with + // the same tag placed immediately above the computed stack region. Under + // some circumstances, the stack for thread 0 winds up broken up into + // multiple distinct abutting regions. This can happen for several reasons, + // including user code that calls setrlimit(RLIMIT_STACK, ...) or changes + // the access on stack pages by calling mprotect. + if (tag == VM_MEMORY_STACK) { + while (true) { + mach_vm_address_t next_region_base = stack_region_base + + stack_region_size; + mach_vm_address_t proposed_next_region_base = next_region_base; + mach_vm_size_t next_region_size; + nesting_level = 0; + info_count = VM_REGION_SUBMAP_INFO_COUNT_64; + result = mach_vm_region_recurse(crashing_task_, &next_region_base, + &next_region_size, &nesting_level, + region_info, &info_count); + if (result != KERN_SUCCESS || + next_region_base != proposed_next_region_base || + submap_info.user_tag != tag || + (submap_info.protection & VM_PROT_READ) == 0) { + break; + } + + stack_region_size += next_region_size; + } + } + + return stack_region_base + stack_region_size - start_addr; +} + +bool MinidumpGenerator::WriteStackFromStartAddress( + mach_vm_address_t start_addr, + MDMemoryDescriptor* stack_location) { + UntypedMDRVA memory(&writer_); + + bool result = false; + size_t size = CalculateStackSize(start_addr); + + if (size == 0) { + // In some situations the stack address for the thread can come back 0. + // In these cases we skip over the threads in question and stuff the + // stack with a clearly borked value. + start_addr = 0xDEADBEEF; + size = 16; + if (!memory.Allocate(size)) + return false; + + unsigned long long dummy_stack[2]; // Fill dummy stack with 16 bytes of + // junk. + dummy_stack[0] = 0xDEADBEEF; + dummy_stack[1] = 0xDEADBEEF; + + result = memory.Copy(dummy_stack, size); + } else { + + if (!memory.Allocate(size)) + return false; + + if (dynamic_images_) { + vector stack_memory; + if (ReadTaskMemory(crashing_task_, + start_addr, + size, + stack_memory) != KERN_SUCCESS) { + return false; + } + + result = memory.Copy(&stack_memory[0], size); + } else { + result = memory.Copy(reinterpret_cast(start_addr), size); + } + } + + stack_location->start_of_memory_range = start_addr; + stack_location->memory = memory.location(); + + return result; +} + +bool MinidumpGenerator::WriteStack(breakpad_thread_state_data_t state, + MDMemoryDescriptor* stack_location) { + switch (cpu_type_) { +#ifdef HAS_ARM_SUPPORT + case CPU_TYPE_ARM: + return WriteStackARM(state, stack_location); +#endif +#ifdef HAS_ARM64_SUPPORT + case CPU_TYPE_ARM64: + return WriteStackARM64(state, stack_location); +#endif +#ifdef HAS_PPC_SUPPORT + case CPU_TYPE_POWERPC: + return WriteStackPPC(state, stack_location); + case CPU_TYPE_POWERPC64: + return WriteStackPPC64(state, stack_location); +#endif +#ifdef HAS_X86_SUPPORT + case CPU_TYPE_I386: + return WriteStackX86(state, stack_location); + case CPU_TYPE_X86_64: + return WriteStackX86_64(state, stack_location); +#endif + default: + return false; + } +} + +bool MinidumpGenerator::WriteContext(breakpad_thread_state_data_t state, + MDLocationDescriptor* register_location) { + switch (cpu_type_) { +#ifdef HAS_ARM_SUPPORT + case CPU_TYPE_ARM: + return WriteContextARM(state, register_location); +#endif +#ifdef HAS_ARM64_SUPPORT + case CPU_TYPE_ARM64: + return WriteContextARM64(state, register_location); +#endif +#ifdef HAS_PPC_SUPPORT + case CPU_TYPE_POWERPC: + return WriteContextPPC(state, register_location); + case CPU_TYPE_POWERPC64: + return WriteContextPPC64(state, register_location); +#endif +#ifdef HAS_X86_SUPPORT + case CPU_TYPE_I386: + return WriteContextX86(state, register_location); + case CPU_TYPE_X86_64: + return WriteContextX86_64(state, register_location); +#endif + default: + return false; + } +} + +uint64_t MinidumpGenerator::CurrentPCForStack( + breakpad_thread_state_data_t state) { + switch (cpu_type_) { +#ifdef HAS_ARM_SUPPORT + case CPU_TYPE_ARM: + return CurrentPCForStackARM(state); +#endif +#ifdef HAS_ARM64_SUPPORT + case CPU_TYPE_ARM64: + return CurrentPCForStackARM64(state); +#endif +#ifdef HAS_PPC_SUPPORT + case CPU_TYPE_POWERPC: + return CurrentPCForStackPPC(state); + case CPU_TYPE_POWERPC64: + return CurrentPCForStackPPC64(state); +#endif +#ifdef HAS_X86_SUPPORT + case CPU_TYPE_I386: + return CurrentPCForStackX86(state); + case CPU_TYPE_X86_64: + return CurrentPCForStackX86_64(state); +#endif + default: + assert(0 && "Unknown CPU type!"); + return 0; + } +} + +#ifdef HAS_ARM_SUPPORT +bool MinidumpGenerator::WriteStackARM(breakpad_thread_state_data_t state, + MDMemoryDescriptor* stack_location) { + arm_thread_state_t* machine_state = + reinterpret_cast(state); + mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, sp); + return WriteStackFromStartAddress(start_addr, stack_location); +} + +uint64_t +MinidumpGenerator::CurrentPCForStackARM(breakpad_thread_state_data_t state) { + arm_thread_state_t* machine_state = + reinterpret_cast(state); + + return REGISTER_FROM_THREADSTATE(machine_state, pc); +} + +bool MinidumpGenerator::WriteContextARM(breakpad_thread_state_data_t state, + MDLocationDescriptor* register_location) +{ + TypedMDRVA context(&writer_); + arm_thread_state_t* machine_state = + reinterpret_cast(state); + + if (!context.Allocate()) + return false; + + *register_location = context.location(); + MDRawContextARM* context_ptr = context.get(); + context_ptr->context_flags = MD_CONTEXT_ARM_FULL; + +#define AddGPR(a) context_ptr->iregs[a] = REGISTER_FROM_THREADSTATE(machine_state, r[a]) + + context_ptr->iregs[13] = REGISTER_FROM_THREADSTATE(machine_state, sp); + context_ptr->iregs[14] = REGISTER_FROM_THREADSTATE(machine_state, lr); + context_ptr->iregs[15] = REGISTER_FROM_THREADSTATE(machine_state, pc); + context_ptr->cpsr = REGISTER_FROM_THREADSTATE(machine_state, cpsr); + + AddGPR(0); + AddGPR(1); + AddGPR(2); + AddGPR(3); + AddGPR(4); + AddGPR(5); + AddGPR(6); + AddGPR(7); + AddGPR(8); + AddGPR(9); + AddGPR(10); + AddGPR(11); + AddGPR(12); +#undef AddGPR + + return true; +} +#endif + +#ifdef HAS_ARM64_SUPPORT +bool MinidumpGenerator::WriteStackARM64(breakpad_thread_state_data_t state, + MDMemoryDescriptor* stack_location) { + arm_thread_state64_t* machine_state = + reinterpret_cast(state); + mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, sp); + return WriteStackFromStartAddress(start_addr, stack_location); +} + +uint64_t +MinidumpGenerator::CurrentPCForStackARM64(breakpad_thread_state_data_t state) { + arm_thread_state64_t* machine_state = + reinterpret_cast(state); + + return REGISTER_FROM_THREADSTATE(machine_state, pc); +} + +bool +MinidumpGenerator::WriteContextARM64(breakpad_thread_state_data_t state, + MDLocationDescriptor* register_location) +{ + TypedMDRVA context(&writer_); + arm_thread_state64_t* machine_state = + reinterpret_cast(state); + + if (!context.Allocate()) + return false; + + *register_location = context.location(); + MDRawContextARM64_Old* context_ptr = context.get(); + context_ptr->context_flags = MD_CONTEXT_ARM64_FULL_OLD; + +#define AddGPR(a) \ + context_ptr->iregs[a] = ARRAY_REGISTER_FROM_THREADSTATE(machine_state, x, a) + + context_ptr->iregs[29] = REGISTER_FROM_THREADSTATE(machine_state, fp); + context_ptr->iregs[30] = REGISTER_FROM_THREADSTATE(machine_state, lr); + context_ptr->iregs[31] = REGISTER_FROM_THREADSTATE(machine_state, sp); + context_ptr->iregs[32] = REGISTER_FROM_THREADSTATE(machine_state, pc); + context_ptr->cpsr = REGISTER_FROM_THREADSTATE(machine_state, cpsr); + + AddGPR(0); + AddGPR(1); + AddGPR(2); + AddGPR(3); + AddGPR(4); + AddGPR(5); + AddGPR(6); + AddGPR(7); + AddGPR(8); + AddGPR(9); + AddGPR(10); + AddGPR(11); + AddGPR(12); + AddGPR(13); + AddGPR(14); + AddGPR(15); + AddGPR(16); + AddGPR(17); + AddGPR(18); + AddGPR(19); + AddGPR(20); + AddGPR(21); + AddGPR(22); + AddGPR(23); + AddGPR(24); + AddGPR(25); + AddGPR(26); + AddGPR(27); + AddGPR(28); +#undef AddGPR + + return true; +} +#endif + +#ifdef HAS_PCC_SUPPORT +bool MinidumpGenerator::WriteStackPPC(breakpad_thread_state_data_t state, + MDMemoryDescriptor* stack_location) { + ppc_thread_state_t* machine_state = + reinterpret_cast(state); + mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, r1); + return WriteStackFromStartAddress(start_addr, stack_location); +} + +bool MinidumpGenerator::WriteStackPPC64(breakpad_thread_state_data_t state, + MDMemoryDescriptor* stack_location) { + ppc_thread_state64_t* machine_state = + reinterpret_cast(state); + mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, r1); + return WriteStackFromStartAddress(start_addr, stack_location); +} + +uint64_t +MinidumpGenerator::CurrentPCForStackPPC(breakpad_thread_state_data_t state) { + ppc_thread_state_t* machine_state = + reinterpret_cast(state); + + return REGISTER_FROM_THREADSTATE(machine_state, srr0); +} + +uint64_t +MinidumpGenerator::CurrentPCForStackPPC64(breakpad_thread_state_data_t state) { + ppc_thread_state64_t* machine_state = + reinterpret_cast(state); + + return REGISTER_FROM_THREADSTATE(machine_state, srr0); +} + +bool MinidumpGenerator::WriteContextPPC(breakpad_thread_state_data_t state, + MDLocationDescriptor* register_location) +{ + TypedMDRVA context(&writer_); + ppc_thread_state_t* machine_state = + reinterpret_cast(state); + + if (!context.Allocate()) + return false; + + *register_location = context.location(); + MDRawContextPPC* context_ptr = context.get(); + context_ptr->context_flags = MD_CONTEXT_PPC_BASE; + +#define AddReg(a) context_ptr->a = static_cast<__typeof__(context_ptr->a)>( \ + REGISTER_FROM_THREADSTATE(machine_state, a)) +#define AddGPR(a) context_ptr->gpr[a] = \ + static_cast<__typeof__(context_ptr->a)>( \ + REGISTER_FROM_THREADSTATE(machine_state, r ## a) + + AddReg(srr0); + AddReg(cr); + AddReg(xer); + AddReg(ctr); + AddReg(lr); + AddReg(vrsave); + + AddGPR(0); + AddGPR(1); + AddGPR(2); + AddGPR(3); + AddGPR(4); + AddGPR(5); + AddGPR(6); + AddGPR(7); + AddGPR(8); + AddGPR(9); + AddGPR(10); + AddGPR(11); + AddGPR(12); + AddGPR(13); + AddGPR(14); + AddGPR(15); + AddGPR(16); + AddGPR(17); + AddGPR(18); + AddGPR(19); + AddGPR(20); + AddGPR(21); + AddGPR(22); + AddGPR(23); + AddGPR(24); + AddGPR(25); + AddGPR(26); + AddGPR(27); + AddGPR(28); + AddGPR(29); + AddGPR(30); + AddGPR(31); + AddReg(mq); +#undef AddReg +#undef AddGPR + + return true; +} + +bool MinidumpGenerator::WriteContextPPC64( + breakpad_thread_state_data_t state, + MDLocationDescriptor* register_location) { + TypedMDRVA context(&writer_); + ppc_thread_state64_t* machine_state = + reinterpret_cast(state); + + if (!context.Allocate()) + return false; + + *register_location = context.location(); + MDRawContextPPC64* context_ptr = context.get(); + context_ptr->context_flags = MD_CONTEXT_PPC_BASE; + +#define AddReg(a) context_ptr->a = static_cast<__typeof__(context_ptr->a)>( \ + REGISTER_FROM_THREADSTATE(machine_state, a)) +#define AddGPR(a) context_ptr->gpr[a] = \ + static_cast<__typeof__(context_ptr->a)>( \ + REGISTER_FROM_THREADSTATE(machine_state, r ## a) + + AddReg(srr0); + AddReg(cr); + AddReg(xer); + AddReg(ctr); + AddReg(lr); + AddReg(vrsave); + + AddGPR(0); + AddGPR(1); + AddGPR(2); + AddGPR(3); + AddGPR(4); + AddGPR(5); + AddGPR(6); + AddGPR(7); + AddGPR(8); + AddGPR(9); + AddGPR(10); + AddGPR(11); + AddGPR(12); + AddGPR(13); + AddGPR(14); + AddGPR(15); + AddGPR(16); + AddGPR(17); + AddGPR(18); + AddGPR(19); + AddGPR(20); + AddGPR(21); + AddGPR(22); + AddGPR(23); + AddGPR(24); + AddGPR(25); + AddGPR(26); + AddGPR(27); + AddGPR(28); + AddGPR(29); + AddGPR(30); + AddGPR(31); +#undef AddReg +#undef AddGPR + + return true; +} + +#endif + +#ifdef HAS_X86_SUPPORT +bool MinidumpGenerator::WriteStackX86(breakpad_thread_state_data_t state, + MDMemoryDescriptor* stack_location) { + i386_thread_state_t* machine_state = + reinterpret_cast(state); + + mach_vm_address_t start_addr = REGISTER_FROM_THREADSTATE(machine_state, esp); + return WriteStackFromStartAddress(start_addr, stack_location); +} + +bool MinidumpGenerator::WriteStackX86_64(breakpad_thread_state_data_t state, + MDMemoryDescriptor* stack_location) { + x86_thread_state64_t* machine_state = + reinterpret_cast(state); + + mach_vm_address_t start_addr = static_cast( + REGISTER_FROM_THREADSTATE(machine_state, rsp)); + return WriteStackFromStartAddress(start_addr, stack_location); +} + +uint64_t +MinidumpGenerator::CurrentPCForStackX86(breakpad_thread_state_data_t state) { + i386_thread_state_t* machine_state = + reinterpret_cast(state); + + return REGISTER_FROM_THREADSTATE(machine_state, eip); +} + +uint64_t +MinidumpGenerator::CurrentPCForStackX86_64(breakpad_thread_state_data_t state) { + x86_thread_state64_t* machine_state = + reinterpret_cast(state); + + return REGISTER_FROM_THREADSTATE(machine_state, rip); +} + +bool MinidumpGenerator::WriteContextX86(breakpad_thread_state_data_t state, + MDLocationDescriptor* register_location) +{ + TypedMDRVA context(&writer_); + i386_thread_state_t* machine_state = + reinterpret_cast(state); + + if (!context.Allocate()) + return false; + + *register_location = context.location(); + MDRawContextX86* context_ptr = context.get(); + +#define AddReg(a) context_ptr->a = static_cast<__typeof__(context_ptr->a)>( \ + REGISTER_FROM_THREADSTATE(machine_state, a)) + + context_ptr->context_flags = MD_CONTEXT_X86; + AddReg(eax); + AddReg(ebx); + AddReg(ecx); + AddReg(edx); + AddReg(esi); + AddReg(edi); + AddReg(ebp); + AddReg(esp); + + AddReg(cs); + AddReg(ds); + AddReg(ss); + AddReg(es); + AddReg(fs); + AddReg(gs); + AddReg(eflags); + + AddReg(eip); +#undef AddReg + + return true; +} + +bool MinidumpGenerator::WriteContextX86_64( + breakpad_thread_state_data_t state, + MDLocationDescriptor* register_location) { + TypedMDRVA context(&writer_); + x86_thread_state64_t* machine_state = + reinterpret_cast(state); + + if (!context.Allocate()) + return false; + + *register_location = context.location(); + MDRawContextAMD64* context_ptr = context.get(); + +#define AddReg(a) context_ptr->a = static_cast<__typeof__(context_ptr->a)>( \ + REGISTER_FROM_THREADSTATE(machine_state, a)) + + context_ptr->context_flags = MD_CONTEXT_AMD64; + AddReg(rax); + AddReg(rbx); + AddReg(rcx); + AddReg(rdx); + AddReg(rdi); + AddReg(rsi); + AddReg(rbp); + AddReg(rsp); + AddReg(r8); + AddReg(r9); + AddReg(r10); + AddReg(r11); + AddReg(r12); + AddReg(r13); + AddReg(r14); + AddReg(r15); + AddReg(rip); + // according to AMD's software developer guide, bits above 18 are + // not used in the flags register. Since the minidump format + // specifies 32 bits for the flags register, we can truncate safely + // with no loss. + context_ptr->eflags = static_cast(REGISTER_FROM_THREADSTATE(machine_state, rflags)); + AddReg(cs); + AddReg(fs); + AddReg(gs); +#undef AddReg + + return true; +} +#endif + +bool MinidumpGenerator::GetThreadState(thread_act_t target_thread, + thread_state_t state, + mach_msg_type_number_t* count) { + if (task_context_ && target_thread == mach_thread_self()) { + switch (cpu_type_) { +#ifdef HAS_ARM_SUPPORT + case CPU_TYPE_ARM: + size_t final_size = + std::min(static_cast(*count), sizeof(arm_thread_state_t)); + memcpy(state, &task_context_->breakpad_uc_mcontext->__ss, final_size); + *count = static_cast(final_size); + return true; +#endif +#ifdef HAS_ARM64_SUPPORT + case CPU_TYPE_ARM64: { + size_t final_size = + std::min(static_cast(*count), sizeof(arm_thread_state64_t)); + memcpy(state, &task_context_->breakpad_uc_mcontext->__ss, final_size); + *count = static_cast(final_size); + return true; + } +#endif +#ifdef HAS_X86_SUPPORT + case CPU_TYPE_I386: + case CPU_TYPE_X86_64: { + size_t state_size = cpu_type_ == CPU_TYPE_I386 ? + sizeof(i386_thread_state_t) : sizeof(x86_thread_state64_t); + size_t final_size = + std::min(static_cast(*count), state_size); + memcpy(state, &task_context_->breakpad_uc_mcontext->__ss, final_size); + *count = static_cast(final_size); + return true; + } +#endif + } + } + + thread_state_flavor_t flavor; + switch (cpu_type_) { +#ifdef HAS_ARM_SUPPORT + case CPU_TYPE_ARM: + flavor = ARM_THREAD_STATE; + break; +#endif +#ifdef HAS_ARM64_SUPPORT + case CPU_TYPE_ARM64: + flavor = ARM_THREAD_STATE64; + break; +#endif +#ifdef HAS_PPC_SUPPORT + case CPU_TYPE_POWERPC: + flavor = PPC_THREAD_STATE; + break; + case CPU_TYPE_POWERPC64: + flavor = PPC_THREAD_STATE64; + break; +#endif +#ifdef HAS_X86_SUPPORT + case CPU_TYPE_I386: + flavor = i386_THREAD_STATE; + break; + case CPU_TYPE_X86_64: + flavor = x86_THREAD_STATE64; + break; +#endif + default: + return false; + } + return thread_get_state(target_thread, flavor, + state, count) == KERN_SUCCESS; +} + +bool MinidumpGenerator::WriteThreadStream(mach_port_t thread_id, + MDRawThread* thread) { + breakpad_thread_state_data_t state; + mach_msg_type_number_t state_count + = static_cast(sizeof(state)); + + if (GetThreadState(thread_id, state, &state_count)) { + if (!WriteStack(state, &thread->stack)) + return false; + + memory_blocks_.push_back(thread->stack); + + if (!WriteContext(state, &thread->thread_context)) + return false; + + thread->thread_id = thread_id; + } else { + return false; + } + + return true; +} + +bool MinidumpGenerator::WriteThreadListStream( + MDRawDirectory* thread_list_stream) { + TypedMDRVA list(&writer_); + thread_act_port_array_t threads_for_task; + mach_msg_type_number_t thread_count; + int non_generator_thread_count; + + if (task_threads(crashing_task_, &threads_for_task, &thread_count)) + return false; + + // Don't include the generator thread + if (handler_thread_ != MACH_PORT_NULL) + non_generator_thread_count = thread_count - 1; + else + non_generator_thread_count = thread_count; + if (!list.AllocateObjectAndArray(non_generator_thread_count, + sizeof(MDRawThread))) + return false; + + thread_list_stream->stream_type = MD_THREAD_LIST_STREAM; + thread_list_stream->location = list.location(); + + list.get()->number_of_threads = non_generator_thread_count; + + MDRawThread thread; + int thread_idx = 0; + + for (unsigned int i = 0; i < thread_count; ++i) { + memset(&thread, 0, sizeof(MDRawThread)); + + if (threads_for_task[i] != handler_thread_) { + if (!WriteThreadStream(threads_for_task[i], &thread)) + return false; + + list.CopyIndexAfterObject(thread_idx++, &thread, sizeof(MDRawThread)); + } + } + + return true; +} + +bool MinidumpGenerator::WriteMemoryListStream( + MDRawDirectory* memory_list_stream) { + TypedMDRVA list(&writer_); + + // If the dump has an exception, include some memory around the + // instruction pointer. + const size_t kIPMemorySize = 256; // bytes + bool have_ip_memory = false; + MDMemoryDescriptor ip_memory_d; + if (exception_thread_ && exception_type_) { + breakpad_thread_state_data_t state; + mach_msg_type_number_t stateCount + = static_cast(sizeof(state)); + + if (GetThreadState(exception_thread_, state, &stateCount)) { + uint64_t ip = CurrentPCForStack(state); + // Bound it to the upper and lower bounds of the region + // it's contained within. If it's not in a known memory region, + // don't bother trying to write it. + mach_vm_address_t addr = static_cast(ip); + mach_vm_size_t size; + natural_t nesting_level = 0; + vm_region_submap_info_64 info; + mach_msg_type_number_t info_count = VM_REGION_SUBMAP_INFO_COUNT_64; + vm_region_recurse_info_t recurse_info; + recurse_info = reinterpret_cast(&info); + + kern_return_t ret = + mach_vm_region_recurse(crashing_task_, + &addr, + &size, + &nesting_level, + recurse_info, + &info_count); + if (ret == KERN_SUCCESS && ip >= addr && ip < (addr + size)) { + // Try to get 128 bytes before and after the IP, but + // settle for whatever's available. + ip_memory_d.start_of_memory_range = + std::max(uintptr_t(addr), + uintptr_t(ip - (kIPMemorySize / 2))); + uintptr_t end_of_range = + std::min(uintptr_t(ip + (kIPMemorySize / 2)), + uintptr_t(addr + size)); + uintptr_t range_diff = end_of_range - + static_cast(ip_memory_d.start_of_memory_range); + ip_memory_d.memory.data_size = static_cast(range_diff); + have_ip_memory = true; + // This needs to get appended to the list even though + // the memory bytes aren't filled in yet so the entire + // list can be written first. The memory bytes will get filled + // in after the memory list is written. + memory_blocks_.push_back(ip_memory_d); + } + } + } + + // Now fill in the memory list and write it. + size_t memory_count = memory_blocks_.size(); + if (!list.AllocateObjectAndArray(memory_count, + sizeof(MDMemoryDescriptor))) + return false; + + memory_list_stream->stream_type = MD_MEMORY_LIST_STREAM; + memory_list_stream->location = list.location(); + + list.get()->number_of_memory_ranges = static_cast(memory_count); + + unsigned int i; + for (i = 0; i < memory_count; ++i) { + list.CopyIndexAfterObject(i, &memory_blocks_[i], + sizeof(MDMemoryDescriptor)); + } + + if (have_ip_memory) { + // Now read the memory around the instruction pointer. + UntypedMDRVA ip_memory(&writer_); + if (!ip_memory.Allocate(ip_memory_d.memory.data_size)) + return false; + + if (dynamic_images_) { + // Out-of-process. + vector memory; + if (ReadTaskMemory(crashing_task_, + ip_memory_d.start_of_memory_range, + ip_memory_d.memory.data_size, + memory) != KERN_SUCCESS) { + return false; + } + + ip_memory.Copy(&memory[0], ip_memory_d.memory.data_size); + } else { + // In-process, just copy from local memory. + ip_memory.Copy( + reinterpret_cast(ip_memory_d.start_of_memory_range), + ip_memory_d.memory.data_size); + } + + ip_memory_d.memory = ip_memory.location(); + // Write this again now that the data location is filled in. + list.CopyIndexAfterObject(i - 1, &ip_memory_d, + sizeof(MDMemoryDescriptor)); + } + + return true; +} + +bool +MinidumpGenerator::WriteExceptionStream(MDRawDirectory* exception_stream) { + TypedMDRVA exception(&writer_); + + if (!exception.Allocate()) + return false; + + exception_stream->stream_type = MD_EXCEPTION_STREAM; + exception_stream->location = exception.location(); + MDRawExceptionStream* exception_ptr = exception.get(); + exception_ptr->thread_id = exception_thread_; + + // This naming is confusing, but it is the proper translation from + // mach naming to minidump naming. + exception_ptr->exception_record.exception_code = exception_type_; + exception_ptr->exception_record.exception_flags = exception_code_; + + breakpad_thread_state_data_t state; + mach_msg_type_number_t state_count + = static_cast(sizeof(state)); + + if (!GetThreadState(exception_thread_, state, &state_count)) + return false; + + if (!WriteContext(state, &exception_ptr->thread_context)) + return false; + + if (exception_type_ == EXC_BAD_ACCESS) + exception_ptr->exception_record.exception_address = exception_subcode_; + else + exception_ptr->exception_record.exception_address = CurrentPCForStack(state); + + return true; +} + +bool MinidumpGenerator::WriteSystemInfoStream( + MDRawDirectory* system_info_stream) { + TypedMDRVA info(&writer_); + + if (!info.Allocate()) + return false; + + system_info_stream->stream_type = MD_SYSTEM_INFO_STREAM; + system_info_stream->location = info.location(); + + // CPU Information + uint32_t number_of_processors; + size_t len = sizeof(number_of_processors); + sysctlbyname("hw.ncpu", &number_of_processors, &len, NULL, 0); + MDRawSystemInfo* info_ptr = info.get(); + + switch (cpu_type_) { +#ifdef HAS_ARM_SUPPORT + case CPU_TYPE_ARM: + info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_ARM; + break; +#endif +#ifdef HAS_ARM64_SUPPORT + case CPU_TYPE_ARM64: + info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_ARM64_OLD; + break; +#endif +#ifdef HAS_PPC_SUPPORT + case CPU_TYPE_POWERPC: + case CPU_TYPE_POWERPC64: + info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_PPC; + break; +#endif +#ifdef HAS_X86_SUPPORT + case CPU_TYPE_I386: + case CPU_TYPE_X86_64: + if (cpu_type_ == CPU_TYPE_I386) + info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_X86; + else + info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_AMD64; +#ifdef __i386__ + // ebx is used for PIC code, so we need + // to preserve it. +#define cpuid(op,eax,ebx,ecx,edx) \ + asm ("pushl %%ebx \n\t" \ + "cpuid \n\t" \ + "movl %%ebx,%1 \n\t" \ + "popl %%ebx" \ + : "=a" (eax), \ + "=g" (ebx), \ + "=c" (ecx), \ + "=d" (edx) \ + : "0" (op)) +#elif defined(__x86_64__) + +#define cpuid(op,eax,ebx,ecx,edx) \ + asm ("cpuid \n\t" \ + : "=a" (eax), \ + "=b" (ebx), \ + "=c" (ecx), \ + "=d" (edx) \ + : "0" (op)) +#endif + +#if defined(__i386__) || defined(__x86_64__) + int unused, unused2; + // get vendor id + cpuid(0, unused, info_ptr->cpu.x86_cpu_info.vendor_id[0], + info_ptr->cpu.x86_cpu_info.vendor_id[2], + info_ptr->cpu.x86_cpu_info.vendor_id[1]); + // get version and feature info + cpuid(1, info_ptr->cpu.x86_cpu_info.version_information, unused, unused2, + info_ptr->cpu.x86_cpu_info.feature_information); + + // family + info_ptr->processor_level = + (info_ptr->cpu.x86_cpu_info.version_information & 0xF00) >> 8; + // 0xMMSS (Model, Stepping) + info_ptr->processor_revision = static_cast( + (info_ptr->cpu.x86_cpu_info.version_information & 0xF) | + ((info_ptr->cpu.x86_cpu_info.version_information & 0xF0) << 4)); + + // decode extended model info + if (info_ptr->processor_level == 0xF || + info_ptr->processor_level == 0x6) { + info_ptr->processor_revision |= + ((info_ptr->cpu.x86_cpu_info.version_information & 0xF0000) >> 4); + } + + // decode extended family info + if (info_ptr->processor_level == 0xF) { + info_ptr->processor_level += + ((info_ptr->cpu.x86_cpu_info.version_information & 0xFF00000) >> 20); + } + +#endif // __i386__ || __x86_64_ + break; +#endif // HAS_X86_SUPPORT + default: + info_ptr->processor_architecture = MD_CPU_ARCHITECTURE_UNKNOWN; + break; + } + + info_ptr->number_of_processors = static_cast(number_of_processors); +#if TARGET_OS_IPHONE + info_ptr->platform_id = MD_OS_IOS; +#else + info_ptr->platform_id = MD_OS_MAC_OS_X; +#endif // TARGET_OS_IPHONE + + MDLocationDescriptor build_string_loc; + + if (!writer_.WriteString(build_string_, 0, + &build_string_loc)) + return false; + + info_ptr->csd_version_rva = build_string_loc.rva; + info_ptr->major_version = os_major_version_; + info_ptr->minor_version = os_minor_version_; + info_ptr->build_number = os_build_number_; + + return true; +} + +bool MinidumpGenerator::WriteModuleStream(unsigned int index, + MDRawModule* module) { + if (dynamic_images_) { + // we're in a different process than the crashed process + DynamicImage* image = dynamic_images_->GetImage(index); + + if (!image) + return false; + + memset(module, 0, sizeof(MDRawModule)); + + MDLocationDescriptor string_location; + + string name = image->GetFilePath(); + if (!writer_.WriteString(name.c_str(), 0, &string_location)) + return false; + + module->base_of_image = image->GetVMAddr() + image->GetVMAddrSlide(); + module->size_of_image = static_cast(image->GetVMSize()); + module->module_name_rva = string_location.rva; + + // We'll skip the executable module, because they don't have + // LC_ID_DYLIB load commands, and the crash processing server gets + // version information from the Plist file, anyway. + if (index != static_cast(FindExecutableModule())) { + module->version_info.signature = MD_VSFIXEDFILEINFO_SIGNATURE; + module->version_info.struct_version |= MD_VSFIXEDFILEINFO_VERSION; + // Convert MAC dylib version format, which is a 32 bit number, to the + // format used by minidump. The mac format is <16 bits>.<8 bits>.<8 bits> + // so it fits nicely into the windows version with some massaging + // The mapping is: + // 1) upper 16 bits of MAC version go to lower 16 bits of product HI + // 2) Next most significant 8 bits go to upper 16 bits of product LO + // 3) Least significant 8 bits go to lower 16 bits of product LO + uint32_t modVersion = image->GetVersion(); + module->version_info.file_version_hi = 0; + module->version_info.file_version_hi = modVersion >> 16; + module->version_info.file_version_lo |= (modVersion & 0xff00) << 8; + module->version_info.file_version_lo |= (modVersion & 0xff); + } + + if (!WriteCVRecord(module, image->GetCPUType(), name.c_str(), false)) { + return false; + } + } else { + // Getting module info in the crashed process + const breakpad_mach_header* header; + header = (breakpad_mach_header*)_dyld_get_image_header(index); + if (!header) + return false; + +#ifdef __LP64__ + assert(header->magic == MH_MAGIC_64); + + if(header->magic != MH_MAGIC_64) + return false; +#else + assert(header->magic == MH_MAGIC); + + if(header->magic != MH_MAGIC) + return false; +#endif + + int cpu_type = header->cputype; + unsigned long slide = _dyld_get_image_vmaddr_slide(index); + const char* name = _dyld_get_image_name(index); + const struct load_command* cmd = + reinterpret_cast(header + 1); + + memset(module, 0, sizeof(MDRawModule)); + + for (unsigned int i = 0; cmd && (i < header->ncmds); i++) { + if (cmd->cmd == LC_SEGMENT_ARCH) { + + const breakpad_mach_segment_command* seg = + reinterpret_cast(cmd); + + if (!strcmp(seg->segname, "__TEXT")) { + MDLocationDescriptor string_location; + + if (!writer_.WriteString(name, 0, &string_location)) + return false; + + module->base_of_image = seg->vmaddr + slide; + module->size_of_image = static_cast(seg->vmsize); + module->module_name_rva = string_location.rva; + + bool in_memory = false; +#if TARGET_OS_IPHONE + in_memory = true; +#endif + if (!WriteCVRecord(module, cpu_type, name, in_memory)) + return false; + + return true; + } + } + + cmd = reinterpret_cast((char*)cmd + cmd->cmdsize); + } + } + + return true; +} + +int MinidumpGenerator::FindExecutableModule() { + if (dynamic_images_) { + int index = dynamic_images_->GetExecutableImageIndex(); + + if (index >= 0) { + return index; + } + } else { + int image_count = _dyld_image_count(); + const struct mach_header* header; + + for (int index = 0; index < image_count; ++index) { + header = _dyld_get_image_header(index); + + if (header->filetype == MH_EXECUTE) + return index; + } + } + + // failed - just use the first image + return 0; +} + +bool MinidumpGenerator::WriteCVRecord(MDRawModule* module, int cpu_type, + const char* module_path, bool in_memory) { + TypedMDRVA cv(&writer_); + + // Only return the last path component of the full module path + const char* module_name = strrchr(module_path, '/'); + + // Increment past the slash + if (module_name) + ++module_name; + else + module_name = ""; + + size_t module_name_length = strlen(module_name); + + if (!cv.AllocateObjectAndArray(module_name_length + 1, sizeof(uint8_t))) + return false; + + if (!cv.CopyIndexAfterObject(0, module_name, module_name_length)) + return false; + + module->cv_record = cv.location(); + MDCVInfoPDB70* cv_ptr = cv.get(); + cv_ptr->cv_signature = MD_CVINFOPDB70_SIGNATURE; + cv_ptr->age = 0; + + // Get the module identifier + unsigned char identifier[16]; + bool result = false; + if (in_memory) { + MacFileUtilities::MachoID macho( + reinterpret_cast(module->base_of_image), + static_cast(module->size_of_image)); + result = macho.UUIDCommand(cpu_type, CPU_SUBTYPE_MULTIPLE, identifier); + if (!result) + result = macho.MD5(cpu_type, CPU_SUBTYPE_MULTIPLE, identifier); + } + + if (!result) { + FileID file_id(module_path); + result = file_id.MachoIdentifier(cpu_type, CPU_SUBTYPE_MULTIPLE, + identifier); + } + + if (result) { + cv_ptr->signature.data1 = + static_cast(identifier[0]) << 24 | + static_cast(identifier[1]) << 16 | + static_cast(identifier[2]) << 8 | + static_cast(identifier[3]); + cv_ptr->signature.data2 = + static_cast(identifier[4] << 8) | identifier[5]; + cv_ptr->signature.data3 = + static_cast(identifier[6] << 8) | identifier[7]; + cv_ptr->signature.data4[0] = identifier[8]; + cv_ptr->signature.data4[1] = identifier[9]; + cv_ptr->signature.data4[2] = identifier[10]; + cv_ptr->signature.data4[3] = identifier[11]; + cv_ptr->signature.data4[4] = identifier[12]; + cv_ptr->signature.data4[5] = identifier[13]; + cv_ptr->signature.data4[6] = identifier[14]; + cv_ptr->signature.data4[7] = identifier[15]; + } + + return true; +} + +bool MinidumpGenerator::WriteModuleListStream( + MDRawDirectory* module_list_stream) { + TypedMDRVA list(&writer_); + + uint32_t image_count = dynamic_images_ ? + dynamic_images_->GetImageCount() : + _dyld_image_count(); + + if (!list.AllocateObjectAndArray(image_count, MD_MODULE_SIZE)) + return false; + + module_list_stream->stream_type = MD_MODULE_LIST_STREAM; + module_list_stream->location = list.location(); + list.get()->number_of_modules = static_cast(image_count); + + // Write out the executable module as the first one + MDRawModule module; + uint32_t executableIndex = FindExecutableModule(); + + if (!WriteModuleStream(static_cast(executableIndex), &module)) { + return false; + } + + list.CopyIndexAfterObject(0, &module, MD_MODULE_SIZE); + int destinationIndex = 1; // Write all other modules after this one + + for (uint32_t i = 0; i < image_count; ++i) { + if (i != executableIndex) { + if (!WriteModuleStream(static_cast(i), &module)) { + return false; + } + + list.CopyIndexAfterObject(destinationIndex++, &module, MD_MODULE_SIZE); + } + } + + return true; +} + +bool MinidumpGenerator::WriteMiscInfoStream(MDRawDirectory* misc_info_stream) { + TypedMDRVA info(&writer_); + + if (!info.Allocate()) + return false; + + misc_info_stream->stream_type = MD_MISC_INFO_STREAM; + misc_info_stream->location = info.location(); + + MDRawMiscInfo* info_ptr = info.get(); + info_ptr->size_of_info = static_cast(sizeof(MDRawMiscInfo)); + info_ptr->flags1 = MD_MISCINFO_FLAGS1_PROCESS_ID | + MD_MISCINFO_FLAGS1_PROCESS_TIMES | + MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO; + + // Process ID + info_ptr->process_id = getpid(); + + // Times + struct rusage usage; + if (getrusage(RUSAGE_SELF, &usage) != -1) { + // Omit the fractional time since the MDRawMiscInfo only wants seconds + info_ptr->process_user_time = + static_cast(usage.ru_utime.tv_sec); + info_ptr->process_kernel_time = + static_cast(usage.ru_stime.tv_sec); + } + int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, + static_cast(info_ptr->process_id) }; + uint mibsize = static_cast(sizeof(mib) / sizeof(mib[0])); + struct kinfo_proc proc; + size_t size = sizeof(proc); + if (sysctl(mib, mibsize, &proc, &size, NULL, 0) == 0) { + info_ptr->process_create_time = + static_cast(proc.kp_proc.p_starttime.tv_sec); + } + + // Speed + uint64_t speed; + const uint64_t kOneMillion = 1000 * 1000; + size = sizeof(speed); + sysctlbyname("hw.cpufrequency_max", &speed, &size, NULL, 0); + info_ptr->processor_max_mhz = static_cast(speed / kOneMillion); + info_ptr->processor_mhz_limit = static_cast(speed / kOneMillion); + size = sizeof(speed); + sysctlbyname("hw.cpufrequency", &speed, &size, NULL, 0); + info_ptr->processor_current_mhz = static_cast(speed / kOneMillion); + + return true; +} + +bool MinidumpGenerator::WriteBreakpadInfoStream( + MDRawDirectory* breakpad_info_stream) { + TypedMDRVA info(&writer_); + + if (!info.Allocate()) + return false; + + breakpad_info_stream->stream_type = MD_BREAKPAD_INFO_STREAM; + breakpad_info_stream->location = info.location(); + MDRawBreakpadInfo* info_ptr = info.get(); + + if (exception_thread_ && exception_type_) { + info_ptr->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID | + MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID; + info_ptr->dump_thread_id = handler_thread_; + info_ptr->requesting_thread_id = exception_thread_; + } else { + info_ptr->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID; + info_ptr->dump_thread_id = handler_thread_; + info_ptr->requesting_thread_id = 0; + } + + return true; +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/mac/handler/minidump_generator.h b/shared/sentry/external/breakpad/src/client/mac/handler/minidump_generator.h new file mode 100644 index 000000000..e3a271b0b --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/handler/minidump_generator.h @@ -0,0 +1,253 @@ +// 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. + +// minidump_generator.h: Create a minidump of the current MacOS process. + +#ifndef CLIENT_MAC_GENERATOR_MINIDUMP_GENERATOR_H__ +#define CLIENT_MAC_GENERATOR_MINIDUMP_GENERATOR_H__ + +#include +#include + +#include + +#include "client/mac/handler/ucontext_compat.h" +#include "client/minidump_file_writer.h" +#include "common/memory_allocator.h" +#include "common/mac/macho_utilities.h" +#include "google_breakpad/common/minidump_format.h" + +#include "dynamic_images.h" +#include "mach_vm_compat.h" + +#if !TARGET_OS_IPHONE && (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7) + #define HAS_PPC_SUPPORT +#endif +#if defined(__arm__) +#define HAS_ARM_SUPPORT +#elif defined(__aarch64__) +#define HAS_ARM64_SUPPORT +#elif defined(__i386__) || defined(__x86_64__) + #define HAS_X86_SUPPORT +#endif + +namespace google_breakpad { + +using std::string; + +// Use the REGISTER_FROM_THREADSTATE to access a register name from the +// breakpad_thread_state_t structure. +#if __DARWIN_OPAQUE_ARM_THREAD_STATE64 +#define ARRAY_REGISTER_FROM_THREADSTATE(a, b, i) ((a)->__##b[i]) +#define GET_REGISTER_FROM_THREADSTATE_fp(a) \ + (reinterpret_cast((a)->__opaque_fp)) +#define GET_REGISTER_FROM_THREADSTATE_lr(a) \ + (reinterpret_cast((a)->__opaque_lr)) +#define GET_REGISTER_FROM_THREADSTATE_sp(a) \ + (reinterpret_cast((a)->__opaque_sp)) +#define GET_REGISTER_FROM_THREADSTATE_pc(a) \ + (reinterpret_cast((a)->__opaque_pc)) +#define GET_REGISTER_FROM_THREADSTATE_cpsr(a) ((a)->__cpsr) +#define GET_REGISTER_FROM_THREADSTATE_flags(a) ((a)->__opaque_flags) +#define REGISTER_FROM_THREADSTATE(a, b) (GET_REGISTER_FROM_THREADSTATE_##b(a)) +#elif __DARWIN_UNIX03 || TARGET_CPU_X86_64 || TARGET_CPU_PPC64 || TARGET_CPU_ARM +// In The 10.5 SDK Headers Apple prepended __ to the variable names in the +// i386_thread_state_t structure. There's no good way to tell what version of +// the SDK we're compiling against so we just toggle on the same preprocessor +// symbol Apple's headers use. +#define REGISTER_FROM_THREADSTATE(a, b) ((a)->__ ## b) +#define ARRAY_REGISTER_FROM_THREADSTATE(a, b, i) \ + REGISTER_FROM_THREADSTATE(a, b[i]) +#else +#define REGISTER_FROM_THREADSTATE(a, b) (a->b) +#define ARRAY_REGISTER_FROM_THREADSTATE(a, b, i) \ + REGISTER_FROM_THREADSTATE(a, b[i]) +#endif + +// Creates a minidump file of the current process. If there is exception data, +// use SetExceptionInformation() to add this to the minidump. The minidump +// file is generated by the Write() function. +// Usage: +// MinidumpGenerator minidump(); +// minidump.Write("/tmp/minidump"); +// +class MinidumpGenerator { + public: + MinidumpGenerator(); + MinidumpGenerator(mach_port_t crashing_task, mach_port_t handler_thread); + + virtual ~MinidumpGenerator(); + + // Return

/.dmp + // Sets |unique_name| (if requested) to the unique name for the minidump + static string UniqueNameInDirectory(const string& dir, string* unique_name); + + // Write out the minidump into |path| + // All of the components of |path| must exist and be writable + // Return true if successful, false otherwise + bool Write(const char* path); + + // Specify some exception information, if applicable + void SetExceptionInformation(int type, int code, int subcode, + mach_port_t thread_name) { + exception_type_ = type; + exception_code_ = code; + exception_subcode_ = subcode; + exception_thread_ = thread_name; + } + + // Specify the task context. If |task_context| is not NULL, it will be used + // to retrieve the context of the current thread, instead of using + // |thread_get_state|. + void SetTaskContext(breakpad_ucontext_t* task_context); + + // Gather system information. This should be call at least once before using + // the MinidumpGenerator class. + static void GatherSystemInformation(); + + protected: + // Overridable Stream writers + virtual bool WriteExceptionStream(MDRawDirectory* exception_stream); + + // Overridable Helper + virtual bool WriteThreadStream(mach_port_t thread_id, MDRawThread* thread); + + private: + typedef bool (MinidumpGenerator::*WriteStreamFN)(MDRawDirectory*); + + // Stream writers + bool WriteThreadListStream(MDRawDirectory* thread_list_stream); + bool WriteMemoryListStream(MDRawDirectory* memory_list_stream); + bool WriteSystemInfoStream(MDRawDirectory* system_info_stream); + bool WriteModuleListStream(MDRawDirectory* module_list_stream); + bool WriteMiscInfoStream(MDRawDirectory* misc_info_stream); + bool WriteBreakpadInfoStream(MDRawDirectory* breakpad_info_stream); + + // Helpers + uint64_t CurrentPCForStack(breakpad_thread_state_data_t state); + bool GetThreadState(thread_act_t target_thread, thread_state_t state, + mach_msg_type_number_t* count); + bool WriteStackFromStartAddress(mach_vm_address_t start_addr, + MDMemoryDescriptor* stack_location); + bool WriteStack(breakpad_thread_state_data_t state, + MDMemoryDescriptor* stack_location); + bool WriteContext(breakpad_thread_state_data_t state, + MDLocationDescriptor* register_location); + bool WriteCVRecord(MDRawModule* module, int cpu_type, + const char* module_path, bool in_memory); + bool WriteModuleStream(unsigned int index, MDRawModule* module); + size_t CalculateStackSize(mach_vm_address_t start_addr); + int FindExecutableModule(); + + // Per-CPU implementations of these methods +#ifdef HAS_ARM_SUPPORT + bool WriteStackARM(breakpad_thread_state_data_t state, + MDMemoryDescriptor* stack_location); + bool WriteContextARM(breakpad_thread_state_data_t state, + MDLocationDescriptor* register_location); + uint64_t CurrentPCForStackARM(breakpad_thread_state_data_t state); +#endif +#ifdef HAS_ARM64_SUPPORT + bool WriteStackARM64(breakpad_thread_state_data_t state, + MDMemoryDescriptor* stack_location); + bool WriteContextARM64(breakpad_thread_state_data_t state, + MDLocationDescriptor* register_location); + uint64_t CurrentPCForStackARM64(breakpad_thread_state_data_t state); +#endif +#ifdef HAS_PPC_SUPPORT + bool WriteStackPPC(breakpad_thread_state_data_t state, + MDMemoryDescriptor* stack_location); + bool WriteContextPPC(breakpad_thread_state_data_t state, + MDLocationDescriptor* register_location); + uint64_t CurrentPCForStackPPC(breakpad_thread_state_data_t state); + bool WriteStackPPC64(breakpad_thread_state_data_t state, + MDMemoryDescriptor* stack_location); + bool WriteContextPPC64(breakpad_thread_state_data_t state, + MDLocationDescriptor* register_location); + uint64_t CurrentPCForStackPPC64(breakpad_thread_state_data_t state); +#endif +#ifdef HAS_X86_SUPPORT + bool WriteStackX86(breakpad_thread_state_data_t state, + MDMemoryDescriptor* stack_location); + bool WriteContextX86(breakpad_thread_state_data_t state, + MDLocationDescriptor* register_location); + uint64_t CurrentPCForStackX86(breakpad_thread_state_data_t state); + bool WriteStackX86_64(breakpad_thread_state_data_t state, + MDMemoryDescriptor* stack_location); + bool WriteContextX86_64(breakpad_thread_state_data_t state, + MDLocationDescriptor* register_location); + uint64_t CurrentPCForStackX86_64(breakpad_thread_state_data_t state); +#endif + + // disallow copy ctor and operator= + explicit MinidumpGenerator(const MinidumpGenerator&); + void operator=(const MinidumpGenerator&); + + protected: + // Use this writer to put the data to disk + MinidumpFileWriter writer_; + + private: + // Exception information + int exception_type_; + int exception_code_; + int exception_subcode_; + mach_port_t exception_thread_; + mach_port_t crashing_task_; + mach_port_t handler_thread_; + + // CPU type of the task being dumped. + cpu_type_t cpu_type_; + + // System information + static char build_string_[16]; + static int os_major_version_; + static int os_minor_version_; + static int os_build_number_; + + // Context of the task to dump. + breakpad_ucontext_t* task_context_; + + // Information about dynamically loaded code + DynamicImages* dynamic_images_; + + // PageAllocator makes it possible to allocate memory + // directly from the system, even while handling an exception. + mutable PageAllocator allocator_; + + protected: + // Blocks of memory written to the dump. These are all currently + // written while writing the thread list stream, but saved here + // so a memory list stream can be written afterwards. + wasteful_vector memory_blocks_; +}; + +} // namespace google_breakpad + +#endif // CLIENT_MAC_GENERATOR_MINIDUMP_GENERATOR_H__ diff --git a/shared/sentry/external/breakpad/src/client/mac/handler/minidump_test.xcodeproj/project.pbxproj b/shared/sentry/external/breakpad/src/client/mac/handler/minidump_test.xcodeproj/project.pbxproj new file mode 100644 index 000000000..1924ac4d5 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/handler/minidump_test.xcodeproj/project.pbxproj @@ -0,0 +1,843 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 8BFC813F11FF9A58002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; }; + 8BFC814411FF9A9C002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; }; + 8BFC814511FF9A9D002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; }; + 8BFC814811FF9B13002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; }; + 8BFC814911FF9B13002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; }; + 8BFC814A11FF9B13002CB4DC /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */; }; + 8BFC814B11FF9B3F002CB4DC /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */; }; + 8BFC814C11FF9B3F002CB4DC /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */; }; + 8BFC81A211FF9C2E002CB4DC /* CPlusTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC819211FF9C23002CB4DC /* CPlusTest.framework */; }; + 8BFC81A311FF9C2F002CB4DC /* CPlusTest.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BFC819211FF9C23002CB4DC /* CPlusTest.framework */; }; + 8BFC81AD11FF9C8A002CB4DC /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; }; + 8BFC81AE11FF9C8C002CB4DC /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; }; + 8BFC81AF11FF9C8C002CB4DC /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; }; + 8BFC81B011FF9C8D002CB4DC /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; }; + 9B35FF5A0B267D5F008DE8C7 /* convert_UTF.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF560B267D5F008DE8C7 /* convert_UTF.cc */; }; + 9B35FF5B0B267D5F008DE8C7 /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF580B267D5F008DE8C7 /* string_conversion.cc */; }; + 9B37CEEC0AF98ECD00FA4BD4 /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B37CEEB0AF98ECD00FA4BD4 /* CoreFoundation.framework */; }; + 9B7CA7700B12873A00CD3A1D /* minidump_file_writer-inl.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BE3C01E0B0CE329009892DF /* minidump_file_writer-inl.h */; }; + 9B7CA8540B12989000CD3A1D /* minidump_file_writer_unittest.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B7CA8530B12989000CD3A1D /* minidump_file_writer_unittest.cc */; }; + 9B7CA8550B1298A100CD3A1D /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C230B01344C0055103E /* minidump_file_writer.cc */; }; + 9BC1D2940B336F2300F2A2B4 /* convert_UTF.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF560B267D5F008DE8C7 /* convert_UTF.cc */; }; + 9BC1D2950B336F2500F2A2B4 /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF580B267D5F008DE8C7 /* string_conversion.cc */; }; + 9BD82AC10B0029DF0055103E /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9B37CEEB0AF98ECD00FA4BD4 /* CoreFoundation.framework */; }; + 9BD82BFF0B01333D0055103E /* exception_handler_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82BFD0B01333D0055103E /* exception_handler_test.cc */; }; + 9BD82C020B01333D0055103E /* minidump_generator_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82BFE0B01333D0055103E /* minidump_generator_test.cc */; }; + 9BD82C0D0B0133520055103E /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C090B0133520055103E /* exception_handler.cc */; }; + 9BD82C0E0B0133520055103E /* minidump_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C0B0B0133520055103E /* minidump_generator.cc */; }; + 9BD82C0F0B0133520055103E /* exception_handler.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C090B0133520055103E /* exception_handler.cc */; }; + 9BD82C100B0133520055103E /* exception_handler.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BD82C0A0B0133520055103E /* exception_handler.h */; }; + 9BD82C110B0133520055103E /* minidump_generator.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C0B0B0133520055103E /* minidump_generator.cc */; }; + 9BD82C120B0133520055103E /* minidump_generator.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BD82C0C0B0133520055103E /* minidump_generator.h */; }; + 9BD82C250B01344C0055103E /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C230B01344C0055103E /* minidump_file_writer.cc */; }; + 9BD82C260B01344C0055103E /* minidump_file_writer.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C230B01344C0055103E /* minidump_file_writer.cc */; }; + 9BD82C270B01344C0055103E /* minidump_file_writer.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BD82C240B01344C0055103E /* minidump_file_writer.h */; }; + 9BD82C2D0B01345E0055103E /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C2B0B01345E0055103E /* string_utilities.cc */; }; + 9BD82C2E0B01345E0055103E /* string_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9BD82C2B0B01345E0055103E /* string_utilities.cc */; }; + 9BD82C2F0B01345E0055103E /* string_utilities.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9BD82C2C0B01345E0055103E /* string_utilities.h */; }; + D2F651000BEF947200920385 /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FA0BEF947200920385 /* file_id.cc */; }; + D2F651010BEF947200920385 /* file_id.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = D2F650FB0BEF947200920385 /* file_id.h */; }; + D2F651020BEF947200920385 /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FC0BEF947200920385 /* macho_id.cc */; }; + D2F651030BEF947200920385 /* macho_id.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = D2F650FD0BEF947200920385 /* macho_id.h */; }; + D2F651040BEF947200920385 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FE0BEF947200920385 /* macho_utilities.cc */; }; + D2F651050BEF947200920385 /* macho_utilities.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = D2F650FF0BEF947200920385 /* macho_utilities.h */; }; + D2F651090BEF949A00920385 /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F651070BEF949A00920385 /* dynamic_images.cc */; }; + D2F6510A0BEF949A00920385 /* dynamic_images.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = D2F651080BEF949A00920385 /* dynamic_images.h */; }; + D2F6510E0BEF94EB00920385 /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F6510C0BEF94EB00920385 /* macho_walker.cc */; }; + D2F6510F0BEF94EB00920385 /* macho_walker.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = D2F6510D0BEF94EB00920385 /* macho_walker.h */; }; + D2F651110BEF951700920385 /* string_conversion.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF580B267D5F008DE8C7 /* string_conversion.cc */; }; + D2F651130BEF951C00920385 /* string_conversion.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9B35FF590B267D5F008DE8C7 /* string_conversion.h */; }; + D2F651150BEF953000920385 /* convert_UTF.cc in Sources */ = {isa = PBXBuildFile; fileRef = 9B35FF560B267D5F008DE8C7 /* convert_UTF.cc */; }; + D2F651160BEF953100920385 /* convert_UTF.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9B35FF570B267D5F008DE8C7 /* convert_UTF.h */; }; + D2F6511B0BEF970E00920385 /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F651070BEF949A00920385 /* dynamic_images.cc */; }; + D2F6511D0BEF973500920385 /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FA0BEF947200920385 /* file_id.cc */; }; + D2F6511E0BEF973600920385 /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FC0BEF947200920385 /* macho_id.cc */; }; + D2F6511F0BEF973900920385 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FE0BEF947200920385 /* macho_utilities.cc */; }; + D2F651210BEF975400920385 /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F6510C0BEF94EB00920385 /* macho_walker.cc */; }; + F93A887D0E8B4C8C0026AF89 /* macho_walker.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F6510C0BEF94EB00920385 /* macho_walker.cc */; }; + F93A887E0E8B4C8C0026AF89 /* macho_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FC0BEF947200920385 /* macho_id.cc */; }; + F93A887F0E8B4C8C0026AF89 /* macho_utilities.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FE0BEF947200920385 /* macho_utilities.cc */; }; + F93A88800E8B4C8C0026AF89 /* file_id.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F650FA0BEF947200920385 /* file_id.cc */; }; + F93A88860E8B4C9A0026AF89 /* dwarftests.mm in Sources */ = {isa = PBXBuildFile; fileRef = F9721F310E8B07E800D7E813 /* dwarftests.mm */; }; + F93A88870E8B4C9A0026AF89 /* dump_syms.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F390E8B0D0D00D7E813 /* dump_syms.cc */; }; + F93A88880E8B4C9A0026AF89 /* bytereader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F760E8B0DC700D7E813 /* bytereader.cc */; }; + F93A88890E8B4C9A0026AF89 /* dwarf2reader.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */; }; + F93A888A0E8B4C9A0026AF89 /* functioninfo.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721F780E8B0DC700D7E813 /* functioninfo.cc */; }; + F93A888B0E8B4C9A0026AF89 /* md5.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9721FA80E8B0E4800D7E813 /* md5.cc */; }; + F9721F6C0E8B0D7000D7E813 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */; }; + F9721FA20E8B0E2300D7E813 /* SenTestingKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */; }; + F982089C0DB3280D0017AECA /* breakpad_nlist_test.cc in Sources */ = {isa = PBXBuildFile; fileRef = F982089B0DB3280D0017AECA /* breakpad_nlist_test.cc */; }; + F98208A30DB32CAE0017AECA /* breakpad_nlist_64.cc in Sources */ = {isa = PBXBuildFile; fileRef = F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */; }; + F9AE5B390DBFDBDB00505983 /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F651070BEF949A00920385 /* dynamic_images.cc */; }; + F9AE5B3A0DBFDBDB00505983 /* DynamicImagesTests.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9C5A4210DB82DD800209C76 /* DynamicImagesTests.cc */; }; + F9B34E870DBC1E1600306484 /* dynamic_images.cc in Sources */ = {isa = PBXBuildFile; fileRef = D2F651070BEF949A00920385 /* dynamic_images.cc */; }; + F9C5A4220DB82DD800209C76 /* DynamicImagesTests.cc in Sources */ = {isa = PBXBuildFile; fileRef = F9C5A4210DB82DD800209C76 /* DynamicImagesTests.cc */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 8DD76F690486A84900D96B5E /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + 9BD82C100B0133520055103E /* exception_handler.h in CopyFiles */, + 9BD82C120B0133520055103E /* minidump_generator.h in CopyFiles */, + 9BD82C270B01344C0055103E /* minidump_file_writer.h in CopyFiles */, + 9BD82C2F0B01345E0055103E /* string_utilities.h in CopyFiles */, + 9B7CA7700B12873A00CD3A1D /* minidump_file_writer-inl.h in CopyFiles */, + D2F651010BEF947200920385 /* file_id.h in CopyFiles */, + D2F651030BEF947200920385 /* macho_id.h in CopyFiles */, + D2F651050BEF947200920385 /* macho_utilities.h in CopyFiles */, + D2F6510A0BEF949A00920385 /* dynamic_images.h in CopyFiles */, + D2F6510F0BEF94EB00920385 /* macho_walker.h in CopyFiles */, + D2F651130BEF951C00920385 /* string_conversion.h in CopyFiles */, + D2F651160BEF953100920385 /* convert_UTF.h in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 8BFC812011FF99D5002CB4DC /* Breakpad.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Breakpad.xcconfig; path = ../../../common/mac/Breakpad.xcconfig; sourceTree = SOURCE_ROOT; }; + 8BFC812111FF99D5002CB4DC /* BreakpadDebug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = BreakpadDebug.xcconfig; path = ../../../common/mac/BreakpadDebug.xcconfig; sourceTree = SOURCE_ROOT; }; + 8BFC812211FF99D5002CB4DC /* BreakpadRelease.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = BreakpadRelease.xcconfig; path = ../../../common/mac/BreakpadRelease.xcconfig; sourceTree = SOURCE_ROOT; }; + 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.dylib; path = usr/lib/libcrypto.dylib; sourceTree = SDKROOT; }; + 8BFC815411FF9B7F002CB4DC /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; }; + 8BFC819211FF9C23002CB4DC /* CPlusTest.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CPlusTest.framework; path = Library/Frameworks/CPlusTest.framework; sourceTree = DEVELOPER_DIR; }; + 8DD76F6C0486A84900D96B5E /* generator_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = generator_test; sourceTree = BUILT_PRODUCTS_DIR; }; + 9B35FF560B267D5F008DE8C7 /* convert_UTF.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = convert_UTF.cc; path = ../../../common/convert_UTF.cc; sourceTree = SOURCE_ROOT; }; + 9B35FF570B267D5F008DE8C7 /* convert_UTF.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = convert_UTF.h; path = ../../../common/convert_UTF.h; sourceTree = SOURCE_ROOT; }; + 9B35FF580B267D5F008DE8C7 /* string_conversion.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = string_conversion.cc; path = ../../../common/string_conversion.cc; sourceTree = SOURCE_ROOT; }; + 9B35FF590B267D5F008DE8C7 /* string_conversion.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = string_conversion.h; path = ../../../common/string_conversion.h; sourceTree = SOURCE_ROOT; }; + 9B37CEEB0AF98ECD00FA4BD4 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; }; + 9B7CA84E0B1297F200CD3A1D /* unit_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = unit_test; sourceTree = BUILT_PRODUCTS_DIR; }; + 9B7CA8530B12989000CD3A1D /* minidump_file_writer_unittest.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_file_writer_unittest.cc; path = ../../minidump_file_writer_unittest.cc; sourceTree = ""; }; + 9BD82A9B0B00267E0055103E /* handler_test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = handler_test; sourceTree = BUILT_PRODUCTS_DIR; }; + 9BD82BFD0B01333D0055103E /* exception_handler_test.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = exception_handler_test.cc; sourceTree = SOURCE_ROOT; }; + 9BD82BFE0B01333D0055103E /* minidump_generator_test.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = minidump_generator_test.cc; sourceTree = SOURCE_ROOT; }; + 9BD82C090B0133520055103E /* exception_handler.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = exception_handler.cc; sourceTree = SOURCE_ROOT; }; + 9BD82C0A0B0133520055103E /* exception_handler.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = exception_handler.h; sourceTree = SOURCE_ROOT; }; + 9BD82C0B0B0133520055103E /* minidump_generator.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = minidump_generator.cc; sourceTree = SOURCE_ROOT; }; + 9BD82C0C0B0133520055103E /* minidump_generator.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = minidump_generator.h; sourceTree = SOURCE_ROOT; }; + 9BD82C230B01344C0055103E /* minidump_file_writer.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = minidump_file_writer.cc; path = ../../minidump_file_writer.cc; sourceTree = SOURCE_ROOT; }; + 9BD82C240B01344C0055103E /* minidump_file_writer.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = minidump_file_writer.h; path = ../../minidump_file_writer.h; sourceTree = SOURCE_ROOT; }; + 9BD82C2B0B01345E0055103E /* string_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = string_utilities.cc; path = ../../../common/mac/string_utilities.cc; sourceTree = SOURCE_ROOT; }; + 9BD82C2C0B01345E0055103E /* string_utilities.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = string_utilities.h; path = ../../../common/mac/string_utilities.h; sourceTree = SOURCE_ROOT; }; + 9BE3C01E0B0CE329009892DF /* minidump_file_writer-inl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "minidump_file_writer-inl.h"; path = "../../minidump_file_writer-inl.h"; sourceTree = SOURCE_ROOT; }; + D2F650FA0BEF947200920385 /* file_id.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = file_id.cc; path = ../../../common/mac/file_id.cc; sourceTree = SOURCE_ROOT; }; + D2F650FB0BEF947200920385 /* file_id.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = file_id.h; path = ../../../common/mac/file_id.h; sourceTree = SOURCE_ROOT; }; + D2F650FC0BEF947200920385 /* macho_id.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = macho_id.cc; path = ../../../common/mac/macho_id.cc; sourceTree = SOURCE_ROOT; }; + D2F650FD0BEF947200920385 /* macho_id.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = macho_id.h; path = ../../../common/mac/macho_id.h; sourceTree = SOURCE_ROOT; }; + D2F650FE0BEF947200920385 /* macho_utilities.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = macho_utilities.cc; path = ../../../common/mac/macho_utilities.cc; sourceTree = SOURCE_ROOT; }; + D2F650FF0BEF947200920385 /* macho_utilities.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = macho_utilities.h; path = ../../../common/mac/macho_utilities.h; sourceTree = SOURCE_ROOT; }; + D2F651070BEF949A00920385 /* dynamic_images.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = dynamic_images.cc; sourceTree = ""; }; + D2F651080BEF949A00920385 /* dynamic_images.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = dynamic_images.h; sourceTree = ""; }; + D2F6510C0BEF94EB00920385 /* macho_walker.cc */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = macho_walker.cc; path = ../../../common/mac/macho_walker.cc; sourceTree = SOURCE_ROOT; }; + D2F6510D0BEF94EB00920385 /* macho_walker.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = macho_walker.h; path = ../../../common/mac/macho_walker.h; sourceTree = SOURCE_ROOT; }; + F917C4F70E03265A00F86017 /* breakpad_exc_server.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = breakpad_exc_server.c; sourceTree = ""; }; + F917C4F80E03265A00F86017 /* breakpad_exc_server.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = breakpad_exc_server.h; sourceTree = ""; }; + F93A88750E8B4C700026AF89 /* octestcases.octest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = octestcases.octest; sourceTree = BUILT_PRODUCTS_DIR; }; + F93A88760E8B4C700026AF89 /* obj-cTestCases-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "obj-cTestCases-Info.plist"; sourceTree = ""; }; + F9721F300E8B07E800D7E813 /* dwarftests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = dwarftests.h; sourceTree = ""; }; + F9721F310E8B07E800D7E813 /* dwarftests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = dwarftests.mm; sourceTree = ""; }; + F9721F380E8B0CFC00D7E813 /* dump_syms.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = dump_syms.h; path = ../../../common/mac/dump_syms.h; sourceTree = SOURCE_ROOT; }; + F9721F390E8B0D0D00D7E813 /* dump_syms.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = dump_syms.cc; path = ../../../common/mac/dump_syms.cc; sourceTree = SOURCE_ROOT; }; + F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; }; + F9721F760E8B0DC700D7E813 /* bytereader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = bytereader.cc; path = ../../../common/dwarf/bytereader.cc; sourceTree = SOURCE_ROOT; }; + F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = dwarf2reader.cc; path = ../../../common/dwarf/dwarf2reader.cc; sourceTree = SOURCE_ROOT; }; + F9721F780E8B0DC700D7E813 /* functioninfo.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = functioninfo.cc; path = ../../../common/dwarf/functioninfo.cc; sourceTree = SOURCE_ROOT; }; + F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SenTestingKit.framework; path = Library/Frameworks/SenTestingKit.framework; sourceTree = DEVELOPER_DIR; }; + F9721FA80E8B0E4800D7E813 /* md5.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = md5.cc; path = ../../../common/md5.cc; sourceTree = SOURCE_ROOT; }; + F982089A0DB3280D0017AECA /* breakpad_nlist_test.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = breakpad_nlist_test.h; sourceTree = ""; }; + F982089B0DB3280D0017AECA /* breakpad_nlist_test.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = breakpad_nlist_test.cc; sourceTree = ""; }; + F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = breakpad_nlist_64.cc; sourceTree = ""; }; + F98208A20DB32CAE0017AECA /* breakpad_nlist_64.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = breakpad_nlist_64.h; sourceTree = ""; }; + F9AE19B50DB040E300C98454 /* minidump_tests32-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "minidump_tests32-Info.plist"; sourceTree = ""; }; + F9AE19C30DB04A9500C98454 /* minidump_tests64.cptest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = minidump_tests64.cptest; sourceTree = BUILT_PRODUCTS_DIR; }; + F9AE5B330DBFDBA300505983 /* minidump_tests32.cptest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = minidump_tests32.cptest; sourceTree = BUILT_PRODUCTS_DIR; }; + F9AE5B340DBFDBA300505983 /* minidump_tests64-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "minidump_tests64-Info.plist"; sourceTree = ""; }; + F9C5A4200DB82DD800209C76 /* DynamicImagesTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DynamicImagesTests.h; sourceTree = ""; }; + F9C5A4210DB82DD800209C76 /* DynamicImagesTests.cc */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DynamicImagesTests.cc; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8DD76F660486A84900D96B5E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9B37CEEC0AF98ECD00FA4BD4 /* CoreFoundation.framework in Frameworks */, + 8BFC813F11FF9A58002CB4DC /* libcrypto.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9B7CA84C0B1297F200CD3A1D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8BFC814511FF9A9D002CB4DC /* libcrypto.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9BD82A990B00267E0055103E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 9BD82AC10B0029DF0055103E /* CoreFoundation.framework in Frameworks */, + 8BFC814411FF9A9C002CB4DC /* libcrypto.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F93A88720E8B4C700026AF89 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8BFC814A11FF9B13002CB4DC /* libcrypto.dylib in Frameworks */, + 8BFC814B11FF9B3F002CB4DC /* SenTestingKit.framework in Frameworks */, + 8BFC814C11FF9B3F002CB4DC /* Cocoa.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9AE19C00DB04A9500C98454 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8BFC814811FF9B13002CB4DC /* libcrypto.dylib in Frameworks */, + 8BFC81A211FF9C2E002CB4DC /* CPlusTest.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9AE5B300DBFDBA300505983 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + F9721F6C0E8B0D7000D7E813 /* Cocoa.framework in Frameworks */, + F9721FA20E8B0E2300D7E813 /* SenTestingKit.framework in Frameworks */, + 8BFC814911FF9B13002CB4DC /* libcrypto.dylib in Frameworks */, + 8BFC81A311FF9C2F002CB4DC /* CPlusTest.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 08FB7794FE84155DC02AAC07 /* MinidumpWriter */ = { + isa = PBXGroup; + children = ( + 8BFC812011FF99D5002CB4DC /* Breakpad.xcconfig */, + 8BFC812111FF99D5002CB4DC /* BreakpadDebug.xcconfig */, + 8BFC812211FF99D5002CB4DC /* BreakpadRelease.xcconfig */, + F9721FA80E8B0E4800D7E813 /* md5.cc */, + F9721F760E8B0DC700D7E813 /* bytereader.cc */, + F9721F770E8B0DC700D7E813 /* dwarf2reader.cc */, + F9721F780E8B0DC700D7E813 /* functioninfo.cc */, + F9721F390E8B0D0D00D7E813 /* dump_syms.cc */, + F9721F380E8B0CFC00D7E813 /* dump_syms.h */, + F917C4F70E03265A00F86017 /* breakpad_exc_server.c */, + F917C4F80E03265A00F86017 /* breakpad_exc_server.h */, + F98208A10DB32CAE0017AECA /* breakpad_nlist_64.cc */, + F98208A20DB32CAE0017AECA /* breakpad_nlist_64.h */, + D2F6510C0BEF94EB00920385 /* macho_walker.cc */, + D2F6510D0BEF94EB00920385 /* macho_walker.h */, + D2F651070BEF949A00920385 /* dynamic_images.cc */, + D2F651080BEF949A00920385 /* dynamic_images.h */, + D2F650FA0BEF947200920385 /* file_id.cc */, + D2F650FB0BEF947200920385 /* file_id.h */, + D2F650FC0BEF947200920385 /* macho_id.cc */, + D2F650FD0BEF947200920385 /* macho_id.h */, + D2F650FE0BEF947200920385 /* macho_utilities.cc */, + D2F650FF0BEF947200920385 /* macho_utilities.h */, + F9C5A41F0DB82DB000209C76 /* testcases */, + 9BD82C040B0133420055103E /* Breakpad */, + 08FB7795FE84155DC02AAC07 /* Source */, + 9B37CEEA0AF98EB600FA4BD4 /* Frameworks */, + 1AB674ADFE9D54B511CA2CBB /* Products */, + F9AE19B50DB040E300C98454 /* minidump_tests32-Info.plist */, + F9AE5B340DBFDBA300505983 /* minidump_tests64-Info.plist */, + F93A88760E8B4C700026AF89 /* obj-cTestCases-Info.plist */, + ); + name = MinidumpWriter; + sourceTree = ""; + }; + 08FB7795FE84155DC02AAC07 /* Source */ = { + isa = PBXGroup; + children = ( + 9BD82BFD0B01333D0055103E /* exception_handler_test.cc */, + 9BD82BFE0B01333D0055103E /* minidump_generator_test.cc */, + 9B7CA8530B12989000CD3A1D /* minidump_file_writer_unittest.cc */, + ); + name = Source; + sourceTree = ""; + }; + 1AB674ADFE9D54B511CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 8DD76F6C0486A84900D96B5E /* generator_test */, + 9BD82A9B0B00267E0055103E /* handler_test */, + 9B7CA84E0B1297F200CD3A1D /* unit_test */, + F9AE19C30DB04A9500C98454 /* minidump_tests64.cptest */, + F9AE5B330DBFDBA300505983 /* minidump_tests32.cptest */, + F93A88750E8B4C700026AF89 /* octestcases.octest */, + ); + name = Products; + sourceTree = ""; + }; + 9B37CEEA0AF98EB600FA4BD4 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 8BFC813E11FF9A58002CB4DC /* libcrypto.dylib */, + 8BFC815411FF9B7F002CB4DC /* Carbon.framework */, + F9721FA10E8B0E2300D7E813 /* SenTestingKit.framework */, + F9721F6B0E8B0D7000D7E813 /* Cocoa.framework */, + 9B37CEEB0AF98ECD00FA4BD4 /* CoreFoundation.framework */, + 8BFC819211FF9C23002CB4DC /* CPlusTest.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 9BD82C040B0133420055103E /* Breakpad */ = { + isa = PBXGroup; + children = ( + 9B35FF560B267D5F008DE8C7 /* convert_UTF.cc */, + 9B35FF570B267D5F008DE8C7 /* convert_UTF.h */, + 9B35FF580B267D5F008DE8C7 /* string_conversion.cc */, + 9B35FF590B267D5F008DE8C7 /* string_conversion.h */, + 9BD82C090B0133520055103E /* exception_handler.cc */, + 9BD82C0A0B0133520055103E /* exception_handler.h */, + 9BD82C0B0B0133520055103E /* minidump_generator.cc */, + 9BD82C0C0B0133520055103E /* minidump_generator.h */, + 9BD82C230B01344C0055103E /* minidump_file_writer.cc */, + 9BE3C01E0B0CE329009892DF /* minidump_file_writer-inl.h */, + 9BD82C240B01344C0055103E /* minidump_file_writer.h */, + 9BD82C2B0B01345E0055103E /* string_utilities.cc */, + 9BD82C2C0B01345E0055103E /* string_utilities.h */, + ); + name = Breakpad; + sourceTree = ""; + }; + F9C5A41F0DB82DB000209C76 /* testcases */ = { + isa = PBXGroup; + children = ( + F982089A0DB3280D0017AECA /* breakpad_nlist_test.h */, + F982089B0DB3280D0017AECA /* breakpad_nlist_test.cc */, + F9C5A4200DB82DD800209C76 /* DynamicImagesTests.h */, + F9C5A4210DB82DD800209C76 /* DynamicImagesTests.cc */, + F9721F300E8B07E800D7E813 /* dwarftests.h */, + F9721F310E8B07E800D7E813 /* dwarftests.mm */, + ); + path = testcases; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8DD76F620486A84900D96B5E /* generator_test */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1DEB923108733DC60010E9CD /* Build configuration list for PBXNativeTarget "generator_test" */; + buildPhases = ( + 8DD76F640486A84900D96B5E /* Sources */, + 8DD76F660486A84900D96B5E /* Frameworks */, + 8DD76F690486A84900D96B5E /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = generator_test; + productInstallPath = "$(HOME)/bin"; + productName = MinidumpWriter; + productReference = 8DD76F6C0486A84900D96B5E /* generator_test */; + productType = "com.apple.product-type.tool"; + }; + 9B7CA84D0B1297F200CD3A1D /* unit_test */ = { + isa = PBXNativeTarget; + buildConfigurationList = 9B7CA8500B12984300CD3A1D /* Build configuration list for PBXNativeTarget "unit_test" */; + buildPhases = ( + 9B7CA84B0B1297F200CD3A1D /* Sources */, + 9B7CA84C0B1297F200CD3A1D /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = unit_test; + productName = "filewriter unit test"; + productReference = 9B7CA84E0B1297F200CD3A1D /* unit_test */; + productType = "com.apple.product-type.tool"; + }; + 9BD82A9A0B00267E0055103E /* handler_test */ = { + isa = PBXNativeTarget; + buildConfigurationList = 9BD82AA60B0026BF0055103E /* Build configuration list for PBXNativeTarget "handler_test" */; + buildPhases = ( + 9BD82A980B00267E0055103E /* Sources */, + 9BD82A990B00267E0055103E /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = handler_test; + productName = ExceptionTester; + productReference = 9BD82A9B0B00267E0055103E /* handler_test */; + productType = "com.apple.product-type.tool"; + }; + F93A88740E8B4C700026AF89 /* obj-c_TestCases */ = { + isa = PBXNativeTarget; + buildConfigurationList = F93A88790E8B4C700026AF89 /* Build configuration list for PBXNativeTarget "obj-c_TestCases" */; + buildPhases = ( + F93A88700E8B4C700026AF89 /* Resources */, + F93A88710E8B4C700026AF89 /* Sources */, + F93A88720E8B4C700026AF89 /* Frameworks */, + F93A88730E8B4C700026AF89 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "obj-c_TestCases"; + productName = octestcases; + productReference = F93A88750E8B4C700026AF89 /* octestcases.octest */; + productType = "com.apple.product-type.bundle"; + }; + F9AE19C20DB04A9500C98454 /* minidump_tests64 */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9AE19C70DB04AA200C98454 /* Build configuration list for PBXNativeTarget "minidump_tests64" */; + buildPhases = ( + F9AE19BE0DB04A9500C98454 /* Resources */, + F9AE19BF0DB04A9500C98454 /* Sources */, + F9AE19C00DB04A9500C98454 /* Frameworks */, + F9AE19C10DB04A9500C98454 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = minidump_tests64; + productName = minidump_tests; + productReference = F9AE19C30DB04A9500C98454 /* minidump_tests64.cptest */; + productType = "com.apple.product-type.bundle"; + }; + F9AE5B320DBFDBA300505983 /* minidump_tests32 */ = { + isa = PBXNativeTarget; + buildConfigurationList = F9AE5B380DBFDBA300505983 /* Build configuration list for PBXNativeTarget "minidump_tests32" */; + buildPhases = ( + F9AE5B2E0DBFDBA300505983 /* Resources */, + F9AE5B2F0DBFDBA300505983 /* Sources */, + F9AE5B300DBFDBA300505983 /* Frameworks */, + F9AE5B310DBFDBA300505983 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = minidump_tests32; + productName = Untitled; + productReference = F9AE5B330DBFDBA300505983 /* minidump_tests32.cptest */; + productType = "com.apple.product-type.bundle"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 08FB7793FE84155DC02AAC07 /* Project object */ = { + isa = PBXProject; + buildConfigurationList = 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "minidump_test" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 1; + knownRegions = ( + English, + Japanese, + French, + German, + ); + mainGroup = 08FB7794FE84155DC02AAC07 /* MinidumpWriter */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8DD76F620486A84900D96B5E /* generator_test */, + 9BD82A9A0B00267E0055103E /* handler_test */, + 9B7CA84D0B1297F200CD3A1D /* unit_test */, + F9AE19C20DB04A9500C98454 /* minidump_tests64 */, + F9AE5B320DBFDBA300505983 /* minidump_tests32 */, + F93A88740E8B4C700026AF89 /* obj-c_TestCases */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + F93A88700E8B4C700026AF89 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9AE19BE0DB04A9500C98454 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9AE5B2E0DBFDBA300505983 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + F93A88730E8B4C700026AF89 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n"; + }; + F9AE19C10DB04A9500C98454 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n# Run gcov on the framework getting tested\nif [ \"${CONFIGURATION}\" = 'Coverage' ];\nthen\n FRAMEWORK_NAME=minidump_tests64\n FRAMEWORK_OBJ_DIR=${OBJROOT}/${PROJECT_NAME}.build/${CONFIGURATION}/${FRAMEWORK_NAME}.build/Objects-normal/${NATIVE_ARCH_ACTUAL}\n mkdir -p coverage\n pushd coverage\n echo find ${OBJROOT} -name *.gcda -exec gcov -o ${FRAMEWORK_OBJ_DIR} {} \\;\n find ${OBJROOT} -name *.gcda -exec gcov -o ${FRAMEWORK_OBJ_DIR} {} \\;\n popd\nfi "; + }; + F9AE5B310DBFDBA300505983 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Run the unit tests in this test bundle.\n\"${SYSTEM_DEVELOPER_DIR}/Tools/RunUnitTests\"\n\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8DD76F640486A84900D96B5E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9BD82C020B01333D0055103E /* minidump_generator_test.cc in Sources */, + 9BD82C0F0B0133520055103E /* exception_handler.cc in Sources */, + 9BD82C110B0133520055103E /* minidump_generator.cc in Sources */, + 9BD82C260B01344C0055103E /* minidump_file_writer.cc in Sources */, + 9BD82C2E0B01345E0055103E /* string_utilities.cc in Sources */, + D2F651000BEF947200920385 /* file_id.cc in Sources */, + D2F651020BEF947200920385 /* macho_id.cc in Sources */, + D2F651040BEF947200920385 /* macho_utilities.cc in Sources */, + D2F651090BEF949A00920385 /* dynamic_images.cc in Sources */, + D2F6510E0BEF94EB00920385 /* macho_walker.cc in Sources */, + D2F651110BEF951700920385 /* string_conversion.cc in Sources */, + D2F651150BEF953000920385 /* convert_UTF.cc in Sources */, + 8BFC81B011FF9C8D002CB4DC /* breakpad_nlist_64.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9B7CA84B0B1297F200CD3A1D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9B7CA8540B12989000CD3A1D /* minidump_file_writer_unittest.cc in Sources */, + 9B7CA8550B1298A100CD3A1D /* minidump_file_writer.cc in Sources */, + 9BC1D2940B336F2300F2A2B4 /* convert_UTF.cc in Sources */, + 9BC1D2950B336F2500F2A2B4 /* string_conversion.cc in Sources */, + 8BFC81AE11FF9C8C002CB4DC /* breakpad_nlist_64.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9BD82A980B00267E0055103E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9BD82BFF0B01333D0055103E /* exception_handler_test.cc in Sources */, + 9BD82C0D0B0133520055103E /* exception_handler.cc in Sources */, + 9BD82C0E0B0133520055103E /* minidump_generator.cc in Sources */, + 9BD82C250B01344C0055103E /* minidump_file_writer.cc in Sources */, + 9BD82C2D0B01345E0055103E /* string_utilities.cc in Sources */, + 9B35FF5A0B267D5F008DE8C7 /* convert_UTF.cc in Sources */, + 9B35FF5B0B267D5F008DE8C7 /* string_conversion.cc in Sources */, + D2F6511B0BEF970E00920385 /* dynamic_images.cc in Sources */, + D2F6511D0BEF973500920385 /* file_id.cc in Sources */, + D2F6511E0BEF973600920385 /* macho_id.cc in Sources */, + D2F6511F0BEF973900920385 /* macho_utilities.cc in Sources */, + D2F651210BEF975400920385 /* macho_walker.cc in Sources */, + 8BFC81AF11FF9C8C002CB4DC /* breakpad_nlist_64.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F93A88710E8B4C700026AF89 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F93A88860E8B4C9A0026AF89 /* dwarftests.mm in Sources */, + F93A88870E8B4C9A0026AF89 /* dump_syms.cc in Sources */, + F93A88880E8B4C9A0026AF89 /* bytereader.cc in Sources */, + F93A88890E8B4C9A0026AF89 /* dwarf2reader.cc in Sources */, + F93A888A0E8B4C9A0026AF89 /* functioninfo.cc in Sources */, + F93A888B0E8B4C9A0026AF89 /* md5.cc in Sources */, + F93A887D0E8B4C8C0026AF89 /* macho_walker.cc in Sources */, + F93A887E0E8B4C8C0026AF89 /* macho_id.cc in Sources */, + F93A887F0E8B4C8C0026AF89 /* macho_utilities.cc in Sources */, + F93A88800E8B4C8C0026AF89 /* file_id.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9AE19BF0DB04A9500C98454 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9B34E870DBC1E1600306484 /* dynamic_images.cc in Sources */, + F982089C0DB3280D0017AECA /* breakpad_nlist_test.cc in Sources */, + F98208A30DB32CAE0017AECA /* breakpad_nlist_64.cc in Sources */, + F9C5A4220DB82DD800209C76 /* DynamicImagesTests.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + F9AE5B2F0DBFDBA300505983 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + F9AE5B390DBFDBDB00505983 /* dynamic_images.cc in Sources */, + F9AE5B3A0DBFDBDB00505983 /* DynamicImagesTests.cc in Sources */, + 8BFC81AD11FF9C8A002CB4DC /* breakpad_nlist_64.cc in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 1DEB923208733DC60010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++17"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"$(DEVELOPER_FRAMEWORKS_DIR)\"", + ); + PRODUCT_NAME = generator_test; + USER_HEADER_SEARCH_PATHS = "../../../** $(inherited)"; + }; + name = Debug; + }; + 1DEB923308733DC60010E9CD /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_CXX_LANGUAGE_STANDARD = "c++17"; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "\"$(DEVELOPER_FRAMEWORKS_DIR)\"", + ); + PRODUCT_NAME = generator_test; + USER_HEADER_SEARCH_PATHS = "../../../** $(inherited)"; + }; + name = Release; + }; + 1DEB923608733DC60010E9CD /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8BFC812111FF99D5002CB4DC /* BreakpadDebug.xcconfig */; + buildSettings = { + }; + name = Debug; + }; + 1DEB923708733DC60010E9CD /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8BFC812211FF99D5002CB4DC /* BreakpadRelease.xcconfig */; + buildSettings = { + }; + name = Release; + }; + 9B7CA8510B12984300CD3A1D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = unit_test; + USER_HEADER_SEARCH_PATHS = "../../../** $(inherited)"; + }; + name = Debug; + }; + 9B7CA8520B12984300CD3A1D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = unit_test; + USER_HEADER_SEARCH_PATHS = "../../../** $(inherited)"; + }; + name = Release; + }; + 9BD82AA70B0026BF0055103E /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = handler_test; + USER_HEADER_SEARCH_PATHS = "../../.. $(inherited)"; + }; + name = Debug; + }; + 9BD82AA80B0026BF0055103E /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = handler_test; + USER_HEADER_SEARCH_PATHS = "../../.. $(inherited)"; + }; + name = Release; + }; + F93A88770E8B4C700026AF89 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks"; + INFOPLIST_FILE = "obj-cTestCases-Info.plist"; + PRODUCT_NAME = octestcases; + USER_HEADER_SEARCH_PATHS = "../../../..//**"; + WRAPPER_EXTENSION = octest; + }; + name = Debug; + }; + F93A88780E8B4C700026AF89 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks"; + INFOPLIST_FILE = "obj-cTestCases-Info.plist"; + PRODUCT_NAME = octestcases; + USER_HEADER_SEARCH_PATHS = "../../../..//**"; + WRAPPER_EXTENSION = octest; + }; + name = Release; + }; + F9AE19C40DB04A9500C98454 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks"; + INFOPLIST_FILE = "minidump_tests64-Info.plist"; + PRODUCT_NAME = minidump_tests64; + USER_HEADER_SEARCH_PATHS = "../../../**"; + WRAPPER_EXTENSION = cptest; + }; + name = Debug; + }; + F9AE19C50DB04A9500C98454 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks"; + INFOPLIST_FILE = "minidump_tests64-Info.plist"; + PRODUCT_NAME = minidump_tests64; + USER_HEADER_SEARCH_PATHS = "../../../**"; + WRAPPER_EXTENSION = cptest; + }; + name = Release; + }; + F9AE5B350DBFDBA300505983 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks"; + INFOPLIST_FILE = "minidump_tests32-Info.plist"; + PRODUCT_NAME = minidump_tests32; + USER_HEADER_SEARCH_PATHS = "../../../**"; + WRAPPER_EXTENSION = cptest; + }; + name = Debug; + }; + F9AE5B370DBFDBA300505983 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + FRAMEWORK_SEARCH_PATHS = "$(DEVELOPER_LIBRARY_DIR)/Frameworks"; + INFOPLIST_FILE = "minidump_tests32-Info.plist"; + PRODUCT_NAME = minidump_tests32; + USER_HEADER_SEARCH_PATHS = "../../../**"; + WRAPPER_EXTENSION = cptest; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 1DEB923108733DC60010E9CD /* Build configuration list for PBXNativeTarget "generator_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB923208733DC60010E9CD /* Debug */, + 1DEB923308733DC60010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 1DEB923508733DC60010E9CD /* Build configuration list for PBXProject "minidump_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1DEB923608733DC60010E9CD /* Debug */, + 1DEB923708733DC60010E9CD /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 9B7CA8500B12984300CD3A1D /* Build configuration list for PBXNativeTarget "unit_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9B7CA8510B12984300CD3A1D /* Debug */, + 9B7CA8520B12984300CD3A1D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 9BD82AA60B0026BF0055103E /* Build configuration list for PBXNativeTarget "handler_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9BD82AA70B0026BF0055103E /* Debug */, + 9BD82AA80B0026BF0055103E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F93A88790E8B4C700026AF89 /* Build configuration list for PBXNativeTarget "obj-c_TestCases" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F93A88770E8B4C700026AF89 /* Debug */, + F93A88780E8B4C700026AF89 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9AE19C70DB04AA200C98454 /* Build configuration list for PBXNativeTarget "minidump_tests64" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9AE19C40DB04A9500C98454 /* Debug */, + F9AE19C50DB04A9500C98454 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F9AE5B380DBFDBA300505983 /* Build configuration list for PBXNativeTarget "minidump_tests32" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F9AE5B350DBFDBA300505983 /* Debug */, + F9AE5B370DBFDBA300505983 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 08FB7793FE84155DC02AAC07 /* Project object */; +} diff --git a/shared/sentry/external/breakpad/src/client/mac/handler/minidump_tests32-Info.plist b/shared/sentry/external/breakpad/src/client/mac/handler/minidump_tests32-Info.plist new file mode 100644 index 000000000..921ebf357 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/handler/minidump_tests32-Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.google.breakpad.minidump_tests32 + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + BNDL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + + diff --git a/shared/sentry/external/breakpad/src/client/mac/handler/minidump_tests64-Info.plist b/shared/sentry/external/breakpad/src/client/mac/handler/minidump_tests64-Info.plist new file mode 100644 index 000000000..acfbd3091 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/handler/minidump_tests64-Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.google.breakpad.minidump_tests64 + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + BNDL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + CSResourcesFileMapped + yes + + diff --git a/shared/sentry/external/breakpad/src/client/mac/handler/obj-cTestCases-Info.plist b/shared/sentry/external/breakpad/src/client/mac/handler/obj-cTestCases-Info.plist new file mode 100644 index 000000000..65013556d --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/handler/obj-cTestCases-Info.plist @@ -0,0 +1,20 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + com.yourcompany.${PRODUCT_NAME:identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundlePackageType + BNDL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + + diff --git a/shared/sentry/external/breakpad/src/client/mac/handler/protected_memory_allocator.cc b/shared/sentry/external/breakpad/src/client/mac/handler/protected_memory_allocator.cc new file mode 100644 index 000000000..6142ad124 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/handler/protected_memory_allocator.cc @@ -0,0 +1,92 @@ +// 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. +// +// ProtectedMemoryAllocator +// +// See the header file for documentation + +#include "protected_memory_allocator.h" +#include + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +ProtectedMemoryAllocator::ProtectedMemoryAllocator(vm_size_t pool_size) + : pool_size_(pool_size), + next_alloc_offset_(0), + valid_(false) { + + kern_return_t result = vm_allocate(mach_task_self(), + &base_address_, + pool_size, + TRUE + ); + + valid_ = (result == KERN_SUCCESS); + assert(valid_); +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +ProtectedMemoryAllocator::~ProtectedMemoryAllocator() { + vm_deallocate(mach_task_self(), + base_address_, + pool_size_ + ); +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +char *ProtectedMemoryAllocator::Allocate(vm_size_t bytes) { + if (valid_ && next_alloc_offset_ + bytes <= pool_size_) { + char *p = (char*)base_address_ + next_alloc_offset_; + next_alloc_offset_ += bytes; + return p; + } + + return NULL; // ran out of memory in our allocation block +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +kern_return_t ProtectedMemoryAllocator::Protect() { + kern_return_t result = vm_protect(mach_task_self(), + base_address_, + pool_size_, + FALSE, + VM_PROT_READ); + + return result; +} + +//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +kern_return_t ProtectedMemoryAllocator::Unprotect() { + kern_return_t result = vm_protect(mach_task_self(), + base_address_, + pool_size_, + FALSE, + VM_PROT_READ | VM_PROT_WRITE); + + return result; +} diff --git a/shared/sentry/external/breakpad/src/client/mac/handler/protected_memory_allocator.h b/shared/sentry/external/breakpad/src/client/mac/handler/protected_memory_allocator.h new file mode 100644 index 000000000..7e188db26 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/handler/protected_memory_allocator.h @@ -0,0 +1,85 @@ +// 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. +// +// ProtectedMemoryAllocator +// +// A very simple allocator class which allows allocation, but not deallocation. +// The allocations can be made read-only with the Protect() method. +// This class is NOT useful as a general-purpose memory allocation system, +// since it does not allow deallocation. It is useful to use for a group +// of allocations which are created in the same time-frame and destroyed +// in the same time-frame. It is useful for making allocations of memory +// which will not need to change often once initialized. This memory can then +// be protected from memory smashers by calling the Protect() method. + +#ifndef PROTECTED_MEMORY_ALLOCATOR_H__ +#define PROTECTED_MEMORY_ALLOCATOR_H__ + +#include + +// +class ProtectedMemoryAllocator { + public: + ProtectedMemoryAllocator(vm_size_t pool_size); + ~ProtectedMemoryAllocator(); + + // Returns a pointer to an allocation of size n within the pool. + // Fails by returning NULL is no more space is available. + // Please note that the pointers returned from this method should not + // be freed in any way (for example by calling free() on them ). + char * Allocate(vm_size_t n); + + // Returns the base address of the allocation pool. + char * GetBaseAddress() { return (char*)base_address_; } + + // Returns the size of the allocation pool, including allocated + // plus free space. + vm_size_t GetTotalSize() { return pool_size_; } + + // Returns the number of bytes already allocated in the pool. + vm_size_t GetAllocatedSize() { return next_alloc_offset_; } + + // Returns the number of bytes available for allocation. + vm_size_t GetFreeSize() { return pool_size_ - next_alloc_offset_; } + + // Makes the entire allocation pool read-only including, of course, + // all allocations made from the pool. + kern_return_t Protect(); + + // Makes the entire allocation pool read/write. + kern_return_t Unprotect(); + + private: + vm_size_t pool_size_; + vm_address_t base_address_; + vm_size_t next_alloc_offset_; + bool valid_; +}; + +#endif // PROTECTED_MEMORY_ALLOCATOR_H__ diff --git a/shared/sentry/external/breakpad/src/client/mac/handler/testcases/DynamicImagesTests.cc b/shared/sentry/external/breakpad/src/client/mac/handler/testcases/DynamicImagesTests.cc new file mode 100644 index 000000000..0a80e434c --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/handler/testcases/DynamicImagesTests.cc @@ -0,0 +1,79 @@ +// 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. + +// +// DynamicImagesTests.cpp +// minidump_test +// +// Created by Neal Sidhwaney on 4/17/08. +// Copyright 2008 Google Inc. All rights reserved. +// + +#include "client/mac/handler/testcases/DynamicImagesTests.h" +#include "client/mac/handler/dynamic_images.h" + +DynamicImagesTests test2(TEST_INVOCATION(DynamicImagesTests, + ReadTaskMemoryTest)); +DynamicImagesTests test3(TEST_INVOCATION(DynamicImagesTests, + ReadLibrariesFromLocalTaskTest)); + +DynamicImagesTests::DynamicImagesTests(TestInvocation* invocation) + : TestCase(invocation) { +} + +DynamicImagesTests::~DynamicImagesTests() { +} + +void DynamicImagesTests::ReadTaskMemoryTest() { + kern_return_t kr; + + // pick test2 as a symbol we know to be valid to read + // anything will work, really + void* addr = reinterpret_cast(&test2); + std::vector buf(getpagesize()); + + fprintf(stderr, "reading 0x%p\n", addr); + kr = google_breakpad::ReadTaskMemory(mach_task_self(), + (uint64_t)addr, + getpagesize(), + buf); + + CPTAssert(kr == KERN_SUCCESS); + + CPTAssert(0 == memcmp(&buf[0], (const void*)addr, getpagesize())); +} + +void DynamicImagesTests::ReadLibrariesFromLocalTaskTest() { + + mach_port_t me = mach_task_self(); + google_breakpad::DynamicImages* d = new google_breakpad::DynamicImages(me); + + fprintf(stderr,"Local task image count: %d\n", d->GetImageCount()); + + CPTAssert(d->GetImageCount() > 0); +} diff --git a/shared/sentry/external/breakpad/src/client/mac/handler/testcases/DynamicImagesTests.h b/shared/sentry/external/breakpad/src/client/mac/handler/testcases/DynamicImagesTests.h new file mode 100644 index 000000000..e1e79993b --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/handler/testcases/DynamicImagesTests.h @@ -0,0 +1,52 @@ +// 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. + +// +// DynamicImagesTests.h +// minidump_test +// +// Created by Neal Sidhwaney on 4/17/08. +// Copyright 2008 Google Inc. All rights reserved. +// +// + +#ifndef _CLIENT_MAC_HANDLER_TESTCASES_DYNAMICIMAGESTESTS_H__ +#define _CLIENT_MAC_HANDLER_TESTCASES_DYNAMICIMAGESTESTS_H__ + +#include + +class DynamicImagesTests : public TestCase { + public: + explicit DynamicImagesTests(TestInvocation* invocation); + virtual ~DynamicImagesTests(); + + void ReadTaskMemoryTest(); + void ReadLibrariesFromLocalTaskTest(); +}; + +#endif /* _CLIENT_MAC_HANDLER_TESTCASES_DYNAMICIMAGESTESTS_H__ */ diff --git a/shared/sentry/external/breakpad/src/client/mac/handler/testcases/breakpad_nlist_test.cc b/shared/sentry/external/breakpad/src/client/mac/handler/testcases/breakpad_nlist_test.cc new file mode 100644 index 000000000..2014b9078 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/handler/testcases/breakpad_nlist_test.cc @@ -0,0 +1,106 @@ +// 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. + +// +// breakpad_nlist_test.cc +// minidump_test +// +// Created by Neal Sidhwaney on 4/13/08. +// Copyright 2008 Google Inc. All rights reserved. +// + +#include "client/mac/handler/testcases/breakpad_nlist_test.h" +#include +#include "client/mac/handler/breakpad_nlist_64.h" + +BreakpadNlistTest test1(TEST_INVOCATION(BreakpadNlistTest, CompareToNM)); + +BreakpadNlistTest::BreakpadNlistTest(TestInvocation* invocation) + : TestCase(invocation) { +} + + +BreakpadNlistTest::~BreakpadNlistTest() { +} + +void BreakpadNlistTest::CompareToNM() { +#if TARGET_CPU_X86_64 + system("/usr/bin/nm -arch x86_64 /usr/lib/dyld > /tmp/dyld-namelist.txt"); +#elif TARGET_CPU_PPC64 + system("/usr/bin/nm -arch ppc64 /usr/lib/dyld > /tmp/dyld-namelist.txt"); +#endif + + FILE* fd = fopen("/tmp/dyld-namelist.txt", "rt"); + + char oneNMAddr[30]; + char symbolType; + char symbolName[500]; + while (!feof(fd)) { + fscanf(fd, "%s %c %s", oneNMAddr, &symbolType, symbolName); + breakpad_nlist symbolList[2]; + breakpad_nlist& list = symbolList[0]; + + memset(symbolList, 0, sizeof(breakpad_nlist)*2); + const char* symbolNames[2]; + symbolNames[0] = (const char*)symbolName; + symbolNames[1] = "\0"; + breakpad_nlist_64("/usr/lib/dyld", &list, symbolNames); + uint64_t nmAddr = strtol(oneNMAddr, NULL, 16); + if (!IsSymbolMoreThanOnceInDyld(symbolName)) { + CPTAssert(nmAddr == symbolList[0].n_value); + } + } + + fclose(fd); +} + +bool BreakpadNlistTest::IsSymbolMoreThanOnceInDyld(const char* symbolName) { + // These are the symbols that occur more than once when nm dumps + // the symbol table of /usr/lib/dyld. Our nlist program returns + // the first address because it's doing a search so we need to exclude + // these from causing the test to fail + const char* multipleSymbols[] = { + "__Z41__static_initialization_and_destruction_0ii", + "___tcf_0", + "___tcf_1", + "_read_encoded_value_with_base", + "_read_sleb128", + "_read_uleb128", + "\0"}; + + bool found = false; + + for (int i = 0; multipleSymbols[i][0]; i++) { + if (!strcmp(multipleSymbols[i], symbolName)) { + found = true; + break; + } + } + + return found; +} diff --git a/shared/sentry/external/breakpad/src/client/mac/handler/testcases/breakpad_nlist_test.h b/shared/sentry/external/breakpad/src/client/mac/handler/testcases/breakpad_nlist_test.h new file mode 100644 index 000000000..ee8010c75 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/handler/testcases/breakpad_nlist_test.h @@ -0,0 +1,62 @@ +// 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. + +// +// breakpad_nlist_test.h +// minidump_test +// +// Created by Neal Sidhwaney on 4/13/08. +// Copyright 2008 Google Inc. All rights reserved. +// +// + +#ifndef CLIENT_MAC_HANDLER_TESTCASES_BREAKPAD_NLIST_TEST_H__ +#define CLIENT_MAC_HANDLER_TESTCASES_BREAKPAD_NLIST_TEST_H__ + +#include + +class BreakpadNlistTest : public TestCase { + private: + + // nm dumps multiple addresses for the same symbol in + // /usr/lib/dyld. So we track those so we don't report failures + // in mismatches between what our nlist returns and what nm has + // for the duplicate symbols. + bool IsSymbolMoreThanOnceInDyld(const char* symbolName); + + public: + explicit BreakpadNlistTest(TestInvocation* invocation); + virtual ~BreakpadNlistTest(); + + + /* This test case runs nm on /usr/lib/dyld and then compares the + output of every symbol to what our nlist implementation returns */ + void CompareToNM(); +}; + +#endif /* CLIENT_MAC_HANDLER_TESTCASES_BREAKPAD_NLIST_TEST_H__*/ diff --git a/shared/sentry/external/breakpad/src/client/mac/handler/testcases/dwarftests.h b/shared/sentry/external/breakpad/src/client/mac/handler/testcases/dwarftests.h new file mode 100644 index 000000000..21ff7a44f --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/handler/testcases/dwarftests.h @@ -0,0 +1,46 @@ +// 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. + +// +// dwarftests.h +// minidump_test +// +// Created by Neal Sidhwaney on 9/24/08. +// Copyright 2008 Google Inc. All rights reserved. +// + +#import + + +@interface dwarftests : SenTestCase { + +} + +- (void) testDWARFSymbolFileGeneration; + +@end diff --git a/shared/sentry/external/breakpad/src/client/mac/handler/testcases/dwarftests.mm b/shared/sentry/external/breakpad/src/client/mac/handler/testcases/dwarftests.mm new file mode 100644 index 000000000..40c69aff2 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/handler/testcases/dwarftests.mm @@ -0,0 +1,60 @@ +// 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. + +// +// dwarftests.m +// minidump_test +// +// Created by Neal Sidhwaney on 9/24/08. +// Copyright 2008 Google Inc. All rights reserved. +// + +#import "dwarftests.h" +#import "dump_syms.h" + +@implementation dwarftests +- (void) testDWARFSymbolFileGeneration { + NSString *inputBreakpadSymbolFile = @"testcases/testdata/dump_syms_i386_breakpad.sym"; + NSString *outputBreakpadSymbolFile = @"/tmp/dump_syms_i386.breakpad"; + + DumpSymbols *dump = [[DumpSymbols alloc] initWithContentsOfFile:@"testcases/testdata/dump_syms_dwarf_data"]; + + STAssertNotNil(dump, @"DumpSymbols is nil"); + [dump setArchitecture:@"i386"]; + [dump writeSymbolFile:outputBreakpadSymbolFile]; + + NSData *d = [[NSData alloc] initWithContentsOfFile:inputBreakpadSymbolFile]; + STAssertNotNil(d, @"Input breakpad symbol file not found"); + + NSData *d1 = [[NSData alloc] initWithContentsOfFile:outputBreakpadSymbolFile]; + STAssertNotNil(d1, @"Output breakpad symbol file not found"); + + STAssertTrue([d isEqualToData:d1], + @"Symbol files were not equal!",nil); +} +@end diff --git a/shared/sentry/external/breakpad/src/client/mac/handler/testcases/testdata/dump_syms_dwarf_data b/shared/sentry/external/breakpad/src/client/mac/handler/testcases/testdata/dump_syms_dwarf_data new file mode 100644 index 0000000000000000000000000000000000000000..5be17aeedcf1bb46e7a5290fbd7d9eee5969caf0 GIT binary patch literal 702795 zcmeFad3Y5?7Czi{ZwIn*lRyF?kSm6;1VTV|G$0@%AR-{5GDz4LWs^-rRAf_85m6D* zQAAWmQ5nSDQCtR>aX~}}9S0qUaY4py+{fj6->N>{H=XWIp6C1Hd%oXq@<5%gdf#*E z)T!#~zP(hx_S3(At#4UY9RD-%zaIX7lYs<(&G5ev|F3`gvdWY0K5qAUt;!m$+x6$K z6w0nbQdKqT#4|@BBY(NrSQnF=MZ0Ove->6%Ets-+0k_PbUw=nZ5X-W?O=gx!wyJ8< z`~~x7%%0{H_-%B0f!>)v&lAf^-G+@hv*#~BI$fDTUmO!WLd$`Z(Pw zs;ZhiVZj6s57c+y)Ve(l);altnUlQ|zm1;s!TIyHq{K+XacXLZ`fc>Bn+;!oR*%Kz zEne-ZeStW-e^ph}CQYiEvUt*zxeKZmOql5RQqmcW# z^C#oVnKHniHD&(%3Dc%j&6_e6P6X;Zx~cxB#se{!Ip5JI)o-Kn8Bf#OGINb%8ak6^ zPDqJ@-^Q{O8>w}RzNvTClm!z!Lw+09vtI4tHfC_DfuZTzwBN?EI@_2qd(xDuMHA-D zuMrRH`MUTqYtH0_GpAI|m_2n4Jn{Q+ONtH8T52??Gyj5F6X(p7vE{e%WSwlxn8nfI zxKYE8y>+m`vAJ8fVel!#PaWwUf%d|4-`M2%l=f6r&7Cu2_JS!0q5i9?3eY!@tHUgh ziSk!fb1^Ku~gm0)t|ZX28FE2La(B-#r-+yD)vdb*%zV@Due@kq55BgYeq5nPi)LxIB_-(#Pj`a$(~r>x_+#(ve#ETBdKOA#m0gQ z=GL^a`Kfg4g(q=H{yc1!U|*Vkak`~Ozf8L@!`g;gr`?-6s;ar++Q9Kt1OB$;EX}Yg z8`xIP_HY~3E&ea}zX<#<0{@G^|03{zY6MiCYL#e|6;Cuvj8wTeF2qW$tgJpLz%>E0 zi8GhRRy=WR)^S;b8{rHl2ZVTnoAtP9S@o3~+(;pxezY~XQAdmVX%eEO(XYcua#bWN?cCXWqlD@Q)SC)rKYUZElW#bwyIWYn610I zF=xGP*nU%^WwmW&ozzG&WsQ_%*wA*att?}xy;p!LX{$Wf>Awz69mbdAvn#WkV+2*C zb`68u(Y0WRogK@as+CLWF%^~*T#s#8ojtB`tBWp3M^~jrqN__Ykrbod+;U-CR(FYz zj->V;&i3%Ni54uYXFzFluzMk_=_DNM^O|MHd6kMEmDOm*c5jacPfS@Kw@gNaWgYL8 z4b3XXAhE2zW`J5&KerS0-qGKsvbkiQ;Fi#!xHF(esb!t0O{K{`FwonT%&u~hS5>jz z>j#m+9f`ZZU}Lc>#sWvv5VwLf<1-6S_6l|Ahi09U$@UN%S`AA@P657S*kzAH?9OqH|Qsvq-6G-Z4Uaq|8cFv;u z3Wu(D$FA94^($SwETiRBqKq-YIn%OMr_`0&-pdpw24`UK7=3vPPgi87x+_#8!VU|mBOcsNHdsy8)=^1 z^(d3`9X9Yf=*jV3JI=!EtF?}Yxf?)}-PN)-z(~q*EVWN>Oo7^@1lxKASBcE!BXhB# zU2BYWOlq4TdqD1k(3=c=Bv_%lbz>%i7x8vi^ng zHT`gs0+ZWL#EB5fpE}vH9zaF2hhd+Fo&As(f;W7mWqkqJceG`F3%lLVggwZCXF;L| zM^Cb>R}sk4^DXOC=q-SxKWiaTmGz1vGZ&luAjk(${)3b6et{`p00~pp0m$RvNB&aF zdI$N>A>pFc^&(?;7-TEt7dUbg;l)OlL&B7G7i2fc_nh+Y9GQEG!Ph_@ z2Ye4?56E93kAdv8%;-&pL};v?kO-x95OM-!-%Al+$jOi$AlE~dK|Tpdzc(z$Ne<*5 z$S#mSL7oOVY=w#6xRu7w#g1GLc^2>^kf%fTUxlMP}r2R9U{8}f!4YCc&_d5B*kc?B&m1g{$14(?nBOiyP zo&AvH*;g6fC`kIb7Lxcj$QK~zUTyMgAjy9maxkR5*0LUhdzw>Oj(pyc2OaqXB-@|4&WxXdj$97O^6McPmtBw}AnRRg>ZyceJ)<2t*O6-= z&qDbj$c2z&)?NIN&V6dMt?XY z_0EB0Kkjz&??TefxEqa~qK(GRnULhqgXI2kKjf#78*eh@J0Z_PK6{fHKj%QsLH=FH z36PaHo4Bur{2lTiLOuYw{uX2JHOPmMU-^5pe>@7w`u0I~gpA#4^oK&8f&5m;Ns#Hc z8UF@CPEhFg&6Wkz)_~iM{y50?$ggqoTOp5Ei0c;1f@y2{9VU+VK(fB)9eEIv^(MBO zde4MBANjqICqU-kY4)2XkR6cU2H6VoHOM^34CcgE$V1k<$Fgom`8LR2kRL+QZu-5(ZV_Y^@=GAu|63qw=Sio$`F6{if&6O7_K-Uv zPlo&zvJqs%`z&h~jiamqn1>klzlu3bHdg`7_Am!#M6h+K(8&3L%;Q74k~RHIL$; zN*;oCHsn%B*0U9o{j%T5{|xzK4EyhshBxX@X5P64l6pTuQm^DGqc;+g`6ZClTkwqG z?SniChFWpjjua!B%bI{9}YuS0(J zeuHm-B)-SVe-6p|?RODl*pa_V^Z{-K%}~zc9Lc|#ak3TiPGCb0Vt#;J{eflu6?)@7 zH2F=3Fm7$^ho4&3DKIt>@@mL0Asa#3hs}7M4Y>;WZy-4yMt)|F(~BTGAiouoe(s0F zE@3S{V)m~OA0g-omO^sBUkllfJV?$%Gru(W zGDz+R4?FpnA)BCGAAW7l1G@jm_`4Z$5_tAEW;_msq~3DKlObPo^7gkTKOAxb%6CGx zf;<5E0_5iJOuf4xxj%M-vv(qXZ~tKW>6{-;efuFV0Y2&{Q~n9$4CF`t*RuWsnf%!t z2i}G3f_&l^{F;MY1i1qCe)<*XgOG=D%aV9fDedPukY&hU4_O8I4dknk&&HJI@3fVk z-;O|@3jNA@N{_S2kdL8Xlla-l^4Zy@d;?@}l<&_~)?C<;zfx?(tZr>$&iEOSr>vIs z&@WAtb+?cp4eycm!pkjGRx{WyZ>p?+L(1P+Y{abdx(Kfbq(&&KfW%Z|eFE75vT<`| zoezK3worQhOfON^>nJ|}`7QeOwN}dd1>^md_R6{ebMCzdPSzZ)T0&vwY0 zAP06(x<6+^_CS6sB&Hnxc(xvh<7#AQWsN{R=X5oEK>DvM(wOz>sbb&mru4iv6tXF> zsgOrOu7JWT$fqD5f{_KIl-_@Tg6!A; z$J2?3E9|$OhW4PJ`_F=(kax{j`nZGRilket^x zLH2^&57`p(2T1mB>kE`V&zT6x^3{;tAfJR}9N%{2myk~*zhj9hf7y|TATL1qxC>3c z&V!`hRglL)z6i;9)>~@)83;*yJS6d3AX)Ej$P*y#iwwU%B;)b}Z5+cguC<7^q^Lm2nnFID<{Y&|6R-#w6*LJnGvctb9R{1aq% z?CfWQfBi~h=UK?}QGVP*M?V{`UgRt zi~KGpe;AVfbzf`nnUI5#f7r<%f#m!)>KZd|?Sf>#ABJ3rddFXD#^=kB$DsUYCqH$) z@plR26qJ7ec?x93bT{dPU% z?ZCU=1iygax=C3tAwT_QrH{8iLt=`t+Wy|;pM+$+nYSAK?vNZOi=6xx$m?kDHf22s zc^H!8t@UQiCy7<>$WOf2BZpSW|p`(TghZ^3i1@f-e>rIw~=+ctJ9i#q({wW79=c6}s? z^i?q%6R_1FP4JPxxgCbj22S6@a9)E-M&OSE=U58UmupvHxLnf;!y95k;@lU8^O{s5 z0`CmGUj#1Kvi$Z-p~FA9#^uLt9uHT<^*IQ6at*9Do?Igf<4M2q8d_AlMgvFaL*ud( zcu@qt0XW;}_e0wKIB?Fne%#+~xyDx;Pp$#h#^W`@0b%v7j=SUjQSgom;7R;=jWH@d zzt)16E{WD30z4|dlWV~*1|HPE6921!4+?0XKmPl{TU`gqG)}&C*iMO#k)1plNKT<$CR>k)mq-xP-PK2%iw_t%0y3p{Afm-_eCf*%APHP+?+SeXBE z-z*I0{j@0mjs@N#pg+a`F(J70^Ag}e=bCyvq#WCMKQGFUUA5rP0*{K5+$Ri+gWNw1 z!+BpZ%D*J=s5tcr!6i=PfY&8XyssH`OxXgwWki4RerVJ=(h=ZM=ia=J8r9C^vS58_ zXD8tN9qx~_e>_YDFRGmv1Lt)Pza5!(e;$V4U_tX)%3LaAcqeQH#a?_q2)uJZU4EZ) zFO5E)w+0?HM*9Mf>buFnI|TS4?T~xb{+LKRo`gP5gKEUg-;N{T1sy-dPT_LP!n9PY z9o{Q%7{C*Mw*fDXz~vshzdfSQd+#kG^ov#mj}g)5J$sx=*Jy{d^F`pZ18^O$l=Hyu zE2HOz0l=gBU7i^TYo|O*5Qg&^gQ&S_Gw`4|m7;C@+YLNu{MQ&4;60b*z8hDidlWW1}0goCB^1Mr!|9lQ6s{UQI*q7&L!tBd) zHDNfPw~2~h<67``z6K<;M1s)YgK35&p&gsB| z+9~!g2R=HY{)4~=MBpWO7$e9(iK9I4?e8al9QizWR67=h)FbVX=g9r_2+rrrgZ3Bk zb3gF20@~^Klh3CI#Y^n01U{t>cI5f?Fn{H__b{B#!$-wyIPew$en>p#0uO43;Cp~a z^?T-q=zcE+UYGqxp5qT|C!g=HjA+MQA$~|Zm-m*!aCx68 z443zu!f?L-6y^W(z@x_Jd%%PC)8>c?|M*^2Ib>Moi~et#vNp8$`V z4~p)N#)kqAI<87Q7{r>$_-VgQj#819A8Z{rh4ZJS%!SHR-^8w#WT^SHBzrS<; z5Ihd0-M0XrQ3pGG|23$e#ZDjKb+IGw&xXZM-m4A6<$YT}E`IPm+#o;waTy6-)ciIc zc+hx}e&&0=QTmtHqA%|U``aP*`QC8UIQj^9w>tD2-#bnQ@T8t&fu9_K%lpbrpXYX%{`y+<`QCj+g#Nq0gU(MR z9(-SaW`Mqq2hQX88;ySh&%!fXX*K<{(*Ziq>Wj@=Yq2S9&wMy~AK@ATePBK;He-QD z&26iJUlgD(eJy^-8UgsW z{b%X7AG~+QJ_omB09Rte^%|-^@kccvReET?>CbVqey~2*YI^j4+(*V?@R6 zJK%MR+k$7zx(oh#{Bhd{-bn%dBs>|rXO6S<``1uqm-x0$xz8 zrOvy6*L8o%d@g#t%6cMx|HT(sXCw@l^+&>RS(hXXk3DbJ=Lo}P9g{Fz);9^mW!;l7 z{8`i=wLi-`DPj7ueo7cF>#Bs|vffGpkYVgAZGG-0@`PvggB-{QJ8b=|irUyAm1 zCh#@^ZI-smnmB%61edjP{J8jf1omg!0mlj+H=T1Fu{Gwk=<}}2fnN~dtMoJ1>Dd;5 zOaBS)9`NQx@Wgkn%QqxeH{YA@HER_4;~;I5wS&TNSyLzsm$in%a9M*W441Wu!f;u$ zC=BOXMp66N9^iG^#}4nS+dkIr&FFC}Ybu5LD{C!<;j#vkAD6!8+Dvub$NKD#_Epw* z^4BF}L)Lu?!}q{`&^{`@a-FEU?qg%$iQdQ70FT`8rGqia9_ z?w^}vZ7%;<5T4Y>wY!3TAGtW{>laX;;8#JvMFhSbc;g8CS>PQaaCc3y`XHq|8!GXT zwZ>{|C)Xlt8&Hqf*#P|P09@iFJXy=Ew)$kPGe1w_CTpOD;j%WG9~VEkW?E$({LMXL z={0WrcBEb657%Dn9$-)KM}e0G;NlP0Y>V?HpkJs!FO4;ufX&o$u+1N;ztD)6A+ z6M}Osx$+48r+`P@Tjd&bJp=Up@l5|bI3D810N_!6a*ewnKPBF`01vwE;rHX+TKs7I zMRYtXfVU24ue4(n@J121tkLHmf1)pI_l4nH(=Qoee>?D&5jfWZ4En7vag2Rgw|g40 zmSC8lvc_N-E^7~l;arn2sGZ{HC%`KM+Nt|H)`Pz_#og>_SXQ9vi~IT{0RT{0?&)UW$nkX{@|LBEh6+s183ia=`R5u<=;73LH$2Bf1BJj?@gXUowkFqwVzy1FCN7l~t^Tdw3rl#-s?zbar zZu;%Wc;Z@|QSqJ(Jm~l@`d0yO6%h|v<1?&&uKgKR|B+hi&;Kr3zb)`6|A*J2Ke-nD ztAIC(Xuqs!8rFWUbsBU$lK6fNJnB5N`S;QFa}Cu25&kUz9%cVl-~%J{4*~BUffxM{ zJztFlJ}g529^gU8d-f^+xHjx$NPm3ofcpZ+{uul_QT8cWU$%xV8x^Fi-@)}~M+MX= zeb)A;XrH-mZC!nS6x@yhb&1ci_HAuGb8Xz9^CI#2V&FmjDD~_DJ|n;n|9m5B@B00b zdblQUivT;)pR$IsUq9$w)SqB0YTx6!zft?vP~cJL=3FoM;Q(Ky%@UjPU-A8jI`CxO z;TmnTp`0?lxGr&v0DBT6t}lIy3K{dhYkHZ=tS#xUQ~E>JJ`TfWO=LeVZI`u@{kT6y zvX*jfJXvcwj3<6`E#{yYiC-VRZ`QmHvm>!w{Q>sHI>vGz>I^!zi%(n^dO$$G3EnA9 z1s;pU=QDxt3ecBWik<9wX8lcn+XR<&sr|ULnd?<|vgS`XA=U8sK$_U7zge*vZ=6{(2;KvZl8m*T);*z9;K``?+FE z)`j=uskVf>=@5=}wTx5Y&i$zFT&_JnvlhG3*E)hkCu|^*Tard;YohxJLsW!@59CR(6ztK|0 zQ1|v`eSLonWDLpr`(ZfO-zPT=zZiJbaZ1+h57Xy*{>Mhx|ELzcMF;a8fH3>=oq#Z0 zz8?^V^Sc7WBmDaYc+fFP;>+(5+!Zjc1Wz1;^}qwhu;BcTK_UWg-39BqN8s}PgRu7T z`v+0|+X8sdZzAzezMBwcU%sahhVwfMQP+?j1^#YC{MUC^*46-A_LJ^Cu&)Nh$A6x~ z?=ox&up@KHR^SP{=6;}`-}5~aCf|k#YqNYa!jFs3@+}EJu6<6qjws)nsEsGzqNt50 z->RsMC*QINp`^PCA*={48kAl&b$`1?5M z_$%L9sliP-my|XP3h`0!alnJF7f8(IdoDHVvf*^fJ}%#T3FAq9{9a1}s(zl}%Ya86 zr}>?i77_Z7*P<`qhY71+z8e#U^LsKueJ=i20*|`(E8n5<+n0FD_j3HW9^)zJOY%J( zKTrJNcW$EMe0?qW7T{6+Am7gkt6#pW6NdA9J5m1q3OuTxlS`uUKENv@#>o=kQT1P5 z3%&t(RD9$+LSgNf?+bRxs-GaumoO|*+dxx@Wo@WJpqvGIr zd`nFQa0BiarZ11)4=aENjdK}y@;$n+cJVuPQP%>W0v;9jgTT)S=rh`;>aU(~q&8F*BDo3Aq8 z3G~+^_T@W*VK~1p7|QA4+q{WLSMcc z>5r4NgWr>!8ldkV*O^yD$Ey?Yp!rhTF%NjqGX;Y4dzMlA=MLae{kgXm{7c}20@^9{ z_r!NIqwJpvJSxue9Zr9o#lC!>GYsc8*e$jAA>U`M%@6s0tDh(Py?oczkNexh@4j}AXwMelQNIiLz1Wfn z{R6e=^EUkRZK5cF7l<(L2c~TF*YdbQc9hK{X<0R*c^MG#+&`-TC zEOj-;w{iyuaHa3$+q!;V{e36j-u3guj(n5XkNfS&H+%g&@t5E7jq0OEfk*B8dw>V+ z`w~0(MzFvB)YwUzc45WQdVd&p5SpAJ0eo{GeY-%(y3Fvg_sg_qo*EL=jWaXs13k+^coZpcSim!~x1;ERK;u7$ij^Cml z8d29_;E{97&B4bu@rB>Go*ZC5b^nldy#j8~d6>kH-^tz{WXncj%Ko$M78UtC>d(M~ zj>qCNzr$TupRWQp$Y*Jje9PNECIpvne23xk?e8#Lz6tKfr7iqccwO64@3!E!Nc{P| z@y&IJ|J&e29e3q><$j+fCj5?h)cNLc;6d-7ioSgRJj}j)7d;G@@1=*~{Eqs7fH+9~ z6}Jb^BNE5az^@L__s;|Jo%S$4-RtrU;FsVIs^6wtySeet!2p>Ue1XA$mNP1CO#l zwif-Bwcy)hyl`CTo3lspve z#}MFAek=k0e$aW74d-xvA+aCxhzkBrLcsaeE^x03Xq&{Q@1xP@@>_tnh|qr)cvS2U z0}pDm)L-;iw0?i!QTp>j^rde%0q+~&zrSzat)-sK$D{r13_QxuiM8mj0e)hDpAx6N zz=O^i1^>Ag`=w7r`!@=BP(R9;TMB#(#$k=Qh-1$8oc-HRN6$s}?&!Hl+Oz|_pf-z7 zZKo;gy`cFq;8`SlR`fl!_fS_+K>TGKeFr@1TCniOJ`?=g$UojTffsb1A#FPhd}V+? zspk*Eo&9XcaW7>)5bhChm(6#s6WPv9S<1m>9k{4dv!X#a{5sxy@#SqTn@L6I*E$0mR2S%*dceF&e@i|iP=l2TA%U8>#D|Pj%DK% z#X5SjzCwZ7*gAT~YJfI&1gqsKdTxD*nUx@vXo1F)+^fPYu z;H85so6cu{gMwC-iXPf=gtyH`8j7h{17z%mNXpL8rki84UGMpz+ikenhno$$krlff zrz%#WAGWQ;=}1*#3euRBF%KfYefxS4nWN)zt9>~)+5X=#*8z`UAZKSzVy@x}olDPL z1Wx;o*Xvw%<}&6w8NFP%osE1Beb2ZGTj}Lpj)lm)5lMO1URuY>{5^BsOwP74?nXAh zyyw*^P&Ap1F{@N|VdZ+(DDQO)l56A4ayriHwZy^lixM zL6Bo*?8bKUitbI~j$0KyjFzdpg3rykjjre=jb4JV{OS?&NCK4Xsw)DJJJsH_YUm>P|Je!py&+wu<2<*WAiD z#CBIa`zKcZ6`|)0WMyumm(QD=ZRLIouxCXhHP^T;bc1*mpeK>_B1YcaJKf24HBQZN zvOV+05Ix7qR_0A+_FO01*UFuZd||~mu|D-A%HPJS4UF%ctey89o%-I%rsrKmOFuZ- z?7WwW{^(?L^Hvc3$;swh8CN2o-ErtHRMYTUY}*}AnT72}8?fE+)Kk$_joyHTjw1@O zi5Y~&BZq(y!)&#Mxzk!YJMJHt8)b57nLC&}y@AH+Wj?~(Xw!gnE8}Tob32Yb3dh=p zFJjy7c=iF-s;e4Dnp0KP?$oNrSF375wW=moq*gV_46<~~eii0BwOvIjxruq4es1kM@A797Tg%J>$k-L-5nN-`Dp(XQ=I!6w>nHMVC=w$4T- z%f5{KG@#0EL{7(}VJf2#n|8Mo9u(DX{Y5q7XhPjHUINr)BNEudOG{Z51M|K%>QtXe?rKcXx(nd&( z{YfnCdF<)cxifX?p>tVE1;bs+(q37YR_9)!OMBnQQYskk?@?NMY(X(jZCoER??U0R z4F>22*qQe+mtbr$p=Cb8T+X!`%g%h3xm**m+{}H<2uX79)2mROYe=`pQD*T1}Fx+phh!_5( zicctpMv}$J3T)!9;0tYVA@(&@a0E0}@U7lXL*J-^zB>JnF29=FMSo-Z2r4~lq#d`? zi#n(#@19_4%{v<rkx|r9sp0_xpp@)|lvQ4cK&gTPv{e;M(2yjqwvTSt0KJwm2LH}OX`&ahR^mn|tJH0)pf3PbaGFjx z6E66NPM7PF7j*g=)1n`kDi}Yi2M*VT8CR<&+i|AL2IP%HNyb{09HaAjvzWa`eK(3( zJMVmE*D0Rx##-jLP^*}~4ndp=zDi~xP0mAtzk-9fC9DeCN)4%GpPcBPt)akK# z-2POj=QQN@0iB-Nh}-{WT6A7M(g&(}yJ6PUc3 zH&*lRW|MWiGdc0j1w-aB{cRD``xY~OgK3FMzW_hg)-6PO<})Zb3(h46 zV*n)i4P2{4{Zr!&mS7fr7Wf6k`GYITpTNMo18J~sKGF+`-$YzbtE2 z1P*AEa4!I1h9>ML;Z_p3mzjjSCI|{QSqkob8L4`j1P(fraHPVKGcG}VPe{KJVkTqp z0a?R3ISDMh45Z-R+m-=Sn81=83?|_S0^wUgO~;PB6og_Ds@mYuFebqEp|t7PZp(r7 zA~vTjFeZra#VBq1+8U(2IxF-)ac=3vYNf8jq}#MpY%`TFB>3PQ zLfulGpj~UFhSb+4X08I`DKcJqL6V(peoIxdnkL@9421Wq6Lfr9sTLk%U%YY?}<`ZOK>E{*L3 ziyfurk)^wgV?OEl+-EI*%0Jxf8W1+tBqUpL__kCp0B`>Gt?R>R@Tb?Qty#UDb^B zV0=)Mk#q*g&J68SE_O=YzIr%Xnf7U)oUWJxxcS4wu7|DingsFbIpEFTzUv0CPN~U) zPw5-9Pea-G1vME--LIwUN#Kb2GE9{^PMb*ehBmhl%_cYLm~X$?ve@rxH(A~Y;1*by zW$O0Lyv6C6kI2$JliI#70XKhm@2xPCjRUohAnjXl8{0SRcCb3sWTAcU147J;wt_LL zCPUhH8m=c`96Sb^_IwGc+Vj+Lu;5PmRCzaSZLUcWpPFo=Pw)HztY>Pn;M48*XrG?i z4#p=n88Qy8zYo#uqJDS3GY%33@TtZ)-wC+sVl^M5UXPZp0jv`B9N^}IH)8(Kq;WM# z#`_YL`A0BkzJpXVSJY%;Ch55oq@VdIC{4PrCP@PO7C3P4>W3Wao1}WXA=P0o`(e6w z;1P%POHGovcfq4{@A$_ZW=Z29bFTt!{_{6aI;5dBN#fqRKWX>+?smrFyy{dvOO&Xm z0XHAK;yH)Jlev^O=+@{tphT5EPxn6D<1nA8$wX`CVn1m9^V*jk(q}bE;$FMI=ppbg z2)*0oH9^facDwnn=ti`C)nWArV3nu`05>1J_%(+#p(aUUeDv#V#37`*($zJYXvC7e zY{bSl9MX=OBpJt*`?P!Sz3EWjCe@o2^xRRR*6*i#1K)N?d}iB668DmSrF*X&aG0%X zGU48}@6f$Xe{)Eu)+C90v)PZXHd!O!|`k_O*ttLs_Yx7UK_sqW> z=JPd~aBuZTbZ^P04(aonB;%gd^pwN+uEaS-IK&j3flXC#u1;UkX_wDfa-vQ*==4XO zRvcmZew{A+H@8Q9#dM2KKh|l#uUYbEosRntx7X;j;2Un=rPE(@TK_wiH2I$CWj`=I z@FUZw|I75sUzpDOjcG+1&MOMO#O)MSa5$Ig=Z%^E(46V5#Z1eROkdaOi!HeQLJ8Af zOPL;z(->8-x{PTK?!w@-^_}*(<22+mr0~E}Sw$EbJT1kcB)42ySr5e?htTIz<-}%y zmMkI{6IBOgW&dL^(uLGLi8!~QdoBvIzKi{e>uCC%bULvoP>W7L*xOMP<{aY?t{~xM zo-Z*0AqxT)9`520?jfNwaykLww62=)Za0VU7ZN7-0D%bz_jU*2iP*o9)BYdvS23-p zCftZigqqL*;~>RC>w(9jeQ(d1fk0XI8ORLncseY%nT}0$=Gkrkk%3m{4KMpx%TXso zy-5co{7G(={acrw!L#Qk-Q3du=+ZIgvy=*k`xAz!N_5%Y95;Pb{X`r$den_TiyuV^ z9tkvMC#W56S@J0$018MRq+WJ`;@<2F^iC;}lNZ2O{)|wck?|7EC0UIShHVCc9j>xb z>JWAsLOCs*_NrLuOoDbu_j2;M`Y_W{btCKS|1u{0dF?RtPZvEHN>pDmCbU1!8Dn$Z zil9B1*t9c%sr5c>?L~Wa8X4zaMn;Nl+$hpF*UI(1sw|rUEw`VCi5zZb>CLhq8SPV# zXEITO?mtd9@_tji{;JY-FoxtxUyJ|xdjrqBs4%=5Zfx!o1?9B$@t z0Oj;XK(g$NOx)1>4K>@R_AKv&TUa~cdtsV-1cG^`e#2kIb1cnJWW%j^!Ru^c!8=Sl zs=27OFk2l&j**P3JL%Xl)V3TnSAF54FA}XJs(&+rVpYO=zN!z=ALViNQL2F^M2pnX z5PTj!uIyxr`L0Bhs?tSEJhYQKg=mR79U`tOiRg+^=M*AkYBmHXh`5^U>X{LvCzeiX z1$oD)>$HWdeH@M%GXr&0eln3`%~_@T02!pmQD=2Ci$1HbdV0Kex^To(YJ^*6 z_8$j$Se@em{5g(2j%G$Yudc2Rn1Mpygz~z2D2!6Aj(61okLUJlHOfEKcRq@~X6^X$ ziE4VZwA{GCE;R-^t5JwR;a`>N>T;aPpj2&RK;Bmq!5D?MsnIS)UwXiyNHacGbA++j zW9Y7O7+`DrP>oTyL+k9U`6aTfV^d(2Bm5~P0b#$gQ7Vl zRa1j~W1orU@>EUUB`q8j`w<{#5p`QC$lFwf!(#REz=#H)Oa*zIDLgIKM8J1Eu$6iq z;D{-)GJxu50%p4Oc3nd$oE_^$N>R&d6omLMw7w`dNUZlD!-Tk%+E45MRAZ=mx=*#0 zdXK8t#?B!++eKmXLjoIPivj2}kSjf#rRo!ccgHRR$U~vJ&jrnl)Zgs?(2FCn2f@&TyNk!rJ94SgjBjJRg>kya za0a(#{2F^j80UM8N$6q(;#br%Vz7E!7&mzgrxwi^qdpPFpQ|%c<_*VMh5A{zM?9`O zF*ubLwu>kFI!#NoavSeBt}888M+u{w$4DJGX*r0|De;b=sna})Zd-}U0XpLQeqv*R zM{t@(LgBgb(}c0PI-^?Cw6XeXns8t6xT#IkCNGO$Ahb`b)6jU$xG}y)7`d(8#-}!| zF@4(=-vpZK=~49I%;S|R&3iNcx|pl-xSHlPRnrc~KNQ-!09v*7QenhQ^}Q%O=~GB; zv^L(*&g|#(=RX6uXt}19+0BKP)24dMYc%Uvy92m-((UeXbqJi{Tg)hpv?qwkvpt%& z#soEM+C+PLtxU!}f%9?h*!jy;;Y%|0UB^tJ)b zW%pEzJGDk!rQ-daSoR9iN)5iQW}jUs3RA)q&`Qny)Gin9h9GWgH)w_b+Q*B+vz~(P z2Aw!VOmiEgEfMa2g19w2FG+hy6pku$1LPT}f_UB|?F-=!58~GF{C8<>PjKRRX^?_< zTIa-OYT6W0*cqgt-A{FCZrT=6_-BxU+Yo8Pg=z1LLVmlT_F_DmlhKZ<6`p??u{_ol zf`@>(8t7?w!_8}TM|CPSu8CE-8jFK8yiVwqp1GsQd)9$MGvCv97xS>gPa4a3fBW?qoE*|XBQXM31M2mPcp)K@iH5}9?_txt! z+*<;;a8T3!S#P+|p7Us_4$h#1U)7r~w8I`P)j`rmbcfnWRu)O{upaXDq6@w*^bPEfobe;l zJ`69`=*S*~l5-j&H6^NhYnGf_;g-a@4`9j6p>B!YeIiR{O>;}qyDwqM>=kZFZud(NcBViVGlNktENL9WMM@{U%2gJ7A8s~+F7s8s!yQq*KH zirT4G5N8-x&;Bgxu1<&0*ZbnCUXSxx)=OOi!P^ZmR&WEhRWCgFiA$1vPHyB%S_ zkJD%dS3h*wDz()uOU?o!z&iCO576D`Xxyqk@BnI zDt0OEC1qa57`f*T;IymyxD4Z&{Rog#D>)2G6kkOcdGINekfx@PyI8lZL~UWDKY0_F z{?)tM#f+U2bsq^|eIkUreS}i=3!stt${vDu!!X9wb6^xtc2$;5r_1g6EZ}CA+2np!md)!o_SpM?^-+%^5car? zlbtEBn5D{G9);3_)L((5Y&7<+^rDlecg177&q1eXC^s>TGuuM9=oFK)Tk5l-qT!uT zsD6fGayDo>!L|6)NF6nnSQ5p>R8mQ%Issz^8E0HcMt7Hi^6e}i^GBAS=9U|!_Ua9S zXCEMFOtQUvqO)A5O@9C~s{Yu%mUVR6P~2%5)d2s#=z-@Y|ALlSiFc8z#1W*i#IH!> ziM(7yB~gYnEzu8Ydg5%PnTd0dW+g5`+8}W?(wxNYNOKd9Ak9ns1!=>?L8Ofm-ykhW zWaY)J#)%@NO%mlu3ll?-Hcgy`v?wtPX|u#Cq|Fl>krpR*B26ZCBRwkdF47i>!$^-# z{D!n7k=rnCwM?`_TADZxX{*FAq^%RIW&%`rG zdnNWEJvQ+v(&G{;KWMl#JNc8B`!c(KXDn-^uz|F8Hue(GZPOY%}P9jG&}Jc(gun5kR}qJ zBF#yBk2E)t-k5t}0n&zv5~Ph19g*fIDv=f>h9PbI|84Eo(S7Z<|h7&G%t~kx6>LX8Y69# zXoWOC(HUt$q7Tx>iQ!0_Bqks&Ow2~wG_f3MQQ{_~%@W&?Hcvc`v^cRBX)McO9O4Qac?Nl42RXCbXfoQt$$;v%HSB(6i+IdM1AE{P|Qc1^s3 zv|HluNV_M#L)s$|Ys&ef7-_FWN2JFljz@Z2;#8!Si3v!1C*~pTleiS=@riXvcUy_u zA)c`k_d`5uC7y?P&Pu!m@w}Bd4Dn|x@gu|@D^b5FZoOb7nnAp1B|1R7WF>k+yo}q_ z5Pz`}lObNQ5(^<-wG!(fUb7OLAzrrB5EM|sR z(cZEW4ItjO5=n@^T8T~&2dqS2hP?>LEa8|E#woB>mlEOybkgRdx5%MI+jgXTeZ-QJ6xe4+X$eST|KwgS~KMlDY@-@g6kbj3<3HdqX zD#+g;S3@R}Xb0p`ke8xsj)Pox$V!ZYT=os>VL9X?NPKYkf203@qaUN}|I*QK6xJ74p;GoPh1j3T&tCzZ@s3JhAS^+*_C6beC^e_hRntnYea|*U#*M%$0OI z5lW-D`PY-6A8VOyHLF6lFzxU9eY>=S*EzNplJx#XI+vb#5py3HEIV@}d`bJqJdNe! zLA5J^s)CI=y_0?x+^x4CuTf_Y5p8?kT}3EClZp{CknQfCcA_gv^PSf{z3B9BQu3x>Y7 zeIr@g6a2t3k4d${wQ=>BPg{E-k4dc~!>OIN`BQ5wej8@l?<1GP&2n8(t_!m4TM?0R z`)7jO%+i}>6TwqCr;SG(ny?t--Ft4T6H1E-r93w^*ey#=#U@@5l;@^2Q3gSlT_0$< zJrijTH_Hl5m)>p!hkIq;%;$ZdvjrAYPX~?*hIq{RW|+S@kb6_H!%w2vwy^ zXAH+}a~9V_;t>912&PNl2SHmXCqX+D(50hBAmqb51~sa8>8oJqE#a=2Np3(9R4WZCDTz2)}n1i6W4)v#GM-E?Ue>{Crx zT&+vvC@m(G(xuJaGV_|H*QJ{1b?Lh>Uv3`{dJZ?U?2A!VzXeFriwadKCP95^t*EOa zfO=yc)jODZih8JyrodTZfA8aN{a?6^*2vVkI2qy5s{OX`p!k1HnddB0W~}EN~9_QJB}m$)u&V$slFoM z6rz1YU?9ux1Y!<1`|;h?^rFkvQJB!0u*9_SE4Jc6H4M*;((#y@P17W)q~2*e<0Czw zyrk6S9^yEwk^M?-_Yi#*mWpN z*CkD8rJoBZY7w(XqsgTn>Q;^`$~ykX#>Ro7qN%aWGjU6fX7{CPYH&`h z3gAlwzev^OlSoC2V-JaD_DK>Cc&ounW9er(nq>r~oqTksXiaP^z?lkW2d092hN9$HoKXI8r}(HRFM6YQ7MANHm)alIo$U!8c>+V;#+|1WhPf zsj(<6`Y1LHLXC8_%ouN_{!H|{*y}{kby3`)E|z3o)X6I993L_c>R*4EH+KC@9Y zKi;a!Vf3udFf?qP6i)h|Y0Q08QD-+v1hu0jzfc(-KEl zQyz%lOUmN`6!Adw{v0o!0N%%8JVS1&)`QjT{+r0Phqz7Fvrqal{%n z5#?`=LaKVXbL=NI=V6bdJKF2TPOAH4jIqUbe~6fR-$&GJCOES6p~Z{r$22jH&T)1y&C#{? zR)fe{WtUH(u@sz>K2Pb(?eG5$dLZ7Z z%r!3HJC(T!uF$O7LB?&|N-n3WoXUI-!uc$I!y{a72rwd5HgOF!YSpjV$endn$9{G-#jU)7DatD~KmV1C6%X%@1c4{2eW~y6U)Ex{ni7YsIy3>_^avA1yv%OkO!s6RN zP)?2d{a)RJXv~LD$SFXQWiu_~Vi?)EH{_j$II@7JE$!558hKJR8wR%txo&IVMb*ax zwA<`qX_=-RP-j%9AQl?u2NUJh}&G=Z7g2(BU)2JH@3Tsq}Da3 zD!J{|@iWlg*FDtr3bQxQMsiP4SAtT?-Sdb`6L7S817JIX8K*xjF^0;B(Qr&!~f9{Zuj}iabloeOa}g<(wtW^uU(-q3Pli=Kxbz)G*7Z zpL0J~j{xOVr|u1A{GxsYgN~|KUE5}V=%jAo;EKm8XLBY$;)C_G&$*3bR|C~GG#F8> z^_C&DjqL?NS5QHMZkegToNeWHiRI66Oq}eRKwx+(mU~=m3_z_u-Bkz36RO-3Vz-Ot zs#Hx4o*er~G`Dz~=CmnyOssq^!nWJhN;-YT)27@xu_Z*`aZ%^Asrc;YP_B1`-&`!o zyr}cEDfik~_w%6M2G?Ai89-CK{mPs+70i5vDb;->7tf7btt~KU_QI`MHfgJv*y&HxQ9ANbH4I8zLpG7 zTM@A0A3xT-)Yj^FVy65zCdP8R7#`)AO{Q78z@E53S)>Bih7lS|L+&D>;@%PvtnF_1E*@$2y{Ql}$Hatk|yhGx$N^anz732_V1pQyF) zX3@Hmkk;yBprwBnEu#UKxvW!`{Q#Ib+$`gD>gM$u*B=e<`P5DQLOyXr@4tj@{UVdI zGrwi7`Q?3yrDy)cT=F`d%g(gUN3Mm*LSP6m3)}0a?X4a2=}r$4T(c|(h$r!^IKw} zJqg+&GsRT9qFcTgBkdHA!8*<1LC%>k1LJ#CtY(mnd%$?7~e$QeI5ATX@45h_{ z7;)!K*k*27vL`lEuIOr_6K$A(2Q`)3eZa}#W?6sJrHju>>C#Cwb^}UMx^$^qmb@8= z*QFa=z}KbMAQUvK9wFl{ZY8gvs&wg(AY9Di10LZTLoi)Bd5L4;SGSIUE`1Fw)dG6P zlCDd}bS*{DpZ5CwN zC&1fs`*wod%+i}>D@>QBjX{^PxLTLKMF$9_bm`}AnO^fPrAsx@>r$>OhaY0l&f#X; zYgkj``DHg~`N3$`*cb_GG`oG{4=+Na^u%3ESS2SQ)d~F_i%bqzrz^Loag*~$mVKvg zD6xJwW3w>hBz0t>bG10*kBTsAh?xtH8ROIqsDeXG?Sx7(seBolmy$Cksrnb0u8pa^ zM8DUlxs;qSO)UZ%Q(w4)FnU}f|weRs@s6N2h?Y-?!;7GjlQQwtq|R1sk-^p{Xjhhl<|+L-vdqRICoUV z1P`gcE0x9J6;qF=fTaXKQM&;$?lJXOg8Eq6QEBjq>bpuR{f3}E26t5D)cjIC1JG#Z zo$YCA@Eg^CwP;olbTqZMKd9#c8gEZd0kyY3tAUq^x6@NVeM(@(_5h@dxB-$i~`B(%SVfLU-`f6E5Gy7xk;%d0Z(PO`}>c-fX#MBfQ!H<4j*ea$j zaSP3gjt){5Q@2(}%pu5uJH*tZ9vq*9eXO(U!+Ltf)SE8iOlRiR>Wp7v)34;o8|I8} zT}o0bz`?Pt0P7>9YG|q^FCkQqf$CCE54_AbB%G!Pa8RKL3 z0o0m*Pu0}mpEHpL2b;hH$(mz}?{vI#84%UvUPJzGtS)1`$ z{2rh>4*fg^XC(Y+)r|Ib!S&Fe?4h~}CPL|H-$cq~9wi4MNiuPM`etm5)xQCh%`OEF z@TIYg{`MRK49BxBXb!dx;|%*9F!aDWM20?`2xBHgI?Eog!D&Q29F(dv=8>`3-T{WL zr7anz<8(7im1eB5OKx;(8B)W>#k6sYeFGTU#>^TvG~<4|{zk{f+8Q?2(8hE25-^Tp zYj@PJp&75*N5E)B#y+2oZfXN<9I&fylJgUDXvO)**Wl(XMz>|zOv`P~r{%i1T$g0& zvz4;;FkF~FcKS5@(0v_d@|Tj1QOif4nylP+C$vzqRf={e5K zWDa_#y3|7#Ko4iKyy}^M-~bF>LPJ|>5P0`B|AV%hoO7Y4NLZ!o#1Bm>A6^bjhx}bv z8HP3XpMAjeu=p-jkCRdUe_P({=rx?|w!>Tq&9~Kg0JUOAf~G>V21I@XwHu(GZic36 za-zy_tnzMgrW?HK;%Ra&%0Eg?0;n}tr)p}jt=cJ?ccf}^qRQ{2V!wAfbayJqi7LOF zIt`%SLI0i#a-zyVR^1`sPpKd;IOd07DO?i(GYKaAL~Oj+Jh4T7Fv%2B)3; zX0gR!=vtnsQHy4@i|qqL*YeLAHaP9%ca3$p!?BTum{j*vGy23X14G+rLxvvm;wh({ z{1LGa!O#=aV4sa{N;4+MDz`clQ>s2DoL!h|a2p0;EVq|HImc`=&C&(sHs_l%)^BJ0 z{gdff{g;cOXOt*?w%!sm%oA`T<1@9Jb(6qZN0)uB-f_#!xw8X&rM~un&!K{O;Z0ik z_D1;0L97xeE&fqkD^Y(Z_T$IE^p%o!sn{pPe(7;1-q@J|y@AAx7g(5tn5|X=ycQLz z=`Ltqp2DIbWMr$iM17UZz$nyUBUO5rqkgvwiaOsQ%V?(NgP~7WULr$#oy>$cRyX|$ zS;kT71!1J32r=C%x4(f}4hK?}{Tnw;sYzK-M$FFGIOrT)Thrk`pwGD({a@LFt*Q^I zD<;n9>$1Dm8E%<5OLl^MZ?n6V#cNP%J_*CLk`v?yrU1K>*fpt`4&;Lm02@X2y{=(& zoW>si0az}I)hjM$f>dI{_-wyzj`Ali0m?eWFRcNll`~-7v_h11gx`1*1RdaZBxsM5 zdffCe0e3{U9LGd<+9Kc8r_Z(#?HCYcb!|7+zAAL z^X%q*F5tT?GzTuwta^=%KXEI$nW}PG=m8LJVDT3o;VwfkmxaC*3k4`O4h38m>U0k# zj2<3?bymMDv;Yh}WuHct_Qbs`w3FHkecIX!xh(Vp8BXoA%>W*w$L7t*<#4lH7nIv9 zz)OT^4&I?7$W6Rdht0Ac;NePN^O?9v!(yDLcvt$4fKg0{5qGZir6Fs|k|o$oxh$lK z-esZZpj>XZ1t*7_Wy4IDPMnp}rKix?B`8Vh(y4A)aupD-OP9HTuSs^goiMDYVsJ^q|0I4ZgIm`3h8Q+eFybtj3-GxV4=Uwl%A9OQF@Pam&KCW;O0dXm!P`4cnU4xE@dFW5QQ& zg>B7h+%3>*+n>U=W;O0xXIpDRsJv!1?u;jK3oDx1S1qB9OFg^QxCf!st8qO;O0ybQ zd#&|4l1fgVXachucLkK>^3~N;rmx0*?krb?EX`_M->0nQrjVssjav_;&h6fi(yYcc zecI;sU`T0J;}$@v*VK-_G{~)4jr$8q-88MKG`TgaaigEHmVH8&W;N~|D7EFqAxpCw z*JGXX_??u9x*1A+q=Z)EOt&|yaZdxxMvSOu12C&lA3<(4uIYNN#_bC*()WYaxL=(n zv>I1)qVq3bg9%pS^3yeXng~|o&I#1>aiE7*<5Eszh8(vVw-7m8%u!*sgT%$yt;X#K zLassXB0?X5ZZ)pm23>1vvopteBQJ5QpXXl##}kx>By*GCCdz^-G|YoG=Jhrsu%%gnTLz^JG2ewP%?jL`)-q#? ztqJsOvjSKBC1ZI4mAa7nwWU~rI}=K2#52Q6vjTS|l!s8CYFJolR^Xnp$}7W4vjX=E zl+{?BcTy>hX^$1N0@vbYGeA8TAk4U8R^UbhAr0_VfM8k=KW+u?Ilxqvsm=`{W?aDv z+)=NXBAgf?LgNZn;Q9ig&JVQWLP*>S+`WX14j}AP!3x|yYG*kueD}nGiWRt=SIz8q zCzZD1%?jK=D4#&DRvW`gvjVpSN@hWQt zgjs>>350m*K}1?3nH9MCK!}%-l{h%mUAj79x^P>l07SmCNxVs7hJRFG%%o|uAwOFeyze441=syV^Gj|vy zq3c_Ic}vUUBcTbdZ<9_@;rjL?72Oh+Z1%FIptDd{Ut8Rg}uIAOGHS^3gmrg znbE#5i!qunf_xEVMr$H3xc`evM)BE9MDxC2ca06$g?J9dY1g+aAkC#JbbY(d$@C}p zf*bk;7+l}-yJ7j!4-m!}&5ZuehW!o!Zs>pHDy-`18~Q1RS9?Rhw@Z1%t44t25=ZCe zhW_@Ht)omkcQ^D82o#uIJ*zc0^p`oU>kRsa{(f*KO}Wq^bQ{?l`fmdO-#!qJK?|ZV zPeK&?Qp&|Jk6$yy4gGwGJBI>Zue%%i50ITImu+)He{8x_NVeI+Ht*(!{^scr)P=dB zzk8tg8q_%Tw>R_`JB=$YC)V6+zDH?(Wj3yIh`89g8~R(mf%4uJXkD%_JenK&#{-h! z;kntttg=u&0%elQ_o@7ozM$j`iyQh6Sj$6UOHqzi&EGVZb!LPtIg8?k{*h41hdNK8 zQfd;kqP#-g2W1~rzv>&dgT5&x*6Kro}~PRFgY=5FY}1qfeluAsIz z^nYSK?5p6x-q7FZ9pm8N3JmUs{=q;@I9;4+Yfcny z=s)v4nEz1$5*%0dhW<5x30r*}G-j}18t(KB{YURHfUfBP@oBalaYO$!z|_zH!sFeX z^x%g6jnqzdT6aVLpOE`%xl@Gtth=HAoV~Aum1t88z2|D!208yk_$WI_a-8wW!3RcZp`E|+3WbfsNMYNg&>MCnkmt| zAsioL1D=LBhvKwpeh$4-6`JN(IGO&+Z7|IXFqr1~{m}gAR)jG|GoxeRDsPxfhkce@ z;gHPXt0;8)#jw>`dNA z^#WuZkIDNS%qk1j#ZXS9@_8yhrY|VXG(XQ;?h9L*X?~@({5x!Erupqq%6GTxU2AIt zv}T(B0?Ox6{i;2c)~A`~tA1oGFAQ6nX}&3xVmUr+X{PxeP|7iReOPIx`BJNVC{Vg- z{u(INYtEk@5#)$<)BHn(e47qIiOh3*uUPhK*M&-CruhR<$~50JP}*reYaeL4Q?7fP z^cek35=`?aLCf(?4L>sCAJr2GW>j6{xb^Yiruh*-;Qb#J)OMO*XgzGN;K5Gw4_gOc zR$y?`{7aT1GS?OZ6=SCPFRl4;RNAIC(|pzarg&$BEzLCF6iO*sBCvGRd=Dtq6lbL$ zk2yh^X?_f~i=Ea@^YbB>1K}B`i!*J_%{2cG&G%P;1jm(~=8yZ>uz5IWO!JY=P4goy zpjkRVd&10tk=yV48oO+M!PCrupw7_ti|N2=!Sv%{Tajn1=$46gDV{o#rnQ zT&M`{aijY&+~r5#0Ygkj8D@&0J1-8RnZ5iq*JFP(`*=JD*2m}7HV4u+sv!k~eN}Hh zDu)n-@lJ$AzG~xy@wHH)yYT**%#r?c$cdb__9Nn1F>5L}&}5GBkNwnK&W{T#c>_mg znLi!MSBanNxIrt*N&bgWeu+Y;RZeN|f#EG2nN$7FpBeL)9RlW}oax^JrI>%~l-8W* zelkRROuVT|pU8QspPmg?|b3R24R zy7NSljehUKpwW3Xo!;m*7od!Ugd6=tCyYM?RnX|CeQB-}&LU#1n7Kw@4y9bzr@~6p z=!c+umiXz8J81NQUn%bcmh>*Cw5?_u{c#|~e6vFYjb7_(W4_-ht+{FRu|UZ6jY1Z5 zzHO75M&AL1IA}tIRER~GMsM+r@p5)04%6uKfDn#Rl{ieJe`7i3R^V`re%`m(JB3o2 z+C>|)X7ZztLKtK9W=1zKs-+C`xax>|<{B;f-}lV@Mt=nGgwNbFSMvrd(>vVmp1Ed$ zqA^S`O7fVm{`5S#nbZFvLUJAN!eY65<~kiP zh_IFi7qW%>e}Ii~={GalmQgL$Aq9EV;hwoG7yr+D<|YD`OFe7Hv zn~coxZZ5TwfUCvG-ZS?QbaLm{wy;hr`NAnju;Imx(&6y$0zF`#ul{Ah^{1i z&s;YE#JG_Iz*y@4m5J8)45zck`kuM_02qf6N0ksDIgU%evG>e5yoJJAOnqQ0cTxi}CNE zqKk$2&5!IDE_o1o10=^!AGbvsc00wA)z5@QQW!U6==yQj(E8wv| z;bi+kZJn?h3fqPq1zT6J6lASW7PK74UOJgtV;C(_xSq^+A^z z?TL7df_xF@ivaryQs*P7WHd9%Xx@Kx&ELNyRNaf9$lVG_sJb6=viQr;1l7IGDJoQV ztDkW{DM>yjVmD*)r)g@ddlLYwD6WFQ05)lWuI^sH7>1SvNQRD3-FtyjeF6l1rdRja zUrlw7Axd)Ms{0UhYJLT4$%U=%`MgJ)=@cc7UF-G%6fajkf!1K>2FP?v< zl2JVWjA-8OnkXrre_o1fW{NTE2X{sKkmgdw8n@3sJ6XIVqM=*p1ZZj-^m{>%=bwRz zG1`1TJ>g;5FXVUoY$$SiAfQL{u$d@=dISjiOz-uDf0$lBj3~*8>-C>Or)E{KmR#6gKjlyMdZ~^$xmls? z{h85~NW~b<7eT%VGNTuvIQafGm5k#1*NEorW!uIEG`|H+OL1DS{{x6zszSZKHj-Kv zZ-r>6*9$P{^}Aqz!b)7-4xZMqB(iy&T2;D|@#jVrdtYz&nXh9Sv z3z>*@g;i#hVcrmw0xNE(-HCdn03`}G*3JevmnxTSv*I>7P)N4f!Zz<_#cftP1a%>b zZ`7@UqC2QVD{fCYjVrERaeEJ?>CJ4s>kx6Vbt`VE|DwEq23nUZoIje4wVwf5Rv#qC z+-gf@m4#~BKgzqE%C=NqNMBHz6}PH~jb;C^rCD)X2&Gt-g)PmBTh6~2V^G6tUf9yC zxZMNgFe+DuEzOEsE6>-KFN7`4irX_#isdI^OS9tEUHSSP;BZ)JR@`=2Wy9NCIo*oe z5Z}i@PK9pspc37R+c$(1r$bOtX2oq%#JHXsug$uoWr5PJxGjKAJz0sC z*K}BMt6#T=>YK=thl`bn0h*Z@OTea+zM(zyUS_airaq3ef5h|NVDpK zxD~f4F=85m&$O%*HYkZ*ach%JTo@zuiIFk~151ANTrk9Rlwqa_y5EDEtCOYF;rp=o zG8|VcStlr-Eny&ywE7m5S*=y;LZ(%uRXO#iD4qiOP5el!8BDiPJeTmBL?W%`G2K>? z9%nz1R!f;~rw*Yi@b;`oQ*}QD+3FYYl2m=lQbq4a4E^abD(k3gYx^EOsDHys-rARy zt8$L=y`7vp8?A7b`b{HQ&DEn&asrO1&eX<)S3ld3m9Ne^+J|O{)5PVZwGBSk4(6Up zT^nema_ho8-jKoZ3{FIV!Cuh)lzQxcqQk&}O1&BY+mJ%vXpPqZ|BC!{ghU;vfM}zh zXO>1QRqZZY3^SuNRU9R=#;I9#%uv;o5L<&BC9@`|SY4y+9#(SaAZwDk8%li<94PfY zaamJT>toPQuXJAH%pY>hDJbh|^&GShqQ9u+4gn=6lC1fvZ;nwu9af6v4eEU;*U)k| zl{UL#d5aoT&nSNiD_>(B+^Y^k$^N5i-W{^sOXW(npuSPI2rIv(a+PY{z$kk=r5-i) z?bKOo)m9+Jat6DE24*?+@PgFwGZVZGMxvi1m zc)JqfL?Y@(y5#yEPulBPs_Xv^gw$klB?NET%Q`u-;8@?gl=U&ak{3ao7dhcL-@BZMyDD*Tw9FbA*#d-k z*;;`^-`bgVeWc&<=wlxT+Ai#4M*C!>Mq_9r_qtND)NuwD@?PXcLXHn0=Yq?^E;ksArXwo#)))F!!p2s z(?MDW5VBZrLN&<0q`9RU5a38L)}TT^P*b>W3nMbCgv($qGm>GprvDj zF)d_98RpBY>+nXdrHu2kHa~h2A)F!bwqQi_4e%_A*^Tm}{TOA3$m9DV>*Ng0eh4?O zuY&WO;bi5^*H~t%oGS+Bn~7>ZjB*L&t>m1ja;Comoy=1!!#d7WIajwo$xKt*0$mIB zIDO1f?@`uJxQuL``pL<3;~2#PRqFu@7Db%15-vuBYO(5Y1g)O4WEN^H(?!lB4b(}16o|*-n%oXzjT@!hOLC*RE(5aPy|Km|0)C~vxoUMaT zHsjSc2Y`;_ea?$pt!{r^=;gjKUqMle*O- zd1JMKmHuJ*DF!g45`Y6}&c_D1e9`RG1eeXobRc#jG9l_c zjjUa_pJo7IB`S@qnKBsWOFWNVAtP(t#8tNHuHpdhs%|>x1*y94SambigLFu=l0iOB zJ*r8xv(+Z2MOL{hmo?|~)6Hql9_psh($>(tx1f{t(cc1{sf3yS=req8I^neuXqUEz z7W@A|SqdZ7)+xigK3Pj{>|!clAOYf5x16b=Tk^Zw8VV^2c#%dPdeEe!7eSHr}Xwmd-CyW0I4Zvn+<_D*63rBiq=0)U;v?_)m z_b_AePiSfvO^@y7d+$=*Iskm70eaDNDgeSzNPuKW&m(C&Gj9Qxi|6T z?&c_5LX_mhEt<}OPCZ@0T5@3*O}`+*cqcdObpvWGGx{M?F-CE-fP@reMsGun|w=FR1qXKcXGN3hsOG5Th(Rd5odxm1M~P0w+%_;5r+i>3lJwGH|xwE5972xE-q zv30X*p1mD$v%ZfN7C9F5`un$BAa^6Z*t zH(uk&(>D(;fkk-ppc;U=M2E>Gtv3&r!Y}J+2ke^P26A=GsP1>m?{WUMu*+$VfR*Jo z56(Cf%X5W5j)o?e7!|(}VX%2{iA5OgE!4uv0{q5WBghwj`O!NmpzFNn^l`A~(a>?Q z4cgq_AqgD^A39n5Z)k$!;Gk1fI1cVYW=N}Y5ad>UjM@E$ruI1a3xItDbPNDLYJfft z3VIlZ5&|ScM>q~11xn2f5cHXT9Mn9^90#`(B{^}&!AR)TmI~IA3ws=FC&G9qH!GCK zLuT}Iq+*Qbiy&VFnbCVuBl%H<0>>E5lxW@!wNTR7fRPaAP@Hxgw1+g8s?c%J*U92z z5e*#&0t}9WENJtimm-WYimg1j%3Fy5n+H7}M~Bs$2Nc7r-8_h&jddd6)e{JEiKBC~ zd9Z`BzLaU_Zu8)qKq1!)k~Md`ZUh$E%ykC6d2ltHNoQ#45W0=*=D}wGDC03`K@{dZ z7{soG*UTuxyhuzo52iegdZYj)3N{a}COcIw+h+6N{y-twW((VrnazVO=@8U~**thJ zP)rAPX!GC~r*XyAn+NBg<9l&od2E{R+`O&PpooqpmduD z9nVK4PILbB;2;OD+dNoI$ntau@?|y;zJOL-KOL~U&4bf>nNy72fzoasEP_t`REgGX z9((~UhdOoCT3Z*ox8v-HZTpXE*W34)QPqwJsSu5Dn+N{^!dC+-sO{##r_hRrNfkWU z+jZOYF%FhiU~roUvw;wbCmaPT#%vzEYt45#b12Q`L5se|@<7a5B{Y2!U~Xb z9IzT^HxDlBZ`dA9hr*rSJowTA-bn|DPn3l(1H@A~#TUTcuK|R|yIGLJQ#p@Qn+1_B zsoOk=UdU!W*(pMO)@>eKB$&YgMhY90#BLsZBREr)L4{zX9EgDluR8}rOh*}JilF;j zs5x#PoVGdGJmC2g2GalU%>$lGgf|aJ|9@^CTs*+6$sGF(s-!zyhc*vBhEf(~P7f>1 z=D`I6eQ!2qE;S@jy3K=Eq2vS{QI}KOTX@~(LAL@fTrG2&xSYlYn+Goh8mZi1^WZ`d z#Rnq@Z6198Kha?zHV>+;vssA`CxzQQcmw$v!z|`iKm?lyXE}|TQQYQ1*FrN?r3kS# zXf_YlSmpGv(rg|aKgf(@w*^YKd9WBtwcdG+Gk?gjYyn)Y{K1&EIm^835Kzh%zH}(Z9gD5oJm@%rW6WR9Sa&$Ld9V&@&5*mvF-SYQ&4czMEu>oj zkxuD04<2=p(E)@k)|=TpIO!rwwJ5-m%HquC!Gpl*Ve?7iLdSvIJZL5Q z51tbB-+@{NP-65T>oyPij3&>qXfWf(0fWthPX%N2GGt<|;w37!yI~kxig_$E$}nFp zW%HvY6mwR<4mqOvMp;fVyH0*|CZp^GdD9VK!=U@~Zo_~~obBv}!O$^ga@qu=Tmpj) zgJ+$*$M~EVW8;i2`&m~H#_rW5bZi=xPo2%E*uB2i>UxHCdc&YrU7sR*Nnce#Gp3}b^;)+Q0qBc zILn|n44RHNWA4cWNDoN^{5A^!8FKqqV$d4~`vDLamsVh~8wTwspw$)z%Ft+v4TBxj zKAx_%1FqRH7;z~euR4esNU>q?3>9BHh3>{~!yt2_$ygP16VtG6!(fzEHV%|-!{8;W z)K;38$3L*KYjAjFMj7Tyn0F#3B;3jSD3oTU@4Q?&r2ogAyi4ej=CW5}|1 z@(ub-bSnG5=2immlqfjOLxB z^FKatQPmH-;20TROdJScF418ZlGZDg73Tw1oNVJv z=QJZO&0a)10CR~}GH1l8$Eu6ji|FaJ7*q6KM6=7xSdZjr z_9E&`^1T{_-w}`+RG2u-;40d&G`7SqmIidjNOZvW&t`+T8X+MYi5*_ z65f!&8se!Z8f6r+Vj6|w|J}8E1DH#6xKSi6K3vzz1|(Cr`{x{2YqfoFduToPCelz6 zTF+hWWbrl71napkI)z))()$_*QA(-duZUPrI=%dC*K<2g_B~!ysto81K+a?AI~S$_ zAPlVtkPOLz9#7lX*bJ2FA0TL<#{;{s5xLw9Gvk~ix1QSrI<>HZwdBIC=gud>cqccj z?lKf9Ke`rXF-Gx1c|`N%vNJZI-iugjq!wJ(5;1~w`Y{9BR6VAcz0D^B zfZVTQn`$Ef!m!u*I6_mknTo3XJ3!DPy{R4nLZ+%5WJ2e}HC5v)VBLvwDR5k@B^S1- zRuB==vSs3MmmeJlvlye963vs_3u6PeL7YQzT2tLbA5?{!YPFN;n*@WV5@66&7X#Rx zcW?4(*Tt`3&BQ$7%v;>yCiIB&-{d4D{IqM`m#r{I=3s_Loa;_Ax8SxR;vO+` zPrE(^rQB@aFRbMLG9Ga*xYG9?A%2|W2CXP3`M*K=3d_3CDfMt>o?@Qr-*lBRU*!-m z=i_*pGyOKxjrmTewB|ek!XwUa0wK2!|44-7+dj)`o_4)#hH+3EnbZ|x5$0*vIx~%z zwnU_HoQgXL@QCx%Ku8%2Dsh;nT?frF9G6$%a8JAb2?Xn0nc78XVa?=6?|?AI>dlNk z$f%Yw?9OF0w)C_eK}&N#BE6-*pi4r+E&Z<(#%DnlwDj#)oAuI0ufptlF>@_lZ8lCx z;Xri?D@{vJgz{$M3#rr&T}yum<&*HKrZ}Z-DrwBA{yG0K=1UzSXz9nG6!T}C(wdu= zZg7ps_g*3--?kl1OJ56wg)qkI&5Yj2sFpJ9&Ic&i(j#{UEzM_{(p!2LT@n&* z>2I7cJ{GE=rB@;!@=QS0*I+hT%v?+Nyw2ATOEn2AO-p|Rnj|eHF^1kFfsjo8T#3W9bhVod$I&RLE~E8gT6z)?!qJ|HP(EBse+mTa zT$$QM!_pu>S_EN?)r;r!7}ZjS=RVpMEnOu~T?+a6Z+c$O3zmQc5j3?rPu-92lPa|m z%G{9> z|Kq6$=nY0&Xw+N1qm8PF(YpU>G)fz_T?V5e>U{jbQ&lng<$oIS;?=raMaxpPRa;gQ z%M+1(Q{hqtK^|r8rH^WIGmJ*77Wjd?3^58BcwXN#P!_ghKQwv~eycDbGHqC-y({6N!5iqjd;hZbz_0tp}vUPPelU3DCr~$u8qe!be zn9fxC%Dic$)%{Fo8GZ9etF=tmH2T(&R$H0QR`laHX%}hrD$_@+t&;8-Y4tYK$Kbt} z)Sn({^%2tzRqX*xcZ;<8lIi0N-&v7XzcYQj@z*nQp1MwY@5r}MnQwtq#rGh@)d1yH zwy6by;w31gLE_9!oG!f!ROT%pd_l1?)M^KImky1neyuS=R)KoiA+UGZhq^BYBaEm| z!iu?6e7#f@e+7zm>Ln_EcncvbFru0vBPQFVk4vAqt~WB)PSsfs?C&Qa)Yw5s!n#g_ z?98IU9}5sh)C30x)3a3k^O9q_*(pN~FI3T60rS;S_|YCs&LmSjc-XC;LdS@)Ci9~^ z5ytfU2v~bq|Db@;{u_acR+;u01pD7c4=V(x?P2GsC#A{iL6J+XihqF+O-6Z@t5m*I zpn1kIQ!^BqR4=D6%`-q1y$})vVa)Bt{cb)s0~Ce+^&%kL9VHQECy2G zx|gs;yn*T;8ZN7Hn;u@o&}3VhjP%+_9HEK`$&Y>q@))B9?P;3d3hP2Gnt9%Z_*Vt> z-toM`*4Q%F3cq}kS8yhA?K=TS%q4>B=5V4ATtDKvodp~*%LO;u;Y8t=Pw)zgf%98- zKp}1gW}V;`5jWZ?gl`;^?al&@m^THt&f!EMxXHwwQ4ZW&zB9ytzS_ve}mK_03#)LAY2UaQI~sO6a1@!GXNfZ2qM2cqwszK5#X0+c|+7g zOqZ@l*c9M4VlbKp8j@jtv%JJlIKiR*GQr%#9BhSI^n7+`P#1N9;!^wYyIDW7)B@~YFM;M#Osoam=|M5 zPreK3tMIiDygP)u!F!&U`WRx+YW>g9zXo0PDXfalmGt#)$EqE4=ZZcN)+cy?^n|=n zbT>mM`IgKu--QtzV6!PlT^4o)Q>@S~q);wx3j7Z=#7EVB&aYnf&5Pj-{4 zS2!e;YUGvhRK}kSK4}U@&;T6nlXNlyV7lCZe^qcBB&o3o*Z?Iw)$unV6f886Lpdk4Jb=FI6|wlKiW(1l#awruVB62bY$}0>EylB$$P6b^4^N)b$&U>dFn^v z^M(B1Ba;tFCm)hdJ|vxdh(2X0Z&aGka#>sW;u$0Tj1XVN9~`lkO4EInru!;Q_f@LM z;+Hov2NusP$v#~?EIzV_(bj`kk{k%@#{iRF&q&(P^%K-dvb07?$a%f+z5_|>HU#YY znbGGl<(KT>Y2#mz2@@k}Of*S;fS^B^$nv%@9s41ezJORWeFc)tXbf$W89f$Z$@gTv z6Chz_L=B$`hH6EhUhR3qXGoMU2p#?(iJkz&@arYolF=LtQ`L$jxoPtQeb_ zl)6%Wk4s%EzfDs&%5QV8q$_*hiJ%OM6&`Hp?Atqiam_C@sDg9p{wM^jr^M+PuPJ|3 zbdw;K!GaM^QX|HC8Rt(GFs7&kzf4jj61{ z9?l4JNYQaHoCCw+UKGz-04~3LMDb9j{v&&J<)e#7Fm=r#z0eFamCY^nma{YO(=Q@3G%$rx6sS23&H$&OP2YJk8PQ6O)K-Qrp!h60QxYX#oQdv zxQ&F7@^v$cr7mt~p(Ev+W)we7-W9A*zr0)Vb4)!&tY3as@eZb*)~TLjTI6}&zxcmt zzvo>%#>==zD|#1ofba+W7x!S1?;Ts6*8Oz_+@C8c9Nk5=TX^2J%+R}}T`z?%e{4qa zhfHqJIT&301ydV!s<`+^rZ#Cu$+0U?Rn5TC5&s$M|BHMS+2ptvp^KD1I0Ld(*3n^F zZ`Ic0ik^XZ4E;+sx9e<9(Aj)RXOr!O)~R_Hn&8+4Xwk@skI^IzCCv;EZ|tJp=sd(T zc0U2ifvwpjJwLHcvOBFwcH3^qQ=^RC+EsSp?~@f$x=+<9H#eZ>U&U?y~)}+LGjvzJ+n-Xxn|tR+0M^9WrnEiF)|2#HG4{)Qk9ifO`%l4>8}O+srk#QI_r@gKc-|JA zP7k7TI}Q6N(@o1>VY07D4la{c>8Ess7kguN!t9N9o;Mq&6E3>M%NS{($ujBH7b)GX z*Us>YMxjuj;a_l53~q!}>e{NP&Yg61t(1>d`Fsaf=h2_p>by3+I7z`rZK*FGD+joyM3Wbh|9x}q@G4Vm(R&BT=I>e z)L$W(R-YS}jD+W(wn+1WFUFUL#`x~g1mA#4t3_ftq@txa{wcW6sPFS2*otbN*$I>d z?|)%it-haj5Y+eWK_IPG-%o3``o3+oJ)pWARAota>jpjvon9S;H-%NrT9 zv&w$vTsE8Ct+|0E%eFC@XObhzUS+a{N#c8+Jn~!m2gG(<*;k~> z_w|uKq3AT6l+(q8i@l7Kjm@N@b0N*4;~$7V#pkgSDPN6&UH-6dh(c#ZmqZx8TZLg4 z9!>`dKUYf2MyYW+8|JdFY{Q(J-Z1B;H_W-|4Rfx^O`W1;pi-dH4b#&nVbCz=r#DP5 zV`dtrcY4G0HCod!{anLjb@se}PSXu@fdLIHst4(pkm`mRXXJy6nnAvfa%q?W=?yc` zz)Zswn51c#LX$KNGsxFNvT2yXu3;WCF4!YlZ=nwgJzf{ zWn&ZEi}6|&9P_nphDqtoFv&JUQ7*uD09?jvvW#+{CO9iA>qWlH{Z4WkF}h3+qba7f z#+Aw7H`QcuLRlff(@e!q)(60qCV4q~N&#p}_?WtXI{0P0VMLENS?jwa?#1|gdyEgO zCmSJM1Wt|yYw@oNE`cP~3<1ZR*7A$-hEJPQ|F&T6fLMk&MmXLSasbWRagTw=_}F=j zPn}xkC_#TqflL)}>O;lGxS#yY={f(hv zL=ESvR?Q)G5%*%e!z0!J(fsIf2xGk8E=iWpVfq6&8(hWW^?@$+m*!1Hk>K!Xb9o8U zl1rfvUq;ZZ-+jK2v=yR-BpM;qD`Qh4k~?w?=?g3x*26M z^u90dBIVD{&|~ih#;%(ldp|UX_*rGwp&>K&8Ewy^f-YEP0V@aFPmQ#9Q3}$b^d*Ds zpGMxlXguU6Q~ni;__@zQ`Q-zPwp;nRIJ7dpGWfw|e0(qCYm+Q4`;J!M_|p}wl4Ua2l8>1xjEcKR`7@>ptB4)1MRXljjd1?rpjz|`h;9TCYpuGW?^bje(rKiZI<8^l zXX#2j)+Botoq8s^4PjD=SrHZs=YmBGtXwLwR)pu+IQP|+7&FPix)QS^X_a_Xq(UW5 zh@ui3+>c5;QA+h7{JnL)RO0ux5>HI8#1m~L7HtA}2f%t#(l*=L)IoKWw2*kJ_X}t7mc6-hZQoM zQ94ZH%tCg*qE7*y0kA$f7#3-PzUY^aE0fFbWaQcE>5LhqMj(iy*#f|a7DuQ}rH1?O*Ny1$jypEv#+ z8m<;58kO~BM$i1wmT+~t;c9DGTKxgBvl(3u+flGBt55kOQQZ>bCa{^-M0GPw(0y`U zDBE$Ow?(U$Bd)73i~mZo_1g~Y4zH8i$B(KP27H4cjI6hUUysJ4yLeuJwpg5X~jybB9CjgpH{o&h9EZosSx<#GWWvH-bc6hKBX zHX@;1BY=9p7zRf4HSVEOT;wequMt}X)y1M1(I|ckyrlj#wCG^Su*9zsz-7; z{43bShEwRp-XR%Hzk`rO2_gFilGFnTNcNcgW@%i*hp>VT%oT#G{o4_62@hqa{(ix< z0Y>^3BWQDOumVt$IUDr7A(N~zf~w~?6O*SR-V1&nfc;wGxD=99cLY-P{F0)-2&up_ zT=fJ`fH_UKiuq$SpJvS`!iT@znll2&{5W8exTUb9)dynvEhMQ82*feJE$CPO-8dG@ zuw#Pnh53gt{awuSfKB~o%#+kV4*fysVz|gw1?PqJCH!inKitwwjl`MsV*FralHg}y zo(fhkzATKr_hV>+k;7(eho4bbrG_UAW7YdfMoZ{ zFWQ{AKY=5rL~vCO9s!s53Z&G}6-+Z=q|F)8ZNB6h0QjXOzc*yM<*1xg<6+;+Eb=cv z0Z~juhT`(&k7RQX}0p`=I(W~DF z>O~+92x@yJRAMfCQu&VnzO(>FGz~x0RIbJ|vq|62K4i+kh$%zTuZsj;;E1)oSOY}y z;wYBnJ8;|uyThQ4&jh@nCzPo_A!bP;Nbsmg@FP~#pDdU$5KBqiqG7&#sVb4#06TYp zj1<`2fR*mRw3zvn&o~%M-_*$m3~`K9lA#iQp3Hv@NvZ!(-it))#rUnPnpT!`9^Z;|ax1RpBChA7y$g?C&6IqO1Gj4= z`0_qtn+bjt@Y4ErUBgQ}4FHv01#n#@Knb6p^PhxVip7XB#af5BDb{90z1Vm_V|@Kr ztQ@f<$#RgLZlE46^uIuox)A}(cDwv$rB=!>@;U@%8v)#6!8ZQOm945T7N%_FaK)Zo z@(gp<2QbMQBQ|FjB5rav2vINg7IU_T!Dk4PE7e_o zk+Z4D*(Tr)2zC?1rq=aBsIB#S%n)jQq8h|j7~<~-QZmPgUC#L&aWBSqrRmI&?n>ZE zUhir)Ywf?0nO7iYW_HVO7D(^br0t*;QdxsBYeW=!u}%o{qg@fkrm>tP+6ybvE*0u= zkfh=WNZnk1vtTk2u~^v)7}A!bsLKF-P~bN~K9~XEUW~?xwE$4LT>x)DRQMi7hNFsU z4p`y-P%3M1sv)JQ!@aP35jKB{o%dgBH!;o5q?jkk&Y!UQ3l2@ znN=D?4v-|0LrMa6FXF$fF69^2;1~aJZ&O)ACI^`0q_W0L4m6zOQ2RV&68R0Z;|`ej zR6(zQP+S%;>l5ASGm1U{b89GznWwDJbe6^yGfP>Y>(qqemNfZ7r!Y2^l=ngwE(V!& z8%ENW?iTh&SD~+yVMotriT4{INfjVq+n0O=kiP~g;b0_<<1jgBaRoSNFCQ~5*bPZ) zIRbKsehu^`&|fY-ehup{RbQeQ%CB?Scwr>XOAHsnN!ol0mMLIZEG(UXOZ{uSloYeH z11yJ>HqN9MTf$5d+-VJteVE=Q<}+#D0|7Fb>Lb4xU0!E_??NglusPqM5U)*SUTAt9 z$Vl{@knMydH4g#FmdG#Ww37j*{yV|^0I>|ijOg>24*@87ZWPRp{@1vai>Bdnqs0%? zhrng>SHPcxw*z4DapI#2P6Iggk9f&QRYO9i{F8@ieRIJSLoD7H(Pte8O3~NeAC9*t z0?}PiS6dV#8g&h_lNd7$`+h)-7Sw|l<+u_@jnK+j0(iv&9L*5b>`~027j{6%X}aHq z%y(3OP{wm?!Eg%N1B4np13 zMeH{4U5#uklC1QDBy}tT_PvsaXn6@zfo1q4o8YfuPSZQZd?C$;So0s?!@t*>GXlqL z2E#FN?H}{S>KURe*pT zm#W|7*nA9WVPPbVWgJ;(b3R!90>jaB*wF7llDZ86S;WhW(ARs)z4em#tr#1M zke?~KdRRKtcN+bq?kzwWK#|x02-SrGIn6=7c94>bE(W$gG?G(B&{(U-B7cO(p76zZ zgb%viNvru{ z`2!@WEeOPM6@dJj5#v}a!;T4F4)g0_dcT;T1Z?ULYkm>Uds}lx(%NQ58L~i&XTb3Q z?AHp%rQ|pjfn;G1K>i}6r0k5Oar{UQT6j<5A{h2Nh2tqmQqvHS!}gMQkroz4(pbDp zz(SigV0j%3UkS_KkffeRKo;?GGxT3mcP)ES!)VA$;_3;=Ddfk9t_yUPdPz?}sBR&U zl!JtNN$Z+mnFWpHlo5UUm_2X~;$H0ID-p;_2F+hVen>V-2%m%`H5LK#n<|%I?y~9m z>usb1&Tuo*tkGKE8x4|KLi8IXsb_@%gH)rdkfl0Rj5#AFOUe3(d$D!U>nzc3ESRna z%RRz$4iKrsnkkimgq$Pa$3k9&lw^nzTgb79;}%9tCHm5x_K$&)Z2N`r7D!V45wMU6 z`3(xW9_fHHTu7RI4x*1i@~05(g(P*K5TK9|j1B&u)|?Sr$is+xu@%tkEYYscQy5La z(%?GwgO*jn^clp=()aR<*|h-*L48}nB!H3mPL?_Vl!pUqjcvzSlUKyW*?4dU&{Bcl z0=S&P)?K9$`V7{lvI-+jBVOHAgz-E!Kgu(kk}hLFwgob&14fjds#i|oR2}Pyfh2h+ z#GR3UI+`NRzJnz7AOhz93He2aj=~&E{hflzs%G-<);tqiFjZ6ei~vru07mpweaR?H z)xANp4v2k%8emZY%|Xymc}M`0EWpuBUXILW>{b0S3wOnQsS1wDz_GKv`*eX~6M9SPf0VXLpG-&cAX?_D$x@?S89m&pxqpV=F-^sQZAxqIFk55z5@_%GYa zto$6h=gKSLw|f0H$dS}JBeu>jsOTy&jh_&qjHuyq$ug5K(;waqrOTQrZ_1DH)%nSlK%4{NUGVg*c&k`Nd`9^1E=(3)~NaZ4RvDj1k=oM)!PSd7h z{u<41w&richyRH+X9SM#36TA}UU0^y$ z%+IEIHUi0a3z{daIU{h)H-;pMdkL0kDkI$dL{%PTu21%+r0^w-56dboAC1qzM zje}p^P152MaL|62a6Ai1>KX*(NF2gd=o^rKBf5hSOTU%NQ2_LuH1twlWR_={$Jo%_ zH}}Ea^DwDDpB=evO=HFgJF=K%Pcde)Hjq8#5RPC@O|h?FTyix6e-QAJc`*z(Tt{dM zH{eDeI2a>NE{7y_CITsYKVENKZ_VX;!%Gsf0!A0ZV2T*6rqL{8G*R6Zjd-z9{wvl_ zt})_Fda;gROcMVu7-+p%7!E>`T8{v3Q`_VhSKXa>BAZ#uv8x5`z>TTg_oz{i7)7P z3uZOM(rU6%2B2v*-Upl3Y722*_BKo+*R$2Wv1W1tXU(#w7_$nkRx*GmeHk%RWU&c1 z7}<{7geMQdJwW)I1DpC)vW%f=H_dxmbJ?Jfb&Bjw6q?gTF#*Xy5KI!1a+0(`!1A9i zzdX5H0g&>SAuZkKQOqRazMrJoqri}4fe<|nNotl5pxZY`)hvX3rRctZ_y`p^*3^WT zxC*CY4+8qGz?)>dQ!$OOr((9|^r;wO`c!N(yp?pH&I*7`ax4`PuD?8J-a(G(V*CQO z&uBBy_kJy&E#}8XmVp#vB^8b z*#8?gl4Y?8XPIoV&3J$i+!3}wLBra}@|O^U+AG;W^V1*_b4JV=^rmizm-J>JJ)HY!;f;kfbgbbJQWf+_nYs z-$nNw#8QWh$mv5JoUddJ@P86~R;ipH4Dj8!#cvSl$By{Ir-LA=egjK=%u$Aj5j~Id zbw?#DS0T5pA(PxPg8Jg8UXl$FAB&9EfPFpTD1an&0s^Tyev!tXgj8S|E+@eoU{2F! zV!njt!;JYv)c^&-K=Zg|U<4ntN9e`)g_RgzDw?EG^)0?obQPM|&dG>$PU($ZOmD=wE#Fj}n07BNm0)m4jMi8q*P|;KCHPJHKY*={ zYZO(m&*IZhpqu$r$O$xG)t2Db3~RvPY%ywFx8h+lf``o@YorgGLGG|A;mb4qKCqHj zWJC`GH3wdZxZVFk6yG+%=7QW19XkrKBwa@^4r_h~g}PlPASd5o}U;VTcqbCcjy!2J)Nj zmGIS&{v4#F;ux_NHy?3Raf=Yeu?}dAFRauRN8ZHqSpR~Ted1|5B&m@ISaIX!H!F38 z{KC_fJR5xxoet{)VdzDMS_oJT_2d_Ko6yr2z!d^BUT|ZHv!3j_ z-SrTPBSzBl+ll$5P0@DWn*fHn!tyv-E=M3$HiuQV1F4|O;!Jw6Ev#{Z55jyNOm7kM zKOjkMM8M2$mtVBm9{~IH>W9m0_MIs7Vy&1Ln$CF97ov4Sb~cnL9|6g-uk5N09lR(@{6HnMhV;u1w`D)qMxSD6RCEY=a&NEi3fdQM$+ft zhAtXov{D5((@kdtbaR&c;_-F)<*f~$MCBA{Ed=68cC_M5ntGxANw46z2HR=E_z5Jb z`w)D}{nw`bqp_L}kh$lwU4$J!op`K`XHQbE`&19kK2}!CJ z0=V-M^8up#2GLy(vGgF>I|RUVT2e`;J&Yc-2vDvg;K6qbm#*aCL%GNccyKxJ1`6{q zOx_l=eb&sby_WJxp6a8NvGWq`i!}BmtVgJVTDc}y>G;_J{uH!sD`5X4_(7HMvQ6AD zYWRyf19m0;%Xm9b_2Z3v66V%*>?V_8A9R!Oz)t41s7?C$2LGyH86>F`0@h|phv6_^ zWzA*f*h>;p?^WOX76vEcP*epwXmpn`x>SvTQuznc%)(v?9|KHt7LH{tsq+ct)S*hpb7l(DtiEz^n5OE{x%J#6P)E?HI5I2XlEO?M~Gu+Tc4NOdU z?hTSuZ3J}FP=2#gC(17m>!V+jC6xIPh$q>Cj5BHKh4y6tk*$d^u7e~s4FMUikzX9v zO)kNuCiMLTvln7H-WbtS&J~k`$L9WpRNOI=c5HuvxhT00(ud)bU%DP6d`*vo&u<}y zPb^pHhwsjYP*@mAW4U20SZK2aEEj{}0b#kAEUgicB~cC#<`ZksTpwH8x(wEbNu=1PBJh z_);Ng1#Ie1Lr}ukNBjM(IV0{C;DJbZvBg+8i1CHSNgCb&s)?YPCseZ_Np(bk0;JBA zUv%U@acV^UBZ9dHV(Au)@S?|yopvWqP-=s64G^CT>OG6HD;9Fmmbt*Eg1slkw}B>} zyiFT4g9}xV)uaNo7-j#U4#~nUveNcetVaObTkt1U#CwTnajHe-BmoQqKo{kHpU1`5&yb{^LBQ%P*@jC$KeMUfkVT0&lU|G;O-&NK4CYV3 z^f@u_2xaPTZJw%v1S`0Np9A#=TN+tSFoQgWUW^~zOw#mQkdf$PA-e{W)R_p7msD^0 zMa3@znEFgKfGT(ZV(9~n=)tVV?*e$4L>TeOu-4IJ*r6m9~;~-xME#u(F*&6zy!1n`gw$U}hp57b0IlU)L zpWgHHsR@3ylJz{h^mlimBt8u zw}IGC1V7B;WtTH(JwB$*oa9G&*_BYWaW5qdYTV9hcmzg;yfmp_%qXpR*LwZpCnS|s z^aG$7fEGW6YnJ*cz6xGmg&3nd2@<#5q1h6`_!Z=7qo(X6;{%XXNR9O7Aj*$-(O|I{ zwQO#T+!EdYy?&e+4f7jex>L+Au4Hc3(Wrn=sW~jG>CqKq7}uQgqwJ@NT-3+wU^ysE zn_*z;gVCTqwrZxL_W}D2|E50p!gO69;{k{k;5KEaD(Ff0kY}Dh? z!PWusrl9_?sPxr>q2}s8xRUw`*?o&9;K1W;EeyHjn=QaAsq87ntm4MXYL;~%Y=W|e z&mze7HVWv2yO;1!f+W=y0jRx_TTvDMP1amy`mnix-|{{7*?YzONt#c$=J_<=oo23^ zZ4-s2l4Z`3_)lPyqz1CZYRM+Zt?aul2XC{qs&=O(I0@`4``XJ z^Z#qk#3q!7}+NDFM;C?>AD46 zwq>O1XIK1js~QGeVtXL_s-T(0yCZF)s{6KFxAlNZI*QyT5oZ!R?>IS2(r7Y3XMtj< z&`f|N)fNFe#~JdA&halgMhWzD1+y4p=^Q3&0JKD*=RtX|0A98LS=0uTK0O$!t0uHE z(iv5GA52~svp=kvt9c&JS^3J?d5Ou`6d|_fJuGtbRwf?~FD)|8Gy$?ziYg`cGr=#Z zh%Xul`Fr>;<8vcf9iw{KA6GOKvJI4PL=$Coj_M3dD4Gh{a>}2l{0t+&i_DFe-87}$d)SD4N2-w1gIchuD-){kl!qU^pY?E;nxF0<5@yb=VTD< zHw2S^fi7cew8LN=fkVh&3bnpJZ)*Cx1g6?&vWw0GxGTW=dHt({=k-e-J`<&x1qLY% zBWWk?+j^k)&_VTgai1x2vq*CE03@k#2w0?&MrVOzJ5s{INE%05sFSpq1P;pQaUsqgt- zEpQJK{?8yutwA7_HwM1_$SKBL&g{&5L7_Q&r^y143U&L+IwZfCFWw_r zzcjLNUTE|&Xh^Y3XvUMKGXlvAzu@67vgVAq>1HVs_+s5I1pKB%l7=Vm!PD=c`C6#9 zLXw(_092{j@{1#L0M4GNKN?$Bs^Bw-rA6GyePRIsb)f7ffM}aa0Djem%BccqX94yk zS)X1n^{J4PCth6VNAp`__UVK(xhr6{QcTAKq0i)AXOs;qXSODVxu+Av34W{RK^W{5 zqx&F|9Lp64Y_z|*J{8Vif*;m-9R~Zw=v8ZEGl>lG>p6+hm<~S%q}~Ip4Zm$L9WqXK zdBFCdUrmz85~(Ce@H-#0%M;s^0FZn!qNiPc10-L*=#iKUKP`YO73{@;*lCyXke~6A zgr7EuNRU{LYYy7265FMaS7@;~0IQ+=S^!U12JmCPR5rl8s0zNcfb=u@(7enq`x4t* z@W-?>9v$9Hh*U_8^IUzRx)Z$*w_$XIRE|8kFEY*~UP15(x@bd0tL(*@8fZrf^+-rk z%@LpmQf=fHU6db9zXhTO=g^tnfa_yg5P8&_B+8ZtcWiv zhI~K%E6#i)A=^s1nfWx~grZrH-A%cf`LtZmeEjOV{|H=2eM^?YqYIt)M|~fF`Gkj< z{hg4cZb5)r!pv8KS6(Xr8%rSXAB*#D2ro(GM%dEscCoG90k(U!ZR#`mMN{xAc+^*U znEZW#k+w?T3LIs&0t@+6X01{Bev53Efs0`!C6H%l<4o%7za;tUzmE-Zuy+v;H$#$2 zAYhLfA-~84KbuJXO2NDhvE*WO4c)T3LyR()*(Mi6$qkz9y@`TXFv!6A6Je-+s$oc9 zF3J z0J%_Lw*zKJ493ZBMy9(hJE>v}O5W>+ez6@fsZ~bOX6~Q5qk$iQv=ge9{7LcgEhMSO z5U_KUoX7K!>KzRSBWWB-sFSo<0uI_w6^>TGrG7IUb|;{pWnm}E;0u0v) z%OtXNM!=jT)&oTO-J-h}Vrg6{767_&$C8SboXGEihi?GgDDd|!T+XB%eCPu}r40_( zoQ0>!Ve+Mz)#zlrFyaP>2B#@w=f#?yjYismMxsGO3^l5tJFvQuHu{Xin{K3y{yCCN zn?Cq;yhgC=EVknSkYd?s&AVLPR0Zof4gGo_;B_qkT_eEP>4aB2(D18obixOXT{5fm zWt<};NhCUW(K)c2jsG%PAIkMVf0R}jL1O%DR$>U|21=`xAuu7 z{f)4Dl=N@V?WsP$2P|*s()80n{{+k>YnFUFcd3iFu?kb(K&-K^^$wVJq-W-Ny zZOk94U@@$vk{Ho*>}~)ORS+BnM4_N|T9ms8;uu|PfWF4a8q9CA^1x3t!i57(W{BAj z*34ei;9LpkNi(7BiV;E;oO+t^ZULn-4;I-A!=eR%o(ibGJ{=mmAYF7fPMM&202QtZ zo~b~TrHQ^bMA=1s0KE-RP4uJBb0@x)PA+tRGQ8uYJ+ls)B zs6zT)FaGyf|BUErWF043BiI1Mzk;fBx+$)kR{uve4#BkQ;hZ}U|5zKcW_E`35{oc} zwt$i+=Bi+NMS!hFDsV^^oeJ0|__x*QW;|IFOzd2boAIIUkMmS~=Hv{MOXJSfB^B(R zhX6er(Em}Fn%vc;PEIIlhV%B9@NetVDnw}8G8 z)c=pY_l~ZrYP(1G+UJmjoUl)TM5N>x0w{qaAW{X9A_yW_Xqr$Giqr@yC3P#f5S{t%PfU>>o0 zvTUUo#I*z__zLp3!UDA~6$_surt%I5Sg3qJTr46tfxH~}Bf_bDglQvsFksM}tvLPv z%rygy&Kp^SV=T+A8RgFdYY%KBM`YvvdhM7|z75eW8O;f~@jxAoX7NV92ZR26*pX6; zo#=*+DIEBL?{%Rv{?xE*5K}oBg5^I|oefa=k6Q!uWURLb3}hW3_FNEYVU}3ggP6+O zA+(ikVx?A3lMQ+@tnfjX3hKNE;u)~8QmnKBukt%%rECO9jK4#$lYmJt&wSKos(EI{ zE8B(Mawn+o3jGSuO>fZ*`@5Clo8CeiXYzFXW~4TH#$eRmqllB*qbDQV{4QO^)rhUV z6F)^EokQ>|fExB8Vk&QeVBtsen{&R`%h;eN!v;T7S3!+|u+bj+*NBZ4;8pH5Hm08j zEN7*>fxaKkcupM6WG?KD@g)y!uFQOBbESJ|Q(maMGV=}eE8QFDN5NEon9^^9yTZVaY5Bq zhHp?uV!Uv@c?bP7=O(4!TQsx$wlbWPj^cnP?N)UF#%OHTx&>7Y__XP5CVbw4DtQv} zc9j(TtNB&(ey2OsV`r1Udwvza`jT^}x_L3fZ_cmUO?!7MeugGC>zVmg|7CcC;>ToQ ze|}ZYE`%SJ@BGDPy#y<|4H_bv^`DDhXVkFUP{FPZ$`)b`V|rLg2_%>41B z=)L$ItJ#m%eam+?5#XRCvEo3|=HjHG`+@ak!+7mDRxeXZqt zjjpk5gT}HA=9f=_-UYP$CsTYtxjpf0m10`E)f*q{FF-E^>l;bs5|_$um9eq6L6eFN zqTvrjHmO{MEfIdkbOwISG+=|qfDPuC{|?>8(5;%Y2buEC*8PmC1q{Z*o&0R|MGnew zvcDJrf8rZW~%bZ!i;;%aKWbwOz?E zC=*T374wfe^Yb#z&&xDF&nJ`SM?!B7&CBJ;Ibk96rsp)6p9lCxF`s>uS!bF&XfiLq zT{d~p8w z!?5)xf`5pu>p_yP+@ynwRw{zFQhDPTf@Csh`UY*2v(bImBA@RAl0hiHnCc1VxnDTp z@cJ10&g&5_`Na4BL~dqpX5UKn$Dq6y(HxYU#m%eSB5r==YvLAo zWk=%3p8TJ%;a9^N4KQ z5s2T3zp4@{ePfiS;ll)JWa&v}OU(UlkQwD25x11_auWBCu{4vZV|qR2dfZX~bjW{L z_M@wn-CUMzRQBwvtO3pxhk|>R@aI^*`HeDfIIk@mY_s&)>b}}C0ym?oIDf5!+AE^E z3#xkFJK5JW+rN!KGC4$Uh2Bf}vm>zDfxvF`MVkIi%=b9jnxB_xzQzb7ljh%r-Y+y? zV+6{1LsotKdJ4GdW2~qPp(H!KCJ&m-?{fr_#(wTbpqNU-R0+%+FQ(pdrnYC8sx|^i zgQ+=fpmZ$$%rVFy*g65hIby5Xz;FacE4AhbB$GMQM-~`?%-QHg*dh3%0m%r|KO)I8 z7#)Gc%?KO^uU6G}hqXkw_*0yTAvZruj;=#YWqSyY!0zJaRURd7e&q?`Vg#Op9!>t~ zPqK2?9b-nIy%rx^Z3M<1$6Lrjtv&*aZG5#6*vrP75jd2^#0cCU)z}V+)fj;fLgC~m z@JG;$Kz%Jesr)TqBZ1W%fwv>}2xhC=2)rNhIgB?WFuW@FFaj^cY&8?;>-bZ{UaD4h zHv$Qu!OVqxe`MkfC^USEO5Pw-Vf64q&kVy_2#U@zpvp1C8FSD&Iam1H+z%T^-PwcD zWn_bYMdlkqu5fP%so*~O3i%WUtd-Az?jY#uE&N*ZNsK$p_Z+U2?@;g)J^FhNSIIXq z_!Tq#!G^01j%~ZV4E`Du#`hoO!xh)bhb!cxIyW+}H<&M1^wgIKx0^SO9I1Et5BhqS z-@p9I3Y{x{reL%A7RE8<>k-Sx4D{C_-Z1gUm2X7+KVuR9*-c2}QTvIFDtTS~e|&x2 zabne@xG>Lo%xH})|K4cvOE^!M*owLBJlMIFd3`eTD;wDMZA3J-ZuLIX8<0k5uCt`c zcCy;#SY*Ar9$S;hwoeD$wok95`Q-;eZzld~Zu@3p4xrVW#d4Qpt>t^&Dl8|hHt4o} z*7Et#yOEY_Zu?$G#h z1CrhL*((6aSB-5Snao+nqBC0!$&==K6V1i8ZUw7+-zZK=PY9dKcCvjp7n!WjPSBf5 z{eAD!ha%&r0A4HR$2#-#+}WX+uhCp&()?M_yMpFxG?$zmo(1z)0)A4=-vuSpTqg7T z+#`_2N^yGxF?AhGJq0u0i>cq7sqGo2sx=pBFg52DD1D1RvrB&xw*HG?!!4}TqlSdd z6|K~odjv9>vy5H(Y<1mN7%;7r|Fz!IHG2Y@Siofx&aaT$gn~ zLOVgMwUtfvEfUf_Xn@@sawczLLV3e^GGIet4=NiCD_OA6QLJ3W7Sld^s0!!GiKBF!B`ss#p#Ae;C?Ktpyp=3qs>9@uP?uwh{^G z#e8t)5rz$#4ly|F_;;ahsXRgq{NW7D&NN_yHF{JB=ncc)|JtLrA)ymNTp$Vc8e#jw z06XvN9;HM7+N1u0m4&cyk62matW3+WVnWq>)J0Ib3x8f&$$t3B4ZgR<%qz~!pBZLM z=-+zOZ1p)>pkal&@<+YC?|`-L_)QtFwmH!@L`7yj+MuaN8^q8)23ex=R55U#Ghl1p zoSE66YY3g={PODu!qZ}< z?n&0l|JV@5%AD(<^d$c9+6kC>2EM&wW`r~IKQ@HTj9tFV+CGRIp<$uy6;^)BvTDO@ zY+sJ2&ZZ^wvl0mEVV5sYD9SQccd>B?7V_mzfwO9n@QR=G_|fR_V^i#jww z;I-(v<~j=}*3exI-KuW1x7hE|p1-}|+ERW4I^SF!&eSinT;d;e8)1GucD6bS8rZ1Z z2!|I!%w)M_w1T;iOS4v)mu5C-=73h_@(Pp4UJR#^P`*G6Y<30?&AbA$L1VxM^UEhe zZ!!M%v5gu2i)>@+{Hn9L1gv``mF!VAn|(7=u|boH4I-O$8({i{@-8tj!x@;JX}|{E zTxTy7ABElr`1@byy3R;wH;CD<;xS*-l! ztW3+WVnTM&F;?ajK&d1Cyt4CQrVD&y#7v*jHZy-_m@y%{=-ABIz3)7=0WE;dN9s4- z5Pk*BZ55N7U{ba*_Ho{S*~S=~b{nIE_~bc^JcB>GjnVPk#;m?dC-LPQtj4@C)sm^U zjVT1{6a4LS8{>@5R$Ea5Y-1+V~MutVbI(^=N|_+Q%YGRIU;O11qfoTk~!kV`@nMsA$pr@}r?QmHPkN z6$)m2?-USMNkS`-kQv$;WjD=Y8$x4|E0k+tWi>2p5i2{KmH)9JjFmYzK-=Xic+yeOQlr3$L*_B*r-HH zRdX9dweY9IYi`u$V(+q|mRpnVhq7whnAJ=f)SBCv3$DeM;VtOPmO=FQwT&UeZe!l1 z-)v)c!5`hm$a~C~opt}<^^{2QPRU^N@z`-(iD>@zaFe)smG_F9U-_uG1(nZYVt2(j#TO0o(`K{x$jF)atzWrqUS~(FP(?mY1 zwiD}SzBoI+>SG4`nBXL@eElS>ptSuRfsyfkVq;qQLpENH-N(e|qfYs4AvtxgF#0nw z%J_lMD{?50fok%8@TUJwTUGM8UzWMvnJo9DNw(ZonIAo_vL8K`j{#Sij{)bGKM2)q zs8$`IU~YPhpLLJTx^8||ONM8}`hUpqP4lZdGkj|7NF*Jbb?f}9J`A63@Qw4Uj%RqL z!5^AmHIm`kvDQP$-!ZFdJi~KhpP?+VSxe?uO=tM57`ij@v#ZW#cy27qXBc<#EMa(_ zkZ8pAgl z{`>Q*b})Ri;p5JtJq+I!!)6xz&*oSC$nf3rvE|sT&*xYD84cskq#PgkJ*K?h&96!^ ze6OjGKj&AqVt9kW7cQvk%J6+Ay^9u{eeF(MhgM-7#9z);u`UzwHaZ)gwiRG(*24L? zuKnf|jHT(A@Il}YE9;GkH4Dmr2ebxI)putoe9%}vjm^8Nu#5f`(GQ=2hc}?H(P&(^ zpgcYU&nh6gO1^jZfNk9cRsEQ|2W{&v(65w#%Jc^8<$q!dPa8gdC+f~?fM-mKk1nV> zp879}zCWvH)dU8A6FPq6o9v?xPrzS8G)C1?F%iRHf|ek$Y7XS{Ft6=*m9OT>p^oXkJ$Fz*QOdn9+@TC%!ul&b^p zpuem3Z>9e*?SG&CZrcAf{oS=c#vC24{q^YYq5aM1KSKLE(%)12kD|Yq_`LH;T6%y-$d@CA}|+w>iB##M^=1J>o5ZHyah= zO`nWLs(`04Ufuxzf;x?Hj2Y{xFs3%}3K0Fj!~eOU1zi#Jf-B;9GAQcz z3VQ-jjc%t74g|f%yHHK>+P^hulR$FKJeB(;pxS>ehGhhsDB`t$S49)^ppjy9>S9pw zukc$MDV&u>`4woY@F(%t%4SSYk-U{x74mE1s?mFtU6K0(PC&kLiVs!|4r#Abtvd=a z#ck9LB@!-HWE8hms^}^(RBAaSlbnn6Rbe4JP~k+8{35z4Y@18DJq6jQ^Z>|*kxb(W zFp`%zuEAZXITS|ao`#6T@#pDa?p%gW(4lzlVupt3P)-v?-NaB;)Sx@8rSc$|{@b)& zxS*Kw?xvKtHi2yP8s))gH<#wMeKOLVs)Hs?9kOZaP$bRTm^fAHZ>TWM(julaPh^@U zESyr0a)-zh>(l?W$YC1l1KBCMiX6h+7G8(iR)u{1p()Ql265ZIM0l8FPim!|Kxig~)l5=jSUJ^O4AjG+J1DEakYC zl-U@$s_+hpNsXibC(1^1$3q?;kMxmmF;PVNK*>y?{;ZH}P8Lve31`=OB5wcG?3^c(k zBF0kK_G}gN0R%boeeT!Co_U5s>^l_HT{yoK0-U z#a3`00AVVi{5!g(LAZv5r!KXEgBE4H8&KVQ)lN8^57aYc(H9D^5(k6ztJ(vHNhnU7 zk;E8>#Exi##0Fglsj@ITY70^m&D9f#MLPA`#=NOWS*1y!FBC{;6kw#Yu3gMyPSpuy zg?XkA=`=Wm>5PU{r#LfVYl|eu&lp(hMaXmVi!he;4Yg1ySMBN8Y{b|cP8fIOr0+FPono87nfb0Lcvy&n5 z;jU_=lxe+zomrUEAk_(EvA&Sk^^87qP!~j>&gig$OkrLDlz;h*t{_~RK|oGl*7&&=0|+e(>!10Jb~Q%e)bMJ z@$7HuG2CiN&+QY6V|*TiEG`znnA#M&V3E=9C;Y9iiN1@RuCb0$xlgxho{p zNkVZiekFqosG5i`giO*>BCh2M_zM~i`V!BnUi}c-y>JA)s&J~vmqhm6%b3+7hkj50 z`X4F(5}Exo{iA=NTq?3q;(Psy5xT8PrnNuEiG+Jdpuqm>M^t2bG%Gwl;xN34w=FB|prStLQ-_T% zy&CZ<=g>p~XMid@;PzAf3_?)@#3&orMJnjtCt1cmE49s%P0z@}pG?&a)svH)Rm1+cHiwLoTC2pLW;o|gO6}vf- zCo4@fl?(IS^rqvNK_=-for{$6^G5Z>!j@ZttE{}rA&B9KYR%AC0)Df2Zbyd3=}=DY z;S7z}p*+lY2VlQ6u^%ECrGA0o6n&^efliqLXdt0_5Gncq{fAn!uCiJO6B4Z+8k)Do zYH1DRo*TAA{CF^vvUgK{9qjj#?7B(zpG15aWa&8&@%CMg!|HqReQ1E%wGQQg9A zmDe5C3sg3olMx?Nxpg828W3%yn!>qJ%(sl99f+n>cZc>1(ROMO(GoQbPE1u0k%W=0 z2}DZODR9_ZV`_=h)7@TnGwsy51Pt(?x+DZ-A7+4O)P_tT%o}DFG5V_Fht^H+mQAtCc}5!V zsy{=XYu60QhpG`SE{rLm$W^sT=yP>=h`J`#2HLBksDavVD|Mpdm>I3Px|J2OON|9% zHpk#ujv||QtZ~BlOij0pbsE;24IH?eknjzjEi{F3tT&Uno|1|zbh#{GAXFy7is+rzrVF!PKK_i z7V1q}|5%k#bxt+a7V3SfuJET2UFlHR{D{CBe>MQw``i=SY_2{hc#D4lK(6QNbqDGh z%P=1DSAxNfiTc6W){H|{k3;sH5q5vlrI?qwU2mYP^u|tKBLV^fX0lpGiaeh@m1poNE{-g^Xy&sg;DGL9w== zsWU^0l&wT1fp++%m$k7bB$%R+P?2FtxDQ%Jd8yE0n)<~m?5?H|EwmV~7tifwS}PkTWYIjB-YB%vcoR!Iv>L5Ou?~wL z46aPNWg%Cpz;xdt*3t>_N^A0*kS5kBQ0GD$8=qmV-5Ann%`(o2-(VRpWHR&)qPc3u z^j5^*w(0#G(p-9W?Gf&+@vkg5jkCgNt%w4fC!^k})!`x)>uu_C)V{Q$l`qM>5#Qe` zT=;JaD5Y?}h^H-g^S-!}FH^sw@JIXztMF;4Ak84cbTQ%9sdbj+Ho&QJZYl2nSzApvIzUj_V-Wj!S zunO5Y|E*D8S8`j1xwUp#g)aM22sfi`RSWze)nSp}0S=ddn3@u5gxxJHbz5~TH7@nX zIE{7t(g+)&gF2fUH!p?M*E>%^L3c=Y0lf*sE{Uv%{JY@D^8XFZ${MWq>Ml1}&0Dhv z|EL}VSLNX}+|&t6fJ%57(Y5!~+6IT&P&_Yp+n#nDUfgOi!T@3SUZT7Vz1OmU+bB&|co*b+-SETq-QpqLD@Z?)XYeQ=RMG1)8j ztfegpY0(VMWd_sPdo1mqkQU7#X&sJ<#qfl@RP=ixtwsjLxsN6mla;Za zmR1_lq8S_sy!Pbmad714mg7Pid#P=8>YeI%I zWY=loE{Yph^Kf(QdCMq4)=brec@S<#o!PB4$g!X)eBdIl zKVqiTgVZsq(}9ec+Q!BBosMM8v}0UMywh05R84d-Ii1d8%qi!(nEXyxGG_XXE~dEC zX2zWQn2R~0@NzVhYI7xI$CP3Yc_7#MHS(IO_uw#2#Z>2?7}Z?;6Nzdt08y<}3pjH% zt4nW2byCCN$a!B(WpzH2ab48{I8Wm=6~+oz!LPbb9?x@C{*vEup1P+PZL^GKaQY!} zi`8>3F0}%P1(vH1LqM9(Xk4S>O|2Et&?^snsMWng9aN1%%PB*-idlU`^>P$S1O+earXE!KO!&#!8k0FLMbs@Ryq+})P2G;cF+knYc@9hrLc1qM; zBz*t5CA?UT&|LimsAGYOAFaIk=ws?LFtqYvstA$zE@BHflFe?qCJ#rQ7a#+=ep75x z9Fle?xu-fBPE2)IlNjuy=1Q=wx`x4iY9kz0PD~vOEnQECt9JZVqLGdy4qBG{oI}HZu zP`sJ!ZHkX;hc_3fPobDP5i}9_Szriai-(RRmV@XbDybBu2r$kiW5hBtiX8*-cQU?g z1LF^O@mi_1dY#~?9R#&W=9+hoq(wIT6-ejo$J*fK6t(b{fOzLR`2Ws;kbdwtWGtwgkDru-Qpj3CFUXu=6l88N1u_vVfUFZ-44DkBhs+N)LDmgkgsd0r zgsdO@2w4~;>c+hNf<}-Hf;8m*!O@TngOeZ|1yzuZgT;_df;Esu!F`aa;0eeBf_EW{ zgD)YQ2LC{o1o`!1UbCPTWb>dKWQ$-humG}Cuo|*+a0g_U;7Q1? z!5ff=1z$jR3sgbO>mC$B9v-xUJm(x0bc4Gb?r??|sbC7+MR3oBdoJ9yaLjPAnONhAq#@;kcGiu$o>A`P2Fp{ztqLtc;~s_AB?&fgFLCA3epee zLB@g$AZrJ!A#;OUArrwvka@xLkadC`kjY>#WPb1mWZfXA0ZY3dWc{E8WI@m#vM}fY zxnFP`WP_j*a{u5I$cDip$VS0h$i~5~kWGTkkVV03kf~q~WO494WYZwFKj)i5$QD6! z$b*88kgbBFAk)D~kZppgkZpsrArA>IgKQt%0(ofg2xN!gCCHA!2auhDpCLO3enZY5 zMUY*CwvdMfM?iK9j)m+VR6-seoCet=I0y2GU^(OyUT{5}C%xcqI8S-OGjN{vg16v2 z;{{*B+2RGi!P)8s*^Oe}vtG~`&U0RHFr4STpbMNAykH=l7rkISoR_>{7Mz#8U^$#u zyx=-GuX@1-IInrZ7C5ha!5eVi@PZHFZ1aNe;JoPtUgMbemKW54v)v0)aNhQUc5rrh zK~FgEc)@XS&Ox@vz+D7)7Tj~;E{A&_+*{!;hPxH+`Eb90djVY4B<3xFTOaO)aMN&? z!aWl1GPvb%7s8zZm-f$xyBzNIa96;61nx?>ufx3z?pJUxhx-rQRdDw&ig~NywuXBJ z+@s*Gfjb`VTDa%Ky%O#^xL3iw7w$Qz@WQcljPK7z%gck4T5{aL<6d2=4#s{r{);|Npl4dyST07^sV}BdvW9R)qq8 z+d%kp+raO?xeH!ytUEIF)?{pVd5cL`hPLyf#BY`p@5fNdMTbIZD7|kVB1(B)<1q;C z?|+cXpCkR9Um9DpNZR$LgmQAvW@xv@@^aT8FaC$m3x=O;3YP&@g=<8%8i{-sKF!<~ z-YoJyiN8hU{fzPAK3=2MCBdtZctCGZ8y6Ds0(z7A^fsn9pSN?yOX#i3+c!(^fxzui zt#GmKZjUN#i_3ze5EHpQs*Q_F4dR8pyFIGE1KeexzCEfGiO{T?OvZ`yrMgko+&?w= z5)j%kdTB`Lr3w1>s2{C`dtDm(Y$N0LsFU!LD)mOlV4C^}!rmVB6d1ItzHtfRYDV84 z)dFvLQu#o2fm5O_xv;lKEhi&_^15axtR>=|pq8XJEfHynNW_0aBc|g+2-2GnZ|NRp zAUR|cq8l(8oh!Uw>H=nfP~?88>s(ywKk(wNe|x`_5X1YWMgvX9pN34*Tl#=*(!UOg zH0kF6iW;@o3CLY=eUt)0$EAvagiYGm0o9uHM^pvPs_ta8rnyu=RokSK@m8l8qeq2= z0!`3Ox(fucaIQ;ZA5A(GuV7L)hYV_DHtEY?ph%v2(k0}Yv?pFLq&}{uExE8w`Un}O z=(Np7`c7nRWHw1}S|ZYMMkM0TA+2=$K!Wrp#9R6$cxck4h;G2>j3zBZY!RVIlb-J4 z^s^ITlL|3xQa*u^j<1GH(wm6Gir4FBq?c2?znY5~M^2-PJD?nruHRO@gL$C1vucai z6R}LbdY`G0i>gwe5bdT`0QJ=$4oOMzn=AfkQQTW);axuLLu!Ah6`dox?bTGo7WY%C zJtM`S?vd@)8AJvuHOL{R49(OcKwbMTgH#h7>h)G%P~`;mJpq$Dl<#^eFvb_ zQgVuys582vZW)8RMH#Bs>m6>Wh;dp91d$mDoNd#_TRLu4M(Mc3!4SJ&D1B2O1EBPUiPw2&X$i;MmEa1?oN zh`Pja2rKn}m_Hg6ssA;kT}*QTdGk(jAOBH+xMRUn4>%hZJjTz*YijVr1ZKjhChzPi z9_CK~D4O3!H3b~yZ?c+s9V2P-=AGh`{am~O215& zXa7~APdOC7Y4S#s;&|+!Q2=&3z^dglJN0Ad68+7g=)t_>q3f`B5ih;ufX=Q87`EzU z6}OKaJsRpqRilKpSt55abRF{uXku+rCKYwY+Z>8dkF^+M7?)-;QdBcP&=#K;TM3%F zE2MCc0p&eY#h1iBC%V<402;EC*T%Y60QkZIVl-uRg>p~q4pQoN$t)}>HV4An62seh zJk{ghc$(Zytpuy<-D}A$54kccHBQXe^Q zLLy4JId2l%?^A%e{J|i$|JbIo7Uc4Q-dH0Yis$m-+gKC5XV1y~ouQQ7S>u~8UaVM$ z3cS+GU@|Y(EP0qU*}|F>wSzPAM;RhN)jOdJ2PC;x>3BV8C+SVZCnBj*%#C6mQmfg9pwx#2C#hSk=D?^XcSwm-)fRwTP{V3cR8zpS)F%KRC%8DO z$sJPST$S~K(Yzt5Dd0t_IY7~TJgUhZQevg*15n1~`%#eNHF1R+Yr)?_Q15>d>(yd_ zs>ufGlgR`_pGy=^s0IIi?Qui%v8-NxE+nN%dleO|#?6D;A3OhSrjnr&X<1OG12 z)a45kC`Z zNqW-~k(LOYt0A}P_~iuYO^CPjMTL?khy04@28_;F@t4_%yoYeTr=3o%^r zuLUt3j~|Kj=uO12czUI;Ltw?WZpCl?8p`{8h`L;1Vl^xNbWq;m@ch)#EI3@<1#lO^SU(iGglPdZ zEB;qjvzVaNhXp68mgpIxd3aRQtoRcEc0~=VvZ$t6@oxfnD8VzMnr6k{V>K_2YMK?l z=^I9KLsZkO_+tRdivLO!G%Nnq7TgAmm=%94 zP)T|K8MYS9ihmP8S@BN}LA&CAOta_KFl$!)W^XEwQ=Phb-;BT1C@`2&^{lh4d%Rom z*MQ-xPpiRp#sAQzkljC{^lrs(@|H=UB?(e8oq${MPXt3U+1Hss#h4X;wN)SM)B&0m z|1GPzAgXCr{Ce9>!s|j!x8ffMP;GWvDbrSF#a~Tyn?o^EaV;+x^)}*VApGXA6vI~C ztoXg(hI-Qhu0Dhkjw`$3-wm2r>zzqOa(czDzrzqFWfCOKaK)bsnmRwEaJ+{r{v$-M zb*NkMe?z>lo^n8>&$<=A-#g^&4LQ|HVpse}glFcvutG3XeujxL9dCdfCAF6_ArYmY zpysgRj~Wh+ZBH%7&5M#k|jBDEB<}d z?o&-$a$#5eU&)BrX4M^mc`p&447DV^X^BWnL?Yf2xlPB}nkT2nH z!03z>e=}l>2t`)>Z7weL9lVjB%!C-O_$z>><3B?t=`G!>o3!1aNRu``1_=y8Or%LW zxVY31AYqdZc0jcz?R*C+l18SJF`B+qFRI!mT?#@cMlTNuM`?m?(w)}AgD#DIH0dFC zqRMxK45pdcq)Wh%CjHSRG<=I zNqSq3U`lmPFC8WB?qD!dd<6hg)aQA1K014zS)hstt5ics5zKD^0!hA&D;+<8ULFkM z9(E}xv0mfg-9PUrb<=S3b6nmAMVK2Z{fkcCXmuXyja^Jlfl3jnd_e<$tIHdwb^&Bn z`)Uc%dj!?L)#Xi8o$ux>c8f!Fz3U&r@}{br0c+(KLW&H-w(2=t(B#cg@p~-g>yX0E zhGX09q@1f}f#R#YV{P&_k&g*Kp!NWM0NL_YD@Q^81$|g`zE{69!&m)@+S2W%?tj!W zpj!9jsIH(-s*j!Sc~RY8sJlgV-(Yobi0TUZyjlg6Z|LyV6H(oGF?3&6-vZSYvooqI z=d&Zd9qR5-Yk+FqM#tGIL3sszUw!FxyAZYIEuijhb@=^w`78Uh8U{2a z_1sn!5!|D01<3B@tJ5N2bAq3%x*Kh(mlBjWjI>n(ex=R=NL#+Tlc2nrq^(L*^ET2wpw4aV_|UB6_D|LPL7Us+2q-T~@%)PcGK;=CoS@0A zfN}qKfVyeQBAW6Zl^lQ4CaZa7L{q>#|1E%8^YVx$e_hC{AM5jw^7!7on0gYR9x0Nt z7N*`x-Ic>AG<|t zWD8$)c9Hshb%tK*tKpf5UV;pKu&-u>@FN&eFxi9YboJFThnVS1zwROLFaHwc@JY@J zcR5N*l<}Y*m3#TSz}Sv#s~5=7JyKr)<@NPDY}U1=K6C2Wg!1YrZ-BoT;H#)o6(17O zTnk zlULz4e$;5LifRft-k%CkH1CUQ@kLEvtbxwV?pdG%UBsQsF8{qx-oXx+adh`$YnbjfvONN2W;X{^$!c zB8;W+_rQ=;eB?VbmHD)>F5dZRW1~44y78r|nk!-49bXBC*yvZo#!}jNI{pn9pRw^L z)vzIqm*WGTQQp^NEUjjvqgqWHJK{HkA^qA6Z0tX72RGRc&6bE$rsJG`(-NJQn1t+B zN`HXi{p*b^F2~#HUR|radhpq6b0`J6WFD)Z6hla(f63mDM2d_Lh3PJL9Ji*$bUPLT5=6uiv8bmuCT!W?Si$ zVOX#q?gu87eRoukGgZAmzP5TVgnD|Ssux$yw;ILILPdS|O1(O2BtV&{;wQQqFyMY_ zH9(neniAAmZLT;M)jL4FZZ&&EH3e*`YCmf<%cGi{sOq&-y#Y#t&W?hdsOoi8^8m_3 zwJHj7qN;b8dccDBML`~y)ElV&3-EAO=bHqjG3~hFrG34Ls?~F5vidn>m^nrm3)Cbq zqydUfayA&&Q<0t{>aA5zfTp@-Qp_Av?^adsc~gW@AtN%!)Z3&6gP|6Ns3bvw34~AN z*W0RYB;}fr!ailDmwK|TN&s-XZs$IwyVM?p?Z z^}bh^1C*lIFLOmV!(ABo=nxpv13Qu-wrvG-Vyc(tCvZG1jW?nOgVRpE#(sY=B$YWe zQV~Wge;F8(%2hRNaN4QY!GG1-_)iTR!sy}G!;c1HV^<9uoObFR?+*t3AAyN$sUfNJKi0@3nZP__BeQ6YqN^D7nWs#iK<7_&6hG zo!k)&9nHIsaUV5~yBR!AEnpVN#%ZsP!^@o|oVpINsr3*{f-jlH`9KNfo$x1Vs1&b2 z_VVL>b4I$H2T9Xx>e@9Sn&O=M*k+x2T;iPH%ck)HaguIO8kj@kmMiPQqKF zgjcN+yXQ7wa{RO(q8;`*vHRZ#wwvts#B^dMO0dnr*UGyB=F|{}>1SX|)DKjCswW7d zJlzp6xeNCEX;yi;!>lqN1I~N>CJDwMKYq48#lCLpV6)z35M;+1fw0bC5T02N83@tl9$u~Kuql`7*EGXLA&&ObR8TM zo;N0PrZk;qu18GdOlgUWOWgt_JX5;C0oBfw4m=Zgf6&O&WZXwzY86%OnbO%HEM@eb zkZ_eI=rg6atc6^}>J07UOesAZQ*AnAFwM*}r3=82S+74?k`s5P^ggwxRMVDR*fXWB zbHFgE(>5zGfiazkFGnazZ(1VK5|M}>i{hl?cM+sFA>L9vmdj(px)nI#VKiDXytDp8 zW`I!SOzC$Qmudhca;7B2@J#6;5YzD@aFX(RVW*~f$BIG86Y)sl$U+Bu6|z)A$pde?kMjJpg8$)h}0a; zm3Tipj`IF+%Bw<(3>9~jcj|m=|Dlk=4iO&Z{RWD!-VR|lk3Fn=KrL8ckJNs06pKEr zz5uFEU+RyGl+GOG4P9t;4#XwV1 z&+aI%=Xv&U?C=O^j`E%W$a?ox89{S6W{&cDF1CrDNs#463z(z4Cjn~B%OjfRD6jYV zR&zr{(;VeJ15oGol?Z5#^7>t1bGtVJnxnj}0A)9!awdeiHAi_zFR@M2f}qK*Im&w# zpw{di(KJVSLoT$MV-l(&iuJ-@i4yzUq2T2uEqbzJ(Gqr95{UWzJJuSPY^ zQC^*kjpkQTO#z4crvntt+7lybnxnjT0j{Fi0}1Luudk5vM*4j&L8l|wC#q?V^6mmC zniWw^bCg$bsnI+;s%eh$<^h!5-jz|%9OZoiP_`SJqM$j-8?@YjZ-$^d%6k}~`Y!<~ zMv5(g`ZGs)%~mMydX}YNl4D3ebVqq>z+k6UooX=5QC{9k!x$AZsF8{qx-oW@rJc7g znmfuXy$t!iA(LV*vvHJnJ5Z^IXF^8gGCQwzyzp}9e-@&W6AD~N%V}Si_*znOC%Xdg z<0vnC6)4Rd1sO0$dD8(%b@X+hy&e|Ei1<5TNXME;hID4jFh_Yut~Q0Zum-~%<=qR0 zq;f}%RLoIc$rUD*muuMI^)Qa|R)HZlzO7+H7G0^=4o{-I>( z4rI;;%u(J~VBAHf^f|$l=d4)NrabnKA@@ zeAn_yyZ<5KPf61r-z@?r)qK2DhGBht_XaTO3RO`(eSFvKDx-H{sOOIFP6a56>qA9% zeD?%EnPQ%fYI5ePx1XxJ&P*}8qnhUUZX7_-{41(yj_>ZXnvJHpLhAQ00Qm0IlgOoJw_n*ghZs{oNr1WMsw!H`;I}6G-q6y$7&=+kJXL@v54&G*@%SovDyScOoAVV zO2?-{CM6x#CJwf4o$;>+Ti1YFM4Xe`-<;5IQJY+x-U8Z#t;V-tRP*X=8wq!dl0Dd( z089={zKvq~VCx=W50jmMFp1fNtzUu7fQm{x%$`l@gRNe-8s+|ufN4)3Y%K*Q%99;t zmG!~aHV|a1vy=qMaq4k+-N9DV+w7VV0a1~(nuu=%HA!z;BGM93%54k|wwj#k8jln# z9OeYJ`oWe0v54$w<4IWZ;tsa9AhksNaad1Mq{W+#w}fWtUpY|EIrNm%BF7>p(e5(D zM2V^eZ7@u_owakvSlU zg&$oS`#2VP11z;4^mLX})W|#*Y1_-!Q)7FwBq#1z zj%o{Uffr(UEOG;g>3B9cNqSqp&6IHW)Cb~jIR-$3JP4*}?w)#f|DSC4B&?r?bdN-y zKvM+}rR*-4PVR<(pT7y12QJ+y{be8)m7sLd9w}k{n}F=RywZVEP?|CmG!s!Vuoj1U;CB z^)VKDksuk`$6&e#EHy4C`i_>3t z33W08@3X7WONj`O`>kRsNl0`^NIs-p#Z46W2v(J-!41zL1*n%ib z8VboVu$zcemh$77c%Gzu9_o=1C{g%4$pl&_Oz1o z)aDSVglQbV*e6Kea)>J~FK!AJAKlOQrZF2oJ4Q-$-SZ?@gP|JEcG-2g!WNa+F?fRX zXHb$Ho}C=cg2UB-qkK<3fjflYX-o?suX6AN>D2&5b4FBCz)9*Gt9fx$lUFi$g0yFU z-y4M*R<}nr1w2b#4sZm)XQP_DlED+CUs%mOQB46aQr!j^O*JQyTVBcF3DQddwnM$D zR21Zu44xqU(1P7U&^=GmR-K*gBnNw!<{`Jvq zYI{iGcsCcGc!G2T(Qh5AO%@t%Rl{yzcK8?eT5m_x1H({7aV{N|7_r0ukDtQ>g zD%NHe@k11MR(>--)@A|2?G^VDK0dnMW+}smDw@acC)Q>a!yOdIl;12r)?D4php`PkS(Tn0J2+KZJsn`CM9VHG` z>dTOL0!`>M`K)sF8uD`@VpQIIo7t2w+N&p-rAbO1G*_G@v!5Hsl+leq(kV-q2`LGOj(8gGBBhj=ha~FbSJBA>~An8vp%k=k%}+| z#LgV!d(+8yqJ|C5mRS>G--98kd{oVbKE=zrC^oJFeat(@m2@=wm_hH4{X?`_h_civ z3QKuAwyqMCULj?2#K23j9%F%y4^eiz6a@@C!+yLo_8}>YLJEhwnX9up#gE6MHR>vd z;y2PxY^EOm698DWn0kz;u4B<{r}~i6+wU@-bIb=$S$DX$>LkJg{f7W+3*I7ULE5pc znncR6e#Z%x(k!G%r!-cE`1d%q>KHZ?Id!aw+AYwGlnM^lz}I+HB-Zv{_Jo2$u- zjDvP+0V&h`l8G$W10hU4e*C|LaGm%A0`ClAnLx?WldK^g7#}o=c6>CLNyZVq!__r3 zHX#0ya11UJBGxKiq9)?4pqQMEWh)V<#AAFYbvn+=eVjvql8z4|g)2l_*l9x?&uDg| zbbLC!>=2~~Llgv~+FgL(IqpE>!8lsw%2y&QRdCj*$!4Lt14>0C@(avdsDk;MfXO=b zY!u@<6)eh|VwS0oL#&nBz%>+affQFIT#SgR6>9iC zP`zf!DpVrNMR3<-WJd<&Y2@2taJ2|FJZiB^q8Jy8;DP#6&02ALh&7X=_Tb@(z|^y0 z`n^yf)D0*6;HgIe%6#>)BLL${5o}FPGuS^-O!Qu;04D21ckuNoFOy$0VkpKj*g)cLLyCJ#dR@f;9j`aOaK zX-As}PQSsgyFid6Dyms%CcD|-&yF+DYG;RFWM&QUDi5O9WukWS@>Jq9ld&Jk&^@rZ;xZoOtBDq@i#}w)j*828P@tZ*pee6R zxR?Msft#F((U6E!rbU*r^WJ#%gxvK3XoI#W5@*kjKv!1*K7&qAJ zzafSXq!L@_H$O7w%_BVtq4R=Wm4J8o*8w~VO6m{?M$hrHUTo6We3||P5+vD_EQtCB z)vQ;qwuC5%N~6_;OonOkm(Ggg7m@6HwqJ_PRe)@XKPbtypRLG_J-kI`saJ!QeMQSb+Xhp9g-xPYLP2EDlLQ2+<1j&Rh~4$#GH zuZuZKsZAj=7uAKcR-W)=SE?z{lY*M+5ft^iwhe1}5lWA@42sk-Kf+Lw5xw~0uXyf2 z|H);z0Lw?8&mH9R^?9gxzrAYAh!cI)dMWZs3cg{aBg=i&*F~nD1%%3B&E-z;??i4m zaj3Cmydi43=5iZ{xTwytlmU?2faF0%P$y_LPPc98(i4v4_+KLI$x%g+33X=p^T(N8~rdBnR*mZ z*yvAuXkOhhf{bTH%{BS~ADOo`&5VMk(dPqvgZvfFcG&3e1N??1z2AYh)l8!w_OVfa z%`w78UjtCo|LZ`jZW{e(FczWgssLHg`L<1J8hykblRzgjq(UsiH2NkmB$X3u*f5R0 z|0l-ASv72!MxPCa*jQK1hHLb9z+jy#Q@dzmDx~92Au`GK!(%G+>R8HNe3chl`npTQ zmgYMVGg~_M5)_P-XiGPDk*W27!j`@Toljm#*Ncn|qUKuq6M*t|#?evGv~;t9G4E0G z=Q`VAOIHBg!Qx%*K-*NNrLVT?n;j!;>36OA`wq0~rllJkWAgns8Io_?j<%)2ko*-R zle$7I!?g5jFeH<`$;hx_T6(*+F}{Wk)6#{5jE(cF*>EjA3=Gz}GPTRmjGDpoKZs1S zdK2+Y^y*m3UVMcMTYBk=u%)Ng7}Gy8Nm8OM9W1wzspWvemhSecdCO00GOiLe*V1bN zG6B^;3YwPw1>kz}Cpg<-OAq}_c~7x;7dX&1m1*e>V2JunjuEzW@VQan>OiY*T6!uN z@^+%n$&h^8b~G*hA{dfDE;6Yr#4=1vxB9}Qk|ra=hH2@gV2F*uHEfuc{=(XrQO$;H z=>cEj{f7XRsa-TIYXz}bFumgND(>aeLC(3+AWr0PD2`{xe;0|FE%M%W>T8Pl=g$jNa-hQXE=}| za?B$+E~RdRO85qod{%fsHwpJOxUsr#4!Dbe2hl8a0Y1{C(* z!!9+4|6h=ClBl`fyB?tY-kWt<1T?+3-g4g?Pku9kIzhcB#x~?^fD2(!^>v`_ex~<+ zZq>&+M%a6YtT5{5Inb(`-uoCB@}tFiG9=%&Z<^klUTO08Txi2=R7~$(3x;I!%NjOJ z@6Eo<*vLacbs23cruWVSLu|AmBa#o-dp`n$b*@b9qDiTM-+vLAWcA{AQhIeP<#E@x zLC1=na5J1mtI?h4VR!1c2%RLhl<%A4;eEdg8C=$|H-kB`_}jUSx1E?y!?eyrb(3|^vx1+jh)F?i`Eg6-Q9cD{hitajf4K>aEmwel^F{2tP&>D_0X&p;m4ONu`+S6tkLQu2M`< z3nrFYOF?V+n!sLWcC4DhN;}^fFqaWs@eWWOdX|20wXq_Cd6mX2Qw znfyUwHy}14c}&M2WHi4ybR4<)v38r_e0v2t6g|?Rq@N7Z{RACK`qyCHzj93fI#&0u zyx0IGpA}LKVWEhvrcyM9OH9VsI#6{Ffy~xMqdvn%?W`W5TDjwyMjfKgz7j?6aDy(o zFRaCmrEkFDbFZk_&180eTQg(Ae5@Li9}!KFt~&x7$Q!EuqTAV&^w*rdBzs#0HKxD@_3slK*CD3zWC*dr z$;N*iA+bSEh7C?b71X#4HmLu**w}-Z%G)7m<5VSI)bjs8Sgdf1p}Y!`o`4!v>VLuP zH@*^T-x;+epV&_F(daTxSgH3sZ!G@Qu#3PN#k!E(Acf(l#PFej39L_l){No&s#mQE zoT>a-(}w@L4t>XjCJm2Y&5&kR3=brvnIn-;`Tm!GBkW5)dDkrR$@D%&J`0e?O3CNb z%;y>i7Vk8bdjg)TKgs{p>d~Xd-HaxWpR^Z#FUk90li$IM_uoM#c~@=)tsDu}v9NiO z*ll(d?7oI**sXk5+&r(0(;`cLqm+IgIQuC55?lHbmT3v6PJdRx@Pj18In)P-OZl<}nOJH*GnL@tI+M6^^9JuC#SOhG8)_Y&e@R0-=g%c4h$$`;F+ zC|9Yo@`fkxV(4yw{;Yg2$qz{A8e5A^=_xR=TFiU`!q^uvs%n?#(2wNCzOIAv8irY@ zG=6x#Y%|oT@`I@LyV|NX&7t3)m8aF3=4iG2MXE3M8?~F|%!YOhX3KAc=I8jUG628n z?ehmzoxpoY;77p6?`0+2;u82Xn|_zTpV@ax&?fMAwo5=kvmgF+0)J;`CLpPd=G!Cu z_hCU=iXJuYvs=;ChkgkExV!J5_fvSiB;P5KMmzy2%#u7dz6Oo> z2%=HC$}QsNRlX)}zE}1q%5w&IHwt$kxWgM<3+u~u1?3E{e@4-$)3)ej>@&PgKyh8WQtvLQ2~twFeMWIShh18zXLBE=}aDQ+I@oviJhXzdk8>@gDaCS*E34l>C%YgEwC?a*2Tvk!^s<%p^54Z$KF zBQ7eD&lUKOAS9JYkFCTf;5U`H1zy~a2&%5c37pPo?;WH->mN!AzaysdN(iQKlel@6 z_lS#1?2bzO2)xwSti)E={Yxdv_7{~{#vdTa?I8St;L8}d6O~uSA0P-062i0^gwZ@q z_Af@fl#L!WGxv7i%Psd;B7h5rWe_~xte|$?p)qeTY@ICj9!E^&84xVYLUA!S^RpJj zR|sb}q9s%G=!vHtHs@t+dU)PSFdh?D*7em{Wjt>s_^J>NvIKgx8J=aQK!S%mm)qhyJdW7H^J#bxYrWs z(HY@8nalWs?ipaL6V~<`EF1@44BAlSl$3Xc7Tgf#l>SIg>q`(hr700*eC0Ov_WPEl z>v5^#5Y-y~Crb*7o7O_Uyo9CfSonVnQGz1`q-x zgx--R(nTps5=to2LX{@i5EVs0RP4&JcRhkA7LL95UXKm2dpvs7qbTZk-_KfW@0le2 ze(!(Z^#kKmq>^Oam1Gq6U zX%v5Wh1}SGScr`c^A8LQ!6Qj>hY5eT<=a-VMiB^&;^*Wkut9{rw?f~Ws;p-ezYS0B zZs8w%gDH6?KQt=A4*^#4tFaV#S%i+TLX?*NQpvMMf*%WEr6n*x!gMYiau0D&L|jbg zv1Eehj+I}J#xw+}Za=V+9<-7SNZ%I8J&$QYnb>np41JqlFi!BWGr^BDRq|^}-Jxfq z=-O{}F%am=c}3l5jNoUL68x&t6n^Q*?Syo>P8qPXZg+&utlI}cd=U#&5lZm$N7EW1 z@e5ob%yPA4yBH}od2o0Mmx!NJ(^`C3%onLE`Q;9hH;ceUz~p+Sq^?dLs1p2}5T3LI z2GXnZImD$pdG?y%;c9{}4p#EzyQ?7ju!ws%8T|~T)k8L-BFz`=5_}17idurCUPzaE zV8GTxe}qjv3`WpNGzV3LbUpY;Y&{_&S?-l=S0SaQ7!K>9O#GahL&eW?rYu?y;9A5a zCkE6EUet|t@_C3E1MXq4)jH)JHhym zu!?Rox`I77uem6Hwi_*$bL=@$-OiQ#5k87E|Cu#5)Qa7LZbao1W&T}e#GJ||=oO@P z6#8sSXF%J+pHHfM4$HR+FqR4HTFVNSF@IqTe;$Y;+eGYDE7lDy-HLK<+ha z-p?HGR{Z9lyL_eNtWvkr{*Tn!WAr`lH^J(?j}>-dkjqEP40qao8#TGuCPJ_$7zn5wZ*?hRkUWG_&I^1VE{#uocbgg)oa;l6x#+xRI*bPEvKTMaB$7n3=$t3 zgM(of@zVV)%MK*V0RI6pN*khh^d1gT%tnSN{v1hypCYX6c`8n1pyC}-Q?SFRv1^>^ z1?aqdMFRr}^i;J2?JfLCUuzUM`;Ns6IJPv(ak=i2)67afiu^8!zlz?$RWx!4_KPVEIsq?z!rTzK47Apk8jC1AK#L3J}%72@_SFIdJjr(ejj7u zHXLeZozEvn-Q{DeccX^0E;{Wj#zvtU_yaIBHTQSszt`r^0Bh4pOhJuR@|R|OFh&Wh z#a*VdSou=te=+&Y!{&k_%SCLU6$^Jh{Hc-^08@W3_YHLkdQCK6>E_F+S_f(?P#4^d zO3YcHDqx$gb^ZVZmFyEm&Q4>X%}Fb25|QuE;jo(H6n-N@NAspXfl5JCXH!ts3rc=q zHqS7%=5pf2FH zEN8aSTf2Y-mXN~90Pu_?b%iB;U zRy=W1TCBNK`R6ojykSYHSaXO~%z*AeXYg{{1y&M_xx$)mS-bu(J*eAs%rOwTK*ZKr zv2e4u6ojn+e-Og+bqTfy=`~7D)zzSVi$B|gAaNK{}fTdJsP__b7aC#K9;EdKJ;-L$a1Y{V zT}%(+XQOsN&#RK{mo{4l(tD7})=BW=MU{M7fxYL9kJy7scbOW>9IEaxJqYWN3o$W( z`qjsz&uN&x$ac7Hd|+S^pRr(vYtB1=iztITe@=o=T~sc_97cP;7MsrnHgOUz$h>)G zx#3iv0me5#E3#ObPmv)T#g%5|q&JEtmUHZj*;J2d_y)*P=2($)?zM7TGvrJx=Y+nX zO~jv5`6vV_l+%bAPO*Yb#-}+`!m35IZku z*cxL|H{NF&-yYvhJiImTG4InVbL56n!M>(4cjnFA2)RX#@vFp;S}>MGJmh{bD}RVn zMPL(@F6d1ls>U8fVDW-NEWZm9W{tmW!7`#v%o29Mf>RkSkZ5H2ZVrmK48TADJzks! z?yhT~T)+4MWNj0c5q#u?U32mtcF%p5AB=|+BNzd@8v4Y;IOcWL;~{!XAJi!`DwWKd z-5>Ntv!248X)uyNrwd~o8M-f;rJ?DIW@Q-m#p5AInGGVhF_7DuA!lMaCoBZ*Ec`i@ z=R%M|{c~9UudHB`vFU;~X8WSX!oGMh6m)|E(-$?&zIY$BQ}Y7R-su5ToF=sy+D**% zMWg+M5fGY>Kd15os9A{6g`#F;J4XG6$};Y;V-@o#>5n%b@a# zVYt%@ZA0>D=aF^ov|W-wupupbqQ}X#E?=;4XC@sL46mLngG&@lMBd zk2|F^FpIE>62;2N8xK@9+KF`6%lcZp#{Z_L@T(Ax)5!Doz-!#O0%_g>P!v0+oWbS@ z#`|H@;dpHr?9X6sHw3+kv(7=-#|ORO;`;%P^6@e-jL5zLITz%#$EwVUalFY%G3d6$ zOye-Uo4_~8oiYTBp+`@V&&iy!@8{3V<;_Ut~6i>Fs9%C$~ zW6{`)jKy^$ z=&vw*U)ck}!c+f+YID5l%kb^?wiQUpUV>n%V|Sc~gz91^5l^uQH1}-A4%M+}>|(|` zd@fi{>>9>8GFyCKBX&1q6&fpuJ;+$6e+jcV_B>;qHP$@#24h`xtVL`OV_gSG^|Xrp z$XGX8!V$<^MPf#y;s!d0KV!l2-~=cF^PRCvBl1u1fyfgo?S$V=IA?GVyu=JuO-H6iLu(M zvD&IpRqsHs8iHr2=2UbPWt6J^&Ozj8>cSv5Z^j%vJ5we(&ZenlNKf!`JyWTc6kA8J zIZ(sVK~3N*g4zZFxN#Dz*J&Gas@qcNbkb0wYN@9)SPQ5psJ{RW>)eA$5GnYF5$IAZ zWV~(=%7gx<&6M*SHftDc9tNB_0(obE<+dF}AeyxwUc_r`h9$2fd(8MLBGmXWL@-dD z3w~q?hXQX0QoJ$a7~F#?kW#+}fO%%MOpPScxf1SL3ZT4J z^el>&sk2Gwcc~=|3<+$Aq_EnnXUQ6}7p_X;!{JWy0)k{qL*!LV1YI31S5?KIa&nP1SD8 z;@F+YDLRKGb6XzkG^WT%TFJ5{9B(J#ximO?H-gi$XMQX;&8cv*L!;(&3SzKoPUj#7 zv*vUOVz6sY*C0l_YMpFjWr0{}W5r@+wz0CgSeb3CtaiMoVM_MgiLj*U%LvBWdAQgM zKSod$eo5Ck{AYmAd$A5{FbGS>ip{YdYv@muna?suTAqrB&VOTkAuB)9@^r+VDTps& zHAGsTg?KLG%UJM8%e{yBAr`Tb-*<6CO3mivm>gM1YteP&`G2cNzS5mUDX`&YnBK6(f>#O zSxP-cfmyEx0)MUCIa@_zBd&(RSXm8(Mm&fhF56gJiRr7%b2cb;64OLs#l<~#W4u`g zFz)@1?uJ*zVfZ=fDHyIgIzIrAvuy^{j3*uR^o}wlCO95Vdl|_U$caOJPnk|yG{SQD zeI)aQ`*ZPgH1BBe$eAX*VGZ#%$^fgnkkY;>XQu~2lf!T^fRocq1sl4s3_QeM1ZFvy z^NyS8sIF4n<%_GAAuH8WE~n*NPpO`a(?ivds=Vb}A3CLaCgc6tb>yhEaT!7{0oB9n zLC!`Q#ntc>6emk)Mq5m9nP!|tGluI7bF@*zwNZIheIZu}x$0H4V+89svi!Ccc(_ar zgx%^GD&@?mCTPiHIO4N#V(Bp)`cb%vCsSM&jz(?SATpOX(z}wc znc8DGQ@KHD9Bxpe{qAHcHz?8m62y}tPWIs}V`HukzQJE~ES!YdyC^waXlU>9JS1Dh zhOrf6c=g4J4ZjR>yqLImiH*Si8Zo_hiH&?xV$s-YVxu%OC$^5U(K?nFJDagFI#v+d z%Gi-QRvdeeIgHh@=4PUfJ-VHlbE1y%bp*+r6Lsuy9rK*c>KinW7jx9Rpq{Jvt_9Aj z37@Ne02GS|f-9ND&-)3wdd8Y4yrJjhi{b(yyDf1mn6iZ?>g8X<=z;2=x?uTP=(e#U zgBIuKFj{P)&GVZu+SEi_N(^2QblTlNV*0yg1Y3B|$xk$xDNJ6qAoPiYv6q%S^PZHhH;; z_S7b?Fwwr+cico)_!vW?`1e=V3#vi_7cB zMb|!n+^(*he6mM zk%e_`orQJoLNs+(2=BqNMz|_I79y?}Xoog1>0~@ArVV!Fh)-xNXaABQm z7S${JLctdVtYdSRU_3u~GkQMfz4_SK7HFjzWU0v~x!+L=b&YR@%8E%0a^704r_wntC-E_t>v- zkVhfAS-(P(xZWsdu({uEc^qAwn?+x*@7Tr1V#M*QE&&4lk)f50R5KK#&HMrUjj zG$%6jL`G-M5)^N~pU3Dd)|=b10NX}xRsHTNF9s7?uex_9sa)5oq4ZE)LJEtPLAi-q zd#cd^CAbvsCUV%ra9O`M2&L?&F36}b4NM)TQZ=u4j>LId5zXX?siKxa%HWk1GFtRW|`bQPR)jgv;WU%_c zIEsv;j|&(h>oCy0t|n!|odLyUA^n!uvvgj(Yt1aA&i}G+M&3 zIg}SC27V4y)`^KhdXCcvI*GwL=EZ&oFENA@8mFl(%AJMX!4|HDxwt=6HXrAx1O^7T zr_&I6NAHTUV-@7=8@MOJaXBRKSN!&L^kb0Al}4SBdn zOU*8?t-hAAaXPlRY7|IEBtg1~$!1r^z12n~Zyzq*3=Q{7?w2*3`_hcumzvxm&+*n> zLD|nl{@sSQ=U{o7#f zZzUy9)GfK>a=p!kXA181HkW@v?nlT~f5}{K;PTR4K1}CwBXhxK{*oW^Ca=M&`YJ>3S61)4klO`0t@rCR zy`1LV->9>Dd+I$eAGaB#p?=f-Ejp14ZTC0M>_PAQjeRGwe9Orb`9o^%Z!#7y-+1!G zAJ`PX)k?2uUiBr^9tyS94>Nf`$0&STVD9foKD8XTjgVLM=hxxN{_`V@pONKPoxEUL zOE4QNj;C^i`!`BPmF}z47Cggfo@_ZI%fCCh>Z-O{)nc@0zS7qOdUVxhs4qp%3tpf= z3)xmvK?uWPvz|e?p);i^u)84M z9+QN=A0AIJ=wjK(Go|>UTOnu9l;R!!CNX_KJkEzhfa&|;@d_=+o52{KoWvl2*$=~x z<~iN*6QTOVDyPTsB4h5h_e|HG)5~gKjhu6OYoCc7~@X0kg{$MgYN&R89@2V^;q z_GRYm{W*_mNH2VH9@jD4$=V1on%jjBB#4!{T`duhbfCck;*E$FtBO6pe9S%r!NX6XO9*uBo9sy@mDLK8r+iSE&3kgt4%{*9#W*Wx9#% z!v0$zX$yPYD@8O|*#9VKu&^I5Xt1z9S)yqRd!7iD?tzR-vcDv$JV}+ErXIEH}nY?hI4f4E*BMaw1*6&353QYT%)<_W- znj-4_7m~%@(K5*8K(2ZnkDuEE&vZP7zyH9{ z>M?{)mk}2al2kDr3nZtyKXzo13pmVU>7r}%N{;1Nf>#Z%&?Kz0BinQy%TOIog2T&3 z#S1s`5erk)nFZQjuRv0TAL%NbHgO6~_(MrUdnlYK2ZIzqTCVm+I9ug+=-Nxn%8+KQ@qCiri!m& zy4T}ars&&{X0excH(NUVIS7Fi7I;NLJ(J?xv zmqpPdbxbddqGNTexw%;uZKHle?sDrQ+E!^$-@1sFo2b57hDScH&<4o;y=Z$iTB7=9 zSu|y$`es@5P!rWR%c31jRNpL%c2wtytiD+mtuRr2vn<-l|1 zQ&!o7bxa@1We;Jwc_?S*cY^HrE#%rtL5G>&MT+zLkz5B1U_+5JQh4~rgy9Lm8DYW- zPWSz$f;S4BS;9M!JXr?_AnS#-njp%fMW3qb8<16qj>n|ovNR7oQ<5;A-;4G5Hn`&05$R^cbH}% zvJX+Xjo;RZ9JcIM)eKqsacuk^9-u@eog^GDIvUei&L{?b27P(_v4Fuuf2KBLFz;{% z%NWcbhhWoRMk?IW+=Jl355q#Q*vJ3(5cLmTh<5Y)!d2;Er|`y6D7Z70q~^uN6y`@eml1v;gVi( z)vf?dGCzQG3FfUN_<9|%Ox=Lw;zPE>C0l(*0-yQi&gTPSD8R z6n;TA1z&$bmVF4f&Wb1CX=#1ytat(kC3Gjbbyhrqa_CLY7#4ulo~}0Ttwc&<51_b5 z6>*Q@lqAQ!)(BU!THU_NxekAL5fsM@M9*ReU(6B$eC7#27Q zR_+vDY|aPBM>cv4s2Nj0bhG6WOYl0J_8^jHAu)HLr@Fqj?vB zR}0Qd!t2ryZwfDD=V(A-rm|nFMY3|HQRpeCc^N8ld$SssQcXWN7^*e?C2$|uNWssq zhsQCRjRJPI;5*XsN{$5^kbZa{>i!s*^bRRJ=K{IWktf3v2Kvg&Kp#pf+Dc8x!+75S zy~NU;Z$E*VC1n`KlF7+w2;yJi zzp12fIf|ePzX44Z{v_cn?7vju0C9hi^i2$xyhqnZ`42i8OPOfwP*wC`Pn~Q2WTeD8 zs3)-yN46~#8L^H^mA35#p=2psX1NAiE>&2FZHX$JE$&0&cEo*IRd_Qeox%_3mi$b2 zD>7>~3#xMbu|)-gObM4F!9VIG+~Gi0u{Dextz%y7493RkSQNiL&gghmdNO2H@_4wk zqVpo=bGNvcGUt+K>AGwQo0~HtBe&;=r{|_)Ha8th%WWQHRdOm^=GF~|=Bn^AacAW) z{LD1Ed&Hex!*KR=x-;6+M#-YO}{84UkKw`9#sxHqKR*bT2sp$b$?*ND9);n1hy|Yl8mp=@ccZ!;TxdrCtAIa#cstYp4xSmhry|rpE7f3Cm z`D{<`G!rdup{Nd9Vt$3|#N8$C`{K4=iuBfM0lj|iQhJxbswBIF zc77fR`7*T*kT-5CTpFV8B8eeDw;?gHS}lqNdB$UF?}k|0?D$q)-i?H z0VscTd=&_9kic%9`~(huhpZVIMOC;*6Z|<-TB^5|+D530+6Rw<`XP8x5je1N@Cjh8 zQh#5K3P&BO!5EU#YKv)LwOs?&ooXXI(KVN>K#hdJ`jD(&)cBK(u5%mE)mmMqEq@Cv zwLOy|WoxCzo(!mqS_6-rM!iK=*nJp6ZrjLOrBsZE!CJ4`F5Pns8m`p)-&O1i4#Ibigx&q$g}WEb#~u{}1K)|?(N>)4E6HUy`ncJ#T1 zxxPLVRrdzqlKm%~XGq)YlXS+dRN{OXcBdK)Pq#m*_1b?CS?FyS*!Cx@PW!h~w*gk` z-6!FRE^OVYLm1E9&~x zXQJAck+lDQOPW=mr2BL$^*uN+Z^^Yr-#Mg)S0nR)zJ~yp3?Fx$A>Cb{WX*dFoRZq3 zt~bng>ocM54!|Y*kG#QGP<`5sOiH%_KnbY&vv+KY<+?q z2Fh*%>&?gEswMSV=;CEJgV0sI1y5a4pJ8npc#A9$&V?rng!@RH@S$c6Dni@)fdaPWHP zGQiO`NBn?T>>PyK9C0&(F18@&N$h@!VX?|BVw+PaiWlgx;Yv_b+|zC$5(>PNV0ybr zYG`h3kKz)N!KoM7pii1@Q9z>mGNf z=^byu?Bs)ZOv`gP`g*xlQR{6u8@z@)Qc7a=Ltw$1}#pKK7gg)iWTLeuWfe(*~Yt2Wk4G0=syU=PFG!l?(Me@<@5d!A;2Nhkb6Ioh2a-+ac~v zgImsxuGgdI{8o;0=nfR5C`;{v$GB2|;$Mfyn3}D~hK-TJmss4wcj$Ihi(x`hj`{#G z7CoZ&(#pk@b_|-Q{vDu=@x)r`8lrOK0*Zt4qM`!T7~Wc>M^tN4tb!t!okjiN zbhIncr0N@>H6dD|MiVVl6W~SEnjmB3Ig3cSS_qG=9#Pi@a=QDl(>WDt9eJJ91)|~k zIvnX|LhPuvlh{k$A1M5>0WvavfxLn09e8Yqh$?Dg3!5}~u409bP~U?RQN01{vnirt z8_a6d4_d}4A6`UFZjjQFlxlUTP)-dfVl%rYw4BO}7O6o(xi+A%nv--a<#=_PP@W7Z zJxQTsDJ#`uLisYF$R#G%g2`x&`cNo21sNqWh5j8_uSZqm{wz^9P)r{YdjkjAH3Os9 zPSsY3M+QWvliJMUoT2&ySug#lv$~u~UpH1OLtZ;c?De495~S&i$pH7Ln?qm|^x*QN znqH5q=Q9D_QD~c`yrh2oD@sNl@2aB0poBrYW>7v>-Re-}Y|_~Bg*rAwgC^AqI;-)h zfugsRIzHf-k)f6R4n@(2YC0Gf@EW`@pd_WNhVi*tWEs0chBTF7{70>^jCVqYbgWit z2dn)Lbp{yhN9w-;15=ZL+3p1Z*@IM*1MLL>@EW8R<-0dn$qIrpT3E2TyTeM3PM5rw zdA4>R0(doAM9oW=6tIK)B0wGutM%!U4^gs*`+=3bE?rW<0q)mU@{x2&9)K4ObN2!4 zLbV^HgZ#)=(HNI6a0~cbI>(ZVM!B3Ws%Du}^sIY@Rnd_InFO>7O?cnE z$!Zu|M*|i;wBj50E-<8XFAW){a|`1q_hHM}8Zu1h7RDd$^OkW>$S|Gz59HEoxO&qv z-VPZi7hzPYFD%1r8kCicv~dHAMQZDy_FHcIkQ)pPsD09k+C}^Urf8!xX@=3OR2^g) z%R)wa$H_`S(dbA=(A4=MMaovD50GR`tS%w%K~O_ViOvn{t# zNl*>xMH7|lBPUo|+e{jYCX9s zkSjD(RH1zp*<)!hH9*TKF9mwls-LaEZ*>IH3oXhI@L~f^dzLi|>PBoZy-{f8UUN$u zQ-@ZsSiQYN!IeRGdB~M2Fx|J5wRE&sV^v-e(nK2_)Va{6dyB2M2SXaQS;h+Q8q4@J zlc84-tyD|qcb@l_%`d-sW=SPeQ{}?F$@|7~kN7KYM!i$2*G3f?Wa@HNLsIFLFWJ26 z6k#(AfLUkSPF%EE;)LOZBY%F=!bX<-FwTCaE9ewKDni=b+x z23a-GHjJ6}Yc$HzhK98COiAnYwL8_)mWH$-Q){58oYZKk zH4WU^$Qf#y_^gnYZXjvBT11*z+Rl(x&p=W6a-;5+`(6WF7$~$)8jZCyw=5`gx`A_P z;P;K@SXy~VOE-|TUfm*RS=xk3j;-EO=N(j4GC%K237+vn%j5= zyt8olm8?1_nx~C?n$g(JX4GFOsDs}1K{cu)%_5r z%F=8em_%|JQ$=LR=5?- zq&i#%w|7#}LLSKVc$vI|)qC)mrXs4xPfTj1{!B|M8jhrPsx`ceG^^(zCUsX6;K_Mk zL^bNMl4-rvDeyMqR1(SxFGN`Nnmd!{lYAbk_lfG+)@Z#Os0ODWl6HprBuGo%2E+ne z)cz2V<}(u8)Io<>Eh3?39`;Zxd#f5$heFLsL%E!$KA@@tN^rh>6E;k}pS%`0`Rb&A zp)K=v0qN0~-JneI3rM{`cn~S1sq4tyB_%6U*RZC)cnz5BdY=d|t*1=gPQv$JSi)y@ z2(8rbfO;1wZ-jC#MITcx?U(B5txA#DyF|5yCua81HQ5h!-VroizezSJ4oUlxJV1?r z7r`@ja~K`0)=0FY+Q#TmwG$pIC!*#tAE~E)>Rp1v)n~#rTBRPv5fUznZfK>%@Hc`VYfB$bT1Z3;!#)2m61* zE%WmZh&V0%c5qwyec-nC$G|)>As|8)4bz&`{21MolH z?E0_5{|x?@@IQyI3M0;`54(O5{Hx&qZ+rcJs=d&2<$9a$^_$Me`25ajN97*|H{y?h z>-m%6X8H5sHu6t^+t^kaDD$vxC#GfxOsl` zKwg1`a1Zdy;5P9)!Y%Oo!Y%a2z&-H)-Q1mni={5sc>UH~*%@^)6*eh<0bJKV5pKj^ z4>#Mt2yV>332xlK7jBOK4BTA*ZMX^lYq)v-Z*cSdXc0?$AlxQ?Yq$k|XSjuaf4B$Y zxm}JrHE@glg>aktr@<}pFNNF8zX@)0|9-fo{>yNa{$98({O{o&>_>_@-W0-Z?YDy4 z#_tBVoqq(}ls^e>2Y)`?j{Zq-JNXyD?d;zOw~PM(+^+ufaJ%{M!|m?>7j6&VZOZYZ z6mBoSBi!EpVQ~BSN5bvv*TC)PFNNFRUkmpze+%4)9sg>0k2wAv@E&#i$KgHZ_;0{_ z-0{DH_k`o`gZHH4H!g`dPdR=wcu(VP8hFp(Ygq7}b^MX=p2G*Y;63m7%i+D?_*>w; z==fK`d&%*)!+Y8BpMdv@`0^Zw> zKLFl4jz1ROT9`c*{%P=+!(Rt~3;fgJ-vs{*_)o$=6aJU**TYxM@yTZRP2ismKLvjS z{2}nqfnNpxRQQYGQ~#Opx4^#|{(0~pfPX&xSKwa&{~P#Q;r|K$Lioj{5$7WK?crYx z{|NY(z@G{KQut@WzYP8r@GpmdC;YXj@Q2`^2LDC)>)^i+|8)4@!aoE4pYYFw?B!Vf6HDx0uKMwACV8!;je&?U)cTM-v7V7AHD4Vr@h}PeC}=x{v~JOVo--+ z_n6%Vb3;MFYrkV+h;?B2^&hbe#^x)=Un?tkqp&SwUaS{mZ*s4IC-%LejFmO*4WaQ2 zzV!*@@nssP*;GV}3*L|ME@8n3e;QrON&4_hiA7^4G4_$ha$=XjmV%Ez5Da&j3*V)M zh1b)<5_uu;3ap>?8+lpsMjokZ35+kz-;5A2zLA$DZ{!KKDIMb*d5z?aJi+b=F#TA9 zej~4uypc!y)JydUd?T-sypczf)pzv>d?T-sypbn_eAuf^vsv(syhidyo)CIu5|SG> zVUMBT$ZI5T!!nO+m5YoYe)6@ruBCa6_% z`^IfnO5F1Py#JolWmk7@x6OlH*HFvrNJ)FJ>wzFG`7RKU&4XQU1VFIy)(>`-V{=Ef z>c3=s%24uYikc_C#%u=RA51QSV+b#5f_|{;N2{S931W~w*U5OWYtk5$ZEDD1p8BxY zKG^jr80d+PT0@ptf@;$bcC|hd(pT1z7Ax$7U0cX7xl=bQ@Gfy5LoC4{p7auz332Z< zlsDylM3BL_1k3LOL*eA24`silO{p7c62NK1}D5O1s5C%uFiKI!!} za!Pq)!AUS!K14U^hMs9nx}3^xL`qtdZVu9tw*v{A^rir))1*J5DyUXHL&m)fB`>F_ zZPK}8ar(sMuR_ArnxLEXLl8tm9#XYI4K(Taqfq6YLk1->oAd=Rq)Eq+C6)wDI^bwX zFRmjkR@f$efDDs6b+eJ45ceX)5)7s!A>}b4?oB{((8vTCLkuO;XS2IJlv$ZFtoaH9B(d=!(3od3nTDgwyY z?Wo?tcoplRI*!vd?5erUja>W$ruvj zUt~M0`ACfoRqCNoOnPKzwSvePw0ot-dE^&cc2>?opgf~gZHO3S z1$kYmEg>RD$`sU>n34WSsarBRjzPXw>hTcCM!x9!JZCIibth~ppdp{uA+(l<7h)}3 zZz75u?GqRrWR7ERssG;Y6i}r8Ta#8obpZLe6h5228(;zIMezd}y3iIJ<>nouoDzal z(?5 z;6v^-fK;YF2yRm+q`PWK0Hk&G3e}Odv&h>moXbLvR6NW!tzDtI z&qNkXO!)B^b{i*RJ?>-@ZUWX{f@27^5bB33iMP+GC%|V}Aqm-9G%OrG= zZd?+|c?j2{;E-DZ#(u{Vyr2!2;Ft?yvQ27hw@I;kfR$@3Csx8(JF`uSoq~683fk-K zSYfOkvD80A%Ym`3j2)_(MX|#e>!4%Bv5|~*V%BcU<}fIQURgduudO~Cn6|ch1(ed? zk&?Ey`YK3EMg}=)Yby^tO|G-Hnu6@)ZLij3_z+Ub?-2~vRvSQIzG`SlkXIzjlwMoy zvKneh5QB8nXDpXL1xsxX8O&2?$Vk5jQMk*z^?gf_QLwfe2uwX+M_R0~YpW$>nB3*f zGF06+5J>PoP)go8Nl8N7+m1>~dD$pxfyc6Ii7{o78BCGPAaB}?7BL}08@usT3gvrRYOlD<&@)al% z`RaZ+rQ|X5qtK=OY3enmO=OzR+yFDx_aPvcm3&^Ioti?iS`~+c77MyTEsd8);g++%=J*$$It=y51eC~ar_Q93-D(51 zvu_7x3-GkMJp{yTwyiChP~KKgW>QcW0(_}H3<2hfMbjqcmZ#LO0TL9KtEQ%Bw7M5Z zr{Jn)Ljr?DR(0w z$DgXMw2~L5OA0tg-D@Rxq)T$`<1bOK1H241tX@i&6mYfr55TPize$(m+Q(m~^6xW} z*@xRA!yo}SszU*a$xvGty0`16#r@w^e=umM>Pv=Hh-Or%3)#6M?y+FFs;Ukw&x-p8 zx~JM4POOuICbV@gvl(1jM}uDY`CZ(*t%~~t6{r}l3jIFrt5*E2KpY^~cK%@ZJ1hBP zx}<<3-JAzZ$?}h|CBu?~D>%Q>EeEJN63`c^X)CVa{6+34qQe5ztl*kXu0^_B2nz!& z$#~L;a|P$0=kB2RMRh3Qb){EuO&9L7+ID49ksViXO;`8XZL)hmlOTDbEPSTXf8RY7 zH1%6Z;q?xb3poE9_iscCkfckBpUvSars=J_k?yJ<0g%>b^(v-m5A~UFriC0SY*>;Z z>N@J$*Zm*VWvVi)5Kc#sO;lraN_lkgP9ze_tMx3;H`#+jv0cpNOQfXD@-GKz$$tY0 zPf)%JfMC4Shhmpv1w^$f4yW{2hLZ17)SjSx3&N{RPK5;d^oE_~ha6`#98H24q^l-l zmcJA%wJc=FOZluAWTa>L|9~Nb$(A6aV3t38Hl%mdkrpfLEPoXlCU@#))!mN8THJdZ zu>^xDNk~aT+`AYxlJb5c$Y5N8-@;7{02Cf>wAn0=6WWl&GkQ`T;!`8;FOY=HrGGIv_VYMnFn+I z`yn8mPd;uG%=JG9XwVt-Tt9M-Y06^gG>WB->|Fmh5R?JJ7DQqA%kqiE&>Q#YmhbZU& zN^=s;;7>9egG4r%>kpfY^0o`nz$y%>X0AU6l)Q&dR1${Q6U)2^&5^uy|IAAM9!dsteQGhPt=W-5r7#Q3k7ll4O|)x(26O#mNS6ztGQg6I+mBG0xxV`n zh@V!65_SnY*WUt~XuCF(8qD=YOAXW4ZLI>%_C83-*c$+tl z=O2ZM!EcF2oDQ`2b~$f1F49K}UTo|YuG6^K9yCm1tf^)e#7@U5qj8BoXef?tWUQHv zHH}@uSaTgK!H=vmnsX~$Rmwb7k`S#q8|IbsXP;CwF%U?9buU&?(Y(eh%_U#TRFxbB zm;58(CyL;tyrUW9KDQcHB2MutYo0%MeV+i+afcU;9dOvg;lSk^d5EhUt;tZ5pgCA%Y-cCSx} zrnaH}E##E)`ht^Sux*Mt9dLYkXYrp{3!rEq9OkII&H+yq|73>jQo_3H1pQ|{RSkzz z&hCE0gpmYl6N*X>TDe)wAB)^11 zWBD9$%KJBB392aXiG1=BhmRZ|*U7yC7rr zM37N%b?gK>|+@^qe3ecc4 z=uN|E$V@uJ=zyWy$Zi@wA;BmPTM&g=3W3D+$R+O4Ex$p^b>Y?3s7EHCMBxG9t<+8^ zFxzYzUJL;-n=NchW;P9JGB1GkfDoWa-jgnAHVxle z$=A{)&8A_%Fc^*+R^O#dnoYx2fVUCMos^cR*))7bf95Jvhk48*CcRBjwRd3hXkQhEIV?)*pu2gH1!< zk*+=<+#iB=)361YYFcSas{69p5`3#Z1Ko(_hq zPOJmlO~W3blEamCaqAu8SrRG?zarr}L1{!1VZ&}`qs%%-8;kwE7LC`Kx72bxYk73tEQ&ke97 z<5t{k8un3qR~<@tUD-{;@nem)w==28P9G3{W(j{}5+u)X(@=dBvTKeD&kQ)c-os79 zcB0(^G}ttFN3&Uv4S=*h8*Cbm6VBR@BZUo1VmA$63eRL8RtQGQlQ5RyBnui6I!G61 zT}T*sK5DKYTdA2d@I1~8tctQ*E3V2gkVZOumFGIy6^cu9x21}7SWdi?;!?|P=|(!7 z!gy!JwS?O;66vsk@h*z$vHFR0xRCL#<~z`Nkyh$@CgiDk<6Nw0(BFqmMup8o)*P~WHtaOkMmt>c!qcCliaL_p|1odM>*=^Oc0676i)Kx^6i(dWMP<9Fn5B#o_ zdMH4WvN-Onb}`#wO8qNDq;l6lJh_(6dODlnV6YeVkxCsn#pa;fEM%t>$17D45-*_& z{o$ePD)ln#*@hH#bR9-#^$1Oxqtt=`F*8al#ZfZ5Rvk9c3{@LQu{Fq1GJCPQ!-6-a zgM2atpEx}D7&DGN9)kLqJA0Kn51`r;?u$WU&_G!vJ)^#r7Ec0i;0LY0X`y92z zg3YJenz54Sst*8eqhv3F)?AUiM2(qbz=`SL^Q?nw)oy??CLf;;zC-Xv)wR-qThhU= z2;QphwBU{av;*@|Ex_2R+E=;Gz3fb{lOf$DDT`!$o|}vZ)QwPLu&HTdW;QZbd3A~#)GVnz4dYt#_-6>sjl-l8I5Y{ z=EBi3dsgIoFeI0@Wav5-OIyo$n!PbHV;cI{&=9?jeaxVDMgAl@H$++LBppk6D{@5* zDCVe}F)FGbg*dchn4^9|tHtcGsQF z)Zcv=0E-q;-t?gKB3q#blQPKdIg?{dOTxP70(VrC2#<8{1*|m;4K+yZbX0RlInwPm z%Tnfs6zP;k%Q*M%0p+}qLM^&`8&$`+haP8D-52tx%JPn9mR0Ue;OSxWpX8-o2NmiR zQfl3@*(}#@AuMBOh1yIw*SlNbR%nN`V$1ld2U$Zr#2Y<_dWJPXbo!}nR5sk(BOIeh zhD^*=+@j(>0R$5dV%my(bW?J2l=8U$Psz46UTZLR1sX&cnUAWb)p0XC^+nLv{B3^pU8>O6Ht1E`*}Hj!5* z(?yfp=fWJhDDNfT7K5`zlkK}L_If(T$)d^KP3D`q;=2&jZvZs8Z#FQMJIfX&xgG^V zhMX#zJh~g8j929W0T{>oCQl}64R&}sCUVbC119rC4KZt|$i2AJVkf0zTXE!m-Ooz@=W&+sP(1>N*d`yn41!qvQAm(0vrIk*(`0Y!g~l{>oHb1kuw`=e z-{gxmAWY|?ri=t>UrXQ!+~k`VK#*1#Tu%ds*CyY!T4cuGne{XX;m5Tg$nbkcJq;Xw zoBZ-22$IDObu_e;(YML|Zi~@skA+~`$l3(Y2NL}(6SadcN5>|fnz;m&KLd&xRGUQA zE&`?K1g7XR^0iW&#+&4*_)=q7Z!&blwo;tNo8+n47Ca^dyD3iNO$yXg7SvMu^1uLa zBDjhB(aYl=-ITcH>^#xzzuhwj=N!*+JlW0@TU^pHUf|g~I5En>+$)UkRIf2dvQ?7A zByn6`g6>kE2dGWE54YF?37NU@>ja!V|IuzR-XG;8-kgNpREaMCKCwq`1jYrn>KS6# z9um94Z8ZdMoRU6)pxAOcjv`)j1?by zZxgUP$o?u&44s1Q`5ag`B&#?G+CtkVKT!Cqfe#tsHUR;`f_;BEFcBUWU{;vl=!ot6 z2?X9q!4KA1i<3JM#9b7Aqa*hF_=gQa7e|9^P>>RQD+*u0fORQv8x#1HD?Z%Jpib2; z6UvEsvBm$Hj~16kxRY;bNgG_pHdw8`0Iifb+d$GzQVLO>rr*ypRejoBuuGmTFD2m@ z5i(VMX7rJWevhhGIwsY*VKT5k$eutbx}TQ6qeQK(JMP z)g)jdd{=;3VX5kyZU;fC`gIb-;-vH*Q`NUMddyaJ8i?wo*0|I!h8@=AvsiF=@xLFt z04s^)P`pvUok7%Oq@C(gjrfDAXD#xWf>f0xhWZrXDRoo`%p{;+#)yA76`3Zv#C2&f19UtB;BYk=pgJf3bTK>YVop}7SBSic>cV{ro{q+A)KnYFzYv=#Zq?!q|at<*6ucHoovl*cL^i^`DY zoj@VHihO}myUCrG1V`$toqCQ5Yt$~9RRx+#QdE9t1x5>cih3?UZQ5G3H%QYz*QJLz z@60jK&0e6K#ZXyGmFN(>s=N(LfKk0_o{n*d^EPhL|r%cYf>Jbjb`pSEzKM0cj+aySltcD&W zyj){7?5n3?5DBlZvl>cJ!b}Z(CdPa31`wpy`qUw`SMRZfK79rRWm<^M$sWk6jC-^! z<;_M~f{~Y)QoVMN!+&?z&6MEMw0_Z%hekIMDqxK;Kb~JS_Eg%&i zmsOuL^My*i#l%u3O1IUQpMjSu^?fD`!9$)#v2Ic-XQ>r5UAv>|LTG~;25%3oQ0>Uk zId)Vh65Xse!IMs`h6Nh%dgE@Q=c||CxmXih-O_1s3Zb3>e38nBCw*RR2r1?i;xJM! zQ%Avb)g2*BzAjdUI-Q({)J4L1A>>Ib&T8Lm1G8cInw zzXxcLEw0)SA~acU(d*&HaISFGb(tJAndrUIRS$+pOK1nOjD>mDRqq5yFcolqDn5J- z2pwq2uK^<|vik0w_lbKk7^-wxU~ymCR5phyqwO!Tx}<=e zB69%FBltzSB=6pNy(4E@$*kqJ#IRPtL6NJiWEnwQP99o&!y^9x*aelY4oe4l4B?HA zyko)Y5Y#7j_|@Ni0M#i$etII6iOx_KMVdYj%9WWE*u{ek{8Vsvpt97vKh&;o(0S`4 z;{eJ4_0M|ZGJ;(cnGXiDQ-1{F_IlzI{N5Fj)4{;Su)>yF!*c)a|6%V-z^kgxcF*4X z1VR!>P((zC;w&IR1+)qlR1g&@3Z-}wLry|~XaX1#83cl}qM{;L#2FE-T5A=pbJaSP zS_ec$>p-m|;!vvyiuS(myVlzKOo;UM|8MVq@AD-4?6tmOed}A_`qsR59H;bL3WpKF z*tII0L3k{9!X+>^gM(Rvi$5E@GbsLW`$`8M5WUgPrj`;tL7gCxoJ# z$aTH=PvR>G7!(1_x?ZQmONdu3m=MCu_bx48&h*5~<3-Pb{E`fmsK%LPyH1mb08QNP zOh-k#%c+u1XP@p6UP(t#nvj+o|Kr!i?`CpeMkw?I=znoGq|@y$5?D0dX#hU_6U20C zjn5&|8hV5fqq*KX77*{2*Hhr1Mc_#O-rmmv>~x|xUO1B?9I2tVcPYVByrlwPoCW?f z!RL8z3Vd@2hG_8@0J*~30YAR?SOhsj=P5hAF+hNiLV!_zBw3jbB6M2g%>^P=>wE2I zxU8p?RBwZ~$$Lx;56QquWf;5&9na3&6+edINf9#f(CUpM%6WNHLX^1~D80QK0qbWje-~JsS?r1c6#`lHQ6q{E*3l*_1hco+hoLqc*4Cke4Vja&Tj7?*A?^`hJc!}2lx~>|k znI>m9`Lm8!@Qbx_XBQBG4A>E+o9%ejzJMJ_FMCreYy@IE-Z%3tK@7<2C7Jxk!9Um(y($*k=FX^Ia4?jUYPkCzOB&Pk#XA$EcA~OTq zk~+U)H7-*_0^aqhK=!js8vH*1_&xC-4tayEfKC3sFXPHO#N!*YP8!n<#^*RlKEuBR z7$V;sVt`!0x&Bv;#AA6D4|di|7Wv;f9?xX(=<5w5#+Cl~H9?Fz_pEkN zq|{8$_@{_0rg{tGoA_#C&9eYmDei{s4B~cW7W%7mqC)4nkMu$x1pKZ)kTbG)*g~K1 zhUIZp77ttK&jCX`9?swq7J9!oAt`{K)h^1I1U=(_Lu4__uQ2{8Urj8Z`?7C|QG4MT z|AtZfbx2=MD;%{qv_NI#opaQFMhJ~Y?MuSA^ilgN@Rnn{mk9025>C&I+BX8GX`EH5 zFpbRTm%fFoD~bJS$QX2jEja=(eks!{o*RnU6DA7;nofM_L`U2sL~yV>E_TGjLYOfe z5W^S5@4d?rH)J80QTwZo!+atrOOAsXwSVV0{3eTo8MW{Bw#~o`Ssc{o{mW1wNNa!3 zKyah>NkH&y329(RzW|jK#&?9$iuu}85rcSIkuG7~nUPE2vF~)3FbY&%iIVCPCWVou zp92)RgzJ&|-vk?Kt9(QE%urA?q$K!{24nCis`8llHx6U*nmccP>* zAv@^olnL3M!09>$F)0(WgTuJeNS&4lbJkcL*~%_T-1*_93>shf~p z0tBY(-4G$1U=Ymy@s*Cl(_tEB9GNyDy9rp{M-hhRH4Z~MNQfiVqGZp%Syz<@?naVu zLiR+!yuBH(44Y3nOA0q3JD(USP-^ig5EOGlsHdjAu&n_Rh4CUt(K9}dK)x30bze>^ zVOI7TMDNIGfA?54E4vu6T?tB=mHllPSNa@YQ_f`wF`AW~3~0~zYCMbiI*2z8u-e{m zZd7e~c?VKV>An@7gCvQPT5b0XBTH`vD5|y(A^SYY?Hx{xyF?aN+l>I~c95~DKwEA5 zyo=R3;?D|sN7Z&RfUiMj@7fUP>Yc5&w>t6%Lky4$IM;vAk-r%N9l5QxyS``T{wFac zcSg0X1cv161|f|?9ER<_T?GsYc?2=ic-U(Dn&VNM#lu$Hj_+F@=VkB+tL-RYQ0Jc2 zF6@ShV*e*1i>cni_hiET$j8_Yk4*Pkc@j9O8Al96Y^_mFp<6VFsja2*+pX|L+A!;cXB1(Bxjd_H@ z*snn!4*`$XCM|-;D*1PTk6hrZaFtpMg@I=eP z`1~^O=?D-9z!VY%D`k?gpQmyWvS~cs?jiI@rv89AU4G z!87o7P6b>*K>tMo^oanFhJahWRrsMY11}jujbt(!ogOXoI$R0l9^Rh#G02zaLJkcM zb3JE};%@I^`~=>wL+n@|@Arhvd^%*cIs}?KVU^_ho)=#NymPk0(vy<7WahKuyO+h_Kfe$2)>TseZKJyr|*i0_(ia{(ctOqt-Hq?YCFvKjt8_W z!5&JY9vB^3JW_a5LVzuak=|^A&pHoJ)CONzOF}Gjp6*N5wvpZg#GL=6!3>1?QU>NT zz$_)YQM;waJF zO0e$AbiL9hJTaNV`23GN@9_wt@*|^CnLfpV!k0qO4u^RZ-1}>YY!$wTC)5ATyB%1v z{qI8*G@;KUHvf2kEq>(Z2O#Z;d1(uSN|k4Xb=z=hM|3Po8`~pkeA}88;cM^Y4+2=~ zFMw>afbF1{BPu^HxJD2g3r$l
    cIv3Wg*uhd*&oFmA@X+bQu90#b^;@|uEIzX#m z-5s5-0lvl@&H2@Drv{6vpFj$L64P%kDZdMJZ{fe;o;?sh&|l9kPcvqKi`j8WECzeB z-oANB zzVyO7o%_kMm{ZuI&P{cALLMEzLkvm82KXfUIB5n$^gRy33m%m;NU zaSM`IG0=-81~G|OfjZhRisPzp!*$%~6)1xIuJSd_RVtADu0|sj<$na>7!X$ElcbbK zoT2nstdb3BtkY$&*jQRDU%Z;z$f&F<-fJcWY*kr)HDX^z_BA__NtqhdVm88R9jYkO z>nKGE_?CpM28-lh5sf@0Kh|$ia)W-SBs~EA=1$2X{dS3ozVX9AwAHxS&inb&ZJ|$a zC*67|v}ihMUj(gcAz!4_9Z`J>zI#Q<72ymo(2Kd{F2T)zB-&quT^3kEzL0QCPZ?}c zc_+Zj@LxffQSdIe*s*wG#oWG_;MT(f039kWhdVAV`B;K&F!S}*V9NI!hajD^&{dk} zIE&t!g>cR}J(xyG`Dvj06m%7=XZh#aDpY;|qUspUHG%x6Og)`dszUYk4_-dgBE5mtv-;{ zJ=9qXnlEjsR*rg@&Rlbc>fI=N505<<6nwRE*uyhf>{5O&7{3L^4a#Cqk3s~^*SFO5 zJ=TluH_VGIMkX3MP}~#5GH71W602J^+>4F+nHRg4&;b-+rWN2mKnZyr|1X8mGfZEy zd0?!LfD=Y|vCl)``4%`dR!6|A_-~6`8orlU;D}f`0Zo)u^<%MTR5Zp&ca_cXIAox5 zmwIIO7vQ1##TT#UCUiIoAJO)}Vr~_zq9!{bV>F#gna|mXNe;q;HRo9U!sBz7AK|A8 z=O#qU!uev_8SVhAWDgAU%VzZ9q1#ln49J#p~8=pnI={0kJNK-Qmy{TQ)i)= zJ&dYbL3=FZT8FHT9LX~8vj}pnKs4ogM88GJmHM5Md_}*VlJDpjHJbDbK|cz#4~xEO zTj(oq9EyZ40Eu$K7Y{Ae(U6RM-UR1HXkxCkaR)ii!2^=TDt9;rFMmKtlzagt@7<~* z-ahLBQC%s@4-u2R4-ZlDDlb3(id4#)4{q^eZEF}AD;`TGB&h(CC1CJ?nDhlO`GsMU z+^k>t7=8$#n0Me;#_;e*Ikt!3#fw$_6h8~WhAdtZnu?(4cW!qkX=PSYruImxPC3pZy_c*2M?Cq`T8wNUasGg zSmJJUcQyiV&lxP`cE3d9LU&nwsi~~_qqo1EAyZe2VwK#r?e_wjB$KDCk$+vO4ck@Z z(CuSBs<~3^mo%QESmR&6P^^#G(^jy`C=@(^FTN=-UVI_jB|u_O%_&y zgRVzlU{gm?e1CCH$8@W$zg5OB-5UC(!SoupdL)BiDt{G>P6neXb+P>0JmclzQ;Ruf zN^q3b1mGs6l*mlUIPsaziHq}0V$q4u1pZ*=6JA`Sl& zN6r@{%#EB?#CsK#q(z&;cF)KqvScli1jA}Y#PDTA?MZtkrj^7kPB9Hifi{63POjt4F59R0#lxr5bT zgHR-;tRiqyRj^s8|99s~RJO~eFV~~E4jfYVW{DlaKBVSNV zyg0dv`1RmG>hs0ne#9g%z(e-2j+AQ`KcuDZ^^3&e-7}Kfc;JI z%TnQ$FEU@ZAu7sODTA@%fYK59FCc#xq+f`94au*2SVid6#RRADW2SbQnthg{Q_RpBejD2}g!;75hXH*wN8jyl zvxD!^9DKV_(dYmj=JvM{4So>g`VJ7O_++%Cl^=VEn+AYnKLIbMiEB5Ck+!-01zwMb z@XsK%yjfKLGQ=d4c(5!_(J#t!IXL=HAS7mdvCOLei1%1AFT^wrIO%T($5!CBmpE=f zO!5vq$nie?BA@&o2;qZ;v*Wc^EqpOWkbekR-h41yc%b?N0Z`(6Nh<|@Hl=tUJgT|@ zvAKMx@>;)`(}1+(>arDC3)sUpd7p9ZliFgQ(2eutQ3 z6CTXK9Q`5#{5l8WR|#hgqGf4(F&PL3!qWI9j;n$3dtrT_g;mMjJN_=$S(*6aH5J_I z$4bimJrIZ$b7xO6_mfnSdIcnV6uj1m-|@gm=Ho%hw$m>pcHwz>EWJtW$0*Jp}b$-OTA;fPnU@g-EzQ3&7`v^jWi_eB+{ zOFu)?2wtnjuL&{99(Yhief0|!@qSUlzZT9SL`y|{F?r*(d4hXNHUi_IIaK6Ihs76S zRTusm<#e(CJi_uV+@Mm-rRD_h9z7UTm7@I0QHJ~z+^R$1sY2*@gB8HBjwN`7W+Q+r zgfPS*I1Wl1muDXsGifJyMJREf7XAc>9~OFoTUVX|@HHWv?+~ICx&7yL05=NZR}Mk? zMsDE`&x2+?e-+79sp1S?r;bIuni z6_&xx{t4~|KMWYB2&GU`ck9#PTh~Bp2a9me=VQWq;U_Sj+(YGU5Ca(egBWF(!K3K$IM}sw@D;lfvqA zlf`l!66a|MP#uwz;f?xFfgt}(>Zr~UIo4S&g3AuyV3hP$74cuX8BSWW6vz8!YBRrD z6BWn%T4ZT1s;+|1#?k~aQG{l2cf4&N@Lhb?O6e=g3G`V!VZ{|h2Or26gAXh5< z=3T4LGq_x{@SFE2-|puamP3k$MT|c|78W$Gozrj?!y|2Y^_+$q86M?xjXP-mU`|~E zgva~>Q?<)6cQlNn%L;!D#0#1~oYPRjaHS1@J||Z9Brrcb(ThzFv8VZ*$qSk{0!qlq z3G~eg746xUb}r_(2)P;mD+s-nVl41E85K024=5q;;eQpOza@0BgT0jYza1!o>hTJ9(ciVKY2b7S3P|fv({*Ig<^H14>w70i3Jk9V^Hl;gT z>JG1j@!|hOB6FWl{>wIln4&8ugsd9ssF>Gn!5!Jq$l%*T^qYq^9CbBju-*|4d&-gJWW`@?*f_KKygk{Z3ut6eL%3*F%*BBOSEOif z`&QHDPP)Y!w&u=IPs-N=wi{p#vZhu6ufC-e=TeG-z|NjFyu^%eZ-Z0Hha|n&I{Y{M zh4DKE#-j~uc$dLWftlW)21ydrQTRasSjQ?)McV%Hj$BzWPV^y0T9-$Xwto-A;4LwkLnepfLH{&Zzi@eP zli4*0$>oVzsw^bx4mM=b_bis-!-z?qFLKDjrBVM4N3LQ`B`5L`Anym#r6S)<@~16% z72(x@6V~o*vhQ+h3cT_pj4S;9h>~<_m~2)VX@?vnIRi|{pqH4`k;z_oFsr@wTZFDE zH{d2-g-aqZ|9~$r(E#ep#i9Fc;P8pzko;P|XiNCr zz=eQ6CY)n|qqfA(=`n}=Y(e7mezDl&K-ny``3{W}scdpge`f;cHPM#XW|~-Prg_;z zg6H{3-Q^q>$>WX#U&KKZlD9uC8uZwBxWAYO{}a3kfr;C3q3V7yu^;q0Avfi#@zPfxxqcFv1}?**&DU`XHiAGokTxmA0<I$J> zo{1hyjD~Ck{!<7q0>Kt3522HxDN+-1MM^XlX-^%`Pw+T;?0j_D@$db7V|F4uYDnM( zLO9?zR%qL&&o8e){CW7-K8pBTflhcePlVEWFlinjH;QtkqvQ(hV2P@KEm(1ydEA;OSG+^%m_tK#)T1h=rp#xKaWeh47w3Sb4n>!E{&` z`2<8AR-WP@!3`qne5d7lcpA|KJ^>+7CBIti{{&Hy&n;yTADF@9P}MGYeHTW)6g=+~ z-|>h^?u!Sqm^@IwXy-lvvVSf@Vj~x9XCoOIEAGk(B$AH&2WC#e=mjym8!^c#c#zpl z{esyBQ247660^Z<2UPzG$~?weZlt{p+{o#3ar1r)ZV!t~e)4Jkf*ZfH>+c4TxM>E= za2w(=);1EP-3z{koDQDHBK;Y;72-kJcF-@l@oTvL6okZ0(@}=oA3ereZlpZ}+{kH+ zxLt#o%*SE0%7svaTc_BM1C3MB)v@Js}Ym@mB?ToO-I9lA^s-8 zzCyI@hcCuLzX3v`Cmzdz@v*S>zsq82ITBK++f-!t1r?3qHgndm1iz)T9wZ$vq!y|i znPw#&S-U3F+k$)RHz_96GNJ#{p{I{pPqL%d(7h#iwLou>j1$?@j*KtNLN(`Yaod2K zHYnx=2nineC&ASs+T(7UqmbwtI--r9^+eUnQ-dYtQ$V!@{|)Rf#=o&>82Tr8PL({r z7vI5-uk#fc`lGi?;BgmGC*!jbc=&KQfD?ppoX*Hu++1&aNV1Kd`zKY;F;&lMs{afCt1$mg*OU&#wUz{*rL!AXPE-`@$1D6=Q-6ct;7>zsL0@Hu!v} z>Dal@)(XP!t^^xB68z%dP|%$%+Wml#E^gu$%uo=xR0zWzf~G214v7~qlR)6zLYV9j z+)M(5^ia@=Lm=b|pnN2>n}Oy!JbVv@e9*sLK?q4!dL$OdW3hFh>pP#?`V8^5b=-~9 zCt>SoLaudmOB9K6q}&gbr--`Sy)NYqKHtNXH~4!gn3gh6lybCr6?*Vk(nF3e5 zKxS6*OSIoK40afE11tzLPO)j;Vv-FBzQ+$aH;Vg}S==T6kRXlwkRXk_3wD?>6ilYz zKUT@_zxxk>TS`l+k9Anp#ktpd-WK@Qzp- z-f61^KNr3d)ZdE3QpX`I4P`POSnG);?ni~+4BXlU%-$;CYYf@`IFm^J=x4YJc^m<% zAWGyTxS8=Fm?5H#E)l+%3c@pJi5q}56BzditIq>AGvU}HcLk~J#K!7qU`LHrtcuhf zHU{GLl=#&lCV3bhtTc_@clmJ+#LsnfYNbjUv?C_s{{$RJ{kAyVjhN(Vc(5$z>KCmF zFD_aO_vQkgLAo?Z1A@SeDz<&q~{%jdk@`66U z8=@q>w(_Nt6ZtNXzYEfiizxmek{97Y@e{oMjq&>mR*h(t249S&@%p($2_D^nF;Q68 zI;^l8nA_44ynl)$SBdQRj?9&0EWsfEWUTL@A!LXLghVxdO1)V-hrm#k9nLk#$NCSCI4s3 zcS?hje?Cg4k~ez!w}E?PI%XI4B{NCM*h)fCK1Cu+klGg#ZY^Sx=i|XjvOvE@ z$!qls;~Ylo@*c4%$hu4lxTXrRmMeK|w+S0Tz_IgUX1N!D$#s_Nmxe2R{?2VMMXT;dyqf9J>#b#w_gu}F@As+cOk&vi~WG#TcZg*uv)Bh zT#H+N1JE{+8;!K!{HTddtR8@VKOlXtq{`0DWHg$Lb{K10zVZTK9f<#m3!uFnk|w$d z@_Yq0{QzhZn?Gc-F%9z6um$DxM}Z4sME1lXuoRG9vxHLgc{C$CniAS!I^*H(%I5>~ za$r_mLMdihDJm8-G~0xxR9w%{9MeYESNw{h78Ak@h+5vWJY9hKTYz-8#kF9$P69MI z0W?~LQI26u*fdS|{stPalXvhp=H?4h^}mXuzXLpDN1Z&|xEVWg!J_Jaus~g~qn{xmL{68u~DJMkIBD-WrclXzvl93<5-^3jI9=f`XmjF%CFLtX z_YM9{SuTmnGI0cSM6P>ZL(yCR!EwGbowEyC&MsI|o)5Y`@o$Rqo=7Rvy%q@Wt@S?p z-W26Nz`97Oj0#g(n_dzwXj5@P6lER6B=Lh{@NmdrV>$yDvqB__RYN6;OJ|7@?zF6nfy6Q`HEdp+^PrMZ4UZB|mh3zO>Zy+4S| z>?t=Fv>NK}nI%1___vVtG!Wk)2JN48ImYLEG(NEmT+lLb!IJVbL3bVgb5!akidp5I z;Kg-K#Zq9kx{i9S3{$D{(waFJw5hlt#C#QElDMxJJRLHap3cApEdv)UDc=rs2jYLL zmHKx_L7_l=MG19z%2sN((rPQUn-nmW+Js8Vj{wb!__vkXM59tC=A$gg@FOuD;F$8o zbnV(54y+--S_h0hucwk`IIM6DcMDy+!Xq%F3X%~byTy@(Yq%s!nMeE%nk4Epp??Z= z<*D=0wj{JD;6g>C*-p4n$(So;6CGlSy<5Q$!Qp0c>HDU!4f{;Xi z6Jfn0?EH`;WT|w1=rFaeMe12}eu!wO|2Fv!!TF!PCOa3|+o zZe+gi47t~*akn97dInZ9V>1Z5f-sgi4{UbD+fib(A!M^Tjg1XC)3a>C{3iGj=b_+m z{LK{R%9WXY0Crq=+GNN~G4HgASWsHs6wr-ahKh@D!w4eAyk8%{Ju*1AR!+z<;M#Bq zA^CPBG^Jq&7|u?`cEHrs&_w9=DyE?M&X$I`40o{M+gn@#e>Xf9I|1Atmz>XK%Bk{f zm+%Ql>0mw#*X=F$UcwJs0}^j{3p4tYKe={dd3r4LCzR2D_~kFoMLS#OW>AJ>WJEoXT!z=6k&0GXH}um`Nzg z{4c>^FEGd|=eNPVHvnVBy)oqegDsflKBGT~PQrgI5ih_DI^NC`oBKjGKiGmfAzgkG zyQ2j=9~|c1#jIZRVnz#AtrqMc+Ycy`7I!+x&DpBmK#*j9bBDTBU{v`VFLpS>rb)v$ zRMe#1($a7uo2H-IhODY#0)qo>OSKFw9to#I3e)0M2$2W8O_P_jX^t~ZUacj(+BSLP zZFq6mN}v#@^1Ev38f)A(_b-f@Ute#59PP`6NTetdR{nE*2xZ6Yr+3c3+jL ztKVZXv)ytls99k`ka#dL%VRQlBtZYLc#O%-<9^2@mf%G~-K#NFpTIN^f61m99@IY^ zur7dAT#M7b`Gfd9r=a=jmWtVE^zx4}J5L8z%wuSn2_0FnkfGzuq4c3MhoTeuJ>;m+ zY19#>yv}biR|yp#maA~^7#(0=>!dyF$f2r2$NQ)7TTs}Di$oH0M5LN-Z{R-;l9w3?tlI|X_08APEnxJiyyW4qVH0r%jg|-9n05K?D?HC+r zQNq!$+v=ccGY3WA19XStKgZ}7pR9>_gT1}*L*m1LRjX7g!c+#Qr{aQEOc#WhqafyZ z5HAvgCqo9)(;2v+W#ED(<&B`b9RFJz{Z99)DD`r0ik12b5N%MRJzjRYS)Q(37qp2k zcgiil5pufzUCW4t}m~=*Ng2+sv5?xv&^S7G&1<9MMFy+OYpAr1TgQGbZuVEC0+c+ zJS$x+!Tao20C(Vh%*_c|__LV8i5>ySP5GlhJq)M~a~WT5&)`o%yUwFF34V5foR*5) zd0E`F*|Iopxy2H^wVb$*3;$k+?=}c&q}}lmhd$lA0I8yy-iaR#aX|KwNP{)5ya$JU zQ+6kTY`Utpyj>6m^Arqb9f)_jpE<7&8B9-S;DVNc3t|!;v^#=!Yt=Nd9tm{@;vgk- z7ZS2_IIc&o0rt^WPCJKVLQ&^F(XkMx9l_yLaoH4dsZZl#L$14Nxy%?2qRIG=B@P9f zM!a1qHa~sM39~tkjSac(rW3~X=cjugu^@(fr?fH!u>{;+7srdh(YW>D=|e3Sw0azF zwTyzOg-9fB^8gFttB`@4541Jc1+xm`ebBZB?N$roLL^iG#Ql}fp|88V|KMbr%exz8 zCGG*21Hd6EE@y^ZesD6)6~tj68jJr};&rg8#@hvA^GwL*2Pe~w<0(5`IzgeA}n7KL`FT%VXz~74f z#UcAy>Fj5vv!CS?lkDFB-6pcv>f{;Ge7+eSb_e^-0Pp@FtzZp^>=4*ZE@}jmF$5+W z#{;f#466}fwFlUY6stYnaI9WQW0f&dAsSfC=nbOd@o$H~r-Iig1Wy;QMj*M7ienJXzH*+J;N%>t)ae$Pw}ebahpH(%|#?}qi3y6+@o_x&L-y6RzU7)MgB`);m0 zJ2t)dKE?Ik4c{YU#4cQ?z@vrefO^ip>|Xa#)Jsi{YEU`&SIIn?@l}@-HLSY+3p4BKdLt-=X8U9+`GgMgrUVM~K< zW&2jwNO6{LW)&LdbC5g_vz6*F8?_m$+xcX)>Zf?I-vMS0$nEL#vObi=>C-{eaBypM z$oF<<=x{9VKwy7N5e5>nzr`MEF4;Oj-2z_Kb^4{G)(N~mmU@^;;2?VfZ-hC4cZdy+ zF(>c_*dPUtC3vYMbumc#7@Vn(*dlKLOyCV@cY`$CQ+LQG7Nf>6qT(^C@nVo^corY( z@tr*+ov|3|El?19Eq{&|JraMPi-8U3mWcK##H(+m^U$EoJX68@|B1Rz=pSUFWBAb) zSMOor{1~W2b>Si?z-@cJg@ZJ-ce^Ft}hBx9DyTT=Xp82-<4U>SY98axA*v zkGzxrdZqE(FpUG#({MqX#(|zoqXl$-CF|@(_j`~9^k(+Ob~5IKDUAOEbMk$d%HZ@= zT+pU6*rgInybUo){Gk|3_^VUJ^mGO;Xc?+P~S_6HY#i1gt+`4Nrqv{tA0z))?bG?y>$0Q$IQfd%p!&G(1P_ zCp~__HE3SeQa)r7IsucxW(@%=H3kTp7q>Kg$ndl7UakgBP^|GNhu=K0L30qVc{eIp zUw#v~c;uo>_g2|U_b`$`uDNhtyu&^yGv4XU?A$b!S^9fcmMQ^T0+^Xk`PR&*+}G0Z z4~mlSL;+8zCgziQ4CmEe!w}8;4v)3a=6+A3&Rq+r*Qz_r#E-ZB3|NKnes~t|hQ}{G zZ|}As*sZna@Zr}0sQ%s2kZRY~z47jisNSDQVd$&V0PEIzUjw5rM#x~|u9u%unycG@ z&fD-+0iGU`#<8N6{?l&&kIIV8dPqtl-x&sgH zw@t~{*HxcV+c>Fgn+aiP6Zf0EO=*#2n+djEvdPsa*KAWVQ)m3R!iKKOCe+lFWn%I-_B# zt-`3cDYZD3S~O)p8+rJQrrMu1qQ3Q-h>bU~)Vfht4cOSp5pYuNv>3Jzz!u9UCZ|pu zJHB}ms1PGJV4T4L5YBLeBN<;;UpuyL9FPz&NT8`L?l z+tgTF3n(oad&r654=vB~=S$u*V^ zNU1@vA&=Cz&pjc5%ece>cQ*TW*H7@;T=(?_{E?HkUvzA2x zv20c?5-Vw0m_Zl;EH^Ej3In&PO=M&7rq?ZEldK|*WXVP<9pX1OPpYN(CV{cECE1(JC5s!c^buuznBee{KdV03Xa~kSeB|h4 zeKR>IFsQLHIl~1;y7Cq1$U0RKw3QBiIGRbNC-@N)la0w5)Ps?=%#Wc-HYMHr;7QF5 zVI>hXMC@PPTvy+;U)`j`(9FkjxE!`oYQv#keXJi7pz4Q&?AI?&)GgB zLWehz{;p>F^kj9NNw#Ln6xli)U=js^NYqw*k;U)67dpK?It0F7;su`H(Fjl%ua(kl&rQeFsJTG1lyf(+* zfdR#|^1%J3Fo5WN2x??EvT|Ff7Dv2ycWh|C< zWEtl}g6XtOoG{e?G_QJzuS9t=WyA34Jta!(hYp!^@2u?;XRIl7nHp?PQycP zZ8E5TKFi4lrDZ7zd~GgdEGTVTI4Ba8i+unZF!OqNKGZJ_*atYr{hu%nZ%xcfi77B} zsbxk>O%Q;9LI1~y_QL{!icu}Rr^q?aTH^hOsS;XLCl8~til;c zarGEWfb1|H>U-G$>HF0@>RSc~z8?Kuq;Fg0QRzHmKhUyo>hTE1LAvPC#`uEY1yrec zL5CChwzEmQ!1D`DS>W6Mt4nxoW+?65lGA@`wVk7iI034j#xX?mXjWLnyF zOuKVt+P83uIX_sAwB_J@!(FreA56F8ZOQyOd2lPACF`|hzLkkby3Zh^&YDY>Ys$_Quj^jF1Tri z39D6$+y}g)Z72`nTUS2tHjCb|wSTA=d^?K{&|@R!Gb?c;`{9fev1}A-4d`70e~1@M z;bVLMKzMay@gq)9R@MY81~GzxSVm41-ygsfz#~^f5;un2o2ddX&dR5DAn?cWt&bO+ z=F(yR}yNg#Pd`?=>^-RFzPo(j5g+nW(4b1+2?v# z^WQhL!XYP${4!+UHi)Vua5lEVMWZII=b3EnPXSTueIJJIn{JGZLl3zFC_SLF%lBc5 zd2CFjgFItr-N+9nmZV|r!b*~)W$xyGE`vP63!bySPVNVSL|)JOP?WhNn;8yFE3UlAA;2zMjKQem@F%1bn9dpUF+VVl3M_mYxW_)B z8VK7`qBhI*t4kF3tW9lNY~4s#?4m4LR$D7JQojH#^B##wP#iC|urm3jF%QIbM zc^?QZYgC_D4|P9%zaP7QUr%Q*%l13kup@Y0Yqo;EKbnsw%gZVc0$YDSoNt3zFz8RU zHhpOM50#j7-Zeyqc?ZU-kVTJuVcDVFqny5H`jpZZ9x|4=>A1BcGAzLJhf!PVKSD84 zk2cqvOxojM!FDmhMJ>iNOWs z3JNJkj2g-}Z?MNjk}bv_fyQaLWG_r@qoKduCw&y%Z-!FKjBYZ~cMBU{x|LB=r*N#Z z#vnW{1PIY46sK+YX5%232CXqv#CWUViP;7Lx^3-pfyQhP*6utuYra2~IoqC=KGD>9 z>AtfUZxg(vEXu-9G$su`g~7+R%LAp`@neTs$XJ`;a}CBoA9Zz4H%TNtVpIZSMlO0gEJ5dp z?#bwO!Q&Pi7`u|8RMrxzzXaoWsD|yBzq7IJ<%aN4OK4^&wj|RVxf9=zJGumm?*EcZ z^GH}2ypBGIBq<5LnC%R}-_gkZ6md#|w_w)HB+&1-GZ>MF_}=h&TYVW6!@*7w&V}@z ztAti)MAH_|)bJi}qFwqZ8*6?3-Ui_=BD4)&g3_3{zl(`}%S2xyOD^%^ML|yv@>K}7 z0gZgkz%$Z$*nEvM(S`@GyBXFCGO)Xv=!a6VOT1vHX|-f>YG074DF*e5Gm&2V;6_`` z;@btkYhgcv?$~c{aGoO0cER6~FSfZZyWg1TMIe>)Zcjb3nmUBxXMt|};L#2pFziI) zanoUy0m9hT1ZntVhfYY=Gm`*lOPVMJ&RSDn-_^5Mqdqf+e#BB^qn_igtUZ67Lu_lG z-7@P;<=K!UyUt8!bV=~MwQ*OK7BD(@odI@MO<7O$Ho+%Wfx+wYWK;v}>t=PTWDM5l z@Du2CgVy-)^P4Q3Gl|nK_;Ho*J|bslZk6Hjw9OnavefboQQ8C-!5vX^F8>|Q&B?GA zquT_ZIp;Mk#d)pykCI<&(*0!yY+WVXj0d@#(g&o;O1@)xGOb)=7 z+Q9Oh=e{QN8Ze(_S>|&eIkgF%k4)^=ES!x7XSuW5t*{)9Z4_H$IiENkgBxtJ&Y51% z=pxN8s~&7`e9EIHdY(0>_>RE?bDURnmY9!C7eBfl97g2qdNAe(cx2Ux`G&{Z43BV& z;qgQE`GP@t!`g|(t1a`Wa?!gk+Tgmu`N6<&CTQ1+|f@kx+r+qNo|{+w{k~sWOT>iCWmdu zQ3aQZ$ty0p%xGi?qoY0!2>%?IX)L%hOL``8vNIqh4aBLUUlc8Ps z(>`)}_;$g=Et$5HSfI@2Ou9|*8%^G*DYMfh8er>z?$)KYo1|VUW+IA%hh}9a(EoBI z&?b0O{pG0pKatUG12^zN99K+{#}{ntxfjofm^XTe)LQ zbf$6}8NHQJ*&PPwA`Pg}nJEd@&M{L@u+7isV46%GA>JwYTU#^j3Z#y~S*LO~Qoh34 z&EhMOSauD)i_u#s;2bryXbI4Z(eT(oVhN_%HU3piiTZ>m6R#vl>--^^uhG+muNhXW&3;Rcs;S4sHoOJF;#1;21Ab5kVe5}_pc#Py-t1)FD4 zm#LP-k5a~W3Rr4(=~g4J0v5tIc9v2SpvH0e4(-Mlb3#dMGq~VtFewT$+k)p9y$!sB zf6R28kyfPVv#(J;()1kM6E3>A?D8{=IV!~}L!Glv<6kSznZ1HxCi)WVr2J!z`&ee8 z(>e<%0Q8Mfj`H6n!MD+rgB{-UQ-)CH2ho(notk!*-P#s$2yqI7uk4Hh(65?pHVDAb zi-P++GeeAm_-za4Y1KaX94^QG8J-*M-*P>;>=?Xly&ecZ4O;-P{QDe7>f7GWkvD)K zyZNhRbWw11hSQ(N=xnF6%wXTIp-c*^_*W z(Oc-3w_7Gyrp=5o(P_pJ_9T&@wV@Y?mR7~HEvCawN+HE!ZjUTxKckkJ#MvqMGs@c5 zTTOZ7hfCkL5TP(Q-}+P^*9mdI&~qkwv5U3_xYk5F0}Ph7IP-Izw(iTt&tX72D`lcI zMQ2a96Q?BL>Km;)-Esr7z<}uD;Fq%)NcBVGZw6_$%`ISbw$0scu>Y%UZv8D1CbYS> z!5g8?L7LsnbQ!uE6-KG8ZUuhSkPD?J{bhu53sDM#g--9b7~e6`nWdC}o3JxWsk4a= z%b;EG;!Kv4d{2%UoSDQ=lPjy6wv;H@3Vw*uor4D=r}a;}l8!9d`OYr4OUU);3)ucD zs^?C@Z7pUpPOXM2IJ~2ssOT2Xr<_aTor2$hSc_ep*mIN1cM+C7BvkJ&;=u0-L2?ep zyhim?0cPiPNZ4tQ~bvMzRh5naww9fGO-^fv6Fvw&mGM97jg1m*nzh*9(+&jos9=YvMO=G};-HkPwBVjd-jU1e1*Ur)XN8SWOYaxzdy!|0{21P}f3`inoNam|%2}lB65M0EU@q-tCi-PW zYlUQYqo36#nyavCM(yI-W=59;Pg^E{L9f1lI9=Fft;`|`bgsR?*2l@?D8t9t4FP#( zY=q(tD5ZvfSYRn2K`9AH+Im38FS~6RY!ua}4UY zs8d1U$9g?~wPJC%$s2{x)9BVQt~GLWty@Zw#AM`v=NYnNF8(_EaKQ(zWc z_aXE!dK}DG*>xI-`G_kxyEkGrcQJbY^-_ndr>)K4tX(Oe2rKj;#HVk^8l%4G(tUf%=c~VtC)m zf%+eLc>Z5KQ)PEXsgJ;7~5P8frq zcE%ZLq#nk$1gYnwZB)vcXB%TqR@*@4F=s@l9CH>D53xdmHb3s*D+EhFE$fcK0)ST? zgEg-_v`iJ&*kiCZ7RO-urem;tF~?xdn~uTam8y$Q9H*be_RXp3@bELjsqf(lTH?8r z*2o*)%^_;UhnhwKAm>OhI=T#Sc-Gdk;4x;r+QZp+n}UP3IL~jQw{&|#2c#U? zb#i#1<+*yxC;N0R81Zy&DXxFR^vt{JR{w;39*m^BvCC!qvl{2(fT-OczRcj8U6}h3 zOKa_8IFZ&~|`aZsYg>^{~SCgx^rcN(2&yXjFgOd7iA4c$jAo3z3~6gT&H z%v+MsY$?UoO|A3${?*_<$Nw4ohZwa7JQtzkHqXb*2_r<>9RaoB0PWnhV)dyq3s&to zfzWK`N7RSKjJ-YMtG*!CY2rQ({7oZDA(3@*_3jpPd_q%9gGsUjHgcK{W#v0c;!ut| zdJ5nqn1_~ztM?{M??l6S>uPclMpIWQ~4nnBfL0+oxx$r!8X zKvNTE@3)x@XRQAeBPDm=?W$-`2zB{4gTt;aE~(C>h}*sa|y|=ZN`H$qZ1NE!C5+nrqpvWz5a)&TVV@{ zJBZ@j{jc#akzA|bm$tAm7k^LI{?}o|DF`0WnqsujawelovUUn%($>StLDyD2mzy~L z)sG<_L9C*HQ-sp7yD&aw7T7%gmoPJ0ZQv?J{OwfJp;u%nR^gpHvmFTkEj zbGjN;y)gJJ+|q>e6iZrYmD<}T`)(nXrjE8}{mfdtcx59}Z!d+bh5UiVi<372wIp~J z8IPwdK(A(Wdl{5X76!4kWJi7Tqm%uRsM*P$@GYX_;Z8v`W@t>(o|iLACo1IQhAju8M*S?L8|fR@-XmE0X4X(rz-)N7(eG>A(mGs4gR?ho%O5hnNiH;1Bc<5+ zky9g12`3YwD0l}-5S`V{LKB^>8`t~%(jY8w+4})aJnk!r|3XdN6;IbhNpNOl8@4~T zo;W4J2GkAPbf5rQ%yMWq-r;~Cw~Q_V$s+cfJ;l5HmVf8h$x zSlPej&d>11xv{(^l=p0wU`j3R+DFbsre6EVsX{jmhWGH=M~fF;`^d1Hd@MFRQpoH~ zB9L?kqmoRy&e6paXs&a#G0}C7hQGOok^J4{CgM|O84-#mHVuiH>op{GoY+Kk>ivue zrrghnS-{~V-Ia_AjC5BrDwK95qd{)QZK1gQ61Q-Upi(t+I(($LX%QG(nz2m1TM^4d zxu=)`a8u0YRa4A-wNuPC7E{c8(-bpb%oMYEV=)jK&J!cY8S}w_x)^tXDx$(!VGy{v zTf9bdw+QH($SqIcf(sWe@Dt9nf+%aA)kLJ;7@PBw>6H5tVYgepq83STHulK&UM%Rv z?h+;jeJ*#~8>4>8E)Q-#(MBhGU0^bdQx|(*LV@C$tyJp#AkOZqR}c?%^n6~gV+XY8 znX6mVinj&L<_0=liK5$PJnnSR_`vgLu@{LUuf0Ji0^rRz*O44VWOT{w=9lPAa}kmt ziaWy#_i<0$5xM5AAog}E@j=^eXN%P{gFe)6=QQgo*r*@-5>wn;U8&qA*UUP;O$7f! zdx5LQCrzDNzfH;fN3QZkS0rPl0)4O0<)U!HchsWsHItg?V(=PzTXfRW?pF>kvqd#% z7}porSe=+Uk+;I)g<75*-o?ynQqsv(6Iuk!0;7B##f^gY?1csynzw+J>k^$jGvZY> z(O?HtDsgiG4@}eE42pfRcD1ADPDsdXIVvF-gAuAW99tfs7#s-`emQ%&&_ z3Nn#b2b<8eWIZm^M>P&(nw#qCaizB|Ax0!?bLX8=$++5~NxN2<9CX#M7a&-bI>=7J z+NC+P47z_Qs_xoAX6w9aon{i+hkQOXd~h$YrOAMI?4or3h&dCxjShQ-12fl-sY-kE zoGSH&m_X1?D{%wH+`)9tEEI7?$+`tib`f#5IrAlvFa@DWhPc^U4ie8?Ply?0>uu)m zDlvG;jFl5vP0dymglB8TV`O&H#0jZBg1tvdE)OyXW?i=4|@~`G8wh? zkj-x5*PEsibH%AA;2sj&9%jLOgS`b${SC>oH{--g%dN`5pI7H5p}P8z|yNsc`!0(Amw72Q#Qf3TfEuYtg3Y_BSiGY$pvbZY~H#;<{I` ziP>vs*1%=fX~^5i6fLRGD}2SYz3J7V46Gr@{ILG0Ea+lmTaqYU6@Zes&?^7p0zeZlIWIAu6gk_UWof%BVo(;QtM(dRJ@Cs(h9WC=WKMUT4p8P-s=<}+ zB69hD=ZNi1t5PKdv)kU$`q%|G0iYEHM;ZIt0>3=d&*?=uT?1~pY>l%M`{X_no9oKz z*Vu8Cw)(5rq}d2O$X7&{q4(;?M*ipc{{KO)99jAA{Ag3UYeu=|cFoba0Ny#(^I~L? z>29qH{Sku{%^XCmH4dS#jrYyvYvCVIJ9B@8WEd#mZgYKFWV`|8{ItlqgayGKp(j?Q z!&m^dDXjU3v%C0Qw^m1GGzq9Fv5ZFsZ3Z+~(B43-jx>Q}qQrG=eFPF~d5OwLfbxfOJbhc)zC!KPfLJB_#33(~L{j8)7xer9b(c73Dr%*b=S5{m|9vN)Qkr06iYbSn*!Zp(h*m8>=)#6d%XS#w z#?84VwN~hZ+j)I8N(kE7NDRF3$_GB(GMh)g_4ZQCylRBbqfPd@5wxwXxSXy}vBbIi zP!G-U#?4f+))+OFjW^9u*t74W$wks=ZWK+V06JQ)yTtSrB#&pk)d2g=o(dvL zXLz$~CL)%mpg21}jyNm5Iv5T`9~bldV@QiGi47ueaWfgDK1dXVg1O_^XxV-WPpTdj zYRw3Uy|H=WQM0HJ3eiD<&8<5@iun-R{CzV^e+U_)lyJ=pDAgYXc6dn~__0W#O_p%&)ABu>w zmTx9zp*2+`o4o1}vTN-T^4rCov^TB%OYH^zcMw4{Xx8CC4y8Ve&ZEHZ#UQk+#W2lI zs>ay071U*`8YQf$N96&fXKz7*(c4Oew;4PzHiwe8HEs~gjn-v!ua*JfsG^KQZVT57 zvmPGIq^-c(*uZnloyn9qxar8m*75$xX%P-Y=bHhic6}qvnfbgALoq2 zpCMJ`(Yg=ujohG<$!_(w4@EKC9GFmUA>AC-uvTU!6`Ha(C8kRE zLYV}tXciH*4_$7?mMBQ<gZB}W~+XLXLkD;|?;puyIR(_bxExP#6 zt~1Nr^w=pJq8{ELE#4(9s%ZjMh85{$ z{U*xC&Q>2z3PA1lHrW<}<9JlB)EE=o@8xr7(e_i9GS(d`K z=!XzpNY1Y8QnPGE#&j?bKfufi%oztbe9Z&cm6KwSo`k{+!Tbm<<(2Uk=?7cr= zYVA_Jsh<*h4C7?fsc|^adyPJ-V~_nvb84~mh^#~HNb4|{>!(2}1^UjEv@(u$$S%|< zBT%@#-}Qj$LVZO&ARR{=PfL;Z-_$8%mVtAAjl?br9!2Y^QfX^G3&nSLlw%1|+J<*z zqfR63RD{(;D2_gMWKWxJG|`#oh4-2v`Tl6-3(knn^4g1Qs}05*YNjEF<##TlBT>yP zwG}$+<5&+7xoy_Rv0lj?{kdTsUaoJoAXP_4KjT4Hr*_EkNDKWf)Bj;f?wpxZl9jom z-(_@B^qH>QlC;bI)Lr4f7UP_ zWpqh&_Pbq=?O?Q(o?Y@?xeceMKl z8vl3j93>onhO8vVaqmWh<8E(fo7B%F>#FH9#3;!+`Te?yPCscK59X_#O|7EawSYcu zkG@CuogQYQztyo~MBCGV^(OipTSc7^a~YjtG;v(4>B{%&Jaf#f#0wfsUlwoq?hO{W zezqB0$s6v#4pQX7aHt#CGRhX4dZv=`!8SN`g)Bgr+7Uo$L?6Eg)4% zczM2Sn4U7sLZzpCd;p&r%lb^x&@+{G&b#HO8yIiclFPu|8yfJac|$dFUzvd~vv-5{ zI}6-81Ov?EHmIO2+ZW!GO$}Pz)T>+7((lRJgTILprc~I3AYb=sQHwoiC~@UX8mx7S zS-0LUJR7MN1oz9aM#cR&ql@g75-{}w-V5OL-z%k@Air&74mn4hE`WvDRw#mvF)-eA zD>?6oKEk*`?T^a1BzVm3ER2q`zfQ{bTRYCa*SWxo+i!3pS$i2lg^4~({WNKQQ0TjE zC#)#zD?(2hk}sU3Ma}|)kHQZhP~+-Ds2ig88r_haTG$b@SqH>UDWDU_&JyylgXzFZ z4tZ0RT688qcD^!kgHvES11Jtm@8TX%mk6qiysL*{w6^FY{{#E}`KUA)$`Jvl;rH$mpWz zdv$GbXOQ(pj`C8XY^8NtO&nYeZck+cJ^N;o?JgD{cQc8*ntdpv+l3lnN}7*}Y;#{) z5evsv-W7&Z=4BG=7`@fn*KdAKG3rgUyYwCsoe^Gw;M$LS4UW43A9{QBbTgcWY<4id zo;djW7Rub_z>X|og>PFo32fU?jk*Nb1p&uqns?BYLu;(exnt`q48mruJ<2F>MNtQ1 zmg@}cy$Gg?+KtM>1J_$1tkL!jv(5NlV}t|vHOKd7^PVCZ`2fSM>M{w=?@O^ZxcgLA zGP)o*3+Lul1U#aP8M4icZl85?!}tYal37z`n&@n$1a)jUw&d>Dk}MuvU`MWgf0K5v z%N4yy(U26278GfjiN4ZSCI3(pz1~FMEEPk8cJgUAe1lp0ZdL0$2Fp(Or&xv9s`>6h zBvTli37eLHMZsJ8Dv8wKrd}2)zu6gs*!M;kIM|`&)x?em??DtfH;um3Pt}~C9j0p5 z0WMl`!#M=gRqpmvCEdebi6)9<5}NG*g=RpaEn}y1P@HdW43N8kcbD*ejAoYnQsR7~ zN~_iZP5mP689=C z%hSvOIVGUp({^Dk?{}q$ztD{hLQvw!38yDOe8x=Eyr^{(g6uo(p-qu2PU?50m?qtFlCuJRPY10t~^Aa(bs_K@y zCADT`ul$Q+b$8XPSFc{Zt6o*nm&ntiSLjRRF}kmyFG+|p{<7voxfj`oGyV}sY6kv{ zYDCFft-kL2_b6Z};d4$tb?X`Y7hTKU>N4de6l`s|Z`s&(kd3e8MqFy_rq^_8HZ{T?5&!RA#Ks)5LqB?XHVFHfJUJniDrEIX z9Siap>gJi0hNIZ#;A?>PSj3yp^R!6clri6L(#LVj%zT5A= zk*_1U8@`IlTZ-<)hFeO(KlBC$?Vc?CNcr0B(NpR5U#R2LSq%ZY3RW`*A|H#_sCjr# zk@^NR{n4~>>on3lx271wQDP!>kUGq?N7G^npVOp**r@Rsl2E6R<~~&()5JQBL+bns zCMDYrY9H$T6m^}$)gLRPzKG(VBDWO*-Wk#t+(tFRQ-_z1lt|b@ZJTMv@Sts9wDZ!@X~r+8`I+p`r_YJ6V7b?gDE%Ss0cMd>6qnLVI{YpAE|y2(GyJqKVurKv z#wl`YB@?tinRaBc3B>sXr^}@k*7{69jK|4xB?u$UMB?i zJn7`8_|CJY!_RJNN$GR`8JL_Cy5eDneC^J*ntbifwin~|$)C}-X^X2EZuPFn)`sP4 zx3wAh+HLJ`@Y=lH#WHru-qgP5=c!=iZ5;nkLUwQCIQQL*o-54{$=9w_dknqSGKMuN z%NX|Q7$--@>?cma(hDb(m{&BQ?#a@a#MiX0Xt;n{V^OEY0WXZgN^T_%-uwadNs9wa zCUx25H|g~$z8!V)w|M=F9V6T<^-$^)E8m16tCMi@1X<)N8HICYVD|HTWA(3$b};$r zl44-((>Ih90f*@qfAH_#01JQYv3`Mj(XUgf=F?8va4lhnnR3d>uRB?mfK129urzmm z{OAq;b`m|ItK_MF0-K{Zh!^4icDnsVR^F=dpti-EZ#d_CC11O!QC{LrAM=I61!JQV zqt|6Fs@Dqiiq46i!N0>7@`2NKLK_kmt;}kA#*sO6CJla4n$>qd2EKmzS$BYq1O59b zd3YKI_S6jzpuQmWg(9c7MrV*VjX#eooh1JA%SgmNkjov-FHKb%nQ?KKub3}JhQ&jy z7vrO10WbDnNi}i{)mlS5%YOz0ag+bFw_2GO-?bk3)#+bF3s+T+mZw+5gZTIC;`B<+ z6AS#Xe~ahMSC+lPv16Id9x@jfy@h;z0Y50AEjzuZAYLI0?g>hcQjsZq%N7>1xnWQI zw=9wwHgh90V|$SbI#rpg?#@?c7mKyY;p~by=WB$|{JPP znk?0_2lwRaN5tPHs+Jj&^>&pOi(IjEWOY@X{`Djs*}Q#rmdKD@J#yrTcywf)k;$X^ z+T5_B(oUk%yAl*+a(*L8K_cdVCn-pzJm==9e%Uw<59J#9T4QImyi{7uH%isY;Yz8I zU0qmMT@X)60APaaPflgCtNU}_1QXy;ex9h?7^QUDDf~;ByHog_8RhTW8{o;A6*nhnd3bcXRxK3kbsyZ}3T=EX&N3=7ZLNxNTqzRfUxz%y zZX3D8EgTa!bkxF*<+-_HElD3RZ&$^iJ8DFpua%?`nqMN1BlF{EHJ+`^G9QQ=Rq3($ zYIT0G=*?hT&NWN<*>Re8Qo=BP37vubai@ajNnR(sa4Id5Gz^YVQ|>gDp%U> zP3>9D?LbwO-r`NIP7_ZLn@D7HlO6#?(OlNc?V+-{10EL)cMh|}5Ze zm>~x}DWwU?Tw?+`{P{(kBu7Y7OP4b`gvihi?0`Cm3nRci;(p&sPBI>lOJs$RazHcC z4a^1CsUo??@Q8PC#-m$CJkGlJ^QryWz6Zo{RD(^ZY!(-`L|2Lsb<>N>b^5E`Z@pT40oOA`mQ_Wr7PH@VOXO!~^|)`PjS^e& zq6SvkS})>2xM~Gp4}H(Av?K+V1UuCzmbXgnd?-J&SVWCie&4M@=hdjroQoeTa4VY^6C#}muNpHuo5E!^ZW4TtmTI~8a?nq!y z4v%@eVNvonpr(=`S0{03NjnH)CGA=ZJ2DO+1?#y954<$wL^L zQHaNogyI$hp@8Ecni<9=9@W{63Zf$WV$7(%=}hrjnmbdT_>9Cc&`{IO)Q3;cq1FT) zWrDH-xDPHZfsWU}q4wjUXIewY#OC=#3T-ETqZi)PA@9%-W>KYA&2eYm9)gaRnZ>gF z{mb|so<+M^c?J{W&bSDrSt)bkJRH}Zgv;%#&Mw2=!6ds{%;0X4D{5dsGIa9|@qIko zHS#NYSm4kD<`k=6E$)hYO6-!*0A?9zgQtbc(D5z7JpD-M`%%J0$_uiK0AmL(7`fnK z`*7SqPHM1?C3pf?lo|`hyRGp5_Ai%biZ#^O`Co1grTeC<*cVo;T`eBdtb~MdwYU)l znCDV4D3+F>w!Yd9NJF+#-QVfD6Ix3J=3u-*<4PoOzimI#beQn=c{9cNQU%T39e0>; z?<1S@adY892ib7(!h(CXgd2$E%!qZj+*F)ZkY(5P@45!Vj_p@fFY^hSC1d}NxP*nM zlAt_K<*_!(lSkf=<-4N}B-w~3KA5~|P!x4Cp7?1Sb=YP?owIQZ3`j!u)3A3rrf0Rr z8y90KO6o8J?#PVH7aQ0rOveSFGN^Ew1lgPlu=|+{o|T{=kMpNwL243JX(Vs?8$FVE z!(;UT>kW;@f_xK)hos$&W_3wXPkhUI$t3f{slRPPF=!%2FoGnP%Q^-sQ6_cV+zEV7 z)1sC(C0OP<*+@_-7M>cyF)cM-)hUFLVr3Q-|EMBOPx?8EZPMgCZ2TS5Kc19qTbHk?`od(5LS*H0bT6yAd$05vvSqVRPQ1^L{FED;v}HHOD)a2~2~uW7UeAT4}|+LSOX$j{Dun}j(KKWF2_}HN{Y8sE0v4cvZ(%v$JkJiF*f>`vqm$% zeFky$m28%cueV8X5k_5diA&&ay*ws$j7epf>_`0f9!h?t7^3jsAvSO%f_N_Dn4 z#nIIV%9ijs($$O$)dQ*(Ym4-3eFHV`HzpTUVxZf;tP-JEXW>Y+5A?5mpuOvXGLT>E zH`P7<3gjmlWM+)kBlNPtJ|qIDC8usEBrZZ81Eym0`J&ZQd;~*!e3cXdvT%B>1V|IX zAKsWQv%XysKk1|3v~rvAg#y;VX0hT7FI3!R1G7LcW!`2Wu~?jIj>BPz zLz2l!_M%#VA`Sc8PHOUW`>g7>Io-%))Qejy;?^fi%0!l6Zn#XAXoa`IO;XYL(+8qk zwI=7u^Ni+N)bbbLWUYTywW23Pd(ly&Rmu6A4bJ~#+vpLC-xZgXvIXR`o*64{550I^ z-j-r(u}u3k-B@6q(S%AKZS;ebL~Q9e!IJuNs$iK-?{TUcgbp#479wz4Y62(-7e@36 z#{NIISC5fC`4-Vg`)x_A0R4Pfvc)W{XE)Clkwj2kg&3@htGEJ5yh-^MfifNNTwynb zJd_Xn2t=*M`_njyiYD0`WpMtxZX$Aa#6fP)-aPX(MbGZdHQ(GE)-ZdAzcLMTx~1;1 zR}dS3Xs;NWVKr@j(z+yqYMW2j%-E6_e0fHWQPiMQVuYt*MH0;i;f`+PdX_&DkJCUU zeY6D`&d!4RfY(6r(yU+kA9UpuFjCb0xCYqF&Yv(*Sc6m!PMn#%QonbWnI(dLjyVmgM`S6xX&3<^cYsCS%!6`C(8t5 z*8=}8vx;P=U%G}3GNryBMTMpd4*tAOlzAgPyE~s5LFcqu;E{ZoY@zum(fU?+{DKdTTX zu@SO8+X0597h00TN=KenLBttnIeH>2Ynr!b_`EDU_eo$Z09(`wd|p}!qC_4ao#i-m z;xY$ZUZT-{gF-8Av_pBqptejTad`-BB;%oGP?>l3X&`>?zB6Z<)%G3C=Sv8! ztFc;nhb-BgPh)9oxLt4`f5h-Czbg&`aRyP8+_F(hi4?nUtEkv60k}&GvUc+>AqU41 zF7>i_nQqf@pnb0qyz2-J)cRvwEgsM;ZUW~BiSCaNRE`+Be0|d*+^09l&&JaDdYNvS zQOeu9rr}kV%icA`)$Bn`__Fv-2*Joyj3)N07FJ})J63eN`p?Ma*v&Q|G`Lz5@0NGa#On+oQ||%Y0wEt{N&< z5Yz4^DzXD00rz>lMqe*Iik9fFOW1a3^0sF~LMq1XW(cA7CWYD>g$DLeY~Tm|Lyn<9 z7?Zt}g(QCX9hI_Cn@x>L8y&(xw20;Iki8Sd_d8;exf1V0$UAq(byY{J>;@x~qT zJc+fAOPJaq8hsu}KeRy8-yXW8^WPt+EDzK-Lez5;bb9X7vaY?`owY1BNzq%Mmb57a z*L-dI2#JI-sT^W78S{X(LPAm-6^^tHiDuu!XKK8p4-jFU`#pbH6067%3EI;WFuqu? z-uo_^A=a?v5J&7;4=kAS&sHtFE^Is0aD;xqi?--!%8cZ^k%b+2O@8{?=OudUJykGu zv0?D;f3YsSLV{zrC$h42(slmoDK8|i%TR^V@2*)F;_yBo5a$Nj`nvLo5ySvSA7YHG zAjWzO6a_@~a6sf1=eP!pqjj{AAT2rDkZV)88l-@&dnmr;=0Ux<)opg7F+;X^;^nfP zm?&XVfSWxb?kXpIbka;5AlIFU%=0ulZe@|lpiX2|R)ty7;rZU6 zn4ER@o=tM|$uWzw_@AtF)TW2zwO? zvIZjkm;@N3BmC?dtz8fR_?oI2tqlAc?wp9s$1!lWGALU}eYKi}E^PbDRt(x6BCl!7-d0&Aq#&41Unu+oDa0|=pQ#M-inzELdOD?tFBXLQk%<|VG z@@o=#O(7eZ*=q=9Ym{p(BCEA%Q=yagUV1ZBi^&)PjqIxMzC&gVjNq z8rfW?Qj<5~aj$ujW%zG>DT+h+dX?%=J zu2H;p3Oj!@nZiQ8h9mKKQ*D8c<||}rYkzJ=?gFNr{G_o;vJ0ee%vC6L#vUZE_=4_a zr%@Ag)sf7GEzyyx+n&qBG-j;hUdK*Wvmsg;MsMMAT`l3F<-~Inj0qQv36^XAOI}3JGfXbKNM@@w^xx^pCnuMMfZy2@- zMLigJXMv_D3Q@#pCr6BaB+&-Pv@IZ_08fK>b!SUQ`ps&_#}ZRiv!`Nz zjPN65iz)R*Nv$WP3e;C*ua;oDR%G$yQ99{6Yt|q_04qY=uT#Zi^+*Y0)@4HyWb-NJ z2W7BEs-u}%bH5MbFGU7|l65+Xd^)kfg=p_~4TMisK%`c@vt!z%fCWt(T8Uu-V72^G zPyC0($KXYC$yrAMO`>+5GZLK^cK-dDTh;^vCl`Vov}JC-eWoy3RgUgXPkdbVB_%1O zhY2U`-17llDzFr}C(xmRj)k*O?;{kYY6en&E0O9%$E8RyQ#F22V$@ucq1|5R|2uJt z-IxZy@1xN}ti?&AB@7Rp*FRH$*Vb>)2(@iKrN~+wPWXC1VB$v&&Oe_A%HZ%Q<8M#z z`8*KA(0W?;DU%VZFDLO_XW8Ieg^j*IM_InlQ^@q|AnNl4Kj`vWxrBVcHDIkv%>xPz z?xI^dqQX)*QwuSTOz6ZfL;)^Ccz%CwzdW;W8s?e!Q(v&csB-4+u_>8>MpQREg*{=vn`Bn&V~W!tK14+$OOEB!M@j*X23 zky9^_3>3)YwRF9-;GZbum-2;DV-+6iZix|n3S1l$DRa1Vy6k>1bjxjyl)1qnIoza@ ziL&A7uze#@bV<=={W&+h7WZt4TY#Fozi-X0)JtFke!|6Sy{3A`UW$t*24w|{-}ep5 z(4B`u-O>Hr#p#P9dl{40ty$N#C;EA}hvbR2*CA}|ie$eIe9GPIQ)Z=4c?sDe zXO1LdhIrrEV&q7CHAyC9lfj@TbIz>>cBDrW+Echcw@cE5ud!+G+0m|tWzG}V9HHdz_qq-Au5(GeA&T;dU#=Za3Edq=>t18b zSHW2Hj8wiq^Tsu6l8vRG%I36H&6|LN?wa0y?w%YD0RG8hu z7pc~k3v`$;Z9%3z_f?62(Twhjiuohc_XTYr8OTt(y>V&}!suoR%a9(OnO9^aSOcKl z47_g{Z_>Mc1NCLDb10katj+GQL|MtPhDwi+ROZ@FiDbW$QX4c!Tv{fw@wzA7lxYNY zx7aYvM$kTK1RWre{3*9n&E{>qCq8Vu_v}xeHQ}o4csN529NKzvNWoF@+IE4k5Nt>o zf!s^2t47Wq_L96Zz8xK&ur?FKcghXSI8lhrQ2XD|gwjo<6Hc*Jd=RXW9e`maZnMRa zG<4TjtY9&%7(5iRU{do~9|~>{N(jI8y{#to$vP1ntf&8?w!n zcepqaeFLMs^T39~c4KSXvdLHQBA&Qrt+!$vAaeSHoB>FrzC%(X9p@#1&OhjhNf+hL zbbgQez=lFmqSzxt0hkZlzN5Ar=tE2AQL1nl5`lfOhwY0!Qn%ATq*W<9iS+8Hxg+V> z7eL)fs@DdGdnTE+^@}W^3C7Su9Uc-sOi9%74`F^lVSfI$^v#kUV!u~oPgK8-aW~B< zN0{qqN4)FcIA=(aUJ)OV;E8q}`c(+6(7thNIhgjc2SL_AqfRk&w4Nm zu$0@O{m428;kur*z;5zKH0aSqtk<_O9MP-a?C##`AxseGi039JpL31#WQ`WaYBFHf z&Kq2f`cQ^zdIq7hN1C8TdqW}0q%0caeQO_-NCtw-<^4EN+|^UJCm{|Oy$_1oSCK4M z^Rx2a<<;^`b&>BNsm{&Siw)co^Vd5PK_0^Vdz~!W!dN5Slz1V^verH)VK*PkHbL9w z+AVhK$(ClW16gK4JEGW&Mj-ggFqM&$@H`2=8|o7cyNcafL-uZoP!~g%0zLu#$yxkm zw?hDo96cXci0vLA=&;9SlU?#)inuARqC*pWtj0XAP+eNZ1&f7ij^=B#93*OeuX1)= z)@WllxVHExDwLf*;a{+%R*pFil`!As&UC znC)3L3d6&0BXCX2c^Mny_}GNR!K+y3miI13MvMFsRix`Y!`>2!}rTijJAD`t2tXFB1r&E~xj3itQ zO)_K!<4_C4WE!hWMRDdOl77QOQP{ROzNwJYz*JnRksEDB%(X|5hTOmrmfs;*e&WZB z>i)pqOPr*W!Vr`BNn!-c++;OSUKL4_d+cVR7ijABiUy9W;9pssGu0<3Wk#$3C3%8a zoxjvY&orBOpl~#o_Cga~XT`}p_C;>vvSn%#(P|_)(DiO&RW?oThY}T?N?K$V>*MAj zGU@6#h^R?_{)-=SOPz>JQ^S@i@fU!11b}y5s$@wzvM@g+pQQTt5_p_^NFkaX2!Oxan@9kp&}PI8 zM*#T?Zu{m8Z+m8kr!uWE-)f)hiq97-IBT;}%oPibQnfN!;(zkBRe0jZ_NyU=2W{KG zJu$^0#YSvzP}}D8AyG6|Tkv}+e3{$liEncky0_rdP=$!AN^2T{Vyw7N98lCV(l)!eN#W7btynwc3{7Qg;*&3v^lIWxtP6(X zfJUmh;$AbvXlM=UX+8fEMiK5q&n7_cBvKhiMzM`~S-`KSA544R^iCL+X-N*#{y>2K zyh6Xn)2+vSGWg7%rEk^Ui?)}$pA2i)G4VgTFTI@Hz3mV2pD|WiChW~A(*^Dng#we9 zgQHw9aqIz?iutahw(9)2?2EX47#2gyE=?~S`zzfwoT4OcUOx&ul-)ZslvN?-b^?s1 z+bcCW$A7cr`-+DglJZE=Cpm%iIsn3tZcO=g3g&Q#^Ehyu8nx`X`wP`-X2K5G$$?L$ zW8;UF5=X2&+=z!$U^{JwV+KK3o;V!`&WOiwgPjD=qP*N;+LEOgh`fAdR?ePjc7eeR zWypHOdj2!jMmHnGRjjShEtA>H)w~TecgAM!1cEk&BwRYsLpls{f#ErfAADVAnFKg= zZ_!c|N}?c_75=DDXfbux1221~ZNEZJgUFe-9_WduDvZdNjhvWkg;gL5c|7$z{w(8% zc^u98NIsV8$EIMLt|9ZS`B6s z&cA~3OG|_C5n{Zcsg?>TJs~n}6grI~{Iu+YbGR?KN~s>b9iuZvt-{4Rx|y`~$H~^$ zAf7x-NfyE4syDM-xTe^sL;2qQVN8KFz(EDmab?OLn49tcd3T zq@CM^4GAiiyf44Bt6JM$D=d^6h%PVJiij!S{^6PrdgeO7B}zSx{76Q-V~->3rD^Lu zHe1)?U)lxpT>)-T%WJ?N_ z78)+O|Bya@wx)ZBLN-#)716V6k59My9 z=U|2zCAD=ZB$P?NRIWpPNMYVWc6D!Pu32|%QFrTrxLa%EJoiu-xXaVuSEH3yw5f+f<19+=$;%0Iyb1%mJGL-*w(8sQCk*Uf ziLdKHYGx+&wPf2*$p)qd(!$^#GksuHwGoCu7?5(aW%jA~bw6MqAHA@VzNEk;kBb~Y z{@CeLsh>E|Jh<`0enHWUeQZ~;4;&;dw#+&-G-Dquxl_oW<&elj(UKh!p>WU@rN+X} zYNb)EH0lTDc9j+>7;;(BEm~b};4<(WZ;Mlf1pb_Nkq{fi<4Pxq8{aF$#V-f<7dZr*v=DLhHr={^!wcsR53@UG7W0&qco+oCt}ZOBE{In~0m=-U7;=Mu zl;w2ZwYIKK#TGDZy8W4|xU|^(&QB823=ZdhjN0;&?1T6Lxj;O=;GLU6Q#9d!10dFbiuPJC^lLEl@zpfp{_(_JPs0 z-waSfTm`;3S@^~<#8nMVmZr-#6x>k{X&K_yFH@r}k)#{Jv5Q?622fpEEZF6Od&f~a zK!*6vuktCoN`gs?>Z&LI&2@7PgEq1Cbx*DSo%i?Lzjc|1vvdLEo^?ckr`ii70>)H} zN^Pb6A;IYvby{VpBN+waSF+aoRW~9E`6qY(sCgN63imBu?v6&}a#Isz^wERE5=zh` zK1&%L7&gxa^!w1kkjv)Kk9=^BA;0bkmTY1n0bxsoD{;d>YRK zC4dNe`0V&tS;VnT!zLj_1kPu9bqFKpe3rOjw>ADS8T?Rczm;GO2_8B6alnm!8Vt_E zcDGkCDO-0lpCd_#ZXTAUje8Gbn^x>>^1zCO5;J_UkRxX;5AO>)Mw9*VTD~$5Gj*X@ zxCYDJ9}fZZ89So%?^NlS&PC5xbxrf8SM!({qo!vo-+}z_rk{uN4Iq5I{N5X;Q7C@U z#UXaI6d!O8{;Sj4vV-^i2Spzw2Fzfib+HIDOyA$NN@gCJayZcnX|*|AbW~_$=qMfd zL@2cg$7X7k)ekvIi#A3xr&qv7(WRhqk>@xNit zDm){+o&eu$@mc~4gZCk?Q5{rHnx)W$$ibvD05{Ll4C|JYXFfHit`{{!r?wM zn{af&SwOhw%f^}$F0MUAPgvw%2|%@^Su8|JN^C~l{o16YjD@L5Oy_>jh}TNM!8~6( z9~Xw{G)%Op^ML5gP5x%?Lm&4bZYDhBRnbu$l&dPfF0nUcgjqqeJgVu?Tj%#jC=26% zLM@tsD?NSTe65n<)pC<0ZTMDD&wCvYZ!ijUSYfnqO>mST&H|cafV4?yvRH4_s;jYw zf;4Fj^O$xVA%X7UI`JY_D2h*mCbT@(7p&jSLIYI%MKA z7Y%RCxU)dHOINBEgF;+_@IE_zaGg9^3pKE$Br71Cfbi1pHz}4*^8|=yb`_oaD;tMB zi-i2=apS_E)+aX%1s75`mEm?t@66PRgC23ORYa z;6vU_G;V*G+i2f1Yr$rcHj>L;r<*s~AQm>nTfil!yhU+|x!j}zYy0ePfn)D4LM+P? z&Bb;g70GFnQ330IED8-!bm2V`k|F+j=KHCe=_wp?hqrR?jaIwz|BO5DcS^{12(#Ie z{HVLOeYjFD%~y)ENmjR4#fL+^2q}EpnP+<{zH0zFsDZIG@=qnuRzsT%rmdGZzD@C) zZ&`I7CSx6KT+y*TP1tlpU!hjS;Z6QU8<0kSlVTHFcrO#(mVtZX8X2D;fv=OJwHp$G z;_WF?EqnqVS~#j(7%AtMf+3+9Ylqno$b=vu6Dg}Rt$-uKWN(lVY>v-_lVLV*pPi*7 zhwSQ+BS*xkH@Y*h9o~&|z_J!gpD)|$jHMGaWpnzBY>lEeYUTxGjW-g(5LTQx(sgtv zz!TS?$%uJ@!T)scTTU=tCTY3=n;q|HHC^IHG8yT^vj`3Uvu;?&vyG<-)71o6N>doaB>*|!-kB6G?4!jUI5k=v~ zG(Y>J!xkuHDQ!D8@tux5Lbcmb-gXm`IW3A;>%MiGQr)~A47$-b7}R|H$G>xf#^z}N zP|=1FUpEp{WE0SoG!l5VF0TJ)HTm^kXdmj9rRDkpA9yZHzDJg{r$>aw+A>+3gTk6D z)wtdf@dvJGa&?^1%@((~>$@cPw9E5x&;t)1#sZO0M!{7G++n4hLL&KMLU+xJ>s5nY z%$W``piCjdUvdcXGd&tnC+fs;Tsjzu%)6C5>2%I@jt4P`K^#3Pk1|Haq>ODrN6gxt zvl;U5dD0?U^Ox^&W^K}D?MV$tha{xfS)Cb@)0#4=d0v-}tgecU$N&##e}}DDTJ8*{ z9mV9FEFAxC7|P+L*o>5Hs1#>mZ)Xde&Tr0&BT3O2UK4Z#{#=Z|8^qZCHi##jBE&@} z3&ECyIEr6~>-qVjcpbefHkOwTEKz=WUEB`^%lT4;s*6)jqWbSVe}j0}X+r$|R7vl# z`D%3@dDt^>xvp8t&yI6~`y|rrvHB`D-lBCWT6->t-K|SyMj7TTHVKsGO2t}sdO>_H zrpnm9d|{zF71SxA(*1!q@kkS#hsO|;TY*~_R6A5v+3M4K7N!d&rb8K2+90kS*&v>O zJ0X5@h7kL&++D3VcC0pv2Wqp$nz+xIvYl}tL8u>YlooNXK(W4AVx_6GFSeYd&)>zB zX5l>VJh&5dE{m&f9n%+8e&6I6mw$0w`OJ`PAh&xvdWROi z5m#eeR>*P$J;NltBENVI7@=Gim9t{-%VZ88o|?qAi8A8XSz?1P$5q*4m%sWpF-^cT z#?F$F;cQ8~Ca&xV5sI+*b6m+$rVtgq;C7-)EQQ!RQ{dRl^on@) z*)ae!!$h<;q23hew$BW;4U=Zvt$4znwrhj<{w;;L1qq%5x!h5NJ1UKlVR0c| z?7vbyi_7`Ro{#^iWK{^UjLA(>WKHjynZ}Al1l1@xL3}?MD4w@5ptdU!)*36G+WBNf zX?{OGQPIrU<+$a9=lS&EL%3H2tozkOHMblr=4VON)w9zp>0@Fx8Kj|IrOK=VQ#yD|jGf+u>#V`ej1n`;YCCbaZT8ra z8S$1xRfmUys^g>E8wdDGw(K!+XK-UAzuPVI<_0Py*lQWx4c~Pg~s%7#{X!( z&t&#r8K}MQGMtfNzbM107w5|ucrAZutTPkgmo})op=&^C!UpBAjLSndh$|(CQ5!^E z9_Y_l3Pt6V(Hn(9M zTGkq+TG`V40^1LmRH*g?S*>|n5LINk4k2Jv%2z5_kIjlRlw=y7#jRj-!!?PNfgmj3 zP^E)$m00}LVoUL%SFxpNH|1O7N@Yf1g339T!#zyd1@XPuDx;`^=roS>tgAXSf)BZh zX3ATy+8}=U10kOEbYdyQ#O81l*7QOSA;$&r3-vOCBYS6ybn*4f@|;H(Uzf$*o56+Q`m`$D=&Kn3C6h?JZX38w=uI&*qjLC~yj_9k-Z!*wPfNMYLFW%(Tut zr9f*I8GbaA134m1xDk%y4y#SeSuv9gH`-*j=D58=;)ZxLj&<6Lh;y?|xuMWNX!oyu z?`&4?zeRlhUu6^Nw}2fUC2`N^NS5mXc-o@7LxPXtE6BZTN=yDOY2s^e#ug?K4h$@3 z#Xlv3!xKswNGsx$8q1BJ9%{Gk z@MirXlRtTISHznXoDPA=6cyUnO-Oy4fmyqk+r>)v#@ft$;R_VkrY*6EzbU_bObS9u z1(w;*m&+Ezi*hzMM@%+(wk-Ht3m=m*V6_+GB@(QIoy)eFAtg6>{Sgv_cKZ%_ znw!OGFN~OE85hcCl6(Xq3}96PXg;x4<&3#0z8ssfzm(-z9AT_hL`ELg!7um>hRe(s zG#Q^IU&NpQenkxXTSjFz6mp(8^~HKp0$1Jz?ujeZJ7Sbcwmbb3ELIP$PV0i00WXpz zo439o3Gns!a!i2Rsgo@7Q>aJ;xXgIL1i03GK?HcSd=Z0!B*34H%9;T8;UgpVgT&k< z3VLFnd`DBoPF!B#V%f|HE>+0AMJoFdX^snacj}*h;2CF3@LF0xt&+0DtQPRE8aGkp7ei5N^BHHr4P)M?6)LB6fAfGrskX8UEbHOz9ork@zF;&h)JN4o~4DDXlBsJA50 zhYNhfr8l3jk(dfdV)jXAr3jGI7%_4%a*$7v8JjK5%@q-EEy#QtGV$oh^ZiJ1=aV>S zky41iXT{XT=;&i5_DV=GBu(7w$SzB!g6q(bjRk~B4A)Ay<_@WO1+AwerswWcWn(>Ui%*#NoTfDLLZvmxdR95j z{!8C7`GrqY|C?1IWINr_KTgH=$HP|E1O1ZCEK}xaO;$xdO) z11bJdf@wutb0-GQ`C1icze@TTiL6^mY3wvAw5}!1K$LrjEN5U8p?YBL`f^)a-m)e5 zMc>vfY0{_?{&1c0YB`?mx1r?g76~AP0}G*&{AinGeyqn2ej&_$V5tC?UB+NTLD^6c zi8Lw_i2T?RLdvwbiKscf55=Fe)8+E?jQG}TxUWsH;RRNLh&^HAYI)850LO-RD0YW~00MaY%iu6LMg9NM)EB(iXPsuEH_STfrZL$eM6GwkA24gvdsi<%p1 z?AVhUv7>o1uCPJwNhZ%TEe{O`9dZZ@Mkx}Cu}%IwwNV8*F0XAIofnM?eA123|jI^BX5f7I=DDTxd`y$IUNDoZD(v9 zI_d!n+;ZO|A3M^hY*}aLc4gb z!~#R^U`hEPX#7U?-C(PT`PIoBF>Hi#3Bj6@_?c|0r%f&G<)*g1nTc;S)iaN!QcmWrdSLv; z6XuMh@Q7J??Zr7z=aCX_bA(-0vsxA}@NtRMX%m(Uro{QG_;l-9bcUkdkV$JO$R(Vn zv-`!ldhfd})*vzWc}vwL-dM-G^;SeSy6A*1hH;(Jx zf{1&{@rHP7Om0axszwRgJxwDHSU0^|f-vHaZph(@%}OZe`c>+C2UzF`;^*CdaP)rx zd=&ENJ2@dbe7N?Rayxgp*E&*vfh-WyrwGEbp|D?pZ@;>x$gI8y*IBi!VN9eiXBuv{|bXcV(Y zAg)Y>M=Agz{LH}l<7J`t2TmCY?+9brD=Wc7`!p z;vNxaJYd1>3F)?CoaX?`Ftomo$pj-0&3+@@W^v96T_H`!u6VPh`cl48SU^mwnODvZ z=SmX<;l1qi5dSof9N2o38u4_-LY=YF6^7#d>Ea} zGZ~YbQuaZ5IN*rS%Be~4X_>b6{$lXsD;PdIGr`$wNl_-#FF1VbYZ6scW;>*SC1c95 zKgE%b%G(lX>RmiW&8dwla0^z%(_}%|r}PGw$ne%|8CiXs{6zBEpM>{ojcz(>fRq-lCaV{58ds#Q6c2j8qF*` zNo&~}g?c`mZiDjj5U8*%A7HX2Q(>|eR9H>cf~hPIBLy?Y9&S2Hrct<_QyJ$k@p-BhY!1oGX~k1(T)vGxYCf^*3sx9# z#4@5Fyl*LsGwyUhC&`==f6bU-CNjO^rgjIdBP1TIBy7=#pzNUK1o2BvmQI&`)x3#A zn_PD?Es3=+`H&upR1a!x5_IFU@h!%BBexI6g3jSFw@=nhOksz^PW!OxRxb~3ZQ99% z%yx*8MBm#yr#CTC0z6u=-Fr2y0_8T8RJnD4iZ%ae8j|Ls#7@$LXMBXlEvY8#pmmE7 zDrQ2L$x`D-xN$mt$QP`cxKpF0pnyJ~J|u65gk&Md@tvw8h`AdAbAgh?q+FHfM4$ap zJ^i|sgb}e7n&4Wz$uoN&!ObNVZ;>D|Vy0sFxbA?KSvo=~Rd}2zqL>r}5rX%3`xsNA zg{it=c_TYy`R1l7ifi*;9y7lxYhb?F!E>E7z|fxS$pBrL6Z*mGwPG+_vjg!+QX6wq*d#NMM-W5I5c zq!U)2SoacDzuOb*63A&&EU49eC$#dFaA#sjm9(=#%oBnmunrU&;;c`oA%j%1hm5=h zuafBC$}Vv!lf3{rjhPQonQ_BT*J>1pq7aPbsg#iu@73spX0E2#TA<{Rj3x!*FXD_( zs^JVFCubn+>bN=BCB3kJLd{k-I~_hRDf~JTLFr;9-mGcWskG`I9~woe@0eIxwEdPb zYjIFvQ<8@{1ILP@s;;Y*6Xy)EVjFdJ z$Qu;cz*uM(p!iYKUiqRmF-+tfsCmyJ3r9%q{H)^YsMA|JZA1Kxg4)zLYX>p~`=#LG z4rsykdEG`!W=kqg%Dkk|)GiqLoMN*kY}siu;{RyCAtf5{Z#%%9ZfI8jOw(q3hU&Gh z#?kwR`f=Fs{Li;w!?nds)D$jFnYZ`&G||;JF=pN9YFd(J#*$07@h5^SWnGrq`q^Kg zVd??_eb+@C3BkrhgRE-zhh4Xe!^kLYRKdId7rX6;h9|C+pkgB58F@$+pDIH;iFSO6 z5ula8h-_=M2ahw}7i7r}LOED}RIwa5W4Kfbp@|zA7#g)=Imk;x>_SdX%{yRHWJjL-bvc4E8p*b$;!GK4TvXp%#Iiz_?8wwx6@*B)FjQCkK{wdUbK@#H zYQ#ut3+2H#tAPj;B4f{@IB^(hnUj2!lU9aAujl6UwCf0@Z)wT3vfn6(Er^{$GxqR2 z@Eei=B8Ml1*2EK310AdP$iWzn4jg`h8-skAkjw3m9&%Msxlf&R2<}t%kh+waV_0J3 zUnR}7Q`R9=0OcF4CD7+6pe>!b4qGw=8 z2*h*0)2i@Tu93%0ZpeL?#abFj5!7DMpOeziT2Cs zT}dxUf|kAWYkF0DEI}Hdi>7%bc+%!r8U9UVq6O?EVOLT(t-DnnkpdBu0-WU%bP z=8#~$LMR~0dNth9CV$nWz!bhp>ZOfe+qywU&O%;b|r z;Vyhd7PaSp=v=`}CODmte4y9v_ecTB*{Q z!*3zpQ={+n=W(Tz#1Z_9gQWAdqooRO+w>aZDb_pk3Gos;UW8X&%F~I{ON|8>;j> z^)Nn)Vu$hIG@9pEH9ArX1?qSL$l^RUrZ&WNcq%L{^9n2;|5`oPmuF~A7H^)5H|81f zZ^QGs-h?@C%*4M2D;o{WRv#hp*Ko^g^pAI@&x zaUtG|GR_h6V(FC7JF0u{Q|Wbq?z<6RrQfUNMt%k^DBqSLw%tOA3LQ!yF2%2Ny^&Gz zcKwQ~e?Yz+6MuOc4cmNWx!|qjJ+5&cUg_Ug<9BuDNWsJZ(3*HUo_8ELuvfegKlbMi z$hU9f2VBjtToj+=-}{%#Gq_RaJ9zN+=MLs8xTfdy)2WL5eS7}By;jSwiU;uTeapaq zu_#|Xm|w{|Y{X&nyLxvGznd&E0rIs~@p!Y4@%{z;e#deJSDlD^-;yxcxsb2r3yoq8 zr>~0#^Q*~xBQFl~PlX4+xt8B#E6Zi1HZ-a=aRa}gdapAJ(~Dc)3SHg4xL7TC3njWu z=Q?^J5Ann1OYtLmyc{&F);!vcDW=ZA3w=j!p}Gk0uQp#%p^NK{*{xd_aX$RTG$F=E zw{7Cv2DgdanMUo_MSnv@&6Z2Ic3zxzmh<8*?u&EoiV_}98#kx?efVXN%5VXmOO>VN zhIPxj7{lwiY7NKx&)OgM;KNL5wp5dRoL{s*=kdAukAhb=w9@V1nsHeQlA^22m+;S;?u~|LP5!vF86Y9x7 zMq8ZyU?Jq$%Hk6G*+6qM%Z(y2l*r?abG*AeI9uH2Awtk0Jp1xX;xF-g*K&oY2t7X> z&tM{4Ju@$VK@QaLi$$$?tooH17f)3`QSt`$6GdN&pEkXjestvW#k0oU4O_QzFMW(R zZH)Lz-QEVGp0>jrtP}uV3xE+=27@7|R99F7?{Dl3;hlC9mriOPqW!*IqwTVF_p}BX z#i?U8ODe}!!tBc#jJy6axUBofG&^YKCozPCQvX?6^f+aJS^l67k5{!wV zJ4ZS}ZX@x>9SdaaFX>L&liK2@9wPDV`GDMT3}Bx4_Em>$C2m_XKBNu74?T>qooO+g1s-<+Dw! zJjR@Ht;_xxWwm=BVRzb~C5Zyts3m9&-TNNf#LDar?4O_D(j%Qo35>6Pl1aMMzQAo9_*Hd&pib%e%b`zR&Jn(GHq{IT2;WYUy1EW}Lu(!B8=#_E#d)mM|7hk57o!V#pDCTdDxWCUvu@JwAM3c7UD zknt&DY)qDcT4GTY5<}eyDDm$Yw`Du*=?b=##&V!-+81x_X`4;b{77fK;C22AywM^c zolZL;`)^={q;G|Yx#the&B}7%;H2#gq^?jya^987Buvipy(sBqu%q$07Yxiz?Z=+L z!~EbELO81gD2|XCw$i|{IAI(M6@3c~fx$3u-P-k9X`cm$NN&U4y7P*pTRpOQ I!%T1x*kgISIUNfY#7jTR>OCWpZ31rK-v4(9doFQLW# zUWcelusR=;v67PQFtGC4*Xe<}<3TV?Z-V6pPNnAUI;_xR2{N$q23DTb)B$(1{X)_I z7NLcKmDeLHZ{{B@ZO}AZaG(c5%UJ>6_EVPLKo4-X+h9$8EAgkjgTA@sb-yIBIJCeX zcm*ebSMay5?}O}$c& zNjuML2whtP-y*JCV9!?qlcpOJ48NQ9-1lrvB_2cdW$%X&lK07QeFi;|R5825USb;>qt#e|4pGvptUN_1p_+ z-81jlCoH>%x_$-)JyY~;_OBo8nsP4+Aqhe zqfN|(a7DO8KS$NI)H+RYQ{f?Prgkqg9VSgrWO4`AKw`!bN|P%6`OYl|Yn1Vbh}(Wd z@u@-ezG5mV!n)_G6F?Kk59gXGk_4zoIamjO*?~p3DnF+%zAju+)pojxc>hPu*r8it z4H3D=$JUA}#`22mdAi z(kFv&5NV*id!)QK@(z%J3jbelvQ9q;p}~~yx${LQaUiPwSD)z zp#*437P$6v>H@Rj-FdocuO=S_4TJUNCDi+#|6Ewi z*X!Z~|Ea&zy^2-blPBm;<^gnnCEZb(nap`(9hck7~mrlMxMyKr-1le;#n^hw{> z>0Enx(=CY>);)b6#w&A+iC?sGHX**Oe&MRT@2H=+D(`3NC$7r-4Sw3B3}%vj ze*7d|oaZCy-aEx#^;LO~0AEKf6m<;n695>2Wk6;lZMf4$pdK=~EYZO`?IvE1CL~O( zN|@QsouTdK*mgg7Aj9<|{d`s44O^W1*nW%`_6-W+Hd|#XVQR~};s78`ySWv9WFoE6WC_RZQ^A+%|UKm z_BNBw$h9i&jz_DqWCH&Lz!i~O6>S&n{V#Rt5r`srfU!*FP_`oP14?O-t&2XMy~m{U z`rzIT%Ex7zD7|JyiW#T`sHv8BoSZ$JtNe3PgsR4|E)7sG1 z!Pb4Nq^)oALejc@AoqG?BsMZ41YP3_iT)!f{2 ztvefx`1@L|+Pfk%yo3ufyrO2#tbWDv%zD^vOG)jd1V+l_I{?qrlcL2HfNDr&IgV1E zY!h#?Kc>{3INlLwG)~w<4Q`LFwGiYfQCOz+goc$JO4 zy%+(O9&TQ?!d3lAPN{!`AaQ?UNv7lGqkf>LKao6sNO|lrgF_7(^^18yEwtFnZ%HJ| z@$3ufEKx~a+q)bar-?BO8&4b@YJlvHR=ebxih)E;K-5eA3c6yqf155x9hy5GYh@Hj z?&v;{Wk_qv@JnqCYcM~w_zGA_3L$GxRWk@7#bi9h@`_^rvE@9A)t zookp5%#fr`2P}zt2)3U;tvO9>I@(KNc28TC4B_9Zute(XPme+>oqpShrvo$8#dN@` zA^OlWqo~n}igq7cl?-9ts<0fKH{gEym`iDOAdbnv{#QB#@fQbRrI<15ZQ|`fs)fn2 z1_)s;-3hnIC!~x`W;C8RSq|vqx~%{m#QCuj!P?m*Wem$$j}H@oxOqW$5`=c+Mf4qg1W%XWUi0 z^RU*b8o(WK-=+HwS&w4VNt84>cE1hc38x705WJz8I5O-t1QypR&8;|J#8JKii;AMK zw9I*dc*KeUKh~FL8mmi1yrCq_{a5Z-Z4?jGW|6fCwI$B=hQ*)p?~Vfp_KI7fsr|VF zGe>}{xDP*UuhsIa;(`2o-!k9zC0|{}uX2qV_BIJhzucdjEO8g}wN>$0vx4#d27bRC zRz|J3SVY>_bQQUwzml+u4RK3kY3|RdhH`kZ885ci3JV7LcHq_Ia(OAYTAryc*2T~8 ze6E+-GBkpI2!Xxlpw~Ok-yq&~nh=}u{_$Vllzny|y(tY{DLKQ&i&!+%qgSd8NgLwY!+>Ts@?|v?+>PW?W zCvjfIEA{nB_$gmP6>>)=(9;*I-^@<9(uY4Vsoso-fVbX_Cxf@Jt={1+Bo2#}vPbE@ z0Q(D`zvv#X1x8FA{V>X#2~8gI-ox>-K+IQ$U5%`vLpf}i=@EAd;8(O)6; zmVaN7U%Up>>CI;#`d=&NuUP^!EH9S=jsP{pnds97aoVm8;`^wFBu;}Zr})WcODyRI zBP<7Q0k70(BAOmB<8^qDC19Kzg9fQ%4%*AqV;~T4y1z9{pTzUI-WG8`;A#w)c)tBi z#JxR+!{Yjj@nSCDP$Mjg>M@VGD?Wt>lTv6#d>MZ?h@T$VAa24qJpfN6)`%yO0~quT z87=`&VlB+*-lOqk@Z?j}W6(X=GX_t-0Z%4R{tKRsPEx0Khxv|P|B+v_Y@^4|pf}CO znS|^H@$y3(#M$7L``}3p*|qq|a!4SACOa$Oyz?*QB*V5)y%sM~Khf6P@H1PPE#)hf zBJ%3Lk4KW<2YDLdL5P1khuVAVRU5=Fe;~v?@g~TrCxNJ1eswnCZn&)i3bD zuhmbK{ImLrqIbN%kv0G1^UbXJXKxB+&A)Nj&zk=M{cHf4ndL^2hFkmzZ=$p2KQ;-z@@0$qWopuu!B7|Qv6>a0elgx)HP9?2a&rNq>T?T>}V+`--1^*d-$nlQUKgxhdHp$$0y;V7L7b8 z)_1oL)<=pCJ$$FX_+)*Ht@wi7F4i_xoJqWO>-HI_rIl?$AOSfLPzft7p_0|hMD^TG zB&tMI*}cc7N}@U)H}u!jk{xnnj*}4rA5Gl(va~lvQ~=RtvA&E8fbw5EXmUrtV!1tc ze5BG$0ngMI*TnUJ8VX0q0Ngh5Yx^T>y0G;k6F0)=3M# z3Y>dyZn?t7up$3t94aMLVslQ?z8~bF=vs^L;97MHME*1|IS#}Kl2pbJZVHr4Uy2c6 z`D$QkX|?`D)YWeNi6!an$6n#1J@lV5xp<^2G<8 zEELF69*`EA7_;zQg+VG!>ZvqOpNFm(q|!8IJK)~Fu%4voPu&`KHA;f{`3Z#Tc?R3Q z?W7$di}=XLMu@QzIFSQ!P4l@T>iS{6mIqS23;Sy%?1<}AI)-`*!rp%F5EBSXT!m}& zRwZfC&r+<|eicYP-_hWdnn#yiJ6L-^H)EMAR0{!3^h(8|8^u~4>x;S}S@FJ`fB4vURx(uW!cdFx3^}`pygT>bu13!&p$8B!T6h4)x}zhE@*5 zCM-D`Yx5hQW&~f=p;EWHK6r;kCn0|DTp@mmzh^y9h|l4#{*!+Zq$d7ekH7n9Wm^6D z65`D-xLAlc^%ScjiVP1U6|Q;|Lsdli^Yvo zmBNJ^XO>Hgvm3`oCq^G2E}FlnfQvgX(yN0PKp4y;KjNZ#t#DBTnGW@fu;O^pERq+z z`f9m;(S;YP{|d+*s8;N+7naLyL&S0)-+4mgk7$kgR9~pp8XF<&g#6`yrC0RVdJ$}V zq))+9xDb8k=KKXdwd5GuvJJK+YBi91v6A;_1p#mLs<*1tWC77bw#Gb4%jLzOnzK}> zfYjB$KrJ(lMMeC}2@W*NX@!~o`#yN_VYTFVfx5A<#C;jLH}0r<)YyFNcW+775cXzbn_Yi*Ra5ZfG6x{XjEu|K%3UM-3oKd zx!%aAsCW#u;w@M3n-_nP%R?DU6^|%}e^&B~%SFL;2qRP*1Ix@;mJ2@PX$IWM=hg91 zcJk;t{)zt)z*0Rg__>Z`r2=dUnI`3@NbS#E&MocF?O!g>AO*zRpF5bZ%olkjn8!`O z*q@VWQ+{%jepDG${(Jw~$eQv$TJLr&)1iIBn_JA!*M(P@(=Rl2(di|mXBA3I`9;}J zuTfsg2>So9@N_Y}&_9Qee6a>dH6rK}wc-%2t-k(#paoSZ=s%0-Jd@?UcLSys4Tijg7p<8&&1^v6cE-B~Fk?wojGfy`(lNeWtKbytEyMa`Cfo9m;u|ao9X?1E(4*;i%W^~_E zA*-5Inio=m33S)}yeS@1i;a0if|2gpw=lN-8RkRKiq*JOXuWA2weE@$`@uiSAZ*_XVy`g`hLxvZ*KCL>Z826lP+fjW2PzMhitVKXv4?neG6mAG`C%oZeUJ^ZHn$Dx9h?ilW<^BB7dKcdslWGQMx@ro4Wq&(4tiP zK?3)0Fw9%GrV##8!_haxOx~f2aScZJgdAZv=r|bW6LOeoBDWU=9x%cQ$q4!|Z|Twv zWP7Stf%I8SC}i9X=JW|Ury<-sShgB4!wJa@&N|{?luyV}9*A~q!S?&+I0hwL>f|$YPYnp ziP$wy)AAWyxD@_Gwu?f59h;y-(K@WBJDKD01O56OL+m3pMmVLBizCchZ3v9R<2c;U z7S|KL{%rM=g}#8b#>iC^isRCziMxuvL49v-{MrRKD~d7&pi{GdGJ2h7$uyhruP(9v-@*H-Y9*8FJR?BEdqM$&Ic)3vvq+IK0hty8* zrwh6XZZDh5`1p@`xp7Z;{(V?pA&tF_gOWlus~P_)f}n%b82>wt)5(6Esi^FK9e z`BI|}YGpH<{g=9p?7=;`5o&!FmpJDug<@QzLqXBjZH>*A=H`mEB4F~}zFD+G|22lk zTigem5RD04t|J`K-TdrI%ieC)EtA?A=cXEH{0f?LiW@-S!c-@!eYHNu-LA|1QBDzj*0X0{HR1&d_N@bV+Y zjftlxbj_)0%v{Vz%+eKxZGla4E`HEXtI`n-@6QaIH8P|2vHvV4G7uAl*C+{zpu4^R z+Pc-q$Zx(dDLe1pOqes|9Cm_*3D=WmA}C>s)XbREqbV(N_K@s_T(yKQ1ZXmWyO{+p zL@owt;|%3Gx_{Ld587QKPeyN2h)ihqd>F5tRZOr52{9*OCWW z_~!O_qN~m&7?uvZXpV@)btl;o$1bjy9tAZhMd%1#OqHQ|wMgcPFgjOST%0mYq|D~f zUFAe<%;T0b9Xd8(ipIEHGM&n8_lQHb^IWEXNH$*LDg%>Syh^oJHg0n7n3a-HTZxVi z*LSs$PSBESC{R6Smv_VLY~*Jai|G4oQL09^mZc%a;M-^mQ`-+;tyP1Lj$Rvfn$+yX zsuo5?0?H)F{T0LxBCaM-;SGZ>LnA76;_L&)+Qpu*ourejar1|U> zs!OZ5Zme+4(R^*z%P$}6u{!9XST@|}P_NpsW=vI23Kb&@d9u^X?eK&J*P_%wtGY_M zL#=8u$bhJ+5LJ2jnGuV9*T^K@rDxD?vP)UEkCR30Roa-7VF|81q*!}Fd%0!uaWRoE zCBX+1Jp)|4vv9>=setv61wPjP%AsHTT-ove)QcY7h*|M$HB>Z0H=o5L(+G}d6=NwZkH5N^vICL1Z%ekJQrPN~G3EEN-@4mA@ z*N*WFjoqffI^aH+!T7oT1;%yZzGyprUHGo4xUeU+aknCfyQZq?ecTgiIJB0cyOs{+T1923 zrQ@D}nF%{}qUTI^98;{PvtDVc;pnN_IX82vL6BWZ3Iw}>c=gH}ia~_O75p6r6xT{K zbZ@yN+3d1Iug|E=LMNL~OWOe}^8n9iZ-q>gbg<4Lao~qgPu>kvAq6Qun z>-2%am^=fcT}~bdgARm2$_v*$)DG?u8Uy3Fvg{R(wYwZUm{nb;3B;3dE!MKvY^n_$ zf$p+O;@QQvoCbQVb3GOr-0f==q2W=pBm!m}|E{yv|) zP5e8b*+ivy)C*FlDESQ?4)LE%iepE<>+3hk_y{*pr4diLnFZuksC(xa@;0ZpLH3HR z11E7o#N74|@mjiMXQ;;*}Ku@IsaQ2@QYxP}lNv~J(&w1H3)HX!Hri>+Jb^%qeT zPE_%{O-vINMo&~F6c$fZiz7Wv`5+O9HO7^g$t&YJvUFy_CVvWU-I_LN8F4dieUkHP zqdEfG5|lhg(&>_aQIx*!G$m5`a059N%v|fHRP&5$m%!;E!;PokM+O^oalju*?_lhV zKS+9~%KTBfz?g%QH(h12Yq?Trl&Y1f%3SrlW(wut3@(r;Ho4I)8QhQ_ac7VQ6TIhN zU2FSIEOkbPrNFnX4Q|0p)VrRd6(WwUf9#uC<8`ZDzLlD?OU7tYyKp-S)UMf6gdzsG zYZk)rgbl`YxYMRZIxs`Sm=0L-5+f;LTyjCWj(x~aXT#`S7 z7UQRDgpF?KoWYbu`q#xYZfeNrCw!NDsZrd1Bp^>RK z2AtMDNA!ymCwVP>Y@Bd~!-|7oX?#SdJ()pETtTR`)F{^Y$UIlv@N@T)W#eEx+m6t+ zH=Y9>z(MSRwb65H193%`3IiQ*{72tS9l#F0H_!yf`=|~y!9Wwp)xY+=jP`7ffhM41 zI8KapSSPF2u-6SY!P#t)W=5O}f*Ogu#Jz|`8oB1S&$7)FF?qRL;`( zE#B0jAuoqpbF;|eChAp+#~R*JtyC^L)E#%fXUjfmFR;$F305VWd-!qyL&QdU0rvRr z%P-OPf%CLyVJRtiP0vAR?QOXe=~h%fg`zMLk$)bqDEeEzCMwI%J*Q%sN@gI+v^&>vS!p5|G+&<9`nu#mg;=k9MTTM^ns4 zyCffx97{n2nOc17)|{>9x)45baMR$ImTjaaa9Xy~si^5*wN06P)tzdaX1>}d`Rahl zS1?UGH3=R()R-YbTM;ZlCUbk*Cor8&)zi{+9>T!E;PGWb7h~6wWQVn@hZ#5r|%G2d9I74x%RmF-o$Xw;Ua;YzO3 zo-O_q&Wu>mkEud-bAEQVR;<^v2McYKh4X5AxFNU`YQ4XhEl=-Ri7TBMnl1VjrVHg* zR3^&#V_v=VsA4u2_7)Bn)|Ly6>~!H^Jm#Bci}2E_t0*lhOVv8zlBPq?D{yxDvs zzqGiF_Xmqh9zsR%NAZhM&eb=o5)v=t8PM>l<`*27PgHK7SDGo#mnsUx7XO9%Sggz% z&`Wrf9~O)GYb18mDm}^fg@s~)Kfr~Wz!!d|abOhq38gFG8U+?km@1{-XUn?uVyRpL zi-C(IEPlpkrbMZJgbu&bP%?lrtKM;dgDFS2(ZxuhCotfO!o?7 zMXpYykk2SkW~R&4Gb3J(A);gXS{+}USW5*bJLVVte6y*X$8O><<_-;I+x3Aq0~l=z z04+5AMzu5ey3Z>wH}VbDaq5|T1~Hi^G=7L7)p)Fd^4!zFXaoR#;T^xW>hHFz)LJX(5nK-SMwVf zW3u^=rDM9B{sp9l^9}vh__?&#ASfPug5tL#=y-l_dWuxZ`4!`p8 z(Ju{zj8_UH{l1ey!vVKw~fjf4BmgdFuvHQ{~=+8XAz7V{BJ*`KVI!k=_Q_LrcY%|@Rp)o{NezFRFe z`L3Id_#Cu79`rG)>F{T(<>=44;qX_qTVI|D_;Fl5DIEZ|*<7fWm-01^IJ_I1D5b<9rW0@z6{jw}7)ry>%jq$~5A-`B0r8k&kw5&jY5M-7g#`slMRF=!d zT1hH1{cW>)Npo{?Q5%u|SM@S08ftaS2Rce`vZ9PO8~GZ~YX94Wev9w$0Q2)jX)|iD z6Z&mdz&EpS9l?vNAn^eP+4SfS`saUrR9P(tNd6axjpVy8q27 z%Ko2)R%U`)_RC8Lk&n?CLFX*XKQgRX3p z2QQ1)lHg#2Ab5$v=9dvf7*`bF?@^T8tX@(>Ij>YHHA?wKIK38{Or+qOTQ=NxrczLW z3&>j1aY6%{h&ICvL&K^AwahZv5~-p>70g|C$c&49f-g)5sAv=iDTAPdT*lU3?isgo25m7UPvu^!Fk0k&8pkDVr7s5mq~ ze+D%cs#WP*hf9spHv%Y@D=mtR%ddcC#-PL`-Ar&OKQDjKB?R)9e_4T$7XXkPm_f%H zD2k;{_`0P_$Wg^{GQxxfc1E8T1ayZLSV}HxI2A_R_uB zvDzpel$CcL-djTK^8dH@_04VM$ld%XeyUQb zU|hqb-za7`p=yt&tCQmv{yjf!k@x)t|Jy$2Klma%@;&*|uCgoM>)~>Za_EfEh??k5tTk`zzSu@U^aLDN zP(TBnG4?~*hQ;dg1OQ>F*_(EKsHW@_m$GfreqK%5$s4_a#|15@vf)hBXg$(T)Tp-< zo<}T&L@j$}u{{j1vs9kbQrX+wT1!W?2^lTd2ie(ctdF*{KFICo6sKdXj?iwD+`mZW zVJ#qe3-h&X;nv_!Eiw`;cg*`V{mO5#nW^yM#5ciU_%~@|WE5?n=X}th46ZoQfU?>^ zD|DDZpw;A^j-dqYP8Vcf>Ej4(B{Fo7rYfyLt28Z$y~H^)NeHndSf9(25i&RmY_aP| z13GB4Nd;V=HjoC^1khA4&ACT1G!4j9NIC~F$?$Zbrb5*@4oXC+O~7tOFG!>DkQ&^~or$Td6wUg#fL#hclU(UxPoKzRg!2536E<`k!cq-yrq(&D*?TVC0 zBtv$xKa*Ie>*?t4YiVsx!|!2LCXis0I3;>W3i^y8;&3tf^?u(n0X1_Q?ec~!bDGRC5W zz3=Wowuj-EjCR1qfwMh2u3>1~fk_0#vHfg|i^m1G9~#@fv+G*Wuvmz0kCW%ky#xK& z*1PTTf!1m}lW)DUjC1)RL9ogRCGr8DJ#;xlI=OF+l6H>YVV*RM?I+1L((QnW4Q6{} zlqF7_YHYj6EhyWg;+nm7UE(=I*S39ArY74>AP@zFLx<3DEwr*-aj)AX?YVYsGxGib z+TlZKVe#5_Olvl#cXQjer_5Zt>SL~9Yg=s0R@yoX-F5y9P`N*krPmb118+_`;iii? zt#b8zLuhxqpp2Q8% z;WBI8({(<03%)Y1L_K`#&*#?bM|Kdz>!pYoa0QqDtVK@kT{s%AWJ&#b2^Ty7T`VqU5p+cl~ct<_Wllbp`mbC;B6&bWxCk}-Vu}agxynxdrM_W z>9UNyUt!VS*I1ycBmuq0%(cew4sDHRzs~!uN8pb?uC+zS@qzO%h*)^%7w_<41Q*eP zHn;vx>Dcx-b=^IMMYF`lvlZAIX(>&DZ5@+L4QxH5%z33zrUq`@kW+Es8tk_;-+RNysn)GacOmWOqN_%Nqx{<=YY^Zi=sUC&y(vnBC7ZF6eOLH~o zc$a1z(gv$-dcG`bmo$jAjXN64>kZwscz?5zsxG!pn)6?8;u!!W21JfZy>|_FXPhIb z7RYvq7e%(q1{)-^rtjq0aC45wr_VMp3PQ(k5Q%^Tqi}Fb{vY0o&$r=L&WJh5F4jE zxwQ1LcMWbun--#`eHsAGy;-R62D@nlhy<7!9clzD1P(X^)+#u_Bsi*NrzdHE)EA82+L)vfHQWlXgArs!f!&k(NF~TIk&*1c`m8kxo9*3NHmRr za7cMAJiHoJ2_TITS7AbN-q@C_*-`Rcy6uaCx!q4gO}68>#WdF)(@c|{aa>{=ogSy9 zCcET1)-*Rk`)bGbI%#x)3%T;DHw#UWG7)^7p|={3C#8pA0;^^ z)C7Ymm2v4om|`^5Rw5xtt;Y+PDX{dF=Y-gXL8n35Rfdxy9Ri&OX;-;TigW?!V{?+i zIRUKnIa*Jxbh%bh6|sDiz?ii$76-En?Dw zCTKsWL`=*FuzPjxHJteO8t|gly@r~Qqc1RQ0k`y~0{T^Q^i{L9z?FoPvcsR6;OU-h z)!l=n{i(iWkg_?~#UJ`~0+MxMCIK1n*H43M0B{j>Iv|rES>b1&at%AK#AbUfQURoF$atzalzy^XeHxuWl{rG0d2 zYwz}YGv&};lO*B#iZ?u0ZevYPm+Sq#m#!&y_ollR;Zy#ac5ewawsPlju)|w@;-Tr) z_H(XB|0Lgp>r==}JSFq>j_p(6?w*At5qsW|eF7|Ph)Ga%4($^lXy7G5&pBkr`b>bK z12PFj_nNY-fKCbEbvi>Yz?`m zf=xHtseLtDe%7B-7->fZ;VPPhbStq>DYs=~>?`~#ow>;NS4c7_>d3y_CAXf=(gaNH z2au2;{L}iBV-SX&WXjrY_q;wW+9BA9&~{Jk)1uuZY|mYmk}V4N8x>-RtnIE#EKKkS zOVl>*u);SD>Q;@KrGBqbZQHtSy|(R|wU9R2YP|+cy;{vVs%O0xovO7Ob(P5)GrDy} zPur~1q@z{VsF>3pYt&^QPZe6VMm@`!Q{64wqIQ8gRn(1D@qLwv8ZbJnk0 z+{v!e&1(Qvo|Uc(Z5)r`OsvA?r``fA@|BEgw%92$s=CrqP3Dqic7f-<$XP zqZME3B{D7psJ$)hiW?o0sz8FtZuyOb5M!ZwLMx3G_O@iMOaJkB4I^Qe=gDl zb%B7Y7xk@5S<^tYMirVw{V^~)wn7xDYkv<^w{)6NZR>NOva8b+t&T#SMRz`0Q&_S| zqKsBz#f*SNQl<#)Sd~IRay1IwvwA>ufI20!H5{c96tbzX%-8je9JYjdQO`}`bfZ~Y zCc9H@%d}AH!ka1!phdmN|0nZ1cl5n!OQt0RudcH;1YO>7Z45eJGi?UCx((YHba6$t z8Sr#kc+=qE%-<;Z4UzqvvJf@cfyuRk-%tl8qlO!R+`~?f!#`Jbp;^we9rQ@XvnB92 zzOyy(7{c5RcqG@^8h8}b*$#R%&)E)qJj>Yi3M4=qKJ#3&*$s)QiQ?Km4p7(CQyR9 z`fkr+Y!x$q(KYnU)u_Q^Sm3=A=!luE*fvl@%~Na@Z-!#4>AA6THM?1H+b7fEuUT(A zp7ndH+2Z>4T{pXU>@6QN3NwoBly}Ex=y+I&Ak4Nx z(PVQsGU&6V1gCGVj=?g)V0s46%gRVA3EK3mSd9*+V{yGCUl2hvjW%l8Y%3`gG~YhU ze#VMe_3n@^TwJPFe4x9`jWgG=(CU0@)_leDoMCXv$)LCuK zkBzq&f2v%J&7%~(R+~q~S;{}rQWoTVNcyo>x?EeH*pkWMO@d-KB9Fb!O+Op~35`as z_v3QucrF$^wb<>b;M;p??nbp|x)754`{Q#zSMfRGs(VA{rYrC{=;+kpbbA;bkJbHL z$?EoiQ#7yJ11=7;`{#8`={59PLqi z5X>Tu*bjnN?lfO6Ky#{d2(*Ofz<Nzl>g5M$zLU56zV+kNUO@O_Ps=c;v( zi7NC8I|Mdi2edjU;6>w0 z4#iIaVdZyRHNLLf@*zke7SrzACtzn$ZrD%3<`ee#KExCne{svX{3)yggP3w;uUZdJ zW&P&d<*N{veCKUO&MLh7GRHSB=p7%|Un~C=TzjBX0q5in>3E_}>cznwd|?nP+{@U} zLB@_(>|TFGhVCQwoVj05`!b)H<@$0cq|PbGFZ_pm-`+#v6O6C%1m-TE4CRR+WiqLR z4@E(PoK5BM=!)}!YyWWfP?UCR+0YI@IG!Et32+A6M05D@QL2U@pTpy0pQ9We8*_y5 z6bR>T_%ND28Hw_w#-@FIx9)!)u8<6PJ$*V_K>K{s`zeV>{wy8o{s*$CV$z*kmc}G!m(|S-jhVJ&G9d@ z*#gUAIb5us(iE%EoVoak2UGN9yw4|tkPag12R1y^2({a89L*;)Qengn&tRpM?tLya z<)LO!A#_8Vz!%nqgwYD=hv@1KM1ZhS{FISRf;{iYF7jl?vJT?i8{R}d8QkB~upit0 zl78;`r$6{L(SI^i;P_ZP0gAB0X&)XP#HD?kO^07s8EaWjM0GwUYuac!T8(<+(fDP8SJeaju4|BCo4X(3)tl=ya@!wt^>L z7GH0o&F~pW6D<{=Z#2x4t9g|Qdd z=%O;PQ7!165zNT%od&}RHgrM3hLVo86*fpxhregB$V!Df>vTAfnaBc-TaZP8%*Zax z)?iWLMy7lyA;1EYzJ)^pX^XVV4M4t;8JmlzvmVZMh`SzSkGfw}Qg=P@(d1nZtV7=~ zh`z;CrW$XHz$J}lLM9A7O6M;1L?rm!b=+g=iJ)rhn&#!H0h6woil8obkDL=C0Q-zGQTKO_ zWfQ`4m!LHJfR`*xObA&WXC)OeX@H5Q+zxw^3P&4eA~dZ?B&o2pKqf-cjZ%_SXV^X5 zNQgSsQ8T&MgyV+1$k=fvIiHo+PAVIw00Kz|QlGC?^q2%dBC?2WQeLu3)T(kUNvadu z2WW}#G(jb-XctMR+_Z2os*8_5;V(GL-VpYEB_|PK0qHJbOtLemxJDDined|1tE4Jh z1eQq9YLRbTbKMN>qm2=uxpwQven>F9>=;}X+<{_`xMtPHMnUY*WoCjsjC$BLv9a!X z8=ZYDTYCermjV5?H`mD}_Ryeh)7V3E#yYW=$*U9OAOSnrZ|^ExQW{%@5q>GnwH^Jy$9W*9$tH5 z$oWgqIqR5xiK6!9=TfuoPLPXCw>tq1x;a^#YbW4gYv4{Gi!FXTfel;rb^@u6f?j#M zmz{uyt#dnp4BDu6f@mX|ij)kNhH>qLq&kJgQK!7zhtLHs584TS+0EXxExEU9Qw_Ka zHPN6~U&^h`ZrmON_{ zF0zDJ<+h+Zt$zE+KGD27JJ(dJyyD1Ur!j~)7T67b*z|7=P36s>1Ur$ymD7DRyTx6# z1f8ANzl@fq)!Mhs5;QBWljU{?`}5Y z2-=ETTe~)HEu=g}hm!D7gD^z>tg=UO$i~tXQtBpR)f#&)))@Tz)CDC<_=m+uQJOo0 zd?<>bv+tqs3n&mzY4`$~#uG!2RT7KlpAveLiMX%$bkWNxbWdsc*i7-1=Xd@F(L0~? z5Y8!q$x|M}$+bez-Y`BTDx8d-I)=E+vrHxCoSQO6qQWy@UdT=-QLCFuB^)DK%I~PR zqQR(^lB~z-mQqQdsFu=j!Rn^c$%N{*60-M*#*%UB>b4Rx-H680@z3hUYOunaL#h0+ zTatqz7|C`IhhH4CJ|uoIEdF5lMGA&P;uj@O4u)W~5IPu!@uKT+D8>rBgJBpiCJ%>V zv~WEbib+LswFZz*x>mQ9Oi)HNl}hGCHI+tuRX3GP5=AwYNT^h|l~6uZHT zh~#f{8r(h}aJ9v-HyAG3Cr+*7qfoWZ=J9d!*y2xD*X#pS3gtqT>}==^lw0`Fdw@j@ z=e*<5a;1-O=zQmtzrR|o7NfiMYS_WM7yRnS*n^j_qgZw*cz3rLe!-h14;3<@ z(MOAc!t!lobDFG#y?~?`K*J_%E)8$i_Xq8ikn0nOFFQE=a9r#~)FXM2NNSF58IEtB<~zC3R=(MSsX>qZL+Uj^SN$OYVMf z`h#Axq^H#h(7N6z7dWgsc%_^I@8$J|E4@NavkeE8;~>D-8@53o+emb9(V76FC}kbL zAt#pZFBuoi4-M9)m4)2#Q3C){1E7SxcLHPOoYek8O)&hU*PGRl1jTR>Pz?))OH5<- zwe^Spxmr)=-Oc1~HeNnM5^vDO>hgBHUebT^{mZ|#mZF6Ei#(~5-W2pPkn&rJai+B) zR*P8SvivW1pCI&G@49b$pNH>J5o>7H4>vB`{9CIx7%VUgAfVN3)f-<=2g9%8=jCh- z{<2^=%k^DvvFL4D5_e#ve~EJXQ9yXt?fiY+8#5M50i-Sd9D4`>0X4}lY01Bo7`>EQ z2!>^}yu7__&F((+=kv?Q-l98PwT6@VY9l%+K8WA$NY8~fEjoL>8ctpc_2onFZaf6w z75!~ZdZTGqs7tNoqTfPVYY)fqtT&KAK;$pQ>O%c26WfjM>i8skf0wNm!(oPzSg!Cv z%#Gd!+<>CJ4TVeHoeq{4?H=HTrC=E-XX5TpTuUM?>L(Cy8ORfWbjOSP`Qg>;Zm6@% z%>?5`3LyQN(iR7jDFA?pn7GFIsTr!Wa@YgCTJ#^$+5T$182+s#6jB0#xkn>l`^HhI z^ya>a*H6pVS@rciQ$>6SFTntk5`+5?52BC{JhsFHUs%q2X2!sxI7Y-z8Py?|JL-qw zfOhbA2T{$Pj>@?cXR_@ulbQEMi$5+`gO@Mi@G$B_>!%IPANyD=-#z2^^b_>i8?Bc7 z`p@#cjvp`HKErz!UB^N+ynHEF#@pWfcYn4!0L`ho2kr76fT)>9Nm5X1I#7Rh{UQ5s zq=@~LI!4JZ6E&p-tC|HsMvE*A#6afBRO~_wAuopyt^RyX|8-Z30R4*!<+T5HHdsT@ zgTVjRx`Sj{w(fh-?3O?BztH}ASP#qAUl$-JvJl|){RB+!>l{l2%=cG(SV4zy(d~Wd z!43kJX0N8@-@H=TBHG`<#*o@`(i=m6832X9;Mc&bLfDwq={8EJ3;aUq`D)>)lIiHQ%i#cf$oh5btkg#Mrat8hUK2KZa_` z&;2n1g0}c=CVr2YPk`z6ru|{YvT^jK2W@CFTs_VP%T^yP4_ovu8}~M|wE`{PfR9Pg zqI^c2jipT{DvEX?OI9XS22_jQ8w%JUE6(ubn0ny9i>kj?pqs#(zd}h ztDBQbcT8Emlq29O9CqjiMgrXFgf>Y%W`hyUiaKq+pAyq0A*@CDVpO3}S5FHHsZNu) zLKQQ#^v!*eyQZo(F#x(V0t;f;ax_F6;zbs7N>W7)W}^XgwKXE0$JodP4d z=-vkAnN=Issh^P!^Ske!jbY(TJ_N6z*zr5AY9(&_^JWNId9AmWRGq%uKMirL5A z(mu}Y38%Bc@ES?TIxR8;sQoTyrM2!e--6C|=|z^m(lb)s0ys z;Ag7y*}T6~paE_ZDmuX(k(SF_%{RK0QMnrkgH&^dfzeIX0w?5z;`bg9m;+c_4))_n z$QHlhf;xqW>hb~;4K-x-(==PjO)=E4vgfFkM2@>(x3xqih{zB@yKc7Jy`ya{Yi&V8 zq(;e*O+njv`=Z@8on(kjs%$;W$gb#$s<35Ep+HLO9*lf^0Fb|+3u7Rk4cOsS!#2M% z{-P~h#L|QzDrS3UhQO*9;)Rxh%h>~30Cc;GWTWuiap>!Ha@WSfBa_Ie=n76fp{bx_ zb+HU9o)xk|=jqeTaBlSKULZH(3W~LbN3BK`eFIxN8%^%eO-6>OA&iQ2d*bC}G*Gi| zVeaj>0-RF~U$s`ONtxQqv*0>_ux%v`4ZGGJcCEe%p$5{Hasn~)rDe?g{?2!e4fOe` zJPqI-F_#c02;V)U&nL%!eun>Jp-|pMBNM=EuCaZ0`?9srRx@a9La_>oRzbeGCK2k4 zcO2*GG>RM6Z`0H)QZMrTFDy;X9lC>}tVcO6m<&J$Tkw*`lJ9lRF(O7_9ApsnNn{ZwWwB zlGv`uE2)$G=py6n3Nfhp#Sx3}I=ZIUaE4(kA zV->+h^QjulAlGPZ1%wtDY56ucD(1TdbKJxz92ALd_}1=ou?>IPz10u})Zvi3W1vUd zH#VBy_%Z-mgEuJx8d0s2Cf1OcrS<)8V zff&QjhiIA4qD9NatYF8j2)7{yG)cn>d(SKs7mR*EAsh1_;4$lYKY>JAc_>B^BlGZ1UytZYr_-Lya0wN3#_h+po{ zOBZH6Bt&B{W#2$8_pJ`1e4*aZ7+_Vt@E{WS!qahzpn5jO*QlggvM4*`ebT&iHl`EENu@;S&6qr7aF21K z1-QL8C(snQJ-L2jHzK8&X5R6bD%Aq>DLb#_>Xtxk;k>nCXm>6=f?_qx9w%)_a6dou z$ae8Zoqp)sDIf^dDLLr7_M!;vedgN;&Y465$8Oi} zAcF(jSGxOAWE8S3c}nZs$1ydN=C?=0kj^`N*_Jn64RqZ?BF6^-RaQ9OWgucZSy~}! zUn|Ytbu1VR`H-o?<(yU7H}J4`!(8XiVy@`^q`pdwt9mC+zyx!sM;MX$cN>`P5X^4^ z6j?Z=h9ZHpn~55@YJ^?jgeHxn!qhgP0RMza&^(>^B5b@zI^Rmjk>+ z^1~I`9Dr`ii2Q?7>5VM5Eqp+l-BBbc;O#S!{Mk7#vjSstn6D%_oKSP2=sMk9 zD?vC{a)Yb+vmOxfT{!Gol;9mLD7>oI+p)Wt^)ev-AX=}6apb{J9+6uDFoS!ZWw@q2 zzJ7Dp?sSm33_opRDA=AIhs2K&EASQ2C%Hx^p^Vh%Gu@}d{b>601ve7t>F#8XF3{;6uKXLr$=U<&;n;L>!ZVuS9{%jYaTZpsCFkIWNzGiaKzB zg5aU;SL0%?{y~?Q#oZTu0omNz9NaySo1wgah%1SAYw?1jHqZ^sOol>0!CyFd0>x0s z`~V6W-8IBz4iSt>``9~g`z9P!whqUm%JqigNbuyPR0KfJ6WF|-(6(1%gpFl?Hs54- ze{4<`tINvJ-*F?K8te5xL#G@t3&ur{b3`^6AW%}TPXV{|EiyQvK6K7SjT)X1iq#R% z?c9FIaQ_3rDsc6ia=^gNRdzU=I$eeiOgC?J=u3-RG%97SvmCiAM^p;R_WL!5>};cBokAmtuQZbNI;b~{7*K7>K^uf#ZiNM+FLEf z-qXgmlnL*}R4`#%0eJSc$FmE>>F!81H@Xux8K5u!Y5GFEzDc!L1`2>}g<6W)W}I@t zXhpy__b6tqHyJF++}%a`4TP;`r-m+v4>uUaeobtRFpNhXzMU-gWOcMSTOkp&%}OKg z=#f$KZ6-~^!wefiZnH`1Q0hPu1N!k%2z^8$A?*{rn}G7M@eI(!Z<`~3|FhNR$Q9^& zTMBJ=DeTUHZI)sgUmXiF9e3K>t>37s?h&?6az$Him#ThSs{XibwN#bjm=`Rxns6GU z1j~L_Q{H&-NP&Tdf9JzybE?tUfuFPiz@hO5LJ|L9Q;l0dq<@;$ZI=OcdKT>}fEK=Z zK?fd&E4d$s=Y|*V6Z`W1da)QzIV>Ryt*r%d>5iyz1+B47`1X2T>40F1xUEK44mZ6n zm)|NVYg6UnqO!<|Edi^5Z^S&0iJy6?-urxyur*zURzvR>*SLKqcI?sQjRZ3y|Xi}vtAI^VfqWf*# zA~vr$^i+pmrNrPIdxaNRQgyMx1r{(a8s#pmS^^vQ072basPYaK@meD8;oF+$E2(+n zdUdhp$ zJux!$o`K3Rma3|9xp~70taOx1@ko{20(f)W`YbDp>9`qg^c&1RW#T}8*=5N7BzizW z%J{7sHJTfKA)>s$Hgjpc^$H1pBJ2A#avHoIt*kefw;w+s`ZvG%IQ=}G{W=vWwWbt} z4YB4q{TKcs69@kkIVf5j?VPBCUrzfIggd8qvwQw|Kkhvs{yE-~pA$|#861`87qki} z^muE!#EB?sZYF?BDIVw#B@bJCrCc@071qjLjY09*Bj|2Hc}03y9LA}Zx8}wN+wxX% zS?-nN*6rsS;I9<#o>_6rcEo-QX*NJyy1%enSth)*--`5Rq{$=w(`X4S-)2Ktb7%MX zGF$YJ0wwEacVqbe_{aus9=fQiDW~O4E)hO}xZ8~7Fv&O}!+dg=#JJHd+CNIa;_vQz z0=aXG(@1u=CW!TGJ-og z%a*pWS5}yC=1M#DXK3xes+08@@-@4UQoC2G6t-95(jffxkcRE6NZ!NA9u{kCl_-)HPp!M*!)V%C z$Uo27T#!2vhGYFO{5rxm1nT_^>b)#68Bz)<^~8uEPciR~WQoNPnTv;%D$0HNMeG7UbLSdV6f#lELC~rs~Yvp zI0C~7>N~xa??%%;@<>|aG@3pRk(Y}SVku}NWb8vCapcodxT-J0cA%a(*YpgDsc0OX z?oe)NyL$wQvO_vAe??X=?7fhzOb(*FUlEv0vV1so`@K~ktH+*B<^^scO0Q@Qiy|*D z2iYQRZ=EXlixI6&`I)xBd|S$Urdt>jRS4_5dBtdxjH~^WdvS4J8_I-B( zu}FuDdoUyi7aE@>`mgKi1?dusO}zFv5y~ShOTPOgQPm*qmv7;)l#OSzxkB>i+f48n z@csT`G{+8JePU*@0t>X(zcTe?Y93T!$(H2osJB18`5%XBsk`Yj7Iv?lEsul4nTRf7}K@$32fEv2woVNcra+=!C;LC zgESs87c2zdXaSoKA@u446ofmlF-8whWQJ1W0gr6l66IzKn5j%buqsfL&=*7jzVpYz zUOtaVlHP;I$(HW$!upP!Dia7dk)KcE0xoAik7vJT^S-S9IC^+APh;@OHCdxXO(Ud$ z^qKtK){X$>%I`n;t|P) zNjS*5QWVl0{W(l zOnUvrjIX-NI!a`+h*X0yUyLx!nl52nrUfEvNWI2co|w}W)Nu5UwKX)a#0Gw@p$mAU zrR1GajU@0dqs0ozrA^ykIFWNkYt#oK_Y0cTpq9@t#gs(Vnl7(pL2I&rRB6@1CPY7e z_>p;*9Bz@(Q-EWQNDUE)5z4GJokTz`0q`4(0QlLe!9wyLmTv)G%gjvdTCBHNyki!p z`A@_7Xgqt!zbD}fIlY(xE9e1I-lA9B%yM?Wg1H{r%V-Xb3CitYILAqV)h5j)*qY6G zMO9v`c1gW7Ehf!H``DTe-380VAH^yGP>SvAnje2iV9$T;J#nPrUU~g zpxNMHLi$W~SSZZ?48%G+qsCw?K7Z43wLT*8JThv6@8yV+tjMU`wm zV1|#g4*87~s?{Ui!^f;)9q1)Wy3^QVVe|~*w1hR6kgN96+`s=w*Y6dp7m|a{pN@Z) z7x>?RsOfk|Jzsvb4}H{I7#L`qF3z9cW;lz}zx%)z`ageSmVCc}6otJ38`3$}7XMhz z$0KOd07K_7nKTTjM`<9yqxmJQ1?sGed;ZONLTSa=*bZW>C~~yF$-gKl_Grj}44iZ; zS#SN7Y$Q0%9Zhi7Oh0>H#ZTB%cujJzqzOj%crn9qA;64>_p8Z_QYWJY=sNmAPy^l) z6#%SIG4KP&SM*F3=`UB(=T)t5>g1}Wj=7}ig!U1ChdLxi2ad8>)o>D}!!BFVu$2KW zBl->3+Ql8M#?k$zCH7*Z2+$@C>=%)Dh&)@)%>JDu|jy$;yYku)?g99P?YxciJXhkmHuiAV0;@K+ua z`aJ+*YBl1KSCPUI&uksU0tL0>^mHZ0f&vOl($a`i*)>Mnoa%UVVSUlly?PvBxM@`S zkBbK~U4t)RWhwS!hZ0#1DH|+~<;lPZ?OJADYYoUhL{=aV5b_z3hjf@aFh;1*IHSLt zjX#jE1E-sSlhOHpm(KUUF_B0`OnoF6K$4ACL`)>`0Go_tR(^GeM9yw5Bqg%eBKtg+ z4*x6Y5bt1r#5(;8V9@-LWl^tMc_1&iQj3)Z4%N&vrpeWE3*ZrTowKTs1b`B7Hs;`R zcx8raKq5YXKP=Q|@x#4E|NSO~%F3;l%LeAFPy34tR!ipu%RK))CdFnMRW!CM0B z2k{AbHPS~=tA4-+1^uCH^ckrEET1yiPM25#lp#L*$|Q>)V!`$9$x12;V->Nx14HD^ z5krB> zLt$~g@Ava5oZaNjeDuTz6!}w%BAT$XKf{Tl41`Mtwt$B-ZEuZr31!bq9qk|AWlx7~4kR!LSl3fG!&nfcHHq|Ix`lR=nOvR(cAH{4Ek~l$kR|_HZLYJh7 z8=olhK2?*6&e1-uzFN=<)|Q~EiuUBVSuJi)J3sNNqFuxE!E;u!zT_64^-Ug&d#lqez#Y0!2vvs2q0p@Anh1X5 za@LZ<$zW_bjNfw_>6U_4-aU7ijY}pww8?IXZGy+x+@iSwD1#dwk%}5MhbR}6&&Ah{ zB8ZiM5#$Jb9lGU4K?GljAr$JMiV8;iA;BtLdXP_od9>j}%}S(Q3x^m>tO0pN_BcaK zP8sY=1{^D&B#T&fqCxv27b)9LSg|yXNLJ83amHY4ipF$^YsH8vOc^LHY41g+tM)ud z;@eqND%_7uGvrvl?0mWX09SNgRQb6rT^E?HGP>rQIuJEsnyI8=WULUQ1BM29;tY8? zMU^7e?6NN_5z4Qb*EpN+Mq?|+~P1v;IJ1tHb;zVZB{+N-qZdM0V zToed>*%M3%Oe@+hB4U-Wz}myyxX!1|N|xi(qMmL`3ncR`A>+|RepL5C?fV?j_C>yr ziN>;kD*tIlP)bUn018$~vB&b`nYsCiL!rD2);FjVJlCR6D66#dH2Qs+#PHd7l|W!= zP=~pfHSTfyvZD3(4_nHEssXSzR<^*Fn*xtF#L0`Ye(z(5;zi7EgHy2BtJH&o4NnD+ zJs4h+vUd=4Th1fQOOnyBR_YOZo~f0y@q>-u6Q9?u#BG`xEm)zy&Ew0L#%^9lx6*P| zT7(T%TWbN^Pc&JugqpysSE&igpZ^ciUhM8(awlw_K(b2bM6~sd#|0g?_ArwX%+Anh zVC*t`U}LMKq|X5@2u5P)HtjRG>evqWlp2E~IlqH8bd63Dr|RhQan-f5X+gh4Un>f6 zry4PM28O3{vSHUTgNKoXoszSZBGpyh;d{-)cOiz4Vz21o`+tA3jcLgU0A4d>^)&dK z$;Tl|aR*=~?NB}nMYurkiOd=GsB+3of9?6I?(rq8K#ncqzu(=Ma2&^WCMYIk?SP^) z7tv}{@i;o&r5BO|ImPsO9|v4Z92MF&t(m+OLq;TYo5W=1a02Z@ltdp3?k(gR6|HU z4A%ev1A{{jA!*G5-Y`7P;Pu#=bzG-Yx7Fn4)e*x=OK04Ms1%9)TuuD zieATS>*09%gUwW_}*0+Z2>08Yt`R!m_P3SreImh!{3BMHBtava?k zR*s>ZaOFsmx@pQv>{a`)#yP|K3DgaETc_!eM1BwB%R+ zX0gJQTl$%t8Frp$Pe=1taEFbc0`cb=v?7Uk6%~{s{(sh?60?<()+*=D*rKq4r9qod z6V$>6PkGHELvv@Ap?R)tn6nl%W5gKY+jY471zY|9;=fO{j|d2myvj&?(42+P>4zs| zbaHc2I3N}XeR%bw(g`lfL5Y#Vm;?rXQ~1~G4Oe=_qU1H0S3rReXnjx*_koH!zxn-E z*OppJr`5Nw_`)ED0@H{4`gF%lUda@fg+)9u+W!nJ8!NCT&8iPB3b83)Yz~8-ok3zTZ_^#<_Kv(T(y*k6SKHQPTO-{O;is$U|!+*89KfluN@tfbh?e%Ft^ARDYa3K-# z!|~#Ac6}xO{ee>}>*L4k zD|J&95}Ila_d@#7Nd6yiBcX?Ak{HJ|k%=mScD>>U1c3 zK)cOzB`BkVwI+UDzAguGp^627+_sO$vmSXo^G)Zkceh_!LZ{_ukXJUfffsxRoH4vxnL20T;#9rQ35_aUfW*@f$4Wk`knQ_i4?6sbE>Q?$+d0 zp&~)arx8N)(|@)3W{AshI|yh(Y-Wr=Y2edzq9?DEXXmFl4pZE7uo;i;TB{Kv$*m9q zN1N4TSQIE;h{3RuA(>RLlor=gbKRmyICRkpYMT0`*^4sMi97MeY$u=TDs}FqGzd!G zBPfVTRD|*mlafQz?IWrsii0)$2$o)>t|{Ibz5m*J4~66PW(B?)gq9(nIUrfBigL zu4qz!q40Cz7L-7<)_9@N;~lo(1BGR-ltr+(1m)vft|x8!=eQ+eD2dP>EWX9U5PXCJ z7GXu>zXvR6{KCJ#3Q;fmiiPf{UvB?6ps~&Eu<_|0YNZ9 zh*v?RsEQ^Wf5fyE${$UErhE!t?2>=>-^b_0pKp)b`lG#S+U(ZC~VTmbK&ZL9}sv;kMxx$Z^gx*p_Gn+*22>`FPttG7q0zfH+N}*i#&> zl4Dw?k>zaC_<^8`eJ@Qfj!6)1cuMiOvM-SA|3Xtkf(M*T%1%+(oTI5{gnB|%^Iphb zSxG#6v!G#XgkukA3(GErtswz%Y#UkHN2W0L(oX5lBkin&Xo;wO;uJ<@x8_Yr!$+nl z_@{?(S;e+h7By)If0+)cOVriUnpRoX@96o%uQRI4dO}&{W4o}f{UfqBMO8nc>bkV9 z-=WxEQ2e{GASiAXf^<9KaqJ?b`fv~j>6{?h6)ZSnYO8QL zA_$_W?v0$=s;X_OD$83WZtuj;-+KrhxMiF7Q^&Dfg4kNJZ{Ne{{Z8!dxJ5a_Da?tz>67! z$|kKmNPewgVV`Mg^Pt35)>cRk@gz@UEMQ_BW1+dE!YI-V<%5M4XJ#!m$9yya* zJ!q<%&s0A##@M(}!gN^6>DTRJ7YCv6uz_iX$9awXZ$h=e+2OhW8mSd^q$Qg1)618y?x4SZQP>2p(G=9ltz37^3u^Jg-nuGJkQtLIM+ATV&OD^fMeps< zLYOlg;~*>({)hNAJlwB%xYg;%pZ=uQx}@BH6#?7MPubIjgOP3CZ*Dgnd5q4u-DPe< zk>SMEzB41%fdb}3#Z{JgsAv*~yxB&uU=M*}^mFqfP{N#f1J6hIUbh5x7`=@k(lck& z{!rLzQHOCIjX`3kPtNV)7};x+Ba{{1KO;8^IOtHMs1H~&a27CE@JnNC1XpTMzo?eR z`7u>N4oTGKhqj8QAI+Jh}ZnVkq%n9k= zh;-(Mj4fb>Ij$bH9MrCcG`qU-_EM8;G!x#nVHx6*Qp3YZORW=H9=@1$pFatMhTW(67uTpOF7uf%EzMHziR#J9%|0BCqNR)^%1$)R3WY+tqT;f&; zXx<9&9&76`Bn5-}Xfpw9&dLIj705b&^A{@+c8p9`fQ`q5qU3Il-$vDPr8QZbcu&oH zfvR0v>=>EN9BdB8@*vF)UOXqgt1p#8QGGkiDDKU2BgZetO~|U4AtPOt*n5g&7)&7_ zg{WA-$1s?>yw!|G)0?FUS=11v8wv@uz;@;lb_lIE5`D%b-R)Dtb+hNj+~UP!J8`w_Dcy>-kRpX2O!VY7A6pciS6 z{s*_9)XZB^u)nvr_v}7_Yp=O{YeG*s7i!7%1owwvN2LTQTMJQl^8Az|VSg;CrrJ4585rkLOMiEE?C zg&Qb3ujJkGslO@H)`qyfmcYU6@awjon+mP5#wWDxmBeV@|2V^aKBx?G_kdNunWze?W)BZGzn-`_vyI(gnC#)dI`E+NvsE+ z-{3k((C@9JqB}jiddT_Eoac%~x#b9`Wy}<>mNEHQDvQ@PaABzWKpBi7f%wQeH?(T+ zQRPeE3;?+MZrmcUfpHp;BV4wz+;TMR9cH!JZjTEra!A5^(cK55kTA-1QD@c~Cksy; zODZYer*1baL4&5cH)U=BmE9v$NuPwChf7d)VW}wc&7hkN#Q=ehNW0`ME(amPD}_3z zyI%TokXAp-?@ie#M>Me^jh8olRDE2O0LDugF;E%sUGk zV(OK)$1JJ^x!}z#Mb%<2TW~K+;&39uSVJjnbCwgWodK9)VAM-kcnO*C4!$#-u#m#D zT0J&=-yCFR9`jpvV&Gcg@0h(oiNs8CprLe(o5i}CNITX`165EV= z<2S?U!|JiMnynyJ$KqkWn2l!-xbGyNCx}9ffX-9(JO&Jog~D;4vqf);$jP&LAE^Vx zYm8PVp9KgJ{K_ObXrDM@9^o#Y_e4ao&U;R@W2g8kDNBAWwm`|ly^vCB*qPkkAU1e; z>GsiJs4GwmU6e0Hh{;%#}mZVDynF{<*J=8N}F^(=PI8rN*XQyNC9n%(DleD z*X%ih?NRl@VbldAsd(df(eKTB{n2VuguvNwGFM-7tt0Ct2L|p)dhviG(VJK(xr)VK z+NU5cW*k}naoGP{0Q$JMDuS&)_V=rN}z##pgTsW&9bJ>~O{+kn-_S_GYwP!R|H1 zzbfFD&QI;{67un{+D`l{lZrY$Xq9QR0mw29XCsg~Q)YU|DuHJskVSUS^q@uBYMmG| zHSvk0)EYdNFVl8Xs!j*g&~rkVnv#>klr)?UrlH=XFcsaV1L{$1I>0uq+KCOjE6U{3 z|4i9~&N%8JesM@OnLAyRx5>6%R%c*z%eV@PEFm1?D#)DaET-dXSlQBR)Y6&KPo+)} z*BG`Tw3AUWNky)7Ty0^6xOiW-v38!E^0Q>SXP3R^wIecjvjO+Iem|-j<;Z z6MN^0+>Vu)o1G_$zRkuxWEu>i4Lx|z&b;%sw@Dp=H7R7BAyCN&pWQM5WY zOt83wVrc~>ECy9vq5=joRcnS+00imRD}W&1bQKV!P_HV24Oc}wI464xb#jG+6ikB3 zpb92{4StK%>7HQnoIdV79vzOa_+K+srOYDBz%6^h88Jsf0n3p{_A;VGV>j?+Ug6m-xn# zPt3Mk*tIIT-z=jN^D5IVd-_`itKQ&ramR zvp@P3EkjVQHS3#St$oBsB4K|j7+A-S#Ki4Gc|m3*$A;x1o-;GKpOt;6SgaOcR$USH zkRZm=5>(t`X#H1K(k3Iff4;@Y{b~$GZ!D+N{Ke)OP=e(x+DY)Sy3@vS(+OQs}lcLHc76?a#)jBVLe7y5uvIWWcPaT zU_M@vdS0L&6;Y0FNGpa=G%21Xt{8wo0gOhNy0hVUI2le?%dEE&t1UEh$3!26HH0({ zeI_e&5FX8DQ3U~x&duU*BeoeV9R^g06Ahscht1@yt|)9-2SlQyqMjXFJ+sQXVyz;SXZ z&TuP;ZaDnRu4AV>_`NZMSCP z8QPBRMDw%V+OgPKc=sai$8-YLHKEXkHeIrg*3C*GD-Kj%NOF*>LcDHD3#k=cRj3+L zg<)O}Lb+{T7OF7Mt5W3_dReO6NG}U{c6u^TyRL{Kog?4~tEad^6b7oQdEcUDoHz-! zITr^Q_1jcrvsKL~ZXQ54nnefd#`E1!*4V^$Hj+VvT1C|;4iTyw%_2gT<9S4|b}W+!RgUKp z!P?PmB2-)R3C?3qu8r5z`&sLo{)P4D8l^7Or?W0nu9Nfc?PAtPhH_-?{)KNQ(3&l% z(?0hLcWybaGUd&tM9{;9cyXp$`MEb8jPaIkk!9^u{aThqzH54)bIR=8PA0ePl39ca zr>qv&HqZxZn_%-XZb6k7x)AMovp)VK17vIYIJ$=;smxmLR?g+`yF8|@m_>E**n>MW z%(2(qC33XRd!xlQvUqQB|GJ%>(nowiI>OZj98+;|WHqB__%Bg1qE2TM9=?++O3!+% z1}{G&dA!<(!0VBx5!X>+7+w&Tu`W``$9;=y=J}3k^29*XIEKe3M_y%s&{RD@W_JBs zGTyq#&4S9F+5z@#SHVQb*J*E2hscp4LDATQs3ZmYZRWj{-7LKw%!j~tO8}_P(%2xA z+>x3{2B!(wq_Gbx`B3of9_o7WjBWH(l$BB0-r28Ej|Djysx0sPMHMRp#SB7f8Uh?o zk(wdk!$T#W3(xsPAu+GTtvyC`?y;k=1~#amCYm0s&Y5Br&1t1efO9&>;88$zE_@41 zza35HV~v7;XUiwy!m|`mJ;Ggd%#|+!E5?spbciRJZ|t&F7qpmC35*sQB2#D@tg+_2 zn*nC{S1?H=a?D1@Z>twcP!o(E7Jc^&K2V?n3i{iTWG#GgEfNZ~l%gTX4&`7+^DXB& z=z^wRdgK;3VV4#KK|Cs?NRWiojY5Z(MLkOB7$7=XL(q~aQ!z||ZHQFObUr!}5&QR!@ zD=wL4W4Zxb(ihEGG(o6NGK*6K;a!4LE8bPKwA{Zs5|XIsv*KU6&1hx#(1<` zZ7qFNcf^DXnC_A*Qf?JF6_W=nHJUZ_uIWiqpj9O#7eX6cfgdSC`l8@qw4C=={YM$s zhXtp5TTgtUy_gpTo3~Z3R;$J6ZoL}%kxBULj|iKiqbH;qWp+EMAlOFqV$ce)Mctk-!4kmQ zRU1NjYGZ3bJ0_cbD?V&rE70r6^V{YX^-X(Yl{jOmwHLcH7v;0*dQ#~ZuXGu<^7`Jl zwj{xg|Q2pnd?9LN| z#d(CI#))H2QfJS+Na8U>Ee7K5dPlOpLsF-II4b!ECTvQ=EocdT%P>w4ARQ>aprP;! z@j09BoyHYkWwAnhHB!Rty4GmPkvO6lc_fZ#0NnUkIiBB+Oj#wLauD&Pmh&wq zot&>Rg43z6V^eCEMnrQfD4UHTHO#((@56W=%EIXEr4tlFHE|C!DvAXldaMtSfb?}zuG$@);x2jPBPx3Pnb6q3tV>Z zGJ{sE=90rxW^QG<1XMKOf;j7v{}$ENwd8sNLzX7MD#9!74h~j8lU{c?_pAWzUfB{m zZWREm@RpRes0~I#;+*(H+!XihdUwm&7Y6jO)M^)vwi6yTpfu@T-RLcXFhua zIS-j*f^!OnNpIvr6n|`SNiGo(j)+@F*x|=LFr>SXEOpggSbBtPRdSVLU%)Gg5NQ4M z?&Gt|XQ$sEecyieZ?EP!Nqlxa?SKF5Zao?go}Hauoc??3hcCUw539-ihlhTj{`&*H k{?`6sx!}hi#-lsDJ^r5DtA=x+y}HLq, std::allocator > >) +4078 15 82 3 +408d 13 83 3 +40a0 1e 85 3 +40be 42 89 3 +4100 20 90 3 +4120 2b 91 3 +414b 1a 92 3 +4165 23 93 3 +4188 46 96 3 +41ce 46 99 3 +4214 33 83 3 +4247 8 102 3 +424f 1 102 3 +FUNC 4250 3ef 0 -[DumpSymbols processDWARFFunctionInfo:] +4250 15 473 3 +4265 25 474 3 +428a 1e 476 3 +42a8 a 480 3 +42b2 3c 481 3 +42ee 3d 483 3 +432b 23 485 3 +434e 26 487 3 +4374 6 489 3 +437a 37 490 3 +43b1 2a 491 3 +43db 17 492 3 +43f2 30 496 3 +4422 3d 497 3 +445f 2e 498 3 +448d 30 502 3 +44bd 64 504 3 +4521 34 507 3 +4555 9d 509 3 +45f2 45 474 3 +4637 8 513 3 +463f 1 513 3 +FUNC 4640 1f5 0 -[DumpSymbols processDWARFLineNumberInfo:] +4640 15 515 3 +4655 25 516 3 +467a 39 520 3 +46b3 26 521 3 +46d9 6 523 3 +46df 37 524 3 +4716 2a 525 3 +4740 17 526 3 +4757 30 529 3 +4787 61 531 3 +47e8 45 516 3 +482d 8 534 3 +4835 1 534 3 +FUNC 4836 10f 0 -[DumpSymbols dealloc] +4836 13 1145 3 +4849 1c 1146 3 +4865 1c 1147 3 +4881 1c 1148 3 +489d 1c 1149 3 +48b9 1c 1150 3 +48d5 1c 1151 3 +48f1 25 1152 3 +4916 29 1154 3 +493f 6 1155 3 +4945 1 1155 3 +FUNC 4946 512 0 -[DumpSymbols loadDWARFSymbolInfo:offset:] +4946 17 402 3 +495d 9 405 3 +4966 10 406 3 +4976 2b 408 3 +49a1 38 409 3 +49d9 3a 410 3 +4a13 2e 411 3 +4a41 31 416 3 +4a72 e 418 3 +4a80 24 420 3 +4aa4 5 422 3 +4aa9 b 424 3 +4ab4 b 425 3 +4abf e 426 3 +4acd 2b 427 3 +4af8 2b 428 3 +4b23 2c 431 3 +4b4f 52 439 3 +4ba1 34 444 3 +4bd5 1a 446 3 +4bef 21 451 3 +4c10 1e 452 3 +4c2e 21 453 3 +4c4f 40 422 3 +4c8f 6 453 3 +4c95 170 422 3 +4e05 43 456 3 +4e48 10 457 3 +FUNC 4e58 4fd 0 -[DumpSymbols generateSectionDictionary:] +4e58 18 663 3 +4e70 10 665 3 +4e80 2e 666 3 +4eae 9 668 3 +4eb7 2b 669 3 +4ee2 7 670 3 +4ee9 2e 672 3 +4f17 d 676 3 +4f24 32 678 3 +4f56 29 680 3 +4f7f a 684 3 +4f89 3c 685 3 +4fc5 31 688 3 +4ff6 5d 689 3 +5053 26 692 3 +5079 21 694 3 +509a c 698 3 +50a6 e 699 3 +50b4 6 700 3 +50ba 9 701 3 +50c3 2e 702 3 +50f1 c 704 3 +50fd 3c 706 3 +5139 66 709 3 +519f 1c 712 3 +51bb fb 714 3 +52b6 6 717 3 +52bc 5 718 3 +52c1 19 704 3 +52da 25 714 3 +52ff 2e 722 3 +532d 9 723 3 +5336 17 698 3 +534d 8 725 3 +5355 1 725 3 +FUNC 5356 24a 0 -[DumpSymbols getSectionMapForArchitecture:] +5356 14 643 3 +536a 43 645 3 +53ad 1a 648 3 +53c7 1c 645 3 +53e3 18 648 3 +53fb 40 650 3 +543b 20 651 3 +545b 17 652 3 +5472 16 651 3 +5488 cb 652 3 +5553 11 654 3 +5564 32 657 3 +5596 a 658 3 +FUNC 55a0 3fe 0 -[DumpSymbols initWithContentsOfFile:] +55a0 14 1056 3 +55b4 3b 1057 3 +55ef 44 1059 3 +5633 17 1060 3 +564a c 1061 3 +5656 1f 1064 3 +5675 2b 1067 3 +56a0 a 1069 3 +56aa 35 1083 3 +56df 2 1087 3 +56e1 1a 1088 3 +56fb 3d 1087 3 +5738 33 1092 3 +576b 6 1094 3 +5771 e 1095 3 +577f 17 1096 3 +5796 c 1097 3 +57a2 1c 1101 3 +57be 1f 1103 3 +57dd 18 1104 3 +57f5 23 1107 3 +5818 25 1109 3 +583d 1c 1107 3 +5859 17 1110 3 +5870 c 1111 3 +587c 2a 1115 3 +58a6 8 1116 3 +58ae a 1118 3 +58b8 9 1119 3 +58c1 d 1122 3 +58ce 29 1124 3 +58f7 20 1126 3 +5917 20 1128 3 +5937 57 1132 3 +598e 9 1136 3 +5997 7 1137 3 +FUNC 599e d74 0 -[DumpSymbols outputSymbolFile:] +599e 18 877 3 +59b6 2e 879 3 +59e4 30 880 3 +5a14 5d 882 3 +5a71 30 883 3 +5aa1 5d 885 3 +5afe 2e 888 3 +5b2c 38 891 3 +5b64 46 892 3 +5baa 26 893 3 +5bd0 20 895 3 +5bf0 20 904 3 +5c10 30 898 3 +5c40 f 899 3 +5c4f 1e 904 3 +5c6d 17 907 3 +5c84 17 908 3 +5c9b 44 911 3 +5cdf 44 914 3 +5d23 a 917 3 +5d2d 36 921 3 +5d63 30 923 3 +5d93 9 18 4 +5d9c 9 19 4 +5da5 c 20 4 +5db1 56 923 3 +5e07 74 925 3 +5e7b f 927 3 +5e8a 44 932 3 +5ece 20 933 3 +5eee c 934 3 +5efa 4e 935 3 +5f48 41 936 3 +5f89 f 937 3 +5f98 14 934 3 +5fac 7 941 3 +5fb3 14 942 3 +5fc7 14 943 3 +5fdb 1d 946 3 +5ff8 c 948 3 +6004 24 949 3 +6028 29 950 3 +6051 9 953 3 +605a 28 954 3 +6082 2e 955 3 +60b0 1e 957 3 +60ce 7 959 3 +60d5 26 962 3 +60fb 2a 963 3 +6125 2a 964 3 +614f 6 966 3 +6155 2a 967 3 +617f e 971 3 +618d 43 972 3 +61d0 4c 974 3 +621c 8 975 3 +6224 2e 979 3 +6252 2e 982 3 +6280 2e 985 3 +62ae 2e 988 3 +62dc 2e 991 3 +630a 2e 994 3 +6338 2e 997 3 +6366 2e 1000 3 +6394 54 1004 3 +63e8 c 1005 3 +63f4 e 1007 3 +6402 27 1008 3 +6429 8 1009 3 +6431 34 1010 3 +6465 24 1012 3 +6489 2 1013 3 +648b 2a 1017 3 +64b5 a 1019 3 +64bf 14 1020 3 +64d3 1d 1021 3 +64f0 a 1025 3 +64fa 32 1026 3 +652c 33 1028 3 +655f c 1029 3 +656b 55 1034 3 +65c0 f 1036 3 +65cf 16 1040 3 +65e5 61 1041 3 +6646 f 1043 3 +6655 47 1046 3 +669c c 1048 3 +66a8 11 948 3 +66b9 4e 1052 3 +6707 b 1053 3 +FUNC 6712 11 0 operator new(unsigned long, void*) +6712 c 94 5 +671e 5 94 5 +6723 1 94 5 +FUNC 6724 e 0 operator delete(void*, void*) +6724 c 98 5 +6730 2 98 5 +673e 7 76 6 +6745 2 77 6 +6747 1a 78 6 +6761 d 77 6 +676e 3 79 6 +6771 2 80 6 +6773 1 80 6 +6780 d 95 6 +678d 1 95 6 +678e 13 127 74 +67a1 2a 127 74 +67cb 1 127 74 +67cc 13 127 74 +67df 2a 127 74 +6809 1 127 74 +680a 13 127 74 +681d 2a 127 74 +6847 1 127 74 +FUNC 6848 e 0 dwarf2reader::LineInfoHandler::DefineDir(std::string const&, unsigned int) +6848 c 131 7 +6854 2 131 74 +FUNC 6856 26 0 dwarf2reader::LineInfoHandler::DefineFile(std::string const&, int, unsigned int, unsigned long long, unsigned long long) +6856 24 142 7 +687a 2 142 74 +FUNC 687c 1a 0 dwarf2reader::LineInfoHandler::AddLine(unsigned long long, unsigned int, unsigned int, unsigned int) +687c 18 150 7 +6894 2 150 74 +6896 12 299 74 +68a8 12 299 74 +68ba 13 301 74 +68cd 2a 301 74 +68f7 1 301 74 +68f8 13 301 74 +690b 2a 301 74 +6935 1 301 74 +6936 13 301 74 +6949 2a 301 74 +6973 1 301 74 +FUNC 6974 44 0 dwarf2reader::Dwarf2Handler::StartCompilationUnit(unsigned long long, unsigned char, unsigned char, unsigned long long, unsigned char) +6974 39 308 7 +69ad b 308 74 +FUNC 69b8 1f 0 dwarf2reader::Dwarf2Handler::StartDIE(unsigned long long, dwarf2reader::DwarfTag, std::list, std::allocator > > const&) +69b8 18 314 7 +69d0 7 314 74 +69d7 1 314 74 +FUNC 69d8 26 0 dwarf2reader::Dwarf2Handler::ProcessAttributeUnsigned(unsigned long long, dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm, unsigned long long) +69d8 24 323 7 +69fc 2 323 74 +FUNC 69fe 26 0 dwarf2reader::Dwarf2Handler::ProcessAttributeSigned(unsigned long long, dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm, long long) +69fe 24 332 7 +6a22 2 332 74 +FUNC 6a24 26 0 dwarf2reader::Dwarf2Handler::ProcessAttributeBuffer(unsigned long long, dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm, char const*, unsigned long long) +6a24 24 345 7 +6a48 2 345 74 +FUNC 6a4a 1a 0 dwarf2reader::Dwarf2Handler::ProcessAttributeString(unsigned long long, dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm, std::string const&) +6a4a 18 354 7 +6a62 2 354 74 +FUNC 6a64 1a 0 dwarf2reader::Dwarf2Handler::EndDIE(unsigned long long) +6a64 18 360 7 +6a7c 2 360 74 +6a7e c 44 8 +6a8a 2 44 8 +6a8c 13 55 32 +6a9f 35 55 32 +6ad4 13 91 32 +6ae7 73 96 32 +6b5a 13 98 32 +6b6d 35 98 32 +6bae 1a 75 3 +6bc8 2 76 3 +FUNC 6bca 20 0 std::_Rb_tree_const_iterator >::operator!=(std::_Rb_tree_const_iterator > const&) const +6bca c 287 10 +6bd6 14 288 40 +FUNC 6bea 16 0 std::_Rb_tree_const_iterator >::operator->() const +6bea c 249 10 +6bf6 a 250 40 +6c0c 7 614 72 +6c13 1 614 72 +6c14 c 241 40 +6c20 c 242 40 +FUNC 6c2c 16 0 std::_Rb_tree_const_iterator >::operator*() const +6c2c c 245 11 +6c38 a 246 40 +6c42 c 241 40 +6c4e c 242 40 +FUNC 6c5a 20 0 std::_Rb_tree_const_iterator > >::operator!=(std::_Rb_tree_const_iterator > > const&) const +6c5a c 287 11 +6c66 14 288 40 +FUNC 6c7a 16 0 std::_Rb_tree_const_iterator > >::operator->() const +6c7a c 249 11 +6c86 a 250 40 +6c90 c 185 34 +6c9c 18 186 34 +6cc0 14 204 34 +6cd4 c 69 70 +6ce0 d 69 70 +6ced 1 69 70 +6cee c 89 70 +6cfa 20 90 70 +6d1a c 69 70 +6d26 d 69 70 +6d33 1 69 70 +6d34 c 69 70 +6d40 d 69 70 +6d4d 1 69 70 +FUNC 6d4e 25 0 std::_Rb_tree_const_iterator >::operator++() +6d4e c 253 13 +6d5a 14 255 40 +6d6e 5 256 40 +6d73 1 256 40 +FUNC 6d74 25 0 std::_Rb_tree_const_iterator > >::operator++() +6d74 c 253 13 +6d80 14 255 40 +6d94 5 256 40 +6d99 1 256 40 +FUNC 6d9a 14 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_M_begin() +6d9a c 461 13 +6da6 8 462 40 +FUNC 6dae 14 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_M_begin() +6dae c 461 13 +6dba 8 462 40 +6dc2 c 65 68 +6dce 2 65 68 +6dd0 c 72 68 +6ddc 2 72 68 +6dde c 97 69 +6dea d 97 69 +6df7 1 97 69 +6df8 c 105 69 +6e04 d 105 69 +6e11 1 105 69 +6e12 c 105 69 +6e1e d 105 69 +6e2b 1 105 69 +6e2c c 67 68 +6e38 2 67 68 +6e3a c 99 69 +6e46 14 100 69 +6e5a c 99 69 +6e66 14 100 69 +FUNC 6e7a 2b 0 std::_Vector_base >::get_allocator() const +6e7a 10 93 16 +6e8a 1b 94 71 +6ea5 1 94 71 +6ea6 c 65 68 +6eb2 2 65 68 +6eb4 c 72 68 +6ec0 2 72 68 +6ec2 c 97 69 +6ece d 97 69 +6edb 1 97 69 +6edc c 105 69 +6ee8 d 105 69 +6ef5 1 105 69 +6ef6 c 105 69 +6f02 d 105 69 +6f0f 1 105 69 +6f10 c 67 68 +6f1c 2 67 68 +6f1e c 99 69 +6f2a 14 100 69 +6f3e c 99 69 +6f4a 14 100 69 +FUNC 6f5e 2b 0 std::_Vector_base >::get_allocator() const +6f5e 10 93 16 +6f6e 1b 94 71 +6f89 1 94 71 +6f8a c 603 72 +6f96 c 603 72 +FUNC 6fa2 23 0 std::vector >::begin() +6fa2 c 333 16 +6fae 17 334 71 +6fc5 1 334 71 +FUNC 6fc6 26 0 std::vector >::end() +6fc6 c 351 16 +6fd2 1a 352 71 +6ff8 5 666 72 +6ffd 1 666 72 +6ffe c 608 72 +700a 14 609 72 +702a 5 666 72 +702f 1 666 72 +FUNC 7030 35 0 bool __gnu_cxx::operator!= > >(__gnu_cxx::__normal_iterator > > const&, __gnu_cxx::__normal_iterator > > const&) +7030 d 693 16 +703d 28 694 72 +7065 1 694 72 +7066 c 603 72 +7072 c 603 72 +708a 27 629 72 +70b1 1 629 72 +70b2 c 84 70 +70be 1f 85 70 +70dd 1 85 70 +FUNC 70de 32 0 std::pair, __gnu_cxx::hash, std::equal_to, std::allocator > >*> std::make_pair, __gnu_cxx::hash, std::equal_to, std::allocator > >*>(std::string, __gnu_cxx::hash_map, __gnu_cxx::hash, std::equal_to, std::allocator > >*) +70de 10 144 16 +70ee 22 145 70 +711c a 190 34 +7132 d 194 34 +713f 1 194 34 +7140 c 84 70 +714c 17 85 70 +7163 1 85 70 +FUNC 7164 2d 0 std::pair std::make_pair(char const*, unsigned long) +7164 c 144 16 +7170 21 145 70 +7191 1 145 70 +7192 c 84 70 +719e 1d 85 70 +71bb 1 85 70 +FUNC 71bc 30 0 std::pair > std::make_pair >(char*, std::pair) +71bc 10 144 16 +71cc 20 145 70 +71ec c 89 70 +71f8 20 90 70 +7218 d 89 70 +7225 70 90 70 +7295 1 90 70 +FUNC 7296 12 0 std::iterator_traits::iterator_category std::__iterator_category(unsigned long const* const&) +7296 c 164 17 +72a2 6 165 17 +FUNC 72a8 1d 0 std::iterator_traits::difference_type std::__distance(unsigned long const*, unsigned long const*, std::random_access_iterator_tag) +72a8 c 92 18 +72b4 11 97 18 +72c5 1 97 18 +FUNC 72c6 33 0 std::iterator_traits::difference_type std::distance(unsigned long const*, unsigned long const*) +72c6 c 114 18 +72d2 27 118 18 +72f9 1 118 18 +FUNC 72fa 20 0 void std::__advance(unsigned long const*&, int, std::random_access_iterator_tag) +72fa c 150 18 +7306 14 155 18 +FUNC 731a 33 0 void std::advance(unsigned long const*&, int) +731a c 172 18 +7326 27 175 18 +734d 1 175 18 +FUNC 734e 7a 0 unsigned long const* std::lower_bound(unsigned long const*, unsigned long const*, unsigned long const&) +734e c 2625 19 +735a 15 2642 19 +736f 2 2646 19 +7371 8 2648 19 +7379 6 2649 19 +737f 12 2650 19 +7391 e 2651 19 +739f 6 2653 19 +73a5 4 2654 19 +73a9 e 2655 19 +73b7 6 2658 19 +73bd 6 2646 19 +73c3 5 2660 19 +73db b 227 34 +73e6 e 228 34 +73f4 1c 229 34 +7410 20 230 34 +7430 6 231 34 +7436 c 72 68 +7442 2 72 68 +7444 c 105 69 +7450 d 105 69 +745d 1 105 69 +745e c 105 69 +746a d 105 69 +7477 1 105 69 +7478 c 80 71 +7484 d 80 71 +7491 1 80 71 +7492 c 67 68 +749e 2 67 68 +74a0 c 99 69 +74ac 14 100 69 +FUNC 74c0 2b 0 std::_Vector_base >::get_allocator() const +74c0 10 93 19 +74d0 1b 94 71 +74eb 1 94 71 +74ec c 238 40 +74f8 a 239 40 +FUNC 7502 26 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::begin() const +7502 c 585 19 +750e 1a 588 40 +FUNC 7528 19 0 std::map, std::allocator > >::begin() const +7528 c 243 20 +7534 d 244 45 +7541 1 244 45 +FUNC 7542 26 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::end() const +7542 c 596 20 +754e 1a 597 40 +FUNC 7568 19 0 std::map, std::allocator > >::end() const +7568 c 260 20 +7574 d 261 45 +7581 1 261 45 +7582 c 65 68 +758e 2 65 68 +7590 c 72 68 +759c 2 72 68 +759e c 97 69 +75aa d 97 69 +75b7 1 97 69 +75b8 c 105 69 +75c4 d 105 69 +75d1 1 105 69 +75d2 c 72 68 +75de 2 72 68 +75e0 c 105 69 +75ec d 105 69 +75f9 1 105 69 +75fa c 397 40 +7606 d 397 40 +7613 1 397 40 +7614 c 105 69 +7620 d 105 69 +762d 1 105 69 +FUNC 762e 14 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_S_right(std::_Rb_tree_node_base*) +762e c 496 20 +763a 8 497 40 +FUNC 7642 14 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_S_left(std::_Rb_tree_node_base*) +7642 c 488 20 +764e 8 489 40 +7656 c 65 68 +7662 2 65 68 +7664 c 72 68 +7670 2 72 68 +7672 c 97 69 +767e d 97 69 +768b 1 97 69 +768c c 105 69 +7698 d 105 69 +76a5 1 105 69 +76a6 c 72 68 +76b2 2 72 68 +76b4 c 105 69 +76c0 d 105 69 +76cd 1 105 69 +76ce c 397 40 +76da d 397 40 +76e7 1 397 40 +76e8 c 105 69 +76f4 d 105 69 +7701 1 105 69 +FUNC 7702 14 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_S_right(std::_Rb_tree_node_base*) +7702 c 496 20 +770e 8 497 40 +FUNC 7716 14 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_S_left(std::_Rb_tree_node_base*) +7716 c 488 20 +7722 8 489 40 +772a c 84 71 +7736 2f 85 71 +7765 2 86 71 +7767 1 86 71 +7768 c 80 71 +7774 d 80 71 +7781 1 80 71 +7782 c 96 71 +778e 12 97 71 +77a0 2 98 71 +77a2 c 84 71 +77ae 2f 85 71 +77dd 2 86 71 +77df 1 86 71 +77e0 c 80 71 +77ec d 80 71 +77f9 1 80 71 +77fa c 96 71 +7806 12 97 71 +7818 2 98 71 +7826 d 107 68 +7833 1 107 68 +FUNC 7834 2e 0 void std::_Destroy >(std::string*, std::string*, std::allocator) +7834 c 171 21 +7840 2 173 73 +7842 12 174 73 +7854 c 173 73 +7860 2 174 73 +7862 c 167 40 +786e a 168 40 +FUNC 7878 26 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::begin() +7878 c 581 21 +7884 1a 582 40 +FUNC 789e 19 0 std::map, std::allocator > >::begin() +789e c 234 21 +78aa d 235 45 +78b7 1 235 45 +FUNC 78b8 26 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::end() +78b8 c 592 21 +78c4 1a 593 40 +FUNC 78de 19 0 std::map, std::allocator > >::end() +78de c 251 21 +78ea d 252 45 +78f7 1 252 45 +78f8 c 167 40 +7904 a 168 40 +FUNC 790e 26 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::begin() +790e c 581 21 +791a 1a 582 40 +FUNC 7934 19 0 std::map, std::less, std::allocator > > >::begin() +7934 c 234 21 +7940 d 235 45 +794d 1 235 45 +FUNC 794e 26 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::end() +794e c 592 21 +795a 1a 593 40 +FUNC 7974 19 0 std::map, std::less, std::allocator > > >::end() +7974 c 251 21 +7980 d 252 45 +798d 1 252 45 +FUNC 798e 11 0 std::_Select1st, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >::operator()(std::pair, __gnu_cxx::hash, std::equal_to, std::allocator > >*>&) const +798e c 546 22 +799a 5 547 41 +799f 1 547 41 +79a0 c 128 34 +79ac 13 129 34 +79bf 1 129 34 +79cc 22 396 34 +79fa d 199 42 +7a07 1 199 42 +7a08 c 65 68 +7a14 2 65 68 +7a16 c 72 68 +7a22 2 72 68 +7a24 c 97 69 +7a30 d 97 69 +7a3d 1 97 69 +7a3e c 105 69 +7a4a d 105 69 +7a57 1 105 69 +7a58 c 65 68 +7a64 2 65 68 +7a66 c 72 68 +7a72 2 72 68 +7a74 c 105 69 +7a80 d 105 69 +7a8d 1 105 69 +7a8e c 97 69 +7a9a d 97 69 +7aa7 1 97 69 +7aa8 c 72 68 +7ab4 2 72 68 +7ab6 c 105 69 +7ac2 d 105 69 +7acf 1 105 69 +7adc d 94 68 +7ae9 1 94 68 +FUNC 7aea 2f 0 std::_Vector_base >::_M_deallocate(dwarf2reader::CompilationUnit::Abbrev*, unsigned long) +7aea c 120 23 +7af6 6 122 71 +7afc 1d 123 71 +7b19 1 123 71 +7b1a c 108 71 +7b26 43 109 71 +7b69 1 109 71 +7b6a c 65 68 +7b76 2 65 68 +7b78 c 103 69 +7b84 d 103 69 +7b91 1 103 69 +7b92 c 65 68 +7b9e 2 65 68 +7ba0 c 103 69 +7bac d 103 69 +7bb9 1 103 69 +7bc6 d 94 68 +7bd3 1 94 68 +FUNC 7bd4 2f 0 std::_Vector_base >::_M_deallocate(dwarf2reader::SourceFileInfo*, unsigned long) +7bd4 c 120 23 +7be0 6 122 71 +7be6 1d 123 71 +7c03 1 123 71 +7c04 c 108 71 +7c10 43 109 71 +7c53 1 109 71 +7c54 c 188 71 +7c60 12 189 71 +7c72 2 190 71 +7c74 c 35 32 +7c80 d 35 32 +7c8d 1 35 32 +7c9a d 107 68 +7ca7 1 107 68 +FUNC 7ca8 2e 0 void std::_Destroy >(dwarf2reader::SourceFileInfo*, dwarf2reader::SourceFileInfo*, std::allocator) +7ca8 c 171 23 +7cb4 2 173 73 +7cb6 12 174 73 +7cc8 c 173 73 +7cd4 2 174 73 +7cd6 d 272 71 +7ce3 8c 273 71 +7d6f 1 273 71 +7d7c d 94 68 +7d89 1 94 68 +FUNC 7d8a 2f 0 std::_Vector_base >::_M_deallocate(std::string*, unsigned long) +7d8a c 120 23 +7d96 6 122 71 +7d9c 1d 123 71 +7db9 1 123 71 +7dba c 108 71 +7dc6 3d 109 71 +7e03 1 109 71 +7e04 c 188 71 +7e10 12 189 71 +7e22 2 190 71 +7e24 d 272 71 +7e31 8c 273 71 +7ebd 1 273 71 +7eca 2b 596 34 +7ef5 1 596 34 +7f02 7 614 72 +7f09 1 614 72 +7f0a c 65 68 +7f16 2 65 68 +7f18 c 72 68 +7f24 2 72 68 +7f26 c 103 69 +7f32 d 103 69 +7f3f 1 103 69 +7f40 c 105 69 +7f4c d 105 69 +7f59 1 105 69 +7f5a c 65 68 +7f66 2 65 68 +7f68 c 72 68 +7f74 2 72 68 +7f76 c 103 69 +7f82 d 103 69 +7f8f 1 103 69 +7f90 c 105 69 +7f9c d 105 69 +7fa9 1 105 69 +7faa c 105 69 +7fb6 d 105 69 +7fc3 1 105 69 +7fd0 d 575 34 +7fdd 1 575 34 +7fea d 575 34 +7ff7 1 575 34 +FUNC 7ff8 11 0 std::_Select1st, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >::operator()(std::pair, __gnu_cxx::hash, std::equal_to, std::allocator > >*> const&) const +7ff8 c 550 23 +8004 5 551 41 +8009 1 551 41 +8016 2f 600 34 +8045 1 600 34 +8046 c 84 70 +8052 1e 85 70 +FUNC 8070 11 0 std::_Select1st > >::operator()(std::pair >&) const +8070 c 546 23 +807c 5 547 41 +8081 1 547 41 +FUNC 8082 11 0 std::_Select1st > >::operator()(std::pair > const&) const +8082 c 550 23 +808e 5 551 41 +8093 1 551 41 +8094 c 128 34 +80a0 13 129 34 +80b3 1 129 34 +80b4 c 84 70 +80c0 1e 85 70 +80de c 65 68 +80ea 2 65 68 +80ec c 103 69 +80f8 d 103 69 +8105 1 103 69 +8106 c 65 68 +8112 2 65 68 +8114 c 72 68 +8120 2 72 68 +8122 c 105 69 +812e d 105 69 +813b 1 105 69 +813c c 103 69 +8148 d 103 69 +8155 1 103 69 +8156 c 105 69 +8162 d 105 69 +816f 1 105 69 +8170 c 80 71 +817c d 80 71 +8189 1 80 71 +818a c 67 68 +8196 2 67 68 +8198 c 99 69 +81a4 14 100 69 +FUNC 81b8 2b 0 std::_Vector_base<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::get_allocator() const +81b8 10 93 23 +81c8 1b 94 71 +81e3 1 94 71 +81e4 c 99 69 +81f0 14 100 69 +8210 2 107 68 +FUNC 8212 2e 0 void std::_Destroy<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >(__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>) +8212 c 171 23 +821e 2 173 73 +8220 12 174 73 +8232 c 173 73 +823e 2 174 73 +824c d 107 68 +8259 1 107 68 +825a c 67 68 +8266 2 67 68 +8268 c 99 69 +8274 14 100 69 +8288 c 403 40 +8294 1c 404 40 +82b0 a 406 40 +82ba a 407 40 +82c4 c 408 40 +82d0 e 409 40 +82de c 553 40 +82ea 36 554 40 +8320 2 555 40 +8322 c 103 69 +832e d 103 69 +833b 1 103 69 +FUNC 833c 2b 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::get_allocator() const +833c 10 350 23 +834c 1b 351 40 +8367 1 351 40 +8368 c 69 70 +8374 2 69 70 +8382 d 107 68 +838f 1 107 68 +839c d 94 68 +83a9 1 94 68 +FUNC 83aa 2a 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_M_put_node(std::_Rb_tree_node >*) +83aa c 359 23 +83b6 1e 360 40 +FUNC 83d4 59 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::destroy_node(std::_Rb_tree_node >*) +83d4 d 387 23 +83e1 35 389 40 +8416 17 390 40 +842d 1 390 40 +FUNC 842e 56 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_M_erase(std::_Rb_tree_node >*) +842e c 1051 23 +843a 2 1054 40 +843c 1a 1056 40 +8456 e 1057 40 +8464 12 1058 40 +8476 6 1059 40 +847c 6 1054 40 +8482 2 1059 40 +8484 d 569 40 +8491 58 570 40 +84e9 1 570 40 +84ea c 147 45 +84f6 31 148 45 +8527 1 148 45 +8528 c 92 45 +8534 d 92 45 +8541 1 92 45 +8542 c 67 68 +854e 2 67 68 +8550 c 99 69 +855c 14 100 69 +8570 c 403 40 +857c 1c 404 40 +8598 a 406 40 +85a2 a 407 40 +85ac c 408 40 +85b8 e 409 40 +85c6 c 553 40 +85d2 36 554 40 +8608 2 555 40 +860a c 103 69 +8616 d 103 69 +8623 1 103 69 +FUNC 8624 2b 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::get_allocator() const +8624 10 350 23 +8634 1b 351 40 +864f 1 351 40 +8650 c 69 70 +865c d 69 70 +8669 1 69 70 +866a c 69 70 +8676 30 69 70 +86b2 d 107 68 +86bf 1 107 68 +86cc d 94 68 +86d9 1 94 68 +FUNC 86da 2a 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_M_put_node(std::_Rb_tree_node > >*) +86da c 359 23 +86e6 1e 360 40 +FUNC 8704 59 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::destroy_node(std::_Rb_tree_node > >*) +8704 d 387 23 +8711 35 389 40 +8746 17 390 40 +875d 1 390 40 +FUNC 875e 56 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_M_erase(std::_Rb_tree_node > >*) +875e c 1051 23 +876a 2 1054 40 +876c 1a 1056 40 +8786 e 1057 40 +8794 12 1058 40 +87a6 6 1059 40 +87ac 6 1054 40 +87b2 2 1059 40 +87b4 d 569 40 +87c1 58 570 40 +8819 1 570 40 +881a c 147 45 +8826 31 148 45 +8857 1 148 45 +8858 c 92 45 +8864 d 92 45 +8871 1 92 45 +8872 c 603 72 +887e c 603 72 +FUNC 888a 23 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::begin() +888a c 333 23 +8896 17 334 71 +88ad 1 334 71 +88ba 2a 654 72 +FUNC 88e4 42 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::operator[](unsigned long) +88e4 c 494 23 +88f0 36 495 71 +FUNC 8926 26 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::end() +8926 c 351 23 +8932 1a 352 71 +FUNC 894c 28 0 bool std::operator==, std::allocator >(std::basic_string, std::allocator > const&, std::basic_string, std::allocator > const&) +894c c 2115 24 +8958 1c 2116 37 +FUNC 8974 23 0 std::equal_to::operator()(std::string const&, std::string const&) const +8974 c 199 24 +8980 17 200 41 +8997 1 200 41 +8998 c 80 71 +89a4 d 80 71 +89b1 1 80 71 +89b2 c 67 68 +89be 2 67 68 +89c0 c 99 69 +89cc 14 100 69 +FUNC 89e0 2b 0 std::_Vector_base<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::get_allocator() const +89e0 10 93 24 +89f0 1b 94 71 +8a0b 1 94 71 +8a0c c 99 69 +8a18 14 100 69 +8a2c c 84 71 +8a38 2f 85 71 +8a67 2 86 71 +8a69 1 86 71 +8a6a c 96 71 +8a76 12 97 71 +8a88 2 98 71 +8a96 2 107 68 +FUNC 8a98 2e 0 void std::_Destroy<__gnu_cxx::_Hashtable_node > >**, std::allocator<__gnu_cxx::_Hashtable_node > >*> >(__gnu_cxx::_Hashtable_node > >**, __gnu_cxx::_Hashtable_node > >**, std::allocator<__gnu_cxx::_Hashtable_node > >*>) +8a98 c 171 24 +8aa4 2 173 73 +8aa6 12 174 73 +8ab8 c 173 73 +8ac4 2 174 73 +FUNC 8ac6 13 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::max_size() const +8ac6 c 407 24 +8ad2 7 408 71 +8ad9 1 408 71 +8ada c 603 72 +8ae6 c 603 72 +FUNC 8af2 26 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::end() +8af2 c 351 24 +8afe 1a 352 71 +FUNC 8b18 23 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::begin() +8b18 c 333 24 +8b24 17 334 71 +8b3b 1 334 71 +8b48 2a 654 72 +8b7e 7 614 72 +8b85 1 614 72 +FUNC 8b86 42 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::operator[](unsigned long) +8b86 c 494 24 +8b92 36 495 71 +8bd4 d 107 68 +8be1 1 107 68 +FUNC 8be2 28 0 void std::swap<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**>(__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**&, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**&) +8be2 c 92 25 +8bee 8 97 61 +8bf6 a 98 61 +8c00 a 99 61 +FUNC 8c0a 50 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::swap(std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >&) +8c0a c 733 25 +8c16 12 735 71 +8c28 18 736 71 +8c40 1a 737 71 +8c66 2b 596 34 +8c91 1 596 34 +8c9e 2f 600 34 +8ccd 1 600 34 +FUNC 8cce 28 0 void std::swap<__gnu_cxx::_Hashtable_node > >**>(__gnu_cxx::_Hashtable_node > >**&, __gnu_cxx::_Hashtable_node > >**&) +8cce c 92 25 +8cda 8 97 61 +8ce2 a 98 61 +8cec a 99 61 +FUNC 8cf6 50 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::swap(std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >&) +8cf6 c 733 25 +8d02 12 735 71 +8d14 18 736 71 +8d2c 1a 737 71 +8d46 c 84 71 +8d52 2f 85 71 +8d81 2 86 71 +8d83 1 86 71 +8d84 c 96 71 +8d90 12 97 71 +8da2 2 98 71 +FUNC 8da4 13 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::max_size() const +8da4 c 407 25 +8db0 7 408 71 +8db7 1 408 71 +8dc4 d 94 68 +8dd1 1 94 68 +FUNC 8dd2 2f 0 std::_Vector_base<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::_M_deallocate(__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, unsigned long) +8dd2 c 120 25 +8dde 6 122 71 +8de4 1d 123 71 +8e01 1 123 71 +8e02 c 108 71 +8e0e 3d 109 71 +8e4b 1 109 71 +8e4c c 272 71 +8e58 4b 273 71 +8ea3 1 273 71 +8ea4 c 188 71 +8eb0 12 189 71 +8ec2 2 190 71 +8ec4 c 603 72 +8ed0 c 603 72 +FUNC 8edc 2b 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::begin() const +8edc c 342 25 +8ee8 1f 343 71 +8f07 1 343 71 +FUNC 8f08 2c 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::end() const +8f08 c 360 25 +8f14 20 361 71 +8f40 5 666 72 +8f45 1 666 72 +8f53 2b 759 72 +FUNC 8f7e 3c 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::size() const +8f7e c 402 25 +8f8a 30 403 71 +8fc6 26 588 34 +8ff8 15 511 34 +900d 79 513 34 +9086 21 517 34 +90a7 1 517 34 +90b4 14 225 42 +90d4 26 592 34 +FUNC 90fa 49 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::capacity() const +90fa c 449 25 +9106 3d 451 71 +9143 1 451 71 +9144 c 103 69 +9150 d 103 69 +915d 1 103 69 +916e 1b 286 34 +9189 1 286 34 +9196 d 94 68 +91a3 1 94 68 +91b0 1e 301 34 +91db 56 622 34 +9231 17 623 34 +9254 9 1080 34 +925d 1a 1082 34 +9277 2 1083 34 +9279 8 1085 34 +9281 12 1086 34 +9293 6 1087 34 +9299 6 1083 34 +929f 1b 1089 34 +92ba 1d 1080 34 +92d7 c 1091 34 +92e3 1 1091 34 +92e4 d 360 34 +92f1 77 361 34 +9368 c 93 42 +9374 d 93 42 +9381 1 93 42 +9382 c 72 68 +938e 2 72 68 +9390 c 105 69 +939c d 105 69 +93a9 1 105 69 +93aa c 301 66 +93b6 d 301 66 +93c3 1 301 66 +93d0 d 94 68 +93dd 1 94 68 +FUNC 93de 2f 0 std::_Vector_base<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::_M_deallocate(__gnu_cxx::_Hashtable_node > >**, unsigned long) +93de c 120 26 +93ea 6 122 71 +93f0 1d 123 71 +940d 1 123 71 +940e c 108 71 +941a 3d 109 71 +9457 1 109 71 +9458 c 188 71 +9464 12 189 71 +9476 2 190 71 +9478 c 272 71 +9484 4b 273 71 +94cf 1 273 71 +94d0 c 603 72 +94dc c 603 72 +FUNC 94e8 2b 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::begin() const +94e8 c 342 26 +94f4 1f 343 71 +9513 1 343 71 +FUNC 9514 2c 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::end() const +9514 c 360 26 +9520 20 361 71 +954c 2d 662 72 +9579 1 662 72 +FUNC 957a 2d 0 unsigned long const& std::max(unsigned long const&, unsigned long const&) +957a c 206 26 +9586 e 211 61 +9594 8 212 61 +959c b 213 61 +95a7 1 213 61 +95b4 19 650 72 +95cd 1 650 72 +95da 5 666 72 +95df 1 666 72 +95ed 2b 759 72 +9624 5 666 72 +9629 1 666 72 +9637 2b 759 72 +FUNC 9662 49 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::capacity() const +9662 c 449 26 +966e 3d 451 71 +96ab 1 451 71 +FUNC 96ac 3c 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::size() const +96ac c 402 26 +96b8 30 403 71 +96f4 26 588 34 +9726 26 592 34 +974c c 103 69 +9758 d 103 69 +9765 1 103 69 +9776 1b 286 34 +9791 1 286 34 +979e d 94 68 +97ab 1 94 68 +97b8 1e 301 34 +97e3 56 622 34 +9839 17 623 34 +985c 9 1080 34 +9865 1a 1082 34 +987f 2 1083 34 +9881 8 1085 34 +9889 12 1086 34 +989b 6 1087 34 +98a1 6 1083 34 +98a7 1b 1089 34 +98c2 1d 1080 34 +98df c 1091 34 +98eb 1 1091 34 +98ec d 360 34 +98f9 77 361 34 +9970 c 69 70 +997c 20 69 70 +99a9 5c 104 68 +9a05 1 104 68 +9a06 c 69 70 +9a12 2c 69 70 +9a4b 5c 104 68 +9aa7 1 104 68 +9ab4 2d 662 72 +9ae1 1 662 72 +9aee 19 650 72 +9b07 1 650 72 +9b14 5 666 72 +9b19 1 666 72 +9b27 2b 759 72 +9b52 c 72 68 +9b5e 2 72 68 +9b60 c 105 69 +9b6c d 105 69 +9b79 1 105 69 +9b7a c 69 70 +9b86 2 69 70 +9b94 d 107 68 +9ba1 1 107 68 +9bae d 94 68 +9bbb 1 94 68 +FUNC 9bbc 2a 0 std::_List_base, std::allocator > >::_M_put_node(std::_List_node >*) +9bbc c 315 26 +9bc8 1e 316 66 +FUNC 9be6 35 0 bool __gnu_cxx::operator!=<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > > const&, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > > const&) +9be6 d 699 26 +9bf3 28 700 72 +9c1b 1 700 72 +9c28 d 623 72 +9c35 5 624 72 +FUNC 9c3a 4b 0 void std::__fill::fill<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, __gnu_cxx::_Hashtable_node > >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, __gnu_cxx::_Hashtable_node > >* const&) +9c3a c 539 61 +9c46 8 541 61 +9c4e 2 542 61 +9c50 12 543 61 +9c62 21 542 61 +9c83 2 543 61 +9c85 1 543 61 +FUNC 9c86 2b 0 void std::fill<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, __gnu_cxx::_Hashtable_node > >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, __gnu_cxx::_Hashtable_node > >* const&) +9c86 c 560 26 +9c92 4 567 61 +9c96 1b 568 61 +9cb1 1 568 61 +FUNC 9cb2 4b 0 void std::_Destroy<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, std::allocator<__gnu_cxx::_Hashtable_node > >*> >(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, std::allocator<__gnu_cxx::_Hashtable_node > >*>) +9cb2 c 171 26 +9cbe 2 173 73 +9cc0 1a 174 73 +9cda 21 173 73 +9cfb 2 174 73 +9cfd 1 174 73 +9d0a 7 98 68 +9d11 1 98 68 +9d1e 1d 85 68 +9d3b 5 86 68 +9d40 16 88 68 +9d62 1d 297 34 +9d7f 1 297 34 +9d8d e 605 34 +9d9b 9 606 34 +9da4 3c 609 34 +9de0 b 610 34 +9deb 11 609 34 +9dfc b 612 34 +9e07 12 614 34 +9e19 b 615 34 +9e24 13 612 34 +9e37 8 615 34 +9e3f 1 615 34 +9e4d 15 751 34 +9e62 1a 752 34 +9e7c b 754 34 +9e87 49 755 34 +9ed0 3b 756 34 +9f0b 12 754 34 +9f1d 15 758 34 +9f32 8 759 34 +9f3a 1c 760 34 +9f56 f 761 34 +9f65 41 762 34 +9fb2 7 98 68 +9fb9 1 98 68 +9fc6 1d 85 68 +9fe3 5 86 68 +9fe8 17 88 68 +9fff 1 88 68 +a00c 1d 297 34 +a029 1 297 34 +a037 e 605 34 +a045 9 606 34 +a04e 3c 609 34 +a08a b 610 34 +a095 11 609 34 +a0a6 b 612 34 +a0b1 12 614 34 +a0c3 b 615 34 +a0ce 13 612 34 +a0e1 8 615 34 +a0e9 1 615 34 +a0f7 15 751 34 +a10c 1a 752 34 +a126 b 754 34 +a131 49 755 34 +a17a 3b 756 34 +a1b5 12 754 34 +a1c7 15 758 34 +a1dc 8 759 34 +a1e4 1c 760 34 +a200 f 761 34 +a20f 41 762 34 +FUNC a250 35 0 bool __gnu_cxx::operator!=<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > > const&, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > > const&) +a250 d 699 26 +a25d 28 700 72 +a285 1 700 72 +a292 d 623 72 +a29f 5 624 72 +FUNC a2a4 4b 0 void std::__fill::fill<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >* const&) +a2a4 c 539 61 +a2b0 8 541 61 +a2b8 2 542 61 +a2ba 12 543 61 +a2cc 21 542 61 +a2ed 2 543 61 +a2ef 1 543 61 +FUNC a2f0 2b 0 void std::fill<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >* const&) +a2f0 c 560 26 +a2fc 4 567 61 +a300 1b 568 61 +a31b 1 568 61 +FUNC a31c 4b 0 void std::_Destroy<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, __gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>) +a31c c 171 26 +a328 2 173 73 +a32a 1a 174 73 +a344 21 173 73 +a365 2 174 73 +a367 1 174 73 +a368 c 65 68 +a374 2 65 68 +a376 c 103 69 +a382 d 103 69 +a38f 1 103 69 +FUNC a390 2b 0 std::_List_base, std::allocator > >::get_allocator() const +a390 10 322 26 +a3a0 1b 324 66 +a3bb 1 324 66 +FUNC a3bc 7b 0 std::_List_base, std::allocator > >::_M_clear() +a3bc d 69 27 +a3c9 8 72 77 +a3d1 2 73 77 +a3d3 6 75 77 +a3d9 8 76 77 +a3e1 35 77 77 +a416 12 78 77 +a428 a 73 77 +a432 5 78 77 +a437 1 78 77 +a438 c 331 66 +a444 18 332 66 +a45c c 392 66 +a468 d 392 66 +a475 1 392 66 +a476 c 211 74 +a482 10 211 74 +a49e d 107 68 +a4ab 1 107 68 +FUNC a4ac 2e 0 void std::_Destroy >(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, std::allocator) +a4ac c 171 27 +a4b8 2 173 73 +a4ba 12 174 73 +a4cc c 173 73 +a4d8 2 174 73 +a4da c 272 71 +a4e6 4b 273 71 +a531 1 273 71 +a532 13 196 74 +a545 10 196 74 +a555 2f 197 74 +a584 1a 198 74 +a59e 13 196 74 +a5b1 10 196 74 +a5c1 2f 197 74 +a5f0 1a 198 74 +a616 7 98 68 +a61d 1 98 68 +a62a 1d 85 68 +a647 5 86 68 +a64c 10 88 68 +FUNC a65c 2a 0 std::_Vector_base<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::_M_allocate(unsigned long) +a65c c 116 27 +a668 1e 117 71 +a686 d 100 71 +a693 12 101 71 +a6a5 19 103 71 +a6be b 104 71 +a6c9 3a 105 71 +a703 1 105 71 +a710 7 98 68 +a717 1 98 68 +a724 1d 85 68 +a741 5 86 68 +a746 10 88 68 +FUNC a756 2a 0 std::_Vector_base<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::_M_allocate(unsigned long) +a756 c 116 27 +a762 1e 117 71 +a780 d 100 71 +a78d 12 101 71 +a79f 19 103 71 +a7b8 b 104 71 +a7c3 3a 105 71 +a7fd 1 105 71 +a80b 12 424 61 +a81d 2e 425 61 +a84b 13 426 61 +a86a 4 440 61 +a86e 1b 443 61 +a889 1 443 61 +a898 56 482 61 +a8fa 4 514 61 +a8fe 4 515 61 +a902 1b 517 61 +a91d 1 517 61 +a92a 8 616 61 +a932 2 617 61 +a934 8 618 61 +a93c f 617 61 +a94b 5 619 61 +a95c 4 641 61 +a960 1b 642 61 +a97b 1 642 61 +FUNC a97c 27 0 void std::__uninitialized_fill_n_aux<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>(__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >* const&, __true_type) +a97c c 182 28 +a988 1b 183 79 +a9a3 1 183 79 +FUNC a9a4 2f 0 void std::uninitialized_fill_n<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>(__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >* const&) +a9a4 c 214 28 +a9b0 23 218 79 +a9d3 1 218 79 +FUNC a9d4 27 0 void std::__uninitialized_fill_n_a<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>(__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >* const&, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>) +a9d4 c 308 28 +a9e0 1b 310 79 +a9fb 1 310 79 +a9fc c 200 71 +aa08 19 201 71 +aa21 42 203 71 +aa63 15 205 71 +aa85 11 992 34 +aa96 c 993 34 +aaa2 15 995 34 +aab7 c 996 34 +aac3 4a 998 34 +ab0d f 1001 34 +ab1c 1c 998 34 +ab38 1a 1003 34 +ab52 5 1004 34 +ab57 1f 1007 34 +ab76 1c 1008 34 +ab92 19 1009 34 +abab 19 1010 34 +abc4 1a 1011 34 +abde a 1004 34 +abe8 11 1001 34 +abf9 15 1014 34 +ac0e 13 1028 34 +ac21 b 1016 34 +ac2c 9 1018 34 +ac35 19 1023 34 +ac4e 23 1024 34 +ac71 19 1025 34 +ac8a 1d 1021 34 +aca7 1a 1018 34 +acc1 b 1028 34 +accc b 1016 34 +acd7 1e 1028 34 +acf5 1 1028 34 +ad06 16 438 34 +ad1c 37 439 34 +ad53 1 439 34 +ad64 37 212 42 +ad9b 1 212 42 +ada8 8 616 61 +adb0 2 617 61 +adb2 8 618 61 +adba f 617 61 +adc9 5 619 61 +adda 4 641 61 +adde 1b 642 61 +adf9 1 642 61 +FUNC adfa 27 0 void std::__uninitialized_fill_n_aux<__gnu_cxx::_Hashtable_node > >**, unsigned long, __gnu_cxx::_Hashtable_node > >*>(__gnu_cxx::_Hashtable_node > >**, unsigned long, __gnu_cxx::_Hashtable_node > >* const&, __true_type) +adfa c 182 28 +ae06 1b 183 79 +ae21 1 183 79 +FUNC ae22 2f 0 void std::uninitialized_fill_n<__gnu_cxx::_Hashtable_node > >**, unsigned long, __gnu_cxx::_Hashtable_node > >*>(__gnu_cxx::_Hashtable_node > >**, unsigned long, __gnu_cxx::_Hashtable_node > >* const&) +ae22 c 214 28 +ae2e 23 218 79 +ae51 1 218 79 +FUNC ae52 27 0 void std::__uninitialized_fill_n_a<__gnu_cxx::_Hashtable_node > >**, unsigned long, __gnu_cxx::_Hashtable_node > >*, __gnu_cxx::_Hashtable_node > >*>(__gnu_cxx::_Hashtable_node > >**, unsigned long, __gnu_cxx::_Hashtable_node > >* const&, std::allocator<__gnu_cxx::_Hashtable_node > >*>) +ae52 c 308 28 +ae5e 1b 310 79 +ae79 1 310 79 +ae7a c 200 71 +ae86 19 201 71 +ae9f 42 203 71 +aee1 15 205 71 +af03 11 992 34 +af14 c 993 34 +af20 15 995 34 +af35 c 996 34 +af41 4a 998 34 +af8b f 1001 34 +af9a 1c 998 34 +afb6 1a 1003 34 +afd0 5 1004 34 +afd5 1f 1007 34 +aff4 1c 1008 34 +b010 19 1009 34 +b029 19 1010 34 +b042 1a 1011 34 +b05c a 1004 34 +b066 11 1001 34 +b077 15 1014 34 +b08c 13 1028 34 +b09f b 1016 34 +b0aa 9 1018 34 +b0b3 19 1023 34 +b0cc 23 1024 34 +b0ef 19 1025 34 +b108 1d 1021 34 +b125 1a 1018 34 +b13f b 1028 34 +b14a b 1016 34 +b155 1e 1028 34 +b173 1 1028 34 +b184 16 438 34 +b19a 37 439 34 +b1d1 1 439 34 +b1e2 37 212 42 +b219 1 212 42 +b227 12 424 61 +b239 2e 425 61 +b267 13 426 61 +b286 4 440 61 +b28a 1b 443 61 +b2a5 1 443 61 +b2b4 56 482 61 +b316 4 514 61 +b31a 4 515 61 +b31e 1b 517 61 +b339 1 517 61 +b346 8 616 61 +b34e 2 617 61 +b350 12 618 61 +b362 16 617 61 +b378 5 619 61 +b37d 1 619 61 +b38a 4 641 61 +b38e 1b 642 61 +b3a9 1 642 61 +FUNC b3aa 27 0 void std::__uninitialized_fill_n_aux<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node > >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node > >* const&, __true_type) +b3aa c 182 28 +b3b6 1b 183 79 +b3d1 1 183 79 +FUNC b3d2 2f 0 void std::uninitialized_fill_n<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node > >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node > >* const&) +b3d2 c 214 28 +b3de 23 218 79 +b401 1 218 79 +FUNC b402 27 0 void std::__uninitialized_fill_n_a<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node > >*, __gnu_cxx::_Hashtable_node > >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node > >* const&, std::allocator<__gnu_cxx::_Hashtable_node > >*>) +b402 c 308 28 +b40e 1b 310 79 +b429 1 310 79 +b436 8 616 61 +b43e 2 617 61 +b440 12 618 61 +b452 16 617 61 +b468 5 619 61 +b46d 1 619 61 +b47a 4 641 61 +b47e 1b 642 61 +b499 1 642 61 +FUNC b49a 27 0 void std::__uninitialized_fill_n_aux<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >* const&, __true_type) +b49a c 182 28 +b4a6 1b 183 79 +b4c1 1 183 79 +FUNC b4c2 2f 0 void std::uninitialized_fill_n<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >* const&) +b4c2 c 214 28 +b4ce 23 218 79 +b4f1 1 218 79 +FUNC b4f2 27 0 void std::__uninitialized_fill_n_a<__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >* const&, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*>) +b4f2 c 308 28 +b4fe 1b 310 79 +b519 1 310 79 +b526 22 300 61 +b548 11 301 61 +b559 1 301 61 +b566 4 315 61 +b56a 1b 317 61 +b585 1 317 61 +b592 1b 326 61 +b5ad 1 326 61 +b5ba 4 384 61 +b5be 4 385 61 +b5c2 1b 387 61 +b5dd 1 387 61 +b5ea 1b 74 79 +b605 1 74 79 +b612 23 113 79 +b635 1 113 79 +b642 1b 254 79 +b65d 1 254 79 +b66a 15 763 71 +b67f 40 766 71 +b6bf 3 768 71 +b6c2 2 773 71 +FUNC b6c4 124 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::reserve(unsigned long) +b6c4 13 69 29 +b6d7 15 71 78 +b6ec e 72 78 +b6fa 19 73 78 +b713 e 75 78 +b721 28 78 78 +b749 3e 79 78 +b787 30 81 78 +b7b7 8 84 78 +b7bf 11 85 78 +b7d0 18 86 78 +b7f5 33 335 61 +b834 4 384 61 +b838 4 385 61 +b83c 1b 387 61 +b857 1 387 61 +b864 1b 74 79 +b87f 1 74 79 +b88c 23 113 79 +b8af 1 113 79 +b8bc 1b 254 79 +b8d7 1 254 79 +b8e6 56 354 61 +b948 4 384 61 +b94c 4 385 61 +b950 1b 387 61 +b96b 1 387 61 +b978 1b 74 79 +b993 1 74 79 +b9a0 23 113 79 +b9c3 1 113 79 +b9d0 1b 254 79 +b9eb 1 254 79 +FUNC b9ec 46e 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::_M_fill_insert(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node > >* const&) +b9ec 14 311 29 +ba00 b 313 78 +ba0b 24 315 78 +ba2f 8 318 78 +ba37 23 319 78 +ba5a 15 320 78 +ba6f c 321 78 +ba7b 51 323 78 +bacc 14 327 78 +bae0 30 328 78 +bb10 35 330 78 +bb45 48 334 78 +bb8d 17 338 78 +bba4 43 339 78 +bbe7 14 342 78 +bbfb 1e 343 78 +bc19 e 348 78 +bc27 1e 349 78 +bc45 e 350 78 +bc53 1d 353 78 +bc70 8 354 78 +bc78 e 355 78 +bc86 27 357 78 +bcad 6 358 78 +bcb3 4d 361 78 +bd00 40 365 78 +bd40 18 367 78 +bd58 4d 368 78 +bda5 3e 379 78 +bde3 30 381 78 +be13 12 384 78 +be25 13 385 78 +be38 22 386 78 +FUNC be5a 2e 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::insert(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node > >**, std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> > >, unsigned long, __gnu_cxx::_Hashtable_node > >* const&) +be5a c 657 29 +be66 22 658 71 +be94 15 580 34 +bea9 15 581 34 +bebe 37 582 34 +bef5 c 583 34 +bf01 1 583 34 +bf02 d 335 34 +bf0f 4e 337 34 +bf5d 4d 338 34 +bfaa d 134 42 +bfb7 65 135 42 +c028 22 300 61 +c04a 11 301 61 +c05b 1 301 61 +c068 4 315 61 +c06c 1b 317 61 +c087 1 317 61 +c094 1b 326 61 +c0af 1 326 61 +c0bc 4 384 61 +c0c0 4 385 61 +c0c4 1b 387 61 +c0df 1 387 61 +c0ec 1b 74 79 +c107 1 74 79 +c114 23 113 79 +c137 1 113 79 +c144 1b 254 79 +c15f 1 254 79 +c16c 15 763 71 +c181 40 766 71 +c1c1 3 768 71 +c1c4 2 773 71 +FUNC c1c6 124 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::reserve(unsigned long) +c1c6 13 69 29 +c1d9 15 71 78 +c1ee e 72 78 +c1fc 19 73 78 +c215 e 75 78 +c223 28 78 78 +c24b 3e 79 78 +c289 30 81 78 +c2b9 8 84 78 +c2c1 11 85 78 +c2d2 18 86 78 +c2f7 33 335 61 +c336 4 384 61 +c33a 4 385 61 +c33e 1b 387 61 +c359 1 387 61 +c366 1b 74 79 +c381 1 74 79 +c38e 23 113 79 +c3b1 1 113 79 +c3be 1b 254 79 +c3d9 1 254 79 +c3e8 56 354 61 +c44a 4 384 61 +c44e 4 385 61 +c452 1b 387 61 +c46d 1 387 61 +c47a 1b 74 79 +c495 1 74 79 +c4a2 23 113 79 +c4c5 1 113 79 +c4d2 1b 254 79 +c4ed 1 254 79 +FUNC c4ee 46e 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::_M_fill_insert(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >* const&) +c4ee 14 311 29 +c502 b 313 78 +c50d 24 315 78 +c531 8 318 78 +c539 23 319 78 +c55c 15 320 78 +c571 c 321 78 +c57d 51 323 78 +c5ce 14 327 78 +c5e2 30 328 78 +c612 35 330 78 +c647 48 334 78 +c68f 17 338 78 +c6a6 43 339 78 +c6e9 14 342 78 +c6fd 1e 343 78 +c71b e 348 78 +c729 1e 349 78 +c747 e 350 78 +c755 1d 353 78 +c772 8 354 78 +c77a e 355 78 +c788 27 357 78 +c7af 6 358 78 +c7b5 4d 361 78 +c802 40 365 78 +c842 18 367 78 +c85a 4d 368 78 +c8a7 3e 379 78 +c8e5 30 381 78 +c915 12 384 78 +c927 13 385 78 +c93a 22 386 78 +FUNC c95c 2e 0 std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> >::insert(__gnu_cxx::__normal_iterator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >**, std::vector<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*, std::allocator<__gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >*> > >, unsigned long, __gnu_cxx::_Hashtable_node, __gnu_cxx::hash, std::equal_to, std::allocator > >*> >* const&) +c95c c 657 29 +c968 22 658 71 +c996 15 580 34 +c9ab 15 581 34 +c9c0 37 582 34 +c9f7 c 583 34 +ca03 1 583 34 +ca04 d 335 34 +ca11 4e 337 34 +ca5f 4d 338 34 +caac d 134 42 +cab9 65 135 42 +FUNC cb1e 44 0 dwarf2reader::CUFunctionInfoHandler::StartCompilationUnit(unsigned long long, unsigned char, unsigned char, unsigned long long, unsigned char) +cb1e 39 135 42 +cb57 5 102 30 +cb5c 6 103 30 +FUNC cb62 41 0 dwarf2reader::CUFunctionInfoHandler::ProcessAttributeString(unsigned long long, dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm, std::string const&) +cb62 18 136 30 +cb7a 10 137 30 +cb8a 17 138 30 +cba1 2 139 30 +cba3 1 139 30 +FUNC cba4 2a5 0 dwarf2reader::CUFunctionInfoHandler::ProcessAttributeUnsigned(unsigned long long, dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm, unsigned long long) +cba4 2d 144 30 +cbd1 a 145 30 +cbdb 58 146 30 +cc33 35 147 30 +cc68 32 146 30 +cc9a 2a 147 30 +ccc4 82 152 30 +cd46 18 153 30 +cd5e 1c 152 30 +cd7a 2f 153 30 +cda9 e 154 30 +cdb7 28 155 30 +cddf 12 157 30 +cdf1 2 158 30 +cdf3 12 160 30 +ce05 2 161 30 +ce07 c 163 30 +ce13 2 164 30 +ce15 2c 166 30 +ce41 8 172 30 +ce49 1 172 30 +FUNC ce4a 19c 0 dwarf2reader::CULineInfoHandler::AddLine(unsigned long long, unsigned int, unsigned int, unsigned int) +ce4a 20 84 30 +ce6a 1c 85 30 +ce86 9c 87 30 +cf22 4f 89 30 +cf71 19 87 30 +cf8a 25 90 30 +cfaf 30 93 30 +cfdf 7 95 30 +FUNC cfe6 9f 0 dwarf2reader::CUFunctionInfoHandler::EndDIE(unsigned long long) +cfe6 19 174 30 +cfff 1c 175 30 +d01b 65 177 30 +d080 5 178 30 +d085 1 178 30 +FUNC d086 164 0 dwarf2reader::CUFunctionInfoHandler::StartDIE(unsigned long long, dwarf2reader::DwarfTag, std::list, std::allocator > > const&) +d086 20 111 30 +d0a6 1c 112 30 +d0c2 c 126 30 +d0ce 23 115 30 +d0f1 26 116 30 +d117 1a 117 30 +d131 d 118 30 +d13e 1b 119 30 +d159 5f 120 30 +d1b8 c 124 30 +d1c4 1c 115 30 +d1e0 3 126 30 +d1e3 7 129 30 +FUNC d1ea 73 0 dwarf2reader::CULineInfoHandler::DefineDir(std::string const&, unsigned int) +d1ea 13 52 30 +d1fd 45 54 30 +d242 15 55 30 +d257 6 56 30 +d25d 1 56 30 +FUNC d25e 23b 0 dwarf2reader::CULineInfoHandler::DefineFile(std::string const&, int, unsigned int, unsigned long long, unsigned long long) +d25e 2c 60 30 +d28a 45 62 30 +d2cf 2f 65 30 +d2fe 24 66 30 +d322 b 68 30 +d32d e 69 30 +d33b 19 71 30 +d354 17 72 30 +d36b 93 74 30 +d3fe 64 77 30 +d462 30 79 30 +d492 7 81 30 +d499 1 81 30 +d49a 14 38 30 +d4ae 36 40 30 +d4e4 41 43 30 +d525 41 44 30 +d566 67 45 30 +d5cd 10 46 30 +d5dd 13 45 30 +d5f0 15 47 30 +d605 e 48 30 +d613 3d 49 30 +d650 20 50 30 +d670 14 38 30 +d684 36 40 30 +d6ba 41 43 30 +d6fb 41 44 30 +d73c 67 45 30 +d7a3 10 46 30 +d7b3 13 45 30 +d7c6 15 47 30 +d7db e 48 30 +d7e9 3d 49 30 +d826 20 50 30 +d846 12 125 74 +d858 12 125 74 +d86a 13 55 32 +d87d 35 55 32 +d8b2 13 98 32 +d8c5 35 98 32 +d8fa c 35 32 +d906 d 35 32 +d913 1 35 32 +d914 d 22 32 +d921 40 22 32 +d961 1 22 32 +d962 c 89 70 +d96e 1e 90 70 +d998 14 208 34 +d9ac c 190 67 +d9b8 a 190 67 +d9c2 c 259 67 +d9ce 21 259 67 +d9ef 1 259 67 +FUNC d9f0 13 0 std::auto_ptr::operator->() const +d9f0 c 283 35 +d9fc 7 286 67 +da03 1 286 67 +da11 5c 104 68 +da6d 1 104 68 +FUNC da6e 28 0 bool std::operator==, std::allocator >(std::basic_string, std::allocator > const&, char const*) +da6e c 2139 37 +da7a 1c 2140 37 +FUNC da96 5d 0 std::basic_string, std::allocator > std::operator+, std::allocator >(std::basic_string, std::allocator > const&, char const*) +da96 d 2081 37 +daa3 12 2083 37 +dab5 1a 2084 37 +dacf 24 2085 37 +daf3 1 2085 37 +FUNC daf4 5d 0 std::basic_string, std::allocator > std::operator+, std::allocator >(std::basic_string, std::allocator > const&, std::basic_string, std::allocator > const&) +daf4 d 2044 37 +db01 12 2046 37 +db13 1a 2047 37 +db2d 24 2048 37 +db51 1 2048 37 +db52 c 84 70 +db5e 17 85 70 +db75 1 85 70 +FUNC db76 2d 0 std::pair std::make_pair(char const*, unsigned int) +db76 c 144 37 +db82 21 145 70 +dba3 1 145 70 +dba4 c 84 70 +dbb0 23 85 70 +dbd3 1 85 70 +FUNC dbd4 3c 0 std::pair > std::make_pair >(unsigned long long, std::pair) +dbd4 1c 144 37 +dbf0 20 145 70 +dc10 d 89 70 +dc1d 64 90 70 +dc81 1 90 70 +dc82 c 89 70 +dc8e 2a 90 70 +dcb8 c 84 70 +dcc4 1d 85 70 +dce1 1 85 70 +FUNC dce2 3c 0 std::pair std::make_pair(unsigned long long, dwarf2reader::FunctionInfo*) +dce2 1c 144 37 +dcfe 20 145 70 +dd2a a 190 34 +dd40 d 194 34 +dd4d 1 194 34 +dd4e c 603 72 +dd5a c 603 72 +FUNC dd66 2b 0 std::vector >::begin() const +dd66 c 342 39 +dd72 1f 343 71 +dd91 1 343 71 +FUNC dd92 2c 0 std::vector >::end() const +dd92 c 360 39 +dd9e 20 361 71 +ddca 5 666 72 +ddcf 1 666 72 +dddd 2b 759 72 +FUNC de08 3c 0 std::vector >::size() const +de08 c 402 39 +de14 30 403 71 +FUNC de44 2b 0 std::vector >::begin() const +de44 c 342 39 +de50 1f 343 71 +de6f 1 343 71 +FUNC de70 2c 0 std::vector >::end() const +de70 c 360 39 +de7c 20 361 71 +dea9 31 759 72 +FUNC deda 3c 0 std::vector >::size() const +deda c 402 39 +dee6 30 403 71 +df16 c 603 72 +df22 c 603 72 +FUNC df2e 26 0 std::vector >::end() +df2e c 351 39 +df3a 1a 352 71 +df60 7 614 72 +df67 1 614 72 +FUNC df68 13 0 std::vector >::max_size() const +df68 c 407 39 +df74 7 408 71 +df7b 1 408 71 +df88 5 666 72 +df8d 1 666 72 +df9a d 623 72 +dfa7 5 624 72 +FUNC dfac 23 0 std::vector >::begin() +dfac c 333 39 +dfb8 17 334 71 +dfcf 1 334 71 +dfd0 c 35 32 +dfdc 26 35 32 +e00f 5c 104 68 +e06b 1 104 68 +e078 7 614 72 +e07f 1 614 72 +FUNC e080 35 0 dwarf2reader::SourceFileInfo::operator=(dwarf2reader::SourceFileInfo const&) +e080 c 35 39 +e08c 29 35 32 +e0b5 1 35 32 +FUNC e0b6 13 0 std::vector >::max_size() const +e0b6 c 407 39 +e0c2 7 408 71 +e0c9 1 408 71 +e0d6 d 623 72 +e0e3 5 624 72 +FUNC e0e8 3c 0 std::vector >::_M_range_check(unsigned long) const +e0e8 13 515 39 +e0fb 15 517 71 +e110 14 518 71 +FUNC e124 3c 0 std::vector >::_M_range_check(unsigned long) const +e124 13 515 39 +e137 15 517 71 +e14c 14 518 71 +e16c 2a 654 72 +FUNC e196 42 0 std::vector >::operator[](unsigned long) +e196 c 494 39 +e1a2 36 495 71 +FUNC e1d8 32 0 std::vector >::at(unsigned long) +e1d8 c 534 39 +e1e4 12 536 71 +e1f6 14 537 71 +e216 32 654 72 +FUNC e248 42 0 std::vector >::operator[](unsigned long) +e248 c 494 39 +e254 36 495 71 +FUNC e28a 32 0 std::vector >::at(unsigned long) +e28a c 534 39 +e296 12 536 71 +e2a8 14 537 71 +FUNC e2bc 14 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_M_end() +e2bc c 472 40 +e2c8 8 473 40 +FUNC e2d0 11 0 std::_Select1st > >::operator()(std::pair > const&) const +e2d0 c 550 41 +e2dc 5 551 41 +e2e1 1 551 41 +FUNC e2e2 53 0 std::less::operator()(unsigned long long const&, unsigned long long const&) const +e2e2 c 226 41 +e2ee 47 227 41 +e335 1 227 41 +FUNC e336 20 0 std::_Rb_tree_iterator > >::operator==(std::_Rb_tree_iterator > > const&) const +e336 c 209 41 +e342 14 210 40 +e356 c 84 70 +e362 18 85 70 +FUNC e37a 14 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_M_end() +e37a c 472 41 +e386 8 473 40 +FUNC e38e 11 0 std::_Select1st >::operator()(std::pair const&) const +e38e c 550 41 +e39a 5 551 41 +e39f 1 551 41 +FUNC e3a0 20 0 std::_Rb_tree_iterator >::operator==(std::_Rb_tree_iterator > const&) const +e3a0 c 209 41 +e3ac 14 210 40 +e3c0 c 84 70 +e3cc 18 85 70 +e3e4 c 180 34 +e3f0 13 181 34 +e403 1 181 34 +e410 22 409 34 +e43e d 207 42 +e44b 1 207 42 +FUNC e44c 35 0 bool __gnu_cxx::operator!= > >(__gnu_cxx::__normal_iterator > > const&, __gnu_cxx::__normal_iterator > > const&) +e44c d 699 42 +e459 28 700 72 +e481 1 700 72 +FUNC e482 4b 0 void std::_Destroy<__gnu_cxx::__normal_iterator > >, std::allocator >(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, std::allocator) +e482 c 171 43 +e48e 2 173 73 +e490 1a 174 73 +e4aa 21 173 73 +e4cb 2 174 73 +e4cd 1 174 73 +FUNC e4ce 35 0 bool __gnu_cxx::operator!= > >(__gnu_cxx::__normal_iterator > > const&, __gnu_cxx::__normal_iterator > > const&) +e4ce d 699 43 +e4db 28 700 72 +e503 1 700 72 +FUNC e504 4b 0 void std::_Destroy<__gnu_cxx::__normal_iterator > >, std::allocator >(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, std::allocator) +e504 c 171 43 +e510 2 173 73 +e512 1a 174 73 +e52c 21 173 73 +e54d 2 174 73 +e54f 1 174 73 +FUNC e550 14 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_S_value(std::_Rb_tree_node > > const*) +e550 c 480 43 +e55c 8 481 40 +FUNC e564 28 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_S_key(std::_Rb_tree_node > > const*) +e564 c 484 43 +e570 1c 485 40 +FUNC e58c 25 0 std::_Rb_tree_iterator >::operator--() +e58c c 194 43 +e598 14 196 40 +e5ac 5 197 40 +e5b1 1 197 40 +FUNC e5b2 25 0 std::_Rb_tree_iterator > >::operator--() +e5b2 c 194 43 +e5be 14 196 40 +e5d2 5 197 40 +e5d7 1 197 40 +FUNC e5d8 14 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_S_value(std::_Rb_tree_node_base const*) +e5d8 c 504 43 +e5e4 8 505 40 +FUNC e5ec 28 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_S_key(std::_Rb_tree_node_base const*) +e5ec c 508 43 +e5f8 1c 509 40 +FUNC e614 14 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_S_value(std::_Rb_tree_node > const*) +e614 c 480 43 +e620 8 481 40 +FUNC e628 28 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_S_key(std::_Rb_tree_node > const*) +e628 c 484 43 +e634 1c 485 40 +FUNC e650 14 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_S_value(std::_Rb_tree_node_base const*) +e650 c 504 43 +e65c 8 505 40 +FUNC e664 28 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_S_key(std::_Rb_tree_node_base const*) +e664 c 508 43 +e670 1c 509 40 +e698 7 614 72 +e69f 1 614 72 +e6ac 7 98 68 +e6b3 1 98 68 +e6c0 1d 85 68 +e6dd 5 86 68 +e6e2 10 88 68 +FUNC e6f2 2a 0 std::_Vector_base >::_M_allocate(unsigned long) +e6f2 c 116 43 +e6fe 1e 117 71 +e728 7 98 68 +e72f 1 98 68 +e73c 1d 85 68 +e759 5 86 68 +e75e 16 88 68 +FUNC e774 2a 0 std::_Vector_base >::_M_allocate(unsigned long) +e774 c 116 43 +e780 1e 117 71 +e7aa 3a 104 68 +e7f0 2a 654 72 +FUNC e81a 42 0 std::vector<__gnu_cxx::_Hashtable_node > >*, std::allocator<__gnu_cxx::_Hashtable_node > >*> >::operator[](unsigned long) const +e81a c 509 43 +e826 36 510 71 +FUNC e85c 4e 0 std::string* std::__copy_backward::copy_b(std::string*, std::string*, std::string*) +e85c c 408 61 +e868 14 411 61 +e87c 1e 412 61 +e89a b 411 61 +e8a5 5 413 61 +FUNC e8aa 2b 0 std::string* std::__copy_backward_aux(std::string*, std::string*, std::string*) +e8aa c 432 44 +e8b6 4 440 61 +e8ba 1b 443 61 +e8d5 1 443 61 +e8e4 56 482 61 +e946 4 514 61 +e94a 4 515 61 +e94e 1b 517 61 +e969 1 517 61 +FUNC e96a 69 0 void std::_Construct(std::string*, std::string const&) +e96a d 77 44 +e977 5c 81 73 +e9d3 1 81 73 +FUNC e9d4 54 0 dwarf2reader::SourceFileInfo* std::__copy_backward::copy_b(dwarf2reader::SourceFileInfo*, dwarf2reader::SourceFileInfo*, dwarf2reader::SourceFileInfo*) +e9d4 c 408 61 +e9e0 1a 411 61 +e9fa 1e 412 61 +ea18 b 411 61 +ea23 5 413 61 +FUNC ea28 2b 0 dwarf2reader::SourceFileInfo* std::__copy_backward_aux(dwarf2reader::SourceFileInfo*, dwarf2reader::SourceFileInfo*, dwarf2reader::SourceFileInfo*) +ea28 c 432 44 +ea34 4 440 61 +ea38 1b 443 61 +ea53 1 443 61 +ea62 56 482 61 +eac4 4 514 61 +eac8 4 515 61 +eacc 1b 517 61 +eae7 1 517 61 +FUNC eae8 69 0 void std::_Construct(dwarf2reader::SourceFileInfo*, dwarf2reader::SourceFileInfo const&) +eae8 d 77 44 +eaf5 5c 81 73 +eb51 1 81 73 +eb52 c 69 70 +eb5e 20 69 70 +eb7e c 69 70 +eb8a 2a 69 70 +ebc1 5c 104 68 +ec1d 1 104 68 +ec2a 15 523 34 +ec3f 79 525 34 +ecb8 21 529 34 +ecd9 1 529 34 +ece6 14 229 42 +ed06 7 98 68 +ed0d 1 98 68 +ed1a 1d 85 68 +ed37 5 86 68 +ed3c 10 88 68 +FUNC ed4c 29 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_M_get_node() +ed4c c 355 44 +ed58 1d 356 40 +ed75 1 356 40 +FUNC ed76 b6 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_M_create_node(std::pair > const&) +ed76 d 363 44 +ed83 e 365 40 +ed91 3c 367 40 +edcd b 373 40 +edd8 11 367 40 +ede9 b 368 40 +edf4 12 370 40 +ee06 b 371 40 +ee11 13 368 40 +ee24 8 373 40 +FUNC ee2c cd 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::_M_insert(std::_Rb_tree_node_base*, std::_Rb_tree_node_base*, std::pair > const&) +ee2c d 787 44 +ee39 15 789 40 +ee4e 5d 792 40 +eeab 24 796 40 +eecf f 798 40 +eede 1b 799 40 +eef9 1 799 40 +FUNC eefa 1ef 0 std::_Rb_tree >, std::_Select1st > >, std::less, std::allocator > > >::insert_unique(std::pair > const&) +eefa d 869 44 +ef07 e 871 40 +ef15 e 872 40 +ef23 4 873 40 +ef27 2 874 40 +ef29 6 876 40 +ef2f 35 877 40 +ef64 2a 878 40 +ef8e 6 874 40 +ef94 12 880 40 +efa6 a 881 40 +efb0 24 882 40 +efd4 51 883 40 +f025 b 885 40 +f030 36 886 40 +f066 4e 887 40 +f0b4 35 888 40 +f0e9 1 888 40 +FUNC f0ea 20 0 std::map, std::less, std::allocator > > >::insert(std::pair > const&) +f0ea c 359 45 +f0f6 14 360 45 +f116 7 98 68 +f11d 1 98 68 +f12a 1d 85 68 +f147 5 86 68 +f14c 1d 88 68 +f169 1 88 68 +FUNC f16a 29 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_M_get_node() +f16a c 355 45 +f176 1d 356 40 +f193 1 356 40 +FUNC f194 5f 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_M_create_node(std::pair const&) +f194 d 363 45 +f1a1 e 365 40 +f1af 3c 367 40 +f1eb 8 373 40 +f1f3 1 373 40 +FUNC f1f4 cd 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::_M_insert(std::_Rb_tree_node_base*, std::_Rb_tree_node_base*, std::pair const&) +f1f4 d 787 45 +f201 15 789 40 +f216 5d 792 40 +f273 24 796 40 +f297 f 798 40 +f2a6 1b 799 40 +f2c1 1 799 40 +FUNC f2c2 1ef 0 std::_Rb_tree, std::_Select1st >, std::less, std::allocator > >::insert_unique(std::pair const&) +f2c2 d 869 45 +f2cf e 871 40 +f2dd e 872 40 +f2eb 4 873 40 +f2ef 2 874 40 +f2f1 6 876 40 +f2f7 35 877 40 +f32c 2a 878 40 +f356 6 874 40 +f35c 12 880 40 +f36e a 881 40 +f378 24 882 40 +f39c 51 883 40 +f3ed b 885 40 +f3f8 36 886 40 +f42e 4e 887 40 +f47c 35 888 40 +f4b1 1 888 40 +FUNC f4b2 20 0 std::map, std::allocator > >::insert(std::pair const&) +f4b2 c 359 45 +f4be 14 360 45 +FUNC f4d2 19 0 void std::_Destroy(std::string*) +f4d2 c 106 45 +f4de d 107 73 +f4eb 1 107 73 +FUNC f4ec 44 0 void std::__destroy_aux<__gnu_cxx::__normal_iterator > > >(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, __false_type) +f4ec c 119 45 +f4f8 2 121 73 +f4fa 13 122 73 +f50d 21 121 73 +f52e 2 122 73 +FUNC f530 28 0 void std::_Destroy<__gnu_cxx::__normal_iterator > > >(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >) +f530 c 148 45 +f53c 1c 155 73 +f565 6 82 79 +f56b 2 85 79 +f56d 24 86 79 +f591 2c 85 79 +f5bd b 87 79 +f5c8 b 89 79 +f5d3 12 91 79 +f5e5 b 92 79 +f5f0 13 89 79 +f603 9 92 79 +f618 23 113 79 +f63b 1 113 79 +f648 1b 254 79 +f663 1 254 79 +FUNC f664 430 0 std::vector >::_M_insert_aux(__gnu_cxx::__normal_iterator > >, std::string const&) +f664 14 249 47 +f678 14 251 78 +f68c 22 253 78 +f6ae f 255 78 +f6bd 12 256 78 +f6cf 55 257 78 +f724 4b 260 78 +f76f e 264 78 +f77d 15 265 78 +f792 e 266 78 +f7a0 1d 271 78 +f7bd 8 272 78 +f7c5 e 273 78 +f7d3 27 275 78 +f7fa 6 276 78 +f800 55 279 78 +f855 25 284 78 +f87a b 285 78 +f885 4f 286 78 +f8d4 3 284 78 +f8d7 13 279 78 +f8ea e 286 78 +f8f8 4d 298 78 +f945 30 299 78 +f975 12 302 78 +f987 13 303 78 +f99a 23 304 78 +f9bd 3 298 78 +f9c0 13 286 78 +f9d3 b 292 78 +f9de 39 294 78 +fa17 23 295 78 +fa3a 8 296 78 +fa42 16 294 78 +fa58 3 296 78 +fa5b 19 292 78 +fa74 19 298 78 +fa8d 7 304 78 +FUNC fa94 70 0 std::vector >::push_back(std::string const&) +fa94 c 602 47 +faa0 10 604 71 +fab0 1e 606 71 +face 11 607 71 +fadf 25 610 71 +FUNC fb04 19 0 void std::_Destroy(dwarf2reader::SourceFileInfo*) +fb04 c 106 47 +fb10 d 107 73 +fb1d 1 107 73 +FUNC fb1e 44 0 void std::__destroy_aux<__gnu_cxx::__normal_iterator > > >(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, __false_type) +fb1e c 119 47 +fb2a 2 121 73 +fb2c 13 122 73 +fb3f 21 121 73 +fb60 2 122 73 +FUNC fb62 28 0 void std::_Destroy<__gnu_cxx::__normal_iterator > > >(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >) +fb62 c 148 47 +fb6e 1c 155 73 +fb97 6 82 79 +fb9d 2 85 79 +fb9f 24 86 79 +fbc3 2c 85 79 +fbef b 87 79 +fbfa b 89 79 +fc05 12 91 79 +fc17 b 92 79 +fc22 13 89 79 +fc35 9 92 79 +fc4a 23 113 79 +fc6d 1 113 79 +fc7a 1b 254 79 +fc95 1 254 79 +FUNC fc96 43d 0 std::vector >::_M_insert_aux(__gnu_cxx::__normal_iterator > >, dwarf2reader::SourceFileInfo const&) +fc96 14 249 47 +fcaa 14 251 78 +fcbe 22 253 78 +fce0 f 255 78 +fcef 12 256 78 +fd01 55 257 78 +fd56 4b 260 78 +fda1 e 264 78 +fdaf 15 265 78 +fdc4 e 266 78 +fdd2 1d 271 78 +fdef 8 272 78 +fdf7 e 273 78 +fe05 27 275 78 +fe2c 6 276 78 +fe32 55 279 78 +fe87 25 284 78 +feac b 285 78 +feb7 4f 286 78 +ff06 3 284 78 +ff09 13 279 78 +ff1c e 286 78 +ff2a 4d 298 78 +ff77 36 299 78 +ffad 12 302 78 +ffbf 13 303 78 +ffd2 2a 304 78 +fffc 3 298 78 +ffff 13 286 78 +10012 b 292 78 +1001d 39 294 78 +10056 23 295 78 +10079 8 296 78 +10081 16 294 78 +10097 3 296 78 +1009a 19 292 78 +100b3 19 298 78 +100cc 7 304 78 +100d3 1 304 78 +FUNC 100d4 70 0 std::vector >::push_back(dwarf2reader::SourceFileInfo const&) +100d4 c 602 47 +100e0 10 604 71 +100f0 1e 606 71 +1010e 11 607 71 +1011f 25 610 71 +FUNC 10144 16c 0 Start +10144 17 610 71 +1015b 40 49 48 +1019b 6 51 48 +101a1 3f 53 48 +101e0 7 54 48 +101e7 5 55 48 +101ec 2a 58 48 +10216 61 61 48 +10277 7 62 48 +1027e 2 63 48 +10280 29 66 48 +102a9 7 67 48 +FUNC 102b0 108 0 Usage +102b0 19 70 48 +102c9 30 71 48 +102f9 29 73 48 +10322 30 74 48 +10352 30 75 48 +10382 30 76 48 +103b2 6 77 48 +FUNC 103b8 3af 0 SetupOptions +103b8 21 80 48 +103d9 8 82 48 +103e1 6 85 48 +103e7 10 86 48 +103f7 2e 88 48 +10425 2f 94 48 +10454 2a 91 48 +1047e 23 95 48 +104a1 3e 97 48 +104df 11 98 48 +104f0 7c 99 48 +1056c c 100 48 +10578 5 99 48 +1057d 3e 101 48 +105bb 11 102 48 +105cc 3e 103 48 +1060a 11 104 48 +1061b 37 106 48 +10652 b 107 48 +1065d c 108 48 +10669 b 113 48 +10674 c 114 48 +10680 14 119 48 +10694 30 120 48 +106c4 b 121 48 +106cf c 122 48 +106db 81 127 48 +1075c b 128 48 +10767 1 128 48 +FUNC 10768 a7 0 main +10768 13 131 48 +1077b 37 132 48 +107b2 1e 135 48 +107d0 e 136 48 +107de 8 137 48 +107e6 17 139 48 +107fd c 141 48 +10809 6 142 48 +1080f 1 142 48 +10810 c 47 49 +1081c 1a 48 49 +10836 2 49 49 +10838 c 47 49 +10844 1a 48 49 +1085e 2 49 49 +FUNC 10860 cb 0 google_breakpad::FileID::FileIdentifier(unsigned char*) +10860 f 51 49 +1086f 16 52 49 +10885 6 53 49 +1088b f 54 49 +1089a b 57 49 +108a5 7 62 49 +108ac 2 63 49 +108ae 1c 64 49 +108ca 32 63 49 +108fc b 67 49 +10907 12 68 49 +10919 10 70 49 +10929 2 71 49 +1092b 1 71 49 +FUNC 1092c f2 0 google_breakpad::FileID::MachoIdentifier(int, unsigned char*) +1092c 10 73 49 +1093c 15 74 49 +10951 20 76 49 +10971 f 77 49 +10980 20 79 49 +109a0 c 80 49 +109ac 69 82 49 +10a15 9 83 49 +FUNC 10a1e fb 0 google_breakpad::FileID::ConvertIdentifierToString(unsigned char const*, char*, int) +10a1e c 87 49 +10a2a 7 88 49 +10a31 c 89 49 +10a3d 15 90 49 +10a52 12 91 49 +10a64 18 93 49 +10a7c e 94 49 +10a8a 2b 96 49 +10ab5 2b 97 49 +10ae0 17 89 49 +10af7 20 101 49 +10b17 2 102 49 +10b19 1 102 49 +FUNC 10b1a 13 0 NXHostByteOrder +10b1a c 144 56 +10b26 5 147 56 +10b2b 2 153 56 +10b2d 1 153 56 +10b2e c 56 51 +10b3a 1a 57 51 +10b54 1e 58 51 +10b72 2 59 51 +10b74 c 56 51 +10b80 1a 57 51 +10b9a 1e 58 51 +10bb8 2 59 51 +10bba c 61 51 +10bc6 e 62 51 +10bd4 11 63 51 +10be5 2 64 51 +10be7 1 64 51 +10be8 c 61 51 +10bf4 e 62 51 +10c02 11 63 51 +10c13 2 64 51 +10c15 1 64 51 +FUNC 10c16 477 0 MacFileUtilities::MachoID::UpdateCRC(unsigned char*, unsigned long) +10c16 c 74 51 +10c22 11 82 51 +10c33 14 83 51 +10c47 5 86 51 +10c4c 9 87 51 +10c55 7 88 51 +10c5c 18b 90 51 +10de7 6 91 51 +10ded 14 89 51 +10e01 23 93 51 +10e24 23 94 51 +10e47 d 86 51 +10e54 f 98 51 +10e63 6 100 51 +10e69 18b 101 51 +10ff4 6 102 51 +10ffa c 99 51 +11006 13 105 51 +11019 8 106 51 +11021 10 104 51 +11031 23 108 51 +11054 23 109 51 +11077 14 110 51 +1108b 2 112 51 +1108d 1 112 51 +FUNC 1108e 2c 0 MacFileUtilities::MachoID::UpdateMD5(unsigned char*, unsigned long) +1108e c 114 51 +1109a 1e 115 51 +110b8 2 116 51 +FUNC 110ba 2c 0 MacFileUtilities::MachoID::UpdateSHA1(unsigned char*, unsigned long) +110ba c 118 51 +110c6 1e 119 51 +110e4 2 120 51 +FUNC 110e6 121 0 MacFileUtilities::MachoID::Update(MacFileUtilities::MachoWalker*, unsigned long, unsigned long) +110e6 f 122 51 +110f5 1b 123 51 +11110 e 129 51 +1111e 5 130 51 +11123 9 131 51 +1112c 7 132 51 +11133 a 133 51 +1113d 6 135 51 +11143 7 136 51 +1114a 35 139 51 +1117f 6c 142 51 +111eb 10 143 51 +111fb a 130 51 +11205 2 145 51 +11207 1 145 51 +FUNC 11208 cf 0 MacFileUtilities::MachoID::UUIDCommand(int, unsigned char*) +11208 14 147 51 +1121c 25 149 51 +11241 7 151 51 +11248 19 152 51 +11261 9 153 51 +1126a 8 157 51 +11272 1f 158 51 +11291 9 159 51 +1129a 36 162 51 +112d0 7 163 51 +112d7 1 163 51 +FUNC 112d8 224 0 MacFileUtilities::MachoID::IDCommand(int, unsigned char*) +112d8 15 165 51 +112ed 25 167 51 +11312 7 169 51 +11319 19 170 51 +11332 c 171 51 +1133e c 175 51 +1134a 6 180 51 +11350 7 181 51 +11357 9 182 51 +11360 9 183 51 +11369 28 185 51 +11391 33 186 51 +113c4 1e 185 51 +113e2 10 189 51 +113f2 10 190 51 +11402 10 191 51 +11412 d 192 51 +1141f 10 193 51 +1142f 10 194 51 +1143f 10 195 51 +1144f d 196 51 +1145c 17 197 51 +11473 17 198 51 +1148a 17 199 51 +114a1 14 200 51 +114b5 9 202 51 +114be 36 205 51 +114f4 8 206 51 +FUNC 114fc d1 0 MacFileUtilities::MachoID::Adler32(int) +114fc 14 208 51 +11510 25 209 51 +11535 27 210 51 +1155c d 211 51 +11569 19 213 51 +11582 9 214 51 +1158b 3b 216 51 +115c6 7 217 51 +115cd 1 217 51 +FUNC 115ce f8 0 MacFileUtilities::MachoID::MD5(int, unsigned char*) +115ce 14 219 51 +115e2 25 220 51 +11607 27 221 51 +1162e 19 223 51 +11647 19 224 51 +11660 9 225 51 +11669 17 227 51 +11680 9 228 51 +11689 36 231 51 +116bf 7 232 51 +FUNC 116c6 f8 0 MacFileUtilities::MachoID::SHA1(int, unsigned char*) +116c6 14 234 51 +116da 25 235 51 +116ff 27 236 51 +11726 19 238 51 +1173f 19 239 51 +11758 9 240 51 +11761 17 242 51 +11778 9 243 51 +11781 36 246 51 +117b7 7 247 51 +FUNC 117be 378 0 MacFileUtilities::MachoID::WalkerCB(MacFileUtilities::MachoWalker*, load_command*, long long, bool, void*) +117be 2b 251 51 +117e9 6 252 51 +117ef e 254 51 +117fd 38 257 51 +11835 f 258 51 +11844 9 260 51 +1184d 17 261 51 +11864 20 266 51 +11884 f 267 51 +11893 d 271 51 +118a0 c 273 51 +118ac 38 274 51 +118e4 f 275 51 +118f3 9 277 51 +118fc 1f 278 51 +1191b 14 282 51 +1192f 2b 283 51 +1195a d 285 51 +11967 19 273 51 +11980 e 287 51 +1198e 38 290 51 +119c6 f 291 51 +119d5 9 293 51 +119de 17 294 51 +119f5 20 299 51 +11a15 f 300 51 +11a24 d 304 51 +11a31 c 306 51 +11a3d 38 307 51 +11a75 f 308 51 +11a84 9 310 51 +11a8d 1f 311 51 +11aac 1a 315 51 +11ac6 39 316 51 +11aff d 318 51 +11b0c 11 306 51 +11b1d 10 323 51 +11b2d 9 324 51 +FUNC 11b36 95 0 MacFileUtilities::MachoID::UUIDWalkerCB(MacFileUtilities::MachoWalker*, load_command*, long long, bool, void*) +11b36 1e 328 51 +11b54 a 329 51 +11b5e 6 331 51 +11b64 2f 333 51 +11b93 9 335 51 +11b9c 6 337 51 +11ba2 14 338 51 +11bb6 9 340 51 +11bbf a 344 51 +11bc9 2 345 51 +11bcb 1 345 51 +FUNC 11bcc 95 0 MacFileUtilities::MachoID::IDWalkerCB(MacFileUtilities::MachoWalker*, load_command*, long long, bool, void*) +11bcc 1e 349 51 +11bea a 350 51 +11bf4 6 351 51 +11bfa 2f 353 51 +11c29 9 354 51 +11c32 6 356 51 +11c38 14 357 51 +11c4c 9 359 51 +11c55 a 363 51 +11c5f 2 364 51 +11c61 1 364 51 +FUNC 11c62 1c 0 _OSSwapInt32 +11c62 f 53 55 +11c71 8 55 55 +11c79 3 56 55 +11c7c 2 57 55 +FUNC 11c7e 19 0 NXSwapInt +11c7e f 52 56 +11c8d 8 54 56 +11c95 2 55 56 +11c97 1 55 56 +FUNC 11c98 13 0 NXHostByteOrder +11c98 c 144 56 +11ca4 5 147 56 +11ca9 2 153 56 +11cab 1 153 56 +11cac c 52 54 +11cb8 12 54 54 +11cca 1a 55 54 +11ce4 2 56 54 +11ce6 c 52 54 +11cf2 12 54 54 +11d04 1a 55 54 +11d1e 2 56 54 +11d20 c 58 54 +11d2c a 59 54 +11d36 d 60 54 +11d43 2 61 54 +11d45 1 61 54 +11d46 c 58 54 +11d52 a 59 54 +11d5c d 60 54 +11d69 2 61 54 +11d6b 1 61 54 +FUNC 11d6c 37 0 MacFileUtilities::MachoWalker::ValidateCPUType(int) +11d6c c 63 54 +11d78 6 66 54 +11d7e 8 67 54 +11d86 6 68 54 +11d8c b 69 54 +11d97 7 74 54 +11d9e 3 80 54 +11da1 2 81 54 +11da3 1 81 54 +FUNC 11da4 50 0 MacFileUtilities::MachoWalker::ReadBytes(void*, unsigned long, long long) +11da4 18 96 54 +11dbc 36 97 54 +11df2 2 98 54 +FUNC 11df4 73 0 MacFileUtilities::MachoWalker::CurrentHeader(mach_header_64*, long long*) +11df4 c 100 54 +11e00 a 101 54 +11e0a 37 102 54 +11e41 11 103 54 +11e52 9 104 54 +11e5b a 107 54 +11e65 2 108 54 +11e67 1 108 54 +FUNC 11e68 2a6 0 MacFileUtilities::MachoWalker::FindHeader(int, long long&) +11e68 c 110 54 +11e74 15 111 54 +11e89 31 114 54 +11eba c 115 54 +11ec6 10 117 54 +11ed6 4 120 54 +11eda 14 121 54 +11eee 4 122 54 +11ef2 11 129 54 +11f03 28 124 54 +11f2b c 126 54 +11f37 31 133 54 +11f68 c 134 54 +11f74 14 136 54 +11f88 b 137 54 +11f93 8 139 54 +11f9b c 140 54 +11fa7 10 142 54 +11fb7 c 143 54 +11fc3 10 146 54 +11fd3 31 148 54 +12004 c 149 54 +12010 f 151 54 +1201f 14 152 54 +12033 16 154 54 +12049 c 158 54 +12055 31 159 54 +12086 9 160 54 +1208f f 162 54 +1209e 1c 163 54 +120ba 8 165 54 +120c2 10 166 54 +120d2 9 167 54 +120db 16 170 54 +120f1 11 158 54 +12102 a 174 54 +1210c 2 175 54 +FUNC 1210e 109 0 MacFileUtilities::MachoWalker::WalkHeaderCore(long long, unsigned int, bool) +1210e 1e 224 54 +1212c c 225 54 +12138 2f 227 54 +12167 c 228 54 +12173 6 230 54 +12179 14 231 54 +1218d 5b 234 54 +121e8 12 237 54 +121fa 11 225 54 +1220b a 240 54 +12215 2 241 54 +12217 1 241 54 +FUNC 12218 10e 0 MacFileUtilities::MachoWalker::WalkHeader64AtOffset(long long) +12218 18 203 54 +12230 2f 205 54 +1225f c 206 54 +1226b e 208 54 +12279 6 209 54 +1227f 14 210 54 +12293 9 212 54 +1229c a 213 54 +122a6 f 214 54 +122b5 15 215 54 +122ca 2b 216 54 +122f5 a 217 54 +122ff a 218 54 +12309 11 219 54 +1231a a 220 54 +12324 2 221 54 +FUNC 12326 143 0 MacFileUtilities::MachoWalker::WalkHeaderAtOffset(long long) +12326 18 177 54 +1233e 2f 179 54 +1236d c 180 54 +12379 e 182 54 +12387 6 183 54 +1238d 14 184 54 +123a1 2e 189 54 +123cf 7 190 54 +123d6 9 192 54 +123df a 193 54 +123e9 f 194 54 +123f8 15 195 54 +1240d 2b 196 54 +12438 a 197 54 +12442 a 198 54 +1244c 11 199 54 +1245d a 200 54 +12467 2 201 54 +12469 1 201 54 +FUNC 1246a 99 0 MacFileUtilities::MachoWalker::WalkHeader(int) +1246a c 83 54 +12476 15 84 54 +1248b 1d 86 54 +124a8 d 87 54 +124b5 21 88 54 +124d6 21 90 54 +124f7 a 93 54 +12501 2 94 54 +12503 1 94 54 +FUNC 12504 1c 0 _OSSwapInt32 +12504 f 53 55 +12513 8 55 55 +1251b 3 56 55 +1251e 2 57 55 +FUNC 12520 2b 0 _OSSwapInt64 +12520 12 64 55 +12532 11 69 55 +12543 6 70 55 +12549 2 71 55 +1254b 1 71 55 +FUNC 1254c 19 0 NXSwapLong +1254c f 61 56 +1255b 8 63 56 +12563 2 64 56 +12565 1 64 56 +FUNC 12566 1f 0 NXSwapLongLong +12566 12 70 56 +12578 b 72 56 +12583 2 73 56 +12585 1 73 56 +FUNC 12586 32 0 breakpad_swap_uuid_command(breakpad_uuid_command*, NXByteOrder) +12586 c 37 57 +12592 11 39 57 +125a3 13 40 57 +125b6 2 41 57 +FUNC 125b8 da 0 breakpad_swap_segment_command_64(segment_command_64*, NXByteOrder) +125b8 c 44 57 +125c4 11 46 57 +125d5 13 47 57 +125e8 17 49 57 +125ff 17 50 57 +12616 17 51 57 +1262d 17 52 57 +12644 13 54 57 +12657 13 55 57 +1266a 13 56 57 +1267d 13 57 57 +12690 2 58 57 +FUNC 12692 a4 0 breakpad_swap_mach_header_64(mach_header_64*, NXByteOrder) +12692 c 61 57 +1269e 11 63 57 +126af 13 64 57 +126c2 13 65 57 +126d5 13 66 57 +126e8 13 67 57 +126fb 13 68 57 +1270e 13 69 57 +12721 13 70 57 +12734 2 71 57 +FUNC 12736 1d1 0 breakpad_swap_section_64(section_64*, unsigned int, NXByteOrder) +12736 d 75 57 +12743 c 77 57 +1274f 33 78 57 +12782 33 79 57 +127b5 2d 81 57 +127e2 2d 82 57 +1280f 2d 83 57 +1283c 2d 84 57 +12869 2d 85 57 +12896 2d 86 57 +128c3 2d 87 57 +128f0 11 77 57 +12901 6 89 57 +12907 1 89 57 +12908 12 9 58 +1291a 4f 11 58 +12969 2 12 58 +1296b 1 12 58 +1296c 12 9 58 +1297e 4f 11 58 +129cd 2 12 58 +129cf 1 12 58 +129d0 13 14 58 +129e3 2a 14 58 +12a0d 1 14 58 +12a0e 13 14 58 +12a21 2a 14 58 +12a4b 1 14 58 +12a4c 13 14 58 +12a5f 2a 14 58 +12a89 1 14 58 +FUNC 12a8a bb 0 dwarf2reader::ByteReader::SetOffsetSize(unsigned char) +12a8a 19 16 58 +12aa3 a 17 58 +12aad 48 18 58 +12af5 6 19 58 +12afb 23 20 58 +12b1e 21 22 58 +12b3f 6 24 58 +12b45 1 24 58 +FUNC 12b46 bb 0 dwarf2reader::ByteReader::SetAddressSize(unsigned char) +12b46 19 26 58 +12b5f a 27 58 +12b69 48 28 58 +12bb1 6 29 58 +12bb7 23 30 58 +12bda 21 32 58 +12bfb 6 34 58 +12c01 1 34 58 +FUNC 12c02 a2 0 dwarf2reader::ByteReader::ReadFourBytes(char const*) const +12c02 c 24 59 +12c0e c 25 64 +12c1a d 26 64 +12c27 f 27 64 +12c36 f 28 64 +12c45 b 29 64 +12c50 27 30 64 +12c77 2b 32 64 +12ca2 2 34 64 +FUNC 12ca4 40e 0 dwarf2reader::ByteReader::ReadEightBytes(char const*) const +12ca4 11 36 59 +12cb5 1a 37 64 +12ccf 1b 38 64 +12cea 1d 39 64 +12d07 1d 40 64 +12d24 1d 41 64 +12d41 1d 42 64 +12d5e 1d 43 64 +12d7b 1d 44 64 +12d98 f 45 64 +12da7 18f 47 64 +12f36 172 50 64 +130a8 a 52 64 +130b2 2 52 64 +FUNC 130b4 a6 0 ReadInitialLength +130b4 15 29 60 +130c9 18 30 60 +130e1 6 31 60 +130e7 d 35 60 +130f4 13 36 60 +13107 9 37 60 +13110 1a 38 60 +1312a 13 40 60 +1313d 9 41 60 +13146 12 43 60 +13158 2 44 60 +1315a 1f 47 60 +13179 65 50 60 +131de 1f 47 60 +131fd 65 50 60 +FUNC 13262 393 0 dwarf2reader::CompilationUnit::SkipAttribute(char const*, dwarf2reader::DwarfForm) +13262 14 133 60 +13276 82 136 60 +132f8 1f 139 60 +13317 a 140 60 +13321 21 141 60 +13342 c 147 60 +1334e e 151 60 +1335c e 155 60 +1336a e 159 60 +13378 27 162 60 +1339f 1c 166 60 +133bb 10 167 60 +133cb 1c 171 60 +133e7 10 172 60 +133f7 1e 175 60 +13415 56 180 60 +1346b d 181 60 +13478 1e 182 60 +13496 11 183 60 +134a7 1e 184 60 +134c5 24 189 60 +134e9 26 192 60 +1350f 23 195 60 +13532 22 198 60 +13554 15 199 60 +13569 1b 203 60 +13584 30 206 60 +135b4 30 208 60 +135e4 a 209 60 +135ee 7 210 60 +135f5 1 210 60 +FUNC 135f6 29b 0 dwarf2reader::CompilationUnit::ReadHeader() +135f6 14 217 60 +1360a 9 218 60 +13613 4e 221 60 +13661 17 223 60 +13678 a 224 60 +13682 f 225 60 +13691 4e 227 60 +136df 1e 228 60 +136fd 6 229 60 +13703 5e 231 60 +13761 1e 232 60 +1377f 18 233 60 +13797 4c 235 60 +137e3 1d 236 60 +13800 1c 237 60 +1381c 5 238 60 +13821 9 240 60 +1382a 60 245 60 +1388a 7 247 60 +13891 1 247 60 +FUNC 13892 a57 0 dwarf2reader::CompilationUnit::ProcessAttribute(unsigned long long, char const*, dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm) +13892 24 299 60 +138b6 8a 302 60 +13940 1f 307 60 +1395f a 308 60 +13969 36 309 60 +1399f 5b 316 60 +139fa c 317 60 +13a06 5b 322 60 +13a61 e 323 60 +13a6f 55 328 60 +13ac4 e 329 60 +13ad2 55 334 60 +13b27 e 335 60 +13b35 6 338 60 +13b3b 9a 340 60 +13bd5 33 341 60 +13c08 25 340 60 +13c2d 5c 348 60 +13c89 10 349 60 +13c99 5c 354 60 +13cf5 10 355 60 +13d05 55 359 60 +13d5a 1e 360 60 +13d78 56 365 60 +13dce d 366 60 +13ddb 55 368 60 +13e30 1e 369 60 +13e4e 11 370 60 +13e5f 55 372 60 +13eb4 1e 373 60 +13ed2 29 378 60 +13efb 4a 380 60 +13f45 f 381 60 +13f54 29 385 60 +13f7d 4c 387 60 +13fc9 11 388 60 +13fda 1b 392 60 +13ff5 4c 394 60 +14041 11 395 60 +14052 22 399 60 +14074 4e 401 60 +140c2 15 402 60 +140d7 3c 406 60 +14113 1b 408 60 +1412e 54 409 60 +14182 f 411 60 +14191 9a 413 60 +1422b 24 414 60 +1424f 25 413 60 +14274 30 418 60 +142a4 30 420 60 +142d4 a 421 60 +142de b 422 60 +142e9 1 422 60 +142ea 1f 489 60 +14309 3a 491 60 +14343 a 492 60 +1434d 6 493 60 +14353 1 493 60 +14354 1f 489 60 +14373 3a 491 60 +143ad a 492 60 +143b7 6 493 60 +143bd 1 493 60 +FUNC 143be b5 0 dwarf2reader::CompilationUnit::ProcessDIE(unsigned long long, char const*, dwarf2reader::CompilationUnit::Abbrev const&) +143be 19 426 60 +143d7 13 427 60 +143ea 46 430 60 +14430 3a 427 60 +1446a 3 432 60 +1446d 6 433 60 +14473 1 433 60 +FUNC 14474 85 0 dwarf2reader::CompilationUnit::SkipDIE(char const*, dwarf2reader::CompilationUnit::Abbrev const&) +14474 c 122 60 +14480 13 123 60 +14493 27 126 60 +144ba 3a 123 60 +144f4 3 128 60 +144f7 2 129 60 +144f9 1 129 60 +FUNC 144fa be4 0 dwarf2reader::LineInfo::ProcessOneOpcode(dwarf2reader::ByteReader*, dwarf2reader::LineInfoHandler*, dwarf2reader::LineInfoHeader const&, char const*, dwarf2reader::LineStateMachine*, unsigned long*, unsigned long, bool*) +144fa 18 593 60 +14512 a 594 60 +1451c 18 596 60 +14534 8 597 60 +1453c 5 598 60 +14541 19 602 60 +1455a f 603 60 +14569 50 605 60 +145b9 46 607 60 +145ff e0 610 60 +146df 6 612 60 +146e5 22 615 60 +14707 22 616 60 +14729 7 617 60 +14730 b 618 60 +1473b f 619 60 +1474a 5a 623 60 +147a4 7 625 60 +147ab b 626 60 +147b6 f 627 60 +147c5 28 631 60 +147ed e 632 60 +147fb 144 635 60 +1493f 6 637 60 +14945 9e 640 60 +149e3 5 642 60 +149e8 22 644 60 +14a0a e 645 60 +14a18 1c 646 60 +14a34 2b 652 60 +14a5f b 653 60 +14a6a 22 658 60 +14a8c e 659 60 +14a9a 8 660 60 +14aa2 5 662 60 +14aa7 22 664 60 +14ac9 e 665 60 +14ad7 9 666 60 +14ae0 5 668 60 +14ae5 12 670 60 +14af7 5 672 60 +14afc 7 674 60 +14b03 5 676 60 +14b08 16 678 60 +14b1e 9 679 60 +14b27 d9 682 60 +14c00 6 684 60 +14c06 1f 687 60 +14c25 5 689 60 +14c2a 40 693 60 +14c6a d6 696 60 +14d40 6 698 60 +14d46 1c 701 60 +14d62 5 703 60 +14d67 1f 706 60 +14d86 d 707 60 +14d93 13 708 60 +14da6 26 710 60 +14dcc 5 711 60 +14dd1 50 713 60 +14e21 7 715 60 +14e28 b 716 60 +14e33 f 717 60 +14e42 18 725 60 +14e5a f 726 60 +14e69 5 728 60 +14e6e 6 730 60 +14e74 28 732 60 +14e9c d 733 60 +14ea9 22 735 60 +14ecb e 736 60 +14ed9 22 739 60 +14efb e 740 60 +14f09 22 743 60 +14f2b e 744 60 +14f39 a 746 60 +14f43 fd 748 60 +15040 a 758 60 +1504a 9 759 60 +15053 1c 761 60 +1506f d 762 60 +1507c e 763 60 +1508a 2e 759 60 +150b8 b 769 60 +150c3 10 770 60 +150d3 b 771 60 +FUNC 150de 14b 0 dwarf2reader::LineInfo::ReadLines() +150de e 773 60 +150ec 9 778 60 +150f5 17 782 60 +1510c 8 783 60 +15114 6 785 60 +1511a 9 787 60 +15123 5 788 60 +15128 19 789 60 +15141 5 790 60 +15146 4a 793 60 +15190 6 794 60 +15196 4a 796 60 +151e0 a 797 60 +151ea f 790 60 +151f9 15 788 60 +1520e 14 801 60 +15222 7 802 60 +15229 1 802 60 +FUNC 1522a 4fd 0 dwarf2reader::CompilationUnit::ReadAbbrevs() +1522a 18 60 60 +15242 e 61 60 +15250 58 65 60 +152a8 38 66 60 +152e0 44 65 60 +15324 2a 66 60 +1534e 45 68 60 +15393 16 69 60 +153a9 1d 75 60 +153c6 6 76 60 +153cc 40 77 60 +1540c b 80 60 +15417 1f 82 60 +15436 e 84 60 +15444 6 77 60 +1544a 1f 68 60 +15469 a 84 60 +15473 1d 79 60 +15490 6 86 60 +15496 a 87 60 +154a0 3d 89 60 +154dd 1f 90 60 +154fc a 91 60 +15506 6 92 60 +1550c 3d 94 60 +15549 1d 95 60 +15566 5 96 60 +1556b 3d 98 60 +155a8 1f 101 60 +155c7 a 102 60 +155d1 3d 104 60 +1560e 1f 105 60 +1562d a 106 60 +15637 c 107 60 +15643 6 111 60 +15649 6 112 60 +1564f 32 113 60 +15681 47 115 60 +156c8 30 116 60 +156f8 24 79 60 +1571c b 118 60 +15727 1 118 60 +FUNC 15728 5dc 0 dwarf2reader::LineInfo::ReadHeader() +15728 18 503 60 +15740 9 504 60 +15749 17 508 60 +15760 a 510 60 +1576a f 511 60 +15779 60 512 60 +157d9 44 516 60 +1581d 1e 518 60 +1583b 6 519 60 +15841 1e 521 60 +1585f 18 522 60 +15877 1d 524 60 +15894 5 525 60 +15899 20 527 60 +158b9 5 528 60 +158be c 530 60 +158ca 5 531 60 +158cf 1d 533 60 +158ec 5 534 60 +158f1 1d 536 60 +1590e 5 537 60 +15913 45 539 60 +15958 1f 540 60 +15977 19 541 60 +15990 15 542 60 +159a5 1f 539 60 +159c4 30 543 60 +159f4 5 544 60 +159f9 14 542 60 +15a0d e 548 60 +15a1b 7 549 60 +15a22 5 550 60 +15a27 6 551 60 +15a2d 8b 552 60 +15ab8 28 553 60 +15ae0 5 554 60 +15ae5 16 550 60 +15afb 25 552 60 +15b20 5 557 60 +15b25 e 560 60 +15b33 7 561 60 +15b3a 5 563 60 +15b3f 6 564 60 +15b45 28 565 60 +15b6d 22 567 60 +15b8f a 568 60 +15b99 22 570 60 +15bbb a 571 60 +15bc5 22 573 60 +15be7 a 574 60 +15bf1 ba 576 60 +15cab 5 577 60 +15cb0 16 563 60 +15cc6 25 576 60 +15ceb 5 580 60 +15cf0 9 582 60 +15cf9 b 583 60 +FUNC 15d04 3d 0 dwarf2reader::LineInfo::Start() +15d04 c 495 60 +15d10 b 496 60 +15d1b b 497 60 +15d26 19 498 60 +15d3f 2 499 60 +15d41 1 499 60 +FUNC 15d42 304 0 dwarf2reader::CompilationUnit::ProcessDIEs() +15d42 11 435 60 +15d53 9 436 60 +15d5c 9 441 60 +15d65 17 445 60 +15d7c 8 446 60 +15d84 6 448 60 +15d8a 6c 453 60 +15df6 8 455 60 +15dfe 16 453 60 +15e14 3 455 60 +15e17 2f 453 60 +15e46 29 458 60 +15e6f 22 460 60 +15e91 a 462 60 +15e9b a 465 60 +15ea5 1e 466 60 +15ec3 13 467 60 +15ed6 2b 468 60 +15f01 18 472 60 +15f19 9 473 60 +15f22 42 474 60 +15f64 1e 475 60 +15f82 2a 477 60 +15fac b 480 60 +15fb7 1e 481 60 +15fd5 26 483 60 +15ffb 1d 455 60 +16018 24 485 60 +1603c a 486 60 +FUNC 16046 35f 0 dwarf2reader::CompilationUnit::Start() +16046 18 249 60 +1605e 58 251 60 +160b6 35 252 60 +160eb 32 251 60 +1611d 2a 252 60 +16147 20 255 60 +16167 37 256 60 +1619e b 259 60 +161a9 f 264 60 +161b8 17 265 60 +161cf c 266 60 +161db a 268 60 +161e5 95 271 60 +1627a 11 276 60 +1628b b 279 60 +16296 58 282 60 +162ee 2f 283 60 +1631d 32 282 60 +1634f 14 284 60 +16363 1a 285 60 +1637d b 289 60 +16388 12 291 60 +1639a b 292 60 +163a5 1 292 60 +FUNC 163a6 3a 0 std::fill(unsigned char*, unsigned char*, unsigned char const&) +163a6 c 573 61 +163b2 9 576 61 +163bb 23 577 61 +163de 2 578 61 +FUNC 163e0 33 0 std::__deque_buf_size(unsigned long) +163e0 c 83 62 +163ec 27 84 62 +16413 1 84 62 +FUNC 16414 18 0 dwarf2reader::ByteReader::OffsetSize() const +16414 c 38 63 +16420 c 38 63 +FUNC 1642c 18 0 dwarf2reader::ByteReader::AddressSize() const +1642c c 41 63 +16438 c 41 63 +FUNC 16444 17 0 dwarf2reader::ByteReader::ReadOneByte(char const*) const +16444 c 10 64 +16450 9 11 64 +16459 2 12 64 +1645b 1 12 64 +FUNC 1645c 63 0 dwarf2reader::ByteReader::ReadTwoBytes(char const*) const +1645c c 14 64 +16468 d 15 64 +16475 e 16 64 +16483 b 17 64 +1648e 17 18 64 +164a5 18 20 64 +164bd 2 22 64 +164bf 1 22 64 +FUNC 164c0 98 0 dwarf2reader::ByteReader::ReadUnsignedLEB128(char const*, unsigned long*) const +164c0 e 59 64 +164ce e 60 64 +164dc 7 61 64 +164e3 7 62 64 +164ea e 66 64 +164f8 5 67 64 +164fd 38 69 64 +16535 6 71 64 +1653b 8 65 64 +16543 8 75 64 +1654b 6 77 64 +16551 7 78 64 +FUNC 16558 ee 0 dwarf2reader::ByteReader::ReadSignedLEB128(char const*, unsigned long*) const +16558 e 84 64 +16566 e 85 64 +16574 7 86 64 +1657b 7 87 64 +16582 e 91 64 +16590 5 92 64 +16595 44 93 64 +165d9 6 94 64 +165df 8 90 64 +165e7 14 97 64 +165fb 36 98 64 +16631 8 99 64 +16639 6 100 64 +1663f 7 101 64 +FUNC 16646 a2 0 dwarf2reader::ByteReader::ReadOffset(char const*) const +16646 13 103 64 +16659 3f 104 64 +16698 4a 105 64 +166e2 6 106 64 +FUNC 166e8 a2 0 dwarf2reader::ByteReader::ReadAddress(char const*) const +166e8 13 108 64 +166fb 3f 109 64 +1673a 4a 110 64 +16784 6 111 64 +FUNC 1678a 61 0 dwarf2reader::LineStateMachine::Reset(bool) +1678a 12 12 65 +1679c 9 13 65 +167a5 11 14 65 +167b6 11 15 65 +167c7 a 16 65 +167d1 a 17 65 +167db 7 18 65 +167e2 7 19 65 +167e9 2 20 65 +167eb 1 20 65 +FUNC 167ec 20 0 std::_List_const_iterator >::operator!=(std::_List_const_iterator > const&) const +167ec c 253 66 +167f8 14 254 66 +FUNC 1680c 25 0 std::_List_const_iterator >::operator++(int) +1680c c 226 66 +16818 8 228 66 +16820 c 229 66 +1682c 5 230 66 +16831 1 230 66 +FUNC 16832 16 0 std::_List_const_iterator >::operator->() const +16832 c 215 66 +1683e a 216 66 +16848 c 190 67 +16854 a 190 67 +FUNC 1685e 13 0 std::auto_ptr > > >::operator->() const +1685e c 283 67 +1686a 7 286 67 +16871 1 286 67 +16872 c 65 68 +1687e 2 65 68 +16880 c 97 69 +1688c d 97 69 +16899 1 97 69 +1689a c 99 69 +168a6 14 100 69 +168ba c 97 69 +168c6 d 97 69 +168d3 1 97 69 +168d4 c 84 70 +168e0 17 85 70 +168f7 1 85 70 +FUNC 168f8 2d 0 std::pair std::make_pair(dwarf2reader::DwarfAttribute, dwarf2reader::DwarfForm) +168f8 c 144 70 +16904 21 145 70 +16925 1 145 70 +16926 c 202 66 +16932 a 203 66 +FUNC 1693c 25 0 std::list, std::allocator > >::begin() const +1693c c 588 70 +16948 19 589 66 +16961 1 589 66 +FUNC 16962 23 0 std::list, std::allocator > >::end() const +16962 c 605 70 +1696e 17 606 66 +16985 1 606 66 +16986 c 65 68 +16992 2 65 68 +16994 c 72 68 +169a0 2 72 68 +169a2 c 97 69 +169ae d 97 69 +169bb 1 97 69 +169bc c 105 69 +169c8 d 105 69 +169d5 1 105 69 +169d6 c 105 69 +169e2 d 105 69 +169ef 1 105 69 +169f0 c 67 68 +169fc 2 67 68 +169fe c 99 69 +16a0a 14 100 69 +16a1e c 99 69 +16a2a 14 100 69 +16a3e c 129 62 +16a4a 30 131 62 +16a7a c 65 68 +16a86 2 65 68 +16a88 c 72 68 +16a94 2 72 68 +16a96 c 97 69 +16aa2 d 97 69 +16aaf 1 97 69 +16ab0 c 105 69 +16abc d 105 69 +16ac9 1 105 69 +16aca c 105 69 +16ad6 d 105 69 +16ae3 1 105 69 +16ae4 c 67 68 +16af0 2 67 68 +16af2 c 99 69 +16afe 14 100 69 +16b12 c 99 69 +16b1e 14 100 69 +FUNC 16b32 2b 0 std::_Vector_base >::get_allocator() const +16b32 10 93 71 +16b42 1b 94 71 +16b5d 1 94 71 +16b6a 7 614 72 +16b71 1 614 72 +16b72 c 80 71 +16b7e d 80 71 +16b8b 1 80 71 +16b98 2 107 68 +FUNC 16b9a 2d 0 void std::_Destroy >(unsigned char*, unsigned char*, std::allocator) +16b9a c 171 73 +16ba6 2 173 73 +16ba8 12 174 73 +16bba b 173 73 +16bc5 2 174 73 +16bc7 1 174 73 +16bc8 c 84 71 +16bd4 2f 85 71 +16c03 2 86 71 +16c05 1 86 71 +16c06 c 96 71 +16c12 12 97 71 +16c24 2 98 71 +FUNC 16c26 1f 0 std::_List_base, std::allocator > >::_M_init() +16c26 c 338 73 +16c32 8 340 66 +16c3a b 341 66 +16c45 1 341 66 +16c46 c 105 69 +16c52 d 105 69 +16c5f 1 105 69 +16c60 c 125 66 +16c6c a 126 66 +FUNC 16c76 25 0 std::list, std::allocator > >::begin() +16c76 c 579 73 +16c82 19 580 66 +16c9b 1 580 66 +FUNC 16c9c 23 0 std::list, std::allocator > >::end() +16c9c c 597 73 +16ca8 17 597 66 +16cbf 1 597 66 +16cc0 c 603 72 +16ccc c 603 72 +FUNC 16cd8 2b 0 std::vector >::begin() const +16cd8 c 342 73 +16ce4 1f 343 71 +16d03 1 343 71 +FUNC 16d04 2c 0 std::vector >::end() const +16d04 c 360 73 +16d10 20 361 71 +16d3c 5 666 72 +16d41 1 666 72 +16d4f 31 759 72 +FUNC 16d80 3c 0 std::vector >::size() const +16d80 c 402 73 +16d8c 30 403 71 +16dbc c 603 72 +16dc8 c 603 72 +FUNC 16dd4 23 0 std::vector >::begin() +16dd4 c 333 73 +16de0 17 334 71 +16df7 1 334 71 +16e04 33 654 72 +16e37 1 654 72 +FUNC 16e38 26 0 std::vector >::end() +16e38 c 351 73 +16e44 1a 352 71 +16e6a 7 614 72 +16e71 1 614 72 +FUNC 16e72 42 0 std::vector >::operator[](unsigned long) +16e72 c 494 73 +16e7e 36 495 71 +FUNC 16eb4 13 0 std::vector >::max_size() const +16eb4 c 407 73 +16ec0 7 408 71 +16ec7 1 408 71 +16ed4 5 666 72 +16ed9 1 666 72 +16ee6 d 623 72 +16ef3 5 624 72 +16ef8 c 382 62 +16f04 d 382 62 +16f11 1 382 62 +FUNC 16f12 2b 0 std::_Deque_base >::get_allocator() const +16f12 10 360 73 +16f22 1b 361 62 +16f3d 1 361 62 +FUNC 16f3e 2d 0 std::deque >::get_allocator() const +16f3e 10 764 73 +16f4e 1d 765 62 +16f6b 1 765 62 +FUNC 16f6c 13 0 std::_Deque_iterator::operator*() const +16f6c c 134 73 +16f78 7 135 62 +16f7f 1 135 62 +16f8c 2 107 68 +16f8e c 129 62 +16f9a 30 131 62 +FUNC 16fca 2c 0 std::deque >::end() const +16fca 10 799 73 +16fda 1c 800 62 +FUNC 16ff6 2c 0 std::deque >::begin() const +16ff6 10 781 73 +17006 1c 782 62 +FUNC 17022 2e 0 std::deque >::end() +17022 10 790 73 +17032 1e 791 62 +FUNC 17050 3c 0 std::vector >::_M_range_check(unsigned long) const +17050 13 515 73 +17063 15 517 71 +17078 14 518 71 +FUNC 1708c 32 0 std::vector >::at(unsigned long) +1708c c 534 73 +17098 12 536 71 +170aa 14 537 71 +170ca 2e 104 68 +170f8 c 84 71 +17104 2f 85 71 +17133 2 86 71 +17135 1 86 71 +17136 c 96 71 +17142 12 97 71 +17154 2 98 71 +17156 c 603 72 +17162 c 603 72 +FUNC 1716e 23 0 std::vector >::begin() +1716e c 333 73 +1717a 17 334 71 +17191 1 334 71 +1719e 27 654 72 +171c5 1 654 72 +FUNC 171c6 42 0 std::vector >::operator[](unsigned long) +171c6 c 494 73 +171d2 36 495 71 +FUNC 17208 26 0 std::vector >::end() +17208 c 351 73 +17214 1a 352 71 +1723a d 94 68 +17247 1 94 68 +FUNC 17248 2f 0 std::_Vector_base >::_M_deallocate(unsigned char*, unsigned long) +17248 c 120 73 +17254 6 122 71 +1725a 1d 123 71 +17277 1 123 71 +17278 c 108 71 +17284 3a 109 71 +172be c 188 71 +172ca 12 189 71 +172dc 2 190 71 +172de c 272 71 +172ea 4b 273 71 +17335 1 273 71 +17336 13 62 74 +17349 10 62 74 +17359 a 63 74 +17363 25 64 74 +17388 1a 66 74 +173a2 13 62 74 +173b5 10 62 74 +173c5 a 63 74 +173cf 25 64 74 +173f4 1a 66 74 +1740e c 188 71 +1741a 12 189 71 +1742c 2 190 71 +1743b 31 759 72 +1746c c 65 68 +17478 2 65 68 +1747a c 103 69 +17486 d 103 69 +17493 1 103 69 +FUNC 17494 2d 0 std::list, std::allocator > >::get_allocator() const +17494 10 570 74 +174a4 1d 571 66 +174c1 1 571 66 +174ce 2e 104 68 +FUNC 174fc 20 0 std::_List_iterator >::operator!=(std::_List_iterator > const&) const +174fc c 172 74 +17508 14 173 66 +FUNC 1751c 1d 0 std::_List_const_iterator >::operator++() +1751c c 219 74 +17528 c 221 66 +17534 5 222 66 +17539 1 222 66 +FUNC 1753a 1d 0 std::_List_iterator >::operator++() +1753a c 138 74 +17546 c 140 66 +17552 5 141 66 +17557 1 141 66 +FUNC 17558 16 0 std::_List_const_iterator >::operator*() const +17558 c 211 74 +17564 a 212 66 +FUNC 1756e 16 0 std::_List_iterator >::operator*() const +1756e c 130 74 +1757a a 131 66 +FUNC 17584 20 0 std::_List_const_iterator >::operator==(std::_List_const_iterator > const&) const +17584 c 249 74 +17590 14 250 66 +FUNC 175a4 35 0 bool __gnu_cxx::operator!= > >(__gnu_cxx::__normal_iterator > > const&, __gnu_cxx::__normal_iterator > > const&) +175a4 d 699 74 +175b1 28 700 72 +175d9 1 700 72 +FUNC 175da 4b 0 void std::_Destroy<__gnu_cxx::__normal_iterator > >, std::allocator >(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, std::allocator) +175da c 171 74 +175e6 2 173 73 +175e8 1a 174 73 +17602 21 173 73 +17623 2 174 73 +17625 1 174 73 +17626 c 127 62 +17632 29 127 62 +1765b 1 127 62 +1765c c 388 62 +17668 41 389 62 +176a9 2 390 62 +176ab 1 390 62 +176b8 d 94 68 +176c5 1 94 68 +FUNC 176c6 20 0 bool std::operator==(std::_Deque_iterator const&, std::_Deque_iterator const&) +176c6 c 243 74 +176d2 14 244 62 +FUNC 176e6 26 0 bool std::operator!=(std::_Deque_iterator const&, std::_Deque_iterator const&) +176e6 c 256 74 +176f2 1a 257 62 +FUNC 1770c 1a 0 std::_Deque_iterator::_S_buffer_size() +1770c c 106 74 +17718 e 107 62 +FUNC 17726 3e 0 std::_Deque_iterator::_M_set_node(unsigned long long**) +17726 d 229 74 +17733 9 231 62 +1773c b 232 62 +17747 1d 233 62 +FUNC 17764 50 0 std::_Deque_iterator::operator++() +17764 c 142 74 +17770 d 144 62 +1777d f 145 62 +1778c 18 147 62 +177a4 b 148 62 +177af 5 150 62 +FUNC 177b4 4b 0 void std::_Destroy, std::allocator >(std::_Deque_iterator, std::_Deque_iterator, std::allocator) +177b4 c 171 74 +177c0 2 173 73 +177c2 1a 174 73 +177dc 21 173 73 +177fd 2 174 73 +177ff 1 174 73 +FUNC 17800 50 0 std::_Deque_iterator::operator--() +17800 c 162 74 +1780c f 164 62 +1781b 18 166 62 +17833 b 167 62 +1783e d 169 62 +1784b 5 170 62 +FUNC 17850 39 0 std::deque >::back() +17850 c 988 74 +1785c 15 990 62 +17871 b 991 62 +1787c d 992 62 +17889 1 992 62 +FUNC 1788a 19 0 std::stack > >::top() +1788a c 163 75 +17896 d 166 75 +178a3 1 166 75 +FUNC 178a4 66 0 std::_Deque_iterator::difference_type std::operator-(std::_Deque_iterator const&, std::_Deque_iterator const&) +178a4 d 328 75 +178b1 59 333 62 +FUNC 1790a 26 0 std::deque >::size() const +1790a c 840 75 +17916 1a 841 62 +1793c 36 662 72 +1797e 23 650 72 +179a1 1 650 72 +179a2 c 67 68 +179ae 2 67 68 +179b0 c 99 69 +179bc 14 100 69 +179d0 c 303 66 +179dc 12 304 66 +179ee 2 305 66 +179f0 c 326 66 +179fc 2f 327 66 +17a2b d 328 66 +17a38 c 457 66 +17a44 14 458 66 +17a58 c 211 74 +17a64 2d 211 74 +17a91 1 211 74 +17a9e 7 98 68 +17aa5 1 98 68 +17ab2 1d 85 68 +17acf 5 86 68 +17ad4 17 88 68 +17aeb 1 88 68 +FUNC 17aec 2a 0 std::_Vector_base >::_M_allocate(unsigned long) +17aec c 116 75 +17af8 1e 117 71 +17b22 d 94 68 +17b2f 1 94 68 +FUNC 17b30 34 0 std::_Deque_base >::_M_deallocate_node(unsigned long long*) +17b30 c 402 75 +17b3c 28 403 62 +FUNC 17b64 38 0 std::_Deque_base >::_M_destroy_nodes(unsigned long long**, unsigned long long**) +17b64 c 504 75 +17b70 8 506 62 +17b78 14 507 62 +17b8c e 506 62 +17b9a 2 507 62 +FUNC 17b9c 62 0 std::deque >::_M_pop_back_aux() +17b9c c 391 76 +17ba8 15 393 76 +17bbd 1b 394 76 +17bd8 f 395 76 +17be7 17 396 76 +FUNC 17bfe 4f 0 std::deque >::pop_back() +17bfe c 1081 76 +17c0a 10 1083 62 +17c1a f 1086 62 +17c29 17 1087 62 +17c40 d 1090 62 +17c4d 1 1090 62 +FUNC 17c4e 19 0 std::stack > >::pop() +17c4e c 205 76 +17c5a d 208 75 +17c67 1 208 75 +17c68 c 72 68 +17c74 2 72 68 +17c76 c 105 69 +17c82 d 105 69 +17c8f 1 105 69 +17c90 c 603 72 +17c9c c 603 72 +FUNC 17ca8 2b 0 std::vector >::begin() const +17ca8 c 342 76 +17cb4 1f 343 71 +17cd3 1 343 71 +FUNC 17cd4 2c 0 std::vector >::end() const +17cd4 c 360 76 +17ce0 20 361 71 +17d0c 5 666 72 +17d11 1 666 72 +17d1f 28 759 72 +17d47 1 759 72 +FUNC 17d48 3c 0 std::vector >::size() const +17d48 c 402 76 +17d54 30 403 71 +17d90 d 623 72 +17d9d 5 624 72 +17dae 5 666 72 +17db3 1 666 72 +FUNC 17db4 35 0 bool __gnu_cxx::operator!= > >(__gnu_cxx::__normal_iterator > > const&, __gnu_cxx::__normal_iterator > > const&) +17db4 d 699 76 +17dc1 28 700 72 +17de9 1 700 72 +FUNC 17dea 4b 0 void std::_Destroy<__gnu_cxx::__normal_iterator > >, std::allocator >(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, std::allocator) +17dea c 171 76 +17df6 2 173 73 +17df8 1a 174 73 +17e12 21 173 73 +17e33 2 174 73 +17e35 1 174 73 +17e43 28 759 72 +17e6b 1 759 72 +17e78 2a 662 72 +FUNC 17ea2 13 0 std::vector >::max_size() const +17ea2 c 407 76 +17eae 7 408 71 +17eb5 1 408 71 +17ec2 16 650 72 +17ee4 7 98 68 +17eeb 1 98 68 +17ef8 1d 85 68 +17f15 5 86 68 +17f1a 10 88 68 +FUNC 17f2a 29 0 std::_List_base, std::allocator > >::_M_get_node() +17f2a c 311 76 +17f36 1d 312 66 +17f53 1 312 66 +FUNC 17f54 5f 0 std::list, std::allocator > >::_M_create_node(std::pair const&) +17f54 d 435 76 +17f61 e 437 66 +17f6f 3c 440 66 +17fab 8 447 66 +17fb3 1 447 66 +FUNC 17fb4 35 0 std::list, std::allocator > >::_M_insert(std::_List_iterator >, std::pair const&) +17fb4 c 1149 76 +17fc0 15 1151 66 +17fd5 14 1152 66 +17fe9 1 1152 66 +FUNC 17fea 52 0 void std::list, std::allocator > >::_M_insert_dispatch > >(std::_List_iterator >, std::_List_const_iterator >, std::_List_const_iterator >, __false_type) +17fea c 1126 66 +17ff6 2 1128 66 +17ff8 21 1129 66 +18019 21 1128 66 +1803a 2 1129 66 +FUNC 1803c 36 0 void std::list, std::allocator > >::insert > >(std::_List_iterator >, std::_List_const_iterator >, std::_List_const_iterator >) +1803c c 838 66 +18048 2a 842 66 +18072 e 491 66 +18080 32 492 66 +180b2 64 493 66 +18116 c 211 74 +18122 3d 211 74 +1815f 1 211 74 +1816d 5c 104 68 +181c9 1 104 68 +FUNC 181ca 31 0 std::list, std::allocator > >::push_back(std::pair const&) +181ca c 772 76 +181d6 25 773 66 +181fb 1 773 66 +FUNC 181fc 69 0 void std::_Construct(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev const&) +181fc d 77 76 +18209 5c 81 73 +18265 1 81 73 +18272 7 98 68 +18279 1 98 68 +18286 1d 85 68 +182a3 5 86 68 +182a8 10 88 68 +182b8 c 65 68 +182c4 2 65 68 +182c6 c 103 69 +182d2 d 103 69 +182df 1 103 69 +FUNC 182e0 4d 0 std::_Deque_base >::_M_get_map_allocator() const +182e0 11 394 76 +182f1 3c 395 62 +1832d 1 395 62 +FUNC 1832e 75 0 std::_Deque_base >::_M_allocate_map(unsigned long) +1832e d 406 76 +1833b 68 407 62 +183a3 1 407 62 +FUNC 183a4 47 0 std::_Deque_base >::_M_deallocate_map(unsigned long long**, unsigned long) +183a4 c 410 76 +183b0 3b 411 62 +183eb 1 411 62 +183ec c 424 62 +183f8 9 426 62 +18401 22 428 62 +18423 2b 430 62 +1844e c 714 62 +1845a 70 715 62 +184ca c 111 75 +184d6 d 111 75 +184e3 1 111 75 +184e4 c 259 67 +184f0 26 259 67 +18522 7 98 68 +18529 1 98 68 +18536 1d 85 68 +18553 5 86 68 +18558 10 88 68 +FUNC 18568 33 0 std::_Deque_base >::_M_allocate_node() +18568 c 398 76 +18574 27 399 62 +1859b 1 399 62 +FUNC 1859c 82 0 std::_Deque_base >::_M_create_nodes(unsigned long long**, unsigned long long**) +1859c d 486 76 +185a9 8 491 62 +185b1 12 492 62 +185c3 13 491 62 +185d6 b 494 62 +185e1 19 496 62 +185fa b 497 62 +18605 13 494 62 +18618 6 497 62 +FUNC 1861e 17b 0 std::_Deque_base >::_M_initialize_map(unsigned long) +1861e d 447 76 +1862b 1e 450 62 +18649 2a 452 62 +18673 1c 454 62 +1868f 19 462 62 +186a8 c 463 62 +186b4 1e 466 62 +186d2 b 467 62 +186dd 1e 469 62 +186fb 9 470 62 +18704 a 471 62 +1870e b 472 62 +18719 13 467 62 +1872c 15 475 62 +18741 18 476 62 +18759 c 477 62 +18765 34 478 62 +18799 1 478 62 +1879a d 366 62 +187a7 12 367 62 +187b9 39 368 62 +187f2 c 645 62 +187fe 1c 646 62 +FUNC 1881a 4d 0 void std::__fill::fill<__gnu_cxx::__normal_iterator > >, unsigned char>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, unsigned char const&) +1881a c 539 61 +18826 9 541 61 +1882f 2 542 61 +18831 13 543 61 +18844 21 542 61 +18865 2 543 61 +18867 1 543 61 +FUNC 18868 2b 0 void std::fill<__gnu_cxx::__normal_iterator > >, unsigned char>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, unsigned char const&) +18868 c 560 76 +18874 4 567 61 +18878 1b 568 61 +18893 1 568 61 +FUNC 18894 6a 0 std::list, std::allocator > >::_M_erase(std::_List_iterator >) +18894 d 1157 76 +188a1 b 1159 66 +188ac 6 1160 66 +188b2 35 1161 66 +188e7 17 1162 66 +FUNC 188fe 37 0 std::list, std::allocator > >::erase(std::_List_iterator >) +188fe c 95 77 +1890a 14 97 77 +1891e 12 98 77 +18930 5 99 77 +18935 1 99 77 +FUNC 18936 3e 0 std::list, std::allocator > >::erase(std::_List_iterator >, std::_List_iterator >) +18936 c 883 77 +18942 2 885 66 +18944 15 886 66 +18959 16 885 66 +1896f 5 887 66 +FUNC 18974 129 0 std::list, std::allocator > >::operator=(std::list, std::allocator > > const&) +18974 e 120 77 +18982 c 122 77 +1898e e 124 77 +1899c e 125 77 +189aa e 126 77 +189b8 e 127 77 +189c6 2 128 77 +189c8 20 130 77 +189e8 5a 128 77 +18a42 16 131 77 +18a58 1b 132 77 +18a73 20 134 77 +18a93 a 136 77 +18a9d 1 136 77 +FUNC 18a9e 4c 0 dwarf2reader::CompilationUnit::Abbrev::operator=(dwarf2reader::CompilationUnit::Abbrev const&) +18a9e c 211 77 +18aaa 40 211 74 +FUNC 18aea 52 0 dwarf2reader::CompilationUnit::Abbrev* std::__copy::copy(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*) +18aea c 280 61 +18af6 1a 283 61 +18b10 12 285 61 +18b22 4 286 61 +18b26 6 287 61 +18b2c b 283 61 +18b37 5 289 61 +FUNC 18b3c 2b 0 dwarf2reader::CompilationUnit::Abbrev* std::__copy_aux(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*) +18b3c c 307 77 +18b48 4 315 61 +18b4c 1b 317 61 +18b67 1 317 61 +18b76 56 354 61 +18bd8 4 384 61 +18bdc 4 385 61 +18be0 1b 387 61 +18bfb 1 387 61 +FUNC 18bfc ac 0 std::vector >::erase(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >) +18bfc d 122 78 +18c09 26 124 78 +18c2f 43 125 78 +18c72 2e 126 78 +18ca0 8 127 78 +FUNC 18ca8 54 0 dwarf2reader::CompilationUnit::Abbrev* std::__copy_backward::copy_b(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*) +18ca8 c 408 61 +18cb4 1a 411 61 +18cce 1e 412 61 +18cec b 411 61 +18cf7 5 413 61 +FUNC 18cfc 2b 0 dwarf2reader::CompilationUnit::Abbrev* std::__copy_backward_aux(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*) +18cfc c 432 78 +18d08 4 440 61 +18d0c 1b 443 61 +18d27 1 443 61 +18d36 56 482 61 +18d98 4 514 61 +18d9c 4 515 61 +18da0 1b 517 61 +18dbb 1 517 61 +FUNC 18dbc 4d 0 void std::__fill::fill<__gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev const&) +18dbc c 526 61 +18dc8 2 528 61 +18dca 1c 529 61 +18de6 21 528 61 +18e07 2 529 61 +18e09 1 529 61 +FUNC 18e0a 2b 0 void std::fill<__gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev const&) +18e0a c 560 78 +18e16 4 567 61 +18e1a 1b 568 61 +18e35 1 568 61 +FUNC 18e36 3f 0 unsigned char* std::__copy::copy(unsigned char const*, unsigned char const*, unsigned char*) +18e36 c 298 61 +18e42 22 300 61 +18e64 11 301 61 +18e75 1 301 61 +FUNC 18e76 2b 0 unsigned char* std::__copy_aux(unsigned char*, unsigned char*, unsigned char*) +18e76 c 307 78 +18e82 4 315 61 +18e86 1b 317 61 +18ea1 1 317 61 +18eb0 56 354 61 +18f12 4 384 61 +18f16 4 385 61 +18f1a 1b 387 61 +18f35 1 387 61 +FUNC 18f36 a0 0 std::vector >::erase(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >) +18f36 d 122 78 +18f43 26 124 78 +18f69 43 125 78 +18fac 22 126 78 +18fce 8 127 78 +18fe2 7 98 68 +18fe9 1 98 68 +18ff6 1d 85 68 +19013 5 86 68 +19018 d 88 68 +19025 1 88 68 +FUNC 19026 2a 0 std::_Vector_base >::_M_allocate(unsigned long) +19026 c 116 78 +19032 1e 117 71 +1905c 1b 74 79 +19077 1 74 79 +19084 23 113 79 +190a7 1 113 79 +190b4 1b 254 79 +190cf 1 254 79 +FUNC 190d0 19 0 void std::_Destroy(dwarf2reader::CompilationUnit::Abbrev*) +190d0 c 106 79 +190dc d 107 73 +190e9 1 107 73 +FUNC 190ea 44 0 void std::__destroy_aux<__gnu_cxx::__normal_iterator > > >(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, __false_type) +190ea c 119 79 +190f6 2 121 73 +190f8 13 122 73 +1910b 21 121 73 +1912c 2 122 73 +FUNC 1912e 28 0 void std::_Destroy<__gnu_cxx::__normal_iterator > > >(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >) +1912e c 148 79 +1913a 1c 155 73 +FUNC 19156 8d 0 void std::__uninitialized_fill_n_aux<__gnu_cxx::__normal_iterator > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev>(__gnu_cxx::__normal_iterator > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&, __false_type) +19156 d 188 79 +19163 6 190 79 +19169 2 193 79 +1916b 1c 194 79 +19187 1b 193 79 +191a2 b 196 79 +191ad 12 198 79 +191bf b 199 79 +191ca 13 196 79 +191dd 6 199 79 +191e3 1 199 79 +FUNC 191e4 2f 0 void std::uninitialized_fill_n<__gnu_cxx::__normal_iterator > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev>(__gnu_cxx::__normal_iterator > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&) +191e4 c 214 79 +191f0 23 218 79 +19213 1 218 79 +FUNC 19214 27 0 void std::__uninitialized_fill_n_a<__gnu_cxx::__normal_iterator > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev, dwarf2reader::CompilationUnit::Abbrev>(__gnu_cxx::__normal_iterator > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&, std::allocator) +19214 c 308 79 +19220 1b 310 79 +1923b 1 310 79 +19249 6 82 79 +1924f 2 85 79 +19251 24 86 79 +19275 2c 85 79 +192a1 b 87 79 +192ac b 89 79 +192b7 12 91 79 +192c9 b 92 79 +192d4 13 89 79 +192e7 9 92 79 +192fc 23 113 79 +1931f 1 113 79 +1932c 1b 254 79 +19347 1 254 79 +FUNC 19348 409 0 std::vector >::_M_insert_aux(__gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev const&) +19348 14 249 79 +1935c 14 251 78 +19370 22 253 78 +19392 f 255 78 +193a1 12 256 78 +193b3 55 257 78 +19408 4b 260 78 +19453 e 264 78 +19461 15 265 78 +19476 e 266 78 +19484 1d 271 78 +194a1 8 272 78 +194a9 e 273 78 +194b7 27 275 78 +194de 6 276 78 +194e4 55 279 78 +19539 25 284 78 +1955e b 285 78 +19569 4f 286 78 +195b8 3 284 78 +195bb 13 279 78 +195ce e 286 78 +195dc 4d 298 78 +19629 36 299 78 +1965f 12 302 78 +19671 13 303 78 +19684 2e 304 78 +196b2 13 286 78 +196c5 b 292 78 +196d0 39 294 78 +19709 23 295 78 +1972c b 296 78 +19737 13 292 78 +1974a 7 304 78 +19751 1 304 78 +FUNC 19752 70 0 std::vector >::push_back(dwarf2reader::CompilationUnit::Abbrev const&) +19752 c 602 79 +1975e 10 604 71 +1976e 1e 606 71 +1978c 11 607 71 +1979d 25 610 71 +FUNC 197c2 50 0 unsigned char* std::__copy_backward::copy_b(unsigned char const*, unsigned char const*, unsigned char*) +197c2 d 422 61 +197cf f 424 61 +197de 24 425 61 +19802 10 426 61 +FUNC 19812 2b 0 unsigned char* std::__copy_backward_aux(unsigned char*, unsigned char*, unsigned char*) +19812 c 432 79 +1981e 4 440 61 +19822 1b 443 61 +1983d 1 443 61 +1984c 56 482 61 +198ae 4 514 61 +198b2 4 515 61 +198b6 1b 517 61 +198d1 1 517 61 +FUNC 198d2 32 0 unsigned char* std::fill_n(unsigned char*, unsigned long, unsigned char const&) +198d2 c 647 79 +198de 1e 649 61 +198fc 8 650 61 +FUNC 19904 27 0 void std::__uninitialized_fill_n_aux(unsigned char*, unsigned long, unsigned char const&, __true_type) +19904 c 182 79 +19910 1b 183 79 +1992b 1 183 79 +FUNC 1992c 2f 0 void std::uninitialized_fill_n(unsigned char*, unsigned long, unsigned char const&) +1992c c 214 79 +19938 23 218 79 +1995b 1 218 79 +FUNC 1995c 27 0 void std::__uninitialized_fill_n_a(unsigned char*, unsigned long, unsigned char const&, std::allocator) +1995c c 308 79 +19968 1b 310 79 +19983 1 310 79 +FUNC 19984 27 0 void std::__destroy_aux(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, __false_type) +19984 c 119 79 +19990 2 121 73 +19992 b 122 73 +1999d c 121 73 +199a9 2 122 73 +199ab 1 122 73 +FUNC 199ac 28 0 void std::_Destroy(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*) +199ac c 148 79 +199b8 1c 155 73 +FUNC 199d4 88 0 dwarf2reader::CompilationUnit::Abbrev* std::__uninitialized_copy_aux(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, __false_type) +199d4 d 80 79 +199e1 6 82 79 +199e7 2 85 79 +199e9 12 86 79 +199fb 12 85 79 +19a0d b 87 79 +19a18 b 89 79 +19a23 12 91 79 +19a35 b 92 79 +19a40 13 89 79 +19a53 9 92 79 +FUNC 19a5c 2f 0 dwarf2reader::CompilationUnit::Abbrev* std::uninitialized_copy(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*) +19a5c c 108 79 +19a68 23 113 79 +19a8b 1 113 79 +FUNC 19a8c 27 0 dwarf2reader::CompilationUnit::Abbrev* std::__uninitialized_copy_a(dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev*, std::allocator) +19a8c c 252 79 +19a98 1b 254 79 +19ab3 1 254 79 +FUNC 19ab4 7e 0 void std::__uninitialized_fill_n_aux(dwarf2reader::CompilationUnit::Abbrev*, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&, __false_type) +19ab4 d 188 79 +19ac1 6 190 79 +19ac7 2 193 79 +19ac9 12 194 79 +19adb 16 193 79 +19af1 b 196 79 +19afc 12 198 79 +19b0e b 199 79 +19b19 13 196 79 +19b2c 6 199 79 +FUNC 19b32 2f 0 void std::uninitialized_fill_n(dwarf2reader::CompilationUnit::Abbrev*, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&) +19b32 c 214 79 +19b3e 23 218 79 +19b61 1 218 79 +FUNC 19b62 27 0 void std::__uninitialized_fill_n_a(dwarf2reader::CompilationUnit::Abbrev*, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&, std::allocator) +19b62 c 308 79 +19b6e 1b 310 79 +19b89 1 310 79 +FUNC 19b8a a5 0 dwarf2reader::CompilationUnit::Abbrev* std::__uninitialized_copy_aux<__gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev*>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev*, __false_type) +19b8a d 80 79 +19b97 6 82 79 +19b9d 2 85 79 +19b9f 1a 86 79 +19bb9 27 85 79 +19be0 b 87 79 +19beb b 89 79 +19bf6 12 91 79 +19c08 b 92 79 +19c13 13 89 79 +19c26 9 92 79 +19c2f 1 92 79 +FUNC 19c30 2f 0 dwarf2reader::CompilationUnit::Abbrev* std::uninitialized_copy<__gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev*>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev*) +19c30 c 108 79 +19c3c 23 113 79 +19c5f 1 113 79 +FUNC 19c60 27 0 dwarf2reader::CompilationUnit::Abbrev* std::__uninitialized_copy_a<__gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev*, dwarf2reader::CompilationUnit::Abbrev>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, dwarf2reader::CompilationUnit::Abbrev*, std::allocator) +19c60 c 252 79 +19c6c 1b 254 79 +19c87 1 254 79 +FUNC 19c88 5f8 0 std::vector >::_M_fill_insert(__gnu_cxx::__normal_iterator > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&) +19c88 15 311 79 +19c9d b 313 78 +19ca8 2a 315 78 +19cd2 12 318 78 +19ce4 23 319 78 +19d07 15 320 78 +19d1c c 321 78 +19d28 5a 323 78 +19d82 1c 327 78 +19d9e 35 328 78 +19dd3 16 323 78 +19de9 30 330 78 +19e19 10 343 78 +19e29 48 334 78 +19e71 21 338 78 +19e92 3d 339 78 +19ecf 13 334 78 +19ee2 b 339 78 +19eed 1c 342 78 +19f09 1e 343 78 +19f27 13 339 78 +19f3a 24 343 78 +19f5e e 348 78 +19f6c 1e 349 78 +19f8a e 350 78 +19f98 1d 353 78 +19fb5 8 354 78 +19fbd e 355 78 +19fcb 27 357 78 +19ff2 6 358 78 +19ff8 4d 361 78 +1a045 40 365 78 +1a085 18 367 78 +1a09d 44 368 78 +1a0e1 3 365 78 +1a0e4 19 361 78 +1a0fd 13 365 78 +1a110 e 368 78 +1a11e 3e 379 78 +1a15c 36 381 78 +1a192 12 384 78 +1a1a4 13 385 78 +1a1b7 2e 386 78 +1a1e5 e 368 78 +1a1f3 b 372 78 +1a1fe 39 374 78 +1a237 23 376 78 +1a25a b 377 78 +1a265 13 372 78 +1a278 8 386 78 +FUNC 1a280 2e 0 std::vector >::insert(__gnu_cxx::__normal_iterator > >, unsigned long, dwarf2reader::CompilationUnit::Abbrev const&) +1a280 c 657 79 +1a28c 22 658 71 +FUNC 1a2ae ab 0 std::vector >::resize(unsigned long, dwarf2reader::CompilationUnit::Abbrev const&) +1a2ae d 422 79 +1a2bb 15 424 71 +1a2d0 48 425 71 +1a318 41 427 71 +1a359 1 427 71 +FUNC 1a35a 63 0 std::vector >::resize(unsigned long) +1a35a d 441 79 +1a367 56 442 71 +1a3bd 1 442 71 +FUNC 1a3be 13 0 std::_Deque_iterator::operator*() const +1a3be c 134 79 +1a3ca 7 135 62 +1a3d1 1 135 62 +FUNC 1a3d2 3f 0 unsigned long long** std::__copy::copy(unsigned long long* const*, unsigned long long* const*, unsigned long long**) +1a3d2 c 298 61 +1a3de 22 300 61 +1a400 11 301 61 +1a411 1 301 61 +FUNC 1a412 2b 0 unsigned long long** std::__copy_aux(unsigned long long**, unsigned long long**, unsigned long long**) +1a412 c 307 79 +1a41e 4 315 61 +1a422 1b 317 61 +1a43d 1 317 61 +FUNC 1a43e 27 0 unsigned long long** std::__copy_normal::copy_n(unsigned long long**, unsigned long long**, unsigned long long**) +1a43e c 325 61 +1a44a 1b 326 61 +1a465 1 326 61 +FUNC 1a466 2f 0 unsigned long long** std::copy(unsigned long long**, unsigned long long**, unsigned long long**) +1a466 c 376 79 +1a472 4 384 61 +1a476 4 385 61 +1a47a 1b 387 61 +1a495 1 387 61 +FUNC 1a496 60 0 unsigned long long** std::__copy_backward::copy_b(unsigned long long* const*, unsigned long long* const*, unsigned long long**) +1a496 d 422 61 +1a4a3 12 424 61 +1a4b5 2e 425 61 +1a4e3 13 426 61 +FUNC 1a4f6 2b 0 unsigned long long** std::__copy_backward_aux(unsigned long long**, unsigned long long**, unsigned long long**) +1a4f6 c 432 79 +1a502 4 440 61 +1a506 1b 443 61 +1a521 1 443 61 +FUNC 1a522 27 0 unsigned long long** std::__copy_backward_normal::copy_b_n(unsigned long long**, unsigned long long**, unsigned long long**) +1a522 c 451 61 +1a52e 1b 452 61 +1a549 1 452 61 +FUNC 1a54a 2f 0 unsigned long long** std::copy_backward(unsigned long long**, unsigned long long**, unsigned long long**) +1a54a c 504 79 +1a556 4 514 61 +1a55a 4 515 61 +1a55e 1b 517 61 +1a579 1 517 61 +FUNC 1a57a 1df 0 std::deque >::_M_reallocate_map(unsigned long, bool) +1a57a 13 723 79 +1a58d 1b 726 76 +1a5a8 9 727 76 +1a5b1 13 730 76 +1a5c4 39 732 76 +1a5fd b 735 76 +1a608 27 736 76 +1a62f 2f 740 76 +1a65e 26 748 76 +1a684 15 750 76 +1a699 36 751 76 +1a6cf 22 753 76 +1a6f1 1e 756 76 +1a70f 8 758 76 +1a717 9 759 76 +1a720 15 762 76 +1a735 24 763 76 +1a759 1 763 76 +FUNC 1a75a 59 0 std::deque >::_M_reserve_map_at_back(unsigned long) +1a75a e 1443 79 +1a768 2a 1445 62 +1a792 21 1447 62 +1a7b3 1 1447 62 +FUNC 1a7b4 8c 0 std::deque >::_M_push_back_aux(unsigned long long const&) +1a7b4 c 345 79 +1a7c0 e 347 76 +1a7ce 13 348 76 +1a7e1 18 349 76 +1a7f9 1e 352 76 +1a817 1b 353 76 +1a832 c 355 76 +1a83e 2 360 76 +FUNC 1a840 62 0 std::deque >::push_back(unsigned long long const&) +1a840 c 1039 79 +1a84c 13 1041 62 +1a85f 1e 1044 62 +1a87d 11 1045 62 +1a88e 14 1048 62 +FUNC 1a8a2 20 0 std::stack > >::push(unsigned long long const&) +1a8a2 c 190 79 +1a8ae 14 191 75 +FUNC 1a8c2 27 0 unsigned char* std::__copy_normal::copy_n(unsigned char*, unsigned char*, unsigned char*) +1a8c2 c 325 61 +1a8ce 1b 326 61 +1a8e9 1 326 61 +FUNC 1a8ea 2f 0 unsigned char* std::copy(unsigned char*, unsigned char*, unsigned char*) +1a8ea c 376 79 +1a8f6 4 384 61 +1a8fa 4 385 61 +1a8fe 1b 387 61 +1a919 1 387 61 +FUNC 1a91a 27 0 unsigned char* std::__uninitialized_copy_aux(unsigned char*, unsigned char*, unsigned char*, __true_type) +1a91a c 73 79 +1a926 1b 74 79 +1a941 1 74 79 +FUNC 1a942 2f 0 unsigned char* std::uninitialized_copy(unsigned char*, unsigned char*, unsigned char*) +1a942 c 108 79 +1a94e 23 113 79 +1a971 1 113 79 +FUNC 1a972 27 0 unsigned char* std::__uninitialized_copy_a(unsigned char*, unsigned char*, unsigned char*, std::allocator) +1a972 c 252 79 +1a97e 1b 254 79 +1a999 1 254 79 +FUNC 1a99a 40 0 unsigned char* std::__copy_normal::copy_n<__gnu_cxx::__normal_iterator > >, unsigned char*>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, unsigned char*) +1a99a d 334 61 +1a9a7 33 335 61 +FUNC 1a9da 2f 0 unsigned char* std::copy<__gnu_cxx::__normal_iterator > >, unsigned char*>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, unsigned char*) +1a9da c 376 79 +1a9e6 4 384 61 +1a9ea 4 385 61 +1a9ee 1b 387 61 +1aa09 1 387 61 +FUNC 1aa0a 27 0 unsigned char* std::__uninitialized_copy_aux<__gnu_cxx::__normal_iterator > >, unsigned char*>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, unsigned char*, __true_type) +1aa0a c 73 79 +1aa16 1b 74 79 +1aa31 1 74 79 +FUNC 1aa32 2f 0 unsigned char* std::uninitialized_copy<__gnu_cxx::__normal_iterator > >, unsigned char*>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, unsigned char*) +1aa32 c 108 79 +1aa3e 23 113 79 +1aa61 1 113 79 +FUNC 1aa62 27 0 unsigned char* std::__uninitialized_copy_a<__gnu_cxx::__normal_iterator > >, unsigned char*, unsigned char>(__gnu_cxx::__normal_iterator > >, __gnu_cxx::__normal_iterator > >, unsigned char*, std::allocator) +1aa62 c 252 79 +1aa6e 1b 254 79 +1aa89 1 254 79 +1aa96 9 616 61 +1aa9f 2 617 61 +1aaa1 13 618 61 +1aab4 16 617 61 +1aaca 5 619 61 +1aacf 1 619 61 +1aadc 4 641 61 +1aae0 1b 642 61 +1aafb 1 642 61 +FUNC 1aafc 27 0 void std::__uninitialized_fill_n_aux<__gnu_cxx::__normal_iterator > >, unsigned long, unsigned char>(__gnu_cxx::__normal_iterator > >, unsigned long, unsigned char const&, __true_type) +1aafc c 182 79 +1ab08 1b 183 79 +1ab23 1 183 79 +FUNC 1ab24 2f 0 void std::uninitialized_fill_n<__gnu_cxx::__normal_iterator > >, unsigned long, unsigned char>(__gnu_cxx::__normal_iterator > >, unsigned long, unsigned char const&) +1ab24 c 214 79 +1ab30 23 218 79 +1ab53 1 218 79 +FUNC 1ab54 27 0 void std::__uninitialized_fill_n_a<__gnu_cxx::__normal_iterator > >, unsigned long, unsigned char, unsigned char>(__gnu_cxx::__normal_iterator > >, unsigned long, unsigned char const&, std::allocator) +1ab54 c 308 79 +1ab60 1b 310 79 +1ab7b 1 310 79 +FUNC 1ab7c 45a 0 std::vector >::_M_fill_insert(__gnu_cxx::__normal_iterator > >, unsigned long, unsigned char const&) +1ab7c 14 311 79 +1ab90 b 313 78 +1ab9b 21 315 78 +1abbc 9 318 78 +1abc5 23 319 78 +1abe8 15 320 78 +1abfd c 321 78 +1ac09 4e 323 78 +1ac57 11 327 78 +1ac68 30 328 78 +1ac98 35 330 78 +1accd 48 334 78 +1ad15 14 338 78 +1ad29 43 339 78 +1ad6c 11 342 78 +1ad7d 1e 343 78 +1ad9b e 348 78 +1ada9 1e 349 78 +1adc7 e 350 78 +1add5 1d 353 78 +1adf2 8 354 78 +1adfa e 355 78 +1ae08 27 357 78 +1ae2f 6 358 78 +1ae35 4d 361 78 +1ae82 40 365 78 +1aec2 18 367 78 +1aeda 4d 368 78 +1af27 3e 379 78 +1af65 2d 381 78 +1af92 12 384 78 +1afa4 13 385 78 +1afb7 1f 386 78 +FUNC 1afd6 2e 0 std::vector >::insert(__gnu_cxx::__normal_iterator > >, unsigned long, unsigned char const&) +1afd6 c 657 79 +1afe2 22 658 71 +FUNC 1b004 ab 0 std::vector >::resize(unsigned long, unsigned char const&) +1b004 d 422 79 +1b011 15 424 71 +1b026 48 425 71 +1b06e 41 427 71 +1b0af 1 427 71 +FUNC 1b0b0 2b 0 std::vector >::resize(unsigned long) +1b0b0 c 441 79 +1b0bc 1f 442 71 +1b0db 1 442 71 +FUNC 1b0dc 1a 0 std::_Deque_iterator::_S_buffer_size() +1b0dc c 106 79 +1b0e8 e 107 62 +FUNC 1b0f6 66 0 std::_Deque_iterator::difference_type std::operator-(std::_Deque_iterator const&, std::_Deque_iterator const&) +1b0f6 d 328 79 +1b103 59 333 62 +FUNC 1b15c 3e 0 std::_Deque_iterator::_M_set_node(unsigned long long**) +1b15c d 229 79 +1b169 9 231 62 +1b172 b 232 62 +1b17d 1d 233 62 +FUNC 1b19a 50 0 std::_Deque_iterator::operator++() +1b19a c 142 79 +1b1a6 d 144 62 +1b1b3 f 145 62 +1b1c2 18 147 62 +1b1da b 148 62 +1b1e5 5 150 62 +FUNC 1b1ea 84 0 std::_Deque_iterator std::__copy::copy, std::_Deque_iterator >(std::_Deque_iterator, std::_Deque_iterator, std::_Deque_iterator) +1b1ea e 280 61 +1b1f8 17 283 61 +1b20f 20 285 61 +1b22f b 286 61 +1b23a b 287 61 +1b245 b 283 61 +1b250 1e 289 61 +FUNC 1b26e 7e 0 std::_Deque_iterator std::__copy_aux, std::_Deque_iterator >(std::_Deque_iterator, std::_Deque_iterator, std::_Deque_iterator) +1b26e 11 307 79 +1b27f 4 315 61 +1b283 69 317 61 +FUNC 1b2ec 7a 0 std::_Deque_iterator std::__copy_normal::copy_n, std::_Deque_iterator >(std::_Deque_iterator, std::_Deque_iterator, std::_Deque_iterator) +1b2ec 11 325 61 +1b2fd 69 326 61 +FUNC 1b366 82 0 std::_Deque_iterator std::copy, std::_Deque_iterator >(std::_Deque_iterator, std::_Deque_iterator, std::_Deque_iterator) +1b366 11 376 79 +1b377 4 384 61 +1b37b 4 385 61 +1b37f 69 387 61 +FUNC 1b3e8 7a 0 std::_Deque_iterator std::__uninitialized_copy_aux, std::_Deque_iterator >(std::_Deque_iterator, std::_Deque_iterator, std::_Deque_iterator, __true_type) +1b3e8 11 73 79 +1b3f9 69 74 79 +FUNC 1b462 82 0 std::_Deque_iterator std::uninitialized_copy, std::_Deque_iterator >(std::_Deque_iterator, std::_Deque_iterator, std::_Deque_iterator) +1b462 11 108 79 +1b473 71 113 79 +FUNC 1b4e4 7a 0 std::_Deque_iterator std::__uninitialized_copy_a, std::_Deque_iterator, unsigned long long>(std::_Deque_iterator, std::_Deque_iterator, std::_Deque_iterator, std::allocator) +1b4e4 11 252 79 +1b4f5 69 254 79 +1b55e 10 679 62 +1b56e 64 680 62 +1b5d2 e8 681 62 +1b6ba c 143 75 +1b6c6 14 144 75 +1b6da 6 144 75 +FUNC 1b6e0 4d 0 __eprintf +1b6e0 6 1826 80 +1b6e6 3 1832 80 +1b6e9 c 1826 80 +1b6f5 29 1832 80 +1b71e a 1837 80 +1b728 5 1838 80 +1b72d e8d3 1838 80 diff --git a/shared/sentry/external/breakpad/src/client/mac/handler/ucontext_compat.h b/shared/sentry/external/breakpad/src/client/mac/handler/ucontext_compat.h new file mode 100644 index 000000000..1e4b752e5 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/handler/ucontext_compat.h @@ -0,0 +1,47 @@ +// 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. + +#ifndef CLIENT_MAC_HANDLER_UCONTEXT_COMPAT_H_ +#define CLIENT_MAC_HANDLER_UCONTEXT_COMPAT_H_ + +#include + +// The purpose of this file is to work around the fact that ucontext_t's +// uc_mcontext member is an mcontext_t rather than an mcontext64_t on ARM64. +#if defined(__aarch64__) +// doesn't include the below file. +#include +typedef ucontext64_t breakpad_ucontext_t; +#define breakpad_uc_mcontext uc_mcontext64 +#else +typedef ucontext_t breakpad_ucontext_t; +#define breakpad_uc_mcontext uc_mcontext +#endif // defined(__aarch64__) + +#endif // CLIENT_MAC_HANDLER_UCONTEXT_COMPAT_H_ diff --git a/shared/sentry/external/breakpad/src/client/mac/sender/Breakpad.xib b/shared/sentry/external/breakpad/src/client/mac/sender/Breakpad.xib new file mode 100644 index 000000000..885e39e68 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/sender/Breakpad.xib @@ -0,0 +1,1140 @@ + + + + 1070 + 10F569 + 762 + 1038.29 + 461.00 + + YES + + YES + + + YES + + + + YES + + + + YES + + + YES + + + + YES + + Reporter + + + FirstResponder + + + NSApplication + + + 1 + 2 + {{72, 251}, {490, 489}} + 536871936 + + NSWindow + + {1.79769e+308, 1.79769e+308} + {72, 5} + + + 264 + + YES + + + 272 + + YES + + + 256 + + YES + + + 290 + {{17, 36}, {456, 70}} + + YES + + 67239424 + 272760832 + Providing your email address is optional and will allow us contact you in case we need more details. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed arcu urna, pulvinar sit amet, tincidunt ac, fermentum ut, ligula. Quisque mi. Duis lectus. Vestibulum velit. Morbi turpis. Nunc at diam consectetur turpis volutpat tristique. Donec quis diam. Suspendisse scelerisque. + + LucidaGrande + 11 + 3100 + + + + 6 + System + controlColor + + 3 + MC42NjY2NjY2NjY3AA + + + + 6 + System + controlTextColor + + 3 + MAA + + + + + + + 290 + {{87, 9}, {195, 19}} + + YES + + -1804468671 + 272761856 + + + optional + + YES + + 6 + System + textBackgroundColor + + 3 + MQA + + + + 6 + System + textColor + + + + + + + 292 + {{17, 11}, {65, 14}} + + YES + + 68288064 + 71435264 + EmailLabel: + + + + + + + + + 289 + {{456, 10}, {16, 17}} + + YES + + -2080244224 + 0 + Privacy Policy + + LucidaGrande + 13 + 1044 + + + -2040250113 + 36 + + NSImage + goArrow + + + + 400 + 75 + + + + + 289 + {{355, 11}, {100, 14}} + + YES + + 68288064 + 4326400 + PrivacyPolicyLabel + + + + + + + + {490, 114} + + + + {{0, 51}, {490, 114}} + + {0, 0} + + 67239424 + 0 + Title + + LucidaGrande + 11 + 16 + + + + 3 + MCAwLjgwMDAwMDAxAA + + + + 0 + 3 + 0 + NO + + + + 289 + {{330, 12}, {146, 32}} + + YES + + 67239424 + 134217728 + SendReportLabel + + + -2038284033 + 129 + + + DQ + 200 + 25 + + + + + 289 + {{214, 12}, {116, 32}} + + YES + + 67239424 + 134217728 + CancelLabel + + + -2038284033 + 129 + + + Gw + 200 + 25 + + + + + 256 + + YES + + + 256 + + YES + + + 266 + {{17, 83}, {456, 154}} + + YES + + 67239424 + 272760832 + VGhlIHN5c3RlbSBhbmQgb3RoZXIgYXBwbGljYXRpb25zIGhhdmUgbm90IGJlZW4gYWZmZWN0ZWQuIEEg +cmVwb3J0IGhhcyBiZWVuIGNyZWF0ZWQgdGhhdCB5b3UgY2FuIHNlbmQgdG8gPFJlYWxseSBMb25nIENv +bXBhbnkgTmFtZT4gdG8gaGVscCBpZGVudGlmeSB0aGUgcHJvYmxlbS4gTG9yZW0gaXBzdW0gZG9sb3Ig +c2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdC4gU2VkIGFyY3UgdXJuYSwgcHVsdmlu +YXIgc2l0IGFtZXQsIHRpbmNpZHVudCBhYywgZmVybWVudHVtIHV0LCBsaWd1bGEuIFF1aXNxdWUgbWku +IER1aXMgbGVjdHVzLiBWZXN0aWJ1bHVtIHZlbGl0LiBNb3JiaSB0dXJwaXMuIE51bmMgYXQgZGlhbSBj +b25zZWN0ZXR1ciB0dXJwaXMgdm9sdXRwYXQgdHJpc3RpcXVlLiBEb25lYyBxdWlzIGRpYW0uIFN1c3Bl +bmRpc3NlIHNjZWxlcmlzcXVlLiBRdWlzcXVlIHB1bHZpbmFyIG1pIGlkIHB1cnVzLiBFdGlhbSB2aXRh +ZSB0dXJwaXMgdml0YWUgbmVxdWUgcG9ydGEgY29uZ3VlLgoKUGxlYXNlIGhlbHAgdXMgZml4IHRoZSBw +cm9ibGVtIGJ5IGRlc2NyaWJpbmcgd2hhdCBoYXBwZW5lZCBiZWZvcmUgdGhlIGNyYXNoLiBMb3JlbSBp +cHN1bSBkb2xvciBzaXQgYW1ldCwgY29uc2VjdGV0dXIgYWRpcGlzY2luZyBlbGl0LiBTZWQgYXJjdSB1 +cm5hLCBwdWx2aW5hciBzaXQgYW1ldCwgdGluY2lkdW50IGFjLCBmZXJtZW50dW0gdXQsIGxpZ3VsYS4g +UXVpc3F1ZSBtaS4gRHVpcyBsZWN0dXMuA + + + + + + + + + 274 + {{20, 14}, {450, 61}} + + YES + + 341966337 + 272760832 + Line 1 Line 1 Line 1 Line 1 Line 1 Line 1 Line 1 Line 1 Line 1 Line 1 Line 1 Line 1 Line 2 Line 2 Line 2 Line 2 Line 2 Line 2 Line 2 Line 2 Line 2 Line 2 Line 2 Line 2 Line 3 Line 3 Line 3 Line 3 Line 3 Line 3 Line 3 Line 3 Line 3 Line 3 Line 3 Line 3 Line 4 Line 4 Line 4 Line 4 Line 4 Line 4 Line 4 Line 4 Line 4 Line 4 Line 4 Line 4 + + + YES + + + + + + + 256 + + YES + + + 256 + + YES + + + 266 + {{85, 10}, {381, 54}} + + YES + + 67239424 + 272629760 + The application <Really Long App Name Here> has quit unexpectedly. + + LucidaGrande-Bold + 14 + 16 + + + + + + + + + 268 + + YES + + YES + Apple PDF pasteboard type + Apple PICT pasteboard type + Apple PNG pasteboard type + NSFilenamesPboardType + NeXT Encapsulated PostScript v1.2 pasteboard type + NeXT TIFF v4.0 pasteboard type + + + {{16, 0}, {64, 64}} + + YES + + 130560 + 33554432 + + NSImage + NSApplicationIcon + + 0 + 0 + 0 + NO + + YES + + + {482, 70} + + + + {{4, 245}, {482, 70}} + + {0, 0} + + 67239424 + 0 + Title + + + + 3 + MCAwLjgwMDAwMDAxAA + + + + 0 + 3 + 0 + NO + + + {490, 325} + + + + {{0, 160}, {490, 325}} + + {0, 0} + + 67239424 + 0 + Title + + + + 3 + MCAwLjgwMDAwMDAxAA + + + + 0 + 3 + 0 + NO + + + + 268 + {{17, 20}, {163, 14}} + + YES + + 68288064 + 272630784 + xx seconds. + + + + + + + + {490, 489} + + {{0, 0}, {2560, 1578}} + {72, 27} + {1.79769e+308, 1.79769e+308} + + + YES + + + + + YES + + + sendReport: + + + + 45 + + + + cancel: + + + + 46 + + + + showPrivacyPolicy: + + + + 53 + + + + value: emailValue + + + + + + value: emailValue + value + emailValue + + NSNullPlaceholder + optional + + 2 + + + 90 + + + + initialFirstResponder + + + + 91 + + + + value: commentsValue + + + + + + value: commentsValue + value + commentsValue + + NSNullPlaceholder + optional comments + + 2 + + + 124 + + + + nextKeyView + + + + 125 + + + + nextKeyView + + + + 126 + + + + nextKeyView + + + + 127 + + + + delegate + + + + 128 + + + + alertWindow_ + + + + 142 + + + + preEmailBox_ + + + + 150 + + + + headerBox_ + + + + 151 + + + + emailSectionBox_ + + + + 152 + + + + privacyLinkLabel_ + + + + 153 + + + + commentMessage_ + + + + 154 + + + + dialogTitle_ + + + + 155 + + + + emailLabel_ + + + + 156 + + + + cancelButton_ + + + + 158 + + + + sendButton_ + + + + 159 + + + + emailEntryField_ + + + + 161 + + + + privacyLinkArrow_ + + + + 162 + + + + emailMessage_ + + + + 163 + + + + commentsEntryField_ + + + + 176 + + + + value: countdownMessage + + + + + + value: countdownMessage + value + countdownMessage + 2 + + + 194 + + + + countdownLabel_ + + + + 208 + + + + + YES + + 0 + + + + + + -2 + + + File's Owner + + + -1 + + + First Responder + + + -3 + + + Application + + + 1 + + + YES + + + + Window + + + 2 + + + YES + + + + + + + + + + 12 + + + YES + + + + + + 14 + + + YES + + + + + + 132 + + + YES + + + + + + + + + + 145 + + + YES + + + + + + + + 189 + + + YES + + + + + + 191 + + + Shared User Defaults Controller + + + 210 + + + + + 211 + + + + + 221 + + + + + 58 + + + YES + + + + + + 215 + + + + + 18 + + + YES + + + + + + 212 + + + + + 20 + + + YES + + + + + + 213 + + + + + 48 + + + YES + + + + + + 214 + + + + + 66 + + + YES + + + + + + 216 + + + + + 8 + + + YES + + + + + + 217 + + + + + 116 + + + YES + + + + + + 218 + + + + + 147 + + + YES + + + + + + + 3 + + + YES + + + + + + 219 + + + + + 6 + + + YES + + + + + + 220 + + + + + + + YES + + YES + -3.ImportedFromIB2 + 1.IBEditorWindowLastContentRect + 1.IBWindowTemplateEditedContentRect + 1.ImportedFromIB2 + 1.windowTemplate.hasMinSize + 1.windowTemplate.minSize + 116.CustomClassName + 116.ImportedFromIB2 + 12.ImportedFromIB2 + 132.ImportedFromIB2 + 14.ImportedFromIB2 + 145.ImportedFromIB2 + 147.ImportedFromIB2 + 18.CustomClassName + 18.ImportedFromIB2 + 189.ImportedFromIB2 + 191.ImportedFromIB2 + 2.ImportedFromIB2 + 20.ImportedFromIB2 + 3.ImportedFromIB2 + 48.ImportedFromIB2 + 58.ImportedFromIB2 + 6.ImportedFromIB2 + 66.ImportedFromIB2 + 8.ImportedFromIB2 + + + YES + + {{0, 656}, {490, 489}} + {{0, 656}, {490, 489}} + + + {72, 5} + LengthLimitingTextField + + + + + + + LengthLimitingTextField + + + + + + + + + + + + + + + YES + + + YES + + + + + YES + + + YES + + + + 221 + + + + YES + + LengthLimitingTextField + NSTextField + + IBUserSource + + + + + Reporter + NSObject + + YES + + YES + cancel: + sendReport: + showPrivacyPolicy: + + + YES + id + id + id + + + + YES + + YES + alertWindow_ + cancelButton_ + commentMessage_ + commentsEntryField_ + countdownLabel_ + dialogTitle_ + emailEntryField_ + emailLabel_ + emailMessage_ + emailSectionBox_ + headerBox_ + preEmailBox_ + privacyLinkArrow_ + privacyLinkLabel_ + sendButton_ + + + YES + NSWindow + NSButton + NSTextField + LengthLimitingTextField + NSTextField + NSTextField + LengthLimitingTextField + NSTextField + NSTextField + NSBox + NSBox + NSBox + NSView + NSTextField + NSButton + + + + IBUserSource + + + + + + 0 + IBCocoaFramework + + com.apple.InterfaceBuilder.CocoaPlugin.macosx + + + + com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 + + + YES + ../Breakpad.xcodeproj + 3 + + YES + + YES + NSApplicationIcon + goArrow + + + YES + {128, 128} + {128, 128} + + + + diff --git a/shared/sentry/external/breakpad/src/client/mac/sender/English.lproj/InfoPlist.strings b/shared/sentry/external/breakpad/src/client/mac/sender/English.lproj/InfoPlist.strings new file mode 100644 index 0000000000000000000000000000000000000000..65d161818a12f785bc58a885664ac859f1e755ff GIT binary patch literal 156 zcmezWPl>^q!HvO*p_CzyA%!7_A(g?8A(0^$NGmZYFxUbyNJSA)te7DK$O{6>6fooi gMM{9IBA~1_0~c5gswplEnLr%{Kob)gD#-EPxB5QXQOr&vYwrWcLivIvP%kdUBIwe$g;#z`Z^afzLVygcxonK*Xa)D;qU z$a1v1o}Y8(Z}a=t&<-uxrPa2ynJsK;75j;m?2}!w%YF5glNyN$@3m#VE|E{1Px#Gv zuaV5qxM6*0LwjL^&e;bvGn*qnwNF^hZ0xoNNWbMB*mpE@R+-)M%pZ9=uy=gDaUaQk z*b0f7lNC5}`-x7Dznb4M+8aB<=9?ZAXJDNo^U!~VZsm3gN3+15c-D?jaoLYhkrTQ* zXR&aNWFvODM=B#y6V?sJBjitbmz>=qy>vDWzgACiRlI^(ava(vaF(!F2(tF@>MvHCkqV^8#wM#Y~5S>_((}Jif)|EYs*STk1`Be|L z)w1G56JbV&Jyj{Ha@CRQsv@JBB_m$VlVoe}3e~G>TfHp|k!e+VOdEM^>ad+84TAx4 z1==%EK1Qx8x#Qb-=QBD(z1r%juEsp7vdUSl-QFB2O(xBzHS1s;F;;adUYU1o+i~I+ zeHZasV>3Cc;8l3t>P#__zK~bDO5T?dWk7y%;#9JJI3G(Qr=Are;g$dUIc?@$%lU6@ zh6SN(ZG6F-@-*+v%8$Ej9CI2|R?}lf9>if|`?SvQNhCXWp1M&sTPyP1OtA}mHgoYk zS*dx%y`9*XNW{GB$~JgnDk`3H+pm8LR!yMb7A!RB8oT?ozS-v|ICuG6*#$9ZvEDfT zTRY}v98za`j_8k)X9~icUF35J>uw|!gSZWKr^!y?xe(r_j&ujsRE#QkvF#ACi25B9 zQ&qwWyQ-WP)d4!mBi>5JjTGIsAfxKywhtxuoVUxZ)(T%4KKuOjdFx|$<~~#v>gGOn X#XZSBfBL_&pIz76bL_hQp7! + + + + ActiveLayerIndex + 0 + ApplicationVersion + + com.omnigroup.OmniGrafflePro + 137.6.0.106738 + + AutoAdjust + + BackgroundGraphic + + Bounds + {{0, 0}, {512, 512}} + Class + SolidGraphic + FontInfo + + Font + CalisMTBol + Size + 112 + + ID + 2 + Style + + fill + + Color + + a + 0 + b + 0 + g + 0.852018 + r + 0.998962 + + + shadow + + Draws + NO + + stroke + + Draws + NO + + + + CanvasOrigin + {0, 0} + CanvasSize + {512, 512} + ColumnAlign + 1 + ColumnSpacing + 36 + CreationDate + 2008-11-14 16:58:15 -0700 + Creator + John P. Developer + DisplayScale + 1 pt = 1 px + FileType + flat + GraphDocumentVersion + 6 + GraphicsList + + + Bounds + {{33.9443, 35.3885}, {444.111, 437.112}} + Class + ShapedGraphic + FontInfo + + Font + CalisMTBol + Size + 112 + + ID + 31 + Rotation + 270 + Shape + Bezier + ShapeData + + UnitPoints + + {-0.5, -0.439247} + {-0.5, -0.485429} + {-0.446294, -0.512626} + {-0.409932, -0.494153} + {-0.373569, -0.47568} + {0.436363, -0.0733799} + {0.472729, -0.0549059} + {0.50909, -0.0364333} + {0.509091, 0.0364345} + {0.472729, 0.0549059} + {0.436368, 0.0733802} + {-0.373569, 0.475681} + {-0.409932, 0.494153} + {-0.446294, 0.512626} + {-0.500001, 0.485429} + {-0.5, 0.439247} + {-0.49998, 0.393072} + {-0.500002, -0.393066} + + + Style + + fill + + Color + + b + 0 + g + 0.770962 + r + 0.997971 + + Draws + NO + FillType + 3 + GradientCenter + {-0.609524, 0} + GradientColor + + b + 0 + g + 0.911574 + r + 0.998779 + + MiddleFraction + 0.6111111044883728 + + shadow + + Color + + a + 0.43 + b + 0 + g + 0 + r + 0 + + Draws + NO + Fuzziness + 7.2213706970214844 + ShadowVector + {0, 6} + + stroke + + Color + + b + 0 + g + 0.766903 + r + 0.997925 + + Width + 7 + + + Text + + Pad + 0 + VerticalPad + 0 + + TextPlacement + 0 + TextRelativeArea + {{0.06, 0.17}, {0.88, 0.5}} + TextRotation + 90 + Wrap + NO + + + Bounds + {{3.89085, 67.8908}, {404.218, 332}} + Class + ShapedGraphic + FontInfo + + Font + CalisMTBol + Size + 112 + + ID + 30 + Rotation + 270 + Shape + Bezier + ShapeData + + UnitPoints + + {-0.5, -0.5} + {-0.459695, -0.475464} + {0.429465, 0.0537758} + {0.469773, 0.0783133} + {0.510074, 0.102849} + {0.510077, 0.198357} + {0.469773, 0.222892} + {0.429473, 0.247428} + {-0.00521517, 0.499998} + {-0.00521785, 0.5} + {-0.00521713, -0.113381} + {-0.44962, -0.458615} + + + Style + + fill + + Color + + a + 0 + b + 1 + g + 1 + r + 1 + + FillType + 2 + GradientAngle + 180 + GradientCenter + {-0.609524, 0} + GradientColor + + a + 0.5 + w + 1 + + MiddleFraction + 0.6111111044883728 + + shadow + + Color + + a + 0.51 + b + 0 + g + 0 + r + 0 + + Draws + NO + Fuzziness + 3.3371961116790771 + ShadowVector + {0, 2} + + stroke + + Color + + b + 0 + g + 0.766903 + r + 0.997925 + + Draws + NO + Width + 2 + + + Text + + Pad + 0 + VerticalPad + 0 + + TextPlacement + 0 + TextRelativeArea + {{0.06, 0.17}, {0.88, 0.5}} + TextRotation + 90 + Wrap + NO + + + Bounds + {{33.9443, 35.3886}, {444.112, 437.111}} + Class + ShapedGraphic + FontInfo + + Font + CalisMTBol + Size + 112 + + ID + 29 + Rotation + 270 + Shape + Bezier + ShapeData + + UnitPoints + + {-0.5, -0.439247} + {-0.500001, -0.485429} + {-0.446295, -0.512626} + {-0.409932, -0.494153} + {-0.373568, -0.475681} + {0.436363, -0.0733802} + {0.472729, -0.0549062} + {0.509089, -0.0364334} + {0.509092, 0.0364341} + {0.472729, 0.0549056} + {0.436369, 0.0733803} + {-0.373568, 0.475681} + {-0.409932, 0.494153} + {-0.446294, 0.512626} + {-0.500001, 0.485428} + {-0.5, 0.439248} + {-0.499978, 0.39307} + {-0.500003, -0.393066} + + + Style + + fill + + Color + + a + 0.2 + b + 1 + g + 1 + r + 1 + + FillType + 2 + GradientAngle + 90 + GradientCenter + {-0.609524, 0} + GradientColor + + a + 0 + w + 1 + + MiddleFraction + 0.6111111044883728 + + shadow + + Color + + a + 0.51 + b + 0 + g + 0 + r + 0 + + Draws + NO + Fuzziness + 3.3371961116790771 + ShadowVector + {0, 2} + + stroke + + Color + + b + 0 + g + 0.766903 + r + 0.997925 + + Draws + NO + Width + 2 + + + Text + + Pad + 0 + VerticalPad + 0 + + TextPlacement + 0 + TextRelativeArea + {{0.06, 0.17}, {0.88, 0.5}} + TextRotation + 90 + Wrap + NO + + + Bounds + {{176, 102.384}, {158.841, 537.616}} + Class + ShapedGraphic + FontInfo + + Font + CalisMTBol + Size + 425 + + ID + 26 + Shape + Rectangle + Style + + fill + + Draws + NO + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Pad + 0 + Text + {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf350 +{\fonttbl\f0\fnil\fcharset0 CalistoMT;} +{\colortbl;\red255\green255\blue255;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural + +\f0\b\fs850 \cf1 !} + VerticalPad + 0 + + Wrap + NO + + + Bounds + {{176, 104}, {158.841, 537.616}} + Class + ShapedGraphic + FontInfo + + Color + + b + 0 + g + 0.749523 + r + 0.997726 + + Font + CalisMTBol + Size + 425 + + ID + 27 + Shape + Rectangle + Style + + fill + + Draws + NO + + shadow + + Draws + NO + + stroke + + Draws + NO + + + Text + + Pad + 0 + RTFD + + BAtzdHJlYW10eXBlZIHoA4QBQISEhBJOU0F0dHJpYnV0 + ZWRTdHJpbmcAhIQITlNPYmplY3QAhZKEhIQITlNTdHJp + bmcBlIQBKwEhhoQCaUkBAZKEhIQMTlNEaWN0aW9uYXJ5 + AJSEAWkEkoSWlhBOU1BhcmFncmFwaFN0eWxlhpKEhIQQ + TlNQYXJhZ3JhcGhTdHlsZQCUhARDQ0BTAgCEhIQHTlNB + cnJheQCUmQyShISECU5TVGV4dFRhYgCUhAJDZgAchpKE + n54AOIaShJ+eAFSGkoSfngBwhpKEn54AgYwAhpKEn54A + gagAhpKEn54AgcQAhpKEn54AgeAAhpKEn54AgfwAhpKE + n54AgRgBhpKEn54AgTQBhpKEn54AgVABhoYAhpKElpYG + TlNGb250hpKEhIQGTlNGb250HpSZIIQFWzMyY10GAAAA + FgAAAP/+QwBhAGwAaQBzAE0AVABCAG8AbAAAAIQBZoGp + AYQBYwCiAaIAogCGkoSWlg1OU1N0cm9rZVdpZHRohpKE + hIQITlNOdW1iZXIAhIQHTlNWYWx1ZQCUhAEqhIQBZKYD + hpKElpYHTlNDb2xvcoaShISEB05TQ29sb3IAlKIChARm + ZmZmAYN4dz8/AAGGhoY= + + Text + {\rtf1\ansi\ansicpg1252\cocoartf949\cocoasubrtf350 +{\fonttbl\f0\fnil\fcharset0 CalistoMT;} +{\colortbl;\red255\green255\blue255;\red254\green191\blue0;} +\pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\qc\pardirnatural + +\f0\b\fs850 \cf2 \outl\strokewidth60 \strokec2 !} + VerticalPad + 0 + + Wrap + NO + + + Bounds + {{33.9441, 35.3884}, {444.112, 437.111}} + Class + ShapedGraphic + FontInfo + + Font + CalisMTBol + Size + 112 + + ID + 16 + Rotation + 270 + Shape + Bezier + ShapeData + + UnitPoints + + {-0.5, -0.439247} + {-0.5, -0.485429} + {-0.446295, -0.512626} + {-0.409933, -0.494153} + {-0.373569, -0.47568} + {0.436363, -0.073379} + {0.472729, -0.0549049} + {0.50909, -0.0364324} + {0.509091, 0.0364344} + {0.472729, 0.0549058} + {0.436368, 0.0733801} + {-0.373569, 0.47568} + {-0.409933, 0.494153} + {-0.446295, 0.512626} + {-0.500001, 0.485429} + {-0.5, 0.439247} + {-0.49998, 0.393072} + {-0.500002, -0.393066} + + + Style + + fill + + Color + + b + 0 + g + 0.770962 + r + 0.997971 + + FillType + 3 + GradientCenter + {-0.609524, 0} + GradientColor + + b + 0 + g + 0.911574 + r + 0.998779 + + MiddleFraction + 0.6111111044883728 + + shadow + + Color + + a + 0.9 + b + 0 + g + 0 + r + 0 + + Fuzziness + 8.0632610321044922 + ShadowVector + {0, 9} + + stroke + + Color + + b + 0 + g + 0.766903 + r + 0.997925 + + Draws + NO + Width + 2 + + + Text + + Pad + 0 + VerticalPad + 0 + + TextPlacement + 0 + TextRelativeArea + {{0.06, 0.17}, {0.88, 0.5}} + TextRotation + 90 + Wrap + NO + + + GridInfo + + GridSpacing + 4 + ShowsGrid + YES + SnapsToGrid + YES + + GuidesLocked + NO + GuidesVisible + YES + HPages + 1 + ImageCounter + 2 + KeepToScale + + Layers + + + Lock + NO + Name + Layer 1 + Print + YES + View + YES + + + LayoutInfo + + Animate + NO + circoMinDist + 18 + circoSeparation + 0.0 + layoutEngine + dot + neatoSeparation + 0.0 + twopiSeparation + 0.0 + + LinksVisible + NO + MagnetsVisible + NO + MasterSheets + + ModificationDate + 2008-11-17 11:41:28 -0700 + Modifier + Preston Jackson + NotesVisible + NO + Orientation + 2 + OriginVisible + NO + PageBreaks + YES + PrintInfo + + NSBottomMargin + + float + 41 + + NSLeftMargin + + float + 18 + + NSPaperSize + + size + {612, 792} + + NSRightMargin + + float + 18 + + NSTopMargin + + float + 18 + + + PrintOnePage + + QuickLookPreview + + JVBERi0xLjMKJcTl8uXrp/Og0MTGCjQgMCBvYmoKPDwgL0xlbmd0aCA1IDAgUiAvRmls + dGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAGVlktvJEUQhO/9K5IbHFyuR9brioEV + nFh5JM5o5MWsPIA9IP4+X2b12gPrBbHWSt3lzkdFREb6Ud7Ko0R+asr+/+lOfpBfJIYW + /Z/8LNc35yTHsyROh/ZYxyTgfJTrN/zip7NcxVD3r1+e9oQbCd/J9fd3T8e7337/48cH + eSLjm3O2wEfR2ry8tiJ5hDJSVEk9pF7jkONJrr89JfnqV/p8uz1KK1YpJynNHlKVk2gJ + JZWqdqZz9iklBc21ZmkxlJoin0/vsEonTo6b9lC79iQzh6azSc7FYjINZWIkk4MCKtrG + ejhyRpZeRYcSNIbk7oXmtk5m4mRaD/NvYcOK1bKnpnkuu4qt6jqEVujLmtuOvFgdrXv7 + mcjWQxut71ds5LcbAIADIcTkDgpt4TKmyigWoXNzVAYtfYzdUe5fOT25ACxMtZAQiPeX + xEVTSCkNeaBLDbXWDhwfnxH1QHJX0sfiulDDhhpMgMnUJZAGDLkCea3T6b+9T3K+N/pf + F6qL8+ZW0hYDjM4ESlFubyTlAFd/kvfwRKilj3IFRdTQHJsk6EwzW5UvDwBY1xf2cNVL + SDWiyTa2AyL8JgXr8fBOPv/sCzm8l68PNERtwm0wGIb4yTrK2LiYt3+rI5+uY1df7JW8 + CD9tS/XNGUdxFSUs1e+yiQPuXPUMyVI9lL2qeh2bq16Rjet+qRdVLcWrceySz8+S30+A + zyTPTNiYWMQSe10Z64vY+/OoudZNus9dudwRqE+rVVty97v63bZd7iZHL7PkjnfYe5xw + vvTOAJtW5+gMv3vFB8RetF6yzYQ5x3/L08wKeQZ3t1pin5Fp1GpD0ORKy7AnlLN/kbPS + 2ofZwIlqwA1G35aT5d3JyGncLARwMKZb0Tt2gIAHLOBGpTJExgtaxZ/MjxbK+B8mYdQ0 + 5QuYoSumBgvBBXEsP0n9khlidnI8xrK6LZqBzVm2bFzEhIjMiIwcPyGGeQqjdjrwT7h+ + LYHiADxbwGHg6+Uux+3+4u1/I6yj5DSiaKw0CBBXpRSDluldCFM4zgHvPa9zujJygMR3 + RXlB+JWt1t0PvmNg35PwHxsOE4mw1Weu0cykNci2JJjJhX+sVUm1pt4BgIOOr6HBGsLd + eYUt0uRFYFIEgAl4n6yrBqw6QuzKxtA0wdf4g/OZ2QWMAd4DfUgXOqHaYjtc4/Gjshmh + y/PP/YQ62VDzj4dlZttYGh2ZHAwCzaCeVcoaJty3VGm2b4bnZwuhC2LommlOA9lxF2ub + WDS6QrjdWjcjNZJ3Uzh/OyA6IjK7cIVwj0t8fPwuD05ya6b+F7C1v1cKZW5kc3RyZWFt + CmVuZG9iago1IDAgb2JqCjk4MwplbmRvYmoKMiAwIG9iago8PCAvVHlwZSAvUGFnZSAv + UGFyZW50IDMgMCBSIC9SZXNvdXJjZXMgNiAwIFIgL0NvbnRlbnRzIDQgMCBSIC9NZWRp + YUJveCBbMCAwIDUxMiA1MTJdCj4+CmVuZG9iago2IDAgb2JqCjw8IC9Qcm9jU2V0IFsg + L1BERiAvVGV4dCAvSW1hZ2VCIC9JbWFnZUMgL0ltYWdlSSBdIC9Db2xvclNwYWNlIDw8 + IC9DczIgMTIgMCBSCi9DczEgNyAwIFIgPj4gL0V4dEdTdGF0ZSA8PCAvR3MxIDE3IDAg + UiAvR3MyIDE4IDAgUiA+PiAvRm9udCA8PCAvRjEuMCAxMSAwIFIKPj4gL1hPYmplY3Qg + PDwgL0ltMiAxMyAwIFIgL0ltMSA4IDAgUiAvSW0zIDE1IDAgUiA+PiAvU2hhZGluZyA8 + PCAvU2gxIDEwIDAgUgo+PiA+PgplbmRvYmoKMTAgMCBvYmoKPDwgL0NvbG9yU3BhY2Ug + NyAwIFIgL1NoYWRpbmdUeXBlIDMgL0Nvb3JkcyBbIC0yNzEuMzA2MyAwIDAgLTI3MS4z + MDYzIDAgNTQwLjI2NApdIC9Eb21haW4gWyAwIDEgXSAvRXh0ZW5kIFsgZmFsc2UgZmFs + c2UgXSAvRnVuY3Rpb24gMTkgMCBSID4+CmVuZG9iagoxMyAwIG9iago8PCAvTGVuZ3Ro + IDE0IDAgUiAvVHlwZSAvWE9iamVjdCAvU3VidHlwZSAvSW1hZ2UgL1dpZHRoIDI1NiAv + SGVpZ2h0IDI1NiAvQ29sb3JTcGFjZQo3IDAgUiAvU01hc2sgMjAgMCBSIC9CaXRzUGVy + Q29tcG9uZW50IDggL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngB7dABAQAA + CAKg/p+2Bx4QJpBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB + AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg + wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM + GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB + AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg + wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM + GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB + AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg + wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM + GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB + AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg + wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM + GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB + AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBg + wIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYM + GDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIAB + AwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDBgwIABAwYMGDDQBg4DBgwYMGDA + wNjAA65NNU0KZW5kc3RyZWFtCmVuZG9iagoxNCAwIG9iago4ODMKZW5kb2JqCjggMCBv + YmoKPDwgL0xlbmd0aCA5IDAgUiAvVHlwZSAvWE9iamVjdCAvU3VidHlwZSAvSW1hZ2Ug + L1dpZHRoIDkxMiAvSGVpZ2h0IDkyNiAvQ29sb3JTcGFjZQoyMiAwIFIgL1NNYXNrIDIz + IDAgUiAvQml0c1BlckNvbXBvbmVudCA4IC9GaWx0ZXIgL0ZsYXRlRGVjb2RlID4+CnN0 + cmVhbQp4Ae3QgQAAAADDoPlTH+SFUGHAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + gAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwY + MGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAED + BgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDAgAEDBgwYMGDA + wMvAAKraAAEKZW5kc3RyZWFtCmVuZG9iago5IDAgb2JqCjExMDcwCmVuZG9iagoxNSAw + IG9iago8PCAvTGVuZ3RoIDE2IDAgUiAvVHlwZSAvWE9iamVjdCAvU3VidHlwZSAvSW1h + Z2UgL1dpZHRoIDI1NiAvSGVpZ2h0IDI1NiAvQ29sb3JTcGFjZQo3IDAgUiAvU01hc2sg + MjUgMCBSIC9CaXRzUGVyQ29tcG9uZW50IDggL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4K + c3RyZWFtCngB7dKBDQAgDMMw/n+6SHBGvA+aeXMKFAucd8XlNiuw8U9BuQD/5e/bzj8D + 5QL8l79vO/8MlAvwX/6+7fwzUC7Af/n7tvPPQLkA/+Xv284/A+UC/Je/bzv/DJQL8F/+ + vu38M1AuwH/5+7bzz0C5AP/l79vOPwPlAvyXv287/wyUC/Bf/r7t/DNQLsB/+fu2889A + uQD/5e/bzj8D5QL8l79vO/8MlAvwX/6+7fwzUC7Af/n7tvPPQLkA/+Xv284/A+UC/Je/ + bzv/DJQL8F/+vu38M1AuwH/5+7bzz0C5AP/l79vOPwPlAvyXv287/wyUC/Bf/r7t/DNQ + LsB/+fu2889AuQD/5e/bzj8D5QL8l79vO/8MlAvwX/6+7fwzUC7Af/n7tvPPQLkA/+Xv + 284/A+UC/Je/bzv/DJQL8F/+vu38M1AuwH/5+7bzz0C5AP/l79vOPwPlAvyXv287/wyU + C/Bf/r7t/DNQLsB/+fu2889AuQD/5e/bzj8D5QL8l79vO/8MlAvwX/6+7fwzUC7Af/n7 + tvPPQLkA/+Xv284/A+UC/Je/bzv/DJQL8F/+vu38M1AuwH/5+7bzz0C5AP/l79vOPwPl + AvyXv287/wyUC/Bf/r7t/DNQLsB/+fu2889AuQD/5e/bzj8D5QL8l79vO/8MlAvwX/6+ + 7fwzUC7Af/n7tvPPQLkA/+Xv284/A+UC/Je/bzv/DJQL8F/+vu38M1AuwH/5+7bzz0C5 + AP/l79vOPwPlAvyXv287/wyUC/Bf/r7t/DNQLsB/+fu2889AuQD/5e/bzj8D5QL8l79v + O/8MlAvwX/6+7fwzUC7Af/n7tvPPQLkA/+Xv284/A+UC/Je/bzv/DJQL8F/+vu38M1Au + wH/5+7bzz0C5AP/l79vOPwPlAvyXv287/wyUC/Bf/r7t/DNQLsB/+fu2889AuQD/5e/b + zj8D5QL8l79vO/8MlAvwX/6+7fwzUC7Af/n7tvPPQLkA/+Xv284/A+UC/Je/bzv/DJQL + 8F/+vu38M1AuwH/5+7bzz0C5AP/l79vOPwPlAvyXv287/wyUC/Bf/r7t/DNQLsB/+fu2 + 889AuQD/5e/bzj8D5QL8l79vO/8MlAvwX/6+7fwzUC7Af/n7tvPPQLkA/+Xv284/A+UC + /Je/bzv/DJQL8F/+vu38M1AuwH/5+7bzz0C5AP/l79vOPwPlAvyXv287/wyUC/Bf/r7t + /DNQLsB/+fu2889AuQD/5e/bzj8D5QL8l79vO/8MlAvwX/6+7fwzUC7Af/n7tvPPQLkA + /+Xv284/A+UC/Je/bzv/DJQL8F/+vu38M1AuwH/5+7bzz0C5AP/l79vOPwPlAvyXv287 + /wyUC/Bf/r7t/DNQLsB/+fu2889AuQD/5e/bzj8D5QL8l79vO/8MlAvwX/6+7fwzUC7A + f/n7tvPPQLkA/+Xv284/A+UC/Je/bzv/DJQL8F/+vu38M1AuwH/5+7bzz0C5AP/l79vO + PwPlAvyXv287/wyUC/Bf/r7t/DNQLsB/+fu2889AuQD/5e/bzj8D5QL8l79vO/8MlAvw + X/6+7fwzUC7Af/n7tvPPQLkA/+Xv284/A+UC/Je/bzv/DJQL8F/+vu3f/wUgwjJ6CmVu + ZHN0cmVhbQplbmRvYmoKMTYgMCBvYmoKMTIxNAplbmRvYmoKMjMgMCBvYmoKPDwgL0xl + bmd0aCAyNCAwIFIgL1R5cGUgL1hPYmplY3QgL1N1YnR5cGUgL0ltYWdlIC9XaWR0aCA5 + MTIgL0hlaWdodCA5MjYgL0NvbG9yU3BhY2UKL0RldmljZUdyYXkgL0JpdHNQZXJDb21w + b25lbnQgOCAvRmlsdGVyIC9GbGF0ZURlY29kZSA+PgpzdHJlYW0KeAHsnYlfTV37/288 + j4xJUVJRGtCEoggliZCxyFQUlUiGNCgJIXOFyFTKlCljmUMlUaZK4XY/N31fv//jd621 + 9z57n6nxDPvU1ev1PHfts89a61zn+njvz7XW3uuvv/AHI4ARwAhgBDACGAGMQOeIQDfy + 0132hx7tHB8QPwVGQCciwOiwR48e//nPf/4r9QMH/gPHQaRwjk58FhwkRkCHI0ClSIT4 + 3//27Kmn10vuR0+vZ8///peoEjWpw98zDl0HIkAuT0GLIEWqxN59+vTt269ff8lPv379 + +vbt07s3iJSoEkSJmtSBrxWHqIsRoGKkWuzVqzcRYn/9AQMMDAwGDhxoyPwMHAh/GgzQ + 1+/fD1QJmtQDTTKc1MUPjGPGCIg1AqwYe8IFKmiRSNFgoKHRoEGDjY1NTIbAjyn5PxMT + Y+PBg4wMQZcD9PszmmQkiWZSrN8sjkv3IgBqJI6xJxEjaNHAAKRobDLE1HSombmFxbBh + w+nPsGHDLMzNzYaampqAKkGUA/SBk+TSFShJLlx174PjiDECoosAMY0gRj0qRuCi0WDj + IaZDzS2GWVpZjbC2sbG1tWN+bG1trEdYWVkOH2ZuBqIETRoYEEyCJOl1KypSdN8tDkjX + IsCqsVevPn37DwAwDjYBLYIUR9jYjRxlb+/g6OjkDD9j4H9OTo4O9vajR9nZgiyHW5iZ + DjEmmNTvB2aSgSQqUte+fhyvqCLAqJGiUZ+QcYipmYWllbXtyNH2js5jxrm4uI6f4Obu + 7j5xIvyfm9uE8a4u48Y6OznYj7KzGWE1zHyoKUjSYED/vqhIUX2vOBhdjAD1jWAbwTXq + GxgOMiFiHGEzcrSD0xgX1wnuEz0mT5nq6TVtmrf39One3t7TvLw8p0yePMndbbzLWGdH + +1G21lbDzU2HDDYayCmS+EhdjASOGSOg9QiQKg4p4vTpBxeqgEbz4VY2I+0dQYtuEydP + 8Zw23cd3pt/sOXPn+jM/c+fM9pvlO8PH22vqZA/3CS5jnQCT1pbDzACSAw0oI0llBy9a + tf7N4gB0LwIUjlDFoWwcPGSohaW13WgQ44SJk6dOmz5j1uy5/vMXLgoIDFyydGkQ+Vm6 + ZElgwOJFC+b5z/Hz9fH2nDzJzXUcSNKGQBIUSRlJa62ISN1LBxyxdiNA4QjGsS9cqYIa + hxE0Oo8bP3Gyp/eMWXP8FywOXLpsxargkNWhYWFr165bt27t2rDQNauDg1cuDwoMWDjf + f7avz7QpHm4uYxxH244gijQyGNCvDxR2EJHa/Waxdx2MAJEjgWP/AQMHmQy1sLIZ5TDG + xc3Dc7qvH2hxybKVwWvCwiPWb4jeFBOzecuWrVu3btmyOSYmemNUZPja0JBVy5cGLpo3 + Z6aP1+SJ4wGSdiOGw1WroYE+FHYAkegidTAlcMhaiwC5ViV1HICjkbGpuaU1qNF14uRp + Pn7+CwODVoaEhkdu2LR5a2x8QmJS8s6UXbt2pe7alZKyM2lHQnzcti0xG9eHr10dvHzJ + YpDk9KkeE1yc7G2thg0dAhetBJHURWrtw2HHGAHdigDI8T//Jc4R4DjEbPiIkfagxine + vnPmBwStXL02YkPM1u3xO3bu2r133/709IOHDh0mP4cOpqcf2Je2JzUlKSFu2+bo9eGh + wcsDF/r7+Xh6TBjnNNrGyoJctFJEkmtW3YoJjhYjoKUIsNaxT38DQ2NTCytbe2cXUOPM + uQsDV4SsjdgYExuflLI77UD64aPHMzNPnDyVnX0afrKzT508kZWZcezIwfR9e1KTE+O2 + booKX7MqaPG82T5eHhPGOoy0Hm4GiNTvR65ZUZBa+naxWx2LACtHcq1qYjbcepTjOLfJ + RI1LVq4J3wBiTE7de+DQ0Yysk9k5OecuXMzNu3QpH34uXcrLvXjh/NmcM6dOZB47nL5v + d8qOuC3RkWHBywLmASMnuY6Bi1Zz08GG5JoVBaljWYHD1VIEmEoOWEe4VjUHOI4Z7+E1 + g6gxNCJ6S3xSalr64eNZp86cvZB7Kf/y1WvXC2/cuHETfuA/NwqvX71SkJ938VzO6ROZ + Rw/u370zITZm/TqiyFnTp7i5OI4cMYyUdfqjILX07WK3OhaBbt2gsNqrN1jHwXCtOtJh + nNsUH7/5gSvXRERvjd+5+wCIMTvnQl7+lWuFN27dKbp3/wH8PCQ/8N/79+4W3b554/rV + gksXz505mXk0PS11x/aYqLXBQYvm+np5uDqPthk+1MSICFIPL1l1LDVwuFqIAJRyeuox + chw2YpSTyyQvX//Fy0LCN25NSNmbfjQTxHjp8jWQ4r0HDx6VPH7y9NmzZ8+ZH/jt6ZOS + 4ocP7hfdvnH9Sn7uuTMnjh/al5q0PWZ92KrA+X7TJ7uNtbe1NBtCBYmXrFr4erFL3YoA + raz27jtgoLHpMOvRY8ZP9vabv2TV2qjN8Tv3ph87ceZ8XsG1G7fvghSfPHv+ovTV6zdl + ZeXsT1nZm9evSl++ePb0cTFo8lbhlfyLOacyD+9P3RG7KXz1ssVzZ3i6uzjYWZmzgsQq + q24lB45W0xEg8456vcA7GpsOt7EfA9eqcxYtWx2xaXvSblBjzoX8qzdu33tQ/OTZi9LX + b8rL31a+q6p6//4D8/P+fdW7yrcV5WVvXr18/rTk4f2im9cLcs9mZx7atythS9TaVYHz + ZnpNcnEUChJnPTT9FWN/uhMBiRwHEzmOdZs6wz9gRVjUloTU/UeyzlzIv3ajiIjx5euy + 8rfvqj5UV9d8/Pjp8+fPX+AH/vPp08ea6uoP799VVpS/KX3+rOThvduFl/POnjp+cE/y + 9k3hwUsX+Hl7uDqOpISEaQ9YqYOC1J30wJFqOAK0ltNH32AwXKzaj3X39PUPDF4XHZu8 + 52BG9vn8azfvPighYqyoBC2CEL98ra2rl/qpq6v9+uUzqPL9u7dEkk8e3QdF5uacOLp/ + V/zmyDXLFs6ePpkI0swEpj16Y01Hw18wdqdLESDmEZasGgzi5DhvSXB4THzK/iMncvKu + 3Lj74PGz0jflle+raz59+QpK/NbQ0Pj9+w/Jz/fvjY0N3+rrQZSfP1Z/qHpb/vrF0+L7 + t6/nXzidkb57x9ao0OWL5hBB2lkONTYc0LeX3n96ICB1KUVwrBqMADGPvfsZGA2xGDF6 + jJun77ylIRExCanpx7MvFBTeATW+KntbRcRYW18PSvzx8+fff/+S/vn7758/vhNR1hFJ + vq8kinx07+bVvJysw2nJsRvDViyeM93DxcF2+NDBA/VhGhKvWDX4BWNXuhQBMtXRq+8A + QxMzq5HOE6b6zlsSErk5cffBzJzcqzfvFT8rLXv7vvojiPFbA2iRKPGf//3vf/8KfuDP + f/6B43//BE0SSdZ8eAeKfPLgTmH+uZNH9+3cvnHtikWzvSe52NsMMx1k0B+vWHUpQXCs + mowAKebA1erAwUMt7RzHT57hD3LckrjnUFbOpet3Hjx58QbY+AkuU0GMoMV//gEl/v79 + h/w00R/66+/fv//993+gSSLJb/W1oMjK8lfPiu/duHwh+9j+lO0bw5Yv8ps2cezoERZD + jAb0Q0Bq8ivGvnQoAlDMIVerYB5tHFwm+0ApJ2Jz4p7DJ87l37j76Nmr8ndEjd8af/wk + XAQtskL8P+EPEeafP6BJKskf3xuIIt+/ffPi8YNbV3NPHz9ABLlswSxPN+dRVmbG9IoV + V5brUJLgUDUVAcnVqvmI0eMmes8JWBUeQ+VYcONeyfM3bz/UfCFq/JsTIyhPqET+d4Ek + AZIN9V8/Vb8rL3368Pa13DPH9++M3bAmaN6MKeMdbYebDmauWLGko6kvGfvRmQh0I8Wc + /gaDh4J5dPOctXDFuk3xuw+dOFdw4/7jl2UAx9r6BkaNQEalYmRlSSlJIAmK/Fb3peZ9 + xetnj+5cA0LuT962PiTQ38fDxd6aXLH2xStWnUkRHKjmIsDg0cCIXK26Tp4xLyh0Y1zq + wayzIMcnpeVVBI7ffxI2UjXyNCS//T/mR+ogoSS9bP37RyMg8sNbRpDZR9N2bA5fFTB7 + mvsYesXavw9MQiIgNfdFY086EQHAY68++gNNyNXqpOlzA0PWx6akZ+bks3L8SuGoSI2s + GOUlKVEkILL2c3XlGyDk1YunjuxJiFm3fIEvc8U6yABLOjqRHzhIjUaA4pEUcyztnCZ4 + zlq0KnxL0r7jZy4V3nv8svz9x6/1jSwcpV2jlBjpH0JIMletcNH6vaGOCvLh7SvnTxxM + jdu4ZilcsY6zH2FuYgiTkAhIjX7X2Jn4I8Dg0XCIhbU91FbnBYVtit9zJDv3+t2SF+VV + rBzlLlXl1SgrSeai9dffP4gg4ZL1wc2Cs5kHdm6NDF4828vNeaQllHQQkOJPDxyhZiPA + 4pEUc8a4T5sTGBIVu+vgifNX7jx6XlZV87WeWEeQoxQclaiRHOYhSRBJ6jpEkB8qXj25 + f+PSmWNpiTFrl8+fMZmWdBCQmv2qsTcdiACHR1LMmeK7cEX45qT9GTn5Nx88e1NZ/YVc + rBLrKJRjM2qUU+RvIshvdZ/el78sKbp28eSh1LgNqwPnek+Eks5QBKQO5AcOUaMR4PE4 + auzE6f5L1myM330kO6/w3pNXb6s/K5BjC2qURSQI8m8o6nysKnv+8Ba5Yk3eEr5y4cwp + rg42w4YgIDX6XWNn4o+ABI+2juOnQjEnYuvO9Cy4Wi1+Uf7+U13DD9mL1ZblKFQkMZH/ + g6JO/ZfqytdP4Yr19NE98dGhS/2nTxqLgBR/duAINRwBDo9mVjDX4eO/NGxTwt5jZ/Jv + wNXqu5qv32Tl2Co1ygvyZ2P95w/kivXqhRPpKdsiVi2aNXW8IwJSw182dif6CFA8DjAa + MszWEeY6FgdHQjHn5MVrdx+XVhDz+Pev/wm9Y2vlKCjrACH/JRaSXLFCjTU/53hawqaw + IH+fSeNGo4MUfX7gADUaASEePXz8g8JiEvdl5BTcevgcpjrqGkgtR1DKab0cBYgka3XA + QsIV69tXj+9ev3jy4K5YmPPw85zgaIsOUqPfNnYm9ggw7pHgEZYC+C0OXh+beuhU7nUo + 5lRWw1TH3zDTIamstkmN0oL836+fDaTGSko6ORn7EmMIID0AkGZYYhV7iuD4NBcBaTzO + C1obs2NfxtnLtx/RYg6Z6oCrVXZGsa1ylFyzMles3799ra589eReYe6pQ6mx6xGQmvuW + sSddiYA0HgOC128HPOYV3n/6upIt5kiuVtsuR16QpMb6s5EA8sWj25fPZuzbEbM2aB4C + UlfSBMepmQjI4HEZwWMmg8cPMPX49y+yEKDdeOQvWdmSztcaMuehAJB4m4dmvm/sRdwR + EMw9gnukeDwsxGOHrlYpUBkxk5LOr79hzgMBKe6EwNFpMwLK8AhLATg8csWc9lytkvew + cKWrAn58EwByu5SDREBqMw+wb3FEAPCo10ffkJl7ZPAo5R55PLZXjpwgm5rInEdj/acP + 5S+KJQ4S5yDFkQc4ClFEQBEeSXGVw6NgrqP9emQJKQ9IpsQKj9Khc5AISFGkBA5CixGQ + xiPMPcoUV1WBRyEgqYMEQHIlVlyko8UvH7sWWwQ4PA6lK1f5uUd5PHaAjhILqRiQszy5 + VawISLHlB45HsxEQ4HE8vzSHn3tUDR45QRIHSQBJHCQAUrJIZ9I4vM1Ds9879ibKCEjj + kaxcZZbmcHiEuUe2uNoxPEquWMmiAGGJla5i5W/zQECKMk1wUBqKgASPNuS+R27lqhrw + yAqSAhLuu2IAya9ixfsgNfSFYzdijgCPx1Fj4b5H9sYOunL1wydmaY6K8MgD8l8OkE/u + Xc89eYgHpAV5UAACUsz5gmNTbwRk8Qj3PcKNHWrBIwdIskiHAJIs0oHbPI6nJW4Kg2c/ + 8oDs0V29nxlbxwiINQJkf7l+ZIcAIR4LmBs7yENzVOYeifvkVs3RG5PhNo/XFJDkPkj6 + oAAHGwSkWPMEx6WRCHB4tGDdI8HjSXLfI3tjB7kNmVFRR6s59P20KTrlIQ9IwZN0EJAa + +fKxE9FFoHsPHo/TyVNzEtOOw2MB6H2PDB5ZPapEjgoBSR4UsE0CSPKwcr3/dsfdPESX + Kjgg9UegW3dm5aqFjQN9qFzkNnhqDoPHauYhVirFI+8gufsgnzMOMmGT5FFzdDcPBKT6 + v3vsQXwRYPA4iLpHwGOoEI91wscCqAaP8g4SHhRAn6RDHjU3cyo8i9UCANlb7z8ISPEl + C45I3RGAyQ5yY4eJFB4vsk/Noc94VDEeFQCSPEmHPGqOAHIieRbrIAPYDxIBqe7vHtsX + XwTk8JjAuMfn5BHI6sCjUkDSZ7GSh5VbIyDFlyc4Io1EgOCxN4NH16kz4ZHkxD2qF48K + AQnPYt2bQB9WDrt5WJoiIDXy7WMnYosAwWNfA+oeYccOcI8MHuGZq2rDoxQguUfNXb/I + PqycBeTA/uggxZYrOB61R0CCR2vY0IriMUX9eFQCyGMEkEv8Ybsr2A8SAan27x47EF8E + WDyaWo4cQ/EYnbD3eE4+eSS5GvEoB8i3r57Aw8oBkFsjmO2uwEEiIMWXLTgiNUeA4rH/ + QBMLHo/pJy5ev0v2l/v67TvsZ6Xy4iozayJcpMM+rDw/59je+Og1S8h+kBJA4poANWcA + Ni+mCAjx6O2/JBTweEwDeJQGZP1XupvHNbLdFQtIewSkmNIEx6KZCEjjceHKiK0pgEfY + 0IrgsV6NeJRykLCbB9mgFba7kgPkf3sgIDWTCtiLCCLQjSmuUvfoPXfJmuh4gseb6naP + 5JJVcJsHt90VAeTOrbBhsu8UV3trc3SQIsgQHIIGI8Dj0R6KqwtXhhM8XmDw+EW9eJTo + kezm0VBH9oMUAtKdc5AISA3mA3al3QjAjVZk7lEej2VVsDu5cEMrVS1dFbQjC8jSx3d5 + QE52QUBqNzewd41HgMOjuTXg0ZfgcafG8MhfsEoB8szRPfEbSYmVAeSAvr0QkBrPC+xQ + OxGgeBxA8ehO3ONGcI9nwD0+K2O3Q5ZsaCXAmup+VQjIrPSdW8JXLPRFQGonJ7BXrUVA + gEeXySwes4h7LH1brW73SFTN6ZEH5IOb+RSQqwPneLs7kzlIBKTW0gM71nAE5PG45yjg + 8YGG8MgLEnbXoSXW0sdFVy8wgFxAADkCS6waTgnsTnsRkMbjghXhW3amaxKPvB4FgLwh + AeQ0d2c7S1MjBKT2MgR71mQEeDw6u0+bE7h6Y7yG8cgLUgjI81npyeAgWUAawyrWnvig + AE3mBfallQgAHnv27j/Q2HyEPbhHDo9Xi1j3+LfaVq4KCkJSDrKWzEE+kAbkcASkVpID + O9V4BBg8Gpla2gnxeENz7lFY0mEBWVFaUnSVA+QM6iCNDRCQGs8N7FDjEZDHY3J61nmC + xwpSXNUIHvkLVuogOUCePro7fgOUWKe5OdshIDWeGdihNiLA4XG4nbMbcY8bqHtk8Fir + 7qU5/CUrd8UqBcjMA8mbwUEiILWRGNinNiLA4NGAcY8ziHtk8FiiUTwqBuSl00ekAdkH + F+loI0ewT81FQB6Pu4+eztc4HnlBygNy+XwKSDN0kJpLC+xJOxGQxuP8FeGbkw9kgnvU + NB55PXIO8g2UWAkg4zaEBM72cnOiDhIBqZ00wV41FAGCxz4DjEwl7jFu95HTlwCPb6o+ + atA9SkqsTU1/KCA/V1e8LCm6cp44yHU8IPv1wjlIDWUGdqOFCFA89jMwNiNzjzPmL5fg + 8WVF9We2uMrsaMVXXtT1G1fRAUD+aKj9+A4BqYWEwC61GgEej05uXrMDQzZI8PhO03hk + r1ilAHnnyrnMA0kcIK3AQSIgtZov2LlaI0DwCPuvSvC4bnPSgcxzV+6UaAGPUg6SB2T2 + kdS4KMZB2pI5SHSQas0IbFybEZDFY5QUHn/8+p9673uUvfKlV6wSQH6oeFnMADIGHKQP + LNJBQGozWbBvdUdAgkcrcI8+85drF4+ygKyhDjIv+zABZMBsrwlOtsMQkOrOCWxfexGQ + 4NGWcY9RcalHsmlx9V1NbYPG8ShxkL+ZEisHyP1JMeuWzfPxGDfaymwwOkjt5Qv2rNYI + yOExJmk/cY/FLys+aLq4yly6Ckus32p5QG6PCmYBOcRIHx2kWrMCG9dWBDg8DrN1muA1 + OyAkanvq4ew8MveoJTxKAbKx/vOHcsZBAiDXIiC1lSbYr2YiwOJxsJnV6HEePvOWrZPg + sRzw2Ag3dvxu0tTcI1fZkQfk/RvEQRJA+nkSBznEEAGpmfzAXjQbAYpHfaMhBI+efgHB + UniE7ck1XFwVXLE2NREHyQHy8rnM/TsQkJpNDuxN0xGQxePamB2se9QeHqVLrMRBvn7K + AnI9BaQjA0g9fBarptMF+1NzBBg8GgIeHSke1zPu8f7T1+AetYVHeQf5ovj25bMISDUn + Azav7QgoxOPZy1Bc1SYeZQD5lQKyMO8UOMj1wYvBQSIgtZ042L9aIgB41OujL43HU3k3 + tI1HJYDM2AcOMoiZgxyKc5BqyQhsVIsRkMFjELjHfZlnL98ufqFdPMoBshIcZGHuqUOp + sRSQ4x1taIkVHaQWkwe7VnkEpPG4OBjc46FTeYWAx8qar9pzj6TIyq5ilZRYqYMkgAwL + 8veZBIt0EJAqTwdsULsR4PA4FOYeJ/n4UzxmiAKPzQNy1lQEpHYzB3tXRwQkeLRxHO85 + C/AYC3jMFQUeBYD8xcxBvngEJdaMfYkMIMeOQkCqIyWwTe1FQBaPYeAeCR4fse7xlxaW + 5nBLdJQCcldsZPBiBKT2sgZ7VlcEhHicCniMFBMehYD82Vj/6X05ALIgJyNNFpA9uqsr + PtguRkCDEeDxOGoscY9hMYn7MnIKKB4/wcpV7eJRASCf3CvMPXmQB6QFWcWKJVYNpgx2 + pcYIcHi0APfI4HGXeNwjX2L98/vfXxwgbxXkHE9L3BS21H/6JImDRECqMUmwaY1FoHsP + 8tScwUOtWDxuSkxj8fge8PhT23jkAfnvP79+fPtaXfn6yb3rBJDbIlctAgfpYMMCsns3 + jcUMO8IIqCkCMnhcFRm76+DJ3Ov3nrBzj5rYYI6v3ij8jZmDZABZRx0kC8hQDpCDyIMC + EJBqShFsVoMREOJxuv/SMMDj8ZyCW1Bc5fD4R9P3PcqKkrsPkgPkKwDkRSlAmhjq99bD + /Vk1mDbYlXoi0K07s3LVwsYB3OOiVZHbAI8XKR6rydIcEeCRK7H+IZsHNBJAPn9EAZmw + iQByInGQgwz6IiDVkyHYqiYjwOBxEHWPgMfQTQkMHp8DHuvAPUruQ5aFlib/VgLIlG0R + qxbNnOoKDhIBqcmkwb7UFQGY7NDrrW9oAnh0JXiM4PD4qlI8eFQAyIe38qHEygJyzCgr + UwSkulIE29VgBAge+xpQPE4ULR7lSqxvWQfJAHKKq4O1hcnA/uggNZg42JU6IkDx2J/B + 45SZBI8pjHsUFx6VAPLY3oTo0CX+3hPHjLREQKojP7BNzUaAxaOp5agxFI/RCXuP5+Tf + eigq90icqsBBfoc5SADk3esXT6SnbI1YuXAmAlKzWYO9qSkCDB4HmlhYO7iyeEw/QYqr + r96KyT1KBNkkKLESB0kAuWbJXCEgcU2AmnIFm1V/BCR4HDlmorf/klDA4zFR4lEakPVf + AJCP7167wAPSHh2k+tMFe1BvBKTxuHBlxNYUwOO1uxSP9d/FMffIzatwi3SYOciqsucP + bxJAxksDEh/9qN6UwdbVGIFuTHHV1JLgce6SNdHxBI83xeceJRes/0c2MP/1XQDInVvD + Vy70neJqb22OJVY15go2rfYIcHg0t7YH97hwZTjB44Vrdx+De/wiNjxKKjpkkU5D3ceq + smcsIDcSB+nOlVgRkGrPG+xAPRGAleRk7pHg0V0Kj2VVn+oaRLI0h7tclXeQpYyDZAAJ + +7MiINWTJtiqZiIgxONkX4LHnSLGI69HHpAPbuafObonXgDIAX17ISA1kz7Yi4ojQPE4 + gMfjRtY9Piur+iiFx//jGaXV34RzkMRBUkBmpe/cEr5igS8CUsXpgc1pNgICPLpweMwi + 7rFUjO5RWNGRB+TqwDnT3J3JIh0EpGazCHtTVQR4PDqDewxcszF+z9Ez+TcfiBaP/BUr + 3cCcArLo6gUBIEeYG8Mq1p54H6SqcgTb0VgEJHgcYQ94XLAifMvOdMBjEYtH2H+VvQ1Z + LFerQEjuglUAyBuMg2QAaWdpaoSA1FgKYUcqjIAEj3bO7tPmBK7WATzygmQBWVH6uOjq + +az0ZM5BIiBVmCHYlAYjAHjs2bv/QGNzKTxeFTUeeT1SQNaSOcgHDCA3EEC6OdsNR0Bq + MImwK5VFgMGjkelwKTzeoO6xVjj3KKLLVcEVqwSQJTwgZ0CJFQBpgA5SZVmCDWkoAgwe + DRg8ziDuMTk96zzBYwVZmiNK9yhXYuUAefro7ngEpIYyB7tRRwQEeHQj7nFD/G4orooe + j/wVqxQgMw8kbw5fPh8BqY5UwTbVHwE5PG5OPpAJeCwROR55PXIO8g04yEunj+yO2xDC + O8g+uEhH/TmEPaguAgrweOT0JR3AIyvIJnKbx9/kNo+KlyVFV84LAWlmbNAP5yBVlyvY + ktojwOHRjBRXZ8xfHq4zeJQD5DshIGd7uTnREisCUu05hB2oLgIEj30G0OKqm9fswJAN + cbsZPL6p+iji4iop6MgA8jMB5J0r5zIPJG1exzpIAsheuEhHdemCLak3AgSPsIOOsTQe + rxSVvKyo/swWV7W9RQCjPUX/L1ik86Oh9iMPyKiQQAJIWzIHiYBUbwph6yqMAI9HJxk8 + vgM8/uAfSS6uuUdOnMyDO1gHKQNIH5iDtEJAqjBZsCl1R0CCRyvGPa7bnHQg89yVO7qB + RykHCYCsYQCZfSQ1jgJyAgJS3QmE7as0AhI82jJ4jIpLPZJNiqtvdAKPsg7yQ8XLYuIg + 9yfFgIP08SCAHIwOUqUpg42pLwJSePSZv3xdDIPHYp1wj+SiVdpBMoDMyz6cuj0qJGC2 + FwBymKmRPjpI9aUQtqzCCAjwOMFrdkAI4PFwdh7FY40OuEeJIJuafsMcZGP95w/lPCCX + zfPxGDcaAanCfMGm1BoBFo+DzaxGj/PwmbcM8LifuEfA4wemuPq7SbzFVaamIwTkN9ZB + MoAMDvDzJIAcYoiAVGsaYeMqigDFo77RkGG2TgSPwVHbBXiE/Vcl+z2Ks7gqEKQ8IHfE + rEVAqihPsBmNREAWj2sleCwHPDbCjR3ix6O0gySAfP30/g0KyPUUkI4MIPXwUXMaySns + pP0RYPBoSPHo6RcQvJ7B4/1nr9/V1OoKHiUlVqGDvHw2cz8Csv2Zge/UQgTk8bgD3ONl + cI+6hEcZQH6lgCzMOwUlVgSkFrIKu2xvBACPen30CR4dJwjx+FS38CgPyBfFty+fzdgH + gAxiSqxDcQ6yvUmC79NUBBTgcV/m2cu3i1/oFh7lAFkJDrIw99Sh1Nj1wYv9PMc72tAS + KzpITWUW9tOeCEjjcTFxj4dO5RXeJ3j8qjvuUdEcpASQYUH+PpNgDhIB2Z4MwfdoMAIc + Hocyc49Ba2N27MvQSTw2D8hZUxGQGkwr7KqdEZDg0cZxvKcf4DEW8JhL8Fipa3gUOMhf + zCKdF4+og0yMoYAcOwoB2c4swbdpKAJCPE7y8Q8K02E8KgXkrtjI4MUISA2lFHbTgQgI + 8Th11uLgSF3GowwgP70vB0AW5GSkyQKyR/cOhAzfihFQVwR4PI4ay+AxcV9GzuXbj6C4 + +gmW5vzSiaU5zII5SUUHNjCHR839+Pa1pvL1k3uFuScP8oC0IKtYscSqrnzCdjsWAQ6P + FuAeGTzu0l33KBEk6PHfXz8b6zlAHk9L3BS21H/6JImDREB2LG/w3eqJQPce5Kk5g4da + SfCYlpFTQPD4HvD4U9fwyDvIf/8hgKwmgLxOALktctUicJAONiwgu3dTT0CxVYxA+yOg + AI8HT+Zev/dEF4urzFUr8yQdBpB1AMjnj24V5BBAhnKAHEQeFICAbH/W4DvVFQEhHqf7 + Lw3blJh2PKfglgCP7IaPvEMT+2/cfZAcIF8BIC9KAdLEUL+3Hu7Pqq6cwnbbHYFu3enK + VRMLGwdwj4tWRW7bdfDkRYrHarI0R4z7r7b47wEHyP+BgxQAMoECciJxkIMM+iIg2500 + +Ea1RYDB4yDqHgGPoZsSGDw+B/dYB+5Rch9yiyIQ0QlKAJmyLWLVoplTXcFBmhj2R0Cq + Lamw4fZGACY79HrrGxI8uhI8RnB4fFWpu3jk5iDJlAcLyIe38sFBsoAcM8rKFAHZ3pTB + 96kxAgSPfQ0oHid2GjzKlVjfsg6SAeQUVwdrC5OBCEg15hU23a4IUDz2H2hiYe3gOmUm + wWMK4x51G49KAHlsb0J06BJ/74ljRloiINuVMPgmtUaAxaOp5cgxFI/RCXuP5+TfeqjT + 7pEYWYGD/A5zkADIu9cvnkhP2RqxcuFMBKRakwobb28EFOAx/QQprr56q8vuUSJIumhO + 4CCP7Y2PXrNkrhCQuCagvcmD71N5BIR49PZfEgp4PNYp8CgNSNif9e2rx3evXeABaW9t + jg5S5fmEDXYoAjwe7cE9LlwZsTUF8HjtLsVj/XfdnHvkpl0Ec5ANdZ+qyp4/vJmfIwdI + fPRjhzII36zKCMBSOVJcpe7Re+6SNdHxBI83dd89Si5Y4TYPWKRDNjDnALlza/jKhb5T + XBGQqswkbEsFEeDwaG4NePRduDKc4PHCtbuPwT1+0XU8Sio6ZA6yoe5jVdkzFpAbiYN0 + pyXWAX17ISBVkEnYhCoiIMCjuxQey6o+1TXo6NIc7nJV3kGWEgeZlc4AEvZnRQepiiTC + NlQVASEeJ1M87uxEeOT1yAPywc38M0f3xDOAdCZzkAhIVWUTttPRCFA8DqDukeJxY/ye + Y2fAPT4rq/oohUcx76DD41D+N+EcJHGQHCC3hK9Y4IuA7Gj+4PtVGgEBHl0AjyvCt+xM + zyLusbQzuEdhRUcekKsD50xzR0CqNJ+wsY5FgMejs7v3nMA1gMejgMcHnQaP/BUr7M9K + S6ylj4uuEgfJAXKEuTGsYu2J90F2LJPw3SqIAOCxZ+/+A43NR9gDHhdweCxi8QgbzLG3 + Ievq1SoQkrtgpYCsJSXWBzcYB8kA0m64qRE6SBUkEzbR4QgweDQytbRzdp82J3B1J8Qj + L0gWkBUEkOez0pMJIGeAg0RAdjiPsAGVREAxHq92KjzyepQD5AYCSDdnBKRKkgkb6XAE + ODwOt3N24/F4g7rHWuHcow5frgquWCWALAFAZkoB0gAdZIezCRvoYAQYPBow7nEGcY/J + 6VnnrxaVlFaQpTmdwj3KlVg5B3n6yO54BGQHMwjfrsoIyOJxQ/zuo6fzOx0e+StWaUAe + SN4cvnw+dZBmxghIVWYWttWOCMjhcXPygcxOiEdej5yDfAMl1ksAyLgNIYFzvFgH2QdX + sbYjifAtKosAwWOfAUamnHvcELf7yOlLnRCPrCCbyG0edA6y4mVJ0ZXzmdKA7IdzkCpL + LWyo7RGgeOxnYGxG5h5nzF8ezuHxZedyjwoc5DshIGd7uTnREisCsu1JhO9QWQR4PDq5 + ec0ODJHg8c27j52ouErkKAPIz9UAyDtXzmUeSNq8jnGQVuAgYfMAXKSjsvTChtoWAYJH + 2EFHgsd1BI/nrhSVAB4/s8XVJrq2hclo3f5/wSKdHw21HzlApsZFhQQSQNqSRToIyLal + EJ6twgjI4jGKc48Ujz/4R5Lr9twj988IFSTnIAkgixlAxgAgfWCRDgJShbmFTbU5AhI8 + WoF79Jm/fN3mJILHO50Tj1IlVgBkDQPI7MMEkAGzvSYgINucQfgGVUZAgkdbpwnEPUbF + pR7JJsXVN+9qahs6HR4lDvI3LbF+/sACcn9SzLpl83w8xo22MhuMDlKVGYZttSECLB4H + mwEePQgeY5L2EzwWv6z40PncI7loFTrIbywg8wCQ26OCGUAOG2Kkjw6yDTmEp6ouAhSP + +kamwygeA0Kitqcezs7rxHiUAmRj/ecP5YyDBECuRUCqLrGwpfZEgMfj6HEePvOWSeOx + EVau/m7qPMVVpqYjD8j7Nygg1wcH+HmCgxw2xBAB2Z5swvd0NAIMHg2HEDx6+gUES+ER + 9l+V7PfYOYqrAkE2NREHyQHy8rnM/TsQkB1NJ3x/xyIgi8e1MTtY91gO7rFz4lHeQb5+ + KgVIRwaQevgs1o4lF767zREAPOr10Sd4dKR4XM+4x/tPX0NxtbPiUd5Bvii+ffls5j4E + ZJsTCN+gyggoxOPZy1Bc7cx4lAHk15p3AMjCvFOHUrevD14MDhIBqcokw7ZaHQFFeDyV + V9jZ8agEkBkEkEH+dA5yKM5BtjqL8EQVRYDD41ArWlwNAve4L/Ps5dvFLzo3HuUAWUkA + mQuAjKWAHO9oQ0us6CBVlGjYTKsiIMDjeE+/xcHrY1MPncoleKys+dp53aNkUYCgxPri + EThIAsgwAOQkWKSDgGxVCuFJqouAEI+TfPyDwgCPGV0Cj80AMjJ48aypCEjVZRm21NoI + SPBo4zh+6qwuhUeBg/wFc5CfPpQDIAtyMvYlMoAcOwoB2do0wvNUEwEej6PGMnhMpHh8 + xLrHX51waQ6zHkBywQr7s8KTdH58+1rDOchdsRwgLcgiHXSQqsk1bKXlCMjiMTJ2V1dx + jxJBEgf56ycA8j0LyLTETdRBSgDZo3vLkcQzMAIdj0D3HuSxAIOHWknwmJaRU3Ab8Pj+ + EyzN6dx4FDjIf1hAPrl3PffkQQRkxzMLW2hHBDg8WrDuEfB48GRu4b0uUFxlrlqZBwX8 + oYCso4C8VZBznAByqf/0SQSQg8h9kAjIdiQXvqXNERDicbr/0rBNiWnHeTz+7Ox45AH5 + LwVkdeVrAORFAOS2yFWLoMTqYGNhQh1k925tji2+ASPQxgh0686sXLWwcYDi6qJVDB6v + 33vyurKazD12hg3m+OqNwt84QP4PHCQB5PNHFJAJm0IRkG3MJjy9oxFg8DiIukfAYyiD + x1vUPdY1EjyyGz4qTOVOcZC7D5ID5CsOkBGrFs2c6soAsrcePvqxo7mG728xAjDZoddb + 39BEgsdt4B4vAh5fdRk8cnOQZMpDASAnMg6yLzrIFpMJT+hwBAge+xpQPE6keEwg7vHW + o+dQXCV4lNyH3ClIqORDKAFkyjYCyCmuDtYWJrCBOQKyw9mGDbQQAQEeXafOXLQqYltK + 18OjAkA+vJWfc3xvQjRxkBPHjLQ0HWSAgGwhl/DljkeAxaOp1agxPB7zbz3sUniUK7G+ + pQ7yRDoAciUCsuNZhi20MgIUj/0HmlhYO7hOkcLj265SXGUuYeVKrASQxwggl8z1RkC2 + Mp3wtA5GgMOj5cgxE739l4RGJ+w9ltP18CgFyO/1X6vfvnp899pFAOTWiJULwUHao4Ps + YKbh21sRAWk8LlwZsTUl/cTF63efvCJ4/N4V5h65Co88IG8SQMZHrxECEp9s1YqswlPa + G4FuTHHVlOJx7pI10fFdFI8ygPzCAPLCifSdW8NXLvQlgDTHEmt70wzf17oI8Hi0B/fI + 4vHCtbuPAY9f6rsUHqVKrA11n6rKnj8UANKdK7EiIFuXWnhWOyIAK8nJ3KMMHm8+fF5W + 1XXmHqUuWOE+SFik871eFpCTEZDtyC98S5siwOHR3Brw6LtwZfjWneknuioehbvr/Gyo + +1hV9gwAeebYnviNxEEygBzQtxcCsk05hie3PgIUjwMoHt29wT1upO7x5sNnZVUf6xq6 + yNIcDo/yDrIUSqwXstJ3bglfsdAX9mdFB9n61MIz2x4BAR5dJrN4zCJ4LO2C7hFkyS2a + I6tYGUA+AEAeBUCuDpzj7e5MFukgINueZ/iO1kVAHo97jp7Jv/lADo+daQcdHofyv3GC + pPuzgoMsfVzEAXIBAeQIc2NYxdoTb/NoXX7hWW2KgDQeF6wI37IzvSvjsQVATnN3trM0 + NUJAtinJ8ORWR4DHo7P7tDmBqzfGd3E88oIUAPLqecZBIiBbnVh4YnsiAHjs2bv/QGPz + EfbgHjk8Xi1i3SPsv8rehtxVrlZlHWQtKbE+uCFxkNPcnO2GIyDbk2v4npYjwODRyNTS + TohH2J5ctrjahfQoKemwgKwoLSkigEyGEuuCGYyDNEAH2XJy4RltjYA8HpPTs84TPFaQ + pTldEo/8BSstsXKAPH10d/wGKLEiINuaZHh+qyPA4XG4nbMbcY8bqHtk8FgrnHvsSnjk + BSkFyMwDyZvDV8xHQLY6u/DENkaAwaMB4x5nEPeYnJ4JeCzp0njk9SgE5KXTR3bHCQHZ + BxfptDHd8PQWIiCPx91HTud3eTzyguQA+ZI4SArI5QwgzYwN+uEcZAvphS+3LQIcHs1I + cXXG/OXhm5MPIB7JCgFuTQALyHdvoMTKADIkcLaXmxMtsSIg25ZueHYLESB47DPAyJS4 + R685gSEb4gCPlwCPb6o+dmH3KBFkE7nN4+/v9Z+rK16W3LlyjgBynQCQvXCRTgsZhi+3 + IQIUj/0MjKXxeKWo5CVfXG2ipCAZ2rV+BID80VD7kQdkFAKyDSmGp7YhAjwendy8Zgvx + +K6r45G9YpUHZBIBpA/MQVoRB4mAbEO64anNR4DgETaYMzazYtzjus1JBzLPXblD8PiZ + nXvsqniUcpAAyBoGkNlHUuMoICc42ZJFOuggm08xfLUNEZDg0ZbBY1RcKuseCR5/8I8k + 71pzj9x1Ob1ilQDyQ8XLYuIg9yfFEEB6EEAORkC2Id3w1OYjIIVHn/nL18UweCxGPFJJ + SjtIBpB52YdTt0eFBMz2AkAOMzXSR0A2n2T4aqsjIMDjBK/ZASGAx8PZtLj6rgbxCJJk + AfkbSqyN9Z8/lPOAXDbPx2PcaARkq3MNT2wxAiweB5tZjR7n4TNvGeBxP3GPgMcPjHv8 + 3dR13SMhpBCQ31gHyQAyOMDPkwByCOzPiot0Wkw1PKEVEaB41DcaMszWieAxOGo74DGP + zD0CHmH/VcmGVl3TPUoE2dQkC8gdMWsRkK3IMDylDRGQxeNaCR7LAY+NcGNHV8ejPCBf + P71/gwJyPQWkIwNIPXzUXBvyDk9VHAEGj4YUj55+AcHrGTzef/Ya8UjYSH8UOMjLZzP3 + IyAV5xQebXcE5PG4A9zjZXCPiEdOjTKA/FrzDgBZmHcKSqwIyHZnHr5RUQQAj3p99Ake + HScI8fgU8cjLUb7E+qL49uWzGfsAkEFMiXUozkEqSi881rYIcHgcyhZX18bs2Jd59vLt + 4heIRzk9wuYBcJvHj29fayoJIHNPHUqNXR+82M9zvKMNLbGig2xb9uHZshGQxuNi4h4P + ncorvE/w+BWLq7wkZR2kBJBhQf4+k2AOEgEpm1v4d5sjII1H/yCCxwzEIy9DyW9Sc5Ay + gJw1FQHZ5tTDNyiIgASPNo7jPf0Aj7GAx1yCx0rEo0SL9BcJIH8xi3RePKIOMjGGAnLs + KASkgvzCQ22KAI/HUeMm+fgHhbF4fITuUVqM8JcSQO6KjQxejIBsU9rhyUoiIMTj1FmL + gyMRj3I6lByQAuSn9+UAyIKcjDRZQPboriTYeBgj0EIEmPseBw+1GjWWwWPivoycgtsE + j59gac4vXJojUaMCQD65V5h78iAPSAuyihVLrC3kHL6sNAIcHi3APTJ43IXuUaBAmV8Z + QP75/e+vn431HCCPpyVuClvqP32SxEEiIJUmHL7QbAS69yCPBWDwOB3c46bENBaP7wGP + PxGP0oLkHCRsYA5zkNWVr5/cu04AuS1y1SJwkA42LCC7d2s26vgiRkBhBLp1Z5bmCPB4 + 8GTu9XtP2OLqP7+73g460gqU+UsIyDoA5PNHtwpyCCBDOUAOIg8KQEAqTDc82EIEGDwO + ou5xuv9SgsfjOQW3wD2+/1RH8cjqUSYtu+yfsoB8BYC8SAAZQQDpCoA0MdTvrYf7s7aQ + ePiyogjAZAdZuWpiYeMA7nHRqshtuw6evEjxWE2W5nTFDeZa+KeGAyRsYN4oAGQCBeRE + 4iAHGfRFQCrKNjzWUgRk8Bi6KYHB43MWj5L7kFtI0i70shJAphBAzpzCALI/ArKlzMPX + FUSA4LE3g0fXqTMXrYrg8PiqEvGo7N8YOUA+vJWfc3wvC8gxoyxNEZAKcg0PtRwBgse+ + BsQ9jpkI7pHD40PEozIxwnEZQL5lHGS6BJDWFiYDEZAtJx+eIRsBisf+A00srB1cp1A8 + pjDuEfHYjB7ZR82R265YB0kAeWxvQnToEn/viWNGIiBlEw3/blUEWDyaWo4cM9Eb8Bid + sPd4Tv4txGNzapQC5HeYgwRA3r1+8UR6ytaIlQvBQdojIFuVfXiSTARk8LgyYmtK+glS + XH31Ft1jc5JU6CCP7Y2PXrNkrhCQuCZAJuPwz+Yi0I1xjwwe5y4heDyGeGxOiOxrAgf5 + vf4LAPLx3WsXCCDDWUCao4NsLvHwNUUR4PFoD+5xIYvHa3cfEzzWf8e5R+XKFACyoe5T + VdnzhzeJg2QA6c45SHz0o6K0w2NKIgAryUlxlcPjmuh4gsebD5+XMUtzcO5RqSCVAHIn + AaTvZHCQCEglSYeHlUWAw6O5NeDRd+HK8K07009cYPD4BfGoVIvkBU6PdAPzuo9VZc9Y + QG4kDpIB5IC+uHmAstzD4/IREODR3XvuEgken5VVfapr+MlvEdBsZnbRFzlBwm0e1EGW + EgeZlc4C0gUBKZ9veKTZCAjw6DJZiMfSt9WIx5b+meH0yAPywc38M0f3xG9cEzjH292Z + zEEiIJtNQHxRKgIUjwOoe6R43Bi/5+gZcI+Ax49SeOy6O+g0J0pOkLD9HAvIIgrILeEr + FvjCBuYjqIPsibd5SCUd/qEsAjJ4XBG+ZWd6FnGPiMfmZCh5jdOjPCBXB86Z5u5sZwkb + mKODVJZ+eFwmAjwend295wSuZvH4APEokVzzv3CCFADyKnGQPCCNYRUrAlIm7/BPhREA + PPbs3X+gsfkIe3CPC1g8Xi1i8QgbzOFjAVqnRwrIWlJifXCDcZAEkG7OdsMRkApTDw8q + iACDRyNTSztn92kSPML+q+gem5ch/6oMICtKHxddPZ+VnkwAOYM6SGMDBKSC3MNDchGQ + x2NyetZ5gscKUlxFPPKyU/obp0cpQJ4+ujt+AwJSLuHwQLMR4PA43M7ZTRaPtcK5Ryyu + KtWjZFEA6yArSksAkJkHkjcjIJtNPnxRNgIMHg0Y9ziDuEcGjyWIR+Xyk3tFESAvnT6y + O04IyD64SEc2+/Bv2QjI4nFD/O6jp/MZ94h4lBOesgOcIOUBuXw+dZBmxgb9sMQqm334 + t0wEpPE4f3n45uQDmeAeEY/KlKf4OKdHzkG+gRIrA8iQwNlebk60xIqAlMk+/FM2AgSP + fQYYmbLuMWRD3O4jpy8BHt9UfUQ8KtaewqNUkE1NfyggP1dXvCwpukId5DoBIHvhHKRs + AuLfwghQPPYzMDYjc48zODxeKSp5icVVhbJTelAAyB8NtR/fISCFeYa/ty4CPB6d3Lxm + Bwrw+A7xqFR7Cl+QB+SdK+cyDyRtZgFpRRwkArJ1edlFzyJ4hB10JHhcR9zjuSt3CB4/ + s3OPTTTRFKYgHhREQCEgs4+kxkUxDtKWLNJBB9lFlda6jy3Boy2DxyiJeyR4/MHf94hz + jwLlKflVDpDFBJD7k2IAkD4eLvZWZoMRkK3Lyy56lgSPVuAefeYvXxeThHhUoraWD0sD + soY6yLzsw6nbo0ICZntNcLIdZmqkj4DsolprzccW4HECcY9RcalHsmlx9V0N4rFlBcqc + wQLyN5RYG+s/fyh/KQHksnk+HuNGIyBbk5Rd9xwWj4PNrEa7ePjMWwZ43E/cY/HLig/o + HmXE1oo/hYD8VisEZHCAnycBJNnAHBfpdF3FNf/JKR71jUyH2ToBHgOCo7anHs7OI3OP + gEfYYE7yUDl0j61QI5yiBJA7YtYiIJtPRXz1r78EeBwnjcdywGMj3NjxuwmLq61TInOW + LCBfP71/gzrI9RSQjgwg9fBZrCg/BRFg8Gg4hODR0w/x2BblKTlXASAvn83cj4BUkH54 + SDoCsnhcG7ODdY+IRyVya/GwFCC/1rwDQBbmnYISKwJSOvnwL7kIAB7J9uSAR0eKx/WM + e7z/9DW6xxaFp+wEWUC+KL4NgNwHgAxiSqxDcQ5SLhPxAERAIR7PXr5T/ALxqExtLR+X + AWQlAWTuqUOpseuDF/t5jne0oSVWdJAoQdkISONxcTDB46m8QorHr1hcbVl6is9QCMgM + AGRYkL/PJJiDREDKZiL+DRHg8DjUajQprgaBe9yXcfbybcSjYp219mhzgJw1FQGJ4lMc + AQkebRzHe/oBHmNTD53KJXisrEE8tlZ9Cs6TAPIXs0jnxSNwkBn7EhlAjh2FgFSckF37 + qBCPk3z8g8IQjwq01Z5DSgC5KzYyeDECsmurTvmnF+Jx6qzFwZGIx/aIT9F7pAD56X05 + ALIgJyNNFpA9uiv/dvCVLhYBHo+jxjJ4TKTu8RFbXP2FS3MUSa1Vx+QBea8w9+RBHpAW + ZBUrlli7mOSa/bgcHi3APTJ43IXusVVqa8VJPCB/NtZzgDyelrgpbKn/9EkSB4mAbDZF + u9SL3XuQxwIMHmolwWNaRk7BbcDj+0/1jT8Rj62QndJTOEDC/qw/vn2trnz95N51Asht + kasWgYN0sGEB2b1bl8o5/LBKI6AAjwdP5hbee4LFVaUqa8MLDCD//P7318/GOgrIWwU5 + BJChHCAHkQcFICCVJmgXe0GIx+n+S8M2JaYdzym4JcAju6NVG5IQT5VEQBaQrwCQFwkg + IzhAmhjq99bD/Vm7mOyUfdxu3enKVRMLGwdwj4tWRW7bdfDkxesEj9Vk7hE3mJNIq32/ + cID8HwvI548oIBMoICcSBznIoC8CUll+drXjDB4HUfcIeAzdlMDg8Tm4xzrqHhGP7RMi + +y4lgEwhgJw51RUcpIlhfwRkV9Odks8Lkx16vfUNCR5dp85ctCqCw+MrxGOHZMi/WQ6Q + D2/l5xzfywJyzChLUwSkkuzseocJHvsaUDxOVIRHyWM6+PzC39oWARlAvmUcZDoDyCmu + DtYWJrCBOTrIric++U9M8dh/oImFtYPrFIrHFMY9Ih7bJrrmzlYIyGN7E6JDl/h7Txwz + EgEpn5hd9AiLR1PLkWMoHqMT9h7Pyb/1kHOPiMfmhNbK1wSA/A5zkADIu9cvnkhP2Rqx + cuFMBGQXlZ6ijy2Dx5UR21LST5Di6qu3WFxtpdpacZoSQMZHr1kyVwhIXBOgKEm70DEh + Hr3nLgkFPB5DPLZCYW07RQjI+i8AyMd3r10ggAyngLS3NkcH2YVUp/Sj8ni0B/e4cGXE + VoLHa3cfEzzWf8e5x7bJTvnZ0oCsKnv+8GZ+zrG9DCDdOQeJj35Umqld4wVYKkeKq9Q9 + Ah7XRMcTPN5E96hcWe17RQkgdxJA+k52RUB2Db218Ck5PJpbAx59F64M37oz/cQFBo9f + EI/tk57idwkA2VD3sarsGQvIjcRBMoAc0Bc3D2ghYTv5ywI8ukvhsazqU13DT36LAMU5 + hkdbHwFZQJYSB5mVzgLSBQHZyaXWmo8nxONkxGPrxdWOMzk9/oZVrAwgH9zMP3N0T/zG + NYEASGcyB4mAbE3Wdt5zKB4HUPdI8bgxfs+xM+Aen5VVfZTCI+6g0w4FyryFEyRsP/ed + lFhLHxdRQG4JX7HAd7KL/QgssXZepbXqkwnw6AJ4XBG+ZWd6FnGPpW+r0T3KyKnDf3J6 + lAfk6sA509yd7RCQrcraznsSj0dnd+85gasBj0cBjw8Qjx0Wn6IGOEEKAHmVOEgekMaw + irUn3gfZeRXX7CcDPPbs3X+gsfkI2J7cdwGHxyIWj7DBHHufFV6tKpJXm49xeqSArCUl + 1gc3GAfJAnK4qRE6yGZTtlO/yODRyNTSztl9GuKxzfpq8xs4QbKArAAHefV8VnoyAeQM + 6iCNDRCQnVpzzXw4eTwmp2edv4p4bLPOWvsGTo8ygNwdv4EA0s3ZDgHZTL529pc4PA63 + c3bj8Qjbk0NxtVY494iXq60VXEvncYKUALIEAJmZnrwZAdnZ1dbi52PwaMC4xxnEPTJ4 + LCmtIMVVdI8tiasdr3N6FALy0ukj0oDsg4t0WkzeTniCLB43xO8+ejof8dgOmbX+LZwg + pQF5AAC5fD51kGbGBv2wxNoJ5dbSR5LG4/wV4ZuTD2SCe0Q8tl5dbT+T0yMHyDdQYiWA + jNsQEjjbi3WQCMiWkrcTvk7w2GeAkSnnHjfE7T5y+hLg8Q26x7brrNXvoIJsavpDAfm5 + uuJlSdGV85kAyHUCQPbCOchOqLhmPxLFYz8DYzMy9zhj/nIJHl+ie2y1uNpxogCQPxpq + P76TAaQTLbEiIJvN3c74Io9HJzev2YEhPB7fYXG1HTpr9VvkAXnnyrnMA0kcIK2Ig0RA + dkbNNfOZCB5hBx0JHtcR93juyp0SwONntrjaRFOn1YmGJ7YqAgoBmX0kNS6KOkgnWzIH + iYBsJnc740uyeIySuEeCxx/8fY8499gqlbXhJDlAFhNA7k+KAQfpA4t0rMwGIyA7o+aa + +UwSPFqBe/SZv3zd5iTEYxs01ZFTpQFZQx1kXvbh1O1RIQGzvSY42Q4zNdJHQDaTvZ3v + JQkebZ0mEPcYFZd6JJsWV9/VIB47orZWvJcF5G8osTbWf/5Q8VICyGXzfDzGjUZAdj7B + Nf+JWDwONrMa7eJB8BiTtJ+4x+KXFR/QPbZCUh06RQjIb7VCQAYH+HkSQJINzHGRTvM5 + 3JlepXjUNzIdRvEYEBK1PfVwdh6ZewQ8wgZzkkeSo3vskPKUvFkGkOUSQK5FQHYmmbX2 + swjwOM7DZ94yHo/lgMdGWLn6uwmLq0rEpILDsoB8/fT+Deog11NAOjKA1MNnsbY2oXX8 + PAaPhkMIHj39AoIRjyoQWVuaUADIy2cz9++IQUDquLTaM3xZPK6N2cG6R8RjW1TV/nMV + ALIw7xSUWBGQ7UloHX8P4FGvjz7BoyPF43rGPd5/+hrdY/s11qZ3ygLyRfFtAOQ+AGQQ + U2IdinOQOq6yVg9fIR7PXr5T/ALx2CZRdeBkKUB+rakEB1mYe+pQauz64MV+nuPRQbY6 + m3X/REV4PJVXSPH4FYurHVBZG96qEJAZFJD+PpNgDhIBqftKa9Un4PA41Go0Ka4GgXvc + l3H28m3EYxvk1OFTmwPkrKnjHW3oHCSWWFuV0jp9kgSPNo7jPf0WB6+PTT10KpfgsbIG + 8dhhobW2AQkgf9FFOuUvHoGDzNiXGBMWBIAcOwoBqdMqa/XghXic5OMfFIZ4bK2EVHqe + EkDuio0MXoyAbHU66/yJQjxOnYV4VKnI2tKYFCA/vSeALMiRB2SP7jqfcvgBlEeAx+Oo + sQweE6l7fMQWV3/h0py2iKoD58oD8l5h7slDPCAtyCpWdJDKc7kzvMLh0QLcI8FjZOwu + dI8dUFUH3soD8mdjPQfI42mJm8KW+k/nHSQCsjPoTtln6N6DPBZg8FArCR7TMnIKbgMe + 33+qb/yJeOyAwNr4Vg6Q//7z68e3r9WVr5/cu5578uCubZGrFoGDdLBhAdm9m7IvE4/r + egQU4PHgydzCe0+wuNpGNangdAaQf37/++tnYx0A8vmjWwU5BJChHCAHkQcFICB1XXXK + xy/E43T/pWGbEtOO5xTcEuCR3dFKBemGTbQQAVlAvgJAXpQCpImhfm893H5OeT7r+Cvd + utOVqyYWNg7gHhetity26+DJi9cJHqvJ3CNuMNeChFT7MgdI2MBcCMgECsiJZA5ykEFf + BKSOi66Z4TN4HETdI+AxdFMCg8fn4B7rqHtEPKpWcs22pgSQKdsiVi2aOdUVHKSJYX8E + ZDMJrdsvwWSHXm99Q4JHV4LHCA6PrxCPzQpHXS/KAfLhrXxwkCwgx4yyMkVA6rbkmh09 + wWNfA4rHiYrwKHlMh7ryD9uVjoAMIN+yDpIB5BRXB2sLE9jAHB1ks1mtsy9SPPYfaGJh + 7eA6ZSbBYwrjHhGP0jLR3F8KAXlsb0J06BJ/74ljRloiIHVWbi0OnMWjqeXIMRSP0Ql7 + j+fk33rIuUfEo+aEyPYkAOR3mIMEQN69fvFEesrWiJULZyIgW0xpXT5BAR7TT5Di6qu3 + WFzVuBKFgmz6Q7afY0qsxEEe2xsfvWbJXCEgcU2ALktP4diFePSeuyQU8HgM8agtIQr1 + +H9k+7lf3+u/ACAf3712gQAynALS3tocHaTCbNb5gzwe7cE9LlwZsTUF8Hjt7mOCx/rv + OPeoHWUKHGRD3aeqsucPb/KAdOccJD76Uef1J/sBYKkcKa5S9wh4XBMdT/B4E92jdnTI + 9Sp0kAJA7iSA9J3iioCUzeNO8jeHR3NrwKPvwpXhBI8XGDx+QTxy8tD8f6UA+bGq7BkL + yI3EQTKAHNAXNw/oJDKUfAwBHt2l8FhW9amu4Se/RYDmM7Jr9ygLyFLiILPSGUDC9nPo + ICU53Il+EeJxMsXjTsSjKP4h4PRISqwNdQSQD27mnzm6J37jmkAApDOZg0RAdiIp0o9C + 8TiAukeKx43xe46dAff4rKzqoxQecQcdTauUEyRXYuUAuSV8xQJfAOQILLF2NjX+JcCj + C+BxRfiWnelZxD2Wvq1G96hpBUr3x+lRHpCrA+dMc3e2s4QNzNFBdipN8nh0dveeE7gG + 8HgU8PgA8SitDa38xQkS9melc5Clj4uuEgfJA9IYVrH2xPsgO40kAY89e/cfaGw+ArYn + 913A4bGIxSNsMMfeZ4VXq1pQJKdHCsha6iBvMA6SBeRwBGSnkSL9IAwejUwt7Zzdp80J + XI141ILslHfJCZIFZAUB5Pms9GQCyBnUQSIgO5Mg5fGYnJ51/iriUblENPoKp0cZQO6O + 30AA6eZsh4DsTHL8i8PjcDtnNx6PsD05FFdrhXOPeLmqUR1KOuMEKQFkCQAyMz15Mw9I + A3SQnUWTDB4NGPc4g7hHBo8lpRWkuIruUaILbf3C6VEIyEunj0gDsg8u0ukcipTF44b4 + 3UdP5yMetaU+Bf1ygpQG5AEA5PL51EGaGRv0wxJrp9CjNB7nLw/fnHwgE9wj4lGBMLR0 + iNMjB8g3zx7cIICM2xASONvLzYk6SARk59Aj3NjRZ4CRKeseQzbE7T5y+hLg8Q26Ry3J + T75bKsgmch8kzEF+rq54WVJ05XwmAHKdAJC9cA5S9xVJ8djPwNiMzD3O4PB4pajkJbpH + eV1o64gMIN8hIHVfeYo/AXGPDB6d3LxmBwrw+A6Lq9qSn3y/8oC8c+Vc5oEkDpBWxEEi + IBXnuA4dJXiEHXQkeFxH3OO5K3cIHj+zxdUmmgzyOYJHNBcBASB/NNR+ZACZfSQ1Lopx + kLZkDhIdpA4pT/FQZfEYJXGPBI8/+Psece5Rc+JT1JMcIIsJIPcnxYCD9PFwsbcyG4yA + VJzjOnRUgkcrcI8+85ev25yEeFQkB60fkwZkDQVkXvbh1O1RIQGzvSY42Q4zNdJHQOqQ + 9hQNVYJHW6cJxD1GxaUeyabF1Xc1iEeti1A4ABaQv6HE2lj/+UPFSwkgl83z8Rg3GgGp + KMF16xiLx8FmVqNdPAgeY5L2E/dY/LLiA7pHoRq0/7sQkN9qhYAMDvDzJIAkG5jjIh3d + UqD0aCke9Y1Mh1E8BoREbU89nJ1H5h4Bj7DBnOSR5OgeRSLIpiYOkOUSQK5FQEqnta7+ + JcDjOA+fect4PJYDHhth5ervJiyual+JzAhkAfn66f0b1EGup4B0ZACph89i1VU50hs7 + +ugbDiF49PQLCEY8ikV7Csch4yAJIC+fzdy/IwYBqbMSFA5cFo9rY3aw7hHxqFAQWj6o + AJCFeaegxIqAFKa1zv4O7lGPwaMjxeN6xj3ef/oa3aOWpae4e1lAvii+DYDcB4AMYkqs + Q3EOUmfVSB4qR5bmkOIqdY8Uj2cv3yl+gXhUrAdtH5UC5NeaSnCQhbmnDhFALvbzHI8O + UnfFCCOXxuPiYILHU3mFFI9fsbiqbfEp6l8hIDMoIP19JsEcJAJSZyXJ4XEog8cgwOO+ + jLOXbyMeFSlBHMeUATIWADlr6nhHGzoHiSVWXRSlBI82juM9/QCPsamHTuUSPFbWIB7F + oT+5UUgA+Ysu0il/8QgcZMa+xJiwIADk2FEISF2UIhmzEI+TfPyDwhCPctkvvgNKALkr + NhIBqatKZMYtxOPUWYhH8WlP4YikAPnpPQFkQU5Gmiwge3TX7ezscqPn8ThqLIPHROoe + H7HF1V+4NEehHrR9UB6Q96DEepAHpAVZxYoOUtcEzeHRAtwjwWNk7C50j9oWW2v65wH5 + s7GeA+TxtMRNYUv9p/MOEgGpW4rs3oPOPQ61kuAxLSOn4Dbg8f2n+safiMfWaEMb53CA + hO3nfnz7Wl35+sm967knD+7aFrlqEZRYHWxYQHbvplsJ2bVHqwCPB0/mFt57gsVVbYis + LX0ygPzz+99fPxvrKCBvFeQQQIZygBxEHhSAgNQlhQvxON1/adimxLTjOQW3BHhkd7Rq + S6bguZqIgCwgXwEgLxJARnCANDHU762H28/pkB67dacrV00sbBzAPS5aFbltF+DxOsFj + NZl7xA3mNKGsdvbBARI2MKeAfP6IAjKBAnIimYMcZNAXAalDcvyLweMg6h4Bj6GbEhg8 + Pgf3WEfdI+KxnWpR/9uUADKFAHLmVFdwkCaG/RGQOqRHmOzQ661vSPDoSvAYQfB4EfD4 + CvGofj11uAc5QD68lQ8OkgXkmFFWpghIHVLjXxSPfQ0oHicqwqPkMR0dTh1sQA0RkAHk + W9ZBMoCc4upgbWECG5ijg9QVTVI89h9oYmHt4DplJsFjCuJRDbpRW5MKAXlsb0J06BJ/ + 74ljRloiIHVFi2ScxD0CHk0tR46heIxO2Hs8J//WQ849Ih7VpiTVNCwA5HeYgwRA3r1+ + 8UR6ytaIlQtnIiB1SYswVgV4TD9B3eNbLK6qRjDqbkUJIOOj1yyZKwQkrgnQAW0K8eg9 + d0ko4PEY4lHdElJp+0JA1n8BQD6+e+0CD0h7a3N0kDqgRDpEHo/24B4XrozYmgJ4vHb3 + 8SvAY/13nHtUqXLU1Jg0IKvKnj+8mZ9zbC8DSHfOQeKjH3VAlLBUTuIeAY9rouMJHm+i + e1STdNTSrBJA7twavnKh72RXBKQOCJEZIodHc2vAo+/CleEEjxcYPH5BPKpFPWpoVADI + hrqPVWXPWEBuJA6SAeSAvrh5gPhlKcCjuxQey6o+1TX85LcIUEMSYZMqi4AsIEuJg8xK + ZwHpgoAUvxLpCIV4nEzxuBPxqDKVaK4hTo+/YRUrA8gHN/PPHN0Tv3FNIADSmcxBIiDF + L0qKxwF07pHicWP8nmNnwD0+K6v6KIVH3EFHc+JqT0+cIGH7ue+kxFr6uIgCckv4igW+ + k13sR2CJVfxqlMw9gnt0ATyuCN+yMz2LuMfSt9XoHtujC229h9OjPCBXB86Z5u5sZwkb + mKODFLkmeTw6u3vPCVwDeDwKeHyAeNSWrtrdLydIASCvEgfJA9IYVrH2xPsgRSxJcI89 + e/cfaGw+guBxAYfHIhaPsMEce58VXq22WyeaeiOnRwrIWlJifXCDcZAsIIcjIEUsRTo0 + Bo9GppZ2zu7T5gSuRjxqSj1q6IcTJAvICnCQV89npScTQM6gDhIBKW5ByuMxOT3r/FXE + oxrUov4mOT3KAHJ3/AYCSDdnOwSkuOVI91/tO8DIdLidsxuPR9ieHIqrtcK5R7xcVb+c + Ot4DJ0gJIEsAkJnpyZt5QBqggxSvJhk8GjDucQZxjwweS0orSHEV3WPHJaLRFjg9CgF5 + 6fQRaUD2wUU6YlUk5x45PG6I3330dD7iUaMiUmVnnCClAXkAALl8PnWQZsYG/bDEKlI9 + SuNx/vLwzckHMsE9Ih5VqRFNtsXpkQPkGyixEkDGbQgJnO3l5kQdJAJSrHqEGzv68O4x + ZEPc7iOnLwEe36B71KSKVNgXFWRT0x8KyM/VFS9Liq6czwRArhMAshfOQYpRkRSP/QyM + zcjc4wwOj1eKSl6ie1ShRDTalAwg3yEgxag8xWMi7pHBo5Ob1+xAAR7fYXFVoypSYWfy + gLxz5VzmgSQOkFbEQSIgFStCq0cJHnsJ8LiOuMdzV+4QPH5mi6tN9OtVYbpgU2qOgACQ + PxpqPzKAzD6SGhfFOEhbMgeJDlKrylPcuSweoyTukeDxB3/fI849qllDKm1eDpDFBJD7 + k2LAQfrAIh0rs8EISMWK0OpRCR6twD36zF++bnMS4lGlytBOY9KArKGAzMs+nLo9KiRg + ttcEJ9thpkb6CEitak9R5xI82jpNIO4xKi71SDYtrr6rQTxqR0sq6ZUF5G8osTbWf/5Q + 8VICyGXzfDzGjUZAKpKDto+xeBxsZjXaxYPgMSZpP3GPxS8rPqB7VIkwtNSIEJDfaoWA + DA7w8ySAJBuY4yIdbStQun+KR30j02EUjwEhUdtTD2fnkblHwCNsMCd5JDm6Ry3pqt3d + ygCynAXkjpi1CEhpEYjnLwEex3n4zFvG47Ec8NgIK1d/N2Fxtd2S0OobZQH5+un9G9RB + rqeAdGQAqYfPYhWPHOmNHX30DYcQPHr6BQQjHrUqIdV2rgCQl89m7kdAikiA0kORxePa + mB2se0Q8qlYb2mhNASAL805BiRUBKS0D0fwF7lGPwaMjxeN6xj3ef/oa3aM2FKTiPmUB + +aL4NgByHwAyiCmxDsU5SNFoEQaiEI9nL98pfoF4VLE0tNKcFCC/1lSCgwRAHiKAXOzn + OR4dpJjESPSoAI+n8gopHr9icVUrGlJppwoBmUEB6e8zCeYgEZAikiSHx6FWo0lxNQjc + 476Ms5dvIx5VKgotNqYIkLkAyFgA5Kyp4x1t6BwklljFIUoJHm0cx3v6LQ5eH5t66FQu + wWNlDeJRizJSXdcSQP6ii3TKXzwCB5mxLzEmLAgAOXYUAlIcUiSjEOJxko9/UBjiUXVC + EElLSgEZiYAUjxKZkQjxOHUW4lEkElLtMKQA+ek9AWRBjjwge3QXW3Z2ufHweBw1lsFj + InWPj9ji6i9cmqNaaWilNXlA3isEB7krlgOkBVnFig5S+/Ln8GgB7pHgMTJ2F7pHrWhG + rZ3ygPzZWM8B8nha4qawpf7TeQeJgNS2Irv3II8FGDzUSoLHtIycgtuAx/ef6ht/Ih7V + KhONNc4B8t9/fv349rW68vWTe9dzTx7ctS1y1SIosTrYsIDs3k3bCdm1+1eAx4Mncwvv + PcHiqsa0opGOGED++f3vr5+NdRSQtwpyCCBDOUAOIg8KQEBq998DIR6n+y8N25SYdjyn + 4JYAj+yOVhpJGuxEbRGQBeQrAORFAsgIDpAmhvq99XD7Oa3qsVt3unLVxMLGAdzjolWR + 23YdPHnxOsFjNZl7xA3m1CYQTTfMARI2MKeAfP6IAjKBAnIimYMcZNAXAalVOf7F4HEQ + dY+Ax9BNCQwen4N7rKPuEfGoaeGoqT8lgEwhgJw51RUcpIlhfwSkVvUIkx16vfUNCR5d + p85ctCqCw+MrxKOaZKG9ZuUA+fBWPjhIFpBjRlmZIiC1qsa/KB77GlA8TlSER8ljOrSX + RdizqiIgA8i3rINkADnF1cHawgQ2MEcHqT1NUjz2H2hiYe3gOoXiMYVxj4hHVYlATO0o + BOSxvQnRoUv8vSeOGWmJgNSeFknPxD0CHk0tR46heIxO2Hs8J//WQ849Ih7FJKcOj0UA + yO8wBwmAvHv94on0lK0RKxfOREBqV4vQuwweV0ZsS0k/QYqrr95icbXD2S/CBpQAMj56 + zZK5QkDimgCtaFOIR++5S0IBj8cQjyLUkaqGJARk/RcA5OO71y7wgLS3NkcHqRUl0k55 + PNqDe1y4MmIrweO1u48JHuu/49yjqmQgnnakAVlV9vzhzfycY3sZQLpzDhIf/agVUcJS + OYl7BDyuiY4neLyJ7lE8+lH1SJQAcufW8JULfSe7IiC1IkSmUw6P5taAR9+FK8O37kw/ + cYHB4xfEo6qlII72BIBsqPtYVfaMBeRG4iAZQA7oi5sHaEOWAjy6S+GxrOpTXcNPfosA + cWQSjkIVEZAFZClxkFnpLCBdEJDaUCLtU4jHyYhHVSS7DrTB6fE3rGJlAPngZv6Zo3vi + N64JBEA6kzlIBKQ2REnxOIDOPVI8bozfc+wMuMdnZVUfpfCIO+jogM5aPUROkLD93HdS + Yi19XEQBuSV8xQJf2J91BJZYtaFGydwjuEcXwOOK8C0707OIeyx9W43usdXprXMncnqU + B+TqwDnT3J3tLGEDc3SQGtckj0dnd+85gasBj0cBjw8QjzonsbYNmBOkAJBXiYPkAWkM + q1h74n2QGpUkuMeevfsPNDYfQfC4gMNjEYtH2GCOvc8Kr1bblu6iP5vTIwVkLSmxPrjB + OEgWkMMRkBqVIu2MwaORqaWds/s0xKPoVaTCAXKCZAFZAQ7y6vms9GQCyBnUQRobICA1 + K0mFeDx/FfGowrwXa1OcHmUAuTt+AwGkm7MdAlKzYoTeODwOt3N24/EI25NDcbVWOPeI + l6tilVX7x8UJUgLIEgBkZnryZgSkxoXIdMjg0YBxjzOIe0xOzwI8lpRWkOIqusf2J7sO + vJPToxCQl04fkQZkH1ykozlxyuJxQ/zuo6fzEY86ICZVDJETpDQgDwAgl8+nDtLM2KAf + llg1pkdpPM5fEb45+UAm4lEVqa4TbXB65AD5BkqsBJBxG0ICZ3u5OVEHiYDUnB7hxo4+ + A4xMOfe4IW73kdOXAI9v0D3qhKA6OkgqyKamPxSQn6srXpYUXTmfCYBcJwBkL5yD1Iwi + KR77GRibkbnHGfOXS/D4Et1jRzNdN94vAOSPhtqP7xCQmlGe4l6Ie2Tw6OTmNTswhMfj + Oyyu6oagOjpKeUDeuXIu80ASB0gr4iARkIr1o+KjBI+wg44Ej+uIezx35U4J4PEzW1xt + ol9YR791fL9YI6AQkNlHUuOiGAdpS+Yg0UGqWHmKm5PFY5TEPRI8/uDve8S5R7HKqePj + kgNkMQHk/qQYcJA+Hi72VmaDEZCK9aPioxI8WoF79Jm/fN3mJMRjxxNcx1qQBmQNdZB5 + 2YdTt0eFBMz2muBkO8zUSB8BqWLtKWpOgkdbpwnEPUbFpR7JpsXVdzWIRx2TVfuHywLy + N5RYG+s/f6h4KQHksnk+HuNGIyAViUf1x1g8DjazGu3iQfAYk7SfuMfilxUf0D22P791 + 7Z1CQH6rFQIyOMDPkwCSbGCOi3RUr0DpFike9Y1Mh1E8BoREbU89nJ1H5h4Bj7DBnOSR + 5OgedU1ibRuvDCDLJYBci4CUlow6/xLgcZyHz7xlPB7LAY+NsHL1dxMWV9uW2bp5tiwg + Xz+9f4M6yPUUkI4MIPXwWazqlCO9saOPvuEQgkdPv4BgxKNuqkkFo1YAyMtnM/fviEFA + qlWCwsZl8bg2ZgfrHhGPKkhxnWpCASAL805BiRUBKZSMWn8H96jH4NGR4nE94x7vP32N + 7lGnxKSKwcoC8kXxbQDkPgBkEFNiHYpzkGpVI3moHFmaQ4qr1D1SPJ69fKf4BeJRFRmu + W21IAfJrTSU4yMLcU4dSY9cHL/bzHI8OUr1ihNYV4fFUXiHF41csruqWnDo+WoWAzKCA + 9PeZBHOQCEi1SpLD41AGj0GAx30ZZy/fRjx2PLd1sYXmADlr6nhHGzoHiSVWdYlSgkcb + x/GefouD18emHjqVS/BYWYN41EVFdXDMEkD+oot0yl88AgeZsS8xJiwIADl2FAJSXVIk + 7QrxOMnHPygM8djBfNb1tysB5K7YyODFCEh1apHqkSuuAh6nzkI86rqaVDB+KUB+ek8A + WZCTkSYLyB7d1Z2bXbB9Ho+jxjJ4TKTu8RFbXP2FS3NUkOG61YQ8IO8V5p48yAPSgqxi + RQepjn8uOPdoweIxMnYXukfdko/qR8sD8mdjPQfI42mJm8KW+k/nHSQCUvWK7N6Dzj0O + tZLgMS0jp+A24PH9p/rGn4hH1We7+FvkAPnvP79+fPtaXfn6yb3rBJDbIlctAgfpYMMC + sns31Sdk125RAR4PnswtvPcEi6vil436RsgA8s/vf3/9bKwDQD5/dKsghwAylAPkIPKg + AASkqv/1EOJxuv/SsE2JacdzCm4J8MjuaKW+7x5bFl8EZAH5CgB5UQqQJob6vfVw+zkV + 67Fbd7py1cTCxgGKq4tWRW7bdfDkxesEj9Vk7hE3mBOfVjQyIg6QsIG5EJAJFJATyRzk + IIO+CEgVy/EvBo+DqHsEPIZuSmDw+BzcYx11j4hHjeS/2DpRAsiUbRGrFs2c6goO0sSw + PwJSxXqEyQ693vqGBI+uBI8RHB5fIR7FphANj0cOkA9v5YODZAE5ZpSVKQJSxWr8i+Kx + rwHF40RFeJQ8pkPDyYDdaT0CMoB8yzpIBpBTXB2sLUxgA3N0kKrUJMVj/4EmFtYOrlNm + EjymMO4R8ah1PWh9AAoBeWxvQnToEn/viWNGWiIgValF0hZxj4BHU8uRYygeoxP2Hs/J + v/WQc4+IR62rQnsDEADyO8xBAiDvXr94Ij1la8TKhTMRkKrWIrSnAI/pJ0hx9dVbLK5q + Twhi6VkJIOOj1yyZKwQkrglQkTaFePT2XxIKeDyGeBSLHLQ+DiEg678AIB/fvXaBADKc + AtLe2hwdpIqUSJvh8WgP7nHhyoitKQSPdxk8fse5R60rQssDEACyoe5TVdnzhzfzc47t + ZQDpzjlIfPSjikQJS+Uk7tF77pI10fGIRy0rQFzdKwHkTgJI3ymuCEgVCZFphsOjuTXg + 0XfhynCCxwvX7j4G9/ilHvEoLm1oZTRSgPxYVfaMBeRG4iAZQA7oi5sHqEaWAjy683i8 + +fB5WdWnuoaf/BYBWkkF7FQEEZAFZClxkFnpDCAnuyAgVaNE2ooQj5MpHnciHkWgATEN + gdPjb1jF2lBHAPngZv6Zo3viN64JBEA6kzlIBKRqREnxOIDOPVI8bozfc+xM/s2Hz8qq + PkrhEXfQEZNENDsWTpBwH+R3UmLlALklfMUCXwDkCCyxqkaNkrlHcI8ugMcV4Vt2pmcR + 91iK7lGzOS/m3jg9ygNydeCcae7OdpawgTk6SBVoksejs7v3nMA1gMejgMcHiEcx60Pj + Y+MECfuzsoAsukocJA9IY1jF2hPvg+ygJME99uzdf6Cx+QiCxwUcHotYPMIGc+x9Vni1 + qnENiKlDTo8UkLXUQd5gHCQLyOEIyA5Kkb6dwaORqaWds/u0OYGrEY9iUoGIxsIJkgVk + Renjoqvns9KTCSBnUAeJgOy4IOXxmAzu8SriUURKEMdQOD3KAHJ3/AYCSDdnOwRkx+VI + 91/tO8DIdLidsxuPR9ieHIqrtcK5R7xcFYcstDcKTpASQJYAIDPTkzfzgDRAB9kxTTJ4 + NGDc4wziHgGP568WlZRWkKU56B61l/6i65nToxCQl04fkQZkH1yk0xFFcu6Rw+OG+N1H + T+cjHkUnBjEMiBOkNCAPACCXz6cO0szYoB+WWDugR2k8zl8Rvjn5QCbiUQzJL8IxcHrk + APnm2YMbBJBxG0ICZ3u5OVEHiYDsiB7hxo4+Ave4IW73kdOXAI9v0D2KUBDaHhIVZFPT + HwrIz9UVL0uKrpzPBECuEwCyF85BtleRFI/9DIzNyNzjjPnLJXh8ie5R27kvxv5lAPkO + Adle5Sl+H3GPDB6d3LxmB4bweHyHxVUxCkLbY5IH5J0r5zIPJHGAtCIOEgGpWG0tHiV4 + 7CXA4zriHs9duVMCePzMFleb6Feg7TzA/sURAQEgfzTUfmQAmX0kNS6KcZC2ZA4SHWSL + ylN8giweoyTukeDxB3/fI849ikMO2h+FHCCLCSD3J8WAg/TxcLG3MhuMgFSsthaPSvBo + Be7RZ/7ydZuTEI/aT3lRj0AakDUUkHnZh1O3R4UEzPaa4GQ7zNRIHwHZovYUnSDBo63T + BOIeo+JSj2TT4uq7GsSjqGWhvcGxgPwNJdbG+s8fKl5KALlsno/HuNEISEVSa80xFo+D + zaxGu3gQPMYk7SfusfhlxQd0j9rLeHH3LATkt1ohIIMD/DwJIMkG5rhIpzUKlD6H4lHf + yHQYxWNASNT21MPZeWTuEfAIG8xJHkmO7lHcEtHs6GQAWS4B5FoEpLTA2vaXAI/jPHzm + LePxWA54bISVq7+bsLiq2VzXhd5kAfn66f0b1EGup4B0ZACph89ibZsc6Y0dffQNhxA8 + evoFBCMedUENIhijAkBePpu5f0cMArKNEhSeLovHtTE7WPeIeBRB0ot4CAoAWZh3Ckqs + CEihwNr4O7hHPQaPjhSP6xn3eP/pa3SPIhaDGIYmC8gXxbcBkPsAkEFMiXUozkG2UY3k + oXJkaQ4prlL3SPF49vKd4heIRzHkvJjHIAXIrzWV4CALc08dIoBc7Oc5Hh1kW8UI50vj + cXEwweOpvEKKx69YXBWzHLQ/NoWAzKCA9PeZBHOQCMg2SpLD41AGj0GAx30ZZy/fRjxq + P9vFPwJlgIwFQM6aOt7Rhs5BYom19aKU4NHGcbynH+AxNvXQqVyCx8oaxKP4FaHlEUoA + +Ysu0il/8QgcZMa+xJiwIADk2FEIyNZLkZwpxOMkH/+gMMSjljNct7pXAshdsZEIyLYp + kTlbiMepsxCPuqUGEYxWCpCf3hNAFuRkpMkCskf39mRnl3sPj8dRYxk8JlL3+Igtrv7C + pTkiyHkxD0EekPegxHqQB6QFWcWKDrJ1/7RweLQA90jwGBm7C92jmNNffGPjAfmzsZ4D + 5PG0xE1hS/2n8w4SAdkaRXbvQeceh1pJ8JiWkVNwG/D4/lN940/Eo/jyX2wj4gAJ28/9 + +Pa1uvL1k3vXc08e3LUtctUiKLE62LCA7N6tNQnZtc9RgMeDJ3ML7z3B4qrY0l6842EA + +ef3v79+NtZRQN4qyCGADOUAOYg8KAAB2fK/NUI8TvdfGrYpMe14TsEtAR7ZHa3Emw04 + Mm1HQBaQrwCQ/7+9c3+oaev6+ME5HLdEKqko5VYpROVSoVCEKNcuRBclhVRUlFshVC7J + raTcKUohSipKN1QKx3OO4/1H3jHnWmvvtXflnEN7t9daY/3wPJ7zPNaec6wxns/8jjnm + HFcIIIM5QOqP0Bo4ANvP/WM89ulLK1f1jc0tQT2u9A3Ztf/omSs3CR4byN4jNpjrbV8X + xO9zgIQG5hSQzx5RQMZSQDqQPciR2oMRkP8Yjr8weBxJ1SPgMWB7LIPHZ6AeW6l6RDwK + IiJ6d5DdAHIfAeQiR1tQkPojhiIg/zEeYbNjwECtEQSPto6LVvoGc3h8gXjsXQ8X2K93 + AmTx3VxQkCwgbSaZGiAg/zEaf6F4HKxN8ejQFR5l13QIzD1wuGq2gBIgX7EKkgHkXFtL + M2N9aGCOCvL7MUnxOHS4vrGZpe1cisd9jHpEPKrZnwX/c10C8uTB2PCA1R7zHWwmmiAg + vx+L5L8l6hHwaGAy0YbiMTz24Kms3LvFnHpEPAo+TtQ1AR4gP8IeJACy8OaV0yn7dgb7 + eC5CQP5zLML/ogs8ppwmydUXrzC5qi5HFsvvdAPImPBNq5fyAYk1Ad3GJh+P8z1WBwAe + TyIexRIgap4HH5Bt7wCQjwtvXJYD0sLMCBVkt5FI/ws5Hi1APXr6BO/cB3i8UUjx2PYR + 9x7V7NEC/zlFQNZVPSu+k5t18qASIPHqx26DEkrlZOpx/tLVm8JjCB7voHoUeGD00vC7 + AWTCziAfz4VzbRGQ3QYi819weDQyAzwu9PQJIni8fKPwMajHd4jHXvJqAf8sD5DtrU11 + VWUsILcRBWlPU6zDBmPzgO7CkodHewU8VtU1t7Z/lrcIELCL4NDVaAFlQFYQBZmRwgBy + znQEZHeRSP85H49zKB4TEI9q9F7x/RQXj1+hipUBZNGd3PMnDsRs2+QNgLQme5AIyO6C + kuJxGN17pHjcFnPg5HlQj2VVdU0KeMQOOuILHdXMiAtIOAf5kaRYKx4XUEDuCNqwYiEA + chymWLuLRtneI6jH6YDHDUE7ElIyiHqsQPWoGm8V/1u5eOwMyI3eS+bZW08wgQbmqCC7 + jEk5Hq3t5y/x3gh4PAF4LEI8ij9uVDZDLiChPysHyOtEQcoBqQdVrP3xHGSnkAT12H/g + 0OF6RuMIHldweCxg8QgN5thzVrhaVZn3iu/FXDxSQLaQFGvRbUZBsoAci4DsFIr0HzB4 + 1DEwmWBtPw/xKL7Q6J0ZcQHJArIGFOT1Sxkp8QSQrlRBIiC7Csgu8XjpOuKxd9xYNL/K + xaMSIJNiwggg7awnICC7Ckfaf3XwMB2DsROs7eR4hPbkkFxt4e894nJVNLGilolwASkD + ZCkAMj0lPlIOSG1UkMoxyeBRm1GPrkQ9xqdkAB5LK2pIaQ6qR7X4rhh/hItHPiCvZqYq + AnIQFukoRiSnHjk8hsUkncjMRTyKMULUPCcuIBUBmQyAXL+cKkhDPQSkYjTSDjoDh3J4 + XL4hKDI+OR3xqGbPFefPcfHIAfIlpFgJIKPD/L3dne2mUAWJgFSISILHQTz1GBadlJp5 + FfEozghR86xoQH779jcF5NuGmvLSgvxL6QDIQDkg4W5k3IOUhSRVj0O09QzJ3qPr8vUy + PJajelSz84rw55QAWYuAlAVeN3+Q43GKnbO7t78Mjy9rMbkqwghR85Q6A/J+/sX05DgO + kKagIBGQ8tgkePydh8dAoh4v5t8vBTy+ZZOr36hR1fwh8edEYQEeID+1tzQxgDyXmhgd + yijI8WQPEhWkLCCV8RjKqUeKx0/yc4+49yiK+FD7JDoBsoQA8khcBChIFyjSMTXURUDK + w5HDoymoR5fl6wMj4xCPavdZMf+gIiAbKSBzzh0ngPRyd545ZfwYBKQsHGlpDk2ujp8y + k6jH0OjE1HMkufqytrGlHfEo5khR09xYQH6FFGtH29v6mnIZINctc5k9bTICkheODB51 + DQGPswkeI+KOEPVYUl5Tj+pRTQ4r8p/hA/JDixyQu0P9vNycCCBH6WihgqRBSdWjlo7B + GIpHL//Q3YnHz+UgHkUeIuqdnhIgq2WA3IKAlKOR/IlNrgIeJ0+b7bJsnRyP1YDHDqhc + /foNk6vq9V7x/ZoyICvLHt4mCnL3VhkgR2gNGoB3sTLqUWvEKIJHJzcvPwU8Qv9VWccO + TK6KL0zUN6MuAJl3If3I3ggEpAIflfG4JWIvqx4Rj+rzVvH/UidAPn14K+esDJBWoCAR + kGS52o+2Jwc8WlE8bmXU48OnlZBcRTyKP1DUNUNlQD4vuQeAPAyAXMukWEfjHmRn9Ujx + eCEPkquIR3V5qjR+RwGQ7xtrKykgjxEFucrNaQYCki5bu8Lj2ZxbFI/vEY/SCBX1zLJL + QKZRQHq4zII9SASkLLk6mkmurgU8Hk67kHev5DniUT1eKp1fUQLkawLI7LPHEqMAkIsB + kOaoIOXq0dxqhpPbKr+tUYnHzmYTPL5uRDxKJ1bUMtNOgHwECjLt8J6IzWsBkFMnISC5 + 5CrB4ywXj7WbEY9q8Uxp/ki3gAwBQDoiIHnJVcCj42LEozTjRG2zlgHyC1SxNtdXP390 + 71pWZ0D266uwIyed/yDH46SpDB73UPX4iFWPX7A0R22+KoUf6gzIB0RB7o/iAGks7T1I + WXKVxWNI1H5Uj1IIjN6aoxyQnwGQbxhAnjq0Z/vmNR4L5ApSqoDs249cC6A72lSGx0Np + WdfuAR7fNLd1fEY89pbfivV3OUBC+7lPH943vK588uBm9pmjAEhfoiAtzVlA9u0jnUWq + fKYcHo3leDx6JvvWgyeYXBVrQPT2vBhA/v31ry+fO1opIO9eyyKADOAAOZJcFCBNQPLx + uMBjzebtew6dUsQj29Gqt78i/r5YLKAMyBcAyCsAyF0hvisZQOqP0Bo4QJJXP/bpSytX + 9Y3NLSG5utI3ZNd+wONNgscGsveIDebEEgUaNA8OkNDAnALy2SMKyFg+IAdLE5AMHkdS + 9Qh4DGDwePfRM1CPrVQ9Ih41yJPFMZRuALlvV7DvykWOtqAgpQpI2OwYMFBrBMGjLcFj + MMHjFcDjC8SjOHxfI2fRCZDFPEA62EwyNRipLUlAEjwO1qZ4dKB4jCXqUY5H2Tlkjfys + OCiBWkAJkK9YBckAcq6tpZmxPjQwl56CpHgcCng0s7Sdu4jgcR/iUaA+LqhhdwHI3KyT + B2PDA1Z7zHewmWgiTUCyeDQwmWjD4vHgqazcu8WcekQ8CsrLhTNYHiA/wh4kALLw5pXT + Kft2Bvt4LpIsIBk8DufjMeU0VY+vMLkqHOcW4ki7BeSm1Uv5gJRUTQAfj/M9VgeExx48 + iXgUonsLbsx8QLa9A0A+LrxxWQ5ICykqSDkeLUA9evoE79xH8Fj45AXB40fcexSclwto + wIqArKt6VnyHKMiYcAVASurqxz5McpWqx/lLV28Kj0E8CsijhT3UbgCZsDPIx3PhXFsL + MyOppVg5PBqZAR4XevoEETxevlH4GPD4rg3xKGx/1/jR8wDZ3tpUV1XGAnIbAaQ9TbEO + G/y7hAAJleRk75Hg0V6OxzvFz6rqmlvbP8vvQNb4T4sDFKAFlAFZwShIBpDQfk5qgOTj + cQ7FYwLiUYB+LdQhc/H4FapYGUAW3ck9f+JAjEQBSfE4TI7HbTEHILl6p7isqq5JAY/Y + IkCoLq/Z4+YCEs5BfiQpVgrIjJSEHUEbViyUHCB5eJwOeNwQtDMhJYOoxwpUj5rtx2IZ + HRePnQG50XvJPHtrUqQjHQUpx6O1/fwl3psAjyfO594pQjyKxd81fh5cQEJ/VhaQBdcv + 8wA5zkgPqlj7S+IcJOCx/8Chw/WMxkF78oUrNgTtoHgsYPEIDebYc1a4WtV4vxbqALl4 + pIBsISnWotuMgmQAOcEEGphLJMXK4FHHwGSCtf28Jd4bEY9C9WoBj5sLSBaQNRWPC65f + ykiJ5xSkdADZNR6vIx4F7N3CGzoXj50AGUYAaWc9YaxUAMnhcewEazs5HqE9OSRXW/h7 + j7hcFZ6bC2fEXEDKAFkKgExnAOkKKVYApLYUFCSDR21GPboS9RifknGJ4LGGlOagehSO + Swt6pFw8KgAyMzUphg/IQeIv0lHGY1hM0onMXMSjoJ1biIPnAlIRkMnxkUHrl1NAGkoB + kJ3wGBmfnA54LEU8CtGpBTxmLh45QL6EFOtVAGR0mL/3EmdWQYoekASPg4bpGHDqEfCY + mnkV8Shgxxbq0GlAfvv2NwfI8tKC/EvpioAcIvI9SIrHIdp6hmTv0XX5+iDEo1DdWfDj + VgJkLR+Q7s52U2iKVeSAlONxip2zu7d/WDSLx5eYXBW8gwttAgqAfNtQU156P/9ienJc + ZCCjIE1BQULzABEX6RA8QgcdGR4DCR4v5heUltc0vGWTq9+omYT2bXG8wrMAD5Cf2lua + OEAmRof6exNAjid7kKIGZPd4rIW9x0/yc4+49yg89xbeiDsBsoQBZAQA0gX2IMUOSBke + TUE9uixfHxgZR/B4H/EoPF8Ww4gVAdnIAPLccQJIL3fnmVPGjxE3IGV4HD9lJlGPodGJ + qedIcvUl4lEM/i24ObCA/EpTrG/ra8opII/ERQSuW+Yye9pkU0NdEStIFo+6hoDH2QSP + EXFHCB5LymvqUT0KzplFMGA+ID+0MIDMAUDuDvVjATlKR0u0CpLiUUvHYAzFo5d/6O7E + 4+dyKB4bUT2KwL2FNwUeIDva3tZXywC5RfyAlONx8rTZLsvWKeKxAypXv37D5KrwfFrI + I+4MyIe3KSC3+nm5OREFOWqEWAHJ4HHEKIJHJzcvPwU8Qv9VWccOTK4K2cWFNfYuAJl3 + Mf3I3gjRA1IZj1si9rLqsRrUI+JRWH4sltEqA7LyqQIgrRhADhDhXayAxwGDtAgerSge + tzLq8eHTytrGFsSjWBxcaPNQBuTzknt5F/iAHC3OFGuXeLyQB8lVxKPQfFhM41UA5PvG + WgDkrZyzkGLd6rcKFKRoAdkVHs/m3EI8ism5hTiXLgGZdhgU5FoPugcpSkByeBxtSpOr + a0E9Hk6/kHev5DniUYheLJ4xKwHyNQFk9tljiVEAyMVOM6zMaYpVbAqSh8cZTm6r/LZG + JR47m03w+LrxPapH8bi38GbSCZCPQEESQG4GQM6aNslUhIDk43GWi8fazYDHNMSj8JxX + hCPuFpAhAEhHcQJShkdzqxmOixGPInRr4U5JBsgvf3S0NddXPwdAZqUd3sMAcqockL+I + 5ZHjcdJUBo97KB4fserxC5bmCNedBT/ybgC5P4oDpDEp0hGTglTGY0jUflSPgvdj0UxA + DsjPAMg3BJDXstIO7dkOCnLBLBkg+/UVCx/7QnvyIdq6o01leDyUlnXtHuDxTTOU5iAe + RePagpyIDJD/+/Lpw/vG15VPHtzMPnO0EyD79hFHQHJ4NGbVI+Dx6JnsWw8wuSpI9xXf + oBlA/v31ry+fO1opIO9eyzpFALmGBeRIcg5SLIDk43GBx5rN2/ccOiXH42fEo/g8XFgz + 4gAJ/VkBkA0UkFcAkLtCfFdCitXS3FifKEiR3GzVpy9TuWpsbgnJ1ZW+IbsIHm8+eFL5 + uoHsPWKDOWF5rwhHywESGphTQD57RAEZuz1AhIBk8DiSqkfAYwCDx7tUPbZ2EDyyDR9F + +KFxSoKwgDIgX4CCBEDu2xXsu3KRoy0DyIHiACRsdgwYqDVCXwGPVwCPLxCPgnBWKQyy + EyCLeYB0sJlkajBSe7A4FCTB42BtikcHisdYoh7vPnoGyVWCR9k5ZCl8d5yjZlrgu4Cc + a2tpZqwPDczFAEiKx6EUj7aOi1b6Bu/ad/QM4lEz3VK6o+oCkLlZJw/GhoOCnO9gM9FE + LIBk8WhgOslGjsfcu8WIR+k6vwbOXAmQr6iCPJ2yb2ewz8pFIgIkg8fh+sZmlrZzFfD4 + CpOrGuiXkh1S94BcvZQPSIHXBHB4NJlo4zDfY3VAeOzBk1mIR8m6vcZOnAfIj23vG169 + eFx44woDSE8ApIU4FKQiHj19gnfuSzl95WbhkxcEjx9x71Fj/VNyA1MEZF31s+I7REHG + hG/iA1LgN1v1YZKrBhSPS1dvCo9BPErO04UxYQVAvmMAefl0SsLOIB/PhQSQRsJPsXJ4 + NDKzAPXI4vHyjcLHgMd3bYhHYXiqREbJA2R7a1NdVRkfkPZcilXQgIRKcrL3qITHO8XP + qupw71Eibi6YaSoDsgIUpAyQ0H5O+IBUwONCT5+gnQkppxGPgvFQaQ2Ui8evUMUqA+T5 + EwdithEFyQBy2ODfBQxIisdhFI/280E9bqPq8U5xWVVdU2s7luZIy981frZcQMIxj49t + oCApIDNSEnYEbfBcKAJA8vA4fQ6LxwyCxwpUjxrvnBIcIBePckAW3cmlgNzovWSevTUp + 0hEyIOV4tAY8egMeD5w4n3unqBMesYOOBL1fA6fMBSTtz0oBWXD9MgPIFQSQ44z0oIq1 + v0DPQcrwOA7aky9csSFoR0IK4lED3RCHxFqAi0ceIG/zATnBBBqYC1ZByvA4wdp+3hLv + jYhHdHwNtwAXkHxAXspIiQcFKXhAAh77Dxw6XM9IAY/XC1j1CP1X2WPIuFrVcC+VzvC4 + eKSAbCF7kEU8QNpZTxgrXEAyeNQxGKuAR2hPDsnVFn5yFeNROg6v6TPlApIFZE1FacF1 + DpCujILUFqaCVMCjK1GP8SkZlwgea0hpDuJR011TkuPj4lEBkJknkmLCSIpVyIDk4dGO + qMewmCRIriIeJenmwpk0F5AKgExPjo8MWr9cyIBk8KjNqEcWj+mAx1LEo3CcU4Ij5eKR + A+RLUJBXM1OTosP85YAcJLwinS7wmJqJeJSghwtsylxAcoAsLy3Iv8QHpKGe9hDB7UFy + eDQkyVXX5euDIuOTEY8Cc01JDpeLRxaQtXxAujvbTaEpVsEBkuBx0DCaXLVzdvf2D4tO + Ss28CurxJSZXJenmwpk0Dchv3/6mgHzbUFNeej//YnpyXGQgqyAJIH8XVpEOxeMQbT1F + POYXlJbXNLxlk6vf6MSF851wpNKwAA+Qn9pbmuSADPX3JoAcT/YgBQZIOR6nKOGxFvce + peHWwp1ld4CMAEC6wB6kqeAASfAIDeb0DE0Z9RgYGZecfjH/PuJRuF4qnZErArKRAeS5 + 1MRoCsiZAgSkDI/jGTyGRiey6pHg8ZP8SnIszZGOmwtnpoqArK8pLyEK8kgcBeRsAkhd + QSlIBTy6LF8fGMHgsQTVo3CcUsIj7QqQOeeOJ+4O9fdydwZAjjHQ0RKQguThcaazu5c/ + 4PH4OZpcrW1EPErY0YUydRaQXyHF2tH2tr5aDsh1y1xmT5ssLECyeNQ1NJ08bbbLsnWA + xyNEPQIe65nk6tdvmFwVim9KcZx8QH5oYRQkA0g/LzcnAshR0J9VKEU6FI9aOqPGjJ9C + 8OgXuhvwmEP2HgGP0H9V1tAK1aMUnV0Ic+4GkHsjtggPkMp43CLDYzXgsQMOdiAeheCT + Uh6jMiArnz68TQG5lQLSigHkAEFcNcfgcQTFo5Obl99WBo8PyyoRj1L2cUHNXRmQz0vu + 511IPyJAQHbG415Qj3mgHhGPgnJJSQ9WAZDvG2sBkLdyzkKKVXCABDwOGKRF8Gg1k4/H + p4hHSXu4wCbfGZD38i6kHQZArmVSrKOFsQfZBR4Pp1/Iu1fyHPEoMJeU9HCVAPmaADL7 + 7LHEqK1+q9ycZliZ0xSr5itIRTyuIurx2NmcWw8JHt9jclXSPi6oyXcLyM1rPVxmwR6k + IADJ4XE03Xv0WLslYu/hNMSjoDwRB0ss8D1ALnYUCiBleDS3muHkBniMAjxmEzy+Rjyi + owvJAjJAfmGKdJ4/ogpyTwQF5NRJQgAkH4+zXDzWbmbx+AjVo5BcEccKFugGkPujQvxW + CQWQfDw6Ll7lF4J4RN8WqgXkgPzc0db8phoAeS0r7ZAyIPv1/UVjH+bco+5o00lTGTzu + OZyWde0ewWMzlOZ8wdIcoTqnBMfdCZBPHtzKPnNUDkhjUsWqySlWDo/GoB4ZPO5H9ShB + TxbJlBlA/v31ry88QJ46tGf75jUeC2bJFKTmArJvP3ItgByP2/ccYvH4BvD4GfEoEkeV + yDQ4QEJ/1k8f3je8rnzy4CYB5K4Q35WgIC3NWUD27aOZC9Y+fZnSHBaPviFR+4+eyb75 + 4AmbXMUOOhJxZLFMkw/IVlCQzx7dvZZFABnAAXIkuShAUwHJx+MCjzWbAY+nsq7dBfXI + 4ZHtaCWW74XzELcFlAH5AgB5hQAymADSFgCpP0Jr4AAN7c8Kmx2kclXf2NwS1ONK35Bd + gMcrFI8NpDQH8Shu7xXh7DhA/gkKkgfIWApIB6IgR2oP1lRAMngcSZOrgMeA7bEMHp8B + HltBPcrOIYvww+GURGmBbgC5jwBy0VwGkEM1FJAEjwMZPNo6LlrpG8zh8cVrxKMovVUC + k+oEyOK7uVmnDrKAtJlkYqCxgCR4HKxN8eiAeJSAr0phikqAfMUoyBQZIM2M9YdrJiAp + HocO1zc2s7SdS/G4j1GPiEcpOK5Y59glIE8ejA0PWO0x38FmosYCksWjgclEG4rH8NiD + p7Jy7xajehSrq0piXjxAfoQ9SABk4c0rp1P27Qz28SQKUlMB2QUeU06T5OqLV6geJeG5 + Ip1kN4CMCd+0eikfkBpWE8DH4/ylqwMAjycRjyL1USlNiw/ItncAyMeFNy7LAWlhZqSJ + ClKORwtQj54+wTv3AR5vFD4meGz7iHuPUnJhcc2VB8j21ua6qmfFd3KzTh5kAGnPKUgN + u/oRKslJcpWqR8DjpvAYgsc7qB7F5ZtSnE03gEzYGeTjuXCOrUYCksOjkRngcaGnTxDB + 42UGj+8Qj1J0Y9HMmYtH2sC8tamuqowF5DaiIBlADhusWc0DeHi0V8BjVV1zazuW5ojG + OaU4ES4g4ZjHR6IgK4iCzEhhATldAwHJx+MciscExKMUXVeUc+biUQ7Ioju5508ciNm2 + yRsAaU32IDULkBSPw6h6pHjcFnPg5HlQj2VVdU0KeMQOOqL0WJFPigtIaD/HArKAAnJH + 0IYVC6GB+TgNS7Hy8Dgd8LghaEdCSgZRjxWvGlA9itxZJTA9Lh47A3Kj95J59tYTNAyQ + cjxa289f4r0R8HgC8FiEeJSAs0philxA8gB5nShIOSD1oIq1v4acgwQ89h84dLie0TgL + wOMKDo8FLB6hwRx7DBlXq1JwXhHOkYtHCsgWkmItus0oSBaQYw10NEdBMnjUMTCZYG0/ + D/EoQn+U/JS4gGQBWVPxuOD6pYyUeAJIV6ogNQeQXeLx0nXEo+S9WDwG4OJRCZBJMWEE + kHbWEzQIkBwex06wtpPjEdqTQ3K1hb/3iMtV8Tio1GbCBaQMkKUAyPSU+Eg5ILU1Q0Ey + eNRm1KMrUY/xKRmAx9KKGpJcRfUoNdcV5Xy5eFQAZGaqBgJSGY9hMUknMnMRj6J0S+lO + igtIRUAmAyDXL6cK0lBPIwDZCY+R8cnpiEfpOq5IZ87FIwfIl5BivQqAjA7z93Z3ZhXk + oN6vYiV4HDRMx4BTj4DH1MyriEeRuqV0p0UD8tu3vykg3zbUlJcW5F9KVwTkkF7fg6R4 + HKKtZ0j2Hl2Xrw/i8FiO6lG6vivGmSsBslYJkFNoirXXASnH4xQ7Z3dv/7BoFo8vazG5 + Kka3lO6cOgPyfv7F9OS4yEBGQZqCgoTmAb1apEPwCB10ZHgMJHi8mF9QCnh8yyZXv9GJ + SPc74szFYQEeID+1tzQxgDyXmhgdShXklPFkD7KXAfl9PH6SX0mOe4/icEopz6ITIEsY + QEYAIF2gSKf3ASnDoymoR5fl6wMj4wge7yMepey3Yp27IiAbWUAeJ4D0cneeqQGAlOFx + PKMeQ6MTU8+R5OrL2saWdsSjWD1TovNiAfmVSbHW15RTQB6Jiwhct8xl9rTJpoa6vaog + O+ExIu4IwWNJeU09qkeJOq2Ip80H5IcWBpA5544n7g71YwA5ZpSOVi8qSIpHLR2DMeOn + zHR29/IP3Z14/FwO4lHELintqfEA2dH2tr5aBsgtmgBIFo+6hqaTp812WbYuUAGPHVC5 + +vUbJlel7cHimr0yICvLHt7mAOnmBApyzKgRvQdIBo8jRhE8Orl5+SngEfqvyvo9YnJV + XG4p3dl0Aci8i+lH9kZoACCV8bglYi+rHqtBPSIepeu14p15J0A+ZQG51c8LAGnFAHJA + r1xWDngk7ckBj1YUj1sZ9fjwaSUkVxGP4nVKKc9MGZDPS+7lXUg/rAGA7BKPF/IguYp4 + lLLHinvuCoB831hb+fThrZyzxxJ3b/Vb1buA7AqPZ3NuIR7F7ZBSn12XgEwjgFzL7EGO + 7p09SA6Po5nk6lpQj4fTL+TdK3mOeJS6z4p5/kqAfE0AmQ2AjKKAnGFlTlOs6leQinhc + 5Qfq8RiDx9eN71E9itklpT23bgG5ea2Hyywo0ukVQPLxOMvFg+IxDfEobVeVxOy/B8jF + jr0FSBkeza1mOC0GPEYBHrOJekQ8SsItpTtJGSC//NHR1lxf/fwRpFjTDu+JoICcOqk3 + ACnH46RpBI+bI/YcJnh8xKrHL1iaI12HFfnMuwHk/qgQv1UUkMakSEe9CpKPR0fAYwji + UeReiNOTWUAOyM8AyDcEkNey0g4pA7Jf31/U9jDXAuiONp00VYbHrGsUj81QmoN4lH08 + /IPoLNAJkE8e3Mw+c7QXAcnh0RjUI4PH/ageRed3OKFuLMAA8u+vf33hAHn3WtapQ3u2 + b17jsWCWTEGqD5B9+5Fbcxg8LgD1uH3PoTQGj28Aj58Rj918SPzHorAAB0hoYP7pw/uG + 15UsIHeF+K4EBWlpzirIvn3Us2Dt05epXGXx6BsStf/omeybD55Uvm4ge4/YYE4UboeT + 6M4CHCD/BEC2goJ89ogCMnZ7AAfIkeSiAHUBksHjSKoeF3isIXg8lXXtLiRX3zS3Ujyy + DR+7mw/+c7SAkC2gDMgXAMgroCB3BRNA2gIg9UdoDRygpqsfYbODHOzQNza3BPW40jdk + F+DxCuJRyB6GY/9PFvguIB2IghypPVhdgFTCY8D2WAaPz1g8ys4h/6cp4v8YLSAYC3QD + yH0EkIvmMoAcqiZAEjwOZPBo67hopW/wrn0MHl+gehSMQ+FAf84CnQBZfDc369TB2HCi + IB1sJpkYqA2QBI+DtUcawN6jA6hHDo/FiMef+8T4twVkASVAvqIK8nSKDJBmxvrD1QNI + isehw/WNzSxt5yIeBeRDONQetECXgDxJALnaY76DzUS1AZLDo8lEG4f5gMfw2IOnsnLv + Ih578GPjqzTeAjxAfoQ9SABk4c0rAMidwT6eREGqC5BKePQB9ZhymiRXX7zCvUeN9yIc + YI9ZoBtAxoRvWr2UD0gV1wQo4HHpaoLHk4jHHvvK+CKhWIAPyLZ3AMjHhTcuywFpYWak + DgUpx6MFqEdPn+CdBI83Ch8TPLZ9xNIcobgTjvNnLcADZHtrc13Vs+I7uVknDzKAtOcU + pIqvfoRKcppcpepx6epN4TEEj3dQPf7s18W/LzQLdAPIhJ1BPp4L59qqBZAcHo3MAI8L + PX2CCB4vM3h8h3gUmkvheH/CAlw8foUq1vbWprqqMhaQ24iCZAA5bPDvKgUkD4/28/l4 + rKprbm3/LG8R8BPzxL+KFhCEBbiAhGMeH4mCrCAKMiOFAST0Z1W9guTjcQ7FYwLiURC+ + g4PseQtw8SgHZNGd3PMnDsRs2+QNgLQme5CqBSTF47CRBqAeKR63xRw4eR7UY1lVXZMC + HrGDTs9/fnyjplmAC0g5IAsoIHcEbVixEAA5TsUpVh4epwMeNwTtSEjJIOqx4lUDqkdN + 8xYcj6otwMVjZ0Bu9F4yz956gooBKcejtf38Jd6bAI8nAI9FiEdVf3l8v0ZagAtI2sCc + KsiC60RBygGpB1Ws/VV0DlKGx3EWgMcVHB4LWDxC/1X2GDKuVjXSe3BQPW0BLh4pIFtI + irXoNqMgZYDUUZ2CZPCoY2Aywdp+3hLvjYjHnv6++D6BWYALSBaQNRWPC65fykiJJ4B0 + pQpSdYAEPPYfOHS4npECHq8jHgXmQzjcnrMAF49KgEyKCSOAtLOeMNZAZYDk8DhWAY+3 + qXps4e894nK15z44vkmzLcAFpAyQpQDIdAVAaqtGQTJ41Gbw6ErUY3xKxiWCxxqSXEX1 + qNmOg6NTiQW4eFQAZGaqGgDJw6MdUY9hMUknMnMRjyr5zPhSoViAC0hFQCbHRwatX04V + pKGeSgDZCY+R8cnpgMdSxKNQXAfHqQILcPHIAfIlpFivAiCjw/y93Z1ZBTmo56tYu8Bj + auZVxKMKPjG+UkgWoAH57dvfFJBvG2rKSwvyL6UrAnJIj+9Bcng0JMlV1+Xrgzg8lqN6 + FJL34Fh72gJKgKxVAuQUmmLtcUASPA4apmMwdsIUO2d3b/+w6CQGjy/rmjC52tPfGN8n + IAt0BuT9/IvpyXGRgYyCNAUFCc0DerRIh+AROujocXgMpHjMLygFPL5lk6vf6MAEZEgc + KlqgByzAA+Sn9pYmOSBDqYKcMp7sQfYwILvHYy3g8ZP83CPuPfbAF8ZXCMoC3wOkCxTp + 9DwgZXg0ZdRjYGRccvrF/PuIR0E5Dg5WJRZQBGQjA8hzqYnRFJAzVQBIGR7HM+oxNDox + 9RxJrr6sbUQ8quQj40uFYwFFQNbXlJcQBXkkLgIUpMtsAkjdHlWQCnh0Wb4+MILBYwmq + R+E4DY5UZRbgA/JDCwPInHPHE3eH+nu5OwMgxxjoaPWgguThcaazu5c/4PH4uRzEo8o+ + ML5YWBZgAfkV9iA72t7WV8sBuW6Zy+xpk3sWkCwedQ1NJ0+f7bJsHeDxCFGPgMd6Jrn6 + 9RsmV4XlQTjanrRA94D083JzIoAcNaLnAEnxqKUzasz4KQSPfqG7eXiE9uSyfo+YXO3J + j4zvEo4FugHk3ogtPQ9IHh6nETxukeGxGvDYAQc7EI/C8RwcqSosoAzIyqcPb1MFuZUC + 0ooB5IAeuYuVweMIikcnNxkeH9LkKuJRFZ8X3yk0CygD8nnJ/bwL6UdUAMjOeNwL6jEP + 1CPiUWheg+NVlQW6AOStnLOQYu1xQAIeBwzSIni0mknwuJVRjw+fVsLeI+JRVR8Y3yss + C3QG5L28C2mHAZBrmRTr6J7Zg+wSjxfy7pU8RzwKy2NwtKq0gAIg3ze+BgV5K/vsscSo + rX6r3Jxm9JiC7AqPZ3NuUTy+Rzyq8hPju4Vkge8A0sNlFuxB9gggOTyONp1Mkqtrt0Ts + PZyGeBSSo+BY1WKB7wFyMQDSnO5B/myKlYfHGU5uq/y2RiUeO5tN8Pi6EfGolg+NPyIM + C8gA+YUp0nn+iCrIPRGb1xJATuoJQPLxOMvFY+1mxKMwnANHqXYLdAPI/VEhfqsWO/YM + IGV4NLea4bgY8aj2j4w/KBwLKACyub4aAHktK+0wC8ipckD+8qOPHI+TpjJ43EPV4yM2 + ufoFS3OE4y44UhVboBMgnzwgKVY5II1JFevPKEhlPIZE7Uf1qOKviq8XrAXkgPzc0db8 + hgHkqUN7tm9eAwpSBsh+fX+Uj8ytObqjTXl4zLp2D/D4prmt4zPiUbCugwNXgQU4QEJ/ + 1k8f3je8rnzy4Gb2maMASF9GQbKA7NvnxwKSw6Mxqx4JHs9k33rwBJOrKvia+ErBW4AB + 5N9f//rCAfLutSwCyIA1Hgt+HpB9+5FL5Rg8LvBYs3n7nkOnEI+C9xqcgKos0BUgrwAg + d4X4roQUq6W5sT5RkD949WOfvkzlqrG5JUmu+gIej57Jvknw2ED2HrH/qqq+K75XoBbg + APknALIVFOSzRxSQsXJAjiQ36fyYgmTwOJKqR8BjAIPHu1Q9tlL1yPZDFqjxcNhogR62 + gDIgX4CCpIAM9l25yNGWAeTAHwMkbHaQgx36DB5X+obsAjxeQTz28CfE14nJAp0AWcwD + pANJsY7UHvxjgOyEx1iiHu8+egbJVYJH2TUdYrInzgUt8DMW6AaQ+3YRQM61tTQjCvKH + AEnwOJDBo63jopW+wRweX6B6/Jkvhn9X1BboApC5WacOxoaTFKuDzSQTgx8EJMHjYG2q + Hh2oemTwWIx4FLU/4eR+zgI8QH6EPchXVEGeTuEDcvjQHwCkDI9mlrZzKR73MeoR8fhz + Hwz/trgt0CUgTxJArvaY72Az8QcByeLRwGSiDcVjeOzBU1m5dxGP4vYmnN3PWoAPyDYK + yMIbVwCQO4N9PFkF+QOApHgcOlzfmMGjT/CufSmnSXL1xSvYe/yIe48/+9nw74vVAp0B + eSc36+TBmPBNq5fyAfmfiub4eJzvsToA8HgS8ShWF8J59aAFFAD5DhTk48Ibl+WAtIAU + 638GpCIePX2CdxI83iikeGxDPPbg58NXic0CPEC2tzbXVT0r7gqQ/+lu5D5McpWqx/lL + V28KjyF4vIPqUWy+g/PpeQt0A8iEnUE+ngvn2lqYGf1XQMrxaAHJVRaPl28UPgb1+A7x + 2POfEN8oIgtw8fgVqljbW5vqqspYQG4jCtKepliHDf79PwASDlqRvcfOeKyqa25tx9Ic + ETkPTqXnLcAFJJyD/NgGCrKCKMiMFAaQ0MD8vwKSw6ORGeBxoadP0M6ElNOIx57/cPhG + UVqAi0c5IIvu5J4/cSDmBwFJ8TiM4tGeqMdtMQdOngf1WFZV16SAR2wwJ0p/wkn9pAW4 + gFQG5I6gDZ4L/zMgeXicPofFYwbBYwWqx5/8UPjXJWEBLh67AKT30nn21qRI598ryC7w + eALwWIR4lIQz4SR/3gJcQEIDc1ZBFlwnChIAuYIActx/SLEq4XFD0I6EFMTjz38jfIN0 + LMDFIw+QtxkFudF7CQBygomBzr8FpByP1vbzlnoT9Yh4lI4r4Ux7wgJcQLKArKl4XHD9 + kgIg9aBIp/+/uElHhsdxFqAeV7B4vF7AqkdoT87e0oHJnJ74cPgOUVqAi0cKyBayB1n0 + g4Bk8KhjYDIB8LjEeyODx9uoHkXpNzgpFVmAC0gZIEspIOOJgnSlClJP+98AEvDYf+DQ + 4XpGMjzGp2RcInisIaU5iEcVfT98rbgswMWjAiAzYQ8yjChIO+sJY/+dgvweHlv4pTm4 + XBWXA+FsetYCXED+HCAZPGozeHQl6pHBYynisWc/F75N5Bbg4pEPyKuZJ5L+GyA5PI6d + YG1H1GMYJFczcxn1iHgUuQvh9HrSAjQgv337WwGQ6cnxkUHrl1MFafjPCrIrPKaDekQ8 + 9uSXwndJwQJKgKx9CSnWq5mpSdFh/nIFOej7xzwIHgcN0zGQ4zHpROZVxKMU/Afn2MMW + UADk24aacpJiJYAM/LeApHgcoq1nSJKrrsvXB0XGJyvj8Rv9mR4eOr4OLSA6C/AA+am9 + pUkBkO7OdlNoivW7gJTjcYqds7u3f1h0UirF48vaJlSPonMYnJBqLdAZkPfzL6Ynx3GA + NAUFCd11ui3SIXiEBnN6hqYMHgMj4wCP+QWl5TUNb9m9R8Sjar8hvl08FugGkInRof7e + BJDjx8Ae5HcASfGopWMwZjyDx9DoRB4eP8k7duDeo3icBmeiOgt0DcgjcRGB65a7zJ42 + 2dRQlwFk1/2S2fbkhqaTp812Wb4uMCLuSPrF/PuIR9V9MXyzmC2gCMhGRkGeO564O9TP + y81pptX4MaNIf9ZubtLh+q+OGW8108nNyy90d+LxcyS5StUj4lHMnoNzU4kFFAFZX1Ne + QhTkkb0RW9Yto4AcTQHZdX9Wcskj2558tsuydVsi9lI8lpTX1KN6VMnnwpeK3AJ8QH5o + IYB8eDuHAHKr3yo3xxlW5gSQ3XTXgWwO12COtCf32wp4PJtz+yHgsbGlHfEoctfB6anC + Aiwgv0KRTkfb2/rq8pJ7eRfSDu+N2LzWY8Esrj9rlwtWWirHXvK4wGPt5og9h9Mu5N0r + eV6NeFTFp8J3SsACyoCsfPrwVvbZY/ujQmT9WZnmAZ0zOtxBK3LJIzSYg/bkx85m33r4 + tBLw+AHxKAHnwSn2vAWUAfn80b1rWacO7dkesIa9G7mbLQ+yXB0Emx1QKgfXdECLALY9 + OcVjB5x7/PoN9x57/nvhG8VtAQVAvm98Xfnkwc0rZ46S9nPMzVZMTUC/Tt2uyHIVsjlM + LQActKIddEiDOaY9+Z9f8ZoOcXsOzk4lFlAAZPOb6mfFd2n7uW0boSZgJtQE0C2PX/sq + L1iZ5eooY3Oy2QGlcswtVrSDThviUSWfCl8qAQsoApJtP5eRAlXlsOXRfUYHNh/hZAfT + sgOyOVsiSKkcc4vVe1SPEvAbnKKKLMAHJGk/xxy7olsei+faWpoZwU1zv/dXXrAyy1Xm + ZMdCT9+QKKgFyGFvser4/AXVo4q+Fr5W7BbgAxK668CxK1ITcBgyOqvJXeXklAfU6Cgv + WKEYYODQEaOgdHUmnOwg2Ry62QGV5KTBHKpHsXsNzk9lFpABEtrPwbEruuVBMjpBG5gi + VqjRge5zfRUzOiS7CgeRTSZNneXisS4Q7iQ/fQWyOZWvoRYA8aiyb4UvFr8FZIAk7eeg + JoDJ6MBNc36rFjvSBSsUlSuXBIB8HAzZVXrLo6cv1OakwrU5xWXVb5rxkkfxuwzOUJUW + YAD5N9xs9enDe9IOktwTAEWsZMFKjiWTmjklAUlqV4cO14fs6gwnd++NdLnKnOx4R7M5 + uPeoyg+G7xa1BThAkqI56JdMLtLJgRqdnUHrmQwrNLtSFpBEPmqNoMUA8z3WbImMp8tV + WpvT/hlbBIjaXXByqraAHJB0wfr8EWxBnjgQvdVv5SJyVTmcgiQCkr8D2YfIx5EGzMnH + DcG72JNWVWS5Ks/mqHrc+H60gBgtIAPkl8/sgpVmWMOhJMAJDnkY6w8HAakQj31/lcnH + RSv9QqEhcta1e3DSqoEuV7E0R4xegnNSmwU4QNIFK8mw3rxyGkoCtpAaVtjxGDUCWnko + 7EBCOgcuzqHy0Q3k454jUAxQ+PgFza7KlqtqGz7+EFpAVBZgAQkZHViwvqkmJQFwCnIX + KyChGSQcglSOx6HDye6jHdSSb44g8hGOdsDFAK1QK4eV5KJyDpyM+i3AAvIvyLC+a6h+ + XgKHPE6CgPT1hJvKJ5uOpgkd3gYkm84xmWjjsMBjXdDO/cfoRR3Vb+BiAJCPeLBD/V8Q + f1FMFuDi8X+f25kdj/yLaYdiyQ7kXFs2ocOv0IF4JGetTCeRi6w2BO9OhI7Idx/BUat3 + bZ++cA1YxWQfnAtaQK0WYALyKwhIUqLz5MGNyxnJe7dv9HajCR1yD6tiPJL06mjTydAS + GaoBYg6eysqj98q9/0B2O+jL1Dp8/DG0gKgswMYjCEioKYdrdG5ln0mJh4oA6AUJZ66U + E6yw3cFW58DVAH5hsYfgnscCLp3zFeNRVK6Bk+kNC5CA/PY3P6FzbN+OLWs9SIIVzkAO + Vdjw6EO2O/QM4aoOx8Ve/ttIevVGIRSvknQOKx97Yw74m2gBsViAiUdI6DAlrHdyM0mC + dZ3HAgebiWMNYMPjt37yggCIR9juMDK3pNVy2+OSSTE5VOdgPIrFHXAevWwBumD9+y+o + CCAJVqjQOX8iMYq/4dFfOR5J9So9bAXbHWfgJquyl3W0OoepBujl6eDPowWEbQESkNCc + lVTowL3IZMMjaXcwPXI1yZRsQPLjkZQDQDX5eCs75yWrAyISSDwWlVXx4lHYtsDRowV6 + 2wJ0wfqVxuOripL7eVmnDkSH+KxwnQN9PAxGag3iF+hw8QjlAEugHCDhKFyEDPHIbD9S + Pvb2bPD30QICtwAE5DeIR7oBWXo/78Kpg7x4HNZdPJLyHIV4pNuPAjcFDh8t0OsWYOLx + D148xmwlfGQLdLrhI8Zjr384HIAoLfBj8YjrVVE6A06q1y3wnfUqKWBV4CPsdyjlc+jd + cvL8aq/PBgeAFhC2Bdh4pCcgO+dzFA5c0f3H7+53CNsUOHq0QG9bAMLx/779y/0O5XoA + clsHd9wK63N6+0vi74vBAiQe2XqAGubGju/WA0C9nJGZ5QxHNy9/OI6ccfkGXPaI9Tli + cAScgyZYgOIR4vFT27v66ufFUC+X+r16ObaeHFrNrYJ68sPpF6FXAL0egBxHJu/ShDnh + GNACQrUAE49w5aPsgoBj+3cEMvXk5MIOxXryfsx5KwvZeasLcN6q4lXDe3q7HHkXBqRQ + PQHHrQkW4OLxYxvTwwPOWyVEbKY3sHZx3grOI5Pr5bjzyEknsq7BeWTmOiv2PLImTArH + gBYQpgUo0r59/ZPcwAoXWkETyIzkuO2b4IK5meSCOaXzyL+Q61ehW8BE6BawbF0QXPeY + mXsHes2RTuVf/mSvlxOmIXDUaAENsACNx7/hfoD2lsbXL+gN5Ydjt/mTjgEW0OIKLmDl + 3w/wCxSwkm460Bt5/lK4Djnh6Jls+QYk3p+jAR8UhyBkCzB4pOlVeryDlJMfjAn181wI + FyKbQkcduKCcd58ViUdSEMAeuNq+N5lNsEI3HUjoICCF7As49t63ABuPNJ1DOurcyT2f + mhglP24FDQMU73ukFwQYjbOwnbsYEqwx0G0O2neQhM4H6G6F8dj7XxRHIGAL0HCE0x3k + ug4mnZNz9ug+kl5lrwdQbqjD9AugF1q5rvAJ2Z3Ev2COu/ARU6wCdgkcei9agFuuMvKR + vV4uLiLAe4nzTHqdleJ2xy+/9Ok3gEno2JD2j0E79x1jTkDWNdMLkRGQvfgx8aeFbgEW + j6QagN4OQE4/ph2KCaPpHNKxnDSAVOjfQS5E5i4oJzcExBEBWfiEaRggW7AiIIXuGTj+ + XrAAF45cuwAiH6E6Zze9HYCeftQerNSwvA9J6NCKObhhjghIuIGVbagDNyJju/Je+Ir4 + k2KxABePTP9HtmF5SsKOQOZ2OaZ9h0J69ZdfaIMrciPytDkgIIOjuIZzcIUOdPDgMjoI + SLH4CM5DbRbgwlHWH7n0PtNuDqoBnGdajR8zavjQ3/mnH8m9j5DQIQJyLNmB9Fi7hV2w + 0hJWyLBCRofp4YEBqbbPiD8kDgsw4Qh3IQMeaXcruttxIknejpXKR4XtDpLQkTcshyMe + 22IOQgdI2sKDlOjIAYkBKQ4vwVmoywI8PH4mxTmQXSXdHxN2BK3rtl05xCMISNIywBRK + ysmCddf+Y2fhDlbScq6lHbYguZoArCtX13fE3xGFBXh4hM1HOGsFd6/mZUFzq/BNsNtB + mnfQbqxK8pEIyAGkxRWzYF2zOWLvEejhQXvqvGMBiStWUTgITkKdFpCFI6hHwGNT7Uto + xpoDzVijQnxpsdw4ZrdDablKF6yw46FvbGZJM6yh0YmpmVdpDWtTKwBSnmLFFas6vyf+ + lqAtwIUjUY8Ej3A1Oc3mJMdFQC8dUpxjYjBy2GDYfeRXr9KETj+6YCUZ1tkuy9YHRsan + ZFy6XlBaAYeuFAGJASloF8HBq88CbDhCMofisbWpror0Ks9MTYoO8/diej+OGgHNyhUO + d9C+On3ognWEAfQsJ008Nm6LOQA1cwwgiYIkgGRXrBiQ6vui+EtCtgCHx7+/0uQq4LGi + tOD6pYyU+B1B65e7zIZWAYa6UJyjvNtBIrIvZFihJMBwHM3obAjakcAHJOxBylM6GJBC + dhIcu7oswIUjrFbhZAenHq+eP3EgZttGms2ZMNZAB852dF6uEgEJC1a4JMAEtiDnLfXe + RABJFeTL2saWD59gzwMDUl0fEn9HBBaQLVaZ1WoHafxYXnI/n8HjBtIpwAKyOcOHDuzf + eblKdjygJAAyOkZm9BYdHwJIkmIteV5dD1XlsGKFKh1uxYqEFIHD4BRUaQF+ONLV6vuG + 15VPH1L1SPE4z96ay+Z0yq4yGZ3+sAUJgJxoA7cEEAWZlHou59bDJy9ekZTOHwoSEgNS + ld8S3y14CyiGI12tvql+9uhe3oX05PjIoA0ryM0AZkb6gEfI5ihnV2k8Mhkd2PIggFyx + ISgy7kgaFOkUQyPIppYPHxkJiYQUvKvgBFRvAS4cSWoVxONnWK1CMudx4c0rZ48lRoeB + emTxCDfndJXNkWd0CCBBQS7x9g/dnXj0zGXY8yivftPc2s5KSFlAIiJV/1nxF4Rpgc7h + +A5Wq7DXkZt16tDeiMD1y4l6JHjU6jqbwyxYf4ManRGMgnRdvm5LROxBktKhK9a3bbDp + QXM6GJDC9BEctdoswIXjN4aOUEf+vrGuClar+RczUvbtCvHzcne2s54AtQBks6OrbA67 + YCVbHiTFOsXOyW2Vb/DO+OT0C9fuFJVVvgYJCTkdDEi1fVP8IaFagIvG/5OF44eWpjfV + 5aUFN7LPHk+KCQ9Y4+EymyRXGTx2mc1hAfn7IC1IsY6bPG3WAo/Vm8KiE4+evpR/79Gz + qtrGrgIS16xCdRoct6os0FU4NtfXVDyB3Or5k4f2Rgb5eC5ynGE1fizgEdRj19kcGSAH + a+tAkY7VjLkLV6yHFeuB1HPZNwpg06Ou8T1HSN6+B573UNVnxfcK0gKyaOTRsbW5/tWL + p0V3crPSkxN2bfXzXjLfYeokU0Oy9zjgt27xSGsCYA9SW2+06SQb+3nuq3yDdsQdPnX+ + 6q1CyOlwAfknKQyQi0hEpCD9BgetCgsoRCOpkvvyx8cPrc0NryCXczfvYsbRxOhtAWuW + uUAyx3zMKB2oJP8OHmlNAOxBDhsxiux5zHbx8PbfuishOS0r9/aDxyQg37W1f4J9SBqQ + GJGq+KD4TgFbQB6NFI5f//zfl88fP7QAHSvLiu/lXz5zPCl2+5b1sFqdOQWSObra/4BH + Akgo0iEpnbHjrWznui5bG7Bt9/6U9Au5tx+SgISkTjsUBvzvz6+KiMRVq4C9CIfeIxbg + BSOJRrrtSPYd2XC8f/3KudSDIB59V7nPc5g62dRIf4TWINh77LIWgKhH8vTpC1WskNLR + MzSdaG3ntNhz/ZbwmMSjGTQgn1fXNrxt+QBpVopIhUUrjKZHZoUvQQsI0QKK0Ui3OWCt + +qm97X3Tm5oXZcUkHE8eitsR4ue9lORWzcYY6MBex4Bfv6MemYD8lVmxQo51qgNISJ/A + iJjEYyQgH5Q+f/m6vvk9XbP+jyxalSMSQ1KIroRj/kkL8GPx/4CNEI1UOX7u+ND6tqGu + uuJp0b3r2ZknD8fv2rpxtYfrHFur8bLV6nfxyACSWbGOMQcJuWCJl29QRGwSDcjCkrLK + mjeN71o/dICK5CKSpyPZgf3k9PCvowWEYgHFUGSCkaxU//cFlCPAsbnhdVX5k6K7+Vcy + Tx5JiArdtHb5QseZ1hNJbpWsVrvf66DLVbpi/RVWrEO1dUebgIScAzkdPxKQR9Ozcm8W + FD+tgDVr83smImlIUkrC/ycojwz/M1pAUhaAGADVSNAI0fgHicaWt41val4+K314J+/y + uZOHE6LCAtatWOxkbzN5nBHNrf7japWuWPuRFSsjIafM4AIyEQIy58a9osfPX9bUQUS2 + fej4+PmPLyQk//oK2R180AIStwCEwV9//UnQ+McnEo3vmupfV1eUPSq8fe3SmROH40k4 + ero5O0wj4hEK5cjW4z+sVhkF2e+3Ab8PGQZ1rKaTrGfOdaWEjElMScu8knf7waOyiqpX + bwgj29o7PpGQ/PK///0Jz1/4oAUkawESAf+DUAQyQjB+ADY21ddWVz57XHTvZu6F08cP + xe8KC1jv6TZvFuw8jiVbHf9qtSoLSNj00BllPI4XkPuPnDx7MfcmILLsRTVEZNNbCMkP + 7R0fP33+/AcJS3zQAhK2wB9/QCh++tjR3gZobG6sr615Wf4U4JiffT796IG9O0MJHSEc + LcePHa07XItUAvxDbpUEI3ngpoD+REKONDAeN9mGEtI3MHx3wqHjGVnZ+bcLi588e1H1 + qraehGRLaxsEZXtHx0fyfMIHLSA5C1DX7+joaP/woa2t9T0EY8Ob19Uvy8tKHgIcL507 + lZIYGxmyce0KJhxNRuuNgMKcf7daZQKSSEheQLos9fLZHLZrb1JK2rmLuTfuFBY/Lqt4 + Wf2qrr6hsentu/cQla0Ql/igBSRrgdbWlpb37942NzVAMNZUvXj+tKTo/q287KyM1MP7 + YiKC/NesWEzpaGII4fhvxSMNR1oVAAGppa3LEHKOy5KV6zdtjYzZfzg14/zl3Jt3CotK + y55DSNa8rn1T39DQ2NjU1AzPW3zQApKzAPH85qamxgYIxbraV9VVleVlTx49vH87P+fC + 2VMpSXFR2wL9vJctcnaAxaqJoT4Tjv+81cEEI/lXUqYzAJKsEJBjQEPOmDPfbcUav6Dw + qLiklJOns67k3rhd8PDR46fPyl+8rKquefX6dW1tXV3dG3zQAlK0APh+be3rVzXVEIsV + z8uelBQV3r2Vl3PxXPoxgGPk1oANXh6uTvbTLGg4Qmr13+w8ysORF5BEQ06cYjtr3qJl + 3j6bQyNjEg4ePXUm63Ju/q27BQ+LSx4/LXtWXvHiReXLly+ryFOND1pAQhagTl/18mVl + 5YuK8ufPyp6UPip6cO/2jWvZF86lpx5J3LsrPMh/recSl7l2Uy3MTUZTOtJcTld3WPFj + UOHPMkJCQJpOsJru4Oi6dOU6/6BtO2P3Hzp68nTmxezc67fu3Ct8WFT8qPTxkydPn5bh + gxaQqgWePn3yuLTkUVHRg/t3b9/Iu3o562z68eSkuOiIrQE+3ssWz58zw2ay2ViSyqF0 + /Dc7j0oB+SuzZB05ysh0vMXUmXMWLF7mvWFT8PZde/YfTElNP5sFIZl3/ebtO3fvFxQ+ + ePDwYRE+aAFJWuDhw4cPCgvu37t7+9aN/Nycy1mZGaeOHUmMi44MDfRbu3Kpq5PD9CmT + xo0x+OFwpLseEJCQZdXRNxxrNtnadpaT6xLP1b6bQ7bviklIOnL0RPqZzKxLV3Jyr+Xl + X79x8+atW7fxQQtI0AK3bt26eePG9fxruVezL1/IOptx6njKwf17oyPDgvzXe3kAHGdO + s5xgamygO/wH6UhQySxZISBH6EFWZ6LlNLs58xd5rFzjGxAcviN6774DR1JST6WfPpuZ + lXXx0uXLV7Kzs3PwQQtIzgLg+FeuXL508ULW+XNnMtJOHEs+lBgfsysiNNB/vfdydxdH + h+nWk81NDEeN1NYaTFI5/3mxyqxcSUDCtseQYcN1DYxMzSdbT3eYu2AxRKTPpqDQiJ3R + exISDx5OOXr8xKm09PSM02fOnDmLD1pAchYAxz99OiM9Pe1k6rGU5ENJ++NioiK3BW/2 + WwfR6Oo8e+ZUy4njxozW14Fw/H3AD4cjJSQUBgwcoqU9Un80INLCxnaWI0Skp/d6v4Cg + 0O2RUTF74vYlJh08dPhIcgo8R5nnGD6aaAH26+C/8S3wMx+KfQ/x/OTkI4cOHkjcn7A3 + ZvfOiG0hWzb6rFm1DKJxjt30KQBHIwPdEaRmdcBvP0pHdsn6a3/YiKRrViMTs0mWU2fM + cpy/cMmyVavX+20KDA4Nj9ixa3d0TOyevXHx8fEJ8OzDBy0gGQsQj0+Ij4+L27snJmZ3 + 1M6I7WEhQZv9fdZ6e3osdiHRaG0xwRTgOJKRjr9BGcB/2uhglqrcv8KSlaRZyZoVEGls + aj7Jaqqtwxxnl8VLl6/0XrvBzz9gS1BwSGjYtvDt2yO4JxIfTbQA93nw33kW+JkPJX/N + 9vDwsNCtIUGBmzf5+axb7bXCw33hfMfZNBrHjTWkcOSk40+EIyxZGRE5kCBSd9ToMRCR + ljbT7WY7znNZ5O6xfKWX95p1G3z8/Pw3bty0KQCezfigBaRkAeL0mzZt9Pfz8/VZv3a1 + 96oVy5a6uS5wmuMwY9oUiwnjYKmqp6M9bMjPrlVZRPaRIVJLW0d3lCFE5ESLKdNs7WbP + dV7gsshticey5StWrvLy8oZnNT5oAclZgHi+t9eqlZ4rlnksdV/s6jLPcY7DzOk2lpNp + NJKl6lACR1ir/ovzx9zStNt/pwHZ/3eyaNXW0Rs12tjEbMJkS+tp02c6zJ7j6DzfxcV1 + 4aLFbm7u7kvgWYoPWkBCFiA+v8Td3c1t8aKFri4L5jnNnTPLbsZ0GyuLieamY40MIBrp + LgeTyPmptSoXoRwiYdEKEak7ysBorCmEpIWVzbTpM+zsZ82eM2euo6OTk7Oz8zx80AIS + swC4vbOTk+PcOXNmz3Kwm2k7faq1pcXE8eNMjA0N9Eg0kqVqf7iboyfgyMRkn75MXodG + 5IiRegajjceajhs/cbKF5RRriMrp021nzJg5c6YdPmgByVkAHH/GDFvb6dOnTbWZYmUx + edIEM1OTMYYGo3R1ZNFIdjl6BI4sJGlEwtbHwEGwGzlcR1ffYLTRGIhJs/ETJk6abGFh + YWlpZWU1BR+0gOQsAI5vZWkJMTB50sTx5uMgFoGMo/RGjtCmuhHY2NPRCEEJi1aGkb8P + GjwUQhIoCTFpCEFpYmoKcWlmZm5uPh4ftIDkLACOb25mNm6cqanJ2DHGRqMNRunr6kAw + wkJ1IFmpqiAaCSXZiKSQJCEJmBypq6c/ygDC0tDIyMgYnjH4oAUkZgHi98ZGRoaGo0cb + QCjqQSwOBzIOgZQqKcdRUTSSiKS7kb/++huEJFByyFCtYRCUI3QgLHX19PT0uWcUPmgB + aViAc3lwf13dkSMhEodr01gEMrJo7FndSMOQ9y8MJJmQBDEJQQlRCWFJn+H4oAWkaAHG + /YcN09ICLA5mYpGsU3s4icOLQ94faUhCGR1gEjj5+8CBgwYNGjx48BD5MxQfTbSA/APh + n2QW+JkPJXvJEIhCiMNBA4GKvw8Y0B9iEZapqiUjLyJh3UqyO/36wdr1t/79IS5JYOKD + FpCyBSAKIBZ+I6FIY7Entzf4wdfdn2lQsmH5Kzy/4YMWkKgFiP+TOCSRCFjs0a3G7uKv + m39Ofh0eGAc+aAHpWoAJA3VDsZugxH+MFkALoAXQAmgBtABaAC2AFkALoAXQAmgBtABa + AC2AFkALoAXQAmgBtABaoFct8P/b5umcCmVuZHN0cmVhbQplbmRvYmoKMjQgMCBvYmoK + NDQ4NjUKZW5kb2JqCjIwIDAgb2JqCjw8IC9MZW5ndGggMjEgMCBSIC9UeXBlIC9YT2Jq + ZWN0IC9TdWJ0eXBlIC9JbWFnZSAvV2lkdGggMjU2IC9IZWlnaHQgMjU2IC9Db2xvclNw + YWNlCi9EZXZpY2VHcmF5IC9CaXRzUGVyQ29tcG9uZW50IDggL0ZpbHRlciAvRmxhdGVE + ZWNvZGUgPj4Kc3RyZWFtCngB7dCFQQIAAABBsTswEAxsBQWD/XdzkL8b4Vartj8MGDBg + IDnwiwEDBgwkB34wYMCAgeTANwYMGDCQHFhiwIABA8mBBQYMGDCQHPjCgAEDBpIDnxgw + YMCAgeDAHAMGDBhIDswwYMCAgeTABwYMGDCQHHjHgAEDBpIDbxgwYMBAcuAVAwYMGEgO + vGDAgAEDBoIDzxgwYMBAcuAJAwYMGEgOPGLAgAEDyYEHDBgwYCA5MMWAAQMGkgP3GDBg + wEBy4A4DBgwYSA7cYsCAAQMGggM3GDBgwEByYIIBAwYMJAfGGDBgIDpwjQED2YERBgxk + B64wYCA7cIkBA9mBCwyEB84xYCA7MMSAgezAGQYMZAdOMWAgO3CCAQPZgWMMGMgOHGHA + QHbgEAMGsgMHGDCQHdjHQHhgDwMGsgO7GDCQHdjBgIHswDYGDGQHtjBgIDuwiQED2YEN + DBjIDqxjwEB2YIABA9mBNQyEB/4BLgXylgplbmRzdHJlYW0KZW5kb2JqCjIxIDAgb2Jq + CjM2NgplbmRvYmoKMjUgMCBvYmoKPDwgL0xlbmd0aCAyNiAwIFIgL1R5cGUgL1hPYmpl + Y3QgL1N1YnR5cGUgL0ltYWdlIC9XaWR0aCAyNTYgL0hlaWdodCAyNTYgL0NvbG9yU3Bh + Y2UKL0RldmljZUdyYXkgL0JpdHNQZXJDb21wb25lbnQgOCAvRmlsdGVyIC9GbGF0ZURl + Y29kZSA+PgpzdHJlYW0KeAHt3Qs/k2EYB+A5j2HOZpjz+TxzNkQSSSJJJJEkkkgStq/e + B+n6CPtte9/nuQ//K51KPT483N//vbv7c3v7++bm1/X11dXPy8sfFxffz8/Pzr6dnn49 + OflyfPz56Ojw8NPBwcf9/Q97e7u773d23m1vv93aerO5ubHxen391dray9XVFysry8vP + l5aeLS4+XViYn38yNzc7OzOTTCanp6empiYnJyYmxsfHxsZGR0dGEonE8HA8Hh8aGhwc + HBjo7+/v6+vt7e3p6e7u6urq7Ozo6Ghvb2tra21taWlpbm5qampsbGiIxWL19XV1dbW1 + 0Wi0piYSiVRXV1VVVVZWVJSXl5eVlZaWlpSEw+Hi4qKiosLCUChUUJCfHwwG8/Jyc3Nz + crKzs7OyMjMzMzICgbTP7/v3+/f/9/zz/Pf+8/53/nH+c/51/nf/cf9z/3X/V/9Q/1H/ + Uv9T/1T/Vf9W/9f/0P/R/9L/0//U/9X/1v83/2D+w/yL+R/zT+a/zL+Z/zP/aP7T/Kv5 + X/PP5r/Nv5v/t/9g/8P+i/0f+0/2v+y/2f+z/2j/0/6r/V/7z/a/7b/b/5d/IP9B/oX8 + D/kn8l/k38j/kX8k/0n+lfwv+Wfy3+Tfyf+Tfyj/Uf6l/E/5p/Jf5d/K/5V/LP9Z/rX8 + b/nn8t/l38v/5x/wH/gX/A/+Cf+Ff8P/4R/xn/hX/C/+Gf+Nf8f/4x/yH/mX/E/+Kf+V + f8v/5R/zn/nX/G/+Of89IxBIp1L8d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8 + d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47/53/ + zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf + +e/8d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47 + /53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47/53/zn/nv/Pf+e/8d/47/53/zn/n + vwfSqdTjw3/sv/8DK6twSQplbmRzdHJlYW0KZW5kb2JqCjI2IDAgb2JqCjgxNwplbmRv + YmoKMTcgMCBvYmoKPDwgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMCA+PgplbmRvYmoKMTgg + MCBvYmoKPDwgL1R5cGUgL0V4dEdTdGF0ZSAvY2EgMSA+PgplbmRvYmoKMjcgMCBvYmoK + PDwgL0xlbmd0aCAyOCAwIFIgL04gMSAvQWx0ZXJuYXRlIC9EZXZpY2VHcmF5IC9GaWx0 + ZXIgL0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4AYVST0gUURz+zTYShIhBhXiIdwoJlSms + rKDadnVZlW1bldKiGGffuqOzM9Ob2TXFkwRdojx1D6JjdOzQoZuXosCsS9cgqSAIPHXo + +83s6iiEb3k73/v9/X7fe0RtnabvOylBVHNDlSulp25OTYuDHylFHdROWKYV+OlicYyx + 67mSv7vX1mfS2LLex7V2+/Y9tZVlYCHqLba3EPohkWYAH5mfKGWAs8Adlq/YPgE8WA6s + GvAjogMPmrkw09GcdKWyLZFT5qIoKq9iO0mu+/m5xr6LtYmD/lyPZtaOvbPqqtFM1LT3 + RKG8D65EGc9fVPZsNRSnDeOcSEMaKfKu1d8rTMcRkSsQSgZSNWS5n2pOnXXgdRi7XbqT + 4/j2EKU+yWCoibXpspkdhX0AdirL7BDwBejxsmIP54F7Yf9bUcOTwCdhP2SHedatH/YX + rlPge4Q9NeDOFK7F8dqKH14tAUP3VCNojHNNxNPXOXOkiO8x1BmY90Y5pgsxd5aqEzeA + O2EfWapmCrFd+67qJe57AnfT4zvRmzkLXKAcSXKxFdkU0DwJWBR9i7BJDjw+zh5V4Heo + mMAcuYnczSj3HtURG2ejUoFWeo1Xxk/jufHF+GVsGM+Afqx213t8/+njFXXXtj48+Y16 + 3DmuvZ0bVWFWcWUL3f/HMoSP2Sc5psHToVlYa9h25A+azEywDCjEfwU+l/qSE1Xc1e7t + uEUSzFA+LGwluktUbinU6j2DSqwcK9gAdnCSxCxaHLhTa7o5eHfYInpt+U1XsuuG/vr2 + evva8h5tyqgpKBPNs0RmlLFbo+TdeNv9ZpERnzg6vue9ilrJ/klFED+FOVoq8hRV9FZQ + 1sRvZw5+G7Z+XD+l5/VB/TwJPa2f0a/ooxG+DHRJz8JzUR+jSfCwaSHiEqCKgzPUTlRj + jQPiKfHytFtkkf0PQBn9ZgplbmRzdHJlYW0KZW5kb2JqCjI4IDAgb2JqCjcwNAplbmRv + YmoKMTIgMCBvYmoKWyAvSUNDQmFzZWQgMjcgMCBSIF0KZW5kb2JqCjI5IDAgb2JqCjw8 + IC9MZW5ndGggMzAgMCBSIC9OIDMgL0FsdGVybmF0ZSAvRGV2aWNlUkdCIC9GaWx0ZXIg + L0ZsYXRlRGVjb2RlID4+CnN0cmVhbQp4Aa2TzWsTQRjGn02QCtZQi0jx4oJSPERdkhbb + W9t8SOwSlySlfhw02d1soslm3d1ErR561H+gFEQQPOjBmxc9tSeR4ieC9OBdUU9a6qGU + 9Z0Zd4Ng8eK7zMxvH555Z+adXSC+UXWcVgxA2/bd0qkZ+ey58/LAOiQcQgJJyFXdc6Y1 + TSXLDrH5gdwU74+xXF9iz7c+fdtz58Li7bXDSw9bO0wK5YRLCwJSkoT9luApxjXBFcbX + fMcnT4Ox3qgaxLeIk26llCF+RJywBD9lXBP8gnFPt9jcdWLFNpo2ENtNPGGYnk48RWwZ + nt4mpjwS2u0O5Y+znEd1x6W58bfER1hdaKS4mgAmv5J+r69dPAE8WQMOaH1tdBkYvgQ8 + m+xrP97wWkkjNa+eTvF00uBBYNdqEHwPgAHyb78Ogq3lINheojU2gJWi3nV73EsblF4B + /3oXZxbZgXd0Bgp+R39nURfuUoAHq0BlEVBpvEvj6E9g301AA+lUpnQ6bKKGJAN7szlV + lVNjynhem+fKf+zarS7dFY9h6gftWvEMjSPUPju+RpsS7PXKuZDrzXwhZKOanQ15oZEp + hlx386WQL1dPswPynKY9Vw7ZafFv//daM5Hf9HKRZ6FRYWfmHrdbmgv5Smc28htmNtqb + 3Sqy/4n7m34h2j+yyEGlR0YKY1AwjjyVfV58kzQDQ4+B+0PKyXR55eNLJvwRvnmd33Wm + 49xwm1bDl6fpjzSTcsHWjyfllKJM4BeDarM/CmVuZHN0cmVhbQplbmRvYmoKMzAgMCBv + YmoKNTY1CmVuZG9iagoyMiAwIG9iagpbIC9JQ0NCYXNlZCAyOSAwIFIgXQplbmRvYmoK + MzEgMCBvYmoKPDwgL0xlbmd0aCAzMiAwIFIgL04gMyAvQWx0ZXJuYXRlIC9EZXZpY2VS + R0IgL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngBhZRNSBRhGMf/s40EsQbR + lwjF0MEkVCYLUgLT9StTtmXVTAlinX13nRxnp5ndLUUihOiYdYwuVkSHiE7hoUOnOkQE + mXWJoKNFEAVeIrb/O5O7Y1S+MDO/eZ7/+3y9wwBVj1KOY0U0YMrOu8nemHZ6dEzb/BpV + qEYUXCnDczoSiQGfqZXP9Wv1LRRpWWqUsdb7NnyrdpkQUDQqd2QDPix5PODjki/knTw1 + ZyQbE6k02SE3uEPJTvIt8tZsiMdDnBaeAVS1U5MzHJdxIjvILUUjK2M+IOt22rTJ76U9 + 7RlT1LDfyDc5C9q48v1A2x5g04uKbcwDHtwDdtdVbPU1wM4RYPFQxfY96c9H2fXKyxxq + 9sMp0Rhr+lAqfa8DNt8Afl4vlX7cLpV+3mEO1vHUMgpu0deyMOUlENQb7Gb85Br9i4Oe + fFULsMA5jmwB+q8ANz8C+x8C2x8DiWpgqBWRy2w3uPLiIucCdOacadfMTuS1Zl0/onXw + aIXWZxtNDVrKsjTf5Wmu8IRbFOkmTFkFztlf23iPCnt4kE/2F7kkvO7frMylU12cJZrY + 1qe06OomN5DvZ8yePnI9r/cZt2c4YOWAme8bCjhyyrbiPBepidTY4/GTZMZXVCcfk/OQ + POcVB2VM334udSJBrqU9OZnrl5pd3Ns+MzHEM5KsWDMTnfHf/MYtJGXefdTcdSz/m2dt + kWcYhQUBEzbvNjQk0YsYGuHARQ4ZekwqTFqlX9BqwsPkX5UWEuVdFhW9WOGeFX/PeRS4 + W8Y/hVgccw3lCJr+Tv+iL+sL+l3983xtob7imXPPmsara18ZV2aW1ci4QY0yvqwpiG+w + 2g56LWRpneIV9OSV9Y3h6jL2fG3Zo8kc4mp8NdSlCGVqxDjjya5l90WyxTfh51vL9q/p + Uft89klNJdeyunhmKfp8NlwNa/+zq2DSsqvw5I2QLjxroe5VD6p9aovaCk09prarbWoX + 346qA+Udw5yViQus22X1KfZgY5reyklXZovg38Ivhv+lXmEL1zQ0+Q9NuLmMaQnfEdw2 + cIeU/8NfswMN3gplbmRzdHJlYW0KZW5kb2JqCjMyIDAgb2JqCjc5MgplbmRvYmoKNyAw + IG9iagpbIC9JQ0NCYXNlZCAzMSAwIFIgXQplbmRvYmoKMTkgMCBvYmoKPDwgL0xlbmd0 + aCAzMyAwIFIgL0Z1bmN0aW9uVHlwZSAwIC9CaXRzUGVyU2FtcGxlIDggL1NpemUgWyAx + MzY1IF0gL0RvbWFpbgpbIDAgMSBdIC9SYW5nZSBbIDAgMSAwIDEgMCAxIF0gL0ZpbHRl + ciAvRmxhdGVEZWNvZGUgPj4Kc3RyZWFtCngBzcLXcQJAEAXByT8vjLDCO4HwIoobfRPA + K7arW4f22V1akT1anX3ae/t85hfWOcAih1jnCIscY50TLHKKdX5jkTMsco51LrDIJda5 + wiLXWOcGQ7cYusPEPYYeMPEHQ4+YeMLQXww9Y+IFQ6+YeMPQO4Y+MPGJoX+Y+MLAf+tF + vN4KZW5kc3RyZWFtCmVuZG9iagozMyAwIG9iagoxMzAKZW5kb2JqCjMgMCBvYmoKPDwg + L1R5cGUgL1BhZ2VzIC9NZWRpYUJveCBbMCAwIDUxMiA1MTJdIC9Db3VudCAxIC9LaWRz + IFsgMiAwIFIgXSA+PgplbmRvYmoKMzQgMCBvYmoKPDwgL1R5cGUgL0NhdGFsb2cgL1Bh + Z2VzIDMgMCBSIC9WZXJzaW9uIC8xLjQgPj4KZW5kb2JqCjM1IDAgb2JqCjw8IC9MZW5n + dGggMzYgMCBSIC9MZW5ndGgxIDUwMzIgL0ZpbHRlciAvRmxhdGVEZWNvZGUgPj4Kc3Ry + ZWFtCngB7TiNfxNVtufcOzOZNkDT0pbQAJl0aJWm2QIKlEJpaJKWNkILVDepsCb9kADF + Flo+n9Iqq2L4sK7a1d+u6K6rFaw6bZENRZevxdUVfn6ivLfig4X14z0R9Af7VoHOO5Nq + BZd9f8Gbk3vn3nPOveeej3vu3AACwBBoAw7u2mXhJpBhGmEOU7HWrmpR3tm342NqnwaQ + V9zetGhZ6hcziZ4QABDKFzWsvT0yatrbAEN/RzyvR+rDdWeXqscBhjVQf3KEEMPGCEQf + ZtDHRpa1rJH3Yxf1X6e+3NBYG4Y6SKc+8YC0LLymiT0q3k39Y9RX7ggvqx+59bl11P8H + 9dWmxuYWvQpuB0gaRf3sphX1TWeOuN6jvpfWEyYcEhjPEJCoADgMjNgLILbBGCojeA9k + 6hf0v1L5zCj9s4j2NCT31wDwEmpvi4+/qhJP0OyeAZT+L56rBlyzMzAQDE2Ncu2nHMxQ + AltABAsI8Az1zsJxuA8klGEyJEM+vIAfgQcq4R4shJ8TTwaUwQuwGz7ARfolEPSgfgCW + QidOIv0zoAAWwF44T3x/gp9CGNZAK96j30XWSYQxkAk3kDVXwDo4SRwJkAY2yMQsNo0f + gxE0sgHaYAPsFir0Z/XjNCaBoAKeheegH6sxou/STxEmH6aCD+bDHcT7AIqYJo7R/6Kf + 18/DRFrnQlhE8yyHx2AXmnA4OvEPrIh3wDjSsxGaIAod8Ev4M2bjm7xS3w4uggkwh+a7 + FWpozi3QB2/ANyixGraGvcB28RX8ogDCUfFtyae/ph+hyBXBSmMKaMRtFE+Lad4H4R34 + EP4KX8HXmIaZOAEn4wwsw6WsQTJLP9X30BgzjAaFbJADebTSAigmmEe2uBMehS54BV4l + OABfokJwHcE4vBWfwR48xZKYwg6zd9g3PJ/7+aOCVVgo3Ck+Jp6UcnSPvosslQJ2UGEK + zVhOFrsVamlVzWT9X0InwYuwE2LwHvwFTsHfaH0j0Iqj0IOlWI7nGGePs8/5ZL5OkPpH + 6xP19fGoTqLVziAoJ7gJZsNcuJk8GoB68vY6+De4C9bDPRQRD0I7gSFnQEof7IPXKeLe + JXv8B/n5FPwXnKOI+BYukz+SSLIDc/AGzMfp6CXwYQVBBFfj/bgZ2/Fh7CKNN7KH2Vc8 + kafycl7P7+ab+JN8Lz/IjwjZwiyhR+gVC8RScROBJh4TP5G45JEekj4zbTe9IlvlIrlM + Xiz/+tLOfug/2n9On6HP0bfoW/V2/b/j3pPIYjL5YwhFeCpFrRNupIgqp+iZD1UQ/M5+ + yyhSmwlWwipYC/fC/bCZYAs8RBH0G3geeuBl2A9/hNdIUwOO0c75mOA07aF/kK4ymnEo + aWwhGIVjcCzFnBNzMQ8nYSG6yf5zMIj1uAI34TZ8GV/Ft/AjBuRtG1NZAZvO6sgKz7Od + bC/55zryUD6/iS8iazwpJAluEcQO8TOpU3oV3sPptN+ufh6Br/RUYQucod2zGt7neXod + r8Xl0CLehk74lnbGbtJF5ZSLcRycE75AJ1vN0nAjS2OT2GnxFXykP4qJ5MuPIIdW5hM6 + 4N+hkM+gvbqW+4WF7PdCNu6kCK3BL9lC9ivmE9ewaijGF9GF5XwcHDFlSZ+wevycZ5pC + cBFXkRaV8Du2AzYyF/4Pm9a/T3KJE2G78BHzwHY4xjJMEkvjF9m77Am4gz1Ee+IRirpv + 4Be03qG4gnKOCwvwXYqmg/g+5d8XxDb9vHgnK2TT8R32N4ogEJ4QwgNc/yJx/j86bgF4 + iS0zzaKsO0I8IB4w2uwQa+Qu7mImOEJ1IzOxD4Ri+AXXTDoWiptQkd6U3sQlUCciZEOF + 8DPswK/xIPQKU+A8fg0opEAFZkvT8AlxGlSII+ExlgkP0Sn3Br7I18DnOJrGLGGHTLMw + X7gICzEffisUC5/wDvYImlkOnsT78O8EDZRdnbgW1ksvwWq+EfJ42PQ+OtjP2R5oYaWU + ZeewbKzkb8Eh2CMVsJvpnQ37+TYWoCyXJx+GM9ggbBA24AdQxuZBG2sVEJLwuHAJGqQW + uFP8EO7C1XEpfYYc2vO/IVmT8E902m2gHJNNZ8sewn4IRZBLuXsN5e0dlAOClCmQzsYz + KFFmMHLeKbYTGfweZsHD3Mct0sNwN/sWMyjD1BE2C5bBVlQpSxRS1jyIH9M5GqBT73pe + A3NN24HhcNqhIG6lk0eFE/AqlsNP4EPMoMyoAjKB34htcAmrWEd/jVACiXw53yAuxRLa + Ty8C6qnwZz0i0PeGe1L+lBsmjM/7iSvXmTPu+uuys8aqmQ7FPmb0KFvGSOuI9LTU4SnJ + lqRhQ4eYExNkkyQKnCHk+tSSkKJlhzQhW501y2X01TAhwlcgQppCqJKreTTFGBcm0lWc + buK8/Uec7gFO9yAnWpTpMN2Vq/hURTviVZUYVs8NUHuLVw0q2pl4e3a8LWTHO0Op43DQ + CMVnjXgVDUOKTyvVhCwtsSrg0MqCqyJRX8jrysVuc6JH9dQnunKhO9FMTTO1tBK1qRtL + ZmC8wUp8Bd0M5KGkrlauen1amUpDaUae5QvXaZVzAz6vzeEIunI19NSqNRqoxVqSM84C + nrgYTfJoprgYZbFGmsEmpTt3X3RzzAI1IeeQOrUuvCCg8TDN4dOSnVqp6tVK1522unJj + +GxVQEvwxBCqAruhXG/rLmvzeoOGtBRP4P4r2W086rMuVozR0ej9ivbU3MAVk9kcxpTB + IE3qyvXPCzho1apvs2KoMS8Q14AmRWseLdzAGWoOKFyv+gxMaImiJajFaiS6JER+y4hq + MG+toyej3L1bPwHlPiVaFVAdWpFNDYa9o7pTITpvbW+ZWym7muLK7bYkD1i6e1jSd40h + Q69s1JMXBmjxVpzdaNGqvzc1GitSyzQ3hVutQisJqBrLyjeq+nyI1uaTR+gJIll0Mdkv + FLUUkHaamGVRlegFoJhQz3xxNSb8HUbKslwAg2hEzmD0aRj+vq05nVpOjhEpJg+5llY2 + I96f5MpdpfnVJoui+clkUBmgQcGCPDK5w2F4eVPMDTXU0drmBgb6CtTYesCd5wxqLGRQ + 9n1PSbvZoLR9TxkcHlIpsnfGbxJpmpw9+EuypA/3RQo0TP8/yPUD9BLax9FoiaqUREPR + cExvq1EVixrt9vujTT7afwMrj+l9m2xayeagZglFkMynDfcEuI0ZsUctZuNBF31GUGKg + nERAX0smutQkO5KzqKLTFS4pfN8ltwgXQRH2ERcw6NKPMyvdY0wwHCrciRIKAJQeuRDD + ZW6b/PwwM98q3CfhSrO0MjmttLTc/HxyWeqCVVbnHMv52Zfn+Oq9n8y2nCY4fwaKLn9Z + hMkpU4HK1AnjcQTjKqNPn4mUwCQ1M3vSjZPx0wlTb1non3ggtTwSKS+LLBK3Zfe/cfkW + thyvL7z4noGKlJVHaG2GGgzgsulXoduSpl8Am2ysGA6VNvQMvmn1pOk24k2I8xsEGiex + y81wWt7f/1j/4/ITgxSDajxMIhTbT9/tQPfJOqoHZC0geQvITIzuU6kwnRgLhR6yokFF + +ko33jQ5fXeCd25xZdktTk+4YXHz7KriRrrC0lqNR7/JuHNe4zHoZNwYf7Rn+Uh7jF/s + pdeUGJrdGWNW22+hspCK0jq+1d3KX37Qa59iWWfpQw6MHd7VNNZ+6ORwewz7e0+OtOfN + TMAHoIgKg5PsIF3F7Oyge/5rqr2pta2VtZofND9pfsm81/yWme5hlgT2dsKJhHMJPInb + OftPfpbrnOcpaM+8LfOsoitCkpKnFCkVSqPSqrykmJIy7ZkVmbxtOVpmOlgTKFTGU3FT + aafyFBVpEGtgeJxWSS02iCd9qW9hTe5EtmNdpr1tHW7siumHe63p8bf7emv6U5+a7E99 + +owpfUnnnqyBquPp8faOpxPGpz/dmZMwUMVwQs/jZnsfToDH8Ua3xXwFLSVZdsdY/ss3 + 2xJsCe1/wOcoktvx1/Fachea2i+a2ttM7UtN7YtM7SFT+89M7QHTWDlTVuQx8ig5gy4c + 6XKqnCJb5GHyEDlRlmVJFmQmg5wa00+4JxiuT6V/HRDSJYvxkowdAkK8bSElKZTIsxQb + DGVG9xD0a/tqwV+jaH+fr8YwcW61JqrFqKX4wV9VbEVtOPcz//xiLd/pj8kwT5vi9GsJ + lbcGuhG3BgmrsY3x4yWGIw3UvTbjZNlNXy7z7t1iM976vVuCQUhfVWQtSpmRPLXEe40q + 9AMy5HX+82P9AYX+yrV7wY5nyWh2VOJ1aq/J/qTJ4PHPJ2J7nNgeJ7YjEdsHiNbRWod/ + fkDbMTqoTTQa+uhgb3XfhqM+OqRCqq+eSkjbtCpi1dpqFKV7Q59BoLMiO1RTGzHe4Xqt + T633ahtUr9JdHR/3I/JRg1ytervhqK8q0H3UXe/tqXZX+9SwN9jb2dLYdZWsBwZlNbZc + Q1aLMVmjIaszPu5HsroMcqchq8uQ1WXI6nR3xmWhb/H8YrJVoFuG4qBnwcC7l5kTyT0h + myNYHPfTNId1va2PXcACMNMpMoQ+PoZSMVzomumaSSQP88RJw4zvku9I1vXTHLY+LGAX + 4iQLoZMpaPAHLxmtZmdzc/PKFqpaVkLLSoJm4miOU5zQMsBMwfi/LcjbTgplbmRzdHJl + YW0KZW5kb2JqCjM2IDAgb2JqCjM0NTYKZW5kb2JqCjM3IDAgb2JqCjw8IC9UeXBlIC9G + b250RGVzY3JpcHRvciAvQXNjZW50IDc1MSAvQ2FwSGVpZ2h0IDY5NSAvRGVzY2VudCAt + MzE5IC9GbGFncyAzMgovRm9udEJCb3ggWy0xNzYgLTIyNyAxMDc2IDkxM10gL0ZvbnRO + YW1lIC9EUUJQSVYrQ2FsaXNNVEJvbCAvSXRhbGljQW5nbGUgMAovU3RlbVYgMCAvTGVh + ZGluZyAxMTEgL01heFdpZHRoIDEwOTQgL1hIZWlnaHQgNDcxIC9Gb250RmlsZTIgMzUg + MCBSID4+CmVuZG9iagozOCAwIG9iagpbIDM0NCBdCmVuZG9iagoxMSAwIG9iago8PCAv + VHlwZSAvRm9udCAvU3VidHlwZSAvVHJ1ZVR5cGUgL0Jhc2VGb250IC9EUUJQSVYrQ2Fs + aXNNVEJvbCAvRm9udERlc2NyaXB0b3IKMzcgMCBSIC9XaWR0aHMgMzggMCBSIC9GaXJz + dENoYXIgMzMgL0xhc3RDaGFyIDMzIC9FbmNvZGluZyAvTWFjUm9tYW5FbmNvZGluZwo+ + PgplbmRvYmoKMSAwIG9iago8PCAvVGl0bGUgKFVudGl0bGVkKSAvQXV0aG9yIChQcmVz + dG9uIEphY2tzb24pIC9DcmVhdG9yIChPbW5pR3JhZmZsZSBQcm9mZXNzaW9uYWwpCi9Q + cm9kdWNlciAoTWFjIE9TIFggMTAuNS41IFF1YXJ0eiBQREZDb250ZXh0KSAvQ3JlYXRp + b25EYXRlIChEOjIwMDgxMTE3MTg0NzE0WjAwJzAwJykKL01vZERhdGUgKEQ6MjAwODEx + MTcxODQ3MTRaMDAnMDAnKSA+PgplbmRvYmoKeHJlZgowIDM5CjAwMDAwMDAwMDAgNjU1 + MzUgZiAKMDAwMDA2OTExNCAwMDAwMCBuIAowMDAwMDAxMDk4IDAwMDAwIG4gCjAwMDAw + NjQ5NjMgMDAwMDAgbiAKMDAwMDAwMDAyMiAwMDAwMCBuIAowMDAwMDAxMDc5IDAwMDAw + IG4gCjAwMDAwMDEyMDIgMDAwMDAgbiAKMDAwMDA2NDYxNCAwMDAwMCBuIAowMDAwMDAy + Njk4IDAwMDAwIG4gCjAwMDAwMTM5NDkgMDAwMDAgbiAKMDAwMDAwMTQ1NSAwMDAwMCBu + IAowMDAwMDY4OTM5IDAwMDAwIG4gCjAwMDAwNjI5MzcgMDAwMDAgbiAKMDAwMDAwMTYx + MyAwMDAwMCBuIAowMDAwMDAyNjc4IDAwMDAwIG4gCjAwMDAwMTM5NzAgMDAwMDAgbiAK + MDAwMDAxNTM2NiAwMDAwMCBuIAowMDAwMDYyMDE5IDAwMDAwIG4gCjAwMDAwNjIwNjQg + MDAwMDAgbiAKMDAwMDA2NDY1MCAwMDAwMCBuIAowMDAwMDYwNDQ4IDAwMDAwIG4gCjAw + MDAwNjA5ODggMDAwMDAgbiAKMDAwMDA2MzY2MiAwMDAwMCBuIAowMDAwMDE1Mzg3IDAw + MDAwIG4gCjAwMDAwNjA0MjYgMDAwMDAgbiAKMDAwMDA2MTAwOCAwMDAwMCBuIAowMDAw + MDYxOTk5IDAwMDAwIG4gCjAwMDAwNjIxMDkgMDAwMDAgbiAKMDAwMDA2MjkxNyAwMDAw + MCBuIAowMDAwMDYyOTc0IDAwMDAwIG4gCjAwMDAwNjM2NDIgMDAwMDAgbiAKMDAwMDA2 + MzY5OSAwMDAwMCBuIAowMDAwMDY0NTk0IDAwMDAwIG4gCjAwMDAwNjQ5NDMgMDAwMDAg + biAKMDAwMDA2NTA0NiAwMDAwMCBuIAowMDAwMDY1MTEwIDAwMDAwIG4gCjAwMDAwNjg2 + NTYgMDAwMDAgbiAKMDAwMDA2ODY3NyAwMDAwMCBuIAowMDAwMDY4OTE1IDAwMDAwIG4g + CnRyYWlsZXIKPDwgL1NpemUgMzkgL1Jvb3QgMzQgMCBSIC9JbmZvIDEgMCBSIC9JRCBb + IDw0OWU2MjQzZGUwYzBiMTQ0NmRmMDQzNjRjNzc1ZGNlZj4KPDQ5ZTYyNDNkZTBjMGIx + NDQ2ZGYwNDM2NGM3NzVkY2VmPiBdID4+CnN0YXJ0eHJlZgo2OTMzNgolJUVPRgoxIDAg + b2JqCjw8L0F1dGhvciAoUHJlc3RvbiBKYWNrc29uKS9DcmVhdGlvbkRhdGUgKEQ6MjAw + ODExMTQyMzU4MDBaKS9DcmVhdG9yIChPbW5pR3JhZmZsZSBQcm9mZXNzaW9uYWwgNS4x + IHJjIDEpL01vZERhdGUgKEQ6MjAwODExMTcxODQxMDBaKS9Qcm9kdWNlciAoTWFjIE9T + IFggMTAuNS41IFF1YXJ0eiBQREZDb250ZXh0KS9UaXRsZSAoUmVwb3J0ZXJJY29uLmdy + YWZmbGUpPj4KZW5kb2JqCnhyZWYKMSAxCjAwMDAwNzAyNzQgMDAwMDAgbiAKdHJhaWxl + cgo8PC9JRCBbPDQ5ZTYyNDNkZTBjMGIxNDQ2ZGYwNDM2NGM3NzVkY2VmPiA8NDllNjI0 + M2RlMGMwYjE0NDZkZjA0MzY0Yzc3NWRjZWY+XSAvSW5mbyAxIDAgUiAvUHJldiA2OTMz + NiAvUm9vdCAzNCAwIFIgL1NpemUgMzk+PgpzdGFydHhyZWYKNzA0OTgKJSVFT0YK + + QuickLookThumbnail + + TU0AKgAALDSAACBQOCQWDQeEQmFQuGQ2HQ+IRGJROKQoViABCsQhoAiEKA8AhR2PF/ux + vuZ/t9quB/NWKy+YTGZTOaTWbTecTmdTucAYCAADHwwUEzlADGcLA8ABaISd/N9PLV9p + 5FKZ9op9PwAPqeV2vV+wWGxWOyWWCiAMAEQMRMgtihsJAANAABAKBAoEgB/gYDAAAgO7 + P9+v2/Pp9gB/PV7P4BP9/gJpt1+tMnHh8E5wOh/uCzZ3PZ/QaHRaOCT6gN5XA1vhsIv8 + NP8HA6BBII366wYAv+BbqB7x/P6BO94QJ4vMAMxsP1mDs0vcd1mt6TpdPqdXrdNIHEEp + I3lIBm0AbGBBQKwfgQjHboA+uB8DHQJ+ul2PwCPZ6gQ9Jl8npDqR9kO68AwFAcCQKh4G + LyBx2FsBp1r4AIDACDoQIEwC9Pe9iDt496/AC3qDH8wh8G2cR8sUf56BAKp6hAeh7gAe + kDRlGcaRqsxGDaBBJjmKwCDYAAIgmvQIAgv0jSO9MOt3DEMve3T1IOfZ2HcfgCnmeICD + uS58juqqrxtMEwzFMaIAUA4AAYdpcAadgEQjM4OhCgTbt2hbeQ/KEPTrPaCRCAB8m6cR + 9OKfx4BCKp7BCex8gAe0yUfSFIxkQw0gQRo8i0Ag5H+B64gCCIKIE3ML1FI9R1IgzeSa + 872Mc4E9ABKUqAKeksDkSZ8jkR5VH2R9JV/YFgs7NwAAVNQGnaBUIgQAAOBFCqfz4gkn + r1I1YWpKD0NwgZ+K0fRvnCfZ2ngfx2BEK57BEfCuHxYV3XfeCZj+MYDkOQAwAKPAAAhI + R/gi8sPT0f73IG9dryXUsLvVVdq1FgVq1gfR0nafYDHseACjaR58jaShXH2Sl45FkeSI + M0wEHcXIHHaBYDn+BR/g6Eq/AGAeCt288j4bhuGSVVGeYPVFW27WJvm+fZ1Hcfx0hHdA + RugrmS6lqdJDwLwDkAQ4ygKP4AAfIQAAkDE8YhDeISNalYT5D20oLbGdW2fJ0nSfQDnu + eIDDQRZ8DQTJZH4TOqcFwcZgLmwDzVlYGgSf4F2aEyBAIAsPz3DiC1hnODyTtW1WnVKB + rsgWiH2cBvH4cp1H8cgTCyewTH2wjD8J2faNIOQsgOPRGjUApCn+BshJCudSbTnudSfD + Gyc5gWCYd5GHc09+JHVup8bwMZDnwMZPFsfhPdr8HwrFCwC8SdoH8YBtmhRyK+z5O+2M + RuFp/hymd214/6/e+CtH4cI3X/DoH8OEE4Wh7AnH6cAwj4oGQNJoGoKYBg5iVDgAYRgA + HgECAmBxPCqlTtuaE8ZJbCyCNsYGz6EMHXnIfHwOcdA+QED6HkAcLwhB8BeFGLkfgo4H + Q9h8Q8ASHgCDsFwA8dgEQFD9KUBwFRenJPzIUbpV5DU7m9VVFZhL9FssOfsk8fZXB/Dj + G8P0bg5B/DcBUFwewKh/RSh/G+OBAgyBNAMGwTYdgDCTTQv0CgHW0J2Q1Fx+61FRJOZv + Id4jZyGJ3iqAAfA5RywwH4PMA4WA/j4CwKkXw/BUxxk8+JPQAx1C2AeOoCgDR+lxA2Cs + gQBX3QgQ4/GFDOWEIdka/dz0UZEPPj+3A3kXwAD9HIN0fw1xwD9GsC4L49wXNmk/M9qg + XAkAFDIKMPYBxNgAAXHwD8KJYwlZxFl+kWXLOeliteQktm1zgaE/pao9xyDiHwAkfo9A + EBTD2PcKYrhhD9FdNCgDJABDoFqA8dIFwHD9VCBsFjkUzv5Z252dMsn7SyQyb8hU6JCu + VkQnVtshx/j6UYP8csxBpGSGkDEMQ9wY0BpcsIKwQwCheFSIAA4oVilxH+BRZ6GZzLWi + 6zeNznXMNueS9GEjZFStBlxR9hI9hxjhHwAof09gnB3HuE4WYxh+izpfV9MYAhyiyAgO + YDQER+AXAABmhpQC8wpL0qyir+Jbl+mctKQNEYsPHYLIyQCsV2j+HNMQZpyRmg2DMPcG + 1YLGIzCeD0AgVxYCGAQKinJAgKgkkC/Wdzb6JV6kXFGRtTmfSGShaNgsHyBD2HEN+qY/ + x6gICQHMe4SBcjKH6LmxtuzrACHCLACA4wPATH4a8DILS/AGAVXmEEunPrai3R6Dz9rm + xUupZtgo+kXj/HONwf4xxqD9GODsNRzreXnNEEgG4BAoC3EYAgV4AAEm1H+BYE8UGFTe + ivUapLP6O17VHOeLE7rQRanVLijcWTgD1HENwe7jh7gJCGG8e4QxfDOH6L69GGyxgBG8 + K0CA4AQgWH4B4AAGLkAAAOAyv8Vb9zfv3f65kuSE1+kdAtdaFTAgML65y67lG1G8Hyo4 + AA6RuAAGCM8fowQgBuHuEDDmUSeBBBkAMJIvhIgJFsAABCRQAAXBTfxDrAqJ3Wc+5tVr + ya+tuH4YQAI+CtSOdlLQg60WvLMLpc+vipa/D0HCNse4DABYRB8GsewPhhjSH8MPKWjS + ZgBGyKoCA3ATAZH4s8DALiBYrrxOSutd8g56nYqhDg+XZZwdEzlO7oQHA5labUAA7KvE + FdDBih4BXQ6huciYgQ6sji7GWPwXYRg5D4CNo7ZBEwdgtAGEEYglgEi+H+AgpQAQL3Ic + 3qNs0W7SIfim0FnJ0B/6mMLqpkzwwJhKg0E3TdaiCpqIEOEQxAh/IvIKBSt8QWEPFlqh + keg3xsD2AYAOqYOQzj2ByMgaw/hkbJ4dRkaopwIjYBUBwfbkAL6avkUq6uNVU11Paed2 + BetyABH1nRPYAnHJACEQICIRyBANBhIIiA7xgkCG8HZOZuwJF5YDj+D8tx8HGABr4AAt + hjj8FsEsOw+Al8P6gQMGoKABg7GUJsBIxMVcc0zqKkCTIunn3EPkrQAeR3VADQ8B4OuX + BEIEA4HCFaHk1HWLEgQ4t5lA1Znh+GoMbEH3+NXgQBB9AKBoGQewNBmjZH8M3qOyAAjQ + FGBEaYLwQD7lYBbFN8682d5CQIedI8234uT28G5AgIBBIEA8HaFa3lhHEI4gQ6xVF3J+ + P8BLk2Db9xpyAgQ9zh6xG8AAWAwh+CwCiHsfAUfH5RBeCQAQNBnifAUMrtIDkPAYpaw2 + pzbU9OYN4O7IgArlnh1cvsH3pkK54NGNpH4AB5+Ogw+401EHPRTo9UDeiMBweCAWAGHy + ASBgDCHuBgpOH8Gk+at2ACGWFAAiGeBoBGH2BeAAAq2wASeC48qM5oN4Hg3sBKe+vkQo + QMGkCSIEH2Hi5czw30p+5+f2IUPOHwHeIEJMAAFWF+H4FWCsD8HwCtAUq+BSIwBcGqFE + AUGg7SAa+yBoIeeQYWYeWqVeHoUYAAA2EC9Q9aQEH1BmAAGo3YT2AkzwSazWwKWqzQqK + WoHqG8GmHsAUAGH0AQBYC8HsBYGuHCH+GvB+meACGME2AgGUByBQH5CWAo40AUVCwI46 + r+HsaiAgC+IEAwC0QGHmGeIEG2DcQqN2AeTPBc28/0VOrqbU5QHsHaIEHaG+AAFMF2H2 + FMC2EEHyC3DyjgBKA4ACBUGyFMAWGoQgAYQ8Ay9OT2o0z47BA2wO7IIEAMB+IEA+X0QE + HUFgIEHIEbGOdCAY7m88rw34o42+YSHoG6GeHsASAGH2AOBSjUBSG2HIH+G3FigcGCEu + AgGMB8BYH41dEILu3cwQxpE+unGIowVixMAABMEqQGHGEhBovivkWiAQcmues6VU7+qK + cuIGHqHWIEHdFOFAFsH2FADCEOHyDDHYfABCAyACBOG6FSAXDwAMAWPWAw/OzSv6nGue + SatGjaL86IAABcFkQGG2DiIEHoGcL0AW90AMZsz4bKl2rk36eYYSliPeHoHAGcHsAQAE + H4AMBMgMBMG+HOJTJCcGF2EkAgGCCIBiH4/SAmxSAWAy4+lg/1DKSYc606HiHqIEBQss + KAbAOoGoCnBPFI/mcjKM4826s8Z05QIQHqHQOCHCAAE2FkH2E2DMEWHyDNK8ZKA6AsAC + BGHAFYAYG2AEAKASNyAy9a93ESxmrm/yTy9A3sA+ES5jAoNIH5LoAAGmCYbcAgWYp8Ig + hOl8kQMIljBYQ5JtKgGaHsQiPoBICwHsBIHGHUH+HHMqXgFoEaAgF0CWBsH4CKH+AkuQ + ACAYeHA0kXH4xcnIIGHsXbAqDWPG6eNIHqGwIEG0DUL1KMACAdDCx+xkeOVZLiME5oly + N8HoHKMeHiHGACEqFcH0EqDYEeH0/fOiUkAyAmACA+HIFeAYG9M+ASLsA1GSz2IAAACA + AA/4NAoJBIPCYS/4IAYHBodA4HCoLDoE+n3BAWTIIGzNDJFI5JJZNJ5E7V3BHGiIIBAF + BAYB4S/oTEYLAoxFpHGIhF4nOYtO5/PIuAHq4GU9wIAH6AhCVnsIXO7X+55RWa1W65Xa + 9X7BYbFY7JJFciQetCiOn6SwAEhbBAaHaNJp3DZLO7vN7xPH2/IIAxjBBCgLLh5E5k9B + HUp4IB6aAASBZrYL1KL3dYVPoi83C/wC8XIAUgqn0kDik30ccRrddr9hsdlYgsEACG3O + sgY4gEBARMQ0QYfMYdQbzIoXFZzQeVDOLOYpBH7gAA+wrBBOmdnYnAg4I8GHBMnBchAu + bJH/NqJy4PEPPBPVfPRzou/QA9nAyHuAX8/n+EIqqmdR4H+dTtwPBEEwVBYAFQQgHlcK + 4fn6KIAAiuIAAaD75uczjMsyk71vg+R/vsAB5uoFhXuHBiTmyNCCHubyZJoACYIezbjM + wvrkR5HaSJsfx5G+fwBHmcwBEUUp9EUO5MH0O8WylKcqSqAAJgcAALnSWYHHKAYCgMAY + AA0ISHzG9cQPons2JGirkqEkp5nsggSk4x4MSshZpiehR8oKBoEIEmLLM3HyHoi9LzR4 + 5SDxEhTAPwZB8H8flKhAKp6hAdp5AAdsrVBUNRLGUI/AcVAvCKfwrwsFlABFRcOIan04 + qyjCFoQzUOome0/gADo/rkGUrHydaCGwLscIIBwFR44r2qLWqjADW9HxNXM3varSHH6e + Btn8AZ6nQARClCfRCj4Th9D5Ud23dd6CAgBkrnWWoHHSAgCALMYNCKwMx2krdqLq56H0 + c+WEJtOLlHufSCAoMmHiVKx5mgghvD0l6KgXQSR4UkVGrxNOFx7XWSrynabHub5iHwfZ + 9n+fQPioeoPngegAHheGd55BZNDyBxQjKJR/C9CwVrkEllYCrdn0RNc1TVQ1cgAfLqAZ + icyDFKx2FqghykqggDUIBMa6Yvbnujk017PH9HoOfp3GofoBnudwBj+Tp8j+QRQH2QWe + 8DwTEAaBIAAgdpcAedYCgIAYCH+DQjUGyNtq/XDj6W57mMq6rqAJYYABBjMqnKTaCHaW + GxMi8aj5BWfNWrzWEzogzAIG6h/9yjcRoK6k4owfs6Huc51HyfB8n+ewPinmp5nvE/B+ + l6atEmOgGk0Ngnn+MvD6QAAHBMu2EKH8jl/NtiGPfRWlvUfqbACfgLoIExJSsb/AROZS + CgQygApiR6jooY/noLUV8P8fB0k6EFgSQR3gAGPtvKArEoRBn4HNIGPseY9R+AFH0PoA + gehMj5D0IcUg+xDvUhVCtjj4B3C4AgOwAwBQAgFH+BsJJ5jKNUUeh12ZfVGsjfhDx1x7 + y+k7HqiYFRjoKILGyGogg+RyEcAMQQATCh/DxOkPMhQ9SFMOh4rci0F1sk5iGTeMrVCG + H9aWyQgp9h8DpHkPkeg9R/jzUwpoesCYvQrj8zwRobgGCTDkFUAAbAAAPBUvGRcRVDpu + jErJzLJ31NTkoQQej0AAJ2bEBJBRCxor+Oqp8AADIHu8Oax+NLA1lPskuUYzkln0yPOS + TYfcc4Oj7HyAQOolR8h1EYKgfYjI/zFVEAmKoCx3i5AgO4A5lIqgcLcAAAUO4xQCZDG4 + 5C0D0E+kirFXDBSdMIj2QQD53gAALBSgofA5iCDOBsSMCS8yhrabRGhEUrkQHKWuTibD + sCJEInuoiLEUR1DyH0PAeY/h3oAKmw0AEmpjUTQWIUNIDBGh6C0AAOQ/wHTrACBFV0rW + PldakyVtL61tRoIVSVDg93eAXDSQQCIQEFDwGMQQawVjAkVWY08gtLo1ENPijhtE3FnI + /VzK59Eb04y3ZcAYfg+gChvEiPkN4khWD7ftRSrxspnmSmWBEd0yB/qCA2R4AAA2zI5q + GrapUk2mnQqXUIfR1AHhQIIBaniCR0CmYuHZsTAIWvtkeSVRrCnMVARKwsopyZ8wRJPK + whg/T7D6HYPQfY6x3j+HUCMK49gRj5I2r6r9pyyB+DGAsQwgQwABDy+AFFNAXohlm2wn + Ftk2xEcufCWI/2XkCAQDkggHA2oKHAIYgg5hLGPh2AomlASIIguoiGVSPGFLRdhUue0k + nOyVaqO9hoBx/D7AMGoRo+A1CXFgPwS9qL4FeX2AAA47xdARHeAu8jhgNhNMCoJtM3ag + NMlYh+ult2SXSpVUZqY+h0wOk8AAFcTEEDYJCAAdwtiCAKiqACGcbaiNPwVdV11Aq6Uu + h6UKbNQcRLQMuTyNg+7Mj7HMOwf45gShYHsCUfZ9oH3xyASMO4XAFB/EQGcARhgGgnII + BIGd37wQVVotOw8r5tWKJItFW7Ch+IGOrg+CEYB7gLIIDMZKChohHIIPUapcmOgDYAtG + xVJzmofyxG3EjJbH5zsni8kg+B3j2H0Ah3QBgyCIHwGQTotR+CdyDo+tZMQC32AkO8mY + /AFgBA4n0f4A3DXaiOdCk59IBSXglqVN51MuwOHQQqB5yj1Dxi8AEGo2DA1tNgMmdZTo + uPgcNNRgD5yJ4FpZixkeCsoomxSfPAN27Hn2KIwOyGPh2wcHEOgf44QThaHsCd95TtIW + oDgFcBQdxHhsAES4Br4krg1J7qOb7lrcNMrgAAfg7oHDji+iSoRDB5M4AAC4Xh4gRmwH + 1vgAAzbauuAes2JsQK3HnMzsSWRD87wUz9LO6RIqhRGghREdzyHDD8AOF8Qg+AviiFyP + wUW4aJgCIGAQdwuQJjuAeAkfYDQAAbQqjbhzJ5/Ym3rONN2zrtxrgWPocB8GcYjbXOIA + I9YFgjMWhYH5sB5jPIINQJzBllz0YOojjNSnZMgUSfbj0QbHdlsR2zEh/t7DuHoP0bo5 + R/jcBUFwewKu4b95c4EMwUAEBuEyHMAgkZSglYeDqw+BuvQVqaTyb/Ht7EEH1vre2YGA + lEqPqhGMmgNLBAABgLhsB1orAANwNzGiCgK0/Bi7ERc6ocLuiDjqiSgyx8ks/Yca2E2M + uyQMew7R7D4AUAEfoCAsiAHwFkVAvR+Co7+4MioAx2i5AoO0CICh8gP52FMggBZ6LSOT + Eaxa294EE1W1UcJCTqbSyn8C7xInkZNDDOa2Jrxx+IAAOSYjDxgBsrp7iBhDtxkR8zsa + 3ZqD+JqiyQzTZo6QmxboeofwbAcIfwa4FoLwe4FreL6ZdoMIJYBANITwPAAi5oBhpQf4 + CgHyoaCRtbKsBQhqMAfAbaN5nS6bKjULoripXSD48QIZ+i5o14bgOYggdYVS5wggBDDr + 87FUDx87LTPL3yH7KxD6e6C7eaWBQwezkL4wAT5IKgPge4KgVoYIfoVsD5d4AQdYW4Cg + dYCgBgfKTwDYKT8L7yuY47UozAjAfiUgfIbrVz+beiAMFzLL9SB7xQAAFoWQ2AaiQxE4 + ZA8TDpxsQ53qbUBJ4C6yxxRhR7PkKrfr+L8qlqN7aAeIe4fwaQbsVIGIMQe4wcNRUILA + IgA4MAU4P4AoT6dJpQAACw4TZDd4rhqROBEwfMGx3RYziSRz3SpkSzj5NkUY+BEwe5jo + GgZw2AZqeIAAfQrCdJGrOCbSer3qbccaHjvyI8UR8q7Q4ruDUCMyNTtQnjFbj4eodofD + MYAYfwBIJwO4e4JwWYYwfoWcWJKYAQdAWoCodIC4Bwe4ChMgKgx4CK3ZbLUpp0eC7BnR + qobQ+CBplC70YCHzoUeL8wjAeSPoGrNqtbn4sAfyMAZMXgoQBzX6t8kMkbAScbBRkKC0 + mh18dEYKNTsalKgC7qCBIIeUVAZobAf0bIMwe8bUghBIKQH4AwLIVoQgAywABZWEXpyb + Fi3q3QhgfQcTyzpcGSbcQbycm8eJ8ZE6BYFoXDDbdgsJlYggaEFokSn8PJOBgjPEHccx + NyNJgLArxwgUUMQbEJRYeodge7MYAkfQJIOge4JIXAZIfsuEqA2QAQcoWYCwcwDICAew + C426voAwCcKMnsZ7oZ3SBga4gQfodiH7jbfkcryCR8ebqDFSMptIeqTQEh05CxMwsQeA + YIgga70qagiIBjT81D3joRRjFTZ0tI+C7JqZaLtRtKxbaK68S8LR9j+DizaAeQfAf4Y4 + agfwY4HYNQe4HczA14JYHIAwKYWgRQAwVgAABQEAggC6HKcEwiyhRKM4hSTQe4aQ+CBb + eUjxWricizykKal5XwDYPoggDBowsQdAUYggb50hxzDbXAuzF825Dkiiobi7jzEkaKCr + jpNJRTLUKi8AegdQe4ewBgAwf4BQIgOAe4IgXoZofoXs9osgAIcQWACwcgDoCYewDRMi + voA468sy3hkofk2CiIawi4jdFrEE6pZzibo7Ubo1LUn8H4t4L6cwPYsYcAQoggc4TBsS + HZ/sHTjibypTWE2wmqorjAm9E7Ok7c71L4o7ANOgpxEwehhwYIZ4foYIIANwe6m1IAr4 + IYGYAoJQXgSAA4Wof4BJDYAIDLrq3SHqb4fIb7y0QNTxEhWkdMHptsPMmtLQjC4M+8II + AAE0IYsIbKmbDAWkJcShytUxg1OUsBtik870wNPL2TeL2ySBacK5WTKYeYdQfAewBtGo + BQHwNYewHwYYaQfw8NRwrIAIbwVoC4cAEICweoD1JcJZ+boskD2oggfDWw6qKddk3Dob + d6AUJzKcnJQxka3QfhEwATdgFoWIsYaStQeoaMJbACGghDyj3aVpD0vjojozFEBFZLEr + Kkw0nrjYy78rF5SzNZhwXgZgfgXgIoOIfCUVbokgHYFoAgIQYgSwBFH4BNc4AADLnsQj + 2UUiiNArezLz872dnKMMBVB1i8Hlo0GDGBhQfLhwGgZgsYZQFw6Qd7DbX4AdhrWETNYp + hbF5NUc5NrPcGMLZk1osTUeQgYeYdIfAeoBoA4f4BYHIM4ewHIZAawf0SVlQh4bIVQC4 + bgEwDIepWADUSABIDaR0m7yp5TrT5DfFjtXtpKbs2jo4zaIboL2FiLK1yiNSDYggG1eA + AZjorIfjgAZaRooQBbhxQkcNzVhsA5HUCEBrjquU09YVO8HNxycET8mxkVj7qJhwWwY4 + fgWwJYOwfCadRwGwFIAgHYZITQBAYgAABADgj6vskLEwiYfyBoe1p6CDgDzb9Ir0vdBU + n9zMtBEL+IebgAF9H4yTgorQeteAaUrooQBjMjojZbEs6VslsUZ9/FVVnNPrxr2kZi7J + qVZwfNtltwBYGgMgewGgZobMpczAAIaoVADAbAFQDYeh8QDKvoBQul/17LNbM6CFA7tj + jViETDKl8F61yZ2MKrilpIeyBYEwUBeMu4rIdwXQggbJiIf5QgAIBTn9VBXV8TzlZUGM + nbtKgCfF/ji9VVEa3LKJRTaL9xEweojYWIYYfgWIKAPQfCvUNQF4EgAQGgZ4T4BQZQAI + BADRagDYLckS7CBIf4erM4/l70tWF1rks8i8Hd3GFsv1EK7lytiwnce4ggDoQk/QLIrY + c7RwAAcAQIl5QgBF0JzdhmF9h0/svtpMZrecBljj3UrzPRN86iSye73LAb31tOBAmdGw + GAMIe4GEVUVLlwAIaAUgDAagF4D4eiRYDMSABYEMHqBCTFvGOlzGF0kGQFKF8uItZdQG + TOZxgIfJXwCU44DJ7orQcoSAxj6QAESq+jDtoUBdo82seU5o91ndBFYNLOUbjTA5zVjM + wS7dfwggewwAVYX4fgVYKwPwfF6q+IFQEAAQF4agUYBQZ4AIA4DIgYDlMl3JPyTAYqLs + vlT9E58l/S6mIjYUnmc8tiV6cKoAjSKJP9fmZwA9Xd/lZY4+QmdEBVr8Kzx8K8LMAlpE + wYlFr6ygh1tIfQeoBdGoBIFoL4ewFoawcAf9Kq04AIZgUIDAZ4GYEQegF4f4DMiAAIBc + RSNZXweuiiCDXtBOsNP1dsr+sSWjPx9b4NMGPloJt9PSxlEE5+AauKpM1CNk5lrZk+ji + oV8Rttebx6x+uLyw6ge4+wUwXYfYUwLYQQfOOCigEoDgAIFQbIU4Bgah/4CwgYDwMYno + wAf4el6A/iLSerEFBktePW08cmTEnBbTFdYVL2s+uVswheu46c6eQIitDjSI+WGKNzAs + 7Y52tVsYk1ErUKMhkVFgorWGU1/eu25hg9yV2gvFZwfYeoBSGwBAFLvQFIbYcgf8GyP4 + Y4ToDAZgHAEwegGYf4DGq4Bi2Z3oegY46TL0mswMwh87Bmc+KW2VVmZNMGJUmmjW4ExG + 2xqqA92dyAjBfNNqNoistetEn2QY82tNo+I+tjUdEu0zzuOM6rOkxAhYfgjAe4wAUIW4 + fYUIMAQwfIMCFQEWNoFAbgVIBgax/4CggYDoM6oAewZaByKbyTBG1GPUJy29L21+Tmsl + 1dw9pAfxEwfCBohYhYA1dQBSRoBZ74ATX7Nme4ald0stVQA5ju3V/+PPDk6uUvCi2+nc + TdsFPzKWTOGNoPNoo4eYdm6oBIpoA4EzbgEwb4c4f9URwQYITIDAYwHwFQei4gDD8BDK + kYe5iyBEQNy9P+eJDu4+PmZiAO1sKtYabfSuj98Bascuu+Q51wBYGAggEYRTDerQsAfd + qgAAb70Qdr1D84A5GucOALUliWvXClinBjeMc7ANssUWULsUvpOFOU2T9RhWwoAATYWQ + fYTYMwRYfLC5eAD2zIEgcAVoBkjYAoCYiAD4NbyxGaiLrVD8w8vzUyld8ikxt1FybNFH + IJhXUYf4yIDb1bnZGAgTYI2IdoW5i5KI6rhAhgBLN91XIpXWnCw1VedvAFjOKJ9WE+ur + FaykxG4IAAegdgfgew8oAoEi0IEgcQdQf8sZdoXYSgDAYIIgF4ekFoC7noBVJRE48Ih2 + mt8ethH2FmPXS4zG4Qu+4EBMaPJ73R479Q6gEpsJK8/hKge79oAAaSUR4RpfgqK1YmnJ + WVQNBLj2mNFzBHYetdsztuU9o4fXJgmwSwVwfYSwNYR4fPcRUADXb4EAcYWABobyaoB/ + G2znjAXw+CVHndBk6G/pNvI9xG2VoHnN82kAoWu9GQggCa/yTb/hd1C5i7/IhgAqHY8r + K+UGJ0sHhXOA9G5nMWusmtemmcTIy4egdfjYAomIAYEQqQEQcwqydxKYWwR4C4XYJIGg + eoIkXtToAJT5EpTsKV1ejHTnCXwe+8oGdKx6SayL+SH/iCNIfRXwAMPAFwXL8MiRnoaq + jYAAeIYRpYBV0MecHviqCcHK3GsmttfKulY/YypHSXYvBDrw9M2htHso0B5IgABSKrfS + ROCSfRwAELhkNh0PiERhoXCIBDjmWIOcICAoNAQACo2hb7c8PAMMf7/iQAlMLk8Llsrl + UymENmcRmM3l8smYBl8xm0MAM3oMOoFEoUwmb4e0LDJphYdOcrqlVq1XhbwYsLaxXlEL + BIIhYCnc7mc6m9npAAoc0hj+l1mr8spNvmsutcxnd0nkmtUunFzvU7uGAllwejsfj2Ac + nAIhKr2ELpd7/dNYzEOV6LCy1KA4exKAARG8sAdNn0Nn9ph9ou8quGOiFnmsqvdGmW0u + dsoz/2Op1tK3eEusRfz9hb3e8LEqVhYTJGZ6XTiL7eELZYsuYHA0LAgE1uFiXil9m8XF + oHB39U3102243mz4XBtlIwes1+1tk/lD5fh/AEfQAAERhTn0Rg6ksfQ6uoqoKAeAAMnS + WYIHIAQCAQj4IhCwz4Pooq7w83a+LlEcTN7D6cMK4D0vlFLhoW47kuWAAYF+hYEA9Bsd + x4ZocIWfJxoWAwCu88D4r5D61tUwqdRHJcVpQnqfvOuazLWtq+yQo8Otc16exA2bkHqd + p/OWuB/BAKp6hAdh4gAdkeIcVRDAqV4qh6e4oAABoQRwBz5xc3jdS3JclylLqiqJMEQr + 5Q0wpMlEVyour2odFiZn8wp8uQAAbGsuM5VEzJrC8rJeoWAsjgM7stJtRkYog4irS5JE + SUQ/K8Rc8z2K+nS9vwvVcNuux/HzTUAwGQ5Rn0Q49E0fQ9TkCIGpAdZaggc4B22AYAAm + Ezi1wiT7TBR8UKuvNDsFYdiUdSEX1zQr5nupoABkZMhgpUd9qsaAhoWexsVTVoC26w1h + N3dVY0ut9YQ8/D40tYFBRBcj1w63CUvfLElp3TqWntMp7n65B+A+Kh6g+d55gAd7qFIQ + ILFULYhHsKoAAYDSwAqwSVSc2csXFRVZUJV+DpavcrxjKdG4q9Dw17S7VuSeqFhMTyFg + iH9+a4hx+nwhZlBOmB+IWA4DoXbcur+u+lP2u1aY5E9eSSAFJp437y0zuWmvpLMW761e + L6+44Bn2AABkET59EEP5PH0P7MAeBYAAkdhbAgdQCW6AZ/gmFK2AEj9XaKwMk7dRdE19 + pFMYx0q5vJoEYKNKstqFoufr4fR8qcN6Fg2NWu+EAB5mghZpiZXQAASBUTqBEss6M+qs + bZW75JTvL54nJUQ+jhXvOE+9I7gmy4Hudp/nwfMBHxNU2Hjqs3qoTo+AmUYxCOfIuAAB + YMoWAsCyjXqnsYU3VQLpkwrmgSa13K73pEOdqh5qZd1NFjA+QsFosSxpFeGqIbweyFjo + FCkZIcHCcMQXMix2rbjVLDYUpY+DdDgKObulp6Lpm/wKPQlEr4/B7j/H6ARsoAw+ibHy + H0Qgoh9iEIiA0BIAAIDsFuA4dQBgCABSK58lgAVurtMOuhdT2DeIldOz1UK8YDH0YhGV + L5dYctRjY9InY+EaAYDQQsDxU4OnUHgMQrgWCHgIie9NqRMHYohYgeWCLCYapJkaW9Tp + wG/u3RcwgvpsD9m3Z8oN1ZcWmKuh47QAA+B3D/HyPZsCbGUJ+IgH0MACBCCCDGASED/X + /wBaO6s96HyhwFXS2s3MEo0upbrA2Q0Z3AQQUrDWZLPx/tlAA+shYKhWELAaDCPZVR+M + sAANFf80RzJDbQ4h0cLIyF5ke+Q4o/mHENlCYOY7TZNwzfK+OGz33qw6dOAFKrGm3gAM + XM8AhyACBqEaPh4JDwBjeFYA8b4IQLj9A6AACgKyXJHhafOZLsi3TEhPPZwEloHonb6o + eRb2lxLtkOpZSw/lqgABEIlrIQJskMHuN8hY3HfPEGcqmDgA4OPbYfMt10657HkjWlZc + UKVJRnUcrAvShHtpZnZUUlcoZhoxHkpoYQ0R+jCIgA4fIvwGjsAMAgA6RQJAoJ5LuSsB + VETzds3WQ6UjYq2edVBRTHWoPjYkiWZrT1zxsmekBsBIJAAAA+Hw7zlEGkxHQJ8hY4RD + oxRoAVVoBITVNkIZhiSIFZ0frxPF6JJ65T6bbUs9ytob0odW9lvsyl5MRIYNoao/RxAW + IrS9AZDAMj+GEAwcYAQDALJOBFcDC1Dl/mM0KBjFJKwOudGqBClGMRme68qqtrHyQTsI + p0fjhy2KAT4DMhYCqLP8vSAOQY9RqkLvcQsejxpRjeJc6MAsHABMGnhF+0ZD3apev63Z + dEjT8UqPFO27ryrA2pV22yYhZSlV3koREZozB+DeBgCQAUF1UkMA+PEXQDRpgOAWhhb1 + 6Y0sRwTGCYOAFKzuXDa5VzpqPLEhhYCT9T1BXek/DCjOQCYKdgqABkmQ25nlYMAN0boo + BEmutCzGc8YY3WozjVLtWLUV/bg61hdc3rOnWCw6qWMl5K/IWOQbw/R3F0sOABVoAAQj + FEwAwXIOqLQXAk6BxGccbVwgPW8t1sWh4MxtaC7Fo1anjsE7kolK5E4LnvUSf6SZnaRk + RMDSuAczX/qwsO1V/56q2tRpbB6gW9aJwroxvxL8itVAALEYY/BcEMowB4KQPgBh8FaI + UBIYQAAIAmSwBoHNRaLP0bKS5dNlYNv7jnQKl2fSPerF5QMzq6pN1Xd5WaXoETDx/BHH + 5PVCUiyuTeutUG2JZy9ivb+DJmQontautx+t6xpbcW2MRPrvHFn5Da1o5Bwj9HuBwCQA + gEhTD2PcMRxQLlkAAEQbgqQFiYBABkAICgAgOBAScA6EI1aAyxYPQq8D4Uez/YJiNSVX + Rkr2rJhNz5/q1cBCrlTQcAbxqdugla7dFt8ulzrTOm3upQ5uoKvXMcHQ2OMX6UY9TfAH + H6P8AI1hwD+HGC4L49wZEQAYQwGQLAQgCDKM0ToCgtX534AwDZQwDgS5nswqtG9A77RZ + y+5rzoHaO1RzLHi8MvbP6FYTdONJLc25JMjA+iMv6hnpOrTVQ9REoU6uO65u6V6ftXPS + AZdK72mKFv8ho9x6KaAGPwlWRx+g2DKPcMA1Bvj+FeRBgwAANkMCGEIGQAwvivEMAgHm + JouD/AGAfqgBgHEnAHnHa3I0RXZ2j5bd27249HdVdLbHio4cux70qz3fOl95+m9xHdJ+ + TfU/HqYiWUldmtH7OweQ8iUgP40AId38h8hUD4PgR4vxnh+hLCGByiqHmjmCGAgAQAMA + Agjg/AwADAbAtgjACAJEiPHqawLwMQMwNQNwOQOwPF9h9B9jfBShdB+BwhBBQB9BQBwB + 0B/hYCGBtqbCsLyKYCGLzCFgXlqM5AUgPgBALAQgMgBAGFVPJnhO6QPwkQkwlQlwmQNB + +GShvhzh/B2urB/Brh4B6AABliGKeiFr7CFpuEGwCgAALsQLfCGQaITHRwmw2Q2w3Q3w + 4Q2DxLxAAB5CGJwrJiGB1CGF6muPapBgAQxpxrevLw4xDRDxERExFO5nyHeGAQZCFvKx + FxJxKRKxLRLxMRMxNRNxOROxPRPxQRQxRRRxSRSxTRTxURUxVRVxWRWxXRXxYRYxZRZx + aRaxbRbxcRcxdRdxeRexfRfxgRgxhRhxiRixjRjxkRkxlRlxmRmxnRnxoRoxpRpxqRqx + rRrxsRsxtRtxuRuxvRvxwRwxxRxxyRyxzRzx0R0x1R1x2R2x3R3x4R4x5R5x6R6x7R7x + 8R8x9R9x+R+x/R/yASAyBSByCSCyDSDyESEyFSFyGSGyHSHyISIyJSJx4iAgAA8BAAAD + AAAAAQBxAAABAQADAAAAAQCZAAABAgADAAAABAAALO4BAwADAAAAAQAFAAABBgADAAAA + AQACAAABEQAEAAAAAQAAAAgBEgADAAAAAQABAAABFQADAAAAAQAEAAABFgADAAAAAQEh + AAABFwAEAAAAAQAALCwBHAADAAAAAQABAAABPQADAAAAAQACAAABUgADAAAAAQABAAAB + UwADAAAABAAALPaHcwAHAAAD9AAALP4AAAAAAAgACAAIAAgAAQABAAEAAQAAA/RhcHBs + AgAAAG1udHJSR0IgWFlaIAfYAAEAHwAOACwAIGFjc3BBUFBMAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAD21gABAAAAANMtYXBwbOoCxvvn7AuJW4CIyiOWp2wAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAADnJYWVoAAAEsAAAAFGdYWVoAAAFAAAAAFGJYWVoA + AAFUAAAAFHd0cHQAAAFoAAAAFGNoYWQAAAF8AAAALHJUUkMAAAGoAAAADmdUUkMAAAG4 + AAAADmJUUkMAAAHIAAAADnZjZ3QAAAHYAAAAMG5kaW4AAAIIAAAAOGRlc2MAAAJAAAAA + Z2RzY20AAAKoAAABAG1tb2QAAAOoAAAAKGNwcnQAAAPQAAAAJFhZWiAAAAAAAABxDgAA + OesAAAOdWFlaIAAAAAAAAF8vAACzygAAFlBYWVogAAAAAAAAJpgAABJgAAC5OVhZWiAA + AAAAAADzzwABAAAAARhic2YzMgAAAAAAAQwaAAAFwP//8v8AAAdgAAD9zv//+5j///2W + AAAD9AAAv05jdXJ2AAAAAAAAAAEBzQAAY3VydgAAAAAAAAABAc0AAGN1cnYAAAAAAAAA + AQHNAAB2Y2d0AAAAAAAAAAEAANF0AAAAAAABAAAAANF0AAAAAAABAAAAANF0AAAAAAAB + AABuZGluAAAAAAAAADAAAKPAAABUgAAATMAAAJuAAAAm9wAAEXsAAFAAAABUAAACMzMA + AjMzAAIzM2Rlc2MAAAAAAAAADURFTEwgMjQwNUZQVwAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAbWx1YwAAAAAAAAASAAAADG5iTk8AAAAYAAAA6HB0UFQAAAAYAAAA6HN2 + U0UAAAAYAAAA6GZpRkkAAAAYAAAA6GRhREsAAAAYAAAA6HpoQ04AAAAYAAAA6GZyRlIA + AAAYAAAA6GphSlAAAAAYAAAA6GVuVVMAAAAYAAAA6HBsUEwAAAAYAAAA6HB0QlIAAAAY + AAAA6GVzRVMAAAAYAAAA6HpoVFcAAAAYAAAA6HJ1UlUAAAAYAAAA6GtvS1IAAAAYAAAA + 6GRlREUAAAAYAAAA6G5sTkwAAAAYAAAA6Gl0SVQAAAAYAAAA6ABEAEUATABMACAAMgA0 + ADAANQBGAFAAV21tb2QAAAAAAAAQrAAAoBAwNzNTv9zMAAAAAAAAAAAAAAAAAAAAAAB0 + ZXh0AAAAAENvcHlyaWdodCBBcHBsZSwgSW5jLiwgMjAwOAA= + + ReadOnly + NO + RowAlign + 1 + RowSpacing + 36 + SheetTitle + Canvas 1 + SmartAlignmentGuidesActive + NO + SmartDistanceGuidesActive + NO + UniqueID + 1 + UseEntirePage + + VPages + 1 + WindowInfo + + CurrentSheet + 0 + ExpandedCanvases + + + name + Canvas 1 + + + Frame + {{2002, 20}, {1215, 1180}} + ListView + + OutlineWidth + 142 + RightSidebar + + Sidebar + + SidebarWidth + 157 + VisibleRegion + {{-4.5, 0.5}, {522, 535.5}} + Zoom + 2 + ZoomValues + + + Canvas 1 + 2 + 4 + + + + saveQuickLookFiles + YES + + diff --git a/shared/sentry/external/breakpad/src/client/mac/sender/crash_report_sender-Info.plist b/shared/sentry/external/breakpad/src/client/mac/sender/crash_report_sender-Info.plist new file mode 100644 index 000000000..976687e11 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/sender/crash_report_sender-Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleDisplayName + ${EXECUTABLE_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + crash_report_sender + CFBundleIdentifier + com.Breakpad.${PRODUCT_NAME:identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${EXECUTABLE_NAME} + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + LSHasLocalizedDisplayName + + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/shared/sentry/external/breakpad/src/client/mac/sender/crash_report_sender.h b/shared/sentry/external/breakpad/src/client/mac/sender/crash_report_sender.h new file mode 100644 index 000000000..6a29d48a1 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/sender/crash_report_sender.h @@ -0,0 +1,117 @@ +// 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. +// +// This component uses the HTTPMultipartUpload of the breakpad project to send +// the minidump and associated data to the crash reporting servers. +// It will perform throttling based on the parameters passed to it and will +// prompt the user to send the minidump. + +#import + +#include "client/mac/sender/uploader.h" +#import "GTMDefines.h" + +// We're sublcassing NSTextField in order to override a particular +// method (see the implementation) that lets us reject changes if they +// are longer than a particular length. Bindings would normally solve +// this problem, but when we implemented a validation method, and +// returned NO for strings that were too long, the UI was not updated +// right away, which was a poor user experience. The UI would be +// updated as soon as the text field lost first responder status, +// which isn't soon enough. It is a known bug that the UI KVO didn't +// work in the middle of a validation. +@interface LengthLimitingTextField : NSTextField { + @private + NSUInteger maximumLength_; +} + +- (void)setMaximumLength:(NSUInteger)maxLength; +@end + +@interface Reporter : NSObject { + @public + IBOutlet NSWindow *alertWindow_; // The alert window + + // Grouping boxes used for resizing. + IBOutlet NSBox *headerBox_; + IBOutlet NSBox *preEmailBox_; + IBOutlet NSBox *emailSectionBox_; + // Localized elements (or things that need to be moved during localization). + IBOutlet NSTextField *dialogTitle_; + IBOutlet NSTextField *commentMessage_; + IBOutlet NSTextField *emailMessage_; + IBOutlet NSTextField *emailLabel_; + IBOutlet NSTextField *privacyLinkLabel_; + IBOutlet NSButton *sendButton_; + IBOutlet NSButton *cancelButton_; + IBOutlet LengthLimitingTextField *emailEntryField_; + IBOutlet LengthLimitingTextField *commentsEntryField_; + IBOutlet NSTextField *countdownLabel_; + IBOutlet NSView *privacyLinkArrow_; + + // Text field bindings, for user input. + NSString *commentsValue_; // Comments from the user + NSString *emailValue_; // Email from the user + NSString *countdownMessage_; // Message indicating time + // left for input. + @private + NSTimeInterval remainingDialogTime_; // Keeps track of how long + // we have until we cancel + // the dialog + NSTimer *messageTimer_; // Timer we use to update + // the dialog + Uploader* uploader_; // Uploader we use to send the data. +} + +// Stops the modal panel with an NSAlertDefaultReturn value. This is the action +// invoked by the "Send Report" button. +- (IBAction)sendReport:(id)sender; +// Stops the modal panel with an NSAlertAlternateReturn value. This is the +// action invoked by the "Cancel" button. +- (IBAction)cancel:(id)sender; +// Opens the Privacy Policy url in the default web browser. +- (IBAction)showPrivacyPolicy:(id)sender; + +// Delegate methods for the NSTextField for comments. We want to capture the +// Return key and use it to send the message when no text has been entered. +// Otherwise, we want Return to add a carriage return to the comments field. +- (BOOL)control:(NSControl *)control textView:(NSTextView *)textView + doCommandBySelector:(SEL)commandSelector; + +// Accessors to make bindings work +- (NSString *)commentsValue; +- (void)setCommentsValue:(NSString *)value; + +- (NSString *)emailValue; +- (void)setEmailValue:(NSString *)value; + +- (NSString *)countdownMessage; +- (void)setCountdownMessage:(NSString *)value; + +@end diff --git a/shared/sentry/external/breakpad/src/client/mac/sender/crash_report_sender.icns b/shared/sentry/external/breakpad/src/client/mac/sender/crash_report_sender.icns new file mode 100644 index 0000000000000000000000000000000000000000..e8c21242bf84a70a1efb7e005f47684a882eabe1 GIT binary patch literal 170816 zcmeFa2Urxz6E{4wkzJAkN>oq*C4-Vd34)4B5Hq4;&N=7VU9w`%Ip=VCo`16kCPA2P>ik?7kQ@(f0cz?5j1b5lc;Ge!$X-%pY6$kP~2fN^P94|x;(v&U$B zj7!Ikkqz<&@)$&RfbnF1xK**IF$b%RiBw`ltNxq!h^uZFo z)3H5d17FT*AopN&8rJnK87@~M-*$t7?PNo^oZUd~z%b*NjgcQ%jW12GNn~SW6T6X| zglQ~XxjcLYefi3T8Xv2GW1S*9VK`NVt>jik6)dZzl~#%>Y<^PkWRs!_X61EnAC;IX zsu1_+AzAY<4pl*ghMAO=*OS#{#U`i<^XORgv(j35v7|a2RRP45RTY-J&&|mbcZMT6 z7Wq6kr{G0)wn(}LqvM!iQDIJwq(meUXJ>YV;x+drx#IFyMM80A`Em?1$jlXs(jSmj zDVgb6$p+Za*U~C!S>@BRlG4(mM?)~RF%yaLN@1hJ#*GUbAF2lRpli^fg>X#QMH|DT zFb2cP)rt{`DS;+#PW|*SJOX2}+*VheuwaE_Tpa7_cb44P3v?4;N{k@?t>khqH`G!% z)@|xA0olJpXEkU=`sf*M!4GXa7-~Z$q0-&wx|sN6nsw@^ugt@|SfkvmtSzS-bm^d{ zNrPIN_O`J%nI{g>HPUUzfp)ZW>e0jK0=c=Zk*=0Hj;Zx>adfcs;oBP->9uRi#PBv6 zL>nb8#!=yNlvwxz=UFqra!Oe92V73^4gCS|J*JM(5Sp*9%DPEM8~F*AIKRPeG6(ZrF^Jsz7mFSMi&}F zN8V50gsI>dJC}rukSg7XF0?VEj35~@attb`Geo4EAenMf%)s#VsC*PD<15NHfS;z5 zaz06ylNrsW!$?^;DjkN=*JHF)QXY;9)A4nPHIS4?qQW4I9*Z$>EP<3!#|ao7gUbC$ zc{It8lYtm`DwN+MWpFGbZ&ChA$NG`-SXAB*qer4X;yNjVV;Ol9`7V==c|lFqA_8xW z9*zk9AZ6y_{^?NGdmxatAn~VJAu1W|Au-PhvE1AsN}UndCH)GCPccg^};JHgevP zVL;aDnWE%bXXL5YOSdjzFSSPAisRd%^Te@raDme3yZ&rR+QdT>X;e!FzJB%^OtuYU z(JwzNeR!F^6}4i(qsl7jLon4A=vFUM2bbuZF%>XVy|k*bQhMJHwICNi_o$+(vaICc zS$rd=M#uWxmzI~5-mfaT-xsx!8jhWKTvGkKfqYu<@C1g(VQpxbcWq_KOR|Z)Us6%y zjnUR&ZE)<^!}5~Hq@1iOEv-L>;cGDs8s=Ujm6p~6VR>13S!GX*7K>@%*r7+|((-DO zY%Hy)D1C4UHLey7bFG$2E6b%#A4YioCbv15tinUT#jI6B=05XxOf&6;;yW0`YUQN|ZrZ{q#&D+&~u?5;A84EY~#xUD98i)f_hQ5K*A7}K}+#a4jL9! zlZ!ZsY>w~^DI*`Gi-ba+NGG!-61iBH>>>&`TO!GH67CtD&G zi-;^{YIdnGC4)%kWwg&sEe`@m(@Czc0*gv3%F0Sh3-gQk#rzVbihH`q9lMMXjOK;n zqen-FvPXCM)GISFnv#anE{wr6v+K&-P7NOpHr=&%U@QjeyDb^ui#Vp&%h}1nNe9Eh z7wL{3Zf@@GHVp7Z95d+S?d@u9+Y8FT7wPW(oE-zUg*(~P!549?L!b|l{EEEY!&eu> zH^A`W8Qfa}{nlQGzK*o@$m==|9qj!(oDsP9>kBk-z~wt)nD1V&%uH`TKNsj&z~efO z9yW47BuTy*6EJ9CfIbFi)&0hWga$7p$(sHij#lP|&8}%UY3!IWq0?TGiTYg(JLnsz zuEW~;PaQX5(h%1&@_2i*E=GnO+Bf^9;jGCs#(P^ErjSwXx^*_{sIR9Si)jWgoHw$E zg)LAWX%Jwi^FIyB~ZDVVvzm*KqH$^47I?9olHjahx`dW3jv#~Q+O}ZJG zK#75_wg&P^9P7;QV{PBv*2cy-_>{S+F~z0bjtgvKgRLF9cek~+>1^X^Vu-l(b+y~I z0jJc1_8M;MWDliwT}D4A^BfJk7@|@wO-?wb>+I_2fXZyTB17)eGHEW;PzS#>z_B*2 z&{9;nMJf4)JpHk}jS?TzH}6T6g0e5P^!T`@Xibh=_-ZaMM!Uo`N4I{1HH3fI`8+lyL8`Y+4S=slKfAjiVrRzEn# zkid9}M!Z(bsWA{7aMZT|Xqj+T%gM~vHK1d|ff=q{G)T580ESH{my@ZjmC~_biX8z2 zw*r7;i66@ozpeo_$^tt2$VvaN0C4P%f*lFC^CbW^2sd+605BAPrX-Ht_{fd`ZhQ_v z$9$-|wIIkzpU(hj*p*M%5daL`e+r;u9xy<+FvDLs@c0CPhF$!O9RXbY6QFr~|Ad_a zfRSDyfrg#?f*k>zZ7GK_34*fE*bzXJqk@2rqJb}p5x~i24Iws$_~5H@h!$Xo0uAWc zkyh9dz!3xhqf|pH?4SYJU=5{ciq!ZOIy%uv?ng0bw|A|wBY=0^AYK_wzL&NBc~6Fd zfbNao!n6&AlIYxg1Q4%)j71<;s8dIRK6vYd&X&s_? z36sn!1qH;Q6h&nV1&Tot1x<=5;>gC0h+-o~uRs)r&ruMnpeRQav1H?B1w|R6FnEIE zO$9|MObakR*|=3fQGzJi){sqG5yf*DR*MnEY*Mxz#lmwiMiHV=DI?|EQRrW$g5iaT zViqaei6~}ci~>ZVTuRD!wov3ND0V9-@>(c%w@~CFikYNruR=-=MNv%3_qI@EBZ}#y zY`=m+f+#qJq56hVrpnV$`5;nuQo)~!$`eUB98={f zs62p_pH`G7qw>3?>@-P(a;mg1DL<Sh-$@yD4D+A_e^)_q z0Z|w{C7bTHP@G2;5oF^%1;sf;(GKP;_gW~lStLU9yP%phf%3W_6$ zf(y={*+Ov`)nyVX6E;)O4;@3UK~|hyt8ntZ;rfrktNT2IoGYD9=U+;Qad) z&JD+u^X_Xdm*t{zaQ?lDayX{S_n>lc{(J@hZd9H`%HfzQhnWI3%`a4xLq-C};z=1C zQ{_8QxgRMnR+Mj7%+iagivvm_Sx7dOB5HRG=D9$9nv}sY<^NlnJzU!C!<$hlTpi_# zQpi@ItD~y9l*(m5sZ>!44hE(2n=h!?JoDn)4=^6wrnw^x)?>5Ai|tOk{X|F1>m zc5r>vHSc9HfB)wN^P zolHW=%)Ao?)`#>Ujm1sEHWisw>Gsw<whz^@?9L!e4$Q;Vz7Nkn-LKywl0 z5Mg>vWED|~RS7DID!fw4uVhxGiNINNkR6rIq!!myRjmW2YH6Ohyo!)2RZ6RhQV>8k z0tgnS)exW+7cv_qk~CpfdPx;u%B)Hi4aMjZB$#zFwFqho_)^u%s;aWwjP$(v#t(TZ z`Fts>vM}WgXG1g{JdO;0nS6NwAB*_$)J#8k9AnEy;6%{-wtD-VLeJ_e!0M;`z z1**fS%FTKLk^wP_q{~ENX?O)wnu72$(M7N)J-?b?$(B|X3ZV;vkd*#5Jr`)`m3irV zKjMUgy-PD-JdJ^_gM5}&R9O)YYEdxh2xd3HAfk%Q2Z#t%zn@krji_K&=BDq#@HC{5 zM@C9jR3$yDQbAFkUM`KQU?d}5QW40m^t@`+0JKVRaU6*kWJQCjIfy?6EeN?~q*Owt zFI-Bm%*}=LEnOy&NP(n0Inx89B_o7w8M)O_P+lPZ0Ojwq@}QhvnUlU7qABRq(~^nG zNYJ}D>op1H04-l+6iQ<&7)hCsl1xNOMrY=LJp@2hmIa|PMf5PEL`qZ;5wx7+t5mc<*^m4 za)5$FT|A;NOG=Rn0EkwZQ?!<(A8 zyi6)aD0e87daR#MRO$@M~6u zv_fKI)C90OLr5q!tB$}i(<>wH93ADw91qFh9rM&$7f^=a7iay~|HoBW! z0i6MXj8y>WN)0)Ad@L_KpP$DBVCdEVK;5Ix4FIm%P|1RvtN4+Exb$8w5EFU)d_f+w8TfMZ zKq7=5U;|QFQjM&P=O#wwhv#94t{_ze{r~SsTb&!p<<)%DCqg1OEuW$krbOkh1wFGV zJujiLW417>rjiH!EiRQ3m5kDYf;4d?xL0aAVB-1tQF&M{FOQj@inP6mfME(*6Pxq6MQD3{!$idR0YnK2g919!Vn133yc35-$NR0VpQOW#vhd zZa~iXJd!gR#;m9+9&~z@r3Jvo%V*>yK-9!T4O?+92eReid6Bu$1i8$-6wz#qehx`f zy_8Z`!-q-{l}c6Rh4}>q@E>?{6Treeh~vnD#K>GcFE1(=%RwT=$^UDE;b)OZG)(ef z?4*}~ijnz@yrdG;f;4e*-lkkU2gsRu$!H`zgCxP2Sq4!6UQxIu zAwP$g%M?-L<7w2?FqGC-!x)a0#^vK_LLh#ZoB_oA93nS7hn<^~nVvvZTs(0f85x3rd5LsZjhYRUu!^n8#S znWvsFNd&J)38U(J5@dBGwwYfkJ3B{6Nk5LH4;7}@32GSCHPyxW`~oaLHcur#=N_~f zVlKG{RgI#=^0SE?BAc0$h^l<7MRXk>u~rrVD>&=gJZQ2i%1QDPb3o;&9C|h^$nbKS zxmh3>1u938;K7t&dabkomVaOFkh((;mXVR^GLEe#fv1R!!#HY+D9^&Er^hmp8|tn~ZMJY`^JbY({a zPpS}j-X>)LPbA3M{HGiSelWeHo~UKk)RsbnMgl`ju5w;RI%MvkuTBsG%dTv;1gx2v zeuM@xAdtvNzt6A5ODO?7pjXSwN`8mvAH<7tfHEc*`8*1^j$GFFyjfqjHoa zNkX*xmYx!x9gcXyCE#`~%o!OF4eV10zE9NAN(&0nbskOe(y~($c?oz@YIycOBq_@i zqwPh@P9W()-F=|tQBBRyjR0rDCE!yS=)}w3Ai>4r7zsT)BV+GAbi;{9Mr!^2`tpK8 z%up(@*oBTMXK&* z@FiOSL?Bj@z-nRwilJZ`8pTkB4}ezyyaez@=CCB$;)GH((kp>i#0w>`1{xv8M1tlY z1p>t7Ycbk3gkYYOdcOoo4@W~FLdedRWT#~`LR8m~o-BzL<027XOo(DYJ|Si#qBvwL zipnB03#IuWS^#SEKyA2mC1TW%$Dod|`Yy;8i$Jh~pWpmN3C>DVg%I{^X%6kuDv|yI zus;DHf^0@sGK7pVRDh6}4%9pmG@b|=5&jaO3MN8oq<|Wm5ds8k#VrWX^$Fnerln`f z<6zRCkqqp7AV=&1_>0(;VAZ!-flY`V;;tgpcqmjLzyYGN@l^2!5*H^D;>{xPRV1Dx z0wxh9b0Z~Fm_;$6J{)B7Kqg(1odSGJaUxI(!$p`dmeQGMW|pL&D#L^yBqk^E;Oan~ z84tRa2(-4HorW}KgT`Pop->>gg~Aw0Y0!WWv0J3CN7rL`R%S5)2z*dkpg>?|XMSR~ zR6yau3N@ezK$93$4+^EZ;*g7g1@EM!!Xg4LX%zPeV7Nt{07KN=fDlkt-^~Pb2?-G) zLpK!_XwzI=kgLp1`EKpY9V5*lWj zm{d@NPB_PR4D2B@*0-U*tEQ*I^cTu()g5(e*#5ODkY;BbS<>fsr0;J3Il1x2P5+ULm*~| z(h`arA>eK-j!({t72;W0d{Btrj1whd!W{)^r|FO)f++S@8Ud98Jz6jb3ZpHwaC$;| z)oWN#l)tV@zn78)6+%GpD+bVz!I7<5VsXkHeJX7Q3DmpkMSQRj>Zl+dgHDDI7d9bM z5oRXeOUe|9GLzzy(s_Ww%OYBk6h45OOq>$m0cE#9F}2^*!;xADijQI(7&#)q&j}$X zBP}^GF)1}8ODF&oghZeUDk`aHLumihKh#lT4CaAbGjphxBcK-$P++L96hO0MSps21 z7AE9pLCf>9cu1+J7C&H$GczHjMYUJ3D76GNK7!!G1x&ydArOayK14GsCR~fY@S7`! zFhDmEQ<5ZxbZ9IJ?AfQ%Ma6Ix6EM0_7-;+?#1Y^n(OE=hcovo!jUb4uXfQBOs3a0e z($Y^u$UymD_tcckLZS#SRJb6IGBBzLUldK5qM1?Q#>nXeB4$c*Y7dMShdKn$cp(@~ zkQts4kuFFRq(-J3NXAl#ZT&5?7#%E8!^Pr>xvYr=w8^9d%`8 z$oMgEWo3O!!kT4eWu~jFj6cQLY-N2rYq%MJu%KJ(Dzl$}X3FYTHa3Jc+X_ybt1HtU zV@x);V|N={!iH@Pv^EA@Hp&Yst6SUI+VX8!)>hW$YRa%)M`v>l9SA$Z7DoVdTLWeG z10+<<#-8X-*x|OeJR7=&nlfIGaex%=PU+sGI~1^OZES7qI&j%2J*ceK-O+)tCwkzh zh-qtQ2@G`@3m9DN9US=fc#j@Lccz`KJ?Mclg}~tK=tuw$l<>PVdRPHN4Mt~iJGv20 z3`Yk?2PorEvh09~RSiDERke3@adsx02uB;H-dq;gPs{$E8 z)xm>s$KBirVCDlh%hAyZF_u$|Uc8>TyF0~=u=!352WJB=6P6CSDo$P=L@&H&PvXyV zmXnjCvyF-}EG4oy9eaCt5*~Q3UIdURB(R;Gog7{CxXeXL5mzES1^IID*zN@bgqiCFX2si;hrFe=*0&yO2`T>Zni4i zB8&|HeS7=#?oD{(UVx*}h2i1R6IF@f;tnu{7@Mo?+|S3?$A{>RdjlFGWO{nq_vqD= z=gx3*w^imAAZ0rE5x#sM9KjJ@9Pi#eZA^ST&3aNqJ$1Rvd`yX}?9$)2Paj`j!iV2m z#mCp%(cC8V`1AXtySfoQneJ}A04on+8T9A%#ryOjeD!^NeEaygT3ZcVn*)moq->?R zyGPHSK&s1SE(3Y}jQaNN(+7UsZLRyPy7v}_2T0qKC%bxh^y<~q-NQzin?s=v zhIT|=g0ExWj|om&F2Ix zqR3#0Tdj!SreOcx&Ee^E}U!wOU(V8e7wEA5UdA) zilI3XMIcNcws~bFsA4CMQ0)o6k;I;`W;rAxc&5!7-j9`6wdwcf=&~%K>;kXPF3?hP9 zfkFNJU|leZ)RL2x9$tR^`l0qhI(YZB;c(JGO|H)1@L*yPD`=3vM>Z*^Oq1f+r$4VB zlcELZqqXDGQ!zG2$z|l=A%h1Cf?0!t2f4wWsz59w&)N4MKtStqfEV7`VoB@zWo6`2^pjP(BZ@|J}@u_4<6EcHr%lwAL|j|PYk3D z=-;34!}|d_-51~!!5m7?qj|&m!-%2mAw!1xg~BRQ1j-IS_U{u2?Z`j^KmxO0-vQPf zP6EV0oOZ)Tjv$6d3}X)+IxJuy#BSVOh z_z2(;3}X!Th4o92AS2`51A~Br;g4AO{TcoIK|nl&Y@Bu@Muh+iA2@i!=$>$g5kzS+ z@`UT4prAltq5FeYy#7qT{(UsKXkA>1?KCcgKMF@&1aL7%dc$IN1oEr6o`VNbY(xM* zkR1@k6f)0qXnb*z``CeV)#g(wMZ{;uti=&h(Vgcg9ir>3h3|a;p*7K z*2==%R8<+0u1L~^P+~L@${$69ut$y@5#qO)Y=T>$Va;z`Ut*Y6uz#PP_Ez1xz-@m9 z`uckCPg8|`3yISV3FD0+M&qHOf>BB#AtOfxOaxCAkX$hKuz~*Gj+UlfB8@nPh8;V0 z=%5GUV9{QQ?Ko)+Z!9qeZq`IHSs|ka52KQcN^&yKtZUcKox2dmj4oX|cQzsn>4=js zpn=32NTNo__=px>ViYrUgg@MTAt2kz$VEF%}HbTdWFT3<2iLWLohtHidSHhuzsJTVRr3lof0g1a|IPw2OqVwaP{+cvW^ zo7o9NWmIFNw!Vf6`+qPna@&lW96o^mK zRayY_bfD%}FqRU#$IJ;6Cq_)b$Ac1_u(4qi#t!IVq1z`MT@??_47!;TCTuXA0)V9e zAoOS&Dy%<{3hJY#@+R>n3MQzHA3tv5_y7kpa~q@XRN9jTH{RRSjPa!6^oiuUM)X^T6me$smmR4pu$)pT`;8qT3%+G<;6hQiVy1MN_ zn@bR{vFzqfolZ={r%n}2;Y^-3xxcN2jkT2(&yubIH_$;Mp^;n-4g0xals;-i=+fG# zFj3l>t2%nl^clo-d>X*1Or0`q=16Bt8ykoNtq4noRojh}T5@u{I-nsx`B=Xewe<9K zb#&URC<7XsZM$H`%$X50*wbcB?Q3mi3sE9Y0a~lbEEfr-*le1XXOXtm>&zr}cJ9lBIy-oK|0C|>e zOpV|!7le0S)f9CQpW=PI*1$7#2yL3W3Ik>eT;;J#=Py`5%xBG8uprRJt_KyFD^vtT zGi~bjRJfT)PWb(P3 zur*7U5la~>L+!fT^S{PyHwu(&BA;67|21Yi4HeD_OpVF1AeJp(zHHg5VYWT&VOH~b z<>?kydcCQKeo73Cx_zmr)~AYsLEE*{Qd40Z2a_s=@mH)|v0`x$vuXtFj?mOm8({+UL9cT0MoXwQ7w%-RgiMH%U0F& zE7W$M>WYloP75h}6jM=F3X531dd->vwvP6#!*Ll}q2?i$G#g!|h$5f1n_kj*;jwAIjPt84f*vUY1KYpANw4`LiH zYwTKn8=HM?X`R;8)LRPU&6oNo;7j!OkZ`f;rm+NWVMZea+@U;QwbP&IDE9dlI_M577!0=2rsd8~-+(8q zG|e?P!F4D{;POT}8K=_uqwWY@?;E-!>FDxJc!t&@Qidi9aH-E#{V`-U zRd{rXOJ{eA>u>K0DT`0dj@v;d35_8vD;pyR7&WOW;!|5~Q$>`4?5+PTo!h9Yz={l$ zOCLAR&h2Be=1ZG1t!y;lCczj|wUOM!HTf7`e-9XRhKH-@ER(5_Liz@>5(G%r-HhPD zO@M~DU$3JHia(npSc7lsjFg5)xHw9Eyqr-6N|9pvTx*1FW90G{&8}79$pHKIrY*Aq z!ti_8s!H1+JIT@UaHi5*pMxu`%|ze^ej7+`9}NX8GINJ-tE>*w09XN1;&knaaz@`! z`k&ycDxrtfl-s(XRMS_$l)}n3)+iPQbHG(Ytu#^Q08cl1_MQGqohB{s(t z@_pYDK-f@C+0`&d(UiHmpEl+9@YPjS*(j+C6>uk9@o&_Cu)(dZ2`jn=Jg7jq;RcQ| zdvzRbV-b%{B3GZC#GSK`Co>u z6eUc(*|{vOEG*N*CAoLy}l=mj%r3&=~!BV zI5Pva4i3IP_Ik=X#wL(qKz1ZL)2JLnvpC3#e5-0I@aQoM=V-aRf5Vat$~_1h4CHBo zJPUIR^KPB>+PBw*nxVlH)vhI_@dLGk`@lJ>_N})Y(VcDw(<8zbgHeNy)-MQ)2y;fa zZZKsgz~$KrGlGMBr=NuDicrNk;2=z{k>@wOk^9l>(fpMT)iURs(LfN-R0W2SkG=hm z_#zV)g1IWz9SG5aAR;o8n_{0r2J=A zM^~57(fqovP*sM$g07-!u2^cIqfU?4JjPIKAc`dy!j?fz@Ci%Y!h$fz%|IT}6-UVe zX7eCM7_k-7^a(vJG`(*RVf)8!NmWG^ZeCYnC@X72WY?`5A9UrL2#k4Mc%Av6D$kJL zk?0^WAb^_(x)R#F_EXK07Qkc1s*OVIQzK=m3OKz;X+bYg5eRP zl{0A!#wW{RB-Vs2q@Ggs{PHPP?EiiKp9TIuV*#AbU@+j}$^SbaoS~4*_`jk7?47`I z9652MBL{7x@XJ3mOr2jvJ)K*{SBHmbfAI%ydntmOTN)c-Um3E<^j9kY&(md-AJ9YX zkWu?UX2S!eztjK>Y#BW8-|}f7mtif=_)FA)=XT&$wHER4ty<%k8i0W7K1xT)2PxcW{Yy-bgfOuApH&}ais$`01Hhf=@G$8o)kj(7GI&_?7a4$h zZu>Lw$N)2cl>y*MG+1T)LcW|V;{GB7pq;Qjb^c~eWaL!XG2s_{;3ocpud0tc0By(h z3*^(WiLF#0dBDV9VF0*Y4ssh`v_3kNlR4~PU;sL7!}WFRBL!sN-T>y`YZiDsTij~( zkpaHB!OXuWpN@roqx#4I-`v3FUsNFL8`Vb!$o#Lbz?Q9cP1XUeeL!t4^aG+1 ziHtn{Z+l?#UM)W;AMVt&`hPKf8S)qUO!-IzJfAHdOmfu9ahv&Ke1o+8sbZpn(sy_07UH_f|s2$LM z{OU(0Xd>Uh)(JmZ?G|=Aw(T1m5jI=9wcl_X@xNsN8rB8&zGzLzch14>Ft9^YOAUVN zFCE+Xx2uo#Xx#Ac7=VTuQP1dqr?RaT!9IYn2h>khyqTHWj;%Ebe{rsnjQuwZ(7g5G zU&v~$7;d^Z_!kU7?KRgL{$HMJB*TB+0PrLz?9}s@vRWg6?F8V#z@PL0cum1SaQ z;HL4P%}K(}e(?B7Yqk4UGkq^g0LkeY$y36 z_53CcY+VQ&#r;DCsGVEC3H@UQGIH8K?F3;Pp4JlWKPI<@N=_EQPBQ<<0MyQ$Eja)9 zuMEAHsTL`rkGH9UJklv_4YtAEo8-w12_*n++r< z)BbMz|5c*>$1LAMB}Y5&{w)ur_TX({|LMOnGV$*@0JY!sPmBE%Ku!kyEd%^@qWw?o zANkhGdk? ztM3;FU%oTqPn3WDO-BClqv?PjO|<_#Wj=qe#TPaH?C|S%Ui>{ttyBMSTAq5RM(Y?q zb+(Cg{h=11-q7_^l3Hi}!L5w>04);&>P17>hpIJ?cWmA`gz!2;jjCkZ@kl_ zb;Q4S_Tzg|zGs4Oy))(SiE5qt2h#F?Gtu6h01n*VL}QCc3JYH9rvfIZe=3y&YF#y=;5O@58O zVE{U|?mw?SGQir_4FE6iqqds(=XLubPT1I>L#vY!Iu`W{s*em1*_r|1orkdP&JR`L z=ZIjB8F-JyS1w3LFLe4j;4$?zd!|jAZ)q+i37mZSNpG}yZ%~1Ia&SX;~L7pw*7Cx-ld;@ zaHimw`rooI$6&T+-7jkZc;&L)moW&#C>D0v`0J+o7bxH<_?7VP_Afqdz0N_;^ea9j zdB|4z3+Wg}HyV24(Z5i;zl3P2-{Y(ORrN89Z5$l=dnUZ1WM3#JU}-mX>g@SrreeJ9gyX8+}csi*!aVp2 zKMXePAPyh(33F!7Z}#jqaM*dm#7UDCOaHKz*Fp`$4(fWS-XGv5kb&eN3|^D_5e|K& zJ_`7jLk1hiKmLcOJXD|n$C>bgNgS3JHI0Hsj0kJ_KO!h-1Uw7|w|)#5H)GtyMOKR) zth-y=!ORy`4U2>2P;!eXUaM_D!ylJ)5VQe5?17C2N;Ubl=&%H{rU( z#su%)CUt-GJoug0^)64$o)V#>QlzsCc{e@ibHf66HFVl=b47TEwKLCdA91XI&anIM z&HSd`I)BkP-df$ZN6Dad$@Jm!51GQQW_GkW`p=x!qCPzT@h0E1QDa#2I0ya`mrGE!FH_+BHQw$B>ul zK|dJQxc91Hox~Xdmd)+ zH1YSxBPNbopH*Hx&MIiqWv%f(`InS#IkmAp<9~RJ%jH$|iQcDGk{X9kOq(^m&#?j1 z4pnOihv<%8Ij<fj=~rv1FJoz`yO+F!G=-EW%rFR#v#Ss83%jKbc=12j^kQ>d0U>X3FeFj|GbtNK{@e3jo9E~$4G)G;O+*A*w-?sRmm zkK>~K2}<^(w*=s4rgByXjDMPwwyyVnpCBiH_A7&~P7!mLFV{J{Y4O8JTG>SjhDL4H z&OEkt>6tO}+VWSvnZ?L8dv1BI`r@fqhMAr{I-K#JzNz=wr`_kx>KxFv?gCxQv(Zm; z`iW`#>&A+6T(2CJau=N3TRk(8Ey8}c*klkrKHrsY-8O3&KuJ{??a@kS#$Wzg`asY53my>q7n zKWyvb9d{=mTDkVkGWQr=+ql?){Gh3;yJm+CAi|B>uR18cw(~|0vHl(X@R=|FFe={| zmz>64JTCW9f#Y!t=9vhcqwWYHDEhE9L_y5##T;5hXF4?#G$)q`H z^vtt?o*`F5vUP8cQOS6{bJ5=RQw?V9+Sct|-@UI*)(M=K%}qJ~K!gU6a2?wYE$U87x4>AHl_d+{d%g2sCgi{5St^O3CV{PMuK$D2Qldhzm&@|0f3 z4qaP0>Tnxu?7(pC*%c@0;d4ew7JHw)wc2I%0G)xye2?7QY1lN`VMOI_Imgj4`ta~Q z-t&4WKh}P~^6YyhW}lQa+ti*7V{O`b_T7F!+4rPZso5jKZn3A^rBn_`sr2`Hn{Cwn!yk*z`-Nv6+VlKo zN4&W1L6^{-hi)%!$9o^P(qXt-iqG{a{m*Y(cTn2zcAL94+Xftd(s{Ej&r@v;vtqZj zPQE&0`PiF-eWIgRO-;L`$?^-rtx{=$nE0bT{8^8`s zJUCc;RtGzIr#`;xw;yrzTOhf2r=Q!woZXEfXMg98ZB$>pL9{0M?c2RCop9kc&9_BI z+_n#Ymo+K)i1WsCa_OU}-qR}j_0;uwVc?a&a;c>T+il?G5c^J#LLGmfr!ON1rfkz6 zel6amYqwDrkxG79<7aO>lPF#{MVK7$>bHEyDf_!@j+w%B==Z*HV#w-l=T#n+*`G7) zpA_16aqxN@q$C%W$r7?@bDzdUeA zmvYS>@7_Wto9^L*P&!)zG) zy^ptdNSG9~-DktwMdhlQ%!^Ix$J*cZ$#5UPtl`M(iauBBwsf6vUmUo|^U}?Z5r1qo z*0^0vJ6iYNcX0Gx-YJjKn|!xfI#jIP?^f(ude=;A(a<3~R?mE!x_a8_%n7kZu@x7` z^qtv#?T)6}Dz|fS{8*3m{?6P|{alj|L5H^Kc-hA3bq=wd6SH8iK&tC=;d#nV=Kk9B zWjI*dJ^QyOf*xE$(30VaNHdw=X{l8r0=_+`iH=?`eL!pRH+oO=m)q%kl`d7w_qZ z#uIM7>O<$IGqWE)d|@@|W_jGMjaS?@>aeW$#{A&8w%nNuraUxPt9L!8 zyel);%F2(Xc06%q)|vAUmv=j)Vc_K!j^{dzoVjw1EUmLu|Fp2_?}Vo<5BEB;`?3Gl zk@Ve}9XvY)kpVdeUanojFkV@f?KLadc+vh*R{5Tx!N03u9R&IQ+iun*J!NwXWsf6$ zZzzR?x=%gko>F8pt;^fw1?s-###hM+L+3S)woG`EyL8p{1?ma@o}mkB?r&*&mUKNm zCopMNoU!J~)$^{p6YpN6>^RRh6HH8uKH2>)J2E~<{~Z~o%+3V!?2*h8L`>tC(RhGW*0o>hS4_TDb^XZGxefDu9s;gYdx-Z zFkaAc*ulW2yyJH+8PjdY^AkH%kG|EX>cOQR!-{dUsrj8589w9nMn(?NEM?YSDt)!J zK?$JWpp$gd1cMi2lF0CTx!m*x?{I1Z0@y}6;)ShGq0VjJ3MT%W#{;eVZm`^XxMw`53lO$8`uDiCJ9r>O(IwXUBVS}Ev!_Mrzr^g?Q(QN1z@Lc`6 z>-+v&W8*lCiTQ{2y}Wkc*C=+CtZd7u@IigG2W>I#2k)_;ks3Vz>fz{!r1|CRqh@z5 zNgm(7)MDrMVP~Q@AMSbGm!IPP-dqhouj^9tvX{*cx85JFY+O}&r0QJS0oBl*CP$WR z>|`yw<3Aw!WT9W>70!JV-`nFK6^)SwXXf~q>8oOkM{8bd^XTaqeqmDJlq0IOn=THo zEyJF1qQ6CT@xuf7rE8DECIpS8&mA$_tnfgaqsQ85 z(bR^S#mTbE_PedN4O#xeazouqL8$0P&XxW%V|s>^PTte@ev~NfLVldbbw=T>DOE#M z+iA_Vh)?%_boRsvuZIVZ2C~!7`;Ixdqa^8KW7t{X=Fj_n$HV9VwT-HJhPEnajhWQ;6*b>}Ac%;}AK%Tx~8Ropw=agSQpjju17 z-z*FX+2+ky9`8e z%8o$Vh7QpWOxHdCFyCP4kt^L*ucffK)}r(&#--PM!*;vQUw=jSu3c_htD&~pPTc?L z9NBe!pm<>V@T*-9wiTXRW;}}#?U$sYx zu`uS4c9#DYJ;QU4lKtko*lof4teVpHY`AIut+32(W$Ui3KRKLs)T@1}$<5H~0qaIh z?x3=9ae>*M&GScvxdyoV+>8t?m8*>p+TH&B+gBm0%^EtMu(X&zW}45y0Z;d+`!3pd z@5WP=4O@o1!UFnhF7Ah`@6)@kzj8Fe*tg95)m-iE_H|dXZAH<#f&e;m-8$U~SKsXN zisEGr88G+=x z@1E^4Zt^x3J>|FG-jo;ybPHcSZuQMWC!=meeejDM){PS-baX#GTP-&!x8n7-qT|*v z!s)!8!F%HF7*05u&{oDbEY4m!XIMAyk}dZAmNNS2SG4y{c;RWP@k}SiKrchJYhaDp zo(*rUy`HHQ-3pJm)crvD+4L*(wFGVA-#M#wpvR?17L{tBynmTF;IY=(-Bl}g^-miq zB8JolkYiqSxn0n`cFWP|y(Rf;*-ILZZrp6d4XGcH|2p$p(BcnQHjjFBoA4caDEI`t z?DyCnPPRe#)IGo53=Um!$j^L4!dVBeD{d18BeU18Pgn?mHcsuSzbMqdKh%7o@7((`rH4GYinMR z+#Aq7z$VP--0By`F{5nwwSE=X-01EG&h(17yJt-7aw}!eOL=E5*!FaaeInYw zuEG6@^FXc8$1@WRw#@2xDsJcEaqYsUHr;u1s;5?ArdmRzx$dMXSfx|>zM8H}X7_bc z?|64|mk)T*jC9V8Ie68MBfBMG!%LUmWGt*3-J_TCuIrDu#8!*W0X5f;jY#RY@4CgJ zWzVmAylNcB7@3gVH}cNIv)+=XMW^kn0>rtxUb8}Gy)`kt&z&^hc1KKjpVc>>Y<8_` z|IRVpu%Kwf(c*0yL`=ne|Hb>3tt%RRSol~y%iyrX+1ObbeIyNURvDaMv3KR~qS`ej zPgHyCSQgZNG8w{Hesp+0X3Ct4@o%OLF&wt;Zo}oN5E7K!?f2efh?CVVr3G%2OfO8B zPb{)>+f*&qwAf&+*u@yyxVQ9ybOu%k~buxw_qlzI%r28MyYZG+*c6!^p(# zBFFjc^aATqy1P4Ip?c0aYmSX&d7qoe;u$qAOk$<)Gz!YIc{Zrxw=qmF{nSxQ#GJ~D zFB@cGgN}F_j9O^z{MMDURoOVAXWW&{$7G0k>7qyOd$GbX7L1UGZ+USdn>W`hp4`ls zVaQLr(BCY!vXAMy-oKv|7&JVr4~z1#H{Kk$@s-((R}zE%Pd269&kvfM;5dB4;sb@I zJulz1?Hki8Z+7PP!f~sV;N{cx8{dzk51n+%?QXlm^mCQB8SkAud$paevS5gXQ&{qa z(6xVH{^K0ODyOaO-X?fdE=%>oI*WZy`Hy+aPu|-&{`lG484q-m`pu5&ee1!ZK-uL< zn>4f=E)PCk!+S7W;+^(-LrM1%z?I|?tUGrq{?BmnDcWItzHYwV#0S;wpOlQ!)QX&|+9%2qi>>y$KIQuJ9Dc=1^Yc1x%Y8Oi--?b; z^gNzt_uO#u#7hoC_-l3#A9YWyr|*D-ngZGF_zeEwOG+yrt@2Mb(a_J**?LP==|ptM zG~ehR6-vi6IaeQ>oNk|FH|}ytAHirle7@%sKU<6X0ZL(;?jAGlUa~al@I%iFftpX- zE?GoN6Q}6!RUh&2O~=fX&T~2zsXUw@dv`0=ELxp&{X)3b9Aa44qca%o{k)bOIwllH z8+sla%0IdEMtI54rEhjFI&?hO)Y~G-=(;P7eql<)hOQl{UDq zj`s6iInm0u;#%o&_U6vw;IuW<`@X)j({PgEC7+T>@fSMlj$B+gnI%yVF%P_v}sXbPE^bB90P}v28 z3zwJ!g!S?GGOhH|1=(6=Z|@!&=@MEHzjpl6UVF6nwAub)br(^mSqFD!pY(2=*rz&6 z&9&o%#+g`huiO3GIr!-mRfRT8o`0s7%^0%Xvrt|t+j8ssEwWQZyK)BUo|@pVHMwza z$ql!FKMXWjnfephLtnfuc4VG$*)~_$;acRO=ffG@>}K6>vRbV&Wc=$W;r#mlxvWol zKKR)>v$&h9f-cm!?SGNj>FC8J_bulb9h2fc_8z>=9^rmv)!dY-@g1TU{C53e*S&|Q zi2Q`PSp!E4-tE@%Gqb!f#N6t|u>1SxJNMpXvDvEUvca#bGvYMsZob^?k#>9f;#;|U z_szK3v!Au`@VRH7?h9Q^yZ%^`v9^1ko?e|hy>5?Ey6+`_;xdwS^jUJNV5=zQR5=vs*-C)Pfbc%SwXh zT^6QpF1f!i>Hh#UK+C_OeML9$r*?0+;9dz4Moq9A7h2?My+B=Wq4r^RW8x!7N=zS?$)3I5*g{bb)GP}F|`I3YJB3nj$5Fcy; zOGv%D@iGaYW&ab19%E-le@wU5BhCseoA!AR8p!FCJbdayxKkjzrU# z46$y;|1$EB-?S}>fD6pp_@u=rzaauR$2Po*SAfy-vqmT5x_jZ$c>e|~|8^H3(`6X8 zru#}G55$Ojl3t3hpn0X;-I?!W1$_6y#Dd|}4SH97iFe2yVNpv*ey?ugz-oY|*DI*mQswWmDgF@D9=?h+D^ARRW!;s1iTJ+a$Vi+h>dJr{~1K;fPw zXQCKcf+OSn#@331&0E{GN*QLOEJUtyT7IMRaR5z>ux>tP=#v%_N-KBXkI-+7gs`mq zCaQZ^D*W6OziJkUdBsdI!;|I@v3VeEXw2Ez6q|fZ@RV(SlLoKi#kL>n&_Hf9GX3+vn)|x2 zl39uC@OfMy6OBP4W^MVOzVVkpa^qK@YY9|_kmfjQCi2*5J0_Y-(8pF$fzONHYX>~h z6G~LsaA2P2fyf2JhbsnG`8fI9Q(2O!K~S_1oK9C%?uK0tQpl7LOt zU@0VUBQs2mJ+=<0E4C5RZ|M*wC{!;`Vs7IIwP8{t)Tn}UJ zc#Y8vWV_ymfGJUPQ!R+KRP+-*jXbGL6Ln^NfclgK)>aVXUSXidPXQr{ZY4xrzYQ^Q8#Ml3Cbf}N@$X1DfKXas z+>E6iO^2XC#LQ(&4aW0Gdzx+bBsBM%$c)v6LVrlIUEDOCwU}NEKy`&<4C`1z zf!Zun<)u75BMN?&xtv?!!NvW?0#Vx!a1aonl4}Rtfr)!3@*)bsd5Bk1L#hIDb7a$GBS9>Fm895C+JLq1sXsi{I+2KAOTX(w8`)IR}*0Jz>T{ z&NB}E1h($%<6yKP&Qd!qKuL}3k!N8%m7pzhXgDK3jv1Z?)4vUxM8Dwk zLA#^v!yzqYA(L1}F|YXfl;(*1)RZ0_G_}D=F5ZZM{Hlhw=&aW$u>=V{+EN*=vA@=8^i=~`GVBMEgAwqpFV^Bstn>f)#P!(mv1u?|kGj#*G|8PH8r@Wj^)O%Zfa1@y@`NQq*QXg+@ z(fD@)!rz;p;|^1T z#$G>dFqJSEE(BH%_^Std$Xj&1??Rj@_4d&$U~xSm(Zq}fcYZ3XS2+HGzeiNG)~r$& z1oC)?^XypeK>Y*oumESXO~(3!O3R-}iiAef;Sq#gt#b_RCf^po+{<0JCukkfpxX*ekH+%^DkZB6v!Az(-4f8MV1Oky}()|@$ zuV+BPFyCn-;7~%|Y$_^?@QdMJTq+GQS2~#OEmn(nxX^Q&<| zT5#EMuqW0UB{uLc8*?a%D>s7!Cu2n!aeNT-=s%~@$3BMw?xpuC?ZcqUdGL`I38XPq zu~6{-(&W0-vDm!{NMpF)iE-kK&x#TCSU$6e607-)3Ab?`o)qEt&~vx!yEjc)&&7Ex z5gMAbvbkHmtd+82sSCa^0|8|rlUg?xH@K1sTrMON6MGoI@)XW;Qqb1VujkgC!;ij$ zn+I$!t(NVtlv8%NKbN_P_Y%;ml^I{Tf$k=SqTR8sKwd; z03~MvBu&D5VPE&HUC~50-~U;>PY8>)aeWe5|3)Y!%7^CEg?zGdleoFj~2-&;@E5eDlyFv>~N26wMSHA z3cSZ;q2K0D$fng=o3Ikd(89hUTq{T%Om85=JBQ&q6}D6#{NNf~SWnk^5;sUJ+Jp~G zzRV$3B2PobOjs*up-g0@CZn-oQu#ny6j7;$p=~R2k!h>24^WMSLwoS{iBa!xvu{by zRor3EnIl_;w?rjg`(u$j8J}O2&F!G|BrrE6dqPOIkP1lzXSD6avl*mL{K2BKaF(5A zdYXWk<=;HamBF+OBDptvZ@e)~b~8PxWl!=9o5borc?U#nzq5%Sp{9y@OGh)6il(kH zQYs6ywlDFN3p@yu631N@@o{uARV9G{_zGlPP!;ZKLQ?+juBZL@d)grMAi3_V2t!JC z<#wr6)a@MoVAqJ+F*HK$d&)l8lGS*c+ z)K)~tc+(hW%O6lXz2A%Xo6eJZVbSHY&~U+cGFxoVG_12Vyu?JAt*L!d^4}O5ZH@rs z_WESWlbzIohNUn<*IdE#4M5=kfpE-X4bk18#UJjtQoh`qVuv2#k8U)WIK8KefNfbE zo>7Fi-Sb3<;JHHFG=C-WbSX$hy>2yER+!Vh-LD7yx*F%9*y1G|JO5AP)}?MO!D57h zNrXd5qEp8A?qYcy!GwdXYOk~=qb_!jkrpL8_LuJVQKRgRtCeE{i_q{5MY^@aS*3Rz z^#>OEqa}(!oKH@}PQcXmlTv7J(5B~_EMUmtw#`f0GeRc+ZN&UAAhhe^rPUh123PwhXo$_MJl0gSmXhlivE%PQSV^&<4ba0;4;Ej6 z#D+B_Zy1O~Hkhgp1l+)iTkEABj|XtIkMbuoo<qAxFCbJLg#@FKqc%u9_x2 z@YVgaJ%%q@`(3`%O&vLIbG! zFU01q1vRdy+TKjHBC39#1+ee_gz9zgL022WREjZE+IUodn$)KYvauTTt_mr~cu7hW z_mZPfVZm-W=(CRYYt)!KKUD2$54eQ1eDfC9{fT9)?Qfr5^gkNT_@}<~URRl`o4H9mvs<4$UBz$|K=wx0O>^t-7vf&cvRSM?L8c5pLkHTq&=! zZH&h;g-E%CM2g|U_E$CFUz>wZ{Rg0cdw^pzunY|m$-q3P6OvDwQU-b{QmMrW|OL~5WU?sF{c@`3Ux)AHQDvq(;mKp*ohm!|PT0)Fi>V&vZaIok z`lgkNmbh61g0s>F13Y6jf6U>WJ09P|o_xyCiWB8dtn%p&dx>;Ai{=1~(-UgRkVk~c z4;%^XBxzEcU{WP|P-zE~O8RPo za^Rf<48j4|y5;_42IoYi2SswzjR}hksNuUV!v#4a*MlmJaV-Jh`m&?JpK2ZdX9yjy zglS-BJk^yktREXepi}DQ(zzU9RV~>K;=n7ZbB41OLrbUslrtUsVME(MdKYhGnKs( z<4Mg__odu{#w|XPY7Xv@)#ts7RmU~?S>@40*xGH3aQUEOs^CRZdn@{o+sV#)5?Kga z?!=nC;)z{P`q;Z89J9-pDC-g?pj|4pyHtwry{FnH$5(?D;>-VPwpuV?r9XgD?Y~^s zG~(tcjO!Ry3X3YMAOQT)c-S!^ebR)>Pr#)AbXA>S;i;33tC9QFP_K??otwE>t7sFb z6Z-9UbRwVT66z~(IZ$LXI4RbjDwR4b3AZT_fykQ){qN4$!%JW#uy;Vbe%Hk3?Es3) z3@^*&Q?kQGKj*aN!@TW{VU{Tg`-YA1XkQU!P$K_0tuzd*4|S*Y*aLrQJK@711a!d2 zi#2IC3QSp9gpWA~ad4b8X$nnR<0W?gT>2_ABe%*+Lcb&q3^i=tYg=^D}-mRmVEIxPt6;mObz=XOc4TNUvZ}DW9K;>mvOtoLm1qUUNkp3y0*Jssu6l0eu2<`L*pem zgQGBkF)~=(F)|9E`HBcXg6vpSD3(K~2m(rp`LN@n$KfTx^>wcY)zsV{S5*My{WmNe z6xmD^9fEiuwz?y({-`9BBiPOsV4>5g|n@bCy@q z+->T)sm+2Tm_>4*b!~X7mKa+ez_a55-*>_e{qOajPZU2x^}bKkCcVBAxZnW_zT3>W z3!)EYj5glil80B*8JIT;byOs#Jk~!BNu$ zu6Yk2I17H7W1|@JkB-=3e@UYf28;yGkCmNgpJ_Q&Q)Lv0(9sp|f-lW~!QZd58!;YN z>1V3t%CTWg|94!FNVAD6Uz}8PpbXgwJ_d=RBiaPYVS)HkE=Y#=im0z{ql{ZyU?(Kp z_$lN7cuO+zv|^J}H@t!`Q7;tOB%DXL^jpGp&r=wL0>ndu4qop_ORCSMVJIfIJXGRo z_GtaA*k;YY&Sgk5x}X~{urRh=MuwR>SDmf2WKR?rV>LunFxrx}jE{xZcOmcxo>W0- zoW)qmJ*meRAz12&=+Bvgp@vO^lk&wn+g?edb%1sAR@`b7?AKE9uSRdFtbp;L<*kI* zh^t>5YJnloy|=ayA!JXXS6RTK8~JY_!h8g-mI5$9*yRbYMn}>u`Sx@n8R}bJ?~g&L z2t5jy<%MBplx&)GEWnyr=)|n<3RLr|Ah0v* z%Yh|79ZM7}Q3*vWGnw~QAxtfD8y4fP<`zKI_5`+v*K=+-!dzn=-{>2bfK?Z~XzsG1 zE^L2z7d%$PH+Y_pt&u^AG}Pm#dLWvr3cR`513O_qhni@FJFe!b+;Eb|QBF>z`fxUC z`cE^d@riUI-2P2W1uB6f?eVp>+h~-3g%HbQ(Tb8X4G*x78MCRS4e6n0kfP;-gDZ-M z-Ko4+Jy@W_5w_IY0TcdRZ}TFg5#w{xG;(*!(nLZQ<=b%3eu4ZLdp%xb{ zq1gg4VSa8U{IpHk)jg0G#WmB{_!TPqmz)sLFed9zf|8PHtA6G{xNz_xfZ4)H|wXPGuD=D?!22UERMr2z52^LOD>+iG6`PomTt*0*cNCR zzRJr?rM8)Tn1iuyFxx9J(5n2Q5GG1W_*(v_otT-gY^#`M_)pl@s(Q6z>n{wK1;TXd zAh)$t&5lU-c!ID3e#PfMW0^J_>Dzq{{#LqhLNpljZFCj>RON*7^{52I&e^tT-(H2v z<+Nlbuu;Fm6_2%Q-G3z?-MLlB!Gv z(VG`{aNLZ0v#ESYv>m0sKvtMQ|1DOwjcAYmGsd$yi@w-wpLh5v2Wvnd-qz0I9IZv; zsKymZbe#0-5!&5s(tp3;I!3Q8B7HX$TYRFAeI9}gnvSg>{W5S6p`|Zqf?^^@Wj%ue zK(e`ZwS;Z`fZe*P%2dpiD?s&Nl+w|CAe4q;QxHWwj{C{IzPK{s5IseI{+4()g}<)h9zv>`INx3Fshj;YB?7XlMj&Oyv@Enp8B1VpUB}_Cu={q3KMi2Q z-DXV^24b4SHZ@^W<-Y`J(Qc{F&QgJi~;k?nhu<$IdX* zxFvz%7u&dkxC)I^kwpD(VsA3b2snc7A&jBY68{tdSG&U^kBT&; zLc`f#!3vd6{AB7V#vAon?oG-L6WruI{o8JyO&AO2pK{P|pr0-}>i;CD`ry&DowAJf zMkVOCEh0`13YA&)YxodGfm+A&6`!4q>qG>1>KX;ex)}|N#;Hb7^%zVJCTn#jB!(h6 z6I6pjGjA9m}Wby7m1h(Wz$Dj)DhlR76Gh zKy56|&y^QBJLbvOL4f8*N!0esmiv7nK%xmXMAg!A;^ZfzgI>O*a1`W)%l}JOb)o-% z^QbBUGJVnDQJ|NQk8WgaUfmx>vQrY4h%~17e=Md6{r3Il24#;r%NCXNf%s{|-z0h; z{GlIq&vhaXpgqNq$xwt6fCdr~>M&5{c({S*Us?(l200E^bIXsPUnTl)WS0op;J|+D zy9&Vae2#y%lu{jJ@Dg@Gjs6#6$nx{~A#g|RAun8=EoDpWRufXPKpVA&?hvAufw4lP zdxQv(OhUQ>3#{na_lZ0j3EP!sbJpSD07h2ODuXs(08`9AG$8N_Sn=41Z!C7G5TgQl z>`?}bFHJ}$t^p2NS3l8rc(~ysm{ymb1_^ga^~F4b3L^_ zcFYCh@<-Oivp#G@K^09I|7O)ziqKvoZ2FpAs=MW+DT-1s!sr#&&JH2= zoU}kQgxA#W-!@ugW)U36FK$@IDKT_RGcE}=&J1x}sw)S+3RtWWqzIuoHK(1sjWNL( zL>Ax;wcTN@;Jvip;?&_6e)cSL+#L%mmFAaCFGmhjCos(r_vknxCz0g=OCG`m zW0EUl+Qx3&s#S`x2vKqt;95DQRH3G<>3AQ1Fjg!(3QlUO%8^0NXpZnQ)nf<~FN9XC z9nAJ|+F#@L>_6?=Z`;&g?dbC#w^KiEy?)-LKW|C6e%*Bay8V4!JzrN!fZM14clRMG zYBP^u#+7J#y_SwW+N{oFWZz;#S$n`WqgQ`;t4qC*Dkfo~%AI!Uat+B{nyg90+p^kT+r&fjYfh$fgDe`8^}O4GCt(P^kVP;5dbsg+{BoGSAz{k?knF znXRF*`0&oa&fFr2cklZCV8bHp&X=5XJZBj4YOGUU%DupAt2_`V2K*lmRhCLBw+}3| zSdT*ppXaMAToE}#vh;E2n5uCZAb|vZWyNGn-ww^I>t?TZ)TXw~Eotb%kCIM)<_BBQ zAPsvs=mA%>H8c%V5(MzkByWHy+k}TDu8U5=NppjS?V2>b=e5t|$}|&nE(nkLJp7lA zF}vLoR{~L~e?sj3(3=$8^#FKU=~Jehr|?F({2{djYJNokpH&q)eaLb$eebN>Y?H}D zlo9EHY50gk*T4vNVJ%$ziL=~9n2pnYHyjmk6v*RijlHP(BAU(FdxoljpGpJP-Z8v> zm&WX)t=fW&X#9lZ@^~Wn8k7RR9?ET?sDozbLpcpP@^^4JjSZgorjtU+3up9kMF>lU zW9yPi0vg?j+6>AeY!e_LY$p<3A&mjfJ}`4TPxq-sN%vU~= zpeMPYJ_sc058)fwY@+%>0?PyIH;6jnn2?n)FoVZEZAs$j0+F74H;M*9nv{A;^e#Fl zbc5rli|3@=O$4ee?9Fj)yxF&> zyfn#Y1>pskhw4fuHTuAnJHZ$TZ*mSnViBCxWD`m6JXft|!1YW%)r7g@ja}x?XvQ%U z&30)`Sd&FvcTu>SgQY8L^1l7V+Q;GRfOrs9vVf2fOCzS0-jd1g4+uPeQFw-J$CGkm z+|VbD7PiCijID45Ev`X_ud-zbRV!0t4p@*d&nF~oC0BA!hn=^^&!%@6_yML4kj8*a zAlWA9wqw@^YohhDJ3~WZnrr=$@w>Q7Yo%8uA&pk6WZJd=bYCCH{ zw*|l^r@1#k?*e0~9H5jEM@@8r1ue5yO4yCctj()JPD#XwkOdzhZiGhv9NM_~8-g~? zZqq#iyzTuIa8Fn!cy%j+m@5i)nbjESUg<|5zp!ZvM0$yu?Tp!WHVw{LuFjUIL~~Qg z*GOc2W)N=OKp+b`mWseeH<3{O0TyyL`Krf#rw6RazUja_(P$twE9S{x8&M(At}NcB zctj}!cpbGNoTgx77u|^p1D{Or&G+KBQ#-A*u<-d)g3_ zZ)nCRM=|Q?plU)aM%9w*B)dh>KLpY7X;9_>^>**Lx=Zv3ehn6KfKU{uOaiGp6_(V) zR|#j^2@)+y1*rk*-@&@eX>ZH853z_9B?Z4GLifoKQZGFRV4VRF%aT#iPx5j7&wMFD z0bRqFi%*^vEsRq7%r#{IE@yX=k7}outhSxMK-V4dNC8(9Kil=@ov~}ZV&{8xFn8X5 zRBZQhgoBvyhAS}4{7t&DEeLuwToxO7$hzOjp0@22%&A26{w9JKZFW93snm`fpJUQm z&*AQL(qEY2zkhup`YB*upPR{oo_g-X%=xs>4HHvm(3wF&q z*x=OLt-(Xicc_su^OdcxW0}B`r-v@8brV_-+~rr`K!=^J2W!l`3hdKRof;jVa^eu~ zJp1B%dqn8W?2pCwdKLZ1VCeFUX1pAv_#z_<)k5h;!*^M8G#$o^TcPl3R>zwTG@Fk- z2dwzKLNEBJ_xg(#!z79#^$SMk8)jwns}@t)8~z5zIngn=L;BKjFGtWHAvzn?YRYcn zxaAqP<|37cXql(sX|NtAPCoYjXdN>IxwZRP)KsWJ^z`qw8O^VPX5z_qg(q~|Pg(fl zI)gVCKm`cIfqDhs904712(*?ZXL)QnLFRPt*@*8)KfOkpEDCH#8?8p>`(1DTsdl(I zW>P3=2+kI;f(;TKXoD1eCg5VG`-?zUlsxO{ND*2rE%{1jN5v2 z`eEWyB}o)D*WIP`2K2KqgR+gi0y0*xQW~8yDIeCa(;bq$KFSUmOkUDk`25%Ebrn<6 zKg*}>U{aH>Tfv2%I)36zMtHwsr8M$-`hXq}iPgNJ?D7F01RxG7>t1WmKuH1MNsxI9Ka(Ejtcl%Q#^hz|N8Nc)iX^APr*+yGX1yxW=3L@? z6O;)euf5zo@a-EsX2CH^*2FF9-0Qn9!A3d7RmF&aPwD+?Y8NgNvf6A*qc2^<8C2sz zs^)oSH2DYek9#iSSD@-~C=TNv?>_y@^`+Y|s(-O{?rCe+*}y0$hoZ53BjC)k9;d(q z0^+I=CmAO78G@EbL`t?vtRb3NfQrQ1}na4iY3y;lP$gxymZ2xc?z#wVhMZ)6l#CkJK-F`9{p-1XfRF}#wx-qLhEondS7hh#9&eE z_b7~^{IND&I-9UqV0hMEX2Ki6*U!es#h_P^VyY)RI3&>bBKp6K*MIzLJA=P?8c;uM zV-T_;OF&sGub++9$^`Xt6J$-zlejlZhkXE>*OY4OaA9IqMu+JV1HQLiGQ=u6rLZSp8)I!o=|S9YJdP4eia+r^69vqu z=gE1Ln7SPBCt%d?Bn0BiE9D%l61=WqsXitt{){RDGlJPeW)Pev;q||uBd#s6!od`P z@ed{su0t$3JjZb4!)X2fu6rWe1r^(OXtnai8EuFm3iI>Fk`X#Asy63s30#b>AOT_* zFcoFO#%!Fry|suMeQ{}V!C;<<@Zhx;>KkNwmhK&0|1&wEZkMTCc1CNyemlEDSr?EW_;266 z82>{qh`2r)U8A+in$MWg^Q}VvDJU@-degD)*Pv3qvy&3KM|>0zH13bq&J!A z7o2uy_Fa0JhA-Z$edyU57!6tm)v=wXS^+-t@Q9E{&WRf&4u2eG6DraKO7sKVn#m;U zg|0{(+PLcW`=1;(=zBw%#@=zdDFJXcl5y}$)SFq%066BwJ!A9eA3f1wp zYRO0%q)k~2+EF$R-KDgG9S8_?>>r|tU&VOMdH~TGq-{8SOPBo~G~KdA%B2rMHS-bx z>e~Vpg7t~?1ZQ8w*I~c*=m4q+%50lC-$yJ$y1}6#dzJWa2Y7HVM1kIdb5!0a8g~D4 z*kuxg8NEhNzI>HAzM_yOmvKbu0rC(ivgKS1PpDFNNQ-5m^!A1HH8oBf$epXH6V$XA zBECdllK)un7%sgejbH$Cea2G3{x?2~I+b>FZe&&rsWxCjUwJiq*g~idrs3rc0#AjN zZ9u|7=Aa+WNnx7H)qlMQ@AbK7-=!#x{&Mrxr^kpun!O6gOfz;`>s|Q$MT6ZO&i`Pi zLd#WZ(J_O(uMm&Ro0q72QA@^nS^kIYCNO@s{cjY<`N<}!pY#>!&JH)2k@5I{M-wL6 zJUolbZlT#ut8NuffGMm`AJp~N@0Z{O&Nnu`N)KKs8UbZ4r zewO&_J4RbDELwf%9)RcddiycBs6hp~k+k|H+{bzM%<3qfvG=}=%=cfLg5X|-A~UbO`@FJgPfz!R9-(IHeT8X-K`jjX$Po4oJeGhn{_7(&4iguk zVPGmB9{v!e)GnsrN(Ty}U%aDe(&0%*-}@57i$yH&b!Yf}DUo-W;5QhTck7yaY1@6Z z=5Vn!h)nlmcfjJCiT*A13E~I+mh?=GOa?MXvc$raQZ+vOx)P#qZ_km!?|B?6j!HqJ zU|+lxaN)*XA-s7C96K?ELVh?2fM^>>XnVAp=etstT`$Vkr!DyK7<;Y03oKG=rPIn; zZ1abMr_6EYlAlNpPNfAphgGgPeOShi!Qv)usLizW_h1N~#Ues%FNkY@V&4{)SLt<1 zvqBh5ebky!y&z-YGG(uJHg4t9+~9ZQKPFXh;a)5y9u#bL-b)8R&H=K7eBpqF>ex;C zEkep-Rtp}I?9Kh1{=!3SwV;Dv*PazUwPM%?%^MTw>g^1;RPz?yAk*BKp=57Ewks30 zYRd*iE(TxqzLHK=?K$hFxHc|H#ANY`*!XKS-LGp(fLNFsy5Q zu5PA68@L8KBAF`I_glmn3fmAP5afwYM^yGHaHjM>%59YxDbrDI^A!xL!Alvoe!L<^ z4f6KK*oDxj1~e7VnDGQ1U}e294r#Xwa!%oXh!-%)C@qiZK5;1uIbri4t_Bvnkx5p8#n~2&5H99bS!e&{3qsr zpPNp$=>+2IIf`p^FyvaLAIMpUlGfAAR+)9QTIpa>U3y$6Axevr1uVoCECRN+JLK0t z7~5dL4n2v1mJq}6RxLzWw@cAnpUNf-17le zAzR8Unx-v&Z(HC%vwROF!s-l^xuBj{EO%&wDRlgaq-sG7HwwYI(-RZcch_V^Wvp7S zoK<#p7z7&|o)iC6kGo!QDSb;1l%7uSVpCQX5zts0>PgH4{D|#Sq3Ag*0yc6+HxGud zh}GcL5gI}gB`3~c!g##JYk~^GoAioWAp`D;ssjHJ?TPb6TtCHzZic6a@(czmRFV#< z``7L&mAWflg4gPe0jP!o@)3Ymp4cO{_l0gW4KY(!E6c_MQ0gr>7wY`!_E4xrQ^&-n zNc}4d=u6GtE%uXAGFi_F!BkECOa``xMjdZW$zHbCC7gH2vFT)qgt(X%Cf_Z?-Uda( zQE}N#%35{YzCY6-uxs;pvyV?K+0mdCN|;rEdhFN>c&V2@uSW5|_hTSp8DR=aU!S4| zQ-CPUvVVtGLdPr5+IQ0BtIMGNGpk;!KN8R1cSE{&!}-V4u|kt~5OMOyi|XnUDb4N+ ze}gbSq<4aHH-l)3V`9z$R!&n~G1>ayQ}9Dt0Q*hLWSW1u7Cug1dy-F3#S`y9;&Rk3 zcLr|5++%acr#bUK(&`?y&GGRR>51g z{jib{-1^Wk=a~<0tKSIW5wo|c9g_=UDMA_vn}}`*yR~o^x~D_H0n4d{skO*F+4;<_ zwIRtQQ359zWmRn6Pds>}Nfnvp7hCl;gGqly0MI5f6yIlj`O-ITfY~XbIekW z$jkW|Zj7>r^rZX!|0wl?!`zuY#W?OcteD|TW`DH_yr{*!<-IDsv*CP%?)O9t{_^Cl zdM2iXzRUSUD(Q@+B=&2#X!LGgwfVaS^cPbVzS)C4u+k>)oFY=tgUR7ppr$3qRhspz zFd0~lBQN`8klBZSFP61*wzYVYfA4lbs~hub_2Ol*h41RVKV3lgs#Y^d<|mLxl)p_$ zA#{<778I+VOaaS8bVWy__W}D_(tI6tGYv<&7M6;`4Mu7 ztzcwUQ=9U3;)`g(6!YE9%|j7+usN;CdlJ$9{`?G+3_Rzxi_f^AZFIp4JmwIwAC|)c zgn=MW{{x{~oMb<%Zj~%PZ;JIb6(*!^z~_eIT2X#qGJb+gx_+7?jTnL+LJSA)VxH3t zgPWly?>`?&8mxHmQ!z2VZ2a_LqCq->sn4)+*9E!ul;T{;)4y&v)=`s0 zA*t;u!dd&FWWiv!44j>H^9?0^oyO1vBHj)2Eu z>=z1kDD7Gdaw`6f_EkZT9dpSxM?9+>uU)@3BD4>6^-~;W(<>Zu5eznr=Hh9F+=g62 z)fgt}=j$NNXJj(z0vCFkpIt~OoZ>ge!h@K_)$-F#`!T{b^E3|8Ja1HY&kEJftD6Gr zbGETrr(h-_j7BG@J@y0+7MjU_wR^;#glqNNGie z8sGVE%T4JxvdH`Aq7Z?K^>GnL=NZyI; zK)}J-JZ%UN(V$lqh@djgocvh2K5*)`o zISg!GBb$%<0h%hBBu2HgLYY2UR#M)TqnL_S?5JpFQi}CU! z<$kh%XpV(y6&3aO7}>XH{GGHNHP)oN_uG?{NYyQ(sZlf%3?sgc403Y%dltK((d2id z`O#BiORwY71oHu4uj;5SenZm$Uu4{!(p--+Kz=IYv5xLV2doil0}y6%x?ww;Z-|7K z8LNaDOmXL!^FP$G4wUVP8HGybf+s&-6iW|;m$U1t5r3p=hjBxrwdR!|E^C?1?6tO( zsb^DE)gpzlnSqa8*RH4_QXthsD?*P@0~4!{Q;Z}Cl_5Hr86yxk#Ayv!ZXufGt>|F9 z*zv+nEKIU=ivZiC9R9}Q9fF4WYmUrP!S|J$yibCRBBw&q;IU4!gGiSk^LeP&)q+2av>7b{1J&m{{r!XN z{VYZA>?bJ3L8vdWMFXSv4LkN0iI-MLj3n~3-4dqev5n)}=vpaJ6(B-#x<+r?7=4iy z>?LO2Ghoz_7IcoIPdZ{yKuP?>6RsXo)5gcP&Tuv!)H#&Vd(!?p2>hBdi>F&1r~WNZ ziG}92y#}IM|87+*jIBdC!V`357`wY@pc9ouZhj6)=$98UH^z)b#oK>&(8;&8I#QS8 zfza=^F$bg*7Ik=42HX=x?(Ana(ppo_ak~K^>Gj;pP*9Z!AYgv@;pzd#7@s~BT>%!B z)6oa2G>^R*B%XEiHC1g85aJziCG2%<@)&6XixdL5zG+KoMzs!QYL+)fb2 zM_CpzBK#AsRmI5BY{-JZ-2=b*_y_DrzD6Hj_}TjG3`aJosu+1GG;z$-kYIN#;~*|- zzlWynFDSv+0_W@{7l;~+_%#AcUo^6#2VDww3VE=@UYXYW zw;h5aq`;U2kBEPOMw2$v+&RvU54ouI@Sk#3XL2cwrsfz;q7s?2+f`!Z*|Ma>Z-X7I zBj~X9mcqI<*5Ex#aV2Bvjbos~!MeYzO!CHqXgXtGQ*^Bqg8Ezrh|!5+v;Kof0-_*^*D6Bbwc#=!kI5-R7?;1%-PRQYPWaPC6Tg+hoKUN3C5A*D0+ zZw?9Cb$3I7UOk}j$U3=OM?_~rv%BModpXRNy^-B>85uF;$R>NhMPBUn6)?t8_7?Dy zJ0qK&O5>?4jsI~9m+I~lm6I@jD#;1}`h|X1w;Vg_kYXHyX;aZz z6ioC_gG0%Ik2AuL7yp3FGs?;5-w5v*Kt8PpgM_ zmnnAye0%sYE46)lCf5$F+ZXUW#9z6nP!K<`HXZScB{CfQR))lEla^mGc)GbmN6Qt$ zjh6zAQQGW71X+g2v#-`g$p>?u5vu{-{SK4$9EadNh;{u;B5K#JvN|x4^DZ$fL<;Nc zNAi94&Fs;kRV7Z>r%n8IyP_rIC3Avh!TPfWBSbaf~z;+eHc#C zd>zENP1m0HBb9}sS`FKioQbtuy#tIVUGOeEwz*>)JGO1xw(%PqJGO1xwr$(ov32*o z|NGtCe4VOPRr;Jt`lLIZgLeu%;R7ZH=lmU3r3K0PE~)4JM|6nEG&oIWAsl0W6WDJhZ)YFBEy zp$zgcfatT90ONtp(k;+Npu4=b00l~kbQpFF?<#WgHquZ65dPe1_vc_u{6VgygTC>A zU^<>+l# zbRQrHJw$rBTM`t~o zbhuFi5KSCUu?nR*rws1w8i=QpQ;X1D@(7DW2_8@{GZU)(nK8 zj?x&f15fgyXzL$XYnLheNWQlhbD*M?F=^Mgbv)$BYFP&b_cE~M?$oz8(ls=P0wm7g zze9KLaZl#823j!2eLDfne+ckH$}W}wxmUIR42dX3N8s&|iVFcIdXYeT6mEfSn9y!vMdT|2Ceec!-Nx+5p-1bGTctA6R-UD@CECUH^0XqKa$`vYZNm~0IOdt zfH-}5uBZ|0!|vYNcbX$jP_XV!3iuDz5ynsjP1NTOY*{P~bzfiw;~MFVkvANnW@YG>DdIXU#VnFSqwytoh#`w7S2oTF< z^U7gR0W8R2vCC~%{4BRNK{FYGUIQ!|0xvIw@}>w5C)45lafG(X5=@=E5l^j~YVsrEE3*FK zHJwPlUaJ^o^ym>BZ1^mx_5xiwHo}!`NsEA1>jgGIsY2~$O$Ay<(FItsa3i|(2-s^0 z^gQGYojWoUc4grZ@0=CU6F|l^oe*u(LGc)#&pLG}?{%*GLU}A})F$Yh7BT9AF1Csl zp=PJS4;QNeCs}_e#o8u>3^uQ$1wO!p?HNK#V(U6RYRJktRW+R()m_{?#Ro!3shMK4 zuveRj1Lw>+@j(4eLG5K02OE;Ub>$KqG{v2P)t#sL+j8lA>0Z1H*1?4{o-v%#d__@m zhlnkU>OQ-?yn6Z6Nb2$IBoA?~%vNS-%ndg)lU|1$w)9rX$ZU#<4Aa~_wIp0h$7a}T z{$b3e>j9xwor5w0YseN*l>5}$bB&q!%WkwARS-fy{Iw~Qj-^EPF-Ww7`8 z@HiGetmc>_FXj|X$B7U|x(>K%H_Xzsj05&jCy;h2jSA+mZg9|k$GNv2xqbny1Fa{{ z125~x7mPOD-f9&+0>+W@$0gP}zl7kc-vovwAQu@alkaxDGXs4N7dz*^r33zjnkZ>~7hRXSQIf@WsJUpTS9o=6RXXaBW{?+su-C z{ff6Zw8^#=PE(qV=j8KDozE7I6u<V%gO}iZ-Nk|*+c_c?MRx)K8p(kf7!e!bAZs~r{ z<}_xd$8zPAN!cm`t~N1ZfE5B27K>gd)!vUiM?QHlq|?On3sg7)c+i}>iElIC;IzE0 zEd2iAXg^;dbQGdxXgs1IibW2t7+n@WmKFoxJ)@??KXaEKYT}(@iytAE;zM196ALyI zwv-(4Lgi(>Recj#s7R9TRcC(%f5R<8Z3l{8+zIt+{Z8^lr2bth6dDoGPN)S}L>4Lr zgbYpDfh8|fpSm9+sx4*E(vYn9jw>wYjb*d_XiJJuUb)~ksQQ-Y4QFE=A2(UFVJWm{ z+|24S*?s^Kh3`V03&x@bVVN<60CtiqNstE$lotA=fHG|j7C7~Fy8EW8O-kY;P9=jGj(SAQEQ>gkc=2klsTDr*$1eDd4qD6Vp1T|(h7+`HSXKSgUX&`^CK4M|Uc zcbhB0LEPlf`NvPn2v+LW3X>Udl;F|CDvKDVznJx)2QpVnG33%7sY;mYyXbf>*t&Yg zXd_0w5uiKGvx3Tc{+^?!8X5mo{}$dZ%>LuI<6WsiomqA#l7(<*EQL+0i4oY*K^jY7 ze5C0zF!##e`~>rBK)zd)LU2jVBcNmj4A&yi5$uee=%_Sv!l=Mi-P-5~myYz3{e#== z?yV#CJG|0%GR@M7Y>9xbYU1mtOG;s*LxD8iE+F+|(~wB!0hY^chxZNlh4m>sRwEfw z-d}{GpUm4TGL}Nho6w~0M5e$|%To1{QbafODT9v!M%Po+yH`{b;F&q@p=b(}%d}?; z1O4zpOrzL^y?g|qtRFN?5q2Cn`(E22Jeu;8VMZlMjg1|SN79*8)6-g@!H_V;cs z>gZ*|W}5m5Z!T5O;pIA3y~P%@bc4^oOwcD2%p&MQq-2K(pT7fnKn@kx{6H3pR72-u zJmgP|s+Bx^+}@c`c`S_Gd*^gIlQqqo6wT$wl$~8 zO6AFU^~)gM!nZI8<5qKY8(hVo08s|{6S%g8l%2#PR&+C@4B!_b@q_k{#j$jAFYhq< z8{up@XUSDX7Gu;!8=yR1qQ?58@iB}gJii`fK5|u(CB3DoUc$L7R~5hv@j$9bD*d*w zTuS}{&ft6TS|ITmA92EeR2K^i$afiY2%RkIy|m5E z@dDB4N&fzu*SZSw9pZA4(|%6$?w}|*X^_q^7Ipj=9!>yNvF;fm5nMcmnD(FqZ8e_^ z+Jlbm7yfsC4_v|@>FGPN*tv|NM&cicGHQrF@juR9vo2`@rTX`yN|yLu>|qNB1j zr?|9v$b4un^Q64M*32oMigPCM1%iI7CC^=4c}E5(@eOFlpK{;(4?Crp%=oI>FKWwY z%fagAE*p<ymb55-op6fNATAa=xT;nGm@u%m)Ys zHL|Bt^O2sh(##}t2U%-&ajL2gc$m|sKTN!!rnJ&E@AWH=8~ZI=K@7#1hD;N8Pi#t? zukTcR71CcbkVNQpM1-TSA&4H&V|m<8#z+pz-cS*BJqD^3eqfeNyA@H2g3JTQ5cMM+k1K0yThLuEpcx>HOUYBP?I6=i4fQHF82jxwrwv^xk8Uk0%MY zBkKqVXbo-BSc7Gx<0U|TRsG=!o&a{^sMnw%ZvqpIv<_wpuolZWK^jv$Y6W>?l{{wO zd}uK9A3)=ogt$id6tuyg&iqEnQOa3${Y=CE@e9exgrXN6m8Pb*x?Xvb_}lh;k0d;U z`BWBg9D&qLGzIp_A<1xqxPp=HK@v3<_1X=6P4|9L3p!JOG-4*OElJbHOc~6;RefY` z?aj}bBcKcy-zIVF&X8#Z zdV`zx`vG90ZOEpdbv1T5c8w0Hp6va%X5XCGt;FEe#Vg}L!zKMi)`wuRUW|c-Isqew z8C0(pT!uNhg4Y5}SIEq1&|h$MKIOn)P|NZAEl>n5gq!bUd@I|0{RKM9X1aN_*la*n z^cObjAyZ0r9bcm#bYsc4&KiFZb8Ag@tl^0SZPXJJQdYFWTVa! zVz+8H3RSS(&+iZK_i?epFwV+V1UI2err*~F-Z(fZyk2CuFr;bj2xPa)O*2hK-a&_E zc4G*MaxbA5u^)Iz736oDllwm^!Ge$3Ty821h7v2EB zxf4WiKFd!|h(}$)>1Xn<_X)!Ft6%24ipk@4sP&7KD5%E+Orq-zX%VI?<$DTC zgO9OLHmO(XJZhy*CEd?qswJ-xm^SIjP7JC_BPbM{6a)wWrN@YurlttuUMRc^^+4s3 z@C!?UY3y|8gFIWY_n={>r(n}%SdizLL{_w{7`zdb&m!IhseCE*`Ke`UHVFswk`o3# z5$z^h56bZaqpFN5mAww{Ye1Us+a{~*DU@K9_D-3S?n^Bu+9yoE#1o3X2vuOQ7^M!IYmDSquVUB6dj_dH9gc%I1PR?{c_Fl zW14|9nlb!{J^pdyyyVe=>>ol#!zC_t>aX1^`|MME*FP+~8!*Lv`>qmrx)Zr88N2~t zFje!+;P%TP9Yr1=%ddf<@@4jNm$#_6&bf5R>Qjq!&%dfM26!qt>pk!+U%RwS3<d6$K4wa&dAbhx z(-AF$&hn*69@89S$*y9vP}5DgSvj0e7n=0QwMnK1aIg3h{VMqqgHPmkwwKDs%~=eH zt*>ibm-{nSKyCnCVd&@0sD^h$^pf07^+QSos$5J$AMeYRL=8DpA1yd7QggCxnqp68VHUUs|2{K0XY>uy=jbqI%lO_^NoLoVQ~Gct2!NKB=%QZqs~ zEb)1kGU><*7#dP2VMn!wxN+y;k(~L#nzt1#%+?hm4cJ|m;zPVV6H}Pm2*U@f{fj3n zUEz6Z=9Oz6wW%9>qVme{NmdyCb0Xja)35) zj6vzFsG{+OLXl^?&j+^qtT4cRznaz-ZQuLLt@^v#ug6d6W%j%k z{Nd^H$tL;XJ@`wP#ed4vV@9bl&IL-!LYIAu(ee0TvGc;xnI(HmCovGgAn_hX1E}8D z_LEpF?ShPoz@zDTn^YsHcWoKaL`s}XkR8-O^e{kaN=$2%Eopqt+-&}7 zah1HoHFLs$>xY_Cas7nV zqH{Z(3_a6d?hm9O3Rv}H<(36lR398dBmFq6Z2uWSkF2_&Or+C5HO}NK*PLZ#yc}j| zj%JLhPr4pV`Wr_?dZs|qe#bfK1WH?u=DsKVP`9I=FuxPYOp`g7_xlIDXbpI9i|rd# zo|1j=2U~i#63m6$a^8NBj>{}tJ9_sMTnCaExxgPH{e8q1t7b}A2YHgQt|{rQ$yJz- zN5LeK-{aUT2l20cQN?mM*j4K8{hngr^TbExMxn>9VGcM99QRGT3{Nl21<|>Y;pIb^ zV1DqH@mS4CV)afYH4F@W^m&CL zb^GQrI4@DnK_XHhhf~0!)K+d0=%-y`snX!7!A?rHrlk9{7$Gch;R55Bt zz~mflTMRKCts$4|slvcdO)pmVj!dfEBLe)$)J_s(7Gb3`g@tkrc4y$$>GCU|vtzb9 z;+T2CA)PCiVc+vw;tX(N3)! z{T6;plM9rrg2&DXp8PnA<1;SmR5A{9`%V-#^eO0|1U80>e5 z50kNU%<^kG94xK%#)d-=pB3{@O)-(+I3@IS+c23nekvH|Clkf=Q&5jDz_(H6o_m?Uy0_bJd=V$qLkv?gOwwVsVX!Qxk^c83ikp*|@|w!j+2$fx zkOrk{pKb>F{N{{Toqsb0DB`Hge-r=jo1KW|g$ z*%q!&eM(5kld>X~sgT?SsWPAZO@-gl)u!tNMS9QZQc^iRDt29_%sFTSz$n0PGFYBN zad*%%C<}?OyPt+DBHU7>$tj@3#6PoY9N^gwlr^l-nD^|G-LO)42rTE#;D_};L^`3g zZSK?2?=G=5JOpQ8>DsqO z`*l1z)u+Y=s&bz%+R`BHPE?>|zV@IA)bpVEVN9{by}JaTJcjnbk4~;8X=vYsI?HG! z#Ek~lRwG$>jC$N9rz)85Y$sF=VzG(f=*-gN*aG78jsL+11B4^NymS^oL+t5245Ai!I@L8&Aa@3}a#DoPi@ zmEO$PZQUMIZ5RdUP%#(BUxxgp?TeyBgr9D77{#QRml+o$d22t-f6x+R(!x(NpR7@= zV>>;;M8(HXJ;{N#O=cIVU+s;wvG5?Q!KRoIHwr^r=a!K1;$y#kbe zZ`3{$$FqcIj+{CL=RJaWSGy!>m5^EA1fG$EgKCBloBjO@_`}g3WrIct*OuKa=nj)~ zl|OsG(0rrbiO=BeV>lJy{o}#zUZ(O0Gg`{PiUKC(-iynZA~{-d)oZQ3l$C99NiJ|R zEqWEwGC7vVo~+&XBBeW;Sb?&xB#;`w!9fmXi}BIHmH7Vpy^0C(1&dh2 zBQhsd03h5xF1j#JEq<_TQo{(rY9y8**v|znbW!YpoQlRg zvAmmRr(whOi=K#tHm7`|&=YnCruQ7KTDqKdM7GNg zwRY_4YUWFK&_{IlVY>`#VYuNB_^0Kxo)dAmxJKJ>x5sn95KH zRJ0tykhQ48I&hs@digzHoP7gBRqkB3W(F3cw<4Hn(sKU{1?=48F|Y z|4663jTQoW{~WXWxQZ$VaHVjyds5LR6wJvka*0g7fvmUo%gFH`N8_M#`6K+&Bk85A zTY_7obM;O#aJ609qhMOS!k-K`By&oiiW%v|ka{}YaG3e?3%vg#hY&1u1rJ-iUy4vq zHK9c%MhJ>t4oeLkhJdDsd1bs=`1yo*(BFJHyo?v#T&3T#OCYr#TLfK>)WR45M)kp8 z$L;%@)KIR#MK}5d7I$ol)p{^QSot<~VkYmM1NnM6S z2)|%KpvhNJL0gT&JP#2MfYyTWLgd+=D1zxZp)>n)s5ZVk&sk`z-78qQUIfUrY8>Xp z;2G%YG4lO!xA8GTN@Ukws3fW?lMr}8CkbOE8qReJ6jd2P!@enp>FDsMWJ# ziEhrA+QDNCZ=FszS)e}oP zsKoBasD5=}K=h17o0EG8)k32@wLh*|t_ zIgTpW5>7oRk!Tfuef>ya*$c&8jBfi=Cwjiq2Zmcp(*JTtM)twp@Ak-;rLuhsrW~^P!r`^=aYA0(Y{BS0qsGO8oY`>w% zC*xOo;9Z;+gKpzuPD4`Tik;2E)simmMo;Gn*xC&CwI-vR-^M!h3s@VLY^R?>cBwh{ znSHb9m@Aoxx~VzHter-?sbtqyH!C#fBS(x9uG9B!N2*cq6!DRq(<;mVp*E2Dsma+4 z(`;ByIMr=y%7HQ#i{Xf=!D?mo)6!{40Dfsg3W?T1nfkET23L5Hh-1!y6o!$kYa$_6 z-~6WD_d%_@i_bfp?=jlaIYBF+EF?FWJ)XCDlssmJkrP&WJ74h|kv?c-sZYI?zTTtNAD#xI_?oe{u0e)DaFt@1XY8 zc)QsKZc1&El@i@&s4SyS2$?oK3j-5pVD|}Wzv?UTn24D1p^&p>5xAG-%RaZ3KGruE zEXWw;SMF^%)ZZK<+^7vL@BTQ$V7E^opbuQYGZ^ekCpms971EX6uvrLRZu`|Yf;N0r=PntgoX7j!i%@(kMKGNT5rYR<=!-w%Oc(pL zb|E2Xs?^S;I%H<9d<4#N@u&e@)iRWKUzZKPDC)`q^Q`*-F1=x;Oz!=iMk4r+udvtu zzOb{4k}eloVyBgRAuYh2)nS^@Hn5MF9(eWh>ACjD*T(MB%aRoY?wTWg$2WTU16WKg zwCl4D#vZ$b@(Yi=05lrZHp znuomjotL|L%F(X@xPKdORf4gcInxzj_s>foMknDHhY&V`VOBDO8+GPwLWtvuDL0Ge zuPIe)CK`5NZ9SEix}p~M;hxpB;?jzM3L^rJTxGQ4+yn6OxrAcQoc`s~9z^-|-pX=e z{x#Fo_WYK&oHCJ{kEaGg^o8P?;Two^7YjUb$N>;j`i{^cZtpgN7>eM5-3G2zha~4r zkcjSAUrT}4Y26a&Mph)Vo>(<2skX=wD10+LOn%k%Y>dZLcj8pW4!MQ24Ix>o*L1R? zx%?;rCQg`Xk7)DFme}H;dL@gdJWFwkXB(6%uP-xOU)nlR2kU#G@{=+>g_6o>RXsv$Me~`gTG8=*=$+>gh`Zq;l&8~dC>z#sOzPk6*$urZcj>>1&De`KwKPN# z$74+#vgv)krQW*q({c!_p-XSRY=5MhmkN!0;z-7?rG>H33lXA~>;u^YPBB^e-bn_{3B2d(#H&ioH?BV&az0O8? zG-XpmbCtj&57$u|1u<&_CC;c*1-Fc4Jacy-P~OjdF|`R~KIRYk51)Uk@@~}IL8xjM zBpvjDBIw|XmnpV21Rn;((X!LP-ZQaP81qf@hkDB9Hm@@!skGX_)O>aL=InbwL4Mb# z>WcMp(-)MGn-t?QgOcil;2m&%*)Gt>nIf)-?Nax;84atkCYvaaJ|=%c4A~@gwCFJY z&{G${7`qrQefZHnE}({s7_0o_i(C3PDQszS4-cH>RXVe(X|7Z|O_^e~2gB{&-@Blu z-Ol}x$habgkxHgY1oJVRrt*6LH18>Q7o(rtH`^ywP91>GplQDBUXA{HfQvs6JuH z77V2mBJ0NXgqJr%lfA&d;gbPxhwso>r=vJ(TUc@DbT-OdJu^xzhW+v*2^yD&RLLsX zGNE)@e3S^){;7`sEBVNgW7g(SE%+s*<5)@hDt*{19>{?LTlLJi5S4>DvAefxyOSar zmzqiV2d^#7de7*d5pkIwLQl4c(KZlpsm7AJ9pNutxdj1|Mthh|?>|j|2$1{$n)Y8w+nT5vW>8*Bg!J49K|$ARC| zd#^I~IGDiooIkMnChFU(OU{H^BjZ{v*=%QsB_|f!&nc)R=2M@8@KmzlLlF9TgjfM+ z;Lf?Xy~PwCV*0RZWxrB>#O!F5bnymp=MHYB_b-Mske3v%B>$kI)mLfAD@+~i!T(7O z>~u@7g}Q|fW)wwSBOe;?%aSZ~7M=6(Q2M*Kn!#TV-&X{mi5rCwG3L*TpHT8P+zn)? z5lC&0jPS(rL$eBnn=?tpX7-GT$-lh^QIocJ}9-NE5Lw$*T(-@lN)@%wM%s!kjlv1(xRi^u%! zkVCKBPiUkU97Wy74_1CB8H1}>+!2EI;q%&VOW#+E{&j=nhc*AEhJ0?`!|544K}+XX zv+I+Z`m!d_X7P@uy&+^IvEN6!ZH?JXe;rYZ`!AZd-U3_-jj|Idw0k8#q=Z_StQ8yQX ztB`!PY_lWg7G}xsL#^hA%={rlACeh)v5-au`@f$NoX}Lw-Yf%tbdG^NA_FnoNNqZR zYi)wCs8ew}4m5|=0wO=vyV9OCqDmE(QxCj2?4S!F6MZH%Ga9J{h#OHugFviTN zjZDdjua44*kf$pGq!bL93?nEv7()1(H71rWe3-1_aHy;D$2!L?e2Y)zeibAM{{;Pb zB#p4pgyN3BRmYq7s;ZO?Q+WxgVwvldai zf$N6@;31)ZEe|B1CO#@pf;4{Xk}6-O5T;STP<`~}Y=T@ghB&QJ7RSEmGGSr;A&Ji5 znjkAe?}zS!n}sZ&E?G1Uh+a&0G0>M#6fmcal8Q;Xi5rP1T@?eKk+LIFzRTs?^y{84 zwG6S9B+{Kf5(nupefgZ7vs@Jruh%y~I1e4`@P0jLt?C+_KCVH&&XewcK#Je2g##Cc zZ!Xt?aZRyoh$HbgUp%x;`UV)ykBWlv6A(7&DOQG?LFTyFU3=2G$IRfCN<5+>#_WFC zHG5<16Rgaah4793AbZ)5)$qAo(zVgySyp-Q#Mm(tztt>08aRs=N$9ec1hJ)NAZz9g++NN_*h)}KL4wyRSud)*FHtuODI&? z2`#|h&;vsE2XS7?9Y?G2gBXp|A>=DZse|0#XYJkmGw^5RO|l0l(baYPDLt>w{d+<*+x5%~i8_bX8^IWEXIx~V) zLJzNYRF9Zz#k0OBPZci_-|X9ve|3$OHawYmQ~B6A^=h^P(S&Vv{bL;4;aG<*C#+8` z2J==1?~&})>6JJ}Vl10#OKI>A??&%s>XP>Xr4PAO%267ef!Bwb{8E`jVgSol zf>~g0o zmbo{E{n5osTQz%F36EHrHG=ZL4QT3 zZUA>-en4*G!XNA}nX_>9YV_)n`Qbh67PQ)fv7z0fJL961C65o@{qYCLALr}DqIPJ< z^qVV&zuMlNE~2MA-DsLEaJvywnsI5`NDvgAcgl*?10Hi>f7;ki0;Ohvbx9pk==;xP zJ&uauN{HQsXyZ+_Q7U(2W%$3nkBE^LHJ7dsZD$$fP~s*ZUsh?3BYntu#4W%ENVq6x=vY|cu&HT{0ERHwMo<0Ry_fM{mqbU03=brX*?yY+aR@j{r~6)PVXSUc9PlOP&UPqj8DCrXmmQE?$eHD-6eC~p+bqRx{tG4H9b z98Pej+w2c|J+2QU+p_r&2j>5Z{bXwgIBjqdhrlh!RMU-Tyn35=UXPn;&4dL}G93Iw`@mV0CJo zOe})z*^^vZA>pwCsxya}?9Rsu{Y{_Gav4f9q^tEFCFaiaFML7XU*VkaZ|Qd*!N<_ui|F8)b{mH+@2C3of2c{`k^$^F^sWwua!J(9ctSyq3(V< zbvFAST%~r9V9#KyWmwpZRxzOIx4;?V%FDN_6Nse@2b{cGaDpQPE-bFLZ zmtoai|7C?bh|sHW;^ZMZ(ebD233fj{_=a969TwE5ISRTeB$zr+e00f z-Me}XNK`udvZ18V9piUB<7Gt`hY+jr1xwwx#ZP$X7ye6KRq@;5XiRtIi7;aaF=K%s zpbVFas4A?QSF*s!_Zn`(aHBxL5*Y^kZBwzm=*W$$5{mbcP+(E=%*S}jrn}{_ zBaQZ}thyf`MHK|Rg8DpCWW>Oq0_`rquJ=B^>d6%?y^)EDGHO(4SpQr9NLZFV_wF#u zQ~);!1p5itO2IV~c2(U3gu~McJDs~H^Hi)2n+p$6Y(ZU#o|+*K_8#WZ^lbWIXTvk< zY5ps@n@%EfKJ?e&9OscCM%!Cq6;$$!kNS7q z@1R(C5OBzTY^;axuX5}vu3?_2$fJlL&M;1C|$VH!yC)^fK4NfhvDomfGX3i>E# z5fTRT*%$m*DQdo663f+4nkiA-$!{lOzYfp?Q1ohjf9UAPq zF6V3hI_~diZkz0HPkNZTAc$8EI&DeQ{J0G>qig`?Ua3GmzzQShX18e$$P)s&;C>6- zgpz1knpjf#bIpIM3Y*)*ZKEHxY-(@r(pb2!Juv1$mnwqY`5OEw8km4l0qi^Kl0F?d9bZr0@mEm4qD#aZZ%pioVb74;hk^@kjSi)Woc2U;aljY0w7b=K*%R9% z?aOVd<9OGW^PwZi1k0%iD+$7iPpSLT89xg(A4HiXJylnzas}FCMW5Eh9DIrLRqR=z z;?}W?4BXXvf$uqf2)^1!z24|XN18%-k1>>XD;HsPA|J3sI9+E4rv7_dte3aC7n2Bt z^*Y2i2;f7ODaWVX79)YV!qJr=x6dXd*CGa;PFjxiZ9J|ATfwn-c}Q8? zw#W)ffSh0E5W@{y9y&I<(aLT&HZ-F&+5;_Elr*6b{e)5ul%VJJ-#x_QK_Vtt(qqUu z%=|6MqQ^*XqvxCU<7HFEny;W*`whET^J3Q5bd(mtG6Apm#AO7^X5V%Najeo1bT=+I z9C@rnKv#bh_pL4$h5)4xDnZfH&pYKt*M8mk=CCyfgXS>`gV#ld&kJ=vO$v;sdL%Ii zbu|~j{p3^joKCWtfEC_$ZfHs1zsVPQLY3eb$K%VpAphtjTiGmJqsA38c`pS5z2w)Z z(m|65(JXW-a1LLW>zZ!K2w{5I(=x)O>5`P+B8}AG;bJxy1cd-=yA^vPMuKV~2ZqT-HAImooSq zpHTqlXZ-_nb>OwyVtbX5{Gi9f%g8*hD|xzafvh{lPu>Kd3~!ECf_05m##Z4l)J|a` z_$+E*mR6sfTON@!*FB!4B_k6V4%bVNG+zMWXv_+M&P;QLS zBD^rZz(Oa3a)_5k=@~NEGL~I6(NM1n2bO#-vOjc+ndl28V;mx#mF3ugMRpy(1Hhphp^H?wb~~hXKKyNNS(TTUxexGk<{|h!m$Jnv-HFdtPLImC4p1 zNE8)$T9<0jer@VTWyL3VPzna)0#{PIEeZSHm$uZW$AWf~2WOK88Ygf{{-w&ohfc2_ zNkuKw97J<(^E=oHGSf{T65M$U{}UE`P_EUapMWE%^31?ekh|=fYnrxmVT<=d6WpV{ zBsT9RWL#Z0pFTb(+$x`Yma0A6NKU&Vdl-3i>iwc^%pPCX>Ac+SgTcVZ!umxwt9+=)bZW#f!fQvgJ3mu+8B%J{#>WXlnpX$aN5pV@w~_(+ZfVB( z!m_D;(lG;JLibE)gC;zfDbziG{{AJD%x3IDB!KokGX56C8IZXDS^7V6X98jd0p?tg zYkju2 z!)SeHjJIGQ>BTLyq%AdSN6BKlL)(z(?V7L9FE-aSl#J8muken`zj!7jBR^!WekTMP zu_k$S9Np(oa;n{M04qY@@~b{7gNES=1!YJunU-}oGKYJEYpah6%~e%xrUxcvOIJW? z(E^Q_*#52)${_`3Wz4)I*(Aoa29_5uspk+zv+40zE*-Ra4scXdbu5hvnB(UVqHuv!@N+0W*=KfGS#7d`v^P~2v9 zgA0aj#_h~h-vHiu;;RvVOVn5?4~^4I_(uSea(4n`MPK3{6Kx+!&BDsx$-+s}e*;G* zA(!=UTT?Yjd6m}D-UqLj&U`w$5eJq0b)1h*6TfeUJ0=$8hi#)tQOkTC+j#t6?nYCq zSIQ`p&^RJ0L(**9^V!I-x;gdTHR}q0P*fj2EtuUqmqeKP@RuJoZvRdefepQS!#KOh zE&`MKSyFp!2v`}lL7e5tDmEMFRde*D6noA{1k&(_jDG$>{>Sptn;CF(u(Z5o`@2Ou zvn~7H{$chq6+q>mQjmt+rh+&o1yn;i?YTId#>q`a(=!^HrMVHI;UBGO-pwAGQW2&D z*vy2E6RO^TF$?fKlCYxcpnn#~?DpNtRW)11abor!svtVakf&m$;RO3^dSzCK#(W`D zIW-A}s6nALb64whbRHq&y_i*ACK@pz=SN%NHMhMT`CkBXK#jk;NmvncY&x!oT2G#Q zkkaRc0!X;NQ}*~neW~aNI6iSIiX@D~Z*6XruB~g~ruq@pj_~D?_M))#V^f<)N&e%z zmtDO^;_0$8R@wn6Xx1VG%S2)tS%y>-+y&*b-1C&%@x4-rL1mG~ya+lVX-7vwg#vpd zv1oo)z8*ID5^@88?KA_$t}OxU+T3o|SUF~4Y5J)VAWz;K3jx{&^ojrg02Dp~n2lct z-5ToB-rDb-sV;Pts%?-e&yBkjqJoWY-*1?Gy=d33c{MExO=Rvi^IqW56vTWA@>TP? z@txtC-|D;yNJ0Q52(}M>Xnqom!Hyv#*$-E#DlGQ~DBoMKRXwH#&td0XvLgfcL+cxoP0n))~WhrA}&Uk5jpYdz5;tpN*J9ibY@R? zJmD@JOlQSe+#F~8YB>D7e+zG$E2IQ$;y+8QA(tl7v=dKknN=|RV^3D~D&2}A$1xkE2 zOP|#=mLPt0nr(~rS#fCB`Wq0f{c{}pv;tUohqz0x>S&_zH{8A%<8za*DY@8)$493W zrorCMXUE~MQ2TY-ABMaDnNKGP!%cb^=*azuC?W?l?7@$&O*8s~Hgvc4oWve!BAB!j zD{j}eo;a+adSjs8!6LOjzci5ylIkyA2lU;}NMI+>^C`<-0coK}%kbjLK&P;XlO#}5 zto>|euAW;IA}smTfoeaqLit0n1QZ-$;O~YSe;)EDhx$ijlN)4+i^Dqlje4450;ybP zwl}m;O`NCYX+k1r(e>0L>i-{WL%JYmar4&Y95=u7T4c73t>fG3aXWr}URQV%TQ7*r z2Sj=IsH#vE(VJKS2%f=&t{&5uC0-lyd=O!tJ%L3HQ87HJy^qx@fkKs6*1I#N5TvbV zFZ&+6Ct5zGpcH?YUPM;w64&$gOC(U1Ex8!lc#h8a`Q~DPj>JttgXf_dFkbwjlCV{qyB9V5-aP+);5W3*%y1oqHuMzXIV;$Dn^CC| zoS-sk^aVrLU_Ilb{+iVXsQ(1<4a5vI1d;6dT_#>>L`+NWn-4{Gox;2B43Cpk^8ijt z6Lv@e51ktNnNeWUaQooC0ub5DkKXd+RX*jUtj)x{d{#y9GZ-1L_6fQ2v%`mnCI zS3d7o2I_0aQ{FTfNNleY!T9PNn@4-~dH)13ZF;ks4o0+>!?&guwvQe5JPw@%DS3K8 zOs!A$VMf$0RSa0Z@@7ghKWz}9dUUl22`nw-bN-WZ2YM${lXm*;a}Awz$ZLrBbpcPR zP@cyqpSP-Fmqs~NtHW+G5{qMokb3D2=x0RoPmxMe__@ToCvplUhqgUJ;ptPg88X1X zS6FK!VB)|atv#10vl!?k zse}j(yHg|88Rzx>$*v1lq>5glx@G`tuUH17Qa+qJ?&l+dJE1gw%4in!w*VtQrYcc3 zBgG^!G?6ro#jXhIzk?v0AIwo0A;*Os*&fia8EW@lshiK8nJ?XYQxM)jfG}(_ME(XJ zJ9k%tIdZBBw(j}_lV&8AytI&E7_O}^BWsD*W1FKKQ11It?KzYGC+_T7l--S#I;G1= zAI1cR_Un*CVvrUaG*GekA><@g5tdR`vAjH_apsDeQB@rHc{utha*)mvVhIn;Jx zb64E>+!?Z6wdty5guKE1!&+xmu{CASduD*(WCea^WK5su3GkC9((^t{UUBP$1tI;Nz$j1P_Ofv0PC))!7qF z!|l_EKCLE$?b6`6Tk9e|OUBK0{jyHc)%J|6pHM^-6Yd)KSju(~fl-U46n)=FEjw}O zqpe8Y@yp<^8yVGff>P1E*UO4r{#uIo#bxQFu~*aR*Px2MxuHMiJTtm_{c$sHuB=3m z8Om5vuqeq36o1uUh3Z2t7c$KnU;{7q(Z|aJf(_UtL7W_cc9+r$7?@a+ZPj=O_kscI z&9W7_o)r%6H~|xobp5|<0kCMQ&o&#J!b?1=qGf2pA5#)X@A;>?zWkOeWRd}jw2Zg> z<%kE#7!U^jD0g})sD1D9ApjcpOwTb;yyf1+-YD)ZaxCYZSgS-!(L)SRlSmNS3TgIX zbm>F#Ds1c*A!mDBdqqIi{aMP_w>s8?j67aH1WU1%YTt(ih=^wKIs^YnBxamoX>(#n zrK7e2S!EV~M+Hw}rmWO!=@#+(ETwub9qR*Ne6`>9QlizR$CM)K$WMMt8(aL-PMor5$!kdt=Qa;Bwj1hirA*+;54)9$8ys|p5o&;t!2^!O zaqUfW-chSrGpRkg9%h(R}6Hz==T^4{G|xUlg86rBb3pOV0zI3AB4z&wfwsb^G&^ioT;P;c)8S$I zeoxs9gm=>3uy4#0rQyhh{KH~%;%$YI#QSM=H?K7{%|J?=;~+HfRQSQG!XZ+Em8cY=cJ<-98sA9thvt$I3Y2lS51#hLS&2%Y^5?AK_NgsmzO zz{lNGY{(=7LTvb8TE%oJ=V{Vx+0Z9G2Q>r(!kq{I zOdV>anX8z|Te))y{(S{}$5u-8uvhk==Y(O?sti1C0Y(wW;af-Hal`Piz+7|W=ac^L z#k$JVPsMGGBonB9aWC^teq2HPHh;z75{KPK^T}4O+=Z1R8vhs`0DAb7J}61YD0F1I zF+jX$<6L`_jw0_6$N1Qo@!Zyh^Gu3W+|r)rtJq)u5mL4aRY-}&euUWY`6gfLrOc*U zc)3K)b5-tlBh6?TwI=rScnca=WJyd%$8{&voV$+zqR%2UDscVgmC!LVt}b+IFBSs# zJ_0NcplADyf;8oW*frKTVzeV-^oP!J=(jnJ$M93}DLDZHt^Aj4y6GHj*@^bIKa`{( z`L&giN_O9dhp)j%(j=X9APG_wb)~Epzov3G_Egn#1O}&8;w<&*{i;jSwpbgr^cSlF zj=SBi+8iARPIs4FPmpPQpjmS&lX&gkQQGI;_$h6bv_L5#|61&4*g<2GcvMpANH05t zuoCnD004dFIdE+g1+XjGacoLrv;(|v=(n%Y6>y&Si{$}|h8?~hlIM?H3{z#b3E5IMpR*aS* zBJ7>(itnj#0mpj7soj69jebYGLaaYk0V-;?+=pW~Rx<==wg{gQ>I!pN=8AZ6B%mpK z^PaA92*bf1v3zajSJJIlyQS~MO_UYKj+I{Hcspk7U))|kVVCK}VC(-ptE<6QLuLNI z1JU~KXuevsxoicM>?{iqX2&=Yv1i}WY0pO6dOrrO^}{||i?aO?={5mHWCz*TqCyQT z^FcXeLyuH6J<&&1h2Z^Z=lyHatwG*|9K-GHMjvlrF#J0i!rD8J;|^dtv4@W>m^$s#C?r`3}2_3t{Q> ziQCGq-|kMbOIq@(_1-WofnXD82CiV;Ednzv$hPA!zbvde?Tz62H1JKo1_8L+s_4td z3Xv&vV zg)69nHybj9+|EBxMvANC1^Kz&e$ZiQ@+$VOgmY}VFnr~5BJ|96aq_kx<;1RE3L@Pv zO-&x)n(L9u-3u3YiY59A6RAmyqoR3VLrnw0q{_!tD7Ru~0vm(KQ$-|=^iA*(defH> z=@r$!@cNAYScQZOKM6x<-Fh|Id=tucyI4Fr-DX$rtWqW`EFB=a_$LDss+4#?PD37DWn%YNr&qV3v|}f zYBW%t#?NZy?&LxKO87{Q8M?^jB)Tq+Sp)A3FcxFKS{nVUS$}yj=VN=KYe#H7jP|(D zCini_1+GA&4UpX3ZkpQ}6Nk0Q=%nZe<^#zcngRb&r^o&P4F@xxl5)w`OdqJ~Tv{Ya zv3ekZEbIS6Gho=91bRC<3+%(rQo%;Z7Sb#a8FoCyl>NIS!0e-ZbQL(=gAzTRCF{XI_jdnmS)8zhX=!!-)@s+taB5YB+UNmRsX0eA4c zHw=_$m}h$aL^Y#lz2TK<+=tUlhNlnrm1T5@ee%8@T?F(G01=P8vEuAe2}Yvwn0cnl zRfow&%>AtNgpq?FGxw!kYmTRk-+DtAw(Jbb105?T1f2_2?*UcBhD?RhI1^<$;PEePU1@o*1@@v>b}`>HDUkdvWnIQjCD+V`RWNgq=RowA zhkDJLqzncn!B>ca8Vo^Q_??6`?LaoZrCR9e+oT32!swNYY z|9_f!vj0w;Gs=_*go|bk>_$pKmBrNBftcp> zA|fhm{3!?jLF~(=&vICAT$wa6Gn}fjNC$_S*EYJWlNMv+N*GaVH}5G7UgweG-8;c& zPeO6!DRL#l8;_ZMN3EDu&5}%&8xn3JqDhp(jX}=Xi1AoOIo1`UxH>v6!tEH4b~rVt zhgSU1HqwkC*CO)i2YjXuQ*ogGjC<|-mhd&vvuIU}7uF&#(+PC%GqyS``|xwDFf;nz zGaXFd<`-NYtVYM)U|@F)1}x}>{!@brXlYyKuEyW2t$jKCxP)j^^ZmukaQXjwIe!!> zt4B~-Acf|x2PHjEo2QEwiJ`0m4|iVqVu4W~Cv;kUy)(m(8Oy`ZZGZrECb=B>7Us>% zr{&n&t>y#yZqRacaU~TlI>=8Uvg=afL-*bn8+Pc@5~1Z)R-re#ccBC}S30x6?KXuG z>U?zRh@_YWMRRx`V=ZWU3h*Jb2@tfm&O%;@ukCtyvQ-qR!MWdP zci%VErB8BD^d$U2wq*t&pltV=%4*MPo5-7CAPKVmij$f}gF)r&S4qQQqX>bAl@7JB z$io=@Wrt!ylwO|^9SA(n$5G}kd$hp#>U8{O+13rtl)6|1*0M?X@%XP{WZTUgIV;I_=T)?* z0zj{aT}4jCfkesilTVRU2Igj7;Ex_r;R|k17$}o=z1CeZyCdJ40_cmLw0~w83;dE^ zHY=@>@a!Af#Kn!}KlZa0Z^1BeWO#PmN=^M|q7b*>)Cn_K9DcJT(t{&B=9=Rn=_`l9 zy$+b_>gL&&k2D_XR&f}klVo6YqJF&2p+v_+#5?1DkO~%{0X?8D^PR%!3nB0XNZ8gU zn|!_`1h4?aW+*H-6xK;wGR4<*2j#veC|3V!bs?IDQ22O(zHIGlQ9eM?h72x3XVXf3aTjJ*fPFTZ9o2UY@0o@SDY4-VZ8zCy?;~7qFK_5 zoEY~8u$Wkh$7b*ZhoM0Q ztB#+yfo6nC+|JJ~$5du<{}PdxW)YXA2r-U=LpPe&F@&S@ylaWmS}fRL8)9#LcaDKC z>vNQ1pW6hu?!7}fZ%{AVBHtmXlKkP*E*r7afIyJvwY9_VEEN{xJ=y!xF)t1CQJE8h zU$kG%#9{TCj|lmdWYHMCN(v+{3+MdvlJgwCYIpQ6Ww4P@a94 z0)9=&Tk_J^)lSjtz$I3lqDJhQm0oyjVN(P}<7)I>eGF?ARs~bSed^tz&}~)Py_qs7 zcI21ctp$?FQq)$Sa8)7i+TTTjlh!roye8~G1I^wVcOmicn76L#sw_gQNCQ92ZB(NU zT9Uz@N2E;)lkV&Cq+syf^E8Jvw)U!c=@mS0I`4rU9Tunb_y^!SWff#8NdpVbTQ34;w=A0I?r94YBcG8fN1 z%=srUY|bCCeHI1kI*w;CZvjUW_Rs!LDSreRL2q*C!0?naLbo-T%F3<-Ixj5_$0TFo zLuEK`)t=gNZAy{f_PUYzNU^)-wnbKH@t}^mfCB8s`bebH@83Pv73y`)w$l(yQ#1#FLZ z%ma{Xt^P=5uON&1JL2L*LMi}JE$f9dBYMN3dmsyQ@Q|2kjjWLi*wlAw0*_}GbhRN3 z$n!j~crvHykc>yRMzL_nmu><7IfQw6-9$4 z39GSmE%`t2zh8L_Oay9!M8pks7gK(m$kqL3LsKy)2cW_Pg<~UX7&=eB#@kd;u@WN; z5-{)b5g9wd zh4iI8I&IjyOMUJma;%er@F=}izQ9~|n&JV++r>})vuTDGWIXP`*_5I7$3R(!WcS63 z()1U6wi%SnpMRY|^SHyR{H9B2GasOvZ}yy39hul__r0Mmi8VMb(|(oY=;Gkl^OYEx z!#fkncAPTIdknRoV)T|G@>Yv6LV3t0D{t!k{@eX2nivlimch_4%D0e6|5TLUSZ<*ArC_f?i*SL(&rhpqOvSjd3^=k;f)Uki;X~iQ zOJN@r5;GQ=^5kxQ8=Xa*x~oaZO_XYnW}e{v0&FwFlv=(GJkL4sK38Duy6dPu!5hm) z}x37Dc!E~?=1>z7M>@=d#bPE@)Dcj z5`Y_Tfs11L81?0#_by_q9b1~37Rxk5w9&~eoG%{B;b!-!lwA$=NJm%=*4vHqTp7yLrC*GCtV{m260$A{w)2J}90+LC=Xqv0Y{WGmj zLr%UvCZ`d?RaQoH-%|SjaegpurL^f^a|hVP!MSras4g!r#ct^>F>^Fq_@3>Fe9;-e z7IkYFQFYUcjb+6sX`-+!rAtTrHVd9eQz_nhxl?^BzZ)A1W>;EjS5AG|ClMTCdCTjd z`;AGkRoju&3RkCeBW-4#+S@x!Vvk!p?im{wlM^k|ek(8GGnN?Y>BZwHM;&g~6{7Zq z4V&R&?jwUCqW)f%4JW|O1M<#qc<}gWmc9!M;0QdnTvCGw@Ssgvj2e@gJ#qL8LAl;V z=jma10MEUae5W&_ml|FJI9-ZM-IC2PtLM=nie++wyOF)3d#JOm9t(Zm>~Ve2oX-^} zUcx6DmLFgXpTl~gMSzCb?xO1Cw0Ui|eiZeF0DX#w0RxHoaMo2c`~nZU!(bYntC^#S z{MB-7A;2SRRSk9W9Q0sOQ!%1WsS1%9vmL<+vDIt z0VI#!;W%u09uIKyBbOn66#=EA$h<+_Moc1cK2BnKq2v>I5SHkQ^;AFl9tOe_GFFUy z_F{j+BmU1rVTMBF#8^=bT_XEYAMY)_)#eUC7X9f>*Q0g72?bb}U^45$gIM_E6@5l| z)51+^XjISUu)(-fNO5TT^}7<5O6N$ICx+2`8rS_-`eVvVI#cm5J%&>~rOhvpm=Fb2 z!Xxu;W?xhpBU>3Nnt1b0N-z=2_QUN*t65q5-;@%8)g4$0H-TPL5FCBxM;k}8$n%uS zkl`EKN1%9k35A*INQ2j6(1)Z*X2+L!Qj)bO#r{+DkNmts#Bu-q0q#|!)P%01Z-fj3raQ8}t zZ3aW7?lMYSTAylNL;`T7tR~bp?KN9-xf+hr_E+UW?i`a(;{Zxyq@1^LUnC z?O&Jy1PBHOvar6+ZD*FZ)(sF1Cy%sb$6u@jR5|DS0@fPk z`wT#JqpPw^PssK1heC-5BG!?P-lQSf zj+YLkM`12z0#HkCvzmqU6zIyrN6wZ>^=4s;p2Qo4q09^HY&_O=!-l|Ftbm^ZsfkhG z`Gn_$`_-pJnS3rQGH}| z+bQQt>ynj~@hsI01TEvgu~muziU%7V7MiH`hTk9qX6ScPS-lE0&TABVM;;+RZdt&l zB*!n1jLw4s*PA*A9c#BwuhXIHU1-%>Mh}JHBDc}z3-nG{DR>Hv#YGtURQN9;fN9V( zDr4~$gQU+EC79Io-Z!H{YNy~CP*+2tIyCS2AoeXxaL~_9#^PYzyz#rh>0F~um@y_b z@rit-p6`ADb)z!S*cc!YY)VURG?eL|vx(jC`sTve@ePs~kor~-&VRNeXzJ2Q)<@Gp zW{!WV58~00jT(bEITtQ9QfObn^>zyf)z$P5tF6Go+B=Wq4r^RW8x!7N=zS?$)0Kl1 z5S3f_eQ`(&=FGuG)f+KIaIzFQVoOae85{txL$RBSHeW^Nd>r#&n9EJT;~LQA?9bqv zA{c^=y(#MmC;LTN+?K-=0Gg7!!cLlG2TsD0_js1&1J14eoidg@E{_`2^C%oh;=G>6 zy)vKl2vNqNapyRT3A9mHJ^Igt~-DCG3v1IGx$bg@%K`lgi#NH`$$W_#KfH6Kw9B$(b zEsO(!6n_PKVNilAYtHL8(6vE@MgIsP zvqB`!X}N8vnJ~^WygfLxhuV z0`+ejXEDADh5Y==n`{%Vnx8A;{{j@z(ew(lU|05m+#F|(`O*^WCOzpx_k@i4R9Dlt ziQj;4RXns95K2Yde5eKkJV$L#ysahbZmSVzSe{gsPJ<#l*Y{)&oh`Wkf=fJ~peq2p z6Vt|ABVO%2TD3I4)vtfvUrbPf`)Y)jWHF94H{3?G;&UDjtM2mT3f$6O9B}4xFt%q^ zj1xaC37dfV0G*5fQ+1^j7V72SKMOif$!C2v+($Eq0Fh(b08ycG+x)@wkDLzE76v z!`ZVFS+W_3VCQRxEY6;<4P0 zB=mH<3#jMwOQ6xefBQOqkYV5OE|=++J1Pruoonq50P%xmCG|u)Im!YS7mmCB#l#%r z+xk>I*cfXyRVbD*&aM_%u=a!OBttKs!;~4qKT5K|PILw?2fbqbTjP;n3MMD7rB%EgpC+hHGN) z8S<}osi}e8vR{KaDR*Mw6GIl?{EwU(m9ax4_O{&1Yb_yHtSCcE60vqNJbs6e{1SB6 zS`Cqo8KjoAuNF#QUE4l-lbL&zw+0G?PkEA%Bs97i^Z+GAKM@e5?%PCueI=$k%?2l1 z$av^Tve|x7g-GiYZ=M;3vU#l_Pqo>334T*yTVW!3%Bt(MzZpj8#x@Ht4wo*G_K@ip z>HDy?D(5p%&m0q^`!?5W#)6suI=qpGqp~0AAo^4o^XAg8**SP1`xwsQQwOdPl@7xM z;vUD24Nol$fu0AZ^GH9VMwqV3){WkN3;0`4|5@ie&$FP!@`|R1eKo;KE#Ogip2=;a z;soCaE1mr&uBuy;zrxpn-nD9JyTWx?PB#e|@RlIZ35V>Se2Sq?4jM|aiPj-FGS2aW z0_N)0)ifU?A22R7qVU$%2o$(g*W-* z2h6qqU6cz#v?}cghkCiM;1;wVLs^aOiZgQ!K06zg6=9Ep@ITN-nOJo$Z0gBbrGx5) zrTF?L`LFZYuzI4mh)0M1tp(w9VyZ2d%$WdT0-0sBE2my}75dk52N`7tTA-pTr_ijI zeKq^D!jBRWU!v*)v$cZH>%tIAqHcPqZe(5$QV!W`Cl@>K;FwXd?MhTfM})epq<)<; zHKK)FCiakT8Is|Lb75F`Oad75Oz#j*ynN56GKd;Q`FPug`(NVFn*+C;%e@@YM=ghCTak~g8ekfY-Hp79773h}ScF@(7Urfk1Lpla} zf{aZ!_&i&z1@I4$G^Js%Q z?ckPGYi`RAO(sPtb>j(%qt^h#a(F4TdcbpUH~%`w)*}-5uyBl zP%xLC5HW6E@}Q)G410!R+!{2?cs)r;ZflhMrcxR+6`yX!lQ^cB?um0>^HFl29hVB! z$d03c;HXg}AWQ`A$Az#KYDJ64hm5;cxgZRZB9Z-TCa_P#*Cx>$M9`H0CSidtstE+D zdw4MK{<>gC({?leE|H*`eAkQ4o%nVVkcpRdVFovmpVqx{W8Dd14>c*r{Q~`mRcW)( zFKzSOynU1jpYUM;rS>D@`~6GYa=>*5W&o+~=jbt-kwfmhKr3g7QNG3p`9b}36a8kT zZrMrO9QqD$Y>zXz{#w6%cBruDyHcI;UY?MNTO;P~_Qdet26m$#Hgqco0BZ6++sWBz zyVaCv0m!I1`KFjqt&uP%8%aX|iVd`U>-;ELR@)Fh0197xW;nXI?yX`3ZSs1=LBR56QdT}N_^&58{pL(kBfd&ClH`ohTp{I zO6%tVAkgW1cHbdR`5OFEA|sx&5*YW&hJvz=f@p>7#@r4oNvi)SGvP_9J{zuFP}LUR zbrXgZsFaITlyc3DKstjq3Gdxifp(a0MqOVpNJFHdZtuAWi8O+O<3!7awUO-vQvZH- z{f-t-NeUHSVpe05PzM$BnrXiLL$mZ%{fb<# zSg9^l-Cr#-Cc^g4O%g(;mS278 zG`C!|wXqztBd`@Nj6bq_M%Cs`vr~Jm1694|&T3#@FV9#}hS}Wv;FTWQepEjWhqc3@ z1bv4RgDFNBzureukOv>_H^_spfwB6UW>Oy)*r03N*q-`mnQKZeTx2Hsz9Q5W0Nh})-j0tn8S zjUbI2F5wauEyzSU$GnOgn)GMxmfYt;nTU5 zw(%4T>qXi(O%qw9n7bo*V3kJ_G8Z2JZe)_gPlKK*BtuC90)>{>Kko@UsQQ%$v`=)^ z7GI1)@P3d#2mSv~CxqU}NiDsB2cT*}PGK35FJ=?;umNKMMB!o8@N7&wS#rTdthy

    )J($(nnSvua7hS*%l@^m7Kf_}O^bE3(^6@=BhGC5alB~(v5 z^u7U8Ie}xceRKoxdD3CzZgXgP%;go9D$8HfNqJj4FH1(=m zg2y^gxb3W&E8?J8a-7C{kPri2F9~=)+)Ra)mQTf%KJD63k8Duc%lFkC_Px=63g09V zL*G-@3>_!VI!QYvHQ}_-_#=#knIz$&#!O|6yvDk_rO<*)Qy}_fVKc=9JNU`)TH@rjWop>NyHm(s?3 z6!qAwK7&R-;mh`+4GSJ?UAc-Mu6;A2p*cTH~l9DK7 zrEXA@ul8RLGrF;%_bE!>6cxinsxV%MTarDbcA!W+CpeEWk?{)gwBSk!S zBjZNoCf|pDe7ko;=nb_(G2Db)4@j#v5?5glSAY>Tv8cr}P#my@kQ4(4@ zJ=SB!RD3Ybgc@wUs!MUT9b%QFa>A?`gqZ zi|=ZlQl%w=6182@Axl%-`vOlR?T$Zh!IaoVw2m+g{-HF5hU=yDgYCxRnBW@B8 zYy{b&)vmdF$Y~cQtS9+`cUB=rL-UcGj+ewU3j_yErydsk^Nbgl{BK&HN?PG~ol;bB z^qmAQk_(1}S@2+<3>vH^mi}H1L_yTpNKc)MN2GX#On2$}$7@jUi{I~OMlmO8FIaQ* zJvHM~DO7>wFuqy!Y$AX0wAfQt9jv%9>NDN2$Mg9OT-x5;fV3B&`0y3tv zHZBv+H7N=JaGBC{H_>D^$PFgzlgCzi!d%zJC^hC4YcsG22B;?Xjs8}AD9%EkvP@0S zx*eU1Vs{|6I;S(bWBxbOW%88~4>U~YIRys?RSNW}?j-a3rFqG29vH=XM1Y!cAfvDR z>0?FBi&`LLtpe=97!5Z4*NF@GjJCO4Bl~#gcy41^iu}EjO>_^^+7tOZrDT?;V64Y25nQFr?Y&v0id_8hEaIkL zr?f6E9(w%@bS~0{cxAf^6vxlAw0m&mqdgMTmqPLtr5yW1-{7xKdjR%V=)l|RJEi<; zmDhl12mSvWO!Qt8X|pNz*Tx&YFm=slLUlQgAV6GqI>BY-TN^{75{+;IOHrI8gGQze zCPPw(qR9-pFr;5c(;84b(f8q~e}nZw9!z!e>C2}1u1Nruk<8Viu7c+9CjEuG4E)we zh}ILf6gsc;IQ-8WGY2P5Hv%5+JA>7j#e`0Cf-~HDQnx2?Eoob+gp*6FO!IR-jsCfn{7Q4sz2$Jg6BN5tqD-8>D^$xlxw%a| zwG9w_C-ljh0E<%Hsh#w=w)wi&oD8ai!2;f%xNPOHp#WG~#EX=rF+GDMG=1N>36ALI z^GfG*G1AIsN#Z@dFPgu;sVhf*ignQr?zqaFodGfyf zxj5pUr0zQ7#);GhV#I1=F77o24S!qxDsYjooahoK}@2S0j4f>K}=t#u?{yYCykd?%J zJLRGIliD#P3+?gx4Er_j?G)zkLCy&J=A%cri~BU8mxJq+`VOu?F#;IDt+bAL9yBrs zFR{72-5u}#ux+7dN!!XrhW{X2eIH6J1K{2m5z_GD0CY1DKz@5o28OTm3fmS}tZ&gb zGr^`bpjQ7A_q%M@oX>}gIgccm{-+hB_mmClK(CrBbMuix&nomijjG3*i@4{l{^)sp zE`nJj#+#Y$VgsfESRwZU^CIm4e-q#k6-8VYp97{2>YZ^Aw)0Am=hvJuWY6RL-!*3zl_&^{8ZSxl~OI%yB(fO4}cDAX|x*% zo30eQ(=;#g4Ppo31mth{Q5pRgR-3Wy?WCTslv~|^Y^-IsahT*}sqWHTpUzhVBG zhucrD;+nlu^LgY6zHMf{Qa`Kf*{J;T=>Y$G)-*(;|6<9)%7h6m3o+&hB_#%~2bFmGImzx+g1#cxSe z$nU8BYi0x`*Oo(CEl+KAGo5j>iV5^cNo#u(Qd?RwgjAxm@Gta*{TGd?36k^}j6CE& z=@t*$`;~!Xkx;{_o{x`Spf`u7h)z41WCWugs&;r_t%lL0hCt&_7Jz&e_!-(hZ;IkF zz0CMqQZcRR;7R5BC(qG61ac~VRt3fjnprQKqjzbKag@uxNYGlbxwrt-5T^*ame)1{ zE$fzNIIucqBcwQb-ZAnV&E5#;u=EFPs4D_g6`S@sra^~wSiQB zW5gnCua7ei&>BWj17yinMfm+^9aFTnFG+avW8Cpyt$QBiO=Q8ZmZr~Edrt@{ zKMGXIN-*7aYKb*k02Q|a(+y_1u<}KxKK73fDyoDue??manV6ZLkEV-Mx>5IH@=2fZ zdrk?{V4U}<>skCpe7oOJr|4)hUtYmbaBd+-__vMP#q8UUso{;hfDu%g%JFAV&_9Fw z6Iwar^{S}#&=Eq-xpNWs4mNH27{(pNxT+9jRFyanTU&_q6sz{=0ICSeY?~m4jZz+1 z);K{96AwroL5643m;(Y8i9wqS9}>r1vxk7-32D@)x$j{ zUXL(r9bm{HXiQr!V`LuAo!i>RcMho-zK`HCiuF^Zo|=(?(2~v_kAM~2;GdD3MbUI4N_M?ckPYW^EW>4Y@JZb#3&0sq zn-?cgrENXZ0s8@kT)G#Zn8PNRWWq`u;;Y zJ_oO3itprp+3GlD^oyCT+qGgFNOJs%DJ&^M%3nAhjC$#xnN@V78V<6tAG={SQP~YM za|9sIyjUW(&!oHmC_}%8*9R}0%J5+Bx~j+aBNvpI<$$Y*Q|3r3icbsW6V4ydpbZRq zc`{@$Ugx;l)ENMHH)jM?rjFmdA#>IwzjW=dVQaKS=D-eRl=eTq$kNuwPLRSb9&p8{ z;X*IVr^Id_!-sP#i zr%~?7MhN!78etUs!AwTw%R=_H(*V>widIh3p#2VU5W!*ncweVFvx0YVpS6G_sN1(y zE?zZ)f`^{!SJW0!6MaNZhvN^;9QZKypvy?Emh?%-$j5T8ch`JqQG8}BZ7*qm+sK?p z6c@dfbwa-7ZwCN}O*U}8C;V3R zuedt_o`u0IJF&{q=7(>L9RM*ZY&^x>&%p=&g;`w2iiLA-1XZ%#H-?ce+_460*T~*o zj-@ADkO!$eQ2t)j3!=&~%*d10J6_T~cET`Uf>V;;98c#yBc~}m`pnvY_>3IM<)F>(vuvK7{>=^h z!~j!3tiRfGQdUg?gjxlXZWKbN)`#JCT2EG)yO*0cxy0z2S|5SE8bpTb{LCkt@$_51EvPA*C;#lE3Tq{40{F*OmjdDUJv|FiKegu6$S-bwi6r z@?@veQ!Ysy&_bEO@CKP7WNo^P>Qh%&L;qy$1tCz+dV)yxZ*!{Or6nv@z7bFh-7s_F z97Bv}ELj&BxUT-}=T|as_DTG!>BK-APZ6Fj&gmQq*SAhT7&JOmo+pY)&4DdB`pN1s?2JjimhoD0UC9Z*@_sttGv%DO5Nas%$tqA*$C zpZbeD(dn=4a-!cY-vK-NEj;^>+2pQOT<fncw0)0)mZwr2Ot0HIBJl8q)<{KDF}ffkbVu~cM#>5H zk$+3rA^O3%Dp8}G{y)98R@ZOxnESd3|7^f=UHGtL%11rl8rg0}p;fl@EWkOjB_D;` z;0J}nCSyRwcDS1)`W0OVD{r05_xPnu8a;relRLb}dF-sEgQAuSt{A7p!=2JLEb#H6 z_<9+ZoV)eeS#YyN(_$?8ch#Xa9LjS#J;ETlS0zh~rE=amMWp4@d?=sQ{IE3Ut0aZ% zbt*C0?<_^i;U-qKehc=i0Kglp?z&tM%>~c+TRZLi2-x5)nV$M%RM{eU4#%b`$j90`$s5iC|PvfrrTc=SO6(}Jsl6*bP@#IQXE&ghJ#m+NaK z)dyluN3zt1Xugi>pb9qrN5sWt#gxpEAFkBSpwQcXHQWK)5XLZ(Lcpjt6~!H z9(BB!HIXlksB+EMX8wRPfWR@ebJL^n<8s#J1ZVp(W?V(O}?pcDUA52aD$AQ?Fy zZok3luDf-o<$uD18%_5(aR85FpIOP1#)n9pl({T3eD&2a>P>^J%-;a1*xpQUU$bf z?-EcC`Y@GbwelWKcK1jDi#z-p@tr`JbSk`JtI}t9(b)qo-$>b#v35s!sG2IRScC}B zb{~4R28;kYuLoo~q=QUdK|UJLThWKRfl+F~P()8zJp}-PG{WmR*7gP#W{9XBECh;% zG_$gsMjCfQs)&rND?!W|$Z4!0D@oj?)r6OBGPwIwFH=KndOItQqPLw;TEo`Pzq%78 zlhAcFJCuLCixL^5)!zde^V;3@$OqI$3nTj|y;`o2s>58=%Yk>Ba^J?@)uJf0KcXm> z^mhzUCZpq!e~P02Y%p=`P&fJCX*#fMw% zUhg<;_iU#4$3Yq2nZmK)eGTe7Y*u%rs9h$rL-9!sRmU*+v|9LWZ%YG zT2^M%h?suuRsbn`+kcR&QASwjo+-msS7n-~_J|Fji9Y{Uq%*UHF4W5LX>n@>E11ez z>6_kXh+IzRym}Vl`*&mZ?@#UQ@Amb{hwa(_ZQPIB*f;I!OCQ5?|3hgoLpZ{vK*Hjv*RdULUlB|z%-aeTyUaLn-PKBE_>jW3Svpv0aSzsK zei8bV$t679Yf4WwDD2HZrN=Ol=fdurhfUdJU#n2^4yrrEe68!tvq(c4yHQnHP+)A8 zr%@S<#z5FzBI`mRaeAp;?;f}OC)Vmh*(M33c7UzFp?6FTh20-`Pmrmo8X@s)STCr4 zweR9<$prRT;34!qnECK>zHUMk!S6s;P$?{;5ckK)e**^^s6fiL?xK+P5-f@4s}Q~< z>|&|z_x2Zj6)TL51aK-qZZAq^VT2RL4nkiHJZJgMQ*XMMOKP8I)ja1kN<@_Bj(60M zq1jyVnV1}gc$+`w-AMF`J3`tH*p*amD_`_#=uCASWFrs{fz?<}TE^U^E55W!7FWO* z^T4-3UbH(IVtAGi+*TVBC`-s#-1#|M^6`VqR9qX5A@~H3Tty9HuiNEkWC`@`eH_yx z7yE2%#5&i)5{N~86As5#2F%`g520DEyS^eRn_8LSNV`UZ4-aFg?7;Tyv24FCJIuls zZc}rFV5#1=X#0PMNntG&;&x15ZuhE#{wQ8$vz3rF5e1+~@40`ZCE*t*ND>sL8dE(J zwo1;OGS++)_1LUFgGN6+Et?*Z%Z$TvJNjpkii}hQ8tWboy0e>YjJQG5`N1H_q4DK5 zQre*miecm6UMw;jQ@j%CLYf_Z;2OKZMfXXpw9}CZ@cD)g&nGL$k)=#@jqm2Zw7JWs zAO~fRY|U}|t6p>%8%!bV*1<0}3yey2OKCNnD00Upq2~`%nD+w zxu91Xx=WM#Ja&GipAOw}NHswo(Vcrtx8c$-Wo>V&`WuplUCKFKPoX18*V*Zj5_xVt z@U-+gB@bmpw+|Aj>90=2v&FuVNK^u5&?$(TTip?>9qBgMNMyyVX%X-QmN;Ca#vGKO;HKehiFu_>fw8_xOr6j+*UFI^{~{}1Qz7wj~u{q_#==BgrmLiJ>zkSmsT+1@IN>vqSgyc_}`u2=6x3YRNxE=Sk~He zVLq3K+uQ3|@}(?QKy!p;yR5UWP>r&z^R9(IUXphV_@I3%J*OVoNEw^6W@$Uu4_!Xw z$gkM+Mpt-PEhp;G#cSfuchEjw5(iLE;8xO4iBny|lNBZFN18Sb2rn8_wtc+(=I=o> z|9F^YO5Y48%V@>JeX%U}JcD6?32jMNF0X=?+_SpUvyw6R8A&y)5PI1iL%r*bz_zqu zq0lT_qw$DLf=j&~_B4)s;fLcoi(jE{6?}W4(YB;40}eg0OR?pYxp7n2h9#zA?QjFS z{IyR#cZ^I&{@O%;wmZ>oZ@Y3;dK$0lFp8RRt{h+d24MPTXGXiYRc`d#j#6UWg;|vE zjLAdfWKNON#H!O$7Zi}j0Ix0l>Ktpu6#+;5kvS7{!DBf2B<`EikwSaW2o3!SQX`My z6VQBgUhToE6I(3?erhEPpWW^xD}ac0z#nO2^acU(59gpr3wv9WTv(8s=hHDu7)Cd+`*;ro_Rfy#E}3^5 zJn4V#!7Lq%%!Tzf$s)pIjqB}@cH9~t1hAzKKQq8wO|*390$yyX=7|}tFpSa5x9R$h zAj?Il70+TqC5tL|vgV7jw>!d55;QLEy${?-9+&POk9Y$Uk*K5pPAkhLgk#X_9{_RK zRr3|!q! zD9yxb7DtWdOWL<{1SPUJv$7Bqj15uwMGq&%LES$d;l0V{OlN9R1@G`Rp6fR=;?M1W z2L@z(F6&_(XOSb^}Hh`Ew|xApWx? z*`ZvK-2uC46&LQ5Y)VHQRsvz&%hu z@A^}%q&rYQ#3#4l8waiq+~zFEEIpXe=~XBMBzy>pfey-uK>^m7$1C;JQ*Gt@6D z7Vd9b#iY1oqNujBLz;VaNWvn^P_4kKF}Z+5YS_@gK2-)bSBh6Z#Kcyf>KK}<1(^7U z-BL(T$GJ_~53NBsM_zjcg;ng3tO@XYgm_(#WR7NI_S?nI8AYAW$z@XFm)U4u;YE{l zvZvy8ud&Fc>7?FiDgkM?-VC(%(2`%Fh#Rs((}u7F1}1ny8Knf}WW*KB2X~5yA(c)T zUr2>OCn)F&#iW1vU$=g=L$rFjfc8}v(w#AJzY%)PZLtd6%7>k}c-*!Fgujq^miV5U z%0_4?p`05@ddKi;yG>A)Ad4`ggH}m?$;o+i2d^!_fuVNI&OD6l^j7J`$M*>`95&W< z=<>87YYfXTeasybJ?R?Oje-Wo=-{zzYW_-iTb?pZz70sW^(WwgZ#1EkFynmSV3qLN zw#1kL&)31CWW{-T8s}9O7c-Y8k z1;oXkvdUXQHC3u=D-^@4%9iN(>}gY3aI|HdjU^TI2izf^JG(C|(+}nUU^1p^Ivkt% zMgF3IUN@jVOO3e>H03}abiX)}ajl-W|)1@i@2A21i!na-Jtzv~X zP|>s~!l2k056eMUcW$iGxJQ2Tf@qNpH}?1too1+tjhQ%)W@jCM3Busyb+7M~pAKSh zEEzvH=s~=kl$`aUlbbcun~#0VU{rUi`_z4oK;`ujOE- zEU_N288YuK0A8!qfGKDTG#awR@dWFhEQBziVM;?M*;ayEyu>bOt17I2`rdsfWi5Na zG{%~{MkvV_rNn_-#qWQjOIZLNVp0=o3M`KpH}a0P=^~3(O&9c;NE|r-}H$jr*=y7+ph4pUmAVb zypO8>?-xkU3OWam>bjE|`vCfGi3DQT1@n9Y5|`ziU)B$B{j7iw|8zdx2m;2zpLai` zkdmr}u-~PxhyZ0hn;^{7$AGU%O+b;Xx`^F5IopU{%3c`-bm$R;7zcDHg^~4J6EJi4 zrc-QTh46gQ#KQMjfBUbaWSbyI?g{P?#*3>-pwIQg*lcydcJ$eoVd5M;YfQ(TEg1G%7*kni>Pt3_Rg${b<8P_QK{2>*ZnG(uE&ic0?<5;rEoLR%Z` zjpVCZZ0Kr3GAFq+G;eSp5OR30fIGgy&1fuUq{pF`hgU1!iq3`SitH{*u29hD`=$>9BG1P8HVj%`;U}5;Vhrkr<;IG z4DBBW#pR@Vlldz)!}SkCgW=|0hu>G2Mn*fajVe}PfBCPXm5IUOH&>&71c2p8Aq;0!^(WV#1p3a7X9mS@OWeRKFoE8g!hrRTk=^WsdwH-h>7V4Te-* z%G=SI2Rr4JI;ksF$MjGO4V1s4w<3(+?V3M|XOHD(X8QdF_Hu8I!9(h+5nf@Se}|le zy0`8@WlIOpaq?v`w~xuE8K!bwI8QoCfa0U38V!eXA7>~-2v@pY^}N{OcQKM?yOgx= z$Kq|BGuk_Pe3e%Cg6i~`4%5(UFEl=uD}2c+3K4)D7?g~iGAg|~pji#OiM6l9=5k4R zosysc5mrIY;UM<2h<~i!fle5@3rV(9~8U@NLYC|k4okr>m zpqkvhdmKddo@FDP;J}=7I&=6s#!8z?nb0*GVn8ZaN%r###lUEtJpqw8Qr8@Eq$AOl ze5?_vwr%VHDKtLMP+6xl=UKk)+!NZ749x+6QN!obuxb z1KX!8wChJ?X!`S(t{pPY^2?1!RPB(rWp$2#dCT&o#jr5$NgTXAK_WwT`{GO92U-d#d0M}2fXbiGy$FsD;y>H-=1H^n6&_K$m z#l(7pTY|o^oNYPst;VB+lo4iDn^Y2xuZn4uME75uiK6S_xbW?#fUUSVDQ#MKHi(bc zOSsq>76s>oAuQ1A?=4GQ=j)rdE|^lrq^e;|S^so1gx-?{@P=O-3jJmaB0PChLPlb5 z07K5Ef6vnhYC;E8wRIQ*$f2l3_Pzoz1T}ZQU^N>fF4!s17ibKP^N(x5)2gmS z0*z9<0SMQ|Y1WeV7|4b&9OMRop?RQ6AV0i#yiNFmz_K28{`0YBLza?~#MAvnLl?igMh z2-!$!L^dnN+}JE69|B4=R^vQ=m<+_^3T4+UPw^(>tl;swYS!tJemlJdK1|gNCZ@U3 zx|#WQn^|%%$PE_15?q|Or1xyz%IKGY1;|hnsnGgY--jlYhSC3a{)^FeKK-F+Q7krx z6SWI&>qeA+nlTrl!rNd)>7k_NoPo!PUZN#zQ@5?MT$qlX&+DcX4&yHa2yuix7pEMsZh5fmSbEq#mH;%;L~mn9>Di4PxHSykJoP6f}kF7Dppu-|sdhLzhwpIrV$x&T zB2zAr(r5Jg9SU`aZ>l(y)plrJhrT3XzvkJk-L*PIDcbrU>@PUA(U^SemH z^RxcrIHQ4y=;G+XJ|t%x-EO&->`1wx@7y$i^)tkooS1s-<&^Bt!w*L4Ind7OA-vFo zt3vR9c72kZ{Xio$3VQz0=nY@ANXi=70m+(Df;BlOcxX33q%U3!21?gAf?Gf2&8}R?e2w$5tzq8*`a#8>8-Pk$SLVS16-Z@@{=L&mBVB2x7Ove0v?druf1+P-$OAQDPz zffD$56f7*E-l9U`4YTguf>jXa< zT!@Ih!ZMc)HZbR)rP9iFN9fpWBNJ^jznkUNU%e!3rN=J5JG-;BU$1lm%h5P}T-Z zf=;_s#sW;xpA`{IR?>Fe@h(Y38Ot`7qNLBQbOo4)V0=LPkPQ!ss9G6 z?nRRWf{=Lgt{I8RaW$RXh+j8oGcL0q9oZ1bJ7wH&w;D|7`iV50QBIPl^}$Jl$fz{;FloBTeCrX0O&<^D2!;@WJd{$Ct)d6a9WT z+kN9<36?!}NjhB*3VGX;=?gK4Ay?enk#H8tj{r;3NpF$>LPahA1%N%-VN)RSf|KYE z^BS+7_$!uSwcH*b+#+1xqfw9rUNn-UZD~v5EbT2;F2~VScDjz$MhzxVf|!^^Y;^$w^w9fiS<Y${2`+JVH;hy{3+T$b9Ix%qa+2ihLi?; z0!M(XCyPBJ-x%QIMkzWSztfmQ*!mq1xLugmdEJ%Wz+546$Hs7uDU$T?_;pwUMtUBv zgUa^CP^6m}Krp1Gpj{~|DBv^_&!}Sa=NR;9E*Jn5<89N78$LcVY4W6#Y`K?kQ)B^o zk6ggY`=&Kq7V>!&A{{jbJnW37vp{5%OH}uWi=Z2)=;451(1F;T*(S}c{6PJ z&ngn4mdW-h&q)PK(L!HwiXt3b20=i*!EtB}(_~C!YkWDN-th`p_pl?=;q5a9yxu>W=nT3>xyq|CWrxd7z{3ju z_7W89fH0I4EO-~4i{k?|BL_FSnv{sB=LoW_vm6?8K2MWw zs|Neyo7~GVIsrBcM^r7?U?#a}c`)^deich*2Vs8tX5W4xK6K>ZK!D%Os|1g4I)9d+B2fMij&D8i%7y3mq~Z8>XLa7#A=GgZt@74h=OZ&4p% zz12?xip*_)30qexvjVC-$I^H9H}{V&9)Es)*1GVkPstS zcg>!H2jd+683kygoR$a)+y6yY_HdD;$gWbK<>TbI_KlPT*TGa=Z$o7nm!rzNly$g0 z5xf+2yn??Xy7geN)>Rq^eH2FB7$qhGuD>EH-L`8)TgM&*BI%S3Z=}^u$3`kr{k&xs z`Rauo_i+@JRYauF%$8zo)_5N>g>8C>qzKOTE?j}y0pWyH(F&i327g(F)(InzR443j zFs>a*pHW-tp+T-M{t`<;nacq7%^~>t(3Movo`rG{puZC-V06TO{`kF_tfZpxF|yTZ z$d?2|)IU@4b*t-(0)P#BvH)yrtmDAZ3-ptd9tXLOtt~Zj%5pO@Io^HN>P?n|B&y0r zptt}L1&jX|ER{S2IVL~>Fbv}pWC{b8v0H@yYadbl$}Ctco0%vKzE3YqLrRXqFuK%(I- z)03GBs_07a=zPL;j_3-r8D7N|b;uC&v%9s55kyP}tB*}7V-WO1IH?gB2l~uMok{I- z?OcR*J$p>u7Q$vy&S>WAGx)d<^B6T|E9Mc0q@3!sDE5mmnP4#Z3yi>H5C)zd$R?40 z@|H&MXAIv5O~put2t%#**(xvciYT-i>c2MAP zgHe?LJ$&Qir5|HqsveCVc|D(#R4tkP=$2tbAd>t`<*4gZ|51++F_<5G7i4-D;U6uM zAiUFDZc08dCslHC3ZN96y%KUO9r^Z%VmZ|+T16>)wlTxw zV(hTvMIJ_%76dny`E)v!~=z~SYj zZBCDMP`=zNCqgC+E?b?z(B}-T1Td$O2&J@tvhaf_hUO?gd!jTNTLK`z%y|pNW4ga~ z*%4GC3p?eM=%o!sCR8KrF98&m*85~FY0sEIsbb6N0brU)&DB-Xszb)tWu;(}SWJdG zLC)GQ%Gqh~{4{Z7LXM+*lA(hX{}i9hIM~R19tj1|d?3X`9<8$F&-Z`z!|63=aJ&uA z=#s=tG^{s&i5!w1)HfTe$XMUpU#bVbT$mqmw4=CZ_$7A7RRHPosu%O)8zBbj1z63l zH;d70Bz*B-UN<|z*h{?aGy^8o#3-fQ9K8@KPAx-sNl6cQm`3+@U?oFVH!_W${jhK& z=KRCh1Ew2a(sO636wzHBU$vMl0No>L)cuES1wL*7)Eq}%`ME#EuEWwJ@?41zZOr5u$(Ed1}UO8LrGiBIc#jb!9g{!n8hs!p#926H)TVfnrn#6&t~I0$t?E z-mxMOa)Fpik7b=P2=brgGcy_(*v`o?w%e+IK3MG>E3wLjmpMce*0_yY@ut8RKSU1` zTW@`D1IL;=Rsz8=^@18d~R{CFYg*ON5mgV!E zpMJmQwmGZo&2FpH!uHQmBOx)sH7^X?dH`Pm2P zfsKEv;IIqm@TZ15(zatng-i`q?Gy}gN?>naUvY>OoK3o5Dlv01HWTi(P4CfIAEzhe zKswcsJcT?gvT-qVrRm>X|0dx)pCnBg8u5c#{B!D~kJcG((d(2FE1s>`m3zqfHFj#$ zx6?Aq4?%lbX}f#6U;_;x9NXPhF3KirY8J~9#MM--EZNbElB+ui&=cy?`=M2=Wa+1h z07562-cg&dnSg;J&b!QW=}P)|B4z5P-=!QFt6^6Y^U$AAZU~K-u55OMi>+}_%Q3j1 zuW^&u)o3RMsGdSXi`vCxGvZp=X<<0BMwOr=^oJUI_GW?F=6=4OrefIGp%B3AR1Oa{ zv^*6weaS|Xg&mFBA<0tjfJ?>SJuc^XL~*T>ShVv{I{}NViqIu+2nfcEZtT+OMm;YO z;bDtPYdl~oa=LbrgLM`S^gp-uA(1QvAuk%p*iDqjl?xb9v;8vTWPW7uT8i{d+Zn2I z7RGy+RcG(i zuiS*G)yJCjJKMfC+9&ptU;ZP`dEMJ@E|!J+sjvJ@CQ@026|VTTY4Wi@;cG7}*lo_m zN-RhvQY@T}x27|zs~7sLmtai+H?U$tX2HXS+{oA+x(aGhu3o(D$ZW4mh1!3m`}lx0 zK4C!72Dm0trkoJ+pyP2@9`r+h7HBMxhu(Em7lR>&q3~yxvlcv<@hkx>2$dc(G+UZT zmg^G`kXF!8RZag25~)cNv!4q1s*N7wV-G`=}6h(-g#n9go@KNaN#T6JQ z$3N#0_$@``+kjuq71{~+Lw48ZOyYrDg19mc_LH-nEuY~zX^8wJf->JN?z|^QPsjlC z6nt;Ba9XFUlqt7@e^5q(WKVLD1LKaUjgY>I-xtyL$OdwJBB4Gc!L&I{;IM_D4&XwK zbYoysNImGGDFn86%t+FQJ@`GnW(Um-QD92rZ$3?8^>V^o9s85Ce>W1C(0zD)BT zg7pR&zvrqT|23m@A?x+Z5q-0Wk!}E;z#V*1H#EtGO35W0enxYy)Kjk)x*A(9$H89* zi9~rRqK++n_~6V#J}RTL7WOX0alJxGrdjgnn_;M&cD_XMoi0Qv5Zm zDYw}$9kw7u@V#2}x2X_@gQ}G=9$uAQ?J(t8h5FM(4bEhyGZ=clP2G=UF^72hv#UpE zJY0+!bqG=p30O*qK4Y5MrTbF*<0&gg3AKuSJ()GG_^a>ghZGT^rJaNz+m#!!*CUO- zMUDJ5nQK+z$UVxT@5dIXn>vRHFW&=>#v&2=BDK z%DHi5nZFB;i)__Fj3`6WICdhimz8X_q4U}DBypSMk8)TNDoI7SWSRyFXY|XAT3Z7) z$A`S=>K~Hk-ES~-!ravf_3((E=(f#!SIef{o`ECsiZj*iWs>h=O25nn&|`+gum!2) z?)N{I_!a*{!2p`rnZwdbE!0sbd=csnSBY;JVcok=dC9&ozK$*R$JIcy!)7Y2Q}B{0{!6)cxC2+_e46dg?3Tfnk* zh7FB{HtQVohMlRPP`+GsSqXC{gWxXKdWooDLd!DIsr~$km;YC_`u$lFD{SQ%qKhLxoAbR*Q1ynFL&u|4Tr~ObfbV@QfYa zL14`|NOz4#s+!%d_H$~ zyk(F_^)$-R$SIyTbSLzdhXedNob5&RFW389)j1S+=ZkWX>4I<^!f_r46Hb3@3FVaJ zBdpgP21ltfuwC~Wf@eUANCztln_xlD9Oa-(!X+DXOE@<%R#gi6O_wdDd z?}S{T1_tdvfeK#J0)|R}0He(3iyE5RvMImy@#iZ5yOcXX^L7L!y>;mwyBWv_2LqFl zaAmx?H^!;*K%D`6)GoCUfCm-MD9(ol$S#Uky+?NPKC&Sb7HW`##0G*S+-{8LDF^Fd zuhwnpq%uh~)1_rQqU~`3&<54Zi3kg)=4+z1;Pk1cw)?TY5P(#?-vsZ#t|u zqilMGtAzHuc`_1XD0ToaLB}va-h=5M8#^Rd_gMpAC`DZCNm?{J@r493lXYDHwemF z`a(x36S7Oj%?0p)(>34cFSn-Sp=@-h{RKVEEd}rpIWI0%{~#%Y(_D82(^dX4qw~BT zu>wUY!46GO=2TUvR1$XX-AhgwP9OGv-)$1H2SWh;#Z!qJ=&cYc54?yx#>YtXv@h_y9yS8jw0+}Dr{Oja2SzWaS@BC0XlLL$!R|N(vJd{fdV_ip9fGtGR?ZCpm zia>b30RVbT_m_QT`QlEja0Gg5JH53P+b#9zE8$O&VR11awT8+;y>DW$T%y zGIKpD;iKPQORRE^@APIz>KLWwz1c#>% z2BI`p9MasW=a(k?hr9luXCtvl9K=I{BU^aFPfbnv5GBB!=t|G_Z@p!RyOsL@?t||^ z%G7iimYu|?2ZkWgVH~})o1Ti&(g;7kCMs?rA%I~!X;zcNzN-0~p89Deo5l?qfnQc* zI=@EAb(X?N#iEp1P4toyiBIp_RO#NwjcuZgRQ{QNpO;D$YqlX)c`_Hi{!|M|fK-i5 z(@WH14jk;#d%tLMaSm^_*iuru4bESdD6*FBcw{HQyEpPyd2Vf#qjTW&TQ#^g(uNB~ ztLH9fdHh-9>_%7RCj|i#ZC0@go1y`FSaAsEHl0V>! z&R%DJSKqJ<&P)mwM)k^Fa3rh_*0T3f^PQF{M%RRO-0-cefN8r$T={eZw0vYx+^N^I zZSR@^6p;$6@Q7j6yi`&@I@PT)1~?~Ro9z|@iZc)%h-4rS+?mMjw@k$}B8?LPM1^W_ zFA2%zbk2x#rb0nq&G`@A+FR1;jVW8Ges#~$xa~0eOWz zn3ZW2QJE(-4$o7dK%XP)@yArpY}ys8?1L!;(sE-e$Xq{+-zDj_DxI7}8?$&dOlG%v5>M zzABcBTG@Jvs95Uzwb7Mn)YKbELrE&9*7)SWj8f!Xv*=e#eP146Ni)!+O6_LVIt=_M z07b8vG}Dajd)Z(k!x_94Bnn|HPTmJK=;?D$0-}Hi;8bd~{rTTX2Q7Q-&UX90;EV3d z=jB5a(3Hc=EUzOD-z9LZUPOj%jH>uvN;Rk}bV6t+ZNv~E{$*P_K-R+{T0Gyr_*#Zx z6}`D1R6qKM8_>6@#`&zh>ofX^?tjzu*UeMxTSsZ=1L8-z5bx>=Dd!$(lDc&HbQZ=? zL}uh6{3I7feW`4I#yU0 zyPP$O9f`M=tdt2ohc|+8-mq4&r)AFr;batahnRS|Ix6=V4Qwx5tfX?Uw1ad}ThdN> zU+~mxaraJ4H5q%MGo%jS1b{9*{mknOcgk;MAHaV&TP3K?;ThKV(@^DKGP}9joDNC@ zYjaI&<%qntb<0tVgT!l=$NnAzt|a53^=ZN1aXgksqla+5XO!1iz7gh61d8mX0j=s- zJLNAGBKkM+QtG!&{FfKhTDaAFVbY4`x#nh&-VT~m&oq9+9(-@6Oi3g@iGh{>FD&LV zEr=yg1$2#VKr@^ADpNb|{xJ=Gov!45)Npoom3@h>tWnK1=psJ!G}D^O99Qoi{z_QB znL;BRugH16=&WJsbK#{6>nWG{HHWwW#{Gv8RHAtNwL8QE)Q5@Ptw&58RfTX_d{aZX z5GXx$!%wVgBk`<>H=qpr!LOzXX@;HbP0FpNngm$xfjZ7q40l218i1W1`7gpD6)1Sz z)G=?XfX)2I!0yj-*UW#Pv*Ewb_>|qP9b=DKiF2#upf{j((*+?V11V#_g^VhbJZgOC z9;#@-iX19S1N8k3z#Ecq<|bm|a)!A7B`Xnqfv-zsH%YL>_3n*~iLIUnJm=iVZ)yO5 z0}^&={1)C5HjP3K2>Eue7Ip|icBbs#_&P4%V=xEq>1AR&z)<9xliht21IEwjSKqqv z8_yP0`Wq|o8;^YxnE~$Fp_S7~;!|qY8D?CsiP+5*1mq@gKS|}kj<{A1H=92-f%f(> z_hkE|x#@8cXvd^?Jz&u^~ZpxSsp*7$%U4VcsGtOOqOjvhAo){xEZs_Io z^G13nphgemvkO3fNEvzu&K$Grc`cq?%rg0)gISKzk*wbG1-bofd1t0f^CVeLrezt| zCUj!`qSA`0R!JUsKczTk2hv1sH+I;ZWyxyh=c^Cdn1$sgqGUXLru&udyC`?O*Z7## zlt(yR1Y5cXAQaPt*QQ|s;-f3{S6Y-@I&zY-NbY|6NVYi)WV6|2q5kwqT&HGPmVNwg zgT%NkP5#46cup1|`Bgi@?9%7p8;g$8d1z`OR_-bk?Y$TOPgy|1q^$STESw|e>WZi5 zhsfVdUwbX=YU3D#7ekioqq~dJC?4+t>mu?**8=A}3tcpoRawk_53&XXl6oP&SyC_S z&ilU4@)4fI3PwgemUxmW0G!dmyCFQ8CHN2$U9l*QL2GlVQ(K8Q~=>eTXEn5Z(BmW+-*A_V;h1Ou2EtT+9lYHjFhjTR# zC(9W%pV`wXjnty@CAV5mPSQ}Rz;nAZ8{6F9CCnYft_o<1SBOHdH}_f#ru?v7T&B&bu{gQxMTu*!9k557_ciJ2(N8I2 z(ySkG=`};W^E@;a%mv9^`27m$;0A zYu}(1r*@k$behlR6bidy;`X;Yu$kp9^Z9%stM=b+jdof4WdqBt)l`E4xT}d!a%F7f z04L8J1FJmF*UmMyH{sYfkV>bmBM23gx^@A2K{^C=QQ`XM4WdyNl|<8N@c9drxmDd( zbMQWONAew6AHdlrW=h5OEcWxeLNujKwF@;qxEvFeaYk5E_syR0_pe~}msc3a&*9sd z{{Aw`u(bIy6i5Q5$;aElqIfbm-zfaDtZlVD@wodQPH!U`7M z)48~Tdd*dL;4kn|E;tA(bmh>a=ZDw5a`_k7F%YjVV;ySF@xJl@EttA{Zc&}XzT@~= zzrxA>7KiY&G5jmP;aShCLg&?>nEnm%^9+M9-w4M?6)YHfNEuq_ET-C`Gdgyb&+eVz8VYGJnlEiz2f?VsyJ_^d?x z+e?oes!B3Re+WIl#Zk5A^0Bi}8_|wbEew4k_D%@j9Y2r%9L3U2)g_QikU}KxjVk1s zU&&KzbfyI2U_h!p^H4nbM|pKBHtH9F0QaT#Bo92665)g#pjMpit3Q5{?Aw36_2+M+ zeWqsxra5egaDXXc8{fLgIyWhh20WGG40YInX>$^w1=b=LUvX@^C1Cjo7I>8EU1vt| zBVpmg2D4|K_^7gWvs1UOt}W(!%fX7L*GhIKKnts9d<7t!Yanq4c!ZdN7cM8GViNaH zkH_Zt`WuyTuphi?A|h{91r*9=;?~;XLP6TVcJK0xEtSW@C6sJ&ldKerlJHgsLqE1S zig8?`d{sPM`@*ZJHqUnp-DnM-PII$>#$5QIKc|ZRCFiBnM7t!1)scK_K_S<@87AUx ztQZFqUBPxS;3*%~7bEr1_82D-gHs^mDfZn6#c;zb$7dvQrY)QO8@7>4-l#IGf@T_e zSu!XV`0P=IL0*w-iS5?q&6^8Is)~$xPw=tDhh6_w3{p2-FC zNai`$!(b@~4pw@TX#$4jo2CN-55fK7j&hg^r-Yc}rp@6DD!NJBV{t@DXQ^SbC+pP0 zfws=0b70HSlJIA{X=DQ6z}3(tcGcX(K};Xx2V=qlNW)= zH9T=9-v1dXxfFZY4-I-%gO?wQw&jF;OTJEeI7+8dVNQ3@BDRTr8kh#M9qdUjxb4(l zidtz(dQy3ClsLZ!GVe$Jccnt$PfdyK8jx$*NcO0h#Ll24M~aheDiNR~N59O|6K8$U z?uZax3fEe@=bw@CS+_GF5VC(g#O=q}h zsFG&f+AC?_n^ewsv@U3Oxr9jyT&tm0-Cp#P@1_ibVZ7}n0B|D(lyH=I3MZ-iO?Hv9 z<-Nt4JpB21^UzRdYP1!D?x&Q{^v4(yb%Iq^@Bc}(8jj{`%n~RN004YIgTGb-;|NA* z`RUzXulR^g*T8z=Wn0n)9ge_(|3*NYxDgivirj9F{Lg5mZ|k!NreT0RnB}P=lA}8qMN000000000A^_caoe7e~{X9pMM0=&%fsV@VH zZO8^jew&^~%K!@MjFh;{mFPQW!pr!ng6?Lhkbf5BfY8WIN6@Us^&&S0>ev99 zS&o4H;^{#mkN`OH-|a81l6e+wYj(n?cl+G@btyMV2JPYS-3R%ps? zLAwWqBrQ{7T0j5*000000007FMpa!Iao|>}2Mb>c#^s%pZn+D8|9|AF)hF6WBJ+t^ z4J=v9kSimE!fX*SGOJE2b4SLqQ~&?~000000CLbB+xrAaN1wq7Z`~jenrhi$(-X?C(bL_2p_5io=_qg6HTZe{7On3ZBAOh zy53Q4IKhX{`B+0gE8=Iw>k-eWid}ZYCLj!kj1!EQQpzEL#99t?-5)a$6RM>;N<#>V zkQV%Nzm_HNix&6v1l9vk&Bj-g*sa+N#mh~RB$*(K|08-`W-hf$ZzU;lTH0VEB0-kD#9 zsj*k)NFnX=>R9{_haC#+SGc6dC~@7tUU}N%4>WPA>80?T7b%`tQ!qD6u)LBsiC+h_ z(7yYZ!`Jn<1;I^sYy8)FK3}s&gRvHZ#peO~#Lti0P8<0na+`~ZftC=%@XEHI zJpVy$6RBZ2q7csk0JwaDaKoPfRW$#4OF$k5e_pWg7>5E`lh#8CY+(`E^6WHg)mdhl zap(ZdbS`BWO7qDICKMZHEKn{Ni5?;!zhdfN24ag&4ntf*eEw*y*C;oFdJ-kiN{aqc zhu{3YX7M6@-gkcy)%)}QH5~0m!_^4o5&i*2t4Jnbh+|%k#%LJPQ%ewM-gk#N@W;$a zJDEZ}*tK|;@UX=#IZn{{($p@EMBA`)xhZ_S-ZYY>)GbI-!9(Ht5Y-oceP2ACTBJEv zVS42&{|gh<^M*44{oHDZu~LtOu)cSJelav4+c{{b+BFUnXnP4j3Q%qgsj;3ceAtx6 zpmPf25$U77DD*uBOxxngHz_%l=8l-pGnWQ|1*UxxfR5OU*b6RexAeK%`8FS)I==A# zXEub#(P*L}^tTBRh+nMFYX|2JFT zv_JOr(ffMH{h{CO+H30Q+rF-kKW?D@-J!m%bH1&h{krF)`*r92yBfZr>+0%i`nub` zp-%d?R{FYt@2jO7>eS!cyx+HUe{Vs*w{!GAw_<w?? zpVyb|*3a9&SJk|C)zLrg-;nC~@+|f0uF86vdN7Y@40Gj|-WSu%(yGl_O-bTv*&L+> zc4!B~Ry+1ZMT2;g7!)Mgu&tXM_g))w8<+h+?Wj+sDwL?uIGZUrbNgzD2s;@9_lFqvUj6Us1oLA1aYxOul^I~q z{-VZQ-lx_5atmi|{lrGg`*hp&ts4E+`w)WOF>WsczB>^M11tNPKX5bDT6&V@=~l}wcZ@i!&TS&6I(C(=&+k68$i211x7^y21|8@sGJ z@;a6|Hh(3IVMP-Vls7)6fSrfp+`EouEoXQDp`YM zKz2tY*AARoC-odXhuTcJS(dM-XSu_VMw3UNb%Q5PP;jF zR2Dn~bTm9eX+(Y^wh=Y)KCR?QD@;LfG)$qFr?&(KRX5!fI&cYXuc3cD>KVj0_YFm2 z&}x-8_?Svp`x6^A^lD`)6aaaLHf1eM8EgMoraM*HLH+>QqTn_k!nXL04;jTVZOgEgFv(MJ}y(_<9tojimxX`nmv)}3RxyoK=4F-V8R6#8V)d|Xpt`PG77 z4~PBRcV=DK8{!E-&mXFB#@I1&hb=KJoghU|Id!~E_K<#lNhr1sC5$@z1Wt7aYA7L( zDMsM==s>B{hTj>yXThLx1nG`ER7Za-=x;#FPl;p$J$Fv>h31D9a$Vb)OODMJD+C&LtPuG5LmDc3*9tR?Gg_GgA z0scve{is$^40%Yhz#B!csY~S#k{%0)L_aXaCL(V`oDPj3q=uaYIQW$y($H6 zL-L3k3JaiF1f4$bgJ`lN(j*NG4hnUU5M}~PTS5#%i|ZW^eg(JaZ7_rP&LtF->RB*W zM+|O*Wx(%?^S-p9Giia%G2k^>6bbc`5XjIA*W}^OvSi&%&vC8D8huttDGF_9bWcOX z^@mhf0;w)LcJPZ^#2fo_@q}7)9}f>c(Bz(1ECPA7P38F3Mjq>B##H5J8O6g|h6~4p^G6$RYvhXs!~P3U&%7#GYNO>NG86YZA_D z)Epv}&N#O>_ve_#PM9w1x)KuiL*+{fmu^O#KzaOy{)i3F^5Q*#!Y|+^j-*aV25mk?hXxmSiP8(_dMa8p9#s!BV0X={rl7Mods3+7vgTrn#mKBJA4kJChHm$TG~O%Ta+K?O-}2nh5n!tQPtBPtaI; z*}^LDLhjxAA*zf9Dsn&}PCW>?MwLBf1T4M!T3YrZqiwg4Zj(&eury;T{=_tQXvCkR z4Yy0kdv9g`K^c8T!rNDLqTCtbR2Q2YJ z!Oj6Trucc=elYiJBC;Wc3>)IP_2+wb>AsUtd~Dno@h*pP28B3jGMKF_@X6woCG{3V z4sH~X0%|9NM)aj940%uPPU*&k)Z|nEB)$+e{1NVe>@|1%{?8-1RBBH5u>TGgOjdMq zwu*7G+1Kf(aqCdQQ1byj5yScjxRs+I}Y}I zQMSQIqCV2EHf3+MPNveP=tH{imxZW!YI-9vWCNEZ=2*v-$7ms9P_&-G!{HzM|6tsu zRXq#OdqcS5h>mj3Jgu`hfM$H7((_IR z-#GcY`?&*d!)*z@1JzB*Yr+M5C+I&*$fsR@ZNFs|No)hTg&=f4VqLmq!S+0EeWaNU zs+x7G4_Yova^XxCQe(dtRbz5#b=2efhuSe^k?HATiPG9mWw2j4}MWpmr$?wNe!~9iVb<2$_ml>1rzf)#$mIv)owTR<#bC46i34z zuf3OQM+ZFUzI1*pbk8Ci8{(?royvpNF4t&Y8Yjm(h%{i`s2QVF3Ww47P2eHZ7IUtp z*=`{V(|?&}lmQ{xdgr|kq6Q?)om73@c)xu68!Oc>;8}6Y>V0fb44nAS^4$026^y^2 zpPSN$S|T~_2I#0tPm@Ag3w>!?z74c5fQ^dtndM#xoN=}9GpqrL2vJ#oQT4WC_qnEC` z82jV_1N-6Vyv)GXegV-j*cQz^C0(dU!SG6^p}T;GDa+ORfQgB!F7B(P{QM>zMgA7D zdpcKELP@YRgaI%etPNOAlr4evf}>HPyr?1Bi+~q|`*vf|ywE~fuPj=wcR72HFW9cA zig6n{&Ix9Hu|jDDyH0Uo(K48CKT4+%%^r8jrVb_CXxVHp|eWXm_(EI>G zC>YY+?OCI0VMS)@;Tf2kSoVVPb^^ugOqc9fd+g@oB`!ezEbBQ{>mRX)A*boz-)HeU~b+<$H zLW2Uw;Uu_4zx%ft`KYAWC80QJaeH5m7{7!qNp>=POB>-BFqsB{U+AIp5)OYnT$x}Q@|0UT`R;8$DbCi--TQ3VKPkIdR)g{L-%@)! zu#UPnaQ=ku;(SFdw&Ntut!@}EMNw1F;Ku#BPbR=A^%MqYWXA=F3qfgvN=$OmF}WlT z-6t_xQEY@1avTJ6M>+ZI3)P!svQ8d9rs(n!2R~(J6Pre-dK>=eV*kx=KEC3S;Ez9@+}3Gjm^}$suk-L z4h}#y?rx0eE)-?j#6bNw#tH1NYC{*Jb#6BwJ;MFpj*51xrgTMMh!WI%p(wJKLb~k$ zsKcWVc(>dI5Lfw=179Ls8(Eb?Qk1V-5pD&=<&X|hOTNFx8~-B{;YLwv@jY5%CWawI zGP=a1`Hw+G*qlW|#LwuFrZt;MRtuVaZP#?RqTS{toY60gH*;hsymRURfuyz{YiH{c zzVFZ8=lw^!wpuH8wfh+z7z~Mai)&gZ%SKcix(E}14#ZXsXiD8=-5~!@2B)Pn?z=@x z;$QBDh*gcVvt-;I%owg3}nnPFM zt-}4vBFLbeaFW23t)=`hWod%1!)=3)Ya-LDmtajbtAp!9toTYVCv}Nwd`a&uN4p+O zy-+$TpkKfG`Fr_jD#dp}KnI7y@~?dfcz|6+f=@vd*+pDC2z3t|TXmSb*T76r>uq$+ z=T;quQH#*O`xZm$iV*X(~9EnExrzV0|z!VGbFvPPRib$mgAuqO5RB zEf#CK0(nlAmouv|#L-UUZ1$v2@a1)qT;?B{ZJnz?z>)rtuz78Zp~x@t7SN!-u{FRy z@P9m-;)uTXpIkKep_L9Im=ZVGep^bkVMWo!!Z&3-zt3Lg@=k)_o_RJj;}5C}{re_0 z!D_=^rrkQhTkp#o3|_%}U2(~34TtV?67wVhP4qX0wkZ;mMn$)cgrSwh%+DIm6 zZI`R4V~+a0BLHJ@u!JH47L2r6_D$q{dB&%WYu_Q3Rki!~z49I3Lo>X6}zz@Bk zc{Jqv<&xq@$f?7nA)^P6v0)}^;U&0$AICO=%icKUiksJ7_~SVll%!&@fg4<7PWLX& z9%4TBS}7Sid!b-wZq|bi@wA-=m1cnBiAT`)i$?eo0$+-cG$wcvfRJw}QrIj@?i>dZ z`SSqzO*mmBBgOzUm-LqNM!~(nj045K8q$G>{eYvMhw=q(%-kB+2#d)Z!Rfft3tl$I zV_rE0r>9Z?q2WU#vk=+-kK&Zo)-L5CzcZN^ zTFQ5Qol2()gE*CYgiPCufIsj;sMh+B+9j3DU6XKv`+lKjlH+zhmx*R)9d!z7Fop$V z8etzcKm=D!@)P}VTpFN9tPKO!UIoVrSXv4*6c5T-`}tiM3}V%Z)w)85q=NrzVv(8z zqlw)sF8M!uL=H9wKzy^*J1*$qEL3RDZHL0>x=RLqgq<9_7+wxNG^!7~6zs*+vM zOF9RnME=1oVqj2F7;$dAk$glfI|Sac`*X&OWsrh}lS(poHPACT@~^n;REvc{Pr$cY zTm7>bOdojK6|`{e zu-HqC-?LV3BGYC=XOh6kV_anWRR$gHqp>*om+<=b{U9I`r7zsCM7FPBN3 zrs=LlXZMb*Kwj{DlsN%bhNNavI6U;?g@Wr=8FFW)cbK|)K`fgV@e3Z2T`5bG56te= zUSE)Lmp-j}s1_l27f7HL$nnDD+r+U}=ppP(c-!Q)V8p)sV@!SLv#PcGa)3pyFhgs8 zNHakxJb(%HgPnE%3Q(*@ivL+k>{6Ufklg2uYeMs!n4I*|s+vhi9ay_RQ`Y;Wa26|D z-ktpOp1bW7Ro!*?USolN7(DfJTp8Ayot&d>aW@3do7N%?<7Np$;S-wov_Yd7q>#|f_PS_r_hc`A^FdzZQ_W&`q0-XbddcD7WS@*36j z(7javvjc)z*ZoRFui8f*6NnUwjTX+Ji!R)LChR{sDYXD>4CGAYQ%j4iG2xgMcHg&Y zx;?l9VEM21Q~HjCR=TqD8W@H_lE8t9^-*GM0D%1@=M>2WH0;i+fQm3nkrZic9D^9N zb?vz-=SwoTP_@+@`$RYfQuEB9jQ=6$+8rZ#(ejyYW}S+3{XK!>fQy;0n$;oZk&XP` zd$1Be;u}oDIW-;DeQ42zaCJbaXfnNp@D>Y{GHKz&U&`th0b5Ff0!_Zx#6VYs^IH~& zvFJ63*r(eIsufeDk^#D1gbKb{5n9%$gf%kk2K>O~*_9YfM;*dva>E9G6Qltr|2(k? zm@xjsc|n`pR$Gsa%%%NpIv(764GGG!usI!Q3Sl$mj%+1akl~L4QZo(!hV0JHL`wk?~Zq><0Cl2c~g6+H(g2W&_`j+-S*3Cvc??t{U}gPi(HnkNDiW%9a-*7MnW(orRY4 zcK!AmF2DVeTR)4NsOkLiO&Qq5x;PjlruP^Ds%6Y=G4w{(8>VRz0*UsNQ{-M2?RQ_| zC3B+XrkSxkvYh+a;qVp`Ziw@#sQ+mEQ60tA4<4AZP#RU6Bw%P^6*6go;QChc>jRAY)Yq2IRu1V>xhbG$uJsaIGiy(8~RiWid+ieNR!= zKvhfLG<~*F1!`(5wUv^0Rz+}P(bi$sOK|gHxXXe#^~BlYfd*YcRq1vQiUGGT*T?4< zLMk2{wljo=ErR;h;6-T)NY_fc-w=W2tvdwxm(?uVIJO!}1|Hze&WMJ_8_;Gt-kWv9 z#t7S{4zCZkWT(qX(PJfZ4F00xPTF^0>iZH_W`+S>?w9PMu^k$adBfcWBCDq_f9;N9 z##cTdV5f6Tsq;Sf*V;w7+wLzbU<#<1yI@KIMU|EkLs#?6Dnl19;BE&}ZL1Ihy4-cquX z89JqXC^8^g@0(kW0rF{*aYU%;e(rh2FS#XhO|@RY23M^@RJ$74tVXp;q!IUS$Sank z_bXHh!~5|^YNg1;PA6UEEHEV&ELVeDBw3SO+>Jg2mjE@INqQ^1Ok?Fn?tT6B!N?ry z_I1#!$B{S(kA-_DzJEQrc3uq(Z}r=3)zvt_P2cDes$ob>8+;!B;wUUV5ytU7K59a>iUcSc91%Q0M8=(JuGZ(*Y|9-59VoO!nJ2raC$_E}p zQ7kM;G$RrpHH3=BwV{;fcXcgsqj)c2d$e49CP=7<%Cq5BrBLYWa9^dX&u5rcv6O0| zh;4Oc)$Y88X#|)dSN$k_c}5`V?|4bjjVEk?GrA^xfTUU8<1;<$HTUK6yf`X=>&Ltt z6K-D$JdF(*(h{%5@sn!_8AeVpgwW!dR}svjYxlNfM67Oz7^K2mNJY^X7%Bp9_ zY*-(Z5wxtG&`CKw&66*mf;yQheSM5^w3{1gQj`u^@zSSjW?gr36hb9fF|;JAt|?0a zUlQ^7F4WvnxE9WxLVt35b+th7Z-2c>r${-va5gXu(H z=!q>oC;q4qr$d4Bqiwzu&)P72bJU3PG(a6(@NP{NSDAuJ#*_SNG*8^dgjwH%T+Army= zC5A`MBR@S+@d1UCCu?PCY@1Tcz>DvNSN~O-33Ss)-a|>6hM@Hy_{KIv_XxCnp~*`QEDG?;cM5(N*fkzZQZvL0yKP2Q*F z4dN#Mb-+bcx>%)>ak^@+doSzE(I5>FufuQIf|DN*iVr%dPd%h}6uwhW`89jk*2Q#} zJ|RS7ECA&wF^g_JPy*%tLy&U?mU&&XaSz?=y0YE3HDN$rO{dsuX$V1QhSAIFFEgA? z*cPkkwG$*Z*M!XQ9$Y@ZqO{g=j=0Q31inHtj|ca3%wq85x@7qp-;iucamUF5Xh57{$X2`dF!uz4h@=%})w+q}(-tsqQv~|_Vn2qMkd-;YwwE8|=Jj4{yKpWN=C0_rj#APWBoo8e z>%5vxkp(obGT5V)e`?fyZa2d*MRla7jP2Nu^>{eRXyfkIt?fk!2q<_tOq6yFGx2pU z*K4SohJ_>4-wu;IYoUr{MHtrVnfjSqns%0om5UxfDR#EyEZ9kIiDDm?g5y)z6_%+|eK#-j9X7;Yh$ z`B$n}EfuXbJHd|!6|MjzSzgFlz-)G%RV!*$A=o47l15ut9Ly`F67t2zpVrs=bN@`Z zB{#`BDCq5>_Z!0@HZbBZjHNCMnJD61*NnTrp zp$2Y7ME82^^z}`cl5B_9>ItW>2^t%bZqdpCQDV`oH$8zPO8fW9SFsdapK_5p#ATeu z8IhD?D7+w9pL|QL2Y&d@1Eh5`MY)j?I$FPC*yXz6)Qrh45HPs>cPCV~d>J?*LWEwh z(2L%KBu7SzuA)ZVY(Jp)slk55=2s%L(EgNk{*Ip2mu<868mtm#a(q)cmCvFbNOO@8 zSuneyv#UM8J$%;?8XS${x|%5dKmD;uePPe{fhKK@aFyyDov7H-=v-|`V&MsqmR(;% z*#OXp%Yrv8MmsS16dMi9HHXzU^nq@{I=LyoqR6B0GuZw~^Th8tq<*2tJ-%Q2-_F-> zGtY>5IR628HBh2NCsm9g0qV7>zO}p<`FE;Qe_x$Ca21M(V8`x1<8b zBhee@e0N!Lkwym$%D-?T-xCiZBhk;61R}~Du;vAE(~m%ks-|;+{$$5H`ZZd;n(%2$ z+)6vDrA7I2JmWC`YOwyOxreAaeV|bpqTN|V06exgaE|bw<--h+0K}Gu`w9Ncb|+p{(u=y&t1CDuV_{+%{u=w5*s^gWFd|(F4vkm63GP}|j8muIYxeXkGVQugeEfD` zgx@qVV8_+cd6I*)UQqVVc3lql_vQYtr?%_jSf&Ki&uee+gT(Re@H&k~1Q=i(hH0V5 z*}tb5`QP7~6G1r6eK6H>A|@DK?kwNZN}uB+6x|kb*`aLpwsPNA@GKV@a-#@Y4u>5QS3{VWK1?a(xKKm1Z&a;LR7%wG`c%0o zVq^F;#Hx>Y5p~Aq@;kCf%QB_s4qyRQqP2v>G0v2 zM`OPO=8Smw2d1-~($1og2-%NS;W2c42Su)nFoi|9t1N(tsQ~X-xh)0x7}D?BkK<1iyBv&~d<+2HVlA+F9=_qofueHhTjhK;rwyRw-sxOJQDg?;&f}gH)wPaZ&xl zX+OJJh6Ib1Ck~O!GDC%@Ucn)<##gw@Lw5ZN4%TuoZe3CWooj{ze$*YV(YU28y}&Mo z3A-x?85`OellkYKs2y-{KluC@hd#l6?Yhf2v8}hTBu%mh&(}Yrg9ogO==#1KadI{h zTFa|Tjj5H8>8y5q?xVXaE(>L(On5gSe|2bzduDxUG>hCyGVO{8?F3H~zhrlJ6pbn} zAVM1ac*Al@aA)9J+#JPR?iroc#WCtJO&B3(tLg7G?*X%gWk>BoV+LHL_AFX(D6!^I zrtmN11@f+~FwSdZ0RdQtz6ww8a@$VI5>;}M2N!Rwcg5CniY$%#`GuAR0Y!Z9g9U;^ zWoq~(bIc`r8zy$>4fL?k_O4sX(JO)gbA1wD7Rbs z@r^VeFia#s&wi$ps}OcvMlLV2+T5o4{{&EsIQ|E|r|ZV!8QOAmha@VcnWsTBcCNch zs3}u~Ql*GU-R*1@y@)q<#M0)6v1!afYj5J1AUNZQEoGEe3%sY#JXb3Z^IPBE!!14R z1?HUi>S7IVZCy;0LhT!^Fb!bje2F!;+6D^iS5# zD^O$qf^5`8$R9;(M%b5;A45KW&94vaPTaE#F4*g8JYQql#5T=_3*BH)+o>BFeqJQM zt@U~$Kd!D?%e4M+nSr;dE}7bD{sl_MholDbV{=%{Yy2{LwQ?8h_8OUC7zH_p846X! zTN+z}auKOmeTCcS`+%_Q+0oG5(E4_{ZipfVmpVuJJgS#1EwAO`YGGoPHOaevGH}K@ zJp|^knp-o+bx9j<*u&~KelHh5<^{Oah$gwwnPaF9Yi?`_+<&|N@%g0P&m53yAV=1r z-aM^NH3i-p7B=XKw8!fNnw&PH+j`egslajCrayKF?o=A58B`KhRKlq6V2^6}%JmQs z^{MyuWE4OAW(y$@_DOQxhp5tt#wslgMjS#FtODO@!GftICA|0IyoYB>n*v{UTj1MJ zA-_imzpo}bDD7zpXl*ntH+F?g@#6@N0(@_!e-%BWDjAcTu;quowup#)4h2+`U_clw zj<>w@Ni#YY!gss7$#@)ABEV$+T3(B5?ur~h&!@O0pBq~a2l+^wCYYjhR(59#85gJ? zVb1`BI6SBiJ}-64Imnv>Y=+Iy`0Em9%A|fujT)T9qTAioRUK>$o3r31?n0M>Pbdd-Wa)e*2iX#?XT?Wwxo;8-e(=rwY0>&V?o`|^=>suvW~xl#aaCO8fn z2k_}yQMn+OaC#NES6p)Rpa7~mBL(p?+V)|M%S$lBAka($ala6*Srq*kL0}UAATW@S zN#G49j5V?yHuLLArUj^b1cn%JL$N7D5=s9pU6H{WVgG6vd`$owFc|M7J&V)~~-HySvV!E2jJ2Odtfr=Ez zL_Fst2{OZvmdgrOnV2TJZnMQYQ%pXc?MF;~WurS{1Ah>A&;DY&`f^i4it=>rM6h|~ zZsNRyMlPu4RKo*HA(Z+{FoX7PFNf#)=eu)-f)M>R1qy{m^G+IZkqRFcU8Pl4fJ`<} zKmnQW6Wr7XjI*(JW9wIw%7ATQK3GOVPB!5Geb6&31&|HI>sETt_l9e?+s8%c53Hrk z82-)epu-t5f)jZuEDqs0rT(yCHTZuq;xNfroE>_A*s0^Q_!?N65suPQd4oZ!72H#wd4 z`w7I#udN5|!b|f;^q>H$IwJIpG8zeeq7#cmT+-4WF=#`-w?p+501E(s0AV2?QcIL% z2VIkixfkULRip$nItz0Ii3&`p1({HPIbO_RvR%+z@05rCe+P5IG!`=f)a(SQjQ>nu z^BfTK-`Qp1G_%`z(nJ4za}6B~{22}n)1ZLw1}-h@FqIqM$iU`Migu516l@7U{W<(D zk-AG~^soX(PY-JNVDJvXE(#MxwS+TYE=N~d@C;upi9bf~OA`NH=v1srf`nD4q#M3q zA4=F5KQ!sN=UOss=Ouu)VrZM=do%buULlu)z?n9Wq`dc;mx)f(xC}l`QI+l>IQou>Rr7r~zEob9#0|IMSJG?-K{?!f#9ZK5 zNXdb`T5Bl}iIajVzn>CPA+?bzjfk$k^|7(ObJIu$^_JT^W=fmV!`!S53bq&7_~N&# zns(n}&2xTHC+jwXvo7OlhW+l2U2Mc$CmOu_U*@1BaaYJ~4U6%xBu!_W?6GZ_*3@sp zwuqd`;Rq>`je1LN-@BQs9Yb1zV~a5i1XBaFqu4O#{!r{a(dN#jO`silV00?7HUecGdtACHoL3 zRl1)qMHEv~lR@@ST7}zF@AW;nLv}2JsTempxDbeGS z;w?3Rp?;!N0(;0LD^A>scXx4*2ou(QI`%nG?MyYkaUXBGr}^i9yz*3fkv)HmEpcC1 zHKJ|7l-YCOpFTK99Lza9`EoRw-kc$Nv(x?!L}j^>F5YitjqWF++7YJ77ym)r1X?QH zYqp8qe2EGmHodN)hfOXAjzTb3h{tvIxu$E3gwU2>?L}gAh`yYD)KyQ-W&06|I3GdE zmmG7j1O!sDt`3cY1Qd>pReY6@CRC;TaS7M`h@6_BYvAR0XXbSRZ+GWGG`!K@-z;T% z)&Q|b2Eh~-EC?e`zoTq=iJ+xUV&y*V<}e5w%h4%Vf`CA1t(5G{%_CJ@feQk?6rs$N zc(-d1$sbsgHJ}>U;~ZnhNEHJWJdr&zt6FtjF!`Pu0R_Hr8YI$35^tI{?F!xD12Te5 z8h5`sn4jY}UAhw71WuNe72aGg_V`6z-~VTMUV=c5pP4VNi7vkrML1n-OrvfbDlR#9y~_b2(fEkdEmX)CSr>S1 zH0+mKDFkZmx>P^-HY#JM#}s3rfR1k?FcJ8X>{>vc1?nc(7upd}tDdGva)_Mc(F81n zrZQwXu89f=mAil-D11OS6h-1741lI-c71xd)Ci1={5M@)E509E94Nc8 z_2tQFP?W)iaAV}gYoL}*zLEH(_Q*4|501sb7=Lm?E`Cbqo(HE!CNp-e59(nA73V9sDPC~teI z)m8>qQ`mV&uPz<(Iohg44sTcj_d>m1V-!nLy>D z|1MttOE@e$&O+`f(HrRz=}~A)7hHi@Q-DB_4n$+u&&sln7LR~^s`a0y9I)GGRTZd# zw^AxBcnG*g`VG44f`51Hw=6_*u?)X)e6F)EpVOaj3Z9}EzFsTGl#EAI)%u0!aCX1f zs49)l;`=xqud!Ccz{fuv{5?varCm-czlyGL3Il9sNjUh{Jc^er|njL_a3xy&`zU3 zdhUeZ9y(krWV(C4Ko7jQEpq_0(`A`6jM^cZaE~qT4$5b1v5kBYbe6kr7rQ;_>b&>l zV$Fs8^HHR5(4#gLV<;#m5pwb_x?)QVA!L0|G;`}BXi4r?3l6*#B3T~i`cTdm5(|+dsK}nM4dPU)(_^{yB))p=88Q~OXpagv8`0mHVO)p(&(!FXTa!9=nTmgZ(4xc=7!c_+OoTEw?G?@`jJn= zI=C)G@;%yPBhdo}t~VZ?${eP=C;(4HTGq-yXlkUtM$nbvvmeOl-NXtqrkeavRLpb| z{VNYBs+l_)t#zaOK*&{|GbFpOsdP{zuK?45Vo-Fs5FUL@CVmFKC<^IIX*-^>&=MVa zt1>BwL_F%R{*(MTtEx^_U8D}4=22c(N<|k*-5iZdZ5M~3(4pw3e|x9CbHpzse4@H0 zBi3o638hD*@#ZBN40Ai%T98olt+XA{<}Vdt6u1HthX*j+uxyUy2zBM53Ar6((Cj^} zu+N==$FADy?n0!koxY(vbZq)+j5iiiS2?i@^YV^0OS!yVKaadqk`G>+aL8h-$o%=m zIA;&+!xP9>2zJtw+|C4vgtqPgk%7qLPLLI}RU@@jxibl{mlN|3GY&aEdUE6)%!oSu z`W*)cKC-6i-0vB22S~J-$`+jUoNOlB+*Sz6G6yoXHd&1-q{`UvfRK6^xdYW;Ow+Xm zkHGh`hf6$)vnmfiK>KzRkIC9|6xq+MpVe7qDD%Mx+>?5y_t1>soLGr|xH65|_yFY`hwR%Ie@ zHV}_sS4)!Qh-)Y1fP&QaTmxQT@A>)Smw1(yI853ROFhcGab_Pdf7@$iOh?p2RdtorrT zblpj9VqFi284@&gjTCOtmZih6eQ)X_wju3>OeF0r4k@?An;*RHZE;W6clb=c+|Nr6 zQ|oJal8Wf+ADU!X)b5!A8ZQLaPZpn&tGTpJMx?(*ub-l`SZC_9F44BNCRVl@7nfa5s=-4bM#X3o4^Y^z( z%V_qBuW{r&Z(j!UU=rVr+;K+9#;?4*r9<`}4v?n}3kzDx;_^@IFk;GkU$l&I=nKF$ipmV(CV~c(e zWnOt8s5_?n!j{(gv77lubz#NFwVK7F2_#WrBjnFoeUZs<62~Eck!Jrw%=7QgH4JI4 z&p5xVHH#`x2lv|P&q}gcF%`wP`zv${O1*O?Yx+f)EJ}0T2OWwIz&}0T{NS3q-mn<} z7qMz(SK0#Nw~Qqim4lErl#1bkQoy?rav>NC@N{{Emqf#}UudTN3PE9WAlHP2@b~=i~e^ho161aFMV9eyCrWV7=quD5^l{ zM*W%PLavx@NGK2Mq4qY232+&ZK*c0?KXZP$S~bpRVtwJ!WAlbF{>Nl}`UDedc^(K`7GBIte6LM;XC?a2z8vUWXY>1-_)qZqgBUQ7zoXZ%moWbfJYl#lMU^*h zEfW>XVU{G&E^JqZtYacj=1d0GkT9}b*o(P0#HQQqDWtVKooe``>$B4p5_`3d0m@Dv z_9L|l$r!xpS9q?m0X(jV>s8`iHn+VTM{IVCe+vW5z@1$lAIQ1XbyJz~q$sSkHF_$; z4^WBXHJj!fV5)t>gy3V$$d@xVn?TF0bV8_$!6js(uaLVK1QX2frvV3Ru&(P#{3pfW zAV}#AWeCoXG_DlZ*!QS$)`(c|u*3obu0e#UzQQ>H;%zPR??)vqu1BOQ^^4BSaFh-^ z@cux=5(46cMju3%B9Ww6ptiZR-BfFaaou`CNC>@P$p}&Pu;ZXuPoA7vnno~Ru2`dr z0@znP{=3OQsec6 z=g{ELnam=0DOQtA3HpuyH(AnT51@n^V|4HWIc?6LpX$oIx*m+eBv7v zKktccS;d9mm+NyBpjUS;%$ENLV(`qyq)0U!gdhAwv0QO{-4Q9fnvqkEKX_}<+Lt!Z zzV^P0z3vT=>E6Jt_Q@U=WW~Hr7~0+J2fFyW4KlV}+fj6Aw)JpV2RFl5rP-}u1^)3x zuEI<0BN(LP-O5H7B@%A{dL3*aGz6He&H@A91TZb|x=a zXF)@IP!Do3aJ~)SMQxd|7!YPe^Ilf%p!Kp5%pT#;=(z|IhVO4;$F1 zkTg0ucq2UF=4qRLk&5vOB8fiJ!N{~z4XL*?U+Wtm6eqeW^vHPD)j9=~Cu zHN@nN$JdPP-hdCqv{_5?Ra+aZMdOiPk$9c{>|JTE)2f>ZR(VCz2lt5G#PUv8TQ|HX zaH54o6AhrDTom3rd>vW~8e3GKhU2VAHrr4_e4$Us6ybhqk-;-&ys&$)scm~=YWPj8~goxjSgFnM857a6LT8BRt z^%Ml=4v7Ikr-6awlj~yU-lT!l+Z<#7`#p3N0(Hf?a##!=)neyj)M+KXCVyG|Ma)l~ zXDtHBfcH7bQ5xcEs-^zba*w>ZiA}4!U5s6b(KDAG^o8knjmB^uqre>kZgsNGi+jl8 zpjGL|0jes*XlC&!G|?C>xz+B{CoczkJ||_$>L7ShI4)uJq>ZexoNTD>{qQ;YNRsHx zARXJQIhqMJcI40pKGk=z`wNmpeQQW|T zh7a+jye+stZ$lKpv!4`qwp}|zAm^#m%C7~?1<84;u@Q@bhSS}X8tpvWZ5kxOGCIT- z=OeLxrv51{e`86EiO*v;IWR^@YT-T9IHQ=C@YB13pg&mlzpZ4dz&6%Q%rxN(?rmqR zLva}-giQvWK++pL^+4IlIjQOeNZMY#Y5lr>`J}HYnPz(!0A!vD;RbkxEm@3Gti~co(xFossYbFf87SY+`ajcmfC=SNg|#giz$@0R zr)RS74>l!hn}KnV5=$U`D*Lo>`7d-cPCz$fkblv?HJadC8(V|yoFe>&rYPs>(cq&i z$%yq5CkHzj159{?DsqATAYOJVZ)_TSY1Fns1|(xUs>>d05BIp^;~;>tC~|>87y(e; z@?Tn-a^L2J1kU0Yy+3n>JYbF%ha#Yz)5h&t^4XzmeGSg&%){0?T!c*t9!@Lvm%a8?Z5?fkoM?et?PdT|sj5xpGdr%02=*%LSqg{jmqj22JQn5GEC3O}v zw~FifD37ZwG~6KL)e{Au%J4=gj>J{po=>GArQ;VU8{@h&30p^=6pUQO38{gk|5|=p zuL8@j)D2jHyG;`8=fyaa*Tvf3`s`{`xlCT0Z1P2s3sheb3Kb4MM+hY)(QlnaeOYOEW?Yq-;538sbk;2&aZcZ9TTK5k%Z4Y0!G1X4EYNr=oQrbIang;}hWH zGi{!!?Ux+lE2RXx3``xFErtXkLQ}W63s^!Y_4yE0XHKz+bGrkAod~7BUJC1NQL6)@jSccq=}FCP{HmjIn=@Aw1F6#2Y@z% z9WC9MQxk!a^UlgfY zI!@URl1TtH{N*qFB75DFsKko7uHEo@ZCF7V4J!$DBWFfV!MyAK>f{Wk6h6vOVwN&D8 z>cN1%7~%|A+|6;0nt=miqUX*kJo*mIxq=E0osgPa z{1>p1VBF&^cBzp9+=kD&E_PJ3*w4OT)EDu4xQTmvCL{#He#+;Be<;nowRWt*20djB z8sC`Kx#&O>Au5rJB^C1@{xv&jM2QoNvPp#XJuk+MT=ZrzPvnbdFv8!%g#=c@r^$6Z zWN)EdaYoCEX-dF2<#6-i%2w2-PBCK?Gu=IbrwAr+d{y2|^+DY60LLzVyo(?@9GmY0 zKb!lY;P{{9?KN-9uxY`>{J|LWD?txq#y)48!W1&f^uSM-=rXN6?t)>7W5F_ya;KvE z```N2VgM0&^1;n{?j=5dgCmy#<;tL&Nh>}HlWTPNc+r{X8Dy{k( z-N~rvCfH|2M$u{s#+zDjoZ{XWRCqAKByKM z__97;DsgYB|K5kzF7%rD3JJ>SwDPFSwcJU71iru0RvZHd`|gXsh_<_WH=5aEf(?C& z6o}`Bn!o!8L#tr7%b-mV4d;s76_K*_zbeIA5jhp#b7rUt_qz>n^*l7HJ})B+3s+kr%$ zV2`MyqJR9zW?YZO?=ss`b^rW24pc$@Cq)nToq4ZSleeWCK|s15R-^==>C9Z_fd-)s z6@gga)2BL0irc^RY4`7b*HY-2EKNvjj zDjW5N%WS-ayd=u|4EwCAv??MGd6s(B@lmY|bQ@-o?j$PRT(j2n>%P22%jJY6;_wnj z@60t(4x(pkD|1zB0Wq*F{@zut=xq6$YtF^qsNxSZHQamink2$jmt4p7g`37s5Wskt zG3k-6R}deIWzz)gVHUuPBr&XV;t|yKXa|%2i@|&zw8L=N&i z8Wy>&YV!M{3IvZQT=1fizs8bV)WJ()frlp$#PL#aLu?|1g;DlTWp=M3G(qJ-G&FMG zjVk1v3ss%Gane+W%b1RlSyACNjd0#30HZ#OXHag?m+EI=h;nJcC84{P&+%)F4&Uko z#rFVuq|k^OILU2c>q68x3#GFJo{vm8^-G7b@pa~F1&J3g9m~?@01gH~yaBrjQheqP z6EAQrPqv9iRv<1oDKr`1h>+7q;UI;*?dMu&syI-r<^)r$XS#k{GZ4|)$Hw^jJZqlU z+&n{hcO&Z**TNHetWw21foofR&TKT7wzhtpe!IpKt6Tx`@H5R%MK0*ha06{ z9@@3I^G5wwpMEZ1T`2Cw{ z@`}&<`PboB*^U`c!+{HVqwVZXR=l!*yTApW_~OsvTwsZPU?cE+A?BAFXBe{v>j)17A#} zcF;beTWe=AP=op?O*+iQzQ+GN9H7FnQ*fGbwe}&AjxD%-KJrDYUR}>XvM)$Vx z7-`4j^dawDlScqEKwvdUy$;2c?*%L`?*$^j_wgg=DFn#@O$}&CUj&>NX zRf+d{sKN<8zq$j>I$WJ6h-!2E>3r)v1`--2Oc$ebpOYwe2u=aJ{!;BIj`|5Oyg_^D zdH=zA4WKI_R8iO#np#-;6^P)-_{425b()*p&HBq0DfLwrl34n=qIz2Z*#|w{;=Uu@ zl0+^mPK{7a%lSsvU_b_Kz14jX)E^GSS=tx_Aq6KX%YVj?%P*VyUNhIr3&p1w^M4vY zyL4OYexIJ_-<>U=o>1Ri&!3)%-(5TZjGvxN^ik!$fj36ts4~;mkCB3HN_3LvyqE(=PY|TwQ7La zHYKmCZ#JDb6VBM>>ge8^ra9S@^&WMV8RLlRFh2}1sggj}^UajB(@&NYe=D;O%kt#? zizC;goLqeetc-DmNyK2MPR)X!5ULz}rn_ z`vl};7#N@R3@Nn8@&{x&4o?G)l3pEUgzZ$EP=nF2aNi(UqZEkl6|Cp!va!w$C0Bhk6Mo1)wX+XB#nCOcrZ60xS5>B7p@%;7osbK>Mw2>Iiu( zI=H-&>fOZqy15V|<3#|;s%Vy?{+~H~T_TP>a{fBKt6GNv=FC$yq*)J5dTd}7ulvTC zrefi_Wze-?y^p*L=-!RZTZ*GRrTe(Z%=fCdCwt*bBpgsf!%F)vAxP?X6Lvnh3AFsp zFxqnHtY>*b~54B2TYc%^c)wBf-lr`!0MBE z-YeB6ywaYxv|p439mF0Mf@zYmlrX6ioBtMnwqoT%7n`0sV1ph($?Qf=LA~Kw4;|w* zeOA;;2{s7^`r=Ch!}Mi{>7CqSO1f$p}p2IOBDz)Zc6dIx;DM+hIUPRkNrHbZdPi zrk6Pnphyc$Uc2R-Bqg6GTqYqwDWNJD=5j||=C#6t{Gxs`J&p(mT^2Vo>yWN4oe-So z-@fG7FaxPI#V1-x>pYZR&hwNyiKpsX@oG)b2q$HYMFh?N&h{BAyXMJh>}0wlwI!g)o=4T_8(QXlzz?QGjL$(g*SsxE$PwR`^3pnO~p}bgMK? zjjW+?y8qceU%aiDb>Nh5GWW9G2Zye*U8#+C0U^`?me|4cCI++u>D?CV5?U?pg+)Hx z(w3Gu)~X~4PUfmJ0)~%8Krm$m!%3{%q3#fJ6Q8|Y=3JtV7#@ zdW&&~Hm_8{Z-ktSzRPLB%3ma9+7#p*^>v)ErAjA*;s>pwyU`^XCaB%-jn;Ox(+V`AJ*e)rDy%RFe%=cHDN{VlFYc$ zn%k&2=OUBl=i$w1vPQ%1lw8kyZ9JEsS88CLSQbA?5gf&D_v2+Qx>=CMC!hr7M=mPY zPIqxGVdvFlceUs~dF;&gDcXJ$4H9sNa1O-4=MC8;b+uroRd&x@{}nVpzuYGt5)SKP zV?!-6HB!~#alZsbK`&jQ>}u3u^(egjQ(2P~DKW|rdbE7FH&UK1pJA^*6N3x4X&H98 zxYae0G-~ayL{0)gxG34`XI1nzxXIK56>D)Uv=WiU>{SHY5$k%WPMqOF9@`CIptv3d z00L!=HuTEMk6E>X&gRPL+vuNjP?&FErPbN9uaLoUy!zS?#vV+B^Y~(&8PgumEc~~N zSu>^|YU)=)U>TEAmwsux-W>MXaKEeqDARI5N%cYgO zq7K3no<6EPFSjqmCHAr8okb4Z47WU1!6WT;2o)9u3mxrBD!v#c_U4&TYu<%~edl1+ zTY_HkrPEj`)0?K8aAdeciwv7kAh9tKG(r@TmV2D~u(p^%RD`irhOQ%xsQAg2NZN3% zFP^^)z~&O+ex<&4Vq_mWvO@2I_TS*ST6vQE)y;6Z?Fc&M>}tIYLX&6?Lh>pr>1#CV z<6xQTYCFG}HPOQ2gn;^Ns{M5;2jUys7XyqHhFf1$Aw`0){*F|5 z0ne);|HX*W1LYK1g!weF0MxQ@*C+x4ZQVOjguOc980>Qkmnv;!aX9F}nU1!Np-aC- zX(T>YPcr!AJN`QZ#~^t7P`m_KWU&m}uL0-QpmvT}lDo&T&2nuq=;^qoG$rBaAQ5ab z$gd|VJT>}f_sI=5K<}#i0)#9VJwlZwEeN7B1096g-S`MCzeDN8WP_MEJt(R=O8G;x zq_$s>iRTulB|h%S9-}L&fI0B6T;{IVoNOs`NvK=*V42|c8RQgfNYs)ZQLKyS94fh2 ze>Raov>t(+C}QBChY*N4Po=Nm`16CF?KprIE;J>%qxm)==tE$r(-=`Y#*L_6FAs)r zT8Di`YNLDDJCj8#ExT+80xoY5{%8gbUG|`jZq#Eh6EbqWjPS4<|EH;bA?1Oc5fiUVpc34-_=UaI7Kp(ev6cROK1(h+WG>V&X_Wmv5cJOA zNkOVxXTZc&HgeNQRE%=rm$`7(N(6KRk%tM8sauI*;4vTWUR3X8ZHm!@kJ8^8_IZg{ z$fEciV4d{mZ`oKzZYge&H(@xY3deZj^w&@SnanmJT-5}rJBRfGuD7<>$ufRROoOt$ zq-FL-s(sjj0VY95R_8#kOkpRHfWNX;f|ykpga!CVnwh6MXEcZMGyY0cha+^q+8lPl z4W`;|Y_OxnyVMz_UJvTxU&u;GR#&M#?@Pghkx;G5o^UZQQu~-}5*?ZDzMG2>5s$~l z!OBdTx9FK}m3ABUoi0xXgX}+Of<&5Csn2+?-X4F&)5DzM^e*x5of33A*%2SpK-Dzy zdi6@CeZ821ba@Rg*!-Wt-B&dZY8?ncSDmedhTMqLzPnW}fms+Tsjv>=1R(k_p8bK+ zutlC5?Sg}X!u%(WI%up`{7dW6)~lxwwEOTRW0cD%qpgpXwzU*U#Q#fCiuGuaMc_G$ zYLtC}*M%{O6M*vfrO7}&Y1HRSMs7h(Eu`SU&-Rwrl`SP0(BKvc`YP|v0yFtjkkgW# z@pK;42pLc`8F!~|8N%p|4Be)b|4=i*z<;te^i~mC&=ClN$YZT_PU1ASWzSk~#KmRV zRxmNzHWC}q7B@A&P<%A1v#%eKYvILhirij2&Ah{wP)9jkyit%UF?gp?n*}VKNI^Lr z(;LtuZTFWi3?zrWG752pwl~!@EreS#53KTVb7sHaYr+gvg_E}EGfxZRFG~G`!At|f z5bO32QhL$W$)+BN0Ka3PUmIOL`GyweLK6G>Z|4bUxmL-gcR) zmR^TI_Sr}+xI}melz|){q3-))M14j0MiU|FQ#Ze{Kn39BS1YIhSY|gWGfPe&zj8D5 zb%T5)-tqQBzc&XNjI{vcr6z{(@nPBku+{+JvzDkw1?5s?$naz0S3#I=SN(bACZp@W z!>?#0d7UM<4H%l(eqamJoyW}1?UA4~w++dX7G-_ z!~l3=Ds@1m3NiDpB<%I&WxgVIZ!(`24|zCK?v>npixy5-?o#rI%#Ep7Zx?V+z7SRG zj|d<%L_stO9$FkfD3V>7uKvuKl!I87>ji*}_s3EQ)`{^Vg_qI;pi9WAy&CNQElUoD zx!uU<28*}1O;%68@u3MFq+24iig~VTXL5|wT|n41^r$@vH$#nER5FK+BgLF1*$2VA zx&6B<2eQldZj2cc3+Y@7gV*Us%c)75_u*OB$6Ca|=&}JJSSyx$!TW#lPZC^rAM;pI#c0`nMYTQotkl6g zo2Q=xDV8hiz&X9+MQlboUM~NgrFvNXZcEQkKsp*Xy!8jBlOe~-V&NIr>!Qg5(St?D zk3PBH0u79_t_p2@t=|dx-s07CcS`ZH)smtQvFZn`I=K6B94A9dd97b_vYMy^>k&BH z=fib~!sl(9gCqp&2GY1sFcXVXcEzkTz|H8}tkmID)<_$9>*q+SCsmaJ__ikvZc*{C z4NHR|;R1*)g@-!15=G+1+*};Yoe(!a5ZP?c+-*lkqtm>^+hJ~K70%+A<2uu1$`2)> zhe;%o99~F`B+rDN2CtnDfD%J>>o(-p%2L6;p!9+wu6{U!07Dj|d`WxeAPhQ&V5O-L zGL(lJkRSEF&)k)2PoyOWvdr2s+!OydiY=OL7|l?SF~OI;L)l73*ty}&8`#))epG-V*atKu{e^t zfCvu|2q)w zKIf75F6|x*f^A>kFCfyxU7@sv(GWvib4`HOC^_@hzO%E(v(7QSe!_#sZMYR`JPj!q z4QATIY-kyT4OEvxqzU{H%L;zhdi8m5F-1|D-MCB)Lut;La<0-nqVW84FFSv~+b6(} zmCT`bJ(_NeP-MZ>kI8dkL)lki%~G_ux^M%RqPD2rlu+hC=l4E0tn%L%SGqH#2F0mgP)hQya7tyEQTf9T|*rq z+_fnh-PELr0r^8A4pZq#aSl18F!M)zqy^anVwBo*U+YCtqVLzK)5XdKwvA*YMQHSx zK=I=;(y*9TAR-e&3-lqlD@i2j*_=XV)j>o0?wGDg%}WT$dYJO?_3z6IhPN>Qqm_y$M?e) zOVic&yA`ru^hNcVzoBoz4DRVy@t?x(NanORuUJ)^OMnlf8;8M$mip-B5Z!i!F|$K% zdLbuCJk(k~w#wP>q4v_;abe~y5p{$%zEsGLn2(%W4h8A!=>vS6Atw}qz5rvhViM>V z29u&VVyY%m@D)=#&JsiSlbr}O7C?h#CaF;AkCQRqn`S|4CsYLpX9Fv|qL@@VTAHPY zE6#!lKhI~E-D!VB+p6i;=)IMM`I?*V{VJ_T8Cq$_O*IX=+jfNVik(&uZ~F9^u-5M+&&Le8+w=3C(`RTXCsb|}Py7H>X^>YAq!cU|-Z`0ZmbAQ>Q@v=mk zLuf6%G%@Dhn!G1XG|{BkxXise2Rg5TDaPi~I3WgGYT}1E1YfwvnUZgv{eFOUdE#W0 z$YDVXl586sc)OZo7-$$M(11i3_)7`wDst0WTk;b(n{GzIakpkJ{Gjqw1D(|z(tV6* z80f<)Gb*K8`tO$=J?jlvqJjwe^0-?T3e(0+l8M9%^%8z(g3@D_6hanlE<^~!r?;cj zDR-AS8MFi*#JZDd`2C70G7^SSf+@b$EtM`zD9oyyGVOKblBnAUrR2ENxX5qjL5=|k>^q(m9sNAM-eb|7)- z08XcqoBlU<;F~1wSvxVMIN)+`sp{5%`nVObmsUHY=)M?=x(7TW@M7d6dRgc%L5ii~ zG8a}sGSlT0Gh!CpMKAgGNRpN;ln#z)wJ=H?yeap>7fY(B(gmSrd6#hsPi0EDJeLbi z*T)Va-`uNVyldsx6NeN|uKHY~Kq*;@Eef|F%nJQyo3ZndZ(a2@_?Z87?rzXn2i=*3 zGrD#!8wgn#p@lW%1(n{1e>`jF*lprC;V)qnx%Jy6!np_Q? z80oPgn7$sL4a~~0uV!7LAq&t;enJHZtw21SWL%Xb30O}>nhyjUz*_&E-YK+8&5WMJ zSz>m@I}d!y&ar_PI7u^LNChJ9dsu2WV$Y66t4#YLn+LR0n@8Wps_b%>Yd_zvTx-1Z z=u0b*$1@VJvuNq#B@uQT013htk5LM}G`{lpFTopdpEp?OOu05shSX{RH2)L~nqSn< znM=cEKstrbuoy6&pHa|MOmjb*iPPBr1Ct526S?{9UR~c7aQrH|nS^xtrESBU+`$FM z03l#>9Wx>YRr`Yt70q{p(K5>^aAtequv@c&@&i=DD0qkQTHGz04pX;*!*fXeueCG} zA5h6?>1=z5#3eS__+`#bXk^l7<_+q-6Z~7S|2lBNAyYvmy~icBNba>RVh=Y|z;RLj z>zT|8s;;&Zgs7#s=pt$;m?j8B&a8?{&j4haF$d2in|j&dox%ev>O0A?Qiy&|?0gAU zW;`&z{}i)p-HJBb9W)l=THP7rWHqP39Jhcd}P%-Cd8zj3}b zgiMEWu|;(;t=3!Z(^^6b$5`loP*n*X5LztawBf8*pDMBmK*GnE+O2kqD}m+}68KI* zg|rWuqDCeEkJorZ>OmtZ$GxpNQJMe&eQv4?qBuD`p5notEiNGIQdJ%y-{5V((U(>yR(Ty~98ro731H=n?+qTJw?WhXJ$lnopCC zjlQ2y9IecCy*C#SN8w&-=>oBy+8dm76#+Tr+eJGEOd$B0gHw;p2Iej2ZuKE(vxN*V zEr3p1yJar_qpmjg_9Ur%02jzwGcfdbCGMA)NC%W_nl3A&Hz$Y%3Ql~C0omfOYF7+3^DI%QUjy{Z2+KdtHlWMa}Ew(oMJpRrR%9s)X2u=XSMtaISr z=>TzPlI+BRQ-EKLdW|8?=U8+}`LVkIF7>;1U<1=I2QM*~7yN(iX?tJ^kpL$VXE;h2 z__(N8YYSmCyC>>b&x*?gf4QlnORxLuqlTM~Jpfk_{joz4)lS@Zr-e{SLa#q)T0(H= z=5@Yg6%c<^n&;<~f^f8N!vu)@!!Oj`CxD?$NwEhmqR<2$*r!Bi6|<3thZ{59D89bj#5p| z;msx6Y@aT;^WwS!TQAB0^?PhqUNR!0SGaY+t7DGG+V|*QmX?HS%k$B5_tO9rorTx> zJh#6Zcf_U%NE%g)(tPM5NnXeWV+t$mvqc+-c;f)Kyw8}xL)$4Z&<

    TxWm44|qnrr$y z!khO2ZIr<4W~?ZEg3#;X!tchV+1&%qhC+i9JoD6n<9k7d1@w!y1}|OcD&*k}U4e%b zs&A`5twH7QnGj{QAqpKvF<33J8H|W?($jFXhd7SA)=WRkk}7jvsCMr0AJEyqj_g+@ z>J6dyu64M2r`hjx7uDsF6umG8A{YvfL(bykVPkZ_v>jRTVsL_(R4hC6#xw*Fk;z`% zS_|<5hI*Ttw}rwU4Z04MGd+#>iUJfxU_PBwm4UZO9!r&WgsbdxZ*cj5DI?XsxuZa^ zroxbTIf*H3;bi>)St?h!&xn3yH7e|dCx}Tah2I-GN((b$bLw;?FJ}pto5`#WGb|Ir z4Di(U5S^Qp*5kq}xoCsMS6Cr`zk)N@gw7Y1!BjP&-l~J_Ou${u%O3Hp77<=Vr#DUo zLV7t?ei50m`N$#&#P3>A+=~5J`;aA{9 zqgL5el1;GtpHbzww1n$a|J|OmAE`@F0wRo|f09}~k7S|xN5wt+u3|6CP@=*$4)6dL z=Z%%*d$E-Lh1CU5pBePt@*}BpB?fXs-b`M3;Y1s~XTpj{EQdUilD?fj+#Rym^`lDn zL2X4VLIShE8nsxbl^Oq*98fiN>1r%O_UnlO0yWV5PZ!OV??o(5 zEWF_?LpC-c+A$p)K2EGjTbLG)y_a)l>RZ}nl}Vp9?$!Fog5drowoxFtcV&&@FW=M2 zUTCdS7u{S{o0WVcBwa10J>R%hs2H`6sVGYZoe++33LvCurMl* z6WWX&u!W;Be)%aBFAFBE4I}wEH*t=;zVrDUY&H*_+x$+_sSHAlk~es4ODP*PRepT3xvAt zBwx}N9#YaXj7#raQG8y77F)@9stoBJ@@d`;uTjM?^V+VR%<+83Usy>*wdP2v-LMs96SrOYO6%W$KN_B2kol20Bj;XV=;R%FJ^JHa46?t(m-K(q{6MuZ)F1P* z{9085oskb=3+Ri93By7LJM%7vhp=m-dMHF?GGgXBmJdGPM55=Wm){3CrNW;qLgM!5 zw2G0D$fE6|g~FCo>O=BK=I9iXFWjz|0N7Is#=kf-=$_SD-(lL=a!J(2j%wo8*#XYV zr_t3I(o1QupVJlDhm$f_GJ1P?5P^&h7@MvLeozUW|EXs!1ViUAgvIp4#xQ4~TcKnH zj4=RknZC&tlgnBe*yk<^*FvF#JQUOQbHMH34QrQ3_OdWV6SCv87>oC922+kUc@mip zFsXDwWC{20P$rgzv9~b5BxVC92x%@{z+e7+ZnlFWHN3K-hjZWB@1v2L)0`wEoZsJ3 zdI-njP0ya=h$lZswjBC(HSgLVSGjkUXL2?deUMxL8YKKB>&3?{MYu$ypE5h0-1(+k zP(UqlV<;`?%Jg41Q%a0$qCG$xXm#0HraAF!x?TIuf6B$)pKRJz)2MN66>s zTcObuOICQH8C7XG^jUn~COd~uF5|B)c`8lo2&!4y`v-sN;}0@=F{0+AG+Vb1O>SfEH8rZ$@7k~D* z1`<<@YKS^y zUsu=-dWFidEtD9XMjh4)46f1;0E@>r6r63Tmji&0BF8Gw$Ja|tsSycWa5Eg+PR(^l zQ{iCbN#E&F0a^N$u_H8sP@@FABHiwk5T1POnV^tyeKTc8a;;r@nrwlxc5JjW0A@z0 zaf`%Hov{qJc!U9i{AO&wzrt%g(vv-nl=-<&Y@9_3OC*l)jez)0LXS6QL6E5LS?IGa z8_EEfWK}*|j>Wtil$$f33S^zQfvl-r8&a2p8V^|m@uw`NDN_XpMc6!Gh%_F{^%O;a zu9?C31)KHw`=0D2)FKQ`IT16DzTZh*p;U5P9F^b;VH<=#`&FIK>3%1wmS5+McYLy z?&z0GvRG9)N6Q)%P{mul;suA&(u0UADZW3D_WD12_!_MFl(|DyQ%3(RP!fYe>IIxi zab^$XtT-a6P@gN9^JO%~q{0cD%&W$zn)>slbk0`a@F?)!P+DEpj=!BrXd}E)C(aq8 zc70{W%&)D7VS3F1&Vcse`0_{cvEk+0jo3aTG|5e<3kk#0!bDdD-B729?^Y^CPby*vJ?w_atBEKU2;Bv5poX!KvIy=)CSaWd6Wr#+@i zz#QXi7J-5T{JO*idN)->^j*2D4S6PrxYNM8I0m;(9jkl6b~1Dp+D+%3HCV426FeVo(haW^DU z?UlS#%Kqqd)c=XpjqR6aBTAC;`~hD_l+!;iPAGgr2F9+&Lmpxk5u2iivKu3<(dVOU zqba~EQ4Wf+KB&6ME6X}JYQIqQGHJfgY9{8g(SzJG7S3Z?wZ0rEH_NFp?Y1akiT!-m zDj_D{;W7-NJ%+ZqE3Nj@%RNu|vkP#6(t{vP`^@)&e&Hw!M9gXCLR3n_>h;Bs=m1F048{dlLo<{}Tc!8PDf&fja`H?nQhc?I<+ddZp zlrqc@DYExtbQZDdByC{V4z^Ywznksz)V;QcvZ>R@{-q)KDr6@f_pogpkL<}n&seHnJGD&pN)@cO) z+wgv4FNY7hp$PWT%d(HaT;8dND8nsS7teMItSQr*eYQ1oCWJkwGdFl0@D~zGGl_a~ zuV12t@ub}7FY@N@MnAJNWG7?O^?_lbUWK7y*`6ZE&i#mnztG`x%R;&c)UBV8% zL!euY8!AonVMfiRq8RNe6A>|9>owln_ldlodo?X5Qfq6neTo9-D$9NuTO9M&jCyROfbueEZf7 zr8LRdaQ+63+cUT6{q{A_=e=^De&(lwDT0fznLeHXp-Rzz@M;_-hGT;Lml_hwkW~6` zpIy-GO7$V}Qea2@)2Q7Rm{FqGKkP$Ks)RE}?*cXd>ywfel%L6uIfxMUrD9?u_=Iv8eN zy1V)rle;zPJUN-?CNYjKk=4?e3MlCtyQ)u)3+& z_|+r)i7@bMYWf#jh1n^MYn$}X*XhYZnutZm?J;yclKTMPiis-0NBHA%`Egl{y8qNF z?b2~cOdKuV24DU4w_Ui%&vJ@-cCIb_;N+yNg2<4daOvTpXSI6A4gt!gI=;Da4bAl( z3hZ)9r_m=K5Vik$G?km1bS;R9tk@fR)m;j<7Z@6C2Mz_Chk-P8VfaO0FFtDW9PYg$kBRN1TYNhqZgGA=*BfjRhZ3aZ?#b&-sZ71@{6KK-7I%-mUVyjtf6EIMwN#X(#Qg9`!Eodt3lUrI-y$?ue6s8cf zQ3z$n$2Fh%x7Alc@;Wb`uZW+h-LcAkbl5TL`nDVK(Kirapkn zG#wYTKy;RWS{3lLP$RPy1c9q9IRDnz@pq<|8RxPFXG{)VpY>k_}{Rbm5eun z`cvD%IhphNp|sJPRY~hB8?suhmKf4rihDbXdy;1!dj4}Y=1eqmis1Yf*%aNap6xx1 z{rCgw`hP%XNaF(xTsqwTV`GQlydPJ;FMC>%ssUtj{qo+xbLI*hOM$E34;kUQ%>-9h z_w6q)M;OSxsX6V&IIb+j?XOQK{F76|5&^6QsOAnJLGP@2n9~t3A`2vRq`ALEc?$K- z*mK2_zw6)q)h5$_Q+JA$@#CmIR<06~o;GUSoery+7BMTXBv@(K28k2J?Jj67L75tz z8dPi?sgu&Mu3bFr$ze|(Kx}JFx%1rXazw{$S~pq5KWf~{rdZ(&^?g+a=@DxZP!#69 zr#R{m9+Fr4#nL8acp2kI`)T^vRHsgJq%6pzS`IwYvS#|&4lVU^&ym<^MJLmB0Y_!b zR~mi}0HD$6!I4nGjbGW4e>M;iM`vS2oSxI#Y4pW!>DW!yHF@-ZP5;(8a;7757=HR> z+@$)gdWTtpnkzfskf7Bhcy6gxX|3bEYiBJlsw4EIixcPV!FSVAxrU zXzX0^#%-H??i(FvLxJ05dl^Jglhj zD81*itz?7>ECovb83_!Fac#wzm3I1UZio-wCq$wC^>kWR;fQanZ$TojtlhbBEpO#Z zkh%tnB4BwyR>*T(0dF2w^xQ4lfLhS=1i)MtVIJez7@c!4Q7?$9fFFmRPn7G&H#-u# zB(8dxOtfMok0|A`vVoA|yMrs9zg9W;Xp2ob(6GO*9v4Gzg5xvV13<kj|`wJ8A-Y9>Jz@iHqcMfglo4PuO` z3nfP_rjqb4C6^?^h-K)5SP%rTX4v=gmfROStZxmBPm`ks)hH# z!BX#D0aUyt*fVU|^CjuawF;KSET*J~cLF^0j46dI9S&KJswx`SCt*_a7T&+W*QBhn=HKd z52*~n!0bB3R55xniLO;=HaI-B$>Xf>L!ac1CW->T@MSLSm0mes>_sEtr(J5fc9aJwV}ca%CVtvrm0*@|{juR3>4*FFAy5 zg2ro8Z5>^12jAQw=b$(@`)x{B8Z2Dbt?@xVH&&)J*B(jUqKmv89r|sGe^KI0UR-`i z=pyAZ=oj%k?d z{t^g-X}4c{U~n+K{gE(Z2!$HDL;yjQBdXNfx7kFWNKN2KF?bp~(EjOuI`mvgsB|n0 zzbL!`)@Ll?8go#n!V1{TzQWft;66O%iHndl?#Xo3tF;rv*abq3M6|-rm9QmweDkSx~gbVh-$1 zZAL+&4&6!iV9sl%gfca7hY4lAyu`@~(DWY}qugGtiCC(dexWXpcnU%TUmY9<6(8K!eB03xzMBGtEi0 z5?x=m2moW6|6b~#HVL{9KvqA7;X+5)Kbp`ccbw_K z27)79q{M0~aD=7iKCujGHhTHZ%9#zLXH|NB8(tLzbhuL(ZUBB4e=`TCu8(~IzAgiG zTnZ6c+>>&Ur4%*ZJZ>&jkA5PuhbJ_PVqE=L0kU`80McXdl22lEpq=mHg3bpSn<1@w5sg!JL!c?l#wu^9AbN^T z#1JOn35?~=gFO|6QXk?z0wBOe{!g6>YizS^@Vh})-?%V9?*-odTT6w|{R8el!;!0~ z%UfajOds&GLV21C?(ZH*kOno9X*oYH5{2IvfrPltjw-*fyCRfLc$LK!+tAw~e{>wD zp*HH@g(vm_EdoAM+PoN>yM)=2E^#!pLk*quB)Gf@NmIn>{}&QA6<<)QZoHN%O*;(c z%RdqRc;2Q^;#$<8L?nEwOxt%G_4jhqp9xd8SMn6TSv!P_g#rUqu%(}uk zIf<7I25vG=U(x@Edi$qtr^MC0`(QG#auaH&EA{rz_m28w&IjhK30y_ilpx&oWk_O) zhkT}P)1(0FSt(Ged;r5U1e9TVvQ-R06y(T0a5roiD%!Au_VmgvEEidt-m>BvW0@M3 z@;Sblw%1qruT=S&2q6>XJ|DI9teOrz9IH{o{9ZK%V5aow`eoj@lAsEzbMF$N$+bhJ zh0ZwwIgfLKeq*F@Chi-#TUS@MMMejwLSN{|b|0~w=6}JP*7U~^o~?4ua-u5mjHd{$ zGya^|YT-eiZH;Lu3Y7K)QiTnB9P8#N618fxSuv?SW2@WGMCPi<+J#B)$lV$5Xq6E? zcqk4$T(T@nKaKHg%g^)C<~Zx%GaL|b)Vhj}C>~t~owhTY$Tvjhm7(*C$bAb0wf=>S zL4;4MybI|g)i52Ns$#KhsQ8!_7!x$m^<5_KDDHsfDPPT?&jodx^0Zg58rJlt4S zc0H=TTEn>kDpGmVsrsvyONID#pzR)q?R_u)i=n}^kZjrfN3@NL$m==vw4)4A!ybmf z25rAW-*W>V#@fYiR_h$J@uovU>EXrc);7c1O4;jMy%>Ob(d9!f)_Vm`0i{Hoo{oQS zo4PborNxr~%QVV{8UuM6A|7gzb%$4Vl@y_B+Hhp0 z&XY&MGY=QB4z2bzz>(EVn;3xY)Nb#eCO421b5;yEJT^&b9njlSP?l zg$VAa@>e6>2@?nTl~NW01kyIVAllD^^57W40LYS@om`ZIhtHw6(%tC+j~PY4Wd(N9 zftgLM#5AJPKo(b#Fhifgs)M>48?4PJ39;dwQL)(_7HBj61&}xwDM{R}3q}{g#d(AA zvrB}m>vHUa^oXK4xnua`Ph~%E(f98SZ*sTVPlUP#noGdb#cB_ILXKJm)uS{||AR>EY> zO|0)#&z@UNh{*%nELR&L@e*}<&Om&;PR5Iq`BZZy{euGFg7m!=sqiNO#hjijj1Y&V z;!BeR(;|@mE0X&)0wJq$)RyFYL{#EpF)6;RWjPQ)9r>IolnxIH)!nsrJi?#KTuhG= zOj-rp^sIwJ|5imHT(k^LAnqJV37fj{IWDDwk4uIyp$_;0T8CQky%hZBI_Iv-AyhgW z9r0ADOKj73ZwR5O7k!@G@s|a$&Y|dD1qfZ5)p)*5j9CS)%-GtkpGNlr{YMGS9MFbD zu<5JL`Uy;`O@uE}?vnVq(OB=kB<0opKUPT4$rI>I+NlALD6D$Z>{_sFUCba`Agh>` z!roS-tp0ZdOwym--V0^KEr-N`0!=Y1pt`TOWoqG)gaWhZJP7|ecdJC$zoO)ev-b)? z44m+ja9mYj@IU(Kxj^7_w=~A*n1oyhn5PkLL>vFUEk7M zk7Mmj%#zr$37MIpdTik^g$YbVn52LNKq4WXf1NxS3ZqZ>ku;9Jq3A2>sk)3<&=0*a zX-=?Ch-d63JpD@iB(BZnF@;2iCFz*Ku2x>e<)qP>vT41yN{vKjTEMhz!`<>)tl!b> z)+k-#NZWe|6upLza@CGVH|Kj*7^aE&sDjm7Z3T&qm5VM#vi~^>HN$n3FFyU`4G#zl zMk)A)sCjNcK?%UxBnfa+PClBSdz}182rhb}Z#uKk5QOT`4ZhTS2~_q@jW73BOC9O{ z3@Ps$t}(C$fEV3(I2NzdhPO5HaPNj}^5&(tv=n%K$N_c0wH;8=((S(?|0%7+EkIJ$ z>d{ZN*$fyfimE=rkz+bL)Xp?X)ZuGRI1G{02bq0vSX;h~2INdFbi9 zx-eYrj%U@8W5_mbe@4Y-IAyAOPsBA^)%4BpcLjX z%TUERQ{3eD%{r@$i{MrT;c-WQCFm!(c$@mY?nvceb%l|9UX}^y6H=Y6ayQ}N;ql)G zmo;_!JVjNSwUVdbpA(0Z1i+ykEPeBI1+ij5knNCljMagUN<3!?dw1mW^roN4cf_%; zcKJE+%|P)mU9mkW1u7lXu$CJqp~0VAIai`{Zx zhnNh-Ic8{6_)r_~jvt?Eau5Q->0GLo&}5uUSgTXpX*y>Wv;S-VO!_st;xh$%xW7eP z3iC!J%{)f_tCu0g3irxqvo$nF zeMRaR08rp=HZhIzWOjcV>DWYR4=XoAUjgQw)pxEBewy`LCe$TuE(iUryMk3`)qC6l zE@q5lS4N`T@CSm~w{&Xwcuwu7e=twb1yqg^IR<<@KFQ0P}FJ^PP zem}t=gKBvD4@y<*7hD6hQ;Ju=y*i9dFnioD&^J$BBs-;tE%EtxtZo!SOYHhg7mYN8 z68LZ9E~qZG#$e&6$jo>CWZgH{=iig}6p323?VK#h=a1XY?)jU%o3%tcse!kj!~EM5 z(`oIIc2-2nVio^KKTbv025?R>dV#MbT47-^jVl=GM)xRZ1r6TH5J?TX7Lwbt%Mf}5MYK3!@?>u^k%Mi9-1Hv;tDz<&maV-w zVntpn2+4tXfdTE}ZZ#@I4+rBTjQFN=Oo&@tf`bXLmMZG$_;3QVN|$*F>Nplge5l>4 zR_b+gUvT}fOf?~9^1Pz4O=b=AH{`84M=JvIf1t5@+}I|Gu~T4kBFL8!h>Ml&LZf3R zodp^fcA;T3C-w=d?L@{3WCqyTe%4X+d#(@`*wS1!n*^khtEo{rc*pj~dSNcq#TO;$ z4${R%q4iL~RF=G^mR5feefQby1}MymZ1b1VQWJ1dPo|Gzx=y*jYQTt8z0QfZ+W6-? zJjF%YElkDGuxQr_?IX;N?;ynN))yD?aX-GB;x6p^%0mY$vOpx(%5CW4XVN|$ zroK{BK8cW3@grpaFXC#|R3(tdUpvC|WbP{nDehMPIzFB?a{v#|lkCbB93;=bUFQht;VpS&vwYIggyMllS{?>EWw%nudJ2YnirYKU|2^$DwMk?Oc^>V5 zi0oTjLZXrr$VX`!rQUPEZ;J>RQf>^y4@tP@Sa&FH(K2bXA#2K1>7eXB{g?cGX)uO{ zB-Q{YNEB#Ks>9b19;uwZG`XZdyr3YK(0Vx|8IX-Qa#t1`l)fvvbAxa{qjv1f|=G6i=nQ44Sw>(gUg6$Y0Dgp@> zp@%3#Wnh8vaLvutk)avDk{#Eh<8uEj`@|xyh|H>Xz~*a0IAOFWqrk==Ikw5nwXfL zc+iwuz((fQBs?kP5yNwRg4D`b`Is(eSL<5%kXY-ECl&@nT5};GocwATr;84k-F$Hw=EP0 z)MEBN;G2y^`)zr^Ee$Cv zfBt1YWe{0dIat%dAd==YuEdA1c5A%bUx$)D(I8j>wL?K!X;{Dwn&qvym}m3o2N{Dd zVcapjbWdw*i02GTCKgdt+x8~WoV$+S0DN5c)@_M_%H6E`{Xg?zDcB|sX#g9JsquDt zv5r}f7~P&Mv*G`C0>+9-XWl*7LSFDAYfnr~MH}1ktQmz?ZfSwJKl57EW^?f5uyg~t zF|&s+UvPEQJPMe_phhqJnU&^5)@y}IXcB6zDhmS>2Q-tuhER)WNC34TP>Ccv)BUtQ zji&P%E}p%`^Wo8?`%mC~Pk%E4Pd<=z#dj|0%w*FxQ6hCf68Wff8vBk9j5NgNrA#DP z1h&P}{71z0Bk9=t%4Act`dkA3k=iRDu&{OXD}?_)iOW&H)Id5aU6=su7+!CF`T%qn z-pT@QtwqvF%AyX*yqI&Oxes42J{C4C8l0ASDxwZ=urJL>`<81&{@Ac`pG4(@=k6`0 zM2!`IZpeHV`W@%*-zZRBU~e3{x$Oaa5j87ej@+NK9|)>PR8afVh6fnnst1j|de7wV zIgp`hkO@6Yb^k!7=R@BWV zKm&USmR1WfQbDm@Z^0G!-$gmp(;&Wgv@y(T7+&Zf9gUhc<>qS`X7Y&j9tj-!_FgUTD z&z|)^ALXd(x(+Xod2fF}KCsgg{Os~taKEKokPuTv&=gavOe~EV{|SQ^T=tc*BmW$$ zQrFu&L`2=7)OE~A)})<^O=Hx9#2;S3v&I*<_2JaP3H;5fALW(HnARXO!^H*bbNm#!r&NI7U73DCeqKHW}Z0=WfT+rxOy35 z%gk~>R-cF%)&4V`2=U|$IH@XTV|3IuIR1#ia6M&3y}!ab3zyy`xFxz{aydsW=Z7B- zx+d(p&1}Qk&g&B*B%aOK_9dkq@kEc(p4+#3(Idd_*Oh;Xkt-9RJij-ws7Xea0VT zXYOPu3Fg+w_bO7JtVX3 zMZ+2g8@x@9N*LYJeE|P5RU^O`BVv4$WL!^4ezY_01y*hsk2@BNezJjS{fXNQ7}LcM z`97C-QAh2ibCph)T8yN1D9t?$OBC@1X+&8F+2C5#lrT_$Zdxde_Af&w%tM@$FK8| z`02-wRZys1P zV32Ykv|BHANxEtOYMra=-7%!y*GntDFa&7lSSU?3@RIav3_iE$@h&9!;L`IKHSIf~ zQqhkF&r&iiBseJk(IF}lfCJDa-&u2RPs}NCm4C@xeZg?5atm~jNp1;?bQX3}nKa(% zG;Y(^Z7R=v*=bfRW^Dlt4Oqaj`Y5OKu!{F)8DmZ`d|QW5EwX^c zd3M`*HK+S7VHz(02;{M65oqC}>4osfd={4!4Lj|ydLJOW6i^Z2aqaYz?qVYSu#wvF zp7Jy}c-6_|@;@LO9m-sBZG`d7yL(t)n(;esuAxsFe~LN_emt%PGW*cx31Vexk^!6y z3lo33-b6E2_#W^zQXC90W2i@>CIjYuwB6j;k+oROW2?z*4T!24W+w)6mvq-Skm~8a zPf$nyEolU1)9hWNdy9wM{zKb;2&YA*!RI(1Z#l$V1e`^^O@C)g)re_2$@OaH9-O^E z6=?<9n^eWe)Z@Tex<#=@6D4jdl1qEZE+*lQ;%_tckUQ8id9$Sf(I$$sU=;Y!Qz@DX zU8$!IX^$V&ljS%VPgL*a21~V!YGxWBFqW2}H~dX;K$-II(-{=m4v(^E;gvKGTC z?%91a$*?5dwC7hF`w4+#^OR|=6f3Zx%r4viW&52It`w^td;-;Rtb{Eu>)SSe;OU2( z( z;fQl@z*h8#^_`zWDHDsVt4S^-A+{w)aML#W zrBM4t?n0I_jioV-y57X(Q<93zBNpf=#hPfXFF%+Y1hbL|j|raTKe?pbZ%S#xOV7?YP2^y7bgJ?&6lbzEvKl(s)90AD;JkDGl+E)x=MpT%;%N{!{p2GO( z@wa+CWlg{neVG>l=iCm0Oxs)a4T^1+O)>b6%Y{_H2z8P`M zskn$@=dRth;F%FIcBFIr$B&L02jmb&;L1iv0>ozm)F=#%f{qL`mLb*A;4NHY8AX&T z5WtCz=3-6~%ukUg5LX|Q8^q7wES>RUY&qyQ=^|WmC|Nw!0zn14i(2v6i#glp6D7r< zy8v^R*SnDr7d9#yO%y_zz!Rlcg9E_5iFwg#;(_UYm9SFav=`pV$t?VJF@ zVb`l@W?6&)<4`yR+ptB=@==SG#~rxCU`%C>&C<jE4EO>%5_#;`T2kHvdF03^!excszNYH>m5L+MR!kbo zxTUgSW!D5n4w>Jvn-Wd$2o1X;;)FK?<@ugUz2t0;)Bk)s*<#ES%&cE^m;5J*KcaX{ z?sYYSO3ZzdBBaxPS2p7h6WYe6zy|(WEE8IR6xdea<=W%Ti+HD9p_EbefUg;is8uot zVLXI3{VL!xCyiqXa2Snm1e@u_|k#1WIIw9MOu32@ zWih(AV#yO;O(~MeoR!v(BRRFWM!{h4+W@KvOBf_E*6I8*Rv z#$WnWiJ_re;eE_1tu=!$%7=zHSs=5Qin@D8h18Ce>ey;?jbb0gVGxa!)?|l4Ea&yE zss%mOWuH4I=V8dRIHcwv>3-d1@3&o7)Ej+WQGX7(FSIv5R>EIDt3Ez|Sf9gPKUU{o zKWt;??c@|t)Hm?ff7QIV&)#VH`~Cbk zhe!Bq|9%@-kDO%sy59aBd*5hweyzg3e`q><{?5N{%YLqkubsJO?ZwiWQ>GOkeq|lMME!1~uX6qe1Q-{`%Ts%({$`=t|KiapG;KuO9j-Q-$i_deP z<%>bon4jj=XmjR_y6|}H9MbHZkyj{gDf%}Z$`qWCAGUU%y}hn+t+*_+UF$oiD^L$l zar8*h(!!h94UXaQD;|``m0HiUB|z#1e>z>!M8jT52wOhqalw4X%8woGGCCOMPJr}X zs|K1)N0!6&ecpTf(}e1T5j=&g?8N2hDUr1O(HZzHW6_y`giam%f-0@B<9{mCO2*+? z+>Km;9Q6+mCDL+h+&+W@966YCU5UFXVeHi|5`V&Xf?@E?l{AqBRnr?lIx;uYzo{|x zsiO$TvVt*k6TdxLTn0y1gam;S*g`*@&|(*$;EF@^Y`mIks+t>K-)P{ITyZ|%-CsX3 z5?&9J#%75sA{c9M+=$D^FK00?%3C{?pQ49Ys&v+zIe;4Og#6`9|~8VV4`Gq*4T3W{0p;$Y95zK9vW| zDxbsqi}YV?W>$ny?A#&Oq=I@A-!Bh*UkT4p=0Iq7Ls)54Zx3JQ4AO>kfdnSc>g0xb|KxiS2(#2-R>eY66wt|heE66u zia&r=Qf@nNb5Dqd*GhIIs$TsxrBt`pCAHcW50+z;nJRU-5)Kk>v~EoT2>2`ie&@=; zSuYcK1rGAN7R*z)c3*w45Bv-9P5oO)w1zxo=YSVGv{8&)vBF1Tdzm7OY?JAB?_Yi8 zqKWp;25K>uB3z+SQRta!WDleq-Cj+X1QaIDL*k=Spq)(6Hj6u;72NFvcp=ck>kasO z6%@s{-_3zTZNI}WpVWSI!4s3xkoFL(T#Sy_V45xAQ#q&N+;rk|QiNXj&3|Y9d z=ROH&a%WvMi54|qoW2!oZjwLcm{OqK1_t=%)t^%?pkqO&-0A-a3;>Rt=};%UyqWAz z#zsNFXrJgjT^PiybKv|KI+$75>1s&oJU?2R z(=|L)L42fQ;P@Z<0BTKcu0?R64P)bD$?^?=2)Gf+2I#@HvkUb)wF82}j-E@unaUs6 zjipcXg;{=wgRaFxTv<$*ia$bOer^0ZTT5^&$ttQzk$ce6--PNJ?=$!|yfkn#s=B5u zM4M9}YH|k&GGuU8+BE`6y~!e!tf;Sest>h|_?QLQ524Tb$v)q#d4TSbfPo>&$%l&q zxh6Pq=roy$aXYapc$td8OKgq+u&Kjq>0!`nBvoH~Oq?E0rwxmT`<~u?DWZ4(?p=49}EnDk;-kki%i9(F09{p+BeZ`LQ;eeJ}~d*ZT?S z0-^**D?z@dS+>WS6$(U|i$d=k+eXy+XtD6uDFKq}AV7ia8L;ThvL;&3#pR2T=zz&L zkHND-_Jm#h_3|qtGKu2CCUdk1X&ukd6t_X!=0=5nr%(rT(R^H!S++Y<#R7FrH|O;O zmc}A1UWbucqly4S{E2H6sT{`Ynlk`2d_q`TzZOM7QE>N7q|R7>?bdaLS5JVY0FU!w zLxDf?fnL|*`jdmBq33XULRf}wHhT`DWBBT~^zh&Mkd+;)aV*W4)tP`G zINFP%7#>FTxy!Z)aOy@)Zup9_YMg47l3la|JLcQxAhc3Lk|tW#v17=;Hy%t}Y0Uni zJW!xLRRW!M&SfE55;}wE0H?rSNuAue8YlBza*(=QwwBQU^(^U5{Az(%}$aU=BG>TOSU67rgl-ZC#La(P1>Zq<}h#S_cSMv=oxYjtxA_<-{lZ@)b=5I&(f62##>LRB0ZouK?p)BWtJ5ERu{30uGy$yTU_%aBq4os|Ah*>DrH*Az>P@3C3H~C`tw=Ud~LwCkTGH&Bb4<5d#vt>UnctgLyZT7 z;BzQgR_gi%Q~dk0%#fAOLSK;RWn-ER{UbiL0J_(W)MFqJY0HjPYw=7}2uU9*4rl3gcXL<*$!n)7$nO0Wvoo|3L#i`zNDrk54t!cU@4P9mYGYX1W2*E<~Zhy zGIgX;Wl8F-W3I=!!*^38((9*(n4{84xMBG+{E2I*4;E^*fDCsf4O#ujj3-r!)L>JG zz9*nbwM$CJWz7)`!Ux>S@j&LMVw;;-9+0`^=~|Vi@f+UIcHpO1o%Z z9aON~|AiCWB0k3Hx63ASEyOXlmK0?aD9V9!rwnkdsvTMIPF`s#Adw6zu(PaTN?V_> z-nu0Rr#?FryJ%~pIL~CdY9*8az023(?*z}lwP5SR`_SFKFVO3Py$s0j+02eoZ!NrN z!@KFmW@30U-=^Gh^6WUp-BquqamVaCI`? zi7FvSLMiP`MSOG?az;+H6kMnKDNSR54dSvbx)Zty9bjThgo0{mb}k7e$~j-)e!&-J zRjsPUWR>(7{|K*t8*+U3U+!X6gsR2=ARS^xvr`+= zq*aZ0-^3EyqH#HGR}xOixhFZZq1sP>3SI>UlE#aXCt}lKr3_?Wui8~@#_)^WdPN+S zvH&h-Jd9s=Ku=iI+kz!c!17sBBZ%oeqEDnxNy9PF_u7n0)|z9Ew}T6Tx+UCrm8bQn z*yrc7%Roo$l1vTSak!RoVIP@{ z!FI{(Rrt>z5XE0St+}c_{Lpp#^cN%DFn;kV)8QZT zr7AkYh2@Twyw40N14ES*Xa;;Dd45jND?mwH5*5Yc&M5+=bCM4_gLi&w(r^-DfegB7 z0+5{q`9w6z%32h)UoEe7=#YnYizg{90yw0OQu_-5CW`w44DCLpa3x6&*;herCDyc% zQ#HNR6`2HNon~E*v7IG%(As+0DS(Ek+%}9m{mV+(5m)SHtM9}zinl(poK;GrX-!gO zP~YMpvaG7wC-cPXC>tKqkSqRNB=i~le|*j>BMl%9Y{;$!55TC=bD)b&!IymWyfa5P zze#M86K)dLoyKcN&r|%IQzL>qV4afNAw}imU7(2~T0Cd}Q0#|pvlr9#ykbAG^-9=G zpM1;Cm+{1{|Dq@lV%7x+$!8Q=jQm{ppSnkSD)-uqy0!fT#pC{U*G%w@EZ zWe_$G9&mW~5eh<9bF}PKN`Wi%L+6G_Tr6$ad(Djb-PQXS(YatnQSlqF>+f?jah?vU?C(a zmaQOO1-<6YP|uEuuCf-TQhbl*N0|fu-?Z0g(r&d!#HuXU+7PZl3fbozo4nVem=h8N z$>K)x8aPIqJsOj@)4WbrI?ZtX-TcSKv{q@+v>Nl|HYsTAwWyc+O`deix%yjj*%^d6nTVjQhr z3e~-d0`q^^zQel@3%*#Qn#j2&NPt0hVUQ9&Og5zLrm8bOF=vih1_9!B^6dsMlB7jo(_g%#IEF z3a@^Z*4N+vKHkvF88^M!Z1;tZ82@L(*mXIsY49c6Y$NERU%yN89U!8m<$?LWJ}HFQ zEOpg4tqCyzS$aEBjs$b+w$slr;*MwgJMid2#R0XOeuX_8W_#DTpT^AZ5B{!+~+hyWq{Xwn6X>zN9-z@CWu$gsg0Ieg;8K;iqtfQK%R1d7`yg8j~zZK{jPO z1&f^9&lh|yYhc@NpJmx#Z0H>(hTkh|B0F(g_hRqZ6Bo1GG*AJ@#%zM}MwHx2Mdws& zGgF>;jscsMXmI4O^qL;s8<0L1{9(PJnqIp~mb5T|$leJg0PiGR(lR_Y0z5T{qj=E@6t zKC%IZj9^~J%@e|p#g4I$H7KF-5)g13x)qRd2iOItlAyu~a_A~fNWJWr_|bF-<^JPn z+!ZzCyRSqG^p%+skk&s*@P{9WHazC+D|9RVuO}K}w;vL>k##S1;E7Ing-ZX6?2CXXua+DKx>ls?!gwunA zhd>c8eq!52>+>K6auVKIsvV~=d}~9n#>D!Ac0*U;A#*EOKipYs2DV=7ip16Iy}qYt znrrk)O*{AL@;HgQ;Pw?Q8$m@+{pQGCqPhq$__Mj-cI7C8yE4h+byo5h;%SiH8G=^SiXj6spCR!d_3Zj$7z|QF(d-vT>jhUt3y~3gXaaN+c03I` zNp7usWWr}WMMcP0d7y6(y{&Bn6beB)bo%q?h{(6O?pu&hzkmE+)2#N^ebC^@DLN3f z!NOH$p^*~yWhnnv9xM}m%zqexCh}lGoFM-nrgh|Un5_(s^yC~@*O7ZWO^JfLM?a5) zxM`!-mj;Tc?+jCFHG%4Hyxs#7tU_~r-jTV-&kw()7`yqWNPzW5qrv?yzI+u*KAnfG zBN>v|;AzM-(=DhGa(bH`N#bp&#ggiRCD$59+Y^_ZLlGafeKZB*(0?`9ZtWkYb0FiB;=}OZ0!_=1!BGu}kRE~*z zX!CH9!qp|XX#2r}z)yw2&IRmC3UY7%4q3KBa?Pp3S0VOZ{vzF!8_Np&^3^R0l1%N5 z)Cw0n=)tT`C_c7@wj>F^Nf+efifQ;ZD~h}`*Zwfdtj5+;$`tPm#{@;m+gdhG{C2Ie zh{O*zZuRBg@_iSm5@pln>gY|@2E%RSZOP&!@7M|xr3mL9{9(ipSN5UUN<9hLDbqoj zl^vr8dAwU8FM#&s-%t5W=L^iZ6gYAmo?UgAeFMyiQ|cP~0{o&yMoakoAbYR@MZ=G< z`HOTauE;2Wy0R9a(4iv{lLp)O>w8J^W3ZilkSn5o;me}_HOLYZC3k$y_8tUMRJy$X zfBQ>Aq|BB^VX3lVj=uy+D^=b@Y~_>EiBB3Iww7D!K=(4A5CNIY{Sr=>>H<8S83XUEjQPBE>Bbk zl29Io+%ncZiVn}}iVzJ9rlu_-3seb1$}}`E>o54zUf@u}lV!+ePlnKGp;C|ArSdt} z*dH{~k*MBN_9#+c^m~PkVH~@b$|loNWj5aae63N%GcDro2fDx5xJKCjPVxtyB@-qk z9TFTptuI*y*+2GMT{Q3mNYpHc_nsaSY1snH;Z5Cme2z}5 z^wg6F3-DfvY`3c__9)Xg>*gR}m3?#}Rvj#q85E^V*B2023#evo;9>WIuP9Ir$K-5q zIeP|eEs$rvA*pk)$BvQtG1&V#=4eq5o8iq9p`U&*7{>MqW7qOd^)|Iap zjYlp-7kAjV26T)Xd-pL-QB7iot-#%Ej7{12*NrFxZAoHKvNHGNLCmo1r`2e_!RvDp zP@_rXMQ>{I%)ZGz1D}m!ZvC)z>*?bsX_sCd#X~S^-N`a~{dH3yBa$=4Ty|(gh=NYn zFoMa3!G9(aT6uZKC%}y*F05`fW6=ycPnTgQh09b{14Q$~?uils^Po}X%CjWb0}}^4 z4+eCehyy95>!s2EdJ6y{b$oMS1-2KXWJP2pc{~sYD9;A+J#xA@k zWA8qIXXHS=YuX|z9~;fhTx9lDW(B?jNfTG#Fc+AYl}CL)yw$RLl+|`|km0Be33D+n z>A9rSwg?1=?n1hd5jO6vaZD;+;GIFwYf8ytJpfVT!*198cMhhwKUOb0kUeQDu9hXi z%KcgR@KQ9YI{-PRMY=D$zqUTbD=guV(`3F^Eg`NupEUQXh9M3HQNSz*Fk)_zoHxvr+yt#uc2K33Z!OkNG;x zL-?+6AhF#^sH#tR!3{Fm=nue6%FuxXpdG#SfJl|x{g+B?9r=`&jv(|72AYFDdS~1_ znwQvgJ2R%X4|dD{RvkM@5SOL5&6p%SlHm(qH9vQ-(!biBRQ<@dpw)iVV(sLrWc`eLs-WJ{Ye@~JaubVlZ>F@rR#K(`+)?^gpk4=O4H-l zJL3`3X9;dI9XgLMq*O59pM&PdqQ=qfNN^72PvXlMXTk9FmQ;sLsg?Bzvkh80`lunmAfnLg*&Eg%?*}gts##oki=cA3;!=^f}KE?MHi*aR0S@xtj0)4?cG1OFjt!N zrwrVd&9k2yD6;`V@QA5uWJq1td7#1Z#a>VPO;&P<*FQ)HX<9WCI=My~qfg?R4`%p` zFwADuk=a&^yaB~T=dxOV{aJl4D@ZZ6DM$Vhmshx#cc87ORNttijX@R1PPUv>dVuBg zO@x^mwMg1r^q{#kt&DB6>t1nGMAu@W5^|pG_7t{D{1^$m$K~!D163g_%S!pOA8w5G zo(+lFWB!$8u?J5~O;K?H$g5;?xwVh^_@Z|bL|&SPH`&%nQ&^Snw~H5JBr+XVIRH8c z!qkBL;NIBnhc^qfJ8FHxfjZ(Ks1~w`JzU5S?g|A!#b1$>;Sh&t*IIdSB-(ctv{Qm3 zgT5cCA zy`3otvVBwoo;=aI*i?<%vO$B#qi%a+3R-TDj|<*{E^s&eux5q+wq@_j2M-jsJqD+c z^omgj$3!hYbx^?XacQ{z@e{)G?hV;lBRKjPtHK#!$K%51fGGdfw(rMl#$j;$HU(pR*ixk<~d|5Hmp) zJ6S>igR1>?bD-igIe43u7lHqKOX8}nr!)&AP_iyFfa3-NK+RCL_f`7 zli=|-H68x?BE7_lHQv4&*9M1rhF3ug%h+Sad13?+j3!=kn_Yfn>N?w1wEUcWI<`6a zb+g?qwZ_7SAIM^bf(L|yYkm()^xNaRQA~{L_0{-iY?53e6F-)~_+$?B_e@i;x_%#@ z#B149_W6cPUiNsJr&#Jwigv2GM_T4rF@Ucs`Pm=VOaLgV;vSedohHyNsxMQA_y$AT z)+KsUdaYFT@kXpcB4+4q^f%8VT@u`*+7{yw5j2+(Wo#@by59O~!L$Q-^C9+@$=)GO5OZu>|>Qe;&es>V+HK0fL7Z5q;FBINn>|? z+us;&3f-rJa&n(WHNC)WOCs^nGAp^wA6qyQNR4wVC*2mn%C^*l&+p^RfRS_I;@;sJ zfZ`a+fd$@D!imz6+mHH)FuTuzoD~roWI3v1Hik{%WTRFC$uz`-;FjK#9B+sOzX-|{ zCFX;|3*7vIUdab?!C%FjKaNh}Z;nU+pZl+zu24i`El?J8X}y1{a%F=xK}|A4#(qk* zy%N)uxL!9_c>rxF`A5BVnGe-hG-9v}62YW~Dr^=Y+5Ja@bvcr?zN2uUW3o_zS^?}y z3OReqU>~9S6}MoB%;dzf5g&CY?!-Jr3pSt?gXg_Oy(LnvY8LSjyJZDW!i@Hz$Tc|y zmW9$kpfmN)SiQ;Hq8G2+f#r&`O6a29hU`BS6p>wNU3m>jbjn1;>{4*hx^CVwa3P0T zxMWntJC-mE%&8wDtRmF>K98W5JZnc{*`4=)kqFP%Ac*{S$F+NLa$oh9P*|I%=A1qA zjc)VjYXmVAYhEiWBWZdspI7t?_S0uW(LxC1R>yrNeyH~WRtpor&+WP0qT$LCO=9eg zrIl}FsQAKNR~!gBSKF?&HnWV`BpzN6lH3n%_EALm3Hvun*J>i`Y%G`R z|8m{~#jd}9%L0;8Qhfn?#UWL^S%MCNEJ`+0KSgAm-wvmJ>$T&xl2 zFql;P#ihQm?k@sh5WPvQXGzyq-*tY9uLTTL9($^`HUr69asDpK%TU#|uMLGuT^cIy zE0`6p5zwcTT6gQT%50=x+YFoGN<8--A*^MD0{WL|h`+MkqXyuv&(>!P9g{s<;L9ZTcSN7t7TNzW?VPezFT7BZ(bdY`?`q5_f!}?%jA&-no#Mi7OcDMyXZzc)!tv_OdvD7 zxvkY|sNjHN9XpWn_8Y8Gc5-8tL71KBE^Hx};<^MHh;gIlhShlJp1<^6Q5KQar86ej zJmEH!N;H&vF>2@W4S2BQ0;o=FN6y}X$xY(8-dgp%Le@DXwOYA^+UXjH3K=Iu!M9%F zJP>cokVxCP6{^rQBR2i~lM8*GNrm>y zbNY0KOl67Dyy;6P(Kk@8PS?5o+em}L?hKJ+_k zC^r2D=Uw{*bUp>`usa3nl%v=l1@2LtmN}RKgog1O+REF%is!v~i|B-*%uh_;YZ_qp zW-bGc+0!%-q-)^*Y&)b6x3*2wX>>(`rqy-0#9Dw12!}VJ^%&*hY)Aw*T0F2c>_aXL zO3zx25E4P^?8tr^l&(?Y#+X!!{Ozhw?hfr={<;9pwfPjZ+M844hNm42MrbKOS?(Go zCTX1iSX`6sG4Ip2!ijjlWLflxGtTUGF4D#nm2Z3Y30F!gu_2FMc zX-b69G0JchWyd|g$qQX37{15Ay?W^VZ^J5e@bqeYH6h&(xmAmd+~&w8tThV_;>>sC zG%;m*==dLu#84?2nQvIw3X$)j1D6BDRL4fiTM-x1bfodLu=;`~&MM-;>?bHlXXFciw0o8!&8_Y6J!=%0h2XL0{AZN-hGKAAGX9YitMx)!F;mx z0`{6hqo&EfNPq7rzZfPGR-VYjOAh=9u3-G~AX|V-adH)?DbtKUB};vOn>T6<@%djE z$Q;nn&2pS$rD`$E7jy%N7DS}3%o`=uQD`J~+s1TUddUg}?Nl(Tg+OFW1Z#BG!kDYw zQr%#;{>~%wR)X>3g(G@MXAyxIrbtVR^m{DDStiiFD23ZX{4+ct?;{H$Gur}(Nd+Lv z5UBAU%Ut1Gt>0+9YwXs1J8*)hD_MJwRtZNVWPBw0rhykGDJ~-6a=+Ea*Qcp&b2PM6 z7F{CIuhI;LoA+ePWugIhB zBa0FU-4Biev+EjKI-|fRG_0=3?}=7XL88FFrJy=X88iiaKr~iOqET?_!}<6s;`#}< z)+phzT7MMri51@djY%uv!i5TkcKTv~>cYw%3?l5$@4bAwe!|j}^99UTUhqRW8diKF z73mdtHHP6-wG19YPOGvcFp9QoH{MRFLwyOvEP-inm?Nu+Wu^E~z2&M_*yE$OpZIJv>BQi^hX4?*8Q@OZg!u{+VJ6+Jh9~ z5m4HU(8jp9cp2R}4(pf^i+b)GyvU!Lz$c9M$Slo%+(vNR{l10+D$6jz1M=hlnS&Yk z5Ehc1Cs#YMNkL`0dmbT_X&8ZB`b9{FA1CU!c81HESv@H|TFP8N7^5i@^_W>m*a>!I zIXo&&S#sLI&QewT`;Fly$VdbL0Lyl@uc0`%lR#VUH?lT^xr=CG;7NJv4jDW58h27V zlIAj%J}Ao6I9-QHvQ48XyA_8)@{i>^Hh{sf2tVOUvA3?cx~^hOaz6BuD%C4vgB7fk z$D+#Kl5rtNQcq>l^L}U36{k_FU*i&Ls;x*TGOL1g`cM0qBzrjLLZnvg=OnY_RrSg6=~ljqmV z%<4>LVJ5jcC+CpE^m^GSj2>qTvI<&cFB=MEoq3(;eVVnr1J&@FqF8<9RbmkT#$w-3 zeJD!jWo0ZHa4>w)sXS&GS+Jk-7)*k|3Jfs+gSOdcRc+MRea$WV$L(1X2PmI&BLoe) zbLutDE;lx%ZD8ofTpx`80dW7i?4cBSTYzC6s>fHpEjTg08o}&u6V^^!<`V%fCxkm6 z8={zd|60nM3TK0GKA@V_>|-yH4Yy0DPQuVI!!W+KiY6~G2Hev885iz1LC8?wNWnR_ za|{0^ub7?W(e{b>^BWtfOd>*vqz(a*On0Sz?FDn&>8lWtOXghcrUx<6ZO2q9g1EU$0Bv!KXB#Izg(En;?}~B zw_|}zM}pe@xci=Dqf@>_ywiAieun#);TV?=m`$6?wpYU@`Y*W#*8UaTL-q5+0|2w5 zU4l}?pGM@!m8@Z{;|EGicHU8_&2uF{OvjLDG2f?##4VW6Q%quUNt0ag(J7`e;a33p z{|^IyLB$&N5k@?fcMkk4{^sAlXjK!O+&HVcRVq88tv^)`!nkd;*POlMtBJwc!xGh8 zg^>|Wil2xCW^8_0PE_Mon=hn2BkYT52#!E8&@>3s9>ol-!FDQfj~3cXjuP-RcO-Ko z$><592tE~N3NN_gfnF*&K|H-a9>#Kb67`@!U9K}yrS zIq_XvOF#Bmi&m8qw_KQ-TV-LYfg~PErGrF*bBADd(kydI`PV19rFXt+w}?k?{5||O z!G0rk#1AtDfBkzcOEHy$B9DuJCkDxrZQn2(3Ck%4rq90<={IhLK)2d>>*Ui28!L~V zu-ion_14vJ2$(;x=f#yh-ucui#{}-;6;CQFNG&j;z<-$YA1$ zT@$ic;b|^DeuV_^mBk~=Mtsto5q0us)%;_|Vra;d0SsS|d}%UztJV;`im`GsfQ*6( z5GHoGO85wC=2E@rUD4^6U&UV=4inAHGFsIUDB7w@dj5=_uD}7dx%~VrQ!*rlh(Ln` zMPMjSzz7`TPCAqD0$u-{$btqGl(vYQo<$Slslnszi| z2l!OQO>NoRAJN#6(aKX97|@?+XZmU#NTcE*Vw3)}krOe-q=8Pf06soh3s%uMWdiYC z+6$F=WAO@6jG#FMv87X4-W_5{inv;sk8*}S;^p)Gl#yzr-`>0BXRA7fi)wuu4R)N6 z09`B?IQq>qbpn31fQs?OBs6ebF&<_&(nL^zE9b?N!~`$zgBrn_nn@LHErWtRv3&Cs zyhmJ4=73J-l3teeYTAFXi>njv-bMT9?|H_{pz2GVT9{@GOV&AVc(^`?SxYxe&@&i zU(1xXrO@Fo?DrZf(Xsud!dJ|2qS9)#9{Bi{vFLLy8k@_I+V)v}aZ8(L*S3Qw z;xsCIj$M9U%k7Gsd6+Ta8!_wyJjP9iu@SM9Du{ok67R72)F7t|=7}#|PJk|X@gs&O zY9eTK%L-}k63Cr_f0f@9oK+<81Yn>uGKzB323U(q8@ve7Q_aNE3M&u}7r)OhDjB&= zXYi`7lZDQt=XV9Qz zRo8(ZH0YhjHK;HLfJSv~Cz82seuGlAEx!SApEovdwS(Tmjf- zA_~boxoq_Zzcs+KsjmYwooHsb3U`;mG}^_qyo}hxH?j?jG?n%eHZsdO;n7eTX$(vE6Eg(%;eiHl)V~{vt&7)V+w7~>0Yj( z*$PDI9&$~}thzqeUU#W>5?=ba+qoDIev1G6@IozQlga{(af++IS%3tQMyvPhNccOm58P_ zZ9TGQo(z#c5HVYY$OSJ$725SYk zjmjHZB;2suIuFEh{X{k9UZNF&Jp8Sl77wh^&_ctd-iM7}f5+F7->&RjXjxAh4e>#5unle@p} z{<(Vn=z{xNN^L#TDOI~t_P$5Hh3Vzzo>vx85Ko-)GXT~pq#S0|_2=)?XRQ9{qK#{< zpIz1-zMP5_X4rg{2k>+$)M!{#?lB2JrrWF*QBnhy84KCPEZj3(nA5Dw(4eXrTVjM$nQ{PTlHTEG>*eEDmO?ISiN+ASEWM$$y}p!fMD4xS z12(3`3!q;#J??}X5vh9%lXmB9Hve3_F_tUiFZ`f&Ib2feq|R_7`o;?NDWf2?W8g!i zim+eS*BgZgzkf=WIbJ85_ZqbLinn^8J=$|5ypUFffz;eJh*kG%;rdkQHR27(y+_P< z;JAQrxto*Yx;J|!?o`%WKnFU;3ICyjX(-4Ps~zynH~P$(neN>C7uU=vi~{OL>n7Qk zh;d;AsCola?sBo&IvI1avN_G)WxJ4$t=M||AhR*zJ(XGk^$F#6Ab!Ioe8`FZ6q6xK z!|F5KK0sH{S{L*o5r%`D(ag~O3aJg2K-1@VfWNRrG1F7Ev~v&EP@xDo4?RfSr;%{2 zpm6XM@`M6SCHe3^rq~bG_#~<$p2!G`Au{bXsF@Fp^;ihZ3l!&A$DD_)f$p3cBBJnA zp3w&~h%^kXLigH+ADRcxta|xQ^~6Q)NE^*RXg>1JqGd@seQh1bIWNAYzv5E5EXT)GG}`lYHnd%diYu_ zloUBXMB*Am<}XQ79i+Y;$T7O^0`3xm;P*%?LHdv~`HVUNxqJ^ML(GVfIE34!etj!8 zf}Yy@qD2;CHwtQo8&`E)P#s4f#|FJVI>=So)xc;P{Q!uiYdehae+S5y_C&{9|rVc#glM}e6u*&s(uobk_r zww&OtgnZ-jEf(JEzQekKGK$0iwaQ$^V?liurE_I*Es1j4KjJ&v=xG2&w(iGa5Y(6YVwfUJL1q&O!6Daz5#t2Cx z`4KxY5u2{kfA0YZ*8b&^);zdInRVNvm4zaX~_s}s@tgq9~q_K-7Dn(RZ@(H)uy zRien+>$OYXM+^^ch3xFvoH{&0<2wHF<96vxWNp@!$_p=)xIH?mVWuUI?LW0oBWAwD zgABGQtPsdg=IUS(4qdjkXq;>mtnq;;rN~g>66Nqq_DwW}>6qc4bF-E*w-=oLkP7T= z7nSf#!BT|?Iuxl3pYu;CP?~I%h}zy7QVkSH8QN>SF4#8%0ui|NFdi;LrotTnlmVbUu$Iv5$A}de9$N)92xrhMr0j;aXEA7r9be2`W6Lg7=<^d|qPL{QWCWUz>ik z2))dDNl>=D8ksoyVTqtEh2m}IMy+UfCSbr=)AUs%YVM*kOJuFue&7SSMqe1g<`uVi zWbYVJD;|Qbdg&LSHZcUly;B6frKlR(@|r)TruaB6*DXD8ZyHx2v(}Av%JwBM=|-SM z;nrf|6RLoY6oxSh-cNv*Ue0oBlPN|9=ZuO5XKzOZ@wD^WaZIn6xnTklfV{EFjb$P0 zP#dx}nXAxp>3~`y=gA$&U{NY5Wnuy5(Eeyh-q|}xfmA1YY>Wz`NaS|(i1MNR2(|ma z^m_2*F~Ms(^4vPK>HwYYR)ymxz=XwCt%MYYIwP@Bn1O+5gVeJ%|5E@NK)_}d^=XYR zMF)$7YrpPE3#^UcXwH&7o7i`%>n(+7vm=6W=5xA2o&cBxcC39-)`;)f;Y^gF**pk;MPK6?ooZ~rIXxD0iJyO>mPoWcGw7PFQ@DZ%R1BAs zi<7+05#<_-{uiKE8;3q_8L^yJ)cj@R6rmZR+nnU}hhBZPV;XZ~#MsW@ z;ppz@o>B$`q#o)*P(Gw7+M^T|Zk@(zF58|xVrTfbG0X!2UZJ$Io!_22u>_Kp0&tCW zessM}W8lz)cl8U|`Q5k=?*n#P2@8~*SyloAGZ@eHaAKI{GLJEdTJ3nW6t(?2WK z>Z;5{CA) z_7bEi>a4m!Xv)3b#&;2;dWjLImy)C9EmG(~~{8DTpJPbgTZ4RnQ0@YZ^(1fmAz?$kv+? zS4dnbwHt<0VntP_juj>GmSM;>bPmF-6+opEm8n%w{IqN8viwM1sKb`0mE3bLGjacL zfh;a^{U_Ek{}8S{BKHFB8mNma3lGl@DmAlGPfl$sR5ts|KVazTWyZBw>aN@DV z3d}_2*+ro8QLf~!*B`kSh$4Nk#vLLm(B1|MSP)JeCj7|BZSsw zz2qV8j1|C8;J2=E5>o}e={|=;<5 z@!#hC%!;VibAwawaDSmIBpsle>?eq`tjN)0G$L#16-_etL48tg{epUSsOfe9pmVrr z3Hvv}fkEpzr81|8ONTzwkI+AZcT1(exlP}GxS36)>s_5yRMLJN`jeRLTCBaqGidf} zN1AnU!V~yVZwbK-gR%(I*Kl+tX_-ub-8Zu#VQ)sK9}HmPk#BX!LM|D?Yt{P?-|{YY zHozUQdxDI(-^<@(&AR)|L#NM|wd5~Cl z2XgXM)$kdrgveNMF^TqdU4N94L=aY}71u~b^E}YM(e+ai8Ecc<%w|6r&C4~J{`=$7H) zPg#2e_Sz>1I;E+toQ8jp9PEpR^#IP3CCxA3)coHAGL5B*0BFwP(l1OsKLq?1e z!ooO(5pJeVX=0+^Hee#`KAl)09vZeBl(sz_$l6}U^HN?dIgQs_W{~Z4*MpEcU9R!> zwJMCia+!)N)3U&@Q{c-kb81Rf0^s}#IDcW&cWU^9hg}qj+G-+v-LMNAURnRqp4L{d zaXzv00HS?}T-aOSee<)~%8-WCBhBy;ctrB-DB^5_aB>K)1uu==Gqm;RH0=;G6%iF1VwHV7xeB3Q91_fcSGd5&drWJ37;^6hLS+=LN%{DefKR)ULbG5ZT`QQR% zt$<=sLv_=*A{1SwJru-eLDe?7P^LyNqpn@AG#XeQ4R`CrY)=a2fsxN!sv^Y9KoNhAT7@51 z2m~8&q9{|z+m*qFGTbumw*>g4##sV@o_77~ybtjmamK%l-n%za%V?>0He;L!G-i0#=z%BL2< zezp#>Thl%@OmaL!WqFVk#ubA$0MwCl*8=4Hnfldq^}lmRTv~Yvb(sB=cklcV`Mb4d zkV2B+b0{o_9YC|eDuf0PccCS%?`S|N-E*uI{&BoOIZnc!&_Rc5mpRmD>+EnB4vFdr zcizTkQHz|1Jt(Aza0@}^7r%H)u4~* zo?baql7ZTzm>=RpJNSRhwXGOebsy&}pMa{HCSQEb7pz3P*T3dsg1wKB2mcl^O}3%q z%&XwqdTfe-EAI;puXAd4DJOi&B*xRJ!oNm^XAe|-y^V^6O2R+>HV!r3;DG?0G^rZ) ze_STuAakJ#*qH1j0LEvh8cd*x25qdc$1-j&CV(-xWWJU%PV9>Ig<}t>0}vRV-`v*h z!O@W(@{uArwUljo)vRnynWN7jM=u_(;TeQGZ1qcHC4lmht_m<(t<^%(d4Qy6% zcQMeC)WNt@F@Ob~5vF@V&tV}Dk_sMI6c+I~w2nx=d5#%KbLhb=%;fgMnMK<2>ud$Y=a{&q@0n{5|8zggXXaG^L$@4CP*!moQ^D`i$qgT_a6BUqKk| zr}d^>>BL|ONTGc$(|IP^Ta)#DHMKSC3ymkb(CyB+BQ(BtkkFhbFrt)Kw2jk2Ti=LM z0lmf2uJ|}^Zx(LOZhRyQz{vn-R%RYAIv!XAnluCmQa@Cw%d(cCLSI|YnTjZLBV;yr zfIMt67zT~c$ks%(l5;XirO6n~U~sL)K~{$ORFAF|`8O*?*0W!Sy+WAw%!1xwP;PM4+M9Q!MZH^ zkz^@xVsfN)E7%OXG7BG}W*fjaj{lsxNwH`^ng!N%3Uqn2kId5(C+F;S&xt5gxO_hW zfd!dc&m)6`8rf6uhKT=If1_~&OdMRi^mwotaV)_o@!#vm=3i|~;(&c|AAl+|l5MNZ zt%Hni3g)?B(-+>L2Vt+E=ZD*fUN-NkxaTzRln0;j>J5p_&!~`5cqC$CkH8o40Z6(}nXMU&8k~?Ndje zYkt-F!mk$d4~yX^@b3-b=Xvy{-|dF}w!i8`=jUaElUp>+!uG@$ha2LeLJD5s2n9Y- zuqXAys~3A3RB0x7t4}6XdD*HT-~RB3f)dOhrgotH-q3B*v4P%|W~|~7O@U&Zti?x2 ztZ%$WX;eM=*tIlgEyEk&BqxYKCO!!)0-om(OD3W!mrWyuaza>j@AS% zn~%KTLYN=4hsQW&z`hN49>J69+E^iKV-yB5B>!TZFYJ-|`fhfOras%Le%)tEQ0YPB zKG;XZ27i&Y9O~#>2mW8~)WG7GklQ7RJdpC0yhl-YYa7d44nt=08+hQm%KBzw ztUKyTVLe$^W;zhHT~~k&0$N}r82rsH#0a;GBBv^t6FJli?*1VV(JmT)2A`y9lKax9 z@bACkw+Ot7@m@EgKW4(iMp*?;DaA3%ye31>V&+)3ReeHsubC1|sI|J2cqIx`r{QdW zjF?U^dmKGVvWhUQmih{nY>u%5+Pu`d00mW+l+%e0i8l9+kR)aT1bQHA&f!;fUs9U; z>EmcUa-`Es2A}yBV8*gbfs9Wif+0lwWcaF-lp_jzeyKmz#(dWvQ@ zYATdbjnS?9OJ_eO-nCZx*Hm5YQo~wYQ9VnG9&z!-EIQhp5QPcL9)lIMD$0#?Zkh37 z6}X}<|Gj7Sj#x6Rl|E0$@dtC_!M}OBY|GzHYr_@x{rZ@kC4=M|koxRq)WvH3H?m@9 zy>pM{VDF$%I}^>ivlE7mbh^p#(whm;oFh6<@&3r1^(Kf(V6E5IU3=JaE^oD@`7Nmz zuE!2z#fWF&nInk!^?ETHZ|}^S@L}0dJFUuYM)Rcz$ZUa_HsmmePfzF9ddp zt&gD2m5=CKq-ul?Y(&Hgi!s~_C6|7etP zt8T{0j|OypZ=#Ib94)KX%JrJ(l!v#!zW0IYK}~D4a`hWdX}D{_-AO&$Y%l5))MnW0rTA?>D>AoUGStR&%RqE5-}jG*hmSq`%$4g zR>`ke;FSB6Ham&FpA442NA;u5t}6KI?zSI8e*d5)Apv-y{}XJzK?4LO+yvXm7=&x4 zRE&6N2wCG;4W2Ctl*~di*w`JDWb-F#_h^UZgTi?c=zwkT^ytB1obzz9lUCeAi{y;7 zg#sc>v~bjQji_rrH2kx?=J@^&KWWvs-FFboT%PjFO3H!?iM@$>hf+5APpUn$r~n&7 z6b@ih`L8qjeCUA2&iQV*AiUU|ip;D`z`5QQ-SAm?{ZBKcTXI1gB+8>UuhM@LsvpO|=~ zkCVz0jR=nPwJ}Itx!;;TTboa{0f;RFoG;C>j6y1usFr>VCTfme&J-+*dBBG#JR zKp6S0J3Urpv;J5Qpghjn{POYuf2NgnZw|k?DwKCA&l~%y+Hi*kf;7lomUc z*!9Y}HnAjc#TrRrVN7|qi=Dg|EGk+y7HU$hU^q>9m-+yVw*J8zuF=-O3pIqF37V7J zENL;Lp*AvnD1*P%i7z5XU%WmypcJ+gqEJn=S%7nq!FoRx0~zE`Is_1viNhf_G zTQHIQZQsa??2vEX@2w~uYmfXz2LL8m_nNmopC+xOB=x~=eP~@s`$Z9gq0pG~M~t02 zTgR_yaq6k{lIGk$;8mup*0p(A6_Dd(l_NaTNK^cDq`Ejnjs&79y>4_{uyP3 zO5iV=d3)16`r=k}zhthQs?x>&MKzt&Qs7u^j8f=n$su$}>guCFt6KlcjjD2OP&A?o za&Ym)W`b=wX6Oq7Ec*nN*Oin8LJJU%z7V*Miy%%^gFBfL{@94uxx`+R@&VeIQgTo3 z)WSg}B~u4Xy274~(F|wq`!m)Y2g9E(Q6_65FhhKb*;0ttOz~__UA8SA#dh9sHb+a@ zWjHx7TIR&Ha#Xz=j&gy+x7j<(V+>4-iWda)EuO6PGhaj3!|5n_y1TJ8)g>iV?(9oi zU;fV7VwpO`GF2K_Vi&HmIHx;kioTDNKAAJFuw%(o2Txt-I)rDkdg~1_fq`?m;%}tu z7I9?OG?hzF?UHtPh$S=i70!?u&v?;A#Y z8*e8TJ}qAk4gyE4%6! za$yShV<4@-qCdh4w@>4FCpK^1z^O;WDnvOhEZMkf_3W;EEx>T{1@y%fKK(Yej=(8uA`iG;d@Q17>QPLxqCDHups-fH z$wJp$M=Cp1cN1%P3<8i=IQ`a31YpRwP4O8?h_zfxR-0 z@0`TBKxBE+U4NF_t|>d0qYJg{={-1vhkg28ko|W==1;7|lM{dTSz*(VkPhmrNw78f z6e?sD7F}FyGYUtp-Bt3g=%>5g#bN^M9#~w6YxsbsR+<`|GQ-20FU2Ge)yUP_;lsfz~IWn{|>8Z`CP<#lTj(SBQNJpMn#v~}nsVvLc) zEuJ0|r%!*CW`_Fy@kmK}VSc7FJ1+sslw1(j%G*8x zo3u_q_>!s&Pn;`T)?^g!X$Sl&+pV;Q{bf5D{z(9g(7XfIy}su1wHU&YB0crcAPvpk zDw9W4bbhG5^CSdPcoov+pKDvJ3YQ;~%se)l6nK>TobeXG`yeT(6zW|S@>3dk%4OXB z(-p`EnBzIi9Lr0Za}PbtM*S;+A;DyNvM8heW0z#;omj!4j4q<64e5CzOUM5(cbc*(lwBxn`0A?rRWK z@PWWBR%vPZ$=J{X?n@r>-Y3C}(uVeaf@P3(^(Xoe2?%s#lxYwGKSx~ix~c_Y7LEyV zNxq1_^{%|yyUpJutr-9X^;h<(_7f1%l_}s_E2x?J z=TU$n(2D4%cnDD;F z2l);>Lh_}jxP6OVZXI7efYHBtXL2}40HkquhX9fxdrpz6;L`w_&h*5Yymf~cv^y9> zz6B=p7q1cnFnAbFroPJ0>0m=U4W3TMkc>MV-0nN4{DYfTFT(S&3`&4#WCCOrA9g7+ zx>cW`Y>)O;#=%u$Hm*kCOah6-aZT#QlcKs~W5|qgND+t#o%Hz|S=kl5Y_)jn9IVkw zgmKak`3EmmIIj7gmMW~&Sh{t0_V7;a%yNJMGitjeekj5qI^?bqCyPJ^VLt=tSL?pAqR-VU1Kwvx#Ih=$L~$dR}`=&D9=D_hb8 z%h>dx)HO5oF^$?4AGxj4Pj8sRP|CFa@V2?Ye_5ZyC~@7(N3aerno<$w%8@Z@1^2! zW+MWy!#)$K_@Ash;~~!8Yoh_i$P?#YIxJ)+vm#e)Nda(qXeynSGmiAv4^7}7AiX?) zXqluhj>2SH`)E6kSlmKj%QQx>TxIj>7!B4&9&tQ)L4Xqi#vND2hH>snQCY2Qc(G zm{vq7#y@rqX_$G|O<4$~<;_v(p&O?zZ7O4M?LB&wnVkvzM;kzPcaRP|+$W@oKPCi2 z&>Q!Ogl>xBU~?S4;P$}NrjB6i+k1_-^UI~~mE0A{IH3>kIF>?R#y{(p#&&AI +#import +#import +#import +#import + +#import "client/apple/Framework/BreakpadDefines.h" +#import "common/mac/GTMLogger.h" +#import "common/mac/HTTPMultipartUpload.h" + + +#define kLastSubmission @"LastSubmission" +const int kUserCommentsMaxLength = 1500; +const int kEmailMaxLength = 64; + +#define kApplePrefsSyncExcludeAllKey \ + @"com.apple.PreferenceSync.ExcludeAllSyncKeys" + +#pragma mark - + +@interface NSView (ResizabilityExtentions) +// Shifts the view vertically by the given amount. +- (void)breakpad_shiftVertically:(CGFloat)offset; + +// Shifts the view horizontally by the given amount. +- (void)breakpad_shiftHorizontally:(CGFloat)offset; +@end + +@implementation NSView (ResizabilityExtentions) +- (void)breakpad_shiftVertically:(CGFloat)offset { + NSPoint origin = [self frame].origin; + origin.y += offset; + [self setFrameOrigin:origin]; +} + +- (void)breakpad_shiftHorizontally:(CGFloat)offset { + NSPoint origin = [self frame].origin; + origin.x += offset; + [self setFrameOrigin:origin]; +} +@end + +@interface NSWindow (ResizabilityExtentions) +// Adjusts the window height by heightDelta relative to its current height, +// keeping all the content at the same size. +- (void)breakpad_adjustHeight:(CGFloat)heightDelta; +@end + +@implementation NSWindow (ResizabilityExtentions) +- (void)breakpad_adjustHeight:(CGFloat)heightDelta { + [[self contentView] setAutoresizesSubviews:NO]; + + NSRect windowFrame = [self frame]; + windowFrame.size.height += heightDelta; + [self setFrame:windowFrame display:YES]; + // For some reason the content view is resizing, but not adjusting its origin, + // so correct it manually. + [[self contentView] setFrameOrigin:NSMakePoint(0, 0)]; + + [[self contentView] setAutoresizesSubviews:YES]; +} +@end + +@interface NSTextField (ResizabilityExtentions) +// Grows or shrinks the height of the field to the minimum required to show the +// current text, preserving the existing width and origin. +// Returns the change in height. +- (CGFloat)breakpad_adjustHeightToFit; + +// Grows or shrinks the width of the field to the minimum required to show the +// current text, preserving the existing height and origin. +// Returns the change in width. +- (CGFloat)breakpad_adjustWidthToFit; +@end + +@implementation NSTextField (ResizabilityExtentions) +- (CGFloat)breakpad_adjustHeightToFit { + NSRect oldFrame = [self frame]; + // Starting with the 10.5 SDK, height won't grow, so make it huge to start. + NSRect presizeFrame = oldFrame; + presizeFrame.size.height = MAXFLOAT; + // sizeToFit will blow out the width rather than making the field taller, so + // we do it manually. + NSSize newSize = [[self cell] cellSizeForBounds:presizeFrame]; + NSRect newFrame = NSMakeRect(oldFrame.origin.x, oldFrame.origin.y, + NSWidth(oldFrame), newSize.height); + [self setFrame:newFrame]; + + return newSize.height - NSHeight(oldFrame); +} + +- (CGFloat)breakpad_adjustWidthToFit { + NSRect oldFrame = [self frame]; + [self sizeToFit]; + return NSWidth([self frame]) - NSWidth(oldFrame); +} +@end + +@interface NSButton (ResizabilityExtentions) +// Resizes to fit the label using IB-style size-to-fit metrics and enforcing a +// minimum width of 70, while preserving the right edge location. +// Returns the change in width. +- (CGFloat)breakpad_smartSizeToFit; +@end + +@implementation NSButton (ResizabilityExtentions) +- (CGFloat)breakpad_smartSizeToFit { + NSRect oldFrame = [self frame]; + [self sizeToFit]; + NSRect newFrame = [self frame]; + // sizeToFit gives much worse results that IB's Size to Fit option. This is + // the amount of padding IB adds over a sizeToFit, empirically determined. + const float kExtraPaddingAmount = 12; + const float kMinButtonWidth = 70; // The default button size in IB. + newFrame.size.width = NSWidth(newFrame) + kExtraPaddingAmount; + if (NSWidth(newFrame) < kMinButtonWidth) + newFrame.size.width = kMinButtonWidth; + // Preserve the right edge location. + newFrame.origin.x = NSMaxX(oldFrame) - NSWidth(newFrame); + [self setFrame:newFrame]; + return NSWidth(newFrame) - NSWidth(oldFrame); +} +@end + +#pragma mark - + +@interface Reporter(PrivateMethods) +- (id)initWithConfigFile:(const char *)configFile; + +// Returns YES if it has been long enough since the last report that we should +// submit a report for this crash. +- (BOOL)reportIntervalElapsed; + +// Returns YES if we should send the report without asking the user first. +- (BOOL)shouldSubmitSilently; + +// Returns YES if the minidump was generated on demand. +- (BOOL)isOnDemand; + +// Returns YES if we should ask the user to provide comments. +- (BOOL)shouldRequestComments; + +// Returns YES if we should ask the user to provide an email address. +- (BOOL)shouldRequestEmail; + +// Shows UI to the user to ask for permission to send and any extra information +// we've been instructed to request. Returns YES if the user allows the report +// to be sent. +- (BOOL)askUserPermissionToSend; + +// Returns the short description of the crash, suitable for use as a dialog +// title (e.g., "The application Foo has quit unexpectedly"). +- (NSString*)shortDialogMessage; + +// Return explanatory text about the crash and the reporter, suitable for the +// body text of a dialog. +- (NSString*)explanatoryDialogText; + +// Returns the amount of time the UI should be shown before timing out. +- (NSTimeInterval)messageTimeout; + +// Preps the comment-prompting alert window for display: +// * localizes all the elements +// * resizes and adjusts layout as necessary for localization +// * removes the email section if includeEmail is NO +- (void)configureAlertWindowIncludingEmail:(BOOL)includeEmail; + +// Rmevoes the email section of the dialog, adjusting the rest of the window +// as necessary. +- (void)removeEmailPrompt; + +// Run an alert window with the given timeout. Returns +// NSRunStoppedResponse if the timeout is exceeded. A timeout of 0 +// queues the message immediately in the modal run loop. +- (NSInteger)runModalWindow:(NSWindow*)window + withTimeout:(NSTimeInterval)timeout; + +// This method is used to periodically update the UI with how many +// seconds are left in the dialog display. +- (void)updateSecondsLeftInDialogDisplay:(NSTimer*)theTimer; + +// When we receive this notification, it means that the user has +// begun editing the email address or comments field, and we disable +// the timers so that the user has as long as they want to type +// in their comments/email. +- (void)controlTextDidBeginEditing:(NSNotification *)aNotification; + +- (void)report; + +@end + +@implementation Reporter +//============================================================================= +- (id)initWithConfigFile:(const char *)configFile { + if ((self = [super init])) { + remainingDialogTime_ = 0; + uploader_ = [[Uploader alloc] initWithConfigFile:configFile]; + if (!uploader_) { + [self release]; + return nil; + } + } + return self; +} + +//============================================================================= +- (BOOL)askUserPermissionToSend { + // Initialize Cocoa, needed to display the alert + NSApplicationLoad(); + + // Get the timeout value for the notification. + NSTimeInterval timeout = [self messageTimeout]; + + NSInteger buttonPressed = NSAlertAlternateReturn; + // Determine whether we should create a text box for user feedback. + if ([self shouldRequestComments]) { + BOOL didLoadNib = [NSBundle loadNibNamed:@"Breakpad" owner:self]; + if (!didLoadNib) { + return NO; + } + + [self configureAlertWindowIncludingEmail:[self shouldRequestEmail]]; + + buttonPressed = [self runModalWindow:alertWindow_ withTimeout:timeout]; + + // Extract info from the user into the uploader_. + if ([self commentsValue]) { + [[uploader_ parameters] setObject:[self commentsValue] + forKey:@BREAKPAD_COMMENTS]; + } + if ([self emailValue]) { + [[uploader_ parameters] setObject:[self emailValue] + forKey:@BREAKPAD_EMAIL]; + } + } else { + // Create an alert panel to tell the user something happened + NSPanel* alert = + NSGetAlertPanel([self shortDialogMessage], + @"%@", + NSLocalizedString(@"sendReportButton", @""), + NSLocalizedString(@"cancelButton", @""), + nil, + [self explanatoryDialogText]); + + // Pop the alert with an automatic timeout, and wait for the response + buttonPressed = [self runModalWindow:alert withTimeout:timeout]; + + // Release the panel memory + NSReleaseAlertPanel(alert); + } + return buttonPressed == NSAlertDefaultReturn; +} + +- (void)configureAlertWindowIncludingEmail:(BOOL)includeEmail { + // Swap in localized values, making size adjustments to impacted elements as + // we go. Remember that the origin is in the bottom left, so elements above + // "fall" as text areas are shrunk from their overly-large IB sizes. + + // Localize the header. No resizing needed, as it has plenty of room. + [dialogTitle_ setStringValue:[self shortDialogMessage]]; + + // Localize the explanatory text field. + [commentMessage_ setStringValue:[NSString stringWithFormat:@"%@\n\n%@", + [self explanatoryDialogText], + NSLocalizedString(@"commentsMsg", @"")]]; + CGFloat commentHeightDelta = [commentMessage_ breakpad_adjustHeightToFit]; + [headerBox_ breakpad_shiftVertically:commentHeightDelta]; + [alertWindow_ breakpad_adjustHeight:commentHeightDelta]; + + // Either localize the email explanation field or remove the whole email + // section depending on whether or not we are asking for email. + if (includeEmail) { + [emailMessage_ setStringValue:NSLocalizedString(@"emailMsg", @"")]; + CGFloat emailHeightDelta = [emailMessage_ breakpad_adjustHeightToFit]; + [preEmailBox_ breakpad_shiftVertically:emailHeightDelta]; + [alertWindow_ breakpad_adjustHeight:emailHeightDelta]; + } else { + [self removeEmailPrompt]; // Handles necessary resizing. + } + + // Localize the email label, and shift the associated text field. + [emailLabel_ setStringValue:NSLocalizedString(@"emailLabel", @"")]; + CGFloat emailLabelWidthDelta = [emailLabel_ breakpad_adjustWidthToFit]; + [emailEntryField_ breakpad_shiftHorizontally:emailLabelWidthDelta]; + + // Localize the privacy policy label, and keep it right-aligned to the arrow. + [privacyLinkLabel_ setStringValue:NSLocalizedString(@"privacyLabel", @"")]; + CGFloat privacyLabelWidthDelta = + [privacyLinkLabel_ breakpad_adjustWidthToFit]; + [privacyLinkLabel_ breakpad_shiftHorizontally:(-privacyLabelWidthDelta)]; + + // Ensure that the email field and the privacy policy link don't overlap. + CGFloat kMinControlPadding = 8; + CGFloat maxEmailFieldWidth = NSMinX([privacyLinkLabel_ frame]) - + NSMinX([emailEntryField_ frame]) - + kMinControlPadding; + if (NSWidth([emailEntryField_ bounds]) > maxEmailFieldWidth && + maxEmailFieldWidth > 0) { + NSSize emailSize = [emailEntryField_ frame].size; + emailSize.width = maxEmailFieldWidth; + [emailEntryField_ setFrameSize:emailSize]; + } + + // Localize the placeholder text. + [[commentsEntryField_ cell] + setPlaceholderString:NSLocalizedString(@"commentsPlaceholder", @"")]; + [[emailEntryField_ cell] + setPlaceholderString:NSLocalizedString(@"emailPlaceholder", @"")]; + + // Localize the buttons, and keep the cancel button at the right distance. + [sendButton_ setTitle:NSLocalizedString(@"sendReportButton", @"")]; + CGFloat sendButtonWidthDelta = [sendButton_ breakpad_smartSizeToFit]; + [cancelButton_ breakpad_shiftHorizontally:(-sendButtonWidthDelta)]; + [cancelButton_ setTitle:NSLocalizedString(@"cancelButton", @"")]; + [cancelButton_ breakpad_smartSizeToFit]; +} + +- (void)removeEmailPrompt { + [emailSectionBox_ setHidden:YES]; + CGFloat emailSectionHeight = NSHeight([emailSectionBox_ frame]); + [preEmailBox_ breakpad_shiftVertically:(-emailSectionHeight)]; + [alertWindow_ breakpad_adjustHeight:(-emailSectionHeight)]; +} + +- (NSInteger)runModalWindow:(NSWindow*)window + withTimeout:(NSTimeInterval)timeout { + // Queue a |stopModal| message to be performed in |timeout| seconds. + if (timeout > 0.001) { + remainingDialogTime_ = timeout; + SEL updateSelector = @selector(updateSecondsLeftInDialogDisplay:); + messageTimer_ = [NSTimer scheduledTimerWithTimeInterval:1.0 + target:self + selector:updateSelector + userInfo:nil + repeats:YES]; + } + + // Run the window modally and wait for either a |stopModal| message or a + // button click. + [NSApp activateIgnoringOtherApps:YES]; + NSInteger returnMethod = [NSApp runModalForWindow:window]; + + return returnMethod; +} + +- (IBAction)sendReport:(id)sender { + // Force the text fields to end editing so text for the currently focused + // field will be commited. + [alertWindow_ makeFirstResponder:alertWindow_]; + + [alertWindow_ orderOut:self]; + // Use NSAlertDefaultReturn so that the return value of |runModalWithWindow| + // matches the AppKit function NSRunAlertPanel() + [NSApp stopModalWithCode:NSAlertDefaultReturn]; +} + +// UI Button Actions +//============================================================================= +- (IBAction)cancel:(id)sender { + [alertWindow_ orderOut:self]; + // Use NSAlertDefaultReturn so that the return value of |runModalWithWindow| + // matches the AppKit function NSRunAlertPanel() + [NSApp stopModalWithCode:NSAlertAlternateReturn]; +} + +- (IBAction)showPrivacyPolicy:(id)sender { + // Get the localized privacy policy URL and open it in the default browser. + NSURL* privacyPolicyURL = + [NSURL URLWithString:NSLocalizedString(@"privacyPolicyURL", @"")]; + [[NSWorkspace sharedWorkspace] openURL:privacyPolicyURL]; +} + +// Text Field Delegate Methods +//============================================================================= +- (BOOL) control:(NSControl*)control + textView:(NSTextView*)textView +doCommandBySelector:(SEL)commandSelector { + BOOL result = NO; + // If the user has entered text on the comment field, don't end + // editing on "return". + if (control == commentsEntryField_ && + commandSelector == @selector(insertNewline:) + && [[textView string] length] > 0) { + [textView insertNewlineIgnoringFieldEditor:self]; + result = YES; + } + return result; +} + +- (void)controlTextDidBeginEditing:(NSNotification *)aNotification { + [messageTimer_ invalidate]; + [self setCountdownMessage:@""]; +} + +- (void)updateSecondsLeftInDialogDisplay:(NSTimer*)theTimer { + remainingDialogTime_ -= 1; + + NSString *countdownMessage; + NSString *formatString; + + int displayedTimeLeft; // This can be either minutes or seconds. + + if (remainingDialogTime_ > 59) { + // calculate minutes remaining for UI purposes + displayedTimeLeft = (int)(remainingDialogTime_ / 60); + + if (displayedTimeLeft == 1) { + formatString = NSLocalizedString(@"countdownMsgMinuteSingular", @""); + } else { + formatString = NSLocalizedString(@"countdownMsgMinutesPlural", @""); + } + } else { + displayedTimeLeft = (int)remainingDialogTime_; + if (displayedTimeLeft == 1) { + formatString = NSLocalizedString(@"countdownMsgSecondSingular", @""); + } else { + formatString = NSLocalizedString(@"countdownMsgSecondsPlural", @""); + } + } + countdownMessage = [NSString stringWithFormat:formatString, + displayedTimeLeft]; + if (remainingDialogTime_ <= 30) { + [countdownLabel_ setTextColor:[NSColor redColor]]; + } + [self setCountdownMessage:countdownMessage]; + if (remainingDialogTime_ <= 0) { + [messageTimer_ invalidate]; + [NSApp stopModal]; + } +} + + + +#pragma mark Accessors +#pragma mark - +//============================================================================= + +- (NSString *)commentsValue { + return [[commentsValue_ retain] autorelease]; +} + +- (void)setCommentsValue:(NSString *)value { + if (commentsValue_ != value) { + [commentsValue_ release]; + commentsValue_ = [value copy]; + } +} + +- (NSString *)emailValue { + return [[emailValue_ retain] autorelease]; +} + +- (void)setEmailValue:(NSString *)value { + if (emailValue_ != value) { + [emailValue_ release]; + emailValue_ = [value copy]; + } +} + +- (NSString *)countdownMessage { + return [[countdownMessage_ retain] autorelease]; +} + +- (void)setCountdownMessage:(NSString *)value { + if (countdownMessage_ != value) { + [countdownMessage_ release]; + countdownMessage_ = [value copy]; + } +} + +#pragma mark - +//============================================================================= +- (BOOL)reportIntervalElapsed { + float interval = [[[uploader_ parameters] + objectForKey:@BREAKPAD_REPORT_INTERVAL] floatValue]; + NSString *program = [[uploader_ parameters] objectForKey:@BREAKPAD_PRODUCT]; + NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; + NSMutableDictionary *programDict = + [NSMutableDictionary dictionaryWithDictionary:[ud dictionaryForKey:program]]; + NSNumber *lastTimeNum = [programDict objectForKey:kLastSubmission]; + NSTimeInterval lastTime = lastTimeNum ? [lastTimeNum floatValue] : 0; + NSTimeInterval now = CFAbsoluteTimeGetCurrent(); + NSTimeInterval spanSeconds = (now - lastTime); + + [programDict setObject:[NSNumber numberWithDouble:now] + forKey:kLastSubmission]; + [ud setObject:programDict forKey:program]; + [ud synchronize]; + + // If we've specified an interval and we're within that time, don't ask the + // user if we should report + GTMLoggerDebug(@"Reporter Interval: %f", interval); + if (interval > spanSeconds) { + GTMLoggerDebug(@"Within throttling interval, not sending report"); + return NO; + } + return YES; +} + +- (BOOL)isOnDemand { + return [[[uploader_ parameters] objectForKey:@BREAKPAD_ON_DEMAND] + isEqualToString:@"YES"]; +} + +- (BOOL)shouldSubmitSilently { + return [[[uploader_ parameters] objectForKey:@BREAKPAD_SKIP_CONFIRM] + isEqualToString:@"YES"]; +} + +- (BOOL)shouldRequestComments { + return [[[uploader_ parameters] objectForKey:@BREAKPAD_REQUEST_COMMENTS] + isEqualToString:@"YES"]; +} + +- (BOOL)shouldRequestEmail { + return [[[uploader_ parameters] objectForKey:@BREAKPAD_REQUEST_EMAIL] + isEqualToString:@"YES"]; +} + +- (NSString*)shortDialogMessage { + NSString *displayName = + [[uploader_ parameters] objectForKey:@BREAKPAD_PRODUCT_DISPLAY]; + if (![displayName length]) + displayName = [[uploader_ parameters] objectForKey:@BREAKPAD_PRODUCT]; + + if ([self isOnDemand]) { + // Local variable to pacify clang's -Wformat-extra-args. + NSString* format = NSLocalizedString(@"noCrashDialogHeader", @""); + return [NSString stringWithFormat:format, displayName]; + } else { + // Local variable to pacify clang's -Wformat-extra-args. + NSString* format = NSLocalizedString(@"crashDialogHeader", @""); + return [NSString stringWithFormat:format, displayName]; + } +} + +- (NSString*)explanatoryDialogText { + NSString *displayName = + [[uploader_ parameters] objectForKey:@BREAKPAD_PRODUCT_DISPLAY]; + if (![displayName length]) + displayName = [[uploader_ parameters] objectForKey:@BREAKPAD_PRODUCT]; + + NSString *vendor = [[uploader_ parameters] objectForKey:@BREAKPAD_VENDOR]; + if (![vendor length]) + vendor = @"unknown vendor"; + + if ([self isOnDemand]) { + // Local variable to pacify clang's -Wformat-extra-args. + NSString* format = NSLocalizedString(@"noCrashDialogMsg", @""); + return [NSString stringWithFormat:format, vendor, displayName]; + } else { + // Local variable to pacify clang's -Wformat-extra-args. + NSString* format = NSLocalizedString(@"crashDialogMsg", @""); + return [NSString stringWithFormat:format, vendor]; + } +} + +- (NSTimeInterval)messageTimeout { + // Get the timeout value for the notification. + NSTimeInterval timeout = [[[uploader_ parameters] + objectForKey:@BREAKPAD_CONFIRM_TIMEOUT] floatValue]; + // Require a timeout of at least a minute (except 0, which means no timeout). + if (timeout > 0.001 && timeout < 60.0) { + timeout = 60.0; + } + return timeout; +} + +- (void)report { + [uploader_ report]; +} + +//============================================================================= +- (void)dealloc { + [uploader_ release]; + [super dealloc]; +} + +- (void)awakeFromNib { + [emailEntryField_ setMaximumLength:kEmailMaxLength]; + [commentsEntryField_ setMaximumLength:kUserCommentsMaxLength]; +} + +@end + +//============================================================================= +@implementation LengthLimitingTextField + +- (void)setMaximumLength:(NSUInteger)maxLength { + maximumLength_ = maxLength; +} + +// This is the method we're overriding in NSTextField, which lets us +// limit the user's input if it makes the string too long. +- (BOOL) textView:(NSTextView *)textView +shouldChangeTextInRange:(NSRange)affectedCharRange + replacementString:(NSString *)replacementString { + + // Sometimes the range comes in invalid, so reject if we can't + // figure out if the replacement text is too long. + if (affectedCharRange.location == NSNotFound) { + return NO; + } + // Figure out what the new string length would be, taking into + // account user selections. + NSUInteger newStringLength = + [[textView string] length] - affectedCharRange.length + + [replacementString length]; + if (newStringLength > maximumLength_) { + return NO; + } else { + return YES; + } +} + +// Cut, copy, and paste have to be caught specifically since there is no menu. +- (BOOL)performKeyEquivalent:(NSEvent*)event { + // Only handle the key equivalent if |self| is the text field with focus. + NSText* fieldEditor = [self currentEditor]; + if (fieldEditor != nil) { + // Check for a single "Command" modifier + NSUInteger modifiers = [event modifierFlags]; + modifiers &= NSDeviceIndependentModifierFlagsMask; + if (modifiers == NSCommandKeyMask) { + // Now, check for Select All, Cut, Copy, or Paste key equivalents. + NSString* characters = [event characters]; + // Select All is Command-A. + if ([characters isEqualToString:@"a"]) { + [fieldEditor selectAll:self]; + return YES; + // Cut is Command-X. + } else if ([characters isEqualToString:@"x"]) { + [fieldEditor cut:self]; + return YES; + // Copy is Command-C. + } else if ([characters isEqualToString:@"c"]) { + [fieldEditor copy:self]; + return YES; + // Paste is Command-V. + } else if ([characters isEqualToString:@"v"]) { + [fieldEditor paste:self]; + return YES; + } + } + } + // Let the super class handle the rest (e.g. Command-Period will cancel). + return [super performKeyEquivalent:event]; +} + +@end + +//============================================================================= +int main(int argc, const char *argv[]) { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; +#if DEBUG + // Log to stderr in debug builds. + [GTMLogger setSharedLogger:[GTMLogger standardLoggerWithStderr]]; +#endif + GTMLoggerDebug(@"Reporter Launched, argc=%d", argc); + // The expectation is that there will be one argument which is the path + // to the configuration file + if (argc != 2) { + exit(1); + } + + Reporter *reporter = [[Reporter alloc] initWithConfigFile:argv[1]]; + if (!reporter) { + GTMLoggerDebug(@"reporter initialization failed"); + exit(1); + } + + // only submit a report if we have not recently crashed in the past + BOOL shouldSubmitReport = [reporter reportIntervalElapsed]; + BOOL okayToSend = NO; + + // ask user if we should send + if (shouldSubmitReport) { + if ([reporter shouldSubmitSilently]) { + GTMLoggerDebug(@"Skipping confirmation and sending report"); + okayToSend = YES; + } else { + okayToSend = [reporter askUserPermissionToSend]; + } + } + + // If we're running as root, switch over to nobody + if (getuid() == 0 || geteuid() == 0) { + struct passwd *pw = getpwnam("nobody"); + + // If we can't get a non-root uid, don't send the report + if (!pw) { + GTMLoggerDebug(@"!pw - %s", strerror(errno)); + exit(0); + } + + if (setgid(pw->pw_gid) == -1) { + GTMLoggerDebug(@"setgid(pw->pw_gid) == -1 - %s", strerror(errno)); + exit(0); + } + + if (setuid(pw->pw_uid) == -1) { + GTMLoggerDebug(@"setuid(pw->pw_uid) == -1 - %s", strerror(errno)); + exit(0); + } + } + else { + GTMLoggerDebug(@"getuid() !=0 || geteuid() != 0"); + } + + if (okayToSend && shouldSubmitReport) { + GTMLoggerDebug(@"Sending Report"); + [reporter report]; + GTMLoggerDebug(@"Report Sent!"); + } else { + GTMLoggerDebug(@"Not sending crash report okayToSend=%d, "\ + "shouldSubmitReport=%d", okayToSend, shouldSubmitReport); + } + + GTMLoggerDebug(@"Exiting with no errors"); + // Cleanup + [reporter release]; + [pool release]; + return 0; +} diff --git a/shared/sentry/external/breakpad/src/client/mac/sender/da.lproj/InfoPlist.strings b/shared/sentry/external/breakpad/src/client/mac/sender/da.lproj/InfoPlist.strings new file mode 100644 index 0000000000000000000000000000000000000000..4cfd32c54b7f216718ef1a97d69bd92a2a92a143 GIT binary patch literal 156 zcmezOpFxSinZb?0iJ_Dsk0FI2har{0k0FsE7f35HC@|OpF-S!bP^_3C1IP;k$`ml< f14T-JtRkSSH3JtGQ(PD_fjSC+CMGgel4B+SuMryf literal 0 HcmV?d00001 diff --git a/shared/sentry/external/breakpad/src/client/mac/sender/da.lproj/Localizable.strings b/shared/sentry/external/breakpad/src/client/mac/sender/da.lproj/Localizable.strings new file mode 100644 index 0000000000000000000000000000000000000000..2b8bb969398e68e8cec2fb664b816d9c5761a3d4 GIT binary patch literal 2428 zcmc(h$!-%t5Qgi_Q;aNp$-xmEgOJ!FMS_483-05cc)Z9n8|zvtE&C;$GM$ZVm&KtZC`C+6U%uY+miRhKJzvTtB>pyNR0UmEDh@=@(KH6 zzEeH}BvUk|tk3M+o>;54_YTd}GUO+Ah1JxC!FCJjmwZ~*MPH|~GUplLC9$@>;@hK0 z@s(qvBU|U{Eau}mDy&+KWfR{tG$-i`ghr*qb}=fPJ*%sWjLCI-cFZih%+?W;r8qb_e& zh!RR5YCs>}on8)`)o-rcJ487JxvG5sCcga}oeebyzvk}PWM zs~6MI4Tm{=33{=no1TYgsG2Hm$$1awL?Qp`RLN|R)te?*!QN~apKPf6b?Q;Yz8r_n z@~ZWYavM{$m67Kn-8ehPq1K%*x25TKOC0NcuE%)mB9$^XM_{>RE!}l(VhP`jD97x^ zEGpg=jm!HKtkC#9yWWY4ud?s~&tg2QMRU35>SEu$svFgVve?=&ElqltD6png9%iN1 znozTyE%}eVc7x|QPv7ir0P$C!QPIxsE2RO z>El|T?EPN4nR_=ds2g+3U3vo+RkTwwEy(DCoATW5!qb7*CC>y@bejxV&pB;c6$(O1 zRy$Nlfs8m2(P7!- ohMZC|>K$|HJiW4KVbCO|9OPDti7{!vAp77px(XcPSgwD*yrIM+KmY&$ literal 0 HcmV?d00001 diff --git a/shared/sentry/external/breakpad/src/client/mac/sender/de.lproj/Localizable.strings b/shared/sentry/external/breakpad/src/client/mac/sender/de.lproj/Localizable.strings new file mode 100644 index 0000000000000000000000000000000000000000..73da36f48b35902943a91752a8f9d5df5f82fa90 GIT binary patch literal 2746 zcmeH}%We}v5JhXvSF|j$$-iP4#WBV5D+-jTK#HKc~g`S5t({r$IdMf?yi*{;B47K`J`29@s zLHk4f7FvBt7Sb5&e&0Iw!di{JLunRPNq%G>Wwo$@+ipqvjaJJJT|3yR>>!1;OI^YC z?5%#UZK*x9g6^iSeJz=}(3D!idipQ*2ASI0viW)giW6ZSN#>#du4|KzQWh7wZnW?L z7CaC1U#$Ghwf`71dka~JvQy96*f9>I`BU+cJMfZuayZoeRG5Puxn8gj+8HZju>zn1 zQ(t?;+RO%TBEm}Yyd?v8IF|-kK%4SNO=FJOyRbVwkxibK9wWFWdWsbahVQXf&!Zo$ z-oTVttDd906oz28k_j$dtz(LHfDco}`d(-wPj=_TSeA$vyeWqJc_XjnX0-Y*g_Rs- zc07;iKSTbNA>WnVx$~KR;hsK$DT(WQ_R3y*A8%$zUkst^;bSdZ4{Oj>rE6WV8crsb zWv=5HCho%G*txOeQx9Qp;r&O?PuxEFVFuxkNST^+4U^)XBw!v>>sHT^f%I>omHCHs zA$!%MSbxY8y_sV>mS&tdM{6xS4SYA|K6X3aK>WCZ*YX9<6xW4tlj}mWfEiEqqTWz@ zbPLfXEh+M;R6Yu27Rxi;$GMXyGmKf-FIl_Q z+c*QM|0hXju5c2R>zSXky3P8~^(G!6(N6qS;N)OpWjmjRk+>@DrZXc~;b86gO7}(ZV?I<`kSjgqvrRja9{VT=K>DP4&wdX%$>q??|VkFIM zkMo(6fy`IWSb@bo&k#tPy3EaU-x)lk`&AW6XYl>3oJu!V@&G}X9YNcAD#dN5}u$in*R~<(byyH|{=uE35 z#TnJC^q$_)Go`vDeglvTx;{=maHW|rShJlBZ}&+jGA)<#2#a$rp7OfQXZ-GZ+UtpX Yf^W>SO_4SwcZsuUXIq?2+09Y@1@J)L6aWAK literal 0 HcmV?d00001 diff --git a/shared/sentry/external/breakpad/src/client/mac/sender/es.lproj/InfoPlist.strings b/shared/sentry/external/breakpad/src/client/mac/sender/es.lproj/InfoPlist.strings new file mode 100644 index 0000000000000000000000000000000000000000..a82c013e059dbf7918192662fe2d4030ff6aee37 GIT binary patch literal 184 zcmbu2I|_g>5JX??DMGFxHY%3hVKD}T_`zu5<<&WWoyD;1o0;AF!PC%DbFvUfjPw|@ prpHl9US#E9B1(hYpnaDUlyah`UJ}d7mJ}a1TQxJAQpWZF!wVZxASeI; literal 0 HcmV?d00001 diff --git a/shared/sentry/external/breakpad/src/client/mac/sender/es.lproj/Localizable.strings b/shared/sentry/external/breakpad/src/client/mac/sender/es.lproj/Localizable.strings new file mode 100644 index 0000000000000000000000000000000000000000..c31d6f48bb2c7e913abc209842492eeb7d2364af GIT binary patch literal 2578 zcmds(%Wl(95Qb;XQ&Fbq zO-YNy3Pp~`?@>=&5k(oj7Yroavfa?sP5r zO#Czb=ej16%%yQIde_GGz(zgZD{1C7m;B7$$ZBp=w>^^db6q2Q>sE4WBo$sJx?j4N z9ov@-XfoLkZy>|tR5mk7F7$RKZTN6=t3)q^wXi+ef3^kDp)_ZbNmyPB1O7@~=vVHu z6>lzz9D%;FFHR3JlNqpNx`PeGp)KiLXhrz7mBkIBbEX{)0n2Y{{6jT+~{0;?UEvYo_9{DZqs{Hc2U2b`6n) zwX}PfdbS8LBe|`XN0PyBp?Re;!wnp@2PYQEa`>thO;^M4lP0dq?OoP~R>A_Cq_e>p zaH{8dZx^#!?pKL~iLF(tJBhXRb7g3gE}q*r#Thk>?xSj*CcB#b+ulHgPsKSNSbsMX9ZYp*L>l28{z8f^`mPoazOm8 zPWofzsl$KnF*Wv7=lIwz{I{nQves`Vyqu`fE#n(GYdtS`np5)Uc5qjahmRkVp0kr!Xoy%{^#4{ZcofVUp*W7iA<$0 nfX)|t@^??eIlC>UL$^+UFq!v{sk+v?UqpRBe-lyPPsH;J4xqw_ literal 0 HcmV?d00001 diff --git a/shared/sentry/external/breakpad/src/client/mac/sender/fr.lproj/InfoPlist.strings b/shared/sentry/external/breakpad/src/client/mac/sender/fr.lproj/InfoPlist.strings new file mode 100644 index 0000000000000000000000000000000000000000..4cfd32c54b7f216718ef1a97d69bd92a2a92a143 GIT binary patch literal 156 zcmezOpFxSinZb?0iJ_Dsk0FI2har{0k0FsE7f35HC@|OpF-S!bP^_3C1IP;k$`ml< f14T-JtRkSSH3JtGQ(PD_fjSC+CMGgel4B+SuMryf literal 0 HcmV?d00001 diff --git a/shared/sentry/external/breakpad/src/client/mac/sender/fr.lproj/Localizable.strings b/shared/sentry/external/breakpad/src/client/mac/sender/fr.lproj/Localizable.strings new file mode 100644 index 0000000000000000000000000000000000000000..c32f8ff7a6219853e541db1bf3df512b3f5e7786 GIT binary patch literal 2694 zcmd^>OKuZE5Qb~b4cLt=vWdVJEI~+whlE4|QLG$bVmr=A_Sj@5LC$G78q4?+!2eab zJ>$fAEZ8B-_H=hW|N5(X{`~IQu4UG@+Rm-8(#BS4eQGnUGdtF5yVkQO)@kj1lxA)d$&c-`tmZa!+Z{>2)wg5&_B6|FuFvF_ zmmPa2nMx8P{nmP}U3)6Y(w2J8++Jo=SuVAEt!UWgoOqEhC3B(wj=i}jx&wI_OQwkn zJC!Zm^R+y%9(fcK{exI8yI`1B+p<8eR0iNi26Jh#x72FsYp||m>D*4VQ(0HL$CA#p z6Ua~(biq66f&0=eL`;{$_`)sE{pnIsT}gi8=#|F#LVHyBm%f2*$g-CUv#3p+$BLMv z{-H_cckGqxwQCFhQ||r|{`fpE#=&8zCk+(DwTPG_UxqD9;b!3mZQ7H49nSAmn zn24~7$x`W)BB7U}@9CSNB$xn9P5KkB13!nYg-1NH=Q?Y86n>^|1rMf6Oe=bpK5R=T z-sf&N@T(k!#twPokJ)k}{YopQV6q0H^(tFrD*2CjwOm&eEFzbLQB3pAsvjytA{lAs z&-8qAqHnUKvB?dC-i36CBqmF>?%Adq4|&*TCpw>a8$0{*0u~ePrn$cN$a?*>*Xzib zcfrJgX6QDnN1B9V`H7n#>K9ex?qnU4l)JD{=F63B;)OTdQAhfxN165Sg%W(AqD}0u zvOULx--qiC;0-46Zm`hZh@Gj{O})|LE~tg!Om@pvH lQvYqs@4$V)6XLrszj$9tlDEA*CcCz`hq7z?yX`G<`wKXU=jQ+b literal 0 HcmV?d00001 diff --git a/shared/sentry/external/breakpad/src/client/mac/sender/goArrow.png b/shared/sentry/external/breakpad/src/client/mac/sender/goArrow.png new file mode 100644 index 0000000000000000000000000000000000000000..f318a56711d43d1b8925f1c26e709da0872dd607 GIT binary patch literal 3591 zcmV+i4*2njP)4Tx0C=30*LgIQeH#bx>z*0LGR7D?VPuzmU$SqBvSv$3c7~adBxOm;nk5u1 zDB2V)Br00S5|TnfQQ1Stl4agML=Xsy={{Fh|>zw<(fA@XP=leSs04t9>l}bhd zKwwa4u&tRP$;sJ;gzW+humAy6K+D}Dglb@IZ3+Ll<~soa0O*i)r&7smFS9a!<~Td} zo7lc+P4bue=lcKv03Yn+>;izu004*2daVxt9Q)U6djQ}#6iN*RKzsp!!^79z699<> z0GVKWTO$DE8UWyZ*6T_D!0%tL8vp#dr zj^90z2LN2|0EH>Pd$YEH006?(7UHd}1ONaC&JcjEpGKP%2f$_ktftdw%Y`)B>SX|Q zFF-BX<6v;uum6Q0Edc*^z5dO=78(IS0U)ACF)9KbiHW2;MjwrhWjM}g%vi^ClKB)a zfh8GVLGWcuC#JJ!aGc@H;>zOA;i==J@aJvH6SyGQKsqQ~xcRb3o7fTYVu^OCW70Rb zbjrrc-IDK7JgIb7`J-yGT7~+6W~x?|_K;4d?i0N+gKWcRMw2GFruAks=9jiNSD$}y_Ho~*{Wy<@o?pDadJp*w`;PdH z25^#}27U{gq)bt#4}1@v37HF>JNP4P{?J1B;^C!;pGQ_ASB|bmNkzYlMUJE5&?hkQ zbSLRgVG|e<8IzcjnNx77ET{2ltm%XdwoKv~_AHKU&K$0@+~;_5dGq+r^IzC>QQ(qb zzEA%ydiZ{`sR2E`IgLW*;4s3g*%FO74IqCS1DJmP^*}K z5LT)FP@_t#TKmz~$6KH1JXx!Ws?~d{|IFaI;R~ZWOkG^P$xG7)vqtkK^Jb>zq*oR# zmaRM6@NK7GTesW1vF#vsWW2R|XWzN2i>oWA+wr|qkIM(X4|%<=eXjj`J_>%k^vP{t z-=Oed;phEdJidAki47GGdyn{x`i@DB-5B?u2>3>xl%2di6*NuxPMuMlxjP#?7xE)? zUUj}=A#5>x>G03P%Ua9TD@Rr%*L2pN&}aaN2nvsyMmJ+_(q+;|Vtp9w8I71!m?dx$ zEIfE_RvrQm8z)hmU6MnC(~@g1cL+}^Zz*3h|EK`Fpqh{m>8$We5kyo+?11b zY4Vn8S+tyie7eGbl9Y11N|&0nI#r`ii(5N<>nmL@J&OJ_Ls6q6#_gtpX5rf&n=e@? zT6*os+SzW6w=uH~w=1%L>%iow?{vWVf=m7GsXan_t=uB^6}s2$ANAz)((`umIp&-1 zSL@#wuoTE1w1r|!^*s<7oEB0TT7Ix0?8Bk4@c9Vz5w=LuQJG_^QTovqF;=m=kME1~ zIzf&PJsEr|Iw3mocv3=gMoLm@?&))BIqB&c=QA_TWM`etzLsca&P99o_}=V z(Z#w;ukzaqItn{3cU<{U^rd+C>fp7n*QaiLzd2Phd28nOROxux#GT>01NR2+_msC+ zG(M=UeDJWe>U?#^qv*$hPj=T>)NXw${fzi*>G`J@O?CI{GhYTb*fpv(@i)<$dtTjd zNo;j#6KNZIeXsrK8GPQ{d%sG2ofxVbjv3iL$~oFMc4eGAp*pelt#LAAYWKA0^u+gjGm*1qbHuss zA4T(&1)Gqax7S=qk)nIz74?`WWm922+MQ z#(1Wa%!#<|xMr3VdabCx%kFONTO(|G|`fj+@Y zLiwZu;eyQtB9}$4h!u$!OI($_CUsrfPkL<24VezNd0uwL+~z zy+R{gV^On8t4h0i>m!}Vy2o_a^lJ2L4W1f4GkR_uXM!=QGp#p!xvjyxaeI?Rk|oaa z)sB{(tyXQ;X*L9#cH1|09rkZ`bvk4_ayfQ6bvwUz>Dis<%J15{r*H2^w@>>%xfkpg z-aqK^+4GCnSMOpU@%04p9}BoimLX64SBlL3l_HggH4ZOD{5-N8xpH*%*jf}V24Ycv zB?!kmb22Ukm&$UQHI0zY_E(m0<#3+lX!OI?3M2bX<#jlEQ zq)Ca)E!o?0e`JYrxk`oV1NBPvhZ;nLxR|Cl20^pDfU{?qbzXd?T4S?$!fHez%y(XaO1OcT(1!TL< z#`ysV5I_Lrz!01v1k#`k77-F+frKO1kXB?E#fmaR#h@OeR?s@=7<4^)1tW}c#uQ_I z(3#Smpev=DqBo;2pdZ3=VqLM18L$kR3~>zOj9QEo##$y>rf8;0W;}BY^LrczE(kZm zV#0EqWdQGvf5wVt^=6$Q*buS_t858uGej+75xY8j5&IN}8^;2tA7=&E7Ot1v-aJ^I z8@yt?hj{1sF7TW2&u+>W5EY0NLVHIf;O&8lG&L=)7aZ}Pr z^176vG@JCmmU}WMWnJZz<#F=E3NI83m131WRm@an)Y#Ql)Q2>3H1V3XT4mY=ThHj6 z(v8wPq<_GGZ0Kj?YwT+hU>al=y6uR0-1gHJd6w6ARPJoF>b3rEi?tK9*Vtv}Ky^%Y zy6ybhW!9BvkM3S?x3qoL?js)Do@QQ$y-WOf{H+5L$#p>}iUBp|!1ECLP>X{Z;i88r z5sxC7j=CHxiK4~W$DvNR#y>d4o^T+sJtZr3Da|dtK2!coZcfPA&$)Jabr)~vixphB z%w0^lntC07_U6PE+1B>gVIB4F zs9pTs4ZY%h-5(PN^uCk~d5#E;_Iz`g;+pQ7$(?gsm{=-bj-v6ZGP(uBg0aG6VLs52==|u)=oaaX=ugwX z$BJPOVxKb*89W&(8JQVf8E-RTnD#Q=XJ%sdV6MS&;;6V57HO6gmI=HizLb@PmBQLd z&?j7B!?T64eIS|=@352DGuYQSLO8y0`g4wPk-28LL%HX9B6tzrWIjH=8~mF5^_v_v zjS55y@(NZ6*$9o1;)TV98#V`taEm+<^$^2}Jrxg<5R!N!nIL5-wJKe`B~nI5Wk8`;^)Uu$gBio`MpMS) zCPSvPX5-s_Y)4seSaR=B+^K72Wo>H{V0+ju&;IVNR}LQ>SDZOqQSzuG!IB|ip^aezheE=eB1Dd)MvfhGj%tWe zi!F*Ho=81OJe8fula!aDn%bJ?mHsm`B}+Z~>)DIB{^xgHl**?on7I6`sQl`=>+v@| zZn>74-qErt{2r<{0J$To7)A#S`z2|IF&mIzb2_tgg-J%ZX64oX@8mC$vgMUv$Y^1SpLhh&AQ}Yj>Du3|1|tt@Grk_G;6iWe%t*m z_n+PVFSR}cemwrxe^(pU^LMwgH~y5Htn^!MHvNX(3z{2xuVilcEg^0QzE`1 zWm}C~U0Az9OQO*L*5@Sv0AMnrkSW0=OC#fd8Sehy6-Yj~@eTw4a9%;X>;QlW0Gq%F zC_n}Z1Oo{y!3c~2)@L;U0D1zzc}D<%n#Cj0e}8VMS9mA@fDwgyIM~m}Hq=-Ed<7005&&L_t(2k&V(l3c^4ThT&%u z5K@VVtq{lwwDJaC!ZwHUPJI*^S&3CsIs{85dETMz%GtU8NSrt1^i<=yGOtq)$vhKxa9CN&5QIRO>nh$t{j`-Kt_y7$#9ND%Wl(95Qb;XQ$!V2r3xCs5`@HERJ2gF@BmKYB$g68%B5BNgggpsc5HzEo5`u& z;Iyj54q0}N$8+ZL|1)#)`&ZZYtz&1lw7C^l+Q=4qA6ljNj(ycz?yFCdETu8j-M7rw zmGnE35B1D+_obO>$5`uq>)Hd`X~^DcXJ!-WkL-i2W;Sr!JJNokd&iD!PtuvL$=b`6 ze({p)`>wszb0JB`zU%JGPhl%b@Lkwc`{vR<*HyT5s(qy*&Fw(epWhPCvGhmMNs$~X zg7CHQ{Zscr6cdkyJU|Rdd&ERpdhGn7H&m@F9z;3OwI?~ufLzUYlL@|p zPvG6tIv4ucHOlZ#?r%I=>fzsT4}uB1zGDjqH}N4is(_qo*0)FVzcByA9(w<5V;^Lv z;(H`E={azXy4ba+zBi#6U0q2#_1D5SQT!qsQ6IT1(x;iUsp)}#Y1UqUElJE)ir`%A zh4-Sh(y)`OSLl$!wM$8&XDZ3bh8eL?h4*!#yYTPSEVBZQRL4|s<$CmCniNsf_}|R# zy|m}5aC#)XMwCl=BL;epJn?j;3t4WD-7A`p^gfd`CP?6t>X~Y00?^$tL!#H1xsd^L zFXm3qdeTYAvPNU%y_b-lN&0i0Ygi73Q=V;Xo+!r5Q5(zN#BrK8rX#V|>=WhC_0f&# z1t&!dl6P5dvs10@5NXZHkv#VFK54}Du7UBhYwyAMxkq@ZGv@Dohe9k)Jn916(T?h2 zBC@_|C2E9-Vu}y!RI$J*yuJ3h%5*Lp(cvW~G8Mx7+??g9^5D93{3ly%z0&tmULv}* zk12PiIHX6jr1S+!@>^mCt#YA-3D EKfkuZ{r~^~ literal 0 HcmV?d00001 diff --git a/shared/sentry/external/breakpad/src/client/mac/sender/ja.lproj/InfoPlist.strings b/shared/sentry/external/breakpad/src/client/mac/sender/ja.lproj/InfoPlist.strings new file mode 100644 index 0000000000000000000000000000000000000000..4cfd32c54b7f216718ef1a97d69bd92a2a92a143 GIT binary patch literal 156 zcmezOpFxSinZb?0iJ_Dsk0FI2har{0k0FsE7f35HC@|OpF-S!bP^_3C1IP;k$`ml< f14T-JtRkSSH3JtGQ(PD_fjSC+CMGgel4B+SuMryf literal 0 HcmV?d00001 diff --git a/shared/sentry/external/breakpad/src/client/mac/sender/ja.lproj/Localizable.strings b/shared/sentry/external/breakpad/src/client/mac/sender/ja.lproj/Localizable.strings new file mode 100644 index 0000000000000000000000000000000000000000..1f1a4fee5cd6f98defce31446463266b67048904 GIT binary patch literal 1792 zcmb_dJ!n&56ulJ`b#ZqwM7wloj&3S~#12CK+Jq2mhxnSlByG}{*d{HhRIG@!R-r^h z(M=q5a1aL(CkH_{aj=s^=^$3Ox>=0perbM9e-L@RyzhJOe&?Qh?oTSeJ<=mCiAX^v zWKpWErhtZ#_tMu=Rq>LgljG+%t@r_LU zn7*U_T9w(_-~UsmRf*$r%;c=9Mhr_3EsVjN%;0S)4hxI-W~TOD^I-u;m#o<`K1ULa{W|6uzr$Ma;x_X1 zy24z2XU%;ev)ANj4?GGhw-7ao(zaIB)!u21zMnj_hHZ9Oj_daRc}etj*5v-muizo; zK&93njVN|PXg#SM8fX3VJ?I4i@9xG`_ihIL*}aT0oD+FZSyNI709s%T3u&uNA|jI~)e zjXZ1SY>76CGj3E@2DQ2eMI{Q5BpN&2`btn&ymoS^GLyM>qpNm1&2H0B5QS&WQ&o2E(ZC~>IDOY;W6`DSvlTat9c z4q0~Y&-|P@cV=#W{T$e#Wp-|jEp1|zjcuj*wbhzucA;76qt9AtByp{GWVw%P$!A)> z);HHXl4LH88;uWbV9%`ASv!_yZZpY`?UStLR=Dk+q~Gc7*|FxCEZ6#7xTVzwQ)UPD zUV9Tqhu?3mdnc)p{58Hl*4oPT7y9;P@$DWAr;;5@CV2-_quI*t-AbwXT=UYS%=Hbt zyD)~_cVWDBjY^}ic%|PTj+y97+3CyfaAO(H7J6VD{}JOH}h?09Jxb| zvitYxj-2J-nL69XBN;;{sI(tBv^~damkSis!b(q^aiLlCoY)1=%4?gxbn6*TE>Q8< zj;=%&AW7C!om4`y6Z5twrHt)Kj(VCX&mYHfq%|U-w$}26pCn6IvQ&*z8@{Ahs_k5n1*V~wfOE06qPB|bi@ zGwI^Dlt!&_bg;JP4L7{fH>i6s6B(zyR77zjfH3vamXD`wV~4&$^zby>*q~#yR!)SE z6J$4a;-S6#!+P>dznTaw(cIfMt+NLbna|>Gm`RhK+{OWh$h^Y7YCUz|58|QG1LhXb zr6iV;hb1x%+ufd>Fmf+e&i2fS197&!uC-1{w`2h!o5Z6%`y=smCu+pKU;rkJt$l)rPaz|8=^+?@M7a)!IS l?Thc!eUK$+?~8BVk)*2r#ext_7SMAo(| z{Z6z;L@T@3ey$UL8tc21)s=SqUPqQ*p74Du`BXB&Yv0z|K{t3L1~SSO;ZpmNEe;|h zmJm~BQ$5HH6iMbHL1&}bGHG&FSx4vB4l(ju=mBFg^awX~$;Q_p#8gUpF4>VXjbxGN zBE=0B;223GllOpEsNhCB6Z;~GnLHwg#gYCwNo0rIL6C2sCy@j+0-Dq^R}60zv#;^L z)f2Bj^n$y`3LMBO(Lb34&!_zU|FwK>PaXgNw4QKkAOr(*+xEg)2x$7AgiG*%J}PY= z#hV>p=hwdzSK)>It}%R=k@ z8A=>=ZJC)^JVff89qKeww28F+MDsR?smA+*q^R;tyJ4PQH_*M@d8VSUfY{e|Oz%#1 zDj^hZAu!Lkh}6h4!zdnb(QXeaVfDWgl>s$Ki)o{p+h z=t+hCSezMZJ=f_&tDpdqSnY^2m1L+EH)P<~N|{!|qLAHuI}OMJi(pjhX6A+ksb;0y z4nHuT>eSyuaGM?iUHWSyt-4dOc+OjY%K*9#{?;fQ`g`2hsoZXz2c{YG=~Qm>og}h< Pms``@x!jum9?R`F*M+v{ literal 0 HcmV?d00001 diff --git a/shared/sentry/external/breakpad/src/client/mac/sender/sl.lproj/InfoPlist.strings b/shared/sentry/external/breakpad/src/client/mac/sender/sl.lproj/InfoPlist.strings new file mode 100644 index 0000000000000000000000000000000000000000..585f6221dc91a2e19ef05347bbee12e0626e227e GIT binary patch literal 184 zcmbu2Jq|!X5QRUj#2sX>AsR#}uCPHtuws$8Jf2>scyA{2e&)I{^u{1IB!nNG1-)F0l9u!ThX?+YAMgMG literal 0 HcmV?d00001 diff --git a/shared/sentry/external/breakpad/src/client/mac/sender/sl.lproj/Localizable.strings b/shared/sentry/external/breakpad/src/client/mac/sender/sl.lproj/Localizable.strings new file mode 100644 index 0000000000000000000000000000000000000000..b7dfeecebe02c401d3d6e6f124fe16974d81fd60 GIT binary patch literal 2632 zcmds(%Wo1<6vhu*6aI$@v71IAy408$ABl_B5Sw;?p_K9(NM~px3;%TW`<=ruLxVKB zG=zEFdmi8U&g0InpFKOZ%r307JDXT;V=MH2V@thfcB!|KKYi6oDTy2XhnD<#Dfvw6 zH@YVMhmuUvxYhHi_3W8-8f%}VnQSikv3-%%WY=!HBk6bgcdRdccCoV1%89+#6$F`0 z{hI5#(*DHStzvJ+qCT*qd8dw%#7`elDA1$y9zXWUo*}BlijmM8yhtCwjwf zjf|Dtb?`gTQ!O=d97G0NUG2kR={WB^ia;Cjgf3QAvX75;3u{Yzp|zR&9t%aLwIp7N zIAl|6?$$sZOrde@ve;Va3C-9K&NA5pS?<`#KW6ZiJy*RS%ObTkP%g-PQl6+Va{t;jn;9E=1uVod zxi0K6UwyB&Li!6y;oK+H zJi5B#u_GLG5!fE{cct~z$|Uj9nMNJKVHe^g6b%6FFmUk9-Zum_zh$Tj^ICu7|x?+bDICtjff3!ng%p4`vR%wFnVvFW5f-6N6J-FU!?K4tE z4?CBN>8yeO)?-?U&7OV_{_A>|Q~6B(u5eRFJu$&aUhxbsaEjHgDyozW+2SOxs&B2R zORu2^Jw;a%4Hn_I&>WbHS0+|7(in>;-=uuPdH?_b literal 0 HcmV?d00001 diff --git a/shared/sentry/external/breakpad/src/client/mac/sender/sv.lproj/InfoPlist.strings b/shared/sentry/external/breakpad/src/client/mac/sender/sv.lproj/InfoPlist.strings new file mode 100644 index 0000000000000000000000000000000000000000..4cfd32c54b7f216718ef1a97d69bd92a2a92a143 GIT binary patch literal 156 zcmezOpFxSinZb?0iJ_Dsk0FI2har{0k0FsE7f35HC@|OpF-S!bP^_3C1IP;k$`ml< f14T-JtRkSSH3JtGQ(PD_fjSC+CMGgel4B+SuMryf literal 0 HcmV?d00001 diff --git a/shared/sentry/external/breakpad/src/client/mac/sender/sv.lproj/Localizable.strings b/shared/sentry/external/breakpad/src/client/mac/sender/sv.lproj/Localizable.strings new file mode 100644 index 0000000000000000000000000000000000000000..f7d1251668bb433b683da995409eacb54d5fc0be GIT binary patch literal 2588 zcmd^>+iKfD5QgWvPqCO9+CXjE%Y;A=2?Uxnp(YO?pPk4DEZHUR+q^N5={!uedP1_N_dfub@*mM7WWjsO2-_^rM4-4SC|XK{a)gMODvP;5hWHtn6ac2f0E z=w4Nva<5(}?0IA_EaIp$3ob|HtB+>Bz9OHTTo!&u84fibx-IlXT4S;xyin`WLYF;N zx~A0HThg-+Ug(AqOw>Q2g4DZWp#IqGy4bI>q*>MT-KJBqwi~Rc$ka#A{k=~5Pu9g$ z-PK21SOa_G`&hFJ89rC2E rS%Ib6uKyMMiMJbTpKn(lLH|xvTl;*QCYtK(-iyMY=HuyEQDP&@P+*beKOYHnPJP8 zw4gB!S=hZhcjnBQGjqB9-LwNstZjwOY-pMFE$8{lCOjv0&Qs6tzOqtaamCfK)bA(Q zC#+xbopN=sOxZEu{=k~{%<7f3PwY(X8vDL|!E0(=_g%;O9ar7#{aG%2-JSQVOT{T@f~^flbKH})KDzJQ0Q;t8#^= zk}jpn=maS>#lBTjud24{kJMF|yV{}?!spU`j-3Vd?!&6rj#7(tVyX+o_9o`f_R1Bl z{?#TYIK`=dbY}6Z(j1IkFD%S>{rLD7h|T#gLW%R;f%`5>H;fGijj9t zwYHI!BIY33b4@Eh&CbzXJ~1Kh*W%Qf6T1T#i@7$d%&2lgX(lP3@Gj;TD~88+Q*X`I zF0Y#NnkcGr)oaX8;avMVCZ^``bp_RZ_7Cq6OyP9MBvs}MduZno@0a)zt1;70J@28{ zLGwIEPiq`Ps>+=48xyRs5Aa{`(TTL4=4b2;i#a(RRi@+mRFHPX_4Ok@Ue~7h)wxyf z(IjfGFgzUh8}?G^x(@eQDDx8|=j3r*!94Ihrgq5r*|Z=2X`syspFSvhXeYY2Np4kc zm8lq%*9O`th+m>Eb*5e_;BJE7Y}G-^MIOazZqkWPfylT#7x#G=O4iI;up{QR)LEU< v^0O;O8xQ)Q5hr|A*?$*hTP`Q=TUt=xZ_A}ihB~pOuYb#>8e>;3+oSve2xX}~ literal 0 HcmV?d00001 diff --git a/shared/sentry/external/breakpad/src/client/mac/sender/uploader.h b/shared/sentry/external/breakpad/src/client/mac/sender/uploader.h new file mode 100644 index 000000000..0897dade0 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/sender/uploader.h @@ -0,0 +1,106 @@ +// 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. +// +// This component uses the HTTPMultipartUpload of the breakpad project to send +// the minidump and associated data to the crash reporting servers. +// It will perform throttling based on the parameters passed to it and will +// prompt the user to send the minidump. + +#include + +#import "common/mac/GTMDefines.h" + +#define kClientIdPreferenceKey @"clientid" + +extern NSString *const kGoogleServerType; +extern NSString *const kSocorroServerType; +extern NSString *const kDefaultServerType; + +// Optional user-defined function that will be called after a network upload +// of a crash report. +// |report_id| will be the id returned by the server, or "ERR" if an error +// occurred. +// |error| will contain the error, or nil if no error occured. +typedef void (^UploadCompletionBlock)(NSString *reportId, NSError *error); + +@interface Uploader : NSObject { + @private + NSMutableDictionary *parameters_; // Key value pairs of data (STRONG) + NSData *minidumpContents_; // The data in the minidump (STRONG) + NSData *logFileData_; // An NSdata for the tar, + // bz2'd log file. + NSMutableDictionary *serverDictionary_; // The dictionary mapping a + // server type name to a + // dictionary of server + // parameter names. + NSMutableDictionary *socorroDictionary_; // The dictionary for + // Socorro. + NSMutableDictionary *googleDictionary_; // The dictionary for + // Google. + NSMutableDictionary *extraServerVars_; // A dictionary containing + // extra key/value pairs + // that are uploaded to the + // crash server with the + // minidump. + UploadCompletionBlock uploadCompletion_; // A block called on network upload + // completion. Parameters are: + // The report ID returned by the + // server, + // the NSError triggered during + // upload. +} + +- (id)initWithConfigFile:(const char *)configFile; + +- (id)initWithConfig:(NSDictionary *)config; + +// Reads the file |configFile| and returns the corresponding NSDictionary. +// |configFile| will be deleted after reading. ++ (NSDictionary *)readConfigurationDataFromFile:(NSString *)configFile; + +- (NSMutableDictionary *)parameters; + +- (void)report; + +// Upload the given data to the crash server. +- (void)uploadData:(NSData *)data name:(NSString *)name; + +// This method adds a key/value pair to the dictionary that +// will be uploaded to the crash server. +- (void)addServerParameter:(id)value forKey:(NSString *)key; + +// This method process the HTTP response and renames the minidump file with the +// new ID. +- (void)handleNetworkResponse:(NSData *)data withError:(NSError *)error; + +// Sets the callback to be called after uploading a crash report to the server. +// Only the latest callback registered will be called. +- (void)setUploadCompletionBlock:(UploadCompletionBlock)uploadCompletion; + +@end diff --git a/shared/sentry/external/breakpad/src/client/mac/sender/uploader.mm b/shared/sentry/external/breakpad/src/client/mac/sender/uploader.mm new file mode 100644 index 000000000..13b6130ad --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/sender/uploader.mm @@ -0,0 +1,688 @@ +// 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. + +#import +#include +#import +#include +#import + +#import + +#import "common/mac/HTTPMultipartUpload.h" + +#import "client/apple/Framework/BreakpadDefines.h" +#import "client/mac/sender/uploader.h" + +const int kMinidumpFileLengthLimit = 2 * 1024 * 1024; // 2MB + +#define kApplePrefsSyncExcludeAllKey \ + @"com.apple.PreferenceSync.ExcludeAllSyncKeys" + +NSString *const kGoogleServerType = @"google"; +NSString *const kSocorroServerType = @"socorro"; +NSString *const kDefaultServerType = @"google"; + +#pragma mark - + +namespace { +// Read one line from the configuration file. +NSString *readString(int fileId) { + NSMutableString *str = [NSMutableString stringWithCapacity:32]; + char ch[2] = { 0 }; + + while (read(fileId, &ch[0], 1) == 1) { + if (ch[0] == '\n') { + // Break if this is the first newline after reading some other string + // data. + if ([str length]) + break; + } else { + [str appendString:[NSString stringWithUTF8String:ch]]; + } + } + + return str; +} + +//============================================================================= +// Read |length| of binary data from the configuration file. This method will +// returns |nil| in case of error. +NSData *readData(int fileId, ssize_t length) { + NSMutableData *data = [NSMutableData dataWithLength:length]; + char *bytes = (char *)[data bytes]; + + if (read(fileId, bytes, length) != length) + return nil; + + return data; +} + +//============================================================================= +// Read the configuration from the config file. +NSDictionary *readConfigurationData(const char *configFile) { + int fileId = open(configFile, O_RDONLY, 0600); + if (fileId == -1) { + fprintf(stderr, "Breakpad Uploader: Couldn't open config file %s - %s", + configFile, strerror(errno)); + } + + // we want to avoid a build-up of old config files even if they + // have been incorrectly written by the framework + if (unlink(configFile)) { + fprintf(stderr, "Breakpad Uploader: Couldn't unlink config file %s - %s", + configFile, strerror(errno)); + } + + if (fileId == -1) { + return nil; + } + + NSMutableDictionary *config = [NSMutableDictionary dictionary]; + + while (1) { + NSString *key = readString(fileId); + + if (![key length]) + break; + + // Read the data. Try to convert to a UTF-8 string, or just save + // the data + NSString *lenStr = readString(fileId); + ssize_t len = [lenStr intValue]; + NSData *data = readData(fileId, len); + id value = [[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding]; + + [config setObject:(value ? value : data) forKey:key]; + [value release]; + } + + close(fileId); + return config; +} +} // namespace + +#pragma mark - + +@interface Uploader(PrivateMethods) + +// Update |parameters_| as well as the server parameters using |config|. +- (void)translateConfigurationData:(NSDictionary *)config; + +// Read the minidump referenced in |parameters_| and update |minidumpContents_| +// with its content. +- (BOOL)readMinidumpData; + +// Read the log files referenced in |parameters_| and update |logFileData_| +// with their content. +- (BOOL)readLogFileData; + +// Returns a unique client id (user-specific), creating a persistent +// one in the user defaults, if necessary. +- (NSString*)clientID; + +// Returns a dictionary that can be used to map Breakpad parameter names to +// URL parameter names. +- (NSMutableDictionary *)dictionaryForServerType:(NSString *)serverType; + +// Helper method to set HTTP parameters based on server type. This is +// called right before the upload - crashParameters will contain, on exit, +// URL parameters that should be sent with the minidump. +- (BOOL)populateServerDictionary:(NSMutableDictionary *)crashParameters; + +// Initialization helper to create dictionaries mapping Breakpad +// parameters to URL parameters +- (void)createServerParameterDictionaries; + +// Accessor method for the URL parameter dictionary +- (NSMutableDictionary *)urlParameterDictionary; + +// Records the uploaded crash ID to the log file. +- (void)logUploadWithID:(const char *)uploadID; + +// Builds an URL parameter for a given dictionary key. Uses Uploader's +// parameters to provide its value. Returns nil if no item is stored for the +// given key. +- (NSURLQueryItem *)queryItemWithName:(NSString *)queryItemName + forParamKey:(NSString *)key; +@end + +@implementation Uploader + +//============================================================================= +- (id)initWithConfigFile:(const char *)configFile { + NSDictionary *config = readConfigurationData(configFile); + if (!config) + return nil; + + return [self initWithConfig:config]; +} + +//============================================================================= +- (id)initWithConfig:(NSDictionary *)config { + if ((self = [super init])) { + // Because the reporter is embedded in the framework (and many copies + // of the framework may exist) its not completely certain that the OS + // will obey the com.apple.PreferenceSync.ExcludeAllSyncKeys in our + // Info.plist. To make sure, also set the key directly if needed. + NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; + if (![ud boolForKey:kApplePrefsSyncExcludeAllKey]) { + [ud setBool:YES forKey:kApplePrefsSyncExcludeAllKey]; + } + + [self createServerParameterDictionaries]; + + [self translateConfigurationData:config]; + + // Read the minidump into memory. + [self readMinidumpData]; + [self readLogFileData]; + } + return self; +} + +//============================================================================= ++ (NSDictionary *)readConfigurationDataFromFile:(NSString *)configFile { + return readConfigurationData([configFile fileSystemRepresentation]); +} + +//============================================================================= +- (void)translateConfigurationData:(NSDictionary *)config { + parameters_ = [[NSMutableDictionary alloc] init]; + + NSEnumerator *it = [config keyEnumerator]; + while (NSString *key = [it nextObject]) { + // If the keyname is prefixed by BREAKPAD_SERVER_PARAMETER_PREFIX + // that indicates that it should be uploaded to the server along + // with the minidump, so we treat it specially. + if ([key hasPrefix:@BREAKPAD_SERVER_PARAMETER_PREFIX]) { + NSString *urlParameterKey = + [key substringFromIndex:[@BREAKPAD_SERVER_PARAMETER_PREFIX length]]; + if ([urlParameterKey length]) { + id value = [config objectForKey:key]; + if ([value isKindOfClass:[NSString class]]) { + [self addServerParameter:(NSString *)value + forKey:urlParameterKey]; + } else { + [self addServerParameter:(NSData *)value + forKey:urlParameterKey]; + } + } + } else { + [parameters_ setObject:[config objectForKey:key] forKey:key]; + } + } + + // generate a unique client ID based on this host's MAC address + // then add a key/value pair for it + NSString *clientID = [self clientID]; + [parameters_ setObject:clientID forKey:@"guid"]; +} + +// Per user per machine +- (NSString *)clientID { + NSUserDefaults *ud = [NSUserDefaults standardUserDefaults]; + NSString *crashClientID = [ud stringForKey:kClientIdPreferenceKey]; + if (crashClientID) { + return crashClientID; + } + + // Otherwise, if we have no client id, generate one! + srandom((int)[[NSDate date] timeIntervalSince1970]); + long clientId1 = random(); + long clientId2 = random(); + long clientId3 = random(); + crashClientID = [NSString stringWithFormat:@"%lx%lx%lx", + clientId1, clientId2, clientId3]; + + [ud setObject:crashClientID forKey:kClientIdPreferenceKey]; + [ud synchronize]; + return crashClientID; +} + +//============================================================================= +- (BOOL)readLogFileData { +#if TARGET_OS_IPHONE + return NO; +#else + unsigned int logFileCounter = 0; + + NSString *logPath; + size_t logFileTailSize = + [[parameters_ objectForKey:@BREAKPAD_LOGFILE_UPLOAD_SIZE] intValue]; + + NSMutableArray *logFilenames; // An array of NSString, one per log file + logFilenames = [[NSMutableArray alloc] init]; + + char tmpDirTemplate[80] = "/tmp/CrashUpload-XXXXX"; + char *tmpDir = mkdtemp(tmpDirTemplate); + + // Construct key names for the keys we expect to contain log file paths + for(logFileCounter = 0;; logFileCounter++) { + NSString *logFileKey = [NSString stringWithFormat:@"%@%d", + @BREAKPAD_LOGFILE_KEY_PREFIX, + logFileCounter]; + + logPath = [parameters_ objectForKey:logFileKey]; + + // They should all be consecutive, so if we don't find one, assume + // we're done + + if (!logPath) { + break; + } + + NSData *entireLogFile = [[NSData alloc] initWithContentsOfFile:logPath]; + + if (entireLogFile == nil) { + continue; + } + + NSRange fileRange; + + // Truncate the log file, only if necessary + + if ([entireLogFile length] <= logFileTailSize) { + fileRange = NSMakeRange(0, [entireLogFile length]); + } else { + fileRange = NSMakeRange([entireLogFile length] - logFileTailSize, + logFileTailSize); + } + + char tmpFilenameTemplate[100]; + + // Generate a template based on the log filename + sprintf(tmpFilenameTemplate,"%s/%s-XXXX", tmpDir, + [[logPath lastPathComponent] fileSystemRepresentation]); + + char *tmpFile = mktemp(tmpFilenameTemplate); + + NSData *logSubdata = [entireLogFile subdataWithRange:fileRange]; + NSString *tmpFileString = [NSString stringWithUTF8String:tmpFile]; + [logSubdata writeToFile:tmpFileString atomically:NO]; + + [logFilenames addObject:[tmpFileString lastPathComponent]]; + [entireLogFile release]; + } + + if ([logFilenames count] == 0) { + [logFilenames release]; + logFileData_ = nil; + return NO; + } + + // now, bzip all files into one + NSTask *tarTask = [[NSTask alloc] init]; + + [tarTask setCurrentDirectoryPath:[NSString stringWithUTF8String:tmpDir]]; + [tarTask setLaunchPath:@"/usr/bin/tar"]; + + NSMutableArray *bzipArgs = [NSMutableArray arrayWithObjects:@"-cjvf", + @"log.tar.bz2",nil]; + [bzipArgs addObjectsFromArray:logFilenames]; + + [logFilenames release]; + + [tarTask setArguments:bzipArgs]; + [tarTask launch]; + [tarTask waitUntilExit]; + [tarTask release]; + + NSString *logTarFile = [NSString stringWithFormat:@"%s/log.tar.bz2",tmpDir]; + logFileData_ = [[NSData alloc] initWithContentsOfFile:logTarFile]; + if (logFileData_ == nil) { + fprintf(stderr, "Breakpad Uploader: Cannot find temp tar log file: %s", + [logTarFile UTF8String]); + return NO; + } + return YES; +#endif // TARGET_OS_IPHONE +} + +//============================================================================= +- (BOOL)readMinidumpData { + NSString *minidumpDir = + [parameters_ objectForKey:@kReporterMinidumpDirectoryKey]; + NSString *minidumpID = [parameters_ objectForKey:@kReporterMinidumpIDKey]; + + if (![minidumpID length]) + return NO; + + NSString *path = [minidumpDir stringByAppendingPathComponent:minidumpID]; + path = [path stringByAppendingPathExtension:@"dmp"]; + + // check the size of the minidump and limit it to a reasonable size + // before attempting to load into memory and upload + const char *fileName = [path fileSystemRepresentation]; + struct stat fileStatus; + + BOOL success = YES; + + if (!stat(fileName, &fileStatus)) { + if (fileStatus.st_size > kMinidumpFileLengthLimit) { + fprintf(stderr, "Breakpad Uploader: minidump file too large " \ + "to upload : %d\n", (int)fileStatus.st_size); + success = NO; + } + } else { + fprintf(stderr, "Breakpad Uploader: unable to determine minidump " \ + "file length\n"); + success = NO; + } + + if (success) { + minidumpContents_ = [[NSData alloc] initWithContentsOfFile:path]; + success = ([minidumpContents_ length] ? YES : NO); + } + + if (!success) { + // something wrong with the minidump file -- delete it + unlink(fileName); + } + + return success; +} + +#pragma mark - +//============================================================================= + +- (void)createServerParameterDictionaries { + serverDictionary_ = [[NSMutableDictionary alloc] init]; + socorroDictionary_ = [[NSMutableDictionary alloc] init]; + googleDictionary_ = [[NSMutableDictionary alloc] init]; + extraServerVars_ = [[NSMutableDictionary alloc] init]; + + [serverDictionary_ setObject:socorroDictionary_ forKey:kSocorroServerType]; + [serverDictionary_ setObject:googleDictionary_ forKey:kGoogleServerType]; + + [googleDictionary_ setObject:@"ptime" forKey:@BREAKPAD_PROCESS_UP_TIME]; + [googleDictionary_ setObject:@"email" forKey:@BREAKPAD_EMAIL]; + [googleDictionary_ setObject:@"comments" forKey:@BREAKPAD_COMMENTS]; + [googleDictionary_ setObject:@"prod" forKey:@BREAKPAD_PRODUCT]; + [googleDictionary_ setObject:@"ver" forKey:@BREAKPAD_VERSION]; + [googleDictionary_ setObject:@"guid" forKey:@"guid"]; + + [socorroDictionary_ setObject:@"Comments" forKey:@BREAKPAD_COMMENTS]; + [socorroDictionary_ setObject:@"CrashTime" + forKey:@BREAKPAD_PROCESS_CRASH_TIME]; + [socorroDictionary_ setObject:@"StartupTime" + forKey:@BREAKPAD_PROCESS_START_TIME]; + [socorroDictionary_ setObject:@"Version" + forKey:@BREAKPAD_VERSION]; + [socorroDictionary_ setObject:@"ProductName" + forKey:@BREAKPAD_PRODUCT]; + [socorroDictionary_ setObject:@"Email" + forKey:@BREAKPAD_EMAIL]; +} + +- (NSMutableDictionary *)dictionaryForServerType:(NSString *)serverType { + if (serverType == nil || [serverType length] == 0) { + return [serverDictionary_ objectForKey:kDefaultServerType]; + } + return [serverDictionary_ objectForKey:serverType]; +} + +- (NSMutableDictionary *)urlParameterDictionary { + NSString *serverType = [parameters_ objectForKey:@BREAKPAD_SERVER_TYPE]; + return [self dictionaryForServerType:serverType]; + +} + +- (BOOL)populateServerDictionary:(NSMutableDictionary *)crashParameters { + NSDictionary *urlParameterNames = [self urlParameterDictionary]; + + id key; + NSEnumerator *enumerator = [parameters_ keyEnumerator]; + + while ((key = [enumerator nextObject])) { + // The key from parameters_ corresponds to a key in + // urlParameterNames. The value in parameters_ gets stored in + // crashParameters with a key that is the value in + // urlParameterNames. + + // For instance, if parameters_ has [PRODUCT_NAME => "FOOBAR"] and + // urlParameterNames has [PRODUCT_NAME => "pname"] the final HTTP + // URL parameter becomes [pname => "FOOBAR"]. + NSString *breakpadParameterName = (NSString *)key; + NSString *urlParameter = [urlParameterNames + objectForKey:breakpadParameterName]; + if (urlParameter) { + [crashParameters setObject:[parameters_ objectForKey:key] + forKey:urlParameter]; + } + } + + // Now, add the parameters that were added by the application. + enumerator = [extraServerVars_ keyEnumerator]; + + while ((key = [enumerator nextObject])) { + NSString *urlParameterName = (NSString *)key; + NSString *urlParameterValue = + [extraServerVars_ objectForKey:urlParameterName]; + [crashParameters setObject:urlParameterValue + forKey:urlParameterName]; + } + return YES; +} + +- (void)addServerParameter:(id)value forKey:(NSString *)key { + [extraServerVars_ setObject:value forKey:key]; +} + +//============================================================================= +- (void)handleNetworkResponse:(NSData *)data withError:(NSError *)error { + NSString *result = [[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding]; + const char *reportID = "ERR"; + if (error) { + fprintf(stderr, "Breakpad Uploader: Send Error: %s\n", + [[error description] UTF8String]); + } else { + NSCharacterSet *trimSet = + [NSCharacterSet whitespaceAndNewlineCharacterSet]; + reportID = [[result stringByTrimmingCharactersInSet:trimSet] UTF8String]; + [self logUploadWithID:reportID]; + } + if (uploadCompletion_) { + uploadCompletion_([NSString stringWithUTF8String:reportID], error); + } + + // rename the minidump file according to the id returned from the server + NSString *minidumpDir = + [parameters_ objectForKey:@kReporterMinidumpDirectoryKey]; + NSString *minidumpID = [parameters_ objectForKey:@kReporterMinidumpIDKey]; + + NSString *srcString = [NSString stringWithFormat:@"%@/%@.dmp", + minidumpDir, minidumpID]; + NSString *destString = [NSString stringWithFormat:@"%@/%s.dmp", + minidumpDir, reportID]; + + const char *src = [srcString fileSystemRepresentation]; + const char *dest = [destString fileSystemRepresentation]; + + if (rename(src, dest) == 0) { + fprintf(stderr, + "Breakpad Uploader: Renamed %s to %s after successful upload", src, + dest); + } + else { + // can't rename - don't worry - it's not important for users + fprintf(stderr, "Breakpad Uploader: successful upload report ID = %s\n", + reportID); + } + [result release]; +} + +//============================================================================= +- (NSURLQueryItem *)queryItemWithName:(NSString *)queryItemName + forParamKey:(NSString *)key { + NSString *value = [parameters_ objectForKey:key]; + NSString *escapedValue = + [value stringByAddingPercentEncodingWithAllowedCharacters: + [NSCharacterSet URLQueryAllowedCharacterSet]]; + return [NSURLQueryItem queryItemWithName:queryItemName value:escapedValue]; +} + +//============================================================================= +- (void)setUploadCompletionBlock:(UploadCompletionBlock)uploadCompletion { + uploadCompletion_ = uploadCompletion; +} + +//============================================================================= +- (void)report { + NSURL *url = [NSURL URLWithString:[parameters_ objectForKey:@BREAKPAD_URL]]; + + NSString *serverType = [parameters_ objectForKey:@BREAKPAD_SERVER_TYPE]; + if ([serverType length] == 0 || + [serverType isEqualToString:kGoogleServerType]) { + // when communicating to Google's crash collecting service, add URL params + // which identify the product + NSURLComponents *urlComponents = [NSURLComponents componentsWithURL:url + resolvingAgainstBaseURL:false]; + NSMutableArray *queryItemsToAdd = [urlComponents.queryItems mutableCopy]; + if (queryItemsToAdd == nil) { + queryItemsToAdd = [[NSMutableArray alloc] init]; + } + + NSURLQueryItem *queryItemProduct = + [self queryItemWithName:@"product" forParamKey:@BREAKPAD_PRODUCT]; + NSURLQueryItem *queryItemVersion = + [self queryItemWithName:@"version" forParamKey:@BREAKPAD_VERSION]; + NSURLQueryItem *queryItemGuid = + [self queryItemWithName:@"guid" forParamKey:@"guid"]; + + if (queryItemProduct != nil) [queryItemsToAdd addObject:queryItemProduct]; + if (queryItemVersion != nil) [queryItemsToAdd addObject:queryItemVersion]; + if (queryItemGuid != nil) [queryItemsToAdd addObject:queryItemGuid]; + + urlComponents.queryItems = queryItemsToAdd; + url = [urlComponents URL]; + } + + HTTPMultipartUpload *upload = [[HTTPMultipartUpload alloc] initWithURL:url]; + NSMutableDictionary *uploadParameters = [NSMutableDictionary dictionary]; + + if (![self populateServerDictionary:uploadParameters]) { + [upload release]; + return; + } + + [upload setParameters:uploadParameters]; + + // Add minidump file + if (minidumpContents_) { + [upload addFileContents:minidumpContents_ name:@"upload_file_minidump"]; + + // If there is a log file, upload it together with the minidump. + if (logFileData_) { + [upload addFileContents:logFileData_ name:@"log"]; + } + + // Send it + NSError *error = nil; + NSData *data = [upload send:&error]; + + if (![url isFileURL]) { + [self handleNetworkResponse:data withError:error]; + } else { + if (error) { + fprintf(stderr, "Breakpad Uploader: Error writing request file: %s\n", + [[error description] UTF8String]); + } + } + + } else { + // Minidump is missing -- upload just the log file. + if (logFileData_) { + [self uploadData:logFileData_ name:@"log"]; + } + } + [upload release]; +} + +- (void)uploadData:(NSData *)data name:(NSString *)name { + NSURL *url = [NSURL URLWithString:[parameters_ objectForKey:@BREAKPAD_URL]]; + NSMutableDictionary *uploadParameters = [NSMutableDictionary dictionary]; + + if (![self populateServerDictionary:uploadParameters]) + return; + + HTTPMultipartUpload *upload = + [[HTTPMultipartUpload alloc] initWithURL:url]; + + [uploadParameters setObject:name forKey:@"type"]; + [upload setParameters:uploadParameters]; + [upload addFileContents:data name:name]; + + [upload send:nil]; + [upload release]; +} + +- (void)logUploadWithID:(const char *)uploadID { + NSString *minidumpDir = + [parameters_ objectForKey:@kReporterMinidumpDirectoryKey]; + NSString *logFilePath = [NSString stringWithFormat:@"%@/%s", + minidumpDir, kReporterLogFilename]; + NSString *logLine = [NSString stringWithFormat:@"%0.f,%s\n", + [[NSDate date] timeIntervalSince1970], uploadID]; + NSData *logData = [logLine dataUsingEncoding:NSUTF8StringEncoding]; + + NSFileManager *fileManager = [NSFileManager defaultManager]; + if ([fileManager fileExistsAtPath:logFilePath]) { + NSFileHandle *logFileHandle = + [NSFileHandle fileHandleForWritingAtPath:logFilePath]; + [logFileHandle seekToEndOfFile]; + [logFileHandle writeData:logData]; + [logFileHandle closeFile]; + } else { + [fileManager createFileAtPath:logFilePath + contents:logData + attributes:nil]; + } +} + +//============================================================================= +- (NSMutableDictionary *)parameters { + return parameters_; +} + +//============================================================================= +- (void)dealloc { + [parameters_ release]; + [minidumpContents_ release]; + [logFileData_ release]; + [googleDictionary_ release]; + [socorroDictionary_ release]; + [serverDictionary_ release]; + [extraServerVars_ release]; + [super dealloc]; +} + +@end diff --git a/shared/sentry/external/breakpad/src/client/mac/testapp/Controller.h b/shared/sentry/external/breakpad/src/client/mac/testapp/Controller.h new file mode 100644 index 000000000..7b3be2d69 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/testapp/Controller.h @@ -0,0 +1,65 @@ +// 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 + +#import + +enum BreakpadForkBehavior { + DONOTHING = 0, + UNINSTALL, + RESETEXCEPTIONPORT +}; + +enum BreakpadForkTestCrashPoint { + DURINGLAUNCH = 5, + AFTERLAUNCH = 6, + BETWEENFORKEXEC = 7 +}; + +@interface Controller : NSObject { + IBOutlet NSWindow *window_; + IBOutlet NSWindow *forkTestOptions_; + + BreakpadRef breakpad_; + + enum BreakpadForkBehavior bpForkOption; + + BOOL useVFork; + enum BreakpadForkTestCrashPoint progCrashPoint; +} + +- (IBAction)crash:(id)sender; +- (IBAction)forkTestOptions:(id)sender; +- (IBAction)forkTestGo:(id)sender; +- (IBAction)showForkTestWindow:(id) sender; +- (void)generateReportWithoutCrash:(id)sender; +- (void)awakeFromNib; + +@end diff --git a/shared/sentry/external/breakpad/src/client/mac/testapp/Controller.m b/shared/sentry/external/breakpad/src/client/mac/testapp/Controller.m new file mode 100644 index 000000000..87c43024b --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/testapp/Controller.m @@ -0,0 +1,261 @@ +// 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 + +#import "Controller.h" +#import "TestClass.h" +#import "GTMDefines.h" +#include +#include + +@implementation Controller + +- (void)causeCrash { + float *aPtr = nil; + NSLog(@"Crash!"); + NSLog(@"Bad programmer: %f", *aPtr); +} + +- (void)generateReportWithoutCrash:(id)sender { + BreakpadGenerateAndSendReport(breakpad_); +} + +- (IBAction)showForkTestWindow:(id) sender { + [forkTestOptions_ setIsVisible:YES]; +} + +- (IBAction)forkTestOptions:(id)sender { + NSInteger tag = [[sender selectedCell] tag]; + NSLog(@"sender tag: %d", tag); + if (tag <= 2) { + bpForkOption = tag; + } + + if (tag == 3) { + useVFork = NO; + } + + if (tag == 4) { + useVFork = YES; + } + + if (tag >= 5 && tag <= 7) { + progCrashPoint = tag; + } + +} + +- (IBAction)forkTestGo:(id)sender { + + NSString *resourcePath = [[NSBundle bundleForClass: + [self class]] resourcePath]; + NSString *execProgname = nil; + if (progCrashPoint == DURINGLAUNCH) { + execProgname = [resourcePath stringByAppendingString:@"/crashduringload"]; + } else if (progCrashPoint == AFTERLAUNCH) { + execProgname = [resourcePath stringByAppendingString:@"/crashInMain"]; + } + + const char *progName = NULL; + if (progCrashPoint != BETWEENFORKEXEC) { + progName = [execProgname UTF8String]; + } + + int pid; + + if (bpForkOption == UNINSTALL) { + BreakpadRelease(breakpad_); + } + + if (useVFork) { + pid = vfork(); + } else { + pid = fork(); + } + + if (pid == 0) { + sleep(3); + NSLog(@"Child continuing"); + FILE *fd = fopen("/tmp/childlog.txt","wt"); + kern_return_t kr; + if (bpForkOption == RESETEXCEPTIONPORT) { + kr = task_set_exception_ports(mach_task_self(), + EXC_MASK_BAD_ACCESS | EXC_MASK_BAD_INSTRUCTION | + EXC_MASK_ARITHMETIC | EXC_MASK_BREAKPOINT, + MACH_PORT_NULL, + EXCEPTION_DEFAULT, + THREAD_STATE_NONE); + fprintf(fd,"task_set_exception_ports returned %d\n", kr); + } + + if (progCrashPoint == BETWEENFORKEXEC) { + fprintf(fd,"crashing post-fork\n"); + int *a = NULL; + printf("%d\n",*a++); + } + + fprintf(fd,"about to call exec with %s\n", progName); + fclose(fd); + int i = execl(progName, progName, NULL); + fprintf(fd, "exec returned! %d\n", i); + fclose(fd); + } +} + +- (IBAction)crash:(id)sender { + NSInteger tag = [sender tag]; + + if (tag == 1) { + [NSObject cancelPreviousPerformRequestsWithTarget:self]; + [self performSelector:@selector(causeCrash) withObject:nil afterDelay:10.0]; + [sender setState:NSOnState]; + return; + } + + if (tag == 2 && breakpad_) { + BreakpadRelease(breakpad_); + breakpad_ = NULL; + return; + } + + [self causeCrash]; +} + +- (void)anotherThread { + NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + TestClass *tc = [[TestClass alloc] init]; + + [tc wait]; + + [pool release]; +} + +- (void)awakeFromNib { + NSBundle *bundle = [NSBundle mainBundle]; + NSDictionary *info = [bundle infoDictionary]; + + + breakpad_ = BreakpadCreate(info); + + // Do some unit tests with keys + // first a series of bogus values + BreakpadSetKeyValue(breakpad_, nil, @"bad2"); + BreakpadSetKeyValue(nil, @"bad3", @"bad3"); + + // Now some good ones + BreakpadSetKeyValue(breakpad_,@"key1", @"value1"); + BreakpadSetKeyValue(breakpad_,@"key2", @"value2"); + BreakpadSetKeyValue(breakpad_,@"key3", @"value3"); + + // Look for a bogus one that we didn't try to set + NSString *test = BreakpadKeyValue(breakpad_, @"bad4"); + if (test) { + NSLog(@"Bad BreakpadKeyValue (bad4)"); + } + + // Look for a bogus one we did try to set + test = BreakpadKeyValue(breakpad_, @"bad1"); + if (test) { + NSLog(@"Bad BreakpadKeyValue (bad1)"); + } + + // Test some bad args for BreakpadKeyValue + test = BreakpadKeyValue(nil, @"bad5"); + if (test) { + NSLog(@"Bad BreakpadKeyValue (bad5)"); + } + + test = BreakpadKeyValue(breakpad_, nil); + if (test) { + NSLog(@"Bad BreakpadKeyValue (nil)"); + } + + // Find some we did set + test = BreakpadKeyValue(breakpad_, @"key1"); + if (![test isEqualToString:@"value1"]) { + NSLog(@"Can't find BreakpadKeyValue (key1)"); + } + test = BreakpadKeyValue(breakpad_, @"key2"); + if (![test isEqualToString:@"value2"]) { + NSLog(@"Can't find BreakpadKeyValue (key2)"); + } + test = BreakpadKeyValue(breakpad_, @"key3"); + if (![test isEqualToString:@"value3"]) { + NSLog(@"Can't find BreakpadKeyValue (key3)"); + } + + // Bad args for BreakpadRemoveKeyValue + BreakpadRemoveKeyValue(nil, @"bad6"); + BreakpadRemoveKeyValue(breakpad_, nil); + + // Remove one that is valid + BreakpadRemoveKeyValue(breakpad_, @"key3"); + + // Try and find it + test = BreakpadKeyValue(breakpad_, @"key3"); + if (test) { + NSLog(@"Shouldn't find BreakpadKeyValue (key3)"); + } + + // Try and remove it again + BreakpadRemoveKeyValue(breakpad_, @"key3"); + + // Try removal by setting to nil + BreakpadSetKeyValue(breakpad_,@"key2", nil); + // Try and find it + test = BreakpadKeyValue(breakpad_, @"key2"); + if (test) { + NSLog(@"Shouldn't find BreakpadKeyValue (key2)"); + } + + BreakpadAddUploadParameter(breakpad_, + @"MeaningOfLife", + @"42"); + [NSThread detachNewThreadSelector:@selector(anotherThread) + toTarget:self withObject:nil]; + + NSUserDefaults *args = [NSUserDefaults standardUserDefaults]; + + // If the user specified autocrash on the command line, toggle + // Breakpad to not confirm and crash immediately. This is for + // automated testing. + if ([args boolForKey:@"autocrash"]) { + BreakpadSetKeyValue(breakpad_, + @BREAKPAD_SKIP_CONFIRM, + @"YES"); + [self causeCrash]; + } + + progCrashPoint = DURINGLAUNCH; + [window_ center]; + [window_ makeKeyAndOrderFront:self]; +} + +@end diff --git a/shared/sentry/external/breakpad/src/client/mac/testapp/English.lproj/InfoPlist.strings b/shared/sentry/external/breakpad/src/client/mac/testapp/English.lproj/InfoPlist.strings new file mode 100644 index 0000000000000000000000000000000000000000..b8c6c6bf0ba1a55cea7e4656822a4ee5d65546b8 GIT binary patch literal 192 zcmXYq!4APt5Jk_rU(r}Lg4kGyl?4*9;0vl+1Z~qI;g9?zju)9^?z}s9&U=68lMu6{ zA|vI@ktaDjy}R1b>Qt0!tQPi#ufn-U9r7(#-IM7@NF_2OYsH2+H!gBZ*6vfeDO;~{ krV?r|8u@z;fiaQO;bot>t@MA%?*Gp$OhX3N$lRFd3!KFv6#xJL literal 0 HcmV?d00001 diff --git a/shared/sentry/external/breakpad/src/client/mac/testapp/English.lproj/MainMenu.xib b/shared/sentry/external/breakpad/src/client/mac/testapp/English.lproj/MainMenu.xib new file mode 100644 index 000000000..840c0db33 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/testapp/English.lproj/MainMenu.xib @@ -0,0 +1,3748 @@ + + + + 1050 + 10F569 + 788 + 1038.29 + 461.00 + + com.apple.InterfaceBuilder.CocoaPlugin + 788 + + + YES + + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + + + YES + + YES + + + YES + + + + YES + + + NSApplication + + + + FirstResponder + + + NSApplication + + + 15 + 2 + {{945, 874}, {320, 188}} + 1886912512 + Window + NSWindow + + View + + {1.79769e+308, 1.79769e+308} + {213, 107} + + + 256 + + YES + + + 301 + {{14, 140}, {292, 32}} + + + YES + + 67239424 + 134217728 + Crash! (Airbag Installed) + + LucidaGrande + 13 + 1044 + + + -2038284033 + 1 + + + + + + 200 + 25 + + + + + 301 + {{14, 76}, {292, 32}} + + + 2 + YES + + 67239424 + 134217728 + Crash! (Airbag NOT Installed) + + + 2 + -2038284033 + 1 + + + + + + 200 + 25 + + + + + 301 + {{14, 108}, {292, 32}} + + + 1 + YES + + 67239424 + 134217728 + Crash! (Airbag Installed w/10sec delay) + + + 1 + -2038284033 + 1 + + + + + + 200 + 25 + + + + + 268 + {{14, 44}, {292, 32}} + + + YES + + 67239424 + 134217728 + Fork Test + + + -2038284033 + 129 + + + 200 + 25 + + + + + 268 + {{14, 12}, {292, 32}} + + + YES + + 67239424 + 134217728 + Generate report without crash + + + -2038284033 + 129 + + + 200 + 25 + + + + {320, 188} + + + + {{0, 0}, {1440, 878}} + {213, 129} + {1.79769e+308, 1.79769e+308} + + + MainMenu + + YES + + + NewApplication + + 1048576 + 2147483647 + + NSImage + NSMenuCheckmark + + + NSImage + NSMenuMixedState + + submenuAction: + + NewApplication + + YES + + + About NewApplication + + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Preferences… + , + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Services + + 1048576 + 2147483647 + + + submenuAction: + + + Services + + + YES + + _NSServicesMenu + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Hide NewApplication + h + 1048576 + 2147483647 + + + + + + Hide Others + h + 1572864 + 2147483647 + + + + + + Show All + + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Quit NewApplication + q + 1048576 + 2147483647 + + + + + _NSAppleMenu + + + + + File + + 1048576 + 2147483647 + + + submenuAction: + + + File + + + YES + + + New + n + 1048576 + 2147483647 + + + + + + Open... + o + 1048576 + 2147483647 + + + + + + Open Recent + + 1048576 + 2147483647 + + + submenuAction: + + + Open Recent + + + YES + + + Clear Menu + + 1048576 + 2147483647 + + + + + _NSRecentDocumentsMenu + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Close + w + 1048576 + 2147483647 + + + + + + Save + s + 1048576 + 2147483647 + + + + + + Save As… + S + 1048576 + 2147483647 + + + + + + Revert + + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Page Setup… + P + 1048576 + 2147483647 + + + + + + Print… + p + 1048576 + 2147483647 + + + + + + + + + Edit + + 1048576 + 2147483647 + + + submenuAction: + + + Edit + + + YES + + + Undo + z + 1048576 + 2147483647 + + + + + + Redo + Z + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Cut + x + 1048576 + 2147483647 + + + + + + Copy + c + 1048576 + 2147483647 + + + + + + Paste + v + 1048576 + 2147483647 + + + + + + Paste and Match Style + V + 1572864 + 2147483647 + + + + + + Delete + + 1048576 + 2147483647 + + + + + + Select All + a + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Find + + 1048576 + 2147483647 + + + submenuAction: + + + Find + + + YES + + + Find… + f + 1048576 + 2147483647 + + + 1 + + + + Find Next + g + 1048576 + 2147483647 + + + 2 + + + + Find Previous + G + 1048576 + 2147483647 + + + 3 + + + + Use Selection for Find + e + 1048576 + 2147483647 + + + 7 + + + + Jump to Selection + j + 1048576 + 2147483647 + + + + + + + + + Spelling + + 1048576 + 2147483647 + + + submenuAction: + + Spelling + + YES + + + Spelling… + : + 1048576 + 2147483647 + + + + + + Check Spelling + ; + 1048576 + 2147483647 + + + + + + Check Spelling as You Type + + 1048576 + 2147483647 + + + + + + + + + + + + Window + + 1048576 + 2147483647 + + + submenuAction: + + + Window + + + YES + + + Minimize + m + 1048576 + 2147483647 + + + + + + Zoom + + 1048576 + 2147483647 + + + + + + YES + YES + + + 1048576 + 2147483647 + + + + + + Bring All to Front + + 1048576 + 2147483647 + + + + + _NSWindowsMenu + + + + + Help + + 1048576 + 2147483647 + + + submenuAction: + + Help + + YES + + + NewApplication Help + ? + 1048576 + 2147483647 + + + + + + + + _NSMainMenu + + + Controller + + + 15 + 2 + {{858, 755}, {787, 260}} + 603979776 + Window + NSWindow + + {1.79769e+308, 1.79769e+308} + + + 256 + + YES + + + 268 + {{20, 7}, {645, 79}} + + YES + 3 + 1 + + YES + + -2080244224 + 0 + program crashes during launch because of missing dylib + + + 5 + 1211912703 + 0 + + NSRadioButton + + + + 200 + 25 + + + 67239424 + 0 + program crashes after launch + + + 6 + 1211912703 + 0 + + 549453824 + {18, 18} + + YES + + YES + + + + TU0AKgAABRgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAMAAAADAAAAAwAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAADwRERGLJycnySsrK/A1NTXw +IyMjyRwcHIsJCQk8AAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFRUVdVBQUOCoqKj/ +29vb//n5+f/6+vr/2tra/6qqqv9UVFTgHx8fdQAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUZGRl5 +dXV198PDw//8/Pz////////////////////////////U1NT/fHx89yUlJXkAAAAFAAAAAAAAAAAAAAAA +AAAAAxEREUZqamrmtbW1/+3t7f/+/v7//v7+//7+/v/9/f3//f39//39/f/39/f/xMTE/3d3d+YZGRlG +AAAAAwAAAAAAAAAAAAAACkJCQqGtra3/xsbG/+vr6//y8vL/9fX1//X19f/z8/P/9fX1//Ly8v/u7u7/ +0tLS/6+vr/9KSkqhAAAACgAAAAAAAAAAAAAAF3h4eN2/v7//z8/P/93d3f/q6ur/7+/v/+/v7//w8PD/ +7e3t/+3t7f/i4uL/zs7O/8XFxf98fHzdAAAAFwAAAAAAAAADAAAAJKSkpPjOzs7/2dnZ/+Dg4P/i4uL/ +5eXl/+bm5v/n5+f/5eXl/+Li4v/e3t7/2tra/9DQ0P+srKz4AAAAJAAAAAMAAAADAAAALrCwsPrW1tb/ +3t7e/+Tk5P/p6en/6+vr/+zs7P/p6en/6+vr/+fn5//k5OT/4ODg/9nZ2f+zs7P6AAAALgAAAAMAAAAD +AAAALp2dnezg4OD/5eXl/+rq6v/u7u7/8PDw//Dw8P/x8fH/8PDw/+7u7v/q6ur/5ubm/+Hh4f+ZmZns +AAAALgAAAAMAAAADAAAAJG5ubs/l5eX/6enp/+/v7//y8vL/9vb2//r6+v/5+fn/9/f3//b29v/x8fH/ +6+vr/+Tk5P9ra2vPAAAAJAAAAAMAAAAAAAAAFy4uLpPCwsL67Ozs//Pz8//5+fn//v7+//7+/v/+/v7/ +/v7+//v7+//19fX/8PDw/8LCwvosLCyTAAAAFwAAAAAAAAAAAAAACgAAAENfX1/S5OTk/vn5+f/+/v7/ +///////////////////////////8/Pz/5ubm/l9fX9IAAABDAAAACgAAAAAAAAAAAAAAAwAAABcAAABl +YmJi3NLS0v3////////////////////////////////V1dX9ZGRk3AAAAGUAAAAXAAAAAwAAAAAAAAAA +AAAAAAAAAAUAAAAfAAAAZTMzM8KAgIDwv7+//O3t7f/t7e3/v7+//ICAgPAzMzPCAAAAZQAAAB8AAAAF +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAFwAAAEMAAAB3AAAAnwAAALMAAACzAAAAnwAAAHcAAABD +AAAAFwAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAoAAAAXAAAAJAAAAC4AAAAu +AAAAJAAAABcAAAAKAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAwAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgEAAAMAAAABABIAAAEB +AAMAAAABABIAAAECAAMAAAAEAAAFxgEDAAMAAAABAAEAAAEGAAMAAAABAAIAAAERAAQAAAABAAAACAES +AAMAAAABAAEAAAEVAAMAAAABAAQAAAEWAAMAAAABABIAAAEXAAQAAAABAAAFEAEcAAMAAAABAAEAAAFS +AAMAAAABAAEAAAFTAAMAAAAEAAAFzodzAAcAAAwYAAAF1gAAAAAACAAIAAgACAABAAEAAQABAAAMGGFw +cGwCAAAAbW50clJHQiBYWVogB9YABAADABMALAASYWNzcEFQUEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAPbWAAEAAAAA0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAOclhZWgAAASwAAAAUZ1hZWgAAAUAAAAAUYlhZWgAAAVQAAAAUd3RwdAAAAWgAAAAUY2hhZAAA +AXwAAAAsclRSQwAAAagAAAAOZ1RSQwAAAbgAAAAOYlRSQwAAAcgAAAAOdmNndAAAAdgAAAMSbmRpbgAA +BOwAAAY+ZGVzYwAACywAAABkZHNjbQAAC5AAAAAubW1vZAAAC8AAAAAoY3BydAAAC+gAAAAtWFlaIAAA +AAAAAF1KAAA0kQAACCVYWVogAAAAAAAAdCAAALRgAAAjPVhZWiAAAAAAAAAlbAAAFyoAAKfDWFlaIAAA +AAAAAPNSAAEAAAABFs9zZjMyAAAAAAABDEIAAAXe///zJgAAB5IAAP2R///7ov///aMAAAPcAADAbGN1 +cnYAAAAAAAAAAQHNAABjdXJ2AAAAAAAAAAEBzQAAY3VydgAAAAAAAAABAc0AAHZjZ3QAAAAAAAAAAAAD +AQAAAQACBAUGBwkKCw0ODxASExQWFxgaGxweHyAiIyQmJygpKywtLzAxMjM1Njc4OTs8PT5AQUJDREZH +SElKS0xOT1BRUlNUVVZXWFlaW1xdXl9hYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ent8fX5/gIGCg4SF +hoeIiYqLjI2Oj5CRkpOUlZaXmJmam5ydnZ6foKGio6SlpqanqKmqq6ytra6vsLGysrO0tba3uLi5uru8 +vL2+v8DBwcLDxMXGxsfIycrKy8zNzs7P0NHS0tPU1dbW19jZ2drb3Nzd3t/g4eLi4+Tl5ufo6enq6+zt +7u/w8fHy8/T19vf4+fr7/P3+/v8AAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR8gISIjJCUnKCkq +Ky0uLzAxMzQ1Njc4OTo7PD0/QEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaWltcXV5fYGFiY2RlZmdo +aWprbG1ub3BxcnN0dXZ3d3h5ent8fH1+f4CBgoKDhIWGh4iIiYqLjI2Oj5CRkpOUlJWWl5iZmpucnZ2e +n6ChoqOkpaamp6ipqqusra6vsLCxsrO0tba3uLm5uru8vb6/wMHCw8TFx8jJysvMzc7P0NDR0tPU1dbX +2Nna29ze3+Dh4uPk5ebn6err7O3u7/Hy8/T19vf5+vv8/f7/AAIDAwQFBgcICQoKCwwNDg8QERITFBUW +FxgZGhscHR4fICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODg5Ojs8PT4+P0BBQkNDREVGR0hJSUpLTE1O +Tk9QUVJSU1RVVVZXWFhZWltbXF1eXl9gYWFiY2RkZWZnZ2hpaWprbGxtbm5vcHFxcnNzdHV1dnd4eHl6 +ent8fH1+fn+AgYGCg4SEhYaHiImJiouMjY6Oj5CRkpOTlJWWl5iZmZqbnJ2en6ChoqOkpaanqKmqq6yt +rq+xsrO0tba3uLq7vL2+wMHDxMbHycrMzs/R0tTW19nb3d7g4uTm6Ors7vDy9Pb4+vz+/wAAbmRpbgAA +AAAAAAY2AACXGgAAVjoAAFPKAACJ3gAAJ8IAABaoAABQDQAAVDkAAiuFAAIZmQABeFEAAwEAAAIAAAAA +AAEABgANABcAIwAxAEAAUgBlAHsAkwCrAMUA4gD/AR8BPwFhAYUBqgHQAfgCIAJLAncCpQLSAwIDMwNl +A5gDzgQFBD0EdQSvBOsFKQVnBacF6AYqBm4GtQb8B0UHkgfkCDkIkAjnCT4JmAn0ClAKrQsLC2sLygwq +DIwM8Q1XDcAOKA6SDv4PbA/bEE0QxBE7EbQSMRKwEzITuRREFNAVYBXxFocXHhfAGGIZBBmsGlQa+RuU +HC4czh1yHhQeux9jIA0gvCFoIhkizyOJJEEk+SW6JnknOygFKMspkypiKzIsASzXLawuhy9gMD4xGzH8 +MtszvzSgNYY2cjdcOEw5OTorOxs8CD0EPfU+6z/nQOFB2ELUQ9VE00XcRttH5EjxSgBLCUwdTTFOUE9v +UI9Rt1LdVAVVNlZsV6VY4FohW21ct135X09goGH0Y0tkqGYFZ19oxGova5ptCG54b/BxbnLsdG119Xd/ +eQh6knwqfcV/W4D4gpSEO4Xih4CJKorYjIqOOY/jkZuTWJUOlsyYiZpSnB6d4Z+soX+jWqUvpxOo+6rj +rMuuwLC4sra0rra0uL+60LzfvwDBHcLdxLXGhchYyi7MCs3lz7rRmtOA1WPXR9kq2xPc/97s4M/iveSn +5o3obupT7ELuLPAM8fLz0PW396H5f/tZ/T3//wAAAAEAAwALABYAJQA3AE0AZQCBAJ8AwQDlAQsBNQFh +AZABwQH1AisCZAKfAtwDHANfA6MD6gQ0BH8EzQT1BR0FcAXEBhsGdAbPBy0HXAeMB+4IUgi4CSAJVAmK +CfYKZArVC0cLgQu8DDIMqw0mDaIOIQ6hDyQPqRAvELgQ/RFDEc8SXRLuE4AUFRSrFUMV3RZ5FxcXthhY +GPwZoRpIGvEbnBxJHPgdqB5bHw8fxSB9ITch8iKwJDAk8yW3Jn4nRigQKNwpqSp5K0osHCzxLccuoC95 +MFUxMzISMvMz1TS5NaA2hzdxOFw5STo4Oyg8Gj4DPvs/9EDuQepD6ETpRexG8Uf3SP9LFEwhTTBOQE9S +UGZSklOrVMVV4Vb/WB5ZP1phW4Vcq13SXvthUmJ/Y69k4GYSZ0dofGm0au1tZG6ib+FxInJlc6l073Y2 +d396FXtjfLJ+A39VgKmB/4NWhK+GCYjCiiGLgYzjjkePrJESknuT5Ja8mCuZm5sMnH+d9J9qoOGiWqPV +pVGmz6eOqE6pzqtRrNSuWq/gsWmy8rR+tgu5Kbq6vE294b93wQ7Cp8RBxd3He8kZyrrLisxbzf/Po9FK +0vHUm9ZF1/HZn9tO3Cbc/96x4GTiGePQ5YjnQegf6Pzquex27jbv9/G583z0X/VC9wj40Pqa/GX+Mf// +AAAAAQADAAsAJQA3AE0AZQCBAJ8AwQELATUBYQGQAcEB9QIrAmQCnwLcAxwDXwOjA+oENAR/BM0FHQVw +BcQGGwZ0Bs8HLQeMB+4IUgi4CSAJign2CmQK1QtHC7wMMgyrDSYNog4hDqEPJA+pEC8QuBFDEl0S7hOA +FBUUqxVDFnkXFxe2GFgY/BpIGvEbnBxJHPgdqB8PH8UgfSE3IfIjbyQwJPMltydGKBAo3Cp5K0osHC3H +LqAveTEzMhIy8zS5NaA2hzhcOUk6ODwaPQ4+Az/0QO5C6EPoROlG8Uf3SglLFEwhTkBPUlF7UpJUxVXh +Vv9ZP1phXKtd0mAlYVJjr2TgZhJofGm0au1tZG6ib+FxInJldO92Nnd/eMl6FXyyfgN/VYCpgf+Er4YJ +h2WIwoohi4GOR4+skRKSe5PklVCWvJgrmZubDJx/nfSfaqDholqj1aVRps+oTqnOq1Gs1K2Xrlqv4LFp +svK0frYLt5m5Kbnxurq8Tb3hv3fBDsHawqfEQcUPxd3He8hKyRnKusuKzFvN/87Rz6PQdtFK0vHTxtSb +1kXXG9fx2MjZn9tO3Cbc/93Y3rHfiuBk4hni9ePQ5KzliOZk50HoH+j86drqueuX7HbtVu427xbv9/DX +8bnymvN89F/1QvYl9wj37PjQ+bX6mvt//GX9S/4x//8AAGRlc2MAAAAAAAAACkNvbG9yIExDRAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAABtbHVjAAAAAAAAAAEAAAAMZW5VUwAAABIAAAAcAEMAbwBsAG8AcgAgAEwAQwBE +AABtbW9kAAAAAAAABhAAAJxOAAAAAL5zkQAAAAAAAAAAAAAAAAAAAAAAdGV4dAAAAABDb3B5cmlnaHQg +QXBwbGUgQ29tcHV0ZXIsIEluYy4sIDIwMDUAAAAAA + + + + + + 3 + MCAwAA + + + + 400 + 75 + + + 67239424 + 0 + program crashes in between fork() and exec() (3rd option in first group will happen before crash) + + + 7 + 1211912703 + 0 + + 549453824 + {18, 18} + + YES + + YES + + + + TU0AKgAABRgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAMAAAADAAAAAwAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAADwRERGLJycnySsrK/A1NTXw +IyMjyRwcHIsJCQk8AAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFRUVdVBQUOCoqKj/ +29vb//n5+f/6+vr/2tra/6qqqv9UVFTgHx8fdQAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUZGRl5 +dXV198PDw//8/Pz////////////////////////////U1NT/fHx89yUlJXkAAAAFAAAAAAAAAAAAAAAA +AAAAAxEREUZqamrmtbW1/+3t7f/+/v7//v7+//7+/v/9/f3//f39//39/f/39/f/xMTE/3d3d+YZGRlG +AAAAAwAAAAAAAAAAAAAACkJCQqGtra3/xsbG/+vr6//y8vL/9fX1//X19f/z8/P/9fX1//Ly8v/u7u7/ +0tLS/6+vr/9KSkqhAAAACgAAAAAAAAAAAAAAF3h4eN2/v7//z8/P/93d3f/q6ur/7+/v/+/v7//w8PD/ +7e3t/+3t7f/i4uL/zs7O/8XFxf98fHzdAAAAFwAAAAAAAAADAAAAJKSkpPjOzs7/2dnZ/+Dg4P/i4uL/ +5eXl/+bm5v/n5+f/5eXl/+Li4v/e3t7/2tra/9DQ0P+srKz4AAAAJAAAAAMAAAADAAAALrCwsPrW1tb/ +3t7e/+Tk5P/p6en/6+vr/+zs7P/p6en/6+vr/+fn5//k5OT/4ODg/9nZ2f+zs7P6AAAALgAAAAMAAAAD +AAAALp2dnezg4OD/5eXl/+rq6v/u7u7/8PDw//Dw8P/x8fH/8PDw/+7u7v/q6ur/5ubm/+Hh4f+ZmZns +AAAALgAAAAMAAAADAAAAJG5ubs/l5eX/6enp/+/v7//y8vL/9vb2//r6+v/5+fn/9/f3//b29v/x8fH/ +6+vr/+Tk5P9ra2vPAAAAJAAAAAMAAAAAAAAAFy4uLpPCwsL67Ozs//Pz8//5+fn//v7+//7+/v/+/v7/ +/v7+//v7+//19fX/8PDw/8LCwvosLCyTAAAAFwAAAAAAAAAAAAAACgAAAENfX1/S5OTk/vn5+f/+/v7/ +///////////////////////////8/Pz/5ubm/l9fX9IAAABDAAAACgAAAAAAAAAAAAAAAwAAABcAAABl +YmJi3NLS0v3////////////////////////////////V1dX9ZGRk3AAAAGUAAAAXAAAAAwAAAAAAAAAA +AAAAAAAAAAUAAAAfAAAAZTMzM8KAgIDwv7+//O3t7f/t7e3/v7+//ICAgPAzMzPCAAAAZQAAAB8AAAAF +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAFwAAAEMAAAB3AAAAnwAAALMAAACzAAAAnwAAAHcAAABD +AAAAFwAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAoAAAAXAAAAJAAAAC4AAAAu +AAAAJAAAABcAAAAKAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAwAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgEAAAMAAAABABIAAAEB +AAMAAAABABIAAAECAAMAAAAEAAAFxgEDAAMAAAABAAEAAAEGAAMAAAABAAIAAAERAAQAAAABAAAACAES +AAMAAAABAAEAAAEVAAMAAAABAAQAAAEWAAMAAAABABIAAAEXAAQAAAABAAAFEAEcAAMAAAABAAEAAAFS +AAMAAAABAAEAAAFTAAMAAAAEAAAFzodzAAcAAAv0AAAF1gAAAAAACAAIAAgACAABAAEAAQABAAAL9GFw +cGwCAAAAbW50clJHQiBYWVogB9gAAgAMAAoAFgAIYWNzcEFQUEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAPbWAAEAAAAA0y1hcHBs625VECyhxeSV9P9A73pKGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAANclhZWgAAASAAAAAUZ1hZWgAAATQAAAAUYlhZWgAAAUgAAAAUd3RwdAAAAVwAAAAUY2hhZAAA +AXAAAAAsclRSQwAAAZwAAAAOZ1RSQwAAAawAAAAOYlRSQwAAAbwAAAAOdmNndAAAAcwAAAYSbmRpbgAA +B+AAAAMOZGVzYwAACvAAAACZY3BydAAAC4wAAABAbW1vZAAAC8wAAAAoWFlaIAAAAAAAAJumAABMVQAA +ArBYWVogAAAAAAAANWMAAJ/rAAAZsVhZWiAAAAAAAAAlzQAAE9UAALbFWFlaIAAAAAAAAPPYAAEAAAAB +FghzZjMyAAAAAAABC7cAAAWW///zVwAABykAAP3X///7t////aYAAAPaAADA9mN1cnYAAAAAAAAAAQHN +AABjdXJ2AAAAAAAAAAEBzQAAY3VydgAAAAAAAAABAc0AAHZjZ3QAAAAAAAAAAAADAQAAAgAAAioENAYA +B9AJlAtRDQwOshBOEekTgxUVFqMYMRm9GzwcvR4+H7chLiKnJBoliCb3KGIpyCswLJMt9C9WMLIyDjNn +NL02FTdoOLs6ETtdPKw9+D9DQJBB1kMeRGZFq0bySDRJeEq6S/tNPk59T75Q+lI2U25Uo1XZVwlYOlln +WpZbwFzsXhdfQWBrYZRiv2PoZRNmPWdqaJhpyGr5bC1tZW6hb+BxLXJ+c9d1OHafeA55gnr5fHR98H9t +gOuCY4PYhUqGsIgSiWyKuIwBjTqObY+VkLORyJLUk9eU05XFlrWXlZh2mUuaG5rsm6ucbJ0qndqei588 +n9+ghKEqoceiYqL/o5qkLqTDpVml66Z7pwunnKgpqLWpQqnQqlqq5Ktvq/qsg60MrZauIa6qrzOvvbBH +sNGxXLHnsnKy/7OMtBm0p7U3tci2Wbbrt4C4FrituUa54rqAux27wbxlvQq9rr5Tvvi/ncBAwOXBisIu +wtPDeMQdxMLFaMYPxrXHXMgEyKzJVcn/yqnLVcwBzK7NXc4Mzr3PcNAk0NnRkdJK0wTTw9SC1UTWCtbR +153Ybdk+2hfa8dvM3KfdhN5g3zzgGuD34dbiteOV5HblWeY85yHoCOjw6drqxuu27Kbtm+6R74zwivGM +8pPzn/Sw9cj25/gP+T76e/u//Rr+hP//AAABpANzBRoGsggnCZsLFQx+Dd4PRRCiEf8TYxS0FgoXXRiu +GgQbTRyZHekfMCB8IcIjCSRSJZUm3SgdKWAqpCvjLSYuZC+lMOIyIDNgNJs12TcTOFA5izrEO/49NT5w +P6dA30IWQ01Eg0W4Ru9IIElVSoZLt0zmThVPRFBwUZ5SylP5VSRWUVd+WKtZ2lsIXDhdaV6bX89hBWI8 +Y3dktGX0ZzhohGnVayxsiW3sb1Vww3I0c6p1I3aeeBl5k3sMfIR99X9kgM6CLYOJhNyGJodriKGJ1Ir5 +jBuNL45Aj0WQSJE8kjKTGJP+lNyVspaKl1OYHZjlmaSaZJshm9ecj51EnfSepJ9Vn/+gqqFWofyio6NL +o/CklKU4pdymfacfp8KoYqkDqaWqRarlq4asJ6zHrWiuCq6rr02v77CSsTax27KAsyezzrR3tSG1zLZ5 +tye32LiKuT659rqwu2q8J7zkvaK+YL8ev93AnMFcwhzC3MOdxF7FIMXixqXHaMgryPDJtcp6y0DMB8zO +zZbOX88oz/PQvtGJ0lbTI9Px1MDVkNZi1zTYB9jb2bDah9te3DjdEt3t3sjfpOB/4VviN+MT4/DkzeWq +5ojnZuhG6SXqBuro68rsre2S7nfvXvBH8TDyHPMI8/j06fXc9tL3yvjF+cL6w/vG/ND92v7s//8AAAMJ +BboIZwrCDSsPghG8E/IWHxg5GkgcVB5VIEQiMyQTJeknuimHK00tCy7AMHEyHDO/NV829ziKOhs7pj0s +PrBALEGmQx9EkkYCR3JI3EpCS6pND05vT89RLVKKU+dVP1aYV+9ZRVqdW/NdSV6hX+thM2JzY61k42YS +Z0FoZ2mOaq5rz2zsbglvI3A9cVRybHOEdJx1tHbOd+d5A3ofez98Yn2Lfrl/8IEqgmyDsoT8hkuHnYjw +ikSLmYzsjj+PjJDWkh2TW5SXlceW85gWmTOaSJtVnFqdWp5Pn0SgKaEQoeuiwqOYpGClKaXtpqmnZqgf +qNKph6o5quWrk6xArOatja41rtevebAcsLyxWbH3spWzL7PJtGO0/LWTtiq2wrdWt+q4f7kUuaW6OLrL +u1277Lx9vQ69nr4tvry/TL/bwGjA98GGwhPCocMvw77ESsTYxWXF9MZ/xwzHmcgkyKXJJ8mpyizKpMsc +y5XMDsyFzPjNa83fzlPOxc81z6fQGNCK0PvRbNHe0lDSw9M206vUINSV1QvVhtYA1nzW+Nd61//YhNkK +2ZnaL9rG213cCty63WveI97d35fgUuEO4crih+NE5ALkw+WD5kXnCufP6JbpYOor6vrry+yd7XbuUe8w +8BXw+/Ht8uDz4PTl9fj3E/hE+X363vxa/gH//wAAbmRpbgAAAAAAAAMGAACogAAAUwAAADRAAACqQAAA +JpcAABLbAABQQAAAVEAAAj99AAI1egACxUsAAwB4AAIAAAADAAsAGQAsAEUAYwCHALEA4QEWAVEBkgHZ +AiYCeQLSAzEDlwQDBHYE7wVvBfUGgwcXB7IIUwj8CawKYgsgC+QMrw2BDloPORAfEQ0SBRMGFBEVJBZA +F2MYjhm/GvYcMh1xHrMf9SE1ImwjnSTJJfAnFig7KWMqjivBLP4uSC+jMRMymzRBNgo3+joWPGY+8EG8 +RNhIQEvvT95UCFhkXOxhlWZYaylv/XTKeYN+GoKOhxGLqJBOlP6Ztp5voyan1Kx0sQG1c7nGvfHB9cX7 +ygbOFNIi1izaMN4p4hTl7emv7Vbw3vRC93z6iP1g//8AAAAEAA8AIgA9AF8AiQC7APQBNAF8AcwCIgKB +AuYDUwPHBEIExAVOBd4GdgcUB7oIZgkaCdQKlQteDC0NAw3gDsQPrxCiEZwSnxOpFLsV0xbyGBcZQRpw +G6Mc2R4RH0oggyG3IuckEyU8JmQniyiyKd0rDCxBLYAuyTAhMYkzBjSbNkw4HToSPDA+fED8Q7FGmUmx +TPdQaFQAV7tbll+KY5RnrmvTb/p0H3g6fEOAMoQXiASL+I/yk/KX+JwBoA2kHKgrrDuwS7RYuGK8aMBo +xGvId8yI0J/UuNjS3OvhAOUQ6RftE/EC9OH4rfxi//8AAAABAAYADQAXACUANQBIAF8AeQCWALcA3AEE +ATABYQGVAc4CDAJOApUC4QMyA4gD5QRGBK4FHAWPBgkGigcQB54IMQjMCW0KFArDC3cMMgzzDbsOiA9a +EDIRFhIIEwgUFRUvFlYXhxjDGgkbVhyqHgMfYCC8IhIjYSSrJfMnOSiBKc0rHyx7LeQvXTDrMpE0VTY7 +OEg6gjzuP5NCckWCSMJMMk/QU5xXk1u1X/9kcGkGbb5ylneLfJqByoeDjcyUf5t4oo2plLBetru8eMFr +xirK7c+v1GnZFd2r4iXme+ql7pryUvXD+OT7qv4M//8AAGRlc2MAAAAAAAAAFUhQIExQMzA2NSBDYWxp +YnJhdGVkAAAAAAAAAAAVAEgAUAAgAEwAUAAzADAANgA1ACAAQwBhAGwAaQBiAHIAYQB0AGUAZAAAAAAV +SFAgTFAzMDY1IENhbGlicmF0ZWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIEluYy4sIDIwMDgAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAABtbW9kAAAAAAAAIvAAACaQAAAAAMJtVwAAAAAAAAAAAAAAAAAAAAAAA + + + + + + + + + 200 + 25 + + + {645, 25} + {4, 2} + 1151868928 + NSActionCell + + -2080244224 + 0 + program crashes after launch + + 5 + 1211912703 + 0 + + + + 200 + 25 + + + + 6 + System + controlColor + + 3 + MC42NjY2NjY2NjY3AA + + + + 3 + MQA + + + + + + 268 + {{20, 170}, {565, 70}} + + YES + 3 + 1 + + YES + + -2080244224 + 0 + Leave breakpad alone before fork + + + 1211912703 + 0 + + + + 200 + 25 + + + 67239424 + 0 + Uninitialize Breakpad before fork + + + 1 + 1211912703 + 0 + + 549453824 + {18, 18} + + YES + + YES + + + + TU0AKgAABRgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAMAAAADAAAAAwAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAADwRERGLJycnySsrK/A1NTXw +IyMjyRwcHIsJCQk8AAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFRUVdVBQUOCoqKj/ +29vb//n5+f/6+vr/2tra/6qqqv9UVFTgHx8fdQAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUZGRl5 +dXV198PDw//8/Pz////////////////////////////U1NT/fHx89yUlJXkAAAAFAAAAAAAAAAAAAAAA +AAAAAxEREUZqamrmtbW1/+3t7f/+/v7//v7+//7+/v/9/f3//f39//39/f/39/f/xMTE/3d3d+YZGRlG +AAAAAwAAAAAAAAAAAAAACkJCQqGtra3/xsbG/+vr6//y8vL/9fX1//X19f/z8/P/9fX1//Ly8v/u7u7/ +0tLS/6+vr/9KSkqhAAAACgAAAAAAAAAAAAAAF3h4eN2/v7//z8/P/93d3f/q6ur/7+/v/+/v7//w8PD/ +7e3t/+3t7f/i4uL/zs7O/8XFxf98fHzdAAAAFwAAAAAAAAADAAAAJKSkpPjOzs7/2dnZ/+Dg4P/i4uL/ +5eXl/+bm5v/n5+f/5eXl/+Li4v/e3t7/2tra/9DQ0P+srKz4AAAAJAAAAAMAAAADAAAALrCwsPrW1tb/ +3t7e/+Tk5P/p6en/6+vr/+zs7P/p6en/6+vr/+fn5//k5OT/4ODg/9nZ2f+zs7P6AAAALgAAAAMAAAAD +AAAALp2dnezg4OD/5eXl/+rq6v/u7u7/8PDw//Dw8P/x8fH/8PDw/+7u7v/q6ur/5ubm/+Hh4f+ZmZns +AAAALgAAAAMAAAADAAAAJG5ubs/l5eX/6enp/+/v7//y8vL/9vb2//r6+v/5+fn/9/f3//b29v/x8fH/ +6+vr/+Tk5P9ra2vPAAAAJAAAAAMAAAAAAAAAFy4uLpPCwsL67Ozs//Pz8//5+fn//v7+//7+/v/+/v7/ +/v7+//v7+//19fX/8PDw/8LCwvosLCyTAAAAFwAAAAAAAAAAAAAACgAAAENfX1/S5OTk/vn5+f/+/v7/ +///////////////////////////8/Pz/5ubm/l9fX9IAAABDAAAACgAAAAAAAAAAAAAAAwAAABcAAABl +YmJi3NLS0v3////////////////////////////////V1dX9ZGRk3AAAAGUAAAAXAAAAAwAAAAAAAAAA +AAAAAAAAAAUAAAAfAAAAZTMzM8KAgIDwv7+//O3t7f/t7e3/v7+//ICAgPAzMzPCAAAAZQAAAB8AAAAF +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAFwAAAEMAAAB3AAAAnwAAALMAAACzAAAAnwAAAHcAAABD +AAAAFwAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAoAAAAXAAAAJAAAAC4AAAAu +AAAAJAAAABcAAAAKAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAwAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgEAAAMAAAABABIAAAEB +AAMAAAABABIAAAECAAMAAAAEAAAFxgEDAAMAAAABAAEAAAEGAAMAAAABAAIAAAERAAQAAAABAAAACAES +AAMAAAABAAEAAAEVAAMAAAABAAQAAAEWAAMAAAABABIAAAEXAAQAAAABAAAFEAEcAAMAAAABAAEAAAFS +AAMAAAABAAEAAAFTAAMAAAAEAAAFzodzAAcAAAwYAAAF1gAAAAAACAAIAAgACAABAAEAAQABAAAMGGFw +cGwCAAAAbW50clJHQiBYWVogB9YABAADABMALAASYWNzcEFQUEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAPbWAAEAAAAA0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAOclhZWgAAASwAAAAUZ1hZWgAAAUAAAAAUYlhZWgAAAVQAAAAUd3RwdAAAAWgAAAAUY2hhZAAA +AXwAAAAsclRSQwAAAagAAAAOZ1RSQwAAAbgAAAAOYlRSQwAAAcgAAAAOdmNndAAAAdgAAAMSbmRpbgAA +BOwAAAY+ZGVzYwAACywAAABkZHNjbQAAC5AAAAAubW1vZAAAC8AAAAAoY3BydAAAC+gAAAAtWFlaIAAA +AAAAAF1KAAA0kQAACCVYWVogAAAAAAAAdCAAALRgAAAjPVhZWiAAAAAAAAAlbAAAFyoAAKfDWFlaIAAA +AAAAAPNSAAEAAAABFs9zZjMyAAAAAAABDEIAAAXe///zJgAAB5IAAP2R///7ov///aMAAAPcAADAbGN1 +cnYAAAAAAAAAAQHNAABjdXJ2AAAAAAAAAAEBzQAAY3VydgAAAAAAAAABAc0AAHZjZ3QAAAAAAAAAAAAD +AQAAAQACBAUGBwkKCw0ODxASExQWFxgaGxweHyAiIyQmJygpKywtLzAxMjM1Njc4OTs8PT5AQUJDREZH +SElKS0xOT1BRUlNUVVZXWFlaW1xdXl9hYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ent8fX5/gIGCg4SF +hoeIiYqLjI2Oj5CRkpOUlZaXmJmam5ydnZ6foKGio6SlpqanqKmqq6ytra6vsLGysrO0tba3uLi5uru8 +vL2+v8DBwcLDxMXGxsfIycrKy8zNzs7P0NHS0tPU1dbW19jZ2drb3Nzd3t/g4eLi4+Tl5ufo6enq6+zt +7u/w8fHy8/T19vf4+fr7/P3+/v8AAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR8gISIjJCUnKCkq +Ky0uLzAxMzQ1Njc4OTo7PD0/QEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaWltcXV5fYGFiY2RlZmdo +aWprbG1ub3BxcnN0dXZ3d3h5ent8fH1+f4CBgoKDhIWGh4iIiYqLjI2Oj5CRkpOUlJWWl5iZmpucnZ2e +n6ChoqOkpaamp6ipqqusra6vsLCxsrO0tba3uLm5uru8vb6/wMHCw8TFx8jJysvMzc7P0NDR0tPU1dbX +2Nna29ze3+Dh4uPk5ebn6err7O3u7/Hy8/T19vf5+vv8/f7/AAIDAwQFBgcICQoKCwwNDg8QERITFBUW +FxgZGhscHR4fICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODg5Ojs8PT4+P0BBQkNDREVGR0hJSUpLTE1O +Tk9QUVJSU1RVVVZXWFhZWltbXF1eXl9gYWFiY2RkZWZnZ2hpaWprbGxtbm5vcHFxcnNzdHV1dnd4eHl6 +ent8fH1+fn+AgYGCg4SEhYaHiImJiouMjY6Oj5CRkpOTlJWWl5iZmZqbnJ2en6ChoqOkpaanqKmqq6yt +rq+xsrO0tba3uLq7vL2+wMHDxMbHycrMzs/R0tTW19nb3d7g4uTm6Ors7vDy9Pb4+vz+/wAAbmRpbgAA +AAAAAAY2AACXGgAAVjoAAFPKAACJ3gAAJ8IAABaoAABQDQAAVDkAAiuFAAIZmQABeFEAAwEAAAIAAAAA +AAEABgANABcAIwAxAEAAUgBlAHsAkwCrAMUA4gD/AR8BPwFhAYUBqgHQAfgCIAJLAncCpQLSAwIDMwNl +A5gDzgQFBD0EdQSvBOsFKQVnBacF6AYqBm4GtQb8B0UHkgfkCDkIkAjnCT4JmAn0ClAKrQsLC2sLygwq +DIwM8Q1XDcAOKA6SDv4PbA/bEE0QxBE7EbQSMRKwEzITuRREFNAVYBXxFocXHhfAGGIZBBmsGlQa+RuU +HC4czh1yHhQeux9jIA0gvCFoIhkizyOJJEEk+SW6JnknOygFKMspkypiKzIsASzXLawuhy9gMD4xGzH8 +MtszvzSgNYY2cjdcOEw5OTorOxs8CD0EPfU+6z/nQOFB2ELUQ9VE00XcRttH5EjxSgBLCUwdTTFOUE9v +UI9Rt1LdVAVVNlZsV6VY4FohW21ct135X09goGH0Y0tkqGYFZ19oxGova5ptCG54b/BxbnLsdG119Xd/ +eQh6knwqfcV/W4D4gpSEO4Xih4CJKorYjIqOOY/jkZuTWJUOlsyYiZpSnB6d4Z+soX+jWqUvpxOo+6rj +rMuuwLC4sra0rra0uL+60LzfvwDBHcLdxLXGhchYyi7MCs3lz7rRmtOA1WPXR9kq2xPc/97s4M/iveSn +5o3obupT7ELuLPAM8fLz0PW396H5f/tZ/T3//wAAAAEAAwALABYAJQA3AE0AZQCBAJ8AwQDlAQsBNQFh +AZABwQH1AisCZAKfAtwDHANfA6MD6gQ0BH8EzQT1BR0FcAXEBhsGdAbPBy0HXAeMB+4IUgi4CSAJVAmK +CfYKZArVC0cLgQu8DDIMqw0mDaIOIQ6hDyQPqRAvELgQ/RFDEc8SXRLuE4AUFRSrFUMV3RZ5FxcXthhY +GPwZoRpIGvEbnBxJHPgdqB5bHw8fxSB9ITch8iKwJDAk8yW3Jn4nRigQKNwpqSp5K0osHCzxLccuoC95 +MFUxMzISMvMz1TS5NaA2hzdxOFw5STo4Oyg8Gj4DPvs/9EDuQepD6ETpRexG8Uf3SP9LFEwhTTBOQE9S +UGZSklOrVMVV4Vb/WB5ZP1phW4Vcq13SXvthUmJ/Y69k4GYSZ0dofGm0au1tZG6ib+FxInJlc6l073Y2 +d396FXtjfLJ+A39VgKmB/4NWhK+GCYjCiiGLgYzjjkePrJESknuT5Ja8mCuZm5sMnH+d9J9qoOGiWqPV +pVGmz6eOqE6pzqtRrNSuWq/gsWmy8rR+tgu5Kbq6vE294b93wQ7Cp8RBxd3He8kZyrrLisxbzf/Po9FK +0vHUm9ZF1/HZn9tO3Cbc/96x4GTiGePQ5YjnQegf6Pzquex27jbv9/G583z0X/VC9wj40Pqa/GX+Mf// +AAAAAQADAAsAJQA3AE0AZQCBAJ8AwQELATUBYQGQAcEB9QIrAmQCnwLcAxwDXwOjA+oENAR/BM0FHQVw +BcQGGwZ0Bs8HLQeMB+4IUgi4CSAJign2CmQK1QtHC7wMMgyrDSYNog4hDqEPJA+pEC8QuBFDEl0S7hOA +FBUUqxVDFnkXFxe2GFgY/BpIGvEbnBxJHPgdqB8PH8UgfSE3IfIjbyQwJPMltydGKBAo3Cp5K0osHC3H +LqAveTEzMhIy8zS5NaA2hzhcOUk6ODwaPQ4+Az/0QO5C6EPoROlG8Uf3SglLFEwhTkBPUlF7UpJUxVXh +Vv9ZP1phXKtd0mAlYVJjr2TgZhJofGm0au1tZG6ib+FxInJldO92Nnd/eMl6FXyyfgN/VYCpgf+Er4YJ +h2WIwoohi4GOR4+skRKSe5PklVCWvJgrmZubDJx/nfSfaqDholqj1aVRps+oTqnOq1Gs1K2Xrlqv4LFp +svK0frYLt5m5Kbnxurq8Tb3hv3fBDsHawqfEQcUPxd3He8hKyRnKusuKzFvN/87Rz6PQdtFK0vHTxtSb +1kXXG9fx2MjZn9tO3Cbc/93Y3rHfiuBk4hni9ePQ5KzliOZk50HoH+j86drqueuX7HbtVu427xbv9/DX +8bnymvN89F/1QvYl9wj37PjQ+bX6mvt//GX9S/4x//8AAGRlc2MAAAAAAAAACkNvbG9yIExDRAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAABtbHVjAAAAAAAAAAEAAAAMZW5VUwAAABIAAAAcAEMAbwBsAG8AcgAgAEwAQwBE +AABtbW9kAAAAAAAABhAAAJxOAAAAAL5zkQAAAAAAAAAAAAAAAAAAAAAAdGV4dAAAAABDb3B5cmlnaHQg +QXBwbGUgQ29tcHV0ZXIsIEluYy4sIDIwMDUAAAAAA + + + + + + + + 400 + 75 + + + 67239424 + 0 + Call task_set_exception_port with null exception port in child process before exec + + + 2 + 1211912703 + 0 + + 549453824 + {18, 18} + + YES + + YES + + + + TU0AKgAABRgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAMAAAADAAAAAwAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAADwRERGLJycnySsrK/A1NTXw +IyMjyRwcHIsJCQk8AAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFRUVdVBQUOCoqKj/ +29vb//n5+f/6+vr/2tra/6qqqv9UVFTgHx8fdQAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUZGRl5 +dXV198PDw//8/Pz////////////////////////////U1NT/fHx89yUlJXkAAAAFAAAAAAAAAAAAAAAA +AAAAAxEREUZqamrmtbW1/+3t7f/+/v7//v7+//7+/v/9/f3//f39//39/f/39/f/xMTE/3d3d+YZGRlG +AAAAAwAAAAAAAAAAAAAACkJCQqGtra3/xsbG/+vr6//y8vL/9fX1//X19f/z8/P/9fX1//Ly8v/u7u7/ +0tLS/6+vr/9KSkqhAAAACgAAAAAAAAAAAAAAF3h4eN2/v7//z8/P/93d3f/q6ur/7+/v/+/v7//w8PD/ +7e3t/+3t7f/i4uL/zs7O/8XFxf98fHzdAAAAFwAAAAAAAAADAAAAJKSkpPjOzs7/2dnZ/+Dg4P/i4uL/ +5eXl/+bm5v/n5+f/5eXl/+Li4v/e3t7/2tra/9DQ0P+srKz4AAAAJAAAAAMAAAADAAAALrCwsPrW1tb/ +3t7e/+Tk5P/p6en/6+vr/+zs7P/p6en/6+vr/+fn5//k5OT/4ODg/9nZ2f+zs7P6AAAALgAAAAMAAAAD +AAAALp2dnezg4OD/5eXl/+rq6v/u7u7/8PDw//Dw8P/x8fH/8PDw/+7u7v/q6ur/5ubm/+Hh4f+ZmZns +AAAALgAAAAMAAAADAAAAJG5ubs/l5eX/6enp/+/v7//y8vL/9vb2//r6+v/5+fn/9/f3//b29v/x8fH/ +6+vr/+Tk5P9ra2vPAAAAJAAAAAMAAAAAAAAAFy4uLpPCwsL67Ozs//Pz8//5+fn//v7+//7+/v/+/v7/ +/v7+//v7+//19fX/8PDw/8LCwvosLCyTAAAAFwAAAAAAAAAAAAAACgAAAENfX1/S5OTk/vn5+f/+/v7/ +///////////////////////////8/Pz/5ubm/l9fX9IAAABDAAAACgAAAAAAAAAAAAAAAwAAABcAAABl +YmJi3NLS0v3////////////////////////////////V1dX9ZGRk3AAAAGUAAAAXAAAAAwAAAAAAAAAA +AAAAAAAAAAUAAAAfAAAAZTMzM8KAgIDwv7+//O3t7f/t7e3/v7+//ICAgPAzMzPCAAAAZQAAAB8AAAAF +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAFwAAAEMAAAB3AAAAnwAAALMAAACzAAAAnwAAAHcAAABD +AAAAFwAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAoAAAAXAAAAJAAAAC4AAAAu +AAAAJAAAABcAAAAKAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAwAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgEAAAMAAAABABIAAAEB +AAMAAAABABIAAAECAAMAAAAEAAAFxgEDAAMAAAABAAEAAAEGAAMAAAABAAIAAAERAAQAAAABAAAACAES +AAMAAAABAAEAAAEVAAMAAAABAAQAAAEWAAMAAAABABIAAAEXAAQAAAABAAAFEAEcAAMAAAABAAEAAAFS +AAMAAAABAAEAAAFTAAMAAAAEAAAFzodzAAcAAAv0AAAF1gAAAAAACAAIAAgACAABAAEAAQABAAAL9GFw +cGwCAAAAbW50clJHQiBYWVogB9gAAgAMAAoAFgAIYWNzcEFQUEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAPbWAAEAAAAA0y1hcHBs625VECyhxeSV9P9A73pKGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAANclhZWgAAASAAAAAUZ1hZWgAAATQAAAAUYlhZWgAAAUgAAAAUd3RwdAAAAVwAAAAUY2hhZAAA +AXAAAAAsclRSQwAAAZwAAAAOZ1RSQwAAAawAAAAOYlRSQwAAAbwAAAAOdmNndAAAAcwAAAYSbmRpbgAA +B+AAAAMOZGVzYwAACvAAAACZY3BydAAAC4wAAABAbW1vZAAAC8wAAAAoWFlaIAAAAAAAAJumAABMVQAA +ArBYWVogAAAAAAAANWMAAJ/rAAAZsVhZWiAAAAAAAAAlzQAAE9UAALbFWFlaIAAAAAAAAPPYAAEAAAAB +FghzZjMyAAAAAAABC7cAAAWW///zVwAABykAAP3X///7t////aYAAAPaAADA9mN1cnYAAAAAAAAAAQHN +AABjdXJ2AAAAAAAAAAEBzQAAY3VydgAAAAAAAAABAc0AAHZjZ3QAAAAAAAAAAAADAQAAAgAAAioENAYA +B9AJlAtRDQwOshBOEekTgxUVFqMYMRm9GzwcvR4+H7chLiKnJBoliCb3KGIpyCswLJMt9C9WMLIyDjNn +NL02FTdoOLs6ETtdPKw9+D9DQJBB1kMeRGZFq0bySDRJeEq6S/tNPk59T75Q+lI2U25Uo1XZVwlYOlln +WpZbwFzsXhdfQWBrYZRiv2PoZRNmPWdqaJhpyGr5bC1tZW6hb+BxLXJ+c9d1OHafeA55gnr5fHR98H9t +gOuCY4PYhUqGsIgSiWyKuIwBjTqObY+VkLORyJLUk9eU05XFlrWXlZh2mUuaG5rsm6ucbJ0qndqei588 +n9+ghKEqoceiYqL/o5qkLqTDpVml66Z7pwunnKgpqLWpQqnQqlqq5Ktvq/qsg60MrZauIa6qrzOvvbBH +sNGxXLHnsnKy/7OMtBm0p7U3tci2Wbbrt4C4FrituUa54rqAux27wbxlvQq9rr5Tvvi/ncBAwOXBisIu +wtPDeMQdxMLFaMYPxrXHXMgEyKzJVcn/yqnLVcwBzK7NXc4Mzr3PcNAk0NnRkdJK0wTTw9SC1UTWCtbR +153Ybdk+2hfa8dvM3KfdhN5g3zzgGuD34dbiteOV5HblWeY85yHoCOjw6drqxuu27Kbtm+6R74zwivGM +8pPzn/Sw9cj25/gP+T76e/u//Rr+hP//AAABpANzBRoGsggnCZsLFQx+Dd4PRRCiEf8TYxS0FgoXXRiu +GgQbTRyZHekfMCB8IcIjCSRSJZUm3SgdKWAqpCvjLSYuZC+lMOIyIDNgNJs12TcTOFA5izrEO/49NT5w +P6dA30IWQ01Eg0W4Ru9IIElVSoZLt0zmThVPRFBwUZ5SylP5VSRWUVd+WKtZ2lsIXDhdaV6bX89hBWI8 +Y3dktGX0ZzhohGnVayxsiW3sb1Vww3I0c6p1I3aeeBl5k3sMfIR99X9kgM6CLYOJhNyGJodriKGJ1Ir5 +jBuNL45Aj0WQSJE8kjKTGJP+lNyVspaKl1OYHZjlmaSaZJshm9ecj51EnfSepJ9Vn/+gqqFWofyio6NL +o/CklKU4pdymfacfp8KoYqkDqaWqRarlq4asJ6zHrWiuCq6rr02v77CSsTax27KAsyezzrR3tSG1zLZ5 +tye32LiKuT659rqwu2q8J7zkvaK+YL8ev93AnMFcwhzC3MOdxF7FIMXixqXHaMgryPDJtcp6y0DMB8zO +zZbOX88oz/PQvtGJ0lbTI9Px1MDVkNZi1zTYB9jb2bDah9te3DjdEt3t3sjfpOB/4VviN+MT4/DkzeWq +5ojnZuhG6SXqBuro68rsre2S7nfvXvBH8TDyHPMI8/j06fXc9tL3yvjF+cL6w/vG/ND92v7s//8AAAMJ +BboIZwrCDSsPghG8E/IWHxg5GkgcVB5VIEQiMyQTJeknuimHK00tCy7AMHEyHDO/NV829ziKOhs7pj0s +PrBALEGmQx9EkkYCR3JI3EpCS6pND05vT89RLVKKU+dVP1aYV+9ZRVqdW/NdSV6hX+thM2JzY61k42YS +Z0FoZ2mOaq5rz2zsbglvI3A9cVRybHOEdJx1tHbOd+d5A3ofez98Yn2Lfrl/8IEqgmyDsoT8hkuHnYjw +ikSLmYzsjj+PjJDWkh2TW5SXlceW85gWmTOaSJtVnFqdWp5Pn0SgKaEQoeuiwqOYpGClKaXtpqmnZqgf +qNKph6o5quWrk6xArOatja41rtevebAcsLyxWbH3spWzL7PJtGO0/LWTtiq2wrdWt+q4f7kUuaW6OLrL +u1277Lx9vQ69nr4tvry/TL/bwGjA98GGwhPCocMvw77ESsTYxWXF9MZ/xwzHmcgkyKXJJ8mpyizKpMsc +y5XMDsyFzPjNa83fzlPOxc81z6fQGNCK0PvRbNHe0lDSw9M206vUINSV1QvVhtYA1nzW+Nd61//YhNkK +2ZnaL9rG213cCty63WveI97d35fgUuEO4crih+NE5ALkw+WD5kXnCufP6JbpYOor6vrry+yd7XbuUe8w +8BXw+/Ht8uDz4PTl9fj3E/hE+X363vxa/gH//wAAbmRpbgAAAAAAAAMGAACogAAAUwAAADRAAACqQAAA +JpcAABLbAABQQAAAVEAAAj99AAI1egACxUsAAwB4AAIAAAADAAsAGQAsAEUAYwCHALEA4QEWAVEBkgHZ +AiYCeQLSAzEDlwQDBHYE7wVvBfUGgwcXB7IIUwj8CawKYgsgC+QMrw2BDloPORAfEQ0SBRMGFBEVJBZA +F2MYjhm/GvYcMh1xHrMf9SE1ImwjnSTJJfAnFig7KWMqjivBLP4uSC+jMRMymzRBNgo3+joWPGY+8EG8 +RNhIQEvvT95UCFhkXOxhlWZYaylv/XTKeYN+GoKOhxGLqJBOlP6Ztp5voyan1Kx0sQG1c7nGvfHB9cX7 +ygbOFNIi1izaMN4p4hTl7emv7Vbw3vRC93z6iP1g//8AAAAEAA8AIgA9AF8AiQC7APQBNAF8AcwCIgKB +AuYDUwPHBEIExAVOBd4GdgcUB7oIZgkaCdQKlQteDC0NAw3gDsQPrxCiEZwSnxOpFLsV0xbyGBcZQRpw +G6Mc2R4RH0oggyG3IuckEyU8JmQniyiyKd0rDCxBLYAuyTAhMYkzBjSbNkw4HToSPDA+fED8Q7FGmUmx +TPdQaFQAV7tbll+KY5RnrmvTb/p0H3g6fEOAMoQXiASL+I/yk/KX+JwBoA2kHKgrrDuwS7RYuGK8aMBo +xGvId8yI0J/UuNjS3OvhAOUQ6RftE/EC9OH4rfxi//8AAAABAAYADQAXACUANQBIAF8AeQCWALcA3AEE +ATABYQGVAc4CDAJOApUC4QMyA4gD5QRGBK4FHAWPBgkGigcQB54IMQjMCW0KFArDC3cMMgzzDbsOiA9a +EDIRFhIIEwgUFRUvFlYXhxjDGgkbVhyqHgMfYCC8IhIjYSSrJfMnOSiBKc0rHyx7LeQvXTDrMpE0VTY7 +OEg6gjzuP5NCckWCSMJMMk/QU5xXk1u1X/9kcGkGbb5ylneLfJqByoeDjcyUf5t4oo2plLBetru8eMFr +xirK7c+v1GnZFd2r4iXme+ql7pryUvXD+OT7qv4M//8AAGRlc2MAAAAAAAAAFUhQIExQMzA2NSBDYWxp +YnJhdGVkAAAAAAAAAAAVAEgAUAAgAEwAUAAzADAANgA1ACAAQwBhAGwAaQBiAHIAYQB0AGUAZAAAAAAV +SFAgTFAzMDY1IENhbGlicmF0ZWQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAHRleHQAAAAAQ29weXJpZ2h0IEFwcGxlIEluYy4sIDIwMDgAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAABtbW9kAAAAAAAAIvAAACaQAAAAAMJtVwAAAAAAAAAAAAAAAAAAAAAAA + + + + + + + + 400 + 75 + + + {565, 22} + {4, 2} + 1151868928 + NSActionCell + + 67239424 + 0 + Radio + + 1211912703 + 0 + + 549453824 + {18, 18} + + YES + + YES + + + + TU0AKgAABRgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAMAAAADAAAAAwAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAADwRERGLJycnySsrK/A1NTXw +IyMjyRwcHIsJCQk8AAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFRUVdVBQUOCoqKj/ +29vb//n5+f/6+vr/2tra/6qqqv9UVFTgHx8fdQAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUZGRl5 +dXV198PDw//8/Pz////////////////////////////U1NT/fHx89yUlJXkAAAAFAAAAAAAAAAAAAAAA +AAAAAxEREUZqamrmtbW1/+3t7f/+/v7//v7+//7+/v/9/f3//f39//39/f/39/f/xMTE/3d3d+YZGRlG +AAAAAwAAAAAAAAAAAAAACkJCQqGtra3/xsbG/+vr6//y8vL/9fX1//X19f/z8/P/9fX1//Ly8v/u7u7/ +0tLS/6+vr/9KSkqhAAAACgAAAAAAAAAAAAAAF3h4eN2/v7//z8/P/93d3f/q6ur/7+/v/+/v7//w8PD/ +7e3t/+3t7f/i4uL/zs7O/8XFxf98fHzdAAAAFwAAAAAAAAADAAAAJKSkpPjOzs7/2dnZ/+Dg4P/i4uL/ +5eXl/+bm5v/n5+f/5eXl/+Li4v/e3t7/2tra/9DQ0P+srKz4AAAAJAAAAAMAAAADAAAALrCwsPrW1tb/ +3t7e/+Tk5P/p6en/6+vr/+zs7P/p6en/6+vr/+fn5//k5OT/4ODg/9nZ2f+zs7P6AAAALgAAAAMAAAAD +AAAALp2dnezg4OD/5eXl/+rq6v/u7u7/8PDw//Dw8P/x8fH/8PDw/+7u7v/q6ur/5ubm/+Hh4f+ZmZns +AAAALgAAAAMAAAADAAAAJG5ubs/l5eX/6enp/+/v7//y8vL/9vb2//r6+v/5+fn/9/f3//b29v/x8fH/ +6+vr/+Tk5P9ra2vPAAAAJAAAAAMAAAAAAAAAFy4uLpPCwsL67Ozs//Pz8//5+fn//v7+//7+/v/+/v7/ +/v7+//v7+//19fX/8PDw/8LCwvosLCyTAAAAFwAAAAAAAAAAAAAACgAAAENfX1/S5OTk/vn5+f/+/v7/ +///////////////////////////8/Pz/5ubm/l9fX9IAAABDAAAACgAAAAAAAAAAAAAAAwAAABcAAABl +YmJi3NLS0v3////////////////////////////////V1dX9ZGRk3AAAAGUAAAAXAAAAAwAAAAAAAAAA +AAAAAAAAAAUAAAAfAAAAZTMzM8KAgIDwv7+//O3t7f/t7e3/v7+//ICAgPAzMzPCAAAAZQAAAB8AAAAF +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAFwAAAEMAAAB3AAAAnwAAALMAAACzAAAAnwAAAHcAAABD +AAAAFwAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAoAAAAXAAAAJAAAAC4AAAAu +AAAAJAAAABcAAAAKAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAwAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQEAAAMAAAABABIAAAEB +AAMAAAABABIAAAECAAMAAAAEAAAFugEDAAMAAAABAAEAAAEGAAMAAAABAAIAAAERAAQAAAABAAAACAES +AAMAAAABAAEAAAEVAAMAAAABAAQAAAEWAAMAAAABABIAAAEXAAQAAAABAAAFEAEcAAMAAAABAAEAAAFS +AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA + + + + + + + + 400 + 75 + + + + + + + + + 268 + {{20, 104}, {565, 38}} + + YES + 2 + 1 + + YES + + -2080244224 + 0 + fork() + + + 3 + 1211912703 + 0 + + + + 200 + 25 + + + 67239424 + 0 + vfork() + + + 4 + 1211912703 + 0 + + 549453824 + {18, 18} + + YES + + YES + + + + TU0AKgAABRgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAMAAAADAAAAAwAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAADwRERGLJycnySsrK/A1NTXw +IyMjyRwcHIsJCQk8AAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFRUVdVBQUOCoqKj/ +29vb//n5+f/6+vr/2tra/6qqqv9UVFTgHx8fdQAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUZGRl5 +dXV198PDw//8/Pz////////////////////////////U1NT/fHx89yUlJXkAAAAFAAAAAAAAAAAAAAAA +AAAAAxEREUZqamrmtbW1/+3t7f/+/v7//v7+//7+/v/9/f3//f39//39/f/39/f/xMTE/3d3d+YZGRlG +AAAAAwAAAAAAAAAAAAAACkJCQqGtra3/xsbG/+vr6//y8vL/9fX1//X19f/z8/P/9fX1//Ly8v/u7u7/ +0tLS/6+vr/9KSkqhAAAACgAAAAAAAAAAAAAAF3h4eN2/v7//z8/P/93d3f/q6ur/7+/v/+/v7//w8PD/ +7e3t/+3t7f/i4uL/zs7O/8XFxf98fHzdAAAAFwAAAAAAAAADAAAAJKSkpPjOzs7/2dnZ/+Dg4P/i4uL/ +5eXl/+bm5v/n5+f/5eXl/+Li4v/e3t7/2tra/9DQ0P+srKz4AAAAJAAAAAMAAAADAAAALrCwsPrW1tb/ +3t7e/+Tk5P/p6en/6+vr/+zs7P/p6en/6+vr/+fn5//k5OT/4ODg/9nZ2f+zs7P6AAAALgAAAAMAAAAD +AAAALp2dnezg4OD/5eXl/+rq6v/u7u7/8PDw//Dw8P/x8fH/8PDw/+7u7v/q6ur/5ubm/+Hh4f+ZmZns +AAAALgAAAAMAAAADAAAAJG5ubs/l5eX/6enp/+/v7//y8vL/9vb2//r6+v/5+fn/9/f3//b29v/x8fH/ +6+vr/+Tk5P9ra2vPAAAAJAAAAAMAAAAAAAAAFy4uLpPCwsL67Ozs//Pz8//5+fn//v7+//7+/v/+/v7/ +/v7+//v7+//19fX/8PDw/8LCwvosLCyTAAAAFwAAAAAAAAAAAAAACgAAAENfX1/S5OTk/vn5+f/+/v7/ +///////////////////////////8/Pz/5ubm/l9fX9IAAABDAAAACgAAAAAAAAAAAAAAAwAAABcAAABl +YmJi3NLS0v3////////////////////////////////V1dX9ZGRk3AAAAGUAAAAXAAAAAwAAAAAAAAAA +AAAAAAAAAAUAAAAfAAAAZTMzM8KAgIDwv7+//O3t7f/t7e3/v7+//ICAgPAzMzPCAAAAZQAAAB8AAAAF +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAFwAAAEMAAAB3AAAAnwAAALMAAACzAAAAnwAAAHcAAABD +AAAAFwAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAoAAAAXAAAAJAAAAC4AAAAu +AAAAJAAAABcAAAAKAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAwAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADgEAAAMAAAABABIAAAEB +AAMAAAABABIAAAECAAMAAAAEAAAFxgEDAAMAAAABAAEAAAEGAAMAAAABAAIAAAERAAQAAAABAAAACAES +AAMAAAABAAEAAAEVAAMAAAABAAQAAAEWAAMAAAABABIAAAEXAAQAAAABAAAFEAEcAAMAAAABAAEAAAFS +AAMAAAABAAEAAAFTAAMAAAAEAAAFzodzAAcAAAwYAAAF1gAAAAAACAAIAAgACAABAAEAAQABAAAMGGFw +cGwCAAAAbW50clJHQiBYWVogB9YABAADABMALAASYWNzcEFQUEwAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAPbWAAEAAAAA0y1hcHBsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAOclhZWgAAASwAAAAUZ1hZWgAAAUAAAAAUYlhZWgAAAVQAAAAUd3RwdAAAAWgAAAAUY2hhZAAA +AXwAAAAsclRSQwAAAagAAAAOZ1RSQwAAAbgAAAAOYlRSQwAAAcgAAAAOdmNndAAAAdgAAAMSbmRpbgAA +BOwAAAY+ZGVzYwAACywAAABkZHNjbQAAC5AAAAAubW1vZAAAC8AAAAAoY3BydAAAC+gAAAAtWFlaIAAA +AAAAAF1KAAA0kQAACCVYWVogAAAAAAAAdCAAALRgAAAjPVhZWiAAAAAAAAAlbAAAFyoAAKfDWFlaIAAA +AAAAAPNSAAEAAAABFs9zZjMyAAAAAAABDEIAAAXe///zJgAAB5IAAP2R///7ov///aMAAAPcAADAbGN1 +cnYAAAAAAAAAAQHNAABjdXJ2AAAAAAAAAAEBzQAAY3VydgAAAAAAAAABAc0AAHZjZ3QAAAAAAAAAAAAD +AQAAAQACBAUGBwkKCw0ODxASExQWFxgaGxweHyAiIyQmJygpKywtLzAxMjM1Njc4OTs8PT5AQUJDREZH +SElKS0xOT1BRUlNUVVZXWFlaW1xdXl9hYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ent8fX5/gIGCg4SF +hoeIiYqLjI2Oj5CRkpOUlZaXmJmam5ydnZ6foKGio6SlpqanqKmqq6ytra6vsLGysrO0tba3uLi5uru8 +vL2+v8DBwcLDxMXGxsfIycrKy8zNzs7P0NHS0tPU1dbW19jZ2drb3Nzd3t/g4eLi4+Tl5ufo6enq6+zt +7u/w8fHy8/T19vf4+fr7/P3+/v8AAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZGhscHR8gISIjJCUnKCkq +Ky0uLzAxMzQ1Njc4OTo7PD0/QEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaWltcXV5fYGFiY2RlZmdo +aWprbG1ub3BxcnN0dXZ3d3h5ent8fH1+f4CBgoKDhIWGh4iIiYqLjI2Oj5CRkpOUlJWWl5iZmpucnZ2e +n6ChoqOkpaamp6ipqqusra6vsLCxsrO0tba3uLm5uru8vb6/wMHCw8TFx8jJysvMzc7P0NDR0tPU1dbX +2Nna29ze3+Dh4uPk5ebn6err7O3u7/Hy8/T19vf5+vv8/f7/AAIDAwQFBgcICQoKCwwNDg8QERITFBUW +FxgZGhscHR4fICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODg5Ojs8PT4+P0BBQkNDREVGR0hJSUpLTE1O +Tk9QUVJSU1RVVVZXWFhZWltbXF1eXl9gYWFiY2RkZWZnZ2hpaWprbGxtbm5vcHFxcnNzdHV1dnd4eHl6 +ent8fH1+fn+AgYGCg4SEhYaHiImJiouMjY6Oj5CRkpOTlJWWl5iZmZqbnJ2en6ChoqOkpaanqKmqq6yt +rq+xsrO0tba3uLq7vL2+wMHDxMbHycrMzs/R0tTW19nb3d7g4uTm6Ors7vDy9Pb4+vz+/wAAbmRpbgAA +AAAAAAY2AACXGgAAVjoAAFPKAACJ3gAAJ8IAABaoAABQDQAAVDkAAiuFAAIZmQABeFEAAwEAAAIAAAAA +AAEABgANABcAIwAxAEAAUgBlAHsAkwCrAMUA4gD/AR8BPwFhAYUBqgHQAfgCIAJLAncCpQLSAwIDMwNl +A5gDzgQFBD0EdQSvBOsFKQVnBacF6AYqBm4GtQb8B0UHkgfkCDkIkAjnCT4JmAn0ClAKrQsLC2sLygwq +DIwM8Q1XDcAOKA6SDv4PbA/bEE0QxBE7EbQSMRKwEzITuRREFNAVYBXxFocXHhfAGGIZBBmsGlQa+RuU +HC4czh1yHhQeux9jIA0gvCFoIhkizyOJJEEk+SW6JnknOygFKMspkypiKzIsASzXLawuhy9gMD4xGzH8 +MtszvzSgNYY2cjdcOEw5OTorOxs8CD0EPfU+6z/nQOFB2ELUQ9VE00XcRttH5EjxSgBLCUwdTTFOUE9v +UI9Rt1LdVAVVNlZsV6VY4FohW21ct135X09goGH0Y0tkqGYFZ19oxGova5ptCG54b/BxbnLsdG119Xd/ +eQh6knwqfcV/W4D4gpSEO4Xih4CJKorYjIqOOY/jkZuTWJUOlsyYiZpSnB6d4Z+soX+jWqUvpxOo+6rj +rMuuwLC4sra0rra0uL+60LzfvwDBHcLdxLXGhchYyi7MCs3lz7rRmtOA1WPXR9kq2xPc/97s4M/iveSn +5o3obupT7ELuLPAM8fLz0PW396H5f/tZ/T3//wAAAAEAAwALABYAJQA3AE0AZQCBAJ8AwQDlAQsBNQFh +AZABwQH1AisCZAKfAtwDHANfA6MD6gQ0BH8EzQT1BR0FcAXEBhsGdAbPBy0HXAeMB+4IUgi4CSAJVAmK +CfYKZArVC0cLgQu8DDIMqw0mDaIOIQ6hDyQPqRAvELgQ/RFDEc8SXRLuE4AUFRSrFUMV3RZ5FxcXthhY +GPwZoRpIGvEbnBxJHPgdqB5bHw8fxSB9ITch8iKwJDAk8yW3Jn4nRigQKNwpqSp5K0osHCzxLccuoC95 +MFUxMzISMvMz1TS5NaA2hzdxOFw5STo4Oyg8Gj4DPvs/9EDuQepD6ETpRexG8Uf3SP9LFEwhTTBOQE9S +UGZSklOrVMVV4Vb/WB5ZP1phW4Vcq13SXvthUmJ/Y69k4GYSZ0dofGm0au1tZG6ib+FxInJlc6l073Y2 +d396FXtjfLJ+A39VgKmB/4NWhK+GCYjCiiGLgYzjjkePrJESknuT5Ja8mCuZm5sMnH+d9J9qoOGiWqPV +pVGmz6eOqE6pzqtRrNSuWq/gsWmy8rR+tgu5Kbq6vE294b93wQ7Cp8RBxd3He8kZyrrLisxbzf/Po9FK +0vHUm9ZF1/HZn9tO3Cbc/96x4GTiGePQ5YjnQegf6Pzquex27jbv9/G583z0X/VC9wj40Pqa/GX+Mf// +AAAAAQADAAsAJQA3AE0AZQCBAJ8AwQELATUBYQGQAcEB9QIrAmQCnwLcAxwDXwOjA+oENAR/BM0FHQVw +BcQGGwZ0Bs8HLQeMB+4IUgi4CSAJign2CmQK1QtHC7wMMgyrDSYNog4hDqEPJA+pEC8QuBFDEl0S7hOA +FBUUqxVDFnkXFxe2GFgY/BpIGvEbnBxJHPgdqB8PH8UgfSE3IfIjbyQwJPMltydGKBAo3Cp5K0osHC3H +LqAveTEzMhIy8zS5NaA2hzhcOUk6ODwaPQ4+Az/0QO5C6EPoROlG8Uf3SglLFEwhTkBPUlF7UpJUxVXh +Vv9ZP1phXKtd0mAlYVJjr2TgZhJofGm0au1tZG6ib+FxInJldO92Nnd/eMl6FXyyfgN/VYCpgf+Er4YJ +h2WIwoohi4GOR4+skRKSe5PklVCWvJgrmZubDJx/nfSfaqDholqj1aVRps+oTqnOq1Gs1K2Xrlqv4LFp +svK0frYLt5m5Kbnxurq8Tb3hv3fBDsHawqfEQcUPxd3He8hKyRnKusuKzFvN/87Rz6PQdtFK0vHTxtSb +1kXXG9fx2MjZn9tO3Cbc/93Y3rHfiuBk4hni9ePQ5KzliOZk50HoH+j86drqueuX7HbtVu427xbv9/DX +8bnymvN89F/1QvYl9wj37PjQ+bX6mvt//GX9S/4x//8AAGRlc2MAAAAAAAAACkNvbG9yIExDRAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAAAAAAAAAAAAABtbHVjAAAAAAAAAAEAAAAMZW5VUwAAABIAAAAcAEMAbwBsAG8AcgAgAEwAQwBE +AABtbW9kAAAAAAAABhAAAJxOAAAAAL5zkQAAAAAAAAAAAAAAAAAAAAAAdGV4dAAAAABDb3B5cmlnaHQg +QXBwbGUgQ29tcHV0ZXIsIEluYy4sIDIwMDUAAAAAA + + + + + + + + 400 + 75 + + + {565, 18} + {4, 2} + 1151868928 + NSActionCell + + 67239424 + 0 + Radio + + 1211912703 + 0 + + 549453824 + {18, 18} + + YES + + YES + + + + TU0AKgAABRgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAMAAAADAAAAAwAAAAAAAAAA +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAADwRERGLJycnySsrK/A1NTXw +IyMjyRwcHIsJCQk8AAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFFRUVdVBQUOCoqKj/ +29vb//n5+f/6+vr/2tra/6qqqv9UVFTgHx8fdQAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUZGRl5 +dXV198PDw//8/Pz////////////////////////////U1NT/fHx89yUlJXkAAAAFAAAAAAAAAAAAAAAA +AAAAAxEREUZqamrmtbW1/+3t7f/+/v7//v7+//7+/v/9/f3//f39//39/f/39/f/xMTE/3d3d+YZGRlG +AAAAAwAAAAAAAAAAAAAACkJCQqGtra3/xsbG/+vr6//y8vL/9fX1//X19f/z8/P/9fX1//Ly8v/u7u7/ +0tLS/6+vr/9KSkqhAAAACgAAAAAAAAAAAAAAF3h4eN2/v7//z8/P/93d3f/q6ur/7+/v/+/v7//w8PD/ +7e3t/+3t7f/i4uL/zs7O/8XFxf98fHzdAAAAFwAAAAAAAAADAAAAJKSkpPjOzs7/2dnZ/+Dg4P/i4uL/ +5eXl/+bm5v/n5+f/5eXl/+Li4v/e3t7/2tra/9DQ0P+srKz4AAAAJAAAAAMAAAADAAAALrCwsPrW1tb/ +3t7e/+Tk5P/p6en/6+vr/+zs7P/p6en/6+vr/+fn5//k5OT/4ODg/9nZ2f+zs7P6AAAALgAAAAMAAAAD +AAAALp2dnezg4OD/5eXl/+rq6v/u7u7/8PDw//Dw8P/x8fH/8PDw/+7u7v/q6ur/5ubm/+Hh4f+ZmZns +AAAALgAAAAMAAAADAAAAJG5ubs/l5eX/6enp/+/v7//y8vL/9vb2//r6+v/5+fn/9/f3//b29v/x8fH/ +6+vr/+Tk5P9ra2vPAAAAJAAAAAMAAAAAAAAAFy4uLpPCwsL67Ozs//Pz8//5+fn//v7+//7+/v/+/v7/ +/v7+//v7+//19fX/8PDw/8LCwvosLCyTAAAAFwAAAAAAAAAAAAAACgAAAENfX1/S5OTk/vn5+f/+/v7/ +///////////////////////////8/Pz/5ubm/l9fX9IAAABDAAAACgAAAAAAAAAAAAAAAwAAABcAAABl +YmJi3NLS0v3////////////////////////////////V1dX9ZGRk3AAAAGUAAAAXAAAAAwAAAAAAAAAA +AAAAAAAAAAUAAAAfAAAAZTMzM8KAgIDwv7+//O3t7f/t7e3/v7+//ICAgPAzMzPCAAAAZQAAAB8AAAAF +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAAAFwAAAEMAAAB3AAAAnwAAALMAAACzAAAAnwAAAHcAAABD +AAAAFwAAAAUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAoAAAAXAAAAJAAAAC4AAAAu +AAAAJAAAABcAAAAKAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAwAAAAMAAAADAAAAAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADQEAAAMAAAABABIAAAEB +AAMAAAABABIAAAECAAMAAAAEAAAFugEDAAMAAAABAAEAAAEGAAMAAAABAAIAAAERAAQAAAABAAAACAES +AAMAAAABAAEAAAEVAAMAAAABAAQAAAEWAAMAAAABABIAAAEXAAQAAAABAAAFEAEcAAMAAAABAAEAAAFS +AAMAAAABAAEAAAFTAAMAAAAEAAAFwgAAAAAACAAIAAgACAABAAEAAQABA + + + + + + + + 400 + 75 + + + + + + + + + 268 + {{591, 59}, {178, 161}} + + YES + + 67239424 + 134217728 + Go! + + LucidaGrande + 10 + 16 + + + -2033434369 + 130 + + + 400 + 75 + + + + {787, 260} + + + {{0, 0}, {1440, 878}} + {1.79769e+308, 1.79769e+308} + + + + + YES + + + performMiniaturize: + + + + 37 + + + + arrangeInFront: + + + + 39 + + + + print: + + + + 86 + + + + runPageLayout: + + + + 87 + + + + showHelp: + + + + 122 + + + + clearRecentDocuments: + + + + 127 + + + + terminate: + + + + 139 + + + + orderFrontStandardAboutPanel: + + + + 142 + + + + hideOtherApplications: + + + + 146 + + + + hide: + + + + 152 + + + + unhideAllApplications: + + + + 153 + + + + cut: + + + + 175 + + + + paste: + + + + 176 + + + + redo: + + + + 178 + + + + selectAll: + + + + 179 + + + + undo: + + + + 180 + + + + copy: + + + + 181 + + + + showGuessPanel: + + + + 188 + + + + checkSpelling: + + + + 190 + + + + toggleContinuousSpellChecking: + + + + 192 + + + + performClose: + + + + 193 + + + + delete: + + + + 195 + + + + performZoom: + + + + 198 + + + + performFindPanelAction: + + + + 199 + + + + performFindPanelAction: + + + + 200 + + + + performFindPanelAction: + + + + 201 + + + + performFindPanelAction: + + + + 202 + + + + centerSelectionInVisibleArea: + + + + 203 + + + + pasteAsPlainText: + + + + 205 + + + + crash: + + + + 208 + + + + window_ + + + + 209 + + + + crash: + + + + 211 + + + + crash: + + + + 213 + + + + forkTestOptions_ + + + + 241 + + + + forkTestOptions: + + + + 242 + + + + forkTestOptions: + + + + 243 + + + + forkTestOptions: + + + + 244 + + + + forkTestGo: + + + + 250 + + + + forkTestOptions: + + + + 261 + + + + forkTestOptions: + + + + 262 + + + + showForkTestWindow: + + + + 283 + + + + generateReportWithoutCrash: + + + + 327 + + + + + YES + + 0 + + + + + + -2 + + + File's Owner + + + -1 + + + First Responder + + + -3 + + + Application + + + 21 + + + YES + + + + Window + + + 2 + + + YES + + + + + + + + + + 206 + + + YES + + + + + + 210 + + + YES + + + + + + 212 + + + YES + + + + + + 218 + + + YES + + + + + + 325 + + + YES + + + + + + 29 + + + YES + + + + + + + + MainMenu + + + 19 + + + YES + + + + + + 24 + + + YES + + + + + + + + + 5 + + + + + 23 + + + + + 92 + + + + + 197 + + + + + 56 + + + YES + + + + + + 57 + + + YES + + + + + + + + + + + + + + + + 58 + + + + + 129 + + + + + 131 + + + YES + + + + + + 130 + + + + + 134 + + + + + 136 + + + + + 143 + + + + + 144 + + + + + 145 + + + + + 149 + + + + + 150 + + + + + 196 + + + + + 83 + + + YES + + + + + + 81 + + + YES + + + + + + + + + + + + + + + + 72 + + + + + 73 + + + + + 74 + + + + + 75 + + + + + 77 + + + + + 78 + + + + + 79 + + + + + 80 + + + + + 82 + + + + + 112 + + + + + 124 + + + YES + + + + + + 125 + + + YES + + + + + + 126 + + + + + 103 + + + YES + + + + + + 106 + + + YES + + + + + + 111 + + + + + 163 + + + YES + + + + + + 169 + + + YES + + + + + + + + + + + + + + + + + 156 + + + + + 157 + + + + + 158 + + + + + 160 + + + + + 164 + + + + + 168 + + + YES + + + + + + 159 + + + YES + + + + + + + + + + 154 + + + + + 155 + + + + + 161 + + + + + 162 + + + + + 167 + + + + + 171 + + + + + 172 + + + + + 173 + + + + + 174 + + + + + 184 + + + YES + + + + + + 185 + + + YES + + + + + + + + 187 + + + + + 189 + + + + + 191 + + + + + 204 + + + + + 207 + + + Controller + + + 220 + + + YES + + + + Window (Window) + + + 221 + + + YES + + + + + + + + + 226 + + + YES + + + + + + + + + 227 + + + + + 228 + + + + + 272 + + + + + 232 + + + YES + + + + + + + + + 233 + + + + + 234 + + + + + 236 + + + + + 237 + + + YES + + + + + + + + 238 + + + + + 239 + + + + + 248 + + + YES + + + + + + 329 + + + + + 330 + + + + + 331 + + + + + 332 + + + + + 333 + + + + + 334 + + + + + 335 + + + + + 336 + + + + + 337 + + + + + + + YES + + YES + -3.IBPluginDependency + -3.ImportedFromIB2 + 103.IBPluginDependency + 103.ImportedFromIB2 + 106.IBPluginDependency + 106.ImportedFromIB2 + 111.IBPluginDependency + 111.ImportedFromIB2 + 112.IBPluginDependency + 112.ImportedFromIB2 + 124.IBPluginDependency + 124.ImportedFromIB2 + 125.IBPluginDependency + 125.ImportedFromIB2 + 126.IBPluginDependency + 126.ImportedFromIB2 + 129.IBPluginDependency + 129.ImportedFromIB2 + 130.IBPluginDependency + 130.ImportedFromIB2 + 131.IBPluginDependency + 131.ImportedFromIB2 + 134.IBPluginDependency + 134.ImportedFromIB2 + 136.IBPluginDependency + 136.ImportedFromIB2 + 143.IBPluginDependency + 143.ImportedFromIB2 + 144.IBPluginDependency + 144.ImportedFromIB2 + 145.IBPluginDependency + 145.ImportedFromIB2 + 149.IBPluginDependency + 149.ImportedFromIB2 + 150.IBPluginDependency + 150.ImportedFromIB2 + 154.IBPluginDependency + 154.ImportedFromIB2 + 155.IBPluginDependency + 155.ImportedFromIB2 + 156.IBPluginDependency + 156.ImportedFromIB2 + 157.IBPluginDependency + 157.ImportedFromIB2 + 158.IBPluginDependency + 158.ImportedFromIB2 + 159.IBPluginDependency + 159.ImportedFromIB2 + 160.IBPluginDependency + 160.ImportedFromIB2 + 161.IBPluginDependency + 161.ImportedFromIB2 + 162.IBPluginDependency + 162.ImportedFromIB2 + 163.IBPluginDependency + 163.ImportedFromIB2 + 164.IBPluginDependency + 164.ImportedFromIB2 + 167.IBPluginDependency + 167.ImportedFromIB2 + 168.IBPluginDependency + 168.ImportedFromIB2 + 169.IBPluginDependency + 169.ImportedFromIB2 + 171.IBPluginDependency + 171.ImportedFromIB2 + 172.IBPluginDependency + 172.ImportedFromIB2 + 173.IBPluginDependency + 173.ImportedFromIB2 + 174.IBPluginDependency + 174.ImportedFromIB2 + 184.IBPluginDependency + 184.ImportedFromIB2 + 185.IBPluginDependency + 185.ImportedFromIB2 + 187.IBPluginDependency + 187.ImportedFromIB2 + 189.IBPluginDependency + 189.ImportedFromIB2 + 19.IBPluginDependency + 19.ImportedFromIB2 + 191.IBPluginDependency + 191.ImportedFromIB2 + 196.IBPluginDependency + 196.ImportedFromIB2 + 197.IBPluginDependency + 197.ImportedFromIB2 + 2.IBPluginDependency + 2.ImportedFromIB2 + 204.IBPluginDependency + 204.ImportedFromIB2 + 206.IBPluginDependency + 206.ImportedFromIB2 + 207.ImportedFromIB2 + 21.IBEditorWindowLastContentRect + 21.IBPluginDependency + 21.IBWindowTemplateEditedContentRect + 21.ImportedFromIB2 + 21.windowTemplate.hasMinSize + 21.windowTemplate.minSize + 210.IBPluginDependency + 210.ImportedFromIB2 + 212.IBPluginDependency + 212.ImportedFromIB2 + 218.IBPluginDependency + 218.ImportedFromIB2 + 220.IBEditorWindowLastContentRect + 220.IBPluginDependency + 220.IBWindowTemplateEditedContentRect + 220.ImportedFromIB2 + 221.IBPluginDependency + 221.ImportedFromIB2 + 226.IBPluginDependency + 226.ImportedFromIB2 + 227.IBPluginDependency + 227.ImportedFromIB2 + 228.IBPluginDependency + 228.ImportedFromIB2 + 23.IBPluginDependency + 23.ImportedFromIB2 + 232.IBPluginDependency + 232.ImportedFromIB2 + 233.IBPluginDependency + 233.ImportedFromIB2 + 234.IBPluginDependency + 234.ImportedFromIB2 + 236.IBPluginDependency + 236.ImportedFromIB2 + 237.IBPluginDependency + 237.ImportedFromIB2 + 238.IBPluginDependency + 238.ImportedFromIB2 + 239.IBPluginDependency + 239.ImportedFromIB2 + 24.IBPluginDependency + 24.ImportedFromIB2 + 248.IBPluginDependency + 248.ImportedFromIB2 + 272.IBPluginDependency + 272.ImportedFromIB2 + 29.IBEditorWindowLastContentRect + 29.IBPluginDependency + 29.ImportedFromIB2 + 325.IBPluginDependency + 325.ImportedFromIB2 + 329.IBPluginDependency + 330.IBPluginDependency + 331.IBPluginDependency + 332.IBPluginDependency + 333.IBPluginDependency + 334.IBPluginDependency + 335.IBPluginDependency + 336.IBPluginDependency + 337.IBPluginDependency + 5.IBPluginDependency + 5.ImportedFromIB2 + 56.IBPluginDependency + 56.ImportedFromIB2 + 57.IBPluginDependency + 57.ImportedFromIB2 + 58.IBPluginDependency + 58.ImportedFromIB2 + 72.IBPluginDependency + 72.ImportedFromIB2 + 73.IBPluginDependency + 73.ImportedFromIB2 + 74.IBPluginDependency + 74.ImportedFromIB2 + 75.IBPluginDependency + 75.ImportedFromIB2 + 77.IBPluginDependency + 77.ImportedFromIB2 + 78.IBPluginDependency + 78.ImportedFromIB2 + 79.IBPluginDependency + 79.ImportedFromIB2 + 80.IBPluginDependency + 80.ImportedFromIB2 + 81.IBPluginDependency + 81.ImportedFromIB2 + 82.IBPluginDependency + 82.ImportedFromIB2 + 83.IBPluginDependency + 83.ImportedFromIB2 + 92.IBPluginDependency + 92.ImportedFromIB2 + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + + {{510, 1250}, {320, 188}} + com.apple.InterfaceBuilder.CocoaPlugin + {{510, 1250}, {320, 188}} + + + {213, 107} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{-55, 1287}, {787, 260}} + com.apple.InterfaceBuilder.CocoaPlugin + {{-55, 1287}, {787, 260}} + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{0, 1114}, {362, 20}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + + + + YES + + + YES + + + + + YES + + + YES + + + + 337 + + + + YES + + Controller + NSObject + + YES + + YES + crash: + forkTestGo: + forkTestOptions: + generateReportWithoutCrash: + showForkTestWindow: + + + YES + id + id + id + id + id + + + + YES + + YES + crash: + forkTestGo: + forkTestOptions: + generateReportWithoutCrash: + showForkTestWindow: + + + YES + + crash: + id + + + forkTestGo: + id + + + forkTestOptions: + id + + + generateReportWithoutCrash: + id + + + showForkTestWindow: + id + + + + + YES + + YES + forkTestOptions_ + window_ + + + YES + NSWindow + NSWindow + + + + YES + + YES + forkTestOptions_ + window_ + + + YES + + forkTestOptions_ + NSWindow + + + window_ + NSWindow + + + + + IBProjectSource + testapp/Controller.h + + + + Controller + NSObject + + IBUserSource + + + + + FirstResponder + NSObject + + IBUserSource + + + + + + YES + + NSActionCell + NSCell + + IBFrameworkSource + AppKit.framework/Headers/NSActionCell.h + + + + NSApplication + NSResponder + + IBFrameworkSource + AppKit.framework/Headers/NSApplication.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSApplicationScripting.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSColorPanel.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSHelpManager.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSPageLayout.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSUserInterfaceItemSearching.h + + + + NSBrowser + NSControl + + IBFrameworkSource + AppKit.framework/Headers/NSBrowser.h + + + + NSButton + NSControl + + IBFrameworkSource + AppKit.framework/Headers/NSButton.h + + + + NSButtonCell + NSActionCell + + IBFrameworkSource + AppKit.framework/Headers/NSButtonCell.h + + + + NSCell + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSCell.h + + + + NSControl + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSControl.h + + + + NSDocument + NSObject + + YES + + YES + printDocument: + revertDocumentToSaved: + runPageLayout: + saveDocument: + saveDocumentAs: + saveDocumentTo: + + + YES + id + id + id + id + id + id + + + + YES + + YES + printDocument: + revertDocumentToSaved: + runPageLayout: + saveDocument: + saveDocumentAs: + saveDocumentTo: + + + YES + + printDocument: + id + + + revertDocumentToSaved: + id + + + runPageLayout: + id + + + saveDocument: + id + + + saveDocumentAs: + id + + + saveDocumentTo: + id + + + + + IBFrameworkSource + AppKit.framework/Headers/NSDocument.h + + + + NSDocument + + IBFrameworkSource + AppKit.framework/Headers/NSDocumentScripting.h + + + + NSDocumentController + NSObject + + YES + + YES + clearRecentDocuments: + newDocument: + openDocument: + saveAllDocuments: + + + YES + id + id + id + id + + + + YES + + YES + clearRecentDocuments: + newDocument: + openDocument: + saveAllDocuments: + + + YES + + clearRecentDocuments: + id + + + newDocument: + id + + + openDocument: + id + + + saveAllDocuments: + id + + + + + IBFrameworkSource + AppKit.framework/Headers/NSDocumentController.h + + + + NSFormatter + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSFormatter.h + + + + NSMatrix + NSControl + + IBFrameworkSource + AppKit.framework/Headers/NSMatrix.h + + + + NSMenu + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSMenu.h + + + + NSMenuItem + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSMenuItem.h + + + + NSMovieView + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSMovieView.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSAccessibility.h + + + + NSObject + + + + NSObject + + + + NSObject + + + + NSObject + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSDictionaryController.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSDragging.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSFontManager.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSFontPanel.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSKeyValueBinding.h + + + + NSObject + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSNibLoading.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSOutlineView.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSPasteboard.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSSavePanel.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSTableView.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSToolbarItem.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSView.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSArchiver.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSClassDescription.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSError.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSFileManager.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSKeyValueCoding.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSKeyValueObserving.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSKeyedArchiver.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSObject.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSObjectScripting.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSPortCoder.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSRunLoop.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSScriptClassDescription.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSScriptKeyValueCoding.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSScriptObjectSpecifiers.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSScriptWhoseTests.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSThread.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSURL.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSURLConnection.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSURLDownload.h + + + + NSResponder + + IBFrameworkSource + AppKit.framework/Headers/NSInterfaceStyle.h + + + + NSResponder + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSResponder.h + + + + NSTableView + NSControl + + + + NSText + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSText.h + + + + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSClipView.h + + + + NSView + + + + NSView + + IBFrameworkSource + AppKit.framework/Headers/NSRulerView.h + + + + NSView + NSResponder + + + + NSWindow + + IBFrameworkSource + AppKit.framework/Headers/NSDrawer.h + + + + NSWindow + NSResponder + + IBFrameworkSource + AppKit.framework/Headers/NSWindow.h + + + + NSWindow + + IBFrameworkSource + AppKit.framework/Headers/NSWindowScripting.h + + + + + 0 + IBCocoaFramework + + com.apple.InterfaceBuilder.CocoaPlugin.macosx + + + + com.apple.InterfaceBuilder.CocoaPlugin.macosx + + + + com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 + + + YES + ../../Breakpad.xcodeproj + 3 + + YES + + YES + NSMenuCheckmark + NSMenuMixedState + + + YES + {9, 8} + {7, 2} + + + + diff --git a/shared/sentry/external/breakpad/src/client/mac/testapp/Info.plist b/shared/sentry/external/breakpad/src/client/mac/testapp/Info.plist new file mode 100644 index 000000000..6094ec6ce --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/testapp/Info.plist @@ -0,0 +1,55 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + bomb + CFBundleIdentifier + com.Google.BreakpadTest + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + BreakpadProductDisplay + Breakpad Tester + BreakpadProduct + Breakpad_Tester + BreakpadVersion + 1.2.3.4 + BreakpadReportInterval + 10 + BreakpadSkipConfirm + NO + BreakpadSendAndExit + YES + BreakpadRequestEmail + YES + BreakpadRequestComments + YES + BreakpadVendor + Foo Bar Corp, Incorporated, LTD, LLC + BreakpadServerParameters + + Param1 + Value1 + Param2 + Value2 + + LSUIElement + 1 + + diff --git a/shared/sentry/external/breakpad/src/client/mac/testapp/TestClass.h b/shared/sentry/external/breakpad/src/client/mac/testapp/TestClass.h new file mode 100644 index 000000000..0a6d736d1 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/testapp/TestClass.h @@ -0,0 +1,37 @@ +// 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 + +@interface TestClass : NSObject { +} + +- (void)wait; + +@end diff --git a/shared/sentry/external/breakpad/src/client/mac/testapp/TestClass.mm b/shared/sentry/external/breakpad/src/client/mac/testapp/TestClass.mm new file mode 100644 index 000000000..6e6a8833d --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/testapp/TestClass.mm @@ -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. + +#include + +#import "TestClass.h" + +struct AStruct { + int x; + float y; + double z; +}; + +class InternalTestClass { + public: + InternalTestClass(int a) : a_(a) {} + ~InternalTestClass() {} + + void snooze(float a); + void snooze(int a); + int snooze(int a, float b); + + protected: + int a_; + AStruct s_; + + static void InternalFunction(AStruct &s); + static float kStaticFloatValue; +}; + +void InternalTestClass::snooze(float a) { + InternalFunction(s_); + sleep(a_ * a); +} + +void InternalTestClass::snooze(int a) { + InternalFunction(s_); + sleep(a_ * a); +} + +int InternalTestClass::snooze(int a, float b) { + InternalFunction(s_); + sleep(a_ * a * b); + + return 33; +} + +void InternalTestClass::InternalFunction(AStruct &s) { + s.x = InternalTestClass::kStaticFloatValue; +} + +float InternalTestClass::kStaticFloatValue = 42; + +static float PlainOldFunction() { + return 3.14145f; +} + +@implementation TestClass + +- (void)wait { + InternalTestClass t(10); + float z = PlainOldFunction(); + + while (1) { + t.snooze(z); + } +} + +@end diff --git a/shared/sentry/external/breakpad/src/client/mac/testapp/bomb.icns b/shared/sentry/external/breakpad/src/client/mac/testapp/bomb.icns new file mode 100644 index 0000000000000000000000000000000000000000..c360dbf618946920498958680eddb253a1ff677f GIT binary patch literal 23659 zcmeHP34Bav*FSeMlSyXHz9&dYvxtNUiCB_I6%}h!LVdhS6y=RZDWxV0K`KJRt3)j! zNJ}a~5t=kgOGB--HE1lg#8$Pmrp|ZnMA|I(PBO_I-|zQ+dHjBpbD#5^^FPmi?s=YT z+&h!v0N$Q3Zc0Eq0GDAv3F+TiP`URTA3M~$edMeqiEC2EyOe`fn-9Orx^VAM`q7j4 z=mg?}d`TT1 zWIa3@kE*X19$0-0@rTrm^N$e^l5p;zU%-AjBFXHCAL-D1NOZa;99;X3Td=j@DDgjz zxdhU5&rgSwWC*`*y8^A#TtYaOF(A5_2fPL_)V%m9tIKil_Y_gtl1DO zevWrPNC&eqM+@=(=h;IFH26I*bF7m=l7jb(@IVHb`K&I;JM__*K_wde0^(6}<@;?p zc^Bew0Wdgf9!P#b{N~Em3^0xQ^xKtF_HBvBO<<3hqnQ{T(bl(hD|h#nzHI}72aHZw zQ;KtdnHHK1%{(3A$}hBU6|YUnF7{NU(Hrn7hSNkVzcdN3TQ}3BHzdK<4-7cEpEH z0BtrcqDo^)B8sR1)`A!_274+p!PGe{xg}V4n*)+jxc?W-jRz)RCp9J_KsQ_mCYapK zaVAal@?%3BUy1N&{vmuBRfY-R;(a)YBDU0UlQ-fp`HmvTq_VZ{k-V z%6ZK$3(U+q0(kv~xOfhjc#WNALtN__JIJV*<{LoV+8$snb#urfVA|i#V|8{g+W-<} zdw|YbYN(QKs#IVsW^Q98oHl~k-x6TA2F1_AAk=|aJu#P9k#b^L`2qB&ZdwH(?&Q7@ zn0B}om~|aDgSed>q(`WoZ@R^Us7-(*1sH$a0?cQvmx5Gc)4X*^Xe-P4Ah!1eh#`C4 zO9YWaup@PJN9uYe&0`6uBmq-4AHVkS$zS;BmxF6t%ms0v6s$@~>p>b+CgN8+HcO^{ zioqN*D7o2UFmav!ll76723qJMQu2C17npy%%g$C?0<7HGN+|992M({xjzG#?zovobdN;< zQ`4!99H?qJh6YAQDH!Z6c*Z#neSzo}{Aaab^IGsSZ}u zWIhfpc5kG*Iug(JY$`I_{76C)?7Rph3K~j1XreIKg*2~Uh|J@0%YX^=N(D)v4S*_^ zc#{a5iLvQG!@CX57v_XF=~eyo2~8l+6(F+i>`Gl6q9nF-m^=Cgb@cJ_5B8T(bGzrQCez!J3X6*ika-fm5x`6%r&f`SB#zGT zu_*}e?*hgLS5etOs^;tk2s?KJBOgyWsrJ-Iv*saF-f9ocM9c(csF<2=^wYTraRtCM zTfdUUSOQG%@+4$Rq?9L(p}cuE)Gh5x6A|9nN3D)4$X+pZV)Mf^+IM{oj7!d^EF!h1 zWj+my*C@+u9A#~H`wxw^75H@#FcG*MnCU9Yjemn4eQy4TM21`f#&H=-bs@@pEKz5? zE(6mCN!NBaq5@ee!Imh`^F&RQ6Xlw{3}DDF)XB+9Nwhaniis@Ml_<%-SSqLt;4PMD zUk1zoBxA7+QIRZl+=HmzEVZpAQQcYUS5Kl|*OhBqSq?iA)mN9-nbZwr)rAp)cqmIn z5oN{>q2UrT$#Mo!lNCfcuOTYlhNxCWM1AH=R6Dj3H++Z+XDNJ*sKH2;eMfSUhSXGQ znfZ1^xn>h}%!8;vwzV*Vs3AyAHs1VaV7zxx!)G}Y^(NZ|7<7S_!#UKA!zJh0C5fn& zPDBl6DGWbH6ENd5U=pp*0uzQrVW%^|sHd(0CRX+nYZXzqy-!m;V%7mOR(6UV=tN<_ zN!F_Mz|2seU}cHI@Z;1~8QH+BZ+;AzD9ol2|DO3#V0?=<5xdI~VC>j+VwKAeG)sr+ z7P1NI!!H&=eDe?u;#IVyV%T@UI3<1w%vZh#Kx8oyw*j-vV?T(@hhsi487;pdatQ7K zW^VIB5GkT*KVarJ+ehRG+)ZWM6oANT1k&i-*L63DnomXI;Q+->8l;&;L{4sz4llo^eP}IIj5?lI*|p5Qwq|IB%xKsQqmGf%c8%J#06CiK6}cH{Kv69x{mVIX98QSB{O^A3!y%Z#B_wKPp_%oQ{x6=oAYUQXDF94~` z+9e=r`c5okxAHy9rVa1aB{0C(!^5|2`!2nrV-vFwZ_^HJa<#OyJb|{$LO{3*;Qkw^ zg8Pk`_?CC4$PebtT$VV-k9?~xlRsZxa&hncq5}`Eeye?UY2$~iZ#6UTzV>pV_R_Z+ zEpxdbUTbnMC53gaUW+NDhuo)6cEoF$Jr6Wm$-^DmJ+r#8?$xqiiZoiAXWJ6L$|%-8 z+?TYV ztic<1{HfKZk&E@}x&s%r(*^+KochPbY94iWN4d6iQaZ^0Rg|Z_v^sN8-nAp;FMKT1 z1ns3f?QTorcFD-i-WxY!;I13Tk8yfg`?T8ar?N6YHfT})l1aPQ-aU2Wem(rG@Pkyl zjs31J0r`WtoaO8DHkDpK4kdN^SsRNrT9c1-ewMuKz~jVunP*C`?a#fz?``?q+@#f( zIaYXE@-ru{h*Ho06)E?1Mw)#P&h7r0zkXgQmQ4?vwV zDDJSw7{hhG#<=o{3U;y7xx(l+W6n1xuY)N`aJH~*k@!=j?X#@FqJNyRd?S|#m_ zgM9fCJCW7)30oL)8im zz5YA5PCb5tyEX*pMFoein)HhsCn3$|r(o23t+-oAI6J{b+ zsbmxZ;PS!d((5dRhnhUFrl40-(5orv)fDt<3VQul1ihvNO`HG_YQ`Qd{(nePwdwyw zHSM~O9oE(V)u6ejuNQ3?>nwV?HndoB8!7$uC;Ln3PFByC*b0(q4=P}*{uYHWFR2ae zzO0J-H~l=*;iVYhcK!wAEjm3#)SkIlX+kL-jv76gY}LWt&y2Kf);Dp>;tn#Wuy;3J zuE^|cba)t{LptMI(c2~!fOKMdksLQ|I>#4jm@%1yThX1`8LV>72V9s<6Lf>N~zF;(WZ31!pU!_8H#)s zU6&qQ(EaJdt*V!fdEni^`~ny55)I0YtHA!}_o|v(kE?D%{`4I?^#7cS_V1y5wuMy(M=6&RmgIhvAydhp*ha^WZ5yzI&tW z^p3Qcu5Pl*!gQ8ZBkiM$9UQ4o@ma)Mzs2o!VXD`FqikiC!f*VB3xCzn&pzHim=;fAaOeuj+(s<*9riR9o#fxGRsDKq*d)^pxW=dJbd9S4N!0JB#$)R= zjBWrr$dd&^N6fA7XscqU;|{^#@vN$f;`))D&It#P=M2X0t=pi4GU1G&_;hzn$h6Wh zFM}ENOh|OR?pD7(uPpU%LD3QEBCjEd?r92|&d_~J15(i6Zz%#<@jnKD1GK8;L^aZk zVud02Rwa#ut114xhavDHx-cwk$}wo|jeIUFIzI1UNcD9uCc;`U%>aB!j z@i9a{zVeij@#pHt?hFO0HOmYA{FmNvtNf_Yc(wE;>+1k7LE;RxFso*K zqb~|GwrWY^(6{uQxiG7ZgIP^$IDZGo=`%p1Cp`xaPIZSD8*kn={uhA&ZR6F~so^|N zES|chf1Sbt6u8TO((t|j>g!bTod3*^zO8);sUP12kO&xR(^HT4`S4{w>X8zTHIENp zd0IFSJ(?Et;adqE^58aN4d1c5t?s0hTS&m%Z7+_1Yc+$*T>(+}Rqf)t_|Ve$yDw{1 zG2aescZ1((0Xh4mwOYO+!=_9?6n<4Z_%0^WsYj8(1ep#8&WazhsuNqYtc|@9UOy}fGe~5Huf$~Dqpa*5}}U2evP^d^=wK5hu%L{Iqu>ZQuWOGx%)qQ8cZpZ8v0JS z)49Q*)dWKu^iue6nxUWcR0FO0V3ln6JSb>WRYPOxj(PR$@)yN!P7H)fzy{JFn7xM60hW9(^^$$^exH78NiY?~jwS zy&ApX)M1kOt7Egzb9@%?yll&aHVxkC>Hw|I7Fl@pid+8m@t<$syMO=Qt#dyXX3dHY fvJqD=W$Y)Z!ot?o&CSJDt&r*o=^EP%;q?D6Q{3}w literal 0 HcmV?d00001 diff --git a/shared/sentry/external/breakpad/src/client/mac/testapp/crashInMain b/shared/sentry/external/breakpad/src/client/mac/testapp/crashInMain new file mode 100755 index 0000000000000000000000000000000000000000..03bb3172769307065261123d239ca253e4dbf1cf GIT binary patch literal 12588 zcmeI3&ubG=5XaxPtA<+JAU%kR28kz2qTs=UhejH-*dL*dP;BLOlWfzp`9-oREeHmd z9>P-e;6ZQV#j6Joo}^kqMD%YcXd#CRJp`mZSiiHoY0{=jPeNgaPTsuv@!s^4J#=CE zZTsi%6GDh~XdAQ>x`NFTRt0(kDrF>eJA6MpI)Y7Iymg~>rMeZUl+o~bv?)$C^*XF{ z?=dLdOCqJAFBp#P?Dy7BT@>Ou0#n_M8arDRWqZYkl<}fbNM)0*V}B1@I73Bj3uiP- zy$A9&`tJ`#Lrqye*!ADR?Z3{AvZPFChDMenQnuFjPzbfn_qplzf-@G=iH2wY89hEB z&LiCOk29$Mhfpa8h9V=Qjc?#8oTw8%XC?lmlush%Y$=nMh89cf&c3z2g91Jwj??Oj z$3bd;mvol%%nS|;-wpQ<)Nk)YfbK>Qy+~@l7hapk88Q&3XYd);r&v!yBTy1zO-i-Y z$KMztB%YxDpm-Nz#EmWqX7Iu#4L$j`w?J#4=#e>Gpdy?OSO-0XN6yhP6^^+3Oy z7kXHs=k*|=yRSp(E~iI2S!wixo|5snEC#Lx&{`^n=>8f-$+gssEfx(u6S%cogbWrY zzyz286JP>NfC(@GCcp&#AAwm>SvP-dnj2p0fm)gLi{&9tWzeshtIGTDZORw8JXX88 zsxGaReqqyD?L~8ye!r+{c-N>^snzkF*K=5`GX2fJv*!4$vb1;iD(hqBhTnEhdc}wUtKn@z*$lK)zdiB{OF(fym~odzsgbe9-$W;@7iMzlwNi^eL{Gk zDpZiNFaajO1egF5U;<2l2`~XBzyz286JP>IjzDc!j$G^9FcV+`On?b60Vco%m;e)C z0!)AjFaaiTNCGr}d;#OfFEFc2^Uq|zfgOO|g<)&2Pl!I)-LPpE;Erq4OhCl7X)a(A zb~_CW;3x0;X=Y%NY@GQ4XFhUmnu}h7tq8>)f3})z8cLvNcQKPDZCEU2&>u)M2G_ zFF@&B5-Ck}(X89``{VWc4IwN9^v-6Do$VDX_KJ&?$)ag!xwPXr*~32e=s;{Ad$^_1 z1NoZ$4@M>dQR>U2nx}S zaNr+%Q2$F%DThX4W8=*?a2rn4iC}#t{-l&oBISH3Taacvp{w=#*80m72nunLR!4k7 zc*&%9Nqg0QGs8op52J%ajnfAZptI3KFOuBn!fW%mP6p!bSk|$=!g>W7gOU(yQmUms z{>Bg?@dEXy#p&G>KRTnYR5bc@Ez##7?}Cqd@GkefUwZTP!|P|a3NP3DJL$Qo2YNO? z^sGX^*VBZ~4ngTGyGIY5+$9ovJTtGQ)0$bf`@8GTu}@c;J)`e*GAWDUJ7Ki1C6G(| zx@#0A56|(MiE`0Yv*CM3MaW=b0!)AjFaajO1egF5U;<3w{}Y%Om0fG+hqdRgKK52- zLt<^jR~Zg@txe_Qw>ISqT)wKu+Vrk&lz!r-z1E8+DuW?Wo5FX;L5*6PK71#!R9BL? z9&Xot)s+eRZmP07VeJKM=ggXQ4bF-;RO?2y^(ip(c{8KxdcHUKG;ipsn~1gwec<@F zpZlQnvC|$D!Uy$21t|*?U;<2l2`~XBzyz286JP>NfC(@GCUEWu9308HYn>Zr0!)Aj zFaajO1egF5U;<2l2`~XBzy!`nfaZ^TFxdPSv&uAuO!fxsFzgNtTl<4T48ZP$O|t+G z9GhkWVvbF70kg0@G%$d_g5#%|fil_Ha|?SeJ2uTlufkS@LgU<`>auCBn`XX)f`+nX z(JUooLd&Hv(JwK)P*4pTv!~EpDVLObKL4atsOQRTJefgKJVi;P(-9+`bk+qF$%2tj t=i*rvb~Yo`MKxJ6)uVhhx1bsMoWL$xP87|!VG1N^xEApL1m>I&e*k;r-N*m{ literal 0 HcmV?d00001 diff --git a/shared/sentry/external/breakpad/src/client/mac/testapp/main.m b/shared/sentry/external/breakpad/src/client/mac/testapp/main.m new file mode 100644 index 000000000..de6733269 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/testapp/main.m @@ -0,0 +1,34 @@ +// 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 + +int main(int argc, char *argv[]) { + return NSApplicationMain(argc, (const char**)argv); +} diff --git a/shared/sentry/external/breakpad/src/client/mac/tests/BreakpadFramework_Test.mm b/shared/sentry/external/breakpad/src/client/mac/tests/BreakpadFramework_Test.mm new file mode 100644 index 000000000..2ea103c69 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/tests/BreakpadFramework_Test.mm @@ -0,0 +1,217 @@ +// 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. +// +// BreakpadFramework_Test.mm +// Test case file for Breakpad.h/mm. +// + +#import "GTMSenTestCase.h" +#import "Breakpad.h" + +#include + +@interface BreakpadFramework_Test : GTMTestCase { + @private + int last_exception_code_; + int last_exception_type_; + mach_port_t last_exception_thread_; + // We're not using Obj-C BOOL because we need to interop with + // Breakpad's callback. + bool shouldHandleException_; +} + +// This method is used by a callback used by test cases to determine +// whether to return true or false to Breakpad when handling an +// exception. +- (bool)shouldHandleException; +// This method returns a minimal dictionary that has what +// Breakpad needs to initialize. +- (NSMutableDictionary *)breakpadInitializationDictionary; +// This method is used by the exception handling callback +// to communicate to test cases the properites of the last +// exception. +- (void)setLastExceptionType:(int)type andCode:(int)code + andThread:(mach_port_t)thread; +@end + +// Callback for Breakpad exceptions +bool myBreakpadCallback(int exception_type, + int exception_code, + mach_port_t crashing_thread, + void *context); + +bool myBreakpadCallback(int exception_type, + int exception_code, + mach_port_t crashing_thread, + void *context) { + BreakpadFramework_Test *testCaseClass = + (BreakpadFramework_Test *)context; + [testCaseClass setLastExceptionType:exception_type + andCode:exception_code + andThread:crashing_thread]; + bool shouldHandleException = + [testCaseClass shouldHandleException]; + NSLog(@"Callback returning %d", shouldHandleException); + return shouldHandleException; +} +const int kNoLastExceptionCode = -1; +const int kNoLastExceptionType = -1; +const mach_port_t kNoLastExceptionThread = MACH_PORT_NULL; + +@implementation BreakpadFramework_Test +- (void) initializeExceptionStateVariables { + last_exception_code_ = kNoLastExceptionCode; + last_exception_type_ = kNoLastExceptionType; + last_exception_thread_ = kNoLastExceptionThread; +} + +- (NSMutableDictionary *)breakpadInitializationDictionary { + NSMutableDictionary *breakpadParams = + [NSMutableDictionary dictionaryWithCapacity:3]; + + [breakpadParams setObject:@"UnitTests" forKey:@BREAKPAD_PRODUCT]; + [breakpadParams setObject:@"1.0" forKey:@BREAKPAD_VERSION]; + [breakpadParams setObject:@"http://staging" forKey:@BREAKPAD_URL]; + return breakpadParams; +} + +- (bool)shouldHandleException { + return shouldHandleException_; +} + +- (void)setLastExceptionType:(int)type + andCode:(int)code + andThread:(mach_port_t)thread { + last_exception_type_ = type; + last_exception_code_ = code; + last_exception_thread_ = thread; +} + +// Test that the parameters mark required actually enable Breakpad to +// be initialized. +- (void)testBreakpadInstantiationWithRequiredParameters { + BreakpadRef b = BreakpadCreate([self breakpadInitializationDictionary]); + STAssertNotNULL(b, @"BreakpadCreate failed with required parameters"); + BreakpadRelease(b); +} + +// Test that Breakpad fails to initialize cleanly when required +// parameters are not present. +- (void)testBreakpadInstantiationWithoutRequiredParameters { + NSMutableDictionary *breakpadDictionary = + [self breakpadInitializationDictionary]; + + // Skip setting version, so that BreakpadCreate fails. + [breakpadDictionary removeObjectForKey:@BREAKPAD_VERSION]; + BreakpadRef b = BreakpadCreate(breakpadDictionary); + STAssertNULL(b, @"BreakpadCreate did not fail when missing a required" + " parameter!"); + + breakpadDictionary = [self breakpadInitializationDictionary]; + // Now test with no product + [breakpadDictionary removeObjectForKey:@BREAKPAD_PRODUCT]; + b = BreakpadCreate(breakpadDictionary); + STAssertNULL(b, @"BreakpadCreate did not fail when missing a required" + " parameter!"); + + breakpadDictionary = [self breakpadInitializationDictionary]; + // Now test with no URL + [breakpadDictionary removeObjectForKey:@BREAKPAD_URL]; + b = BreakpadCreate(breakpadDictionary); + STAssertNULL(b, @"BreakpadCreate did not fail when missing a required" + " parameter!"); + BreakpadRelease(b); +} + +// Test to ensure that when we call BreakpadAddUploadParameter, +// it's added to the dictionary correctly(this test depends on +// some internal details of Breakpad, namely, the special prefix +// that it uses to figure out which key/value pairs to upload). +- (void)testAddingBreakpadServerVariable { + NSMutableDictionary *breakpadDictionary = + [self breakpadInitializationDictionary]; + + BreakpadRef b = BreakpadCreate(breakpadDictionary); + STAssertNotNULL(b, @"BreakpadCreate failed with valid dictionary!"); + + BreakpadAddUploadParameter(b, + @"key", + @"value"); + + // Test that it did not add the key/value directly, e.g. without + // prepending the key with the prefix. + STAssertNil(BreakpadKeyValue(b, @"key"), + @"AddUploadParameter added key directly to dictionary" + " instead of prepending it!"); + + NSString *prependedKeyname = + [@BREAKPAD_SERVER_PARAMETER_PREFIX stringByAppendingString:@"key"]; + + STAssertEqualStrings(BreakpadKeyValue(b, prependedKeyname), + @"value", + @"Calling BreakpadAddUploadParameter did not prepend " + "key name"); + BreakpadRelease(b); +} + +// Test that when we do on-demand minidump generation, +// the exception code/type/thread are set properly. +- (void)testFilterCallbackReturnsFalse { + NSMutableDictionary *breakpadDictionary = + [self breakpadInitializationDictionary]; + + BreakpadRef b = BreakpadCreate(breakpadDictionary); + STAssertNotNULL(b, @"BreakpadCreate failed with valid dictionary!"); + BreakpadSetFilterCallback(b, &myBreakpadCallback, self); + + // This causes the callback to return false, meaning + // Breakpad won't take the exception + shouldHandleException_ = false; + + [self initializeExceptionStateVariables]; + STAssertEquals(last_exception_type_, kNoLastExceptionType, + @"Last exception type not initialized correctly."); + STAssertEquals(last_exception_code_, kNoLastExceptionCode, + @"Last exception code not initialized correctly."); + STAssertEquals(last_exception_thread_, kNoLastExceptionThread, + @"Last exception thread is not initialized correctly."); + + // Cause Breakpad's exception handler to be invoked. + BreakpadGenerateAndSendReport(b); + + STAssertEquals(last_exception_type_, 0, + @"Last exception type is not 0 for on demand"); + STAssertEquals(last_exception_code_, 0, + @"Last exception code is not 0 for on demand"); + STAssertEquals(last_exception_thread_, mach_thread_self(), + @"Last exception thread is not mach_thread_self() " + "for on demand"); +} + +@end diff --git a/shared/sentry/external/breakpad/src/client/mac/tests/crash_generation_server_test.cc b/shared/sentry/external/breakpad/src/client/mac/tests/crash_generation_server_test.cc new file mode 100644 index 000000000..128f25c10 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/tests/crash_generation_server_test.cc @@ -0,0 +1,398 @@ +// 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. +// +// crash_generation_server_test.cc +// Unit tests for CrashGenerationServer + +#include +#include +#include +#include +#include + +#include + +#include "breakpad_googletest_includes.h" +#include "client/mac/crash_generation/client_info.h" +#include "client/mac/crash_generation/crash_generation_client.h" +#include "client/mac/crash_generation/crash_generation_server.h" +#include "client/mac/handler/exception_handler.h" +#include "client/mac/tests/spawn_child_process.h" +#include "common/tests/auto_tempdir.h" +#include "google_breakpad/processor/minidump.h" + +namespace google_breakpad { +// This acts as the log sink for INFO logging from the processor +// logging code. The logging output confuses XCode and makes it think +// there are unit test failures. testlogging.h handles the overriding. +std::ostringstream info_log; +} + +namespace { +using std::string; +using google_breakpad::AutoTempDir; +using google_breakpad::ClientInfo; +using google_breakpad::CrashGenerationClient; +using google_breakpad::CrashGenerationServer; +using google_breakpad::ExceptionHandler; +using google_breakpad::Minidump; +using google_breakpad::MinidumpContext; +using google_breakpad::MinidumpException; +using google_breakpad::MinidumpModule; +using google_breakpad::MinidumpModuleList; +using google_breakpad::MinidumpSystemInfo; +using google_breakpad::MinidumpThread; +using google_breakpad::MinidumpThreadList; +using testing::Test; +using namespace google_breakpad_test; + +class CrashGenerationServerTest : public Test { +public: + // The port name to receive messages on + char mach_port_name[128]; + // Filename of the last dump that was generated + string last_dump_name; + // PID of the child process + pid_t child_pid; + // A temp dir + AutoTempDir temp_dir; + // Counter just to ensure that we don't hit the same port again + static int i; + bool filter_callback_called; + + void SetUp() { + sprintf(mach_port_name, + "com.google.breakpad.ServerTest.%d.%d", getpid(), + CrashGenerationServerTest::i++); + child_pid = (pid_t)-1; + filter_callback_called = false; + } +}; +int CrashGenerationServerTest::i = 0; + +// Test that starting and stopping a server works +TEST_F(CrashGenerationServerTest, testStartStopServer) { + CrashGenerationServer server(mach_port_name, + NULL, // filter callback + NULL, // filter context + NULL, // dump callback + NULL, // dump context + NULL, // exit callback + NULL, // exit context + false, // generate dumps + ""); // dump path + ASSERT_TRUE(server.Start()); + ASSERT_TRUE(server.Stop()); +} + +// Test that requesting a dump via CrashGenerationClient works +// Test without actually dumping +TEST_F(CrashGenerationServerTest, testRequestDumpNoDump) { + CrashGenerationServer server(mach_port_name, + NULL, // filter callback + NULL, // filter context + NULL, // dump callback + NULL, // dump context + NULL, // exit callback + NULL, // exit context + false, // don't generate dumps + temp_dir.path()); // dump path + ASSERT_TRUE(server.Start()); + + pid_t pid = fork(); + ASSERT_NE(-1, pid); + if (pid == 0) { + CrashGenerationClient client(mach_port_name); + bool result = client.RequestDump(); + exit(result ? 0 : 1); + } + + int ret; + ASSERT_EQ(pid, waitpid(pid, &ret, 0)); + EXPECT_TRUE(WIFEXITED(ret)); + EXPECT_EQ(0, WEXITSTATUS(ret)); + EXPECT_TRUE(server.Stop()); + // check that no minidump was written + string pattern = temp_dir.path() + "/*"; + glob_t dirContents; + ret = glob(pattern.c_str(), GLOB_NOSORT, NULL, &dirContents); + EXPECT_EQ(GLOB_NOMATCH, ret); + if (ret != GLOB_NOMATCH) + globfree(&dirContents); +} + +void dumpCallback(void* context, const ClientInfo& client_info, + const std::string& file_path) { + if (context) { + CrashGenerationServerTest* self = + reinterpret_cast(context); + if (!file_path.empty()) + self->last_dump_name = file_path; + self->child_pid = client_info.pid(); + } +} + +void* RequestDump(void* context) { + CrashGenerationClient client((const char*)context); + bool result = client.RequestDump(); + return (void*)(result ? 0 : 1); +} + +// Test that actually writing a minidump works +TEST_F(CrashGenerationServerTest, testRequestDump) { + CrashGenerationServer server(mach_port_name, + NULL, // filter callback + NULL, // filter context + dumpCallback, // dump callback + this, // dump context + NULL, // exit callback + NULL, // exit context + true, // generate dumps + temp_dir.path()); // dump path + ASSERT_TRUE(server.Start()); + + pid_t pid = fork(); + ASSERT_NE(-1, pid); + if (pid == 0) { + // Have to spawn off a separate thread to request the dump, + // because MinidumpGenerator assumes the handler thread is not + // the only thread + pthread_t thread; + if (pthread_create(&thread, NULL, RequestDump, (void*)mach_port_name) != 0) + exit(1); + void* result; + pthread_join(thread, &result); + exit(reinterpret_cast(result)); + } + + int ret; + ASSERT_EQ(pid, waitpid(pid, &ret, 0)); + EXPECT_TRUE(WIFEXITED(ret)); + EXPECT_EQ(0, WEXITSTATUS(ret)); + EXPECT_TRUE(server.Stop()); + // check that minidump was written + ASSERT_FALSE(last_dump_name.empty()); + struct stat st; + EXPECT_EQ(0, stat(last_dump_name.c_str(), &st)); + EXPECT_LT(0, st.st_size); + // check client's PID + ASSERT_EQ(pid, child_pid); +} + +static void Crasher() { + int* a = (int*)0x42; + + fprintf(stdout, "Going to crash...\n"); + fprintf(stdout, "A = %d", *a); +} + +// Test that crashing a child process with an OOP ExceptionHandler installed +// results in a minidump being written by the CrashGenerationServer in +// the parent. +TEST_F(CrashGenerationServerTest, testChildProcessCrash) { + CrashGenerationServer server(mach_port_name, + NULL, // filter callback + NULL, // filter context + dumpCallback, // dump callback + this, // dump context + NULL, // exit callback + NULL, // exit context + true, // generate dumps + temp_dir.path()); // dump path + ASSERT_TRUE(server.Start()); + + pid_t pid = fork(); + ASSERT_NE(-1, pid); + if (pid == 0) { + // Instantiate an OOP exception handler. + ExceptionHandler eh("", NULL, NULL, NULL, true, mach_port_name); + Crasher(); + // not reached + exit(0); + } + + int ret; + ASSERT_EQ(pid, waitpid(pid, &ret, 0)); + EXPECT_FALSE(WIFEXITED(ret)); + EXPECT_TRUE(server.Stop()); + // check that minidump was written + ASSERT_FALSE(last_dump_name.empty()); + struct stat st; + EXPECT_EQ(0, stat(last_dump_name.c_str(), &st)); + EXPECT_LT(0, st.st_size); + + // Read the minidump, sanity check some data. + Minidump minidump(last_dump_name.c_str()); + ASSERT_TRUE(minidump.Read()); + + MinidumpSystemInfo* system_info = minidump.GetSystemInfo(); + ASSERT_TRUE(system_info); + const MDRawSystemInfo* raw_info = system_info->system_info(); + ASSERT_TRUE(raw_info); + EXPECT_EQ(kNativeArchitecture, raw_info->processor_architecture); + + MinidumpThreadList* thread_list = minidump.GetThreadList(); + ASSERT_TRUE(thread_list); + ASSERT_EQ((unsigned int)1, thread_list->thread_count()); + + MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0); + ASSERT_TRUE(main_thread); + MinidumpContext* context = main_thread->GetContext(); + ASSERT_TRUE(context); + EXPECT_EQ(kNativeContext, context->GetContextCPU()); + + MinidumpModuleList* module_list = minidump.GetModuleList(); + ASSERT_TRUE(module_list); + const MinidumpModule* main_module = module_list->GetMainModule(); + ASSERT_TRUE(main_module); + EXPECT_EQ(GetExecutablePath(), main_module->code_file()); +} + +#if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6) && \ + (defined(__x86_64__) || defined(__i386__)) +// Test that crashing a child process of a different architecture +// produces a valid minidump. +TEST_F(CrashGenerationServerTest, testChildProcessCrashCrossArchitecture) { + CrashGenerationServer server(mach_port_name, + NULL, // filter callback + NULL, // filter context + dumpCallback, // dump callback + this, // dump context + NULL, // exit callback + NULL, // exit context + true, // generate dumps + temp_dir.path()); // dump path + ASSERT_TRUE(server.Start()); + + // Spawn a child process + string helper_path = GetHelperPath(); + const char* argv[] = { + helper_path.c_str(), + "crash", + mach_port_name, + NULL + }; + pid_t pid = spawn_child_process(argv); + ASSERT_NE(-1, pid); + + int ret; + ASSERT_EQ(pid, waitpid(pid, &ret, 0)); + EXPECT_FALSE(WIFEXITED(ret)); + EXPECT_TRUE(server.Stop()); + // check that minidump was written + ASSERT_FALSE(last_dump_name.empty()); + struct stat st; + EXPECT_EQ(0, stat(last_dump_name.c_str(), &st)); + EXPECT_LT(0, st.st_size); + +const MDCPUArchitecture kExpectedArchitecture = +#if defined(__x86_64__) + MD_CPU_ARCHITECTURE_X86 +#elif defined(__i386__) + MD_CPU_ARCHITECTURE_AMD64 +#endif + ; +const uint32_t kExpectedContext = +#if defined(__i386__) + MD_CONTEXT_AMD64 +#elif defined(__x86_64__) + MD_CONTEXT_X86 +#endif + ; + + // Read the minidump, sanity check some data. + Minidump minidump(last_dump_name.c_str()); + ASSERT_TRUE(minidump.Read()); + + MinidumpSystemInfo* system_info = minidump.GetSystemInfo(); + ASSERT_TRUE(system_info); + const MDRawSystemInfo* raw_info = system_info->system_info(); + ASSERT_TRUE(raw_info); + EXPECT_EQ(kExpectedArchitecture, raw_info->processor_architecture); + + MinidumpThreadList* thread_list = minidump.GetThreadList(); + ASSERT_TRUE(thread_list); + ASSERT_EQ((unsigned int)1, thread_list->thread_count()); + + MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0); + ASSERT_TRUE(main_thread); + MinidumpContext* context = main_thread->GetContext(); + ASSERT_TRUE(context); + EXPECT_EQ(kExpectedContext, context->GetContextCPU()); + + MinidumpModuleList* module_list = minidump.GetModuleList(); + ASSERT_TRUE(module_list); + const MinidumpModule* main_module = module_list->GetMainModule(); + ASSERT_TRUE(main_module); + EXPECT_EQ(helper_path, main_module->code_file()); +} +#endif + +bool filter_callback(void* context) { + CrashGenerationServerTest* self = + reinterpret_cast(context); + self->filter_callback_called = true; + // veto dump generation + return false; +} + +// Test that a filter callback can veto minidump writing. +TEST_F(CrashGenerationServerTest, testFilter) { + CrashGenerationServer server(mach_port_name, + filter_callback, // filter callback + this, // filter context + dumpCallback, // dump callback + this, // dump context + NULL, // exit callback + NULL, // exit context + true, // generate dumps + temp_dir.path()); // dump path + ASSERT_TRUE(server.Start()); + + pid_t pid = fork(); + ASSERT_NE(-1, pid); + if (pid == 0) { + // Instantiate an OOP exception handler. + ExceptionHandler eh("", NULL, NULL, NULL, true, mach_port_name); + Crasher(); + // not reached + exit(0); + } + + int ret; + ASSERT_EQ(pid, waitpid(pid, &ret, 0)); + EXPECT_FALSE(WIFEXITED(ret)); + EXPECT_TRUE(server.Stop()); + + // check that no minidump was written + EXPECT_TRUE(last_dump_name.empty()); + EXPECT_TRUE(filter_callback_called); +} + +} // namespace diff --git a/shared/sentry/external/breakpad/src/client/mac/tests/exception_handler_test.cc b/shared/sentry/external/breakpad/src/client/mac/tests/exception_handler_test.cc new file mode 100644 index 000000000..50f03f81b --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/tests/exception_handler_test.cc @@ -0,0 +1,714 @@ +// 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. + +// exception_handler_test.cc: Unit tests for google_breakpad::ExceptionHandler + +#include +#include +#include +#include + +#include "breakpad_googletest_includes.h" +#include "client/mac/handler/exception_handler.h" +#include "common/linux/ignore_ret.h" +#include "common/mac/MachIPC.h" +#include "common/tests/auto_tempdir.h" +#include "google_breakpad/processor/minidump.h" + +namespace google_breakpad { +// This acts as the log sink for INFO logging from the processor +// logging code. The logging output confuses XCode and makes it think +// there are unit test failures. testlogging.h handles the overriding. +std::ostringstream info_log; +} + +namespace { +using std::string; +using google_breakpad::AutoTempDir; +using google_breakpad::ExceptionHandler; +using google_breakpad::MachPortSender; +using google_breakpad::MachReceiveMessage; +using google_breakpad::MachSendMessage; +using google_breakpad::Minidump; +using google_breakpad::MinidumpContext; +using google_breakpad::MinidumpException; +using google_breakpad::MinidumpMemoryList; +using google_breakpad::MinidumpMemoryRegion; +using google_breakpad::ReceivePort; +using testing::Test; + +class ExceptionHandlerTest : public Test { + public: + void InProcessCrash(bool aborting); + AutoTempDir tempDir; + string lastDumpName; +}; + +static void Crasher() { + int* a = (int*)0x42; + + fprintf(stdout, "Going to crash...\n"); + fprintf(stdout, "A = %d", *a); +} + +static void AbortCrasher() { + fprintf(stdout, "Going to crash...\n"); + abort(); +} + +static void SoonToCrash(void(*crasher)()) { + crasher(); +} + +static bool MDCallback(const char* dump_dir, const char* file_name, + void* context, bool success) { + string path(dump_dir); + path.append("/"); + path.append(file_name); + path.append(".dmp"); + + int fd = *reinterpret_cast(context); + IGNORE_RET(write(fd, path.c_str(), path.length() + 1)); + close(fd); + exit(0); + // not reached + return true; +} + +void ExceptionHandlerTest::InProcessCrash(bool aborting) { + // Give the child process a pipe to report back on. + int fds[2]; + ASSERT_EQ(0, pipe(fds)); + // Fork off a child process so it can crash. + pid_t pid = fork(); + if (pid == 0) { + // In the child process. + close(fds[0]); + ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL); + // crash + SoonToCrash(aborting ? &AbortCrasher : &Crasher); + // not reached + exit(1); + } + // In the parent process. + ASSERT_NE(-1, pid); + // Wait for the background process to return the minidump file. + close(fds[1]); + char minidump_file[PATH_MAX]; + ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file)); + ASSERT_NE(0, nbytes); + + Minidump minidump(minidump_file); + ASSERT_TRUE(minidump.Read()); + + MinidumpException* exception = minidump.GetException(); + ASSERT_TRUE(exception); + + const MDRawExceptionStream* raw_exception = exception->exception(); + ASSERT_TRUE(raw_exception); + + if (aborting) { + EXPECT_EQ(MD_EXCEPTION_MAC_SOFTWARE, + raw_exception->exception_record.exception_code); + EXPECT_EQ(MD_EXCEPTION_CODE_MAC_ABORT, + raw_exception->exception_record.exception_flags); + } else { + EXPECT_EQ(MD_EXCEPTION_MAC_BAD_ACCESS, + raw_exception->exception_record.exception_code); +#if defined(__x86_64__) + EXPECT_EQ(MD_EXCEPTION_CODE_MAC_INVALID_ADDRESS, + raw_exception->exception_record.exception_flags); +#elif defined(__i386__) + EXPECT_EQ(MD_EXCEPTION_CODE_MAC_PROTECTION_FAILURE, + raw_exception->exception_record.exception_flags); +#endif + } + + const MinidumpContext* context = exception->GetContext(); + ASSERT_TRUE(context); + + uint64_t instruction_pointer; + ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer)); + + // Ideally would like to sanity check that abort() is on the stack + // but that's hard. + MinidumpMemoryList* memory_list = minidump.GetMemoryList(); + ASSERT_TRUE(memory_list); + MinidumpMemoryRegion* region = + memory_list->GetMemoryRegionForAddress(instruction_pointer); + EXPECT_TRUE(region); + + // Child process should have exited with a zero status. + int ret; + ASSERT_EQ(pid, waitpid(pid, &ret, 0)); + EXPECT_NE(0, WIFEXITED(ret)); + EXPECT_EQ(0, WEXITSTATUS(ret)); +} + +TEST_F(ExceptionHandlerTest, InProcess) { + InProcessCrash(false); +} + +TEST_F(ExceptionHandlerTest, InProcessAbort) { + InProcessCrash(true); +} + +static bool DumpNameMDCallback(const char* dump_dir, const char* file_name, + void* context, bool success) { + ExceptionHandlerTest* self = reinterpret_cast(context); + if (dump_dir && file_name) { + self->lastDumpName = dump_dir; + self->lastDumpName += "/"; + self->lastDumpName += file_name; + self->lastDumpName += ".dmp"; + } + return true; +} + +TEST_F(ExceptionHandlerTest, WriteMinidump) { + ExceptionHandler eh(tempDir.path(), NULL, DumpNameMDCallback, this, true, + NULL); + ASSERT_TRUE(eh.WriteMinidump()); + + // Ensure that minidump file exists and is > 0 bytes. + ASSERT_FALSE(lastDumpName.empty()); + struct stat st; + ASSERT_EQ(0, stat(lastDumpName.c_str(), &st)); + ASSERT_LT(0, st.st_size); + + // The minidump should not contain an exception stream. + Minidump minidump(lastDumpName); + ASSERT_TRUE(minidump.Read()); + + MinidumpException* exception = minidump.GetException(); + EXPECT_FALSE(exception); +} + +TEST_F(ExceptionHandlerTest, WriteMinidumpWithException) { + ExceptionHandler eh(tempDir.path(), NULL, DumpNameMDCallback, this, true, + NULL); + ASSERT_TRUE(eh.WriteMinidump(true)); + + // Ensure that minidump file exists and is > 0 bytes. + ASSERT_FALSE(lastDumpName.empty()); + struct stat st; + ASSERT_EQ(0, stat(lastDumpName.c_str(), &st)); + ASSERT_LT(0, st.st_size); + + // The minidump should contain an exception stream. + Minidump minidump(lastDumpName); + ASSERT_TRUE(minidump.Read()); + + MinidumpException* exception = minidump.GetException(); + ASSERT_TRUE(exception); + const MDRawExceptionStream* raw_exception = exception->exception(); + ASSERT_TRUE(raw_exception); + + EXPECT_EQ(MD_EXCEPTION_MAC_BREAKPOINT, + raw_exception->exception_record.exception_code); +} + +TEST_F(ExceptionHandlerTest, DumpChildProcess) { + const int kTimeoutMs = 2000; + // Create a mach port to receive the child task on. + char machPortName[128]; + sprintf(machPortName, "ExceptionHandlerTest.%d", getpid()); + ReceivePort parent_recv_port(machPortName); + + // Give the child process a pipe to block on. + int fds[2]; + ASSERT_EQ(0, pipe(fds)); + + // Fork off a child process to dump. + pid_t pid = fork(); + if (pid == 0) { + // In the child process + close(fds[1]); + + // Send parent process the task and thread ports. + MachSendMessage child_message(0); + child_message.AddDescriptor(mach_task_self()); + child_message.AddDescriptor(mach_thread_self()); + + MachPortSender child_sender(machPortName); + if (child_sender.SendMessage(child_message, kTimeoutMs) != KERN_SUCCESS) + exit(1); + + // Wait for the parent process. + uint8_t data; + read(fds[0], &data, 1); + exit(0); + } + // In the parent process. + ASSERT_NE(-1, pid); + close(fds[0]); + + // Read the child's task and thread ports. + MachReceiveMessage child_message; + ASSERT_EQ(KERN_SUCCESS, + parent_recv_port.WaitForMessage(&child_message, kTimeoutMs)); + mach_port_t child_task = child_message.GetTranslatedPort(0); + mach_port_t child_thread = child_message.GetTranslatedPort(1); + ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_task); + ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_thread); + + // Write a minidump of the child process. + bool result = ExceptionHandler::WriteMinidumpForChild(child_task, + child_thread, + tempDir.path(), + DumpNameMDCallback, + this); + ASSERT_EQ(true, result); + + // Ensure that minidump file exists and is > 0 bytes. + ASSERT_FALSE(lastDumpName.empty()); + struct stat st; + ASSERT_EQ(0, stat(lastDumpName.c_str(), &st)); + ASSERT_LT(0, st.st_size); + + // Unblock child process + uint8_t data = 1; + IGNORE_RET(write(fds[1], &data, 1)); + + // Child process should have exited with a zero status. + int ret; + ASSERT_EQ(pid, waitpid(pid, &ret, 0)); + EXPECT_NE(0, WIFEXITED(ret)); + EXPECT_EQ(0, WEXITSTATUS(ret)); +} + +// Test that memory around the instruction pointer is written +// to the dump as a MinidumpMemoryRegion. +TEST_F(ExceptionHandlerTest, InstructionPointerMemory) { + // Give the child process a pipe to report back on. + int fds[2]; + ASSERT_EQ(0, pipe(fds)); + + // These are defined here so the parent can use them to check the + // data from the minidump afterwards. + const uint32_t kMemorySize = 256; // bytes + const int kOffset = kMemorySize / 2; + // This crashes with SIGILL on x86/x86-64/arm. + const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff }; + + pid_t pid = fork(); + if (pid == 0) { + close(fds[0]); + ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL); + // Get some executable memory. + char* memory = + reinterpret_cast(mmap(NULL, + kMemorySize, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANON, + -1, + 0)); + if (!memory) + exit(0); + + // Write some instructions that will crash. Put them in the middle + // of the block of memory, because the minidump should contain 128 + // bytes on either side of the instruction pointer. + memcpy(memory + kOffset, instructions, sizeof(instructions)); + + // Now execute the instructions, which should crash. + typedef void (*void_function)(void); + void_function memory_function = + reinterpret_cast(memory + kOffset); + memory_function(); + // not reached + exit(1); + } + // In the parent process. + ASSERT_NE(-1, pid); + close(fds[1]); + + // Wait for the background process to return the minidump file. + close(fds[1]); + char minidump_file[PATH_MAX]; + ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file)); + ASSERT_NE(0, nbytes); + // Ensure that minidump file exists and is > 0 bytes. + struct stat st; + ASSERT_EQ(0, stat(minidump_file, &st)); + ASSERT_LT(0, st.st_size); + + // Child process should have exited with a zero status. + int ret; + ASSERT_EQ(pid, waitpid(pid, &ret, 0)); + EXPECT_NE(0, WIFEXITED(ret)); + EXPECT_EQ(0, WEXITSTATUS(ret)); + + // Read the minidump. Locate the exception record and the + // memory list, and then ensure that there is a memory region + // in the memory list that covers the instruction pointer from + // the exception record. + Minidump minidump(minidump_file); + ASSERT_TRUE(minidump.Read()); + + MinidumpException* exception = minidump.GetException(); + MinidumpMemoryList* memory_list = minidump.GetMemoryList(); + ASSERT_TRUE(exception); + ASSERT_TRUE(memory_list); + ASSERT_NE((unsigned int)0, memory_list->region_count()); + + MinidumpContext* context = exception->GetContext(); + ASSERT_TRUE(context); + + uint64_t instruction_pointer; + ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer)); + + MinidumpMemoryRegion* region = + memory_list->GetMemoryRegionForAddress(instruction_pointer); + EXPECT_TRUE(region); + + EXPECT_EQ(kMemorySize, region->GetSize()); + const uint8_t* bytes = region->GetMemory(); + ASSERT_TRUE(bytes); + + uint8_t prefix_bytes[kOffset]; + uint8_t suffix_bytes[kMemorySize - kOffset - sizeof(instructions)]; + memset(prefix_bytes, 0, sizeof(prefix_bytes)); + memset(suffix_bytes, 0, sizeof(suffix_bytes)); + EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0); + EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0); + EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions), + suffix_bytes, sizeof(suffix_bytes)) == 0); +} + +// Test that the memory region around the instruction pointer is +// bounded correctly on the low end. +TEST_F(ExceptionHandlerTest, InstructionPointerMemoryMinBound) { + // Give the child process a pipe to report back on. + int fds[2]; + ASSERT_EQ(0, pipe(fds)); + + // These are defined here so the parent can use them to check the + // data from the minidump afterwards. + const uint32_t kMemorySize = 256; // bytes + const int kOffset = 0; + // This crashes with SIGILL on x86/x86-64/arm. + const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff }; + + pid_t pid = fork(); + if (pid == 0) { + close(fds[0]); + ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL); + // Get some executable memory. + char* memory = + reinterpret_cast(mmap(NULL, + kMemorySize, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANON, + -1, + 0)); + if (!memory) + exit(0); + + // Write some instructions that will crash. Put them at the start + // of the block of memory, to ensure that the memory bounding + // works properly. + memcpy(memory + kOffset, instructions, sizeof(instructions)); + + // Now execute the instructions, which should crash. + typedef void (*void_function)(void); + void_function memory_function = + reinterpret_cast(memory + kOffset); + memory_function(); + // not reached + exit(1); + } + // In the parent process. + ASSERT_NE(-1, pid); + close(fds[1]); + + // Wait for the background process to return the minidump file. + close(fds[1]); + char minidump_file[PATH_MAX]; + ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file)); + ASSERT_NE(0, nbytes); + // Ensure that minidump file exists and is > 0 bytes. + struct stat st; + ASSERT_EQ(0, stat(minidump_file, &st)); + ASSERT_LT(0, st.st_size); + + // Child process should have exited with a zero status. + int ret; + ASSERT_EQ(pid, waitpid(pid, &ret, 0)); + EXPECT_NE(0, WIFEXITED(ret)); + EXPECT_EQ(0, WEXITSTATUS(ret)); + + // Read the minidump. Locate the exception record and the + // memory list, and then ensure that there is a memory region + // in the memory list that covers the instruction pointer from + // the exception record. + Minidump minidump(minidump_file); + ASSERT_TRUE(minidump.Read()); + + MinidumpException* exception = minidump.GetException(); + MinidumpMemoryList* memory_list = minidump.GetMemoryList(); + ASSERT_TRUE(exception); + ASSERT_TRUE(memory_list); + ASSERT_NE((unsigned int)0, memory_list->region_count()); + + MinidumpContext* context = exception->GetContext(); + ASSERT_TRUE(context); + + uint64_t instruction_pointer; + ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer)); + + MinidumpMemoryRegion* region = + memory_list->GetMemoryRegionForAddress(instruction_pointer); + EXPECT_TRUE(region); + + EXPECT_EQ(kMemorySize / 2, region->GetSize()); + const uint8_t* bytes = region->GetMemory(); + ASSERT_TRUE(bytes); + + uint8_t suffix_bytes[kMemorySize / 2 - sizeof(instructions)]; + memset(suffix_bytes, 0, sizeof(suffix_bytes)); + EXPECT_TRUE(memcmp(bytes + kOffset, instructions, sizeof(instructions)) == 0); + EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions), + suffix_bytes, sizeof(suffix_bytes)) == 0); +} + +// Test that the memory region around the instruction pointer is +// bounded correctly on the high end. +TEST_F(ExceptionHandlerTest, InstructionPointerMemoryMaxBound) { + // Give the child process a pipe to report back on. + int fds[2]; + ASSERT_EQ(0, pipe(fds)); + + // These are defined here so the parent can use them to check the + // data from the minidump afterwards. + // Use 4k here because the OS will hand out a single page even + // if a smaller size is requested, and this test wants to + // test the upper bound of the memory range. + const uint32_t kMemorySize = 4096; // bytes + // This crashes with SIGILL on x86/x86-64/arm. + const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff }; + const int kOffset = kMemorySize - sizeof(instructions); + + pid_t pid = fork(); + if (pid == 0) { + close(fds[0]); + ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL); + // Get some executable memory. + char* memory = + reinterpret_cast(mmap(NULL, + kMemorySize, + PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_PRIVATE | MAP_ANON, + -1, + 0)); + if (!memory) + exit(0); + + // Write some instructions that will crash. Put them at the start + // of the block of memory, to ensure that the memory bounding + // works properly. + memcpy(memory + kOffset, instructions, sizeof(instructions)); + + // Now execute the instructions, which should crash. + typedef void (*void_function)(void); + void_function memory_function = + reinterpret_cast(memory + kOffset); + memory_function(); + // not reached + exit(1); + } + // In the parent process. + ASSERT_NE(-1, pid); + close(fds[1]); + + // Wait for the background process to return the minidump file. + close(fds[1]); + char minidump_file[PATH_MAX]; + ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file)); + ASSERT_NE(0, nbytes); + // Ensure that minidump file exists and is > 0 bytes. + struct stat st; + ASSERT_EQ(0, stat(minidump_file, &st)); + ASSERT_LT(0, st.st_size); + + // Child process should have exited with a zero status. + int ret; + ASSERT_EQ(pid, waitpid(pid, &ret, 0)); + EXPECT_NE(0, WIFEXITED(ret)); + EXPECT_EQ(0, WEXITSTATUS(ret)); + + // Read the minidump. Locate the exception record and the + // memory list, and then ensure that there is a memory region + // in the memory list that covers the instruction pointer from + // the exception record. + Minidump minidump(minidump_file); + ASSERT_TRUE(minidump.Read()); + + MinidumpException* exception = minidump.GetException(); + MinidumpMemoryList* memory_list = minidump.GetMemoryList(); + ASSERT_TRUE(exception); + ASSERT_TRUE(memory_list); + ASSERT_NE((unsigned int)0, memory_list->region_count()); + + MinidumpContext* context = exception->GetContext(); + ASSERT_TRUE(context); + + uint64_t instruction_pointer; + ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer)); + + MinidumpMemoryRegion* region = + memory_list->GetMemoryRegionForAddress(instruction_pointer); + EXPECT_TRUE(region); + + const size_t kPrefixSize = 128; // bytes + EXPECT_EQ(kPrefixSize + sizeof(instructions), region->GetSize()); + const uint8_t* bytes = region->GetMemory(); + ASSERT_TRUE(bytes); + + uint8_t prefix_bytes[kPrefixSize]; + memset(prefix_bytes, 0, sizeof(prefix_bytes)); + EXPECT_TRUE(memcmp(bytes, prefix_bytes, sizeof(prefix_bytes)) == 0); + EXPECT_TRUE(memcmp(bytes + kPrefixSize, + instructions, sizeof(instructions)) == 0); +} + +// Ensure that an extra memory block doesn't get added when the +// instruction pointer is not in mapped memory. +TEST_F(ExceptionHandlerTest, InstructionPointerMemoryNullPointer) { + // Give the child process a pipe to report back on. + int fds[2]; + ASSERT_EQ(0, pipe(fds)); + + pid_t pid = fork(); + if (pid == 0) { + close(fds[0]); + ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL); + // Try calling a NULL pointer. + typedef void (*void_function)(void); + // Volatile markings are needed to keep Clang from generating invalid + // opcodes. See http://crbug.com/498354 for details. + volatile void_function memory_function = + reinterpret_cast(NULL); + memory_function(); + // not reached + exit(1); + } + // In the parent process. + ASSERT_NE(-1, pid); + close(fds[1]); + + // Wait for the background process to return the minidump file. + close(fds[1]); + char minidump_file[PATH_MAX]; + ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file)); + ASSERT_NE(0, nbytes); + // Ensure that minidump file exists and is > 0 bytes. + struct stat st; + ASSERT_EQ(0, stat(minidump_file, &st)); + ASSERT_LT(0, st.st_size); + + // Child process should have exited with a zero status. + int ret; + ASSERT_EQ(pid, waitpid(pid, &ret, 0)); + EXPECT_NE(0, WIFEXITED(ret)); + EXPECT_EQ(0, WEXITSTATUS(ret)); + + // Read the minidump. Locate the exception record and the + // memory list, and then ensure that there is only one memory region + // in the memory list (the thread memory from the single thread). + Minidump minidump(minidump_file); + ASSERT_TRUE(minidump.Read()); + + MinidumpException* exception = minidump.GetException(); + MinidumpMemoryList* memory_list = minidump.GetMemoryList(); + ASSERT_TRUE(exception); + ASSERT_TRUE(memory_list); + ASSERT_EQ((unsigned int)1, memory_list->region_count()); +} + +static void* Junk(void*) { + sleep(1000000); + return NULL; +} + +// Test that the memory list gets written correctly when multiple +// threads are running. +TEST_F(ExceptionHandlerTest, MemoryListMultipleThreads) { + // Give the child process a pipe to report back on. + int fds[2]; + ASSERT_EQ(0, pipe(fds)); + + pid_t pid = fork(); + if (pid == 0) { + close(fds[0]); + ExceptionHandler eh(tempDir.path(), NULL, MDCallback, &fds[1], true, NULL); + + // Run an extra thread so >2 memory regions will be written. + pthread_t junk_thread; + if (pthread_create(&junk_thread, NULL, Junk, NULL) == 0) + pthread_detach(junk_thread); + + // Just crash. + Crasher(); + + // not reached + exit(1); + } + // In the parent process. + ASSERT_NE(-1, pid); + close(fds[1]); + + // Wait for the background process to return the minidump file. + close(fds[1]); + char minidump_file[PATH_MAX]; + ssize_t nbytes = read(fds[0], minidump_file, sizeof(minidump_file)); + ASSERT_NE(0, nbytes); + // Ensure that minidump file exists and is > 0 bytes. + struct stat st; + ASSERT_EQ(0, stat(minidump_file, &st)); + ASSERT_LT(0, st.st_size); + + // Child process should have exited with a zero status. + int ret; + ASSERT_EQ(pid, waitpid(pid, &ret, 0)); + EXPECT_NE(0, WIFEXITED(ret)); + EXPECT_EQ(0, WEXITSTATUS(ret)); + + // Read the minidump, and verify that the memory list can be read. + Minidump minidump(minidump_file); + ASSERT_TRUE(minidump.Read()); + + MinidumpMemoryList* memory_list = minidump.GetMemoryList(); + ASSERT_TRUE(memory_list); + // Verify that there are three memory regions: + // one per thread, and one for the instruction pointer memory. + ASSERT_EQ((unsigned int)3, memory_list->region_count()); +} + +} diff --git a/shared/sentry/external/breakpad/src/client/mac/tests/minidump_generator_test.cc b/shared/sentry/external/breakpad/src/client/mac/tests/minidump_generator_test.cc new file mode 100644 index 000000000..1f374657a --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/tests/minidump_generator_test.cc @@ -0,0 +1,320 @@ +// 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. + +// minidump_generator_test.cc: Unit tests for google_breakpad::MinidumpGenerator + +#include +#ifndef MAC_OS_X_VERSION_10_6 +#define MAC_OS_X_VERSION_10_6 1060 +#endif +#include +#include + +#include +#include + +#include "breakpad_googletest_includes.h" +#include "client/mac/handler/minidump_generator.h" +#include "client/mac/tests/spawn_child_process.h" +#include "common/linux/ignore_ret.h" +#include "common/mac/MachIPC.h" +#include "common/tests/auto_tempdir.h" +#include "google_breakpad/processor/minidump.h" + +namespace google_breakpad { +// This acts as the log sink for INFO logging from the processor +// logging code. The logging output confuses XCode and makes it think +// there are unit test failures. testlogging.h handles the overriding. +std::ostringstream info_log; +} + +namespace { +using std::string; +using std::vector; +using google_breakpad::AutoTempDir; +using google_breakpad::MinidumpGenerator; +using google_breakpad::MachPortSender; +using google_breakpad::MachReceiveMessage; +using google_breakpad::MachSendMessage; +using google_breakpad::Minidump; +using google_breakpad::MinidumpContext; +using google_breakpad::MinidumpException; +using google_breakpad::MinidumpModule; +using google_breakpad::MinidumpModuleList; +using google_breakpad::MinidumpSystemInfo; +using google_breakpad::MinidumpThread; +using google_breakpad::MinidumpThreadList; +using google_breakpad::ReceivePort; +using testing::Test; +using namespace google_breakpad_test; + +class MinidumpGeneratorTest : public Test { + public: + AutoTempDir tempDir; +}; + +static void* Junk(void* data) { + bool* wait = reinterpret_cast(data); + while (!*wait) { + usleep(10000); + } + return NULL; +} + +TEST_F(MinidumpGeneratorTest, InProcess) { + MinidumpGenerator generator; + string dump_filename = + MinidumpGenerator::UniqueNameInDirectory(tempDir.path(), NULL); + + // Run an extra thread since MinidumpGenerator assumes there + // are 2 or more threads. + pthread_t junk_thread; + bool quit = false; + ASSERT_EQ(0, pthread_create(&junk_thread, NULL, Junk, &quit)); + + ASSERT_TRUE(generator.Write(dump_filename.c_str())); + // Ensure that minidump file exists and is > 0 bytes. + struct stat st; + ASSERT_EQ(0, stat(dump_filename.c_str(), &st)); + ASSERT_LT(0, st.st_size); + + // join the background thread + quit = true; + pthread_join(junk_thread, NULL); + + // Read the minidump, sanity check some data. + Minidump minidump(dump_filename.c_str()); + ASSERT_TRUE(minidump.Read()); + + MinidumpSystemInfo* system_info = minidump.GetSystemInfo(); + ASSERT_TRUE(system_info); + const MDRawSystemInfo* raw_info = system_info->system_info(); + ASSERT_TRUE(raw_info); + EXPECT_EQ(kNativeArchitecture, raw_info->processor_architecture); + + MinidumpThreadList* thread_list = minidump.GetThreadList(); + ASSERT_TRUE(thread_list); + ASSERT_EQ((unsigned int)1, thread_list->thread_count()); + + MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0); + ASSERT_TRUE(main_thread); + MinidumpContext* context = main_thread->GetContext(); + ASSERT_TRUE(context); + EXPECT_EQ(kNativeContext, context->GetContextCPU()); + + MinidumpModuleList* module_list = minidump.GetModuleList(); + ASSERT_TRUE(module_list); + const MinidumpModule* main_module = module_list->GetMainModule(); + ASSERT_TRUE(main_module); + EXPECT_EQ(GetExecutablePath(), main_module->code_file()); +} + +TEST_F(MinidumpGeneratorTest, OutOfProcess) { + const int kTimeoutMs = 2000; + // Create a mach port to receive the child task on. + char machPortName[128]; + sprintf(machPortName, "MinidumpGeneratorTest.OutOfProcess.%d", getpid()); + ReceivePort parent_recv_port(machPortName); + + // Give the child process a pipe to block on. + int fds[2]; + ASSERT_EQ(0, pipe(fds)); + + // Fork off a child process to dump. + pid_t pid = fork(); + if (pid == 0) { + // In the child process + close(fds[1]); + + // Send parent process the task port. + MachSendMessage child_message(0); + child_message.AddDescriptor(mach_task_self()); + + MachPortSender child_sender(machPortName); + if (child_sender.SendMessage(child_message, kTimeoutMs) != KERN_SUCCESS) { + fprintf(stderr, "Error sending message from child process!\n"); + exit(1); + } + + // Wait for the parent process. + uint8_t data; + read(fds[0], &data, 1); + exit(0); + } + // In the parent process. + ASSERT_NE(-1, pid); + close(fds[0]); + + // Read the child's task port. + MachReceiveMessage child_message; + ASSERT_EQ(KERN_SUCCESS, + parent_recv_port.WaitForMessage(&child_message, kTimeoutMs)); + mach_port_t child_task = child_message.GetTranslatedPort(0); + ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_task); + + // Write a minidump of the child process. + MinidumpGenerator generator(child_task, MACH_PORT_NULL); + string dump_filename = + MinidumpGenerator::UniqueNameInDirectory(tempDir.path(), NULL); + ASSERT_TRUE(generator.Write(dump_filename.c_str())); + + // Ensure that minidump file exists and is > 0 bytes. + struct stat st; + ASSERT_EQ(0, stat(dump_filename.c_str(), &st)); + ASSERT_LT(0, st.st_size); + + // Unblock child process + uint8_t data = 1; + IGNORE_RET(write(fds[1], &data, 1)); + + // Child process should have exited with a zero status. + int ret; + ASSERT_EQ(pid, waitpid(pid, &ret, 0)); + EXPECT_NE(0, WIFEXITED(ret)); + EXPECT_EQ(0, WEXITSTATUS(ret)); + + // Read the minidump, sanity check some data. + Minidump minidump(dump_filename.c_str()); + ASSERT_TRUE(minidump.Read()); + + MinidumpSystemInfo* system_info = minidump.GetSystemInfo(); + ASSERT_TRUE(system_info); + const MDRawSystemInfo* raw_info = system_info->system_info(); + ASSERT_TRUE(raw_info); + EXPECT_EQ(kNativeArchitecture, raw_info->processor_architecture); + + MinidumpThreadList* thread_list = minidump.GetThreadList(); + ASSERT_TRUE(thread_list); + ASSERT_EQ((unsigned int)1, thread_list->thread_count()); + + MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0); + ASSERT_TRUE(main_thread); + MinidumpContext* context = main_thread->GetContext(); + ASSERT_TRUE(context); + EXPECT_EQ(kNativeContext, context->GetContextCPU()); + + MinidumpModuleList* module_list = minidump.GetModuleList(); + ASSERT_TRUE(module_list); + const MinidumpModule* main_module = module_list->GetMainModule(); + ASSERT_TRUE(main_module); + EXPECT_EQ(GetExecutablePath(), main_module->code_file()); +} + +// This test fails on 10.5, but I don't have easy access to a 10.5 machine, +// so it's simpler to just limit it to 10.6 for now. +#if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6) && \ + (defined(__x86_64__) || defined(__i386__)) + +TEST_F(MinidumpGeneratorTest, CrossArchitectureDump) { + const int kTimeoutMs = 5000; + // Create a mach port to receive the child task on. + char machPortName[128]; + sprintf(machPortName, + "MinidumpGeneratorTest.CrossArchitectureDump.%d", getpid()); + + ReceivePort parent_recv_port(machPortName); + + // Spawn a child process to dump. + string helper_path = GetHelperPath(); + const char* argv[] = { + helper_path.c_str(), + machPortName, + NULL + }; + pid_t pid = spawn_child_process(argv); + ASSERT_NE(-1, pid); + + // Read the child's task port. + MachReceiveMessage child_message; + ASSERT_EQ(KERN_SUCCESS, + parent_recv_port.WaitForMessage(&child_message, kTimeoutMs)); + mach_port_t child_task = child_message.GetTranslatedPort(0); + ASSERT_NE((mach_port_t)MACH_PORT_NULL, child_task); + + // Write a minidump of the child process. + MinidumpGenerator generator(child_task, MACH_PORT_NULL); + string dump_filename = + MinidumpGenerator::UniqueNameInDirectory(tempDir.path(), NULL); + ASSERT_TRUE(generator.Write(dump_filename.c_str())); + + // Ensure that minidump file exists and is > 0 bytes. + struct stat st; + ASSERT_EQ(0, stat(dump_filename.c_str(), &st)); + ASSERT_LT(0, st.st_size); + + // Kill child process. + kill(pid, SIGKILL); + + int ret; + ASSERT_EQ(pid, waitpid(pid, &ret, 0)); + +const MDCPUArchitecture kExpectedArchitecture = +#if defined(__x86_64__) + MD_CPU_ARCHITECTURE_X86 +#elif defined(__i386__) + MD_CPU_ARCHITECTURE_AMD64 +#endif + ; +const uint32_t kExpectedContext = +#if defined(__i386__) + MD_CONTEXT_AMD64 +#elif defined(__x86_64__) + MD_CONTEXT_X86 +#endif + ; + + // Read the minidump, sanity check some data. + Minidump minidump(dump_filename.c_str()); + ASSERT_TRUE(minidump.Read()); + + MinidumpSystemInfo* system_info = minidump.GetSystemInfo(); + ASSERT_TRUE(system_info); + const MDRawSystemInfo* raw_info = system_info->system_info(); + ASSERT_TRUE(raw_info); + EXPECT_EQ(kExpectedArchitecture, raw_info->processor_architecture); + + MinidumpThreadList* thread_list = minidump.GetThreadList(); + ASSERT_TRUE(thread_list); + ASSERT_EQ((unsigned int)1, thread_list->thread_count()); + + MinidumpThread* main_thread = thread_list->GetThreadAtIndex(0); + ASSERT_TRUE(main_thread); + MinidumpContext* context = main_thread->GetContext(); + ASSERT_TRUE(context); + EXPECT_EQ(kExpectedContext, context->GetContextCPU()); + + MinidumpModuleList* module_list = minidump.GetModuleList(); + ASSERT_TRUE(module_list); + const MinidumpModule* main_module = module_list->GetMainModule(); + ASSERT_TRUE(main_module); + EXPECT_EQ(helper_path, main_module->code_file()); +} +#endif // 10.6 && (x86-64 || i386) + +} diff --git a/shared/sentry/external/breakpad/src/client/mac/tests/minidump_generator_test_helper.cc b/shared/sentry/external/breakpad/src/client/mac/tests/minidump_generator_test_helper.cc new file mode 100644 index 000000000..4e8ce3cf0 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/tests/minidump_generator_test_helper.cc @@ -0,0 +1,74 @@ +// 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. + +// minidump_generator_test_helper.cc: A helper program that +// minidump_generator_test.cc can launch to test certain things +// that require a separate executable. + +#include + +#include "client/mac/handler/exception_handler.h" +#include "common/mac/MachIPC.h" + +using google_breakpad::MachPortSender; +using google_breakpad::MachReceiveMessage; +using google_breakpad::MachSendMessage; +using google_breakpad::ReceivePort; + +int main(int argc, char** argv) { + if (argc < 2) + return 1; + + if (strcmp(argv[1], "crash") != 0) { + const int kTimeoutMs = 2000; + // Send parent process the task and thread ports. + MachSendMessage child_message(0); + child_message.AddDescriptor(mach_task_self()); + child_message.AddDescriptor(mach_thread_self()); + + MachPortSender child_sender(argv[1]); + if (child_sender.SendMessage(child_message, kTimeoutMs) != KERN_SUCCESS) { + fprintf(stderr, "Error sending message from child process!\n"); + exit(1); + } + + // Loop forever. + while (true) { + sleep(100); + } + } else if (argc == 3 && strcmp(argv[1], "crash") == 0) { + // Instantiate an OOP exception handler + google_breakpad::ExceptionHandler eh("", NULL, NULL, NULL, true, argv[2]); + // and crash. + int *a = (int*)0x42; + *a = 1; + } + + return 0; +} diff --git a/shared/sentry/external/breakpad/src/client/mac/tests/spawn_child_process.h b/shared/sentry/external/breakpad/src/client/mac/tests/spawn_child_process.h new file mode 100644 index 000000000..e52ff6b65 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/tests/spawn_child_process.h @@ -0,0 +1,149 @@ +// 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. + +// Utility functions for spawning a helper process using a different +// CPU architecture. + +#ifndef GOOGLE_BREAKPAD_CLIENT_MAC_TESTS_SPAWN_CHILD_PROCESS +#define GOOGLE_BREAKPAD_CLIENT_MAC_TESTS_SPAWN_CHILD_PROCESS + +#include +#ifndef MAC_OS_X_VERSION_10_6 +#define MAC_OS_X_VERSION_10_6 1060 +#endif +#include +#include +#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6 +#include +#endif + +#include +#include + +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad_test { + +using std::string; +using std::vector; + +const MDCPUArchitecture kNativeArchitecture = +#if defined(__i386__) + MD_CPU_ARCHITECTURE_X86 +#elif defined(__x86_64__) + MD_CPU_ARCHITECTURE_AMD64 +#elif defined(__ppc__) || defined(__ppc64__) + MD_CPU_ARCHITECTURE_PPC +#else +#error "This file has not been ported to this CPU architecture." +#endif + ; + +const uint32_t kNativeContext = +#if defined(__i386__) + MD_CONTEXT_X86 +#elif defined(__x86_64__) + MD_CONTEXT_AMD64 +#elif defined(__ppc__) || defined(__ppc64__) + MD_CONTEXT_PPC +#else +#error "This file has not been ported to this CPU architecture." +#endif + ; + +string GetExecutablePath() { + char self_path[PATH_MAX]; + uint32_t size = sizeof(self_path); + if (_NSGetExecutablePath(self_path, &size) != 0) + return ""; + return self_path; +} + +string GetHelperPath() { + string helper_path(GetExecutablePath()); + size_t pos = helper_path.rfind('/'); + if (pos == string::npos) + return ""; + + helper_path.erase(pos + 1); + helper_path += "minidump_generator_test_helper"; + return helper_path; +} + +#if MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6 + +pid_t spawn_child_process(const char** argv) { + posix_spawnattr_t spawnattr; + if (posix_spawnattr_init(&spawnattr) != 0) + return (pid_t)-1; + + cpu_type_t pref_cpu_types[2] = { +#if defined(__x86_64__) + CPU_TYPE_X86, +#elif defined(__i386__) + CPU_TYPE_X86_64, +#endif + CPU_TYPE_ANY + }; + + // Set spawn attributes. + size_t attr_count = sizeof(pref_cpu_types) / sizeof(pref_cpu_types[0]); + size_t attr_ocount = 0; + if (posix_spawnattr_setbinpref_np(&spawnattr, + attr_count, + pref_cpu_types, + &attr_ocount) != 0 || + attr_ocount != attr_count) { + posix_spawnattr_destroy(&spawnattr); + return (pid_t)-1; + } + + // Create an argv array. + vector argv_v; + while (*argv) { + argv_v.push_back(strdup(*argv)); + argv++; + } + argv_v.push_back(NULL); + pid_t new_pid = 0; + int result = posix_spawnp(&new_pid, argv_v[0], NULL, &spawnattr, + &argv_v[0], *_NSGetEnviron()); + posix_spawnattr_destroy(&spawnattr); + + for (unsigned i = 0; i < argv_v.size(); i++) { + free(argv_v[i]); + } + + return result == 0 ? new_pid : -1; +} +#endif + +} // namespace google_breakpad_test + +#endif // GOOGLE_BREAKPAD_CLIENT_MAC_TESTS_SPAWN_CHILD_PROCESS diff --git a/shared/sentry/external/breakpad/src/client/mac/tests/testlogging.h b/shared/sentry/external/breakpad/src/client/mac/tests/testlogging.h new file mode 100644 index 000000000..c6b6be699 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/mac/tests/testlogging.h @@ -0,0 +1,9 @@ +// This file exists to override the processor logging for unit tests, +// since it confuses XCode into thinking unit tests have failed. +#include + +namespace google_breakpad { +extern std::ostringstream info_log; +} + +#define BPLOG_INFO_STREAM google_breakpad::info_log diff --git a/shared/sentry/external/breakpad/src/client/minidump_file_writer-inl.h b/shared/sentry/external/breakpad/src/client/minidump_file_writer-inl.h new file mode 100644 index 000000000..bdac2dae8 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/minidump_file_writer-inl.h @@ -0,0 +1,97 @@ +// 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. + +// minidump_file_writer-inl.h: Minidump file writer implementation. +// +// See minidump_file_writer.h for documentation. + +#ifndef CLIENT_MINIDUMP_FILE_WRITER_INL_H__ +#define CLIENT_MINIDUMP_FILE_WRITER_INL_H__ + +#include + +#include "client/minidump_file_writer.h" +#include "google_breakpad/common/minidump_size.h" + +namespace google_breakpad { + +template +inline bool TypedMDRVA::Allocate() { + allocation_state_ = SINGLE_OBJECT; + return UntypedMDRVA::Allocate(minidump_size::size()); +} + +template +inline bool TypedMDRVA::Allocate(size_t additional) { + allocation_state_ = SINGLE_OBJECT; + return UntypedMDRVA::Allocate(minidump_size::size() + additional); +} + +template +inline bool TypedMDRVA::AllocateArray(size_t count) { + assert(count); + allocation_state_ = ARRAY; + return UntypedMDRVA::Allocate(minidump_size::size() * count); +} + +template +inline bool TypedMDRVA::AllocateObjectAndArray(size_t count, + size_t length) { + assert(count && length); + allocation_state_ = SINGLE_OBJECT_WITH_ARRAY; + return UntypedMDRVA::Allocate(minidump_size::size() + count * length); +} + +template +inline bool TypedMDRVA::CopyIndex(unsigned int index, MDType* item) { + assert(allocation_state_ == ARRAY); + return writer_->Copy( + static_cast(position_ + index * minidump_size::size()), + item, minidump_size::size()); +} + +template +inline bool TypedMDRVA::CopyIndexAfterObject(unsigned int index, + const void* src, + size_t length) { + assert(allocation_state_ == SINGLE_OBJECT_WITH_ARRAY); + return writer_->Copy( + static_cast(position_ + minidump_size::size() + + index * length), + src, length); +} + +template +inline bool TypedMDRVA::Flush() { + return writer_->Copy(position_, &data_, minidump_size::size()); +} + +} // namespace google_breakpad + +#endif // CLIENT_MINIDUMP_FILE_WRITER_INL_H__ diff --git a/shared/sentry/external/breakpad/src/client/minidump_file_writer.cc b/shared/sentry/external/breakpad/src/client/minidump_file_writer.cc new file mode 100644 index 000000000..5c3c5cbb0 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/minidump_file_writer.cc @@ -0,0 +1,350 @@ +// 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. + +// minidump_file_writer.cc: Minidump file writer implementation. +// +// See minidump_file_writer.h for documentation. + +#include +#include +#include +#include +#include + +#include "client/minidump_file_writer-inl.h" +#include "common/linux/linux_libc_support.h" +#include "common/string_conversion.h" +#if defined(__linux__) && __linux__ +#include "third_party/lss/linux_syscall_support.h" +#endif + +#if defined(__ANDROID__) +#include + +namespace { + +bool g_need_ftruncate_workaround = false; +bool g_checked_need_ftruncate_workaround = false; + +void CheckNeedsFTruncateWorkAround(int file) { + if (g_checked_need_ftruncate_workaround) { + return; + } + g_checked_need_ftruncate_workaround = true; + + // Attempt an idempotent truncate that chops off nothing and see if we + // run into any sort of errors. + off_t offset = sys_lseek(file, 0, SEEK_END); + if (offset == -1) { + // lseek failed. Don't apply work around. It's unlikely that we can write + // to a minidump with either method. + return; + } + + int result = ftruncate(file, offset); + if (result == -1 && errno == EACCES) { + // It very likely that we are running into the kernel bug in M devices. + // We are going to deploy the workaround for writing minidump files + // without uses of ftruncate(). This workaround should be fine even + // for kernels without the bug. + // See http://crbug.com/542840 for more details. + g_need_ftruncate_workaround = true; + } +} + +bool NeedsFTruncateWorkAround() { + return g_need_ftruncate_workaround; +} + +} // namespace +#endif // defined(__ANDROID__) + +namespace google_breakpad { + +const MDRVA MinidumpFileWriter::kInvalidMDRVA = static_cast(-1); + +MinidumpFileWriter::MinidumpFileWriter() + : file_(-1), + close_file_when_destroyed_(true), + position_(0), + size_(0) { +} + +MinidumpFileWriter::~MinidumpFileWriter() { + if (close_file_when_destroyed_) + Close(); +} + +bool MinidumpFileWriter::Open(const char* path) { + assert(file_ == -1); +#if defined(__linux__) && __linux__ + file_ = sys_open(path, O_WRONLY | O_CREAT | O_EXCL, 0600); +#else + file_ = open(path, O_WRONLY | O_CREAT | O_EXCL, 0600); +#endif + + return file_ != -1; +} + +void MinidumpFileWriter::SetFile(const int file) { + assert(file_ == -1); + file_ = file; + close_file_when_destroyed_ = false; +#if defined(__ANDROID__) + CheckNeedsFTruncateWorkAround(file); +#endif +} + +bool MinidumpFileWriter::Close() { + bool result = true; + + if (file_ != -1) { +#if defined(__ANDROID__) + if (!NeedsFTruncateWorkAround() && ftruncate(file_, position_)) { + return false; + } +#else + if (ftruncate(file_, position_)) { + return false; + } +#endif +#if defined(__linux__) && __linux__ + result = (sys_close(file_) == 0); +#else + result = (close(file_) == 0); +#endif + file_ = -1; + } + + return result; +} + +bool MinidumpFileWriter::CopyStringToMDString(const wchar_t* str, + unsigned int length, + TypedMDRVA* mdstring) { + bool result = true; + if (sizeof(wchar_t) == sizeof(uint16_t)) { + // Shortcut if wchar_t is the same size as MDString's buffer + result = mdstring->Copy(str, mdstring->get()->length); + } else { + uint16_t out[2]; + int out_idx = 0; + + // Copy the string character by character + while (length && result) { + UTF32ToUTF16Char(*str, out); + if (!out[0]) + return false; + + // Process one character at a time + --length; + ++str; + + // Append the one or two UTF-16 characters. The first one will be non- + // zero, but the second one may be zero, depending on the conversion from + // UTF-32. + int out_count = out[1] ? 2 : 1; + size_t out_size = sizeof(uint16_t) * out_count; + result = mdstring->CopyIndexAfterObject(out_idx, out, out_size); + out_idx += out_count; + } + } + return result; +} + +bool MinidumpFileWriter::CopyStringToMDString(const char* str, + unsigned int length, + TypedMDRVA* mdstring) { + bool result = true; + uint16_t out[2]; + int out_idx = 0; + + // Copy the string character by character + while (length && result) { + int conversion_count = UTF8ToUTF16Char(str, length, out); + if (!conversion_count) + return false; + + // Move the pointer along based on the nubmer of converted characters + length -= conversion_count; + str += conversion_count; + + // Append the one or two UTF-16 characters + int out_count = out[1] ? 2 : 1; + size_t out_size = sizeof(uint16_t) * out_count; + result = mdstring->CopyIndexAfterObject(out_idx, out, out_size); + out_idx += out_count; + } + return result; +} + +template +bool MinidumpFileWriter::WriteStringCore(const CharType* str, + unsigned int length, + MDLocationDescriptor* location) { + assert(str); + assert(location); + // Calculate the mdstring length by either limiting to |length| as passed in + // or by finding the location of the NULL character. + unsigned int mdstring_length = 0; + if (!length) + length = INT_MAX; + for (; mdstring_length < length && str[mdstring_length]; ++mdstring_length) + ; + + // Allocate the string buffer + TypedMDRVA mdstring(this); + if (!mdstring.AllocateObjectAndArray(mdstring_length + 1, sizeof(uint16_t))) + return false; + + // Set length excluding the NULL and copy the string + mdstring.get()->length = + static_cast(mdstring_length * sizeof(uint16_t)); + bool result = CopyStringToMDString(str, mdstring_length, &mdstring); + + // NULL terminate + if (result) { + uint16_t ch = 0; + result = mdstring.CopyIndexAfterObject(mdstring_length, &ch, sizeof(ch)); + + if (result) + *location = mdstring.location(); + } + + return result; +} + +bool MinidumpFileWriter::WriteString(const wchar_t* str, unsigned int length, + MDLocationDescriptor* location) { + return WriteStringCore(str, length, location); +} + +bool MinidumpFileWriter::WriteString(const char* str, unsigned int length, + MDLocationDescriptor* location) { + return WriteStringCore(str, length, location); +} + +bool MinidumpFileWriter::WriteMemory(const void* src, size_t size, + MDMemoryDescriptor* output) { + assert(src); + assert(output); + UntypedMDRVA mem(this); + + if (!mem.Allocate(size)) + return false; + if (!mem.Copy(src, mem.size())) + return false; + + output->start_of_memory_range = reinterpret_cast(src); + output->memory = mem.location(); + + return true; +} + +MDRVA MinidumpFileWriter::Allocate(size_t size) { + assert(size); + assert(file_ != -1); +#if defined(__ANDROID__) + if (NeedsFTruncateWorkAround()) { + // If ftruncate() is not available. We simply increase the size beyond the + // current file size. sys_write() will expand the file when data is written + // to it. Because we did not over allocate to fit memory pages, we also + // do not need to ftruncate() the file once we are done. + size_ += size; + + // We don't need to seek since the file is unchanged. + MDRVA current_position = position_; + position_ += static_cast(size); + return current_position; + } +#endif + size_t aligned_size = (size + 7) & ~7; // 64-bit alignment + + if (position_ + aligned_size > size_) { + size_t growth = aligned_size; + size_t minimal_growth = getpagesize(); + + // Ensure that the file grows by at least the size of a memory page + if (growth < minimal_growth) + growth = minimal_growth; + + size_t new_size = size_ + growth; + if (ftruncate(file_, new_size) != 0) + return kInvalidMDRVA; + + size_ = new_size; + } + + MDRVA current_position = position_; + position_ += static_cast(aligned_size); + + return current_position; +} + +bool MinidumpFileWriter::Copy(MDRVA position, const void* src, ssize_t size) { + assert(src); + assert(size); + assert(file_ != -1); + + // Ensure that the data will fit in the allocated space + if (static_cast(size + position) > size_) + return false; + + // Seek and write the data +#if defined(__linux__) && __linux__ + if (sys_lseek(file_, position, SEEK_SET) == static_cast(position)) { + if (sys_write(file_, src, size) == size) { + return true; + } + } +#else + if (lseek(file_, position, SEEK_SET) == static_cast(position)) { + if (write(file_, src, size) == size) { + return true; + } + } +#endif + return false; +} + +bool UntypedMDRVA::Allocate(size_t size) { + assert(size_ == 0); + size_ = size; + position_ = writer_->Allocate(size_); + return position_ != MinidumpFileWriter::kInvalidMDRVA; +} + +bool UntypedMDRVA::Copy(MDRVA pos, const void* src, size_t size) { + assert(src); + assert(size); + assert(pos + size <= position_ + size_); + return writer_->Copy(pos, src, size); +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/minidump_file_writer.h b/shared/sentry/external/breakpad/src/client/minidump_file_writer.h new file mode 100644 index 000000000..c66dc5912 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/minidump_file_writer.h @@ -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. + +// minidump_file_writer.h: Implements file-based minidump generation. It's +// intended to be used with the Google Breakpad open source crash handling +// project. + +#ifndef CLIENT_MINIDUMP_FILE_WRITER_H__ +#define CLIENT_MINIDUMP_FILE_WRITER_H__ + +#include + +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +class UntypedMDRVA; +template class TypedMDRVA; + +// The user of this class can Open() a file and add minidump streams, data, and +// strings using the definitions in minidump_format.h. Since this class is +// expected to be used in a situation where the current process may be +// damaged, it will not allocate heap memory. +// Sample usage: +// MinidumpFileWriter writer; +// writer.Open("/tmp/minidump.dmp"); +// TypedMDRVA header(&writer_); +// header.Allocate(); +// header->get()->signature = MD_HEADER_SIGNATURE; +// : +// writer.Close(); +// +// An alternative is to use SetFile and provide a file descriptor: +// MinidumpFileWriter writer; +// writer.SetFile(minidump_fd); +// TypedMDRVA header(&writer_); +// header.Allocate(); +// header->get()->signature = MD_HEADER_SIGNATURE; +// : +// writer.Close(); + +class MinidumpFileWriter { +public: + // Invalid MDRVA (Minidump Relative Virtual Address) + // returned on failed allocation + static const MDRVA kInvalidMDRVA; + + MinidumpFileWriter(); + ~MinidumpFileWriter(); + + // Open |path| as the destination of the minidump data. If |path| already + // exists, then Open() will fail. + // Return true on success, or false on failure. + bool Open(const char* path); + + // Sets the file descriptor |file| as the destination of the minidump data. + // Can be used as an alternative to Open() when a file descriptor is + // available. + // Note that |fd| is not closed when the instance of MinidumpFileWriter is + // destroyed. + void SetFile(const int file); + + // Close the current file (that was either created when Open was called, or + // specified with SetFile). + // Return true on success, or false on failure. + bool Close(); + + // Copy the contents of |str| to a MDString and write it to the file. + // |str| is expected to be either UTF-16 or UTF-32 depending on the size + // of wchar_t. + // Maximum |length| of characters to copy from |str|, or specify 0 to use the + // entire NULL terminated string. Copying will stop at the first NULL. + // |location| the allocated location + // Return true on success, or false on failure + bool WriteString(const wchar_t* str, unsigned int length, + MDLocationDescriptor* location); + + // Same as above, except with |str| as a UTF-8 string + bool WriteString(const char* str, unsigned int length, + MDLocationDescriptor* location); + + // Write |size| bytes starting at |src| into the current position. + // Return true on success and set |output| to position, or false on failure + bool WriteMemory(const void* src, size_t size, MDMemoryDescriptor* output); + + // Copies |size| bytes from |src| to |position| + // Return true on success, or false on failure + bool Copy(MDRVA position, const void* src, ssize_t size); + + // Return the current position for writing to the minidump + inline MDRVA position() const { return position_; } + + private: + friend class UntypedMDRVA; + + // Allocates an area of |size| bytes. + // Returns the position of the allocation, or kInvalidMDRVA if it was + // unable to allocate the bytes. + MDRVA Allocate(size_t size); + + // The file descriptor for the output file. + int file_; + + // Whether |file_| should be closed when the instance is destroyed. + bool close_file_when_destroyed_; + + // Current position in buffer + MDRVA position_; + + // Current allocated size + size_t size_; + + // Copy |length| characters from |str| to |mdstring|. These are distinct + // because the underlying MDString is a UTF-16 based string. The wchar_t + // variant may need to create a MDString that has more characters than the + // source |str|, whereas the UTF-8 variant may coalesce characters to form + // a single UTF-16 character. + bool CopyStringToMDString(const wchar_t* str, unsigned int length, + TypedMDRVA* mdstring); + bool CopyStringToMDString(const char* str, unsigned int length, + TypedMDRVA* mdstring); + + // The common templated code for writing a string + template + bool WriteStringCore(const CharType* str, unsigned int length, + MDLocationDescriptor* location); +}; + +// Represents an untyped allocated chunk +class UntypedMDRVA { + public: + explicit UntypedMDRVA(MinidumpFileWriter* writer) + : writer_(writer), + position_(writer->position()), + size_(0) {} + + // Allocates |size| bytes. Must not call more than once. + // Return true on success, or false on failure + bool Allocate(size_t size); + + // Returns the current position or kInvalidMDRVA if allocation failed + inline MDRVA position() const { return position_; } + + // Number of bytes allocated + inline size_t size() const { return size_; } + + // Return size and position + inline MDLocationDescriptor location() const { + MDLocationDescriptor location = { static_cast(size_), + position_ }; + return location; + } + + // Copy |size| bytes starting at |src| into the minidump at |position| + // Return true on success, or false on failure + bool Copy(MDRVA position, const void* src, size_t size); + + // Copy |size| bytes from |src| to the current position + inline bool Copy(const void* src, size_t size) { + return Copy(position_, src, size); + } + + protected: + // Writer we associate with + MinidumpFileWriter* writer_; + + // Position of the start of the data + MDRVA position_; + + // Allocated size + size_t size_; +}; + +// Represents a Minidump object chunk. Additional memory can be allocated at +// the end of the object as a: +// - single allocation +// - Array of MDType objects +// - A MDType object followed by an array +template +class TypedMDRVA : public UntypedMDRVA { + public: + // Constructs an unallocated MDRVA + explicit TypedMDRVA(MinidumpFileWriter* writer) + : UntypedMDRVA(writer), + data_(), + allocation_state_(UNALLOCATED) {} + + inline ~TypedMDRVA() { + // Ensure that the data_ object is written out + if (allocation_state_ != ARRAY) + Flush(); + } + + // Address of object data_ of MDType. This is not declared const as the + // typical usage will be to access the underlying |data_| object as to + // alter its contents. + MDType* get() { return &data_; } + + // Allocates minidump_size::size() bytes. + // Must not call more than once. + // Return true on success, or false on failure + bool Allocate(); + + // Allocates minidump_size::size() + |additional| bytes. + // Must not call more than once. + // Return true on success, or false on failure + bool Allocate(size_t additional); + + // Allocate an array of |count| elements of MDType. + // Must not call more than once. + // Return true on success, or false on failure + bool AllocateArray(size_t count); + + // Allocate an array of |count| elements of |size| after object of MDType + // Must not call more than once. + // Return true on success, or false on failure + bool AllocateObjectAndArray(size_t count, size_t size); + + // Copy |item| to |index| + // Must have been allocated using AllocateArray(). + // Return true on success, or false on failure + bool CopyIndex(unsigned int index, MDType* item); + + // Copy |size| bytes starting at |str| to |index| + // Must have been allocated using AllocateObjectAndArray(). + // Return true on success, or false on failure + bool CopyIndexAfterObject(unsigned int index, const void* src, size_t size); + + // Write data_ + bool Flush(); + + private: + enum AllocationState { + UNALLOCATED = 0, + SINGLE_OBJECT, + ARRAY, + SINGLE_OBJECT_WITH_ARRAY + }; + + MDType data_; + AllocationState allocation_state_; +}; + +} // namespace google_breakpad + +#endif // CLIENT_MINIDUMP_FILE_WRITER_H__ diff --git a/shared/sentry/external/breakpad/src/client/minidump_file_writer_unittest.cc b/shared/sentry/external/breakpad/src/client/minidump_file_writer_unittest.cc new file mode 100644 index 000000000..16c407d2e --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/minidump_file_writer_unittest.cc @@ -0,0 +1,180 @@ +// 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. + +// Author: waylonis@google.com (Dan Waylonis) + +/* + g++ -I../ ../common/convert_UTF.cc \ + ../common/string_conversion.cc \ + minidump_file_writer.cc \ + minidump_file_writer_unittest.cc \ + -o minidump_file_writer_unittest + */ + +#include +#include + +#include "minidump_file_writer-inl.h" + +using google_breakpad::MinidumpFileWriter; + +#define ASSERT_TRUE(cond) \ +if (!(cond)) { \ + fprintf(stderr, "FAILED: %s at %s:%d\n", #cond, __FILE__, __LINE__); \ + return false; \ +} + +#define ASSERT_EQ(e1, e2) ASSERT_TRUE((e1) == (e2)) +#define ASSERT_NE(e1, e2) ASSERT_TRUE((e1) != (e2)) + +struct StringStructure { + unsigned long integer_value; + MDLocationDescriptor first_string; + MDLocationDescriptor second_string; +}; + +struct ArrayStructure { + unsigned char char_value; + unsigned short short_value; + unsigned long long_value; +}; + +typedef struct { + unsigned long count; + ArrayStructure array[0]; +} ObjectAndArrayStructure; + +static bool WriteFile(const char* path) { + MinidumpFileWriter writer; + if (writer.Open(path)) { + // Test a single structure + google_breakpad::TypedMDRVA strings(&writer); + ASSERT_TRUE(strings.Allocate()); + strings.get()->integer_value = 0xBEEF; + const char* first = "First String"; + ASSERT_TRUE(writer.WriteString(first, 0, &strings.get()->first_string)); + const wchar_t* second = L"Second String"; + ASSERT_TRUE(writer.WriteString(second, 0, &strings.get()->second_string)); + + // Test an array structure + google_breakpad::TypedMDRVA array(&writer); + unsigned int count = 10; + ASSERT_TRUE(array.AllocateArray(count)); + for (unsigned char i = 0; i < count; ++i) { + ArrayStructure local; + local.char_value = i; + local.short_value = i + 1; + local.long_value = i + 2; + ASSERT_TRUE(array.CopyIndex(i, &local)); + } + + // Test an object followed by an array + google_breakpad::TypedMDRVA obj_array(&writer); + ASSERT_TRUE(obj_array.AllocateObjectAndArray(count, + sizeof(ArrayStructure))); + obj_array.get()->count = count; + for (unsigned char i = 0; i < count; ++i) { + ArrayStructure local; + local.char_value = i; + local.short_value = i + 1; + local.long_value = i + 2; + ASSERT_TRUE(obj_array.CopyIndexAfterObject(i, &local, sizeof(local))); + } + } + + return writer.Close(); +} + +static bool CompareFile(const char* path) { + unsigned long expected[] = { +#if defined(__BIG_ENDIAN__) + 0x0000beef, 0x0000001e, 0x00000018, 0x00000020, 0x00000038, 0x00000000, + 0x00000018, 0x00460069, 0x00720073, 0x00740020, 0x00530074, 0x00720069, + 0x006e0067, 0x00000000, 0x0000001a, 0x00530065, 0x0063006f, 0x006e0064, + 0x00200053, 0x00740072, 0x0069006e, 0x00670000, 0x00000001, 0x00000002, + 0x01000002, 0x00000003, 0x02000003, 0x00000004, 0x03000004, 0x00000005, + 0x04000005, 0x00000006, 0x05000006, 0x00000007, 0x06000007, 0x00000008, + 0x07000008, 0x00000009, 0x08000009, 0x0000000a, 0x0900000a, 0x0000000b, + 0x0000000a, 0x00000001, 0x00000002, 0x01000002, 0x00000003, 0x02000003, + 0x00000004, 0x03000004, 0x00000005, 0x04000005, 0x00000006, 0x05000006, + 0x00000007, 0x06000007, 0x00000008, 0x07000008, 0x00000009, 0x08000009, + 0x0000000a, 0x0900000a, 0x0000000b, 0x00000000 +#else + 0x0000beef, 0x0000001e, 0x00000018, 0x00000020, + 0x00000038, 0x00000000, 0x00000018, 0x00690046, + 0x00730072, 0x00200074, 0x00740053, 0x00690072, + 0x0067006e, 0x00000000, 0x0000001a, 0x00650053, + 0x006f0063, 0x0064006e, 0x00530020, 0x00720074, + 0x006e0069, 0x00000067, 0x00011e00, 0x00000002, + 0x00021e01, 0x00000003, 0x00031e02, 0x00000004, + 0x00041e03, 0x00000005, 0x00051e04, 0x00000006, + 0x00061e05, 0x00000007, 0x00071e06, 0x00000008, + 0x00081e07, 0x00000009, 0x00091e08, 0x0000000a, + 0x000a1e09, 0x0000000b, 0x0000000a, 0x00011c00, + 0x00000002, 0x00021c01, 0x00000003, 0x00031c02, + 0x00000004, 0x00041c03, 0x00000005, 0x00051c04, + 0x00000006, 0x00061c05, 0x00000007, 0x00071c06, + 0x00000008, 0x00081c07, 0x00000009, 0x00091c08, + 0x0000000a, 0x000a1c09, 0x0000000b, 0x00000000, +#endif + }; + size_t expected_byte_count = sizeof(expected); + int fd = open(path, O_RDONLY, 0600); + void* buffer = malloc(expected_byte_count); + ASSERT_NE(fd, -1); + ASSERT_TRUE(buffer); + ASSERT_EQ(read(fd, buffer, expected_byte_count), + static_cast(expected_byte_count)); + + char* b1; + char* b2; + b1 = reinterpret_cast(buffer); + b2 = reinterpret_cast(expected); + while (*b1 == *b2) { + b1++; + b2++; + } + + printf("%p\n", reinterpret_cast(b1 - (char*)buffer)); + + ASSERT_EQ(memcmp(buffer, expected, expected_byte_count), 0); + return true; +} + +static bool RunTests() { + const char* path = "/tmp/minidump_file_writer_unittest.dmp"; + ASSERT_TRUE(WriteFile(path)); + ASSERT_TRUE(CompareFile(path)); + unlink(path); + return true; +} + +extern "C" int main(int argc, const char* argv[]) { + return RunTests() ? 0 : 1; +} diff --git a/shared/sentry/external/breakpad/src/client/solaris/handler/Makefile b/shared/sentry/external/breakpad/src/client/solaris/handler/Makefile new file mode 100644 index 000000000..6da9464e6 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/solaris/handler/Makefile @@ -0,0 +1,77 @@ +# 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 + +CC=cc +CXX=CC + +CPPFLAGS=-g -I../../.. -DNDEBUG -features=extensions -D_REENTRANT +LDFLAGS=-lpthread -lssl -lgnutls-openssl -lelf + +OBJ_DIR=. +BIN_DIR=. + +THREAD_SRC=solaris_lwp.cc +SHARE_SRC=../../minidump_file_writer.cc\ + ../../../common/convert_UTF.cc\ + ../../../common/md5.cc\ + ../../../common/string_conversion.cc\ + ../../../common/solaris/file_id.cc\ + minidump_generator.cc +HANDLER_SRC=exception_handler.cc\ + ../../../common/solaris/guid_creator.cc + +MINIDUMP_TEST_SRC=minidump_test.cc +EXCEPTION_TEST_SRC=exception_handler_test.cc + +THREAD_OBJ=$(patsubst %.cc,$(OBJ_DIR)/%.o,$(THREAD_SRC)) +SHARE_OBJ=$(patsubst %.cc,$(OBJ_DIR)/%.o,$(SHARE_SRC)) +HANDLER_OBJ=$(patsubst %.cc,$(OBJ_DIR)/%.o,$(HANDLER_SRC)) +MINIDUMP_TEST_OBJ=$(patsubst %.cc,$(OBJ_DIR)/%.o, $(MINIDUMP_TEST_SRC))\ + $(THREAD_OBJ) $(SHARE_OBJ) $(HANDLER_OBJ) +EXCEPTION_TEST_OBJ=$(patsubst %.cc,$(OBJ_DIR)/%.o, $(EXCEPTION_TEST_SRC))\ + $(THREAD_OBJ) $(SHARE_OBJ) $(HANDLER_OBJ) + +BIN=$(BIN_DIR)/minidump_test\ + $(BIN_DIR)/exception_handler_test + +.PHONY:all clean + +all:$(BIN) + +$(BIN_DIR)/minidump_test:$(MINIDUMP_TEST_OBJ) + $(CXX) $(CPPFLAGS) $(LDFLAGS) $^ -o $@ + +$(BIN_DIR)/exception_handler_test:$(EXCEPTION_TEST_OBJ) + $(CXX) $(CPPFLAGS) $(LDFLAGS) $^ -o $@ + +clean: + rm -f $(BIN) *.o *.out *.dmp core ../../minidump_file_writer.o\ + ../../../common/*.o ../../../common/solaris/*.o diff --git a/shared/sentry/external/breakpad/src/client/solaris/handler/exception_handler.cc b/shared/sentry/external/breakpad/src/client/solaris/handler/exception_handler.cc new file mode 100644 index 000000000..c96683f6f --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/solaris/handler/exception_handler.cc @@ -0,0 +1,258 @@ +// 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 +#include +#include +#include + +#include +#include +#include + +#include "client/solaris/handler/exception_handler.h" +#include "common/solaris/guid_creator.h" +#include "common/solaris/message_output.h" +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +// Signals that we are interested. +static const int kSigTable[] = { + SIGSEGV, + SIGABRT, + SIGFPE, + SIGILL, + SIGBUS +}; + +std::vector* ExceptionHandler::handler_stack_ = NULL; +int ExceptionHandler::handler_stack_index_ = 0; +pthread_mutex_t ExceptionHandler::handler_stack_mutex_ = + PTHREAD_MUTEX_INITIALIZER; + +ExceptionHandler::ExceptionHandler(const string& dump_path, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + bool install_handler) + : filter_(filter), + callback_(callback), + callback_context_(callback_context), + dump_path_(), + installed_handler_(install_handler) { + set_dump_path(dump_path); + + if (install_handler) { + SetupHandler(); + } + + if (install_handler) { + pthread_mutex_lock(&handler_stack_mutex_); + + if (handler_stack_ == NULL) + handler_stack_ = new std::vector; + handler_stack_->push_back(this); + pthread_mutex_unlock(&handler_stack_mutex_); + } +} + +ExceptionHandler::~ExceptionHandler() { + TeardownAllHandlers(); + pthread_mutex_lock(&handler_stack_mutex_); + if (handler_stack_->back() == this) { + handler_stack_->pop_back(); + } else { + print_message1(2, "warning: removing Breakpad handler out of order\n"); + for (std::vector::iterator iterator = + handler_stack_->begin(); + iterator != handler_stack_->end(); + ++iterator) { + if (*iterator == this) { + handler_stack_->erase(iterator); + } + } + } + + if (handler_stack_->empty()) { + // When destroying the last ExceptionHandler that installed a handler, + // clean up the handler stack. + delete handler_stack_; + handler_stack_ = NULL; + } + pthread_mutex_unlock(&handler_stack_mutex_); +} + +bool ExceptionHandler::WriteMinidump() { + return InternalWriteMinidump(0, 0, NULL); +} + +// static +bool ExceptionHandler::WriteMinidump(const string& dump_path, + MinidumpCallback callback, + void* callback_context) { + ExceptionHandler handler(dump_path, NULL, callback, + callback_context, false); + return handler.InternalWriteMinidump(0, 0, NULL); +} + +void ExceptionHandler::SetupHandler() { + // Signal on a different stack to avoid using the stack + // of the crashing lwp. + struct sigaltstack sig_stack; + sig_stack.ss_sp = malloc(MINSIGSTKSZ); + if (sig_stack.ss_sp == NULL) + return; + sig_stack.ss_size = MINSIGSTKSZ; + sig_stack.ss_flags = 0; + + if (sigaltstack(&sig_stack, NULL) < 0) + return; + for (size_t i = 0; i < sizeof(kSigTable) / sizeof(kSigTable[0]); ++i) + SetupHandler(kSigTable[i]); +} + +void ExceptionHandler::SetupHandler(int signo) { + struct sigaction act, old_act; + act.sa_handler = HandleException; + act.sa_flags = SA_ONSTACK; + if (sigaction(signo, &act, &old_act) < 0) + return; + old_handlers_[signo] = old_act.sa_handler; +} + +void ExceptionHandler::TeardownHandler(int signo) { + if (old_handlers_.find(signo) != old_handlers_.end()) { + struct sigaction act; + act.sa_handler = old_handlers_[signo]; + act.sa_flags = 0; + sigaction(signo, &act, 0); + } +} + +void ExceptionHandler::TeardownAllHandlers() { + for (size_t i = 0; i < sizeof(kSigTable) / sizeof(kSigTable[0]); ++i) { + TeardownHandler(kSigTable[i]); + } +} + +// static +void ExceptionHandler::HandleException(int signo) { +//void ExceptionHandler::HandleException(int signo, siginfo_t* sip, ucontext_t* sig_ctx) { + // The context information about the signal is put on the stack of + // the signal handler frame as value parameter. For some reasons, the + // prototype of the handler doesn't declare this information as parameter, we + // will do it by hand. The stack layout for a signal handler frame is here: + // http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/lib/libproc/common/Pstack.c#81 + // + // However, if we are being called by another signal handler passing the + // signal up the chain, then we may not have this random extra parameter, + // so we may have to walk the stack to find it. We do the actual work + // on another thread, where it's a little safer, but we want the ebp + // from this frame to find it. + uintptr_t current_ebp = (uintptr_t)_getfp(); + + pthread_mutex_lock(&handler_stack_mutex_); + ExceptionHandler* current_handler = + handler_stack_->at(handler_stack_->size() - ++handler_stack_index_); + pthread_mutex_unlock(&handler_stack_mutex_); + + // Restore original handler. + current_handler->TeardownHandler(signo); + + ucontext_t* sig_ctx = NULL; + if (current_handler->InternalWriteMinidump(signo, current_ebp, &sig_ctx)) { +// if (current_handler->InternalWriteMinidump(signo, &sig_ctx)) { + // Fully handled this exception, safe to exit. + exit(EXIT_FAILURE); + } else { + // Exception not fully handled, will call the next handler in stack to + // process it. + typedef void (*SignalHandler)(int signo); + SignalHandler old_handler = + reinterpret_cast(current_handler->old_handlers_[signo]); + if (old_handler != NULL) + old_handler(signo); + } + + pthread_mutex_lock(&handler_stack_mutex_); + current_handler->SetupHandler(signo); + --handler_stack_index_; + // All the handlers in stack have been invoked to handle the exception, + // normally the process should be terminated and should not reach here. + // In case we got here, ask the OS to handle it to avoid endless loop, + // normally the OS will generate a core and termiate the process. This + // may be desired to debug the program. + if (handler_stack_index_ == 0) + signal(signo, SIG_DFL); + pthread_mutex_unlock(&handler_stack_mutex_); +} + +bool ExceptionHandler::InternalWriteMinidump(int signo, + uintptr_t sighandler_ebp, + ucontext_t** sig_ctx) { + if (filter_ && !filter_(callback_context_)) + return false; + + bool success = false; + GUID guid; + char guid_str[kGUIDStringLength + 1]; + if (CreateGUID(&guid) && GUIDToString(&guid, guid_str, sizeof(guid_str))) { + char minidump_path[PATH_MAX]; + snprintf(minidump_path, sizeof(minidump_path), "%s/%s.dmp", + dump_path_c_, guid_str); + + // Block all the signals we want to process when writing minidump. + // We don't want it to be interrupted. + sigset_t sig_blocked, sig_old; + bool blocked = true; + sigfillset(&sig_blocked); + for (size_t i = 0; i < sizeof(kSigTable) / sizeof(kSigTable[0]); ++i) + sigdelset(&sig_blocked, kSigTable[i]); + if (sigprocmask(SIG_BLOCK, &sig_blocked, &sig_old) != 0) { + blocked = false; + print_message1(2, "HandleException: failed to block signals.\n"); + } + + success = minidump_generator_.WriteMinidumpToFile( + minidump_path, signo, sighandler_ebp, sig_ctx); + + // Unblock the signals. + if (blocked) + sigprocmask(SIG_SETMASK, &sig_old, &sig_old); + + if (callback_) + success = callback_(dump_path_c_, guid_str, callback_context_, success); + } + return success; +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/solaris/handler/exception_handler.h b/shared/sentry/external/breakpad/src/client/solaris/handler/exception_handler.h new file mode 100644 index 000000000..cd6c85ea1 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/solaris/handler/exception_handler.h @@ -0,0 +1,201 @@ +// 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 CLIENT_SOLARIS_HANDLER_EXCEPTION_HANDLER_H__ +#define CLIENT_SOLARIS_HANDLER_EXCEPTION_HANDLER_H__ + +#include +#include +#include + +#include "client/solaris/handler/minidump_generator.h" + +namespace google_breakpad { + +using std::string; + +// +// ExceptionHandler +// +// ExceptionHandler can write a minidump file when an exception occurs, +// or when WriteMinidump() is called explicitly by your program. +// +// To have the exception handler write minidumps when an uncaught exception +// (crash) occurs, you should create an instance early in the execution +// of your program, and keep it around for the entire time you want to +// have crash handling active (typically, until shutdown). +// (NOTE): There should be only one this kind of exception handler +// object per process. +// +// If you want to write minidumps without installing the exception handler, +// you can create an ExceptionHandler with install_handler set to false, +// then call WriteMinidump. You can also use this technique if you want to +// use different minidump callbacks for different call sites. +// +// In either case, a callback function is called when a minidump is written, +// which receives the unqiue id of the minidump. The caller can use this +// id to collect and write additional application state, and to launch an +// external crash-reporting application. +// +// Caller should try to make the callbacks as crash-friendly as possible, +// it should avoid use heap memory allocation as much as possible. +// +class ExceptionHandler { + public: + // A callback function to run before Breakpad performs any substantial + // processing of an exception. A FilterCallback is called before writing + // a minidump. context is the parameter supplied by the user as + // callback_context when the handler was created. + // + // If a FilterCallback returns true, Breakpad will continue processing, + // attempting to write a minidump. If a FilterCallback returns false, + // Breakpad will immediately report the exception as unhandled without + // writing a minidump, allowing another handler the opportunity to handle it. + typedef bool (*FilterCallback)(void* context); + + // A callback function to run after the minidump has been written. + // minidump_id is a unique id for the dump, so the minidump + // file is /.dmp. context is the parameter supplied + // by the user as callback_context when the handler was created. succeeded + // indicates whether a minidump file was successfully written. + // + // If an exception occurred and the callback returns true, Breakpad will + // treat the exception as fully-handled, suppressing any other handlers from + // being notified of the exception. If the callback returns false, Breakpad + // will treat the exception as unhandled, and allow another handler to handle + // it. If there are no other handlers, Breakpad will report the exception to + // the system as unhandled, allowing a debugger or native crash dialog the + // opportunity to handle the exception. Most callback implementations + // should normally return the value of |succeeded|, or when they wish to + // not report an exception of handled, false. Callbacks will rarely want to + // return true directly (unless |succeeded| is true). + typedef bool (*MinidumpCallback)(const char* dump_path, + const char* minidump_id, + void* context, + bool succeeded); + + // Creates a new ExceptionHandler instance to handle writing minidumps. + // Before writing a minidump, the optional filter callback will be called. + // Its return value determines whether or not Breakpad should write a + // minidump. Minidump files will be written to dump_path, and the optional + // callback is called after writing the dump file, as described above. + // If install_handler is true, then a minidump will be written whenever + // an unhandled exception occurs. If it is false, minidumps will only + // be written when WriteMinidump is called. + ExceptionHandler(const string& dump_path, + FilterCallback filter, MinidumpCallback callback, + void* callback_context, + bool install_handler); + ~ExceptionHandler(); + + // Get and Set the minidump path. + string dump_path() const { return dump_path_; } + void set_dump_path(const string& dump_path) { + dump_path_ = dump_path; + dump_path_c_ = dump_path_.c_str(); + } + + // Writes a minidump immediately. This can be used to capture the + // execution state independently of a crash. Returns true on success. + bool WriteMinidump(); + + // Convenience form of WriteMinidump which does not require an + // ExceptionHandler instance. + static bool WriteMinidump(const string& dump_path, + MinidumpCallback callback, + void* callback_context); + + private: + // Setup crash handler. + void SetupHandler(); + // Setup signal handler for a signal. + void SetupHandler(int signo); + // Teardown the handler for a signal. + void TeardownHandler(int signo); + // Teardown all handlers. + void TeardownAllHandlers(); + + // Runs the main loop for the exception handler thread. + static void* ExceptionHandlerThreadMain(void* lpParameter); + + // Signal handler. + static void HandleException(int signo); + + // Write all the information to the dump file. + // If called from a signal handler, sighandler_ebp is the ebp of + // that signal handler's frame, and sig_ctx is an out parameter + // that will be set to point at the ucontext_t that was placed + // on the stack by the kernel. You can pass zero and NULL + // for the second and third parameters if you are not calling + // this from a signal handler. + bool InternalWriteMinidump(int signo, uintptr_t sighandler_ebp, + ucontext_t** sig_ctx); + + private: + // The callbacks before and after writing the dump file. + FilterCallback filter_; + MinidumpCallback callback_; + void* callback_context_; + + // The directory in which a minidump will be written, set by the dump_path + // argument to the constructor, or set_dump_path. + string dump_path_; + // C style dump path. Keep this when setting dump path, since calling + // c_str() of std::string when crashing may not be safe. + const char* dump_path_c_; + + // True if the ExceptionHandler installed an unhandled exception filter + // when created (with an install_handler parameter set to true). + bool installed_handler_; + + // Keep the previous handlers for the signal. + typedef void (*sighandler_t)(int); + std::map old_handlers_; + + // The global exception handler stack. This is need becuase there may exist + // multiple ExceptionHandler instances in a process. Each will have itself + // registered in this stack. + static std::vector* handler_stack_; + // The index of the handler that should handle the next exception. + static int handler_stack_index_; + static pthread_mutex_t handler_stack_mutex_; + + // The minidump generator. + MinidumpGenerator minidump_generator_; + + // disallow copy ctor and operator= + explicit ExceptionHandler(const ExceptionHandler&); + void operator=(const ExceptionHandler&); +}; + +} // namespace google_breakpad + +#endif // CLIENT_SOLARIS_HANDLER_EXCEPTION_HANDLER_H__ diff --git a/shared/sentry/external/breakpad/src/client/solaris/handler/exception_handler_test.cc b/shared/sentry/external/breakpad/src/client/solaris/handler/exception_handler_test.cc new file mode 100644 index 000000000..4d2b33faf --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/solaris/handler/exception_handler_test.cc @@ -0,0 +1,119 @@ +// 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 +#include + +#include +#include +#include +#include + +#include "client/solaris/handler/exception_handler.h" +#include "client/solaris/handler/solaris_lwp.h" + +using namespace google_breakpad; + +// Thread use this to see if it should stop working. +static bool should_exit = false; + +static int foo2(int arg) { + // Stack variable, used for debugging stack dumps. + int c = 0xcccccccc; + fprintf(stderr, "Thread trying to crash: %x\n", getpid()); + c = *reinterpret_cast(0x5); + return c; +} + +static int foo(int arg) { + // Stack variable, used for debugging stack dumps. + int b = 0xbbbbbbbb; + b = foo2(b); + return b; +} + +static void* thread_crash(void*) { + // Stack variable, used for debugging stack dumps. + int a = 0xaaaaaaaa; + sleep(3); + a = foo(a); + printf("%x\n", a); + return NULL; +} + +static void* thread_main(void*) { + while (!should_exit) + sleep(1); + return NULL; +} + +static void CreateCrashThread() { + pthread_t h; + pthread_create(&h, NULL, thread_crash, NULL); + pthread_detach(h); +} + +// Create working threads. +static void CreateThread(int num) { + pthread_t h; + for (int i = 0; i < num; ++i) { + pthread_create(&h, NULL, thread_main, NULL); + pthread_detach(h); + } +} + +// Callback when minidump written. +static bool MinidumpCallback(const char* dump_path, + const char* minidump_id, + void* context, + bool succeeded) { + int index = reinterpret_cast(context); + if (index == 0) { + should_exit = true; + return true; + } + // Don't process it. + return false; +} + +int main(int argc, char* argv[]) { + int handler_index = 1; + ExceptionHandler handler_ignore(".", NULL, MinidumpCallback, + (void*)handler_index, true); + CreateCrashThread(); + CreateThread(10); + + while (true) + sleep(20); + should_exit = true; + + return 0; +} diff --git a/shared/sentry/external/breakpad/src/client/solaris/handler/minidump_generator.cc b/shared/sentry/external/breakpad/src/client/solaris/handler/minidump_generator.cc new file mode 100644 index 000000000..81a535870 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/solaris/handler/minidump_generator.cc @@ -0,0 +1,787 @@ +// 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "client/solaris/handler/minidump_generator.h" +#include "client/minidump_file_writer-inl.h" +#include "common/solaris/file_id.h" + +namespace { + +using namespace google_breakpad; +using namespace google_breakpad::elf::FileID; + +// Argument for the writer function. +struct WriterArgument { + MinidumpFileWriter* minidump_writer; + + // Pid of the lwp who called WriteMinidumpToFile + int requester_pid; + + // The stack bottom of the lwp which caused the dump. + // Mainly used to find the lwp id of the crashed lwp since signal + // handler may not be called in the lwp who caused it. + uintptr_t crashed_stack_bottom; + + // Id of the crashing lwp. + int crashed_lwpid; + + // Signal number when crash happened. Can be 0 if this is a requested dump. + int signo; + + // The ebp of the signal handler frame on x86. Can be 0 if this is a + // requested dump. + uintptr_t sighandler_ebp; + + // User context when crash happens. Can be NULL if this is a requested dump. + // This is actually an out parameter, but it will be filled in at the start + // of the writer LWP. + ucontext_t* sig_ctx; + + // Used to get information about the lwps. + SolarisLwp* lwp_lister; +}; + +// Holding context information for the callback of finding the crashing lwp. +struct FindCrashLwpContext { + const SolarisLwp* lwp_lister; + uintptr_t crashing_stack_bottom; + int crashing_lwpid; + + FindCrashLwpContext() : + lwp_lister(NULL), + crashing_stack_bottom(0UL), + crashing_lwpid(-1) { + } +}; + +// Callback for list lwps. +// It will compare the stack bottom of the provided lwp with the stack +// bottom of the crashed lwp, it they are eqaul, this lwp is the one +// who crashed. +bool IsLwpCrashedCallback(lwpstatus_t* lsp, void* context) { + FindCrashLwpContext* crashing_context = + static_cast(context); + const SolarisLwp* lwp_lister = crashing_context->lwp_lister; + const prgregset_t* gregs = &(lsp->pr_reg); +#if TARGET_CPU_SPARC + uintptr_t last_ebp = (*gregs)[R_FP]; +#elif TARGET_CPU_X86 + uintptr_t last_ebp = (*gregs)[EBP]; +#endif + uintptr_t stack_bottom = lwp_lister->GetLwpStackBottom(last_ebp); + if (stack_bottom > last_ebp && + stack_bottom == crashing_context->crashing_stack_bottom) { + // Got it. Stop iteration. + crashing_context->crashing_lwpid = lsp->pr_lwpid; + return false; + } + + return true; +} + +// Find the crashing lwpid. +// This is done based on stack bottom comparing. +int FindCrashingLwp(uintptr_t crashing_stack_bottom, + int requester_pid, + const SolarisLwp* lwp_lister) { + FindCrashLwpContext context; + context.lwp_lister = lwp_lister; + context.crashing_stack_bottom = crashing_stack_bottom; + CallbackParam callback_param(IsLwpCrashedCallback, + &context); + lwp_lister->Lwp_iter_all(lwp_lister->getpid(), &callback_param); + return context.crashing_lwpid; +} + +bool WriteLwpStack(const SolarisLwp* lwp_lister, + uintptr_t last_esp, + UntypedMDRVA* memory, + MDMemoryDescriptor* loc) { + uintptr_t stack_bottom = lwp_lister->GetLwpStackBottom(last_esp); + if (stack_bottom >= last_esp) { + int size = stack_bottom - last_esp; + if (size > 0) { + if (!memory->Allocate(size)) + return false; + memory->Copy(reinterpret_cast(last_esp), size); + loc->start_of_memory_range = last_esp; + loc->memory = memory->location(); + } + return true; + } + return false; +} + +#if TARGET_CPU_SPARC +bool WriteContext(MDRawContextSPARC* context, ucontext_t* sig_ctx) { + assert(sig_ctx != NULL); + int* regs = sig_ctx->uc_mcontext.gregs; + context->context_flags = MD_CONTEXT_SPARC_FULL; + + context->ccr = (unsigned int)(regs[0]); + context->pc = (unsigned int)(regs[REG_PC]); + context->npc = (unsigned int)(regs[REG_nPC]); + context->y = (unsigned int)(regs[REG_Y]); + context->asi = (unsigned int)(regs[19]); + context->fprs = (unsigned int)(regs[20]); + + for ( int i = 0 ; i < 32; ++i ) { + context->g_r[i] = 0; + } + + for ( int i = 1 ; i < 16; ++i ) { + context->g_r[i] = (uintptr_t)(sig_ctx->uc_mcontext.gregs[i + 3]); + } + context->g_r[30] = (uintptr_t)(((struct frame*)context->g_r[14])->fr_savfp); + + return true; +} + +bool WriteContext(MDRawContextSPARC* context, prgregset_t regs, + prfpregset_t* fp_regs) { + if (!context || !regs) + return false; + + context->context_flags = MD_CONTEXT_SPARC_FULL; + + context->ccr = (uintptr_t)(regs[32]); + context->pc = (uintptr_t)(regs[R_PC]); + context->npc = (uintptr_t)(regs[R_nPC]); + context->y = (uintptr_t)(regs[R_Y]); + context->asi = (uintptr_t)(regs[36]); + context->fprs = (uintptr_t)(regs[37]); + for ( int i = 0 ; i < 32 ; ++i ){ + context->g_r[i] = (uintptr_t)(regs[i]); + } + + return true; +} +#elif TARGET_CPU_X86 +bool WriteContext(MDRawContextX86* context, prgregset_t regs, + prfpregset_t* fp_regs) { + if (!context || !regs) + return false; + + context->context_flags = MD_CONTEXT_X86_FULL; + + context->cs = regs[CS]; + context->ds = regs[DS]; + context->es = regs[ES]; + context->fs = regs[FS]; + context->gs = regs[GS]; + context->ss = regs[SS]; + context->edi = regs[EDI]; + context->esi = regs[ESI]; + context->ebx = regs[EBX]; + context->edx = regs[EDX]; + context->ecx = regs[ECX]; + context->eax = regs[EAX]; + context->ebp = regs[EBP]; + context->eip = regs[EIP]; + context->esp = regs[UESP]; + context->eflags = regs[EFL]; + + return true; +} +#endif /* TARGET_CPU_XXX */ + +// Write information about a crashed Lwp. +// When a lwp crash, kernel will write something on the stack for processing +// signal. This makes the current stack not reliable, and our stack walker +// won't figure out the whole call stack for this. So we write the stack at the +// time of the crash into the minidump file, not the current stack. +bool WriteCrashedLwpStream(MinidumpFileWriter* minidump_writer, + const WriterArgument* writer_args, + const lwpstatus_t* lsp, + MDRawThread* lwp) { + assert(writer_args->sig_ctx != NULL); + + lwp->thread_id = lsp->pr_lwpid; + +#if TARGET_CPU_SPARC + UntypedMDRVA memory(minidump_writer); + if (!WriteLwpStack(writer_args->lwp_lister, + writer_args->sig_ctx->uc_mcontext.gregs[REG_O6], + &memory, + &lwp->stack)) + return false; + + TypedMDRVA context(minidump_writer); + if (!context.Allocate()) + return false; + lwp->thread_context = context.location(); + memset(context.get(), 0, sizeof(MDRawContextSPARC)); + return WriteContext(context.get(), writer_args->sig_ctx); +#elif TARGET_CPU_X86 + UntypedMDRVA memory(minidump_writer); + if (!WriteLwpStack(writer_args->lwp_lister, + writer_args->sig_ctx->uc_mcontext.gregs[UESP], + &memory, + &lwp->stack)) + return false; + + TypedMDRVA context(minidump_writer); + if (!context.Allocate()) + return false; + lwp->thread_context = context.location(); + memset(context.get(), 0, sizeof(MDRawContextX86)); + return WriteContext(context.get(), + (int*)&writer_args->sig_ctx->uc_mcontext.gregs, + &writer_args->sig_ctx->uc_mcontext.fpregs); +#endif +} + +bool WriteLwpStream(MinidumpFileWriter* minidump_writer, + const SolarisLwp* lwp_lister, + const lwpstatus_t* lsp, MDRawThread* lwp) { + prfpregset_t fp_regs = lsp->pr_fpreg; + const prgregset_t* gregs = &(lsp->pr_reg); + UntypedMDRVA memory(minidump_writer); +#if TARGET_CPU_SPARC + if (!WriteLwpStack(lwp_lister, + (*gregs)[R_SP], + &memory, + &lwp->stack)) + return false; + + // Write context + TypedMDRVA context(minidump_writer); + if (!context.Allocate()) + return false; + // should be the thread_id + lwp->thread_id = lsp->pr_lwpid; + lwp->thread_context = context.location(); + memset(context.get(), 0, sizeof(MDRawContextSPARC)); +#elif TARGET_CPU_X86 + if (!WriteLwpStack(lwp_lister, + (*gregs)[UESP], + &memory, + &lwp->stack)) + return false; + + // Write context + TypedMDRVA context(minidump_writer); + if (!context.Allocate()) + return false; + // should be the thread_id + lwp->thread_id = lsp->pr_lwpid; + lwp->thread_context = context.location(); + memset(context.get(), 0, sizeof(MDRawContextX86)); +#endif /* TARGET_CPU_XXX */ + return WriteContext(context.get(), (int*)gregs, &fp_regs); +} + +bool WriteCPUInformation(MDRawSystemInfo* sys_info) { + struct utsname uts; + char *major, *minor, *build; + + sys_info->number_of_processors = sysconf(_SC_NPROCESSORS_CONF); + sys_info->processor_architecture = MD_CPU_ARCHITECTURE_UNKNOWN; + if (uname(&uts) != -1) { + // Match "i86pc" as X86 architecture. + if (strcmp(uts.machine, "i86pc") == 0) + sys_info->processor_architecture = MD_CPU_ARCHITECTURE_X86; + else if (strcmp(uts.machine, "sun4u") == 0) + sys_info->processor_architecture = MD_CPU_ARCHITECTURE_SPARC; + } + + major = uts.release; + minor = strchr(major, '.'); + *minor = '\0'; + ++minor; + sys_info->major_version = atoi(major); + sys_info->minor_version = atoi(minor); + + build = strchr(uts.version, '_'); + ++build; + sys_info->build_number = atoi(build); + + return true; +} + +bool WriteOSInformation(MinidumpFileWriter* minidump_writer, + MDRawSystemInfo* sys_info) { + sys_info->platform_id = MD_OS_SOLARIS; + + struct utsname uts; + if (uname(&uts) != -1) { + char os_version[512]; + size_t space_left = sizeof(os_version); + memset(os_version, 0, space_left); + const char* os_info_table[] = { + uts.sysname, + uts.release, + uts.version, + uts.machine, + "OpenSolaris", + NULL + }; + for (const char** cur_os_info = os_info_table; + *cur_os_info != NULL; + ++cur_os_info) { + if (cur_os_info != os_info_table && space_left > 1) { + strcat(os_version, " "); + --space_left; + } + if (space_left > strlen(*cur_os_info)) { + strcat(os_version, *cur_os_info); + space_left -= strlen(*cur_os_info); + } else { + break; + } + } + + MDLocationDescriptor location; + if (!minidump_writer->WriteString(os_version, 0, &location)) + return false; + sys_info->csd_version_rva = location.rva; + } + return true; +} + +// Callback context for get writting lwp information. +struct LwpInfoCallbackCtx { + MinidumpFileWriter* minidump_writer; + const WriterArgument* writer_args; + TypedMDRVA* list; + int lwp_index; +}; + +bool LwpInformationCallback(lwpstatus_t* lsp, void* context) { + bool success = true; + LwpInfoCallbackCtx* callback_context = + static_cast(context); + + // The current lwp is the one to handle the crash. Ignore it. + if (lsp->pr_lwpid != pthread_self()) { + LwpInfoCallbackCtx* callback_context = + static_cast(context); + MDRawThread lwp; + memset(&lwp, 0, sizeof(MDRawThread)); + + if (lsp->pr_lwpid != callback_context->writer_args->crashed_lwpid || + callback_context->writer_args->sig_ctx == NULL) { + success = WriteLwpStream(callback_context->minidump_writer, + callback_context->writer_args->lwp_lister, + lsp, &lwp); + } else { + success = WriteCrashedLwpStream(callback_context->minidump_writer, + callback_context->writer_args, + lsp, &lwp); + } + if (success) { + callback_context->list->CopyIndexAfterObject( + callback_context->lwp_index++, + &lwp, sizeof(MDRawThread)); + } + } + + return success; +} + +bool WriteLwpListStream(MinidumpFileWriter* minidump_writer, + const WriterArgument* writer_args, + MDRawDirectory* dir) { + // Get the lwp information. + const SolarisLwp* lwp_lister = writer_args->lwp_lister; + int lwp_count = lwp_lister->GetLwpCount(); + if (lwp_count < 0) + return false; + TypedMDRVA list(minidump_writer); + if (!list.AllocateObjectAndArray(lwp_count - 1, sizeof(MDRawThread))) + return false; + dir->stream_type = MD_THREAD_LIST_STREAM; + dir->location = list.location(); + list.get()->number_of_threads = lwp_count - 1; + + LwpInfoCallbackCtx context; + context.minidump_writer = minidump_writer; + context.writer_args = writer_args; + context.list = &list; + context.lwp_index = 0; + CallbackParam callback_param(LwpInformationCallback, + &context); + int written = + lwp_lister->Lwp_iter_all(lwp_lister->getpid(), &callback_param); + return written == lwp_count; +} + +bool WriteCVRecord(MinidumpFileWriter* minidump_writer, + MDRawModule* module, + const char* module_path, + char* realname) { + TypedMDRVA cv(minidump_writer); + + char path[PATH_MAX]; + const char* module_name = module_path ? module_path : ""; + snprintf(path, sizeof(path), "/proc/self/object/%s", module_name); + + size_t module_name_length = strlen(realname); + if (!cv.AllocateObjectAndArray(module_name_length + 1, sizeof(uint8_t))) + return false; + if (!cv.CopyIndexAfterObject(0, realname, module_name_length)) + return false; + + module->cv_record = cv.location(); + MDCVInfoPDB70* cv_ptr = cv.get(); + memset(cv_ptr, 0, sizeof(MDCVInfoPDB70)); + cv_ptr->cv_signature = MD_CVINFOPDB70_SIGNATURE; + cv_ptr->age = 0; + + // Get the module identifier + FileID file_id(path); + unsigned char identifier[16]; + + if (file_id.ElfFileIdentifier(identifier)) { + cv_ptr->signature.data1 = (uint32_t)identifier[0] << 24 | + (uint32_t)identifier[1] << 16 | (uint32_t)identifier[2] << 8 | + (uint32_t)identifier[3]; + cv_ptr->signature.data2 = (uint32_t)identifier[4] << 8 | identifier[5]; + cv_ptr->signature.data3 = (uint32_t)identifier[6] << 8 | identifier[7]; + cv_ptr->signature.data4[0] = identifier[8]; + cv_ptr->signature.data4[1] = identifier[9]; + cv_ptr->signature.data4[2] = identifier[10]; + cv_ptr->signature.data4[3] = identifier[11]; + cv_ptr->signature.data4[4] = identifier[12]; + cv_ptr->signature.data4[5] = identifier[13]; + cv_ptr->signature.data4[6] = identifier[14]; + cv_ptr->signature.data4[7] = identifier[15]; + } + return true; +} + +struct ModuleInfoCallbackCtx { + MinidumpFileWriter* minidump_writer; + const WriterArgument* writer_args; + TypedMDRVA* list; + int module_index; +}; + +bool ModuleInfoCallback(const ModuleInfo& module_info, void* context) { + ModuleInfoCallbackCtx* callback_context = + static_cast(context); + // Skip those modules without name, or those that are not modules. + if (strlen(module_info.name) == 0) + return true; + + MDRawModule module; + memset(&module, 0, sizeof(module)); + MDLocationDescriptor loc; + char path[PATH_MAX]; + char buf[PATH_MAX]; + char* realname; + int count; + + snprintf(path, sizeof (path), "/proc/self/path/%s", module_info.name); + if ((count = readlink(path, buf, PATH_MAX)) < 0) + return false; + buf[count] = '\0'; + + if ((realname = strrchr(buf, '/')) == NULL) + return false; + realname++; + + if (!callback_context->minidump_writer->WriteString(realname, 0, &loc)) + return false; + + module.base_of_image = (uint64_t)module_info.start_addr; + module.size_of_image = module_info.size; + module.module_name_rva = loc.rva; + + if (!WriteCVRecord(callback_context->minidump_writer, &module, + module_info.name, realname)) + return false; + + callback_context->list->CopyIndexAfterObject( + callback_context->module_index++, &module, MD_MODULE_SIZE); + return true; +} + +bool WriteModuleListStream(MinidumpFileWriter* minidump_writer, + const WriterArgument* writer_args, + MDRawDirectory* dir) { + TypedMDRVA list(minidump_writer); + int module_count = writer_args->lwp_lister->GetModuleCount(); + + if (module_count <= 0 || + !list.AllocateObjectAndArray(module_count, MD_MODULE_SIZE)) { + return false; + } + + dir->stream_type = MD_MODULE_LIST_STREAM; + dir->location = list.location(); + list.get()->number_of_modules = module_count; + ModuleInfoCallbackCtx context; + context.minidump_writer = minidump_writer; + context.writer_args = writer_args; + context.list = &list; + context.module_index = 0; + CallbackParam callback(ModuleInfoCallback, &context); + return writer_args->lwp_lister->ListModules(&callback) == module_count; +} + +bool WriteSystemInfoStream(MinidumpFileWriter* minidump_writer, + const WriterArgument* writer_args, + MDRawDirectory* dir) { + TypedMDRVA sys_info(minidump_writer); + + if (!sys_info.Allocate()) + return false; + + dir->stream_type = MD_SYSTEM_INFO_STREAM; + dir->location = sys_info.location(); + + return WriteCPUInformation(sys_info.get()) && + WriteOSInformation(minidump_writer, sys_info.get()); +} + +bool WriteExceptionStream(MinidumpFileWriter* minidump_writer, + const WriterArgument* writer_args, + MDRawDirectory* dir) { + // This happenes when this is not a crash, but a requested dump. + if (writer_args->sig_ctx == NULL) + return false; + + TypedMDRVA exception(minidump_writer); + if (!exception.Allocate()) + return false; + + dir->stream_type = MD_EXCEPTION_STREAM; + dir->location = exception.location(); + exception.get()->thread_id = writer_args->crashed_lwpid; + exception.get()->exception_record.exception_code = writer_args->signo; + exception.get()->exception_record.exception_flags = 0; + +#if TARGET_CPU_SPARC + if (writer_args->sig_ctx != NULL) { + exception.get()->exception_record.exception_address = + writer_args->sig_ctx->uc_mcontext.gregs[REG_PC]; + } else { + return true; + } + + // Write context of the exception. + TypedMDRVA context(minidump_writer); + if (!context.Allocate()) + return false; + exception.get()->thread_context = context.location(); + memset(context.get(), 0, sizeof(MDRawContextSPARC)); + return WriteContext(context.get(), writer_args->sig_ctx); +#elif TARGET_CPU_X86 + if (writer_args->sig_ctx != NULL) { + exception.get()->exception_record.exception_address = + writer_args->sig_ctx->uc_mcontext.gregs[EIP]; + } else { + return true; + } + + // Write context of the exception. + TypedMDRVA context(minidump_writer); + if (!context.Allocate()) + return false; + exception.get()->thread_context = context.location(); + memset(context.get(), 0, sizeof(MDRawContextX86)); + return WriteContext(context.get(), + (int*)&writer_args->sig_ctx->uc_mcontext.gregs, + NULL); +#endif +} + +bool WriteMiscInfoStream(MinidumpFileWriter* minidump_writer, + const WriterArgument* writer_args, + MDRawDirectory* dir) { + TypedMDRVA info(minidump_writer); + + if (!info.Allocate()) + return false; + + dir->stream_type = MD_MISC_INFO_STREAM; + dir->location = info.location(); + info.get()->size_of_info = sizeof(MDRawMiscInfo); + info.get()->flags1 = MD_MISCINFO_FLAGS1_PROCESS_ID; + info.get()->process_id = writer_args->requester_pid; + + return true; +} + +bool WriteBreakpadInfoStream(MinidumpFileWriter* minidump_writer, + const WriterArgument* writer_args, + MDRawDirectory* dir) { + TypedMDRVA info(minidump_writer); + + if (!info.Allocate()) + return false; + + dir->stream_type = MD_BREAKPAD_INFO_STREAM; + dir->location = info.location(); + + info.get()->validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID | + MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID; + info.get()->dump_thread_id = getpid(); + info.get()->requesting_thread_id = writer_args->requester_pid; + return true; +} + +class AutoLwpResumer { + public: + AutoLwpResumer(SolarisLwp* lwp) : lwp_(lwp) {} + ~AutoLwpResumer() { lwp_->ControlAllLwps(false); } + private: + SolarisLwp* lwp_; +}; + +// Prototype of writer functions. +typedef bool (*WriteStreamFN)(MinidumpFileWriter*, + const WriterArgument*, + MDRawDirectory*); + +// Function table to writer a full minidump. +const WriteStreamFN writers[] = { + WriteLwpListStream, + WriteModuleListStream, + WriteSystemInfoStream, + WriteExceptionStream, + WriteMiscInfoStream, + WriteBreakpadInfoStream, +}; + +// Will call each writer function in the writers table. +//void* MinidumpGenerator::Write(void* argument) { +void* Write(void* argument) { + WriterArgument* writer_args = static_cast(argument); + + if (!writer_args->lwp_lister->ControlAllLwps(true)) + return NULL; + + AutoLwpResumer lwpResumer(writer_args->lwp_lister); + + if (writer_args->sighandler_ebp != 0 && + writer_args->lwp_lister->FindSigContext(writer_args->sighandler_ebp, + &writer_args->sig_ctx)) { + writer_args->crashed_stack_bottom = + writer_args->lwp_lister->GetLwpStackBottom( +#if TARGET_CPU_SPARC + writer_args->sig_ctx->uc_mcontext.gregs[REG_O6] +#elif TARGET_CPU_X86 + writer_args->sig_ctx->uc_mcontext.gregs[UESP] +#endif + ); + + int crashed_lwpid = FindCrashingLwp(writer_args->crashed_stack_bottom, + writer_args->requester_pid, + writer_args->lwp_lister); + if (crashed_lwpid > 0) + writer_args->crashed_lwpid = crashed_lwpid; + } + + MinidumpFileWriter* minidump_writer = writer_args->minidump_writer; + TypedMDRVA header(minidump_writer); + TypedMDRVA dir(minidump_writer); + if (!header.Allocate()) + return 0; + + int writer_count = sizeof(writers) / sizeof(writers[0]); + // Need directory space for all writers. + if (!dir.AllocateArray(writer_count)) + return 0; + header.get()->signature = MD_HEADER_SIGNATURE; + header.get()->version = MD_HEADER_VERSION; + header.get()->time_date_stamp = time(NULL); + header.get()->stream_count = writer_count; + header.get()->stream_directory_rva = dir.position(); + + int dir_index = 0; + MDRawDirectory local_dir; + for (int i = 0; i < writer_count; ++i) { + if ((*writers[i])(minidump_writer, writer_args, &local_dir)) + dir.CopyIndex(dir_index++, &local_dir); + } + + return 0; +} + +} // namespace + +namespace google_breakpad { + +MinidumpGenerator::MinidumpGenerator() { +} + +MinidumpGenerator::~MinidumpGenerator() { +} + +// Write minidump into file. +// It runs in a different thread from the crashing thread. +bool MinidumpGenerator::WriteMinidumpToFile(const char* file_pathname, + int signo, + uintptr_t sighandler_ebp, + ucontext_t** sig_ctx) const { + // The exception handler thread. + pthread_t handler_thread; + + assert(file_pathname != NULL); + + if (file_pathname == NULL) + return false; + + MinidumpFileWriter minidump_writer; + if (minidump_writer.Open(file_pathname)) { + WriterArgument argument; + memset(&argument, 0, sizeof(argument)); + SolarisLwp lwp_lister(getpid()); + argument.lwp_lister = &lwp_lister; + argument.minidump_writer = &minidump_writer; + argument.requester_pid = getpid(); + argument.crashed_lwpid = pthread_self(); + argument.signo = signo; + argument.sighandler_ebp = sighandler_ebp; + argument.sig_ctx = NULL; + + pthread_create(&handler_thread, NULL, Write, (void*)&argument); + pthread_join(handler_thread, NULL); + return true; + } + + return false; +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/solaris/handler/minidump_generator.h b/shared/sentry/external/breakpad/src/client/solaris/handler/minidump_generator.h new file mode 100644 index 000000000..daa6fe015 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/solaris/handler/minidump_generator.h @@ -0,0 +1,70 @@ +// 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 CLIENT_SOLARIS_HANDLER_MINIDUMP_GENERATOR_H__ +#define CLIENT_SOLARIS_HANDLER_MINIDUMP_GENERATOR_H__ + +#include + +#include "client/minidump_file_writer.h" +#include "client/solaris/handler/solaris_lwp.h" +#include "google_breakpad/common/breakpad_types.h" +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +// +// MinidumpGenerator +// +// A minidump generator should be created before any exception happen. +// +class MinidumpGenerator { + // Callback run for writing lwp information in the process. + friend bool LwpInformationCallback(lwpstatus_t* lsp, void* context); + + // Callback run for writing module information in the process. + friend bool ModuleInfoCallback(const ModuleInfo& module_info, void* context); + + public: + MinidumpGenerator(); + + ~MinidumpGenerator(); + + // Write minidump. + bool WriteMinidumpToFile(const char* file_pathname, + int signo, + uintptr_t sighandler_ebp, + ucontext_t** sig_ctx) const; +}; + +} // namespace google_breakpad + +#endif // CLIENT_SOLARIS_HANDLER_MINIDUMP_GENERATOR_H_ diff --git a/shared/sentry/external/breakpad/src/client/solaris/handler/minidump_test.cc b/shared/sentry/external/breakpad/src/client/solaris/handler/minidump_test.cc new file mode 100644 index 000000000..5f685ef24 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/solaris/handler/minidump_test.cc @@ -0,0 +1,75 @@ +// 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 +#include + +#include "client/minidump_file_writer.h" +#include "client/solaris/handler/minidump_generator.h" + +using std::string; +using google_breakpad::MinidumpGenerator; + +static bool doneWritingReport = false; + +static void* Reporter(void*) { + char buffer[PATH_MAX]; + MinidumpGenerator md; + + // Write it to the desktop + snprintf(buffer, sizeof(buffer), "./minidump_test.out"); + fprintf(stdout, "Writing %s\n", buffer); + + md.WriteMinidumpToFile(buffer, 0, 0, NULL); + doneWritingReport = true; + + return NULL; +} + +static void SleepyFunction() { + while (!doneWritingReport) { + usleep(100); + } +} + +int main(int argc, char * const argv[]) { + pthread_t reporter_thread; + + if (pthread_create(&reporter_thread, NULL, Reporter, NULL) == 0) { + pthread_detach(reporter_thread); + } else { + perror("pthread_create"); + } + + SleepyFunction(); + + return 0; +} diff --git a/shared/sentry/external/breakpad/src/client/solaris/handler/solaris_lwp.cc b/shared/sentry/external/breakpad/src/client/solaris/handler/solaris_lwp.cc new file mode 100644 index 000000000..cb6cc8ab9 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/solaris/handler/solaris_lwp.cc @@ -0,0 +1,436 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "client/solaris/handler/solaris_lwp.h" +#include "common/solaris/message_output.h" + +using namespace google_breakpad; + +// This unamed namespace contains helper function. +namespace { + +uintptr_t stack_base_address = 0; +static const int HEADER_MAX = 2000; +static const int MAP_MAX = 1000; + +// Context information for the callbacks when validating address by listing +// modules. +struct AddressValidatingContext { + uintptr_t address; + bool is_mapped; + + AddressValidatingContext() : address(0UL), is_mapped(false) { + } +}; + +// Convert from string to int. +static bool LocalAtoi(char* s, int* r) { + assert(s != NULL); + assert(r != NULL); + char* endptr = NULL; + int ret = strtol(s, &endptr, 10); + if (endptr == s) + return false; + *r = ret; + return true; +} + +// Callback invoked for each mapped module. +// It uses the module's adderss range to validate the address. +static bool AddressNotInModuleCallback(const ModuleInfo& module_info, + void* context) { + AddressValidatingContext* addr = + reinterpret_cast(context); + if (addr->is_mapped = ((module_info.start_addr > 0) && + (addr->address >= module_info.start_addr) && + (addr->address <= module_info.start_addr + + module_info.size))) { + stack_base_address = module_info.start_addr + module_info.size; + } + + return !addr->is_mapped; +} + +static int IterateLwpAll(int pid, + CallbackParam* callback_param) { + char lwp_path[40]; + DIR* dir; + int count = 0; + + snprintf(lwp_path, sizeof (lwp_path), "/proc/%d/lwp", (int)pid); + if ((dir = opendir(lwp_path)) == NULL) + return -1; + + struct dirent* entry = NULL; + while ((entry = readdir(dir)) != NULL) { + if ((strcmp(entry->d_name, ".") != 0) && + (strcmp(entry->d_name, "..") != 0)) { + int lwpid = 0; + int last_pid = 0; + if (LocalAtoi(entry->d_name, &lwpid) && last_pid != lwpid) { + last_pid = lwpid; + ++count; + if (callback_param && + !(callback_param->call_back)(lwpid, callback_param->context)) { + break; + } + } + } + } + + closedir(dir); + return count; +} + +#if defined(__i386) && !defined(NO_FRAME_POINTER) +void* GetNextFrame(void** last_ebp) { + void* sp = *last_ebp; + if ((unsigned long)sp == (unsigned long)last_ebp) + return NULL; + if ((unsigned long)sp & (sizeof(void*) - 1)) + return NULL; + if ((unsigned long)sp - (unsigned long)last_ebp > 100000) + return NULL; + return sp; +} +#elif defined(__sparc) +void* GetNextFrame(void* last_ebp) { + return reinterpret_cast(last_ebp)->fr_savfp; +} +#else +void* GetNextFrame(void** last_ebp) { + return reinterpret_cast(last_ebp); +} +#endif + + +class AutoCloser { + public: + AutoCloser(int fd) : fd_(fd) {} + ~AutoCloser() { if (fd_) close(fd_); } + private: + int fd_; +}; + +// Control the execution of the lwp. +// Suspend/Resume lwp based on the value of context. +static bool ControlLwp(int lwpid, void* context) { + // The current thread is the one to handle the crash. Ignore it. + if (lwpid != pthread_self()) { + int ctlfd; + char procname[PATH_MAX]; + bool suspend = *(bool*)context; + + // Open the /proc/$pid/lwp/$lwpid/lwpctl files + snprintf(procname, sizeof (procname), "/proc/self/lwp/%d/lwpctl", lwpid); + + if ((ctlfd = open(procname, O_WRONLY|O_EXCL)) < 0) { + print_message2(2, "failed to open %s in ControlLwp\n", procname); + return false; + } + + AutoCloser autocloser(ctlfd); + + long ctl[2]; + ctl[0] = suspend ? PCSTOP : PCRUN; + ctl[1] = 0; + if (write(ctlfd, ctl, sizeof (ctl)) != sizeof (ctl)) { + print_message2(2, "failed in lwp %d\n", lwpid); + return false; + } + } + + return true; +} + +/* + * Utility function to read the contents of a file that contains a + * prheader_t at the start (/proc/$pid/lstatus or /proc/$pid/lpsinfo). + * Return true on success. + */ +static bool read_lfile(int pid, const char* lname, prheader_t* lhp) { + char lpath[PATH_MAX]; + struct stat statb; + int fd; + size_t size; + + snprintf(lpath, sizeof (lpath), "/proc/%d/%s", pid, lname); + if ((fd = open(lpath, O_RDONLY)) < 0) { + print_message2(2, "failed to open %s in read_lfile\n", lpath); + return false; + } + + AutoCloser autocloser(fd); + + if (fstat(fd, &statb) != 0) + return false; + + size = statb.st_size; + if ((size / sizeof (prheader_t)) + 32 > HEADER_MAX) { + print_message1(2, "map size overflow\n"); + return false; + } + + if (pread(fd, lhp, size, 0) <= sizeof (prheader_t)) + return false; + + return true; +} + +} // namespace + +namespace google_breakpad { + +SolarisLwp::SolarisLwp(int pid) : pid_(pid) { +} + +SolarisLwp::~SolarisLwp() { +} + +int SolarisLwp::ControlAllLwps(bool suspend) { + CallbackParam callback_param(ControlLwp, &suspend); + return IterateLwpAll(pid_, &callback_param); +} + +int SolarisLwp::GetLwpCount() const { + return IterateLwpAll(pid_, NULL); +} + +int SolarisLwp::Lwp_iter_all(int pid, + CallbackParam* callback_param) const { + lwpstatus_t* Lsp; + lwpstatus_t* sp; + prheader_t lphp[HEADER_MAX]; + prheader_t lhp[HEADER_MAX]; + prheader_t* Lphp = lphp; + prheader_t* Lhp = lhp; + lwpsinfo_t* Lpsp; + long nstat; + long ninfo; + int rv = 0; + + /* + * The /proc/pid/lstatus file has the array of lwpstatus_t's and the + * /proc/pid/lpsinfo file has the array of lwpsinfo_t's. + */ + if (read_lfile(pid, "lstatus", Lhp) == NULL) + return -1; + if (read_lfile(pid, "lpsinfo", Lphp) == NULL) { + return -1; + } + + Lsp = (lwpstatus_t*)(uintptr_t)(Lhp + 1); + Lpsp = (lwpsinfo_t*)(uintptr_t)(Lphp + 1); + + for (ninfo = Lphp->pr_nent; ninfo != 0; --ninfo) { + if (Lpsp->pr_sname != 'Z') { + sp = Lsp; + Lsp = (lwpstatus_t*)((uintptr_t)Lsp + Lhp->pr_entsize); + } else { + sp = NULL; + } + if (callback_param && + !(callback_param->call_back)(sp, callback_param->context)) + break; + ++rv; + Lpsp = (lwpsinfo_t*)((uintptr_t)Lpsp + Lphp->pr_entsize); + } + + return rv; +} + +uintptr_t SolarisLwp::GetLwpStackBottom(uintptr_t current_esp) const { + AddressValidatingContext addr; + addr.address = current_esp; + CallbackParam callback_param(AddressNotInModuleCallback, + &addr); + ListModules(&callback_param); + return stack_base_address; +} + +int SolarisLwp::GetModuleCount() const { + return ListModules(NULL); +} + +int SolarisLwp::ListModules( + CallbackParam* callback_param) const { + const char* maps_path = "/proc/self/map"; + struct stat status; + int fd = 0, num; + prmap_t map_array[MAP_MAX]; + prmap_t* maps = map_array; + size_t size; + + if ((fd = open(maps_path, O_RDONLY)) == -1) { + print_message2(2, "failed to open %s in ListModules\n", maps_path); + return -1; + } + + AutoCloser autocloser(fd); + + if (fstat(fd, &status)) + return -1; + + /* + * Determine number of mappings, this value must be + * larger than the actual module count + */ + size = status.st_size; + if ((num = (int)(size / sizeof (prmap_t))) > MAP_MAX) { + print_message1(2, "map size overflow\n"); + return -1; + } + + if (read(fd, (void*)maps, size) < 0) { + print_message2(2, "failed to read %d\n", fd); + return -1; + } + + prmap_t* _maps; + int _num; + int module_count = 0; + + /* + * Scan each mapping - note it is assummed that the mappings are + * presented in order. We fill holes between mappings. On intel + * the last mapping is usually the data segment of ld.so.1, after + * this comes a red zone into which non-fixed mapping won't get + * place. Thus we can simply bail from the loop after seeing the + * last mapping. + */ + for (_num = 0, _maps = maps; _num < num; ++_num, ++_maps) { + ModuleInfo module; + char* name = _maps->pr_mapname; + + memset(&module, 0, sizeof (module)); + module.start_addr = _maps->pr_vaddr; + module.size = _maps->pr_size; + if (strlen(name) > 0) { + int objectfd = 0; + char path[PATH_MAX]; + char buf[SELFMAG]; + + snprintf(path, sizeof (path), "/proc/self/object/%s", name); + if ((objectfd = open(path, O_RDONLY)) < 0) { + print_message1(2, "can't open module file\n"); + continue; + } + + AutoCloser autocloser(objectfd); + + if (read(objectfd, buf, SELFMAG) != SELFMAG) { + print_message1(2, "can't read module file\n"); + continue; + } + if (buf[0] != ELFMAG0 || buf[1] != ELFMAG1 || + buf[2] != ELFMAG2 || buf[3] != ELFMAG3) { + continue; + } + + strncpy(module.name, name, sizeof (module.name) - 1); + ++module_count; + } + if (callback_param && + (!callback_param->call_back(module, callback_param->context))) { + break; + } + } + + return module_count; +} + +// Check if the address is a valid virtual address. +// If the address is in any of the mapped modules, we take it as valid. +// Otherwise it is invalid. +bool SolarisLwp::IsAddressMapped(uintptr_t address) const { + AddressValidatingContext addr; + addr.address = address; + CallbackParam callback_param(AddressNotInModuleCallback, + &addr); + ListModules(&callback_param); + return addr.is_mapped; +} + +// We're looking for a ucontext_t as the second parameter +// to a signal handler function call. Luckily, the ucontext_t +// has an ebp(fp on SPARC) member which should match the ebp(fp) +// pointed to by the ebp(fp) of the signal handler frame. +// The Solaris stack looks like this: +// http://src.opensolaris.org/source/xref/onnv/onnv-gate/usr/src/lib/libproc/common/Pstack.c#81 +bool SolarisLwp::FindSigContext(uintptr_t sighandler_ebp, + ucontext_t** sig_ctx) { + uintptr_t previous_ebp; + uintptr_t sig_ebp; + const int MAX_STACK_DEPTH = 50; + int depth_counter = 0; + + do { +#if TARGET_CPU_SPARC + previous_ebp = reinterpret_cast(GetNextFrame( + reinterpret_cast(sighandler_ebp))); + *sig_ctx = reinterpret_cast(sighandler_ebp + sizeof (struct frame)); + uintptr_t sig_esp = (*sig_ctx)->uc_mcontext.gregs[REG_O6]; + if (sig_esp < previous_ebp && sig_esp > sighandler_ebp) + sig_ebp = (uintptr_t)(((struct frame*)sig_esp)->fr_savfp); + +#elif TARGET_CPU_X86 + previous_ebp = reinterpret_cast(GetNextFrame( + reinterpret_cast(sighandler_ebp))); + *sig_ctx = reinterpret_cast(sighandler_ebp + sizeof (struct frame) + + 3 * sizeof(uintptr_t)); + sig_ebp = (*sig_ctx)->uc_mcontext.gregs[EBP]; +#endif + sighandler_ebp = previous_ebp; + depth_counter++; + } while(previous_ebp != sig_ebp && sighandler_ebp != 0 && + IsAddressMapped(sighandler_ebp) && depth_counter < MAX_STACK_DEPTH); + + return previous_ebp == sig_ebp && previous_ebp != 0; +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/solaris/handler/solaris_lwp.h b/shared/sentry/external/breakpad/src/client/solaris/handler/solaris_lwp.h new file mode 100644 index 000000000..afe352ce6 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/solaris/handler/solaris_lwp.h @@ -0,0 +1,160 @@ +// 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 CLIENT_SOLARIS_HANDLER_SOLARIS_LWP_H__ +#define CLIENT_SOLARIS_HANDLER_SOLARIS_LWP_H__ + +#if defined(sparc) || defined(__sparc) +#define TARGET_CPU_SPARC 1 +#elif defined(i386) || defined(__i386) +#define TARGET_CPU_X86 1 +#else +#error "cannot determine cpu type" +#endif + +#include +#include +#include +#include + +#ifndef _KERNEL +#define _KERNEL +#define MUST_UNDEF_KERNEL +#endif // _KERNEL +#include +#ifdef MUST_UNDEF_KERNEL +#undef _KERNEL +#undef MUST_UNDEF_KERNEL +#endif // MUST_UNDEF_KERNEL + +namespace google_breakpad { + +// Max module path name length. +static const int kMaxModuleNameLength = 256; + +// Holding infomaton about a module in the process. +struct ModuleInfo { + char name[kMaxModuleNameLength]; + uintptr_t start_addr; + int size; +}; + +// A callback to run when getting a lwp in the process. +// Return true will go on to the next lwp while return false will stop the +// iteration. +typedef bool (*LwpCallback)(lwpstatus_t* lsp, void* context); + +// A callback to run when a new module is found in the process. +// Return true will go on to the next module while return false will stop the +// iteration. +typedef bool (*ModuleCallback)(const ModuleInfo& module_info, void* context); + +// A callback to run when getting a lwpid in the process. +// Return true will go on to the next lwp while return false will stop the +// iteration. +typedef bool (*LwpidCallback)(int lwpid, void* context); + +// Holding the callback information. +template +struct CallbackParam { + // Callback function address. + CallbackFunc call_back; + // Callback context; + void* context; + + CallbackParam() : call_back(NULL), context(NULL) { + } + + CallbackParam(CallbackFunc func, void* func_context) : + call_back(func), context(func_context) { + } +}; + +/////////////////////////////////////////////////////////////////////////////// + +// +// SolarisLwp +// +// Provides handy support for operation on Solaris lwps. +// It uses proc file system to get lwp information. +// +// TODO(Alfred): Currently it only supports x86. Add SPARC support. +// +class SolarisLwp { + public: + // Create a SolarisLwp instance to list all the lwps in a process. + explicit SolarisLwp(int pid); + ~SolarisLwp(); + + int getpid() const { return this->pid_; } + + // Control all the lwps in the process. + // Return the number of suspended/resumed lwps in the process. + // Return -1 means failed to control lwps. + int ControlAllLwps(bool suspend); + + // Get the count of lwps in the process. + // Return -1 means error. + int GetLwpCount() const; + + // Iterate the lwps of process. + // Whenever there is a lwp found, the callback will be invoked to process + // the information. + // Return the callback return value or -1 on error. + int Lwp_iter_all(int pid, CallbackParam* callback_param) const; + + // Get the module count of the current process. + int GetModuleCount() const; + + // Get the mapped modules in the address space. + // Whenever a module is found, the callback will be invoked to process the + // information. + // Return how may modules are found. + int ListModules(CallbackParam* callback_param) const; + + // Get the bottom of the stack from esp. + uintptr_t GetLwpStackBottom(uintptr_t current_esp) const; + + // Finds a signal context on the stack given the ebp of our signal handler. + bool FindSigContext(uintptr_t sighandler_ebp, ucontext_t** sig_ctx); + + private: + // Check if the address is a valid virtual address. + bool IsAddressMapped(uintptr_t address) const; + + private: + // The pid of the process we are listing lwps. + int pid_; +}; + +} // namespace google_breakpad + +#endif // CLIENT_SOLARIS_HANDLER_SOLARIS_LWP_H__ diff --git a/shared/sentry/external/breakpad/src/client/windows/breakpad_client.gyp b/shared/sentry/external/breakpad/src/client/windows/breakpad_client.gyp new file mode 100644 index 000000000..647975342 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/breakpad_client.gyp @@ -0,0 +1,66 @@ +# Copyright 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. + +{ + 'includes': [ + '../../build/common.gypi', + ], + 'targets': [ + { + 'target_name': 'build_all', + 'type': 'none', + 'dependencies': [ + './crash_generation/crash_generation.gyp:*', + './handler/exception_handler.gyp:*', + './sender/crash_report_sender.gyp:*', + './unittests/client_tests.gyp:*', + './unittests/testing.gyp:*', + './tests/crash_generation_app/crash_generation_app.gyp:*', + ] + }, + { + 'target_name': 'common', + 'type': 'static_library', + 'include_dirs': [ + '<(DEPTH)', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '<(DEPTH)', + ] + }, + 'sources': [ + '<(DEPTH)/common/windows/guid_string.cc', + '<(DEPTH)/common/windows/guid_string.h', + '<(DEPTH)/common/windows/http_upload.cc', + '<(DEPTH)/common/windows/http_upload.h', + '<(DEPTH)/common/windows/string_utils.cc', + ] + } + ] +} diff --git a/shared/sentry/external/breakpad/src/client/windows/common/auto_critical_section.h b/shared/sentry/external/breakpad/src/client/windows/common/auto_critical_section.h new file mode 100644 index 000000000..3fd4b9b7e --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/common/auto_critical_section.h @@ -0,0 +1,81 @@ +// 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. + +#ifndef CLIENT_WINDOWS_COMMON_AUTO_CRITICAL_SECTION_H__ +#define CLIENT_WINDOWS_COMMON_AUTO_CRITICAL_SECTION_H__ + +#include + +namespace google_breakpad { + +// Automatically enters the critical section in the constructor and leaves +// the critical section in the destructor. +class AutoCriticalSection { + public: + // Creates a new instance with the given critical section object + // and enters the critical section immediately. + explicit AutoCriticalSection(CRITICAL_SECTION* cs) : cs_(cs), taken_(false) { + assert(cs_); + Acquire(); + } + + // Destructor: leaves the critical section. + ~AutoCriticalSection() { + if (taken_) { + Release(); + } + } + + // Enters the critical section. Recursive Acquire() calls are not allowed. + void Acquire() { + assert(!taken_); + EnterCriticalSection(cs_); + taken_ = true; + } + + // Leaves the critical section. The caller should not call Release() unless + // the critical seciton has been entered already. + void Release() { + assert(taken_); + taken_ = false; + LeaveCriticalSection(cs_); + } + + private: + // Disable copy ctor and operator=. + AutoCriticalSection(const AutoCriticalSection&); + AutoCriticalSection& operator=(const AutoCriticalSection&); + + CRITICAL_SECTION* cs_; + bool taken_; +}; + +} // namespace google_breakpad + +#endif // CLIENT_WINDOWS_COMMON_AUTO_CRITICAL_SECTION_H__ diff --git a/shared/sentry/external/breakpad/src/client/windows/common/ipc_protocol.h b/shared/sentry/external/breakpad/src/client/windows/common/ipc_protocol.h new file mode 100644 index 000000000..c74868198 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/common/ipc_protocol.h @@ -0,0 +1,181 @@ +// 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. + +#ifndef CLIENT_WINDOWS_COMMON_IPC_PROTOCOL_H__ +#define CLIENT_WINDOWS_COMMON_IPC_PROTOCOL_H__ + +#include +#include +#include +#include +#include "common/windows/string_utils-inl.h" +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +// Name/value pair for custom client information. +struct CustomInfoEntry { + // Maximum length for name and value for client custom info. + static const int kNameMaxLength = 64; + static const int kValueMaxLength = 64; + + CustomInfoEntry() { + // Putting name and value in initializer list makes VC++ show warning 4351. + set_name(NULL); + set_value(NULL); + } + + CustomInfoEntry(const wchar_t* name_arg, const wchar_t* value_arg) { + set_name(name_arg); + set_value(value_arg); + } + + void set_name(const wchar_t* name_arg) { + if (!name_arg) { + name[0] = L'\0'; + return; + } + WindowsStringUtils::safe_wcscpy(name, kNameMaxLength, name_arg); + } + + void set_value(const wchar_t* value_arg) { + if (!value_arg) { + value[0] = L'\0'; + return; + } + + WindowsStringUtils::safe_wcscpy(value, kValueMaxLength, value_arg); + } + + void set(const wchar_t* name_arg, const wchar_t* value_arg) { + set_name(name_arg); + set_value(value_arg); + } + + wchar_t name[kNameMaxLength]; + wchar_t value[kValueMaxLength]; +}; + +// Constants for the protocol between client and the server. + +// Tags sent with each message indicating the purpose of +// the message. +enum MessageTag { + MESSAGE_TAG_NONE = 0, + MESSAGE_TAG_REGISTRATION_REQUEST = 1, + MESSAGE_TAG_REGISTRATION_RESPONSE = 2, + MESSAGE_TAG_REGISTRATION_ACK = 3, + MESSAGE_TAG_UPLOAD_REQUEST = 4 +}; + +struct CustomClientInfo { + const CustomInfoEntry* entries; + size_t count; +}; + +// Message structure for IPC between crash client and crash server. +struct ProtocolMessage { + ProtocolMessage() + : tag(MESSAGE_TAG_NONE), + id(0), + dump_type(MiniDumpNormal), + thread_id(0), + exception_pointers(NULL), + assert_info(NULL), + custom_client_info(), + dump_request_handle(NULL), + dump_generated_handle(NULL), + server_alive_handle(NULL) { + } + + // Creates an instance with the given parameters. + ProtocolMessage(MessageTag arg_tag, + DWORD arg_id, + MINIDUMP_TYPE arg_dump_type, + DWORD* arg_thread_id, + EXCEPTION_POINTERS** arg_exception_pointers, + MDRawAssertionInfo* arg_assert_info, + const CustomClientInfo& custom_info, + HANDLE arg_dump_request_handle, + HANDLE arg_dump_generated_handle, + HANDLE arg_server_alive) + : tag(arg_tag), + id(arg_id), + dump_type(arg_dump_type), + thread_id(arg_thread_id), + exception_pointers(arg_exception_pointers), + assert_info(arg_assert_info), + custom_client_info(custom_info), + dump_request_handle(arg_dump_request_handle), + dump_generated_handle(arg_dump_generated_handle), + server_alive_handle(arg_server_alive) { + } + + // Tag in the message. + MessageTag tag; + + // The id for this message. This may be either a process id or a crash id + // depending on the type of message. + DWORD id; + + // Dump type requested. + MINIDUMP_TYPE dump_type; + + // Client thread id pointer. + DWORD* thread_id; + + // Exception information. + EXCEPTION_POINTERS** exception_pointers; + + // Assert information in case of an invalid parameter or + // pure call failure. + MDRawAssertionInfo* assert_info; + + // Custom client information. + CustomClientInfo custom_client_info; + + // Handle to signal the crash event. + HANDLE dump_request_handle; + + // Handle to check if server is done generating crash. + HANDLE dump_generated_handle; + + // Handle to a mutex that becomes signaled (WAIT_ABANDONED) + // if server process goes down. + HANDLE server_alive_handle; + + private: + // Disable copy ctor and operator=. + ProtocolMessage(const ProtocolMessage& msg); + ProtocolMessage& operator=(const ProtocolMessage& msg); +}; + +} // namespace google_breakpad + +#endif // CLIENT_WINDOWS_COMMON_IPC_PROTOCOL_H__ diff --git a/shared/sentry/external/breakpad/src/client/windows/crash_generation/ReadMe.txt b/shared/sentry/external/breakpad/src/client/windows/crash_generation/ReadMe.txt new file mode 100644 index 000000000..b54d0e11b --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/crash_generation/ReadMe.txt @@ -0,0 +1,58 @@ +========================================================================= + State machine transitions for the Crash Generation Server +========================================================================= + +========================================================================= + | + STATE | ACTIONS + | +========================================================================= + ERROR | Clean up resources used to serve clients. + | Always remain in ERROR state. +------------------------------------------------------------------------- + INITIAL | Connect to the pipe asynchronously. + | If connection is successfully queued up asynchronously, + | go into CONNECTING state. + | If connection is done synchronously, go into CONNECTED + | state. + | For any unexpected problems, go into ERROR state. +------------------------------------------------------------------------- + CONNECTING | Get the result of async connection request. + | If I/O is still incomplete, remain in the CONNECTING + | state. + | If connection is complete, go into CONNECTED state. + | For any unexpected problems, go into DISCONNECTING state. +------------------------------------------------------------------------- + CONNECTED | Read from the pipe asynchronously. + | If read request is successfully queued up asynchronously, + | go into READING state. + | For any unexpected problems, go into DISCONNECTING state. +------------------------------------------------------------------------- + READING | Get the result of async read request. + | If read is done, go into READ_DONE state. + | For any unexpected problems, go into DISCONNECTING state. +------------------------------------------------------------------------- + READ_DONE | Register the client, prepare the reply and write the + | reply to the pipe asynchronously. + | If write request is successfully queued up asynchronously, + | go into WRITING state. + | For any unexpected problems, go into DISCONNECTING state. +------------------------------------------------------------------------- + WRITING | Get the result of the async write request. + | If write is done, go into WRITE_DONE state. + | For any unexpected problems, go into DISCONNECTING state. +------------------------------------------------------------------------- + WRITE_DONE | Read from the pipe asynchronously (for an ACK). + | If read request is successfully queued up asynchonously, + | go into READING_ACK state. + | For any unexpected problems, go into DISCONNECTING state. +------------------------------------------------------------------------- + READING_ACK | Get the result of the async read request. + | If read is done, perform action for successful client + | connection. + | Go into DISCONNECTING state. +------------------------------------------------------------------------- + DISCONNECTING | Disconnect from the pipe, reset the event and go into + | INITIAL state and signal the event again. If anything + | fails, go into ERROR state. +========================================================================= diff --git a/shared/sentry/external/breakpad/src/client/windows/crash_generation/client_info.cc b/shared/sentry/external/breakpad/src/client/windows/crash_generation/client_info.cc new file mode 100644 index 000000000..ed3126381 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/crash_generation/client_info.cc @@ -0,0 +1,223 @@ +// 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 "client/windows/crash_generation/client_info.h" +#include "client/windows/common/ipc_protocol.h" + +static const wchar_t kCustomInfoProcessUptimeName[] = L"ptime"; +static const size_t kMaxCustomInfoEntries = 4096; + +namespace google_breakpad { + +ClientInfo::ClientInfo(CrashGenerationServer* crash_server, + DWORD pid, + MINIDUMP_TYPE dump_type, + DWORD* thread_id, + EXCEPTION_POINTERS** ex_info, + MDRawAssertionInfo* assert_info, + const CustomClientInfo& custom_client_info) + : crash_server_(crash_server), + pid_(pid), + dump_type_(dump_type), + ex_info_(ex_info), + assert_info_(assert_info), + custom_client_info_(custom_client_info), + thread_id_(thread_id), + process_handle_(NULL), + dump_requested_handle_(NULL), + dump_generated_handle_(NULL), + dump_request_wait_handle_(NULL), + process_exit_wait_handle_(NULL), + crash_id_(NULL) { + GetSystemTimeAsFileTime(&start_time_); +} + +bool ClientInfo::Initialize() { + process_handle_ = OpenProcess(GENERIC_ALL, FALSE, pid_); + if (!process_handle_) { + return false; + } + + // The crash_id will be the low order word of the process creation time. + FILETIME creation_time, exit_time, kernel_time, user_time; + if (GetProcessTimes(process_handle_, &creation_time, &exit_time, + &kernel_time, &user_time)) { + start_time_ = creation_time; + } + crash_id_ = start_time_.dwLowDateTime; + + dump_requested_handle_ = CreateEvent(NULL, // Security attributes. + TRUE, // Manual reset. + FALSE, // Initial state. + NULL); // Name. + if (!dump_requested_handle_) { + return false; + } + + dump_generated_handle_ = CreateEvent(NULL, // Security attributes. + TRUE, // Manual reset. + FALSE, // Initial state. + NULL); // Name. + return dump_generated_handle_ != NULL; +} + +void ClientInfo::UnregisterDumpRequestWaitAndBlockUntilNoPending() { + if (dump_request_wait_handle_) { + // Wait for callbacks that might already be running to finish. + UnregisterWaitEx(dump_request_wait_handle_, INVALID_HANDLE_VALUE); + dump_request_wait_handle_ = NULL; + } +} + +void ClientInfo::UnregisterProcessExitWait(bool block_until_no_pending) { + if (process_exit_wait_handle_) { + if (block_until_no_pending) { + // Wait for the callback that might already be running to finish. + UnregisterWaitEx(process_exit_wait_handle_, INVALID_HANDLE_VALUE); + } else { + UnregisterWait(process_exit_wait_handle_); + } + process_exit_wait_handle_ = NULL; + } +} + +ClientInfo::~ClientInfo() { + // Waiting for the callback to finish here is safe because ClientInfo's are + // never destroyed from the dump request handling callback. + UnregisterDumpRequestWaitAndBlockUntilNoPending(); + + // This is a little tricky because ClientInfo's may be destroyed by the same + // callback (OnClientEnd) and waiting for it to finish will cause a deadlock. + // Regardless of this complication, wait for any running callbacks to finish + // so that the common case is properly handled. In order to avoid deadlocks, + // the OnClientEnd callback must call UnregisterProcessExitWait(false) + // before deleting the ClientInfo. + UnregisterProcessExitWait(true); + + if (process_handle_) { + CloseHandle(process_handle_); + } + + if (dump_requested_handle_) { + CloseHandle(dump_requested_handle_); + } + + if (dump_generated_handle_) { + CloseHandle(dump_generated_handle_); + } +} + +bool ClientInfo::GetClientExceptionInfo(EXCEPTION_POINTERS** ex_info) const { + SIZE_T bytes_count = 0; + if (!ReadProcessMemory(process_handle_, + ex_info_, + ex_info, + sizeof(*ex_info), + &bytes_count)) { + return false; + } + + return bytes_count == sizeof(*ex_info); +} + +bool ClientInfo::GetClientThreadId(DWORD* thread_id) const { + SIZE_T bytes_count = 0; + if (!ReadProcessMemory(process_handle_, + thread_id_, + thread_id, + sizeof(*thread_id), + &bytes_count)) { + return false; + } + + return bytes_count == sizeof(*thread_id); +} + +void ClientInfo::SetProcessUptime() { + FILETIME now = {0}; + GetSystemTimeAsFileTime(&now); + + ULARGE_INTEGER time_start; + time_start.HighPart = start_time_.dwHighDateTime; + time_start.LowPart = start_time_.dwLowDateTime; + + ULARGE_INTEGER time_now; + time_now.HighPart = now.dwHighDateTime; + time_now.LowPart = now.dwLowDateTime; + + // Calculate the delay and convert it from 100-nanoseconds to milliseconds. + __int64 delay = (time_now.QuadPart - time_start.QuadPart) / 10 / 1000; + + // Convert it to a string. + wchar_t* value = custom_info_entries_.get()[custom_client_info_.count].value; + _i64tow_s(delay, value, CustomInfoEntry::kValueMaxLength, 10); +} + +bool ClientInfo::PopulateCustomInfo() { + if (custom_client_info_.count > kMaxCustomInfoEntries) + return false; + + SIZE_T bytes_count = 0; + SIZE_T read_count = sizeof(CustomInfoEntry) * custom_client_info_.count; + + // If the scoped array for custom info already has an array, it will be + // the same size as what we need. This is because the number of custom info + // entries is always the same. So allocate memory only if scoped array has + // a NULL pointer. + if (!custom_info_entries_.get()) { + // Allocate an extra entry for reporting uptime for the client process. + custom_info_entries_.reset( + new CustomInfoEntry[custom_client_info_.count + 1]); + // Use the last element in the array for uptime. + custom_info_entries_.get()[custom_client_info_.count].set_name( + kCustomInfoProcessUptimeName); + } + + if (!ReadProcessMemory(process_handle_, + custom_client_info_.entries, + custom_info_entries_.get(), + read_count, + &bytes_count)) { + return false; + } + + SetProcessUptime(); + return (bytes_count == read_count); +} + +CustomClientInfo ClientInfo::GetCustomInfo() const { + CustomClientInfo custom_info; + custom_info.entries = custom_info_entries_.get(); + // Add 1 to the count from the client process to account for extra entry for + // process uptime. + custom_info.count = custom_client_info_.count + 1; + return custom_info; +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/windows/crash_generation/client_info.h b/shared/sentry/external/breakpad/src/client/windows/crash_generation/client_info.h new file mode 100644 index 000000000..6a8fba31f --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/crash_generation/client_info.h @@ -0,0 +1,177 @@ +// 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. + +#ifndef CLIENT_WINDOWS_CRASH_GENERATION_CLIENT_INFO_H__ +#define CLIENT_WINDOWS_CRASH_GENERATION_CLIENT_INFO_H__ + +#include +#include +#include "client/windows/common/ipc_protocol.h" +#include "common/scoped_ptr.h" +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +class CrashGenerationServer; + +// Abstraction for a crash client process. +class ClientInfo { + public: + // Creates an instance with the given values. Gets the process + // handle for the given process id and creates necessary event + // objects. + ClientInfo(CrashGenerationServer* crash_server, + DWORD pid, + MINIDUMP_TYPE dump_type, + DWORD* thread_id, + EXCEPTION_POINTERS** ex_info, + MDRawAssertionInfo* assert_info, + const CustomClientInfo& custom_client_info); + + ~ClientInfo(); + + CrashGenerationServer* crash_server() const { return crash_server_; } + DWORD pid() const { return pid_; } + MINIDUMP_TYPE dump_type() const { return dump_type_; } + EXCEPTION_POINTERS** ex_info() const { return ex_info_; } + MDRawAssertionInfo* assert_info() const { return assert_info_; } + DWORD* thread_id() const { return thread_id_; } + HANDLE process_handle() const { return process_handle_; } + HANDLE dump_requested_handle() const { return dump_requested_handle_; } + HANDLE dump_generated_handle() const { return dump_generated_handle_; } + DWORD crash_id() const { return crash_id_; } + const CustomClientInfo& custom_client_info() const { + return custom_client_info_; + } + + void set_dump_request_wait_handle(HANDLE value) { + dump_request_wait_handle_ = value; + } + + void set_process_exit_wait_handle(HANDLE value) { + process_exit_wait_handle_ = value; + } + + // Unregister the dump request wait operation and wait for all callbacks + // that might already be running to complete before returning. + void UnregisterDumpRequestWaitAndBlockUntilNoPending(); + + // Unregister the process exit wait operation. If block_until_no_pending is + // true, wait for all callbacks that might already be running to complete + // before returning. + void UnregisterProcessExitWait(bool block_until_no_pending); + + bool Initialize(); + bool GetClientExceptionInfo(EXCEPTION_POINTERS** ex_info) const; + bool GetClientThreadId(DWORD* thread_id) const; + + // Reads the custom information from the client process address space. + bool PopulateCustomInfo(); + + // Returns the client custom information. + CustomClientInfo GetCustomInfo() const; + + private: + // Calcualtes the uptime for the client process, converts it to a string and + // stores it in the last entry of client custom info. + void SetProcessUptime(); + + // Crash generation server. + CrashGenerationServer* crash_server_; + + // Client process ID. + DWORD pid_; + + // Dump type requested by the client. + MINIDUMP_TYPE dump_type_; + + // Address of an EXCEPTION_POINTERS* variable in the client + // process address space that will point to an instance of + // EXCEPTION_POINTERS containing information about crash. + // + // WARNING: Do not dereference these pointers as they are pointers + // in the address space of another process. + EXCEPTION_POINTERS** ex_info_; + + // Address of an instance of MDRawAssertionInfo in the client + // process address space that will contain information about + // non-exception related crashes like invalid parameter assertion + // failures and pure calls. + // + // WARNING: Do not dereference these pointers as they are pointers + // in the address space of another process. + MDRawAssertionInfo* assert_info_; + + // Custom information about the client. + CustomClientInfo custom_client_info_; + + // Contains the custom client info entries read from the client process + // memory. This will be populated only if the method GetClientCustomInfo + // is called. + scoped_array custom_info_entries_; + + // Address of a variable in the client process address space that + // will contain the thread id of the crashing client thread. + // + // WARNING: Do not dereference these pointers as they are pointers + // in the address space of another process. + DWORD* thread_id_; + + // Client process handle. + HANDLE process_handle_; + + // Dump request event handle. + HANDLE dump_requested_handle_; + + // Dump generated event handle. + HANDLE dump_generated_handle_; + + // Wait handle for dump request event. + HANDLE dump_request_wait_handle_; + + // Wait handle for process exit event. + HANDLE process_exit_wait_handle_; + + // Time when the client process started. It is used to determine the uptime + // for the client process when it signals a crash. + FILETIME start_time_; + + // The crash id which can be used to request an upload. This will be the + // value of the low order dword of the process creation time for the process + // being dumped. + DWORD crash_id_; + + // Disallow copy ctor and operator=. + ClientInfo(const ClientInfo& client_info); + ClientInfo& operator=(const ClientInfo& client_info); +}; + +} // namespace google_breakpad + +#endif // CLIENT_WINDOWS_CRASH_GENERATION_CLIENT_INFO_H__ diff --git a/shared/sentry/external/breakpad/src/client/windows/crash_generation/crash_generation.gyp b/shared/sentry/external/breakpad/src/client/windows/crash_generation/crash_generation.gyp new file mode 100644 index 000000000..ba343768a --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/crash_generation/crash_generation.gyp @@ -0,0 +1,63 @@ +# Copyright 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. + +{ + 'includes': [ + '../../../build/common.gypi', + ], + 'targets': [ + { + 'target_name': 'crash_generation_server', + 'type': 'static_library', + 'sources': [ + 'client_info.cc', + 'crash_generation_server.cc', + 'minidump_generator.cc', + 'client_info.h', + 'crash_generation_client.h', + 'crash_generation_server.h', + 'minidump_generator.h', + ], + 'dependencies': [ + '../breakpad_client.gyp:common' + ], + }, + { + 'target_name': 'crash_generation_client', + 'type': 'static_library', + 'include_dirs': [ + '<(DEPTH)', + ], + 'sources': [ + 'crash_generation_client.h', + 'crash_generation_client.cc', + 'crash_generation_server.h', + ], + }, + ], +} diff --git a/shared/sentry/external/breakpad/src/client/windows/crash_generation/crash_generation_client.cc b/shared/sentry/external/breakpad/src/client/windows/crash_generation/crash_generation_client.cc new file mode 100644 index 000000000..3ba5d4e4f --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/crash_generation/crash_generation_client.cc @@ -0,0 +1,405 @@ +// 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 "client/windows/crash_generation/crash_generation_client.h" +#include +#include +#include "client/windows/common/ipc_protocol.h" + +namespace google_breakpad { + +const int kPipeBusyWaitTimeoutMs = 2000; + +#ifdef _DEBUG +const DWORD kWaitForServerTimeoutMs = INFINITE; +#else +const DWORD kWaitForServerTimeoutMs = 15000; +#endif + +const int kPipeConnectMaxAttempts = 2; + +const DWORD kPipeDesiredAccess = FILE_READ_DATA | + FILE_WRITE_DATA | + FILE_WRITE_ATTRIBUTES; + +const DWORD kPipeFlagsAndAttributes = SECURITY_IDENTIFICATION | + SECURITY_SQOS_PRESENT; + +const DWORD kPipeMode = PIPE_READMODE_MESSAGE; + +const size_t kWaitEventCount = 2; + +// This function is orphan for production code. It can be used +// for debugging to help repro some scenarios like the client +// is slow in writing to the pipe after connecting, the client +// is slow in reading from the pipe after writing, etc. The parameter +// overlapped below is not used and it is present to match the signature +// of this function to TransactNamedPipe Win32 API. Uncomment if needed +// for debugging. +/** +static bool TransactNamedPipeDebugHelper(HANDLE pipe, + const void* in_buffer, + DWORD in_size, + void* out_buffer, + DWORD out_size, + DWORD* bytes_count, + LPOVERLAPPED) { + // Uncomment the next sleep to create a gap before writing + // to pipe. + // Sleep(5000); + + if (!WriteFile(pipe, + in_buffer, + in_size, + bytes_count, + NULL)) { + return false; + } + + // Uncomment the next sleep to create a gap between write + // and read. + // Sleep(5000); + + return ReadFile(pipe, out_buffer, out_size, bytes_count, NULL) != FALSE; +} +**/ + +CrashGenerationClient::CrashGenerationClient( + const wchar_t* pipe_name, + MINIDUMP_TYPE dump_type, + const CustomClientInfo* custom_info) + : pipe_name_(pipe_name), + pipe_handle_(NULL), + custom_info_(), + dump_type_(dump_type), + crash_event_(NULL), + crash_generated_(NULL), + server_alive_(NULL), + server_process_id_(0), + thread_id_(0), + exception_pointers_(NULL) { + memset(&assert_info_, 0, sizeof(assert_info_)); + if (custom_info) { + custom_info_ = *custom_info; + } +} + +CrashGenerationClient::CrashGenerationClient( + HANDLE pipe_handle, + MINIDUMP_TYPE dump_type, + const CustomClientInfo* custom_info) + : pipe_name_(), + pipe_handle_(pipe_handle), + custom_info_(), + dump_type_(dump_type), + crash_event_(NULL), + crash_generated_(NULL), + server_alive_(NULL), + server_process_id_(0), + thread_id_(0), + exception_pointers_(NULL) { + memset(&assert_info_, 0, sizeof(assert_info_)); + if (custom_info) { + custom_info_ = *custom_info; + } +} + +CrashGenerationClient::~CrashGenerationClient() { + if (crash_event_) { + CloseHandle(crash_event_); + } + + if (crash_generated_) { + CloseHandle(crash_generated_); + } + + if (server_alive_) { + CloseHandle(server_alive_); + } +} + +// Performs the registration step with the server process. +// The registration step involves communicating with the server +// via a named pipe. The client sends the following pieces of +// data to the server: +// +// * Message tag indicating the client is requesting registration. +// * Process id of the client process. +// * Address of a DWORD variable in the client address space +// that will contain the thread id of the client thread that +// caused the crash. +// * Address of a EXCEPTION_POINTERS* variable in the client +// address space that will point to an instance of EXCEPTION_POINTERS +// when the crash happens. +// * Address of an instance of MDRawAssertionInfo that will contain +// relevant information in case of non-exception crashes like assertion +// failures and pure calls. +// +// In return the client expects the following information from the server: +// +// * Message tag indicating successful registration. +// * Server process id. +// * Handle to an object that client can signal to request dump +// generation from the server. +// * Handle to an object that client can wait on after requesting +// dump generation for the server to finish dump generation. +// * Handle to a mutex object that client can wait on to make sure +// server is still alive. +// +// If any step of the expected behavior mentioned above fails, the +// registration step is not considered successful and hence out-of-process +// dump generation service is not available. +// +// Returns true if the registration is successful; false otherwise. +bool CrashGenerationClient::Register() { + if (IsRegistered()) { + return true; + } + + HANDLE pipe = ConnectToServer(); + if (!pipe) { + return false; + } + + bool success = RegisterClient(pipe); + CloseHandle(pipe); + return success; +} + +bool CrashGenerationClient::RequestUpload(DWORD crash_id) { + HANDLE pipe = ConnectToServer(); + if (!pipe) { + return false; + } + + CustomClientInfo custom_info = {NULL, 0}; + ProtocolMessage msg(MESSAGE_TAG_UPLOAD_REQUEST, crash_id, + static_cast(NULL), NULL, NULL, NULL, + custom_info, NULL, NULL, NULL); + DWORD bytes_count = 0; + bool success = WriteFile(pipe, &msg, sizeof(msg), &bytes_count, NULL) != 0; + + CloseHandle(pipe); + return success; +} + +HANDLE CrashGenerationClient::ConnectToServer() { + HANDLE pipe = ConnectToPipe(pipe_name_.c_str(), + kPipeDesiredAccess, + kPipeFlagsAndAttributes); + if (!pipe) { + return NULL; + } + + DWORD mode = kPipeMode; + if (!SetNamedPipeHandleState(pipe, &mode, NULL, NULL)) { + CloseHandle(pipe); + pipe = NULL; + } + + return pipe; +} + +bool CrashGenerationClient::RegisterClient(HANDLE pipe) { + ProtocolMessage msg(MESSAGE_TAG_REGISTRATION_REQUEST, + GetCurrentProcessId(), + dump_type_, + &thread_id_, + &exception_pointers_, + &assert_info_, + custom_info_, + NULL, + NULL, + NULL); + ProtocolMessage reply; + DWORD bytes_count = 0; + // The call to TransactNamedPipe below can be changed to a call + // to TransactNamedPipeDebugHelper to help repro some scenarios. + // For details see comments for TransactNamedPipeDebugHelper. + if (!TransactNamedPipe(pipe, + &msg, + sizeof(msg), + &reply, + sizeof(ProtocolMessage), + &bytes_count, + NULL)) { + return false; + } + + if (!ValidateResponse(reply)) { + return false; + } + + ProtocolMessage ack_msg; + ack_msg.tag = MESSAGE_TAG_REGISTRATION_ACK; + + if (!WriteFile(pipe, &ack_msg, sizeof(ack_msg), &bytes_count, NULL)) { + return false; + } + crash_event_ = reply.dump_request_handle; + crash_generated_ = reply.dump_generated_handle; + server_alive_ = reply.server_alive_handle; + server_process_id_ = reply.id; + + return true; +} + +HANDLE CrashGenerationClient::ConnectToPipe(const wchar_t* pipe_name, + DWORD pipe_access, + DWORD flags_attrs) { + if (pipe_handle_) { + HANDLE t = pipe_handle_; + pipe_handle_ = NULL; + return t; + } + + for (int i = 0; i < kPipeConnectMaxAttempts; ++i) { + HANDLE pipe = CreateFile(pipe_name, + pipe_access, + 0, + NULL, + OPEN_EXISTING, + flags_attrs, + NULL); + if (pipe != INVALID_HANDLE_VALUE) { + return pipe; + } + + // Cannot continue retrying if error is something other than + // ERROR_PIPE_BUSY. + if (GetLastError() != ERROR_PIPE_BUSY) { + break; + } + + // Cannot continue retrying if wait on pipe fails. + if (!WaitNamedPipe(pipe_name, kPipeBusyWaitTimeoutMs)) { + break; + } + } + + return NULL; +} + +bool CrashGenerationClient::ValidateResponse( + const ProtocolMessage& msg) const { + return (msg.tag == MESSAGE_TAG_REGISTRATION_RESPONSE) && + (msg.id != 0) && + (msg.dump_request_handle != NULL) && + (msg.dump_generated_handle != NULL) && + (msg.server_alive_handle != NULL); +} + +bool CrashGenerationClient::IsRegistered() const { + return crash_event_ != NULL; +} + +bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info, + MDRawAssertionInfo* assert_info) { + if (!IsRegistered()) { + return false; + } + + exception_pointers_ = ex_info; + thread_id_ = GetCurrentThreadId(); + + if (assert_info) { + memcpy(&assert_info_, assert_info, sizeof(assert_info_)); + } else { + memset(&assert_info_, 0, sizeof(assert_info_)); + } + + return SignalCrashEventAndWait(); +} + +bool CrashGenerationClient::RequestDump(EXCEPTION_POINTERS* ex_info) { + return RequestDump(ex_info, NULL); +} + +bool CrashGenerationClient::RequestDump(MDRawAssertionInfo* assert_info) { + return RequestDump(NULL, assert_info); +} + +bool CrashGenerationClient::SignalCrashEventAndWait() { + assert(crash_event_); + assert(crash_generated_); + assert(server_alive_); + + // Reset the dump generated event before signaling the crash + // event so that the server can set the dump generated event + // once it is done generating the event. + if (!ResetEvent(crash_generated_)) { + return false; + } + + if (!SetEvent(crash_event_)) { + return false; + } + + HANDLE wait_handles[kWaitEventCount] = {crash_generated_, server_alive_}; + + DWORD result = WaitForMultipleObjects(kWaitEventCount, + wait_handles, + FALSE, + kWaitForServerTimeoutMs); + + // Crash dump was successfully generated only if the server + // signaled the crash generated event. + return result == WAIT_OBJECT_0; +} + +HANDLE CrashGenerationClient::DuplicatePipeToClientProcess(const wchar_t* pipe_name, + HANDLE hProcess) { + for (int i = 0; i < kPipeConnectMaxAttempts; ++i) { + HANDLE local_pipe = CreateFile(pipe_name, kPipeDesiredAccess, + 0, NULL, OPEN_EXISTING, + kPipeFlagsAndAttributes, NULL); + if (local_pipe != INVALID_HANDLE_VALUE) { + HANDLE remotePipe = INVALID_HANDLE_VALUE; + if (DuplicateHandle(GetCurrentProcess(), local_pipe, + hProcess, &remotePipe, 0, FALSE, + DUPLICATE_CLOSE_SOURCE | DUPLICATE_SAME_ACCESS)) { + return remotePipe; + } else { + return INVALID_HANDLE_VALUE; + } + } + + // Cannot continue retrying if the error wasn't a busy pipe. + if (GetLastError() != ERROR_PIPE_BUSY) { + return INVALID_HANDLE_VALUE; + } + + if (!WaitNamedPipe(pipe_name, kPipeBusyWaitTimeoutMs)) { + return INVALID_HANDLE_VALUE; + } + } + return INVALID_HANDLE_VALUE; +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/windows/crash_generation/crash_generation_client.h b/shared/sentry/external/breakpad/src/client/windows/crash_generation/crash_generation_client.h new file mode 100644 index 000000000..457f73195 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/crash_generation/crash_generation_client.h @@ -0,0 +1,182 @@ +// 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. + +#ifndef CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ +#define CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ + +#include +#include +#include +#include +#include "client/windows/common/ipc_protocol.h" +#include "common/scoped_ptr.h" + +namespace google_breakpad { + +struct CustomClientInfo; + +// Abstraction of client-side implementation of out of process +// crash generation. +// +// The process that desires to have out-of-process crash dump +// generation service can use this class in the following way: +// +// * Create an instance. +// * Call Register method so that the client tries to register +// with the server process and check the return value. If +// registration is not successful, out-of-process crash dump +// generation will not be available +// * Request dump generation by calling either of the two +// overloaded RequestDump methods - one in case of exceptions +// and the other in case of assertion failures +// +// Note that it is the responsibility of the client code of +// this class to set the unhandled exception filter with the +// system by calling the SetUnhandledExceptionFilter function +// and the client code should explicitly request dump generation. +class CrashGenerationClient { + public: + CrashGenerationClient(const wchar_t* pipe_name, + MINIDUMP_TYPE dump_type, + const CustomClientInfo* custom_info); + + CrashGenerationClient(HANDLE pipe_handle, + MINIDUMP_TYPE dump_type, + const CustomClientInfo* custom_info); + + ~CrashGenerationClient(); + + // Registers the client process with the crash server. + // + // Returns true if the registration is successful; false otherwise. + bool Register(); + + // Requests the crash server to upload a previous dump with the + // given crash id. + bool RequestUpload(DWORD crash_id); + + bool RequestDump(EXCEPTION_POINTERS* ex_info, + MDRawAssertionInfo* assert_info); + + // Requests the crash server to generate a dump with the given + // exception information. + // + // Returns true if the dump was successful; false otherwise. Note that + // if the registration step was not performed or it was not successful, + // false will be returned. + bool RequestDump(EXCEPTION_POINTERS* ex_info); + + // Requests the crash server to generate a dump with the given + // assertion information. + // + // Returns true if the dump was successful; false otherwise. Note that + // if the registration step was not performed or it was not successful, + // false will be returned. + bool RequestDump(MDRawAssertionInfo* assert_info); + + // If the crash generation client is running in a sandbox that prevents it + // from opening the named pipe directly, the server process may open the + // handle and duplicate it into the client process with this helper method. + // Returns INVALID_HANDLE_VALUE on failure. The process must have been opened + // with the PROCESS_DUP_HANDLE access right. + static HANDLE DuplicatePipeToClientProcess(const wchar_t* pipe_name, + HANDLE hProcess); + + private: + // Connects to the appropriate pipe and sets the pipe handle state. + // + // Returns the pipe handle if everything goes well; otherwise Returns NULL. + HANDLE ConnectToServer(); + + // Performs a handshake with the server over the given pipe which should be + // already connected to the server. + // + // Returns true if handshake with the server was successful; false otherwise. + bool RegisterClient(HANDLE pipe); + + // Validates the given server response. + bool ValidateResponse(const ProtocolMessage& msg) const; + + // Returns true if the registration step succeeded; false otherwise. + bool IsRegistered() const; + + // Connects to the given named pipe with given parameters. + // + // Returns true if the connection is successful; false otherwise. + HANDLE ConnectToPipe(const wchar_t* pipe_name, + DWORD pipe_access, + DWORD flags_attrs); + + // Signals the crash event and wait for the server to generate crash. + bool SignalCrashEventAndWait(); + + // Pipe name to use to talk to server. + std::wstring pipe_name_; + + // Pipe handle duplicated from server process. Only valid before + // Register is called. + HANDLE pipe_handle_; + + // Custom client information + CustomClientInfo custom_info_; + + // Type of dump to generate. + MINIDUMP_TYPE dump_type_; + + // Event to signal in case of a crash. + HANDLE crash_event_; + + // Handle to wait on after signaling a crash for the server + // to finish generating crash dump. + HANDLE crash_generated_; + + // Handle to a mutex that will become signaled with WAIT_ABANDONED + // if the server process goes down. + HANDLE server_alive_; + + // Server process id. + DWORD server_process_id_; + + // Id of the thread that caused the crash. + DWORD thread_id_; + + // Exception pointers for an exception crash. + EXCEPTION_POINTERS* exception_pointers_; + + // Assertion info for an invalid parameter or pure call crash. + MDRawAssertionInfo assert_info_; + + // Disable copy ctor and operator=. + CrashGenerationClient(const CrashGenerationClient& crash_client); + CrashGenerationClient& operator=(const CrashGenerationClient& crash_client); +}; + +} // namespace google_breakpad + +#endif // CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_CLIENT_H_ diff --git a/shared/sentry/external/breakpad/src/client/windows/crash_generation/crash_generation_server.cc b/shared/sentry/external/breakpad/src/client/windows/crash_generation/crash_generation_server.cc new file mode 100644 index 000000000..0af213ba2 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/crash_generation/crash_generation_server.cc @@ -0,0 +1,943 @@ +// 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 "client/windows/crash_generation/crash_generation_server.h" +#include +#include +#include +#include "client/windows/common/auto_critical_section.h" +#include "common/scoped_ptr.h" + +#include "client/windows/crash_generation/client_info.h" + +namespace google_breakpad { + +// Output buffer size. +static const size_t kOutBufferSize = 64; + +// Input buffer size. +static const size_t kInBufferSize = 64; + +// Access flags for the client on the dump request event. +static const DWORD kDumpRequestEventAccess = EVENT_MODIFY_STATE; + +// Access flags for the client on the dump generated event. +static const DWORD kDumpGeneratedEventAccess = EVENT_MODIFY_STATE | + SYNCHRONIZE; + +// Access flags for the client on the mutex. +static const DWORD kMutexAccess = SYNCHRONIZE; + +// Attribute flags for the pipe. +static const DWORD kPipeAttr = FILE_FLAG_FIRST_PIPE_INSTANCE | + PIPE_ACCESS_DUPLEX | + FILE_FLAG_OVERLAPPED; + +// Mode for the pipe. +static const DWORD kPipeMode = PIPE_TYPE_MESSAGE | + PIPE_READMODE_MESSAGE | + PIPE_WAIT; + +// For pipe I/O, execute the callback in the wait thread itself, +// since the callback does very little work. The callback executes +// the code for one of the states of the server state machine and +// the code for all of the states perform async I/O and hence +// finish very quickly. +static const ULONG kPipeIOThreadFlags = WT_EXECUTEINWAITTHREAD; + +// Dump request threads will, most likely, generate dumps. That may +// take some time to finish, so specify WT_EXECUTELONGFUNCTION flag. +static const ULONG kDumpRequestThreadFlags = WT_EXECUTEINWAITTHREAD | + WT_EXECUTELONGFUNCTION; + +static bool IsClientRequestValid(const ProtocolMessage& msg) { + return msg.tag == MESSAGE_TAG_UPLOAD_REQUEST || + (msg.tag == MESSAGE_TAG_REGISTRATION_REQUEST && + msg.id != 0 && + msg.thread_id != NULL && + msg.exception_pointers != NULL && + msg.assert_info != NULL); +} + +#ifndef NDEBUG +static bool CheckForIOIncomplete(bool success) { + // We should never get an I/O incomplete since we should not execute this + // unless the operation has finished and the overlapped event is signaled. If + // we do get INCOMPLETE, we have a bug in our code. + return success ? false : (GetLastError() == ERROR_IO_INCOMPLETE); +} +#endif + +CrashGenerationServer::CrashGenerationServer( + const std::wstring& pipe_name, + SECURITY_ATTRIBUTES* pipe_sec_attrs, + OnClientConnectedCallback connect_callback, + void* connect_context, + OnClientDumpRequestCallback dump_callback, + void* dump_context, + OnClientExitedCallback exit_callback, + void* exit_context, + OnClientUploadRequestCallback upload_request_callback, + void* upload_context, + bool generate_dumps, + const std::wstring* dump_path) + : pipe_name_(pipe_name), + pipe_sec_attrs_(pipe_sec_attrs), + pipe_(NULL), + pipe_wait_handle_(NULL), + server_alive_handle_(NULL), + connect_callback_(connect_callback), + connect_context_(connect_context), + dump_callback_(dump_callback), + dump_context_(dump_context), + exit_callback_(exit_callback), + exit_context_(exit_context), + upload_request_callback_(upload_request_callback), + upload_context_(upload_context), + generate_dumps_(generate_dumps), + pre_fetch_custom_info_(true), + dump_path_(dump_path ? *dump_path : L""), + server_state_(IPC_SERVER_STATE_UNINITIALIZED), + shutting_down_(false), + overlapped_(), + client_info_(NULL) { + InitializeCriticalSection(&sync_); +} + +// This should never be called from the OnPipeConnected callback. +// Otherwise the UnregisterWaitEx call below will cause a deadlock. +CrashGenerationServer::~CrashGenerationServer() { + // New scope to release the lock automatically. + { + // Make sure no clients are added or removed beyond this point. + // Before adding or removing any clients, the critical section + // must be entered and the shutting_down_ flag checked. The + // critical section is then exited only after the clients_ list + // modifications are done and the list is in a consistent state. + AutoCriticalSection lock(&sync_); + + // Indicate to existing threads that server is shutting down. + shutting_down_ = true; + } + // No one will modify the clients_ list beyond this point - + // not even from another thread. + + // Even if there are no current worker threads running, it is possible that + // an I/O request is pending on the pipe right now but not yet done. + // In fact, it's very likely this is the case unless we are in an ERROR + // state. If we don't wait for the pending I/O to be done, then when the I/O + // completes, it may write to invalid memory. AppVerifier will flag this + // problem too. So we disconnect from the pipe and then wait for the server + // to get into error state so that the pending I/O will fail and get + // cleared. + DisconnectNamedPipe(pipe_); + int num_tries = 100; + while (num_tries-- && server_state_ != IPC_SERVER_STATE_ERROR) { + Sleep(10); + } + + // Unregister wait on the pipe. + if (pipe_wait_handle_) { + // Wait for already executing callbacks to finish. + UnregisterWaitEx(pipe_wait_handle_, INVALID_HANDLE_VALUE); + } + + // Close the pipe to avoid further client connections. + if (pipe_) { + CloseHandle(pipe_); + } + + // Request all ClientInfo objects to unregister all waits. + // No need to enter the critical section because no one is allowed to modify + // the clients_ list once the shutting_down_ flag is set. + std::list::iterator iter; + for (iter = clients_.begin(); iter != clients_.end(); ++iter) { + ClientInfo* client_info = *iter; + // Unregister waits. Wait for already executing callbacks to finish. + // Unregister the client process exit wait first and only then unregister + // the dump request wait. The reason is that the OnClientExit callback + // also unregisters the dump request wait and such a race (doing the same + // unregistration from two threads) is undesirable. + client_info->UnregisterProcessExitWait(true); + client_info->UnregisterDumpRequestWaitAndBlockUntilNoPending(); + + // Destroying the ClientInfo here is safe because all wait operations for + // this ClientInfo were unregistered and no pending or running callbacks + // for this ClientInfo can possible exist (block_until_no_pending option + // was used). + delete client_info; + } + + if (server_alive_handle_) { + // Release the mutex before closing the handle so that clients requesting + // dumps wait for a long time for the server to generate a dump. + ReleaseMutex(server_alive_handle_); + CloseHandle(server_alive_handle_); + } + + if (overlapped_.hEvent) { + CloseHandle(overlapped_.hEvent); + } + + DeleteCriticalSection(&sync_); +} + +bool CrashGenerationServer::Start() { + if (server_state_ != IPC_SERVER_STATE_UNINITIALIZED) { + return false; + } + + server_state_ = IPC_SERVER_STATE_INITIAL; + + server_alive_handle_ = CreateMutex(NULL, TRUE, NULL); + if (!server_alive_handle_) { + return false; + } + + // Event to signal the client connection and pipe reads and writes. + overlapped_.hEvent = CreateEvent(NULL, // Security descriptor. + TRUE, // Manual reset. + FALSE, // Initially nonsignaled. + NULL); // Name. + if (!overlapped_.hEvent) { + return false; + } + + // Register a callback with the thread pool for the client connection. + if (!RegisterWaitForSingleObject(&pipe_wait_handle_, + overlapped_.hEvent, + OnPipeConnected, + this, + INFINITE, + kPipeIOThreadFlags)) { + return false; + } + + pipe_ = CreateNamedPipe(pipe_name_.c_str(), + kPipeAttr, + kPipeMode, + 1, + kOutBufferSize, + kInBufferSize, + 0, + pipe_sec_attrs_); + if (pipe_ == INVALID_HANDLE_VALUE) { + return false; + } + + // Kick-start the state machine. This will initiate an asynchronous wait + // for client connections. + if (!SetEvent(overlapped_.hEvent)) { + server_state_ = IPC_SERVER_STATE_ERROR; + return false; + } + + // If we are in error state, it's because we failed to start listening. + return true; +} + +// If the server thread serving clients ever gets into the +// ERROR state, reset the event, close the pipe and remain +// in the error state forever. Error state means something +// that we didn't account for has happened, and it's dangerous +// to do anything unknowingly. +void CrashGenerationServer::HandleErrorState() { + assert(server_state_ == IPC_SERVER_STATE_ERROR); + + // If the server is shutting down anyway, don't clean up + // here since shut down process will clean up. + if (shutting_down_) { + return; + } + + if (pipe_wait_handle_) { + UnregisterWait(pipe_wait_handle_); + pipe_wait_handle_ = NULL; + } + + if (pipe_) { + CloseHandle(pipe_); + pipe_ = NULL; + } + + if (overlapped_.hEvent) { + CloseHandle(overlapped_.hEvent); + overlapped_.hEvent = NULL; + } +} + +// When the server thread serving clients is in the INITIAL state, +// try to connect to the pipe asynchronously. If the connection +// finishes synchronously, directly go into the CONNECTED state; +// otherwise go into the CONNECTING state. For any problems, go +// into the ERROR state. +void CrashGenerationServer::HandleInitialState() { + assert(server_state_ == IPC_SERVER_STATE_INITIAL); + + if (!ResetEvent(overlapped_.hEvent)) { + EnterErrorState(); + return; + } + + bool success = ConnectNamedPipe(pipe_, &overlapped_) != FALSE; + DWORD error_code = success ? ERROR_SUCCESS : GetLastError(); + + // From MSDN, it is not clear that when ConnectNamedPipe is used + // in an overlapped mode, will it ever return non-zero value, and + // if so, in what cases. + assert(!success); + + switch (error_code) { + case ERROR_IO_PENDING: + EnterStateWhenSignaled(IPC_SERVER_STATE_CONNECTING); + break; + + case ERROR_PIPE_CONNECTED: + EnterStateImmediately(IPC_SERVER_STATE_CONNECTED); + break; + + default: + EnterErrorState(); + break; + } +} + +// When the server thread serving the clients is in the CONNECTING state, +// try to get the result of the asynchronous connection request using +// the OVERLAPPED object. If the result indicates the connection is done, +// go into the CONNECTED state. If the result indicates I/O is still +// INCOMPLETE, remain in the CONNECTING state. For any problems, +// go into the DISCONNECTING state. +void CrashGenerationServer::HandleConnectingState() { + assert(server_state_ == IPC_SERVER_STATE_CONNECTING); + + DWORD bytes_count = 0; + bool success = GetOverlappedResult(pipe_, + &overlapped_, + &bytes_count, + FALSE) != FALSE; + DWORD error_code = success ? ERROR_SUCCESS : GetLastError(); + + if (success) { + EnterStateImmediately(IPC_SERVER_STATE_CONNECTED); + } else if (error_code != ERROR_IO_INCOMPLETE) { + EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING); + } else { + // remain in CONNECTING state + } +} + +// When the server thread serving the clients is in the CONNECTED state, +// try to issue an asynchronous read from the pipe. If read completes +// synchronously or if I/O is pending then go into the READING state. +// For any problems, go into the DISCONNECTING state. +void CrashGenerationServer::HandleConnectedState() { + assert(server_state_ == IPC_SERVER_STATE_CONNECTED); + + DWORD bytes_count = 0; + memset(&msg_, 0, sizeof(msg_)); + bool success = ReadFile(pipe_, + &msg_, + sizeof(msg_), + &bytes_count, + &overlapped_) != FALSE; + DWORD error_code = success ? ERROR_SUCCESS : GetLastError(); + + // Note that the asynchronous read issued above can finish before the + // code below executes. But, it is okay to change state after issuing + // the asynchronous read. This is because even if the asynchronous read + // is done, the callback for it would not be executed until the current + // thread finishes its execution. + if (success || error_code == ERROR_IO_PENDING) { + EnterStateWhenSignaled(IPC_SERVER_STATE_READING); + } else { + EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING); + } +} + +// When the server thread serving the clients is in the READING state, +// try to get the result of the async read. If async read is done, +// go into the READ_DONE state. For any problems, go into the +// DISCONNECTING state. +void CrashGenerationServer::HandleReadingState() { + assert(server_state_ == IPC_SERVER_STATE_READING); + + DWORD bytes_count = 0; + bool success = GetOverlappedResult(pipe_, + &overlapped_, + &bytes_count, + FALSE) != FALSE; + if (success && bytes_count == sizeof(ProtocolMessage)) { + EnterStateImmediately(IPC_SERVER_STATE_READ_DONE); + return; + } + + assert(!CheckForIOIncomplete(success)); + EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING); +} + +// When the server thread serving the client is in the READ_DONE state, +// validate the client's request message, register the client by +// creating appropriate objects and prepare the response. Then try to +// write the response to the pipe asynchronously. If that succeeds, +// go into the WRITING state. For any problems, go into the DISCONNECTING +// state. +void CrashGenerationServer::HandleReadDoneState() { + assert(server_state_ == IPC_SERVER_STATE_READ_DONE); + + if (!IsClientRequestValid(msg_)) { + EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING); + return; + } + + if (msg_.tag == MESSAGE_TAG_UPLOAD_REQUEST) { + if (upload_request_callback_) + upload_request_callback_(upload_context_, msg_.id); + EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING); + return; + } + + scoped_ptr client_info( + new ClientInfo(this, + msg_.id, + msg_.dump_type, + msg_.thread_id, + msg_.exception_pointers, + msg_.assert_info, + msg_.custom_client_info)); + + if (!client_info->Initialize()) { + EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING); + return; + } + + // Issues an asynchronous WriteFile call if successful. + // Iff successful, assigns ownership of the client_info pointer to the server + // instance, in which case we must be sure not to free it in this function. + if (!RespondToClient(client_info.get())) { + EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING); + return; + } + + // This is only valid as long as it can be found in the clients_ list + client_info_ = client_info.release(); + + // Note that the asynchronous write issued by RespondToClient function + // can finish before the code below executes. But it is okay to change + // state after issuing the asynchronous write. This is because even if + // the asynchronous write is done, the callback for it would not be + // executed until the current thread finishes its execution. + EnterStateWhenSignaled(IPC_SERVER_STATE_WRITING); +} + +// When the server thread serving the clients is in the WRITING state, +// try to get the result of the async write. If the async write is done, +// go into the WRITE_DONE state. For any problems, go into the +// DISONNECTING state. +void CrashGenerationServer::HandleWritingState() { + assert(server_state_ == IPC_SERVER_STATE_WRITING); + + DWORD bytes_count = 0; + bool success = GetOverlappedResult(pipe_, + &overlapped_, + &bytes_count, + FALSE) != FALSE; + if (success) { + EnterStateImmediately(IPC_SERVER_STATE_WRITE_DONE); + return; + } + + assert(!CheckForIOIncomplete(success)); + EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING); +} + +// When the server thread serving the clients is in the WRITE_DONE state, +// try to issue an async read on the pipe. If the read completes synchronously +// or if I/O is still pending then go into the READING_ACK state. For any +// issues, go into the DISCONNECTING state. +void CrashGenerationServer::HandleWriteDoneState() { + assert(server_state_ == IPC_SERVER_STATE_WRITE_DONE); + + DWORD bytes_count = 0; + bool success = ReadFile(pipe_, + &msg_, + sizeof(msg_), + &bytes_count, + &overlapped_) != FALSE; + DWORD error_code = success ? ERROR_SUCCESS : GetLastError(); + + if (success) { + EnterStateImmediately(IPC_SERVER_STATE_READING_ACK); + } else if (error_code == ERROR_IO_PENDING) { + EnterStateWhenSignaled(IPC_SERVER_STATE_READING_ACK); + } else { + EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING); + } +} + +// When the server thread serving the clients is in the READING_ACK state, +// try to get result of async read. Go into the DISCONNECTING state. +void CrashGenerationServer::HandleReadingAckState() { + assert(server_state_ == IPC_SERVER_STATE_READING_ACK); + + DWORD bytes_count = 0; + bool success = GetOverlappedResult(pipe_, + &overlapped_, + &bytes_count, + FALSE) != FALSE; + if (success) { + // The connection handshake with the client is now complete; perform + // the callback. + if (connect_callback_) { + // Note that there is only a single copy of the ClientInfo of the + // currently connected client. However it is being referenced from + // two different places: + // - the client_info_ member + // - the clients_ list + // The lifetime of this ClientInfo depends on the lifetime of the + // client process - basically it can go away at any time. + // However, as long as it is referenced by the clients_ list it + // is guaranteed to be valid. Enter the critical section and check + // to see whether the client_info_ can be found in the list. + // If found, execute the callback and only then leave the critical + // section. + AutoCriticalSection lock(&sync_); + + bool client_is_still_alive = false; + std::list::iterator iter; + for (iter = clients_.begin(); iter != clients_.end(); ++iter) { + if (client_info_ == *iter) { + client_is_still_alive = true; + break; + } + } + + if (client_is_still_alive) { + connect_callback_(connect_context_, client_info_); + } + } + } else { + assert(!CheckForIOIncomplete(success)); + } + + EnterStateImmediately(IPC_SERVER_STATE_DISCONNECTING); +} + +// When the server thread serving the client is in the DISCONNECTING state, +// disconnect from the pipe and reset the event. If anything fails, go into +// the ERROR state. If it goes well, go into the INITIAL state and set the +// event to start all over again. +void CrashGenerationServer::HandleDisconnectingState() { + assert(server_state_ == IPC_SERVER_STATE_DISCONNECTING); + + // Done serving the client. + client_info_ = NULL; + + overlapped_.Internal = NULL; + overlapped_.InternalHigh = NULL; + overlapped_.Offset = 0; + overlapped_.OffsetHigh = 0; + overlapped_.Pointer = NULL; + + if (!ResetEvent(overlapped_.hEvent)) { + EnterErrorState(); + return; + } + + if (!DisconnectNamedPipe(pipe_)) { + EnterErrorState(); + return; + } + + // If the server is shutting down do not connect to the + // next client. + if (shutting_down_) { + return; + } + + EnterStateImmediately(IPC_SERVER_STATE_INITIAL); +} + +void CrashGenerationServer::EnterErrorState() { + SetEvent(overlapped_.hEvent); + server_state_ = IPC_SERVER_STATE_ERROR; +} + +void CrashGenerationServer::EnterStateWhenSignaled(IPCServerState state) { + server_state_ = state; +} + +void CrashGenerationServer::EnterStateImmediately(IPCServerState state) { + server_state_ = state; + + if (!SetEvent(overlapped_.hEvent)) { + server_state_ = IPC_SERVER_STATE_ERROR; + } +} + +bool CrashGenerationServer::PrepareReply(const ClientInfo& client_info, + ProtocolMessage* reply) const { + reply->tag = MESSAGE_TAG_REGISTRATION_RESPONSE; + reply->id = GetCurrentProcessId(); + + if (CreateClientHandles(client_info, reply)) { + return true; + } + + // Closing of remote handles (belonging to a different process) can + // only be done through DuplicateHandle. + if (reply->dump_request_handle) { + DuplicateHandle(client_info.process_handle(), // hSourceProcessHandle + reply->dump_request_handle, // hSourceHandle + NULL, // hTargetProcessHandle + 0, // lpTargetHandle + 0, // dwDesiredAccess + FALSE, // bInheritHandle + DUPLICATE_CLOSE_SOURCE); // dwOptions + reply->dump_request_handle = NULL; + } + + if (reply->dump_generated_handle) { + DuplicateHandle(client_info.process_handle(), // hSourceProcessHandle + reply->dump_generated_handle, // hSourceHandle + NULL, // hTargetProcessHandle + 0, // lpTargetHandle + 0, // dwDesiredAccess + FALSE, // bInheritHandle + DUPLICATE_CLOSE_SOURCE); // dwOptions + reply->dump_generated_handle = NULL; + } + + if (reply->server_alive_handle) { + DuplicateHandle(client_info.process_handle(), // hSourceProcessHandle + reply->server_alive_handle, // hSourceHandle + NULL, // hTargetProcessHandle + 0, // lpTargetHandle + 0, // dwDesiredAccess + FALSE, // bInheritHandle + DUPLICATE_CLOSE_SOURCE); // dwOptions + reply->server_alive_handle = NULL; + } + + return false; +} + +bool CrashGenerationServer::CreateClientHandles(const ClientInfo& client_info, + ProtocolMessage* reply) const { + HANDLE current_process = GetCurrentProcess(); + if (!DuplicateHandle(current_process, + client_info.dump_requested_handle(), + client_info.process_handle(), + &reply->dump_request_handle, + kDumpRequestEventAccess, + FALSE, + 0)) { + return false; + } + + if (!DuplicateHandle(current_process, + client_info.dump_generated_handle(), + client_info.process_handle(), + &reply->dump_generated_handle, + kDumpGeneratedEventAccess, + FALSE, + 0)) { + return false; + } + + if (!DuplicateHandle(current_process, + server_alive_handle_, + client_info.process_handle(), + &reply->server_alive_handle, + kMutexAccess, + FALSE, + 0)) { + return false; + } + + return true; +} + +bool CrashGenerationServer::RespondToClient(ClientInfo* client_info) { + ProtocolMessage reply; + if (!PrepareReply(*client_info, &reply)) { + return false; + } + + DWORD bytes_count = 0; + bool success = WriteFile(pipe_, + &reply, + sizeof(reply), + &bytes_count, + &overlapped_) != FALSE; + DWORD error_code = success ? ERROR_SUCCESS : GetLastError(); + + if (!success && error_code != ERROR_IO_PENDING) { + return false; + } + + // Takes over ownership of client_info. We MUST return true if AddClient + // succeeds. + return AddClient(client_info); +} + +// The server thread servicing the clients runs this method. The method +// implements the state machine described in ReadMe.txt along with the +// helper methods HandleXXXState. +void CrashGenerationServer::HandleConnectionRequest() { + // If the server is shutting down, get into ERROR state, reset the event so + // more workers don't run and return immediately. + if (shutting_down_) { + server_state_ = IPC_SERVER_STATE_ERROR; + ResetEvent(overlapped_.hEvent); + return; + } + + switch (server_state_) { + case IPC_SERVER_STATE_ERROR: + HandleErrorState(); + break; + + case IPC_SERVER_STATE_INITIAL: + HandleInitialState(); + break; + + case IPC_SERVER_STATE_CONNECTING: + HandleConnectingState(); + break; + + case IPC_SERVER_STATE_CONNECTED: + HandleConnectedState(); + break; + + case IPC_SERVER_STATE_READING: + HandleReadingState(); + break; + + case IPC_SERVER_STATE_READ_DONE: + HandleReadDoneState(); + break; + + case IPC_SERVER_STATE_WRITING: + HandleWritingState(); + break; + + case IPC_SERVER_STATE_WRITE_DONE: + HandleWriteDoneState(); + break; + + case IPC_SERVER_STATE_READING_ACK: + HandleReadingAckState(); + break; + + case IPC_SERVER_STATE_DISCONNECTING: + HandleDisconnectingState(); + break; + + default: + assert(false); + // This indicates that we added one more state without + // adding handling code. + server_state_ = IPC_SERVER_STATE_ERROR; + break; + } +} + +bool CrashGenerationServer::AddClient(ClientInfo* client_info) { + HANDLE request_wait_handle = NULL; + if (!RegisterWaitForSingleObject(&request_wait_handle, + client_info->dump_requested_handle(), + OnDumpRequest, + client_info, + INFINITE, + kDumpRequestThreadFlags)) { + return false; + } + + client_info->set_dump_request_wait_handle(request_wait_handle); + + // OnClientEnd will be called when the client process terminates. + HANDLE process_wait_handle = NULL; + if (!RegisterWaitForSingleObject(&process_wait_handle, + client_info->process_handle(), + OnClientEnd, + client_info, + INFINITE, + WT_EXECUTEONLYONCE)) { + return false; + } + + client_info->set_process_exit_wait_handle(process_wait_handle); + + // New scope to hold the lock for the shortest time. + { + AutoCriticalSection lock(&sync_); + if (shutting_down_) { + // If server is shutting down, don't add new clients + return false; + } + clients_.push_back(client_info); + } + + return true; +} + +// static +void CALLBACK CrashGenerationServer::OnPipeConnected(void* context, BOOLEAN) { + assert(context); + + CrashGenerationServer* obj = + reinterpret_cast(context); + obj->HandleConnectionRequest(); +} + +// static +void CALLBACK CrashGenerationServer::OnDumpRequest(void* context, BOOLEAN) { + assert(context); + ClientInfo* client_info = reinterpret_cast(context); + + CrashGenerationServer* crash_server = client_info->crash_server(); + assert(crash_server); + if (crash_server->pre_fetch_custom_info_) { + client_info->PopulateCustomInfo(); + } + crash_server->HandleDumpRequest(*client_info); + + ResetEvent(client_info->dump_requested_handle()); +} + +// static +void CALLBACK CrashGenerationServer::OnClientEnd(void* context, BOOLEAN) { + assert(context); + ClientInfo* client_info = reinterpret_cast(context); + + CrashGenerationServer* crash_server = client_info->crash_server(); + assert(crash_server); + + crash_server->HandleClientProcessExit(client_info); +} + +void CrashGenerationServer::HandleClientProcessExit(ClientInfo* client_info) { + assert(client_info); + + // Must unregister the dump request wait operation and wait for any + // dump requests that might be pending to finish before proceeding + // with the client_info cleanup. + client_info->UnregisterDumpRequestWaitAndBlockUntilNoPending(); + + if (exit_callback_) { + exit_callback_(exit_context_, client_info); + } + + // Start a new scope to release lock automatically. + { + AutoCriticalSection lock(&sync_); + if (shutting_down_) { + // The crash generation server is shutting down and as part of the + // shutdown process it will delete all clients from the clients_ list. + return; + } + clients_.remove(client_info); + } + + // Explicitly unregister the process exit wait using the non-blocking method. + // Otherwise, the destructor will attempt to unregister it using the blocking + // method which will lead to a deadlock because it is being called from the + // callback of the same wait operation + client_info->UnregisterProcessExitWait(false); + + delete client_info; +} + +void CrashGenerationServer::HandleDumpRequest(const ClientInfo& client_info) { + bool execute_callback = true; + // Generate the dump only if it's explicitly requested by the + // server application; otherwise the server might want to generate + // dump in the callback. + std::wstring dump_path; + if (generate_dumps_) { + if (!GenerateDump(client_info, &dump_path)) { + // client proccess terminated or some other error + execute_callback = false; + } + } + + if (dump_callback_ && execute_callback) { + std::wstring* ptr_dump_path = (dump_path == L"") ? NULL : &dump_path; + dump_callback_(dump_context_, &client_info, ptr_dump_path); + } + + SetEvent(client_info.dump_generated_handle()); +} + +bool CrashGenerationServer::GenerateDump(const ClientInfo& client, + std::wstring* dump_path) { + assert(client.pid() != 0); + assert(client.process_handle()); + + // We have to get the address of EXCEPTION_INFORMATION from + // the client process address space. + EXCEPTION_POINTERS* client_ex_info = NULL; + if (!client.GetClientExceptionInfo(&client_ex_info)) { + return false; + } + + DWORD client_thread_id = 0; + if (!client.GetClientThreadId(&client_thread_id)) { + return false; + } + + MinidumpGenerator dump_generator(dump_path_, + client.process_handle(), + client.pid(), + client_thread_id, + GetCurrentThreadId(), + client_ex_info, + client.assert_info(), + client.dump_type(), + true); + + if (!dump_generator.GenerateDumpFile(dump_path)) { + return false; + } + + // If the client requests a full memory dump, we will write a normal mini + // dump and a full memory dump. Both dump files use the same uuid as file + // name prefix. + if (client.dump_type() & MiniDumpWithFullMemory) { + std::wstring full_dump_path; + if (!dump_generator.GenerateFullDumpFile(&full_dump_path)) { + return false; + } + } + + return dump_generator.WriteMinidump(); +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/windows/crash_generation/crash_generation_server.h b/shared/sentry/external/breakpad/src/client/windows/crash_generation/crash_generation_server.h new file mode 100644 index 000000000..0ea90e510 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/crash_generation/crash_generation_server.h @@ -0,0 +1,299 @@ +// 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. + +#ifndef CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_SERVER_H__ +#define CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_SERVER_H__ + +#include +#include +#include "client/windows/common/ipc_protocol.h" +#include "client/windows/crash_generation/minidump_generator.h" +#include "common/scoped_ptr.h" + +namespace google_breakpad { +class ClientInfo; + +// Abstraction for server side implementation of out-of-process crash +// generation protocol for Windows platform only. It generates Windows +// minidump files for client processes that request dump generation. When +// the server is requested to start listening for clients (by calling the +// Start method), it creates a named pipe and waits for the clients to +// register. In response, it hands them event handles that the client can +// signal to request dump generation. When the clients request dump +// generation in this way, the server generates Windows minidump files. +class CrashGenerationServer { + public: + typedef void (*OnClientConnectedCallback)(void* context, + const ClientInfo* client_info); + + typedef void (*OnClientDumpRequestCallback)(void* context, + const ClientInfo* client_info, + const std::wstring* file_path); + + typedef void (*OnClientExitedCallback)(void* context, + const ClientInfo* client_info); + + typedef void (*OnClientUploadRequestCallback)(void* context, + const DWORD crash_id); + + // Creates an instance with the given parameters. + // + // Parameter pipe_name: Name of the Windows named pipe + // Parameter pipe_sec_attrs Security attributes to set on the pipe. Pass + // NULL to use default security on the pipe. By default, the pipe created + // allows Local System, Administrators and the Creator full control and + // the Everyone group read access on the pipe. + // Parameter connect_callback: Callback for a new client connection. + // Parameter connect_context: Context for client connection callback. + // Parameter crash_callback: Callback for a client crash dump request. + // Parameter crash_context: Context for client crash dump request callback. + // Parameter exit_callback: Callback for client process exit. + // Parameter exit_context: Context for client exit callback. + // Parameter generate_dumps: Whether to automatically generate dumps. + // Client code of this class might want to generate dumps explicitly in the + // crash dump request callback. In that case, false can be passed for this + // parameter. + // Parameter dump_path: Path for generating dumps; required only if true is + // passed for generateDumps parameter; NULL can be passed otherwise. + CrashGenerationServer(const std::wstring& pipe_name, + SECURITY_ATTRIBUTES* pipe_sec_attrs, + OnClientConnectedCallback connect_callback, + void* connect_context, + OnClientDumpRequestCallback dump_callback, + void* dump_context, + OnClientExitedCallback exit_callback, + void* exit_context, + OnClientUploadRequestCallback upload_request_callback, + void* upload_context, + bool generate_dumps, + const std::wstring* dump_path); + + ~CrashGenerationServer(); + + // Performs initialization steps needed to start listening to clients. Upon + // successful return clients may connect to this server's pipe. + // + // Returns true if initialization is successful; false otherwise. + bool Start(); + + void pre_fetch_custom_info(bool do_pre_fetch) { + pre_fetch_custom_info_ = do_pre_fetch; + } + + private: + // Various states the client can be in during the handshake with + // the server. + enum IPCServerState { + // Server starts in this state. + IPC_SERVER_STATE_UNINITIALIZED, + + // Server is in error state and it cannot serve any clients. + IPC_SERVER_STATE_ERROR, + + // Server starts in this state. + IPC_SERVER_STATE_INITIAL, + + // Server has issued an async connect to the pipe and it is waiting + // for the connection to be established. + IPC_SERVER_STATE_CONNECTING, + + // Server is connected successfully. + IPC_SERVER_STATE_CONNECTED, + + // Server has issued an async read from the pipe and it is waiting for + // the read to finish. + IPC_SERVER_STATE_READING, + + // Server is done reading from the pipe. + IPC_SERVER_STATE_READ_DONE, + + // Server has issued an async write to the pipe and it is waiting for + // the write to finish. + IPC_SERVER_STATE_WRITING, + + // Server is done writing to the pipe. + IPC_SERVER_STATE_WRITE_DONE, + + // Server has issued an async read from the pipe for an ack and it + // is waiting for the read to finish. + IPC_SERVER_STATE_READING_ACK, + + // Server is done writing to the pipe and it is now ready to disconnect + // and reconnect. + IPC_SERVER_STATE_DISCONNECTING + }; + + // + // Helper methods to handle various server IPC states. + // + void HandleErrorState(); + void HandleInitialState(); + void HandleConnectingState(); + void HandleConnectedState(); + void HandleReadingState(); + void HandleReadDoneState(); + void HandleWritingState(); + void HandleWriteDoneState(); + void HandleReadingAckState(); + void HandleDisconnectingState(); + + // Prepares reply for a client from the given parameters. + bool PrepareReply(const ClientInfo& client_info, + ProtocolMessage* reply) const; + + // Duplicates various handles in the ClientInfo object for the client + // process and stores them in the given ProtocolMessage instance. If + // creating any handle fails, ProtocolMessage will contain the handles + // already created successfully, which should be closed by the caller. + bool CreateClientHandles(const ClientInfo& client_info, + ProtocolMessage* reply) const; + + // Response to the given client. Return true if all steps of + // responding to the client succeed, false otherwise. + bool RespondToClient(ClientInfo* client_info); + + // Handles a connection request from the client. + void HandleConnectionRequest(); + + // Handles a dump request from the client. + void HandleDumpRequest(const ClientInfo& client_info); + + // Callback for pipe connected event. + static void CALLBACK OnPipeConnected(void* context, BOOLEAN timer_or_wait); + + // Callback for a dump request. + static void CALLBACK OnDumpRequest(void* context, BOOLEAN timer_or_wait); + + // Callback for client process exit event. + static void CALLBACK OnClientEnd(void* context, BOOLEAN timer_or_wait); + + // Handles client process exit. + void HandleClientProcessExit(ClientInfo* client_info); + + // Adds the given client to the list of registered clients. + bool AddClient(ClientInfo* client_info); + + // Generates dump for the given client. + bool GenerateDump(const ClientInfo& client, std::wstring* dump_path); + + // Puts the server in a permanent error state and sets a signal such that + // the state will be immediately entered after the current state transition + // is complete. + void EnterErrorState(); + + // Puts the server in the specified state and sets a signal such that the + // state is immediately entered after the current state transition is + // complete. + void EnterStateImmediately(IPCServerState state); + + // Puts the server in the specified state. No signal will be set, so the state + // transition will only occur when signaled manually or by completion of an + // asynchronous IO operation. + void EnterStateWhenSignaled(IPCServerState state); + + // Sync object for thread-safe access to the shared list of clients. + CRITICAL_SECTION sync_; + + // List of clients. + std::list clients_; + + // Pipe name. + std::wstring pipe_name_; + + // Pipe security attributes + SECURITY_ATTRIBUTES* pipe_sec_attrs_; + + // Handle to the pipe used for handshake with clients. + HANDLE pipe_; + + // Pipe wait handle. + HANDLE pipe_wait_handle_; + + // Handle to server-alive mutex. + HANDLE server_alive_handle_; + + // Callback for a successful client connection. + OnClientConnectedCallback connect_callback_; + + // Context for client connected callback. + void* connect_context_; + + // Callback for a client dump request. + OnClientDumpRequestCallback dump_callback_; + + // Context for client dump request callback. + void* dump_context_; + + // Callback for client process exit. + OnClientExitedCallback exit_callback_; + + // Context for client process exit callback. + void* exit_context_; + + // Callback for upload request. + OnClientUploadRequestCallback upload_request_callback_; + + // Context for upload request callback. + void* upload_context_; + + // Whether to generate dumps. + bool generate_dumps_; + + // Wether to populate custom information up-front. + bool pre_fetch_custom_info_; + + // The dump path for the server. + const std::wstring dump_path_; + + // State of the server in performing the IPC with the client. + // Note that since we restrict the pipe to one instance, we + // only need to keep one state of the server. Otherwise, server + // would have one state per client it is talking to. + IPCServerState server_state_; + + // Whether the server is shutting down. + bool shutting_down_; + + // Overlapped instance for async I/O on the pipe. + OVERLAPPED overlapped_; + + // Message object used in IPC with the client. + ProtocolMessage msg_; + + // Client Info for the client that's connecting to the server. + ClientInfo* client_info_; + + // Disable copy ctor and operator=. + CrashGenerationServer(const CrashGenerationServer& crash_server); + CrashGenerationServer& operator=(const CrashGenerationServer& crash_server); +}; + +} // namespace google_breakpad + +#endif // CLIENT_WINDOWS_CRASH_GENERATION_CRASH_GENERATION_SERVER_H__ diff --git a/shared/sentry/external/breakpad/src/client/windows/crash_generation/minidump_generator.cc b/shared/sentry/external/breakpad/src/client/windows/crash_generation/minidump_generator.cc new file mode 100644 index 000000000..b6e293700 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/crash_generation/minidump_generator.cc @@ -0,0 +1,583 @@ +// 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 "client/windows/crash_generation/minidump_generator.h" + +#include +#include + +#include +#include +#include +#include + +#include "client/windows/common/auto_critical_section.h" +#include "common/scoped_ptr.h" +#include "common/windows/guid_string.h" + +using std::wstring; + +namespace { + +// A helper class used to collect handle operations data. Unlike +// |MiniDumpWithHandleData| it records the operations for a single handle value +// only, making it possible to include this information to a minidump. +class HandleTraceData { + public: + HandleTraceData(); + ~HandleTraceData(); + + // Collects the handle operations data and formats a user stream to be added + // to the minidump. + bool CollectHandleData(HANDLE process_handle, + EXCEPTION_POINTERS* exception_pointers); + + // Fills the user dump entry with a pointer to the collected handle operations + // data. Returns |true| if the entry was initialized successfully, or |false| + // if no trace data is available. + bool GetUserStream(MINIDUMP_USER_STREAM* user_stream); + + private: + // Reads the exception code from the client process's address space. + // This routine assumes that the client process's pointer width matches ours. + static bool ReadExceptionCode(HANDLE process_handle, + EXCEPTION_POINTERS* exception_pointers, + DWORD* exception_code); + + // Stores handle operations retrieved by VerifierEnumerateResource(). + static ULONG CALLBACK RecordHandleOperations(void* resource_description, + void* enumeration_context, + ULONG* enumeration_level); + + // Function pointer type for VerifierEnumerateResource, which is looked up + // dynamically. + typedef BOOL (WINAPI* VerifierEnumerateResourceType)( + HANDLE Process, + ULONG Flags, + ULONG ResourceType, + AVRF_RESOURCE_ENUMERATE_CALLBACK ResourceCallback, + PVOID EnumerationContext); + + // Handle to dynamically loaded verifier.dll. + HMODULE verifier_module_; + + // Pointer to the VerifierEnumerateResource function. + VerifierEnumerateResourceType enumerate_resource_; + + // Handle value to look for. + ULONG64 handle_; + + // List of handle operations for |handle_|. + std::list operations_; + + // Minidump stream data. + std::vector stream_; +}; + +HandleTraceData::HandleTraceData() + : verifier_module_(NULL), + enumerate_resource_(NULL), + handle_(NULL) { +} + +HandleTraceData::~HandleTraceData() { + if (verifier_module_) { + FreeLibrary(verifier_module_); + } +} + +bool HandleTraceData::CollectHandleData( + HANDLE process_handle, + EXCEPTION_POINTERS* exception_pointers) { + DWORD exception_code; + if (!ReadExceptionCode(process_handle, exception_pointers, &exception_code)) { + return false; + } + + // Verify whether the execption is STATUS_INVALID_HANDLE. Do not record any + // handle information if it is a different exception to keep the minidump + // small. + if (exception_code != STATUS_INVALID_HANDLE) { + return true; + } + + // Load verifier!VerifierEnumerateResource() dynamically. + verifier_module_ = LoadLibrary(TEXT("verifier.dll")); + if (!verifier_module_) { + return false; + } + + enumerate_resource_ = reinterpret_cast( + GetProcAddress(verifier_module_, "VerifierEnumerateResource")); + if (!enumerate_resource_) { + return false; + } + + // STATUS_INVALID_HANDLE does not provide the offending handle value in + // the exception parameters so we have to guess. At the moment we scan + // the handle operations trace looking for the last invalid handle operation + // and record only the operations for that handle value. + if (enumerate_resource_(process_handle, + 0, + AvrfResourceHandleTrace, + &RecordHandleOperations, + this) != ERROR_SUCCESS) { + // The handle tracing must have not been enabled. + return true; + } + + // Now that |handle_| is initialized, purge all irrelevant operations. + std::list::iterator i = operations_.begin(); + std::list::iterator i_end = operations_.end(); + while (i != i_end) { + if (i->Handle == handle_) { + ++i; + } else { + i = operations_.erase(i); + } + } + + // Convert the list of recorded operations to a minidump stream. + stream_.resize(sizeof(MINIDUMP_HANDLE_OPERATION_LIST) + + sizeof(AVRF_HANDLE_OPERATION) * operations_.size()); + + MINIDUMP_HANDLE_OPERATION_LIST* stream_data = + reinterpret_cast( + &stream_.front()); + stream_data->SizeOfHeader = sizeof(MINIDUMP_HANDLE_OPERATION_LIST); + stream_data->SizeOfEntry = sizeof(AVRF_HANDLE_OPERATION); + stream_data->NumberOfEntries = static_cast(operations_.size()); + stream_data->Reserved = 0; + std::copy(operations_.begin(), + operations_.end(), +#if defined(_MSC_VER) && !defined(_LIBCPP_STD_VER) + stdext::checked_array_iterator( + reinterpret_cast(stream_data + 1), + operations_.size()) +#else + reinterpret_cast(stream_data + 1) +#endif + ); + + return true; +} + +bool HandleTraceData::GetUserStream(MINIDUMP_USER_STREAM* user_stream) { + if (stream_.empty()) { + return false; + } else { + user_stream->Type = HandleOperationListStream; + user_stream->BufferSize = static_cast(stream_.size()); + user_stream->Buffer = &stream_.front(); + return true; + } +} + +bool HandleTraceData::ReadExceptionCode( + HANDLE process_handle, + EXCEPTION_POINTERS* exception_pointers, + DWORD* exception_code) { + EXCEPTION_POINTERS pointers; + if (!ReadProcessMemory(process_handle, + exception_pointers, + &pointers, + sizeof(pointers), + NULL)) { + return false; + } + + if (!ReadProcessMemory(process_handle, + pointers.ExceptionRecord, + exception_code, + sizeof(*exception_code), + NULL)) { + return false; + } + + return true; +} + +ULONG CALLBACK HandleTraceData::RecordHandleOperations( + void* resource_description, + void* enumeration_context, + ULONG* enumeration_level) { + AVRF_HANDLE_OPERATION* description = + reinterpret_cast(resource_description); + HandleTraceData* self = + reinterpret_cast(enumeration_context); + + // Remember the last invalid handle operation. + if (description->OperationType == OperationDbBADREF) { + self->handle_ = description->Handle; + } + + // Record all handle operations. + self->operations_.push_back(*description); + + *enumeration_level = HeapEnumerationEverything; + return ERROR_SUCCESS; +} + +} // namespace + +namespace google_breakpad { + +MinidumpGenerator::MinidumpGenerator( + const std::wstring& dump_path, + const HANDLE process_handle, + const DWORD process_id, + const DWORD thread_id, + const DWORD requesting_thread_id, + EXCEPTION_POINTERS* exception_pointers, + MDRawAssertionInfo* assert_info, + const MINIDUMP_TYPE dump_type, + const bool is_client_pointers) + : dbghelp_module_(NULL), + write_dump_(NULL), + rpcrt4_module_(NULL), + create_uuid_(NULL), + process_handle_(process_handle), + process_id_(process_id), + thread_id_(thread_id), + requesting_thread_id_(requesting_thread_id), + exception_pointers_(exception_pointers), + assert_info_(assert_info), + dump_type_(dump_type), + is_client_pointers_(is_client_pointers), + dump_path_(dump_path), + uuid_generated_(false), + dump_file_(INVALID_HANDLE_VALUE), + full_dump_file_(INVALID_HANDLE_VALUE), + dump_file_is_internal_(false), + full_dump_file_is_internal_(false), + additional_streams_(NULL), + callback_info_(NULL) { + uuid_ = {0}; + InitializeCriticalSection(&module_load_sync_); + InitializeCriticalSection(&get_proc_address_sync_); +} + +MinidumpGenerator::~MinidumpGenerator() { + if (dump_file_is_internal_ && dump_file_ != INVALID_HANDLE_VALUE) { + CloseHandle(dump_file_); + } + + if (full_dump_file_is_internal_ && full_dump_file_ != INVALID_HANDLE_VALUE) { + CloseHandle(full_dump_file_); + } + + if (dbghelp_module_) { + FreeLibrary(dbghelp_module_); + } + + if (rpcrt4_module_) { + FreeLibrary(rpcrt4_module_); + } + + DeleteCriticalSection(&get_proc_address_sync_); + DeleteCriticalSection(&module_load_sync_); +} + +bool MinidumpGenerator::WriteMinidump() { + bool full_memory_dump = (dump_type_ & MiniDumpWithFullMemory) != 0; + if (dump_file_ == INVALID_HANDLE_VALUE || + (full_memory_dump && full_dump_file_ == INVALID_HANDLE_VALUE)) { + return false; + } + + MiniDumpWriteDumpType write_dump = GetWriteDump(); + if (!write_dump) { + return false; + } + + MINIDUMP_EXCEPTION_INFORMATION* dump_exception_pointers = NULL; + MINIDUMP_EXCEPTION_INFORMATION dump_exception_info; + + // Setup the exception information object only if it's a dump + // due to an exception. + if (exception_pointers_) { + dump_exception_pointers = &dump_exception_info; + dump_exception_info.ThreadId = thread_id_; + dump_exception_info.ExceptionPointers = exception_pointers_; + dump_exception_info.ClientPointers = is_client_pointers_; + } + + // Add an MDRawBreakpadInfo stream to the minidump, to provide additional + // information about the exception handler to the Breakpad processor. + // The information will help the processor determine which threads are + // relevant. The Breakpad processor does not require this information but + // can function better with Breakpad-generated dumps when it is present. + // The native debugger is not harmed by the presence of this information. + MDRawBreakpadInfo breakpad_info = {0}; + if (!is_client_pointers_) { + // Set the dump thread id and requesting thread id only in case of + // in-process dump generation. + breakpad_info.validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID | + MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID; + breakpad_info.dump_thread_id = thread_id_; + breakpad_info.requesting_thread_id = requesting_thread_id_; + } + + int additional_streams_count = additional_streams_ ? + additional_streams_->UserStreamCount : 0; + scoped_array user_stream_array( + new MINIDUMP_USER_STREAM[3 + additional_streams_count]); + user_stream_array[0].Type = MD_BREAKPAD_INFO_STREAM; + user_stream_array[0].BufferSize = sizeof(breakpad_info); + user_stream_array[0].Buffer = &breakpad_info; + + MINIDUMP_USER_STREAM_INFORMATION user_streams; + user_streams.UserStreamCount = 1; + user_streams.UserStreamArray = user_stream_array.get(); + + MDRawAssertionInfo* actual_assert_info = assert_info_; + MDRawAssertionInfo client_assert_info = {{0}}; + + if (assert_info_) { + // If the assertion info object lives in the client process, + // read the memory of the client process. + if (is_client_pointers_) { + SIZE_T bytes_read = 0; + if (!ReadProcessMemory(process_handle_, + assert_info_, + &client_assert_info, + sizeof(client_assert_info), + &bytes_read)) { + if (dump_file_is_internal_) + CloseHandle(dump_file_); + if (full_dump_file_is_internal_ && + full_dump_file_ != INVALID_HANDLE_VALUE) + CloseHandle(full_dump_file_); + return false; + } + + if (bytes_read != sizeof(client_assert_info)) { + if (dump_file_is_internal_) + CloseHandle(dump_file_); + if (full_dump_file_is_internal_ && + full_dump_file_ != INVALID_HANDLE_VALUE) + CloseHandle(full_dump_file_); + return false; + } + + actual_assert_info = &client_assert_info; + } + + user_stream_array[1].Type = MD_ASSERTION_INFO_STREAM; + user_stream_array[1].BufferSize = sizeof(MDRawAssertionInfo); + user_stream_array[1].Buffer = actual_assert_info; + ++user_streams.UserStreamCount; + } + + if (additional_streams_) { + for (size_t i = 0; + i < additional_streams_->UserStreamCount; + i++, user_streams.UserStreamCount++) { + user_stream_array[user_streams.UserStreamCount].Type = + additional_streams_->UserStreamArray[i].Type; + user_stream_array[user_streams.UserStreamCount].BufferSize = + additional_streams_->UserStreamArray[i].BufferSize; + user_stream_array[user_streams.UserStreamCount].Buffer = + additional_streams_->UserStreamArray[i].Buffer; + } + } + + // If the process is terminated by STATUS_INVALID_HANDLE exception store + // the trace of operations for the offending handle value. Do nothing special + // if the client already requested the handle trace to be stored in the dump. + HandleTraceData handle_trace_data; + if (exception_pointers_ && (dump_type_ & MiniDumpWithHandleData) == 0) { + if (!handle_trace_data.CollectHandleData(process_handle_, + exception_pointers_)) { + if (dump_file_is_internal_) + CloseHandle(dump_file_); + if (full_dump_file_is_internal_ && + full_dump_file_ != INVALID_HANDLE_VALUE) + CloseHandle(full_dump_file_); + return false; + } + } + + bool result_full_memory = true; + if (full_memory_dump) { + result_full_memory = write_dump( + process_handle_, + process_id_, + full_dump_file_, + static_cast((dump_type_ & (~MiniDumpNormal)) + | MiniDumpWithHandleData), + dump_exception_pointers, + &user_streams, + NULL) != FALSE; + } + + // Add handle operations trace stream to the minidump if it was collected. + if (handle_trace_data.GetUserStream( + &user_stream_array[user_streams.UserStreamCount])) { + ++user_streams.UserStreamCount; + } + + bool result_minidump = write_dump( + process_handle_, + process_id_, + dump_file_, + static_cast((dump_type_ & (~MiniDumpWithFullMemory)) + | MiniDumpNormal), + dump_exception_pointers, + &user_streams, + callback_info_) != FALSE; + + return result_minidump && result_full_memory; +} + +bool MinidumpGenerator::GenerateDumpFile(wstring* dump_path) { + // The dump file was already set by handle or this function was previously + // called. + if (dump_file_ != INVALID_HANDLE_VALUE) { + return false; + } + + wstring dump_file_path; + if (!GenerateDumpFilePath(&dump_file_path)) { + return false; + } + + dump_file_ = CreateFile(dump_file_path.c_str(), + GENERIC_WRITE, + 0, + NULL, + CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (dump_file_ == INVALID_HANDLE_VALUE) { + return false; + } + + dump_file_is_internal_ = true; + *dump_path = dump_file_path; + return true; +} + +bool MinidumpGenerator::GenerateFullDumpFile(wstring* full_dump_path) { + // A full minidump was not requested. + if ((dump_type_ & MiniDumpWithFullMemory) == 0) { + return false; + } + + // The dump file was already set by handle or this function was previously + // called. + if (full_dump_file_ != INVALID_HANDLE_VALUE) { + return false; + } + + wstring full_dump_file_path; + if (!GenerateDumpFilePath(&full_dump_file_path)) { + return false; + } + full_dump_file_path.resize(full_dump_file_path.size() - 4); // strip .dmp + full_dump_file_path.append(TEXT("-full.dmp")); + + full_dump_file_ = CreateFile(full_dump_file_path.c_str(), + GENERIC_WRITE, + 0, + NULL, + CREATE_NEW, + FILE_ATTRIBUTE_NORMAL, + NULL); + if (full_dump_file_ == INVALID_HANDLE_VALUE) { + return false; + } + + full_dump_file_is_internal_ = true; + *full_dump_path = full_dump_file_path; + return true; +} + +HMODULE MinidumpGenerator::GetDbghelpModule() { + AutoCriticalSection lock(&module_load_sync_); + if (!dbghelp_module_) { + dbghelp_module_ = LoadLibrary(TEXT("dbghelp.dll")); + } + + return dbghelp_module_; +} + +MinidumpGenerator::MiniDumpWriteDumpType MinidumpGenerator::GetWriteDump() { + AutoCriticalSection lock(&get_proc_address_sync_); + if (!write_dump_) { + HMODULE module = GetDbghelpModule(); + if (module) { + FARPROC proc = GetProcAddress(module, "MiniDumpWriteDump"); + write_dump_ = reinterpret_cast(proc); + } + } + + return write_dump_; +} + +HMODULE MinidumpGenerator::GetRpcrt4Module() { + AutoCriticalSection lock(&module_load_sync_); + if (!rpcrt4_module_) { + rpcrt4_module_ = LoadLibrary(TEXT("rpcrt4.dll")); + } + + return rpcrt4_module_; +} + +MinidumpGenerator::UuidCreateType MinidumpGenerator::GetCreateUuid() { + AutoCriticalSection lock(&module_load_sync_); + if (!create_uuid_) { + HMODULE module = GetRpcrt4Module(); + if (module) { + FARPROC proc = GetProcAddress(module, "UuidCreate"); + create_uuid_ = reinterpret_cast(proc); + } + } + + return create_uuid_; +} + +bool MinidumpGenerator::GenerateDumpFilePath(wstring* file_path) { + if (!uuid_generated_) { + UuidCreateType create_uuid = GetCreateUuid(); + if (!create_uuid) { + return false; + } + + create_uuid(&uuid_); + uuid_generated_ = true; + } + + wstring id_str = GUIDString::GUIDToWString(&uuid_); + + *file_path = dump_path_ + TEXT("\\") + id_str + TEXT(".dmp"); + return true; +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/windows/crash_generation/minidump_generator.h b/shared/sentry/external/breakpad/src/client/windows/crash_generation/minidump_generator.h new file mode 100644 index 000000000..a707c0bb1 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/crash_generation/minidump_generator.h @@ -0,0 +1,203 @@ +// 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. + +#ifndef CLIENT_WINDOWS_CRASH_GENERATION_MINIDUMP_GENERATOR_H_ +#define CLIENT_WINDOWS_CRASH_GENERATION_MINIDUMP_GENERATOR_H_ + +#include +#include +#include +#include +#include +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +// Abstraction for various objects and operations needed to generate +// minidump on Windows. This abstraction is useful to hide all the gory +// details for minidump generation and provide a clean interface to +// the clients to generate minidumps. +class MinidumpGenerator { + public: + // Creates an instance with the given parameters. + // is_client_pointers specifies whether the exception_pointers and + // assert_info point into the process that is being dumped. + // Before calling WriteMinidump on the returned instance a dump file muct be + // specified by a call to either SetDumpFile() or GenerateDumpFile(). + // If a full dump file will be requested via a subsequent call to either + // SetFullDumpFile or GenerateFullDumpFile() dump_type must include + // MiniDumpWithFullMemory. + MinidumpGenerator(const std::wstring& dump_path, + const HANDLE process_handle, + const DWORD process_id, + const DWORD thread_id, + const DWORD requesting_thread_id, + EXCEPTION_POINTERS* exception_pointers, + MDRawAssertionInfo* assert_info, + const MINIDUMP_TYPE dump_type, + const bool is_client_pointers); + + ~MinidumpGenerator(); + + void SetDumpFile(const HANDLE dump_file) { dump_file_ = dump_file; } + void SetFullDumpFile(const HANDLE full_dump_file) { + full_dump_file_ = full_dump_file; + } + + // Generate the name for the dump file that will be written to once + // WriteMinidump() is called. Can only be called once and cannot be called + // if the dump file is set via SetDumpFile(). + bool GenerateDumpFile(std::wstring* dump_path); + + // Generate the name for the full dump file that will be written to once + // WriteMinidump() is called. Cannot be called unless the minidump type + // includes MiniDumpWithFullMemory. Can only be called once and cannot be + // called if the dump file is set via SetFullDumpFile(). + bool GenerateFullDumpFile(std::wstring* full_dump_path); + + void SetAdditionalStreams( + MINIDUMP_USER_STREAM_INFORMATION* additional_streams) { + additional_streams_ = additional_streams; + } + + void SetCallback(MINIDUMP_CALLBACK_INFORMATION* callback_info) { + callback_info_ = callback_info; + } + + // Writes the minidump with the given parameters. Stores the + // dump file path in the dump_path parameter if dump generation + // succeeds. + bool WriteMinidump(); + + private: + // Function pointer type for MiniDumpWriteDump, which is looked up + // dynamically. + typedef BOOL (WINAPI* MiniDumpWriteDumpType)( + HANDLE hProcess, + DWORD ProcessId, + HANDLE hFile, + MINIDUMP_TYPE DumpType, + CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, + CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, + CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam); + + // Function pointer type for UuidCreate, which is looked up dynamically. + typedef RPC_STATUS (RPC_ENTRY* UuidCreateType)(UUID* Uuid); + + // Loads the appropriate DLL lazily in a thread safe way. + HMODULE GetDbghelpModule(); + + // Loads the appropriate DLL and gets a pointer to the MiniDumpWriteDump + // function lazily and in a thread-safe manner. + MiniDumpWriteDumpType GetWriteDump(); + + // Loads the appropriate DLL lazily in a thread safe way. + HMODULE GetRpcrt4Module(); + + // Loads the appropriate DLL and gets a pointer to the UuidCreate + // function lazily and in a thread-safe manner. + UuidCreateType GetCreateUuid(); + + // Returns the path for the file to write dump to. + bool GenerateDumpFilePath(std::wstring* file_path); + + // Handle to dynamically loaded DbgHelp.dll. + HMODULE dbghelp_module_; + + // Pointer to the MiniDumpWriteDump function. + MiniDumpWriteDumpType write_dump_; + + // Handle to dynamically loaded rpcrt4.dll. + HMODULE rpcrt4_module_; + + // Pointer to the UuidCreate function. + UuidCreateType create_uuid_; + + // Handle for the process to dump. + HANDLE process_handle_; + + // Process ID for the process to dump. + DWORD process_id_; + + // The crashing thread ID. + DWORD thread_id_; + + // The thread ID which is requesting the dump. + DWORD requesting_thread_id_; + + // Pointer to the exception information for the crash. This may point to an + // address in the crashing process so it should not be dereferenced. + EXCEPTION_POINTERS* exception_pointers_; + + // Assertion info for the report. + MDRawAssertionInfo* assert_info_; + + // Type of minidump to generate. + MINIDUMP_TYPE dump_type_; + + // Specifies whether the exception_pointers_ reference memory in the crashing + // process. + bool is_client_pointers_; + + // Folder path to store dump files. + std::wstring dump_path_; + + // UUID used to make dump file names. + UUID uuid_; + bool uuid_generated_; + + // The file where the dump will be written. + HANDLE dump_file_; + + // The file where the full dump will be written. + HANDLE full_dump_file_; + + // Tracks whether the dump file handle is managed externally. + bool dump_file_is_internal_; + + // Tracks whether the full dump file handle is managed externally. + bool full_dump_file_is_internal_; + + // Additional streams to be written to the dump. + MINIDUMP_USER_STREAM_INFORMATION* additional_streams_; + + // The user defined callback for the various stages of the dump process. + MINIDUMP_CALLBACK_INFORMATION* callback_info_; + + // Critical section to sychronize action of loading modules dynamically. + CRITICAL_SECTION module_load_sync_; + + // Critical section to synchronize action of dynamically getting function + // addresses from modules. + CRITICAL_SECTION get_proc_address_sync_; +}; + +} // namespace google_breakpad + +#endif // CLIENT_WINDOWS_CRASH_GENERATION_MINIDUMP_GENERATOR_H_ diff --git a/shared/sentry/external/breakpad/src/client/windows/handler/exception_handler.cc b/shared/sentry/external/breakpad/src/client/windows/handler/exception_handler.cc new file mode 100644 index 000000000..20d63a841 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/handler/exception_handler.cc @@ -0,0 +1,1082 @@ +// 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 + +#include +#include +#include + +#include "common/windows/string_utils-inl.h" + +#include "client/windows/common/ipc_protocol.h" +#include "client/windows/handler/exception_handler.h" +#include "common/windows/guid_string.h" + +namespace google_breakpad { + +// This is passed as the context to the MinidumpWriteDump callback. +typedef struct { + AppMemoryList::const_iterator iter; + AppMemoryList::const_iterator end; +} MinidumpCallbackContext; + +// This define is new to Windows 10. +#ifndef DBG_PRINTEXCEPTION_WIDE_C +#define DBG_PRINTEXCEPTION_WIDE_C ((DWORD)0x4001000A) +#endif + +vector* ExceptionHandler::handler_stack_ = NULL; +LONG ExceptionHandler::handler_stack_index_ = 0; +CRITICAL_SECTION ExceptionHandler::handler_stack_critical_section_; +volatile LONG ExceptionHandler::instance_count_ = 0; + +ExceptionHandler::ExceptionHandler(const wstring& dump_path, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + int handler_types, + MINIDUMP_TYPE dump_type, + const wchar_t* pipe_name, + const CustomClientInfo* custom_info) { + Initialize(dump_path, + filter, + callback, + callback_context, + handler_types, + dump_type, + pipe_name, + NULL, // pipe_handle + NULL, // crash_generation_client + custom_info); +} + +ExceptionHandler::ExceptionHandler(const wstring& dump_path, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + int handler_types, + MINIDUMP_TYPE dump_type, + HANDLE pipe_handle, + const CustomClientInfo* custom_info) { + Initialize(dump_path, + filter, + callback, + callback_context, + handler_types, + dump_type, + NULL, // pipe_name + pipe_handle, + NULL, // crash_generation_client + custom_info); +} + +ExceptionHandler::ExceptionHandler( + const wstring& dump_path, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + int handler_types, + CrashGenerationClient* crash_generation_client) { + // The dump_type, pipe_name and custom_info that are passed in to Initialize() + // are not used. The ones set in crash_generation_client are used instead. + Initialize(dump_path, + filter, + callback, + callback_context, + handler_types, + MiniDumpNormal, // dump_type - not used + NULL, // pipe_name - not used + NULL, // pipe_handle + crash_generation_client, + NULL); // custom_info - not used +} + +ExceptionHandler::ExceptionHandler(const wstring& dump_path, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + int handler_types) { + Initialize(dump_path, + filter, + callback, + callback_context, + handler_types, + MiniDumpNormal, + NULL, // pipe_name + NULL, // pipe_handle + NULL, // crash_generation_client + NULL); // custom_info +} + +void ExceptionHandler::Initialize( + const wstring& dump_path, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + int handler_types, + MINIDUMP_TYPE dump_type, + const wchar_t* pipe_name, + HANDLE pipe_handle, + CrashGenerationClient* crash_generation_client, + const CustomClientInfo* custom_info) { + LONG instance_count = InterlockedIncrement(&instance_count_); + filter_ = filter; + callback_ = callback; + callback_context_ = callback_context; + dump_path_c_ = NULL; + next_minidump_id_c_ = NULL; + next_minidump_path_c_ = NULL; + dbghelp_module_ = NULL; + minidump_write_dump_ = NULL; + dump_type_ = dump_type; + rpcrt4_module_ = NULL; + uuid_create_ = NULL; + handler_types_ = handler_types; + previous_filter_ = NULL; +#if _MSC_VER >= 1400 // MSVC 2005/8 + previous_iph_ = NULL; +#endif // _MSC_VER >= 1400 + previous_pch_ = NULL; + handler_thread_ = NULL; + is_shutdown_ = false; + handler_start_semaphore_ = NULL; + handler_finish_semaphore_ = NULL; + requesting_thread_id_ = 0; + exception_info_ = NULL; + assertion_ = NULL; + handler_return_value_ = false; + handle_debug_exceptions_ = false; + consume_invalid_handle_exceptions_ = false; + + // Attempt to use out-of-process if user has specified a pipe or a + // crash generation client. + scoped_ptr client; + if (crash_generation_client) { + client.reset(crash_generation_client); + } else if (pipe_name) { + client.reset( + new CrashGenerationClient(pipe_name, dump_type_, custom_info)); + } else if (pipe_handle) { + client.reset( + new CrashGenerationClient(pipe_handle, dump_type_, custom_info)); + } + + if (client.get() != NULL) { + // If successful in registering with the monitoring process, + // there is no need to setup in-process crash generation. + if (client->Register()) { + crash_generation_client_.reset(client.release()); + } + } + + if (!IsOutOfProcess()) { + // Either client did not ask for out-of-process crash generation + // or registration with the server process failed. In either case, + // setup to do in-process crash generation. + + // Set synchronization primitives and the handler thread. Each + // ExceptionHandler object gets its own handler thread because that's the + // only way to reliably guarantee sufficient stack space in an exception, + // and it allows an easy way to get a snapshot of the requesting thread's + // context outside of an exception. + InitializeCriticalSection(&handler_critical_section_); + handler_start_semaphore_ = CreateSemaphore(NULL, 0, 1, NULL); + assert(handler_start_semaphore_ != NULL); + + handler_finish_semaphore_ = CreateSemaphore(NULL, 0, 1, NULL); + assert(handler_finish_semaphore_ != NULL); + + // Don't attempt to create the thread if we could not create the semaphores. + if (handler_finish_semaphore_ != NULL && handler_start_semaphore_ != NULL) { + DWORD thread_id; + const int kExceptionHandlerThreadInitialStackSize = 64 * 1024; + handler_thread_ = CreateThread(NULL, // lpThreadAttributes + kExceptionHandlerThreadInitialStackSize, + ExceptionHandlerThreadMain, + this, // lpParameter + 0, // dwCreationFlags + &thread_id); + assert(handler_thread_ != NULL); + } + + dbghelp_module_ = LoadLibrary(L"dbghelp.dll"); + if (dbghelp_module_) { + minidump_write_dump_ = reinterpret_cast( + GetProcAddress(dbghelp_module_, "MiniDumpWriteDump")); + } + + // Load this library dynamically to not affect existing projects. Most + // projects don't link against this directly, it's usually dynamically + // loaded by dependent code. + rpcrt4_module_ = LoadLibrary(L"rpcrt4.dll"); + if (rpcrt4_module_) { + uuid_create_ = reinterpret_cast( + GetProcAddress(rpcrt4_module_, "UuidCreate")); + } + + // set_dump_path calls UpdateNextID. This sets up all of the path and id + // strings, and their equivalent c_str pointers. + set_dump_path(dump_path); + } + + // Reserve one element for the instruction memory + AppMemory instruction_memory; + instruction_memory.ptr = NULL; + instruction_memory.length = 0; + app_memory_info_.push_back(instruction_memory); + + // There is a race condition here. If the first instance has not yet + // initialized the critical section, the second (and later) instances may + // try to use uninitialized critical section object. The feature of multiple + // instances in one module is not used much, so leave it as is for now. + // One way to solve this in the current design (that is, keeping the static + // handler stack) is to use spin locks with volatile bools to synchronize + // the handler stack. This works only if the compiler guarantees to generate + // cache coherent code for volatile. + // TODO(munjal): Fix this in a better way by changing the design if possible. + + // Lazy initialization of the handler_stack_critical_section_ + if (instance_count == 1) { + InitializeCriticalSection(&handler_stack_critical_section_); + } + + if (handler_types != HANDLER_NONE) { + EnterCriticalSection(&handler_stack_critical_section_); + + // The first time an ExceptionHandler that installs a handler is + // created, set up the handler stack. + if (!handler_stack_) { + handler_stack_ = new vector(); + } + handler_stack_->push_back(this); + + if (handler_types & HANDLER_EXCEPTION) + previous_filter_ = SetUnhandledExceptionFilter(HandleException); + +#if _MSC_VER >= 1400 // MSVC 2005/8 + if (handler_types & HANDLER_INVALID_PARAMETER) + previous_iph_ = _set_invalid_parameter_handler(HandleInvalidParameter); +#endif // _MSC_VER >= 1400 + + if (handler_types & HANDLER_PURECALL) + previous_pch_ = _set_purecall_handler(HandlePureVirtualCall); + + LeaveCriticalSection(&handler_stack_critical_section_); + } +} + +ExceptionHandler::~ExceptionHandler() { + if (dbghelp_module_) { + FreeLibrary(dbghelp_module_); + } + + if (rpcrt4_module_) { + FreeLibrary(rpcrt4_module_); + } + + if (handler_types_ != HANDLER_NONE) { + EnterCriticalSection(&handler_stack_critical_section_); + + if (handler_types_ & HANDLER_EXCEPTION) + SetUnhandledExceptionFilter(previous_filter_); + +#if _MSC_VER >= 1400 // MSVC 2005/8 + if (handler_types_ & HANDLER_INVALID_PARAMETER) + _set_invalid_parameter_handler(previous_iph_); +#endif // _MSC_VER >= 1400 + + if (handler_types_ & HANDLER_PURECALL) + _set_purecall_handler(previous_pch_); + + if (handler_stack_->back() == this) { + handler_stack_->pop_back(); + } else { + // TODO(mmentovai): use advapi32!ReportEvent to log the warning to the + // system's application event log. + fprintf(stderr, "warning: removing Breakpad handler out of order\n"); + vector::iterator iterator = handler_stack_->begin(); + while (iterator != handler_stack_->end()) { + if (*iterator == this) { + iterator = handler_stack_->erase(iterator); + } else { + ++iterator; + } + } + } + + if (handler_stack_->empty()) { + // When destroying the last ExceptionHandler that installed a handler, + // clean up the handler stack. + delete handler_stack_; + handler_stack_ = NULL; + } + + LeaveCriticalSection(&handler_stack_critical_section_); + } + + // Some of the objects were only initialized if out of process + // registration was not done. + if (!IsOutOfProcess()) { +#ifdef BREAKPAD_NO_TERMINATE_THREAD + // Clean up the handler thread and synchronization primitives. The handler + // thread is either waiting on the semaphore to handle a crash or it is + // handling a crash. Coming out of the wait is fast but wait more in the + // eventuality a crash is handled. This compilation option results in a + // deadlock if the exception handler is destroyed while executing code + // inside DllMain. + is_shutdown_ = true; + ReleaseSemaphore(handler_start_semaphore_, 1, NULL); + const int kWaitForHandlerThreadMs = 60000; + WaitForSingleObject(handler_thread_, kWaitForHandlerThreadMs); +#else + TerminateThread(handler_thread_, 1); +#endif // BREAKPAD_NO_TERMINATE_THREAD + + CloseHandle(handler_thread_); + handler_thread_ = NULL; + DeleteCriticalSection(&handler_critical_section_); + CloseHandle(handler_start_semaphore_); + CloseHandle(handler_finish_semaphore_); + } + + // There is a race condition in the code below: if this instance is + // deleting the static critical section and a new instance of the class + // is created, then there is a possibility that the critical section be + // initialized while the same critical section is being deleted. Given the + // usage pattern for the code, this race condition is unlikely to hit, but it + // is a race condition nonetheless. + if (InterlockedDecrement(&instance_count_) == 0) { + DeleteCriticalSection(&handler_stack_critical_section_); + } +} + +bool ExceptionHandler::RequestUpload(DWORD crash_id) { + return crash_generation_client_->RequestUpload(crash_id); +} + +// static +DWORD ExceptionHandler::ExceptionHandlerThreadMain(void* lpParameter) { + ExceptionHandler* self = reinterpret_cast(lpParameter); + assert(self); + assert(self->handler_start_semaphore_ != NULL); + assert(self->handler_finish_semaphore_ != NULL); + + for (;;) { + if (WaitForSingleObject(self->handler_start_semaphore_, INFINITE) == + WAIT_OBJECT_0) { + // Perform the requested action. + if (self->is_shutdown_) { + // The instance of the exception handler is being destroyed. + break; + } else { + self->handler_return_value_ = + self->WriteMinidumpWithException(self->requesting_thread_id_, + self->exception_info_, + self->assertion_); + } + + // Allow the requesting thread to proceed. + ReleaseSemaphore(self->handler_finish_semaphore_, 1, NULL); + } + } + + // This statement is not reached when the thread is unconditionally + // terminated by the ExceptionHandler destructor. + return 0; +} + +// HandleException and HandleInvalidParameter must create an +// AutoExceptionHandler object to maintain static state and to determine which +// ExceptionHandler instance to use. The constructor locates the correct +// instance, and makes it available through get_handler(). The destructor +// restores the state in effect prior to allocating the AutoExceptionHandler. +class AutoExceptionHandler { + public: + AutoExceptionHandler() { + // Increment handler_stack_index_ so that if another Breakpad handler is + // registered using this same HandleException function, and it needs to be + // called while this handler is running (either because this handler + // declines to handle the exception, or an exception occurs during + // handling), HandleException will find the appropriate ExceptionHandler + // object in handler_stack_ to deliver the exception to. + // + // Because handler_stack_ is addressed in reverse (as |size - index|), + // preincrementing handler_stack_index_ avoids needing to subtract 1 from + // the argument to |at|. + // + // The index is maintained instead of popping elements off of the handler + // stack and pushing them at the end of this method. This avoids ruining + // the order of elements in the stack in the event that some other thread + // decides to manipulate the handler stack (such as creating a new + // ExceptionHandler object) while an exception is being handled. + EnterCriticalSection(&ExceptionHandler::handler_stack_critical_section_); + handler_ = ExceptionHandler::handler_stack_->at( + ExceptionHandler::handler_stack_->size() - + ++ExceptionHandler::handler_stack_index_); + + // In case another exception occurs while this handler is doing its thing, + // it should be delivered to the previous filter. + SetUnhandledExceptionFilter(handler_->previous_filter_); +#if _MSC_VER >= 1400 // MSVC 2005/8 + _set_invalid_parameter_handler(handler_->previous_iph_); +#endif // _MSC_VER >= 1400 + _set_purecall_handler(handler_->previous_pch_); + } + + ~AutoExceptionHandler() { + // Put things back the way they were before entering this handler. + SetUnhandledExceptionFilter(ExceptionHandler::HandleException); +#if _MSC_VER >= 1400 // MSVC 2005/8 + _set_invalid_parameter_handler(ExceptionHandler::HandleInvalidParameter); +#endif // _MSC_VER >= 1400 + _set_purecall_handler(ExceptionHandler::HandlePureVirtualCall); + + --ExceptionHandler::handler_stack_index_; + LeaveCriticalSection(&ExceptionHandler::handler_stack_critical_section_); + } + + ExceptionHandler* get_handler() const { return handler_; } + + private: + ExceptionHandler* handler_; +}; + +// static +LONG ExceptionHandler::HandleException(EXCEPTION_POINTERS* exinfo) { + AutoExceptionHandler auto_exception_handler; + ExceptionHandler* current_handler = auto_exception_handler.get_handler(); + + // Ignore EXCEPTION_BREAKPOINT and EXCEPTION_SINGLE_STEP exceptions. This + // logic will short-circuit before calling WriteMinidumpOnHandlerThread, + // allowing something else to handle the breakpoint without incurring the + // overhead transitioning to and from the handler thread. This behavior + // can be overridden by calling ExceptionHandler::set_handle_debug_exceptions. + DWORD code = exinfo->ExceptionRecord->ExceptionCode; + LONG action; + bool is_debug_exception = (code == EXCEPTION_BREAKPOINT) || + (code == EXCEPTION_SINGLE_STEP) || + (code == DBG_PRINTEXCEPTION_C) || + (code == DBG_PRINTEXCEPTION_WIDE_C); + + if (code == EXCEPTION_INVALID_HANDLE && + current_handler->consume_invalid_handle_exceptions_) { + return EXCEPTION_CONTINUE_EXECUTION; + } + + bool success = false; + + if (!is_debug_exception || + current_handler->get_handle_debug_exceptions()) { + // If out-of-proc crash handler client is available, we have to use that + // to generate dump and we cannot fall back on in-proc dump generation + // because we never prepared for an in-proc dump generation + + // In case of out-of-process dump generation, directly call + // WriteMinidumpWithException since there is no separate thread running. + if (current_handler->IsOutOfProcess()) { + success = current_handler->WriteMinidumpWithException( + GetCurrentThreadId(), + exinfo, + NULL); + } else { + success = current_handler->WriteMinidumpOnHandlerThread(exinfo, NULL); + } + } + + // The handler fully handled the exception. Returning + // EXCEPTION_EXECUTE_HANDLER indicates this to the system, and usually + // results in the application being terminated. + // + // Note: If the application was launched from within the Cygwin + // environment, returning EXCEPTION_EXECUTE_HANDLER seems to cause the + // application to be restarted. + if (success) { + action = EXCEPTION_EXECUTE_HANDLER; + } else { + // There was an exception, it was a breakpoint or something else ignored + // above, or it was passed to the handler, which decided not to handle it. + // This could be because the filter callback didn't want it, because + // minidump writing failed for some reason, or because the post-minidump + // callback function indicated failure. Give the previous handler a + // chance to do something with the exception. If there is no previous + // handler, return EXCEPTION_CONTINUE_SEARCH, which will allow a debugger + // or native "crashed" dialog to handle the exception. + if (current_handler->previous_filter_) { + action = current_handler->previous_filter_(exinfo); + } else { + action = EXCEPTION_CONTINUE_SEARCH; + } + } + + return action; +} + +#if _MSC_VER >= 1400 // MSVC 2005/8 +// static +void ExceptionHandler::HandleInvalidParameter(const wchar_t* expression, + const wchar_t* function, + const wchar_t* file, + unsigned int line, + uintptr_t reserved) { + // This is an invalid parameter, not an exception. It's safe to play with + // sprintf here. + AutoExceptionHandler auto_exception_handler; + ExceptionHandler* current_handler = auto_exception_handler.get_handler(); + + MDRawAssertionInfo assertion; + memset(&assertion, 0, sizeof(assertion)); + _snwprintf_s(reinterpret_cast(assertion.expression), + sizeof(assertion.expression) / sizeof(assertion.expression[0]), + _TRUNCATE, L"%s", expression); + _snwprintf_s(reinterpret_cast(assertion.function), + sizeof(assertion.function) / sizeof(assertion.function[0]), + _TRUNCATE, L"%s", function); + _snwprintf_s(reinterpret_cast(assertion.file), + sizeof(assertion.file) / sizeof(assertion.file[0]), + _TRUNCATE, L"%s", file); + assertion.line = line; + assertion.type = MD_ASSERTION_INFO_TYPE_INVALID_PARAMETER; + + // Make up an exception record for the current thread and CPU context + // to make it possible for the crash processor to classify these + // as do regular crashes, and to make it humane for developers to + // analyze them. + EXCEPTION_RECORD exception_record = {}; + CONTEXT exception_context = {}; + EXCEPTION_POINTERS exception_ptrs = { &exception_record, &exception_context }; + + ::RtlCaptureContext(&exception_context); + + exception_record.ExceptionCode = STATUS_INVALID_PARAMETER; + + // We store pointers to the the expression and function strings, + // and the line as exception parameters to make them easy to + // access by the developer on the far side. + exception_record.NumberParameters = 3; + exception_record.ExceptionInformation[0] = + reinterpret_cast(&assertion.expression); + exception_record.ExceptionInformation[1] = + reinterpret_cast(&assertion.file); + exception_record.ExceptionInformation[2] = assertion.line; + + bool success = false; + // In case of out-of-process dump generation, directly call + // WriteMinidumpWithException since there is no separate thread running. + if (current_handler->IsOutOfProcess()) { + success = current_handler->WriteMinidumpWithException( + GetCurrentThreadId(), + &exception_ptrs, + &assertion); + } else { + success = current_handler->WriteMinidumpOnHandlerThread(&exception_ptrs, + &assertion); + } + + if (!success) { + if (current_handler->previous_iph_) { + // The handler didn't fully handle the exception. Give it to the + // previous invalid parameter handler. + current_handler->previous_iph_(expression, + function, + file, + line, + reserved); + } else { + // If there's no previous handler, pass the exception back in to the + // invalid parameter handler's core. That's the routine that called this + // function, but now, since this function is no longer registered (and in + // fact, no function at all is registered), this will result in the + // default code path being taken: _CRT_DEBUGGER_HOOK and _invoke_watson. + // Use _invalid_parameter where it exists (in _DEBUG builds) as it passes + // more information through. In non-debug builds, it is not available, + // so fall back to using _invalid_parameter_noinfo. See invarg.c in the + // CRT source. +#ifdef _DEBUG + _invalid_parameter(expression, function, file, line, reserved); +#else // _DEBUG + _invalid_parameter_noinfo(); +#endif // _DEBUG + } + } + + // The handler either took care of the invalid parameter problem itself, + // or passed it on to another handler. "Swallow" it by exiting, paralleling + // the behavior of "swallowing" exceptions. + exit(0); +} +#endif // _MSC_VER >= 1400 + +// static +void ExceptionHandler::HandlePureVirtualCall() { + // This is an pure virtual function call, not an exception. It's safe to + // play with sprintf here. + AutoExceptionHandler auto_exception_handler; + ExceptionHandler* current_handler = auto_exception_handler.get_handler(); + + MDRawAssertionInfo assertion; + memset(&assertion, 0, sizeof(assertion)); + assertion.type = MD_ASSERTION_INFO_TYPE_PURE_VIRTUAL_CALL; + + // Make up an exception record for the current thread and CPU context + // to make it possible for the crash processor to classify these + // as do regular crashes, and to make it humane for developers to + // analyze them. + EXCEPTION_RECORD exception_record = {}; + CONTEXT exception_context = {}; + EXCEPTION_POINTERS exception_ptrs = { &exception_record, &exception_context }; + + ::RtlCaptureContext(&exception_context); + + exception_record.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION; + + // We store pointers to the the expression and function strings, + // and the line as exception parameters to make them easy to + // access by the developer on the far side. + exception_record.NumberParameters = 3; + exception_record.ExceptionInformation[0] = + reinterpret_cast(&assertion.expression); + exception_record.ExceptionInformation[1] = + reinterpret_cast(&assertion.file); + exception_record.ExceptionInformation[2] = assertion.line; + + bool success = false; + // In case of out-of-process dump generation, directly call + // WriteMinidumpWithException since there is no separate thread running. + + if (current_handler->IsOutOfProcess()) { + success = current_handler->WriteMinidumpWithException( + GetCurrentThreadId(), + &exception_ptrs, + &assertion); + } else { + success = current_handler->WriteMinidumpOnHandlerThread(&exception_ptrs, + &assertion); + } + + if (!success) { + if (current_handler->previous_pch_) { + // The handler didn't fully handle the exception. Give it to the + // previous purecall handler. + current_handler->previous_pch_(); + } else { + // If there's no previous handler, return and let _purecall handle it. + // This will just put up an assertion dialog. + return; + } + } + + // The handler either took care of the invalid parameter problem itself, + // or passed it on to another handler. "Swallow" it by exiting, paralleling + // the behavior of "swallowing" exceptions. + exit(0); +} + +bool ExceptionHandler::WriteMinidumpOnHandlerThread( + EXCEPTION_POINTERS* exinfo, MDRawAssertionInfo* assertion) { + EnterCriticalSection(&handler_critical_section_); + + // There isn't much we can do if the handler thread + // was not successfully created. + if (handler_thread_ == NULL) { + LeaveCriticalSection(&handler_critical_section_); + return false; + } + + // The handler thread should only be created when the semaphores are valid. + assert(handler_start_semaphore_ != NULL); + assert(handler_finish_semaphore_ != NULL); + + // Set up data to be passed in to the handler thread. + requesting_thread_id_ = GetCurrentThreadId(); + exception_info_ = exinfo; + assertion_ = assertion; + + // This causes the handler thread to call WriteMinidumpWithException. + ReleaseSemaphore(handler_start_semaphore_, 1, NULL); + + // Wait until WriteMinidumpWithException is done and collect its return value. + WaitForSingleObject(handler_finish_semaphore_, INFINITE); + bool status = handler_return_value_; + + // Clean up. + requesting_thread_id_ = 0; + exception_info_ = NULL; + assertion_ = NULL; + + LeaveCriticalSection(&handler_critical_section_); + + return status; +} + +bool ExceptionHandler::WriteMinidump() { + // Make up an exception record for the current thread and CPU context + // to make it possible for the crash processor to classify these + // as do regular crashes, and to make it humane for developers to + // analyze them. + EXCEPTION_RECORD exception_record = {}; + CONTEXT exception_context = {}; + EXCEPTION_POINTERS exception_ptrs = { &exception_record, &exception_context }; + + ::RtlCaptureContext(&exception_context); + exception_record.ExceptionCode = STATUS_NONCONTINUABLE_EXCEPTION; + + return WriteMinidumpForException(&exception_ptrs); +} + +bool ExceptionHandler::WriteMinidumpForException(EXCEPTION_POINTERS* exinfo) { + // In case of out-of-process dump generation, directly call + // WriteMinidumpWithException since there is no separate thread running. + if (IsOutOfProcess()) { + return WriteMinidumpWithException(GetCurrentThreadId(), + exinfo, + NULL); + } + + bool success = WriteMinidumpOnHandlerThread(exinfo, NULL); + UpdateNextID(); + return success; +} + +// static +bool ExceptionHandler::WriteMinidump(const wstring& dump_path, + MinidumpCallback callback, + void* callback_context, + MINIDUMP_TYPE dump_type) { + ExceptionHandler handler(dump_path, NULL, callback, callback_context, + HANDLER_NONE, dump_type, (HANDLE)NULL, NULL); + return handler.WriteMinidump(); +} + +// static +bool ExceptionHandler::WriteMinidumpForChild(HANDLE child, + DWORD child_blamed_thread, + const wstring& dump_path, + MinidumpCallback callback, + void* callback_context, + MINIDUMP_TYPE dump_type) { + EXCEPTION_RECORD ex; + CONTEXT ctx; + EXCEPTION_POINTERS exinfo = { NULL, NULL }; + // As documented on MSDN, on failure SuspendThread returns (DWORD) -1 + const DWORD kFailedToSuspendThread = static_cast(-1); + DWORD last_suspend_count = kFailedToSuspendThread; + HANDLE child_thread_handle = OpenThread(THREAD_GET_CONTEXT | + THREAD_QUERY_INFORMATION | + THREAD_SUSPEND_RESUME, + FALSE, + child_blamed_thread); + // This thread may have died already, so not opening the handle is a + // non-fatal error. + if (child_thread_handle != NULL) { + last_suspend_count = SuspendThread(child_thread_handle); + if (last_suspend_count != kFailedToSuspendThread) { + ctx.ContextFlags = CONTEXT_ALL; + if (GetThreadContext(child_thread_handle, &ctx)) { + memset(&ex, 0, sizeof(ex)); + ex.ExceptionCode = EXCEPTION_BREAKPOINT; +#if defined(_M_IX86) + ex.ExceptionAddress = reinterpret_cast(ctx.Eip); +#elif defined(_M_X64) + ex.ExceptionAddress = reinterpret_cast(ctx.Rip); +#endif + exinfo.ExceptionRecord = &ex; + exinfo.ContextRecord = &ctx; + } + } + } + + ExceptionHandler handler(dump_path, NULL, callback, callback_context, + HANDLER_NONE, dump_type, (HANDLE)NULL, NULL); + bool success = handler.WriteMinidumpWithExceptionForProcess( + child_blamed_thread, + exinfo.ExceptionRecord ? &exinfo : NULL, + NULL, child, false); + + if (last_suspend_count != kFailedToSuspendThread) { + ResumeThread(child_thread_handle); + } + + CloseHandle(child_thread_handle); + + if (callback) { + success = callback(handler.dump_path_c_, handler.next_minidump_id_c_, + callback_context, NULL, NULL, success); + } + + return success; +} + +bool ExceptionHandler::WriteMinidumpWithException( + DWORD requesting_thread_id, + EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion) { + // Give user code a chance to approve or prevent writing a minidump. If the + // filter returns false, don't handle the exception at all. If this method + // was called as a result of an exception, returning false will cause + // HandleException to call any previous handler or return + // EXCEPTION_CONTINUE_SEARCH on the exception thread, allowing it to appear + // as though this handler were not present at all. + if (filter_ && !filter_(callback_context_, exinfo, assertion)) { + return false; + } + + bool success = false; + if (IsOutOfProcess()) { + success = crash_generation_client_->RequestDump(exinfo, assertion); + } else { + success = WriteMinidumpWithExceptionForProcess(requesting_thread_id, + exinfo, + assertion, + GetCurrentProcess(), + true); + } + + if (callback_) { + // TODO(munjal): In case of out-of-process dump generation, both + // dump_path_c_ and next_minidump_id_ will be NULL. For out-of-process + // scenario, the server process ends up creating the dump path and dump + // id so they are not known to the client. + success = callback_(dump_path_c_, next_minidump_id_c_, callback_context_, + exinfo, assertion, success); + } + + return success; +} + +// static +BOOL CALLBACK ExceptionHandler::MinidumpWriteDumpCallback( + PVOID context, + const PMINIDUMP_CALLBACK_INPUT callback_input, + PMINIDUMP_CALLBACK_OUTPUT callback_output) { + switch (callback_input->CallbackType) { + case MemoryCallback: { + MinidumpCallbackContext* callback_context = + reinterpret_cast(context); + if (callback_context->iter == callback_context->end) + return FALSE; + + // Include the specified memory region. + callback_output->MemoryBase = callback_context->iter->ptr; + callback_output->MemorySize = callback_context->iter->length; + callback_context->iter++; + return TRUE; + } + + // Include all modules. + case IncludeModuleCallback: + case ModuleCallback: + return TRUE; + + // Include all threads. + case IncludeThreadCallback: + case ThreadCallback: + return TRUE; + + // Stop receiving cancel callbacks. + case CancelCallback: + callback_output->CheckCancel = FALSE; + callback_output->Cancel = FALSE; + return TRUE; + } + // Ignore other callback types. + return FALSE; +} + +bool ExceptionHandler::WriteMinidumpWithExceptionForProcess( + DWORD requesting_thread_id, + EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion, + HANDLE process, + bool write_requester_stream) { + bool success = false; + if (minidump_write_dump_) { + HANDLE dump_file = CreateFile(next_minidump_path_c_, + GENERIC_WRITE, + 0, // no sharing + NULL, + CREATE_NEW, // fail if exists + FILE_ATTRIBUTE_NORMAL, + NULL); + if (dump_file != INVALID_HANDLE_VALUE) { + MINIDUMP_EXCEPTION_INFORMATION except_info; + except_info.ThreadId = requesting_thread_id; + except_info.ExceptionPointers = exinfo; + except_info.ClientPointers = FALSE; + + // Leave room in user_stream_array for possible breakpad and + // assertion info streams. + MINIDUMP_USER_STREAM user_stream_array[2]; + MINIDUMP_USER_STREAM_INFORMATION user_streams; + user_streams.UserStreamCount = 0; + user_streams.UserStreamArray = user_stream_array; + + if (write_requester_stream) { + // Add an MDRawBreakpadInfo stream to the minidump, to provide + // additional information about the exception handler to the Breakpad + // processor. The information will help the processor determine which + // threads are relevant. The Breakpad processor does not require this + // information but can function better with Breakpad-generated dumps + // when it is present. The native debugger is not harmed by the + // presence of this information. + MDRawBreakpadInfo breakpad_info; + breakpad_info.validity = MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID | + MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID; + breakpad_info.dump_thread_id = GetCurrentThreadId(); + breakpad_info.requesting_thread_id = requesting_thread_id; + + int index = user_streams.UserStreamCount; + user_stream_array[index].Type = MD_BREAKPAD_INFO_STREAM; + user_stream_array[index].BufferSize = sizeof(breakpad_info); + user_stream_array[index].Buffer = &breakpad_info; + ++user_streams.UserStreamCount; + } + + if (assertion) { + int index = user_streams.UserStreamCount; + user_stream_array[index].Type = MD_ASSERTION_INFO_STREAM; + user_stream_array[index].BufferSize = sizeof(MDRawAssertionInfo); + user_stream_array[index].Buffer = assertion; + ++user_streams.UserStreamCount; + } + + // Older versions of DbgHelp.dll don't correctly put the memory around + // the faulting instruction pointer into the minidump. This + // callback will ensure that it gets included. + if (exinfo) { + // Find a memory region of 256 bytes centered on the + // faulting instruction pointer. + const ULONG64 instruction_pointer = +#if defined(_M_IX86) + exinfo->ContextRecord->Eip; +#elif defined(_M_AMD64) + exinfo->ContextRecord->Rip; +#elif defined(_M_ARM64) + exinfo->ContextRecord->Pc; +#else +#error Unsupported platform +#endif + + MEMORY_BASIC_INFORMATION info; + if (VirtualQueryEx(process, + reinterpret_cast(instruction_pointer), + &info, + sizeof(MEMORY_BASIC_INFORMATION)) != 0 && + info.State == MEM_COMMIT) { + // Attempt to get 128 bytes before and after the instruction + // pointer, but settle for whatever's available up to the + // boundaries of the memory region. + const ULONG64 kIPMemorySize = 256; + ULONG64 base = + (std::max)(reinterpret_cast(info.BaseAddress), + instruction_pointer - (kIPMemorySize / 2)); + ULONG64 end_of_range = + (std::min)(instruction_pointer + (kIPMemorySize / 2), + reinterpret_cast(info.BaseAddress) + + info.RegionSize); + ULONG size = static_cast(end_of_range - base); + + AppMemory& elt = app_memory_info_.front(); + elt.ptr = base; + elt.length = size; + } + } + + MinidumpCallbackContext context; + context.iter = app_memory_info_.begin(); + context.end = app_memory_info_.end(); + + // Skip the reserved element if there was no instruction memory + if (context.iter->ptr == 0) { + context.iter++; + } + + MINIDUMP_CALLBACK_INFORMATION callback; + callback.CallbackRoutine = MinidumpWriteDumpCallback; + callback.CallbackParam = reinterpret_cast(&context); + + // The explicit comparison to TRUE avoids a warning (C4800). + success = (minidump_write_dump_(process, + GetProcessId(process), + dump_file, + dump_type_, + exinfo ? &except_info : NULL, + &user_streams, + &callback) == TRUE); + + CloseHandle(dump_file); + } + } + + return success; +} + +void ExceptionHandler::UpdateNextID() { + assert(uuid_create_); + UUID id = {0}; + if (uuid_create_) { + uuid_create_(&id); + } + next_minidump_id_ = GUIDString::GUIDToWString(&id); + next_minidump_id_c_ = next_minidump_id_.c_str(); + + wchar_t minidump_path[MAX_PATH]; + swprintf(minidump_path, MAX_PATH, L"%s\\%s.dmp", + dump_path_c_, next_minidump_id_c_); + + // remove when VC++7.1 is no longer supported + minidump_path[MAX_PATH - 1] = L'\0'; + + next_minidump_path_ = minidump_path; + next_minidump_path_c_ = next_minidump_path_.c_str(); +} + +void ExceptionHandler::RegisterAppMemory(void* ptr, size_t length) { + AppMemoryList::iterator iter = + std::find(app_memory_info_.begin(), app_memory_info_.end(), ptr); + if (iter != app_memory_info_.end()) { + // Don't allow registering the same pointer twice. + return; + } + + AppMemory app_memory; + app_memory.ptr = reinterpret_cast(ptr); + app_memory.length = static_cast(length); + app_memory_info_.push_back(app_memory); +} + +void ExceptionHandler::UnregisterAppMemory(void* ptr) { + AppMemoryList::iterator iter = + std::find(app_memory_info_.begin(), app_memory_info_.end(), ptr); + if (iter != app_memory_info_.end()) { + app_memory_info_.erase(iter); + } +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/windows/handler/exception_handler.gyp b/shared/sentry/external/breakpad/src/client/windows/handler/exception_handler.gyp new file mode 100644 index 000000000..c5733277d --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/handler/exception_handler.gyp @@ -0,0 +1,47 @@ +# Copyright 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. + +{ + 'includes': [ + '../../../build/common.gypi', + ], + 'targets': [ + { + 'target_name': 'exception_handler', + 'type': 'static_library', + 'sources': [ + "exception_handler.cc", + "exception_handler.h", + ], + 'dependencies': [ + '../breakpad_client.gyp:common', + '../crash_generation/crash_generation.gyp:crash_generation_server', + ] + }, + ], +} diff --git a/shared/sentry/external/breakpad/src/client/windows/handler/exception_handler.h b/shared/sentry/external/breakpad/src/client/windows/handler/exception_handler.h new file mode 100644 index 000000000..eb5adaac5 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/handler/exception_handler.h @@ -0,0 +1,524 @@ +// 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. + +// ExceptionHandler can write a minidump file when an exception occurs, +// or when WriteMinidump() is called explicitly by your program. +// +// To have the exception handler write minidumps when an uncaught exception +// (crash) occurs, you should create an instance early in the execution +// of your program, and keep it around for the entire time you want to +// have crash handling active (typically, until shutdown). +// +// If you want to write minidumps without installing the exception handler, +// you can create an ExceptionHandler with install_handler set to false, +// then call WriteMinidump. You can also use this technique if you want to +// use different minidump callbacks for different call sites. +// +// In either case, a callback function is called when a minidump is written, +// which receives the unqiue id of the minidump. The caller can use this +// id to collect and write additional application state, and to launch an +// external crash-reporting application. +// +// It is important that creation and destruction of ExceptionHandler objects +// be nested cleanly, when using install_handler = true. +// Avoid the following pattern: +// ExceptionHandler *e = new ExceptionHandler(...); +// ExceptionHandler *f = new ExceptionHandler(...); +// delete e; +// This will put the exception filter stack into an inconsistent state. + +#ifndef CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__ +#define CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__ + +#include +#include +#include +#include + +#pragma warning(push) +// Disable exception handler warnings. +#pragma warning(disable:4530) + +#include +#include +#include + +#include "client/windows/common/ipc_protocol.h" +#include "client/windows/crash_generation/crash_generation_client.h" +#include "common/scoped_ptr.h" +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +using std::vector; +using std::wstring; + +// These entries store a list of memory regions that the client wants included +// in the minidump. +struct AppMemory { + ULONG64 ptr; + ULONG length; + + bool operator==(const struct AppMemory& other) const { + return ptr == other.ptr; + } + + bool operator==(const void* other) const { + return ptr == reinterpret_cast(other); + } +}; +typedef std::list AppMemoryList; + +class ExceptionHandler { + public: + // A callback function to run before Breakpad performs any substantial + // processing of an exception. A FilterCallback is called before writing + // a minidump. context is the parameter supplied by the user as + // callback_context when the handler was created. exinfo points to the + // exception record, if any; assertion points to assertion information, + // if any. + // + // If a FilterCallback returns true, Breakpad will continue processing, + // attempting to write a minidump. If a FilterCallback returns false, + // Breakpad will immediately report the exception as unhandled without + // writing a minidump, allowing another handler the opportunity to handle it. + typedef bool (*FilterCallback)(void* context, EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion); + + // A callback function to run after the minidump has been written. + // minidump_id is a unique id for the dump, so the minidump + // file is \.dmp. context is the parameter supplied + // by the user as callback_context when the handler was created. exinfo + // points to the exception record, or NULL if no exception occurred. + // succeeded indicates whether a minidump file was successfully written. + // assertion points to information about an assertion if the handler was + // invoked by an assertion. + // + // If an exception occurred and the callback returns true, Breakpad will treat + // the exception as fully-handled, suppressing any other handlers from being + // notified of the exception. If the callback returns false, Breakpad will + // treat the exception as unhandled, and allow another handler to handle it. + // If there are no other handlers, Breakpad will report the exception to the + // system as unhandled, allowing a debugger or native crash dialog the + // opportunity to handle the exception. Most callback implementations + // should normally return the value of |succeeded|, or when they wish to + // not report an exception of handled, false. Callbacks will rarely want to + // return true directly (unless |succeeded| is true). + // + // For out-of-process dump generation, dump path and minidump ID will always + // be NULL. In case of out-of-process dump generation, the dump path and + // minidump id are controlled by the server process and are not communicated + // back to the crashing process. + typedef bool (*MinidumpCallback)(const wchar_t* dump_path, + const wchar_t* minidump_id, + void* context, + EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion, + bool succeeded); + + // HandlerType specifies which types of handlers should be installed, if + // any. Use HANDLER_NONE for an ExceptionHandler that remains idle, + // without catching any failures on its own. This type of handler may + // still be triggered by calling WriteMinidump. Otherwise, use a + // combination of the other HANDLER_ values, or HANDLER_ALL to install + // all handlers. + enum HandlerType { + HANDLER_NONE = 0, + HANDLER_EXCEPTION = 1 << 0, // SetUnhandledExceptionFilter + HANDLER_INVALID_PARAMETER = 1 << 1, // _set_invalid_parameter_handler + HANDLER_PURECALL = 1 << 2, // _set_purecall_handler + HANDLER_ALL = HANDLER_EXCEPTION | + HANDLER_INVALID_PARAMETER | + HANDLER_PURECALL + }; + + // Creates a new ExceptionHandler instance to handle writing minidumps. + // Before writing a minidump, the optional filter callback will be called. + // Its return value determines whether or not Breakpad should write a + // minidump. Minidump files will be written to dump_path, and the optional + // callback is called after writing the dump file, as described above. + // handler_types specifies the types of handlers that should be installed. + ExceptionHandler(const wstring& dump_path, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + int handler_types); + + // Creates a new ExceptionHandler instance that can attempt to perform + // out-of-process dump generation if pipe_name is not NULL. If pipe_name is + // NULL, or if out-of-process dump generation registration step fails, + // in-process dump generation will be used. This also allows specifying + // the dump type to generate. + ExceptionHandler(const wstring& dump_path, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + int handler_types, + MINIDUMP_TYPE dump_type, + const wchar_t* pipe_name, + const CustomClientInfo* custom_info); + + // As above, creates a new ExceptionHandler instance to perform + // out-of-process dump generation if the given pipe_handle is not NULL. + ExceptionHandler(const wstring& dump_path, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + int handler_types, + MINIDUMP_TYPE dump_type, + HANDLE pipe_handle, + const CustomClientInfo* custom_info); + + // ExceptionHandler that ENSURES out-of-process dump generation. Expects a + // crash generation client that is already registered with a crash generation + // server. Takes ownership of the passed-in crash_generation_client. + // + // Usage example: + // crash_generation_client = new CrashGenerationClient(..); + // if (crash_generation_client->Register()) { + // // Registration with the crash generation server succeeded. + // // Out-of-process dump generation is guaranteed. + // g_handler = new ExceptionHandler(.., crash_generation_client, ..); + // return true; + // } + ExceptionHandler(const wstring& dump_path, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + int handler_types, + CrashGenerationClient* crash_generation_client); + + ~ExceptionHandler(); + + // Get and set the minidump path. + wstring dump_path() const { return dump_path_; } + void set_dump_path(const wstring& dump_path) { + dump_path_ = dump_path; + dump_path_c_ = dump_path_.c_str(); + UpdateNextID(); // Necessary to put dump_path_ in next_minidump_path_. + } + + // Requests that a previously reported crash be uploaded. + bool RequestUpload(DWORD crash_id); + + // Writes a minidump immediately. This can be used to capture the + // execution state independently of a crash. Returns true on success. + bool WriteMinidump(); + + // Writes a minidump immediately, with the user-supplied exception + // information. + bool WriteMinidumpForException(EXCEPTION_POINTERS* exinfo); + + // Convenience form of WriteMinidump which does not require an + // ExceptionHandler instance. + static bool WriteMinidump(const wstring& dump_path, + MinidumpCallback callback, void* callback_context, + MINIDUMP_TYPE dump_type = MiniDumpNormal); + + // Write a minidump of |child| immediately. This can be used to + // capture the execution state of |child| independently of a crash. + // Pass a meaningful |child_blamed_thread| to make that thread in + // the child process the one from which a crash signature is + // extracted. + static bool WriteMinidumpForChild(HANDLE child, + DWORD child_blamed_thread, + const wstring& dump_path, + MinidumpCallback callback, + void* callback_context, + MINIDUMP_TYPE dump_type = MiniDumpNormal); + + // Get the thread ID of the thread requesting the dump (either the exception + // thread or any other thread that called WriteMinidump directly). This + // may be useful if you want to include additional thread state in your + // dumps. + DWORD get_requesting_thread_id() const { return requesting_thread_id_; } + + // Controls behavior of EXCEPTION_BREAKPOINT and EXCEPTION_SINGLE_STEP. + bool get_handle_debug_exceptions() const { return handle_debug_exceptions_; } + void set_handle_debug_exceptions(bool handle_debug_exceptions) { + handle_debug_exceptions_ = handle_debug_exceptions; + } + + // Controls behavior of EXCEPTION_INVALID_HANDLE. + bool get_consume_invalid_handle_exceptions() const { + return consume_invalid_handle_exceptions_; + } + void set_consume_invalid_handle_exceptions( + bool consume_invalid_handle_exceptions) { + consume_invalid_handle_exceptions_ = consume_invalid_handle_exceptions; + } + + // Returns whether out-of-process dump generation is used or not. + bool IsOutOfProcess() const { return crash_generation_client_.get() != NULL; } + + // Calling RegisterAppMemory(p, len) causes len bytes starting + // at address p to be copied to the minidump when a crash happens. + void RegisterAppMemory(void* ptr, size_t length); + void UnregisterAppMemory(void* ptr); + + private: + friend class AutoExceptionHandler; + + // Initializes the instance with given values. + void Initialize(const wstring& dump_path, + FilterCallback filter, + MinidumpCallback callback, + void* callback_context, + int handler_types, + MINIDUMP_TYPE dump_type, + const wchar_t* pipe_name, + HANDLE pipe_handle, + CrashGenerationClient* crash_generation_client, + const CustomClientInfo* custom_info); + + // Function pointer type for MiniDumpWriteDump, which is looked up + // dynamically. + typedef BOOL (WINAPI *MiniDumpWriteDump_type)( + HANDLE hProcess, + DWORD dwPid, + HANDLE hFile, + MINIDUMP_TYPE DumpType, + CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, + CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, + CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam); + + // Function pointer type for UuidCreate, which is looked up dynamically. + typedef RPC_STATUS (RPC_ENTRY *UuidCreate_type)(UUID* Uuid); + + // Runs the main loop for the exception handler thread. + static DWORD WINAPI ExceptionHandlerThreadMain(void* lpParameter); + + // Called on the exception thread when an unhandled exception occurs. + // Signals the exception handler thread to handle the exception. + static LONG WINAPI HandleException(EXCEPTION_POINTERS* exinfo); + +#if _MSC_VER >= 1400 // MSVC 2005/8 + // This function will be called by some CRT functions when they detect + // that they were passed an invalid parameter. Note that in _DEBUG builds, + // the CRT may display an assertion dialog before calling this function, + // and the function will not be called unless the assertion dialog is + // dismissed by clicking "Ignore." + static void HandleInvalidParameter(const wchar_t* expression, + const wchar_t* function, + const wchar_t* file, + unsigned int line, + uintptr_t reserved); +#endif // _MSC_VER >= 1400 + + // This function will be called by the CRT when a pure virtual + // function is called. + static void HandlePureVirtualCall(); + + // This is called on the exception thread or on another thread that + // the user wishes to produce a dump from. It calls + // WriteMinidumpWithException on the handler thread, avoiding stack + // overflows and inconsistent dumps due to writing the dump from + // the exception thread. If the dump is requested as a result of an + // exception, exinfo contains exception information, otherwise, it + // is NULL. If the dump is requested as a result of an assertion + // (such as an invalid parameter being passed to a CRT function), + // assertion contains data about the assertion, otherwise, it is NULL. + bool WriteMinidumpOnHandlerThread(EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion); + + // This function is called on the handler thread. It calls into + // WriteMinidumpWithExceptionForProcess() with a handle to the + // current process. requesting_thread_id is the ID of the thread + // that requested the dump. If the dump is requested as a result of + // an exception, exinfo contains exception information, otherwise, + // it is NULL. + bool WriteMinidumpWithException(DWORD requesting_thread_id, + EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion); + + // This function is used as a callback when calling MinidumpWriteDump, + // in order to add additional memory regions to the dump. + static BOOL CALLBACK MinidumpWriteDumpCallback( + PVOID context, + const PMINIDUMP_CALLBACK_INPUT callback_input, + PMINIDUMP_CALLBACK_OUTPUT callback_output); + + // This function does the actual writing of a minidump. It is + // called on the handler thread. requesting_thread_id is the ID of + // the thread that requested the dump, if that information is + // meaningful. If the dump is requested as a result of an + // exception, exinfo contains exception information, otherwise, it + // is NULL. process is the one that will be dumped. If + // requesting_thread_id is meaningful and should be added to the + // minidump, write_requester_stream is |true|. + bool WriteMinidumpWithExceptionForProcess(DWORD requesting_thread_id, + EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion, + HANDLE process, + bool write_requester_stream); + + // Generates a new ID and stores it in next_minidump_id_, and stores the + // path of the next minidump to be written in next_minidump_path_. + void UpdateNextID(); + + FilterCallback filter_; + MinidumpCallback callback_; + void* callback_context_; + + scoped_ptr crash_generation_client_; + + // The directory in which a minidump will be written, set by the dump_path + // argument to the constructor, or set_dump_path. + wstring dump_path_; + + // The basename of the next minidump to be written, without the extension. + wstring next_minidump_id_; + + // The full pathname of the next minidump to be written, including the file + // extension. + wstring next_minidump_path_; + + // Pointers to C-string representations of the above. These are set when + // the above wstring versions are set in order to avoid calling c_str during + // an exception, as c_str may attempt to allocate heap memory. These + // pointers are not owned by the ExceptionHandler object, but their lifetimes + // should be equivalent to the lifetimes of the associated wstring, provided + // that the wstrings are not altered. + const wchar_t* dump_path_c_; + const wchar_t* next_minidump_id_c_; + const wchar_t* next_minidump_path_c_; + + HMODULE dbghelp_module_; + MiniDumpWriteDump_type minidump_write_dump_; + MINIDUMP_TYPE dump_type_; + + HMODULE rpcrt4_module_; + UuidCreate_type uuid_create_; + + // Tracks the handler types that were installed according to the + // handler_types constructor argument. + int handler_types_; + + // When installed_handler_ is true, previous_filter_ is the unhandled + // exception filter that was set prior to installing ExceptionHandler as + // the unhandled exception filter and pointing it to |this|. NULL indicates + // that there is no previous unhandled exception filter. + LPTOP_LEVEL_EXCEPTION_FILTER previous_filter_; + +#if _MSC_VER >= 1400 // MSVC 2005/8 + // Beginning in VC 8, the CRT provides an invalid parameter handler that will + // be called when some CRT functions are passed invalid parameters. In + // earlier CRTs, the same conditions would cause unexpected behavior or + // crashes. + _invalid_parameter_handler previous_iph_; +#endif // _MSC_VER >= 1400 + + // The CRT allows you to override the default handler for pure + // virtual function calls. + _purecall_handler previous_pch_; + + // The exception handler thread. + HANDLE handler_thread_; + + // True if the exception handler is being destroyed. + // Starting with MSVC 2005, Visual C has stronger guarantees on volatile vars. + // It has release semantics on write and acquire semantics on reads. + // See the msdn documentation. + volatile bool is_shutdown_; + + // The critical section enforcing the requirement that only one exception be + // handled by a handler at a time. + CRITICAL_SECTION handler_critical_section_; + + // Semaphores used to move exception handling between the exception thread + // and the handler thread. handler_start_semaphore_ is signalled by the + // exception thread to wake up the handler thread when an exception occurs. + // handler_finish_semaphore_ is signalled by the handler thread to wake up + // the exception thread when handling is complete. + HANDLE handler_start_semaphore_; + HANDLE handler_finish_semaphore_; + + // The next 2 fields contain data passed from the requesting thread to + // the handler thread. + + // The thread ID of the thread requesting the dump (either the exception + // thread or any other thread that called WriteMinidump directly). + DWORD requesting_thread_id_; + + // The exception info passed to the exception handler on the exception + // thread, if an exception occurred. NULL for user-requested dumps. + EXCEPTION_POINTERS* exception_info_; + + // If the handler is invoked due to an assertion, this will contain a + // pointer to the assertion information. It is NULL at other times. + MDRawAssertionInfo* assertion_; + + // The return value of the handler, passed from the handler thread back to + // the requesting thread. + bool handler_return_value_; + + // If true, the handler will intercept EXCEPTION_BREAKPOINT and + // EXCEPTION_SINGLE_STEP exceptions. Leave this false (the default) + // to not interfere with debuggers. + bool handle_debug_exceptions_; + + // If true, the handler will consume any EXCEPTION_INVALID_HANDLE exceptions. + // Leave this false (the default) to handle these exceptions as normal. + bool consume_invalid_handle_exceptions_; + + // Callers can request additional memory regions to be included in + // the dump. + AppMemoryList app_memory_info_; + + // A stack of ExceptionHandler objects that have installed unhandled + // exception filters. This vector is used by HandleException to determine + // which ExceptionHandler object to route an exception to. When an + // ExceptionHandler is created with install_handler true, it will append + // itself to this list. + static vector* handler_stack_; + + // The index of the ExceptionHandler in handler_stack_ that will handle the + // next exception. Note that 0 means the last entry in handler_stack_, 1 + // means the next-to-last entry, and so on. This is used by HandleException + // to support multiple stacked Breakpad handlers. + static LONG handler_stack_index_; + + // handler_stack_critical_section_ guards operations on handler_stack_ and + // handler_stack_index_. The critical section is initialized by the + // first instance of the class and destroyed by the last instance of it. + static CRITICAL_SECTION handler_stack_critical_section_; + + // The number of instances of this class. + static volatile LONG instance_count_; + + // disallow copy ctor and operator= + explicit ExceptionHandler(const ExceptionHandler&); + void operator=(const ExceptionHandler&); +}; + +} // namespace google_breakpad + +#pragma warning(pop) + +#endif // CLIENT_WINDOWS_HANDLER_EXCEPTION_HANDLER_H__ diff --git a/shared/sentry/external/breakpad/src/client/windows/sender/crash_report_sender.cc b/shared/sentry/external/breakpad/src/client/windows/sender/crash_report_sender.cc new file mode 100644 index 000000000..7fc644839 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/sender/crash_report_sender.cc @@ -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. + +// Disable exception handler warnings. +#pragma warning( disable : 4530 ) + +#include + +#include "client/windows/sender/crash_report_sender.h" +#include "common/windows/http_upload.h" + +#if _MSC_VER < 1400 // MSVC 2005/8 +// Older MSVC doesn't have fscanf_s, but they are compatible as long as +// we don't use the string conversions (%s/%c/%S/%C). +#define fscanf_s fscanf +#endif + +namespace google_breakpad { + +static const char kCheckpointSignature[] = "GBP1\n"; + +CrashReportSender::CrashReportSender(const wstring& checkpoint_file) + : checkpoint_file_(checkpoint_file), + max_reports_per_day_(-1), + last_sent_date_(-1), + reports_sent_(0) { + FILE* fd; + if (OpenCheckpointFile(L"r", &fd) == 0) { + ReadCheckpoint(fd); + fclose(fd); + } +} + +ReportResult CrashReportSender::SendCrashReport( + const wstring& url, const map& parameters, + const map& files, wstring* report_code) { + int today = GetCurrentDate(); + if (today == last_sent_date_ && + max_reports_per_day_ != -1 && + reports_sent_ >= max_reports_per_day_) { + return RESULT_THROTTLED; + } + + int http_response = 0; + bool result = HTTPUpload::SendMultipartPostRequest( + url, parameters, files, NULL, report_code, + &http_response); + + if (result) { + ReportSent(today); + return RESULT_SUCCEEDED; + } else if (http_response >= 400 && http_response < 500) { + return RESULT_REJECTED; + } else { + return RESULT_FAILED; + } +} + +void CrashReportSender::ReadCheckpoint(FILE* fd) { + char buf[128]; + if (!fgets(buf, sizeof(buf), fd) || + strcmp(buf, kCheckpointSignature) != 0) { + return; + } + + if (fscanf_s(fd, "%d\n", &last_sent_date_) != 1) { + last_sent_date_ = -1; + return; + } + if (fscanf_s(fd, "%d\n", &reports_sent_) != 1) { + reports_sent_ = 0; + return; + } +} + +void CrashReportSender::ReportSent(int today) { + // Update the report stats + if (today != last_sent_date_) { + last_sent_date_ = today; + reports_sent_ = 0; + } + ++reports_sent_; + + // Update the checkpoint file + FILE* fd; + if (OpenCheckpointFile(L"w", &fd) == 0) { + fputs(kCheckpointSignature, fd); + fprintf(fd, "%d\n", last_sent_date_); + fprintf(fd, "%d\n", reports_sent_); + fclose(fd); + } +} + +int CrashReportSender::GetCurrentDate() const { + SYSTEMTIME system_time; + GetSystemTime(&system_time); + return (system_time.wYear * 10000) + (system_time.wMonth * 100) + + system_time.wDay; +} + +int CrashReportSender::OpenCheckpointFile(const wchar_t* mode, FILE** fd) { + if (checkpoint_file_.empty()) { + return ENOENT; + } +#if _MSC_VER >= 1400 // MSVC 2005/8 + return _wfopen_s(fd, checkpoint_file_.c_str(), mode); +#else + *fd = _wfopen(checkpoint_file_.c_str(), mode); + if (*fd == NULL) { + return errno; + } + return 0; +#endif +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/windows/sender/crash_report_sender.gyp b/shared/sentry/external/breakpad/src/client/windows/sender/crash_report_sender.gyp new file mode 100644 index 000000000..dc8583a0a --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/sender/crash_report_sender.gyp @@ -0,0 +1,46 @@ +# Copyright 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. + +{ + 'includes': [ + '../../../build/common.gypi', + ], + 'targets': [ + { + 'target_name': 'crash_report_sender', + 'type': 'static_library', + 'sources': [ + 'crash_report_sender.cc', + 'crash_report_sender.h', + ], + 'dependencies': [ + '../breakpad_client.gyp:common' + ], + }, + ], +} diff --git a/shared/sentry/external/breakpad/src/client/windows/sender/crash_report_sender.h b/shared/sentry/external/breakpad/src/client/windows/sender/crash_report_sender.h new file mode 100644 index 000000000..e6055857c --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/sender/crash_report_sender.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. + +#ifndef CLIENT_WINDOWS_SENDER_CRASH_REPORT_SENDER_H__ +#define CLIENT_WINDOWS_SENDER_CRASH_REPORT_SENDER_H__ + +// CrashReportSender is a "static" class which provides an API to upload +// crash reports via HTTP(S). A crash report is formatted as a multipart POST +// request, which contains a set of caller-supplied string key/value pairs, +// and a minidump file to upload. +// +// To use this library in your project, you will need to link against +// wininet.lib. + +#pragma warning( push ) +// Disable exception handler warnings. +#pragma warning( disable : 4530 ) + +#include +#include + +namespace google_breakpad { + +using std::wstring; +using std::map; + +typedef enum { + RESULT_FAILED = 0, // Failed to communicate with the server; try later. + RESULT_REJECTED, // Successfully sent the crash report, but the + // server rejected it; don't resend this report. + RESULT_SUCCEEDED, // The server accepted the crash report. + RESULT_THROTTLED // No attempt was made to send the crash report, because + // we exceeded the maximum reports per day. +} ReportResult; + +class CrashReportSender { + public: + // Initializes a CrashReportSender instance. + // If checkpoint_file is non-empty, breakpad will persist crash report + // state to this file. A checkpoint file is required for + // set_max_reports_per_day() to function properly. + explicit CrashReportSender(const wstring& checkpoint_file); + ~CrashReportSender() {} + + // Sets the maximum number of crash reports that will be sent in a 24-hour + // period. This uses the state persisted to the checkpoint file. + // The default value of -1 means that there is no limit on reports sent. + void set_max_reports_per_day(int reports) { + max_reports_per_day_ = reports; + } + + int max_reports_per_day() const { return max_reports_per_day_; } + + // Sends the specified files, along with the map of + // name value pairs, as a multipart POST request to the given URL. + // Parameter names must contain only printable ASCII characters, + // and may not contain a quote (") character. + // Only HTTP(S) URLs are currently supported. The return value indicates + // the result of the operation (see above for possible results). + // If report_code is non-NULL and the report is sent successfully (that is, + // the return value is RESULT_SUCCEEDED), a code uniquely identifying the + // report will be returned in report_code. + // (Otherwise, report_code will be unchanged.) + ReportResult SendCrashReport(const wstring& url, + const map& parameters, + const map& files, + wstring* report_code); + + private: + // Reads persistent state from a checkpoint file. + void ReadCheckpoint(FILE* fd); + + // Called when a new report has been sent, to update the checkpoint state. + void ReportSent(int today); + + // Returns today's date (UTC) formatted as YYYYMMDD. + int GetCurrentDate() const; + + // Opens the checkpoint file with the specified mode. + // Returns zero on success, or an error code on failure. + int OpenCheckpointFile(const wchar_t* mode, FILE** fd); + + wstring checkpoint_file_; + int max_reports_per_day_; + // The last date on which we sent a report, expressed as YYYYMMDD. + int last_sent_date_; + // Number of reports sent on last_sent_date_ + int reports_sent_; + + // Disallow copy constructor and operator= + explicit CrashReportSender(const CrashReportSender&); + void operator=(const CrashReportSender&); +}; + +} // namespace google_breakpad + +#pragma warning( pop ) + +#endif // CLIENT_WINDOWS_SENDER_CRASH_REPORT_SENDER_H__ diff --git a/shared/sentry/external/breakpad/src/client/windows/tests/crash_generation_app/abstract_class.cc b/shared/sentry/external/breakpad/src/client/windows/tests/crash_generation_app/abstract_class.cc new file mode 100644 index 000000000..32f78f2b9 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/tests/crash_generation_app/abstract_class.cc @@ -0,0 +1,53 @@ +// 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 "client/windows/tests/crash_generation_app/abstract_class.h" + +namespace google_breakpad { + +Base::Base(Derived* derived) + : derived_(derived) { +} + +Base::~Base() { + derived_->DoSomething(); +} + +#pragma warning(push) +#pragma warning(disable:4355) +// Disable warning C4355: 'this' : used in base member initializer list. +Derived::Derived() + : Base(this) { // C4355 +} +#pragma warning(pop) + +void Derived::DoSomething() { +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/client/windows/tests/crash_generation_app/abstract_class.h b/shared/sentry/external/breakpad/src/client/windows/tests/crash_generation_app/abstract_class.h new file mode 100644 index 000000000..e3f2a4f37 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/tests/crash_generation_app/abstract_class.h @@ -0,0 +1,57 @@ +// 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. + +#ifndef CLIENT_WINDOWS_TESTS_CRASH_GENERATION_APP_ABSTRACT_CLASS_H__ +#define CLIENT_WINDOWS_TESTS_CRASH_GENERATION_APP_ABSTRACT_CLASS_H__ + +namespace google_breakpad { + +// Dummy classes to help generate a pure call violation. + +class Derived; + +class Base { + public: + Base(Derived* derived); + virtual ~Base(); + virtual void DoSomething() = 0; + + private: + Derived* derived_; +}; + +class Derived : public Base { + public: + Derived(); + virtual void DoSomething(); +}; + +} // namespace google_breakpad + +#endif // CLIENT_WINDOWS_TESTS_CRASH_GENERATION_APP_CRASH_GENERATION_APP_H__ diff --git a/shared/sentry/external/breakpad/src/client/windows/tests/crash_generation_app/crash_generation_app.cc b/shared/sentry/external/breakpad/src/client/windows/tests/crash_generation_app/crash_generation_app.cc new file mode 100644 index 000000000..0d837e521 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/tests/crash_generation_app/crash_generation_app.cc @@ -0,0 +1,522 @@ +// 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. + +// crash_generation_app.cpp : Defines the entry point for the application. +// + +#include "client/windows/tests/crash_generation_app/crash_generation_app.h" + +#include +#include + +#include "client/windows/crash_generation/client_info.h" +#include "client/windows/crash_generation/crash_generation_server.h" +#include "client/windows/handler/exception_handler.h" +#include "client/windows/common/ipc_protocol.h" + +#include "client/windows/tests/crash_generation_app/abstract_class.h" + +namespace google_breakpad { + +const int kMaxLoadString = 100; +const wchar_t kPipeName[] = L"\\\\.\\pipe\\BreakpadCrashServices\\TestServer"; + +const DWORD kEditBoxStyles = WS_CHILD | + WS_VISIBLE | + WS_VSCROLL | + ES_LEFT | + ES_MULTILINE | + ES_AUTOVSCROLL | + ES_READONLY; + +// Maximum length of a line in the edit box. +const size_t kMaximumLineLength = 256; + +// CS to access edit control in a thread safe way. +static CRITICAL_SECTION* cs_edit = NULL; + +// Edit control. +static HWND client_status_edit_box; + +HINSTANCE current_instance; // Current instance. +TCHAR title[kMaxLoadString]; // Title bar text. +TCHAR window_class[kMaxLoadString]; // Main window class name. + +ATOM MyRegisterClass(HINSTANCE instance); +BOOL InitInstance(HINSTANCE, int); +LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +INT_PTR CALLBACK About(HWND, UINT, WPARAM, LPARAM); + +static size_t kCustomInfoCount = 2; +static CustomInfoEntry kCustomInfoEntries[] = { + CustomInfoEntry(L"prod", L"CrashTestApp"), + CustomInfoEntry(L"ver", L"1.0"), +}; + +static ExceptionHandler* handler = NULL; +static CrashGenerationServer* crash_server = NULL; + +// Registers the window class. +// +// This function and its usage are only necessary if you want this code +// to be compatible with Win32 systems prior to the 'RegisterClassEx' +// function that was added to Windows 95. It is important to call this +// function so that the application will get 'well formed' small icons +// associated with it. +ATOM MyRegisterClass(HINSTANCE instance) { + WNDCLASSEX wcex; + wcex.cbSize = sizeof(WNDCLASSEX); + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = WndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = instance; + wcex.hIcon = LoadIcon(instance, + MAKEINTRESOURCE(IDI_CRASHGENERATIONAPP)); + wcex.hCursor = LoadCursor(NULL, IDC_ARROW); + wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); + wcex.lpszMenuName = MAKEINTRESOURCE(IDC_CRASHGENERATIONAPP); + wcex.lpszClassName = window_class; + wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); + + return RegisterClassEx(&wcex); +} + +// Saves instance handle and creates main window +// +// In this function, we save the instance handle in a global variable and +// create and display the main program window. +BOOL InitInstance(HINSTANCE instance, int command_show) { + current_instance = instance; + HWND wnd = CreateWindow(window_class, + title, + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, + 0, + CW_USEDEFAULT, + 0, + NULL, + NULL, + instance, + NULL); + + if (!wnd) { + return FALSE; + } + + ShowWindow(wnd, command_show); + UpdateWindow(wnd); + + return TRUE; +} + +static void AppendTextToEditBox(TCHAR* text) { + EnterCriticalSection(cs_edit); + SYSTEMTIME current_time; + GetLocalTime(¤t_time); + TCHAR line[kMaximumLineLength]; + int result = swprintf_s(line, + kMaximumLineLength, + L"[%.2d-%.2d-%.4d %.2d:%.2d:%.2d] %s", + current_time.wMonth, + current_time.wDay, + current_time.wYear, + current_time.wHour, + current_time.wMinute, + current_time.wSecond, + text); + + if (result == -1) { + return; + } + + int length = GetWindowTextLength(client_status_edit_box); + SendMessage(client_status_edit_box, + EM_SETSEL, + (WPARAM)length, + (LPARAM)length); + SendMessage(client_status_edit_box, + EM_REPLACESEL, + (WPARAM)FALSE, + (LPARAM)line); + LeaveCriticalSection(cs_edit); +} + +static DWORD WINAPI AppendTextWorker(void* context) { + TCHAR* text = reinterpret_cast(context); + + AppendTextToEditBox(text); + delete[] text; + + return 0; +} + +bool ShowDumpResults(const wchar_t* dump_path, + const wchar_t* minidump_id, + void* context, + EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion, + bool succeeded) { + TCHAR* text = new TCHAR[kMaximumLineLength]; + text[0] = _T('\0'); + int result = swprintf_s(text, + kMaximumLineLength, + TEXT("Dump generation request %s\r\n"), + succeeded ? TEXT("succeeded") : TEXT("failed")); + if (result == -1) { + delete [] text; + } + + QueueUserWorkItem(AppendTextWorker, text, WT_EXECUTEDEFAULT); + return succeeded; +} + +static void ShowClientConnected(void* context, + const ClientInfo* client_info) { + TCHAR* line = new TCHAR[kMaximumLineLength]; + line[0] = _T('\0'); + int result = swprintf_s(line, + kMaximumLineLength, + L"Client connected:\t\t%d\r\n", + client_info->pid()); + + if (result == -1) { + delete[] line; + return; + } + + QueueUserWorkItem(AppendTextWorker, line, WT_EXECUTEDEFAULT); +} + +static void ShowClientCrashed(void* context, + const ClientInfo* client_info, + const wstring* dump_path) { + TCHAR* line = new TCHAR[kMaximumLineLength]; + line[0] = _T('\0'); + int result = swprintf_s(line, + kMaximumLineLength, + TEXT("Client requested dump:\t%d\r\n"), + client_info->pid()); + + if (result == -1) { + delete[] line; + return; + } + + QueueUserWorkItem(AppendTextWorker, line, WT_EXECUTEDEFAULT); + + CustomClientInfo custom_info = client_info->GetCustomInfo(); + if (custom_info.count <= 0) { + return; + } + + wstring str_line; + for (size_t i = 0; i < custom_info.count; ++i) { + if (i > 0) { + str_line += L", "; + } + str_line += custom_info.entries[i].name; + str_line += L": "; + str_line += custom_info.entries[i].value; + } + + line = new TCHAR[kMaximumLineLength]; + line[0] = _T('\0'); + result = swprintf_s(line, + kMaximumLineLength, + L"%s\n", + str_line.c_str()); + if (result == -1) { + delete[] line; + return; + } + QueueUserWorkItem(AppendTextWorker, line, WT_EXECUTEDEFAULT); +} + +static void ShowClientExited(void* context, + const ClientInfo* client_info) { + TCHAR* line = new TCHAR[kMaximumLineLength]; + line[0] = _T('\0'); + int result = swprintf_s(line, + kMaximumLineLength, + TEXT("Client exited:\t\t%d\r\n"), + client_info->pid()); + + if (result == -1) { + delete[] line; + return; + } + + QueueUserWorkItem(AppendTextWorker, line, WT_EXECUTEDEFAULT); +} + +void CrashServerStart() { + // Do not create another instance of the server. + if (crash_server) { + return; + } + + std::wstring dump_path = L"C:\\Dumps\\"; + + if (_wmkdir(dump_path.c_str()) && (errno != EEXIST)) { + MessageBoxW(NULL, L"Unable to create dump directory", L"Dumper", MB_OK); + return; + } + + crash_server = new CrashGenerationServer(kPipeName, + NULL, + ShowClientConnected, + NULL, + ShowClientCrashed, + NULL, + ShowClientExited, + NULL, + NULL, + NULL, + true, + &dump_path); + + if (!crash_server->Start()) { + MessageBoxW(NULL, L"Unable to start server", L"Dumper", MB_OK); + delete crash_server; + crash_server = NULL; + } +} + +void CrashServerStop() { + delete crash_server; + crash_server = NULL; +} + +void DerefZeroCrash() { + int* x = 0; + *x = 1; +} + +void InvalidParamCrash() { + printf(NULL); +} + +void PureCallCrash() { + Derived derived; +} + +void RequestDump() { + if (!handler->WriteMinidump()) { + MessageBoxW(NULL, L"Dump request failed", L"Dumper", MB_OK); + } + kCustomInfoEntries[1].set_value(L"1.1"); +} + +void CleanUp() { + if (cs_edit) { + DeleteCriticalSection(cs_edit); + delete cs_edit; + } + + if (handler) { + delete handler; + } + + if (crash_server) { + delete crash_server; + } +} + +// Processes messages for the main window. +// +// WM_COMMAND - process the application menu. +// WM_PAINT - Paint the main window. +// WM_DESTROY - post a quit message and return. +LRESULT CALLBACK WndProc(HWND wnd, + UINT message, + WPARAM w_param, + LPARAM l_param) { + int message_id; + int message_event; + PAINTSTRUCT ps; + HDC hdc; + + HINSTANCE instance = (HINSTANCE)GetWindowLongPtr(wnd, GWLP_HINSTANCE); + + switch (message) { + case WM_COMMAND: + // Parse the menu selections. + message_id = LOWORD(w_param); + message_event = HIWORD(w_param); + switch (message_id) { + case IDM_ABOUT: + DialogBox(current_instance, + MAKEINTRESOURCE(IDD_ABOUTBOX), + wnd, + About); + break; + case IDM_EXIT: + DestroyWindow(wnd); + break; + case ID_SERVER_START: + CrashServerStart(); + break; + case ID_SERVER_STOP: + CrashServerStop(); + break; + case ID_CLIENT_DEREFZERO: + DerefZeroCrash(); + break; + case ID_CLIENT_INVALIDPARAM: + InvalidParamCrash(); + break; + case ID_CLIENT_PURECALL: + PureCallCrash(); + break; + case ID_CLIENT_REQUESTEXPLICITDUMP: + RequestDump(); + break; + default: + return DefWindowProc(wnd, message, w_param, l_param); + } + break; + case WM_CREATE: + client_status_edit_box = CreateWindow(TEXT("EDIT"), + NULL, + kEditBoxStyles, + 0, + 0, + 0, + 0, + wnd, + NULL, + instance, + NULL); + break; + case WM_SIZE: + // Make the edit control the size of the window's client area. + MoveWindow(client_status_edit_box, + 0, + 0, + LOWORD(l_param), // width of client area. + HIWORD(l_param), // height of client area. + TRUE); // repaint window. + break; + case WM_SETFOCUS: + SetFocus(client_status_edit_box); + break; + case WM_PAINT: + hdc = BeginPaint(wnd, &ps); + EndPaint(wnd, &ps); + break; + case WM_DESTROY: + CleanUp(); + PostQuitMessage(0); + break; + default: + return DefWindowProc(wnd, message, w_param, l_param); + } + + return 0; +} + +// Message handler for about box. +INT_PTR CALLBACK About(HWND dlg, + UINT message, + WPARAM w_param, + LPARAM l_param) { + UNREFERENCED_PARAMETER(l_param); + switch (message) { + case WM_INITDIALOG: + return (INT_PTR)TRUE; + + case WM_COMMAND: + if (LOWORD(w_param) == IDOK || LOWORD(w_param) == IDCANCEL) { + EndDialog(dlg, LOWORD(w_param)); + return (INT_PTR)TRUE; + } + break; + } + + return (INT_PTR)FALSE; +} + +} // namespace google_breakpad + +int APIENTRY _tWinMain(HINSTANCE instance, + HINSTANCE previous_instance, + LPTSTR command_line, + int command_show) { + using namespace google_breakpad; + + UNREFERENCED_PARAMETER(previous_instance); + UNREFERENCED_PARAMETER(command_line); + + cs_edit = new CRITICAL_SECTION(); + InitializeCriticalSection(cs_edit); + + CustomClientInfo custom_info = {kCustomInfoEntries, kCustomInfoCount}; + + CrashServerStart(); + // This is needed for CRT to not show dialog for invalid param + // failures and instead let the code handle it. + _CrtSetReportMode(_CRT_ASSERT, 0); + handler = new ExceptionHandler(L"C:\\dumps\\", + NULL, + google_breakpad::ShowDumpResults, + NULL, + ExceptionHandler::HANDLER_ALL, + MiniDumpNormal, + kPipeName, + &custom_info); + + // Initialize global strings. + LoadString(instance, IDS_APP_TITLE, title, kMaxLoadString); + LoadString(instance, + IDC_CRASHGENERATIONAPP, + window_class, + kMaxLoadString); + MyRegisterClass(instance); + + // Perform application initialization. + if (!InitInstance(instance, command_show)) { + return FALSE; + } + + HACCEL accel_table = LoadAccelerators( + instance, + MAKEINTRESOURCE(IDC_CRASHGENERATIONAPP)); + + // Main message loop. + MSG msg; + while (GetMessage(&msg, NULL, 0, 0)) { + if (!TranslateAccelerator(msg.hwnd, accel_table, &msg)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + return static_cast(msg.wParam); +} diff --git a/shared/sentry/external/breakpad/src/client/windows/tests/crash_generation_app/crash_generation_app.gyp b/shared/sentry/external/breakpad/src/client/windows/tests/crash_generation_app/crash_generation_app.gyp new file mode 100644 index 000000000..3ce307da0 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/tests/crash_generation_app/crash_generation_app.gyp @@ -0,0 +1,63 @@ +# Copyright 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. + +{ + 'includes': [ + '../../../../build/common.gypi', + ], + 'targets': [ + { + 'target_name': 'crash_generation_app', + 'type': 'executable', + 'sources': [ + 'abstract_class.cc', + 'abstract_class.h', + 'crash_generation_app.cc', + 'crash_generation_app.h', + 'crash_generation_app.ico', + 'crash_generation_app.rc', + 'resource.h', + 'small.ico', + ], + 'libraries': [ + 'user32.lib', + ], + 'dependencies': [ + '../../breakpad_client.gyp:common', + '../../crash_generation/crash_generation.gyp:crash_generation_server', + '../../crash_generation/crash_generation.gyp:crash_generation_client', + '../../handler/exception_handler.gyp:exception_handler', + ], + 'msvs_settings': { + 'VCLinkerTool': { + 'SubSystem': '2', # Windows Subsystem as opposed to a console app + }, + }, + } + ] +} diff --git a/shared/sentry/external/breakpad/src/client/windows/tests/crash_generation_app/crash_generation_app.h b/shared/sentry/external/breakpad/src/client/windows/tests/crash_generation_app/crash_generation_app.h new file mode 100644 index 000000000..4d3bb6eb2 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/tests/crash_generation_app/crash_generation_app.h @@ -0,0 +1,35 @@ +// 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. + +#ifndef CLIENT_WINDOWS_TESTS_CRASH_GENERATION_APP_CRASH_GENERATION_APP_H__ +#define CLIENT_WINDOWS_TESTS_CRASH_GENERATION_APP_CRASH_GENERATION_APP_H__ + +#include "resource.h" + +#endif // CLIENT_WINDOWS_TESTS_CRASH_GENERATION_APP_CRASH_GENERATION_APP_H__ diff --git a/shared/sentry/external/breakpad/src/client/windows/tests/crash_generation_app/crash_generation_app.ico b/shared/sentry/external/breakpad/src/client/windows/tests/crash_generation_app/crash_generation_app.ico new file mode 100644 index 0000000000000000000000000000000000000000..d551aa3aaf80adf9b7760e2eb8de95a5c3e53df6 GIT binary patch literal 23558 zcmeI430zgx+QuJHKtxbe5gbu*030B5$VyGcDGSFOalkY&2LuvC5pp(7&2XNl96=@z zNXGH2`|DO#nx)3nwUq43A>_N=+wHsYe$U#6ePmShD&p^B>2uySylbs@uYIPy&-w#c zpc-6UYC)x+ErDgUwQ8BlZ7hIURRB*7exZ#T}AXG2* z=^weGTI5~Inq#r?3QZRh5>Vvy7AqDy*^i;1p6BY7;LQSXZ{;g>M z?fm5AM!1uJ~14CP5-;mbWJGeF0 z_iurN!(6GBI54yo4h(CB{j~e(6Em$hj*V=Fqpvo{5$e#07L+U2`wvFkn8s8S#Efo= z^|!}o{tozLT1|Z7UlaSMxZ(5FgK^Rilm(Khv|vko7i5X}36?lI))Ggklas69 zVxSe$=33+10BfA^v%)uXY;b;dHGCaV4e6oPadwt1PEE7L#SjO4G`kKy33kG#^P1yK zcx(J^Ra<Ti+?95-JJvGIWK0JnTs;vs^DcXy)=jK$w z=lme~e0CM~SM61i7E+Zy6!Vv8(?YCpX|5H%3$bS21{dbq;8I96Tne>C8jm-9o*mM| z?2r~#1K&~U^BwT@ygK+I#1UDG8sIO%&iE*}A+E1$jbGNa!S(fRas9ovxba>)TBY{5 zxxo`Rq9|oIDtY0?rjE#1t!!u9+}s5>w|2#i&D55z%y+}h?JrQ>af9~O4zA^n9=Nr$ z7jEt9gPXg&@$23JxV49(y|Q~4emOiI-)H_6dH=qKoBYhlq5e+&PW_AegZf|U-_)N} z9@RJC3MS7vp?yXL1qC4>AOQaU{+Kjr5++WZhzS!Wz}MFoW5Wxo&I+1!G$zZHn#$;`!98-<yjHIyy#~ zd!^|5sm6LSF)_!K%8;V#rWzZU(N_%@(#Q5Ewg{KRHI95 zY?=LIo2D9@#Ky*zb^O>SmHu~IE44l?Dgh-;K81z)WLJ`;4wqn z_ZrZ%LmzL?wy3kD_lL%jZ@l`n*YIJJ=8o?=KVm^dc=tK8XTNSrUK1xwofb5!|4WPJ z4;&O=5uecStt8`&$o&U)@7lX>*XEsj-g|fBj_upFZrx%^n^vq{{r0M5OP8-%`Odni z4ek1_pUw~WS3(xf3w~KkBmDdVRSL~dfr0)bOf7sI@n%@?lm1=c0pd4Z&T02Hm@RH2 z)we;5{I7(S*0d0%twR;wLsA|##n-X4buN70s`TsBg@MbpxknH6!QPjfV-K~P+VA6v z_lLE?{$Xwi?eB?&gE}IlpC>|?5A<%2&;edpIl33d4IhkA?7Qcs#@NdnYWsbf({dao zjuAS*69M!eGt37G)4CyX#*2ub-V>ij1>vuo!mzs+z)KgL@b7{zHqOE48v-$!zJ3#Y zv6uJbc6$T6dQ*KU=65px!K_Y5n$a2Cr*_9zn`Ys&O+gqt+y{pT0q+l>1_JwOKM87w zj|1D|zXCjwI@=4Ewok|DRTFSw+Z#B)bq3CDnTav%mol33yacQq;D9qB?)YqOTV(8< zhO{02IO`82u>Hs|UYpK$#ksIn_%f8&v3sW=YtK}ip9y^Z1~r3H`B~I#;2iDQ=@jeE zsP;Kl_%^%|E=9QF`(^IPTIr6TH*`S`ui5^ww+}9?dJfr}dg8{OA;>xEhiiu?LYUzwb+T)8Ci=PAZtkjWKvm68X{|HBivlm3|Y&X;^sP6+GhB5eJk92w>5I2 z+$j(Ix}hC1827D>9dK(?2jp()h@8zG@!QT$$l2N%x3+e|?QJ|JOre?J8PhnJ%Ni~CLrzWB&44|iS%zyB8@if zn`DaR3m@|O^QyPhwX#dzrgIKY+OQIBHLeiIw|EP z&VT0+jvL~&)rdRJe}-vnAIJ6*Q-ZDH1N-*w-gRv2&ZLw99b3D3xO=#{xw*T!wQ+Oz@bGBcd0?|n&$#sN_2S8-lrFX#RqEa{~iIg60Iwp0)kazxeJo zgX#N&>G3k(9Zpk`k46?8yGp_NR9<~gx%0b2>EBc6h6N*s;*a0{2Wy6O#7ZA8q(u55 zXmAg#9`ZC+QBk9x#nSQpa4CKpR!sCp#>stnXRBl-)qQFW^fsryy=(Z?FI2AS<5;lV$HB*W zpm$$$hhFu3THa~z+qYL;AE$u>2QZl)2G;Ru)3f^vUAny3rOUHDp6~jct50i}CXE|6 zZPK7&qvp+?vT*b1+^M5y`wmZgdAPT0`%H^xiXL6DvWOu*60xx;u6V#Q2{0r8adCy( zEn;IuV&g28p4jI>W#CW53OF&!CsAr~RottogHM>&s@S>DKq|7h|3SD9 zqF9XiYwfgmNUJRFhY%(1o6xLY)@?;QKJMM%9Zv1};>0~2!r#}0zp0zW`xNH9UeDj( zg}=XRQtjm}{_d~Eq+;bB6m$ICmr^L!lH$^jp`^CQQOEr>=J>f^rrg)^KRssd^D)QI zeLuo|80KTp^Sb>{=X%)v)pLRSmCW&T|B@EJinpT1Tyzb%m&zPJ_g4w`z?hFg`Rd1_ z>Wj7&9jm;{DmLy1Gsn+8Vp@!PtSTNouWWh8cdz+W{M_4Sj-PwjDs;R>k4LR3_uiS~ z=YBll{weJklr8FC(aI`*?jJPA&pn00ytW2@1pNNmFr)z)}MRaMZIsT^P*Jr zd{v~ficiI=V%Fb3xlf-prc}}2|5bcSDrP-?@&@_Qn~c8Rs-)*Df-M*%`H0H+%lZ72 zvi{EGQOr#h;dxS84CWx2AwMJBn{b$~fyU%&3N}@!=X}9qDHtRuG5tUm68j-~fkG1sqOUyGmYlwPgb z2OYaS`ssnHnDzL{f$7y1HvU2ZvOsRl96y=1qRkb)O#V)fzZuy)A>;K#iJYK%{YIx)`7mahDM1B1t%cm9kaZNYkD4X_DC9qd+$8->B5TQhB} zPLpFP(T5^y$$V8IA1dTRh5V#84>?gGBg(O=3b|S#mnh^Cg)FI%vsB;THmdl^aSGW> zA@3;U9fcgEkcSj)tKX)y|CMyJ9 zWMGAisgNZVGNwZIRLI7bES?uKuA0cIN->306SAtME58p}SdPK5N}H!(y?QQ$SPR)# zEw=cH;9p8myVEOE~ZJrY}3iIg?0rP&%LTBp=}8h@I%TXv<9-xUO`%}-uWt5a*E=2Z6^)Nip$4?6}mrb=W3r9pMm{N(?%I<=0f{ZX!iK0oKQ1d^EdG#^%`N>O4Lp#&)lc_BC`N?cbBh&ou z$Ha>#mE4>Z3XbJ2L!+Nt++W%XmzCnEDKwe#1XEVN#&9kX7z*Ba>aDt~p(O7d58 ztNMbLMIj4qo}V1Gs?t)?V|bWl{j*<9L>}8bKN)V*HyMT)&Xn7jpKpqbGz6zmVk@{(S%;moMb= zg`B=PIy$QPUCF}>xq2agFXZoq+`W*w*DN`FAuBIr%G&-D!IW`F9}` zFJ#_@jJ%MQmz-@~sV+i3UdYL7B1xFE+kg*rC_sn}}eaYVo*?J*YFZ>$;!oOJ{ z{QCgB-)1FF4i?imzkPZz{4Rvr{h7I>sgUu{%LsSK%b0JUml0-1RnN;GSP!(-+jpO%JopO`B((dnpK-(&yRaUJ6F; zchnE_k$Wv1f4{oG;*T$8Vx5|ss!Wf01@yO_$nuNBLZ4Gvb)Vu6x9f7RD3t3{RPFna z@~=**zWfUs8kYPPZCSL4e)B1xT|TXnSM+U>y|{O?8%m4vtzIr_BVKg5vCP}`*3dR} z&a!{N#n>%>kU18z!$Q_q$meQ#RW3=oZ=knFmg=8&V&`qOUg~p1N&lWwnpHmPb9YW3 zw+z)kIP(xwOMAJX5{|A*v__uZdtvV;w2rOkgeCCc1i z#a5Q%Amc3IgIa3+fBIm(x&OWTs_~Un|HxNN{coH$#m{POUDev^Dy>e{FMhe1Y5iiu zZ_N=+wHsYe$U#6ePmShD&p^B>2uySylbs@uYIPy&-w#c zpc-6UYC)x+ErDgUwQ8BlZ7hIURRB*7exZ#T}AXG2* z=^weGTI5~Inq#r?3QZRh5>Vvy7AqDy*^i;1p6BY7;LQSXZ{;g>M z?fm5AM!1uJ~14CP5-;mbWJGeF0 z_iurN!(6GBI54yo4h(CB{j~e(6Em$hj*V=Fqpvo{5$e#07L+U2`wvFkn8s8S#Efo= z^|!}o{tozLT1|Z7UlaSMxZ(5FgK^Rilm(Khv|vko7i5X}36?lI))Ggklas69 zVxSe$=33+10BfA^v%)uXY;b;dHGCaV4e6oPadwt1PEE7L#SjO4G`kKy33kG#^P1yK zcx(J^Ra<Ti+?95-JJvGIWK0JnTs;vs^DcXy)=jK$w z=lme~e0CM~SM61i7E+Zy6!Vv8(?YCpX|5H%3$bS21{dbq;8I96Tne>C8jm-9o*mM| z?2r~#1K&~U^BwT@ygK+I#1UDG8sIO%&iE*}A+E1$jbGNa!S(fRas9ovxba>)TBY{5 zxxo`Rq9|oIDtY0?rjE#1t!!u9+}s5>w|2#i&D55z%y+}h?JrQ>af9~O4zA^n9=Nr$ z7jEt9gPXg&@$23JxV49(y|Q~4emOiI-)H_6dH=qKoBYhlq5e+&PW_AegZf|U-_)N} z9@RJC3MS7vp?yXL1qC4>AOQaU{+Kjr5++WZhzS!Wz}MFoW5Wxo&I+1!G$zZHn#$;`!98-<yjHIyy#~ zd!^|5sm6LSF)_!K%8;V#rWzZU(N_%@(#Q5Ewg{KRHI95 zY?=LIo2D9@#Ky*zb^O>SmHu~IE44l?Dgh-;K81z)WLJ`;4wqn z_ZrZ%LmzL?wy3kD_lL%jZ@l`n*YIJJ=8o?=KVm^dc=tK8XTNSrUK1xwofb5!|4WPJ z4;&O=5uecStt8`&$o&U)@7lX>*XEsj-g|fBj_upFZrx%^n^vq{{r0M5OP8-%`Odni z4ek1_pUw~WS3(xf3w~KkBmDdVRSL~dfr0)bOf7sI@n%@?lm1=c0pd4Z&T02Hm@RH2 z)we;5{I7(S*0d0%twR;wLsA|##n-X4buN70s`TsBg@MbpxknH6!QPjfV-K~P+VA6v z_lLE?{$Xwi?eB?&gE}IlpC>|?5A<%2&;edpIl33d4IhkA?7Qcs#@NdnYWsbf({dao zjuAS*69M!eGt37G)4CyX#*2ub-V>ij1>vuo!mzs+z)KgL@b7{zHqOE48v-$!zJ3#Y zv6uJbc6$T6dQ*KU=65px!K_Y5n$a2Cr*_9zn`Ys&O+gqt+y{pT0q+l>1_JwOKM87w zj|1D|zXCjwI@=4Ewok|DRTFSw+Z#B)bq3CDnTav%mol33yacQq;D9qB?)YqOTV(8< zhO{02IO`82u>Hs|UYpK$#ksIn_%f8&v3sW=YtK}ip9y^Z1~r3H`B~I#;2iDQ=@jeE zsP;Kl_%^%|E=9QF`(^IPTIr6TH*`S`ui5^ww+}9?dJfr}dg8{OA;>xEhiiu?LYUzwb+T)8Ci=PAZtkjWKvm68X{|HBivlm3|Y&X;^sP6+GhB5eJk92w>5I2 z+$j(Ix}hC1827D>9dK(?2jp()h@8zG@!QT$$l2N%x3+e|?QJ|JOre?J8PhnJ%Ni~CLrzWB&44|iS%zyB8@if zn`DaR3m@|O^QyPhwX#dzrgIKY+OQIBHLeiIw|EP z&VT0+jvL~&)rdRJe}-vnAIJ6*Q-ZDH1N-*w-gRv2&ZLw99b3D3xO=#{xw*T!wQ+Oz@bGBcd0?|n&$#sN_2S8-lrFX#RqEa{~iIg60Iwp0)kazxeJo zgX#N&>G3k(9Zpk`k46?8yGp_NR9<~gx%0b2>EBc6h6N*s;*a0{2Wy6O#7ZA8q(u55 zXmAg#9`ZC+QBk9x#nSQpa4CKpR!sCp#>stnXRBl-)qQFW^fsryy=(Z?FI2AS<5;lV$HB*W zpm$$$hhFu3THa~z+qYL;AE$u>2QZl)2G;Ru)3f^vUAny3rOUHDp6~jct50i}CXE|6 zZPK7&qvp+?vT*b1+^M5y`wmZgdAPT0`%H^xiXL6DvWOu*60xx;u6V#Q2{0r8adCy( zEn;IuV&g28p4jI>W#CW53OF&!CsAr~RottogHM>&s@S>DKq|7h|3SD9 zqF9XiYwfgmNUJRFhY%(1o6xLY)@?;QKJMM%9Zv1};>0~2!r#}0zp0zW`xNH9UeDj( zg}=XRQtjm}{_d~Eq+;bB6m$ICmr^L!lH$^jp`^CQQOEr>=J>f^rrg)^KRssd^D)QI zeLuo|80KTp^Sb>{=X%)v)pLRSmCW&T|B@EJinpT1Tyzb%m&zPJ_g4w`z?hFg`Rd1_ z>Wj7&9jm;{DmLy1Gsn+8Vp@!PtSTNouWWh8cdz+W{M_4Sj-PwjDs;R>k4LR3_uiS~ z=YBll{weJklr8FC(aI`*?jJPA&pn00ytW2@1pNNmFr)z)}MRaMZIsT^P*Jr zd{v~ficiI=V%Fb3xlf-prc}}2|5bcSDrP-?@&@_Qn~c8Rs-)*Df-M*%`H0H+%lZ72 zvi{EGQOr#h;dxS84CWx2AwMJBn{b$~fyU%&3N}@!=X}9qDHtRuG5tUm68j-~fkG1sqOUyGmYlwPgb z2OYaS`ssnHnDzL{f$7y1HvU2ZvOsRl96y=1qRkb)O#V)fzZuy)A>;K#iJYK%{YIx)`7mahDM1B1t%cm9kaZNYkD4X_DC9qd+$8->B5TQhB} zPLpFP(T5^y$$V8IA1dTRh5V#84>?gGBg(O=3b|S#mnh^Cg)FI%vsB;THmdl^aSGW> zA@3;U9fcgEkcSj)tKX)y|CMyJ9 zWMGAisgNZVGNwZIRLI7bES?uKuA0cIN->306SAtME58p}SdPK5N}H!(y?QQ$SPR)# zEw=cH;9p8myVEOE~ZJrY}3iIg?0rP&%LTBp=}8h@I%TXv<9-xUO`%}-uWt5a*E=2Z6^)Nip$4?6}mrb=W3r9pMm{N(?%I<=0f{ZX!iK0oKQ1d^EdG#^%`N>O4Lp#&)lc_BC`N?cbBh&ou z$Ha>#mE4>Z3XbJ2L!+Nt++W%XmzCnEDKwe#1XEVN#&9kX7z*Ba>aDt~p(O7d58 ztNMbLMIj4qo}V1Gs?t)?V|bWl{j*<9L>}8bKN)V*HyMT)&Xn7jpKpqbGz6zmVk@{(S%;moMb= zg`B=PIy$QPUCF}>xq2agFXZoq+`W*w*DN`FAuBIr%G&-D!IW`F9}` zFJ#_@jJ%MQmz-@~sV+i3UdYL7B1xFE+kg*rC_sn}}eaYVo*?J*YFZ>$;!oOJ{ z{QCgB-)1FF4i?imzkPZz{4Rvr{h7I>sgUu{%LsSK%b0JUml0-1RnN;GSP!(-+jpO%JopO`B((dnpK-(&yRaUJ6F; zchnE_k$Wv1f4{oG;*T$8Vx5|ss!Wf01@yO_$nuNBLZ4Gvb)Vu6x9f7RD3t3{RPFna z@~=**zWfUs8kYPPZCSL4e)B1xT|TXnSM+U>y|{O?8%m4vtzIr_BVKg5vCP}`*3dR} z&a!{N#n>%>kU18z!$Q_q$meQ#RW3=oZ=knFmg=8&V&`qOUg~p1N&lWwnpHmPb9YW3 zw+z)kIP(xwOMAJX5{|A*v__uZdtvV;w2rOkgeCCc1i z#a5Q%Amc3IgIa3+fBIm(x&OWTs_~Un|HxNN{coH$#m{POUDev^Dy>e{FMhe1Y5iiu zZ(context)-> + OnClientConnected(client_info); + } + + static void CallOnClientDumpRequested( + void* context, + const google_breakpad::ClientInfo* client_info, + const std::wstring* file_path) { + static_cast(context)-> + OnClientDumpRequested(client_info, file_path); + } + + static void CallOnClientExited( + void* context, const google_breakpad::ClientInfo* client_info) { + static_cast(context)-> + OnClientExited(client_info); + } + + static void CallOnClientUploadRequested(void* context, const DWORD crash_id) { + static_cast(context)-> + OnClientUploadRequested(crash_id); + } + + DWORD thread_id_; + EXCEPTION_POINTERS* exception_pointers_; + MDRawAssertionInfo assert_info_; + + google_breakpad::CrashGenerationServer crash_generation_server_; +}; + +TEST_F(CrashGenerationServerTest, PingServerTest) { + DoTestFault(CLOSE_AFTER_CONNECT); +} + +TEST_F(CrashGenerationServerTest, InvalidRegistration) { + DoTestFault(SEND_INVALID_REGISTRATION); +} + +TEST_F(CrashGenerationServerTest, TruncateRegistration) { + DoTestFault(TRUNCATE_REGISTRATION); +} + +TEST_F(CrashGenerationServerTest, CloseAfterRegistration) { + DoTestFault(CLOSE_AFTER_REGISTRATION); +} + +TEST_F(CrashGenerationServerTest, ResponseBufferTooSmall) { + DoTestFault(RESPONSE_BUFFER_TOO_SMALL); +} + +TEST_F(CrashGenerationServerTest, CloseAfterResponse) { + DoTestFault(CLOSE_AFTER_RESPONSE); +} + +// It turns out that, as long as you send one byte, the ACK is accepted and +// registration succeeds. +TEST_F(CrashGenerationServerTest, SendInvalidAck) { + EXPECT_CALL(mock_callbacks_, OnClientConnected(_)); + ASSERT_NO_FATAL_FAILURE(FaultyClient(SEND_INVALID_ACK)); + + // See DoTestFault for an explanation of this line + ASSERT_NO_FATAL_FAILURE(FaultyClient(CLOSE_AFTER_CONNECT)); + + EXPECT_CALL(mock_callbacks_, OnClientConnected(_)); + ASSERT_NO_FATAL_FAILURE(FaultyClient(NO_FAULT)); + + // See DoTestFault for an explanation of this line + ASSERT_NO_FATAL_FAILURE(FaultyClient(CLOSE_AFTER_CONNECT)); +} + +} // anonymous namespace diff --git a/shared/sentry/external/breakpad/src/client/windows/unittests/dump_analysis.cc b/shared/sentry/external/breakpad/src/client/windows/unittests/dump_analysis.cc new file mode 100644 index 000000000..0bb8f6c46 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/unittests/dump_analysis.cc @@ -0,0 +1,184 @@ +// 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 +#include +#include + +#include "breakpad_googletest_includes.h" +#include "client/windows/unittests/dump_analysis.h" // NOLINT + +DumpAnalysis::~DumpAnalysis() { + if (dump_file_view_ != NULL) { + EXPECT_TRUE(::UnmapViewOfFile(dump_file_view_)); + ::CloseHandle(dump_file_mapping_); + dump_file_mapping_ = NULL; + } + + if (dump_file_handle_ != NULL) { + ::CloseHandle(dump_file_handle_); + dump_file_handle_ = NULL; + } +} + +void DumpAnalysis::EnsureDumpMapped() { + if (dump_file_view_ == NULL) { + dump_file_handle_ = ::CreateFile(dump_file_.c_str(), + GENERIC_READ, + 0, + NULL, + OPEN_EXISTING, + 0, + NULL); + ASSERT_TRUE(dump_file_handle_ != NULL); + ASSERT_TRUE(dump_file_mapping_ == NULL); + + dump_file_mapping_ = ::CreateFileMapping(dump_file_handle_, + NULL, + PAGE_READONLY, + 0, + 0, + NULL); + ASSERT_TRUE(dump_file_mapping_ != NULL); + + dump_file_view_ = ::MapViewOfFile(dump_file_mapping_, + FILE_MAP_READ, + 0, + 0, + 0); + ASSERT_TRUE(dump_file_view_ != NULL); + } +} + +bool DumpAnalysis::HasTebs() const { + MINIDUMP_THREAD_LIST* thread_list = NULL; + size_t thread_list_size = GetStream(ThreadListStream, &thread_list); + + if (thread_list_size > 0 && thread_list != NULL) { + for (ULONG i = 0; i < thread_list->NumberOfThreads; ++i) { + if (!HasMemory(thread_list->Threads[i].Teb)) + return false; + } + + return true; + } + + // No thread list, no TEB info. + return false; +} + +bool DumpAnalysis::HasPeb() const { + MINIDUMP_THREAD_LIST* thread_list = NULL; + size_t thread_list_size = GetStream(ThreadListStream, &thread_list); + + if (thread_list_size > 0 && thread_list != NULL && + thread_list->NumberOfThreads > 0) { + FakeTEB* teb = NULL; + if (!HasMemory(thread_list->Threads[0].Teb, &teb)) + return false; + + return HasMemory(teb->peb); + } + + return false; +} + +bool DumpAnalysis::HasStream(ULONG stream_number) const { + void* stream = NULL; + size_t stream_size = GetStreamImpl(stream_number, &stream); + return stream_size > 0 && stream != NULL; +} + +size_t DumpAnalysis::GetStreamImpl(ULONG stream_number, void** stream) const { + MINIDUMP_DIRECTORY* directory = NULL; + ULONG memory_list_size = 0; + BOOL ret = ::MiniDumpReadDumpStream(dump_file_view_, + stream_number, + &directory, + stream, + &memory_list_size); + + return ret ? memory_list_size : 0; +} + +bool DumpAnalysis::HasMemoryImpl(const void* addr_in, size_t structuresize, + void** structure) const { + uintptr_t address = reinterpret_cast(addr_in); + MINIDUMP_MEMORY_LIST* memory_list = NULL; + size_t memory_list_size = GetStream(MemoryListStream, &memory_list); + if (memory_list_size > 0 && memory_list != NULL) { + for (ULONG i = 0; i < memory_list->NumberOfMemoryRanges; ++i) { + MINIDUMP_MEMORY_DESCRIPTOR& descr = memory_list->MemoryRanges[i]; + const uintptr_t range_start = + static_cast(descr.StartOfMemoryRange); + uintptr_t range_end = range_start + descr.Memory.DataSize; + + if (address >= range_start && + address + structuresize < range_end) { + // The start address falls in the range, and the end address is + // in bounds, return a pointer to the structure if requested. + if (structure != NULL) + *structure = RVA_TO_ADDR(dump_file_view_, descr.Memory.Rva); + + return true; + } + } + } + + // We didn't find the range in a MINIDUMP_MEMORY_LIST, so maybe this + // is a full dump using MINIDUMP_MEMORY64_LIST with all the memory at the + // end of the dump file. + MINIDUMP_MEMORY64_LIST* memory64_list = NULL; + memory_list_size = GetStream(Memory64ListStream, &memory64_list); + if (memory_list_size > 0 && memory64_list != NULL) { + // Keep track of where the current descriptor maps to. + RVA64 curr_rva = memory64_list->BaseRva; + for (ULONG i = 0; i < memory64_list->NumberOfMemoryRanges; ++i) { + MINIDUMP_MEMORY_DESCRIPTOR64& descr = memory64_list->MemoryRanges[i]; + uintptr_t range_start = + static_cast(descr.StartOfMemoryRange); + uintptr_t range_end = range_start + static_cast(descr.DataSize); + + if (address >= range_start && + address + structuresize < range_end) { + // The start address falls in the range, and the end address is + // in bounds, return a pointer to the structure if requested. + if (structure != NULL) + *structure = RVA_TO_ADDR(dump_file_view_, curr_rva); + + return true; + } + + // Advance the current RVA. + curr_rva += descr.DataSize; + } + } + + return false; +} diff --git a/shared/sentry/external/breakpad/src/client/windows/unittests/dump_analysis.h b/shared/sentry/external/breakpad/src/client/windows/unittests/dump_analysis.h new file mode 100644 index 000000000..6cef48d81 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/unittests/dump_analysis.h @@ -0,0 +1,102 @@ +// 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 CLIENT_WINDOWS_UNITTESTS_DUMP_ANALYSIS_H_ +#define CLIENT_WINDOWS_UNITTESTS_DUMP_ANALYSIS_H_ + +#include "client/windows/crash_generation/minidump_generator.h" + +// Convenience to get to the PEB pointer in a TEB. +struct FakeTEB { + char dummy[0x30]; + void* peb; +}; + +class DumpAnalysis { + public: + explicit DumpAnalysis(const std::wstring& file_path) + : dump_file_(file_path), dump_file_view_(NULL), dump_file_mapping_(NULL), + dump_file_handle_(NULL) { + EnsureDumpMapped(); + } + ~DumpAnalysis(); + + bool HasStream(ULONG stream_number) const; + + // This is template to keep type safety in the front, but we end up casting + // to void** inside the implementation to pass the pointer to Win32. So + // casting here is considered safe. + template + size_t GetStream(ULONG stream_number, StreamType** stream) const { + return GetStreamImpl(stream_number, reinterpret_cast(stream)); + } + + bool HasTebs() const; + bool HasPeb() const; + bool HasMemory(ULONG64 address) const { + return HasMemory(address, NULL); + } + + bool HasMemory(const void* address) const { + return HasMemory(address, NULL); + } + + template + bool HasMemory(ULONG64 address, StructureType** structure = NULL) const { + // We can't cope with 64 bit addresses for now. + if (address > 0xFFFFFFFFUL) + return false; + + return HasMemory(reinterpret_cast(address), structure); + } + + template + bool HasMemory(const void* addr_in, StructureType** structure = NULL) const { + return HasMemoryImpl(addr_in, sizeof(StructureType), + reinterpret_cast(structure)); + } + + protected: + void EnsureDumpMapped(); + + HANDLE dump_file_mapping_; + HANDLE dump_file_handle_; + void* dump_file_view_; + std::wstring dump_file_; + + private: + // This is the implementation of GetStream<>. + size_t GetStreamImpl(ULONG stream_number, void** stream) const; + + // This is the implementation of HasMemory<>. + bool HasMemoryImpl(const void* addr_in, size_t pointersize, + void** structure) const; +}; + +#endif // CLIENT_WINDOWS_UNITTESTS_DUMP_ANALYSIS_H_ diff --git a/shared/sentry/external/breakpad/src/client/windows/unittests/exception_handler_death_test.cc b/shared/sentry/external/breakpad/src/client/windows/unittests/exception_handler_death_test.cc new file mode 100644 index 000000000..04034e8d4 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/unittests/exception_handler_death_test.cc @@ -0,0 +1,587 @@ +// Copyright 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 +#include +#include +#include +#include + +#include + +#include "breakpad_googletest_includes.h" +#include "client/windows/crash_generation/crash_generation_server.h" +#include "client/windows/handler/exception_handler.h" +#include "client/windows/unittests/exception_handler_test.h" +#include "common/windows/string_utils-inl.h" +#include "google_breakpad/processor/minidump.h" + +namespace { + +using std::wstring; +using namespace google_breakpad; + +const wchar_t kPipeName[] = L"\\\\.\\pipe\\BreakpadCrashTest\\TestCaseServer"; +const char kSuccessIndicator[] = "success"; +const char kFailureIndicator[] = "failure"; + +// Utility function to test for a path's existence. +BOOL DoesPathExist(const TCHAR* path_name); + +enum OutOfProcGuarantee { + OUT_OF_PROC_GUARANTEED, + OUT_OF_PROC_BEST_EFFORT, +}; + +class ExceptionHandlerDeathTest : public ::testing::Test { + protected: + // Member variable for each test that they can use + // for temporary storage. + TCHAR temp_path_[MAX_PATH]; + // Actually constructs a temp path name. + virtual void SetUp(); + // A helper method that tests can use to crash. + void DoCrashAccessViolation(const OutOfProcGuarantee out_of_proc_guarantee); + void DoCrashPureVirtualCall(); +}; + +void ExceptionHandlerDeathTest::SetUp() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + TCHAR temp_path[MAX_PATH] = { '\0' }; + TCHAR test_name_wide[MAX_PATH] = { '\0' }; + // We want the temporary directory to be what the OS returns + // to us, + the test case name. + GetTempPath(MAX_PATH, temp_path); + // The test case name is exposed as a c-style string, + // convert it to a wchar_t string. + int dwRet = MultiByteToWideChar(CP_ACP, 0, test_info->name(), + static_cast(strlen(test_info->name())), + test_name_wide, + MAX_PATH); + if (!dwRet) { + assert(false); + } + StringCchPrintfW(temp_path_, MAX_PATH, L"%s%s", temp_path, test_name_wide); + CreateDirectory(temp_path_, NULL); +} + +BOOL DoesPathExist(const TCHAR* path_name) { + DWORD flags = GetFileAttributes(path_name); + if (flags == INVALID_FILE_ATTRIBUTES) { + return FALSE; + } + return TRUE; +} + +bool MinidumpWrittenCallback(const wchar_t* dump_path, + const wchar_t* minidump_id, + void* context, + EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion, + bool succeeded) { + if (succeeded && DoesPathExist(dump_path)) { + fprintf(stderr, kSuccessIndicator); + } else { + fprintf(stderr, kFailureIndicator); + } + // If we don't flush, the output doesn't get sent before + // this process dies. + fflush(stderr); + return succeeded; +} + +TEST_F(ExceptionHandlerDeathTest, InProcTest) { + // For the in-proc test, we just need to instantiate an exception + // handler in in-proc mode, and crash. Since the entire test is + // reexecuted in the child process, we don't have to worry about + // the semantics of the exception handler being inherited/not + // inherited across CreateProcess(). + ASSERT_TRUE(DoesPathExist(temp_path_)); + scoped_ptr exc( + new google_breakpad::ExceptionHandler( + temp_path_, + NULL, + &MinidumpWrittenCallback, + NULL, + google_breakpad::ExceptionHandler::HANDLER_ALL)); + + // Disable GTest SEH handler + testing::DisableExceptionHandlerInScope disable_exception_handler; + + int* i = NULL; + ASSERT_DEATH((*i)++, kSuccessIndicator); +} + +static bool gDumpCallbackCalled = false; + +void clientDumpCallback(void* dump_context, + const google_breakpad::ClientInfo* client_info, + const std::wstring* dump_path) { + gDumpCallbackCalled = true; +} + +void ExceptionHandlerDeathTest::DoCrashAccessViolation( + const OutOfProcGuarantee out_of_proc_guarantee) { + scoped_ptr exc; + + if (out_of_proc_guarantee == OUT_OF_PROC_GUARANTEED) { + google_breakpad::CrashGenerationClient* client = + new google_breakpad::CrashGenerationClient(kPipeName, + MiniDumpNormal, + NULL); // custom_info + ASSERT_TRUE(client->Register()); + exc.reset(new google_breakpad::ExceptionHandler( + temp_path_, + NULL, // filter + NULL, // callback + NULL, // callback_context + google_breakpad::ExceptionHandler::HANDLER_ALL, + client)); + } else { + ASSERT_TRUE(out_of_proc_guarantee == OUT_OF_PROC_BEST_EFFORT); + exc.reset(new google_breakpad::ExceptionHandler( + temp_path_, + NULL, // filter + NULL, // callback + NULL, // callback_context + google_breakpad::ExceptionHandler::HANDLER_ALL, + MiniDumpNormal, + kPipeName, + NULL)); // custom_info + } + + // Disable GTest SEH handler + testing::DisableExceptionHandlerInScope disable_exception_handler; + + // Although this is executing in the child process of the death test, + // if it's not true we'll still get an error rather than the crash + // being expected. + ASSERT_TRUE(exc->IsOutOfProcess()); + int* i = NULL; + printf("%d\n", (*i)++); +} + +TEST_F(ExceptionHandlerDeathTest, OutOfProcTest) { + // We can take advantage of a detail of google test here to save some + // complexity in testing: when you do a death test, it actually forks. + // So we can make the main test harness the crash generation server, + // and call ASSERT_DEATH on a NULL dereference, it to expecting test + // the out of process scenario, since it's happening in a different + // process! This is different from the above because, above, we pass + // a NULL pipe name, and we also don't start a crash generation server. + + ASSERT_TRUE(DoesPathExist(temp_path_)); + std::wstring dump_path(temp_path_); + google_breakpad::CrashGenerationServer server( + kPipeName, NULL, NULL, NULL, &clientDumpCallback, NULL, NULL, NULL, NULL, + NULL, true, &dump_path); + + // This HAS to be EXPECT_, because when this test case is executed in the + // child process, the server registration will fail due to the named pipe + // being the same. + EXPECT_TRUE(server.Start()); + gDumpCallbackCalled = false; + ASSERT_DEATH(this->DoCrashAccessViolation(OUT_OF_PROC_BEST_EFFORT), ""); + EXPECT_TRUE(gDumpCallbackCalled); +} + +TEST_F(ExceptionHandlerDeathTest, OutOfProcGuaranteedTest) { + // This is similar to the previous test (OutOfProcTest). The only difference + // is that in this test, the crash generation client is created and registered + // with the crash generation server outside of the ExceptionHandler + // constructor which allows breakpad users to opt out of the default + // in-process dump generation when the registration with the crash generation + // server fails. + + ASSERT_TRUE(DoesPathExist(temp_path_)); + std::wstring dump_path(temp_path_); + google_breakpad::CrashGenerationServer server( + kPipeName, NULL, NULL, NULL, &clientDumpCallback, NULL, NULL, NULL, NULL, + NULL, true, &dump_path); + + // This HAS to be EXPECT_, because when this test case is executed in the + // child process, the server registration will fail due to the named pipe + // being the same. + EXPECT_TRUE(server.Start()); + gDumpCallbackCalled = false; + ASSERT_DEATH(this->DoCrashAccessViolation(OUT_OF_PROC_GUARANTEED), ""); + EXPECT_TRUE(gDumpCallbackCalled); +} + +TEST_F(ExceptionHandlerDeathTest, InvalidParameterTest) { + using google_breakpad::ExceptionHandler; + + ASSERT_TRUE(DoesPathExist(temp_path_)); + ExceptionHandler handler(temp_path_, NULL, NULL, NULL, + ExceptionHandler::HANDLER_INVALID_PARAMETER); + + // Disable the message box for assertions + _CrtSetReportMode(_CRT_ASSERT, 0); + + // Call with a bad argument. The invalid parameter will be swallowed + // and a dump will be generated, the process will exit(0). + ASSERT_EXIT(printf(NULL), ::testing::ExitedWithCode(0), ""); +} + + +struct PureVirtualCallBase { + PureVirtualCallBase() { + // We have to reinterpret so the linker doesn't get confused because the + // method isn't defined. + reinterpret_cast(this)->PureFunction(); + } + virtual ~PureVirtualCallBase() {} + virtual void PureFunction() const = 0; +}; +struct PureVirtualCall : public PureVirtualCallBase { + PureVirtualCall() { PureFunction(); } + virtual void PureFunction() const {} +}; + +void ExceptionHandlerDeathTest::DoCrashPureVirtualCall() { + PureVirtualCall instance; +} + +TEST_F(ExceptionHandlerDeathTest, PureVirtualCallTest) { + using google_breakpad::ExceptionHandler; + + ASSERT_TRUE(DoesPathExist(temp_path_)); + ExceptionHandler handler(temp_path_, NULL, NULL, NULL, + ExceptionHandler::HANDLER_PURECALL); + + // Disable the message box for assertions + _CrtSetReportMode(_CRT_ASSERT, 0); + + // Calls a pure virtual function. + EXPECT_EXIT(DoCrashPureVirtualCall(), ::testing::ExitedWithCode(0), ""); +} + +wstring find_minidump_in_directory(const wstring& directory) { + wstring search_path = directory + L"\\*"; + WIN32_FIND_DATA find_data; + HANDLE find_handle = FindFirstFileW(search_path.c_str(), &find_data); + if (find_handle == INVALID_HANDLE_VALUE) + return wstring(); + + wstring filename; + do { + const wchar_t extension[] = L".dmp"; + const size_t extension_length = sizeof(extension) / sizeof(extension[0]) - 1; + const size_t filename_length = wcslen(find_data.cFileName); + if (filename_length > extension_length && + wcsncmp(extension, + find_data.cFileName + filename_length - extension_length, + extension_length) == 0) { + filename = directory + L"\\" + find_data.cFileName; + break; + } + } while (FindNextFile(find_handle, &find_data)); + FindClose(find_handle); + return filename; +} + +#ifndef ADDRESS_SANITIZER + +TEST_F(ExceptionHandlerDeathTest, InstructionPointerMemory) { + ASSERT_TRUE(DoesPathExist(temp_path_)); + scoped_ptr exc( + new google_breakpad::ExceptionHandler( + temp_path_, + NULL, + NULL, + NULL, + google_breakpad::ExceptionHandler::HANDLER_ALL)); + + // Disable GTest SEH handler + testing::DisableExceptionHandlerInScope disable_exception_handler; + + // Get some executable memory. + const uint32_t kMemorySize = 256; // bytes + const int kOffset = kMemorySize / 2; + // This crashes with SIGILL on x86/x86-64/arm. + const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff }; + char* memory = reinterpret_cast(VirtualAlloc(NULL, + kMemorySize, + MEM_COMMIT | MEM_RESERVE, + PAGE_EXECUTE_READWRITE)); + ASSERT_TRUE(memory); + + // Write some instructions that will crash. Put them + // in the middle of the block of memory, because the + // minidump should contain 128 bytes on either side of the + // instruction pointer. + memcpy(memory + kOffset, instructions, sizeof(instructions)); + + // Now execute the instructions, which should crash. + typedef void (*void_function)(void); + void_function memory_function = + reinterpret_cast(memory + kOffset); + ASSERT_DEATH(memory_function(), ""); + + // free the memory. + VirtualFree(memory, 0, MEM_RELEASE); + + // Verify that the resulting minidump contains the memory around the IP + wstring minidump_filename_wide = find_minidump_in_directory(temp_path_); + ASSERT_FALSE(minidump_filename_wide.empty()); + string minidump_filename; + ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(minidump_filename_wide, + &minidump_filename)); + + // Read the minidump. Locate the exception record and the + // memory list, and then ensure that there is a memory region + // in the memory list that covers at least 128 bytes on either + // side of the instruction pointer from the exception record. + { + Minidump minidump(minidump_filename); + ASSERT_TRUE(minidump.Read()); + + MinidumpException* exception = minidump.GetException(); + MinidumpMemoryList* memory_list = minidump.GetMemoryList(); + ASSERT_TRUE(exception); + ASSERT_TRUE(memory_list); + ASSERT_LT((unsigned)0, memory_list->region_count()); + + MinidumpContext* context = exception->GetContext(); + ASSERT_TRUE(context); + + uint64_t instruction_pointer; + ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer)); + + MinidumpMemoryRegion* region = + memory_list->GetMemoryRegionForAddress(instruction_pointer); + ASSERT_TRUE(region); + + EXPECT_LE(kMemorySize, region->GetSize()); + const uint8_t* bytes = region->GetMemory(); + ASSERT_TRUE(bytes); + + uint64_t ip_offset = instruction_pointer - region->GetBase(); + EXPECT_GE(region->GetSize() - kOffset, ip_offset); + EXPECT_LE(kOffset, ip_offset); + + uint8_t prefix_bytes[kOffset]; + uint8_t suffix_bytes[kMemorySize - kOffset - sizeof(instructions)]; + memset(prefix_bytes, 0, sizeof(prefix_bytes)); + memset(suffix_bytes, 0, sizeof(suffix_bytes)); + EXPECT_EQ(0, memcmp(bytes + ip_offset - kOffset, prefix_bytes, + sizeof(prefix_bytes))); + EXPECT_EQ(0, memcmp(bytes + ip_offset, instructions, sizeof(instructions))); + EXPECT_EQ(0, memcmp(bytes + ip_offset + sizeof(instructions), suffix_bytes, + sizeof(suffix_bytes))); + } + + DeleteFileW(minidump_filename_wide.c_str()); +} + +TEST_F(ExceptionHandlerDeathTest, InstructionPointerMemoryMinBound) { + ASSERT_TRUE(DoesPathExist(temp_path_)); + scoped_ptr exc( + new google_breakpad::ExceptionHandler( + temp_path_, + NULL, + NULL, + NULL, + google_breakpad::ExceptionHandler::HANDLER_ALL)); + + // Disable GTest SEH handler + testing::DisableExceptionHandlerInScope disable_exception_handler; + + SYSTEM_INFO sSysInfo; // Useful information about the system + GetSystemInfo(&sSysInfo); // Initialize the structure. + + const uint32_t kMemorySize = 256; // bytes + const DWORD kPageSize = sSysInfo.dwPageSize; + const int kOffset = 0; + // This crashes with SIGILL on x86/x86-64/arm. + const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff }; + // Get some executable memory. Specifically, reserve two pages, + // but only commit the second. + char* all_memory = reinterpret_cast(VirtualAlloc(NULL, + kPageSize * 2, + MEM_RESERVE, + PAGE_NOACCESS)); + ASSERT_TRUE(all_memory); + char* memory = all_memory + kPageSize; + ASSERT_TRUE(VirtualAlloc(memory, kPageSize, + MEM_COMMIT, PAGE_EXECUTE_READWRITE)); + + // Write some instructions that will crash. Put them + // in the middle of the block of memory, because the + // minidump should contain 128 bytes on either side of the + // instruction pointer. + memcpy(memory + kOffset, instructions, sizeof(instructions)); + + // Now execute the instructions, which should crash. + typedef void (*void_function)(void); + void_function memory_function = + reinterpret_cast(memory + kOffset); + ASSERT_DEATH(memory_function(), ""); + + // free the memory. + VirtualFree(memory, 0, MEM_RELEASE); + + // Verify that the resulting minidump contains the memory around the IP + wstring minidump_filename_wide = find_minidump_in_directory(temp_path_); + ASSERT_FALSE(minidump_filename_wide.empty()); + string minidump_filename; + ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(minidump_filename_wide, + &minidump_filename)); + + // Read the minidump. Locate the exception record and the + // memory list, and then ensure that there is a memory region + // in the memory list that covers the instruction pointer from + // the exception record. + { + Minidump minidump(minidump_filename); + ASSERT_TRUE(minidump.Read()); + + MinidumpException* exception = minidump.GetException(); + MinidumpMemoryList* memory_list = minidump.GetMemoryList(); + ASSERT_TRUE(exception); + ASSERT_TRUE(memory_list); + ASSERT_LT((unsigned)0, memory_list->region_count()); + + MinidumpContext* context = exception->GetContext(); + ASSERT_TRUE(context); + + uint64_t instruction_pointer; + ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer)); + + MinidumpMemoryRegion* region = + memory_list->GetMemoryRegionForAddress(instruction_pointer); + ASSERT_TRUE(region); + + EXPECT_EQ(kMemorySize / 2, region->GetSize()); + const uint8_t* bytes = region->GetMemory(); + ASSERT_TRUE(bytes); + + uint8_t suffix_bytes[kMemorySize / 2 - sizeof(instructions)]; + memset(suffix_bytes, 0, sizeof(suffix_bytes)); + EXPECT_TRUE(memcmp(bytes + kOffset, + instructions, sizeof(instructions)) == 0); + EXPECT_TRUE(memcmp(bytes + kOffset + sizeof(instructions), + suffix_bytes, sizeof(suffix_bytes)) == 0); + } + + DeleteFileW(minidump_filename_wide.c_str()); +} + +TEST_F(ExceptionHandlerDeathTest, InstructionPointerMemoryMaxBound) { + ASSERT_TRUE(DoesPathExist(temp_path_)); + scoped_ptr exc( + new google_breakpad::ExceptionHandler( + temp_path_, + NULL, + NULL, + NULL, + google_breakpad::ExceptionHandler::HANDLER_ALL)); + + // Disable GTest SEH handler + testing::DisableExceptionHandlerInScope disable_exception_handler; + + SYSTEM_INFO sSysInfo; // Useful information about the system + GetSystemInfo(&sSysInfo); // Initialize the structure. + + const DWORD kPageSize = sSysInfo.dwPageSize; + // This crashes with SIGILL on x86/x86-64/arm. + const unsigned char instructions[] = { 0xff, 0xff, 0xff, 0xff }; + const int kOffset = kPageSize - sizeof(instructions); + // Get some executable memory. Specifically, reserve two pages, + // but only commit the first. + char* memory = reinterpret_cast(VirtualAlloc(NULL, + kPageSize * 2, + MEM_RESERVE, + PAGE_NOACCESS)); + ASSERT_TRUE(memory); + ASSERT_TRUE(VirtualAlloc(memory, kPageSize, + MEM_COMMIT, PAGE_EXECUTE_READWRITE)); + + // Write some instructions that will crash. + memcpy(memory + kOffset, instructions, sizeof(instructions)); + + // Now execute the instructions, which should crash. + typedef void (*void_function)(void); + void_function memory_function = + reinterpret_cast(memory + kOffset); + ASSERT_DEATH(memory_function(), ""); + + // free the memory. + VirtualFree(memory, 0, MEM_RELEASE); + + // Verify that the resulting minidump contains the memory around the IP + wstring minidump_filename_wide = find_minidump_in_directory(temp_path_); + ASSERT_FALSE(minidump_filename_wide.empty()); + string minidump_filename; + ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(minidump_filename_wide, + &minidump_filename)); + + // Read the minidump. Locate the exception record and the + // memory list, and then ensure that there is a memory region + // in the memory list that covers the instruction pointer from + // the exception record. + { + Minidump minidump(minidump_filename); + ASSERT_TRUE(minidump.Read()); + + MinidumpException* exception = minidump.GetException(); + MinidumpMemoryList* memory_list = minidump.GetMemoryList(); + ASSERT_TRUE(exception); + ASSERT_TRUE(memory_list); + ASSERT_LT((unsigned)0, memory_list->region_count()); + + MinidumpContext* context = exception->GetContext(); + ASSERT_TRUE(context); + + uint64_t instruction_pointer; + ASSERT_TRUE(context->GetInstructionPointer(&instruction_pointer)); + + MinidumpMemoryRegion* region = + memory_list->GetMemoryRegionForAddress(instruction_pointer); + ASSERT_TRUE(region); + + const size_t kPrefixSize = 128; // bytes + EXPECT_EQ(kPrefixSize + sizeof(instructions), region->GetSize()); + const uint8_t* bytes = region->GetMemory(); + ASSERT_TRUE(bytes); + + uint8_t prefix_bytes[kPrefixSize]; + memset(prefix_bytes, 0, sizeof(prefix_bytes)); + EXPECT_EQ(0, memcmp(bytes, prefix_bytes, sizeof(prefix_bytes))); + EXPECT_EQ(0, memcmp(bytes + kPrefixSize, + instructions, sizeof(instructions))); + } + + DeleteFileW(minidump_filename_wide.c_str()); +} + +#endif // !ADDRESS_SANITIZER + +} // namespace diff --git a/shared/sentry/external/breakpad/src/client/windows/unittests/exception_handler_nesting_test.cc b/shared/sentry/external/breakpad/src/client/windows/unittests/exception_handler_nesting_test.cc new file mode 100644 index 000000000..e24bd18b6 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/unittests/exception_handler_nesting_test.cc @@ -0,0 +1,327 @@ +// Copyright 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 + +#include + +#include "breakpad_googletest_includes.h" +#include "client/windows/handler/exception_handler.h" +#include "client/windows/unittests/exception_handler_test.h" + +namespace { + +const char kFoo[] = "foo"; +const char kBar[] = "bar"; + +const char kStartOfLine[] = "^"; +const char kEndOfLine[] = "$"; + +const char kFilterReturnsTrue[] = "filter_returns_true"; +const char kFilterReturnsFalse[] = "filter_returns_false"; + +const char kCallbackReturnsTrue[] = "callback_returns_true"; +const char kCallbackReturnsFalse[] = "callback_returns_false"; + +bool DoesPathExist(const wchar_t* path_name) { + DWORD flags = GetFileAttributes(path_name); + if (flags == INVALID_FILE_ATTRIBUTES) { + return false; + } + return true; +} + +// A callback function to run before Breakpad performs any substantial +// processing of an exception. A FilterCallback is called before writing +// a minidump. context is the parameter supplied by the user as +// callback_context when the handler was created. exinfo points to the +// exception record, if any; assertion points to assertion information, +// if any. +// +// If a FilterCallback returns true, Breakpad will continue processing, +// attempting to write a minidump. If a FilterCallback returns false, +// Breakpad will immediately report the exception as unhandled without +// writing a minidump, allowing another handler the opportunity to handle it. +template +bool CrashHandlerFilter(void* context, + EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion) { + if (filter_return_value) { + fprintf(stderr, kFilterReturnsTrue); + } else { + fprintf(stderr, kFilterReturnsFalse); + } + fflush(stderr); + + return filter_return_value; +} + +// A callback function to run after the minidump has been written. +// minidump_id is a unique id for the dump, so the minidump +// file is \.dmp. context is the parameter supplied +// by the user as callback_context when the handler was created. exinfo +// points to the exception record, or NULL if no exception occurred. +// succeeded indicates whether a minidump file was successfully written. +// assertion points to information about an assertion if the handler was +// invoked by an assertion. +// +// If an exception occurred and the callback returns true, Breakpad will treat +// the exception as fully-handled, suppressing any other handlers from being +// notified of the exception. If the callback returns false, Breakpad will +// treat the exception as unhandled, and allow another handler to handle it. +// If there are no other handlers, Breakpad will report the exception to the +// system as unhandled, allowing a debugger or native crash dialog the +// opportunity to handle the exception. Most callback implementations +// should normally return the value of |succeeded|, or when they wish to +// not report an exception of handled, false. Callbacks will rarely want to +// return true directly (unless |succeeded| is true). +// +// For out-of-process dump generation, dump path and minidump ID will always +// be NULL. In case of out-of-process dump generation, the dump path and +// minidump id are controlled by the server process and are not communicated +// back to the crashing process. +template +bool MinidumpWrittenCallback(const wchar_t* dump_path, + const wchar_t* minidump_id, + void* context, + EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion, + bool succeeded) { + bool rv = false; + if (callback_return_value && + succeeded && + DoesPathExist(dump_path)) { + rv = true; + fprintf(stderr, kCallbackReturnsTrue); + } else { + fprintf(stderr, kCallbackReturnsFalse); + } + fflush(stderr); + + return rv; +} + + +void DoCrash(const char* message) { + if (message) { + fprintf(stderr, "%s", message); + fflush(stderr); + } + int* i = NULL; + (*i)++; + + ASSERT_TRUE(false); +} + +void InstallExceptionHandlerAndCrash(bool install_filter, + bool filter_return_value, + bool install_callback, + bool callback_return_value) { + wchar_t temp_path[MAX_PATH] = { '\0' }; + GetTempPath(MAX_PATH, temp_path); + + ASSERT_TRUE(DoesPathExist(temp_path)); + google_breakpad::ExceptionHandler exc( + temp_path, + install_filter ? + (filter_return_value ? + &CrashHandlerFilter : + &CrashHandlerFilter) : + NULL, + install_callback ? + (callback_return_value ? + &MinidumpWrittenCallback : + &MinidumpWrittenCallback) : + NULL, + NULL, // callback_context + google_breakpad::ExceptionHandler::HANDLER_EXCEPTION); + + // Disable GTest SEH handler + testing::DisableExceptionHandlerInScope disable_exception_handler; + + DoCrash(NULL); +} + +TEST(AssertDeathSanity, Simple) { + ASSERT_DEATH(DoCrash(NULL), ""); +} + +TEST(AssertDeathSanity, Regex) { + ASSERT_DEATH(DoCrash(kFoo), + std::string(kStartOfLine) + + std::string(kFoo) + + std::string(kEndOfLine)); + + ASSERT_DEATH(DoCrash(kBar), + std::string(kStartOfLine) + + std::string(kBar) + + std::string(kEndOfLine)); +} + +TEST(ExceptionHandlerCallbacks, FilterTrue_No_Callback) { + ASSERT_DEATH( + InstallExceptionHandlerAndCrash(true, // install_filter + true, // filter_return_value + false, // install_callback + false), // callback_return_value + std::string(kStartOfLine) + + std::string(kFilterReturnsTrue) + + std::string(kEndOfLine)); +} + +TEST(ExceptionHandlerCallbacks, FilterTrue_Callback) { + ASSERT_DEATH( + InstallExceptionHandlerAndCrash(true, // install_filter + true, // filter_return_value + true, // install_callback + false), // callback_return_value + std::string(kStartOfLine) + + std::string(kFilterReturnsTrue) + + std::string(kCallbackReturnsFalse) + + std::string(kEndOfLine)); +} + +TEST(ExceptionHandlerCallbacks, FilterFalse_No_Callback) { + ASSERT_DEATH( + InstallExceptionHandlerAndCrash(true, // install_filter + false, // filter_return_value + false, // install_callback + false), // callback_return_value + std::string(kStartOfLine) + + std::string(kFilterReturnsFalse) + + std::string(kEndOfLine)); +} + +// Callback shouldn't be executed when filter returns false +TEST(ExceptionHandlerCallbacks, FilterFalse_Callback) { + ASSERT_DEATH( + InstallExceptionHandlerAndCrash(true, // install_filter + false, // filter_return_value + true, // install_callback + false), // callback_return_value + std::string(kStartOfLine) + + std::string(kFilterReturnsFalse) + + std::string(kEndOfLine)); +} + +TEST(ExceptionHandlerCallbacks, No_Filter_No_Callback) { + ASSERT_DEATH( + InstallExceptionHandlerAndCrash(false, // install_filter + true, // filter_return_value + false, // install_callback + false), // callback_return_value + std::string(kStartOfLine) + + std::string(kEndOfLine)); +} + +TEST(ExceptionHandlerCallbacks, No_Filter_Callback) { + ASSERT_DEATH( + InstallExceptionHandlerAndCrash(false, // install_filter + true, // filter_return_value + true, // install_callback + false), // callback_return_value + std::string(kStartOfLine) + + std::string(kCallbackReturnsFalse) + + std::string(kEndOfLine)); +} + + +TEST(ExceptionHandlerNesting, Skip_From_Inner_Filter) { + wchar_t temp_path[MAX_PATH] = { '\0' }; + GetTempPath(MAX_PATH, temp_path); + + ASSERT_TRUE(DoesPathExist(temp_path)); + google_breakpad::ExceptionHandler exc( + temp_path, + &CrashHandlerFilter, + &MinidumpWrittenCallback, + NULL, // callback_context + google_breakpad::ExceptionHandler::HANDLER_EXCEPTION); + + ASSERT_DEATH( + InstallExceptionHandlerAndCrash(true, // install_filter + false, // filter_return_value + true, // install_callback + true), // callback_return_value + std::string(kStartOfLine) + + std::string(kFilterReturnsFalse) + // inner filter + std::string(kFilterReturnsTrue) + // outer filter + std::string(kCallbackReturnsFalse) + // outer callback + std::string(kEndOfLine)); +} + +TEST(ExceptionHandlerNesting, Skip_From_Inner_Callback) { + wchar_t temp_path[MAX_PATH] = { '\0' }; + GetTempPath(MAX_PATH, temp_path); + + ASSERT_TRUE(DoesPathExist(temp_path)); + google_breakpad::ExceptionHandler exc( + temp_path, + &CrashHandlerFilter, + &MinidumpWrittenCallback, + NULL, // callback_context + google_breakpad::ExceptionHandler::HANDLER_EXCEPTION); + + ASSERT_DEATH( + InstallExceptionHandlerAndCrash(true, // install_filter + true, // filter_return_value + true, // install_callback + false), // callback_return_value + std::string(kStartOfLine) + + std::string(kFilterReturnsTrue) + // inner filter + std::string(kCallbackReturnsFalse) + // inner callback + std::string(kFilterReturnsTrue) + // outer filter + std::string(kCallbackReturnsFalse) + // outer callback + std::string(kEndOfLine)); +} + +TEST(ExceptionHandlerNesting, Handled_By_Inner_Handler) { + wchar_t temp_path[MAX_PATH] = { '\0' }; + GetTempPath(MAX_PATH, temp_path); + + ASSERT_TRUE(DoesPathExist(temp_path)); + google_breakpad::ExceptionHandler exc( + temp_path, + &CrashHandlerFilter, + &MinidumpWrittenCallback, + NULL, // callback_context + google_breakpad::ExceptionHandler::HANDLER_EXCEPTION); + + ASSERT_DEATH( + InstallExceptionHandlerAndCrash(true, // install_filter + true, // filter_return_value + true, // install_callback + true), // callback_return_value + std::string(kStartOfLine) + + std::string(kFilterReturnsTrue) + // inner filter + std::string(kCallbackReturnsTrue) + // inner callback + std::string(kEndOfLine)); +} + +} // namespace diff --git a/shared/sentry/external/breakpad/src/client/windows/unittests/exception_handler_test.cc b/shared/sentry/external/breakpad/src/client/windows/unittests/exception_handler_test.cc new file mode 100644 index 000000000..51196ca06 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/unittests/exception_handler_test.cc @@ -0,0 +1,503 @@ +// Copyright 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 "client/windows/unittests/exception_handler_test.h" + +#include +#include +#include +#include +#include + +#include + +#include "breakpad_googletest_includes.h" +#include "client/windows/crash_generation/crash_generation_server.h" +#include "client/windows/handler/exception_handler.h" +#include "client/windows/unittests/dump_analysis.h" // NOLINT +#include "common/windows/string_utils-inl.h" +#include "google_breakpad/processor/minidump.h" + +namespace testing { + +DisableExceptionHandlerInScope::DisableExceptionHandlerInScope() { + catch_exceptions_ = GTEST_FLAG(catch_exceptions); + GTEST_FLAG(catch_exceptions) = false; +} + +DisableExceptionHandlerInScope::~DisableExceptionHandlerInScope() { + GTEST_FLAG(catch_exceptions) = catch_exceptions_; +} + +} // namespace testing + +namespace { + +using std::wstring; +using namespace google_breakpad; + +const wchar_t kPipeName[] = L"\\\\.\\pipe\\BreakpadCrashTest\\TestCaseServer"; +const char kSuccessIndicator[] = "success"; +const char kFailureIndicator[] = "failure"; + +const MINIDUMP_TYPE kFullDumpType = static_cast( + MiniDumpWithFullMemory | // Full memory from process. + MiniDumpWithProcessThreadData | // Get PEB and TEB. + MiniDumpWithHandleData); // Get all handle information. + +class ExceptionHandlerTest : public ::testing::Test { + protected: + // Member variable for each test that they can use + // for temporary storage. + TCHAR temp_path_[MAX_PATH]; + + // Actually constructs a temp path name. + virtual void SetUp(); + + // Deletes temporary files. + virtual void TearDown(); + + void DoCrashInvalidParameter(); + void DoCrashPureVirtualCall(); + + // Utility function to test for a path's existence. + static BOOL DoesPathExist(const TCHAR* path_name); + + // Client callback. + static void ClientDumpCallback( + void* dump_context, + const google_breakpad::ClientInfo* client_info, + const std::wstring* dump_path); + + static bool DumpCallback(const wchar_t* dump_path, + const wchar_t* minidump_id, + void* context, + EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion, + bool succeeded); + + static std::wstring dump_file; + static std::wstring full_dump_file; +}; + +std::wstring ExceptionHandlerTest::dump_file; +std::wstring ExceptionHandlerTest::full_dump_file; + +void ExceptionHandlerTest::SetUp() { + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + TCHAR temp_path[MAX_PATH] = { '\0' }; + TCHAR test_name_wide[MAX_PATH] = { '\0' }; + // We want the temporary directory to be what the OS returns + // to us, + the test case name. + GetTempPath(MAX_PATH, temp_path); + // THe test case name is exposed to use as a c-style string, + // But we might be working in UNICODE here on Windows. + int dwRet = MultiByteToWideChar(CP_ACP, 0, test_info->name(), + static_cast(strlen(test_info->name())), + test_name_wide, + MAX_PATH); + if (!dwRet) { + assert(false); + } + StringCchPrintfW(temp_path_, MAX_PATH, L"%s%s", temp_path, test_name_wide); + CreateDirectory(temp_path_, NULL); +} + +void ExceptionHandlerTest::TearDown() { + if (!dump_file.empty()) { + ::DeleteFile(dump_file.c_str()); + dump_file = L""; + } + if (!full_dump_file.empty()) { + ::DeleteFile(full_dump_file.c_str()); + full_dump_file = L""; + } +} + +BOOL ExceptionHandlerTest::DoesPathExist(const TCHAR* path_name) { + DWORD flags = GetFileAttributes(path_name); + if (flags == INVALID_FILE_ATTRIBUTES) { + return FALSE; + } + return TRUE; +} + +// static +void ExceptionHandlerTest::ClientDumpCallback( + void* dump_context, + const google_breakpad::ClientInfo* client_info, + const wstring* dump_path) { + dump_file = *dump_path; + // Create the full dump file name from the dump path. + full_dump_file = dump_file.substr(0, dump_file.length() - 4) + L"-full.dmp"; +} + +// static +bool ExceptionHandlerTest::DumpCallback(const wchar_t* dump_path, + const wchar_t* minidump_id, + void* context, + EXCEPTION_POINTERS* exinfo, + MDRawAssertionInfo* assertion, + bool succeeded) { + dump_file = dump_path; + dump_file += L"\\"; + dump_file += minidump_id; + dump_file += L".dmp"; + return succeeded; +} + +void ExceptionHandlerTest::DoCrashInvalidParameter() { + google_breakpad::ExceptionHandler* exc = + new google_breakpad::ExceptionHandler( + temp_path_, NULL, NULL, NULL, + google_breakpad::ExceptionHandler::HANDLER_INVALID_PARAMETER, + kFullDumpType, kPipeName, NULL); + + // Disable the message box for assertions + _CrtSetReportMode(_CRT_ASSERT, 0); + + // Although this is executing in the child process of the death test, + // if it's not true we'll still get an error rather than the crash + // being expected. + ASSERT_TRUE(exc->IsOutOfProcess()); + printf(NULL); +} + + +struct PureVirtualCallBase { + PureVirtualCallBase() { + // We have to reinterpret so the linker doesn't get confused because the + // method isn't defined. + reinterpret_cast(this)->PureFunction(); + } + virtual ~PureVirtualCallBase() {} + virtual void PureFunction() const = 0; +}; +struct PureVirtualCall : public PureVirtualCallBase { + PureVirtualCall() { PureFunction(); } + virtual void PureFunction() const {} +}; + +void ExceptionHandlerTest::DoCrashPureVirtualCall() { + google_breakpad::ExceptionHandler* exc = + new google_breakpad::ExceptionHandler( + temp_path_, NULL, NULL, NULL, + google_breakpad::ExceptionHandler::HANDLER_PURECALL, + kFullDumpType, kPipeName, NULL); + + // Disable the message box for assertions + _CrtSetReportMode(_CRT_ASSERT, 0); + + // Although this is executing in the child process of the death test, + // if it's not true we'll still get an error rather than the crash + // being expected. + ASSERT_TRUE(exc->IsOutOfProcess()); + + // Create a new frame to ensure PureVirtualCall is not optimized to some + // other line in this function. + { + PureVirtualCall instance; + } +} + +// This test validates that the minidump is written correctly. +TEST_F(ExceptionHandlerTest, InvalidParameterMiniDumpTest) { + ASSERT_TRUE(DoesPathExist(temp_path_)); + + // Call with a bad argument + ASSERT_TRUE(DoesPathExist(temp_path_)); + wstring dump_path(temp_path_); + google_breakpad::CrashGenerationServer server( + kPipeName, NULL, NULL, NULL, ClientDumpCallback, NULL, NULL, NULL, NULL, + NULL, true, &dump_path); + + ASSERT_TRUE(dump_file.empty() && full_dump_file.empty()); + + // This HAS to be EXPECT_, because when this test case is executed in the + // child process, the server registration will fail due to the named pipe + // being the same. + EXPECT_TRUE(server.Start()); + EXPECT_EXIT(DoCrashInvalidParameter(), ::testing::ExitedWithCode(0), ""); + ASSERT_TRUE(!dump_file.empty() && !full_dump_file.empty()); + ASSERT_TRUE(DoesPathExist(dump_file.c_str())); + ASSERT_TRUE(DoesPathExist(full_dump_file.c_str())); + + // Verify the dump for infos. + DumpAnalysis mini(dump_file); + DumpAnalysis full(full_dump_file); + + // The dump should have all of these streams. + EXPECT_TRUE(mini.HasStream(ThreadListStream)); + EXPECT_TRUE(full.HasStream(ThreadListStream)); + EXPECT_TRUE(mini.HasStream(ModuleListStream)); + EXPECT_TRUE(full.HasStream(ModuleListStream)); + EXPECT_TRUE(mini.HasStream(ExceptionStream)); + EXPECT_TRUE(full.HasStream(ExceptionStream)); + EXPECT_TRUE(mini.HasStream(SystemInfoStream)); + EXPECT_TRUE(full.HasStream(SystemInfoStream)); + EXPECT_TRUE(mini.HasStream(MiscInfoStream)); + EXPECT_TRUE(full.HasStream(MiscInfoStream)); + EXPECT_TRUE(mini.HasStream(HandleDataStream)); + EXPECT_TRUE(full.HasStream(HandleDataStream)); + + // We expect PEB and TEBs in this dump. + EXPECT_TRUE(mini.HasTebs() || full.HasTebs()); + EXPECT_TRUE(mini.HasPeb() || full.HasPeb()); + + // Minidump should have a memory listing, but no 64-bit memory. + EXPECT_TRUE(mini.HasStream(MemoryListStream)); + EXPECT_FALSE(mini.HasStream(Memory64ListStream)); + + EXPECT_FALSE(full.HasStream(MemoryListStream)); + EXPECT_TRUE(full.HasStream(Memory64ListStream)); + + // This is the only place we don't use OR because we want both not + // to have the streams. + EXPECT_FALSE(mini.HasStream(ThreadExListStream)); + EXPECT_FALSE(full.HasStream(ThreadExListStream)); + EXPECT_FALSE(mini.HasStream(CommentStreamA)); + EXPECT_FALSE(full.HasStream(CommentStreamA)); + EXPECT_FALSE(mini.HasStream(CommentStreamW)); + EXPECT_FALSE(full.HasStream(CommentStreamW)); + EXPECT_FALSE(mini.HasStream(FunctionTableStream)); + EXPECT_FALSE(full.HasStream(FunctionTableStream)); + EXPECT_FALSE(mini.HasStream(MemoryInfoListStream)); + EXPECT_FALSE(full.HasStream(MemoryInfoListStream)); + EXPECT_FALSE(mini.HasStream(ThreadInfoListStream)); + EXPECT_FALSE(full.HasStream(ThreadInfoListStream)); + EXPECT_FALSE(mini.HasStream(HandleOperationListStream)); + EXPECT_FALSE(full.HasStream(HandleOperationListStream)); + EXPECT_FALSE(mini.HasStream(TokenStream)); + EXPECT_FALSE(full.HasStream(TokenStream)); +} + + +// This test validates that the minidump is written correctly. +TEST_F(ExceptionHandlerTest, PureVirtualCallMiniDumpTest) { + ASSERT_TRUE(DoesPathExist(temp_path_)); + + // Call with a bad argument + ASSERT_TRUE(DoesPathExist(temp_path_)); + wstring dump_path(temp_path_); + google_breakpad::CrashGenerationServer server( + kPipeName, NULL, NULL, NULL, ClientDumpCallback, NULL, NULL, NULL, NULL, + NULL, true, &dump_path); + + ASSERT_TRUE(dump_file.empty() && full_dump_file.empty()); + + // This HAS to be EXPECT_, because when this test case is executed in the + // child process, the server registration will fail due to the named pipe + // being the same. + EXPECT_TRUE(server.Start()); + EXPECT_EXIT(DoCrashPureVirtualCall(), ::testing::ExitedWithCode(0), ""); + ASSERT_TRUE(!dump_file.empty() && !full_dump_file.empty()); + ASSERT_TRUE(DoesPathExist(dump_file.c_str())); + ASSERT_TRUE(DoesPathExist(full_dump_file.c_str())); + + // Verify the dump for infos. + DumpAnalysis mini(dump_file); + DumpAnalysis full(full_dump_file); + + // The dump should have all of these streams. + EXPECT_TRUE(mini.HasStream(ThreadListStream)); + EXPECT_TRUE(full.HasStream(ThreadListStream)); + EXPECT_TRUE(mini.HasStream(ModuleListStream)); + EXPECT_TRUE(full.HasStream(ModuleListStream)); + EXPECT_TRUE(mini.HasStream(ExceptionStream)); + EXPECT_TRUE(full.HasStream(ExceptionStream)); + EXPECT_TRUE(mini.HasStream(SystemInfoStream)); + EXPECT_TRUE(full.HasStream(SystemInfoStream)); + EXPECT_TRUE(mini.HasStream(MiscInfoStream)); + EXPECT_TRUE(full.HasStream(MiscInfoStream)); + EXPECT_TRUE(mini.HasStream(HandleDataStream)); + EXPECT_TRUE(full.HasStream(HandleDataStream)); + + // We expect PEB and TEBs in this dump. + EXPECT_TRUE(mini.HasTebs() || full.HasTebs()); + EXPECT_TRUE(mini.HasPeb() || full.HasPeb()); + + // Minidump should have a memory listing, but no 64-bit memory. + EXPECT_TRUE(mini.HasStream(MemoryListStream)); + EXPECT_FALSE(mini.HasStream(Memory64ListStream)); + + EXPECT_FALSE(full.HasStream(MemoryListStream)); + EXPECT_TRUE(full.HasStream(Memory64ListStream)); + + // This is the only place we don't use OR because we want both not + // to have the streams. + EXPECT_FALSE(mini.HasStream(ThreadExListStream)); + EXPECT_FALSE(full.HasStream(ThreadExListStream)); + EXPECT_FALSE(mini.HasStream(CommentStreamA)); + EXPECT_FALSE(full.HasStream(CommentStreamA)); + EXPECT_FALSE(mini.HasStream(CommentStreamW)); + EXPECT_FALSE(full.HasStream(CommentStreamW)); + EXPECT_FALSE(mini.HasStream(FunctionTableStream)); + EXPECT_FALSE(full.HasStream(FunctionTableStream)); + EXPECT_FALSE(mini.HasStream(MemoryInfoListStream)); + EXPECT_FALSE(full.HasStream(MemoryInfoListStream)); + EXPECT_FALSE(mini.HasStream(ThreadInfoListStream)); + EXPECT_FALSE(full.HasStream(ThreadInfoListStream)); + EXPECT_FALSE(mini.HasStream(HandleOperationListStream)); + EXPECT_FALSE(full.HasStream(HandleOperationListStream)); + EXPECT_FALSE(mini.HasStream(TokenStream)); + EXPECT_FALSE(full.HasStream(TokenStream)); +} + +// Test that writing a minidump produces a valid minidump containing +// some expected structures. +TEST_F(ExceptionHandlerTest, WriteMinidumpTest) { + ExceptionHandler handler(temp_path_, + NULL, + DumpCallback, + NULL, + ExceptionHandler::HANDLER_ALL); + + // Disable GTest SEH handler + testing::DisableExceptionHandlerInScope disable_exception_handler; + + ASSERT_TRUE(handler.WriteMinidump()); + ASSERT_FALSE(dump_file.empty()); + + string minidump_filename; + ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(dump_file, + &minidump_filename)); + + // Read the minidump and verify some info. + Minidump minidump(minidump_filename); + ASSERT_TRUE(minidump.Read()); + // TODO(ted): more comprehensive tests... +} + +// Test that an additional memory region can be included in the minidump. +TEST_F(ExceptionHandlerTest, AdditionalMemory) { + SYSTEM_INFO si; + GetSystemInfo(&si); + const uint32_t kMemorySize = si.dwPageSize; + + // Get some heap memory. + uint8_t* memory = new uint8_t[kMemorySize]; + const uintptr_t kMemoryAddress = reinterpret_cast(memory); + ASSERT_TRUE(memory); + + // Stick some data into the memory so the contents can be verified. + for (uint32_t i = 0; i < kMemorySize; ++i) { + memory[i] = i % 255; + } + + ExceptionHandler handler(temp_path_, + NULL, + DumpCallback, + NULL, + ExceptionHandler::HANDLER_ALL); + + // Disable GTest SEH handler + testing::DisableExceptionHandlerInScope disable_exception_handler; + + // Add the memory region to the list of memory to be included. + handler.RegisterAppMemory(memory, kMemorySize); + ASSERT_TRUE(handler.WriteMinidump()); + ASSERT_FALSE(dump_file.empty()); + + string minidump_filename; + ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(dump_file, + &minidump_filename)); + + // Read the minidump. Ensure that the memory region is present + Minidump minidump(minidump_filename); + ASSERT_TRUE(minidump.Read()); + + MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList(); + ASSERT_TRUE(dump_memory_list); + const MinidumpMemoryRegion* region = + dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress); + ASSERT_TRUE(region); + + EXPECT_EQ(kMemoryAddress, region->GetBase()); + EXPECT_EQ(kMemorySize, region->GetSize()); + + // Verify memory contents. + EXPECT_EQ(0, memcmp(region->GetMemory(), memory, kMemorySize)); + + delete[] memory; +} + +// Test that a memory region that was previously registered +// can be unregistered. +TEST_F(ExceptionHandlerTest, AdditionalMemoryRemove) { + SYSTEM_INFO si; + GetSystemInfo(&si); + const uint32_t kMemorySize = si.dwPageSize; + + // Get some heap memory. + uint8_t* memory = new uint8_t[kMemorySize]; + const uintptr_t kMemoryAddress = reinterpret_cast(memory); + ASSERT_TRUE(memory); + + // Stick some data into the memory so the contents can be verified. + for (uint32_t i = 0; i < kMemorySize; ++i) { + memory[i] = i % 255; + } + + ExceptionHandler handler(temp_path_, + NULL, + DumpCallback, + NULL, + ExceptionHandler::HANDLER_ALL); + + // Disable GTest SEH handler + testing::DisableExceptionHandlerInScope disable_exception_handler; + + // Add the memory region to the list of memory to be included. + handler.RegisterAppMemory(memory, kMemorySize); + + // ...and then remove it + handler.UnregisterAppMemory(memory); + + ASSERT_TRUE(handler.WriteMinidump()); + ASSERT_FALSE(dump_file.empty()); + + string minidump_filename; + ASSERT_TRUE(WindowsStringUtils::safe_wcstombs(dump_file, + &minidump_filename)); + + // Read the minidump. Ensure that the memory region is not present. + Minidump minidump(minidump_filename); + ASSERT_TRUE(minidump.Read()); + + MinidumpMemoryList* dump_memory_list = minidump.GetMemoryList(); + ASSERT_TRUE(dump_memory_list); + const MinidumpMemoryRegion* region = + dump_memory_list->GetMemoryRegionForAddress(kMemoryAddress); + EXPECT_FALSE(region); + + delete[] memory; +} + +} // namespace diff --git a/shared/sentry/external/breakpad/src/client/windows/unittests/exception_handler_test.h b/shared/sentry/external/breakpad/src/client/windows/unittests/exception_handler_test.h new file mode 100644 index 000000000..ef973e539 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/unittests/exception_handler_test.h @@ -0,0 +1,61 @@ +// Copyright 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 CLIENT_WINDOWS_UNITTESTS_EXCEPTION_HANDLER_TEST_H_ +#define CLIENT_WINDOWS_UNITTESTS_EXCEPTION_HANDLER_TEST_H_ + +namespace testing { + +// By default, GTest (on Windows) installs a SEH filter (and a handler) before +// starting to run all the tests in order to avoid test interruptions is some +// of the tests are crashing. Unfortunately, this functionality prevents the +// execution to reach the UnhandledExceptionFilter installed by Google-Breakpad +// ExceptionHandler so in order to test the Google-Breakpad exception handling +// code the exception handling done by GTest must be disabled. +// Usage: +// +// google_breakpad::ExceptionHandler exc(...); +// +// // Disable GTest SEH handler +// testing::DisableExceptionHandlerInScope disable_exception_handler; +// ... +// ASSERT_DEATH( ... some crash ...); +// +class DisableExceptionHandlerInScope { + public: + DisableExceptionHandlerInScope(); + ~DisableExceptionHandlerInScope(); + + private: + bool catch_exceptions_; +}; + +} // namespace testing + +#endif // CLIENT_WINDOWS_UNITTESTS_EXCEPTION_HANDLER_TEST_H_ diff --git a/shared/sentry/external/breakpad/src/client/windows/unittests/minidump_test.cc b/shared/sentry/external/breakpad/src/client/windows/unittests/minidump_test.cc new file mode 100644 index 000000000..82641125c --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/unittests/minidump_test.cc @@ -0,0 +1,332 @@ +// 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 +#include +#include + +#include "breakpad_googletest_includes.h" +#include "client/windows/crash_generation/minidump_generator.h" +#include "client/windows/unittests/dump_analysis.h" // NOLINT + +namespace { + +// Minidump with stacks, PEB, TEB, and unloaded module list. +const MINIDUMP_TYPE kSmallDumpType = static_cast( + MiniDumpWithProcessThreadData | // Get PEB and TEB. + MiniDumpWithUnloadedModules); // Get unloaded modules when available. + +// Minidump with all of the above, plus memory referenced from stack. +const MINIDUMP_TYPE kLargerDumpType = static_cast( + MiniDumpWithProcessThreadData | // Get PEB and TEB. + MiniDumpWithUnloadedModules | // Get unloaded modules when available. + MiniDumpWithIndirectlyReferencedMemory); // Get memory referenced by stack. + +// Large dump with all process memory. +const MINIDUMP_TYPE kFullDumpType = static_cast( + MiniDumpWithFullMemory | // Full memory from process. + MiniDumpWithProcessThreadData | // Get PEB and TEB. + MiniDumpWithHandleData | // Get all handle information. + MiniDumpWithUnloadedModules); // Get unloaded modules when available. + +class MinidumpTest: public testing::Test { + public: + MinidumpTest() { + wchar_t temp_dir_path[ MAX_PATH ] = {0}; + ::GetTempPath(MAX_PATH, temp_dir_path); + dump_path_ = temp_dir_path; + } + + virtual void SetUp() { + // Make sure URLMon isn't loaded into our process. + ASSERT_EQ(NULL, ::GetModuleHandle(L"urlmon.dll")); + + // Then load and unload it to ensure we have something to + // stock the unloaded module list with. + HMODULE urlmon = ::LoadLibrary(L"urlmon.dll"); + ASSERT_TRUE(urlmon != NULL); + ASSERT_TRUE(::FreeLibrary(urlmon)); + } + + virtual void TearDown() { + if (!dump_file_.empty()) { + ::DeleteFile(dump_file_.c_str()); + dump_file_ = L""; + } + if (!full_dump_file_.empty()) { + ::DeleteFile(full_dump_file_.c_str()); + full_dump_file_ = L""; + } + } + + bool WriteDump(ULONG flags) { + using google_breakpad::MinidumpGenerator; + + // Fake exception is access violation on write to this. + EXCEPTION_RECORD ex_record = { + STATUS_ACCESS_VIOLATION, // ExceptionCode + 0, // ExceptionFlags + NULL, // ExceptionRecord; + reinterpret_cast(static_cast(0xCAFEBABE)), // ExceptionAddress; + 2, // NumberParameters; + { EXCEPTION_WRITE_FAULT, reinterpret_cast(this) } + }; + CONTEXT ctx_record = {}; + EXCEPTION_POINTERS ex_ptrs = { + &ex_record, + &ctx_record, + }; + + MinidumpGenerator generator(dump_path_, + ::GetCurrentProcess(), + ::GetCurrentProcessId(), + ::GetCurrentThreadId(), + ::GetCurrentThreadId(), + &ex_ptrs, + NULL, + static_cast(flags), + TRUE); + generator.GenerateDumpFile(&dump_file_); + generator.GenerateFullDumpFile(&full_dump_file_); + // And write a dump + bool result = generator.WriteMinidump(); + return result == TRUE; + } + + protected: + std::wstring dump_file_; + std::wstring full_dump_file_; + + std::wstring dump_path_; +}; + +// We need to be able to get file information from Windows +bool HasFileInfo(const std::wstring& file_path) { + DWORD dummy; + const wchar_t* path = file_path.c_str(); + DWORD length = ::GetFileVersionInfoSize(path, &dummy); + if (length == 0) + return NULL; + + void* data = calloc(length, 1); + if (!data) + return false; + + if (!::GetFileVersionInfo(path, dummy, length, data)) { + free(data); + return false; + } + + void* translate = NULL; + UINT page_count; + BOOL query_result = VerQueryValue( + data, + L"\\VarFileInfo\\Translation", + static_cast(&translate), + &page_count); + + free(data); + if (query_result && translate) { + return true; + } else { + return false; + } +} + +TEST_F(MinidumpTest, Version) { + // Loads DbgHelp.dll in process + ImagehlpApiVersion(); + + HMODULE dbg_help = ::GetModuleHandle(L"dbghelp.dll"); + ASSERT_TRUE(dbg_help != NULL); + + wchar_t dbg_help_file[1024] = {}; + ASSERT_TRUE(::GetModuleFileName(dbg_help, + dbg_help_file, + sizeof(dbg_help_file) / + sizeof(*dbg_help_file))); + ASSERT_TRUE(HasFileInfo(std::wstring(dbg_help_file)) != NULL); + +// LOG(INFO) << "DbgHelp.dll version: " << file_info->file_version(); +} + +TEST_F(MinidumpTest, Normal) { + EXPECT_TRUE(WriteDump(MiniDumpNormal)); + DumpAnalysis mini(dump_file_); + + // We expect threads, modules and some memory. + EXPECT_TRUE(mini.HasStream(ThreadListStream)); + EXPECT_TRUE(mini.HasStream(ModuleListStream)); + EXPECT_TRUE(mini.HasStream(MemoryListStream)); + EXPECT_TRUE(mini.HasStream(ExceptionStream)); + EXPECT_TRUE(mini.HasStream(SystemInfoStream)); + EXPECT_TRUE(mini.HasStream(MiscInfoStream)); + + EXPECT_FALSE(mini.HasStream(ThreadExListStream)); + EXPECT_FALSE(mini.HasStream(Memory64ListStream)); + EXPECT_FALSE(mini.HasStream(CommentStreamA)); + EXPECT_FALSE(mini.HasStream(CommentStreamW)); + EXPECT_FALSE(mini.HasStream(HandleDataStream)); + EXPECT_FALSE(mini.HasStream(FunctionTableStream)); + EXPECT_FALSE(mini.HasStream(UnloadedModuleListStream)); + EXPECT_FALSE(mini.HasStream(MemoryInfoListStream)); + EXPECT_FALSE(mini.HasStream(ThreadInfoListStream)); + EXPECT_FALSE(mini.HasStream(HandleOperationListStream)); + EXPECT_FALSE(mini.HasStream(TokenStream)); + + // We expect no PEB nor TEBs in this dump. + EXPECT_FALSE(mini.HasTebs()); + EXPECT_FALSE(mini.HasPeb()); + + // We expect no off-stack memory in this dump. + EXPECT_FALSE(mini.HasMemory(this)); +} + +TEST_F(MinidumpTest, SmallDump) { + ASSERT_TRUE(WriteDump(kSmallDumpType)); + DumpAnalysis mini(dump_file_); + + EXPECT_TRUE(mini.HasStream(ThreadListStream)); + EXPECT_TRUE(mini.HasStream(ModuleListStream)); + EXPECT_TRUE(mini.HasStream(MemoryListStream)); + EXPECT_TRUE(mini.HasStream(ExceptionStream)); + EXPECT_TRUE(mini.HasStream(SystemInfoStream)); + EXPECT_TRUE(mini.HasStream(UnloadedModuleListStream)); + EXPECT_TRUE(mini.HasStream(MiscInfoStream)); + + // We expect PEB and TEBs in this dump. + EXPECT_TRUE(mini.HasTebs()); + EXPECT_TRUE(mini.HasPeb()); + + EXPECT_FALSE(mini.HasStream(ThreadExListStream)); + EXPECT_FALSE(mini.HasStream(Memory64ListStream)); + EXPECT_FALSE(mini.HasStream(CommentStreamA)); + EXPECT_FALSE(mini.HasStream(CommentStreamW)); + EXPECT_FALSE(mini.HasStream(HandleDataStream)); + EXPECT_FALSE(mini.HasStream(FunctionTableStream)); + EXPECT_FALSE(mini.HasStream(MemoryInfoListStream)); + EXPECT_FALSE(mini.HasStream(ThreadInfoListStream)); + EXPECT_FALSE(mini.HasStream(HandleOperationListStream)); + EXPECT_FALSE(mini.HasStream(TokenStream)); + + // We expect no off-stack memory in this dump. + EXPECT_FALSE(mini.HasMemory(this)); +} + +TEST_F(MinidumpTest, LargerDump) { + ASSERT_TRUE(WriteDump(kLargerDumpType)); + DumpAnalysis mini(dump_file_); + + // The dump should have all of these streams. + EXPECT_TRUE(mini.HasStream(ThreadListStream)); + EXPECT_TRUE(mini.HasStream(ModuleListStream)); + EXPECT_TRUE(mini.HasStream(MemoryListStream)); + EXPECT_TRUE(mini.HasStream(ExceptionStream)); + EXPECT_TRUE(mini.HasStream(SystemInfoStream)); + EXPECT_TRUE(mini.HasStream(UnloadedModuleListStream)); + EXPECT_TRUE(mini.HasStream(MiscInfoStream)); + + // We expect memory referenced by stack in this dump. + EXPECT_TRUE(mini.HasMemory(this)); + + // We expect PEB and TEBs in this dump. + EXPECT_TRUE(mini.HasTebs()); + EXPECT_TRUE(mini.HasPeb()); + + EXPECT_FALSE(mini.HasStream(ThreadExListStream)); + EXPECT_FALSE(mini.HasStream(Memory64ListStream)); + EXPECT_FALSE(mini.HasStream(CommentStreamA)); + EXPECT_FALSE(mini.HasStream(CommentStreamW)); + EXPECT_FALSE(mini.HasStream(HandleDataStream)); + EXPECT_FALSE(mini.HasStream(FunctionTableStream)); + EXPECT_FALSE(mini.HasStream(MemoryInfoListStream)); + EXPECT_FALSE(mini.HasStream(ThreadInfoListStream)); + EXPECT_FALSE(mini.HasStream(HandleOperationListStream)); + EXPECT_FALSE(mini.HasStream(TokenStream)); +} + +TEST_F(MinidumpTest, FullDump) { + ASSERT_TRUE(WriteDump(kFullDumpType)); + ASSERT_TRUE(dump_file_ != L""); + ASSERT_TRUE(full_dump_file_ != L""); + DumpAnalysis mini(dump_file_); + DumpAnalysis full(full_dump_file_); + + // Either dumps can contain part of the information. + + // The dump should have all of these streams. + EXPECT_TRUE(mini.HasStream(ThreadListStream)); + EXPECT_TRUE(full.HasStream(ThreadListStream)); + EXPECT_TRUE(mini.HasStream(ModuleListStream)); + EXPECT_TRUE(full.HasStream(ModuleListStream)); + EXPECT_TRUE(mini.HasStream(ExceptionStream)); + EXPECT_TRUE(full.HasStream(ExceptionStream)); + EXPECT_TRUE(mini.HasStream(SystemInfoStream)); + EXPECT_TRUE(full.HasStream(SystemInfoStream)); + EXPECT_TRUE(mini.HasStream(UnloadedModuleListStream)); + EXPECT_TRUE(full.HasStream(UnloadedModuleListStream)); + EXPECT_TRUE(mini.HasStream(MiscInfoStream)); + EXPECT_TRUE(full.HasStream(MiscInfoStream)); + EXPECT_TRUE(mini.HasStream(HandleDataStream)); + EXPECT_TRUE(full.HasStream(HandleDataStream)); + + // We expect memory referenced by stack in this dump. + EXPECT_FALSE(mini.HasMemory(this)); + EXPECT_TRUE(full.HasMemory(this)); + + // We expect PEB and TEBs in this dump. + EXPECT_TRUE(mini.HasTebs() || full.HasTebs()); + EXPECT_TRUE(mini.HasPeb() || full.HasPeb()); + + EXPECT_TRUE(mini.HasStream(MemoryListStream)); + EXPECT_TRUE(full.HasStream(Memory64ListStream)); + EXPECT_FALSE(mini.HasStream(Memory64ListStream)); + EXPECT_FALSE(full.HasStream(MemoryListStream)); + + // This is the only place we don't use OR because we want both not + // to have the streams. + EXPECT_FALSE(mini.HasStream(ThreadExListStream)); + EXPECT_FALSE(full.HasStream(ThreadExListStream)); + EXPECT_FALSE(mini.HasStream(CommentStreamA)); + EXPECT_FALSE(full.HasStream(CommentStreamA)); + EXPECT_FALSE(mini.HasStream(CommentStreamW)); + EXPECT_FALSE(full.HasStream(CommentStreamW)); + EXPECT_FALSE(mini.HasStream(FunctionTableStream)); + EXPECT_FALSE(full.HasStream(FunctionTableStream)); + EXPECT_FALSE(mini.HasStream(MemoryInfoListStream)); + EXPECT_FALSE(full.HasStream(MemoryInfoListStream)); + EXPECT_FALSE(mini.HasStream(ThreadInfoListStream)); + EXPECT_FALSE(full.HasStream(ThreadInfoListStream)); + EXPECT_FALSE(mini.HasStream(HandleOperationListStream)); + EXPECT_FALSE(full.HasStream(HandleOperationListStream)); + EXPECT_FALSE(mini.HasStream(TokenStream)); + EXPECT_FALSE(full.HasStream(TokenStream)); +} + +} // namespace diff --git a/shared/sentry/external/breakpad/src/client/windows/unittests/testing.gyp b/shared/sentry/external/breakpad/src/client/windows/unittests/testing.gyp new file mode 100644 index 000000000..0f9f944c2 --- /dev/null +++ b/shared/sentry/external/breakpad/src/client/windows/unittests/testing.gyp @@ -0,0 +1,90 @@ +# Copyright 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. + +{ + 'includes': [ + '../../../build/common.gypi', + ], + 'target_defaults': { + }, + 'targets': [ + { + 'target_name': 'gtest', + 'type': 'static_library', + 'include_dirs': [ + '<(DEPTH)/testing/include', + '<(DEPTH)/testing/googletest/include', + '<(DEPTH)/testing/googletest', + '<(DEPTH)/testing', + ], + 'sources': [ + '<(DEPTH)/testing/googletest/src/gtest-all.cc', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '<(DEPTH)/testing/include', + '<(DEPTH)/testing/gtest/include', + ], + # Visual C++ implements variadic templates strangely, and + # VC++2012 broke Google Test by lowering this value. See + # http://stackoverflow.com/questions/12558327/google-test-in-visual-studio-2012 + 'defines': ['_VARIADIC_MAX=10', '_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING'], + }, + 'defines': ['_VARIADIC_MAX=10', '_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING'], + }, + { + 'target_name': 'gmock', + 'type': 'static_library', + 'include_dirs': [ + '<(DEPTH)/testing/include', + '<(DEPTH)/testing/googletest/include', + '<(DEPTH)/testing/googletest', + '<(DEPTH)/testing/googlemock/include', + '<(DEPTH)/testing/googlemock', + '<(DEPTH)/testing', + ], + 'sources': [ + '<(DEPTH)/testing/googlemock/src/gmock-all.cc', + '<(DEPTH)/testing/googletest/src/gtest_main.cc', + ], + 'direct_dependent_settings': { + 'include_dirs': [ + '<(DEPTH)/testing/include', + '<(DEPTH)/testing/googletest/include', + '<(DEPTH)/testing/googletest', + '<(DEPTH)/testing/googlemock/include', + '<(DEPTH)/testing/googlemock', + '<(DEPTH)/testing', + ], + 'defines': ['_VARIADIC_MAX=10', '_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING'], + }, + 'defines': ['_VARIADIC_MAX=10', '_SILENCE_TR1_NAMESPACE_DEPRECATION_WARNING'], + }, + + ], +} diff --git a/shared/sentry/external/breakpad/src/common/android/include/asm-mips/README.md b/shared/sentry/external/breakpad/src/common/android/include/asm-mips/README.md new file mode 100644 index 000000000..b56ee60f1 --- /dev/null +++ b/shared/sentry/external/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/shared/sentry/external/breakpad/src/common/android/include/asm-mips/asm.h b/shared/sentry/external/breakpad/src/common/android/include/asm-mips/asm.h new file mode 100644 index 000000000..8f086e756 --- /dev/null +++ b/shared/sentry/external/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() +#include_next +#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 +#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() +#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ASM_MIPS_ASM_H +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ diff --git a/shared/sentry/external/breakpad/src/common/android/include/asm-mips/fpregdef.h b/shared/sentry/external/breakpad/src/common/android/include/asm-mips/fpregdef.h new file mode 100644 index 000000000..a6eedc0e9 --- /dev/null +++ b/shared/sentry/external/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() +#include_next +#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 +#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() +#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ASM_MIPS_FPREGDEF_H diff --git a/shared/sentry/external/breakpad/src/common/android/include/asm-mips/regdef.h b/shared/sentry/external/breakpad/src/common/android/include/asm-mips/regdef.h new file mode 100644 index 000000000..a7fd76905 --- /dev/null +++ b/shared/sentry/external/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() +#include_next +#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 +#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() +#endif // GOOGLE_BREAKPAD_COMMON_ANDROID_INCLUDE_ASM_MIPS_REGDEF_H +/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */ diff --git a/shared/sentry/external/breakpad/src/common/android/include/elf.h b/shared/sentry/external/breakpad/src/common/android/include/elf.h new file mode 100644 index 000000000..e6f0c672f --- /dev/null +++ b/shared/sentry/external/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 +#include + +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus + +// The Android 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 + +#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/shared/sentry/external/breakpad/src/common/android/include/link.h b/shared/sentry/external/breakpad/src/common/android/include/link.h new file mode 100644 index 000000000..4324629df --- /dev/null +++ b/shared/sentry/external/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 . + Provide custom version here. */ +#include_next + +#include + +// 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/shared/sentry/external/breakpad/src/common/android/include/stab.h b/shared/sentry/external/breakpad/src/common/android/include/stab.h new file mode 100644 index 000000000..cd9290215 --- /dev/null +++ b/shared/sentry/external/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 + +#ifdef __BIONIC_HAVE_STAB_H +#include +#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/shared/sentry/external/breakpad/src/common/android/include/sys/procfs.h b/shared/sentry/external/breakpad/src/common/android/include/sys/procfs.h new file mode 100644 index 000000000..185124364 --- /dev/null +++ b/shared/sentry/external/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 + +#else + +#include +#include +#if defined (__mips__) +#include +#endif +#include +#include + +#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/shared/sentry/external/breakpad/src/common/android/include/sys/user.h b/shared/sentry/external/breakpad/src/common/android/include/sys/user.h new file mode 100644 index 000000000..9c27ef022 --- /dev/null +++ b/shared/sentry/external/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, forgot to do this. Unified headers get +// it right. +#include + +#include_next + +#include + +#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/shared/sentry/external/breakpad/src/common/android/testing/include/wchar.h b/shared/sentry/external/breakpad/src/common/android/testing/include/wchar.h new file mode 100644 index 000000000..85373fd2a --- /dev/null +++ b/shared/sentry/external/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 + +#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/shared/sentry/external/breakpad/src/common/android/testing/mkdtemp.h b/shared/sentry/external/breakpad/src/common/android/testing/mkdtemp.h new file mode 100644 index 000000000..b86e2cd78 --- /dev/null +++ b/shared/sentry/external/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 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 +#include +#include +#include +#include +#include + +// 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(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/shared/sentry/external/breakpad/src/common/android/testing/pthread_fixes.h b/shared/sentry/external/breakpad/src/common/android/testing/pthread_fixes.h new file mode 100644 index 000000000..b0a3d82e4 --- /dev/null +++ b/shared/sentry/external/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 + +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/shared/sentry/external/breakpad/src/common/basictypes.h b/shared/sentry/external/breakpad/src/common/basictypes.h new file mode 100644 index 000000000..9426c1f6c --- /dev/null +++ b/shared/sentry/external/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 my_var = ...; +// if (TakeOwnership(my_var.get()) == SUCCESS) +// ignore_result(my_var.release()); +// +template +inline void ignore_result(const T&) { +} + +} // namespace google_breakpad + +#endif // COMMON_BASICTYPES_H_ diff --git a/shared/sentry/external/breakpad/src/common/byte_cursor.h b/shared/sentry/external/breakpad/src/common/byte_cursor.h new file mode 100644 index 000000000..28bb8e767 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/byte_cursor.h @@ -0,0 +1,266 @@ +// -*- 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 + +// 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 +#include +#include +#include +#include + +#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; + const uint8_t* 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 + 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 + 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(memchr(here_, '\0', Available())); + if (end) { + str->assign(reinterpret_cast(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(memchr(here_, '\0', limit)); + if (end) + str->assign(reinterpret_cast(here_), end - here_); + else + str->assign(reinterpret_cast(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/shared/sentry/external/breakpad/src/common/byte_cursor_unittest.cc b/shared/sentry/external/breakpad/src/common/byte_cursor_unittest.cc new file mode 100644 index 000000000..45e6f2b4b --- /dev/null +++ b/shared/sentry/external/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 + +// byte_cursor_unittest.cc: Unit tests for google_breakpad::ByteBuffer +// and google_breakpad::ByteCursor. + +#include + +#include + +#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/shared/sentry/external/breakpad/src/common/common.gyp b/shared/sentry/external/breakpad/src/common/common.gyp new file mode 100644 index 000000000..c1dbb0feb --- /dev/null +++ b/shared/sentry/external/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/shared/sentry/external/breakpad/src/common/convert_UTF.cc b/shared/sentry/external/breakpad/src/common/convert_UTF.cc new file mode 100644 index 000000000..4a5df1eb2 --- /dev/null +++ b/shared/sentry/external/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 +#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/shared/sentry/external/breakpad/src/common/convert_UTF.h b/shared/sentry/external/breakpad/src/common/convert_UTF.h new file mode 100644 index 000000000..2f69495d2 --- /dev/null +++ b/shared/sentry/external/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: , , +or 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/shared/sentry/external/breakpad/src/common/dwarf/bytereader-inl.h b/shared/sentry/external/breakpad/src/common/dwarf/bytereader-inl.h new file mode 100644 index 000000000..dec20e6ff --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/dwarf/bytereader-inl.h @@ -0,0 +1,181 @@ +// 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 +#include + +namespace google_breakpad { + +inline uint8_t ByteReader::ReadOneByte(const uint8_t* buffer) const { + return buffer[0]; +} + +inline uint16_t ByteReader::ReadTwoBytes(const uint8_t* buffer) const { + const uint16_t buffer0 = buffer[0]; + const uint16_t buffer1 = buffer[1]; + if (endian_ == ENDIANNESS_LITTLE) { + return buffer0 | buffer1 << 8; + } else { + return buffer1 | buffer0 << 8; + } +} + +inline uint64_t ByteReader::ReadThreeBytes(const uint8_t* buffer) const { + const uint32_t buffer0 = buffer[0]; + const uint32_t buffer1 = buffer[1]; + const uint32_t buffer2 = buffer[2]; + if (endian_ == ENDIANNESS_LITTLE) { + return buffer0 | buffer1 << 8 | buffer2 << 16; + } else { + return buffer2 | buffer1 << 8 | buffer0 << 16; + } +} + +inline uint64_t ByteReader::ReadFourBytes(const uint8_t* buffer) const { + const uint32_t buffer0 = buffer[0]; + const uint32_t buffer1 = buffer[1]; + const uint32_t buffer2 = buffer[2]; + const uint32_t 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_t ByteReader::ReadEightBytes(const uint8_t* buffer) const { + const uint64_t buffer0 = buffer[0]; + const uint64_t buffer1 = buffer[1]; + const uint64_t buffer2 = buffer[2]; + const uint64_t buffer3 = buffer[3]; + const uint64_t buffer4 = buffer[4]; + const uint64_t buffer5 = buffer[5]; + const uint64_t buffer6 = buffer[6]; + const uint64_t 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_t ByteReader::ReadUnsignedLEB128(const uint8_t* buffer, + size_t* len) const { + uint64_t result = 0; + size_t num_read = 0; + unsigned int shift = 0; + uint8_t byte; + + do { + byte = *buffer++; + num_read++; + + result |= (static_cast(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_t ByteReader::ReadSignedLEB128(const uint8_t* buffer, + size_t* len) const { + int64_t result = 0; + unsigned int shift = 0; + size_t num_read = 0; + uint8_t byte; + + do { + byte = *buffer++; + num_read++; + result |= (static_cast(byte & 0x7f) << shift); + shift += 7; + } while (byte & 0x80); + + if ((shift < 8 * sizeof (result)) && (byte & 0x40)) + result |= -((static_cast(1)) << shift); + *len = num_read; + return result; +} + +inline uint64_t ByteReader::ReadOffset(const uint8_t* buffer) const { + assert(this->offset_reader_); + return (this->*offset_reader_)(buffer); +} + +inline uint64_t ByteReader::ReadAddress(const uint8_t* buffer) const { + assert(this->address_reader_); + return (this->*address_reader_)(buffer); +} + +inline void ByteReader::SetCFIDataBase(uint64_t section_base, + const uint8_t* buffer_base) { + section_base_ = section_base; + buffer_base_ = buffer_base; + have_section_base_ = true; +} + +inline void ByteReader::SetTextBase(uint64_t text_base) { + text_base_ = text_base; + have_text_base_ = true; +} + +inline void ByteReader::SetDataBase(uint64_t data_base) { + data_base_ = data_base; + have_data_base_ = true; +} + +inline void ByteReader::SetFunctionBase(uint64_t function_base) { + function_base_ = function_base; + have_function_base_ = true; +} + +inline void ByteReader::ClearFunctionBase() { + have_function_base_ = false; +} + +} // namespace google_breakpad + +#endif // UTIL_DEBUGINFO_BYTEREADER_INL_H__ diff --git a/shared/sentry/external/breakpad/src/common/dwarf/bytereader.cc b/shared/sentry/external/breakpad/src/common/dwarf/bytereader.cc new file mode 100644 index 000000000..ea3e4f6bc --- /dev/null +++ b/shared/sentry/external/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 +#include +#include + +#include "common/dwarf/bytereader-inl.h" +#include "common/dwarf/bytereader.h" + +namespace google_breakpad { + +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_t 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_t size) { + address_size_ = size; + assert(size == 4 || size == 8); + if (size == 4) { + this->address_reader_ = &ByteReader::ReadFourBytes; + } else { + this->address_reader_ = &ByteReader::ReadEightBytes; + } +} + +uint64_t ByteReader::ReadInitialLength(const uint8_t* start, size_t* len) { + const uint64_t 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_t 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_t skew = section_base_ & (AddressSize() - 1); + // Now find the offset from that aligned address to buffer. + uint64_t offset = skew + (buffer - buffer_base_); + // Round up to the next boundary. + uint64_t 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_t 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_t 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_t pointer = base + offset; + + // Remove inappropriate upper bits. + if (AddressSize() == 4) + pointer = pointer & 0xffffffff; + else + assert(AddressSize() == sizeof(uint64_t)); + + return pointer; +} + +Endianness ByteReader::GetEndianness() const { + return endian_; +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/common/dwarf/bytereader.h b/shared/sentry/external/breakpad/src/common/dwarf/bytereader.h new file mode 100644 index 000000000..b0cac4331 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/dwarf/bytereader.h @@ -0,0 +1,320 @@ +// -*- 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 + +#include + +#include "common/dwarf/types.h" +#include "common/dwarf/dwarf2enums.h" + +namespace google_breakpad { + +// 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_t 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_t ReadTwoBytes(const uint8_t* buffer) const; + + // Read three bytes from BUFFER and return them as an unsigned 64 bit + // number, using this ByteReader's endianness. DWARF 5 uses this encoding + // for various index-related DW_FORMs. + uint64_t ReadThreeBytes(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_t 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_t 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_t 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_t 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_t 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_t size); + + // Return the current address size, in bytes. This is either 4, + // indicating 32-bit addresses, or 8, indicating 64-bit addresses. + uint8_t 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_t 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_t 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_t 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_t 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_t 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_t 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_t 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_t 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_t 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_t 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_t (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_t address_size_; + uint8_t 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_t section_base_, text_base_, data_base_, function_base_; + const uint8_t* buffer_base_; +}; + +} // namespace google_breakpad + +#endif // COMMON_DWARF_BYTEREADER_H__ diff --git a/shared/sentry/external/breakpad/src/common/dwarf/bytereader_unittest.cc b/shared/sentry/external/breakpad/src/common/dwarf/bytereader_unittest.cc new file mode 100644 index 000000000..9a9464d89 --- /dev/null +++ b/shared/sentry/external/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 + +// bytereader_unittest.cc: Unit tests for google_breakpad::ByteReader + +#include + +#include + +#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 google_breakpad::ByteReader; +using google_breakpad::DwarfPointerEncoding; +using google_breakpad::ENDIANNESS_BIG; +using google_breakpad::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(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(google_breakpad::DW_EH_PE_absptr))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_omit))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_aligned))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_uleb128))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_udata2))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_udata4))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_udata8))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_sleb128))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_sdata2))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_sdata4))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_sdata8))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_absptr | + google_breakpad::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_uleb128 | + google_breakpad::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_udata2 | + google_breakpad::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_udata4 | + google_breakpad::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_udata8 | + google_breakpad::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_sleb128 | + google_breakpad::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_sdata2 | + google_breakpad::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_sdata4 | + google_breakpad::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_sdata8 | + google_breakpad::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_absptr | + google_breakpad::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_uleb128 | + google_breakpad::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_udata2 | + google_breakpad::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_udata4 | + google_breakpad::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_udata8 | + google_breakpad::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_sleb128 | + google_breakpad::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_sdata2 | + google_breakpad::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_sdata4 | + google_breakpad::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_sdata8 | + google_breakpad::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_absptr | + google_breakpad::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_uleb128 | + google_breakpad::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_udata2 | + google_breakpad::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_udata4 | + google_breakpad::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_udata8 | + google_breakpad::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_sleb128 | + google_breakpad::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_sdata2 | + google_breakpad::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_sdata4 | + google_breakpad::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_sdata8 | + google_breakpad::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_absptr | + google_breakpad::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_uleb128 | + google_breakpad::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_udata2 | + google_breakpad::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_udata4 | + google_breakpad::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_udata8 | + google_breakpad::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_sleb128 | + google_breakpad::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_sdata2 | + google_breakpad::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_sdata4 | + google_breakpad::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_sdata8 | + google_breakpad::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_absptr | + google_breakpad::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_uleb128 | + google_breakpad::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_udata2 | + google_breakpad::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_udata4 | + google_breakpad::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_udata8 | + google_breakpad::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_sleb128 | + google_breakpad::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_sdata2 | + google_breakpad::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_sdata4 | + google_breakpad::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_sdata8 | + google_breakpad::DW_EH_PE_pcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_absptr | + google_breakpad::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_uleb128 | + google_breakpad::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_udata2 | + google_breakpad::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_udata4 | + google_breakpad::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_udata8 | + google_breakpad::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_sleb128 | + google_breakpad::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_sdata2 | + google_breakpad::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_sdata4 | + google_breakpad::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_sdata8 | + google_breakpad::DW_EH_PE_textrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_absptr | + google_breakpad::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_uleb128 | + google_breakpad::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_udata2 | + google_breakpad::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_udata4 | + google_breakpad::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_udata8 | + google_breakpad::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_sleb128 | + google_breakpad::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_sdata2 | + google_breakpad::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_sdata4 | + google_breakpad::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_sdata8 | + google_breakpad::DW_EH_PE_datarel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_absptr | + google_breakpad::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_uleb128 | + google_breakpad::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_udata2 | + google_breakpad::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_udata4 | + google_breakpad::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_udata8 | + google_breakpad::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_sleb128 | + google_breakpad::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_sdata2 | + google_breakpad::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_sdata4 | + google_breakpad::DW_EH_PE_funcrel))); + EXPECT_TRUE(reader.ValidEncoding( + DwarfPointerEncoding(google_breakpad::DW_EH_PE_indirect | + google_breakpad::DW_EH_PE_sdata8 | + google_breakpad::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, google_breakpad::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, google_breakpad::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, google_breakpad::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, google_breakpad::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, google_breakpad::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, google_breakpad::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, google_breakpad::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, google_breakpad::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, google_breakpad::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, google_breakpad::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, google_breakpad::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, google_breakpad::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(google_breakpad::DW_EH_PE_pcrel + | google_breakpad::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(google_breakpad::DW_EH_PE_textrel + | google_breakpad::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(google_breakpad::DW_EH_PE_datarel + | google_breakpad::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(google_breakpad::DW_EH_PE_funcrel + | google_breakpad::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(google_breakpad::DW_EH_PE_absptr)); + EXPECT_TRUE(reader.UsableEncoding(google_breakpad::DW_EH_PE_pcrel)); + EXPECT_FALSE(reader.UsableEncoding(google_breakpad::DW_EH_PE_textrel)); + EXPECT_FALSE(reader.UsableEncoding(google_breakpad::DW_EH_PE_datarel)); + EXPECT_FALSE(reader.UsableEncoding(google_breakpad::DW_EH_PE_funcrel)); + EXPECT_FALSE(reader.UsableEncoding(google_breakpad::DW_EH_PE_omit)); + EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60))); +} + +TEST(UsableBase, Text) { + ByteReader reader(ENDIANNESS_BIG); + reader.SetTextBase(0xa899ccb9); + EXPECT_TRUE(reader.UsableEncoding(google_breakpad::DW_EH_PE_absptr)); + EXPECT_FALSE(reader.UsableEncoding(google_breakpad::DW_EH_PE_pcrel)); + EXPECT_TRUE(reader.UsableEncoding(google_breakpad::DW_EH_PE_textrel)); + EXPECT_FALSE(reader.UsableEncoding(google_breakpad::DW_EH_PE_datarel)); + EXPECT_FALSE(reader.UsableEncoding(google_breakpad::DW_EH_PE_funcrel)); + EXPECT_FALSE(reader.UsableEncoding(google_breakpad::DW_EH_PE_omit)); + EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60))); +} + +TEST(UsableBase, Data) { + ByteReader reader(ENDIANNESS_BIG); + reader.SetDataBase(0xf7b10bcd); + EXPECT_TRUE(reader.UsableEncoding(google_breakpad::DW_EH_PE_absptr)); + EXPECT_FALSE(reader.UsableEncoding(google_breakpad::DW_EH_PE_pcrel)); + EXPECT_FALSE(reader.UsableEncoding(google_breakpad::DW_EH_PE_textrel)); + EXPECT_TRUE(reader.UsableEncoding(google_breakpad::DW_EH_PE_datarel)); + EXPECT_FALSE(reader.UsableEncoding(google_breakpad::DW_EH_PE_funcrel)); + EXPECT_FALSE(reader.UsableEncoding(google_breakpad::DW_EH_PE_omit)); + EXPECT_FALSE(reader.UsableEncoding(DwarfPointerEncoding(0x60))); +} + +TEST(UsableBase, Function) { + ByteReader reader(ENDIANNESS_BIG); + reader.SetFunctionBase(0xc2c0ed81); + EXPECT_TRUE(reader.UsableEncoding(google_breakpad::DW_EH_PE_absptr)); + EXPECT_FALSE(reader.UsableEncoding(google_breakpad::DW_EH_PE_pcrel)); + EXPECT_FALSE(reader.UsableEncoding(google_breakpad::DW_EH_PE_textrel)); + EXPECT_FALSE(reader.UsableEncoding(google_breakpad::DW_EH_PE_datarel)); + EXPECT_TRUE(reader.UsableEncoding(google_breakpad::DW_EH_PE_funcrel)); + EXPECT_FALSE(reader.UsableEncoding(google_breakpad::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(google_breakpad::DW_EH_PE_absptr)); + EXPECT_FALSE(reader.UsableEncoding(google_breakpad::DW_EH_PE_pcrel)); + EXPECT_FALSE(reader.UsableEncoding(google_breakpad::DW_EH_PE_textrel)); + EXPECT_FALSE(reader.UsableEncoding(google_breakpad::DW_EH_PE_datarel)); + EXPECT_FALSE(reader.UsableEncoding(google_breakpad::DW_EH_PE_funcrel)); + EXPECT_FALSE(reader.UsableEncoding(google_breakpad::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, google_breakpad::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, google_breakpad::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, google_breakpad::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, google_breakpad::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, + google_breakpad::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, + google_breakpad::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, + google_breakpad::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, + google_breakpad::DW_EH_PE_aligned, + &pointer_size)); + EXPECT_EQ(5U, pointer_size); +} diff --git a/shared/sentry/external/breakpad/src/common/dwarf/cfi_assembler.cc b/shared/sentry/external/breakpad/src/common/dwarf/cfi_assembler.cc new file mode 100644 index 000000000..b8dc920fa --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/dwarf/cfi_assembler.cc @@ -0,0 +1,202 @@ +// 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 + +// cfi_assembler.cc: Implementation of google_breakpad::CFISection class. +// See cfi_assembler.h for details. + +#include "common/dwarf/cfi_assembler.h" + +#include +#include + +namespace google_breakpad { + +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_, 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 == DW_EH_PE_omit) + return *this; + + // If (encoding & 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 & ~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 DW_EH_PE_absptr: base = 0; break; + case DW_EH_PE_pcrel: base = bases.cfi + Size(); break; + case DW_EH_PE_textrel: base = bases.text; break; + case DW_EH_PE_datarel: base = bases.data; break; + case DW_EH_PE_funcrel: base = fde_start_address_; break; + case 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) == 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 DW_EH_PE_absptr: + Address(address); + break; + + case DW_EH_PE_uleb128: + ULEB128(address); + break; + + case DW_EH_PE_sleb128: + LEB128(address); + break; + + case DW_EH_PE_udata2: + case DW_EH_PE_sdata2: + D16(address); + break; + + case DW_EH_PE_udata4: + case DW_EH_PE_sdata4: + D32(address); + break; + + case DW_EH_PE_udata8: + case 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/shared/sentry/external/breakpad/src/common/dwarf/cfi_assembler.h b/shared/sentry/external/breakpad/src/common/dwarf/cfi_assembler.h new file mode 100644 index 000000000..6ee2557a7 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/dwarf/cfi_assembler.h @@ -0,0 +1,269 @@ +// -*- 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 + +// 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 + +#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 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(google_breakpad::test_assembler::Endianness endianness, size_t address_size, + bool eh_frame = false) + : Section(endianness), address_size_(address_size), eh_frame_(eh_frame), + pointer_encoding_(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/shared/sentry/external/breakpad/src/common/dwarf/dwarf2diehandler.cc b/shared/sentry/external/breakpad/src/common/dwarf/dwarf2diehandler.cc new file mode 100644 index 000000000..1393c3d7d --- /dev/null +++ b/shared/sentry/external/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 + +// dwarf2diehandler.cc: Implement the dwarf2reader::DieDispatcher class. +// See dwarf2diehandler.h for details. + +#include +#include + +#include + +#include "common/dwarf/dwarf2diehandler.h" +#include "common/using_std_string.h" + +namespace google_breakpad { + +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_t offset, uint8_t address_size, + uint8_t offset_size, uint64_t cu_length, + uint8_t dwarf_version) { + return root_handler_->StartCompilationUnit(offset, address_size, + offset_size, cu_length, + dwarf_version); +} + +bool DIEDispatcher::StartDIE(uint64_t 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_t 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_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t data) { + HandlerStack& current = 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_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + int64_t data) { + HandlerStack& current = 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_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t data) { + HandlerStack& current = 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_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const uint8_t* data, + uint64_t len) { + HandlerStack& current = 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_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const string& data) { + HandlerStack& current = 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_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t signature) { + HandlerStack& current = 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 google_breakpad diff --git a/shared/sentry/external/breakpad/src/common/dwarf/dwarf2diehandler.h b/shared/sentry/external/breakpad/src/common/dwarf/dwarf2diehandler.h new file mode 100644 index 000000000..5e568eb85 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/dwarf/dwarf2diehandler.h @@ -0,0 +1,368 @@ +// -*- 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 + +// 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 + +#include +#include + +#include "common/dwarf/types.h" +#include "common/dwarf/dwarf2enums.h" +#include "common/dwarf/dwarf2reader.h" +#include "common/using_std_string.h" + +namespace google_breakpad { + +// 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_t data) { } + virtual void ProcessAttributeSigned(enum DwarfAttribute attr, + enum DwarfForm form, + int64_t data) { } + virtual void ProcessAttributeReference(enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t data) { } + virtual void ProcessAttributeBuffer(enum DwarfAttribute attr, + enum DwarfForm form, + const uint8_t* data, + uint64_t len) { } + virtual void ProcessAttributeString(enum DwarfAttribute attr, + enum DwarfForm form, + const string& data) { } + virtual void ProcessAttributeSignature(enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t 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_t 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: + bool handle_inline; + + explicit RootDIEHandler(bool handle_inline = false) + : handle_inline(handle_inline) {} + 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_t offset, uint8_t address_size, + uint8_t offset_size, uint64_t cu_length, + uint8_t 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_t 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_t offset, uint8_t address_size, + uint8_t offset_size, uint64_t cu_length, + uint8_t dwarf_version); + bool StartDIE(uint64_t offset, enum DwarfTag tag); + void ProcessAttributeUnsigned(uint64_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t data); + void ProcessAttributeSigned(uint64_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + int64_t data); + void ProcessAttributeReference(uint64_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t data); + void ProcessAttributeBuffer(uint64_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const uint8_t* data, + uint64_t len); + void ProcessAttributeString(uint64_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const string& data); + void ProcessAttributeSignature(uint64_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t signature); + void EndDIE(uint64_t 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_t 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 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 google_breakpad +#endif // COMMON_DWARF_DWARF2DIEHANDLER_H__ diff --git a/shared/sentry/external/breakpad/src/common/dwarf/dwarf2diehandler_unittest.cc b/shared/sentry/external/breakpad/src/common/dwarf/dwarf2diehandler_unittest.cc new file mode 100644 index 000000000..164b34627 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/dwarf/dwarf2diehandler_unittest.cc @@ -0,0 +1,528 @@ +// -*- 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 + +// dwarf2diehander_unittest.cc: Unit tests for google_breakpad::DIEDispatcher. + +#include + +#include +#include + +#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 google_breakpad::DIEDispatcher; +using google_breakpad::DIEHandler; +using google_breakpad::DwarfAttribute; +using google_breakpad::DwarfForm; +using google_breakpad::DwarfTag; +using google_breakpad::RootDIEHandler; + +class MockDIEHandler: public DIEHandler { + public: + MOCK_METHOD3(ProcessAttributeUnsigned, + void(DwarfAttribute, DwarfForm, uint64_t)); + MOCK_METHOD3(ProcessAttributeSigned, + void(DwarfAttribute, DwarfForm, int64_t)); + MOCK_METHOD3(ProcessAttributeReference, + void(DwarfAttribute, DwarfForm, uint64_t)); + MOCK_METHOD4(ProcessAttributeBuffer, + void(DwarfAttribute, DwarfForm, const uint8_t*, uint64_t)); + MOCK_METHOD3(ProcessAttributeString, + void(DwarfAttribute, DwarfForm, const string&)); + MOCK_METHOD3(ProcessAttributeSignature, + void(DwarfAttribute, DwarfForm, uint64_t)); + MOCK_METHOD0(EndAttributes, bool()); + MOCK_METHOD2(FindChildHandler, DIEHandler *(uint64_t, DwarfTag)); + MOCK_METHOD0(Finish, void()); +}; + +class MockRootDIEHandler: public RootDIEHandler { + public: + MOCK_METHOD3(ProcessAttributeUnsigned, + void(DwarfAttribute, DwarfForm, uint64_t)); + MOCK_METHOD3(ProcessAttributeSigned, + void(DwarfAttribute, DwarfForm, int64_t)); + MOCK_METHOD3(ProcessAttributeReference, + void(DwarfAttribute, DwarfForm, uint64_t)); + MOCK_METHOD4(ProcessAttributeBuffer, + void(DwarfAttribute, DwarfForm, const uint8_t*, uint64_t)); + MOCK_METHOD3(ProcessAttributeString, + void(DwarfAttribute, DwarfForm, const string&)); + MOCK_METHOD3(ProcessAttributeSignature, + void(DwarfAttribute, DwarfForm, uint64_t)); + MOCK_METHOD0(EndAttributes, bool()); + MOCK_METHOD2(FindChildHandler, DIEHandler *(uint64_t, DwarfTag)); + MOCK_METHOD0(Finish, void()); + MOCK_METHOD5(StartCompilationUnit, bool(uint64_t, uint8_t, uint8_t, uint64_t, + uint8_t)); + MOCK_METHOD2(StartRootDIE, bool(uint64_t, 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/shared/sentry/external/breakpad/src/common/dwarf/dwarf2enums.h b/shared/sentry/external/breakpad/src/common/dwarf/dwarf2enums.h new file mode 100644 index 000000000..7d84f35e2 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/dwarf/dwarf2enums.h @@ -0,0 +1,744 @@ +// -*- 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 google_breakpad { + +// 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, + // DWARF 4. + DW_TAG_type_unit = 0x41, + // DWARF 5. + DW_TAG_skeleton_unit = 0x4a, + // 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 DwarfUnitHeader { + DW_UT_compile = 0x01, + DW_UT_type = 0x02, + DW_UT_partial = 0x03, + DW_UT_skeleton = 0x04, + DW_UT_split_compile = 0x05, + DW_UT_split_type = 0x06, + DW_UT_lo_user = 0x80, + DW_UT_hi_user = 0xFF +}; + +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, + + // Added in DWARF 5: + DW_FORM_strx = 0x1a, + DW_FORM_addrx = 0x1b, + DW_FORM_ref_sup4 = 0x1c, + DW_FORM_strp_sup = 0x1d, + DW_FORM_data16 = 0x1e, + DW_FORM_line_strp = 0x1f, + + // DWARF 4, but value out of order. + DW_FORM_ref_sig8 = 0x20, + + // Added in DWARF 5: + DW_FORM_implicit_const = 0x21, + DW_FORM_loclistx = 0x22, + DW_FORM_rnglistx = 0x23, + DW_FORM_ref_sup8 = 0x24, + DW_FORM_strx1 = 0x25, + DW_FORM_strx2 = 0x26, + DW_FORM_strx3 = 0x27, + DW_FORM_strx4 = 0x28, + + DW_FORM_addrx1 = 0x29, + DW_FORM_addrx2 = 0x2a, + DW_FORM_addrx3 = 0x2b, + DW_FORM_addrx4 = 0x2c, + + // 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, + // DWARF 5 + DW_AT_str_offsets_base = 0x72, + DW_AT_addr_base = 0x73, + DW_AT_rnglists_base = 0x74, + DW_AT_dwo_name = 0x76, + // 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 +}; + +// .debug_rngslist entry types +enum DwarfRngListEntry { + DW_RLE_end_of_list = 0, + DW_RLE_base_addressx = 1, + DW_RLE_startx_endx = 2, + DW_RLE_startx_length = 3, + DW_RLE_offset_pair = 4, + DW_RLE_base_address = 5, + DW_RLE_start_end = 6, + DW_RLE_start_length = 7, +}; + +// Line number content type codes (DWARF 5). +enum DwarfLineNumberContentType { + DW_LNCT_path = 1, + DW_LNCT_directory_index = 2, + DW_LNCT_timestamp = 3, + DW_LNCT_size = 4, + DW_LNCT_MD5 = 5, +}; + +// 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 google_breakpad +#endif // COMMON_DWARF_DWARF2ENUMS_H__ diff --git a/shared/sentry/external/breakpad/src/common/dwarf/dwarf2reader.cc b/shared/sentry/external/breakpad/src/common/dwarf/dwarf2reader.cc new file mode 100644 index 000000000..bf6758d8a --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/dwarf/dwarf2reader.cc @@ -0,0 +1,3423 @@ +// 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 + +// Implementation of LineInfo, CompilationUnit, +// and CallFrameInfo. See dwarf2reader.h for details. + +#include "common/dwarf/dwarf2reader.h" + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#include "common/dwarf/bytereader-inl.h" +#include "common/dwarf/bytereader.h" +#include "common/dwarf/line_state_machine.h" +#include "common/using_std_string.h" +#include "google_breakpad/common/breakpad_types.h" + +namespace google_breakpad { + +const SectionMap::const_iterator GetSectionByName(const SectionMap& + sections, const char *name) { + assert(name[0] == '.'); + auto iter = sections.find(name); + if (iter != sections.end()) + return iter; + std::string macho_name("__"); + macho_name += name + 1; + iter = sections.find(macho_name); + return iter; +} + +CompilationUnit::CompilationUnit(const string& path, + const SectionMap& sections, uint64_t 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), + line_string_buffer_(NULL), line_string_buffer_length_(0), + str_offsets_buffer_(NULL), str_offsets_buffer_length_(0), + addr_buffer_(NULL), addr_buffer_length_(0), + is_split_dwarf_(false), is_type_unit_(false), dwo_id_(0), dwo_name_(), + skeleton_dwo_id_(0), ranges_base_(0), addr_base_(0), + str_offsets_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_t addr_buffer_length, + uint64_t addr_base, + uint64_t ranges_base, + uint64_t 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. + SectionMap::const_iterator iter = + GetSectionByName(sections_, ".debug_abbrev"); + assert(iter != sections_.end()); + + abbrevs_ = new std::vector; + 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_t abbrev_length = iter->second.second - header_.abbrev_offset; +#endif + + while (1) { + CompilationUnit::Abbrev abbrev; + size_t len; + const uint64_t number = reader_->ReadUnsignedLEB128(abbrevptr, &len); + + if (number == 0) + break; + abbrev.number = number; + abbrevptr += len; + + assert(abbrevptr < abbrev_start + abbrev_length); + const uint64_t tag = reader_->ReadUnsignedLEB128(abbrevptr, &len); + abbrevptr += len; + abbrev.tag = static_cast(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_t nametemp = reader_->ReadUnsignedLEB128(abbrevptr, &len); + abbrevptr += len; + + assert(abbrevptr < abbrev_start + abbrev_length); + const uint64_t formtemp = reader_->ReadUnsignedLEB128(abbrevptr, &len); + abbrevptr += len; + if (nametemp == 0 && formtemp == 0) + break; + + uint64_t value = 0; + if (formtemp == DW_FORM_implicit_const) { + value = reader_->ReadUnsignedLEB128(abbrevptr, &len); + abbrevptr += len; + } + AttrForm abbrev_attr(static_cast(nametemp), + static_cast(formtemp), + value); + abbrev.attributes.push_back(abbrev_attr); + } + 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->form_); + } + 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(reader_->ReadUnsignedLEB128(start, + &len)); + start += len; + return SkipAttribute(start, form); + + case DW_FORM_flag_present: + case DW_FORM_implicit_const: + return start; + case DW_FORM_addrx1: + case DW_FORM_data1: + case DW_FORM_flag: + case DW_FORM_ref1: + case DW_FORM_strx1: + return start + 1; + case DW_FORM_addrx2: + case DW_FORM_ref2: + case DW_FORM_data2: + case DW_FORM_strx2: + return start + 2; + case DW_FORM_addrx3: + case DW_FORM_strx3: + return start + 3; + case DW_FORM_addrx4: + case DW_FORM_ref4: + case DW_FORM_data4: + case DW_FORM_strx4: + case DW_FORM_ref_sup4: + return start + 4; + case DW_FORM_ref8: + case DW_FORM_data8: + case DW_FORM_ref_sig8: + case DW_FORM_ref_sup8: + return start + 8; + case DW_FORM_data16: + return start + 16; + case DW_FORM_string: + return start + strlen(reinterpret_cast(start)) + 1; + case DW_FORM_udata: + case DW_FORM_ref_udata: + case DW_FORM_strx: + case DW_FORM_GNU_str_index: + case DW_FORM_GNU_addr_index: + case DW_FORM_addrx: + case DW_FORM_rnglistx: + case DW_FORM_loclistx: + 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_t size = reader_->ReadUnsignedLEB128(start, &len); + return start + size + len; + } + case DW_FORM_strp: + case DW_FORM_line_strp: + case DW_FORM_strp_sup: + case DW_FORM_sec_offset: + return start + reader_->OffsetSize(); + } + fprintf(stderr,"Unhandled form type"); + return NULL; +} + +// Read the abbreviation offset from a compilation unit header. +size_t CompilationUnit::ReadAbbrevOffset(const uint8_t* headerptr) { + assert(headerptr + reader_->OffsetSize() < buffer_ + buffer_length_); + header_.abbrev_offset = reader_->ReadOffset(headerptr); + return reader_->OffsetSize(); +} + +// Read the address size from a compilation unit header. +size_t CompilationUnit::ReadAddressSize(const uint8_t* headerptr) { + // 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); + return 1; +} + +// Read the DWO id from a split or skeleton compilation unit header. +size_t CompilationUnit::ReadDwoId(const uint8_t* headerptr) { + assert(headerptr + 8 <= buffer_ + buffer_length_); + dwo_id_ = reader_->ReadEightBytes(headerptr); + return 8; +} + +// Read the type signature from a type or split type compilation unit header. +size_t CompilationUnit::ReadTypeSignature(const uint8_t* headerptr) { + assert(headerptr + 8 <= buffer_ + buffer_length_); + type_signature_ = reader_->ReadEightBytes(headerptr); + return 8; +} + +// Read the DWO id from a split or skeleton compilation unit header. +size_t CompilationUnit::ReadTypeOffset(const uint8_t* headerptr) { + assert(headerptr + reader_->OffsetSize() < buffer_ + buffer_length_); + type_offset_ = reader_->ReadOffset(headerptr); + return reader_->OffsetSize(); +} + + +// Read a DWARF header. The header is variable length in DWARF3 and DWARF4 +// (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. DWARF5 adds a unit_type to distinguish +// between partial-, full-, skeleton-, split-, and type- compilation units. +void CompilationUnit::ReadHeader() { + const uint8_t* headerptr = buffer_; + size_t initial_length_size; + + assert(headerptr + 4 < buffer_ + buffer_length_); + const uint64_t 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; + + if (header_.version <= 4) { + // Older versions of dwarf have a relatively simple structure. + headerptr += ReadAbbrevOffset(headerptr); + headerptr += ReadAddressSize(headerptr); + } else { + // DWARF5 adds a unit_type field, and various fields based on unit_type. + assert(headerptr + 1 < buffer_ + buffer_length_); + uint8_t unit_type = reader_->ReadOneByte(headerptr); + headerptr += 1; + headerptr += ReadAddressSize(headerptr); + headerptr += ReadAbbrevOffset(headerptr); + switch (unit_type) { + case DW_UT_compile: + case DW_UT_partial: + // nothing else to read + break; + case DW_UT_skeleton: + case DW_UT_split_compile: + headerptr += ReadDwoId(headerptr); + break; + case DW_UT_type: + case DW_UT_split_type: + is_type_unit_ = true; + headerptr += ReadTypeSignature(headerptr); + headerptr += ReadTypeOffset(headerptr); + break; + default: + fprintf(stderr, "Unhandled compilation unit type 0x%x", unit_type); + break; + } + } + 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_t CompilationUnit::Start() { + // First get the debug_info section. + SectionMap::const_iterator iter = + GetSectionByName(sections_, ".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_t 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; + else if (header_.version == 5 && is_type_unit_) + return ourlength; + + // Otherwise, continue by reading our abbreviation entries. + ReadAbbrevs(); + + // Set the string section if we have one. + iter = GetSectionByName(sections_, ".debug_str"); + if (iter != sections_.end()) { + string_buffer_ = iter->second.first; + string_buffer_length_ = iter->second.second; + } + + // Set the line string section if we have one. + iter = GetSectionByName(sections_, ".debug_line_str"); + if (iter != sections_.end()) { + line_string_buffer_ = iter->second.first; + line_string_buffer_length_ = iter->second.second; + } + + // Set the string offsets section if we have one. + iter = GetSectionByName(sections_, ".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 = GetSectionByName(sections_, ".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; +} + +void CompilationUnit::ProcessFormStringIndex( + uint64_t dieoffset, enum DwarfAttribute attr, enum DwarfForm form, + uint64_t str_index) { + const size_t kStringOffsetsTableHeaderSize = + header_.version >= 5 ? (reader_->OffsetSize() == 8 ? 16 : 8) : 0; + const uint8_t* str_offsets_table_after_header = str_offsets_base_ ? + str_offsets_buffer_ + str_offsets_base_ : + str_offsets_buffer_ + kStringOffsetsTableHeaderSize; + const uint8_t* offset_ptr = + str_offsets_table_after_header + str_index * reader_->OffsetSize(); + + const uint64_t offset = reader_->ReadOffset(offset_ptr); + if (offset >= string_buffer_length_) { + return; + } + + const char* str = reinterpret_cast(string_buffer_) + offset; + ProcessAttributeString(dieoffset, attr, form, str); +} + +// Special function for pre-processing the +// DW_AT_str_offsets_base and DW_AT_addr_base in a DW_TAG_compile_unit die (for +// DWARF v5). We must make sure to find and process the +// DW_AT_str_offsets_base and DW_AT_addr_base attributes before attempting to +// read any string and address attribute in the compile unit. +const uint8_t* CompilationUnit::ProcessOffsetBaseAttribute( + uint64_t dieoffset, const uint8_t* start, enum DwarfAttribute attr, + enum DwarfForm form, uint64_t implicit_const) { + 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(reader_->ReadUnsignedLEB128(start, + &len)); + start += len; + return ProcessOffsetBaseAttribute(dieoffset, start, attr, form, + implicit_const); + + case DW_FORM_flag_present: + return start; + case DW_FORM_data1: + case DW_FORM_flag: + return start + 1; + case DW_FORM_data2: + return start + 2; + case DW_FORM_data4: + return start + 4; + case DW_FORM_data8: + return start + 8; + case DW_FORM_data16: + // This form is designed for an md5 checksum inside line tables. + return start + 16; + case DW_FORM_string: { + const char* str = reinterpret_cast(start); + return start + strlen(str) + 1; + } + case DW_FORM_udata: + reader_->ReadUnsignedLEB128(start, &len); + return start + len; + case DW_FORM_sdata: + reader_->ReadSignedLEB128(start, &len); + return start + len; + case DW_FORM_addr: + reader_->ReadAddress(start); + return start + reader_->AddressSize(); + + // This is the important one here! + case DW_FORM_sec_offset: + if (attr == DW_AT_str_offsets_base || + attr == DW_AT_addr_base) + ProcessAttributeUnsigned(dieoffset, attr, form, + reader_->ReadOffset(start)); + else + reader_->ReadOffset(start); + return start + reader_->OffsetSize(); + + case DW_FORM_ref1: + return start + 1; + case DW_FORM_ref2: + return start + 2; + case DW_FORM_ref4: + return start + 4; + case DW_FORM_ref8: + return start + 8; + case DW_FORM_ref_udata: + reader_->ReadUnsignedLEB128(start, &len); + 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) { + reader_->ReadAddress(start); + return start + reader_->AddressSize(); + } else if (header_.version >= 3) { + reader_->ReadOffset(start); + return start + reader_->OffsetSize(); + } + break; + case DW_FORM_ref_sig8: + return start + 8; + case DW_FORM_implicit_const: + return start; + case DW_FORM_block1: { + uint64_t datalen = reader_->ReadOneByte(start); + return start + 1 + datalen; + } + case DW_FORM_block2: { + uint64_t datalen = reader_->ReadTwoBytes(start); + return start + 2 + datalen; + } + case DW_FORM_block4: { + uint64_t datalen = reader_->ReadFourBytes(start); + return start + 4 + datalen; + } + case DW_FORM_block: + case DW_FORM_exprloc: { + uint64_t datalen = reader_->ReadUnsignedLEB128(start, &len); + return start + datalen + len; + } + case DW_FORM_strp: { + reader_->ReadOffset(start); + return start + reader_->OffsetSize(); + } + case DW_FORM_line_strp: { + reader_->ReadOffset(start); + return start + reader_->OffsetSize(); + } + case DW_FORM_strp_sup: + return start + 4; + case DW_FORM_ref_sup4: + return start + 4; + case DW_FORM_ref_sup8: + return start + 8; + case DW_FORM_loclistx: + reader_->ReadUnsignedLEB128(start, &len); + return start + len; + case DW_FORM_strx: + case DW_FORM_GNU_str_index: { + reader_->ReadUnsignedLEB128(start, &len); + return start + len; + } + case DW_FORM_strx1: { + return start + 1; + } + case DW_FORM_strx2: { + return start + 2; + } + case DW_FORM_strx3: { + return start + 3; + } + case DW_FORM_strx4: { + return start + 4; + } + + case DW_FORM_addrx: + case DW_FORM_GNU_addr_index: + reader_->ReadUnsignedLEB128(start, &len); + return start + len; + case DW_FORM_addrx1: + return start + 1; + case DW_FORM_addrx2: + return start + 2; + case DW_FORM_addrx3: + return start + 3; + case DW_FORM_addrx4: + return start + 4; + case DW_FORM_rnglistx: + reader_->ReadUnsignedLEB128(start, &len); + return start + len; + } + fprintf(stderr, "Unhandled form type\n"); + return NULL; +} + +// 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_t dieoffset, const uint8_t* start, enum DwarfAttribute attr, + enum DwarfForm form, uint64_t implicit_const) { + 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(reader_->ReadUnsignedLEB128(start, + &len)); + start += len; + return ProcessAttribute(dieoffset, start, attr, form, implicit_const); + + 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_data16: + // This form is designed for an md5 checksum inside line tables. + fprintf(stderr, "Unhandled form type: DW_FORM_data16\n"); + return start + 16; + case DW_FORM_string: { + const char* str = reinterpret_cast(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_implicit_const: + handler_->ProcessAttributeUnsigned(dieoffset, attr, form, + implicit_const); + return start; + case DW_FORM_block1: { + uint64_t datalen = reader_->ReadOneByte(start); + handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 1, + datalen); + return start + 1 + datalen; + } + case DW_FORM_block2: { + uint64_t datalen = reader_->ReadTwoBytes(start); + handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 2, + datalen); + return start + 2 + datalen; + } + case DW_FORM_block4: { + uint64_t 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_t 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_t offset = reader_->ReadOffset(start); + assert(string_buffer_ + offset < string_buffer_ + string_buffer_length_); + + const char* str = reinterpret_cast(string_buffer_ + offset); + ProcessAttributeString(dieoffset, attr, form, str); + return start + reader_->OffsetSize(); + } + case DW_FORM_line_strp: { + assert(line_string_buffer_ != NULL); + + const uint64_t offset = reader_->ReadOffset(start); + assert(line_string_buffer_ + offset < + line_string_buffer_ + line_string_buffer_length_); + + const char* str = + reinterpret_cast(line_string_buffer_ + offset); + ProcessAttributeString(dieoffset, attr, form, str); + return start + reader_->OffsetSize(); + } + case DW_FORM_strp_sup: + // No support currently for suplementary object files. + fprintf(stderr, "Unhandled form type: DW_FORM_strp_sup\n"); + return start + 4; + case DW_FORM_ref_sup4: + // No support currently for suplementary object files. + fprintf(stderr, "Unhandled form type: DW_FORM_ref_sup4\n"); + return start + 4; + case DW_FORM_ref_sup8: + // No support currently for suplementary object files. + fprintf(stderr, "Unhandled form type: DW_FORM_ref_sup8\n"); + return start + 8; + case DW_FORM_loclistx: + ProcessAttributeUnsigned(dieoffset, attr, form, + reader_->ReadUnsignedLEB128(start, &len)); + return start + len; + case DW_FORM_strx: + case DW_FORM_GNU_str_index: { + uint64_t str_index = reader_->ReadUnsignedLEB128(start, &len); + ProcessFormStringIndex(dieoffset, attr, form, str_index); + return start + len; + } + case DW_FORM_strx1: { + uint64_t str_index = reader_->ReadOneByte(start); + ProcessFormStringIndex(dieoffset, attr, form, str_index); + return start + 1; + } + case DW_FORM_strx2: { + uint64_t str_index = reader_->ReadTwoBytes(start); + ProcessFormStringIndex(dieoffset, attr, form, str_index); + return start + 2; + } + case DW_FORM_strx3: { + uint64_t str_index = reader_->ReadThreeBytes(start); + ProcessFormStringIndex(dieoffset, attr, form, str_index); + return start + 3; + } + case DW_FORM_strx4: { + uint64_t str_index = reader_->ReadFourBytes(start); + ProcessFormStringIndex(dieoffset, attr, form, str_index); + return start + 4; + } + + case DW_FORM_addrx: + case DW_FORM_GNU_addr_index: + ProcessAttributeAddrIndex( + dieoffset, attr, form, reader_->ReadUnsignedLEB128(start, &len)); + return start + len; + case DW_FORM_addrx1: + ProcessAttributeAddrIndex( + dieoffset, attr, form, reader_->ReadOneByte(start)); + return start + 1; + case DW_FORM_addrx2: + ProcessAttributeAddrIndex( + dieoffset, attr, form, reader_->ReadTwoBytes(start)); + return start + 2; + case DW_FORM_addrx3: + ProcessAttributeAddrIndex( + dieoffset, attr, form, reader_->ReadThreeBytes(start)); + return start + 3; + case DW_FORM_addrx4: + ProcessAttributeAddrIndex( + dieoffset, attr, form, reader_->ReadFourBytes(start)); + return start + 4; + case DW_FORM_rnglistx: + ProcessAttributeUnsigned( + dieoffset, attr, form, reader_->ReadUnsignedLEB128(start, &len)); + return start + len; + } + fprintf(stderr, "Unhandled form type\n"); + return NULL; +} + +const uint8_t* CompilationUnit::ProcessDIE(uint64_t dieoffset, + const uint8_t* start, + const Abbrev& abbrev) { + // With DWARF v5, the compile_unit die may contain a + // DW_AT_str_offsets_base or DW_AT_addr_base. If it does, that attribute must + // be found and processed before trying to process the other attributes; + // otherwise the string or address values will all come out incorrect. + if (abbrev.tag == DW_TAG_compile_unit && header_.version == 5) { + uint64_t dieoffset_copy = dieoffset; + const uint8_t* start_copy = start; + for (AttributeList::const_iterator i = abbrev.attributes.begin(); + i != abbrev.attributes.end(); + i++) { + start_copy = ProcessOffsetBaseAttribute(dieoffset_copy, start_copy, + i->attr_, i->form_, + i->value_); + } + } + + for (AttributeList::const_iterator i = abbrev.attributes.begin(); + i != abbrev.attributes.end(); + i++) { + start = ProcessAttribute(dieoffset, start, i->attr_, i->form_, i->value_); + } + + // 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 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_t absolute_offset = (dieptr - buffer_) + offset_from_section_start_; + + uint64_t 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_t offset = die_stack.top(); + die_stack.pop(); + handler_->EndDIE(offset); + continue; + } + + const Abbrev& abbrev = abbrevs_->at(static_cast(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(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(cu_index_)); + + if (version_ == 1) { + nslots_ = byte_reader_.ReadFourBytes( + reinterpret_cast(cu_index_) + + 3 * sizeof(uint32_t)); + phash_ = cu_index_ + 4 * sizeof(uint32_t); + pindex_ = phash_ + nslots_ * sizeof(uint64_t); + shndx_pool_ = pindex_ + nslots_ * sizeof(uint32_t); + if (shndx_pool_ >= cu_index_ + cu_index_size_) { + version_ = 0; + } + } else if (version_ == 2 || version_ == 5) { + ncolumns_ = byte_reader_.ReadFourBytes( + reinterpret_cast(cu_index_) + sizeof(uint32_t)); + nunits_ = byte_reader_.ReadFourBytes( + reinterpret_cast(cu_index_) + 2 * sizeof(uint32_t)); + nslots_ = byte_reader_.ReadFourBytes( + reinterpret_cast(cu_index_) + 3 * sizeof(uint32_t)); + phash_ = cu_index_ + 4 * sizeof(uint32_t); + pindex_ = phash_ + nslots_ * sizeof(uint64_t); + offset_table_ = pindex_ + nslots_ * sizeof(uint32_t); + size_table_ = offset_table_ + ncolumns_ * (nunits_ + 1) * sizeof(uint32_t); + 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_t 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(pindex_) + + slot * sizeof(uint32_t)); + const char* shndx_list = shndx_pool_ + index * sizeof(uint32_t); + for (;;) { + if (shndx_list >= cu_index_ + cu_index_size_) { + version_ = 0; + return; + } + unsigned int shndx = byte_reader_.ReadFourBytes( + reinterpret_cast(shndx_list)); + shndx_list += sizeof(uint32_t); + 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 (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 (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 (section_data), + section_size))); + } + } + sections->insert(std::make_pair( + ".debug_str", + std::make_pair(reinterpret_cast (string_buffer_), + string_buffer_size_))); + } else if (version_ == 2 || version_ == 5) { + uint32_t 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_t); + const char* size_row = + size_table_ + (index - 1) * ncolumns_ * sizeof(uint32_t); + if (size_row + ncolumns_ * sizeof(uint32_t) > cu_index_ + cu_index_size_) { + version_ = 0; + return; + } + for (unsigned int col = 0u; col < ncolumns_; ++col) { + uint32_t section_id = + byte_reader_.ReadFourBytes(reinterpret_cast(id_row) + + col * sizeof(uint32_t)); + uint32_t offset = byte_reader_.ReadFourBytes( + reinterpret_cast(offset_row) + + col * sizeof(uint32_t)); + uint32_t size = byte_reader_.ReadFourBytes( + reinterpret_cast(size_row) + col * sizeof(uint32_t)); + if (section_id == DW_SECT_ABBREV) { + sections->insert(std::make_pair( + ".debug_abbrev", + std::make_pair(reinterpret_cast (abbrev_data_) + + offset, size))); + } else if (section_id == DW_SECT_INFO) { + sections->insert(std::make_pair( + ".debug_info", + std::make_pair(reinterpret_cast (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 (str_offsets_data_) + + offset, size))); + } + } + sections->insert(std::make_pair( + ".debug_str", + std::make_pair(reinterpret_cast (string_buffer_), + string_buffer_size_))); + } +} + +int DwpReader::LookupCU(uint64_t dwo_id) { + uint32_t slot = static_cast(dwo_id) & (nslots_ - 1); + uint64_t probe = byte_reader_.ReadEightBytes( + reinterpret_cast(phash_) + slot * sizeof(uint64_t)); + if (probe != 0 && probe != dwo_id) { + uint32_t secondary_hash = + (static_cast(dwo_id >> 32) & (nslots_ - 1)) | 1; + do { + slot = (slot + secondary_hash) & (nslots_ - 1); + probe = byte_reader_.ReadEightBytes( + reinterpret_cast(phash_) + slot * sizeof(uint64_t)); + } while (probe != 0 && probe != dwo_id); + } + if (probe == 0) + return -1; + return slot; +} + +uint32_t DwpReader::LookupCUv2(uint64_t dwo_id) { + uint32_t slot = static_cast(dwo_id) & (nslots_ - 1); + uint64_t probe = byte_reader_.ReadEightBytes( + reinterpret_cast(phash_) + slot * sizeof(uint64_t)); + uint32_t index = byte_reader_.ReadFourBytes( + reinterpret_cast(pindex_) + slot * sizeof(uint32_t)); + if (index != 0 && probe != dwo_id) { + uint32_t secondary_hash = + (static_cast(dwo_id >> 32) & (nslots_ - 1)) | 1; + do { + slot = (slot + secondary_hash) & (nslots_ - 1); + probe = byte_reader_.ReadEightBytes( + reinterpret_cast(phash_) + slot * sizeof(uint64_t)); + index = byte_reader_.ReadFourBytes( + reinterpret_cast(pindex_) + slot * sizeof(uint32_t)); + } while (index != 0 && probe != dwo_id); + } + return index; +} + +LineInfo::LineInfo(const uint8_t* buffer, uint64_t buffer_length, + ByteReader* reader, const uint8_t* string_buffer, + size_t string_buffer_length, + const uint8_t* line_string_buffer, + size_t line_string_buffer_length, LineInfoHandler* handler): + handler_(handler), reader_(reader), buffer_(buffer), + string_buffer_(string_buffer), + line_string_buffer_(line_string_buffer) { +#ifndef NDEBUG + buffer_length_ = buffer_length; + string_buffer_length_ = string_buffer_length; + line_string_buffer_length_ = line_string_buffer_length; +#endif + header_.std_opcode_lengths = NULL; +} + +uint64_t LineInfo::Start() { + ReadHeader(); + ReadLines(); + return after_header_ - buffer_; +} + +void LineInfo::ReadTypesAndForms(const uint8_t** lineptr, + uint32_t* content_types, + uint32_t* content_forms, + uint32_t max_types, + uint32_t* format_count) { + size_t len; + + uint32_t count = reader_->ReadUnsignedLEB128(*lineptr, &len); + *lineptr += len; + if (count < 1 || count > max_types) { + return; + } + for (uint32_t col = 0; col < count; ++col) { + content_types[col] = reader_->ReadUnsignedLEB128(*lineptr, &len); + *lineptr += len; + content_forms[col] = reader_->ReadUnsignedLEB128(*lineptr, &len); + *lineptr += len; + } + *format_count = count; +} + +const char* LineInfo::ReadStringForm(uint32_t form, const uint8_t** lineptr) { + const char* name = nullptr; + if (form == DW_FORM_string) { + name = reinterpret_cast(*lineptr); + *lineptr += strlen(name) + 1; + return name; + } else if (form == DW_FORM_strp) { + uint64_t offset = reader_->ReadOffset(*lineptr); + assert(offset < string_buffer_length_); + *lineptr += reader_->OffsetSize(); + if (string_buffer_ != nullptr) { + name = reinterpret_cast(string_buffer_) + offset; + return name; + } + } else if (form == DW_FORM_line_strp) { + uint64_t offset = reader_->ReadOffset(*lineptr); + assert(offset < line_string_buffer_length_); + *lineptr += reader_->OffsetSize(); + if (line_string_buffer_ != nullptr) { + name = reinterpret_cast(line_string_buffer_) + offset; + return name; + } + } + // Shouldn't be called with a non-string-form, and + // if there is a string form but no string buffer, + // that is a problem too. + assert(0); + return nullptr; +} + +uint64_t LineInfo::ReadUnsignedData(uint32_t form, const uint8_t** lineptr) { + size_t len; + uint64_t value; + + switch (form) { + case DW_FORM_data1: + value = reader_->ReadOneByte(*lineptr); + *lineptr += 1; + return value; + case DW_FORM_data2: + value = reader_->ReadTwoBytes(*lineptr); + *lineptr += 2; + return value; + case DW_FORM_data4: + value = reader_->ReadFourBytes(*lineptr); + *lineptr += 4; + return value; + case DW_FORM_data8: + value = reader_->ReadEightBytes(*lineptr); + *lineptr += 8; + return value; + case DW_FORM_udata: + value = reader_->ReadUnsignedLEB128(*lineptr, &len); + *lineptr += len; + return value; + default: + fprintf(stderr, "Unrecognized data form."); + return 0; + } +} + +void LineInfo::ReadFileRow(const uint8_t** lineptr, + const uint32_t* content_types, + const uint32_t* content_forms, uint32_t row, + uint32_t format_count) { + const char* filename = nullptr; + uint64_t dirindex = 0; + uint64_t mod_time = 0; + uint64_t filelength = 0; + + for (uint32_t col = 0; col < format_count; ++col) { + switch (content_types[col]) { + case DW_LNCT_path: + filename = ReadStringForm(content_forms[col], lineptr); + break; + case DW_LNCT_directory_index: + dirindex = ReadUnsignedData(content_forms[col], lineptr); + break; + case DW_LNCT_timestamp: + mod_time = ReadUnsignedData(content_forms[col], lineptr); + break; + case DW_LNCT_size: + filelength = ReadUnsignedData(content_forms[col], lineptr); + break; + case DW_LNCT_MD5: + // MD5 entries help a debugger sort different versions of files with + // the same name. It is always paired with a DW_FORM_data16 and is + // unused in this case. + *lineptr += 16; + break; + default: + fprintf(stderr, "Unrecognized form in line table header. %d\n", + content_types[col]); + assert(false); + break; + } + } + assert(filename != nullptr); + handler_->DefineFile(filename, row, dirindex, mod_time, filelength); +} + +// 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_t 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_); + + + header_.version = reader_->ReadTwoBytes(lineptr); + lineptr += 2; + + if (header_.version >= 5) { + uint8_t address_size = reader_->ReadOneByte(lineptr); + reader_->SetAddressSize(address_size); + lineptr += 1; + uint8_t segment_selector_size = reader_->ReadOneByte(lineptr); + if (segment_selector_size != 0) { + fprintf(stderr,"No support for segmented memory."); + } + lineptr += 1; + } else { + // Address size *must* be set by CU ahead of time. + assert(reader_->AddressSize() != 0); + } + + 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_t 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(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; + 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; + } + + if (header_.version <= 4) { + // Directory zero is assumed to be the compilation directory and special + // cased where used. It is not actually stored in the dwarf data. But an + // empty entry here avoids off-by-one errors elsewhere in the code. + handler_->DefineDir("", 0); + // It is legal for the directory entry table to be empty. + if (*lineptr) { + uint32_t dirindex = 1; + while (*lineptr) { + const char* dirname = reinterpret_cast(lineptr); + handler_->DefineDir(dirname, dirindex); + lineptr += strlen(dirname) + 1; + dirindex++; + } + } + lineptr++; + // It is also legal for the file entry table to be empty. + + // Similarly for file zero. + handler_->DefineFile("", 0, 0, 0, 0); + if (*lineptr) { + uint32_t fileindex = 1; + size_t len; + while (*lineptr) { + const char* filename = ReadStringForm(DW_FORM_string, &lineptr); + + uint64_t dirindex = reader_->ReadUnsignedLEB128(lineptr, &len); + lineptr += len; + + uint64_t mod_time = reader_->ReadUnsignedLEB128(lineptr, &len); + lineptr += len; + + uint64_t filelength = reader_->ReadUnsignedLEB128(lineptr, &len); + lineptr += len; + handler_->DefineFile(filename, fileindex, + static_cast(dirindex), mod_time, + filelength); + fileindex++; + } + } + lineptr++; + } else { + // Read the DWARF-5 directory table. + + // Dwarf5 supports five different types and forms per directory- and + // file-table entry. Theoretically, there could be duplicate entries + // in this table, but that would be quite unusual. + static const uint32_t kMaxTypesAndForms = 5; + uint32_t content_types[kMaxTypesAndForms]; + uint32_t content_forms[kMaxTypesAndForms]; + uint32_t format_count; + size_t len; + + ReadTypesAndForms(&lineptr, content_types, content_forms, kMaxTypesAndForms, + &format_count); + uint32_t entry_count = reader_->ReadUnsignedLEB128(lineptr, &len); + lineptr += len; + for (uint32_t row = 0; row < entry_count; ++row) { + const char* dirname = nullptr; + for (uint32_t col = 0; col < format_count; ++col) { + // The path is the only relevant content type for this implementation. + if (content_types[col] == DW_LNCT_path) { + dirname = ReadStringForm(content_forms[col], &lineptr); + } + } + handler_->DefineDir(dirname, row); + } + + // Read the DWARF-5 filename table. + ReadTypesAndForms(&lineptr, content_types, content_forms, kMaxTypesAndForms, + &format_count); + entry_count = reader_->ReadUnsignedLEB128(lineptr, &len); + lineptr += len; + + for (uint32_t row = 0; row < entry_count; ++row) { + ReadFileRow(&lineptr, content_types, content_forms, row, format_count); + } + } + 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_t 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_t advance_address = (opcode / header.line_range) + * header.min_insn_length; + const int32_t 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_t 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_t advance_line = reader->ReadSignedLEB128(start, &templen); + oplen += templen; + lsm->line_num += static_cast(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_t fileno = reader->ReadUnsignedLEB128(start, &templen); + oplen += templen; + lsm->file_num = static_cast(fileno); + } + break; + case DW_LNS_set_column: { + const uint64_t colno = reader->ReadUnsignedLEB128(start, &templen); + oplen += templen; + lsm->column_num = static_cast(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_t 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_t 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_t extended_op_len = reader->ReadUnsignedLEB128(start, + &templen); + start += templen; + oplen += templen + extended_op_len; + + const uint64_t 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_t address = reader->ReadAddress(start); + lsm->address = address; + } + break; + case DW_LNE_define_file: { + const char* filename = reinterpret_cast(start); + + templen = strlen(filename) + 1; + start += templen; + + uint64_t dirindex = reader->ReadUnsignedLEB128(start, &templen); + oplen += templen; + + const uint64_t mod_time = reader->ReadUnsignedLEB128(start, + &templen); + oplen += templen; + + const uint64_t filelength = reader->ReadUnsignedLEB128(start, + &templen); + oplen += templen; + + if (handler) { + handler->DefineFile(filename, -1, static_cast(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_t pending_address = 0; + uint32_t 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; +} + +bool RangeListReader::ReadRanges(enum DwarfForm form, uint64_t data) { + if (form == DW_FORM_sec_offset) { + if (cu_info_->version_ <= 4) { + return ReadDebugRanges(data); + } else { + return ReadDebugRngList(data); + } + } else if (form == DW_FORM_rnglistx) { + offset_array_ = cu_info_->ranges_base_; + uint64_t index_offset = reader_->OffsetSize() * data; + uint64_t range_list_offset = + reader_->ReadOffset(cu_info_->buffer_ + offset_array_ + index_offset); + + return ReadDebugRngList(offset_array_ + range_list_offset); + } + return false; +} + +bool RangeListReader::ReadDebugRanges(uint64_t offset) { + const uint64_t max_address = + (reader_->AddressSize() == 4) ? 0xffffffffUL + : 0xffffffffffffffffULL; + const uint64_t entry_size = reader_->AddressSize() * 2; + bool list_end = false; + + do { + if (offset > cu_info_->size_ - entry_size) { + return false; // Invalid range detected + } + + uint64_t start_address = reader_->ReadAddress(cu_info_->buffer_ + offset); + uint64_t end_address = reader_->ReadAddress( + cu_info_->buffer_ + offset + reader_->AddressSize()); + + if (start_address == max_address) { // Base address selection + cu_info_->base_address_ = 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 + cu_info_->base_address_, + end_address + cu_info_->base_address_); + } + + offset += entry_size; + } while (!list_end); + + return true; +} + +bool RangeListReader::ReadDebugRngList(uint64_t offset) { + uint64_t start = 0; + uint64_t end = 0; + uint64_t range_len = 0; + uint64_t index = 0; + // A uleb128's length isn't known until after it has been read, so overruns + // are only caught after an entire entry. + while (offset < cu_info_->size_) { + uint8_t entry_type = reader_->ReadOneByte(cu_info_->buffer_ + offset); + offset += 1; + // Handle each entry type per Dwarf 5 Standard, section 2.17.3. + switch (entry_type) { + case DW_RLE_end_of_list: + handler_->Finish(); + return true; + case DW_RLE_base_addressx: + offset += ReadULEB(offset, &index); + cu_info_->base_address_ = GetAddressAtIndex(index); + break; + case DW_RLE_startx_endx: + offset += ReadULEB(offset, &index); + start = GetAddressAtIndex(index); + offset += ReadULEB(offset, &index); + end = GetAddressAtIndex(index); + handler_->AddRange(start, end); + break; + case DW_RLE_startx_length: + offset += ReadULEB(offset, &index); + start = GetAddressAtIndex(index); + offset += ReadULEB(offset, &range_len); + handler_->AddRange(start, start + range_len); + break; + case DW_RLE_offset_pair: + offset += ReadULEB(offset, &start); + offset += ReadULEB(offset, &end); + handler_->AddRange(start + cu_info_->base_address_, + end + cu_info_->base_address_); + break; + case DW_RLE_base_address: + offset += ReadAddress(offset, &cu_info_->base_address_); + break; + case DW_RLE_start_end: + offset += ReadAddress(offset, &start); + offset += ReadAddress(offset, &end); + handler_->AddRange(start, end); + break; + case DW_RLE_start_length: + offset += ReadAddress(offset, &start); + offset += ReadULEB(offset, &end); + handler_->AddRange(start, start + end); + break; + } + } + return false; +} + +// 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_t 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_t 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(&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_t 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(&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_t 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(&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_t 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(&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_t 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(&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_t 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(&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_t 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(&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_t address, + const RuleMap& new_rules) const; + + private: + // A map from register numbers to Rules. + typedef std::map 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_t 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_t 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_t 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_t 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_t 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 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(*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(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_t 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(memchr(augmentation_start, '\0', + cie->end - augmentation_start)); + if (! augmentation_end) return ReportIncomplete(cie); + cursor = augmentation_end; + cie->augmentation = string(reinterpret_cast(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_t(*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_t offset, + CallFrameInfo::EntryKind kind) { + fprintf(stderr, + "%s: CFI %s at offset 0x%" PRIx64 " in '%s': entry ends early\n", + filename_.c_str(), CallFrameInfo::KindName(kind), offset, + section_.c_str()); +} + +void CallFrameInfo::Reporter::EarlyEHTerminator(uint64_t offset) { + fprintf(stderr, + "%s: CFI at offset 0x%" PRIx64 " 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_t offset, + uint64_t cie_offset) { + fprintf(stderr, + "%s: CFI frame description entry at offset 0x%" PRIx64 " in '%s':" + " CIE pointer is out of range: 0x%" PRIx64 "\n", + filename_.c_str(), offset, section_.c_str(), cie_offset); +} + +void CallFrameInfo::Reporter::BadCIEId(uint64_t offset, uint64_t cie_offset) { + fprintf(stderr, + "%s: CFI frame description entry at offset 0x%" PRIx64 " in '%s':" + " CIE pointer does not point to a CIE: 0x%" PRIx64 "\n", + filename_.c_str(), offset, section_.c_str(), cie_offset); +} + +void CallFrameInfo::Reporter::UnexpectedAddressSize(uint64_t offset, + uint8_t address_size) { + fprintf(stderr, + "%s: CFI frame description entry at offset 0x%" PRIx64 " in '%s':" + " CIE specifies unexpected address size: %d\n", + filename_.c_str(), offset, section_.c_str(), address_size); +} + +void CallFrameInfo::Reporter::UnexpectedSegmentSize(uint64_t offset, + uint8_t segment_size) { + fprintf(stderr, + "%s: CFI frame description entry at offset 0x%" PRIx64 " in '%s':" + " CIE specifies unexpected segment size: %d\n", + filename_.c_str(), offset, section_.c_str(), segment_size); +} + +void CallFrameInfo::Reporter::UnrecognizedVersion(uint64_t offset, int version) { + fprintf(stderr, + "%s: CFI frame description entry at offset 0x%" PRIx64 " in '%s':" + " CIE specifies unrecognized version: %d\n", + filename_.c_str(), offset, section_.c_str(), version); +} + +void CallFrameInfo::Reporter::UnrecognizedAugmentation(uint64_t offset, + const string& aug) { + fprintf(stderr, + "%s: CFI frame description entry at offset 0x%" PRIx64 " in '%s':" + " CIE specifies unrecognized augmentation: '%s'\n", + filename_.c_str(), offset, section_.c_str(), aug.c_str()); +} + +void CallFrameInfo::Reporter::InvalidPointerEncoding(uint64_t offset, + uint8_t encoding) { + fprintf(stderr, + "%s: CFI common information entry at offset 0x%" PRIx64 " in '%s':" + " 'z' augmentation specifies invalid pointer encoding: 0x%02x\n", + filename_.c_str(), offset, section_.c_str(), encoding); +} + +void CallFrameInfo::Reporter::UnusablePointerEncoding(uint64_t offset, + uint8_t encoding) { + fprintf(stderr, + "%s: CFI common information entry at offset 0x%" PRIx64 " 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_t offset, uint64_t insn_offset) { + fprintf(stderr, + "%s: CFI common information entry at offset 0x%" PRIx64 " in '%s':" + " the DW_CFA_restore instruction at offset 0x%" PRIx64 + " cannot be used in a common information entry\n", + filename_.c_str(), offset, section_.c_str(), insn_offset); +} + +void CallFrameInfo::Reporter::BadInstruction(uint64_t offset, + CallFrameInfo::EntryKind kind, + uint64_t insn_offset) { + fprintf(stderr, + "%s: CFI %s at offset 0x%" PRIx64 " in section '%s':" + " the instruction at offset 0x%" PRIx64 " is unrecognized\n", + filename_.c_str(), CallFrameInfo::KindName(kind), + offset, section_.c_str(), insn_offset); +} + +void CallFrameInfo::Reporter::NoCFARule(uint64_t offset, + CallFrameInfo::EntryKind kind, + uint64_t insn_offset) { + fprintf(stderr, + "%s: CFI %s at offset 0x%" PRIx64 " in section '%s':" + " the instruction at offset 0x%" PRIx64 " 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_t offset, + CallFrameInfo::EntryKind kind, + uint64_t insn_offset) { + fprintf(stderr, + "%s: CFI %s at offset 0x%" PRIx64 " in section '%s':" + " the DW_CFA_restore_state instruction at offset 0x%" PRIx64 + " 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_t offset, + CallFrameInfo::EntryKind kind, + uint64_t insn_offset) { + fprintf(stderr, + "%s: CFI %s at offset 0x%" PRIx64 " in section '%s':" + " the DW_CFA_restore_state instruction at offset 0x%" PRIx64 + " would clear the CFA rule in effect\n", + filename_.c_str(), CallFrameInfo::KindName(kind), offset, + section_.c_str(), insn_offset); +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/common/dwarf/dwarf2reader.h b/shared/sentry/external/breakpad/src/common/dwarf/dwarf2reader.h new file mode 100644 index 000000000..97e5fea90 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/dwarf/dwarf2reader.h @@ -0,0 +1,1494 @@ +// -*- 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 + +// 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 +#include + +#include +#include +#include +#include +#include +#include + +#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 google_breakpad { +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 > SectionMap; + +// Abstract away the difference between elf and mach-o section names. +// Elf-names use ".section_name, mach-o uses "__section_name". Pass "name" in +// the elf form, ".section_name". +const SectionMap::const_iterator GetSectionByName(const SectionMap& + sections, const char* name); + +// Most of the time, this struct functions as a simple attribute and form pair. +// However, Dwarf5 DW_FORM_implicit_const means that a form may have its value +// in line in the abbrev table, and that value must be associated with the +// pair until the attr's value is needed. +struct AttrForm { + AttrForm(enum DwarfAttribute attr, enum DwarfForm form, uint64_t value) : + attr_(attr), form_(form), value_(value) { } + + enum DwarfAttribute attr_; + enum DwarfForm form_; + uint64_t value_; +}; +typedef std::list AttributeList; +typedef AttributeList::iterator AttributeIterator; +typedef AttributeList::const_iterator ConstAttributeIterator; + +struct LineInfoHeader { + uint64_t total_length; + uint16_t version; + uint64_t prologue_length; + uint8_t min_insn_length; // insn stands for instructin + bool default_is_stmt; // stmt stands for statement + int8_t line_base; + uint8_t line_range; + uint8_t opcode_base; + // Use a pointer so that signalsafe_addr2line is able to use this structure + // without heap allocation problem. + std::vector* 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_t buffer_length, + ByteReader* reader, const uint8_t* string_buffer, + size_t string_buffer_length, const uint8_t* line_string_buffer, + size_t line_string_buffer_length, 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_t 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(); + + // Read the DWARF5 types and forms for the file and directory tables. + void ReadTypesAndForms(const uint8_t** lineptr, uint32_t* content_types, + uint32_t* content_forms, uint32_t max_types, + uint32_t* format_count); + + // Read a row from the dwarf5 LineInfo file table. + void ReadFileRow(const uint8_t** lineptr, const uint32_t* content_types, + const uint32_t* content_forms, uint32_t row, + uint32_t format_count); + + // Read and return the data at *lineptr according to form. Advance + // *lineptr appropriately. + uint64_t ReadUnsignedData(uint32_t form, const uint8_t** lineptr); + + // Read and return the data at *lineptr according to form. Advance + // *lineptr appropriately. + const char* ReadStringForm(uint32_t form, const uint8_t** lineptr); + + // The associated handler to call processing functions in + LineInfoHandler* handler_; + + // The associated ByteReader that handles endianness issues for us + ByteReader* reader_; + + // A DWARF 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_t buffer_length_; +#endif + // Convenience pointers into .debug_str and .debug_line_str. These exactly + // correspond to those in the compilation unit. + const uint8_t* string_buffer_; +#ifndef NDEBUG + uint64_t string_buffer_length_; +#endif + const uint8_t* line_string_buffer_; +#ifndef NDEBUG + uint64_t line_string_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_t 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_t file_num, + uint32_t dir_num, uint64_t mod_time, + uint64_t 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_t address, uint64_t length, + uint32_t file_num, uint32_t line_num, uint32_t column_num) { } +}; + +class RangeListHandler { + public: + RangeListHandler() { } + + virtual ~RangeListHandler() { } + + // Add a range. + virtual void AddRange(uint64_t begin, uint64_t end) { }; + + // Finish processing the range list. + virtual void Finish() { }; +}; + +class RangeListReader { + public: + // Reading a range list requires quite a bit of information + // from the compilation unit. Package it conveniently. + struct CURangesInfo { + CURangesInfo() : + version_(0), base_address_(0), ranges_base_(0), + buffer_(nullptr), size_(0), addr_buffer_(nullptr), + addr_buffer_size_(0), addr_base_(0) { } + + uint16_t version_; + // Ranges base address. Ordinarily the CU's low_pc. + uint64_t base_address_; + // Offset into .debug_rnglists for this CU's rangelists. + uint64_t ranges_base_; + // Contents of either .debug_ranges or .debug_rnglists. + const uint8_t* buffer_; + uint64_t size_; + // Contents of .debug_addr. This cu's contribution starts at + // addr_base_ + const uint8_t* addr_buffer_; + uint64_t addr_buffer_size_; + uint64_t addr_base_; + }; + + RangeListReader(ByteReader* reader, CURangesInfo* cu_info, + RangeListHandler* handler) : + reader_(reader), cu_info_(cu_info), handler_(handler), + offset_array_(0) { } + + // Read ranges from cu_info as specified by form and data. + bool ReadRanges(enum DwarfForm form, uint64_t data); + + private: + // Read dwarf4 .debug_ranges at offset. + bool ReadDebugRanges(uint64_t offset); + // Read dwarf5 .debug_rngslist at offset. + bool ReadDebugRngList(uint64_t offset); + + // Convenience functions to handle the mechanics of reading entries in the + // ranges section. + uint64_t ReadULEB(uint64_t offset, uint64_t* value) { + size_t len; + *value = reader_->ReadUnsignedLEB128(cu_info_->buffer_ + offset, &len); + return len; + } + + uint64_t ReadAddress(uint64_t offset, uint64_t* value) { + *value = reader_->ReadAddress(cu_info_->buffer_ + offset); + return reader_->AddressSize(); + } + + // Read the address at this CU's addr_index in the .debug_addr section. + uint64_t GetAddressAtIndex(uint64_t addr_index) { + assert(cu_info_->addr_buffer_ != nullptr); + uint64_t offset = + cu_info_->addr_base_ + addr_index * reader_->AddressSize(); + assert(offset < cu_info_->addr_buffer_size_); + return reader_->ReadAddress(cu_info_->addr_buffer_ + offset); + } + + ByteReader* reader_; + CURangesInfo* cu_info_; + RangeListHandler* handler_; + uint64_t offset_array_; +}; + +// 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_t offset, uint8_t address_size, + uint8_t offset_size, uint64_t cu_length, + uint8_t 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_t offset, + uint64_t 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_t 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_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t 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_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + int64_t 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_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t 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_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const uint8_t* data, + uint64_t 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_t 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_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t 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_t 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_t 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_t addr_buffer_length, + uint64_t addr_base, uint64_t ranges_base, uint64_t 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_t 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_t 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_t length; + uint16_t version; + uint64_t abbrev_offset; + uint8_t 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(); + + // Read the abbreviation offset for this compilation unit + size_t ReadAbbrevOffset(const uint8_t* headerptr); + + // Read the address size for this compilation unit + size_t ReadAddressSize(const uint8_t* headerptr); + + // Read the DWO id from a split or skeleton compilation unit header + size_t ReadDwoId(const uint8_t* headerptr); + + // Read the type signature from a type or split type compilation unit header + size_t ReadTypeSignature(const uint8_t* headerptr); + + // Read the DWO id from a split or skeleton compilation unit header + size_t ReadTypeOffset(const uint8_t* headerptr); + + // Processes a single DIE for this compilation unit and return a new + // pointer just past the end of it + const uint8_t* ProcessDIE(uint64_t 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_t dieoffset, + const uint8_t* start, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t implicit_const); + + // Special version of ProcessAttribute, for finding str_offsets_base and + // DW_AT_addr_base in DW_TAG_compile_unit, for DWARF v5. + const uint8_t* ProcessOffsetBaseAttribute(uint64_t dieoffset, + const uint8_t* start, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t implicit_const); + + // 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_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t data) { + if (attr == DW_AT_GNU_dwo_id) { + dwo_id_ = data; + } + else if (attr == DW_AT_GNU_addr_base || attr == DW_AT_addr_base) { + addr_base_ = data; + } + else if (attr == DW_AT_str_offsets_base) { + str_offsets_base_ = data; + } + else if (attr == DW_AT_GNU_ranges_base || attr == DW_AT_rnglists_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_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + int64_t 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_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const uint8_t* data, + uint64_t len) { + handler_->ProcessAttributeBuffer(offset, attr, form, data, len); + } + + // Handles the common parts of DW_FORM_GNU_str_index, DW_FORM_strx, + // DW_FORM_strx1, DW_FORM_strx2, DW_FORM_strx3, and DW_FORM_strx4. + // Retrieves the data and calls through to ProcessAttributeString. + void ProcessFormStringIndex(uint64_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t str_index); + + // 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_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const char* data) { + if (attr == DW_AT_GNU_dwo_name || attr == DW_AT_dwo_name) + dwo_name_ = data; + handler_->ProcessAttributeString(offset, attr, form, data); + } + + // Called to handle common portions of DW_FORM_addrx and variations, as well + // as DW_FORM_GNU_addr_index. + void ProcessAttributeAddrIndex(uint64_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t addr_index) { + const uint8_t* addr_ptr = + addr_buffer_ + addr_base_ + addr_index * reader_->AddressSize(); + ProcessAttributeUnsigned( + offset, attr, form, reader_->ReadAddress(addr_ptr)); + } + + // 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_t 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_t 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* 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_t string_buffer_length_; + + // Similarly for .debug_line_string. + const uint8_t* line_string_buffer_; + uint64_t line_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_t str_offsets_buffer_length_; + + // Address section buffer and length, if we have an address section + // (.debug_addr). + const uint8_t* addr_buffer_; + uint64_t 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_; + + // Flag indicating if it's a Type Unit (only applicable to DWARF v5). + bool is_type_unit_; + + // The value of the DW_AT_GNU_dwo_id attribute, if any. + uint64_t dwo_id_; + + // The value of the DW_AT_GNU_type_signature attribute, if any. + uint64_t type_signature_; + + // The value of the DW_AT_GNU_type_offset attribute, if any. + size_t type_offset_; + + // 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_t skeleton_dwo_id_; + + // The value of the DW_AT_GNU_ranges_base or DW_AT_rnglists_base attribute, + // if any. + uint64_t ranges_base_; + + // The value of the DW_AT_GNU_addr_base attribute, if any. + uint64_t addr_base_; + + // The value of DW_AT_str_offsets_base attribute, if any. + uint64_t str_offsets_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 dwp_byte_reader_; + + // DWP reader. + std::unique_ptr 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_t 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_t 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_t LookupCUv2(uint64_t 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_t 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_t version; // CFI data version number + string augmentation; // vendor format extension markers + uint64_t 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_t 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_t address_size; + uint8_t segment_size; + }; + + // A frame description entry (FDE). + struct FDE: public Entry { + uint64_t address; // start address of described code + uint64_t 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_t 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_t address, uint64_t length, + uint8_t 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_t address, int reg) = 0; + + // At ADDRESS, register REG's value is the same as that it had in + // the caller. + virtual bool SameValueRule(uint64_t address, int reg) = 0; + + // At ADDRESS, register REG has been saved at offset OFFSET from + // BASE_REGISTER. + virtual bool OffsetRule(uint64_t 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_t 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_t 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_t 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_t 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_t 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_t 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& section = ".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_t 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_t offset); + + // The FDE at OFFSET refers to the CIE at CIE_OFFSET, but the + // section is not that large. + virtual void CIEPointerOutOfRange(uint64_t offset, uint64_t 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_t offset, uint64_t 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_t 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_t 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_t 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_t offset, + const string& augmentation); + + // The pointer encoding ENCODING, specified by the CIE at OFFSET, is not + // a valid encoding. + virtual void InvalidPointerEncoding(uint64_t offset, uint8_t 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_t offset, uint8_t 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_t offset, uint64_t insn_offset); + + // The entry at OFFSET, of kind KIND, has an unrecognized + // instruction at INSN_OFFSET. + virtual void BadInstruction(uint64_t offset, CallFrameInfo::EntryKind kind, + uint64_t 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_t offset, CallFrameInfo::EntryKind kind, + uint64_t 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_t offset, CallFrameInfo::EntryKind kind, + uint64_t 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_t offset, CallFrameInfo::EntryKind kind, + uint64_t 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 google_breakpad + +#endif // UTIL_DEBUGINFO_DWARF2READER_H__ diff --git a/shared/sentry/external/breakpad/src/common/dwarf/dwarf2reader_cfi_unittest.cc b/shared/sentry/external/breakpad/src/common/dwarf/dwarf2reader_cfi_unittest.cc new file mode 100644 index 000000000..5f7037941 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/dwarf/dwarf2reader_cfi_unittest.cc @@ -0,0 +1,2584 @@ +// 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 + +// dwarf2reader_cfi_unittest.cc: Unit tests for google_breakpad::CallFrameInfo + +#include +#include + +#include +#include + +// 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 +#include +#include +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 google_breakpad::DwarfPointerEncoding; +using google_breakpad::ENDIANNESS_BIG; +using google_breakpad::ENDIANNESS_LITTLE; +using google_breakpad::ByteReader; +using google_breakpad::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& section); +#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_t address, uint64_t length, + uint8_t version, const string& augmentation, + unsigned return_address)); + MOCK_METHOD2(UndefinedRule, bool(uint64_t address, int reg)); + MOCK_METHOD2(SameValueRule, bool(uint64_t address, int reg)); + MOCK_METHOD4(OffsetRule, bool(uint64_t address, int reg, int base_register, + long offset)); + MOCK_METHOD4(ValOffsetRule, bool(uint64_t address, int reg, int base_register, + long offset)); + MOCK_METHOD3(RegisterRule, bool(uint64_t address, int reg, int base_register)); + MOCK_METHOD3(ExpressionRule, bool(uint64_t address, int reg, + const string& expression)); + MOCK_METHOD3(ValExpressionRule, bool(uint64_t address, int reg, + const string& expression)); + MOCK_METHOD0(End, bool()); + MOCK_METHOD2(PersonalityRoutine, bool(uint64_t address, bool indirect)); + MOCK_METHOD2(LanguageSpecificDataArea, bool(uint64_t address, bool indirect)); + MOCK_METHOD0(SignalHandler, bool()); +}; + +class MockCallFrameErrorReporter: public CallFrameInfo::Reporter { + public: + MockCallFrameErrorReporter() : Reporter("mock filename", "mock section") { } + MOCK_METHOD2(Incomplete, void(uint64_t, CallFrameInfo::EntryKind)); + MOCK_METHOD1(EarlyEHTerminator, void(uint64_t)); + MOCK_METHOD2(CIEPointerOutOfRange, void(uint64_t, uint64_t)); + MOCK_METHOD2(BadCIEId, void(uint64_t, uint64_t)); + MOCK_METHOD2(UnexpectedAddressSize, void(uint64_t, uint8_t)); + MOCK_METHOD2(UnexpectedSegmentSize, void(uint64_t, uint8_t)); + MOCK_METHOD2(UnrecognizedVersion, void(uint64_t, int version)); + MOCK_METHOD2(UnrecognizedAugmentation, void(uint64_t, const string&)); + MOCK_METHOD2(InvalidPointerEncoding, void(uint64_t, uint8_t)); + MOCK_METHOD2(UnusablePointerEncoding, void(uint64_t, uint8_t)); + MOCK_METHOD2(RestoreInCIE, void(uint64_t, uint64_t)); + MOCK_METHOD3(BadInstruction, void(uint64_t, CallFrameInfo::EntryKind, + uint64_t)); + MOCK_METHOD3(NoCFARule, void(uint64_t, CallFrameInfo::EntryKind, uint64_t)); + MOCK_METHOD3(EmptyStateStack, void(uint64_t, CallFrameInfo::EntryKind, + uint64_t)); +}; + +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(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(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(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(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, google_breakpad::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(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(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(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(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(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(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(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(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(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(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(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(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(google_breakpad::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)); + google_breakpad::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(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_t code_factor; + int data_factor; + unsigned return_register; + unsigned version; + unsigned cfa_base_register; + int cfa_offset; + uint64_t 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(google_breakpad::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(google_breakpad::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(google_breakpad::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(google_breakpad::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(google_breakpad::DW_CFA_advance_loc1).D8(0xd8) + .D8(google_breakpad::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(google_breakpad::DW_CFA_advance_loc2).D16(0x3adb) + .D8(google_breakpad::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(google_breakpad::DW_CFA_advance_loc4).D32(0x15813c88) + .D8(google_breakpad::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(google_breakpad::DW_CFA_MIPS_advance_loc8).D64(0x3c4f3945b92c14ULL) + .D8(google_breakpad::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(google_breakpad::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(google_breakpad::DW_CFA_def_cfa_sf).ULEB128(0x8ccb32b7).LEB128(0x9ea) + .D8(google_breakpad::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(google_breakpad::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(google_breakpad::DW_CFA_def_cfa_expression).Block("needle in a haystack") + .D8(google_breakpad::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(google_breakpad::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(google_breakpad::DW_CFA_def_cfa_offset_sf).LEB128(0x970) + .D8(google_breakpad::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(google_breakpad::DW_CFA_def_cfa_expression).Block("six ways to Sunday") + .D8(google_breakpad::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(google_breakpad::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(google_breakpad::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(google_breakpad::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(google_breakpad::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(google_breakpad::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(google_breakpad::DW_CFA_offset_extended_sf) + .ULEB128(0x997c23ee).LEB128(0x2d00) + .D8(google_breakpad::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(google_breakpad::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(google_breakpad::DW_CFA_val_offset_sf).ULEB128(0x6f4f).LEB128(0xaab) + .D8(google_breakpad::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(google_breakpad::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(google_breakpad::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(google_breakpad::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(google_breakpad::DW_CFA_def_cfa).ULEB128(0x6ca1d50e).ULEB128(0x372e38e8) + // Provide an offset(N) rule for register 0x3c. + .D8(google_breakpad::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(google_breakpad::DW_CFA_advance_loc | 0x13) + .D8(google_breakpad::DW_CFA_offset | 0x3c).ULEB128(0x9a50) + // At a third address, restore the original rule for register 0x3c. + .D8(google_breakpad::DW_CFA_advance_loc | 0x01) + .D8(google_breakpad::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(google_breakpad::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(google_breakpad::DW_CFA_advance_loc | 0x7) + .D8(google_breakpad::DW_CFA_offset | 0x2c).ULEB128(0x1f47) + // At a third address, restore the (missing) CIE rule for register 0x2c. + .D8(google_breakpad::DW_CFA_advance_loc | 0xb) + .D8(google_breakpad::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(google_breakpad::DW_CFA_def_cfa).ULEB128(0x56fa0edd).ULEB128(0x097f78a5) + // Provide an offset(N) rule for register 0x0f9b8a1c. + .D8(google_breakpad::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(google_breakpad::DW_CFA_advance_loc | 0x3) + .D8(google_breakpad::DW_CFA_offset_extended) + .ULEB128(0x0f9b8a1c).ULEB128(0x3b7b) + // At a third address, restore the original rule for register 0x0f9b8a1c. + .D8(google_breakpad::DW_CFA_advance_loc | 0x04) + .D8(google_breakpad::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(google_breakpad::DW_CFA_offset | 2).ULEB128(0x9806) + .D8(google_breakpad::DW_CFA_offset | 3).ULEB128(0x995d) + .D8(google_breakpad::DW_CFA_offset | 4).ULEB128(0x7055) + .D8(google_breakpad::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(google_breakpad::DW_CFA_advance_loc | 1) + // Create the "outgoing" state, which we will discard. + .D8(google_breakpad::DW_CFA_offset | 1).ULEB128(0xea1a) + .D8(google_breakpad::DW_CFA_register).ULEB128(2).ULEB128(0x1d2a3767) + .D8(google_breakpad::DW_CFA_offset | 3).ULEB128(0xdd29) + .D8(google_breakpad::DW_CFA_offset | 5).ULEB128(0xf1ce) + // At a third address, restore the incoming state. + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::DW_CFA_restore_state) + .FinishEntry(); + + uint64_t 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(google_breakpad::DW_CFA_remember_state) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::DW_CFA_def_cfa_offset).ULEB128(0x90481102) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::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(google_breakpad::DW_CFA_nop) + .D8(google_breakpad::DW_CFA_def_cfa).ULEB128(0x3fb8d4f1).ULEB128(0x078dc67b) + .D8(google_breakpad::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(google_breakpad::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(google_breakpad::DW_CFA_GNU_args_size).ULEB128(0xeddfa520) + // Verify that we see this, meaning we parsed the above properly. + .D8(google_breakpad::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(google_breakpad::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(google_breakpad::DW_CFA_def_cfa).ULEB128(0x42ed390b).ULEB128(0x98f43aad) + .FinishEntry() + // First FDE. + .FDEHeader(cie, 0xa870ebdd, 0x60f6aa4) + .D8(google_breakpad::DW_CFA_register).ULEB128(0x3a860351).ULEB128(0x6c9a6bcf) + .FinishEntry() + // Second FDE. + .FDEHeader(cie, 0xc534f7c0, 0xf6552e9, true /* dwarf64 */) + .D8(google_breakpad::DW_CFA_register).ULEB128(0x1b62c234).ULEB128(0x26586b18) + .FinishEntry() + // Third FDE. + .FDEHeader(cie, 0xf681cfc8, 0x7e4594e) + .D8(google_breakpad::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(google_breakpad::DW_CFA_register).ULEB128(0xe0cf850d).ULEB128(0x15aab431) + .D8(google_breakpad::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(google_breakpad::DW_CFA_undefined).ULEB128(0x0bac878e) + .D8(google_breakpad::DW_CFA_remember_state) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::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(google_breakpad::DW_CFA_undefined).ULEB128(0x7dedff5f) + .D8(google_breakpad::DW_CFA_remember_state) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::DW_CFA_same_value).ULEB128(0x7dedff5f) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::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(google_breakpad::DW_CFA_same_value).ULEB128(0xadbc9b3a) + .D8(google_breakpad::DW_CFA_remember_state) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::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(google_breakpad::DW_CFA_same_value).ULEB128(0x3d90dcb5) + .D8(google_breakpad::DW_CFA_remember_state) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::DW_CFA_undefined).ULEB128(0x3d90dcb5) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::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(google_breakpad::DW_CFA_offset | 0x14).ULEB128(0xb6f) + .D8(google_breakpad::DW_CFA_remember_state) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::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(google_breakpad::DW_CFA_offset | 0x21).ULEB128(0xeb7) + .D8(google_breakpad::DW_CFA_remember_state) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::DW_CFA_undefined).ULEB128(0x21) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::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(google_breakpad::DW_CFA_offset | 0x21).ULEB128(0x134) + .D8(google_breakpad::DW_CFA_remember_state) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::DW_CFA_offset | 0x21).ULEB128(0xf4f) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::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(google_breakpad::DW_CFA_val_offset).ULEB128(0x829caee6).ULEB128(0xe4c) + .D8(google_breakpad::DW_CFA_remember_state) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::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(google_breakpad::DW_CFA_val_offset).ULEB128(0xf17c36d6).ULEB128(0xeb7) + .D8(google_breakpad::DW_CFA_remember_state) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::DW_CFA_undefined).ULEB128(0xf17c36d6) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::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(google_breakpad::DW_CFA_val_offset).ULEB128(0x2cf0ab1b).ULEB128(0x562) + .D8(google_breakpad::DW_CFA_remember_state) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::DW_CFA_val_offset).ULEB128(0x2cf0ab1b).ULEB128(0xe88) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::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(google_breakpad::DW_CFA_register).ULEB128(0x77514acc).ULEB128(0x464de4ce) + .D8(google_breakpad::DW_CFA_remember_state) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::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(google_breakpad::DW_CFA_register).ULEB128(0xe39acce5).ULEB128(0x095f1559) + .D8(google_breakpad::DW_CFA_remember_state) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::DW_CFA_undefined).ULEB128(0xe39acce5) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::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(google_breakpad::DW_CFA_register).ULEB128(0xd40e21b1).ULEB128(0x16607d6a) + .D8(google_breakpad::DW_CFA_remember_state) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::DW_CFA_register).ULEB128(0xd40e21b1).ULEB128(0xbabb4742) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::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(google_breakpad::DW_CFA_expression).ULEB128(0x666ae152).Block("dwarf") + .D8(google_breakpad::DW_CFA_remember_state) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::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(google_breakpad::DW_CFA_expression).ULEB128(0xb5ca5c46).Block("elf") + .D8(google_breakpad::DW_CFA_remember_state) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::DW_CFA_undefined).ULEB128(0xb5ca5c46) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::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(google_breakpad::DW_CFA_expression).ULEB128(0x500f5739).Block("smurf") + .D8(google_breakpad::DW_CFA_remember_state) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::DW_CFA_expression).ULEB128(0x500f5739).Block("orc") + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::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(google_breakpad::DW_CFA_val_expression).ULEB128(0x666ae152) + .Block("hideous") + .D8(google_breakpad::DW_CFA_remember_state) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::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(google_breakpad::DW_CFA_val_expression).ULEB128(0xb5ca5c46) + .Block("revolting") + .D8(google_breakpad::DW_CFA_remember_state) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::DW_CFA_undefined).ULEB128(0xb5ca5c46) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::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(google_breakpad::DW_CFA_val_expression).ULEB128(0x500f5739) + .Block("repulsive") + .D8(google_breakpad::DW_CFA_remember_state) + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::DW_CFA_val_expression).ULEB128(0x500f5739) + .Block("nauseous") + .D8(google_breakpad::DW_CFA_advance_loc | 1) + .D8(google_breakpad::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)); + google_breakpad::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(contents.data())); + byte_reader.SetTextBase(encoded_pointer_bases.text); + byte_reader.SetDataBase(encoded_pointer_bases.data); + CallFrameInfo parser(reinterpret_cast(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(google_breakpad::DW_CFA_def_cfa).ULEB128(3772).ULEB128(1372) + .FinishEntry() + .FDEHeader(cie, 0x848037a1, 0x7b30475e) + .D8(google_breakpad::DW_CFA_set_loc).D32(0x17713850) + .D8(google_breakpad::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(google_breakpad::DW_EH_PE_indirect + | google_breakpad::DW_EH_PE_datarel + | google_breakpad::DW_EH_PE_sdata2); + DwarfPointerEncoding fde_encoding = + DwarfPointerEncoding(google_breakpad::DW_EH_PE_textrel + | google_breakpad::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(google_breakpad::DW_EH_PE_pcrel) // personality pointer format + .EncodedPointer(0x97baa00, google_breakpad::DW_EH_PE_pcrel) // and value + .D8(fde_encoding) // FDE pointer format + .D8(google_breakpad::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(google_breakpad::DW_CFA_set_loc) + .EncodedPointer(0x540fa4ce, fde_encoding) + .D8(google_breakpad::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(google_breakpad::DW_CFA_def_cfa).ULEB128(3629).ULEB128(247) + .FinishEntry() + .FDEHeader(cie, 0xda007738, 0xfb55c641) + .ULEB128(0) // Augmentation data length + .D8(google_breakpad::DW_CFA_advance_loc1).D8(11) + .D8(google_breakpad::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(google_breakpad::DW_CFA_def_cfa).ULEB128(9006).ULEB128(7725) + .FinishEntry() + .FDEHeader(cie, 0x1293efa8, 0x236f53f2) + .ULEB128(0) // Augmentation data length + .D8(google_breakpad::DW_CFA_advance_loc | 12) + .D8(google_breakpad::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(google_breakpad::DW_EH_PE_funcrel + | google_breakpad::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(google_breakpad::DW_EH_PE_datarel + | google_breakpad::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(google_breakpad::DW_EH_PE_textrel + | google_breakpad::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/shared/sentry/external/breakpad/src/common/dwarf/dwarf2reader_die_unittest.cc b/shared/sentry/external/breakpad/src/common/dwarf/dwarf2reader_die_unittest.cc new file mode 100644 index 000000000..85eedeee5 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/dwarf/dwarf2reader_die_unittest.cc @@ -0,0 +1,964 @@ +// 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 + +// dwarf2reader_die_unittest.cc: Unit tests for google_breakpad::CompilationUnit + +#include +#include + +#include +#include +#include + +#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 google_breakpad::ByteReader; +using google_breakpad::CompilationUnit; +using google_breakpad::Dwarf2Handler; +using google_breakpad::DwarfAttribute; +using google_breakpad::DwarfForm; +using google_breakpad::DwarfHasChild; +using google_breakpad::DwarfTag; +using google_breakpad::ENDIANNESS_BIG; +using google_breakpad::ENDIANNESS_LITTLE; +using google_breakpad::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_t offset, uint8_t address_size, + uint8_t offset_size, + uint64_t cu_length, + uint8_t dwarf_version)); + MOCK_METHOD2(StartDIE, bool(uint64_t offset, enum DwarfTag tag)); + MOCK_METHOD4(ProcessAttributeUnsigned, void(uint64_t offset, + DwarfAttribute attr, + enum DwarfForm form, + uint64_t data)); + MOCK_METHOD4(ProcessAttributeSigned, void(uint64_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + int64_t data)); + MOCK_METHOD4(ProcessAttributeReference, void(uint64_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t data)); + MOCK_METHOD5(ProcessAttributeBuffer, void(uint64_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const uint8_t* data, + uint64_t len)); + MOCK_METHOD4(ProcessAttributeString, void(uint64_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const string& data)); + MOCK_METHOD4(ProcessAttributeSignature, void(uint64_t offset, + DwarfAttribute attr, + enum DwarfForm form, + uint64_t signature)); + MOCK_METHOD1(EndDIE, void(uint64_t 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(info_contents.data()); + section_map[".debug_info"].second = info_contents.size(); + section_map[".debug_abbrev"].first + = reinterpret_cast(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, int header_type) + : endianness(endianness), format_size(format_size), + version(version), address_size(address_size), header_type(header_type) + { } + Endianness endianness; + size_t format_size; // 4-byte or 8-byte DWARF offsets + int version; + size_t address_size; + int header_type; // DW_UT_{compile, type, partial, skeleton, etc} +}; + +class DwarfHeader: public DIEFixture, + public TestWithParam { }; + +TEST_P(DwarfHeader, Header) { + Label abbrev_table = abbrevs.Here(); + abbrevs.Abbrev(1, google_breakpad::DW_TAG_compile_unit, + google_breakpad::DW_children_yes) + .Attribute(google_breakpad::DW_AT_name, google_breakpad::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, + google_breakpad::DW_UT_compile) + .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(_, google_breakpad::DW_TAG_compile_unit)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, ProcessAttributeString(_, google_breakpad::DW_AT_name, + google_breakpad::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()); +} + +TEST_P(DwarfHeader, TypeUnitHeader) { + Label abbrev_table = abbrevs.Here(); + int version = 5; + abbrevs.Abbrev(1, google_breakpad::DW_TAG_type_unit, + google_breakpad::DW_children_yes) + .Attribute(google_breakpad::DW_AT_name, google_breakpad::DW_FORM_string) + .EndAbbrev() + .EndTable(); + + info.set_format_size(GetParam().format_size); + info.set_endianness(GetParam().endianness); + + info.Header(version, abbrev_table, GetParam().address_size, + google_breakpad::DW_UT_type) + .ULEB128(0x41) // DW_TAG_type_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, _, + version)) + .WillOnce(Return(true)); + // If the type unit is handled properly, these calls will be skipped. + EXPECT_CALL(handler, StartDIE(_, google_breakpad::DW_TAG_type_unit)) + .Times(0); + EXPECT_CALL(handler, ProcessAttributeString(_, google_breakpad::DW_AT_name, + google_breakpad::DW_FORM_string, + "sam")) + .Times(0); + EXPECT_CALL(handler, EndDIE(_)) + .Times(0); + } + + 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_SUITE_P( + HeaderVariants, DwarfHeader, + ::testing::Values(DwarfHeaderParams(kLittleEndian, 4, 2, 4, 1), + DwarfHeaderParams(kLittleEndian, 4, 2, 8, 1), + DwarfHeaderParams(kLittleEndian, 4, 3, 4, 1), + DwarfHeaderParams(kLittleEndian, 4, 3, 8, 1), + DwarfHeaderParams(kLittleEndian, 4, 4, 4, 1), + DwarfHeaderParams(kLittleEndian, 4, 4, 8, 1), + DwarfHeaderParams(kLittleEndian, 8, 2, 4, 1), + DwarfHeaderParams(kLittleEndian, 8, 2, 8, 1), + DwarfHeaderParams(kLittleEndian, 8, 3, 4, 1), + DwarfHeaderParams(kLittleEndian, 8, 3, 8, 1), + DwarfHeaderParams(kLittleEndian, 8, 4, 4, 1), + DwarfHeaderParams(kLittleEndian, 8, 4, 8, 1), + DwarfHeaderParams(kLittleEndian, 8, 5, 4, 1), + DwarfHeaderParams(kLittleEndian, 8, 5, 8, 1), + DwarfHeaderParams(kBigEndian, 4, 2, 4, 1), + DwarfHeaderParams(kBigEndian, 4, 2, 8, 1), + DwarfHeaderParams(kBigEndian, 4, 3, 4, 1), + DwarfHeaderParams(kBigEndian, 4, 3, 8, 1), + DwarfHeaderParams(kBigEndian, 4, 4, 4, 1), + DwarfHeaderParams(kBigEndian, 4, 4, 8, 1), + DwarfHeaderParams(kBigEndian, 8, 2, 4, 1), + DwarfHeaderParams(kBigEndian, 8, 2, 8, 1), + DwarfHeaderParams(kBigEndian, 8, 3, 4, 1), + DwarfHeaderParams(kBigEndian, 8, 3, 8, 1), + DwarfHeaderParams(kBigEndian, 8, 4, 4, 1), + DwarfHeaderParams(kBigEndian, 8, 4, 8, 1), + DwarfHeaderParams(kBigEndian, 8, 5, 4, 1), + DwarfHeaderParams(kBigEndian, 8, 5, 8, 1))); + +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& params, + DwarfTag tag, DwarfAttribute name, + DwarfForm form) { + // Create the abbreviation table. + Label abbrev_table = abbrevs.Here(); + abbrevs.Abbrev(1, tag, google_breakpad::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, + google_breakpad::DW_UT_compile) + .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& params, + DwarfTag tag, uint64_t 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& params, + uint64_t 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 { }; + +TEST_P(DwarfForms, addr) { + StartSingleAttributeDIE(GetParam(), google_breakpad::DW_TAG_compile_unit, + google_breakpad::DW_AT_low_pc, + google_breakpad::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(), google_breakpad::DW_TAG_compile_unit); + EXPECT_CALL(handler, ProcessAttributeUnsigned(_, google_breakpad::DW_AT_low_pc, + google_breakpad::DW_FORM_addr, + value)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +TEST_P(DwarfForms, strx1) { + if (GetParam().version != 5) { + return; + } + Label abbrev_table = abbrevs.Here(); + abbrevs.Abbrev(1, google_breakpad::DW_TAG_compile_unit, + google_breakpad::DW_children_no) + .Attribute(google_breakpad::DW_AT_name, google_breakpad::DW_FORM_strx1) + .Attribute(google_breakpad::DW_AT_low_pc, google_breakpad::DW_FORM_addr) + .Attribute(google_breakpad::DW_AT_str_offsets_base, + google_breakpad::DW_FORM_sec_offset) + .EndAbbrev() + .EndTable(); + + info.set_format_size(GetParam().format_size); + info.set_endianness(GetParam().endianness); + info.Header(GetParam().version, abbrev_table, GetParam().address_size, + google_breakpad::DW_UT_compile) + .ULEB128(1) // abbrev index + .D8(2); // string index + + uint64_t value; + uint64_t offsets_base; + if (GetParam().address_size == 4) { + value = 0xc8e9ffcc; + offsets_base = 8; + info.D32(value); // low pc + info.D32(offsets_base); // str_offsets_base + } else { + value = 0xe942517fc2768564ULL; + offsets_base = 16; + info.D64(value); // low_pc + info.D64(offsets_base); // str_offsets_base + } + info.Finish(); + + Section debug_strings; + // no header, just a series of null-terminated strings. + debug_strings.AppendCString("apple"); // offset = 0 + debug_strings.AppendCString("bird"); // offset = 6 + debug_strings.AppendCString("canary"); // offset = 11 + debug_strings.AppendCString("dinosaur"); // offset = 18 + + Section str_offsets; + str_offsets.set_endianness(GetParam().endianness); + // Header for .debug_str_offsets + if (GetParam().address_size == 4) { + str_offsets.D32(24); // section length (4 bytes) + } else { + str_offsets.D32(0xffffffff); + str_offsets.D64(48); // section length (12 bytes) + } + str_offsets.D16(GetParam().version); // version (2 bytes) + str_offsets.D16(0); // padding (2 bytes) + + // .debug_str_offsets data (the offsets) + if (GetParam().address_size == 4) { + str_offsets.D32(0); + str_offsets.D32(6); + str_offsets.D32(11); + str_offsets.D32(18); + } else { + str_offsets.D64(0); + str_offsets.D64(6); + str_offsets.D64(11); + str_offsets.D64(18); + } + + + ExpectBeginCompilationUnit(GetParam(), google_breakpad::DW_TAG_compile_unit); + EXPECT_CALL(handler, ProcessAttributeString(_, google_breakpad::DW_AT_name, + google_breakpad::DW_FORM_strx1, + "bird")) + .WillOnce(Return()); + EXPECT_CALL(handler, ProcessAttributeUnsigned(_, google_breakpad::DW_AT_low_pc, + google_breakpad::DW_FORM_addr, + value)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +TEST_P(DwarfForms, block2_empty) { + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x16e4d2f7, + (DwarfAttribute) 0xe52c4463, + google_breakpad::DW_FORM_block2); + info.D16(0); + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x16e4d2f7); + EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xe52c4463, + google_breakpad::DW_FORM_block2, + _, 0)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +TEST_P(DwarfForms, block2) { + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x16e4d2f7, + (DwarfAttribute) 0xe52c4463, + google_breakpad::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, + google_breakpad::DW_FORM_block2, + Pointee('*'), 258)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +TEST_P(DwarfForms, flag_present) { + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x3e449ac2, + (DwarfAttribute) 0x359d1972, + google_breakpad::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, + google_breakpad::DW_FORM_flag_present, + 1)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +TEST_P(DwarfForms, sec_offset) { + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x1d971689, + (DwarfAttribute) 0xa060bfd1, + google_breakpad::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, + google_breakpad::DW_FORM_sec_offset, + value)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +TEST_P(DwarfForms, exprloc) { + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0xb6d167bb, + (DwarfAttribute) 0xba3ae5cb, + google_breakpad::DW_FORM_exprloc); + info.ULEB128(29) + .Append(29, 173); + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0xb6d167bb); + EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xba3ae5cb, + google_breakpad::DW_FORM_exprloc, + Pointee(173), 29)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +TEST_P(DwarfForms, ref_sig8) { + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x253e7b2b, + (DwarfAttribute) 0xd708d908, + google_breakpad::DW_FORM_ref_sig8); + info.D64(0xf72fa0cb6ddcf9d6ULL); + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x253e7b2b); + EXPECT_CALL(handler, ProcessAttributeSignature(_, (DwarfAttribute) 0xd708d908, + google_breakpad::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, + google_breakpad::DW_FORM_ref_sig8); + info.D64(0xf72fa0cb6ddcf9d6ULL); + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x253e7b2b, 98); + EXPECT_CALL(handler, ProcessAttributeSignature(_, (DwarfAttribute) 0xd708d908, + google_breakpad::DW_FORM_ref_sig8, + 0xf72fa0cb6ddcf9d6ULL)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam(), 98); +} + +TEST_P(DwarfForms, implicit_const) { + const DwarfHeaderParams& params = GetParam(); + const uint64_t implicit_constant_value = 0x1234; + // Create the abbreviation table. + Label abbrev_table = abbrevs.Here(); + abbrevs.Abbrev(1, (DwarfTag) 0x253e7b2b, google_breakpad::DW_children_no) + .Attribute((DwarfAttribute) 0xd708d908, + google_breakpad::DW_FORM_implicit_const) + .ULEB128(implicit_constant_value); + abbrevs.EndAbbrev().EndTable(); + + info.set_format_size(params.format_size); + info.set_endianness(params.endianness); + info.Header(params.version, abbrev_table, params.address_size, + google_breakpad::DW_UT_compile) + .ULEB128(1); // abbrev code + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x253e7b2b); + EXPECT_CALL(handler, + ProcessAttributeUnsigned(_, (DwarfAttribute) 0xd708d908, + google_breakpad::DW_FORM_implicit_const, + implicit_constant_value)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +// Tests for the other attribute forms could go here. + +INSTANTIATE_TEST_SUITE_P( + HeaderVariants, DwarfForms, + ::testing::Values(DwarfHeaderParams(kLittleEndian, 4, 2, 4, 1), + DwarfHeaderParams(kLittleEndian, 4, 2, 8, 1), + DwarfHeaderParams(kLittleEndian, 4, 3, 4, 1), + DwarfHeaderParams(kLittleEndian, 4, 3, 8, 1), + DwarfHeaderParams(kLittleEndian, 4, 4, 4, 1), + DwarfHeaderParams(kLittleEndian, 4, 4, 8, 1), + DwarfHeaderParams(kLittleEndian, 8, 2, 4, 1), + DwarfHeaderParams(kLittleEndian, 8, 2, 8, 1), + DwarfHeaderParams(kLittleEndian, 8, 3, 4, 1), + DwarfHeaderParams(kLittleEndian, 8, 3, 8, 1), + DwarfHeaderParams(kLittleEndian, 8, 4, 4, 1), + DwarfHeaderParams(kLittleEndian, 8, 4, 8, 1), + DwarfHeaderParams(kBigEndian, 4, 2, 4, 1), + DwarfHeaderParams(kBigEndian, 4, 2, 8, 1), + DwarfHeaderParams(kBigEndian, 4, 3, 4, 1), + DwarfHeaderParams(kBigEndian, 4, 3, 8, 1), + DwarfHeaderParams(kBigEndian, 4, 4, 4, 1), + DwarfHeaderParams(kBigEndian, 4, 4, 8, 1), + DwarfHeaderParams(kBigEndian, 8, 2, 4, 1), + DwarfHeaderParams(kBigEndian, 8, 2, 8, 1), + DwarfHeaderParams(kBigEndian, 8, 3, 4, 1), + DwarfHeaderParams(kBigEndian, 8, 3, 8, 1), + DwarfHeaderParams(kBigEndian, 8, 4, 4, 1), + DwarfHeaderParams(kBigEndian, 8, 4, 8, 1))); + +class MockRangeListHandler: public google_breakpad::RangeListHandler { + public: + MOCK_METHOD(void, AddRange, (uint64_t begin, uint64_t end)); + MOCK_METHOD(void, Finish, ()); +}; + +TEST(RangeList, Dwarf4ReadRangeList) { + using google_breakpad::RangeListReader; + using google_breakpad::DW_FORM_sec_offset; + + // Create a dwarf4 .debug_ranges section. + google_breakpad::test_assembler::Section ranges(kBigEndian); + std::string padding_offset = "padding offset"; + ranges.Append(padding_offset); + const uint64_t section_offset = ranges.Size(); + ranges.D32(1).D32(2); // (2, 3) + ranges.D32(0xFFFFFFFF).D32(3); // base_address = 3. + ranges.D32(1).D32(2); // (4, 5) + ranges.D32(0).D32(1); // (3, 4) An out of order entry is legal. + ranges.D32(0).D32(0); // End of range. + + std::string section_contents; + ranges.GetContents(§ion_contents); + + ByteReader byte_reader(ENDIANNESS_BIG); + byte_reader.SetAddressSize(4); + + RangeListReader::CURangesInfo cu_info; + // Only set the fields that matter for dwarf 4. + cu_info.version_ = 4; + cu_info.base_address_ = 1; + cu_info.buffer_ = reinterpret_cast(section_contents.data()); + cu_info.size_ = section_contents.size(); + + MockRangeListHandler handler; + google_breakpad::RangeListReader range_list_reader(&byte_reader, &cu_info, + &handler); + EXPECT_CALL(handler, AddRange(2, 3)); + EXPECT_CALL(handler, AddRange(4, 5)); + EXPECT_CALL(handler, AddRange(3, 4)); + EXPECT_CALL(handler, Finish()); + EXPECT_TRUE(range_list_reader.ReadRanges(DW_FORM_sec_offset, + section_offset)); +} + +TEST(RangeList, Dwarf5ReadRangeList_rnglists) { + using google_breakpad::RangeListReader; + using google_breakpad::DW_RLE_base_addressx; + using google_breakpad::DW_RLE_startx_endx; + using google_breakpad::DW_RLE_startx_length; + using google_breakpad::DW_RLE_offset_pair; + using google_breakpad::DW_RLE_end_of_list; + using google_breakpad::DW_RLE_base_address; + using google_breakpad::DW_RLE_start_end; + using google_breakpad::DW_RLE_start_length; + using google_breakpad::DW_FORM_sec_offset; + using google_breakpad::DW_FORM_rnglistx; + + // Size of header + const uint64_t header_size = 12; + // Size of length field in header + const uint64_t length_size = 4; + + // .debug_addr for the indexed entries like startx. + Section addr; + addr.set_endianness(kBigEndian); + // Test addr_base handling with a padding address at 0. + addr.D32(0).D32(1).D32(2).D32(3).D32(4); + std::string addr_contents; + assert(addr.GetContents(&addr_contents)); + + // .debug_rnglists is the dwarf 5 section. + Section rnglists1(kBigEndian); + Section rnglists2(kBigEndian); + + // First header and body. + Label section_size1; + rnglists1.Append(kBigEndian, length_size, section_size1); + rnglists1.D16(5); // Version + rnglists1.D8(4); // Address size + rnglists1.D8(0); // Segment selector size + rnglists1.D32(2); // Offset entry count + const uint64_t ranges_base_1 = rnglists1.Size(); + + // Offset entries. + Label range0; + rnglists1.Append(kBigEndian, 4, range0); + Label range1; + rnglists1.Append(kBigEndian, 4, range1); + + // Range 0 (will be read via DW_AT_ranges, DW_FORM_rnglistx). + range0 = rnglists1.Size() - header_size; + rnglists1.D8(DW_RLE_base_addressx).ULEB128(0); // base_addr = 1 + rnglists1.D8(DW_RLE_startx_endx).ULEB128(1).ULEB128(2); // [2, 3) + rnglists1.D8(DW_RLE_startx_length).ULEB128(3).ULEB128(1); // [4, 5) + rnglists1.D8(DW_RLE_offset_pair).ULEB128(5).ULEB128(6); // [6, 7) + rnglists1.D8(DW_RLE_end_of_list); + + // Range 1 (will be read via DW_AT_ranges, DW_FORM_rnglistx). + range1 = rnglists1.Size() - header_size; + rnglists1.D8(DW_RLE_base_address).D32(8); // base_addr = 8 + rnglists1.D8(DW_RLE_offset_pair).ULEB128(1).ULEB128(2); // [9, 10) + rnglists1.D8(DW_RLE_start_end).D32(10).D32(11); // [10, 11) + rnglists1.D8(DW_RLE_start_length).D32(12).ULEB128(1); // [12, 13) + rnglists1.D8(DW_RLE_end_of_list); + // The size doesn't include the size of length field itself. + section_size1 = rnglists1.Size() - length_size; + + // Second header and body. + Label section_size2; + rnglists2.Append(kBigEndian, length_size, section_size2); + rnglists2.D16(5); // Version + rnglists2.D8(4); // Address size + rnglists2.D8(0); // Segment selector size + rnglists2.D32(2); // Offset entry count + const uint64_t ranges_base_2 = rnglists1.Size() + rnglists2.Size(); + + // Offset entries. + Label range2; + rnglists2.Append(kBigEndian, 4, range2); + Label range3; + rnglists2.Append(kBigEndian, 4, range3); + + // Range 2 (will be read via DW_AT_ranges, DW_FORM_sec_offset). + range2 = rnglists2.Size() - header_size; + rnglists2.D8(DW_RLE_base_addressx).ULEB128(0); // base_addr = 1 + rnglists2.D8(DW_RLE_startx_endx).ULEB128(1).ULEB128(2); // [2, 3) + rnglists2.D8(DW_RLE_startx_length).ULEB128(3).ULEB128(1); // [4, 5) + rnglists2.D8(DW_RLE_offset_pair).ULEB128(5).ULEB128(6); // [6, 7) + rnglists2.D8(DW_RLE_end_of_list); + + // Range 3 (will be read via DW_AT_ranges, DW_FORM_rnglistx). + range3 = rnglists2.Size() - header_size; + rnglists2.D8(DW_RLE_base_address).D32(15); // base_addr = 15 + rnglists2.D8(DW_RLE_offset_pair).ULEB128(1).ULEB128(2); // [16, 17) + rnglists2.D8(DW_RLE_start_end).D32(17).D32(18); // [17, 18) + rnglists2.D8(DW_RLE_start_length).D32(19).ULEB128(1); // [19, 20) + rnglists2.D8(DW_RLE_end_of_list); + // The size doesn't include the size of length field itself. + section_size2 = rnglists2.Size() - length_size; + + rnglists1.Append(rnglists2); + string rnglists_contents; + assert(rnglists1.GetContents(&rnglists_contents)); + + RangeListReader::CURangesInfo cu_info; + cu_info.version_ = 5; + cu_info.base_address_ = 1; + cu_info.ranges_base_ = ranges_base_1; + cu_info.buffer_ = + reinterpret_cast(rnglists_contents.data()); + cu_info.size_ = rnglists_contents.size(); + cu_info.addr_buffer_ = + reinterpret_cast(addr_contents.data()); + cu_info.addr_buffer_size_ = addr_contents.size(); + cu_info.addr_base_ = 4; + + ByteReader byte_reader(ENDIANNESS_BIG); + byte_reader.SetOffsetSize(4); + byte_reader.SetAddressSize(4); + MockRangeListHandler handler; + google_breakpad::RangeListReader range_list_reader1(&byte_reader, &cu_info, + &handler); + EXPECT_CALL(handler, AddRange(2, 3)); + EXPECT_CALL(handler, AddRange(4, 5)); + EXPECT_CALL(handler, AddRange(6, 7)); + EXPECT_CALL(handler, AddRange(9, 10)); + EXPECT_CALL(handler, AddRange(10, 11)); + EXPECT_CALL(handler, AddRange(12, 13)); + EXPECT_CALL(handler, Finish()).Times(2); + EXPECT_TRUE(range_list_reader1.ReadRanges(DW_FORM_rnglistx, 0)); + EXPECT_TRUE(range_list_reader1.ReadRanges(DW_FORM_rnglistx, 1)); + // Out of range index, should result in no calls. + EXPECT_FALSE(range_list_reader1.ReadRanges(DW_FORM_rnglistx, 2)); + + // Set to new ranges_base + cu_info.ranges_base_ = ranges_base_2; + google_breakpad::RangeListReader range_list_reader2(&byte_reader, &cu_info, + &handler); + EXPECT_CALL(handler, AddRange(2, 3)); + EXPECT_CALL(handler, AddRange(4, 5)); + EXPECT_CALL(handler, AddRange(6, 7)); + EXPECT_CALL(handler, AddRange(16, 17)); + EXPECT_CALL(handler, AddRange(17, 18)); + EXPECT_CALL(handler, AddRange(19, 20)); + EXPECT_CALL(handler, Finish()).Times(2); + EXPECT_TRUE(range_list_reader2.ReadRanges(DW_FORM_rnglistx, 0)); + EXPECT_TRUE(range_list_reader2.ReadRanges(DW_FORM_rnglistx, 1)); + // Out of range index, should result in no calls. + EXPECT_FALSE(range_list_reader2.ReadRanges(DW_FORM_rnglistx, 2)); +} + +TEST(RangeList, Dwarf5ReadRangeList_sec_offset) { + using google_breakpad::RangeListReader; + using google_breakpad::DW_RLE_base_addressx; + using google_breakpad::DW_RLE_startx_endx; + using google_breakpad::DW_RLE_startx_length; + using google_breakpad::DW_RLE_offset_pair; + using google_breakpad::DW_RLE_end_of_list; + using google_breakpad::DW_RLE_base_address; + using google_breakpad::DW_RLE_start_end; + using google_breakpad::DW_RLE_start_length; + using google_breakpad::DW_FORM_sec_offset; + using google_breakpad::DW_FORM_rnglistx; + + // Size of length field in header + const uint64_t length_size = 4; + + // .debug_addr for the indexed entries like startx. + Section addr; + addr.set_endianness(kBigEndian); + // Test addr_base handling with a padding address at 0. + addr.D32(0).D32(1).D32(2).D32(3).D32(4).D32(21).D32(22); + std::string addr_contents; + assert(addr.GetContents(&addr_contents)); + + // .debug_rnglists is the dwarf 5 section. + Section rnglists1(kBigEndian); + Section rnglists2(kBigEndian); + + // First header and body. + Label section_size1; + rnglists1.Append(kBigEndian, length_size, section_size1); + rnglists1.D16(5); // Version + rnglists1.D8(4); // Address size + rnglists1.D8(0); // Segment selector size + rnglists1.D32(0); // Offset entry count + + const uint64_t offset1 = rnglists1.Size(); + + rnglists1.D8(DW_RLE_base_addressx).ULEB128(0); // base_addr = 1 + rnglists1.D8(DW_RLE_startx_endx).ULEB128(1).ULEB128(2); // [2, 3) + rnglists1.D8(DW_RLE_startx_length).ULEB128(3).ULEB128(1); // [4, 5) + rnglists1.D8(DW_RLE_offset_pair).ULEB128(5).ULEB128(6); // [6, 7) + rnglists1.D8(DW_RLE_base_address).D32(8); // base_addr = 8 + rnglists1.D8(DW_RLE_offset_pair).ULEB128(1).ULEB128(2); // [9, 10) + rnglists1.D8(DW_RLE_start_end).D32(10).D32(11); // [10, 11) + rnglists1.D8(DW_RLE_start_length).D32(12).ULEB128(1); // [12, 13) + rnglists1.D8(DW_RLE_end_of_list); + // The size doesn't include the size of length field itself. + section_size1 = rnglists1.Size() - length_size; + + // Second header and body. + Label section_size2; + rnglists2.Append(kBigEndian, length_size, section_size2); + rnglists2.D16(5); // Version + rnglists2.D8(4); // Address size + rnglists2.D8(0); // Segment selector size + rnglists2.D32(0); // Offset entry count + + const uint64_t offset2 = rnglists1.Size() + rnglists2.Size(); + + rnglists2.D8(DW_RLE_base_addressx).ULEB128(0); // base_addr = 1 + rnglists2.D8(DW_RLE_startx_endx).ULEB128(1).ULEB128(2); // [2, 3) + rnglists2.D8(DW_RLE_startx_length).ULEB128(3).ULEB128(1); // [4, 5) + rnglists2.D8(DW_RLE_offset_pair).ULEB128(5).ULEB128(6); // [6, 7) + rnglists2.D8(DW_RLE_base_address).D32(15); // base_addr = 15 + rnglists2.D8(DW_RLE_offset_pair).ULEB128(1).ULEB128(2); // [16, 17) + rnglists2.D8(DW_RLE_start_end).D32(17).D32(18); // [17, 18) + rnglists2.D8(DW_RLE_start_length).D32(19).ULEB128(1); // [19, 20) + rnglists2.D8(DW_RLE_end_of_list); + // The size doesn't include the size of length field itself. + section_size2 = rnglists2.Size() - length_size; + + rnglists1.Append(rnglists2); + string rnglists_contents; + assert(rnglists1.GetContents(&rnglists_contents)); + + RangeListReader::CURangesInfo cu_info; + cu_info.version_ = 5; + cu_info.base_address_ = 1; + cu_info.buffer_ = + reinterpret_cast(rnglists_contents.data()); + cu_info.size_ = rnglists_contents.size(); + cu_info.addr_buffer_ = + reinterpret_cast(addr_contents.data()); + cu_info.addr_buffer_size_ = addr_contents.size(); + cu_info.addr_base_ = 4; + + ByteReader byte_reader(ENDIANNESS_BIG); + byte_reader.SetOffsetSize(4); + byte_reader.SetAddressSize(4); + MockRangeListHandler handler; + google_breakpad::RangeListReader range_list_reader(&byte_reader, &cu_info, + &handler); + EXPECT_CALL(handler, AddRange(2, 3)); + EXPECT_CALL(handler, AddRange(4, 5)); + EXPECT_CALL(handler, AddRange(6, 7)); + EXPECT_CALL(handler, AddRange(9, 10)); + EXPECT_CALL(handler, AddRange(10, 11)); + EXPECT_CALL(handler, AddRange(12, 13)); + EXPECT_CALL(handler, Finish()).Times(1); + EXPECT_TRUE(range_list_reader.ReadRanges(DW_FORM_sec_offset, offset1)); + // Out of range index, should result in no calls. + EXPECT_FALSE(range_list_reader.ReadRanges(DW_FORM_sec_offset, + rnglists_contents.size())); + + EXPECT_CALL(handler, AddRange(2, 3)); + EXPECT_CALL(handler, AddRange(4, 5)); + EXPECT_CALL(handler, AddRange(6, 7)); + EXPECT_CALL(handler, AddRange(16, 17)); + EXPECT_CALL(handler, AddRange(17, 18)); + EXPECT_CALL(handler, AddRange(19, 20)); + EXPECT_CALL(handler, Finish()).Times(1); + EXPECT_TRUE(range_list_reader.ReadRanges(DW_FORM_sec_offset, offset2)); + // Out of range index, should result in no calls. + EXPECT_FALSE(range_list_reader.ReadRanges(DW_FORM_sec_offset, + rnglists_contents.size())); +} diff --git a/shared/sentry/external/breakpad/src/common/dwarf/dwarf2reader_lineinfo_unittest.cc b/shared/sentry/external/breakpad/src/common/dwarf/dwarf2reader_lineinfo_unittest.cc new file mode 100644 index 000000000..8c0a1f07f --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/dwarf/dwarf2reader_lineinfo_unittest.cc @@ -0,0 +1,187 @@ +// Copyright (c) 2020, 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: Sterling Augustine + +// dwarf2reader_lineinfo_unittest.cc: Unit tests for google_breakpad::LineInfo + +#include +#include + +#include +#include + +#include "breakpad_googletest_includes.h" +#include "common/dwarf/bytereader.h" +#include "common/dwarf/dwarf2reader.h" +#include "google_breakpad/common/breakpad_types.h" + +using std::vector; +using testing::InSequence; +using testing::Return; +using testing::Sequence; +using testing::Test; +using testing::_; + +using namespace google_breakpad; + +namespace { + +const uint8_t dwarf5_line_program[] = { + 0x40, 0x0, 0x0, 0x0, // unit_length (end - begin) + // begin + 0x05, 0x0, // version + 0x8, // address_size + 0x0, // segment_selector_size + 0x26, 0x0, 0x0, 0x0, // header_length (end_header_end - begin_header) + // begin_header: + 0x1, // minimum_instruction_length + 0x1, // maximum_operations_per_instruction + 0x1, // default_is_stmt + 0xfb, // line_base + 0xe, // line_range + 0xd, // opcode_base and lengths + 0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x1, + 0x1, // directory entry format count + DW_LNCT_path, DW_FORM_strp, + 0x1, // directories count + 0x1, 0x0, 0x0, 0x0, // offset into .debug_line_str + 0x2, // file_name_entry_format_count + DW_LNCT_directory_index, DW_FORM_data1, + DW_LNCT_path, DW_FORM_line_strp, + 0x1, // filename count + 0x0, // directory index + 0x1, 0x0, 0x0, 0x0, // offset into .debug_str + // end_header + DW_LNS_set_file, 0x0, + // set address to 0x0 + 0x0, 0x9, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + // Advance Address by 0 and line by 3 + 0x15, + // Advance PC by 1 + 0x2, 0x1, + 0x0, + DW_LNE_end_sequence, + DW_LNE_end_sequence, + // end +}; + +const uint8_t dwarf4_line_program[] = { + 0x37, 0x0, 0x0, 0x0, // unit_length (end - begin) + // begin + 0x04, 0x0, // version + 0x1d, 0x0, 0x0, 0x0, // header_length (end_header - begin_header) + // begin_header: + 0x1, // minimum_instruction_length + 0x1, // maximum_operations_per_instruction + 0x1, // default_is_stmt + 0xfb, // line_base + 0xe, // line_range + 0xd, // opcode_base and lengths + 0x0, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x1, + '/', 'a', '\0', // directory entry 1 (zeroth entry implied) + '\0', // end of directory table + 'b', '/', 'c', '\0', // file entry 1 (zeroth entry implied) + 0, // file 1 directory + 0, // file 1 modification time + 0, // file 1 length + '\0', // end of file table + // end_header + DW_LNS_set_file, 0x0, + // set address to 0x0 + 0x0, 0x9, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + // Advance Address by 0 and line by 3 + 0x15, + // Advance PC by 1 + 0x2, 0x1, + 0x0, + DW_LNE_end_sequence, + DW_LNE_end_sequence, + // end +}; + +class MockLineInfoHandler: public LineInfoHandler { + public: + MOCK_METHOD(void, DefineDir, (const string&, uint32_t dir_num), (override)); + MOCK_METHOD(void, DefineFile, (const string& name, int32_t file_num, + uint32_t dir_num, uint64_t mod_time, + uint64_t length), (override)); + MOCK_METHOD(void, AddLine, (uint64_t address, uint64_t length, + uint32_t file_num, uint32_t line_num, + uint32_t column_num), (override)); +}; + +const uint8_t string_section[] = {'x', '/', 'a', '\0'}; +const uint8_t line_string_section[] = {'x', 'b', '/', 'c', '\0' }; + +struct LineProgram: public Test { + MockLineInfoHandler handler_; +}; + +TEST_F(LineProgram, ReadLinesDwarf5) { + ByteReader byte_reader(ENDIANNESS_LITTLE); + // LineTables don't specify the offset size like Compilation Units do. + byte_reader.SetOffsetSize(4); + LineInfo line_reader(dwarf5_line_program, + sizeof(dwarf5_line_program), + &byte_reader, + string_section, + sizeof(string_section), + line_string_section, + sizeof(line_string_section), + &handler_); + EXPECT_CALL(handler_, DefineDir("/a", 0)).Times(1); + EXPECT_CALL(handler_, DefineFile("b/c", 0, 0, 0, 0)).Times(1); + EXPECT_CALL(handler_, AddLine(0, 1, 0, 4, 0)).Times(1); + EXPECT_EQ(line_reader.Start(), sizeof(dwarf5_line_program)); +} + +TEST_F(LineProgram, ReadLinesDwarf4) { + ByteReader byte_reader(ENDIANNESS_LITTLE); + // LineTables don't specify the offset size like Compilation Units do. + byte_reader.SetOffsetSize(4); + // dwarf4 line info headers don't encode the address size. + byte_reader.SetAddressSize(8); + LineInfo line_reader(dwarf4_line_program, + sizeof(dwarf4_line_program), + &byte_reader, + // dwarf4 line tables can't access the string sections + // so pass values likely to make assertions fail if + // the code uses them improperly. + nullptr, 0, nullptr, 0, + &handler_); + EXPECT_CALL(handler_, DefineDir("", 0)).Times(1); + EXPECT_CALL(handler_, DefineDir("/a", 1)).Times(1); + EXPECT_CALL(handler_, DefineFile("", 0, 0, 0, 0)).Times(1); + EXPECT_CALL(handler_, DefineFile("b/c", 1, 0, 0, 0)).Times(1); + EXPECT_CALL(handler_, AddLine(0, 1, 0, 4, 0)).Times(1); + EXPECT_EQ(line_reader.Start(), sizeof(dwarf4_line_program)); +} + +} // anonymous namespace diff --git a/shared/sentry/external/breakpad/src/common/dwarf/dwarf2reader_splitfunctions_unittest.cc b/shared/sentry/external/breakpad/src/common/dwarf/dwarf2reader_splitfunctions_unittest.cc new file mode 100644 index 000000000..a13faa140 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/dwarf/dwarf2reader_splitfunctions_unittest.cc @@ -0,0 +1,126 @@ +// Copyright (c) 2020, 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: Snehasish Kumar + +// dwarf2reader_splitfunctions_unittest.cc: Unit tests for with a focus on debug +// information generated when with splitting optimizations such as +// -fsplit-machine-functions (clang) -freorder-blocks-and-partition (gcc). + +#include +#include + +#include + +#include "breakpad_googletest_includes.h" +#include "common/dwarf/bytereader.h" +#include "common/dwarf/dwarf2reader.h" +#include "google_breakpad/common/breakpad_types.h" + +using testing::_; +using namespace google_breakpad; + +namespace { + +class MockLineInfoHandler: public LineInfoHandler { + public: + MOCK_METHOD(void, DefineFile, (const string& name, int32_t file_num, + uint32_t dir_num, uint64_t mod_time, + uint64_t length), (override)); + MOCK_METHOD(void, AddLine, (uint64_t address, uint64_t length, + uint32_t file_num, uint32_t line_num, + uint32_t column_num), (override)); +}; + +struct LineProgram: public testing::Test { + MockLineInfoHandler handler_; +}; + +// The debug information is generated from the following program -- +// $ cat -n split_functions.c +// 1 #include +// 2 +// 3 __attribute__((noinline)) int foo(int i) { +// 4 if (i % 100) { +// 5 return i + 1; +// 6 } else { +// 7 return i * 10 % 3; +// 8 } +// 9 } +// 10 +// 11 +// 12 int main(int argc, char *argv[]) { +// 13 int total = 0; +// 14 for (int i = 0; i < 1000; ++i) { +// 15 total += foo(i); +// 16 } +// 17 printf("%d\n", total); +// 18 } +// +// $ bin/clang -fprofile-generate -O2 split_functions.c +// $ ./a.out > /dev/null +// $ bin/llvm-profdata merge -o default.profdata default_*.profraw +// $ bin/clang -fprofile-use -O2 -gmlt -gdwarf-5 -fsplit-machine-functions \ +// split_functions.c -o split.out +// +// For the test we pick the first instruction in foo.cold which should be the +// else part of the function foo above. + +const uint8_t debug_line[] = { + 0xb0,0x0,0x0,0x0,0x5,0x0,0x8,0x0,0x37,0x0,0x0,0x0,0x1,0x1,0x1,0xfb,0xe,0xd,0x0,0x1,0x1,0x1,0x1,0x0,0x0,0x0,0x1,0x0,0x0,0x1,0x1,0x1,0x1f,0x1,0x0,0x0,0x0,0x0,0x3,0x1,0x1f,0x2,0xf,0x5,0x1e,0x1,0x3d,0x0,0x0,0x0,0x0,0x24,0xb2,0xb6,0xb5,0xbb,0xf,0xf7,0x6d,0x27,0x92,0xab,0x55,0x3a,0x29,0x48,0x81,0x4,0x0,0x0,0x9,0x2,0x40,0x10,0x40,0x0,0x0,0x0,0x0,0x0,0x14,0x5,0x9,0xa,0x2f,0x5,0x7,0x6,0x8,0x4a,0x5,0xe,0x6,0x67,0x5,0x1,0x40,0x5,0x0,0xf5,0x5,0xe,0xa,0xf5,0x5,0xb,0x6,0x74,0x5,0x1d,0x6,0x2d,0x5,0x15,0x6,0x3c,0x5,0x3,0x66,0x2,0x7,0x0,0x1,0x1,0x4,0x0,0x5,0xe,0x0,0x9,0x2,0x84,0x11,0x40,0x0,0x0,0x0,0x0,0x0,0x18,0x5,0x13,0x6,0x58,0x5,0x1,0x6,0x8,0xa0,0x2,0x1,0x0,0x1,0x1,0x4,0x0,0x5,0x3,0x0,0x9,0x2,0xa5,0x11,0x40,0x0,0x0,0x0,0x0,0x0,0x3,0x10,0x1,0x5,0x1,0xd7,0x2,0x9,0x0,0x1,0x1 +}; + +const uint8_t debug_str[] = { + 0x63,0x6c,0x61,0x6e,0x67,0x20,0x76,0x65,0x72,0x73,0x69,0x6f,0x6e,0x20,0x31,0x32,0x2e,0x30,0x2e,0x30,0x20,0x28,0x67,0x69,0x74,0x40,0x67,0x69,0x74,0x68,0x75,0x62,0x2e,0x63,0x6f,0x6d,0x3a,0x6c,0x6c,0x76,0x6d,0x2f,0x6c,0x6c,0x76,0x6d,0x2d,0x70,0x72,0x6f,0x6a,0x65,0x63,0x74,0x2e,0x67,0x69,0x74,0x20,0x63,0x37,0x35,0x61,0x30,0x61,0x31,0x65,0x39,0x64,0x63,0x32,0x39,0x62,0x65,0x34,0x65,0x30,0x30,0x64,0x33,0x37,0x64,0x30,0x64,0x30,0x30,0x32,0x38,0x38,0x61,0x66,0x63,0x31,0x61,0x36,0x31,0x35,0x33,0x66,0x29,0x0,0x73,0x70,0x6c,0x69,0x74,0x5f,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x73,0x2e,0x63,0x0,0x2f,0x75,0x73,0x72,0x2f,0x6c,0x6f,0x63,0x61,0x6c,0x2f,0x67,0x6f,0x6f,0x67,0x6c,0x65,0x2f,0x68,0x6f,0x6d,0x65,0x2f,0x73,0x6e,0x65,0x68,0x61,0x73,0x69,0x73,0x68,0x6b,0x2f,0x77,0x6f,0x72,0x6b,0x69,0x6e,0x67,0x2f,0x6c,0x6c,0x76,0x6d,0x2d,0x70,0x72,0x6f,0x6a,0x65,0x63,0x74,0x2f,0x62,0x75,0x69,0x6c,0x64,0x0,0x66,0x6f,0x6f,0x0,0x69,0x6e,0x74,0x0,0x6d,0x61,0x69,0x6e,0x0,0x69,0x0,0x61,0x72,0x67,0x63,0x0,0x61,0x72,0x67,0x76,0x0,0x63,0x68,0x61,0x72,0x0,0x74,0x6f,0x74,0x61,0x6c,0x0 +}; + +const uint8_t debug_line_str[] = { + 0x2f,0x75,0x73,0x72,0x2f,0x6c,0x6f,0x63,0x61,0x6c,0x2f,0x67,0x6f,0x6f,0x67,0x6c,0x65,0x2f,0x68,0x6f,0x6d,0x65,0x2f,0x73,0x6e,0x65,0x68,0x61,0x73,0x69,0x73,0x68,0x6b,0x2f,0x77,0x6f,0x72,0x6b,0x69,0x6e,0x67,0x2f,0x6c,0x6c,0x76,0x6d,0x2d,0x70,0x72,0x6f,0x6a,0x65,0x63,0x74,0x2f,0x62,0x75,0x69,0x6c,0x64,0x0,0x73,0x70,0x6c,0x69,0x74,0x5f,0x66,0x75,0x6e,0x63,0x74,0x69,0x6f,0x6e,0x73,0x2e,0x63,0x0 +}; + +TEST_F(LineProgram, ReadLinesSplitFunctions) { + ByteReader byte_reader(ENDIANNESS_LITTLE); + // LineTables don't specify the offset size like Compilation Units do. + byte_reader.SetOffsetSize(4); + LineInfo line_reader(debug_line, + sizeof(debug_line), + &byte_reader, + debug_str, + sizeof(debug_str), + debug_line_str, + sizeof(debug_line_str), + &handler_); + EXPECT_CALL(handler_, DefineFile("split_functions.c", 0, 0, 0, 0)).Times(1); + EXPECT_CALL(handler_, AddLine(_, _, _, _, _)).Times(testing::AtLeast(1)); + // Pick the first address from the foo.cold symbol and check the line number. + EXPECT_CALL(handler_, AddLine(testing::Eq(0x401184lu), _, _, /*line_num*/ 7, _)).Times(1); + EXPECT_EQ(line_reader.Start(), sizeof(debug_line)); +} + +} // anonymous namespace diff --git a/shared/sentry/external/breakpad/src/common/dwarf/dwarf2reader_test_common.h b/shared/sentry/external/breakpad/src/common/dwarf/dwarf2reader_test_common.h new file mode 100644 index 000000000..b84cff010 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/dwarf/dwarf2reader_test_common.h @@ -0,0 +1,164 @@ +// -*- 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 + +// 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 google_breakpad::DwarfTag DwarfTag; + typedef google_breakpad::DwarfAttribute DwarfAttribute; + typedef google_breakpad::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 + 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, int header_type) { + if (format_size_ == 4) { + D32(length_); + } else { + D32(0xffffffff); + D64(length_); + } + post_length_offset_ = Size(); + D16(version); + if (version <= 4) { + SectionOffset(abbrev_offset); + D8(address_size); + } else { + D8(header_type); // DW_UT_compile, DW_UT_type, etc. + D8(address_size); + SectionOffset(abbrev_offset); + if (header_type == google_breakpad::DW_UT_type) { + uint64_t dummy_type_signature = 0xdeadbeef; + uint64_t dummy_type_offset = 0x2b; + D64(dummy_type_signature); + if (format_size_ == 4) + D32(dummy_type_offset); + else + D64(dummy_type_offset); + } + } + 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 google_breakpad::DwarfTag DwarfTag; + typedef google_breakpad::DwarfAttribute DwarfAttribute; + typedef google_breakpad::DwarfForm DwarfForm; + typedef google_breakpad::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(tag)); + D8(static_cast(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(name)); + ULEB128(static_cast(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/shared/sentry/external/breakpad/src/common/dwarf/elf_reader.cc b/shared/sentry/external/breakpad/src/common/dwarf/elf_reader.cc new file mode 100644 index 000000000..c6d9f08a9 --- /dev/null +++ b/shared/sentry/external/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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +// 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__ +#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 +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 google_breakpad { + +template 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 ElfSectionReader { + public: + ElfSectionReader(const char* name, const string& path, int fd, + const typename ElfArch::Shdr& section_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(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 SymbolIterator { + public: + SymbolIterator(ElfReaderImpl* 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( + 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* const symbol_section_; + const ElfSectionReader* 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 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_t mem_offset, + uint64_t file_offset) { + // This map is used to filter out "nested" functions. + // See comment below. + AddrToSymMap addr_to_sym_map; + for (SymbolIterator 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_t expected_base = hdr.sh_addr - hdr.sh_offset; + const int64_t real_base = mem_offset - file_offset; + const int64_t adjust = real_base - expected_base; + + uint64_t 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_t 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_t 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 : cmpl $0x0,0x2781b9(%rip) + // 0x00000000000aa387 : jne 0xaa39b + // 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 + // 0x00000000000aa39a <__read_nocancel+17>: retq + // 0x00000000000aa39b : sub $0x28,%rsp + // 0x00000000000aa39f : 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* const symbol_section = + this->GetSectionByType(section_type); + const ElfSectionReader* 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_t prev_addr = prev->first; + const uint64_t 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 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(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(rel_section); + const typename ElfArch::Rela* rela = + reinterpret_cast(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(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* 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* 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& section_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* 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& section_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* 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_t 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 > 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* 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* 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*& reader = sections_[num]; + if (reader == NULL) + reader = new ElfSectionReader(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_t AdjustPPC64FunctionDescriptorSymbolValue(uint64_t value) { + if (opd_section_ != NULL && + opd_info_.addr <= value && + value < opd_info_.addr + opd_info_.size) { + uint64_t offset = value - opd_info_.addr; + return (*reinterpret_cast(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; + + // 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*> 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_t 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 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 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 +static bool IsElfFile(const int fd, const string& path) { + if (fd < 0) + return false; + if (!ElfReaderImpl::IsArchElfFile(fd, NULL)) { + // No error message here. IsElfFile gets called many times. + return false; + } + return true; +} + +bool ElfReader::IsNativeElfFile() const { + return IsElfFile(fd_, path_); +} + +bool ElfReader::IsElf32File() const { + return IsElfFile(fd_, path_); +} + +bool ElfReader::IsElf64File() const { + return IsElfFile(fd_, path_); +} + +/* +void ElfReader::AddSymbols(SymbolMap* symbols, + uint64_t mem_offset, uint64_t file_offset, + uint64_t 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_t 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(shndx) >= GetNumSections()) return NULL; + if (IsElf32File()) { + return GetImpl32()->GetSectionNameByIndex(shndx); + } else if (IsElf64File()) { + return GetImpl64()->GetSectionNameByIndex(shndx); + } else { + return NULL; + } +} + +uint64_t 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& section_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& section_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* ElfReader::GetImpl32() { + if (impl32_ == NULL) { + impl32_ = new ElfReaderImpl(path_, fd_); + } + return impl32_; +} + +ElfReaderImpl* ElfReader::GetImpl64() { + if (impl64_ == NULL) { + impl64_ = new ElfReaderImpl(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 +static bool IsNonStrippedELFBinaryImpl(const string& path, const int fd, + bool debug_only) { + if (!ElfReaderImpl::IsArchElfFile(fd, NULL)) return false; + ElfReaderImpl 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(path, fd, debug_only) || + IsNonStrippedELFBinaryImpl(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 google_breakpad diff --git a/shared/sentry/external/breakpad/src/common/dwarf/elf_reader.h b/shared/sentry/external/breakpad/src/common/dwarf/elf_reader.h new file mode 100644 index 000000000..13ac39beb --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/dwarf/elf_reader.h @@ -0,0 +1,167 @@ +// 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 +#include + +#include "common/dwarf/types.h" +#include "common/using_std_string.h" + +using std::vector; +using std::pair; + +namespace google_breakpad { + +class SymbolMap; +class Elf32; +class Elf64; +template +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_t mem_offset, uint64_t file_offset, + uint64_t length); + + class SymbolSink { + public: + virtual ~SymbolSink() {} + virtual void AddSymbol(const char* name, uint64_t address, + uint64_t 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_t 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_t 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& section_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 , but is redefined + // here so that the many short macro names in don't have to be + // added to our already cluttered namespace. + struct SectionInfo { + uint32_t type; // Section type (SHT_xxx constant from elf.h). + uint64_t flags; // Section flags (SHF_xxx constants from elf.h). + uint64_t addr; // Section virtual address at execution. + uint64_t offset; // Section file offset. + uint64_t size; // Section size in bytes. + uint32_t link; // Link to another section. + uint32_t info; // Additional section information. + uint64_t addralign; // Section alignment. + uint64_t entsize; // Entry size if section holds a table. + }; + const char* GetSectionInfoByName(const string& section_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* GetImpl32(); + // Ditto for impl64_. + ElfReaderImpl* 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* impl32_; + ElfReaderImpl* impl64_; +}; + +} // namespace google_breakpad + +#endif // COMMON_DWARF_ELF_READER_H__ diff --git a/shared/sentry/external/breakpad/src/common/dwarf/functioninfo.cc b/shared/sentry/external/breakpad/src/common/dwarf/functioninfo.cc new file mode 100644 index 000000000..57f843bb0 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/dwarf/functioninfo.cc @@ -0,0 +1,228 @@ +// 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 +#include +#include + +#include +#include +#include + +#include "common/dwarf/functioninfo.h" +#include "common/dwarf/bytereader.h" +#include "common/scoped_ptr.h" +#include "common/using_std_string.h" + +namespace google_breakpad { + +CULineInfoHandler::CULineInfoHandler(std::vector* files, + std::vector* dirs, + LineMap* linemap):linemap_(linemap), + files_(files), + dirs_(dirs) { + // In dwarf4, the dirs and files are 1 indexed, and in dwarf5 they are zero + // indexed. This is handled in the LineInfo reader, so empty files are not + // needed here. +} + +void CULineInfoHandler::DefineDir(const string& name, uint32_t 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_t dir_num, + uint64_t mod_time, uint64_t 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_t address, uint64_t length, + uint32_t file_num, uint32_t line_num, + uint32_t 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_t offset, + uint8_t address_size, + uint8_t offset_size, + uint64_t cu_length, + uint8_t 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_t 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_t 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_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t data) { + if (attr == DW_AT_stmt_list) { + SectionMap::const_iterator iter = + GetSectionByName(sections_, ".debug_line"); + assert(iter != sections_.end()); + + scoped_ptr 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_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t 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_t offset) { + if (current_function_info_ && current_function_info_->lowpc) + address_to_funcinfo_->insert(std::make_pair(current_function_info_->lowpc, + current_function_info_)); +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/common/dwarf/functioninfo.h b/shared/sentry/external/breakpad/src/common/dwarf/functioninfo.h new file mode 100644 index 000000000..a6f05af6e --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/dwarf/functioninfo.h @@ -0,0 +1,191 @@ +// 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 +#include +#include +#include + +#include "common/dwarf/dwarf2reader.h" +#include "common/using_std_string.h" + + +namespace google_breakpad { + +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_t line; + // Beginning address for this function + uint64_t lowpc; + // End address for this function. + uint64_t highpc; + // Ranges offset + uint64_t ranges; +}; + +struct SourceFileInfo { + // Name of the source file name + string name; + // Low address of source file name + uint64_t lowpc; +}; + +typedef std::map FunctionMap; +typedef std::map > 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* files, + std::vector* 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_t 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_t dir_num, uint64_t mod_time, uint64_t 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_t address, uint64_t length, + uint32_t file_num, uint32_t line_num, + uint32_t column_num); + + private: + LineMap* linemap_; + std::vector* files_; + std::vector* dirs_; +}; + +class CUFunctionInfoHandler: public Dwarf2Handler { + public: + CUFunctionInfoHandler(std::vector* files, + std::vector* 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_t offset, uint8_t address_size, + uint8_t offset_size, uint64_t cu_length, + uint8_t 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_t 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_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t 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_t offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t 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_t 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_t offset); + + private: + std::vector* files_; + std::vector* dirs_; + LineMap* linemap_; + FunctionMap* offset_to_funcinfo_; + FunctionMap* address_to_funcinfo_; + CULineInfoHandler* linehandler_; + const SectionMap& sections_; + ByteReader* reader_; + FunctionInfo* current_function_info_; + uint64_t current_compilation_unit_offset_; +}; + +} // namespace google_breakpad +#endif // COMMON_DWARF_FUNCTIONINFO_H__ diff --git a/shared/sentry/external/breakpad/src/common/dwarf/line_state_machine.h b/shared/sentry/external/breakpad/src/common/dwarf/line_state_machine.h new file mode 100644 index 000000000..b0f3f4907 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/dwarf/line_state_machine.h @@ -0,0 +1,63 @@ +// 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__ + +#include + +namespace google_breakpad { + +// 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_t file_num; + uint64_t address; + uint32_t line_num; + uint32_t column_num; + bool is_stmt; // stmt means statement. + bool basic_block; + bool end_sequence; +}; + +} // namespace google_breakpad + + +#endif // COMMON_DWARF_LINE_STATE_MACHINE_H__ diff --git a/shared/sentry/external/breakpad/src/common/dwarf/types.h b/shared/sentry/external/breakpad/src/common/dwarf/types.h new file mode 100644 index 000000000..23412d0ea --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/dwarf/types.h @@ -0,0 +1,41 @@ +// 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 + +typedef intptr_t intptr; +typedef uintptr_t uintptr; + +#endif // _COMMON_DWARF_TYPES_H__ diff --git a/shared/sentry/external/breakpad/src/common/dwarf_cfi_to_module.cc b/shared/sentry/external/breakpad/src/common/dwarf_cfi_to_module.cc new file mode 100644 index 000000000..d7d19834b --- /dev/null +++ b/shared/sentry/external/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 + +// Implementation of google_breakpad::DwarfCFIToModule. +// See dwarf_cfi_to_module.h for details. + +#include + +#include "common/dwarf_cfi_to_module.h" + +namespace google_breakpad { + +using std::ostringstream; + +vector DwarfCFIToModule::RegisterNames::MakeVector( + const char * const *strings, + size_t size) { + vector names(strings, strings + size); + return names; +} + +vector 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 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 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 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 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_t address, uint64_t length, + uint8_t version, const string& augmentation, + unsigned return_address) { + assert(!entry_); + + // If 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, 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_t address, int reg) { + reporter_->UndefinedNotSupported(entry_offset_, RegisterName(reg)); + // Treat this as a non-fatal error. + return true; +} + +bool DwarfCFIToModule::SameValueRule(uint64_t address, int reg) { + ostringstream s; + s << RegisterName(reg); + Record(address, reg, s.str()); + return true; +} + +bool DwarfCFIToModule::OffsetRule(uint64_t 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_t 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_t address, int reg, + int base_register) { + ostringstream s; + s << RegisterName(base_register); + Record(address, reg, s.str()); + return true; +} + +bool DwarfCFIToModule::ExpressionRule(uint64_t 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_t 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& reg) { + 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& reg) { + 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/shared/sentry/external/breakpad/src/common/dwarf_cfi_to_module.h b/shared/sentry/external/breakpad/src/common/dwarf_cfi_to_module.h new file mode 100644 index 000000000..3e2e6ffec --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/dwarf_cfi_to_module.h @@ -0,0 +1,201 @@ +// -*- 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 + +// 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 +#include + +#include +#include +#include + +#include "common/module.h" +#include "common/dwarf/dwarf2reader.h" +#include "common/using_std_string.h" + +namespace google_breakpad { + +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& section) + : 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& reg); + + // 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& reg); + + 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 I386(); + + // AMD x86_64, AMD64, Intel EM64T, or Intel 64 + static vector X86_64(); + + // ARM. + static vector ARM(); + + // ARM64, aka AARCH64. + static vector ARM64(); + + // MIPS. + static vector MIPS(); + + private: + // Given STRINGS, an array of C strings with SIZE elements, return an + // equivalent vector. + static vector MakeVector(const char* const* strings, size_t size); + }; + + // Create a handler for the 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& register_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_t address, uint64_t length, + uint8_t version, const string& augmentation, + unsigned return_address); + virtual bool UndefinedRule(uint64_t address, int reg); + virtual bool SameValueRule(uint64_t address, int reg); + virtual bool OffsetRule(uint64_t address, int reg, + int base_register, long offset); + virtual bool ValOffsetRule(uint64_t address, int reg, + int base_register, long offset); + virtual bool RegisterRule(uint64_t address, int reg, int base_register); + virtual bool ExpressionRule(uint64_t address, int reg, + const string& expression); + virtual bool ValExpressionRule(uint64_t 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& register_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, for example), + // causes those strings to share a single instance of each distinct piece + // of text. + set common_strings_; +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_DWARF_CFI_TO_MODULE_H diff --git a/shared/sentry/external/breakpad/src/common/dwarf_cfi_to_module_unittest.cc b/shared/sentry/external/breakpad/src/common/dwarf_cfi_to_module_unittest.cc new file mode 100644 index 000000000..58c3cca3a --- /dev/null +++ b/shared/sentry/external/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 + +// dwarf_cfi_to_module_unittest.cc: Tests for google_breakpad::DwarfCFIToModule. + +#include +#include + +#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& section) + : Reporter(file, section) { } + MOCK_METHOD2(UnnamedRegister, void(size_t offset, int reg)); + MOCK_METHOD2(UndefinedNotSupported, void(size_t offset, const string& reg)); + MOCK_METHOD2(ExpressionsNotSupported, void(size_t offset, const string& reg)); +}; + +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 register_names; + MockCFIReporter reporter; + DwarfCFIToModule handler; + vector 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_t 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 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 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 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/shared/sentry/external/breakpad/src/common/dwarf_cu_to_module.cc b/shared/sentry/external/breakpad/src/common/dwarf_cu_to_module.cc new file mode 100644 index 000000000..3435e5b1d --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/dwarf_cu_to_module.cc @@ -0,0 +1,1515 @@ +// 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 + +// Implement the DwarfCUToModule class; see dwarf_cu_to_module.h. + +// For 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 +#include +#include +#include + +#include +#include +#include +#include + +#include "common/string_view.h" +#include "common/dwarf_line_to_module.h" +#include "google_breakpad/common/breakpad_types.h" + +namespace google_breakpad { + +using std::accumulate; +using std::map; +using std::pair; +using std::sort; +using std::vector; +using std::unique_ptr; + +// 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. + StringView qualified_name; + + // The name of the enclosing scope, or the empty string if there is none. + StringView enclosing_name; + + // The name for the specification DIE itself, without any enclosing + // name components. + StringView unqualified_name; +}; + +// An abstract origin -- base definition of an inline function. +struct AbstractOrigin { + explicit AbstractOrigin(StringView name) : name(name) {} + + StringView name; +}; + +typedef map AbstractOriginByOffset; + +// Data global to the DWARF-bearing file that is private to the +// DWARF-to-Module process. +struct DwarfCUToModule::FilePrivate { + // 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; + + // Keep a list of forward references from DW_AT_abstract_origin and + // DW_AT_specification attributes so names can be fixed up. + std::map forward_ref_die_to_func; +}; + +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_t length) { + section_map_[name] = std::make_pair(contents, length); +} + +void DwarfCUToModule::FileContext::ClearSectionMapForTest() { + section_map_.clear(); +} + +const 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_t offset, uint64_t 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) + : version(0), + file_context(file_context_arg), + reporter(reporter_arg), + ranges_handler(ranges_handler_arg), + language(Language::CPlusPlus), + low_pc(0), + high_pc(0), + ranges_form(DW_FORM_sec_offset), + ranges_data(0), + ranges_base(0), + str_offsets_base(0) { } + + ~CUContext() { + for (vector::iterator it = functions.begin(); + it != functions.end(); ++it) { + delete *it; + } + }; + + // Dwarf version of the source CU. + uint8_t version; + + // 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_data is non-zero and low_pc represents + // the base address of the ranges covered by the CU. ranges_data will define + // the CU's actual ranges. + uint64_t low_pc; + uint64_t high_pc; + + // Ranges for this CU are read according to this form. + enum DwarfForm ranges_form; + uint64_t ranges_data; + + // Offset into .debug_rngslists where this CU's ranges are stored. + // Data in DW_FORM_rnglistx is relative to this offset. + uint64_t ranges_base; + + // Offset into .debug_addr where this CU's addresses are stored. Data in + // form DW_FORM_addrxX is relative to this offset. + uint64_t addr_base; + + // Offset into this CU's contribution to .debug_str_offsets. + uint64_t str_offsets_base; + + // Collect all the data from the CU that a RangeListReader needs to read a + // range. + bool AssembleRangeListInfo( + RangeListReader::CURangesInfo* info) { + const SectionMap& section_map + = file_context->section_map(); + info->version_ = version; + info->base_address_ = low_pc; + info->ranges_base_ = ranges_base; + const char* section_name = (version <= 4 ? + ".debug_ranges" : ".debug_rnglists"); + SectionMap::const_iterator map_entry + = GetSectionByName(section_map, section_name); + if (map_entry == section_map.end()) { + return false; + } + info->buffer_ = map_entry->second.first; + info->size_ = map_entry->second.second; + if (version > 4) { + SectionMap::const_iterator map_entry + = GetSectionByName(section_map, ".debug_addr"); + if (map_entry == section_map.end()) { + return false; + } + info->addr_buffer_ = map_entry->second.first; + info->addr_buffer_size_ = map_entry->second.second; + info->addr_base_ = addr_base; + } + return true; + } + + // 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 functions; + + // A map of function pointers to the its forward specification DIE's offset. + map spec_function_offsets; +}; + +// 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 "". + StringView name; +}; + +// An abstract base class for all the dumper's DIE handlers. +class DwarfCUToModule::GenericDIEHandler: public 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_t offset) + : cu_context_(cu_context), + parent_context_(parent_context), + offset_(offset), + declaration_(false), + specification_(NULL), + abstract_origin_(NULL), + forward_ref_die_offset_(0), specification_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_t 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_t 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. + StringView ComputeQualifiedName(); + + CUContext* cu_context_; + DIEContext* parent_context_; + uint64_t offset_; + + // 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_abstract_origin attribute, this is the + // AbstractOrigin structure for the DIE the attribute refers to. + // Otherwise, this is NULL. + const AbstractOrigin* abstract_origin_; + + // 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 root offset of Specification or abstract origin. + uint64_t specification_offset_; + + // The value of the DW_AT_name attribute, or the empty string if the + // DIE has no such attribute. + StringView 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. + StringView demangled_name_; + + // The non-demangled value of the DW_AT_MIPS_linkage_name attribute, + // it its content count not be demangled. + StringView raw_name_; +}; + +void DwarfCUToModule::GenericDIEHandler::ProcessAttributeUnsigned( + enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t data) { + switch (attr) { + case DW_AT_declaration: declaration_ = (data != 0); break; + default: break; + } +} + +void DwarfCUToModule::GenericDIEHandler::ProcessAttributeReference( + enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t data) { + switch (attr) { + case 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); + } + specification_offset_ = data; + break; + } + case 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; + } + specification_offset_ = data; + break; + } + default: break; + } +} + +void DwarfCUToModule::GenericDIEHandler::ProcessAttributeString( + enum DwarfAttribute attr, + enum DwarfForm form, + const string& data) { + switch (attr) { + case DW_AT_name: + name_attribute_ = + cu_context_->file_context->module_->AddStringToPool(data); + break; + case DW_AT_MIPS_linkage_name: + case DW_AT_linkage_name: { + string demangled; + Language::DemangleResult result = + cu_context_->language->DemangleName(data, &demangled); + switch (result) { + case Language::kDemangleSuccess: + demangled_name_ = + cu_context_->file_context->module_->AddStringToPool(demangled); + break; + + case Language::kDemangleFailure: + cu_context_->reporter->DemangleError(data); + // fallthrough + case Language::kDontDemangle: + demangled_name_ = StringView(); + raw_name_ = cu_context_->file_context->module_->AddStringToPool(data); + break; + } + break; + } + default: break; + } +} + +StringView 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. + StringView* qualified_name = nullptr; + 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; + } + + StringView* unqualified_name = nullptr; + StringView* enclosing_name = nullptr; + 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->str(); + } 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->str(), unqualified_name->str()); + } + + // 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 cu_context_->file_context->module_->AddStringToPool(return_value); +} + +static bool IsEmptyRange(const vector& ranges) { + uint64_t size = accumulate(ranges.cbegin(), ranges.cend(), 0, + [](uint64_t total, Module::Range entry) { + return total + entry.size; + } + ); + + return size == 0; +} + + +// A handler for DW_TAG_inlined_subroutine DIEs. +class DwarfCUToModule::InlineHandler : public GenericDIEHandler { + public: + InlineHandler(CUContext* cu_context, + DIEContext* parent_context, + uint64_t offset, + int inline_nest_level, + vector>& inlines) + : GenericDIEHandler(cu_context, parent_context, offset), + low_pc_(0), + high_pc_(0), + high_pc_form_(DW_FORM_addr), + ranges_form_(DW_FORM_sec_offset), + ranges_data_(0), + call_site_line_(0), + inline_nest_level_(inline_nest_level), + inlines_(inlines) {} + + void ProcessAttributeUnsigned(enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t data); + DIEHandler* FindChildHandler(uint64_t offset, enum DwarfTag tag); + bool EndAttributes(); + void Finish(); + + private: + // The fully-qualified name, as derived from name_attribute_, + // specification_, parent_context_. Computed in EndAttributes. + StringView name_; + uint64_t low_pc_; // DW_AT_low_pc + uint64_t high_pc_; // DW_AT_high_pc + DwarfForm high_pc_form_; // DW_AT_high_pc can be length or address. + DwarfForm ranges_form_; // DW_FORM_sec_offset or DW_FORM_rnglistx + uint64_t ranges_data_; // DW_AT_ranges + int call_site_line_; // DW_AT_call_line + int call_site_file_id_; // DW_AT_call_file + int inline_nest_level_; + // A vector of inlines in the same nest level. It's owned by its parent + // function/inline. At Finish(), add this inline into the vector. + vector>& inlines_; + // A vector of child inlines. + vector> child_inlines_; +}; + +void DwarfCUToModule::InlineHandler::ProcessAttributeUnsigned( + enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t data) { + switch (attr) { + case DW_AT_low_pc: + low_pc_ = data; + break; + case DW_AT_high_pc: + high_pc_form_ = form; + high_pc_ = data; + break; + case DW_AT_ranges: + ranges_data_ = data; + ranges_form_ = form; + break; + case DW_AT_call_line: + call_site_line_ = data; + break; + case DW_AT_call_file: + call_site_file_id_ = data; + break; + default: + GenericDIEHandler::ProcessAttributeUnsigned(attr, form, data); + break; + } +} + +DIEHandler* DwarfCUToModule::InlineHandler::FindChildHandler( + uint64_t offset, + enum DwarfTag tag) { + switch (tag) { + case DW_TAG_inlined_subroutine: + return new InlineHandler(cu_context_, new DIEContext(), offset, + inline_nest_level_ + 1, child_inlines_); + default: + return NULL; + } +} + +bool DwarfCUToModule::InlineHandler::EndAttributes() { + if (abstract_origin_) + name_ = abstract_origin_->name; + if (name_.empty()) { + // We haven't seen the abstract origin yet, which might appears later and we + // will fix the name after calling + // InlineOriginMap::GetOrCreateInlineOrigin with right name. + name_ = + cu_context_->file_context->module_->AddStringToPool(""); + } + return true; +} + +void DwarfCUToModule::InlineHandler::Finish() { + vector ranges; + + if (low_pc_ && high_pc_) { + if (high_pc_form_ != DW_FORM_addr && + high_pc_form_ != DW_FORM_GNU_addr_index && + high_pc_form_ != DW_FORM_addrx && + high_pc_form_ != DW_FORM_addrx1 && + high_pc_form_ != DW_FORM_addrx2 && + high_pc_form_ != DW_FORM_addrx3 && + high_pc_form_ != DW_FORM_addrx4) { + 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) { + RangeListReader::CURangesInfo cu_info; + if (cu_context_->AssembleRangeListInfo(&cu_info)) { + if (!ranges_handler->ReadRanges(ranges_form_, ranges_data_, + &cu_info, &ranges)) { + ranges.clear(); + cu_context_->reporter->MalformedRangeList(ranges_data_); + } + } else { + cu_context_->reporter->MissingRanges(); + } + } + } + + // Ignore DW_TAG_inlined_subroutine with empty range. + if (ranges.empty()) { + return; + } + + // Every DW_TAG_inlined_subroutine should have a DW_AT_abstract_origin. + assert(specification_offset_ != 0); + + cu_context_->file_context->module_->inline_origin_map.SetReference( + specification_offset_, specification_offset_); + Module::InlineOrigin* origin = + cu_context_->file_context->module_->inline_origin_map + .GetOrCreateInlineOrigin(specification_offset_, name_); + unique_ptr in( + new Module::Inline(origin, ranges, call_site_line_, call_site_file_id_, + inline_nest_level_, std::move(child_inlines_))); + inlines_.push_back(std::move(in)); +} + +// A handler class for DW_TAG_subprogram DIEs. +class DwarfCUToModule::FuncHandler: public GenericDIEHandler { + public: + FuncHandler(CUContext* cu_context, + DIEContext* parent_context, + uint64_t offset, + bool handle_inline) + : GenericDIEHandler(cu_context, parent_context, offset), + low_pc_(0), + high_pc_(0), + high_pc_form_(DW_FORM_addr), + ranges_form_(DW_FORM_sec_offset), + ranges_data_(0), + inline_(false), + handle_inline_(handle_inline) {} + + void ProcessAttributeUnsigned(enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t data); + void ProcessAttributeSigned(enum DwarfAttribute attr, + enum DwarfForm form, + int64_t data); + DIEHandler* FindChildHandler(uint64_t offset, enum DwarfTag tag); + bool EndAttributes(); + void Finish(); + + private: + // The fully-qualified name, as derived from name_attribute_, + // specification_, parent_context_. Computed in EndAttributes. + StringView name_; + uint64_t 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. + DwarfForm ranges_form_; // DW_FORM_sec_offset or DW_FORM_rnglistx + uint64_t ranges_data_; // DW_AT_ranges + bool inline_; + vector> child_inlines_; + bool handle_inline_; +}; + +void DwarfCUToModule::FuncHandler::ProcessAttributeUnsigned( + enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t 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 DW_AT_inline: inline_ = true; break; + + case DW_AT_low_pc: low_pc_ = data; break; + case DW_AT_high_pc: + high_pc_form_ = form; + high_pc_ = data; + break; + case DW_AT_ranges: + ranges_data_ = data; + ranges_form_ = form; + break; + default: + GenericDIEHandler::ProcessAttributeUnsigned(attr, form, data); + break; + } +} + +void DwarfCUToModule::FuncHandler::ProcessAttributeSigned( + enum DwarfAttribute attr, + enum DwarfForm form, + int64_t 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 DW_AT_inline: inline_ = true; break; + + default: + break; + } +} + +DIEHandler* DwarfCUToModule::FuncHandler::FindChildHandler( + uint64_t offset, + enum DwarfTag tag) { + switch (tag) { + case DW_TAG_inlined_subroutine: + if (handle_inline_) + return new InlineHandler(cu_context_, new DIEContext(), offset, 0, + child_inlines_); + default: + return NULL; + } +} + +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; +} + +void DwarfCUToModule::FuncHandler::Finish() { + vector 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_->file_context->file_private_->forward_ref_die_to_func.find( + offset_); + if (iter != + cu_context_->file_context->file_private_->forward_ref_die_to_func.end()) + iter->second->name = name_; + } + + if (!ranges_data_) { + // Make high_pc_ an address, if it isn't already. + if (high_pc_form_ != DW_FORM_addr && + high_pc_form_ != DW_FORM_GNU_addr_index && + high_pc_form_ != DW_FORM_addrx && + high_pc_form_ != DW_FORM_addrx1 && + high_pc_form_ != DW_FORM_addrx2 && + high_pc_form_ != DW_FORM_addrx3 && + high_pc_form_ != DW_FORM_addrx4) { + 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) { + RangeListReader::CURangesInfo cu_info; + if (cu_context_->AssembleRangeListInfo(&cu_info)) { + if (!ranges_handler->ReadRanges(ranges_form_, ranges_data_, + &cu_info, &ranges)) { + ranges.clear(); + cu_context_->reporter->MalformedRangeList(ranges_data_); + } + } else { + cu_context_->reporter->MissingRanges(); + } + } + } + + StringView name_omitted = + cu_context_->file_context->module_->AddStringToPool(""); + bool empty_range = IsEmptyRange(ranges); + // 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 (!empty_range) { + low_pc_ = ranges.front().address; + // Malformed DWARF may omit the name, but all Module::Functions must + // have names. + StringView name = name_.empty() ? name_omitted : name_; + // Create a Module::Function based on the data we've gathered, and + // add it to the functions_ list. + scoped_ptr 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) { + cu_context_->file_context->file_private_ + ->forward_ref_die_to_func[forward_ref_die_offset_] = + cu_context_->functions.back(); + + cu_context_->spec_function_offsets[cu_context_->functions.back()] = + forward_ref_die_offset_; + } + + cu_context_->functions.back()->inlines.swap(child_inlines_); + } + } else if (inline_) { + AbstractOrigin origin(name_); + cu_context_->file_context->file_private_->origins.insert({offset_, origin}); + } + + // Only keep track of DW_TAG_subprogram which have the attributes we are + // interested. + if (handle_inline_ && (!empty_range || inline_)) { + StringView name = name_.empty() ? name_omitted : name_; + uint64_t offset = + specification_offset_ != 0 ? specification_offset_ : offset_; + cu_context_->file_context->module_->inline_origin_map.SetReference(offset_, + offset); + cu_context_->file_context->module_->inline_origin_map + .GetOrCreateInlineOrigin(offset_, name); + } +} + +// 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_t offset, + bool handle_inline) + : GenericDIEHandler(cu_context, parent_context, offset), + handle_inline_(handle_inline) {} + bool EndAttributes(); + DIEHandler* FindChildHandler(uint64_t offset, enum DwarfTag tag); + + private: + DIEContext child_context_; // A context for our children. + bool handle_inline_; +}; + +bool DwarfCUToModule::NamedScopeHandler::EndAttributes() { + child_context_.name = ComputeQualifiedName(); + return true; +} + +DIEHandler* DwarfCUToModule::NamedScopeHandler::FindChildHandler( + uint64_t offset, + enum DwarfTag tag) { + switch (tag) { + case DW_TAG_subprogram: + return new FuncHandler(cu_context_, &child_context_, offset, + handle_inline_); + case DW_TAG_namespace: + case DW_TAG_class_type: + case DW_TAG_structure_type: + case DW_TAG_union_type: + return new NamedScopeHandler(cu_context_, &child_context_, offset, + handle_inline_); + default: + return NULL; + } +} + +void DwarfCUToModule::WarningReporter::CUHeading() { + if (printed_cu_header_) + return; + fprintf(stderr, "%s: in compilation unit '%s' (offset 0x%" PRIx64 "):\n", + filename_.c_str(), cu_name_.c_str(), cu_offset_); + printed_cu_header_ = true; +} + +void DwarfCUToModule::WarningReporter::UnknownSpecification(uint64_t offset, + uint64_t target) { + CUHeading(); + fprintf(stderr, "%s: the DIE at offset 0x%" PRIx64 " has a " + "DW_AT_specification attribute referring to the DIE at offset 0x%" + PRIx64 ", which was not marked as a declaration\n", + filename_.c_str(), offset, target); +} + +void DwarfCUToModule::WarningReporter::UnknownAbstractOrigin(uint64_t offset, + uint64_t target) { + CUHeading(); + fprintf(stderr, "%s: the DIE at offset 0x%" PRIx64 " has a " + "DW_AT_abstract_origin attribute referring to the DIE at offset 0x%" + PRIx64 ", 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_t 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.str().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_t offset) { + CUHeading(); + fprintf(stderr, "%s: warning: function at offset 0x%" PRIx64 " 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_t offset, uint64_t target) { + CUHeading(); + fprintf(stderr, "%s: warning: the DIE at offset 0x%" PRIx64 " has a " + "DW_FORM_ref_addr attribute with an inter-CU reference to " + "0x%" PRIx64 ", but inter-CU reference handling is turned " + " off.\n", filename_.c_str(), offset, target); +} + +void DwarfCUToModule::WarningReporter::MalformedRangeList(uint64_t offset) { + CUHeading(); + fprintf(stderr, "%s: warning: the range list at offset 0x%" PRIx64 " 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, + bool handle_inline) + : RootDIEHandler(handle_inline), + 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_t data) { + switch (attr) { + case DW_AT_language: // source language of this CU + SetLanguage(static_cast(data)); + break; + default: + break; + } +} + +void DwarfCUToModule::ProcessAttributeUnsigned(enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t data) { + switch (attr) { + case DW_AT_stmt_list: // Line number information. + has_source_line_info_ = true; + source_line_offset_ = data; + break; + case DW_AT_language: // source language of this CU + SetLanguage(static_cast(data)); + break; + case DW_AT_low_pc: + cu_context_->low_pc = data; + break; + case DW_AT_high_pc: + cu_context_->high_pc = data; + break; + case DW_AT_ranges: + cu_context_->ranges_data = data; + cu_context_->ranges_form = form; + break; + case DW_AT_rnglists_base: + cu_context_->ranges_base = data; + break; + case DW_AT_addr_base: + case DW_AT_GNU_addr_base: + cu_context_->addr_base = data; + break; + case DW_AT_str_offsets_base: + cu_context_->str_offsets_base = data; + break; + default: + break; + } +} + +void DwarfCUToModule::ProcessAttributeString(enum DwarfAttribute attr, + enum DwarfForm form, + const string& data) { + switch (attr) { + case DW_AT_name: + cu_context_->reporter->SetCUName(data); + break; + case DW_AT_comp_dir: + line_reader_->StartCompilationUnit(data); + break; + default: + break; + } +} + +bool DwarfCUToModule::EndAttributes() { + return true; +} + +DIEHandler* DwarfCUToModule::FindChildHandler( + uint64_t offset, + enum DwarfTag tag) { + switch (tag) { + case DW_TAG_subprogram: + return new FuncHandler(cu_context_.get(), child_context_.get(), offset, + handle_inline); + case DW_TAG_namespace: + case DW_TAG_class_type: + case DW_TAG_structure_type: + case DW_TAG_union_type: + case DW_TAG_module: + return new NamedScopeHandler(cu_context_.get(), child_context_.get(), + offset, handle_inline); + default: + return NULL; + } +} + +void DwarfCUToModule::SetLanguage(DwarfLanguage language) { + switch (language) { + case DW_LANG_Java: + cu_context_->language = Language::Java; + break; + + case DW_LANG_Swift: + cu_context_->language = Language::Swift; + break; + + case 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 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 DW_LANG_ObjC: + case DW_LANG_ObjC_plus_plus: + case DW_LANG_C: + case DW_LANG_C89: + case DW_LANG_C99: + case DW_LANG_C_plus_plus: + cu_context_->language = Language::CPlusPlus; + break; + } +} + +void DwarfCUToModule::ReadSourceLines(uint64_t offset) { + const SectionMap& section_map + = cu_context_->file_context->section_map(); + SectionMap::const_iterator map_entry + = GetSectionByName(section_map, ".debug_line"); + if (map_entry == section_map.end()) { + cu_context_->reporter->MissingSection(".debug_line"); + return; + } + const uint8_t* line_section_start = map_entry->second.first + offset; + uint64_t line_section_length = map_entry->second.second; + if (offset >= line_section_length) { + cu_context_->reporter->BadLineInfoOffset(offset); + return; + } + line_section_length -= offset; + // When reading line tables, string sections are never needed for dwarf4, and + // may or may not be needed by dwarf5, so no error if they are missing. + const uint8_t* string_section_start = nullptr; + uint64_t string_section_length = 0; + map_entry = GetSectionByName(section_map, ".debug_str"); + if (map_entry != section_map.end()) { + string_section_start = map_entry->second.first; + string_section_length = map_entry->second.second; + } + const uint8_t* line_string_section_start = nullptr; + uint64_t line_string_section_length = 0; + map_entry = GetSectionByName(section_map, ".debug_line_str"); + if (map_entry != section_map.end()) { + line_string_section_start = map_entry->second.first; + line_string_section_length = map_entry->second.second; + } + line_reader_->ReadProgram( + line_section_start, line_section_length, + string_section_start, string_section_length, + line_string_section_start, line_string_section_length, + cu_context_->file_context->module_, &lines_, &files_); +} + +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& dest_ranges, + vector* functions) { + for (vector::const_iterator func_it = functions->cbegin(); + func_it != functions->cend(); + func_it++) + { + Module::Function* func = *func_it; + vector& ranges = func->ranges; + for (vector::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 +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* 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 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::iterator range_it = sorted_ranges.begin(); + vector::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; + } + + // Some dwarf producers handle linker-removed functions by using -1 as a + // tombstone in the line table. So the end marker can be -1. + if (current == Module::kMaxAddress) + 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. + + // Some dwarf producers handle linker-removed functions by using -1 as a + // tombstone in the line table. So the end marker can be -1. + if (!next_transition || next_transition == Module::kMaxAddress) + 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::AssignFilesToInlines() { + // Assign File* to Inlines inside this CU. + auto assignFile = [this](unique_ptr& in) { + in->call_site_file = files_[in->call_site_file_id]; + }; + for (auto func : cu_context_->functions) { + Module::Inline::InlineDFS(func->inlines, assignFile); + } +} + +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* functions = &cu_context_->functions; + + // Dole out lines to the appropriate functions. + AssignLinesToFunctions(); + + AssignFilesToInlines(); + + // Add our functions, which now have source lines assigned to them, + // to module_, and remove duplicate functions. + for (Module::Function* func : *functions) + if (!cu_context_->file_context->module_->AddFunction(func)) { + auto iter = cu_context_->spec_function_offsets.find(func); + if (iter != cu_context_->spec_function_offsets.end()) + cu_context_->file_context->file_private_->forward_ref_die_to_func.erase( + iter->second); + delete func; + } + + // Ownership of the function objects has shifted from cu_context to + // the Module. + functions->clear(); + + cu_context_->file_context->ClearSpecifications(); +} + +bool DwarfCUToModule::StartCompilationUnit(uint64_t offset, + uint8_t address_size, + uint8_t offset_size, + uint64_t cu_length, + uint8_t dwarf_version) { + cu_context_->version = dwarf_version; + return dwarf_version >= 2; +} + +bool DwarfCUToModule::StartRootDIE(uint64_t 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 == DW_TAG_compile_unit + || tag == DW_TAG_skeleton_unit); +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/common/dwarf_cu_to_module.h b/shared/sentry/external/breakpad/src/common/dwarf_cu_to_module.h new file mode 100644 index 000000000..2873101a9 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/dwarf_cu_to_module.h @@ -0,0 +1,350 @@ +// -*- 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 + +// 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 + +#include + +#include "common/language.h" +#include "common/module.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 { + +// Populate a google_breakpad::Module with DWARF debugging information. +// +// An instance of this class can be provided as a handler to a +// DIEDispatcher, which can in turn be a handler for a +// 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 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_t length); + + // Clear the section map for testing. + void ClearSectionMapForTest(); + + const 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_t offset, + uint64_t 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. + 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 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 entries are read according to the form and data. + virtual bool ReadRanges( + enum DwarfForm form, uint64_t data, + RangeListReader::CURangesInfo* cu_info, + vector* ranges) = 0; + }; + + // An abstract base class for handlers that handle DWARF line data + // for DwarfCUToModule. DwarfCUToModule could certainly just use + // 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_t length, + const uint8_t* string_section, + uint64_t string_section_length, + const uint8_t* line_string_section, + uint64_t line_string_length, + Module* module, vector* lines, + map* files) = 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_t 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_t offset, uint64_t 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_t offset, uint64_t target); + + // We were unable to find the DWARF section named SECTION_NAME. + virtual void MissingSection(const string& section_name); + + // The CU's DW_AT_stmt_list offset OFFSET is bogus. + virtual void BadLineInfoOffset(uint64_t 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_t 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_t offset, uint64_t target); + + // The DW_AT_ranges at offset is malformed (truncated or outside of the + // .debug_ranges section's bound). + virtual void MalformedRangeList(uint64_t offset); + + // A DW_AT_ranges attribute was encountered but the no .debug_ranges + // section was found. + virtual void MissingRanges(); + + uint64_t cu_offset() const { + return cu_offset_; + } + + protected: + const string filename_; + const uint64_t 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 + // 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, + bool handle_inline = false); + ~DwarfCUToModule(); + + void ProcessAttributeSigned(enum DwarfAttribute attr, + enum DwarfForm form, + int64_t data); + void ProcessAttributeUnsigned(enum DwarfAttribute attr, + enum DwarfForm form, + uint64_t data); + void ProcessAttributeString(enum DwarfAttribute attr, + enum DwarfForm form, + const string& data); + bool EndAttributes(); + DIEHandler* FindChildHandler(uint64_t 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_t offset, uint8_t address_size, + uint8_t offset_size, uint64_t cu_length, + uint8_t dwarf_version); + bool StartRootDIE(uint64_t 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 InlineHandler; + class NamedScopeHandler; + + // A map from section offsets to specifications. + typedef map 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_t 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(); + + void AssignFilesToInlines(); + + // 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 cu_context_; + + // A context for our children. + scoped_ptr 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_t 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 lines_; + + // The map from file index to File* in this CU. + std::map files_; +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_DWARF_CU_TO_MODULE_H__ diff --git a/shared/sentry/external/breakpad/src/common/dwarf_cu_to_module_unittest.cc b/shared/sentry/external/breakpad/src/common/dwarf_cu_to_module_unittest.cc new file mode 100644 index 000000000..499ec49bb --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/dwarf_cu_to_module_unittest.cc @@ -0,0 +1,1864 @@ +// 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 + +// dwarf_cu_to_module.cc: Unit tests for google_breakpad::DwarfCUToModule. + +#include + +#include +#include +#include + +#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 google_breakpad::DIEHandler; +using google_breakpad::DwarfTag; +using google_breakpad::DwarfAttribute; +using google_breakpad::DwarfForm; +using google_breakpad::DwarfInline; +using google_breakpad::DwarfCUToModule; +using google_breakpad::Module; + +using ::testing::_; +using ::testing::AtMost; +using ::testing::DoAll; +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_METHOD9(ReadProgram, void(const uint8_t* program, uint64_t length, + const uint8_t* string_section, + uint64_t string_section_length, + const uint8_t* line_string_section, + uint64_t line_string_section_length, + Module* module, vector* lines, + std::map* files)); +}; + +class MockWarningReporter: public DwarfCUToModule::WarningReporter { + public: + MockWarningReporter(const string& filename, uint64_t cu_offset) + : DwarfCUToModule::WarningReporter(filename, cu_offset) { } + MOCK_METHOD1(SetCUName, void(const string& name)); + MOCK_METHOD2(UnknownSpecification, void(uint64_t offset, uint64_t target)); + MOCK_METHOD2(UnknownAbstractOrigin, void(uint64_t offset, uint64_t target)); + MOCK_METHOD1(MissingSection, void(const string& section_name)); + MOCK_METHOD1(BadLineInfoOffset, void(uint64_t offset)); + MOCK_METHOD1(UncoveredFunction, void(const Module::Function& function)); + MOCK_METHOD1(UncoveredLine, void(const Module::Line& line)); + MOCK_METHOD1(UnnamedFunction, void(uint64_t offset)); + MOCK_METHOD1(DemangleError, void(const string& input)); + MOCK_METHOD2(UnhandledInterCUReference, void(uint64_t offset, uint64_t 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 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* lines) : lines_(lines) { } + void operator()(const uint8_t* program, uint64_t length, + const uint8_t* string_section, + uint64_t string_section_length, + const uint8_t* line_string_section, + uint64_t line_string_section_length, + Module *module, vector* lines, + std::map* files) { + lines->insert(lines->end(), lines_->begin(), lines_->end()); + } + private: + const vector* lines_; + }; + + CUFixtureBase() + : module_("module-name", "module-os", "module-arch", "module-id"), + file_context_("dwarf-filename", &module_, true), + language_(google_breakpad::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(google_breakpad::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(google_breakpad::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_t 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 = google_breakpad::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_t 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_t 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_t offset, + DwarfInline type, uint64_t specification, + const string& name, + DwarfForm form = google_breakpad::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_t 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. + google_breakpad::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 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 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, + google_breakpad::DW_TAG_compile_unit)); + } + root_handler_.ProcessAttributeString(google_breakpad::DW_AT_name, + google_breakpad::DW_FORM_strp, + "compilation-unit-name"); + if (!compilation_dir_.empty()) + root_handler_.ProcessAttributeString(google_breakpad::DW_AT_comp_dir, + google_breakpad::DW_FORM_strp, + compilation_dir_); + if (!lines_.empty()) + root_handler_.ProcessAttributeUnsigned(google_breakpad::DW_AT_stmt_list, + google_breakpad::DW_FORM_ref4, + 0); + if (language_ != google_breakpad::DW_LANG_none) { + if (language_signed_) + root_handler_.ProcessAttributeSigned(google_breakpad::DW_AT_language, + google_breakpad::DW_FORM_sdata, + language_); + else + root_handler_.ProcessAttributeUnsigned(google_breakpad::DW_AT_language, + google_breakpad::DW_FORM_udata, + language_); + } + ASSERT_TRUE(root_handler_.EndAttributes()); +} + +void CUFixtureBase::ProcessStrangeAttributes( + google_breakpad::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) { + google_breakpad::DIEHandler* handler + = parent->FindChildHandler(0x8f4c783c0467c989ULL, tag); + if (!handler) + return NULL; + handler->ProcessAttributeString(google_breakpad::DW_AT_name, + google_breakpad::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_t specification, + const char* name) { + google_breakpad::DIEHandler* handler + = parent->FindChildHandler(0x8f4c783c0467c989ULL, tag); + if (!handler) + return NULL; + if (name) + handler->ProcessAttributeString(google_breakpad::DW_AT_name, + google_breakpad::DW_FORM_strp, + name); + handler->ProcessAttributeReference(google_breakpad::DW_AT_specification, + google_breakpad::DW_FORM_ref4, + specification); + if (!handler->EndAttributes()) { + handler->Finish(); + delete handler; + return NULL; + } + + return handler; +} + +void CUFixtureBase::DefineFunction(google_breakpad::DIEHandler* parent, + const string& name, Module::Address address, + Module::Address size, + const char* mangled_name, + DwarfForm high_pc_form) { + google_breakpad::DIEHandler* func + = parent->FindChildHandler(0xe34797c7e68590a8LL, + google_breakpad::DW_TAG_subprogram); + ASSERT_TRUE(func != NULL); + func->ProcessAttributeString(google_breakpad::DW_AT_name, + google_breakpad::DW_FORM_strp, + name); + func->ProcessAttributeUnsigned(google_breakpad::DW_AT_low_pc, + google_breakpad::DW_FORM_addr, + address); + + Module::Address high_pc = size; + if (high_pc_form == google_breakpad::DW_FORM_addr) { + high_pc += address; + } + func->ProcessAttributeUnsigned(google_breakpad::DW_AT_high_pc, + high_pc_form, + high_pc); + + if (mangled_name) + func->ProcessAttributeString(google_breakpad::DW_AT_MIPS_linkage_name, + google_breakpad::DW_FORM_strp, + mangled_name); + + ProcessStrangeAttributes(func); + EXPECT_TRUE(func->EndAttributes()); + func->Finish(); + delete func; +} + +void CUFixtureBase::DeclarationDIE(DIEHandler* parent, uint64_t offset, + DwarfTag tag, + const string& name, + const string& mangled_name) { + google_breakpad::DIEHandler* die = parent->FindChildHandler(offset, tag); + ASSERT_TRUE(die != NULL); + if (!name.empty()) + die->ProcessAttributeString(google_breakpad::DW_AT_name, + google_breakpad::DW_FORM_strp, + name); + if (!mangled_name.empty()) + die->ProcessAttributeString(google_breakpad::DW_AT_MIPS_linkage_name, + google_breakpad::DW_FORM_strp, + mangled_name); + + die->ProcessAttributeUnsigned(google_breakpad::DW_AT_declaration, + google_breakpad::DW_FORM_flag, + 1); + EXPECT_TRUE(die->EndAttributes()); + die->Finish(); + delete die; +} + +void CUFixtureBase::DefinitionDIE(DIEHandler* parent, + DwarfTag tag, + uint64_t specification, + const string& name, + Module::Address address, + Module::Address size) { + google_breakpad::DIEHandler* die + = parent->FindChildHandler(0x6ccfea031a9e6cc9ULL, tag); + ASSERT_TRUE(die != NULL); + die->ProcessAttributeReference(google_breakpad::DW_AT_specification, + google_breakpad::DW_FORM_ref4, + specification); + if (!name.empty()) + die->ProcessAttributeString(google_breakpad::DW_AT_name, + google_breakpad::DW_FORM_strp, + name); + if (size) { + die->ProcessAttributeUnsigned(google_breakpad::DW_AT_low_pc, + google_breakpad::DW_FORM_addr, + address); + die->ProcessAttributeUnsigned(google_breakpad::DW_AT_high_pc, + google_breakpad::DW_FORM_addr, + address + size); + } + EXPECT_TRUE(die->EndAttributes()); + die->Finish(); + delete die; +} + +void CUFixtureBase::AbstractInstanceDIE(DIEHandler* parent, + uint64_t offset, + DwarfInline type, + uint64_t specification, + const string& name, + DwarfForm form) { + google_breakpad::DIEHandler* die + = parent->FindChildHandler(offset, google_breakpad::DW_TAG_subprogram); + ASSERT_TRUE(die != NULL); + if (specification != 0ULL) + die->ProcessAttributeReference(google_breakpad::DW_AT_specification, + google_breakpad::DW_FORM_ref4, + specification); + if (form == google_breakpad::DW_FORM_sdata) { + die->ProcessAttributeSigned(google_breakpad::DW_AT_inline, form, type); + } else { + die->ProcessAttributeUnsigned(google_breakpad::DW_AT_inline, form, type); + } + if (!name.empty()) + die->ProcessAttributeString(google_breakpad::DW_AT_name, + google_breakpad::DW_FORM_strp, + name); + + EXPECT_TRUE(die->EndAttributes()); + die->Finish(); + delete die; +} + +void CUFixtureBase::DefineInlineInstanceDIE(DIEHandler* parent, + const string& name, + uint64_t origin, + Module::Address address, + Module::Address size) { + google_breakpad::DIEHandler* func + = parent->FindChildHandler(0x11c70f94c6e87ccdLL, + google_breakpad::DW_TAG_subprogram); + ASSERT_TRUE(func != NULL); + if (!name.empty()) { + func->ProcessAttributeString(google_breakpad::DW_AT_name, + google_breakpad::DW_FORM_strp, + name); + } + func->ProcessAttributeUnsigned(google_breakpad::DW_AT_low_pc, + google_breakpad::DW_FORM_addr, + address); + func->ProcessAttributeUnsigned(google_breakpad::DW_AT_high_pc, + google_breakpad::DW_FORM_addr, + address + size); + func->ProcessAttributeReference(google_breakpad::DW_AT_abstract_origin, + google_breakpad::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, + google_breakpad::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, + google_breakpad::DW_TAG_lexical_block)); +} + +TEST_F(SimpleCU, IrrelevantNamedScopeChildren) { + StartCU(); + DIEHandler* class_A_handler + = StartNamedDIE(&root_handler_, google_breakpad::DW_TAG_class_type, "class_A"); + EXPECT_TRUE(class_A_handler != NULL); + EXPECT_FALSE(class_A_handler + ->FindChildHandler(0x02e55999b865e4e9ULL, + google_breakpad::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, + google_breakpad::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, + google_breakpad::DW_INL_inlined, 0, "inline-name", + google_breakpad::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, + google_breakpad::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, + google_breakpad::DW_INL_inlined, 0, "inline-name"); + DefineInlineInstanceDIE(&root_handler_, "", 1ULL, + 0x1758a0f941b71efbULL, 0x1cf154f1f545e146ULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, "", + 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, "", + 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 { }; + +INSTANTIATE_TEST_SUITE_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 { }; + +INSTANTIATE_TEST_SUITE_P(VersusEnclosures, CXXQualifiedNames, + Values(google_breakpad::DW_TAG_class_type, + google_breakpad::DW_TAG_structure_type, + google_breakpad::DW_TAG_union_type, + google_breakpad::DW_TAG_namespace)); + +TEST_P(CXXQualifiedNames, TwoFunctions) { + DwarfTag tag = GetParam(); + + SetLanguage(google_breakpad::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(google_breakpad::DW_LANG_C_plus_plus); + PushLine(10, 1, "line-file", 69819327); + + StartCU(); + DIEHandler* namespace_handler + = StartNamedDIE(&root_handler_, google_breakpad::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(google_breakpad::DW_LANG_C_plus_plus); + PushLine(10, 1, "filename1", 69819327); + + StartCU(); + DIEHandler* namespace_handler + = StartNamedDIE(&root_handler_, google_breakpad::DW_TAG_namespace, + "namespace_A"); + EXPECT_TRUE(namespace_handler != NULL); + DIEHandler* struct_handler + = StartNamedDIE(namespace_handler, google_breakpad::DW_TAG_structure_type, + "struct_B"); + EXPECT_TRUE(struct_handler != NULL); + DIEHandler* class_handler + = StartNamedDIE(struct_handler, google_breakpad::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 { + google_breakpad::DwarfLanguage language; + const char* name; +}; + +const LanguageAndQualifiedName LanguageAndQualifiedNameCases[] = { + { google_breakpad::DW_LANG_none, "class_A::function_B" }, + { google_breakpad::DW_LANG_C, "class_A::function_B" }, + { google_breakpad::DW_LANG_C89, "class_A::function_B" }, + { google_breakpad::DW_LANG_C99, "class_A::function_B" }, + { google_breakpad::DW_LANG_C_plus_plus, "class_A::function_B" }, + { google_breakpad::DW_LANG_Java, "class_A.function_B" }, + { google_breakpad::DW_LANG_Cobol74, "class_A::function_B" }, + { google_breakpad::DW_LANG_Mips_Assembler, NULL } +}; + +class QualifiedForLanguage + : public CUFixtureBase, + public TestWithParam { }; + +INSTANTIATE_TEST_SUITE_P(LanguageAndQualifiedName, QualifiedForLanguage, + ValuesIn(LanguageAndQualifiedNameCases)); + +TEST_P(QualifiedForLanguage, MemberFunction) { + const LanguageAndQualifiedName& param = GetParam(); + + PushLine(10, 1, "line-file", 212966758); + SetLanguage(param.language); + + StartCU(); + DIEHandler* class_handler + = StartNamedDIE(&root_handler_, google_breakpad::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& param = GetParam(); + + PushLine(10, 1, "line-file", 212966758); + SetLanguage(param.language); + SetLanguageSigned(true); + + StartCU(); + DIEHandler* class_handler + = StartNamedDIE(&root_handler_, google_breakpad::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, + google_breakpad::DW_TAG_subprogram, "declaration-name", ""); + DefinitionDIE(&root_handler_, google_breakpad::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, + google_breakpad::DW_TAG_subprogram, "declaration-name", + "_ZN1C1fEi"); + DefinitionDIE(&root_handler_, google_breakpad::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(google_breakpad::DW_LANG_Swift); + PushLine(0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL, "line-file", 54883661); + StartCU(); + const string kName = "_TFC9swifttest5Shape17simpleDescriptionfS0_FT_Si"; + DeclarationDIE(&root_handler_, 0xcd3c51b946fb1eeeLL, + google_breakpad::DW_TAG_subprogram, "declaration-name", + kName); + DefinitionDIE(&root_handler_, google_breakpad::DW_TAG_subprogram, + 0xcd3c51b946fb1eeeLL, "", + 0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, kName, + 0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL); +} + +TEST_F(Specifications, MangledNameRust) { + SetLanguage(google_breakpad::DW_LANG_Rust); + PushLine(0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL, "line-file", 54883661); + + StartCU(); + const string kName = "_ZN14rustc_demangle8demangle17h373defa94bffacdeE"; + DeclarationDIE(&root_handler_, 0xcd3c51b946fb1eeeLL, + google_breakpad::DW_TAG_subprogram, "declaration-name", + kName); + DefinitionDIE(&root_handler_, google_breakpad::DW_TAG_subprogram, + 0xcd3c51b946fb1eeeLL, "", + 0x93cd3dfc1aa10097ULL, 0x0397d47a0b4ca0d4ULL); + root_handler_.Finish(); + + TestFunctionCount(1); + TestFunction(0, +#ifndef HAVE_RUSTC_DEMANGLE + // Rust mangled names should pass through untouched if not + // using rustc-demangle. + kName, +#else + // If rustc-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_, google_breakpad::DW_TAG_class_type, "class_A"); + DeclarationDIE(class_handler, 0x7d83028c431406e8ULL, + google_breakpad::DW_TAG_subprogram, "declaration-name", ""); + class_handler->Finish(); + delete class_handler; + DefinitionDIE(&root_handler_, google_breakpad::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_, google_breakpad::DW_TAG_class_type, + "class_A"); + ASSERT_TRUE(class_handler != NULL); + DeclarationDIE(class_handler, 0x0e0e877c8404544aULL, + google_breakpad::DW_TAG_subprogram, "declaration-name", ""); + class_handler->Finish(); + delete class_handler; + } + + DefinitionDIE(&root_handler_, google_breakpad::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_, google_breakpad::DW_TAG_namespace, + "space_A"); + ASSERT_TRUE(space_handler != NULL); + DeclarationDIE(space_handler, 0x419bb1d12f9a73a2ULL, + google_breakpad::DW_TAG_class_type, "class-declaration-name", + ""); + space_handler->Finish(); + delete space_handler; + } + + { + DIEHandler* class_handler + = StartSpecifiedDIE(&root_handler_, google_breakpad::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, + google_breakpad::DW_TAG_subprogram, "inline-name", ""); + AbstractInstanceDIE(&root_handler_, 0x1e8dac5d507ed7abULL, + google_breakpad::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_, google_breakpad::DW_TAG_namespace, + "Namespace"); + ASSERT_TRUE(space_handler != NULL); + AbstractInstanceDIE(space_handler, 0x1e8dac5d507ed7abULL, + google_breakpad::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(google_breakpad::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_, google_breakpad::DW_TAG_namespace, + "space_A"); + DeclarationDIE(space_A_handler, 0x2e111126496596e2ULL, + google_breakpad::DW_TAG_namespace, "space_B", ""); + space_A_handler->Finish(); + delete space_A_handler; + } + + { + DIEHandler* space_B_handler + = StartSpecifiedDIE(&root_handler_, google_breakpad::DW_TAG_namespace, + 0x2e111126496596e2ULL); + DIEHandler* struct_C_handler + = StartNamedDIE(space_B_handler, google_breakpad::DW_TAG_structure_type, + "struct_C"); + DeclarationDIE(struct_C_handler, 0x20cd423bf2a25a4cULL, + google_breakpad::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_, google_breakpad::DW_TAG_structure_type, + 0x20cd423bf2a25a4cULL); + DIEHandler* union_E_handler + = StartNamedDIE(struct_D_handler, google_breakpad::DW_TAG_union_type, + "union_E"); + DeclarationDIE(union_E_handler, 0xe25c84805aa58c32ULL, + google_breakpad::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_, google_breakpad::DW_TAG_union_type, + 0xe25c84805aa58c32ULL); + DIEHandler* class_G_handler + = StartNamedDIE(union_F_handler, google_breakpad::DW_TAG_class_type, + "class_G"); + DeclarationDIE(class_G_handler, 0xb70d960dcc173b6eULL, + google_breakpad::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_, google_breakpad::DW_TAG_class_type, + 0xb70d960dcc173b6eULL); + DeclarationDIE(class_H_handler, 0x27ff829e3bf69f37ULL, + google_breakpad::DW_TAG_subprogram, "func_I", ""); + class_H_handler->Finish(); + delete class_H_handler; + } + + DefinitionDIE(&root_handler_, google_breakpad::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, + google_breakpad::DW_TAG_compile_unit)); + ProcessStrangeAttributes(&root1_handler); + ASSERT_TRUE(root1_handler.EndAttributes()); + DeclarationDIE(&root1_handler, 0xb8fbfdd5f0b26fceULL, + google_breakpad::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, + google_breakpad::DW_TAG_compile_unit)); + ASSERT_TRUE(root2_handler.EndAttributes()); + DIEHandler* class_A_handler + = StartSpecifiedDIE(&root2_handler, google_breakpad::DW_TAG_class_type, + 0xb8fbfdd5f0b26fceULL); + DeclarationDIE(class_A_handler, 0xb01fef8b380bd1a2ULL, + google_breakpad::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, + google_breakpad::DW_TAG_compile_unit)); + ASSERT_TRUE(root3_handler.EndAttributes()); + DefinitionDIE(&root3_handler, google_breakpad::DW_TAG_subprogram, + 0xb01fef8b380bd1a2ULL, "", + 0x2618f00a1a711e53ULL, 0x4fd94b76d7c2caf5ULL); + root3_handler.Finish(); + } + + vector functions; + m.GetFunctions(&functions, functions.end()); + EXPECT_EQ(1U, functions.size()); + EXPECT_STREQ("class_A::member_func_B", functions[0]->name.str().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, + google_breakpad::DW_TAG_compile_unit)); + ProcessStrangeAttributes(&root1_handler); + ASSERT_TRUE(root1_handler.EndAttributes()); + DeclarationDIE(&root1_handler, 0xb8fbfdd5f0b26fceULL, + google_breakpad::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, + google_breakpad::DW_TAG_compile_unit)); + ASSERT_TRUE(root2_handler.EndAttributes()); + EXPECT_CALL(reporter_, UnhandledInterCUReference(_, _)).Times(1); + DIEHandler* class_A_handler + = StartSpecifiedDIE(&root2_handler, google_breakpad::DW_TAG_class_type, + 0xb8fbfdd5f0b26fceULL); + DeclarationDIE(class_A_handler, 0xb01fef8b380bd1a2ULL, + google_breakpad::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, + google_breakpad::DW_TAG_compile_unit)); + ASSERT_TRUE(root3_handler.EndAttributes()); + EXPECT_CALL(reporter_, UnhandledInterCUReference(_, _)).Times(1); + EXPECT_CALL(reporter_, UnnamedFunction(_)).Times(1); + DefinitionDIE(&root3_handler, google_breakpad::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, + google_breakpad::DW_TAG_subprogram, "", ""); + DefinitionDIE(&root_handler_, google_breakpad::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, + google_breakpad::DW_TAG_subprogram, "declaration-name", ""); + DefinitionDIE(&root_handler_, google_breakpad::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, + google_breakpad::DW_TAG_class_type, "class-declaration-name", ""); + + google_breakpad::DIEHandler* class_definition + = StartSpecifiedDIE(&root_handler_, google_breakpad::DW_TAG_class_type, + 0xd0fe467ec2f1a58cULL, "class-definition-name"); + ASSERT_TRUE(class_definition); + DeclarationDIE(class_definition, 0x6d028229c15623dbULL, + google_breakpad::DW_TAG_subprogram, + "function-declaration-name", ""); + class_definition->Finish(); + delete class_definition; + + DefinitionDIE(&root_handler_, google_breakpad::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(); + { + google_breakpad::DIEHandler* declaration_class_handler = + StartNamedDIE(&root_handler_, google_breakpad::DW_TAG_class_type, + "declaration-class"); + DeclarationDIE(declaration_class_handler, 0x9ddb35517455ef7aULL, + google_breakpad::DW_TAG_subprogram, "function-declaration", + ""); + declaration_class_handler->Finish(); + delete declaration_class_handler; + } + { + google_breakpad::DIEHandler* definition_class_handler + = StartNamedDIE(&root_handler_, google_breakpad::DW_TAG_class_type, + "definition-class"); + DefinitionDIE(definition_class_handler, google_breakpad::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, + google_breakpad::DW_TAG_compile_unit)); + root_handler_.ProcessAttributeString(google_breakpad::DW_AT_name, + google_breakpad::DW_FORM_strp, + "compilation-unit-name"); + root_handler_.ProcessAttributeUnsigned(google_breakpad::DW_AT_stmt_list, + google_breakpad::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, + google_breakpad::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/shared/sentry/external/breakpad/src/common/dwarf_line_to_module.cc b/shared/sentry/external/breakpad/src/common/dwarf_line_to_module.cc new file mode 100644 index 000000000..83bb8f155 --- /dev/null +++ b/shared/sentry/external/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 + +// dwarf_line_to_module.cc: Implementation of DwarfLineToModule class. +// See dwarf_line_to_module.h for details. + +#include + +#include + +#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_t 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_t file_num, + uint32_t dir_num, uint64_t mod_time, + uint64_t 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_t address, uint64_t length, + uint32_t file_num, uint32_t line_num, + uint32_t 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/shared/sentry/external/breakpad/src/common/dwarf_line_to_module.h b/shared/sentry/external/breakpad/src/common/dwarf_line_to_module.h new file mode 100644 index 000000000..da2c5f0ec --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/dwarf_line_to_module.h @@ -0,0 +1,191 @@ +// -*- 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 + +// 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 + +#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 +// 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 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* lines, + std::map* files) + : module_(module), + compilation_dir_(compilation_dir), + lines_(lines), + files_(files), + 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_t dir_num); + void DefineFile(const string& name, int32_t file_num, + uint32_t dir_num, uint64_t mod_time, + uint64_t length); + void AddLine(uint64_t address, uint64_t length, + uint32_t file_num, uint32_t line_num, uint32_t column_num); + + private: + + typedef std::map DirectoryTable; + typedef std::map 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* 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_t 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_t 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/shared/sentry/external/breakpad/src/common/dwarf_line_to_module_unittest.cc b/shared/sentry/external/breakpad/src/common/dwarf_line_to_module_unittest.cc new file mode 100644 index 000000000..34cb02ed4 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/dwarf_line_to_module_unittest.cc @@ -0,0 +1,410 @@ +// 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 + +// dwarf_line_to_module.cc: Unit tests for google_breakpad::DwarfLineToModule. + +#include + +#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 lines; + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); + + h.DefineFile("file1", 0x30bf0f27, 0, 0, 0); + h.AddLine(0x6fd126fbf74f2680LL, 0x63c9a14cf556712bLL, 0x30bf0f27, + 0x4c090cbf, 0x1cf9fe0d); + + vector 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 lines; + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); + + 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 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 lines; + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); + + h.DefineDir("directory1", 1); + h.DefineFile("/absolute", 1, 1, 0, 0); + + h.AddLine(1, 1, 1, 0, 0); + + vector 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 lines; + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); + + h.DefineDir("directory1", 1); + h.DefineFile("relative", 1, 1, 0, 0); + + h.AddLine(1, 1, 1, 0, 0); + + vector 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 lines; + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); + + 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 lines; + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); + + 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 lines; + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); + + 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 lines; + std::map cu_files; + DwarfLineToModule h(&m, "src/build", &lines, &cu_files); + + 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 lines; + std::map cu_files; + DwarfLineToModule h(&m, "src/build", &lines, &cu_files); + + 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 lines; + std::map cu_files; + DwarfLineToModule h(&m, "src/build", &lines, &cu_files); + + 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 lines; + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); + + 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 lines; + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); + + 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 lines; + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); + + 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 lines; + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); + + 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 lines; + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); + + 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 lines; + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); + + 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 lines; + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); + + 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 lines; + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); + + 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 lines; + std::map cu_files; + DwarfLineToModule h(&m, "/", &lines, &cu_files); + + 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/shared/sentry/external/breakpad/src/common/dwarf_range_list_handler.cc b/shared/sentry/external/breakpad/src/common/dwarf_range_list_handler.cc new file mode 100644 index 000000000..3fecb5645 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/dwarf_range_list_handler.cc @@ -0,0 +1,56 @@ +// 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 +// + +// dwarf_range_list_handler.cc: Implementation of DwarfRangeListHandler class. +// See dwarf_range_list_handler.h for details. + +#include + +#include "common/dwarf_range_list_handler.h" + +namespace google_breakpad { + +void DwarfRangeListHandler::AddRange(uint64_t begin, uint64_t end) { + Module::Range r(begin, end - begin); + + ranges_->push_back(r); +} + +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/shared/sentry/external/breakpad/src/common/dwarf_range_list_handler.h b/shared/sentry/external/breakpad/src/common/dwarf_range_list_handler.h new file mode 100644 index 000000000..7344e6047 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/dwarf_range_list_handler.h @@ -0,0 +1,72 @@ +// -*- 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 +// + +// 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 + +#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 RangeListHandler { + public: + DwarfRangeListHandler(vector* ranges) + : ranges_(ranges) { } + + ~DwarfRangeListHandler() { } + + // Add a range to the list + void AddRange(uint64_t begin, uint64_t end); + + // Sort the ranges so that they are in ascending order of starting address + void Finish(); + + private: + // The list of ranges to be populated + vector* ranges_; +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_DWARF_RANGE_LIST_HANDLER_H diff --git a/shared/sentry/external/breakpad/src/common/language.cc b/shared/sentry/external/breakpad/src/common/language.cc new file mode 100644 index 000000000..63b72a79c --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/language.cc @@ -0,0 +1,220 @@ +// 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 + +// language.cc: Subclasses and singletons for google_breakpad::Language. +// See language.h for details. + +#include "common/language.h" + +#include +#include + +#if !defined(__ANDROID__) +#include +#endif + +#if defined(HAVE_RUSTC_DEMANGLE) +#include +#endif + +#include + +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 + // Attempting to demangle non-C++ symbols with the C++ demangler would print + // warnings and fail, so return kDontDemangle for these. + if (!IsMangledName(mangled)) { + demangled->clear(); + return kDontDemangle; + } + + 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(demangled_c)); + } + + return result; +#endif + } + + private: + static bool IsMangledName(const string& name) { + // NOTE: For proper cross-compilation support, this should depend on target + // binary's platform, not current build platform. +#if defined(__APPLE__) + // Mac C++ symbols can have up to 4 underscores, followed by a "Z". + // Non-C++ symbols are not coded that way, but may have leading underscores. + size_t i = name.find_first_not_of('_'); + return i > 0 && i != string::npos && i <= 4 && name[i] == 'Z'; +#else + // Linux C++ symbols always start with "_Z". + return name.size() > 2 && name[0] == '_' && name[1] == 'Z'; +#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_RUSTC_DEMANGLE) + std::array rustc_demangled; + if (rustc_demangle(mangled.c_str(), rustc_demangled.data(), + rustc_demangled.size()) == 0) { + return kDemangleFailure; + } + demangled->assign(rustc_demangled.data()); +#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/shared/sentry/external/breakpad/src/common/language.h b/shared/sentry/external/breakpad/src/common/language.h new file mode 100644 index 000000000..892ea862e --- /dev/null +++ b/shared/sentry/external/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 + +// 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 + +#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/shared/sentry/external/breakpad/src/common/linux/breakpad_getcontext.S b/shared/sentry/external/breakpad/src/common/linux/breakpad_getcontext.S new file mode 100644 index 000000000..2ebcf3191 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/linux/breakpad_getcontext.S @@ -0,0 +1,552 @@ +// 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__) + +#if defined(__ARM_FEATURE_PAC_DEFAULT) && __ARM_FEATURE_PAC_DEFAULT + // ENABLE_PAUTH must be defined to 1 since this value will be used in + // bitwise-shift later! + #define ENABLE_PAUTH 1 + + #if ((__ARM_FEATURE_PAC_DEFAULT&((1<<0)|(1<<1)))==0) + #error Pointer authentication defines no valid key! + #endif +#else + #define ENABLE_PAUTH 0 +#endif + +#if defined(__ARM_FEATURE_BTI_DEFAULT) && (__ARM_FEATURE_BTI_DEFAULT==1) + // ENABLE_BTI must be defined to 1 since this value will be used in + // bitwise-shift later! + #define ENABLE_BTI 1 +#else + #define ENABLE_BTI 0 +#endif + + +// Although Pointer Authentication and Branch Target Instructions are technically +// seperate features they work together, i.e. the paciasp and pacibsp instructions +// serve as BTI landing pads. +// Therefore PA-instructions are enabled when PA _or_ BTI is enabled! +#if ENABLE_PAUTH || ENABLE_BTI + // See section "Pointer Authentication" of + // https://developer.arm.com/documentation/101028/0012/5--Feature-test-macros + // for details how to interpret __ARM_FEATURE_PAC_DEFAULT + #if (__ARM_FEATURE_PAC_DEFAULT & (1<<0)) + #define PAUTH_SIGN_SP paciasp + #define PAUTH_AUTH_SP autiasp + #else + #define PAUTH_SIGN_SP pacibsp + #define PAUTH_AUTH_SP autibsp + #endif +#else + #define PAUTH_SIGN_SP + #define PAUTH_AUTH_SP +#endif + +#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: + + PAUTH_SIGN_SP + + /* 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 + + PAUTH_AUTH_SP + + 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 +#include +#if _MIPS_SIM == _ABIO32 +#include +#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 // 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 + Copyright (C) 2010 Konstantin Belousov + +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 + +#if defined(__aarch64__) +// ENABLE_PAUTH and ENABLE_BTI would be enabled at the definition +// of AArch64 specific breakpad_getcontext function +#if ENABLE_PAUTH || ENABLE_BTI +// for further information on the .note.gnu.property section see +// https://github.com/ARM-software/abi-aa/blob/main/aaelf64/aaelf64.rst#program-property +.pushsection .note.gnu.property, "a"; + .balign 8 + .long 4 + .long 0x10 + .long 0x5 + .asciz "GNU" + .long 0xc0000000 /* GNU_PROPERTY_AARCH64_FEATURE_1_AND */ + .long 4 + .long ((ENABLE_PAUTH)<<1) | ((ENABLE_BTI)<<0) /* PAuth and BTI */ + .long 0 +.popsection +#endif +#endif diff --git a/shared/sentry/external/breakpad/src/common/linux/breakpad_getcontext.h b/shared/sentry/external/breakpad/src/common/linux/breakpad_getcontext.h new file mode 100644 index 000000000..1418cde62 --- /dev/null +++ b/shared/sentry/external/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 +#endif + +#ifndef HAVE_GETCONTEXT + +#include + +#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/shared/sentry/external/breakpad/src/common/linux/breakpad_getcontext_unittest.cc b/shared/sentry/external/breakpad/src/common/linux/breakpad_getcontext_unittest.cc new file mode 100644 index 000000000..a57bfedf9 --- /dev/null +++ b/shared/sentry/external/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 +#endif + +#include + +#include + +#include "breakpad_googletest_includes.h" +#include "common/linux/ucontext_constants.h" + +template +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 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(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(MCONTEXT_GREGS_OFFSET), + offsetof(ucontext_t,uc_mcontext.regs[0])); + ASSERT_EQ(static_cast(MCONTEXT_SP_OFFSET), + offsetof(ucontext_t,uc_mcontext.sp)); + ASSERT_EQ(static_cast(MCONTEXT_PC_OFFSET), + offsetof(ucontext_t,uc_mcontext.pc)); + ASSERT_EQ(static_cast(MCONTEXT_PSTATE_OFFSET), + offsetof(ucontext_t,uc_mcontext.pstate)); + ASSERT_EQ(static_cast(MCONTEXT_EXTENSION_OFFSET), + offsetof(ucontext_t,uc_mcontext.__reserved)); +#elif defined(__i386__) + ASSERT_EQ(static_cast(MCONTEXT_GREGS_OFFSET), + offsetof(ucontext_t,uc_mcontext.gregs)); +#define CHECK_REG(x) \ + ASSERT_EQ(static_cast(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(UCONTEXT_FPREGS_OFFSET), + offsetof(ucontext_t,uc_mcontext.fpregs)); + + ASSERT_EQ(static_cast(UCONTEXT_FPREGS_MEM_OFFSET), + offsetof(ucontext_t,__fpregs_mem)); +#elif defined(__mips__) + ASSERT_EQ(static_cast(MCONTEXT_GREGS_OFFSET), + offsetof(ucontext_t,uc_mcontext.gregs)); + + // PC for mips is not part of gregs. + ASSERT_EQ(static_cast(MCONTEXT_PC_OFFSET), + offsetof(ucontext_t,uc_mcontext.pc)); + + ASSERT_EQ(static_cast(MCONTEXT_FPREGS_OFFSET), + offsetof(ucontext_t,uc_mcontext.fpregs)); + + ASSERT_EQ(static_cast(MCONTEXT_FPC_CSR), + offsetof(ucontext_t,uc_mcontext.fpc_csr)); +#elif defined(__x86_64__) + + COMPILE_ASSERT_EQ(static_cast(MCONTEXT_GREGS_OFFSET), + offsetof(ucontext_t,uc_mcontext.gregs), + mcontext_gregs_offset); +#define CHECK_REG(x) \ + COMPILE_ASSERT_EQ(static_cast(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::type,mxcsr), + fpregs_offset_mxcsr); + COMPILE_ASSERT_EQ(UCONTEXT_SIGMASK_OFFSET, offsetof(ucontext_t, uc_sigmask), + ucontext_sigmask); +#else + ASSERT_EQ(static_cast(MCONTEXT_GREGS_OFFSET), + offsetof(ucontext_t,uc_mcontext.gregs)); +#endif +} + +TEST(AndroidUContext, SigmakOffset) { + ASSERT_EQ(static_cast(UCONTEXT_SIGMASK_OFFSET), + offsetof(ucontext_t,uc_sigmask)); +} diff --git a/shared/sentry/external/breakpad/src/common/linux/crc32.cc b/shared/sentry/external/breakpad/src/common/linux/crc32.cc new file mode 100644 index 000000000..8df636ce4 --- /dev/null +++ b/shared/sentry/external/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(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/shared/sentry/external/breakpad/src/common/linux/crc32.h b/shared/sentry/external/breakpad/src/common/linux/crc32.h new file mode 100644 index 000000000..e3d9db92b --- /dev/null +++ b/shared/sentry/external/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 + +#include + +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/shared/sentry/external/breakpad/src/common/linux/dump_symbols.cc b/shared/sentry/external/breakpad/src/common/linux/dump_symbols.cc new file mode 100644 index 000000000..75a4ceed2 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/linux/dump_symbols.cc @@ -0,0 +1,1148 @@ +// 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 + +// 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#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" + +// 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::elf::FileID; +using google_breakpad::FindElfSectionByName; +using google_breakpad::GetOffset; +using google_breakpad::IsValidElf; +using google_breakpad::elf::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::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 +vector GetPtLoadSegmentRanges( + const typename ElfClass::Phdr* program_headers, + int nheader) { + typedef typename ElfClass::Phdr Phdr; + vector 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 +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(elf_header, stab_section->sh_offset); + const uint8_t* stabstr = + GetOffset(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 +// google_breakpad::RangeListReader and populates a range vector (typically +// owned by a function) with the results. +class DumperRangesHandler : public DwarfCUToModule::RangesHandler { + public: + DumperRangesHandler(google_breakpad::ByteReader* reader) : + reader_(reader) { } + + bool ReadRanges( + enum google_breakpad::DwarfForm form, uint64_t data, + google_breakpad::RangeListReader::CURangesInfo* cu_info, + vector* ranges) { + DwarfRangeListHandler handler(ranges); + google_breakpad::RangeListReader range_list_reader(reader_, cu_info, + &handler); + return range_list_reader.ReadRanges(form, data); + } + + private: + google_breakpad::ByteReader* reader_; +}; + +// A line-to-module loader that accepts line number info parsed by +// google_breakpad::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(google_breakpad::ByteReader* byte_reader) + : byte_reader_(byte_reader) { } + void StartCompilationUnit(const string& compilation_dir) { + compilation_dir_ = compilation_dir; + } + void ReadProgram(const uint8_t* program, + uint64_t length, + const uint8_t* string_section, + uint64_t string_section_length, + const uint8_t* line_string_section, + uint64_t line_string_section_length, + Module* module, + std::vector* lines, + std::map* files) { + DwarfLineToModule handler(module, compilation_dir_, lines, files); + google_breakpad::LineInfo parser(program, length, byte_reader_, + string_section, string_section_length, + line_string_section, + line_string_section_length, + &handler); + parser.Start(); + } + private: + string compilation_dir_; + google_breakpad::ByteReader* byte_reader_; +}; + +template +bool LoadDwarf(const string& dwarf_filename, + const typename ElfClass::Ehdr* elf_header, + const bool big_endian, + bool handle_inter_cu_refs, + bool handle_inline, + Module* module) { + typedef typename ElfClass::Shdr Shdr; + + const google_breakpad::Endianness endianness = big_endian ? + google_breakpad::ENDIANNESS_BIG : google_breakpad::ENDIANNESS_LITTLE; + google_breakpad::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(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(elf_header, + section_names->sh_offset) + + section->sh_name; + const uint8_t* contents = GetOffset(elf_header, + section->sh_offset); + file_context.AddSectionToSectionMap(name, contents, section->sh_size); + } + + // .debug_ranges and .debug_rnglists reader + DumperRangesHandler ranges_handler(&byte_reader); + + // Parse all the compilation units in the .debug_info section. + DumperLineToModule line_to_module(&byte_reader); + google_breakpad::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& 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_t debug_info_length = debug_info_section.second; + for (uint64_t 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, &reporter, handle_inline); + // Make a Dwarf2Handler that drives the DIEHandler. + google_breakpad::DIEDispatcher die_dispatcher(&root_handler); + // Make a DWARF parser for the compilation unit at OFFSET. + google_breakpad::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 +bool DwarfCFIRegisterNames(const typename ElfClass::Ehdr* elf_header, + std::vector* 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 +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 register_names; + if (!DwarfCFIRegisterNames(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 google_breakpad::Endianness endianness = big_endian ? + google_breakpad::ENDIANNESS_BIG : google_breakpad::ENDIANNESS_LITTLE; + + // Find the call frame information and its size. + const uint8_t* cfi = + GetOffset(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); + google_breakpad::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); + + google_breakpad::CallFrameInfo::Reporter dwarf_reporter(dwarf_filename, + section_name); + google_breakpad::CallFrameInfo parser(cfi, cfi_size, + &byte_reader, &handler, &dwarf_reporter, + eh_frame); + 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 +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& debug_dirs) { + // Include '\0' + CRC32 (4 bytes). + size_t debuglink_len = strlen(reinterpret_cast(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 searched_paths; + string debuglink_path; + std::vector::const_iterator it; + for (it = debug_dirs.begin(); it < debug_dirs.end(); ++it) { + const string& debug_dir = *it; + debuglink_path = debug_dir + "/" + + reinterpret_cast(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 google_breakpad::Endianness endianness = big_endian ? + google_breakpad::ENDIANNESS_BIG : google_breakpad::ENDIANNESS_LITTLE; + google_breakpad::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 +class LoadSymbolsInfo { + public: + typedef typename ElfClass::Addr Addr; + + explicit LoadSymbolsInfo(const std::vector& 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& section) { + 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& 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& 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 loaded_sections_; // Tracks the Loaded ELF sections + // between calls to LoadSymbols(). +}; + +template +bool LoadSymbols(const string& obj_file, + const bool big_endian, + const typename ElfClass::Ehdr* elf_header, + const bool read_gnu_debug_link, + LoadSymbolsInfo* 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( + GetOffset(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 address_ranges = GetPtLoadSegmentRanges( + GetOffset(elf_header, elf_header->e_phoff), + elf_header->e_phnum); + module->SetAddressRanges(address_ranges); + + const Shdr* sections = + GetOffset(elf_header, elf_header->e_shoff); + const Shdr* section_names = sections + elf_header->e_shstrndx; + const char* names = + GetOffset(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 & SYMBOLS_AND_FILES) || + (options.symbol_data & INLINES)) { +#ifndef NO_STABS_SUPPORT + // Look for STABS debugging information, and load it if present. + const Shdr* stab_section = + FindElfSectionByName(".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(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 + + // See if there are export symbols available. + const Shdr* symtab_section = + FindElfSectionByName(".symtab", SHT_SYMTAB, + sections, names, names_end, + elf_header->e_shnum); + const Shdr* strtab_section = + FindElfSectionByName(".strtab", SHT_STRTAB, + sections, names, names_end, + elf_header->e_shnum); + if (symtab_section && strtab_section) { + info->LoadedSection(".symtab"); + + const uint8_t* symtab = + GetOffset(elf_header, + symtab_section->sh_offset); + const uint8_t* strtab = + GetOffset(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(".dynsym", SHT_DYNSYM, + sections, names, names_end, + elf_header->e_shnum); + const Shdr* dynstr_section = + FindElfSectionByName(".dynstr", SHT_STRTAB, + sections, names, names_end, + elf_header->e_shnum); + if (dynsym_section && dynstr_section) { + info->LoadedSection(".dynsym"); + + const uint8_t* dynsyms = + GetOffset(elf_header, + dynsym_section->sh_offset); + const uint8_t* dynstrs = + GetOffset(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; + } + } + + // Only Load .debug_info after loading symbol table to avoid duplicate + // PUBLIC records. + // Look for DWARF debugging information, and load it if present. + const Shdr* dwarf_section = + FindElfSectionByName(".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(".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(obj_file, elf_header, big_endian, + options.handle_inter_cu_refs, + options.symbol_data & INLINES, module)) { + fprintf(stderr, "%s: \".debug_info\" section found, but failed to load " + "DWARF debugging information\n", obj_file.c_str()); + } + } + } + + if (options.symbol_data & 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(".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(".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(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(".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(".got", SHT_PROGBITS, + sections, names, names_end, + elf_header->e_shnum); + const Shdr* text_section = + FindElfSectionByName(".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(obj_file, elf_header, ".eh_frame", + eh_frame_section, true, + got_section, text_section, big_endian, 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(".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(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 +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 +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(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(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 +bool InitModuleForElfClass(const typename ElfClass::Ehdr* elf_header, + const string& obj_filename, + const string& obj_os, + scoped_ptr& module) { + PageAllocator allocator; + wasteful_vector 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(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 +bool ReadSymbolDataElfClass(const typename ElfClass::Ehdr* elf_header, + const string& obj_filename, + const string& obj_os, + const std::vector& debug_dirs, + const DumpOptions& options, + Module** out_module) { + typedef typename ElfClass::Ehdr Ehdr; + + *out_module = NULL; + + scoped_ptr module; + if (!InitModuleForElfClass(elf_header, obj_filename, obj_os, + module)) { + return false; + } + + // Figure out what endianness this file is. + bool big_endian; + if (!ElfEndianness(elf_header, &big_endian)) + return false; + + LoadSymbolsInfo info(debug_dirs); + if (!LoadSymbols(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(&debug_elf_header)) || + !SanitizeDebugFile(debug_elf_header, debuglink_file, + obj_filename, + module->architecture().c_str(), + big_endian)) { + return false; + } + + if (!LoadSymbols(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& 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( + reinterpret_cast(obj_file), obj_filename, obj_os, + debug_dirs, options, module); + } + if (elfclass == ELFCLASS64) { + return ReadSymbolDataElfClass( + reinterpret_cast(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& 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; + if (elfclass == ELFCLASS32) { + if (!InitModuleForElfClass( + reinterpret_cast(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( + reinterpret_cast(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& 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(elf_header), + obj_file, obj_os, debug_dirs, options, module); +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/common/linux/dump_symbols.h b/shared/sentry/external/breakpad/src/common/linux/dump_symbols.h new file mode 100644 index 000000000..b033ce00e --- /dev/null +++ b/shared/sentry/external/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 +#include +#include + +#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& 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& debug_dirs, + const DumpOptions& options, + Module** module); + +} // namespace google_breakpad + +#endif // COMMON_LINUX_DUMP_SYMBOLS_H__ diff --git a/shared/sentry/external/breakpad/src/common/linux/dump_symbols_unittest.cc b/shared/sentry/external/breakpad/src/common/linux/dump_symbols_unittest.cc new file mode 100644 index 000000000..54c210962 --- /dev/null +++ b/shared/sentry/external/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 + +// dump_symbols_unittest.cc: +// Unittests for google_breakpad::DumpSymbols + +#include +#include +#include + +#include +#include + +#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& 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 +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 elfdata_v; + uint8_t* elfdata; +}; + +typedef Types 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(&header), + "foo", + "Linux", + vector(), + 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(), + 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(), + 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/shared/sentry/external/breakpad/src/common/linux/eintr_wrapper.h b/shared/sentry/external/breakpad/src/common/linux/eintr_wrapper.h new file mode 100644 index 000000000..3f1d18481 --- /dev/null +++ b/shared/sentry/external/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 + +// 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/shared/sentry/external/breakpad/src/common/linux/elf_core_dump.cc b/shared/sentry/external/breakpad/src/common/linux/elf_core_dump.cc new file mode 100644 index 000000000..f3206092b --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/linux/elf_core_dump.cc @@ -0,0 +1,203 @@ +// 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 +#include +#include + +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(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() : proc_mem_fd_(-1) {} + +ElfCoreDump::ElfCoreDump(const MemoryRange& content) + : content_(content), proc_mem_fd_(-1) {} + +ElfCoreDump::~ElfCoreDump() { + if (proc_mem_fd_ != -1) { + close(proc_mem_fd_); + proc_mem_fd_ = -1; + } +} + +void ElfCoreDump::SetContent(const MemoryRange& content) { + content_ = content; +} + +void ElfCoreDump::SetProcMem(int fd) { + if (proc_mem_fd_ != -1) { + close(proc_mem_fd_); + } + proc_mem_fd_ = fd; +} + +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(0); +} + +const ElfCoreDump::Phdr* ElfCoreDump::GetProgramHeader(unsigned index) const { + const Ehdr* header = GetHeader(); + if (header) { + return reinterpret_cast(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; + } + } + } + + /* fallback: if available, read from /proc//mem */ + if (proc_mem_fd_ != -1) { + off_t offset = virtual_address; + ssize_t r = pread(proc_mem_fd_, buffer, length, offset); + if (r < ssize_t(length)) { + return false; + } + 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/shared/sentry/external/breakpad/src/common/linux/elf_core_dump.h b/shared/sentry/external/breakpad/src/common/linux/elf_core_dump.h new file mode 100644 index 000000000..c8117a0e2 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/linux/elf_core_dump.h @@ -0,0 +1,157 @@ +// 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 +#include +#include +#include + +#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); + + ~ElfCoreDump(); + + // 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; + + // Sets the mem fd. + void SetProcMem(const int fd); + + private: + // Core dump content. + MemoryRange content_; + + // Descriptor for /proc//mem. + int proc_mem_fd_; +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_ELF_CORE_DUMP_H_ diff --git a/shared/sentry/external/breakpad/src/common/linux/elf_core_dump_unittest.cc b/shared/sentry/external/breakpad/src/common/linux/elf_core_dump_unittest.cc new file mode 100644 index 000000000..2399c12fd --- /dev/null +++ b/shared/sentry/external/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 + +#include +#include + +#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 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 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(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::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/shared/sentry/external/breakpad/src/common/linux/elf_gnu_compat.h b/shared/sentry/external/breakpad/src/common/linux/elf_gnu_compat.h new file mode 100644 index 000000000..0a3dfedb5 --- /dev/null +++ b/shared/sentry/external/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 + +// 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 + +// 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/shared/sentry/external/breakpad/src/common/linux/elf_symbols_to_module.cc b/shared/sentry/external/breakpad/src/common/linux/elf_symbols_to_module.cc new file mode 100644 index 000000000..81e985a72 --- /dev/null +++ b/shared/sentry/external/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 + +#include "common/linux/elf_symbols_to_module.h" + +#include +#include +#include + +#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(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(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/shared/sentry/external/breakpad/src/common/linux/elf_symbols_to_module.h b/shared/sentry/external/breakpad/src/common/linux/elf_symbols_to_module.h new file mode 100644 index 000000000..861f72529 --- /dev/null +++ b/shared/sentry/external/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 + +// 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 +#include + +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/shared/sentry/external/breakpad/src/common/linux/elf_symbols_to_module_unittest.cc b/shared/sentry/external/breakpad/src/common/linux/elf_symbols_to_module_unittest.cc new file mode 100644 index 000000000..6a860701b --- /dev/null +++ b/shared/sentry/external/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 + +// elf_symbols_to_module_unittest.cc: +// Unittests for google_breakpad::ELFSymbolsToModule + +#include + +#include +#include + +#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(section_contents.data()), + section_contents.size(), + reinterpret_cast(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 externs; +}; + +class ELFSymbolsToModuleTest32 : public ELFSymbolsToModuleTestFixture, + public TestWithParam { +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_SUITE_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 { +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_SUITE_P(Endian, + ELFSymbolsToModuleTest64, + ::testing::Values(kLittleEndian, kBigEndian)); diff --git a/shared/sentry/external/breakpad/src/common/linux/elfutils-inl.h b/shared/sentry/external/breakpad/src/common/linux/elfutils-inl.h new file mode 100644 index 000000000..e56b37a9f --- /dev/null +++ b/shared/sentry/external/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 +const T* GetOffset(const typename ElfClass::Ehdr* elf_header, + typename ElfClass::Off offset) { + return reinterpret_cast(reinterpret_cast(elf_header) + + offset); +} + +template +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/shared/sentry/external/breakpad/src/common/linux/elfutils.cc b/shared/sentry/external/breakpad/src/common/linux/elfutils.cc new file mode 100644 index 000000000..aa95357a3 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/linux/elfutils.cc @@ -0,0 +1,243 @@ +// 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 +#include + +#include "common/linux/linux_libc_support.h" +#include "common/linux/elfutils-inl.h" + +namespace google_breakpad { + +namespace { + +template +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(elf_base); + assert(elf_header->e_ident[EI_CLASS] == ElfClass::kClass); + + if (elf_header->e_shoff == 0) { + *section_start = NULL; + *section_size = 0; + return; + } + + const Shdr* sections = + GetOffset(elf_header, elf_header->e_shoff); + const Shdr* section_names = sections + elf_header->e_shstrndx; + const char* names = + GetOffset(elf_header, section_names->sh_offset); + const char* names_end = names + section_names->sh_size; + + const Shdr* section = + FindElfSectionByName(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 +void FindElfClassSegment(const char* elf_base, + typename ElfClass::Word segment_type, + wasteful_vector* 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(elf_base); + assert(elf_header->e_ident[EI_CLASS] == ElfClass::kClass); + + const Phdr* phdrs = + GetOffset(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(elf_base), + ELFMAG, SELFMAG) == 0; +} + +int ElfClass(const void* elf_base) { + const ElfW(Ehdr)* elf_header = + reinterpret_cast(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(elf_mapped_base); + + if (cls == ELFCLASS32) { + FindElfClassSection(elf_base, section_name, section_type, + section_start, section_size); + return *section_start != NULL; + } else if (cls == ELFCLASS64) { + FindElfClassSection(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* 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(elf_mapped_base); + + if (cls == ELFCLASS32) { + FindElfClassSegment(elf_base, segment_type, segments); + return true; + } else if (cls == ELFCLASS64) { + FindElfClassSegment(elf_base, segment_type, segments); + return true; + } + + return false; +} + +template +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(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(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( + segment_start, segment_size, dynstr_start, + dynstr_size, soname, soname_size) + : FindElfSoNameFromDynamicSection( + segment_start, segment_size, dynstr_start, + dynstr_size, soname, soname_size); +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/common/linux/elfutils.h b/shared/sentry/external/breakpad/src/common/linux/elfutils.h new file mode 100644 index 000000000..ec5872a4f --- /dev/null +++ b/shared/sentry/external/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 +#include +#include + +#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 +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* 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 +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/shared/sentry/external/breakpad/src/common/linux/file_id.cc b/shared/sentry/external/breakpad/src/common/linux/file_id.cc new file mode 100644 index 000000000..b483eb5c0 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/linux/file_id.cc @@ -0,0 +1,203 @@ +// 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 +#include +#include + +#include +#include + +#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 { +namespace elf { + +// 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& 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(section) + length; + const Nhdr* note_header = reinterpret_cast(section); + while (reinterpret_cast(note_header) < section_end) { + if (note_header->n_type == NT_GNU_BUILD_ID) + break; + note_header = reinterpret_cast( + reinterpret_cast(note_header) + sizeof(Nhdr) + + NOTE_PADDING(note_header->n_namesz) + + NOTE_PADDING(note_header->n_descsz)); + } + if (reinterpret_cast(note_header) >= section_end || + note_header->n_descsz == 0) { + return false; + } + + const uint8_t* build_id = reinterpret_cast(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& identifier) { + PageAllocator allocator; + // lld normally creates 2 PT_NOTEs, gold normally creates 1. + auto_wasteful_vector 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& identifier) { + identifier.resize(kMDGUIDSize); + + 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. + my_memset(&identifier[0], 0, kMDGUIDSize); + const uint8_t* ptr = reinterpret_cast(text_section); + const uint8_t* ptr_end = ptr + std::min(text_size, static_cast(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& 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& 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& 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(identifier_swapped); + *data1 = htonl(*data1); + uint16_t* data2 = reinterpret_cast(identifier_swapped + 4); + *data2 = htons(*data2); + uint16_t* data3 = reinterpret_cast(identifier_swapped + 6); + *data3 = htons(*data3); + + return bytes_to_hex_string(identifier_swapped, kMDGUIDSize); +} + +// static +string FileID::ConvertIdentifierToString( + const wasteful_vector& identifier) { + return bytes_to_hex_string(&identifier[0], identifier.size()); +} + +} // elf +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/common/linux/file_id.h b/shared/sentry/external/breakpad/src/common/linux/file_id.h new file mode 100644 index 000000000..8556a2e5a --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/linux/file_id.h @@ -0,0 +1,90 @@ +// 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 +#include + +#include "common/linux/guid_creator.h" +#include "common/memory_allocator.h" +#include "common/using_std_string.h" + +namespace google_breakpad { +namespace elf { + +// 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& 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& 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& identifier); + + // Convert the entire |identifier| data to a hex string. + static string ConvertIdentifierToString( + const wasteful_vector& identifier); + + private: + // Storage for the path specified + string path_; +}; + +} // namespace elf +} // namespace google_breakpad + +#endif // COMMON_LINUX_FILE_ID_H__ diff --git a/shared/sentry/external/breakpad/src/common/linux/file_id_unittest.cc b/shared/sentry/external/breakpad/src/common/linux/file_id_unittest.cc new file mode 100644 index 000000000..43c9af7b5 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/linux/file_id_unittest.cc @@ -0,0 +1,383 @@ +// 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 +#include +#include +#include +#include + +#include +#include + +#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/tests/file_utils.h" +#include "common/using_std_string.h" +#include "breakpad_googletest_includes.h" + +using namespace google_breakpad; +using google_breakpad::elf::FileID; +using google_breakpad::elf::kDefaultBuildIdSize; +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 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"; + ASSERT_TRUE(CopyFile(exe_name, templ)); + pid_t pid; + char* argv[] = { + const_cast("strip"), + const_cast(templ.c_str()), + nullptr, + }; + ASSERT_EQ(0, posix_spawnp(&pid, argv[0], nullptr, nullptr, argv, nullptr)); + int status; + ASSERT_EQ(pid, waitpid(pid, &status, 0)); + ASSERT_TRUE(WIFEXITED(status)); + ASSERT_EQ(0, WEXITSTATUS(status)); + + 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 +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 + 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 elfdata_v; + uint8_t* elfdata; + PageAllocator allocator; +}; + +typedef Types 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("\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("\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/shared/sentry/external/breakpad/src/common/linux/google_crashdump_uploader.cc b/shared/sentry/external/breakpad/src/common/linux/google_crashdump_uploader.cc new file mode 100644 index 000000000..a0d940b61 --- /dev/null +++ b/shared/sentry/external/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 +#include +#include + +#include + +#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/shared/sentry/external/breakpad/src/common/linux/google_crashdump_uploader.h b/shared/sentry/external/breakpad/src/common/linux/google_crashdump_uploader.h new file mode 100644 index 000000000..a2d0575b5 --- /dev/null +++ b/shared/sentry/external/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 +#include + +#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 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 parameters_; +}; +} + +#endif // COMMON_LINUX_GOOGLE_CRASHDUMP_UPLOADER_H_ diff --git a/shared/sentry/external/breakpad/src/common/linux/google_crashdump_uploader_test.cc b/shared/sentry/external/breakpad/src/common/linux/google_crashdump_uploader_test.cc new file mode 100644 index 000000000..3d6612e82 --- /dev/null +++ b/shared/sentry/external/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 + +#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& 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/shared/sentry/external/breakpad/src/common/linux/guid_creator.cc b/shared/sentry/external/breakpad/src/common/linux/guid_creator.cc new file mode 100644 index 000000000..637406382 --- /dev/null +++ b/shared/sentry/external/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 +#endif + +#include "common/linux/eintr_wrapper.h" +#include "common/linux/guid_creator.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(HAVE_SYS_RANDOM_H) +#include +#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(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(guid); + int read_bytes = getrandom(buf, sizeof(GUID), GRND_NONBLOCK); + + return (read_bytes == static_cast(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(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(sizeof(GUID))); + } + + // Populate the GUID using a stream of random bytes obtained from rand(). + static void CreateGUIDFromRand(GUID *guid) { + char *buf = reinterpret_cast(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/shared/sentry/external/breakpad/src/common/linux/guid_creator.h b/shared/sentry/external/breakpad/src/common/linux/guid_creator.h new file mode 100644 index 000000000..c86d856c4 --- /dev/null +++ b/shared/sentry/external/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/shared/sentry/external/breakpad/src/common/linux/http_upload.cc b/shared/sentry/external/breakpad/src/common/linux/http_upload.cc new file mode 100644 index 000000000..ace12b84e --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/linux/http_upload.cc @@ -0,0 +1,230 @@ +// 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 +#include +#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(userp); + size_t real_size = size * nmemb; + response->append(reinterpret_cast(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 map& parameters, + const map& 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; + + if (!CheckParameters(parameters)) + return false; + + // 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"); + map::const_iterator iter = parameters.begin(); + for (; iter != parameters.end(); ++iter) + (*curl_formadd)(&formpost, &lastptr, + CURLFORM_COPYNAME, iter->first.c_str(), + CURLFORM_COPYCONTENTS, iter->second.c_str(), + CURLFORM_END); + + // Add form files. + 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(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"); +} + +// static +bool HTTPUpload::CheckParameters(const map& parameters) { + for (map::const_iterator pos = parameters.begin(); + pos != parameters.end(); ++pos) { + const string& str = pos->first; + if (str.size() == 0) + return false; // disallow empty parameter names + for (unsigned int i = 0; i < str.size(); ++i) { + int c = str[i]; + if (c < 32 || c == '"' || c > 127) { + return false; + } + } + } + return true; +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/common/linux/http_upload.h b/shared/sentry/external/breakpad/src/common/linux/http_upload.h new file mode 100644 index 000000000..13f3d56cc --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/linux/http_upload.h @@ -0,0 +1,90 @@ +// 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 +// a set of string parameters (key/value pairs), and a file to upload. + +#ifndef COMMON_LINUX_HTTP_UPLOAD_H__ +#define COMMON_LINUX_HTTP_UPLOAD_H__ + +#include +#include + +#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 . + // Parameter names must contain only printable ASCII characters, + // and may not contain a quote (") character. + // 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 map& parameters, + const map& 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 that the given list of parameters has only printable + // ASCII characters in the parameter name, and does not contain + // any quote (") characters. Returns true if so. + static bool CheckParameters(const map& parameters); + + // 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/shared/sentry/external/breakpad/src/common/linux/ignore_ret.h b/shared/sentry/external/breakpad/src/common/linux/ignore_ret.h new file mode 100644 index 000000000..efd274c20 --- /dev/null +++ b/shared/sentry/external/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/shared/sentry/external/breakpad/src/common/linux/libcurl_wrapper.cc b/shared/sentry/external/breakpad/src/common/linux/libcurl_wrapper.cc new file mode 100644 index 000000000..fdb200f8e --- /dev/null +++ b/shared/sentry/external/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 + +#include +#include + +#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(userp); + size_t real_size = size * nmemb; + response->append(reinterpret_cast(ptr), real_size); + return real_size; +} + +bool LibcurlWrapper::SendRequest(const string& url, + const std::map& parameters, + long* http_status_code, + string* http_header_data, + string* http_response_data) { + if (!CheckInit()) return false; + + std::map::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(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(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(http_header_data)); + } + CURLcode err_code = CURLE_OK; + err_code = (*easy_perform_)(curl_); + easy_strerror_ = reinterpret_cast + (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/shared/sentry/external/breakpad/src/common/linux/libcurl_wrapper.h b/shared/sentry/external/breakpad/src/common/linux/libcurl_wrapper.h new file mode 100644 index 000000000..823f83c7e --- /dev/null +++ b/shared/sentry/external/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 +#include + +#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& 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/shared/sentry/external/breakpad/src/common/linux/linux_libc_support.cc b/shared/sentry/external/breakpad/src/common/linux/linux_libc_support.cc new file mode 100644 index 000000000..dd2929626 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/linux/linux_libc_support.cc @@ -0,0 +1,237 @@ +// 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 + +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; +} + +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/shared/sentry/external/breakpad/src/common/linux/linux_libc_support.h b/shared/sentry/external/breakpad/src/common/linux/linux_libc_support.h new file mode 100644 index 000000000..ec5a8d6b6 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/linux/linux_libc_support.h @@ -0,0 +1,96 @@ +// 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 +#include +#include + +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); + +// 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/shared/sentry/external/breakpad/src/common/linux/linux_libc_support_unittest.cc b/shared/sentry/external/breakpad/src/common/linux/linux_libc_support_unittest.cc new file mode 100644 index 000000000..adadfed44 --- /dev/null +++ b/shared/sentry/external/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/shared/sentry/external/breakpad/src/common/linux/memory_mapped_file.cc b/shared/sentry/external/breakpad/src/common/linux/memory_mapped_file.cc new file mode 100644 index 000000000..99362945c --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/linux/memory_mapped_file.cc @@ -0,0 +1,108 @@ +// 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 +#include +#if defined(__ANDROID__) +#include +#endif +#include + +#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 + +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(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; + } + + size_t content_len = file_len - offset; + void* data = sys_mmap(NULL, content_len, PROT_READ, MAP_PRIVATE, fd, offset); + sys_close(fd); + if (data == MAP_FAILED) { + return false; + } + + content_.Set(data, content_len); + return true; +} + +void MemoryMappedFile::Unmap() { + if (content_.data()) { + sys_munmap(const_cast(content_.data()), content_.length()); + content_.Set(NULL, 0); + } +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/common/linux/memory_mapped_file.h b/shared/sentry/external/breakpad/src/common/linux/memory_mapped_file.h new file mode 100644 index 000000000..fa660cc91 --- /dev/null +++ b/shared/sentry/external/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 +#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/shared/sentry/external/breakpad/src/common/linux/memory_mapped_file_unittest.cc b/shared/sentry/external/breakpad/src/common/linux/memory_mapped_file_unittest.cc new file mode 100644 index 000000000..fad59f40c --- /dev/null +++ b/shared/sentry/external/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 +#include +#include + +#include + +#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/shared/sentry/external/breakpad/src/common/linux/safe_readlink.cc b/shared/sentry/external/breakpad/src/common/linux/safe_readlink.cc new file mode 100644 index 000000000..870c28af3 --- /dev/null +++ b/shared/sentry/external/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 + +#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(result_size) < buffer_size) { + buffer[result_size] = '\0'; + return true; + } + return false; +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/common/linux/safe_readlink.h b/shared/sentry/external/breakpad/src/common/linux/safe_readlink.h new file mode 100644 index 000000000..4ae131b58 --- /dev/null +++ b/shared/sentry/external/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 + +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 +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/shared/sentry/external/breakpad/src/common/linux/safe_readlink_unittest.cc b/shared/sentry/external/breakpad/src/common/linux/safe_readlink_unittest.cc new file mode 100644 index 000000000..d346b2a80 --- /dev/null +++ b/shared/sentry/external/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/shared/sentry/external/breakpad/src/common/linux/symbol_collector_client.cc b/shared/sentry/external/breakpad/src/common/linux/symbol_collector_client.cc new file mode 100644 index 000000000..92b25ddba --- /dev/null +++ b/shared/sentry/external/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 + +#include +#include + +#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/shared/sentry/external/breakpad/src/common/linux/symbol_collector_client.h b/shared/sentry/external/breakpad/src/common/linux/symbol_collector_client.h new file mode 100644 index 000000000..0e23242a2 --- /dev/null +++ b/shared/sentry/external/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 + +#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/shared/sentry/external/breakpad/src/common/linux/symbol_upload.cc b/shared/sentry/external/breakpad/src/common/linux/symbol_upload.cc new file mode 100644 index 000000000..1d5ff7196 --- /dev/null +++ b/shared/sentry/external/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 +#include + +#include +#include +#include + +#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* 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 +bool ModuleDataForSymbolFile(const string& file, + std::vector* 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 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 module_parts, + const string& compacted_id) { + std::map parameters; + // Add parameters + if (!options.version.empty()) + parameters["version"] = options.version; + + // MODULE + // 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 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 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 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/shared/sentry/external/breakpad/src/common/linux/symbol_upload.h b/shared/sentry/external/breakpad/src/common/linux/symbol_upload.h new file mode 100644 index 000000000..9033152bf --- /dev/null +++ b/shared/sentry/external/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 + +#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/shared/sentry/external/breakpad/src/common/linux/synth_elf.cc b/shared/sentry/external/breakpad/src/common/linux/synth_elf.cc new file mode 100644 index 000000000..2ba25e611 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/linux/synth_elf.cc @@ -0,0 +1,263 @@ +#include "common/linux/synth_elf.h" + +#include +#include +#include +#include + +#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& section) { + // 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::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(¬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/shared/sentry/external/breakpad/src/common/linux/synth_elf.h b/shared/sentry/external/breakpad/src/common/linux/synth_elf.h new file mode 100644 index 000000000..90fa28c0d --- /dev/null +++ b/shared/sentry/external/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 + +// 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 +#include +#include +#include +#include + +#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 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 sections_; + + void AppendSection(ElfSection& section); +}; + +// 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/shared/sentry/external/breakpad/src/common/linux/synth_elf_unittest.cc b/shared/sentry/external/breakpad/src/common/linux/synth_elf_unittest.cc new file mode 100644 index 000000000..fb3601e65 --- /dev/null +++ b/shared/sentry/external/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 + +// synth_elf_unittest.cc: +// Unittests for google_breakpad::synth_elf::ELF + +#include + +#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 +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 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(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(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(contents.data() + header->e_shoff); + EXPECT_EQ(0U, shdr[0].sh_name); + EXPECT_EQ(static_cast(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(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(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(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(contents.data() + header->e_shoff); + EXPECT_EQ(0U, shdr[0].sh_name); + EXPECT_EQ(static_cast(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(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(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(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(contents.data() + header->e_phoff); + EXPECT_EQ(static_cast(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("\x42\x02\0\0"), + 4); + notes.AddNote(2, "a", reinterpret_cast("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/shared/sentry/external/breakpad/src/common/linux/tests/auto_testfile.h b/shared/sentry/external/breakpad/src/common/linux/tests/auto_testfile.h new file mode 100644 index 000000000..92fe017b9 --- /dev/null +++ b/shared/sentry/external/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 +#include + +#include + +#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(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(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(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/shared/sentry/external/breakpad/src/common/linux/tests/crash_generator.cc b/shared/sentry/external/breakpad/src/common/linux/tests/crash_generator.cc new file mode 100644 index 000000000..a70df28ad --- /dev/null +++ b/shared/sentry/external/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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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(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(shared_memory_)[index]; +} + +pid_t* CrashGenerator::GetThreadIdPointer(unsigned index) { + return reinterpret_cast(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/shared/sentry/external/breakpad/src/common/linux/tests/crash_generator.h b/shared/sentry/external/breakpad/src/common/linux/tests/crash_generator.h new file mode 100644 index 000000000..7e2fcbf98 --- /dev/null +++ b/shared/sentry/external/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 + +#include + +#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/shared/sentry/external/breakpad/src/common/linux/ucontext_constants.h b/shared/sentry/external/breakpad/src/common/linux/ucontext_constants.h new file mode 100644 index 000000000..c390508a1 --- /dev/null +++ b/shared/sentry/external/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/shared/sentry/external/breakpad/src/common/long_string_dictionary.cc b/shared/sentry/external/breakpad/src/common/long_string_dictionary.cc new file mode 100644 index 000000000..46bbf6137 --- /dev/null +++ b/shared/sentry/external/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 +#include + +#include +#include + +#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/shared/sentry/external/breakpad/src/common/long_string_dictionary.h b/shared/sentry/external/breakpad/src/common/long_string_dictionary.h new file mode 100644 index 000000000..68bf03de2 --- /dev/null +++ b/shared/sentry/external/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 + +#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/shared/sentry/external/breakpad/src/common/long_string_dictionary_unittest.cc b/shared/sentry/external/breakpad/src/common/long_string_dictionary_unittest.cc new file mode 100644 index 000000000..f9b645ba7 --- /dev/null +++ b/shared/sentry/external/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 +#include + +#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 + int keyNumber; + sscanf(entry->key, "key%d", &keyNumber); + + // Extract valueNumber from a string of the form value + 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/shared/sentry/external/breakpad/src/common/mac/Breakpad.xcconfig b/shared/sentry/external/breakpad/src/common/mac/Breakpad.xcconfig new file mode 100644 index 000000000..f09136908 --- /dev/null +++ b/shared/sentry/external/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/shared/sentry/external/breakpad/src/common/mac/BreakpadDebug.xcconfig b/shared/sentry/external/breakpad/src/common/mac/BreakpadDebug.xcconfig new file mode 100644 index 000000000..94cdd8cfc --- /dev/null +++ b/shared/sentry/external/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/shared/sentry/external/breakpad/src/common/mac/BreakpadRelease.xcconfig b/shared/sentry/external/breakpad/src/common/mac/BreakpadRelease.xcconfig new file mode 100644 index 000000000..920f277db --- /dev/null +++ b/shared/sentry/external/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/shared/sentry/external/breakpad/src/common/mac/GTMDefines.h b/shared/sentry/external/breakpad/src/common/mac/GTMDefines.h new file mode 100644 index 000000000..04fcf6d0c --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/mac/GTMDefines.h @@ -0,0 +1,456 @@ +// +// 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 +#include + +#ifdef __OBJC__ +#include +#endif // __OBJC__ + +#if TARGET_OS_IPHONE +#include +#endif // TARGET_OS_IPHONE + +// Not all MAC_OS_X_VERSION_10_X macros defined in past SDKs +#ifndef MAC_OS_X_VERSION_10_5 + #define MAC_OS_X_VERSION_10_5 1050 +#endif +#ifndef MAC_OS_X_VERSION_10_6 + #define MAC_OS_X_VERSION_10_6 1060 +#endif +#ifndef MAC_OS_X_VERSION_10_7 + #define MAC_OS_X_VERSION_10_7 1070 +#endif + +// 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 + +// To simplify support for 64bit (and Leopard in general), we provide the type +// defines for non Leopard SDKs +#if !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) + // NSInteger/NSUInteger and Max/Mins + #ifndef NSINTEGER_DEFINED + #if (defined(__LP64__) && __LP64__) || NS_BUILD_32_LIKE_64 + typedef long NSInteger; + typedef unsigned long NSUInteger; + #else + typedef int NSInteger; + typedef unsigned int NSUInteger; + #endif + #define NSIntegerMax LONG_MAX + #define NSIntegerMin LONG_MIN + #define NSUIntegerMax ULONG_MAX + #define NSINTEGER_DEFINED 1 + #endif // NSINTEGER_DEFINED + // CGFloat + #ifndef CGFLOAT_DEFINED + #if defined(__LP64__) && __LP64__ + // This really is an untested path (64bit on Tiger?) + typedef double CGFloat; + #define CGFLOAT_MIN DBL_MIN + #define CGFLOAT_MAX DBL_MAX + #define CGFLOAT_IS_DOUBLE 1 + #else /* !defined(__LP64__) || !__LP64__ */ + typedef float CGFloat; + #define CGFLOAT_MIN FLT_MIN + #define CGFLOAT_MAX FLT_MAX + #define CGFLOAT_IS_DOUBLE 0 + #endif /* !defined(__LP64__) || !__LP64__ */ + #define CGFLOAT_DEFINED 1 + #endif // CGFLOAT_DEFINED +#endif // MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5 + +// 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 || !(MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) + #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 + +// ============================================================================ + +// To simplify support for both Leopard and Snow Leopard we declare +// the Snow Leopard protocols that we need here. +#if !defined(GTM_10_6_PROTOCOLS_DEFINED) && !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) +#define GTM_10_6_PROTOCOLS_DEFINED 1 +@protocol NSConnectionDelegate +@end +@protocol NSAnimationDelegate +@end +@protocol NSImageDelegate +@end +@protocol NSTabViewDelegate +@end +#endif // !defined(GTM_10_6_PROTOCOLS_DEFINED) && !(MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_6) + +// 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/shared/sentry/external/breakpad/src/common/mac/GTMLogger.h b/shared/sentry/external/breakpad/src/common/mac/GTMLogger.h new file mode 100644 index 000000000..c4fd14029 --- /dev/null +++ b/shared/sentry/external/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 +#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= +// +// 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 writer_; + id formatter_; + id 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)writer + formatter:(id)formatter + filter:(id)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)writer + formatter:(id)formatter + filter:(id)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)writer; +- (void)setWriter:(id)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)formatter; +- (void)setFormatter:(id)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)filter; +- (void)setFilter:(id)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 +// 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) +// 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) +@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) +@end // GTMLoggerLogWriter + + +// +// Log Formatters +// + +// Protocol to be implemented by a GTMLogFormatter instance. +@protocol GTMLogFormatter +// 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 + +// 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 +// 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 +@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 +@end // GTMLogNoFilter + + +// Base class for custom level filters. Not for direct use, use the minimum +// or maximum level subclasses below. +@interface GTMLogAllowedLevelFilter : NSObject { + @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/shared/sentry/external/breakpad/src/common/mac/GTMLogger.m b/shared/sentry/external/breakpad/src/common/mac/GTMLogger.m new file mode 100644 index 000000000..ebc5836a2 --- /dev/null +++ b/shared/sentry/external/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 +#import +#import +#import + + +#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 writer = [NSFileHandle fileHandleWithStandardOutput]; + id fr = [[[GTMLogStandardFormatter alloc] init] + autorelease]; + id 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)writer + formatter:(id)formatter + filter:(id)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)writer + formatter:(id)formatter + filter:(id)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)writer { + return [[writer_ retain] autorelease]; +} + +- (void)setWriter:(id)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)formatter { + return [[formatter_ retain] autorelease]; +} + +- (void)setFormatter:(id)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)filter { + return [[filter_ retain] autorelease]; +} + +- (void)setFilter:(id)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 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/shared/sentry/external/breakpad/src/common/mac/HTTPGetRequest.h b/shared/sentry/external/breakpad/src/common/mac/HTTPGetRequest.h new file mode 100644 index 000000000..3aa316b54 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/mac/HTTPGetRequest.h @@ -0,0 +1,42 @@ +// Copyright (c) 2020, 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 + +#import "HTTPRequest.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + Represents a HTTP GET request + */ +@interface HTTPGetRequest : HTTPRequest +@end + +NS_ASSUME_NONNULL_END diff --git a/shared/sentry/external/breakpad/src/common/mac/HTTPGetRequest.m b/shared/sentry/external/breakpad/src/common/mac/HTTPGetRequest.m new file mode 100644 index 000000000..f9b7bf0ec --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/mac/HTTPGetRequest.m @@ -0,0 +1,39 @@ +// Copyright (c) 2020, 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 "HTTPGetRequest.h" + +@implementation HTTPGetRequest + +//============================================================================= +- (NSString*)HTTPMethod { + return @"GET"; +} + +@end diff --git a/shared/sentry/external/breakpad/src/common/mac/HTTPMultipartUpload.h b/shared/sentry/external/breakpad/src/common/mac/HTTPMultipartUpload.h new file mode 100644 index 000000000..56d5394db --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/mac/HTTPMultipartUpload.h @@ -0,0 +1,63 @@ +// 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 + +#import "HTTPRequest.h" +/** + Represents a multipart/form-data HTTP upload (POST request). + Each parameter pair is sent as a boundary. + Each file is sent with a name field in addition to the filename and data. + */ +@interface HTTPMultipartUpload : HTTPRequest { + @protected + NSDictionary* parameters_; // The key/value pairs for sending data (STRONG) + NSMutableDictionary* files_; // Dictionary of name/file-path (STRONG) + NSString* boundary_; // The boundary string (STRONG) +} + +/** + Sets the parameters that will be sent in the multipart POST request. + */ +- (void)setParameters:(NSDictionary*)parameters; +- (NSDictionary*)parameters; + +/** + Adds a file to be uploaded in the multipart POST request, by its file path. + */ +- (void)addFileAtPath:(NSString*)path name:(NSString*)name; + +/** + Adds a file to be uploaded in the multipart POST request, by its name and + contents. + */ +- (void)addFileContents:(NSData*)data name:(NSString*)name; +- (NSDictionary*)files; + +@end diff --git a/shared/sentry/external/breakpad/src/common/mac/HTTPMultipartUpload.m b/shared/sentry/external/breakpad/src/common/mac/HTTPMultipartUpload.m new file mode 100644 index 000000000..2aa64f791 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/mac/HTTPMultipartUpload.m @@ -0,0 +1,170 @@ +// 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" +#import "encoding_util.h" + +@interface HTTPMultipartUpload (PrivateMethods) +- (NSString*)multipartBoundary; +// Each of the following methods will append the starting multipart boundary, +// but not the ending one. +- (NSData*)formDataForKey:(NSString*)key value:(NSString*)value; +- (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*)formDataForKey:(NSString*)key value:(NSString*)value { + NSMutableData* data = [NSMutableData data]; + [self appendBoundaryData:data]; + + NSString* escaped = PercentEncodeNSString(key); + NSString* fmt = @"Content-Disposition: form-data; name=\"%@\"\r\n\r\n%@\r\n"; + NSString *form = [NSString stringWithFormat:fmt, escaped, value]; + + [data appendData:[form dataUsingEncoding:NSUTF8StringEncoding]]; + return data; +} + +//============================================================================= +- (void)appendBoundaryData:(NSMutableData*)data { + NSString* fmt = @"--%@\r\n"; + NSString* pre = [NSString stringWithFormat:fmt, boundary_]; + + [data appendData:[pre dataUsingEncoding:NSUTF8StringEncoding]]; +} + +//============================================================================= +#pragma mark - +#pragma mark || Public || +//============================================================================= +- (id)initWithURL:(NSURL*)url { + if ((self = [super initWithURL:url])) { + boundary_ = [[self multipartBoundary] retain]; + files_ = [[NSMutableDictionary alloc] init]; + } + + return self; +} + +//============================================================================= +- (void)dealloc { + [parameters_ release]; + [files_ release]; + [boundary_ release]; + + [super dealloc]; +} + +//============================================================================= +- (void)setParameters:(NSDictionary*)parameters { + if (parameters != parameters_) { + [parameters_ release]; + parameters_ = [parameters copy]; + } +} + +//============================================================================= +- (NSDictionary*)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_; +} + +//============================================================================= +- (NSString*)HTTPMethod { + return @"POST"; +} + +//============================================================================= +- (NSString*)contentType { + return [NSString + stringWithFormat:@"multipart/form-data; boundary=%@", boundary_]; +} + +//============================================================================= +- (NSData*)bodyData { + NSMutableData* postBody = [NSMutableData data]; + + // Add any parameters to the message + NSArray* parameterKeys = [parameters_ allKeys]; + NSString* key; + + NSInteger count = [parameterKeys count]; + for (NSInteger i = 0; i < count; ++i) { + key = [parameterKeys objectAtIndex:i]; + [postBody appendData:[self formDataForKey:key + value:[parameters_ objectForKey:key]]]; + } + + // Add any files to the message + NSArray* fileNames = [files_ allKeys]; + for (NSString* name in fileNames) { + // First append boundary + [self appendBoundaryData:postBody]; + // Then the formdata + id fileOrData = [files_ objectForKey:name]; + [HTTPRequest appendFileToBodyData:postBody + withName:name + withFileOrData:fileOrData]; + } + + NSString* epilogue = [NSString stringWithFormat:@"\r\n--%@--\r\n", boundary_]; + [postBody appendData:[epilogue dataUsingEncoding:NSUTF8StringEncoding]]; + + return postBody; +} + +@end diff --git a/shared/sentry/external/breakpad/src/common/mac/HTTPPutRequest.h b/shared/sentry/external/breakpad/src/common/mac/HTTPPutRequest.h new file mode 100644 index 000000000..431ea6104 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/mac/HTTPPutRequest.h @@ -0,0 +1,51 @@ +// Copyright (c) 2020, 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 + +#import "HTTPRequest.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + Represents an HTTP PUT request. + */ +@interface HTTPPutRequest : HTTPRequest { + @protected + NSString* file_; +} + +/** + Sets the path of the file that will be sent in the PUT request. + */ +- (void)setFile:(NSString*)file; + +@end + +NS_ASSUME_NONNULL_END diff --git a/shared/sentry/external/breakpad/src/common/mac/HTTPPutRequest.m b/shared/sentry/external/breakpad/src/common/mac/HTTPPutRequest.m new file mode 100644 index 000000000..f6f1400a7 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/mac/HTTPPutRequest.m @@ -0,0 +1,56 @@ +// Copyright (c) 2020, 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 "HTTPPutRequest.h" + +@implementation HTTPPutRequest + +//============================================================================= +- (void)dealloc { + [file_ release]; + + [super dealloc]; +} + +//============================================================================= +- (void)setFile:(NSString*)file { + file_ = [file copy]; +} + +//============================================================================= +- (NSString*)HTTPMethod { + return @"PUT"; +} + +//============================================================================= +- (NSData*)bodyData { + return [NSData dataWithContentsOfFile:file_]; +} + +@end diff --git a/shared/sentry/external/breakpad/src/common/mac/HTTPRequest.h b/shared/sentry/external/breakpad/src/common/mac/HTTPRequest.h new file mode 100644 index 000000000..b78fad6ba --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/mac/HTTPRequest.h @@ -0,0 +1,73 @@ +// Copyright (c) 2020, 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 + +NS_ASSUME_NONNULL_BEGIN +/** + Represents a single HTTP request. Sending the request is synchronous. + Once the send is complete, the response will be set. + + This is a base interface that specific HTTP requests derive from. + It is not intended to be instantiated directly. + */ +@interface HTTPRequest : NSObject { + @protected + NSURL* URL_; // The destination URL (STRONG) + NSHTTPURLResponse* response_; // The response from the send (STRONG) +} + +/** + Initializes the HTTPRequest and sets its URL. + */ +- (id)initWithURL:(NSURL*)URL; + +- (NSURL*)URL; + +- (NSHTTPURLResponse*)response; + +- (NSString*)HTTPMethod; // Internal, don't call outside class hierarchy. + +- (NSString*)contentType; // Internal, don't call outside class hierarchy. + +- (NSData*)bodyData; // Internal, don't call outside class hierarchy. + +- (NSData*)send:(NSError**)error; + +/** + Appends a file to the HTTP request, either by filename or by file content + (in the form of NSData). + */ ++ (void)appendFileToBodyData:(NSMutableData*)data + withName:(NSString*)name + withFileOrData:(id)fileOrData; + +@end + +NS_ASSUME_NONNULL_END diff --git a/shared/sentry/external/breakpad/src/common/mac/HTTPRequest.m b/shared/sentry/external/breakpad/src/common/mac/HTTPRequest.m new file mode 100644 index 000000000..925ab582b --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/mac/HTTPRequest.m @@ -0,0 +1,268 @@ +// Copyright (c) 2020, 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 "HTTPRequest.h" + +#include +#include + +#if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && defined(__IPHONE_7_0) && \ + __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_7_0) +#import +#define HAS_BACKGROUND_TASK_API 1 +#else +#define HAS_BACKGROUND_TASK_API 0 +#endif + +#import "encoding_util.h" + +#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) +#define USE_NSURLSESSION 1 +#else +#define USE_NSURLSESSION 0 +#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** outResponse, + NSError** outError) { +#if USE_NSURLSESSION + __block NSData* result = nil; + __block NSError* error = nil; + __block NSURLResponse* response = nil; + dispatch_semaphore_t waitSemaphone = dispatch_semaphore_create(0); + + NSURLSessionConfiguration* config = + [NSURLSessionConfiguration defaultSessionConfiguration]; + [config setTimeoutIntervalForRequest:240.0]; + NSURLSession* session = [NSURLSession sessionWithConfiguration:config]; + NSURLSessionDataTask *task = [session + dataTaskWithRequest:req + completionHandler:^(NSData* data, NSURLResponse* resp, NSError* err) { + if (outError) + error = [err retain]; + if (outResponse) + response = [resp retain]; + if (err == nil) + result = [data retain]; + dispatch_semaphore_signal(waitSemaphone); + }]; + [task resume]; + +#if HAS_BACKGROUND_TASK_API + // Used to guard against ending the background task twice, which UIKit + // considers to be an error. + __block BOOL isBackgroundTaskActive = YES; + __block UIBackgroundTaskIdentifier backgroundTaskIdentifier = + UIBackgroundTaskInvalid; + backgroundTaskIdentifier = [UIApplication.sharedApplication + beginBackgroundTaskWithName:@"Breakpad Upload" + expirationHandler:^{ + if (!isBackgroundTaskActive) { + return; + } + isBackgroundTaskActive = NO; + + [task cancel]; + [UIApplication.sharedApplication + endBackgroundTask:backgroundTaskIdentifier]; + }]; +#endif // HAS_BACKGROUND_TASK_API + + dispatch_semaphore_wait(waitSemaphone, DISPATCH_TIME_FOREVER); + dispatch_release(waitSemaphone); + +#if HAS_BACKGROUND_TASK_API + if (backgroundTaskIdentifier != UIBackgroundTaskInvalid) { + // Dispatch to main queue in order to synchronize access to + // `isBackgroundTaskActive` with the background task expiration handler, + // which is always run on the main thread. + dispatch_async(dispatch_get_main_queue(), ^{ + if (!isBackgroundTaskActive) { + return; + } + isBackgroundTaskActive = NO; + + [UIApplication.sharedApplication + endBackgroundTask:backgroundTaskIdentifier]; + }); + } +#endif // HAS_BACKGROUND_TASK_API + + if (outError) + *outError = [error autorelease]; + if (outResponse) + *outResponse = [response autorelease]; + return [result autorelease]; +#else // USE_NSURLSESSION + return [NSURLConnection sendSynchronousRequest:req + returningResponse:outResponse + error:outError]; +#endif // USE_NSURLSESSION +} + +@implementation HTTPRequest + +//============================================================================= +- (id)initWithURL:(NSURL*)URL { + if ((self = [super init])) { + URL_ = [URL copy]; + } + + return self; +} + +//============================================================================= +- (void)dealloc { + [URL_ release]; + [response_ release]; + + [super dealloc]; +} + +//============================================================================= +- (NSURL*)URL { + return URL_; +} + +//============================================================================= +- (NSHTTPURLResponse*)response { + return response_; +} + +//============================================================================= +- (NSString*)HTTPMethod { + @throw [NSException + exceptionWithName:NSInternalInconsistencyException + reason:[NSString stringWithFormat:@"You must" + "override %@ in a subclass", + NSStringFromSelector(_cmd)] + userInfo:nil]; +} + +//============================================================================= +- (NSString*)contentType { + return nil; +} + +//============================================================================= +- (NSData*)bodyData { + return nil; +} + +//============================================================================= +- (NSData*)send:(NSError**)withError { + NSMutableURLRequest* req = [[NSMutableURLRequest alloc] + initWithURL:URL_ + cachePolicy:NSURLRequestUseProtocolCachePolicy + timeoutInterval:60.0]; + + NSString* contentType = [self contentType]; + if ([contentType length] > 0) { + [req setValue:contentType forHTTPHeaderField:@"Content-type"]; + } + + NSData* bodyData = [self bodyData]; + if ([bodyData length] > 0) { + [req setHTTPBody:bodyData]; + } + + [req setHTTPMethod:[self HTTPMethod]]; + + [response_ release]; + response_ = nil; + + NSData* data = nil; + if ([[req URL] isFileURL]) { + [[req HTTPBody] writeToURL:[req URL] options:0 error:withError]; + } else { + NSURLResponse* response = nil; + data = SendSynchronousNSURLRequest(req, &response, withError); + response_ = (NSHTTPURLResponse*)[response retain]; + } + [req release]; + + return data; +} + +//============================================================================= ++ (NSData*)formDataForFileContents:(NSData*)contents withName:(NSString*)name { + NSMutableData* data = [NSMutableData data]; + NSString* escaped = PercentEncodeNSString(name); + NSString* fmt = @"Content-Disposition: form-data; name=\"%@\"; " + "filename=\"minidump.dmp\"\r\nContent-Type: " + "application/octet-stream\r\n\r\n"; + NSString* pre = [NSString stringWithFormat:fmt, escaped]; + + [data appendData:[pre dataUsingEncoding:NSUTF8StringEncoding]]; + [data appendData:contents]; + + return data; +} + +//============================================================================= ++ (NSData*)formDataForFile:(NSString*)file withName:(NSString*)name { + NSData* contents = [NSData dataWithContentsOfFile:file]; + + return [HTTPRequest formDataForFileContents:contents withName:name]; +} + +//============================================================================= ++ (NSData*)formDataForKey:(NSString*)key value:(NSString*)value { + NSString* escaped = PercentEncodeNSString(key); + NSString* fmt = @"Content-Disposition: form-data; name=\"%@\"\r\n\r\n%@\r\n"; + NSString* form = [NSString stringWithFormat:fmt, escaped, value]; + + return [form dataUsingEncoding:NSUTF8StringEncoding]; +} + +//============================================================================= ++ (void)appendFileToBodyData:(NSMutableData*)data + withName:(NSString*)name + withFileOrData:(id)fileOrData { + 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 withName:name]; + else + fileData = [HTTPRequest formDataForFile:fileOrData withName:name]; + + [data appendData:fileData]; +} + +@end diff --git a/shared/sentry/external/breakpad/src/common/mac/HTTPSimplePostRequest.h b/shared/sentry/external/breakpad/src/common/mac/HTTPSimplePostRequest.h new file mode 100644 index 000000000..bfabfead6 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/mac/HTTPSimplePostRequest.h @@ -0,0 +1,57 @@ +// Copyright (c) 2020, 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 + +#import "HTTPRequest.h" + +NS_ASSUME_NONNULL_BEGIN + +/** + Represents a simple (non-multipart) HTTP POST request. + */ +@interface HTTPSimplePostRequest : HTTPRequest { + @protected + NSString* contentType_; + NSString* body_; +} + +/** + Sets the content type of the POST request. + */ +- (void)setContentType:(NSString*)contentType; + +/** + Sets the contents of the POST request's body. + */ +- (void)setBody:(NSString*)body; + +@end + +NS_ASSUME_NONNULL_END diff --git a/shared/sentry/external/breakpad/src/common/mac/HTTPSimplePostRequest.m b/shared/sentry/external/breakpad/src/common/mac/HTTPSimplePostRequest.m new file mode 100644 index 000000000..4cb3ef159 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/mac/HTTPSimplePostRequest.m @@ -0,0 +1,69 @@ +// Copyright (c) 2020, 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 "HTTPSimplePostRequest.h" + +@implementation HTTPSimplePostRequest + +//============================================================================= +- (void)dealloc { + [contentType_ release]; + [body_ release]; + + [super dealloc]; +} + +//============================================================================= +- (void)setContentType:(NSString*)contentType { + contentType_ = [contentType copy]; +} + +//============================================================================= +- (void)setBody:(NSString*)body { + body_ = [body copy]; +} + +//============================================================================= +- (NSString*)HTTPMethod { + return @"POST"; +} + +//============================================================================= +- (NSString*)contentType { + return contentType_; +} + +//============================================================================= +- (NSData*)bodyData { + NSMutableData* data = [NSMutableData data]; + [data appendData:[body_ dataUsingEncoding:NSUTF8StringEncoding]]; + return data; +} + +@end diff --git a/shared/sentry/external/breakpad/src/common/mac/MachIPC.h b/shared/sentry/external/breakpad/src/common/mac/MachIPC.h new file mode 100644 index 000000000..a3fae5a14 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/mac/MachIPC.h @@ -0,0 +1,301 @@ +// 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 +#import +#import +#import + +#import + +//============================================================================== +// 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 { +#define PRINT_MACH_RESULT(result_, message_) \ + printf(message_" %s (%d)\n", mach_error_string(result_), result_ ); + +//============================================================================== +// 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/shared/sentry/external/breakpad/src/common/mac/MachIPC.mm b/shared/sentry/external/breakpad/src/common/mac/MachIPC.mm new file mode 100644 index 000000000..b41a825d4 --- /dev/null +++ b/shared/sentry/external/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 +#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(size); + + return head.msgh_size; +} + +//============================================================================== +MachMessage::MessageDataPacket* MachMessage::GetDataPacket() { + size_t desc_size = sizeof(MachMsgPortDescriptor)*GetDescriptorCount(); + MessageDataPacket* packet = + reinterpret_cast(padding + desc_size); + + return packet; +} + +//============================================================================== +void MachMessage::SetDescriptor(int n, + const MachMsgPortDescriptor& desc) { + MachMsgPortDescriptor* desc_array = + reinterpret_cast(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(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(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(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(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/shared/sentry/external/breakpad/src/common/mac/SymbolCollectorClient.h b/shared/sentry/external/breakpad/src/common/mac/SymbolCollectorClient.h new file mode 100644 index 000000000..8848cca6a --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/mac/SymbolCollectorClient.h @@ -0,0 +1,104 @@ +// Copyright (c) 2020, 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 + +NS_ASSUME_NONNULL_BEGIN + +/** + Represents a response from a sym-upload-v2 server to a createUploadURLOnServer + call. + */ +@interface UploadURLResponse : NSObject { + @protected + NSString* uploadURL_; + NSString* uploadKey_; +} + +- (id)initWithUploadURL:(NSString*)uploadURL withUploadKey:(NSString*)uploadKey; + +- (NSString*)uploadURL; +- (NSString*)uploadKey; +@end + +/** + Possible return statuses from a sym-upload-v2 server to a + completeUploadOnServer call. + */ +typedef NS_ENUM(NSInteger, CompleteUploadResult) { + CompleteUploadResultOk, + CompleteUploadResultDuplicateData, + CompleteUploadResultError +}; + +/** + Possible return statuses from a sym-upload-v2 server to a + checkSymbolStatusOnServer call. + */ +typedef NS_ENUM(NSInteger, SymbolStatus) { + SymbolStatusFound, + SymbolStatusMissing, + SymbolStatusUnknown +}; + +/** + Interface to help a client interact with a sym-upload-v2 server, over HTTP. + For details of the API and protocol, see :/docs/sym_upload_v2_protocol.md. + */ +@interface SymbolCollectorClient : NSObject +; + +/** + Calls the /v1/symbols/{debug_file}/{debug_id}:checkStatus API on the server. + */ ++ (SymbolStatus)checkSymbolStatusOnServer:(NSString*)APIURL + withAPIKey:(NSString*)APIKey + withDebugFile:(NSString*)debugFile + withDebugID:(NSString*)debugID; + +/** + Calls the /v1/uploads:create API on the server. + */ ++ (UploadURLResponse*)createUploadURLOnServer:(NSString*)APIURL + withAPIKey:(NSString*)APIKey; + +/** + Calls the /v1/uploads/{key}:complete API on the server. + */ ++ (CompleteUploadResult)completeUploadOnServer:(NSString*)APIURL + withAPIKey:(NSString*)APIKey + withUploadKey:(NSString*)uploadKey + withDebugFile:(NSString*)debugFile + withDebugID:(NSString*)debugID + withType:(NSString*)type + withProductName:(NSString*)productName; + +@end + +NS_ASSUME_NONNULL_END diff --git a/shared/sentry/external/breakpad/src/common/mac/SymbolCollectorClient.m b/shared/sentry/external/breakpad/src/common/mac/SymbolCollectorClient.m new file mode 100644 index 000000000..5465056dd --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/mac/SymbolCollectorClient.m @@ -0,0 +1,272 @@ +// Copyright (c) 2020, 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 "SymbolCollectorClient.h" + +#import "HTTPGetRequest.h" +#import "HTTPSimplePostRequest.h" + +@implementation UploadURLResponse + +//============================================================================= +- (id)initWithUploadURL:(NSString*)uploadURL + withUploadKey:(NSString*)uploadKey { + if (self = [super init]) { + uploadURL_ = [uploadURL copy]; + uploadKey_ = [uploadKey copy]; + } + return self; +} + +//============================================================================= +- (void)dealloc { + [uploadURL_ release]; + [uploadKey_ release]; + + [super dealloc]; +} + +//============================================================================= +- (NSString*)uploadURL { + return uploadURL_; +} + +//============================================================================= +- (NSString*)uploadKey { + return uploadKey_; +} +@end + +@implementation SymbolCollectorClient + +//============================================================================= ++ (SymbolStatus)checkSymbolStatusOnServer:(NSString*)APIURL + withAPIKey:(NSString*)APIKey + withDebugFile:(NSString*)debugFile + withDebugID:(NSString*)debugID { + // Note that forward-slash is listed as a character to escape here, for + // completeness, however it is illegal in a debugFile input. + NSMutableCharacterSet* allowedDebugFileCharacters = [NSMutableCharacterSet + characterSetWithCharactersInString:@" \"\\/#%:?@|^`{}<>[]&=;"]; + [allowedDebugFileCharacters + formUnionWithCharacterSet:[NSCharacterSet controlCharacterSet]]; + [allowedDebugFileCharacters invert]; + NSString* escapedDebugFile = + [debugFile stringByAddingPercentEncodingWithAllowedCharacters: + allowedDebugFileCharacters]; + + NSURL* URL = [NSURL + URLWithString:[NSString + stringWithFormat:@"%@/v1/symbols/%@/%@:checkStatus" + @"?key=%@", + APIURL, escapedDebugFile, debugID, + APIKey]]; + + HTTPGetRequest* getRequest = [[HTTPGetRequest alloc] initWithURL:URL]; + NSError* error = nil; + NSData* data = [getRequest send:&error]; + NSString* result = [[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding]; + int responseCode = [[getRequest response] statusCode]; + [getRequest release]; + + if (error || responseCode != 200) { + fprintf(stdout, "Failed to check symbol status.\n"); + fprintf(stdout, "Response code: %d\n", responseCode); + fprintf(stdout, "Response:\n"); + fprintf(stdout, "%s\n", [result UTF8String]); + return SymbolStatusUnknown; + } + + error = nil; + NSRegularExpression* statusRegex = [NSRegularExpression + regularExpressionWithPattern:@"\"status\": \"([^\"]+)\"" + options:0 + error:&error]; + NSArray* matches = + [statusRegex matchesInString:result + options:0 + range:NSMakeRange(0, [result length])]; + if ([matches count] != 1) { + fprintf(stdout, "Failed to parse check symbol status response."); + fprintf(stdout, "Response:\n"); + fprintf(stdout, "%s\n", [result UTF8String]); + return SymbolStatusUnknown; + } + + NSString* status = [result substringWithRange:[matches[0] rangeAtIndex:1]]; + [result release]; + + return [status isEqualToString:@"FOUND"] ? SymbolStatusFound + : SymbolStatusMissing; +} + +//============================================================================= ++ (UploadURLResponse*)createUploadURLOnServer:(NSString*)APIURL + withAPIKey:(NSString*)APIKey { + NSURL* URL = [NSURL + URLWithString:[NSString stringWithFormat:@"%@/v1/uploads:create?key=%@", + APIURL, APIKey]]; + + HTTPSimplePostRequest* postRequest = + [[HTTPSimplePostRequest alloc] initWithURL:URL]; + NSError* error = nil; + NSData* data = [postRequest send:&error]; + NSString* result = [[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding]; + int responseCode = [[postRequest response] statusCode]; + [postRequest release]; + + if (error || responseCode != 200) { + fprintf(stdout, "Failed to create upload URL.\n"); + fprintf(stdout, "Response code: %d\n", responseCode); + fprintf(stdout, "Response:\n"); + fprintf(stdout, "%s\n", [result UTF8String]); + return nil; + } + + // Note camel-case rather than underscores. + NSRegularExpression* uploadURLRegex = [NSRegularExpression + regularExpressionWithPattern:@"\"uploadUrl\": \"([^\"]+)\"" + options:0 + error:&error]; + NSRegularExpression* uploadKeyRegex = [NSRegularExpression + regularExpressionWithPattern:@"\"uploadKey\": \"([^\"]+)\"" + options:0 + error:&error]; + + NSArray* uploadURLMatches = + [uploadURLRegex matchesInString:result + options:0 + range:NSMakeRange(0, [result length])]; + NSArray* uploadKeyMatches = + [uploadKeyRegex matchesInString:result + options:0 + range:NSMakeRange(0, [result length])]; + if ([uploadURLMatches count] != 1 || [uploadKeyMatches count] != 1) { + fprintf(stdout, "Failed to parse create url response."); + fprintf(stdout, "Response:\n"); + fprintf(stdout, "%s\n", [result UTF8String]); + return nil; + } + NSString* uploadURL = + [result substringWithRange:[uploadURLMatches[0] rangeAtIndex:1]]; + NSString* uploadKey = + [result substringWithRange:[uploadKeyMatches[0] rangeAtIndex:1]]; + + return [[UploadURLResponse alloc] initWithUploadURL:uploadURL + withUploadKey:uploadKey]; +} + +//============================================================================= ++ (CompleteUploadResult)completeUploadOnServer:(NSString*)APIURL + withAPIKey:(NSString*)APIKey + withUploadKey:(NSString*)uploadKey + withDebugFile:(NSString*)debugFile + withDebugID:(NSString*)debugID + withType:(NSString*)type + withProductName:(NSString*)productName { + NSURL* URL = [NSURL + URLWithString:[NSString + stringWithFormat:@"%@/v1/uploads/%@:complete?key=%@", + APIURL, uploadKey, APIKey]]; + + NSMutableDictionary* jsonDictionary = [@{ + @"symbol_id" : @{@"debug_file" : debugFile, @"debug_id" : debugID}, + @"symbol_upload_type" : type, @"use_async_processing" : @"true" + } mutableCopy]; + + if (productName != nil) { + jsonDictionary[@"metadata"] = @{@"product_name": productName}; + } + + NSError* error = nil; + NSData* jsonData = + [NSJSONSerialization dataWithJSONObject:jsonDictionary + options:NSJSONWritingPrettyPrinted + error:&error]; + if (jsonData == nil) { + fprintf(stdout, "Error: %s\n", [[error localizedDescription] UTF8String]); + fprintf(stdout, + "Failed to complete upload. Could not write JSON payload.\n"); + return CompleteUploadResultError; + } + + NSString* body = [[NSString alloc] initWithData:jsonData + encoding:NSUTF8StringEncoding]; + HTTPSimplePostRequest* postRequest = + [[HTTPSimplePostRequest alloc] initWithURL:URL]; + [postRequest setBody:body]; + [postRequest setContentType:@"application/json"]; + + NSData* data = [postRequest send:&error]; + if (data == nil) { + fprintf(stdout, "Error: %s\n", [[error localizedDescription] UTF8String]); + fprintf(stdout, "Failed to complete upload URL.\n"); + return CompleteUploadResultError; + } + + NSString* result = [[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding]; + int responseCode = [[postRequest response] statusCode]; + [postRequest release]; + if (responseCode != 200) { + fprintf(stdout, "Failed to complete upload URL.\n"); + fprintf(stdout, "Response code: %d\n", responseCode); + fprintf(stdout, "Response:\n"); + fprintf(stdout, "%s\n", [result UTF8String]); + return CompleteUploadResultError; + } + + // Note camel-case rather than underscores. + NSRegularExpression* completeResultRegex = [NSRegularExpression + regularExpressionWithPattern:@"\"result\": \"([^\"]+)\"" + options:0 + error:&error]; + + NSArray* completeResultMatches = + [completeResultRegex matchesInString:result + options:0 + range:NSMakeRange(0, [result length])]; + + if ([completeResultMatches count] != 1) { + fprintf(stdout, "Failed to parse complete upload response."); + fprintf(stdout, "Response:\n"); + fprintf(stdout, "%s\n", [result UTF8String]); + return CompleteUploadResultError; + } + NSString* completeResult = + [result substringWithRange:[completeResultMatches[0] rangeAtIndex:1]]; + [result release]; + + return ([completeResult isEqualToString:@"DUPLICATE_DATA"]) + ? CompleteUploadResultDuplicateData + : CompleteUploadResultOk; +} +@end diff --git a/shared/sentry/external/breakpad/src/common/mac/arch_utilities.cc b/shared/sentry/external/breakpad/src/common/mac/arch_utilities.cc new file mode 100644 index 000000000..6d06e6bd0 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/mac/arch_utilities.cc @@ -0,0 +1,269 @@ +// 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 +#include +#include +#include + +#ifndef CPU_SUBTYPE_ARM_V7S +#define CPU_SUBTYPE_ARM_V7S (static_cast(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(0)) +#endif // CPU_SUBTYPE_ARM64_ALL + +#ifndef CPU_SUBTYPE_ARM64_E +#define CPU_SUBTYPE_ARM64_E (static_cast(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 + +// TODO(crbug.com/1242776): The "#ifndef __APPLE__" should be here, but the +// system version of NXGetLocalArchInfo returns incorrect information on +// x86_64 machines (treating them as just x86), so use the Breakpad version +// all the time for now. +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]; +} + +#ifndef __APPLE__ + +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/shared/sentry/external/breakpad/src/common/mac/arch_utilities.h b/shared/sentry/external/breakpad/src/common/mac/arch_utilities.h new file mode 100644 index 000000000..397c1f587 --- /dev/null +++ b/shared/sentry/external/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 + +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/shared/sentry/external/breakpad/src/common/mac/bootstrap_compat.cc b/shared/sentry/external/breakpad/src/common/mac/bootstrap_compat.cc new file mode 100644 index 000000000..d875d95b5 --- /dev/null +++ b/shared/sentry/external/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/shared/sentry/external/breakpad/src/common/mac/bootstrap_compat.h b/shared/sentry/external/breakpad/src/common/mac/bootstrap_compat.h new file mode 100644 index 000000000..8ca7357c3 --- /dev/null +++ b/shared/sentry/external/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 + +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/shared/sentry/external/breakpad/src/common/mac/byteswap.h b/shared/sentry/external/breakpad/src/common/mac/byteswap.h new file mode 100644 index 000000000..b7bbc0b95 --- /dev/null +++ b/shared/sentry/external/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 + +// byteswap.h: Overloaded functions for conveniently byteswapping values. + +#ifndef COMMON_MAC_BYTESWAP_H_ +#define COMMON_MAC_BYTESWAP_H_ + +#ifdef __APPLE__ +#include + +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 +#include +#include +#include_next + +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/shared/sentry/external/breakpad/src/common/mac/dump_syms.cc b/shared/sentry/external/breakpad/src/common/mac/dump_syms.cc new file mode 100644 index 000000000..b1cb1a300 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/mac/dump_syms.cc @@ -0,0 +1,699 @@ +// -*- 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 + +// dump_syms.cc: Create a symbol file for use with minidumps + +#include "common/mac/dump_syms.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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(12)) +#endif // CPU_TYPE_ARM + +#ifndef CPU_TYPE_ARM64 +#define CPU_TYPE_ARM64 (static_cast(16777228)) +#endif // CPU_TYPE_ARM64 + +using google_breakpad::ByteReader; +using google_breakpad::DwarfCUToModule; +using google_breakpad::DwarfLineToModule; +using google_breakpad::DwarfRangeListHandler; +using google_breakpad::mach_o::FatReader; +using google_breakpad::mach_o::FileID; +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 with absolute paths to all the entries +// in directory (excluding . and ..). +vector list_directory(const string& directory) { + vector 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; + } + + from_disk_ = true; + + // Does this filename refer to a dSYM bundle? + string contents_path = filename + "/Contents/Resources/DWARF"; + string object_filename; + 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 entries = list_directory(contents_path); + if (entries.size() == 0) { + fprintf(stderr, "Unable to find DWARF-bearing file in bundle: %s\n", + filename.c_str()); + return false; + } + if (entries.size() > 1) { + fprintf(stderr, "Too many DWARF files in bundle: %s\n", + filename.c_str()); + return false; + } + + object_filename = entries[0]; + } else { + object_filename = filename; + } + + // Read the file's contents into memory. + bool read_ok = true; + string error; + scoped_array contents; + off_t total = 0; + 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]); + 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; + } + return ReadData(contents.release(), total, object_filename); +} + +bool DumpSymbols::ReadData(uint8_t* contents, size_t size, + const std::string& filename) { + contents_.reset(contents); + size_ = size; + object_filename_ = filename; + + // 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_.get(), 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 fat_arch_vector; + for (vector::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(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::iterator it = object_files_.begin(); + it != object_files_.end(); + ++it) { + if (static_cast(it->cputype) == cpu_type && + static_cast(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() { + scoped_ptr file_id; + + if (from_disk_) { + file_id.reset(new FileID(object_filename_.c_str())); + } else { + file_id.reset(new FileID(contents_.get(), size_)); + } + unsigned char identifier_bytes[16]; + scoped_ptr module; + if (!selected_object_file_) { + if (!CreateEmptyModule(module)) + return string(); + } + cpu_type_t cpu_type = selected_object_file_->cputype; + cpu_subtype_t cpu_subtype = selected_object_file_->cpusubtype; + 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); + + // The pdb for these IDs has an extra byte, so to make everything uniform put + // a 0 on the end of mac IDs. + compacted += "0"; + + return compacted; +} + +// A range handler that accepts rangelist data parsed by +// RangeListReader and populates a range vector (typically +// owned by a function) with the results. +class DumpSymbols::DumperRangesHandler: + public DwarfCUToModule::RangesHandler { + public: + DumperRangesHandler(ByteReader* reader) : + reader_(reader) { } + + bool ReadRanges( + enum DwarfForm form, uint64_t data, + RangeListReader::CURangesInfo* cu_info, + vector* ranges) { + DwarfRangeListHandler handler(ranges); + RangeListReader range_list_reader(reader_, cu_info, + &handler); + return range_list_reader.ReadRanges(form, data); + } + + private: + ByteReader* reader_; +}; + +// A line-to-module loader that accepts line number info parsed by +// 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(ByteReader* byte_reader) + : byte_reader_(byte_reader) { } + + void StartCompilationUnit(const string& compilation_dir) { + compilation_dir_ = compilation_dir; + } + + void ReadProgram(const uint8_t* program, + uint64_t length, + const uint8_t* string_section, + uint64_t string_section_length, + const uint8_t* line_string_section, + uint64_t line_string_section_length, + Module* module, + vector* lines, + std::map* files) { + DwarfLineToModule handler(module, compilation_dir_, lines, files); + LineInfo parser(program, length, byte_reader_, nullptr, 0, + nullptr, 0, &handler); + parser.Start(); + } + private: + string compilation_dir_; + ByteReader* byte_reader_; // WEAK +}; + +bool DumpSymbols::CreateEmptyModule(scoped_ptr& 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; + + // 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() + ? ENDIANNESS_BIG + : ENDIANNESS_LITTLE); + + // Construct a context for this file. + DwarfCUToModule::FileContext file_context(selected_object_name_, + module, + handle_inter_cu_refs); + + // Build a 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. + 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& 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); + + // .debug_ranges and .debug_rngslists reader + DumperRangesHandler ranges_handler(&byte_reader); + + // Walk the __debug_info section, one compilation unit at a time. + uint64_t debug_info_length = debug_info_section.second; + for (uint64_t 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, &reporter, + symbol_data_ & INLINES); + // Make a Dwarf2Handler that drives our DIEHandler. + DIEDispatcher die_dispatcher(&root_handler); + // Make a DWARF parser for the compilation unit at OFFSET. + 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& section, + bool eh_frame) const { + // Find the appropriate set of register names for this file's + // architecture. + vector 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); + ByteReader byte_reader(macho_reader.big_endian() ? + ENDIANNESS_BIG : + 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); + + CallFrameInfo::Reporter dwarf_reporter(selected_object_name_, + section.section_name); + 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_ & 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_ & SYMBOLS_AND_FILES) || (symbol_data_ & INLINES)) { + dumper_.ReadDwarf(module_, reader_, section_map, handle_inter_cu_refs_); + } + if (symbol_data_ & 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; + 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; + if (!CreateEmptyModule(module)) + return false; + + return module->Write(stream, symbol_data_); +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/common/mac/dump_syms.h b/shared/sentry/external/breakpad/src/common/mac/dump_syms.h new file mode 100644 index 000000000..34ba14bc0 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/mac/dump_syms.h @@ -0,0 +1,207 @@ +// -*- 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 + +// 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 +#include +#include + +#include +#include +#include + +#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), + object_filename_(), + contents_(), + size_(0), + from_disk_(false), + object_files_(), + selected_object_file_(), + selected_object_name_() {} + ~DumpSymbols() { + } + + // Prepare to read debugging information from |filename|. |filename| may be + // the name of a fat file, 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); + + // Prepare to read debugging information from |contents|. |contents| is + // expected to be the data obtained from reading a fat file, or a Mach-O file. + // |filename| is used to determine the object filename in the generated + // output; there will not be an attempt to open this file as the data + // is already expected to be in memory. On success, return true; if there is a + // problem reading |contents|, report it and return false. + bool ReadData(uint8_t* contents, size_t size, 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); + + // Return an identifier string for the file this DumpSymbols is dumping. + std::string Identifier(); + + 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); + + // Creates an empty module object. + bool CreateEmptyModule(scoped_ptr& 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& section, + 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 this DumpSymbols will actually read debugging + // information from. If the filename passed to Read 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 contents_; + + // The size of contents_. + size_t size_; + + // Indicates which entry point to DumpSymbols was used, i.e. Read vs ReadData. + // This is used to indicate that downstream code paths can/should also read + // from disk or not. + bool from_disk_; + + // 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 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/shared/sentry/external/breakpad/src/common/mac/encoding_util.h b/shared/sentry/external/breakpad/src/common/mac/encoding_util.h new file mode 100644 index 000000000..6495a7427 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/mac/encoding_util.h @@ -0,0 +1,41 @@ +// Copyright (c) 2020, 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_MAC_ENCODING_UTIL_H +#define GOOGLE_BREAKPAD_COMMON_MAC_ENCODING_UTIL_H + +#import + +// 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. +NSString* PercentEncodeNSString(NSString* key); + +#endif // GOOGLE_BREAKPAD_COMMON_MAC_ENCODING_UTIL_H diff --git a/shared/sentry/external/breakpad/src/common/mac/encoding_util.m b/shared/sentry/external/breakpad/src/common/mac/encoding_util.m new file mode 100644 index 000000000..86d70fb6e --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/mac/encoding_util.m @@ -0,0 +1,47 @@ +// Copyright (c) 2020, 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 "encoding_util.h" + +#include +#include +#import + +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 +} diff --git a/shared/sentry/external/breakpad/src/common/mac/file_id.cc b/shared/sentry/external/breakpad/src/common/mac/file_id.cc new file mode 100644 index 000000000..9ed65e5c8 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/mac/file_id.cc @@ -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. + +// file_id.cc: Return a unique identifier for a file +// +// See file_id.h for documentation +// +// Author: Dan Waylonis + +#include "common/mac/file_id.h" + +#include +#include +#include + +#include "common/mac/macho_id.h" +#include "common/scoped_ptr.h" + +using MacFileUtilities::MachoID; + +namespace google_breakpad { +namespace mach_o { +// Constructs a FileID given a path to a file +FileID::FileID(const char* path) : memory_(nullptr), size_(0) { + snprintf(path_, sizeof(path_), "%s", path); +} + +// Constructs a FileID given the contents of a file and its size +FileID::FileID(void* memory, size_t size) + : path_(), memory_(memory), size_(size) {} + +bool FileID::MachoIdentifier(cpu_type_t cpu_type, + cpu_subtype_t cpu_subtype, + unsigned char identifier[16]) { + scoped_ptr macho; + if (memory_) { + macho.reset(new MachoID(memory_, size_)); + } else { + macho.reset(new MachoID(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((hi >= 10) ? ('A' + hi - 10) : ('0' + hi)); + buffer[buffer_idx++] = + static_cast((lo >= 10) ? ('A' + lo - 10) : ('0' + lo)); + } + + // NULL terminate + buffer[(buffer_idx < buffer_length) ? buffer_idx : buffer_idx - 1] = 0; +} + +} // namespace mach_o +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/common/mac/file_id.h b/shared/sentry/external/breakpad/src/common/mac/file_id.h new file mode 100644 index 000000000..fc1821e7f --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/mac/file_id.h @@ -0,0 +1,92 @@ +// 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 +#include +#include + +namespace google_breakpad { +namespace mach_o { + +class FileID { + public: + // Constructs a FileID given a path to a file + FileID(const char* path); + + // Constructs a FileID given the contents of a file and its size. + FileID(void* memory, size_t size); + ~FileID() {} + + // 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]; + + // Storage for contents of a file if this instance is used to operate on in + // memory file data rather than directly from a filesystem. If memory_ is + // null, the file represented by path_ will be opened/read. If memory_ is + // non-null, it is assumed to contain valid data, and no file operations will + // occur. + void* memory_; + + // Size of memory_ + size_t size_; +}; + +} // namespace mach_o +} // namespace google_breakpad + +#endif // COMMON_MAC_FILE_ID_H__ diff --git a/shared/sentry/external/breakpad/src/common/mac/launch_reporter.cc b/shared/sentry/external/breakpad/src/common/mac/launch_reporter.cc new file mode 100644 index 000000000..245be8265 --- /dev/null +++ b/shared/sentry/external/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 +#include +#include + +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/shared/sentry/external/breakpad/src/common/mac/launch_reporter.h b/shared/sentry/external/breakpad/src/common/mac/launch_reporter.h new file mode 100644 index 000000000..4531123c2 --- /dev/null +++ b/shared/sentry/external/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/shared/sentry/external/breakpad/src/common/mac/macho_id.cc b/shared/sentry/external/breakpad/src/common/mac/macho_id.cc new file mode 100644 index 000000000..9b7ca24f3 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/mac/macho_id.cc @@ -0,0 +1,237 @@ +// 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 +#include +#include +#include + +#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), md5_context_(), update_function_(NULL) { + snprintf(path_, sizeof(path_), "%s", path); +} + +MachoID::MachoID(void* memory, size_t size) + : path_(), + memory_(memory), + memory_size_(size), + md5_context_(), + update_function_(NULL) {} + +MachoID::~MachoID() {} + +void MachoID::UpdateMD5(unsigned char* bytes, size_t size) { + MD5Update(&md5_context_, bytes, static_cast(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::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; +} +} // namespace MacFileUtilities diff --git a/shared/sentry/external/breakpad/src/common/mac/macho_id.h b/shared/sentry/external/breakpad/src/common/mac/macho_id.h new file mode 100644 index 000000000..0262697d4 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/mac/macho_id.h @@ -0,0 +1,107 @@ +// 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 +#include +#include + +#include "common/mac/macho_walker.h" +#include "common/md5.h" + +namespace MacFileUtilities { + +class MachoID { + public: + MachoID(const char* path); + MachoID(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 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 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); + + // File path + char path_[PATH_MAX]; + + // Memory region to read from + void* memory_; + + // Size of the memory region + size_t memory_size_; + + // 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/shared/sentry/external/breakpad/src/common/mac/macho_reader.cc b/shared/sentry/external/breakpad/src/common/mac/macho_reader.cc new file mode 100644 index 000000000..e89e0906f --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/mac/macho_reader.cc @@ -0,0 +1,567 @@ +// 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 + +// 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 +#include +#include + +#include + +// 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(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& section, + 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::max()) { + const uint64_t section_offset_recomputed = + segment.fileoff + section.address - segment.vmaddr; + if (offset == static_cast(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)) { + if (offset > 0) { + reporter_->MisplacedSectionData(section.section_name, + section.segment_name); + return false; + } else { + // Mach-O files in .dSYM bundles have the contents of the loaded + // segments partially removed. The removed sections will have zero as + // their offset. MisplacedSectionData should not be called in this + // case. + section.contents.start = section.contents.end = NULL; + } + } else { + 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& section) { + (*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/shared/sentry/external/breakpad/src/common/mac/macho_reader.h b/shared/sentry/external/breakpad/src/common/mac/macho_reader.h new file mode 100644 index 000000000..02762c55c --- /dev/null +++ b/shared/sentry/external/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 + +// 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 +#include +#include +#include +#include + +#include +#include +#include + +#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 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 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& section, + 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& section) = 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/shared/sentry/external/breakpad/src/common/mac/macho_reader_unittest.cc b/shared/sentry/external/breakpad/src/common/mac/macho_reader_unittest.cc new file mode 100644 index 000000000..fb2c479cc --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/mac/macho_reader_unittest.cc @@ -0,0 +1,1947 @@ +// 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 + +// macho_reader_unittest.cc: Unit tests for google_breakpad::Mach_O::FatReader +// and google_breakpad::Mach_O::Reader. + +#include +#include +#include + +#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& section, + 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& section)); +}; + + +// 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(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 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& section_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(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(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(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 MatchSection( + Matcher bits_64, + Matcher section_name, + Matcher segment_name, + Matcher address, + Matcher alignment, + Matcher flags, + Matcher 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 MatchSection( + Matcher bits_64, + Matcher section_name, + Matcher segment_name, + Matcher 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(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(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 with 0 as is start address. + LoadedSection empty; + empty.Append(10, '4'); + empty.start() = 0; + empty.address() = segment.address() + 1; + empty.final_size() = empty.Size(); + + SegmentLoadCommand command; + command.Header("segment", segment, 0x173baa29, 0x8407275d, 0xed8f7057) + .AppendSectionEntry("empty", "segment", 0, 0x8b53ae5c, empty); + + 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("empty", "segment")).Times(0); + + EXPECT_CALL(section_handler, + HandleSection(MatchSection(true, "empty", "segment", + empty.address().Value()))) + .WillOnce(Return(true)); + + EXPECT_TRUE(reader.WalkSegmentSections(actual_segment, §ion_handler)); +} + +TEST_F(LoadCommand, MisplacedSectionButSectionIsEmpty) { + 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/shared/sentry/external/breakpad/src/common/mac/macho_utilities.cc b/shared/sentry/external/breakpad/src/common/mac/macho_utilities.cc new file mode 100644 index 000000000..f56fe768c --- /dev/null +++ b/shared/sentry/external/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 +#include + +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/shared/sentry/external/breakpad/src/common/mac/macho_utilities.h b/shared/sentry/external/breakpad/src/common/mac/macho_utilities.h new file mode 100644 index 000000000..00563a77c --- /dev/null +++ b/shared/sentry/external/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 +#include + +/* 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/shared/sentry/external/breakpad/src/common/mac/macho_walker.cc b/shared/sentry/external/breakpad/src/common/mac/macho_walker.cc new file mode 100644 index 000000000..a42128b80 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/mac/macho_walker.cc @@ -0,0 +1,268 @@ +// 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 +#include +#include +#include +#include +#include +#include + +#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(offset) >= memory_size_) + return false; + size = memory_size_ - static_cast(offset); + result = false; + } + memcpy(buffer, static_cast(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); + + 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); + + 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/shared/sentry/external/breakpad/src/common/mac/macho_walker.h b/shared/sentry/external/breakpad/src/common/mac/macho_walker.h new file mode 100644 index 000000000..168f30e64 --- /dev/null +++ b/shared/sentry/external/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 +#include +#include + +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/shared/sentry/external/breakpad/src/common/mac/minidump_upload.m b/shared/sentry/external/breakpad/src/common/mac/minidump_upload.m new file mode 100644 index 000000000..1fe4f213f --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/mac/minidump_upload.m @@ -0,0 +1,135 @@ +// 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. + +// minidump_upload.m: Upload a minidump to a HTTP server. The upload is sent as +// a multipart/form-data POST request with the following parameters: +// prod: the product name +// ver: the product version +// symbol_file: the breakpad format symbol file + +#import + +#import + +#import "common/mac/HTTPMultipartUpload.h" + +typedef struct { + NSString* minidumpPath; + NSString* uploadURLStr; + NSString* product; + NSString* version; + BOOL success; +} Options; + +//============================================================================= +static void Start(Options* options) { + NSURL* url = [NSURL URLWithString:options->uploadURLStr]; + HTTPMultipartUpload* ul = [[HTTPMultipartUpload alloc] initWithURL:url]; + NSMutableDictionary* parameters = [NSMutableDictionary dictionary]; + + // Add parameters + [parameters setObject:options->product forKey:@"prod"]; + [parameters setObject:options->version forKey:@"ver"]; + [ul setParameters:parameters]; + + // Add file + [ul addFileAtPath:options->minidumpPath name:@"upload_file_minidump"]; + + // Send it + NSError* error = nil; + NSData* data = [ul send:&error]; + NSString* result = [[NSString alloc] initWithData:data + encoding:NSUTF8StringEncoding]; + + NSLog(@"Send: %@", error ? [error description] : @"No Error"); + NSLog(@"Response: %ld", (long)[[ul response] statusCode]); + NSLog(@"Result: %lu bytes\n%@", (unsigned long)[data length], result); + + [result release]; + [ul release]; + options->success = !error; +} + +//============================================================================= +static void Usage(int argc, const char* argv[]) { + fprintf(stderr, "Submit minidump information.\n"); + fprintf(stderr, + "Usage: %s -p -v " + "\n", + argv[0]); + fprintf(stderr, " should be a minidump.\n"); + fprintf(stderr, " is the destination for the upload\n"); + + fprintf(stderr, "\t-h: Usage\n"); + fprintf(stderr, "\t-?: Usage\n"); +} + +//============================================================================= +static void SetupOptions(int argc, const char* argv[], Options* options) { + extern int optind; + char ch; + + while ((ch = getopt(argc, (char* const*)argv, "p:v:h?")) != -1) { + switch (ch) { + case 'p': + options->product = [NSString stringWithUTF8String:optarg]; + break; + case 'v': + options->version = [NSString stringWithUTF8String:optarg]; + break; + + default: + Usage(argc, argv); + exit(0); + break; + } + } + + if ((argc - optind) != 2) { + fprintf(stderr, "%s: Missing symbols file and/or upload-URL\n", argv[0]); + Usage(argc, argv); + exit(1); + } + + options->minidumpPath = [NSString stringWithUTF8String:argv[optind]]; + options->uploadURLStr = [NSString stringWithUTF8String:argv[optind + 1]]; +} + +//============================================================================= +int main(int argc, const char* argv[]) { + NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; + Options options; + + bzero(&options, sizeof(Options)); + SetupOptions(argc, argv, &options); + Start(&options); + + [pool release]; + return options.success ? 0 : 1; +} diff --git a/shared/sentry/external/breakpad/src/common/mac/scoped_task_suspend-inl.h b/shared/sentry/external/breakpad/src/common/mac/scoped_task_suspend-inl.h new file mode 100644 index 000000000..d6d1bef97 --- /dev/null +++ b/shared/sentry/external/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 + +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/shared/sentry/external/breakpad/src/common/mac/string_utilities.cc b/shared/sentry/external/breakpad/src/common/mac/string_utilities.cc new file mode 100644 index 000000000..cb1554037 --- /dev/null +++ b/shared/sentry/external/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 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/shared/sentry/external/breakpad/src/common/mac/string_utilities.h b/shared/sentry/external/breakpad/src/common/mac/string_utilities.h new file mode 100644 index 000000000..e87304c18 --- /dev/null +++ b/shared/sentry/external/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 + +#include + +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/shared/sentry/external/breakpad/src/common/mac/super_fat_arch.h b/shared/sentry/external/breakpad/src/common/mac/super_fat_arch.h new file mode 100644 index 000000000..d7fa018ae --- /dev/null +++ b/shared/sentry/external/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 + +// 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 +#include +#include + +// 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::max()) + return false; + if (size > std::numeric_limits::max()) + return false; + if (align > std::numeric_limits::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/shared/sentry/external/breakpad/src/common/mac/testing/GTMSenTestCase.h b/shared/sentry/external/breakpad/src/common/mac/testing/GTMSenTestCase.h new file mode 100644 index 000000000..ce3d9022c --- /dev/null +++ b/shared/sentry/external/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 +#else +#import +#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 { + @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/shared/sentry/external/breakpad/src/common/mac/testing/GTMSenTestCase.m b/shared/sentry/external/breakpad/src/common/mac/testing/GTMSenTestCase.m new file mode 100644 index 000000000..162f01e97 --- /dev/null +++ b/shared/sentry/external/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 +#if GTM_IPHONE_SIMULATOR +#import +#endif + +#import "GTMObjC2Runtime.h" +#import "GTMUnitTestDevLog.h" + +#if GTM_IPHONE_SDK && !GTM_IPHONE_USE_SENTEST +#import + +@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/shared/sentry/external/breakpad/src/common/macros.h b/shared/sentry/external/breakpad/src/common/macros.h new file mode 100644 index 000000000..14bb3f7bd --- /dev/null +++ b/shared/sentry/external/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/shared/sentry/external/breakpad/src/common/md5.cc b/shared/sentry/external/breakpad/src/common/md5.cc new file mode 100644 index 000000000..b6e710da9 --- /dev/null +++ b/shared/sentry/external/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 + +#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<>(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/shared/sentry/external/breakpad/src/common/md5.h b/shared/sentry/external/breakpad/src/common/md5.h new file mode 100644 index 000000000..1b478feaf --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/md5.h @@ -0,0 +1,28 @@ +// Copyright 2007 Google Inc. All Rights Reserved. +// Author: liuli@google.com (Liu Li) +#ifndef COMMON_MD5_H__ +#define COMMON_MD5_H__ + +#include +#include + +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/shared/sentry/external/breakpad/src/common/memory_allocator.h b/shared/sentry/external/breakpad/src/common/memory_allocator.h new file mode 100644 index 000000000..d28fbfc36 --- /dev/null +++ b/shared/sentry/external/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 +#include +#include +#include + +#include +#include + +#if defined(MEMORY_SANITIZER) +#include +#endif + +#ifdef __APPLE__ +#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(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(a); + header->next = last_; + header->num_pages = num_pages; + last_ = header; + + pages_allocated_ += num_pages; + + return reinterpret_cast(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 +struct PageStdAllocator { + using AllocatorTraits = std::allocator_traits>; + 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 PageStdAllocator(const PageStdAllocator& 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(allocator_.Alloc(size)); + } + + inline void deallocate(pointer, size_type) { + // The PageAllocator doesn't free. + } + + template struct rebind { + typedef PageStdAllocator 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 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 wasteful_vector : public std::vector > { + public: + wasteful_vector(PageAllocator* allocator, unsigned size_hint = 16) + : std::vector >(PageStdAllocator(*allocator)) { + std::vector >::reserve(size_hint); + } + protected: + wasteful_vector(PageStdAllocator allocator) + : std::vector >(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 auto_wasteful_vector : public wasteful_vector { + T stackdata_[N]; + public: + auto_wasteful_vector(PageAllocator* allocator) + : wasteful_vector( + PageStdAllocator(*allocator, + &stackdata_[0], + sizeof(stackdata_))) { + std::vector >::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/shared/sentry/external/breakpad/src/common/memory_allocator_unittest.cc b/shared/sentry/external/breakpad/src/common/memory_allocator_unittest.cc new file mode 100644 index 000000000..5803b90d5 --- /dev/null +++ b/shared/sentry/external/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(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(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(allocator.Alloc(i)); + ASSERT_FALSE(p == NULL); + memset(p, 0, i); + } +} + +namespace { +typedef testing::Test WastefulVectorTest; +} + +TEST(WastefulVectorTest, Setup) { + PageAllocator allocator_; + wasteful_vector v(&allocator_); + ASSERT_TRUE(v.empty()); + ASSERT_EQ(v.size(), 0u); +} + +TEST(WastefulVectorTest, Simple) { + PageAllocator allocator_; + EXPECT_EQ(0U, allocator_.pages_allocated()); + wasteful_vector 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 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 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/shared/sentry/external/breakpad/src/common/memory_range.h b/shared/sentry/external/breakpad/src/common/memory_range.h new file mode 100644 index 000000000..41dd2da62 --- /dev/null +++ b/shared/sentry/external/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 + +#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(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 + const DataType* GetData(size_t sub_offset) const { + return reinterpret_cast( + 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 + const ElementType* GetArrayElement(size_t element_offset, + unsigned element_index) const { + return reinterpret_cast( + 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/shared/sentry/external/breakpad/src/common/memory_range_unittest.cc b/shared/sentry/external/breakpad/src/common/memory_range_unittest.cc new file mode 100644 index 000000000..f6cf8c8b2 --- /dev/null +++ b/shared/sentry/external/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(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(-1) }, + { false, kBufferSize + 1, 0 }, + { false, static_cast(-1), 2 }, + { false, 1, kBufferSize }, + { false, kBufferSize - 1, 2 }, + { false, 0, static_cast(-1) }, + { false, 1, static_cast(-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(0); + EXPECT_EQ(reinterpret_cast(kBufferPointer), char_pointer); + const int* int_pointer = range.GetData(0); + EXPECT_EQ(reinterpret_cast(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(0, 0); + EXPECT_EQ(reinterpret_cast(kBufferPointer), char_pointer); + const int* int_pointer = range.GetArrayElement(0, 0); + EXPECT_EQ(reinterpret_cast(kBufferPointer), int_pointer); +} diff --git a/shared/sentry/external/breakpad/src/common/minidump_type_helper.h b/shared/sentry/external/breakpad/src/common/minidump_type_helper.h new file mode 100644 index 000000000..5a7d5a6a8 --- /dev/null +++ b/shared/sentry/external/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 + +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +template +struct MDTypeHelper; + +template <> +struct MDTypeHelper { + typedef MDRawDebug32 MDRawDebug; + typedef MDRawLinkMap32 MDRawLinkMap; +}; + +template <> +struct MDTypeHelper { + typedef MDRawDebug64 MDRawDebug; + typedef MDRawLinkMap64 MDRawLinkMap; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_COMMON_MINIDUMP_TYPE_HELPER_H_ diff --git a/shared/sentry/external/breakpad/src/common/module.cc b/shared/sentry/external/breakpad/src/common/module.cc new file mode 100644 index 000000000..13c229eb4 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/module.cc @@ -0,0 +1,464 @@ +// 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 + +// module.cc: Implement google_breakpad::Module. See module.h. + +#include "common/module.h" +#include "common/string_view.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace google_breakpad { + +using std::dec; +using std::hex; +using std::unique_ptr; + +Module::InlineOrigin* Module::InlineOriginMap::GetOrCreateInlineOrigin( + uint64_t offset, + StringView name) { + uint64_t specification_offset = references_[offset]; + // Find the root offset. + auto iter = references_.find(specification_offset); + while (iter != references_.end() && + specification_offset != references_[specification_offset]) { + specification_offset = references_[specification_offset]; + iter = references_.find(specification_offset); + } + if (inline_origins_.find(specification_offset) != inline_origins_.end()) { + if (inline_origins_[specification_offset]->name == "") { + inline_origins_[specification_offset]->name = name; + } + return inline_origins_[specification_offset]; + } + inline_origins_[specification_offset] = new Module::InlineOrigin(name); + return inline_origins_[specification_offset]; +} + +void Module::InlineOriginMap::SetReference(uint64_t offset, + uint64_t specification_offset) { + // If we haven't seen this doesn't exist in reference map, always add it. + if (references_.find(offset) == references_.end()) { + references_[offset] = specification_offset; + return; + } + // If offset equals specification_offset and offset exists in + // references_, there is no need to update the references_ map. + // This early return is necessary because the call to erase in following if + // will remove the entry of specification_offset in inline_origins_. If + // specification_offset equals to references_[offset], it might be + // duplicate debug info. + if (offset == specification_offset || + specification_offset == references_[offset]) + return; + + // Fix up mapping in inline_origins_. + auto remove = inline_origins_.find(references_[offset]); + if (remove != inline_origins_.end()) { + inline_origins_[specification_offset] = std::move(remove->second); + inline_origins_.erase(remove); + } + references_[offset] = specification_offset; +} + +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 (vector::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& ranges) { + address_ranges_ = ranges; +} + +bool 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 false; + } + + // 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 ret = functions_.insert(function); + if (!ret.second && (*ret.first != function)) { + // Free the duplicate that was not inserted because this Module + // now owns it. + return false; + } + return true; +} + +void Module::AddStackFrameEntry(StackFrameEntry* stack_frame_entry) { + if (!AddressIsInModule(stack_frame_entry->address)) { + return; + } + + stack_frame_entries_.push_back(stack_frame_entry); +} + +void Module::AddExtern(Extern* ext) { + if (!AddressIsInModule(ext->address)) { + return; + } + + std::pair 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* vec, + vector::iterator i) { + vec->insert(i, functions_.begin(), functions_.end()); +} + +void Module::GetExterns(vector* vec, + vector::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* vec) { + vec->clear(); + for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); ++it) + vec->push_back(it->second); +} + +void Module::GetStackFrameEntries(vector* vec) const { + *vec = stack_frame_entries_; +} + +void Module::AssignSourceIds( + set& inline_origins) { + // 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::iterator line_it = func->lines.begin(); + line_it != func->lines.end(); ++line_it) + line_it->file->source_id = 0; + } + + // Also mark all files cited by inline callsite by setting each one's source + // id to zero. + auto markInlineFiles = [](unique_ptr& in) { + // There are some artificial inline functions which don't belong to + // any file. Those will have file id -1. + if (in->call_site_file) { + in->call_site_file->source_id = 0; + } + }; + for (auto func : functions_) { + Inline::InlineDFS(func->inlines, markInlineFiles); + } + + // 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++; + } +} + +void Module::CreateInlineOrigins( + set& inline_origins) { + // Only add origins that have file and deduplicate origins with same name and + // file id by doing a DFS. + auto addInlineOrigins = [&](unique_ptr& in) { + auto it = inline_origins.find(in->origin); + if (it == inline_origins.end()) + inline_origins.insert(in->origin); + else + in->origin = *it; + }; + for (Function* func : functions_) + Module::Inline::InlineDFS(func->inlines, addInlineOrigins); + int next_id = 0; + for (InlineOrigin* origin : inline_origins) { + origin->id = next_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 & SYMBOLS_AND_FILES) { + // Get all referenced inline origins. + set inline_origins; + CreateInlineOrigins(inline_origins); + AssignSourceIds(inline_origins); + + // 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 inline origins. + for (InlineOrigin* origin : inline_origins) { + stream << "INLINE_ORIGIN " << origin->id << " " << origin->name << "\n"; + if (!stream.good()) + return ReportError(); + } + + // Write out functions and their inlines and lines. + for (FunctionSet::const_iterator func_it = functions_.begin(); + func_it != functions_.end(); ++func_it) { + Function* func = *func_it; + vector::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(); + + // Write out inlines. + auto write_inline = [&](unique_ptr& in) { + stream << "INLINE "; + stream << in->inline_nest_level << " " << in->call_site_line << " " + << in->getCallSiteFileID() << " " << in->origin->id << hex; + for (const Range& r : in->ranges) + stream << " " << (r.address - load_address_) << " " << r.size; + stream << dec << "\n"; + }; + Module::Inline::InlineDFS(func->inlines, write_inline); + 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 & CFI) { + // Write out 'STACK CFI INIT' and 'STACK CFI' records. + vector::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/shared/sentry/external/breakpad/src/common/module.h b/shared/sentry/external/breakpad/src/common/module.h new file mode 100644 index 000000000..01ecfa8aa --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/module.h @@ -0,0 +1,479 @@ +// -*- 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 + +// 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 +#include +#include +#include +#include +#include +#include +#include + +#include "common/string_view.h" +#include "common/symbol_data.h" +#include "common/unordered.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; + static constexpr uint64_t kMaxAddress = std::numeric_limits
    ::max(); + struct File; + struct Function; + struct InlineOrigin; + struct Inline; + 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(StringView 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. + StringView name; + + // The start address and the address ranges covered by the function. + const Address address; + vector ranges; + + // The function's parameter size. + Address parameter_size; + + // Source lines belonging to this function, sorted by increasing + // address. + vector lines; + + // Inlined call sites belonging to this functions. + vector> inlines; + }; + + struct InlineOrigin { + explicit InlineOrigin(StringView name) : id(-1), name(name) {} + + // A unique id for each InlineOrigin object. INLINE records use the id to + // refer to its INLINE_ORIGIN record. + int id; + + // The inlined function's name. + StringView name; + + File* file; + + int getFileID() const { return file ? file->source_id : -1; } + }; + + // A inlined call site. + struct Inline { + Inline(InlineOrigin* origin, + const vector& ranges, + int call_site_line, + int call_site_file_id, + int inline_nest_level, + vector> child_inlines) + : origin(origin), + ranges(ranges), + call_site_line(call_site_line), + call_site_file_id(call_site_file_id), + call_site_file(nullptr), + inline_nest_level(inline_nest_level), + child_inlines(std::move(child_inlines)) {} + + InlineOrigin* origin; + + // The list of addresses and sizes. + vector ranges; + + int call_site_line; + + // The id is only meanful inside a CU. It's only used for looking up real + // File* after scanning a CU. + int call_site_file_id; + + File* call_site_file; + + int inline_nest_level; + + // A list of inlines which are children of this inline. + vector> child_inlines; + + int getCallSiteFileID() const { + return call_site_file ? call_site_file->source_id : -1; + } + + static void InlineDFS( + vector>& inlines, + std::function&)> const& forEach) { + for (std::unique_ptr& in : inlines) { + forEach(in); + InlineDFS(in->child_inlines, forEach); + } + } + }; + + typedef map InlineOriginByOffset; + + class InlineOriginMap { + public: + // Add INLINE ORIGIN to the module. Return a pointer to origin . + InlineOrigin* GetOrCreateInlineOrigin(uint64_t offset, StringView name); + + // offset is the offset of a DW_TAG_subprogram. specification_offset is the + // value of its DW_AT_specification or equals to offset if + // DW_AT_specification doesn't exist in that DIE. + void SetReference(uint64_t offset, uint64_t specification_offset); + + ~InlineOriginMap() { + for (const auto& iter : inline_origins_) { + delete iter.second; + } + } + + private: + // A map from a DW_TAG_subprogram's offset to the DW_TAG_subprogram. + InlineOriginByOffset inline_origins_; + + // A map from a DW_TAG_subprogram's offset to the offset of its + // specification or abstract origin subprogram. The set of values in this + // map should always be the same set of keys in inline_origins_. + map references_; + }; + + InlineOriginMap inline_origin_map; + + // 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 RuleMap; + + // A map from addresses to RuleMaps, representing changes that take + // effect at given addresses. + typedef map 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 InlineOriginCompare { + bool operator()(const InlineOrigin* lhs, const InlineOrigin* rhs) const { + return lhs->name < rhs->name; + } + }; + + struct ExternCompare { + bool operator() (const Extern* lhs, const Extern* 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& 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. + // Return false if the function is duplicate and needs to be freed. + bool AddFunction(Function* function); + + // 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* vec, vector::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* vec, vector::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* 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* vec) const; + + // 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(set& inline_origins); + + // This function should be called before AssignSourceIds() to get the set of + // valid InlineOrigins*. + void CreateInlineOrigins( + set& inline_origins); + + // 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 CFI then: + // - the source files added via FindFile, + // - the functions added via AddFunctions, each with its lines, + // - all public records, + // If symbol_data is 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); + + // Place the name in the global set of strings. Return a StringView points to + // a string inside the pool. + StringView AddStringToPool(const string& str) { + auto result = common_strings_.insert(str); + return *(result.first); + } + + 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 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 FileByNameMap; + + // A set containing Function structures, sorted by address. + typedef set FunctionSet; + + // A set containing Extern structures, sorted by address. + typedef set ExternSet; + + // 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. + vector 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_; + + unordered_set common_strings_; +}; + +} // namespace google_breakpad + +#endif // COMMON_LINUX_MODULE_H__ diff --git a/shared/sentry/external/breakpad/src/common/module_unittest.cc b/shared/sentry/external/breakpad/src/common/module_unittest.cc new file mode 100644 index 000000000..26964a560 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/module_unittest.cc @@ -0,0 +1,623 @@ +// 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 + +// module_unittest.cc: Unit tests for google_breakpad::Module. + +#include +#include +#include +#include + +#include +#include +#include + +#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); + + std::set inline_origins; + m.AssignSourceIds(inline_origins); + + vector 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, SYMBOLS_AND_FILES | INLINES); + 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, AddFunction) { + 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 vec; + vec.push_back(function1); + vec.push_back(function2); + + for (Module::Function* func: vec) + m.AddFunction(func); + + 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 ddb5f41285aa7757 1486493370dc5073 \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 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", + contents.c_str()); + + // Check that GetStackFrameEntries works. + vector entries; + m.GetStackFrameEntries(&entries); + ASSERT_EQ(3U, entries.size()); + // Check first entry. + EXPECT_EQ(0xddb5f41285aa7757ULL, entries[0]->address); + EXPECT_EQ(0x1486493370dc5073ULL, entries[0]->size); + ASSERT_EQ(0U, entries[0]->initial_rules.size()); + ASSERT_EQ(0U, entries[0]->rule_changes.size()); + // 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(0x5e8d0db0a7075c6cULL, entries[2]->address); + EXPECT_EQ(0x1c7edb12a7aea229ULL, entries[2]->size); + Module::RuleMap entry3_initial; + entry3_initial[".cfa"] = "Whose woods are these"; + EXPECT_THAT(entries[2]->initial_rules, ContainerEq(entry3_initial)); + Module::RuleChangeMap entry3_changes; + entry3_changes[0x36682fad3763ffffULL][".cfa"] = "I think I know"; + entry3_changes[0x36682fad3763ffffULL]["stromboli"] = "his house is in"; + entry3_changes[0x47ceb0f63c269d7fULL]["calzone"] = "the village though"; + entry3_changes[0x47ceb0f63c269d7fULL]["cannoli"] = + "he will not see me stopping here"; + EXPECT_THAT(entries[2]->rule_changes, ContainerEq(entry3_changes)); +} + +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 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()); + + // Cleanup - Prevent Memory Leak errors. + delete (extern1); + delete (function); + delete (entry3); + delete (entry1); +} diff --git a/shared/sentry/external/breakpad/src/common/path_helper.cc b/shared/sentry/external/breakpad/src/common/path_helper.cc new file mode 100644 index 000000000..61a6e3184 --- /dev/null +++ b/shared/sentry/external/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 +#include +#include +#include + +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/shared/sentry/external/breakpad/src/common/path_helper.h b/shared/sentry/external/breakpad/src/common/path_helper.h new file mode 100644 index 000000000..2166ba018 --- /dev/null +++ b/shared/sentry/external/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 + +#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/shared/sentry/external/breakpad/src/common/scoped_ptr.h b/shared/sentry/external/breakpad/src/common/scoped_ptr.h new file mode 100644 index 000000000..d137c1868 --- /dev/null +++ b/shared/sentry/external/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(new Foo("wee")); +// } // foo goes out of scope, releasing the pointer with it. +// +// { +// scoped_ptr 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(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 +#include +#include + +namespace google_breakpad { + +// A scoped_ptr is like a T*, except that the destructor of scoped_ptr +// automatically deletes the pointer it holds (if any). +// That is, scoped_ptr owns the T object that it points to. +// Like a T*, a scoped_ptr may hold either NULL or a pointer to a T object. +// Also like T*, scoped_ptr 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) == sizeof(C*) +template +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 bool operator==(scoped_ptr const& p2) const; + template bool operator!=(scoped_ptr const& p2) const; + + // Disallow evil constructors + scoped_ptr(const scoped_ptr&); + void operator=(const scoped_ptr&); +}; + +// Free functions +template +void swap(scoped_ptr& p1, scoped_ptr& p2) { + p1.swap(p2); +} + +template +bool operator==(C* p1, const scoped_ptr& p2) { + return p1 == p2.get(); +} + +template +bool operator!=(C* p1, const scoped_ptr& p2) { + return p1 != p2.get(); +} + +// scoped_array is like scoped_ptr, except that the caller must allocate +// with new [] and the destructor deletes objects with delete []. +// +// As with scoped_ptr, a scoped_array either points to an object +// or is NULL. A scoped_array owns the object that it points to. +// scoped_array is thread-compatible, and once you index into it, +// the returned objects have only the threadsafety guarantees of T. +// +// Size: sizeof(scoped_array) == sizeof(C*) +template +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 bool operator==(scoped_array const& p2) const; + template bool operator!=(scoped_array const& p2) const; + + // Disallow evil constructors + scoped_array(const scoped_array&); + void operator=(const scoped_array&); +}; + +// Free functions +template +void swap(scoped_array& p1, scoped_array& p2) { + p1.swap(p2); +} + +template +bool operator==(C* p1, const scoped_array& p2) { + return p1 == p2.get(); +} + +template +bool operator!=(C* p1, const scoped_array& 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 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 + bool operator==(scoped_ptr_malloc const& p) const; + template + bool operator!=(scoped_ptr_malloc const& p) const; + + // Disallow evil constructors + scoped_ptr_malloc(const scoped_ptr_malloc&); + void operator=(const scoped_ptr_malloc&); +}; + +template inline +void swap(scoped_ptr_malloc& a, scoped_ptr_malloc& b) { + a.swap(b); +} + +template inline +bool operator==(C* p, const scoped_ptr_malloc& b) { + return p == b.get(); +} + +template inline +bool operator!=(C* p, const scoped_ptr_malloc& b) { + return p != b.get(); +} + +} // namespace google_breakpad + +#endif // COMMON_SCOPED_PTR_H_ diff --git a/shared/sentry/external/breakpad/src/common/simple_string_dictionary.cc b/shared/sentry/external/breakpad/src/common/simple_string_dictionary.cc new file mode 100644 index 000000000..e0a74ceeb --- /dev/null +++ b/shared/sentry/external/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/shared/sentry/external/breakpad/src/common/simple_string_dictionary.h b/shared/sentry/external/breakpad/src/common/simple_string_dictionary.h new file mode 100644 index 000000000..948492053 --- /dev/null +++ b/shared/sentry/external/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 +#include + +#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 +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(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/shared/sentry/external/breakpad/src/common/simple_string_dictionary_unittest.cc b/shared/sentry/external/breakpad/src/common/simple_string_dictionary_unittest.cc new file mode 100644 index 000000000..e7b8fd763 --- /dev/null +++ b/shared/sentry/external/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 + int keyNumber; + sscanf(entry->key, "key%d", &keyNumber); + + // Extract valueNumber from a string of the form value + 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(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/shared/sentry/external/breakpad/src/common/solaris/dump_symbols.cc b/shared/sentry/external/breakpad/src/common/solaris/dump_symbols.cc new file mode 100644 index 000000000..93aeed243 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/solaris/dump_symbols.cc @@ -0,0 +1,680 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 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 func_info; +}; + +struct CompareString { + bool operator()(const char* s1, const char* s2) const { + return strcmp(s1, s2) < 0; + } +}; + +typedef std::map SymbolMap; + +// Information of a symbol table. +// This is the root of all types of symbol. +struct SymbolInfo { + std::vector 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* 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* 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(stabstr_section->sh_offset + base); + struct slist* lists = + reinterpret_cast(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
    + 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(obj_base); + + const GElf_Shdr* sections = + reinterpret_cast(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( + 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::elf::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/shared/sentry/external/breakpad/src/common/solaris/dump_symbols.h b/shared/sentry/external/breakpad/src/common/solaris/dump_symbols.h new file mode 100644 index 000000000..3bca8a235 --- /dev/null +++ b/shared/sentry/external/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 + +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/shared/sentry/external/breakpad/src/common/solaris/file_id.cc b/shared/sentry/external/breakpad/src/common/solaris/file_id.cc new file mode 100644 index 000000000..837793c86 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/solaris/file_id.cc @@ -0,0 +1,200 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#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; +} + +class AutoCloser { + public: + AutoCloser(int fd) : fd_(fd) {} + ~AutoCloser() { if (fd_) close(fd_); } + private: + int fd_; +}; + +namespace elf { + +FileID::FileID(const char* path) { + strcpy(path_, path); +} + +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; +} + +} // elf +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/common/solaris/file_id.h b/shared/sentry/external/breakpad/src/common/solaris/file_id.h new file mode 100644 index 000000000..cacf17a96 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/solaris/file_id.h @@ -0,0 +1,68 @@ +// 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 + +namespace google_breakpad { +namespace elf { + +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]; +}; + +} // elf +} // namespace google_breakpad + +#endif // COMMON_SOLARIS_FILE_ID_H__ diff --git a/shared/sentry/external/breakpad/src/common/solaris/guid_creator.cc b/shared/sentry/external/breakpad/src/common/solaris/guid_creator.cc new file mode 100644 index 000000000..17d773e72 --- /dev/null +++ b/shared/sentry/external/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 +#include + +#include +#include +#include + +#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(&guid->data4[0]) = random(); + *reinterpret_cast(&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(&(guid->data4[0])), + *reinterpret_cast(&(guid->data4[4]))); + if (num != kGUIDStringLength) + return false; + + buf[num] = '\0'; + return true; +} diff --git a/shared/sentry/external/breakpad/src/common/solaris/guid_creator.h b/shared/sentry/external/breakpad/src/common/solaris/guid_creator.h new file mode 100644 index 000000000..4aee3a1c2 --- /dev/null +++ b/shared/sentry/external/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/shared/sentry/external/breakpad/src/common/solaris/message_output.h b/shared/sentry/external/breakpad/src/common/solaris/message_output.h new file mode 100644 index 000000000..3e3b1d465 --- /dev/null +++ b/shared/sentry/external/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/shared/sentry/external/breakpad/src/common/stabs_reader.cc b/shared/sentry/external/breakpad/src/common/stabs_reader.cc new file mode 100644 index 000000000..43c404029 --- /dev/null +++ b/shared/sentry/external/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 + +// This file implements the google_breakpad::StabsReader class. +// See stabs_reader.h. + +#include "common/stabs_reader.h" + +#include +#include +#include + +#include + +#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(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::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/shared/sentry/external/breakpad/src/common/stabs_reader.h b/shared/sentry/external/breakpad/src/common/stabs_reader.h new file mode 100644 index 000000000..1e773f45d --- /dev/null +++ b/shared/sentry/external/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 + +// 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 +#include + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_MACH_O_NLIST_H +#include +#elif defined(HAVE_A_OUT_H) +#include +#endif + +#include +#include + +#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 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, 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/shared/sentry/external/breakpad/src/common/stabs_reader_unittest.cc b/shared/sentry/external/breakpad/src/common/stabs_reader_unittest.cc new file mode 100644 index 000000000..24f3e1a57 --- /dev/null +++ b/shared/sentry/external/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 + +// stabs_reader_unittest.cc: Unit tests for google_breakpad::StabsReader. + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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::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 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(stabs_contents.data()), + stabs_contents.size(), + reinterpret_cast(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/shared/sentry/external/breakpad/src/common/stabs_to_module.cc b/shared/sentry/external/breakpad/src/common/stabs_to_module.cc new file mode 100644 index 000000000..cbddd33db --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/stabs_to_module.cc @@ -0,0 +1,201 @@ +// 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 + +// dump_stabs.cc --- implement the StabsToModule class. + +#include +#include +#include +#include + +#include + +#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::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(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(address)); + return true; +} + +bool StabsToModule::StartFunction(const string& name, + uint64_t address) { + assert(!current_function_); + Module::Function* f = + new Module::Function(module_->AddStringToPool(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(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(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::const_iterator func_it = functions_.begin(); + func_it != functions_.end(); + func_it++) { + Module::Function *f = *func_it; + // Compute the function f's size. + vector::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::iterator last_line = f->lines.end() - 1; + for (vector::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. + for (Module::Function* func: functions_) + module_->AddFunction(func); + functions_.clear(); +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/common/stabs_to_module.h b/shared/sentry/external/breakpad/src/common/stabs_to_module.h new file mode 100644 index 000000000..6f6e0ed7f --- /dev/null +++ b/shared/sentry/external/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 + +// 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 + +#include +#include + +#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 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 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/shared/sentry/external/breakpad/src/common/stabs_to_module_unittest.cc b/shared/sentry/external/breakpad/src/common/stabs_to_module_unittest.cc new file mode 100644 index 000000000..d265f4dc4 --- /dev/null +++ b/shared/sentry/external/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 + +// dump_stabs_unittest.cc: Unit tests for StabsToModule. + +#include + +#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 functions; + m.GetFunctions(&functions, functions.end()); + ASSERT_EQ((size_t) 1, functions.size()); + Module::Function *function = functions[0]; + EXPECT_STREQ("function", function->name.str().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 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 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 functions; + m.GetFunctions(&functions, functions.end()); + ASSERT_EQ((size_t) 1, functions.size()); + + Module::Function *function = functions[0]; + EXPECT_STREQ("function", function->name.str().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 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 >::" + "push_back(unsigned long long const&)", + function->name.str().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/shared/sentry/external/breakpad/src/common/stdio_wrapper.h b/shared/sentry/external/breakpad/src/common/stdio_wrapper.h new file mode 100644 index 000000000..a3dd50aab --- /dev/null +++ b/shared/sentry/external/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 + +#if defined(_MSC_VER) && MSC_VER < 1900 +#include + +#define snprintf _snprintf +typedef SSIZE_T ssize_t; +#endif + + +#endif // GOOGLE_BREAKPAD_COMMON_STDIO_WRAPPER_H diff --git a/shared/sentry/external/breakpad/src/common/string_conversion.cc b/shared/sentry/external/breakpad/src/common/string_conversion.cc new file mode 100644 index 000000000..6a78ed7e4 --- /dev/null +++ b/shared/sentry/external/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 + +#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* out) { + size_t source_length = strlen(in); + const UTF8* source_ptr = reinterpret_cast(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(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(source_ptr - reinterpret_cast(in)); + + // Add another character to the input stream and try again + source_ptr = reinterpret_cast(in); + ++source_end_ptr; + + if (source_end_ptr > reinterpret_cast(in) + in_length) + break; + } + + return 0; +} + +void UTF32ToUTF16(const wchar_t* in, vector* out) { + size_t source_length = wcslen(in); + const UTF32* source_ptr = reinterpret_cast(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(&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(value << 8); +} + +string UTF16ToUTF8(const vector& in, bool swap) { + const UTF16* source_ptr = &in[0]; + scoped_array 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::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 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(target_buffer.get()); + return targetPtr; + } + + return ""; +} + +} // namespace google_breakpad diff --git a/shared/sentry/external/breakpad/src/common/string_conversion.h b/shared/sentry/external/breakpad/src/common/string_conversion.h new file mode 100644 index 000000000..02d1486aa --- /dev/null +++ b/shared/sentry/external/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 +#include + +#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* 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* 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& in, bool swap); + +} // namespace google_breakpad + +#endif // COMMON_STRING_CONVERSION_H__ diff --git a/shared/sentry/external/breakpad/src/common/string_conversion_unittest.cc b/shared/sentry/external/breakpad/src/common/string_conversion_unittest.cc new file mode 100644 index 000000000..e9f9b55d9 --- /dev/null +++ b/shared/sentry/external/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 +#include + +#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 out; + vector 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 in{'a', 0xdf, 'c', 0}; + EXPECT_EQ("aßc", UTF16ToUTF8(in, false)); +} diff --git a/shared/sentry/external/breakpad/src/common/string_view.h b/shared/sentry/external/breakpad/src/common/string_view.h new file mode 100644 index 000000000..aa01db8bc --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/string_view.h @@ -0,0 +1,114 @@ +// Copyright (c) 2021 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_STRING_VIEW_H__ +#define COMMON_STRING_VIEW_H__ + +#include +#include +#include +#include "common/using_std_string.h" + +namespace google_breakpad { + +// A StringView is a string reference to a string object, but not own the +// string object. It's a compatibile layer until we can use std::string_view in +// C++17. +class StringView { + private: + // The start of the string, in an external buffer. It doesn't have to be + // null-terminated. + const char* data_ = ""; + + size_t length_ = 0; + + public: + // Construct an empty StringView. + StringView() = default; + + // Disallow construct StringView from nullptr. + StringView(std::nullptr_t) = delete; + + // Construct a StringView from a cstring. + StringView(const char* str) : data_(str) { + assert(str); + length_ = strlen(str); + } + + // Construct a StringView from a cstring with fixed length. + StringView(const char* str, size_t length) : data_(str), length_(length) { + assert(str); + } + + // Construct a StringView from an std::string. + StringView(const string& str) : data_(str.data()), length_(str.length()) {} + + string str() const { return string(data_, length_); } + + const char* data() const { return data_; } + + bool empty() const { return length_ == 0; } + + size_t size() const { return length_; } + + int compare(StringView rhs) const { + size_t min_len = std::min(size(), rhs.size()); + int res = memcmp(data_, rhs.data(), min_len); + if (res != 0) + return res; + if (size() == rhs.size()) + return 0; + return size() < rhs.size() ? -1 : 1; + } +}; + +inline bool operator==(StringView lhs, StringView rhs) { + return lhs.compare(rhs) == 0; +} + +inline bool operator!=(StringView lhs, StringView rhs) { + return lhs.compare(rhs) != 0; +} + +inline bool operator<(StringView lhs, StringView rhs) { + return lhs.compare(rhs) < 0; +} + +inline bool operator>(StringView lhs, StringView rhs) { + return lhs.compare(rhs) > 0; +} + +inline std::ostream& operator<<(std::ostream& os, StringView s) { + os << s.str(); + return os; +} + +} // namespace google_breakpad + +#endif // COMMON_STRING_VIEW_H__ diff --git a/shared/sentry/external/breakpad/src/common/symbol_data.h b/shared/sentry/external/breakpad/src/common/symbol_data.h new file mode 100644 index 000000000..a790974bc --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/symbol_data.h @@ -0,0 +1,58 @@ +// -*- 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_ + +#include + +// Control what data is used from the symbol file. +enum SymbolData { + NO_DATA = 0, + SYMBOLS_AND_FILES = 1, + CFI = 1 << 1, + INLINES = 1 << 2, + ALL_SYMBOL_DATA = INLINES | CFI | SYMBOLS_AND_FILES +}; + +inline SymbolData operator&(SymbolData data1, SymbolData data2) { + return static_cast( + static_cast::type>(data1) & + static_cast::type>(data2)); +} + +inline SymbolData operator|(SymbolData data1, SymbolData data2) { + return static_cast( + static_cast::type>(data1) | + static_cast::type>(data2)); +} + +#endif // COMMON_SYMBOL_DATA_H_ diff --git a/shared/sentry/external/breakpad/src/common/test_assembler.cc b/shared/sentry/external/breakpad/src/common/test_assembler.cc new file mode 100644 index 000000000..8a48bfb0b --- /dev/null +++ b/shared/sentry/external/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 + +// test_assembler.cc: Implementation of google_breakpad::TestAssembler. +// See test_assembler.h for details. + +#include "common/test_assembler.h" + +#include +#include + +#include + +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 +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(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(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(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& section) { + size_t base = contents_.size(); + contents_.append(section.contents_); + for (vector::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/shared/sentry/external/breakpad/src/common/test_assembler.h b/shared/sentry/external/breakpad/src/common/test_assembler.h new file mode 100644 index 000000000..125ef4503 --- /dev/null +++ b/shared/sentry/external/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 + +// 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 +#include +#include + +#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 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(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& section); + + // 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 : + // is either 'L' (little-endian, least significant byte first), + // 'B' (big-endian, most significant byte first), or + // 'D' (default, the section's default endianness) + // is 8, 16, 32, or 64. + // + // Since endianness doesn't matter for a single byte, all the + // =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 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/shared/sentry/external/breakpad/src/common/test_assembler_unittest.cc b/shared/sentry/external/breakpad/src/common/test_assembler_unittest.cc new file mode 100644 index 000000000..bda25ebfc --- /dev/null +++ b/shared/sentry/external/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 + +// test_assembler_unittest.cc: Unit tests for google_breakpad::TestAssembler. + +#include +#include + +#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/shared/sentry/external/breakpad/src/common/testdata/func-line-pairing.h b/shared/sentry/external/breakpad/src/common/testdata/func-line-pairing.h new file mode 100644 index 000000000..05538f961 --- /dev/null +++ b/shared/sentry/external/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/shared/sentry/external/breakpad/src/common/tests/auto_tempdir.h b/shared/sentry/external/breakpad/src/common/tests/auto_tempdir.h new file mode 100644 index 000000000..1df88db8b --- /dev/null +++ b/shared/sentry/external/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 +#include + +#include + +#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/shared/sentry/external/breakpad/src/common/tests/file_utils.cc b/shared/sentry/external/breakpad/src/common/tests/file_utils.cc new file mode 100644 index 000000000..c1cbb39cc --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/tests/file_utils.cc @@ -0,0 +1,157 @@ +// 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 +#include +#include +#include +#include + +#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 CopyFile(const std::string& from_path, const std::string& to_path) { + return CopyFile(from_path.c_str(), to_path.c_str()); +} + +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(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/shared/sentry/external/breakpad/src/common/tests/file_utils.h b/shared/sentry/external/breakpad/src/common/tests/file_utils.h new file mode 100644 index 000000000..3d1a9c6fa --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/tests/file_utils.h @@ -0,0 +1,55 @@ +// 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_ + +#include + +namespace google_breakpad { + +// Copies a file from |from_path| to |to_path|. Returns true on success. +bool CopyFile(const std::string& from_path, const std::string& to_path); +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/shared/sentry/external/breakpad/src/common/unordered.h b/shared/sentry/external/breakpad/src/common/unordered.h new file mode 100644 index 000000000..0be7f48f6 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/unordered.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. + +// 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 +#include + +// For hash. +#include "util/hash/hash.h" + +template > +struct unordered_map : public __gnu_cxx::hash_map {}; +template > +struct unordered_set : public __gnu_cxx::hash_set {}; + +#else +#include +#include +using std::unordered_map; +using std::unordered_set; +#endif + +#endif // COMMON_UNORDERED_H_ diff --git a/shared/sentry/external/breakpad/src/common/using_std_string.h b/shared/sentry/external/breakpad/src/common/using_std_string.h new file mode 100644 index 000000000..f0e1aed90 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/using_std_string.h @@ -0,0 +1,66 @@ +// -*- 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 +#include + 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/shared/sentry/external/breakpad/src/common/windows/common_windows.gyp b/shared/sentry/external/breakpad/src/common/windows/common_windows.gyp new file mode 100644 index 000000000..5f7594b16 --- /dev/null +++ b/shared/sentry/external/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/shared/sentry/external/breakpad/src/common/windows/dia_util.cc b/shared/sentry/external/breakpad/src/common/windows/dia_util.cc new file mode 100644 index 000000000..ed8cb5b65 --- /dev/null +++ b/shared/sentry/external/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 + +namespace google_breakpad { + +bool FindDebugStream(const wchar_t* name, + IDiaSession* session, + IDiaEnumDebugStreamData** debug_stream) { + CComPtr enum_debug_streams; + if (FAILED(session->getEnumDebugStreams(&enum_debug_streams))) { + fprintf(stderr, "IDiaSession::getEnumDebugStreams failed\n"); + return false; + } + + CComPtr 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 enum_tables; + if (FAILED(session->getEnumTables(&enum_tables))) { + fprintf(stderr, "IDiaSession::getEnumTables failed\n"); + return false; + } + + // Iterate through the tables. + CComPtr 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/shared/sentry/external/breakpad/src/common/windows/dia_util.h b/shared/sentry/external/breakpad/src/common/windows/dia_util.h new file mode 100644 index 000000000..b9e0df2d5 --- /dev/null +++ b/shared/sentry/external/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 +#include + +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 +bool FindTable(IDiaSession* session, InterfaceType** table) { + return FindTable(__uuidof(InterfaceType), + session, + reinterpret_cast(table)); +} + +} // namespace google_breakpad + +#endif // COMMON_WINDOWS_DIA_UTIL_H_ diff --git a/shared/sentry/external/breakpad/src/common/windows/guid_string.cc b/shared/sentry/external/breakpad/src/common/windows/guid_string.cc new file mode 100644 index 000000000..b7f877e66 --- /dev/null +++ b/shared/sentry/external/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 + +#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/shared/sentry/external/breakpad/src/common/windows/guid_string.h b/shared/sentry/external/breakpad/src/common/windows/guid_string.h new file mode 100644 index 000000000..48a5c1d37 --- /dev/null +++ b/shared/sentry/external/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 + +#include + +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/shared/sentry/external/breakpad/src/common/windows/http_upload.cc b/shared/sentry/external/breakpad/src/common/windows/http_upload.cc new file mode 100644 index 000000000..af6f8d9ce --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/windows/http_upload.cc @@ -0,0 +1,510 @@ +// 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 + +// Disable exception handler warnings. +#pragma warning(disable:4530) + +#include +#include + +#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* 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::size_type + std::vector::size_type vector_size = + static_cast::size_type>(length); + if (static_cast(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; + } + + bool CheckParameters(const map& parameters) { + for (map::const_iterator pos = parameters.begin(); + pos != parameters.end(); ++pos) { + const wstring& str = pos->first; + if (str.size() == 0) { + return false; // disallow empty parameter names + } + for (unsigned int i = 0; i < str.size(); ++i) { + wchar_t c = str[i]; + if (c < 32 || c == '"' || c > 127) { + return false; + } + } + } + return true; + } + + // 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(&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 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(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 + 0)); // 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, + 0)); // context + if (!request.get()) { + return false; + } + + if (!content_type_header.empty()) { + HttpAddRequestHeaders(request.get(), + content_type_header.c_str(), + static_cast(-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(request_body.data()), + static_cast(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(&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 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 map& parameters, + const map& files, + const wstring& boundary, + string *request_body) { + string boundary_str = WideToUTF8(boundary); + if (boundary_str.empty()) { + return false; + } + + request_body->clear(); + + // Append each of the parameter pairs as a form-data part + for (map::const_iterator pos = parameters.begin(); + pos != parameters.end(); ++pos) { + request_body->append("--" + boundary_str + "\r\n"); + request_body->append("Content-Disposition: form-data; name=\"" + + WideToUTF8(pos->first) + "\"\r\n\r\n" + + WideToUTF8(pos->second) + "\r\n"); + } + + // Now append each upload file as a binary (octet-stream) part + for (map::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 map& parameters, + const map& files, + int* timeout_ms, + wstring* response_body, + int* response_code) { + // TODO(bryner): support non-ASCII parameter names + if (!CheckParameters(parameters)) { + return false; + } + + 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/shared/sentry/external/breakpad/src/common/windows/http_upload.h b/shared/sentry/external/breakpad/src/common/windows/http_upload.h new file mode 100644 index 000000000..c7d8c6fec --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/windows/http_upload.h @@ -0,0 +1,126 @@ +// 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 +// a set of string parameters (key/value pairs), 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 +#include + +#include + +namespace google_breakpad { + +using std::string; +using std::wstring; +using std::map; + +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 . + // Parameter names must contain only printable ASCII characters, + // and may not contain a quote (") character. + // 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 map& parameters, + const map& 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/shared/sentry/external/breakpad/src/common/windows/module_info.h b/shared/sentry/external/breakpad/src/common/windows/module_info.h new file mode 100644 index 000000000..3dccc8088 --- /dev/null +++ b/shared/sentry/external/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 + +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/shared/sentry/external/breakpad/src/common/windows/omap.cc b/shared/sentry/external/breakpad/src/common/windows/omap.cc new file mode 100644 index 000000000..5a821b64f --- /dev/null +++ b/shared/sentry/external/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 + +#include +#include +#include + +#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 +const T& Min(const T& t1, const T& t2) { return t1 < t2 ? t1 : t2; } +template +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 OmapFromTable; +typedef std::vector 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 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(&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 enum_segments; + if (!FindTable(session, &enum_segments)) + return false; + assert(enum_segments.p != NULL); + + DWORD temp_image_length = 0; + CComPtr 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(omap_data.omap_to); + const OmapFromTable& orig2tran = reinterpret_cast( + 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 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::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* subsequent) { + assert(subsequent->empty()); + const OmapFromTable& orig2tran = + reinterpret_cast(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 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/shared/sentry/external/breakpad/src/common/windows/omap.h b/shared/sentry/external/breakpad/src/common/windows/omap.h new file mode 100644 index 000000000..bc293afb5 --- /dev/null +++ b/shared/sentry/external/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/shared/sentry/external/breakpad/src/common/windows/omap_internal.h b/shared/sentry/external/breakpad/src/common/windows/omap_internal.h new file mode 100644 index 000000000..2a4713d93 --- /dev/null +++ b/shared/sentry/external/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 +#include + +#include +#include + +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 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 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 Mapping; + +// Used as a secondary search structure accompanying a Mapping. +struct EndpointIndex { + DWORD endpoint; + size_t index; +}; +typedef std::vector 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 subsequent_rva_block; +}; + +} // namespace google_breakpad + +#endif // COMMON_WINDOWS_OMAP_INTERNAL_H_ diff --git a/shared/sentry/external/breakpad/src/common/windows/omap_unittest.cc b/shared/sentry/external/breakpad/src/common/windows/omap_unittest.cc new file mode 100644 index 000000000..7fe66bd40 --- /dev/null +++ b/shared/sentry/external/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/shared/sentry/external/breakpad/src/common/windows/pdb_source_line_writer.cc b/shared/sentry/external/breakpad/src/common/windows/pdb_source_line_writer.cc new file mode 100644 index 000000000..25a1ca053 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/windows/pdb_source_line_writer.cc @@ -0,0 +1,1509 @@ +// 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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::set; +using std::unique_ptr; +using std::vector; + +// The symbol (among possibly many) selected to represent an rva. +struct SelectedSymbol { + SelectedSymbol(const CComPtr& symbol, bool is_public) + : symbol(symbol), is_public(is_public), is_multiple(false) {} + + // The symbol to use for an rva. + CComPtr 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 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 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& 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(&data_source)))) { + return true; + } + + return false; +} + +const DWORD kUndecorateOptions = 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; + +#define arraysize(f) (sizeof(f) / sizeof(*f)) + +void StripLlvmSuffixAndUndecorate(BSTR* name) { + // LLVM sometimes puts a suffix on symbols to give them a globally unique + // name. The suffix is either some string preceded by a period (like in the + // Itanium ABI; also on Windows this is safe since periods are otherwise + // never part of mangled names), or a dollar sign followed by a 32-char hex + // string (this should go away in future LLVM versions). Strip such suffixes + // and try demangling again. + // + // + // Example symbol names with such suffixes: + // + // ?foo@@YAXXZ$5520c83448162c04f2b239db4b5a2c61 + // ?foo@@YAXXZ.llvm.13040715209719948753 + + if (**name != L'?') + return; // The name is already demangled. + + for (size_t i = 0, len = wcslen(*name); i < len; i++) { + wchar_t c = (*name)[i]; + + if (c == L'.' || (c == L'$' && len - i == 32 + 1)) { + (*name)[i] = L'\0'; + wchar_t undecorated[1024]; + DWORD res = UnDecorateSymbolNameW(*name, undecorated, + arraysize(undecorated), + kUndecorateOptions); + if (res == 0 || undecorated[0] == L'?') { + // Demangling failed; restore the symbol name and return. + (*name)[i] = c; + return; + } + + SysFreeString(*name); + *name = SysAllocString(undecorated); + return; + } + } +} + +} // namespace + +PDBSourceLineWriter::Inline::Inline(int inline_nest_level) + : inline_nest_level_(inline_nest_level) {} + +void PDBSourceLineWriter::Inline::SetOriginId(int origin_id) { + origin_id_ = origin_id; +} + +void PDBSourceLineWriter::Inline::ExtendRanges(const Line& line) { + if (ranges_.empty()) { + ranges_[line.rva] = line.length; + return; + } + auto iter = ranges_.lower_bound(line.rva); + // There is no overlap if this function is called with inlinee lines from + // the same callsite. + if (iter == ranges_.begin()) { + return; + } + if (line.rva + line.length == iter->first) { + // If they are connected, merge their ranges into one. + DWORD length = line.length + iter->second; + ranges_.erase(iter); + ranges_[line.rva] = length; + } else { + --iter; + if (iter->first + iter->second == line.rva) { + ranges_[iter->first] = iter->second + line.length; + } else { + ranges_[line.rva] = line.length; + } + } +} + +void PDBSourceLineWriter::Inline::SetCallSiteLine(DWORD call_site_line) { + call_site_line_ = call_site_line; +} + +void PDBSourceLineWriter::Inline::SetCallSiteFileId(DWORD call_site_file_id) { + call_site_file_id_ = call_site_file_id; +} + +void PDBSourceLineWriter::Inline::SetChildInlines( + vector> child_inlines) { + child_inlines_ = std::move(child_inlines); +} + +void PDBSourceLineWriter::Inline::Print(FILE* output) const { + // Ignore INLINE record that doesn't have any range. + if (ranges_.empty()) + return; + fprintf(output, "INLINE %d %lu %lu %d", inline_nest_level_, call_site_line_, + call_site_file_id_, origin_id_); + for (const auto& r : ranges_) { + fprintf(output, " %lx %lx", r.first, r.second); + } + fprintf(output, "\n"); + for (const unique_ptr& in : child_inlines_) { + in->Print(output); + } +} + +const PDBSourceLineWriter::Line* PDBSourceLineWriter::Lines::GetLine( + DWORD rva) const { + auto iter = line_map_.find(rva); + if (iter == line_map_.end()) { + // If not found exact rva, check if it's within any range. + iter = line_map_.lower_bound(rva); + if (iter == line_map_.begin()) + return nullptr; + --iter; + auto l = iter->second; + // This happens when there is no top level lines cover this rva (e.g. empty + // lines found for the function). Then we don't know the call site line + // number for this inlined function. + if (rva >= l.rva + l.length) + return nullptr; + } + return &iter->second; +} + +DWORD PDBSourceLineWriter::Lines::GetLineNum(DWORD rva) const { + const Line* line = GetLine(rva); + return line ? line->line_num : 0; +} + +DWORD PDBSourceLineWriter::Lines::GetFileId(DWORD rva) const { + const Line* line = GetLine(rva); + return line ? line->file_id : 0; +} + +void PDBSourceLineWriter::Lines::AddLine(const Line& line) { + if (line_map_.empty()) { + line_map_[line.rva] = line; + return; + } + + // Given an existing line in line_map_, remove it from line_map_ if it + // overlaps with the line and add a new line for the non-overlap range. Return + // true if there is an overlap. + auto intercept = [&](Line old_line) { + DWORD end = old_line.rva + old_line.length; + // No overlap. + if (old_line.rva >= line.rva + line.length || line.rva >= end) + return false; + // old_line is within the line. + if (old_line.rva >= line.rva && end <= line.rva + line.length) { + line_map_.erase(old_line.rva); + return true; + } + // Then there is a overlap. + if (old_line.rva < line.rva) { + old_line.length -= end - line.rva; + if (end > line.rva + line.length) { + Line new_line = old_line; + new_line.rva = line.rva + line.length; + new_line.length = end - new_line.rva; + line_map_[new_line.rva] = new_line; + } + } else { + line_map_.erase(old_line.rva); + old_line.length -= line.rva + line.length - old_line.rva; + old_line.rva = line.rva + line.length; + } + line_map_[old_line.rva] = old_line; + return true; + }; + + bool is_intercept; + // Use a loop in cases that there are multiple lines within the given line. + do { + auto iter = line_map_.lower_bound(line.rva); + if (iter == line_map_.end()) { + if (!line_map_.empty()) { + --iter; + intercept(iter->second); + } + break; + } + is_intercept = false; + if (iter != line_map_.begin()) { + // Check if the given line overlaps a line with smaller in the map. + auto prev = line_map_.lower_bound(line.rva); + --prev; + is_intercept = intercept(prev->second); + } + // Check if the given line overlaps a line with greater or equal rva in the + // map. Using operator |= here since it's possible that there are multiple + // lines with greater rva in the map overlap with the given line. + is_intercept |= intercept(iter->second); + } while (is_intercept); + line_map_[line.rva] = line; +} + +PDBSourceLineWriter::PDBSourceLineWriter(bool handle_inline) + : output_(NULL), handle_inline_(handle_inline) {} + +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 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::GetLine(IDiaLineNumber* dia_line, Line* line) const { + if (FAILED(dia_line->get_relativeVirtualAddress(&line->rva))) { + fprintf(stderr, "failed to get line rva\n"); + return false; + } + + if (FAILED(dia_line->get_length(&line->length))) { + fprintf(stderr, "failed to get line code length\n"); + return false; + } + + DWORD dia_source_id; + if (FAILED(dia_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 + line->file_id = GetRealFileID(dia_source_id); + + if (FAILED(dia_line->get_lineNumber(&line->line_num))) { + fprintf(stderr, "failed to get line number\n"); + return false; + } + return true; +} + +bool PDBSourceLineWriter::GetLines(IDiaEnumLineNumbers* lines, + Lines* line_list) const { + CComPtr line; + ULONG count; + + while (SUCCEEDED(lines->Next(1, &line, &count)) && count == 1) { + Line l; + if (!GetLine(line, &l)) + return false; + // Silently ignore zero-length lines. + if (l.length != 0) + line_list->AddLine(l); + line.Release(); + } + return true; +} + +void PDBSourceLineWriter::PrintLines(const Lines& lines) const { + // The line number format is: + // + for (const auto& kv : lines.GetLineMap()) { + const Line& l = kv.second; + AddressRangeVector ranges; + MapAddressRange(image_map_, AddressRange(l.rva, l.length), &ranges); + for (auto& range : ranges) { + fprintf(output_, "%lx %lx %lu %lu\n", range.rva, range.length, l.line_num, + l.file_id); + } + } +} + +bool PDBSourceLineWriter::PrintFunction(IDiaSymbol* function, + IDiaSymbol* block, + bool has_multiple_symbols) { + // The function format is: + // FUNC
    + 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(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 lines; + if (FAILED(session_->findLinesByRVA(rva, DWORD(length), &lines))) { + return false; + } + + // Get top level lines first, which later may be split into multiple smaller + // lines if any inline exists in their ranges if we want to handle inline. + Lines line_list; + if (!GetLines(lines, &line_list)) { + return false; + } + if (handle_inline_) { + vector> inlines; + if (!GetInlines(block, &line_list, 0, &inlines)) { + return false; + } + PrintInlines(inlines); + } + PrintLines(line_list); + return true; +} + +bool PDBSourceLineWriter::PrintSourceFiles() { + CComPtr global; + if (FAILED(session_->get_globalScope(&global))) { + fprintf(stderr, "get_globalScope failed\n"); + return false; + } + + CComPtr compilands; + if (FAILED(global->findChildren(SymTagCompiland, NULL, + nsNone, &compilands))) { + fprintf(stderr, "findChildren failed\n"); + return false; + } + + // Print a dummy file with id equals 0 to represent unknown file, because + // inline records might have unknown call site. + fwprintf(output_, L"FILE %d unknown file\n", 0); + + CComPtr compiland; + ULONG count; + while (SUCCEEDED(compilands->Next(1, &compiland, &count)) && count == 1) { + CComPtr source_files; + if (FAILED(session_->findFile(compiland, NULL, nsNone, &source_files))) { + return false; + } + CComPtr 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 global; + HRESULT hr; + + if (FAILED(session_->get_globalScope(&global))) { + fprintf(stderr, "get_globalScope failed\n"); + return false; + } + + CComPtr symbols = NULL; + + // Find all function symbols first. + SymbolMap rva_symbol; + hr = global->findChildren(SymTagFunction, NULL, nsNone, &symbols); + + if (SUCCEEDED(hr)) { + CComPtr 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 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 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 compilands; + if (FAILED(global->findChildren(SymTagCompiland, NULL, + nsNone, &compilands))) { + fprintf(stderr, "findChildren failed on the global\n"); + return false; + } + + CComPtr compiland; + while (SUCCEEDED(compilands->Next(1, &compiland, &count)) && count == 1) { + CComPtr blocks; + if (FAILED(compiland->findChildren(SymTagBlock, NULL, + nsNone, &blocks))) { + fprintf(stderr, "findChildren failed on a compiland\n"); + return false; + } + + CComPtr block; + while (SUCCEEDED(blocks->Next(1, &block, &count)) && count == 1) { + // find this block's lexical parent function + CComPtr 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; +} + +void PDBSourceLineWriter::PrintInlineOrigins() const { + struct OriginCompare { + bool operator()(const InlineOrigin lhs, const InlineOrigin rhs) const { + return lhs.id < rhs.id; + } + }; + set origins; + // Sort by origin id. + for (auto const& origin : inline_origins_) + origins.insert(origin.second); + for (auto o : origins) { + fprintf(output_, "INLINE_ORIGIN %d %ls\n", o.id, o.name.c_str()); + } +} + +bool PDBSourceLineWriter::GetInlines(IDiaSymbol* block, + Lines* line_list, + int inline_nest_level, + vector>* inlines) { + CComPtr inline_callsites; + if (FAILED(block->findChildrenEx(SymTagInlineSite, nullptr, nsNone, + &inline_callsites))) { + return false; + } + ULONG count; + CComPtr callsite; + while (SUCCEEDED(inline_callsites->Next(1, &callsite, &count)) && + count == 1) { + unique_ptr new_inline(new Inline(inline_nest_level)); + CComPtr lines; + // All inlinee lines have the same file id. + DWORD file_id = 0; + DWORD call_site_line = 0; + if (FAILED(session_->findInlineeLines(callsite, &lines))) { + return false; + } + CComPtr dia_line; + while (SUCCEEDED(lines->Next(1, &dia_line, &count)) && count == 1) { + Line line; + if (!GetLine(dia_line, &line)) { + return false; + } + // Silently ignore zero-length lines. + if (line.length != 0) { + // Use the first line num and file id at rva as this inline's call site + // line number, because after adding lines it may be changed to inner + // line number and inner file id. + if (call_site_line == 0) + call_site_line = line_list->GetLineNum(line.rva); + if (file_id == 0) + file_id = line_list->GetFileId(line.rva); + line_list->AddLine(line); + new_inline->ExtendRanges(line); + } + dia_line.Release(); + } + BSTR name; + callsite->get_name(&name); + if (SysStringLen(name) == 0) { + name = SysAllocString(L""); + } + auto iter = inline_origins_.find(name); + if (iter == inline_origins_.end()) { + InlineOrigin origin; + origin.id = inline_origins_.size(); + origin.name = name; + inline_origins_[name] = origin; + } + new_inline->SetOriginId(inline_origins_[name].id); + new_inline->SetCallSiteLine(call_site_line); + new_inline->SetCallSiteFileId(file_id); + // Go to next level. + vector> child_inlines; + if (!GetInlines(callsite, line_list, inline_nest_level + 1, + &child_inlines)) { + return false; + } + new_inline->SetChildInlines(std::move(child_inlines)); + inlines->push_back(std::move(new_inline)); + callsite.Release(); + } + return true; +} + +void PDBSourceLineWriter::PrintInlines( + const vector>& inlines) const { + for (const unique_ptr& in : inlines) { + in->Print(output_); + } +} + +#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 frame_data_enum; + if (!FindTable(session_, &frame_data_enum)) + return false; + + DWORD last_type = std::numeric_limits::max(); + DWORD last_rva = std::numeric_limits::max(); + DWORD last_code_size = 0; + DWORD last_prolog_size = std::numeric_limits::max(); + + CComPtr 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 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(); + } + return PrintFrameDataUsingPDB(); +} + +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 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 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; + + // Use get_undecoratedNameEx to get readable C++ names with arguments. + if (function->get_undecoratedNameEx(kUndecorateOptions, 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 "", so match that. + *name = SysAllocString(L""); + 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 { + StripLlvmSuffixAndUndecorate(name); + + // 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 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 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 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(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(); + PrintInlineOrigins(); + + 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 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(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 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/shared/sentry/external/breakpad/src/common/windows/pdb_source_line_writer.h b/shared/sentry/external/breakpad/src/common/windows/pdb_source_line_writer.h new file mode 100644 index 000000000..42bd94d95 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/windows/pdb_source_line_writer.h @@ -0,0 +1,357 @@ +// 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 + +#include +#include +#include +#include +#include + +#include "common/windows/module_info.h" +#include "common/windows/omap.h" + +struct IDiaEnumLineNumbers; +struct IDiaSession; +struct IDiaSymbol; + +namespace google_breakpad { + +using std::map; +using std::vector; +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(bool handle_inline = false); + ~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: + // InlineOrigin represents INLINE_ORIGIN record in a symbol file. It's an + // inlined function. + struct InlineOrigin { + // The unique id for an InlineOrigin. + int id; + // The name of the inlined function. + wstring name; + }; + + // Line represents LINE record in a symbol file. It represents a source code + // line. + struct Line { + // The relative address of a line. + DWORD rva; + // The number bytes this line has. + DWORD length; + // The source line number. + DWORD line_num; + // The source file id where the source line is located at. + DWORD file_id; + }; + + // Inline represents INLINE record in a symbol file. + class Inline { + public: + explicit Inline(int inline_nest_level); + + void SetOriginId(int origin_id); + + // Adding inlinee line's range into ranges. If line is adjacent with any + // existing lines, extend the range. Otherwise, add line as a new range. + void ExtendRanges(const Line& line); + + void SetCallSiteLine(DWORD call_site_line); + + void SetCallSiteFileId(DWORD call_site_file_id); + + void SetChildInlines(std::vector> child_inlines); + + void Print(FILE* output) const; + + private: + // The nest level of this inline record. + int inline_nest_level_; + // The source line number at where this inlined function is called. + DWORD call_site_line_ = 0; + // The call site file id at where this inlined function is called. + DWORD call_site_file_id_ = 0; + // The id used for referring to an InlineOrigin. + int origin_id_ = 0; + // A map from rva to length. This is the address ranges covered by this + // Inline. + map ranges_; + // The list of direct Inlines inlined inside this Inline. + vector> child_inlines_; + }; + + // Lines represents a map of lines inside a function with rva as the key. + // AddLine function adds a line into the map and ensures that there is no + // overlap between any two lines in the map. + class Lines { + public: + const map& GetLineMap() const { return line_map_; } + + // Finds the line from line_map_ that contains the given rva returns its + // line_num. If not found, return 0. + DWORD GetLineNum(DWORD rva) const; + + // Finds the line from line_map_ that contains the given rva returns its + // file_id. If not found, return 0. + DWORD GetFileId(DWORD rva) const; + + // Add the `line` into line_map_. If the `line` overlaps with existing + // lines, truncate the existing lines and add the given line. It ensures + // that all lines in line_map_ do not overlap with each other. For example, + // suppose there is a line A in the map and we call AddLine with Line B. + // Line A: rva: 100, length: 20, line_num: 10, file_id: 1 + // Line B: rva: 105, length: 10, line_num: 4, file_id: 2 + // After calling AddLine with Line B, we will have the following lines: + // Line 1: rva: 100, length: 5, line_num: 10, file_id: 1 + // Line 2: rva: 105, length: 10, line_num: 4, file_id: 2 + // Line 3: rva: 115, length: 5, line_num: 10, file_id: 1 + void AddLine(const Line& line); + + private: + // Finds the line from line_map_ that contains the given rva. If not found, + // return nullptr. + const Line* GetLine(DWORD rva) const; + // The key is rva. AddLine function ensures that any two lines in the map do + // not overlap. + map line_map_; + }; + + // Construct Line from IDiaLineNumber. The output Line is stored at line. + // Return true on success. + bool GetLine(IDiaLineNumber* dia_line, Line* line) const; + + // Construct Lines from IDiaEnumLineNumbers. The list of Lines are stored at + // line_list. + // Returns true on success. + bool GetLines(IDiaEnumLineNumbers* lines, Lines* line_list) const; + + // Outputs the line/address pairs for each line in the enumerator. + void PrintLines(const Lines& lines) const; + + // 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(); + + // Output all inline origins. + void PrintInlineOrigins() const; + + // Retrieve inlines inside the given block. It also adds inlinee lines to + // `line_list` since inner lines are more precise source location. If the + // block has children wih SymTagInlineSite Tag, it will recursively (DFS) call + // itself with each child as first argument. Returns true on success. + // `block`: the IDiaSymbol that may have inline sites. + // `line_list`: the list of lines inside current function. + // `inline_nest_level`: the nest level of block's Inlines. + // `inlines`: the vector to store the list of inlines for the block. + bool GetInlines(IDiaSymbol* block, + Lines* line_list, + int inline_nest_level, + vector>* inlines); + + // Outputs all inlines. + void PrintInlines(const vector>& inlines) const; + + // 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::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) const { + unordered_map::const_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 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 file_ids_; + // This maps unique filenames to file IDs. + unordered_map unique_files_; + + // The INLINE_ORIGINS records. The key is the function name. + std::map inline_origins_; + + // This is used for calculating post-transform symbol addresses and lengths. + ImageMap image_map_; + + // If we should output INLINE/INLINE_ORIGIN records + bool handle_inline_; + + // 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/shared/sentry/external/breakpad/src/common/windows/pe_source_line_writer.cc b/shared/sentry/external/breakpad/src/common/windows/pe_source_line_writer.cc new file mode 100644 index 000000000..cb6cc7139 --- /dev/null +++ b/shared/sentry/external/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/shared/sentry/external/breakpad/src/common/windows/pe_source_line_writer.h b/shared/sentry/external/breakpad/src/common/windows/pe_source_line_writer.h new file mode 100644 index 000000000..2bf1d4fd2 --- /dev/null +++ b/shared/sentry/external/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 + +#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/shared/sentry/external/breakpad/src/common/windows/pe_util.cc b/shared/sentry/external/breakpad/src/common/windows/pe_util.cc new file mode 100644 index 000000000..f8934d27e --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/windows/pe_util.cc @@ -0,0 +1,412 @@ +// 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 +#include +#include +#include + +#include +#include + +#include "common/windows/string_utils-inl.h" +#include "common/windows/guid_string.h" + +namespace { + +/* + * Not defined in WinNT.h prior to SDK 10.0.20348.0 for some reason. + * Definitions taken from: http://uninformed.org/index.cgi?v=4&a=1&p=13 + * + */ +typedef unsigned char UBYTE; + +#if !defined(UNW_FLAG_EHANDLER) +#define UNW_FLAG_EHANDLER 0x01 +#endif +#if !defined(UNW_FLAG_UHANDLER) +#define UNW_FLAG_UHANDLER 0x02 +#endif +#if !defined(UNW_FLAG_CHAININFO) +#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(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( + 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(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(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( + 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( + ImageRvaToVa(img->FileHeader, + img->MappedAddress, + unwind_rva, + &img->LastRvaSection)); + unwind_rva = chained_func->UnwindInfoAddress; + } + + UnwindInfo *unwind_info = static_cast( + 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( + (unwind_info->unwind_code + + ((unwind_info->count_of_codes + 1) & ~1))); + + unwind_info = static_cast( + 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/shared/sentry/external/breakpad/src/common/windows/pe_util.h b/shared/sentry/external/breakpad/src/common/windows/pe_util.h new file mode 100644 index 000000000..634ba2934 --- /dev/null +++ b/shared/sentry/external/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 + +#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/shared/sentry/external/breakpad/src/common/windows/string_utils-inl.h b/shared/sentry/external/breakpad/src/common/windows/string_utils-inl.h new file mode 100644 index 000000000..935e19f52 --- /dev/null +++ b/shared/sentry/external/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 +#include + +#include + +// 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/shared/sentry/external/breakpad/src/common/windows/string_utils.cc b/shared/sentry/external/breakpad/src/common/windows/string_utils.cc new file mode 100644 index 000000000..90aab0386 --- /dev/null +++ b/shared/sentry/external/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 +#include + +#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 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 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/shared/sentry/external/breakpad/src/common/windows/symbol_collector_client.cc b/shared/sentry/external/breakpad/src/common/windows/symbol_collector_client.cc new file mode 100644 index 000000000..558a97b98 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/windows/symbol_collector_client.cc @@ -0,0 +1,159 @@ +#include "common/windows/symbol_collector_client.h" + +#include + +#include + +#include "common/windows/http_upload.h" + +namespace google_breakpad { + + // static + bool SymbolCollectorClient::CreateUploadUrl( + wstring& api_url, + wstring& api_key, + int* timeout_ms, + 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"", + timeout_ms, + &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, + int* timeout_ms, + 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"}, " + L"use_async_processing: true }"; + wstring response; + int response_code; + + if (!HTTPUpload::SendSimplePostRequest( + url, + body, + L"application/json", + timeout_ms, + &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, + int* timeout_ms, + 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, + timeout_ms, + &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/shared/sentry/external/breakpad/src/common/windows/symbol_collector_client.h b/shared/sentry/external/breakpad/src/common/windows/symbol_collector_client.h new file mode 100644 index 000000000..4a85c0529 --- /dev/null +++ b/shared/sentry/external/breakpad/src/common/windows/symbol_collector_client.h @@ -0,0 +1,92 @@ +// 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 + +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, + int* timeout_ms, + 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, + int* timeout_ms, + 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, + int* timeout_ms, + const wstring& debug_file, + const wstring& debug_id); + }; + +} // namespace google_breakpad + +#endif // COMMON_WINDOWS_SYMBOL_COLLECTOR_CLIENT_H_ diff --git a/shared/sentry/external/breakpad/src/config.h.in b/shared/sentry/external/breakpad/src/config.h.in new file mode 100644 index 000000000..ba6c520f7 --- /dev/null +++ b/shared/sentry/external/breakpad/src/config.h.in @@ -0,0 +1,100 @@ +/* src/config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the `arc4random' function. */ +#undef HAVE_ARC4RANDOM + +/* Define to 1 if you have the header file. */ +#undef HAVE_A_OUT_H + +/* define if the compiler supports basic C++11 syntax */ +#undef HAVE_CXX11 + +/* Define to 1 if you have the `getcontext' function. */ +#undef HAVE_GETCONTEXT + +/* Define to 1 if you have the `getrandom' function. */ +#undef HAVE_GETRANDOM + +/* Define to 1 if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `memfd_create' function. */ +#undef HAVE_MEMFD_CREATE + +/* Define to 1 if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define if you have POSIX threads libraries and header files. */ +#undef HAVE_PTHREAD + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_MMAN_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_RANDOM_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Fallback definition for old systems */ +#undef O_CLOEXEC + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to necessary symbol if this constant uses a non-standard name on + your system. */ +#undef PTHREAD_CREATE_JOINABLE + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* Version number of package */ +#undef VERSION + +/* Enable large inode numbers on Mac OS X 10.5. */ +#ifndef _DARWIN_USE_64_BIT_INODE +# define _DARWIN_USE_64_BIT_INODE 1 +#endif + +/* Number of bits in a file offset, on hosts where this is settable. */ +#undef _FILE_OFFSET_BITS + +/* Define for large files, on AIX-style hosts. */ +#undef _LARGE_FILES diff --git a/shared/sentry/external/breakpad/src/google_breakpad/common/breakpad_types.h b/shared/sentry/external/breakpad/src/google_breakpad/common/breakpad_types.h new file mode 100644 index 000000000..d8828043f --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/common/breakpad_types.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. */ + +/* breakpad_types.h: Precise-width types + * + * (This is C99 source, please don't corrupt it with C++.) + * + * This file ensures that types uintN_t are defined for N = 8, 16, 32, and + * 64. Types of precise widths are crucial to the task of writing data + * structures on one platform and reading them on another. + * + * Author: Mark Mentovai */ + +#ifndef GOOGLE_BREAKPAD_COMMON_BREAKPAD_TYPES_H__ +#define GOOGLE_BREAKPAD_COMMON_BREAKPAD_TYPES_H__ + +#if (defined(_INTTYPES_H) || defined(_INTTYPES_H_)) && \ + !defined(__STDC_FORMAT_MACROS) +#error "inttypes.h has already been included before this header file, but " +#error "without __STDC_FORMAT_MACROS defined." +#endif + +#ifndef __STDC_FORMAT_MACROS +#define __STDC_FORMAT_MACROS +#endif /* __STDC_FORMAT_MACROS */ +#include + +typedef struct { + uint64_t high; + uint64_t low; +} uint128_struct; + +typedef uint64_t breakpad_time_t; + +/* Try to get PRIx64 from inttypes.h, but if it's not defined, fall back to + * llx, which is the format string for "long long" - this is a 64-bit + * integral type on many systems. */ +#ifndef PRIx64 +#define PRIx64 "llx" +#endif /* !PRIx64 */ + +#endif /* GOOGLE_BREAKPAD_COMMON_BREAKPAD_TYPES_H__ */ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_amd64.h b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_amd64.h new file mode 100644 index 000000000..4256706d7 --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_amd64.h @@ -0,0 +1,235 @@ +/* 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. */ + +/* minidump_format.h: A cross-platform reimplementation of minidump-related + * portions of DbgHelp.h from the Windows Platform SDK. + * + * (This is C99 source, please don't corrupt it with C++.) + * + * This file contains the necessary definitions to read minidump files + * produced on amd64. These files may be read on any platform provided + * that the alignments of these structures on the processing system are + * identical to the alignments of these structures on the producing system. + * For this reason, precise-sized types are used. The structures defined + * by this file have been laid out to minimize alignment problems by ensuring + * ensuring that all members are aligned on their natural boundaries. In + * In some cases, tail-padding may be significant when different ABIs specify + * different tail-padding behaviors. To avoid problems when reading or + * writing affected structures, MD_*_SIZE macros are provided where needed, + * containing the useful size of the structures without padding. + * + * Structures that are defined by Microsoft to contain a zero-length array + * are instead defined here to contain an array with one element, as + * zero-length arrays are forbidden by standard C and C++. In these cases, + * *_minsize constants are provided to be used in place of sizeof. For a + * cleaner interface to these sizes when using C++, see minidump_size.h. + * + * These structures are also sufficient to populate minidump files. + * + * These definitions may be extended to support handling minidump files + * for other CPUs and other operating systems. + * + * Because precise data type sizes are crucial for this implementation to + * function properly and portably in terms of interoperability with minidumps + * produced by DbgHelp on Windows, a set of primitive types with known sizes + * are used as the basis of each structure defined by this file. DbgHelp + * on Windows is assumed to be the reference implementation; this file + * seeks to provide a cross-platform compatible implementation. To avoid + * collisions with the types and values defined and used by DbgHelp in the + * event that this implementation is used on Windows, each type and value + * defined here is given a new name, beginning with "MD". Names of the + * equivalent types and values in the Windows Platform SDK are given in + * comments. + * + * Author: Mark Mentovai + * Change to split into its own file: Neal Sidhwaney */ + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_AMD64_H__ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_AMD64_H__ + + +/* + * AMD64 support, see WINNT.H + */ + +typedef struct { + uint16_t control_word; + uint16_t status_word; + uint8_t tag_word; + uint8_t reserved1; + uint16_t error_opcode; + uint32_t error_offset; + uint16_t error_selector; + uint16_t reserved2; + uint32_t data_offset; + uint16_t data_selector; + uint16_t reserved3; + uint32_t mx_csr; + uint32_t mx_csr_mask; + uint128_struct float_registers[8]; + uint128_struct xmm_registers[16]; + uint8_t reserved4[96]; +} MDXmmSaveArea32AMD64; /* XMM_SAVE_AREA32 */ + +#define MD_CONTEXT_AMD64_VR_COUNT 26 + +typedef struct { + /* + * Register parameter home addresses. + */ + uint64_t p1_home; + uint64_t p2_home; + uint64_t p3_home; + uint64_t p4_home; + uint64_t p5_home; + uint64_t p6_home; + + /* The next field determines the layout of the structure, and which parts + * of it are populated */ + uint32_t context_flags; + uint32_t mx_csr; + + /* The next register is included with MD_CONTEXT_AMD64_CONTROL */ + uint16_t cs; + + /* The next 4 registers are included with MD_CONTEXT_AMD64_SEGMENTS */ + uint16_t ds; + uint16_t es; + uint16_t fs; + uint16_t gs; + + /* The next 2 registers are included with MD_CONTEXT_AMD64_CONTROL */ + uint16_t ss; + uint32_t eflags; + + /* The next 6 registers are included with MD_CONTEXT_AMD64_DEBUG_REGISTERS */ + uint64_t dr0; + uint64_t dr1; + uint64_t dr2; + uint64_t dr3; + uint64_t dr6; + uint64_t dr7; + + /* The next 4 registers are included with MD_CONTEXT_AMD64_INTEGER */ + uint64_t rax; + uint64_t rcx; + uint64_t rdx; + uint64_t rbx; + + /* The next register is included with MD_CONTEXT_AMD64_CONTROL */ + uint64_t rsp; + + /* The next 11 registers are included with MD_CONTEXT_AMD64_INTEGER */ + uint64_t rbp; + uint64_t rsi; + uint64_t rdi; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + + /* The next register is included with MD_CONTEXT_AMD64_CONTROL */ + uint64_t rip; + + /* The next set of registers are included with + * MD_CONTEXT_AMD64_FLOATING_POINT + */ + union { + MDXmmSaveArea32AMD64 flt_save; + struct { + uint128_struct header[2]; + uint128_struct legacy[8]; + uint128_struct xmm0; + uint128_struct xmm1; + uint128_struct xmm2; + uint128_struct xmm3; + uint128_struct xmm4; + uint128_struct xmm5; + uint128_struct xmm6; + uint128_struct xmm7; + uint128_struct xmm8; + uint128_struct xmm9; + uint128_struct xmm10; + uint128_struct xmm11; + uint128_struct xmm12; + uint128_struct xmm13; + uint128_struct xmm14; + uint128_struct xmm15; + } sse_registers; + }; + + uint128_struct vector_register[MD_CONTEXT_AMD64_VR_COUNT]; + uint64_t vector_control; + + /* The next 5 registers are included with MD_CONTEXT_AMD64_DEBUG_REGISTERS */ + uint64_t debug_control; + uint64_t last_branch_to_rip; + uint64_t last_branch_from_rip; + uint64_t last_exception_to_rip; + uint64_t last_exception_from_rip; + +} MDRawContextAMD64; /* CONTEXT */ + +/* For (MDRawContextAMD64).context_flags. These values indicate the type of + * context stored in the structure. The high 24 bits identify the CPU, the + * low 8 bits identify the type of context saved. */ +#define MD_CONTEXT_AMD64 0x00100000 /* CONTEXT_AMD64 */ +#define MD_CONTEXT_AMD64_CONTROL (MD_CONTEXT_AMD64 | 0x00000001) + /* CONTEXT_CONTROL */ +#define MD_CONTEXT_AMD64_INTEGER (MD_CONTEXT_AMD64 | 0x00000002) + /* CONTEXT_INTEGER */ +#define MD_CONTEXT_AMD64_SEGMENTS (MD_CONTEXT_AMD64 | 0x00000004) + /* CONTEXT_SEGMENTS */ +#define MD_CONTEXT_AMD64_FLOATING_POINT (MD_CONTEXT_AMD64 | 0x00000008) + /* CONTEXT_FLOATING_POINT */ +#define MD_CONTEXT_AMD64_DEBUG_REGISTERS (MD_CONTEXT_AMD64 | 0x00000010) + /* CONTEXT_DEBUG_REGISTERS */ +#define MD_CONTEXT_AMD64_XSTATE (MD_CONTEXT_AMD64 | 0x00000040) + /* CONTEXT_XSTATE */ + +/* WinNT.h refers to CONTEXT_MMX_REGISTERS but doesn't appear to define it + * I think it really means CONTEXT_FLOATING_POINT. + */ + +#define MD_CONTEXT_AMD64_FULL (MD_CONTEXT_AMD64_CONTROL | \ + MD_CONTEXT_AMD64_INTEGER | \ + MD_CONTEXT_AMD64_FLOATING_POINT) + /* CONTEXT_FULL */ + +#define MD_CONTEXT_AMD64_ALL (MD_CONTEXT_AMD64_FULL | \ + MD_CONTEXT_AMD64_SEGMENTS | \ + MD_CONTEXT_X86_DEBUG_REGISTERS) + /* CONTEXT_ALL */ + + +#endif /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_AMD64_H__ */ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_arm.h b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_arm.h new file mode 100644 index 000000000..6a7113833 --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_arm.h @@ -0,0 +1,151 @@ +/* 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. */ + +/* minidump_format.h: A cross-platform reimplementation of minidump-related + * portions of DbgHelp.h from the Windows Platform SDK. + * + * (This is C99 source, please don't corrupt it with C++.) + * + * This file contains the necessary definitions to read minidump files + * produced on ARM. These files may be read on any platform provided + * that the alignments of these structures on the processing system are + * identical to the alignments of these structures on the producing system. + * For this reason, precise-sized types are used. The structures defined + * by this file have been laid out to minimize alignment problems by + * ensuring that all members are aligned on their natural boundaries. + * In some cases, tail-padding may be significant when different ABIs specify + * different tail-padding behaviors. To avoid problems when reading or + * writing affected structures, MD_*_SIZE macros are provided where needed, + * containing the useful size of the structures without padding. + * + * Structures that are defined by Microsoft to contain a zero-length array + * are instead defined here to contain an array with one element, as + * zero-length arrays are forbidden by standard C and C++. In these cases, + * *_minsize constants are provided to be used in place of sizeof. For a + * cleaner interface to these sizes when using C++, see minidump_size.h. + * + * These structures are also sufficient to populate minidump files. + * + * Because precise data type sizes are crucial for this implementation to + * function properly and portably, a set of primitive types with known sizes + * are used as the basis of each structure defined by this file. + * + * Author: Julian Seward + */ + +/* + * ARM support + */ + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_ARM_H__ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_ARM_H__ + +#define MD_FLOATINGSAVEAREA_ARM_FPR_COUNT 32 +#define MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT 8 + +/* + * Note that these structures *do not* map directly to the CONTEXT + * structure defined in WinNT.h in the Windows Mobile SDK. That structure + * does not accomodate VFPv3, and I'm unsure if it was ever used in the + * wild anyway, as Windows CE only seems to produce "cedumps" which + * are not exactly minidumps. + */ +typedef struct { + uint64_t fpscr; /* FPU status register */ + + /* 32 64-bit floating point registers, d0 .. d31. */ + uint64_t regs[MD_FLOATINGSAVEAREA_ARM_FPR_COUNT]; + + /* Miscellaneous control words */ + uint32_t extra[MD_FLOATINGSAVEAREA_ARM_FPEXTRA_COUNT]; +} MDFloatingSaveAreaARM; + +#define MD_CONTEXT_ARM_GPR_COUNT 16 + +typedef struct { + /* The next field determines the layout of the structure, and which parts + * of it are populated + */ + uint32_t context_flags; + + /* 16 32-bit integer registers, r0 .. r15 + * Note the following fixed uses: + * r13 is the stack pointer + * r14 is the link register + * r15 is the program counter + */ + uint32_t iregs[MD_CONTEXT_ARM_GPR_COUNT]; + + /* CPSR (flags, basically): 32 bits: + bit 31 - N (negative) + bit 30 - Z (zero) + bit 29 - C (carry) + bit 28 - V (overflow) + bit 27 - Q (saturation flag, sticky) + All other fields -- ignore */ + uint32_t cpsr; + + /* The next field is included with MD_CONTEXT_ARM_FLOATING_POINT */ + MDFloatingSaveAreaARM float_save; + +} MDRawContextARM; + +/* Indices into iregs for registers with a dedicated or conventional + * purpose. + */ +enum MDARMRegisterNumbers { + MD_CONTEXT_ARM_REG_IOS_FP = 7, + MD_CONTEXT_ARM_REG_FP = 11, + MD_CONTEXT_ARM_REG_SP = 13, + MD_CONTEXT_ARM_REG_LR = 14, + MD_CONTEXT_ARM_REG_PC = 15 +}; + +/* For (MDRawContextARM).context_flags. These values indicate the type of + * context stored in the structure. */ +/* CONTEXT_ARM from the Windows CE 5.0 SDK. This value isn't correct + * because this bit can be used for flags. Presumably this value was + * never actually used in minidumps, but only in "CEDumps" which + * are a whole parallel minidump file format for Windows CE. + * Therefore, Breakpad defines its own value for ARM CPUs. + */ +#define MD_CONTEXT_ARM_OLD 0x00000040 +/* This value was chosen to avoid likely conflicts with MD_CONTEXT_* + * for other CPUs. */ +#define MD_CONTEXT_ARM 0x40000000 +#define MD_CONTEXT_ARM_INTEGER (MD_CONTEXT_ARM | 0x00000002) +#define MD_CONTEXT_ARM_FLOATING_POINT (MD_CONTEXT_ARM | 0x00000004) + +#define MD_CONTEXT_ARM_FULL (MD_CONTEXT_ARM_INTEGER | \ + MD_CONTEXT_ARM_FLOATING_POINT) + +#define MD_CONTEXT_ARM_ALL (MD_CONTEXT_ARM_INTEGER | \ + MD_CONTEXT_ARM_FLOATING_POINT) + +#endif /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_ARM_H__ */ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_arm64.h b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_arm64.h new file mode 100644 index 000000000..0411bebb4 --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_arm64.h @@ -0,0 +1,192 @@ +/* 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. */ + +/* minidump_format.h: A cross-platform reimplementation of minidump-related + * portions of DbgHelp.h from the Windows Platform SDK. + * + * (This is C99 source, please don't corrupt it with C++.) + * + * This file contains the necessary definitions to read minidump files + * produced on ARM. These files may be read on any platform provided + * that the alignments of these structures on the processing system are + * identical to the alignments of these structures on the producing system. + * For this reason, precise-sized types are used. The structures defined + * by this file have been laid out to minimize alignment problems by + * ensuring that all members are aligned on their natural boundaries. + * In some cases, tail-padding may be significant when different ABIs specify + * different tail-padding behaviors. To avoid problems when reading or + * writing affected structures, MD_*_SIZE macros are provided where needed, + * containing the useful size of the structures without padding. + * + * Structures that are defined by Microsoft to contain a zero-length array + * are instead defined here to contain an array with one element, as + * zero-length arrays are forbidden by standard C and C++. In these cases, + * *_minsize constants are provided to be used in place of sizeof. For a + * cleaner interface to these sizes when using C++, see minidump_size.h. + * + * These structures are also sufficient to populate minidump files. + * + * Because precise data type sizes are crucial for this implementation to + * function properly and portably, a set of primitive types with known sizes + * are used as the basis of each structure defined by this file. + * + * Author: Colin Blundell + */ + +/* + * ARM64 support + */ + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_ARM64_H__ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_ARM64_H__ + +#include "google_breakpad/common/breakpad_types.h" + +#define MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT 32 +#define MD_CONTEXT_ARM64_GPR_COUNT 33 + +typedef struct { + /* 32 128-bit floating point registers, d0 .. d31. */ + uint128_struct regs[MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT]; + + uint32_t fpcr; /* FPU control register */ + uint32_t fpsr; /* FPU status register */ +} MDFloatingSaveAreaARM64; + +/* For (MDRawContextARM64).context_flags. These values indicate the type of + * context stored in the structure. */ +#define MD_CONTEXT_ARM64 0x00400000 +#define MD_CONTEXT_ARM64_CONTROL (MD_CONTEXT_ARM64 | 0x00000001) +#define MD_CONTEXT_ARM64_INTEGER (MD_CONTEXT_ARM64 | 0x00000002) +#define MD_CONTEXT_ARM64_FLOATING_POINT (MD_CONTEXT_ARM64 | 0x00000004) +#define MD_CONTEXT_ARM64_DEBUG (MD_CONTEXT_ARM64 | 0x00000008) +#define MD_CONTEXT_ARM64_FULL (MD_CONTEXT_ARM64_CONTROL | \ + MD_CONTEXT_ARM64_INTEGER | \ + MD_CONTEXT_ARM64_FLOATING_POINT) +#define MD_CONTEXT_ARM64_ALL (MD_CONTEXT_ARM64_FULL | MD_CONTEXT_ARM64_DEBUG) + +typedef struct { + /* Determines which fields of this struct are populated */ + uint32_t context_flags; + + /* CPSR (flags, basically): 32 bits: + bit 31 - N (negative) + bit 30 - Z (zero) + bit 29 - C (carry) + bit 28 - V (overflow) + bit 27 - Q (saturation flag, sticky) + All other fields -- ignore */ + uint32_t cpsr; + + /* 33 64-bit integer registers, x0 .. x31 + the PC + * Note the following fixed uses: + * x29 is the frame pointer + * x30 is the link register + * x31 is the stack pointer + * The PC is effectively x32. + */ + uint64_t iregs[MD_CONTEXT_ARM64_GPR_COUNT]; + + /* The next field is included with MD_CONTEXT64_ARM_FLOATING_POINT */ + MDFloatingSaveAreaARM64 float_save; + + uint32_t bcr[8]; + uint64_t bvr[8]; + uint32_t wcr[2]; + uint64_t wvr[2]; +} MDRawContextARM64; + +typedef struct { + uint32_t fpsr; /* FPU status register */ + uint32_t fpcr; /* FPU control register */ + + /* 32 128-bit floating point registers, d0 .. d31. */ + uint128_struct regs[MD_FLOATINGSAVEAREA_ARM64_FPR_COUNT]; +} MDFloatingSaveAreaARM64_Old; + +/* Use the same 32-bit alignment when accessing this structure from 64-bit code + * as is used natively in 32-bit code. */ +#pragma pack(push, 4) + +typedef struct { + /* The next field determines the layout of the structure, and which parts + * of it are populated + */ + uint64_t context_flags; + + /* 33 64-bit integer registers, x0 .. x31 + the PC + * Note the following fixed uses: + * x29 is the frame pointer + * x30 is the link register + * x31 is the stack pointer + * The PC is effectively x32. + */ + uint64_t iregs[MD_CONTEXT_ARM64_GPR_COUNT]; + + /* CPSR (flags, basically): 32 bits: + bit 31 - N (negative) + bit 30 - Z (zero) + bit 29 - C (carry) + bit 28 - V (overflow) + bit 27 - Q (saturation flag, sticky) + All other fields -- ignore */ + uint32_t cpsr; + + /* The next field is included with MD_CONTEXT64_ARM_FLOATING_POINT */ + MDFloatingSaveAreaARM64_Old float_save; + +} MDRawContextARM64_Old; + +#pragma pack(pop) + +/* Indices into iregs for registers with a dedicated or conventional + * purpose. + */ +enum MDARM64RegisterNumbers { + MD_CONTEXT_ARM64_REG_FP = 29, + MD_CONTEXT_ARM64_REG_LR = 30, + MD_CONTEXT_ARM64_REG_SP = 31, + MD_CONTEXT_ARM64_REG_PC = 32 +}; + +/* For (MDRawContextARM64_Old).context_flags. These values indicate the type of + * context stored in the structure. MD_CONTEXT_ARM64_OLD is Breakpad-defined. + * This value was chosen to avoid likely conflicts with MD_CONTEXT_* + * for other CPUs. */ +#define MD_CONTEXT_ARM64_OLD 0x80000000 +#define MD_CONTEXT_ARM64_INTEGER_OLD (MD_CONTEXT_ARM64_OLD | 0x00000002) +#define MD_CONTEXT_ARM64_FLOATING_POINT_OLD (MD_CONTEXT_ARM64_OLD | 0x00000004) + +#define MD_CONTEXT_ARM64_FULL_OLD (MD_CONTEXT_ARM64_INTEGER_OLD | \ + MD_CONTEXT_ARM64_FLOATING_POINT_OLD) + +#define MD_CONTEXT_ARM64_ALL_OLD (MD_CONTEXT_ARM64_INTEGER_OLD | \ + MD_CONTEXT_ARM64_FLOATING_POINT_OLD) + +#endif /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_ARM64_H__ */ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_mips.h b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_mips.h new file mode 100644 index 000000000..f4e2b5891 --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_mips.h @@ -0,0 +1,176 @@ +/* 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. */ + +/* minidump_format.h: A cross-platform reimplementation of minidump-related + * portions of DbgHelp.h from the Windows Platform SDK. + * + * (This is C99 source, please don't corrupt it with C++.) + * + * This file contains the necessary definitions to read minidump files + * produced on MIPS. These files may be read on any platform provided + * that the alignments of these structures on the processing system are + * identical to the alignments of these structures on the producing system. + * For this reason, precise-sized types are used. The structures defined + * by this file have been laid out to minimize alignment problems by + * ensuring that all members are aligned on their natural boundaries. + * In some cases, tail-padding may be significant when different ABIs specify + * different tail-padding behaviors. To avoid problems when reading or + * writing affected structures, MD_*_SIZE macros are provided where needed, + * containing the useful size of the structures without padding. + * + * Structures that are defined by Microsoft to contain a zero-length array + * are instead defined here to contain an array with one element, as + * zero-length arrays are forbidden by standard C and C++. In these cases, + * *_minsize constants are provided to be used in place of sizeof. For a + * cleaner interface to these sizes when using C++, see minidump_size.h. + * + * These structures are also sufficient to populate minidump files. + * + * Because precise data type sizes are crucial for this implementation to + * function properly and portably, a set of primitive types with known sizes + * are used as the basis of each structure defined by this file. + * + * Author: Chris Dearman + */ + +/* + * MIPS support + */ + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_MIPS_H__ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_MIPS_H__ + +#define MD_CONTEXT_MIPS_GPR_COUNT 32 +#define MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT 32 +#define MD_CONTEXT_MIPS_DSP_COUNT 3 + +/* + * Note that these structures *do not* map directly to the CONTEXT + * structure defined in WinNT.h in the Windows Mobile SDK. That structure + * does not accomodate VFPv3, and I'm unsure if it was ever used in the + * wild anyway, as Windows CE only seems to produce "cedumps" which + * are not exactly minidumps. + */ +typedef struct { + /* 32 64-bit floating point registers, f0..f31 */ + uint64_t regs[MD_FLOATINGSAVEAREA_MIPS_FPR_COUNT]; + + uint32_t fpcsr; /* FPU status register. */ + uint32_t fir; /* FPU implementation register. */ +} MDFloatingSaveAreaMIPS; + +typedef struct { + /* The next field determines the layout of the structure, and which parts + * of it are populated. + */ + uint32_t context_flags; + uint32_t _pad0; + + /* 32 64-bit integer registers, r0..r31. + * Note the following fixed uses: + * r29 is the stack pointer. + * r31 is the return address. + */ + uint64_t iregs[MD_CONTEXT_MIPS_GPR_COUNT]; + + /* multiply/divide result. */ + uint64_t mdhi, mdlo; + + /* DSP accumulators. */ + uint32_t hi[MD_CONTEXT_MIPS_DSP_COUNT]; + uint32_t lo[MD_CONTEXT_MIPS_DSP_COUNT]; + uint32_t dsp_control; + uint32_t _pad1; + + uint64_t epc; + uint64_t badvaddr; + uint32_t status; + uint32_t cause; + + /* The next field is included with MD_CONTEXT_MIPS_FLOATING_POINT. */ + MDFloatingSaveAreaMIPS float_save; + +} MDRawContextMIPS; + +/* Indices into iregs for registers with a dedicated or conventional + * purpose. + */ +enum MDMIPSRegisterNumbers { + MD_CONTEXT_MIPS_REG_S0 = 16, + MD_CONTEXT_MIPS_REG_S1 = 17, + MD_CONTEXT_MIPS_REG_S2 = 18, + MD_CONTEXT_MIPS_REG_S3 = 19, + MD_CONTEXT_MIPS_REG_S4 = 20, + MD_CONTEXT_MIPS_REG_S5 = 21, + MD_CONTEXT_MIPS_REG_S6 = 22, + MD_CONTEXT_MIPS_REG_S7 = 23, + MD_CONTEXT_MIPS_REG_GP = 28, + MD_CONTEXT_MIPS_REG_SP = 29, + MD_CONTEXT_MIPS_REG_FP = 30, + MD_CONTEXT_MIPS_REG_RA = 31, +}; + +/* For (MDRawContextMIPS).context_flags. These values indicate the type of + * context stored in the structure. */ +/* CONTEXT_MIPS from the Windows CE 5.0 SDK. This value isn't correct + * because this bit can be used for flags. Presumably this value was + * never actually used in minidumps, but only in "CEDumps" which + * are a whole parallel minidump file format for Windows CE. + * Therefore, Breakpad defines its own value for MIPS CPUs. + */ +#define MD_CONTEXT_MIPS 0x00040000 +#define MD_CONTEXT_MIPS_INTEGER (MD_CONTEXT_MIPS | 0x00000002) +#define MD_CONTEXT_MIPS_FLOATING_POINT (MD_CONTEXT_MIPS | 0x00000004) +#define MD_CONTEXT_MIPS_DSP (MD_CONTEXT_MIPS | 0x00000008) + +#define MD_CONTEXT_MIPS_FULL (MD_CONTEXT_MIPS_INTEGER | \ + MD_CONTEXT_MIPS_FLOATING_POINT | \ + MD_CONTEXT_MIPS_DSP) + +#define MD_CONTEXT_MIPS_ALL (MD_CONTEXT_MIPS_INTEGER | \ + MD_CONTEXT_MIPS_FLOATING_POINT \ + MD_CONTEXT_MIPS_DSP) + +/** + * Breakpad defines for MIPS64 + */ +#define MD_CONTEXT_MIPS64 0x00080000 +#define MD_CONTEXT_MIPS64_INTEGER (MD_CONTEXT_MIPS64 | 0x00000002) +#define MD_CONTEXT_MIPS64_FLOATING_POINT (MD_CONTEXT_MIPS64 | 0x00000004) +#define MD_CONTEXT_MIPS64_DSP (MD_CONTEXT_MIPS64 | 0x00000008) + +#define MD_CONTEXT_MIPS64_FULL (MD_CONTEXT_MIPS64_INTEGER | \ + MD_CONTEXT_MIPS64_FLOATING_POINT | \ + MD_CONTEXT_MIPS64_DSP) + +#define MD_CONTEXT_MIPS64_ALL (MD_CONTEXT_MIPS64_INTEGER | \ + MD_CONTEXT_MIPS64_FLOATING_POINT \ + MD_CONTEXT_MIPS64_DSP) + +#endif // GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_MIPS_H__ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_ppc.h b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_ppc.h new file mode 100644 index 000000000..b24cc4243 --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_ppc.h @@ -0,0 +1,168 @@ +/* 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. */ + +/* minidump_format.h: A cross-platform reimplementation of minidump-related + * portions of DbgHelp.h from the Windows Platform SDK. + * + * (This is C99 source, please don't corrupt it with C++.) + * + * This file contains the necessary definitions to read minidump files + * produced on ppc. These files may be read on any platform provided + * that the alignments of these structures on the processing system are + * identical to the alignments of these structures on the producing system. + * For this reason, precise-sized types are used. The structures defined + * by this file have been laid out to minimize alignment problems by ensuring + * ensuring that all members are aligned on their natural boundaries. In + * In some cases, tail-padding may be significant when different ABIs specify + * different tail-padding behaviors. To avoid problems when reading or + * writing affected structures, MD_*_SIZE macros are provided where needed, + * containing the useful size of the structures without padding. + * + * Structures that are defined by Microsoft to contain a zero-length array + * are instead defined here to contain an array with one element, as + * zero-length arrays are forbidden by standard C and C++. In these cases, + * *_minsize constants are provided to be used in place of sizeof. For a + * cleaner interface to these sizes when using C++, see minidump_size.h. + * + * These structures are also sufficient to populate minidump files. + * + * These definitions may be extended to support handling minidump files + * for other CPUs and other operating systems. + * + * Because precise data type sizes are crucial for this implementation to + * function properly and portably in terms of interoperability with minidumps + * produced by DbgHelp on Windows, a set of primitive types with known sizes + * are used as the basis of each structure defined by this file. DbgHelp + * on Windows is assumed to be the reference implementation; this file + * seeks to provide a cross-platform compatible implementation. To avoid + * collisions with the types and values defined and used by DbgHelp in the + * event that this implementation is used on Windows, each type and value + * defined here is given a new name, beginning with "MD". Names of the + * equivalent types and values in the Windows Platform SDK are given in + * comments. + * + * Author: Mark Mentovai + * Change to split into its own file: Neal Sidhwaney */ + +/* + * Breakpad minidump extension for PowerPC support. Based on Darwin/Mac OS X' + * mach/ppc/_types.h + */ + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_PPC_H__ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_PPC_H__ + +#define MD_FLOATINGSAVEAREA_PPC_FPR_COUNT 32 + +typedef struct { + /* fpregs is a double[32] in mach/ppc/_types.h, but a uint64_t is used + * here for precise sizing. */ + uint64_t fpregs[MD_FLOATINGSAVEAREA_PPC_FPR_COUNT]; + uint32_t fpscr_pad; + uint32_t fpscr; /* Status/control */ +} MDFloatingSaveAreaPPC; /* Based on ppc_float_state */ + + +#define MD_VECTORSAVEAREA_PPC_VR_COUNT 32 + +typedef struct { + /* Vector registers (including vscr) are 128 bits, but mach/ppc/_types.h + * exposes them as four 32-bit quantities. */ + uint128_struct save_vr[MD_VECTORSAVEAREA_PPC_VR_COUNT]; + uint128_struct save_vscr; /* Status/control */ + uint32_t save_pad5[4]; + uint32_t save_vrvalid; /* Indicates which vector registers are saved */ + uint32_t save_pad6[7]; +} MDVectorSaveAreaPPC; /* ppc_vector_state */ + + +#define MD_CONTEXT_PPC_GPR_COUNT 32 + +/* Use the same 32-bit alignment when accessing this structure from 64-bit code + * as is used natively in 32-bit code. #pragma pack is a MSVC extension + * supported by gcc. */ +#if defined(__SUNPRO_C) || defined(__SUNPRO_CC) +#pragma pack(4) +#else +#pragma pack(push, 4) +#endif + +typedef struct { + /* context_flags is not present in ppc_thread_state, but it aids + * identification of MDRawContextPPC among other raw context types, + * and it guarantees alignment when we get to float_save. */ + uint32_t context_flags; + + uint32_t srr0; /* Machine status save/restore: stores pc + * (instruction) */ + uint32_t srr1; /* Machine status save/restore: stores msr + * (ps, program/machine state) */ + /* ppc_thread_state contains 32 fields, r0 .. r31. Here, an array is + * used for brevity. */ + uint32_t gpr[MD_CONTEXT_PPC_GPR_COUNT]; + uint32_t cr; /* Condition */ + uint32_t xer; /* Integer (fiXed-point) exception */ + uint32_t lr; /* Link */ + uint32_t ctr; /* Count */ + uint32_t mq; /* Multiply/Quotient (PPC 601, POWER only) */ + uint32_t vrsave; /* Vector save */ + + /* float_save and vector_save aren't present in ppc_thread_state, but + * are represented in separate structures that still define a thread's + * context. */ + MDFloatingSaveAreaPPC float_save; + MDVectorSaveAreaPPC vector_save; +} MDRawContextPPC; /* Based on ppc_thread_state */ + +/* Indices into gpr for registers with a dedicated or conventional purpose. */ +enum MDPPCRegisterNumbers { + MD_CONTEXT_PPC_REG_SP = 1 +}; + +#if defined(__SUNPRO_C) || defined(__SUNPRO_CC) +#pragma pack(0) +#else +#pragma pack(pop) +#endif + +/* For (MDRawContextPPC).context_flags. These values indicate the type of + * context stored in the structure. MD_CONTEXT_PPC is Breakpad-defined. Its + * value was chosen to avoid likely conflicts with MD_CONTEXT_* for other + * CPUs. */ +#define MD_CONTEXT_PPC 0x20000000 +#define MD_CONTEXT_PPC_BASE (MD_CONTEXT_PPC | 0x00000001) +#define MD_CONTEXT_PPC_FLOATING_POINT (MD_CONTEXT_PPC | 0x00000008) +#define MD_CONTEXT_PPC_VECTOR (MD_CONTEXT_PPC | 0x00000020) + +#define MD_CONTEXT_PPC_FULL MD_CONTEXT_PPC_BASE +#define MD_CONTEXT_PPC_ALL (MD_CONTEXT_PPC_FULL | \ + MD_CONTEXT_PPC_FLOATING_POINT | \ + MD_CONTEXT_PPC_VECTOR) + +#endif /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_PPC_H__ */ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_ppc64.h b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_ppc64.h new file mode 100644 index 000000000..61f419386 --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_ppc64.h @@ -0,0 +1,134 @@ +/* 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. */ + +/* minidump_format.h: A cross-platform reimplementation of minidump-related + * portions of DbgHelp.h from the Windows Platform SDK. + * + * (This is C99 source, please don't corrupt it with C++.) + * + * This file contains the necessary definitions to read minidump files + * produced on ppc64. These files may be read on any platform provided + * that the alignments of these structures on the processing system are + * identical to the alignments of these structures on the producing system. + * For this reason, precise-sized types are used. The structures defined + * by this file have been laid out to minimize alignment problems by ensuring + * ensuring that all members are aligned on their natural boundaries. In + * In some cases, tail-padding may be significant when different ABIs specify + * different tail-padding behaviors. To avoid problems when reading or + * writing affected structures, MD_*_SIZE macros are provided where needed, + * containing the useful size of the structures without padding. + * + * Structures that are defined by Microsoft to contain a zero-length array + * are instead defined here to contain an array with one element, as + * zero-length arrays are forbidden by standard C and C++. In these cases, + * *_minsize constants are provided to be used in place of sizeof. For a + * cleaner interface to these sizes when using C++, see minidump_size.h. + * + * These structures are also sufficient to populate minidump files. + * + * These definitions may be extended to support handling minidump files + * for other CPUs and other operating systems. + * + * Because precise data type sizes are crucial for this implementation to + * function properly and portably in terms of interoperability with minidumps + * produced by DbgHelp on Windows, a set of primitive types with known sizes + * are used as the basis of each structure defined by this file. DbgHelp + * on Windows is assumed to be the reference implementation; this file + * seeks to provide a cross-platform compatible implementation. To avoid + * collisions with the types and values defined and used by DbgHelp in the + * event that this implementation is used on Windows, each type and value + * defined here is given a new name, beginning with "MD". Names of the + * equivalent types and values in the Windows Platform SDK are given in + * comments. + * + * Author: Neal Sidhwaney */ + + +/* + * Breakpad minidump extension for PPC64 support. Based on Darwin/Mac OS X' + * mach/ppc/_types.h + */ + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_PPC64_H__ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_PPC64_H__ + +#include "minidump_cpu_ppc.h" + +// these types are the same in ppc64 & ppc +typedef MDFloatingSaveAreaPPC MDFloatingSaveAreaPPC64; +typedef MDVectorSaveAreaPPC MDVectorSaveAreaPPC64; + +#define MD_CONTEXT_PPC64_GPR_COUNT MD_CONTEXT_PPC_GPR_COUNT + +typedef struct { + /* context_flags is not present in ppc_thread_state, but it aids + * identification of MDRawContextPPC among other raw context types, + * and it guarantees alignment when we get to float_save. */ + uint64_t context_flags; + + uint64_t srr0; /* Machine status save/restore: stores pc + * (instruction) */ + uint64_t srr1; /* Machine status save/restore: stores msr + * (ps, program/machine state) */ + /* ppc_thread_state contains 32 fields, r0 .. r31. Here, an array is + * used for brevity. */ + uint64_t gpr[MD_CONTEXT_PPC64_GPR_COUNT]; + uint64_t cr; /* Condition */ + uint64_t xer; /* Integer (fiXed-point) exception */ + uint64_t lr; /* Link */ + uint64_t ctr; /* Count */ + uint64_t vrsave; /* Vector save */ + + /* float_save and vector_save aren't present in ppc_thread_state, but + * are represented in separate structures that still define a thread's + * context. */ + MDFloatingSaveAreaPPC float_save; + MDVectorSaveAreaPPC vector_save; +} MDRawContextPPC64; /* Based on ppc_thread_state */ + +/* Indices into gpr for registers with a dedicated or conventional purpose. */ +enum MDPPC64RegisterNumbers { + MD_CONTEXT_PPC64_REG_SP = 1 +}; + +/* For (MDRawContextPPC).context_flags. These values indicate the type of + * context stored in the structure. MD_CONTEXT_PPC is Breakpad-defined. Its + * value was chosen to avoid likely conflicts with MD_CONTEXT_* for other + * CPUs. */ +#define MD_CONTEXT_PPC64 0x01000000 +#define MD_CONTEXT_PPC64_BASE (MD_CONTEXT_PPC64 | 0x00000001) +#define MD_CONTEXT_PPC64_FLOATING_POINT (MD_CONTEXT_PPC64 | 0x00000008) +#define MD_CONTEXT_PPC64_VECTOR (MD_CONTEXT_PPC64 | 0x00000020) + +#define MD_CONTEXT_PPC64_FULL MD_CONTEXT_PPC64_BASE +#define MD_CONTEXT_PPC64_ALL (MD_CONTEXT_PPC64_FULL | \ + MD_CONTEXT_PPC64_FLOATING_POINT | \ + MD_CONTEXT_PPC64_VECTOR) + +#endif /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_PPC64_H__ */ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_sparc.h b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_sparc.h new file mode 100644 index 000000000..95c08b174 --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_sparc.h @@ -0,0 +1,163 @@ +/* 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. */ + +/* minidump_format.h: A cross-platform reimplementation of minidump-related + * portions of DbgHelp.h from the Windows Platform SDK. + * + * (This is C99 source, please don't corrupt it with C++.) + * + * This file contains the necessary definitions to read minidump files + * produced on sparc. These files may be read on any platform provided + * that the alignments of these structures on the processing system are + * identical to the alignments of these structures on the producing system. + * For this reason, precise-sized types are used. The structures defined + * by this file have been laid out to minimize alignment problems by ensuring + * ensuring that all members are aligned on their natural boundaries. In + * In some cases, tail-padding may be significant when different ABIs specify + * different tail-padding behaviors. To avoid problems when reading or + * writing affected structures, MD_*_SIZE macros are provided where needed, + * containing the useful size of the structures without padding. + * + * Structures that are defined by Microsoft to contain a zero-length array + * are instead defined here to contain an array with one element, as + * zero-length arrays are forbidden by standard C and C++. In these cases, + * *_minsize constants are provided to be used in place of sizeof. For a + * cleaner interface to these sizes when using C++, see minidump_size.h. + * + * These structures are also sufficient to populate minidump files. + * + * These definitions may be extended to support handling minidump files + * for other CPUs and other operating systems. + * + * Because precise data type sizes are crucial for this implementation to + * function properly and portably in terms of interoperability with minidumps + * produced by DbgHelp on Windows, a set of primitive types with known sizes + * are used as the basis of each structure defined by this file. DbgHelp + * on Windows is assumed to be the reference implementation; this file + * seeks to provide a cross-platform compatible implementation. To avoid + * collisions with the types and values defined and used by DbgHelp in the + * event that this implementation is used on Windows, each type and value + * defined here is given a new name, beginning with "MD". Names of the + * equivalent types and values in the Windows Platform SDK are given in + * comments. + * + * Author: Mark Mentovai + * Change to split into its own file: Neal Sidhwaney */ + +/* + * SPARC support, see (solaris)sys/procfs_isa.h also + */ + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_SPARC_H__ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_SPARC_H__ + +#define MD_FLOATINGSAVEAREA_SPARC_FPR_COUNT 32 + +typedef struct { + + /* FPU floating point regs */ + uint64_t regs[MD_FLOATINGSAVEAREA_SPARC_FPR_COUNT]; + + uint64_t filler; + uint64_t fsr; /* FPU status register */ +} MDFloatingSaveAreaSPARC; /* FLOATING_SAVE_AREA */ + +#define MD_CONTEXT_SPARC_GPR_COUNT 32 + +typedef struct { + /* The next field determines the layout of the structure, and which parts + * of it are populated + */ + uint32_t context_flags; + uint32_t flag_pad; + /* + * General register access (SPARC). + * Don't confuse definitions here with definitions in . + * Registers are 32 bits for ILP32, 64 bits for LP64. + * SPARC V7/V8 is for 32bit, SPARC V9 is for 64bit + */ + + /* 32 Integer working registers */ + + /* g_r[0-7] global registers(g0-g7) + * g_r[8-15] out registers(o0-o7) + * g_r[16-23] local registers(l0-l7) + * g_r[24-31] in registers(i0-i7) + */ + uint64_t g_r[MD_CONTEXT_SPARC_GPR_COUNT]; + + /* several control registers */ + + /* Processor State register(PSR) for SPARC V7/V8 + * Condition Code register (CCR) for SPARC V9 + */ + uint64_t ccr; + + uint64_t pc; /* Program Counter register (PC) */ + uint64_t npc; /* Next Program Counter register (nPC) */ + uint64_t y; /* Y register (Y) */ + + /* Address Space Identifier register (ASI) for SPARC V9 + * WIM for SPARC V7/V8 + */ + uint64_t asi; + + /* Floating-Point Registers State register (FPRS) for SPARC V9 + * TBR for for SPARC V7/V8 + */ + uint64_t fprs; + + /* The next field is included with MD_CONTEXT_SPARC_FLOATING_POINT */ + MDFloatingSaveAreaSPARC float_save; + +} MDRawContextSPARC; /* CONTEXT_SPARC */ + +/* Indices into g_r for registers with a dedicated or conventional purpose. */ +enum MDSPARCRegisterNumbers { + MD_CONTEXT_SPARC_REG_SP = 14 +}; + +/* For (MDRawContextSPARC).context_flags. These values indicate the type of + * context stored in the structure. MD_CONTEXT_SPARC is Breakpad-defined. Its + * value was chosen to avoid likely conflicts with MD_CONTEXT_* for other + * CPUs. */ +#define MD_CONTEXT_SPARC 0x10000000 +#define MD_CONTEXT_SPARC_CONTROL (MD_CONTEXT_SPARC | 0x00000001) +#define MD_CONTEXT_SPARC_INTEGER (MD_CONTEXT_SPARC | 0x00000002) +#define MD_CONTEXT_SAPARC_FLOATING_POINT (MD_CONTEXT_SPARC | 0x00000004) +#define MD_CONTEXT_SAPARC_EXTRA (MD_CONTEXT_SPARC | 0x00000008) + +#define MD_CONTEXT_SPARC_FULL (MD_CONTEXT_SPARC_CONTROL | \ + MD_CONTEXT_SPARC_INTEGER) + +#define MD_CONTEXT_SPARC_ALL (MD_CONTEXT_SPARC_FULL | \ + MD_CONTEXT_SAPARC_FLOATING_POINT | \ + MD_CONTEXT_SAPARC_EXTRA) + +#endif /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_SPARC_H__ */ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_x86.h b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_x86.h new file mode 100644 index 000000000..e09cb7cb5 --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_cpu_x86.h @@ -0,0 +1,174 @@ +/* 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. */ + +/* minidump_format.h: A cross-platform reimplementation of minidump-related + * portions of DbgHelp.h from the Windows Platform SDK. + * + * (This is C99 source, please don't corrupt it with C++.) + * + * This file contains the necessary definitions to read minidump files + * produced on x86. These files may be read on any platform provided + * that the alignments of these structures on the processing system are + * identical to the alignments of these structures on the producing system. + * For this reason, precise-sized types are used. The structures defined + * by this file have been laid out to minimize alignment problems by ensuring + * ensuring that all members are aligned on their natural boundaries. In + * In some cases, tail-padding may be significant when different ABIs specify + * different tail-padding behaviors. To avoid problems when reading or + * writing affected structures, MD_*_SIZE macros are provided where needed, + * containing the useful size of the structures without padding. + * + * Structures that are defined by Microsoft to contain a zero-length array + * are instead defined here to contain an array with one element, as + * zero-length arrays are forbidden by standard C and C++. In these cases, + * *_minsize constants are provided to be used in place of sizeof. For a + * cleaner interface to these sizes when using C++, see minidump_size.h. + * + * These structures are also sufficient to populate minidump files. + * + * These definitions may be extended to support handling minidump files + * for other CPUs and other operating systems. + * + * Because precise data type sizes are crucial for this implementation to + * function properly and portably in terms of interoperability with minidumps + * produced by DbgHelp on Windows, a set of primitive types with known sizes + * are used as the basis of each structure defined by this file. DbgHelp + * on Windows is assumed to be the reference implementation; this file + * seeks to provide a cross-platform compatible implementation. To avoid + * collisions with the types and values defined and used by DbgHelp in the + * event that this implementation is used on Windows, each type and value + * defined here is given a new name, beginning with "MD". Names of the + * equivalent types and values in the Windows Platform SDK are given in + * comments. + * + * Author: Mark Mentovai */ + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_X86_H__ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_X86_H__ + +#define MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE 80 + /* SIZE_OF_80387_REGISTERS */ + +typedef struct { + uint32_t control_word; + uint32_t status_word; + uint32_t tag_word; + uint32_t error_offset; + uint32_t error_selector; + uint32_t data_offset; + uint32_t data_selector; + + /* register_area contains eight 80-bit (x87 "long double") quantities for + * floating-point registers %st0 (%mm0) through %st7 (%mm7). */ + uint8_t register_area[MD_FLOATINGSAVEAREA_X86_REGISTERAREA_SIZE]; + uint32_t cr0_npx_state; +} MDFloatingSaveAreaX86; /* FLOATING_SAVE_AREA */ + + +#define MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE 512 + /* MAXIMUM_SUPPORTED_EXTENSION */ + +typedef struct { + /* The next field determines the layout of the structure, and which parts + * of it are populated */ + uint32_t context_flags; + + /* The next 6 registers are included with MD_CONTEXT_X86_DEBUG_REGISTERS */ + uint32_t dr0; + uint32_t dr1; + uint32_t dr2; + uint32_t dr3; + uint32_t dr6; + uint32_t dr7; + + /* The next field is included with MD_CONTEXT_X86_FLOATING_POINT */ + MDFloatingSaveAreaX86 float_save; + + /* The next 4 registers are included with MD_CONTEXT_X86_SEGMENTS */ + uint32_t gs; + uint32_t fs; + uint32_t es; + uint32_t ds; + /* The next 6 registers are included with MD_CONTEXT_X86_INTEGER */ + uint32_t edi; + uint32_t esi; + uint32_t ebx; + uint32_t edx; + uint32_t ecx; + uint32_t eax; + + /* The next 6 registers are included with MD_CONTEXT_X86_CONTROL */ + uint32_t ebp; + uint32_t eip; + uint32_t cs; /* WinNT.h says "must be sanitized" */ + uint32_t eflags; /* WinNT.h says "must be sanitized" */ + uint32_t esp; + uint32_t ss; + + /* The next field is included with MD_CONTEXT_X86_EXTENDED_REGISTERS. + * It contains vector (MMX/SSE) registers. It it laid out in the + * format used by the fxsave and fsrstor instructions, so it includes + * a copy of the x87 floating-point registers as well. See FXSAVE in + * "Intel Architecture Software Developer's Manual, Volume 2." */ + uint8_t extended_registers[ + MD_CONTEXT_X86_EXTENDED_REGISTERS_SIZE]; +} MDRawContextX86; /* CONTEXT */ + +/* For (MDRawContextX86).context_flags. These values indicate the type of + * context stored in the structure. The high 24 bits identify the CPU, the + * low 8 bits identify the type of context saved. */ +#define MD_CONTEXT_X86 0x00010000 + /* CONTEXT_i386, CONTEXT_i486: identifies CPU */ +#define MD_CONTEXT_X86_CONTROL (MD_CONTEXT_X86 | 0x00000001) + /* CONTEXT_CONTROL */ +#define MD_CONTEXT_X86_INTEGER (MD_CONTEXT_X86 | 0x00000002) + /* CONTEXT_INTEGER */ +#define MD_CONTEXT_X86_SEGMENTS (MD_CONTEXT_X86 | 0x00000004) + /* CONTEXT_SEGMENTS */ +#define MD_CONTEXT_X86_FLOATING_POINT (MD_CONTEXT_X86 | 0x00000008) + /* CONTEXT_FLOATING_POINT */ +#define MD_CONTEXT_X86_DEBUG_REGISTERS (MD_CONTEXT_X86 | 0x00000010) + /* CONTEXT_DEBUG_REGISTERS */ +#define MD_CONTEXT_X86_EXTENDED_REGISTERS (MD_CONTEXT_X86 | 0x00000020) + /* CONTEXT_EXTENDED_REGISTERS */ +#define MD_CONTEXT_X86_XSTATE (MD_CONTEXT_X86 | 0x00000040) + /* CONTEXT_XSTATE */ + +#define MD_CONTEXT_X86_FULL (MD_CONTEXT_X86_CONTROL | \ + MD_CONTEXT_X86_INTEGER | \ + MD_CONTEXT_X86_SEGMENTS) + /* CONTEXT_FULL */ + +#define MD_CONTEXT_X86_ALL (MD_CONTEXT_X86_FULL | \ + MD_CONTEXT_X86_FLOATING_POINT | \ + MD_CONTEXT_X86_DEBUG_REGISTERS | \ + MD_CONTEXT_X86_EXTENDED_REGISTERS) + /* CONTEXT_ALL */ + +#endif /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_CPU_X86_H__ */ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_exception_fuchsia.h b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_exception_fuchsia.h new file mode 100644 index 000000000..f26a8a2ae --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_exception_fuchsia.h @@ -0,0 +1,58 @@ +/* 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. */ + +/* minidump_exception_fuchsia.h: A definition of exception codes for Fuchsia. + * + * Author: Ivan Penkov */ + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_FUCHSIA_H_ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_FUCHSIA_H_ + +#include + +#include "google_breakpad/common/breakpad_types.h" + +// Based on zircon/system/public/zircon/syscalls/exception.h +typedef enum { + // Architectural exceptions + MD_EXCEPTION_CODE_FUCHSIA_GENERAL = 0x8, + MD_EXCEPTION_CODE_FUCHSIA_FATAL_PAGE_FAULT = 0x108, + MD_EXCEPTION_CODE_FUCHSIA_UNDEFINED_INSTRUCTION = 0x208, + MD_EXCEPTION_CODE_FUCHSIA_SW_BREAKPOINT = 0x308, + MD_EXCEPTION_CODE_FUCHSIA_HW_BREAKPOINT = 0x408, + MD_EXCEPTION_CODE_FUCHSIA_UNALIGNED_ACCESS = 0x508, + // + // Synthetic exceptions + MD_EXCEPTION_CODE_FUCHSIA_THREAD_STARTING = 0x8008, + MD_EXCEPTION_CODE_FUCHSIA_THREAD_EXITING = 0x8108, + MD_EXCEPTION_CODE_FUCHSIA_POLICY_ERROR = 0x8208, + MD_EXCEPTION_CODE_FUCHSIA_PROCESS_STARTING = 0x8308, +} MDExceptionCodeFuchsia; + +#endif // GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_FUCHSIA_H_ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_exception_linux.h b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_exception_linux.h new file mode 100644 index 000000000..84dd9e169 --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_exception_linux.h @@ -0,0 +1,170 @@ +/* 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. */ + +/* minidump_exception_linux.h: A definition of exception codes for + * Linux + * + * (This is C99 source, please don't corrupt it with C++.) + * + * Author: Mark Mentovai + * Split into its own file: Neal Sidhwaney */ + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_LINUX_H__ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_LINUX_H__ + +#include + +#include "google_breakpad/common/breakpad_types.h" + +#if defined(__unix__) || defined(__linux__) || defined(__APPLE__) +#include +#else +#define SIGHUP 1 +#define SIGINT 2 +#define SIGQUIT 3 +#define SIGILL 4 +#define SIGTRAP 5 +#define SIGABRT 6 +#define SIGBUS 7 +#define SIGFPE 8 +#define SIGKILL 9 +#define SIGUSR1 10 +#define SIGSEGV 11 +#define SIGUSR2 12 +#define SIGPIPE 13 +#define SIGALRM 14 +#define SIGTERM 15 +#define SIGSTKFLT 16 +#define SIGCHLD 17 +#define SIGCONT 18 +#define SIGSTOP 19 +#define SIGTSTP 20 +#define SIGTTIN 21 +#define SIGTTOU 22 +#define SIGURG 23 +#define SIGXCPU 24 +#define SIGXFSZ 25 +#define SIGVTALRM 26 +#define SIGPROF 27 +#define SIGWINCH 28 +#define SIGIO 29 +#define SIGPWR 30 +#define SIGSYS 31 +#endif + +#ifndef SIGSTKFLT +#define SIGSTKFLT 990 /* 16 on x64 */ +#endif +#ifndef SIGPWR +#define SIGPWR 991 /* 30 on x64 */ +#endif + +/* For (MDException).exception_code. These values come from bits/signum.h. + */ +typedef enum { + MD_EXCEPTION_CODE_LIN_SIGHUP = SIGHUP, /* Hangup (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGINT = SIGINT, /* Interrupt (ANSI) */ + MD_EXCEPTION_CODE_LIN_SIGQUIT = SIGQUIT, /* Quit (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGILL = SIGILL, /* Illegal instruction (ANSI) */ + MD_EXCEPTION_CODE_LIN_SIGTRAP = SIGTRAP, /* Trace trap (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGABRT = SIGABRT, /* Abort (ANSI) */ + MD_EXCEPTION_CODE_LIN_SIGBUS = SIGBUS, /* BUS error (4.2 BSD) */ + MD_EXCEPTION_CODE_LIN_SIGFPE = SIGFPE, /* Floating-point exception (ANSI) */ + MD_EXCEPTION_CODE_LIN_SIGKILL = SIGKILL, /* Kill, unblockable (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGUSR1 = SIGUSR1, /* User-defined signal 1 (POSIX). */ + MD_EXCEPTION_CODE_LIN_SIGSEGV = SIGSEGV, /* Segmentation violation (ANSI) */ + MD_EXCEPTION_CODE_LIN_SIGUSR2 = SIGUSR2, /* User-defined signal 2 (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGPIPE = SIGPIPE, /* Broken pipe (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGALRM = SIGALRM, /* Alarm clock (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGTERM = SIGTERM, /* Termination (ANSI) */ + MD_EXCEPTION_CODE_LIN_SIGSTKFLT = SIGSTKFLT, /* Stack faultd */ + MD_EXCEPTION_CODE_LIN_SIGCHLD = + SIGCHLD, /* Child status has changed (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGCONT = SIGCONT, /* Continue (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGSTOP = SIGSTOP, /* Stop, unblockable (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGTSTP = SIGTSTP, /* Keyboard stop (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGTTIN = + SIGTTIN, /* Background read from tty (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGTTOU = SIGTTOU, /* Background write to tty (POSIX) */ + MD_EXCEPTION_CODE_LIN_SIGURG = SIGURG, + /* Urgent condition on socket (4.2 BSD) */ + MD_EXCEPTION_CODE_LIN_SIGXCPU = SIGXCPU, /* CPU limit exceeded (4.2 BSD) */ + MD_EXCEPTION_CODE_LIN_SIGXFSZ = SIGXFSZ, + /* File size limit exceeded (4.2 BSD) */ + MD_EXCEPTION_CODE_LIN_SIGVTALRM = + SIGVTALRM, /* Virtual alarm clock (4.2 BSD) */ + MD_EXCEPTION_CODE_LIN_SIGPROF = SIGPROF, /* Profiling alarm clock (4.2 BSD) */ + MD_EXCEPTION_CODE_LIN_SIGWINCH = + SIGWINCH, /* Window size change (4.3 BSD, Sun) */ + MD_EXCEPTION_CODE_LIN_SIGIO = SIGIO, /* I/O now possible (4.2 BSD) */ + MD_EXCEPTION_CODE_LIN_SIGPWR = SIGPWR, /* Power failure restart (System V) */ + MD_EXCEPTION_CODE_LIN_SIGSYS = SIGSYS, /* Bad system call */ + MD_EXCEPTION_CODE_LIN_DUMP_REQUESTED = 0xFFFFFFFF /* No exception, + dump requested. */ +} MDExceptionCodeLinux; + +/* For (MDException).exception_flags. These values come from + * asm-generic/siginfo.h. + */ +typedef enum { + /* SIGILL */ + MD_EXCEPTION_FLAG_LIN_ILL_ILLOPC = 1, + MD_EXCEPTION_FLAG_LIN_ILL_ILLOPN = 2, + MD_EXCEPTION_FLAG_LIN_ILL_ILLADR = 3, + MD_EXCEPTION_FLAG_LIN_ILL_ILLTRP = 4, + MD_EXCEPTION_FLAG_LIN_ILL_PRVOPC = 5, + MD_EXCEPTION_FLAG_LIN_ILL_PRVREG = 6, + MD_EXCEPTION_FLAG_LIN_ILL_COPROC = 7, + MD_EXCEPTION_FLAG_LIN_ILL_BADSTK = 8, + + /* SIGFPE */ + MD_EXCEPTION_FLAG_LIN_FPE_INTDIV = 1, + MD_EXCEPTION_FLAG_LIN_FPE_INTOVF = 2, + MD_EXCEPTION_FLAG_LIN_FPE_FLTDIV = 3, + MD_EXCEPTION_FLAG_LIN_FPE_FLTOVF = 4, + MD_EXCEPTION_FLAG_LIN_FPE_FLTUND = 5, + MD_EXCEPTION_FLAG_LIN_FPE_FLTRES = 6, + MD_EXCEPTION_FLAG_LIN_FPE_FLTINV = 7, + MD_EXCEPTION_FLAG_LIN_FPE_FLTSUB = 8, + + /* SIGSEGV */ + MD_EXCEPTION_FLAG_LIN_SEGV_MAPERR = 1, + MD_EXCEPTION_FLAG_LIN_SEGV_ACCERR = 2, + MD_EXCEPTION_FLAG_LIN_SEGV_BNDERR = 3, + MD_EXCEPTION_FLAG_LIN_SEGV_PKUERR = 4, + + /* SIGBUS */ + MD_EXCEPTION_FLAG_LIN_BUS_ADRALN = 1, + MD_EXCEPTION_FLAG_LIN_BUS_ADRERR = 2, + MD_EXCEPTION_FLAG_LIN_BUS_OBJERR = 3, + MD_EXCEPTION_FLAG_LIN_BUS_MCEERR_AR = 4, + MD_EXCEPTION_FLAG_LIN_BUS_MCEERR_AO = 5, +} MDExceptionFlagLinux; + +#endif /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_LINUX_H__ */ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_exception_mac.h b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_exception_mac.h new file mode 100644 index 000000000..e53edc5d5 --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_exception_mac.h @@ -0,0 +1,211 @@ +/* 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. */ + +/* minidump_exception_mac.h: A definition of exception codes for Mac + * OS X + * + * (This is C99 source, please don't corrupt it with C++.) + * + * Author: Mark Mentovai + * Split into its own file: Neal Sidhwaney */ + + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_MAC_H__ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_MAC_H__ + +#include + +#include "google_breakpad/common/breakpad_types.h" + +/* For (MDException).exception_code. Breakpad minidump extension for Mac OS X + * support. Based on Darwin/Mac OS X' mach/exception_types.h. This is + * what Mac OS X calls an "exception", not a "code". */ +typedef enum { + /* Exception code. The high 16 bits of exception_code contains one of + * these values. */ + MD_EXCEPTION_MAC_BAD_ACCESS = 1, /* code can be a kern_return_t */ + /* EXC_BAD_ACCESS */ + MD_EXCEPTION_MAC_BAD_INSTRUCTION = 2, /* code is CPU-specific */ + /* EXC_BAD_INSTRUCTION */ + MD_EXCEPTION_MAC_ARITHMETIC = 3, /* code is CPU-specific */ + /* EXC_ARITHMETIC */ + MD_EXCEPTION_MAC_EMULATION = 4, /* code is CPU-specific */ + /* EXC_EMULATION */ + MD_EXCEPTION_MAC_SOFTWARE = 5, + /* EXC_SOFTWARE */ + MD_EXCEPTION_MAC_BREAKPOINT = 6, /* code is CPU-specific */ + /* EXC_BREAKPOINT */ + MD_EXCEPTION_MAC_SYSCALL = 7, + /* EXC_SYSCALL */ + MD_EXCEPTION_MAC_MACH_SYSCALL = 8, + /* EXC_MACH_SYSCALL */ + MD_EXCEPTION_MAC_RPC_ALERT = 9, + /* EXC_RPC_ALERT */ + MD_EXCEPTION_MAC_SIMULATED = 0x43507378, + /* Fake exception code used by Crashpad's SimulateCrash ('CPsx'). */ + MD_NS_EXCEPTION_SIMULATED = 0x43506E78 + /* Fake exception code used by Crashpad's uncaught exceptions ('CPnx'). */ +} MDExceptionMac; + +/* For (MDException).exception_flags. Breakpad minidump extension for Mac OS X + * support. Based on Darwin/Mac OS X' mach/ppc/exception.h and + * mach/i386/exception.h. This is what Mac OS X calls a "code". */ +typedef enum { + /* With MD_EXCEPTION_BAD_ACCESS. These are relevant kern_return_t values + * from mach/kern_return.h. */ + MD_EXCEPTION_CODE_MAC_INVALID_ADDRESS = 1, + /* KERN_INVALID_ADDRESS */ + MD_EXCEPTION_CODE_MAC_PROTECTION_FAILURE = 2, + /* KERN_PROTECTION_FAILURE */ + MD_EXCEPTION_CODE_MAC_NO_ACCESS = 8, + /* KERN_NO_ACCESS */ + MD_EXCEPTION_CODE_MAC_MEMORY_FAILURE = 9, + /* KERN_MEMORY_FAILURE */ + MD_EXCEPTION_CODE_MAC_MEMORY_ERROR = 10, + /* KERN_MEMORY_ERROR */ + MD_EXCEPTION_CODE_MAC_CODESIGN_ERROR = 50, + /* KERN_CODESIGN_ERROR */ + + /* With MD_EXCEPTION_SOFTWARE */ + MD_EXCEPTION_CODE_MAC_BAD_SYSCALL = 0x00010000, /* Mach SIGSYS */ + MD_EXCEPTION_CODE_MAC_BAD_PIPE = 0x00010001, /* Mach SIGPIPE */ + MD_EXCEPTION_CODE_MAC_ABORT = 0x00010002, /* Mach SIGABRT */ + /* Custom values */ + MD_EXCEPTION_CODE_MAC_NS_EXCEPTION = 0xDEADC0DE, /* uncaught NSException */ + + /* With MD_EXCEPTION_MAC_BAD_ACCESS on arm */ + MD_EXCEPTION_CODE_MAC_ARM_DA_ALIGN = 0x0101, /* EXC_ARM_DA_ALIGN */ + MD_EXCEPTION_CODE_MAC_ARM_DA_DEBUG = 0x0102, /* EXC_ARM_DA_DEBUG */ + + /* With MD_EXCEPTION_MAC_BAD_INSTRUCTION on arm */ + MD_EXCEPTION_CODE_MAC_ARM_UNDEFINED = 1, /* EXC_ARM_UNDEFINED */ + + /* With MD_EXCEPTION_MAC_BREAKPOINT on arm */ + MD_EXCEPTION_CODE_MAC_ARM_BREAKPOINT = 1, /* EXC_ARM_BREAKPOINT */ + + /* With MD_EXCEPTION_MAC_BAD_ACCESS on ppc */ + MD_EXCEPTION_CODE_MAC_PPC_VM_PROT_READ = 0x0101, + /* EXC_PPC_VM_PROT_READ */ + MD_EXCEPTION_CODE_MAC_PPC_BADSPACE = 0x0102, + /* EXC_PPC_BADSPACE */ + MD_EXCEPTION_CODE_MAC_PPC_UNALIGNED = 0x0103, + /* EXC_PPC_UNALIGNED */ + + /* With MD_EXCEPTION_MAC_BAD_INSTRUCTION on ppc */ + MD_EXCEPTION_CODE_MAC_PPC_INVALID_SYSCALL = 1, + /* EXC_PPC_INVALID_SYSCALL */ + MD_EXCEPTION_CODE_MAC_PPC_UNIMPLEMENTED_INSTRUCTION = 2, + /* EXC_PPC_UNIPL_INST */ + MD_EXCEPTION_CODE_MAC_PPC_PRIVILEGED_INSTRUCTION = 3, + /* EXC_PPC_PRIVINST */ + MD_EXCEPTION_CODE_MAC_PPC_PRIVILEGED_REGISTER = 4, + /* EXC_PPC_PRIVREG */ + MD_EXCEPTION_CODE_MAC_PPC_TRACE = 5, + /* EXC_PPC_TRACE */ + MD_EXCEPTION_CODE_MAC_PPC_PERFORMANCE_MONITOR = 6, + /* EXC_PPC_PERFMON */ + + /* With MD_EXCEPTION_MAC_ARITHMETIC on ppc */ + MD_EXCEPTION_CODE_MAC_PPC_OVERFLOW = 1, + /* EXC_PPC_OVERFLOW */ + MD_EXCEPTION_CODE_MAC_PPC_ZERO_DIVIDE = 2, + /* EXC_PPC_ZERO_DIVIDE */ + MD_EXCEPTION_CODE_MAC_PPC_FLOAT_INEXACT = 3, + /* EXC_FLT_INEXACT */ + MD_EXCEPTION_CODE_MAC_PPC_FLOAT_ZERO_DIVIDE = 4, + /* EXC_PPC_FLT_ZERO_DIVIDE */ + MD_EXCEPTION_CODE_MAC_PPC_FLOAT_UNDERFLOW = 5, + /* EXC_PPC_FLT_UNDERFLOW */ + MD_EXCEPTION_CODE_MAC_PPC_FLOAT_OVERFLOW = 6, + /* EXC_PPC_FLT_OVERFLOW */ + MD_EXCEPTION_CODE_MAC_PPC_FLOAT_NOT_A_NUMBER = 7, + /* EXC_PPC_FLT_NOT_A_NUMBER */ + + /* With MD_EXCEPTION_MAC_EMULATION on ppc */ + MD_EXCEPTION_CODE_MAC_PPC_NO_EMULATION = 8, + /* EXC_PPC_NOEMULATION */ + MD_EXCEPTION_CODE_MAC_PPC_ALTIVEC_ASSIST = 9, + /* EXC_PPC_ALTIVECASSIST */ + + /* With MD_EXCEPTION_MAC_SOFTWARE on ppc */ + MD_EXCEPTION_CODE_MAC_PPC_TRAP = 0x00000001, /* EXC_PPC_TRAP */ + MD_EXCEPTION_CODE_MAC_PPC_MIGRATE = 0x00010100, /* EXC_PPC_MIGRATE */ + + /* With MD_EXCEPTION_MAC_BREAKPOINT on ppc */ + MD_EXCEPTION_CODE_MAC_PPC_BREAKPOINT = 1, /* EXC_PPC_BREAKPOINT */ + + /* With MD_EXCEPTION_MAC_BAD_INSTRUCTION on x86, see also x86 interrupt + * values below. */ + MD_EXCEPTION_CODE_MAC_X86_INVALID_OPERATION = 1, /* EXC_I386_INVOP */ + + /* With MD_EXCEPTION_MAC_ARITHMETIC on x86 */ + MD_EXCEPTION_CODE_MAC_X86_DIV = 1, /* EXC_I386_DIV */ + MD_EXCEPTION_CODE_MAC_X86_INTO = 2, /* EXC_I386_INTO */ + MD_EXCEPTION_CODE_MAC_X86_NOEXT = 3, /* EXC_I386_NOEXT */ + MD_EXCEPTION_CODE_MAC_X86_EXTOVR = 4, /* EXC_I386_EXTOVR */ + MD_EXCEPTION_CODE_MAC_X86_EXTERR = 5, /* EXC_I386_EXTERR */ + MD_EXCEPTION_CODE_MAC_X86_EMERR = 6, /* EXC_I386_EMERR */ + MD_EXCEPTION_CODE_MAC_X86_BOUND = 7, /* EXC_I386_BOUND */ + MD_EXCEPTION_CODE_MAC_X86_SSEEXTERR = 8, /* EXC_I386_SSEEXTERR */ + + /* With MD_EXCEPTION_MAC_BREAKPOINT on x86 */ + MD_EXCEPTION_CODE_MAC_X86_SGL = 1, /* EXC_I386_SGL */ + MD_EXCEPTION_CODE_MAC_X86_BPT = 2, /* EXC_I386_BPT */ + + /* With MD_EXCEPTION_MAC_BAD_INSTRUCTION on x86. These are the raw + * x86 interrupt codes. Most of these are mapped to other Mach + * exceptions and codes, are handled, or should not occur in user space. + * A few of these will do occur with MD_EXCEPTION_MAC_BAD_INSTRUCTION. */ + /* EXC_I386_DIVERR = 0: mapped to EXC_ARITHMETIC/EXC_I386_DIV */ + /* EXC_I386_SGLSTP = 1: mapped to EXC_BREAKPOINT/EXC_I386_SGL */ + /* EXC_I386_NMIFLT = 2: should not occur in user space */ + /* EXC_I386_BPTFLT = 3: mapped to EXC_BREAKPOINT/EXC_I386_BPT */ + /* EXC_I386_INTOFLT = 4: mapped to EXC_ARITHMETIC/EXC_I386_INTO */ + /* EXC_I386_BOUNDFLT = 5: mapped to EXC_ARITHMETIC/EXC_I386_BOUND */ + /* EXC_I386_INVOPFLT = 6: mapped to EXC_BAD_INSTRUCTION/EXC_I386_INVOP */ + /* EXC_I386_NOEXTFLT = 7: should be handled by the kernel */ + /* EXC_I386_DBLFLT = 8: should be handled (if possible) by the kernel */ + /* EXC_I386_EXTOVRFLT = 9: mapped to EXC_BAD_ACCESS/(PROT_READ|PROT_EXEC) */ + MD_EXCEPTION_CODE_MAC_X86_INVALID_TASK_STATE_SEGMENT = 10, + /* EXC_INVTSSFLT */ + MD_EXCEPTION_CODE_MAC_X86_SEGMENT_NOT_PRESENT = 11, + /* EXC_SEGNPFLT */ + MD_EXCEPTION_CODE_MAC_X86_STACK_FAULT = 12, + /* EXC_STKFLT */ + MD_EXCEPTION_CODE_MAC_X86_GENERAL_PROTECTION_FAULT = 13, + /* EXC_GPFLT */ + /* EXC_I386_PGFLT = 14: should not occur in user space */ + /* EXC_I386_EXTERRFLT = 16: mapped to EXC_ARITHMETIC/EXC_I386_EXTERR */ + MD_EXCEPTION_CODE_MAC_X86_ALIGNMENT_FAULT = 17 + /* EXC_ALIGNFLT (for vector operations) */ + /* EXC_I386_ENOEXTFLT = 32: should be handled by the kernel */ + /* EXC_I386_ENDPERR = 33: should not occur */ +} MDExceptionCodeMac; + +#endif /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_MAC_OSX_H__ */ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_exception_ps3.h b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_exception_ps3.h new file mode 100644 index 000000000..adff5a6bb --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_exception_ps3.h @@ -0,0 +1,67 @@ +/* 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. */ + +/* minidump_exception_ps3.h: A definition of exception codes for + * PS3 */ + + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_PS3_H__ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_PS3_H__ + +#include + +#include "google_breakpad/common/breakpad_types.h" + +typedef enum { + MD_EXCEPTION_CODE_PS3_UNKNOWN = 0, + MD_EXCEPTION_CODE_PS3_TRAP_EXCEP = 1, + MD_EXCEPTION_CODE_PS3_PRIV_INSTR = 2, + MD_EXCEPTION_CODE_PS3_ILLEGAL_INSTR = 3, + MD_EXCEPTION_CODE_PS3_INSTR_STORAGE = 4, + MD_EXCEPTION_CODE_PS3_INSTR_SEGMENT = 5, + MD_EXCEPTION_CODE_PS3_DATA_STORAGE = 6, + MD_EXCEPTION_CODE_PS3_DATA_SEGMENT = 7, + MD_EXCEPTION_CODE_PS3_FLOAT_POINT = 8, + MD_EXCEPTION_CODE_PS3_DABR_MATCH = 9, + MD_EXCEPTION_CODE_PS3_ALIGN_EXCEP = 10, + MD_EXCEPTION_CODE_PS3_MEMORY_ACCESS = 11, + MD_EXCEPTION_CODE_PS3_COPRO_ALIGN = 12, + MD_EXCEPTION_CODE_PS3_COPRO_INVALID_COM = 13, + MD_EXCEPTION_CODE_PS3_COPRO_ERR = 14, + MD_EXCEPTION_CODE_PS3_COPRO_FIR = 15, + MD_EXCEPTION_CODE_PS3_COPRO_DATA_SEGMENT = 16, + MD_EXCEPTION_CODE_PS3_COPRO_DATA_STORAGE = 17, + MD_EXCEPTION_CODE_PS3_COPRO_STOP_INSTR = 18, + MD_EXCEPTION_CODE_PS3_COPRO_HALT_INSTR = 19, + MD_EXCEPTION_CODE_PS3_COPRO_HALTINST_UNKNOWN = 20, + MD_EXCEPTION_CODE_PS3_COPRO_MEMORY_ACCESS = 21, + MD_EXCEPTION_CODE_PS3_GRAPHIC = 22 +} MDExceptionCodePS3; + +#endif /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_PS3_H__ */ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_exception_solaris.h b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_exception_solaris.h new file mode 100644 index 000000000..f18ddf424 --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_exception_solaris.h @@ -0,0 +1,94 @@ +/* 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. */ + +/* minidump_exception_solaris.h: A definition of exception codes for + * Solaris + * + * (This is C99 source, please don't corrupt it with C++.) + * + * Author: Mark Mentovai + * Split into its own file: Neal Sidhwaney */ + + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_SOLARIS_H__ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_SOLARIS_H__ + +#include + +#include "google_breakpad/common/breakpad_types.h" + +/* For (MDException).exception_code. These values come from sys/iso/signal_iso.h + */ +typedef enum { + MD_EXCEPTION_CODE_SOL_SIGHUP = 1, /* Hangup */ + MD_EXCEPTION_CODE_SOL_SIGINT = 2, /* interrupt (rubout) */ + MD_EXCEPTION_CODE_SOL_SIGQUIT = 3, /* quit (ASCII FS) */ + MD_EXCEPTION_CODE_SOL_SIGILL = 4, /* illegal instruction (not reset when caught) */ + MD_EXCEPTION_CODE_SOL_SIGTRAP = 5, /* trace trap (not reset when caught) */ + MD_EXCEPTION_CODE_SOL_SIGIOT = 6, /* IOT instruction */ + MD_EXCEPTION_CODE_SOL_SIGABRT = 6, /* used by abort, replace SIGIOT in the future */ + MD_EXCEPTION_CODE_SOL_SIGEMT = 7, /* EMT instruction */ + MD_EXCEPTION_CODE_SOL_SIGFPE = 8, /* floating point exception */ + MD_EXCEPTION_CODE_SOL_SIGKILL = 9, /* kill (cannot be caught or ignored) */ + MD_EXCEPTION_CODE_SOL_SIGBUS = 10, /* bus error */ + MD_EXCEPTION_CODE_SOL_SIGSEGV = 11, /* segmentation violation */ + MD_EXCEPTION_CODE_SOL_SIGSYS = 12, /* bad argument to system call */ + MD_EXCEPTION_CODE_SOL_SIGPIPE = 13, /* write on a pipe with no one to read it */ + MD_EXCEPTION_CODE_SOL_SIGALRM = 14, /* alarm clock */ + MD_EXCEPTION_CODE_SOL_SIGTERM = 15, /* software termination signal from kill */ + MD_EXCEPTION_CODE_SOL_SIGUSR1 = 16, /* user defined signal 1 */ + MD_EXCEPTION_CODE_SOL_SIGUSR2 = 17, /* user defined signal 2 */ + MD_EXCEPTION_CODE_SOL_SIGCLD = 18, /* child status change */ + MD_EXCEPTION_CODE_SOL_SIGCHLD = 18, /* child status change alias (POSIX) */ + MD_EXCEPTION_CODE_SOL_SIGPWR = 19, /* power-fail restart */ + MD_EXCEPTION_CODE_SOL_SIGWINCH = 20, /* window size change */ + MD_EXCEPTION_CODE_SOL_SIGURG = 21, /* urgent socket condition */ + MD_EXCEPTION_CODE_SOL_SIGPOLL = 22, /* pollable event occurred */ + MD_EXCEPTION_CODE_SOL_SIGIO = 22, /* socket I/O possible (SIGPOLL alias) */ + MD_EXCEPTION_CODE_SOL_SIGSTOP = 23, /* stop (cannot be caught or ignored) */ + MD_EXCEPTION_CODE_SOL_SIGTSTP = 24, /* user stop requested from tty */ + MD_EXCEPTION_CODE_SOL_SIGCONT = 25, /* stopped process has been continued */ + MD_EXCEPTION_CODE_SOL_SIGTTIN = 26, /* background tty read attempted */ + MD_EXCEPTION_CODE_SOL_SIGTTOU = 27, /* background tty write attempted */ + MD_EXCEPTION_CODE_SOL_SIGVTALRM = 28, /* virtual timer expired */ + MD_EXCEPTION_CODE_SOL_SIGPROF = 29, /* profiling timer expired */ + MD_EXCEPTION_CODE_SOL_SIGXCPU = 30, /* exceeded cpu limit */ + MD_EXCEPTION_CODE_SOL_SIGXFSZ = 31, /* exceeded file size limit */ + MD_EXCEPTION_CODE_SOL_SIGWAITING = 32, /* reserved signal no longer used by threading code */ + MD_EXCEPTION_CODE_SOL_SIGLWP = 33, /* reserved signal no longer used by threading code */ + MD_EXCEPTION_CODE_SOL_SIGFREEZE = 34, /* special signal used by CPR */ + MD_EXCEPTION_CODE_SOL_SIGTHAW = 35, /* special signal used by CPR */ + MD_EXCEPTION_CODE_SOL_SIGCANCEL = 36, /* reserved signal for thread cancellation */ + MD_EXCEPTION_CODE_SOL_SIGLOST = 37, /* resource lost (eg, record-lock lost) */ + MD_EXCEPTION_CODE_SOL_SIGXRES = 38, /* resource control exceeded */ + MD_EXCEPTION_CODE_SOL_SIGJVM1 = 39, /* reserved signal for Java Virtual Machine */ + MD_EXCEPTION_CODE_SOL_SIGJVM2 = 40 /* reserved signal for Java Virtual Machine */ +} MDExceptionCodeSolaris; + +#endif /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_SOLARIS_H__ */ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_exception_win32.h b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_exception_win32.h new file mode 100644 index 000000000..4b5d57c85 --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_exception_win32.h @@ -0,0 +1,2269 @@ +/* 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. */ + +/* minidump_exception_win32.h: Definitions of exception codes for + * Win32 platform + * + * (This is C99 source, please don't corrupt it with C++.) + * + * Author: Mark Mentovai + * Split into its own file: Neal Sidhwaney */ + + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_WIN32_H__ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_WIN32_H__ + +#include + +#include "google_breakpad/common/breakpad_types.h" + + +/* For (MDException).exception_code. These values come from WinBase.h + * and WinNT.h (names beginning with EXCEPTION_ are in WinBase.h, + * they are STATUS_ in WinNT.h). */ +typedef enum { + MD_EXCEPTION_CODE_WIN_CONTROL_C = 0x40010005, + /* DBG_CONTROL_C */ + MD_EXCEPTION_CODE_WIN_GUARD_PAGE_VIOLATION = 0x80000001, + /* EXCEPTION_GUARD_PAGE */ + MD_EXCEPTION_CODE_WIN_DATATYPE_MISALIGNMENT = 0x80000002, + /* EXCEPTION_DATATYPE_MISALIGNMENT */ + MD_EXCEPTION_CODE_WIN_BREAKPOINT = 0x80000003, + /* EXCEPTION_BREAKPOINT */ + MD_EXCEPTION_CODE_WIN_SINGLE_STEP = 0x80000004, + /* EXCEPTION_SINGLE_STEP */ + MD_EXCEPTION_CODE_WIN_ACCESS_VIOLATION = 0xc0000005, + /* EXCEPTION_ACCESS_VIOLATION */ + MD_EXCEPTION_CODE_WIN_IN_PAGE_ERROR = 0xc0000006, + /* EXCEPTION_IN_PAGE_ERROR */ + MD_EXCEPTION_CODE_WIN_INVALID_HANDLE = 0xc0000008, + /* EXCEPTION_INVALID_HANDLE */ + MD_EXCEPTION_CODE_WIN_ILLEGAL_INSTRUCTION = 0xc000001d, + /* EXCEPTION_ILLEGAL_INSTRUCTION */ + MD_EXCEPTION_CODE_WIN_NONCONTINUABLE_EXCEPTION = 0xc0000025, + /* EXCEPTION_NONCONTINUABLE_EXCEPTION */ + MD_EXCEPTION_CODE_WIN_INVALID_DISPOSITION = 0xc0000026, + /* EXCEPTION_INVALID_DISPOSITION */ + MD_EXCEPTION_CODE_WIN_ARRAY_BOUNDS_EXCEEDED = 0xc000008c, + /* EXCEPTION_BOUNDS_EXCEEDED */ + MD_EXCEPTION_CODE_WIN_FLOAT_DENORMAL_OPERAND = 0xc000008d, + /* EXCEPTION_FLT_DENORMAL_OPERAND */ + MD_EXCEPTION_CODE_WIN_FLOAT_DIVIDE_BY_ZERO = 0xc000008e, + /* EXCEPTION_FLT_DIVIDE_BY_ZERO */ + MD_EXCEPTION_CODE_WIN_FLOAT_INEXACT_RESULT = 0xc000008f, + /* EXCEPTION_FLT_INEXACT_RESULT */ + MD_EXCEPTION_CODE_WIN_FLOAT_INVALID_OPERATION = 0xc0000090, + /* EXCEPTION_FLT_INVALID_OPERATION */ + MD_EXCEPTION_CODE_WIN_FLOAT_OVERFLOW = 0xc0000091, + /* EXCEPTION_FLT_OVERFLOW */ + MD_EXCEPTION_CODE_WIN_FLOAT_STACK_CHECK = 0xc0000092, + /* EXCEPTION_FLT_STACK_CHECK */ + MD_EXCEPTION_CODE_WIN_FLOAT_UNDERFLOW = 0xc0000093, + /* EXCEPTION_FLT_UNDERFLOW */ + MD_EXCEPTION_CODE_WIN_INTEGER_DIVIDE_BY_ZERO = 0xc0000094, + /* EXCEPTION_INT_DIVIDE_BY_ZERO */ + MD_EXCEPTION_CODE_WIN_INTEGER_OVERFLOW = 0xc0000095, + /* EXCEPTION_INT_OVERFLOW */ + MD_EXCEPTION_CODE_WIN_PRIVILEGED_INSTRUCTION = 0xc0000096, + /* EXCEPTION_PRIV_INSTRUCTION */ + MD_EXCEPTION_CODE_WIN_STACK_OVERFLOW = 0xc00000fd, + /* EXCEPTION_STACK_OVERFLOW */ + MD_EXCEPTION_CODE_WIN_BAD_FUNCTION_TABLE = 0xc00000ff, + /* EXCEPTION_BAD_FUNCTION_TABLE */ + MD_EXCEPTION_CODE_WIN_POSSIBLE_DEADLOCK = 0xc0000194, + /* EXCEPTION_POSSIBLE_DEADLOCK */ + MD_EXCEPTION_CODE_WIN_STACK_BUFFER_OVERRUN = 0xc0000409, + /* STATUS_STACK_BUFFER_OVERRUN */ + MD_EXCEPTION_CODE_WIN_HEAP_CORRUPTION = 0xc0000374, + /* STATUS_HEAP_CORRUPTION */ + MD_EXCEPTION_OUT_OF_MEMORY = 0xe0000008, + /* Exception thrown by Chromium allocators to indicate OOM. + See base/process/memory.h in Chromium for rationale. */ + MD_EXCEPTION_CODE_WIN_UNHANDLED_CPP_EXCEPTION = 0xe06d7363, + /* Per http://support.microsoft.com/kb/185294, + generated by Visual C++ compiler */ + MD_EXCEPTION_CODE_WIN_SIMULATED = 0x0517a7ed + /* Fake exception code used by Crashpad's + CrashpadClient::DumpWithoutCrash. */ +} MDExceptionCodeWin; + + +/* For (MDException).exception_information[2], when (MDException).exception_code + * is MD_EXCEPTION_CODE_WIN_IN_PAGE_ERROR. This describes the underlying reason + * for the error. These values come from ntstatus.h. + * + * The content of this enum was created from ntstatus.h in the 8.1 SDK with + * + * egrep '#define [A-Z_0-9]+\s+\(\(NTSTATUS\)0xC[0-9A-F]+L\)' ntstatus.h + * | tr -d '\r' + * | sed -r 's@#define ([A-Z_0-9]+)\s+\(\(NTSTATUS\)(0xC[0-9A-F]+)L\).*@\2 \1@' + * | sort + * | sed -r 's@(0xC[0-9A-F]+) ([A-Z_0-9]+)@ MD_NTSTATUS_WIN_\2 = \1,@' + * + * With easy copy to clipboard with + * | xclip -selection c # on linux + * | clip # on windows + * | pbcopy # on mac + * + * and then the last comma manually removed. */ +typedef enum { + MD_NTSTATUS_WIN_STATUS_UNSUCCESSFUL = 0xC0000001, + MD_NTSTATUS_WIN_STATUS_NOT_IMPLEMENTED = 0xC0000002, + MD_NTSTATUS_WIN_STATUS_INVALID_INFO_CLASS = 0xC0000003, + MD_NTSTATUS_WIN_STATUS_INFO_LENGTH_MISMATCH = 0xC0000004, + MD_NTSTATUS_WIN_STATUS_ACCESS_VIOLATION = 0xC0000005, + MD_NTSTATUS_WIN_STATUS_IN_PAGE_ERROR = 0xC0000006, + MD_NTSTATUS_WIN_STATUS_PAGEFILE_QUOTA = 0xC0000007, + MD_NTSTATUS_WIN_STATUS_INVALID_HANDLE = 0xC0000008, + MD_NTSTATUS_WIN_STATUS_BAD_INITIAL_STACK = 0xC0000009, + MD_NTSTATUS_WIN_STATUS_BAD_INITIAL_PC = 0xC000000A, + MD_NTSTATUS_WIN_STATUS_INVALID_CID = 0xC000000B, + MD_NTSTATUS_WIN_STATUS_TIMER_NOT_CANCELED = 0xC000000C, + MD_NTSTATUS_WIN_STATUS_INVALID_PARAMETER = 0xC000000D, + MD_NTSTATUS_WIN_STATUS_NO_SUCH_DEVICE = 0xC000000E, + MD_NTSTATUS_WIN_STATUS_NO_SUCH_FILE = 0xC000000F, + MD_NTSTATUS_WIN_STATUS_INVALID_DEVICE_REQUEST = 0xC0000010, + MD_NTSTATUS_WIN_STATUS_END_OF_FILE = 0xC0000011, + MD_NTSTATUS_WIN_STATUS_WRONG_VOLUME = 0xC0000012, + MD_NTSTATUS_WIN_STATUS_NO_MEDIA_IN_DEVICE = 0xC0000013, + MD_NTSTATUS_WIN_STATUS_UNRECOGNIZED_MEDIA = 0xC0000014, + MD_NTSTATUS_WIN_STATUS_NONEXISTENT_SECTOR = 0xC0000015, + MD_NTSTATUS_WIN_STATUS_MORE_PROCESSING_REQUIRED = 0xC0000016, + MD_NTSTATUS_WIN_STATUS_NO_MEMORY = 0xC0000017, + MD_NTSTATUS_WIN_STATUS_CONFLICTING_ADDRESSES = 0xC0000018, + MD_NTSTATUS_WIN_STATUS_NOT_MAPPED_VIEW = 0xC0000019, + MD_NTSTATUS_WIN_STATUS_UNABLE_TO_FREE_VM = 0xC000001A, + MD_NTSTATUS_WIN_STATUS_UNABLE_TO_DELETE_SECTION = 0xC000001B, + MD_NTSTATUS_WIN_STATUS_INVALID_SYSTEM_SERVICE = 0xC000001C, + MD_NTSTATUS_WIN_STATUS_ILLEGAL_INSTRUCTION = 0xC000001D, + MD_NTSTATUS_WIN_STATUS_INVALID_LOCK_SEQUENCE = 0xC000001E, + MD_NTSTATUS_WIN_STATUS_INVALID_VIEW_SIZE = 0xC000001F, + MD_NTSTATUS_WIN_STATUS_INVALID_FILE_FOR_SECTION = 0xC0000020, + MD_NTSTATUS_WIN_STATUS_ALREADY_COMMITTED = 0xC0000021, + MD_NTSTATUS_WIN_STATUS_ACCESS_DENIED = 0xC0000022, + MD_NTSTATUS_WIN_STATUS_BUFFER_TOO_SMALL = 0xC0000023, + MD_NTSTATUS_WIN_STATUS_OBJECT_TYPE_MISMATCH = 0xC0000024, + MD_NTSTATUS_WIN_STATUS_NONCONTINUABLE_EXCEPTION = 0xC0000025, + MD_NTSTATUS_WIN_STATUS_INVALID_DISPOSITION = 0xC0000026, + MD_NTSTATUS_WIN_STATUS_UNWIND = 0xC0000027, + MD_NTSTATUS_WIN_STATUS_BAD_STACK = 0xC0000028, + MD_NTSTATUS_WIN_STATUS_INVALID_UNWIND_TARGET = 0xC0000029, + MD_NTSTATUS_WIN_STATUS_NOT_LOCKED = 0xC000002A, + MD_NTSTATUS_WIN_STATUS_PARITY_ERROR = 0xC000002B, + MD_NTSTATUS_WIN_STATUS_UNABLE_TO_DECOMMIT_VM = 0xC000002C, + MD_NTSTATUS_WIN_STATUS_NOT_COMMITTED = 0xC000002D, + MD_NTSTATUS_WIN_STATUS_INVALID_PORT_ATTRIBUTES = 0xC000002E, + MD_NTSTATUS_WIN_STATUS_PORT_MESSAGE_TOO_LONG = 0xC000002F, + MD_NTSTATUS_WIN_STATUS_INVALID_PARAMETER_MIX = 0xC0000030, + MD_NTSTATUS_WIN_STATUS_INVALID_QUOTA_LOWER = 0xC0000031, + MD_NTSTATUS_WIN_STATUS_DISK_CORRUPT_ERROR = 0xC0000032, + MD_NTSTATUS_WIN_STATUS_OBJECT_NAME_INVALID = 0xC0000033, + MD_NTSTATUS_WIN_STATUS_OBJECT_NAME_NOT_FOUND = 0xC0000034, + MD_NTSTATUS_WIN_STATUS_OBJECT_NAME_COLLISION = 0xC0000035, + MD_NTSTATUS_WIN_STATUS_PORT_DISCONNECTED = 0xC0000037, + MD_NTSTATUS_WIN_STATUS_DEVICE_ALREADY_ATTACHED = 0xC0000038, + MD_NTSTATUS_WIN_STATUS_OBJECT_PATH_INVALID = 0xC0000039, + MD_NTSTATUS_WIN_STATUS_OBJECT_PATH_NOT_FOUND = 0xC000003A, + MD_NTSTATUS_WIN_STATUS_OBJECT_PATH_SYNTAX_BAD = 0xC000003B, + MD_NTSTATUS_WIN_STATUS_DATA_OVERRUN = 0xC000003C, + MD_NTSTATUS_WIN_STATUS_DATA_LATE_ERROR = 0xC000003D, + MD_NTSTATUS_WIN_STATUS_DATA_ERROR = 0xC000003E, + MD_NTSTATUS_WIN_STATUS_CRC_ERROR = 0xC000003F, + MD_NTSTATUS_WIN_STATUS_SECTION_TOO_BIG = 0xC0000040, + MD_NTSTATUS_WIN_STATUS_PORT_CONNECTION_REFUSED = 0xC0000041, + MD_NTSTATUS_WIN_STATUS_INVALID_PORT_HANDLE = 0xC0000042, + MD_NTSTATUS_WIN_STATUS_SHARING_VIOLATION = 0xC0000043, + MD_NTSTATUS_WIN_STATUS_QUOTA_EXCEEDED = 0xC0000044, + MD_NTSTATUS_WIN_STATUS_INVALID_PAGE_PROTECTION = 0xC0000045, + MD_NTSTATUS_WIN_STATUS_MUTANT_NOT_OWNED = 0xC0000046, + MD_NTSTATUS_WIN_STATUS_SEMAPHORE_LIMIT_EXCEEDED = 0xC0000047, + MD_NTSTATUS_WIN_STATUS_PORT_ALREADY_SET = 0xC0000048, + MD_NTSTATUS_WIN_STATUS_SECTION_NOT_IMAGE = 0xC0000049, + MD_NTSTATUS_WIN_STATUS_SUSPEND_COUNT_EXCEEDED = 0xC000004A, + MD_NTSTATUS_WIN_STATUS_THREAD_IS_TERMINATING = 0xC000004B, + MD_NTSTATUS_WIN_STATUS_BAD_WORKING_SET_LIMIT = 0xC000004C, + MD_NTSTATUS_WIN_STATUS_INCOMPATIBLE_FILE_MAP = 0xC000004D, + MD_NTSTATUS_WIN_STATUS_SECTION_PROTECTION = 0xC000004E, + MD_NTSTATUS_WIN_STATUS_EAS_NOT_SUPPORTED = 0xC000004F, + MD_NTSTATUS_WIN_STATUS_EA_TOO_LARGE = 0xC0000050, + MD_NTSTATUS_WIN_STATUS_NONEXISTENT_EA_ENTRY = 0xC0000051, + MD_NTSTATUS_WIN_STATUS_NO_EAS_ON_FILE = 0xC0000052, + MD_NTSTATUS_WIN_STATUS_EA_CORRUPT_ERROR = 0xC0000053, + MD_NTSTATUS_WIN_STATUS_FILE_LOCK_CONFLICT = 0xC0000054, + MD_NTSTATUS_WIN_STATUS_LOCK_NOT_GRANTED = 0xC0000055, + MD_NTSTATUS_WIN_STATUS_DELETE_PENDING = 0xC0000056, + MD_NTSTATUS_WIN_STATUS_CTL_FILE_NOT_SUPPORTED = 0xC0000057, + MD_NTSTATUS_WIN_STATUS_UNKNOWN_REVISION = 0xC0000058, + MD_NTSTATUS_WIN_STATUS_REVISION_MISMATCH = 0xC0000059, + MD_NTSTATUS_WIN_STATUS_INVALID_OWNER = 0xC000005A, + MD_NTSTATUS_WIN_STATUS_INVALID_PRIMARY_GROUP = 0xC000005B, + MD_NTSTATUS_WIN_STATUS_NO_IMPERSONATION_TOKEN = 0xC000005C, + MD_NTSTATUS_WIN_STATUS_CANT_DISABLE_MANDATORY = 0xC000005D, + MD_NTSTATUS_WIN_STATUS_NO_LOGON_SERVERS = 0xC000005E, + MD_NTSTATUS_WIN_STATUS_NO_SUCH_LOGON_SESSION = 0xC000005F, + MD_NTSTATUS_WIN_STATUS_NO_SUCH_PRIVILEGE = 0xC0000060, + MD_NTSTATUS_WIN_STATUS_PRIVILEGE_NOT_HELD = 0xC0000061, + MD_NTSTATUS_WIN_STATUS_INVALID_ACCOUNT_NAME = 0xC0000062, + MD_NTSTATUS_WIN_STATUS_USER_EXISTS = 0xC0000063, + MD_NTSTATUS_WIN_STATUS_NO_SUCH_USER = 0xC0000064, + MD_NTSTATUS_WIN_STATUS_GROUP_EXISTS = 0xC0000065, + MD_NTSTATUS_WIN_STATUS_NO_SUCH_GROUP = 0xC0000066, + MD_NTSTATUS_WIN_STATUS_MEMBER_IN_GROUP = 0xC0000067, + MD_NTSTATUS_WIN_STATUS_MEMBER_NOT_IN_GROUP = 0xC0000068, + MD_NTSTATUS_WIN_STATUS_LAST_ADMIN = 0xC0000069, + MD_NTSTATUS_WIN_STATUS_WRONG_PASSWORD = 0xC000006A, + MD_NTSTATUS_WIN_STATUS_ILL_FORMED_PASSWORD = 0xC000006B, + MD_NTSTATUS_WIN_STATUS_PASSWORD_RESTRICTION = 0xC000006C, + MD_NTSTATUS_WIN_STATUS_LOGON_FAILURE = 0xC000006D, + MD_NTSTATUS_WIN_STATUS_ACCOUNT_RESTRICTION = 0xC000006E, + MD_NTSTATUS_WIN_STATUS_INVALID_LOGON_HOURS = 0xC000006F, + MD_NTSTATUS_WIN_STATUS_INVALID_WORKSTATION = 0xC0000070, + MD_NTSTATUS_WIN_STATUS_PASSWORD_EXPIRED = 0xC0000071, + MD_NTSTATUS_WIN_STATUS_ACCOUNT_DISABLED = 0xC0000072, + MD_NTSTATUS_WIN_STATUS_NONE_MAPPED = 0xC0000073, + MD_NTSTATUS_WIN_STATUS_TOO_MANY_LUIDS_REQUESTED = 0xC0000074, + MD_NTSTATUS_WIN_STATUS_LUIDS_EXHAUSTED = 0xC0000075, + MD_NTSTATUS_WIN_STATUS_INVALID_SUB_AUTHORITY = 0xC0000076, + MD_NTSTATUS_WIN_STATUS_INVALID_ACL = 0xC0000077, + MD_NTSTATUS_WIN_STATUS_INVALID_SID = 0xC0000078, + MD_NTSTATUS_WIN_STATUS_INVALID_SECURITY_DESCR = 0xC0000079, + MD_NTSTATUS_WIN_STATUS_PROCEDURE_NOT_FOUND = 0xC000007A, + MD_NTSTATUS_WIN_STATUS_INVALID_IMAGE_FORMAT = 0xC000007B, + MD_NTSTATUS_WIN_STATUS_NO_TOKEN = 0xC000007C, + MD_NTSTATUS_WIN_STATUS_BAD_INHERITANCE_ACL = 0xC000007D, + MD_NTSTATUS_WIN_STATUS_RANGE_NOT_LOCKED = 0xC000007E, + MD_NTSTATUS_WIN_STATUS_DISK_FULL = 0xC000007F, + MD_NTSTATUS_WIN_STATUS_SERVER_DISABLED = 0xC0000080, + MD_NTSTATUS_WIN_STATUS_SERVER_NOT_DISABLED = 0xC0000081, + MD_NTSTATUS_WIN_STATUS_TOO_MANY_GUIDS_REQUESTED = 0xC0000082, + MD_NTSTATUS_WIN_STATUS_GUIDS_EXHAUSTED = 0xC0000083, + MD_NTSTATUS_WIN_STATUS_INVALID_ID_AUTHORITY = 0xC0000084, + MD_NTSTATUS_WIN_STATUS_AGENTS_EXHAUSTED = 0xC0000085, + MD_NTSTATUS_WIN_STATUS_INVALID_VOLUME_LABEL = 0xC0000086, + MD_NTSTATUS_WIN_STATUS_SECTION_NOT_EXTENDED = 0xC0000087, + MD_NTSTATUS_WIN_STATUS_NOT_MAPPED_DATA = 0xC0000088, + MD_NTSTATUS_WIN_STATUS_RESOURCE_DATA_NOT_FOUND = 0xC0000089, + MD_NTSTATUS_WIN_STATUS_RESOURCE_TYPE_NOT_FOUND = 0xC000008A, + MD_NTSTATUS_WIN_STATUS_RESOURCE_NAME_NOT_FOUND = 0xC000008B, + MD_NTSTATUS_WIN_STATUS_ARRAY_BOUNDS_EXCEEDED = 0xC000008C, + MD_NTSTATUS_WIN_STATUS_FLOAT_DENORMAL_OPERAND = 0xC000008D, + MD_NTSTATUS_WIN_STATUS_FLOAT_DIVIDE_BY_ZERO = 0xC000008E, + MD_NTSTATUS_WIN_STATUS_FLOAT_INEXACT_RESULT = 0xC000008F, + MD_NTSTATUS_WIN_STATUS_FLOAT_INVALID_OPERATION = 0xC0000090, + MD_NTSTATUS_WIN_STATUS_FLOAT_OVERFLOW = 0xC0000091, + MD_NTSTATUS_WIN_STATUS_FLOAT_STACK_CHECK = 0xC0000092, + MD_NTSTATUS_WIN_STATUS_FLOAT_UNDERFLOW = 0xC0000093, + MD_NTSTATUS_WIN_STATUS_INTEGER_DIVIDE_BY_ZERO = 0xC0000094, + MD_NTSTATUS_WIN_STATUS_INTEGER_OVERFLOW = 0xC0000095, + MD_NTSTATUS_WIN_STATUS_PRIVILEGED_INSTRUCTION = 0xC0000096, + MD_NTSTATUS_WIN_STATUS_TOO_MANY_PAGING_FILES = 0xC0000097, + MD_NTSTATUS_WIN_STATUS_FILE_INVALID = 0xC0000098, + MD_NTSTATUS_WIN_STATUS_ALLOTTED_SPACE_EXCEEDED = 0xC0000099, + MD_NTSTATUS_WIN_STATUS_INSUFFICIENT_RESOURCES = 0xC000009A, + MD_NTSTATUS_WIN_STATUS_DFS_EXIT_PATH_FOUND = 0xC000009B, + MD_NTSTATUS_WIN_STATUS_DEVICE_DATA_ERROR = 0xC000009C, + MD_NTSTATUS_WIN_STATUS_DEVICE_NOT_CONNECTED = 0xC000009D, + MD_NTSTATUS_WIN_STATUS_DEVICE_POWER_FAILURE = 0xC000009E, + MD_NTSTATUS_WIN_STATUS_FREE_VM_NOT_AT_BASE = 0xC000009F, + MD_NTSTATUS_WIN_STATUS_MEMORY_NOT_ALLOCATED = 0xC00000A0, + MD_NTSTATUS_WIN_STATUS_WORKING_SET_QUOTA = 0xC00000A1, + MD_NTSTATUS_WIN_STATUS_MEDIA_WRITE_PROTECTED = 0xC00000A2, + MD_NTSTATUS_WIN_STATUS_DEVICE_NOT_READY = 0xC00000A3, + MD_NTSTATUS_WIN_STATUS_INVALID_GROUP_ATTRIBUTES = 0xC00000A4, + MD_NTSTATUS_WIN_STATUS_BAD_IMPERSONATION_LEVEL = 0xC00000A5, + MD_NTSTATUS_WIN_STATUS_CANT_OPEN_ANONYMOUS = 0xC00000A6, + MD_NTSTATUS_WIN_STATUS_BAD_VALIDATION_CLASS = 0xC00000A7, + MD_NTSTATUS_WIN_STATUS_BAD_TOKEN_TYPE = 0xC00000A8, + MD_NTSTATUS_WIN_STATUS_BAD_MASTER_BOOT_RECORD = 0xC00000A9, + MD_NTSTATUS_WIN_STATUS_INSTRUCTION_MISALIGNMENT = 0xC00000AA, + MD_NTSTATUS_WIN_STATUS_INSTANCE_NOT_AVAILABLE = 0xC00000AB, + MD_NTSTATUS_WIN_STATUS_PIPE_NOT_AVAILABLE = 0xC00000AC, + MD_NTSTATUS_WIN_STATUS_INVALID_PIPE_STATE = 0xC00000AD, + MD_NTSTATUS_WIN_STATUS_PIPE_BUSY = 0xC00000AE, + MD_NTSTATUS_WIN_STATUS_ILLEGAL_FUNCTION = 0xC00000AF, + MD_NTSTATUS_WIN_STATUS_PIPE_DISCONNECTED = 0xC00000B0, + MD_NTSTATUS_WIN_STATUS_PIPE_CLOSING = 0xC00000B1, + MD_NTSTATUS_WIN_STATUS_PIPE_CONNECTED = 0xC00000B2, + MD_NTSTATUS_WIN_STATUS_PIPE_LISTENING = 0xC00000B3, + MD_NTSTATUS_WIN_STATUS_INVALID_READ_MODE = 0xC00000B4, + MD_NTSTATUS_WIN_STATUS_IO_TIMEOUT = 0xC00000B5, + MD_NTSTATUS_WIN_STATUS_FILE_FORCED_CLOSED = 0xC00000B6, + MD_NTSTATUS_WIN_STATUS_PROFILING_NOT_STARTED = 0xC00000B7, + MD_NTSTATUS_WIN_STATUS_PROFILING_NOT_STOPPED = 0xC00000B8, + MD_NTSTATUS_WIN_STATUS_COULD_NOT_INTERPRET = 0xC00000B9, + MD_NTSTATUS_WIN_STATUS_FILE_IS_A_DIRECTORY = 0xC00000BA, + MD_NTSTATUS_WIN_STATUS_NOT_SUPPORTED = 0xC00000BB, + MD_NTSTATUS_WIN_STATUS_REMOTE_NOT_LISTENING = 0xC00000BC, + MD_NTSTATUS_WIN_STATUS_DUPLICATE_NAME = 0xC00000BD, + MD_NTSTATUS_WIN_STATUS_BAD_NETWORK_PATH = 0xC00000BE, + MD_NTSTATUS_WIN_STATUS_NETWORK_BUSY = 0xC00000BF, + MD_NTSTATUS_WIN_STATUS_DEVICE_DOES_NOT_EXIST = 0xC00000C0, + MD_NTSTATUS_WIN_STATUS_TOO_MANY_COMMANDS = 0xC00000C1, + MD_NTSTATUS_WIN_STATUS_ADAPTER_HARDWARE_ERROR = 0xC00000C2, + MD_NTSTATUS_WIN_STATUS_INVALID_NETWORK_RESPONSE = 0xC00000C3, + MD_NTSTATUS_WIN_STATUS_UNEXPECTED_NETWORK_ERROR = 0xC00000C4, + MD_NTSTATUS_WIN_STATUS_BAD_REMOTE_ADAPTER = 0xC00000C5, + MD_NTSTATUS_WIN_STATUS_PRINT_QUEUE_FULL = 0xC00000C6, + MD_NTSTATUS_WIN_STATUS_NO_SPOOL_SPACE = 0xC00000C7, + MD_NTSTATUS_WIN_STATUS_PRINT_CANCELLED = 0xC00000C8, + MD_NTSTATUS_WIN_STATUS_NETWORK_NAME_DELETED = 0xC00000C9, + MD_NTSTATUS_WIN_STATUS_NETWORK_ACCESS_DENIED = 0xC00000CA, + MD_NTSTATUS_WIN_STATUS_BAD_DEVICE_TYPE = 0xC00000CB, + MD_NTSTATUS_WIN_STATUS_BAD_NETWORK_NAME = 0xC00000CC, + MD_NTSTATUS_WIN_STATUS_TOO_MANY_NAMES = 0xC00000CD, + MD_NTSTATUS_WIN_STATUS_TOO_MANY_SESSIONS = 0xC00000CE, + MD_NTSTATUS_WIN_STATUS_SHARING_PAUSED = 0xC00000CF, + MD_NTSTATUS_WIN_STATUS_REQUEST_NOT_ACCEPTED = 0xC00000D0, + MD_NTSTATUS_WIN_STATUS_REDIRECTOR_PAUSED = 0xC00000D1, + MD_NTSTATUS_WIN_STATUS_NET_WRITE_FAULT = 0xC00000D2, + MD_NTSTATUS_WIN_STATUS_PROFILING_AT_LIMIT = 0xC00000D3, + MD_NTSTATUS_WIN_STATUS_NOT_SAME_DEVICE = 0xC00000D4, + MD_NTSTATUS_WIN_STATUS_FILE_RENAMED = 0xC00000D5, + MD_NTSTATUS_WIN_STATUS_VIRTUAL_CIRCUIT_CLOSED = 0xC00000D6, + MD_NTSTATUS_WIN_STATUS_NO_SECURITY_ON_OBJECT = 0xC00000D7, + MD_NTSTATUS_WIN_STATUS_CANT_WAIT = 0xC00000D8, + MD_NTSTATUS_WIN_STATUS_PIPE_EMPTY = 0xC00000D9, + MD_NTSTATUS_WIN_STATUS_CANT_ACCESS_DOMAIN_INFO = 0xC00000DA, + MD_NTSTATUS_WIN_STATUS_CANT_TERMINATE_SELF = 0xC00000DB, + MD_NTSTATUS_WIN_STATUS_INVALID_SERVER_STATE = 0xC00000DC, + MD_NTSTATUS_WIN_STATUS_INVALID_DOMAIN_STATE = 0xC00000DD, + MD_NTSTATUS_WIN_STATUS_INVALID_DOMAIN_ROLE = 0xC00000DE, + MD_NTSTATUS_WIN_STATUS_NO_SUCH_DOMAIN = 0xC00000DF, + MD_NTSTATUS_WIN_STATUS_DOMAIN_EXISTS = 0xC00000E0, + MD_NTSTATUS_WIN_STATUS_DOMAIN_LIMIT_EXCEEDED = 0xC00000E1, + MD_NTSTATUS_WIN_STATUS_OPLOCK_NOT_GRANTED = 0xC00000E2, + MD_NTSTATUS_WIN_STATUS_INVALID_OPLOCK_PROTOCOL = 0xC00000E3, + MD_NTSTATUS_WIN_STATUS_INTERNAL_DB_CORRUPTION = 0xC00000E4, + MD_NTSTATUS_WIN_STATUS_INTERNAL_ERROR = 0xC00000E5, + MD_NTSTATUS_WIN_STATUS_GENERIC_NOT_MAPPED = 0xC00000E6, + MD_NTSTATUS_WIN_STATUS_BAD_DESCRIPTOR_FORMAT = 0xC00000E7, + MD_NTSTATUS_WIN_STATUS_INVALID_USER_BUFFER = 0xC00000E8, + MD_NTSTATUS_WIN_STATUS_UNEXPECTED_IO_ERROR = 0xC00000E9, + MD_NTSTATUS_WIN_STATUS_UNEXPECTED_MM_CREATE_ERR = 0xC00000EA, + MD_NTSTATUS_WIN_STATUS_UNEXPECTED_MM_MAP_ERROR = 0xC00000EB, + MD_NTSTATUS_WIN_STATUS_UNEXPECTED_MM_EXTEND_ERR = 0xC00000EC, + MD_NTSTATUS_WIN_STATUS_NOT_LOGON_PROCESS = 0xC00000ED, + MD_NTSTATUS_WIN_STATUS_LOGON_SESSION_EXISTS = 0xC00000EE, + MD_NTSTATUS_WIN_STATUS_INVALID_PARAMETER_1 = 0xC00000EF, + MD_NTSTATUS_WIN_STATUS_INVALID_PARAMETER_2 = 0xC00000F0, + MD_NTSTATUS_WIN_STATUS_INVALID_PARAMETER_3 = 0xC00000F1, + MD_NTSTATUS_WIN_STATUS_INVALID_PARAMETER_4 = 0xC00000F2, + MD_NTSTATUS_WIN_STATUS_INVALID_PARAMETER_5 = 0xC00000F3, + MD_NTSTATUS_WIN_STATUS_INVALID_PARAMETER_6 = 0xC00000F4, + MD_NTSTATUS_WIN_STATUS_INVALID_PARAMETER_7 = 0xC00000F5, + MD_NTSTATUS_WIN_STATUS_INVALID_PARAMETER_8 = 0xC00000F6, + MD_NTSTATUS_WIN_STATUS_INVALID_PARAMETER_9 = 0xC00000F7, + MD_NTSTATUS_WIN_STATUS_INVALID_PARAMETER_10 = 0xC00000F8, + MD_NTSTATUS_WIN_STATUS_INVALID_PARAMETER_11 = 0xC00000F9, + MD_NTSTATUS_WIN_STATUS_INVALID_PARAMETER_12 = 0xC00000FA, + MD_NTSTATUS_WIN_STATUS_REDIRECTOR_NOT_STARTED = 0xC00000FB, + MD_NTSTATUS_WIN_STATUS_REDIRECTOR_STARTED = 0xC00000FC, + MD_NTSTATUS_WIN_STATUS_STACK_OVERFLOW = 0xC00000FD, + MD_NTSTATUS_WIN_STATUS_NO_SUCH_PACKAGE = 0xC00000FE, + MD_NTSTATUS_WIN_STATUS_BAD_FUNCTION_TABLE = 0xC00000FF, + MD_NTSTATUS_WIN_STATUS_VARIABLE_NOT_FOUND = 0xC0000100, + MD_NTSTATUS_WIN_STATUS_DIRECTORY_NOT_EMPTY = 0xC0000101, + MD_NTSTATUS_WIN_STATUS_FILE_CORRUPT_ERROR = 0xC0000102, + MD_NTSTATUS_WIN_STATUS_NOT_A_DIRECTORY = 0xC0000103, + MD_NTSTATUS_WIN_STATUS_BAD_LOGON_SESSION_STATE = 0xC0000104, + MD_NTSTATUS_WIN_STATUS_LOGON_SESSION_COLLISION = 0xC0000105, + MD_NTSTATUS_WIN_STATUS_NAME_TOO_LONG = 0xC0000106, + MD_NTSTATUS_WIN_STATUS_FILES_OPEN = 0xC0000107, + MD_NTSTATUS_WIN_STATUS_CONNECTION_IN_USE = 0xC0000108, + MD_NTSTATUS_WIN_STATUS_MESSAGE_NOT_FOUND = 0xC0000109, + MD_NTSTATUS_WIN_STATUS_PROCESS_IS_TERMINATING = 0xC000010A, + MD_NTSTATUS_WIN_STATUS_INVALID_LOGON_TYPE = 0xC000010B, + MD_NTSTATUS_WIN_STATUS_NO_GUID_TRANSLATION = 0xC000010C, + MD_NTSTATUS_WIN_STATUS_CANNOT_IMPERSONATE = 0xC000010D, + MD_NTSTATUS_WIN_STATUS_IMAGE_ALREADY_LOADED = 0xC000010E, + MD_NTSTATUS_WIN_STATUS_ABIOS_NOT_PRESENT = 0xC000010F, + MD_NTSTATUS_WIN_STATUS_ABIOS_LID_NOT_EXIST = 0xC0000110, + MD_NTSTATUS_WIN_STATUS_ABIOS_LID_ALREADY_OWNED = 0xC0000111, + MD_NTSTATUS_WIN_STATUS_ABIOS_NOT_LID_OWNER = 0xC0000112, + MD_NTSTATUS_WIN_STATUS_ABIOS_INVALID_COMMAND = 0xC0000113, + MD_NTSTATUS_WIN_STATUS_ABIOS_INVALID_LID = 0xC0000114, + MD_NTSTATUS_WIN_STATUS_ABIOS_SELECTOR_NOT_AVAILABLE = 0xC0000115, + MD_NTSTATUS_WIN_STATUS_ABIOS_INVALID_SELECTOR = 0xC0000116, + MD_NTSTATUS_WIN_STATUS_NO_LDT = 0xC0000117, + MD_NTSTATUS_WIN_STATUS_INVALID_LDT_SIZE = 0xC0000118, + MD_NTSTATUS_WIN_STATUS_INVALID_LDT_OFFSET = 0xC0000119, + MD_NTSTATUS_WIN_STATUS_INVALID_LDT_DESCRIPTOR = 0xC000011A, + MD_NTSTATUS_WIN_STATUS_INVALID_IMAGE_NE_FORMAT = 0xC000011B, + MD_NTSTATUS_WIN_STATUS_RXACT_INVALID_STATE = 0xC000011C, + MD_NTSTATUS_WIN_STATUS_RXACT_COMMIT_FAILURE = 0xC000011D, + MD_NTSTATUS_WIN_STATUS_MAPPED_FILE_SIZE_ZERO = 0xC000011E, + MD_NTSTATUS_WIN_STATUS_TOO_MANY_OPENED_FILES = 0xC000011F, + MD_NTSTATUS_WIN_STATUS_CANCELLED = 0xC0000120, + MD_NTSTATUS_WIN_STATUS_CANNOT_DELETE = 0xC0000121, + MD_NTSTATUS_WIN_STATUS_INVALID_COMPUTER_NAME = 0xC0000122, + MD_NTSTATUS_WIN_STATUS_FILE_DELETED = 0xC0000123, + MD_NTSTATUS_WIN_STATUS_SPECIAL_ACCOUNT = 0xC0000124, + MD_NTSTATUS_WIN_STATUS_SPECIAL_GROUP = 0xC0000125, + MD_NTSTATUS_WIN_STATUS_SPECIAL_USER = 0xC0000126, + MD_NTSTATUS_WIN_STATUS_MEMBERS_PRIMARY_GROUP = 0xC0000127, + MD_NTSTATUS_WIN_STATUS_FILE_CLOSED = 0xC0000128, + MD_NTSTATUS_WIN_STATUS_TOO_MANY_THREADS = 0xC0000129, + MD_NTSTATUS_WIN_STATUS_THREAD_NOT_IN_PROCESS = 0xC000012A, + MD_NTSTATUS_WIN_STATUS_TOKEN_ALREADY_IN_USE = 0xC000012B, + MD_NTSTATUS_WIN_STATUS_PAGEFILE_QUOTA_EXCEEDED = 0xC000012C, + MD_NTSTATUS_WIN_STATUS_COMMITMENT_LIMIT = 0xC000012D, + MD_NTSTATUS_WIN_STATUS_INVALID_IMAGE_LE_FORMAT = 0xC000012E, + MD_NTSTATUS_WIN_STATUS_INVALID_IMAGE_NOT_MZ = 0xC000012F, + MD_NTSTATUS_WIN_STATUS_INVALID_IMAGE_PROTECT = 0xC0000130, + MD_NTSTATUS_WIN_STATUS_INVALID_IMAGE_WIN_16 = 0xC0000131, + MD_NTSTATUS_WIN_STATUS_LOGON_SERVER_CONFLICT = 0xC0000132, + MD_NTSTATUS_WIN_STATUS_TIME_DIFFERENCE_AT_DC = 0xC0000133, + MD_NTSTATUS_WIN_STATUS_SYNCHRONIZATION_REQUIRED = 0xC0000134, + MD_NTSTATUS_WIN_STATUS_DLL_NOT_FOUND = 0xC0000135, + MD_NTSTATUS_WIN_STATUS_OPEN_FAILED = 0xC0000136, + MD_NTSTATUS_WIN_STATUS_IO_PRIVILEGE_FAILED = 0xC0000137, + MD_NTSTATUS_WIN_STATUS_ORDINAL_NOT_FOUND = 0xC0000138, + MD_NTSTATUS_WIN_STATUS_ENTRYPOINT_NOT_FOUND = 0xC0000139, + MD_NTSTATUS_WIN_STATUS_CONTROL_C_EXIT = 0xC000013A, + MD_NTSTATUS_WIN_STATUS_LOCAL_DISCONNECT = 0xC000013B, + MD_NTSTATUS_WIN_STATUS_REMOTE_DISCONNECT = 0xC000013C, + MD_NTSTATUS_WIN_STATUS_REMOTE_RESOURCES = 0xC000013D, + MD_NTSTATUS_WIN_STATUS_LINK_FAILED = 0xC000013E, + MD_NTSTATUS_WIN_STATUS_LINK_TIMEOUT = 0xC000013F, + MD_NTSTATUS_WIN_STATUS_INVALID_CONNECTION = 0xC0000140, + MD_NTSTATUS_WIN_STATUS_INVALID_ADDRESS = 0xC0000141, + MD_NTSTATUS_WIN_STATUS_DLL_INIT_FAILED = 0xC0000142, + MD_NTSTATUS_WIN_STATUS_MISSING_SYSTEMFILE = 0xC0000143, + MD_NTSTATUS_WIN_STATUS_UNHANDLED_EXCEPTION = 0xC0000144, + MD_NTSTATUS_WIN_STATUS_APP_INIT_FAILURE = 0xC0000145, + MD_NTSTATUS_WIN_STATUS_PAGEFILE_CREATE_FAILED = 0xC0000146, + MD_NTSTATUS_WIN_STATUS_NO_PAGEFILE = 0xC0000147, + MD_NTSTATUS_WIN_STATUS_INVALID_LEVEL = 0xC0000148, + MD_NTSTATUS_WIN_STATUS_WRONG_PASSWORD_CORE = 0xC0000149, + MD_NTSTATUS_WIN_STATUS_ILLEGAL_FLOAT_CONTEXT = 0xC000014A, + MD_NTSTATUS_WIN_STATUS_PIPE_BROKEN = 0xC000014B, + MD_NTSTATUS_WIN_STATUS_REGISTRY_CORRUPT = 0xC000014C, + MD_NTSTATUS_WIN_STATUS_REGISTRY_IO_FAILED = 0xC000014D, + MD_NTSTATUS_WIN_STATUS_NO_EVENT_PAIR = 0xC000014E, + MD_NTSTATUS_WIN_STATUS_UNRECOGNIZED_VOLUME = 0xC000014F, + MD_NTSTATUS_WIN_STATUS_SERIAL_NO_DEVICE_INITED = 0xC0000150, + MD_NTSTATUS_WIN_STATUS_NO_SUCH_ALIAS = 0xC0000151, + MD_NTSTATUS_WIN_STATUS_MEMBER_NOT_IN_ALIAS = 0xC0000152, + MD_NTSTATUS_WIN_STATUS_MEMBER_IN_ALIAS = 0xC0000153, + MD_NTSTATUS_WIN_STATUS_ALIAS_EXISTS = 0xC0000154, + MD_NTSTATUS_WIN_STATUS_LOGON_NOT_GRANTED = 0xC0000155, + MD_NTSTATUS_WIN_STATUS_TOO_MANY_SECRETS = 0xC0000156, + MD_NTSTATUS_WIN_STATUS_SECRET_TOO_LONG = 0xC0000157, + MD_NTSTATUS_WIN_STATUS_INTERNAL_DB_ERROR = 0xC0000158, + MD_NTSTATUS_WIN_STATUS_FULLSCREEN_MODE = 0xC0000159, + MD_NTSTATUS_WIN_STATUS_TOO_MANY_CONTEXT_IDS = 0xC000015A, + MD_NTSTATUS_WIN_STATUS_LOGON_TYPE_NOT_GRANTED = 0xC000015B, + MD_NTSTATUS_WIN_STATUS_NOT_REGISTRY_FILE = 0xC000015C, + MD_NTSTATUS_WIN_STATUS_NT_CROSS_ENCRYPTION_REQUIRED = 0xC000015D, + MD_NTSTATUS_WIN_STATUS_DOMAIN_CTRLR_CONFIG_ERROR = 0xC000015E, + MD_NTSTATUS_WIN_STATUS_FT_MISSING_MEMBER = 0xC000015F, + MD_NTSTATUS_WIN_STATUS_ILL_FORMED_SERVICE_ENTRY = 0xC0000160, + MD_NTSTATUS_WIN_STATUS_ILLEGAL_CHARACTER = 0xC0000161, + MD_NTSTATUS_WIN_STATUS_UNMAPPABLE_CHARACTER = 0xC0000162, + MD_NTSTATUS_WIN_STATUS_UNDEFINED_CHARACTER = 0xC0000163, + MD_NTSTATUS_WIN_STATUS_FLOPPY_VOLUME = 0xC0000164, + MD_NTSTATUS_WIN_STATUS_FLOPPY_ID_MARK_NOT_FOUND = 0xC0000165, + MD_NTSTATUS_WIN_STATUS_FLOPPY_WRONG_CYLINDER = 0xC0000166, + MD_NTSTATUS_WIN_STATUS_FLOPPY_UNKNOWN_ERROR = 0xC0000167, + MD_NTSTATUS_WIN_STATUS_FLOPPY_BAD_REGISTERS = 0xC0000168, + MD_NTSTATUS_WIN_STATUS_DISK_RECALIBRATE_FAILED = 0xC0000169, + MD_NTSTATUS_WIN_STATUS_DISK_OPERATION_FAILED = 0xC000016A, + MD_NTSTATUS_WIN_STATUS_DISK_RESET_FAILED = 0xC000016B, + MD_NTSTATUS_WIN_STATUS_SHARED_IRQ_BUSY = 0xC000016C, + MD_NTSTATUS_WIN_STATUS_FT_ORPHANING = 0xC000016D, + MD_NTSTATUS_WIN_STATUS_BIOS_FAILED_TO_CONNECT_INTERRUPT = 0xC000016E, + MD_NTSTATUS_WIN_STATUS_PARTITION_FAILURE = 0xC0000172, + MD_NTSTATUS_WIN_STATUS_INVALID_BLOCK_LENGTH = 0xC0000173, + MD_NTSTATUS_WIN_STATUS_DEVICE_NOT_PARTITIONED = 0xC0000174, + MD_NTSTATUS_WIN_STATUS_UNABLE_TO_LOCK_MEDIA = 0xC0000175, + MD_NTSTATUS_WIN_STATUS_UNABLE_TO_UNLOAD_MEDIA = 0xC0000176, + MD_NTSTATUS_WIN_STATUS_EOM_OVERFLOW = 0xC0000177, + MD_NTSTATUS_WIN_STATUS_NO_MEDIA = 0xC0000178, + MD_NTSTATUS_WIN_STATUS_NO_SUCH_MEMBER = 0xC000017A, + MD_NTSTATUS_WIN_STATUS_INVALID_MEMBER = 0xC000017B, + MD_NTSTATUS_WIN_STATUS_KEY_DELETED = 0xC000017C, + MD_NTSTATUS_WIN_STATUS_NO_LOG_SPACE = 0xC000017D, + MD_NTSTATUS_WIN_STATUS_TOO_MANY_SIDS = 0xC000017E, + MD_NTSTATUS_WIN_STATUS_LM_CROSS_ENCRYPTION_REQUIRED = 0xC000017F, + MD_NTSTATUS_WIN_STATUS_KEY_HAS_CHILDREN = 0xC0000180, + MD_NTSTATUS_WIN_STATUS_CHILD_MUST_BE_VOLATILE = 0xC0000181, + MD_NTSTATUS_WIN_STATUS_DEVICE_CONFIGURATION_ERROR = 0xC0000182, + MD_NTSTATUS_WIN_STATUS_DRIVER_INTERNAL_ERROR = 0xC0000183, + MD_NTSTATUS_WIN_STATUS_INVALID_DEVICE_STATE = 0xC0000184, + MD_NTSTATUS_WIN_STATUS_IO_DEVICE_ERROR = 0xC0000185, + MD_NTSTATUS_WIN_STATUS_DEVICE_PROTOCOL_ERROR = 0xC0000186, + MD_NTSTATUS_WIN_STATUS_BACKUP_CONTROLLER = 0xC0000187, + MD_NTSTATUS_WIN_STATUS_LOG_FILE_FULL = 0xC0000188, + MD_NTSTATUS_WIN_STATUS_TOO_LATE = 0xC0000189, + MD_NTSTATUS_WIN_STATUS_NO_TRUST_LSA_SECRET = 0xC000018A, + MD_NTSTATUS_WIN_STATUS_NO_TRUST_SAM_ACCOUNT = 0xC000018B, + MD_NTSTATUS_WIN_STATUS_TRUSTED_DOMAIN_FAILURE = 0xC000018C, + MD_NTSTATUS_WIN_STATUS_TRUSTED_RELATIONSHIP_FAILURE = 0xC000018D, + MD_NTSTATUS_WIN_STATUS_EVENTLOG_FILE_CORRUPT = 0xC000018E, + MD_NTSTATUS_WIN_STATUS_EVENTLOG_CANT_START = 0xC000018F, + MD_NTSTATUS_WIN_STATUS_TRUST_FAILURE = 0xC0000190, + MD_NTSTATUS_WIN_STATUS_MUTANT_LIMIT_EXCEEDED = 0xC0000191, + MD_NTSTATUS_WIN_STATUS_NETLOGON_NOT_STARTED = 0xC0000192, + MD_NTSTATUS_WIN_STATUS_ACCOUNT_EXPIRED = 0xC0000193, + MD_NTSTATUS_WIN_STATUS_POSSIBLE_DEADLOCK = 0xC0000194, + MD_NTSTATUS_WIN_STATUS_NETWORK_CREDENTIAL_CONFLICT = 0xC0000195, + MD_NTSTATUS_WIN_STATUS_REMOTE_SESSION_LIMIT = 0xC0000196, + MD_NTSTATUS_WIN_STATUS_EVENTLOG_FILE_CHANGED = 0xC0000197, + MD_NTSTATUS_WIN_STATUS_NOLOGON_INTERDOMAIN_TRUST_ACCOUNT = 0xC0000198, + MD_NTSTATUS_WIN_STATUS_NOLOGON_WORKSTATION_TRUST_ACCOUNT = 0xC0000199, + MD_NTSTATUS_WIN_STATUS_NOLOGON_SERVER_TRUST_ACCOUNT = 0xC000019A, + MD_NTSTATUS_WIN_STATUS_DOMAIN_TRUST_INCONSISTENT = 0xC000019B, + MD_NTSTATUS_WIN_STATUS_FS_DRIVER_REQUIRED = 0xC000019C, + MD_NTSTATUS_WIN_STATUS_IMAGE_ALREADY_LOADED_AS_DLL = 0xC000019D, + MD_NTSTATUS_WIN_STATUS_INCOMPATIBLE_WITH_GLOBAL_SHORT_NAME_REGISTRY_SETTING = 0xC000019E, + MD_NTSTATUS_WIN_STATUS_SHORT_NAMES_NOT_ENABLED_ON_VOLUME = 0xC000019F, + MD_NTSTATUS_WIN_STATUS_SECURITY_STREAM_IS_INCONSISTENT = 0xC00001A0, + MD_NTSTATUS_WIN_STATUS_INVALID_LOCK_RANGE = 0xC00001A1, + MD_NTSTATUS_WIN_STATUS_INVALID_ACE_CONDITION = 0xC00001A2, + MD_NTSTATUS_WIN_STATUS_IMAGE_SUBSYSTEM_NOT_PRESENT = 0xC00001A3, + MD_NTSTATUS_WIN_STATUS_NOTIFICATION_GUID_ALREADY_DEFINED = 0xC00001A4, + MD_NTSTATUS_WIN_STATUS_INVALID_EXCEPTION_HANDLER = 0xC00001A5, + MD_NTSTATUS_WIN_STATUS_DUPLICATE_PRIVILEGES = 0xC00001A6, + MD_NTSTATUS_WIN_STATUS_NOT_ALLOWED_ON_SYSTEM_FILE = 0xC00001A7, + MD_NTSTATUS_WIN_STATUS_REPAIR_NEEDED = 0xC00001A8, + MD_NTSTATUS_WIN_STATUS_QUOTA_NOT_ENABLED = 0xC00001A9, + MD_NTSTATUS_WIN_STATUS_NO_APPLICATION_PACKAGE = 0xC00001AA, + MD_NTSTATUS_WIN_STATUS_NETWORK_OPEN_RESTRICTION = 0xC0000201, + MD_NTSTATUS_WIN_STATUS_NO_USER_SESSION_KEY = 0xC0000202, + MD_NTSTATUS_WIN_STATUS_USER_SESSION_DELETED = 0xC0000203, + MD_NTSTATUS_WIN_STATUS_RESOURCE_LANG_NOT_FOUND = 0xC0000204, + MD_NTSTATUS_WIN_STATUS_INSUFF_SERVER_RESOURCES = 0xC0000205, + MD_NTSTATUS_WIN_STATUS_INVALID_BUFFER_SIZE = 0xC0000206, + MD_NTSTATUS_WIN_STATUS_INVALID_ADDRESS_COMPONENT = 0xC0000207, + MD_NTSTATUS_WIN_STATUS_INVALID_ADDRESS_WILDCARD = 0xC0000208, + MD_NTSTATUS_WIN_STATUS_TOO_MANY_ADDRESSES = 0xC0000209, + MD_NTSTATUS_WIN_STATUS_ADDRESS_ALREADY_EXISTS = 0xC000020A, + MD_NTSTATUS_WIN_STATUS_ADDRESS_CLOSED = 0xC000020B, + MD_NTSTATUS_WIN_STATUS_CONNECTION_DISCONNECTED = 0xC000020C, + MD_NTSTATUS_WIN_STATUS_CONNECTION_RESET = 0xC000020D, + MD_NTSTATUS_WIN_STATUS_TOO_MANY_NODES = 0xC000020E, + MD_NTSTATUS_WIN_STATUS_TRANSACTION_ABORTED = 0xC000020F, + MD_NTSTATUS_WIN_STATUS_TRANSACTION_TIMED_OUT = 0xC0000210, + MD_NTSTATUS_WIN_STATUS_TRANSACTION_NO_RELEASE = 0xC0000211, + MD_NTSTATUS_WIN_STATUS_TRANSACTION_NO_MATCH = 0xC0000212, + MD_NTSTATUS_WIN_STATUS_TRANSACTION_RESPONDED = 0xC0000213, + MD_NTSTATUS_WIN_STATUS_TRANSACTION_INVALID_ID = 0xC0000214, + MD_NTSTATUS_WIN_STATUS_TRANSACTION_INVALID_TYPE = 0xC0000215, + MD_NTSTATUS_WIN_STATUS_NOT_SERVER_SESSION = 0xC0000216, + MD_NTSTATUS_WIN_STATUS_NOT_CLIENT_SESSION = 0xC0000217, + MD_NTSTATUS_WIN_STATUS_CANNOT_LOAD_REGISTRY_FILE = 0xC0000218, + MD_NTSTATUS_WIN_STATUS_DEBUG_ATTACH_FAILED = 0xC0000219, + MD_NTSTATUS_WIN_STATUS_SYSTEM_PROCESS_TERMINATED = 0xC000021A, + MD_NTSTATUS_WIN_STATUS_DATA_NOT_ACCEPTED = 0xC000021B, + MD_NTSTATUS_WIN_STATUS_NO_BROWSER_SERVERS_FOUND = 0xC000021C, + MD_NTSTATUS_WIN_STATUS_VDM_HARD_ERROR = 0xC000021D, + MD_NTSTATUS_WIN_STATUS_DRIVER_CANCEL_TIMEOUT = 0xC000021E, + MD_NTSTATUS_WIN_STATUS_REPLY_MESSAGE_MISMATCH = 0xC000021F, + MD_NTSTATUS_WIN_STATUS_MAPPED_ALIGNMENT = 0xC0000220, + MD_NTSTATUS_WIN_STATUS_IMAGE_CHECKSUM_MISMATCH = 0xC0000221, + MD_NTSTATUS_WIN_STATUS_LOST_WRITEBEHIND_DATA = 0xC0000222, + MD_NTSTATUS_WIN_STATUS_CLIENT_SERVER_PARAMETERS_INVALID = 0xC0000223, + MD_NTSTATUS_WIN_STATUS_PASSWORD_MUST_CHANGE = 0xC0000224, + MD_NTSTATUS_WIN_STATUS_NOT_FOUND = 0xC0000225, + MD_NTSTATUS_WIN_STATUS_NOT_TINY_STREAM = 0xC0000226, + MD_NTSTATUS_WIN_STATUS_RECOVERY_FAILURE = 0xC0000227, + MD_NTSTATUS_WIN_STATUS_STACK_OVERFLOW_READ = 0xC0000228, + MD_NTSTATUS_WIN_STATUS_FAIL_CHECK = 0xC0000229, + MD_NTSTATUS_WIN_STATUS_DUPLICATE_OBJECTID = 0xC000022A, + MD_NTSTATUS_WIN_STATUS_OBJECTID_EXISTS = 0xC000022B, + MD_NTSTATUS_WIN_STATUS_CONVERT_TO_LARGE = 0xC000022C, + MD_NTSTATUS_WIN_STATUS_RETRY = 0xC000022D, + MD_NTSTATUS_WIN_STATUS_FOUND_OUT_OF_SCOPE = 0xC000022E, + MD_NTSTATUS_WIN_STATUS_ALLOCATE_BUCKET = 0xC000022F, + MD_NTSTATUS_WIN_STATUS_PROPSET_NOT_FOUND = 0xC0000230, + MD_NTSTATUS_WIN_STATUS_MARSHALL_OVERFLOW = 0xC0000231, + MD_NTSTATUS_WIN_STATUS_INVALID_VARIANT = 0xC0000232, + MD_NTSTATUS_WIN_STATUS_DOMAIN_CONTROLLER_NOT_FOUND = 0xC0000233, + MD_NTSTATUS_WIN_STATUS_ACCOUNT_LOCKED_OUT = 0xC0000234, + MD_NTSTATUS_WIN_STATUS_HANDLE_NOT_CLOSABLE = 0xC0000235, + MD_NTSTATUS_WIN_STATUS_CONNECTION_REFUSED = 0xC0000236, + MD_NTSTATUS_WIN_STATUS_GRACEFUL_DISCONNECT = 0xC0000237, + MD_NTSTATUS_WIN_STATUS_ADDRESS_ALREADY_ASSOCIATED = 0xC0000238, + MD_NTSTATUS_WIN_STATUS_ADDRESS_NOT_ASSOCIATED = 0xC0000239, + MD_NTSTATUS_WIN_STATUS_CONNECTION_INVALID = 0xC000023A, + MD_NTSTATUS_WIN_STATUS_CONNECTION_ACTIVE = 0xC000023B, + MD_NTSTATUS_WIN_STATUS_NETWORK_UNREACHABLE = 0xC000023C, + MD_NTSTATUS_WIN_STATUS_HOST_UNREACHABLE = 0xC000023D, + MD_NTSTATUS_WIN_STATUS_PROTOCOL_UNREACHABLE = 0xC000023E, + MD_NTSTATUS_WIN_STATUS_PORT_UNREACHABLE = 0xC000023F, + MD_NTSTATUS_WIN_STATUS_REQUEST_ABORTED = 0xC0000240, + MD_NTSTATUS_WIN_STATUS_CONNECTION_ABORTED = 0xC0000241, + MD_NTSTATUS_WIN_STATUS_BAD_COMPRESSION_BUFFER = 0xC0000242, + MD_NTSTATUS_WIN_STATUS_USER_MAPPED_FILE = 0xC0000243, + MD_NTSTATUS_WIN_STATUS_AUDIT_FAILED = 0xC0000244, + MD_NTSTATUS_WIN_STATUS_TIMER_RESOLUTION_NOT_SET = 0xC0000245, + MD_NTSTATUS_WIN_STATUS_CONNECTION_COUNT_LIMIT = 0xC0000246, + MD_NTSTATUS_WIN_STATUS_LOGIN_TIME_RESTRICTION = 0xC0000247, + MD_NTSTATUS_WIN_STATUS_LOGIN_WKSTA_RESTRICTION = 0xC0000248, + MD_NTSTATUS_WIN_STATUS_IMAGE_MP_UP_MISMATCH = 0xC0000249, + MD_NTSTATUS_WIN_STATUS_INSUFFICIENT_LOGON_INFO = 0xC0000250, + MD_NTSTATUS_WIN_STATUS_BAD_DLL_ENTRYPOINT = 0xC0000251, + MD_NTSTATUS_WIN_STATUS_BAD_SERVICE_ENTRYPOINT = 0xC0000252, + MD_NTSTATUS_WIN_STATUS_LPC_REPLY_LOST = 0xC0000253, + MD_NTSTATUS_WIN_STATUS_IP_ADDRESS_CONFLICT1 = 0xC0000254, + MD_NTSTATUS_WIN_STATUS_IP_ADDRESS_CONFLICT2 = 0xC0000255, + MD_NTSTATUS_WIN_STATUS_REGISTRY_QUOTA_LIMIT = 0xC0000256, + MD_NTSTATUS_WIN_STATUS_PATH_NOT_COVERED = 0xC0000257, + MD_NTSTATUS_WIN_STATUS_NO_CALLBACK_ACTIVE = 0xC0000258, + MD_NTSTATUS_WIN_STATUS_LICENSE_QUOTA_EXCEEDED = 0xC0000259, + MD_NTSTATUS_WIN_STATUS_PWD_TOO_SHORT = 0xC000025A, + MD_NTSTATUS_WIN_STATUS_PWD_TOO_RECENT = 0xC000025B, + MD_NTSTATUS_WIN_STATUS_PWD_HISTORY_CONFLICT = 0xC000025C, + MD_NTSTATUS_WIN_STATUS_PLUGPLAY_NO_DEVICE = 0xC000025E, + MD_NTSTATUS_WIN_STATUS_UNSUPPORTED_COMPRESSION = 0xC000025F, + MD_NTSTATUS_WIN_STATUS_INVALID_HW_PROFILE = 0xC0000260, + MD_NTSTATUS_WIN_STATUS_INVALID_PLUGPLAY_DEVICE_PATH = 0xC0000261, + MD_NTSTATUS_WIN_STATUS_DRIVER_ORDINAL_NOT_FOUND = 0xC0000262, + MD_NTSTATUS_WIN_STATUS_DRIVER_ENTRYPOINT_NOT_FOUND = 0xC0000263, + MD_NTSTATUS_WIN_STATUS_RESOURCE_NOT_OWNED = 0xC0000264, + MD_NTSTATUS_WIN_STATUS_TOO_MANY_LINKS = 0xC0000265, + MD_NTSTATUS_WIN_STATUS_QUOTA_LIST_INCONSISTENT = 0xC0000266, + MD_NTSTATUS_WIN_STATUS_FILE_IS_OFFLINE = 0xC0000267, + MD_NTSTATUS_WIN_STATUS_EVALUATION_EXPIRATION = 0xC0000268, + MD_NTSTATUS_WIN_STATUS_ILLEGAL_DLL_RELOCATION = 0xC0000269, + MD_NTSTATUS_WIN_STATUS_LICENSE_VIOLATION = 0xC000026A, + MD_NTSTATUS_WIN_STATUS_DLL_INIT_FAILED_LOGOFF = 0xC000026B, + MD_NTSTATUS_WIN_STATUS_DRIVER_UNABLE_TO_LOAD = 0xC000026C, + MD_NTSTATUS_WIN_STATUS_DFS_UNAVAILABLE = 0xC000026D, + MD_NTSTATUS_WIN_STATUS_VOLUME_DISMOUNTED = 0xC000026E, + MD_NTSTATUS_WIN_STATUS_WX86_INTERNAL_ERROR = 0xC000026F, + MD_NTSTATUS_WIN_STATUS_WX86_FLOAT_STACK_CHECK = 0xC0000270, + MD_NTSTATUS_WIN_STATUS_VALIDATE_CONTINUE = 0xC0000271, + MD_NTSTATUS_WIN_STATUS_NO_MATCH = 0xC0000272, + MD_NTSTATUS_WIN_STATUS_NO_MORE_MATCHES = 0xC0000273, + MD_NTSTATUS_WIN_STATUS_NOT_A_REPARSE_POINT = 0xC0000275, + MD_NTSTATUS_WIN_STATUS_IO_REPARSE_TAG_INVALID = 0xC0000276, + MD_NTSTATUS_WIN_STATUS_IO_REPARSE_TAG_MISMATCH = 0xC0000277, + MD_NTSTATUS_WIN_STATUS_IO_REPARSE_DATA_INVALID = 0xC0000278, + MD_NTSTATUS_WIN_STATUS_IO_REPARSE_TAG_NOT_HANDLED = 0xC0000279, + MD_NTSTATUS_WIN_STATUS_PWD_TOO_LONG = 0xC000027A, + MD_NTSTATUS_WIN_STATUS_STOWED_EXCEPTION = 0xC000027B, + MD_NTSTATUS_WIN_STATUS_REPARSE_POINT_NOT_RESOLVED = 0xC0000280, + MD_NTSTATUS_WIN_STATUS_DIRECTORY_IS_A_REPARSE_POINT = 0xC0000281, + MD_NTSTATUS_WIN_STATUS_RANGE_LIST_CONFLICT = 0xC0000282, + MD_NTSTATUS_WIN_STATUS_SOURCE_ELEMENT_EMPTY = 0xC0000283, + MD_NTSTATUS_WIN_STATUS_DESTINATION_ELEMENT_FULL = 0xC0000284, + MD_NTSTATUS_WIN_STATUS_ILLEGAL_ELEMENT_ADDRESS = 0xC0000285, + MD_NTSTATUS_WIN_STATUS_MAGAZINE_NOT_PRESENT = 0xC0000286, + MD_NTSTATUS_WIN_STATUS_REINITIALIZATION_NEEDED = 0xC0000287, + MD_NTSTATUS_WIN_STATUS_ENCRYPTION_FAILED = 0xC000028A, + MD_NTSTATUS_WIN_STATUS_DECRYPTION_FAILED = 0xC000028B, + MD_NTSTATUS_WIN_STATUS_RANGE_NOT_FOUND = 0xC000028C, + MD_NTSTATUS_WIN_STATUS_NO_RECOVERY_POLICY = 0xC000028D, + MD_NTSTATUS_WIN_STATUS_NO_EFS = 0xC000028E, + MD_NTSTATUS_WIN_STATUS_WRONG_EFS = 0xC000028F, + MD_NTSTATUS_WIN_STATUS_NO_USER_KEYS = 0xC0000290, + MD_NTSTATUS_WIN_STATUS_FILE_NOT_ENCRYPTED = 0xC0000291, + MD_NTSTATUS_WIN_STATUS_NOT_EXPORT_FORMAT = 0xC0000292, + MD_NTSTATUS_WIN_STATUS_FILE_ENCRYPTED = 0xC0000293, + MD_NTSTATUS_WIN_STATUS_WMI_GUID_NOT_FOUND = 0xC0000295, + MD_NTSTATUS_WIN_STATUS_WMI_INSTANCE_NOT_FOUND = 0xC0000296, + MD_NTSTATUS_WIN_STATUS_WMI_ITEMID_NOT_FOUND = 0xC0000297, + MD_NTSTATUS_WIN_STATUS_WMI_TRY_AGAIN = 0xC0000298, + MD_NTSTATUS_WIN_STATUS_SHARED_POLICY = 0xC0000299, + MD_NTSTATUS_WIN_STATUS_POLICY_OBJECT_NOT_FOUND = 0xC000029A, + MD_NTSTATUS_WIN_STATUS_POLICY_ONLY_IN_DS = 0xC000029B, + MD_NTSTATUS_WIN_STATUS_VOLUME_NOT_UPGRADED = 0xC000029C, + MD_NTSTATUS_WIN_STATUS_REMOTE_STORAGE_NOT_ACTIVE = 0xC000029D, + MD_NTSTATUS_WIN_STATUS_REMOTE_STORAGE_MEDIA_ERROR = 0xC000029E, + MD_NTSTATUS_WIN_STATUS_NO_TRACKING_SERVICE = 0xC000029F, + MD_NTSTATUS_WIN_STATUS_SERVER_SID_MISMATCH = 0xC00002A0, + MD_NTSTATUS_WIN_STATUS_DS_NO_ATTRIBUTE_OR_VALUE = 0xC00002A1, + MD_NTSTATUS_WIN_STATUS_DS_INVALID_ATTRIBUTE_SYNTAX = 0xC00002A2, + MD_NTSTATUS_WIN_STATUS_DS_ATTRIBUTE_TYPE_UNDEFINED = 0xC00002A3, + MD_NTSTATUS_WIN_STATUS_DS_ATTRIBUTE_OR_VALUE_EXISTS = 0xC00002A4, + MD_NTSTATUS_WIN_STATUS_DS_BUSY = 0xC00002A5, + MD_NTSTATUS_WIN_STATUS_DS_UNAVAILABLE = 0xC00002A6, + MD_NTSTATUS_WIN_STATUS_DS_NO_RIDS_ALLOCATED = 0xC00002A7, + MD_NTSTATUS_WIN_STATUS_DS_NO_MORE_RIDS = 0xC00002A8, + MD_NTSTATUS_WIN_STATUS_DS_INCORRECT_ROLE_OWNER = 0xC00002A9, + MD_NTSTATUS_WIN_STATUS_DS_RIDMGR_INIT_ERROR = 0xC00002AA, + MD_NTSTATUS_WIN_STATUS_DS_OBJ_CLASS_VIOLATION = 0xC00002AB, + MD_NTSTATUS_WIN_STATUS_DS_CANT_ON_NON_LEAF = 0xC00002AC, + MD_NTSTATUS_WIN_STATUS_DS_CANT_ON_RDN = 0xC00002AD, + MD_NTSTATUS_WIN_STATUS_DS_CANT_MOD_OBJ_CLASS = 0xC00002AE, + MD_NTSTATUS_WIN_STATUS_DS_CROSS_DOM_MOVE_FAILED = 0xC00002AF, + MD_NTSTATUS_WIN_STATUS_DS_GC_NOT_AVAILABLE = 0xC00002B0, + MD_NTSTATUS_WIN_STATUS_DIRECTORY_SERVICE_REQUIRED = 0xC00002B1, + MD_NTSTATUS_WIN_STATUS_REPARSE_ATTRIBUTE_CONFLICT = 0xC00002B2, + MD_NTSTATUS_WIN_STATUS_CANT_ENABLE_DENY_ONLY = 0xC00002B3, + MD_NTSTATUS_WIN_STATUS_FLOAT_MULTIPLE_FAULTS = 0xC00002B4, + MD_NTSTATUS_WIN_STATUS_FLOAT_MULTIPLE_TRAPS = 0xC00002B5, + MD_NTSTATUS_WIN_STATUS_DEVICE_REMOVED = 0xC00002B6, + MD_NTSTATUS_WIN_STATUS_JOURNAL_DELETE_IN_PROGRESS = 0xC00002B7, + MD_NTSTATUS_WIN_STATUS_JOURNAL_NOT_ACTIVE = 0xC00002B8, + MD_NTSTATUS_WIN_STATUS_NOINTERFACE = 0xC00002B9, + MD_NTSTATUS_WIN_STATUS_DS_RIDMGR_DISABLED = 0xC00002BA, + MD_NTSTATUS_WIN_STATUS_DS_ADMIN_LIMIT_EXCEEDED = 0xC00002C1, + MD_NTSTATUS_WIN_STATUS_DRIVER_FAILED_SLEEP = 0xC00002C2, + MD_NTSTATUS_WIN_STATUS_MUTUAL_AUTHENTICATION_FAILED = 0xC00002C3, + MD_NTSTATUS_WIN_STATUS_CORRUPT_SYSTEM_FILE = 0xC00002C4, + MD_NTSTATUS_WIN_STATUS_DATATYPE_MISALIGNMENT_ERROR = 0xC00002C5, + MD_NTSTATUS_WIN_STATUS_WMI_READ_ONLY = 0xC00002C6, + MD_NTSTATUS_WIN_STATUS_WMI_SET_FAILURE = 0xC00002C7, + MD_NTSTATUS_WIN_STATUS_COMMITMENT_MINIMUM = 0xC00002C8, + MD_NTSTATUS_WIN_STATUS_REG_NAT_CONSUMPTION = 0xC00002C9, + MD_NTSTATUS_WIN_STATUS_TRANSPORT_FULL = 0xC00002CA, + MD_NTSTATUS_WIN_STATUS_DS_SAM_INIT_FAILURE = 0xC00002CB, + MD_NTSTATUS_WIN_STATUS_ONLY_IF_CONNECTED = 0xC00002CC, + MD_NTSTATUS_WIN_STATUS_DS_SENSITIVE_GROUP_VIOLATION = 0xC00002CD, + MD_NTSTATUS_WIN_STATUS_PNP_RESTART_ENUMERATION = 0xC00002CE, + MD_NTSTATUS_WIN_STATUS_JOURNAL_ENTRY_DELETED = 0xC00002CF, + MD_NTSTATUS_WIN_STATUS_DS_CANT_MOD_PRIMARYGROUPID = 0xC00002D0, + MD_NTSTATUS_WIN_STATUS_SYSTEM_IMAGE_BAD_SIGNATURE = 0xC00002D1, + MD_NTSTATUS_WIN_STATUS_PNP_REBOOT_REQUIRED = 0xC00002D2, + MD_NTSTATUS_WIN_STATUS_POWER_STATE_INVALID = 0xC00002D3, + MD_NTSTATUS_WIN_STATUS_DS_INVALID_GROUP_TYPE = 0xC00002D4, + MD_NTSTATUS_WIN_STATUS_DS_NO_NEST_GLOBALGROUP_IN_MIXEDDOMAIN = 0xC00002D5, + MD_NTSTATUS_WIN_STATUS_DS_NO_NEST_LOCALGROUP_IN_MIXEDDOMAIN = 0xC00002D6, + MD_NTSTATUS_WIN_STATUS_DS_GLOBAL_CANT_HAVE_LOCAL_MEMBER = 0xC00002D7, + MD_NTSTATUS_WIN_STATUS_DS_GLOBAL_CANT_HAVE_UNIVERSAL_MEMBER = 0xC00002D8, + MD_NTSTATUS_WIN_STATUS_DS_UNIVERSAL_CANT_HAVE_LOCAL_MEMBER = 0xC00002D9, + MD_NTSTATUS_WIN_STATUS_DS_GLOBAL_CANT_HAVE_CROSSDOMAIN_MEMBER = 0xC00002DA, + MD_NTSTATUS_WIN_STATUS_DS_LOCAL_CANT_HAVE_CROSSDOMAIN_LOCAL_MEMBER = 0xC00002DB, + MD_NTSTATUS_WIN_STATUS_DS_HAVE_PRIMARY_MEMBERS = 0xC00002DC, + MD_NTSTATUS_WIN_STATUS_WMI_NOT_SUPPORTED = 0xC00002DD, + MD_NTSTATUS_WIN_STATUS_INSUFFICIENT_POWER = 0xC00002DE, + MD_NTSTATUS_WIN_STATUS_SAM_NEED_BOOTKEY_PASSWORD = 0xC00002DF, + MD_NTSTATUS_WIN_STATUS_SAM_NEED_BOOTKEY_FLOPPY = 0xC00002E0, + MD_NTSTATUS_WIN_STATUS_DS_CANT_START = 0xC00002E1, + MD_NTSTATUS_WIN_STATUS_DS_INIT_FAILURE = 0xC00002E2, + MD_NTSTATUS_WIN_STATUS_SAM_INIT_FAILURE = 0xC00002E3, + MD_NTSTATUS_WIN_STATUS_DS_GC_REQUIRED = 0xC00002E4, + MD_NTSTATUS_WIN_STATUS_DS_LOCAL_MEMBER_OF_LOCAL_ONLY = 0xC00002E5, + MD_NTSTATUS_WIN_STATUS_DS_NO_FPO_IN_UNIVERSAL_GROUPS = 0xC00002E6, + MD_NTSTATUS_WIN_STATUS_DS_MACHINE_ACCOUNT_QUOTA_EXCEEDED = 0xC00002E7, + MD_NTSTATUS_WIN_STATUS_MULTIPLE_FAULT_VIOLATION = 0xC00002E8, + MD_NTSTATUS_WIN_STATUS_CURRENT_DOMAIN_NOT_ALLOWED = 0xC00002E9, + MD_NTSTATUS_WIN_STATUS_CANNOT_MAKE = 0xC00002EA, + MD_NTSTATUS_WIN_STATUS_SYSTEM_SHUTDOWN = 0xC00002EB, + MD_NTSTATUS_WIN_STATUS_DS_INIT_FAILURE_CONSOLE = 0xC00002EC, + MD_NTSTATUS_WIN_STATUS_DS_SAM_INIT_FAILURE_CONSOLE = 0xC00002ED, + MD_NTSTATUS_WIN_STATUS_UNFINISHED_CONTEXT_DELETED = 0xC00002EE, + MD_NTSTATUS_WIN_STATUS_NO_TGT_REPLY = 0xC00002EF, + MD_NTSTATUS_WIN_STATUS_OBJECTID_NOT_FOUND = 0xC00002F0, + MD_NTSTATUS_WIN_STATUS_NO_IP_ADDRESSES = 0xC00002F1, + MD_NTSTATUS_WIN_STATUS_WRONG_CREDENTIAL_HANDLE = 0xC00002F2, + MD_NTSTATUS_WIN_STATUS_CRYPTO_SYSTEM_INVALID = 0xC00002F3, + MD_NTSTATUS_WIN_STATUS_MAX_REFERRALS_EXCEEDED = 0xC00002F4, + MD_NTSTATUS_WIN_STATUS_MUST_BE_KDC = 0xC00002F5, + MD_NTSTATUS_WIN_STATUS_STRONG_CRYPTO_NOT_SUPPORTED = 0xC00002F6, + MD_NTSTATUS_WIN_STATUS_TOO_MANY_PRINCIPALS = 0xC00002F7, + MD_NTSTATUS_WIN_STATUS_NO_PA_DATA = 0xC00002F8, + MD_NTSTATUS_WIN_STATUS_PKINIT_NAME_MISMATCH = 0xC00002F9, + MD_NTSTATUS_WIN_STATUS_SMARTCARD_LOGON_REQUIRED = 0xC00002FA, + MD_NTSTATUS_WIN_STATUS_KDC_INVALID_REQUEST = 0xC00002FB, + MD_NTSTATUS_WIN_STATUS_KDC_UNABLE_TO_REFER = 0xC00002FC, + MD_NTSTATUS_WIN_STATUS_KDC_UNKNOWN_ETYPE = 0xC00002FD, + MD_NTSTATUS_WIN_STATUS_SHUTDOWN_IN_PROGRESS = 0xC00002FE, + MD_NTSTATUS_WIN_STATUS_SERVER_SHUTDOWN_IN_PROGRESS = 0xC00002FF, + MD_NTSTATUS_WIN_STATUS_NOT_SUPPORTED_ON_SBS = 0xC0000300, + MD_NTSTATUS_WIN_STATUS_WMI_GUID_DISCONNECTED = 0xC0000301, + MD_NTSTATUS_WIN_STATUS_WMI_ALREADY_DISABLED = 0xC0000302, + MD_NTSTATUS_WIN_STATUS_WMI_ALREADY_ENABLED = 0xC0000303, + MD_NTSTATUS_WIN_STATUS_MFT_TOO_FRAGMENTED = 0xC0000304, + MD_NTSTATUS_WIN_STATUS_COPY_PROTECTION_FAILURE = 0xC0000305, + MD_NTSTATUS_WIN_STATUS_CSS_AUTHENTICATION_FAILURE = 0xC0000306, + MD_NTSTATUS_WIN_STATUS_CSS_KEY_NOT_PRESENT = 0xC0000307, + MD_NTSTATUS_WIN_STATUS_CSS_KEY_NOT_ESTABLISHED = 0xC0000308, + MD_NTSTATUS_WIN_STATUS_CSS_SCRAMBLED_SECTOR = 0xC0000309, + MD_NTSTATUS_WIN_STATUS_CSS_REGION_MISMATCH = 0xC000030A, + MD_NTSTATUS_WIN_STATUS_CSS_RESETS_EXHAUSTED = 0xC000030B, + MD_NTSTATUS_WIN_STATUS_PASSWORD_CHANGE_REQUIRED = 0xC000030C, + MD_NTSTATUS_WIN_STATUS_PKINIT_FAILURE = 0xC0000320, + MD_NTSTATUS_WIN_STATUS_SMARTCARD_SUBSYSTEM_FAILURE = 0xC0000321, + MD_NTSTATUS_WIN_STATUS_NO_KERB_KEY = 0xC0000322, + MD_NTSTATUS_WIN_STATUS_HOST_DOWN = 0xC0000350, + MD_NTSTATUS_WIN_STATUS_UNSUPPORTED_PREAUTH = 0xC0000351, + MD_NTSTATUS_WIN_STATUS_EFS_ALG_BLOB_TOO_BIG = 0xC0000352, + MD_NTSTATUS_WIN_STATUS_PORT_NOT_SET = 0xC0000353, + MD_NTSTATUS_WIN_STATUS_DEBUGGER_INACTIVE = 0xC0000354, + MD_NTSTATUS_WIN_STATUS_DS_VERSION_CHECK_FAILURE = 0xC0000355, + MD_NTSTATUS_WIN_STATUS_AUDITING_DISABLED = 0xC0000356, + MD_NTSTATUS_WIN_STATUS_PRENT4_MACHINE_ACCOUNT = 0xC0000357, + MD_NTSTATUS_WIN_STATUS_DS_AG_CANT_HAVE_UNIVERSAL_MEMBER = 0xC0000358, + MD_NTSTATUS_WIN_STATUS_INVALID_IMAGE_WIN_32 = 0xC0000359, + MD_NTSTATUS_WIN_STATUS_INVALID_IMAGE_WIN_64 = 0xC000035A, + MD_NTSTATUS_WIN_STATUS_BAD_BINDINGS = 0xC000035B, + MD_NTSTATUS_WIN_STATUS_NETWORK_SESSION_EXPIRED = 0xC000035C, + MD_NTSTATUS_WIN_STATUS_APPHELP_BLOCK = 0xC000035D, + MD_NTSTATUS_WIN_STATUS_ALL_SIDS_FILTERED = 0xC000035E, + MD_NTSTATUS_WIN_STATUS_NOT_SAFE_MODE_DRIVER = 0xC000035F, + MD_NTSTATUS_WIN_STATUS_ACCESS_DISABLED_BY_POLICY_DEFAULT = 0xC0000361, + MD_NTSTATUS_WIN_STATUS_ACCESS_DISABLED_BY_POLICY_PATH = 0xC0000362, + MD_NTSTATUS_WIN_STATUS_ACCESS_DISABLED_BY_POLICY_PUBLISHER = 0xC0000363, + MD_NTSTATUS_WIN_STATUS_ACCESS_DISABLED_BY_POLICY_OTHER = 0xC0000364, + MD_NTSTATUS_WIN_STATUS_FAILED_DRIVER_ENTRY = 0xC0000365, + MD_NTSTATUS_WIN_STATUS_DEVICE_ENUMERATION_ERROR = 0xC0000366, + MD_NTSTATUS_WIN_STATUS_MOUNT_POINT_NOT_RESOLVED = 0xC0000368, + MD_NTSTATUS_WIN_STATUS_INVALID_DEVICE_OBJECT_PARAMETER = 0xC0000369, + MD_NTSTATUS_WIN_STATUS_MCA_OCCURED = 0xC000036A, + MD_NTSTATUS_WIN_STATUS_DRIVER_BLOCKED_CRITICAL = 0xC000036B, + MD_NTSTATUS_WIN_STATUS_DRIVER_BLOCKED = 0xC000036C, + MD_NTSTATUS_WIN_STATUS_DRIVER_DATABASE_ERROR = 0xC000036D, + MD_NTSTATUS_WIN_STATUS_SYSTEM_HIVE_TOO_LARGE = 0xC000036E, + MD_NTSTATUS_WIN_STATUS_INVALID_IMPORT_OF_NON_DLL = 0xC000036F, + MD_NTSTATUS_WIN_STATUS_NO_SECRETS = 0xC0000371, + MD_NTSTATUS_WIN_STATUS_ACCESS_DISABLED_NO_SAFER_UI_BY_POLICY = 0xC0000372, + MD_NTSTATUS_WIN_STATUS_FAILED_STACK_SWITCH = 0xC0000373, + MD_NTSTATUS_WIN_STATUS_HEAP_CORRUPTION = 0xC0000374, + MD_NTSTATUS_WIN_STATUS_SMARTCARD_WRONG_PIN = 0xC0000380, + MD_NTSTATUS_WIN_STATUS_SMARTCARD_CARD_BLOCKED = 0xC0000381, + MD_NTSTATUS_WIN_STATUS_SMARTCARD_CARD_NOT_AUTHENTICATED = 0xC0000382, + MD_NTSTATUS_WIN_STATUS_SMARTCARD_NO_CARD = 0xC0000383, + MD_NTSTATUS_WIN_STATUS_SMARTCARD_NO_KEY_CONTAINER = 0xC0000384, + MD_NTSTATUS_WIN_STATUS_SMARTCARD_NO_CERTIFICATE = 0xC0000385, + MD_NTSTATUS_WIN_STATUS_SMARTCARD_NO_KEYSET = 0xC0000386, + MD_NTSTATUS_WIN_STATUS_SMARTCARD_IO_ERROR = 0xC0000387, + MD_NTSTATUS_WIN_STATUS_DOWNGRADE_DETECTED = 0xC0000388, + MD_NTSTATUS_WIN_STATUS_SMARTCARD_CERT_REVOKED = 0xC0000389, + MD_NTSTATUS_WIN_STATUS_ISSUING_CA_UNTRUSTED = 0xC000038A, + MD_NTSTATUS_WIN_STATUS_REVOCATION_OFFLINE_C = 0xC000038B, + MD_NTSTATUS_WIN_STATUS_PKINIT_CLIENT_FAILURE = 0xC000038C, + MD_NTSTATUS_WIN_STATUS_SMARTCARD_CERT_EXPIRED = 0xC000038D, + MD_NTSTATUS_WIN_STATUS_DRIVER_FAILED_PRIOR_UNLOAD = 0xC000038E, + MD_NTSTATUS_WIN_STATUS_SMARTCARD_SILENT_CONTEXT = 0xC000038F, + MD_NTSTATUS_WIN_STATUS_PER_USER_TRUST_QUOTA_EXCEEDED = 0xC0000401, + MD_NTSTATUS_WIN_STATUS_ALL_USER_TRUST_QUOTA_EXCEEDED = 0xC0000402, + MD_NTSTATUS_WIN_STATUS_USER_DELETE_TRUST_QUOTA_EXCEEDED = 0xC0000403, + MD_NTSTATUS_WIN_STATUS_DS_NAME_NOT_UNIQUE = 0xC0000404, + MD_NTSTATUS_WIN_STATUS_DS_DUPLICATE_ID_FOUND = 0xC0000405, + MD_NTSTATUS_WIN_STATUS_DS_GROUP_CONVERSION_ERROR = 0xC0000406, + MD_NTSTATUS_WIN_STATUS_VOLSNAP_PREPARE_HIBERNATE = 0xC0000407, + MD_NTSTATUS_WIN_STATUS_USER2USER_REQUIRED = 0xC0000408, + MD_NTSTATUS_WIN_STATUS_STACK_BUFFER_OVERRUN = 0xC0000409, + MD_NTSTATUS_WIN_STATUS_NO_S4U_PROT_SUPPORT = 0xC000040A, + MD_NTSTATUS_WIN_STATUS_CROSSREALM_DELEGATION_FAILURE = 0xC000040B, + MD_NTSTATUS_WIN_STATUS_REVOCATION_OFFLINE_KDC = 0xC000040C, + MD_NTSTATUS_WIN_STATUS_ISSUING_CA_UNTRUSTED_KDC = 0xC000040D, + MD_NTSTATUS_WIN_STATUS_KDC_CERT_EXPIRED = 0xC000040E, + MD_NTSTATUS_WIN_STATUS_KDC_CERT_REVOKED = 0xC000040F, + MD_NTSTATUS_WIN_STATUS_PARAMETER_QUOTA_EXCEEDED = 0xC0000410, + MD_NTSTATUS_WIN_STATUS_HIBERNATION_FAILURE = 0xC0000411, + MD_NTSTATUS_WIN_STATUS_DELAY_LOAD_FAILED = 0xC0000412, + MD_NTSTATUS_WIN_STATUS_AUTHENTICATION_FIREWALL_FAILED = 0xC0000413, + MD_NTSTATUS_WIN_STATUS_VDM_DISALLOWED = 0xC0000414, + MD_NTSTATUS_WIN_STATUS_HUNG_DISPLAY_DRIVER_THREAD = 0xC0000415, + MD_NTSTATUS_WIN_STATUS_INSUFFICIENT_RESOURCE_FOR_SPECIFIED_SHARED_SECTION_SIZE = 0xC0000416, + MD_NTSTATUS_WIN_STATUS_INVALID_CRUNTIME_PARAMETER = 0xC0000417, + MD_NTSTATUS_WIN_STATUS_NTLM_BLOCKED = 0xC0000418, + MD_NTSTATUS_WIN_STATUS_DS_SRC_SID_EXISTS_IN_FOREST = 0xC0000419, + MD_NTSTATUS_WIN_STATUS_DS_DOMAIN_NAME_EXISTS_IN_FOREST = 0xC000041A, + MD_NTSTATUS_WIN_STATUS_DS_FLAT_NAME_EXISTS_IN_FOREST = 0xC000041B, + MD_NTSTATUS_WIN_STATUS_INVALID_USER_PRINCIPAL_NAME = 0xC000041C, + MD_NTSTATUS_WIN_STATUS_FATAL_USER_CALLBACK_EXCEPTION = 0xC000041D, + MD_NTSTATUS_WIN_STATUS_ASSERTION_FAILURE = 0xC0000420, + MD_NTSTATUS_WIN_STATUS_VERIFIER_STOP = 0xC0000421, + MD_NTSTATUS_WIN_STATUS_CALLBACK_POP_STACK = 0xC0000423, + MD_NTSTATUS_WIN_STATUS_INCOMPATIBLE_DRIVER_BLOCKED = 0xC0000424, + MD_NTSTATUS_WIN_STATUS_HIVE_UNLOADED = 0xC0000425, + MD_NTSTATUS_WIN_STATUS_COMPRESSION_DISABLED = 0xC0000426, + MD_NTSTATUS_WIN_STATUS_FILE_SYSTEM_LIMITATION = 0xC0000427, + MD_NTSTATUS_WIN_STATUS_INVALID_IMAGE_HASH = 0xC0000428, + MD_NTSTATUS_WIN_STATUS_NOT_CAPABLE = 0xC0000429, + MD_NTSTATUS_WIN_STATUS_REQUEST_OUT_OF_SEQUENCE = 0xC000042A, + MD_NTSTATUS_WIN_STATUS_IMPLEMENTATION_LIMIT = 0xC000042B, + MD_NTSTATUS_WIN_STATUS_ELEVATION_REQUIRED = 0xC000042C, + MD_NTSTATUS_WIN_STATUS_NO_SECURITY_CONTEXT = 0xC000042D, + MD_NTSTATUS_WIN_STATUS_PKU2U_CERT_FAILURE = 0xC000042F, + MD_NTSTATUS_WIN_STATUS_BEYOND_VDL = 0xC0000432, + MD_NTSTATUS_WIN_STATUS_ENCOUNTERED_WRITE_IN_PROGRESS = 0xC0000433, + MD_NTSTATUS_WIN_STATUS_PTE_CHANGED = 0xC0000434, + MD_NTSTATUS_WIN_STATUS_PURGE_FAILED = 0xC0000435, + MD_NTSTATUS_WIN_STATUS_CRED_REQUIRES_CONFIRMATION = 0xC0000440, + MD_NTSTATUS_WIN_STATUS_CS_ENCRYPTION_INVALID_SERVER_RESPONSE = 0xC0000441, + MD_NTSTATUS_WIN_STATUS_CS_ENCRYPTION_UNSUPPORTED_SERVER = 0xC0000442, + MD_NTSTATUS_WIN_STATUS_CS_ENCRYPTION_EXISTING_ENCRYPTED_FILE = 0xC0000443, + MD_NTSTATUS_WIN_STATUS_CS_ENCRYPTION_NEW_ENCRYPTED_FILE = 0xC0000444, + MD_NTSTATUS_WIN_STATUS_CS_ENCRYPTION_FILE_NOT_CSE = 0xC0000445, + MD_NTSTATUS_WIN_STATUS_INVALID_LABEL = 0xC0000446, + MD_NTSTATUS_WIN_STATUS_DRIVER_PROCESS_TERMINATED = 0xC0000450, + MD_NTSTATUS_WIN_STATUS_AMBIGUOUS_SYSTEM_DEVICE = 0xC0000451, + MD_NTSTATUS_WIN_STATUS_SYSTEM_DEVICE_NOT_FOUND = 0xC0000452, + MD_NTSTATUS_WIN_STATUS_RESTART_BOOT_APPLICATION = 0xC0000453, + MD_NTSTATUS_WIN_STATUS_INSUFFICIENT_NVRAM_RESOURCES = 0xC0000454, + MD_NTSTATUS_WIN_STATUS_INVALID_SESSION = 0xC0000455, + MD_NTSTATUS_WIN_STATUS_THREAD_ALREADY_IN_SESSION = 0xC0000456, + MD_NTSTATUS_WIN_STATUS_THREAD_NOT_IN_SESSION = 0xC0000457, + MD_NTSTATUS_WIN_STATUS_INVALID_WEIGHT = 0xC0000458, + MD_NTSTATUS_WIN_STATUS_REQUEST_PAUSED = 0xC0000459, + MD_NTSTATUS_WIN_STATUS_NO_RANGES_PROCESSED = 0xC0000460, + MD_NTSTATUS_WIN_STATUS_DISK_RESOURCES_EXHAUSTED = 0xC0000461, + MD_NTSTATUS_WIN_STATUS_NEEDS_REMEDIATION = 0xC0000462, + MD_NTSTATUS_WIN_STATUS_DEVICE_FEATURE_NOT_SUPPORTED = 0xC0000463, + MD_NTSTATUS_WIN_STATUS_DEVICE_UNREACHABLE = 0xC0000464, + MD_NTSTATUS_WIN_STATUS_INVALID_TOKEN = 0xC0000465, + MD_NTSTATUS_WIN_STATUS_SERVER_UNAVAILABLE = 0xC0000466, + MD_NTSTATUS_WIN_STATUS_FILE_NOT_AVAILABLE = 0xC0000467, + MD_NTSTATUS_WIN_STATUS_DEVICE_INSUFFICIENT_RESOURCES = 0xC0000468, + MD_NTSTATUS_WIN_STATUS_PACKAGE_UPDATING = 0xC0000469, + MD_NTSTATUS_WIN_STATUS_NOT_READ_FROM_COPY = 0xC000046A, + MD_NTSTATUS_WIN_STATUS_FT_WRITE_FAILURE = 0xC000046B, + MD_NTSTATUS_WIN_STATUS_FT_DI_SCAN_REQUIRED = 0xC000046C, + MD_NTSTATUS_WIN_STATUS_OBJECT_NOT_EXTERNALLY_BACKED = 0xC000046D, + MD_NTSTATUS_WIN_STATUS_EXTERNAL_BACKING_PROVIDER_UNKNOWN = 0xC000046E, + MD_NTSTATUS_WIN_STATUS_DATA_CHECKSUM_ERROR = 0xC0000470, + MD_NTSTATUS_WIN_STATUS_INTERMIXED_KERNEL_EA_OPERATION = 0xC0000471, + MD_NTSTATUS_WIN_STATUS_TRIM_READ_ZERO_NOT_SUPPORTED = 0xC0000472, + MD_NTSTATUS_WIN_STATUS_TOO_MANY_SEGMENT_DESCRIPTORS = 0xC0000473, + MD_NTSTATUS_WIN_STATUS_INVALID_OFFSET_ALIGNMENT = 0xC0000474, + MD_NTSTATUS_WIN_STATUS_INVALID_FIELD_IN_PARAMETER_LIST = 0xC0000475, + MD_NTSTATUS_WIN_STATUS_OPERATION_IN_PROGRESS = 0xC0000476, + MD_NTSTATUS_WIN_STATUS_INVALID_INITIATOR_TARGET_PATH = 0xC0000477, + MD_NTSTATUS_WIN_STATUS_SCRUB_DATA_DISABLED = 0xC0000478, + MD_NTSTATUS_WIN_STATUS_NOT_REDUNDANT_STORAGE = 0xC0000479, + MD_NTSTATUS_WIN_STATUS_RESIDENT_FILE_NOT_SUPPORTED = 0xC000047A, + MD_NTSTATUS_WIN_STATUS_COMPRESSED_FILE_NOT_SUPPORTED = 0xC000047B, + MD_NTSTATUS_WIN_STATUS_DIRECTORY_NOT_SUPPORTED = 0xC000047C, + MD_NTSTATUS_WIN_STATUS_IO_OPERATION_TIMEOUT = 0xC000047D, + MD_NTSTATUS_WIN_STATUS_SYSTEM_NEEDS_REMEDIATION = 0xC000047E, + MD_NTSTATUS_WIN_STATUS_APPX_INTEGRITY_FAILURE_CLR_NGEN = 0xC000047F, + MD_NTSTATUS_WIN_STATUS_SHARE_UNAVAILABLE = 0xC0000480, + MD_NTSTATUS_WIN_STATUS_APISET_NOT_HOSTED = 0xC0000481, + MD_NTSTATUS_WIN_STATUS_APISET_NOT_PRESENT = 0xC0000482, + MD_NTSTATUS_WIN_STATUS_DEVICE_HARDWARE_ERROR = 0xC0000483, + MD_NTSTATUS_WIN_STATUS_INVALID_TASK_NAME = 0xC0000500, + MD_NTSTATUS_WIN_STATUS_INVALID_TASK_INDEX = 0xC0000501, + MD_NTSTATUS_WIN_STATUS_THREAD_ALREADY_IN_TASK = 0xC0000502, + MD_NTSTATUS_WIN_STATUS_CALLBACK_BYPASS = 0xC0000503, + MD_NTSTATUS_WIN_STATUS_UNDEFINED_SCOPE = 0xC0000504, + MD_NTSTATUS_WIN_STATUS_INVALID_CAP = 0xC0000505, + MD_NTSTATUS_WIN_STATUS_NOT_GUI_PROCESS = 0xC0000506, + MD_NTSTATUS_WIN_STATUS_FAIL_FAST_EXCEPTION = 0xC0000602, + MD_NTSTATUS_WIN_STATUS_IMAGE_CERT_REVOKED = 0xC0000603, + MD_NTSTATUS_WIN_STATUS_DYNAMIC_CODE_BLOCKED = 0xC0000604, + MD_NTSTATUS_WIN_STATUS_PORT_CLOSED = 0xC0000700, + MD_NTSTATUS_WIN_STATUS_MESSAGE_LOST = 0xC0000701, + MD_NTSTATUS_WIN_STATUS_INVALID_MESSAGE = 0xC0000702, + MD_NTSTATUS_WIN_STATUS_REQUEST_CANCELED = 0xC0000703, + MD_NTSTATUS_WIN_STATUS_RECURSIVE_DISPATCH = 0xC0000704, + MD_NTSTATUS_WIN_STATUS_LPC_RECEIVE_BUFFER_EXPECTED = 0xC0000705, + MD_NTSTATUS_WIN_STATUS_LPC_INVALID_CONNECTION_USAGE = 0xC0000706, + MD_NTSTATUS_WIN_STATUS_LPC_REQUESTS_NOT_ALLOWED = 0xC0000707, + MD_NTSTATUS_WIN_STATUS_RESOURCE_IN_USE = 0xC0000708, + MD_NTSTATUS_WIN_STATUS_HARDWARE_MEMORY_ERROR = 0xC0000709, + MD_NTSTATUS_WIN_STATUS_THREADPOOL_HANDLE_EXCEPTION = 0xC000070A, + MD_NTSTATUS_WIN_STATUS_THREADPOOL_SET_EVENT_ON_COMPLETION_FAILED = 0xC000070B, + MD_NTSTATUS_WIN_STATUS_THREADPOOL_RELEASE_SEMAPHORE_ON_COMPLETION_FAILED = 0xC000070C, + MD_NTSTATUS_WIN_STATUS_THREADPOOL_RELEASE_MUTEX_ON_COMPLETION_FAILED = 0xC000070D, + MD_NTSTATUS_WIN_STATUS_THREADPOOL_FREE_LIBRARY_ON_COMPLETION_FAILED = 0xC000070E, + MD_NTSTATUS_WIN_STATUS_THREADPOOL_RELEASED_DURING_OPERATION = 0xC000070F, + MD_NTSTATUS_WIN_STATUS_CALLBACK_RETURNED_WHILE_IMPERSONATING = 0xC0000710, + MD_NTSTATUS_WIN_STATUS_APC_RETURNED_WHILE_IMPERSONATING = 0xC0000711, + MD_NTSTATUS_WIN_STATUS_PROCESS_IS_PROTECTED = 0xC0000712, + MD_NTSTATUS_WIN_STATUS_MCA_EXCEPTION = 0xC0000713, + MD_NTSTATUS_WIN_STATUS_CERTIFICATE_MAPPING_NOT_UNIQUE = 0xC0000714, + MD_NTSTATUS_WIN_STATUS_SYMLINK_CLASS_DISABLED = 0xC0000715, + MD_NTSTATUS_WIN_STATUS_INVALID_IDN_NORMALIZATION = 0xC0000716, + MD_NTSTATUS_WIN_STATUS_NO_UNICODE_TRANSLATION = 0xC0000717, + MD_NTSTATUS_WIN_STATUS_ALREADY_REGISTERED = 0xC0000718, + MD_NTSTATUS_WIN_STATUS_CONTEXT_MISMATCH = 0xC0000719, + MD_NTSTATUS_WIN_STATUS_PORT_ALREADY_HAS_COMPLETION_LIST = 0xC000071A, + MD_NTSTATUS_WIN_STATUS_CALLBACK_RETURNED_THREAD_PRIORITY = 0xC000071B, + MD_NTSTATUS_WIN_STATUS_INVALID_THREAD = 0xC000071C, + MD_NTSTATUS_WIN_STATUS_CALLBACK_RETURNED_TRANSACTION = 0xC000071D, + MD_NTSTATUS_WIN_STATUS_CALLBACK_RETURNED_LDR_LOCK = 0xC000071E, + MD_NTSTATUS_WIN_STATUS_CALLBACK_RETURNED_LANG = 0xC000071F, + MD_NTSTATUS_WIN_STATUS_CALLBACK_RETURNED_PRI_BACK = 0xC0000720, + MD_NTSTATUS_WIN_STATUS_CALLBACK_RETURNED_THREAD_AFFINITY = 0xC0000721, + MD_NTSTATUS_WIN_STATUS_DISK_REPAIR_DISABLED = 0xC0000800, + MD_NTSTATUS_WIN_STATUS_DS_DOMAIN_RENAME_IN_PROGRESS = 0xC0000801, + MD_NTSTATUS_WIN_STATUS_DISK_QUOTA_EXCEEDED = 0xC0000802, + MD_NTSTATUS_WIN_STATUS_CONTENT_BLOCKED = 0xC0000804, + MD_NTSTATUS_WIN_STATUS_BAD_CLUSTERS = 0xC0000805, + MD_NTSTATUS_WIN_STATUS_VOLUME_DIRTY = 0xC0000806, + MD_NTSTATUS_WIN_STATUS_DISK_REPAIR_UNSUCCESSFUL = 0xC0000808, + MD_NTSTATUS_WIN_STATUS_CORRUPT_LOG_OVERFULL = 0xC0000809, + MD_NTSTATUS_WIN_STATUS_CORRUPT_LOG_CORRUPTED = 0xC000080A, + MD_NTSTATUS_WIN_STATUS_CORRUPT_LOG_UNAVAILABLE = 0xC000080B, + MD_NTSTATUS_WIN_STATUS_CORRUPT_LOG_DELETED_FULL = 0xC000080C, + MD_NTSTATUS_WIN_STATUS_CORRUPT_LOG_CLEARED = 0xC000080D, + MD_NTSTATUS_WIN_STATUS_ORPHAN_NAME_EXHAUSTED = 0xC000080E, + MD_NTSTATUS_WIN_STATUS_PROACTIVE_SCAN_IN_PROGRESS = 0xC000080F, + MD_NTSTATUS_WIN_STATUS_ENCRYPTED_IO_NOT_POSSIBLE = 0xC0000810, + MD_NTSTATUS_WIN_STATUS_CORRUPT_LOG_UPLEVEL_RECORDS = 0xC0000811, + MD_NTSTATUS_WIN_STATUS_FILE_CHECKED_OUT = 0xC0000901, + MD_NTSTATUS_WIN_STATUS_CHECKOUT_REQUIRED = 0xC0000902, + MD_NTSTATUS_WIN_STATUS_BAD_FILE_TYPE = 0xC0000903, + MD_NTSTATUS_WIN_STATUS_FILE_TOO_LARGE = 0xC0000904, + MD_NTSTATUS_WIN_STATUS_FORMS_AUTH_REQUIRED = 0xC0000905, + MD_NTSTATUS_WIN_STATUS_VIRUS_INFECTED = 0xC0000906, + MD_NTSTATUS_WIN_STATUS_VIRUS_DELETED = 0xC0000907, + MD_NTSTATUS_WIN_STATUS_BAD_MCFG_TABLE = 0xC0000908, + MD_NTSTATUS_WIN_STATUS_CANNOT_BREAK_OPLOCK = 0xC0000909, + MD_NTSTATUS_WIN_STATUS_BAD_KEY = 0xC000090A, + MD_NTSTATUS_WIN_STATUS_BAD_DATA = 0xC000090B, + MD_NTSTATUS_WIN_STATUS_NO_KEY = 0xC000090C, + MD_NTSTATUS_WIN_STATUS_FILE_HANDLE_REVOKED = 0xC0000910, + MD_NTSTATUS_WIN_STATUS_WOW_ASSERTION = 0xC0009898, + MD_NTSTATUS_WIN_STATUS_INVALID_SIGNATURE = 0xC000A000, + MD_NTSTATUS_WIN_STATUS_HMAC_NOT_SUPPORTED = 0xC000A001, + MD_NTSTATUS_WIN_STATUS_AUTH_TAG_MISMATCH = 0xC000A002, + MD_NTSTATUS_WIN_STATUS_INVALID_STATE_TRANSITION = 0xC000A003, + MD_NTSTATUS_WIN_STATUS_INVALID_KERNEL_INFO_VERSION = 0xC000A004, + MD_NTSTATUS_WIN_STATUS_INVALID_PEP_INFO_VERSION = 0xC000A005, + MD_NTSTATUS_WIN_STATUS_IPSEC_QUEUE_OVERFLOW = 0xC000A010, + MD_NTSTATUS_WIN_STATUS_ND_QUEUE_OVERFLOW = 0xC000A011, + MD_NTSTATUS_WIN_STATUS_HOPLIMIT_EXCEEDED = 0xC000A012, + MD_NTSTATUS_WIN_STATUS_PROTOCOL_NOT_SUPPORTED = 0xC000A013, + MD_NTSTATUS_WIN_STATUS_FASTPATH_REJECTED = 0xC000A014, + MD_NTSTATUS_WIN_STATUS_LOST_WRITEBEHIND_DATA_NETWORK_DISCONNECTED = 0xC000A080, + MD_NTSTATUS_WIN_STATUS_LOST_WRITEBEHIND_DATA_NETWORK_SERVER_ERROR = 0xC000A081, + MD_NTSTATUS_WIN_STATUS_LOST_WRITEBEHIND_DATA_LOCAL_DISK_ERROR = 0xC000A082, + MD_NTSTATUS_WIN_STATUS_XML_PARSE_ERROR = 0xC000A083, + MD_NTSTATUS_WIN_STATUS_XMLDSIG_ERROR = 0xC000A084, + MD_NTSTATUS_WIN_STATUS_WRONG_COMPARTMENT = 0xC000A085, + MD_NTSTATUS_WIN_STATUS_AUTHIP_FAILURE = 0xC000A086, + MD_NTSTATUS_WIN_STATUS_DS_OID_MAPPED_GROUP_CANT_HAVE_MEMBERS = 0xC000A087, + MD_NTSTATUS_WIN_STATUS_DS_OID_NOT_FOUND = 0xC000A088, + MD_NTSTATUS_WIN_STATUS_INCORRECT_ACCOUNT_TYPE = 0xC000A089, + MD_NTSTATUS_WIN_STATUS_HASH_NOT_SUPPORTED = 0xC000A100, + MD_NTSTATUS_WIN_STATUS_HASH_NOT_PRESENT = 0xC000A101, + MD_NTSTATUS_WIN_STATUS_SECONDARY_IC_PROVIDER_NOT_REGISTERED = 0xC000A121, + MD_NTSTATUS_WIN_STATUS_GPIO_CLIENT_INFORMATION_INVALID = 0xC000A122, + MD_NTSTATUS_WIN_STATUS_GPIO_VERSION_NOT_SUPPORTED = 0xC000A123, + MD_NTSTATUS_WIN_STATUS_GPIO_INVALID_REGISTRATION_PACKET = 0xC000A124, + MD_NTSTATUS_WIN_STATUS_GPIO_OPERATION_DENIED = 0xC000A125, + MD_NTSTATUS_WIN_STATUS_GPIO_INCOMPATIBLE_CONNECT_MODE = 0xC000A126, + MD_NTSTATUS_WIN_STATUS_CANNOT_SWITCH_RUNLEVEL = 0xC000A141, + MD_NTSTATUS_WIN_STATUS_INVALID_RUNLEVEL_SETTING = 0xC000A142, + MD_NTSTATUS_WIN_STATUS_RUNLEVEL_SWITCH_TIMEOUT = 0xC000A143, + MD_NTSTATUS_WIN_STATUS_RUNLEVEL_SWITCH_AGENT_TIMEOUT = 0xC000A145, + MD_NTSTATUS_WIN_STATUS_RUNLEVEL_SWITCH_IN_PROGRESS = 0xC000A146, + MD_NTSTATUS_WIN_STATUS_NOT_APPCONTAINER = 0xC000A200, + MD_NTSTATUS_WIN_STATUS_NOT_SUPPORTED_IN_APPCONTAINER = 0xC000A201, + MD_NTSTATUS_WIN_STATUS_INVALID_PACKAGE_SID_LENGTH = 0xC000A202, + MD_NTSTATUS_WIN_STATUS_APP_DATA_NOT_FOUND = 0xC000A281, + MD_NTSTATUS_WIN_STATUS_APP_DATA_EXPIRED = 0xC000A282, + MD_NTSTATUS_WIN_STATUS_APP_DATA_CORRUPT = 0xC000A283, + MD_NTSTATUS_WIN_STATUS_APP_DATA_LIMIT_EXCEEDED = 0xC000A284, + MD_NTSTATUS_WIN_STATUS_APP_DATA_REBOOT_REQUIRED = 0xC000A285, + MD_NTSTATUS_WIN_STATUS_OFFLOAD_READ_FLT_NOT_SUPPORTED = 0xC000A2A1, + MD_NTSTATUS_WIN_STATUS_OFFLOAD_WRITE_FLT_NOT_SUPPORTED = 0xC000A2A2, + MD_NTSTATUS_WIN_STATUS_OFFLOAD_READ_FILE_NOT_SUPPORTED = 0xC000A2A3, + MD_NTSTATUS_WIN_STATUS_OFFLOAD_WRITE_FILE_NOT_SUPPORTED = 0xC000A2A4, + MD_NTSTATUS_WIN_DBG_NO_STATE_CHANGE = 0xC0010001, + MD_NTSTATUS_WIN_DBG_APP_NOT_IDLE = 0xC0010002, + MD_NTSTATUS_WIN_RPC_NT_INVALID_STRING_BINDING = 0xC0020001, + MD_NTSTATUS_WIN_RPC_NT_WRONG_KIND_OF_BINDING = 0xC0020002, + MD_NTSTATUS_WIN_RPC_NT_INVALID_BINDING = 0xC0020003, + MD_NTSTATUS_WIN_RPC_NT_PROTSEQ_NOT_SUPPORTED = 0xC0020004, + MD_NTSTATUS_WIN_RPC_NT_INVALID_RPC_PROTSEQ = 0xC0020005, + MD_NTSTATUS_WIN_RPC_NT_INVALID_STRING_UUID = 0xC0020006, + MD_NTSTATUS_WIN_RPC_NT_INVALID_ENDPOINT_FORMAT = 0xC0020007, + MD_NTSTATUS_WIN_RPC_NT_INVALID_NET_ADDR = 0xC0020008, + MD_NTSTATUS_WIN_RPC_NT_NO_ENDPOINT_FOUND = 0xC0020009, + MD_NTSTATUS_WIN_RPC_NT_INVALID_TIMEOUT = 0xC002000A, + MD_NTSTATUS_WIN_RPC_NT_OBJECT_NOT_FOUND = 0xC002000B, + MD_NTSTATUS_WIN_RPC_NT_ALREADY_REGISTERED = 0xC002000C, + MD_NTSTATUS_WIN_RPC_NT_TYPE_ALREADY_REGISTERED = 0xC002000D, + MD_NTSTATUS_WIN_RPC_NT_ALREADY_LISTENING = 0xC002000E, + MD_NTSTATUS_WIN_RPC_NT_NO_PROTSEQS_REGISTERED = 0xC002000F, + MD_NTSTATUS_WIN_RPC_NT_NOT_LISTENING = 0xC0020010, + MD_NTSTATUS_WIN_RPC_NT_UNKNOWN_MGR_TYPE = 0xC0020011, + MD_NTSTATUS_WIN_RPC_NT_UNKNOWN_IF = 0xC0020012, + MD_NTSTATUS_WIN_RPC_NT_NO_BINDINGS = 0xC0020013, + MD_NTSTATUS_WIN_RPC_NT_NO_PROTSEQS = 0xC0020014, + MD_NTSTATUS_WIN_RPC_NT_CANT_CREATE_ENDPOINT = 0xC0020015, + MD_NTSTATUS_WIN_RPC_NT_OUT_OF_RESOURCES = 0xC0020016, + MD_NTSTATUS_WIN_RPC_NT_SERVER_UNAVAILABLE = 0xC0020017, + MD_NTSTATUS_WIN_RPC_NT_SERVER_TOO_BUSY = 0xC0020018, + MD_NTSTATUS_WIN_RPC_NT_INVALID_NETWORK_OPTIONS = 0xC0020019, + MD_NTSTATUS_WIN_RPC_NT_NO_CALL_ACTIVE = 0xC002001A, + MD_NTSTATUS_WIN_RPC_NT_CALL_FAILED = 0xC002001B, + MD_NTSTATUS_WIN_RPC_NT_CALL_FAILED_DNE = 0xC002001C, + MD_NTSTATUS_WIN_RPC_NT_PROTOCOL_ERROR = 0xC002001D, + MD_NTSTATUS_WIN_RPC_NT_UNSUPPORTED_TRANS_SYN = 0xC002001F, + MD_NTSTATUS_WIN_RPC_NT_UNSUPPORTED_TYPE = 0xC0020021, + MD_NTSTATUS_WIN_RPC_NT_INVALID_TAG = 0xC0020022, + MD_NTSTATUS_WIN_RPC_NT_INVALID_BOUND = 0xC0020023, + MD_NTSTATUS_WIN_RPC_NT_NO_ENTRY_NAME = 0xC0020024, + MD_NTSTATUS_WIN_RPC_NT_INVALID_NAME_SYNTAX = 0xC0020025, + MD_NTSTATUS_WIN_RPC_NT_UNSUPPORTED_NAME_SYNTAX = 0xC0020026, + MD_NTSTATUS_WIN_RPC_NT_UUID_NO_ADDRESS = 0xC0020028, + MD_NTSTATUS_WIN_RPC_NT_DUPLICATE_ENDPOINT = 0xC0020029, + MD_NTSTATUS_WIN_RPC_NT_UNKNOWN_AUTHN_TYPE = 0xC002002A, + MD_NTSTATUS_WIN_RPC_NT_MAX_CALLS_TOO_SMALL = 0xC002002B, + MD_NTSTATUS_WIN_RPC_NT_STRING_TOO_LONG = 0xC002002C, + MD_NTSTATUS_WIN_RPC_NT_PROTSEQ_NOT_FOUND = 0xC002002D, + MD_NTSTATUS_WIN_RPC_NT_PROCNUM_OUT_OF_RANGE = 0xC002002E, + MD_NTSTATUS_WIN_RPC_NT_BINDING_HAS_NO_AUTH = 0xC002002F, + MD_NTSTATUS_WIN_RPC_NT_UNKNOWN_AUTHN_SERVICE = 0xC0020030, + MD_NTSTATUS_WIN_RPC_NT_UNKNOWN_AUTHN_LEVEL = 0xC0020031, + MD_NTSTATUS_WIN_RPC_NT_INVALID_AUTH_IDENTITY = 0xC0020032, + MD_NTSTATUS_WIN_RPC_NT_UNKNOWN_AUTHZ_SERVICE = 0xC0020033, + MD_NTSTATUS_WIN_EPT_NT_INVALID_ENTRY = 0xC0020034, + MD_NTSTATUS_WIN_EPT_NT_CANT_PERFORM_OP = 0xC0020035, + MD_NTSTATUS_WIN_EPT_NT_NOT_REGISTERED = 0xC0020036, + MD_NTSTATUS_WIN_RPC_NT_NOTHING_TO_EXPORT = 0xC0020037, + MD_NTSTATUS_WIN_RPC_NT_INCOMPLETE_NAME = 0xC0020038, + MD_NTSTATUS_WIN_RPC_NT_INVALID_VERS_OPTION = 0xC0020039, + MD_NTSTATUS_WIN_RPC_NT_NO_MORE_MEMBERS = 0xC002003A, + MD_NTSTATUS_WIN_RPC_NT_NOT_ALL_OBJS_UNEXPORTED = 0xC002003B, + MD_NTSTATUS_WIN_RPC_NT_INTERFACE_NOT_FOUND = 0xC002003C, + MD_NTSTATUS_WIN_RPC_NT_ENTRY_ALREADY_EXISTS = 0xC002003D, + MD_NTSTATUS_WIN_RPC_NT_ENTRY_NOT_FOUND = 0xC002003E, + MD_NTSTATUS_WIN_RPC_NT_NAME_SERVICE_UNAVAILABLE = 0xC002003F, + MD_NTSTATUS_WIN_RPC_NT_INVALID_NAF_ID = 0xC0020040, + MD_NTSTATUS_WIN_RPC_NT_CANNOT_SUPPORT = 0xC0020041, + MD_NTSTATUS_WIN_RPC_NT_NO_CONTEXT_AVAILABLE = 0xC0020042, + MD_NTSTATUS_WIN_RPC_NT_INTERNAL_ERROR = 0xC0020043, + MD_NTSTATUS_WIN_RPC_NT_ZERO_DIVIDE = 0xC0020044, + MD_NTSTATUS_WIN_RPC_NT_ADDRESS_ERROR = 0xC0020045, + MD_NTSTATUS_WIN_RPC_NT_FP_DIV_ZERO = 0xC0020046, + MD_NTSTATUS_WIN_RPC_NT_FP_UNDERFLOW = 0xC0020047, + MD_NTSTATUS_WIN_RPC_NT_FP_OVERFLOW = 0xC0020048, + MD_NTSTATUS_WIN_RPC_NT_CALL_IN_PROGRESS = 0xC0020049, + MD_NTSTATUS_WIN_RPC_NT_NO_MORE_BINDINGS = 0xC002004A, + MD_NTSTATUS_WIN_RPC_NT_GROUP_MEMBER_NOT_FOUND = 0xC002004B, + MD_NTSTATUS_WIN_EPT_NT_CANT_CREATE = 0xC002004C, + MD_NTSTATUS_WIN_RPC_NT_INVALID_OBJECT = 0xC002004D, + MD_NTSTATUS_WIN_RPC_NT_NO_INTERFACES = 0xC002004F, + MD_NTSTATUS_WIN_RPC_NT_CALL_CANCELLED = 0xC0020050, + MD_NTSTATUS_WIN_RPC_NT_BINDING_INCOMPLETE = 0xC0020051, + MD_NTSTATUS_WIN_RPC_NT_COMM_FAILURE = 0xC0020052, + MD_NTSTATUS_WIN_RPC_NT_UNSUPPORTED_AUTHN_LEVEL = 0xC0020053, + MD_NTSTATUS_WIN_RPC_NT_NO_PRINC_NAME = 0xC0020054, + MD_NTSTATUS_WIN_RPC_NT_NOT_RPC_ERROR = 0xC0020055, + MD_NTSTATUS_WIN_RPC_NT_SEC_PKG_ERROR = 0xC0020057, + MD_NTSTATUS_WIN_RPC_NT_NOT_CANCELLED = 0xC0020058, + MD_NTSTATUS_WIN_RPC_NT_INVALID_ASYNC_HANDLE = 0xC0020062, + MD_NTSTATUS_WIN_RPC_NT_INVALID_ASYNC_CALL = 0xC0020063, + MD_NTSTATUS_WIN_RPC_NT_PROXY_ACCESS_DENIED = 0xC0020064, + MD_NTSTATUS_WIN_RPC_NT_COOKIE_AUTH_FAILED = 0xC0020065, + MD_NTSTATUS_WIN_RPC_NT_NO_MORE_ENTRIES = 0xC0030001, + MD_NTSTATUS_WIN_RPC_NT_SS_CHAR_TRANS_OPEN_FAIL = 0xC0030002, + MD_NTSTATUS_WIN_RPC_NT_SS_CHAR_TRANS_SHORT_FILE = 0xC0030003, + MD_NTSTATUS_WIN_RPC_NT_SS_IN_NULL_CONTEXT = 0xC0030004, + MD_NTSTATUS_WIN_RPC_NT_SS_CONTEXT_MISMATCH = 0xC0030005, + MD_NTSTATUS_WIN_RPC_NT_SS_CONTEXT_DAMAGED = 0xC0030006, + MD_NTSTATUS_WIN_RPC_NT_SS_HANDLES_MISMATCH = 0xC0030007, + MD_NTSTATUS_WIN_RPC_NT_SS_CANNOT_GET_CALL_HANDLE = 0xC0030008, + MD_NTSTATUS_WIN_RPC_NT_NULL_REF_POINTER = 0xC0030009, + MD_NTSTATUS_WIN_RPC_NT_ENUM_VALUE_OUT_OF_RANGE = 0xC003000A, + MD_NTSTATUS_WIN_RPC_NT_BYTE_COUNT_TOO_SMALL = 0xC003000B, + MD_NTSTATUS_WIN_RPC_NT_BAD_STUB_DATA = 0xC003000C, + MD_NTSTATUS_WIN_RPC_NT_INVALID_ES_ACTION = 0xC0030059, + MD_NTSTATUS_WIN_RPC_NT_WRONG_ES_VERSION = 0xC003005A, + MD_NTSTATUS_WIN_RPC_NT_WRONG_STUB_VERSION = 0xC003005B, + MD_NTSTATUS_WIN_RPC_NT_INVALID_PIPE_OBJECT = 0xC003005C, + MD_NTSTATUS_WIN_RPC_NT_INVALID_PIPE_OPERATION = 0xC003005D, + MD_NTSTATUS_WIN_RPC_NT_WRONG_PIPE_VERSION = 0xC003005E, + MD_NTSTATUS_WIN_RPC_NT_PIPE_CLOSED = 0xC003005F, + MD_NTSTATUS_WIN_RPC_NT_PIPE_DISCIPLINE_ERROR = 0xC0030060, + MD_NTSTATUS_WIN_RPC_NT_PIPE_EMPTY = 0xC0030061, + MD_NTSTATUS_WIN_STATUS_PNP_BAD_MPS_TABLE = 0xC0040035, + MD_NTSTATUS_WIN_STATUS_PNP_TRANSLATION_FAILED = 0xC0040036, + MD_NTSTATUS_WIN_STATUS_PNP_IRQ_TRANSLATION_FAILED = 0xC0040037, + MD_NTSTATUS_WIN_STATUS_PNP_INVALID_ID = 0xC0040038, + MD_NTSTATUS_WIN_STATUS_IO_REISSUE_AS_CACHED = 0xC0040039, + MD_NTSTATUS_WIN_STATUS_CTX_WINSTATION_NAME_INVALID = 0xC00A0001, + MD_NTSTATUS_WIN_STATUS_CTX_INVALID_PD = 0xC00A0002, + MD_NTSTATUS_WIN_STATUS_CTX_PD_NOT_FOUND = 0xC00A0003, + MD_NTSTATUS_WIN_STATUS_CTX_CLOSE_PENDING = 0xC00A0006, + MD_NTSTATUS_WIN_STATUS_CTX_NO_OUTBUF = 0xC00A0007, + MD_NTSTATUS_WIN_STATUS_CTX_MODEM_INF_NOT_FOUND = 0xC00A0008, + MD_NTSTATUS_WIN_STATUS_CTX_INVALID_MODEMNAME = 0xC00A0009, + MD_NTSTATUS_WIN_STATUS_CTX_RESPONSE_ERROR = 0xC00A000A, + MD_NTSTATUS_WIN_STATUS_CTX_MODEM_RESPONSE_TIMEOUT = 0xC00A000B, + MD_NTSTATUS_WIN_STATUS_CTX_MODEM_RESPONSE_NO_CARRIER = 0xC00A000C, + MD_NTSTATUS_WIN_STATUS_CTX_MODEM_RESPONSE_NO_DIALTONE = 0xC00A000D, + MD_NTSTATUS_WIN_STATUS_CTX_MODEM_RESPONSE_BUSY = 0xC00A000E, + MD_NTSTATUS_WIN_STATUS_CTX_MODEM_RESPONSE_VOICE = 0xC00A000F, + MD_NTSTATUS_WIN_STATUS_CTX_TD_ERROR = 0xC00A0010, + MD_NTSTATUS_WIN_STATUS_CTX_LICENSE_CLIENT_INVALID = 0xC00A0012, + MD_NTSTATUS_WIN_STATUS_CTX_LICENSE_NOT_AVAILABLE = 0xC00A0013, + MD_NTSTATUS_WIN_STATUS_CTX_LICENSE_EXPIRED = 0xC00A0014, + MD_NTSTATUS_WIN_STATUS_CTX_WINSTATION_NOT_FOUND = 0xC00A0015, + MD_NTSTATUS_WIN_STATUS_CTX_WINSTATION_NAME_COLLISION = 0xC00A0016, + MD_NTSTATUS_WIN_STATUS_CTX_WINSTATION_BUSY = 0xC00A0017, + MD_NTSTATUS_WIN_STATUS_CTX_BAD_VIDEO_MODE = 0xC00A0018, + MD_NTSTATUS_WIN_STATUS_CTX_GRAPHICS_INVALID = 0xC00A0022, + MD_NTSTATUS_WIN_STATUS_CTX_NOT_CONSOLE = 0xC00A0024, + MD_NTSTATUS_WIN_STATUS_CTX_CLIENT_QUERY_TIMEOUT = 0xC00A0026, + MD_NTSTATUS_WIN_STATUS_CTX_CONSOLE_DISCONNECT = 0xC00A0027, + MD_NTSTATUS_WIN_STATUS_CTX_CONSOLE_CONNECT = 0xC00A0028, + MD_NTSTATUS_WIN_STATUS_CTX_SHADOW_DENIED = 0xC00A002A, + MD_NTSTATUS_WIN_STATUS_CTX_WINSTATION_ACCESS_DENIED = 0xC00A002B, + MD_NTSTATUS_WIN_STATUS_CTX_INVALID_WD = 0xC00A002E, + MD_NTSTATUS_WIN_STATUS_CTX_WD_NOT_FOUND = 0xC00A002F, + MD_NTSTATUS_WIN_STATUS_CTX_SHADOW_INVALID = 0xC00A0030, + MD_NTSTATUS_WIN_STATUS_CTX_SHADOW_DISABLED = 0xC00A0031, + MD_NTSTATUS_WIN_STATUS_RDP_PROTOCOL_ERROR = 0xC00A0032, + MD_NTSTATUS_WIN_STATUS_CTX_CLIENT_LICENSE_NOT_SET = 0xC00A0033, + MD_NTSTATUS_WIN_STATUS_CTX_CLIENT_LICENSE_IN_USE = 0xC00A0034, + MD_NTSTATUS_WIN_STATUS_CTX_SHADOW_ENDED_BY_MODE_CHANGE = 0xC00A0035, + MD_NTSTATUS_WIN_STATUS_CTX_SHADOW_NOT_RUNNING = 0xC00A0036, + MD_NTSTATUS_WIN_STATUS_CTX_LOGON_DISABLED = 0xC00A0037, + MD_NTSTATUS_WIN_STATUS_CTX_SECURITY_LAYER_ERROR = 0xC00A0038, + MD_NTSTATUS_WIN_STATUS_TS_INCOMPATIBLE_SESSIONS = 0xC00A0039, + MD_NTSTATUS_WIN_STATUS_TS_VIDEO_SUBSYSTEM_ERROR = 0xC00A003A, + MD_NTSTATUS_WIN_STATUS_MUI_FILE_NOT_FOUND = 0xC00B0001, + MD_NTSTATUS_WIN_STATUS_MUI_INVALID_FILE = 0xC00B0002, + MD_NTSTATUS_WIN_STATUS_MUI_INVALID_RC_CONFIG = 0xC00B0003, + MD_NTSTATUS_WIN_STATUS_MUI_INVALID_LOCALE_NAME = 0xC00B0004, + MD_NTSTATUS_WIN_STATUS_MUI_INVALID_ULTIMATEFALLBACK_NAME = 0xC00B0005, + MD_NTSTATUS_WIN_STATUS_MUI_FILE_NOT_LOADED = 0xC00B0006, + MD_NTSTATUS_WIN_STATUS_RESOURCE_ENUM_USER_STOP = 0xC00B0007, + MD_NTSTATUS_WIN_STATUS_CLUSTER_INVALID_NODE = 0xC0130001, + MD_NTSTATUS_WIN_STATUS_CLUSTER_NODE_EXISTS = 0xC0130002, + MD_NTSTATUS_WIN_STATUS_CLUSTER_JOIN_IN_PROGRESS = 0xC0130003, + MD_NTSTATUS_WIN_STATUS_CLUSTER_NODE_NOT_FOUND = 0xC0130004, + MD_NTSTATUS_WIN_STATUS_CLUSTER_LOCAL_NODE_NOT_FOUND = 0xC0130005, + MD_NTSTATUS_WIN_STATUS_CLUSTER_NETWORK_EXISTS = 0xC0130006, + MD_NTSTATUS_WIN_STATUS_CLUSTER_NETWORK_NOT_FOUND = 0xC0130007, + MD_NTSTATUS_WIN_STATUS_CLUSTER_NETINTERFACE_EXISTS = 0xC0130008, + MD_NTSTATUS_WIN_STATUS_CLUSTER_NETINTERFACE_NOT_FOUND = 0xC0130009, + MD_NTSTATUS_WIN_STATUS_CLUSTER_INVALID_REQUEST = 0xC013000A, + MD_NTSTATUS_WIN_STATUS_CLUSTER_INVALID_NETWORK_PROVIDER = 0xC013000B, + MD_NTSTATUS_WIN_STATUS_CLUSTER_NODE_DOWN = 0xC013000C, + MD_NTSTATUS_WIN_STATUS_CLUSTER_NODE_UNREACHABLE = 0xC013000D, + MD_NTSTATUS_WIN_STATUS_CLUSTER_NODE_NOT_MEMBER = 0xC013000E, + MD_NTSTATUS_WIN_STATUS_CLUSTER_JOIN_NOT_IN_PROGRESS = 0xC013000F, + MD_NTSTATUS_WIN_STATUS_CLUSTER_INVALID_NETWORK = 0xC0130010, + MD_NTSTATUS_WIN_STATUS_CLUSTER_NO_NET_ADAPTERS = 0xC0130011, + MD_NTSTATUS_WIN_STATUS_CLUSTER_NODE_UP = 0xC0130012, + MD_NTSTATUS_WIN_STATUS_CLUSTER_NODE_PAUSED = 0xC0130013, + MD_NTSTATUS_WIN_STATUS_CLUSTER_NODE_NOT_PAUSED = 0xC0130014, + MD_NTSTATUS_WIN_STATUS_CLUSTER_NO_SECURITY_CONTEXT = 0xC0130015, + MD_NTSTATUS_WIN_STATUS_CLUSTER_NETWORK_NOT_INTERNAL = 0xC0130016, + MD_NTSTATUS_WIN_STATUS_CLUSTER_POISONED = 0xC0130017, + MD_NTSTATUS_WIN_STATUS_CLUSTER_NON_CSV_PATH = 0xC0130018, + MD_NTSTATUS_WIN_STATUS_CLUSTER_CSV_VOLUME_NOT_LOCAL = 0xC0130019, + MD_NTSTATUS_WIN_STATUS_CLUSTER_CSV_READ_OPLOCK_BREAK_IN_PROGRESS = 0xC0130020, + MD_NTSTATUS_WIN_STATUS_CLUSTER_CSV_AUTO_PAUSE_ERROR = 0xC0130021, + MD_NTSTATUS_WIN_STATUS_CLUSTER_CSV_REDIRECTED = 0xC0130022, + MD_NTSTATUS_WIN_STATUS_CLUSTER_CSV_NOT_REDIRECTED = 0xC0130023, + MD_NTSTATUS_WIN_STATUS_CLUSTER_CSV_VOLUME_DRAINING = 0xC0130024, + MD_NTSTATUS_WIN_STATUS_CLUSTER_CSV_SNAPSHOT_CREATION_IN_PROGRESS = 0xC0130025, + MD_NTSTATUS_WIN_STATUS_CLUSTER_CSV_VOLUME_DRAINING_SUCCEEDED_DOWNLEVEL = 0xC0130026, + MD_NTSTATUS_WIN_STATUS_ACPI_INVALID_OPCODE = 0xC0140001, + MD_NTSTATUS_WIN_STATUS_ACPI_STACK_OVERFLOW = 0xC0140002, + MD_NTSTATUS_WIN_STATUS_ACPI_ASSERT_FAILED = 0xC0140003, + MD_NTSTATUS_WIN_STATUS_ACPI_INVALID_INDEX = 0xC0140004, + MD_NTSTATUS_WIN_STATUS_ACPI_INVALID_ARGUMENT = 0xC0140005, + MD_NTSTATUS_WIN_STATUS_ACPI_FATAL = 0xC0140006, + MD_NTSTATUS_WIN_STATUS_ACPI_INVALID_SUPERNAME = 0xC0140007, + MD_NTSTATUS_WIN_STATUS_ACPI_INVALID_ARGTYPE = 0xC0140008, + MD_NTSTATUS_WIN_STATUS_ACPI_INVALID_OBJTYPE = 0xC0140009, + MD_NTSTATUS_WIN_STATUS_ACPI_INVALID_TARGETTYPE = 0xC014000A, + MD_NTSTATUS_WIN_STATUS_ACPI_INCORRECT_ARGUMENT_COUNT = 0xC014000B, + MD_NTSTATUS_WIN_STATUS_ACPI_ADDRESS_NOT_MAPPED = 0xC014000C, + MD_NTSTATUS_WIN_STATUS_ACPI_INVALID_EVENTTYPE = 0xC014000D, + MD_NTSTATUS_WIN_STATUS_ACPI_HANDLER_COLLISION = 0xC014000E, + MD_NTSTATUS_WIN_STATUS_ACPI_INVALID_DATA = 0xC014000F, + MD_NTSTATUS_WIN_STATUS_ACPI_INVALID_REGION = 0xC0140010, + MD_NTSTATUS_WIN_STATUS_ACPI_INVALID_ACCESS_SIZE = 0xC0140011, + MD_NTSTATUS_WIN_STATUS_ACPI_ACQUIRE_GLOBAL_LOCK = 0xC0140012, + MD_NTSTATUS_WIN_STATUS_ACPI_ALREADY_INITIALIZED = 0xC0140013, + MD_NTSTATUS_WIN_STATUS_ACPI_NOT_INITIALIZED = 0xC0140014, + MD_NTSTATUS_WIN_STATUS_ACPI_INVALID_MUTEX_LEVEL = 0xC0140015, + MD_NTSTATUS_WIN_STATUS_ACPI_MUTEX_NOT_OWNED = 0xC0140016, + MD_NTSTATUS_WIN_STATUS_ACPI_MUTEX_NOT_OWNER = 0xC0140017, + MD_NTSTATUS_WIN_STATUS_ACPI_RS_ACCESS = 0xC0140018, + MD_NTSTATUS_WIN_STATUS_ACPI_INVALID_TABLE = 0xC0140019, + MD_NTSTATUS_WIN_STATUS_ACPI_REG_HANDLER_FAILED = 0xC0140020, + MD_NTSTATUS_WIN_STATUS_ACPI_POWER_REQUEST_FAILED = 0xC0140021, + MD_NTSTATUS_WIN_STATUS_SXS_SECTION_NOT_FOUND = 0xC0150001, + MD_NTSTATUS_WIN_STATUS_SXS_CANT_GEN_ACTCTX = 0xC0150002, + MD_NTSTATUS_WIN_STATUS_SXS_INVALID_ACTCTXDATA_FORMAT = 0xC0150003, + MD_NTSTATUS_WIN_STATUS_SXS_ASSEMBLY_NOT_FOUND = 0xC0150004, + MD_NTSTATUS_WIN_STATUS_SXS_MANIFEST_FORMAT_ERROR = 0xC0150005, + MD_NTSTATUS_WIN_STATUS_SXS_MANIFEST_PARSE_ERROR = 0xC0150006, + MD_NTSTATUS_WIN_STATUS_SXS_ACTIVATION_CONTEXT_DISABLED = 0xC0150007, + MD_NTSTATUS_WIN_STATUS_SXS_KEY_NOT_FOUND = 0xC0150008, + MD_NTSTATUS_WIN_STATUS_SXS_VERSION_CONFLICT = 0xC0150009, + MD_NTSTATUS_WIN_STATUS_SXS_WRONG_SECTION_TYPE = 0xC015000A, + MD_NTSTATUS_WIN_STATUS_SXS_THREAD_QUERIES_DISABLED = 0xC015000B, + MD_NTSTATUS_WIN_STATUS_SXS_ASSEMBLY_MISSING = 0xC015000C, + MD_NTSTATUS_WIN_STATUS_SXS_PROCESS_DEFAULT_ALREADY_SET = 0xC015000E, + MD_NTSTATUS_WIN_STATUS_SXS_EARLY_DEACTIVATION = 0xC015000F, + MD_NTSTATUS_WIN_STATUS_SXS_INVALID_DEACTIVATION = 0xC0150010, + MD_NTSTATUS_WIN_STATUS_SXS_MULTIPLE_DEACTIVATION = 0xC0150011, + MD_NTSTATUS_WIN_STATUS_SXS_SYSTEM_DEFAULT_ACTIVATION_CONTEXT_EMPTY = 0xC0150012, + MD_NTSTATUS_WIN_STATUS_SXS_PROCESS_TERMINATION_REQUESTED = 0xC0150013, + MD_NTSTATUS_WIN_STATUS_SXS_CORRUPT_ACTIVATION_STACK = 0xC0150014, + MD_NTSTATUS_WIN_STATUS_SXS_CORRUPTION = 0xC0150015, + MD_NTSTATUS_WIN_STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_VALUE = 0xC0150016, + MD_NTSTATUS_WIN_STATUS_SXS_INVALID_IDENTITY_ATTRIBUTE_NAME = 0xC0150017, + MD_NTSTATUS_WIN_STATUS_SXS_IDENTITY_DUPLICATE_ATTRIBUTE = 0xC0150018, + MD_NTSTATUS_WIN_STATUS_SXS_IDENTITY_PARSE_ERROR = 0xC0150019, + MD_NTSTATUS_WIN_STATUS_SXS_COMPONENT_STORE_CORRUPT = 0xC015001A, + MD_NTSTATUS_WIN_STATUS_SXS_FILE_HASH_MISMATCH = 0xC015001B, + MD_NTSTATUS_WIN_STATUS_SXS_MANIFEST_IDENTITY_SAME_BUT_CONTENTS_DIFFERENT = 0xC015001C, + MD_NTSTATUS_WIN_STATUS_SXS_IDENTITIES_DIFFERENT = 0xC015001D, + MD_NTSTATUS_WIN_STATUS_SXS_ASSEMBLY_IS_NOT_A_DEPLOYMENT = 0xC015001E, + MD_NTSTATUS_WIN_STATUS_SXS_FILE_NOT_PART_OF_ASSEMBLY = 0xC015001F, + MD_NTSTATUS_WIN_STATUS_ADVANCED_INSTALLER_FAILED = 0xC0150020, + MD_NTSTATUS_WIN_STATUS_XML_ENCODING_MISMATCH = 0xC0150021, + MD_NTSTATUS_WIN_STATUS_SXS_MANIFEST_TOO_BIG = 0xC0150022, + MD_NTSTATUS_WIN_STATUS_SXS_SETTING_NOT_REGISTERED = 0xC0150023, + MD_NTSTATUS_WIN_STATUS_SXS_TRANSACTION_CLOSURE_INCOMPLETE = 0xC0150024, + MD_NTSTATUS_WIN_STATUS_SMI_PRIMITIVE_INSTALLER_FAILED = 0xC0150025, + MD_NTSTATUS_WIN_STATUS_GENERIC_COMMAND_FAILED = 0xC0150026, + MD_NTSTATUS_WIN_STATUS_SXS_FILE_HASH_MISSING = 0xC0150027, + MD_NTSTATUS_WIN_STATUS_TRANSACTIONAL_CONFLICT = 0xC0190001, + MD_NTSTATUS_WIN_STATUS_INVALID_TRANSACTION = 0xC0190002, + MD_NTSTATUS_WIN_STATUS_TRANSACTION_NOT_ACTIVE = 0xC0190003, + MD_NTSTATUS_WIN_STATUS_TM_INITIALIZATION_FAILED = 0xC0190004, + MD_NTSTATUS_WIN_STATUS_RM_NOT_ACTIVE = 0xC0190005, + MD_NTSTATUS_WIN_STATUS_RM_METADATA_CORRUPT = 0xC0190006, + MD_NTSTATUS_WIN_STATUS_TRANSACTION_NOT_JOINED = 0xC0190007, + MD_NTSTATUS_WIN_STATUS_DIRECTORY_NOT_RM = 0xC0190008, + MD_NTSTATUS_WIN_STATUS_TRANSACTIONS_UNSUPPORTED_REMOTE = 0xC019000A, + MD_NTSTATUS_WIN_STATUS_LOG_RESIZE_INVALID_SIZE = 0xC019000B, + MD_NTSTATUS_WIN_STATUS_REMOTE_FILE_VERSION_MISMATCH = 0xC019000C, + MD_NTSTATUS_WIN_STATUS_CRM_PROTOCOL_ALREADY_EXISTS = 0xC019000F, + MD_NTSTATUS_WIN_STATUS_TRANSACTION_PROPAGATION_FAILED = 0xC0190010, + MD_NTSTATUS_WIN_STATUS_CRM_PROTOCOL_NOT_FOUND = 0xC0190011, + MD_NTSTATUS_WIN_STATUS_TRANSACTION_SUPERIOR_EXISTS = 0xC0190012, + MD_NTSTATUS_WIN_STATUS_TRANSACTION_REQUEST_NOT_VALID = 0xC0190013, + MD_NTSTATUS_WIN_STATUS_TRANSACTION_NOT_REQUESTED = 0xC0190014, + MD_NTSTATUS_WIN_STATUS_TRANSACTION_ALREADY_ABORTED = 0xC0190015, + MD_NTSTATUS_WIN_STATUS_TRANSACTION_ALREADY_COMMITTED = 0xC0190016, + MD_NTSTATUS_WIN_STATUS_TRANSACTION_INVALID_MARSHALL_BUFFER = 0xC0190017, + MD_NTSTATUS_WIN_STATUS_CURRENT_TRANSACTION_NOT_VALID = 0xC0190018, + MD_NTSTATUS_WIN_STATUS_LOG_GROWTH_FAILED = 0xC0190019, + MD_NTSTATUS_WIN_STATUS_OBJECT_NO_LONGER_EXISTS = 0xC0190021, + MD_NTSTATUS_WIN_STATUS_STREAM_MINIVERSION_NOT_FOUND = 0xC0190022, + MD_NTSTATUS_WIN_STATUS_STREAM_MINIVERSION_NOT_VALID = 0xC0190023, + MD_NTSTATUS_WIN_STATUS_MINIVERSION_INACCESSIBLE_FROM_SPECIFIED_TRANSACTION = 0xC0190024, + MD_NTSTATUS_WIN_STATUS_CANT_OPEN_MINIVERSION_WITH_MODIFY_INTENT = 0xC0190025, + MD_NTSTATUS_WIN_STATUS_CANT_CREATE_MORE_STREAM_MINIVERSIONS = 0xC0190026, + MD_NTSTATUS_WIN_STATUS_HANDLE_NO_LONGER_VALID = 0xC0190028, + MD_NTSTATUS_WIN_STATUS_LOG_CORRUPTION_DETECTED = 0xC0190030, + MD_NTSTATUS_WIN_STATUS_RM_DISCONNECTED = 0xC0190032, + MD_NTSTATUS_WIN_STATUS_ENLISTMENT_NOT_SUPERIOR = 0xC0190033, + MD_NTSTATUS_WIN_STATUS_FILE_IDENTITY_NOT_PERSISTENT = 0xC0190036, + MD_NTSTATUS_WIN_STATUS_CANT_BREAK_TRANSACTIONAL_DEPENDENCY = 0xC0190037, + MD_NTSTATUS_WIN_STATUS_CANT_CROSS_RM_BOUNDARY = 0xC0190038, + MD_NTSTATUS_WIN_STATUS_TXF_DIR_NOT_EMPTY = 0xC0190039, + MD_NTSTATUS_WIN_STATUS_INDOUBT_TRANSACTIONS_EXIST = 0xC019003A, + MD_NTSTATUS_WIN_STATUS_TM_VOLATILE = 0xC019003B, + MD_NTSTATUS_WIN_STATUS_ROLLBACK_TIMER_EXPIRED = 0xC019003C, + MD_NTSTATUS_WIN_STATUS_TXF_ATTRIBUTE_CORRUPT = 0xC019003D, + MD_NTSTATUS_WIN_STATUS_EFS_NOT_ALLOWED_IN_TRANSACTION = 0xC019003E, + MD_NTSTATUS_WIN_STATUS_TRANSACTIONAL_OPEN_NOT_ALLOWED = 0xC019003F, + MD_NTSTATUS_WIN_STATUS_TRANSACTED_MAPPING_UNSUPPORTED_REMOTE = 0xC0190040, + MD_NTSTATUS_WIN_STATUS_TRANSACTION_REQUIRED_PROMOTION = 0xC0190043, + MD_NTSTATUS_WIN_STATUS_CANNOT_EXECUTE_FILE_IN_TRANSACTION = 0xC0190044, + MD_NTSTATUS_WIN_STATUS_TRANSACTIONS_NOT_FROZEN = 0xC0190045, + MD_NTSTATUS_WIN_STATUS_TRANSACTION_FREEZE_IN_PROGRESS = 0xC0190046, + MD_NTSTATUS_WIN_STATUS_NOT_SNAPSHOT_VOLUME = 0xC0190047, + MD_NTSTATUS_WIN_STATUS_NO_SAVEPOINT_WITH_OPEN_FILES = 0xC0190048, + MD_NTSTATUS_WIN_STATUS_SPARSE_NOT_ALLOWED_IN_TRANSACTION = 0xC0190049, + MD_NTSTATUS_WIN_STATUS_TM_IDENTITY_MISMATCH = 0xC019004A, + MD_NTSTATUS_WIN_STATUS_FLOATED_SECTION = 0xC019004B, + MD_NTSTATUS_WIN_STATUS_CANNOT_ACCEPT_TRANSACTED_WORK = 0xC019004C, + MD_NTSTATUS_WIN_STATUS_CANNOT_ABORT_TRANSACTIONS = 0xC019004D, + MD_NTSTATUS_WIN_STATUS_TRANSACTION_NOT_FOUND = 0xC019004E, + MD_NTSTATUS_WIN_STATUS_RESOURCEMANAGER_NOT_FOUND = 0xC019004F, + MD_NTSTATUS_WIN_STATUS_ENLISTMENT_NOT_FOUND = 0xC0190050, + MD_NTSTATUS_WIN_STATUS_TRANSACTIONMANAGER_NOT_FOUND = 0xC0190051, + MD_NTSTATUS_WIN_STATUS_TRANSACTIONMANAGER_NOT_ONLINE = 0xC0190052, + MD_NTSTATUS_WIN_STATUS_TRANSACTIONMANAGER_RECOVERY_NAME_COLLISION = 0xC0190053, + MD_NTSTATUS_WIN_STATUS_TRANSACTION_NOT_ROOT = 0xC0190054, + MD_NTSTATUS_WIN_STATUS_TRANSACTION_OBJECT_EXPIRED = 0xC0190055, + MD_NTSTATUS_WIN_STATUS_COMPRESSION_NOT_ALLOWED_IN_TRANSACTION = 0xC0190056, + MD_NTSTATUS_WIN_STATUS_TRANSACTION_RESPONSE_NOT_ENLISTED = 0xC0190057, + MD_NTSTATUS_WIN_STATUS_TRANSACTION_RECORD_TOO_LONG = 0xC0190058, + MD_NTSTATUS_WIN_STATUS_NO_LINK_TRACKING_IN_TRANSACTION = 0xC0190059, + MD_NTSTATUS_WIN_STATUS_OPERATION_NOT_SUPPORTED_IN_TRANSACTION = 0xC019005A, + MD_NTSTATUS_WIN_STATUS_TRANSACTION_INTEGRITY_VIOLATED = 0xC019005B, + MD_NTSTATUS_WIN_STATUS_TRANSACTIONMANAGER_IDENTITY_MISMATCH = 0xC019005C, + MD_NTSTATUS_WIN_STATUS_RM_CANNOT_BE_FROZEN_FOR_SNAPSHOT = 0xC019005D, + MD_NTSTATUS_WIN_STATUS_TRANSACTION_MUST_WRITETHROUGH = 0xC019005E, + MD_NTSTATUS_WIN_STATUS_TRANSACTION_NO_SUPERIOR = 0xC019005F, + MD_NTSTATUS_WIN_STATUS_EXPIRED_HANDLE = 0xC0190060, + MD_NTSTATUS_WIN_STATUS_TRANSACTION_NOT_ENLISTED = 0xC0190061, + MD_NTSTATUS_WIN_STATUS_LOG_SECTOR_INVALID = 0xC01A0001, + MD_NTSTATUS_WIN_STATUS_LOG_SECTOR_PARITY_INVALID = 0xC01A0002, + MD_NTSTATUS_WIN_STATUS_LOG_SECTOR_REMAPPED = 0xC01A0003, + MD_NTSTATUS_WIN_STATUS_LOG_BLOCK_INCOMPLETE = 0xC01A0004, + MD_NTSTATUS_WIN_STATUS_LOG_INVALID_RANGE = 0xC01A0005, + MD_NTSTATUS_WIN_STATUS_LOG_BLOCKS_EXHAUSTED = 0xC01A0006, + MD_NTSTATUS_WIN_STATUS_LOG_READ_CONTEXT_INVALID = 0xC01A0007, + MD_NTSTATUS_WIN_STATUS_LOG_RESTART_INVALID = 0xC01A0008, + MD_NTSTATUS_WIN_STATUS_LOG_BLOCK_VERSION = 0xC01A0009, + MD_NTSTATUS_WIN_STATUS_LOG_BLOCK_INVALID = 0xC01A000A, + MD_NTSTATUS_WIN_STATUS_LOG_READ_MODE_INVALID = 0xC01A000B, + MD_NTSTATUS_WIN_STATUS_LOG_METADATA_CORRUPT = 0xC01A000D, + MD_NTSTATUS_WIN_STATUS_LOG_METADATA_INVALID = 0xC01A000E, + MD_NTSTATUS_WIN_STATUS_LOG_METADATA_INCONSISTENT = 0xC01A000F, + MD_NTSTATUS_WIN_STATUS_LOG_RESERVATION_INVALID = 0xC01A0010, + MD_NTSTATUS_WIN_STATUS_LOG_CANT_DELETE = 0xC01A0011, + MD_NTSTATUS_WIN_STATUS_LOG_CONTAINER_LIMIT_EXCEEDED = 0xC01A0012, + MD_NTSTATUS_WIN_STATUS_LOG_START_OF_LOG = 0xC01A0013, + MD_NTSTATUS_WIN_STATUS_LOG_POLICY_ALREADY_INSTALLED = 0xC01A0014, + MD_NTSTATUS_WIN_STATUS_LOG_POLICY_NOT_INSTALLED = 0xC01A0015, + MD_NTSTATUS_WIN_STATUS_LOG_POLICY_INVALID = 0xC01A0016, + MD_NTSTATUS_WIN_STATUS_LOG_POLICY_CONFLICT = 0xC01A0017, + MD_NTSTATUS_WIN_STATUS_LOG_PINNED_ARCHIVE_TAIL = 0xC01A0018, + MD_NTSTATUS_WIN_STATUS_LOG_RECORD_NONEXISTENT = 0xC01A0019, + MD_NTSTATUS_WIN_STATUS_LOG_RECORDS_RESERVED_INVALID = 0xC01A001A, + MD_NTSTATUS_WIN_STATUS_LOG_SPACE_RESERVED_INVALID = 0xC01A001B, + MD_NTSTATUS_WIN_STATUS_LOG_TAIL_INVALID = 0xC01A001C, + MD_NTSTATUS_WIN_STATUS_LOG_FULL = 0xC01A001D, + MD_NTSTATUS_WIN_STATUS_LOG_MULTIPLEXED = 0xC01A001E, + MD_NTSTATUS_WIN_STATUS_LOG_DEDICATED = 0xC01A001F, + MD_NTSTATUS_WIN_STATUS_LOG_ARCHIVE_NOT_IN_PROGRESS = 0xC01A0020, + MD_NTSTATUS_WIN_STATUS_LOG_ARCHIVE_IN_PROGRESS = 0xC01A0021, + MD_NTSTATUS_WIN_STATUS_LOG_EPHEMERAL = 0xC01A0022, + MD_NTSTATUS_WIN_STATUS_LOG_NOT_ENOUGH_CONTAINERS = 0xC01A0023, + MD_NTSTATUS_WIN_STATUS_LOG_CLIENT_ALREADY_REGISTERED = 0xC01A0024, + MD_NTSTATUS_WIN_STATUS_LOG_CLIENT_NOT_REGISTERED = 0xC01A0025, + MD_NTSTATUS_WIN_STATUS_LOG_FULL_HANDLER_IN_PROGRESS = 0xC01A0026, + MD_NTSTATUS_WIN_STATUS_LOG_CONTAINER_READ_FAILED = 0xC01A0027, + MD_NTSTATUS_WIN_STATUS_LOG_CONTAINER_WRITE_FAILED = 0xC01A0028, + MD_NTSTATUS_WIN_STATUS_LOG_CONTAINER_OPEN_FAILED = 0xC01A0029, + MD_NTSTATUS_WIN_STATUS_LOG_CONTAINER_STATE_INVALID = 0xC01A002A, + MD_NTSTATUS_WIN_STATUS_LOG_STATE_INVALID = 0xC01A002B, + MD_NTSTATUS_WIN_STATUS_LOG_PINNED = 0xC01A002C, + MD_NTSTATUS_WIN_STATUS_LOG_METADATA_FLUSH_FAILED = 0xC01A002D, + MD_NTSTATUS_WIN_STATUS_LOG_INCONSISTENT_SECURITY = 0xC01A002E, + MD_NTSTATUS_WIN_STATUS_LOG_APPENDED_FLUSH_FAILED = 0xC01A002F, + MD_NTSTATUS_WIN_STATUS_LOG_PINNED_RESERVATION = 0xC01A0030, + MD_NTSTATUS_WIN_STATUS_VIDEO_HUNG_DISPLAY_DRIVER_THREAD = 0xC01B00EA, + MD_NTSTATUS_WIN_STATUS_FLT_NO_HANDLER_DEFINED = 0xC01C0001, + MD_NTSTATUS_WIN_STATUS_FLT_CONTEXT_ALREADY_DEFINED = 0xC01C0002, + MD_NTSTATUS_WIN_STATUS_FLT_INVALID_ASYNCHRONOUS_REQUEST = 0xC01C0003, + MD_NTSTATUS_WIN_STATUS_FLT_DISALLOW_FAST_IO = 0xC01C0004, + MD_NTSTATUS_WIN_STATUS_FLT_INVALID_NAME_REQUEST = 0xC01C0005, + MD_NTSTATUS_WIN_STATUS_FLT_NOT_SAFE_TO_POST_OPERATION = 0xC01C0006, + MD_NTSTATUS_WIN_STATUS_FLT_NOT_INITIALIZED = 0xC01C0007, + MD_NTSTATUS_WIN_STATUS_FLT_FILTER_NOT_READY = 0xC01C0008, + MD_NTSTATUS_WIN_STATUS_FLT_POST_OPERATION_CLEANUP = 0xC01C0009, + MD_NTSTATUS_WIN_STATUS_FLT_INTERNAL_ERROR = 0xC01C000A, + MD_NTSTATUS_WIN_STATUS_FLT_DELETING_OBJECT = 0xC01C000B, + MD_NTSTATUS_WIN_STATUS_FLT_MUST_BE_NONPAGED_POOL = 0xC01C000C, + MD_NTSTATUS_WIN_STATUS_FLT_DUPLICATE_ENTRY = 0xC01C000D, + MD_NTSTATUS_WIN_STATUS_FLT_CBDQ_DISABLED = 0xC01C000E, + MD_NTSTATUS_WIN_STATUS_FLT_DO_NOT_ATTACH = 0xC01C000F, + MD_NTSTATUS_WIN_STATUS_FLT_DO_NOT_DETACH = 0xC01C0010, + MD_NTSTATUS_WIN_STATUS_FLT_INSTANCE_ALTITUDE_COLLISION = 0xC01C0011, + MD_NTSTATUS_WIN_STATUS_FLT_INSTANCE_NAME_COLLISION = 0xC01C0012, + MD_NTSTATUS_WIN_STATUS_FLT_FILTER_NOT_FOUND = 0xC01C0013, + MD_NTSTATUS_WIN_STATUS_FLT_VOLUME_NOT_FOUND = 0xC01C0014, + MD_NTSTATUS_WIN_STATUS_FLT_INSTANCE_NOT_FOUND = 0xC01C0015, + MD_NTSTATUS_WIN_STATUS_FLT_CONTEXT_ALLOCATION_NOT_FOUND = 0xC01C0016, + MD_NTSTATUS_WIN_STATUS_FLT_INVALID_CONTEXT_REGISTRATION = 0xC01C0017, + MD_NTSTATUS_WIN_STATUS_FLT_NAME_CACHE_MISS = 0xC01C0018, + MD_NTSTATUS_WIN_STATUS_FLT_NO_DEVICE_OBJECT = 0xC01C0019, + MD_NTSTATUS_WIN_STATUS_FLT_VOLUME_ALREADY_MOUNTED = 0xC01C001A, + MD_NTSTATUS_WIN_STATUS_FLT_ALREADY_ENLISTED = 0xC01C001B, + MD_NTSTATUS_WIN_STATUS_FLT_CONTEXT_ALREADY_LINKED = 0xC01C001C, + MD_NTSTATUS_WIN_STATUS_FLT_NO_WAITER_FOR_REPLY = 0xC01C0020, + MD_NTSTATUS_WIN_STATUS_FLT_REGISTRATION_BUSY = 0xC01C0023, + MD_NTSTATUS_WIN_STATUS_MONITOR_NO_DESCRIPTOR = 0xC01D0001, + MD_NTSTATUS_WIN_STATUS_MONITOR_UNKNOWN_DESCRIPTOR_FORMAT = 0xC01D0002, + MD_NTSTATUS_WIN_STATUS_MONITOR_INVALID_DESCRIPTOR_CHECKSUM = 0xC01D0003, + MD_NTSTATUS_WIN_STATUS_MONITOR_INVALID_STANDARD_TIMING_BLOCK = 0xC01D0004, + MD_NTSTATUS_WIN_STATUS_MONITOR_WMI_DATABLOCK_REGISTRATION_FAILED = 0xC01D0005, + MD_NTSTATUS_WIN_STATUS_MONITOR_INVALID_SERIAL_NUMBER_MONDSC_BLOCK = 0xC01D0006, + MD_NTSTATUS_WIN_STATUS_MONITOR_INVALID_USER_FRIENDLY_MONDSC_BLOCK = 0xC01D0007, + MD_NTSTATUS_WIN_STATUS_MONITOR_NO_MORE_DESCRIPTOR_DATA = 0xC01D0008, + MD_NTSTATUS_WIN_STATUS_MONITOR_INVALID_DETAILED_TIMING_BLOCK = 0xC01D0009, + MD_NTSTATUS_WIN_STATUS_MONITOR_INVALID_MANUFACTURE_DATE = 0xC01D000A, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_NOT_EXCLUSIVE_MODE_OWNER = 0xC01E0000, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INSUFFICIENT_DMA_BUFFER = 0xC01E0001, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_DISPLAY_ADAPTER = 0xC01E0002, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_ADAPTER_WAS_RESET = 0xC01E0003, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_DRIVER_MODEL = 0xC01E0004, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_PRESENT_MODE_CHANGED = 0xC01E0005, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_PRESENT_OCCLUDED = 0xC01E0006, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_PRESENT_DENIED = 0xC01E0007, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_CANNOTCOLORCONVERT = 0xC01E0008, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_DRIVER_MISMATCH = 0xC01E0009, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_PRESENT_REDIRECTION_DISABLED = 0xC01E000B, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_PRESENT_UNOCCLUDED = 0xC01E000C, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_WINDOWDC_NOT_AVAILABLE = 0xC01E000D, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_WINDOWLESS_PRESENT_DISABLED = 0xC01E000E, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_NO_VIDEO_MEMORY = 0xC01E0100, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_CANT_LOCK_MEMORY = 0xC01E0101, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_ALLOCATION_BUSY = 0xC01E0102, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_TOO_MANY_REFERENCES = 0xC01E0103, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_TRY_AGAIN_LATER = 0xC01E0104, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_TRY_AGAIN_NOW = 0xC01E0105, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_ALLOCATION_INVALID = 0xC01E0106, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNAVAILABLE = 0xC01E0107, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_UNSWIZZLING_APERTURE_UNSUPPORTED = 0xC01E0108, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_CANT_EVICT_PINNED_ALLOCATION = 0xC01E0109, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_ALLOCATION_USAGE = 0xC01E0110, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_CANT_RENDER_LOCKED_ALLOCATION = 0xC01E0111, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_ALLOCATION_CLOSED = 0xC01E0112, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_ALLOCATION_INSTANCE = 0xC01E0113, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_ALLOCATION_HANDLE = 0xC01E0114, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_WRONG_ALLOCATION_DEVICE = 0xC01E0115, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_ALLOCATION_CONTENT_LOST = 0xC01E0116, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_GPU_EXCEPTION_ON_DEVICE = 0xC01E0200, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY = 0xC01E0300, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_VIDPN_TOPOLOGY_NOT_SUPPORTED = 0xC01E0301, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_VIDPN_TOPOLOGY_CURRENTLY_NOT_SUPPORTED = 0xC01E0302, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_VIDPN = 0xC01E0303, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE = 0xC01E0304, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET = 0xC01E0305, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_VIDPN_MODALITY_NOT_SUPPORTED = 0xC01E0306, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_VIDPN_SOURCEMODESET = 0xC01E0308, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_VIDPN_TARGETMODESET = 0xC01E0309, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_FREQUENCY = 0xC01E030A, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_ACTIVE_REGION = 0xC01E030B, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_TOTAL_REGION = 0xC01E030C, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_SOURCE_MODE = 0xC01E0310, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_VIDEO_PRESENT_TARGET_MODE = 0xC01E0311, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_PINNED_MODE_MUST_REMAIN_IN_SET = 0xC01E0312, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_PATH_ALREADY_IN_TOPOLOGY = 0xC01E0313, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_MODE_ALREADY_IN_MODESET = 0xC01E0314, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_VIDEOPRESENTSOURCESET = 0xC01E0315, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_VIDEOPRESENTTARGETSET = 0xC01E0316, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_SOURCE_ALREADY_IN_SET = 0xC01E0317, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_TARGET_ALREADY_IN_SET = 0xC01E0318, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_VIDPN_PRESENT_PATH = 0xC01E0319, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_NO_RECOMMENDED_VIDPN_TOPOLOGY = 0xC01E031A, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGESET = 0xC01E031B, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE = 0xC01E031C, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_FREQUENCYRANGE_NOT_IN_SET = 0xC01E031D, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_FREQUENCYRANGE_ALREADY_IN_SET = 0xC01E031F, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_STALE_MODESET = 0xC01E0320, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_MONITOR_SOURCEMODESET = 0xC01E0321, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_MONITOR_SOURCE_MODE = 0xC01E0322, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_NO_RECOMMENDED_FUNCTIONAL_VIDPN = 0xC01E0323, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_MODE_ID_MUST_BE_UNIQUE = 0xC01E0324, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_EMPTY_ADAPTER_MONITOR_MODE_SUPPORT_INTERSECTION = 0xC01E0325, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_VIDEO_PRESENT_TARGETS_LESS_THAN_SOURCES = 0xC01E0326, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_PATH_NOT_IN_TOPOLOGY = 0xC01E0327, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_SOURCE = 0xC01E0328, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_ADAPTER_MUST_HAVE_AT_LEAST_ONE_TARGET = 0xC01E0329, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_MONITORDESCRIPTORSET = 0xC01E032A, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_MONITORDESCRIPTOR = 0xC01E032B, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_MONITORDESCRIPTOR_NOT_IN_SET = 0xC01E032C, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_MONITORDESCRIPTOR_ALREADY_IN_SET = 0xC01E032D, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_MONITORDESCRIPTOR_ID_MUST_BE_UNIQUE = 0xC01E032E, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_VIDPN_TARGET_SUBSET_TYPE = 0xC01E032F, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_RESOURCES_NOT_RELATED = 0xC01E0330, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_SOURCE_ID_MUST_BE_UNIQUE = 0xC01E0331, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_TARGET_ID_MUST_BE_UNIQUE = 0xC01E0332, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_NO_AVAILABLE_VIDPN_TARGET = 0xC01E0333, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_MONITOR_COULD_NOT_BE_ASSOCIATED_WITH_ADAPTER = 0xC01E0334, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_NO_VIDPNMGR = 0xC01E0335, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_NO_ACTIVE_VIDPN = 0xC01E0336, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_STALE_VIDPN_TOPOLOGY = 0xC01E0337, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_MONITOR_NOT_CONNECTED = 0xC01E0338, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_SOURCE_NOT_IN_TOPOLOGY = 0xC01E0339, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_PRIMARYSURFACE_SIZE = 0xC01E033A, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_VISIBLEREGION_SIZE = 0xC01E033B, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_STRIDE = 0xC01E033C, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_PIXELFORMAT = 0xC01E033D, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_COLORBASIS = 0xC01E033E, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_PIXELVALUEACCESSMODE = 0xC01E033F, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_TARGET_NOT_IN_TOPOLOGY = 0xC01E0340, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_NO_DISPLAY_MODE_MANAGEMENT_SUPPORT = 0xC01E0341, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_VIDPN_SOURCE_IN_USE = 0xC01E0342, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_CANT_ACCESS_ACTIVE_VIDPN = 0xC01E0343, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_PATH_IMPORTANCE_ORDINAL = 0xC01E0344, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_PATH_CONTENT_GEOMETRY_TRANSFORMATION = 0xC01E0345, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_PATH_CONTENT_GEOMETRY_TRANSFORMATION_NOT_SUPPORTED = 0xC01E0346, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_GAMMA_RAMP = 0xC01E0347, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_GAMMA_RAMP_NOT_SUPPORTED = 0xC01E0348, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_MULTISAMPLING_NOT_SUPPORTED = 0xC01E0349, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_MODE_NOT_IN_MODESET = 0xC01E034A, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_VIDPN_TOPOLOGY_RECOMMENDATION_REASON = 0xC01E034D, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_PATH_CONTENT_TYPE = 0xC01E034E, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_COPYPROTECTION_TYPE = 0xC01E034F, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_UNASSIGNED_MODESET_ALREADY_EXISTS = 0xC01E0350, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_SCANLINE_ORDERING = 0xC01E0352, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_TOPOLOGY_CHANGES_NOT_ALLOWED = 0xC01E0353, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_NO_AVAILABLE_IMPORTANCE_ORDINALS = 0xC01E0354, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INCOMPATIBLE_PRIVATE_FORMAT = 0xC01E0355, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_MODE_PRUNING_ALGORITHM = 0xC01E0356, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_MONITOR_CAPABILITY_ORIGIN = 0xC01E0357, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_MONITOR_FREQUENCYRANGE_CONSTRAINT = 0xC01E0358, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_MAX_NUM_PATHS_REACHED = 0xC01E0359, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_CANCEL_VIDPN_TOPOLOGY_AUGMENTATION = 0xC01E035A, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_CLIENT_TYPE = 0xC01E035B, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_CLIENTVIDPN_NOT_SET = 0xC01E035C, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_SPECIFIED_CHILD_ALREADY_CONNECTED = 0xC01E0400, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_CHILD_DESCRIPTOR_NOT_SUPPORTED = 0xC01E0401, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_NOT_A_LINKED_ADAPTER = 0xC01E0430, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_LEADLINK_NOT_ENUMERATED = 0xC01E0431, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_CHAINLINKS_NOT_ENUMERATED = 0xC01E0432, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_ADAPTER_CHAIN_NOT_READY = 0xC01E0433, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_CHAINLINKS_NOT_STARTED = 0xC01E0434, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_CHAINLINKS_NOT_POWERED_ON = 0xC01E0435, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INCONSISTENT_DEVICE_LINK_STATE = 0xC01E0436, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_NOT_POST_DEVICE_DRIVER = 0xC01E0438, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_ADAPTER_ACCESS_NOT_EXCLUDED = 0xC01E043B, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_NOT_SUPPORTED = 0xC01E0500, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_COPP_NOT_SUPPORTED = 0xC01E0501, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_UAB_NOT_SUPPORTED = 0xC01E0502, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_INVALID_ENCRYPTED_PARAMETERS = 0xC01E0503, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_NO_PROTECTED_OUTPUTS_EXIST = 0xC01E0505, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_INTERNAL_ERROR = 0xC01E050B, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_INVALID_HANDLE = 0xC01E050C, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_PVP_INVALID_CERTIFICATE_LENGTH = 0xC01E050E, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_SPANNING_MODE_ENABLED = 0xC01E050F, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_THEATER_MODE_ENABLED = 0xC01E0510, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_PVP_HFS_FAILED = 0xC01E0511, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_INVALID_SRM = 0xC01E0512, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_HDCP = 0xC01E0513, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_ACP = 0xC01E0514, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_OUTPUT_DOES_NOT_SUPPORT_CGMSA = 0xC01E0515, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_HDCP_SRM_NEVER_SET = 0xC01E0516, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_RESOLUTION_TOO_HIGH = 0xC01E0517, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_ALL_HDCP_HARDWARE_ALREADY_IN_USE = 0xC01E0518, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_NO_LONGER_EXISTS = 0xC01E051A, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_COPP_SEMANTICS = 0xC01E051C, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_INVALID_INFORMATION_REQUEST = 0xC01E051D, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_DRIVER_INTERNAL_ERROR = 0xC01E051E, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_PROTECTED_OUTPUT_DOES_NOT_HAVE_OPM_SEMANTICS = 0xC01E051F, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_SIGNALING_NOT_SUPPORTED = 0xC01E0520, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_OPM_INVALID_CONFIGURATION_REQUEST = 0xC01E0521, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_I2C_NOT_SUPPORTED = 0xC01E0580, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_I2C_DEVICE_DOES_NOT_EXIST = 0xC01E0581, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_I2C_ERROR_TRANSMITTING_DATA = 0xC01E0582, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_I2C_ERROR_RECEIVING_DATA = 0xC01E0583, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_DDCCI_VCP_NOT_SUPPORTED = 0xC01E0584, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_DDCCI_INVALID_DATA = 0xC01E0585, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_DDCCI_MONITOR_RETURNED_INVALID_TIMING_STATUS_BYTE = 0xC01E0586, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_DDCCI_INVALID_CAPABILITIES_STRING = 0xC01E0587, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_MCA_INTERNAL_ERROR = 0xC01E0588, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_COMMAND = 0xC01E0589, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_LENGTH = 0xC01E058A, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_DDCCI_INVALID_MESSAGE_CHECKSUM = 0xC01E058B, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_PHYSICAL_MONITOR_HANDLE = 0xC01E058C, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_MONITOR_NO_LONGER_EXISTS = 0xC01E058D, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_ONLY_CONSOLE_SESSION_SUPPORTED = 0xC01E05E0, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_NO_DISPLAY_DEVICE_CORRESPONDS_TO_NAME = 0xC01E05E1, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_DISPLAY_DEVICE_NOT_ATTACHED_TO_DESKTOP = 0xC01E05E2, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_MIRRORING_DEVICES_NOT_SUPPORTED = 0xC01E05E3, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INVALID_POINTER = 0xC01E05E4, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_NO_MONITORS_CORRESPOND_TO_DISPLAY_DEVICE = 0xC01E05E5, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_PARAMETER_ARRAY_TOO_SMALL = 0xC01E05E6, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_INTERNAL_ERROR = 0xC01E05E7, + MD_NTSTATUS_WIN_STATUS_GRAPHICS_SESSION_TYPE_CHANGE_IN_PROGRESS = 0xC01E05E8, + MD_NTSTATUS_WIN_STATUS_FVE_LOCKED_VOLUME = 0xC0210000, + MD_NTSTATUS_WIN_STATUS_FVE_NOT_ENCRYPTED = 0xC0210001, + MD_NTSTATUS_WIN_STATUS_FVE_BAD_INFORMATION = 0xC0210002, + MD_NTSTATUS_WIN_STATUS_FVE_TOO_SMALL = 0xC0210003, + MD_NTSTATUS_WIN_STATUS_FVE_FAILED_WRONG_FS = 0xC0210004, + MD_NTSTATUS_WIN_STATUS_FVE_BAD_PARTITION_SIZE = 0xC0210005, + MD_NTSTATUS_WIN_STATUS_FVE_FS_NOT_EXTENDED = 0xC0210006, + MD_NTSTATUS_WIN_STATUS_FVE_FS_MOUNTED = 0xC0210007, + MD_NTSTATUS_WIN_STATUS_FVE_NO_LICENSE = 0xC0210008, + MD_NTSTATUS_WIN_STATUS_FVE_ACTION_NOT_ALLOWED = 0xC0210009, + MD_NTSTATUS_WIN_STATUS_FVE_BAD_DATA = 0xC021000A, + MD_NTSTATUS_WIN_STATUS_FVE_VOLUME_NOT_BOUND = 0xC021000B, + MD_NTSTATUS_WIN_STATUS_FVE_NOT_DATA_VOLUME = 0xC021000C, + MD_NTSTATUS_WIN_STATUS_FVE_CONV_READ_ERROR = 0xC021000D, + MD_NTSTATUS_WIN_STATUS_FVE_CONV_WRITE_ERROR = 0xC021000E, + MD_NTSTATUS_WIN_STATUS_FVE_OVERLAPPED_UPDATE = 0xC021000F, + MD_NTSTATUS_WIN_STATUS_FVE_FAILED_SECTOR_SIZE = 0xC0210010, + MD_NTSTATUS_WIN_STATUS_FVE_FAILED_AUTHENTICATION = 0xC0210011, + MD_NTSTATUS_WIN_STATUS_FVE_NOT_OS_VOLUME = 0xC0210012, + MD_NTSTATUS_WIN_STATUS_FVE_KEYFILE_NOT_FOUND = 0xC0210013, + MD_NTSTATUS_WIN_STATUS_FVE_KEYFILE_INVALID = 0xC0210014, + MD_NTSTATUS_WIN_STATUS_FVE_KEYFILE_NO_VMK = 0xC0210015, + MD_NTSTATUS_WIN_STATUS_FVE_TPM_DISABLED = 0xC0210016, + MD_NTSTATUS_WIN_STATUS_FVE_TPM_SRK_AUTH_NOT_ZERO = 0xC0210017, + MD_NTSTATUS_WIN_STATUS_FVE_TPM_INVALID_PCR = 0xC0210018, + MD_NTSTATUS_WIN_STATUS_FVE_TPM_NO_VMK = 0xC0210019, + MD_NTSTATUS_WIN_STATUS_FVE_PIN_INVALID = 0xC021001A, + MD_NTSTATUS_WIN_STATUS_FVE_AUTH_INVALID_APPLICATION = 0xC021001B, + MD_NTSTATUS_WIN_STATUS_FVE_AUTH_INVALID_CONFIG = 0xC021001C, + MD_NTSTATUS_WIN_STATUS_FVE_DEBUGGER_ENABLED = 0xC021001D, + MD_NTSTATUS_WIN_STATUS_FVE_DRY_RUN_FAILED = 0xC021001E, + MD_NTSTATUS_WIN_STATUS_FVE_BAD_METADATA_POINTER = 0xC021001F, + MD_NTSTATUS_WIN_STATUS_FVE_OLD_METADATA_COPY = 0xC0210020, + MD_NTSTATUS_WIN_STATUS_FVE_REBOOT_REQUIRED = 0xC0210021, + MD_NTSTATUS_WIN_STATUS_FVE_RAW_ACCESS = 0xC0210022, + MD_NTSTATUS_WIN_STATUS_FVE_RAW_BLOCKED = 0xC0210023, + MD_NTSTATUS_WIN_STATUS_FVE_NO_AUTOUNLOCK_MASTER_KEY = 0xC0210024, + MD_NTSTATUS_WIN_STATUS_FVE_MOR_FAILED = 0xC0210025, + MD_NTSTATUS_WIN_STATUS_FVE_NO_FEATURE_LICENSE = 0xC0210026, + MD_NTSTATUS_WIN_STATUS_FVE_POLICY_USER_DISABLE_RDV_NOT_ALLOWED = 0xC0210027, + MD_NTSTATUS_WIN_STATUS_FVE_CONV_RECOVERY_FAILED = 0xC0210028, + MD_NTSTATUS_WIN_STATUS_FVE_VIRTUALIZED_SPACE_TOO_BIG = 0xC0210029, + MD_NTSTATUS_WIN_STATUS_FVE_INVALID_DATUM_TYPE = 0xC021002A, + MD_NTSTATUS_WIN_STATUS_FVE_VOLUME_TOO_SMALL = 0xC0210030, + MD_NTSTATUS_WIN_STATUS_FVE_ENH_PIN_INVALID = 0xC0210031, + MD_NTSTATUS_WIN_STATUS_FVE_FULL_ENCRYPTION_NOT_ALLOWED_ON_TP_STORAGE = 0xC0210032, + MD_NTSTATUS_WIN_STATUS_FVE_WIPE_NOT_ALLOWED_ON_TP_STORAGE = 0xC0210033, + MD_NTSTATUS_WIN_STATUS_FVE_NOT_ALLOWED_ON_CSV_STACK = 0xC0210034, + MD_NTSTATUS_WIN_STATUS_FVE_NOT_ALLOWED_ON_CLUSTER = 0xC0210035, + MD_NTSTATUS_WIN_STATUS_FVE_NOT_ALLOWED_TO_UPGRADE_WHILE_CONVERTING = 0xC0210036, + MD_NTSTATUS_WIN_STATUS_FVE_WIPE_CANCEL_NOT_APPLICABLE = 0xC0210037, + MD_NTSTATUS_WIN_STATUS_FVE_EDRIVE_DRY_RUN_FAILED = 0xC0210038, + MD_NTSTATUS_WIN_STATUS_FVE_SECUREBOOT_DISABLED = 0xC0210039, + MD_NTSTATUS_WIN_STATUS_FVE_SECUREBOOT_CONFIG_CHANGE = 0xC021003A, + MD_NTSTATUS_WIN_STATUS_FVE_DEVICE_LOCKEDOUT = 0xC021003B, + MD_NTSTATUS_WIN_STATUS_FVE_VOLUME_EXTEND_PREVENTS_EOW_DECRYPT = 0xC021003C, + MD_NTSTATUS_WIN_STATUS_FVE_NOT_DE_VOLUME = 0xC021003D, + MD_NTSTATUS_WIN_STATUS_FVE_PROTECTION_DISABLED = 0xC021003E, + MD_NTSTATUS_WIN_STATUS_FVE_PROTECTION_CANNOT_BE_DISABLED = 0xC021003F, + MD_NTSTATUS_WIN_STATUS_FWP_CALLOUT_NOT_FOUND = 0xC0220001, + MD_NTSTATUS_WIN_STATUS_FWP_CONDITION_NOT_FOUND = 0xC0220002, + MD_NTSTATUS_WIN_STATUS_FWP_FILTER_NOT_FOUND = 0xC0220003, + MD_NTSTATUS_WIN_STATUS_FWP_LAYER_NOT_FOUND = 0xC0220004, + MD_NTSTATUS_WIN_STATUS_FWP_PROVIDER_NOT_FOUND = 0xC0220005, + MD_NTSTATUS_WIN_STATUS_FWP_PROVIDER_CONTEXT_NOT_FOUND = 0xC0220006, + MD_NTSTATUS_WIN_STATUS_FWP_SUBLAYER_NOT_FOUND = 0xC0220007, + MD_NTSTATUS_WIN_STATUS_FWP_NOT_FOUND = 0xC0220008, + MD_NTSTATUS_WIN_STATUS_FWP_ALREADY_EXISTS = 0xC0220009, + MD_NTSTATUS_WIN_STATUS_FWP_IN_USE = 0xC022000A, + MD_NTSTATUS_WIN_STATUS_FWP_DYNAMIC_SESSION_IN_PROGRESS = 0xC022000B, + MD_NTSTATUS_WIN_STATUS_FWP_WRONG_SESSION = 0xC022000C, + MD_NTSTATUS_WIN_STATUS_FWP_NO_TXN_IN_PROGRESS = 0xC022000D, + MD_NTSTATUS_WIN_STATUS_FWP_TXN_IN_PROGRESS = 0xC022000E, + MD_NTSTATUS_WIN_STATUS_FWP_TXN_ABORTED = 0xC022000F, + MD_NTSTATUS_WIN_STATUS_FWP_SESSION_ABORTED = 0xC0220010, + MD_NTSTATUS_WIN_STATUS_FWP_INCOMPATIBLE_TXN = 0xC0220011, + MD_NTSTATUS_WIN_STATUS_FWP_TIMEOUT = 0xC0220012, + MD_NTSTATUS_WIN_STATUS_FWP_NET_EVENTS_DISABLED = 0xC0220013, + MD_NTSTATUS_WIN_STATUS_FWP_INCOMPATIBLE_LAYER = 0xC0220014, + MD_NTSTATUS_WIN_STATUS_FWP_KM_CLIENTS_ONLY = 0xC0220015, + MD_NTSTATUS_WIN_STATUS_FWP_LIFETIME_MISMATCH = 0xC0220016, + MD_NTSTATUS_WIN_STATUS_FWP_BUILTIN_OBJECT = 0xC0220017, + MD_NTSTATUS_WIN_STATUS_FWP_TOO_MANY_CALLOUTS = 0xC0220018, + MD_NTSTATUS_WIN_STATUS_FWP_NOTIFICATION_DROPPED = 0xC0220019, + MD_NTSTATUS_WIN_STATUS_FWP_TRAFFIC_MISMATCH = 0xC022001A, + MD_NTSTATUS_WIN_STATUS_FWP_INCOMPATIBLE_SA_STATE = 0xC022001B, + MD_NTSTATUS_WIN_STATUS_FWP_NULL_POINTER = 0xC022001C, + MD_NTSTATUS_WIN_STATUS_FWP_INVALID_ENUMERATOR = 0xC022001D, + MD_NTSTATUS_WIN_STATUS_FWP_INVALID_FLAGS = 0xC022001E, + MD_NTSTATUS_WIN_STATUS_FWP_INVALID_NET_MASK = 0xC022001F, + MD_NTSTATUS_WIN_STATUS_FWP_INVALID_RANGE = 0xC0220020, + MD_NTSTATUS_WIN_STATUS_FWP_INVALID_INTERVAL = 0xC0220021, + MD_NTSTATUS_WIN_STATUS_FWP_ZERO_LENGTH_ARRAY = 0xC0220022, + MD_NTSTATUS_WIN_STATUS_FWP_NULL_DISPLAY_NAME = 0xC0220023, + MD_NTSTATUS_WIN_STATUS_FWP_INVALID_ACTION_TYPE = 0xC0220024, + MD_NTSTATUS_WIN_STATUS_FWP_INVALID_WEIGHT = 0xC0220025, + MD_NTSTATUS_WIN_STATUS_FWP_MATCH_TYPE_MISMATCH = 0xC0220026, + MD_NTSTATUS_WIN_STATUS_FWP_TYPE_MISMATCH = 0xC0220027, + MD_NTSTATUS_WIN_STATUS_FWP_OUT_OF_BOUNDS = 0xC0220028, + MD_NTSTATUS_WIN_STATUS_FWP_RESERVED = 0xC0220029, + MD_NTSTATUS_WIN_STATUS_FWP_DUPLICATE_CONDITION = 0xC022002A, + MD_NTSTATUS_WIN_STATUS_FWP_DUPLICATE_KEYMOD = 0xC022002B, + MD_NTSTATUS_WIN_STATUS_FWP_ACTION_INCOMPATIBLE_WITH_LAYER = 0xC022002C, + MD_NTSTATUS_WIN_STATUS_FWP_ACTION_INCOMPATIBLE_WITH_SUBLAYER = 0xC022002D, + MD_NTSTATUS_WIN_STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_LAYER = 0xC022002E, + MD_NTSTATUS_WIN_STATUS_FWP_CONTEXT_INCOMPATIBLE_WITH_CALLOUT = 0xC022002F, + MD_NTSTATUS_WIN_STATUS_FWP_INCOMPATIBLE_AUTH_METHOD = 0xC0220030, + MD_NTSTATUS_WIN_STATUS_FWP_INCOMPATIBLE_DH_GROUP = 0xC0220031, + MD_NTSTATUS_WIN_STATUS_FWP_EM_NOT_SUPPORTED = 0xC0220032, + MD_NTSTATUS_WIN_STATUS_FWP_NEVER_MATCH = 0xC0220033, + MD_NTSTATUS_WIN_STATUS_FWP_PROVIDER_CONTEXT_MISMATCH = 0xC0220034, + MD_NTSTATUS_WIN_STATUS_FWP_INVALID_PARAMETER = 0xC0220035, + MD_NTSTATUS_WIN_STATUS_FWP_TOO_MANY_SUBLAYERS = 0xC0220036, + MD_NTSTATUS_WIN_STATUS_FWP_CALLOUT_NOTIFICATION_FAILED = 0xC0220037, + MD_NTSTATUS_WIN_STATUS_FWP_INVALID_AUTH_TRANSFORM = 0xC0220038, + MD_NTSTATUS_WIN_STATUS_FWP_INVALID_CIPHER_TRANSFORM = 0xC0220039, + MD_NTSTATUS_WIN_STATUS_FWP_INCOMPATIBLE_CIPHER_TRANSFORM = 0xC022003A, + MD_NTSTATUS_WIN_STATUS_FWP_INVALID_TRANSFORM_COMBINATION = 0xC022003B, + MD_NTSTATUS_WIN_STATUS_FWP_DUPLICATE_AUTH_METHOD = 0xC022003C, + MD_NTSTATUS_WIN_STATUS_FWP_INVALID_TUNNEL_ENDPOINT = 0xC022003D, + MD_NTSTATUS_WIN_STATUS_FWP_L2_DRIVER_NOT_READY = 0xC022003E, + MD_NTSTATUS_WIN_STATUS_FWP_KEY_DICTATOR_ALREADY_REGISTERED = 0xC022003F, + MD_NTSTATUS_WIN_STATUS_FWP_KEY_DICTATION_INVALID_KEYING_MATERIAL = 0xC0220040, + MD_NTSTATUS_WIN_STATUS_FWP_CONNECTIONS_DISABLED = 0xC0220041, + MD_NTSTATUS_WIN_STATUS_FWP_INVALID_DNS_NAME = 0xC0220042, + MD_NTSTATUS_WIN_STATUS_FWP_STILL_ON = 0xC0220043, + MD_NTSTATUS_WIN_STATUS_FWP_IKEEXT_NOT_RUNNING = 0xC0220044, + MD_NTSTATUS_WIN_STATUS_FWP_TCPIP_NOT_READY = 0xC0220100, + MD_NTSTATUS_WIN_STATUS_FWP_INJECT_HANDLE_CLOSING = 0xC0220101, + MD_NTSTATUS_WIN_STATUS_FWP_INJECT_HANDLE_STALE = 0xC0220102, + MD_NTSTATUS_WIN_STATUS_FWP_CANNOT_PEND = 0xC0220103, + MD_NTSTATUS_WIN_STATUS_FWP_DROP_NOICMP = 0xC0220104, + MD_NTSTATUS_WIN_STATUS_NDIS_CLOSING = 0xC0230002, + MD_NTSTATUS_WIN_STATUS_NDIS_BAD_VERSION = 0xC0230004, + MD_NTSTATUS_WIN_STATUS_NDIS_BAD_CHARACTERISTICS = 0xC0230005, + MD_NTSTATUS_WIN_STATUS_NDIS_ADAPTER_NOT_FOUND = 0xC0230006, + MD_NTSTATUS_WIN_STATUS_NDIS_OPEN_FAILED = 0xC0230007, + MD_NTSTATUS_WIN_STATUS_NDIS_DEVICE_FAILED = 0xC0230008, + MD_NTSTATUS_WIN_STATUS_NDIS_MULTICAST_FULL = 0xC0230009, + MD_NTSTATUS_WIN_STATUS_NDIS_MULTICAST_EXISTS = 0xC023000A, + MD_NTSTATUS_WIN_STATUS_NDIS_MULTICAST_NOT_FOUND = 0xC023000B, + MD_NTSTATUS_WIN_STATUS_NDIS_REQUEST_ABORTED = 0xC023000C, + MD_NTSTATUS_WIN_STATUS_NDIS_RESET_IN_PROGRESS = 0xC023000D, + MD_NTSTATUS_WIN_STATUS_NDIS_INVALID_PACKET = 0xC023000F, + MD_NTSTATUS_WIN_STATUS_NDIS_INVALID_DEVICE_REQUEST = 0xC0230010, + MD_NTSTATUS_WIN_STATUS_NDIS_ADAPTER_NOT_READY = 0xC0230011, + MD_NTSTATUS_WIN_STATUS_NDIS_INVALID_LENGTH = 0xC0230014, + MD_NTSTATUS_WIN_STATUS_NDIS_INVALID_DATA = 0xC0230015, + MD_NTSTATUS_WIN_STATUS_NDIS_BUFFER_TOO_SHORT = 0xC0230016, + MD_NTSTATUS_WIN_STATUS_NDIS_INVALID_OID = 0xC0230017, + MD_NTSTATUS_WIN_STATUS_NDIS_ADAPTER_REMOVED = 0xC0230018, + MD_NTSTATUS_WIN_STATUS_NDIS_UNSUPPORTED_MEDIA = 0xC0230019, + MD_NTSTATUS_WIN_STATUS_NDIS_GROUP_ADDRESS_IN_USE = 0xC023001A, + MD_NTSTATUS_WIN_STATUS_NDIS_FILE_NOT_FOUND = 0xC023001B, + MD_NTSTATUS_WIN_STATUS_NDIS_ERROR_READING_FILE = 0xC023001C, + MD_NTSTATUS_WIN_STATUS_NDIS_ALREADY_MAPPED = 0xC023001D, + MD_NTSTATUS_WIN_STATUS_NDIS_RESOURCE_CONFLICT = 0xC023001E, + MD_NTSTATUS_WIN_STATUS_NDIS_MEDIA_DISCONNECTED = 0xC023001F, + MD_NTSTATUS_WIN_STATUS_NDIS_INVALID_ADDRESS = 0xC0230022, + MD_NTSTATUS_WIN_STATUS_NDIS_PAUSED = 0xC023002A, + MD_NTSTATUS_WIN_STATUS_NDIS_INTERFACE_NOT_FOUND = 0xC023002B, + MD_NTSTATUS_WIN_STATUS_NDIS_UNSUPPORTED_REVISION = 0xC023002C, + MD_NTSTATUS_WIN_STATUS_NDIS_INVALID_PORT = 0xC023002D, + MD_NTSTATUS_WIN_STATUS_NDIS_INVALID_PORT_STATE = 0xC023002E, + MD_NTSTATUS_WIN_STATUS_NDIS_LOW_POWER_STATE = 0xC023002F, + MD_NTSTATUS_WIN_STATUS_NDIS_REINIT_REQUIRED = 0xC0230030, + MD_NTSTATUS_WIN_STATUS_NDIS_NOT_SUPPORTED = 0xC02300BB, + MD_NTSTATUS_WIN_STATUS_NDIS_OFFLOAD_POLICY = 0xC023100F, + MD_NTSTATUS_WIN_STATUS_NDIS_OFFLOAD_CONNECTION_REJECTED = 0xC0231012, + MD_NTSTATUS_WIN_STATUS_NDIS_OFFLOAD_PATH_REJECTED = 0xC0231013, + MD_NTSTATUS_WIN_STATUS_NDIS_DOT11_AUTO_CONFIG_ENABLED = 0xC0232000, + MD_NTSTATUS_WIN_STATUS_NDIS_DOT11_MEDIA_IN_USE = 0xC0232001, + MD_NTSTATUS_WIN_STATUS_NDIS_DOT11_POWER_STATE_INVALID = 0xC0232002, + MD_NTSTATUS_WIN_STATUS_NDIS_PM_WOL_PATTERN_LIST_FULL = 0xC0232003, + MD_NTSTATUS_WIN_STATUS_NDIS_PM_PROTOCOL_OFFLOAD_LIST_FULL = 0xC0232004, + MD_NTSTATUS_WIN_STATUS_TPM_ERROR_MASK = 0xC0290000, + MD_NTSTATUS_WIN_STATUS_TPM_AUTHFAIL = 0xC0290001, + MD_NTSTATUS_WIN_STATUS_TPM_BADINDEX = 0xC0290002, + MD_NTSTATUS_WIN_STATUS_TPM_BAD_PARAMETER = 0xC0290003, + MD_NTSTATUS_WIN_STATUS_TPM_AUDITFAILURE = 0xC0290004, + MD_NTSTATUS_WIN_STATUS_TPM_CLEAR_DISABLED = 0xC0290005, + MD_NTSTATUS_WIN_STATUS_TPM_DEACTIVATED = 0xC0290006, + MD_NTSTATUS_WIN_STATUS_TPM_DISABLED = 0xC0290007, + MD_NTSTATUS_WIN_STATUS_TPM_DISABLED_CMD = 0xC0290008, + MD_NTSTATUS_WIN_STATUS_TPM_FAIL = 0xC0290009, + MD_NTSTATUS_WIN_STATUS_TPM_BAD_ORDINAL = 0xC029000A, + MD_NTSTATUS_WIN_STATUS_TPM_INSTALL_DISABLED = 0xC029000B, + MD_NTSTATUS_WIN_STATUS_TPM_INVALID_KEYHANDLE = 0xC029000C, + MD_NTSTATUS_WIN_STATUS_TPM_KEYNOTFOUND = 0xC029000D, + MD_NTSTATUS_WIN_STATUS_TPM_INAPPROPRIATE_ENC = 0xC029000E, + MD_NTSTATUS_WIN_STATUS_TPM_MIGRATEFAIL = 0xC029000F, + MD_NTSTATUS_WIN_STATUS_TPM_INVALID_PCR_INFO = 0xC0290010, + MD_NTSTATUS_WIN_STATUS_TPM_NOSPACE = 0xC0290011, + MD_NTSTATUS_WIN_STATUS_TPM_NOSRK = 0xC0290012, + MD_NTSTATUS_WIN_STATUS_TPM_NOTSEALED_BLOB = 0xC0290013, + MD_NTSTATUS_WIN_STATUS_TPM_OWNER_SET = 0xC0290014, + MD_NTSTATUS_WIN_STATUS_TPM_RESOURCES = 0xC0290015, + MD_NTSTATUS_WIN_STATUS_TPM_SHORTRANDOM = 0xC0290016, + MD_NTSTATUS_WIN_STATUS_TPM_SIZE = 0xC0290017, + MD_NTSTATUS_WIN_STATUS_TPM_WRONGPCRVAL = 0xC0290018, + MD_NTSTATUS_WIN_STATUS_TPM_BAD_PARAM_SIZE = 0xC0290019, + MD_NTSTATUS_WIN_STATUS_TPM_SHA_THREAD = 0xC029001A, + MD_NTSTATUS_WIN_STATUS_TPM_SHA_ERROR = 0xC029001B, + MD_NTSTATUS_WIN_STATUS_TPM_FAILEDSELFTEST = 0xC029001C, + MD_NTSTATUS_WIN_STATUS_TPM_AUTH2FAIL = 0xC029001D, + MD_NTSTATUS_WIN_STATUS_TPM_BADTAG = 0xC029001E, + MD_NTSTATUS_WIN_STATUS_TPM_IOERROR = 0xC029001F, + MD_NTSTATUS_WIN_STATUS_TPM_ENCRYPT_ERROR = 0xC0290020, + MD_NTSTATUS_WIN_STATUS_TPM_DECRYPT_ERROR = 0xC0290021, + MD_NTSTATUS_WIN_STATUS_TPM_INVALID_AUTHHANDLE = 0xC0290022, + MD_NTSTATUS_WIN_STATUS_TPM_NO_ENDORSEMENT = 0xC0290023, + MD_NTSTATUS_WIN_STATUS_TPM_INVALID_KEYUSAGE = 0xC0290024, + MD_NTSTATUS_WIN_STATUS_TPM_WRONG_ENTITYTYPE = 0xC0290025, + MD_NTSTATUS_WIN_STATUS_TPM_INVALID_POSTINIT = 0xC0290026, + MD_NTSTATUS_WIN_STATUS_TPM_INAPPROPRIATE_SIG = 0xC0290027, + MD_NTSTATUS_WIN_STATUS_TPM_BAD_KEY_PROPERTY = 0xC0290028, + MD_NTSTATUS_WIN_STATUS_TPM_BAD_MIGRATION = 0xC0290029, + MD_NTSTATUS_WIN_STATUS_TPM_BAD_SCHEME = 0xC029002A, + MD_NTSTATUS_WIN_STATUS_TPM_BAD_DATASIZE = 0xC029002B, + MD_NTSTATUS_WIN_STATUS_TPM_BAD_MODE = 0xC029002C, + MD_NTSTATUS_WIN_STATUS_TPM_BAD_PRESENCE = 0xC029002D, + MD_NTSTATUS_WIN_STATUS_TPM_BAD_VERSION = 0xC029002E, + MD_NTSTATUS_WIN_STATUS_TPM_NO_WRAP_TRANSPORT = 0xC029002F, + MD_NTSTATUS_WIN_STATUS_TPM_AUDITFAIL_UNSUCCESSFUL = 0xC0290030, + MD_NTSTATUS_WIN_STATUS_TPM_AUDITFAIL_SUCCESSFUL = 0xC0290031, + MD_NTSTATUS_WIN_STATUS_TPM_NOTRESETABLE = 0xC0290032, + MD_NTSTATUS_WIN_STATUS_TPM_NOTLOCAL = 0xC0290033, + MD_NTSTATUS_WIN_STATUS_TPM_BAD_TYPE = 0xC0290034, + MD_NTSTATUS_WIN_STATUS_TPM_INVALID_RESOURCE = 0xC0290035, + MD_NTSTATUS_WIN_STATUS_TPM_NOTFIPS = 0xC0290036, + MD_NTSTATUS_WIN_STATUS_TPM_INVALID_FAMILY = 0xC0290037, + MD_NTSTATUS_WIN_STATUS_TPM_NO_NV_PERMISSION = 0xC0290038, + MD_NTSTATUS_WIN_STATUS_TPM_REQUIRES_SIGN = 0xC0290039, + MD_NTSTATUS_WIN_STATUS_TPM_KEY_NOTSUPPORTED = 0xC029003A, + MD_NTSTATUS_WIN_STATUS_TPM_AUTH_CONFLICT = 0xC029003B, + MD_NTSTATUS_WIN_STATUS_TPM_AREA_LOCKED = 0xC029003C, + MD_NTSTATUS_WIN_STATUS_TPM_BAD_LOCALITY = 0xC029003D, + MD_NTSTATUS_WIN_STATUS_TPM_READ_ONLY = 0xC029003E, + MD_NTSTATUS_WIN_STATUS_TPM_PER_NOWRITE = 0xC029003F, + MD_NTSTATUS_WIN_STATUS_TPM_FAMILYCOUNT = 0xC0290040, + MD_NTSTATUS_WIN_STATUS_TPM_WRITE_LOCKED = 0xC0290041, + MD_NTSTATUS_WIN_STATUS_TPM_BAD_ATTRIBUTES = 0xC0290042, + MD_NTSTATUS_WIN_STATUS_TPM_INVALID_STRUCTURE = 0xC0290043, + MD_NTSTATUS_WIN_STATUS_TPM_KEY_OWNER_CONTROL = 0xC0290044, + MD_NTSTATUS_WIN_STATUS_TPM_BAD_COUNTER = 0xC0290045, + MD_NTSTATUS_WIN_STATUS_TPM_NOT_FULLWRITE = 0xC0290046, + MD_NTSTATUS_WIN_STATUS_TPM_CONTEXT_GAP = 0xC0290047, + MD_NTSTATUS_WIN_STATUS_TPM_MAXNVWRITES = 0xC0290048, + MD_NTSTATUS_WIN_STATUS_TPM_NOOPERATOR = 0xC0290049, + MD_NTSTATUS_WIN_STATUS_TPM_RESOURCEMISSING = 0xC029004A, + MD_NTSTATUS_WIN_STATUS_TPM_DELEGATE_LOCK = 0xC029004B, + MD_NTSTATUS_WIN_STATUS_TPM_DELEGATE_FAMILY = 0xC029004C, + MD_NTSTATUS_WIN_STATUS_TPM_DELEGATE_ADMIN = 0xC029004D, + MD_NTSTATUS_WIN_STATUS_TPM_TRANSPORT_NOTEXCLUSIVE = 0xC029004E, + MD_NTSTATUS_WIN_STATUS_TPM_OWNER_CONTROL = 0xC029004F, + MD_NTSTATUS_WIN_STATUS_TPM_DAA_RESOURCES = 0xC0290050, + MD_NTSTATUS_WIN_STATUS_TPM_DAA_INPUT_DATA0 = 0xC0290051, + MD_NTSTATUS_WIN_STATUS_TPM_DAA_INPUT_DATA1 = 0xC0290052, + MD_NTSTATUS_WIN_STATUS_TPM_DAA_ISSUER_SETTINGS = 0xC0290053, + MD_NTSTATUS_WIN_STATUS_TPM_DAA_TPM_SETTINGS = 0xC0290054, + MD_NTSTATUS_WIN_STATUS_TPM_DAA_STAGE = 0xC0290055, + MD_NTSTATUS_WIN_STATUS_TPM_DAA_ISSUER_VALIDITY = 0xC0290056, + MD_NTSTATUS_WIN_STATUS_TPM_DAA_WRONG_W = 0xC0290057, + MD_NTSTATUS_WIN_STATUS_TPM_BAD_HANDLE = 0xC0290058, + MD_NTSTATUS_WIN_STATUS_TPM_BAD_DELEGATE = 0xC0290059, + MD_NTSTATUS_WIN_STATUS_TPM_BADCONTEXT = 0xC029005A, + MD_NTSTATUS_WIN_STATUS_TPM_TOOMANYCONTEXTS = 0xC029005B, + MD_NTSTATUS_WIN_STATUS_TPM_MA_TICKET_SIGNATURE = 0xC029005C, + MD_NTSTATUS_WIN_STATUS_TPM_MA_DESTINATION = 0xC029005D, + MD_NTSTATUS_WIN_STATUS_TPM_MA_SOURCE = 0xC029005E, + MD_NTSTATUS_WIN_STATUS_TPM_MA_AUTHORITY = 0xC029005F, + MD_NTSTATUS_WIN_STATUS_TPM_PERMANENTEK = 0xC0290061, + MD_NTSTATUS_WIN_STATUS_TPM_BAD_SIGNATURE = 0xC0290062, + MD_NTSTATUS_WIN_STATUS_TPM_NOCONTEXTSPACE = 0xC0290063, + MD_NTSTATUS_WIN_STATUS_TPM_COMMAND_BLOCKED = 0xC0290400, + MD_NTSTATUS_WIN_STATUS_TPM_INVALID_HANDLE = 0xC0290401, + MD_NTSTATUS_WIN_STATUS_TPM_DUPLICATE_VHANDLE = 0xC0290402, + MD_NTSTATUS_WIN_STATUS_TPM_EMBEDDED_COMMAND_BLOCKED = 0xC0290403, + MD_NTSTATUS_WIN_STATUS_TPM_EMBEDDED_COMMAND_UNSUPPORTED = 0xC0290404, + MD_NTSTATUS_WIN_STATUS_TPM_RETRY = 0xC0290800, + MD_NTSTATUS_WIN_STATUS_TPM_NEEDS_SELFTEST = 0xC0290801, + MD_NTSTATUS_WIN_STATUS_TPM_DOING_SELFTEST = 0xC0290802, + MD_NTSTATUS_WIN_STATUS_TPM_DEFEND_LOCK_RUNNING = 0xC0290803, + MD_NTSTATUS_WIN_STATUS_TPM_COMMAND_CANCELED = 0xC0291001, + MD_NTSTATUS_WIN_STATUS_TPM_TOO_MANY_CONTEXTS = 0xC0291002, + MD_NTSTATUS_WIN_STATUS_TPM_NOT_FOUND = 0xC0291003, + MD_NTSTATUS_WIN_STATUS_TPM_ACCESS_DENIED = 0xC0291004, + MD_NTSTATUS_WIN_STATUS_TPM_INSUFFICIENT_BUFFER = 0xC0291005, + MD_NTSTATUS_WIN_STATUS_TPM_PPI_FUNCTION_UNSUPPORTED = 0xC0291006, + MD_NTSTATUS_WIN_STATUS_PCP_ERROR_MASK = 0xC0292000, + MD_NTSTATUS_WIN_STATUS_PCP_DEVICE_NOT_READY = 0xC0292001, + MD_NTSTATUS_WIN_STATUS_PCP_INVALID_HANDLE = 0xC0292002, + MD_NTSTATUS_WIN_STATUS_PCP_INVALID_PARAMETER = 0xC0292003, + MD_NTSTATUS_WIN_STATUS_PCP_FLAG_NOT_SUPPORTED = 0xC0292004, + MD_NTSTATUS_WIN_STATUS_PCP_NOT_SUPPORTED = 0xC0292005, + MD_NTSTATUS_WIN_STATUS_PCP_BUFFER_TOO_SMALL = 0xC0292006, + MD_NTSTATUS_WIN_STATUS_PCP_INTERNAL_ERROR = 0xC0292007, + MD_NTSTATUS_WIN_STATUS_PCP_AUTHENTICATION_FAILED = 0xC0292008, + MD_NTSTATUS_WIN_STATUS_PCP_AUTHENTICATION_IGNORED = 0xC0292009, + MD_NTSTATUS_WIN_STATUS_PCP_POLICY_NOT_FOUND = 0xC029200A, + MD_NTSTATUS_WIN_STATUS_PCP_PROFILE_NOT_FOUND = 0xC029200B, + MD_NTSTATUS_WIN_STATUS_PCP_VALIDATION_FAILED = 0xC029200C, + MD_NTSTATUS_WIN_STATUS_PCP_DEVICE_NOT_FOUND = 0xC029200D, + MD_NTSTATUS_WIN_STATUS_HV_INVALID_HYPERCALL_CODE = 0xC0350002, + MD_NTSTATUS_WIN_STATUS_HV_INVALID_HYPERCALL_INPUT = 0xC0350003, + MD_NTSTATUS_WIN_STATUS_HV_INVALID_ALIGNMENT = 0xC0350004, + MD_NTSTATUS_WIN_STATUS_HV_INVALID_PARAMETER = 0xC0350005, + MD_NTSTATUS_WIN_STATUS_HV_ACCESS_DENIED = 0xC0350006, + MD_NTSTATUS_WIN_STATUS_HV_INVALID_PARTITION_STATE = 0xC0350007, + MD_NTSTATUS_WIN_STATUS_HV_OPERATION_DENIED = 0xC0350008, + MD_NTSTATUS_WIN_STATUS_HV_UNKNOWN_PROPERTY = 0xC0350009, + MD_NTSTATUS_WIN_STATUS_HV_PROPERTY_VALUE_OUT_OF_RANGE = 0xC035000A, + MD_NTSTATUS_WIN_STATUS_HV_INSUFFICIENT_MEMORY = 0xC035000B, + MD_NTSTATUS_WIN_STATUS_HV_PARTITION_TOO_DEEP = 0xC035000C, + MD_NTSTATUS_WIN_STATUS_HV_INVALID_PARTITION_ID = 0xC035000D, + MD_NTSTATUS_WIN_STATUS_HV_INVALID_VP_INDEX = 0xC035000E, + MD_NTSTATUS_WIN_STATUS_HV_INVALID_PORT_ID = 0xC0350011, + MD_NTSTATUS_WIN_STATUS_HV_INVALID_CONNECTION_ID = 0xC0350012, + MD_NTSTATUS_WIN_STATUS_HV_INSUFFICIENT_BUFFERS = 0xC0350013, + MD_NTSTATUS_WIN_STATUS_HV_NOT_ACKNOWLEDGED = 0xC0350014, + MD_NTSTATUS_WIN_STATUS_HV_ACKNOWLEDGED = 0xC0350016, + MD_NTSTATUS_WIN_STATUS_HV_INVALID_SAVE_RESTORE_STATE = 0xC0350017, + MD_NTSTATUS_WIN_STATUS_HV_INVALID_SYNIC_STATE = 0xC0350018, + MD_NTSTATUS_WIN_STATUS_HV_OBJECT_IN_USE = 0xC0350019, + MD_NTSTATUS_WIN_STATUS_HV_INVALID_PROXIMITY_DOMAIN_INFO = 0xC035001A, + MD_NTSTATUS_WIN_STATUS_HV_NO_DATA = 0xC035001B, + MD_NTSTATUS_WIN_STATUS_HV_INACTIVE = 0xC035001C, + MD_NTSTATUS_WIN_STATUS_HV_NO_RESOURCES = 0xC035001D, + MD_NTSTATUS_WIN_STATUS_HV_FEATURE_UNAVAILABLE = 0xC035001E, + MD_NTSTATUS_WIN_STATUS_HV_INSUFFICIENT_BUFFER = 0xC0350033, + MD_NTSTATUS_WIN_STATUS_HV_INSUFFICIENT_DEVICE_DOMAINS = 0xC0350038, + MD_NTSTATUS_WIN_STATUS_HV_INVALID_LP_INDEX = 0xC0350041, + MD_NTSTATUS_WIN_STATUS_HV_NOT_PRESENT = 0xC0351000, + MD_NTSTATUS_WIN_STATUS_IPSEC_BAD_SPI = 0xC0360001, + MD_NTSTATUS_WIN_STATUS_IPSEC_SA_LIFETIME_EXPIRED = 0xC0360002, + MD_NTSTATUS_WIN_STATUS_IPSEC_WRONG_SA = 0xC0360003, + MD_NTSTATUS_WIN_STATUS_IPSEC_REPLAY_CHECK_FAILED = 0xC0360004, + MD_NTSTATUS_WIN_STATUS_IPSEC_INVALID_PACKET = 0xC0360005, + MD_NTSTATUS_WIN_STATUS_IPSEC_INTEGRITY_CHECK_FAILED = 0xC0360006, + MD_NTSTATUS_WIN_STATUS_IPSEC_CLEAR_TEXT_DROP = 0xC0360007, + MD_NTSTATUS_WIN_STATUS_IPSEC_AUTH_FIREWALL_DROP = 0xC0360008, + MD_NTSTATUS_WIN_STATUS_IPSEC_THROTTLE_DROP = 0xC0360009, + MD_NTSTATUS_WIN_STATUS_IPSEC_DOSP_BLOCK = 0xC0368000, + MD_NTSTATUS_WIN_STATUS_IPSEC_DOSP_RECEIVED_MULTICAST = 0xC0368001, + MD_NTSTATUS_WIN_STATUS_IPSEC_DOSP_INVALID_PACKET = 0xC0368002, + MD_NTSTATUS_WIN_STATUS_IPSEC_DOSP_STATE_LOOKUP_FAILED = 0xC0368003, + MD_NTSTATUS_WIN_STATUS_IPSEC_DOSP_MAX_ENTRIES = 0xC0368004, + MD_NTSTATUS_WIN_STATUS_IPSEC_DOSP_KEYMOD_NOT_ALLOWED = 0xC0368005, + MD_NTSTATUS_WIN_STATUS_IPSEC_DOSP_MAX_PER_IP_RATELIMIT_QUEUES = 0xC0368006, + MD_NTSTATUS_WIN_STATUS_VID_DUPLICATE_HANDLER = 0xC0370001, + MD_NTSTATUS_WIN_STATUS_VID_TOO_MANY_HANDLERS = 0xC0370002, + MD_NTSTATUS_WIN_STATUS_VID_QUEUE_FULL = 0xC0370003, + MD_NTSTATUS_WIN_STATUS_VID_HANDLER_NOT_PRESENT = 0xC0370004, + MD_NTSTATUS_WIN_STATUS_VID_INVALID_OBJECT_NAME = 0xC0370005, + MD_NTSTATUS_WIN_STATUS_VID_PARTITION_NAME_TOO_LONG = 0xC0370006, + MD_NTSTATUS_WIN_STATUS_VID_MESSAGE_QUEUE_NAME_TOO_LONG = 0xC0370007, + MD_NTSTATUS_WIN_STATUS_VID_PARTITION_ALREADY_EXISTS = 0xC0370008, + MD_NTSTATUS_WIN_STATUS_VID_PARTITION_DOES_NOT_EXIST = 0xC0370009, + MD_NTSTATUS_WIN_STATUS_VID_PARTITION_NAME_NOT_FOUND = 0xC037000A, + MD_NTSTATUS_WIN_STATUS_VID_MESSAGE_QUEUE_ALREADY_EXISTS = 0xC037000B, + MD_NTSTATUS_WIN_STATUS_VID_EXCEEDED_MBP_ENTRY_MAP_LIMIT = 0xC037000C, + MD_NTSTATUS_WIN_STATUS_VID_MB_STILL_REFERENCED = 0xC037000D, + MD_NTSTATUS_WIN_STATUS_VID_CHILD_GPA_PAGE_SET_CORRUPTED = 0xC037000E, + MD_NTSTATUS_WIN_STATUS_VID_INVALID_NUMA_SETTINGS = 0xC037000F, + MD_NTSTATUS_WIN_STATUS_VID_INVALID_NUMA_NODE_INDEX = 0xC0370010, + MD_NTSTATUS_WIN_STATUS_VID_NOTIFICATION_QUEUE_ALREADY_ASSOCIATED = 0xC0370011, + MD_NTSTATUS_WIN_STATUS_VID_INVALID_MEMORY_BLOCK_HANDLE = 0xC0370012, + MD_NTSTATUS_WIN_STATUS_VID_PAGE_RANGE_OVERFLOW = 0xC0370013, + MD_NTSTATUS_WIN_STATUS_VID_INVALID_MESSAGE_QUEUE_HANDLE = 0xC0370014, + MD_NTSTATUS_WIN_STATUS_VID_INVALID_GPA_RANGE_HANDLE = 0xC0370015, + MD_NTSTATUS_WIN_STATUS_VID_NO_MEMORY_BLOCK_NOTIFICATION_QUEUE = 0xC0370016, + MD_NTSTATUS_WIN_STATUS_VID_MEMORY_BLOCK_LOCK_COUNT_EXCEEDED = 0xC0370017, + MD_NTSTATUS_WIN_STATUS_VID_INVALID_PPM_HANDLE = 0xC0370018, + MD_NTSTATUS_WIN_STATUS_VID_MBPS_ARE_LOCKED = 0xC0370019, + MD_NTSTATUS_WIN_STATUS_VID_MESSAGE_QUEUE_CLOSED = 0xC037001A, + MD_NTSTATUS_WIN_STATUS_VID_VIRTUAL_PROCESSOR_LIMIT_EXCEEDED = 0xC037001B, + MD_NTSTATUS_WIN_STATUS_VID_STOP_PENDING = 0xC037001C, + MD_NTSTATUS_WIN_STATUS_VID_INVALID_PROCESSOR_STATE = 0xC037001D, + MD_NTSTATUS_WIN_STATUS_VID_EXCEEDED_KM_CONTEXT_COUNT_LIMIT = 0xC037001E, + MD_NTSTATUS_WIN_STATUS_VID_KM_INTERFACE_ALREADY_INITIALIZED = 0xC037001F, + MD_NTSTATUS_WIN_STATUS_VID_MB_PROPERTY_ALREADY_SET_RESET = 0xC0370020, + MD_NTSTATUS_WIN_STATUS_VID_MMIO_RANGE_DESTROYED = 0xC0370021, + MD_NTSTATUS_WIN_STATUS_VID_INVALID_CHILD_GPA_PAGE_SET = 0xC0370022, + MD_NTSTATUS_WIN_STATUS_VID_RESERVE_PAGE_SET_IS_BEING_USED = 0xC0370023, + MD_NTSTATUS_WIN_STATUS_VID_RESERVE_PAGE_SET_TOO_SMALL = 0xC0370024, + MD_NTSTATUS_WIN_STATUS_VID_MBP_ALREADY_LOCKED_USING_RESERVED_PAGE = 0xC0370025, + MD_NTSTATUS_WIN_STATUS_VID_MBP_COUNT_EXCEEDED_LIMIT = 0xC0370026, + MD_NTSTATUS_WIN_STATUS_VID_SAVED_STATE_CORRUPT = 0xC0370027, + MD_NTSTATUS_WIN_STATUS_VID_SAVED_STATE_UNRECOGNIZED_ITEM = 0xC0370028, + MD_NTSTATUS_WIN_STATUS_VID_SAVED_STATE_INCOMPATIBLE = 0xC0370029, + MD_NTSTATUS_WIN_STATUS_VOLMGR_DATABASE_FULL = 0xC0380001, + MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_CONFIGURATION_CORRUPTED = 0xC0380002, + MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_CONFIGURATION_NOT_IN_SYNC = 0xC0380003, + MD_NTSTATUS_WIN_STATUS_VOLMGR_PACK_CONFIG_UPDATE_FAILED = 0xC0380004, + MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_CONTAINS_NON_SIMPLE_VOLUME = 0xC0380005, + MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_DUPLICATE = 0xC0380006, + MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_DYNAMIC = 0xC0380007, + MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_ID_INVALID = 0xC0380008, + MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_INVALID = 0xC0380009, + MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_LAST_VOTER = 0xC038000A, + MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_LAYOUT_INVALID = 0xC038000B, + MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_LAYOUT_NON_BASIC_BETWEEN_BASIC_PARTITIONS = 0xC038000C, + MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_LAYOUT_NOT_CYLINDER_ALIGNED = 0xC038000D, + MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_LAYOUT_PARTITIONS_TOO_SMALL = 0xC038000E, + MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_LAYOUT_PRIMARY_BETWEEN_LOGICAL_PARTITIONS = 0xC038000F, + MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_LAYOUT_TOO_MANY_PARTITIONS = 0xC0380010, + MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_MISSING = 0xC0380011, + MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_NOT_EMPTY = 0xC0380012, + MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_NOT_ENOUGH_SPACE = 0xC0380013, + MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_REVECTORING_FAILED = 0xC0380014, + MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_SECTOR_SIZE_INVALID = 0xC0380015, + MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_SET_NOT_CONTAINED = 0xC0380016, + MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_USED_BY_MULTIPLE_MEMBERS = 0xC0380017, + MD_NTSTATUS_WIN_STATUS_VOLMGR_DISK_USED_BY_MULTIPLE_PLEXES = 0xC0380018, + MD_NTSTATUS_WIN_STATUS_VOLMGR_DYNAMIC_DISK_NOT_SUPPORTED = 0xC0380019, + MD_NTSTATUS_WIN_STATUS_VOLMGR_EXTENT_ALREADY_USED = 0xC038001A, + MD_NTSTATUS_WIN_STATUS_VOLMGR_EXTENT_NOT_CONTIGUOUS = 0xC038001B, + MD_NTSTATUS_WIN_STATUS_VOLMGR_EXTENT_NOT_IN_PUBLIC_REGION = 0xC038001C, + MD_NTSTATUS_WIN_STATUS_VOLMGR_EXTENT_NOT_SECTOR_ALIGNED = 0xC038001D, + MD_NTSTATUS_WIN_STATUS_VOLMGR_EXTENT_OVERLAPS_EBR_PARTITION = 0xC038001E, + MD_NTSTATUS_WIN_STATUS_VOLMGR_EXTENT_VOLUME_LENGTHS_DO_NOT_MATCH = 0xC038001F, + MD_NTSTATUS_WIN_STATUS_VOLMGR_FAULT_TOLERANT_NOT_SUPPORTED = 0xC0380020, + MD_NTSTATUS_WIN_STATUS_VOLMGR_INTERLEAVE_LENGTH_INVALID = 0xC0380021, + MD_NTSTATUS_WIN_STATUS_VOLMGR_MAXIMUM_REGISTERED_USERS = 0xC0380022, + MD_NTSTATUS_WIN_STATUS_VOLMGR_MEMBER_IN_SYNC = 0xC0380023, + MD_NTSTATUS_WIN_STATUS_VOLMGR_MEMBER_INDEX_DUPLICATE = 0xC0380024, + MD_NTSTATUS_WIN_STATUS_VOLMGR_MEMBER_INDEX_INVALID = 0xC0380025, + MD_NTSTATUS_WIN_STATUS_VOLMGR_MEMBER_MISSING = 0xC0380026, + MD_NTSTATUS_WIN_STATUS_VOLMGR_MEMBER_NOT_DETACHED = 0xC0380027, + MD_NTSTATUS_WIN_STATUS_VOLMGR_MEMBER_REGENERATING = 0xC0380028, + MD_NTSTATUS_WIN_STATUS_VOLMGR_ALL_DISKS_FAILED = 0xC0380029, + MD_NTSTATUS_WIN_STATUS_VOLMGR_NO_REGISTERED_USERS = 0xC038002A, + MD_NTSTATUS_WIN_STATUS_VOLMGR_NO_SUCH_USER = 0xC038002B, + MD_NTSTATUS_WIN_STATUS_VOLMGR_NOTIFICATION_RESET = 0xC038002C, + MD_NTSTATUS_WIN_STATUS_VOLMGR_NUMBER_OF_MEMBERS_INVALID = 0xC038002D, + MD_NTSTATUS_WIN_STATUS_VOLMGR_NUMBER_OF_PLEXES_INVALID = 0xC038002E, + MD_NTSTATUS_WIN_STATUS_VOLMGR_PACK_DUPLICATE = 0xC038002F, + MD_NTSTATUS_WIN_STATUS_VOLMGR_PACK_ID_INVALID = 0xC0380030, + MD_NTSTATUS_WIN_STATUS_VOLMGR_PACK_INVALID = 0xC0380031, + MD_NTSTATUS_WIN_STATUS_VOLMGR_PACK_NAME_INVALID = 0xC0380032, + MD_NTSTATUS_WIN_STATUS_VOLMGR_PACK_OFFLINE = 0xC0380033, + MD_NTSTATUS_WIN_STATUS_VOLMGR_PACK_HAS_QUORUM = 0xC0380034, + MD_NTSTATUS_WIN_STATUS_VOLMGR_PACK_WITHOUT_QUORUM = 0xC0380035, + MD_NTSTATUS_WIN_STATUS_VOLMGR_PARTITION_STYLE_INVALID = 0xC0380036, + MD_NTSTATUS_WIN_STATUS_VOLMGR_PARTITION_UPDATE_FAILED = 0xC0380037, + MD_NTSTATUS_WIN_STATUS_VOLMGR_PLEX_IN_SYNC = 0xC0380038, + MD_NTSTATUS_WIN_STATUS_VOLMGR_PLEX_INDEX_DUPLICATE = 0xC0380039, + MD_NTSTATUS_WIN_STATUS_VOLMGR_PLEX_INDEX_INVALID = 0xC038003A, + MD_NTSTATUS_WIN_STATUS_VOLMGR_PLEX_LAST_ACTIVE = 0xC038003B, + MD_NTSTATUS_WIN_STATUS_VOLMGR_PLEX_MISSING = 0xC038003C, + MD_NTSTATUS_WIN_STATUS_VOLMGR_PLEX_REGENERATING = 0xC038003D, + MD_NTSTATUS_WIN_STATUS_VOLMGR_PLEX_TYPE_INVALID = 0xC038003E, + MD_NTSTATUS_WIN_STATUS_VOLMGR_PLEX_NOT_RAID5 = 0xC038003F, + MD_NTSTATUS_WIN_STATUS_VOLMGR_PLEX_NOT_SIMPLE = 0xC0380040, + MD_NTSTATUS_WIN_STATUS_VOLMGR_STRUCTURE_SIZE_INVALID = 0xC0380041, + MD_NTSTATUS_WIN_STATUS_VOLMGR_TOO_MANY_NOTIFICATION_REQUESTS = 0xC0380042, + MD_NTSTATUS_WIN_STATUS_VOLMGR_TRANSACTION_IN_PROGRESS = 0xC0380043, + MD_NTSTATUS_WIN_STATUS_VOLMGR_UNEXPECTED_DISK_LAYOUT_CHANGE = 0xC0380044, + MD_NTSTATUS_WIN_STATUS_VOLMGR_VOLUME_CONTAINS_MISSING_DISK = 0xC0380045, + MD_NTSTATUS_WIN_STATUS_VOLMGR_VOLUME_ID_INVALID = 0xC0380046, + MD_NTSTATUS_WIN_STATUS_VOLMGR_VOLUME_LENGTH_INVALID = 0xC0380047, + MD_NTSTATUS_WIN_STATUS_VOLMGR_VOLUME_LENGTH_NOT_SECTOR_SIZE_MULTIPLE = 0xC0380048, + MD_NTSTATUS_WIN_STATUS_VOLMGR_VOLUME_NOT_MIRRORED = 0xC0380049, + MD_NTSTATUS_WIN_STATUS_VOLMGR_VOLUME_NOT_RETAINED = 0xC038004A, + MD_NTSTATUS_WIN_STATUS_VOLMGR_VOLUME_OFFLINE = 0xC038004B, + MD_NTSTATUS_WIN_STATUS_VOLMGR_VOLUME_RETAINED = 0xC038004C, + MD_NTSTATUS_WIN_STATUS_VOLMGR_NUMBER_OF_EXTENTS_INVALID = 0xC038004D, + MD_NTSTATUS_WIN_STATUS_VOLMGR_DIFFERENT_SECTOR_SIZE = 0xC038004E, + MD_NTSTATUS_WIN_STATUS_VOLMGR_BAD_BOOT_DISK = 0xC038004F, + MD_NTSTATUS_WIN_STATUS_VOLMGR_PACK_CONFIG_OFFLINE = 0xC0380050, + MD_NTSTATUS_WIN_STATUS_VOLMGR_PACK_CONFIG_ONLINE = 0xC0380051, + MD_NTSTATUS_WIN_STATUS_VOLMGR_NOT_PRIMARY_PACK = 0xC0380052, + MD_NTSTATUS_WIN_STATUS_VOLMGR_PACK_LOG_UPDATE_FAILED = 0xC0380053, + MD_NTSTATUS_WIN_STATUS_VOLMGR_NUMBER_OF_DISKS_IN_PLEX_INVALID = 0xC0380054, + MD_NTSTATUS_WIN_STATUS_VOLMGR_NUMBER_OF_DISKS_IN_MEMBER_INVALID = 0xC0380055, + MD_NTSTATUS_WIN_STATUS_VOLMGR_VOLUME_MIRRORED = 0xC0380056, + MD_NTSTATUS_WIN_STATUS_VOLMGR_PLEX_NOT_SIMPLE_SPANNED = 0xC0380057, + MD_NTSTATUS_WIN_STATUS_VOLMGR_NO_VALID_LOG_COPIES = 0xC0380058, + MD_NTSTATUS_WIN_STATUS_VOLMGR_PRIMARY_PACK_PRESENT = 0xC0380059, + MD_NTSTATUS_WIN_STATUS_VOLMGR_NUMBER_OF_DISKS_INVALID = 0xC038005A, + MD_NTSTATUS_WIN_STATUS_VOLMGR_MIRROR_NOT_SUPPORTED = 0xC038005B, + MD_NTSTATUS_WIN_STATUS_VOLMGR_RAID5_NOT_SUPPORTED = 0xC038005C, + MD_NTSTATUS_WIN_STATUS_BCD_TOO_MANY_ELEMENTS = 0xC0390002, + MD_NTSTATUS_WIN_STATUS_VHD_DRIVE_FOOTER_MISSING = 0xC03A0001, + MD_NTSTATUS_WIN_STATUS_VHD_DRIVE_FOOTER_CHECKSUM_MISMATCH = 0xC03A0002, + MD_NTSTATUS_WIN_STATUS_VHD_DRIVE_FOOTER_CORRUPT = 0xC03A0003, + MD_NTSTATUS_WIN_STATUS_VHD_FORMAT_UNKNOWN = 0xC03A0004, + MD_NTSTATUS_WIN_STATUS_VHD_FORMAT_UNSUPPORTED_VERSION = 0xC03A0005, + MD_NTSTATUS_WIN_STATUS_VHD_SPARSE_HEADER_CHECKSUM_MISMATCH = 0xC03A0006, + MD_NTSTATUS_WIN_STATUS_VHD_SPARSE_HEADER_UNSUPPORTED_VERSION = 0xC03A0007, + MD_NTSTATUS_WIN_STATUS_VHD_SPARSE_HEADER_CORRUPT = 0xC03A0008, + MD_NTSTATUS_WIN_STATUS_VHD_BLOCK_ALLOCATION_FAILURE = 0xC03A0009, + MD_NTSTATUS_WIN_STATUS_VHD_BLOCK_ALLOCATION_TABLE_CORRUPT = 0xC03A000A, + MD_NTSTATUS_WIN_STATUS_VHD_INVALID_BLOCK_SIZE = 0xC03A000B, + MD_NTSTATUS_WIN_STATUS_VHD_BITMAP_MISMATCH = 0xC03A000C, + MD_NTSTATUS_WIN_STATUS_VHD_PARENT_VHD_NOT_FOUND = 0xC03A000D, + MD_NTSTATUS_WIN_STATUS_VHD_CHILD_PARENT_ID_MISMATCH = 0xC03A000E, + MD_NTSTATUS_WIN_STATUS_VHD_CHILD_PARENT_TIMESTAMP_MISMATCH = 0xC03A000F, + MD_NTSTATUS_WIN_STATUS_VHD_METADATA_READ_FAILURE = 0xC03A0010, + MD_NTSTATUS_WIN_STATUS_VHD_METADATA_WRITE_FAILURE = 0xC03A0011, + MD_NTSTATUS_WIN_STATUS_VHD_INVALID_SIZE = 0xC03A0012, + MD_NTSTATUS_WIN_STATUS_VHD_INVALID_FILE_SIZE = 0xC03A0013, + MD_NTSTATUS_WIN_STATUS_VIRTDISK_PROVIDER_NOT_FOUND = 0xC03A0014, + MD_NTSTATUS_WIN_STATUS_VIRTDISK_NOT_VIRTUAL_DISK = 0xC03A0015, + MD_NTSTATUS_WIN_STATUS_VHD_PARENT_VHD_ACCESS_DENIED = 0xC03A0016, + MD_NTSTATUS_WIN_STATUS_VHD_CHILD_PARENT_SIZE_MISMATCH = 0xC03A0017, + MD_NTSTATUS_WIN_STATUS_VHD_DIFFERENCING_CHAIN_CYCLE_DETECTED = 0xC03A0018, + MD_NTSTATUS_WIN_STATUS_VHD_DIFFERENCING_CHAIN_ERROR_IN_PARENT = 0xC03A0019, + MD_NTSTATUS_WIN_STATUS_VIRTUAL_DISK_LIMITATION = 0xC03A001A, + MD_NTSTATUS_WIN_STATUS_VHD_INVALID_TYPE = 0xC03A001B, + MD_NTSTATUS_WIN_STATUS_VHD_INVALID_STATE = 0xC03A001C, + MD_NTSTATUS_WIN_STATUS_VIRTDISK_UNSUPPORTED_DISK_SECTOR_SIZE = 0xC03A001D, + MD_NTSTATUS_WIN_STATUS_VIRTDISK_DISK_ALREADY_OWNED = 0xC03A001E, + MD_NTSTATUS_WIN_STATUS_VIRTDISK_DISK_ONLINE_AND_WRITABLE = 0xC03A001F, + MD_NTSTATUS_WIN_STATUS_CTLOG_TRACKING_NOT_INITIALIZED = 0xC03A0020, + MD_NTSTATUS_WIN_STATUS_CTLOG_LOGFILE_SIZE_EXCEEDED_MAXSIZE = 0xC03A0021, + MD_NTSTATUS_WIN_STATUS_CTLOG_VHD_CHANGED_OFFLINE = 0xC03A0022, + MD_NTSTATUS_WIN_STATUS_CTLOG_INVALID_TRACKING_STATE = 0xC03A0023, + MD_NTSTATUS_WIN_STATUS_CTLOG_INCONSISTENT_TRACKING_FILE = 0xC03A0024, + MD_NTSTATUS_WIN_STATUS_VHD_METADATA_FULL = 0xC03A0028, + MD_NTSTATUS_WIN_STATUS_RKF_KEY_NOT_FOUND = 0xC0400001, + MD_NTSTATUS_WIN_STATUS_RKF_DUPLICATE_KEY = 0xC0400002, + MD_NTSTATUS_WIN_STATUS_RKF_BLOB_FULL = 0xC0400003, + MD_NTSTATUS_WIN_STATUS_RKF_STORE_FULL = 0xC0400004, + MD_NTSTATUS_WIN_STATUS_RKF_FILE_BLOCKED = 0xC0400005, + MD_NTSTATUS_WIN_STATUS_RKF_ACTIVE_KEY = 0xC0400006, + MD_NTSTATUS_WIN_STATUS_RDBSS_RESTART_OPERATION = 0xC0410001, + MD_NTSTATUS_WIN_STATUS_RDBSS_CONTINUE_OPERATION = 0xC0410002, + MD_NTSTATUS_WIN_STATUS_RDBSS_POST_OPERATION = 0xC0410003, + MD_NTSTATUS_WIN_STATUS_BTH_ATT_INVALID_HANDLE = 0xC0420001, + MD_NTSTATUS_WIN_STATUS_BTH_ATT_READ_NOT_PERMITTED = 0xC0420002, + MD_NTSTATUS_WIN_STATUS_BTH_ATT_WRITE_NOT_PERMITTED = 0xC0420003, + MD_NTSTATUS_WIN_STATUS_BTH_ATT_INVALID_PDU = 0xC0420004, + MD_NTSTATUS_WIN_STATUS_BTH_ATT_INSUFFICIENT_AUTHENTICATION = 0xC0420005, + MD_NTSTATUS_WIN_STATUS_BTH_ATT_REQUEST_NOT_SUPPORTED = 0xC0420006, + MD_NTSTATUS_WIN_STATUS_BTH_ATT_INVALID_OFFSET = 0xC0420007, + MD_NTSTATUS_WIN_STATUS_BTH_ATT_INSUFFICIENT_AUTHORIZATION = 0xC0420008, + MD_NTSTATUS_WIN_STATUS_BTH_ATT_PREPARE_QUEUE_FULL = 0xC0420009, + MD_NTSTATUS_WIN_STATUS_BTH_ATT_ATTRIBUTE_NOT_FOUND = 0xC042000A, + MD_NTSTATUS_WIN_STATUS_BTH_ATT_ATTRIBUTE_NOT_LONG = 0xC042000B, + MD_NTSTATUS_WIN_STATUS_BTH_ATT_INSUFFICIENT_ENCRYPTION_KEY_SIZE = 0xC042000C, + MD_NTSTATUS_WIN_STATUS_BTH_ATT_INVALID_ATTRIBUTE_VALUE_LENGTH = 0xC042000D, + MD_NTSTATUS_WIN_STATUS_BTH_ATT_UNLIKELY = 0xC042000E, + MD_NTSTATUS_WIN_STATUS_BTH_ATT_INSUFFICIENT_ENCRYPTION = 0xC042000F, + MD_NTSTATUS_WIN_STATUS_BTH_ATT_UNSUPPORTED_GROUP_TYPE = 0xC0420010, + MD_NTSTATUS_WIN_STATUS_BTH_ATT_INSUFFICIENT_RESOURCES = 0xC0420011, + MD_NTSTATUS_WIN_STATUS_BTH_ATT_UNKNOWN_ERROR = 0xC0421000, + MD_NTSTATUS_WIN_STATUS_SECUREBOOT_ROLLBACK_DETECTED = 0xC0430001, + MD_NTSTATUS_WIN_STATUS_SECUREBOOT_POLICY_VIOLATION = 0xC0430002, + MD_NTSTATUS_WIN_STATUS_SECUREBOOT_INVALID_POLICY = 0xC0430003, + MD_NTSTATUS_WIN_STATUS_SECUREBOOT_POLICY_PUBLISHER_NOT_FOUND = 0xC0430004, + MD_NTSTATUS_WIN_STATUS_SECUREBOOT_POLICY_NOT_SIGNED = 0xC0430005, + MD_NTSTATUS_WIN_STATUS_SECUREBOOT_FILE_REPLACED = 0xC0430007, + MD_NTSTATUS_WIN_STATUS_AUDIO_ENGINE_NODE_NOT_FOUND = 0xC0440001, + MD_NTSTATUS_WIN_STATUS_HDAUDIO_EMPTY_CONNECTION_LIST = 0xC0440002, + MD_NTSTATUS_WIN_STATUS_HDAUDIO_CONNECTION_LIST_NOT_SUPPORTED = 0xC0440003, + MD_NTSTATUS_WIN_STATUS_HDAUDIO_NO_LOGICAL_DEVICES_CREATED = 0xC0440004, + MD_NTSTATUS_WIN_STATUS_HDAUDIO_NULL_LINKED_LIST_ENTRY = 0xC0440005, + MD_NTSTATUS_WIN_STATUS_VOLSNAP_BOOTFILE_NOT_VALID = 0xC0500003, + MD_NTSTATUS_WIN_STATUS_IO_PREEMPTED = 0xC0510001, + MD_NTSTATUS_WIN_STATUS_SVHDX_ERROR_STORED = 0xC05C0000, + MD_NTSTATUS_WIN_STATUS_SVHDX_ERROR_NOT_AVAILABLE = 0xC05CFF00, + MD_NTSTATUS_WIN_STATUS_SVHDX_UNIT_ATTENTION_AVAILABLE = 0xC05CFF01, + MD_NTSTATUS_WIN_STATUS_SVHDX_UNIT_ATTENTION_CAPACITY_DATA_CHANGED = 0xC05CFF02, + MD_NTSTATUS_WIN_STATUS_SVHDX_UNIT_ATTENTION_RESERVATIONS_PREEMPTED = 0xC05CFF03, + MD_NTSTATUS_WIN_STATUS_SVHDX_UNIT_ATTENTION_RESERVATIONS_RELEASED = 0xC05CFF04, + MD_NTSTATUS_WIN_STATUS_SVHDX_UNIT_ATTENTION_REGISTRATIONS_PREEMPTED = 0xC05CFF05, + MD_NTSTATUS_WIN_STATUS_SVHDX_UNIT_ATTENTION_OPERATING_DEFINITION_CHANGED = 0xC05CFF06, + MD_NTSTATUS_WIN_STATUS_SVHDX_RESERVATION_CONFLICT = 0xC05CFF07, + MD_NTSTATUS_WIN_STATUS_SVHDX_WRONG_FILE_TYPE = 0xC05CFF08, + MD_NTSTATUS_WIN_STATUS_SVHDX_VERSION_MISMATCH = 0xC05CFF09, + MD_NTSTATUS_WIN_STATUS_VHD_SHARED = 0xC05CFF0A, + MD_NTSTATUS_WIN_STATUS_SPACES_RESILIENCY_TYPE_INVALID = 0xC0E70003, + MD_NTSTATUS_WIN_STATUS_SPACES_DRIVE_SECTOR_SIZE_INVALID = 0xC0E70004, + MD_NTSTATUS_WIN_STATUS_SPACES_INTERLEAVE_LENGTH_INVALID = 0xC0E70009, + MD_NTSTATUS_WIN_STATUS_SPACES_NUMBER_OF_COLUMNS_INVALID = 0xC0E7000A, + MD_NTSTATUS_WIN_STATUS_SPACES_NOT_ENOUGH_DRIVES = 0xC0E7000B +} MDNTStatusCodeWin; + +// These constants are defined in the MSDN documentation of +// the EXCEPTION_RECORD structure. +typedef enum { + MD_ACCESS_VIOLATION_WIN_READ = 0, + MD_ACCESS_VIOLATION_WIN_WRITE = 1, + MD_ACCESS_VIOLATION_WIN_EXEC = 8 +} MDAccessViolationTypeWin; + +// These constants are defined in the MSDN documentation of +// the EXCEPTION_RECORD structure. +typedef enum { + MD_IN_PAGE_ERROR_WIN_READ = 0, + MD_IN_PAGE_ERROR_WIN_WRITE = 1, + MD_IN_PAGE_ERROR_WIN_EXEC = 8 +} MDInPageErrorTypeWin; + +#endif /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_EXCEPTION_WIN32_H__ */ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_format.h b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_format.h new file mode 100644 index 000000000..7b36d1127 --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_format.h @@ -0,0 +1,1102 @@ +/* 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. */ + +/* minidump_format.h: A cross-platform reimplementation of minidump-related + * portions of DbgHelp.h from the Windows Platform SDK. + * + * (This is C99 source, please don't corrupt it with C++.) + * + * Structures that are defined by Microsoft to contain a zero-length array + * are instead defined here to contain an array with one element, as + * zero-length arrays are forbidden by standard C and C++. In these cases, + * *_minsize constants are provided to be used in place of sizeof. For a + * cleaner interface to these sizes when using C++, see minidump_size.h. + * + * These structures are also sufficient to populate minidump files. + * + * These definitions may be extended to support handling minidump files + * for other CPUs and other operating systems. + * + * Because precise data type sizes are crucial for this implementation to + * function properly and portably in terms of interoperability with minidumps + * produced by DbgHelp on Windows, a set of primitive types with known sizes + * are used as the basis of each structure defined by this file. DbgHelp + * on Windows is assumed to be the reference implementation; this file + * seeks to provide a cross-platform compatible implementation. To avoid + * collisions with the types and values defined and used by DbgHelp in the + * event that this implementation is used on Windows, each type and value + * defined here is given a new name, beginning with "MD". Names of the + * equivalent types and values in the Windows Platform SDK are given in + * comments. + * + * Author: Mark Mentovai */ + + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_FORMAT_H__ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_FORMAT_H__ + +#include + +#include "google_breakpad/common/breakpad_types.h" + + +#if defined(_MSC_VER) +/* Disable "zero-sized array in struct/union" warnings when compiling in + * MSVC. DbgHelp.h does this too. */ +#pragma warning(push) +#pragma warning(disable:4200) +#endif /* _MSC_VER */ + + +/* + * guiddef.h + */ + +typedef struct { + uint32_t data1; + uint16_t data2; + uint16_t data3; + uint8_t data4[8]; +} MDGUID; /* GUID */ + + +/* + * WinNT.h + */ + +/* Non-x86 CPU identifiers found in the high 24 bits of + * (MDRawContext*).context_flags. These aren't used by Breakpad, but are + * defined here for reference, to avoid assigning values that conflict + * (although some values already conflict). */ +#define MD_CONTEXT_IA64 0x00080000 /* CONTEXT_IA64 */ +/* Additional values from winnt.h in the Windows CE 5.0 SDK: */ +#define MD_CONTEXT_SHX 0x000000c0 /* CONTEXT_SH4 (Super-H, includes SH3) */ +#define MD_CONTEXT_ALPHA 0x00020000 /* CONTEXT_ALPHA */ + +/* As of Windows 7 SP1, the number of flag bits has increased to + * include 0x40 (CONTEXT_XSTATE): + * http://msdn.microsoft.com/en-us/library/hh134238%28v=vs.85%29.aspx */ +#define MD_CONTEXT_CPU_MASK 0xffffff00 + + +/* This is a base type for MDRawContextX86 and MDRawContextPPC. This + * structure should never be allocated directly. The actual structure type + * can be determined by examining the context_flags field. */ +typedef struct { + uint32_t context_flags; +} MDRawContextBase; + +#include "minidump_cpu_amd64.h" +#include "minidump_cpu_arm.h" +#include "minidump_cpu_arm64.h" +#include "minidump_cpu_mips.h" +#include "minidump_cpu_ppc.h" +#include "minidump_cpu_ppc64.h" +#include "minidump_cpu_sparc.h" +#include "minidump_cpu_x86.h" + +/* + * WinVer.h + */ + + +typedef struct { + uint32_t signature; + uint32_t struct_version; + uint32_t file_version_hi; + uint32_t file_version_lo; + uint32_t product_version_hi; + uint32_t product_version_lo; + uint32_t file_flags_mask; /* Identifies valid bits in fileFlags */ + uint32_t file_flags; + uint32_t file_os; + uint32_t file_type; + uint32_t file_subtype; + uint32_t file_date_hi; + uint32_t file_date_lo; +} MDVSFixedFileInfo; /* VS_FIXEDFILEINFO */ + +/* For (MDVSFixedFileInfo).signature */ +#define MD_VSFIXEDFILEINFO_SIGNATURE 0xfeef04bd + /* VS_FFI_SIGNATURE */ + +/* For (MDVSFixedFileInfo).version */ +#define MD_VSFIXEDFILEINFO_VERSION 0x00010000 + /* VS_FFI_STRUCVERSION */ + +/* For (MDVSFixedFileInfo).file_flags_mask and + * (MDVSFixedFileInfo).file_flags */ +#define MD_VSFIXEDFILEINFO_FILE_FLAGS_DEBUG 0x00000001 + /* VS_FF_DEBUG */ +#define MD_VSFIXEDFILEINFO_FILE_FLAGS_PRERELEASE 0x00000002 + /* VS_FF_PRERELEASE */ +#define MD_VSFIXEDFILEINFO_FILE_FLAGS_PATCHED 0x00000004 + /* VS_FF_PATCHED */ +#define MD_VSFIXEDFILEINFO_FILE_FLAGS_PRIVATEBUILD 0x00000008 + /* VS_FF_PRIVATEBUILD */ +#define MD_VSFIXEDFILEINFO_FILE_FLAGS_INFOINFERRED 0x00000010 + /* VS_FF_INFOINFERRED */ +#define MD_VSFIXEDFILEINFO_FILE_FLAGS_SPECIALBUILD 0x00000020 + /* VS_FF_SPECIALBUILD */ + +/* For (MDVSFixedFileInfo).file_os: high 16 bits */ +#define MD_VSFIXEDFILEINFO_FILE_OS_UNKNOWN 0 /* VOS_UNKNOWN */ +#define MD_VSFIXEDFILEINFO_FILE_OS_DOS (1 << 16) /* VOS_DOS */ +#define MD_VSFIXEDFILEINFO_FILE_OS_OS216 (2 << 16) /* VOS_OS216 */ +#define MD_VSFIXEDFILEINFO_FILE_OS_OS232 (3 << 16) /* VOS_OS232 */ +#define MD_VSFIXEDFILEINFO_FILE_OS_NT (4 << 16) /* VOS_NT */ +#define MD_VSFIXEDFILEINFO_FILE_OS_WINCE (5 << 16) /* VOS_WINCE */ +/* Low 16 bits */ +#define MD_VSFIXEDFILEINFO_FILE_OS__BASE 0 /* VOS__BASE */ +#define MD_VSFIXEDFILEINFO_FILE_OS__WINDOWS16 1 /* VOS__WINDOWS16 */ +#define MD_VSFIXEDFILEINFO_FILE_OS__PM16 2 /* VOS__PM16 */ +#define MD_VSFIXEDFILEINFO_FILE_OS__PM32 3 /* VOS__PM32 */ +#define MD_VSFIXEDFILEINFO_FILE_OS__WINDOWS32 4 /* VOS__WINDOWS32 */ + +/* For (MDVSFixedFileInfo).file_type */ +#define MD_VSFIXEDFILEINFO_FILE_TYPE_UNKNOWN 0 /* VFT_UNKNOWN */ +#define MD_VSFIXEDFILEINFO_FILE_TYPE_APP 1 /* VFT_APP */ +#define MD_VSFIXEDFILEINFO_FILE_TYPE_DLL 2 /* VFT_DLL */ +#define MD_VSFIXEDFILEINFO_FILE_TYPE_DRV 3 /* VFT_DLL */ +#define MD_VSFIXEDFILEINFO_FILE_TYPE_FONT 4 /* VFT_FONT */ +#define MD_VSFIXEDFILEINFO_FILE_TYPE_VXD 5 /* VFT_VXD */ +#define MD_VSFIXEDFILEINFO_FILE_TYPE_STATIC_LIB 7 /* VFT_STATIC_LIB */ + +/* For (MDVSFixedFileInfo).file_subtype */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_UNKNOWN 0 + /* VFT2_UNKNOWN */ +/* with file_type = MD_VSFIXEDFILEINFO_FILETYPE_DRV */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_DRV_PRINTER 1 + /* VFT2_DRV_PRINTER */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_DRV_KEYBOARD 2 + /* VFT2_DRV_KEYBOARD */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_DRV_LANGUAGE 3 + /* VFT2_DRV_LANGUAGE */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_DRV_DISPLAY 4 + /* VFT2_DRV_DISPLAY */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_DRV_MOUSE 5 + /* VFT2_DRV_MOUSE */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_DRV_NETWORK 6 + /* VFT2_DRV_NETWORK */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_DRV_SYSTEM 7 + /* VFT2_DRV_SYSTEM */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_DRV_INSTALLABLE 8 + /* VFT2_DRV_INSTALLABLE */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_DRV_SOUND 9 + /* VFT2_DRV_SOUND */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_DRV_COMM 10 + /* VFT2_DRV_COMM */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_DRV_INPUTMETHOD 11 + /* VFT2_DRV_INPUTMETHOD */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_DRV_VERSIONED_PRINTER 12 + /* VFT2_DRV_VERSIONED_PRINTER */ +/* with file_type = MD_VSFIXEDFILEINFO_FILETYPE_FONT */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_FONT_RASTER 1 + /* VFT2_FONT_RASTER */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_FONT_VECTOR 2 + /* VFT2_FONT_VECTOR */ +#define MD_VSFIXEDFILEINFO_FILE_SUBTYPE_FONT_TRUETYPE 3 + /* VFT2_FONT_TRUETYPE */ + + +/* + * DbgHelp.h + */ + + +/* An MDRVA is an offset into the minidump file. The beginning of the + * MDRawHeader is at offset 0. */ +typedef uint32_t MDRVA; /* RVA */ + +typedef struct { + uint32_t data_size; + MDRVA rva; +} MDLocationDescriptor; /* MINIDUMP_LOCATION_DESCRIPTOR */ + + +typedef struct { + /* The base address of the memory range on the host that produced the + * minidump. */ + uint64_t start_of_memory_range; + + MDLocationDescriptor memory; +} MDMemoryDescriptor; /* MINIDUMP_MEMORY_DESCRIPTOR */ + + +typedef struct { + uint32_t signature; + uint32_t version; + uint32_t stream_count; + MDRVA stream_directory_rva; /* A |stream_count|-sized array of + * MDRawDirectory structures. */ + uint32_t checksum; /* Can be 0. In fact, that's all that's + * been found in minidump files. */ + uint32_t time_date_stamp; /* time_t */ + uint64_t flags; +} MDRawHeader; /* MINIDUMP_HEADER */ + +/* For (MDRawHeader).signature and (MDRawHeader).version. Note that only the + * low 16 bits of (MDRawHeader).version are MD_HEADER_VERSION. Per the + * documentation, the high 16 bits are implementation-specific. */ +#define MD_HEADER_SIGNATURE 0x504d444d /* 'PMDM' */ + /* MINIDUMP_SIGNATURE */ +#define MD_HEADER_VERSION 0x0000a793 /* 42899 */ + /* MINIDUMP_VERSION */ + +/* For (MDRawHeader).flags: */ +typedef enum { + /* MD_NORMAL is the standard type of minidump. It includes full + * streams for the thread list, module list, exception, system info, + * and miscellaneous info. A memory list stream is also present, + * pointing to the same stack memory contained in the thread list, + * as well as a 256-byte region around the instruction address that + * was executing when the exception occurred. Stack memory is from + * 4 bytes below a thread's stack pointer up to the top of the + * memory region encompassing the stack. */ + MD_NORMAL = 0x00000000, + MD_WITH_DATA_SEGS = 0x00000001, + MD_WITH_FULL_MEMORY = 0x00000002, + MD_WITH_HANDLE_DATA = 0x00000004, + MD_FILTER_MEMORY = 0x00000008, + MD_SCAN_MEMORY = 0x00000010, + MD_WITH_UNLOADED_MODULES = 0x00000020, + MD_WITH_INDIRECTLY_REFERENCED_MEMORY = 0x00000040, + MD_FILTER_MODULE_PATHS = 0x00000080, + MD_WITH_PROCESS_THREAD_DATA = 0x00000100, + MD_WITH_PRIVATE_READ_WRITE_MEMORY = 0x00000200, + MD_WITHOUT_OPTIONAL_DATA = 0x00000400, + MD_WITH_FULL_MEMORY_INFO = 0x00000800, + MD_WITH_THREAD_INFO = 0x00001000, + MD_WITH_CODE_SEGS = 0x00002000, + MD_WITHOUT_AUXILLIARY_SEGS = 0x00004000, + MD_WITH_FULL_AUXILLIARY_STATE = 0x00008000, + MD_WITH_PRIVATE_WRITE_COPY_MEMORY = 0x00010000, + MD_IGNORE_INACCESSIBLE_MEMORY = 0x00020000, + MD_WITH_TOKEN_INFORMATION = 0x00040000 +} MDType; /* MINIDUMP_TYPE */ + + +typedef struct { + uint32_t stream_type; + MDLocationDescriptor location; +} MDRawDirectory; /* MINIDUMP_DIRECTORY */ + +/* For (MDRawDirectory).stream_type */ +typedef enum { + MD_UNUSED_STREAM = 0, + MD_RESERVED_STREAM_0 = 1, + MD_RESERVED_STREAM_1 = 2, + MD_THREAD_LIST_STREAM = 3, /* MDRawThreadList */ + MD_MODULE_LIST_STREAM = 4, /* MDRawModuleList */ + MD_MEMORY_LIST_STREAM = 5, /* MDRawMemoryList */ + MD_EXCEPTION_STREAM = 6, /* MDRawExceptionStream */ + MD_SYSTEM_INFO_STREAM = 7, /* MDRawSystemInfo */ + MD_THREAD_EX_LIST_STREAM = 8, + MD_MEMORY_64_LIST_STREAM = 9, + MD_COMMENT_STREAM_A = 10, + MD_COMMENT_STREAM_W = 11, + MD_HANDLE_DATA_STREAM = 12, + MD_FUNCTION_TABLE_STREAM = 13, + MD_UNLOADED_MODULE_LIST_STREAM = 14, + MD_MISC_INFO_STREAM = 15, /* MDRawMiscInfo */ + MD_MEMORY_INFO_LIST_STREAM = 16, /* MDRawMemoryInfoList */ + MD_THREAD_INFO_LIST_STREAM = 17, + MD_HANDLE_OPERATION_LIST_STREAM = 18, + MD_TOKEN_STREAM = 19, + MD_JAVASCRIPT_DATA_STREAM = 20, + MD_SYSTEM_MEMORY_INFO_STREAM = 21, + MD_PROCESS_VM_COUNTERS_STREAM = 22, + MD_LAST_RESERVED_STREAM = 0x0000ffff, + + /* Breakpad extension types. 0x4767 = "Gg" */ + MD_BREAKPAD_INFO_STREAM = 0x47670001, /* MDRawBreakpadInfo */ + MD_ASSERTION_INFO_STREAM = 0x47670002, /* MDRawAssertionInfo */ + /* These are additional minidump stream values which are specific to + * the linux breakpad implementation. */ + MD_LINUX_CPU_INFO = 0x47670003, /* /proc/cpuinfo */ + MD_LINUX_PROC_STATUS = 0x47670004, /* /proc/$x/status */ + MD_LINUX_LSB_RELEASE = 0x47670005, /* /etc/lsb-release */ + MD_LINUX_CMD_LINE = 0x47670006, /* /proc/$x/cmdline */ + MD_LINUX_ENVIRON = 0x47670007, /* /proc/$x/environ */ + MD_LINUX_AUXV = 0x47670008, /* /proc/$x/auxv */ + MD_LINUX_MAPS = 0x47670009, /* /proc/$x/maps */ + MD_LINUX_DSO_DEBUG = 0x4767000A, /* MDRawDebug{32,64} */ + + /* Crashpad extension types. 0x4350 = "CP" + * See Crashpad's minidump/minidump_extensions.h. */ + MD_CRASHPAD_INFO_STREAM = 0x43500001, /* MDRawCrashpadInfo */ +} MDStreamType; /* MINIDUMP_STREAM_TYPE */ + + +typedef struct { + uint32_t length; /* Length of buffer in bytes (not characters), + * excluding 0-terminator */ + uint16_t buffer[1]; /* UTF-16-encoded, 0-terminated */ +} MDString; /* MINIDUMP_STRING */ + +static const size_t MDString_minsize = offsetof(MDString, buffer[0]); + + +typedef struct { + uint32_t thread_id; + uint32_t suspend_count; + uint32_t priority_class; + uint32_t priority; + uint64_t teb; /* Thread environment block */ + MDMemoryDescriptor stack; + MDLocationDescriptor thread_context; /* MDRawContext[CPU] */ +} MDRawThread; /* MINIDUMP_THREAD */ + + +typedef struct { + uint32_t number_of_threads; + MDRawThread threads[1]; +} MDRawThreadList; /* MINIDUMP_THREAD_LIST */ + +static const size_t MDRawThreadList_minsize = offsetof(MDRawThreadList, + threads[0]); + + +typedef struct { + uint64_t base_of_image; + uint32_t size_of_image; + uint32_t checksum; /* 0 if unknown */ + uint32_t time_date_stamp; /* time_t */ + MDRVA module_name_rva; /* MDString, pathname or filename */ + MDVSFixedFileInfo version_info; + + /* The next field stores a CodeView record and is populated when a module's + * debug information resides in a PDB file. It identifies the PDB file. */ + MDLocationDescriptor cv_record; + + /* The next field is populated when a module's debug information resides + * in a DBG file. It identifies the DBG file. This field is effectively + * obsolete with modules built by recent toolchains. */ + MDLocationDescriptor misc_record; + + /* Alignment problem: reserved0 and reserved1 are defined by the platform + * SDK as 64-bit quantities. However, that results in a structure whose + * alignment is unpredictable on different CPUs and ABIs. If the ABI + * specifies full alignment of 64-bit quantities in structures (as ppc + * does), there will be padding between miscRecord and reserved0. If + * 64-bit quantities can be aligned on 32-bit boundaries (as on x86), + * this padding will not exist. (Note that the structure up to this point + * contains 1 64-bit member followed by 21 32-bit members.) + * As a workaround, reserved0 and reserved1 are instead defined here as + * four 32-bit quantities. This should be harmless, as there are + * currently no known uses for these fields. */ + uint32_t reserved0[2]; + uint32_t reserved1[2]; +} MDRawModule; /* MINIDUMP_MODULE */ + +/* The inclusion of a 64-bit type in MINIDUMP_MODULE forces the struct to + * be tail-padded out to a multiple of 64 bits under some ABIs (such as PPC). + * This doesn't occur on systems that don't tail-pad in this manner. Define + * this macro to be the usable size of the MDRawModule struct, and use it in + * place of sizeof(MDRawModule). */ +#define MD_MODULE_SIZE 108 + + +/* (MDRawModule).cv_record can reference MDCVInfoPDB20 or MDCVInfoPDB70. + * Ref.: http://www.debuginfo.com/articles/debuginfomatch.html + * MDCVInfoPDB70 is the expected structure type with recent toolchains. */ + +typedef struct { + uint32_t signature; + uint32_t offset; /* Offset to debug data (expect 0 in minidump) */ +} MDCVHeader; + +typedef struct { + MDCVHeader cv_header; + uint32_t signature; /* time_t debug information created */ + uint32_t age; /* revision of PDB file */ + uint8_t pdb_file_name[1]; /* Pathname or filename of PDB file */ +} MDCVInfoPDB20; + +static const size_t MDCVInfoPDB20_minsize = offsetof(MDCVInfoPDB20, + pdb_file_name[0]); + +#define MD_CVINFOPDB20_SIGNATURE 0x3031424e /* cvHeader.signature = '01BN' */ + +typedef struct { + uint32_t cv_signature; + MDGUID signature; /* GUID, identifies PDB file */ + uint32_t age; /* Identifies incremental changes to PDB file */ + uint8_t pdb_file_name[1]; /* Pathname or filename of PDB file, + * 0-terminated 8-bit character data (UTF-8?) */ +} MDCVInfoPDB70; + +static const size_t MDCVInfoPDB70_minsize = offsetof(MDCVInfoPDB70, + pdb_file_name[0]); + +#define MD_CVINFOPDB70_SIGNATURE 0x53445352 /* cvSignature = 'SDSR' */ + +/* + * Modern ELF toolchains insert a "build id" into the ELF headers that + * usually contains a hash of some ELF headers + sections to uniquely + * identify a binary. + * + * https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Developer_Guide/compiling-build-id.html + * https://sourceware.org/binutils/docs-2.26/ld/Options.html#index-g_t_002d_002dbuild_002did-292 + */ +typedef struct { + uint32_t cv_signature; + uint8_t build_id[1]; /* Bytes of build id from GNU_BUILD_ID ELF note. + * This is variable-length, but usually 20 bytes + * as the binutils ld default is a SHA-1 hash. */ +} MDCVInfoELF; + +static const size_t MDCVInfoELF_minsize = offsetof(MDCVInfoELF, + build_id[0]); + +#define MD_CVINFOELF_SIGNATURE 0x4270454c /* cvSignature = 'BpEL' */ + +/* In addition to the two CodeView record formats above, used for linking + * to external pdb files, it is possible for debugging data to be carried + * directly in the CodeView record itself. These signature values will + * be found in the first 4 bytes of the CodeView record. Additional values + * not commonly experienced in the wild are given by "Microsoft Symbol and + * Type Information", http://www.x86.org/ftp/manuals/tools/sym.pdf, section + * 7.2. An in-depth description of the CodeView 4.1 format is given by + * "Undocumented Windows 2000 Secrets", Windows 2000 Debugging Support/ + * Microsoft Symbol File Internals/CodeView Subsections, + * http://www.rawol.com/features/undocumented/sbs-w2k-1-windows-2000-debugging-support.pdf + */ +#define MD_CVINFOCV41_SIGNATURE 0x3930424e /* '90BN', CodeView 4.10. */ +#define MD_CVINFOCV50_SIGNATURE 0x3131424e /* '11BN', CodeView 5.0, + * MS C7-format (/Z7). */ + +#define MD_CVINFOUNKNOWN_SIGNATURE 0xffffffff /* An unlikely value. */ + +/* (MDRawModule).miscRecord can reference MDImageDebugMisc. The Windows + * structure is actually defined in WinNT.h. This structure is effectively + * obsolete with modules built by recent toolchains. */ + +typedef struct { + uint32_t data_type; /* IMAGE_DEBUG_TYPE_*, not defined here because + * this debug record type is mostly obsolete. */ + uint32_t length; /* Length of entire MDImageDebugMisc structure */ + uint8_t unicode; /* True if data is multibyte */ + uint8_t reserved[3]; + uint8_t data[1]; +} MDImageDebugMisc; /* IMAGE_DEBUG_MISC */ + +static const size_t MDImageDebugMisc_minsize = offsetof(MDImageDebugMisc, + data[0]); + + +typedef struct { + uint32_t number_of_modules; + MDRawModule modules[1]; +} MDRawModuleList; /* MINIDUMP_MODULE_LIST */ + +static const size_t MDRawModuleList_minsize = offsetof(MDRawModuleList, + modules[0]); + + +typedef struct { + uint32_t number_of_memory_ranges; + MDMemoryDescriptor memory_ranges[1]; +} MDRawMemoryList; /* MINIDUMP_MEMORY_LIST */ + +static const size_t MDRawMemoryList_minsize = offsetof(MDRawMemoryList, + memory_ranges[0]); + + +#define MD_EXCEPTION_MAXIMUM_PARAMETERS 15u + +typedef struct { + uint32_t exception_code; /* Windows: MDExceptionCodeWin, + * Mac OS X: MDExceptionMac, + * Linux: MDExceptionCodeLinux. */ + uint32_t exception_flags; /* Windows: 1 if noncontinuable, + Mac OS X: MDExceptionCodeMac. */ + uint64_t exception_record; /* Address (in the minidump-producing host's + * memory) of another MDException, for + * nested exceptions. */ + uint64_t exception_address; /* The address that caused the exception. + * Mac OS X: exception subcode (which is + * typically the address). */ + uint32_t number_parameters; /* Number of valid elements in + * exception_information. */ + uint32_t __align; + uint64_t exception_information[MD_EXCEPTION_MAXIMUM_PARAMETERS]; +} MDException; /* MINIDUMP_EXCEPTION */ + +#include "minidump_exception_fuchsia.h" +#include "minidump_exception_linux.h" +#include "minidump_exception_mac.h" +#include "minidump_exception_ps3.h" +#include "minidump_exception_solaris.h" +#include "minidump_exception_win32.h" + +typedef struct { + uint32_t thread_id; /* Thread in which the exception + * occurred. Corresponds to + * (MDRawThread).thread_id. */ + uint32_t __align; + MDException exception_record; + MDLocationDescriptor thread_context; /* MDRawContext[CPU] */ +} MDRawExceptionStream; /* MINIDUMP_EXCEPTION_STREAM */ + + +typedef union { + struct { + uint32_t vendor_id[3]; /* cpuid 0: ebx, edx, ecx */ + uint32_t version_information; /* cpuid 1: eax */ + uint32_t feature_information; /* cpuid 1: edx */ + uint32_t amd_extended_cpu_features; /* cpuid 0x80000001, ebx */ + } x86_cpu_info; + struct { + uint32_t cpuid; + uint32_t elf_hwcaps; /* linux specific, 0 otherwise */ + } arm_cpu_info; + struct { + uint64_t processor_features[2]; + } other_cpu_info; +} MDCPUInformation; /* CPU_INFORMATION */ + +/* For (MDCPUInformation).arm_cpu_info.elf_hwcaps. + * This matches the Linux kernel definitions from */ +typedef enum { + MD_CPU_ARM_ELF_HWCAP_SWP = (1 << 0), + MD_CPU_ARM_ELF_HWCAP_HALF = (1 << 1), + MD_CPU_ARM_ELF_HWCAP_THUMB = (1 << 2), + MD_CPU_ARM_ELF_HWCAP_26BIT = (1 << 3), + MD_CPU_ARM_ELF_HWCAP_FAST_MULT = (1 << 4), + MD_CPU_ARM_ELF_HWCAP_FPA = (1 << 5), + MD_CPU_ARM_ELF_HWCAP_VFP = (1 << 6), + MD_CPU_ARM_ELF_HWCAP_EDSP = (1 << 7), + MD_CPU_ARM_ELF_HWCAP_JAVA = (1 << 8), + MD_CPU_ARM_ELF_HWCAP_IWMMXT = (1 << 9), + MD_CPU_ARM_ELF_HWCAP_CRUNCH = (1 << 10), + MD_CPU_ARM_ELF_HWCAP_THUMBEE = (1 << 11), + MD_CPU_ARM_ELF_HWCAP_NEON = (1 << 12), + MD_CPU_ARM_ELF_HWCAP_VFPv3 = (1 << 13), + MD_CPU_ARM_ELF_HWCAP_VFPv3D16 = (1 << 14), + MD_CPU_ARM_ELF_HWCAP_TLS = (1 << 15), + MD_CPU_ARM_ELF_HWCAP_VFPv4 = (1 << 16), + MD_CPU_ARM_ELF_HWCAP_IDIVA = (1 << 17), + MD_CPU_ARM_ELF_HWCAP_IDIVT = (1 << 18), +} MDCPUInformationARMElfHwCaps; + +typedef struct { + /* The next 3 fields and numberOfProcessors are from the SYSTEM_INFO + * structure as returned by GetSystemInfo */ + uint16_t processor_architecture; + uint16_t processor_level; /* x86: 5 = 586, 6 = 686, ... */ + /* ARM: 6 = ARMv6, 7 = ARMv7 ... */ + uint16_t processor_revision; /* x86: 0xMMSS, where MM=model, + * SS=stepping */ + /* ARM: 0 */ + + uint8_t number_of_processors; + uint8_t product_type; /* Windows: VER_NT_* from WinNT.h */ + + /* The next 5 fields are from the OSVERSIONINFO structure as returned + * by GetVersionEx */ + uint32_t major_version; + uint32_t minor_version; + uint32_t build_number; + uint32_t platform_id; + MDRVA csd_version_rva; /* MDString further identifying the + * host OS. + * Windows: name of the installed OS + * service pack. + * Mac OS X: the Apple OS build number + * (sw_vers -buildVersion). + * Linux: uname -srvmo */ + + uint16_t suite_mask; /* Windows: VER_SUITE_* from WinNT.h */ + uint16_t reserved2; + + MDCPUInformation cpu; +} MDRawSystemInfo; /* MINIDUMP_SYSTEM_INFO */ + +/* For (MDRawSystemInfo).processor_architecture: */ +typedef enum { + MD_CPU_ARCHITECTURE_X86 = 0, /* PROCESSOR_ARCHITECTURE_INTEL */ + MD_CPU_ARCHITECTURE_MIPS = 1, /* PROCESSOR_ARCHITECTURE_MIPS */ + MD_CPU_ARCHITECTURE_ALPHA = 2, /* PROCESSOR_ARCHITECTURE_ALPHA */ + MD_CPU_ARCHITECTURE_PPC = 3, /* PROCESSOR_ARCHITECTURE_PPC */ + MD_CPU_ARCHITECTURE_SHX = 4, /* PROCESSOR_ARCHITECTURE_SHX + * (Super-H) */ + MD_CPU_ARCHITECTURE_ARM = 5, /* PROCESSOR_ARCHITECTURE_ARM */ + MD_CPU_ARCHITECTURE_IA64 = 6, /* PROCESSOR_ARCHITECTURE_IA64 */ + MD_CPU_ARCHITECTURE_ALPHA64 = 7, /* PROCESSOR_ARCHITECTURE_ALPHA64 */ + MD_CPU_ARCHITECTURE_MSIL = 8, /* PROCESSOR_ARCHITECTURE_MSIL + * (Microsoft Intermediate Language) */ + MD_CPU_ARCHITECTURE_AMD64 = 9, /* PROCESSOR_ARCHITECTURE_AMD64 */ + MD_CPU_ARCHITECTURE_X86_WIN64 = 10, + /* PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 (WoW64) */ + MD_CPU_ARCHITECTURE_ARM64 = 12, /* PROCESSOR_ARCHITECTURE_ARM64 */ + MD_CPU_ARCHITECTURE_SPARC = 0x8001, /* Breakpad-defined value for SPARC */ + MD_CPU_ARCHITECTURE_PPC64 = 0x8002, /* Breakpad-defined value for PPC64 */ + MD_CPU_ARCHITECTURE_ARM64_OLD = 0x8003, /* Breakpad-defined value for ARM64 */ + MD_CPU_ARCHITECTURE_MIPS64 = 0x8004, /* Breakpad-defined value for MIPS64 */ + MD_CPU_ARCHITECTURE_UNKNOWN = 0xffff /* PROCESSOR_ARCHITECTURE_UNKNOWN */ +} MDCPUArchitecture; + +/* For (MDRawSystemInfo).platform_id: */ +typedef enum { + MD_OS_WIN32S = 0, /* VER_PLATFORM_WIN32s (Windows 3.1) */ + MD_OS_WIN32_WINDOWS = 1, /* VER_PLATFORM_WIN32_WINDOWS (Windows 95-98-Me) */ + MD_OS_WIN32_NT = 2, /* VER_PLATFORM_WIN32_NT (Windows NT, 2000+) */ + MD_OS_WIN32_CE = 3, /* VER_PLATFORM_WIN32_CE, VER_PLATFORM_WIN32_HH + * (Windows CE, Windows Mobile, "Handheld") */ + + /* The following values are Breakpad-defined. */ + MD_OS_UNIX = 0x8000, /* Generic Unix-ish */ + MD_OS_MAC_OS_X = 0x8101, /* Mac OS X/Darwin */ + MD_OS_IOS = 0x8102, /* iOS */ + MD_OS_LINUX = 0x8201, /* Linux */ + MD_OS_SOLARIS = 0x8202, /* Solaris */ + MD_OS_ANDROID = 0x8203, /* Android */ + MD_OS_PS3 = 0x8204, /* PS3 */ + MD_OS_NACL = 0x8205, /* Native Client (NaCl) */ + MD_OS_FUCHSIA = 0x8206 /* Fuchsia */ +} MDOSPlatform; + +typedef struct { + uint64_t base_of_image; + uint32_t size_of_image; + uint32_t checksum; + uint32_t time_date_stamp; + MDRVA module_name_rva; +} MDRawUnloadedModule; + +typedef struct { + uint32_t size_of_header; + uint32_t size_of_entry; + uint32_t number_of_entries; +} MDRawUnloadedModuleList; /* MINIDUMP_UNLOADED_MODULE_LIST */ + +typedef struct { + uint16_t year; + uint16_t month; + uint16_t day_of_week; + uint16_t day; + uint16_t hour; + uint16_t minute; + uint16_t second; + uint16_t milliseconds; +} MDSystemTime; /* SYSTEMTIME */ + +typedef struct { + /* Required field. The bias is the difference, in minutes, between + * Coordinated Universal Time (UTC) and local time. + * Formula: UTC = local time + bias */ + int32_t bias; + /* A description for standard time. For example, "EST" could indicate Eastern + * Standard Time. In practice this contains the full time zone names. This + * string can be empty. */ + uint16_t standard_name[32]; /* UTF-16-encoded, 0-terminated */ + /* A MDSystemTime structure that contains a date and local time when the + * transition from daylight saving time to standard time occurs on this + * operating system. If the time zone does not support daylight saving time, + * the month member in the MDSystemTime structure is zero. */ + MDSystemTime standard_date; + /* The bias value to be used during local time translations that occur during + * standard time. */ + int32_t standard_bias; + /* A description for daylight saving time. For example, "PDT" could indicate + * Pacific Daylight Time. In practice this contains the full time zone names. + * This string can be empty. */ + uint16_t daylight_name[32]; /* UTF-16-encoded, 0-terminated */ + /* A MDSystemTime structure that contains a date and local time when the + * transition from standard time to daylight saving time occurs on this + * operating system. If the time zone does not support daylight saving time, + * the month member in the MDSystemTime structure is zero.*/ + MDSystemTime daylight_date; + /* The bias value to be used during local time translations that occur during + * daylight saving time. */ + int32_t daylight_bias; +} MDTimeZoneInformation; /* TIME_ZONE_INFORMATION */ + +/* MAX_PATH from windef.h */ +#define MD_MAX_PATH 260 + +/* For MDXStateConfigFeatureMscInfo.features */ +typedef struct { + uint32_t offset; + uint32_t size; +} MDXStateFeature; + +/* For MDXStateConfigFeatureMscInfo.enabled_features from winnt.h */ +typedef enum { + MD_XSTATE_LEGACY_FLOATING_POINT = 0, /* XSTATE_LEGACY_FLOATING_POINT */ + MD_XSTATE_LEGACY_SSE = 1, /* XSTATE_LEGACY_SSE */ + MD_XSTATE_GSSE = 2, /* XSTATE_GSSE */ + MD_XSTATE_AVX = MD_XSTATE_GSSE, /* XSTATE_AVX */ + MD_XSTATE_MPX_BNDREGS = 3, /* XSTATE_MPX_BNDREGS */ + MD_XSTATE_MPX_BNDCSR = 4, /* XSTATE_MPX_BNDCSR */ + MD_XSTATE_AVX512_KMASK = 5, /* XSTATE_AVX512_KMASK */ + MD_XSTATE_AVX512_ZMM_H = 6, /* XSTATE_AVX512_ZMM_H */ + MD_XSTATE_AVX512_ZMM = 7, /* XSTATE_AVX512_ZMM */ + MD_XSTATE_IPT = 8, /* XSTATE_IPT */ + MD_XSTATE_LWP = 62 /* XSTATE_LWP */ +} MDXStateFeatureFlag; + +/* MAXIMUM_XSTATE_FEATURES from winnt.h */ +#define MD_MAXIMUM_XSTATE_FEATURES 64 + +/* For MDRawMiscInfo.xstate_data */ +typedef struct { + uint32_t size_of_info; + uint32_t context_size; + /* An entry in the features array is valid only if the corresponding bit in + * the enabled_features flag is set. */ + uint64_t enabled_features; + MDXStateFeature features[MD_MAXIMUM_XSTATE_FEATURES]; +} MDXStateConfigFeatureMscInfo; + + +/* The miscellaneous information stream contains a variety + * of small pieces of information. A member is valid if + * it's within the available size and its corresponding + * bit is set. */ +typedef struct { + uint32_t size_of_info; /* Length of entire MDRawMiscInfo structure. */ + uint32_t flags1; + + /* The next field is only valid if flags1 contains + * MD_MISCINFO_FLAGS1_PROCESS_ID. */ + uint32_t process_id; + + /* The next 3 fields are only valid if flags1 contains + * MD_MISCINFO_FLAGS1_PROCESS_TIMES. */ + uint32_t process_create_time; /* time_t process started */ + uint32_t process_user_time; /* seconds of user CPU time */ + uint32_t process_kernel_time; /* seconds of kernel CPU time */ + + /* The following fields are not present in MINIDUMP_MISC_INFO but are + * in MINIDUMP_MISC_INFO_2. When this struct is populated, these values + * may not be set. Use flags1 and size_of_info to determine whether these + * values are present. These are only valid when flags1 contains + * MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO. */ + uint32_t processor_max_mhz; + uint32_t processor_current_mhz; + uint32_t processor_mhz_limit; + uint32_t processor_max_idle_state; + uint32_t processor_current_idle_state; + + /* The following fields are not present in MINIDUMP_MISC_INFO_2 but are + * in MINIDUMP_MISC_INFO_3. When this struct is populated, these values + * may not be set. Use flags1 and size_of_info to determine whether these + * values are present. */ + + /* The following field is only valid if flags1 contains + * MD_MISCINFO_FLAGS1_PROCESS_INTEGRITY. */ + uint32_t process_integrity_level; + + /* The following field is only valid if flags1 contains + * MD_MISCINFO_FLAGS1_PROCESS_EXECUTE_FLAGS. */ + uint32_t process_execute_flags; + + /* The following field is only valid if flags1 contains + * MD_MISCINFO_FLAGS1_PROTECTED_PROCESS. */ + uint32_t protected_process; + + /* The following 2 fields are only valid if flags1 contains + * MD_MISCINFO_FLAGS1_TIMEZONE. */ + uint32_t time_zone_id; + MDTimeZoneInformation time_zone; + + /* The following fields are not present in MINIDUMP_MISC_INFO_3 but are + * in MINIDUMP_MISC_INFO_4. When this struct is populated, these values + * may not be set. Use flags1 and size_of_info to determine whether these + * values are present. */ + + /* The following 2 fields are only valid if flags1 contains + * MD_MISCINFO_FLAGS1_BUILDSTRING. */ + uint16_t build_string[MD_MAX_PATH]; /* UTF-16-encoded, 0-terminated */ + uint16_t dbg_bld_str[40]; /* UTF-16-encoded, 0-terminated */ + + /* The following fields are not present in MINIDUMP_MISC_INFO_4 but are + * in MINIDUMP_MISC_INFO_5. When this struct is populated, these values + * may not be set. Use flags1 and size_of_info to determine whether these + * values are present. */ + + /* The following field has its own flags for establishing the validity of + * the structure's contents.*/ + MDXStateConfigFeatureMscInfo xstate_data; + + /* The following field is only valid if flags1 contains + * MD_MISCINFO_FLAGS1_PROCESS_COOKIE. */ + uint32_t process_cookie; +} MDRawMiscInfo; /* MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2, + * MINIDUMP_MISC_INFO_3, MINIDUMP_MISC_INFO_4, + * MINIDUMP_MISC_INFO_5, MINIDUMP_MISC_INFO_N */ + +static const size_t MD_MISCINFO_SIZE = + offsetof(MDRawMiscInfo, processor_max_mhz); +static const size_t MD_MISCINFO2_SIZE = + offsetof(MDRawMiscInfo, process_integrity_level); +static const size_t MD_MISCINFO3_SIZE = + offsetof(MDRawMiscInfo, build_string[0]); +static const size_t MD_MISCINFO4_SIZE = + offsetof(MDRawMiscInfo, xstate_data); +/* Version 5 of the MDRawMiscInfo structure is not a multiple of 8 in size and + * yet it contains some 8-bytes sized fields. This causes many compilers to + * round the structure size up to a multiple of 8 by adding padding at the end. + * The following hack is thus required for matching the proper on-disk size. */ +static const size_t MD_MISCINFO5_SIZE = + offsetof(MDRawMiscInfo, process_cookie) + sizeof(uint32_t); + +/* For (MDRawMiscInfo).flags1. These values indicate which fields in the + * MDRawMiscInfoStructure are valid. */ +typedef enum { + MD_MISCINFO_FLAGS1_PROCESS_ID = 0x00000001, + /* MINIDUMP_MISC1_PROCESS_ID */ + MD_MISCINFO_FLAGS1_PROCESS_TIMES = 0x00000002, + /* MINIDUMP_MISC1_PROCESS_TIMES */ + MD_MISCINFO_FLAGS1_PROCESSOR_POWER_INFO = 0x00000004, + /* MINIDUMP_MISC1_PROCESSOR_POWER_INFO */ + MD_MISCINFO_FLAGS1_PROCESS_INTEGRITY = 0x00000010, + /* MINIDUMP_MISC3_PROCESS_INTEGRITY */ + MD_MISCINFO_FLAGS1_PROCESS_EXECUTE_FLAGS = 0x00000020, + /* MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS */ + MD_MISCINFO_FLAGS1_TIMEZONE = 0x00000040, + /* MINIDUMP_MISC3_TIMEZONE */ + MD_MISCINFO_FLAGS1_PROTECTED_PROCESS = 0x00000080, + /* MINIDUMP_MISC3_PROTECTED_PROCESS */ + MD_MISCINFO_FLAGS1_BUILDSTRING = 0x00000100, + /* MINIDUMP_MISC4_BUILDSTRING */ + MD_MISCINFO_FLAGS1_PROCESS_COOKIE = 0x00000200, + /* MINIDUMP_MISC5_PROCESS_COOKIE */ +} MDMiscInfoFlags1; + +/* + * Around DbgHelp version 6.0, the style of new LIST structures changed + * from including an array of length 1 at the end of the struct to + * represent the variable-length data to including explicit + * "size of header", "size of entry" and "number of entries" fields + * in the header, presumably to allow backwards-compatibly-extending + * the structures in the future. The actual list entries follow the + * header data directly in this case. + */ + +typedef struct { + uint32_t size_of_header; /* sizeof(MDRawMemoryInfoList) */ + uint32_t size_of_entry; /* sizeof(MDRawMemoryInfo) */ + uint64_t number_of_entries; +} MDRawMemoryInfoList; /* MINIDUMP_MEMORY_INFO_LIST */ + +typedef struct { + uint64_t base_address; /* Base address of a region of pages */ + uint64_t allocation_base; /* Base address of a range of pages + * within this region. */ + uint32_t allocation_protection; /* Memory protection when this region + * was originally allocated: + * MDMemoryProtection */ + uint32_t __alignment1; + uint64_t region_size; + uint32_t state; /* MDMemoryState */ + uint32_t protection; /* MDMemoryProtection */ + uint32_t type; /* MDMemoryType */ + uint32_t __alignment2; +} MDRawMemoryInfo; /* MINIDUMP_MEMORY_INFO */ + +/* For (MDRawMemoryInfo).state */ +typedef enum { + MD_MEMORY_STATE_COMMIT = 0x1000, /* physical storage has been allocated */ + MD_MEMORY_STATE_RESERVE = 0x2000, /* reserved, but no physical storage */ + MD_MEMORY_STATE_FREE = 0x10000 /* available to be allocated */ +} MDMemoryState; + +/* For (MDRawMemoryInfo).allocation_protection and .protection */ +typedef enum { + MD_MEMORY_PROTECT_NOACCESS = 0x01, /* PAGE_NOACCESS */ + MD_MEMORY_PROTECT_READONLY = 0x02, /* PAGE_READONLY */ + MD_MEMORY_PROTECT_READWRITE = 0x04, /* PAGE_READWRITE */ + MD_MEMORY_PROTECT_WRITECOPY = 0x08, /* PAGE_WRITECOPY */ + MD_MEMORY_PROTECT_EXECUTE = 0x10, /* PAGE_EXECUTE */ + MD_MEMORY_PROTECT_EXECUTE_READ = 0x20, /* PAGE_EXECUTE_READ */ + MD_MEMORY_PROTECT_EXECUTE_READWRITE = 0x40, /* PAGE_EXECUTE_READWRITE */ + MD_MEMORY_PROTECT_EXECUTE_WRITECOPY = 0x80, /* PAGE_EXECUTE_WRITECOPY */ + /* These options can be combined with the previous flags. */ + MD_MEMORY_PROTECT_GUARD = 0x100, /* PAGE_GUARD */ + MD_MEMORY_PROTECT_NOCACHE = 0x200, /* PAGE_NOCACHE */ + MD_MEMORY_PROTECT_WRITECOMBINE = 0x400, /* PAGE_WRITECOMBINE */ +} MDMemoryProtection; + +/* Used to mask the mutually exclusive options from the combinable flags. */ +const uint32_t MD_MEMORY_PROTECTION_ACCESS_MASK = 0xFF; + +/* For (MDRawMemoryInfo).type */ +typedef enum { + MD_MEMORY_TYPE_PRIVATE = 0x20000, /* not shared by other processes */ + MD_MEMORY_TYPE_MAPPED = 0x40000, /* mapped into the view of a section */ + MD_MEMORY_TYPE_IMAGE = 0x1000000 /* mapped into the view of an image */ +} MDMemoryType; + +/* + * Breakpad extension types + */ + + +typedef struct { + /* validity is a bitmask with values from MDBreakpadInfoValidity, indicating + * which of the other fields in the structure are valid. */ + uint32_t validity; + + /* Thread ID of the handler thread. dump_thread_id should correspond to + * the thread_id of an MDRawThread in the minidump's MDRawThreadList if + * a dedicated thread in that list was used to produce the minidump. If + * the MDRawThreadList does not contain a dedicated thread used to produce + * the minidump, this field should be set to 0 and the validity field + * must not contain MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID. */ + uint32_t dump_thread_id; + + /* Thread ID of the thread that requested the minidump be produced. As + * with dump_thread_id, requesting_thread_id should correspond to the + * thread_id of an MDRawThread in the minidump's MDRawThreadList. For + * minidumps produced as a result of an exception, requesting_thread_id + * will be the same as the MDRawExceptionStream's thread_id field. For + * minidumps produced "manually" at the program's request, + * requesting_thread_id will indicate which thread caused the dump to be + * written. If the minidump was produced at the request of something + * other than a thread in the MDRawThreadList, this field should be set + * to 0 and the validity field must not contain + * MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID. */ + uint32_t requesting_thread_id; +} MDRawBreakpadInfo; + +/* For (MDRawBreakpadInfo).validity: */ +typedef enum { + /* When set, the dump_thread_id field is valid. */ + MD_BREAKPAD_INFO_VALID_DUMP_THREAD_ID = 1 << 0, + + /* When set, the requesting_thread_id field is valid. */ + MD_BREAKPAD_INFO_VALID_REQUESTING_THREAD_ID = 1 << 1 +} MDBreakpadInfoValidity; + +typedef struct { + /* expression, function, and file are 0-terminated UTF-16 strings. They + * may be truncated if necessary, but should always be 0-terminated when + * written to a file. + * Fixed-length strings are used because MiniDumpWriteDump doesn't offer + * a way for user streams to point to arbitrary RVAs for strings. */ + uint16_t expression[128]; /* Assertion that failed... */ + uint16_t function[128]; /* ...within this function... */ + uint16_t file[128]; /* ...in this file... */ + uint32_t line; /* ...at this line. */ + uint32_t type; +} MDRawAssertionInfo; + +/* For (MDRawAssertionInfo).type: */ +typedef enum { + MD_ASSERTION_INFO_TYPE_UNKNOWN = 0, + + /* Used for assertions that would be raised by the MSVC CRT but are + * directed to an invalid parameter handler instead. */ + MD_ASSERTION_INFO_TYPE_INVALID_PARAMETER, + + /* Used for assertions that would be raised by the MSVC CRT but are + * directed to a pure virtual call handler instead. */ + MD_ASSERTION_INFO_TYPE_PURE_VIRTUAL_CALL +} MDAssertionInfoData; + +/* These structs are used to store the DSO debug data in Linux minidumps, + * which is necessary for converting minidumps to usable coredumps. + * Because of a historical accident, several fields are variably encoded + * according to client word size, so tools potentially need to support both. */ + +typedef struct { + uint32_t addr; + MDRVA name; + uint32_t ld; +} MDRawLinkMap32; + +typedef struct { + uint32_t version; + MDRVA map; /* array of MDRawLinkMap32 */ + uint32_t dso_count; + uint32_t brk; + uint32_t ldbase; + uint32_t dynamic; +} MDRawDebug32; + +typedef struct { + uint64_t addr; + MDRVA name; + uint64_t ld; +} MDRawLinkMap64; + +typedef struct { + uint32_t version; + MDRVA map; /* array of MDRawLinkMap64 */ + uint32_t dso_count; + uint64_t brk; + uint64_t ldbase; + uint64_t dynamic; +} MDRawDebug64; + +/* Crashpad extension types. See Crashpad's minidump/minidump_extensions.h. */ + +typedef struct { + MDRVA key; + MDRVA value; +} MDRawSimpleStringDictionaryEntry; + +typedef struct { + uint32_t count; + MDRawSimpleStringDictionaryEntry entries[0]; +} MDRawSimpleStringDictionary; + +typedef struct { + uint32_t version; + MDLocationDescriptor list_annotations; + MDLocationDescriptor simple_annotations; /* MDRawSimpleStringDictionary */ +} MDRawModuleCrashpadInfo; + +typedef struct { + uint32_t minidump_module_list_index; + MDLocationDescriptor location; /* MDRawModuleCrashpadInfo */ +} MDRawModuleCrashpadInfoLink; + +typedef struct { + uint32_t count; + MDLocationDescriptor modules[0]; /* MDRawModuleCrashpadInfoLink */ +} MDRawModuleCrashpadInfoList; + +typedef struct { + uint32_t version; + MDGUID report_id; + MDGUID client_id; + MDLocationDescriptor simple_annotations; /* MDRawSimpleStringDictionary */ + MDLocationDescriptor module_list; /* MDRawModuleCrashpadInfoList */ +} MDRawCrashpadInfo; + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif /* _MSC_VER */ + + +#endif /* GOOGLE_BREAKPAD_COMMON_MINIDUMP_FORMAT_H__ */ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_size.h b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_size.h new file mode 100644 index 000000000..fae57923c --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/common/minidump_size.h @@ -0,0 +1,113 @@ +// 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. */ + +// minidump_size.h: Provides a C++ template for programmatic access to +// the sizes of various types defined in minidump_format.h. +// +// Author: Mark Mentovai + +#ifndef GOOGLE_BREAKPAD_COMMON_MINIDUMP_SIZE_H__ +#define GOOGLE_BREAKPAD_COMMON_MINIDUMP_SIZE_H__ + +#include + +#include "google_breakpad/common/minidump_format.h" + +namespace google_breakpad { + +template +class minidump_size { + public: + static size_t size() { return sizeof(T); } +}; + +// Explicit specializations for variable-length types. The size returned +// for these should be the size for an object without its variable-length +// section. + +template<> +class minidump_size { + public: + static size_t size() { return MDString_minsize; } +}; + +template<> +class minidump_size { + public: + static size_t size() { return MDRawThreadList_minsize; } +}; + +template<> +class minidump_size { + public: + static size_t size() { return MDCVInfoPDB20_minsize; } +}; + +template<> +class minidump_size { + public: + static size_t size() { return MDCVInfoPDB70_minsize; } +}; + +template<> +class minidump_size { + public: + static size_t size() { return MDCVInfoELF_minsize; } +}; + +template<> +class minidump_size { + public: + static size_t size() { return MDImageDebugMisc_minsize; } +}; + +template<> +class minidump_size { + public: + static size_t size() { return MDRawModuleList_minsize; } +}; + +template<> +class minidump_size { + public: + static size_t size() { return MDRawMemoryList_minsize; } +}; + +// Explicit specialization for MDRawModule, for which sizeof may include +// tail-padding on some architectures but not others. + +template<> +class minidump_size { + public: + static size_t size() { return MD_MODULE_SIZE; } +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_COMMON_MINIDUMP_SIZE_H__ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/processor/basic_source_line_resolver.h b/shared/sentry/external/breakpad/src/google_breakpad/processor/basic_source_line_resolver.h new file mode 100644 index 000000000..d676ab70e --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/processor/basic_source_line_resolver.h @@ -0,0 +1,184 @@ +// 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. + +// basic_source_line_resolver.h: BasicSourceLineResolver is derived from +// SourceLineResolverBase, and is a concrete implementation of +// SourceLineResolverInterface, using address map files produced by a +// compatible writer, e.g. PDBSourceLineWriter. +// +// see "processor/source_line_resolver_base.h" +// and "source_line_resolver_interface.h" for more documentation. + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_BASIC_SOURCE_LINE_RESOLVER_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_BASIC_SOURCE_LINE_RESOLVER_H__ + +#include +#include +#include + +#include "common/using_std_string.h" +#include "google_breakpad/processor/source_line_resolver_base.h" + +namespace google_breakpad { + +using std::map; + +class BasicSourceLineResolver : public SourceLineResolverBase { + public: + BasicSourceLineResolver(); + virtual ~BasicSourceLineResolver() { } + + using SourceLineResolverBase::LoadModule; + using SourceLineResolverBase::LoadModuleUsingMapBuffer; + using SourceLineResolverBase::LoadModuleUsingMemoryBuffer; + using SourceLineResolverBase::ShouldDeleteMemoryBufferAfterLoadModule; + using SourceLineResolverBase::UnloadModule; + using SourceLineResolverBase::HasModule; + using SourceLineResolverBase::IsModuleCorrupt; + using SourceLineResolverBase::FillSourceLineInfo; + using SourceLineResolverBase::FindWindowsFrameInfo; + using SourceLineResolverBase::FindCFIFrameInfo; + + private: + // friend declarations: + friend class BasicModuleFactory; + friend class ModuleComparer; + friend class ModuleSerializer; + template friend class SimpleSerializer; + + // Function derives from SourceLineResolverBase::Function. + struct Function; + // Module implements SourceLineResolverBase::Module interface. + class Module; + + // Disallow unwanted copy ctor and assignment operator + BasicSourceLineResolver(const BasicSourceLineResolver&); + void operator=(const BasicSourceLineResolver&); +}; + +// Helper class, containing useful methods for parsing of Breakpad symbol files. +class SymbolParseHelper { + public: + using MemAddr = SourceLineResolverInterface::MemAddr; + + // Parses a |file_line| declaration. Returns true on success. + // Format: FILE . + // Notice, that this method modifies the input |file_line| which is why it + // can't be const. On success, , and are stored in |*index|, + // and |*filename|. No allocation is done, |*filename| simply points inside + // |file_line|. + static bool ParseFile(char* file_line, // in + long* index, // out + char** filename); // out + + // Parses a |inline_origin_line| declaration. Returns true on success. + // Old Format: INLINE_ORIGIN . + // New Format: INLINE_ORIGIN . + // Notice, that this method modifies the input |inline_origin_line| which is + // why it can't be const. On success, , , + // and are stored in |*has_file_id*|, |*origin_id|, |*file_id|, and + // |*name|. No allocation is done, |*name| simply points inside + // |inline_origin_line|. + static bool ParseInlineOrigin(char* inline_origin_line, // in + bool* has_file_id, // out + long* origin_id, // out + long* file_id, // out + char** name); // out + + // Parses a |inline| declaration. Returns true on success. + // Old Format: INLINE + // [
    ]+ + // New Format: INLINE + // [
    ]+ + // Notice, that this method modifies the input |inline| + // which is why it can't be const. On success, , + // , and are stored in + // |*has_call_site_file_id*|, |*inline_nest_level|, |*call_site_line|, and + // |*origin_id|, and all pairs of (
    , ) are added into ranges. + static bool ParseInline( + char* inline_line, // in + bool* has_call_site_file_id, // out + long* inline_nest_level, // out + long* call_site_line, // out + long* call_site_file_id, // out + long* origin_id, // out + std::vector>* ranges); // out + + // Parses a |function_line| declaration. Returns true on success. + // Format: FUNC []
    . + // Notice, that this method modifies the input |function_line| which is why it + // can't be const. On success, the presence of ,
    , , + // , and are stored in |*is_multiple|, |*address|, + // |*size|, |*stack_param_size|, and |*name|. No allocation is done, |*name| + // simply points inside |function_line|. + static bool ParseFunction(char* function_line, // in + bool* is_multiple, // out + uint64_t* address, // out + uint64_t* size, // out + long* stack_param_size, // out + char** name); // out + + // Parses a |line| declaration. Returns true on success. + // Format:
    + // Notice, that this method modifies the input |function_line| which is why + // it can't be const. On success,
    , , , and + // are stored in |*address|, |*size|, |*line_number|, and + // |*source_file|. + static bool ParseLine(char* line_line, // in + uint64_t* address, // out + uint64_t* size, // out + long* line_number, // out + long* source_file); // out + + // Parses a |public_line| declaration. Returns true on success. + // Format: PUBLIC []
    + // Notice, that this method modifies the input |function_line| which is why + // it can't be const. On success, the presence of ,
    , + // , are stored in |*is_multiple|, |*address|, + // |*stack_param_size|, and |*name|. No allocation is done, |*name| simply + // points inside |public_line|. + static bool ParsePublicSymbol(char* public_line, // in + bool* is_multiple, // out + uint64_t* address, // out + long* stack_param_size, // out + char** name); // out + + private: + // Used for success checks after strtoull and strtol. + static bool IsValidAfterNumber(char* after_number); + + // Only allow static methods. + SymbolParseHelper(); + SymbolParseHelper(const SymbolParseHelper&); + void operator=(const SymbolParseHelper&); +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_BASIC_SOURCE_LINE_RESOLVER_H__ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/processor/call_stack.h b/shared/sentry/external/breakpad/src/google_breakpad/processor/call_stack.h new file mode 100644 index 000000000..c59142315 --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/processor/call_stack.h @@ -0,0 +1,87 @@ +// 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. + +// call_stack.h: A call stack comprised of stack frames. +// +// This class manages a vector of stack frames. It is used instead of +// exposing the vector directly to allow the CallStack to own StackFrame +// pointers without having to publicly export the linked_ptr class. A +// CallStack must be composed of pointers instead of objects to allow for +// CPU-specific StackFrame subclasses. +// +// By convention, the stack frame at index 0 is the innermost callee frame, +// and the frame at the highest index in a call stack is the outermost +// caller. CallStack only allows stacks to be built by pushing frames, +// beginning with the innermost callee frame. +// +// Author: Mark Mentovai + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_CALL_STACK_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_CALL_STACK_H__ + +#include +#include + +namespace google_breakpad { + +using std::vector; + +struct StackFrame; +template class linked_ptr; + +class CallStack { + public: + CallStack() { Clear(); } + ~CallStack(); + + // Resets the CallStack to its initial empty state + void Clear(); + + const vector* frames() const { return &frames_; } + + // Set the TID associated with this call stack. + void set_tid(uint32_t tid) { tid_ = tid; } + + uint32_t tid() const { return tid_; } + + private: + // Stackwalker is responsible for building the frames_ vector. + friend class Stackwalker; + + // Storage for pushed frames. + vector frames_; + + // The TID associated with this call stack. Default to 0 if it's not + // available. + uint32_t tid_; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCSSOR_CALL_STACK_H__ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/processor/code_module.h b/shared/sentry/external/breakpad/src/google_breakpad/processor/code_module.h new file mode 100644 index 000000000..29b8d9c9a --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/processor/code_module.h @@ -0,0 +1,104 @@ +// 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. + +// code_module.h: Carries information about code modules that are loaded +// into a process. +// +// Author: Mark Mentovai + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_CODE_MODULE_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_CODE_MODULE_H__ + +#include + +#include "common/using_std_string.h" +#include "google_breakpad/common/breakpad_types.h" + +namespace google_breakpad { + +class CodeModule { + public: + virtual ~CodeModule() {} + + // The base address of this code module as it was loaded by the process. + // (uint64_t)-1 on error. + virtual uint64_t base_address() const = 0; + + // The size of the code module. 0 on error. + virtual uint64_t size() const = 0; + + // The path or file name that the code module was loaded from. Empty on + // error. + virtual string code_file() const = 0; + + // An identifying string used to discriminate between multiple versions and + // builds of the same code module. This may contain a uuid, timestamp, + // version number, or any combination of this or other information, in an + // implementation-defined format. Empty on error. + virtual string code_identifier() const = 0; + + // The filename containing debugging information associated with the code + // module. If debugging information is stored in a file separate from the + // code module itself (as is the case when .pdb or .dSYM files are used), + // this will be different from code_file. If debugging information is + // stored in the code module itself (possibly prior to stripping), this + // will be the same as code_file. Empty on error. + virtual string debug_file() const = 0; + + // An identifying string similar to code_identifier, but identifies a + // specific version and build of the associated debug file. This may be + // the same as code_identifier when the debug_file and code_file are + // identical or when the same identifier is used to identify distinct + // debug and code files. + virtual string debug_identifier() const = 0; + + // A human-readable representation of the code module's version. Empty on + // error. + virtual string version() const = 0; + + // Creates a new copy of this CodeModule object, which the caller takes + // ownership of. The new CodeModule may be of a different concrete class + // than the CodeModule being copied, but will behave identically to the + // copied CodeModule as far as the CodeModule interface is concerned. + virtual CodeModule* Copy() const = 0; + + // Getter and setter for shrink_down_delta. This is used when the address + // range for a module is shrunk down due to address range conflicts with + // other modules. The base_address and size fields are not updated and they + // should always reflect the original values (reported in the minidump). + virtual uint64_t shrink_down_delta() const = 0; + virtual void SetShrinkDownDelta(uint64_t shrink_down_delta) = 0; + + // Whether the module was unloaded from memory. + virtual bool is_unloaded() const = 0; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_CODE_MODULE_H__ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/processor/code_modules.h b/shared/sentry/external/breakpad/src/google_breakpad/processor/code_modules.h new file mode 100644 index 000000000..74f113c19 --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/processor/code_modules.h @@ -0,0 +1,108 @@ +// 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. + +// code_modules.h: Contains all of the CodeModule objects that were loaded +// into a single process. +// +// Author: Mark Mentovai + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_CODE_MODULES_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_CODE_MODULES_H__ + +#include + +#include + +#include "google_breakpad/common/breakpad_types.h" +#include "processor/linked_ptr.h" + +namespace google_breakpad { + +class CodeModule; + +class CodeModules { + public: + virtual ~CodeModules() {} + + // The number of contained CodeModule objects. + virtual unsigned int module_count() const = 0; + + // Random access to modules. Returns the module whose code is present + // at the address indicated by |address|. If no module is present at this + // address, returns NULL. Ownership of the returned CodeModule is retained + // by the CodeModules object; pointers returned by this method are valid for + // comparison with pointers returned by the other Get methods. + virtual const CodeModule* GetModuleForAddress(uint64_t address) const = 0; + + // Returns the module corresponding to the main executable. If there is + // no main executable, returns NULL. Ownership of the returned CodeModule + // is retained by the CodeModules object; pointers returned by this method + // are valid for comparison with pointers returned by the other Get + // methods. + virtual const CodeModule* GetMainModule() const = 0; + + // Sequential access to modules. A sequence number of 0 corresponds to the + // module residing lowest in memory. If the sequence number is out of + // range, returns NULL. Ownership of the returned CodeModule is retained + // by the CodeModules object; pointers returned by this method are valid for + // comparison with pointers returned by the other Get methods. + virtual const CodeModule* GetModuleAtSequence( + unsigned int sequence) const = 0; + + // Sequential access to modules. This is similar to GetModuleAtSequence, + // except no ordering requirement is enforced. A CodeModules implementation + // may return CodeModule objects from GetModuleAtIndex in any order it + // wishes, provided that the order remain the same throughout the life of + // the CodeModules object. Typically, GetModuleAtIndex would be used by + // a caller to enumerate all CodeModule objects quickly when the enumeration + // does not require any ordering. If the index argument is out of range, + // returns NULL. Ownership of the returned CodeModule is retained by + // the CodeModules object; pointers returned by this method are valid for + // comparison with pointers returned by the other Get methods. + virtual const CodeModule* GetModuleAtIndex(unsigned int index) const = 0; + + // Creates a new copy of this CodeModules object, which the caller takes + // ownership of. The new object will also contain copies of the existing + // object's child CodeModule objects. The new CodeModules object may be of + // a different concrete class than the object being copied, but will behave + // identically to the copied object as far as the CodeModules and CodeModule + // interfaces are concerned, except that the order that GetModuleAtIndex + // returns objects in may differ between a copy and the original CodeModules + // object. + virtual const CodeModules* Copy() const = 0; + + // Returns a vector of all modules which address ranges needed to be shrunk + // down due to address range conflicts with other modules. + virtual std::vector > + GetShrunkRangeModules() const = 0; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_CODE_MODULES_H__ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/processor/dump_context.h b/shared/sentry/external/breakpad/src/google_breakpad/processor/dump_context.h new file mode 100644 index 000000000..df80bf7ef --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/processor/dump_context.h @@ -0,0 +1,116 @@ +// 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. + +// dump_context.h: A (mini/micro) dump CPU-specific context. + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_DUMP_CONTEXT_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_DUMP_CONTEXT_H__ + +#include "google_breakpad/common/minidump_format.h" +#include "google_breakpad/processor/dump_object.h" + +namespace google_breakpad { + +// DumpContext carries a CPU-specific MDRawContext structure, which contains CPU +// context such as register states. +class DumpContext : public DumpObject { + public: + virtual ~DumpContext(); + + // Returns an MD_CONTEXT_* value such as MD_CONTEXT_X86 or MD_CONTEXT_PPC + // identifying the CPU type that the context was collected from. The + // returned value will identify the CPU only, and will have any other + // MD_CONTEXT_* bits masked out. Returns 0 on failure. + uint32_t GetContextCPU() const; + + // Return the raw value of |context_flags_| + uint32_t GetContextFlags() const; + + // Returns raw CPU-specific context data for the named CPU type. If the + // context data does not match the CPU type or does not exist, returns NULL. + const MDRawContextAMD64* GetContextAMD64() const; + const MDRawContextARM* GetContextARM() const; + const MDRawContextARM64* GetContextARM64() const; + const MDRawContextMIPS* GetContextMIPS() const; + const MDRawContextPPC* GetContextPPC() const; + const MDRawContextPPC64* GetContextPPC64() const; + const MDRawContextSPARC* GetContextSPARC() const; + const MDRawContextX86* GetContextX86() const; + + // A convenience method to get the instruction pointer out of the + // MDRawContext, since it varies per-CPU architecture. + bool GetInstructionPointer(uint64_t* ip) const; + + // Similar to the GetInstructionPointer method, this method gets the stack + // pointer for all CPU architectures. + bool GetStackPointer(uint64_t* sp) const; + + // Print a human-readable representation of the object to stdout. + void Print(); + + protected: + DumpContext(); + + // Sets row CPU-specific context data for the names CPU type. + void SetContextFlags(uint32_t context_flags); + void SetContextX86(MDRawContextX86* x86); + void SetContextPPC(MDRawContextPPC* ppc); + void SetContextPPC64(MDRawContextPPC64* ppc64); + void SetContextAMD64(MDRawContextAMD64* amd64); + void SetContextSPARC(MDRawContextSPARC* ctx_sparc); + void SetContextARM(MDRawContextARM* arm); + void SetContextARM64(MDRawContextARM64* arm64); + void SetContextMIPS(MDRawContextMIPS* ctx_mips); + + // Free the CPU-specific context structure. + void FreeContext(); + + private: + // The CPU-specific context structure. + union { + MDRawContextBase* base; + MDRawContextX86* x86; + MDRawContextPPC* ppc; + MDRawContextPPC64* ppc64; + MDRawContextAMD64* amd64; + // on Solaris SPARC, sparc is defined as a numeric constant, + // so variables can NOT be named as sparc + MDRawContextSPARC* ctx_sparc; + MDRawContextARM* arm; + MDRawContextARM64* arm64; + MDRawContextMIPS* ctx_mips; + } context_; + + // Store this separately because of the weirdo AMD64 context + uint32_t context_flags_; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_DUMP_CONTEXT_H__ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/processor/dump_object.h b/shared/sentry/external/breakpad/src/google_breakpad/processor/dump_object.h new file mode 100644 index 000000000..112f687f4 --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/processor/dump_object.h @@ -0,0 +1,53 @@ +// 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. + +// dump_object.h: A base class for all mini/micro dump object. + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_DUMP_OBJECT_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_DUMP_OBJECT_H__ + +namespace google_breakpad { + +// DumpObject is the base of various mini/micro dump's objects. +class DumpObject { + public: + DumpObject(); + + bool valid() const { return valid_; } + + protected: + // DumpObjects are not valid when created. When a subclass populates its own + // fields, it can set valid_ to true. Accessors and mutators may wish to + // consider or alter the valid_ state as they interact with objects. + bool valid_; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_DUMP_OBJECT_H__ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/processor/exception_record.h b/shared/sentry/external/breakpad/src/google_breakpad/processor/exception_record.h new file mode 100644 index 000000000..eac6c90ae --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/processor/exception_record.h @@ -0,0 +1,124 @@ +// 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. +// +// exception_record.h: A snapshot of an exception record. +// +// Author: Ivan Penkov + +#ifndef THIRD_PARTY_BREAKPAD_SRC_GOOGLE_BREAKPAD_PROCESSOR_EXCEPTION_RECORD_H_ +#define THIRD_PARTY_BREAKPAD_SRC_GOOGLE_BREAKPAD_PROCESSOR_EXCEPTION_RECORD_H_ + +#include + +namespace google_breakpad { + +// Additional argument that describes the exception. +class ExceptionParameter { + public: + ExceptionParameter(uint64_t value, const string& description) + : value_(value), description_(description) {} + // Accessors. See the data declarations below. + uint64_t value() const { return value_; } + void set_value(uint64_t value) { value_ = value; } + const string& description() const { return description_; } + void set_description(const string& description) { + description_ = description; + } + + private: + // Parameter value. + uint64_t value_; + // Human readable description/interpretation of the above value. + string description_; +}; + +// A snapshot of an exception record. Contains exception record details: code, +// flags, address, parameters. +class ExceptionRecord { + public: + // Accessors. See the data declarations below. + uint32_t code() const { return code_; } + const string& code_description() const { return code_description_; } + void set_code(uint32_t code, const string& description) { + code_ = code; + code_description_ = description; + } + + uint32_t flags() const { return flags_; } + const string& flags_description() const { return flags_description_; } + void set_flags(uint32_t flags, const string& description) { + flags_ = flags; + flags_description_ = description; + } + + uint64_t nested_exception_record_address() const { + return nested_exception_record_address_; + } + void set_nested_exception_record_address( + uint64_t nested_exception_record_address) { + nested_exception_record_address_ = nested_exception_record_address; + } + + uint64_t address() const { return address_; } + void set_address(uint64_t address) { address_ = address; } + + const std::vector* parameters() const { + return ¶meters_; + } + void add_parameter(uint64_t value, const string& description) { + parameters_.push_back(ExceptionParameter(value, description)); + } + + private: + // Exception code. + uint32_t code_; + string code_description_; + + // Exception flags. + uint32_t flags_; + string flags_description_; + + // The address of an associated MDException structure. Exception records can + // be chained together to provide additional information when nested + // exceptions occur. + uint64_t nested_exception_record_address_; + + // The memory address that caused the exception. For data access errors, + // this will be the data address that caused the fault. For code errors, + // this will be the address of the instruction that caused the fault. + uint64_t address_; + + // An array of additional arguments that describe the exception. + std::vector parameters_; +}; + +} // namespace google_breakpad + + +#endif // THIRD_PARTY_BREAKPAD_SRC_GOOGLE_BREAKPAD_PROCESSOR_EXCEPTION_RECORD_H_ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/processor/exploitability.h b/shared/sentry/external/breakpad/src/google_breakpad/processor/exploitability.h new file mode 100644 index 000000000..014413c94 --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/processor/exploitability.h @@ -0,0 +1,82 @@ +// 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. + +// exploitability_engine.h: Generic exploitability engine. +// +// The Exploitability class is an abstract base class providing common +// generic methods that apply to exploitability engines for specific platforms. +// Specific implementations will extend this class by providing run +// methods to fill in the exploitability_ enumeration of the ProcessState +// for a crash. +// +// Author: Cris Neckar + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_EXPLOITABILITY_H_ +#define GOOGLE_BREAKPAD_PROCESSOR_EXPLOITABILITY_H_ + +#include "google_breakpad/common/breakpad_types.h" +#include "google_breakpad/processor/minidump.h" +#include "google_breakpad/processor/process_state.h" + +namespace google_breakpad { + +class Exploitability { + public: + virtual ~Exploitability() {} + + static Exploitability *ExploitabilityForPlatform(Minidump *dump, + ProcessState *process_state); + + // The boolean parameter signals whether the exploitability engine is + // enabled to call out to objdump for disassembly. This is disabled by + // default. It is used to check the identity of the instruction that + // caused the program to crash. This should not be enabled if there are + // portability concerns. + static Exploitability *ExploitabilityForPlatform(Minidump *dump, + ProcessState *process_state, + bool enable_objdump); + + ExploitabilityRating CheckExploitability(); + bool AddressIsAscii(uint64_t); + + protected: + Exploitability(Minidump *dump, + ProcessState *process_state); + + Minidump *dump_; + ProcessState *process_state_; + SystemInfo *system_info_; + + private: + virtual ExploitabilityRating CheckPlatformExploitability() = 0; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_EXPLOITABILITY_H_ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/processor/fast_source_line_resolver.h b/shared/sentry/external/breakpad/src/google_breakpad/processor/fast_source_line_resolver.h new file mode 100644 index 000000000..535fc1064 --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/processor/fast_source_line_resolver.h @@ -0,0 +1,102 @@ +// 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. +// +// fast_source_line_resolver.h: FastSourceLineResolver is derived from +// SourceLineResolverBase, and is a concrete implementation of +// SourceLineResolverInterface. +// +// FastSourceLineResolver is a sibling class of BasicSourceLineResolver. The +// difference is FastSourceLineResolver loads a serialized memory chunk of data +// which can be used directly a Module without parsing or copying of underlying +// data. Therefore loading a symbol in FastSourceLineResolver is much faster +// and more memory-efficient than BasicSourceLineResolver. +// +// See "source_line_resolver_base.h" and +// "google_breakpad/source_line_resolver_interface.h" for more reference. +// +// Author: Siyang Xie (lambxsy@google.com) + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_FAST_SOURCE_LINE_RESOLVER_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_FAST_SOURCE_LINE_RESOLVER_H__ + +#include +#include + +#include "google_breakpad/processor/source_line_resolver_base.h" + +namespace google_breakpad { + +using std::map; + +class FastSourceLineResolver : public SourceLineResolverBase { + public: + FastSourceLineResolver(); + virtual ~FastSourceLineResolver() { } + + using SourceLineResolverBase::FillSourceLineInfo; + using SourceLineResolverBase::FindCFIFrameInfo; + using SourceLineResolverBase::FindWindowsFrameInfo; + using SourceLineResolverBase::HasModule; + using SourceLineResolverBase::IsModuleCorrupt; + using SourceLineResolverBase::LoadModule; + using SourceLineResolverBase::LoadModuleUsingMapBuffer; + using SourceLineResolverBase::LoadModuleUsingMemoryBuffer; + using SourceLineResolverBase::UnloadModule; + + private: + // Friend declarations. + friend class ModuleComparer; + friend class ModuleSerializer; + friend class FastModuleFactory; + + // Nested types that will derive from corresponding nested types defined in + // SourceLineResolverBase. + struct Line; + struct Function; + struct Inline; + struct InlineOrigin; + struct PublicSymbol; + class Module; + + // Deserialize raw memory data to construct a WindowsFrameInfo object. + static WindowsFrameInfo CopyWFI(const char *raw_memory); + + // FastSourceLineResolver requires the memory buffer stays alive during the + // lifetime of a corresponding module, therefore it needs to redefine this + // virtual method. + virtual bool ShouldDeleteMemoryBufferAfterLoadModule(); + + // Disallow unwanted copy ctor and assignment operator + FastSourceLineResolver(const FastSourceLineResolver&); + void operator=(const FastSourceLineResolver&); +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_FAST_SOURCE_LINE_RESOLVER_H__ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/processor/memory_region.h b/shared/sentry/external/breakpad/src/google_breakpad/processor/memory_region.h new file mode 100644 index 000000000..30f88df49 --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/processor/memory_region.h @@ -0,0 +1,79 @@ +// 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. + +// memory_region.h: Access to memory regions. +// +// A MemoryRegion provides virtual access to a range of memory. It is an +// abstraction allowing the actual source of memory to be independent of +// methods which need to access a virtual memory space. +// +// Author: Mark Mentovai + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_MEMORY_REGION_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_MEMORY_REGION_H__ + + +#include "google_breakpad/common/breakpad_types.h" + + +namespace google_breakpad { + + +class MemoryRegion { + public: + virtual ~MemoryRegion() {} + + // The base address of this memory region. + virtual uint64_t GetBase() const = 0; + + // The size of this memory region. + virtual uint32_t GetSize() const = 0; + + // Access to data of various sizes within the memory region. address + // is a pointer to read, and it must lie within the memory region as + // defined by its base address and size. The location pointed to by + // value is set to the value at address. Byte-swapping is performed + // if necessary so that the value is appropriate for the running + // program. Returns true on success. Fails and returns false if address + // is out of the region's bounds (after considering the width of value), + // or for other types of errors. + virtual bool GetMemoryAtAddress(uint64_t address, uint8_t* value) const = 0; + virtual bool GetMemoryAtAddress(uint64_t address, uint16_t* value) const = 0; + virtual bool GetMemoryAtAddress(uint64_t address, uint32_t* value) const = 0; + virtual bool GetMemoryAtAddress(uint64_t address, uint64_t* value) const = 0; + + // Print a human-readable representation of the object to stdout. + virtual void Print() const = 0; +}; + + +} // namespace google_breakpad + + +#endif // GOOGLE_BREAKPAD_PROCESSOR_MEMORY_REGION_H__ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/processor/microdump.h b/shared/sentry/external/breakpad/src/google_breakpad/processor/microdump.h new file mode 100644 index 000000000..02ebdcd79 --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/processor/microdump.h @@ -0,0 +1,135 @@ +// 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. + +// microdump.h: A microdump reader. Microdump is a minified variant of a +// minidump (see minidump.h for documentation) which contains the minimum +// amount of information required to get a stack trace for the crashing thread. +// The information contained in a microdump is: +// - the crashing thread stack +// - system information (os type / version) +// - cpu context (state of the registers) +// - list of mmaps + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_MICRODUMP_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_MICRODUMP_H__ + +#include +#include + +#include "common/scoped_ptr.h" +#include "common/using_std_string.h" +#include "google_breakpad/processor/dump_context.h" +#include "google_breakpad/processor/memory_region.h" +#include "google_breakpad/processor/system_info.h" +#include "processor/basic_code_modules.h" + +namespace google_breakpad { + +// MicrodumpModuleList contains all of the loaded code modules for a process +// in the form of MicrodumpModules. It maintains a vector of these modules +// and provides access to a code module corresponding to a specific address. +class MicrodumpModules : public BasicCodeModules { + public: + // Takes over ownership of |module|. + void Add(const CodeModule* module); + + // Enables/disables module address range shrink. + void SetEnableModuleShrink(bool is_enabled); +}; + +// MicrodumpContext carries a CPU-specific context. +// See dump_context.h for documentation. +class MicrodumpContext : public DumpContext { + public: + virtual void SetContextARM(MDRawContextARM* arm); + virtual void SetContextARM64(MDRawContextARM64* arm64); + virtual void SetContextX86(MDRawContextX86* x86); + virtual void SetContextMIPS(MDRawContextMIPS* mips32); + virtual void SetContextMIPS64(MDRawContextMIPS* mips64); +}; + +// This class provides access to microdump memory regions. +// See memory_region.h for documentation. +class MicrodumpMemoryRegion : public MemoryRegion { + public: + MicrodumpMemoryRegion(); + virtual ~MicrodumpMemoryRegion() {} + + // Set this region's address and contents. If we have placed an + // instance of this class in a test fixture class, individual tests + // can use this to provide the region's contents. + void Init(uint64_t base_address, const std::vector& contents); + + virtual uint64_t GetBase() const; + virtual uint32_t GetSize() const; + + virtual bool GetMemoryAtAddress(uint64_t address, uint8_t* value) const; + virtual bool GetMemoryAtAddress(uint64_t address, uint16_t* value) const; + virtual bool GetMemoryAtAddress(uint64_t address, uint32_t* value) const; + virtual bool GetMemoryAtAddress(uint64_t address, uint64_t* value) const; + + // Print a human-readable representation of the object to stdout. + virtual void Print() const; + + private: + // Fetch a little-endian value from ADDRESS in contents_ whose size + // is BYTES, and store it in *VALUE. Returns true on success. + template + bool GetMemoryLittleEndian(uint64_t address, ValueType* value) const; + + uint64_t base_address_; + std::vector contents_; +}; + +// Microdump is the user's interface to a microdump file. It provides access to +// the microdump's context, memory regions and modules. +class Microdump { + public: + explicit Microdump(const string& contents); + virtual ~Microdump() {} + + DumpContext* GetContext() { return context_.get(); } + MicrodumpMemoryRegion* GetMemory() { return stack_region_.get(); } + MicrodumpModules* GetModules() { return modules_.get(); } + SystemInfo* GetSystemInfo() { return system_info_.get(); } + + string GetCrashReason() { return crash_reason_; } + uint64_t GetCrashAddress() { return crash_address_; } + private: + scoped_ptr context_; + scoped_ptr stack_region_; + scoped_ptr modules_; + scoped_ptr system_info_; + string crash_reason_; + uint64_t crash_address_; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_MICRODUMP_H__ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/processor/microdump_processor.h b/shared/sentry/external/breakpad/src/google_breakpad/processor/microdump_processor.h new file mode 100644 index 000000000..60d14a541 --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/processor/microdump_processor.h @@ -0,0 +1,64 @@ +// 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. + +// The processor for microdump (a reduced dump containing only the state of the +// crashing thread). See crbug.com/410294 for more info and design docs. + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_MICRODUMP_PROCESSOR_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_MICRODUMP_PROCESSOR_H__ + +#include + +#include "common/using_std_string.h" +#include "google_breakpad/processor/process_result.h" + +namespace google_breakpad { + +class Microdump; +class ProcessState; +class StackFrameSymbolizer; + +class MicrodumpProcessor { + public: + // Initializes the MicrodumpProcessor with a stack frame symbolizer. + // Does not take ownership of frame_symbolizer, which must NOT be NULL. + explicit MicrodumpProcessor(StackFrameSymbolizer* frame_symbolizer); + + virtual ~MicrodumpProcessor(); + + // Processes the microdump contents and fills process_state with the result. + google_breakpad::ProcessResult Process(Microdump* microdump, + ProcessState* process_state); + private: + StackFrameSymbolizer* frame_symbolizer_; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_MICRODUMP_PROCESSOR_H__ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/processor/minidump.h b/shared/sentry/external/breakpad/src/google_breakpad/processor/minidump.h new file mode 100644 index 000000000..1c40a821a --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/processor/minidump.h @@ -0,0 +1,1349 @@ +// 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. + +// minidump.h: A minidump reader. +// +// The basic structure of this module tracks the structure of the minidump +// file itself. At the top level, a minidump file is represented by a +// Minidump object. Like most other classes in this module, Minidump +// provides a Read method that initializes the object with information from +// the file. Most of the classes in this file are wrappers around the +// "raw" structures found in the minidump file itself, and defined in +// minidump_format.h. For example, each thread is represented by a +// MinidumpThread object, whose parameters are specified in an MDRawThread +// structure. A properly byte-swapped MDRawThread can be obtained from a +// MinidumpThread easily by calling its thread() method. +// +// Most of the module lazily reads only the portion of the minidump file +// necessary to fulfill the user's request. Calling Minidump::Read +// only reads the minidump's directory. The thread list is not read until +// it is needed, and even once it's read, the memory regions for each +// thread's stack aren't read until they're needed. This strategy avoids +// unnecessary file input, and allocating memory for data in which the user +// has no interest. Note that although memory allocations for a typical +// minidump file are not particularly large, it is possible for legitimate +// minidumps to be sizable. A full-memory minidump, for example, contains +// a snapshot of the entire mapped memory space. Even a normal minidump, +// with stack memory only, can be large if, for example, the dump was +// generated in response to a crash that occurred due to an infinite- +// recursion bug that caused the stack's limits to be exceeded. Finally, +// some users of this library will unfortunately find themselves in the +// position of having to process potentially-hostile minidumps that might +// attempt to cause problems by forcing the minidump processor to over- +// allocate memory. +// +// Memory management in this module is based on a strict +// you-don't-own-anything policy. The only object owned by the user is +// the top-level Minidump object, the creation and destruction of which +// must be the user's own responsibility. All other objects obtained +// through interaction with this module are ultimately owned by the +// Minidump object, and will be freed upon the Minidump object's destruction. +// Because memory regions can potentially involve large allocations, a +// FreeMemory method is provided by MinidumpMemoryRegion, allowing the user +// to release data when it is no longer needed. Use of this method is +// optional but recommended. If freed data is later required, it will +// be read back in from the minidump file again. +// +// There is one exception to this memory management policy: +// Minidump::ReadString will return a string object to the user, and the user +// is responsible for its deletion. +// +// Author: Mark Mentovai + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_MINIDUMP_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_MINIDUMP_H__ + +#include + +#ifndef _WIN32 +#include +#endif + +#include +#include +#include +#include + +#include "common/basictypes.h" +#include "common/using_std_string.h" +#include "google_breakpad/processor/code_module.h" +#include "google_breakpad/processor/code_modules.h" +#include "google_breakpad/processor/dump_context.h" +#include "google_breakpad/processor/dump_object.h" +#include "google_breakpad/processor/memory_region.h" +#include "google_breakpad/processor/proc_maps_linux.h" + + +namespace google_breakpad { + + +using std::map; +using std::vector; + + +class Minidump; +template class RangeMap; + + +// MinidumpObject is the base of all Minidump* objects except for Minidump +// itself. +class MinidumpObject : public DumpObject { + public: + virtual ~MinidumpObject() {} + + protected: + explicit MinidumpObject(Minidump* minidump); + + // Refers to the Minidump object that is the ultimate parent of this + // Some MinidumpObjects are owned by other MinidumpObjects, but at the + // root of the ownership tree is always a Minidump. The Minidump object + // is kept here for access to its seeking and reading facilities, and + // for access to data about the minidump file itself, such as whether + // it should be byte-swapped. + Minidump* minidump_; +}; + + +// This class exists primarily to provide a virtual destructor in a base +// class common to all objects that might be stored in +// Minidump::mStreamObjects. Some object types will never be stored in +// Minidump::mStreamObjects, but are represented as streams and adhere to the +// same interface, and may be derived from this class. +class MinidumpStream : public MinidumpObject { + public: + virtual ~MinidumpStream() {} + + protected: + explicit MinidumpStream(Minidump* minidump); + + private: + // Populate (and validate) the MinidumpStream. minidump_ is expected + // to be positioned at the beginning of the stream, so that the next + // read from the minidump will be at the beginning of the stream. + // expected_size should be set to the stream's length as contained in + // the MDRawDirectory record or other identifying record. A class + // that implements MinidumpStream can compare expected_size to a + // known size as an integrity check. + virtual bool Read(uint32_t expected_size) = 0; + + DISALLOW_COPY_AND_ASSIGN(MinidumpStream); +}; + + +// MinidumpContext carries a CPU-specific MDRawContext structure, which +// contains CPU context such as register states. Each thread has its +// own context, and the exception record, if present, also has its own +// context. Note that if the exception record is present, the context it +// refers to is probably what the user wants to use for the exception +// thread, instead of that thread's own context. The exception thread's +// context (as opposed to the exception record's context) will contain +// context for the exception handler (which performs minidump generation), +// and not the context that caused the exception (which is probably what the +// user wants). +class MinidumpContext : public DumpContext { + public: + virtual ~MinidumpContext(); + + protected: + explicit MinidumpContext(Minidump* minidump); + + private: + friend class MinidumpThread; + friend class MinidumpException; + + bool Read(uint32_t expected_size); + + // If the minidump contains a SYSTEM_INFO_STREAM, makes sure that the + // system info stream gives an appropriate CPU type matching the context + // CPU type in context_cpu_type. Returns false if the CPU type does not + // match. Returns true if the CPU type matches or if the minidump does + // not contain a system info stream. + bool CheckAgainstSystemInfo(uint32_t context_cpu_type); + + // Refers to the Minidump object that is the ultimate parent of this + // Some MinidumpObjects are owned by other MinidumpObjects, but at the + // root of the ownership tree is always a Minidump. The Minidump object + // is kept here for access to its seeking and reading facilities, and + // for access to data about the minidump file itself, such as whether + // it should be byte-swapped. + Minidump* minidump_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpContext); +}; + + +// MinidumpMemoryRegion does not wrap any MDRaw structure, and only contains +// a reference to an MDMemoryDescriptor. This object is intended to wrap +// portions of a minidump file that contain memory dumps. In normal +// minidumps, each MinidumpThread owns a MinidumpMemoryRegion corresponding +// to the thread's stack memory. MinidumpMemoryList also gives access to +// memory regions in its list as MinidumpMemoryRegions. This class +// adheres to MemoryRegion so that it may be used as a data provider to +// the Stackwalker family of classes. +class MinidumpMemoryRegion : public MinidumpObject, + public MemoryRegion { + public: + virtual ~MinidumpMemoryRegion(); + + static void set_max_bytes(uint32_t max_bytes) { max_bytes_ = max_bytes; } + static uint32_t max_bytes() { return max_bytes_; } + + // Returns a pointer to the base of the memory region. Returns the + // cached value if available, otherwise, reads the minidump file and + // caches the memory region. + const uint8_t* GetMemory() const; + + // The address of the base of the memory region. + uint64_t GetBase() const; + + // The size, in bytes, of the memory region. + uint32_t GetSize() const; + + // Frees the cached memory region, if cached. + void FreeMemory(); + + // Obtains the value of memory at the pointer specified by address. + bool GetMemoryAtAddress(uint64_t address, uint8_t* value) const; + bool GetMemoryAtAddress(uint64_t address, uint16_t* value) const; + bool GetMemoryAtAddress(uint64_t address, uint32_t* value) const; + bool GetMemoryAtAddress(uint64_t address, uint64_t* value) const; + + // Print a human-readable representation of the object to stdout. + void Print() const; + void SetPrintMode(bool hexdump, unsigned int width); + + protected: + explicit MinidumpMemoryRegion(Minidump* minidump); + + private: + friend class MinidumpThread; + friend class MinidumpMemoryList; + + // Identify the base address and size of the memory region, and the + // location it may be found in the minidump file. + void SetDescriptor(MDMemoryDescriptor* descriptor); + + // Implementation for GetMemoryAtAddress + template bool GetMemoryAtAddressInternal(uint64_t address, + T* value) const; + + // Knobs for controlling display of memory printing. + bool hexdump_; + unsigned int hexdump_width_; + + // The largest memory region that will be read from a minidump. + static uint32_t max_bytes_; + + // Base address and size of the memory region, and its position in the + // minidump file. + MDMemoryDescriptor* descriptor_; + + // Cached memory. + mutable vector* memory_; +}; + + +// MinidumpThread contains information about a thread of execution, +// including a snapshot of the thread's stack and CPU context. For +// the thread that caused an exception, the context carried by +// MinidumpException is probably desired instead of the CPU context +// provided here. +// Note that a MinidumpThread may be valid() even if it does not +// contain a memory region or context. +class MinidumpThread : public MinidumpObject { + public: + virtual ~MinidumpThread(); + + const MDRawThread* thread() const { return valid_ ? &thread_ : NULL; } + // GetMemory may return NULL even if the MinidumpThread is valid, + // if the thread memory cannot be read. + virtual MinidumpMemoryRegion* GetMemory(); + // GetContext may return NULL even if the MinidumpThread is valid. + virtual MinidumpContext* GetContext(); + + // The thread ID is used to determine if a thread is the exception thread, + // so a special getter is provided to retrieve this data from the + // MDRawThread structure. Returns false if the thread ID cannot be + // determined. + virtual bool GetThreadID(uint32_t* thread_id) const; + + // Print a human-readable representation of the object to stdout. + void Print(); + + // Returns the start address of the thread stack memory region. Returns 0 if + // MinidumpThread is invalid. Note that this method can be called even when + // the thread memory cannot be read and GetMemory returns NULL. + virtual uint64_t GetStartOfStackMemoryRange() const; + + protected: + explicit MinidumpThread(Minidump* minidump); + + private: + // These objects are managed by MinidumpThreadList. + friend class MinidumpThreadList; + + // This works like MinidumpStream::Read, but is driven by + // MinidumpThreadList. No size checking is done, because + // MinidumpThreadList handles that directly. + bool Read(); + + MDRawThread thread_; + MinidumpMemoryRegion* memory_; + MinidumpContext* context_; +}; + + +// MinidumpThreadList contains all of the threads (as MinidumpThreads) in +// a process. +class MinidumpThreadList : public MinidumpStream { + public: + virtual ~MinidumpThreadList(); + + static void set_max_threads(uint32_t max_threads) { + max_threads_ = max_threads; + } + static uint32_t max_threads() { return max_threads_; } + + virtual unsigned int thread_count() const { + return valid_ ? thread_count_ : 0; + } + + // Sequential access to threads. + virtual MinidumpThread* GetThreadAtIndex(unsigned int index) const; + + // Random access to threads. + MinidumpThread* GetThreadByID(uint32_t thread_id); + + // Print a human-readable representation of the object to stdout. + void Print(); + + protected: + explicit MinidumpThreadList(Minidump* aMinidump); + + private: + friend class Minidump; + + typedef map IDToThreadMap; + typedef vector MinidumpThreads; + + static const uint32_t kStreamType = MD_THREAD_LIST_STREAM; + + bool Read(uint32_t aExpectedSize) override; + + // The largest number of threads that will be read from a minidump. The + // default is 256. + static uint32_t max_threads_; + + // Access to threads using the thread ID as the key. + IDToThreadMap id_to_thread_map_; + + // The list of threads. + MinidumpThreads* threads_; + uint32_t thread_count_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpThreadList); +}; + + +// MinidumpModule wraps MDRawModule, which contains information about loaded +// code modules. Access is provided to various data referenced indirectly +// by MDRawModule, such as the module's name and a specification for where +// to locate debugging information for the module. +class MinidumpModule : public MinidumpObject, + public CodeModule { + public: + virtual ~MinidumpModule(); + + static void set_max_cv_bytes(uint32_t max_cv_bytes) { + max_cv_bytes_ = max_cv_bytes; + } + static uint32_t max_cv_bytes() { return max_cv_bytes_; } + + static void set_max_misc_bytes(uint32_t max_misc_bytes) { + max_misc_bytes_ = max_misc_bytes; + } + static uint32_t max_misc_bytes() { return max_misc_bytes_; } + + const MDRawModule* module() const { return valid_ ? &module_ : NULL; } + + // CodeModule implementation + virtual uint64_t base_address() const { + return valid_ ? module_.base_of_image : static_cast(-1); + } + virtual uint64_t size() const { return valid_ ? module_.size_of_image : 0; } + virtual string code_file() const; + virtual string code_identifier() const; + virtual string debug_file() const; + virtual string debug_identifier() const; + virtual string version() const; + virtual CodeModule* Copy() const; + virtual bool is_unloaded() const { return false; } + + // Getter and setter for shrink_down_delta. This is used when the address + // range for a module is shrunk down due to address range conflicts with + // other modules. The base_address and size fields are not updated and they + // should always reflect the original values (reported in the minidump). + virtual uint64_t shrink_down_delta() const; + virtual void SetShrinkDownDelta(uint64_t shrink_down_delta); + + // The CodeView record, which contains information to locate the module's + // debugging information (pdb). This is returned as uint8_t* because + // the data can be of types MDCVInfoPDB20* or MDCVInfoPDB70*, or it may be + // of a type unknown to Breakpad, in which case the raw data will still be + // returned but no byte-swapping will have been performed. Check the + // record's signature in the first four bytes to differentiate between + // the various types. Current toolchains generate modules which carry + // MDCVInfoPDB70 by default. Returns a pointer to the CodeView record on + // success, and NULL on failure. On success, the optional |size| argument + // is set to the size of the CodeView record. + const uint8_t* GetCVRecord(uint32_t* size); + + // The miscellaneous debug record, which is obsolete. Current toolchains + // do not generate this type of debugging information (dbg), and this + // field is not expected to be present. Returns a pointer to the debugging + // record on success, and NULL on failure. On success, the optional |size| + // argument is set to the size of the debugging record. + const MDImageDebugMisc* GetMiscRecord(uint32_t* size); + + // Print a human-readable representation of the object to stdout. + void Print(); + + private: + // These objects are managed by MinidumpModuleList. + friend class MinidumpModuleList; + + explicit MinidumpModule(Minidump* minidump); + + // This works like MinidumpStream::Read, but is driven by + // MinidumpModuleList. No size checking is done, because + // MinidumpModuleList handles that directly. + bool Read(); + + // Reads indirectly-referenced data, including the module name, CodeView + // record, and miscellaneous debugging record. This is necessary to allow + // MinidumpModuleList to fully construct MinidumpModule objects without + // requiring seeks to read a contiguous set of MinidumpModule objects. + // All auxiliary data should be available when Read is called, in order to + // allow the CodeModule getters to be const methods. + bool ReadAuxiliaryData(); + + // The largest number of bytes that will be read from a minidump for a + // CodeView record or miscellaneous debugging record, respectively. The + // default for each is 1024. + static uint32_t max_cv_bytes_; + static uint32_t max_misc_bytes_; + + // True after a successful Read. This is different from valid_, which is + // not set true until ReadAuxiliaryData also completes successfully. + // module_valid_ is only used by ReadAuxiliaryData and the functions it + // calls to determine whether the object is ready for auxiliary data to + // be read. + bool module_valid_; + + // True if debug info was read from the module. Certain modules + // may contain debug records in formats we don't support, + // so we can just set this to false to ignore them. + bool has_debug_info_; + + MDRawModule module_; + + // Cached module name. + const string* name_; + + // Cached CodeView record - this is MDCVInfoPDB20 or (likely) + // MDCVInfoPDB70, or possibly something else entirely. Stored as a uint8_t + // because the structure contains a variable-sized string and its exact + // size cannot be known until it is processed. + vector* cv_record_; + + // If cv_record_ is present, cv_record_signature_ contains a copy of the + // CodeView record's first four bytes, for ease of determinining the + // type of structure that cv_record_ contains. + uint32_t cv_record_signature_; + + // Cached MDImageDebugMisc (usually not present), stored as uint8_t + // because the structure contains a variable-sized string and its exact + // size cannot be known until it is processed. + vector* misc_record_; +}; + + +// MinidumpModuleList contains all of the loaded code modules for a process +// in the form of MinidumpModules. It maintains a map of these modules +// so that it may easily provide a code module corresponding to a specific +// address. +class MinidumpModuleList : public MinidumpStream, + public CodeModules { + public: + virtual ~MinidumpModuleList(); + + static void set_max_modules(uint32_t max_modules) { + max_modules_ = max_modules; + } + static uint32_t max_modules() { return max_modules_; } + + // CodeModules implementation. + virtual unsigned int module_count() const { + return valid_ ? module_count_ : 0; + } + virtual const MinidumpModule* GetModuleForAddress(uint64_t address) const; + virtual const MinidumpModule* GetMainModule() const; + virtual const MinidumpModule* GetModuleAtSequence( + unsigned int sequence) const; + virtual const MinidumpModule* GetModuleAtIndex(unsigned int index) const; + virtual const CodeModules* Copy() const; + + // Returns a vector of all modules which address ranges needed to be shrunk + // down due to address range conflicts with other modules. + virtual vector > GetShrunkRangeModules() const; + + // Print a human-readable representation of the object to stdout. + void Print(); + + protected: + explicit MinidumpModuleList(Minidump* minidump); + + private: + friend class Minidump; + + typedef vector MinidumpModules; + + static const uint32_t kStreamType = MD_MODULE_LIST_STREAM; + + bool Read(uint32_t expected_size); + + bool StoreRange(const MinidumpModule& module, + uint64_t base_address, + uint32_t module_index, + uint32_t module_count, + bool is_android); + + // The largest number of modules that will be read from a minidump. The + // default is 1024. + static uint32_t max_modules_; + + // Access to modules using addresses as the key. + RangeMap* range_map_; + + MinidumpModules* modules_; + uint32_t module_count_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpModuleList); +}; + + +// MinidumpMemoryList corresponds to a minidump's MEMORY_LIST_STREAM stream, +// which references the snapshots of all of the memory regions contained +// within the minidump. For a normal minidump, this includes stack memory +// (also referenced by each MinidumpThread, in fact, the MDMemoryDescriptors +// here and in MDRawThread both point to exactly the same data in a +// minidump file, conserving space), as well as a 256-byte snapshot of memory +// surrounding the instruction pointer in the case of an exception. Other +// types of minidumps may contain significantly more memory regions. Full- +// memory minidumps contain all of a process' mapped memory. +class MinidumpMemoryList : public MinidumpStream { + public: + virtual ~MinidumpMemoryList(); + + static void set_max_regions(uint32_t max_regions) { + max_regions_ = max_regions; + } + static uint32_t max_regions() { return max_regions_; } + + unsigned int region_count() const { return valid_ ? region_count_ : 0; } + + // Sequential access to memory regions. + MinidumpMemoryRegion* GetMemoryRegionAtIndex(unsigned int index); + + // Random access to memory regions. Returns the region encompassing + // the address identified by address. + virtual MinidumpMemoryRegion* GetMemoryRegionForAddress(uint64_t address); + + // Print a human-readable representation of the object to stdout. + void Print(); + + private: + friend class Minidump; + friend class MockMinidumpMemoryList; + + typedef vector MemoryDescriptors; + typedef vector MemoryRegions; + + static const uint32_t kStreamType = MD_MEMORY_LIST_STREAM; + + explicit MinidumpMemoryList(Minidump* minidump); + + bool Read(uint32_t expected_size) override; + + // The largest number of memory regions that will be read from a minidump. + // The default is 256. + static uint32_t max_regions_; + + // Access to memory regions using addresses as the key. + RangeMap* range_map_; + + // The list of descriptors. This is maintained separately from the list + // of regions, because MemoryRegion doesn't own its MemoryDescriptor, it + // maintains a pointer to it. descriptors_ provides the storage for this + // purpose. + MemoryDescriptors* descriptors_; + + // The list of regions. + MemoryRegions* regions_; + uint32_t region_count_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpMemoryList); +}; + + +// MinidumpException wraps MDRawExceptionStream, which contains information +// about the exception that caused the minidump to be generated, if the +// minidump was generated in an exception handler called as a result of an +// exception. It also provides access to a MinidumpContext object, which +// contains the CPU context for the exception thread at the time the exception +// occurred. +class MinidumpException : public MinidumpStream { + public: + virtual ~MinidumpException(); + + const MDRawExceptionStream* exception() const { + return valid_ ? &exception_ : NULL; + } + + // The thread ID is used to determine if a thread is the exception thread, + // so a special getter is provided to retrieve this data from the + // MDRawExceptionStream structure. Returns false if the thread ID cannot + // be determined. + bool GetThreadID(uint32_t* thread_id) const; + + MinidumpContext* GetContext(); + + // Print a human-readable representation of the object to stdout. + void Print(); + + private: + friend class Minidump; + + static const uint32_t kStreamType = MD_EXCEPTION_STREAM; + + explicit MinidumpException(Minidump* minidump); + + bool Read(uint32_t expected_size) override; + + MDRawExceptionStream exception_; + MinidumpContext* context_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpException); +}; + +// MinidumpAssertion wraps MDRawAssertionInfo, which contains information +// about an assertion that caused the minidump to be generated. +class MinidumpAssertion : public MinidumpStream { + public: + virtual ~MinidumpAssertion(); + + const MDRawAssertionInfo* assertion() const { + return valid_ ? &assertion_ : NULL; + } + + string expression() const { + return valid_ ? expression_ : ""; + } + + string function() const { + return valid_ ? function_ : ""; + } + + string file() const { + return valid_ ? file_ : ""; + } + + // Print a human-readable representation of the object to stdout. + void Print(); + + private: + friend class Minidump; + + static const uint32_t kStreamType = MD_ASSERTION_INFO_STREAM; + + explicit MinidumpAssertion(Minidump* minidump); + + bool Read(uint32_t expected_size) override; + + MDRawAssertionInfo assertion_; + string expression_; + string function_; + string file_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpAssertion); +}; + + +// MinidumpSystemInfo wraps MDRawSystemInfo and provides information about +// the system on which the minidump was generated. See also MinidumpMiscInfo. +class MinidumpSystemInfo : public MinidumpStream { + public: + virtual ~MinidumpSystemInfo(); + + const MDRawSystemInfo* system_info() const { + return valid_ ? &system_info_ : NULL; + } + + // GetOS and GetCPU return textual representations of the operating system + // and CPU that produced the minidump. Unlike most other Minidump* methods, + // they return string objects, not weak pointers. Defined values for + // GetOS() are "mac", "windows", and "linux". Defined values for GetCPU + // are "x86" and "ppc". These methods return an empty string when their + // values are unknown. + string GetOS(); + string GetCPU(); + + // I don't know what CSD stands for, but this field is documented as + // returning a textual representation of the OS service pack. On other + // platforms, this provides additional information about an OS version + // level beyond major.minor.micro. Returns NULL if unknown. + const string* GetCSDVersion(); + + // If a CPU vendor string can be determined, returns a pointer to it, + // otherwise, returns NULL. CPU vendor strings can be determined from + // x86 CPUs with CPUID 0. + const string* GetCPUVendor(); + + // Print a human-readable representation of the object to stdout. + void Print(); + + protected: + explicit MinidumpSystemInfo(Minidump* minidump); + MDRawSystemInfo system_info_; + + // Textual representation of the OS service pack, for minidumps produced + // by MiniDumpWriteDump on Windows. + const string* csd_version_; + + private: + friend class Minidump; + + static const uint32_t kStreamType = MD_SYSTEM_INFO_STREAM; + + bool Read(uint32_t expected_size) override; + + // A string identifying the CPU vendor, if known. + const string* cpu_vendor_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpSystemInfo); +}; + + +// MinidumpUnloadedModule wraps MDRawUnloadedModule +class MinidumpUnloadedModule : public MinidumpObject, + public CodeModule { + public: + ~MinidumpUnloadedModule() override; + + const MDRawUnloadedModule* module() const { + return valid_ ? &unloaded_module_ : NULL; + } + + // CodeModule implementation + uint64_t base_address() const override { + return valid_ ? unloaded_module_.base_of_image : 0; + } + uint64_t size() const override { + return valid_ ? unloaded_module_.size_of_image : 0; + } + string code_file() const override; + string code_identifier() const override; + string debug_file() const override; + string debug_identifier() const override; + string version() const override; + CodeModule* Copy() const override; + bool is_unloaded() const override { return true; } + uint64_t shrink_down_delta() const override; + void SetShrinkDownDelta(uint64_t shrink_down_delta) override; + + protected: + explicit MinidumpUnloadedModule(Minidump* minidump); + + private: + // These objects are managed by MinidumpUnloadedModuleList + friend class MinidumpUnloadedModuleList; + + // This works like MinidumpStream::Read, but is driven by + // MinidumpUnloadedModuleList. + bool Read(uint32_t expected_size); + + // Reads the module name. This is done separately from Read to + // allow contiguous reading of code modules by MinidumpUnloadedModuleList. + bool ReadAuxiliaryData(); + + // True after a successful Read. This is different from valid_, which + // is not set true until ReadAuxiliaryData also completes successfully. + // module_valid_ is only used by ReadAuxiliaryData and the functions it + // calls to determine whether the object is ready for auxiliary data to + // be read. + bool module_valid_; + + MDRawUnloadedModule unloaded_module_; + + // Cached module name + const string* name_; +}; + + +// MinidumpUnloadedModuleList contains all the unloaded code modules for a +// process in the form of MinidumpUnloadedModules. It maintains a map of +// these modules so that it may easily provide a code module corresponding +// to a specific address. If multiple modules in the list have identical +// ranges, only the first module encountered is recorded in the range map. +class MinidumpUnloadedModuleList : public MinidumpStream, + public CodeModules { + public: + ~MinidumpUnloadedModuleList() override; + + static void set_max_modules(uint32_t max_modules) { + max_modules_ = max_modules; + } + static uint32_t max_modules() { return max_modules_; } + + // CodeModules implementation. + unsigned int module_count() const override { + return valid_ ? module_count_ : 0; + } + const MinidumpUnloadedModule* + GetModuleForAddress(uint64_t address) const override; + const MinidumpUnloadedModule* GetMainModule() const override; + const MinidumpUnloadedModule* + GetModuleAtSequence(unsigned int sequence) const override; + const MinidumpUnloadedModule* + GetModuleAtIndex(unsigned int index) const override; + const CodeModules* Copy() const override; + vector> GetShrunkRangeModules() const override; + + protected: + explicit MinidumpUnloadedModuleList(Minidump* minidump_); + + private: + friend class Minidump; + + typedef vector MinidumpUnloadedModules; + + static const uint32_t kStreamType = MD_UNLOADED_MODULE_LIST_STREAM; + + bool Read(uint32_t expected_size_) override; + + // The largest number of modules that will be read from a minidump. The + // default is 1024. + static uint32_t max_modules_; + + // Access to module indices using addresses as the key. + RangeMap* range_map_; + + MinidumpUnloadedModules* unloaded_modules_; + uint32_t module_count_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpUnloadedModuleList); +}; + + +// MinidumpMiscInfo wraps MDRawMiscInfo and provides information about +// the process that generated the minidump, and optionally additional system +// information. See also MinidumpSystemInfo. +class MinidumpMiscInfo : public MinidumpStream { + public: + const MDRawMiscInfo* misc_info() const { + return valid_ ? &misc_info_ : NULL; + } + + // Print a human-readable representation of the object to stdout. + void Print(); + + private: + friend class Minidump; + friend class TestMinidumpMiscInfo; + + static const uint32_t kStreamType = MD_MISC_INFO_STREAM; + + explicit MinidumpMiscInfo(Minidump* minidump_); + + bool Read(uint32_t expected_size_) override; + + MDRawMiscInfo misc_info_; + + // Populated by Read. Contains the converted strings from the corresponding + // UTF-16 fields in misc_info_ + string standard_name_; + string daylight_name_; + string build_string_; + string dbg_bld_str_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpMiscInfo); +}; + + +// MinidumpBreakpadInfo wraps MDRawBreakpadInfo, which is an optional stream in +// a minidump that provides additional information about the process state +// at the time the minidump was generated. +class MinidumpBreakpadInfo : public MinidumpStream { + public: + const MDRawBreakpadInfo* breakpad_info() const { + return valid_ ? &breakpad_info_ : NULL; + } + + // These thread IDs are used to determine if threads deserve special + // treatment, so special getters are provided to retrieve this data from + // the MDRawBreakpadInfo structure. The getters return false if the thread + // IDs cannot be determined. + bool GetDumpThreadID(uint32_t* thread_id) const; + bool GetRequestingThreadID(uint32_t* thread_id) const; + + // Print a human-readable representation of the object to stdout. + void Print(); + + private: + friend class Minidump; + + static const uint32_t kStreamType = MD_BREAKPAD_INFO_STREAM; + + explicit MinidumpBreakpadInfo(Minidump* minidump_); + + bool Read(uint32_t expected_size_) override; + + MDRawBreakpadInfo breakpad_info_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpBreakpadInfo); +}; + +// MinidumpMemoryInfo wraps MDRawMemoryInfo, which provides information +// about mapped memory regions in a process, including their ranges +// and protection. +class MinidumpMemoryInfo : public MinidumpObject { + public: + const MDRawMemoryInfo* info() const { return valid_ ? &memory_info_ : NULL; } + + // The address of the base of the memory region. + uint64_t GetBase() const { return valid_ ? memory_info_.base_address : 0; } + + // The size, in bytes, of the memory region. + uint64_t GetSize() const { return valid_ ? memory_info_.region_size : 0; } + + // Return true if the memory protection allows execution. + bool IsExecutable() const; + + // Return true if the memory protection allows writing. + bool IsWritable() const; + + // Print a human-readable representation of the object to stdout. + void Print(); + + private: + // These objects are managed by MinidumpMemoryInfoList. + friend class MinidumpMemoryInfoList; + + explicit MinidumpMemoryInfo(Minidump* minidump_); + + // This works like MinidumpStream::Read, but is driven by + // MinidumpMemoryInfoList. No size checking is done, because + // MinidumpMemoryInfoList handles that directly. + bool Read(); + + MDRawMemoryInfo memory_info_; +}; + +// MinidumpMemoryInfoList contains a list of information about +// mapped memory regions for a process in the form of MDRawMemoryInfo. +// It maintains a map of these structures so that it may easily provide +// info corresponding to a specific address. +class MinidumpMemoryInfoList : public MinidumpStream { + public: + virtual ~MinidumpMemoryInfoList(); + + unsigned int info_count() const { return valid_ ? info_count_ : 0; } + + const MinidumpMemoryInfo* GetMemoryInfoForAddress(uint64_t address) const; + const MinidumpMemoryInfo* GetMemoryInfoAtIndex(unsigned int index) const; + + // Print a human-readable representation of the object to stdout. + void Print(); + + private: + friend class Minidump; + + typedef vector MinidumpMemoryInfos; + + static const uint32_t kStreamType = MD_MEMORY_INFO_LIST_STREAM; + + explicit MinidumpMemoryInfoList(Minidump* minidump_); + + bool Read(uint32_t expected_size) override; + + // Access to memory info using addresses as the key. + RangeMap* range_map_; + + MinidumpMemoryInfos* infos_; + uint32_t info_count_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpMemoryInfoList); +}; + +// MinidumpLinuxMaps wraps information about a single mapped memory region +// from /proc/self/maps. +class MinidumpLinuxMaps : public MinidumpObject { + public: + // The memory address of the base of the mapped region. + uint64_t GetBase() const { return valid_ ? region_.start : 0; } + // The size of the mapped region. + uint64_t GetSize() const { return valid_ ? region_.end - region_.start : 0; } + + // The permissions of the mapped region. + bool IsReadable() const { + return valid_ ? region_.permissions & MappedMemoryRegion::READ : false; + } + bool IsWriteable() const { + return valid_ ? region_.permissions & MappedMemoryRegion::WRITE : false; + } + bool IsExecutable() const { + return valid_ ? region_.permissions & MappedMemoryRegion::EXECUTE : false; + } + bool IsPrivate() const { + return valid_ ? region_.permissions & MappedMemoryRegion::PRIVATE : false; + } + + // The offset of the mapped region. + uint64_t GetOffset() const { return valid_ ? region_.offset : 0; } + + // The major device number. + uint8_t GetMajorDevice() const { return valid_ ? region_.major_device : 0; } + // The minor device number. + uint8_t GetMinorDevice() const { return valid_ ? region_.minor_device : 0; } + + // The inode of the mapped region. + uint64_t GetInode() const { return valid_ ? region_.inode : 0; } + + // The pathname of the mapped region. + const string GetPathname() const { return valid_ ? region_.path : ""; } + + // Print the contents of this mapping. + void Print() const; + + private: + // These objects are managed by MinidumpLinuxMapsList. + friend class MinidumpLinuxMapsList; + + // This caller owns the pointer. + explicit MinidumpLinuxMaps(Minidump* minidump); + + // The memory region struct that this class wraps. + MappedMemoryRegion region_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpLinuxMaps); +}; + +// MinidumpLinuxMapsList corresponds to the Linux-exclusive MD_LINUX_MAPS +// stream, which contains the contents of /prod/self/maps, which contains +// the mapped memory regions and their access permissions. +class MinidumpLinuxMapsList : public MinidumpStream { + public: + virtual ~MinidumpLinuxMapsList(); + + // Get number of mappings. + unsigned int get_maps_count() const { return valid_ ? maps_count_ : 0; } + + // Get mapping at the given memory address. The caller owns the pointer. + const MinidumpLinuxMaps* GetLinuxMapsForAddress(uint64_t address) const; + // Get mapping at the given index. The caller owns the pointer. + const MinidumpLinuxMaps* GetLinuxMapsAtIndex(unsigned int index) const; + + // Print the contents of /proc/self/maps to stdout. + void Print() const; + + private: + friend class Minidump; + + typedef vector MinidumpLinuxMappings; + + static const uint32_t kStreamType = MD_LINUX_MAPS; + + // The caller owns the pointer. + explicit MinidumpLinuxMapsList(Minidump* minidump); + + // Read and load the contents of the process mapping data. + // The stream should have data in the form of /proc/self/maps. + // This method returns whether the stream was read successfully. + bool Read(uint32_t expected_size) override; + + // The list of individual mappings. + MinidumpLinuxMappings* maps_; + // The number of mappings. + uint32_t maps_count_; + + DISALLOW_COPY_AND_ASSIGN(MinidumpLinuxMapsList); +}; + +// MinidumpCrashpadInfo wraps MDRawCrashpadInfo, which is an optional stream in +// a minidump that provides additional information about the process state +// at the time the minidump was generated. +class MinidumpCrashpadInfo : public MinidumpStream { + public: + const MDRawCrashpadInfo* crashpad_info() const { + return valid_ ? &crashpad_info_ : NULL; + } + + // Print a human-readable representation of the object to stdout. + void Print(); + + private: + friend class Minidump; + + static const uint32_t kStreamType = MD_CRASHPAD_INFO_STREAM; + + explicit MinidumpCrashpadInfo(Minidump* minidump_); + + bool Read(uint32_t expected_size); + + MDRawCrashpadInfo crashpad_info_; + std::vector module_crashpad_info_links_; + std::vector module_crashpad_info_; + std::vector> module_crashpad_info_list_annotations_; + std::vector> + module_crashpad_info_simple_annotations_; + std::map simple_annotations_; +}; + + +// Minidump is the user's interface to a minidump file. It wraps MDRawHeader +// and provides access to the minidump's top-level stream directory. +class Minidump { + public: + // path is the pathname of a file containing the minidump. + explicit Minidump(const string& path, + bool hexdump=false, + unsigned int hexdump_width=16); + // input is an istream wrapping minidump data. Minidump holds a + // weak pointer to input, and the caller must ensure that the stream + // is valid as long as the Minidump object is. + explicit Minidump(std::istream& input); + + virtual ~Minidump(); + + // path may be empty if the minidump was not opened from a file + virtual string path() const { + return path_; + } + static void set_max_streams(uint32_t max_streams) { + max_streams_ = max_streams; + } + static uint32_t max_streams() { return max_streams_; } + + static void set_max_string_length(uint32_t max_string_length) { + max_string_length_ = max_string_length; + } + static uint32_t max_string_length() { return max_string_length_; } + + virtual const MDRawHeader* header() const { return valid_ ? &header_ : NULL; } + + // Reads the CPU information from the system info stream and generates the + // appropriate CPU flags. The returned context_cpu_flags are the same as + // if the CPU type bits were set in the context_flags of a context record. + // On success, context_cpu_flags will have the flags that identify the CPU. + // If a system info stream is missing, context_cpu_flags will be 0. + // Returns true if the current position in the stream was not changed. + // Returns false when the current location in the stream was changed and the + // attempt to restore the original position failed. + bool GetContextCPUFlagsFromSystemInfo(uint32_t* context_cpu_flags); + + // Reads the minidump file's header and top-level stream directory. + // The minidump is expected to be positioned at the beginning of the + // header. Read() sets up the stream list and map, and validates the + // Minidump object. + virtual bool Read(); + + // The next set of methods are stubs that call GetStream. They exist to + // force code generation of the templatized API within the module, and + // to avoid exposing an ugly API (GetStream needs to accept a garbage + // parameter). + virtual MinidumpThreadList* GetThreadList(); + virtual MinidumpModuleList* GetModuleList(); + virtual MinidumpMemoryList* GetMemoryList(); + virtual MinidumpException* GetException(); + virtual MinidumpAssertion* GetAssertion(); + virtual MinidumpSystemInfo* GetSystemInfo(); + virtual MinidumpUnloadedModuleList* GetUnloadedModuleList(); + virtual MinidumpMiscInfo* GetMiscInfo(); + virtual MinidumpBreakpadInfo* GetBreakpadInfo(); + virtual MinidumpMemoryInfoList* GetMemoryInfoList(); + MinidumpCrashpadInfo* GetCrashpadInfo(); + + // The next method also calls GetStream, but is exclusive for Linux dumps. + virtual MinidumpLinuxMapsList* GetLinuxMapsList(); + + // The next set of methods are provided for users who wish to access + // data in minidump files directly, while leveraging the rest of + // this class and related classes to handle the basic minidump + // structure and known stream types. + + unsigned int GetDirectoryEntryCount() const { + return valid_ ? header_.stream_count : 0; + } + const MDRawDirectory* GetDirectoryEntryAtIndex(unsigned int index) const; + + // The next 2 methods are lower-level I/O routines. They use fd_. + + // Reads count bytes from the minidump at the current position into + // the storage area pointed to by bytes. bytes must be of sufficient + // size. After the read, the file position is advanced by count. + bool ReadBytes(void* bytes, size_t count); + + // Sets the position of the minidump file to offset. + bool SeekSet(off_t offset); + + // Returns the current position of the minidump file. + off_t Tell(); + + // Medium-level I/O routines. + + // ReadString returns a string which is owned by the caller! offset + // specifies the offset that a length-encoded string is stored at in the + // minidump file. + string* ReadString(off_t offset); + + bool ReadUTF8String(off_t offset, string* string_utf8); + + bool ReadStringList(off_t offset, std::vector* string_list); + + bool ReadSimpleStringDictionary( + off_t offset, + std::map* simple_string_dictionary); + + // SeekToStreamType positions the file at the beginning of a stream + // identified by stream_type, and informs the caller of the stream's + // length by setting *stream_length. Because stream_map maps each stream + // type to only one stream in the file, this might mislead the user into + // thinking that the stream that this seeks to is the only stream with + // type stream_type. That can't happen for streams that these classes + // deal with directly, because they're only supposed to be present in the + // file singly, and that's verified when stream_map_ is built. Users who + // are looking for other stream types should be aware of this + // possibility, and consider using GetDirectoryEntryAtIndex (possibly + // with GetDirectoryEntryCount) if expecting multiple streams of the same + // type in a single minidump file. + bool SeekToStreamType(uint32_t stream_type, uint32_t* stream_length); + + bool swap() const { return valid_ ? swap_ : false; } + + bool is_big_endian() const { return valid_ ? is_big_endian_ : false; } + + // Print a human-readable representation of the object to stdout. + void Print(); + + // Is the OS Android. + bool IsAndroid(); + + // Determines the platform where the minidump was produced. |platform| is + // valid iff this method returns true. + bool GetPlatform(MDOSPlatform* platform); + + // Get current hexdump display settings. + unsigned int HexdumpMode() const { return hexdump_ ? hexdump_width_ : 0; } + + private: + // MinidumpStreamInfo is used in the MinidumpStreamMap. It lets + // the Minidump object locate interesting streams quickly, and + // provides a convenient place to stash MinidumpStream objects. + struct MinidumpStreamInfo { + MinidumpStreamInfo() : stream_index(0), stream(NULL) {} + ~MinidumpStreamInfo() { delete stream; } + + // Index into the MinidumpDirectoryEntries vector + unsigned int stream_index; + + // Pointer to the stream if cached, or NULL if not yet populated + MinidumpStream* stream; + }; + + typedef vector MinidumpDirectoryEntries; + typedef map MinidumpStreamMap; + + template T* GetStream(T** stream); + + // Opens the minidump file, or if already open, seeks to the beginning. + bool Open(); + + // The largest number of top-level streams that will be read from a minidump. + // Note that streams are only read (and only consume memory) as needed, + // when directed by the caller. The default is 128. + static uint32_t max_streams_; + + // The maximum length of a UTF-16 string that will be read from a minidump + // in 16-bit words. The default is 1024. UTF-16 strings are converted + // to UTF-8 when stored in memory, and each UTF-16 word will be represented + // by as many as 3 bytes in UTF-8. + static unsigned int max_string_length_; + + MDRawHeader header_; + + // The list of streams. + MinidumpDirectoryEntries* directory_; + + // Access to streams using the stream type as the key. + MinidumpStreamMap* stream_map_; + + // The pathname of the minidump file to process, set in the constructor. + // This may be empty if the minidump was opened directly from a stream. + const string path_; + + // The stream for all file I/O. Used by ReadBytes and SeekSet. + // Set based on the path in Open, or directly in the constructor. + std::istream* stream_; + + // swap_ is true if the minidump file should be byte-swapped. If the + // minidump was produced by a CPU that is other-endian than the CPU + // processing the minidump, this will be true. If the two CPUs are + // same-endian, this will be false. + bool swap_; + + // true if the minidump was produced by a big-endian cpu. + bool is_big_endian_; + + // Validity of the Minidump structure, false immediately after + // construction or after a failed Read(); true following a successful + // Read(). + bool valid_; + + // Knobs for controlling display of memory printing. + bool hexdump_; + unsigned int hexdump_width_; + + DISALLOW_COPY_AND_ASSIGN(Minidump); +}; + + +} // namespace google_breakpad + + +#endif // GOOGLE_BREAKPAD_PROCESSOR_MINIDUMP_H__ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/processor/minidump_processor.h b/shared/sentry/external/breakpad/src/google_breakpad/processor/minidump_processor.h new file mode 100644 index 000000000..414050e97 --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/processor/minidump_processor.h @@ -0,0 +1,147 @@ +// 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 GOOGLE_BREAKPAD_PROCESSOR_MINIDUMP_PROCESSOR_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_MINIDUMP_PROCESSOR_H__ + +#include +#include + +#include "common/using_std_string.h" +#include "google_breakpad/common/breakpad_types.h" +#include "google_breakpad/processor/process_result.h" + +namespace google_breakpad { + +class Minidump; +class ProcessState; +class StackFrameSymbolizer; +class SourceLineResolverInterface; +class SymbolSupplier; +struct SystemInfo; + +class MinidumpProcessor { + public: + // Initializes this MinidumpProcessor. supplier should be an + // implementation of the SymbolSupplier abstract base class. + MinidumpProcessor(SymbolSupplier* supplier, + SourceLineResolverInterface* resolver); + + // Initializes the MinidumpProcessor with the option of + // enabling the exploitability framework to analyze dumps + // for probable security relevance. + MinidumpProcessor(SymbolSupplier* supplier, + SourceLineResolverInterface* resolver, + bool enable_exploitability); + + // Initializes the MinidumpProcessor with source line resolver helper, and + // the option of enabling the exploitability framework to analyze dumps + // for probable security relevance. + // Does not take ownership of resolver_helper, which must NOT be NULL. + MinidumpProcessor(StackFrameSymbolizer* stack_frame_symbolizer, + bool enable_exploitability); + + ~MinidumpProcessor(); + + // Processes the minidump file and fills process_state with the result. + ProcessResult Process(const string& minidump_file, + ProcessState* process_state); + + // Processes the minidump structure and fills process_state with the + // result. + ProcessResult Process(Minidump* minidump, + ProcessState* process_state); + // Populates the cpu_* fields of the |info| parameter with textual + // representations of the CPU type that the minidump in |dump| was + // produced on. Returns false if this information is not available in + // the minidump. + static bool GetCPUInfo(Minidump* dump, SystemInfo* info); + + // Populates the os_* fields of the |info| parameter with textual + // representations of the operating system that the minidump in |dump| + // was produced on. Returns false if this information is not available in + // the minidump. + static bool GetOSInfo(Minidump* dump, SystemInfo* info); + + // Populates the |process_create_time| parameter with the create time of the + // crashed process. Returns false if this information is not available in + // the minidump |dump|. + static bool GetProcessCreateTime(Minidump* dump, + uint32_t* process_create_time); + + // Returns a textual representation of the reason that a crash occurred, + // if the minidump in dump was produced as a result of a crash. Returns + // an empty string if this information cannot be determined. If address + // is non-NULL, it will be set to contain the address that caused the + // exception, if this information is available. This will be a code + // address when the crash was caused by problems such as illegal + // instructions or divisions by zero, or a data address when the crash + // was caused by a memory access violation. + static string GetCrashReason(Minidump* dump, uint64_t* address); + + // This function returns true if the passed-in error code is + // something unrecoverable(i.e. retry should not happen). For + // instance, if the minidump is corrupt, then it makes no sense to + // retry as we won't be able to glean additional information. + // However, as an example of the other case, the symbol supplier can + // return an error code indicating it was 'interrupted', which can + // happen of the symbols are fetched from a remote store, and a + // retry might be successful later on. + // You should not call this method with PROCESS_OK! Test for + // that separately before calling this. + static bool IsErrorUnrecoverable(ProcessResult p) { + assert(p != PROCESS_OK); + return (p != PROCESS_SYMBOL_SUPPLIER_INTERRUPTED); + } + + // Returns a textual representation of an assertion included + // in the minidump. Returns an empty string if this information + // does not exist or cannot be determined. + static string GetAssertion(Minidump* dump); + + void set_enable_objdump(bool enabled) { enable_objdump_ = enabled; } + + private: + StackFrameSymbolizer* frame_symbolizer_; + // Indicate whether resolver_helper_ is owned by this instance. + bool own_frame_symbolizer_; + + // This flag enables the exploitability scanner which attempts to + // guess how likely it is that the crash represents an exploitable + // memory corruption issue. + bool enable_exploitability_; + + // This flag permits the exploitability scanner to shell out to objdump + // for purposes of disassembly. + bool enable_objdump_; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_MINIDUMP_PROCESSOR_H__ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/processor/proc_maps_linux.h b/shared/sentry/external/breakpad/src/google_breakpad/processor/proc_maps_linux.h new file mode 100644 index 000000000..3045daa5f --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/processor/proc_maps_linux.h @@ -0,0 +1,60 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_DEBUG_PROC_MAPS_LINUX_H_ +#define BASE_DEBUG_PROC_MAPS_LINUX_H_ + +#include +#include + +#include "common/using_std_string.h" +#include "google_breakpad/common/breakpad_types.h" + +namespace google_breakpad { + +// Describes a region of mapped memory and the path of the file mapped. +struct MappedMemoryRegion { + enum Permission { + READ = 1 << 0, + WRITE = 1 << 1, + EXECUTE = 1 << 2, + PRIVATE = 1 << 3, // If set, region is private, otherwise it is shared. + }; + + // The address range [start,end) of mapped memory. + uint64_t start; + uint64_t end; + + // Byte offset into |path| of the range mapped into memory. + uint64_t offset; + + // Bitmask of read/write/execute/private/shared permissions. + uint8_t permissions; + + // Major and minor devices. + uint8_t major_device; + uint8_t minor_device; + + // Value of the inode. + uint64_t inode; + + // Name of the file mapped into memory. + // + // NOTE: path names aren't guaranteed to point at valid files. For example, + // "[heap]" and "[stack]" are used to represent the location of the process' + // heap and stack, respectively. + string path; + + // The line from /proc//maps that this struct represents. + string line; +}; + +// Parses /proc//maps input data and stores in |regions|. Returns true +// and updates |regions| if and only if all of |input| was successfully parsed. +bool ParseProcMaps(const string& input, + std::vector* regions); + +} // namespace google_breakpad + +#endif // BASE_DEBUG_PROC_MAPS_LINUX_H_ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/processor/process_result.h b/shared/sentry/external/breakpad/src/google_breakpad/processor/process_result.h new file mode 100644 index 000000000..15c7213e9 --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/processor/process_result.h @@ -0,0 +1,66 @@ +// 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_PROCESSOR_PROCESS_RESULT_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_PROCESS_RESULT_H__ + +namespace google_breakpad { + +// Return type for MinidumpProcessor or MicrodumpProcessor's Process() +enum ProcessResult { + PROCESS_OK, // The dump was processed + // successfully. + + PROCESS_ERROR_MINIDUMP_NOT_FOUND, // The minidump file was not + // found. + + PROCESS_ERROR_NO_MINIDUMP_HEADER, // The minidump file had no + // header. + + PROCESS_ERROR_NO_THREAD_LIST, // The minidump file has no + // thread list. + + PROCESS_ERROR_GETTING_THREAD, // There was an error getting one + // thread's data from th dump. + + PROCESS_ERROR_GETTING_THREAD_ID, // There was an error getting a + // thread id from the thread's + // data. + + PROCESS_ERROR_DUPLICATE_REQUESTING_THREADS, // There was more than one + // requesting thread. + + PROCESS_SYMBOL_SUPPLIER_INTERRUPTED // The dump processing was + // interrupted by the + // SymbolSupplier(not fatal). +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_PROCESS_RESULT_H__ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/processor/process_state.h b/shared/sentry/external/breakpad/src/google_breakpad/processor/process_state.h new file mode 100644 index 000000000..9bc44c450 --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/processor/process_state.h @@ -0,0 +1,208 @@ +// 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. + +// process_state.h: A snapshot of a process, in a fully-digested state. +// +// Author: Mark Mentovai + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_PROCESS_STATE_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_PROCESS_STATE_H__ + +#include +#include + +#include "common/using_std_string.h" +#include "google_breakpad/common/breakpad_types.h" +#include "google_breakpad/processor/code_modules.h" +#include "google_breakpad/processor/exception_record.h" +#include "google_breakpad/processor/minidump.h" +#include "google_breakpad/processor/system_info.h" +#include "processor/linked_ptr.h" + +namespace google_breakpad { + +using std::vector; + +class CallStack; +class CodeModules; + +enum ExploitabilityRating { + EXPLOITABILITY_HIGH, // The crash likely represents + // a exploitable memory corruption + // vulnerability. + + EXPLOITABILITY_MEDIUM, // The crash appears to corrupt + // memory in a way which may be + // exploitable in some situations. + + EXPLOITABLITY_MEDIUM = EXPLOITABILITY_MEDIUM, // an old misspelling + + EXPLOITABILITY_LOW, // The crash either does not corrupt + // memory directly or control over + // the affected data is limited. The + // issue may still be exploitable + // on certain platforms or situations. + + EXPLOITABILITY_INTERESTING, // The crash does not appear to be + // directly exploitable. However it + // represents a condition which should + // be further analyzed. + + EXPLOITABILITY_NONE, // The crash does not appear to represent + // an exploitable condition. + + EXPLOITABILITY_NOT_ANALYZED, // The crash was not analyzed for + // exploitability because the engine + // was disabled. + + EXPLOITABILITY_ERR_NOENGINE, // The supplied minidump's platform does + // not have a exploitability engine + // associated with it. + + EXPLOITABILITY_ERR_PROCESSING // An error occured within the + // exploitability engine and no rating + // was calculated. +}; + +class ProcessState { + public: + ProcessState() : modules_(NULL), unloaded_modules_(NULL) { Clear(); } + ~ProcessState(); + + // Resets the ProcessState to its default values + void Clear(); + + // Accessors. See the data declarations below. + uint32_t time_date_stamp() const { return time_date_stamp_; } + uint32_t process_create_time() const { return process_create_time_; } + bool crashed() const { return crashed_; } + string crash_reason() const { return crash_reason_; } + uint64_t crash_address() const { return crash_address_; } + string assertion() const { return assertion_; } + int requesting_thread() const { return requesting_thread_; } + const ExceptionRecord* exception_record() const { return &exception_record_; } + const vector* threads() const { return &threads_; } + const vector* thread_memory_regions() const { + return &thread_memory_regions_; + } + const SystemInfo* system_info() const { return &system_info_; } + const CodeModules* modules() const { return modules_; } + const CodeModules* unloaded_modules() const { return unloaded_modules_; } + const vector >* shrunk_range_modules() const { + return &shrunk_range_modules_; + } + const vector* modules_without_symbols() const { + return &modules_without_symbols_; + } + const vector* modules_with_corrupt_symbols() const { + return &modules_with_corrupt_symbols_; + } + ExploitabilityRating exploitability() const { return exploitability_; } + + private: + // MinidumpProcessor and MicrodumpProcessor are responsible for building + // ProcessState objects. + friend class MinidumpProcessor; + friend class MicrodumpProcessor; + + // The time-date stamp of the minidump (time_t format) + uint32_t time_date_stamp_; + + // The time-date stamp when the process was created (time_t format) + uint32_t process_create_time_; + + // True if the process crashed, false if the dump was produced outside + // of an exception handler. + bool crashed_; + + // If the process crashed, the type of crash. OS- and possibly CPU- + // specific. For example, "EXCEPTION_ACCESS_VIOLATION" (Windows), + // "EXC_BAD_ACCESS / KERN_INVALID_ADDRESS" (Mac OS X), "SIGSEGV" + // (other Unix). + string crash_reason_; + + // If the process crashed, and if crash_reason implicates memory, + // the memory address that caused the crash. For data access errors, + // this will be the data address that caused the fault. For code errors, + // this will be the address of the instruction that caused the fault. + uint64_t crash_address_; + + // If there was an assertion that was hit, a textual representation + // of that assertion, possibly including the file and line at which + // it occurred. + string assertion_; + + // The index of the thread that requested a dump be written in the + // threads vector. If a dump was produced as a result of a crash, this + // will point to the thread that crashed. If the dump was produced as + // by user code without crashing, and the dump contains extended Breakpad + // information, this will point to the thread that requested the dump. + // If the dump was not produced as a result of an exception and no + // extended Breakpad information is present, this field will be set to -1, + // indicating that the dump thread is not available. + int requesting_thread_; + + // Exception record details: code, flags, address, parameters. + ExceptionRecord exception_record_; + + // Stacks for each thread (except possibly the exception handler + // thread) at the time of the crash. + vector threads_; + vector thread_memory_regions_; + + // OS and CPU information. + SystemInfo system_info_; + + // The modules that were loaded into the process represented by the + // ProcessState. + const CodeModules *modules_; + + // The modules that have been unloaded from the process represented by the + // ProcessState. + const CodeModules *unloaded_modules_; + + // The modules which virtual address ranges were shrunk down due to + // virtual address conflicts. + vector > shrunk_range_modules_; + + // The modules that didn't have symbols when the report was processed. + vector modules_without_symbols_; + + // The modules that had corrupt symbols when the report was processed. + vector modules_with_corrupt_symbols_; + + // The exploitability rating as determined by the exploitability + // engine. When the exploitability engine is not enabled this + // defaults to EXPLOITABILITY_NOT_ANALYZED. + ExploitabilityRating exploitability_; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_PROCESS_STATE_H__ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/processor/source_line_resolver_base.h b/shared/sentry/external/breakpad/src/google_breakpad/processor/source_line_resolver_base.h new file mode 100644 index 000000000..ba68798ab --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/processor/source_line_resolver_base.h @@ -0,0 +1,133 @@ +// 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. +// +// source_line_resolver_base.h: SourceLineResolverBase, an (incomplete) +// implementation of SourceLineResolverInterface. It serves as a common base +// class for concrete implementations: FastSourceLineResolver and +// BasicSourceLineResolver. It is designed for refactoring that removes +// code redundancy in the two concrete source line resolver classes. +// +// See "google_breakpad/processor/source_line_resolver_interface.h" for more +// documentation. + +// Author: Siyang Xie (lambxsy@google.com) + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_SOURCE_LINE_RESOLVER_BASE_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_SOURCE_LINE_RESOLVER_BASE_H__ + +#include +#include +#include +#include + +#include "google_breakpad/processor/source_line_resolver_interface.h" + +namespace google_breakpad { + +using std::map; +using std::set; + +// Forward declaration. +// ModuleFactory is a simple factory interface for creating a Module instance +// at run-time. +class ModuleFactory; + +class SourceLineResolverBase : public SourceLineResolverInterface { + public: + // Read the symbol_data from a file with given file_name. + // The part of code was originally in BasicSourceLineResolver::Module's + // LoadMap() method. + // Place dynamically allocated heap buffer in symbol_data. Caller has the + // ownership of the buffer, and should call delete [] to free the buffer. + static bool ReadSymbolFile(const string& file_name, + char** symbol_data, + size_t* symbol_data_size); + + protected: + // Users are not allowed create SourceLineResolverBase instance directly. + SourceLineResolverBase(ModuleFactory* module_factory); + virtual ~SourceLineResolverBase(); + + // Virtual methods inherited from SourceLineResolverInterface. + virtual bool LoadModule(const CodeModule* module, const string& map_file); + virtual bool LoadModuleUsingMapBuffer(const CodeModule* module, + const string& map_buffer); + virtual bool LoadModuleUsingMemoryBuffer(const CodeModule* module, + char* memory_buffer, + size_t memory_buffer_size); + virtual bool ShouldDeleteMemoryBufferAfterLoadModule(); + virtual void UnloadModule(const CodeModule* module); + virtual bool HasModule(const CodeModule* module); + virtual bool IsModuleCorrupt(const CodeModule* module); + virtual void FillSourceLineInfo( + StackFrame* frame, + std::deque>* inlined_frames); + virtual WindowsFrameInfo* FindWindowsFrameInfo(const StackFrame* frame); + virtual CFIFrameInfo* FindCFIFrameInfo(const StackFrame* frame); + + // Nested structs and classes. + struct InlineOrigin; + struct Inline; + struct Line; + struct Function; + struct PublicSymbol; + struct CompareString { + bool operator()(const string& s1, const string& s2) const; + }; + // Module is an interface for an in-memory symbol file. + class Module; + class AutoFileCloser; + + // All of the modules that are loaded. + typedef map ModuleMap; + ModuleMap* modules_; + + // The loaded modules that were detecting to be corrupt during load. + typedef set ModuleSet; + ModuleSet* corrupt_modules_; + + // All of heap-allocated buffers that are owned locally by resolver. + typedef std::map MemoryMap; + MemoryMap* memory_buffers_; + + // Creates a concrete module at run-time. + ModuleFactory* module_factory_; + + private: + // ModuleFactory needs to have access to protected type Module. + friend class ModuleFactory; + + // Disallow unwanted copy ctor and assignment operator + SourceLineResolverBase(const SourceLineResolverBase&); + void operator=(const SourceLineResolverBase&); +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_SOURCE_LINE_RESOLVER_BASE_H__ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/processor/source_line_resolver_interface.h b/shared/sentry/external/breakpad/src/google_breakpad/processor/source_line_resolver_interface.h new file mode 100644 index 000000000..2614f65e0 --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/processor/source_line_resolver_interface.h @@ -0,0 +1,124 @@ +// -*- 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. + +// Abstract interface to return function/file/line info for a memory address. + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_SOURCE_LINE_RESOLVER_INTERFACE_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_SOURCE_LINE_RESOLVER_INTERFACE_H__ + +#include +#include +#include +#include + +#include "common/using_std_string.h" +#include "google_breakpad/common/breakpad_types.h" +#include "google_breakpad/processor/code_module.h" + +namespace google_breakpad { + +struct StackFrame; +struct WindowsFrameInfo; +class CFIFrameInfo; + +class SourceLineResolverInterface { + public: + typedef uint64_t MemAddr; + + virtual ~SourceLineResolverInterface() {} + + // Adds a module to this resolver, returning true on success. + // + // module should have at least the code_file, debug_file, + // and debug_identifier members populated. + // + // map_file should contain line/address mappings for this module. + virtual bool LoadModule(const CodeModule* module, + const string& map_file) = 0; + // Same as above, but takes the contents of a pre-read map buffer + virtual bool LoadModuleUsingMapBuffer(const CodeModule* module, + const string& map_buffer) = 0; + + // Add an interface to load symbol using C-String data instead of string. + // This is useful in the optimization design for avoiding unnecessary copying + // of symbol data, in order to improve memory efficiency. + // LoadModuleUsingMemoryBuffer() does NOT take ownership of memory_buffer. + // LoadModuleUsingMemoryBuffer() null terminates the passed in buffer, if + // the last character is not a null terminator. + virtual bool LoadModuleUsingMemoryBuffer(const CodeModule* module, + char* memory_buffer, + size_t memory_buffer_size) = 0; + + // Return true if the memory buffer should be deleted immediately after + // LoadModuleUsingMemoryBuffer(). Return false if the memory buffer has to be + // alive during the lifetime of the corresponding Module. + virtual bool ShouldDeleteMemoryBufferAfterLoadModule() = 0; + + // Request that the specified module be unloaded from this resolver. + // A resolver may choose to ignore such a request. + virtual void UnloadModule(const CodeModule* module) = 0; + + // Returns true if the module has been loaded. + virtual bool HasModule(const CodeModule* module) = 0; + + // Returns true if the module has been loaded and it is corrupt. + virtual bool IsModuleCorrupt(const CodeModule* module) = 0; + + // Fills in the function_base, function_name, source_file_name, + // and source_line fields of the StackFrame. The instruction and + // module_name fields must already be filled in. If inlined_frames is not + // nullptr, it will try to construct inlined frames by adding them into + // inlined_frames in an order from outermost frame to inner most frame. + virtual void FillSourceLineInfo( + StackFrame* frame, + std::deque>* inlined_frames) = 0; + + // If Windows stack walking information is available covering + // FRAME's instruction address, return a WindowsFrameInfo structure + // describing it. If the information is not available, returns NULL. + // A NULL return value does not indicate an error. The caller takes + // ownership of any returned WindowsFrameInfo object. + virtual WindowsFrameInfo* FindWindowsFrameInfo(const StackFrame* frame) = 0; + + // If CFI stack walking information is available covering ADDRESS, + // return a CFIFrameInfo structure describing it. If the information + // is not available, return NULL. The caller takes ownership of any + // returned CFIFrameInfo object. + virtual CFIFrameInfo* FindCFIFrameInfo(const StackFrame* frame) = 0; + + protected: + // SourceLineResolverInterface cannot be instantiated except by subclasses + SourceLineResolverInterface() {} +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_SOURCE_LINE_RESOLVER_INTERFACE_H__ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/processor/stack_frame.h b/shared/sentry/external/breakpad/src/google_breakpad/processor/stack_frame.h new file mode 100644 index 000000000..7d5682a7d --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/processor/stack_frame.h @@ -0,0 +1,159 @@ +// 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 GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_H__ + +#include + +#include "common/using_std_string.h" +#include "google_breakpad/common/breakpad_types.h" + +namespace google_breakpad { + +class CodeModule; + +struct StackFrame { + // Indicates how well the instruction pointer derived during + // stack walking is trusted. Since the stack walker can resort to + // stack scanning, it can wind up with dubious frames. + // In rough order of "trust metric". + enum FrameTrust { + FRAME_TRUST_NONE, // Unknown + FRAME_TRUST_SCAN, // Scanned the stack, found this + FRAME_TRUST_CFI_SCAN, // Found while scanning stack using call frame info + FRAME_TRUST_FP, // Derived from frame pointer + FRAME_TRUST_CFI, // Derived from call frame info + // Explicitly provided by some external stack walker. + FRAME_TRUST_PREWALKED, + FRAME_TRUST_CONTEXT, // Given as instruction pointer in a context + FRAME_TRUST_INLINE, // Found by inline records in symbol files. + // Derived from leaf function by simulating a return. + FRAME_TRUST_LEAF, + }; + + StackFrame() + : instruction(), + module(NULL), + function_name(), + function_base(), + source_file_name(), + source_line(0), + source_line_base(), + trust(FRAME_TRUST_NONE), + is_multiple(false) {} + virtual ~StackFrame() {} + + // Return a string describing how this stack frame was found + // by the stackwalker. + string trust_description() const { + switch (trust) { + case StackFrame::FRAME_TRUST_CONTEXT: + return "given as instruction pointer in context"; + case StackFrame::FRAME_TRUST_PREWALKED: + return "recovered by external stack walker"; + case StackFrame::FRAME_TRUST_CFI: + return "call frame info"; + case StackFrame::FRAME_TRUST_CFI_SCAN: + return "call frame info with scanning"; + case StackFrame::FRAME_TRUST_FP: + return "previous frame's frame pointer"; + case StackFrame::FRAME_TRUST_SCAN: + return "stack scanning"; + case StackFrame::FRAME_TRUST_INLINE: + return "inline record"; + case StackFrame::FRAME_TRUST_LEAF: + return "simulating a return from leaf function"; + default: + return "unknown"; + } + } + + // Return the actual return address, as saved on the stack or in a + // register. See the comments for 'instruction', below, for details. + virtual uint64_t ReturnAddress() const { return instruction; } + + // The program counter location as an absolute virtual address. + // + // - For the innermost called frame in a stack, this will be an exact + // program counter or instruction pointer value. + // + // - For all other frames, this address is within the instruction that + // caused execution to branch to this frame's callee (although it may + // not point to the exact beginning of that instruction). This ensures + // that, when we look up the source code location for this frame, we + // get the source location of the call, not of the point at which + // control will resume when the call returns, which may be on the next + // line. (If the compiler knows the callee never returns, it may even + // place the call instruction at the very end of the caller's machine + // code, such that the "return address" (which will never be used) + // immediately after the call instruction is in an entirely different + // function, perhaps even from a different source file.) + // + // On some architectures, the return address as saved on the stack or in + // a register is fine for looking up the point of the call. On others, it + // requires adjustment. ReturnAddress returns the address as saved by the + // machine. + uint64_t instruction; + + // The module in which the instruction resides. + const CodeModule *module; + + // The function name, may be omitted if debug symbols are not available. + string function_name; + + // The start address of the function, may be omitted if debug symbols + // are not available. + uint64_t function_base; + + // The source file name, may be omitted if debug symbols are not available. + string source_file_name; + + // The (1-based) source line number, may be omitted if debug symbols are + // not available. + int source_line; + + // The start address of the source line, may be omitted if debug symbols + // are not available. + uint64_t source_line_base; + + // Amount of trust the stack walker has in the instruction pointer + // of this frame. + FrameTrust trust; + + // True if the frame corresponds to multiple functions, for example as the + // result of identical code folding by the linker. In that case the function + // name, filename, etc. information above represents the state of an arbitrary + // one of these functions. + bool is_multiple; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_H__ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/processor/stack_frame_cpu.h b/shared/sentry/external/breakpad/src/google_breakpad/processor/stack_frame_cpu.h new file mode 100644 index 000000000..24e28ca12 --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/processor/stack_frame_cpu.h @@ -0,0 +1,408 @@ +// -*- 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. + +// stack_frame_cpu.h: CPU-specific StackFrame extensions. +// +// These types extend the StackFrame structure to carry CPU-specific register +// state. They are defined in this header instead of stack_frame.h to +// avoid the need to include minidump_format.h when only the generic +// StackFrame type is needed. +// +// Author: Mark Mentovai + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_CPU_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_CPU_H__ + +#include "google_breakpad/common/minidump_format.h" +#include "google_breakpad/processor/stack_frame.h" + +namespace google_breakpad { + +struct WindowsFrameInfo; +class CFIFrameInfo; + +struct StackFrameX86 : public StackFrame { + // ContextValidity has one entry for each relevant hardware pointer + // register (%eip and %esp) and one entry for each general-purpose + // register. It's worthwhile having validity flags for caller-saves + // registers: they are valid in the youngest frame, and such a frame + // might save a callee-saves register in a caller-saves register, but + // SimpleCFIWalker won't touch registers unless they're marked as valid. + enum ContextValidity { + CONTEXT_VALID_NONE = 0, + CONTEXT_VALID_EIP = 1 << 0, + CONTEXT_VALID_ESP = 1 << 1, + CONTEXT_VALID_EBP = 1 << 2, + CONTEXT_VALID_EAX = 1 << 3, + CONTEXT_VALID_EBX = 1 << 4, + CONTEXT_VALID_ECX = 1 << 5, + CONTEXT_VALID_EDX = 1 << 6, + CONTEXT_VALID_ESI = 1 << 7, + CONTEXT_VALID_EDI = 1 << 8, + CONTEXT_VALID_ALL = -1 + }; + + StackFrameX86() + : context(), + context_validity(CONTEXT_VALID_NONE), + windows_frame_info(NULL), + cfi_frame_info(NULL) {} + ~StackFrameX86(); + + // Overriden to return the return address as saved on the stack. + virtual uint64_t ReturnAddress() const; + + // Register state. This is only fully valid for the topmost frame in a + // stack. In other frames, the values of nonvolatile registers may be + // present, given sufficient debugging information. Refer to + // context_validity. + MDRawContextX86 context; + + // context_validity is actually ContextValidity, but int is used because + // the OR operator doesn't work well with enumerated types. This indicates + // which fields in context are valid. + int context_validity; + + // Any stack walking information we found describing this.instruction. + // These may be NULL if there is no such information for that address. + WindowsFrameInfo *windows_frame_info; + CFIFrameInfo *cfi_frame_info; +}; + +struct StackFramePPC : public StackFrame { + // ContextValidity should eventually contain entries for the validity of + // other nonvolatile (callee-save) registers as in + // StackFrameX86::ContextValidity, but the ppc stackwalker doesn't currently + // locate registers other than the ones listed here. + enum ContextValidity { + CONTEXT_VALID_NONE = 0, + CONTEXT_VALID_SRR0 = 1 << 0, + CONTEXT_VALID_GPR1 = 1 << 1, + CONTEXT_VALID_ALL = -1 + }; + + StackFramePPC() : context(), context_validity(CONTEXT_VALID_NONE) {} + + // Register state. This is only fully valid for the topmost frame in a + // stack. In other frames, the values of nonvolatile registers may be + // present, given sufficient debugging information. Refer to + // context_validity. + MDRawContextPPC context; + + // context_validity is actually ContextValidity, but int is used because + // the OR operator doesn't work well with enumerated types. This indicates + // which fields in context are valid. + int context_validity; +}; + +struct StackFramePPC64 : public StackFrame { + // ContextValidity should eventually contain entries for the validity of + // other nonvolatile (callee-save) registers as in + // StackFrameX86::ContextValidity, but the ppc stackwalker doesn't currently + // locate registers other than the ones listed here. + enum ContextValidity { + CONTEXT_VALID_NONE = 0, + CONTEXT_VALID_SRR0 = 1 << 0, + CONTEXT_VALID_GPR1 = 1 << 1, + CONTEXT_VALID_ALL = -1 + }; + + StackFramePPC64() : context(), context_validity(CONTEXT_VALID_NONE) {} + + // Register state. This is only fully valid for the topmost frame in a + // stack. In other frames, the values of nonvolatile registers may be + // present, given sufficient debugging information. Refer to + // context_validity. + MDRawContextPPC64 context; + + // context_validity is actually ContextValidity, but int is used because + // the OR operator doesn't work well with enumerated types. This indicates + // which fields in context are valid. + int context_validity; +}; + +struct StackFrameAMD64 : public StackFrame { + // ContextValidity has one entry for each register that we might be able + // to recover. + enum ContextValidity { + CONTEXT_VALID_NONE = 0, + CONTEXT_VALID_RAX = 1 << 0, + CONTEXT_VALID_RDX = 1 << 1, + CONTEXT_VALID_RCX = 1 << 2, + CONTEXT_VALID_RBX = 1 << 3, + CONTEXT_VALID_RSI = 1 << 4, + CONTEXT_VALID_RDI = 1 << 5, + CONTEXT_VALID_RBP = 1 << 6, + CONTEXT_VALID_RSP = 1 << 7, + CONTEXT_VALID_R8 = 1 << 8, + CONTEXT_VALID_R9 = 1 << 9, + CONTEXT_VALID_R10 = 1 << 10, + CONTEXT_VALID_R11 = 1 << 11, + CONTEXT_VALID_R12 = 1 << 12, + CONTEXT_VALID_R13 = 1 << 13, + CONTEXT_VALID_R14 = 1 << 14, + CONTEXT_VALID_R15 = 1 << 15, + CONTEXT_VALID_RIP = 1 << 16, + CONTEXT_VALID_ALL = -1 + }; + + StackFrameAMD64() : context(), context_validity(CONTEXT_VALID_NONE) {} + + // Overriden to return the return address as saved on the stack. + virtual uint64_t ReturnAddress() const; + + // Register state. This is only fully valid for the topmost frame in a + // stack. In other frames, which registers are present depends on what + // debugging information we had available. Refer to context_validity. + MDRawContextAMD64 context; + + // For each register in context whose value has been recovered, we set + // the corresponding CONTEXT_VALID_ bit in context_validity. + // + // context_validity's type should actually be ContextValidity, but + // we use int instead because the bitwise inclusive or operator + // yields an int when applied to enum values, and C++ doesn't + // silently convert from ints to enums. + int context_validity; +}; + +struct StackFrameSPARC : public StackFrame { + // to be confirmed + enum ContextValidity { + CONTEXT_VALID_NONE = 0, + CONTEXT_VALID_PC = 1 << 0, + CONTEXT_VALID_SP = 1 << 1, + CONTEXT_VALID_FP = 1 << 2, + CONTEXT_VALID_ALL = -1 + }; + + StackFrameSPARC() : context(), context_validity(CONTEXT_VALID_NONE) {} + + // Register state. This is only fully valid for the topmost frame in a + // stack. In other frames, the values of nonvolatile registers may be + // present, given sufficient debugging information. Refer to + // context_validity. + MDRawContextSPARC context; + + // context_validity is actually ContextValidity, but int is used because + // the OR operator doesn't work well with enumerated types. This indicates + // which fields in context are valid. + int context_validity; +}; + +struct StackFrameARM : public StackFrame { + // A flag for each register we might know. + enum ContextValidity { + CONTEXT_VALID_NONE = 0, + CONTEXT_VALID_R0 = 1 << 0, + CONTEXT_VALID_R1 = 1 << 1, + CONTEXT_VALID_R2 = 1 << 2, + CONTEXT_VALID_R3 = 1 << 3, + CONTEXT_VALID_R4 = 1 << 4, + CONTEXT_VALID_R5 = 1 << 5, + CONTEXT_VALID_R6 = 1 << 6, + CONTEXT_VALID_R7 = 1 << 7, + CONTEXT_VALID_R8 = 1 << 8, + CONTEXT_VALID_R9 = 1 << 9, + CONTEXT_VALID_R10 = 1 << 10, + CONTEXT_VALID_R11 = 1 << 11, + CONTEXT_VALID_R12 = 1 << 12, + CONTEXT_VALID_R13 = 1 << 13, + CONTEXT_VALID_R14 = 1 << 14, + CONTEXT_VALID_R15 = 1 << 15, + CONTEXT_VALID_ALL = ~CONTEXT_VALID_NONE, + + // Aliases for registers with dedicated or conventional roles. + CONTEXT_VALID_FP = CONTEXT_VALID_R11, + CONTEXT_VALID_SP = CONTEXT_VALID_R13, + CONTEXT_VALID_LR = CONTEXT_VALID_R14, + CONTEXT_VALID_PC = CONTEXT_VALID_R15 + }; + + StackFrameARM() : context(), context_validity(CONTEXT_VALID_NONE) {} + + // Return the ContextValidity flag for register rN. + static ContextValidity RegisterValidFlag(int n) { + if (0 <= n && n <= 15) { + return ContextValidity(1 << n); + } + return CONTEXT_VALID_NONE; + } + + // Register state. This is only fully valid for the topmost frame in a + // stack. In other frames, the values of nonvolatile registers may be + // present, given sufficient debugging information. Refer to + // context_validity. + MDRawContextARM context; + + // For each register in context whose value has been recovered, we set + // the corresponding CONTEXT_VALID_ bit in context_validity. + // + // context_validity's type should actually be ContextValidity, but + // we use int instead because the bitwise inclusive or operator + // yields an int when applied to enum values, and C++ doesn't + // silently convert from ints to enums. + int context_validity; +}; + +struct StackFrameARM64 : public StackFrame { + // A flag for each register we might know. Note that we can't use an enum + // here as there are 33 values to represent. + static const uint64_t CONTEXT_VALID_NONE = 0; + static const uint64_t CONTEXT_VALID_X0 = 1ULL << 0; + static const uint64_t CONTEXT_VALID_X1 = 1ULL << 1; + static const uint64_t CONTEXT_VALID_X2 = 1ULL << 2; + static const uint64_t CONTEXT_VALID_X3 = 1ULL << 3; + static const uint64_t CONTEXT_VALID_X4 = 1ULL << 4; + static const uint64_t CONTEXT_VALID_X5 = 1ULL << 5; + static const uint64_t CONTEXT_VALID_X6 = 1ULL << 6; + static const uint64_t CONTEXT_VALID_X7 = 1ULL << 7; + static const uint64_t CONTEXT_VALID_X8 = 1ULL << 8; + static const uint64_t CONTEXT_VALID_X9 = 1ULL << 9; + static const uint64_t CONTEXT_VALID_X10 = 1ULL << 10; + static const uint64_t CONTEXT_VALID_X11 = 1ULL << 11; + static const uint64_t CONTEXT_VALID_X12 = 1ULL << 12; + static const uint64_t CONTEXT_VALID_X13 = 1ULL << 13; + static const uint64_t CONTEXT_VALID_X14 = 1ULL << 14; + static const uint64_t CONTEXT_VALID_X15 = 1ULL << 15; + static const uint64_t CONTEXT_VALID_X16 = 1ULL << 16; + static const uint64_t CONTEXT_VALID_X17 = 1ULL << 17; + static const uint64_t CONTEXT_VALID_X18 = 1ULL << 18; + static const uint64_t CONTEXT_VALID_X19 = 1ULL << 19; + static const uint64_t CONTEXT_VALID_X20 = 1ULL << 20; + static const uint64_t CONTEXT_VALID_X21 = 1ULL << 21; + static const uint64_t CONTEXT_VALID_X22 = 1ULL << 22; + static const uint64_t CONTEXT_VALID_X23 = 1ULL << 23; + static const uint64_t CONTEXT_VALID_X24 = 1ULL << 24; + static const uint64_t CONTEXT_VALID_X25 = 1ULL << 25; + static const uint64_t CONTEXT_VALID_X26 = 1ULL << 26; + static const uint64_t CONTEXT_VALID_X27 = 1ULL << 27; + static const uint64_t CONTEXT_VALID_X28 = 1ULL << 28; + static const uint64_t CONTEXT_VALID_X29 = 1ULL << 29; + static const uint64_t CONTEXT_VALID_X30 = 1ULL << 30; + static const uint64_t CONTEXT_VALID_X31 = 1ULL << 31; + static const uint64_t CONTEXT_VALID_X32 = 1ULL << 32; + static const uint64_t CONTEXT_VALID_ALL = ~CONTEXT_VALID_NONE; + + // Aliases for registers with dedicated or conventional roles. + static const uint64_t CONTEXT_VALID_FP = CONTEXT_VALID_X29; + static const uint64_t CONTEXT_VALID_LR = CONTEXT_VALID_X30; + static const uint64_t CONTEXT_VALID_SP = CONTEXT_VALID_X31; + static const uint64_t CONTEXT_VALID_PC = CONTEXT_VALID_X32; + + StackFrameARM64() : context(), + context_validity(CONTEXT_VALID_NONE) {} + + // Return the validity flag for register xN. + static uint64_t RegisterValidFlag(int n) { + return 1ULL << n; + } + + // Register state. This is only fully valid for the topmost frame in a + // stack. In other frames, the values of nonvolatile registers may be + // present, given sufficient debugging information. Refer to + // context_validity. + MDRawContextARM64 context; + + // For each register in context whose value has been recovered, we set + // the corresponding CONTEXT_VALID_ bit in context_validity. + uint64_t context_validity; +}; + +struct StackFrameMIPS : public StackFrame { + // MIPS callee save registers for o32 ABI (32bit registers) are: + // 1. $s0-$s7, + // 2. $sp, $fp + // 3. $f20-$f31 + // + // The register structure is available at + // http://en.wikipedia.org/wiki/MIPS_architecture#Compiler_register_usage + +#define INDEX_MIPS_REG_S0 MD_CONTEXT_MIPS_REG_S0 // 16 +#define INDEX_MIPS_REG_S7 MD_CONTEXT_MIPS_REG_S7 // 23 +#define INDEX_MIPS_REG_GP MD_CONTEXT_MIPS_REG_GP // 28 +#define INDEX_MIPS_REG_RA MD_CONTEXT_MIPS_REG_RA // 31 +#define INDEX_MIPS_REG_PC 34 +#define SHIFT_MIPS_REG_S0 0 +#define SHIFT_MIPS_REG_GP 8 +#define SHIFT_MIPS_REG_PC 12 + + enum ContextValidity { + CONTEXT_VALID_NONE = 0, + CONTEXT_VALID_S0 = 1 << 0, // $16 + CONTEXT_VALID_S1 = 1 << 1, // $17 + CONTEXT_VALID_S2 = 1 << 2, // $18 + CONTEXT_VALID_S3 = 1 << 3, // $19 + CONTEXT_VALID_S4 = 1 << 4, // $20 + CONTEXT_VALID_S5 = 1 << 5, // $21 + CONTEXT_VALID_S6 = 1 << 6, // $22 + CONTEXT_VALID_S7 = 1 << 7, // $23 + // GP is not calee-save for o32 abi. + CONTEXT_VALID_GP = 1 << 8, // $28 + CONTEXT_VALID_SP = 1 << 9, // $29 + CONTEXT_VALID_FP = 1 << 10, // $30 + CONTEXT_VALID_RA = 1 << 11, // $31 + CONTEXT_VALID_PC = 1 << 12, // $34 + CONTEXT_VALID_ALL = ~CONTEXT_VALID_NONE + }; + + // Return the ContextValidity flag for register rN. + static ContextValidity RegisterValidFlag(int n) { + if (n >= INDEX_MIPS_REG_S0 && n <= INDEX_MIPS_REG_S7) + return ContextValidity(1 << (n - INDEX_MIPS_REG_S0 + SHIFT_MIPS_REG_S0)); + else if (n >= INDEX_MIPS_REG_GP && n <= INDEX_MIPS_REG_RA) + return ContextValidity(1 << (n - INDEX_MIPS_REG_GP + SHIFT_MIPS_REG_GP)); + else if (n == INDEX_MIPS_REG_PC) + return ContextValidity(1 << SHIFT_MIPS_REG_PC); + + return CONTEXT_VALID_NONE; + } + + StackFrameMIPS() : context(), context_validity(CONTEXT_VALID_NONE) {} + + // Register state. This is only fully valid for the topmost frame in a + // stack. In other frames, which registers are present depends on what + // debugging information were available. Refer to 'context_validity' below. + MDRawContextMIPS context; + + // For each register in context whose value has been recovered, + // the corresponding CONTEXT_VALID_ bit in 'context_validity' is set. + // + // context_validity's type should actually be ContextValidity, but + // type int is used instead because the bitwise inclusive or operator + // yields an int when applied to enum values, and C++ doesn't + // silently convert from ints to enums. + int context_validity; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_CPU_H__ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/processor/stack_frame_symbolizer.h b/shared/sentry/external/breakpad/src/google_breakpad/processor/stack_frame_symbolizer.h new file mode 100644 index 000000000..91ef64b54 --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/processor/stack_frame_symbolizer.h @@ -0,0 +1,114 @@ +// -*- 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. + +// Helper class that encapsulates the logic of how symbol supplier interacts +// with source line resolver to fill stack frame information. + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_SYMBOLIZER_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_SYMBOLIZER_H__ + +#include +#include +#include +#include +#include + +#include "common/using_std_string.h" +#include "google_breakpad/common/breakpad_types.h" +#include "google_breakpad/processor/code_module.h" + +namespace google_breakpad { +class CFIFrameInfo; +class CodeModules; +class SymbolSupplier; +class SourceLineResolverInterface; +struct StackFrame; +struct SystemInfo; +struct WindowsFrameInfo; + +class StackFrameSymbolizer { + public: + enum SymbolizerResult { + // Symbol data was found and successfully loaded in resolver. + // This does NOT guarantee source line info is found within symbol file. + kNoError, + // This indicates non-critical error, such as, no code module found for + // frame's instruction, no symbol file, or resolver failed to load symbol. + kError, + // This indicates error for which stack walk should be interrupted + // and retried in future. + kInterrupt, + // Symbol data was found and loaded in resolver however some corruptions + // were detected. + kWarningCorruptSymbols, + }; + + StackFrameSymbolizer(SymbolSupplier* supplier, + SourceLineResolverInterface* resolver); + + virtual ~StackFrameSymbolizer() { } + + // Encapsulate the step of resolving source line info for a stack frame. + // "frame" must not be NULL. + virtual SymbolizerResult FillSourceLineInfo( + const CodeModules* modules, + const CodeModules* unloaded_modules, + const SystemInfo* system_info, + StackFrame* stack_frame, + std::deque>* inlined_frames); + + virtual WindowsFrameInfo* FindWindowsFrameInfo(const StackFrame* frame); + + virtual CFIFrameInfo* FindCFIFrameInfo(const StackFrame* frame); + + // Reset internal (locally owned) data as if the helper is re-instantiated. + // A typical case is to call Reset() after processing an individual report + // before start to process next one, in order to reset internal information + // about missing symbols found so far. + virtual void Reset() { no_symbol_modules_.clear(); } + + // Returns true if there is valid implementation for stack symbolization. + virtual bool HasImplementation() { return resolver_ && supplier_; } + + SourceLineResolverInterface* resolver() { return resolver_; } + SymbolSupplier* supplier() { return supplier_; } + + protected: + SymbolSupplier* supplier_; + SourceLineResolverInterface* resolver_; + // A list of modules known to have symbols missing. This helps avoid + // repeated lookups for the missing symbols within one minidump. + std::set no_symbol_modules_; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_STACK_FRAME_SYMBOLIZER_H__ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/processor/stackwalker.h b/shared/sentry/external/breakpad/src/google_breakpad/processor/stackwalker.h new file mode 100644 index 000000000..daa5039ae --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/processor/stackwalker.h @@ -0,0 +1,261 @@ +// 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. + +// stackwalker.h: Generic stackwalker. +// +// The Stackwalker class is an abstract base class providing common generic +// methods that apply to stacks from all systems. Specific implementations +// will extend this class by providing GetContextFrame and GetCallerFrame +// methods to fill in system-specific data in a StackFrame structure. +// Stackwalker assembles these StackFrame strucutres into a CallStack. +// +// Author: Mark Mentovai + + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_STACKWALKER_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_STACKWALKER_H__ + +#include +#include +#include + +#include "common/using_std_string.h" +#include "google_breakpad/common/breakpad_types.h" +#include "google_breakpad/processor/code_modules.h" +#include "google_breakpad/processor/memory_region.h" +#include "google_breakpad/processor/stack_frame_symbolizer.h" + +namespace google_breakpad { + +class CallStack; +class DumpContext; +class StackFrameSymbolizer; + +using std::set; +using std::vector; + +class Stackwalker { + public: + virtual ~Stackwalker() {} + + // Populates the given CallStack by calling GetContextFrame and + // GetCallerFrame. The frames are further processed to fill all available + // data. Returns true if the stackwalk completed, or false if it was + // interrupted by SymbolSupplier::GetSymbolFile(). + // Upon return, |modules_without_symbols| will be populated with pointers to + // the code modules (CodeModule*) that DON'T have symbols. + // |modules_with_corrupt_symbols| will be populated with pointers to the + // modules which have corrupt symbols. |modules_without_symbols| and + // |modules_with_corrupt_symbols| DO NOT take ownership of the code modules. + // The lifetime of these code modules is the same as the lifetime of the + // CodeModules passed to the StackWalker constructor (which currently + // happens to be the lifetime of the Breakpad's ProcessingState object). + // There is a check for duplicate modules so no duplicates are expected. + bool Walk(CallStack* stack, + vector* modules_without_symbols, + vector* modules_with_corrupt_symbols); + + // Returns a new concrete subclass suitable for the CPU that a stack was + // generated on, according to the CPU type indicated by the context + // argument. If no suitable concrete subclass exists, returns NULL. + static Stackwalker* StackwalkerForCPU( + const SystemInfo* system_info, + DumpContext* context, + MemoryRegion* memory, + const CodeModules* modules, + const CodeModules* unloaded_modules, + StackFrameSymbolizer* resolver_helper); + + + static void set_max_frames(uint32_t max_frames) { + max_frames_ = max_frames; + max_frames_set_ = true; + } + static uint32_t max_frames() { return max_frames_; } + + static void set_max_frames_scanned(uint32_t max_frames_scanned) { + max_frames_scanned_ = max_frames_scanned; + } + + protected: + // system_info identifies the operating system, NULL or empty if unknown. + // memory identifies a MemoryRegion that provides the stack memory + // for the stack to walk. modules, if non-NULL, is a CodeModules + // object that is used to look up which code module each stack frame is + // associated with. frame_symbolizer is a StackFrameSymbolizer object that + // encapsulates the logic of how source line resolver interacts with symbol + // supplier to symbolize stack frame and look up caller frame information + // (see stack_frame_symbolizer.h). + // frame_symbolizer MUST NOT be NULL (asserted). + Stackwalker(const SystemInfo* system_info, + MemoryRegion* memory, + const CodeModules* modules, + StackFrameSymbolizer* frame_symbolizer); + + // This can be used to filter out potential return addresses when + // the stack walker resorts to stack scanning. + // Returns true if any of: + // * This address is within a loaded module, but we don't have symbols + // for that module. + // * This address is within a loaded module for which we have symbols, + // and falls inside a function in that module. + // Returns false otherwise. + bool InstructionAddressSeemsValid(uint64_t address) const; + + // Checks whether we should stop the stack trace. + // (either we reached the end-of-stack or we detected a + // broken callstack invariant) + bool TerminateWalk(uint64_t caller_ip, + uint64_t caller_sp, + uint64_t callee_sp, + bool first_unwind) const; + + // The default number of words to search through on the stack + // for a return address. + static const int kRASearchWords; + + template + bool ScanForReturnAddress(InstructionType location_start, + InstructionType* location_found, + InstructionType* ip_found, + bool is_context_frame) { + // When searching for the caller of the context frame, + // allow the scanner to look farther down the stack. + const int search_words = is_context_frame ? + kRASearchWords * 4 : + kRASearchWords; + + return ScanForReturnAddress(location_start, location_found, ip_found, + search_words); + } + + // Scan the stack starting at location_start, looking for an address + // that looks like a valid instruction pointer. Addresses must + // 1) be contained in the current stack memory + // 2) pass the checks in InstructionAddressSeemsValid + // + // Returns true if a valid-looking instruction pointer was found. + // When returning true, sets location_found to the address at which + // the value was found, and ip_found to the value contained at that + // location in memory. + template + bool ScanForReturnAddress(InstructionType location_start, + InstructionType* location_found, + InstructionType* ip_found, + int searchwords) { + for (InstructionType location = location_start; + location <= location_start + searchwords * sizeof(InstructionType); + location += sizeof(InstructionType)) { + InstructionType ip; + if (!memory_->GetMemoryAtAddress(location, &ip)) + break; + + // The return address points to the instruction after a call. If the + // caller was a no return function, this might point past the end of the + // function. Subtract one from the instruction pointer so it points into + // the call instruction instead. + if (modules_ && modules_->GetModuleForAddress(ip - 1) && + InstructionAddressSeemsValid(ip - 1)) { + *ip_found = ip; + *location_found = location; + return true; + } + } + // nothing found + return false; + } + + // Information about the system that produced the minidump. Subclasses + // and the SymbolSupplier may find this information useful. + const SystemInfo* system_info_; + + // The stack memory to walk. Subclasses will require this region to + // get information from the stack. + MemoryRegion* memory_; + + // A list of modules, for populating each StackFrame's module information. + // This field is optional and may be NULL. + const CodeModules* modules_; + + // A list of unloaded modules, for populating frames which aren't matched + // to any loaded modules. + // This field is optional and may be NULL. + const CodeModules* unloaded_modules_; + + protected: + // The StackFrameSymbolizer implementation. + StackFrameSymbolizer* frame_symbolizer_; + + private: + // Obtains the context frame, the innermost called procedure in a stack + // trace. Returns NULL on failure. GetContextFrame allocates a new + // StackFrame (or StackFrame subclass), ownership of which is taken by + // the caller. + virtual StackFrame* GetContextFrame() = 0; + + // Obtains a caller frame. Each call to GetCallerFrame should return the + // frame that called the last frame returned by GetContextFrame or + // GetCallerFrame. To aid this purpose, stack contains the CallStack + // made of frames that have already been walked. GetCallerFrame should + // return NULL on failure or when there are no more caller frames (when + // the end of the stack has been reached). GetCallerFrame allocates a new + // StackFrame (or StackFrame subclass), ownership of which is taken by + // the caller. |stack_scan_allowed| controls whether stack scanning is + // an allowable frame-recovery method, since it is desirable to be able to + // disable stack scanning in performance-critical use cases. + // + // CONSIDER: a way to differentiate between: + // - full stack traces + // - explicitly truncated traces (max_frames_) + // - stopping after max scanned frames + // - failed stack walk (breaking one of the stack walk invariants) + // + virtual StackFrame* GetCallerFrame(const CallStack* stack, + bool stack_scan_allowed) = 0; + + // The maximum number of frames Stackwalker will walk through. + // This defaults to 1024 to prevent infinite loops. + static uint32_t max_frames_; + + // Keep track of whether max_frames_ has been set by the user, since + // it affects whether or not an error message is printed in the case + // where an unwind got stopped by the limit. + static bool max_frames_set_; + + // The maximum number of stack-scanned and otherwise untrustworthy + // frames allowed. Stack-scanning can be expensive, so the option to + // disable or limit it is helpful in cases where unwind performance is + // important. This defaults to 1024, the same as max_frames_. + static uint32_t max_frames_scanned_; +}; + +} // namespace google_breakpad + + +#endif // GOOGLE_BREAKPAD_PROCESSOR_STACKWALKER_H__ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/processor/symbol_supplier.h b/shared/sentry/external/breakpad/src/google_breakpad/processor/symbol_supplier.h new file mode 100644 index 000000000..6ec017665 --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/processor/symbol_supplier.h @@ -0,0 +1,99 @@ +// 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. + +// The caller may implement the SymbolSupplier abstract base class +// to provide symbols for a given module. + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_SYMBOL_SUPPLIER_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_SYMBOL_SUPPLIER_H__ + +#include +#include "common/using_std_string.h" + +namespace google_breakpad { + +class CodeModule; +struct SystemInfo; + +class SymbolSupplier { + public: + // Result type for GetSymbolFile + enum SymbolResult { + // no symbols were found, but continue processing + NOT_FOUND, + + // symbols were found, and the path has been placed in symbol_file + FOUND, + + // stops processing the minidump immediately + INTERRUPT + }; + + virtual ~SymbolSupplier() {} + + // Retrieves the symbol file for the given CodeModule, placing the + // path in symbol_file if successful. system_info contains strings + // identifying the operating system and CPU; SymbolSupplier may use + // to help locate the symbol file. system_info may be NULL or its + // fields may be empty if these values are unknown. symbol_file + // must be a pointer to a valid string + virtual SymbolResult GetSymbolFile(const CodeModule* module, + const SystemInfo* system_info, + string* symbol_file) = 0; + // Same as above, except also places symbol data into symbol_data. + // If symbol_data is NULL, the data is not returned. + // TODO(nealsid) Once we have symbol data caching behavior implemented + // investigate making all symbol suppliers implement all methods, + // and make this pure virtual + virtual SymbolResult GetSymbolFile(const CodeModule* module, + const SystemInfo* system_info, + string* symbol_file, + string* symbol_data) = 0; + + // Same as above, except allocates data buffer on heap and then places the + // symbol data into the buffer as C-string. + // SymbolSupplier is responsible for deleting the data buffer. After the call + // to GetCStringSymbolData(), the caller should call FreeSymbolData(const + // Module* module) once the data buffer is no longer needed. + // If symbol_data is not NULL, symbol supplier won't return FOUND unless it + // returns a valid buffer in symbol_data, e.g., returns INTERRUPT on memory + // allocation failure. + virtual SymbolResult GetCStringSymbolData(const CodeModule* module, + const SystemInfo* system_info, + string* symbol_file, + char** symbol_data, + size_t* symbol_data_size) = 0; + + // Frees the data buffer allocated for the module in GetCStringSymbolData. + virtual void FreeSymbolData(const CodeModule* module) = 0; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_SYMBOL_SUPPLIER_H__ diff --git a/shared/sentry/external/breakpad/src/google_breakpad/processor/system_info.h b/shared/sentry/external/breakpad/src/google_breakpad/processor/system_info.h new file mode 100644 index 000000000..8d2f60be4 --- /dev/null +++ b/shared/sentry/external/breakpad/src/google_breakpad/processor/system_info.h @@ -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. + +// system_info.h: Information about the system that was running a program +// when a crash report was produced. +// +// Author: Mark Mentovai + +#ifndef GOOGLE_BREAKPAD_PROCESSOR_SYSTEM_INFO_H__ +#define GOOGLE_BREAKPAD_PROCESSOR_SYSTEM_INFO_H__ + +#include + +#include "common/using_std_string.h" + +namespace google_breakpad { + +struct SystemInfo { + public: + SystemInfo() : os(), os_short(), os_version(), cpu(), cpu_info(), + cpu_count(0), gl_version(), gl_vendor(), gl_renderer() {} + + // Resets the SystemInfo object to its default values. + void Clear() { + os.clear(); + os_short.clear(); + os_version.clear(); + cpu.clear(); + cpu_info.clear(); + cpu_count = 0; + gl_version.clear(); + gl_vendor.clear(); + gl_renderer.clear(); + } + + // A string identifying the operating system, such as "Windows NT", + // "Mac OS X", or "Linux". If the information is present in the dump but + // its value is unknown, this field will contain a numeric value. If + // the information is not present in the dump, this field will be empty. + string os; + + // A short form of the os string, using lowercase letters and no spaces, + // suitable for use in a filesystem. Possible values include "windows", + // "mac", "linux" and "nacl". Empty if the information is not present + // in the dump or if the OS given by the dump is unknown. The values + // stored in this field should match those used by + // MinidumpSystemInfo::GetOS. + string os_short; + + // A string identifying the version of the operating system, such as + // "5.1.2600 Service Pack 2" or "10.4.8 8L2127". If the dump does not + // contain this information, this field will be empty. + string os_version; + + // A string identifying the basic CPU family, such as "x86" or "ppc". + // If this information is present in the dump but its value is unknown, + // this field will contain a numeric value. If the information is not + // present in the dump, this field will be empty. The values stored in + // this field should match those used by MinidumpSystemInfo::GetCPU. + string cpu; + + // A string further identifying the specific CPU, such as + // "GenuineIntel level 6 model 13 stepping 8". If the information is not + // present in the dump, or additional identifying information is not + // defined for the CPU family, this field will be empty. + string cpu_info; + + // The number of processors in the system. Will be greater than one for + // multi-core systems. + int cpu_count; + + // The GPU information. Currently only populated in microdumps. + string gl_version; + string gl_vendor; + string gl_renderer; +}; + +} // namespace google_breakpad + +#endif // GOOGLE_BREAKPAD_PROCESSOR_SYSTEM_INFO_H__ diff --git a/shared/sentry/external/breakpad/src/third_party/curl/COPYING b/shared/sentry/external/breakpad/src/third_party/curl/COPYING new file mode 100644 index 000000000..610fbdb07 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/curl/COPYING @@ -0,0 +1,22 @@ +COPYRIGHT AND PERMISSION NOTICE + +Copyright (c) 1996 - 2011, Daniel Stenberg, . + +All rights reserved. + +Permission to use, copy, modify, and distribute this software for any purpose +with or without fee is hereby granted, provided that the above copyright +notice and this permission notice appear in all copies. + +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 OF THIRD PARTY RIGHTS. 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. + +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 this Software without prior written authorization of the copyright holder. + diff --git a/shared/sentry/external/breakpad/src/third_party/curl/curl.h b/shared/sentry/external/breakpad/src/third_party/curl/curl.h new file mode 100644 index 000000000..0d80936f7 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/curl/curl.h @@ -0,0 +1,1936 @@ +#ifndef __CURL_CURL_H +#define __CURL_CURL_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2009, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id: curl.h,v 1.396 2009-10-16 13:30:31 yangtse Exp $ + ***************************************************************************/ + +/* + * If you have libcurl problems, all docs and details are found here: + * http://curl.haxx.se/libcurl/ + * + * curl-library mailing list subscription and unsubscription web interface: + * http://cool.haxx.se/mailman/listinfo/curl-library/ + */ + +/* + * Leading 'curl' path on the 'curlbuild.h' include statement is + * required to properly allow building outside of the source tree, + * due to the fact that in this case 'curlbuild.h' is generated in + * a subdirectory of the build tree while 'curl.h actually remains + * in a subdirectory of the source tree. + */ + +#include "third_party/curl/curlver.h" /* libcurl version defines */ +#include "third_party/curl/curlbuild.h" /* libcurl build definitions */ +#include "third_party/curl/curlrules.h" /* libcurl rules enforcement */ + +/* + * Define WIN32 when build target is Win32 API + */ + +#if (defined(_WIN32) || defined(__WIN32__)) && \ + !defined(WIN32) && !defined(__SYMBIAN32__) +#define WIN32 +#endif + +#include +#include + +/* The include stuff here below is mainly for time_t! */ +#include +#include + +#if defined(WIN32) && !defined(_WIN32_WCE) && !defined(__GNUC__) && \ + !defined(__CYGWIN__) || defined(__MINGW32__) +#if !(defined(_WINSOCKAPI_) || defined(_WINSOCK_H)) +/* The check above prevents the winsock2 inclusion if winsock.h already was + included, since they can't co-exist without problems */ +#include +#include +#endif +#else + +/* HP-UX systems version 9, 10 and 11 lack sys/select.h and so does oldish + libc5-based Linux systems. Only include it on system that are known to + require it! */ +#if defined(_AIX) || defined(__NOVELL_LIBC__) || defined(__NetBSD__) || \ + defined(__minix) || defined(__SYMBIAN32__) || defined(__INTEGRITY) || \ + defined(__ANDROID__) +#include +#endif + +#ifndef _WIN32_WCE +#include +#endif +#if !defined(WIN32) && !defined(__WATCOMC__) && !defined(__VXWORKS__) +#include +#endif +#include +#endif + +#ifdef __BEOS__ +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void CURL; + +/* + * Decorate exportable functions for Win32 and Symbian OS DLL linking. + * This avoids using a .def file for building libcurl.dll. + */ +#if (defined(WIN32) || defined(_WIN32) || defined(__SYMBIAN32__)) && \ + !defined(CURL_STATICLIB) +#if defined(BUILDING_LIBCURL) +#define CURL_EXTERN __declspec(dllexport) +#else +#define CURL_EXTERN __declspec(dllimport) +#endif +#else + +#ifdef CURL_HIDDEN_SYMBOLS +/* + * This definition is used to make external definitions visible in the + * shared library when symbols are hidden by default. It makes no + * difference when compiling applications whether this is set or not, + * only when compiling the library. + */ +#define CURL_EXTERN CURL_EXTERN_SYMBOL +#else +#define CURL_EXTERN +#endif +#endif + +#ifndef curl_socket_typedef +/* socket typedef */ +#ifdef WIN32 +typedef SOCKET curl_socket_t; +#define CURL_SOCKET_BAD INVALID_SOCKET +#else +typedef int curl_socket_t; +#define CURL_SOCKET_BAD -1 +#endif +#define curl_socket_typedef +#endif /* curl_socket_typedef */ + +struct curl_httppost { + struct curl_httppost *next; /* next entry in the list */ + char *name; /* pointer to allocated name */ + long namelength; /* length of name length */ + char *contents; /* pointer to allocated data contents */ + long contentslength; /* length of contents field */ + char *buffer; /* pointer to allocated buffer contents */ + long bufferlength; /* length of buffer field */ + char *contenttype; /* Content-Type */ + struct curl_slist* contentheader; /* list of extra headers for this form */ + struct curl_httppost *more; /* if one field name has more than one + file, this link should link to following + files */ + long flags; /* as defined below */ +#define HTTPPOST_FILENAME (1<<0) /* specified content is a file name */ +#define HTTPPOST_READFILE (1<<1) /* specified content is a file name */ +#define HTTPPOST_PTRNAME (1<<2) /* name is only stored pointer + do not free in formfree */ +#define HTTPPOST_PTRCONTENTS (1<<3) /* contents is only stored pointer + do not free in formfree */ +#define HTTPPOST_BUFFER (1<<4) /* upload file from buffer */ +#define HTTPPOST_PTRBUFFER (1<<5) /* upload file from pointer contents */ +#define HTTPPOST_CALLBACK (1<<6) /* upload file contents by using the + regular read callback to get the data + and pass the given pointer as custom + pointer */ + + char *showfilename; /* The file name to show. If not set, the + actual file name will be used (if this + is a file part) */ + void *userp; /* custom pointer used for + HTTPPOST_CALLBACK posts */ +}; + +typedef int (*curl_progress_callback)(void *clientp, + double dltotal, + double dlnow, + double ultotal, + double ulnow); + +#ifndef CURL_MAX_WRITE_SIZE + /* Tests have proven that 20K is a very bad buffer size for uploads on + Windows, while 16K for some odd reason performed a lot better. + We do the ifndef check to allow this value to easier be changed at build + time for those who feel adventurous. */ +#define CURL_MAX_WRITE_SIZE 16384 +#endif + +#ifndef CURL_MAX_HTTP_HEADER +/* The only reason to have a max limit for this is to avoid the risk of a bad + server feeding libcurl with a never-ending header that will cause reallocs + infinitely */ +#define CURL_MAX_HTTP_HEADER (100*1024) +#endif + + +/* This is a magic return code for the write callback that, when returned, + will signal libcurl to pause receiving on the current transfer. */ +#define CURL_WRITEFUNC_PAUSE 0x10000001 +typedef size_t (*curl_write_callback)(char *buffer, + size_t size, + size_t nitems, + void *outstream); + +/* These are the return codes for the seek callbacks */ +#define CURL_SEEKFUNC_OK 0 +#define CURL_SEEKFUNC_FAIL 1 /* fail the entire transfer */ +#define CURL_SEEKFUNC_CANTSEEK 2 /* tell libcurl seeking can't be done, so + libcurl might try other means instead */ +typedef int (*curl_seek_callback)(void *instream, + curl_off_t offset, + int origin); /* 'whence' */ + +/* This is a return code for the read callback that, when returned, will + signal libcurl to immediately abort the current transfer. */ +#define CURL_READFUNC_ABORT 0x10000000 +/* This is a return code for the read callback that, when returned, will + signal libcurl to pause sending data on the current transfer. */ +#define CURL_READFUNC_PAUSE 0x10000001 + +typedef size_t (*curl_read_callback)(char *buffer, + size_t size, + size_t nitems, + void *instream); + +typedef enum { + CURLSOCKTYPE_IPCXN, /* socket created for a specific IP connection */ + CURLSOCKTYPE_LAST /* never use */ +} curlsocktype; + +typedef int (*curl_sockopt_callback)(void *clientp, + curl_socket_t curlfd, + curlsocktype purpose); + +struct curl_sockaddr { + int family; + int socktype; + int protocol; + unsigned int addrlen; /* addrlen was a socklen_t type before 7.18.0 but it + turned really ugly and painful on the systems that + lack this type */ + struct sockaddr addr; +}; + +typedef curl_socket_t +(*curl_opensocket_callback)(void *clientp, + curlsocktype purpose, + struct curl_sockaddr *address); + +#ifndef CURL_NO_OLDIES + /* not used since 7.10.8, will be removed in a future release */ +typedef int (*curl_passwd_callback)(void *clientp, + const char *prompt, + char *buffer, + int buflen); +#endif + +typedef enum { + CURLIOE_OK, /* I/O operation successful */ + CURLIOE_UNKNOWNCMD, /* command was unknown to callback */ + CURLIOE_FAILRESTART, /* failed to restart the read */ + CURLIOE_LAST /* never use */ +} curlioerr; + +typedef enum { + CURLIOCMD_NOP, /* no operation */ + CURLIOCMD_RESTARTREAD, /* restart the read stream from start */ + CURLIOCMD_LAST /* never use */ +} curliocmd; + +typedef curlioerr (*curl_ioctl_callback)(CURL *handle, + int cmd, + void *clientp); + +/* + * The following typedef's are signatures of malloc, free, realloc, strdup and + * calloc respectively. Function pointers of these types can be passed to the + * curl_global_init_mem() function to set user defined memory management + * callback routines. + */ +typedef void *(*curl_malloc_callback)(size_t size); +typedef void (*curl_free_callback)(void *ptr); +typedef void *(*curl_realloc_callback)(void *ptr, size_t size); +typedef char *(*curl_strdup_callback)(const char *str); +typedef void *(*curl_calloc_callback)(size_t nmemb, size_t size); + +/* the kind of data that is passed to information_callback*/ +typedef enum { + CURLINFO_TEXT = 0, + CURLINFO_HEADER_IN, /* 1 */ + CURLINFO_HEADER_OUT, /* 2 */ + CURLINFO_DATA_IN, /* 3 */ + CURLINFO_DATA_OUT, /* 4 */ + CURLINFO_SSL_DATA_IN, /* 5 */ + CURLINFO_SSL_DATA_OUT, /* 6 */ + CURLINFO_END +} curl_infotype; + +typedef int (*curl_debug_callback) + (CURL *handle, /* the handle/transfer this concerns */ + curl_infotype type, /* what kind of data */ + char *data, /* points to the data */ + size_t size, /* size of the data pointed to */ + void *userptr); /* whatever the user please */ + +/* All possible error codes from all sorts of curl functions. Future versions + may return other values, stay prepared. + + Always add new return codes last. Never *EVER* remove any. The return + codes must remain the same! + */ + +typedef enum { + CURLE_OK = 0, + CURLE_UNSUPPORTED_PROTOCOL, /* 1 */ + CURLE_FAILED_INIT, /* 2 */ + CURLE_URL_MALFORMAT, /* 3 */ + CURLE_OBSOLETE4, /* 4 - NOT USED */ + CURLE_COULDNT_RESOLVE_PROXY, /* 5 */ + CURLE_COULDNT_RESOLVE_HOST, /* 6 */ + CURLE_COULDNT_CONNECT, /* 7 */ + CURLE_FTP_WEIRD_SERVER_REPLY, /* 8 */ + CURLE_REMOTE_ACCESS_DENIED, /* 9 a service was denied by the server + due to lack of access - when login fails + this is not returned. */ + CURLE_OBSOLETE10, /* 10 - NOT USED */ + CURLE_FTP_WEIRD_PASS_REPLY, /* 11 */ + CURLE_OBSOLETE12, /* 12 - NOT USED */ + CURLE_FTP_WEIRD_PASV_REPLY, /* 13 */ + CURLE_FTP_WEIRD_227_FORMAT, /* 14 */ + CURLE_FTP_CANT_GET_HOST, /* 15 */ + CURLE_OBSOLETE16, /* 16 - NOT USED */ + CURLE_FTP_COULDNT_SET_TYPE, /* 17 */ + CURLE_PARTIAL_FILE, /* 18 */ + CURLE_FTP_COULDNT_RETR_FILE, /* 19 */ + CURLE_OBSOLETE20, /* 20 - NOT USED */ + CURLE_QUOTE_ERROR, /* 21 - quote command failure */ + CURLE_HTTP_RETURNED_ERROR, /* 22 */ + CURLE_WRITE_ERROR, /* 23 */ + CURLE_OBSOLETE24, /* 24 - NOT USED */ + CURLE_UPLOAD_FAILED, /* 25 - failed upload "command" */ + CURLE_READ_ERROR, /* 26 - couldn't open/read from file */ + CURLE_OUT_OF_MEMORY, /* 27 */ + /* Note: CURLE_OUT_OF_MEMORY may sometimes indicate a conversion error + instead of a memory allocation error if CURL_DOES_CONVERSIONS + is defined + */ + CURLE_OPERATION_TIMEDOUT, /* 28 - the timeout time was reached */ + CURLE_OBSOLETE29, /* 29 - NOT USED */ + CURLE_FTP_PORT_FAILED, /* 30 - FTP PORT operation failed */ + CURLE_FTP_COULDNT_USE_REST, /* 31 - the REST command failed */ + CURLE_OBSOLETE32, /* 32 - NOT USED */ + CURLE_RANGE_ERROR, /* 33 - RANGE "command" didn't work */ + CURLE_HTTP_POST_ERROR, /* 34 */ + CURLE_SSL_CONNECT_ERROR, /* 35 - wrong when connecting with SSL */ + CURLE_BAD_DOWNLOAD_RESUME, /* 36 - couldn't resume download */ + CURLE_FILE_COULDNT_READ_FILE, /* 37 */ + CURLE_LDAP_CANNOT_BIND, /* 38 */ + CURLE_LDAP_SEARCH_FAILED, /* 39 */ + CURLE_OBSOLETE40, /* 40 - NOT USED */ + CURLE_FUNCTION_NOT_FOUND, /* 41 */ + CURLE_ABORTED_BY_CALLBACK, /* 42 */ + CURLE_BAD_FUNCTION_ARGUMENT, /* 43 */ + CURLE_OBSOLETE44, /* 44 - NOT USED */ + CURLE_INTERFACE_FAILED, /* 45 - CURLOPT_INTERFACE failed */ + CURLE_OBSOLETE46, /* 46 - NOT USED */ + CURLE_TOO_MANY_REDIRECTS , /* 47 - catch endless re-direct loops */ + CURLE_UNKNOWN_TELNET_OPTION, /* 48 - User specified an unknown option */ + CURLE_TELNET_OPTION_SYNTAX , /* 49 - Malformed telnet option */ + CURLE_OBSOLETE50, /* 50 - NOT USED */ + CURLE_PEER_FAILED_VERIFICATION, /* 51 - peer's certificate or fingerprint + wasn't verified fine */ + CURLE_GOT_NOTHING, /* 52 - when this is a specific error */ + CURLE_SSL_ENGINE_NOTFOUND, /* 53 - SSL crypto engine not found */ + CURLE_SSL_ENGINE_SETFAILED, /* 54 - can not set SSL crypto engine as + default */ + CURLE_SEND_ERROR, /* 55 - failed sending network data */ + CURLE_RECV_ERROR, /* 56 - failure in receiving network data */ + CURLE_OBSOLETE57, /* 57 - NOT IN USE */ + CURLE_SSL_CERTPROBLEM, /* 58 - problem with the local certificate */ + CURLE_SSL_CIPHER, /* 59 - couldn't use specified cipher */ + CURLE_SSL_CACERT, /* 60 - problem with the CA cert (path?) */ + CURLE_BAD_CONTENT_ENCODING, /* 61 - Unrecognized transfer encoding */ + CURLE_LDAP_INVALID_URL, /* 62 - Invalid LDAP URL */ + CURLE_FILESIZE_EXCEEDED, /* 63 - Maximum file size exceeded */ + CURLE_USE_SSL_FAILED, /* 64 - Requested FTP SSL level failed */ + CURLE_SEND_FAIL_REWIND, /* 65 - Sending the data requires a rewind + that failed */ + CURLE_SSL_ENGINE_INITFAILED, /* 66 - failed to initialise ENGINE */ + CURLE_LOGIN_DENIED, /* 67 - user, password or similar was not + accepted and we failed to login */ + CURLE_TFTP_NOTFOUND, /* 68 - file not found on server */ + CURLE_TFTP_PERM, /* 69 - permission problem on server */ + CURLE_REMOTE_DISK_FULL, /* 70 - out of disk space on server */ + CURLE_TFTP_ILLEGAL, /* 71 - Illegal TFTP operation */ + CURLE_TFTP_UNKNOWNID, /* 72 - Unknown transfer ID */ + CURLE_REMOTE_FILE_EXISTS, /* 73 - File already exists */ + CURLE_TFTP_NOSUCHUSER, /* 74 - No such user */ + CURLE_CONV_FAILED, /* 75 - conversion failed */ + CURLE_CONV_REQD, /* 76 - caller must register conversion + callbacks using curl_easy_setopt options + CURLOPT_CONV_FROM_NETWORK_FUNCTION, + CURLOPT_CONV_TO_NETWORK_FUNCTION, and + CURLOPT_CONV_FROM_UTF8_FUNCTION */ + CURLE_SSL_CACERT_BADFILE, /* 77 - could not load CACERT file, missing + or wrong format */ + CURLE_REMOTE_FILE_NOT_FOUND, /* 78 - remote file not found */ + CURLE_SSH, /* 79 - error from the SSH layer, somewhat + generic so the error message will be of + interest when this has happened */ + + CURLE_SSL_SHUTDOWN_FAILED, /* 80 - Failed to shut down the SSL + connection */ + CURLE_AGAIN, /* 81 - socket is not ready for send/recv, + wait till it's ready and try again (Added + in 7.18.2) */ + CURLE_SSL_CRL_BADFILE, /* 82 - could not load CRL file, missing or + wrong format (Added in 7.19.0) */ + CURLE_SSL_ISSUER_ERROR, /* 83 - Issuer check failed. (Added in + 7.19.0) */ + CURL_LAST /* never use! */ +} CURLcode; + +#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all + the obsolete stuff removed! */ + +/* Backwards compatibility with older names */ + +/* The following were added in 7.17.1 */ +/* These are scheduled to disappear by 2009 */ +#define CURLE_SSL_PEER_CERTIFICATE CURLE_PEER_FAILED_VERIFICATION + +/* The following were added in 7.17.0 */ +/* These are scheduled to disappear by 2009 */ +#define CURLE_OBSOLETE CURLE_OBSOLETE50 /* noone should be using this! */ +#define CURLE_BAD_PASSWORD_ENTERED CURLE_OBSOLETE46 +#define CURLE_BAD_CALLING_ORDER CURLE_OBSOLETE44 +#define CURLE_FTP_USER_PASSWORD_INCORRECT CURLE_OBSOLETE10 +#define CURLE_FTP_CANT_RECONNECT CURLE_OBSOLETE16 +#define CURLE_FTP_COULDNT_GET_SIZE CURLE_OBSOLETE32 +#define CURLE_FTP_COULDNT_SET_ASCII CURLE_OBSOLETE29 +#define CURLE_FTP_WEIRD_USER_REPLY CURLE_OBSOLETE12 +#define CURLE_FTP_WRITE_ERROR CURLE_OBSOLETE20 +#define CURLE_LIBRARY_NOT_FOUND CURLE_OBSOLETE40 +#define CURLE_MALFORMAT_USER CURLE_OBSOLETE24 +#define CURLE_SHARE_IN_USE CURLE_OBSOLETE57 +#define CURLE_URL_MALFORMAT_USER CURLE_OBSOLETE4 + +#define CURLE_FTP_ACCESS_DENIED CURLE_REMOTE_ACCESS_DENIED +#define CURLE_FTP_COULDNT_SET_BINARY CURLE_FTP_COULDNT_SET_TYPE +#define CURLE_FTP_QUOTE_ERROR CURLE_QUOTE_ERROR +#define CURLE_TFTP_DISKFULL CURLE_REMOTE_DISK_FULL +#define CURLE_TFTP_EXISTS CURLE_REMOTE_FILE_EXISTS +#define CURLE_HTTP_RANGE_ERROR CURLE_RANGE_ERROR +#define CURLE_FTP_SSL_FAILED CURLE_USE_SSL_FAILED + +/* The following were added earlier */ + +#define CURLE_OPERATION_TIMEOUTED CURLE_OPERATION_TIMEDOUT + +#define CURLE_HTTP_NOT_FOUND CURLE_HTTP_RETURNED_ERROR +#define CURLE_HTTP_PORT_FAILED CURLE_INTERFACE_FAILED +#define CURLE_FTP_COULDNT_STOR_FILE CURLE_UPLOAD_FAILED + +#define CURLE_FTP_PARTIAL_FILE CURLE_PARTIAL_FILE +#define CURLE_FTP_BAD_DOWNLOAD_RESUME CURLE_BAD_DOWNLOAD_RESUME + +/* This was the error code 50 in 7.7.3 and a few earlier versions, this + is no longer used by libcurl but is instead #defined here only to not + make programs break */ +#define CURLE_ALREADY_COMPLETE 99999 + +#endif /*!CURL_NO_OLDIES*/ + +/* This prototype applies to all conversion callbacks */ +typedef CURLcode (*curl_conv_callback)(char *buffer, size_t length); + +typedef CURLcode (*curl_ssl_ctx_callback)(CURL *curl, /* easy handle */ + void *ssl_ctx, /* actually an + OpenSSL SSL_CTX */ + void *userptr); + +typedef enum { + CURLPROXY_HTTP = 0, /* added in 7.10, new in 7.19.4 default is to use + CONNECT HTTP/1.1 */ + CURLPROXY_HTTP_1_0 = 1, /* added in 7.19.4, force to use CONNECT + HTTP/1.0 */ + CURLPROXY_SOCKS4 = 4, /* support added in 7.15.2, enum existed already + in 7.10 */ + CURLPROXY_SOCKS5 = 5, /* added in 7.10 */ + CURLPROXY_SOCKS4A = 6, /* added in 7.18.0 */ + CURLPROXY_SOCKS5_HOSTNAME = 7 /* Use the SOCKS5 protocol but pass along the + host name rather than the IP address. added + in 7.18.0 */ +} curl_proxytype; /* this enum was added in 7.10 */ + +#define CURLAUTH_NONE 0 /* nothing */ +#define CURLAUTH_BASIC (1<<0) /* Basic (default) */ +#define CURLAUTH_DIGEST (1<<1) /* Digest */ +#define CURLAUTH_GSSNEGOTIATE (1<<2) /* GSS-Negotiate */ +#define CURLAUTH_NTLM (1<<3) /* NTLM */ +#define CURLAUTH_DIGEST_IE (1<<4) /* Digest with IE flavour */ +#define CURLAUTH_ANY (~CURLAUTH_DIGEST_IE) /* all fine types set */ +#define CURLAUTH_ANYSAFE (~(CURLAUTH_BASIC|CURLAUTH_DIGEST_IE)) + +#define CURLSSH_AUTH_ANY ~0 /* all types supported by the server */ +#define CURLSSH_AUTH_NONE 0 /* none allowed, silly but complete */ +#define CURLSSH_AUTH_PUBLICKEY (1<<0) /* public/private key files */ +#define CURLSSH_AUTH_PASSWORD (1<<1) /* password */ +#define CURLSSH_AUTH_HOST (1<<2) /* host key files */ +#define CURLSSH_AUTH_KEYBOARD (1<<3) /* keyboard interactive */ +#define CURLSSH_AUTH_DEFAULT CURLSSH_AUTH_ANY + +#define CURL_ERROR_SIZE 256 + +struct curl_khkey { + const char *key; /* points to a zero-terminated string encoded with base64 + if len is zero, otherwise to the "raw" data */ + size_t len; + enum type { + CURLKHTYPE_UNKNOWN, + CURLKHTYPE_RSA1, + CURLKHTYPE_RSA, + CURLKHTYPE_DSS + } keytype; +}; + +/* this is the set of return values expected from the curl_sshkeycallback + callback */ +enum curl_khstat { + CURLKHSTAT_FINE_ADD_TO_FILE, + CURLKHSTAT_FINE, + CURLKHSTAT_REJECT, /* reject the connection, return an error */ + CURLKHSTAT_DEFER, /* do not accept it, but we can't answer right now so + this causes a CURLE_DEFER error but otherwise the + connection will be left intact etc */ + CURLKHSTAT_LAST /* not for use, only a marker for last-in-list */ +}; + +/* this is the set of status codes pass in to the callback */ +enum curl_khmatch { + CURLKHMATCH_OK, /* match */ + CURLKHMATCH_MISMATCH, /* host found, key mismatch! */ + CURLKHMATCH_MISSING, /* no matching host/key found */ + CURLKHMATCH_LAST /* not for use, only a marker for last-in-list */ +}; + +typedef int + (*curl_sshkeycallback) (CURL *easy, /* easy handle */ + const struct curl_khkey *knownkey, /* known */ + const struct curl_khkey *foundkey, /* found */ + enum curl_khmatch, /* libcurl's view on the keys */ + void *clientp); /* custom pointer passed from app */ + +/* parameter for the CURLOPT_USE_SSL option */ +typedef enum { + CURLUSESSL_NONE, /* do not attempt to use SSL */ + CURLUSESSL_TRY, /* try using SSL, proceed anyway otherwise */ + CURLUSESSL_CONTROL, /* SSL for the control connection or fail */ + CURLUSESSL_ALL, /* SSL for all communication or fail */ + CURLUSESSL_LAST /* not an option, never use */ +} curl_usessl; + +#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all + the obsolete stuff removed! */ + +/* Backwards compatibility with older names */ +/* These are scheduled to disappear by 2009 */ + +#define CURLFTPSSL_NONE CURLUSESSL_NONE +#define CURLFTPSSL_TRY CURLUSESSL_TRY +#define CURLFTPSSL_CONTROL CURLUSESSL_CONTROL +#define CURLFTPSSL_ALL CURLUSESSL_ALL +#define CURLFTPSSL_LAST CURLUSESSL_LAST +#define curl_ftpssl curl_usessl +#endif /*!CURL_NO_OLDIES*/ + +/* parameter for the CURLOPT_FTP_SSL_CCC option */ +typedef enum { + CURLFTPSSL_CCC_NONE, /* do not send CCC */ + CURLFTPSSL_CCC_PASSIVE, /* Let the server initiate the shutdown */ + CURLFTPSSL_CCC_ACTIVE, /* Initiate the shutdown */ + CURLFTPSSL_CCC_LAST /* not an option, never use */ +} curl_ftpccc; + +/* parameter for the CURLOPT_FTPSSLAUTH option */ +typedef enum { + CURLFTPAUTH_DEFAULT, /* let libcurl decide */ + CURLFTPAUTH_SSL, /* use "AUTH SSL" */ + CURLFTPAUTH_TLS, /* use "AUTH TLS" */ + CURLFTPAUTH_LAST /* not an option, never use */ +} curl_ftpauth; + +/* parameter for the CURLOPT_FTP_CREATE_MISSING_DIRS option */ +typedef enum { + CURLFTP_CREATE_DIR_NONE, /* do NOT create missing dirs! */ + CURLFTP_CREATE_DIR, /* (FTP/SFTP) if CWD fails, try MKD and then CWD + again if MKD succeeded, for SFTP this does + similar magic */ + CURLFTP_CREATE_DIR_RETRY, /* (FTP only) if CWD fails, try MKD and then CWD + again even if MKD failed! */ + CURLFTP_CREATE_DIR_LAST /* not an option, never use */ +} curl_ftpcreatedir; + +/* parameter for the CURLOPT_FTP_FILEMETHOD option */ +typedef enum { + CURLFTPMETHOD_DEFAULT, /* let libcurl pick */ + CURLFTPMETHOD_MULTICWD, /* single CWD operation for each path part */ + CURLFTPMETHOD_NOCWD, /* no CWD at all */ + CURLFTPMETHOD_SINGLECWD, /* one CWD to full dir, then work on file */ + CURLFTPMETHOD_LAST /* not an option, never use */ +} curl_ftpmethod; + +/* CURLPROTO_ defines are for the CURLOPT_*PROTOCOLS options */ +#define CURLPROTO_HTTP (1<<0) +#define CURLPROTO_HTTPS (1<<1) +#define CURLPROTO_FTP (1<<2) +#define CURLPROTO_FTPS (1<<3) +#define CURLPROTO_SCP (1<<4) +#define CURLPROTO_SFTP (1<<5) +#define CURLPROTO_TELNET (1<<6) +#define CURLPROTO_LDAP (1<<7) +#define CURLPROTO_LDAPS (1<<8) +#define CURLPROTO_DICT (1<<9) +#define CURLPROTO_FILE (1<<10) +#define CURLPROTO_TFTP (1<<11) +#define CURLPROTO_ALL (~0) /* enable everything */ + +/* long may be 32 or 64 bits, but we should never depend on anything else + but 32 */ +#define CURLOPTTYPE_LONG 0 +#define CURLOPTTYPE_OBJECTPOINT 10000 +#define CURLOPTTYPE_FUNCTIONPOINT 20000 +#define CURLOPTTYPE_OFF_T 30000 + +/* name is uppercase CURLOPT_, + type is one of the defined CURLOPTTYPE_ + number is unique identifier */ +#ifdef CINIT +#undef CINIT +#endif + +#ifdef CURL_ISOCPP +#define CINIT(name,type,number) CURLOPT_ ## name = CURLOPTTYPE_ ## type + number +#else +/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */ +#define LONG CURLOPTTYPE_LONG +#define OBJECTPOINT CURLOPTTYPE_OBJECTPOINT +#define FUNCTIONPOINT CURLOPTTYPE_FUNCTIONPOINT +#define OFF_T CURLOPTTYPE_OFF_T +#define CINIT(name,type,number) CURLOPT_/**/name = type + number +#endif + +/* + * This macro-mania below setups the CURLOPT_[what] enum, to be used with + * curl_easy_setopt(). The first argument in the CINIT() macro is the [what] + * word. + */ + +typedef enum { + /* This is the FILE * or void * the regular output should be written to. */ + CINIT(FILE, OBJECTPOINT, 1), + + /* The full URL to get/put */ + CINIT(URL, OBJECTPOINT, 2), + + /* Port number to connect to, if other than default. */ + CINIT(PORT, LONG, 3), + + /* Name of proxy to use. */ + CINIT(PROXY, OBJECTPOINT, 4), + + /* "name:password" to use when fetching. */ + CINIT(USERPWD, OBJECTPOINT, 5), + + /* "name:password" to use with proxy. */ + CINIT(PROXYUSERPWD, OBJECTPOINT, 6), + + /* Range to get, specified as an ASCII string. */ + CINIT(RANGE, OBJECTPOINT, 7), + + /* not used */ + + /* Specified file stream to upload from (use as input): */ + CINIT(INFILE, OBJECTPOINT, 9), + + /* Buffer to receive error messages in, must be at least CURL_ERROR_SIZE + * bytes big. If this is not used, error messages go to stderr instead: */ + CINIT(ERRORBUFFER, OBJECTPOINT, 10), + + /* Function that will be called to store the output (instead of fwrite). The + * parameters will use fwrite() syntax, make sure to follow them. */ + CINIT(WRITEFUNCTION, FUNCTIONPOINT, 11), + + /* Function that will be called to read the input (instead of fread). The + * parameters will use fread() syntax, make sure to follow them. */ + CINIT(READFUNCTION, FUNCTIONPOINT, 12), + + /* Time-out the read operation after this amount of seconds */ + CINIT(TIMEOUT, LONG, 13), + + /* If the CURLOPT_INFILE is used, this can be used to inform libcurl about + * how large the file being sent really is. That allows better error + * checking and better verifies that the upload was successful. -1 means + * unknown size. + * + * For large file support, there is also a _LARGE version of the key + * which takes an off_t type, allowing platforms with larger off_t + * sizes to handle larger files. See below for INFILESIZE_LARGE. + */ + CINIT(INFILESIZE, LONG, 14), + + /* POST static input fields. */ + CINIT(POSTFIELDS, OBJECTPOINT, 15), + + /* Set the referrer page (needed by some CGIs) */ + CINIT(REFERER, OBJECTPOINT, 16), + + /* Set the FTP PORT string (interface name, named or numerical IP address) + Use i.e '-' to use default address. */ + CINIT(FTPPORT, OBJECTPOINT, 17), + + /* Set the User-Agent string (examined by some CGIs) */ + CINIT(USERAGENT, OBJECTPOINT, 18), + + /* If the download receives less than "low speed limit" bytes/second + * during "low speed time" seconds, the operations is aborted. + * You could i.e if you have a pretty high speed connection, abort if + * it is less than 2000 bytes/sec during 20 seconds. + */ + + /* Set the "low speed limit" */ + CINIT(LOW_SPEED_LIMIT, LONG, 19), + + /* Set the "low speed time" */ + CINIT(LOW_SPEED_TIME, LONG, 20), + + /* Set the continuation offset. + * + * Note there is also a _LARGE version of this key which uses + * off_t types, allowing for large file offsets on platforms which + * use larger-than-32-bit off_t's. Look below for RESUME_FROM_LARGE. + */ + CINIT(RESUME_FROM, LONG, 21), + + /* Set cookie in request: */ + CINIT(COOKIE, OBJECTPOINT, 22), + + /* This points to a linked list of headers, struct curl_slist kind */ + CINIT(HTTPHEADER, OBJECTPOINT, 23), + + /* This points to a linked list of post entries, struct curl_httppost */ + CINIT(HTTPPOST, OBJECTPOINT, 24), + + /* name of the file keeping your private SSL-certificate */ + CINIT(SSLCERT, OBJECTPOINT, 25), + + /* password for the SSL or SSH private key */ + CINIT(KEYPASSWD, OBJECTPOINT, 26), + + /* send TYPE parameter? */ + CINIT(CRLF, LONG, 27), + + /* send linked-list of QUOTE commands */ + CINIT(QUOTE, OBJECTPOINT, 28), + + /* send FILE * or void * to store headers to, if you use a callback it + is simply passed to the callback unmodified */ + CINIT(WRITEHEADER, OBJECTPOINT, 29), + + /* point to a file to read the initial cookies from, also enables + "cookie awareness" */ + CINIT(COOKIEFILE, OBJECTPOINT, 31), + + /* What version to specifically try to use. + See CURL_SSLVERSION defines below. */ + CINIT(SSLVERSION, LONG, 32), + + /* What kind of HTTP time condition to use, see defines */ + CINIT(TIMECONDITION, LONG, 33), + + /* Time to use with the above condition. Specified in number of seconds + since 1 Jan 1970 */ + CINIT(TIMEVALUE, LONG, 34), + + /* 35 = OBSOLETE */ + + /* Custom request, for customizing the get command like + HTTP: DELETE, TRACE and others + FTP: to use a different list command + */ + CINIT(CUSTOMREQUEST, OBJECTPOINT, 36), + + /* HTTP request, for odd commands like DELETE, TRACE and others */ + CINIT(STDERR, OBJECTPOINT, 37), + + /* 38 is not used */ + + /* send linked-list of post-transfer QUOTE commands */ + CINIT(POSTQUOTE, OBJECTPOINT, 39), + + /* Pass a pointer to string of the output using full variable-replacement + as described elsewhere. */ + CINIT(WRITEINFO, OBJECTPOINT, 40), + + CINIT(VERBOSE, LONG, 41), /* talk a lot */ + CINIT(HEADER, LONG, 42), /* throw the header out too */ + CINIT(NOPROGRESS, LONG, 43), /* shut off the progress meter */ + CINIT(NOBODY, LONG, 44), /* use HEAD to get http document */ + CINIT(FAILONERROR, LONG, 45), /* no output on http error codes >= 300 */ + CINIT(UPLOAD, LONG, 46), /* this is an upload */ + CINIT(POST, LONG, 47), /* HTTP POST method */ + CINIT(DIRLISTONLY, LONG, 48), /* return bare names when listing directories */ + + CINIT(APPEND, LONG, 50), /* Append instead of overwrite on upload! */ + + /* Specify whether to read the user+password from the .netrc or the URL. + * This must be one of the CURL_NETRC_* enums below. */ + CINIT(NETRC, LONG, 51), + + CINIT(FOLLOWLOCATION, LONG, 52), /* use Location: Luke! */ + + CINIT(TRANSFERTEXT, LONG, 53), /* transfer data in text/ASCII format */ + CINIT(PUT, LONG, 54), /* HTTP PUT */ + + /* 55 = OBSOLETE */ + + /* Function that will be called instead of the internal progress display + * function. This function should be defined as the curl_progress_callback + * prototype defines. */ + CINIT(PROGRESSFUNCTION, FUNCTIONPOINT, 56), + + /* Data passed to the progress callback */ + CINIT(PROGRESSDATA, OBJECTPOINT, 57), + + /* We want the referrer field set automatically when following locations */ + CINIT(AUTOREFERER, LONG, 58), + + /* Port of the proxy, can be set in the proxy string as well with: + "[host]:[port]" */ + CINIT(PROXYPORT, LONG, 59), + + /* size of the POST input data, if strlen() is not good to use */ + CINIT(POSTFIELDSIZE, LONG, 60), + + /* tunnel non-http operations through a HTTP proxy */ + CINIT(HTTPPROXYTUNNEL, LONG, 61), + + /* Set the interface string to use as outgoing network interface */ + CINIT(INTERFACE, OBJECTPOINT, 62), + + /* Set the krb4/5 security level, this also enables krb4/5 awareness. This + * is a string, 'clear', 'safe', 'confidential' or 'private'. If the string + * is set but doesn't match one of these, 'private' will be used. */ + CINIT(KRBLEVEL, OBJECTPOINT, 63), + + /* Set if we should verify the peer in ssl handshake, set 1 to verify. */ + CINIT(SSL_VERIFYPEER, LONG, 64), + + /* The CApath or CAfile used to validate the peer certificate + this option is used only if SSL_VERIFYPEER is true */ + CINIT(CAINFO, OBJECTPOINT, 65), + + /* 66 = OBSOLETE */ + /* 67 = OBSOLETE */ + + /* Maximum number of http redirects to follow */ + CINIT(MAXREDIRS, LONG, 68), + + /* Pass a long set to 1 to get the date of the requested document (if + possible)! Pass a zero to shut it off. */ + CINIT(FILETIME, LONG, 69), + + /* This points to a linked list of telnet options */ + CINIT(TELNETOPTIONS, OBJECTPOINT, 70), + + /* Max amount of cached alive connections */ + CINIT(MAXCONNECTS, LONG, 71), + + /* What policy to use when closing connections when the cache is filled + up */ + CINIT(CLOSEPOLICY, LONG, 72), + + /* 73 = OBSOLETE */ + + /* Set to explicitly use a new connection for the upcoming transfer. + Do not use this unless you're absolutely sure of this, as it makes the + operation slower and is less friendly for the network. */ + CINIT(FRESH_CONNECT, LONG, 74), + + /* Set to explicitly forbid the upcoming transfer's connection to be re-used + when done. Do not use this unless you're absolutely sure of this, as it + makes the operation slower and is less friendly for the network. */ + CINIT(FORBID_REUSE, LONG, 75), + + /* Set to a file name that contains random data for libcurl to use to + seed the random engine when doing SSL connects. */ + CINIT(RANDOM_FILE, OBJECTPOINT, 76), + + /* Set to the Entropy Gathering Daemon socket pathname */ + CINIT(EGDSOCKET, OBJECTPOINT, 77), + + /* Time-out connect operations after this amount of seconds, if connects + are OK within this time, then fine... This only aborts the connect + phase. [Only works on unix-style/SIGALRM operating systems] */ + CINIT(CONNECTTIMEOUT, LONG, 78), + + /* Function that will be called to store headers (instead of fwrite). The + * parameters will use fwrite() syntax, make sure to follow them. */ + CINIT(HEADERFUNCTION, FUNCTIONPOINT, 79), + + /* Set this to force the HTTP request to get back to GET. Only really usable + if POST, PUT or a custom request have been used first. + */ + CINIT(HTTPGET, LONG, 80), + + /* Set if we should verify the Common name from the peer certificate in ssl + * handshake, set 1 to check existence, 2 to ensure that it matches the + * provided hostname. */ + CINIT(SSL_VERIFYHOST, LONG, 81), + + /* Specify which file name to write all known cookies in after completed + operation. Set file name to "-" (dash) to make it go to stdout. */ + CINIT(COOKIEJAR, OBJECTPOINT, 82), + + /* Specify which SSL ciphers to use */ + CINIT(SSL_CIPHER_LIST, OBJECTPOINT, 83), + + /* Specify which HTTP version to use! This must be set to one of the + CURL_HTTP_VERSION* enums set below. */ + CINIT(HTTP_VERSION, LONG, 84), + + /* Specifically switch on or off the FTP engine's use of the EPSV command. By + default, that one will always be attempted before the more traditional + PASV command. */ + CINIT(FTP_USE_EPSV, LONG, 85), + + /* type of the file keeping your SSL-certificate ("DER", "PEM", "ENG") */ + CINIT(SSLCERTTYPE, OBJECTPOINT, 86), + + /* name of the file keeping your private SSL-key */ + CINIT(SSLKEY, OBJECTPOINT, 87), + + /* type of the file keeping your private SSL-key ("DER", "PEM", "ENG") */ + CINIT(SSLKEYTYPE, OBJECTPOINT, 88), + + /* crypto engine for the SSL-sub system */ + CINIT(SSLENGINE, OBJECTPOINT, 89), + + /* set the crypto engine for the SSL-sub system as default + the param has no meaning... + */ + CINIT(SSLENGINE_DEFAULT, LONG, 90), + + /* Non-zero value means to use the global dns cache */ + CINIT(DNS_USE_GLOBAL_CACHE, LONG, 91), /* To become OBSOLETE soon */ + + /* DNS cache timeout */ + CINIT(DNS_CACHE_TIMEOUT, LONG, 92), + + /* send linked-list of pre-transfer QUOTE commands */ + CINIT(PREQUOTE, OBJECTPOINT, 93), + + /* set the debug function */ + CINIT(DEBUGFUNCTION, FUNCTIONPOINT, 94), + + /* set the data for the debug function */ + CINIT(DEBUGDATA, OBJECTPOINT, 95), + + /* mark this as start of a cookie session */ + CINIT(COOKIESESSION, LONG, 96), + + /* The CApath directory used to validate the peer certificate + this option is used only if SSL_VERIFYPEER is true */ + CINIT(CAPATH, OBJECTPOINT, 97), + + /* Instruct libcurl to use a smaller receive buffer */ + CINIT(BUFFERSIZE, LONG, 98), + + /* Instruct libcurl to not use any signal/alarm handlers, even when using + timeouts. This option is useful for multi-threaded applications. + See libcurl-the-guide for more background information. */ + CINIT(NOSIGNAL, LONG, 99), + + /* Provide a CURLShare for mutexing non-ts data */ + CINIT(SHARE, OBJECTPOINT, 100), + + /* indicates type of proxy. accepted values are CURLPROXY_HTTP (default), + CURLPROXY_SOCKS4, CURLPROXY_SOCKS4A and CURLPROXY_SOCKS5. */ + CINIT(PROXYTYPE, LONG, 101), + + /* Set the Accept-Encoding string. Use this to tell a server you would like + the response to be compressed. */ + CINIT(ENCODING, OBJECTPOINT, 102), + + /* Set pointer to private data */ + CINIT(PRIVATE, OBJECTPOINT, 103), + + /* Set aliases for HTTP 200 in the HTTP Response header */ + CINIT(HTTP200ALIASES, OBJECTPOINT, 104), + + /* Continue to send authentication (user+password) when following locations, + even when hostname changed. This can potentially send off the name + and password to whatever host the server decides. */ + CINIT(UNRESTRICTED_AUTH, LONG, 105), + + /* Specifically switch on or off the FTP engine's use of the EPRT command ( it + also disables the LPRT attempt). By default, those ones will always be + attempted before the good old traditional PORT command. */ + CINIT(FTP_USE_EPRT, LONG, 106), + + /* Set this to a bitmask value to enable the particular authentications + methods you like. Use this in combination with CURLOPT_USERPWD. + Note that setting multiple bits may cause extra network round-trips. */ + CINIT(HTTPAUTH, LONG, 107), + + /* Set the ssl context callback function, currently only for OpenSSL ssl_ctx + in second argument. The function must be matching the + curl_ssl_ctx_callback proto. */ + CINIT(SSL_CTX_FUNCTION, FUNCTIONPOINT, 108), + + /* Set the userdata for the ssl context callback function's third + argument */ + CINIT(SSL_CTX_DATA, OBJECTPOINT, 109), + + /* FTP Option that causes missing dirs to be created on the remote server. + In 7.19.4 we introduced the convenience enums for this option using the + CURLFTP_CREATE_DIR prefix. + */ + CINIT(FTP_CREATE_MISSING_DIRS, LONG, 110), + + /* Set this to a bitmask value to enable the particular authentications + methods you like. Use this in combination with CURLOPT_PROXYUSERPWD. + Note that setting multiple bits may cause extra network round-trips. */ + CINIT(PROXYAUTH, LONG, 111), + + /* FTP option that changes the timeout, in seconds, associated with + getting a response. This is different from transfer timeout time and + essentially places a demand on the FTP server to acknowledge commands + in a timely manner. */ + CINIT(FTP_RESPONSE_TIMEOUT, LONG, 112), + + /* Set this option to one of the CURL_IPRESOLVE_* defines (see below) to + tell libcurl to resolve names to those IP versions only. This only has + affect on systems with support for more than one, i.e IPv4 _and_ IPv6. */ + CINIT(IPRESOLVE, LONG, 113), + + /* Set this option to limit the size of a file that will be downloaded from + an HTTP or FTP server. + + Note there is also _LARGE version which adds large file support for + platforms which have larger off_t sizes. See MAXFILESIZE_LARGE below. */ + CINIT(MAXFILESIZE, LONG, 114), + + /* See the comment for INFILESIZE above, but in short, specifies + * the size of the file being uploaded. -1 means unknown. + */ + CINIT(INFILESIZE_LARGE, OFF_T, 115), + + /* Sets the continuation offset. There is also a LONG version of this; + * look above for RESUME_FROM. + */ + CINIT(RESUME_FROM_LARGE, OFF_T, 116), + + /* Sets the maximum size of data that will be downloaded from + * an HTTP or FTP server. See MAXFILESIZE above for the LONG version. + */ + CINIT(MAXFILESIZE_LARGE, OFF_T, 117), + + /* Set this option to the file name of your .netrc file you want libcurl + to parse (using the CURLOPT_NETRC option). If not set, libcurl will do + a poor attempt to find the user's home directory and check for a .netrc + file in there. */ + CINIT(NETRC_FILE, OBJECTPOINT, 118), + + /* Enable SSL/TLS for FTP, pick one of: + CURLFTPSSL_TRY - try using SSL, proceed anyway otherwise + CURLFTPSSL_CONTROL - SSL for the control connection or fail + CURLFTPSSL_ALL - SSL for all communication or fail + */ + CINIT(USE_SSL, LONG, 119), + + /* The _LARGE version of the standard POSTFIELDSIZE option */ + CINIT(POSTFIELDSIZE_LARGE, OFF_T, 120), + + /* Enable/disable the TCP Nagle algorithm */ + CINIT(TCP_NODELAY, LONG, 121), + + /* 122 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 123 OBSOLETE. Gone in 7.16.0 */ + /* 124 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 125 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 126 OBSOLETE, used in 7.12.3. Gone in 7.13.0 */ + /* 127 OBSOLETE. Gone in 7.16.0 */ + /* 128 OBSOLETE. Gone in 7.16.0 */ + + /* When FTP over SSL/TLS is selected (with CURLOPT_USE_SSL), this option + can be used to change libcurl's default action which is to first try + "AUTH SSL" and then "AUTH TLS" in this order, and proceed when a OK + response has been received. + + Available parameters are: + CURLFTPAUTH_DEFAULT - let libcurl decide + CURLFTPAUTH_SSL - try "AUTH SSL" first, then TLS + CURLFTPAUTH_TLS - try "AUTH TLS" first, then SSL + */ + CINIT(FTPSSLAUTH, LONG, 129), + + CINIT(IOCTLFUNCTION, FUNCTIONPOINT, 130), + CINIT(IOCTLDATA, OBJECTPOINT, 131), + + /* 132 OBSOLETE. Gone in 7.16.0 */ + /* 133 OBSOLETE. Gone in 7.16.0 */ + + /* zero terminated string for pass on to the FTP server when asked for + "account" info */ + CINIT(FTP_ACCOUNT, OBJECTPOINT, 134), + + /* feed cookies into cookie engine */ + CINIT(COOKIELIST, OBJECTPOINT, 135), + + /* ignore Content-Length */ + CINIT(IGNORE_CONTENT_LENGTH, LONG, 136), + + /* Set to non-zero to skip the IP address received in a 227 PASV FTP server + response. Typically used for FTP-SSL purposes but is not restricted to + that. libcurl will then instead use the same IP address it used for the + control connection. */ + CINIT(FTP_SKIP_PASV_IP, LONG, 137), + + /* Select "file method" to use when doing FTP, see the curl_ftpmethod + above. */ + CINIT(FTP_FILEMETHOD, LONG, 138), + + /* Local port number to bind the socket to */ + CINIT(LOCALPORT, LONG, 139), + + /* Number of ports to try, including the first one set with LOCALPORT. + Thus, setting it to 1 will make no additional attempts but the first. + */ + CINIT(LOCALPORTRANGE, LONG, 140), + + /* no transfer, set up connection and let application use the socket by + extracting it with CURLINFO_LASTSOCKET */ + CINIT(CONNECT_ONLY, LONG, 141), + + /* Function that will be called to convert from the + network encoding (instead of using the iconv calls in libcurl) */ + CINIT(CONV_FROM_NETWORK_FUNCTION, FUNCTIONPOINT, 142), + + /* Function that will be called to convert to the + network encoding (instead of using the iconv calls in libcurl) */ + CINIT(CONV_TO_NETWORK_FUNCTION, FUNCTIONPOINT, 143), + + /* Function that will be called to convert from UTF8 + (instead of using the iconv calls in libcurl) + Note that this is used only for SSL certificate processing */ + CINIT(CONV_FROM_UTF8_FUNCTION, FUNCTIONPOINT, 144), + + /* if the connection proceeds too quickly then need to slow it down */ + /* limit-rate: maximum number of bytes per second to send or receive */ + CINIT(MAX_SEND_SPEED_LARGE, OFF_T, 145), + CINIT(MAX_RECV_SPEED_LARGE, OFF_T, 146), + + /* Pointer to command string to send if USER/PASS fails. */ + CINIT(FTP_ALTERNATIVE_TO_USER, OBJECTPOINT, 147), + + /* callback function for setting socket options */ + CINIT(SOCKOPTFUNCTION, FUNCTIONPOINT, 148), + CINIT(SOCKOPTDATA, OBJECTPOINT, 149), + + /* set to 0 to disable session ID re-use for this transfer, default is + enabled (== 1) */ + CINIT(SSL_SESSIONID_CACHE, LONG, 150), + + /* allowed SSH authentication methods */ + CINIT(SSH_AUTH_TYPES, LONG, 151), + + /* Used by scp/sftp to do public/private key authentication */ + CINIT(SSH_PUBLIC_KEYFILE, OBJECTPOINT, 152), + CINIT(SSH_PRIVATE_KEYFILE, OBJECTPOINT, 153), + + /* Send CCC (Clear Command Channel) after authentication */ + CINIT(FTP_SSL_CCC, LONG, 154), + + /* Same as TIMEOUT and CONNECTTIMEOUT, but with ms resolution */ + CINIT(TIMEOUT_MS, LONG, 155), + CINIT(CONNECTTIMEOUT_MS, LONG, 156), + + /* set to zero to disable the libcurl's decoding and thus pass the raw body + data to the application even when it is encoded/compressed */ + CINIT(HTTP_TRANSFER_DECODING, LONG, 157), + CINIT(HTTP_CONTENT_DECODING, LONG, 158), + + /* Permission used when creating new files and directories on the remote + server for protocols that support it, SFTP/SCP/FILE */ + CINIT(NEW_FILE_PERMS, LONG, 159), + CINIT(NEW_DIRECTORY_PERMS, LONG, 160), + + /* Set the behaviour of POST when redirecting. Values must be set to one + of CURL_REDIR* defines below. This used to be called CURLOPT_POST301 */ + CINIT(POSTREDIR, LONG, 161), + + /* used by scp/sftp to verify the host's public key */ + CINIT(SSH_HOST_PUBLIC_KEY_MD5, OBJECTPOINT, 162), + + /* Callback function for opening socket (instead of socket(2)). Optionally, + callback is able change the address or refuse to connect returning + CURL_SOCKET_BAD. The callback should have type + curl_opensocket_callback */ + CINIT(OPENSOCKETFUNCTION, FUNCTIONPOINT, 163), + CINIT(OPENSOCKETDATA, OBJECTPOINT, 164), + + /* POST volatile input fields. */ + CINIT(COPYPOSTFIELDS, OBJECTPOINT, 165), + + /* set transfer mode (;type=) when doing FTP via an HTTP proxy */ + CINIT(PROXY_TRANSFER_MODE, LONG, 166), + + /* Callback function for seeking in the input stream */ + CINIT(SEEKFUNCTION, FUNCTIONPOINT, 167), + CINIT(SEEKDATA, OBJECTPOINT, 168), + + /* CRL file */ + CINIT(CRLFILE, OBJECTPOINT, 169), + + /* Issuer certificate */ + CINIT(ISSUERCERT, OBJECTPOINT, 170), + + /* (IPv6) Address scope */ + CINIT(ADDRESS_SCOPE, LONG, 171), + + /* Collect certificate chain info and allow it to get retrievable with + CURLINFO_CERTINFO after the transfer is complete. (Unfortunately) only + working with OpenSSL-powered builds. */ + CINIT(CERTINFO, LONG, 172), + + /* "name" and "pwd" to use when fetching. */ + CINIT(USERNAME, OBJECTPOINT, 173), + CINIT(PASSWORD, OBJECTPOINT, 174), + + /* "name" and "pwd" to use with Proxy when fetching. */ + CINIT(PROXYUSERNAME, OBJECTPOINT, 175), + CINIT(PROXYPASSWORD, OBJECTPOINT, 176), + + /* Comma separated list of hostnames defining no-proxy zones. These should + match both hostnames directly, and hostnames within a domain. For + example, local.com will match local.com and www.local.com, but NOT + notlocal.com or www.notlocal.com. For compatibility with other + implementations of this, .local.com will be considered to be the same as + local.com. A single * is the only valid wildcard, and effectively + disables the use of proxy. */ + CINIT(NOPROXY, OBJECTPOINT, 177), + + /* block size for TFTP transfers */ + CINIT(TFTP_BLKSIZE, LONG, 178), + + /* Socks Service */ + CINIT(SOCKS5_GSSAPI_SERVICE, OBJECTPOINT, 179), + + /* Socks Service */ + CINIT(SOCKS5_GSSAPI_NEC, LONG, 180), + + /* set the bitmask for the protocols that are allowed to be used for the + transfer, which thus helps the app which takes URLs from users or other + external inputs and want to restrict what protocol(s) to deal + with. Defaults to CURLPROTO_ALL. */ + CINIT(PROTOCOLS, LONG, 181), + + /* set the bitmask for the protocols that libcurl is allowed to follow to, + as a subset of the CURLOPT_PROTOCOLS ones. That means the protocol needs + to be set in both bitmasks to be allowed to get redirected to. Defaults + to all protocols except FILE and SCP. */ + CINIT(REDIR_PROTOCOLS, LONG, 182), + + /* set the SSH knownhost file name to use */ + CINIT(SSH_KNOWNHOSTS, OBJECTPOINT, 183), + + /* set the SSH host key callback, must point to a curl_sshkeycallback + function */ + CINIT(SSH_KEYFUNCTION, FUNCTIONPOINT, 184), + + /* set the SSH host key callback custom pointer */ + CINIT(SSH_KEYDATA, OBJECTPOINT, 185), + + CURLOPT_LASTENTRY /* the last unused */ +} CURLoption; + +#ifndef CURL_NO_OLDIES /* define this to test if your app builds with all + the obsolete stuff removed! */ + +/* Backwards compatibility with older names */ +/* These are scheduled to disappear by 2011 */ + +/* This was added in version 7.19.1 */ +#define CURLOPT_POST301 CURLOPT_POSTREDIR + +/* These are scheduled to disappear by 2009 */ + +/* The following were added in 7.17.0 */ +#define CURLOPT_SSLKEYPASSWD CURLOPT_KEYPASSWD +#define CURLOPT_FTPAPPEND CURLOPT_APPEND +#define CURLOPT_FTPLISTONLY CURLOPT_DIRLISTONLY +#define CURLOPT_FTP_SSL CURLOPT_USE_SSL + +/* The following were added earlier */ + +#define CURLOPT_SSLCERTPASSWD CURLOPT_KEYPASSWD +#define CURLOPT_KRB4LEVEL CURLOPT_KRBLEVEL + +#else +/* This is set if CURL_NO_OLDIES is defined at compile-time */ +#undef CURLOPT_DNS_USE_GLOBAL_CACHE /* soon obsolete */ +#endif + + + /* Below here follows defines for the CURLOPT_IPRESOLVE option. If a host + name resolves addresses using more than one IP protocol version, this + option might be handy to force libcurl to use a specific IP version. */ +#define CURL_IPRESOLVE_WHATEVER 0 /* default, resolves addresses to all IP + versions that your system allows */ +#define CURL_IPRESOLVE_V4 1 /* resolve to ipv4 addresses */ +#define CURL_IPRESOLVE_V6 2 /* resolve to ipv6 addresses */ + + /* three convenient "aliases" that follow the name scheme better */ +#define CURLOPT_WRITEDATA CURLOPT_FILE +#define CURLOPT_READDATA CURLOPT_INFILE +#define CURLOPT_HEADERDATA CURLOPT_WRITEHEADER + + /* These enums are for use with the CURLOPT_HTTP_VERSION option. */ +enum { + CURL_HTTP_VERSION_NONE, /* setting this means we don't care, and that we'd + like the library to choose the best possible + for us! */ + CURL_HTTP_VERSION_1_0, /* please use HTTP 1.0 in the request */ + CURL_HTTP_VERSION_1_1, /* please use HTTP 1.1 in the request */ + + CURL_HTTP_VERSION_LAST /* *ILLEGAL* http version */ +}; + + /* These enums are for use with the CURLOPT_NETRC option. */ +enum CURL_NETRC_OPTION { + CURL_NETRC_IGNORED, /* The .netrc will never be read. + * This is the default. */ + CURL_NETRC_OPTIONAL, /* A user:password in the URL will be preferred + * to one in the .netrc. */ + CURL_NETRC_REQUIRED, /* A user:password in the URL will be ignored. + * Unless one is set programmatically, the .netrc + * will be queried. */ + CURL_NETRC_LAST +}; + +enum { + CURL_SSLVERSION_DEFAULT, + CURL_SSLVERSION_TLSv1, + CURL_SSLVERSION_SSLv2, + CURL_SSLVERSION_SSLv3, + + CURL_SSLVERSION_LAST /* never use, keep last */ +}; + +/* symbols to use with CURLOPT_POSTREDIR. + CURL_REDIR_POST_301 and CURL_REDIR_POST_302 can be bitwise ORed so that + CURL_REDIR_POST_301 | CURL_REDIR_POST_302 == CURL_REDIR_POST_ALL */ + +#define CURL_REDIR_GET_ALL 0 +#define CURL_REDIR_POST_301 1 +#define CURL_REDIR_POST_302 2 +#define CURL_REDIR_POST_ALL (CURL_REDIR_POST_301|CURL_REDIR_POST_302) + +typedef enum { + CURL_TIMECOND_NONE, + + CURL_TIMECOND_IFMODSINCE, + CURL_TIMECOND_IFUNMODSINCE, + CURL_TIMECOND_LASTMOD, + + CURL_TIMECOND_LAST +} curl_TimeCond; + + +/* curl_strequal() and curl_strnequal() are subject for removal in a future + libcurl, see lib/README.curlx for details */ +CURL_EXTERN int (curl_strequal)(const char *s1, const char *s2); +CURL_EXTERN int (curl_strnequal)(const char *s1, const char *s2, size_t n); + +/* name is uppercase CURLFORM_ */ +#ifdef CFINIT +#undef CFINIT +#endif + +#ifdef CURL_ISOCPP +#define CFINIT(name) CURLFORM_ ## name +#else +/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */ +#define CFINIT(name) CURLFORM_/**/name +#endif + +typedef enum { + CFINIT(NOTHING), /********* the first one is unused ************/ + + /* */ + CFINIT(COPYNAME), + CFINIT(PTRNAME), + CFINIT(NAMELENGTH), + CFINIT(COPYCONTENTS), + CFINIT(PTRCONTENTS), + CFINIT(CONTENTSLENGTH), + CFINIT(FILECONTENT), + CFINIT(ARRAY), + CFINIT(OBSOLETE), + CFINIT(FILE), + + CFINIT(BUFFER), + CFINIT(BUFFERPTR), + CFINIT(BUFFERLENGTH), + + CFINIT(CONTENTTYPE), + CFINIT(CONTENTHEADER), + CFINIT(FILENAME), + CFINIT(END), + CFINIT(OBSOLETE2), + + CFINIT(STREAM), + + CURLFORM_LASTENTRY /* the last unused */ +} CURLformoption; + +#undef CFINIT /* done */ + +/* structure to be used as parameter for CURLFORM_ARRAY */ +struct curl_forms { + CURLformoption option; + const char *value; +}; + +/* use this for multipart formpost building */ +/* Returns code for curl_formadd() + * + * Returns: + * CURL_FORMADD_OK on success + * CURL_FORMADD_MEMORY if the FormInfo allocation fails + * CURL_FORMADD_OPTION_TWICE if one option is given twice for one Form + * CURL_FORMADD_NULL if a null pointer was given for a char + * CURL_FORMADD_MEMORY if the allocation of a FormInfo struct failed + * CURL_FORMADD_UNKNOWN_OPTION if an unknown option was used + * CURL_FORMADD_INCOMPLETE if the some FormInfo is not complete (or error) + * CURL_FORMADD_MEMORY if a curl_httppost struct cannot be allocated + * CURL_FORMADD_MEMORY if some allocation for string copying failed. + * CURL_FORMADD_ILLEGAL_ARRAY if an illegal option is used in an array + * + ***************************************************************************/ +typedef enum { + CURL_FORMADD_OK, /* first, no error */ + + CURL_FORMADD_MEMORY, + CURL_FORMADD_OPTION_TWICE, + CURL_FORMADD_NULL, + CURL_FORMADD_UNKNOWN_OPTION, + CURL_FORMADD_INCOMPLETE, + CURL_FORMADD_ILLEGAL_ARRAY, + CURL_FORMADD_DISABLED, /* libcurl was built with this disabled */ + + CURL_FORMADD_LAST /* last */ +} CURLFORMcode; + +/* + * NAME curl_formadd() + * + * DESCRIPTION + * + * Pretty advanced function for building multi-part formposts. Each invoke + * adds one part that together construct a full post. Then use + * CURLOPT_HTTPPOST to send it off to libcurl. + */ +CURL_EXTERN CURLFORMcode curl_formadd(struct curl_httppost **httppost, + struct curl_httppost **last_post, + ...); + +/* + * callback function for curl_formget() + * The void *arg pointer will be the one passed as second argument to + * curl_formget(). + * The character buffer passed to it must not be freed. + * Should return the buffer length passed to it as the argument "len" on + * success. + */ +typedef size_t (*curl_formget_callback)(void *arg, const char *buf, size_t len); + +/* + * NAME curl_formget() + * + * DESCRIPTION + * + * Serialize a curl_httppost struct built with curl_formadd(). + * Accepts a void pointer as second argument which will be passed to + * the curl_formget_callback function. + * Returns 0 on success. + */ +CURL_EXTERN int curl_formget(struct curl_httppost *form, void *arg, + curl_formget_callback append); +/* + * NAME curl_formfree() + * + * DESCRIPTION + * + * Free a multipart formpost previously built with curl_formadd(). + */ +CURL_EXTERN void curl_formfree(struct curl_httppost *form); + +/* + * NAME curl_getenv() + * + * DESCRIPTION + * + * Returns a malloc()'ed string that MUST be curl_free()ed after usage is + * complete. DEPRECATED - see lib/README.curlx + */ +CURL_EXTERN char *curl_getenv(const char *variable); + +/* + * NAME curl_version() + * + * DESCRIPTION + * + * Returns a static ascii string of the libcurl version. + */ +CURL_EXTERN char *curl_version(void); + +/* + * NAME curl_easy_escape() + * + * DESCRIPTION + * + * Escapes URL strings (converts all letters consider illegal in URLs to their + * %XX versions). This function returns a new allocated string or NULL if an + * error occurred. + */ +CURL_EXTERN char *curl_easy_escape(CURL *handle, + const char *string, + int length); + +/* the previous version: */ +CURL_EXTERN char *curl_escape(const char *string, + int length); + + +/* + * NAME curl_easy_unescape() + * + * DESCRIPTION + * + * Unescapes URL encoding in strings (converts all %XX codes to their 8bit + * versions). This function returns a new allocated string or NULL if an error + * occurred. + * Conversion Note: On non-ASCII platforms the ASCII %XX codes are + * converted into the host encoding. + */ +CURL_EXTERN char *curl_easy_unescape(CURL *handle, + const char *string, + int length, + int *outlength); + +/* the previous version */ +CURL_EXTERN char *curl_unescape(const char *string, + int length); + +/* + * NAME curl_free() + * + * DESCRIPTION + * + * Provided for de-allocation in the same translation unit that did the + * allocation. Added in libcurl 7.10 + */ +CURL_EXTERN void curl_free(void *p); + +/* + * NAME curl_global_init() + * + * DESCRIPTION + * + * curl_global_init() should be invoked exactly once for each application that + * uses libcurl and before any call of other libcurl functions. + * + * This function is not thread-safe! + */ +CURL_EXTERN CURLcode curl_global_init(long flags); + +/* + * NAME curl_global_init_mem() + * + * DESCRIPTION + * + * curl_global_init() or curl_global_init_mem() should be invoked exactly once + * for each application that uses libcurl. This function can be used to + * initialize libcurl and set user defined memory management callback + * functions. Users can implement memory management routines to check for + * memory leaks, check for mis-use of the curl library etc. User registered + * callback routines with be invoked by this library instead of the system + * memory management routines like malloc, free etc. + */ +CURL_EXTERN CURLcode curl_global_init_mem(long flags, + curl_malloc_callback m, + curl_free_callback f, + curl_realloc_callback r, + curl_strdup_callback s, + curl_calloc_callback c); + +/* + * NAME curl_global_cleanup() + * + * DESCRIPTION + * + * curl_global_cleanup() should be invoked exactly once for each application + * that uses libcurl + */ +CURL_EXTERN void curl_global_cleanup(void); + +/* linked-list structure for the CURLOPT_QUOTE option (and other) */ +struct curl_slist { + char *data; + struct curl_slist *next; +}; + +/* + * NAME curl_slist_append() + * + * DESCRIPTION + * + * Appends a string to a linked list. If no list exists, it will be created + * first. Returns the new list, after appending. + */ +CURL_EXTERN struct curl_slist *curl_slist_append(struct curl_slist *, + const char *); + +/* + * NAME curl_slist_free_all() + * + * DESCRIPTION + * + * free a previously built curl_slist. + */ +CURL_EXTERN void curl_slist_free_all(struct curl_slist *); + +/* + * NAME curl_getdate() + * + * DESCRIPTION + * + * Returns the time, in seconds since 1 Jan 1970 of the time string given in + * the first argument. The time argument in the second parameter is unused + * and should be set to NULL. + */ +CURL_EXTERN time_t curl_getdate(const char *p, const time_t *unused); + +/* info about the certificate chain, only for OpenSSL builds. Asked + for with CURLOPT_CERTINFO / CURLINFO_CERTINFO */ +struct curl_certinfo { + int num_of_certs; /* number of certificates with information */ + struct curl_slist **certinfo; /* for each index in this array, there's a + linked list with textual information in the + format "name: value" */ +}; + +#define CURLINFO_STRING 0x100000 +#define CURLINFO_LONG 0x200000 +#define CURLINFO_DOUBLE 0x300000 +#define CURLINFO_SLIST 0x400000 +#define CURLINFO_MASK 0x0fffff +#define CURLINFO_TYPEMASK 0xf00000 + +typedef enum { + CURLINFO_NONE, /* first, never use this */ + CURLINFO_EFFECTIVE_URL = CURLINFO_STRING + 1, + CURLINFO_RESPONSE_CODE = CURLINFO_LONG + 2, + CURLINFO_TOTAL_TIME = CURLINFO_DOUBLE + 3, + CURLINFO_NAMELOOKUP_TIME = CURLINFO_DOUBLE + 4, + CURLINFO_CONNECT_TIME = CURLINFO_DOUBLE + 5, + CURLINFO_PRETRANSFER_TIME = CURLINFO_DOUBLE + 6, + CURLINFO_SIZE_UPLOAD = CURLINFO_DOUBLE + 7, + CURLINFO_SIZE_DOWNLOAD = CURLINFO_DOUBLE + 8, + CURLINFO_SPEED_DOWNLOAD = CURLINFO_DOUBLE + 9, + CURLINFO_SPEED_UPLOAD = CURLINFO_DOUBLE + 10, + CURLINFO_HEADER_SIZE = CURLINFO_LONG + 11, + CURLINFO_REQUEST_SIZE = CURLINFO_LONG + 12, + CURLINFO_SSL_VERIFYRESULT = CURLINFO_LONG + 13, + CURLINFO_FILETIME = CURLINFO_LONG + 14, + CURLINFO_CONTENT_LENGTH_DOWNLOAD = CURLINFO_DOUBLE + 15, + CURLINFO_CONTENT_LENGTH_UPLOAD = CURLINFO_DOUBLE + 16, + CURLINFO_STARTTRANSFER_TIME = CURLINFO_DOUBLE + 17, + CURLINFO_CONTENT_TYPE = CURLINFO_STRING + 18, + CURLINFO_REDIRECT_TIME = CURLINFO_DOUBLE + 19, + CURLINFO_REDIRECT_COUNT = CURLINFO_LONG + 20, + CURLINFO_PRIVATE = CURLINFO_STRING + 21, + CURLINFO_HTTP_CONNECTCODE = CURLINFO_LONG + 22, + CURLINFO_HTTPAUTH_AVAIL = CURLINFO_LONG + 23, + CURLINFO_PROXYAUTH_AVAIL = CURLINFO_LONG + 24, + CURLINFO_OS_ERRNO = CURLINFO_LONG + 25, + CURLINFO_NUM_CONNECTS = CURLINFO_LONG + 26, + CURLINFO_SSL_ENGINES = CURLINFO_SLIST + 27, + CURLINFO_COOKIELIST = CURLINFO_SLIST + 28, + CURLINFO_LASTSOCKET = CURLINFO_LONG + 29, + CURLINFO_FTP_ENTRY_PATH = CURLINFO_STRING + 30, + CURLINFO_REDIRECT_URL = CURLINFO_STRING + 31, + CURLINFO_PRIMARY_IP = CURLINFO_STRING + 32, + CURLINFO_APPCONNECT_TIME = CURLINFO_DOUBLE + 33, + CURLINFO_CERTINFO = CURLINFO_SLIST + 34, + CURLINFO_CONDITION_UNMET = CURLINFO_LONG + 35, + /* Fill in new entries below here! */ + + CURLINFO_LASTONE = 35 +} CURLINFO; + +/* CURLINFO_RESPONSE_CODE is the new name for the option previously known as + CURLINFO_HTTP_CODE */ +#define CURLINFO_HTTP_CODE CURLINFO_RESPONSE_CODE + +typedef enum { + CURLCLOSEPOLICY_NONE, /* first, never use this */ + + CURLCLOSEPOLICY_OLDEST, + CURLCLOSEPOLICY_LEAST_RECENTLY_USED, + CURLCLOSEPOLICY_LEAST_TRAFFIC, + CURLCLOSEPOLICY_SLOWEST, + CURLCLOSEPOLICY_CALLBACK, + + CURLCLOSEPOLICY_LAST /* last, never use this */ +} curl_closepolicy; + +#define CURL_GLOBAL_SSL (1<<0) +#define CURL_GLOBAL_WIN32 (1<<1) +#define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32) +#define CURL_GLOBAL_NOTHING 0 +#define CURL_GLOBAL_DEFAULT CURL_GLOBAL_ALL + + +/***************************************************************************** + * Setup defines, protos etc for the sharing stuff. + */ + +/* Different data locks for a single share */ +typedef enum { + CURL_LOCK_DATA_NONE = 0, + /* CURL_LOCK_DATA_SHARE is used internally to say that + * the locking is just made to change the internal state of the share + * itself. + */ + CURL_LOCK_DATA_SHARE, + CURL_LOCK_DATA_COOKIE, + CURL_LOCK_DATA_DNS, + CURL_LOCK_DATA_SSL_SESSION, + CURL_LOCK_DATA_CONNECT, + CURL_LOCK_DATA_LAST +} curl_lock_data; + +/* Different lock access types */ +typedef enum { + CURL_LOCK_ACCESS_NONE = 0, /* unspecified action */ + CURL_LOCK_ACCESS_SHARED = 1, /* for read perhaps */ + CURL_LOCK_ACCESS_SINGLE = 2, /* for write perhaps */ + CURL_LOCK_ACCESS_LAST /* never use */ +} curl_lock_access; + +typedef void (*curl_lock_function)(CURL *handle, + curl_lock_data data, + curl_lock_access locktype, + void *userptr); +typedef void (*curl_unlock_function)(CURL *handle, + curl_lock_data data, + void *userptr); + +typedef void CURLSH; + +typedef enum { + CURLSHE_OK, /* all is fine */ + CURLSHE_BAD_OPTION, /* 1 */ + CURLSHE_IN_USE, /* 2 */ + CURLSHE_INVALID, /* 3 */ + CURLSHE_NOMEM, /* out of memory */ + CURLSHE_LAST /* never use */ +} CURLSHcode; + +typedef enum { + CURLSHOPT_NONE, /* don't use */ + CURLSHOPT_SHARE, /* specify a data type to share */ + CURLSHOPT_UNSHARE, /* specify which data type to stop sharing */ + CURLSHOPT_LOCKFUNC, /* pass in a 'curl_lock_function' pointer */ + CURLSHOPT_UNLOCKFUNC, /* pass in a 'curl_unlock_function' pointer */ + CURLSHOPT_USERDATA, /* pass in a user data pointer used in the lock/unlock + callback functions */ + CURLSHOPT_LAST /* never use */ +} CURLSHoption; + +CURL_EXTERN CURLSH *curl_share_init(void); +CURL_EXTERN CURLSHcode curl_share_setopt(CURLSH *, CURLSHoption option, ...); +CURL_EXTERN CURLSHcode curl_share_cleanup(CURLSH *); + +/**************************************************************************** + * Structures for querying information about the curl library at runtime. + */ + +typedef enum { + CURLVERSION_FIRST, + CURLVERSION_SECOND, + CURLVERSION_THIRD, + CURLVERSION_FOURTH, + CURLVERSION_LAST /* never actually use this */ +} CURLversion; + +/* The 'CURLVERSION_NOW' is the symbolic name meant to be used by + basically all programs ever that want to get version information. It is + meant to be a built-in version number for what kind of struct the caller + expects. If the struct ever changes, we redefine the NOW to another enum + from above. */ +#define CURLVERSION_NOW CURLVERSION_FOURTH + +typedef struct { + CURLversion age; /* age of the returned struct */ + const char *version; /* LIBCURL_VERSION */ + unsigned int version_num; /* LIBCURL_VERSION_NUM */ + const char *host; /* OS/host/cpu/machine when configured */ + int features; /* bitmask, see defines below */ + const char *ssl_version; /* human readable string */ + long ssl_version_num; /* not used anymore, always 0 */ + const char *libz_version; /* human readable string */ + /* protocols is terminated by an entry with a NULL protoname */ + const char * const *protocols; + + /* The fields below this were added in CURLVERSION_SECOND */ + const char *ares; + int ares_num; + + /* This field was added in CURLVERSION_THIRD */ + const char *libidn; + + /* These field were added in CURLVERSION_FOURTH */ + + /* Same as '_libiconv_version' if built with HAVE_ICONV */ + int iconv_ver_num; + + const char *libssh_version; /* human readable string */ + +} curl_version_info_data; + +#define CURL_VERSION_IPV6 (1<<0) /* IPv6-enabled */ +#define CURL_VERSION_KERBEROS4 (1<<1) /* kerberos auth is supported */ +#define CURL_VERSION_SSL (1<<2) /* SSL options are present */ +#define CURL_VERSION_LIBZ (1<<3) /* libz features are present */ +#define CURL_VERSION_NTLM (1<<4) /* NTLM auth is supported */ +#define CURL_VERSION_GSSNEGOTIATE (1<<5) /* Negotiate auth support */ +#define CURL_VERSION_DEBUG (1<<6) /* built with debug capabilities */ +#define CURL_VERSION_ASYNCHDNS (1<<7) /* asynchronous dns resolves */ +#define CURL_VERSION_SPNEGO (1<<8) /* SPNEGO auth */ +#define CURL_VERSION_LARGEFILE (1<<9) /* supports files bigger than 2GB */ +#define CURL_VERSION_IDN (1<<10) /* International Domain Names support */ +#define CURL_VERSION_SSPI (1<<11) /* SSPI is supported */ +#define CURL_VERSION_CONV (1<<12) /* character conversions supported */ +#define CURL_VERSION_CURLDEBUG (1<<13) /* debug memory tracking supported */ + +/* + * NAME curl_version_info() + * + * DESCRIPTION + * + * This function returns a pointer to a static copy of the version info + * struct. See above. + */ +CURL_EXTERN curl_version_info_data *curl_version_info(CURLversion); + +/* + * NAME curl_easy_strerror() + * + * DESCRIPTION + * + * The curl_easy_strerror function may be used to turn a CURLcode value + * into the equivalent human readable error string. This is useful + * for printing meaningful error messages. + */ +CURL_EXTERN const char *curl_easy_strerror(CURLcode); + +/* + * NAME curl_share_strerror() + * + * DESCRIPTION + * + * The curl_share_strerror function may be used to turn a CURLSHcode value + * into the equivalent human readable error string. This is useful + * for printing meaningful error messages. + */ +CURL_EXTERN const char *curl_share_strerror(CURLSHcode); + +/* + * NAME curl_easy_pause() + * + * DESCRIPTION + * + * The curl_easy_pause function pauses or unpauses transfers. Select the new + * state by setting the bitmask, use the convenience defines below. + * + */ +CURL_EXTERN CURLcode curl_easy_pause(CURL *handle, int bitmask); + +#define CURLPAUSE_RECV (1<<0) +#define CURLPAUSE_RECV_CONT (0) + +#define CURLPAUSE_SEND (1<<2) +#define CURLPAUSE_SEND_CONT (0) + +#define CURLPAUSE_ALL (CURLPAUSE_RECV|CURLPAUSE_SEND) +#define CURLPAUSE_CONT (CURLPAUSE_RECV_CONT|CURLPAUSE_SEND_CONT) + +#ifdef __cplusplus +} +#endif + +/* unfortunately, the easy.h and multi.h include files need options and info + stuff before they can be included! */ +#include "easy.h" /* nothing in curl is fun without the easy stuff */ +#include "multi.h" + +/* the typechecker doesn't work in C++ (yet) */ +#if defined(__GNUC__) && defined(__GNUC_MINOR__) && \ + ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) && \ + !defined(__cplusplus) && !defined(CURL_DISABLE_TYPECHECK) +#include "typecheck-gcc.h" +#else +#if defined(__STDC__) && (__STDC__ >= 1) +/* This preprocessor magic that replaces a call with the exact same call is + only done to make sure application authors pass exactly three arguments + to these functions. */ +#define curl_easy_setopt(handle,opt,param) curl_easy_setopt(handle,opt,param) +#define curl_easy_getinfo(handle,info,arg) curl_easy_getinfo(handle,info,arg) +#define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param) +#define curl_multi_setopt(handle,opt,param) curl_multi_setopt(handle,opt,param) +#endif /* __STDC__ >= 1 */ +#endif /* gcc >= 4.3 && !__cplusplus */ + +#endif /* __CURL_CURL_H */ diff --git a/shared/sentry/external/breakpad/src/third_party/curl/curlbuild.h b/shared/sentry/external/breakpad/src/third_party/curl/curlbuild.h new file mode 100644 index 000000000..595df4e45 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/curl/curlbuild.h @@ -0,0 +1,203 @@ +/* include/curl/curlbuild.h. Generated from curlbuild.h.in by configure. */ +#ifndef __CURL_CURLBUILD_H +#define __CURL_CURLBUILD_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2009, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id: curlbuild.h.in,v 1.8 2009-04-29 15:15:38 yangtse Exp $ + ***************************************************************************/ + +/* ================================================================ */ +/* NOTES FOR CONFIGURE CAPABLE SYSTEMS */ +/* ================================================================ */ + +/* + * NOTE 1: + * ------- + * + * Nothing in this file is intended to be modified or adjusted by the + * curl library user nor by the curl library builder. + * + * If you think that something actually needs to be changed, adjusted + * or fixed in this file, then, report it on the libcurl development + * mailing list: http://cool.haxx.se/mailman/listinfo/curl-library/ + * + * This header file shall only export symbols which are 'curl' or 'CURL' + * prefixed, otherwise public name space would be polluted. + * + * NOTE 2: + * ------- + * + * Right now you might be staring at file include/curl/curlbuild.h.in or + * at file include/curl/curlbuild.h, this is due to the following reason: + * + * On systems capable of running the configure script, the configure process + * will overwrite the distributed include/curl/curlbuild.h file with one that + * is suitable and specific to the library being configured and built, which + * is generated from the include/curl/curlbuild.h.in template file. + * + */ + +/* ================================================================ */ +/* DEFINITION OF THESE SYMBOLS SHALL NOT TAKE PLACE ANYWHERE ELSE */ +/* ================================================================ */ + +#ifdef CURL_SIZEOF_LONG +# error "CURL_SIZEOF_LONG shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SIZEOF_LONG_already_defined +#endif + +#ifdef CURL_TYPEOF_CURL_SOCKLEN_T +# error "CURL_TYPEOF_CURL_SOCKLEN_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_TYPEOF_CURL_SOCKLEN_T_already_defined +#endif + +#ifdef CURL_SIZEOF_CURL_SOCKLEN_T +# error "CURL_SIZEOF_CURL_SOCKLEN_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SIZEOF_CURL_SOCKLEN_T_already_defined +#endif + +#ifdef CURL_TYPEOF_CURL_OFF_T +# error "CURL_TYPEOF_CURL_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_TYPEOF_CURL_OFF_T_already_defined +#endif + +#ifdef CURL_FORMAT_CURL_OFF_T +# error "CURL_FORMAT_CURL_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_FORMAT_CURL_OFF_T_already_defined +#endif + +#ifdef CURL_FORMAT_CURL_OFF_TU +# error "CURL_FORMAT_CURL_OFF_TU shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_FORMAT_CURL_OFF_TU_already_defined +#endif + +#ifdef CURL_FORMAT_OFF_T +# error "CURL_FORMAT_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_FORMAT_OFF_T_already_defined +#endif + +#ifdef CURL_SIZEOF_CURL_OFF_T +# error "CURL_SIZEOF_CURL_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SIZEOF_CURL_OFF_T_already_defined +#endif + +#ifdef CURL_SUFFIX_CURL_OFF_T +# error "CURL_SUFFIX_CURL_OFF_T shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SUFFIX_CURL_OFF_T_already_defined +#endif + +#ifdef CURL_SUFFIX_CURL_OFF_TU +# error "CURL_SUFFIX_CURL_OFF_TU shall not be defined except in curlbuild.h" + Error Compilation_aborted_CURL_SUFFIX_CURL_OFF_TU_already_defined +#endif + +/* ================================================================ */ +/* EXTERNAL INTERFACE SETTINGS FOR CONFIGURE CAPABLE SYSTEMS ONLY */ +/* ================================================================ */ + +/* Configure process defines this to 1 when it finds out that system */ +/* header file ws2tcpip.h must be included by the external interface. */ +/* #undef CURL_PULL_WS2TCPIP_H */ +#ifdef CURL_PULL_WS2TCPIP_H +# ifndef WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +# endif +# include +# include +# include +#endif + +/* Configure process defines this to 1 when it finds out that system */ +/* header file sys/types.h must be included by the external interface. */ +#define CURL_PULL_SYS_TYPES_H 1 +#ifdef CURL_PULL_SYS_TYPES_H +# include +#endif + +/* Configure process defines this to 1 when it finds out that system */ +/* header file stdint.h must be included by the external interface. */ +/* #undef CURL_PULL_STDINT_H */ +#ifdef CURL_PULL_STDINT_H +# include +#endif + +/* Configure process defines this to 1 when it finds out that system */ +/* header file inttypes.h must be included by the external interface. */ +/* #undef CURL_PULL_INTTYPES_H */ +#ifdef CURL_PULL_INTTYPES_H +# include +#endif + +/* Configure process defines this to 1 when it finds out that system */ +/* header file sys/socket.h must be included by the external interface. */ +#define CURL_PULL_SYS_SOCKET_H 1 +#ifdef CURL_PULL_SYS_SOCKET_H +# include +#endif + +/* The size of `long', as computed by sizeof. */ +#if defined(_M_X64) || (defined(__x86_64__) && !defined(__ILP32__)) || \ + defined(__aarch64__) || (defined(__mips__) && _MIPS_SIM == _ABI64) || \ + defined(__powerpc64__) || defined(__s390x__) || defined(__LP64__) +#define CURL_SIZEOF_LONG 8 +#else +#define CURL_SIZEOF_LONG 4 +#endif + +/* Integral data type used for curl_socklen_t. */ +#define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t + +/* The size of `curl_socklen_t', as computed by sizeof. */ +#define CURL_SIZEOF_CURL_SOCKLEN_T 4 + +/* Data type definition of curl_socklen_t. */ +typedef CURL_TYPEOF_CURL_SOCKLEN_T curl_socklen_t; + +/* Signed integral data type used for curl_off_t. */ +#if defined(_M_X64) || (defined(__x86_64__) && !defined(__ILP32__)) || \ + defined(__aarch64__) +#define CURL_TYPEOF_CURL_OFF_T long +#else +#define CURL_TYPEOF_CURL_OFF_T int64_t +#endif + +/* Data type definition of curl_off_t. */ +typedef CURL_TYPEOF_CURL_OFF_T curl_off_t; + +/* curl_off_t formatting string directive without "%" conversion specifier. */ +#define CURL_FORMAT_CURL_OFF_T "ld" + +/* unsigned curl_off_t formatting string without "%" conversion specifier. */ +#define CURL_FORMAT_CURL_OFF_TU "lu" + +/* curl_off_t formatting string directive with "%" conversion specifier. */ +#define CURL_FORMAT_OFF_T "%ld" + +/* The size of `curl_off_t', as computed by sizeof. */ +#define CURL_SIZEOF_CURL_OFF_T 8 + +/* curl_off_t constant suffix. */ +#define CURL_SUFFIX_CURL_OFF_T L + +/* unsigned curl_off_t constant suffix. */ +#define CURL_SUFFIX_CURL_OFF_TU UL + +#endif /* __CURL_CURLBUILD_H */ diff --git a/shared/sentry/external/breakpad/src/third_party/curl/curlrules.h b/shared/sentry/external/breakpad/src/third_party/curl/curlrules.h new file mode 100644 index 000000000..abac4397d --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/curl/curlrules.h @@ -0,0 +1,249 @@ +#ifndef __CURL_CURLRULES_H +#define __CURL_CURLRULES_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2009, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id: curlrules.h,v 1.7 2009-10-27 16:56:20 yangtse Exp $ + ***************************************************************************/ + +/* ================================================================ */ +/* COMPILE TIME SANITY CHECKS */ +/* ================================================================ */ + +/* + * NOTE 1: + * ------- + * + * All checks done in this file are intentionally placed in a public + * header file which is pulled by curl/curl.h when an application is + * being built using an already built libcurl library. Additionally + * this file is also included and used when building the library. + * + * If compilation fails on this file it is certainly sure that the + * problem is elsewhere. It could be a problem in the curlbuild.h + * header file, or simply that you are using different compilation + * settings than those used to build the library. + * + * Nothing in this file is intended to be modified or adjusted by the + * curl library user nor by the curl library builder. + * + * Do not deactivate any check, these are done to make sure that the + * library is properly built and used. + * + * You can find further help on the libcurl development mailing list: + * http://cool.haxx.se/mailman/listinfo/curl-library/ + * + * NOTE 2 + * ------ + * + * Some of the following compile time checks are based on the fact + * that the dimension of a constant array can not be a negative one. + * In this way if the compile time verification fails, the compilation + * will fail issuing an error. The error description wording is compiler + * dependent but it will be quite similar to one of the following: + * + * "negative subscript or subscript is too large" + * "array must have at least one element" + * "-1 is an illegal array size" + * "size of array is negative" + * + * If you are building an application which tries to use an already + * built libcurl library and you are getting this kind of errors on + * this file, it is a clear indication that there is a mismatch between + * how the library was built and how you are trying to use it for your + * application. Your already compiled or binary library provider is the + * only one who can give you the details you need to properly use it. + */ + +/* + * Verify that some macros are actually defined. + */ + +#ifndef CURL_SIZEOF_LONG +# error "CURL_SIZEOF_LONG definition is missing!" + Error Compilation_aborted_CURL_SIZEOF_LONG_is_missing +#endif + +#ifndef CURL_TYPEOF_CURL_SOCKLEN_T +# error "CURL_TYPEOF_CURL_SOCKLEN_T definition is missing!" + Error Compilation_aborted_CURL_TYPEOF_CURL_SOCKLEN_T_is_missing +#endif + +#ifndef CURL_SIZEOF_CURL_SOCKLEN_T +# error "CURL_SIZEOF_CURL_SOCKLEN_T definition is missing!" + Error Compilation_aborted_CURL_SIZEOF_CURL_SOCKLEN_T_is_missing +#endif + +#ifndef CURL_TYPEOF_CURL_OFF_T +# error "CURL_TYPEOF_CURL_OFF_T definition is missing!" + Error Compilation_aborted_CURL_TYPEOF_CURL_OFF_T_is_missing +#endif + +#ifndef CURL_FORMAT_CURL_OFF_T +# error "CURL_FORMAT_CURL_OFF_T definition is missing!" + Error Compilation_aborted_CURL_FORMAT_CURL_OFF_T_is_missing +#endif + +#ifndef CURL_FORMAT_CURL_OFF_TU +# error "CURL_FORMAT_CURL_OFF_TU definition is missing!" + Error Compilation_aborted_CURL_FORMAT_CURL_OFF_TU_is_missing +#endif + +#ifndef CURL_FORMAT_OFF_T +# error "CURL_FORMAT_OFF_T definition is missing!" + Error Compilation_aborted_CURL_FORMAT_OFF_T_is_missing +#endif + +#ifndef CURL_SIZEOF_CURL_OFF_T +# error "CURL_SIZEOF_CURL_OFF_T definition is missing!" + Error Compilation_aborted_CURL_SIZEOF_CURL_OFF_T_is_missing +#endif + +#ifndef CURL_SUFFIX_CURL_OFF_T +# error "CURL_SUFFIX_CURL_OFF_T definition is missing!" + Error Compilation_aborted_CURL_SUFFIX_CURL_OFF_T_is_missing +#endif + +#ifndef CURL_SUFFIX_CURL_OFF_TU +# error "CURL_SUFFIX_CURL_OFF_TU definition is missing!" + Error Compilation_aborted_CURL_SUFFIX_CURL_OFF_TU_is_missing +#endif + +/* + * Macros private to this header file. + */ + +#define CurlchkszEQ(t, s) sizeof(t) == s ? 1 : -1 + +#define CurlchkszGE(t1, t2) sizeof(t1) >= sizeof(t2) ? 1 : -1 + +/* + * Verify that the size previously defined and expected for long + * is the same as the one reported by sizeof() at compile time. + */ + +typedef char + __curl_rule_01__ + [CurlchkszEQ(long, CURL_SIZEOF_LONG)]; + +/* + * Verify that the size previously defined and expected for + * curl_off_t is actually the the same as the one reported + * by sizeof() at compile time. + */ + +typedef char + __curl_rule_02__ + [CurlchkszEQ(curl_off_t, CURL_SIZEOF_CURL_OFF_T)]; + +/* + * Verify at compile time that the size of curl_off_t as reported + * by sizeof() is greater or equal than the one reported for long + * for the current compilation. + */ + +typedef char + __curl_rule_03__ + [CurlchkszGE(curl_off_t, long)]; + +/* + * Verify that the size previously defined and expected for + * curl_socklen_t is actually the the same as the one reported + * by sizeof() at compile time. + */ + +typedef char + __curl_rule_04__ + [CurlchkszEQ(curl_socklen_t, CURL_SIZEOF_CURL_SOCKLEN_T)]; + +/* + * Verify at compile time that the size of curl_socklen_t as reported + * by sizeof() is greater or equal than the one reported for int for + * the current compilation. + */ + +typedef char + __curl_rule_05__ + [CurlchkszGE(curl_socklen_t, int)]; + +/* ================================================================ */ +/* EXTERNALLY AND INTERNALLY VISIBLE DEFINITIONS */ +/* ================================================================ */ + +/* + * CURL_ISOCPP and CURL_OFF_T_C definitions are done here in order to allow + * these to be visible and exported by the external libcurl interface API, + * while also making them visible to the library internals, simply including + * setup.h, without actually needing to include curl.h internally. + * If some day this section would grow big enough, all this should be moved + * to its own header file. + */ + +/* + * Figure out if we can use the ## preprocessor operator, which is supported + * by ISO/ANSI C and C++. Some compilers support it without setting __STDC__ + * or __cplusplus so we need to carefully check for them too. + */ + +#if defined(__STDC__) || defined(_MSC_VER) || defined(__cplusplus) || \ + defined(__HP_aCC) || defined(__BORLANDC__) || defined(__LCC__) || \ + defined(__POCC__) || defined(__SALFORDC__) || defined(__HIGHC__) || \ + defined(__ILEC400__) + /* This compiler is believed to have an ISO compatible preprocessor */ +#define CURL_ISOCPP +#else + /* This compiler is believed NOT to have an ISO compatible preprocessor */ +#undef CURL_ISOCPP +#endif + +/* + * Macros for minimum-width signed and unsigned curl_off_t integer constants. + */ + +#ifdef CURL_ISOCPP +# define __CURL_OFF_T_C_HELPER2(Val,Suffix) Val ## Suffix +#else +# define __CURL_OFF_T_C_HELPER2(Val,Suffix) Val/**/Suffix +#endif +#define __CURL_OFF_T_C_HELPER1(Val,Suffix) __CURL_OFF_T_C_HELPER2(Val,Suffix) +#define CURL_OFF_T_C(Val) __CURL_OFF_T_C_HELPER1(Val,CURL_SUFFIX_CURL_OFF_T) +#define CURL_OFF_TU_C(Val) __CURL_OFF_T_C_HELPER1(Val,CURL_SUFFIX_CURL_OFF_TU) + +/* + * Get rid of macros private to this header file. + */ + +#undef CurlchkszEQ +#undef CurlchkszGE + +/* + * Get rid of macros not intended to exist beyond this point. + */ + +#undef CURL_PULL_WS2TCPIP_H +#undef CURL_PULL_SYS_TYPES_H +#undef CURL_PULL_SYS_SOCKET_H +#undef CURL_PULL_STDINT_H +#undef CURL_PULL_INTTYPES_H + +#undef CURL_TYPEOF_CURL_SOCKLEN_T +#undef CURL_TYPEOF_CURL_OFF_T + +#endif /* __CURL_CURLRULES_H */ diff --git a/shared/sentry/external/breakpad/src/third_party/curl/curlver.h b/shared/sentry/external/breakpad/src/third_party/curl/curlver.h new file mode 100644 index 000000000..afa85c15a --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/curl/curlver.h @@ -0,0 +1,70 @@ +#ifndef __CURL_CURLVER_H +#define __CURL_CURLVER_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2009, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id: curlver.h,v 1.48 2009-08-12 11:24:52 bagder Exp $ + ***************************************************************************/ + +/* This header file contains nothing but libcurl version info, generated by + a script at release-time. This was made its own header file in 7.11.2 */ + +/* This is the global package copyright */ +#define LIBCURL_COPYRIGHT "1996 - 2009 Daniel Stenberg, ." + +/* This is the version number of the libcurl package from which this header + file origins: */ +#define LIBCURL_VERSION "7.19.7" + +/* The numeric version number is also available "in parts" by using these + defines: */ +#define LIBCURL_VERSION_MAJOR 7 +#define LIBCURL_VERSION_MINOR 19 +#define LIBCURL_VERSION_PATCH 7 + +/* This is the numeric version of the libcurl version number, meant for easier + parsing and comparions by programs. The LIBCURL_VERSION_NUM define will + always follow this syntax: + + 0xXXYYZZ + + Where XX, YY and ZZ are the main version, release and patch numbers in + hexadecimal (using 8 bits each). All three numbers are always represented + using two digits. 1.2 would appear as "0x010200" while version 9.11.7 + appears as "0x090b07". + + This 6-digit (24 bits) hexadecimal number does not show pre-release number, + and it is always a greater number in a more recent release. It makes + comparisons with greater than and less than work. +*/ +#define LIBCURL_VERSION_NUM 0x071307 + +/* + * This is the date and time when the full source package was created. The + * timestamp is not stored in CVS, as the timestamp is properly set in the + * tarballs by the maketgz script. + * + * The format of the date should follow this template: + * + * "Mon Feb 12 11:35:33 UTC 2007" + */ +#define LIBCURL_TIMESTAMP "Wed Nov 4 12:34:59 UTC 2009" + +#endif /* __CURL_CURLVER_H */ diff --git a/shared/sentry/external/breakpad/src/third_party/curl/easy.h b/shared/sentry/external/breakpad/src/third_party/curl/easy.h new file mode 100644 index 000000000..40449c3ec --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/curl/easy.h @@ -0,0 +1,103 @@ +#ifndef __CURL_EASY_H +#define __CURL_EASY_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2008, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id: easy.h,v 1.14 2008-05-12 21:43:28 bagder Exp $ + ***************************************************************************/ +#ifdef __cplusplus +extern "C" { +#endif + +CURL_EXTERN CURL *curl_easy_init(void); +CURL_EXTERN CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...); +CURL_EXTERN CURLcode curl_easy_perform(CURL *curl); +CURL_EXTERN void curl_easy_cleanup(CURL *curl); + +/* + * NAME curl_easy_getinfo() + * + * DESCRIPTION + * + * Request internal information from the curl session with this function. The + * third argument MUST be a pointer to a long, a pointer to a char * or a + * pointer to a double (as the documentation describes elsewhere). The data + * pointed to will be filled in accordingly and can be relied upon only if the + * function returns CURLE_OK. This function is intended to get used *AFTER* a + * performed transfer, all results from this function are undefined until the + * transfer is completed. + */ +CURL_EXTERN CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...); + + +/* + * NAME curl_easy_duphandle() + * + * DESCRIPTION + * + * Creates a new curl session handle with the same options set for the handle + * passed in. Duplicating a handle could only be a matter of cloning data and + * options, internal state info and things like persistant connections cannot + * be transfered. It is useful in multithreaded applications when you can run + * curl_easy_duphandle() for each new thread to avoid a series of identical + * curl_easy_setopt() invokes in every thread. + */ +CURL_EXTERN CURL* curl_easy_duphandle(CURL *curl); + +/* + * NAME curl_easy_reset() + * + * DESCRIPTION + * + * Re-initializes a CURL handle to the default values. This puts back the + * handle to the same state as it was in when it was just created. + * + * It does keep: live connections, the Session ID cache, the DNS cache and the + * cookies. + */ +CURL_EXTERN void curl_easy_reset(CURL *curl); + +/* + * NAME curl_easy_recv() + * + * DESCRIPTION + * + * Receives data from the connected socket. Use after successful + * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. + */ +CURL_EXTERN CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, + size_t *n); + +/* + * NAME curl_easy_send() + * + * DESCRIPTION + * + * Sends data over the connected socket. Use after successful + * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. + */ +CURL_EXTERN CURLcode curl_easy_send(CURL *curl, const void *buffer, + size_t buflen, size_t *n); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/shared/sentry/external/breakpad/src/third_party/curl/mprintf.h b/shared/sentry/external/breakpad/src/third_party/curl/mprintf.h new file mode 100644 index 000000000..d7202de17 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/curl/mprintf.h @@ -0,0 +1,82 @@ +#ifndef __CURL_MPRINTF_H +#define __CURL_MPRINTF_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2006, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id: mprintf.h,v 1.16 2008-05-20 10:21:50 patrickm Exp $ + ***************************************************************************/ + +#include +#include /* needed for FILE */ + +#include "curl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +CURL_EXTERN int curl_mprintf(const char *format, ...); +CURL_EXTERN int curl_mfprintf(FILE *fd, const char *format, ...); +CURL_EXTERN int curl_msprintf(char *buffer, const char *format, ...); +CURL_EXTERN int curl_msnprintf(char *buffer, size_t maxlength, + const char *format, ...); +CURL_EXTERN int curl_mvprintf(const char *format, va_list args); +CURL_EXTERN int curl_mvfprintf(FILE *fd, const char *format, va_list args); +CURL_EXTERN int curl_mvsprintf(char *buffer, const char *format, va_list args); +CURL_EXTERN int curl_mvsnprintf(char *buffer, size_t maxlength, + const char *format, va_list args); +CURL_EXTERN char *curl_maprintf(const char *format, ...); +CURL_EXTERN char *curl_mvaprintf(const char *format, va_list args); + +#ifdef _MPRINTF_REPLACE +# undef printf +# undef fprintf +# undef sprintf +# undef vsprintf +# undef snprintf +# undef vprintf +# undef vfprintf +# undef vsnprintf +# undef aprintf +# undef vaprintf +# define printf curl_mprintf +# define fprintf curl_mfprintf +#ifdef CURLDEBUG +/* When built with CURLDEBUG we define away the sprintf() functions since we + don't want internal code to be using them */ +# define sprintf sprintf_was_used +# define vsprintf vsprintf_was_used +#else +# define sprintf curl_msprintf +# define vsprintf curl_mvsprintf +#endif +# define snprintf curl_msnprintf +# define vprintf curl_mvprintf +# define vfprintf curl_mvfprintf +# define vsnprintf curl_mvsnprintf +# define aprintf curl_maprintf +# define vaprintf curl_mvaprintf +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __CURL_MPRINTF_H */ diff --git a/shared/sentry/external/breakpad/src/third_party/curl/multi.h b/shared/sentry/external/breakpad/src/third_party/curl/multi.h new file mode 100644 index 000000000..153f7721c --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/curl/multi.h @@ -0,0 +1,346 @@ +#ifndef __CURL_MULTI_H +#define __CURL_MULTI_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2007, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id: multi.h,v 1.45 2008-05-20 10:21:50 patrickm Exp $ + ***************************************************************************/ +/* + This is an "external" header file. Don't give away any internals here! + + GOALS + + o Enable a "pull" interface. The application that uses libcurl decides where + and when to ask libcurl to get/send data. + + o Enable multiple simultaneous transfers in the same thread without making it + complicated for the application. + + o Enable the application to select() on its own file descriptors and curl's + file descriptors simultaneous easily. + +*/ + +/* + * This header file should not really need to include "curl.h" since curl.h + * itself includes this file and we expect user applications to do #include + * without the need for especially including multi.h. + * + * For some reason we added this include here at one point, and rather than to + * break existing (wrongly written) libcurl applications, we leave it as-is + * but with this warning attached. + */ +#include "curl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef void CURLM; + +typedef enum { + CURLM_CALL_MULTI_PERFORM = -1, /* please call curl_multi_perform() or + curl_multi_socket*() soon */ + CURLM_OK, + CURLM_BAD_HANDLE, /* the passed-in handle is not a valid CURLM handle */ + CURLM_BAD_EASY_HANDLE, /* an easy handle was not good/valid */ + CURLM_OUT_OF_MEMORY, /* if you ever get this, you're in deep sh*t */ + CURLM_INTERNAL_ERROR, /* this is a libcurl bug */ + CURLM_BAD_SOCKET, /* the passed in socket argument did not match */ + CURLM_UNKNOWN_OPTION, /* curl_multi_setopt() with unsupported option */ + CURLM_LAST +} CURLMcode; + +/* just to make code nicer when using curl_multi_socket() you can now check + for CURLM_CALL_MULTI_SOCKET too in the same style it works for + curl_multi_perform() and CURLM_CALL_MULTI_PERFORM */ +#define CURLM_CALL_MULTI_SOCKET CURLM_CALL_MULTI_PERFORM + +typedef enum { + CURLMSG_NONE, /* first, not used */ + CURLMSG_DONE, /* This easy handle has completed. 'result' contains + the CURLcode of the transfer */ + CURLMSG_LAST /* last, not used */ +} CURLMSG; + +struct CURLMsg { + CURLMSG msg; /* what this message means */ + CURL *easy_handle; /* the handle it concerns */ + union { + void *whatever; /* message-specific data */ + CURLcode result; /* return code for transfer */ + } data; +}; +typedef struct CURLMsg CURLMsg; + +/* + * Name: curl_multi_init() + * + * Desc: inititalize multi-style curl usage + * + * Returns: a new CURLM handle to use in all 'curl_multi' functions. + */ +CURL_EXTERN CURLM *curl_multi_init(void); + +/* + * Name: curl_multi_add_handle() + * + * Desc: add a standard curl handle to the multi stack + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_add_handle(CURLM *multi_handle, + CURL *curl_handle); + + /* + * Name: curl_multi_remove_handle() + * + * Desc: removes a curl handle from the multi stack again + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_remove_handle(CURLM *multi_handle, + CURL *curl_handle); + + /* + * Name: curl_multi_fdset() + * + * Desc: Ask curl for its fd_set sets. The app can use these to select() or + * poll() on. We want curl_multi_perform() called as soon as one of + * them are ready. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_fdset(CURLM *multi_handle, + fd_set *read_fd_set, + fd_set *write_fd_set, + fd_set *exc_fd_set, + int *max_fd); + + /* + * Name: curl_multi_perform() + * + * Desc: When the app thinks there's data available for curl it calls this + * function to read/write whatever there is right now. This returns + * as soon as the reads and writes are done. This function does not + * require that there actually is data available for reading or that + * data can be written, it can be called just in case. It returns + * the number of handles that still transfer data in the second + * argument's integer-pointer. + * + * Returns: CURLMcode type, general multi error code. *NOTE* that this only + * returns errors etc regarding the whole multi stack. There might + * still have occurred problems on invidual transfers even when this + * returns OK. + */ +CURL_EXTERN CURLMcode curl_multi_perform(CURLM *multi_handle, + int *running_handles); + + /* + * Name: curl_multi_cleanup() + * + * Desc: Cleans up and removes a whole multi stack. It does not free or + * touch any individual easy handles in any way. We need to define + * in what state those handles will be if this function is called + * in the middle of a transfer. + * + * Returns: CURLMcode type, general multi error code. + */ +CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *multi_handle); + +/* + * Name: curl_multi_info_read() + * + * Desc: Ask the multi handle if there's any messages/informationals from + * the individual transfers. Messages include informationals such as + * error code from the transfer or just the fact that a transfer is + * completed. More details on these should be written down as well. + * + * Repeated calls to this function will return a new struct each + * time, until a special "end of msgs" struct is returned as a signal + * that there is no more to get at this point. + * + * The data the returned pointer points to will not survive calling + * curl_multi_cleanup(). + * + * The 'CURLMsg' struct is meant to be very simple and only contain + * very basic informations. If more involved information is wanted, + * we will provide the particular "transfer handle" in that struct + * and that should/could/would be used in subsequent + * curl_easy_getinfo() calls (or similar). The point being that we + * must never expose complex structs to applications, as then we'll + * undoubtably get backwards compatibility problems in the future. + * + * Returns: A pointer to a filled-in struct, or NULL if it failed or ran out + * of structs. It also writes the number of messages left in the + * queue (after this read) in the integer the second argument points + * to. + */ +CURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *multi_handle, + int *msgs_in_queue); + +/* + * Name: curl_multi_strerror() + * + * Desc: The curl_multi_strerror function may be used to turn a CURLMcode + * value into the equivalent human readable error string. This is + * useful for printing meaningful error messages. + * + * Returns: A pointer to a zero-terminated error message. + */ +CURL_EXTERN const char *curl_multi_strerror(CURLMcode); + +/* + * Name: curl_multi_socket() and + * curl_multi_socket_all() + * + * Desc: An alternative version of curl_multi_perform() that allows the + * application to pass in one of the file descriptors that have been + * detected to have "action" on them and let libcurl perform. + * See man page for details. + */ +#define CURL_POLL_NONE 0 +#define CURL_POLL_IN 1 +#define CURL_POLL_OUT 2 +#define CURL_POLL_INOUT 3 +#define CURL_POLL_REMOVE 4 + +#define CURL_SOCKET_TIMEOUT CURL_SOCKET_BAD + +#define CURL_CSELECT_IN 0x01 +#define CURL_CSELECT_OUT 0x02 +#define CURL_CSELECT_ERR 0x04 + +typedef int (*curl_socket_callback)(CURL *easy, /* easy handle */ + curl_socket_t s, /* socket */ + int what, /* see above */ + void *userp, /* private callback + pointer */ + void *socketp); /* private socket + pointer */ +/* + * Name: curl_multi_timer_callback + * + * Desc: Called by libcurl whenever the library detects a change in the + * maximum number of milliseconds the app is allowed to wait before + * curl_multi_socket() or curl_multi_perform() must be called + * (to allow libcurl's timed events to take place). + * + * Returns: The callback should return zero. + */ +typedef int (*curl_multi_timer_callback)(CURLM *multi, /* multi handle */ + long timeout_ms, /* see above */ + void *userp); /* private callback + pointer */ + +CURL_EXTERN CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s, + int *running_handles); + +CURL_EXTERN CURLMcode curl_multi_socket_action(CURLM *multi_handle, + curl_socket_t s, + int ev_bitmask, + int *running_handles); + +CURL_EXTERN CURLMcode curl_multi_socket_all(CURLM *multi_handle, + int *running_handles); + +#ifndef CURL_ALLOW_OLD_MULTI_SOCKET +/* This macro below was added in 7.16.3 to push users who recompile to use + the new curl_multi_socket_action() instead of the old curl_multi_socket() +*/ +#define curl_multi_socket(x,y,z) curl_multi_socket_action(x,y,0,z) +#endif + +/* + * Name: curl_multi_timeout() + * + * Desc: Returns the maximum number of milliseconds the app is allowed to + * wait before curl_multi_socket() or curl_multi_perform() must be + * called (to allow libcurl's timed events to take place). + * + * Returns: CURLM error code. + */ +CURL_EXTERN CURLMcode curl_multi_timeout(CURLM *multi_handle, + long *milliseconds); + +#undef CINIT /* re-using the same name as in curl.h */ + +#ifdef CURL_ISOCPP +#define CINIT(name,type,num) CURLMOPT_ ## name = CURLOPTTYPE_ ## type + num +#else +/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */ +#define LONG CURLOPTTYPE_LONG +#define OBJECTPOINT CURLOPTTYPE_OBJECTPOINT +#define FUNCTIONPOINT CURLOPTTYPE_FUNCTIONPOINT +#define OFF_T CURLOPTTYPE_OFF_T +#define CINIT(name,type,number) CURLMOPT_/**/name = type + number +#endif + +typedef enum { + /* This is the socket callback function pointer */ + CINIT(SOCKETFUNCTION, FUNCTIONPOINT, 1), + + /* This is the argument passed to the socket callback */ + CINIT(SOCKETDATA, OBJECTPOINT, 2), + + /* set to 1 to enable pipelining for this multi handle */ + CINIT(PIPELINING, LONG, 3), + + /* This is the timer callback function pointer */ + CINIT(TIMERFUNCTION, FUNCTIONPOINT, 4), + + /* This is the argument passed to the timer callback */ + CINIT(TIMERDATA, OBJECTPOINT, 5), + + /* maximum number of entries in the connection cache */ + CINIT(MAXCONNECTS, LONG, 6), + + CURLMOPT_LASTENTRY /* the last unused */ +} CURLMoption; + + +/* + * Name: curl_multi_setopt() + * + * Desc: Sets options for the multi handle. + * + * Returns: CURLM error code. + */ +CURL_EXTERN CURLMcode curl_multi_setopt(CURLM *multi_handle, + CURLMoption option, ...); + + +/* + * Name: curl_multi_assign() + * + * Desc: This function sets an association in the multi handle between the + * given socket and a private pointer of the application. This is + * (only) useful for curl_multi_socket uses. + * + * Returns: CURLM error code. + */ +CURL_EXTERN CURLMcode curl_multi_assign(CURLM *multi_handle, + curl_socket_t sockfd, void *sockp); + +#ifdef __cplusplus +} /* end of extern "C" */ +#endif + +#endif diff --git a/shared/sentry/external/breakpad/src/third_party/curl/stdcheaders.h b/shared/sentry/external/breakpad/src/third_party/curl/stdcheaders.h new file mode 100644 index 000000000..f739d7f9a --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/curl/stdcheaders.h @@ -0,0 +1,34 @@ +#ifndef __STDC_HEADERS_H +#define __STDC_HEADERS_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2009, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id: stdcheaders.h,v 1.9 2009-05-18 12:25:45 yangtse Exp $ + ***************************************************************************/ + +#include + +size_t fread (void *, size_t, size_t, FILE *); +size_t fwrite (const void *, size_t, size_t, FILE *); + +int strcasecmp(const char *, const char *); +int strncasecmp(const char *, const char *, size_t); + +#endif diff --git a/shared/sentry/external/breakpad/src/third_party/curl/typecheck-gcc.h b/shared/sentry/external/breakpad/src/third_party/curl/typecheck-gcc.h new file mode 100644 index 000000000..978830581 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/curl/typecheck-gcc.h @@ -0,0 +1,551 @@ +#ifndef __CURL_TYPECHECK_GCC_H +#define __CURL_TYPECHECK_GCC_H +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2009, Daniel Stenberg, , et al. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://curl.haxx.se/docs/copyright.html. + * + * You may opt to use, copy, modify, merge, publish, distribute and/or sell + * copies of the Software, and permit persons to whom the Software is + * furnished to do so, under the terms of the COPYING file. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + * $Id: typecheck-gcc.h,v 1.9 2009-01-25 23:26:31 bagder Exp $ + ***************************************************************************/ + +/* wraps curl_easy_setopt() with typechecking */ + +/* To add a new kind of warning, add an + * if(_curl_is_sometype_option(_curl_opt) && ! _curl_is_sometype(value)) + * _curl_easy_setopt_err_sometype(); + * block and define _curl_is_sometype_option, _curl_is_sometype and + * _curl_easy_setopt_err_sometype below + * + * To add an option that uses the same type as an existing option, you'll just + * need to extend the appropriate _curl_*_option macro + */ +#define curl_easy_setopt(handle, option, value) \ +__extension__ ({ \ + __typeof__ (option) _curl_opt = option; \ + if (__builtin_constant_p(_curl_opt)) { \ + if (_curl_is_long_option(_curl_opt) && !_curl_is_long(value)) \ + _curl_easy_setopt_err_long(); \ + if (_curl_is_off_t_option(_curl_opt) && !_curl_is_off_t(value)) \ + _curl_easy_setopt_err_curl_off_t(); \ + if (_curl_is_string_option(_curl_opt) && !_curl_is_string(value)) \ + _curl_easy_setopt_err_string(); \ + if (_curl_is_write_cb_option(_curl_opt) && !_curl_is_write_cb(value)) \ + _curl_easy_setopt_err_write_callback(); \ + if ((_curl_opt) == CURLOPT_READFUNCTION && !_curl_is_read_cb(value)) \ + _curl_easy_setopt_err_read_cb(); \ + if ((_curl_opt) == CURLOPT_IOCTLFUNCTION && !_curl_is_ioctl_cb(value)) \ + _curl_easy_setopt_err_ioctl_cb(); \ + if ((_curl_opt) == CURLOPT_SOCKOPTFUNCTION && !_curl_is_sockopt_cb(value))\ + _curl_easy_setopt_err_sockopt_cb(); \ + if ((_curl_opt) == CURLOPT_OPENSOCKETFUNCTION && \ + !_curl_is_opensocket_cb(value)) \ + _curl_easy_setopt_err_opensocket_cb(); \ + if ((_curl_opt) == CURLOPT_PROGRESSFUNCTION && \ + !_curl_is_progress_cb(value)) \ + _curl_easy_setopt_err_progress_cb(); \ + if ((_curl_opt) == CURLOPT_DEBUGFUNCTION && !_curl_is_debug_cb(value)) \ + _curl_easy_setopt_err_debug_cb(); \ + if ((_curl_opt) == CURLOPT_SSL_CTX_FUNCTION && \ + !_curl_is_ssl_ctx_cb(value)) \ + _curl_easy_setopt_err_ssl_ctx_cb(); \ + if (_curl_is_conv_cb_option(_curl_opt) && !_curl_is_conv_cb(value)) \ + _curl_easy_setopt_err_conv_cb(); \ + if ((_curl_opt) == CURLOPT_SEEKFUNCTION && !_curl_is_seek_cb(value)) \ + _curl_easy_setopt_err_seek_cb(); \ + if (_curl_is_cb_data_option(_curl_opt) && !_curl_is_cb_data(value)) \ + _curl_easy_setopt_err_cb_data(); \ + if ((_curl_opt) == CURLOPT_ERRORBUFFER && !_curl_is_error_buffer(value)) \ + _curl_easy_setopt_err_error_buffer(); \ + if ((_curl_opt) == CURLOPT_STDERR && !_curl_is_FILE(value)) \ + _curl_easy_setopt_err_FILE(); \ + if (_curl_is_postfields_option(_curl_opt) && !_curl_is_postfields(value)) \ + _curl_easy_setopt_err_postfields(); \ + if ((_curl_opt) == CURLOPT_HTTPPOST && \ + !_curl_is_arr((value), struct curl_httppost)) \ + _curl_easy_setopt_err_curl_httpost(); \ + if (_curl_is_slist_option(_curl_opt) && \ + !_curl_is_arr((value), struct curl_slist)) \ + _curl_easy_setopt_err_curl_slist(); \ + if ((_curl_opt) == CURLOPT_SHARE && !_curl_is_ptr((value), CURLSH)) \ + _curl_easy_setopt_err_CURLSH(); \ + } \ + curl_easy_setopt(handle, _curl_opt, value); \ +}) + +/* wraps curl_easy_getinfo() with typechecking */ +/* FIXME: don't allow const pointers */ +#define curl_easy_getinfo(handle, info, arg) \ +__extension__ ({ \ + __typeof__ (info) _curl_info = info; \ + if (__builtin_constant_p(_curl_info)) { \ + if (_curl_is_string_info(_curl_info) && !_curl_is_arr((arg), char *)) \ + _curl_easy_getinfo_err_string(); \ + if (_curl_is_long_info(_curl_info) && !_curl_is_arr((arg), long)) \ + _curl_easy_getinfo_err_long(); \ + if (_curl_is_double_info(_curl_info) && !_curl_is_arr((arg), double)) \ + _curl_easy_getinfo_err_double(); \ + if (_curl_is_slist_info(_curl_info) && \ + !_curl_is_arr((arg), struct curl_slist *)) \ + _curl_easy_getinfo_err_curl_slist(); \ + } \ + curl_easy_getinfo(handle, _curl_info, arg); \ +}) + +/* TODO: typechecking for curl_share_setopt() and curl_multi_setopt(), + * for now just make sure that the functions are called with three + * arguments + */ +#define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param) +#define curl_multi_setopt(handle,opt,param) curl_multi_setopt(handle,opt,param) + + +/* the actual warnings, triggered by calling the _curl_easy_setopt_err* + * functions */ + +/* To define a new warning, use _CURL_WARNING(identifier, "message") */ +#define _CURL_WARNING(id, message) \ + static void __attribute__((warning(message))) __attribute__((unused)) \ + __attribute__((noinline)) id(void) { __asm__(""); } + +_CURL_WARNING(_curl_easy_setopt_err_long, + "curl_easy_setopt expects a long argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_curl_off_t, + "curl_easy_setopt expects a curl_off_t argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_string, + "curl_easy_setopt expects a string (char* or char[]) argument for this option" + ) +_CURL_WARNING(_curl_easy_setopt_err_write_callback, + "curl_easy_setopt expects a curl_write_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_read_cb, + "curl_easy_setopt expects a curl_read_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_ioctl_cb, + "curl_easy_setopt expects a curl_ioctl_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_sockopt_cb, + "curl_easy_setopt expects a curl_sockopt_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_opensocket_cb, + "curl_easy_setopt expects a curl_opensocket_callback argument for this option" + ) +_CURL_WARNING(_curl_easy_setopt_err_progress_cb, + "curl_easy_setopt expects a curl_progress_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_debug_cb, + "curl_easy_setopt expects a curl_debug_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_ssl_ctx_cb, + "curl_easy_setopt expects a curl_ssl_ctx_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_conv_cb, + "curl_easy_setopt expects a curl_conv_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_seek_cb, + "curl_easy_setopt expects a curl_seek_callback argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_cb_data, + "curl_easy_setopt expects a private data pointer as argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_error_buffer, + "curl_easy_setopt expects a char buffer of CURL_ERROR_SIZE as argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_FILE, + "curl_easy_setopt expects a FILE* argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_postfields, + "curl_easy_setopt expects a void* or char* argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_curl_httpost, + "curl_easy_setopt expects a struct curl_httppost* argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_curl_slist, + "curl_easy_setopt expects a struct curl_slist* argument for this option") +_CURL_WARNING(_curl_easy_setopt_err_CURLSH, + "curl_easy_setopt expects a CURLSH* argument for this option") + +_CURL_WARNING(_curl_easy_getinfo_err_string, + "curl_easy_getinfo expects a pointer to char * for this info") +_CURL_WARNING(_curl_easy_getinfo_err_long, + "curl_easy_getinfo expects a pointer to long for this info") +_CURL_WARNING(_curl_easy_getinfo_err_double, + "curl_easy_getinfo expects a pointer to double for this info") +_CURL_WARNING(_curl_easy_getinfo_err_curl_slist, + "curl_easy_getinfo expects a pointer to struct curl_slist * for this info") + +/* groups of curl_easy_setops options that take the same type of argument */ + +/* To add a new option to one of the groups, just add + * (option) == CURLOPT_SOMETHING + * to the or-expression. If the option takes a long or curl_off_t, you don't + * have to do anything + */ + +/* evaluates to true if option takes a long argument */ +#define _curl_is_long_option(option) \ + (0 < (option) && (option) < CURLOPTTYPE_OBJECTPOINT) + +#define _curl_is_off_t_option(option) \ + ((option) > CURLOPTTYPE_OFF_T) + +/* evaluates to true if option takes a char* argument */ +#define _curl_is_string_option(option) \ + ((option) == CURLOPT_URL || \ + (option) == CURLOPT_PROXY || \ + (option) == CURLOPT_INTERFACE || \ + (option) == CURLOPT_NETRC_FILE || \ + (option) == CURLOPT_USERPWD || \ + (option) == CURLOPT_USERNAME || \ + (option) == CURLOPT_PASSWORD || \ + (option) == CURLOPT_PROXYUSERPWD || \ + (option) == CURLOPT_PROXYUSERNAME || \ + (option) == CURLOPT_PROXYPASSWORD || \ + (option) == CURLOPT_NOPROXY || \ + (option) == CURLOPT_ENCODING || \ + (option) == CURLOPT_REFERER || \ + (option) == CURLOPT_USERAGENT || \ + (option) == CURLOPT_COOKIE || \ + (option) == CURLOPT_COOKIEFILE || \ + (option) == CURLOPT_COOKIEJAR || \ + (option) == CURLOPT_COOKIELIST || \ + (option) == CURLOPT_FTPPORT || \ + (option) == CURLOPT_FTP_ALTERNATIVE_TO_USER || \ + (option) == CURLOPT_FTP_ACCOUNT || \ + (option) == CURLOPT_RANGE || \ + (option) == CURLOPT_CUSTOMREQUEST || \ + (option) == CURLOPT_SSLCERT || \ + (option) == CURLOPT_SSLCERTTYPE || \ + (option) == CURLOPT_SSLKEY || \ + (option) == CURLOPT_SSLKEYTYPE || \ + (option) == CURLOPT_KEYPASSWD || \ + (option) == CURLOPT_SSLENGINE || \ + (option) == CURLOPT_CAINFO || \ + (option) == CURLOPT_CAPATH || \ + (option) == CURLOPT_RANDOM_FILE || \ + (option) == CURLOPT_EGDSOCKET || \ + (option) == CURLOPT_SSL_CIPHER_LIST || \ + (option) == CURLOPT_KRBLEVEL || \ + (option) == CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 || \ + (option) == CURLOPT_SSH_PUBLIC_KEYFILE || \ + (option) == CURLOPT_SSH_PRIVATE_KEYFILE || \ + (option) == CURLOPT_CRLFILE || \ + (option) == CURLOPT_ISSUERCERT || \ + 0) + +/* evaluates to true if option takes a curl_write_callback argument */ +#define _curl_is_write_cb_option(option) \ + ((option) == CURLOPT_HEADERFUNCTION || \ + (option) == CURLOPT_WRITEFUNCTION) + +/* evaluates to true if option takes a curl_conv_callback argument */ +#define _curl_is_conv_cb_option(option) \ + ((option) == CURLOPT_CONV_TO_NETWORK_FUNCTION || \ + (option) == CURLOPT_CONV_FROM_NETWORK_FUNCTION || \ + (option) == CURLOPT_CONV_FROM_UTF8_FUNCTION) + +/* evaluates to true if option takes a data argument to pass to a callback */ +#define _curl_is_cb_data_option(option) \ + ((option) == CURLOPT_WRITEDATA || \ + (option) == CURLOPT_READDATA || \ + (option) == CURLOPT_IOCTLDATA || \ + (option) == CURLOPT_SOCKOPTDATA || \ + (option) == CURLOPT_OPENSOCKETDATA || \ + (option) == CURLOPT_PROGRESSDATA || \ + (option) == CURLOPT_WRITEHEADER || \ + (option) == CURLOPT_DEBUGDATA || \ + (option) == CURLOPT_SSL_CTX_DATA || \ + (option) == CURLOPT_SEEKDATA || \ + (option) == CURLOPT_PRIVATE || \ + 0) + +/* evaluates to true if option takes a POST data argument (void* or char*) */ +#define _curl_is_postfields_option(option) \ + ((option) == CURLOPT_POSTFIELDS || \ + (option) == CURLOPT_COPYPOSTFIELDS || \ + 0) + +/* evaluates to true if option takes a struct curl_slist * argument */ +#define _curl_is_slist_option(option) \ + ((option) == CURLOPT_HTTPHEADER || \ + (option) == CURLOPT_HTTP200ALIASES || \ + (option) == CURLOPT_QUOTE || \ + (option) == CURLOPT_POSTQUOTE || \ + (option) == CURLOPT_PREQUOTE || \ + (option) == CURLOPT_TELNETOPTIONS || \ + 0) + +/* groups of curl_easy_getinfo infos that take the same type of argument */ + +/* evaluates to true if info expects a pointer to char * argument */ +#define _curl_is_string_info(info) \ + (CURLINFO_STRING < (info) && (info) < CURLINFO_LONG) + +/* evaluates to true if info expects a pointer to long argument */ +#define _curl_is_long_info(info) \ + (CURLINFO_LONG < (info) && (info) < CURLINFO_DOUBLE) + +/* evaluates to true if info expects a pointer to double argument */ +#define _curl_is_double_info(info) \ + (CURLINFO_DOUBLE < (info) && (info) < CURLINFO_SLIST) + +/* true if info expects a pointer to struct curl_slist * argument */ +#define _curl_is_slist_info(info) \ + (CURLINFO_SLIST < (info)) + + +/* typecheck helpers -- check whether given expression has requested type*/ + +/* For pointers, you can use the _curl_is_ptr/_curl_is_arr macros, + * otherwise define a new macro. Search for __builtin_types_compatible_p + * in the GCC manual. + * NOTE: these macros MUST NOT EVALUATE their arguments! The argument is + * the actual expression passed to the curl_easy_setopt macro. This + * means that you can only apply the sizeof and __typeof__ operators, no + * == or whatsoever. + */ + +/* XXX: should evaluate to true iff expr is a pointer */ +#define _curl_is_any_ptr(expr) \ + (sizeof(expr) == sizeof(void*)) + +/* evaluates to true if expr is NULL */ +/* XXX: must not evaluate expr, so this check is not accurate */ +#define _curl_is_NULL(expr) \ + (__builtin_types_compatible_p(__typeof__(expr), __typeof__(NULL))) + +/* evaluates to true if expr is type*, const type* or NULL */ +#define _curl_is_ptr(expr, type) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), type *) || \ + __builtin_types_compatible_p(__typeof__(expr), const type *)) + +/* evaluates to true if expr is one of type[], type*, NULL or const type* */ +#define _curl_is_arr(expr, type) \ + (_curl_is_ptr((expr), type) || \ + __builtin_types_compatible_p(__typeof__(expr), type [])) + +/* evaluates to true if expr is a string */ +#define _curl_is_string(expr) \ + (_curl_is_arr((expr), char) || \ + _curl_is_arr((expr), signed char) || \ + _curl_is_arr((expr), unsigned char)) + +/* evaluates to true if expr is a long (no matter the signedness) + * XXX: for now, int is also accepted (and therefore short and char, which + * are promoted to int when passed to a variadic function) */ +#define _curl_is_long(expr) \ + (__builtin_types_compatible_p(__typeof__(expr), long) || \ + __builtin_types_compatible_p(__typeof__(expr), signed long) || \ + __builtin_types_compatible_p(__typeof__(expr), unsigned long) || \ + __builtin_types_compatible_p(__typeof__(expr), int) || \ + __builtin_types_compatible_p(__typeof__(expr), signed int) || \ + __builtin_types_compatible_p(__typeof__(expr), unsigned int) || \ + __builtin_types_compatible_p(__typeof__(expr), short) || \ + __builtin_types_compatible_p(__typeof__(expr), signed short) || \ + __builtin_types_compatible_p(__typeof__(expr), unsigned short) || \ + __builtin_types_compatible_p(__typeof__(expr), char) || \ + __builtin_types_compatible_p(__typeof__(expr), signed char) || \ + __builtin_types_compatible_p(__typeof__(expr), unsigned char)) + +/* evaluates to true if expr is of type curl_off_t */ +#define _curl_is_off_t(expr) \ + (__builtin_types_compatible_p(__typeof__(expr), curl_off_t)) + +/* evaluates to true if expr is abuffer suitable for CURLOPT_ERRORBUFFER */ +/* XXX: also check size of an char[] array? */ +#define _curl_is_error_buffer(expr) \ + (__builtin_types_compatible_p(__typeof__(expr), char *) || \ + __builtin_types_compatible_p(__typeof__(expr), char[])) + +/* evaluates to true if expr is of type (const) void* or (const) FILE* */ +#if 0 +#define _curl_is_cb_data(expr) \ + (_curl_is_ptr((expr), void) || \ + _curl_is_ptr((expr), FILE)) +#else /* be less strict */ +#define _curl_is_cb_data(expr) \ + _curl_is_any_ptr(expr) +#endif + +/* evaluates to true if expr is of type FILE* */ +#define _curl_is_FILE(expr) \ + (__builtin_types_compatible_p(__typeof__(expr), FILE *)) + +/* evaluates to true if expr can be passed as POST data (void* or char*) */ +#define _curl_is_postfields(expr) \ + (_curl_is_ptr((expr), void) || \ + _curl_is_arr((expr), char)) + +/* FIXME: the whole callback checking is messy... + * The idea is to tolerate char vs. void and const vs. not const + * pointers in arguments at least + */ +/* helper: __builtin_types_compatible_p distinguishes between functions and + * function pointers, hide it */ +#define _curl_callback_compatible(func, type) \ + (__builtin_types_compatible_p(__typeof__(func), type) || \ + __builtin_types_compatible_p(__typeof__(func), type*)) + +/* evaluates to true if expr is of type curl_read_callback or "similar" */ +#define _curl_is_read_cb(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), __typeof__(fread)) || \ + __builtin_types_compatible_p(__typeof__(expr), curl_read_callback) || \ + _curl_callback_compatible((expr), _curl_read_callback1) || \ + _curl_callback_compatible((expr), _curl_read_callback2) || \ + _curl_callback_compatible((expr), _curl_read_callback3) || \ + _curl_callback_compatible((expr), _curl_read_callback4) || \ + _curl_callback_compatible((expr), _curl_read_callback5) || \ + _curl_callback_compatible((expr), _curl_read_callback6)) +typedef size_t (_curl_read_callback1)(char *, size_t, size_t, void*); +typedef size_t (_curl_read_callback2)(char *, size_t, size_t, const void*); +typedef size_t (_curl_read_callback3)(char *, size_t, size_t, FILE*); +typedef size_t (_curl_read_callback4)(void *, size_t, size_t, void*); +typedef size_t (_curl_read_callback5)(void *, size_t, size_t, const void*); +typedef size_t (_curl_read_callback6)(void *, size_t, size_t, FILE*); + +/* evaluates to true if expr is of type curl_write_callback or "similar" */ +#define _curl_is_write_cb(expr) \ + (_curl_is_read_cb(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), __typeof__(fwrite)) || \ + __builtin_types_compatible_p(__typeof__(expr), curl_write_callback) || \ + _curl_callback_compatible((expr), _curl_write_callback1) || \ + _curl_callback_compatible((expr), _curl_write_callback2) || \ + _curl_callback_compatible((expr), _curl_write_callback3) || \ + _curl_callback_compatible((expr), _curl_write_callback4) || \ + _curl_callback_compatible((expr), _curl_write_callback5) || \ + _curl_callback_compatible((expr), _curl_write_callback6)) +typedef size_t (_curl_write_callback1)(const char *, size_t, size_t, void*); +typedef size_t (_curl_write_callback2)(const char *, size_t, size_t, + const void*); +typedef size_t (_curl_write_callback3)(const char *, size_t, size_t, FILE*); +typedef size_t (_curl_write_callback4)(const void *, size_t, size_t, void*); +typedef size_t (_curl_write_callback5)(const void *, size_t, size_t, + const void*); +typedef size_t (_curl_write_callback6)(const void *, size_t, size_t, FILE*); + +/* evaluates to true if expr is of type curl_ioctl_callback or "similar" */ +#define _curl_is_ioctl_cb(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), curl_ioctl_callback) || \ + _curl_callback_compatible((expr), _curl_ioctl_callback1) || \ + _curl_callback_compatible((expr), _curl_ioctl_callback2) || \ + _curl_callback_compatible((expr), _curl_ioctl_callback3) || \ + _curl_callback_compatible((expr), _curl_ioctl_callback4)) +typedef curlioerr (_curl_ioctl_callback1)(CURL *, int, void*); +typedef curlioerr (_curl_ioctl_callback2)(CURL *, int, const void*); +typedef curlioerr (_curl_ioctl_callback3)(CURL *, curliocmd, void*); +typedef curlioerr (_curl_ioctl_callback4)(CURL *, curliocmd, const void*); + +/* evaluates to true if expr is of type curl_sockopt_callback or "similar" */ +#define _curl_is_sockopt_cb(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), curl_sockopt_callback) || \ + _curl_callback_compatible((expr), _curl_sockopt_callback1) || \ + _curl_callback_compatible((expr), _curl_sockopt_callback2)) +typedef int (_curl_sockopt_callback1)(void *, curl_socket_t, curlsocktype); +typedef int (_curl_sockopt_callback2)(const void *, curl_socket_t, + curlsocktype); + +/* evaluates to true if expr is of type curl_opensocket_callback or "similar" */ +#define _curl_is_opensocket_cb(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), curl_opensocket_callback) ||\ + _curl_callback_compatible((expr), _curl_opensocket_callback1) || \ + _curl_callback_compatible((expr), _curl_opensocket_callback2) || \ + _curl_callback_compatible((expr), _curl_opensocket_callback3) || \ + _curl_callback_compatible((expr), _curl_opensocket_callback4)) +typedef curl_socket_t (_curl_opensocket_callback1) + (void *, curlsocktype, struct curl_sockaddr *); +typedef curl_socket_t (_curl_opensocket_callback2) + (void *, curlsocktype, const struct curl_sockaddr *); +typedef curl_socket_t (_curl_opensocket_callback3) + (const void *, curlsocktype, struct curl_sockaddr *); +typedef curl_socket_t (_curl_opensocket_callback4) + (const void *, curlsocktype, const struct curl_sockaddr *); + +/* evaluates to true if expr is of type curl_progress_callback or "similar" */ +#define _curl_is_progress_cb(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), curl_progress_callback) || \ + _curl_callback_compatible((expr), _curl_progress_callback1) || \ + _curl_callback_compatible((expr), _curl_progress_callback2)) +typedef int (_curl_progress_callback1)(void *, + double, double, double, double); +typedef int (_curl_progress_callback2)(const void *, + double, double, double, double); + +/* evaluates to true if expr is of type curl_debug_callback or "similar" */ +#define _curl_is_debug_cb(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), curl_debug_callback) || \ + _curl_callback_compatible((expr), _curl_debug_callback1) || \ + _curl_callback_compatible((expr), _curl_debug_callback2) || \ + _curl_callback_compatible((expr), _curl_debug_callback3) || \ + _curl_callback_compatible((expr), _curl_debug_callback4)) +typedef int (_curl_debug_callback1) (CURL *, + curl_infotype, char *, size_t, void *); +typedef int (_curl_debug_callback2) (CURL *, + curl_infotype, char *, size_t, const void *); +typedef int (_curl_debug_callback3) (CURL *, + curl_infotype, const char *, size_t, void *); +typedef int (_curl_debug_callback4) (CURL *, + curl_infotype, const char *, size_t, const void *); + +/* evaluates to true if expr is of type curl_ssl_ctx_callback or "similar" */ +/* this is getting even messier... */ +#define _curl_is_ssl_ctx_cb(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), curl_ssl_ctx_callback) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback1) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback2) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback3) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback4) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback5) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback6) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback7) || \ + _curl_callback_compatible((expr), _curl_ssl_ctx_callback8)) +typedef CURLcode (_curl_ssl_ctx_callback1)(CURL *, void *, void *); +typedef CURLcode (_curl_ssl_ctx_callback2)(CURL *, void *, const void *); +typedef CURLcode (_curl_ssl_ctx_callback3)(CURL *, const void *, void *); +typedef CURLcode (_curl_ssl_ctx_callback4)(CURL *, const void *, const void *); +#ifdef HEADER_SSL_H +/* hack: if we included OpenSSL's ssl.h, we know about SSL_CTX + * this will of course break if we're included before OpenSSL headers... + */ +typedef CURLcode (_curl_ssl_ctx_callback5)(CURL *, SSL_CTX, void *); +typedef CURLcode (_curl_ssl_ctx_callback6)(CURL *, SSL_CTX, const void *); +typedef CURLcode (_curl_ssl_ctx_callback7)(CURL *, const SSL_CTX, void *); +typedef CURLcode (_curl_ssl_ctx_callback8)(CURL *, const SSL_CTX, const void *); +#else +typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback5; +typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback6; +typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback7; +typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback8; +#endif + +/* evaluates to true if expr is of type curl_conv_callback or "similar" */ +#define _curl_is_conv_cb(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), curl_conv_callback) || \ + _curl_callback_compatible((expr), _curl_conv_callback1) || \ + _curl_callback_compatible((expr), _curl_conv_callback2) || \ + _curl_callback_compatible((expr), _curl_conv_callback3) || \ + _curl_callback_compatible((expr), _curl_conv_callback4)) +typedef CURLcode (*_curl_conv_callback1)(char *, size_t length); +typedef CURLcode (*_curl_conv_callback2)(const char *, size_t length); +typedef CURLcode (*_curl_conv_callback3)(void *, size_t length); +typedef CURLcode (*_curl_conv_callback4)(const void *, size_t length); + +/* evaluates to true if expr is of type curl_seek_callback or "similar" */ +#define _curl_is_seek_cb(expr) \ + (_curl_is_NULL(expr) || \ + __builtin_types_compatible_p(__typeof__(expr), curl_seek_callback) || \ + _curl_callback_compatible((expr), _curl_seek_callback1) || \ + _curl_callback_compatible((expr), _curl_seek_callback2)) +typedef CURLcode (*_curl_seek_callback1)(void *, curl_off_t, int); +typedef CURLcode (*_curl_seek_callback2)(const void *, curl_off_t, int); + + +#endif /* __CURL_TYPECHECK_GCC_H */ diff --git a/shared/sentry/external/breakpad/src/third_party/curl/types.h b/shared/sentry/external/breakpad/src/third_party/curl/types.h new file mode 100644 index 000000000..d37d6ae9e --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/curl/types.h @@ -0,0 +1 @@ +/* not used */ diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/LICENSE b/shared/sentry/external/breakpad/src/third_party/libdisasm/LICENSE new file mode 100644 index 000000000..c5638285a --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/LICENSE @@ -0,0 +1,137 @@ + + + + + The "Clarified Artistic License" + + Preamble + +The intent of this document is to state the conditions under which a +Package may be copied, such that the Copyright Holder maintains some +semblance of artistic control over the development of the package, +while giving the users of the package the right to use and distribute +the Package in a more-or-less customary fashion, plus the right to make +reasonable modifications. + +Definitions: + + "Package" refers to the collection of files distributed by the + Copyright Holder, and derivatives of that collection of files + created through textual modification. + + "Standard Version" refers to such a Package if it has not been + modified, or has been modified in accordance with the wishes + of the Copyright Holder as specified below. + + "Copyright Holder" is whoever is named in the copyright or + copyrights for the package. + + "You" is you, if you're thinking about copying or distributing + this Package. + + "Distribution fee" is a fee you charge for providing a copy of this + Package to another party. + + "Freely Available" means that no fee is charged for the right to use + the item, though there may be fees involved in handling the item. + +1. You may make and give away verbatim copies of the source form of the +Standard Version of this Package without restriction, provided that you +duplicate all of the original copyright notices and associated disclaimers. + +2. You may apply bug fixes, portability fixes and other modifications +derived from the Public Domain, or those made Freely Available, or from +the Copyright Holder. A Package modified in such a way shall still be +considered the Standard Version. + +3. You may otherwise modify your copy of this Package in any way, provided +that you insert a prominent notice in each changed file stating how and +when you changed that file, and provided that you do at least ONE of the +following: + + a) place your modifications in the Public Domain or otherwise make them + Freely Available, such as by posting said modifications to Usenet or + an equivalent medium, or placing the modifications on a major archive + site allowing unrestricted access to them, or by allowing the Copyright + Holder to include your modifications in the Standard Version of the + Package. + + b) use the modified Package only within your corporation or organization. + + c) rename any non-standard executables so the names do not conflict + with standard executables, which must also be provided, and provide + a separate manual page for each non-standard executable that clearly + documents how it differs from the Standard Version. + + d) make other distribution arrangements with the Copyright Holder. + + e) permit and encourge anyone who receives a copy of the modified Package + permission to make your modifications Freely Available in some specific + way. + +4. You may distribute the programs of this Package in object code or +executable form, provided that you do at least ONE of the following: + + a) distribute a Standard Version of the executables and library files, + together with instructions (in the manual page or equivalent) on where + to get the Standard Version. + + b) accompany the distribution with the machine-readable source of + the Package with your modifications. + + c) give non-standard executables non-standard names, and clearly + document the differences in manual pages (or equivalent), together + with instructions on where to get the Standard Version. + + d) make other distribution arrangements with the Copyright Holder. + + e) offer the machine-readable source of the Package, with your + modifications, by mail order. + +5. You may charge a distribution fee for any distribution of this Package. +If you offer support for this Package, you may charge any fee you choose +for that support. You may not charge a license fee for the right to use +this Package itself. You may distribute this Package in aggregate with +other (possibly commercial and possibly nonfree) programs as part of a +larger (possibly commercial and possibly nonfree) software distribution, +and charge license fees for other parts of that software distribution, +provided that you do not advertise this Package as a product of your own. +If the Package includes an interpreter, You may embed this Package's +interpreter within an executable of yours (by linking); this shall be +construed as a mere form of aggregation, provided that the complete +Standard Version of the interpreter is so embedded. + +6. The scripts and library files supplied as input to or produced as +output from the programs of this Package do not automatically fall +under the copyright of this Package, but belong to whoever generated +them, and may be sold commercially, and may be aggregated with this +Package. If such scripts or library files are aggregated with this +Package via the so-called "undump" or "unexec" methods of producing a +binary executable image, then distribution of such an image shall +neither be construed as a distribution of this Package nor shall it +fall under the restrictions of Paragraphs 3 and 4, provided that you do +not represent such an executable image as a Standard Version of this +Package. + +7. C subroutines (or comparably compiled subroutines in other +languages) supplied by you and linked into this Package in order to +emulate subroutines and variables of the language defined by this +Package shall not be considered part of this Package, but are the +equivalent of input as in Paragraph 6, provided these subroutines do +not change the language in any way that would cause it to fail the +regression tests for the language. + +8. Aggregation of the Standard Version of the Package with a commercial +distribution is always permitted provided that the use of this Package is +embedded; that is, when no overt attempt is made to make this Package's +interfaces visible to the end user of the commercial distribution. +Such use shall not be construed as a distribution of this Package. + +9. The name of the Copyright Holder may not be used to endorse or promote +products derived from this software without specific prior written permission. + +10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED +WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + + The End diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/Makefile.am b/shared/sentry/external/breakpad/src/third_party/libdisasm/Makefile.am new file mode 100644 index 000000000..bd3129e1a --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/Makefile.am @@ -0,0 +1,43 @@ +include_HEADERS = libdis.h +lib_LTLIBRARIES = libdisasm.la +libdisasm_la_SOURCES = \ + ia32_implicit.c \ + ia32_implicit.h \ + ia32_insn.c \ + ia32_insn.h \ + ia32_invariant.c \ + ia32_invariant.h \ + ia32_modrm.c \ + ia32_modrm.h \ + ia32_opcode_tables.c \ + ia32_opcode_tables.h \ + ia32_operand.c \ + ia32_operand.h \ + ia32_reg.c \ + ia32_reg.h \ + ia32_settings.c \ + ia32_settings.h \ + libdis.h \ + qword.h \ + x86_disasm.c \ + x86_format.c \ + x86_imm.c \ + x86_imm.h \ + x86_insn.c \ + x86_misc.c \ + x86_operand_list.c \ + x86_operand_list.h + +# Cheat to get non-autoconf swig into tarball, +# even if it doesn't build by default. +EXTRA_DIST = \ +swig/Makefile \ +swig/libdisasm.i \ +swig/libdisasm_oop.i \ +swig/python/Makefile-swig \ +swig/perl/Makefile-swig \ +swig/perl/Makefile.PL \ +swig/ruby/Makefile-swig \ +swig/ruby/extconf.rb \ +swig/tcl/Makefile-swig \ +swig/README diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/README.breakpad b/shared/sentry/external/breakpad/src/third_party/libdisasm/README.breakpad new file mode 100644 index 000000000..a34541140 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/README.breakpad @@ -0,0 +1,9 @@ +Name: libdisasm +URL: https://sourceforge.net/projects/bastard/files/libdisasm/0.23/libdisasm-0.23.tar.gz/download +Version: 0.23 +License: Clarified-Artistic +License File: LICENSE + +Description: +This contains a copy of libdisasm. It is no longer under development upstream, +so we keep a copy here to maintain fixes ourselves. diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/TODO b/shared/sentry/external/breakpad/src/third_party/libdisasm/TODO new file mode 100644 index 000000000..148addf9b --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/TODO @@ -0,0 +1,43 @@ +x86_format.c +------------ +intel: jmpf -> jmp, callf -> call +att: jmpf -> ljmp, callf -> lcall + +opcode table +------------ +finish typing instructions +fix flag clear/set/toggle types + +ix64 stuff +---------- +document output file formats in web page +features doc: register aliases, implicit operands, stack mods, +ring0 flags, eflags, cpu model/isa + +ia32_handle_* implementation + +fix operand 0F C2 +CMPPS + +* sysenter, sysexit as CALL types -- preceded by MSR writes +* SYSENTER/SYSEXIT stack : overwrites SS, ESP +* stos, cmps, scas, movs, ins, outs, lods -> OP_PTR +* OP_SIZE in implicit operands +* use OP_SIZE to choose reg sizes! + +DONE?? : +implicit operands: provide action ? +e.g. add/inc for stach, write, etc +replace table numbers in opcodes.dat with +#defines for table names + +replace 0 with INSN_INVALID [or maybe FF for imnvalid and 00 for Not Applicable */ +no wait that is only for prefix tables -- n/p + +if ( prefx) only use if insn != invalid + +these should cover all the wacky disasm exceptions + +for the rep one we can chet, match only a 0x90 + +todo: privilege | ring diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_implicit.c b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_implicit.c new file mode 100644 index 000000000..8b075d2ee --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_implicit.c @@ -0,0 +1,422 @@ +#include + +#include "ia32_implicit.h" +#include "ia32_insn.h" +#include "ia32_reg.h" +#include "x86_operand_list.h" + +/* Conventions: Register operands which are aliases of another register + * operand (e.g. AX in one operand and AL in another) assume that the + * operands are different registers and that alias tracking will resolve + * data flow. This means that something like + * mov ax, al + * would have 'write only' access for AX and 'read only' access for AL, + * even though both AL and AX are read and written */ +typedef struct { + uint32_t type; + uint32_t operand; +} op_implicit_list_t; + +static op_implicit_list_t list_aaa[] = + /* 37 : AAA : rw AL */ + /* 3F : AAS : rw AL */ + {{ OP_R | OP_W, REG_BYTE_OFFSET }, {0}}; /* aaa */ + +static op_implicit_list_t list_aad[] = + /* D5 0A, D5 (ib) : AAD : rw AX */ + /* D4 0A, D4 (ib) : AAM : rw AX */ + {{ OP_R | OP_W, REG_WORD_OFFSET }, {0}}; /* aad */ + +static op_implicit_list_t list_call[] = + /* E8, FF, 9A, FF : CALL : rw ESP, rw EIP */ + /* C2, C3, CA, CB : RET : rw ESP, rw EIP */ + {{ OP_R | OP_W, REG_EIP_INDEX }, + { OP_R | OP_W, REG_ESP_INDEX }, {0}}; /* call, ret */ + +static op_implicit_list_t list_cbw[] = + /* 98 : CBW : r AL, rw AX */ + {{ OP_R | OP_W, REG_WORD_OFFSET }, + { OP_R, REG_BYTE_OFFSET}, {0}}; /* cbw */ + +static op_implicit_list_t list_cwde[] = + /* 98 : CWDE : r AX, rw EAX */ + {{ OP_R | OP_W, REG_DWORD_OFFSET }, + { OP_R, REG_WORD_OFFSET }, {0}}; /* cwde */ + +static op_implicit_list_t list_clts[] = + /* 0F 06 : CLTS : rw CR0 */ + {{ OP_R | OP_W, REG_CTRL_OFFSET}, {0}}; /* clts */ + +static op_implicit_list_t list_cmpxchg[] = + /* 0F B0 : CMPXCHG : rw AL */ + {{ OP_R | OP_W, REG_BYTE_OFFSET }, {0}}; /* cmpxchg */ + +static op_implicit_list_t list_cmpxchgb[] = + /* 0F B1 : CMPXCHG : rw EAX */ + {{ OP_R | OP_W, REG_DWORD_OFFSET }, {0}}; /* cmpxchg */ + +static op_implicit_list_t list_cmpxchg8b[] = + /* 0F C7 : CMPXCHG8B : rw EDX, rw EAX, r ECX, r EBX */ + {{ OP_R | OP_W, REG_DWORD_OFFSET }, + { OP_R | OP_W, REG_DWORD_OFFSET + 2 }, + { OP_R, REG_DWORD_OFFSET + 1 }, + { OP_R, REG_DWORD_OFFSET + 3 }, {0}}; /* cmpxchg8b */ + +static op_implicit_list_t list_cpuid[] = + /* 0F A2 : CPUID : rw EAX, w EBX, w ECX, w EDX */ + {{ OP_R | OP_W, REG_DWORD_OFFSET }, + { OP_W, REG_DWORD_OFFSET + 1 }, + { OP_W, REG_DWORD_OFFSET + 2 }, + { OP_W, REG_DWORD_OFFSET + 3 }, {0}}; /* cpuid */ + +static op_implicit_list_t list_cwd[] = + /* 99 : CWD/CWQ : rw EAX, w EDX */ + {{ OP_R | OP_W, REG_DWORD_OFFSET }, + { OP_W, REG_DWORD_OFFSET + 2 }, {0}}; /* cwd */ + +static op_implicit_list_t list_daa[] = + /* 27 : DAA : rw AL */ + /* 2F : DAS : rw AL */ + {{ OP_R | OP_W, REG_BYTE_OFFSET }, {0}}; /* daa */ + +static op_implicit_list_t list_idiv[] = + /* F6 : DIV, IDIV : r AX, w AL, w AH */ + /* FIXED: first op was EAX, not Aw. TODO: verify! */ + {{ OP_R, REG_WORD_OFFSET }, + { OP_W, REG_BYTE_OFFSET }, + { OP_W, REG_BYTE_OFFSET + 4 }, {0}}; /* div */ + +static op_implicit_list_t list_div[] = + /* F7 : DIV, IDIV : rw EDX, rw EAX */ + {{ OP_R | OP_W, REG_DWORD_OFFSET + 2 }, + { OP_R | OP_W, REG_DWORD_OFFSET }, {0}}; /* div */ + +static op_implicit_list_t list_enter[] = + /* C8 : ENTER : rw ESP w EBP */ + {{ OP_R | OP_W, REG_DWORD_OFFSET + 4 }, + { OP_R, REG_DWORD_OFFSET + 5 }, {0}}; /* enter */ + +static op_implicit_list_t list_f2xm1[] = + /* D9 F0 : F2XM1 : rw ST(0) */ + /* D9 E1 : FABS : rw ST(0) */ + /* D9 E0 : FCHS : rw ST(0) */ + /* D9 FF : FCOS : rw ST(0)*/ + /* D8, DA : FDIV : rw ST(0) */ + /* D8, DA : FDIVR : rw ST(0) */ + /* D9 F2 : FPTAN : rw ST(0) */ + /* D9 FC : FRNDINT : rw ST(0) */ + /* D9 FB : FSINCOS : rw ST(0) */ + /* D9 FE : FSIN : rw ST(0) */ + /* D9 FA : FSQRT : rw ST(0) */ + /* D9 F4 : FXTRACT : rw ST(0) */ + {{ OP_R | OP_W, REG_FPU_OFFSET }, {0}}; /* f2xm1 */ + +static op_implicit_list_t list_fcom[] = + /* D8, DC, DE D9 : FCOM : r ST(0) */ + /* DE, DA : FICOM : r ST(0) */ + /* DF, D8 : FIST : r ST(0) */ + /* D9 E4 : FTST : r ST(0) */ + /* D9 E5 : FXAM : r ST(0) */ + {{ OP_R, REG_FPU_OFFSET }, {0}}; /* fcom */ + +static op_implicit_list_t list_fpatan[] = + /* D9 F3 : FPATAN : r ST(0), rw ST(1) */ + {{ OP_R, REG_FPU_OFFSET }, {0}}; /* fpatan */ + +static op_implicit_list_t list_fprem[] = + /* D9 F8, D9 F5 : FPREM : rw ST(0) r ST(1) */ + /* D9 FD : FSCALE : rw ST(0), r ST(1) */ + {{ OP_R | OP_W, REG_FPU_OFFSET }, + { OP_R, REG_FPU_OFFSET + 1 }, {0}}; /* fprem */ + +static op_implicit_list_t list_faddp[] = + /* DE C1 : FADDP : r ST(0), rw ST(1) */ + /* DE E9 : FSUBP : r ST(0), rw ST(1) */ + /* D9 F1 : FYL2X : r ST(0), rw ST(1) */ + /* D9 F9 : FYL2XP1 : r ST(0), rw ST(1) */ + {{ OP_R, REG_FPU_OFFSET }, + { OP_R | OP_W, REG_FPU_OFFSET + 1 }, {0}}; /* faddp */ + +static op_implicit_list_t list_fucompp[] = + /* DA E9 : FUCOMPP : r ST(0), r ST(1) */ + {{ OP_R, REG_FPU_OFFSET }, + { OP_R, REG_FPU_OFFSET + 1 }, {0}}; /* fucompp */ + +static op_implicit_list_t list_imul[] = + /* F6 : IMUL : r AL, w AX */ + /* F6 : MUL : r AL, w AX */ + {{ OP_R, REG_BYTE_OFFSET }, + { OP_W, REG_WORD_OFFSET }, {0}}; /* imul */ + +static op_implicit_list_t list_mul[] = + /* F7 : IMUL : rw EAX, w EDX */ + /* F7 : MUL : rw EAX, w EDX */ + {{ OP_R | OP_W, REG_DWORD_OFFSET }, + { OP_W, REG_DWORD_OFFSET + 2 }, {0}}; /* imul */ + +static op_implicit_list_t list_lahf[] = + /* 9F : LAHF : r EFLAGS, w AH */ + {{ OP_R, REG_FLAGS_INDEX }, + { OP_W, REG_BYTE_OFFSET + 4 }, {0}}; /* lahf */ + +static op_implicit_list_t list_ldmxcsr[] = + /* 0F AE : LDMXCSR : w MXCSR SSE Control Status Reg */ + {{ OP_W, REG_MXCSG_INDEX }, {0}}; /* ldmxcsr */ + +static op_implicit_list_t list_leave[] = + /* C9 : LEAVE : rw ESP, w EBP */ + {{ OP_R | OP_W, REG_ESP_INDEX }, + { OP_W, REG_DWORD_OFFSET + 5 }, {0}}; /* leave */ + +static op_implicit_list_t list_lgdt[] = + /* 0F 01 : LGDT : w GDTR */ + {{ OP_W, REG_GDTR_INDEX }, {0}}; /* lgdt */ + +static op_implicit_list_t list_lidt[] = + /* 0F 01 : LIDT : w IDTR */ + {{ OP_W, REG_IDTR_INDEX }, {0}}; /* lidt */ + +static op_implicit_list_t list_lldt[] = + /* 0F 00 : LLDT : w LDTR */ + {{ OP_W, REG_LDTR_INDEX }, {0}}; /* lldt */ + +static op_implicit_list_t list_lmsw[] = + /* 0F 01 : LMSW : w CR0 */ + {{ OP_W, REG_CTRL_OFFSET }, {0}}; /* lmsw */ + +static op_implicit_list_t list_loop[] = + /* E0, E1, E2 : LOOP : rw ECX */ + {{ OP_R | OP_W, REG_DWORD_OFFSET + 1 }, {0}};/* loop */ + +static op_implicit_list_t list_ltr[] = + /* 0F 00 : LTR : w Task Register */ + {{ OP_W, REG_TR_INDEX }, {0}}; /* ltr */ + +static op_implicit_list_t list_pop[] = + /* 8F, 58, 1F, 07, 17, 0F A1, 0F A9 : POP : rw ESP */ + /* FF, 50, 6A, 68, 0E, 16, 1E, 06, 0F A0, 0F A8 : PUSH : rw ESP */ + {{ OP_R | OP_W, REG_ESP_INDEX }, {0}}; /* pop, push */ + +static op_implicit_list_t list_popad[] = + /* 61 : POPAD : rw esp, w edi esi ebp ebx edx ecx eax */ + {{ OP_R | OP_W, REG_ESP_INDEX }, + { OP_W, REG_DWORD_OFFSET + 7 }, + { OP_W, REG_DWORD_OFFSET + 6 }, + { OP_W, REG_DWORD_OFFSET + 5 }, + { OP_W, REG_DWORD_OFFSET + 3 }, + { OP_W, REG_DWORD_OFFSET + 2 }, + { OP_W, REG_DWORD_OFFSET + 1 }, + { OP_W, REG_DWORD_OFFSET }, {0}}; /* popad */ + +static op_implicit_list_t list_popfd[] = + /* 9D : POPFD : rw esp, w eflags */ + {{ OP_R | OP_W, REG_ESP_INDEX }, + { OP_W, REG_FLAGS_INDEX }, {0}}; /* popfd */ + +static op_implicit_list_t list_pushad[] = + /* FF, 50, 6A, 68, 0E, 16, 1E, 06, 0F A0, 0F A8 : PUSH : rw ESP */ + /* 60 : PUSHAD : rw esp, r eax ecx edx ebx esp ebp esi edi */ + {{ OP_R | OP_W, REG_ESP_INDEX }, + { OP_R, REG_DWORD_OFFSET }, + { OP_R, REG_DWORD_OFFSET + 1 }, + { OP_R, REG_DWORD_OFFSET + 2 }, + { OP_R, REG_DWORD_OFFSET + 3 }, + { OP_R, REG_DWORD_OFFSET + 5 }, + { OP_R, REG_DWORD_OFFSET + 6 }, + { OP_R, REG_DWORD_OFFSET + 7 }, {0}}; /* pushad */ + +static op_implicit_list_t list_pushfd[] = + /* 9C : PUSHFD : rw esp, r eflags */ + {{ OP_R | OP_W, REG_ESP_INDEX }, + { OP_R, REG_FLAGS_INDEX }, {0}}; /* pushfd */ + +static op_implicit_list_t list_rdmsr[] = + /* 0F 32 : RDMSR : r ECX, w EDX, w EAX */ + {{ OP_R, REG_DWORD_OFFSET + 1 }, + { OP_W, REG_DWORD_OFFSET + 2 }, + { OP_W, REG_DWORD_OFFSET }, {0}}; /* rdmsr */ + +static op_implicit_list_t list_rdpmc[] = + /* 0F 33 : RDPMC : r ECX, w EDX, w EAX */ + {{ OP_R, REG_DWORD_OFFSET + 1 }, + { OP_W, REG_DWORD_OFFSET + 2 }, + { OP_W, REG_DWORD_OFFSET }, {0}}; /* rdpmc */ + +static op_implicit_list_t list_rdtsc[] = + /* 0F 31 : RDTSC : rw EDX, rw EAX */ + {{ OP_R | OP_W, REG_DWORD_OFFSET + 2 }, + { OP_R | OP_W, REG_DWORD_OFFSET }, {0}}; /* rdtsc */ + +static op_implicit_list_t list_rep[] = + /* F3, F2 ... : REP : rw ECX */ + {{ OP_R | OP_W, REG_DWORD_OFFSET + 1 }, {0}};/* rep */ + +static op_implicit_list_t list_rsm[] = + /* 0F AA : RSM : r CR4, r CR0 */ + {{ OP_R, REG_CTRL_OFFSET + 4 }, + { OP_R, REG_CTRL_OFFSET }, {0}}; /* rsm */ + +static op_implicit_list_t list_sahf[] = + /* 9E : SAHF : r ah, rw eflags (set SF ZF AF PF CF) */ + {{ OP_R, REG_DWORD_OFFSET }, {0}}; /* sahf */ + +static op_implicit_list_t list_sgdt[] = + /* 0F : SGDT : r gdtr */ + /* TODO: finish this! */ + {{ OP_R, REG_DWORD_OFFSET }, {0}}; /* sgdt */ + +static op_implicit_list_t list_sidt[] = + /* 0F : SIDT : r idtr */ + /* TODO: finish this! */ + {{ OP_R, REG_DWORD_OFFSET }, {0}}; /* sidt */ + +static op_implicit_list_t list_sldt[] = + /* 0F : SLDT : r ldtr */ + /* TODO: finish this! */ + {{ OP_R, REG_DWORD_OFFSET }, {0}}; /* sldt */ + +static op_implicit_list_t list_smsw[] = + /* 0F : SMSW : r CR0 */ + /* TODO: finish this! */ + {{ OP_R, REG_DWORD_OFFSET }, {0}}; /* smsw */ + +static op_implicit_list_t list_stmxcsr[] = + /* 0F AE : STMXCSR : r MXCSR */ + /* TODO: finish this! */ + {{ OP_R, REG_DWORD_OFFSET }, {0}}; /* stmxcsr */ + +static op_implicit_list_t list_str[] = + /* 0F 00 : STR : r TR (task register) */ + /* TODO: finish this! */ + {{ OP_R, REG_DWORD_OFFSET }, {0}}; /* str */ + +static op_implicit_list_t list_sysenter[] = + /* 0F 34 : SYSENTER : w cs, w eip, w ss, w esp, r CR0, w eflags + * r sysenter_cs_msr, sysenter_esp_msr, sysenter_eip_msr */ + /* TODO: finish this! */ + {{ OP_R, REG_DWORD_OFFSET }, {0}}; /* sysenter */ + +static op_implicit_list_t list_sysexit[] = + /* 0F 35 : SYSEXIT : r edx, r ecx, w cs, w eip, w ss, w esp + * r sysenter_cs_msr */ + /* TODO: finish this! */ + {{ OP_R, REG_DWORD_OFFSET }, {0}}; /* sysexit */ + +static op_implicit_list_t list_wrmsr[] = + /* 0F 30 : WRMST : r edx, r eax, r ecx */ + /* TODO: finish this! */ + {{ OP_R, REG_DWORD_OFFSET }, {0}}; /* wrmsr */ + +static op_implicit_list_t list_xlat[] = + /* D7 : XLAT : rw al r ebx (ptr) */ + /* TODO: finish this! */ + {{ OP_R, REG_DWORD_OFFSET }, {0}}; /* xlat */ +/* TODO: + * monitor 0f 01 c8 eax OP_R ecx OP_R edx OP_R + * mwait 0f 01 c9 eax OP_R ecx OP_R + */ +static op_implicit_list_t list_monitor[] = + {{ OP_R, REG_DWORD_OFFSET }, {0}}; /* monitor */ +static op_implicit_list_t list_mwait[] = + {{ OP_R, REG_DWORD_OFFSET }, {0}}; /* mwait */ + +op_implicit_list_t *op_implicit_list[] = { + /* This is a list of implicit operands which are read/written by + * various x86 instructions. Note that modifications to the stack + * register are mentioned here, but that additional information on + * the effect an instruction has on the stack is contained in the + * x86_insn_t 'stack_mod' and 'stack_mod_val' fields. Use of the + * eflags register, i.e. setting, clearing, and testing flags, is + * not recorded here but rather in the flags_set and flags_tested + * fields of the x86_insn_t.*/ + NULL, + list_aaa, list_aad, list_call, list_cbw, /* 1 - 4 */ + list_cwde, list_clts, list_cmpxchg, list_cmpxchgb, /* 5 - 8 */ + list_cmpxchg8b, list_cpuid, list_cwd, list_daa, /* 9 - 12 */ + list_idiv, list_div, list_enter, list_f2xm1, /* 13 - 16 */ + list_fcom, list_fpatan, list_fprem, list_faddp, /* 17 - 20 */ + list_fucompp, list_imul, list_mul, list_lahf, /* 21 - 24 */ + list_ldmxcsr, list_leave, list_lgdt, list_lidt, /* 25 - 28 */ + list_lldt, list_lmsw, list_loop, list_ltr, /* 29 - 32 */ + list_pop, list_popad, list_popfd, list_pushad, /* 33 - 36 */ + list_pushfd, list_rdmsr, list_rdpmc, list_rdtsc, /* 37 - 40 */ + /* NOTE: 'REP' is a hack since it is a prefix: if its position + * in the table changes, then change IDX_IMPLICIT_REP in the .h */ + list_rep, list_rsm, list_sahf, list_sgdt, /* 41 - 44 */ + list_sidt, list_sldt, list_smsw, list_stmxcsr, /* 45 - 48 */ + list_str, list_sysenter, list_sysexit, list_wrmsr, /* 49 - 52 */ + list_xlat, list_monitor, list_mwait, /* 53 - 55*/ + NULL /* end of list */ + }; + +#define LAST_IMPL_IDX 55 + +static void handle_impl_reg( x86_op_t *op, uint32_t val ) { + x86_reg_t *reg = &op->data.reg; + op->type = op_register; + ia32_handle_register( reg, (unsigned int) val ); + switch (reg->size) { + case 1: + op->datatype = op_byte; break; + case 2: + op->datatype = op_word; break; + case 4: + op->datatype = op_dword; break; + case 8: + op->datatype = op_qword; break; + case 10: + op->datatype = op_extreal; break; + case 16: + op->datatype = op_dqword; break; + } + return; +} + +/* 'impl_idx' is the value from the opcode table: between 1 and LAST_IMPL_IDX */ +/* returns number of operands added */ +unsigned int ia32_insn_implicit_ops( x86_insn_t *insn, unsigned int impl_idx ) { + op_implicit_list_t *list; + x86_op_t *op; + unsigned int num = 0; + + if (! impl_idx || impl_idx > LAST_IMPL_IDX ) { + return 0; + } + + for ( list = op_implicit_list[impl_idx]; list->type; list++, num++ ) { + enum x86_op_access access = (enum x86_op_access) OP_PERM(list->type); + enum x86_op_flags flags = (enum x86_op_flags) (OP_FLAGS(list->type) >> 12); + + op = NULL; + /* In some cases (MUL), EAX is an implicit operand hardcoded in + * the instruction without being explicitly listed in assembly. + * For this situation, find the hardcoded operand and add the + * implied flag rather than adding a new implicit operand. */ + x86_oplist_t * existing; + if (ia32_true_register_id(list->operand) == REG_DWORD_OFFSET) { + for ( existing = insn->operands; existing; existing = existing->next ) { + if (existing->op.type == op_register && + existing->op.data.reg.id == list->operand) { + op = &existing->op; + break; + } + } + } + if (!op) { + op = x86_operand_new( insn ); + /* all implicit operands are registers */ + handle_impl_reg( op, list->operand ); + /* decrement the 'explicit count' incremented by default in + * x86_operand_new */ + insn->explicit_count = insn->explicit_count -1; + } + if (!op) { + return num; /* gah! return early */ + } + op->access |= access; + op->flags |= flags; + op->flags |= op_implied; + } + + return num; +} diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_implicit.h b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_implicit.h new file mode 100644 index 000000000..0002b28b9 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_implicit.h @@ -0,0 +1,13 @@ +#ifndef IA32_IMPLICIT_H +#define IA32_IMPLICIT_H + +#include "libdis.h" + +/* OK, this is a hack to deal with prefixes having implicit operands... + * thought I had removed all the old hackishness ;( */ + +#define IDX_IMPLICIT_REP 41 /* change this if the table changes! */ + +unsigned int ia32_insn_implicit_ops( x86_insn_t *insn, unsigned int impl_idx ); + +#endif diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_insn.c b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_insn.c new file mode 100644 index 000000000..cc277608b --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_insn.c @@ -0,0 +1,623 @@ +#include +#include +#include +#include "qword.h" + +#include "ia32_insn.h" +#include "ia32_opcode_tables.h" + +#include "ia32_reg.h" +#include "ia32_operand.h" +#include "ia32_implicit.h" +#include "ia32_settings.h" + +#include "libdis.h" + +extern ia32_table_desc_t ia32_tables[]; +extern ia32_settings_t ia32_settings; + +#define IS_SP( op ) (op->type == op_register && \ + (op->data.reg.id == REG_ESP_INDEX || \ + op->data.reg.alias == REG_ESP_INDEX) ) +#define IS_IMM( op ) (op->type == op_immediate ) + +#ifdef WIN32 +# define INLINE +#else +# define INLINE inline +#endif + +/* for calculating stack modification based on an operand */ +static INLINE int32_t long_from_operand( x86_op_t *op ) { + + if (! IS_IMM(op) ) { + return 0L; + } + + switch ( op->datatype ) { + case op_byte: + return (int32_t) op->data.sbyte; + case op_word: + return (int32_t) op->data.sword; + case op_qword: + return (int32_t) op->data.sqword; + case op_dword: + return op->data.sdword; + default: + /* these are not used in stack insn */ + break; + } + + return 0L; +} + + +/* determine what this insn does to the stack */ +static void ia32_stack_mod(x86_insn_t *insn) { + x86_op_t *dest, *src = NULL; + + if (! insn || ! insn->operands ) { + return; + } + + dest = &insn->operands->op; + if ( dest ) { + src = &insn->operands->next->op; + } + + insn->stack_mod = 0; + insn->stack_mod_val = 0; + + switch ( insn->type ) { + case insn_call: + case insn_callcc: + insn->stack_mod = 1; + insn->stack_mod_val = insn->addr_size * -1; + break; + case insn_push: + insn->stack_mod = 1; + insn->stack_mod_val = insn->addr_size * -1; + break; + case insn_return: + insn->stack_mod = 1; + insn->stack_mod_val = insn->addr_size; + case insn_int: case insn_intcc: + case insn_iret: + break; + case insn_pop: + insn->stack_mod = 1; + if (! IS_SP( dest ) ) { + insn->stack_mod_val = insn->op_size; + } /* else we don't know the stack change in a pop esp */ + break; + case insn_enter: + insn->stack_mod = 1; + insn->stack_mod_val = 0; /* TODO : FIX */ + break; + case insn_leave: + insn->stack_mod = 1; + insn->stack_mod_val = 0; /* TODO : FIX */ + break; + case insn_pushregs: + insn->stack_mod = 1; + insn->stack_mod_val = 0; /* TODO : FIX */ + break; + case insn_popregs: + insn->stack_mod = 1; + insn->stack_mod_val = 0; /* TODO : FIX */ + break; + case insn_pushflags: + insn->stack_mod = 1; + insn->stack_mod_val = 0; /* TODO : FIX */ + break; + case insn_popflags: + insn->stack_mod = 1; + insn->stack_mod_val = 0; /* TODO : FIX */ + break; + case insn_add: + if ( IS_SP( dest ) ) { + insn->stack_mod = 1; + insn->stack_mod_val = long_from_operand( src ); + } + break; + case insn_sub: + if ( IS_SP( dest ) ) { + insn->stack_mod = 1; + insn->stack_mod_val = long_from_operand( src ); + insn->stack_mod_val *= -1; + } + break; + case insn_inc: + if ( IS_SP( dest ) ) { + insn->stack_mod = 1; + insn->stack_mod_val = 1; + } + break; + case insn_dec: + if ( IS_SP( dest ) ) { + insn->stack_mod = 1; + insn->stack_mod_val = 1; + } + break; + case insn_mov: case insn_movcc: + case insn_xchg: case insn_xchgcc: + case insn_mul: case insn_div: + case insn_shl: case insn_shr: + case insn_rol: case insn_ror: + case insn_and: case insn_or: + case insn_not: case insn_neg: + case insn_xor: + if ( IS_SP( dest ) ) { + insn->stack_mod = 1; + } + break; + default: + break; + } + if (! strcmp("enter", insn->mnemonic) ) { + insn->stack_mod = 1; + } else if (! strcmp("leave", insn->mnemonic) ) { + insn->stack_mod = 1; + } + + /* for mov, etc we return 0 -- unknown stack mod */ + + return; +} + +/* get the cpu details for this insn from cpu flags int */ +static void ia32_handle_cpu( x86_insn_t *insn, unsigned int cpu ) { + insn->cpu = (enum x86_insn_cpu) CPU_MODEL(cpu); + insn->isa = (enum x86_insn_isa) (ISA_SUBSET(cpu)) >> 16; + return; +} + +/* handle mnemonic type and group */ +static void ia32_handle_mnemtype(x86_insn_t *insn, unsigned int mnemtype) { + unsigned int type = mnemtype & ~INS_FLAG_MASK; + insn->group = (enum x86_insn_group) (INS_GROUP(type)) >> 12; + insn->type = (enum x86_insn_type) INS_TYPE(type); + + return; +} + +static void ia32_handle_notes(x86_insn_t *insn, unsigned int notes) { + insn->note = (enum x86_insn_note) notes; + return; +} + +static void ia32_handle_eflags( x86_insn_t *insn, unsigned int eflags) { + unsigned int flags; + + /* handle flags effected */ + flags = INS_FLAGS_TEST(eflags); + /* handle weird OR cases */ + /* these are either JLE (ZF | SF<>OF) or JBE (CF | ZF) */ + if (flags & INS_TEST_OR) { + flags &= ~INS_TEST_OR; + if ( flags & INS_TEST_ZERO ) { + flags &= ~INS_TEST_ZERO; + if ( flags & INS_TEST_CARRY ) { + flags &= ~INS_TEST_CARRY ; + flags |= (int)insn_carry_or_zero_set; + } else if ( flags & INS_TEST_SFNEOF ) { + flags &= ~INS_TEST_SFNEOF; + flags |= (int)insn_zero_set_or_sign_ne_oflow; + } + } + } + insn->flags_tested = (enum x86_flag_status) flags; + + insn->flags_set = (enum x86_flag_status) INS_FLAGS_SET(eflags) >> 16; + + return; +} + +static void ia32_handle_prefix( x86_insn_t *insn, unsigned int prefixes ) { + + insn->prefix = (enum x86_insn_prefix) prefixes & PREFIX_MASK; // >> 20; + if (! (insn->prefix & PREFIX_PRINT_MASK) ) { + /* no printable prefixes */ + insn->prefix = insn_no_prefix; + } + + /* concat all prefix strings */ + if ( (unsigned int)insn->prefix & PREFIX_LOCK ) { + strncat(insn->prefix_string, "lock ", 32 - + strlen(insn->prefix_string)); + } + + if ( (unsigned int)insn->prefix & PREFIX_REPNZ ) { + strncat(insn->prefix_string, "repnz ", 32 - + strlen(insn->prefix_string)); + } else if ( (unsigned int)insn->prefix & PREFIX_REPZ ) { + strncat(insn->prefix_string, "repz ", 32 - + strlen(insn->prefix_string)); + } + + return; +} + + +static void reg_32_to_16( x86_op_t *op, x86_insn_t *insn, void *arg ) { + + /* if this is a 32-bit register and it is a general register ... */ + if ( op->type == op_register && op->data.reg.size == 4 && + (op->data.reg.type & reg_gen) ) { + /* WORD registers are 8 indices off from DWORD registers */ + ia32_handle_register( &(op->data.reg), + op->data.reg.id + 8 ); + } +} + +static void handle_insn_metadata( x86_insn_t *insn, ia32_insn_t *raw_insn ) { + ia32_handle_mnemtype( insn, raw_insn->mnem_flag ); + ia32_handle_notes( insn, raw_insn->notes ); + ia32_handle_eflags( insn, raw_insn->flags_effected ); + ia32_handle_cpu( insn, raw_insn->cpu ); + ia32_stack_mod( insn ); +} + +static size_t ia32_decode_insn( unsigned char *buf, size_t buf_len, + ia32_insn_t *raw_insn, x86_insn_t *insn, + unsigned int prefixes ) { + size_t size, op_size; + unsigned char modrm; + + /* this should never happen, but just in case... */ + if ( raw_insn->mnem_flag == INS_INVALID ) { + return 0; + } + + if (ia32_settings.options & opt_16_bit) { + insn->op_size = ( prefixes & PREFIX_OP_SIZE ) ? 4 : 2; + insn->addr_size = ( prefixes & PREFIX_ADDR_SIZE ) ? 4 : 2; + } else { + insn->op_size = ( prefixes & PREFIX_OP_SIZE ) ? 2 : 4; + insn->addr_size = ( prefixes & PREFIX_ADDR_SIZE ) ? 2 : 4; + } + + + /* ++++ 1. Copy mnemonic and mnemonic-flags to CODE struct */ + if ((ia32_settings.options & opt_att_mnemonics) && raw_insn->mnemonic_att[0]) { + strncpy( insn->mnemonic, raw_insn->mnemonic_att, 16 ); + } + else { + strncpy( insn->mnemonic, raw_insn->mnemonic, 16 ); + } + ia32_handle_prefix( insn, prefixes ); + + handle_insn_metadata( insn, raw_insn ); + + /* prefetch the next byte in case it is a modr/m byte -- saves + * worrying about whether the 'mod/rm' operand or the 'reg' operand + * occurs first */ + modrm = GET_BYTE( buf, buf_len ); + + /* ++++ 2. Decode Explicit Operands */ + /* Intel uses up to 3 explicit operands in its instructions; + * the first is 'dest', the second is 'src', and the third + * is an additional source value (usually an immediate value, + * e.g. in the MUL instructions). These three explicit operands + * are encoded in the opcode tables, even if they are not used + * by the instruction. Additional implicit operands are stored + * in a supplemental table and are handled later. */ + + op_size = ia32_decode_operand( buf, buf_len, insn, raw_insn->dest, + raw_insn->dest_flag, prefixes, modrm ); + /* advance buffer, increase size if necessary */ + buf += op_size; + buf_len -= op_size; + size = op_size; + + op_size = ia32_decode_operand( buf, buf_len, insn, raw_insn->src, + raw_insn->src_flag, prefixes, modrm ); + buf += op_size; + buf_len -= op_size; + size += op_size; + + op_size = ia32_decode_operand( buf, buf_len, insn, raw_insn->aux, + raw_insn->aux_flag, prefixes, modrm ); + size += op_size; + + + /* ++++ 3. Decode Implicit Operands */ + /* apply implicit operands */ + ia32_insn_implicit_ops( insn, raw_insn->implicit_ops ); + /* we have one small inelegant hack here, to deal with + * the two prefixes that have implicit operands. If Intel + * adds more, we'll change the algorithm to suit :) */ + if ( (prefixes & PREFIX_REPZ) || (prefixes & PREFIX_REPNZ) ) { + ia32_insn_implicit_ops( insn, IDX_IMPLICIT_REP ); + } + + + /* 16-bit hack: foreach operand, if 32-bit reg, make 16-bit reg */ + if ( insn->op_size == 2 ) { + x86_operand_foreach( insn, reg_32_to_16, NULL, op_any ); + } + + return size; +} + + +/* convenience routine */ +#define USES_MOD_RM(flag) \ + (flag == ADDRMETH_E || flag == ADDRMETH_M || flag == ADDRMETH_Q || \ + flag == ADDRMETH_W || flag == ADDRMETH_R) + +static int uses_modrm_flag( unsigned int flag ) { + unsigned int meth; + if ( flag == ARG_NONE ) { + return 0; + } + meth = (flag & ADDRMETH_MASK); + if ( USES_MOD_RM(meth) ) { + return 1; + } + + return 0; +} + +/* This routine performs the actual byte-by-byte opcode table lookup. + * Originally it was pretty simple: get a byte, adjust it to a proper + * index into the table, then check the table row at that index to + * determine what to do next. But is anything that simple with Intel? + * This is now a huge, convoluted mess, mostly of bitter comments. */ +/* buf: pointer to next byte to read from stream + * buf_len: length of buf + * table: index of table to use for lookups + * raw_insn: output pointer that receives opcode definition + * prefixes: output integer that is encoded with prefixes in insn + * returns : number of bytes consumed from stream during lookup */ +size_t ia32_table_lookup( unsigned char *buf, size_t buf_len, + unsigned int table, ia32_insn_t **raw_insn, + unsigned int *prefixes ) { + unsigned char *next, op = buf[0]; /* byte value -- 'opcode' */ + size_t size = 1, sub_size = 0, next_len; + ia32_table_desc_t *table_desc; + unsigned int subtable, prefix = 0, recurse_table = 0; + + table_desc = &ia32_tables[table]; + + op = GET_BYTE( buf, buf_len ); + + if ( table_desc->type == tbl_fpu && op > table_desc->maxlim) { + /* one of the fucking FPU tables out of the 00-BH range */ + /* OK,. this is a bit of a hack -- the proper way would + * have been to use subtables in the 00-BF FPU opcode tables, + * but that is rather wasteful of space... */ + table_desc = &ia32_tables[table +1]; + } + + /* PERFORM TABLE LOOKUP */ + + /* ModR/M trick: shift extension bits into lowest bits of byte */ + /* Note: non-ModR/M tables have a shift value of 0 */ + op >>= table_desc->shift; + + /* ModR/M trick: mask out high bits to turn extension into an index */ + /* Note: non-ModR/M tables have a mask value of 0xFF */ + op &= table_desc->mask; + + + /* Sparse table trick: check that byte is <= max value */ + /* Note: full (256-entry) tables have a maxlim of 155 */ + if ( op > table_desc->maxlim ) { + /* this is a partial table, truncated at the tail, + and op is out of range! */ + return INVALID_INSN; + } + + /* Sparse table trick: check that byte is >= min value */ + /* Note: full (256-entry) tables have a minlim of 0 */ + if ( table_desc->minlim > op ) { + /* this is a partial table, truncated at the head, + and op is out of range! */ + return INVALID_INSN; + } + /* adjust op to be an offset from table index 0 */ + op -= table_desc->minlim; + + /* Yay! 'op' is now fully adjusted to be an index into 'table' */ + *raw_insn = &(table_desc->table[op]); + //printf("BYTE %X TABLE %d OP %X\n", buf[0], table, op ); + + if ( (*raw_insn)->mnem_flag & INS_FLAG_PREFIX ) { + prefix = (*raw_insn)->mnem_flag & PREFIX_MASK; + } + + + /* handle escape to a multibyte/coproc/extension/etc table */ + /* NOTE: if insn is a prefix and has a subtable, then we + * only recurse if this is the first prefix byte -- + * that is, if *prefixes is 0. + * NOTE also that suffix tables are handled later */ + subtable = (*raw_insn)->table; + + if ( subtable && ia32_tables[subtable].type != tbl_suffix && + (! prefix || ! *prefixes) ) { + + if ( ia32_tables[subtable].type == tbl_ext_ext || + ia32_tables[subtable].type == tbl_fpu_ext ) { + /* opcode extension: reuse current byte in buffer */ + next = buf; + next_len = buf_len; + } else { + /* "normal" opcode: advance to next byte in buffer */ + if ( buf_len > 1 ) { + next = &buf[1]; + next_len = buf_len - 1; + } + else { + // buffer is truncated + return INVALID_INSN; + } + } + /* we encountered a multibyte opcode: recurse using the + * table specified in the opcode definition */ + sub_size = ia32_table_lookup( next, next_len, subtable, + raw_insn, prefixes ); + + /* SSE/prefix hack: if the original opcode def was a + * prefix that specified a subtable, and the subtable + * lookup returned a valid insn, then we have encountered + * an SSE opcode definition; otherwise, we pretend we + * never did the subtable lookup, and deal with the + * prefix normally later */ + if ( prefix && ( sub_size == INVALID_INSN || + INS_TYPE((*raw_insn)->mnem_flag) == INS_INVALID ) ) { + /* this is a prefix, not an SSE insn : + * lookup next byte in main table, + * subsize will be reset during the + * main table lookup */ + recurse_table = 1; + } else { + /* this is either a subtable (two-byte) insn + * or an invalid insn: either way, set prefix + * to NULL and end the opcode lookup */ + prefix = 0; + // short-circuit lookup on invalid insn + if (sub_size == INVALID_INSN) return INVALID_INSN; + } + } else if ( prefix ) { + recurse_table = 1; + } + + /* by default, we assume that we have the opcode definition, + * and there is no need to recurse on the same table, but + * if we do then a prefix was encountered... */ + if ( recurse_table ) { + /* this must have been a prefix: use the same table for + * lookup of the next byte */ + sub_size = ia32_table_lookup( &buf[1], buf_len - 1, table, + raw_insn, prefixes ); + + // short-circuit lookup on invalid insn + if (sub_size == INVALID_INSN) return INVALID_INSN; + + /* a bit of a hack for branch hints */ + if ( prefix & BRANCH_HINT_MASK ) { + if ( INS_GROUP((*raw_insn)->mnem_flag) == INS_EXEC ) { + /* segment override prefixes are invalid for + * all branch instructions, so delete them */ + prefix &= ~PREFIX_REG_MASK; + } else { + prefix &= ~BRANCH_HINT_MASK; + } + } + + /* apply prefix to instruction */ + + /* TODO: implement something enforcing prefix groups */ + (*prefixes) |= prefix; + } + + /* if this lookup was in a ModR/M table, then an opcode byte is + * NOT consumed: subtract accordingly. NOTE that if none of the + * operands used the ModR/M, then we need to consume the byte + * here, but ONLY in the 'top-level' opcode extension table */ + + if ( table_desc->type == tbl_ext_ext ) { + /* extensions-to-extensions never consume a byte */ + --size; + } else if ( (table_desc->type == tbl_extension || + table_desc->type == tbl_fpu || + table_desc->type == tbl_fpu_ext ) && + /* extensions that have an operand encoded in ModR/M + * never consume a byte */ + (uses_modrm_flag((*raw_insn)->dest_flag) || + uses_modrm_flag((*raw_insn)->src_flag) ) ) { + --size; + } + + size += sub_size; + + return size; +} + +static size_t handle_insn_suffix( unsigned char *buf, size_t buf_len, + ia32_insn_t *raw_insn, x86_insn_t * insn ) { + ia32_insn_t *sfx_insn; + size_t size; + unsigned int prefixes = 0; + + size = ia32_table_lookup( buf, buf_len, raw_insn->table, &sfx_insn, + &prefixes ); + if (size == INVALID_INSN || sfx_insn->mnem_flag == INS_INVALID ) { + return 0; + } + + strncpy( insn->mnemonic, sfx_insn->mnemonic, 16 ); + handle_insn_metadata( insn, sfx_insn ); + + return 1; +} + +/* invalid instructions are handled by returning 0 [error] from the + * function, setting the size of the insn to 1 byte, and copying + * the byte at the start of the invalid insn into the x86_insn_t. + * if the caller is saving the x86_insn_t for invalid instructions, + * instead of discarding them, this will maintain a consistent + * address space in the x86_insn_ts */ + +/* this function is called by the controlling disassembler, so its name and + * calling convention cannot be changed */ +/* buf points to the loc of the current opcode (start of the + * instruction) in the instruction stream. The instruction + * stream is assumed to be a buffer of bytes read directly + * from the file for the purpose of disassembly; a mem-mapped + * file is ideal for * this. + * insn points to a code structure to be filled by instr_decode + * returns the size of the decoded instruction in bytes */ +size_t ia32_disasm_addr( unsigned char * buf, size_t buf_len, + x86_insn_t *insn ) { + ia32_insn_t *raw_insn = NULL; + unsigned int prefixes = 0; + size_t size, sfx_size; + + if ( (ia32_settings.options & opt_ignore_nulls) && buf_len > 3 && + !buf[0] && !buf[1] && !buf[2] && !buf[3]) { + /* IF IGNORE_NULLS is set AND + * first 4 bytes in the intruction stream are NULL + * THEN return 0 (END_OF_DISASSEMBLY) */ + /* TODO: set errno */ + MAKE_INVALID( insn, buf ); + return 0; /* 4 00 bytes in a row? This isn't code! */ + } + + /* Perform recursive table lookup starting with main table (0) */ + size = ia32_table_lookup(buf, buf_len, idx_Main, &raw_insn, &prefixes); + if ( size == INVALID_INSN || size > buf_len || raw_insn->mnem_flag == INS_INVALID ) { + MAKE_INVALID( insn, buf ); + /* TODO: set errno */ + return 0; + } + + /* We now have the opcode itself figured out: we can decode + * the rest of the instruction. */ + size += ia32_decode_insn( &buf[size], buf_len - size, raw_insn, insn, + prefixes ); + if ( raw_insn->mnem_flag & INS_FLAG_SUFFIX ) { + /* AMD 3DNow! suffix -- get proper operand type here */ + sfx_size = handle_insn_suffix( &buf[size], buf_len - size, + raw_insn, insn ); + if (! sfx_size ) { + /* TODO: set errno */ + MAKE_INVALID( insn, buf ); + return 0; + } + + size += sfx_size; + } + + if (! size ) { + /* invalid insn */ + MAKE_INVALID( insn, buf ); + return 0; + } + + + insn->size = size; + return size; /* return size of instruction in bytes */ +} diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_insn.h b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_insn.h new file mode 100644 index 000000000..d3f36c3b2 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_insn.h @@ -0,0 +1,506 @@ +#ifndef IA32_INSN_H +#define IA32_INSN_H +/* this file contains the structure of opcode definitions and the + * constants they use */ + +#include +#include "libdis.h" + + +#define GET_BYTE( buf, buf_len ) buf_len ? *buf : 0 + +#define OP_SIZE_16 1 +#define OP_SIZE_32 2 +#define ADDR_SIZE_16 4 +#define ADDR_SIZE_32 8 + +#define MAX_INSTRUCTION_SIZE 20 + +/* invalid instructions are handled by returning 0 [error] from the + * function, setting the size of the insn to 1 byte, and copying + * the byte at the start of the invalid insn into the x86_insn_t. + * if the caller is saving the x86_insn_t for invalid instructions, + * instead of discarding them, this will maintain a consistent + * address space in the x86_insn_ts */ + +#define INVALID_INSN ((size_t) -1) /* return value for invalid insn */ +#define MAKE_INVALID( i, buf ) \ + strcpy( i->mnemonic, "invalid" ); \ + x86_oplist_free( i ); \ + i->size = 1; \ + i->group = insn_none; \ + i->type = insn_invalid; \ + memcpy( i->bytes, buf, 1 ); + + +size_t ia32_disasm_addr( unsigned char * buf, size_t buf_len, + x86_insn_t *insn); + + +/* --------------------------------------------------------- Table Lookup */ +/* IA32 Instruction defintion for ia32_opcodes.c */ +typedef struct { + unsigned int table; /* escape to this sub-table */ + unsigned int mnem_flag; /* Flags referring to mnemonic */ + unsigned int notes; /* Notes for this instruction */ + unsigned int dest_flag, src_flag, aux_flag; /* and for specific operands */ + unsigned int cpu; /* minimumCPU [AND with clocks?? */ + char mnemonic[16]; /* buffers for building instruction */ + char mnemonic_att[16]; /* at&t style mnemonic name */ + int32_t dest; + int32_t src; + int32_t aux; + unsigned int flags_effected; + unsigned int implicit_ops; /* implicit operands */ +} ia32_insn_t; + + + +/* --------------------------------------------------------- Prefixes */ +/* Prefix Flags */ +/* Prefixes, same order as in the manual */ +/* had to reverse the values of the first three as they were entered into + * libdis.h incorrectly. */ +#define PREFIX_LOCK 0x0004 +#define PREFIX_REPNZ 0x0002 +#define PREFIX_REPZ 0x0001 +#define PREFIX_OP_SIZE 0x0010 +#define PREFIX_ADDR_SIZE 0x0020 +#define PREFIX_CS 0x0100 +#define PREFIX_SS 0x0200 +#define PREFIX_DS 0x0300 +#define PREFIX_ES 0x0400 +#define PREFIX_FS 0x0500 +#define PREFIX_GS 0x0600 +#define PREFIX_TAKEN 0x1000 /* branch taken */ +#define PREFIX_NOTTAKEN 0x2000 /* branch not taken */ +#define PREFIX_REG_MASK 0x0F00 +#define BRANCH_HINT_MASK 0x3000 +#define PREFIX_PRINT_MASK 0x000F /* printable prefixes */ +#define PREFIX_MASK 0xFFFF + +/* ---------------------------------------------------------- CPU Type */ + +#define cpu_8086 0x0001 +#define cpu_80286 0x0002 +#define cpu_80386 0x0003 +#define cpu_80387 0x0004 /* originally these were a co-proc */ +#define cpu_80486 0x0005 +#define cpu_PENTIUM 0x0006 +#define cpu_PENTPRO 0x0007 +#define cpu_PENTIUM2 0x0008 +#define cpu_PENTIUM3 0x0009 +#define cpu_PENTIUM4 0x000A +#define cpu_K6 0x0010 +#define cpu_K7 0x0020 +#define cpu_ATHLON 0x0030 +#define CPU_MODEL_MASK 0xFFFF +#define CPU_MODEL(cpu) (cpu & CPU_MODEL_MASK) +/* intel instruction subsets */ +#define isa_GP 0x10000 /* General Purpose Instructions */ +#define isa_FPU 0x20000 /* FPU instructions */ +#define isa_FPUMGT 0x30000 /* FPU/SIMD Management */ +#define isa_MMX 0x40000 /* MMX */ +#define isa_SSE1 0x50000 /* SSE */ +#define isa_SSE2 0x60000 /* SSE 2 */ +#define isa_SSE3 0x70000 /* SSE 3 */ +#define isa_3DNOW 0x80000 /* AMD 3d Now */ +#define isa_SYS 0x90000 /* System Instructions */ +#define ISA_SUBSET_MASK 0xFFFF0000 +#define ISA_SUBSET(isa) (isa & ISA_SUBSET_MASK) + + +/* ------------------------------------------------------ Operand Decoding */ +#define ARG_NONE 0 + +/* Using a mask allows us to store info such as OP_SIGNED in the + * operand flags field */ +#define OPFLAGS_MASK 0x0000FFFF + +/* Operand Addressing Methods, per intel manual */ +#define ADDRMETH_MASK 0x00FF0000 + +/* note: for instructions with implied operands, use no ADDRMETH */ +#define ADDRMETH_A 0x00010000 +#define ADDRMETH_C 0x00020000 +#define ADDRMETH_D 0x00030000 +#define ADDRMETH_E 0x00040000 +#define ADDRMETH_F 0x00050000 +#define ADDRMETH_G 0x00060000 +#define ADDRMETH_I 0x00070000 +#define ADDRMETH_J 0x00080000 +#define ADDRMETH_M 0x00090000 +#define ADDRMETH_O 0x000A0000 +#define ADDRMETH_P 0x000B0000 +#define ADDRMETH_Q 0x000C0000 +#define ADDRMETH_R 0x000D0000 +#define ADDRMETH_S 0x000E0000 +#define ADDRMETH_T 0x000F0000 +#define ADDRMETH_V 0x00100000 +#define ADDRMETH_W 0x00110000 +#define ADDRMETH_X 0x00120000 +#define ADDRMETH_Y 0x00130000 +#define ADDRMETH_RR 0x00140000 /* gen reg hard-coded in opcode */ +#define ADDRMETH_RS 0x00150000 /* seg reg hard-coded in opcode */ +#define ADDRMETH_RT 0x00160000 /* test reg hard-coded in opcode */ +#define ADDRMETH_RF 0x00170000 /* fpu reg hard-coded in opcode */ +#define ADDRMETH_II 0x00180000 /* immediate hard-coded in opcode */ +#define ADDRMETH_PP 0x00190000 /* mm reg ONLY in modr/m field */ +#define ADDRMETH_VV 0x001A0000 /* xmm reg ONLY in mod/rm field */ + +/* Operand Types, per intel manual */ +#define OPTYPE_MASK 0xFF000000 + +#define OPTYPE_a 0x01000000 /* BOUND: h:h or w:w */ +#define OPTYPE_b 0x02000000 /* byte */ +#define OPTYPE_c 0x03000000 /* byte or word */ +#define OPTYPE_d 0x04000000 /* word */ +#define OPTYPE_dq 0x05000000 /* qword */ +#define OPTYPE_p 0x06000000 /* 16:16 or 16:32 pointer */ +#define OPTYPE_pi 0x07000000 /* dword MMX reg */ +#define OPTYPE_ps 0x08000000 /* 128-bit single fp */ +#define OPTYPE_q 0x09000000 /* dword */ +#define OPTYPE_s 0x0A000000 /* 6-byte descriptor */ +#define OPTYPE_ss 0x0B000000 /* scalar of 128-bit single fp */ +#define OPTYPE_si 0x0C000000 /* word general register */ +#define OPTYPE_v 0x0D000000 /* hword or word */ +#define OPTYPE_w 0x0E000000 /* hword */ +#define OPTYPE_m 0x0F000000 /* to handle LEA */ +#define OPTYPE_none 0xFF000000 /* no valid operand size, INVLPG */ + +/* custom ones for FPU instructions */ +#define OPTYPE_fs 0x10000000 /* pointer to single-real*/ +#define OPTYPE_fd 0x20000000 /* pointer to double real */ +#define OPTYPE_fe 0x30000000 /* pointer to extended real */ +#define OPTYPE_fb 0x40000000 /* pointer to packed BCD */ +#define OPTYPE_fv 0x50000000 /* pointer to FPU env: 14|28-bytes */ +#define OPTYPE_ft 0x60000000 /* pointer to FPU state: 94|108-bytes */ +#define OPTYPE_fx 0x70000000 /* pointer to FPU regs: 512 bites */ +#define OPTYPE_fp 0x80000000 /* general fpu register: dbl ext */ + +/* SSE2 operand types */ +#define OPTYPE_sd 0x90000000 /* scalar of 128-bit double fp */ +#define OPTYPE_pd 0xA0000000 /* 128-bit double fp */ + + + +/* ---------------------------------------------- Opcode Table Descriptions */ +/* the table type describes how to handle byte/size increments before + * and after lookup. Some tables re-use the current byte, others + * consume a byte only if the ModR/M encodes no operands, etc */ +enum ia32_tbl_type_id { + tbl_opcode = 0, /* standard opcode table: no surprises */ + tbl_prefix, /* Prefix Override, e.g. 66/F2/F3 */ + tbl_suffix, /* 3D Now style */ + tbl_extension, /* ModR/M extension: 00-FF -> 00-07 */ + tbl_ext_ext, /* extension of modr/m using R/M field */ + tbl_fpu, /* fpu table: 00-BF -> 00-0F */ + tbl_fpu_ext /* fpu extension : C0-FF -> 00-1F */ + }; + +/* How it works: + * Bytes are 'consumed' if the next table lookup requires that the byte + * pointer be advanced in the instruction stream. 'Does not consume' means + * that, when the lookup function recurses, the same byte it re-used in the + * new table. It also means that size is not decremented, for example when + * a ModR/M byte is used. Note that tbl_extension (ModR/M) instructions that + * do not increase the size of an insn with their operands have a forced + 3 size increase in the lookup algo. Weird, yes, confusing, yes, welcome + * to the Intel ISA. Another note: tbl_prefix is used as an override, so an + * empty insn in a prefix table causes the instruction in the original table + * to be used, rather than an invalid insn being generated. + * tbl_opcode uses current byte and consumes it + * tbl_prefix uses current byte but does not consume it + * tbl_suffix uses and consumes last byte in insn + * tbl_extension uses current byte but does not consume it + * tbl_ext_ext uses current byte but does not consume it + * tbl_fpu uses current byte and consumes it + * tbl_fpu_ext uses current byte but does not consume it + */ + +/* Convenience struct for opcode tables : these will be stored in a + * 'table of tables' so we can use a table index instead of a pointer */ +typedef struct { /* Assembly instruction tables */ + ia32_insn_t *table; /* Pointer to table of instruction encodings */ + enum ia32_tbl_type_id type; + unsigned char shift; /* amount to shift modrm byte */ + unsigned char mask; /* bit mask for look up */ + unsigned char minlim,maxlim; /* limits on min/max entries. */ +} ia32_table_desc_t; + + +/* ---------------------------------------------- 'Cooked' Operand Type Info */ +/* Permissions: */ +#define OP_R 0x001 /* operand is READ */ +#define OP_W 0x002 /* operand is WRITTEN */ +#define OP_RW 0x003 /* (OP_R|OP_W): convenience macro */ +#define OP_X 0x004 /* operand is EXECUTED */ + +#define OP_PERM_MASK 0x0000007 /* perms are NOT mutually exclusive */ +#define OP_PERM( type ) (type & OP_PERM_MASK) + +/* Flags */ +#define OP_SIGNED 0x010 /* operand is signed */ + +#define OP_FLAG_MASK 0x0F0 /* mods are NOT mutually exclusive */ +#define OP_FLAGS( type ) (type & OP_FLAG_MASK) + +#define OP_REG_MASK 0x0000FFFF /* lower WORD is register ID */ +#define OP_REGTBL_MASK 0xFFFF0000 /* higher word is register type [gen/dbg] */ +#define OP_REGID( type ) (type & OP_REG_MASK) +#define OP_REGTYPE( type ) (type & OP_REGTBL_MASK) + +/* ------------------------------------------'Cooked' Instruction Type Info */ +/* high-bit opcode types/insn meta-types */ +#define INS_FLAG_PREFIX 0x10000000 /* insn is a prefix */ +#define INS_FLAG_SUFFIX 0x20000000 /* followed by a suffix byte */ +#define INS_FLAG_MASK 0xFF000000 + +/* insn notes */ +#define INS_NOTE_RING0 0x00000001 /* insn is privileged */ +#define INS_NOTE_SMM 0x00000002 /* Sys Mgt Mode only */ +#define INS_NOTE_SERIAL 0x00000004 /* serializes */ +#define INS_NOTE_NONSWAP 0x00000008 /* insn is not swapped in att format */ // could be separate field? +#define INS_NOTE_NOSUFFIX 0x00000010 /* insn has no size suffix in att format */ // could be separate field? +//#define INS_NOTE_NMI + +#define INS_INVALID 0 + +/* instruction groups */ +#define INS_EXEC 0x1000 +#define INS_ARITH 0x2000 +#define INS_LOGIC 0x3000 +#define INS_STACK 0x4000 +#define INS_COND 0x5000 +#define INS_LOAD 0x6000 +#define INS_ARRAY 0x7000 +#define INS_BIT 0x8000 +#define INS_FLAG 0x9000 +#define INS_FPU 0xA000 +#define INS_TRAPS 0xD000 +#define INS_SYSTEM 0xE000 +#define INS_OTHER 0xF000 + +#define INS_GROUP_MASK 0xF000 +#define INS_GROUP( type ) ( type & INS_GROUP_MASK ) + +/* INS_EXEC group */ +#define INS_BRANCH (INS_EXEC | 0x01) /* Unconditional branch */ +#define INS_BRANCHCC (INS_EXEC | 0x02) /* Conditional branch */ +#define INS_CALL (INS_EXEC | 0x03) /* Jump to subroutine */ +#define INS_CALLCC (INS_EXEC | 0x04) /* Jump to subroutine */ +#define INS_RET (INS_EXEC | 0x05) /* Return from subroutine */ + +/* INS_ARITH group */ +#define INS_ADD (INS_ARITH | 0x01) +#define INS_SUB (INS_ARITH | 0x02) +#define INS_MUL (INS_ARITH | 0x03) +#define INS_DIV (INS_ARITH | 0x04) +#define INS_INC (INS_ARITH | 0x05) /* increment */ +#define INS_DEC (INS_ARITH | 0x06) /* decrement */ +#define INS_SHL (INS_ARITH | 0x07) /* shift right */ +#define INS_SHR (INS_ARITH | 0x08) /* shift left */ +#define INS_ROL (INS_ARITH | 0x09) /* rotate left */ +#define INS_ROR (INS_ARITH | 0x0A) /* rotate right */ +#define INS_MIN (INS_ARITH | 0x0B) /* min func */ +#define INS_MAX (INS_ARITH | 0x0C) /* max func */ +#define INS_AVG (INS_ARITH | 0x0D) /* avg func */ +#define INS_FLR (INS_ARITH | 0x0E) /* floor func */ +#define INS_CEIL (INS_ARITH | 0x0F) /* ceiling func */ + +/* INS_LOGIC group */ +#define INS_AND (INS_LOGIC | 0x01) +#define INS_OR (INS_LOGIC | 0x02) +#define INS_XOR (INS_LOGIC | 0x03) +#define INS_NOT (INS_LOGIC | 0x04) +#define INS_NEG (INS_LOGIC | 0x05) +#define INS_NAND (INS_LOGIC | 0x06) + +/* INS_STACK group */ +#define INS_PUSH (INS_STACK | 0x01) +#define INS_POP (INS_STACK | 0x02) +#define INS_PUSHREGS (INS_STACK | 0x03) /* push register context */ +#define INS_POPREGS (INS_STACK | 0x04) /* pop register context */ +#define INS_PUSHFLAGS (INS_STACK | 0x05) /* push all flags */ +#define INS_POPFLAGS (INS_STACK | 0x06) /* pop all flags */ +#define INS_ENTER (INS_STACK | 0x07) /* enter stack frame */ +#define INS_LEAVE (INS_STACK | 0x08) /* leave stack frame */ + +/* INS_COND group */ +#define INS_TEST (INS_COND | 0x01) +#define INS_CMP (INS_COND | 0x02) + +/* INS_LOAD group */ +#define INS_MOV (INS_LOAD | 0x01) +#define INS_MOVCC (INS_LOAD | 0x02) +#define INS_XCHG (INS_LOAD | 0x03) +#define INS_XCHGCC (INS_LOAD | 0x04) +#define INS_CONV (INS_LOAD | 0x05) /* move and convert type */ + +/* INS_ARRAY group */ +#define INS_STRCMP (INS_ARRAY | 0x01) +#define INS_STRLOAD (INS_ARRAY | 0x02) +#define INS_STRMOV (INS_ARRAY | 0x03) +#define INS_STRSTOR (INS_ARRAY | 0x04) +#define INS_XLAT (INS_ARRAY | 0x05) + +/* INS_BIT group */ +#define INS_BITTEST (INS_BIT | 0x01) +#define INS_BITSET (INS_BIT | 0x02) +#define INS_BITCLR (INS_BIT | 0x03) + +/* INS_FLAG group */ +#define INS_CLEARCF (INS_FLAG | 0x01) /* clear Carry flag */ +#define INS_CLEARZF (INS_FLAG | 0x02) /* clear Zero flag */ +#define INS_CLEAROF (INS_FLAG | 0x03) /* clear Overflow flag */ +#define INS_CLEARDF (INS_FLAG | 0x04) /* clear Direction flag */ +#define INS_CLEARSF (INS_FLAG | 0x05) /* clear Sign flag */ +#define INS_CLEARPF (INS_FLAG | 0x06) /* clear Parity flag */ +#define INS_SETCF (INS_FLAG | 0x07) +#define INS_SETZF (INS_FLAG | 0x08) +#define INS_SETOF (INS_FLAG | 0x09) +#define INS_SETDF (INS_FLAG | 0x0A) +#define INS_SETSF (INS_FLAG | 0x0B) +#define INS_SETPF (INS_FLAG | 0x0C) +#define INS_TOGCF (INS_FLAG | 0x10) /* toggle */ +#define INS_TOGZF (INS_FLAG | 0x20) +#define INS_TOGOF (INS_FLAG | 0x30) +#define INS_TOGDF (INS_FLAG | 0x40) +#define INS_TOGSF (INS_FLAG | 0x50) +#define INS_TOGPF (INS_FLAG | 0x60) + +/* INS_FPU */ +#define INS_FMOV (INS_FPU | 0x1) +#define INS_FMOVCC (INS_FPU | 0x2) +#define INS_FNEG (INS_FPU | 0x3) +#define INS_FABS (INS_FPU | 0x4) +#define INS_FADD (INS_FPU | 0x5) +#define INS_FSUB (INS_FPU | 0x6) +#define INS_FMUL (INS_FPU | 0x7) +#define INS_FDIV (INS_FPU | 0x8) +#define INS_FSQRT (INS_FPU | 0x9) +#define INS_FCMP (INS_FPU | 0xA) +#define INS_FCOS (INS_FPU | 0xC) /* cosine */ +#define INS_FLDPI (INS_FPU | 0xD) /* load pi */ +#define INS_FLDZ (INS_FPU | 0xE) /* load 0 */ +#define INS_FTAN (INS_FPU | 0xF) /* tanget */ +#define INS_FSINE (INS_FPU | 0x10) /* sine */ +#define INS_FSYS (INS_FPU | 0x20) /* misc */ + +/* INS_TRAP */ +#define INS_TRAP (INS_TRAPS | 0x01) /* generate trap */ +#define INS_TRAPCC (INS_TRAPS | 0x02) /* conditional trap gen */ +#define INS_TRET (INS_TRAPS | 0x03) /* return from trap */ +#define INS_BOUNDS (INS_TRAPS | 0x04) /* gen bounds trap */ +#define INS_DEBUG (INS_TRAPS | 0x05) /* gen breakpoint trap */ +#define INS_TRACE (INS_TRAPS | 0x06) /* gen single step trap */ +#define INS_INVALIDOP (INS_TRAPS | 0x07) /* gen invalid insn */ +#define INS_OFLOW (INS_TRAPS | 0x08) /* gen overflow trap */ +#define INS_ICEBP (INS_TRAPS | 0x09) /* ICE breakpoint */ + +/* INS_SYSTEM */ +#define INS_HALT (INS_SYSTEM | 0x01) /* halt machine */ +#define INS_IN (INS_SYSTEM | 0x02) /* input form port */ +#define INS_OUT (INS_SYSTEM | 0x03) /* output to port */ +#define INS_CPUID (INS_SYSTEM | 0x04) /* identify cpu */ + +/* INS_OTHER */ +#define INS_NOP (INS_OTHER | 0x01) +#define INS_BCDCONV (INS_OTHER | 0x02) /* convert to/from BCD */ +#define INS_SZCONV (INS_OTHER | 0x03) /* convert size of operand */ +#define INS_SALC (INS_OTHER | 0x04) /* set %al on carry */ +#define INS_UNKNOWN (INS_OTHER | 0x05) + + +#define INS_TYPE_MASK 0xFFFF +#define INS_TYPE( type ) ( type & INS_TYPE_MASK ) + + /* flags effected by instruction */ +#define INS_TEST_CARRY 0x01 /* carry */ +#define INS_TEST_ZERO 0x02 /* zero/equal */ +#define INS_TEST_OFLOW 0x04 /* overflow */ +#define INS_TEST_DIR 0x08 /* direction */ +#define INS_TEST_SIGN 0x10 /* negative */ +#define INS_TEST_PARITY 0x20 /* parity */ +#define INS_TEST_OR 0x40 /* used in jle */ +#define INS_TEST_NCARRY 0x100 /* ! carry */ +#define INS_TEST_NZERO 0x200 /* ! zero */ +#define INS_TEST_NOFLOW 0x400 /* ! oflow */ +#define INS_TEST_NDIR 0x800 /* ! dir */ +#define INS_TEST_NSIGN 0x100 /* ! sign */ +#define INS_TEST_NPARITY 0x2000 /* ! parity */ +/* SF == OF */ +#define INS_TEST_SFEQOF 0x4000 +/* SF != OF */ +#define INS_TEST_SFNEOF 0x8000 + +#define INS_TEST_ALL INS_TEST_CARRY | INS_TEST_ZERO | \ + INS_TEST_OFLOW | INS_TEST_SIGN | \ + INS_TEST_PARITY + +#define INS_SET_CARRY 0x010000 /* carry */ +#define INS_SET_ZERO 0x020000 /* zero/equal */ +#define INS_SET_OFLOW 0x040000 /* overflow */ +#define INS_SET_DIR 0x080000 /* direction */ +#define INS_SET_SIGN 0x100000 /* negative */ +#define INS_SET_PARITY 0x200000 /* parity */ +#define INS_SET_NCARRY 0x1000000 +#define INS_SET_NZERO 0x2000000 +#define INS_SET_NOFLOW 0x4000000 +#define INS_SET_NDIR 0x8000000 +#define INS_SET_NSIGN 0x10000000 +#define INS_SET_NPARITY 0x20000000 +#define INS_SET_SFEQOF 0x40000000 +#define INS_SET_SFNEOF 0x80000000 + +#define INS_SET_ALL INS_SET_CARRY | INS_SET_ZERO | \ + INS_SET_OFLOW | INS_SET_SIGN | \ + INS_SET_PARITY + +#define INS_TEST_MASK 0x0000FFFF +#define INS_FLAGS_TEST(x) (x & INS_TEST_MASK) +#define INS_SET_MASK 0xFFFF0000 +#define INS_FLAGS_SET(x) (x & INS_SET_MASK) + +#if 0 +/* TODO: actually start using these */ +#define X86_PAIR_NP 1 /* not pairable; execs in U */ +#define X86_PAIR_PU 2 /* pairable in U pipe */ +#define X86_PAIR_PV 3 /* pairable in V pipe */ +#define X86_PAIR_UV 4 /* pairable in UV pipe */ +#define X86_PAIR_FX 5 /* pairable with FXCH */ + +#define X86_EXEC_PORT_0 1 +#define X86_EXEC_PORT_1 2 +#define X86_EXEC_PORT_2 4 +#define X86_EXEC_PORT_3 8 +#define X86_EXEC_PORT_4 16 + +#define X86_EXEC_UNITS + +typedef struct { /* representation of an insn during decoding */ + uint32_t flags; /* runtime settings */ + /* instruction prefixes and other foolishness */ + uint32_t prefix; /* encoding of prefix */ + char prefix_str[16]; /* mnemonics for prefix */ + uint32_t branch_hint; /* gah! */ + unsigned int cpu_ver; /* TODO: cpu version */ + unsigned int clocks; /* TODO: clock cycles: min/max */ + unsigned char last_prefix; + /* runtime intruction decoding helpers */ + unsigned char mode; /* 16, 32, 64 */ + unsigned char gen_regs; /* offset of default general reg set */ + unsigned char sz_operand; /* operand size for insn */ + unsigned char sz_address; /* address size for insn */ + unsigned char uops; /* uops per insn */ + unsigned char pairing; /* np,pu,pv.lv */ + unsigned char exec_unit; + unsigned char exec_port; + unsigned char latency; +} ia32_info_t; +#define MODE_32 0 /* default */ +#define MODE_16 1 +#define MODE_64 2 +#endif + +#endif diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_invariant.c b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_invariant.c new file mode 100644 index 000000000..68ec153d2 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_invariant.c @@ -0,0 +1,313 @@ +#include +#include + +#include "ia32_invariant.h" +#include "ia32_insn.h" +#include "ia32_settings.h" + +extern ia32_table_desc_t *ia32_tables; +extern ia32_settings_t ia32_settings; + +extern size_t ia32_table_lookup( unsigned char *buf, size_t buf_len, + unsigned int table, ia32_insn_t **raw_insn, + unsigned int *prefixes ); + + +/* -------------------------------- ModR/M, SIB */ +/* Convenience flags */ +#define MODRM_EA 1 /* ModR/M is an effective addr */ +#define MODRM_reg 2 /* ModR/M is a register */ + +/* ModR/M flags */ +#define MODRM_RM_SIB 0x04 /* R/M == 100 */ +#define MODRM_RM_NOREG 0x05 /* R/B == 101 */ +/* if (MODRM.MOD_NODISP && MODRM.RM_NOREG) then just disp32 */ +#define MODRM_MOD_NODISP 0x00 /* mod == 00 */ +#define MODRM_MOD_DISP8 0x01 /* mod == 01 */ +#define MODRM_MOD_DISP32 0x02 /* mod == 10 */ +#define MODRM_MOD_NOEA 0x03 /* mod == 11 */ +/* 16-bit modrm flags */ +#define MOD16_MOD_NODISP 0 +#define MOD16_MOD_DISP8 1 +#define MOD16_MOD_DISP16 2 +#define MOD16_MOD_REG 3 + +#define MOD16_RM_BXSI 0 +#define MOD16_RM_BXDI 1 +#define MOD16_RM_BPSI 2 +#define MOD16_RM_BPDI 3 +#define MOD16_RM_SI 4 +#define MOD16_RM_DI 5 +#define MOD16_RM_BP 6 +#define MOD16_RM_BX 7 + +/* SIB flags */ +#define SIB_INDEX_NONE 0x04 +#define SIB_BASE_EBP 0x05 +#define SIB_SCALE_NOBASE 0x00 + +/* Convenience struct for modR/M bitfield */ +struct modRM_byte { + unsigned int mod : 2; + unsigned int reg : 3; + unsigned int rm : 3; +}; + +/* Convenience struct for SIB bitfield */ +struct SIB_byte { + unsigned int scale : 2; + unsigned int index : 3; + unsigned int base : 3; +}; + +#ifdef WIN32 +static void byte_decode(unsigned char b, struct modRM_byte *modrm) { +#else +static inline void byte_decode(unsigned char b, struct modRM_byte *modrm) { +#endif + /* generic bitfield-packing routine */ + + modrm->mod = b >> 6; /* top 2 bits */ + modrm->reg = (b & 56) >> 3; /* middle 3 bits */ + modrm->rm = b & 7; /* bottom 3 bits */ +} +static int ia32_invariant_modrm( unsigned char *in, unsigned char *out, + unsigned int mode_16, x86_invariant_op_t *op) { + struct modRM_byte modrm; + struct SIB_byte sib; + unsigned char *c, *cin; + unsigned short *s; + unsigned int *i; + int size = 0; /* modrm byte is already counted */ + + + byte_decode(*in, &modrm); /* get bitfields */ + + out[0] = in[0]; /* save modrm byte */ + cin = &in[1]; + c = &out[1]; + s = (unsigned short *)&out[1]; + i = (unsigned int *)&out[1]; + + op->type = op_expression; + op->flags |= op_pointer; + if ( ! mode_16 && modrm.rm == MODRM_RM_SIB && + modrm.mod != MODRM_MOD_NOEA ) { + size ++; + byte_decode(*cin, (struct modRM_byte *)(void*)&sib); + + out[1] = in[1]; /* save sib byte */ + cin = &in[2]; + c = &out[2]; + s = (unsigned short *)&out[2]; + i = (unsigned int *)&out[2]; + + if ( sib.base == SIB_BASE_EBP && ! modrm.mod ) { + /* disp 32 is variant! */ + memset( i, X86_WILDCARD_BYTE, 4 ); + size += 4; + } + } + + if (! modrm.mod && modrm.rm == 101) { + if ( mode_16 ) { /* straight RVA in disp */ + memset( s, X86_WILDCARD_BYTE, 2 ); + size += 2; + } else { + memset( i, X86_WILDCARD_BYTE, 2 ); + size += 4; + } + } else if (modrm.mod && modrm.mod < 3) { + if (modrm.mod == MODRM_MOD_DISP8) { /* offset in disp */ + *c = *cin; + size += 1; + } else if ( mode_16 ) { + *s = (* ((unsigned short *) cin)); + size += 2; + } else { + *i = (*((unsigned int *) cin)); + size += 4; + } + } else if ( modrm.mod == 3 ) { + op->type = op_register; + op->flags &= ~op_pointer; + } + + return (size); +} + + +static int ia32_decode_invariant( unsigned char *buf, size_t buf_len, + ia32_insn_t *t, unsigned char *out, + unsigned int prefixes, x86_invariant_t *inv) { + + unsigned int addr_size, op_size, mode_16; + unsigned int op_flags[3] = { t->dest_flag, t->src_flag, t->aux_flag }; + int x, type, bytes = 0, size = 0, modrm = 0; + + /* set addressing mode */ + if (ia32_settings.options & opt_16_bit) { + op_size = ( prefixes & PREFIX_OP_SIZE ) ? 4 : 2; + addr_size = ( prefixes & PREFIX_ADDR_SIZE ) ? 4 : 2; + mode_16 = ( prefixes & PREFIX_ADDR_SIZE ) ? 0 : 1; + } else { + op_size = ( prefixes & PREFIX_OP_SIZE ) ? 2 : 4; + addr_size = ( prefixes & PREFIX_ADDR_SIZE ) ? 2 : 4; + mode_16 = ( prefixes & PREFIX_ADDR_SIZE ) ? 1 : 0; + } + + for (x = 0; x < 3; x++) { + inv->operands[x].access = (enum x86_op_access) + OP_PERM(op_flags[x]); + inv->operands[x].flags = (enum x86_op_flags) + (OP_FLAGS(op_flags[x]) >> 12); + + switch (op_flags[x] & OPTYPE_MASK) { + case OPTYPE_c: + size = (op_size == 4) ? 2 : 1; + break; + case OPTYPE_a: case OPTYPE_v: + size = (op_size == 4) ? 4 : 2; + break; + case OPTYPE_p: + size = (op_size == 4) ? 6 : 4; + break; + case OPTYPE_b: + size = 1; + break; + case OPTYPE_w: + size = 2; + break; + case OPTYPE_d: case OPTYPE_fs: case OPTYPE_fd: + case OPTYPE_fe: case OPTYPE_fb: case OPTYPE_fv: + case OPTYPE_si: case OPTYPE_fx: + size = 4; + break; + case OPTYPE_s: + size = 6; + break; + case OPTYPE_q: case OPTYPE_pi: + size = 8; + break; + case OPTYPE_dq: case OPTYPE_ps: case OPTYPE_ss: + case OPTYPE_pd: case OPTYPE_sd: + size = 16; + break; + case OPTYPE_m: + size = (addr_size == 4) ? 4 : 2; + break; + default: + break; + } + + type = op_flags[x] & ADDRMETH_MASK; + switch (type) { + case ADDRMETH_E: case ADDRMETH_M: case ADDRMETH_Q: + case ADDRMETH_R: case ADDRMETH_W: + modrm = 1; + bytes += ia32_invariant_modrm( buf, out, + mode_16, &inv->operands[x]); + break; + case ADDRMETH_C: case ADDRMETH_D: case ADDRMETH_G: + case ADDRMETH_P: case ADDRMETH_S: case ADDRMETH_T: + case ADDRMETH_V: + inv->operands[x].type = op_register; + modrm = 1; + break; + case ADDRMETH_A: case ADDRMETH_O: + /* pad with xF4's */ + memset( &out[bytes + modrm], X86_WILDCARD_BYTE, + size ); + bytes += size; + inv->operands[x].type = op_offset; + if ( type == ADDRMETH_O ) { + inv->operands[x].flags |= op_signed | + op_pointer; + } + break; + case ADDRMETH_I: case ADDRMETH_J: + /* grab imm value */ + if ((op_flags[x] & OPTYPE_MASK) == OPTYPE_v) { + /* assume this is an address */ + memset( &out[bytes + modrm], + X86_WILDCARD_BYTE, size ); + } else { + memcpy( &out[bytes + modrm], + &buf[bytes + modrm], size ); + } + + bytes += size; + if ( type == ADDRMETH_J ) { + if ( size == 1 ) { + inv->operands[x].type = + op_relative_near; + } else { + inv->operands[x].type = + op_relative_far; + } + inv->operands[x].flags |= op_signed; + } else { + inv->operands[x].type = op_immediate; + } + break; + case ADDRMETH_F: + inv->operands[x].type = op_register; + break; + case ADDRMETH_X: + inv->operands[x].flags |= op_signed | + op_pointer | op_ds_seg | op_string; + break; + case ADDRMETH_Y: + inv->operands[x].flags |= op_signed | + op_pointer | op_es_seg | op_string; + break; + case ADDRMETH_RR: + inv->operands[x].type = op_register; + break; + case ADDRMETH_II: + inv->operands[x].type = op_immediate; + break; + default: + inv->operands[x].type = op_unused; + break; + } + } + + return (bytes + modrm); +} + +size_t ia32_disasm_invariant( unsigned char * buf, size_t buf_len, + x86_invariant_t *inv ) { + ia32_insn_t *raw_insn = NULL; + unsigned int prefixes; + unsigned int type; + size_t size; + + /* Perform recursive table lookup starting with main table (0) */ + size = ia32_table_lookup( buf, buf_len, 0, &raw_insn, &prefixes ); + if ( size == INVALID_INSN || size > buf_len ) { + /* TODO: set errno */ + return 0; + } + + /* copy opcode bytes to buffer */ + memcpy( inv->bytes, buf, size ); + + /* set mnemonic type and group */ + type = raw_insn->mnem_flag & ~INS_FLAG_MASK; + inv->group = (enum x86_insn_group) (INS_GROUP(type)) >> 12; + inv->type = (enum x86_insn_type) INS_TYPE(type); + + /* handle operands */ + size += ia32_decode_invariant( buf + size, buf_len - size, raw_insn, + &buf[size - 1], prefixes, inv ); + + inv->size = size; + + return size; /* return size of instruction in bytes */ +} + +size_t ia32_disasm_size( unsigned char *buf, size_t buf_len ) { + x86_invariant_t inv = { {0} }; + return( ia32_disasm_invariant( buf, buf_len, &inv ) ); +} diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_invariant.h b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_invariant.h new file mode 100644 index 000000000..e1cea60e9 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_invariant.h @@ -0,0 +1,11 @@ +#ifndef IA32_INVARIANT_H +#define IA32_INVARIANT_H + +#include "libdis.h" + +size_t ia32_disasm_invariant( unsigned char *buf, size_t buf_len, + x86_invariant_t *inv); + +size_t ia32_disasm_size( unsigned char *buf, size_t buf_len ); + +#endif diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_modrm.c b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_modrm.c new file mode 100644 index 000000000..b0fe2ed3d --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_modrm.c @@ -0,0 +1,310 @@ +#include "ia32_modrm.h" +#include "ia32_reg.h" +#include "x86_imm.h" + +/* NOTE: when decoding ModR/M and SIB, we have to add 1 to all register + * values obtained from decoding the ModR/M or SIB byte, since they + * are encoded with eAX = 0 and the tables in ia32_reg.c use eAX = 1. + * ADDENDUM: this is only the case when the register value is used + * directly as an index into the register table, not when it is added to + * a genregs offset. */ + +/* -------------------------------- ModR/M, SIB */ +/* ModR/M flags */ +#define MODRM_RM_SIB 0x04 /* R/M == 100 */ +#define MODRM_RM_NOREG 0x05 /* R/B == 101 */ + +/* if (MODRM.MOD_NODISP && MODRM.RM_NOREG) then just disp32 */ +#define MODRM_MOD_NODISP 0x00 /* mod == 00 */ +#define MODRM_MOD_DISP8 0x01 /* mod == 01 */ +#define MODRM_MOD_DISP32 0x02 /* mod == 10 */ +#define MODRM_MOD_NOEA 0x03 /* mod == 11 */ + +/* 16-bit modrm flags */ +#define MOD16_MOD_NODISP 0 +#define MOD16_MOD_DISP8 1 +#define MOD16_MOD_DISP16 2 +#define MOD16_MOD_REG 3 + +#define MOD16_RM_BXSI 0 +#define MOD16_RM_BXDI 1 +#define MOD16_RM_BPSI 2 +#define MOD16_RM_BPDI 3 +#define MOD16_RM_SI 4 +#define MOD16_RM_DI 5 +#define MOD16_RM_BP 6 +#define MOD16_RM_BX 7 + +/* SIB flags */ +#define SIB_INDEX_NONE 0x04 +#define SIB_BASE_EBP 0x05 +#define SIB_SCALE_NOBASE 0x00 + +/* Convenience struct for modR/M bitfield */ +struct modRM_byte { + unsigned int mod : 2; + unsigned int reg : 3; + unsigned int rm : 3; +}; + +/* Convenience struct for SIB bitfield */ +struct SIB_byte { + unsigned int scale : 2; + unsigned int index : 3; + unsigned int base : 3; +}; + + +#if 0 +int modrm_rm[] = {0,1,2,3,MODRM_RM_SIB,MODRM_MOD_DISP32,6,7}; +int modrm_reg[] = {0, 1, 2, 3, 4, 5, 6, 7}; +int modrm_mod[] = {0, MODRM_MOD_DISP8, MODRM_MOD_DISP32, MODRM_MOD_NOEA}; +int sib_scl[] = {0, 2, 4, 8}; +int sib_idx[] = {0, 1, 2, 3, SIB_INDEX_NONE, 5, 6, 7 }; +int sib_bas[] = {0, 1, 2, 3, 4, SIB_SCALE_NOBASE, 6, 7 }; +#endif + +/* this is needed to replace x86_imm_signsized() which does not sign-extend + * to dest */ +static unsigned int imm32_signsized( unsigned char *buf, size_t buf_len, + int32_t *dest, unsigned int size ) { + if ( size > buf_len ) { + return 0; + } + + switch (size) { + case 1: + *dest = *((signed char *) buf); + break; + case 2: + *dest = *((signed short *) buf); + break; + case 4: + default: + *dest = *((signed int *) buf); + break; + } + + return size; +} + + + +static void byte_decode(unsigned char b, struct modRM_byte *modrm) { + /* generic bitfield-packing routine */ + + modrm->mod = b >> 6; /* top 2 bits */ + modrm->reg = (b & 56) >> 3; /* middle 3 bits */ + modrm->rm = b & 7; /* bottom 3 bits */ +} + + +static size_t sib_decode( unsigned char *buf, size_t buf_len, x86_ea_t *ea, + unsigned int mod ) { + /* set Address Expression fields (scale, index, base, disp) + * according to the contents of the SIB byte. + * b points to the SIB byte in the instruction-stream buffer; the + * byte after b[0] is therefore the byte after the SIB + * returns number of bytes 'used', including the SIB byte */ + size_t size = 1; /* start at 1 for SIB byte */ + struct SIB_byte sib; + + if ( buf_len < 1 ) { + return 0; + } + + byte_decode( *buf, (struct modRM_byte *)(void*)&sib ); /* get bit-fields */ + + if ( sib.base == SIB_BASE_EBP && ! mod ) { /* if base == 101 (ebp) */ + /* IF BASE == EBP, deal with exception */ + /* IF (ModR/M did not create a Disp */ + /* ... create a 32-bit Displacement */ + imm32_signsized( &buf[1], buf_len, &ea->disp, sizeof(int32_t)); + ea->disp_size = sizeof(int32_t); + ea->disp_sign = (ea->disp < 0) ? 1 : 0; + size += 4; /* add sizeof disp to count */ + + } else { + /* ELSE BASE refers to a General Register */ + ia32_handle_register( &ea->base, sib.base + 1 ); + } + + /* set scale to 1, 2, 4, 8 */ + ea->scale = 1 << sib.scale; + + if (sib.index != SIB_INDEX_NONE) { + /* IF INDEX is not 'ESP' (100) */ + ia32_handle_register( &ea->index, sib.index + 1 ); + } + + return (size); /* return number of bytes processed */ +} + +static size_t modrm_decode16( unsigned char *buf, unsigned int buf_len, + x86_op_t *op, struct modRM_byte *modrm ) { + /* 16-bit mode: hackish, but not as hackish as 32-bit mode ;) */ + size_t size = 1; /* # of bytes decoded [1 for modR/M byte] */ + x86_ea_t * ea = &op->data.expression; + + switch( modrm->rm ) { + case MOD16_RM_BXSI: + ia32_handle_register(&ea->base, REG_WORD_OFFSET + 3); + ia32_handle_register(&ea->index, REG_WORD_OFFSET + 6); + break; + case MOD16_RM_BXDI: + ia32_handle_register(&ea->base, REG_WORD_OFFSET + 3); + ia32_handle_register(&ea->index, REG_WORD_OFFSET + 7); + case MOD16_RM_BPSI: + op->flags |= op_ss_seg; + ia32_handle_register(&ea->base, REG_WORD_OFFSET + 5); + ia32_handle_register(&ea->index, REG_WORD_OFFSET + 6); + break; + case MOD16_RM_BPDI: + op->flags |= op_ss_seg; + ia32_handle_register(&ea->base, REG_WORD_OFFSET + 5); + ia32_handle_register(&ea->index, REG_WORD_OFFSET + 7); + break; + case MOD16_RM_SI: + ia32_handle_register(&ea->base, REG_WORD_OFFSET + 6); + break; + case MOD16_RM_DI: + ia32_handle_register(&ea->base, REG_WORD_OFFSET + 7); + break; + case MOD16_RM_BP: + if ( modrm->mod != MOD16_MOD_NODISP ) { + op->flags |= op_ss_seg; + ia32_handle_register(&ea->base, + REG_WORD_OFFSET + 5); + } + break; + case MOD16_RM_BX: + ia32_handle_register(&ea->base, REG_WORD_OFFSET + 3); + break; + } + + /* move to byte after ModR/M */ + ++buf; + --buf_len; + + if ( modrm->mod == MOD16_MOD_DISP8 ) { + imm32_signsized( buf, buf_len, &ea->disp, sizeof(char) ); + ea->disp_sign = (ea->disp < 0) ? 1 : 0; + ea->disp_size = sizeof(char); + size += sizeof(char); + } else if ( modrm->mod == MOD16_MOD_DISP16 ) { + imm32_signsized( buf, buf_len, &ea->disp, sizeof(short) ); + ea->disp_sign = (ea->disp < 0) ? 1 : 0; + ea->disp_size = sizeof(short); + size += sizeof(short); + } + + return size; +} + +/* TODO : Mark index modes + Use addressing mode flags to imply arrays (index), structure (disp), + two-dimensional arrays [disp + index], classes [ea reg], and so on. +*/ +size_t ia32_modrm_decode( unsigned char *buf, unsigned int buf_len, + x86_op_t *op, x86_insn_t *insn, size_t gen_regs ) { + /* create address expression and/or fill operand based on value of + * ModR/M byte. Calls sib_decode as appropriate. + * flags specifies whether Reg or mod+R/M fields are being decoded + * returns the number of bytes in the instruction, including modR/M */ + struct modRM_byte modrm; + size_t size = 1; /* # of bytes decoded [1 for modR/M byte] */ + x86_ea_t * ea; + + + byte_decode(*buf, &modrm); /* get bitfields */ + + /* first, handle the case where the mod field is a register only */ + if ( modrm.mod == MODRM_MOD_NOEA ) { + op->type = op_register; + ia32_handle_register(&op->data.reg, modrm.rm + gen_regs); + /* increase insn size by 1 for modrm byte */ + return 1; + } + + /* then deal with cases where there is an effective address */ + ea = &op->data.expression; + op->type = op_expression; + op->flags |= op_pointer; + + if ( insn->addr_size == 2 ) { + /* gah! 16 bit mode! */ + return modrm_decode16( buf, buf_len, op, &modrm); + } + + /* move to byte after ModR/M */ + ++buf; + --buf_len; + + if (modrm.mod == MODRM_MOD_NODISP) { /* if mod == 00 */ + + /* IF MOD == No displacement, just Indirect Register */ + if (modrm.rm == MODRM_RM_NOREG) { /* if r/m == 101 */ + /* IF RM == No Register, just Displacement */ + /* This is an Intel Moronic Exception TM */ + imm32_signsized( buf, buf_len, &ea->disp, + sizeof(int32_t) ); + ea->disp_size = sizeof(int32_t); + ea->disp_sign = (ea->disp < 0) ? 1 : 0; + size += 4; /* add sizeof disp to count */ + + } else if (modrm.rm == MODRM_RM_SIB) { /* if r/m == 100 */ + /* ELSE IF an SIB byte is present */ + /* TODO: check for 0 retval */ + size += sib_decode( buf, buf_len, ea, modrm.mod); + /* move to byte after SIB for displacement */ + ++buf; + --buf_len; + } else { /* modR/M specifies base register */ + /* ELSE RM encodes a general register */ + ia32_handle_register( &ea->base, modrm.rm + 1 ); + } + } else { /* mod is 01 or 10 */ + if (modrm.rm == MODRM_RM_SIB) { /* rm == 100 */ + /* IF base is an AddrExpr specified by an SIB byte */ + /* TODO: check for 0 retval */ + size += sib_decode( buf, buf_len, ea, modrm.mod); + /* move to byte after SIB for displacement */ + ++buf; + --buf_len; + } else { + /* ELSE base is a general register */ + ia32_handle_register( &ea->base, modrm.rm + 1 ); + } + + /* ELSE mod + r/m specify a disp##[base] or disp##(SIB) */ + if (modrm.mod == MODRM_MOD_DISP8) { /* mod == 01 */ + /* If this is an 8-bit displacement */ + imm32_signsized( buf, buf_len, &ea->disp, + sizeof(char)); + ea->disp_size = sizeof(char); + ea->disp_sign = (ea->disp < 0) ? 1 : 0; + size += 1; /* add sizeof disp to count */ + + } else { + /* Displacement is dependent on address size */ + imm32_signsized( buf, buf_len, &ea->disp, + insn->addr_size); + ea->disp_size = insn->addr_size; + ea->disp_sign = (ea->disp < 0) ? 1 : 0; + size += 4; + } + } + + return size; /* number of bytes found in instruction */ +} + +void ia32_reg_decode( unsigned char byte, x86_op_t *op, size_t gen_regs ) { + struct modRM_byte modrm; + byte_decode( byte, &modrm ); /* get bitfields */ + + /* set operand to register ID */ + op->type = op_register; + ia32_handle_register(&op->data.reg, modrm.reg + gen_regs); + + return; +} diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_modrm.h b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_modrm.h new file mode 100644 index 000000000..765cb0833 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_modrm.h @@ -0,0 +1,13 @@ +#ifndef IA32_MODRM_H +#define IA32_MODRM_H + +#include "libdis.h" +#include "ia32_insn.h" + +size_t ia32_modrm_decode( unsigned char *buf, unsigned int buf_len, + x86_op_t *op, x86_insn_t *insn, + size_t gen_regs ); + +void ia32_reg_decode( unsigned char byte, x86_op_t *op, size_t gen_regs ); + +#endif diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_opcode_tables.c b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_opcode_tables.c new file mode 100644 index 000000000..ef97c7a35 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_opcode_tables.c @@ -0,0 +1,2939 @@ +#include "ia32_insn.h" + +#include "ia32_reg.h" + +#include "ia32_opcode_tables.h" + +static ia32_insn_t tbl_Main[] = { /* One-byte Opcodes */ + { 0, INS_ADD, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_G | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "add", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_ADD, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_G | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "add", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_ADD, 0, ADDRMETH_G | OPTYPE_b | OP_W | OP_R, ADDRMETH_E | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "add", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_ADD, 0, ADDRMETH_G | OPTYPE_v | OP_W | OP_R, ADDRMETH_E | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "add", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_ADD, 0, ADDRMETH_RR | OPTYPE_b | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "add", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_ADD, 0, ADDRMETH_RR | OPTYPE_v | OP_W | OP_R, ADDRMETH_I | OPTYPE_v | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "add", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_PUSH, 0, ADDRMETH_RS | OPTYPE_w | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "push", "", 0, 0, 0, 0 , 33 }, + { 0, INS_POP, 0, ADDRMETH_RS | OPTYPE_w | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "pop", "", 0, 0, 0, 0 , 33 }, + { 0, INS_OR, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_G | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "or", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_OR, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_G | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "or", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_OR, 0, ADDRMETH_G | OPTYPE_b | OP_W | OP_R, ADDRMETH_E | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "or", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_OR, 0, ADDRMETH_G | OPTYPE_v | OP_W | OP_R, ADDRMETH_E | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "or", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_OR, 0, ADDRMETH_RR | OPTYPE_b | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "or", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_OR, 0, ADDRMETH_RR | OPTYPE_v | OP_W | OP_R, ADDRMETH_I | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "or", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_PUSH, 0, ADDRMETH_RS | OPTYPE_w | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "push", "", 1, 0, 0, 0 , 33 }, + { idx_0F, 0, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, +/* 0x10 */ + { 0, INS_ADD, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_G | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "adc", "", 0, 0, 0, INS_SET_ALL|INS_TEST_CARRY, 0 }, + { 0, INS_ADD, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_G | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "adc", "", 0, 0, 0, INS_SET_ALL|INS_TEST_CARRY, 0 }, + { 0, INS_ADD, 0, ADDRMETH_G | OPTYPE_b | OP_W | OP_R, ADDRMETH_E | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "adc", "", 0, 0, 0, INS_SET_ALL|INS_TEST_CARRY, 0 }, + { 0, INS_ADD, 0, ADDRMETH_G | OPTYPE_v | OP_W | OP_R, ADDRMETH_E | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "adc", "", 0, 0, 0, INS_SET_ALL|INS_TEST_CARRY, 0 }, + { 0, INS_ADD, 0, ADDRMETH_RR | OPTYPE_b | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "adc", "", 0, 0, 0, INS_SET_ALL|INS_TEST_CARRY, 0 }, + { 0, INS_ADD, 0, ADDRMETH_RR | OPTYPE_v | OP_W | OP_R, ADDRMETH_I | OPTYPE_v | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "adc", "", 0, 0, 0, INS_SET_ALL|INS_TEST_CARRY, 0 }, + { 0, INS_PUSH, 0, ADDRMETH_RS | OPTYPE_w | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "push", "", 2, 0, 0, 0 , 33 }, + { 0, INS_POP, 0, ADDRMETH_RS | OPTYPE_w | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "pop", "", 2, 0, 0, 0 , 33 }, + { 0, INS_SUB, 0, ADDRMETH_E | OPTYPE_b | OP_SIGNED | OP_W | OP_R, ADDRMETH_G | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sbb", "", 0, 0, 0, INS_SET_ALL|INS_TEST_CARRY, 0 }, + { 0, INS_SUB, 0, ADDRMETH_E | OPTYPE_v | OP_SIGNED | OP_W | OP_R, ADDRMETH_G | OPTYPE_v | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sbb", "", 0, 0, 0, INS_SET_ALL|INS_TEST_CARRY, 0 }, + { 0, INS_SUB, 0, ADDRMETH_G | OPTYPE_b | OP_W | OP_SIGNED | OP_R, ADDRMETH_E | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sbb", "", 0, 0, 0, INS_SET_ALL|INS_TEST_CARRY, 0 }, + { 0, INS_SUB, 0, ADDRMETH_G | OPTYPE_v | OP_SIGNED | OP_W | OP_R, ADDRMETH_E | OPTYPE_v | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sbb", "", 0, 0, 0, INS_SET_ALL|INS_TEST_CARRY, 0 }, + { 0, INS_SUB, 0, ADDRMETH_RR | OPTYPE_b | OP_SIGNED | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sbb", "", 0, 0, 0, INS_SET_ALL|INS_TEST_CARRY, 0 }, + { 0, INS_SUB, 0, ADDRMETH_RR | OPTYPE_v | OP_SIGNED | OP_W | OP_R, ADDRMETH_I | OPTYPE_v | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sbb", "", 0, 0, 0, INS_SET_ALL|INS_TEST_CARRY, 0 }, + { 0, INS_PUSH, 0, ADDRMETH_RS | OPTYPE_w | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "push", "", 3, 0, 0, 0 , 33 }, + { 0, INS_POP, 0, ADDRMETH_RS | OPTYPE_w | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "pop", "", 3, 0, 0, 0 , 33 }, +/* 0x20 */ + { 0, INS_AND, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_G | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "and", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_AND, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_G | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "and", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_AND, 0, ADDRMETH_G | OPTYPE_b | OP_W | OP_R, ADDRMETH_E | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "and", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_AND, 0, ADDRMETH_G | OPTYPE_v | OP_W | OP_R, ADDRMETH_E | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "and", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_AND, 0, ADDRMETH_RR | OPTYPE_b | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "and", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_AND, 0, ADDRMETH_RR | OPTYPE_v | OP_W | OP_R, ADDRMETH_I | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "and", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_FLAG_PREFIX | PREFIX_ES, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_BCDCONV, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "daa", "", 0, 0, 0, INS_SET_SIGN|INS_SET_ZERO|INS_SET_CARRY|INS_SET_PARITY|INS_TEST_CARRY, 12 }, + { 0, INS_SUB, 0, ADDRMETH_E | OPTYPE_b | OP_SIGNED | OP_W | OP_R, ADDRMETH_G | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sub", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_SUB, 0, ADDRMETH_E | OPTYPE_v | OP_SIGNED | OP_W | OP_R, ADDRMETH_G | OPTYPE_v | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sub", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_SUB, 0, ADDRMETH_G | OPTYPE_b | OP_SIGNED | OP_W | OP_R, ADDRMETH_E | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sub", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_SUB, 0, ADDRMETH_G | OPTYPE_v | OP_SIGNED | OP_W | OP_R, ADDRMETH_E | OPTYPE_v | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sub", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_SUB, 0, ADDRMETH_RR | OPTYPE_b | OP_SIGNED | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sub", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_SUB, 0, ADDRMETH_RR | OPTYPE_v | OP_SIGNED | OP_W | OP_R, ADDRMETH_I | OPTYPE_v | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sub", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_FLAG_PREFIX | PREFIX_CS | PREFIX_NOTTAKEN, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_BCDCONV, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "das", "", 0, 0, 0, INS_SET_SIGN|INS_SET_ZERO|INS_SET_CARRY|INS_SET_PARITY|INS_TEST_CARRY, 0 }, +/* 0x30 */ + { 0, INS_XOR, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_G | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "xor", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_XOR, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_G | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "xor", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_XOR, 0, ADDRMETH_G | OPTYPE_b | OP_W | OP_R, ADDRMETH_E | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "xor", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_XOR, 0, ADDRMETH_G | OPTYPE_v | OP_W | OP_R, ADDRMETH_E | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "xor", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_XOR, 0, ADDRMETH_RR | OPTYPE_b | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "xor", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_XOR, 0, ADDRMETH_RR | OPTYPE_v | OP_W | OP_R, ADDRMETH_I | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "xor", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_FLAG_PREFIX | PREFIX_SS, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_BCDCONV, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "aaa", "", 0, 0, 0, INS_SET_CARRY, 1 }, + { 0, INS_CMP, 0, ADDRMETH_E | OPTYPE_b | OP_R, ADDRMETH_G | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "cmp", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_CMP, 0, ADDRMETH_E | OPTYPE_v | OP_R, ADDRMETH_G | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "cmp", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_CMP, 0, ADDRMETH_G | OPTYPE_b | OP_R, ADDRMETH_E | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "cmp", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_CMP, 0, ADDRMETH_G | OPTYPE_v | OP_R, ADDRMETH_E | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "cmp", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_CMP, 0, ADDRMETH_RR | OPTYPE_b | OP_R, ADDRMETH_I | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "cmp", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_CMP, 0, ADDRMETH_RR | OPTYPE_v | OP_R, ADDRMETH_I | OPTYPE_v | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "cmp", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_FLAG_PREFIX | PREFIX_DS | PREFIX_TAKEN, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_BCDCONV, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "aas", "", 0, 0, 0, INS_SET_CARRY, 0 }, +/* 0x40 */ + { 0, INS_INC, 0, ADDRMETH_RR | OPTYPE_v | OP_R | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "inc", "", 0, 0, 0, INS_SET_OFLOW|INS_SET_SIGN|INS_SET_ZERO|INS_SET_PARITY, 0 }, + { 0, INS_INC, 0, ADDRMETH_RR | OPTYPE_v | OP_R | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "inc", "", 1, 0, 0, INS_SET_OFLOW|INS_SET_SIGN|INS_SET_ZERO|INS_SET_PARITY, 0 }, + { 0, INS_INC, 0, ADDRMETH_RR | OPTYPE_v | OP_R | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "inc", "", 2, 0, 0, INS_SET_OFLOW|INS_SET_SIGN|INS_SET_ZERO|INS_SET_PARITY, 0 }, + { 0, INS_INC, 0, ADDRMETH_RR | OPTYPE_v | OP_R | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "inc", "", 3, 0, 0, INS_SET_OFLOW|INS_SET_SIGN|INS_SET_ZERO|INS_SET_PARITY, 0 }, + { 0, INS_INC, 0, ADDRMETH_RR | OPTYPE_v | OP_R | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "inc", "", 4, 0, 0, INS_SET_OFLOW|INS_SET_SIGN|INS_SET_ZERO|INS_SET_PARITY, 0 }, + { 0, INS_INC, 0, ADDRMETH_RR | OPTYPE_v | OP_R | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "inc", "", 5, 0, 0, INS_SET_OFLOW|INS_SET_SIGN|INS_SET_ZERO|INS_SET_PARITY, 0 }, + { 0, INS_INC, 0, ADDRMETH_RR | OPTYPE_v | OP_R | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "inc", "", 6, 0, 0, INS_SET_OFLOW|INS_SET_SIGN|INS_SET_ZERO|INS_SET_PARITY, 0 }, + { 0, INS_INC, 0, ADDRMETH_RR | OPTYPE_v | OP_R | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "inc", "", 7, 0, 0, INS_SET_OFLOW|INS_SET_SIGN|INS_SET_ZERO|INS_SET_PARITY, 0 }, + { 0, INS_DEC, 0, ADDRMETH_RR | OPTYPE_v | OP_W | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "dec", "", 0, 0, 0, INS_SET_OFLOW|INS_SET_SIGN|INS_SET_ZERO|INS_SET_PARITY, 0 }, + { 0, INS_DEC, 0, ADDRMETH_RR | OPTYPE_v | OP_W | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "dec", "", 1, 0, 0, INS_SET_OFLOW|INS_SET_SIGN|INS_SET_ZERO|INS_SET_PARITY, 0 }, + { 0, INS_DEC, 0, ADDRMETH_RR | OPTYPE_v | OP_W | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "dec", "", 2, 0, 0, INS_SET_OFLOW|INS_SET_SIGN|INS_SET_ZERO|INS_SET_PARITY, 0 }, + { 0, INS_DEC, 0, ADDRMETH_RR | OPTYPE_v | OP_W | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "dec", "", 3, 0, 0, INS_SET_OFLOW|INS_SET_SIGN|INS_SET_ZERO|INS_SET_PARITY, 0 }, + { 0, INS_DEC, 0, ADDRMETH_RR | OPTYPE_v | OP_W | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "dec", "", 4, 0, 0, INS_SET_OFLOW|INS_SET_SIGN|INS_SET_ZERO|INS_SET_PARITY, 0 }, + { 0, INS_DEC, 0, ADDRMETH_RR | OPTYPE_v | OP_W | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "dec", "", 5, 0, 0, INS_SET_OFLOW|INS_SET_SIGN|INS_SET_ZERO|INS_SET_PARITY, 0 }, + { 0, INS_DEC, 0, ADDRMETH_RR | OPTYPE_v | OP_W | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "dec", "", 6, 0, 0, INS_SET_OFLOW|INS_SET_SIGN|INS_SET_ZERO|INS_SET_PARITY, 0 }, + { 0, INS_DEC, 0, ADDRMETH_RR | OPTYPE_v | OP_W | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "dec", "", 7, 0, 0, INS_SET_OFLOW|INS_SET_SIGN|INS_SET_ZERO|INS_SET_PARITY, 0 }, +/* 0x50 */ + { 0, INS_PUSH, 0, ADDRMETH_RR | OPTYPE_v | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "push", "", 0, 0, 0, 0 , 33 }, + { 0, INS_PUSH, 0, ADDRMETH_RR | OPTYPE_v | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "push", "", 1, 0, 0, 0 , 33 }, + { 0, INS_PUSH, 0, ADDRMETH_RR | OPTYPE_v | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "push", "", 2, 0, 0, 0 , 33 }, + { 0, INS_PUSH, 0, ADDRMETH_RR | OPTYPE_v | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "push", "", 3, 0, 0, 0 , 33 }, + { 0, INS_PUSH, 0, ADDRMETH_RR | OPTYPE_v | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "push", "", 4, 0, 0, 0 , 33 }, + { 0, INS_PUSH, 0, ADDRMETH_RR | OPTYPE_v | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "push", "", 5, 0, 0, 0 , 33 }, + { 0, INS_PUSH, 0, ADDRMETH_RR | OPTYPE_v | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "push", "", 6, 0, 0, 0 , 33 }, + { 0, INS_PUSH, 0, ADDRMETH_RR | OPTYPE_v | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "push", "", 7, 0, 0, 0 , 33 }, + { 0, INS_POP, 0, ADDRMETH_RR | OPTYPE_v | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "pop", "", 0, 0, 0, 0 , 33 }, + { 0, INS_POP, 0, ADDRMETH_RR | OPTYPE_v | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "pop", "", 1, 0, 0, 0 , 33 }, + { 0, INS_POP, 0, ADDRMETH_RR | OPTYPE_v | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "pop", "", 2, 0, 0, 0 , 33 }, + { 0, INS_POP, 0, ADDRMETH_RR | OPTYPE_v | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "pop", "", 3, 0, 0, 0 , 33 }, + { 0, INS_POP, 0, ADDRMETH_RR | OPTYPE_v | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "pop", "", 4, 0, 0, 0 , 33 }, + { 0, INS_POP, 0, ADDRMETH_RR | OPTYPE_v | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "pop", "", 5, 0, 0, 0 , 33 }, + { 0, INS_POP, 0, ADDRMETH_RR | OPTYPE_v | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "pop", "", 6, 0, 0, 0 , 33 }, + { 0, INS_POP, 0, ADDRMETH_RR | OPTYPE_v | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "pop", "", 7, 0, 0, 0 , 33 }, +/* 0x60 */ + { 0, INS_PUSHREGS, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "pusha", "", 0, 0, 0, 0 , 36 }, + { 0, INS_POPREGS, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "popa", "", 0, 0, 0, 0 , 34 }, + { 0, INS_BOUNDS, INS_NOTE_NONSWAP, ADDRMETH_G | OPTYPE_v | OP_R, ADDRMETH_M | OPTYPE_a | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "bound", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ADDRMETH_E | OPTYPE_w | OP_R | OP_W, ADDRMETH_G | OPTYPE_w | OP_R, ARG_NONE, cpu_80386 | isa_GP, "arpl", "", 0, 0, 0, INS_SET_ZERO, 0 }, + { 0, INS_FLAG_PREFIX | PREFIX_FS, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_FLAG_PREFIX | PREFIX_GS, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { idx_66, INS_FLAG_PREFIX | PREFIX_OP_SIZE, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_FLAG_PREFIX | PREFIX_ADDR_SIZE, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_PUSH, 0, ADDRMETH_I | OPTYPE_v | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "push", "", 0, 0, 0, 0 , 33 }, + { 0, INS_MUL, 0, ADDRMETH_G | OPTYPE_v | OP_SIGNED | OP_R | OP_W, ADDRMETH_E | OPTYPE_v | OP_SIGNED | OP_R, ADDRMETH_I | OPTYPE_v | OP_SIGNED | OP_R, cpu_80386 | isa_GP, "imul", "", 0, 0, 0, INS_SET_OFLOW|INS_SET_CARRY, 0 }, + { 0, INS_PUSH, 0, ADDRMETH_I | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "push", "", 0, 0, 0, 0 , 33 }, + { 0, INS_MUL, 0, ADDRMETH_G | OPTYPE_v | OP_SIGNED | OP_R | OP_W, ADDRMETH_E | OPTYPE_v | OP_SIGNED | OP_R, ADDRMETH_I | OPTYPE_b | OP_SIGNED | OP_R, cpu_80386 | isa_GP, "imul", "", 0, 0, 0, INS_SET_OFLOW|INS_SET_CARRY, 0 }, + { 0, INS_IN, 0, ADDRMETH_Y | OPTYPE_b | OP_W, ADDRMETH_RR | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "ins", "", 0, 2, 0, 0 , 0 }, + { 0, INS_IN, 0, ADDRMETH_Y | OPTYPE_v | OP_W, ADDRMETH_RR | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "ins", "", 0, 2, 0, 0 , 0 }, + { 0, INS_OUT, 0, ADDRMETH_RR | OPTYPE_b | OP_R, ADDRMETH_X | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "outs", "", 2, 0, 0, 0 , 0 }, + { 0, INS_OUT, 0, ADDRMETH_RR | OPTYPE_v | OP_R, ADDRMETH_X | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "outs", "", 2, 0, 0, 0 , 0 }, +/* 0x70 */ + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_b | OP_X, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jo", "", 0, 0, 0, INS_TEST_OFLOW, 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_b | OP_X, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jno", "", 0, 0, 0, INS_TEST_NOFLOW, 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_b | OP_X, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jc", "", 0, 0, 0, INS_TEST_CARRY, 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_b | OP_X, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jnc", "", 0, 0, 0, INS_TEST_NCARRY, 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_b | OP_X, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jz", "", 0, 0, 0, INS_TEST_ZERO, 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_b | OP_X, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jnz", "", 0, 0, 0, INS_TEST_NZERO, 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_b | OP_X, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jbe", "", 0, 0, 0, INS_TEST_CARRY|INS_TEST_OR|INS_TEST_ZERO, 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_b | OP_X, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "ja", "", 0, 0, 0, INS_TEST_NCARRY|INS_TEST_NZERO, 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_b | OP_X, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "js", "", 0, 0, 0, INS_TEST_SIGN, 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_b | OP_X, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jns", "", 0, 0, 0, INS_TEST_NSIGN, 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_b | OP_X, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jpe", "", 0, 0, 0, INS_TEST_PARITY, 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_b | OP_X, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jpo", "", 0, 0, 0, INS_TEST_NPARITY, 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_b | OP_X, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jl", "", 0, 0, 0, INS_TEST_SFNEOF, 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_b | OP_X, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jge", "", 0, 0, 0, INS_TEST_SFEQOF, 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_b | OP_X, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jle", "", 0, 0, 0, INS_TEST_ZERO|INS_TEST_OR|INS_TEST_SFNEOF, 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_b | OP_X, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jg", "", 0, 0, 0, INS_TEST_NZERO|INS_TEST_SFEQOF, 0 }, +/* 0x80 */ + { idx_80, 0, 0, ADDRMETH_E | OPTYPE_b, ADDRMETH_I | OPTYPE_b, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { idx_81, 0, 0, ADDRMETH_E | OPTYPE_v, ADDRMETH_I | OPTYPE_v, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { idx_82, 0, 0, ADDRMETH_E | OPTYPE_b, ADDRMETH_I | OPTYPE_b, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { idx_83, 0, 0, ADDRMETH_E | OPTYPE_v, ADDRMETH_I | OPTYPE_b, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_TEST, 0, ADDRMETH_E | OPTYPE_b | OP_R, ADDRMETH_G | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "test", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_TEST, 0, ADDRMETH_E | OPTYPE_v | OP_R, ADDRMETH_G | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "test", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_XCHG, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_G | OPTYPE_b | OP_W | OP_R, ARG_NONE, cpu_80386 | isa_GP, "xchg", "", 0, 0, 0, 0 , 0 }, + { 0, INS_XCHG, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_G | OPTYPE_v | OP_W | OP_R, ARG_NONE, cpu_80386 | isa_GP, "xchg", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_E | OPTYPE_b | OP_W, ADDRMETH_G | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_E | OPTYPE_v | OP_W, ADDRMETH_G | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_G | OPTYPE_b | OP_W, ADDRMETH_E | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_G | OPTYPE_v | OP_W, ADDRMETH_E | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_E | OPTYPE_w | OP_W, ADDRMETH_S | OPTYPE_w | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_G | OPTYPE_v | OP_W, ADDRMETH_M | OPTYPE_m | OP_R, ARG_NONE, cpu_80386 | isa_GP, "lea", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_S | OPTYPE_w | OP_W, ADDRMETH_E | OPTYPE_w | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 0, 0, 0, 0 , 0 }, + { 0, INS_POP, 0, ADDRMETH_E | OPTYPE_v | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "pop", "", 0, 0, 0, 0 , 33 }, +/* 0x90 */ + { 0, INS_NOP, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "nop", "", 0, 0, 0, 0 , 0 }, + { 0, INS_XCHG, 0, ADDRMETH_RR | OPTYPE_v | OP_W | OP_R, ADDRMETH_RR | OPTYPE_v | OP_W | OP_R, ARG_NONE, cpu_80386 | isa_GP, "xchg", "", 0, 1, 0, 0 , 0 }, + { 0, INS_XCHG, 0, ADDRMETH_RR | OPTYPE_v | OP_W | OP_R, ADDRMETH_RR | OPTYPE_v | OP_W | OP_R, ARG_NONE, cpu_80386 | isa_GP, "xchg", "", 0, 2, 0, 0 , 0 }, + { 0, INS_XCHG, 0, ADDRMETH_RR | OPTYPE_v | OP_W | OP_R, ADDRMETH_RR | OPTYPE_v | OP_W | OP_R, ARG_NONE, cpu_80386 | isa_GP, "xchg", "", 0, 3, 0, 0 , 0 }, + { 0, INS_XCHG, 0, ADDRMETH_RR | OPTYPE_v | OP_W | OP_R, ADDRMETH_RR | OPTYPE_v | OP_W | OP_R, ARG_NONE, cpu_80386 | isa_GP, "xchg", "", 0, 4, 0, 0 , 0 }, + { 0, INS_XCHG, 0, ADDRMETH_RR | OPTYPE_v | OP_W | OP_R, ADDRMETH_RR | OPTYPE_v | OP_W | OP_R, ARG_NONE, cpu_80386 | isa_GP, "xchg", "", 0, 5, 0, 0 , 0 }, + { 0, INS_XCHG, 0, ADDRMETH_RR | OPTYPE_v | OP_W | OP_R, ADDRMETH_RR | OPTYPE_v | OP_W | OP_R, ARG_NONE, cpu_80386 | isa_GP, "xchg", "", 0, 6, 0, 0 , 0 }, + { 0, INS_XCHG, 0, ADDRMETH_RR | OPTYPE_v | OP_W | OP_R, ADDRMETH_RR | OPTYPE_v | OP_W | OP_R, ARG_NONE, cpu_80386 | isa_GP, "xchg", "", 0, 7, 0, 0 , 0 }, + { 0, INS_SZCONV, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "cwde", "", 0, 0, 0, 0 , 5 }, + { 0, INS_SZCONV, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "cdq", "", 0, 0, 0, 0 , 11 }, + { 0, INS_CALL, 0, ADDRMETH_A | OPTYPE_p | OP_X, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "callf", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "wait", "", 0, 0, 0, 0 , 0 }, + { 0, INS_PUSHFLAGS, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "pushf", "", 0, 0, 0, 0 , 37 }, + { 0, INS_POPFLAGS, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "popf", "", 0, 0, 0, 0 , 35 }, + { 0, INS_MOV, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "sahf", "", 0, 0, 0, INS_SET_SIGN|INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 43 }, + { 0, INS_MOV, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "lahf", "", 0, 0, 0, 0 , 24 }, +/* 0xa0 */ + { 0, INS_MOV, 0, ADDRMETH_RR | OPTYPE_b | OP_W, ADDRMETH_O | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_RR | OPTYPE_v | OP_W, ADDRMETH_O | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_O | OPTYPE_b | OP_W, ADDRMETH_RR | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_O | OPTYPE_v | OP_W, ADDRMETH_RR | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 0, 0, 0, 0 , 0 }, + { 0, INS_STRMOV, 0, ADDRMETH_Y | OPTYPE_b | OP_W, ADDRMETH_X | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "movs", "", 0, 0, 0, 0 , 0 }, + { 0, INS_STRMOV, 0, ADDRMETH_Y | OPTYPE_v | OP_W, ADDRMETH_X | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "movs", "", 0, 0, 0, 0 , 0 }, + { 0, INS_STRCMP, 0, ADDRMETH_Y | OPTYPE_b | OP_R, ADDRMETH_X | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "cmps", "", 0, 0, 0, 0 , 0 }, + { 0, INS_STRCMP, 0, ADDRMETH_X | OPTYPE_v | OP_R, ADDRMETH_Y | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "cmps", "", 0, 0, 0, 0 , 0 }, + { 0, INS_TEST, 0, ADDRMETH_RR | OPTYPE_b | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "test", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_TEST, 0, ADDRMETH_RR | OPTYPE_v | OP_R, ADDRMETH_I | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "test", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_STRSTOR, 0, ADDRMETH_Y | OPTYPE_b | OP_W, ADDRMETH_RR | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "stos", "", 0, 0, 0, 0 , 0 }, + { 0, INS_STRSTOR, 0, ADDRMETH_Y | OPTYPE_v | OP_W, ADDRMETH_RR | OPTYPE_v |OP_R, ARG_NONE, cpu_80386 | isa_GP, "stos", "", 0, 0, 0, 0 , 0 }, + { 0, INS_STRLOAD, 0, ADDRMETH_RR | OPTYPE_b | OP_W, ADDRMETH_X| OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "lods", "", 0, 0, 0, 0 , 0 }, + { 0, INS_STRLOAD, 0, ADDRMETH_RR | OPTYPE_v | OP_W, ADDRMETH_X| OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "lods", "", 0, 0, 0, 0 , 0 }, + { 0, INS_STRCMP, 0, ADDRMETH_RR | OPTYPE_b | OP_R, ADDRMETH_Y | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "scas", "", 0, 0, 0, 0 , 0 }, + { 0, INS_STRCMP, 0, ADDRMETH_RR | OPTYPE_v | OP_R, ADDRMETH_Y | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "scas", "", 0, 0, 0, 0 , 0 }, +/* 0xb0 */ + { 0, INS_MOV, 0, ADDRMETH_RR | OPTYPE_b | OP_W, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_RR | OPTYPE_b | OP_W, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 1, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_RR | OPTYPE_b | OP_W, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 2, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_RR | OPTYPE_b | OP_W, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 3, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_RR | OPTYPE_b | OP_W, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 4, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_RR | OPTYPE_b | OP_W, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 5, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_RR | OPTYPE_b | OP_W, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 6, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_RR | OPTYPE_b | OP_W, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 7, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_RR | OPTYPE_v | OP_W, ADDRMETH_I | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_RR | OPTYPE_v | OP_W, ADDRMETH_I | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 1, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_RR | OPTYPE_v | OP_W, ADDRMETH_I | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 2, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_RR | OPTYPE_v | OP_W, ADDRMETH_I | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 3, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_RR | OPTYPE_v | OP_W, ADDRMETH_I | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 4, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_RR | OPTYPE_v | OP_W, ADDRMETH_I | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 5, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_RR | OPTYPE_v | OP_W, ADDRMETH_I | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 6, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_RR | OPTYPE_v | OP_W, ADDRMETH_I | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 7, 0, 0, 0 , 0 }, +/* 0xc0 */ + { idx_C0, 0, 0, ADDRMETH_E | OPTYPE_b, ADDRMETH_I | OPTYPE_b, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { idx_C1, 0, 0, ADDRMETH_E | OPTYPE_v, ADDRMETH_I | OPTYPE_b, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_RET, 0, ADDRMETH_I | OPTYPE_w | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "ret", "", 0, 0, 0, 0 , 3 }, + { 0, INS_RET, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "ret", "", 0, 0, 0, 0 , 3 }, + { 0, INS_MOV, 0, ADDRMETH_G | OPTYPE_v | OP_W, ADDRMETH_M | OPTYPE_p | OP_R, ARG_NONE, cpu_80386 | isa_GP, "les", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_G | OPTYPE_v | OP_W, ADDRMETH_M | OPTYPE_p | OP_R, ARG_NONE, cpu_80386 | isa_GP, "lds", "", 0, 0, 0, 0 , 0 }, + { idx_C6, 0, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { idx_C7, 0, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_ENTER, INS_NOTE_NONSWAP, ADDRMETH_I | OPTYPE_w | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "enter", "", 0, 0, 0, 0 , 15 }, + { 0, INS_LEAVE, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "leave", "", 0, 0, 0, 0 , 26 }, + { 0, INS_RET, 0, ADDRMETH_I | OPTYPE_w | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "retf", "lret", 0, 0, 0, 0 , 3 }, + { 0, INS_RET, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "retf", "lret", 0, 0, 0, 0 , 3 }, + { 0, INS_DEBUG, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "int3", "", 0, 0, 0, 0 , 0 }, + { 0, INS_TRAP, 0, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "int", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OFLOW, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "into", "", 0, 0, 0, INS_TEST_OFLOW, 0 }, + { 0, INS_TRET, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "iret", "", 0, 0, 0, INS_SET_ALL|INS_SET_DIR, 0 }, +/* 0xd0 */ + { idx_D0, 0, 0, ADDRMETH_E | OPTYPE_b, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 1, 0, 0 , 0 }, + { idx_D1, 0, 0, ADDRMETH_E | OPTYPE_v, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 1, 0, 0 , 0 }, + { idx_D2, 0, 0, ADDRMETH_E | OPTYPE_b, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 1, 0, 0 , 0 }, + { idx_D3, 0, 0, ADDRMETH_E | OPTYPE_v, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 1, 0, 0 , 0 }, + { 0, INS_BCDCONV, 0, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "aam", "", 0, 0, 0, INS_SET_SIGN|INS_SET_ZERO|INS_SET_PARITY, 0 }, + { 0, INS_BCDCONV, 0, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "aad", "", 0, 0, 0, INS_SET_SIGN|INS_SET_ZERO|INS_SET_PARITY, 2 }, + { 0, INS_SALC, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "salc", "", 0, 0, 0, 0 , 0 }, + { 0, INS_XLAT, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "xlat", "", 0, 0, 0, 0 , 53 }, + { idx_D8, 0, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { idx_D9, 0, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { idx_DA, 0, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { idx_DB, 0, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { idx_DC, 0, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { idx_DD, 0, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { idx_DE, 0, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { idx_DF, 0, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, +/* 0xe0 */ + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_b | OP_X, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "loopnz", "", 0, 0, 0, INS_TEST_NZERO, 31 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_b | OP_X, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "loopz", "", 0, 0, 0, INS_TEST_ZERO, 31 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_b | OP_X, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "loop", "", 0, 0, 0, 0 , 31 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_b | OP_X, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jcxz", "", 0, 0, 0, 0 , 31 }, + { 0, INS_IN, 0, ADDRMETH_RR | OPTYPE_b | OP_W, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "in", "", 0, 0, 0, 0 , 0 }, + { 0, INS_IN, 0, ADDRMETH_RR | OPTYPE_b | OP_W, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "in", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OUT, 0, ADDRMETH_I | OPTYPE_b | OP_R, ADDRMETH_RR | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "out", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OUT, 0, ADDRMETH_I | OPTYPE_b | OP_R, ADDRMETH_RR | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "out", "", 0, 0, 0, 0 , 0 }, + { 0, INS_CALL, 0, ADDRMETH_J | OPTYPE_v | OP_X | OP_SIGNED, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "call", "", 0, 0, 0, 0 , 3 }, + { 0, INS_BRANCH, 0, ADDRMETH_J | OPTYPE_v | OP_X | OP_SIGNED, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jmp", "", 0, 0, 0, 0 , 0 }, + { 0, INS_BRANCH, 0, ADDRMETH_A | OPTYPE_p | OP_X, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jmp", "", 0, 0, 0, 0 , 0 }, + { 0, INS_BRANCH, 0, ADDRMETH_J | OPTYPE_b | OP_X | OP_SIGNED, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jmp", "", 0, 0, 0, 0 , 0 }, + { 0, INS_IN, 0, ADDRMETH_RR | OPTYPE_b| OP_W, ADDRMETH_RR | OPTYPE_w| OP_R, ARG_NONE, cpu_80386 | isa_GP, "in", "", 0, 2, 0, 0 , 0 }, + { 0, INS_IN, 0, ADDRMETH_RR | OPTYPE_v | OP_W, ADDRMETH_RR | OPTYPE_w| OP_R, ARG_NONE, cpu_80386 | isa_GP, "in", "", 0, 2, 0, 0 , 0 }, + { 0, INS_OUT, 0, ADDRMETH_RR | OPTYPE_w| OP_R, ADDRMETH_RR | OPTYPE_b| OP_R, ARG_NONE, cpu_80386 | isa_GP, "out", "", 2, 0, 0, 0 , 0 }, + { 0, INS_OUT, 0, ADDRMETH_RR | OPTYPE_w| OP_R, ADDRMETH_RR | OPTYPE_v| OP_R, ARG_NONE, cpu_80386 | isa_GP, "out", "", 2, 0, 0, 0 , 0 }, +/* 0xf0 */ + { 0, INS_FLAG_PREFIX | PREFIX_LOCK, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_ICEBP, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "icebp", "", 0, 0, 0, 0 , 0 }, + { idx_F2, INS_FLAG_PREFIX | PREFIX_REPNZ, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { idx_F3, INS_FLAG_PREFIX | PREFIX_REPZ, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_HALT, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "hlt", "", 0, 0, 0, 0 , 0 }, + { 0, INS_TOGCF, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "cmc", "", 0, 0, 0, INS_SET_CARRY, 0 }, + { idx_F6, 0, 0, ADDRMETH_E | OPTYPE_b, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { idx_F7, 0, 0, ADDRMETH_E | OPTYPE_v, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_CLEARCF, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "clc", "", 0, 0, 0, INS_SET_NCARRY, 0 }, + { 0, INS_SETCF, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "stc", "", 0, 0, 0, INS_SET_CARRY, 0 }, + { 0, INS_SYSTEM, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "cli", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "sti", "", 0, 0, 0, 0 , 0 }, + { 0, INS_CLEARDF, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "cld", "", 0, 0, 0, INS_SET_NDIR, 0 }, + { 0, INS_SETDF, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "std", "", 0, 0, 0, INS_SET_DIR, 0 }, + { idx_FE, 0, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { idx_FF, 0, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 } +}; + + +static ia32_insn_t tbl_66[] = { /* SIMD 66 one-byte Opcodes */ + { idx_660F, 0, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 } +}; + + +static ia32_insn_t tbl_F2[] = { /* SIMD F2 one-byte Opcodes */ + { idx_F20F, 0, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 } +}; + + +static ia32_insn_t tbl_F3[] = { /* SIMD F3 one-byte Opcodes */ + { idx_F30F, 0, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "pause", "", 0, 0, 0, 0, 0 } +}; + + +static ia32_insn_t tbl_0F[] = { /* Two-byte Opcodes */ + { idx_0F00, 0, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { idx_0F01, 0, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ADDRMETH_G | OPTYPE_v | OP_W, ADDRMETH_E | OPTYPE_w | OP_R, ARG_NONE, cpu_80386 | isa_GP, "lar", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ADDRMETH_G | OPTYPE_v | OP_W, ADDRMETH_E | OPTYPE_w | OP_R, ARG_NONE, cpu_80386 | isa_GP, "lsl", "", 0, 0, 0, INS_SET_ZERO, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "clts", "", 0, 0, 0, 0 , 6 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80486 | isa_GP, "invd", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80486 | isa_GP, "wbinvd", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_UNKNOWN, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTPRO | isa_GP, "ud2", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ADDRMETH_M | OPTYPE_b | OP_R, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "prefetch", "", 0, 0, 0, 0, 0 }, + { 0, INS_SYSTEM, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "femms", "", 0, 0, 0, 0, 0 }, + { idx_0F0F, INS_FLAG_SUFFIX, 0, ADDRMETH_P | OPTYPE_pi | OP_R | OP_W, ADDRMETH_Q | OPTYPE_q |OP_R, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_MOV, 0, ADDRMETH_V | OPTYPE_ps | OP_W, ADDRMETH_W | OPTYPE_ps | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "movups", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_W | OPTYPE_ps | OP_W, ADDRMETH_V | OPTYPE_ps | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "movups", "", 0, 0, 0, 0 , 0 }, + { idx_0F12, 0, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, INS_NOTE_NOSUFFIX, ADDRMETH_V | OPTYPE_q | OP_W, ADDRMETH_W | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "movlps", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_V | OPTYPE_ps | OP_W, ADDRMETH_W | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "unpcklps", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_V | OPTYPE_ps | OP_W, ADDRMETH_W | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "unpckhps", "", 0, 0, 0, 0 , 0 }, + { idx_0F16, 0, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_W | OPTYPE_q | OP_W, ADDRMETH_V | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "movhps", "", 0, 0, 0, 0 , 0 }, + { idx_0F18, 0, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_R | OPTYPE_d | OP_W, ADDRMETH_C | OPTYPE_d | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_R | OPTYPE_d | OP_W, ADDRMETH_D | OPTYPE_d | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_C | OPTYPE_d | OP_W, ADDRMETH_R | OPTYPE_d | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_D | OPTYPE_d | OP_W, ADDRMETH_R | OPTYPE_d | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_R | OPTYPE_d | OP_W, ADDRMETH_T | OPTYPE_d | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_T | OPTYPE_d | OP_W, ADDRMETH_R | OPTYPE_d | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_V | OPTYPE_ps | OP_W, ADDRMETH_W | OPTYPE_ps | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "movaps", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_W | OPTYPE_ps | OP_W, ADDRMETH_V | OPTYPE_ps | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "movaps", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_V | OPTYPE_ps | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "cvtpi2ps", "", 0, 0, 0, 0, 0 }, + { 0, INS_MOV, 0, ADDRMETH_W | OPTYPE_ps | OP_W, ADDRMETH_V | OPTYPE_ps | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "movntps", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_W | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "cvttps2pi", "", 0, 0, 0, 0, 0 }, + { 0, INS_MOV, 0, ADDRMETH_P | OPTYPE_q | OP_W , ADDRMETH_W | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "cvtps2pi", "", 0, 0, 0, 0, 0 }, + { 0, INS_OTHER, 0, ADDRMETH_V | OPTYPE_ss | OP_W, ADDRMETH_W | OPTYPE_ss | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "ucomiss", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_OTHER, 0, ADDRMETH_V | OPTYPE_ps | OP_W, ADDRMETH_W | OPTYPE_ss | OP_W, ARG_NONE, cpu_PENTIUM2 | isa_GP, "comiss", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_SYSTEM, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM | isa_GP, "wrmsr", "", 0, 0, 0, 0 , 52 }, + { 0, INS_SYSTEM, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM | isa_GP, "rdtsc", "", 0, 0, 0, 0 , 40 }, + { 0, INS_SYSTEM, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM | isa_GP, "rdmsr", "", 0, 0, 0, 0 , 38 }, + { 0, INS_SYSTEM, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTPRO | isa_GP, "rdpmc", "", 0, 0, 0, 0 , 39 }, + { 0, INS_SYSTEM, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM2 | isa_GP, "sysenter", "", 0, 0, 0, 0 , 50 }, + { 0, INS_SYSTEM, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM2 | isa_GP, "sysexit", "", 0, 0, 0, 0 , 51 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_G | OPTYPE_v | OP_W, ADDRMETH_E | OPTYPE_v | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "cmovo", "", 0, 0, 0, INS_TEST_OFLOW, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_G | OPTYPE_v | OP_W, ADDRMETH_E | OPTYPE_v | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "cmovno", "", 0, 0, 0, INS_TEST_NOFLOW, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_G | OPTYPE_v | OP_W, ADDRMETH_E | OPTYPE_v | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "cmovc", "", 0, 0, 0, INS_TEST_CARRY, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_G | OPTYPE_v | OP_W, ADDRMETH_E | OPTYPE_v | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "cmovnc", "", 0, 0, 0, INS_TEST_NCARRY, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_G | OPTYPE_v | OP_W, ADDRMETH_E | OPTYPE_v | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "cmovz", "", 0, 0, 0, INS_TEST_ZERO, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_G | OPTYPE_v | OP_W, ADDRMETH_E | OPTYPE_v | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "cmovnz", "", 0, 0, 0, INS_TEST_NZERO, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_G | OPTYPE_v | OP_W, ADDRMETH_E | OPTYPE_v | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "cmovbe", "", 0, 0, 0, INS_TEST_CARRY|INS_TEST_OR|INS_TEST_ZERO, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_G | OPTYPE_v | OP_W, ADDRMETH_E | OPTYPE_v | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "cmova", "", 0, 0, 0, INS_TEST_NZERO|INS_TEST_NCARRY, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_G | OPTYPE_v | OP_W, ADDRMETH_E | OPTYPE_v | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "cmovs", "", 0, 0, 0, INS_TEST_SIGN, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_G | OPTYPE_v | OP_W, ADDRMETH_E | OPTYPE_v | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "cmovns", "", 0, 0, 0, INS_TEST_NSIGN, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_G | OPTYPE_v | OP_W, ADDRMETH_E | OPTYPE_v | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "cmovp", "", 0, 0, 0, INS_TEST_PARITY, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_G | OPTYPE_v | OP_W, ADDRMETH_E | OPTYPE_v | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "cmovnp", "", 0, 0, 0, INS_TEST_NPARITY, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_G | OPTYPE_v | OP_W, ADDRMETH_E | OPTYPE_v | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "cmovl", "", 0, 0, 0, INS_TEST_SFNEOF, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_G | OPTYPE_v | OP_W, ADDRMETH_E | OPTYPE_v | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "cmovge", "", 0, 0, 0, INS_TEST_SFEQOF, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_G | OPTYPE_v | OP_W, ADDRMETH_E | OPTYPE_v | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "cmovle", "", 0, 0, 0, INS_TEST_ZERO|INS_TEST_OR|INS_TEST_SFNEOF, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_G | OPTYPE_v | OP_W, ADDRMETH_E | OPTYPE_v | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "cmovg", "", 0, 0, 0, INS_TEST_NZERO|INS_TEST_SFEQOF, 0 }, + { 0, INS_MOV, 0, ADDRMETH_G | OPTYPE_d | OP_W, ADDRMETH_W | OPTYPE_ps | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "movmskps", "", 0, 0, 0, 0 , 0 }, + { 0, INS_ARITH, 0, ADDRMETH_V | OPTYPE_ps | OP_W, ADDRMETH_W | OPTYPE_ps | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "sqrtps", "", 0, 0, 0, 0 , 0 }, + { 0, INS_ARITH, 0, ADDRMETH_V | OPTYPE_ps | OP_W, ADDRMETH_W | OPTYPE_ps | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "rsqrtps", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_V | OPTYPE_ps | OP_W, ADDRMETH_W | OPTYPE_ps | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "rcpps", "", 0, 0, 0, 0 , 0 }, + { 0, INS_AND, 0, ADDRMETH_V | OPTYPE_ps | OP_W, ADDRMETH_W | OPTYPE_ps | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "andps", "", 0, 0, 0, 0 , 0 }, + { 0, INS_AND, 0, ADDRMETH_V | OPTYPE_ps | OP_W, ADDRMETH_W | OPTYPE_ps | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "andnps", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OR, 0, ADDRMETH_V | OPTYPE_ps | OP_W, ADDRMETH_W | OPTYPE_ps | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "orps", "", 0, 0, 0, 0 , 0 }, + { 0, INS_XOR, 0, ADDRMETH_V | OPTYPE_ps | OP_W, ADDRMETH_W | OPTYPE_ps | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "xorps", "", 0, 0, 0, 0 , 0 }, + { 0, INS_ADD, 0, ADDRMETH_V | OPTYPE_ps | OP_W, ADDRMETH_W | OPTYPE_ps | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "addps", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MUL, 0, ADDRMETH_V | OPTYPE_ps | OP_R, ADDRMETH_W | OPTYPE_ps | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "mulps", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_V | OPTYPE_pd, ADDRMETH_W | OPTYPE_q, ARG_NONE, cpu_PENTIUM4 | isa_GP, "cvtps2pd", "", 0, 0, 0, 0, 0 }, + { 0, INS_MOV, 0, ADDRMETH_V | OPTYPE_ps | OP_W, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "cvtdq2ps", "", 0, 0, 0, 0, 0 }, + { 0, INS_SUB, 0, ADDRMETH_V | OPTYPE_ps | OP_W, ADDRMETH_W | OPTYPE_ps | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "subps", "", 0, 0, 0, 0 , 0 }, + { 0, INS_ARITH, 0, ADDRMETH_V | OPTYPE_ps | OP_W, ADDRMETH_W | OPTYPE_ps | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "minps", "", 0, 0, 0, 0 , 0 }, + { 0, INS_DIV, 0, ADDRMETH_V | OPTYPE_ps | OP_W, ADDRMETH_W | OPTYPE_ps | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "divps", "", 0, 0, 0, 0 , 0 }, + { 0, INS_ARITH, 0, ADDRMETH_V | OPTYPE_ps | OP_W, ADDRMETH_W | OPTYPE_ps | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "maxps", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_d | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "punpcklbw", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_d | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "punpcklwd", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_d | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "punpckldq", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_d | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "packsswb", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_d | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "pcmpgtb", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_d | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "pcmpgtw", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_d | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "pcmpgtd", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_d | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "packuswb", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_d | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "punpckhbw", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_d | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "punpckhwd", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_d | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "punpckhdq", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_d | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "packssdw", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_P | OPTYPE_d | OP_W, ADDRMETH_E | OPTYPE_d | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "movd", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "movq", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, cpu_PENTIUM2 | isa_GP, "pshufw", "", 0, 0, 0, 0, 0 }, + { idx_0F71, 0, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM | isa_MMX, "", "", 0, 0, 0, 0 , 0 }, + { idx_0F72, 0, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM | isa_MMX, "", "", 0, 0, 0, 0 , 0 }, + { idx_0F73, 0, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM | isa_MMX, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "pcmpeqb", "", 0, 0, 0, 0 , 0 }, + { 0, INS_CMP, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "pcmpeqw", "", 0, 0, 0, 0 , 0 }, + { 0, INS_CMP, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "pcmpeqd", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM | isa_MMX, "emms", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_E | OPTYPE_d | OP_W, ADDRMETH_P | OPTYPE_d | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "movd", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_Q | OPTYPE_q | OP_W, ADDRMETH_P | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "movq", "", 0, 0, 0, 0 , 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_v | OP_X | OP_SIGNED, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jo", "", 0, 0, 0, INS_TEST_OFLOW, 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_v | OP_X | OP_SIGNED, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jno", "", 0, 0, 0, INS_TEST_NOFLOW, 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_v | OP_X | OP_SIGNED, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jc", "", 0, 0, 0, INS_TEST_CARRY, 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_v | OP_X | OP_SIGNED, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jnc", "", 0, 0, 0, INS_TEST_NCARRY, 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_v | OP_X | OP_SIGNED, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jz", "", 0, 0, 0, INS_TEST_ZERO, 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_v | OP_X | OP_SIGNED, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jnz", "", 0, 0, 0, INS_TEST_NZERO, 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_v | OP_X | OP_SIGNED, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jbe", "", 0, 0, 0, INS_TEST_CARRY|INS_TEST_OR|INS_TEST_ZERO, 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_v | OP_X | OP_SIGNED, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "ja", "", 0, 0, 0, INS_TEST_NCARRY|INS_TEST_NZERO, 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_v | OP_X | OP_SIGNED, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "js", "", 0, 0, 0, INS_TEST_SIGN, 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_v | OP_X | OP_SIGNED, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jns", "", 0, 0, 0, INS_TEST_NSIGN, 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_v | OP_X | OP_SIGNED, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jpe", "", 0, 0, 0, INS_TEST_PARITY, 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_v | OP_X | OP_SIGNED, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jpo", "", 0, 0, 0, INS_TEST_NPARITY, 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_v | OP_X | OP_SIGNED, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jl", "", 0, 0, 0, INS_TEST_SFNEOF, 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_v | OP_X | OP_SIGNED, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jge", "", 0, 0, 0, INS_TEST_SFEQOF, 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_v | OP_X | OP_SIGNED, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jle", "", 0, 0, 0, INS_TEST_ZERO|INS_TEST_OR|INS_TEST_SFNEOF, 0 }, + { 0, INS_BRANCHCC, 0, ADDRMETH_J | OPTYPE_v | OP_X | OP_SIGNED, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jg", "", 0, 0, 0, INS_TEST_NZERO|INS_TEST_SFEQOF, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_E | OPTYPE_b | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "seto", "", 0, 0, 0, INS_TEST_OFLOW, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_E | OPTYPE_b | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "setno", "", 0, 0, 0, INS_TEST_OFLOW, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_E | OPTYPE_b | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "setc", "", 0, 0, 0, INS_TEST_CARRY, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_E | OPTYPE_b | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "setnc", "", 0, 0, 0, INS_TEST_NCARRY, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_E | OPTYPE_b | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "setz", "", 0, 0, 0, INS_TEST_ZERO, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_E | OPTYPE_b | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "setnz", "", 0, 0, 0, INS_TEST_NZERO, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_E | OPTYPE_b | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "setbe", "", 0, 0, 0, INS_TEST_CARRY|INS_TEST_OR|INS_TEST_ZERO, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_E | OPTYPE_b | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "seta", "", 0, 0, 0, INS_TEST_NCARRY|INS_TEST_NZERO, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_E | OPTYPE_b | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "sets", "", 0, 0, 0, INS_TEST_SIGN, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_E | OPTYPE_b | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "setns", "", 0, 0, 0, INS_TEST_NSIGN, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_E | OPTYPE_b | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "setpe", "", 0, 0, 0, INS_TEST_PARITY, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_E | OPTYPE_b | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "setpo", "", 0, 0, 0, INS_TEST_NPARITY, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_E | OPTYPE_b | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "setl", "", 0, 0, 0, INS_TEST_SFNEOF, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_E | OPTYPE_b | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "setge", "", 0, 0, 0, INS_TEST_SFEQOF, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_E | OPTYPE_b | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "setle", "", 0, 0, 0, INS_TEST_ZERO|INS_TEST_OR|INS_TEST_SFNEOF, 0 }, + { 0, INS_MOVCC, 0, ADDRMETH_E | OPTYPE_b | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "setg", "", 0, 0, 0, INS_TEST_NZERO|INS_TEST_SFEQOF, 0 }, + { 0, INS_PUSH, 0, ADDRMETH_RS | OPTYPE_w | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "push", "", 4, 0, 0, 0 , 33 }, + { 0, INS_POP, 0, ADDRMETH_RS | OPTYPE_w | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "pop", "", 4, 0, 0, 0 , 33 }, + { 0, INS_CPUID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM | isa_GP, "cpuid", "", 0, 0, 0, 0 , 10 }, + { 0, INS_BITTEST, 0, ADDRMETH_E | OPTYPE_v | OP_R, ADDRMETH_G | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "bt", "", 0, 0, 0, INS_SET_CARRY, 0 }, + { 0, INS_SHL, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_G | OPTYPE_v | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, cpu_80386 | isa_GP, "shld", "", 0, 0, 0, INS_SET_SIGN|INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0 }, + //{ 0, INS_SHL, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_G | OPTYPE_v | OP_R, ADDRMETH_I | OP_R | OPTYPE_b | ADDRMETH_RR, cpu_80386 | isa_GP, "shld", "", 0, 0, 1, INS_SET_SIGN|INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0 }, + { 0, INS_SHL, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_G | OPTYPE_v | OP_R, ADDRMETH_RR | OP_R | OPTYPE_b, cpu_80386 | isa_GP, "shld", "", 0, 0, 1, INS_SET_SIGN|INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_PUSH, 0, ADDRMETH_RS | OPTYPE_w | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "push", "", 5, 0, 0, 0 , 33 }, + { 0, INS_POP, 0, ADDRMETH_RS | OPTYPE_w | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "pop", "", 5, 0, 0, 0 , 33 }, + { 0, INS_SYSTEM, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "rsm", "", 0, 0, 0, INS_SET_ALL|INS_SET_DIR, 42 }, + { 0, INS_BITTEST, 0, ADDRMETH_E | OPTYPE_v | OP_R, ADDRMETH_G | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "bts", "", 0, 0, 0, INS_SET_CARRY, 0 }, + { 0, INS_SHR, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_G | OPTYPE_v | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, cpu_80386 | isa_GP, "shrd", "", 0, 0, 0, INS_SET_SIGN|INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0 }, + { 0, INS_SHR, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_G | OPTYPE_v | OP_R, ADDRMETH_RR | OP_R | OPTYPE_b , cpu_80386 | isa_GP, "shrd", "", 0, 0, 1, INS_SET_SIGN|INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0 }, + { idx_0FAE, 0, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM2 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MUL, 0, ADDRMETH_G | OPTYPE_v | OP_SIGNED | OP_R | OP_W, ADDRMETH_E | OPTYPE_v | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "imul", "", 0, 0, 0, INS_SET_OFLOW|INS_SET_CARRY, }, + { 0, INS_XCHGCC, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_G | OPTYPE_b | OP_W, ARG_NONE, cpu_80486 | isa_GP, "cmpxchg", "", 0, 0, 0, INS_SET_ALL, 8 }, + { 0, INS_XCHGCC, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_G | OPTYPE_v | OP_W, ARG_NONE, cpu_80486 | isa_GP, "cmpxchg", "", 0, 0, 0, INS_SET_ALL, 7 }, + { 0, INS_MOV, 0, ADDRMETH_G | OPTYPE_v | OP_W, ADDRMETH_M | OPTYPE_p | OP_W, ARG_NONE, cpu_80386 | isa_GP, "lss", "", 0, 0, 0, 0 , 0 }, + { 0, INS_BITTEST, 0, ADDRMETH_E | OPTYPE_v | OP_R, ADDRMETH_G | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "btr", "", 0, 0, 0, INS_SET_CARRY, 0 }, + { 0, INS_MOV, 0, ADDRMETH_G | OPTYPE_v | OP_W, ADDRMETH_M | OPTYPE_p | OP_W, ARG_NONE, cpu_80386 | isa_GP, "lfs", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_G | OPTYPE_v | OP_W, ADDRMETH_M | OPTYPE_p | OP_W, ARG_NONE, cpu_80386 | isa_GP, "lgs", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_G | OPTYPE_v | OP_W, ADDRMETH_E | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "movzx", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_G | OPTYPE_v | OP_W, ADDRMETH_E | OPTYPE_w | OP_R, ARG_NONE, cpu_80386 | isa_GP, "movzx", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_UNKNOWN, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "ud1", "", 0, 0, 0, 0 , 0 }, + { idx_0FBA, 0, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_BITTEST, 0, ADDRMETH_E | OPTYPE_v | OP_R, ADDRMETH_G | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "btc", "", 0, 0, 0, INS_SET_CARRY, 0 }, + { 0, INS_BITTEST, 0, ADDRMETH_G | OPTYPE_v | OP_R | OP_W, ADDRMETH_E | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "bsf", "", 0, 0, 0, INS_SET_ZERO, 0 }, + { 0, INS_BITTEST, 0, ADDRMETH_G | OPTYPE_v | OP_R | OP_W, ADDRMETH_E | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "bsr", "", 0, 0, 0, INS_SET_ZERO, 0 }, + { 0, INS_MOV, 0, ADDRMETH_G | OPTYPE_v | OP_W, ADDRMETH_E | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "movsx", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_G | OPTYPE_v | OP_W, ADDRMETH_E | OPTYPE_w | OP_R, ARG_NONE, cpu_80386 | isa_GP, "movsx", "", 0, 0, 0, 0 , 0 }, + { 0, INS_ADD, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_G | OPTYPE_b | OP_W, ARG_NONE, cpu_80486 | isa_GP, "xadd", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_ADD, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_G | OPTYPE_v | OP_W, ARG_NONE, cpu_80486 | isa_GP, "xadd", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_CMP, 0, ADDRMETH_V | OPTYPE_ps | OP_R, ADDRMETH_W | OPTYPE_ps | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, cpu_PENTIUM4 | isa_GP, "cmpps", "", 0, 0, 0, 0, 0 }, + { 0, INS_MOV, 0, ADDRMETH_M | OPTYPE_d | OP_W, ADDRMETH_G | OPTYPE_d | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "movnti", "", 0, 0, 0, 0, 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_E | OPTYPE_d | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, cpu_PENTIUM2 | isa_GP, "pinsrw", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_G | OPTYPE_d | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, cpu_PENTIUM2 | isa_GP, "pextrw", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_V | OPTYPE_ps | OP_W, ADDRMETH_W | OPTYPE_ps | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, cpu_PENTIUM2 | isa_GP, "shufps", "", 0, 0, 0, 0 , 0 }, + { 0, INS_XCHGCC, 0, ADDRMETH_M | OPTYPE_q | OP_R | OP_W, ARG_NONE, ARG_NONE, cpu_PENTIUM | isa_GP, "cmpxchg8b", "", 0, 0, 0, 0, 9 }, + { 0, INS_XCHG, 0, ADDRMETH_RR | OPTYPE_d | OP_W | OP_R, ARG_NONE, ARG_NONE, cpu_80486 | isa_GP, "bswap", "", 0, 0, 0, 0 , 0 }, + { 0, INS_XCHG, 0, ADDRMETH_RR | OPTYPE_d | OP_W | OP_R, ARG_NONE, ARG_NONE, cpu_80486 | isa_GP, "bswap", "", 1, 0, 0, 0 , 0 }, + { 0, INS_XCHG, 0, ADDRMETH_RR | OPTYPE_d | OP_W | OP_R, ARG_NONE, ARG_NONE, cpu_80486 | isa_GP, "bswap", "", 2, 0, 0, 0 , 0 }, + { 0, INS_XCHG, 0, ADDRMETH_RR | OPTYPE_d | OP_W | OP_R, ARG_NONE, ARG_NONE, cpu_80486 | isa_GP, "bswap", "", 3, 0, 0, 0 , 0 }, + { 0, INS_XCHG, 0, ADDRMETH_RR | OPTYPE_d | OP_W | OP_R, ARG_NONE, ARG_NONE, cpu_80486 | isa_GP, "bswap", "", 4, 0, 0, 0 , 0 }, + { 0, INS_XCHG, 0, ADDRMETH_RR | OPTYPE_d | OP_W | OP_R, ARG_NONE, ARG_NONE, cpu_80486 | isa_GP, "bswap", "", 5, 0, 0, 0 , 0 }, + { 0, INS_XCHG, 0, ADDRMETH_RR | OPTYPE_d | OP_W | OP_R, ARG_NONE, ARG_NONE, cpu_80486 | isa_GP, "bswap", "", 6, 0, 0, 0 , 0 }, + { 0, INS_XCHG, 0, ADDRMETH_RR | OPTYPE_d | OP_W | OP_R, ARG_NONE, ARG_NONE, cpu_80486 | isa_GP, "bswap", "", 7, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "psrlw", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "psrld", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "psrlq", "", 0, 0, 0, 0 , 0 }, + { 0, INS_ADD, 0, ADDRMETH_P | OPTYPE_q | OP_R | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "paddq", "", 0, 0, 0, 0, 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "pmullw", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_G | OPTYPE_d | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "pmovmskb", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "psubusb", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "psubusw", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "pminub", "", 0, 0, 0, 0 , 0 }, + { 0, INS_AND, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "pand", "", 0, 0, 0, 0 , 0 }, + { 0, INS_ADD, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "paddusb", "", 0, 0, 0, 0 , 0 }, + { 0, INS_ADD, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "paddusw", "", 0, 0, 0, 0 , 0 }, + { 0, INS_ARITH, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "pmaxub", "", 0, 0, 0, 0 , 0 }, + { 0, INS_AND, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "pandn", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "pavgb", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "psraw", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "psrad", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "pavgw", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MUL, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "pmulhuw", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MUL, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "pmulhw", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, INS_NOTE_NOSUFFIX, ADDRMETH_W | OPTYPE_q | OP_W, ADDRMETH_V | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "movntq", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SUB, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "psubsb", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SUB, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "psubsw", "", 0, 0, 0, 0 , 0 }, + { 0, INS_ARITH, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "pminsw", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OR, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "por", "", 0, 0, 0, 0 , 0 }, + { 0, INS_ADD, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "paddsb", "", 0, 0, 0, 0 , 0 }, + { 0, INS_ADD, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "paddsw", "", 0, 0, 0, 0 , 0 }, + { 0, INS_ARITH, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "pmaxsw", "", 0, 0, 0, 0 , 0 }, + { 0, INS_XOR, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "pxor", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "psllw", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "pslld", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "psllq", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MUL, 0, ADDRMETH_P | OPTYPE_q | OP_R | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "pmuludq", "", 0, 0, 0, 0, 0 }, + { 0, INS_ADD, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "pmaddwd", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "psadbw", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_P | OPTYPE_pi | OP_W, ADDRMETH_Q | OPTYPE_pi | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "maskmovq", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SUB, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "psubb", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SUB, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "psubw", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SUB, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "psubd", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SUB, 0, ADDRMETH_P | OPTYPE_q | OP_R | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "psubq", "", 0, 0, 0, 0, 0 }, + { 0, INS_ADD, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "paddb", "", 0, 0, 0, 0 , 0 }, + { 0, INS_ADD, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "paddw", "", 0, 0, 0, 0 , 0 }, + { 0, INS_ADD, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "paddd", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 } +}; + + +static ia32_insn_t tbl_660F[] = { /* SIMD 66 Two-byte Opcodes */ + { 0, INS_MOV, 0, ADDRMETH_V | OPTYPE_pd | OP_R, ADDRMETH_W | OPTYPE_pd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "movupd", "", 0, 0, 0, 0, 0 }, + { 0, INS_MOV, 0, ADDRMETH_W | OPTYPE_pd | OP_R, ADDRMETH_V | OPTYPE_pd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "movupd", "", 0, 0, 0, 0, 0 }, + { 0, INS_MOV, INS_NOTE_NOSUFFIX, ADDRMETH_V | OPTYPE_q | OP_R, ADDRMETH_M | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "movlpd", "", 0, 0, 0, 0, 0 }, + { 0, INS_MOV, INS_NOTE_NOSUFFIX, ADDRMETH_M | OPTYPE_q | OP_R, ADDRMETH_V | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "movlpd", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_pd | OP_R, ADDRMETH_W | OPTYPE_pd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "unpcklpd", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_pd | OP_R, ADDRMETH_W | OPTYPE_pd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "unpckhpd", "", 0, 0, 0, 0, 0 }, + { 0, INS_MOV, INS_NOTE_NOSUFFIX, ADDRMETH_V | OPTYPE_q | OP_R, ADDRMETH_M | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "movhpd", "", 0, 0, 0, 0, 0 }, + { 0, INS_MOV, INS_NOTE_NOSUFFIX, ADDRMETH_M | OPTYPE_q | OP_R, ADDRMETH_V | OPTYPE_pd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "movhpd", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_MOV, 0, ADDRMETH_V | OPTYPE_pd | OP_R, ADDRMETH_W | OPTYPE_pd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "movapd", "", 0, 0, 0, 0, 0 }, + { 0, INS_MOV, 0, ADDRMETH_W | OPTYPE_pd | OP_R, ADDRMETH_V | OPTYPE_pd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "movapd", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_pd | OP_R, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "cvtpi2pd", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_M | OPTYPE_pd | OP_R, ADDRMETH_V | OPTYPE_pd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "movntpd", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_P | OPTYPE_q | OP_R, ADDRMETH_W | OPTYPE_pd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "cvttpd2pi", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_P | OPTYPE_q | OP_R, ADDRMETH_W | OPTYPE_pd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "cvtpd2pi", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_sd | OP_R, ADDRMETH_W | OPTYPE_sd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "ucomisd", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_sd | OP_R, ADDRMETH_W | OPTYPE_sd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "comisd", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_G | OPTYPE_d | OP_R, ADDRMETH_W | OPTYPE_pd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "movmskpd", "", 0, 0, 0, 0, 0 }, + { 0, INS_FSQRT, 0, ADDRMETH_V | OPTYPE_pd | OP_R, ADDRMETH_W | OPTYPE_pd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "sqrtpd", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_AND, 0, ADDRMETH_V | OPTYPE_pd | OP_R, ADDRMETH_W | OPTYPE_pd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "andpd", "", 0, 0, 0, 0, 0 }, + { 0, INS_AND, 0, ADDRMETH_V | OPTYPE_pd | OP_R, ADDRMETH_W | OPTYPE_pd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "andnpd", "", 0, 0, 0, 0, 0 }, + { 0, INS_OR, 0, ADDRMETH_V | OPTYPE_pd | OP_R, ADDRMETH_W | OPTYPE_pd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "orpd", "", 0, 0, 0, 0, 0 }, + { 0, INS_XOR, 0, ADDRMETH_V | OPTYPE_pd | OP_R, ADDRMETH_W | OPTYPE_pd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "xorpd", "", 0, 0, 0, 0, 0 }, + { 0, INS_ADD, 0, ADDRMETH_V | OPTYPE_pd | OP_R, ADDRMETH_W | OPTYPE_pd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "addpd", "", 0, 0, 0, 0, 0 }, + { 0, INS_MUL, 0, ADDRMETH_V | OPTYPE_pd | OP_R, ADDRMETH_W | OPTYPE_pd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "mulpd", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_ps | OP_R, ADDRMETH_W | OPTYPE_pd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "cvtpd2ps", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_ps | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "cvtps2dq", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_pd | OP_R, ADDRMETH_W | OPTYPE_pd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "subpd", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_pd | OP_R, ADDRMETH_W | OPTYPE_pd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "minpd", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_pd | OP_R, ADDRMETH_W | OPTYPE_pd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "divpd", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_pd | OP_R, ADDRMETH_W | OPTYPE_pd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "maxpd", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "punpcklbw", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "punpcklwd", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "punpckldq", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "packsswb", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "pcmpgtb", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "pcmpgtw", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "pcmpgtd", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "packuswb", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "punpckhbw", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "punpckhwd", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "punpckhdq", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "packssdw", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "punpcklqdq", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "punpckhqdq", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_d | OP_R, ADDRMETH_E | OPTYPE_d | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "movd", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "movdqa", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, cpu_PENTIUM4 | isa_GP, "pshufd", "", 0, 0, 0, 0, 0 }, + { idx_660F71, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { idx_660F72, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { idx_660F73, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "pcmpeqb", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "pcmpeqw", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "pcmpeqd", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_pd | OP_R, ADDRMETH_W | OPTYPE_pd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "haddpd", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_pd | OP_R, ADDRMETH_W | OPTYPE_pd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "hsubpd", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_E | OPTYPE_d | OP_R, ADDRMETH_V | OPTYPE_d | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "movd", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_W | OPTYPE_dq | OP_R, ADDRMETH_V | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "movdqa", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_pd | OP_R, ADDRMETH_W | OPTYPE_pd | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, cpu_PENTIUM4 | isa_GP, "cmppd", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_w | OP_R, ADDRMETH_E | OPTYPE_d | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, cpu_PENTIUM4 | isa_GP, "pinsrw", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_G | OPTYPE_d | OP_R, ADDRMETH_W | OPTYPE_w | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, cpu_PENTIUM4 | isa_GP, "pextrw", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_pd | OP_R, ADDRMETH_W | OPTYPE_pd | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, cpu_PENTIUM4 | isa_GP, "shufpd", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_pd | OP_R, ADDRMETH_W | OPTYPE_pd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "addsubpd", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "psrlw", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "psrld", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "psrlq", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "paddq", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "pmullw", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_W | OPTYPE_q | OP_R, ADDRMETH_V | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "movq", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_G | OPTYPE_d | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "pmovmskb", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "psubusb", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "psubusw", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "pminub", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "pand", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "paddusb", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "paddusw", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "pmaxub", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "pandn", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "pavgb", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "psraw", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "psrad", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "pavgw", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "pmulhuw", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "pmulhw", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_pd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "cvttpd2dq", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_M | OPTYPE_dq | OP_R, ADDRMETH_V | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "movntdq", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "psubsb", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "psubsw", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "pminsw", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "por", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "paddsb", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "paddsw", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "pmaxsw", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "pxor", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "psllw", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "pslld", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "psllq", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "pmuludq", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "pmaddwd", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "psadbw", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "maskmovdqu", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "psubb", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "psubw", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "psubd", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "psubq", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "paddb", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "paddw", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "paddd", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 } +}; + + +static ia32_insn_t tbl_F20F[] = { /* SIMD F2 Two-byte Opcodes */ + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_sd | OP_R, ADDRMETH_W | OPTYPE_sd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "movsd", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_W | OPTYPE_sd | OP_R, ADDRMETH_V | OPTYPE_sd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "movsd", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_q | OP_R, ADDRMETH_W | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "movddup", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_sd | OP_R, ADDRMETH_E | OPTYPE_d | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "cvtsi2sd", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_G | OPTYPE_d | OP_R, ADDRMETH_W | OPTYPE_sd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "cvttsd2si", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_G | OPTYPE_d | OP_R, ADDRMETH_W | OPTYPE_sd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "cvtsd2si", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_sd | OP_R, ADDRMETH_W | OPTYPE_sd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "sqrtsd", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_sd | OP_R, ADDRMETH_W | OPTYPE_sd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "addsd", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_sd | OP_R, ADDRMETH_W | OPTYPE_sd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "mulsd", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_ss | OP_R, ADDRMETH_W | OPTYPE_sd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "cvtsd2ss", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_sd | OP_R, ADDRMETH_W | OPTYPE_sd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "subsd", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_sd | OP_R, ADDRMETH_W | OPTYPE_sd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "minsd", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_sd | OP_R, ADDRMETH_W | OPTYPE_sd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "divsd", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_sd | OP_R, ADDRMETH_W | OPTYPE_sd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "maxsd", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, cpu_PENTIUM4 | isa_GP, "pshuflw", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_ps | OP_R, ADDRMETH_W | OPTYPE_ps | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "haddps", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_ps | OP_R, ADDRMETH_W | OPTYPE_ps | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "hsubps", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_sd | OP_R, ADDRMETH_W | OPTYPE_sd | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, cpu_PENTIUM4 | isa_GP, "cmpsd", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_ps | OP_R, ADDRMETH_W | OPTYPE_ps | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "addsubps", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_P | OPTYPE_q | OP_R, ADDRMETH_W | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "movdq2q", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_pd | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "cvtpd2dq", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_M | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "lddqu", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 } +}; + + +static ia32_insn_t tbl_F30F[] = { /* SIMD F3 Two-byte Opcodes */ + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_ss | OP_R, ADDRMETH_W | OPTYPE_ss | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "movss", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_W | OPTYPE_ss | OP_R, ADDRMETH_V | OPTYPE_ss | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "movss", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_ps | OP_R, ADDRMETH_W | OPTYPE_ps | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "movsldup", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_ps | OP_R, ADDRMETH_W | OPTYPE_ps | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "movshdup", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_ss | OP_R, ADDRMETH_E | OPTYPE_d | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "cvtsi2ss", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_G | OPTYPE_d | OP_R, ADDRMETH_W | OPTYPE_ss | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "cvttss2si", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_G | OPTYPE_d | OP_R, ADDRMETH_W | OPTYPE_ss | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "cvtss2si", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_ss | OP_R, ADDRMETH_W | OPTYPE_ss | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "sqrtss", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_ss | OP_R, ADDRMETH_W | OPTYPE_ss | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "rsqrtss", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_ss | OP_R, ADDRMETH_W | OPTYPE_ss | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "rcpss", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_ss | OP_R, ADDRMETH_W | OPTYPE_ss | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "addss", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_ss | OP_R, ADDRMETH_W | OPTYPE_ss | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "mulss", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_sd | OP_R, ADDRMETH_W | OPTYPE_ss | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "cvtss2sd", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_ps | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "cvttps2dq", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_ss | OP_R, ADDRMETH_W | OPTYPE_ss | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "subss", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_ss | OP_R, ADDRMETH_W | OPTYPE_ss | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "minss", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_ss | OP_R, ADDRMETH_W | OPTYPE_ss | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "divss", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_ss | OP_R, ADDRMETH_W | OPTYPE_ss | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "maxss", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "movdqu", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_W | OPTYPE_dq | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, cpu_PENTIUM4 | isa_GP, "pshufhw", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_q | OP_R, ADDRMETH_W | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "movq", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_W | OPTYPE_dq | OP_R, ADDRMETH_V | OPTYPE_dq | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "movdqu", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_ss | OP_R, ADDRMETH_W | OPTYPE_ss | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, cpu_PENTIUM4 | isa_GP, "cmpss", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_dq | OP_R, ADDRMETH_Q | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "movq2dq", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_UNKNOWN, 0, ADDRMETH_V | OPTYPE_pd | OP_R, ADDRMETH_W | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM4 | isa_GP, "cvtdq2pd", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "", "", 0, 0, 0, 0, 0 } +}; + + +static ia32_insn_t tbl_0F00[] = { /* Group 6 */ + { 0, INS_SYSTEM, 0, ADDRMETH_E | OPTYPE_w | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "sldt", "", 0, 0, 0, 0 , 46 }, + { 0, INS_SYSTEM, 0, ADDRMETH_E | OPTYPE_w | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "str", "", 0, 0, 0, 0 , 49 }, + { 0, INS_SYSTEM, 0, ADDRMETH_E | OPTYPE_w | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "lldt", "", 0, 0, 0, 0 , 29 }, + { 0, INS_SYSTEM, 0, ADDRMETH_E | OPTYPE_w | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "ltr", "", 0, 0, 0, 0 , 32 }, + { 0, INS_SYSTEM, 0, ADDRMETH_E | OPTYPE_w | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "verr", "", 0, 0, 0, INS_SET_ZERO, 0 }, + { 0, INS_SYSTEM, 0, ADDRMETH_E | OPTYPE_w | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "verw", "", 0, 0, 0, INS_SET_ZERO, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 } +}; + + +static ia32_insn_t tbl_0F01[] = { /* Group 7 */ + { 0, INS_SYSTEM, 0, ADDRMETH_M | OPTYPE_s | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "sgdt", "", 0, 0, 0, 0 , 44 }, + { 0, INS_SYSTEM, 0, ADDRMETH_M | OPTYPE_s | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "sidt", "", 0, 0, 0, 0 , 45 }, + { 0, INS_SYSTEM, 0, ADDRMETH_M | OPTYPE_s | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "lgdt", "", 0, 0, 0, 0 , 27 }, + { 0, INS_SYSTEM, 0, ADDRMETH_M | OPTYPE_s | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "lidt", "", 0, 0, 0, 0 , 28 }, + { 0, INS_SYSTEM, 0, ADDRMETH_E | OPTYPE_w | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "smsw", "", 0, 0, 0, 0 , 47 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ADDRMETH_E | OPTYPE_w | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "lmsw", "", 0, 0, 0, 0 , 30 }, + { 0, INS_SYSTEM, 0, ADDRMETH_M | OPTYPE_none | OP_R, ARG_NONE, ARG_NONE, cpu_80486 | isa_GP, "invlpg", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ADDRMETH_M | OPTYPE_s | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "sgdt", "", 0, 0, 0, 0 , 44 }, + { 0, INS_SYSTEM, 0, ADDRMETH_M | OPTYPE_s | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "sidt", "", 0, 0, 0, 0 , 45 }, + { 0, INS_SYSTEM, 0, ADDRMETH_M | OPTYPE_s | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "lgdt", "", 0, 0, 0, 0 , 27 }, + { 0, INS_SYSTEM, 0, ADDRMETH_M | OPTYPE_s | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "lidt", "", 0, 0, 0, 0 , 28 }, + { 0, INS_SYSTEM, 0, ADDRMETH_E | OPTYPE_w | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "smsw", "", 0, 0, 0, 0 , 47 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ADDRMETH_E | OPTYPE_w | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "lmsw", "", 0, 0, 0, 0 , 30 }, + { 0, INS_SYSTEM, 0, ADDRMETH_M | OPTYPE_none | OP_R, ARG_NONE, ARG_NONE, cpu_80486 | isa_GP, "invlpg", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ADDRMETH_M | OPTYPE_s | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "sgdt", "", 0, 0, 0, 0 , 44 }, + { 0, INS_SYSTEM, 0, ADDRMETH_M | OPTYPE_s | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "sidt", "", 0, 0, 0, 0 , 45 }, + { 0, INS_SYSTEM, 0, ADDRMETH_M | OPTYPE_s | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "lgdt", "", 0, 0, 0, 0 , 27 }, + { 0, INS_SYSTEM, 0, ADDRMETH_M | OPTYPE_s | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "lidt", "", 0, 0, 0, 0 , 28 }, + { 0, INS_SYSTEM, 0, ADDRMETH_E | OPTYPE_w | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "smsw", "", 0, 0, 0, 0 , 47 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ADDRMETH_E | OPTYPE_w | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "lmsw", "", 0, 0, 0, 0 , 30 }, + { 0, INS_SYSTEM, 0, ADDRMETH_M | OPTYPE_none | OP_R, ARG_NONE, ARG_NONE, cpu_80486 | isa_GP, "invlpg", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { idx_0F0111, 0, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ADDRMETH_E | OPTYPE_w | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "smsw", "", 0, 0, 0, 0 , 47 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ADDRMETH_E | OPTYPE_w | OP_W, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "lmsw", "", 0, 0, 0, 0 , 30 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 } +}; + + +static ia32_insn_t tbl_0F0111[] = { /* Monitor/MWait opcode */ + { 0, INS_SYSTEM, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "monitor", "", 0, 0, 0, 0, 54 }, + { 0, INS_SYSTEM, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "mwait", "", 0, 0, 0, 0, 55 } +}; + + +static ia32_insn_t tbl_0F12[] = { /* Movlps Opcode */ + { 0, INS_MOV, INS_NOTE_NOSUFFIX, ADDRMETH_V | OPTYPE_q | OP_W, ADDRMETH_M | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "movlps", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, INS_NOTE_NOSUFFIX, ADDRMETH_V | OPTYPE_q | OP_W, ADDRMETH_M | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "movlps", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, INS_NOTE_NOSUFFIX, ADDRMETH_V | OPTYPE_q | OP_W, ADDRMETH_M | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "movlps", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_V | OPTYPE_ps | OP_R | OP_W, ADDRMETH_W | OPTYPE_ps | OP_R , ARG_NONE, cpu_PENTIUM4 | isa_GP, "movhlps", "", 0, 0, 0, 0, 0 } +}; + + +static ia32_insn_t tbl_0F16[] = { /* Movhps Opcode */ + { 0, INS_OTHER, 0, ADDRMETH_V | OPTYPE_q | OP_W, ADDRMETH_M | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "movhps", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_V | OPTYPE_q | OP_W, ADDRMETH_M | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "movhps", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_V | OPTYPE_q | OP_W, ADDRMETH_M | OPTYPE_q | OP_R, ARG_NONE, cpu_PENTIUM2 | isa_GP, "movhps", "", 0, 0, 0, 0 , 0 }, + { 0, INS_MOV, 0, ADDRMETH_V | OPTYPE_ps | OP_R | OP_W, ADDRMETH_W | OPTYPE_ps | OP_R , ARG_NONE, cpu_PENTIUM4 | isa_GP, "movlhps", "", 0, 0, 0, 0, 0 } +}; + + +static ia32_insn_t tbl_0F18[] = { /* Group 16 */ + { 0, INS_SYSTEM, 0, OP_W | OPTYPE_b | ADDRMETH_M, ARG_NONE, ARG_NONE, cpu_PENTIUM2 | isa_GP, "prefetchnta", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ADDRMETH_RT | OPTYPE_d | OP_W, ARG_NONE, ARG_NONE, cpu_PENTIUM2 | isa_GP, "prefetcht0", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ADDRMETH_RT | OPTYPE_d | OP_W, ARG_NONE, ARG_NONE, cpu_PENTIUM2 | isa_GP, "prefetcht1", "", 1, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ADDRMETH_RT | OPTYPE_d | OP_W, ARG_NONE, ARG_NONE, cpu_PENTIUM2 | isa_GP, "prefetcht2", "", 2, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, OP_W | OPTYPE_b | ADDRMETH_M, ARG_NONE, ARG_NONE, cpu_PENTIUM2 | isa_GP, "prefetchnta", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ADDRMETH_RT | OPTYPE_d | OP_W, ARG_NONE, ARG_NONE, cpu_PENTIUM2 | isa_GP, "prefetcht0", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ADDRMETH_RT | OPTYPE_d | OP_W, ARG_NONE, ARG_NONE, cpu_PENTIUM2 | isa_GP, "prefetcht1", "", 1, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ADDRMETH_RT | OPTYPE_d | OP_W, ARG_NONE, ARG_NONE, cpu_PENTIUM2 | isa_GP, "prefetcht2", "", 2, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, OP_W | OPTYPE_b | ADDRMETH_M, ARG_NONE, ARG_NONE, cpu_PENTIUM2 | isa_GP, "prefetchnta", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ADDRMETH_RT | OPTYPE_d | OP_W, ARG_NONE, ARG_NONE, cpu_PENTIUM2 | isa_GP, "prefetcht0", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ADDRMETH_RT | OPTYPE_d | OP_W, ARG_NONE, ARG_NONE, cpu_PENTIUM2 | isa_GP, "prefetcht1", "", 1, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ADDRMETH_RT | OPTYPE_d | OP_W, ARG_NONE, ARG_NONE, cpu_PENTIUM2 | isa_GP, "prefetcht2", "", 2, 0, 0, 0 , 0 } +}; + + +static ia32_insn_t tbl_0F71[] = { /* Group 12 */ + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "psrlw", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "psraw", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "psllw", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 } +}; + + +static ia32_insn_t tbl_660F71[] = { /* Group 12 SSE */ + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_dq | OP_W, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "psrlw", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_dq | OP_W, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "psraw", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_dq | OP_W, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "psllw", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 } +}; + + +static ia32_insn_t tbl_0F72[] = { /* Group 13 */ + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "psrld", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "psrad", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "pslld", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 } +}; + + +static ia32_insn_t tbl_660F72[] = { /* Group 13 SSE */ + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_W | OPTYPE_dq | OP_W, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "psrld", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_W | OPTYPE_dq | OP_W, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "psrad", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_W | OPTYPE_dq | OP_W, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "pslld", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 } +}; + + +static ia32_insn_t tbl_0F73[] = { /* Group 14 */ + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "psrlq", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_P | OPTYPE_q | OP_W, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "psllq", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 } +}; + + +static ia32_insn_t tbl_660F73[] = { /* Group 14 SSE */ + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_W | OPTYPE_dq | OP_W, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "psrlq", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_W | OPTYPE_dq | OP_W, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "psrldq", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_W | OPTYPE_dq | OP_W, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "psllq", "", 0, 0, 0, 0 , 0 }, + { 0, INS_OTHER, 0, ADDRMETH_W | OPTYPE_dq | OP_W, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_PENTIUM | isa_MMX, "pslldq", "", 0, 0, 0, 0 , 0 } +}; + + +static ia32_insn_t tbl_0FAE[] = { /* Group 15 */ + { 0, INS_FPU, 0, ADDRMETH_E | OPTYPE_fx | OP_W, ARG_NONE, ARG_NONE, cpu_PENTIUM | isa_MMX, "fxsave", "", 0, 0, 0, 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_E | OPTYPE_fx | OP_R, ARG_NONE, ARG_NONE, cpu_PENTIUM | isa_MMX, "fxrstor", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ADDRMETH_E | OPTYPE_d | OP_R, ARG_NONE, ARG_NONE, cpu_PENTIUM2, "ldmxcsr", "", 0, 0, 0, 0 , 25 }, + { 0, INS_SYSTEM, 0, ADDRMETH_E | OPTYPE_d | OP_W, ARG_NONE, ARG_NONE, cpu_PENTIUM2, "stmxcsr", "", 0, 0, 0 , 0, 48 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ADDRMETH_M | OPTYPE_b | OP_R, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "clflush", "", 0, 0, 0, 0, 0 }, + { 0, INS_FPU, 0, ADDRMETH_E | OPTYPE_fx | OP_W, ARG_NONE, ARG_NONE, cpu_PENTIUM | isa_MMX, "fxsave", "", 0, 0, 0, 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_E | OPTYPE_fx | OP_R, ARG_NONE, ARG_NONE, cpu_PENTIUM | isa_MMX, "fxrstor", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ADDRMETH_E | OPTYPE_d | OP_R, ARG_NONE, ARG_NONE, cpu_PENTIUM2, "ldmxcsr", "", 0, 0, 0, 0 , 25 }, + { 0, INS_SYSTEM, 0, ADDRMETH_E | OPTYPE_d | OP_W, ARG_NONE, ARG_NONE, cpu_PENTIUM2, "stmxcsr", "", 0, 0, 0 , 0, 48 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ADDRMETH_M | OPTYPE_b | OP_R, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "clflush", "", 0, 0, 0, 0, 0 }, + { 0, INS_FPU, 0, ADDRMETH_E | OPTYPE_fx | OP_W, ARG_NONE, ARG_NONE, cpu_PENTIUM | isa_MMX, "fxsave", "", 0, 0, 0, 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_E | OPTYPE_fx | OP_R, ARG_NONE, ARG_NONE, cpu_PENTIUM | isa_MMX, "fxrstor", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ADDRMETH_E | OPTYPE_d | OP_R, ARG_NONE, ARG_NONE, cpu_PENTIUM2, "ldmxcsr", "", 0, 0, 0, 0 , 25 }, + { 0, INS_SYSTEM, 0, ADDRMETH_E | OPTYPE_d | OP_W, ARG_NONE, ARG_NONE, cpu_PENTIUM2, "stmxcsr", "", 0, 0, 0 , 0, 48 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ADDRMETH_M | OPTYPE_b | OP_R, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "clflush", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "lfence", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "mfence", "", 0, 0, 0, 0 , 0 }, + { 0, INS_SYSTEM, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "sfence", "", 0, 0, 0, 0 , 0 } +}; + + +static ia32_insn_t tbl_0FBA[] = { /* Group 8 */ + { 0, INS_BITTEST, 0, ADDRMETH_E | OPTYPE_v | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "bt", "", 0, 0, 0, INS_SET_CARRY, 0 }, + { 0, INS_BITTEST, 0, ADDRMETH_E | OPTYPE_v | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "bts", "", 0, 0, 0, INS_SET_CARRY, 0 }, + { 0, INS_BITTEST, 0, ADDRMETH_E | OPTYPE_v | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "btr", "", 0, 0, 0, INS_SET_CARRY, 0 }, + { 0, INS_BITTEST, 0, ADDRMETH_E | OPTYPE_v | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "btc", "", 0, 0, 0 , INS_SET_CARRY, 0 } +}; + + +static ia32_insn_t tbl_0FC7[] = { /* Group 9 */ + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_XCHGCC, 0, ADDRMETH_M | OPTYPE_q | OP_W, ARG_NONE, ARG_NONE, cpu_PENTIUM | isa_GP, "cmpxch8b", "", 0, 0, 0 , 0 , 9 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_XCHGCC, 0, ADDRMETH_M | OPTYPE_q | OP_W, ARG_NONE, ARG_NONE, cpu_PENTIUM | isa_GP, "cmpxch8b", "", 0, 0, 0 , 0 , 9 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "", "", 0, 0, 0, 0 , 0 }, + { 0, INS_XCHGCC, 0, ADDRMETH_M | OPTYPE_q | OP_W, ARG_NONE, ARG_NONE, cpu_PENTIUM | isa_GP, "cmpxch8b", "", 0, 0, 0 , 0 , 9 } +}; + + +static ia32_insn_t tbl_0FB9[] = { /* Group 10 */ + { 0, INS_SYSTEM, 0, ARG_NONE, ARG_NONE, ARG_NONE, 0, "fxsave", "", 0, 0, 0, 0 , 0 } +}; + + +static ia32_insn_t tbl_C6[] = { /* Group 11a */ + { 0, INS_MOV, 0, ADDRMETH_E | OPTYPE_b | OP_W, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 0, 0, 0, 0 , 0 } +}; + + +static ia32_insn_t tbl_C7[] = { /* Group 11b */ + { 0, INS_MOV, 0, ADDRMETH_E | OPTYPE_v | OP_W, ADDRMETH_I | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mov", "", 0, 0, 0, 0 , 0 } +}; + + +static ia32_insn_t tbl_80[] = { /* Group 1a */ + { 0, INS_ADD, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "add", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_OR, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "or", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_ADD, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "adc", "", 0, 0, 0, INS_SET_ALL|INS_TEST_CARRY, 0 }, + { 0, INS_SUB, 0, ADDRMETH_E | OPTYPE_b | OP_SIGNED | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sbb", "", 0, 0, 0, INS_SET_ALL|INS_TEST_CARRY, 0 }, + { 0, INS_AND, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "and", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_SUB, 0, ADDRMETH_E | OPTYPE_b | OP_SIGNED | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sub", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_XOR, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "xor", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_CMP, 0, ADDRMETH_E | OPTYPE_b | OP_R, ADDRMETH_I | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "cmp", "", 0, 0, 0 , INS_SET_ALL, 0 } +}; + + +static ia32_insn_t tbl_81[] = { /* Group 1b */ + { 0, INS_ADD, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_I | OPTYPE_v | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "add", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_OR, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_I | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "or", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_ADD, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_I | OPTYPE_v | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "adc", "", 0, 0, 0, INS_SET_ALL|INS_TEST_CARRY, 0 }, + { 0, INS_SUB, 0, ADDRMETH_E | OPTYPE_v | OP_SIGNED | OP_W | OP_R, ADDRMETH_I | OPTYPE_v | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sbb", "", 0, 0, 0, INS_SET_ALL|INS_TEST_CARRY, 0 }, + { 0, INS_AND, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_I | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "and", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_SUB, 0, ADDRMETH_E | OPTYPE_v | OP_SIGNED | OP_W | OP_R, ADDRMETH_I | OPTYPE_v | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sub", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_XOR, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_I | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "xor", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_CMP, 0, ADDRMETH_E | OPTYPE_v | OP_R, ADDRMETH_I | OPTYPE_v | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "cmp", "", 0, 0, 0 , INS_SET_ALL, 0 } +}; + + +static ia32_insn_t tbl_82[] = { /* Group 1c */ + { 0, INS_ADD, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "add", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_OR, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "or", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_ADD, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "adc", "", 0, 0, 0, INS_SET_ALL|INS_TEST_CARRY, 0 }, + { 0, INS_SUB, 0, ADDRMETH_E | OPTYPE_b | OP_SIGNED | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sbb", "", 0, 0, 0, INS_SET_ALL|INS_TEST_CARRY, 0 }, + { 0, INS_AND, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "and", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_SUB, 0, ADDRMETH_E | OPTYPE_b | OP_SIGNED | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sub", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_XOR, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "xor", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_CMP, 0, ADDRMETH_E | OPTYPE_b | OP_R, ADDRMETH_I | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "cmp", "", 0, 0, 0 , INS_SET_ALL, 0 } +}; + + +static ia32_insn_t tbl_83[] = { /* Group 1d */ + { 0, INS_ADD, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "add", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_OR, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "or", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_ADD, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "adc", "", 0, 0, 0, INS_SET_ALL|INS_TEST_CARRY, 0 }, + { 0, INS_SUB, 0, ADDRMETH_E | OPTYPE_v | OP_SIGNED | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sbb", "", 0, 0, 0, INS_SET_ALL|INS_TEST_CARRY, 0 }, + { 0, INS_AND, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "and", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_SUB, 0, ADDRMETH_E | OPTYPE_v | OP_SIGNED | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sub", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_XOR, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "xor", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_CMP, 0, ADDRMETH_E | OPTYPE_v | OP_R, ADDRMETH_I | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "cmp", "", 0, 0, 0 , INS_SET_ALL, 0 } +}; + + +static ia32_insn_t tbl_C0[] = { /* Group 2a */ + { 0, INS_ROL, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "rol", "", 0, 0, 0, INS_SET_CARRY|INS_SET_OFLOW, 0 }, + { 0, INS_ROR, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "ror", "", 0, 0, 0, INS_SET_CARRY|INS_SET_OFLOW, 0 }, + { 0, INS_ROL, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "rcl", "", 0, 0, 0, INS_SET_CARRY|INS_SET_OFLOW|INS_TEST_CARRY, 0 }, + { 0, INS_ROR, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "rcr", "", 0, 0, 0, INS_SET_CARRY|INS_SET_OFLOW|INS_TEST_CARRY, 0 }, + { 0, INS_SHL, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "shl", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_SHR, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "shr", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_SHL, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sal", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_SHR, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sar", "", 0, 0, 0 , INS_SET_ALL, 0 } +}; + + +static ia32_insn_t tbl_C1[] = { /* Group 2b */ + { 0, INS_ROL, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "rol", "", 0, 0, 0, INS_SET_CARRY|INS_SET_OFLOW, 0 }, + { 0, INS_ROR, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "ror", "", 0, 0, 0, INS_SET_CARRY|INS_SET_OFLOW, 0 }, + { 0, INS_ROL, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "rcl", "", 0, 0, 0, INS_SET_CARRY|INS_SET_OFLOW|INS_TEST_CARRY, 0 }, + { 0, INS_ROR, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "rcr", "", 0, 0, 0, INS_SET_CARRY|INS_SET_OFLOW|INS_TEST_CARRY, 0 }, + { 0, INS_SHL, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "shl", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_SHR, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "shr", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_SHL, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sal", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_SHR, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_I | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sar", "", 0, 0, 0 , INS_SET_ALL, 0 } +}; + + +static ia32_insn_t tbl_D0[] = { /* Group 2c */ + { 0, INS_ROL, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_II | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "rol", "", 0, 1, 0, INS_SET_CARRY|INS_SET_OFLOW, 0 }, + { 0, INS_ROR, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_II | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "ror", "", 0, 1, 0, INS_SET_CARRY|INS_SET_OFLOW, 0 }, + { 0, INS_ROL, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_II | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "rcl", "", 0, 1, 0, INS_SET_CARRY|INS_SET_OFLOW|INS_TEST_CARRY, 0 }, + { 0, INS_ROR, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_II | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "rcr", "", 0, 1, 0, INS_SET_CARRY|INS_SET_OFLOW|INS_TEST_CARRY, 0 }, + { 0, INS_SHL, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_II | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "shl", "", 0, 1, 0, INS_SET_ALL, 0 }, + { 0, INS_SHR, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_II | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "shr", "", 0, 1, 0, INS_SET_ALL, 0 }, + { 0, INS_SHL, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_II | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sal", "", 0, 1, 0, INS_SET_ALL, 0 }, + { 0, INS_SHR, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_II | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sar", "", 0, 1, 0 , INS_SET_ALL, 0 } +}; + + +static ia32_insn_t tbl_D1[] = { /* Group 2d */ + { 0, INS_ROL, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_II | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "rol", "", 0, 1, 0, INS_SET_CARRY|INS_SET_OFLOW, 0 }, + { 0, INS_ROR, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_II | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "ror", "", 0, 1, 0, INS_SET_CARRY|INS_SET_OFLOW, 0 }, + { 0, INS_ROL, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_II | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "rcl", "", 0, 1, 0, INS_SET_CARRY|INS_SET_OFLOW|INS_TEST_CARRY, 0 }, + { 0, INS_ROR, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_II | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "rcr", "", 0, 1, 0, INS_SET_CARRY|INS_SET_OFLOW|INS_TEST_CARRY, 0 }, + { 0, INS_SHL, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_II | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "shl", "", 0, 1, 0, INS_SET_ALL, 0 }, + { 0, INS_SHR, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_II | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "shr", "", 0, 1, 0, INS_SET_ALL, 0 }, + { 0, INS_SHL, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_II | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sal", "", 0, 1, 0, INS_SET_ALL, 0 }, + { 0, INS_SHR, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_II | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sar", "", 0, 1, 0 , INS_SET_ALL, 0 } +}; + + +static ia32_insn_t tbl_D2[] = { /* Group 2e */ + { 0, INS_ROL, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_RR | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "rol", "", 0, 1, 0, INS_SET_CARRY|INS_SET_OFLOW, 0 }, + { 0, INS_ROR, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_RR | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "ror", "", 0, 1, 0, INS_SET_CARRY|INS_SET_OFLOW, 0 }, + { 0, INS_ROL, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_RR | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "rcl", "", 0, 1, 0, INS_SET_CARRY|INS_SET_OFLOW|INS_TEST_CARRY, 0 }, + { 0, INS_ROR, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_RR | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "rcr", "", 0, 1, 0, INS_SET_CARRY|INS_SET_OFLOW|INS_TEST_CARRY, 0 }, + { 0, INS_SHL, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_RR | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "shl", "", 0, 1, 0, INS_SET_ALL, 0 }, + { 0, INS_SHR, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_RR | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "shr", "", 0, 1, 0, INS_SET_ALL, 0 }, + { 0, INS_SHL, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_RR | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sal", "", 0, 1, 0, INS_SET_ALL, 0 }, + { 0, INS_SHR, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ADDRMETH_RR | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sar", "", 0, 1, 0 , INS_SET_ALL, 0 } +}; + + +static ia32_insn_t tbl_D3[] = { /* Group 2f */ + { 0, INS_ROL, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_RR | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "rol", "", 0, 1, 0, INS_SET_CARRY|INS_SET_OFLOW, 0 }, + { 0, INS_ROR, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_RR | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "ror", "", 0, 1, 0, INS_SET_CARRY|INS_SET_OFLOW, 0 }, + { 0, INS_ROL, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_RR | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "rcl", "", 0, 1, 0, INS_SET_CARRY|INS_SET_OFLOW|INS_TEST_CARRY, 0 }, + { 0, INS_ROR, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_RR | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "rcr", "", 0, 1, 0, INS_SET_CARRY|INS_SET_OFLOW|INS_TEST_CARRY, 0 }, + { 0, INS_SHL, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_RR | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "shl", "", 0, 1, 0, INS_SET_ALL, 0 }, + { 0, INS_SHR, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_RR | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "shr", "", 0, 1, 0, INS_SET_ALL, 0 }, + { 0, INS_SHL, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_RR | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sal", "", 0, 1, 0, INS_SET_ALL, 0 }, + { 0, INS_SHR, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ADDRMETH_RR | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "sar", "", 0, 1, 0 , INS_SET_ALL, 0 } +}; + + +static ia32_insn_t tbl_F6[] = { /* Group 3a */ + { 0, INS_TEST, 0, ADDRMETH_E | OPTYPE_b | OP_R, ADDRMETH_I | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "test", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_TEST, 0, ADDRMETH_E | OPTYPE_b | OP_R, ADDRMETH_I | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "test", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_NOT, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "not", "", 0, 0, 0, 0 , 0 }, + { 0, INS_NEG, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "neg", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_MUL, 0, OPTYPE_b | ADDRMETH_RR | OP_W | OP_R, ADDRMETH_E | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mul", "", 0, 0, 0, INS_SET_OFLOW|INS_SET_CARRY, 22 }, + { 0, INS_MUL, 0, OPTYPE_b | ADDRMETH_RR | OP_W | OP_SIGNED | OP_R, ADDRMETH_E | OPTYPE_b | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "imul", "", 0, 0, 0, INS_SET_OFLOW|INS_SET_CARRY, 22 }, + { 0, INS_DIV, 0, ADDRMETH_RR | OPTYPE_b | OP_W | OP_R, ADDRMETH_E | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "div", "", 0, 0, 0, 0 , 13 }, + { 0, INS_DIV, 0, ADDRMETH_RR | OPTYPE_b | OP_W | OP_R, ADDRMETH_E | OPTYPE_b | OP_R, ARG_NONE, cpu_80386 | isa_GP, "idiv", "", 0, 0, 0 , 0 , 13 } +}; + + +static ia32_insn_t tbl_F7[] = { /* Group 3b */ + { 0, INS_TEST, 0, ADDRMETH_E | OPTYPE_v | OP_R, ADDRMETH_I | OPTYPE_v | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "test", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_TEST, 0, ADDRMETH_E | OPTYPE_v | OP_R, ADDRMETH_I | OPTYPE_v | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "test", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_NOT, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "not", "", 0, 0, 0, 0 , 0 }, + { 0, INS_NEG, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "neg", "", 0, 0, 0, INS_SET_ALL, 0 }, + { 0, INS_MUL, 0, ADDRMETH_RR | OPTYPE_v | OP_W | OP_R, ADDRMETH_E | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "mul", "", 0, 0, 0, INS_SET_OFLOW|INS_SET_CARRY, 23 }, + { 0, INS_MUL, 0, ADDRMETH_RR | OPTYPE_v | OP_SIGNED | OP_W | OP_R, ADDRMETH_E | OPTYPE_v | OP_SIGNED | OP_R, ARG_NONE, cpu_80386 | isa_GP, "imul", "", 0, 0, 0, INS_SET_OFLOW|INS_SET_CARRY, 23 }, + { 0, INS_DIV, 0, ADDRMETH_RR | OPTYPE_v | OP_W | OP_R, ADDRMETH_E | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "div", "", 0, 0, 0, 0 , 14 }, + { 0, INS_DIV, 0, ADDRMETH_RR | OPTYPE_v | OP_W | OP_R, ADDRMETH_E | OPTYPE_v | OP_R, ARG_NONE, cpu_80386 | isa_GP, "idiv", "", 0, 0, 0, 0 , 14 } +}; + + +static ia32_insn_t tbl_FE[] = { /* Group 4 */ + { 0, INS_INC, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "inc", "", 0, 0, 0, INS_SET_OFLOW|INS_SET_SIGN|INS_SET_ZERO|INS_SET_PARITY, 0 }, + { 0, INS_DEC, 0, ADDRMETH_E | OPTYPE_b | OP_W | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "dec", "", 0, 0, 0 , INS_SET_OFLOW|INS_SET_SIGN|INS_SET_ZERO|INS_SET_PARITY, 0 } +}; + + +static ia32_insn_t tbl_FF[] = { /* Group 5 */ + { 0, INS_INC, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "inc", "", 0, 0, 0, INS_SET_OFLOW|INS_SET_SIGN|INS_SET_ZERO|INS_SET_PARITY, 0 }, + { 0, INS_DEC, 0, ADDRMETH_E | OPTYPE_v | OP_W | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "dec", "", 0, 0, 0, INS_SET_OFLOW|INS_SET_SIGN|INS_SET_ZERO|INS_SET_PARITY, 0 }, + { 0, INS_CALL, 0, ADDRMETH_E | OPTYPE_v | OP_X, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "call", "", 0, 0, 0, 0 , 3 }, + { 0, INS_CALL, 0, ADDRMETH_M | OPTYPE_p | OP_X, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "call", "", 0, 0, 0, 0 , 0 }, + { 0, INS_BRANCH, 0, ADDRMETH_E | OPTYPE_v | OP_X, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jmp", "", 0, 0, 0, 0 , 0 }, + { 0, INS_BRANCH, 0, ADDRMETH_M | OPTYPE_p | OP_X, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "jmp", "", 0, 0, 0, 0 , 0 }, + { 0, INS_PUSH, 0, ADDRMETH_E | OPTYPE_v | OP_R, ARG_NONE, ARG_NONE, cpu_80386 | isa_GP, "push", "", 0, 0, 0, 0 , 33 } +}; + + +static ia32_insn_t tbl_D8[] = { /* FPU D8 */ + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_fs|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fadd", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_fs|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fmul", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_fs|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fcom", "", 0, 0, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 17 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_fs|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fcomp", "", 0, 0, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_fs|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fsub", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_fs|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fsubr", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_fs|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fdiv", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_fs|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fdivr", "", 0, 0, 0 , 0 , 0 } +}; + + +static ia32_insn_t tbl_D8C0[] = { /* FPU D8 C0 */ + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fadd", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fadd", "", 0, 1, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fadd", "", 0, 2, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fadd", "", 0, 3, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fadd", "", 0, 4, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fadd", "", 0, 5, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fadd", "", 0, 6, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fadd", "", 0, 7, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fmul", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fmul", "", 0, 1, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fmul", "", 0, 2, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fmul", "", 0, 3, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fmul", "", 0, 4, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fmul", "", 0, 5, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fmul", "", 0, 6, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fmul", "", 0, 7, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcom", "", 0, 0, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 17 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcom", "", 0, 1, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 17 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcom", "", 0, 2, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 17 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcom", "", 0, 3, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 17 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcom", "", 0, 4, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 17 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcom", "", 0, 5, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 17 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcom", "", 0, 6, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 17 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcom", "", 0, 7, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 17 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcomp", "", 0, 0, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcomp", "", 0, 1, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcomp", "", 0, 2, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcomp", "", 0, 3, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcomp", "", 0, 4, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcomp", "", 0, 5, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcomp", "", 0, 6, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcomp", "", 0, 7, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsub", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsub", "", 0, 1, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsub", "", 0, 2, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsub", "", 0, 3, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsub", "", 0, 4, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsub", "", 0, 5, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsub", "", 0, 6, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsub", "", 0, 7, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubr", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubr", "", 0, 1, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubr", "", 0, 2, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubr", "", 0, 3, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubr", "", 0, 4, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubr", "", 0, 5, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubr", "", 0, 6, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubr", "", 0, 7, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdiv", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdiv", "", 0, 1, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdiv", "", 0, 2, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdiv", "", 0, 3, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdiv", "", 0, 4, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdiv", "", 0, 5, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdiv", "", 0, 6, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdiv", "", 0, 7, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivr", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivr", "", 0, 1, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivr", "", 0, 2, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivr", "", 0, 3, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivr", "", 0, 4, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivr", "", 0, 5, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivr", "", 0, 6, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivr", "", 0, 7, 0 , 0 , 0 } +}; + + +static ia32_insn_t tbl_D9[] = { /* FPU D9 */ + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_fs|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fld", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_fs|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fst", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_fs|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fstp", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_fv|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fldenv", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_w|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fldcw", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_fv|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fnstenv", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_w|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fnstcw", "", 0, 0, 0 , 0 , 0 } +}; + + +static ia32_insn_t tbl_D9C0[] = { /* FPU D9 C0 */ + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fld", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fld", "", 0, 1, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fld", "", 0, 2, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fld", "", 0, 3, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fld", "", 0, 4, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fld", "", 0, 5, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fld", "", 0, 6, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fld", "", 0, 7, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fxch", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fxch", "", 0, 1, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fxch", "", 0, 2, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fxch", "", 0, 3, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fxch", "", 0, 4, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fxch", "", 0, 5, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fxch", "", 0, 6, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fxch", "", 0, 7, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fnop", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fchs", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fabs", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "ftst", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fxam", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fld1", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fldl2t", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fldl2e", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fldpi", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fldlg2", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fldln2", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fldz", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "f2xm1", "", 0, 0, 0 , 0 , 16 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fyl2x", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fptan", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fpatan", "", 0, 0, 0 , 0 , 18 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fxtract", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fprem1", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fdecstp", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fincstp", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fprem", "", 0, 0, 0 , 0 , 19 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fyl2xp1", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fsqrt", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fsincos", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "frndint", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fscale", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fsin", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fcos", "", 0, 0, 0 , 0 , 0 } +}; + + +static ia32_insn_t tbl_DA[] = { /* FPU DA */ + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_d|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fiadd", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_d|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fimul", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_d|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "ficom", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_d|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "ficomp", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_d|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fisub", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_d|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fisubr", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_d|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fidiv", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_d|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fidivr", "", 0, 0, 0 , 0 , 0 } +}; + + +static ia32_insn_t tbl_DAC0[] = { /* FPU DA C0 */ + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmovb", "", 0, 0, 0 , INS_TEST_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmovb", "", 0, 1, 0 , INS_TEST_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmovb", "", 0, 2, 0 , INS_TEST_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmovb", "", 0, 3, 0 , INS_TEST_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmovb", "", 0, 4, 0 , INS_TEST_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmovb", "", 0, 5, 0 , INS_TEST_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmovb", "", 0, 6, 0 , INS_TEST_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmovb", "", 0, 7, 0 , INS_TEST_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmove", "", 0, 0, 0 , INS_TEST_ZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmove", "", 0, 1, 0 , INS_TEST_ZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmove", "", 0, 2, 0 , INS_TEST_ZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmove", "", 0, 3, 0 , INS_TEST_ZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmove", "", 0, 4, 0 , INS_TEST_ZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmove", "", 0, 5, 0 , INS_TEST_ZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmove", "", 0, 6, 0 , INS_TEST_ZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmove", "", 0, 7, 0 , INS_TEST_ZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmovbe", "", 0, 0, 0 , INS_TEST_CARRY|INS_TEST_OR|INS_TEST_ZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmovbe", "", 0, 1, 0 , INS_TEST_CARRY|INS_TEST_OR|INS_TEST_ZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmovbe", "", 0, 2, 0 , INS_TEST_CARRY|INS_TEST_OR|INS_TEST_ZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmovbe", "", 0, 3, 0 , INS_TEST_CARRY|INS_TEST_OR|INS_TEST_ZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmovbe", "", 0, 4, 0 , INS_TEST_CARRY|INS_TEST_OR|INS_TEST_ZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmovbe", "", 0, 5, 0 , INS_TEST_CARRY|INS_TEST_OR|INS_TEST_ZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmovbe", "", 0, 6, 0 , INS_TEST_CARRY|INS_TEST_OR|INS_TEST_ZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmovbe", "", 0, 7, 0 , INS_TEST_CARRY|INS_TEST_OR|INS_TEST_ZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmovu", "", 0, 0, 0 , INS_TEST_PARITY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmovu", "", 0, 1, 0 , INS_TEST_PARITY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmovu", "", 0, 2, 0 , INS_TEST_PARITY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmovu", "", 0, 3, 0 , INS_TEST_PARITY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmovu", "", 0, 4, 0 , INS_TEST_PARITY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmovu", "", 0, 5, 0 , INS_TEST_PARITY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmovu", "", 0, 6, 0 , INS_TEST_PARITY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcmovu", "", 0, 7, 0 , INS_TEST_PARITY, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fucompp", "", 0, 0, 0 , 0 , 21 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 } +}; + + +static ia32_insn_t tbl_DB[] = { /* FPU DB */ + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_d|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fild", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_d|OP_W, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "fisttp", "", 0, 0, 0, 0, 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_d|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fist", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_d|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fistp", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_fe|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fld", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_fe|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fstp", "", 0, 0, 0 , 0 , 0 } +}; + + +static ia32_insn_t tbl_DBC0[] = { /* FPU DB C0 */ + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovnb", "", 0, 0, 0 , INS_TEST_NCARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovnb", "", 0, 1, 0 , INS_TEST_NCARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovnb", "", 0, 2, 0 , INS_TEST_NCARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovnb", "", 0, 3, 0 , INS_TEST_NCARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovnb", "", 0, 4, 0 , INS_TEST_NCARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovnb", "", 0, 5, 0 , INS_TEST_NCARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovnb", "", 0, 6, 0 , INS_TEST_NCARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovnb", "", 0, 7, 0 , INS_TEST_NCARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovne", "", 0, 0, 0 , INS_TEST_NZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovne", "", 0, 1, 0 , INS_TEST_NZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovne", "", 0, 2, 0 , INS_TEST_NZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovne", "", 0, 3, 0 , INS_TEST_NZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovne", "", 0, 4, 0 , INS_TEST_NZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovne", "", 0, 5, 0 , INS_TEST_NZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovne", "", 0, 6, 0 , INS_TEST_NZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovne", "", 0, 7, 0 , INS_TEST_NZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovnbe", "", 0, 0, 0 , INS_TEST_NCARRY|INS_TEST_NZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovnbe", "", 0, 1, 0 , INS_TEST_NCARRY|INS_TEST_NZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovnbe", "", 0, 2, 0 , INS_TEST_NCARRY|INS_TEST_NZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovnbe", "", 0, 3, 0 , INS_TEST_NCARRY|INS_TEST_NZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovnbe", "", 0, 4, 0 , INS_TEST_NCARRY|INS_TEST_NZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovnbe", "", 0, 5, 0 , INS_TEST_NCARRY|INS_TEST_NZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovnbe", "", 0, 6, 0 , INS_TEST_NCARRY|INS_TEST_NZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovnbe", "", 0, 7, 0 , INS_TEST_NCARRY|INS_TEST_NZERO, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovnu", "", 0, 0, 0 , INS_TEST_NPARITY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovnu", "", 0, 1, 0 , INS_TEST_NPARITY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovnu", "", 0, 2, 0 , INS_TEST_NPARITY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovnu", "", 0, 3, 0 , INS_TEST_NPARITY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovnu", "", 0, 4, 0 , INS_TEST_NPARITY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovnu", "", 0, 5, 0 , INS_TEST_NPARITY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovnu", "", 0, 6, 0 , INS_TEST_NPARITY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcmovnu", "", 0, 7, 0 , INS_TEST_NPARITY, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fnclex", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fninit", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucomi", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucomi", "", 0, 1, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucomi", "", 0, 2, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucomi", "", 0, 3, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucomi", "", 0, 4, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucomi", "", 0, 5, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucomi", "", 0, 6, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucomi", "", 0, 7, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcomi", "", 0, 0, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0, }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcomi", "", 0, 1, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcomi", "", 0, 2, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcomi", "", 0, 3, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcomi", "", 0, 4, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcomi", "", 0, 5, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcomi", "", 0, 6, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_PENTPRO | isa_GP, "fcomi", "", 0, 7, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 } +}; + + +static ia32_insn_t tbl_DC[] = { /* FPU DC */ + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_fd|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fadd", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_fd|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fmul", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_fd|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fcom", "", 0, 0, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 17 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_fd|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fcomp", "", 0, 0, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_fd|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fsub", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_fd|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fsubr", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_fd|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fdiv", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_fd|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fdivr", "", 0, 0, 0, 0 , 0 } +}; + + +static ia32_insn_t tbl_DCC0[] = { /* FPU DC C0 */ + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fadd", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fadd", "", 1, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fadd", "", 2, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fadd", "", 3, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fadd", "", 4, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fadd", "", 5, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fadd", "", 6, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fadd", "", 7, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fmul", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fmul", "", 1, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fmul", "", 2, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fmul", "", 3, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fmul", "", 4, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fmul", "", 5, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fmul", "", 6, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fmul", "", 7, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubr", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubr", "", 1, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubr", "", 2, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubr", "", 3, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubr", "", 4, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubr", "", 5, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubr", "", 6, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubr", "", 7, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsub", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsub", "", 1, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsub", "", 2, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsub", "", 3, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsub", "", 4, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsub", "", 5, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsub", "", 6, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsub", "", 7, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivr", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivr", "", 1, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivr", "", 2, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivr", "", 3, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivr", "", 4, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivr", "", 5, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivr", "", 6, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivr", "", 7, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdiv", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdiv", "", 1, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdiv", "", 2, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdiv", "", 3, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdiv", "", 4, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdiv", "", 5, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdiv", "", 6, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdiv", "", 7, 0, 0 , 0 , 0 } +}; + + +static ia32_insn_t tbl_DD[] = { /* FPU DD */ + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_fd|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fld", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_q|OP_W, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "fisttp", "", 0, 0, 0, 0, 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_fd|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fst", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_fd|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fstp", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_ft|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "frstor", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_ft|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fnsave", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_w|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fnstsw", "", 0, 0, 0 , 0 , 0 } +}; + + +static ia32_insn_t tbl_DDC0[] = { /* FPU DD C0 */ + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "ffree", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "ffree", "", 1, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "ffree", "", 2, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "ffree", "", 3, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "ffree", "", 4, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "ffree", "", 5, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "ffree", "", 6, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "ffree", "", 7, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fst", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fst", "", 1, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fst", "", 2, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fst", "", 3, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fst", "", 4, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fst", "", 5, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fst", "", 6, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fst", "", 7, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fstp", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fstp", "", 1, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fstp", "", 2, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fstp", "", 3, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fstp", "", 4, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fstp", "", 5, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fstp", "", 6, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fstp", "", 7, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucom", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucom", "", 1, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucom", "", 2, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucom", "", 3, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucom", "", 4, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucom", "", 5, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucom", "", 6, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucom", "", 7, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucomp", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucomp", "", 1, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucomp", "", 2, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucomp", "", 3, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucomp", "", 4, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucomp", "", 5, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucomp", "", 6, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucomp", "", 7, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 } +}; + + +static ia32_insn_t tbl_DE[] = { /* FPU DE */ + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_w|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fiadd", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_w|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fimul", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_w|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "ficom", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_w|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "ficomp", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_w|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fisub", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_w|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fisubr", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_w|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fidiv", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_w|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fidivr", "", 0, 0, 0, 0 , 0 } +}; + + +static ia32_insn_t tbl_DEC0[] = { /* FPU DE C0 */ + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "faddp", "", 0, 0, 0 , 0 , 20 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "faddp", "", 1, 0, 0 , 0 , 20 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "faddp", "", 2, 0, 0 , 0 , 20 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "faddp", "", 3, 0, 0 , 0 , 20 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "faddp", "", 4, 0, 0 , 0 , 20 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "faddp", "", 5, 0, 0 , 0 , 20 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "faddp", "", 6, 0, 0 , 0 , 20 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "faddp", "", 7, 0, 0 , 0 , 20 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fmulp", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fmulp", "", 1, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fmulp", "", 2, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fmulp", "", 3, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fmulp", "", 4, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fmulp", "", 5, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fmulp", "", 6, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fmulp", "", 7, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fcompp", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubrp", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubrp", "", 1, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubrp", "", 2, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubrp", "", 3, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubrp", "", 4, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubrp", "", 5, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubrp", "", 6, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubrp", "", 7, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubp", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubp", "", 1, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubp", "", 2, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubp", "", 3, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubp", "", 4, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubp", "", 5, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubp", "", 6, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fsubp", "", 7, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivrp", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivrp", "", 1, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivrp", "", 2, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivrp", "", 3, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivrp", "", 4, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivrp", "", 5, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivrp", "", 6, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivrp", "", 7, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivp", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivp", "", 1, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivp", "", 2, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivp", "", 3, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivp", "", 4, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivp", "", 5, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivp", "", 6, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fdivp", "", 7, 0, 0 , 0 , 0 } +}; + + +static ia32_insn_t tbl_DF[] = { /* FPU DF */ + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_w|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fild", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_w|OP_W, ARG_NONE, ARG_NONE, cpu_PENTIUM4 | isa_GP, "fisttp", "", 0, 0, 0, 0, 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_w|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fist", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_w|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fistp", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_fb|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fbld", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_q|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fild", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_fb|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fbstp", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_M|OPTYPE_q|OP_W, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fistp", "", 0, 0, 0 , 0 , 0 } +}; + + +static ia32_insn_t tbl_DFC0[] = { /* FPU DF C0 */ + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RR | OPTYPE_w | OP_R, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "fnstsw", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucomip", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucomip", "", 0, 1, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucomip", "", 0, 2, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucomip", "", 0, 3, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucomip", "", 0, 4, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucomip", "", 0, 5, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucomip", "", 0, 6, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fucomip", "", 0, 7, 0 , 0 , 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcomip", "", 0, 0, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcomip", "", 0, 1, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcomip", "", 0, 2, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcomip", "", 0, 3, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcomip", "", 0, 4, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcomip", "", 0, 5, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcomip", "", 0, 6, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0 }, + { 0, INS_FPU, 0, ADDRMETH_RF | OPTYPE_fp | OP_W, ADDRMETH_RF | OPTYPE_fp | OP_R, ARG_NONE, cpu_80387 | isa_FPU, "fcomip", "", 0, 7, 0 , INS_SET_ZERO|INS_SET_PARITY|INS_SET_CARRY, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_80387 | isa_FPU, "", "", 0, 0, 0 , 0 , 0 } +}; + + +static ia32_insn_t tbl_0F0F[] = { /* 3D Now! 0F Suffix */ + /* 00 */ { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_CONV, 0, ADDRMETH_P | OPTYPE_pi | OP_R | OP_W, ADDRMETH_Q | OPTYPE_q |OP_R, ARG_NONE, cpu_K6 | isa_3DNOW, "pi2fd", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + /* 10 */ { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_CONV, 0, ADDRMETH_P | OPTYPE_pi | OP_R | OP_W, ADDRMETH_Q | OPTYPE_q |OP_R, ARG_NONE, cpu_K6 | isa_3DNOW, "pf2id", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + /* 20 */ { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + /* 30 */ { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + /* 40 */ { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + /* 50 */ { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + /* 60 */ { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + /* 70 */ { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + /* 80 */ { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_CMP, 0, ADDRMETH_P | OPTYPE_pi | OP_R | OP_W, ADDRMETH_Q | OPTYPE_q |OP_R, ARG_NONE, cpu_K6 | isa_3DNOW, "pfcmpge", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_MIN, 0, ADDRMETH_P | OPTYPE_pi | OP_R | OP_W, ADDRMETH_Q | OPTYPE_q |OP_R, ARG_NONE, cpu_K6 | isa_3DNOW, "pfmin", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_ARITH, 0, ADDRMETH_P | OPTYPE_pi | OP_R | OP_W, ADDRMETH_Q | OPTYPE_q |OP_R, ARG_NONE, cpu_K6 | isa_3DNOW, "pfrcp", "", 0, 0, 0, 0, 0 }, + { 0, INS_ARITH, 0, ADDRMETH_P | OPTYPE_pi | OP_R | OP_W, ADDRMETH_Q | OPTYPE_q |OP_R, ARG_NONE, cpu_K6 | isa_3DNOW, "pfrsqrt", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_SUB, 0, ADDRMETH_P | OPTYPE_pi | OP_R | OP_W, ADDRMETH_Q | OPTYPE_q |OP_R, ARG_NONE, cpu_K6 | isa_3DNOW, "pfsub", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_ADD, 0, ADDRMETH_P | OPTYPE_pi | OP_R | OP_W, ADDRMETH_Q | OPTYPE_q |OP_R, ARG_NONE, cpu_K6 | isa_3DNOW, "pfadd", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_CMP, 0, ADDRMETH_P | OPTYPE_pi | OP_R | OP_W, ADDRMETH_Q | OPTYPE_q |OP_R, ARG_NONE, cpu_K6 | isa_3DNOW, "pfcmpgt", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_MAX, 0, ADDRMETH_P | OPTYPE_pi | OP_R | OP_W, ADDRMETH_Q | OPTYPE_q |OP_R, ARG_NONE, cpu_K6 | isa_3DNOW, "pfmax", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_ARITH, 0, ADDRMETH_P | OPTYPE_pi | OP_R | OP_W, ADDRMETH_Q | OPTYPE_q |OP_R, ARG_NONE, cpu_K6 | isa_3DNOW, "pfrcpit1", "", 0, 0, 0, 0, 0 }, + { 0, INS_ARITH, 0, ADDRMETH_P | OPTYPE_pi | OP_R | OP_W, ADDRMETH_Q | OPTYPE_q |OP_R, ARG_NONE, cpu_K6 | isa_3DNOW, "pfrsqit1", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_SUB, 0, ADDRMETH_P | OPTYPE_pi | OP_R | OP_W, ADDRMETH_Q | OPTYPE_q |OP_R, ARG_NONE, cpu_K6 | isa_3DNOW, "pfsubr", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_ADD, 0, ADDRMETH_P | OPTYPE_pi | OP_R | OP_W, ADDRMETH_Q | OPTYPE_q |OP_R, ARG_NONE, cpu_K6 | isa_3DNOW, "pfacc", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_CMP, 0, ADDRMETH_P | OPTYPE_pi | OP_R | OP_W, ADDRMETH_Q | OPTYPE_q |OP_R, ARG_NONE, cpu_K6 | isa_3DNOW, "pfcmpeq", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_MUL, 0, ADDRMETH_P | OPTYPE_pi | OP_R | OP_W, ADDRMETH_Q | OPTYPE_q |OP_R, ARG_NONE, cpu_K6 | isa_3DNOW, "pfmul", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_ARITH, 0, ADDRMETH_P | OPTYPE_pi | OP_R | OP_W, ADDRMETH_Q | OPTYPE_q |OP_R, ARG_NONE, cpu_K6 | isa_3DNOW, "pfrcpit2", "", 0, 0, 0, 0, 0 }, + { 0, INS_MUL, 0, ADDRMETH_P | OPTYPE_pi | OP_R | OP_W, ADDRMETH_Q | OPTYPE_q |OP_R, ARG_NONE, cpu_K6 | isa_3DNOW, "pmulhrw", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_INVALID, 0, ARG_NONE, ARG_NONE, ARG_NONE, cpu_K6 | isa_3DNOW, "", "", 0, 0, 0, 0, 0 }, + { 0, INS_AVG, 0, ADDRMETH_P | OPTYPE_pi | OP_R | OP_W, ADDRMETH_Q | OPTYPE_q |OP_R, ARG_NONE, cpu_K6 | isa_3DNOW, "pavgusb", "", 0, 0, 0, 0, 0 } +}; + + + +/* ================== Table of Opcode Tables ================== */ +ia32_table_desc_t ia32_tables[] = { + /* table, prefix table, type, shift, mask, min, max */ + { tbl_Main, tbl_opcode, 0x00, 0xFF, 0x00, 0xFF }, + { tbl_66, tbl_prefix, 0x00, 0xFF, 0x0F, 0x0F }, + { tbl_F2, tbl_prefix, 0x00, 0xFF, 0x0F, 0x0F }, + { tbl_F3, tbl_prefix, 0x00, 0xFF, 0x0F, 0x90 }, + { tbl_0F, tbl_opcode, 0x00, 0xFF, 0x00, 0xFF }, + /* 5 */ + { tbl_660F, tbl_prefix, 0x00, 0xFF, 0x10, 0xFF }, + { tbl_F20F, tbl_prefix, 0x00, 0xFF, 0x10, 0xFF }, + { tbl_F30F, tbl_prefix, 0x00, 0xFF, 0x10, 0xFF }, + { tbl_0F00, tbl_extension, 0x03, 0x07, 0x00, 0x07 }, + { tbl_0F01, tbl_extension, 0x03, 0x1F, 0x00, 0x1F }, + /* 10 */ + { tbl_0F0111, tbl_ext_ext, 0x00, 0x01, 0x00, 0x01 }, + { tbl_0F12, tbl_extension, 0x06, 0x03, 0x00, 0x03 }, + { tbl_0F16, tbl_extension, 0x06, 0x03, 0x00, 0x03 }, + { tbl_0F18, tbl_extension, 0x03, 0x1F, 0x00, 0x13 }, + { tbl_0F71, tbl_extension, 0x03, 0x1F, 0x00, 0x1F }, + /* 15 */ + { tbl_660F71, tbl_extension, 0x03, 0x1F, 0x00, 0x1F }, + { tbl_0F72, tbl_extension, 0x03, 0x1F, 0x00, 0x1F }, + { tbl_660F72, tbl_extension, 0x03, 0x1F, 0x00, 0x1F }, + { tbl_0F73, tbl_extension, 0x00, 0x00, 0x00, 0x00 }, + { tbl_660F73, tbl_extension, 0x03, 0x1F, 0x00, 0x1F }, + /* 20 */ + { tbl_0FAE, tbl_extension, 0x03, 0x1F, 0x00, 0x1F }, + { tbl_0FBA, tbl_extension, 0x03, 0x07, 0x04, 0x07 }, + { tbl_0FC7, tbl_extension, 0x03, 0x1F, 0x00, 0x11 }, + { tbl_0FB9, tbl_extension, 0x03, 0x07, 0x00, 0x00 }, + { tbl_C6, tbl_extension, 0x03, 0x07, 0x00, 0x00 }, + /* 25 */ + { tbl_C7, tbl_extension, 0x03, 0x07, 0x00, 0x00 }, + { tbl_80, tbl_extension, 0x03, 0x07, 0x00, 0x07 }, + { tbl_81, tbl_extension, 0x03, 0x07, 0x00, 0x07 }, + { tbl_82, tbl_extension, 0x03, 0x07, 0x00, 0x07 }, + { tbl_83, tbl_extension, 0x03, 0x07, 0x00, 0x07 }, + /* 30 */ + { tbl_C0, tbl_extension, 0x03, 0x07, 0x00, 0x07 }, + { tbl_C1, tbl_extension, 0x03, 0x07, 0x00, 0x07 }, + { tbl_D0, tbl_extension, 0x03, 0x07, 0x00, 0x07 }, + { tbl_D1, tbl_extension, 0x03, 0x07, 0x00, 0x07 }, + { tbl_D2, tbl_extension, 0x03, 0x07, 0x00, 0x07 }, + /* 35 */ + { tbl_D3, tbl_extension, 0x03, 0x07, 0x00, 0x07 }, + { tbl_F6, tbl_extension, 0x03, 0x07, 0x00, 0x07 }, + { tbl_F7, tbl_extension, 0x03, 0x07, 0x00, 0x07 }, + { tbl_FE, tbl_extension, 0x03, 0x07, 0x00, 0x01 }, + { tbl_FF, tbl_extension, 0x03, 0x07, 0x00, 0x06 }, + /* 40 */ + { tbl_D8, tbl_fpu, 0x03, 0x07, 0x00, 0xBF }, + { tbl_D8C0, tbl_fpu_ext, 0x00, 0xFF, 0xC0, 0xFF }, + { tbl_D9, tbl_fpu, 0x03, 0x07, 0x00, 0xBF }, + { tbl_D9C0, tbl_fpu_ext, 0x00, 0xFF, 0xC0, 0xFF }, + { tbl_DA, tbl_fpu, 0x03, 0x07, 0x00, 0xBF }, + /* 45 */ + { tbl_DAC0, tbl_fpu_ext, 0x00, 0xFF, 0xC0, 0xFF }, + { tbl_DB, tbl_fpu, 0x03, 0x07, 0x00, 0xBF }, + { tbl_DBC0, tbl_fpu_ext, 0x00, 0xFF, 0xC0, 0xFF }, + { tbl_DC, tbl_fpu, 0x03, 0x07, 0x00, 0xBF }, + { tbl_DCC0, tbl_fpu_ext, 0x00, 0xFF, 0xC0, 0xFF }, + /* 50 */ + { tbl_DD, tbl_fpu, 0x03, 0x07, 0x00, 0xBF }, + { tbl_DDC0, tbl_fpu_ext, 0x00, 0xFF, 0xC0, 0xFF }, + { tbl_DE, tbl_fpu, 0x03, 0x07, 0x00, 0xBF }, + { tbl_DEC0, tbl_fpu_ext, 0x00, 0xFF, 0xC0, 0xFF }, + { tbl_DF, tbl_fpu, 0x03, 0x07, 0x00, 0xBF }, + /* 55 */ + { tbl_DFC0, tbl_fpu_ext, 0x00, 0xFF, 0xC0, 0xFF }, + { tbl_0F0F, tbl_suffix, 0x00, 0xFF, 0x00, 0xBF } +}; +/* ia32_opcode_tables.h */ +/* Table index constants: +#define idx_Main 0 +#define idx_66 1 +#define idx_F2 2 +#define idx_F3 3 +#define idx_0F 4 +#define idx_660F 5 +#define idx_F20F 6 +#define idx_F30F 7 +#define idx_0F00 8 +#define idx_0F01 9 +#define idx_0F0111 10 +#define idx_0F12 11 +#define idx_0F16 12 +#define idx_0F18 13 +#define idx_0F71 14 +#define idx_660F71 15 +#define idx_0F72 16 +#define idx_660F72 17 +#define idx_0F73 18 +#define idx_660F73 19 +#define idx_0FAE 20 +#define idx_0FBA 21 +#define idx_0FC7 22 +#define idx_0FB9 23 +#define idx_C6 24 +#define idx_C7 25 +#define idx_80 26 +#define idx_81 27 +#define idx_82 28 +#define idx_83 29 +#define idx_C0 30 +#define idx_C1 31 +#define idx_D0 32 +#define idx_D1 33 +#define idx_D2 34 +#define idx_D3 35 +#define idx_F6 36 +#define idx_F7 37 +#define idx_FE 38 +#define idx_FF 39 +#define idx_D8 40 +#define idx_D8C0 41 +#define idx_D9 42 +#define idx_D9C0 43 +#define idx_DA 44 +#define idx_DAC0 45 +#define idx_DB 46 +#define idx_DBC0 47 +#define idx_DC 48 +#define idx_DCC0 49 +#define idx_DD 50 +#define idx_DDC0 51 +#define idx_DE 52 +#define idx_DEC0 53 +#define idx_DF 54 +#define idx_DFC0 55 +#define idx_0F0F 56 +*/ diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_opcode_tables.h b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_opcode_tables.h new file mode 100644 index 000000000..bbd4fae9a --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_opcode_tables.h @@ -0,0 +1,57 @@ +#define idx_Main 0 +#define idx_66 1 +#define idx_F2 2 +#define idx_F3 3 +#define idx_0F 4 +#define idx_660F 5 +#define idx_F20F 6 +#define idx_F30F 7 +#define idx_0F00 8 +#define idx_0F01 9 +#define idx_0F0111 10 +#define idx_0F12 11 +#define idx_0F16 12 +#define idx_0F18 13 +#define idx_0F71 14 +#define idx_660F71 15 +#define idx_0F72 16 +#define idx_660F72 17 +#define idx_0F73 18 +#define idx_660F73 19 +#define idx_0FAE 20 +#define idx_0FBA 21 +#define idx_0FC7 22 +#define idx_0FB9 23 +#define idx_C6 24 +#define idx_C7 25 +#define idx_80 26 +#define idx_81 27 +#define idx_82 28 +#define idx_83 29 +#define idx_C0 30 +#define idx_C1 31 +#define idx_D0 32 +#define idx_D1 33 +#define idx_D2 34 +#define idx_D3 35 +#define idx_F6 36 +#define idx_F7 37 +#define idx_FE 38 +#define idx_FF 39 +#define idx_D8 40 +#define idx_D8C0 41 +#define idx_D9 42 +#define idx_D9C0 43 +#define idx_DA 44 +#define idx_DAC0 45 +#define idx_DB 46 +#define idx_DBC0 47 +#define idx_DC 48 +#define idx_DCC0 49 +#define idx_DD 50 +#define idx_DDC0 51 +#define idx_DE 52 +#define idx_DEC0 53 +#define idx_DF 54 +#define idx_DFC0 55 +#define idx_0F0F 56 diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_operand.c b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_operand.c new file mode 100644 index 000000000..8e7f16a0c --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_operand.c @@ -0,0 +1,425 @@ +#include +#include +#include + +#include "libdis.h" +#include "ia32_insn.h" +#include "ia32_operand.h" +#include "ia32_modrm.h" +#include "ia32_reg.h" +#include "x86_imm.h" +#include "x86_operand_list.h" + + + +/* apply segment override to memory operand in insn */ +static void apply_seg( x86_op_t *op, unsigned int prefixes ) { + if (! prefixes ) return; + + /* apply overrides from prefix */ + switch ( prefixes & PREFIX_REG_MASK ) { + case PREFIX_CS: + op->flags |= op_cs_seg; break; + case PREFIX_SS: + op->flags |= op_ss_seg; break; + case PREFIX_DS: + op->flags |= op_ds_seg; break; + case PREFIX_ES: + op->flags |= op_es_seg; break; + case PREFIX_FS: + op->flags |= op_fs_seg; break; + case PREFIX_GS: + op->flags |= op_gs_seg; break; + } + + return; +} + +static size_t decode_operand_value( unsigned char *buf, size_t buf_len, + x86_op_t *op, x86_insn_t *insn, + unsigned int addr_meth, size_t op_size, + unsigned int op_value, unsigned char modrm, + size_t gen_regs ) { + size_t size = 0; + + /* ++ Do Operand Addressing Method / Decode operand ++ */ + switch (addr_meth) { + /* This sets the operand Size based on the Intel Opcode Map + * (Vol 2, Appendix A). Letter encodings are from section + * A.1.1, 'Codes for Addressing Method' */ + + /* ---------------------- Addressing Method -------------- */ + /* Note that decoding mod ModR/M operand adjusts the size of + * the instruction, but decoding the reg operand does not. + * This should not cause any problems, as every 'reg' operand + * has an associated 'mod' operand. + * Goddamn-Intel-Note: + * Some Intel addressing methods [M, R] specify that modR/M + * byte may only refer to a memory address/may only refer to + * a register -- however Intel provides no clues on what to do + * if, say, the modR/M for an M opcode decodes to a register + * rather than a memory address ... returning 0 is out of the + * question, as this would be an Immediate or a RelOffset, so + * instead these modR/Ms are decoded with total disregard to + * the M, R constraints. */ + + /* MODRM -- mod operand. sets size to at least 1! */ + case ADDRMETH_E: /* ModR/M present, Gen reg or memory */ + size = ia32_modrm_decode( buf, buf_len, op, insn, + gen_regs ); + break; + case ADDRMETH_M: /* ModR/M only refers to memory */ + size = ia32_modrm_decode( buf, buf_len, op, insn, + gen_regs ); + break; + case ADDRMETH_Q: /* ModR/M present, MMX or Memory */ + size = ia32_modrm_decode( buf, buf_len, op, insn, + REG_MMX_OFFSET ); + break; + case ADDRMETH_R: /* ModR/M mod == gen reg */ + size = ia32_modrm_decode( buf, buf_len, op, insn, + gen_regs ); + break; + case ADDRMETH_W: /* ModR/M present, mem or SIMD reg */ + size = ia32_modrm_decode( buf, buf_len, op, insn, + REG_SIMD_OFFSET ); + break; + + /* MODRM -- reg operand. does not effect size! */ + case ADDRMETH_C: /* ModR/M reg == control reg */ + ia32_reg_decode( modrm, op, REG_CTRL_OFFSET ); + break; + case ADDRMETH_D: /* ModR/M reg == debug reg */ + ia32_reg_decode( modrm, op, REG_DEBUG_OFFSET ); + break; + case ADDRMETH_G: /* ModR/M reg == gen-purpose reg */ + ia32_reg_decode( modrm, op, gen_regs ); + break; + case ADDRMETH_P: /* ModR/M reg == qword MMX reg */ + ia32_reg_decode( modrm, op, REG_MMX_OFFSET ); + break; + case ADDRMETH_S: /* ModR/M reg == segment reg */ + ia32_reg_decode( modrm, op, REG_SEG_OFFSET ); + break; + case ADDRMETH_T: /* ModR/M reg == test reg */ + ia32_reg_decode( modrm, op, REG_TEST_OFFSET ); + break; + case ADDRMETH_V: /* ModR/M reg == SIMD reg */ + ia32_reg_decode( modrm, op, REG_SIMD_OFFSET ); + break; + + /* No MODRM : note these set operand type explicitly */ + case ADDRMETH_A: /* No modR/M -- direct addr */ + op->type = op_absolute; + + /* segment:offset address used in far calls */ + x86_imm_sized( buf, buf_len, + &op->data.absolute.segment, 2 ); + if ( insn->addr_size == 4 ) { + x86_imm_sized( buf, buf_len, + &op->data.absolute.offset.off32, 4 ); + size = 6; + } else { + x86_imm_sized( buf, buf_len, + &op->data.absolute.offset.off16, 2 ); + size = 4; + } + + break; + case ADDRMETH_I: /* Immediate val */ + op->type = op_immediate; + /* if it ever becomes legal to have imm as dest and + * there is a src ModR/M operand, we are screwed! */ + if ( op->flags & op_signed ) { + x86_imm_signsized(buf, buf_len, &op->data.byte, + op_size); + } else { + x86_imm_sized(buf, buf_len, &op->data.byte, + op_size); + } + size = op_size; + break; + case ADDRMETH_J: /* Rel offset to add to IP [jmp] */ + /* this fills op->data.near_offset or + op->data.far_offset depending on the size of + the operand */ + op->flags |= op_signed; + if ( op_size == 1 ) { + /* one-byte near offset */ + op->type = op_relative_near; + x86_imm_signsized(buf, buf_len, + &op->data.relative_near, 1); + } else { + /* far offset...is this truly signed? */ + op->type = op_relative_far; + x86_imm_signsized(buf, buf_len, + &op->data.relative_far, op_size ); + } + size = op_size; + break; + case ADDRMETH_O: /* No ModR/M; op is word/dword offset */ + /* NOTE: these are actually RVAs not offsets to seg!! */ + /* note bene: 'O' ADDR_METH uses addr_size to + determine operand size */ + op->type = op_offset; + op->flags |= op_pointer; + x86_imm_sized( buf, buf_len, &op->data.offset, + insn->addr_size ); + + size = insn->addr_size; + break; + + /* Hard-coded: these are specified in the insn definition */ + case ADDRMETH_F: /* EFLAGS register */ + op->type = op_register; + op->flags |= op_hardcode; + ia32_handle_register( &op->data.reg, REG_FLAGS_INDEX ); + break; + case ADDRMETH_X: /* Memory addressed by DS:SI [string] */ + op->type = op_expression; + op->flags |= op_hardcode; + op->flags |= op_ds_seg | op_pointer | op_string; + ia32_handle_register( &op->data.expression.base, + REG_DWORD_OFFSET + 6 ); + break; + case ADDRMETH_Y: /* Memory addressed by ES:DI [string] */ + op->type = op_expression; + op->flags |= op_hardcode; + op->flags |= op_es_seg | op_pointer | op_string; + ia32_handle_register( &op->data.expression.base, + REG_DWORD_OFFSET + 7 ); + break; + case ADDRMETH_RR: /* Gen Register hard-coded in opcode */ + op->type = op_register; + op->flags |= op_hardcode; + ia32_handle_register( &op->data.reg, + op_value + gen_regs ); + break; + case ADDRMETH_RS: /* Seg Register hard-coded in opcode */ + op->type = op_register; + op->flags |= op_hardcode; + ia32_handle_register( &op->data.reg, + op_value + REG_SEG_OFFSET ); + break; + case ADDRMETH_RF: /* FPU Register hard-coded in opcode */ + op->type = op_register; + op->flags |= op_hardcode; + ia32_handle_register( &op->data.reg, + op_value + REG_FPU_OFFSET ); + break; + case ADDRMETH_RT: /* TST Register hard-coded in opcode */ + op->type = op_register; + op->flags |= op_hardcode; + ia32_handle_register( &op->data.reg, + op_value + REG_TEST_OFFSET ); + break; + case ADDRMETH_II: /* Immediate hard-coded in opcode */ + op->type = op_immediate; + op->data.dword = op_value; + op->flags |= op_hardcode; + break; + + case 0: /* Operand is not used */ + default: + /* ignore -- operand not used in this insn */ + op->type = op_unused; /* this shouldn't happen! */ + break; + } + + return size; +} + +static size_t decode_operand_size( unsigned int op_type, x86_insn_t *insn, + x86_op_t *op ){ + size_t size; + + /* ++ Do Operand Type ++ */ + switch (op_type) { + /* This sets the operand Size based on the Intel Opcode Map + * (Vol 2, Appendix A). Letter encodings are from section + * A.1.2, 'Codes for Operand Type' */ + /* NOTE: in this routines, 'size' refers to the size + * of the operand in the raw (encoded) instruction; + * 'datatype' stores the actual size and datatype + * of the operand */ + + /* ------------------------ Operand Type ----------------- */ + case OPTYPE_c: /* byte or word [op size attr] */ + size = (insn->op_size == 4) ? 2 : 1; + op->datatype = (size == 4) ? op_word : op_byte; + break; + case OPTYPE_a: /* 2 word or 2 dword [op size attr] */ + /* pointer to a 16:16 or 32:32 BOUNDS operand */ + size = (insn->op_size == 4) ? 8 : 4; + op->datatype = (size == 4) ? op_bounds32 : op_bounds16; + break; + case OPTYPE_v: /* word or dword [op size attr] */ + size = (insn->op_size == 4) ? 4 : 2; + op->datatype = (size == 4) ? op_dword : op_word; + break; + case OPTYPE_p: /* 32/48-bit ptr [op size attr] */ + /* technically these flags are not accurate: the + * value s a 16:16 pointer or a 16:32 pointer, where + * the first '16' is a segment */ + size = (insn->addr_size == 4) ? 6 : 4; + op->datatype = (size == 4) ? op_descr32 : op_descr16; + break; + case OPTYPE_b: /* byte, ignore op-size */ + size = 1; + op->datatype = op_byte; + break; + case OPTYPE_w: /* word, ignore op-size */ + size = 2; + op->datatype = op_word; + break; + case OPTYPE_d: /* dword , ignore op-size */ + size = 4; + op->datatype = op_dword; + break; + case OPTYPE_s: /* 6-byte psuedo-descriptor */ + /* ptr to 6-byte value which is 32:16 in 32-bit + * mode, or 8:24:16 in 16-bit mode. The high byte + * is ignored in 16-bit mode. */ + size = 6; + op->datatype = (insn->addr_size == 4) ? + op_pdescr32 : op_pdescr16; + break; + case OPTYPE_q: /* qword, ignore op-size */ + size = 8; + op->datatype = op_qword; + break; + case OPTYPE_dq: /* d-qword, ignore op-size */ + size = 16; + op->datatype = op_dqword; + break; + case OPTYPE_ps: /* 128-bit FP data */ + size = 16; + /* really this is 4 packed SP FP values */ + op->datatype = op_ssimd; + break; + case OPTYPE_pd: /* 128-bit FP data */ + size = 16; + /* really this is 2 packed DP FP values */ + op->datatype = op_dsimd; + break; + case OPTYPE_ss: /* Scalar elem of 128-bit FP data */ + size = 16; + /* this only looks at the low dword (4 bytes) + * of the xmmm register passed as a param. + * This is a 16-byte register where only 4 bytes + * are used in the insn. Painful, ain't it? */ + op->datatype = op_sssimd; + break; + case OPTYPE_sd: /* Scalar elem of 128-bit FP data */ + size = 16; + /* this only looks at the low qword (8 bytes) + * of the xmmm register passed as a param. + * This is a 16-byte register where only 8 bytes + * are used in the insn. Painful, again... */ + op->datatype = op_sdsimd; + break; + case OPTYPE_pi: /* qword mmx register */ + size = 8; + op->datatype = op_qword; + break; + case OPTYPE_si: /* dword integer register */ + size = 4; + op->datatype = op_dword; + break; + case OPTYPE_fs: /* single-real */ + size = 4; + op->datatype = op_sreal; + break; + case OPTYPE_fd: /* double real */ + size = 8; + op->datatype = op_dreal; + break; + case OPTYPE_fe: /* extended real */ + size = 10; + op->datatype = op_extreal; + break; + case OPTYPE_fb: /* packed BCD */ + size = 10; + op->datatype = op_bcd; + break; + case OPTYPE_fv: /* pointer to FPU env: 14 or 28-bytes */ + size = (insn->addr_size == 4)? 28 : 14; + op->datatype = (size == 28)? op_fpuenv32: op_fpuenv16; + break; + case OPTYPE_ft: /* pointer to FPU env: 94 or 108 bytes */ + size = (insn->addr_size == 4)? 108 : 94; + op->datatype = (size == 108)? + op_fpustate32: op_fpustate16; + break; + case OPTYPE_fx: /* 512-byte register stack */ + size = 512; + op->datatype = op_fpregset; + break; + case OPTYPE_fp: /* floating point register */ + size = 10; /* double extended precision */ + op->datatype = op_fpreg; + break; + case OPTYPE_m: /* fake operand type used for "lea Gv, M" */ + size = insn->addr_size; + op->datatype = (size == 4) ? op_dword : op_word; + break; + case OPTYPE_none: /* handle weird instructions that have no encoding but use a dword datatype, like invlpg */ + size = 0; + op->datatype = op_none; + break; + case 0: + default: + size = insn->op_size; + op->datatype = (size == 4) ? op_dword : op_word; + break; + } + return size; +} + +size_t ia32_decode_operand( unsigned char *buf, size_t buf_len, + x86_insn_t *insn, unsigned int raw_op, + unsigned int raw_flags, unsigned int prefixes, + unsigned char modrm ) { + unsigned int addr_meth, op_type, op_size, gen_regs; + x86_op_t *op; + size_t size; + + /* ++ Yank optype and addr mode out of operand flags */ + addr_meth = raw_flags & ADDRMETH_MASK; + op_type = raw_flags & OPTYPE_MASK; + + if ( raw_flags == ARG_NONE ) { + /* operand is not used in this instruction */ + return 0; + } + + /* allocate a new operand */ + op = x86_operand_new( insn ); + + /* ++ Copy flags from opcode table to x86_insn_t */ + op->access = (enum x86_op_access) OP_PERM(raw_flags); + op->flags = (enum x86_op_flags) (OP_FLAGS(raw_flags) >> 12); + + /* Get size (for decoding) and datatype of operand */ + op_size = decode_operand_size(op_type, insn, op); + + /* override default register set based on Operand Type */ + /* this allows mixing of 8, 16, and 32 bit regs in insn */ + if (op_size == 1) { + gen_regs = REG_BYTE_OFFSET; + } else if (op_size == 2) { + gen_regs = REG_WORD_OFFSET; + } else { + gen_regs = REG_DWORD_OFFSET; + } + + size = decode_operand_value( buf, buf_len, op, insn, addr_meth, + op_size, raw_op, modrm, gen_regs ); + + /* if operand is an address, apply any segment override prefixes */ + if ( op->type == op_expression || op->type == op_offset ) { + apply_seg(op, prefixes); + } + + return size; /* return number of bytes in instruction */ +} diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_operand.h b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_operand.h new file mode 100644 index 000000000..08c3074cd --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_operand.h @@ -0,0 +1,11 @@ +#ifndef IA32_OPERAND_H +#define IA32_OPERAND_H + +#include "libdis.h" +#include "ia32_insn.h" + +size_t ia32_decode_operand( unsigned char *buf, size_t buf_len, + x86_insn_t *insn, unsigned int raw_op, + unsigned int raw_flags, unsigned int prefixes, + unsigned char modrm ); +#endif diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_reg.c b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_reg.c new file mode 100644 index 000000000..f270c1f34 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_reg.c @@ -0,0 +1,234 @@ +#include +#include + +#include "ia32_reg.h" +#include "ia32_insn.h" + +#define NUM_X86_REGS 92 + +/* register sizes */ +#define REG_DWORD_SIZE 4 +#define REG_WORD_SIZE 2 +#define REG_BYTE_SIZE 1 +#define REG_MMX_SIZE 8 +#define REG_SIMD_SIZE 16 +#define REG_DEBUG_SIZE 4 +#define REG_CTRL_SIZE 4 +#define REG_TEST_SIZE 4 +#define REG_SEG_SIZE 2 +#define REG_FPU_SIZE 10 +#define REG_FLAGS_SIZE 4 +#define REG_FPCTRL_SIZE 2 +#define REG_FPSTATUS_SIZE 2 +#define REG_FPTAG_SIZE 2 +#define REG_EIP_SIZE 4 +#define REG_IP_SIZE 2 + +/* REGISTER ALIAS TABLE: + * + * NOTE: the MMX register mapping is fixed to the physical registers + * used by the FPU. The floating FP stack does not effect the location + * of the MMX registers, so this aliasing is not 100% accurate. + * */ +static struct { + unsigned char alias; /* id of register this is an alias for */ + unsigned char shift; /* # of bits register must be shifted */ +} ia32_reg_aliases[] = { + { 0,0 }, + { REG_DWORD_OFFSET, 0 }, /* al : 1 */ + { REG_DWORD_OFFSET, 8 }, /* ah : 2 */ + { REG_DWORD_OFFSET, 0 }, /* ax : 3 */ + { REG_DWORD_OFFSET + 1, 0 }, /* cl : 4 */ + { REG_DWORD_OFFSET + 1, 8 }, /* ch : 5 */ + { REG_DWORD_OFFSET + 1, 0 }, /* cx : 6 */ + { REG_DWORD_OFFSET + 2, 0 }, /* dl : 7 */ + { REG_DWORD_OFFSET + 2, 8 }, /* dh : 8 */ + { REG_DWORD_OFFSET + 2, 0 }, /* dx : 9 */ + { REG_DWORD_OFFSET + 3, 0 }, /* bl : 10 */ + { REG_DWORD_OFFSET + 3, 8 }, /* bh : 11 */ + { REG_DWORD_OFFSET + 3, 0 }, /* bx : 12 */ + { REG_DWORD_OFFSET + 4, 0 }, /* sp : 13 */ + { REG_DWORD_OFFSET + 5, 0 }, /* bp : 14 */ + { REG_DWORD_OFFSET + 6, 0 }, /* si : 15 */ + { REG_DWORD_OFFSET + 7, 0 }, /* di : 16 */ + { REG_EIP_INDEX, 0 }, /* ip : 17 */ + { REG_FPU_OFFSET, 0 }, /* mm0 : 18 */ + { REG_FPU_OFFSET + 1, 0 }, /* mm1 : 19 */ + { REG_FPU_OFFSET + 2, 0 }, /* mm2 : 20 */ + { REG_FPU_OFFSET + 3, 0 }, /* mm3 : 21 */ + { REG_FPU_OFFSET + 4, 0 }, /* mm4 : 22 */ + { REG_FPU_OFFSET + 5, 0 }, /* mm5 : 23 */ + { REG_FPU_OFFSET + 6, 0 }, /* mm6 : 24 */ + { REG_FPU_OFFSET + 7, 0 } /* mm7 : 25 */ + }; + +/* REGISTER TABLE: size, type, and name of every register in the + * CPU. Does not include MSRs since the are, after all, + * model specific. */ +static struct { + unsigned int size; + enum x86_reg_type type; + unsigned int alias; + char mnemonic[8]; +} ia32_reg_table[NUM_X86_REGS + 2] = { + { 0, 0, 0, "" }, + /* REG_DWORD_OFFSET */ + { REG_DWORD_SIZE, reg_gen | reg_ret, 0, "eax" }, + { REG_DWORD_SIZE, reg_gen | reg_count, 0, "ecx" }, + { REG_DWORD_SIZE, reg_gen, 0, "edx" }, + { REG_DWORD_SIZE, reg_gen, 0, "ebx" }, + /* REG_ESP_INDEX */ + { REG_DWORD_SIZE, reg_gen | reg_sp, 0, "esp" }, + { REG_DWORD_SIZE, reg_gen | reg_fp, 0, "ebp" }, + { REG_DWORD_SIZE, reg_gen | reg_src, 0, "esi" }, + { REG_DWORD_SIZE, reg_gen | reg_dest, 0, "edi" }, + /* REG_WORD_OFFSET */ + { REG_WORD_SIZE, reg_gen | reg_ret, 3, "ax" }, + { REG_WORD_SIZE, reg_gen | reg_count, 6, "cx" }, + { REG_WORD_SIZE, reg_gen, 9, "dx" }, + { REG_WORD_SIZE, reg_gen, 12, "bx" }, + { REG_WORD_SIZE, reg_gen | reg_sp, 13, "sp" }, + { REG_WORD_SIZE, reg_gen | reg_fp, 14, "bp" }, + { REG_WORD_SIZE, reg_gen | reg_src, 15, "si" }, + { REG_WORD_SIZE, reg_gen | reg_dest, 16, "di" }, + /* REG_BYTE_OFFSET */ + { REG_BYTE_SIZE, reg_gen, 1, "al" }, + { REG_BYTE_SIZE, reg_gen, 4, "cl" }, + { REG_BYTE_SIZE, reg_gen, 7, "dl" }, + { REG_BYTE_SIZE, reg_gen, 10, "bl" }, + { REG_BYTE_SIZE, reg_gen, 2, "ah" }, + { REG_BYTE_SIZE, reg_gen, 5, "ch" }, + { REG_BYTE_SIZE, reg_gen, 8, "dh" }, + { REG_BYTE_SIZE, reg_gen, 11, "bh" }, + /* REG_MMX_OFFSET */ + { REG_MMX_SIZE, reg_simd, 18, "mm0" }, + { REG_MMX_SIZE, reg_simd, 19, "mm1" }, + { REG_MMX_SIZE, reg_simd, 20, "mm2" }, + { REG_MMX_SIZE, reg_simd, 21, "mm3" }, + { REG_MMX_SIZE, reg_simd, 22, "mm4" }, + { REG_MMX_SIZE, reg_simd, 23, "mm5" }, + { REG_MMX_SIZE, reg_simd, 24, "mm6" }, + { REG_MMX_SIZE, reg_simd, 25, "mm7" }, + /* REG_SIMD_OFFSET */ + { REG_SIMD_SIZE, reg_simd, 0, "xmm0" }, + { REG_SIMD_SIZE, reg_simd, 0, "xmm1" }, + { REG_SIMD_SIZE, reg_simd, 0, "xmm2" }, + { REG_SIMD_SIZE, reg_simd, 0, "xmm3" }, + { REG_SIMD_SIZE, reg_simd, 0, "xmm4" }, + { REG_SIMD_SIZE, reg_simd, 0, "xmm5" }, + { REG_SIMD_SIZE, reg_simd, 0, "xmm6" }, + { REG_SIMD_SIZE, reg_simd, 0, "xmm7" }, + /* REG_DEBUG_OFFSET */ + { REG_DEBUG_SIZE, reg_sys, 0, "dr0" }, + { REG_DEBUG_SIZE, reg_sys, 0, "dr1" }, + { REG_DEBUG_SIZE, reg_sys, 0, "dr2" }, + { REG_DEBUG_SIZE, reg_sys, 0, "dr3" }, + { REG_DEBUG_SIZE, reg_sys, 0, "dr4" }, + { REG_DEBUG_SIZE, reg_sys, 0, "dr5" }, + { REG_DEBUG_SIZE, reg_sys, 0, "dr6" }, + { REG_DEBUG_SIZE, reg_sys, 0, "dr7" }, + /* REG_CTRL_OFFSET */ + { REG_CTRL_SIZE, reg_sys, 0, "cr0" }, + { REG_CTRL_SIZE, reg_sys, 0, "cr1" }, + { REG_CTRL_SIZE, reg_sys, 0, "cr2" }, + { REG_CTRL_SIZE, reg_sys, 0, "cr3" }, + { REG_CTRL_SIZE, reg_sys, 0, "cr4" }, + { REG_CTRL_SIZE, reg_sys, 0, "cr5" }, + { REG_CTRL_SIZE, reg_sys, 0, "cr6" }, + { REG_CTRL_SIZE, reg_sys, 0, "cr7" }, + /* REG_TEST_OFFSET */ + { REG_TEST_SIZE, reg_sys, 0, "tr0" }, + { REG_TEST_SIZE, reg_sys, 0, "tr1" }, + { REG_TEST_SIZE, reg_sys, 0, "tr2" }, + { REG_TEST_SIZE, reg_sys, 0, "tr3" }, + { REG_TEST_SIZE, reg_sys, 0, "tr4" }, + { REG_TEST_SIZE, reg_sys, 0, "tr5" }, + { REG_TEST_SIZE, reg_sys, 0, "tr6" }, + { REG_TEST_SIZE, reg_sys, 0, "tr7" }, + /* REG_SEG_OFFSET */ + { REG_SEG_SIZE, reg_seg, 0, "es" }, + { REG_SEG_SIZE, reg_seg, 0, "cs" }, + { REG_SEG_SIZE, reg_seg, 0, "ss" }, + { REG_SEG_SIZE, reg_seg, 0, "ds" }, + { REG_SEG_SIZE, reg_seg, 0, "fs" }, + { REG_SEG_SIZE, reg_seg, 0, "gs" }, + /* REG_LDTR_INDEX */ + { REG_DWORD_SIZE, reg_sys, 0, "ldtr" }, + /* REG_GDTR_INDEX */ + { REG_DWORD_SIZE, reg_sys, 0, "gdtr" }, + /* REG_FPU_OFFSET */ + { REG_FPU_SIZE, reg_fpu, 0, "st(0)" }, + { REG_FPU_SIZE, reg_fpu, 0, "st(1)" }, + { REG_FPU_SIZE, reg_fpu, 0, "st(2)" }, + { REG_FPU_SIZE, reg_fpu, 0, "st(3)" }, + { REG_FPU_SIZE, reg_fpu, 0, "st(4)" }, + { REG_FPU_SIZE, reg_fpu, 0, "st(5)" }, + { REG_FPU_SIZE, reg_fpu, 0, "st(6)" }, + { REG_FPU_SIZE, reg_fpu, 0, "st(7)" }, + /* REG_FLAGS_INDEX : 81 */ + { REG_FLAGS_SIZE, reg_cond, 0, "eflags" }, + /* REG_FPCTRL_INDEX : 82*/ + { REG_FPCTRL_SIZE, reg_fpu | reg_sys, 0, "fpctrl" }, + /* REG_FPSTATUS_INDEX : 83*/ + { REG_FPSTATUS_SIZE, reg_fpu | reg_sys, 0, "fpstat" }, + /* REG_FPTAG_INDEX : 84 */ + { REG_FPTAG_SIZE, reg_fpu | reg_sys, 0, "fptag" }, + /* REG_EIP_INDEX : 85 */ + { REG_EIP_SIZE, reg_pc, 0, "eip" }, + /* REG_IP_INDEX : 86 */ + { REG_IP_SIZE, reg_pc, 17, "ip" }, + /* REG_IDTR_INDEX : 87 */ + { REG_DWORD_SIZE, reg_sys, 0, "idtr" }, + /* REG_MXCSG_INDEX : SSE Control Reg : 88 */ + { REG_DWORD_SIZE, reg_sys | reg_simd, 0, "mxcsr" }, + /* REG_TR_INDEX : Task Register : 89 */ + { 16 + 64, reg_sys, 0, "tr" }, + /* REG_CSMSR_INDEX : SYSENTER_CS_MSR : 90 */ + { REG_DWORD_SIZE, reg_sys, 0, "cs_msr" }, + /* REG_ESPMSR_INDEX : SYSENTER_ESP_MSR : 91 */ + { REG_DWORD_SIZE, reg_sys, 0, "esp_msr" }, + /* REG_EIPMSR_INDEX : SYSENTER_EIP_MSR : 92 */ + { REG_DWORD_SIZE, reg_sys, 0, "eip_msr" }, + { 0 } + }; + + +static size_t sz_regtable = NUM_X86_REGS + 1; + + +void ia32_handle_register( x86_reg_t *reg, size_t id ) { + unsigned int alias; + if (! id || id > sz_regtable ) { + return; + } + + memset( reg, 0, sizeof(x86_reg_t) ); + + strncpy( reg->name, ia32_reg_table[id].mnemonic, MAX_REGNAME ); + + reg->type = ia32_reg_table[id].type; + reg->size = ia32_reg_table[id].size; + + alias = ia32_reg_table[id].alias; + if ( alias ) { + reg->alias = ia32_reg_aliases[alias].alias; + reg->shift = ia32_reg_aliases[alias].shift; + } + reg->id = id; + + return; +} + +size_t ia32_true_register_id( size_t id ) { + size_t reg; + + if (! id || id > sz_regtable ) { + return 0; + } + + reg = id; + if (ia32_reg_table[reg].alias) { + reg = ia32_reg_aliases[ia32_reg_table[reg].alias].alias; + } + return reg; +} diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_reg.h b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_reg.h new file mode 100644 index 000000000..fbbc77a17 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_reg.h @@ -0,0 +1,41 @@ +#ifndef IA32_REG_H +#define IA32_REG_H + +#include /* for size_t */ +#include "libdis.h" /* for x86_reg_t */ + +/* NOTE these are used in opcode tables for hard-coded registers */ +#define REG_DWORD_OFFSET 1 /* 0 + 1 */ +#define REG_ECX_INDEX 2 /* 0 + 1 + 1 */ +#define REG_ESP_INDEX 5 /* 0 + 4 + 1 */ +#define REG_EBP_INDEX 6 /* 0 + 5 + 1 */ +#define REG_ESI_INDEX 7 /* 0 + 6 + 1 */ +#define REG_EDI_INDEX 8 /* 0 + 7 + 1 */ +#define REG_WORD_OFFSET 9 /* 1 * 8 + 1 */ +#define REG_BYTE_OFFSET 17 /* 2 * 8 + 1 */ +#define REG_MMX_OFFSET 25 /* 3 * 8 + 1 */ +#define REG_SIMD_OFFSET 33 /* 4 * 8 + 1 */ +#define REG_DEBUG_OFFSET 41 /* 5 * 8 + 1 */ +#define REG_CTRL_OFFSET 49 /* 6 * 8 + 1 */ +#define REG_TEST_OFFSET 57 /* 7 * 8 + 1 */ +#define REG_SEG_OFFSET 65 /* 8 * 8 + 1 */ +#define REG_LDTR_INDEX 71 /* 8 * 8 + 1 + 1 */ +#define REG_GDTR_INDEX 72 /* 8 * 8 + 2 + 1 */ +#define REG_FPU_OFFSET 73 /* 9 * 8 + 1 */ +#define REG_FLAGS_INDEX 81 /* 10 * 8 + 1 */ +#define REG_FPCTRL_INDEX 82 /* 10 * 8 + 1 + 1 */ +#define REG_FPSTATUS_INDEX 83 /* 10 * 8 + 2 + 1 */ +#define REG_FPTAG_INDEX 84 /* 10 * 8 + 3 + 1 */ +#define REG_EIP_INDEX 85 /* 10 * 8 + 4 + 1 */ +#define REG_IP_INDEX 86 /* 10 * 8 + 5 + 1 */ +#define REG_IDTR_INDEX 87 /* 10 * 8 + 6 + 1 */ +#define REG_MXCSG_INDEX 88 /* 10 * 8 + 7 + 1 */ +#define REG_TR_INDEX 89 /* 10 * 8 + 8 + 1 */ +#define REG_CSMSR_INDEX 90 /* 10 * 8 + 9 + 1 */ +#define REG_ESPMSR_INDEX 91 /* 10 * 8 + 10 + 1 */ +#define REG_EIPMSR_INDEX 92 /* 10 * 8 + 11 + 1 */ + +void ia32_handle_register( x86_reg_t *reg, size_t id ); +size_t ia32_true_register_id( size_t id ); + +#endif diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_settings.c b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_settings.c new file mode 100644 index 000000000..b578e3448 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_settings.c @@ -0,0 +1,13 @@ +#include "libdis.h" +#include "ia32_settings.h" +#include "ia32_reg.h" +#include "ia32_insn.h" + +ia32_settings_t ia32_settings = { + 1, 0xF4, + MAX_INSTRUCTION_SIZE, + 4, 4, 8, 4, 8, + REG_ESP_INDEX, REG_EBP_INDEX, REG_EIP_INDEX, REG_FLAGS_INDEX, + REG_DWORD_OFFSET, REG_SEG_OFFSET, REG_FPU_OFFSET, + opt_none +}; diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_settings.h b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_settings.h new file mode 100644 index 000000000..769c0e9fa --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/ia32_settings.h @@ -0,0 +1,27 @@ +#ifndef IA32_SETTINGS_H +#define IA32_SETTINGS_H + +#include "libdis.h" + +typedef struct { + /* options */ + unsigned char endian, /* 0 = big, 1 = little */ + wc_byte, /* wildcard byte */ + max_insn, /* max insn size */ + sz_addr, /* default address size */ + sz_oper, /* default operand size */ + sz_byte, /* # bits in byte */ + sz_word, /* # bytes in machine word */ + sz_dword; /* # bytes in machine dword */ + unsigned int id_sp_reg, /* id of stack pointer */ + id_fp_reg, /* id of frame pointer */ + id_ip_reg, /* id of instruction pointer */ + id_flag_reg, /* id of flags register */ + offset_gen_regs, /* start of general regs */ + offset_seg_regs, /* start of segment regs */ + offset_fpu_regs; /* start of floating point regs */ + /* user-controlled settings */ + enum x86_options options; +} ia32_settings_t; + +#endif diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/libdis.h b/shared/sentry/external/breakpad/src/third_party/libdisasm/libdis.h new file mode 100644 index 000000000..83a88612a --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/libdis.h @@ -0,0 +1,832 @@ +#ifndef LIBDISASM_H +#define LIBDISASM_H + +#include + +/* 'NEW" types + * __________________________________________________________________________*/ +#ifndef LIBDISASM_QWORD_H /* do not interfere with qword.h */ + #define LIBDISASM_QWORD_H + #ifdef _MSC_VER + typedef __int64 qword_t; + #else + typedef int64_t qword_t; + #endif +#endif + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* 'NEW" x86 API + * __________________________________________________________________________*/ + + +/* ========================================= Error Reporting */ +/* REPORT CODES + * These are passed to a reporter function passed at initialization. + * Each code determines the type of the argument passed to the reporter; + * this allows the report to recover from errors, or just log them. + */ +enum x86_report_codes { + report_disasm_bounds, /* RVA OUT OF BOUNDS : The disassembler could + not disassemble the supplied RVA as it is + out of the range of the buffer. The + application should store the address and + attempt to determine what section of the + binary it is in, then disassemble the + address from the bytes in that section. + data: uint32_t rva */ + report_insn_bounds, /* INSTRUCTION OUT OF BOUNDS: The disassembler + could not disassemble the instruction as + the instruction would require bytes beyond + the end of the current buffer. This usually + indicated garbage bytes at the end of a + buffer, or an incorrectly-sized buffer. + data: uint32_t rva */ + report_invalid_insn, /* INVALID INSTRUCTION: The disassembler could + not disassemble the instruction as it has an + invalid combination of opcodes and operands. + This will stop automated disassembly; the + application can restart the disassembly + after the invalid instruction. + data: uint32_t rva */ + report_unknown +}; + +/* 'arg' is optional arbitrary data provided by the code passing the + * callback -- for example, it could be 'this' or 'self' in OOP code. + * 'code' is provided by libdisasm, it is one of the above + * 'data' is provided by libdisasm and is context-specific, per the enums */ +typedef void (*DISASM_REPORTER)( enum x86_report_codes code, + void *data, void *arg ); + + +/* x86_report_error : Call the register reporter to report an error */ +void x86_report_error( enum x86_report_codes code, void *data ); + +/* ========================================= Libdisasm Management Routines */ +enum x86_options { /* these can be ORed together */ + opt_none= 0, + opt_ignore_nulls=1, /* ignore sequences of > 4 NULL bytes */ + opt_16_bit=2, /* 16-bit/DOS disassembly */ + opt_att_mnemonics=4, /* use AT&T syntax names for alternate opcode mnemonics */ +}; + +/* management routines */ +/* 'arg' is caller-specific data which is passed as the first argument + * to the reporter callback routine */ +int x86_init( enum x86_options options, DISASM_REPORTER reporter, void *arg); +void x86_set_reporter( DISASM_REPORTER reporter, void *arg); +void x86_set_options( enum x86_options options ); +enum x86_options x86_get_options( void ); +int x86_cleanup(void); + + +/* ========================================= Instruction Representation */ +/* these defines are only intended for use in the array decl's */ +#define MAX_REGNAME 8 + +#define MAX_PREFIX_STR 32 +#define MAX_MNEM_STR 16 +#define MAX_INSN_SIZE 20 /* same as in i386.h */ +#define MAX_OP_STRING 32 /* max possible operand size in string form */ +#define MAX_OP_RAW_STRING 64 /* max possible operand size in raw form */ +#define MAX_OP_XML_STRING 256 /* max possible operand size in xml form */ +#define MAX_NUM_OPERANDS 8 /* max # implicit and explicit operands */ +/* in these, the '2 *' is arbitrary: the max # of operands should require + * more space than the rest of the insn */ +#define MAX_INSN_STRING 512 /* 2 * 8 * MAX_OP_STRING */ +#define MAX_INSN_RAW_STRING 1024 /* 2 * 8 * MAX_OP_RAW_STRING */ +#define MAX_INSN_XML_STRING 4096 /* 2 * 8 * MAX_OP_XML_STRING */ + +enum x86_reg_type { /* NOTE: these may be ORed together */ + reg_gen = 0x00001, /* general purpose */ + reg_in = 0x00002, /* incoming args, ala RISC */ + reg_out = 0x00004, /* args to calls, ala RISC */ + reg_local = 0x00008, /* local vars, ala RISC */ + reg_fpu = 0x00010, /* FPU data register */ + reg_seg = 0x00020, /* segment register */ + reg_simd = 0x00040, /* SIMD/MMX reg */ + reg_sys = 0x00080, /* restricted/system register */ + reg_sp = 0x00100, /* stack pointer */ + reg_fp = 0x00200, /* frame pointer */ + reg_pc = 0x00400, /* program counter */ + reg_retaddr = 0x00800, /* return addr for func */ + reg_cond = 0x01000, /* condition code / flags */ + reg_zero = 0x02000, /* zero register, ala RISC */ + reg_ret = 0x04000, /* return value */ + reg_src = 0x10000, /* array/rep source */ + reg_dest = 0x20000, /* array/rep destination */ + reg_count = 0x40000 /* array/rep/loop counter */ +}; + +/* x86_reg_t : an X86 CPU register */ +typedef struct { + char name[MAX_REGNAME]; + enum x86_reg_type type; /* what register is used for */ + unsigned int size; /* size of register in bytes */ + unsigned int id; /* register ID #, for quick compares */ + unsigned int alias; /* ID of reg this is an alias for */ + unsigned int shift; /* amount to shift aliased reg by */ +} x86_reg_t; + +/* x86_ea_t : an X86 effective address (address expression) */ +typedef struct { + unsigned int scale; /* scale factor */ + x86_reg_t index, base; /* index, base registers */ + int32_t disp; /* displacement */ + char disp_sign; /* is negative? 1/0 */ + char disp_size; /* 0, 1, 2, 4 */ +} x86_ea_t; + +/* x86_absolute_t : an X86 segment:offset address (descriptor) */ +typedef struct { + unsigned short segment; /* loaded directly into CS */ + union { + unsigned short off16; /* loaded directly into IP */ + uint32_t off32; /* loaded directly into EIP */ + } offset; +} x86_absolute_t; + +enum x86_op_type { /* mutually exclusive */ + op_unused = 0, /* empty/unused operand: should never occur */ + op_register = 1, /* CPU register */ + op_immediate = 2, /* Immediate Value */ + op_relative_near = 3, /* Relative offset from IP */ + op_relative_far = 4, /* Relative offset from IP */ + op_absolute = 5, /* Absolute address (ptr16:32) */ + op_expression = 6, /* Address expression (scale/index/base/disp) */ + op_offset = 7, /* Offset from start of segment (m32) */ + op_unknown +}; + +#define x86_optype_is_address( optype ) \ + ( optype == op_absolute || optype == op_offset ) +#define x86_optype_is_relative( optype ) \ + ( optype == op_relative_near || optype == op_relative_far ) +#define x86_optype_is_memory( optype ) \ + ( optype > op_immediate && optype < op_unknown ) + +enum x86_op_datatype { /* these use Intel's lame terminology */ + op_byte = 1, /* 1 byte integer */ + op_word = 2, /* 2 byte integer */ + op_dword = 3, /* 4 byte integer */ + op_qword = 4, /* 8 byte integer */ + op_dqword = 5, /* 16 byte integer */ + op_sreal = 6, /* 4 byte real (single real) */ + op_dreal = 7, /* 8 byte real (double real) */ + op_extreal = 8, /* 10 byte real (extended real) */ + op_bcd = 9, /* 10 byte binary-coded decimal */ + op_ssimd = 10, /* 16 byte : 4 packed single FP (SIMD, MMX) */ + op_dsimd = 11, /* 16 byte : 2 packed double FP (SIMD, MMX) */ + op_sssimd = 12, /* 4 byte : scalar single FP (SIMD, MMX) */ + op_sdsimd = 13, /* 8 byte : scalar double FP (SIMD, MMX) */ + op_descr32 = 14, /* 6 byte Intel descriptor 2:4 */ + op_descr16 = 15, /* 4 byte Intel descriptor 2:2 */ + op_pdescr32 = 16, /* 6 byte Intel pseudo-descriptor 32:16 */ + op_pdescr16 = 17, /* 6 byte Intel pseudo-descriptor 8:24:16 */ + op_bounds16 = 18, /* signed 16:16 lower:upper bounds */ + op_bounds32 = 19, /* signed 32:32 lower:upper bounds */ + op_fpuenv16 = 20, /* 14 byte FPU control/environment data */ + op_fpuenv32 = 21, /* 28 byte FPU control/environment data */ + op_fpustate16 = 22, /* 94 byte FPU state (env & reg stack) */ + op_fpustate32 = 23, /* 108 byte FPU state (env & reg stack) */ + op_fpregset = 24, /* 512 bytes: register set */ + op_fpreg = 25, /* FPU register */ + op_none = 0xFF, /* operand without a datatype (INVLPG) */ +}; + +enum x86_op_access { /* ORed together */ + op_read = 1, + op_write = 2, + op_execute = 4 +}; + +enum x86_op_flags { /* ORed together, but segs are mutually exclusive */ + op_signed = 1, /* signed integer */ + op_string = 2, /* possible string or array */ + op_constant = 4, /* symbolic constant */ + op_pointer = 8, /* operand points to a memory address */ + op_sysref = 0x010, /* operand is a syscall number */ + op_implied = 0x020, /* operand is implicit in the insn */ + op_hardcode = 0x40, /* operand is hardcoded in insn definition */ + /* NOTE: an 'implied' operand is one which can be considered a side + * effect of the insn, e.g. %esp being modified by PUSH or POP. A + * 'hard-coded' operand is one which is specified in the instruction + * definition, e.g. %es:%edi in MOVSB or 1 in ROL Eb, 1. The difference + * is that hard-coded operands are printed by disassemblers and are + * required to re-assemble, while implicit operands are invisible. */ + op_es_seg = 0x100, /* ES segment override */ + op_cs_seg = 0x200, /* CS segment override */ + op_ss_seg = 0x300, /* SS segment override */ + op_ds_seg = 0x400, /* DS segment override */ + op_fs_seg = 0x500, /* FS segment override */ + op_gs_seg = 0x600 /* GS segment override */ +}; + +/* x86_op_t : an X86 instruction operand */ +typedef struct { + enum x86_op_type type; /* operand type */ + enum x86_op_datatype datatype; /* operand size */ + enum x86_op_access access; /* operand access [RWX] */ + enum x86_op_flags flags; /* misc flags */ + union { + /* sizeof will have to work on these union members! */ + /* immediate values */ + char sbyte; + short sword; + int32_t sdword; + qword_t sqword; + unsigned char byte; + unsigned short word; + uint32_t dword; + qword_t qword; + float sreal; + double dreal; + /* misc large/non-native types */ + unsigned char extreal[10]; + unsigned char bcd[10]; + qword_t dqword[2]; + unsigned char simd[16]; + unsigned char fpuenv[28]; + /* offset from segment */ + uint32_t offset; + /* ID of CPU register */ + x86_reg_t reg; + /* offsets from current insn */ + char relative_near; + int32_t relative_far; + /* segment:offset */ + x86_absolute_t absolute; + /* effective address [expression] */ + x86_ea_t expression; + } data; + /* this is needed to make formatting operands more sane */ + void * insn; /* pointer to x86_insn_t owning operand */ +} x86_op_t; + +/* Linked list of x86_op_t; provided for manual traversal of the operand + * list in an insn. Users wishing to add operands to this list, e.g. to add + * implicit operands, should use x86_operand_new in x86_operand_list.h */ +typedef struct x86_operand_list { + x86_op_t op; + struct x86_operand_list *next; +} x86_oplist_t; + +enum x86_insn_group { + insn_none = 0, /* invalid instruction */ + insn_controlflow = 1, + insn_arithmetic = 2, + insn_logic = 3, + insn_stack = 4, + insn_comparison = 5, + insn_move = 6, + insn_string = 7, + insn_bit_manip = 8, + insn_flag_manip = 9, + insn_fpu = 10, + insn_interrupt = 13, + insn_system = 14, + insn_other = 15 +}; + +enum x86_insn_type { + insn_invalid = 0, /* invalid instruction */ + /* insn_controlflow */ + insn_jmp = 0x1001, + insn_jcc = 0x1002, + insn_call = 0x1003, + insn_callcc = 0x1004, + insn_return = 0x1005, + /* insn_arithmetic */ + insn_add = 0x2001, + insn_sub = 0x2002, + insn_mul = 0x2003, + insn_div = 0x2004, + insn_inc = 0x2005, + insn_dec = 0x2006, + insn_shl = 0x2007, + insn_shr = 0x2008, + insn_rol = 0x2009, + insn_ror = 0x200A, + /* insn_logic */ + insn_and = 0x3001, + insn_or = 0x3002, + insn_xor = 0x3003, + insn_not = 0x3004, + insn_neg = 0x3005, + /* insn_stack */ + insn_push = 0x4001, + insn_pop = 0x4002, + insn_pushregs = 0x4003, + insn_popregs = 0x4004, + insn_pushflags = 0x4005, + insn_popflags = 0x4006, + insn_enter = 0x4007, + insn_leave = 0x4008, + /* insn_comparison */ + insn_test = 0x5001, + insn_cmp = 0x5002, + /* insn_move */ + insn_mov = 0x6001, /* move */ + insn_movcc = 0x6002, /* conditional move */ + insn_xchg = 0x6003, /* exchange */ + insn_xchgcc = 0x6004, /* conditional exchange */ + /* insn_string */ + insn_strcmp = 0x7001, + insn_strload = 0x7002, + insn_strmov = 0x7003, + insn_strstore = 0x7004, + insn_translate = 0x7005, /* xlat */ + /* insn_bit_manip */ + insn_bittest = 0x8001, + insn_bitset = 0x8002, + insn_bitclear = 0x8003, + /* insn_flag_manip */ + insn_clear_carry = 0x9001, + insn_clear_zero = 0x9002, + insn_clear_oflow = 0x9003, + insn_clear_dir = 0x9004, + insn_clear_sign = 0x9005, + insn_clear_parity = 0x9006, + insn_set_carry = 0x9007, + insn_set_zero = 0x9008, + insn_set_oflow = 0x9009, + insn_set_dir = 0x900A, + insn_set_sign = 0x900B, + insn_set_parity = 0x900C, + insn_tog_carry = 0x9010, + insn_tog_zero = 0x9020, + insn_tog_oflow = 0x9030, + insn_tog_dir = 0x9040, + insn_tog_sign = 0x9050, + insn_tog_parity = 0x9060, + /* insn_fpu */ + insn_fmov = 0xA001, + insn_fmovcc = 0xA002, + insn_fneg = 0xA003, + insn_fabs = 0xA004, + insn_fadd = 0xA005, + insn_fsub = 0xA006, + insn_fmul = 0xA007, + insn_fdiv = 0xA008, + insn_fsqrt = 0xA009, + insn_fcmp = 0xA00A, + insn_fcos = 0xA00C, + insn_fldpi = 0xA00D, + insn_fldz = 0xA00E, + insn_ftan = 0xA00F, + insn_fsine = 0xA010, + insn_fsys = 0xA020, + /* insn_interrupt */ + insn_int = 0xD001, + insn_intcc = 0xD002, /* not present in x86 ISA */ + insn_iret = 0xD003, + insn_bound = 0xD004, + insn_debug = 0xD005, + insn_trace = 0xD006, + insn_invalid_op = 0xD007, + insn_oflow = 0xD008, + /* insn_system */ + insn_halt = 0xE001, + insn_in = 0xE002, /* input from port/bus */ + insn_out = 0xE003, /* output to port/bus */ + insn_cpuid = 0xE004, + /* insn_other */ + insn_nop = 0xF001, + insn_bcdconv = 0xF002, /* convert to or from BCD */ + insn_szconv = 0xF003 /* change size of operand */ +}; + +/* These flags specify special characteristics of the instruction, such as + * whether the inatruction is privileged or whether it serializes the + * pipeline. + * NOTE : These may not be accurate for all instructions; updates to the + * opcode tables have not been completed. */ +enum x86_insn_note { + insn_note_ring0 = 1, /* Only available in ring 0 */ + insn_note_smm = 2, /* "" in System Management Mode */ + insn_note_serial = 4, /* Serializing instruction */ + insn_note_nonswap = 8, /* Does not swap arguments in att-style formatting */ + insn_note_nosuffix = 16, /* Does not have size suffix in att-style formatting */ +}; + +/* This specifies what effects the instruction has on the %eflags register */ +enum x86_flag_status { + insn_carry_set = 0x1, /* CF */ + insn_zero_set = 0x2, /* ZF */ + insn_oflow_set = 0x4, /* OF */ + insn_dir_set = 0x8, /* DF */ + insn_sign_set = 0x10, /* SF */ + insn_parity_set = 0x20, /* PF */ + insn_carry_or_zero_set = 0x40, + insn_zero_set_or_sign_ne_oflow = 0x80, + insn_carry_clear = 0x100, + insn_zero_clear = 0x200, + insn_oflow_clear = 0x400, + insn_dir_clear = 0x800, + insn_sign_clear = 0x1000, + insn_parity_clear = 0x2000, + insn_sign_eq_oflow = 0x4000, + insn_sign_ne_oflow = 0x8000 +}; + +/* The CPU model in which the insturction first appeared; this can be used + * to mask out instructions appearing in earlier or later models or to + * check the portability of a binary. + * NOTE : These may not be accurate for all instructions; updates to the + * opcode tables have not been completed. */ +enum x86_insn_cpu { + cpu_8086 = 1, /* Intel */ + cpu_80286 = 2, + cpu_80386 = 3, + cpu_80387 = 4, + cpu_80486 = 5, + cpu_pentium = 6, + cpu_pentiumpro = 7, + cpu_pentium2 = 8, + cpu_pentium3 = 9, + cpu_pentium4 = 10, + cpu_k6 = 16, /* AMD */ + cpu_k7 = 32, + cpu_athlon = 48 +}; + +/* CPU ISA subsets: These are derived from the Instruction Groups in + * Intel Vol 1 Chapter 5; they represent subsets of the IA32 ISA but + * do not reflect the 'type' of the instruction in the same way that + * x86_insn_group does. In short, these are AMD/Intel's somewhat useless + * designations. + * NOTE : These may not be accurate for all instructions; updates to the + * opcode tables have not been completed. */ +enum x86_insn_isa { + isa_gp = 1, /* general purpose */ + isa_fp = 2, /* floating point */ + isa_fpumgt = 3, /* FPU/SIMD management */ + isa_mmx = 4, /* Intel MMX */ + isa_sse1 = 5, /* Intel SSE SIMD */ + isa_sse2 = 6, /* Intel SSE2 SIMD */ + isa_sse3 = 7, /* Intel SSE3 SIMD */ + isa_3dnow = 8, /* AMD 3DNow! SIMD */ + isa_sys = 9 /* system instructions */ +}; + +enum x86_insn_prefix { + insn_no_prefix = 0, + insn_rep_zero = 1, /* REPZ and REPE */ + insn_rep_notzero = 2, /* REPNZ and REPNZ */ + insn_lock = 4 /* LOCK: */ +}; + +/* TODO: maybe provide insn_new/free(), and have disasm return new insn_t */ +/* x86_insn_t : an X86 instruction */ +typedef struct { + /* information about the instruction */ + uint32_t addr; /* load address */ + uint32_t offset; /* offset into file/buffer */ + enum x86_insn_group group; /* meta-type, e.g. INS_EXEC */ + enum x86_insn_type type; /* type, e.g. INS_BRANCH */ + enum x86_insn_note note; /* note, e.g. RING0 */ + unsigned char bytes[MAX_INSN_SIZE]; + unsigned char size; /* size of insn in bytes */ + /* 16/32-bit mode settings */ + unsigned char addr_size; /* default address size : 2 or 4 */ + unsigned char op_size; /* default operand size : 2 or 4 */ + /* CPU/instruction set */ + enum x86_insn_cpu cpu; + enum x86_insn_isa isa; + /* flags */ + enum x86_flag_status flags_set; /* flags set or tested by insn */ + enum x86_flag_status flags_tested; + /* stack */ + unsigned char stack_mod; /* 0 or 1 : is the stack modified? */ + int32_t stack_mod_val; /* val stack is modified by if known */ + + /* the instruction proper */ + enum x86_insn_prefix prefix; /* prefixes ORed together */ + char prefix_string[MAX_PREFIX_STR]; /* prefixes [might be truncated] */ + char mnemonic[MAX_MNEM_STR]; + x86_oplist_t *operands; /* list of explicit/implicit operands */ + size_t operand_count; /* total number of operands */ + size_t explicit_count; /* number of explicit operands */ + /* convenience fields for user */ + void *block; /* code block containing this insn */ + void *function; /* function containing this insn */ + int tag; /* tag the insn as seen/processed */ +} x86_insn_t; + + +/* returns 0 if an instruction is invalid, 1 if valid */ +int x86_insn_is_valid( x86_insn_t *insn ); + +/* DISASSEMBLY ROUTINES + * Canonical order of arguments is + * (buf, buf_len, buf_rva, offset, len, insn, func, arg, resolve_func) + * ...but of course all of these are not used at the same time. + */ + + +/* Function prototype for caller-supplied callback routine + * These callbacks are intended to process 'insn' further, e.g. by + * adding it to a linked list, database, etc */ +typedef void (*DISASM_CALLBACK)( x86_insn_t *insn, void * arg ); + +/* Function prototype for caller-supplied address resolver. + * This routine is used to determine the rva to disassemble next, given + * the 'dest' operand of a jump/call. This allows the caller to resolve + * jump/call targets stored in a register or on the stack, and also allows + * the caller to prevent endless loops by checking if an address has + * already been disassembled. If an address cannot be resolved from the + * operand, or if the address has already been disassembled, this routine + * should return -1; in all other cases the RVA to be disassembled next + * should be returned. */ +typedef int32_t (*DISASM_RESOLVER)( x86_op_t *op, x86_insn_t * current_insn, + void *arg ); + + +/* x86_disasm: Disassemble a single instruction from a buffer of bytes. + * Returns size of instruction in bytes. + * Caller is responsible for calling x86_oplist_free() on + * a reused "insn" to avoid leaking memory when calling this + * function repeatedly. + * buf : Buffer of bytes to disassemble + * buf_len : Length of the buffer + * buf_rva : Load address of the start of the buffer + * offset : Offset in buffer to disassemble + * insn : Structure to fill with disassembled instruction + */ +unsigned int x86_disasm( unsigned char *buf, unsigned int buf_len, + uint32_t buf_rva, unsigned int offset, + x86_insn_t * insn ); + +/* x86_disasm_range: Sequential disassembly of a range of bytes in a buffer, + * invoking a callback function each time an instruction + * is successfully disassembled. The 'range' refers to the + * bytes between 'offset' and 'offset + len' in the buffer; + * 'len' is assumed to be less than the length of the buffer. + * Returns number of instructions processed. + * buf : Buffer of bytes to disassemble (e.g. .text section) + * buf_rva : Load address of buffer (e.g. ELF Virtual Address) + * offset : Offset in buffer to start disassembly at + * len : Number of bytes to disassemble + * func : Callback function to invoke (may be NULL) + * arg : Arbitrary data to pass to callback (may be NULL) + */ +unsigned int x86_disasm_range( unsigned char *buf, uint32_t buf_rva, + unsigned int offset, unsigned int len, + DISASM_CALLBACK func, void *arg ); + +/* x86_disasm_forward: Flow-of-execution disassembly of the bytes in a buffer, + * invoking a callback function each time an instruction + * is successfully disassembled. + * buf : Buffer to disassemble (e.g. .text section) + * buf_len : Number of bytes in buffer + * buf_rva : Load address of buffer (e.g. ELF Virtual Address) + * offset : Offset in buffer to start disassembly at (e.g. entry point) + * func : Callback function to invoke (may be NULL) + * arg : Arbitrary data to pass to callback (may be NULL) + * resolver: Caller-supplied address resolver. If no resolver is + * supplied, a default internal one is used -- however the + * internal resolver does NOT catch loops and could end up + * disassembling forever.. + * r_arg : Arbitrary data to pass to resolver (may be NULL) + */ +unsigned int x86_disasm_forward( unsigned char *buf, unsigned int buf_len, + uint32_t buf_rva, unsigned int offset, + DISASM_CALLBACK func, void *arg, + DISASM_RESOLVER resolver, void *r_arg ); + +/* Instruction operands: these are stored as a list of explicit and + * implicit operands. It is recommended that the 'foreach' routines + * be used to when examining operands for purposes of data flow analysis */ + +/* Operand FOREACH callback: 'arg' is an abritrary parameter passed to the + * foreach routine, 'insn' is the x86_insn_t whose operands are being + * iterated over, and 'op' is the current x86_op_t */ +typedef void (*x86_operand_fn)(x86_op_t *op, x86_insn_t *insn, void *arg); + +/* FOREACH types: these are used to limit the foreach results to + * operands which match a certain "type" (implicit or explicit) + * or which are accessed in certain ways (e.g. read or write). Note + * that this operates on the operand list of single instruction, so + * specifying the 'real' operand type (register, memory, etc) is not + * useful. Note also that by definition Execute Access implies Read + * Access and implies Not Write Access. + * The "type" (implicit or explicit) and the access method can + * be ORed together, e.g. op_wo | op_explicit */ +enum x86_op_foreach_type { + op_any = 0, /* ALL operands (explicit, implicit, rwx) */ + op_dest = 1, /* operands with Write access */ + op_src = 2, /* operands with Read access */ + op_ro = 3, /* operands with Read but not Write access */ + op_wo = 4, /* operands with Write but not Read access */ + op_xo = 5, /* operands with Execute access */ + op_rw = 6, /* operands with Read AND Write access */ + op_implicit = 0x10, /* operands that are implied by the opcode */ + op_explicit = 0x20 /* operands that are not side-effects */ +}; + + +/* free the operand list associated with an instruction -- useful for + * preventing memory leaks when free()ing an x86_insn_t */ +void x86_oplist_free( x86_insn_t *insn ); + +/* Operand foreach: invokes 'func' with 'insn' and 'arg' as arguments. The + * 'type' parameter is used to select only operands matching specific + * criteria. */ +int x86_operand_foreach( x86_insn_t *insn, x86_operand_fn func, void *arg, + enum x86_op_foreach_type type); + +/* convenience routine: returns count of operands matching 'type' */ +size_t x86_operand_count( x86_insn_t *insn, enum x86_op_foreach_type type ); + +/* accessor functions for the operands */ +x86_op_t * x86_operand_1st( x86_insn_t *insn ); +x86_op_t * x86_operand_2nd( x86_insn_t *insn ); +x86_op_t * x86_operand_3rd( x86_insn_t *insn ); + +/* these allow libdisasm 2.0 accessor functions to still be used */ +#define x86_get_dest_operand( insn ) x86_operand_1st( insn ) +#define x86_get_src_operand( insn ) x86_operand_2nd( insn ) +#define x86_get_imm_operand( insn ) x86_operand_3rd( insn ) + +/* get size of operand data in bytes */ +unsigned int x86_operand_size( x86_op_t *op ); + +/* Operand Convenience Routines: the following three routines are common + * operations on operands, intended to ease the burden of the programmer. */ + +/* Get Address: return the value of an offset operand, or the offset of + * a segment:offset absolute address */ +uint32_t x86_get_address( x86_insn_t *insn ); + +/* Get Relative Offset: return as a sign-extended int32_t the near or far + * relative offset operand, or 0 if there is none. There can be only one + * relaive offset operand in an instruction. */ +int32_t x86_get_rel_offset( x86_insn_t *insn ); + +/* Get Branch Target: return the x86_op_t containing the target of + * a jump or call operand, or NULL if there is no branch target. + * Internally, a 'branch target' is defined as any operand with + * Execute Access set. There can be only one branch target per instruction. */ +x86_op_t * x86_get_branch_target( x86_insn_t *insn ); + +/* Get Immediate: return the x86_op_t containing the immediate operand + * for this instruction, or NULL if there is no immediate operand. There + * can be only one immediate operand per instruction */ +x86_op_t * x86_get_imm( x86_insn_t *insn ); + +/* Get Raw Immediate Data: returns a pointer to the immediate data encoded + * in the instruction. This is useful for large data types [>32 bits] currently + * not supported by libdisasm, or for determining if the disassembler + * screwed up the conversion of the immediate data. Note that 'imm' in this + * context refers to immediate data encoded at the end of an instruction as + * detailed in the Intel Manual Vol II Chapter 2; it does not refer to the + * 'op_imm' operand (the third operand in instructions like 'mul' */ +unsigned char * x86_get_raw_imm( x86_insn_t *insn ); + + +/* More accessor fuctions, this time for user-defined info... */ +/* set the address (usually RVA) of the insn */ +void x86_set_insn_addr( x86_insn_t *insn, uint32_t addr ); + +/* set the offset (usually offset into file) of the insn */ +void x86_set_insn_offset( x86_insn_t *insn, unsigned int offset ); + +/* set a pointer to the function owning the instruction. The + * type of 'func' is user-defined; libdisasm does not use the func field. */ +void x86_set_insn_function( x86_insn_t *insn, void * func ); + +/* set a pointer to the block of code owning the instruction. The + * type of 'block' is user-defined; libdisasm does not use the block field. */ +void x86_set_insn_block( x86_insn_t *insn, void * block ); + +/* instruction tagging: these routines allow the programmer to mark + * instructions as "seen" in a DFS, for example. libdisasm does not use + * the tag field.*/ +/* set insn->tag to 1 */ +void x86_tag_insn( x86_insn_t *insn ); +/* set insn->tag to 0 */ +void x86_untag_insn( x86_insn_t *insn ); +/* return insn->tag */ +int x86_insn_is_tagged( x86_insn_t *insn ); + + +/* Disassembly formats: + * AT&T is standard AS/GAS-style: "mnemonic\tsrc, dest, imm" + * Intel is standard MASM/NASM/TASM: "mnemonic\tdest,src, imm" + * Native is tab-delimited: "RVA\tbytes\tmnemonic\tdest\tsrc\timm" + * XML is your typical ... + * Raw is addr|offset|size|bytes|prefix... see libdisasm_formats.7 + */ +enum x86_asm_format { + unknown_syntax = 0, /* never use! */ + native_syntax, /* header: 35 bytes */ + intel_syntax, /* header: 23 bytes */ + att_syntax, /* header: 23 bytes */ + xml_syntax, /* header: 679 bytes */ + raw_syntax /* header: 172 bytes */ +}; + +/* format (sprintf) an operand into 'buf' using specified syntax */ +int x86_format_operand(x86_op_t *op, char *buf, int len, + enum x86_asm_format format); + +/* format (sprintf) an instruction mnemonic into 'buf' using specified syntax */ +int x86_format_mnemonic(x86_insn_t *insn, char *buf, int len, + enum x86_asm_format format); + +/* format (sprintf) an instruction into 'buf' using specified syntax; + * this includes formatting all operands */ +int x86_format_insn(x86_insn_t *insn, char *buf, int len, enum x86_asm_format); + +/* fill 'buf' with a description of the format's syntax */ +int x86_format_header( char *buf, int len, enum x86_asm_format format); + +/* Endianness of an x86 CPU : 0 is big, 1 is little; always returns 1 */ +unsigned int x86_endian(void); + +/* Default address and operand size in bytes */ +unsigned int x86_addr_size(void); +unsigned int x86_op_size(void); + +/* Size of a machine word in bytes */ +unsigned int x86_word_size(void); + +/* maximum size of a code instruction */ +#define x86_max_inst_size(x) x86_max_insn_size(x) +unsigned int x86_max_insn_size(void); + +/* register IDs of Stack, Frame, Instruction pointer and Flags register */ +unsigned int x86_sp_reg(void); +unsigned int x86_fp_reg(void); +unsigned int x86_ip_reg(void); +unsigned int x86_flag_reg(void); + +/* fill 'reg' struct with details of register 'id' */ +void x86_reg_from_id( unsigned int id, x86_reg_t * reg ); + +/* convenience macro demonstrating how to get an aliased register; proto is + * void x86_get_aliased_reg( x86_reg_t *alias_reg, x86_reg_t *output_reg ) + * where 'alias_reg' is a reg operand and 'output_reg' is filled with the + * register that the operand is an alias for */ +#define x86_get_aliased_reg( alias_reg, output_reg ) \ + x86_reg_from_id( alias_reg->alias, output_reg ) + + +/* ================================== Invariant Instruction Representation */ +/* Invariant instructions are used for generating binary signatures; + * the instruction is modified so that all variant bytes in an instruction + * are replaced with a wildcard byte. + * + * A 'variant byte' is one that is expected to be modified by either the + * static or the dynamic linker: for example, an address encoded in an + * instruction. + * + * By comparing the invariant representation of one instruction [or of a + * sequence of instructions] with the invariant representation of another, + * one determine whether the two invariant representations are from the same + * relocatable object [.o] file. Thus one can use binary signatures [which + * are just sequences of invariant instruction representations] to look for + * library routines which have been statically-linked into a binary. + * + * The invariant routines are faster and smaller than the disassembly + * routines; they can be used to determine the size of an instruction + * without all of the overhead of a full instruction disassembly. + */ + +/* This byte is used to replace variant bytes */ +#define X86_WILDCARD_BYTE 0xF4 + +typedef struct { + enum x86_op_type type; /* operand type */ + enum x86_op_datatype datatype; /* operand size */ + enum x86_op_access access; /* operand access [RWX] */ + enum x86_op_flags flags; /* misc flags */ +} x86_invariant_op_t; + +typedef struct { + unsigned char bytes[64]; /* invariant representation */ + unsigned int size; /* number of bytes in insn */ + enum x86_insn_group group; /* meta-type, e.g. INS_EXEC */ + enum x86_insn_type type; /* type, e.g. INS_BRANCH */ + x86_invariant_op_t operands[3]; /* operands: dest, src, imm */ +} x86_invariant_t; + + +/* return a version of the instruction with the variant bytes masked out */ +size_t x86_invariant_disasm( unsigned char *buf, int buf_len, + x86_invariant_t *inv ); +/* return the size in bytes of the intruction pointed to by 'buf'; + * this used x86_invariant_disasm since it faster than x86_disasm */ +size_t x86_size_disasm( unsigned char *buf, unsigned int buf_len ); + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/libdisasm.gyp b/shared/sentry/external/breakpad/src/third_party/libdisasm/libdisasm.gyp new file mode 100644 index 000000000..5c8dc4586 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/libdisasm.gyp @@ -0,0 +1,67 @@ +# 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. + +{ + 'includes': [ + '../../build/common.gypi', + ], + 'targets': [ + { + 'target_name': 'libdisasm', + 'type': 'static_library', + 'sources': [ + 'ia32_implicit.c', + 'ia32_implicit.h', + 'ia32_insn.c', + 'ia32_insn.h', + 'ia32_invariant.c', + 'ia32_invariant.h', + 'ia32_modrm.c', + 'ia32_modrm.h', + 'ia32_opcode_tables.c', + 'ia32_opcode_tables.h', + 'ia32_operand.c', + 'ia32_operand.h', + 'ia32_reg.c', + 'ia32_reg.h', + 'ia32_settings.c', + 'ia32_settings.h', + 'libdis.h', + 'qword.h', + 'x86_disasm.c', + 'x86_format.c', + 'x86_imm.c', + 'x86_imm.h', + 'x86_insn.c', + 'x86_misc.c', + 'x86_operand_list.c', + 'x86_operand_list.h', + ], + }, + ], +} diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/qword.h b/shared/sentry/external/breakpad/src/third_party/libdisasm/qword.h new file mode 100644 index 000000000..5f0e803c9 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/qword.h @@ -0,0 +1,14 @@ +#ifndef LIBDISASM_QWORD_H +#define LIBDISASM_QWORD_H + +#include + +/* platform independent data types */ + +#ifdef _MSC_VER + typedef __int64 qword_t; +#else + typedef int64_t qword_t; +#endif + +#endif diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/swig/Makefile b/shared/sentry/external/breakpad/src/third_party/libdisasm/swig/Makefile new file mode 100644 index 000000000..44ef486b6 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/swig/Makefile @@ -0,0 +1,70 @@ +# change these values if you need to +SWIG = swig # apt-get install swig ! +GCC = gcc + +CC_FLAGS = -c -fPIC +LD_FLAGS = -shared -L../.. -ldisasm + +BASE_NAME = x86disasm + +export INTERFACE_FILE BASE_NAME SWIG GCC CC_FLAGS LD_FLAGS + +#==================================================== +# TARGETS + +all: swig +dummy: swig swig-python swig-ruby swig-perl swig-tcl install uninstall clean + +swig: swig-python swig-perl +# swig-rub swig-tcl + +swig-python: + cd python && make -f Makefile-swig + +swig-ruby: + cd ruby && make -f Makefile-swig + +swig-perl: + cd perl && make -f Makefile-swig + +swig-tcl: + cd tcl && make -f Makefile-swig + +# ================================================================== +install: install-python install-perl +# install-ruby install-tcl + +install-python: + cd python && sudo make -f Makefile-swig install + +install-ruby: + cd ruby && sudo make -f Makefile-swig install + +install-perl: + cd perl && sudo make -f Makefile-swig install + +install-tcl: + cd tcl && sudo make -f Makefile-swig install + +# ================================================================== +uninstall: uninstall-python +#uninstall-ruby uninstall-perl uninstall-tcl + +uninstall-python: + cd python && sudo make -f Makefile-swig uninstall + +uninstall-ruby: + cd ruby && sudo make -f Makefile-swig uninstall + +uninstall-perl: + cd perl && sudo make -f Makefile-swig uninstall + +uninstall-tcl: + cd tcl && sudo make -f Makefile-swig uninstall + +# ================================================================== +clean: + cd python && make -f Makefile-swig clean + cd ruby && make -f Makefile-swig clean + cd perl && make -f Makefile-swig clean + cd tcl && make -f Makefile-swig clean diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/swig/README b/shared/sentry/external/breakpad/src/third_party/libdisasm/swig/README new file mode 100644 index 000000000..a9fa79ec2 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/swig/README @@ -0,0 +1,128 @@ + Libdisasm SWIG README + +The SWIG utility (www.swig.org) can be used to generate + + +Building SWIG Modules +--------------------- + + make + make install + +Make and Install both build Python, Perl, Ruby, and Tcl modules. If you +do not have one of these languages installed, comment out the relevant +target in the main Makefile. + +Install uses 'sudo' to put files in the correct locations; if you +do not have sudo installed, change the install targets. + +The Module API +-------------- + +The OOP API +----------- + + +The Python Module +----------------- + +To test that the module loads: + + bash# python + >>> import x86disasm + >>> x86disasm.version_string() + '0.21-pre' + >>>^D + bash# + + >>> import x86disasm + >>> import array + >>> disasm = x86disasm.X86_Disasm( ) + >>> tgt = open( "/tmp/a.out", "rb" ) + >>> tgt.seek( 0, 2 ) + >>> size = tgt.tell() + >>> tgt.seek( 0, 0 ) + >>> buf = array.array( 'B' ) + >>> buf.fromfile( tgt, size ) + >>> tgt.close() + >>> data = x86disasm.byteArray( size ) + >>> for i in range( size ): + ... data[i] = buf.pop(0) + ... + >>> del buf + >>> del tgt + >>> insn = disasm.disasm( data, size - 1, 0, 0 ) + >>> insn.format( x86disasm.att_syntax ) + 'jg\t0x00000047' + >>> insn.format( x86disasm.raw_syntax ) + '0x00000000|0x00000000|2|7F 45 |||controlflow|jcc|jg|80386|General Purpose|||zero_clear sign_eq_oflow |0|0|relative|sbyte|00000047|' + >>> ops = insn.operand_list() + >>> node = ops.first() + >>> while node is not None: + ... s = node.op.format(x86disasm.raw_syntax) + ... print s + ... node = ops.next() + ... + relative|sbyte|00000047| + + + + + + +The Perl Module +--------------- + +To test that the module loads: + + bash# perl + use x86disasm; + print x86disasm::version_string() . "\n"; + ^D + 0.21-pre + bash# + +The Ruby Module +--------------- + +To test that the module loads: + + bash# irb + irb(main):001:0> require 'x86disasm' + => true + irb(main):002:0> X86disasm.version_string() + => "0.21-pre" + irb(main):003:0> x = X86disasm::X86_Disasm.new + => # + irb(main):004:0> x.max_register_string() + => 8 + irb(main):003:0> ^D + bash# + +The Tcl Module +--------------- + +To test that the module loads: + + bash# tclsh + % load /usr/lib/tcl8.3/x86disasm.so X86disasm + % version_string + 0.21-pre + % ^D + bash# + + % x86_init 0 NULL NULL + OR + % x86disasm dis + _486b0708_p_x86disasm + % puts "[dis cget -last_error]" + 0 + + + + +The Interface Files +------------------- + + libdisasm.i -- interface file without shadow classes + libdisasm_oop.i -- interface file with shadow classes diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/swig/libdisasm.i b/shared/sentry/external/breakpad/src/third_party/libdisasm/swig/libdisasm.i new file mode 100644 index 000000000..ec1204175 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/swig/libdisasm.i @@ -0,0 +1,508 @@ +%module x86disasm +%{ +#include "../../libdis.h" +#include "../../../config.h" +%} + +%rename(version_string) x86_version_string; +%include "../../libdis.h" +#include "../../../config.h" + +%inline %{ + const char * x86_version_string( void ) { + return PACKAGE_VERSION; + } +%} + +%rename(report_codes) x86_report_codes; +%rename(report_error) x86_report_error; +%rename(options) x86_options; +%rename(init) x86_init; +%rename(set_reporter) x86_set_reporter; +%rename(set_options) x86_set_options; +%rename(options) x86_get_options; +%rename(cleanup) x86_cleanup; +%rename(reg_type) x86_reg_type; +%rename(reg) x86_reg_t; +%rename(eaddr) x86_ea_t; +%rename(op_type) x86_op_type; +%rename(optype_is_address) x86_optype_is_address; +%rename(optype_is_relative) x86_optype_is_relative; +%rename(op_datatype) x86_op_datatype; +%rename(op_access) x86_op_access; +%rename(op_flags) x86_op_flags; +%rename(operand) x86_op_t; +%rename(insn_group) x86_insn_group; +%rename(insn_type) x86_insn_type; +%rename(insn_note) x86_insn_note ; +%rename(flag_status) x86_flag_status; +%rename(insn_cpu) x86_insn_cpu ; +%rename(insn_isa) x86_insn_isa ; +%rename(insn_prefix) x86_insn_prefix ; +%rename(insn) x86_insn_t; +%rename(insn_is_valid) x86_insn_is_valid; +%rename(i_disasm) x86_disasm; +%rename(i_disasm_range) x86_disasm_range; +%rename(i_disasm_forward) x86_disasm_forward; +%rename(insn_operand_count) x86_operand_count; +%rename(insn_operand_1st) x86_operand_1st; +%rename(insn_operand_2nd) x86_operand_2nd; +%rename(insn_operand_3rd) x86_operand_3rd; +%rename(insn_dest_operand) x86_get_dest_operand; +%rename(insn_src_operand) x86_get_src_operand; +%rename(insn_imm_operand) x86_get_imm_operand; +%rename(operand_size) x86_operand_size; +%rename(insn_rel_offset) x86_get_rel_offset; +%rename(insn_branch_target) x86_get_branch_target; +%rename(insn_imm) x86_get_imm; +%rename(insn_raw_imm) x86_get_raw_imm; +%rename(insn_set_addr) x86_set_insn_addr; +%rename(insn_set_offset) x86_set_insn_offset; +%rename(insn_set_function) x86_set_insn_function; +%rename(insn_set_block) x86_set_insn_block; +%rename(insn_tag) x86_tag_insn; +%rename(insn_untag) x86_untag_insn; +%rename(insn_is_tagged) x86_insn_is_tagged; +%rename(asm_format) x86_asm_format; +%rename(operand_format) x86_format_operand; +%rename(insn_format_mnemonic) x86_format_mnemonic; +%rename(insn_format) x86_format_insn; +%rename(header_format) x86_format_header; +%rename(endian) x86_endian; +%rename(size_default_address) x86_addr_size; +%rename(size_default_operand) x86_op_size; +%rename(size_machine_word) x86_word_size; +%rename(size_max_insn) x86_max_insn_size; +%rename(reg_sp) x86_sp_reg; +%rename(reg_fp) x86_fp_reg; +%rename(reg_ip) x86_ip_reg; +%rename(reg_from_id) x86_reg_from_id; +%rename(reg_from_alias) x86_get_aliased_reg; +%rename(invariant_op) x86_invariant_op_t; +%rename(invariant) x86_invariant_t; +%rename(disasm_invariant) x86_invariant_disasm; +%rename(disasm_size) x86_size_disasm; + +%include "carrays.i" + +%array_class( unsigned char, byteArray ); + + +%apply (unsigned char *STRING, int LENGTH) { + (unsigned char *buf, size_t buf_len) +}; + + +%newobject x86_op_copy; +%inline %{ + x86_op_t * x86_op_copy( x86_op_t * src ) { + x86_op_t *op; + + if (! src ) { + return NULL; + } + + op = (x86_op_t *) calloc( sizeof(x86_op_t), 1 ); + if ( op ) { + memcpy( op, src, sizeof(x86_op_t) ); + } + + return op; + } + + typedef struct x86_op_list_node { + x86_op_t *op; + struct x86_op_list_node *next, *prev; + } x86_op_list_node; + + typedef struct x86_op_list { + size_t count; + x86_op_list_node *head, *tail, *curr; + } x86_op_list; + + x86_op_list * x86_op_list_new () { + x86_op_list *list = (x86_op_list *) + calloc( sizeof(x86_op_list), 1 ); + list->count = 0; + return list; + } + + void x86_op_list_free(x86_op_list *list) { + x86_op_list_node *node, *next; + + node = list->head; + while ( node ) { + next = node->next; + /* free( node->insn ); */ + free( node ); + node = next; + } + + free( list ); + } + + x86_op_list_node * x86_op_list_first(x86_op_list *list) { + return list->head; + } + + x86_op_list_node * x86_op_list_last(x86_op_list *list) { + return list->tail; + } + + x86_op_list_node * x86_op_list_next(x86_op_list *list) { + if (! list->curr ) { + list->curr = list->head; + return list->head; + } + + list->curr = list->curr->next; + return list->curr; + } + + x86_op_list_node * x86_op_list_prev(x86_op_list *list) { + if (! list->curr ) { + list->curr = list->tail; + return list->tail; + } + + list->curr = list->curr->prev; + return list->curr; + } + +%} + +%newobject x86_op_list_append; + +%inline %{ + void x86_op_list_append( x86_op_list * list, x86_op_t *op ) { + x86_op_list_node *node = (x86_op_list_node *) + calloc( sizeof(x86_op_list_node) , 1 ); + if (! node ) { + return; + } + + list->count++; + if ( ! list->tail ) { + list->head = list->tail = node; + } else { + list->tail->next = node; + node->prev = list->tail; + list->tail = node; + } + + node->op = x86_op_copy( op ); + } + + x86_oplist_t * x86_op_list_node_copy( x86_oplist_t * list ) { + x86_oplist_t *ptr; + ptr = (x86_oplist_t *) calloc( sizeof(x86_oplist_t), 1 ); + if ( ptr ) { + memcpy( &ptr->op, &list->op, sizeof(x86_op_t) ); + } + + return ptr; + } + + x86_insn_t * x86_insn_new() { + x86_insn_t *insn = (x86_insn_t *) + calloc( sizeof(x86_insn_t), 1 ); + return insn; + } + + void x86_insn_free( x86_insn_t *insn ) { + x86_oplist_free( insn ); + free( insn ); + } +%} + +%newobject x86_insn_copy; + +%inline %{ + x86_insn_t * x86_insn_copy( x86_insn_t *src) { + x86_oplist_t *ptr, *list, *last = NULL; + x86_insn_t *insn = (x86_insn_t *) + calloc( sizeof(x86_insn_t), 1 ); + + if ( insn ) { + memcpy( insn, src, sizeof(x86_insn_t) ); + insn->operands = NULL; + insn->block = NULL; + insn->function = NULL; + + /* copy operand list */ + for ( list = src->operands; list; list = list->next ) { + ptr = x86_op_list_node_copy( list ); + + if (! ptr ) { + continue; + } + + if ( insn->operands ) { + last->next = ptr; + } else { + insn->operands = ptr; + } + last = ptr; + } + } + + return insn; + } + + x86_op_list * x86_insn_op_list( x86_insn_t *insn ) { + x86_oplist_t *list = insn->operands; + x86_op_list *op_list = x86_op_list_new(); + + for ( list = insn->operands; list; list = list->next ) { + x86_op_list_append( op_list, &list->op ); + } + + return op_list; + } + + typedef struct x86_insn_list_node { + x86_insn_t *insn; + struct x86_insn_list_node *next, *prev; + } x86_insn_list_node; + + typedef struct x86_insn_list { + size_t count; + x86_insn_list_node *head, *tail, *curr; + } x86_insn_list; + +%} + +%newobject x86_insn_list_new; + +%inline %{ + x86_insn_list * x86_insn_list_new () { + x86_insn_list *list = (x86_insn_list *) + calloc( sizeof(x86_insn_list), 1 ); + list->count = 0; + return list; + } + + void x86_insn_list_free( x86_insn_list * list ) { + x86_insn_list_node *node, *next; + + if (! list ) { + return; + } + + node = list->head; + while ( node ) { + next = node->next; + /* free( node->insn ); */ + free( node ); + node = next; + } + + free( list ); + } + + x86_insn_list_node * x86_insn_list_first( x86_insn_list *list ) { + if (! list ) { + return NULL; + } + return list->head; + } + + x86_insn_list_node * x86_insn_list_last( x86_insn_list *list ) { + if (! list ) { + return NULL; + } + return list->tail; + } + + x86_insn_list_node * x86_insn_list_next( x86_insn_list *list ) { + if (! list ) { + return NULL; + } + if (! list->curr ) { + list->curr = list->head; + return list->head; + } + + list->curr = list->curr->next; + return list->curr; + } + + x86_insn_list_node * x86_insn_list_prev( x86_insn_list *list ) { + if (! list ) { + return NULL; + } + if (! list->curr ) { + list->curr = list->tail; + return list->tail; + } + + list->curr = list->curr->prev; + return list->curr; + } + +%} + +%newobject x86_insn_list_append; + +%inline %{ + void x86_insn_list_append( x86_insn_list *list, x86_insn_t *insn ) { + x86_insn_list_node *node; + if (! list ) { + return; + } + + node = (x86_insn_list_node *) + calloc( sizeof(x86_insn_list_node) , 1 ); + + if (! node ) { + return; + } + + list->count++; + if ( ! list->tail ) { + list->head = list->tail = node; + } else { + list->tail->next = node; + node->prev = list->tail; + list->tail = node; + } + + node->insn = x86_insn_copy( insn ); + } + + typedef struct { + enum x86_report_codes last_error; + void * last_error_data; + void * disasm_callback; + void * disasm_resolver; + } x86disasm; + + void x86_default_reporter( enum x86_report_codes code, + void *data, void *arg ) { + x86disasm *dis = (x86disasm *) arg; + if ( dis ) { + dis->last_error = code; + dis->last_error_data = data; + } + } + + void x86_default_callback( x86_insn_t *insn, void *arg ) { + x86_insn_list *list = (x86_insn_list *) arg; + if ( list ) { + x86_insn_list_append( list, insn ); + } + } + + /* TODO: resolver stack, maybe a callback */ + long x86_default_resolver( x86_op_t *op, x86_insn_t *insn, void *arg ) { + x86disasm *dis = (x86disasm *) arg; + if ( dis ) { + //return dis->resolver( op, insn ); + return 0; + } + + return 0; + } + + +%} + +%newobject x86disasm_new; + +%inline %{ + x86disasm * x86disasm_new ( enum x86_options options ) { + x86disasm * dis = (x86disasm *) + calloc( sizeof( x86disasm ), 1 ); + x86_init( options, x86_default_reporter, dis ); + return dis; + } + + void x86disasm_free( x86disasm * dis ) { + x86_cleanup(); + free( dis ); + } +%} + +%newobject x86_disasm; + +%inline %{ + x86_insn_t * disasm( unsigned char *buf, size_t buf_len, + unsigned long buf_rva, unsigned int offset ) { + x86_insn_t *insn = calloc( sizeof( x86_insn_t ), 1 ); + x86_disasm( buf, buf_len, buf_rva, offset, insn ); + return insn; + } + + int disasm_range( unsigned char *buf, size_t buf_len, + unsigned long buf_rva, unsigned int offset, + unsigned int len ) { + + x86_insn_list *list = x86_insn_list_new(); + + if ( len > buf_len ) { + len = buf_len; + } + + return x86_disasm_range( buf, buf_rva, offset, len, + x86_default_callback, list ); + } + + int disasm_forward( unsigned char *buf, size_t buf_len, + unsigned long buf_rva, unsigned int offset ) { + x86_insn_list *list = x86_insn_list_new(); + + /* use default resolver: damn SWIG callbacks! */ + return x86_disasm_forward( buf, buf_len, buf_rva, offset, + x86_default_callback, list, + x86_default_resolver, NULL ); + } + + size_t disasm_invariant( unsigned char *buf, size_t buf_len, + x86_invariant_t *inv ) { + return x86_invariant_disasm( buf, buf_len, inv ); + } + + size_t disasm_size( unsigned char *buf, size_t buf_len ) { + return x86_size_disasm( buf, buf_len ); + } + + int x86_max_operand_string( enum x86_asm_format format ) { + switch ( format ) { + case xml_syntax: + return MAX_OP_XML_STRING; + break; + case raw_syntax: + return MAX_OP_RAW_STRING; + break; + case native_syntax: + case intel_syntax: + case att_syntax: + case unknown_syntax: + default: + return MAX_OP_STRING; + break; + } + } + + + int x86_max_insn_string( enum x86_asm_format format ) { + switch ( format ) { + case xml_syntax: + return MAX_INSN_XML_STRING; + break; + case raw_syntax: + return MAX_INSN_RAW_STRING; + break; + case native_syntax: + case intel_syntax: + case att_syntax: + case unknown_syntax: + default: + return MAX_INSN_STRING; + break; + } + } + + int x86_max_num_operands( ) { return MAX_NUM_OPERANDS; } +%} + diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/swig/libdisasm_oop.i b/shared/sentry/external/breakpad/src/third_party/libdisasm/swig/libdisasm_oop.i new file mode 100644 index 000000000..973a47e27 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/swig/libdisasm_oop.i @@ -0,0 +1,1114 @@ +%module x86disasm +%{ +#ifdef _MSC_VER + typedef __int64 qword; +#else + typedef long long qword; +#endif + +#include + +#define MAX_REGNAME 8 +#define MAX_PREFIX_STR 32 +#define MAX_MNEM_STR 16 +#define MAX_INSN_SIZE 20 +#define MAX_OP_STRING 32 +#define MAX_OP_RAW_STRING 64 +#define MAX_OP_XML_STRING 256 +#define MAX_NUM_OPERANDS 8 +#define MAX_INSN_STRING 512 +#define MAX_INSN_RAW_STRING 1024 +#define MAX_INSN_XML_STRING 4096 + +#include "../../../config.h" + + +const char * version_string( void ) { + return PACKAGE_VERSION; +} + +%} + +const char * version_string( void ); + +%rename(X86_Register) x86_reg_t; +%rename(X86_EAddr) x86_ea_t; +%rename(X86_Operand) x86_op_t; +//%rename(X86_OpList) x86_oplist_t; +%rename(X86_Insn) x86_insn_t; +%rename(X86_InvOperand) x86_invariant_op_t; +%rename(X86_Invariant) x86_invariant_t; + +%include "carrays.i" + +%array_class( unsigned char, byteArray ); + + +%apply (unsigned char *STRING, int LENGTH) { + (unsigned char *buf, size_t buf_len) +}; + + +%inline %{ + + +enum x86_asm_format { + unknown_syntax = 0, /* never use! */ + native_syntax, /* header: 35 bytes */ + intel_syntax, /* header: 23 bytes */ + att_syntax, /* header: 23 bytes */ + xml_syntax, /* header: 679 bytes */ + raw_syntax /* header: 172 bytes */ +}; +%} + +/* ================================================================== */ +/* operand class */ +%inline %{ + enum x86_reg_type { + reg_gen = 0x00001, reg_in = 0x00002, + reg_out = 0x00004, reg_local = 0x00008, + reg_fpu = 0x00010, reg_seg = 0x00020, + reg_simd = 0x00040, reg_sys = 0x00080, + reg_sp = 0x00100, reg_fp = 0x00200, + reg_pc = 0x00400, reg_retaddr = 0x00800, + reg_cond = 0x01000, reg_zero = 0x02000, + reg_ret = 0x04000, reg_src = 0x10000, + reg_dest = 0x20000, reg_count = 0x40000 + }; + + typedef struct { + char name[MAX_REGNAME]; + enum x86_reg_type type; + unsigned int size; + unsigned int id; + unsigned int alias; + unsigned int shift; + } x86_reg_t; + + void x86_reg_from_id( unsigned int id, x86_reg_t * reg ); + + typedef struct { + unsigned int scale; + x86_reg_t index, base; + long disp; + char disp_sign; + char disp_size; + } x86_ea_t; + + enum x86_op_type { + op_unused = 0, + op_register = 1, + op_immediate = 2, + op_relative_near = 3, + op_relative_far = 4, + op_absolute = 5, + op_expression = 6, + op_offset = 7, + op_unknown + }; + + enum x86_op_datatype { + op_byte = 1, op_word = 2, + op_dword = 3, op_qword = 4, + op_dqword = 5, op_sreal = 6, + op_dreal = 7, op_extreal = 8, + op_bcd = 9, op_ssimd = 10, + op_dsimd = 11, op_sssimd = 12, + op_sdsimd = 13, op_descr32 = 14, + op_descr16 = 15, op_pdescr32 = 16, + op_pdescr16 = 17, op_fpuenv = 18, + op_fpregset = 19, + }; + + enum x86_op_access { + op_read = 1, + op_write = 2, + op_execute = 4 + }; + + enum x86_op_flags { + op_signed = 1, op_string = 2, + op_constant = 4, op_pointer = 8, + op_sysref = 0x010, op_implied = 0x020, + op_hardcode = 0x40, op_es_seg = 0x100, + op_cs_seg = 0x200, op_ss_seg = 0x300, + op_ds_seg = 0x400, op_fs_seg = 0x500, + op_gs_seg = 0x600 + }; + + typedef struct { + enum x86_op_type type; + enum x86_op_datatype datatype; + enum x86_op_access access; + enum x86_op_flags flags; + union { + char sbyte; + short sword; + long sdword; + qword sqword; + unsigned char byte; + unsigned short word; + unsigned long dword; + qword qword; + float sreal; + double dreal; + unsigned char extreal[10]; + unsigned char bcd[10]; + qword dqword[2]; + unsigned char simd[16]; + unsigned char fpuenv[28]; + void * address; + unsigned long offset; + x86_reg_t reg; + char relative_near; + long relative_far; + x86_ea_t expression; + } data; + void * insn; + } x86_op_t; + + unsigned int x86_operand_size( x86_op_t *op ); + + int x86_format_operand(x86_op_t *op, char *buf, int len, + enum x86_asm_format format); +%} + +%extend x86_reg_t{ + x86_reg_t * aliased_reg( ) { + x86_reg_t * reg = (x86_reg_t * ) + calloc( sizeof(x86_reg_t), 1 ); + x86_reg_from_id( self->id, reg ); + return reg; + } +} + +%extend x86_op_t{ + size_t size() { + return x86_operand_size( self ); + } + char * format( enum x86_asm_format format ) { + char *buf, *str; + size_t len; + + switch ( format ) { + case xml_syntax: + len = MAX_OP_XML_STRING; + break; + case raw_syntax: + len = MAX_OP_RAW_STRING; + break; + case native_syntax: + case intel_syntax: + case att_syntax: + case unknown_syntax: + default: + len = MAX_OP_STRING; + break; + } + + buf = (char * ) calloc( len + 1, 1 ); + x86_format_operand( self, buf, len, format ); + + /* drop buffer down to a reasonable size */ + str = strdup( buf ); + free(buf); + return str; + } + + int is_address( ) { + if ( self->type == op_absolute || + self->type == op_offset ) { + return 1; + } + + return 0; + } + + int is_relative( ) { + if ( self->type == op_relative_near || + self->type == op_relative_far ) { + return 1; + } + + return 0; + } + + %newobject copy; + x86_op_t * copy() { + x86_op_t *op = (x86_op_t *) calloc( sizeof(x86_op_t), 1 ); + + if ( op ) { + memcpy( op, self, sizeof(x86_op_t) ); + } + + return op; + } +} + +/* ================================================================== */ +/* operand list class */ +%inline %{ + typedef struct X86_OpListNode { + x86_op_t *op; + struct X86_OpListNode *next, *prev; + } X86_OpListNode; + + typedef struct X86_OpList { + size_t count; + X86_OpListNode *head, *tail, *curr; + } X86_OpList; +%} + +%extend X86_OpList { + X86_OpList () { + X86_OpList *list = (X86_OpList *) + calloc( sizeof(X86_OpList), 1 ); + list->count = 0; + return list; + } + + ~X86_OpList() { + X86_OpListNode *node, *next; + + node = self->head; + while ( node ) { + next = node->next; + /* free( node->insn ); */ + free( node ); + node = next; + } + + free( self ); + } + + X86_OpListNode * first() { + self->curr = self->head; + return self->head; + } + + X86_OpListNode * last() { + self->curr = self->tail; + return self->tail; + } + + X86_OpListNode * next() { + if (! self->curr ) { + self->curr = self->head; + return self->head; + } + + self->curr = self->curr->next; + return self->curr; + } + + X86_OpListNode * prev() { + if (! self->curr ) { + self->curr = self->tail; + return self->tail; + } + + self->curr = self->curr->prev; + return self->curr; + } + + %newobject append; + void append( x86_op_t *op ) { + X86_OpListNode *node = (X86_OpListNode *) + calloc( sizeof(X86_OpListNode) , 1 ); + if (! node ) { + return; + } + + self->count++; + if ( ! self->tail ) { + self->head = self->tail = node; + } else { + self->tail->next = node; + node->prev = self->tail; + self->tail = node; + } + + node->op = x86_op_t_copy( op ); + } +} + +%inline %{ + typedef struct x86_operand_list { + x86_op_t op; + struct x86_operand_list *next; + } x86_oplist_t; +%} + +%extend x86_oplist_t { + %newobject x86_oplist_node_copy; +} + +/* ================================================================== */ +/* instruction class */ +%inline %{ + x86_oplist_t * x86_oplist_node_copy( x86_oplist_t * list ) { + x86_oplist_t *ptr; + ptr = (x86_oplist_t *) calloc( sizeof(x86_oplist_t), 1 ); + if ( ptr ) { + memcpy( &ptr->op, &list->op, sizeof(x86_op_t) ); + } + + return ptr; + } + + enum x86_insn_group { + insn_none = 0, insn_controlflow = 1, + insn_arithmetic = 2, insn_logic = 3, + insn_stack = 4, insn_comparison = 5, + insn_move = 6, insn_string = 7, + insn_bit_manip = 8, insn_flag_manip = 9, + insn_fpu = 10, insn_interrupt = 13, + insn_system = 14, insn_other = 15 + }; + + enum x86_insn_type { + insn_invalid = 0, insn_jmp = 0x1001, + insn_jcc = 0x1002, insn_call = 0x1003, + insn_callcc = 0x1004, insn_return = 0x1005, + insn_add = 0x2001, insn_sub = 0x2002, + insn_mul = 0x2003, insn_div = 0x2004, + insn_inc = 0x2005, insn_dec = 0x2006, + insn_shl = 0x2007, insn_shr = 0x2008, + insn_rol = 0x2009, insn_ror = 0x200A, + insn_and = 0x3001, insn_or = 0x3002, + insn_xor = 0x3003, insn_not = 0x3004, + insn_neg = 0x3005, insn_push = 0x4001, + insn_pop = 0x4002, insn_pushregs = 0x4003, + insn_popregs = 0x4004, insn_pushflags = 0x4005, + insn_popflags = 0x4006, insn_enter = 0x4007, + insn_leave = 0x4008, insn_test = 0x5001, + insn_cmp = 0x5002, insn_mov = 0x6001, + insn_movcc = 0x6002, insn_xchg = 0x6003, + insn_xchgcc = 0x6004, insn_strcmp = 0x7001, + insn_strload = 0x7002, insn_strmov = 0x7003, + insn_strstore = 0x7004, insn_translate = 0x7005, + insn_bittest = 0x8001, insn_bitset = 0x8002, + insn_bitclear = 0x8003, insn_clear_carry = 0x9001, + insn_clear_zero = 0x9002, insn_clear_oflow = 0x9003, + insn_clear_dir = 0x9004, insn_clear_sign = 0x9005, + insn_clear_parity = 0x9006, insn_set_carry = 0x9007, + insn_set_zero = 0x9008, insn_set_oflow = 0x9009, + insn_set_dir = 0x900A, insn_set_sign = 0x900B, + insn_set_parity = 0x900C, insn_tog_carry = 0x9010, + insn_tog_zero = 0x9020, insn_tog_oflow = 0x9030, + insn_tog_dir = 0x9040, insn_tog_sign = 0x9050, + insn_tog_parity = 0x9060, insn_fmov = 0xA001, + insn_fmovcc = 0xA002, insn_fneg = 0xA003, + insn_fabs = 0xA004, insn_fadd = 0xA005, + insn_fsub = 0xA006, insn_fmul = 0xA007, + insn_fdiv = 0xA008, insn_fsqrt = 0xA009, + insn_fcmp = 0xA00A, insn_fcos = 0xA00C, + insn_fldpi = 0xA00D, insn_fldz = 0xA00E, + insn_ftan = 0xA00F, insn_fsine = 0xA010, + insn_fsys = 0xA020, insn_int = 0xD001, + insn_intcc = 0xD002, insn_iret = 0xD003, + insn_bound = 0xD004, insn_debug = 0xD005, + insn_trace = 0xD006, insn_invalid_op = 0xD007, + insn_oflow = 0xD008, insn_halt = 0xE001, + insn_in = 0xE002, insn_out = 0xE003, + insn_cpuid = 0xE004, insn_nop = 0xF001, + insn_bcdconv = 0xF002, insn_szconv = 0xF003 + }; + + enum x86_insn_note { + insn_note_ring0 = 1, + insn_note_smm = 2, + insn_note_serial = 4 + }; + + enum x86_flag_status { + insn_carry_set = 0x1, + insn_zero_set = 0x2, + insn_oflow_set = 0x4, + insn_dir_set = 0x8, + insn_sign_set = 0x10, + insn_parity_set = 0x20, + insn_carry_or_zero_set = 0x40, + insn_zero_set_or_sign_ne_oflow = 0x80, + insn_carry_clear = 0x100, + insn_zero_clear = 0x200, + insn_oflow_clear = 0x400, + insn_dir_clear = 0x800, + insn_sign_clear = 0x1000, + insn_parity_clear = 0x2000, + insn_sign_eq_oflow = 0x4000, + insn_sign_ne_oflow = 0x8000 + }; + + enum x86_insn_cpu { + cpu_8086 = 1, cpu_80286 = 2, + cpu_80386 = 3, cpu_80387 = 4, + cpu_80486 = 5, cpu_pentium = 6, + cpu_pentiumpro = 7, cpu_pentium2 = 8, + cpu_pentium3 = 9, cpu_pentium4 = 10, + cpu_k6 = 16, cpu_k7 = 32, + cpu_athlon = 48 + }; + + enum x86_insn_isa { + isa_gp = 1, isa_fp = 2, + isa_fpumgt = 3, isa_mmx = 4, + isa_sse1 = 5, isa_sse2 = 6, + isa_sse3 = 7, isa_3dnow = 8, + isa_sys = 9 + }; + + enum x86_insn_prefix { + insn_no_prefix = 0, + insn_rep_zero = 1, + insn_rep_notzero = 2, + insn_lock = 4 + }; + + + typedef struct { + unsigned long addr; + unsigned long offset; + enum x86_insn_group group; + enum x86_insn_type type; + enum x86_insn_note note; + unsigned char bytes[MAX_INSN_SIZE]; + unsigned char size; + unsigned char addr_size; + unsigned char op_size; + enum x86_insn_cpu cpu; + enum x86_insn_isa isa; + enum x86_flag_status flags_set; + enum x86_flag_status flags_tested; + unsigned char stack_mod; + long stack_mod_val; + enum x86_insn_prefix prefix; + char prefix_string[MAX_PREFIX_STR]; + char mnemonic[MAX_MNEM_STR]; + x86_oplist_t *operands; + size_t operand_count; + size_t explicit_count; + void *block; + void *function; + int tag; + } x86_insn_t; + + typedef void (*x86_operand_fn)(x86_op_t *op, x86_insn_t *insn, + void *arg); + + enum x86_op_foreach_type { + op_any = 0, + op_dest = 1, + op_src = 2, + op_ro = 3, + op_wo = 4, + op_xo = 5, + op_rw = 6, + op_implicit = 0x10, + op_explicit = 0x20 + }; + + size_t x86_operand_count( x86_insn_t *insn, + enum x86_op_foreach_type type ); + x86_op_t * x86_operand_1st( x86_insn_t *insn ); + x86_op_t * x86_operand_2nd( x86_insn_t *insn ); + x86_op_t * x86_operand_3rd( x86_insn_t *insn ); + long x86_get_rel_offset( x86_insn_t *insn ); + x86_op_t * x86_get_branch_target( x86_insn_t *insn ); + x86_op_t * x86_get_imm( x86_insn_t *insn ); + unsigned char * x86_get_raw_imm( x86_insn_t *insn ); + void x86_set_insn_addr( x86_insn_t *insn, unsigned long addr ); + int x86_format_mnemonic(x86_insn_t *insn, char *buf, int len, + enum x86_asm_format format); + int x86_format_insn(x86_insn_t *insn, char *buf, int len, + enum x86_asm_format); + void x86_oplist_free( x86_insn_t *insn ); + int x86_insn_is_valid( x86_insn_t *insn ); +%} + +%extend x86_insn_t { + x86_insn_t() { + x86_insn_t *insn = (x86_insn_t *) + calloc( sizeof(x86_insn_t), 1 ); + return insn; + } + ~x86_insn_t() { + x86_oplist_free( self ); + free( self ); + } + + int is_valid( ) { + return x86_insn_is_valid( self ); + } + + x86_op_t * operand_1st() { + return x86_operand_1st( self ); + } + + x86_op_t * operand_2nd() { + return x86_operand_2nd( self ); + } + + x86_op_t * operand_3rd() { + return x86_operand_3rd( self ); + } + + x86_op_t * operand_dest() { + return x86_operand_1st( self ); + } + + x86_op_t * operand_src() { + return x86_operand_2nd( self ); + } + + size_t num_operands( enum x86_op_foreach_type type ) { + return x86_operand_count( self, type ); + } + + long rel_offset() { + return x86_get_rel_offset( self ); + } + + x86_op_t * branch_target() { + return x86_get_branch_target( self ); + } + + x86_op_t * imm() { + return x86_get_imm( self ); + } + + unsigned char * raw_imm() { + return x86_get_raw_imm( self ); + } + + %newobject format; + char * format( enum x86_asm_format format ) { + char *buf, *str; + size_t len; + + switch ( format ) { + case xml_syntax: + len = MAX_INSN_XML_STRING; + break; + case raw_syntax: + len = MAX_INSN_RAW_STRING; + break; + case native_syntax: + case intel_syntax: + case att_syntax: + case unknown_syntax: + default: + len = MAX_INSN_STRING; + break; + } + + buf = (char * ) calloc( len + 1, 1 ); + x86_format_insn( self, buf, len, format ); + + /* drop buffer down to a reasonable size */ + str = strdup( buf ); + free(buf); + return str; + } + + %newobject format_mnemonic; + char * format_mnemonic( enum x86_asm_format format ) { + char *buf, *str; + size_t len = MAX_MNEM_STR + MAX_PREFIX_STR + 4; + + buf = (char * ) calloc( len, 1 ); + x86_format_mnemonic( self, buf, len, format ); + + /* drop buffer down to a reasonable size */ + str = strdup( buf ); + free(buf); + + return str; + } + + %newobject copy; + x86_insn_t * copy() { + x86_oplist_t *ptr, *list, *last = NULL; + x86_insn_t *insn = (x86_insn_t *) + calloc( sizeof(x86_insn_t), 1 ); + + if ( insn ) { + memcpy( insn, self, sizeof(x86_insn_t) ); + insn->operands = NULL; + insn->block = NULL; + insn->function = NULL; + + /* copy operand list */ + for ( list = self->operands; list; list = list->next ) { + ptr = x86_oplist_node_copy( list ); + + if (! ptr ) { + continue; + } + + if ( insn->operands ) { + last->next = ptr; + } else { + insn->operands = ptr; + } + last = ptr; + } + } + + return insn; + } + + X86_OpList * operand_list( ) { + x86_oplist_t *list = self->operands; + X86_OpList *op_list = new_X86_OpList(); + + for ( list = self->operands; list; list = list->next ) { + X86_OpList_append( op_list, &list->op ); + } + + return op_list; + } +} + +/* ================================================================== */ +/* invariant instruction class */ +%inline %{ + #define X86_WILDCARD_BYTE 0xF4 + + typedef struct { + enum x86_op_type type; + enum x86_op_datatype datatype; + enum x86_op_access access; + enum x86_op_flags flags; + } x86_invariant_op_t; + + typedef struct { + unsigned char bytes[64]; + unsigned int size; + enum x86_insn_group group; + enum x86_insn_type type; + x86_invariant_op_t operands[3]; + } x86_invariant_t; +%} + +%extend x86_invariant_t { + + x86_invariant_t() { + x86_invariant_t *inv = (x86_invariant_t *) + calloc( sizeof(x86_invariant_t), 1 ); + return inv; + } + + ~x86_invariant_t() { + free( self ); + } +} + +/* ================================================================== */ +/* instruction list class */ +%inline %{ + typedef struct X86_InsnListNode { + x86_insn_t *insn; + struct X86_InsnListNode *next, *prev; + } X86_InsnListNode; + + typedef struct X86_InsnList { + size_t count; + X86_InsnListNode *head, *tail, *curr; + } X86_InsnList; +%} + +%extend X86_InsnList { + X86_InsnList () { + X86_InsnList *list = (X86_InsnList *) + calloc( sizeof(X86_InsnList), 1 ); + list->count = 0; + return list; + } + + ~X86_InsnList() { + X86_InsnListNode *node, *next; + + node = self->head; + while ( node ) { + next = node->next; + /* free( node->insn ); */ + free( node ); + node = next; + } + + free( self ); + } + + X86_InsnListNode * first() { return self->head; } + + X86_InsnListNode * last() { return self->tail; } + + X86_InsnListNode * next() { + if (! self->curr ) { + self->curr = self->head; + return self->head; + } + + self->curr = self->curr->next; + return self->curr; + } + + X86_InsnListNode * prev() { + if (! self->curr ) { + self->curr = self->tail; + return self->tail; + } + + self->curr = self->curr->prev; + return self->curr; + } + + %newobject append; + void append( x86_insn_t *insn ) { + X86_InsnListNode *node = (X86_InsnListNode *) + calloc( sizeof(X86_InsnListNode) , 1 ); + if (! node ) { + return; + } + + self->count++; + if ( ! self->tail ) { + self->head = self->tail = node; + } else { + self->tail->next = node; + node->prev = self->tail; + self->tail = node; + } + + node->insn = x86_insn_t_copy( insn ); + } +} + +/* ================================================================== */ +/* address table class */ +/* slight TODO */ + +/* ================================================================== */ +/* Main disassembler class */ +%inline %{ + + enum x86_options { + opt_none= 0, + opt_ignore_nulls=1, + opt_16_bit=2 + }; + enum x86_report_codes { + report_disasm_bounds, + report_insn_bounds, + report_invalid_insn, + report_unknown + }; + + + typedef struct { + enum x86_report_codes last_error; + void * last_error_data; + void * disasm_callback; + void * disasm_resolver; + } X86_Disasm; + + typedef void (*DISASM_REPORTER)( enum x86_report_codes code, + void *data, void *arg ); + typedef void (*DISASM_CALLBACK)( x86_insn_t *insn, void * arg ); + typedef long (*DISASM_RESOLVER)( x86_op_t *op, + x86_insn_t * current_insn, + void *arg ); + + void x86_report_error( enum x86_report_codes code, void *data ); + int x86_init( enum x86_options options, DISASM_REPORTER reporter, + void *arg); + void x86_set_reporter( DISASM_REPORTER reporter, void *arg); + void x86_set_options( enum x86_options options ); + enum x86_options x86_get_options( void ); + int x86_cleanup(void); + int x86_format_header( char *buf, int len, enum x86_asm_format format); + unsigned int x86_endian(void); + unsigned int x86_addr_size(void); + unsigned int x86_op_size(void); + unsigned int x86_word_size(void); + unsigned int x86_max_insn_size(void); + unsigned int x86_sp_reg(void); + unsigned int x86_fp_reg(void); + unsigned int x86_ip_reg(void); + size_t x86_invariant_disasm( unsigned char *buf, int buf_len, + x86_invariant_t *inv ); + size_t x86_size_disasm( unsigned char *buf, unsigned int buf_len ); + int x86_disasm( unsigned char *buf, unsigned int buf_len, + unsigned long buf_rva, unsigned int offset, + x86_insn_t * insn ); + int x86_disasm_range( unsigned char *buf, unsigned long buf_rva, + unsigned int offset, unsigned int len, + DISASM_CALLBACK func, void *arg ); + int x86_disasm_forward( unsigned char *buf, unsigned int buf_len, + unsigned long buf_rva, unsigned int offset, + DISASM_CALLBACK func, void *arg, + DISASM_RESOLVER resolver, void *r_arg ); + + void x86_default_reporter( enum x86_report_codes code, + void *data, void *arg ) { + X86_Disasm *dis = (X86_Disasm *) arg; + if ( dis ) { + dis->last_error = code; + dis->last_error_data = data; + } + } + + void x86_default_callback( x86_insn_t *insn, void *arg ) { + X86_InsnList *list = (X86_InsnList *) arg; + if ( list ) { + X86_InsnList_append( list, insn ); + } + } + + /* TODO: resolver stack, maybe a callback */ + long x86_default_resolver( x86_op_t *op, x86_insn_t *insn, void *arg ) { + X86_Disasm *dis = (X86_Disasm *) arg; + if ( dis ) { + //return dis->resolver( op, insn ); + return 0; + } + + return 0; + } + +%} + +%extend X86_Disasm { + + X86_Disasm( ) { + X86_Disasm * dis = (X86_Disasm *) + calloc( sizeof( X86_Disasm ), 1 ); + x86_init( opt_none, x86_default_reporter, dis ); + return dis; + } + + X86_Disasm( enum x86_options options ) { + X86_Disasm * dis = (X86_Disasm *) + calloc( sizeof( X86_Disasm ), 1 ); + x86_init( options, x86_default_reporter, dis ); + return dis; + } + + X86_Disasm( enum x86_options options, DISASM_REPORTER reporter ) { + X86_Disasm * dis = (X86_Disasm *) + calloc( sizeof( X86_Disasm ), 1 ); + x86_init( options, reporter, NULL ); + return dis; + } + + X86_Disasm( enum x86_options options, DISASM_REPORTER reporter, + void * arg ) { + X86_Disasm * dis = (X86_Disasm *) + calloc( sizeof( X86_Disasm ), 1 ); + x86_init( options, reporter, arg ); + return dis; + } + + ~X86_Disasm() { + x86_cleanup(); + free( self ); + } + + void set_options( enum x86_options options ) { + return x86_set_options( options ); + } + + enum x86_options options() { + return x86_get_options(); + } + + void set_callback( void * callback ) { + self->disasm_callback = callback; + } + + void set_resolver( void * callback ) { + self->disasm_resolver = callback; + } + + void report_error( enum x86_report_codes code ) { + x86_report_error( code, NULL ); + } + + %newobject disasm; + x86_insn_t * disasm( unsigned char *buf, size_t buf_len, + unsigned long buf_rva, unsigned int offset ) { + x86_insn_t *insn = calloc( sizeof( x86_insn_t ), 1 ); + x86_disasm( buf, buf_len, buf_rva, offset, insn ); + return insn; + } + + int disasm_range( unsigned char *buf, size_t buf_len, + unsigned long buf_rva, unsigned int offset, + unsigned int len ) { + + X86_InsnList *list = new_X86_InsnList(); + + if ( len > buf_len ) { + len = buf_len; + } + + return x86_disasm_range( buf, buf_rva, offset, len, + x86_default_callback, list ); + } + + int disasm_forward( unsigned char *buf, size_t buf_len, + unsigned long buf_rva, unsigned int offset ) { + X86_InsnList *list = new_X86_InsnList(); + + /* use default resolver: damn SWIG callbacks! */ + return x86_disasm_forward( buf, buf_len, buf_rva, offset, + x86_default_callback, list, + x86_default_resolver, NULL ); + } + + size_t disasm_invariant( unsigned char *buf, size_t buf_len, + x86_invariant_t *inv ) { + return x86_invariant_disasm( buf, buf_len, inv ); + } + + size_t disasm_size( unsigned char *buf, size_t buf_len ) { + return x86_size_disasm( buf, buf_len ); + } + + %newobject format_header; + char * format_header( enum x86_asm_format format) { + char *buf, *str; + size_t len; + + switch ( format ) { + /* these were obtained from x86_format.c */ + case xml_syntax: + len = 679; break; + case raw_syntax: + len = 172; break; + case native_syntax: + len = 35; break; + case intel_syntax: + len = 23; break; + case att_syntax: + len = 23; break; + case unknown_syntax: + default: + len = 23; break; + } + + buf = (char * ) calloc( len + 1, 1 ); + x86_format_header( buf, len, format ); + + return buf; + } + + unsigned int endian() { + return x86_endian(); + } + + unsigned int addr_size() { + return x86_addr_size(); + } + + unsigned int op_size() { + return x86_op_size(); + } + + unsigned int word_size() { + return x86_word_size(); + } + + unsigned int max_insn_size() { + return x86_max_insn_size(); + } + + unsigned int sp_reg() { + return x86_sp_reg(); + } + + unsigned int fp_reg() { + return x86_fp_reg(); + } + + unsigned int ip_reg() { + return x86_ip_reg(); + } + + %newobject reg_from_id; + x86_reg_t * reg_from_id( unsigned int id ) { + x86_reg_t * reg = calloc( sizeof(x86_reg_t), 1 ); + x86_reg_from_id( id, reg ); + return reg; + } + + unsigned char wildcard_byte() { return X86_WILDCARD_BYTE; } + + int max_register_string() { return MAX_REGNAME; } + + int max_prefix_string() { return MAX_PREFIX_STR; } + + int max_mnemonic_string() { return MAX_MNEM_STR; } + + int max_operand_string( enum x86_asm_format format ) { + switch ( format ) { + case xml_syntax: + return MAX_OP_XML_STRING; + break; + case raw_syntax: + return MAX_OP_RAW_STRING; + break; + case native_syntax: + case intel_syntax: + case att_syntax: + case unknown_syntax: + default: + return MAX_OP_STRING; + break; + } + } + + + int max_insn_string( enum x86_asm_format format ) { + switch ( format ) { + case xml_syntax: + return MAX_INSN_XML_STRING; + break; + case raw_syntax: + return MAX_INSN_RAW_STRING; + break; + case native_syntax: + case intel_syntax: + case att_syntax: + case unknown_syntax: + default: + return MAX_INSN_STRING; + break; + } + } + + int max_num_operands( ) { return MAX_NUM_OPERANDS; } +} + +/* python callback, per the manual */ +/*%typemap(python,in) PyObject *pyfunc { + if (!PyCallable_Check($source)) { + PyErr_SetString(PyExc_TypeError, "Need a callable object!"); + return NULL; + } + $target = $source; +}*/ + +/* python FILE * callback, per the manual */ +/* +%typemap(python,in) FILE * { + if (!PyFile_Check($source)) { + PyErr_SetString(PyExc_TypeError, "Need a file!"); + return NULL; + } + $target = PyFile_AsFile($source); +}*/ + + diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/swig/perl/Makefile-swig b/shared/sentry/external/breakpad/src/third_party/libdisasm/swig/perl/Makefile-swig new file mode 100644 index 000000000..9f3a64573 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/swig/perl/Makefile-swig @@ -0,0 +1,65 @@ +ifndef BASE_NAME +BASE_NAME = x86disasm +endif + +ifndef SWIG +SWIG = swig # apt-get install swig ! +endif + +ifndef GCC +GCC = gcc +endif + +ifndef CC_FLAGS +CC_FLAGS = -c -fPIC +endif + +ifndef LD_FLAGS +LD_FLAGS = -shared -L.. -ldisasm +endif + +INTERFACE_FILE = libdisasm_oop.i + +SWIG_INTERFACE = ../$(INTERFACE_FILE) + +# PERL rules +PERL_MOD = blib/arch/auto/$(BASE_NAME)/$(BASE_NAME).so +PERL_SHADOW = $(BASE_NAME)_wrap.c +PERL_SWIG = $(BASE_NAME).pl +PERL_OBJ = $(BASE_NAME)_wrap.o +PERL_INC = `perl -e 'use Config; print $$Config{archlib};'`/CORE +PERL_CC_FLAGS = `perl -e 'use Config; print $$Config{ccflags};'` + +#==================================================== +# TARGETS + +all: swig-perl + +dummy: swig-perl install uninstall clean + +swig-perl: $(PERL_MOD) + +$(PERL_MOD): $(PERL_OBJ) + perl Makefile.PL + make + #$(GCC) $(LD_FLAGS) $(PERL_OBJ) -o $@ + +$(PERL_OBJ): $(PERL_SHADOW) + $(GCC) $(CC_FLAGS) $(PERL_CC_FLAGS) -I$(PERL_INC) -o $@ $< + +$(PERL_SHADOW): $(SWIG_INTERFACE) + swig -perl -shadow -o $(PERL_SHADOW) -outdir . $< + +# ================================================================== +install: $(PERL_MOD) + make install + +# ================================================================== +uninstall: + +# ================================================================== +clean: + rm $(PERL_MOD) $(PERL_OBJ) + rm $(PERL_SHADOW) + rm -rf Makefile blib pm_to_blib + diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/swig/perl/Makefile.PL b/shared/sentry/external/breakpad/src/third_party/libdisasm/swig/perl/Makefile.PL new file mode 100644 index 000000000..6e625df18 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/swig/perl/Makefile.PL @@ -0,0 +1,7 @@ +use ExtUtils::MakeMaker; + +WriteMakefile( + 'NAME' => 'x86disasm', + 'LIBS' => ['-ldisasm'], + 'OBJECT' => 'x86disasm_wrap.o' +); diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/swig/python/Makefile-swig b/shared/sentry/external/breakpad/src/third_party/libdisasm/swig/python/Makefile-swig new file mode 100644 index 000000000..544681a13 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/swig/python/Makefile-swig @@ -0,0 +1,64 @@ +ifndef BASE_NAME +BASE_NAME = x86disasm +endif + +ifndef SWIG +SWIG = swig # apt-get install swig ! +endif + +ifndef GCC +GCC = gcc +endif + +ifndef CC_FLAGS +CC_FLAGS = -c -fPIC +endif + +ifndef LD_FLAGS +LD_FLAGS = -shared -L.. -ldisasm +endif + +INTERFACE_FILE = libdisasm_oop.i + +SWIG_INTERFACE = ../$(INTERFACE_FILE) + +# PYTHON rules +PYTHON_MOD = $(BASE_NAME)-python.so +PYTHON_SHADOW = $(BASE_NAME)_wrap.c +PYTHON_SWIG = $(BASE_NAME).py +PYTHON_OBJ = $(BASE_NAME)_wrap.o +PYTHON_INC = `/bin/echo -e 'import sys\nprint sys.prefix + "/include/python" + sys.version[:3]' | python` +PYTHON_LIB = `/bin/echo -e 'import sys\nprint sys.prefix + "/lib/python" + sys.version[:3]' | python` +PYTHON_DEST = $(PYTHON_LIB)/lib-dynload/_$(BASE_NAME).so + +#==================================================== +# TARGETS + +all: swig-python + +dummy: swig-python install uninstall clean + +swig-python: $(PYTHON_MOD) + +$(PYTHON_MOD): $(PYTHON_OBJ) + $(GCC) $(LD_FLAGS) $(PYTHON_OBJ) -o $@ + +$(PYTHON_OBJ): $(PYTHON_SHADOW) + $(GCC) $(CC_FLAGS) -I$(PYTHON_INC) -I.. -o $@ $< + +$(PYTHON_SHADOW): $(SWIG_INTERFACE) + swig -python -shadow -o $(PYTHON_SHADOW) -outdir . $< + +# ================================================================== +install: $(PYTHON_MOD) + sudo cp $(PYTHON_MOD) $(PYTHON_DEST) + sudo cp $(PYTHON_SWIG) $(PYTHON_LIB) + +# ================================================================== +uninstall: + +# ================================================================== +clean: + rm $(PYTHON_MOD) $(PYTHON_SWIG) $(PYTHON_OBJ) + rm $(PYTHON_SHADOW) + diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/swig/ruby/Makefile-swig b/shared/sentry/external/breakpad/src/third_party/libdisasm/swig/ruby/Makefile-swig new file mode 100644 index 000000000..ee4800232 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/swig/ruby/Makefile-swig @@ -0,0 +1,68 @@ +ifndef BASE_NAME +BASE_NAME = x86disasm +endif + +ifndef SWIG +SWIG = swig # apt-get install swig ! +endif + +ifndef GCC +GCC = gcc +endif + +ifndef CC_FLAGS +CC_FLAGS = -c -fPIC +endif + +ifndef LD_FLAGS +LD_FLAGS = -shared -L../.. -ldisasm +endif + +LIBDISASM_DIR = ../.. + +INTERFACE_FILE = libdisasm_oop.i + +SWIG_INTERFACE = ../$(INTERFACE_FILE) + +# RUBY rules +RUBY_MAKEFILE = Makefile +RUBY_MOD = $(BASE_NAME).so +RUBY_SHADOW = $(BASE_NAME)_wrap.c +#RUBY_SWIG = $(BASE_NAME).rb +RUBY_OBJ = $(BASE_NAME)_wrap.o +RUBY_INC = `ruby -e 'puts $$:.join("\n")' | tail -2 | head -1` +#RUBY_LIB = +#RUBY_DEST = + +#==================================================== +# TARGETS + +all: swig-ruby + +dummy: swig-ruby install uninstall clean + +swig-ruby: $(RUBY_MOD) + +$(RUBY_MOD): $(RUBY_MAKEFILE) + make + +$(RUBY_MAKEFILE): $(RUBY_OBJ) + ruby extconf.rb + +$(RUBY_OBJ):$(RUBY_SHADOW) + $(GCC) $(CC_FLAGS) -I$(RUBY_INC) -I.. -o $@ $< + +$(RUBY_SHADOW): $(SWIG_INTERFACE) + swig -ruby -o $(RUBY_SHADOW) -outdir . $< + +# ================================================================== +install: $(RUBY_MOD) + make install + +# ================================================================== +uninstall: + +# ================================================================== +clean: + make clean || true + rm $(RUBY_SHADOW) $(RUBY_MAKEFILE) $(RUBY_MOD) $(RUBY_OBJ) diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/swig/ruby/extconf.rb b/shared/sentry/external/breakpad/src/third_party/libdisasm/swig/ruby/extconf.rb new file mode 100644 index 000000000..4e7432643 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/swig/ruby/extconf.rb @@ -0,0 +1,4 @@ +require 'mkmf' +find_library('disasm', 'x86_init', "/usr/local/lib", "../..") +create_makefile('x86disasm') + diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/swig/tcl/Makefile-swig b/shared/sentry/external/breakpad/src/third_party/libdisasm/swig/tcl/Makefile-swig new file mode 100644 index 000000000..5145a8293 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/swig/tcl/Makefile-swig @@ -0,0 +1,63 @@ +ifndef BASE_NAME +BASE_NAME = x86disasm +endif + +ifndef SWIG +SWIG = swig # apt-get install swig ! +endif + +ifndef GCC +GCC = gcc +endif + +ifndef CC_FLAGS +CC_FLAGS = -c -fPIC +endif + +ifndef LD_FLAGS +LD_FLAGS = -shared -L../.. -ldisasm +endif + +INTERFACE_FILE = libdisasm.i + +SWIG_INTERFACE = ../$(INTERFACE_FILE) + +# TCL rules +TCL_VERSION = 8.3 +TCL_MOD = $(BASE_NAME)-tcl.so +TCL_SHADOW = $(BASE_NAME)_wrap.c +TCL_OBJ = $(BASE_NAME)_wrap.o +TCL_INC = /usr/include/tcl$(TCL_VERSION) +TCL_LIB = /usr/lib/tcl$(TCL_VERSION) +TCL_DEST = $(TCL_LIB)/$(BASE_NAME).so + +#==================================================== +# TARGETS + +all: swig-tcl + +dummy: swig-tcl install uninstall clean + +swig-tcl: $(TCL_MOD) + +$(TCL_MOD): $(TCL_OBJ) + $(GCC) $(LD_FLAGS) $(TCL_OBJ) -o $@ + +$(TCL_OBJ): $(TCL_SHADOW) + $(GCC) $(CC_FLAGS) -I$(TCL_INC) -I.. -o $@ $< + +$(TCL_SHADOW): $(SWIG_INTERFACE) + swig -tcl -o $(TCL_SHADOW) -outdir . $< + +# ================================================================== +install: $(TCL_MOD) + sudo cp $(TCL_MOD) $(TCL_DEST) + +# ================================================================== +uninstall: + +# ================================================================== +clean: + rm $(TCL_MOD) $(TCL_SWIG) $(TCL_OBJ) + rm $(TCL_SHADOW) + diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/x86_disasm.c b/shared/sentry/external/breakpad/src/third_party/libdisasm/x86_disasm.c new file mode 100644 index 000000000..1b82f4e66 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/x86_disasm.c @@ -0,0 +1,210 @@ +#include +#include +#include + +#include "libdis.h" +#include "ia32_insn.h" +#include "ia32_invariant.h" +#include "x86_operand_list.h" + + +#ifdef _MSC_VER + #define snprintf _snprintf + #define inline __inline +#endif + +unsigned int x86_disasm( unsigned char *buf, unsigned int buf_len, + uint32_t buf_rva, unsigned int offset, + x86_insn_t *insn ){ + int len, size; + unsigned char bytes[MAX_INSTRUCTION_SIZE]; + + if ( ! buf || ! insn || ! buf_len ) { + /* caller screwed up somehow */ + return 0; + } + + + /* ensure we are all NULLed up */ + memset( insn, 0, sizeof(x86_insn_t) ); + insn->addr = buf_rva + offset; + insn->offset = offset; + /* default to invalid insn */ + insn->type = insn_invalid; + insn->group = insn_none; + + if ( offset >= buf_len ) { + /* another caller screwup ;) */ + x86_report_error(report_disasm_bounds, (void*)(long)(buf_rva+offset)); + return 0; + } + + len = buf_len - offset; + + /* copy enough bytes for disassembly into buffer : this + * helps prevent buffer overruns at the end of a file */ + memset( bytes, 0, MAX_INSTRUCTION_SIZE ); + memcpy( bytes, &buf[offset], (len < MAX_INSTRUCTION_SIZE) ? len : + MAX_INSTRUCTION_SIZE ); + + /* actually do the disassembly */ + /* TODO: allow switching when more disassemblers are added */ + size = ia32_disasm_addr( bytes, len, insn); + + /* check and see if we had an invalid instruction */ + if (! size ) { + x86_report_error(report_invalid_insn, (void*)(long)(buf_rva+offset)); + return 0; + } + + /* check if we overran the end of the buffer */ + if ( size > len ) { + x86_report_error( report_insn_bounds, (void*)(long)(buf_rva + offset)); + MAKE_INVALID( insn, bytes ); + return 0; + } + + /* fill bytes field of insn */ + memcpy( insn->bytes, bytes, size ); + + return size; +} + +unsigned int x86_disasm_range( unsigned char *buf, uint32_t buf_rva, + unsigned int offset, unsigned int len, + DISASM_CALLBACK func, void *arg ) { + x86_insn_t insn; + unsigned int buf_len, size, count = 0, bytes = 0; + + /* buf_len is implied by the arguments */ + buf_len = len + offset; + + while ( bytes < len ) { + size = x86_disasm( buf, buf_len, buf_rva, offset + bytes, + &insn ); + if ( size ) { + /* invoke callback if it exists */ + if ( func ) { + (*func)( &insn, arg ); + } + bytes += size; + count ++; + } else { + /* error */ + bytes++; /* try next byte */ + } + + x86_oplist_free( &insn ); + } + + return( count ); +} + +static inline int follow_insn_dest( x86_insn_t *insn ) { + if ( insn->type == insn_jmp || insn->type == insn_jcc || + insn->type == insn_call || insn->type == insn_callcc ) { + return(1); + } + return(0); +} + +static inline int insn_doesnt_return( x86_insn_t *insn ) { + return( (insn->type == insn_jmp || insn->type == insn_return) ? 1: 0 ); +} + +static int32_t internal_resolver( x86_op_t *op, x86_insn_t *insn ){ + int32_t next_addr = -1; + if ( x86_optype_is_address(op->type) ) { + next_addr = op->data.sdword; + } else if ( op->type == op_relative_near ) { + next_addr = insn->addr + insn->size + op->data.relative_near; + } else if ( op->type == op_relative_far ) { + next_addr = insn->addr + insn->size + op->data.relative_far; + } + return( next_addr ); +} + +unsigned int x86_disasm_forward( unsigned char *buf, unsigned int buf_len, + uint32_t buf_rva, unsigned int offset, + DISASM_CALLBACK func, void *arg, + DISASM_RESOLVER resolver, void *r_arg ){ + x86_insn_t insn; + x86_op_t *op; + int32_t next_addr; + uint32_t next_offset; + unsigned int size, count = 0, bytes = 0, cont = 1; + + while ( cont && bytes < buf_len ) { + size = x86_disasm( buf, buf_len, buf_rva, offset + bytes, + &insn ); + + if ( size ) { + /* invoke callback if it exists */ + if ( func ) { + (*func)( &insn, arg ); + } + bytes += size; + count ++; + } else { + /* error */ + bytes++; /* try next byte */ + } + + if ( follow_insn_dest(&insn) ) { + op = x86_get_dest_operand( &insn ); + next_addr = -1; + + /* if caller supplied a resolver, use it to determine + * the address to disassemble */ + if ( resolver ) { + next_addr = resolver(op, &insn, r_arg); + } else { + next_addr = internal_resolver(op, &insn); + } + + if (next_addr != -1 ) { + next_offset = next_addr - buf_rva; + /* if offset is in this buffer... */ + if ( (uint32_t)next_addr >= buf_rva && + next_offset < buf_len ) { + /* go ahead and disassemble */ + count += x86_disasm_forward( buf, + buf_len, + buf_rva, + next_offset, + func, arg, + resolver, r_arg ); + } else { + /* report unresolved address */ + x86_report_error( report_disasm_bounds, + (void*)(long)next_addr ); + } + } + } /* end follow_insn */ + + if ( insn_doesnt_return(&insn) ) { + /* stop disassembling */ + cont = 0; + } + + x86_oplist_free( &insn ); + } + return( count ); +} + +/* invariant instruction representation */ +size_t x86_invariant_disasm( unsigned char *buf, int buf_len, + x86_invariant_t *inv ){ + if (! buf || ! buf_len || ! inv ) { + return(0); + } + + return ia32_disasm_invariant(buf, buf_len, inv); +} +size_t x86_size_disasm( unsigned char *buf, unsigned int buf_len ) { + if (! buf || ! buf_len ) { + return(0); + } + + return ia32_disasm_size(buf, buf_len); +} diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/x86_format.c b/shared/sentry/external/breakpad/src/third_party/libdisasm/x86_format.c new file mode 100644 index 000000000..0ec960dc8 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/x86_format.c @@ -0,0 +1,1430 @@ +#include +#include +#include + +#include "libdis.h" +#include + +#ifdef _MSC_VER + #define snprintf _snprintf + #define inline __inline +#endif + + +/* + * concatenation macros. STRNCATF concatenates a format string, buf + * only with one argument. + */ +#define STRNCAT( buf, str, len ) do { \ + int _i = strlen(str), _blen = strlen(buf), _len = len - 1; \ + if ( len ) { \ + strncat( buf, str, _len ); \ + if ( _len <= _i ) { \ + buf[_blen+_len] = '\0'; \ + len = 0; \ + } else { \ + len -= _i; \ + } \ + } \ +} while( 0 ) + +#define STRNCATF( buf, fmt, data, len ) do { \ + char _tmp[MAX_OP_STRING]; \ + \ + snprintf( _tmp, sizeof _tmp, fmt, data ); \ + STRNCAT( buf, _tmp, len ); \ +} while( 0 ) + + +#define PRINT_DISPLACEMENT( ea ) do { \ + if ( ea->disp_size && ea->disp ) { \ + if ( ea->disp_sign ) { \ + STRNCATF( buf, "-0x%" PRIX32, -ea->disp, len ); \ + } else { \ + STRNCATF( buf, "0x%" PRIX32, ea->disp, len ); \ + } \ + } \ +} while( 0 ) + +static const char *prefix_strings[] = { + "", /* no prefix */ + "repz ", /* the trailing spaces make it easy to prepend to mnemonic */ + "repnz ", + "lock ", + "branch delay " /* unused in x86 */ +}; + +static int format_insn_prefix_str( enum x86_insn_prefix prefix, char *buf, + int len ) { + + int len_orig = len; + + /* concat all prefix strings */ + if ( prefix & 1 ) { STRNCAT( buf, prefix_strings[1], len ); } + if ( prefix & 2 ) { STRNCAT( buf, prefix_strings[2], len ); } + if ( prefix & 4 ) { STRNCAT( buf, prefix_strings[3], len ); } + if ( prefix & 8 ) { STRNCAT( buf, prefix_strings[4], len ); } + + /* return the number of characters added */ + return (len_orig - len); +} + +/* + * sprint's an operand's data to string str. + */ +static void get_operand_data_str( x86_op_t *op, char *str, int len ){ + + if ( op->flags & op_signed ) { + switch ( op->datatype ) { + case op_byte: + snprintf( str, len, "%" PRId8, op->data.sbyte ); + return; + case op_word: + snprintf( str, len, "%" PRId16, op->data.sword ); + return; + case op_qword: + snprintf( str, len, "%" PRId64, op->data.sqword ); + return; + default: + snprintf( str, len, "%" PRId32, op->data.sdword ); + return; + } + } + + //else + switch ( op->datatype ) { + case op_byte: + snprintf( str, len, "0x%02" PRIX8, op->data.byte ); + return; + case op_word: + snprintf( str, len, "0x%04" PRIX16, op->data.word ); + return; + case op_qword: + snprintf( str, len, "0x%08" PRIX64,op->data.sqword ); + return; + default: + snprintf( str, len, "0x%08" PRIX32, op->data.dword ); + return; + } +} + +/* + * sprints register types to a string. the register types can be ORed + * together. + */ +static void get_operand_regtype_str( int regtype, char *str, int len ) +{ + static struct { + const char *name; + int value; + } operand_regtypes[] = { + {"reg_gen" , 0x00001}, + {"reg_in" , 0x00002}, + {"reg_out" , 0x00004}, + {"reg_local" , 0x00008}, + {"reg_fpu" , 0x00010}, + {"reg_seg" , 0x00020}, + {"reg_simd" , 0x00040}, + {"reg_sys" , 0x00080}, + {"reg_sp" , 0x00100}, + {"reg_fp" , 0x00200}, + {"reg_pc" , 0x00400}, + {"reg_retaddr", 0x00800}, + {"reg_cond" , 0x01000}, + {"reg_zero" , 0x02000}, + {"reg_ret" , 0x04000}, + {"reg_src" , 0x10000}, + {"reg_dest" , 0x20000}, + {"reg_count" , 0x40000}, + {NULL, 0}, //end + }; + + unsigned int i; + + memset( str, 0, len ); + + //go thru every type in the enum + for ( i = 0; operand_regtypes[i].name; i++ ) { + //skip if type is not set + if(! (regtype & operand_regtypes[i].value) ) + continue; + + //not the first time around + if( str[0] ) { + STRNCAT( str, " ", len ); + } + + STRNCAT(str, operand_regtypes[i].name, len ); + } +} + +static int format_expr( x86_ea_t *ea, char *buf, int len, + enum x86_asm_format format ) { + char str[MAX_OP_STRING]; + + if ( format == att_syntax ) { + if (ea->base.name[0] || ea->index.name[0] || ea->scale) { + PRINT_DISPLACEMENT(ea); + STRNCAT( buf, "(", len ); + + if ( ea->base.name[0]) { + STRNCATF( buf, "%%%s", ea->base.name, len ); + } + if ( ea->index.name[0]) { + STRNCATF( buf, ",%%%s", ea->index.name, len ); + if ( ea->scale > 1 ) { + STRNCATF( buf, ",%d", ea->scale, len ); + } + } + /* handle the syntactic exception */ + if ( ! ea->base.name[0] && + ! ea->index.name[0] ) { + STRNCATF( buf, ",%d", ea->scale, len ); + } + + STRNCAT( buf, ")", len ); + } else + STRNCATF( buf, "0x%" PRIX32, ea->disp, len ); + + } else if ( format == xml_syntax ){ + + if ( ea->base.name[0]) { + STRNCAT (buf, "\t\t\t\n", len); + + get_operand_regtype_str (ea->base.type, str, + sizeof str); + STRNCAT (buf, "\t\t\t\tbase.name, len); + STRNCATF (buf, "type=\"%s\" ", str, len); + STRNCATF (buf, "size=%d/>\n", ea->base.size, len); + + STRNCAT (buf, "\t\t\t\n", len); + } + + if ( ea->index.name[0]) { + STRNCAT (buf, "\t\t\t\n", len); + + get_operand_regtype_str (ea->index.type, str, + sizeof str); + + STRNCAT (buf, "\t\t\t\tindex.name, len); + STRNCATF (buf, "type=\"%s\" ", str, len); + STRNCATF (buf, "size=%d/>\n", ea->index.size, len); + + STRNCAT (buf, "\t\t\t\n", len); + } + + //scale + STRNCAT (buf, "\t\t\t\n", len); + STRNCAT (buf, "\t\t\t\t\n", ea->scale, len); + STRNCAT (buf, "\t\t\t\n", len); + + if ( ea->disp_size ) { + + STRNCAT (buf, "\t\t\t\n", len); + + if ( ea->disp_size > 1 && ! ea->disp_sign ) { + STRNCAT (buf, "\t\t\t\t
    \n", ea->disp, + len); + } else { + STRNCAT (buf, "\t\t\t\t\n", ea->disp, len); + } + + STRNCAT (buf, "\t\t\t\n", len); + } + + } else if ( format == raw_syntax ) { + + PRINT_DISPLACEMENT(ea); + STRNCAT( buf, "(", len ); + + STRNCATF( buf, "%s,", ea->base.name, len ); + STRNCATF( buf, "%s,", ea->index.name, len ); + STRNCATF( buf, "%d", ea->scale, len ); + STRNCAT( buf, ")", len ); + + } else { + + STRNCAT( buf, "[", len ); + + if ( ea->base.name[0] ) { + STRNCAT( buf, ea->base.name, len ); + if ( ea->index.name[0] || + (ea->disp_size && ! ea->disp_sign) ) { + STRNCAT( buf, "+", len ); + } + } + if ( ea->index.name[0] ) { + STRNCAT( buf, ea->index.name, len ); + if ( ea->scale > 1 ) + { + STRNCATF( buf, "*%" PRId32, ea->scale, len ); + } + if ( ea->disp_size && ! ea->disp_sign ) + { + STRNCAT( buf, "+", len ); + } + } + + if ( ea->disp_size || (! ea->index.name[0] && + ! ea->base.name[0] ) ) + { + PRINT_DISPLACEMENT(ea); + } + + STRNCAT( buf, "]", len ); + } + + return( strlen(buf) ); +} + +static int format_seg( x86_op_t *op, char *buf, int len, + enum x86_asm_format format ) { + int len_orig = len; + const char *reg = ""; + + if (! op || ! buf || ! len || ! op->flags) { + return(0); + } + if ( op->type != op_offset && op->type != op_expression ){ + return(0); + } + if (! ((int) op->flags & 0xF00) ) { + return(0); + } + + switch (op->flags & 0xF00) { + case op_es_seg: reg = "es"; break; + case op_cs_seg: reg = "cs"; break; + case op_ss_seg: reg = "ss"; break; + case op_ds_seg: reg = "ds"; break; + case op_fs_seg: reg = "fs"; break; + case op_gs_seg: reg = "gs"; break; + default: + break; + } + + if (! reg[0] ) { + return( 0 ); + } + + switch( format ) { + case xml_syntax: + STRNCAT( buf, "\t\t\t\n", reg, len ); + break; + case att_syntax: + STRNCATF( buf, "%%%s:", reg, len ); + break; + + default: + STRNCATF( buf, "%s:", reg, len ); + break; + } + + return( len_orig - len ); /* return length of appended string */ +} + +static const char *get_operand_datatype_str( x86_op_t *op ){ + + static const char *types[] = { + "sbyte", /* 0 */ + "sword", + "sqword", + "sdword", + "sdqword", /* 4 */ + "byte", + "word", + "qword", + "dword", /* 8 */ + "dqword", + "sreal", + "dreal", + "extreal", /* 12 */ + "bcd", + "ssimd", + "dsimd", + "sssimd", /* 16 */ + "sdsimd", + "descr32", + "descr16", + "pdescr32", /* 20 */ + "pdescr16", + "bounds16", + "bounds32", + "fpu_env16", + "fpu_env32", /* 25 */ + "fpu_state16", + "fpu_state32", + "fp_reg_set" + }; + + /* handle signed values first */ + if ( op->flags & op_signed ) { + switch (op->datatype) { + case op_byte: return types[0]; + case op_word: return types[1]; + case op_qword: return types[2]; + case op_dqword: return types[4]; + default: return types[3]; + } + } + + switch (op->datatype) { + case op_byte: return types[5]; + case op_word: return types[6]; + case op_qword: return types[7]; + case op_dqword: return types[9]; + case op_sreal: return types[10]; + case op_dreal: return types[11]; + case op_extreal: return types[12]; + case op_bcd: return types[13]; + case op_ssimd: return types[14]; + case op_dsimd: return types[15]; + case op_sssimd: return types[16]; + case op_sdsimd: return types[17]; + case op_descr32: return types[18]; + case op_descr16: return types[19]; + case op_pdescr32: return types[20]; + case op_pdescr16: return types[21]; + case op_bounds16: return types[22]; + case op_bounds32: return types[23]; + case op_fpustate16: return types[24]; + case op_fpustate32: return types[25]; + case op_fpuenv16: return types[26]; + case op_fpuenv32: return types[27]; + case op_fpregset: return types[28]; + default: return types[8]; + } +} + +static int format_insn_eflags_str( enum x86_flag_status flags, char *buf, + int len) { + + static struct { + const char *name; + int value; + } insn_flags[] = { + { "carry_set ", 0x0001 }, + { "zero_set ", 0x0002 }, + { "oflow_set ", 0x0004 }, + { "dir_set ", 0x0008 }, + { "sign_set ", 0x0010 }, + { "parity_set ", 0x0020 }, + { "carry_or_zero_set ", 0x0040 }, + { "zero_set_or_sign_ne_oflow ", 0x0080 }, + { "carry_clear ", 0x0100 }, + { "zero_clear ", 0x0200 }, + { "oflow_clear ", 0x0400 }, + { "dir_clear ", 0x0800 }, + { "sign_clear ", 0x1000 }, + { "parity_clear ", 0x2000 }, + { "sign_eq_oflow ", 0x4000 }, + { "sign_ne_oflow ", 0x8000 }, + { NULL, 0x0000 }, //end + }; + + unsigned int i; + int len_orig = len; + + for (i = 0; insn_flags[i].name; i++) { + if (! (flags & insn_flags[i].value) ) + continue; + + STRNCAT( buf, insn_flags[i].name, len ); + } + + return( len_orig - len ); +} + +static const char *get_insn_group_str( enum x86_insn_group gp ) { + + static const char *types[] = { + "", // 0 + "controlflow",// 1 + "arithmetic", // 2 + "logic", // 3 + "stack", // 4 + "comparison", // 5 + "move", // 6 + "string", // 7 + "bit_manip", // 8 + "flag_manip", // 9 + "fpu", // 10 + "", // 11 + "", // 12 + "interrupt", // 13 + "system", // 14 + "other", // 15 + }; + + if ( gp > sizeof (types)/sizeof(types[0]) ) + return ""; + + return types[gp]; +} + +static const char *get_insn_type_str( enum x86_insn_type type ) { + + static struct { + const char *name; + int value; + } types[] = { + /* insn_controlflow */ + { "jmp", 0x1001 }, + { "jcc", 0x1002 }, + { "call", 0x1003 }, + { "callcc", 0x1004 }, + { "return", 0x1005 }, + { "loop", 0x1006 }, + /* insn_arithmetic */ + { "add", 0x2001 }, + { "sub", 0x2002 }, + { "mul", 0x2003 }, + { "div", 0x2004 }, + { "inc", 0x2005 }, + { "dec", 0x2006 }, + { "shl", 0x2007 }, + { "shr", 0x2008 }, + { "rol", 0x2009 }, + { "ror", 0x200A }, + /* insn_logic */ + { "and", 0x3001 }, + { "or", 0x3002 }, + { "xor", 0x3003 }, + { "not", 0x3004 }, + { "neg", 0x3005 }, + /* insn_stack */ + { "push", 0x4001 }, + { "pop", 0x4002 }, + { "pushregs", 0x4003 }, + { "popregs", 0x4004 }, + { "pushflags", 0x4005 }, + { "popflags", 0x4006 }, + { "enter", 0x4007 }, + { "leave", 0x4008 }, + /* insn_comparison */ + { "test", 0x5001 }, + { "cmp", 0x5002 }, + /* insn_move */ + { "mov", 0x6001 }, /* move */ + { "movcc", 0x6002 }, /* conditional move */ + { "xchg", 0x6003 }, /* exchange */ + { "xchgcc", 0x6004 }, /* conditional exchange */ + /* insn_string */ + { "strcmp", 0x7001 }, + { "strload", 0x7002 }, + { "strmov", 0x7003 }, + { "strstore", 0x7004 }, + { "translate", 0x7005 }, /* xlat */ + /* insn_bit_manip */ + { "bittest", 0x8001 }, + { "bitset", 0x8002 }, + { "bitclear", 0x8003 }, + /* insn_flag_manip */ + { "clear_carry", 0x9001 }, + { "clear_zero", 0x9002 }, + { "clear_oflow", 0x9003 }, + { "clear_dir", 0x9004 }, + { "clear_sign", 0x9005 }, + { "clear_parity", 0x9006 }, + { "set_carry", 0x9007 }, + { "set_zero", 0x9008 }, + { "set_oflow", 0x9009 }, + { "set_dir", 0x900A }, + { "set_sign", 0x900B }, + { "set_parity", 0x900C }, + { "tog_carry", 0x9010 }, + { "tog_zero", 0x9020 }, + { "tog_oflow", 0x9030 }, + { "tog_dir", 0x9040 }, + { "tog_sign", 0x9050 }, + { "tog_parity", 0x9060 }, + /* insn_fpu */ + { "fmov", 0xA001 }, + { "fmovcc", 0xA002 }, + { "fneg", 0xA003 }, + { "fabs", 0xA004 }, + { "fadd", 0xA005 }, + { "fsub", 0xA006 }, + { "fmul", 0xA007 }, + { "fdiv", 0xA008 }, + { "fsqrt", 0xA009 }, + { "fcmp", 0xA00A }, + { "fcos", 0xA00C }, + { "fldpi", 0xA00D }, + { "fldz", 0xA00E }, + { "ftan", 0xA00F }, + { "fsine", 0xA010 }, + { "fsys", 0xA020 }, + /* insn_interrupt */ + { "int", 0xD001 }, + { "intcc", 0xD002 }, /* not present in x86 ISA */ + { "iret", 0xD003 }, + { "bound", 0xD004 }, + { "debug", 0xD005 }, + { "trace", 0xD006 }, + { "invalid_op", 0xD007 }, + { "oflow", 0xD008 }, + /* insn_system */ + { "halt", 0xE001 }, + { "in", 0xE002 }, /* input from port/bus */ + { "out", 0xE003 }, /* output to port/bus */ + { "cpuid", 0xE004 }, + /* insn_other */ + { "nop", 0xF001 }, + { "bcdconv", 0xF002 }, /* convert to or from BCD */ + { "szconv", 0xF003 }, /* change size of operand */ + { NULL, 0 }, //end + }; + + unsigned int i; + + //go thru every type in the enum + for ( i = 0; types[i].name; i++ ) { + if ( types[i].value == type ) + return types[i].name; + } + + return ""; +} + +static const char *get_insn_cpu_str( enum x86_insn_cpu cpu ) { + static const char *intel[] = { + "", // 0 + "8086", // 1 + "80286", // 2 + "80386", // 3 + "80387", // 4 + "80486", // 5 + "Pentium", // 6 + "Pentium Pro", // 7 + "Pentium 2", // 8 + "Pentium 3", // 9 + "Pentium 4" // 10 + }; + + if ( cpu < sizeof(intel)/sizeof(intel[0]) ) { + return intel[cpu]; + } else if ( cpu == 16 ) { + return "K6"; + } else if ( cpu == 32 ) { + return "K7"; + } else if ( cpu == 48 ) { + return "Athlon"; + } + + return ""; +} + +static const char *get_insn_isa_str( enum x86_insn_isa isa ) { + static const char *subset[] = { + NULL, // 0 + "General Purpose", // 1 + "Floating Point", // 2 + "FPU Management", // 3 + "MMX", // 4 + "SSE", // 5 + "SSE2", // 6 + "SSE3", // 7 + "3DNow!", // 8 + "System" // 9 + }; + + if ( isa > sizeof (subset)/sizeof(subset[0]) ) { + return ""; + } + + return subset[isa]; +} + +static int format_operand_att( x86_op_t *op, x86_insn_t *insn, char *buf, + int len){ + + char str[MAX_OP_STRING]; + + memset (str, 0, sizeof str); + + switch ( op->type ) { + case op_register: + STRNCATF( buf, "%%%s", op->data.reg.name, len ); + break; + + case op_immediate: + get_operand_data_str( op, str, sizeof str ); + STRNCATF( buf, "$%s", str, len ); + break; + + case op_relative_near: + STRNCATF( buf, "0x%08X", + (unsigned int)(op->data.sbyte + + insn->addr + insn->size), len ); + break; + + case op_relative_far: + if (op->datatype == op_word) { + STRNCATF( buf, "0x%08X", + (unsigned int)(op->data.sword + + insn->addr + insn->size), len ); + } else { + STRNCATF( buf, "0x%08X", + (unsigned int)(op->data.sdword + + insn->addr + insn->size), len ); + } + break; + + case op_absolute: + /* ATT uses the syntax $section, $offset */ + STRNCATF( buf, "$0x%04" PRIX16 ", ", op->data.absolute.segment, + len ); + if (op->datatype == op_descr16) { + STRNCATF( buf, "$0x%04" PRIX16, + op->data.absolute.offset.off16, len ); + } else { + STRNCATF( buf, "$0x%08" PRIX32, + op->data.absolute.offset.off32, len ); + } + break; + case op_offset: + /* ATT requires a '*' before JMP/CALL ops */ + if (insn->type == insn_jmp || insn->type == insn_call) + STRNCAT( buf, "*", len ); + + len -= format_seg( op, buf, len, att_syntax ); + STRNCATF( buf, "0x%08" PRIX32, op->data.sdword, len ); + break; + + case op_expression: + /* ATT requires a '*' before JMP/CALL ops */ + if (insn->type == insn_jmp || insn->type == insn_call) + STRNCAT( buf, "*", len ); + + len -= format_seg( op, buf, len, att_syntax ); + len -= format_expr( &op->data.expression, buf, len, + att_syntax ); + break; + case op_unused: + case op_unknown: + /* return 0-truncated buffer */ + break; + } + + return ( strlen( buf ) ); +} + +static int format_operand_native( x86_op_t *op, x86_insn_t *insn, char *buf, + int len){ + + char str[MAX_OP_STRING]; + + switch (op->type) { + case op_register: + STRNCAT( buf, op->data.reg.name, len ); + break; + + case op_immediate: + get_operand_data_str( op, str, sizeof str ); + STRNCAT( buf, str, len ); + break; + + case op_relative_near: + STRNCATF( buf, "0x%08" PRIX32, + (unsigned int)(op->data.sbyte + + insn->addr + insn->size), len ); + break; + + case op_relative_far: + if ( op->datatype == op_word ) { + STRNCATF( buf, "0x%08" PRIX32, + (unsigned int)(op->data.sword + + insn->addr + insn->size), len ); + break; + } else { + STRNCATF( buf, "0x%08" PRIX32, op->data.sdword + + insn->addr + insn->size, len ); + } + break; + + case op_absolute: + STRNCATF( buf, "$0x%04" PRIX16 ":", op->data.absolute.segment, + len ); + if (op->datatype == op_descr16) { + STRNCATF( buf, "0x%04" PRIX16, + op->data.absolute.offset.off16, len ); + } else { + STRNCATF( buf, "0x%08" PRIX32, + op->data.absolute.offset.off32, len ); + } + break; + + case op_offset: + len -= format_seg( op, buf, len, native_syntax ); + STRNCATF( buf, "[0x%08" PRIX32 "]", op->data.sdword, len ); + break; + + case op_expression: + len -= format_seg( op, buf, len, native_syntax ); + len -= format_expr( &op->data.expression, buf, len, + native_syntax ); + break; + case op_unused: + case op_unknown: + /* return 0-truncated buffer */ + break; + } + + return( strlen( buf ) ); +} + +static int format_operand_xml( x86_op_t *op, x86_insn_t *insn, char *buf, + int len){ + + char str[MAX_OP_STRING] = "\0"; + + switch (op->type) { + case op_register: + + get_operand_regtype_str( op->data.reg.type, str, + sizeof str ); + + STRNCAT( buf, "\t\tdata.reg.name, len ); + STRNCATF( buf, "type=\"%s\" ", str, len ); + STRNCATF( buf, "size=%d/>\n", op->data.reg.size, len ); + break; + + case op_immediate: + + get_operand_data_str( op, str, sizeof str ); + + STRNCAT( buf, "\t\t\n", str, len ); + break; + + case op_relative_near: + STRNCAT( buf, "\t\t\n", + (unsigned int)(op->data.sbyte + + insn->addr + insn->size), len ); + break; + + case op_relative_far: + STRNCAT( buf, "\t\tdatatype == op_word) { + STRNCATF( buf, "value=\"0x%08" PRIX32 "\"/>\n", + (unsigned int)(op->data.sword + + insn->addr + insn->size), len); + break; + } else { + + STRNCATF( buf, "value=\"0x%08" PRIX32 "\"/>\n", + op->data.sdword + insn->addr + insn->size, + len ); + } + break; + + case op_absolute: + + STRNCATF( buf, + "\t\tdata.absolute.segment, len ); + + if (op->datatype == op_descr16) { + STRNCATF( buf, "offset=\"0x%04" PRIX16 "\">", + op->data.absolute.offset.off16, len ); + } else { + STRNCATF( buf, "offset=\"0x%08" PRIX32 "\">", + op->data.absolute.offset.off32, len ); + } + + STRNCAT( buf, "\t\t\n", len ); + break; + + case op_expression: + + + STRNCAT( buf, "\t\t\n", len ); + + len -= format_seg( op, buf, len, xml_syntax ); + len -= format_expr( &op->data.expression, buf, len, + xml_syntax ); + + STRNCAT( buf, "\t\t\n", len ); + break; + + case op_offset: + + STRNCAT( buf, "\t\t\n", len ); + + len -= format_seg( op, buf, len, xml_syntax ); + + STRNCAT( buf, "\t\t\t
    \n", + op->data.sdword, len ); + STRNCAT( buf, "\t\t\n", len ); + break; + + case op_unused: + case op_unknown: + /* return 0-truncated buffer */ + break; + } + + return( strlen( buf ) ); +} + +static int format_operand_raw( x86_op_t *op, x86_insn_t *insn, char *buf, + int len){ + + char str[MAX_OP_RAW_STRING]; + const char *datatype = get_operand_datatype_str(op); + + switch (op->type) { + case op_register: + + get_operand_regtype_str( op->data.reg.type, str, + sizeof str ); + + STRNCAT( buf, "reg|", len ); + STRNCATF( buf, "%s|", datatype, len ); + STRNCATF( buf, "%s:", op->data.reg.name, len ); + STRNCATF( buf, "%s:", str, len ); + STRNCATF( buf, "%d|", op->data.reg.size, len ); + break; + + case op_immediate: + + get_operand_data_str( op, str, sizeof str ); + + STRNCAT( buf, "immediate|", len ); + STRNCATF( buf, "%s|", datatype, len ); + STRNCATF( buf, "%s|", str, len ); + break; + + case op_relative_near: + /* NOTE: in raw format, we print the + * relative offset, not the actual + * address of the jump target */ + + STRNCAT( buf, "relative|", len ); + STRNCATF( buf, "%s|", datatype, len ); + STRNCATF( buf, "%" PRId8 "|", op->data.sbyte, len ); + break; + + case op_relative_far: + + STRNCAT( buf, "relative|", len ); + STRNCATF( buf, "%s|", datatype, len ); + + if (op->datatype == op_word) { + STRNCATF( buf, "%" PRId16 "|", op->data.sword, len); + break; + } else { + STRNCATF( buf, "%" PRId32 "|", op->data.sdword, len ); + } + break; + + case op_absolute: + + STRNCAT( buf, "absolute_address|", len ); + STRNCATF( buf, "%s|", datatype, len ); + + STRNCATF( buf, "$0x%04" PRIX16 ":", op->data.absolute.segment, + len ); + if (op->datatype == op_descr16) { + STRNCATF( buf, "0x%04" PRIX16 "|", + op->data.absolute.offset.off16, len ); + } else { + STRNCATF( buf, "0x%08" PRIX32 "|", + op->data.absolute.offset.off32, len ); + } + + break; + + case op_expression: + + STRNCAT( buf, "address_expression|", len ); + STRNCATF( buf, "%s|", datatype, len ); + + len -= format_seg( op, buf, len, native_syntax ); + len -= format_expr( &op->data.expression, buf, len, + raw_syntax ); + + STRNCAT( buf, "|", len ); + break; + + case op_offset: + + STRNCAT( buf, "segment_offset|", len ); + STRNCATF( buf, "%s|", datatype, len ); + + len -= format_seg( op, buf, len, xml_syntax ); + + STRNCATF( buf, "%08" PRIX32 "|", op->data.sdword, len ); + break; + + case op_unused: + case op_unknown: + /* return 0-truncated buffer */ + break; + } + + return( strlen( buf ) ); +} + +int x86_format_operand( x86_op_t *op, char *buf, int len, + enum x86_asm_format format ){ + x86_insn_t *insn; + + if ( ! op || ! buf || len < 1 ) { + return(0); + } + + /* insn is stored in x86_op_t since .21-pre3 */ + insn = (x86_insn_t *) op->insn; + + memset( buf, 0, len ); + + switch ( format ) { + case att_syntax: + return format_operand_att( op, insn, buf, len ); + case xml_syntax: + return format_operand_xml( op, insn, buf, len ); + case raw_syntax: + return format_operand_raw( op, insn, buf, len ); + case native_syntax: + case intel_syntax: + default: + return format_operand_native( op, insn, buf, len ); + } +} + +#define is_imm_jmp(op) (op->type == op_absolute || \ + op->type == op_immediate || \ + op->type == op_offset) +#define is_memory_op(op) (op->type == op_absolute || \ + op->type == op_expression || \ + op->type == op_offset) + +static int format_att_mnemonic( x86_insn_t *insn, char *buf, int len) { + int size = 0; + const char *suffix; + + if (! insn || ! buf || ! len ) + return(0); + + memset( buf, 0, len ); + + /* do long jump/call prefix */ + if ( insn->type == insn_jmp || insn->type == insn_call ) { + if (! is_imm_jmp( x86_operand_1st(insn) ) || + (x86_operand_1st(insn))->datatype != op_byte ) { + /* far jump/call, use "l" prefix */ + STRNCAT( buf, "l", len ); + } + STRNCAT( buf, insn->mnemonic, len ); + + return ( strlen( buf ) ); + } + + /* do mnemonic */ + STRNCAT( buf, insn->mnemonic, len ); + + /* do suffixes for memory operands */ + if (!(insn->note & insn_note_nosuffix) && + (insn->group == insn_arithmetic || + insn->group == insn_logic || + insn->group == insn_move || + insn->group == insn_stack || + insn->group == insn_string || + insn->group == insn_comparison || + insn->type == insn_in || + insn->type == insn_out + )) { + if ( x86_operand_count( insn, op_explicit ) > 0 && + is_memory_op( x86_operand_1st(insn) ) ){ + size = x86_operand_size( x86_operand_1st( insn ) ); + } else if ( x86_operand_count( insn, op_explicit ) > 1 && + is_memory_op( x86_operand_2nd(insn) ) ){ + size = x86_operand_size( x86_operand_2nd( insn ) ); + } + } + + if ( size == 1 ) suffix = "b"; + else if ( size == 2 ) suffix = "w"; + else if ( size == 4 ) suffix = "l"; + else if ( size == 8 ) suffix = "q"; + else suffix = ""; + + STRNCAT( buf, suffix, len ); + return ( strlen( buf ) ); +} + +int x86_format_mnemonic(x86_insn_t *insn, char *buf, int len, + enum x86_asm_format format){ + char str[MAX_OP_STRING]; + + memset( buf, 0, len ); + STRNCAT( buf, insn->prefix_string, len ); + if ( format == att_syntax ) { + format_att_mnemonic( insn, str, sizeof str ); + STRNCAT( buf, str, len ); + } else { + STRNCAT( buf, insn->mnemonic, len ); + } + + return( strlen( buf ) ); +} + +struct op_string { char *buf; size_t len; }; + +static void format_op_raw( x86_op_t *op, x86_insn_t *insn, void *arg ) { + struct op_string * opstr = (struct op_string *) arg; + + format_operand_raw(op, insn, opstr->buf, opstr->len); +} + +static int format_insn_note(x86_insn_t *insn, char *buf, int len){ + char note[32] = {0}; + int len_orig = len, note_len = 32; + + if ( insn->note & insn_note_ring0 ) { + STRNCATF( note, "%s", "Ring0 ", note_len ); + } + if ( insn->note & insn_note_smm ) { + STRNCATF( note, "%s", "SMM ", note_len ); + } + if ( insn->note & insn_note_serial ) { + STRNCATF(note, "%s", "Serialize ", note_len ); + } + STRNCATF( buf, "%s|", note, len ); + + return( len_orig - len ); +} + +static int format_raw_insn( x86_insn_t *insn, char *buf, int len ){ + struct op_string opstr = { buf, len }; + int i; + + /* RAW style: + * ADDRESS|OFFSET|SIZE|BYTES| + * PREFIX|PREFIX_STRING|GROUP|TYPE|NOTES| + * MNEMONIC|CPU|ISA|FLAGS_SET|FLAGS_TESTED| + * STACK_MOD|STACK_MOD_VAL + * [|OP_TYPE|OP_DATATYPE|OP_ACCESS|OP_FLAGS|OP]* + * + * Register values are encoded as: + * NAME:TYPE:SIZE + * + * Effective addresses are encoded as: + * disp(base_reg,index_reg,scale) + */ + STRNCATF( buf, "0x%08" PRIX32 "|", insn->addr , len ); + STRNCATF( buf, "0x%08" PRIX32 "|", insn->offset, len ); + STRNCATF( buf, "%d|" , insn->size , len ); + + /* print bytes */ + for ( i = 0; i < insn->size; i++ ) { + STRNCATF( buf, "%02X ", insn->bytes[i], len ); + } + STRNCAT( buf, "|", len ); + + len -= format_insn_prefix_str( insn->prefix, buf, len ); + STRNCATF( buf, "|%s|", insn->prefix_string , len ); + STRNCATF( buf, "%s|", get_insn_group_str( insn->group ), len ); + STRNCATF( buf, "%s|", get_insn_type_str( insn->type ) , len ); + STRNCATF( buf, "%s|", insn->mnemonic , len ); + STRNCATF( buf, "%s|", get_insn_cpu_str( insn->cpu ) , len ); + STRNCATF( buf, "%s|", get_insn_isa_str( insn->isa ) , len ); + + /* insn note */ + len -= format_insn_note( insn, buf, len ); + + len -= format_insn_eflags_str( insn->flags_set, buf, len ); + STRNCAT( buf, "|", len ); + len -= format_insn_eflags_str( insn->flags_tested, buf, len ); + STRNCAT( buf, "|", len ); + STRNCATF( buf, "%d|", insn->stack_mod, len ); + STRNCATF( buf, "%" PRId32 "|", insn->stack_mod_val, len ); + + opstr.len = len; + x86_operand_foreach( insn, format_op_raw, &opstr, op_any ); + + return( strlen (buf) ); +} + +static int format_xml_insn( x86_insn_t *insn, char *buf, int len ) { + char str[MAX_OP_XML_STRING]; + int i; + + STRNCAT( buf, "\n", len ); + + STRNCATF( buf, "\t
    addr, len ); + STRNCATF( buf, "offset=\"0x%08" PRIX32 "\" ", insn->offset, len ); + STRNCATF( buf, "size=%d bytes=\"", insn->size, len ); + + for ( i = 0; i < insn->size; i++ ) { + STRNCATF( buf, "%02X ", insn->bytes[i], len ); + } + STRNCAT( buf, "\"/>\n", len ); + + STRNCAT( buf, "\tprefix, buf, len ); + STRNCATF( buf, "\" string=\"%s\"/>\n", insn->prefix_string, len ); + + STRNCATF( buf, "\tgroup), len ); + STRNCATF( buf, "type=\"%s\" ", get_insn_type_str (insn->type), len ); + STRNCATF( buf, "string=\"%s\"/>\n", insn->mnemonic, len ); + + STRNCAT( buf, "\t\n", len ); + STRNCAT( buf, "\t\tflags_set, buf, len ); + STRNCAT( buf, "\"/>\n\t\n", len ); + + + STRNCAT( buf, "\t\n", len ); + STRNCAT( buf, "\t\tflags_tested, buf, len ); + STRNCAT( buf, "\"/>\n\t\n", len ); + + if ( x86_operand_1st( insn ) ) { + x86_format_operand( x86_operand_1st(insn), str, + sizeof str, xml_syntax); + STRNCAT( buf, "\t\n", len ); + STRNCAT( buf, str, len ); + STRNCAT( buf, "\t\n", len ); + } + + if ( x86_operand_2nd( insn ) ) { + x86_format_operand( x86_operand_2nd( insn ), str, + sizeof str, xml_syntax); + STRNCAT( buf, "\t\n", len ); + STRNCAT( buf, str, len ); + STRNCAT( buf, "\t\n", len ); + } + + if ( x86_operand_3rd( insn ) ) { + x86_format_operand( x86_operand_3rd(insn), str, + sizeof str, xml_syntax); + STRNCAT( buf, "\t\n", len ); + STRNCAT( buf, str, len ); + STRNCAT( buf, "\t\n", len ); + } + + STRNCAT( buf, "\n", len ); + + return strlen (buf); +} + +int x86_format_header( char *buf, int len, enum x86_asm_format format ) { + switch (format) { + case att_syntax: + snprintf( buf, len, "MNEMONIC\tSRC, DEST, IMM" ); + break; + case intel_syntax: + snprintf( buf, len, "MNEMONIC\tDEST, SRC, IMM" ); + break; + case native_syntax: + snprintf( buf, len, "ADDRESS\tBYTES\tMNEMONIC\t" + "DEST\tSRC\tIMM" ); + break; + case raw_syntax: + snprintf( buf, len, "ADDRESS|OFFSET|SIZE|BYTES|" + "PREFIX|PREFIX_STRING|GROUP|TYPE|NOTES|" + "MNEMONIC|CPU|ISA|FLAGS_SET|FLAGS_TESTED|" + "STACK_MOD|STACK_MOD_VAL" + "[|OP_TYPE|OP_DATATYPE|OP_ACCESS|OP_FLAGS|OP]*" + ); + break; + case xml_syntax: + snprintf( buf, len, + "" + "
    " + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "" + "
    " + "" + "" + "" + "
    " + "" + "" + "" + ); + break; + case unknown_syntax: + if ( len ) { + buf[0] = '\0'; + } + break; + } + + return( strlen(buf) ); +} + +int x86_format_insn( x86_insn_t *insn, char *buf, int len, + enum x86_asm_format format ){ + char str[MAX_OP_STRING]; + x86_op_t *src, *dst; + int i; + + memset(buf, 0, len); + if ( format == intel_syntax ) { + /* INTEL STYLE: mnemonic dest, src, imm */ + STRNCAT( buf, insn->prefix_string, len ); + STRNCAT( buf, insn->mnemonic, len ); + STRNCAT( buf, "\t", len ); + + /* dest */ + if ( (dst = x86_operand_1st( insn )) && !(dst->flags & op_implied) ) { + x86_format_operand( dst, str, MAX_OP_STRING, format); + STRNCAT( buf, str, len ); + } + + /* src */ + if ( (src = x86_operand_2nd( insn )) ) { + if ( !(dst->flags & op_implied) ) { + STRNCAT( buf, ", ", len ); + } + x86_format_operand( src, str, MAX_OP_STRING, format); + STRNCAT( buf, str, len ); + } + + /* imm */ + if ( x86_operand_3rd( insn )) { + STRNCAT( buf, ", ", len ); + x86_format_operand( x86_operand_3rd( insn ), + str, MAX_OP_STRING, format); + STRNCAT( buf, str, len ); + } + + } else if ( format == att_syntax ) { + /* ATT STYLE: mnemonic src, dest, imm */ + STRNCAT( buf, insn->prefix_string, len ); + format_att_mnemonic(insn, str, MAX_OP_STRING); + STRNCATF( buf, "%s\t", str, len); + + + /* not sure which is correct? sometimes GNU as requires + * an imm as the first operand, sometimes as the third... */ + /* imm */ + if ( x86_operand_3rd( insn ) ) { + x86_format_operand(x86_operand_3rd( insn ), + str, MAX_OP_STRING, format); + STRNCAT( buf, str, len ); + /* there is always 'dest' operand if there is 'src' */ + STRNCAT( buf, ", ", len ); + } + + if ( (insn->note & insn_note_nonswap ) == 0 ) { + /* regular AT&T style swap */ + src = x86_operand_2nd( insn ); + dst = x86_operand_1st( insn ); + } + else { + /* special-case instructions */ + src = x86_operand_1st( insn ); + dst = x86_operand_2nd( insn ); + } + + /* src */ + if ( src ) { + x86_format_operand(src, str, MAX_OP_STRING, format); + STRNCAT( buf, str, len ); + /* there is always 'dest' operand if there is 'src' */ + if ( dst && !(dst->flags & op_implied) ) { + STRNCAT( buf, ", ", len ); + } + } + + /* dest */ + if ( dst && !(dst->flags & op_implied) ) { + x86_format_operand( dst, str, MAX_OP_STRING, format); + STRNCAT( buf, str, len ); + } + + + } else if ( format == raw_syntax ) { + format_raw_insn( insn, buf, len ); + } else if ( format == xml_syntax ) { + format_xml_insn( insn, buf, len ); + } else { /* default to native */ + /* NATIVE style: RVA\tBYTES\tMNEMONIC\tOPERANDS */ + /* print address */ + STRNCATF( buf, "%08" PRIX32 "\t", insn->addr, len ); + + /* print bytes */ + for ( i = 0; i < insn->size; i++ ) { + STRNCATF( buf, "%02X ", insn->bytes[i], len ); + } + + STRNCAT( buf, "\t", len ); + + /* print mnemonic */ + STRNCAT( buf, insn->prefix_string, len ); + STRNCAT( buf, insn->mnemonic, len ); + STRNCAT( buf, "\t", len ); + + /* print operands */ + /* dest */ + if ( x86_operand_1st( insn ) ) { + x86_format_operand( x86_operand_1st( insn ), + str, MAX_OP_STRING, format); + STRNCATF( buf, "%s\t", str, len ); + } + + /* src */ + if ( x86_operand_2nd( insn ) ) { + x86_format_operand(x86_operand_2nd( insn ), + str, MAX_OP_STRING, format); + STRNCATF( buf, "%s\t", str, len ); + } + + /* imm */ + if ( x86_operand_3rd( insn )) { + x86_format_operand( x86_operand_3rd( insn ), + str, MAX_OP_STRING, format); + STRNCAT( buf, str, len ); + } + } + + return( strlen( buf ) ); +} + diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/x86_imm.c b/shared/sentry/external/breakpad/src/third_party/libdisasm/x86_imm.c new file mode 100644 index 000000000..cd59bfc9a --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/x86_imm.c @@ -0,0 +1,70 @@ +#include "qword.h" +#include "x86_imm.h" + +#include + +unsigned int x86_imm_signsized( unsigned char * buf, size_t buf_len, + void *dest, unsigned int size ) { + signed char *cp = (signed char *) dest; + signed short *sp = (signed short *) dest; + int32_t *lp = (int32_t *) dest; + qword_t *qp = (qword_t *) dest; + + if ( size > buf_len ) { + return 0; + } + + /* Copy 'size' bytes from *buf to *op + * return number of bytes copied */ + switch (size) { + case 1: /* BYTE */ + *cp = *((signed char *) buf); + break; + case 2: /* WORD */ + *sp = *((signed short *) buf); + break; + case 6: + case 8: /* QWORD */ + *qp = *((qword_t *) buf); + break; + case 4: /* DWORD */ + default: + *lp = *((int32_t *) buf); + break; + } + return (size); +} + +unsigned int x86_imm_sized( unsigned char * buf, size_t buf_len, void *dest, + unsigned int size ) { + unsigned char *cp = (unsigned char *) dest; + unsigned short *sp = (unsigned short *) dest; + uint32_t *lp = (uint32_t *) dest; + qword_t *qp = (qword_t *) dest; + + if ( size > buf_len ) { + return 0; + } + + /* Copy 'size' bytes from *buf to *op + * return number of bytes copied */ + switch (size) { + case 1: /* BYTE */ + *cp = *((unsigned char *) buf); + break; + case 2: /* WORD */ + *sp = *((unsigned short *) buf); + break; + case 6: + case 8: /* QWORD */ + *qp = *((qword_t *) buf); + break; + case 4: /* DWORD */ + default: + *lp = *((uint32_t *) buf); + break; + } + + return (size); +} + diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/x86_imm.h b/shared/sentry/external/breakpad/src/third_party/libdisasm/x86_imm.h new file mode 100644 index 000000000..fa35ff2de --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/x86_imm.h @@ -0,0 +1,18 @@ +#ifndef x86_IMM_H +#define x86_IMM_H + +#include "./qword.h" +#include + +#ifdef WIN32 +#include +#endif + +/* these are in the global x86 namespace but are not a part of the + * official API */ +unsigned int x86_imm_sized( unsigned char *buf, size_t buf_len, void *dest, + unsigned int size ); + +unsigned int x86_imm_signsized( unsigned char *buf, size_t buf_len, void *dest, + unsigned int size ); +#endif diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/x86_insn.c b/shared/sentry/external/breakpad/src/third_party/libdisasm/x86_insn.c new file mode 100644 index 000000000..5649b89fb --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/x86_insn.c @@ -0,0 +1,182 @@ +#include +#include + +#include "libdis.h" + +#ifdef _MSC_VER + #define snprintf _snprintf + #define inline __inline +#endif + +int x86_insn_is_valid( x86_insn_t *insn ) { + if ( insn && insn->type != insn_invalid && insn->size > 0 ) { + return 1; + } + + return 0; +} + +uint32_t x86_get_address( x86_insn_t *insn ) { + x86_oplist_t *op_lst; + if (! insn || ! insn->operands ) { + return 0; + } + + for (op_lst = insn->operands; op_lst; op_lst = op_lst->next ) { + if ( op_lst->op.type == op_offset ) { + return op_lst->op.data.offset; + } else if ( op_lst->op.type == op_absolute ) { + if ( op_lst->op.datatype == op_descr16 ) { + return (uint32_t) + op_lst->op.data.absolute.offset.off16; + } + return op_lst->op.data.absolute.offset.off32; + } + } + + return 0; +} + +int32_t x86_get_rel_offset( x86_insn_t *insn ) { + x86_oplist_t *op_lst; + if (! insn || ! insn->operands ) { + return 0; + } + + for (op_lst = insn->operands; op_lst; op_lst = op_lst->next ) { + if ( op_lst->op.type == op_relative_near ) { + return (int32_t) op_lst->op.data.relative_near; + } else if ( op_lst->op.type == op_relative_far ) { + return op_lst->op.data.relative_far; + } + } + + return 0; +} + +x86_op_t * x86_get_branch_target( x86_insn_t *insn ) { + x86_oplist_t *op_lst; + if (! insn || ! insn->operands ) { + return NULL; + } + + for (op_lst = insn->operands; op_lst; op_lst = op_lst->next ) { + if ( op_lst->op.access & op_execute ) { + return &(op_lst->op); + } + } + + return NULL; +} +x86_op_t * x86_get_imm( x86_insn_t *insn ) { + x86_oplist_t *op_lst; + if (! insn || ! insn->operands ) { + return NULL; + } + + for (op_lst = insn->operands; op_lst; op_lst = op_lst->next ) { + if ( op_lst->op.type == op_immediate ) { + return &(op_lst->op); + } + } + + return NULL; +} + +#define IS_PROPER_IMM( x ) \ + x->op.type == op_immediate && ! (x->op.flags & op_hardcode) + + +/* if there is an immediate value in the instruction, return a pointer to + * it */ +unsigned char * x86_get_raw_imm( x86_insn_t *insn ) { + int size, offset; + x86_op_t *op = NULL; + + if (! insn || ! insn->operands ) { + return(NULL); + } + + /* a bit inelegant, but oh well... */ + if ( IS_PROPER_IMM( insn->operands ) ) { + op = &insn->operands->op; + } else if ( insn->operands->next ) { + if ( IS_PROPER_IMM( insn->operands->next ) ) { + op = &insn->operands->next->op; + } else if ( insn->operands->next->next && + IS_PROPER_IMM( insn->operands->next->next ) ) { + op = &insn->operands->next->next->op; + } + } + + if (! op ) { + return( NULL ); + } + + /* immediate data is at the end of the insn */ + size = x86_operand_size( op ); + offset = insn->size - size; + return( &insn->bytes[offset] ); +} + + +unsigned int x86_operand_size( x86_op_t *op ) { + switch (op->datatype ) { + case op_byte: return 1; + case op_word: return 2; + case op_dword: return 4; + case op_qword: return 8; + case op_dqword: return 16; + case op_sreal: return 4; + case op_dreal: return 8; + case op_extreal: return 10; + case op_bcd: return 10; + case op_ssimd: return 16; + case op_dsimd: return 16; + case op_sssimd: return 4; + case op_sdsimd: return 8; + case op_descr32: return 6; + case op_descr16: return 4; + case op_pdescr32: return 6; + case op_pdescr16: return 6; + case op_bounds16: return 4; + case op_bounds32: return 8; + case op_fpuenv16: return 14; + case op_fpuenv32: return 28; + case op_fpustate16: return 94; + case op_fpustate32: return 108; + case op_fpregset: return 512; + case op_fpreg: return 10; + case op_none: return 0; + } + return(4); /* default size */ +} + +void x86_set_insn_addr( x86_insn_t *insn, uint32_t addr ) { + if ( insn ) insn->addr = addr; +} + +void x86_set_insn_offset( x86_insn_t *insn, unsigned int offset ){ + if ( insn ) insn->offset = offset; +} + +void x86_set_insn_function( x86_insn_t *insn, void * func ){ + if ( insn ) insn->function = func; +} + +void x86_set_insn_block( x86_insn_t *insn, void * block ){ + if ( insn ) insn->block = block; +} + +void x86_tag_insn( x86_insn_t *insn ){ + if ( insn ) insn->tag = 1; +} + +void x86_untag_insn( x86_insn_t *insn ){ + if ( insn ) insn->tag = 0; +} + +int x86_insn_is_tagged( x86_insn_t *insn ){ + return insn->tag; +} + diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/x86_misc.c b/shared/sentry/external/breakpad/src/third_party/libdisasm/x86_misc.c new file mode 100644 index 000000000..3d2dd0ae8 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/x86_misc.c @@ -0,0 +1,71 @@ +#include +#include +#include + +#include "libdis.h" +#include "ia32_insn.h" +#include "ia32_reg.h" /* for ia32_reg wrapper */ +#include "ia32_settings.h" +extern ia32_settings_t ia32_settings; + +#ifdef _MSC_VER + #define snprintf _snprintf + #define inline __inline +#endif + + +/* =========================================================== INIT/TERM */ +static DISASM_REPORTER __x86_reporter_func = NULL; +static void * __x86_reporter_arg = NULL; + +int x86_init( enum x86_options options, DISASM_REPORTER reporter, void * arg ) +{ + ia32_settings.options = options; + __x86_reporter_func = reporter; + __x86_reporter_arg = arg; + + return 1; +} + +void x86_set_reporter( DISASM_REPORTER reporter, void * arg ) { + __x86_reporter_func = reporter; + __x86_reporter_arg = arg; +} + +void x86_set_options( enum x86_options options ){ + ia32_settings.options = options; +} + +enum x86_options x86_get_options( void ) { + return ia32_settings.options; +} + +int x86_cleanup( void ) +{ + return 1; +} + +/* =========================================================== ERRORS */ +void x86_report_error( enum x86_report_codes code, void *data ) { + if ( __x86_reporter_func ) { + (*__x86_reporter_func)(code, data, __x86_reporter_arg); + } +} + + +/* =========================================================== MISC */ +unsigned int x86_endian(void) { return ia32_settings.endian; } +unsigned int x86_addr_size(void) { return ia32_settings.sz_addr; } +unsigned int x86_op_size(void) { return ia32_settings.sz_oper; } +unsigned int x86_word_size(void) { return ia32_settings.sz_word; } +unsigned int x86_max_insn_size(void) { return ia32_settings.max_insn; } +unsigned int x86_sp_reg(void) { return ia32_settings.id_sp_reg; } +unsigned int x86_fp_reg(void) { return ia32_settings.id_fp_reg; } +unsigned int x86_ip_reg(void) { return ia32_settings.id_ip_reg; } +unsigned int x86_flag_reg(void) { return ia32_settings.id_flag_reg; } + +/* wrapper function to hide the IA32 register fn */ +void x86_reg_from_id( unsigned int id, x86_reg_t * reg ) { + ia32_handle_register( reg, id ); + return; +} diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/x86_operand_list.c b/shared/sentry/external/breakpad/src/third_party/libdisasm/x86_operand_list.c new file mode 100644 index 000000000..95409e069 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/x86_operand_list.c @@ -0,0 +1,191 @@ +#include +#include "libdis.h" + + +static void x86_oplist_append( x86_insn_t *insn, x86_oplist_t *op ) { + x86_oplist_t *list; + + if (! insn ) { + return; + } + + list = insn->operands; + if (! list ) { + insn->operand_count = 1; + /* Note that we have no way of knowing if this is an + * exlicit operand or not, since the caller fills + * the x86_op_t after we return. We increase the + * explicit count automatically, and ia32_insn_implicit_ops + * decrements it */ + insn->explicit_count = 1; + insn->operands = op; + return; + } + + /* get to end of list */ + for ( ; list->next; list = list->next ) + ; + + insn->operand_count = insn->operand_count + 1; + insn->explicit_count = insn->explicit_count + 1; + list->next = op; + + return; +} + +x86_op_t * x86_operand_new( x86_insn_t *insn ) { + x86_oplist_t *op; + + if (! insn ) { + return(NULL); + } + op = calloc( sizeof(x86_oplist_t), 1 ); + op->op.insn = insn; + x86_oplist_append( insn, op ); + return( &(op->op) ); +} + +void x86_oplist_free( x86_insn_t *insn ) { + x86_oplist_t *op, *list; + + if (! insn ) { + return; + } + + for ( list = insn->operands; list; ) { + op = list; + list = list->next; + free(op); + } + + insn->operands = NULL; + insn->operand_count = 0; + insn->explicit_count = 0; + + return; +} + +/* ================================================== LIBDISASM API */ +/* these could probably just be #defines, but that means exposing the + enum... yet one more confusing thing in the API */ +int x86_operand_foreach( x86_insn_t *insn, x86_operand_fn func, void *arg, + enum x86_op_foreach_type type ){ + x86_oplist_t *list; + char explicit = 1, implicit = 1; + + if (! insn || ! func ) { + return 0; + } + + /* note: explicit and implicit can be ORed together to + * allow an "all" limited by access type, even though the + * user is stupid to do this since it is default behavior :) */ + if ( (type & op_explicit) && ! (type & op_implicit) ) { + implicit = 0; + } + if ( (type & op_implicit) && ! (type & op_explicit) ) { + explicit = 0; + } + + type = type & 0x0F; /* mask out explicit/implicit operands */ + + for ( list = insn->operands; list; list = list->next ) { + if (! implicit && (list->op.flags & op_implied) ) { + /* operand is implicit */ + continue; + } + + if (! explicit && ! (list->op.flags & op_implied) ) { + /* operand is not implicit */ + continue; + } + + switch ( type ) { + case op_any: + break; + case op_dest: + if (! (list->op.access & op_write) ) { + continue; + } + break; + case op_src: + if (! (list->op.access & op_read) ) { + continue; + } + break; + case op_ro: + if (! (list->op.access & op_read) || + (list->op.access & op_write ) ) { + continue; + } + break; + case op_wo: + if (! (list->op.access & op_write) || + (list->op.access & op_read ) ) { + continue; + } + break; + case op_xo: + if (! (list->op.access & op_execute) ) { + continue; + } + break; + case op_rw: + if (! (list->op.access & op_write) || + ! (list->op.access & op_read ) ) { + continue; + } + break; + case op_implicit: case op_explicit: /* make gcc happy */ + break; + } + /* any non-continue ends up here: invoke the callback */ + (*func)( &list->op, insn, arg ); + } + + return 1; +} + +static void count_operand( x86_op_t *op, x86_insn_t *insn, void *arg ) { + size_t * count = (size_t *) arg; + *count = *count + 1; +} + +size_t x86_operand_count( x86_insn_t *insn, enum x86_op_foreach_type type ) { + size_t count = 0; + + /* save us a list traversal for common counts... */ + if ( type == op_any ) { + return insn->operand_count; + } else if ( type == op_explicit ) { + return insn->explicit_count; + } + + x86_operand_foreach( insn, count_operand, &count, type ); + return count; +} + +/* accessor functions */ +x86_op_t * x86_operand_1st( x86_insn_t *insn ) { + if (! insn->explicit_count ) { + return NULL; + } + + return &(insn->operands->op); +} + +x86_op_t * x86_operand_2nd( x86_insn_t *insn ) { + if ( insn->explicit_count < 2 ) { + return NULL; + } + + return &(insn->operands->next->op); +} + +x86_op_t * x86_operand_3rd( x86_insn_t *insn ) { + if ( insn->explicit_count < 3 ) { + return NULL; + } + + return &(insn->operands->next->next->op); +} diff --git a/shared/sentry/external/breakpad/src/third_party/libdisasm/x86_operand_list.h b/shared/sentry/external/breakpad/src/third_party/libdisasm/x86_operand_list.h new file mode 100644 index 000000000..53668658e --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/libdisasm/x86_operand_list.h @@ -0,0 +1,8 @@ +#ifndef X86_OPERAND_LIST_H +#define X86_OPERAND_LIST_H +#include "libdis.h" + + +x86_op_t * x86_operand_new( x86_insn_t *insn ); + +#endif diff --git a/shared/sentry/external/breakpad/src/third_party/linux/include/gflags/gflags_completions.h b/shared/sentry/external/breakpad/src/third_party/linux/include/gflags/gflags_completions.h new file mode 100644 index 000000000..9d9ce7a5f --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/linux/include/gflags/gflags_completions.h @@ -0,0 +1,121 @@ +// 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. +// +// --- +// Author: Dave Nicponski +// +// Implement helpful bash-style command line flag completions +// +// ** Functional API: +// HandleCommandLineCompletions() should be called early during +// program startup, but after command line flag code has been +// initialized, such as the beginning of HandleCommandLineHelpFlags(). +// It checks the value of the flag --tab_completion_word. If this +// flag is empty, nothing happens here. If it contains a string, +// however, then HandleCommandLineCompletions() will hijack the +// process, attempting to identify the intention behind this +// completion. Regardless of the outcome of this deduction, the +// process will be terminated, similar to --helpshort flag +// handling. +// +// ** Overview of Bash completions: +// Bash can be told to programatically determine completions for the +// current 'cursor word'. It does this by (in this case) invoking a +// command with some additional arguments identifying the command +// being executed, the word being completed, and the previous word +// (if any). Bash then expects a sequence of output lines to be +// printed to stdout. If these lines all contain a common prefix +// longer than the cursor word, bash will replace the cursor word +// with that common prefix, and display nothing. If there isn't such +// a common prefix, bash will display the lines in pages using 'more'. +// +// ** Strategy taken for command line completions: +// If we can deduce either the exact flag intended, or a common flag +// prefix, we'll output exactly that. Otherwise, if information +// must be displayed to the user, we'll take the opportunity to add +// some helpful information beyond just the flag name (specifically, +// we'll include the default flag value and as much of the flag's +// description as can fit on a single terminal line width, as specified +// by the flag --tab_completion_columns). Furthermore, we'll try to +// make bash order the output such that the most useful or relevent +// flags are the most likely to be shown at the top. +// +// ** Additional features: +// To assist in finding that one really useful flag, substring matching +// was implemented. Before pressing a to get completion for the +// current word, you can append one or more '?' to the flag to do +// substring matching. Here's the semantics: +// --foo Show me all flags with names prefixed by 'foo' +// --foo? Show me all flags with 'foo' somewhere in the name +// --foo?? Same as prior case, but also search in module +// definition path for 'foo' +// --foo??? Same as prior case, but also search in flag +// descriptions for 'foo' +// Finally, we'll trim the output to a relatively small number of +// flags to keep bash quiet about the verbosity of output. If one +// really wanted to see all possible matches, appending a '+' to the +// search word will force the exhaustive list of matches to be printed. +// +// ** How to have bash accept completions from a binary: +// Bash requires that it be informed about each command that programmatic +// completion should be enabled for. Example addition to a .bashrc +// file would be (your path to gflags_completions.sh file may differ): + +/* +$ complete -o bashdefault -o default -o nospace -C \ + '/usr/local/bin/gflags_completions.sh --tab_completion_columns $COLUMNS' \ + time env binary_name another_binary [...] +*/ + +// This would allow the following to work: +// $ /path/to/binary_name --vmodule +// Or: +// $ ./bin/path/another_binary --gfs_u +// (etc) +// +// Sadly, it appears that bash gives no easy way to force this behavior for +// all commands. That's where the "time" in the above example comes in. +// If you haven't specifically added a command to the list of completion +// supported commands, you can still get completions by prefixing the +// entire command with "env". +// $ env /some/brand/new/binary --vmod +// Assuming that "binary" is a newly compiled binary, this should still +// produce the expected completion output. + + +#ifndef GOOGLE_GFLAGS_COMPLETIONS_H_ +#define GOOGLE_GFLAGS_COMPLETIONS_H_ + +namespace google { + +void HandleCommandLineCompletions(void); + +} + +#endif // GOOGLE_GFLAGS_COMPLETIONS_H_ diff --git a/shared/sentry/external/breakpad/src/third_party/mac_headers/README b/shared/sentry/external/breakpad/src/third_party/mac_headers/README new file mode 100644 index 000000000..c681bb3d6 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/mac_headers/README @@ -0,0 +1,2 @@ +These headers were copied from the Mac OS X 10.7 SDK to enable building +the Mac dump_syms code that processes Mach-O files on Linux. diff --git a/shared/sentry/external/breakpad/src/third_party/mac_headers/architecture/byte_order.h b/shared/sentry/external/breakpad/src/third_party/mac_headers/architecture/byte_order.h new file mode 100644 index 000000000..b772d9f38 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/mac_headers/architecture/byte_order.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 1999-2008 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +/* + * Copyright (c) 1992 NeXT Computer, Inc. + * + * Byte ordering conversion. + * + */ +/* This file mostly left blank */ + +#ifndef _ARCHITECTURE_BYTE_ORDER_H_ +#define _ARCHITECTURE_BYTE_ORDER_H_ + +/* + * Identify the byte order + * of the current host. + */ + +enum NXByteOrder { + NX_UnknownByteOrder, + NX_LittleEndian, + NX_BigEndian +}; + +#endif /* _ARCHITECTURE_BYTE_ORDER_H_ */ diff --git a/shared/sentry/external/breakpad/src/third_party/mac_headers/i386/_types.h b/shared/sentry/external/breakpad/src/third_party/mac_headers/i386/_types.h new file mode 100644 index 000000000..2ed7fd675 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/mac_headers/i386/_types.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2000-2003 Apple Computer, Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +#ifndef _BSD_I386__TYPES_H_ +#define _BSD_I386__TYPES_H_ + +typedef long __darwin_intptr_t; +typedef unsigned int __darwin_natural_t; + +#endif /* _BSD_I386__TYPES_H_ */ diff --git a/shared/sentry/external/breakpad/src/third_party/mac_headers/mach-o/arch.h b/shared/sentry/external/breakpad/src/third_party/mac_headers/mach-o/arch.h new file mode 100644 index 000000000..526c10fc8 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/mac_headers/mach-o/arch.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#ifndef _MACH_O_ARCH_H_ +#define _MACH_O_ARCH_H_ +/* + * Copyright (c) 1997 Apple Computer, Inc. + * + * Functions that deal with information about architectures. + * + */ + +#include +#include +#include + +/* The NXArchInfo structs contain the architectures symbolic name + * (such as "ppc"), its CPU type and CPU subtype as defined in + * mach/machine.h, the byte order for the architecture, and a + * describing string (such as "PowerPC"). + * There will both be entries for specific CPUs (such as ppc604e) as + * well as generic "family" entries (such as ppc). + */ +typedef struct { + const char *name; + cpu_type_t cputype; + cpu_subtype_t cpusubtype; + enum NXByteOrder byteorder; + const char *description; +} NXArchInfo; + +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* NXGetAllArchInfos() returns a pointer to an array of all known + * NXArchInfo structures. The last NXArchInfo is marked by a NULL name. + */ +extern const NXArchInfo *NXGetAllArchInfos(void); + +/* NXGetLocalArchInfo() returns the NXArchInfo for the local host, or NULL + * if none is known. + */ +extern const NXArchInfo *NXGetLocalArchInfo(void); + +/* NXGetArchInfoFromName() and NXGetArchInfoFromCpuType() return the + * NXArchInfo from the architecture's name or cputype/cpusubtype + * combination. A cpusubtype of CPU_SUBTYPE_MULTIPLE can be used + * to request the most general NXArchInfo known for the given cputype. + * NULL is returned if no matching NXArchInfo can be found. + */ +extern const NXArchInfo *NXGetArchInfoFromName(const char *name); +extern const NXArchInfo *NXGetArchInfoFromCpuType(cpu_type_t cputype, + cpu_subtype_t cpusubtype); + +/* NXFindBestFatArch() is passed a cputype and cpusubtype and a set of + * fat_arch structs and selects the best one that matches (if any) and returns + * a pointer to that fat_arch struct (or NULL). The fat_arch structs must be + * in the host byte order and correct such that the fat_archs really points to + * enough memory for nfat_arch structs. It is possible that this routine could + * fail if new cputypes or cpusubtypes are added and an old version of this + * routine is used. But if there is an exact match between the cputype and + * cpusubtype and one of the fat_arch structs this routine will always succeed. + */ +extern struct fat_arch *NXFindBestFatArch(cpu_type_t cputype, + cpu_subtype_t cpusubtype, + struct fat_arch *fat_archs, + uint32_t nfat_archs); + +/* NXCombineCpuSubtypes() returns the resulting cpusubtype when combining two + * different cpusubtypes for the specified cputype. If the two cpusubtypes + * can't be combined (the specific subtypes are mutually exclusive) -1 is + * returned indicating it is an error to combine them. This can also fail and + * return -1 if new cputypes or cpusubtypes are added and an old version of + * this routine is used. But if the cpusubtypes are the same they can always + * be combined and this routine will return the cpusubtype pass in. + */ +extern cpu_subtype_t NXCombineCpuSubtypes(cpu_type_t cputype, + cpu_subtype_t cpusubtype1, + cpu_subtype_t cpusubtype2); + +#if __cplusplus +} +#endif /* __cplusplus */ + +#endif /* _MACH_O_ARCH_H_ */ diff --git a/shared/sentry/external/breakpad/src/third_party/mac_headers/mach-o/fat.h b/shared/sentry/external/breakpad/src/third_party/mac_headers/mach-o/fat.h new file mode 100644 index 000000000..e2bcf433d --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/mac_headers/mach-o/fat.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#ifndef _MACH_O_FAT_H_ +#define _MACH_O_FAT_H_ +/* + * This header file describes the structures of the file format for "fat" + * architecture specific file (wrapper design). At the begining of the file + * there is one fat_header structure followed by a number of fat_arch + * structures. For each architecture in the file, specified by a pair of + * cputype and cpusubtype, the fat_header describes the file offset, file + * size and alignment in the file of the architecture specific member. + * The padded bytes in the file to place each member on it's specific alignment + * are defined to be read as zeros and can be left as "holes" if the file system + * can support them as long as they read as zeros. + * + * All structures defined here are always written and read to/from disk + * in big-endian order. + */ + +/* + * is needed here for the cpu_type_t and cpu_subtype_t types + * and contains the constants for the possible values of these types. + */ +#include +#include +#include + +#define FAT_MAGIC 0xcafebabe +#define FAT_CIGAM 0xbebafeca /* NXSwapLong(FAT_MAGIC) */ + +struct fat_header { + uint32_t magic; /* FAT_MAGIC */ + uint32_t nfat_arch; /* number of structs that follow */ +}; + +struct fat_arch { + cpu_type_t cputype; /* cpu specifier (int) */ + cpu_subtype_t cpusubtype; /* machine specifier (int) */ + uint32_t offset; /* file offset to this object file */ + uint32_t size; /* size of this object file */ + uint32_t align; /* alignment as a power of 2 */ +}; + +#endif /* _MACH_O_FAT_H_ */ diff --git a/shared/sentry/external/breakpad/src/third_party/mac_headers/mach-o/loader.h b/shared/sentry/external/breakpad/src/third_party/mac_headers/mach-o/loader.h new file mode 100644 index 000000000..ff18e29c7 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/mac_headers/mach-o/loader.h @@ -0,0 +1,1402 @@ +/* + * Copyright (c) 1999-2010 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#ifndef _MACHO_LOADER_H_ +#define _MACHO_LOADER_H_ + +/* + * This file describes the format of mach object files. + */ +#include + +/* + * is needed here for the cpu_type_t and cpu_subtype_t types + * and contains the constants for the possible values of these types. + */ +#include + +/* + * is needed here for the vm_prot_t type and contains the + * constants that are or'ed together for the possible values of this type. + */ +#include + +/* + * is expected to define the flavors of the thread + * states and the structures of those flavors for each machine. + */ +#include +#include + +/* + * The 32-bit mach header appears at the very beginning of the object file for + * 32-bit architectures. + */ +struct mach_header { + uint32_t magic; /* mach magic number identifier */ + cpu_type_t cputype; /* cpu specifier */ + cpu_subtype_t cpusubtype; /* machine specifier */ + uint32_t filetype; /* type of file */ + uint32_t ncmds; /* number of load commands */ + uint32_t sizeofcmds; /* the size of all the load commands */ + uint32_t flags; /* flags */ +}; + +/* Constant for the magic field of the mach_header (32-bit architectures) */ +#define MH_MAGIC 0xfeedface /* the mach magic number */ +#define MH_CIGAM 0xcefaedfe /* NXSwapInt(MH_MAGIC) */ + +/* + * The 64-bit mach header appears at the very beginning of object files for + * 64-bit architectures. + */ +struct mach_header_64 { + uint32_t magic; /* mach magic number identifier */ + cpu_type_t cputype; /* cpu specifier */ + cpu_subtype_t cpusubtype; /* machine specifier */ + uint32_t filetype; /* type of file */ + uint32_t ncmds; /* number of load commands */ + uint32_t sizeofcmds; /* the size of all the load commands */ + uint32_t flags; /* flags */ + uint32_t reserved; /* reserved */ +}; + +/* Constant for the magic field of the mach_header_64 (64-bit architectures) */ +#define MH_MAGIC_64 0xfeedfacf /* the 64-bit mach magic number */ +#define MH_CIGAM_64 0xcffaedfe /* NXSwapInt(MH_MAGIC_64) */ + +/* + * The layout of the file depends on the filetype. For all but the MH_OBJECT + * file type the segments are padded out and aligned on a segment alignment + * boundary for efficient demand pageing. The MH_EXECUTE, MH_FVMLIB, MH_DYLIB, + * MH_DYLINKER and MH_BUNDLE file types also have the headers included as part + * of their first segment. + * + * The file type MH_OBJECT is a compact format intended as output of the + * assembler and input (and possibly output) of the link editor (the .o + * format). All sections are in one unnamed segment with no segment padding. + * This format is used as an executable format when the file is so small the + * segment padding greatly increases its size. + * + * The file type MH_PRELOAD is an executable format intended for things that + * are not executed under the kernel (proms, stand alones, kernels, etc). The + * format can be executed under the kernel but may demand paged it and not + * preload it before execution. + * + * A core file is in MH_CORE format and can be any in an arbritray legal + * Mach-O file. + * + * Constants for the filetype field of the mach_header + */ +#define MH_OBJECT 0x1 /* relocatable object file */ +#define MH_EXECUTE 0x2 /* demand paged executable file */ +#define MH_FVMLIB 0x3 /* fixed VM shared library file */ +#define MH_CORE 0x4 /* core file */ +#define MH_PRELOAD 0x5 /* preloaded executable file */ +#define MH_DYLIB 0x6 /* dynamically bound shared library */ +#define MH_DYLINKER 0x7 /* dynamic link editor */ +#define MH_BUNDLE 0x8 /* dynamically bound bundle file */ +#define MH_DYLIB_STUB 0x9 /* shared library stub for static */ + /* linking only, no section contents */ +#define MH_DSYM 0xa /* companion file with only debug */ + /* sections */ +#define MH_KEXT_BUNDLE 0xb /* x86_64 kexts */ + +/* Constants for the flags field of the mach_header */ +#define MH_NOUNDEFS 0x1 /* the object file has no undefined + references */ +#define MH_INCRLINK 0x2 /* the object file is the output of an + incremental link against a base file + and can't be link edited again */ +#define MH_DYLDLINK 0x4 /* the object file is input for the + dynamic linker and can't be staticly + link edited again */ +#define MH_BINDATLOAD 0x8 /* the object file's undefined + references are bound by the dynamic + linker when loaded. */ +#define MH_PREBOUND 0x10 /* the file has its dynamic undefined + references prebound. */ +#define MH_SPLIT_SEGS 0x20 /* the file has its read-only and + read-write segments split */ +#define MH_LAZY_INIT 0x40 /* the shared library init routine is + to be run lazily via catching memory + faults to its writeable segments + (obsolete) */ +#define MH_TWOLEVEL 0x80 /* the image is using two-level name + space bindings */ +#define MH_FORCE_FLAT 0x100 /* the executable is forcing all images + to use flat name space bindings */ +#define MH_NOMULTIDEFS 0x200 /* this umbrella guarantees no multiple + defintions of symbols in its + sub-images so the two-level namespace + hints can always be used. */ +#define MH_NOFIXPREBINDING 0x400 /* do not have dyld notify the + prebinding agent about this + executable */ +#define MH_PREBINDABLE 0x800 /* the binary is not prebound but can + have its prebinding redone. only used + when MH_PREBOUND is not set. */ +#define MH_ALLMODSBOUND 0x1000 /* indicates that this binary binds to + all two-level namespace modules of + its dependent libraries. only used + when MH_PREBINDABLE and MH_TWOLEVEL + are both set. */ +#define MH_SUBSECTIONS_VIA_SYMBOLS 0x2000/* safe to divide up the sections into + sub-sections via symbols for dead + code stripping */ +#define MH_CANONICAL 0x4000 /* the binary has been canonicalized + via the unprebind operation */ +#define MH_WEAK_DEFINES 0x8000 /* the final linked image contains + external weak symbols */ +#define MH_BINDS_TO_WEAK 0x10000 /* the final linked image uses + weak symbols */ + +#define MH_ALLOW_STACK_EXECUTION 0x20000/* When this bit is set, all stacks + in the task will be given stack + execution privilege. Only used in + MH_EXECUTE filetypes. */ +#define MH_ROOT_SAFE 0x40000 /* When this bit is set, the binary + declares it is safe for use in + processes with uid zero */ + +#define MH_SETUID_SAFE 0x80000 /* When this bit is set, the binary + declares it is safe for use in + processes when issetugid() is true */ + +#define MH_NO_REEXPORTED_DYLIBS 0x100000 /* When this bit is set on a dylib, + the static linker does not need to + examine dependent dylibs to see + if any are re-exported */ +#define MH_PIE 0x200000 /* When this bit is set, the OS will + load the main executable at a + random address. Only used in + MH_EXECUTE filetypes. */ +#define MH_DEAD_STRIPPABLE_DYLIB 0x400000 /* Only for use on dylibs. When + linking against a dylib that + has this bit set, the static linker + will automatically not create a + LC_LOAD_DYLIB load command to the + dylib if no symbols are being + referenced from the dylib. */ +#define MH_HAS_TLV_DESCRIPTORS 0x800000 /* Contains a section of type + S_THREAD_LOCAL_VARIABLES */ + +#define MH_NO_HEAP_EXECUTION 0x1000000 /* When this bit is set, the OS will + run the main executable with + a non-executable heap even on + platforms (e.g. i386) that don't + require it. Only used in MH_EXECUTE + filetypes. */ + +/* + * The load commands directly follow the mach_header. The total size of all + * of the commands is given by the sizeofcmds field in the mach_header. All + * load commands must have as their first two fields cmd and cmdsize. The cmd + * field is filled in with a constant for that command type. Each command type + * has a structure specifically for it. The cmdsize field is the size in bytes + * of the particular load command structure plus anything that follows it that + * is a part of the load command (i.e. section structures, strings, etc.). To + * advance to the next load command the cmdsize can be added to the offset or + * pointer of the current load command. The cmdsize for 32-bit architectures + * MUST be a multiple of 4 bytes and for 64-bit architectures MUST be a multiple + * of 8 bytes (these are forever the maximum alignment of any load commands). + * The padded bytes must be zero. All tables in the object file must also + * follow these rules so the file can be memory mapped. Otherwise the pointers + * to these tables will not work well or at all on some machines. With all + * padding zeroed like objects will compare byte for byte. + */ +struct load_command { + uint32_t cmd; /* type of load command */ + uint32_t cmdsize; /* total size of command in bytes */ +}; + +/* + * After MacOS X 10.1 when a new load command is added that is required to be + * understood by the dynamic linker for the image to execute properly the + * LC_REQ_DYLD bit will be or'ed into the load command constant. If the dynamic + * linker sees such a load command it it does not understand will issue a + * "unknown load command required for execution" error and refuse to use the + * image. Other load commands without this bit that are not understood will + * simply be ignored. + */ +#define LC_REQ_DYLD 0x80000000 + +/* Constants for the cmd field of all load commands, the type */ +#define LC_SEGMENT 0x1 /* segment of this file to be mapped */ +#define LC_SYMTAB 0x2 /* link-edit stab symbol table info */ +#define LC_SYMSEG 0x3 /* link-edit gdb symbol table info (obsolete) */ +#define LC_THREAD 0x4 /* thread */ +#define LC_UNIXTHREAD 0x5 /* unix thread (includes a stack) */ +#define LC_LOADFVMLIB 0x6 /* load a specified fixed VM shared library */ +#define LC_IDFVMLIB 0x7 /* fixed VM shared library identification */ +#define LC_IDENT 0x8 /* object identification info (obsolete) */ +#define LC_FVMFILE 0x9 /* fixed VM file inclusion (internal use) */ +#define LC_PREPAGE 0xa /* prepage command (internal use) */ +#define LC_DYSYMTAB 0xb /* dynamic link-edit symbol table info */ +#define LC_LOAD_DYLIB 0xc /* load a dynamically linked shared library */ +#define LC_ID_DYLIB 0xd /* dynamically linked shared lib ident */ +#define LC_LOAD_DYLINKER 0xe /* load a dynamic linker */ +#define LC_ID_DYLINKER 0xf /* dynamic linker identification */ +#define LC_PREBOUND_DYLIB 0x10 /* modules prebound for a dynamically */ + /* linked shared library */ +#define LC_ROUTINES 0x11 /* image routines */ +#define LC_SUB_FRAMEWORK 0x12 /* sub framework */ +#define LC_SUB_UMBRELLA 0x13 /* sub umbrella */ +#define LC_SUB_CLIENT 0x14 /* sub client */ +#define LC_SUB_LIBRARY 0x15 /* sub library */ +#define LC_TWOLEVEL_HINTS 0x16 /* two-level namespace lookup hints */ +#define LC_PREBIND_CKSUM 0x17 /* prebind checksum */ + +/* + * load a dynamically linked shared library that is allowed to be missing + * (all symbols are weak imported). + */ +#define LC_LOAD_WEAK_DYLIB (0x18 | LC_REQ_DYLD) + +#define LC_SEGMENT_64 0x19 /* 64-bit segment of this file to be + mapped */ +#define LC_ROUTINES_64 0x1a /* 64-bit image routines */ +#define LC_UUID 0x1b /* the uuid */ +#define LC_RPATH (0x1c | LC_REQ_DYLD) /* runpath additions */ +#define LC_CODE_SIGNATURE 0x1d /* local of code signature */ +#define LC_SEGMENT_SPLIT_INFO 0x1e /* local of info to split segments */ +#define LC_REEXPORT_DYLIB (0x1f | LC_REQ_DYLD) /* load and re-export dylib */ +#define LC_LAZY_LOAD_DYLIB 0x20 /* delay load of dylib until first use */ +#define LC_ENCRYPTION_INFO 0x21 /* encrypted segment information */ +#define LC_DYLD_INFO 0x22 /* compressed dyld information */ +#define LC_DYLD_INFO_ONLY (0x22|LC_REQ_DYLD) /* compressed dyld information only */ +#define LC_LOAD_UPWARD_DYLIB (0x23 | LC_REQ_DYLD) /* load upward dylib */ +#define LC_VERSION_MIN_MACOSX 0x24 /* build for MacOSX min OS version */ +#define LC_VERSION_MIN_IPHONEOS 0x25 /* build for iPhoneOS min OS version */ +#define LC_FUNCTION_STARTS 0x26 /* compressed table of function start addresses */ +#define LC_DYLD_ENVIRONMENT 0x27 /* string for dyld to treat + like environment variable */ + +/* + * A variable length string in a load command is represented by an lc_str + * union. The strings are stored just after the load command structure and + * the offset is from the start of the load command structure. The size + * of the string is reflected in the cmdsize field of the load command. + * Once again any padded bytes to bring the cmdsize field to a multiple + * of 4 bytes must be zero. + */ +union lc_str { + uint32_t offset; /* offset to the string */ +#ifndef __LP64__ + char *ptr; /* pointer to the string */ +#endif +}; + +/* + * The segment load command indicates that a part of this file is to be + * mapped into the task's address space. The size of this segment in memory, + * vmsize, maybe equal to or larger than the amount to map from this file, + * filesize. The file is mapped starting at fileoff to the beginning of + * the segment in memory, vmaddr. The rest of the memory of the segment, + * if any, is allocated zero fill on demand. The segment's maximum virtual + * memory protection and initial virtual memory protection are specified + * by the maxprot and initprot fields. If the segment has sections then the + * section structures directly follow the segment command and their size is + * reflected in cmdsize. + */ +struct segment_command { /* for 32-bit architectures */ + uint32_t cmd; /* LC_SEGMENT */ + uint32_t cmdsize; /* includes sizeof section structs */ + char segname[16]; /* segment name */ + uint32_t vmaddr; /* memory address of this segment */ + uint32_t vmsize; /* memory size of this segment */ + uint32_t fileoff; /* file offset of this segment */ + uint32_t filesize; /* amount to map from the file */ + vm_prot_t maxprot; /* maximum VM protection */ + vm_prot_t initprot; /* initial VM protection */ + uint32_t nsects; /* number of sections in segment */ + uint32_t flags; /* flags */ +}; + +/* + * The 64-bit segment load command indicates that a part of this file is to be + * mapped into a 64-bit task's address space. If the 64-bit segment has + * sections then section_64 structures directly follow the 64-bit segment + * command and their size is reflected in cmdsize. + */ +struct segment_command_64 { /* for 64-bit architectures */ + uint32_t cmd; /* LC_SEGMENT_64 */ + uint32_t cmdsize; /* includes sizeof section_64 structs */ + char segname[16]; /* segment name */ + uint64_t vmaddr; /* memory address of this segment */ + uint64_t vmsize; /* memory size of this segment */ + uint64_t fileoff; /* file offset of this segment */ + uint64_t filesize; /* amount to map from the file */ + vm_prot_t maxprot; /* maximum VM protection */ + vm_prot_t initprot; /* initial VM protection */ + uint32_t nsects; /* number of sections in segment */ + uint32_t flags; /* flags */ +}; + +/* Constants for the flags field of the segment_command */ +#define SG_HIGHVM 0x1 /* the file contents for this segment is for + the high part of the VM space, the low part + is zero filled (for stacks in core files) */ +#define SG_FVMLIB 0x2 /* this segment is the VM that is allocated by + a fixed VM library, for overlap checking in + the link editor */ +#define SG_NORELOC 0x4 /* this segment has nothing that was relocated + in it and nothing relocated to it, that is + it maybe safely replaced without relocation*/ +#define SG_PROTECTED_VERSION_1 0x8 /* This segment is protected. If the + segment starts at file offset 0, the + first page of the segment is not + protected. All other pages of the + segment are protected. */ + +/* + * A segment is made up of zero or more sections. Non-MH_OBJECT files have + * all of their segments with the proper sections in each, and padded to the + * specified segment alignment when produced by the link editor. The first + * segment of a MH_EXECUTE and MH_FVMLIB format file contains the mach_header + * and load commands of the object file before its first section. The zero + * fill sections are always last in their segment (in all formats). This + * allows the zeroed segment padding to be mapped into memory where zero fill + * sections might be. The gigabyte zero fill sections, those with the section + * type S_GB_ZEROFILL, can only be in a segment with sections of this type. + * These segments are then placed after all other segments. + * + * The MH_OBJECT format has all of its sections in one segment for + * compactness. There is no padding to a specified segment boundary and the + * mach_header and load commands are not part of the segment. + * + * Sections with the same section name, sectname, going into the same segment, + * segname, are combined by the link editor. The resulting section is aligned + * to the maximum alignment of the combined sections and is the new section's + * alignment. The combined sections are aligned to their original alignment in + * the combined section. Any padded bytes to get the specified alignment are + * zeroed. + * + * The format of the relocation entries referenced by the reloff and nreloc + * fields of the section structure for mach object files is described in the + * header file . + */ +struct section { /* for 32-bit architectures */ + char sectname[16]; /* name of this section */ + char segname[16]; /* segment this section goes in */ + uint32_t addr; /* memory address of this section */ + uint32_t size; /* size in bytes of this section */ + uint32_t offset; /* file offset of this section */ + uint32_t align; /* section alignment (power of 2) */ + uint32_t reloff; /* file offset of relocation entries */ + uint32_t nreloc; /* number of relocation entries */ + uint32_t flags; /* flags (section type and attributes)*/ + uint32_t reserved1; /* reserved (for offset or index) */ + uint32_t reserved2; /* reserved (for count or sizeof) */ +}; + +struct section_64 { /* for 64-bit architectures */ + char sectname[16]; /* name of this section */ + char segname[16]; /* segment this section goes in */ + uint64_t addr; /* memory address of this section */ + uint64_t size; /* size in bytes of this section */ + uint32_t offset; /* file offset of this section */ + uint32_t align; /* section alignment (power of 2) */ + uint32_t reloff; /* file offset of relocation entries */ + uint32_t nreloc; /* number of relocation entries */ + uint32_t flags; /* flags (section type and attributes)*/ + uint32_t reserved1; /* reserved (for offset or index) */ + uint32_t reserved2; /* reserved (for count or sizeof) */ + uint32_t reserved3; /* reserved */ +}; + +/* + * The flags field of a section structure is separated into two parts a section + * type and section attributes. The section types are mutually exclusive (it + * can only have one type) but the section attributes are not (it may have more + * than one attribute). + */ +#define SECTION_TYPE 0x000000ff /* 256 section types */ +#define SECTION_ATTRIBUTES 0xffffff00 /* 24 section attributes */ + +/* Constants for the type of a section */ +#define S_REGULAR 0x0 /* regular section */ +#define S_ZEROFILL 0x1 /* zero fill on demand section */ +#define S_CSTRING_LITERALS 0x2 /* section with only literal C strings*/ +#define S_4BYTE_LITERALS 0x3 /* section with only 4 byte literals */ +#define S_8BYTE_LITERALS 0x4 /* section with only 8 byte literals */ +#define S_LITERAL_POINTERS 0x5 /* section with only pointers to */ + /* literals */ +/* + * For the two types of symbol pointers sections and the symbol stubs section + * they have indirect symbol table entries. For each of the entries in the + * section the indirect symbol table entries, in corresponding order in the + * indirect symbol table, start at the index stored in the reserved1 field + * of the section structure. Since the indirect symbol table entries + * correspond to the entries in the section the number of indirect symbol table + * entries is inferred from the size of the section divided by the size of the + * entries in the section. For symbol pointers sections the size of the entries + * in the section is 4 bytes and for symbol stubs sections the byte size of the + * stubs is stored in the reserved2 field of the section structure. + */ +#define S_NON_LAZY_SYMBOL_POINTERS 0x6 /* section with only non-lazy + symbol pointers */ +#define S_LAZY_SYMBOL_POINTERS 0x7 /* section with only lazy symbol + pointers */ +#define S_SYMBOL_STUBS 0x8 /* section with only symbol + stubs, byte size of stub in + the reserved2 field */ +#define S_MOD_INIT_FUNC_POINTERS 0x9 /* section with only function + pointers for initialization*/ +#define S_MOD_TERM_FUNC_POINTERS 0xa /* section with only function + pointers for termination */ +#define S_COALESCED 0xb /* section contains symbols that + are to be coalesced */ +#define S_GB_ZEROFILL 0xc /* zero fill on demand section + (that can be larger than 4 + gigabytes) */ +#define S_INTERPOSING 0xd /* section with only pairs of + function pointers for + interposing */ +#define S_16BYTE_LITERALS 0xe /* section with only 16 byte + literals */ +#define S_DTRACE_DOF 0xf /* section contains + DTrace Object Format */ +#define S_LAZY_DYLIB_SYMBOL_POINTERS 0x10 /* section with only lazy + symbol pointers to lazy + loaded dylibs */ +/* + * Section types to support thread local variables + */ +#define S_THREAD_LOCAL_REGULAR 0x11 /* template of initial + values for TLVs */ +#define S_THREAD_LOCAL_ZEROFILL 0x12 /* template of initial + values for TLVs */ +#define S_THREAD_LOCAL_VARIABLES 0x13 /* TLV descriptors */ +#define S_THREAD_LOCAL_VARIABLE_POINTERS 0x14 /* pointers to TLV + descriptors */ +#define S_THREAD_LOCAL_INIT_FUNCTION_POINTERS 0x15 /* functions to call + to initialize TLV + values */ + +/* + * Constants for the section attributes part of the flags field of a section + * structure. + */ +#define SECTION_ATTRIBUTES_USR 0xff000000 /* User setable attributes */ +#define S_ATTR_PURE_INSTRUCTIONS 0x80000000 /* section contains only true + machine instructions */ +#define S_ATTR_NO_TOC 0x40000000 /* section contains coalesced + symbols that are not to be + in a ranlib table of + contents */ +#define S_ATTR_STRIP_STATIC_SYMS 0x20000000 /* ok to strip static symbols + in this section in files + with the MH_DYLDLINK flag */ +#define S_ATTR_NO_DEAD_STRIP 0x10000000 /* no dead stripping */ +#define S_ATTR_LIVE_SUPPORT 0x08000000 /* blocks are live if they + reference live blocks */ +#define S_ATTR_SELF_MODIFYING_CODE 0x04000000 /* Used with i386 code stubs + written on by dyld */ +/* + * If a segment contains any sections marked with S_ATTR_DEBUG then all + * sections in that segment must have this attribute. No section other than + * a section marked with this attribute may reference the contents of this + * section. A section with this attribute may contain no symbols and must have + * a section type S_REGULAR. The static linker will not copy section contents + * from sections with this attribute into its output file. These sections + * generally contain DWARF debugging info. + */ +#define S_ATTR_DEBUG 0x02000000 /* a debug section */ +#define SECTION_ATTRIBUTES_SYS 0x00ffff00 /* system setable attributes */ +#define S_ATTR_SOME_INSTRUCTIONS 0x00000400 /* section contains some + machine instructions */ +#define S_ATTR_EXT_RELOC 0x00000200 /* section has external + relocation entries */ +#define S_ATTR_LOC_RELOC 0x00000100 /* section has local + relocation entries */ + + +/* + * The names of segments and sections in them are mostly meaningless to the + * link-editor. But there are few things to support traditional UNIX + * executables that require the link-editor and assembler to use some names + * agreed upon by convention. + * + * The initial protection of the "__TEXT" segment has write protection turned + * off (not writeable). + * + * The link-editor will allocate common symbols at the end of the "__common" + * section in the "__DATA" segment. It will create the section and segment + * if needed. + */ + +/* The currently known segment names and the section names in those segments */ + +#define SEG_PAGEZERO "__PAGEZERO" /* the pagezero segment which has no */ + /* protections and catches NULL */ + /* references for MH_EXECUTE files */ + + +#define SEG_TEXT "__TEXT" /* the tradition UNIX text segment */ +#define SECT_TEXT "__text" /* the real text part of the text */ + /* section no headers, and no padding */ +#define SECT_FVMLIB_INIT0 "__fvmlib_init0" /* the fvmlib initialization */ + /* section */ +#define SECT_FVMLIB_INIT1 "__fvmlib_init1" /* the section following the */ + /* fvmlib initialization */ + /* section */ + +#define SEG_DATA "__DATA" /* the tradition UNIX data segment */ +#define SECT_DATA "__data" /* the real initialized data section */ + /* no padding, no bss overlap */ +#define SECT_BSS "__bss" /* the real uninitialized data section*/ + /* no padding */ +#define SECT_COMMON "__common" /* the section common symbols are */ + /* allocated in by the link editor */ + +#define SEG_OBJC "__OBJC" /* objective-C runtime segment */ +#define SECT_OBJC_SYMBOLS "__symbol_table" /* symbol table */ +#define SECT_OBJC_MODULES "__module_info" /* module information */ +#define SECT_OBJC_STRINGS "__selector_strs" /* string table */ +#define SECT_OBJC_REFS "__selector_refs" /* string table */ + +#define SEG_ICON "__ICON" /* the icon segment */ +#define SECT_ICON_HEADER "__header" /* the icon headers */ +#define SECT_ICON_TIFF "__tiff" /* the icons in tiff format */ + +#define SEG_LINKEDIT "__LINKEDIT" /* the segment containing all structs */ + /* created and maintained by the link */ + /* editor. Created with -seglinkedit */ + /* option to ld(1) for MH_EXECUTE and */ + /* FVMLIB file types only */ + +#define SEG_UNIXSTACK "__UNIXSTACK" /* the unix stack segment */ + +#define SEG_IMPORT "__IMPORT" /* the segment for the self (dyld) */ + /* modifing code stubs that has read, */ + /* write and execute permissions */ + +/* + * Fixed virtual memory shared libraries are identified by two things. The + * target pathname (the name of the library as found for execution), and the + * minor version number. The address of where the headers are loaded is in + * header_addr. (THIS IS OBSOLETE and no longer supported). + */ +struct fvmlib { + union lc_str name; /* library's target pathname */ + uint32_t minor_version; /* library's minor version number */ + uint32_t header_addr; /* library's header address */ +}; + +/* + * A fixed virtual shared library (filetype == MH_FVMLIB in the mach header) + * contains a fvmlib_command (cmd == LC_IDFVMLIB) to identify the library. + * An object that uses a fixed virtual shared library also contains a + * fvmlib_command (cmd == LC_LOADFVMLIB) for each library it uses. + * (THIS IS OBSOLETE and no longer supported). + */ +struct fvmlib_command { + uint32_t cmd; /* LC_IDFVMLIB or LC_LOADFVMLIB */ + uint32_t cmdsize; /* includes pathname string */ + struct fvmlib fvmlib; /* the library identification */ +}; + +/* + * Dynamicly linked shared libraries are identified by two things. The + * pathname (the name of the library as found for execution), and the + * compatibility version number. The pathname must match and the compatibility + * number in the user of the library must be greater than or equal to the + * library being used. The time stamp is used to record the time a library was + * built and copied into user so it can be use to determined if the library used + * at runtime is exactly the same as used to built the program. + */ +struct dylib { + union lc_str name; /* library's path name */ + uint32_t timestamp; /* library's build time stamp */ + uint32_t current_version; /* library's current version number */ + uint32_t compatibility_version; /* library's compatibility vers number*/ +}; + +/* + * A dynamically linked shared library (filetype == MH_DYLIB in the mach header) + * contains a dylib_command (cmd == LC_ID_DYLIB) to identify the library. + * An object that uses a dynamically linked shared library also contains a + * dylib_command (cmd == LC_LOAD_DYLIB, LC_LOAD_WEAK_DYLIB, or + * LC_REEXPORT_DYLIB) for each library it uses. + */ +struct dylib_command { + uint32_t cmd; /* LC_ID_DYLIB, LC_LOAD_{,WEAK_}DYLIB, + LC_REEXPORT_DYLIB */ + uint32_t cmdsize; /* includes pathname string */ + struct dylib dylib; /* the library identification */ +}; + +/* + * A dynamically linked shared library may be a subframework of an umbrella + * framework. If so it will be linked with "-umbrella umbrella_name" where + * Where "umbrella_name" is the name of the umbrella framework. A subframework + * can only be linked against by its umbrella framework or other subframeworks + * that are part of the same umbrella framework. Otherwise the static link + * editor produces an error and states to link against the umbrella framework. + * The name of the umbrella framework for subframeworks is recorded in the + * following structure. + */ +struct sub_framework_command { + uint32_t cmd; /* LC_SUB_FRAMEWORK */ + uint32_t cmdsize; /* includes umbrella string */ + union lc_str umbrella; /* the umbrella framework name */ +}; + +/* + * For dynamically linked shared libraries that are subframework of an umbrella + * framework they can allow clients other than the umbrella framework or other + * subframeworks in the same umbrella framework. To do this the subframework + * is built with "-allowable_client client_name" and an LC_SUB_CLIENT load + * command is created for each -allowable_client flag. The client_name is + * usually a framework name. It can also be a name used for bundles clients + * where the bundle is built with "-client_name client_name". + */ +struct sub_client_command { + uint32_t cmd; /* LC_SUB_CLIENT */ + uint32_t cmdsize; /* includes client string */ + union lc_str client; /* the client name */ +}; + +/* + * A dynamically linked shared library may be a sub_umbrella of an umbrella + * framework. If so it will be linked with "-sub_umbrella umbrella_name" where + * Where "umbrella_name" is the name of the sub_umbrella framework. When + * staticly linking when -twolevel_namespace is in effect a twolevel namespace + * umbrella framework will only cause its subframeworks and those frameworks + * listed as sub_umbrella frameworks to be implicited linked in. Any other + * dependent dynamic libraries will not be linked it when -twolevel_namespace + * is in effect. The primary library recorded by the static linker when + * resolving a symbol in these libraries will be the umbrella framework. + * Zero or more sub_umbrella frameworks may be use by an umbrella framework. + * The name of a sub_umbrella framework is recorded in the following structure. + */ +struct sub_umbrella_command { + uint32_t cmd; /* LC_SUB_UMBRELLA */ + uint32_t cmdsize; /* includes sub_umbrella string */ + union lc_str sub_umbrella; /* the sub_umbrella framework name */ +}; + +/* + * A dynamically linked shared library may be a sub_library of another shared + * library. If so it will be linked with "-sub_library library_name" where + * Where "library_name" is the name of the sub_library shared library. When + * staticly linking when -twolevel_namespace is in effect a twolevel namespace + * shared library will only cause its subframeworks and those frameworks + * listed as sub_umbrella frameworks and libraries listed as sub_libraries to + * be implicited linked in. Any other dependent dynamic libraries will not be + * linked it when -twolevel_namespace is in effect. The primary library + * recorded by the static linker when resolving a symbol in these libraries + * will be the umbrella framework (or dynamic library). Zero or more sub_library + * shared libraries may be use by an umbrella framework or (or dynamic library). + * The name of a sub_library framework is recorded in the following structure. + * For example /usr/lib/libobjc_profile.A.dylib would be recorded as "libobjc". + */ +struct sub_library_command { + uint32_t cmd; /* LC_SUB_LIBRARY */ + uint32_t cmdsize; /* includes sub_library string */ + union lc_str sub_library; /* the sub_library name */ +}; + +/* + * A program (filetype == MH_EXECUTE) that is + * prebound to its dynamic libraries has one of these for each library that + * the static linker used in prebinding. It contains a bit vector for the + * modules in the library. The bits indicate which modules are bound (1) and + * which are not (0) from the library. The bit for module 0 is the low bit + * of the first byte. So the bit for the Nth module is: + * (linked_modules[N/8] >> N%8) & 1 + */ +struct prebound_dylib_command { + uint32_t cmd; /* LC_PREBOUND_DYLIB */ + uint32_t cmdsize; /* includes strings */ + union lc_str name; /* library's path name */ + uint32_t nmodules; /* number of modules in library */ + union lc_str linked_modules; /* bit vector of linked modules */ +}; + +/* + * A program that uses a dynamic linker contains a dylinker_command to identify + * the name of the dynamic linker (LC_LOAD_DYLINKER). And a dynamic linker + * contains a dylinker_command to identify the dynamic linker (LC_ID_DYLINKER). + * A file can have at most one of these. + * This struct is also used for the LC_DYLD_ENVIRONMENT load command and + * contains string for dyld to treat like environment variable. + */ +struct dylinker_command { + uint32_t cmd; /* LC_ID_DYLINKER, LC_LOAD_DYLINKER or + LC_DYLD_ENVIRONMENT */ + uint32_t cmdsize; /* includes pathname string */ + union lc_str name; /* dynamic linker's path name */ +}; + +/* + * Thread commands contain machine-specific data structures suitable for + * use in the thread state primitives. The machine specific data structures + * follow the struct thread_command as follows. + * Each flavor of machine specific data structure is preceded by an unsigned + * long constant for the flavor of that data structure, an uint32_t + * that is the count of longs of the size of the state data structure and then + * the state data structure follows. This triple may be repeated for many + * flavors. The constants for the flavors, counts and state data structure + * definitions are expected to be in the header file . + * These machine specific data structures sizes must be multiples of + * 4 bytes The cmdsize reflects the total size of the thread_command + * and all of the sizes of the constants for the flavors, counts and state + * data structures. + * + * For executable objects that are unix processes there will be one + * thread_command (cmd == LC_UNIXTHREAD) created for it by the link-editor. + * This is the same as a LC_THREAD, except that a stack is automatically + * created (based on the shell's limit for the stack size). Command arguments + * and environment variables are copied onto that stack. + */ +struct thread_command { + uint32_t cmd; /* LC_THREAD or LC_UNIXTHREAD */ + uint32_t cmdsize; /* total size of this command */ + /* uint32_t flavor flavor of thread state */ + /* uint32_t count count of longs in thread state */ + /* struct XXX_thread_state state thread state for this flavor */ + /* ... */ +}; + +/* + * The routines command contains the address of the dynamic shared library + * initialization routine and an index into the module table for the module + * that defines the routine. Before any modules are used from the library the + * dynamic linker fully binds the module that defines the initialization routine + * and then calls it. This gets called before any module initialization + * routines (used for C++ static constructors) in the library. + */ +struct routines_command { /* for 32-bit architectures */ + uint32_t cmd; /* LC_ROUTINES */ + uint32_t cmdsize; /* total size of this command */ + uint32_t init_address; /* address of initialization routine */ + uint32_t init_module; /* index into the module table that */ + /* the init routine is defined in */ + uint32_t reserved1; + uint32_t reserved2; + uint32_t reserved3; + uint32_t reserved4; + uint32_t reserved5; + uint32_t reserved6; +}; + +/* + * The 64-bit routines command. Same use as above. + */ +struct routines_command_64 { /* for 64-bit architectures */ + uint32_t cmd; /* LC_ROUTINES_64 */ + uint32_t cmdsize; /* total size of this command */ + uint64_t init_address; /* address of initialization routine */ + uint64_t init_module; /* index into the module table that */ + /* the init routine is defined in */ + uint64_t reserved1; + uint64_t reserved2; + uint64_t reserved3; + uint64_t reserved4; + uint64_t reserved5; + uint64_t reserved6; +}; + +/* + * The symtab_command contains the offsets and sizes of the link-edit 4.3BSD + * "stab" style symbol table information as described in the header files + * and . + */ +struct symtab_command { + uint32_t cmd; /* LC_SYMTAB */ + uint32_t cmdsize; /* sizeof(struct symtab_command) */ + uint32_t symoff; /* symbol table offset */ + uint32_t nsyms; /* number of symbol table entries */ + uint32_t stroff; /* string table offset */ + uint32_t strsize; /* string table size in bytes */ +}; + +/* + * This is the second set of the symbolic information which is used to support + * the data structures for the dynamically link editor. + * + * The original set of symbolic information in the symtab_command which contains + * the symbol and string tables must also be present when this load command is + * present. When this load command is present the symbol table is organized + * into three groups of symbols: + * local symbols (static and debugging symbols) - grouped by module + * defined external symbols - grouped by module (sorted by name if not lib) + * undefined external symbols (sorted by name if MH_BINDATLOAD is not set, + * and in order the were seen by the static + * linker if MH_BINDATLOAD is set) + * In this load command there are offsets and counts to each of the three groups + * of symbols. + * + * This load command contains a the offsets and sizes of the following new + * symbolic information tables: + * table of contents + * module table + * reference symbol table + * indirect symbol table + * The first three tables above (the table of contents, module table and + * reference symbol table) are only present if the file is a dynamically linked + * shared library. For executable and object modules, which are files + * containing only one module, the information that would be in these three + * tables is determined as follows: + * table of contents - the defined external symbols are sorted by name + * module table - the file contains only one module so everything in the + * file is part of the module. + * reference symbol table - is the defined and undefined external symbols + * + * For dynamically linked shared library files this load command also contains + * offsets and sizes to the pool of relocation entries for all sections + * separated into two groups: + * external relocation entries + * local relocation entries + * For executable and object modules the relocation entries continue to hang + * off the section structures. + */ +struct dysymtab_command { + uint32_t cmd; /* LC_DYSYMTAB */ + uint32_t cmdsize; /* sizeof(struct dysymtab_command) */ + + /* + * The symbols indicated by symoff and nsyms of the LC_SYMTAB load command + * are grouped into the following three groups: + * local symbols (further grouped by the module they are from) + * defined external symbols (further grouped by the module they are from) + * undefined symbols + * + * The local symbols are used only for debugging. The dynamic binding + * process may have to use them to indicate to the debugger the local + * symbols for a module that is being bound. + * + * The last two groups are used by the dynamic binding process to do the + * binding (indirectly through the module table and the reference symbol + * table when this is a dynamically linked shared library file). + */ + uint32_t ilocalsym; /* index to local symbols */ + uint32_t nlocalsym; /* number of local symbols */ + + uint32_t iextdefsym;/* index to externally defined symbols */ + uint32_t nextdefsym;/* number of externally defined symbols */ + + uint32_t iundefsym; /* index to undefined symbols */ + uint32_t nundefsym; /* number of undefined symbols */ + + /* + * For the for the dynamic binding process to find which module a symbol + * is defined in the table of contents is used (analogous to the ranlib + * structure in an archive) which maps defined external symbols to modules + * they are defined in. This exists only in a dynamically linked shared + * library file. For executable and object modules the defined external + * symbols are sorted by name and is use as the table of contents. + */ + uint32_t tocoff; /* file offset to table of contents */ + uint32_t ntoc; /* number of entries in table of contents */ + + /* + * To support dynamic binding of "modules" (whole object files) the symbol + * table must reflect the modules that the file was created from. This is + * done by having a module table that has indexes and counts into the merged + * tables for each module. The module structure that these two entries + * refer to is described below. This exists only in a dynamically linked + * shared library file. For executable and object modules the file only + * contains one module so everything in the file belongs to the module. + */ + uint32_t modtaboff; /* file offset to module table */ + uint32_t nmodtab; /* number of module table entries */ + + /* + * To support dynamic module binding the module structure for each module + * indicates the external references (defined and undefined) each module + * makes. For each module there is an offset and a count into the + * reference symbol table for the symbols that the module references. + * This exists only in a dynamically linked shared library file. For + * executable and object modules the defined external symbols and the + * undefined external symbols indicates the external references. + */ + uint32_t extrefsymoff; /* offset to referenced symbol table */ + uint32_t nextrefsyms; /* number of referenced symbol table entries */ + + /* + * The sections that contain "symbol pointers" and "routine stubs" have + * indexes and (implied counts based on the size of the section and fixed + * size of the entry) into the "indirect symbol" table for each pointer + * and stub. For every section of these two types the index into the + * indirect symbol table is stored in the section header in the field + * reserved1. An indirect symbol table entry is simply a 32bit index into + * the symbol table to the symbol that the pointer or stub is referring to. + * The indirect symbol table is ordered to match the entries in the section. + */ + uint32_t indirectsymoff; /* file offset to the indirect symbol table */ + uint32_t nindirectsyms; /* number of indirect symbol table entries */ + + /* + * To support relocating an individual module in a library file quickly the + * external relocation entries for each module in the library need to be + * accessed efficiently. Since the relocation entries can't be accessed + * through the section headers for a library file they are separated into + * groups of local and external entries further grouped by module. In this + * case the presents of this load command who's extreloff, nextrel, + * locreloff and nlocrel fields are non-zero indicates that the relocation + * entries of non-merged sections are not referenced through the section + * structures (and the reloff and nreloc fields in the section headers are + * set to zero). + * + * Since the relocation entries are not accessed through the section headers + * this requires the r_address field to be something other than a section + * offset to identify the item to be relocated. In this case r_address is + * set to the offset from the vmaddr of the first LC_SEGMENT command. + * For MH_SPLIT_SEGS images r_address is set to the the offset from the + * vmaddr of the first read-write LC_SEGMENT command. + * + * The relocation entries are grouped by module and the module table + * entries have indexes and counts into them for the group of external + * relocation entries for that the module. + * + * For sections that are merged across modules there must not be any + * remaining external relocation entries for them (for merged sections + * remaining relocation entries must be local). + */ + uint32_t extreloff; /* offset to external relocation entries */ + uint32_t nextrel; /* number of external relocation entries */ + + /* + * All the local relocation entries are grouped together (they are not + * grouped by their module since they are only used if the object is moved + * from it staticly link edited address). + */ + uint32_t locreloff; /* offset to local relocation entries */ + uint32_t nlocrel; /* number of local relocation entries */ + +}; + +/* + * An indirect symbol table entry is simply a 32bit index into the symbol table + * to the symbol that the pointer or stub is refering to. Unless it is for a + * non-lazy symbol pointer section for a defined symbol which strip(1) as + * removed. In which case it has the value INDIRECT_SYMBOL_LOCAL. If the + * symbol was also absolute INDIRECT_SYMBOL_ABS is or'ed with that. + */ +#define INDIRECT_SYMBOL_LOCAL 0x80000000 +#define INDIRECT_SYMBOL_ABS 0x40000000 + + +/* a table of contents entry */ +struct dylib_table_of_contents { + uint32_t symbol_index; /* the defined external symbol + (index into the symbol table) */ + uint32_t module_index; /* index into the module table this symbol + is defined in */ +}; + +/* a module table entry */ +struct dylib_module { + uint32_t module_name; /* the module name (index into string table) */ + + uint32_t iextdefsym; /* index into externally defined symbols */ + uint32_t nextdefsym; /* number of externally defined symbols */ + uint32_t irefsym; /* index into reference symbol table */ + uint32_t nrefsym; /* number of reference symbol table entries */ + uint32_t ilocalsym; /* index into symbols for local symbols */ + uint32_t nlocalsym; /* number of local symbols */ + + uint32_t iextrel; /* index into external relocation entries */ + uint32_t nextrel; /* number of external relocation entries */ + + uint32_t iinit_iterm; /* low 16 bits are the index into the init + section, high 16 bits are the index into + the term section */ + uint32_t ninit_nterm; /* low 16 bits are the number of init section + entries, high 16 bits are the number of + term section entries */ + + uint32_t /* for this module address of the start of */ + objc_module_info_addr; /* the (__OBJC,__module_info) section */ + uint32_t /* for this module size of */ + objc_module_info_size; /* the (__OBJC,__module_info) section */ +}; + +/* a 64-bit module table entry */ +struct dylib_module_64 { + uint32_t module_name; /* the module name (index into string table) */ + + uint32_t iextdefsym; /* index into externally defined symbols */ + uint32_t nextdefsym; /* number of externally defined symbols */ + uint32_t irefsym; /* index into reference symbol table */ + uint32_t nrefsym; /* number of reference symbol table entries */ + uint32_t ilocalsym; /* index into symbols for local symbols */ + uint32_t nlocalsym; /* number of local symbols */ + + uint32_t iextrel; /* index into external relocation entries */ + uint32_t nextrel; /* number of external relocation entries */ + + uint32_t iinit_iterm; /* low 16 bits are the index into the init + section, high 16 bits are the index into + the term section */ + uint32_t ninit_nterm; /* low 16 bits are the number of init section + entries, high 16 bits are the number of + term section entries */ + + uint32_t /* for this module size of */ + objc_module_info_size; /* the (__OBJC,__module_info) section */ + uint64_t /* for this module address of the start of */ + objc_module_info_addr; /* the (__OBJC,__module_info) section */ +}; + +/* + * The entries in the reference symbol table are used when loading the module + * (both by the static and dynamic link editors) and if the module is unloaded + * or replaced. Therefore all external symbols (defined and undefined) are + * listed in the module's reference table. The flags describe the type of + * reference that is being made. The constants for the flags are defined in + * as they are also used for symbol table entries. + */ +struct dylib_reference { + uint32_t isym:24, /* index into the symbol table */ + flags:8; /* flags to indicate the type of reference */ +}; + +/* + * The twolevel_hints_command contains the offset and number of hints in the + * two-level namespace lookup hints table. + */ +struct twolevel_hints_command { + uint32_t cmd; /* LC_TWOLEVEL_HINTS */ + uint32_t cmdsize; /* sizeof(struct twolevel_hints_command) */ + uint32_t offset; /* offset to the hint table */ + uint32_t nhints; /* number of hints in the hint table */ +}; + +/* + * The entries in the two-level namespace lookup hints table are twolevel_hint + * structs. These provide hints to the dynamic link editor where to start + * looking for an undefined symbol in a two-level namespace image. The + * isub_image field is an index into the sub-images (sub-frameworks and + * sub-umbrellas list) that made up the two-level image that the undefined + * symbol was found in when it was built by the static link editor. If + * isub-image is 0 the the symbol is expected to be defined in library and not + * in the sub-images. If isub-image is non-zero it is an index into the array + * of sub-images for the umbrella with the first index in the sub-images being + * 1. The array of sub-images is the ordered list of sub-images of the umbrella + * that would be searched for a symbol that has the umbrella recorded as its + * primary library. The table of contents index is an index into the + * library's table of contents. This is used as the starting point of the + * binary search or a directed linear search. + */ +struct twolevel_hint { + uint32_t + isub_image:8, /* index into the sub images */ + itoc:24; /* index into the table of contents */ +}; + +/* + * The prebind_cksum_command contains the value of the original check sum for + * prebound files or zero. When a prebound file is first created or modified + * for other than updating its prebinding information the value of the check sum + * is set to zero. When the file has it prebinding re-done and if the value of + * the check sum is zero the original check sum is calculated and stored in + * cksum field of this load command in the output file. If when the prebinding + * is re-done and the cksum field is non-zero it is left unchanged from the + * input file. + */ +struct prebind_cksum_command { + uint32_t cmd; /* LC_PREBIND_CKSUM */ + uint32_t cmdsize; /* sizeof(struct prebind_cksum_command) */ + uint32_t cksum; /* the check sum or zero */ +}; + +/* + * The uuid load command contains a single 128-bit unique random number that + * identifies an object produced by the static link editor. + */ +struct uuid_command { + uint32_t cmd; /* LC_UUID */ + uint32_t cmdsize; /* sizeof(struct uuid_command) */ + uint8_t uuid[16]; /* the 128-bit uuid */ +}; + +/* + * The rpath_command contains a path which at runtime should be added to + * the current run path used to find @rpath prefixed dylibs. + */ +struct rpath_command { + uint32_t cmd; /* LC_RPATH */ + uint32_t cmdsize; /* includes string */ + union lc_str path; /* path to add to run path */ +}; + +/* + * The linkedit_data_command contains the offsets and sizes of a blob + * of data in the __LINKEDIT segment. + */ +struct linkedit_data_command { + uint32_t cmd; /* LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO, + or LC_FUNCTION_STARTS */ + uint32_t cmdsize; /* sizeof(struct linkedit_data_command) */ + uint32_t dataoff; /* file offset of data in __LINKEDIT segment */ + uint32_t datasize; /* file size of data in __LINKEDIT segment */ +}; + +/* + * The encryption_info_command contains the file offset and size of an + * of an encrypted segment. + */ +struct encryption_info_command { + uint32_t cmd; /* LC_ENCRYPTION_INFO */ + uint32_t cmdsize; /* sizeof(struct encryption_info_command) */ + uint32_t cryptoff; /* file offset of encrypted range */ + uint32_t cryptsize; /* file size of encrypted range */ + uint32_t cryptid; /* which enryption system, + 0 means not-encrypted yet */ +}; + +/* + * The version_min_command contains the min OS version on which this + * binary was built to run. + */ +struct version_min_command { + uint32_t cmd; /* LC_VERSION_MIN_MACOSX or + LC_VERSION_MIN_IPHONEOS */ + uint32_t cmdsize; /* sizeof(struct min_version_command) */ + uint32_t version; /* X.Y.Z is encoded in nibbles xxxx.yy.zz */ + uint32_t reserved; /* zero */ +}; + +/* + * The dyld_info_command contains the file offsets and sizes of + * the new compressed form of the information dyld needs to + * load the image. This information is used by dyld on Mac OS X + * 10.6 and later. All information pointed to by this command + * is encoded using byte streams, so no endian swapping is needed + * to interpret it. + */ +struct dyld_info_command { + uint32_t cmd; /* LC_DYLD_INFO or LC_DYLD_INFO_ONLY */ + uint32_t cmdsize; /* sizeof(struct dyld_info_command) */ + + /* + * Dyld rebases an image whenever dyld loads it at an address different + * from its preferred address. The rebase information is a stream + * of byte sized opcodes whose symbolic names start with REBASE_OPCODE_. + * Conceptually the rebase information is a table of tuples: + * + * The opcodes are a compressed way to encode the table by only + * encoding when a column changes. In addition simple patterns + * like "every n'th offset for m times" can be encoded in a few + * bytes. + */ + uint32_t rebase_off; /* file offset to rebase info */ + uint32_t rebase_size; /* size of rebase info */ + + /* + * Dyld binds an image during the loading process, if the image + * requires any pointers to be initialized to symbols in other images. + * The bind information is a stream of byte sized + * opcodes whose symbolic names start with BIND_OPCODE_. + * Conceptually the bind information is a table of tuples: + * + * The opcodes are a compressed way to encode the table by only + * encoding when a column changes. In addition simple patterns + * like for runs of pointers initialzed to the same value can be + * encoded in a few bytes. + */ + uint32_t bind_off; /* file offset to binding info */ + uint32_t bind_size; /* size of binding info */ + + /* + * Some C++ programs require dyld to unique symbols so that all + * images in the process use the same copy of some code/data. + * This step is done after binding. The content of the weak_bind + * info is an opcode stream like the bind_info. But it is sorted + * alphabetically by symbol name. This enable dyld to walk + * all images with weak binding information in order and look + * for collisions. If there are no collisions, dyld does + * no updating. That means that some fixups are also encoded + * in the bind_info. For instance, all calls to "operator new" + * are first bound to libstdc++.dylib using the information + * in bind_info. Then if some image overrides operator new + * that is detected when the weak_bind information is processed + * and the call to operator new is then rebound. + */ + uint32_t weak_bind_off; /* file offset to weak binding info */ + uint32_t weak_bind_size; /* size of weak binding info */ + + /* + * Some uses of external symbols do not need to be bound immediately. + * Instead they can be lazily bound on first use. The lazy_bind + * are contains a stream of BIND opcodes to bind all lazy symbols. + * Normal use is that dyld ignores the lazy_bind section when + * loading an image. Instead the static linker arranged for the + * lazy pointer to initially point to a helper function which + * pushes the offset into the lazy_bind area for the symbol + * needing to be bound, then jumps to dyld which simply adds + * the offset to lazy_bind_off to get the information on what + * to bind. + */ + uint32_t lazy_bind_off; /* file offset to lazy binding info */ + uint32_t lazy_bind_size; /* size of lazy binding infs */ + + /* + * The symbols exported by a dylib are encoded in a trie. This + * is a compact representation that factors out common prefixes. + * It also reduces LINKEDIT pages in RAM because it encodes all + * information (name, address, flags) in one small, contiguous range. + * The export area is a stream of nodes. The first node sequentially + * is the start node for the trie. + * + * Nodes for a symbol start with a uleb128 that is the length of + * the exported symbol information for the string so far. + * If there is no exported symbol, the node starts with a zero byte. + * If there is exported info, it follows the length. First is + * a uleb128 containing flags. Normally, it is followed by a + * uleb128 encoded offset which is location of the content named + * by the symbol from the mach_header for the image. If the flags + * is EXPORT_SYMBOL_FLAGS_REEXPORT, then following the flags is + * a uleb128 encoded library ordinal, then a zero terminated + * UTF8 string. If the string is zero length, then the symbol + * is re-export from the specified dylib with the same name. + * + * After the optional exported symbol information is a byte of + * how many edges (0-255) that this node has leaving it, + * followed by each edge. + * Each edge is a zero terminated UTF8 of the addition chars + * in the symbol, followed by a uleb128 offset for the node that + * edge points to. + * + */ + uint32_t export_off; /* file offset to lazy binding info */ + uint32_t export_size; /* size of lazy binding infs */ +}; + +/* + * The following are used to encode rebasing information + */ +#define REBASE_TYPE_POINTER 1 +#define REBASE_TYPE_TEXT_ABSOLUTE32 2 +#define REBASE_TYPE_TEXT_PCREL32 3 + +#define REBASE_OPCODE_MASK 0xF0 +#define REBASE_IMMEDIATE_MASK 0x0F +#define REBASE_OPCODE_DONE 0x00 +#define REBASE_OPCODE_SET_TYPE_IMM 0x10 +#define REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB 0x20 +#define REBASE_OPCODE_ADD_ADDR_ULEB 0x30 +#define REBASE_OPCODE_ADD_ADDR_IMM_SCALED 0x40 +#define REBASE_OPCODE_DO_REBASE_IMM_TIMES 0x50 +#define REBASE_OPCODE_DO_REBASE_ULEB_TIMES 0x60 +#define REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB 0x70 +#define REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB 0x80 + + +/* + * The following are used to encode binding information + */ +#define BIND_TYPE_POINTER 1 +#define BIND_TYPE_TEXT_ABSOLUTE32 2 +#define BIND_TYPE_TEXT_PCREL32 3 + +#define BIND_SPECIAL_DYLIB_SELF 0 +#define BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE -1 +#define BIND_SPECIAL_DYLIB_FLAT_LOOKUP -2 + +#define BIND_SYMBOL_FLAGS_WEAK_IMPORT 0x1 +#define BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION 0x8 + +#define BIND_OPCODE_MASK 0xF0 +#define BIND_IMMEDIATE_MASK 0x0F +#define BIND_OPCODE_DONE 0x00 +#define BIND_OPCODE_SET_DYLIB_ORDINAL_IMM 0x10 +#define BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB 0x20 +#define BIND_OPCODE_SET_DYLIB_SPECIAL_IMM 0x30 +#define BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM 0x40 +#define BIND_OPCODE_SET_TYPE_IMM 0x50 +#define BIND_OPCODE_SET_ADDEND_SLEB 0x60 +#define BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB 0x70 +#define BIND_OPCODE_ADD_ADDR_ULEB 0x80 +#define BIND_OPCODE_DO_BIND 0x90 +#define BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB 0xA0 +#define BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED 0xB0 +#define BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB 0xC0 + + +/* + * The following are used on the flags byte of a terminal node + * in the export information. + */ +#define EXPORT_SYMBOL_FLAGS_KIND_MASK 0x03 +#define EXPORT_SYMBOL_FLAGS_KIND_REGULAR 0x00 +#define EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL 0x01 +#define EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION 0x04 +#define EXPORT_SYMBOL_FLAGS_REEXPORT 0x08 +#define EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER 0x10 + +/* + * The symseg_command contains the offset and size of the GNU style + * symbol table information as described in the header file . + * The symbol roots of the symbol segments must also be aligned properly + * in the file. So the requirement of keeping the offsets aligned to a + * multiple of a 4 bytes translates to the length field of the symbol + * roots also being a multiple of a long. Also the padding must again be + * zeroed. (THIS IS OBSOLETE and no longer supported). + */ +struct symseg_command { + uint32_t cmd; /* LC_SYMSEG */ + uint32_t cmdsize; /* sizeof(struct symseg_command) */ + uint32_t offset; /* symbol segment offset */ + uint32_t size; /* symbol segment size in bytes */ +}; + +/* + * The ident_command contains a free format string table following the + * ident_command structure. The strings are null terminated and the size of + * the command is padded out with zero bytes to a multiple of 4 bytes/ + * (THIS IS OBSOLETE and no longer supported). + */ +struct ident_command { + uint32_t cmd; /* LC_IDENT */ + uint32_t cmdsize; /* strings that follow this command */ +}; + +/* + * The fvmfile_command contains a reference to a file to be loaded at the + * specified virtual address. (Presently, this command is reserved for + * internal use. The kernel ignores this command when loading a program into + * memory). + */ +struct fvmfile_command { + uint32_t cmd; /* LC_FVMFILE */ + uint32_t cmdsize; /* includes pathname string */ + union lc_str name; /* files pathname */ + uint32_t header_addr; /* files virtual address */ +}; + +/* + * Sections of type S_THREAD_LOCAL_VARIABLES contain an array + * of tlv_descriptor structures. + */ +struct tlv_descriptor +{ + void* (*thunk)(struct tlv_descriptor*); + unsigned long key; + unsigned long offset; +}; + +#endif /* _MACHO_LOADER_H_ */ diff --git a/shared/sentry/external/breakpad/src/third_party/mac_headers/mach-o/nlist.h b/shared/sentry/external/breakpad/src/third_party/mac_headers/mach-o/nlist.h new file mode 100644 index 000000000..1c1941012 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/mac_headers/mach-o/nlist.h @@ -0,0 +1,312 @@ +/* + * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ +#ifndef _MACHO_NLIST_H_ +#define _MACHO_NLIST_H_ +/* $NetBSD: nlist.h,v 1.5 1994/10/26 00:56:11 cgd Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * (c) UNIX System Laboratories, Inc. + * All or some portions of this file are derived from material licensed + * to the University of California by American Telephone and Telegraph + * Co. or Unix System Laboratories, Inc. and are reproduced herein with + * the permission of UNIX System Laboratories, Inc. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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. + * + * @(#)nlist.h 8.2 (Berkeley) 1/21/94 + */ +#include + +/* + * Format of a symbol table entry of a Mach-O file for 32-bit architectures. + * Modified from the BSD format. The modifications from the original format + * were changing n_other (an unused field) to n_sect and the addition of the + * N_SECT type. These modifications are required to support symbols in a larger + * number of sections not just the three sections (text, data and bss) in a BSD + * file. + */ +struct nlist { + union { +#ifndef __LP64__ + char *n_name; /* for use when in-core */ +#endif + int32_t n_strx; /* index into the string table */ + } n_un; + uint8_t n_type; /* type flag, see below */ + uint8_t n_sect; /* section number or NO_SECT */ + int16_t n_desc; /* see */ + uint32_t n_value; /* value of this symbol (or stab offset) */ +}; + +/* + * This is the symbol table entry structure for 64-bit architectures. + */ +struct nlist_64 { + union { + uint32_t n_strx; /* index into the string table */ + } n_un; + uint8_t n_type; /* type flag, see below */ + uint8_t n_sect; /* section number or NO_SECT */ + uint16_t n_desc; /* see */ + uint64_t n_value; /* value of this symbol (or stab offset) */ +}; + +/* + * Symbols with a index into the string table of zero (n_un.n_strx == 0) are + * defined to have a null, "", name. Therefore all string indexes to non null + * names must not have a zero string index. This is bit historical information + * that has never been well documented. + */ + +/* + * The n_type field really contains four fields: + * unsigned char N_STAB:3, + * N_PEXT:1, + * N_TYPE:3, + * N_EXT:1; + * which are used via the following masks. + */ +#define N_STAB 0xe0 /* if any of these bits set, a symbolic debugging entry */ +#define N_PEXT 0x10 /* private external symbol bit */ +#define N_TYPE 0x0e /* mask for the type bits */ +#define N_EXT 0x01 /* external symbol bit, set for external symbols */ + +/* + * Only symbolic debugging entries have some of the N_STAB bits set and if any + * of these bits are set then it is a symbolic debugging entry (a stab). In + * which case then the values of the n_type field (the entire field) are given + * in + */ + +/* + * Values for N_TYPE bits of the n_type field. + */ +#define N_UNDF 0x0 /* undefined, n_sect == NO_SECT */ +#define N_ABS 0x2 /* absolute, n_sect == NO_SECT */ +#define N_SECT 0xe /* defined in section number n_sect */ +#define N_PBUD 0xc /* prebound undefined (defined in a dylib) */ +#define N_INDR 0xa /* indirect */ + +/* + * If the type is N_INDR then the symbol is defined to be the same as another + * symbol. In this case the n_value field is an index into the string table + * of the other symbol's name. When the other symbol is defined then they both + * take on the defined type and value. + */ + +/* + * If the type is N_SECT then the n_sect field contains an ordinal of the + * section the symbol is defined in. The sections are numbered from 1 and + * refer to sections in order they appear in the load commands for the file + * they are in. This means the same ordinal may very well refer to different + * sections in different files. + * + * The n_value field for all symbol table entries (including N_STAB's) gets + * updated by the link editor based on the value of it's n_sect field and where + * the section n_sect references gets relocated. If the value of the n_sect + * field is NO_SECT then it's n_value field is not changed by the link editor. + */ +#define NO_SECT 0 /* symbol is not in any section */ +#define MAX_SECT 255 /* 1 thru 255 inclusive */ + +/* + * Common symbols are represented by undefined (N_UNDF) external (N_EXT) types + * who's values (n_value) are non-zero. In which case the value of the n_value + * field is the size (in bytes) of the common symbol. The n_sect field is set + * to NO_SECT. The alignment of a common symbol may be set as a power of 2 + * between 2^1 and 2^15 as part of the n_desc field using the macros below. If + * the alignment is not set (a value of zero) then natural alignment based on + * the size is used. + */ +#define GET_COMM_ALIGN(n_desc) (((n_desc) >> 8) & 0x0f) +#define SET_COMM_ALIGN(n_desc,align) \ + (n_desc) = (((n_desc) & 0xf0ff) | (((align) & 0x0f) << 8)) + +/* + * To support the lazy binding of undefined symbols in the dynamic link-editor, + * the undefined symbols in the symbol table (the nlist structures) are marked + * with the indication if the undefined reference is a lazy reference or + * non-lazy reference. If both a non-lazy reference and a lazy reference is + * made to the same symbol the non-lazy reference takes precedence. A reference + * is lazy only when all references to that symbol are made through a symbol + * pointer in a lazy symbol pointer section. + * + * The implementation of marking nlist structures in the symbol table for + * undefined symbols will be to use some of the bits of the n_desc field as a + * reference type. The mask REFERENCE_TYPE will be applied to the n_desc field + * of an nlist structure for an undefined symbol to determine the type of + * undefined reference (lazy or non-lazy). + * + * The constants for the REFERENCE FLAGS are propagated to the reference table + * in a shared library file. In that case the constant for a defined symbol, + * REFERENCE_FLAG_DEFINED, is also used. + */ +/* Reference type bits of the n_desc field of undefined symbols */ +#define REFERENCE_TYPE 0x7 +/* types of references */ +#define REFERENCE_FLAG_UNDEFINED_NON_LAZY 0 +#define REFERENCE_FLAG_UNDEFINED_LAZY 1 +#define REFERENCE_FLAG_DEFINED 2 +#define REFERENCE_FLAG_PRIVATE_DEFINED 3 +#define REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY 4 +#define REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY 5 + +/* + * To simplify stripping of objects that use are used with the dynamic link + * editor, the static link editor marks the symbols defined an object that are + * referenced by a dynamicly bound object (dynamic shared libraries, bundles). + * With this marking strip knows not to strip these symbols. + */ +#define REFERENCED_DYNAMICALLY 0x0010 + +/* + * For images created by the static link editor with the -twolevel_namespace + * option in effect the flags field of the mach header is marked with + * MH_TWOLEVEL. And the binding of the undefined references of the image are + * determined by the static link editor. Which library an undefined symbol is + * bound to is recorded by the static linker in the high 8 bits of the n_desc + * field using the SET_LIBRARY_ORDINAL macro below. The ordinal recorded + * references the libraries listed in the Mach-O's LC_LOAD_DYLIB, + * LC_LOAD_WEAK_DYLIB, LC_REEXPORT_DYLIB, LC_LOAD_UPWARD_DYLIB, and + * LC_LAZY_LOAD_DYLIB, etc. load commands in the order they appear in the + * headers. The library ordinals start from 1. + * For a dynamic library that is built as a two-level namespace image the + * undefined references from module defined in another use the same nlist struct + * an in that case SELF_LIBRARY_ORDINAL is used as the library ordinal. For + * defined symbols in all images they also must have the library ordinal set to + * SELF_LIBRARY_ORDINAL. The EXECUTABLE_ORDINAL refers to the executable + * image for references from plugins that refer to the executable that loads + * them. + * + * The DYNAMIC_LOOKUP_ORDINAL is for undefined symbols in a two-level namespace + * image that are looked up by the dynamic linker with flat namespace semantics. + * This ordinal was added as a feature in Mac OS X 10.3 by reducing the + * value of MAX_LIBRARY_ORDINAL by one. So it is legal for existing binaries + * or binaries built with older tools to have 0xfe (254) dynamic libraries. In + * this case the ordinal value 0xfe (254) must be treated as a library ordinal + * for compatibility. + */ +#define GET_LIBRARY_ORDINAL(n_desc) (((n_desc) >> 8) & 0xff) +#define SET_LIBRARY_ORDINAL(n_desc,ordinal) \ + (n_desc) = (((n_desc) & 0x00ff) | (((ordinal) & 0xff) << 8)) +#define SELF_LIBRARY_ORDINAL 0x0 +#define MAX_LIBRARY_ORDINAL 0xfd +#define DYNAMIC_LOOKUP_ORDINAL 0xfe +#define EXECUTABLE_ORDINAL 0xff + +/* + * The bit 0x0020 of the n_desc field is used for two non-overlapping purposes + * and has two different symbolic names, N_NO_DEAD_STRIP and N_DESC_DISCARDED. + */ + +/* + * The N_NO_DEAD_STRIP bit of the n_desc field only ever appears in a + * relocatable .o file (MH_OBJECT filetype). And is used to indicate to the + * static link editor it is never to dead strip the symbol. + */ +#define N_NO_DEAD_STRIP 0x0020 /* symbol is not to be dead stripped */ + +/* + * The N_DESC_DISCARDED bit of the n_desc field never appears in linked image. + * But is used in very rare cases by the dynamic link editor to mark an in + * memory symbol as discared and longer used for linking. + */ +#define N_DESC_DISCARDED 0x0020 /* symbol is discarded */ + +/* + * The N_WEAK_REF bit of the n_desc field indicates to the dynamic linker that + * the undefined symbol is allowed to be missing and is to have the address of + * zero when missing. + */ +#define N_WEAK_REF 0x0040 /* symbol is weak referenced */ + +/* + * The N_WEAK_DEF bit of the n_desc field indicates to the static and dynamic + * linkers that the symbol definition is weak, allowing a non-weak symbol to + * also be used which causes the weak definition to be discared. Currently this + * is only supported for symbols in coalesed sections. + */ +#define N_WEAK_DEF 0x0080 /* coalesed symbol is a weak definition */ + +/* + * The N_REF_TO_WEAK bit of the n_desc field indicates to the dynamic linker + * that the undefined symbol should be resolved using flat namespace searching. + */ +#define N_REF_TO_WEAK 0x0080 /* reference to a weak symbol */ + +/* + * The N_ARM_THUMB_DEF bit of the n_desc field indicates that the symbol is + * a defintion of a Thumb function. + */ +#define N_ARM_THUMB_DEF 0x0008 /* symbol is a Thumb function (ARM) */ + +/* + * The N_SYMBOL_RESOLVER bit of the n_desc field indicates that the + * that the function is actually a resolver function and should + * be called to get the address of the real function to use. + * This bit is only available in .o files (MH_OBJECT filetype) + */ +#define N_SYMBOL_RESOLVER 0x0100 + +#ifndef __STRICT_BSD__ +#if __cplusplus +extern "C" { +#endif /* __cplusplus */ +/* + * The function nlist(3) from the C library. + */ +extern int nlist (const char *filename, struct nlist *list); +#if __cplusplus +} +#endif /* __cplusplus */ +#endif /* __STRICT_BSD__ */ + +#endif /* _MACHO_LIST_H_ */ diff --git a/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/boolean.h b/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/boolean.h new file mode 100644 index 000000000..641c3962d --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/boolean.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2000 Apple Computer, Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * @OSF_COPYRIGHT@ + */ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + */ +/* + * File: mach/boolean.h + * + * Boolean data type. + * + */ + +#ifndef _MACH_BOOLEAN_H_ +#define _MACH_BOOLEAN_H_ + +/* + * Pick up "boolean_t" type definition + */ + +#ifndef ASSEMBLER +#include +#endif /* ASSEMBLER */ + +/* + * Define TRUE and FALSE if not defined. + */ + +#ifndef TRUE +#define TRUE 1 +#endif /* TRUE */ + +#ifndef FALSE +#define FALSE 0 +#endif /* FALSE */ + +#endif /* _MACH_BOOLEAN_H_ */ diff --git a/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/i386/boolean.h b/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/i386/boolean.h new file mode 100644 index 000000000..100f7e7b5 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/i386/boolean.h @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * @OSF_COPYRIGHT@ + */ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + */ + +/* + * File: boolean.h + * + * Boolean type, for I386. + */ + +#ifndef _MACH_I386_BOOLEAN_H_ +#define _MACH_I386_BOOLEAN_H_ + +#if defined(__x86_64__) && !defined(KERNEL) +typedef unsigned int boolean_t; +#else +typedef int boolean_t; +#endif + +#endif /* _MACH_I386_BOOLEAN_H_ */ diff --git a/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/i386/vm_param.h b/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/i386/vm_param.h new file mode 100644 index 000000000..edcb83496 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/i386/vm_param.h @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * @OSF_COPYRIGHT@ + */ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ + +/* + * Copyright (c) 1994 The University of Utah and + * the Computer Systems Laboratory at the University of Utah (CSL). + * All rights reserved. + * + * Permission to use, copy, modify and distribute this software is hereby + * granted provided that (1) source code retains these copyright, permission, + * and disclaimer notices, and (2) redistributions including binaries + * reproduce the notices in supporting documentation, and (3) all advertising + * materials mentioning features or use of this software display the following + * acknowledgement: ``This product includes software developed by the + * Computer Systems Laboratory at the University of Utah.'' + * + * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS + * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF + * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * CSL requests users of this software to return to csl-dist@cs.utah.edu any + * improvements that they make and grant CSL redistribution rights. + * + */ + +/* + * File: vm_param.h + * Author: Avadis Tevanian, Jr. + * Date: 1985 + * + * I386 machine dependent virtual memory parameters. + * Most of the declarations are preceeded by I386_ (or i386_) + * which is OK because only I386 specific code will be using + * them. + */ + +#ifndef _MACH_I386_VM_PARAM_H_ +#define _MACH_I386_VM_PARAM_H_ + +#define BYTE_SIZE 8 /* byte size in bits */ + +#define I386_PGBYTES 4096 /* bytes per 80386 page */ +#define I386_PGSHIFT 12 /* bitshift for pages */ + +#define PAGE_SIZE I386_PGBYTES +#define PAGE_SHIFT I386_PGSHIFT +#define PAGE_MASK (PAGE_SIZE - 1) + +#define I386_LPGBYTES 2*1024*1024 /* bytes per large page */ +#define I386_LPGSHIFT 21 /* bitshift for large pages */ +#define I386_LPGMASK (I386_LPGBYTES-1) + +/* + * Convert bytes to pages and convert pages to bytes. + * No rounding is used. + */ + +#define i386_btop(x) ((ppnum_t)((x) >> I386_PGSHIFT)) +#define machine_btop(x) i386_btop(x) +#define i386_ptob(x) (((pmap_paddr_t)(x)) << I386_PGSHIFT) + +/* + * Round off or truncate to the nearest page. These will work + * for either addresses or counts. (i.e. 1 byte rounds to 1 page + * bytes. + */ + +#define i386_round_page(x) ((((pmap_paddr_t)(x)) + I386_PGBYTES - 1) & \ + ~(I386_PGBYTES-1)) +#define i386_trunc_page(x) (((pmap_paddr_t)(x)) & ~(I386_PGBYTES-1)) + + + +#define VM_MIN_ADDRESS64 ((user_addr_t) 0x0000000000000000ULL) +/* + * default top of user stack... it grows down from here + */ +#define VM_USRSTACK64 ((user_addr_t) 0x00007FFF5FC00000ULL) +#define VM_DYLD64 ((user_addr_t) 0x00007FFF5FC00000ULL) +#define VM_LIB64_SHR_DATA ((user_addr_t) 0x00007FFF60000000ULL) +#define VM_LIB64_SHR_TEXT ((user_addr_t) 0x00007FFF80000000ULL) +/* + * the end of the usable user address space , for now about 47 bits. + * the 64 bit commpage is past the end of this + */ +#define VM_MAX_PAGE_ADDRESS ((user_addr_t) 0x00007FFFFFE00000ULL) +/* + * canonical end of user address space for limits checking + */ +#define VM_MAX_USER_PAGE_ADDRESS ((user_addr_t)0x00007FFFFFFFF000ULL) + + +/* system-wide values */ +#define MACH_VM_MIN_ADDRESS ((mach_vm_offset_t) 0) +#define MACH_VM_MAX_ADDRESS ((mach_vm_offset_t) VM_MAX_PAGE_ADDRESS) + +/* process-relative values (all 32-bit legacy only for now) */ +#define VM_MIN_ADDRESS ((vm_offset_t) 0) +#define VM_USRSTACK32 ((vm_offset_t) 0xC0000000) +#define VM_MAX_ADDRESS ((vm_offset_t) 0xFFE00000) + + + +#endif /* _MACH_I386_VM_PARAM_H_ */ diff --git a/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/i386/vm_types.h b/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/i386/vm_types.h new file mode 100644 index 000000000..2c38fa2d7 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/i386/vm_types.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2000-2006 Apple Computer, Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * @OSF_COPYRIGHT@ + */ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + */ + +/* + * File: vm_types.h + * Author: Avadis Tevanian, Jr. + * Date: 1985 + * + * Header file for VM data types. I386 version. + */ + +#ifndef _MACH_I386_VM_TYPES_H_ +#define _MACH_I386_VM_TYPES_H_ + +#ifndef ASSEMBLER + +#include +#include +#include + +/* + * natural_t and integer_t are Mach's legacy types for machine- + * independent integer types (unsigned, and signed, respectively). + * Their original purpose was to define other types in a machine/ + * compiler independent way. + * + * They also had an implicit "same size as pointer" characteristic + * to them (i.e. Mach's traditional types are very ILP32 or ILP64 + * centric). We support x86 ABIs that do not follow either of + * these models (specifically LP64). Therefore, we had to make a + * choice between making these types scale with pointers or stay + * tied to integers. Because their use is predominantly tied to + * to the size of an integer, we are keeping that association and + * breaking free from pointer size guarantees. + * + * New use of these types is discouraged. + */ +typedef __darwin_natural_t natural_t; +typedef int integer_t; + +/* + * A vm_offset_t is a type-neutral pointer, + * e.g. an offset into a virtual memory space. + */ +#ifdef __LP64__ +typedef uintptr_t vm_offset_t; +#else /* __LP64__ */ +typedef natural_t vm_offset_t; +#endif /* __LP64__ */ + +/* + * A vm_size_t is the proper type for e.g. + * expressing the difference between two + * vm_offset_t entities. + */ +#ifdef __LP64__ +typedef uintptr_t vm_size_t; +#else /* __LP64__ */ +typedef natural_t vm_size_t; +#endif /* __LP64__ */ + +/* + * This new type is independent of a particular vm map's + * implementation size - and represents appropriate types + * for all possible maps. This is used for interfaces + * where the size of the map is not known - or we don't + * want to have to distinguish. + */ +typedef uint64_t mach_vm_address_t; +typedef uint64_t mach_vm_offset_t; +typedef uint64_t mach_vm_size_t; + +typedef uint64_t vm_map_offset_t; +typedef uint64_t vm_map_address_t; +typedef uint64_t vm_map_size_t; + + +#endif /* ASSEMBLER */ + +/* + * If composing messages by hand (please do not) + */ +#define MACH_MSG_TYPE_INTEGER_T MACH_MSG_TYPE_INTEGER_32 + +#endif /* _MACH_I386_VM_TYPES_H_ */ diff --git a/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/machine.h b/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/machine.h new file mode 100644 index 000000000..5bb21e48b --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/machine.h @@ -0,0 +1,347 @@ +/* + * Copyright (c) 2000-2007 Apple Computer, Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* File: machine.h + * Author: Avadis Tevanian, Jr. + * Date: 1986 + * + * Machine independent machine abstraction. + */ + +#ifndef _MACH_MACHINE_H_ +#define _MACH_MACHINE_H_ + +#include +#include +#include + +typedef integer_t cpu_type_t; +typedef integer_t cpu_subtype_t; +typedef integer_t cpu_threadtype_t; + +#define CPU_STATE_MAX 4 + +#define CPU_STATE_USER 0 +#define CPU_STATE_SYSTEM 1 +#define CPU_STATE_IDLE 2 +#define CPU_STATE_NICE 3 + + + +/* + * Capability bits used in the definition of cpu_type. + */ +#define CPU_ARCH_MASK 0xff000000 /* mask for architecture bits */ +#define CPU_ARCH_ABI64 0x01000000 /* 64 bit ABI */ + +/* + * Machine types known by all. + */ + +#define CPU_TYPE_ANY ((cpu_type_t) -1) + +#define CPU_TYPE_VAX ((cpu_type_t) 1) +/* skip ((cpu_type_t) 2) */ +/* skip ((cpu_type_t) 3) */ +/* skip ((cpu_type_t) 4) */ +/* skip ((cpu_type_t) 5) */ +#define CPU_TYPE_MC680x0 ((cpu_type_t) 6) +#define CPU_TYPE_X86 ((cpu_type_t) 7) +#define CPU_TYPE_I386 CPU_TYPE_X86 /* compatibility */ +#define CPU_TYPE_X86_64 (CPU_TYPE_X86 | CPU_ARCH_ABI64) + +/* skip CPU_TYPE_MIPS ((cpu_type_t) 8) */ +/* skip ((cpu_type_t) 9) */ +#define CPU_TYPE_MC98000 ((cpu_type_t) 10) +#define CPU_TYPE_HPPA ((cpu_type_t) 11) +#define CPU_TYPE_ARM ((cpu_type_t) 12) +#define CPU_TYPE_MC88000 ((cpu_type_t) 13) +#define CPU_TYPE_SPARC ((cpu_type_t) 14) +#define CPU_TYPE_I860 ((cpu_type_t) 15) +/* skip CPU_TYPE_ALPHA ((cpu_type_t) 16) */ +/* skip ((cpu_type_t) 17) */ +#define CPU_TYPE_POWERPC ((cpu_type_t) 18) +#define CPU_TYPE_POWERPC64 (CPU_TYPE_POWERPC | CPU_ARCH_ABI64) + +/* + * Machine subtypes (these are defined here, instead of in a machine + * dependent directory, so that any program can get all definitions + * regardless of where is it compiled). + */ + +/* + * Capability bits used in the definition of cpu_subtype. + */ +#define CPU_SUBTYPE_MASK 0xff000000 /* mask for feature flags */ +#define CPU_SUBTYPE_LIB64 0x80000000 /* 64 bit libraries */ + + +/* + * Object files that are hand-crafted to run on any + * implementation of an architecture are tagged with + * CPU_SUBTYPE_MULTIPLE. This functions essentially the same as + * the "ALL" subtype of an architecture except that it allows us + * to easily find object files that may need to be modified + * whenever a new implementation of an architecture comes out. + * + * It is the responsibility of the implementor to make sure the + * software handles unsupported implementations elegantly. + */ +#define CPU_SUBTYPE_MULTIPLE ((cpu_subtype_t) -1) +#define CPU_SUBTYPE_LITTLE_ENDIAN ((cpu_subtype_t) 0) +#define CPU_SUBTYPE_BIG_ENDIAN ((cpu_subtype_t) 1) + +/* + * Machine threadtypes. + * This is none - not defined - for most machine types/subtypes. + */ +#define CPU_THREADTYPE_NONE ((cpu_threadtype_t) 0) + +/* + * VAX subtypes (these do *not* necessary conform to the actual cpu + * ID assigned by DEC available via the SID register). + */ + +#define CPU_SUBTYPE_VAX_ALL ((cpu_subtype_t) 0) +#define CPU_SUBTYPE_VAX780 ((cpu_subtype_t) 1) +#define CPU_SUBTYPE_VAX785 ((cpu_subtype_t) 2) +#define CPU_SUBTYPE_VAX750 ((cpu_subtype_t) 3) +#define CPU_SUBTYPE_VAX730 ((cpu_subtype_t) 4) +#define CPU_SUBTYPE_UVAXI ((cpu_subtype_t) 5) +#define CPU_SUBTYPE_UVAXII ((cpu_subtype_t) 6) +#define CPU_SUBTYPE_VAX8200 ((cpu_subtype_t) 7) +#define CPU_SUBTYPE_VAX8500 ((cpu_subtype_t) 8) +#define CPU_SUBTYPE_VAX8600 ((cpu_subtype_t) 9) +#define CPU_SUBTYPE_VAX8650 ((cpu_subtype_t) 10) +#define CPU_SUBTYPE_VAX8800 ((cpu_subtype_t) 11) +#define CPU_SUBTYPE_UVAXIII ((cpu_subtype_t) 12) + +/* + * 680x0 subtypes + * + * The subtype definitions here are unusual for historical reasons. + * NeXT used to consider 68030 code as generic 68000 code. For + * backwards compatability: + * + * CPU_SUBTYPE_MC68030 symbol has been preserved for source code + * compatability. + * + * CPU_SUBTYPE_MC680x0_ALL has been defined to be the same + * subtype as CPU_SUBTYPE_MC68030 for binary comatability. + * + * CPU_SUBTYPE_MC68030_ONLY has been added to allow new object + * files to be tagged as containing 68030-specific instructions. + */ + +#define CPU_SUBTYPE_MC680x0_ALL ((cpu_subtype_t) 1) +#define CPU_SUBTYPE_MC68030 ((cpu_subtype_t) 1) /* compat */ +#define CPU_SUBTYPE_MC68040 ((cpu_subtype_t) 2) +#define CPU_SUBTYPE_MC68030_ONLY ((cpu_subtype_t) 3) + +/* + * I386 subtypes + */ + +#define CPU_SUBTYPE_INTEL(f, m) ((cpu_subtype_t) (f) + ((m) << 4)) + +#define CPU_SUBTYPE_I386_ALL CPU_SUBTYPE_INTEL(3, 0) +#define CPU_SUBTYPE_386 CPU_SUBTYPE_INTEL(3, 0) +#define CPU_SUBTYPE_486 CPU_SUBTYPE_INTEL(4, 0) +#define CPU_SUBTYPE_486SX CPU_SUBTYPE_INTEL(4, 8) // 8 << 4 = 128 +#define CPU_SUBTYPE_586 CPU_SUBTYPE_INTEL(5, 0) +#define CPU_SUBTYPE_PENT CPU_SUBTYPE_INTEL(5, 0) +#define CPU_SUBTYPE_PENTPRO CPU_SUBTYPE_INTEL(6, 1) +#define CPU_SUBTYPE_PENTII_M3 CPU_SUBTYPE_INTEL(6, 3) +#define CPU_SUBTYPE_PENTII_M5 CPU_SUBTYPE_INTEL(6, 5) +#define CPU_SUBTYPE_CELERON CPU_SUBTYPE_INTEL(7, 6) +#define CPU_SUBTYPE_CELERON_MOBILE CPU_SUBTYPE_INTEL(7, 7) +#define CPU_SUBTYPE_PENTIUM_3 CPU_SUBTYPE_INTEL(8, 0) +#define CPU_SUBTYPE_PENTIUM_3_M CPU_SUBTYPE_INTEL(8, 1) +#define CPU_SUBTYPE_PENTIUM_3_XEON CPU_SUBTYPE_INTEL(8, 2) +#define CPU_SUBTYPE_PENTIUM_M CPU_SUBTYPE_INTEL(9, 0) +#define CPU_SUBTYPE_PENTIUM_4 CPU_SUBTYPE_INTEL(10, 0) +#define CPU_SUBTYPE_PENTIUM_4_M CPU_SUBTYPE_INTEL(10, 1) +#define CPU_SUBTYPE_ITANIUM CPU_SUBTYPE_INTEL(11, 0) +#define CPU_SUBTYPE_ITANIUM_2 CPU_SUBTYPE_INTEL(11, 1) +#define CPU_SUBTYPE_XEON CPU_SUBTYPE_INTEL(12, 0) +#define CPU_SUBTYPE_XEON_MP CPU_SUBTYPE_INTEL(12, 1) + +#define CPU_SUBTYPE_INTEL_FAMILY(x) ((x) & 15) +#define CPU_SUBTYPE_INTEL_FAMILY_MAX 15 + +#define CPU_SUBTYPE_INTEL_MODEL(x) ((x) >> 4) +#define CPU_SUBTYPE_INTEL_MODEL_ALL 0 + +/* + * X86 subtypes. + */ + +#define CPU_SUBTYPE_X86_ALL ((cpu_subtype_t)3) +#define CPU_SUBTYPE_X86_64_ALL ((cpu_subtype_t)3) +#define CPU_SUBTYPE_X86_ARCH1 ((cpu_subtype_t)4) +#define CPU_SUBTYPE_X86_64_H ((cpu_subtype_t)8) /* Haswell feature subset */ + + +#define CPU_THREADTYPE_INTEL_HTT ((cpu_threadtype_t) 1) + +/* + * Mips subtypes. + */ + +#define CPU_SUBTYPE_MIPS_ALL ((cpu_subtype_t) 0) +#define CPU_SUBTYPE_MIPS_R2300 ((cpu_subtype_t) 1) +#define CPU_SUBTYPE_MIPS_R2600 ((cpu_subtype_t) 2) +#define CPU_SUBTYPE_MIPS_R2800 ((cpu_subtype_t) 3) +#define CPU_SUBTYPE_MIPS_R2000a ((cpu_subtype_t) 4) /* pmax */ +#define CPU_SUBTYPE_MIPS_R2000 ((cpu_subtype_t) 5) +#define CPU_SUBTYPE_MIPS_R3000a ((cpu_subtype_t) 6) /* 3max */ +#define CPU_SUBTYPE_MIPS_R3000 ((cpu_subtype_t) 7) + +/* + * MC98000 (PowerPC) subtypes + */ +#define CPU_SUBTYPE_MC98000_ALL ((cpu_subtype_t) 0) +#define CPU_SUBTYPE_MC98601 ((cpu_subtype_t) 1) + +/* + * HPPA subtypes for Hewlett-Packard HP-PA family of + * risc processors. Port by NeXT to 700 series. + */ + +#define CPU_SUBTYPE_HPPA_ALL ((cpu_subtype_t) 0) +#define CPU_SUBTYPE_HPPA_7100 ((cpu_subtype_t) 0) /* compat */ +#define CPU_SUBTYPE_HPPA_7100LC ((cpu_subtype_t) 1) + +/* + * MC88000 subtypes. + */ +#define CPU_SUBTYPE_MC88000_ALL ((cpu_subtype_t) 0) +#define CPU_SUBTYPE_MC88100 ((cpu_subtype_t) 1) +#define CPU_SUBTYPE_MC88110 ((cpu_subtype_t) 2) + +/* + * SPARC subtypes + */ +#define CPU_SUBTYPE_SPARC_ALL ((cpu_subtype_t) 0) + +/* + * I860 subtypes + */ +#define CPU_SUBTYPE_I860_ALL ((cpu_subtype_t) 0) +#define CPU_SUBTYPE_I860_860 ((cpu_subtype_t) 1) + +/* + * PowerPC subtypes + */ +#define CPU_SUBTYPE_POWERPC_ALL ((cpu_subtype_t) 0) +#define CPU_SUBTYPE_POWERPC_601 ((cpu_subtype_t) 1) +#define CPU_SUBTYPE_POWERPC_602 ((cpu_subtype_t) 2) +#define CPU_SUBTYPE_POWERPC_603 ((cpu_subtype_t) 3) +#define CPU_SUBTYPE_POWERPC_603e ((cpu_subtype_t) 4) +#define CPU_SUBTYPE_POWERPC_603ev ((cpu_subtype_t) 5) +#define CPU_SUBTYPE_POWERPC_604 ((cpu_subtype_t) 6) +#define CPU_SUBTYPE_POWERPC_604e ((cpu_subtype_t) 7) +#define CPU_SUBTYPE_POWERPC_620 ((cpu_subtype_t) 8) +#define CPU_SUBTYPE_POWERPC_750 ((cpu_subtype_t) 9) +#define CPU_SUBTYPE_POWERPC_7400 ((cpu_subtype_t) 10) +#define CPU_SUBTYPE_POWERPC_7450 ((cpu_subtype_t) 11) +#define CPU_SUBTYPE_POWERPC_970 ((cpu_subtype_t) 100) + +/* + * ARM subtypes + */ +#define CPU_SUBTYPE_ARM_ALL ((cpu_subtype_t) 0) +#define CPU_SUBTYPE_ARM_V4T ((cpu_subtype_t) 5) +#define CPU_SUBTYPE_ARM_V6 ((cpu_subtype_t) 6) +#define CPU_SUBTYPE_ARM_V5TEJ ((cpu_subtype_t) 7) +#define CPU_SUBTYPE_ARM_XSCALE ((cpu_subtype_t) 8) +#define CPU_SUBTYPE_ARM_V7 ((cpu_subtype_t) 9) + +/* + * CPU families (sysctl hw.cpufamily) + * + * These are meant to identify the CPU's marketing name - an + * application can map these to (possibly) localized strings. + * NB: the encodings of the CPU families are intentionally arbitrary. + * There is no ordering, and you should never try to deduce whether + * or not some feature is available based on the family. + * Use feature flags (eg, hw.optional.altivec) to test for optional + * functionality. + */ +#define CPUFAMILY_UNKNOWN 0 +#define CPUFAMILY_POWERPC_G3 0xcee41549 +#define CPUFAMILY_POWERPC_G4 0x77c184ae +#define CPUFAMILY_POWERPC_G5 0xed76d8aa +#define CPUFAMILY_INTEL_6_13 0xaa33392b +#define CPUFAMILY_INTEL_YONAH 0x73d67300 +#define CPUFAMILY_INTEL_MEROM 0x426f69ef +#define CPUFAMILY_INTEL_PENRYN 0x78ea4fbc +#define CPUFAMILY_INTEL_NEHALEM 0x6b5a4cd2 +#define CPUFAMILY_INTEL_WESTMERE 0x573b5eec +#define CPUFAMILY_INTEL_SANDYBRIDGE 0x5490b78c +#define CPUFAMILY_ARM_9 0xe73283ae +#define CPUFAMILY_ARM_11 0x8ff620d8 +#define CPUFAMILY_ARM_XSCALE 0x53b005f5 +#define CPUFAMILY_ARM_13 0x0cc90e64 +#define CPUFAMILY_ARM_14 0x96077ef1 + +/* The following synonyms are deprecated: */ +#define CPUFAMILY_INTEL_6_14 CPUFAMILY_INTEL_YONAH +#define CPUFAMILY_INTEL_6_15 CPUFAMILY_INTEL_MEROM +#define CPUFAMILY_INTEL_6_23 CPUFAMILY_INTEL_PENRYN +#define CPUFAMILY_INTEL_6_26 CPUFAMILY_INTEL_NEHALEM + +#define CPUFAMILY_INTEL_CORE CPUFAMILY_INTEL_YONAH +#define CPUFAMILY_INTEL_CORE2 CPUFAMILY_INTEL_MEROM + + +#endif /* _MACH_MACHINE_H_ */ diff --git a/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/machine/boolean.h b/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/machine/boolean.h new file mode 100644 index 000000000..ffdc2390a --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/machine/boolean.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2000-2007 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifndef _MACH_MACHINE_BOOLEAN_H_ +#define _MACH_MACHINE_BOOLEAN_H_ + +#if defined (__i386__) || defined(__x86_64__) +#include "mach/i386/boolean.h" +#elif defined (__arm__) +#include "mach/arm/boolean.h" +#else +#error architecture not supported +#endif + +#endif /* _MACH_MACHINE_BOOLEAN_H_ */ diff --git a/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/machine/thread_state.h b/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/machine/thread_state.h new file mode 100644 index 000000000..1547acac7 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/machine/thread_state.h @@ -0,0 +1,9 @@ +/* + * This file is a stub with the bare minimum needed to make things work. + */ +#ifndef _MACH_MACHINE_THREAD_STATE_H_ +#define _MACH_MACHINE_THREAD_STATE_H_ + +#define THREAD_STATE_MAX 1 + +#endif /* _MACH_MACHINE_THREAD_STATE_H_ */ diff --git a/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/machine/thread_status.h b/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/machine/thread_status.h new file mode 100644 index 000000000..d1ab56ad5 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/machine/thread_status.h @@ -0,0 +1 @@ +/* This file intentionally left blank */ diff --git a/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/machine/vm_types.h b/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/machine/vm_types.h new file mode 100644 index 000000000..8ccd24be5 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/machine/vm_types.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2000-2007 Apple Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ + +#ifndef _MACH_MACHINE_VM_TYPES_H_ +#define _MACH_MACHINE_VM_TYPES_H_ + +#if defined (__i386__) || defined(__x86_64__) +#include "mach/i386/vm_types.h" +#elif defined (__arm__) +#include "mach/arm/vm_types.h" +#else +#error architecture not supported +#endif + +#endif /* _MACH_MACHINE_VM_TYPES_H_ */ diff --git a/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/thread_status.h b/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/thread_status.h new file mode 100644 index 000000000..aead09bf9 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/thread_status.h @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * @OSF_COPYRIGHT@ + */ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + */ +/* + * File: mach/thread_status.h + * Author: Avadis Tevanian, Jr. + * + * This file contains the structure definitions for the user-visible + * thread state. This thread state is examined with the thread_get_state + * kernel call and may be changed with the thread_set_state kernel call. + * + */ + +#ifndef _MACH_THREAD_STATUS_H_ +#define _MACH_THREAD_STATUS_H_ + +/* + * The actual structure that comprises the thread state is defined + * in the machine dependent module. + */ +#include +#include +#include + +/* + * Generic definition for machine-dependent thread status. + */ + +typedef natural_t *thread_state_t; /* Variable-length array */ + +/* THREAD_STATE_MAX is now defined in */ +typedef natural_t thread_state_data_t[THREAD_STATE_MAX]; + +#define THREAD_STATE_FLAVOR_LIST 0 /* List of valid flavors */ +#define THREAD_STATE_FLAVOR_LIST_NEW 128 + +typedef int thread_state_flavor_t; +typedef thread_state_flavor_t *thread_state_flavor_array_t; + +#endif /* _MACH_THREAD_STATUS_H_ */ diff --git a/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/vm_prot.h b/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/vm_prot.h new file mode 100644 index 000000000..07c2114e5 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/mac_headers/mach/vm_prot.h @@ -0,0 +1,140 @@ +/* + * Copyright (c) 2000-2002 Apple Computer, Inc. All rights reserved. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. The rights granted to you under the License + * may not be used to create, or enable the creation or redistribution of, + * unlawful or unlicensed copies of an Apple operating system, or to + * circumvent, violate, or enable the circumvention or violation of, any + * terms of an Apple operating system software license agreement. + * + * Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ + */ +/* + * @OSF_COPYRIGHT@ + */ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + */ +/* + * File: mach/vm_prot.h + * Author: Avadis Tevanian, Jr., Michael Wayne Young + * + * Virtual memory protection definitions. + * + */ + +#ifndef _MACH_VM_PROT_H_ +#define _MACH_VM_PROT_H_ + +/* + * Types defined: + * + * vm_prot_t VM protection values. + */ + +typedef int vm_prot_t; + +/* + * Protection values, defined as bits within the vm_prot_t type + */ + +#define VM_PROT_NONE ((vm_prot_t) 0x00) + +#define VM_PROT_READ ((vm_prot_t) 0x01) /* read permission */ +#define VM_PROT_WRITE ((vm_prot_t) 0x02) /* write permission */ +#define VM_PROT_EXECUTE ((vm_prot_t) 0x04) /* execute permission */ + +/* + * The default protection for newly-created virtual memory + */ + +#define VM_PROT_DEFAULT (VM_PROT_READ|VM_PROT_WRITE) + +/* + * The maximum privileges possible, for parameter checking. + */ + +#define VM_PROT_ALL (VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE) + +/* + * An invalid protection value. + * Used only by memory_object_lock_request to indicate no change + * to page locks. Using -1 here is a bad idea because it + * looks like VM_PROT_ALL and then some. + */ + +#define VM_PROT_NO_CHANGE ((vm_prot_t) 0x08) + +/* + * When a caller finds that he cannot obtain write permission on a + * mapped entry, the following flag can be used. The entry will + * be made "needs copy" effectively copying the object (using COW), + * and write permission will be added to the maximum protections + * for the associated entry. + */ + +#define VM_PROT_COPY ((vm_prot_t) 0x10) + + +/* + * Another invalid protection value. + * Used only by memory_object_data_request upon an object + * which has specified a copy_call copy strategy. It is used + * when the kernel wants a page belonging to a copy of the + * object, and is only asking the object as a result of + * following a shadow chain. This solves the race between pages + * being pushed up by the memory manager and the kernel + * walking down the shadow chain. + */ + +#define VM_PROT_WANTS_COPY ((vm_prot_t) 0x10) + + +/* + * Another invalid protection value. + * Indicates that the other protection bits are to be applied as a mask + * against the actual protection bits of the map entry. + */ +#define VM_PROT_IS_MASK ((vm_prot_t) 0x40) + +#endif /* _MACH_VM_PROT_H_ */ diff --git a/shared/sentry/external/breakpad/src/third_party/musl/COPYRIGHT b/shared/sentry/external/breakpad/src/third_party/musl/COPYRIGHT new file mode 100644 index 000000000..f0ee3b78d --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/musl/COPYRIGHT @@ -0,0 +1,163 @@ +musl as a whole is licensed under the following standard MIT license: + +---------------------------------------------------------------------- +Copyright © 2005-2014 Rich Felker, et al. + +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. +---------------------------------------------------------------------- + +Authors/contributors include: + +Alex Dowad +Alexander Monakov +Anthony G. Basile +Arvid Picciani +Bobby Bingham +Boris Brezillon +Brent Cook +Chris Spiegel +Clément Vasseur +Daniel Micay +Denys Vlasenko +Emil Renner Berthing +Felix Fietkau +Felix Janda +Gianluca Anzolin +Hauke Mehrtens +Hiltjo Posthuma +Isaac Dunham +Jaydeep Patil +Jens Gustedt +Jeremy Huntwork +Jo-Philipp Wich +Joakim Sindholt +John Spencer +Josiah Worcester +Justin Cormack +Khem Raj +Kylie McClain +Luca Barbato +Luka Perkov +M Farkas-Dyck (Strake) +Mahesh Bodapati +Michael Forney +Natanael Copa +Nicholas J. Kain +orc +Pascal Cuoq +Petr Hosek +Pierre Carrier +Rich Felker +Richard Pennington +Shiz +sin +Solar Designer +Stefan Kristiansson +Szabolcs Nagy +Timo Teräs +Trutz Behn +Valentin Ochs +William Haddon + +Portions of this software are derived from third-party works licensed +under terms compatible with the above MIT license: + +The TRE regular expression implementation (src/regex/reg* and +src/regex/tre*) is Copyright © 2001-2008 Ville Laurikari and licensed +under a 2-clause BSD license (license text in the source files). The +included version has been heavily modified by Rich Felker in 2012, in +the interests of size, simplicity, and namespace cleanliness. + +Much of the math library code (src/math/* and src/complex/*) is +Copyright © 1993,2004 Sun Microsystems or +Copyright © 2003-2011 David Schultz or +Copyright © 2003-2009 Steven G. Kargl or +Copyright © 2003-2009 Bruce D. Evans or +Copyright © 2008 Stephen L. Moshier +and labelled as such in comments in the individual source files. All +have been licensed under extremely permissive terms. + +The ARM memcpy code (src/string/arm/memcpy_el.S) is Copyright © 2008 +The Android Open Source Project and is licensed under a two-clause BSD +license. It was taken from Bionic libc, used on Android. + +The implementation of DES for crypt (src/crypt/crypt_des.c) is +Copyright © 1994 David Burren. It is licensed under a BSD license. + +The implementation of blowfish crypt (src/crypt/crypt_blowfish.c) was +originally written by Solar Designer and placed into the public +domain. The code also comes with a fallback permissive license for use +in jurisdictions that may not recognize the public domain. + +The smoothsort implementation (src/stdlib/qsort.c) is Copyright © 2011 +Valentin Ochs and is licensed under an MIT-style license. + +The BSD PRNG implementation (src/prng/random.c) and XSI search API +(src/search/*.c) functions are Copyright © 2011 Szabolcs Nagy and +licensed under following terms: "Permission to use, copy, modify, +and/or distribute this code for any purpose with or without fee is +hereby granted. There is no warranty." + +The x86_64 port was written by Nicholas J. Kain and is licensed under +the standard MIT terms. + +The mips and microblaze ports were originally written by Richard +Pennington for use in the ellcc project. The original code was adapted +by Rich Felker for build system and code conventions during upstream +integration. It is licensed under the standard MIT terms. + +The mips64 port was contributed by Imagination Technologies and is +licensed under the standard MIT terms. + +The powerpc port was also originally written by Richard Pennington, +and later supplemented and integrated by John Spencer. It is licensed +under the standard MIT terms. + +All other files which have no copyright comments are original works +produced specifically for use as part of this library, written either +by Rich Felker, the main author of the library, or by one or more +contibutors listed above. Details on authorship of individual files +can be found in the git version control history of the project. The +omission of copyright and license comments in each file is in the +interest of source tree size. + +In addition, permission is hereby granted for all public header files +(include/* and arch/*/bits/*) and crt files intended to be linked into +applications (crt/*, ldso/dlstart.c, and arch/*/crt_arch.h) to omit +the copyright notice and permission notice otherwise required by the +license, and to use these files without any requirement of +attribution. These files include substantial contributions from: + +Bobby Bingham +John Spencer +Nicholas J. Kain +Rich Felker +Richard Pennington +Stefan Kristiansson +Szabolcs Nagy + +all of whom have explicitly granted such permission. + +This file previously contained text expressing a belief that most of +the files covered by the above exception were sufficiently trivial not +to be subject to copyright, resulting in confusion over whether it +negated the permissions granted in the license. In the spirit of +permissive licensing, and of not having licensing issues being an +obstacle to adoption, that text has been removed. diff --git a/shared/sentry/external/breakpad/src/third_party/musl/README b/shared/sentry/external/breakpad/src/third_party/musl/README new file mode 100644 index 000000000..a30eb1127 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/musl/README @@ -0,0 +1,23 @@ + + musl libc + +musl, pronounced like the word "mussel", is an MIT-licensed +implementation of the standard C library targetting the Linux syscall +API, suitable for use in a wide range of deployment environments. musl +offers efficient static and dynamic linking support, lightweight code +and low runtime overhead, strong fail-safe guarantees under correct +usage, and correctness in the sense of standards conformance and +safety. musl is built on the principle that these goals are best +achieved through simple code that is easy to understand and maintain. + +The 1.1 release series for musl features coverage for all interfaces +defined in ISO C99 and POSIX 2008 base, along with a number of +non-standardized interfaces for compatibility with Linux, BSD, and +glibc functionality. + +For basic installation instructions, see the included INSTALL file. +Information on full musl-targeted compiler toolchains, system +bootstrapping, and Linux distributions built on musl can be found on +the project website: + + http://www.musl-libc.org/ diff --git a/shared/sentry/external/breakpad/src/third_party/musl/README.breakpad b/shared/sentry/external/breakpad/src/third_party/musl/README.breakpad new file mode 100644 index 000000000..f500c4359 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/musl/README.breakpad @@ -0,0 +1,3 @@ +This directory contains the elf header from +https://git.musl-libc.org/cgit/musl/tree/ +that is required to get ELF working in dump_syms on Mac OS X. diff --git a/shared/sentry/external/breakpad/src/third_party/musl/VERSION b/shared/sentry/external/breakpad/src/third_party/musl/VERSION new file mode 100644 index 000000000..e9bc14996 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/musl/VERSION @@ -0,0 +1 @@ +1.1.14 diff --git a/shared/sentry/external/breakpad/src/third_party/musl/include/elf.h b/shared/sentry/external/breakpad/src/third_party/musl/include/elf.h new file mode 100644 index 000000000..8b3cd3ed3 --- /dev/null +++ b/shared/sentry/external/breakpad/src/third_party/musl/include/elf.h @@ -0,0 +1,2827 @@ +#ifndef _ELF_H +#define _ELF_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +typedef uint16_t Elf32_Half; +typedef uint16_t Elf64_Half; + +typedef uint32_t Elf32_Word; +typedef int32_t Elf32_Sword; +typedef uint32_t Elf64_Word; +typedef int32_t Elf64_Sword; + +typedef uint64_t Elf32_Xword; +typedef int64_t Elf32_Sxword; +typedef uint64_t Elf64_Xword; +typedef int64_t Elf64_Sxword; + +typedef uint32_t Elf32_Addr; +typedef uint64_t Elf64_Addr; + +typedef uint32_t Elf32_Off; +typedef uint64_t Elf64_Off; + +typedef uint16_t Elf32_Section; +typedef uint16_t Elf64_Section; + +typedef Elf32_Half Elf32_Versym; +typedef Elf64_Half Elf64_Versym; + +#define EI_NIDENT (16) + +typedef struct { + unsigned char e_ident[EI_NIDENT]; + Elf32_Half e_type; + Elf32_Half e_machine; + Elf32_Word e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; +} Elf32_Ehdr; + +typedef struct { + unsigned char e_ident[EI_NIDENT]; + Elf64_Half e_type; + Elf64_Half e_machine; + Elf64_Word e_version; + Elf64_Addr e_entry; + Elf64_Off e_phoff; + Elf64_Off e_shoff; + Elf64_Word e_flags; + Elf64_Half e_ehsize; + Elf64_Half e_phentsize; + Elf64_Half e_phnum; + Elf64_Half e_shentsize; + Elf64_Half e_shnum; + Elf64_Half e_shstrndx; +} Elf64_Ehdr; + +#define EI_MAG0 0 +#define ELFMAG0 0x7f + +#define EI_MAG1 1 +#define ELFMAG1 'E' + +#define EI_MAG2 2 +#define ELFMAG2 'L' + +#define EI_MAG3 3 +#define ELFMAG3 'F' + + +#define ELFMAG "\177ELF" +#define SELFMAG 4 + +#define EI_CLASS 4 +#define ELFCLASSNONE 0 +#define ELFCLASS32 1 +#define ELFCLASS64 2 +#define ELFCLASSNUM 3 + +#define EI_DATA 5 +#define ELFDATANONE 0 +#define ELFDATA2LSB 1 +#define ELFDATA2MSB 2 +#define ELFDATANUM 3 + +#define EI_VERSION 6 + + +#define EI_OSABI 7 +#define ELFOSABI_NONE 0 +#define ELFOSABI_SYSV 0 +#define ELFOSABI_HPUX 1 +#define ELFOSABI_NETBSD 2 +#define ELFOSABI_LINUX 3 +#define ELFOSABI_GNU 3 +#define ELFOSABI_SOLARIS 6 +#define ELFOSABI_AIX 7 +#define ELFOSABI_IRIX 8 +#define ELFOSABI_FREEBSD 9 +#define ELFOSABI_TRU64 10 +#define ELFOSABI_MODESTO 11 +#define ELFOSABI_OPENBSD 12 +#define ELFOSABI_ARM 97 +#define ELFOSABI_STANDALONE 255 + +#define EI_ABIVERSION 8 + +#define EI_PAD 9 + + + +#define ET_NONE 0 +#define ET_REL 1 +#define ET_EXEC 2 +#define ET_DYN 3 +#define ET_CORE 4 +#define ET_NUM 5 +#define ET_LOOS 0xfe00 +#define ET_HIOS 0xfeff +#define ET_LOPROC 0xff00 +#define ET_HIPROC 0xffff + + + +#define EM_NONE 0 +#define EM_M32 1 +#define EM_SPARC 2 +#define EM_386 3 +#define EM_68K 4 +#define EM_88K 5 +#define EM_860 7 +#define EM_MIPS 8 +#define EM_S370 9 +#define EM_MIPS_RS3_LE 10 + +#define EM_PARISC 15 +#define EM_VPP500 17 +#define EM_SPARC32PLUS 18 +#define EM_960 19 +#define EM_PPC 20 +#define EM_PPC64 21 +#define EM_S390 22 + +#define EM_V800 36 +#define EM_FR20 37 +#define EM_RH32 38 +#define EM_RCE 39 +#define EM_ARM 40 +#define EM_FAKE_ALPHA 41 +#define EM_SH 42 +#define EM_SPARCV9 43 +#define EM_TRICORE 44 +#define EM_ARC 45 +#define EM_H8_300 46 +#define EM_H8_300H 47 +#define EM_H8S 48 +#define EM_H8_500 49 +#define EM_IA_64 50 +#define EM_MIPS_X 51 +#define EM_COLDFIRE 52 +#define EM_68HC12 53 +#define EM_MMA 54 +#define EM_PCP 55 +#define EM_NCPU 56 +#define EM_NDR1 57 +#define EM_STARCORE 58 +#define EM_ME16 59 +#define EM_ST100 60 +#define EM_TINYJ 61 +#define EM_X86_64 62 +#define EM_PDSP 63 + +#define EM_FX66 66 +#define EM_ST9PLUS 67 +#define EM_ST7 68 +#define EM_68HC16 69 +#define EM_68HC11 70 +#define EM_68HC08 71 +#define EM_68HC05 72 +#define EM_SVX 73 +#define EM_ST19 74 +#define EM_VAX 75 +#define EM_CRIS 76 +#define EM_JAVELIN 77 +#define EM_FIREPATH 78 +#define EM_ZSP 79 +#define EM_MMIX 80 +#define EM_HUANY 81 +#define EM_PRISM 82 +#define EM_AVR 83 +#define EM_FR30 84 +#define EM_D10V 85 +#define EM_D30V 86 +#define EM_V850 87 +#define EM_M32R 88 +#define EM_MN10300 89 +#define EM_MN10200 90 +#define EM_PJ 91 +#define EM_OR1K 92 +#define EM_ARC_A5 93 +#define EM_XTENSA 94 +#define EM_AARCH64 183 +#define EM_TILEPRO 188 +#define EM_MICROBLAZE 189 +#define EM_TILEGX 191 +#define EM_NUM 192 +#define EM_ALPHA 0x9026 + +#define EV_NONE 0 +#define EV_CURRENT 1 +#define EV_NUM 2 + +typedef struct { + Elf32_Word sh_name; + Elf32_Word sh_type; + Elf32_Word sh_flags; + Elf32_Addr sh_addr; + Elf32_Off sh_offset; + Elf32_Word sh_size; + Elf32_Word sh_link; + Elf32_Word sh_info; + Elf32_Word sh_addralign; + Elf32_Word sh_entsize; +} Elf32_Shdr; + +typedef struct { + Elf64_Word sh_name; + Elf64_Word sh_type; + Elf64_Xword sh_flags; + Elf64_Addr sh_addr; + Elf64_Off sh_offset; + Elf64_Xword sh_size; + Elf64_Word sh_link; + Elf64_Word sh_info; + Elf64_Xword sh_addralign; + Elf64_Xword sh_entsize; +} Elf64_Shdr; + + + +#define SHN_UNDEF 0 +#define SHN_LORESERVE 0xff00 +#define SHN_LOPROC 0xff00 +#define SHN_BEFORE 0xff00 + +#define SHN_AFTER 0xff01 + +#define SHN_HIPROC 0xff1f +#define SHN_LOOS 0xff20 +#define SHN_HIOS 0xff3f +#define SHN_ABS 0xfff1 +#define SHN_COMMON 0xfff2 +#define SHN_XINDEX 0xffff +#define SHN_HIRESERVE 0xffff + + + +#define SHT_NULL 0 +#define SHT_PROGBITS 1 +#define SHT_SYMTAB 2 +#define SHT_STRTAB 3 +#define SHT_RELA 4 +#define SHT_HASH 5 +#define SHT_DYNAMIC 6 +#define SHT_NOTE 7 +#define SHT_NOBITS 8 +#define SHT_REL 9 +#define SHT_SHLIB 10 +#define SHT_DYNSYM 11 +#define SHT_INIT_ARRAY 14 +#define SHT_FINI_ARRAY 15 +#define SHT_PREINIT_ARRAY 16 +#define SHT_GROUP 17 +#define SHT_SYMTAB_SHNDX 18 +#define SHT_NUM 19 +#define SHT_LOOS 0x60000000 +#define SHT_GNU_ATTRIBUTES 0x6ffffff5 +#define SHT_GNU_HASH 0x6ffffff6 +#define SHT_GNU_LIBLIST 0x6ffffff7 +#define SHT_CHECKSUM 0x6ffffff8 +#define SHT_LOSUNW 0x6ffffffa +#define SHT_SUNW_move 0x6ffffffa +#define SHT_SUNW_COMDAT 0x6ffffffb +#define SHT_SUNW_syminfo 0x6ffffffc +#define SHT_GNU_verdef 0x6ffffffd +#define SHT_GNU_verneed 0x6ffffffe +#define SHT_GNU_versym 0x6fffffff +#define SHT_HISUNW 0x6fffffff +#define SHT_HIOS 0x6fffffff +#define SHT_LOPROC 0x70000000 +#define SHT_HIPROC 0x7fffffff +#define SHT_LOUSER 0x80000000 +#define SHT_HIUSER 0x8fffffff + +#define SHF_WRITE (1 << 0) +#define SHF_ALLOC (1 << 1) +#define SHF_EXECINSTR (1 << 2) +#define SHF_MERGE (1 << 4) +#define SHF_STRINGS (1 << 5) +#define SHF_INFO_LINK (1 << 6) +#define SHF_LINK_ORDER (1 << 7) +#define SHF_OS_NONCONFORMING (1 << 8) + +#define SHF_GROUP (1 << 9) +#define SHF_TLS (1 << 10) +#define SHF_MASKOS 0x0ff00000 +#define SHF_MASKPROC 0xf0000000 +#define SHF_ORDERED (1 << 30) +#define SHF_EXCLUDE (1U << 31) + +#define GRP_COMDAT 0x1 + +typedef struct { + Elf32_Word st_name; + Elf32_Addr st_value; + Elf32_Word st_size; + unsigned char st_info; + unsigned char st_other; + Elf32_Section st_shndx; +} Elf32_Sym; + +typedef struct { + Elf64_Word st_name; + unsigned char st_info; + unsigned char st_other; + Elf64_Section st_shndx; + Elf64_Addr st_value; + Elf64_Xword st_size; +} Elf64_Sym; + +typedef struct { + Elf32_Half si_boundto; + Elf32_Half si_flags; +} Elf32_Syminfo; + +typedef struct { + Elf64_Half si_boundto; + Elf64_Half si_flags; +} Elf64_Syminfo; + +#define SYMINFO_BT_SELF 0xffff +#define SYMINFO_BT_PARENT 0xfffe +#define SYMINFO_BT_LOWRESERVE 0xff00 + +#define SYMINFO_FLG_DIRECT 0x0001 +#define SYMINFO_FLG_PASSTHRU 0x0002 +#define SYMINFO_FLG_COPY 0x0004 +#define SYMINFO_FLG_LAZYLOAD 0x0008 + +#define SYMINFO_NONE 0 +#define SYMINFO_CURRENT 1 +#define SYMINFO_NUM 2 + +#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) +#define ELF32_ST_TYPE(val) ((val) & 0xf) +#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) + +#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) +#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) +#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) + +#define STB_LOCAL 0 +#define STB_GLOBAL 1 +#define STB_WEAK 2 +#define STB_NUM 3 +#define STB_LOOS 10 +#define STB_GNU_UNIQUE 10 +#define STB_HIOS 12 +#define STB_LOPROC 13 +#define STB_HIPROC 15 + +#define STT_NOTYPE 0 +#define STT_OBJECT 1 +#define STT_FUNC 2 +#define STT_SECTION 3 +#define STT_FILE 4 +#define STT_COMMON 5 +#define STT_TLS 6 +#define STT_NUM 7 +#define STT_LOOS 10 +#define STT_GNU_IFUNC 10 +#define STT_HIOS 12 +#define STT_LOPROC 13 +#define STT_HIPROC 15 + +#define STN_UNDEF 0 + +#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) +#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) + +#define STV_DEFAULT 0 +#define STV_INTERNAL 1 +#define STV_HIDDEN 2 +#define STV_PROTECTED 3 + + + + +typedef struct +{ + Elf32_Addr r_offset; + Elf32_Word r_info; +} Elf32_Rel; + +typedef struct { + Elf64_Addr r_offset; + Elf64_Xword r_info; +} Elf64_Rel; + + + +typedef struct { + Elf32_Addr r_offset; + Elf32_Word r_info; + Elf32_Sword r_addend; +} Elf32_Rela; + +typedef struct { + Elf64_Addr r_offset; + Elf64_Xword r_info; + Elf64_Sxword r_addend; +} Elf64_Rela; + + + +#define ELF32_R_SYM(val) ((val) >> 8) +#define ELF32_R_TYPE(val) ((val) & 0xff) +#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) + +#define ELF64_R_SYM(i) ((i) >> 32) +#define ELF64_R_TYPE(i) ((i) & 0xffffffff) +#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) + + + +typedef struct { + Elf32_Word p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf32_Word p_filesz; + Elf32_Word p_memsz; + Elf32_Word p_flags; + Elf32_Word p_align; +} Elf32_Phdr; + +typedef struct { + Elf64_Word p_type; + Elf64_Word p_flags; + Elf64_Off p_offset; + Elf64_Addr p_vaddr; + Elf64_Addr p_paddr; + Elf64_Xword p_filesz; + Elf64_Xword p_memsz; + Elf64_Xword p_align; +} Elf64_Phdr; + + + +#define PT_NULL 0 +#define PT_LOAD 1 +#define PT_DYNAMIC 2 +#define PT_INTERP 3 +#define PT_NOTE 4 +#define PT_SHLIB 5 +#define PT_PHDR 6 +#define PT_TLS 7 +#define PT_NUM 8 +#define PT_LOOS 0x60000000 +#define PT_GNU_EH_FRAME 0x6474e550 +#define PT_GNU_STACK 0x6474e551 +#define PT_GNU_RELRO 0x6474e552 +#define PT_LOSUNW 0x6ffffffa +#define PT_SUNWBSS 0x6ffffffa +#define PT_SUNWSTACK 0x6ffffffb +#define PT_HISUNW 0x6fffffff +#define PT_HIOS 0x6fffffff +#define PT_LOPROC 0x70000000 +#define PT_HIPROC 0x7fffffff + + +#define PN_XNUM 0xffff + + +#define PF_X (1 << 0) +#define PF_W (1 << 1) +#define PF_R (1 << 2) +#define PF_MASKOS 0x0ff00000 +#define PF_MASKPROC 0xf0000000 + + + +#define NT_PRSTATUS 1 +#define NT_FPREGSET 2 +#define NT_PRPSINFO 3 +#define NT_PRXREG 4 +#define NT_TASKSTRUCT 4 +#define NT_PLATFORM 5 +#define NT_AUXV 6 +#define NT_GWINDOWS 7 +#define NT_ASRS 8 +#define NT_PSTATUS 10 +#define NT_PSINFO 13 +#define NT_PRCRED 14 +#define NT_UTSNAME 15 +#define NT_LWPSTATUS 16 +#define NT_LWPSINFO 17 +#define NT_PRFPXREG 20 +#define NT_SIGINFO 0x53494749 +#define NT_FILE 0x46494c45 +#define NT_PRXFPREG 0x46e62b7f +#define NT_PPC_VMX 0x100 +#define NT_PPC_SPE 0x101 +#define NT_PPC_VSX 0x102 +#define NT_386_TLS 0x200 +#define NT_386_IOPERM 0x201 +#define NT_X86_XSTATE 0x202 +#define NT_S390_HIGH_GPRS 0x300 +#define NT_S390_TIMER 0x301 +#define NT_S390_TODCMP 0x302 +#define NT_S390_TODPREG 0x303 +#define NT_S390_CTRS 0x304 +#define NT_S390_PREFIX 0x305 +#define NT_S390_LAST_BREAK 0x306 +#define NT_S390_SYSTEM_CALL 0x307 +#define NT_S390_TDB 0x308 +#define NT_ARM_VFP 0x400 +#define NT_ARM_TLS 0x401 +#define NT_ARM_HW_BREAK 0x402 +#define NT_ARM_HW_WATCH 0x403 +#define NT_METAG_CBUF 0x500 +#define NT_METAG_RPIPE 0x501 +#define NT_METAG_TLS 0x502 +#define NT_VERSION 1 + + + + +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; + + + +#define DT_NULL 0 +#define DT_NEEDED 1 +#define DT_PLTRELSZ 2 +#define DT_PLTGOT 3 +#define DT_HASH 4 +#define DT_STRTAB 5 +#define DT_SYMTAB 6 +#define DT_RELA 7 +#define DT_RELASZ 8 +#define DT_RELAENT 9 +#define DT_STRSZ 10 +#define DT_SYMENT 11 +#define DT_INIT 12 +#define DT_FINI 13 +#define DT_SONAME 14 +#define DT_RPATH 15 +#define DT_SYMBOLIC 16 +#define DT_REL 17 +#define DT_RELSZ 18 +#define DT_RELENT 19 +#define DT_PLTREL 20 +#define DT_DEBUG 21 +#define DT_TEXTREL 22 +#define DT_JMPREL 23 +#define DT_BIND_NOW 24 +#define DT_INIT_ARRAY 25 +#define DT_FINI_ARRAY 26 +#define DT_INIT_ARRAYSZ 27 +#define DT_FINI_ARRAYSZ 28 +#define DT_RUNPATH 29 +#define DT_FLAGS 30 +#define DT_ENCODING 32 +#define DT_PREINIT_ARRAY 32 +#define DT_PREINIT_ARRAYSZ 33 +#define DT_NUM 34 +#define DT_LOOS 0x6000000d +#define DT_HIOS 0x6ffff000 +#define DT_LOPROC 0x70000000 +#define DT_HIPROC 0x7fffffff +#define DT_PROCNUM DT_MIPS_NUM + +#define DT_VALRNGLO 0x6ffffd00 +#define DT_GNU_PRELINKED 0x6ffffdf5 +#define DT_GNU_CONFLICTSZ 0x6ffffdf6 +#define DT_GNU_LIBLISTSZ 0x6ffffdf7 +#define DT_CHECKSUM 0x6ffffdf8 +#define DT_PLTPADSZ 0x6ffffdf9 +#define DT_MOVEENT 0x6ffffdfa +#define DT_MOVESZ 0x6ffffdfb +#define DT_FEATURE_1 0x6ffffdfc +#define DT_POSFLAG_1 0x6ffffdfd + +#define DT_SYMINSZ 0x6ffffdfe +#define DT_SYMINENT 0x6ffffdff +#define DT_VALRNGHI 0x6ffffdff +#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) +#define DT_VALNUM 12 + +#define DT_ADDRRNGLO 0x6ffffe00 +#define DT_GNU_HASH 0x6ffffef5 +#define DT_TLSDESC_PLT 0x6ffffef6 +#define DT_TLSDESC_GOT 0x6ffffef7 +#define DT_GNU_CONFLICT 0x6ffffef8 +#define DT_GNU_LIBLIST 0x6ffffef9 +#define DT_CONFIG 0x6ffffefa +#define DT_DEPAUDIT 0x6ffffefb +#define DT_AUDIT 0x6ffffefc +#define DT_PLTPAD 0x6ffffefd +#define DT_MOVETAB 0x6ffffefe +#define DT_SYMINFO 0x6ffffeff +#define DT_ADDRRNGHI 0x6ffffeff +#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) +#define DT_ADDRNUM 11 + + + +#define DT_VERSYM 0x6ffffff0 + +#define DT_RELACOUNT 0x6ffffff9 +#define DT_RELCOUNT 0x6ffffffa + + +#define DT_FLAGS_1 0x6ffffffb +#define DT_VERDEF 0x6ffffffc + +#define DT_VERDEFNUM 0x6ffffffd +#define DT_VERNEED 0x6ffffffe + +#define DT_VERNEEDNUM 0x6fffffff +#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) +#define DT_VERSIONTAGNUM 16 + + + +#define DT_AUXILIARY 0x7ffffffd +#define DT_FILTER 0x7fffffff +#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) +#define DT_EXTRANUM 3 + + +#define DF_ORIGIN 0x00000001 +#define DF_SYMBOLIC 0x00000002 +#define DF_TEXTREL 0x00000004 +#define DF_BIND_NOW 0x00000008 +#define DF_STATIC_TLS 0x00000010 + + + +#define DF_1_NOW 0x00000001 +#define DF_1_GLOBAL 0x00000002 +#define DF_1_GROUP 0x00000004 +#define DF_1_NODELETE 0x00000008 +#define DF_1_LOADFLTR 0x00000010 +#define DF_1_INITFIRST 0x00000020 +#define DF_1_NOOPEN 0x00000040 +#define DF_1_ORIGIN 0x00000080 +#define DF_1_DIRECT 0x00000100 +#define DF_1_TRANS 0x00000200 +#define DF_1_INTERPOSE 0x00000400 +#define DF_1_NODEFLIB 0x00000800 +#define DF_1_NODUMP 0x00001000 +#define DF_1_CONFALT 0x00002000 +#define DF_1_ENDFILTEE 0x00004000 +#define DF_1_DISPRELDNE 0x00008000 +#define DF_1_DISPRELPND 0x00010000 +#define DF_1_NODIRECT 0x00020000 +#define DF_1_IGNMULDEF 0x00040000 +#define DF_1_NOKSYMS 0x00080000 +#define DF_1_NOHDR 0x00100000 +#define DF_1_EDITED 0x00200000 +#define DF_1_NORELOC 0x00400000 +#define DF_1_SYMINTPOSE 0x00800000 +#define DF_1_GLOBAUDIT 0x01000000 +#define DF_1_SINGLETON 0x02000000 + +#define DTF_1_PARINIT 0x00000001 +#define DTF_1_CONFEXP 0x00000002 + + +#define DF_P1_LAZYLOAD 0x00000001 +#define DF_P1_GROUPPERM 0x00000002 + + + + +typedef struct { + Elf32_Half vd_version; + Elf32_Half vd_flags; + Elf32_Half vd_ndx; + Elf32_Half vd_cnt; + Elf32_Word vd_hash; + Elf32_Word vd_aux; + Elf32_Word vd_next; +} Elf32_Verdef; + +typedef struct { + Elf64_Half vd_version; + Elf64_Half vd_flags; + Elf64_Half vd_ndx; + Elf64_Half vd_cnt; + Elf64_Word vd_hash; + Elf64_Word vd_aux; + Elf64_Word vd_next; +} Elf64_Verdef; + + + +#define VER_DEF_NONE 0 +#define VER_DEF_CURRENT 1 +#define VER_DEF_NUM 2 + + +#define VER_FLG_BASE 0x1 +#define VER_FLG_WEAK 0x2 + + +#define VER_NDX_LOCAL 0 +#define VER_NDX_GLOBAL 1 +#define VER_NDX_LORESERVE 0xff00 +#define VER_NDX_ELIMINATE 0xff01 + + + +typedef struct { + Elf32_Word vda_name; + Elf32_Word vda_next; +} Elf32_Verdaux; + +typedef struct { + Elf64_Word vda_name; + Elf64_Word vda_next; +} Elf64_Verdaux; + + + + +typedef struct { + Elf32_Half vn_version; + Elf32_Half vn_cnt; + Elf32_Word vn_file; + Elf32_Word vn_aux; + Elf32_Word vn_next; +} Elf32_Verneed; + +typedef struct { + Elf64_Half vn_version; + Elf64_Half vn_cnt; + Elf64_Word vn_file; + Elf64_Word vn_aux; + Elf64_Word vn_next; +} Elf64_Verneed; + + + +#define VER_NEED_NONE 0 +#define VER_NEED_CURRENT 1 +#define VER_NEED_NUM 2 + + + +typedef struct { + Elf32_Word vna_hash; + Elf32_Half vna_flags; + Elf32_Half vna_other; + Elf32_Word vna_name; + Elf32_Word vna_next; +} Elf32_Vernaux; + +typedef struct { + Elf64_Word vna_hash; + Elf64_Half vna_flags; + Elf64_Half vna_other; + Elf64_Word vna_name; + Elf64_Word vna_next; +} Elf64_Vernaux; + + + +#define VER_FLG_WEAK 0x2 + + + +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; + + + +#define AT_NULL 0 +#define AT_IGNORE 1 +#define AT_EXECFD 2 +#define AT_PHDR 3 +#define AT_PHENT 4 +#define AT_PHNUM 5 +#define AT_PAGESZ 6 +#define AT_BASE 7 +#define AT_FLAGS 8 +#define AT_ENTRY 9 +#define AT_NOTELF 10 +#define AT_UID 11 +#define AT_EUID 12 +#define AT_GID 13 +#define AT_EGID 14 +#define AT_CLKTCK 17 + + +#define AT_PLATFORM 15 +#define AT_HWCAP 16 + + + + +#define AT_FPUCW 18 + + +#define AT_DCACHEBSIZE 19 +#define AT_ICACHEBSIZE 20 +#define AT_UCACHEBSIZE 21 + + + +#define AT_IGNOREPPC 22 + +#define AT_SECURE 23 + +#define AT_BASE_PLATFORM 24 + +#define AT_RANDOM 25 + +#define AT_HWCAP2 26 + +#define AT_EXECFN 31 + + + +#define AT_SYSINFO 32 +#define AT_SYSINFO_EHDR 33 + + + +#define AT_L1I_CACHESHAPE 34 +#define AT_L1D_CACHESHAPE 35 +#define AT_L2_CACHESHAPE 36 +#define AT_L3_CACHESHAPE 37 + + + + +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; + + + + +#define ELF_NOTE_SOLARIS "SUNW Solaris" + + +#define ELF_NOTE_GNU "GNU" + + + + + +#define ELF_NOTE_PAGESIZE_HINT 1 + + +#define NT_GNU_ABI_TAG 1 +#define ELF_NOTE_ABI NT_GNU_ABI_TAG + + + +#define ELF_NOTE_OS_LINUX 0 +#define ELF_NOTE_OS_GNU 1 +#define ELF_NOTE_OS_SOLARIS2 2 +#define ELF_NOTE_OS_FREEBSD 3 + +#define NT_GNU_BUILD_ID 3 +#define NT_GNU_GOLD_VERSION 4 + + + +typedef struct { + Elf32_Xword m_value; + Elf32_Word m_info; + Elf32_Word m_poffset; + Elf32_Half m_repeat; + Elf32_Half m_stride; +} Elf32_Move; + +typedef struct { + Elf64_Xword m_value; + Elf64_Xword m_info; + Elf64_Xword m_poffset; + Elf64_Half m_repeat; + Elf64_Half m_stride; +} Elf64_Move; + + +#define ELF32_M_SYM(info) ((info) >> 8) +#define ELF32_M_SIZE(info) ((unsigned char) (info)) +#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) + +#define ELF64_M_SYM(info) ELF32_M_SYM (info) +#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) +#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) + +#define EF_CPU32 0x00810000 + +#define R_68K_NONE 0 +#define R_68K_32 1 +#define R_68K_16 2 +#define R_68K_8 3 +#define R_68K_PC32 4 +#define R_68K_PC16 5 +#define R_68K_PC8 6 +#define R_68K_GOT32 7 +#define R_68K_GOT16 8 +#define R_68K_GOT8 9 +#define R_68K_GOT32O 10 +#define R_68K_GOT16O 11 +#define R_68K_GOT8O 12 +#define R_68K_PLT32 13 +#define R_68K_PLT16 14 +#define R_68K_PLT8 15 +#define R_68K_PLT32O 16 +#define R_68K_PLT16O 17 +#define R_68K_PLT8O 18 +#define R_68K_COPY 19 +#define R_68K_GLOB_DAT 20 +#define R_68K_JMP_SLOT 21 +#define R_68K_RELATIVE 22 +#define R_68K_NUM 23 + +#define R_386_NONE 0 +#define R_386_32 1 +#define R_386_PC32 2 +#define R_386_GOT32 3 +#define R_386_PLT32 4 +#define R_386_COPY 5 +#define R_386_GLOB_DAT 6 +#define R_386_JMP_SLOT 7 +#define R_386_RELATIVE 8 +#define R_386_GOTOFF 9 +#define R_386_GOTPC 10 +#define R_386_32PLT 11 +#define R_386_TLS_TPOFF 14 +#define R_386_TLS_IE 15 +#define R_386_TLS_GOTIE 16 +#define R_386_TLS_LE 17 +#define R_386_TLS_GD 18 +#define R_386_TLS_LDM 19 +#define R_386_16 20 +#define R_386_PC16 21 +#define R_386_8 22 +#define R_386_PC8 23 +#define R_386_TLS_GD_32 24 +#define R_386_TLS_GD_PUSH 25 +#define R_386_TLS_GD_CALL 26 +#define R_386_TLS_GD_POP 27 +#define R_386_TLS_LDM_32 28 +#define R_386_TLS_LDM_PUSH 29 +#define R_386_TLS_LDM_CALL 30 +#define R_386_TLS_LDM_POP 31 +#define R_386_TLS_LDO_32 32 +#define R_386_TLS_IE_32 33 +#define R_386_TLS_LE_32 34 +#define R_386_TLS_DTPMOD32 35 +#define R_386_TLS_DTPOFF32 36 +#define R_386_TLS_TPOFF32 37 +#define R_386_SIZE32 38 +#define R_386_TLS_GOTDESC 39 +#define R_386_TLS_DESC_CALL 40 +#define R_386_TLS_DESC 41 +#define R_386_IRELATIVE 42 +#define R_386_NUM 43 + + + + + +#define STT_SPARC_REGISTER 13 + + + +#define EF_SPARCV9_MM 3 +#define EF_SPARCV9_TSO 0 +#define EF_SPARCV9_PSO 1 +#define EF_SPARCV9_RMO 2 +#define EF_SPARC_LEDATA 0x800000 +#define EF_SPARC_EXT_MASK 0xFFFF00 +#define EF_SPARC_32PLUS 0x000100 +#define EF_SPARC_SUN_US1 0x000200 +#define EF_SPARC_HAL_R1 0x000400 +#define EF_SPARC_SUN_US3 0x000800 + + + +#define R_SPARC_NONE 0 +#define R_SPARC_8 1 +#define R_SPARC_16 2 +#define R_SPARC_32 3 +#define R_SPARC_DISP8 4 +#define R_SPARC_DISP16 5 +#define R_SPARC_DISP32 6 +#define R_SPARC_WDISP30 7 +#define R_SPARC_WDISP22 8 +#define R_SPARC_HI22 9 +#define R_SPARC_22 10 +#define R_SPARC_13 11 +#define R_SPARC_LO10 12 +#define R_SPARC_GOT10 13 +#define R_SPARC_GOT13 14 +#define R_SPARC_GOT22 15 +#define R_SPARC_PC10 16 +#define R_SPARC_PC22 17 +#define R_SPARC_WPLT30 18 +#define R_SPARC_COPY 19 +#define R_SPARC_GLOB_DAT 20 +#define R_SPARC_JMP_SLOT 21 +#define R_SPARC_RELATIVE 22 +#define R_SPARC_UA32 23 + + + +#define R_SPARC_PLT32 24 +#define R_SPARC_HIPLT22 25 +#define R_SPARC_LOPLT10 26 +#define R_SPARC_PCPLT32 27 +#define R_SPARC_PCPLT22 28 +#define R_SPARC_PCPLT10 29 +#define R_SPARC_10 30 +#define R_SPARC_11 31 +#define R_SPARC_64 32 +#define R_SPARC_OLO10 33 +#define R_SPARC_HH22 34 +#define R_SPARC_HM10 35 +#define R_SPARC_LM22 36 +#define R_SPARC_PC_HH22 37 +#define R_SPARC_PC_HM10 38 +#define R_SPARC_PC_LM22 39 +#define R_SPARC_WDISP16 40 +#define R_SPARC_WDISP19 41 +#define R_SPARC_GLOB_JMP 42 +#define R_SPARC_7 43 +#define R_SPARC_5 44 +#define R_SPARC_6 45 +#define R_SPARC_DISP64 46 +#define R_SPARC_PLT64 47 +#define R_SPARC_HIX22 48 +#define R_SPARC_LOX10 49 +#define R_SPARC_H44 50 +#define R_SPARC_M44 51 +#define R_SPARC_L44 52 +#define R_SPARC_REGISTER 53 +#define R_SPARC_UA64 54 +#define R_SPARC_UA16 55 +#define R_SPARC_TLS_GD_HI22 56 +#define R_SPARC_TLS_GD_LO10 57 +#define R_SPARC_TLS_GD_ADD 58 +#define R_SPARC_TLS_GD_CALL 59 +#define R_SPARC_TLS_LDM_HI22 60 +#define R_SPARC_TLS_LDM_LO10 61 +#define R_SPARC_TLS_LDM_ADD 62 +#define R_SPARC_TLS_LDM_CALL 63 +#define R_SPARC_TLS_LDO_HIX22 64 +#define R_SPARC_TLS_LDO_LOX10 65 +#define R_SPARC_TLS_LDO_ADD 66 +#define R_SPARC_TLS_IE_HI22 67 +#define R_SPARC_TLS_IE_LO10 68 +#define R_SPARC_TLS_IE_LD 69 +#define R_SPARC_TLS_IE_LDX 70 +#define R_SPARC_TLS_IE_ADD 71 +#define R_SPARC_TLS_LE_HIX22 72 +#define R_SPARC_TLS_LE_LOX10 73 +#define R_SPARC_TLS_DTPMOD32 74 +#define R_SPARC_TLS_DTPMOD64 75 +#define R_SPARC_TLS_DTPOFF32 76 +#define R_SPARC_TLS_DTPOFF64 77 +#define R_SPARC_TLS_TPOFF32 78 +#define R_SPARC_TLS_TPOFF64 79 +#define R_SPARC_GOTDATA_HIX22 80 +#define R_SPARC_GOTDATA_LOX10 81 +#define R_SPARC_GOTDATA_OP_HIX22 82 +#define R_SPARC_GOTDATA_OP_LOX10 83 +#define R_SPARC_GOTDATA_OP 84 +#define R_SPARC_H34 85 +#define R_SPARC_SIZE32 86 +#define R_SPARC_SIZE64 87 +#define R_SPARC_GNU_VTINHERIT 250 +#define R_SPARC_GNU_VTENTRY 251 +#define R_SPARC_REV32 252 + +#define R_SPARC_NUM 253 + + + +#define DT_SPARC_REGISTER 0x70000001 +#define DT_SPARC_NUM 2 + + +#define EF_MIPS_NOREORDER 1 +#define EF_MIPS_PIC 2 +#define EF_MIPS_CPIC 4 +#define EF_MIPS_XGOT 8 +#define EF_MIPS_64BIT_WHIRL 16 +#define EF_MIPS_ABI2 32 +#define EF_MIPS_ABI_ON32 64 +#define EF_MIPS_NAN2008 1024 +#define EF_MIPS_ARCH 0xf0000000 + + + +#define EF_MIPS_ARCH_1 0x00000000 +#define EF_MIPS_ARCH_2 0x10000000 +#define EF_MIPS_ARCH_3 0x20000000 +#define EF_MIPS_ARCH_4 0x30000000 +#define EF_MIPS_ARCH_5 0x40000000 +#define EF_MIPS_ARCH_32 0x50000000 +#define EF_MIPS_ARCH_64 0x60000000 +#define EF_MIPS_ARCH_32R2 0x70000000 +#define EF_MIPS_ARCH_64R2 0x80000000 + + +#define E_MIPS_ARCH_1 0x00000000 +#define E_MIPS_ARCH_2 0x10000000 +#define E_MIPS_ARCH_3 0x20000000 +#define E_MIPS_ARCH_4 0x30000000 +#define E_MIPS_ARCH_5 0x40000000 +#define E_MIPS_ARCH_32 0x50000000 +#define E_MIPS_ARCH_64 0x60000000 + + + +#define SHN_MIPS_ACOMMON 0xff00 +#define SHN_MIPS_TEXT 0xff01 +#define SHN_MIPS_DATA 0xff02 +#define SHN_MIPS_SCOMMON 0xff03 +#define SHN_MIPS_SUNDEFINED 0xff04 + + + +#define SHT_MIPS_LIBLIST 0x70000000 +#define SHT_MIPS_MSYM 0x70000001 +#define SHT_MIPS_CONFLICT 0x70000002 +#define SHT_MIPS_GPTAB 0x70000003 +#define SHT_MIPS_UCODE 0x70000004 +#define SHT_MIPS_DEBUG 0x70000005 +#define SHT_MIPS_REGINFO 0x70000006 +#define SHT_MIPS_PACKAGE 0x70000007 +#define SHT_MIPS_PACKSYM 0x70000008 +#define SHT_MIPS_RELD 0x70000009 +#define SHT_MIPS_IFACE 0x7000000b +#define SHT_MIPS_CONTENT 0x7000000c +#define SHT_MIPS_OPTIONS 0x7000000d +#define SHT_MIPS_SHDR 0x70000010 +#define SHT_MIPS_FDESC 0x70000011 +#define SHT_MIPS_EXTSYM 0x70000012 +#define SHT_MIPS_DENSE 0x70000013 +#define SHT_MIPS_PDESC 0x70000014 +#define SHT_MIPS_LOCSYM 0x70000015 +#define SHT_MIPS_AUXSYM 0x70000016 +#define SHT_MIPS_OPTSYM 0x70000017 +#define SHT_MIPS_LOCSTR 0x70000018 +#define SHT_MIPS_LINE 0x70000019 +#define SHT_MIPS_RFDESC 0x7000001a +#define SHT_MIPS_DELTASYM 0x7000001b +#define SHT_MIPS_DELTAINST 0x7000001c +#define SHT_MIPS_DELTACLASS 0x7000001d +#define SHT_MIPS_DWARF 0x7000001e +#define SHT_MIPS_DELTADECL 0x7000001f +#define SHT_MIPS_SYMBOL_LIB 0x70000020 +#define SHT_MIPS_EVENTS 0x70000021 +#define SHT_MIPS_TRANSLATE 0x70000022 +#define SHT_MIPS_PIXIE 0x70000023 +#define SHT_MIPS_XLATE 0x70000024 +#define SHT_MIPS_XLATE_DEBUG 0x70000025 +#define SHT_MIPS_WHIRL 0x70000026 +#define SHT_MIPS_EH_REGION 0x70000027 +#define SHT_MIPS_XLATE_OLD 0x70000028 +#define SHT_MIPS_PDR_EXCEPTION 0x70000029 + + + +#define SHF_MIPS_GPREL 0x10000000 +#define SHF_MIPS_MERGE 0x20000000 +#define SHF_MIPS_ADDR 0x40000000 +#define SHF_MIPS_STRINGS 0x80000000 +#define SHF_MIPS_NOSTRIP 0x08000000 +#define SHF_MIPS_LOCAL 0x04000000 +#define SHF_MIPS_NAMES 0x02000000 +#define SHF_MIPS_NODUPE 0x01000000 + + + + + +#define STO_MIPS_DEFAULT 0x0 +#define STO_MIPS_INTERNAL 0x1 +#define STO_MIPS_HIDDEN 0x2 +#define STO_MIPS_PROTECTED 0x3 +#define STO_MIPS_PLT 0x8 +#define STO_MIPS_SC_ALIGN_UNUSED 0xff + + +#define STB_MIPS_SPLIT_COMMON 13 + + + +typedef union { + struct { + Elf32_Word gt_current_g_value; + Elf32_Word gt_unused; + } gt_header; + struct { + Elf32_Word gt_g_value; + Elf32_Word gt_bytes; + } gt_entry; +} Elf32_gptab; + + + +typedef struct { + Elf32_Word ri_gprmask; + Elf32_Word ri_cprmask[4]; + Elf32_Sword ri_gp_value; +} Elf32_RegInfo; + + + +typedef struct { + unsigned char kind; + + unsigned char size; + Elf32_Section section; + + Elf32_Word info; +} Elf_Options; + + + +#define ODK_NULL 0 +#define ODK_REGINFO 1 +#define ODK_EXCEPTIONS 2 +#define ODK_PAD 3 +#define ODK_HWPATCH 4 +#define ODK_FILL 5 +#define ODK_TAGS 6 +#define ODK_HWAND 7 +#define ODK_HWOR 8 + + + +#define OEX_FPU_MIN 0x1f +#define OEX_FPU_MAX 0x1f00 +#define OEX_PAGE0 0x10000 +#define OEX_SMM 0x20000 +#define OEX_FPDBUG 0x40000 +#define OEX_PRECISEFP OEX_FPDBUG +#define OEX_DISMISS 0x80000 + +#define OEX_FPU_INVAL 0x10 +#define OEX_FPU_DIV0 0x08 +#define OEX_FPU_OFLO 0x04 +#define OEX_FPU_UFLO 0x02 +#define OEX_FPU_INEX 0x01 + + + +#define OHW_R4KEOP 0x1 +#define OHW_R8KPFETCH 0x2 +#define OHW_R5KEOP 0x4 +#define OHW_R5KCVTL 0x8 + +#define OPAD_PREFIX 0x1 +#define OPAD_POSTFIX 0x2 +#define OPAD_SYMBOL 0x4 + + + +typedef struct { + Elf32_Word hwp_flags1; + Elf32_Word hwp_flags2; +} Elf_Options_Hw; + + + +#define OHWA0_R4KEOP_CHECKED 0x00000001 +#define OHWA1_R4KEOP_CLEAN 0x00000002 + + + +#define R_MIPS_NONE 0 +#define R_MIPS_16 1 +#define R_MIPS_32 2 +#define R_MIPS_REL32 3 +#define R_MIPS_26 4 +#define R_MIPS_HI16 5 +#define R_MIPS_LO16 6 +#define R_MIPS_GPREL16 7 +#define R_MIPS_LITERAL 8 +#define R_MIPS_GOT16 9 +#define R_MIPS_PC16 10 +#define R_MIPS_CALL16 11 +#define R_MIPS_GPREL32 12 + +#define R_MIPS_SHIFT5 16 +#define R_MIPS_SHIFT6 17 +#define R_MIPS_64 18 +#define R_MIPS_GOT_DISP 19 +#define R_MIPS_GOT_PAGE 20 +#define R_MIPS_GOT_OFST 21 +#define R_MIPS_GOT_HI16 22 +#define R_MIPS_GOT_LO16 23 +#define R_MIPS_SUB 24 +#define R_MIPS_INSERT_A 25 +#define R_MIPS_INSERT_B 26 +#define R_MIPS_DELETE 27 +#define R_MIPS_HIGHER 28 +#define R_MIPS_HIGHEST 29 +#define R_MIPS_CALL_HI16 30 +#define R_MIPS_CALL_LO16 31 +#define R_MIPS_SCN_DISP 32 +#define R_MIPS_REL16 33 +#define R_MIPS_ADD_IMMEDIATE 34 +#define R_MIPS_PJUMP 35 +#define R_MIPS_RELGOT 36 +#define R_MIPS_JALR 37 +#define R_MIPS_TLS_DTPMOD32 38 +#define R_MIPS_TLS_DTPREL32 39 +#define R_MIPS_TLS_DTPMOD64 40 +#define R_MIPS_TLS_DTPREL64 41 +#define R_MIPS_TLS_GD 42 +#define R_MIPS_TLS_LDM 43 +#define R_MIPS_TLS_DTPREL_HI16 44 +#define R_MIPS_TLS_DTPREL_LO16 45 +#define R_MIPS_TLS_GOTTPREL 46 +#define R_MIPS_TLS_TPREL32 47 +#define R_MIPS_TLS_TPREL64 48 +#define R_MIPS_TLS_TPREL_HI16 49 +#define R_MIPS_TLS_TPREL_LO16 50 +#define R_MIPS_GLOB_DAT 51 +#define R_MIPS_COPY 126 +#define R_MIPS_JUMP_SLOT 127 + +#define R_MIPS_NUM 128 + + + +#define PT_MIPS_REGINFO 0x70000000 +#define PT_MIPS_RTPROC 0x70000001 +#define PT_MIPS_OPTIONS 0x70000002 + + + +#define PF_MIPS_LOCAL 0x10000000 + + + +#define DT_MIPS_RLD_VERSION 0x70000001 +#define DT_MIPS_TIME_STAMP 0x70000002 +#define DT_MIPS_ICHECKSUM 0x70000003 +#define DT_MIPS_IVERSION 0x70000004 +#define DT_MIPS_FLAGS 0x70000005 +#define DT_MIPS_BASE_ADDRESS 0x70000006 +#define DT_MIPS_MSYM 0x70000007 +#define DT_MIPS_CONFLICT 0x70000008 +#define DT_MIPS_LIBLIST 0x70000009 +#define DT_MIPS_LOCAL_GOTNO 0x7000000a +#define DT_MIPS_CONFLICTNO 0x7000000b +#define DT_MIPS_LIBLISTNO 0x70000010 +#define DT_MIPS_SYMTABNO 0x70000011 +#define DT_MIPS_UNREFEXTNO 0x70000012 +#define DT_MIPS_GOTSYM 0x70000013 +#define DT_MIPS_HIPAGENO 0x70000014 +#define DT_MIPS_RLD_MAP 0x70000016 +#define DT_MIPS_DELTA_CLASS 0x70000017 +#define DT_MIPS_DELTA_CLASS_NO 0x70000018 + +#define DT_MIPS_DELTA_INSTANCE 0x70000019 +#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a + +#define DT_MIPS_DELTA_RELOC 0x7000001b +#define DT_MIPS_DELTA_RELOC_NO 0x7000001c + +#define DT_MIPS_DELTA_SYM 0x7000001d + +#define DT_MIPS_DELTA_SYM_NO 0x7000001e + +#define DT_MIPS_DELTA_CLASSSYM 0x70000020 + +#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 + +#define DT_MIPS_CXX_FLAGS 0x70000022 +#define DT_MIPS_PIXIE_INIT 0x70000023 +#define DT_MIPS_SYMBOL_LIB 0x70000024 +#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 +#define DT_MIPS_LOCAL_GOTIDX 0x70000026 +#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 +#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 +#define DT_MIPS_OPTIONS 0x70000029 +#define DT_MIPS_INTERFACE 0x7000002a +#define DT_MIPS_DYNSTR_ALIGN 0x7000002b +#define DT_MIPS_INTERFACE_SIZE 0x7000002c +#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d + +#define DT_MIPS_PERF_SUFFIX 0x7000002e + +#define DT_MIPS_COMPACT_SIZE 0x7000002f +#define DT_MIPS_GP_VALUE 0x70000030 +#define DT_MIPS_AUX_DYNAMIC 0x70000031 + +#define DT_MIPS_PLTGOT 0x70000032 + +#define DT_MIPS_RWPLT 0x70000034 +#define DT_MIPS_NUM 0x35 + + + +#define RHF_NONE 0 +#define RHF_QUICKSTART (1 << 0) +#define RHF_NOTPOT (1 << 1) +#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) +#define RHF_NO_MOVE (1 << 3) +#define RHF_SGI_ONLY (1 << 4) +#define RHF_GUARANTEE_INIT (1 << 5) +#define RHF_DELTA_C_PLUS_PLUS (1 << 6) +#define RHF_GUARANTEE_START_INIT (1 << 7) +#define RHF_PIXIE (1 << 8) +#define RHF_DEFAULT_DELAY_LOAD (1 << 9) +#define RHF_REQUICKSTART (1 << 10) +#define RHF_REQUICKSTARTED (1 << 11) +#define RHF_CORD (1 << 12) +#define RHF_NO_UNRES_UNDEF (1 << 13) +#define RHF_RLD_ORDER_SAFE (1 << 14) + + + +typedef struct +{ + Elf32_Word l_name; + Elf32_Word l_time_stamp; + Elf32_Word l_checksum; + Elf32_Word l_version; + Elf32_Word l_flags; +} Elf32_Lib; + +typedef struct +{ + Elf64_Word l_name; + Elf64_Word l_time_stamp; + Elf64_Word l_checksum; + Elf64_Word l_version; + Elf64_Word l_flags; +} Elf64_Lib; + + + + +#define LL_NONE 0 +#define LL_EXACT_MATCH (1 << 0) +#define LL_IGNORE_INT_VER (1 << 1) +#define LL_REQUIRE_MINOR (1 << 2) +#define LL_EXPORTS (1 << 3) +#define LL_DELAY_LOAD (1 << 4) +#define LL_DELTA (1 << 5) + + + +typedef Elf32_Addr Elf32_Conflict; + + + + + + +#define EF_PARISC_TRAPNIL 0x00010000 +#define EF_PARISC_EXT 0x00020000 +#define EF_PARISC_LSB 0x00040000 +#define EF_PARISC_WIDE 0x00080000 +#define EF_PARISC_NO_KABP 0x00100000 + +#define EF_PARISC_LAZYSWAP 0x00400000 +#define EF_PARISC_ARCH 0x0000ffff + + + +#define EFA_PARISC_1_0 0x020b +#define EFA_PARISC_1_1 0x0210 +#define EFA_PARISC_2_0 0x0214 + + + +#define SHN_PARISC_ANSI_COMMON 0xff00 + +#define SHN_PARISC_HUGE_COMMON 0xff01 + + + +#define SHT_PARISC_EXT 0x70000000 +#define SHT_PARISC_UNWIND 0x70000001 +#define SHT_PARISC_DOC 0x70000002 + + + +#define SHF_PARISC_SHORT 0x20000000 +#define SHF_PARISC_HUGE 0x40000000 +#define SHF_PARISC_SBP 0x80000000 + + + +#define STT_PARISC_MILLICODE 13 + +#define STT_HP_OPAQUE (STT_LOOS + 0x1) +#define STT_HP_STUB (STT_LOOS + 0x2) + + + +#define R_PARISC_NONE 0 +#define R_PARISC_DIR32 1 +#define R_PARISC_DIR21L 2 +#define R_PARISC_DIR17R 3 +#define R_PARISC_DIR17F 4 +#define R_PARISC_DIR14R 6 +#define R_PARISC_PCREL32 9 +#define R_PARISC_PCREL21L 10 +#define R_PARISC_PCREL17R 11 +#define R_PARISC_PCREL17F 12 +#define R_PARISC_PCREL14R 14 +#define R_PARISC_DPREL21L 18 +#define R_PARISC_DPREL14R 22 +#define R_PARISC_GPREL21L 26 +#define R_PARISC_GPREL14R 30 +#define R_PARISC_LTOFF21L 34 +#define R_PARISC_LTOFF14R 38 +#define R_PARISC_SECREL32 41 +#define R_PARISC_SEGBASE 48 +#define R_PARISC_SEGREL32 49 +#define R_PARISC_PLTOFF21L 50 +#define R_PARISC_PLTOFF14R 54 +#define R_PARISC_LTOFF_FPTR32 57 +#define R_PARISC_LTOFF_FPTR21L 58 +#define R_PARISC_LTOFF_FPTR14R 62 +#define R_PARISC_FPTR64 64 +#define R_PARISC_PLABEL32 65 +#define R_PARISC_PLABEL21L 66 +#define R_PARISC_PLABEL14R 70 +#define R_PARISC_PCREL64 72 +#define R_PARISC_PCREL22F 74 +#define R_PARISC_PCREL14WR 75 +#define R_PARISC_PCREL14DR 76 +#define R_PARISC_PCREL16F 77 +#define R_PARISC_PCREL16WF 78 +#define R_PARISC_PCREL16DF 79 +#define R_PARISC_DIR64 80 +#define R_PARISC_DIR14WR 83 +#define R_PARISC_DIR14DR 84 +#define R_PARISC_DIR16F 85 +#define R_PARISC_DIR16WF 86 +#define R_PARISC_DIR16DF 87 +#define R_PARISC_GPREL64 88 +#define R_PARISC_GPREL14WR 91 +#define R_PARISC_GPREL14DR 92 +#define R_PARISC_GPREL16F 93 +#define R_PARISC_GPREL16WF 94 +#define R_PARISC_GPREL16DF 95 +#define R_PARISC_LTOFF64 96 +#define R_PARISC_LTOFF14WR 99 +#define R_PARISC_LTOFF14DR 100 +#define R_PARISC_LTOFF16F 101 +#define R_PARISC_LTOFF16WF 102 +#define R_PARISC_LTOFF16DF 103 +#define R_PARISC_SECREL64 104 +#define R_PARISC_SEGREL64 112 +#define R_PARISC_PLTOFF14WR 115 +#define R_PARISC_PLTOFF14DR 116 +#define R_PARISC_PLTOFF16F 117 +#define R_PARISC_PLTOFF16WF 118 +#define R_PARISC_PLTOFF16DF 119 +#define R_PARISC_LTOFF_FPTR64 120 +#define R_PARISC_LTOFF_FPTR14WR 123 +#define R_PARISC_LTOFF_FPTR14DR 124 +#define R_PARISC_LTOFF_FPTR16F 125 +#define R_PARISC_LTOFF_FPTR16WF 126 +#define R_PARISC_LTOFF_FPTR16DF 127 +#define R_PARISC_LORESERVE 128 +#define R_PARISC_COPY 128 +#define R_PARISC_IPLT 129 +#define R_PARISC_EPLT 130 +#define R_PARISC_TPREL32 153 +#define R_PARISC_TPREL21L 154 +#define R_PARISC_TPREL14R 158 +#define R_PARISC_LTOFF_TP21L 162 +#define R_PARISC_LTOFF_TP14R 166 +#define R_PARISC_LTOFF_TP14F 167 +#define R_PARISC_TPREL64 216 +#define R_PARISC_TPREL14WR 219 +#define R_PARISC_TPREL14DR 220 +#define R_PARISC_TPREL16F 221 +#define R_PARISC_TPREL16WF 222 +#define R_PARISC_TPREL16DF 223 +#define R_PARISC_LTOFF_TP64 224 +#define R_PARISC_LTOFF_TP14WR 227 +#define R_PARISC_LTOFF_TP14DR 228 +#define R_PARISC_LTOFF_TP16F 229 +#define R_PARISC_LTOFF_TP16WF 230 +#define R_PARISC_LTOFF_TP16DF 231 +#define R_PARISC_GNU_VTENTRY 232 +#define R_PARISC_GNU_VTINHERIT 233 +#define R_PARISC_TLS_GD21L 234 +#define R_PARISC_TLS_GD14R 235 +#define R_PARISC_TLS_GDCALL 236 +#define R_PARISC_TLS_LDM21L 237 +#define R_PARISC_TLS_LDM14R 238 +#define R_PARISC_TLS_LDMCALL 239 +#define R_PARISC_TLS_LDO21L 240 +#define R_PARISC_TLS_LDO14R 241 +#define R_PARISC_TLS_DTPMOD32 242 +#define R_PARISC_TLS_DTPMOD64 243 +#define R_PARISC_TLS_DTPOFF32 244 +#define R_PARISC_TLS_DTPOFF64 245 +#define R_PARISC_TLS_LE21L R_PARISC_TPREL21L +#define R_PARISC_TLS_LE14R R_PARISC_TPREL14R +#define R_PARISC_TLS_IE21L R_PARISC_LTOFF_TP21L +#define R_PARISC_TLS_IE14R R_PARISC_LTOFF_TP14R +#define R_PARISC_TLS_TPREL32 R_PARISC_TPREL32 +#define R_PARISC_TLS_TPREL64 R_PARISC_TPREL64 +#define R_PARISC_HIRESERVE 255 + + + +#define PT_HP_TLS (PT_LOOS + 0x0) +#define PT_HP_CORE_NONE (PT_LOOS + 0x1) +#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) +#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) +#define PT_HP_CORE_COMM (PT_LOOS + 0x4) +#define PT_HP_CORE_PROC (PT_LOOS + 0x5) +#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) +#define PT_HP_CORE_STACK (PT_LOOS + 0x7) +#define PT_HP_CORE_SHM (PT_LOOS + 0x8) +#define PT_HP_CORE_MMF (PT_LOOS + 0x9) +#define PT_HP_PARALLEL (PT_LOOS + 0x10) +#define PT_HP_FASTBIND (PT_LOOS + 0x11) +#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) +#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) +#define PT_HP_STACK (PT_LOOS + 0x14) + +#define PT_PARISC_ARCHEXT 0x70000000 +#define PT_PARISC_UNWIND 0x70000001 + + + +#define PF_PARISC_SBP 0x08000000 + +#define PF_HP_PAGE_SIZE 0x00100000 +#define PF_HP_FAR_SHARED 0x00200000 +#define PF_HP_NEAR_SHARED 0x00400000 +#define PF_HP_CODE 0x01000000 +#define PF_HP_MODIFY 0x02000000 +#define PF_HP_LAZYSWAP 0x04000000 +#define PF_HP_SBP 0x08000000 + + + + + + +#define EF_ALPHA_32BIT 1 +#define EF_ALPHA_CANRELAX 2 + + + + +#define SHT_ALPHA_DEBUG 0x70000001 +#define SHT_ALPHA_REGINFO 0x70000002 + + + +#define SHF_ALPHA_GPREL 0x10000000 + + +#define STO_ALPHA_NOPV 0x80 +#define STO_ALPHA_STD_GPLOAD 0x88 + + + +#define R_ALPHA_NONE 0 +#define R_ALPHA_REFLONG 1 +#define R_ALPHA_REFQUAD 2 +#define R_ALPHA_GPREL32 3 +#define R_ALPHA_LITERAL 4 +#define R_ALPHA_LITUSE 5 +#define R_ALPHA_GPDISP 6 +#define R_ALPHA_BRADDR 7 +#define R_ALPHA_HINT 8 +#define R_ALPHA_SREL16 9 +#define R_ALPHA_SREL32 10 +#define R_ALPHA_SREL64 11 +#define R_ALPHA_GPRELHIGH 17 +#define R_ALPHA_GPRELLOW 18 +#define R_ALPHA_GPREL16 19 +#define R_ALPHA_COPY 24 +#define R_ALPHA_GLOB_DAT 25 +#define R_ALPHA_JMP_SLOT 26 +#define R_ALPHA_RELATIVE 27 +#define R_ALPHA_TLS_GD_HI 28 +#define R_ALPHA_TLSGD 29 +#define R_ALPHA_TLS_LDM 30 +#define R_ALPHA_DTPMOD64 31 +#define R_ALPHA_GOTDTPREL 32 +#define R_ALPHA_DTPREL64 33 +#define R_ALPHA_DTPRELHI 34 +#define R_ALPHA_DTPRELLO 35 +#define R_ALPHA_DTPREL16 36 +#define R_ALPHA_GOTTPREL 37 +#define R_ALPHA_TPREL64 38 +#define R_ALPHA_TPRELHI 39 +#define R_ALPHA_TPRELLO 40 +#define R_ALPHA_TPREL16 41 + +#define R_ALPHA_NUM 46 + + +#define LITUSE_ALPHA_ADDR 0 +#define LITUSE_ALPHA_BASE 1 +#define LITUSE_ALPHA_BYTOFF 2 +#define LITUSE_ALPHA_JSR 3 +#define LITUSE_ALPHA_TLS_GD 4 +#define LITUSE_ALPHA_TLS_LDM 5 + + +#define DT_ALPHA_PLTRO (DT_LOPROC + 0) +#define DT_ALPHA_NUM 1 + + + + +#define EF_PPC_EMB 0x80000000 + + +#define EF_PPC_RELOCATABLE 0x00010000 +#define EF_PPC_RELOCATABLE_LIB 0x00008000 + + + +#define R_PPC_NONE 0 +#define R_PPC_ADDR32 1 +#define R_PPC_ADDR24 2 +#define R_PPC_ADDR16 3 +#define R_PPC_ADDR16_LO 4 +#define R_PPC_ADDR16_HI 5 +#define R_PPC_ADDR16_HA 6 +#define R_PPC_ADDR14 7 +#define R_PPC_ADDR14_BRTAKEN 8 +#define R_PPC_ADDR14_BRNTAKEN 9 +#define R_PPC_REL24 10 +#define R_PPC_REL14 11 +#define R_PPC_REL14_BRTAKEN 12 +#define R_PPC_REL14_BRNTAKEN 13 +#define R_PPC_GOT16 14 +#define R_PPC_GOT16_LO 15 +#define R_PPC_GOT16_HI 16 +#define R_PPC_GOT16_HA 17 +#define R_PPC_PLTREL24 18 +#define R_PPC_COPY 19 +#define R_PPC_GLOB_DAT 20 +#define R_PPC_JMP_SLOT 21 +#define R_PPC_RELATIVE 22 +#define R_PPC_LOCAL24PC 23 +#define R_PPC_UADDR32 24 +#define R_PPC_UADDR16 25 +#define R_PPC_REL32 26 +#define R_PPC_PLT32 27 +#define R_PPC_PLTREL32 28 +#define R_PPC_PLT16_LO 29 +#define R_PPC_PLT16_HI 30 +#define R_PPC_PLT16_HA 31 +#define R_PPC_SDAREL16 32 +#define R_PPC_SECTOFF 33 +#define R_PPC_SECTOFF_LO 34 +#define R_PPC_SECTOFF_HI 35 +#define R_PPC_SECTOFF_HA 36 + + +#define R_PPC_TLS 67 +#define R_PPC_DTPMOD32 68 +#define R_PPC_TPREL16 69 +#define R_PPC_TPREL16_LO 70 +#define R_PPC_TPREL16_HI 71 +#define R_PPC_TPREL16_HA 72 +#define R_PPC_TPREL32 73 +#define R_PPC_DTPREL16 74 +#define R_PPC_DTPREL16_LO 75 +#define R_PPC_DTPREL16_HI 76 +#define R_PPC_DTPREL16_HA 77 +#define R_PPC_DTPREL32 78 +#define R_PPC_GOT_TLSGD16 79 +#define R_PPC_GOT_TLSGD16_LO 80 +#define R_PPC_GOT_TLSGD16_HI 81 +#define R_PPC_GOT_TLSGD16_HA 82 +#define R_PPC_GOT_TLSLD16 83 +#define R_PPC_GOT_TLSLD16_LO 84 +#define R_PPC_GOT_TLSLD16_HI 85 +#define R_PPC_GOT_TLSLD16_HA 86 +#define R_PPC_GOT_TPREL16 87 +#define R_PPC_GOT_TPREL16_LO 88 +#define R_PPC_GOT_TPREL16_HI 89 +#define R_PPC_GOT_TPREL16_HA 90 +#define R_PPC_GOT_DTPREL16 91 +#define R_PPC_GOT_DTPREL16_LO 92 +#define R_PPC_GOT_DTPREL16_HI 93 +#define R_PPC_GOT_DTPREL16_HA 94 + + + +#define R_PPC_EMB_NADDR32 101 +#define R_PPC_EMB_NADDR16 102 +#define R_PPC_EMB_NADDR16_LO 103 +#define R_PPC_EMB_NADDR16_HI 104 +#define R_PPC_EMB_NADDR16_HA 105 +#define R_PPC_EMB_SDAI16 106 +#define R_PPC_EMB_SDA2I16 107 +#define R_PPC_EMB_SDA2REL 108 +#define R_PPC_EMB_SDA21 109 +#define R_PPC_EMB_MRKREF 110 +#define R_PPC_EMB_RELSEC16 111 +#define R_PPC_EMB_RELST_LO 112 +#define R_PPC_EMB_RELST_HI 113 +#define R_PPC_EMB_RELST_HA 114 +#define R_PPC_EMB_BIT_FLD 115 +#define R_PPC_EMB_RELSDA 116 + + +#define R_PPC_DIAB_SDA21_LO 180 +#define R_PPC_DIAB_SDA21_HI 181 +#define R_PPC_DIAB_SDA21_HA 182 +#define R_PPC_DIAB_RELSDA_LO 183 +#define R_PPC_DIAB_RELSDA_HI 184 +#define R_PPC_DIAB_RELSDA_HA 185 + + +#define R_PPC_IRELATIVE 248 + + +#define R_PPC_REL16 249 +#define R_PPC_REL16_LO 250 +#define R_PPC_REL16_HI 251 +#define R_PPC_REL16_HA 252 + + + +#define R_PPC_TOC16 255 + + +#define DT_PPC_GOT (DT_LOPROC + 0) +#define DT_PPC_NUM 1 + + +#define R_PPC64_NONE R_PPC_NONE +#define R_PPC64_ADDR32 R_PPC_ADDR32 +#define R_PPC64_ADDR24 R_PPC_ADDR24 +#define R_PPC64_ADDR16 R_PPC_ADDR16 +#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO +#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI +#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA +#define R_PPC64_ADDR14 R_PPC_ADDR14 +#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN +#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN +#define R_PPC64_REL24 R_PPC_REL24 +#define R_PPC64_REL14 R_PPC_REL14 +#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN +#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN +#define R_PPC64_GOT16 R_PPC_GOT16 +#define R_PPC64_GOT16_LO R_PPC_GOT16_LO +#define R_PPC64_GOT16_HI R_PPC_GOT16_HI +#define R_PPC64_GOT16_HA R_PPC_GOT16_HA + +#define R_PPC64_COPY R_PPC_COPY +#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT +#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT +#define R_PPC64_RELATIVE R_PPC_RELATIVE + +#define R_PPC64_UADDR32 R_PPC_UADDR32 +#define R_PPC64_UADDR16 R_PPC_UADDR16 +#define R_PPC64_REL32 R_PPC_REL32 +#define R_PPC64_PLT32 R_PPC_PLT32 +#define R_PPC64_PLTREL32 R_PPC_PLTREL32 +#define R_PPC64_PLT16_LO R_PPC_PLT16_LO +#define R_PPC64_PLT16_HI R_PPC_PLT16_HI +#define R_PPC64_PLT16_HA R_PPC_PLT16_HA + +#define R_PPC64_SECTOFF R_PPC_SECTOFF +#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO +#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI +#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA +#define R_PPC64_ADDR30 37 +#define R_PPC64_ADDR64 38 +#define R_PPC64_ADDR16_HIGHER 39 +#define R_PPC64_ADDR16_HIGHERA 40 +#define R_PPC64_ADDR16_HIGHEST 41 +#define R_PPC64_ADDR16_HIGHESTA 42 +#define R_PPC64_UADDR64 43 +#define R_PPC64_REL64 44 +#define R_PPC64_PLT64 45 +#define R_PPC64_PLTREL64 46 +#define R_PPC64_TOC16 47 +#define R_PPC64_TOC16_LO 48 +#define R_PPC64_TOC16_HI 49 +#define R_PPC64_TOC16_HA 50 +#define R_PPC64_TOC 51 +#define R_PPC64_PLTGOT16 52 +#define R_PPC64_PLTGOT16_LO 53 +#define R_PPC64_PLTGOT16_HI 54 +#define R_PPC64_PLTGOT16_HA 55 + +#define R_PPC64_ADDR16_DS 56 +#define R_PPC64_ADDR16_LO_DS 57 +#define R_PPC64_GOT16_DS 58 +#define R_PPC64_GOT16_LO_DS 59 +#define R_PPC64_PLT16_LO_DS 60 +#define R_PPC64_SECTOFF_DS 61 +#define R_PPC64_SECTOFF_LO_DS 62 +#define R_PPC64_TOC16_DS 63 +#define R_PPC64_TOC16_LO_DS 64 +#define R_PPC64_PLTGOT16_DS 65 +#define R_PPC64_PLTGOT16_LO_DS 66 + + +#define R_PPC64_TLS 67 +#define R_PPC64_DTPMOD64 68 +#define R_PPC64_TPREL16 69 +#define R_PPC64_TPREL16_LO 70 +#define R_PPC64_TPREL16_HI 71 +#define R_PPC64_TPREL16_HA 72 +#define R_PPC64_TPREL64 73 +#define R_PPC64_DTPREL16 74 +#define R_PPC64_DTPREL16_LO 75 +#define R_PPC64_DTPREL16_HI 76 +#define R_PPC64_DTPREL16_HA 77 +#define R_PPC64_DTPREL64 78 +#define R_PPC64_GOT_TLSGD16 79 +#define R_PPC64_GOT_TLSGD16_LO 80 +#define R_PPC64_GOT_TLSGD16_HI 81 +#define R_PPC64_GOT_TLSGD16_HA 82 +#define R_PPC64_GOT_TLSLD16 83 +#define R_PPC64_GOT_TLSLD16_LO 84 +#define R_PPC64_GOT_TLSLD16_HI 85 +#define R_PPC64_GOT_TLSLD16_HA 86 +#define R_PPC64_GOT_TPREL16_DS 87 +#define R_PPC64_GOT_TPREL16_LO_DS 88 +#define R_PPC64_GOT_TPREL16_HI 89 +#define R_PPC64_GOT_TPREL16_HA 90 +#define R_PPC64_GOT_DTPREL16_DS 91 +#define R_PPC64_GOT_DTPREL16_LO_DS 92 +#define R_PPC64_GOT_DTPREL16_HI 93 +#define R_PPC64_GOT_DTPREL16_HA 94 +#define R_PPC64_TPREL16_DS 95 +#define R_PPC64_TPREL16_LO_DS 96 +#define R_PPC64_TPREL16_HIGHER 97 +#define R_PPC64_TPREL16_HIGHERA 98 +#define R_PPC64_TPREL16_HIGHEST 99 +#define R_PPC64_TPREL16_HIGHESTA 100 +#define R_PPC64_DTPREL16_DS 101 +#define R_PPC64_DTPREL16_LO_DS 102 +#define R_PPC64_DTPREL16_HIGHER 103 +#define R_PPC64_DTPREL16_HIGHERA 104 +#define R_PPC64_DTPREL16_HIGHEST 105 +#define R_PPC64_DTPREL16_HIGHESTA 106 + + +#define R_PPC64_JMP_IREL 247 +#define R_PPC64_IRELATIVE 248 +#define R_PPC64_REL16 249 +#define R_PPC64_REL16_LO 250 +#define R_PPC64_REL16_HI 251 +#define R_PPC64_REL16_HA 252 + + +#define DT_PPC64_GLINK (DT_LOPROC + 0) +#define DT_PPC64_OPD (DT_LOPROC + 1) +#define DT_PPC64_OPDSZ (DT_LOPROC + 2) +#define DT_PPC64_NUM 3 + + + + + +#define EF_ARM_RELEXEC 0x01 +#define EF_ARM_HASENTRY 0x02 +#define EF_ARM_INTERWORK 0x04 +#define EF_ARM_APCS_26 0x08 +#define EF_ARM_APCS_FLOAT 0x10 +#define EF_ARM_PIC 0x20 +#define EF_ARM_ALIGN8 0x40 +#define EF_ARM_NEW_ABI 0x80 +#define EF_ARM_OLD_ABI 0x100 +#define EF_ARM_SOFT_FLOAT 0x200 +#define EF_ARM_VFP_FLOAT 0x400 +#define EF_ARM_MAVERICK_FLOAT 0x800 + +#define EF_ARM_ABI_FLOAT_SOFT 0x200 +#define EF_ARM_ABI_FLOAT_HARD 0x400 + + +#define EF_ARM_SYMSARESORTED 0x04 +#define EF_ARM_DYNSYMSUSESEGIDX 0x08 +#define EF_ARM_MAPSYMSFIRST 0x10 +#define EF_ARM_EABIMASK 0XFF000000 + + +#define EF_ARM_BE8 0x00800000 +#define EF_ARM_LE8 0x00400000 + +#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) +#define EF_ARM_EABI_UNKNOWN 0x00000000 +#define EF_ARM_EABI_VER1 0x01000000 +#define EF_ARM_EABI_VER2 0x02000000 +#define EF_ARM_EABI_VER3 0x03000000 +#define EF_ARM_EABI_VER4 0x04000000 +#define EF_ARM_EABI_VER5 0x05000000 + + +#define STT_ARM_TFUNC STT_LOPROC +#define STT_ARM_16BIT STT_HIPROC + + +#define SHF_ARM_ENTRYSECT 0x10000000 +#define SHF_ARM_COMDEF 0x80000000 + + + +#define PF_ARM_SB 0x10000000 + +#define PF_ARM_PI 0x20000000 +#define PF_ARM_ABS 0x40000000 + + +#define PT_ARM_EXIDX (PT_LOPROC + 1) + + +#define SHT_ARM_EXIDX (SHT_LOPROC + 1) +#define SHT_ARM_PREEMPTMAP (SHT_LOPROC + 2) +#define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) + + +#define R_AARCH64_NONE 0 +#define R_AARCH64_ABS64 257 +#define R_AARCH64_ABS32 258 +#define R_AARCH64_ABS16 259 +#define R_AARCH64_PREL64 260 +#define R_AARCH64_PREL32 261 +#define R_AARCH64_PREL16 262 +#define R_AARCH64_MOVW_UABS_G0 263 +#define R_AARCH64_MOVW_UABS_G0_NC 264 +#define R_AARCH64_MOVW_UABS_G1 265 +#define R_AARCH64_MOVW_UABS_G1_NC 266 +#define R_AARCH64_MOVW_UABS_G2 267 +#define R_AARCH64_MOVW_UABS_G2_NC 268 +#define R_AARCH64_MOVW_UABS_G3 269 +#define R_AARCH64_MOVW_SABS_G0 270 +#define R_AARCH64_MOVW_SABS_G1 271 +#define R_AARCH64_MOVW_SABS_G2 272 +#define R_AARCH64_LD_PREL_LO19 273 +#define R_AARCH64_ADR_PREL_LO21 274 +#define R_AARCH64_ADR_PREL_PG_HI21 275 +#define R_AARCH64_ADR_PREL_PG_HI21_NC 276 +#define R_AARCH64_ADD_ABS_LO12_NC 277 +#define R_AARCH64_LDST8_ABS_LO12_NC 278 +#define R_AARCH64_TSTBR14 279 +#define R_AARCH64_CONDBR19 280 +#define R_AARCH64_JUMP26 282 +#define R_AARCH64_CALL26 283 +#define R_AARCH64_LDST16_ABS_LO12_NC 284 +#define R_AARCH64_LDST32_ABS_LO12_NC 285 +#define R_AARCH64_LDST64_ABS_LO12_NC 286 +#define R_AARCH64_MOVW_PREL_G0 287 +#define R_AARCH64_MOVW_PREL_G0_NC 288 +#define R_AARCH64_MOVW_PREL_G1 289 +#define R_AARCH64_MOVW_PREL_G1_NC 290 +#define R_AARCH64_MOVW_PREL_G2 291 +#define R_AARCH64_MOVW_PREL_G2_NC 292 +#define R_AARCH64_MOVW_PREL_G3 293 +#define R_AARCH64_LDST128_ABS_LO12_NC 299 +#define R_AARCH64_MOVW_GOTOFF_G0 300 +#define R_AARCH64_MOVW_GOTOFF_G0_NC 301 +#define R_AARCH64_MOVW_GOTOFF_G1 302 +#define R_AARCH64_MOVW_GOTOFF_G1_NC 303 +#define R_AARCH64_MOVW_GOTOFF_G2 304 +#define R_AARCH64_MOVW_GOTOFF_G2_NC 305 +#define R_AARCH64_MOVW_GOTOFF_G3 306 +#define R_AARCH64_GOTREL64 307 +#define R_AARCH64_GOTREL32 308 +#define R_AARCH64_GOT_LD_PREL19 309 +#define R_AARCH64_LD64_GOTOFF_LO15 310 +#define R_AARCH64_ADR_GOT_PAGE 311 +#define R_AARCH64_LD64_GOT_LO12_NC 312 +#define R_AARCH64_LD64_GOTPAGE_LO15 313 +#define R_AARCH64_TLSGD_ADR_PREL21 512 +#define R_AARCH64_TLSGD_ADR_PAGE21 513 +#define R_AARCH64_TLSGD_ADD_LO12_NC 514 +#define R_AARCH64_TLSGD_MOVW_G1 515 +#define R_AARCH64_TLSGD_MOVW_G0_NC 516 +#define R_AARCH64_TLSLD_ADR_PREL21 517 +#define R_AARCH64_TLSLD_ADR_PAGE21 518 +#define R_AARCH64_TLSLD_ADD_LO12_NC 519 +#define R_AARCH64_TLSLD_MOVW_G1 520 +#define R_AARCH64_TLSLD_MOVW_G0_NC 521 +#define R_AARCH64_TLSLD_LD_PREL19 522 +#define R_AARCH64_TLSLD_MOVW_DTPREL_G2 523 +#define R_AARCH64_TLSLD_MOVW_DTPREL_G1 524 +#define R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC 525 +#define R_AARCH64_TLSLD_MOVW_DTPREL_G0 526 +#define R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC 527 +#define R_AARCH64_TLSLD_ADD_DTPREL_HI12 528 +#define R_AARCH64_TLSLD_ADD_DTPREL_LO12 529 +#define R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC 530 +#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12 531 +#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC 532 +#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12 533 +#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC 534 +#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12 535 +#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC 536 +#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12 537 +#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC 538 +#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G1 539 +#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC 540 +#define R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 541 +#define R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC 542 +#define R_AARCH64_TLSIE_LD_GOTTPREL_PREL19 543 +#define R_AARCH64_TLSLE_MOVW_TPREL_G2 544 +#define R_AARCH64_TLSLE_MOVW_TPREL_G1 545 +#define R_AARCH64_TLSLE_MOVW_TPREL_G1_NC 546 +#define R_AARCH64_TLSLE_MOVW_TPREL_G0 547 +#define R_AARCH64_TLSLE_MOVW_TPREL_G0_NC 548 +#define R_AARCH64_TLSLE_ADD_TPREL_HI12 549 +#define R_AARCH64_TLSLE_ADD_TPREL_LO12 550 +#define R_AARCH64_TLSLE_ADD_TPREL_LO12_NC 551 +#define R_AARCH64_TLSLE_LDST8_TPREL_LO12 552 +#define R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC 553 +#define R_AARCH64_TLSLE_LDST16_TPREL_LO12 554 +#define R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC 555 +#define R_AARCH64_TLSLE_LDST32_TPREL_LO12 556 +#define R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC 557 +#define R_AARCH64_TLSLE_LDST64_TPREL_LO12 558 +#define R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC 559 +#define R_AARCH64_TLSDESC_LD_PREL19 560 +#define R_AARCH64_TLSDESC_ADR_PREL21 561 +#define R_AARCH64_TLSDESC_ADR_PAGE21 562 +#define R_AARCH64_TLSDESC_LD64_LO12 563 +#define R_AARCH64_TLSDESC_ADD_LO12 564 +#define R_AARCH64_TLSDESC_OFF_G1 565 +#define R_AARCH64_TLSDESC_OFF_G0_NC 566 +#define R_AARCH64_TLSDESC_LDR 567 +#define R_AARCH64_TLSDESC_ADD 568 +#define R_AARCH64_TLSDESC_CALL 569 +#define R_AARCH64_TLSLE_LDST128_TPREL_LO12 570 +#define R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC 571 +#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12 572 +#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC 573 +#define R_AARCH64_COPY 1024 +#define R_AARCH64_GLOB_DAT 1025 +#define R_AARCH64_JUMP_SLOT 1026 +#define R_AARCH64_RELATIVE 1027 +#define R_AARCH64_TLS_DTPMOD64 1028 +#define R_AARCH64_TLS_DTPREL64 1029 +#define R_AARCH64_TLS_TPREL64 1030 +#define R_AARCH64_TLSDESC 1031 + + +#define R_ARM_NONE 0 +#define R_ARM_PC24 1 +#define R_ARM_ABS32 2 +#define R_ARM_REL32 3 +#define R_ARM_PC13 4 +#define R_ARM_ABS16 5 +#define R_ARM_ABS12 6 +#define R_ARM_THM_ABS5 7 +#define R_ARM_ABS8 8 +#define R_ARM_SBREL32 9 +#define R_ARM_THM_PC22 10 +#define R_ARM_THM_PC8 11 +#define R_ARM_AMP_VCALL9 12 +#define R_ARM_TLS_DESC 13 +#define R_ARM_THM_SWI8 14 +#define R_ARM_XPC25 15 +#define R_ARM_THM_XPC22 16 +#define R_ARM_TLS_DTPMOD32 17 +#define R_ARM_TLS_DTPOFF32 18 +#define R_ARM_TLS_TPOFF32 19 +#define R_ARM_COPY 20 +#define R_ARM_GLOB_DAT 21 +#define R_ARM_JUMP_SLOT 22 +#define R_ARM_RELATIVE 23 +#define R_ARM_GOTOFF 24 +#define R_ARM_GOTPC 25 +#define R_ARM_GOT32 26 +#define R_ARM_PLT32 27 +#define R_ARM_CALL 28 +#define R_ARM_JUMP24 29 +#define R_ARM_THM_JUMP24 30 +#define R_ARM_BASE_ABS 31 +#define R_ARM_ALU_PCREL_7_0 32 +#define R_ARM_ALU_PCREL_15_8 33 +#define R_ARM_ALU_PCREL_23_15 34 +#define R_ARM_LDR_SBREL_11_0 35 +#define R_ARM_ALU_SBREL_19_12 36 +#define R_ARM_ALU_SBREL_27_20 37 +#define R_ARM_TARGET1 38 +#define R_ARM_SBREL31 39 +#define R_ARM_V4BX 40 +#define R_ARM_TARGET2 41 +#define R_ARM_PREL31 42 +#define R_ARM_MOVW_ABS_NC 43 +#define R_ARM_MOVT_ABS 44 +#define R_ARM_MOVW_PREL_NC 45 +#define R_ARM_MOVT_PREL 46 +#define R_ARM_THM_MOVW_ABS_NC 47 +#define R_ARM_THM_MOVT_ABS 48 +#define R_ARM_THM_MOVW_PREL_NC 49 +#define R_ARM_THM_MOVT_PREL 50 +#define R_ARM_THM_JUMP19 51 +#define R_ARM_THM_JUMP6 52 +#define R_ARM_THM_ALU_PREL_11_0 53 +#define R_ARM_THM_PC12 54 +#define R_ARM_ABS32_NOI 55 +#define R_ARM_REL32_NOI 56 +#define R_ARM_ALU_PC_G0_NC 57 +#define R_ARM_ALU_PC_G0 58 +#define R_ARM_ALU_PC_G1_NC 59 +#define R_ARM_ALU_PC_G1 60 +#define R_ARM_ALU_PC_G2 61 +#define R_ARM_LDR_PC_G1 62 +#define R_ARM_LDR_PC_G2 63 +#define R_ARM_LDRS_PC_G0 64 +#define R_ARM_LDRS_PC_G1 65 +#define R_ARM_LDRS_PC_G2 66 +#define R_ARM_LDC_PC_G0 67 +#define R_ARM_LDC_PC_G1 68 +#define R_ARM_LDC_PC_G2 69 +#define R_ARM_ALU_SB_G0_NC 70 +#define R_ARM_ALU_SB_G0 71 +#define R_ARM_ALU_SB_G1_NC 72 +#define R_ARM_ALU_SB_G1 73 +#define R_ARM_ALU_SB_G2 74 +#define R_ARM_LDR_SB_G0 75 +#define R_ARM_LDR_SB_G1 76 +#define R_ARM_LDR_SB_G2 77 +#define R_ARM_LDRS_SB_G0 78 +#define R_ARM_LDRS_SB_G1 79 +#define R_ARM_LDRS_SB_G2 80 +#define R_ARM_LDC_SB_G0 81 +#define R_ARM_LDC_SB_G1 82 +#define R_ARM_LDC_SB_G2 83 +#define R_ARM_MOVW_BREL_NC 84 +#define R_ARM_MOVT_BREL 85 +#define R_ARM_MOVW_BREL 86 +#define R_ARM_THM_MOVW_BREL_NC 87 +#define R_ARM_THM_MOVT_BREL 88 +#define R_ARM_THM_MOVW_BREL 89 +#define R_ARM_TLS_GOTDESC 90 +#define R_ARM_TLS_CALL 91 +#define R_ARM_TLS_DESCSEQ 92 +#define R_ARM_THM_TLS_CALL 93 +#define R_ARM_PLT32_ABS 94 +#define R_ARM_GOT_ABS 95 +#define R_ARM_GOT_PREL 96 +#define R_ARM_GOT_BREL12 97 +#define R_ARM_GOTOFF12 98 +#define R_ARM_GOTRELAX 99 +#define R_ARM_GNU_VTENTRY 100 +#define R_ARM_GNU_VTINHERIT 101 +#define R_ARM_THM_PC11 102 +#define R_ARM_THM_PC9 103 +#define R_ARM_TLS_GD32 104 + +#define R_ARM_TLS_LDM32 105 + +#define R_ARM_TLS_LDO32 106 + +#define R_ARM_TLS_IE32 107 + +#define R_ARM_TLS_LE32 108 +#define R_ARM_TLS_LDO12 109 +#define R_ARM_TLS_LE12 110 +#define R_ARM_TLS_IE12GP 111 +#define R_ARM_ME_TOO 128 +#define R_ARM_THM_TLS_DESCSEQ 129 +#define R_ARM_THM_TLS_DESCSEQ16 129 +#define R_ARM_THM_TLS_DESCSEQ32 130 +#define R_ARM_THM_GOT_BREL12 131 +#define R_ARM_IRELATIVE 160 +#define R_ARM_RXPC25 249 +#define R_ARM_RSBREL32 250 +#define R_ARM_THM_RPC22 251 +#define R_ARM_RREL32 252 +#define R_ARM_RABS22 253 +#define R_ARM_RPC24 254 +#define R_ARM_RBASE 255 + +#define R_ARM_NUM 256 + + + + +#define EF_IA_64_MASKOS 0x0000000f +#define EF_IA_64_ABI64 0x00000010 +#define EF_IA_64_ARCH 0xff000000 + + +#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) +#define PT_IA_64_UNWIND (PT_LOPROC + 1) +#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12) +#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13) +#define PT_IA_64_HP_STACK (PT_LOOS + 0x14) + + +#define PF_IA_64_NORECOV 0x80000000 + + +#define SHT_IA_64_EXT (SHT_LOPROC + 0) +#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) + + +#define SHF_IA_64_SHORT 0x10000000 +#define SHF_IA_64_NORECOV 0x20000000 + + +#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) +#define DT_IA_64_NUM 1 + + +#define R_IA64_NONE 0x00 +#define R_IA64_IMM14 0x21 +#define R_IA64_IMM22 0x22 +#define R_IA64_IMM64 0x23 +#define R_IA64_DIR32MSB 0x24 +#define R_IA64_DIR32LSB 0x25 +#define R_IA64_DIR64MSB 0x26 +#define R_IA64_DIR64LSB 0x27 +#define R_IA64_GPREL22 0x2a +#define R_IA64_GPREL64I 0x2b +#define R_IA64_GPREL32MSB 0x2c +#define R_IA64_GPREL32LSB 0x2d +#define R_IA64_GPREL64MSB 0x2e +#define R_IA64_GPREL64LSB 0x2f +#define R_IA64_LTOFF22 0x32 +#define R_IA64_LTOFF64I 0x33 +#define R_IA64_PLTOFF22 0x3a +#define R_IA64_PLTOFF64I 0x3b +#define R_IA64_PLTOFF64MSB 0x3e +#define R_IA64_PLTOFF64LSB 0x3f +#define R_IA64_FPTR64I 0x43 +#define R_IA64_FPTR32MSB 0x44 +#define R_IA64_FPTR32LSB 0x45 +#define R_IA64_FPTR64MSB 0x46 +#define R_IA64_FPTR64LSB 0x47 +#define R_IA64_PCREL60B 0x48 +#define R_IA64_PCREL21B 0x49 +#define R_IA64_PCREL21M 0x4a +#define R_IA64_PCREL21F 0x4b +#define R_IA64_PCREL32MSB 0x4c +#define R_IA64_PCREL32LSB 0x4d +#define R_IA64_PCREL64MSB 0x4e +#define R_IA64_PCREL64LSB 0x4f +#define R_IA64_LTOFF_FPTR22 0x52 +#define R_IA64_LTOFF_FPTR64I 0x53 +#define R_IA64_LTOFF_FPTR32MSB 0x54 +#define R_IA64_LTOFF_FPTR32LSB 0x55 +#define R_IA64_LTOFF_FPTR64MSB 0x56 +#define R_IA64_LTOFF_FPTR64LSB 0x57 +#define R_IA64_SEGREL32MSB 0x5c +#define R_IA64_SEGREL32LSB 0x5d +#define R_IA64_SEGREL64MSB 0x5e +#define R_IA64_SEGREL64LSB 0x5f +#define R_IA64_SECREL32MSB 0x64 +#define R_IA64_SECREL32LSB 0x65 +#define R_IA64_SECREL64MSB 0x66 +#define R_IA64_SECREL64LSB 0x67 +#define R_IA64_REL32MSB 0x6c +#define R_IA64_REL32LSB 0x6d +#define R_IA64_REL64MSB 0x6e +#define R_IA64_REL64LSB 0x6f +#define R_IA64_LTV32MSB 0x74 +#define R_IA64_LTV32LSB 0x75 +#define R_IA64_LTV64MSB 0x76 +#define R_IA64_LTV64LSB 0x77 +#define R_IA64_PCREL21BI 0x79 +#define R_IA64_PCREL22 0x7a +#define R_IA64_PCREL64I 0x7b +#define R_IA64_IPLTMSB 0x80 +#define R_IA64_IPLTLSB 0x81 +#define R_IA64_COPY 0x84 +#define R_IA64_SUB 0x85 +#define R_IA64_LTOFF22X 0x86 +#define R_IA64_LDXMOV 0x87 +#define R_IA64_TPREL14 0x91 +#define R_IA64_TPREL22 0x92 +#define R_IA64_TPREL64I 0x93 +#define R_IA64_TPREL64MSB 0x96 +#define R_IA64_TPREL64LSB 0x97 +#define R_IA64_LTOFF_TPREL22 0x9a +#define R_IA64_DTPMOD64MSB 0xa6 +#define R_IA64_DTPMOD64LSB 0xa7 +#define R_IA64_LTOFF_DTPMOD22 0xaa +#define R_IA64_DTPREL14 0xb1 +#define R_IA64_DTPREL22 0xb2 +#define R_IA64_DTPREL64I 0xb3 +#define R_IA64_DTPREL32MSB 0xb4 +#define R_IA64_DTPREL32LSB 0xb5 +#define R_IA64_DTPREL64MSB 0xb6 +#define R_IA64_DTPREL64LSB 0xb7 +#define R_IA64_LTOFF_DTPREL22 0xba + + + + +#define R_SH_NONE 0 +#define R_SH_DIR32 1 +#define R_SH_REL32 2 +#define R_SH_DIR8WPN 3 +#define R_SH_IND12W 4 +#define R_SH_DIR8WPL 5 +#define R_SH_DIR8WPZ 6 +#define R_SH_DIR8BP 7 +#define R_SH_DIR8W 8 +#define R_SH_DIR8L 9 +#define R_SH_SWITCH16 25 +#define R_SH_SWITCH32 26 +#define R_SH_USES 27 +#define R_SH_COUNT 28 +#define R_SH_ALIGN 29 +#define R_SH_CODE 30 +#define R_SH_DATA 31 +#define R_SH_LABEL 32 +#define R_SH_SWITCH8 33 +#define R_SH_GNU_VTINHERIT 34 +#define R_SH_GNU_VTENTRY 35 +#define R_SH_TLS_GD_32 144 +#define R_SH_TLS_LD_32 145 +#define R_SH_TLS_LDO_32 146 +#define R_SH_TLS_IE_32 147 +#define R_SH_TLS_LE_32 148 +#define R_SH_TLS_DTPMOD32 149 +#define R_SH_TLS_DTPOFF32 150 +#define R_SH_TLS_TPOFF32 151 +#define R_SH_GOT32 160 +#define R_SH_PLT32 161 +#define R_SH_COPY 162 +#define R_SH_GLOB_DAT 163 +#define R_SH_JMP_SLOT 164 +#define R_SH_RELATIVE 165 +#define R_SH_GOTOFF 166 +#define R_SH_GOTPC 167 +#define R_SH_GOT20 201 +#define R_SH_GOTOFF20 202 +#define R_SH_GOTFUNCDESC 203 +#define R_SH_GOTFUNCDEST20 204 +#define R_SH_GOTOFFFUNCDESC 205 +#define R_SH_GOTOFFFUNCDEST20 206 +#define R_SH_FUNCDESC 207 +#define R_SH_FUNCDESC_VALUE 208 + +#define R_SH_NUM 256 + + + +#define R_390_NONE 0 +#define R_390_8 1 +#define R_390_12 2 +#define R_390_16 3 +#define R_390_32 4 +#define R_390_PC32 5 +#define R_390_GOT12 6 +#define R_390_GOT32 7 +#define R_390_PLT32 8 +#define R_390_COPY 9 +#define R_390_GLOB_DAT 10 +#define R_390_JMP_SLOT 11 +#define R_390_RELATIVE 12 +#define R_390_GOTOFF32 13 +#define R_390_GOTPC 14 +#define R_390_GOT16 15 +#define R_390_PC16 16 +#define R_390_PC16DBL 17 +#define R_390_PLT16DBL 18 +#define R_390_PC32DBL 19 +#define R_390_PLT32DBL 20 +#define R_390_GOTPCDBL 21 +#define R_390_64 22 +#define R_390_PC64 23 +#define R_390_GOT64 24 +#define R_390_PLT64 25 +#define R_390_GOTENT 26 +#define R_390_GOTOFF16 27 +#define R_390_GOTOFF64 28 +#define R_390_GOTPLT12 29 +#define R_390_GOTPLT16 30 +#define R_390_GOTPLT32 31 +#define R_390_GOTPLT64 32 +#define R_390_GOTPLTENT 33 +#define R_390_PLTOFF16 34 +#define R_390_PLTOFF32 35 +#define R_390_PLTOFF64 36 +#define R_390_TLS_LOAD 37 +#define R_390_TLS_GDCALL 38 + +#define R_390_TLS_LDCALL 39 + +#define R_390_TLS_GD32 40 + +#define R_390_TLS_GD64 41 + +#define R_390_TLS_GOTIE12 42 + +#define R_390_TLS_GOTIE32 43 + +#define R_390_TLS_GOTIE64 44 + +#define R_390_TLS_LDM32 45 + +#define R_390_TLS_LDM64 46 + +#define R_390_TLS_IE32 47 + +#define R_390_TLS_IE64 48 + +#define R_390_TLS_IEENT 49 + +#define R_390_TLS_LE32 50 + +#define R_390_TLS_LE64 51 + +#define R_390_TLS_LDO32 52 + +#define R_390_TLS_LDO64 53 + +#define R_390_TLS_DTPMOD 54 +#define R_390_TLS_DTPOFF 55 +#define R_390_TLS_TPOFF 56 + +#define R_390_20 57 +#define R_390_GOT20 58 +#define R_390_GOTPLT20 59 +#define R_390_TLS_GOTIE20 60 + + +#define R_390_NUM 61 + + + +#define R_CRIS_NONE 0 +#define R_CRIS_8 1 +#define R_CRIS_16 2 +#define R_CRIS_32 3 +#define R_CRIS_8_PCREL 4 +#define R_CRIS_16_PCREL 5 +#define R_CRIS_32_PCREL 6 +#define R_CRIS_GNU_VTINHERIT 7 +#define R_CRIS_GNU_VTENTRY 8 +#define R_CRIS_COPY 9 +#define R_CRIS_GLOB_DAT 10 +#define R_CRIS_JUMP_SLOT 11 +#define R_CRIS_RELATIVE 12 +#define R_CRIS_16_GOT 13 +#define R_CRIS_32_GOT 14 +#define R_CRIS_16_GOTPLT 15 +#define R_CRIS_32_GOTPLT 16 +#define R_CRIS_32_GOTREL 17 +#define R_CRIS_32_PLT_GOTREL 18 +#define R_CRIS_32_PLT_PCREL 19 + +#define R_CRIS_NUM 20 + + + +#define R_X86_64_NONE 0 +#define R_X86_64_64 1 +#define R_X86_64_PC32 2 +#define R_X86_64_GOT32 3 +#define R_X86_64_PLT32 4 +#define R_X86_64_COPY 5 +#define R_X86_64_GLOB_DAT 6 +#define R_X86_64_JUMP_SLOT 7 +#define R_X86_64_RELATIVE 8 +#define R_X86_64_GOTPCREL 9 + +#define R_X86_64_32 10 +#define R_X86_64_32S 11 +#define R_X86_64_16 12 +#define R_X86_64_PC16 13 +#define R_X86_64_8 14 +#define R_X86_64_PC8 15 +#define R_X86_64_DTPMOD64 16 +#define R_X86_64_DTPOFF64 17 +#define R_X86_64_TPOFF64 18 +#define R_X86_64_TLSGD 19 + +#define R_X86_64_TLSLD 20 + +#define R_X86_64_DTPOFF32 21 +#define R_X86_64_GOTTPOFF 22 + +#define R_X86_64_TPOFF32 23 +#define R_X86_64_PC64 24 +#define R_X86_64_GOTOFF64 25 +#define R_X86_64_GOTPC32 26 +#define R_X86_64_GOT64 27 +#define R_X86_64_GOTPCREL64 28 +#define R_X86_64_GOTPC64 29 +#define R_X86_64_GOTPLT64 30 +#define R_X86_64_PLTOFF64 31 +#define R_X86_64_SIZE32 32 +#define R_X86_64_SIZE64 33 + +#define R_X86_64_GOTPC32_TLSDESC 34 +#define R_X86_64_TLSDESC_CALL 35 + +#define R_X86_64_TLSDESC 36 +#define R_X86_64_IRELATIVE 37 +#define R_X86_64_RELATIVE64 38 +#define R_X86_64_NUM 39 + + + +#define R_MN10300_NONE 0 +#define R_MN10300_32 1 +#define R_MN10300_16 2 +#define R_MN10300_8 3 +#define R_MN10300_PCREL32 4 +#define R_MN10300_PCREL16 5 +#define R_MN10300_PCREL8 6 +#define R_MN10300_GNU_VTINHERIT 7 +#define R_MN10300_GNU_VTENTRY 8 +#define R_MN10300_24 9 +#define R_MN10300_GOTPC32 10 +#define R_MN10300_GOTPC16 11 +#define R_MN10300_GOTOFF32 12 +#define R_MN10300_GOTOFF24 13 +#define R_MN10300_GOTOFF16 14 +#define R_MN10300_PLT32 15 +#define R_MN10300_PLT16 16 +#define R_MN10300_GOT32 17 +#define R_MN10300_GOT24 18 +#define R_MN10300_GOT16 19 +#define R_MN10300_COPY 20 +#define R_MN10300_GLOB_DAT 21 +#define R_MN10300_JMP_SLOT 22 +#define R_MN10300_RELATIVE 23 + +#define R_MN10300_NUM 24 + + + +#define R_M32R_NONE 0 +#define R_M32R_16 1 +#define R_M32R_32 2 +#define R_M32R_24 3 +#define R_M32R_10_PCREL 4 +#define R_M32R_18_PCREL 5 +#define R_M32R_26_PCREL 6 +#define R_M32R_HI16_ULO 7 +#define R_M32R_HI16_SLO 8 +#define R_M32R_LO16 9 +#define R_M32R_SDA16 10 +#define R_M32R_GNU_VTINHERIT 11 +#define R_M32R_GNU_VTENTRY 12 + +#define R_M32R_16_RELA 33 +#define R_M32R_32_RELA 34 +#define R_M32R_24_RELA 35 +#define R_M32R_10_PCREL_RELA 36 +#define R_M32R_18_PCREL_RELA 37 +#define R_M32R_26_PCREL_RELA 38 +#define R_M32R_HI16_ULO_RELA 39 +#define R_M32R_HI16_SLO_RELA 40 +#define R_M32R_LO16_RELA 41 +#define R_M32R_SDA16_RELA 42 +#define R_M32R_RELA_GNU_VTINHERIT 43 +#define R_M32R_RELA_GNU_VTENTRY 44 +#define R_M32R_REL32 45 + +#define R_M32R_GOT24 48 +#define R_M32R_26_PLTREL 49 +#define R_M32R_COPY 50 +#define R_M32R_GLOB_DAT 51 +#define R_M32R_JMP_SLOT 52 +#define R_M32R_RELATIVE 53 +#define R_M32R_GOTOFF 54 +#define R_M32R_GOTPC24 55 +#define R_M32R_GOT16_HI_ULO 56 + +#define R_M32R_GOT16_HI_SLO 57 + +#define R_M32R_GOT16_LO 58 +#define R_M32R_GOTPC_HI_ULO 59 + +#define R_M32R_GOTPC_HI_SLO 60 + +#define R_M32R_GOTPC_LO 61 + +#define R_M32R_GOTOFF_HI_ULO 62 + +#define R_M32R_GOTOFF_HI_SLO 63 + +#define R_M32R_GOTOFF_LO 64 +#define R_M32R_NUM 256 + +#define R_MICROBLAZE_NONE 0 +#define R_MICROBLAZE_32 1 +#define R_MICROBLAZE_32_PCREL 2 +#define R_MICROBLAZE_64_PCREL 3 +#define R_MICROBLAZE_32_PCREL_LO 4 +#define R_MICROBLAZE_64 5 +#define R_MICROBLAZE_32_LO 6 +#define R_MICROBLAZE_SRO32 7 +#define R_MICROBLAZE_SRW32 8 +#define R_MICROBLAZE_64_NONE 9 +#define R_MICROBLAZE_32_SYM_OP_SYM 10 +#define R_MICROBLAZE_GNU_VTINHERIT 11 +#define R_MICROBLAZE_GNU_VTENTRY 12 +#define R_MICROBLAZE_GOTPC_64 13 +#define R_MICROBLAZE_GOT_64 14 +#define R_MICROBLAZE_PLT_64 15 +#define R_MICROBLAZE_REL 16 +#define R_MICROBLAZE_JUMP_SLOT 17 +#define R_MICROBLAZE_GLOB_DAT 18 +#define R_MICROBLAZE_GOTOFF_64 19 +#define R_MICROBLAZE_GOTOFF_32 20 +#define R_MICROBLAZE_COPY 21 +#define R_MICROBLAZE_TLS 22 +#define R_MICROBLAZE_TLSGD 23 +#define R_MICROBLAZE_TLSLD 24 +#define R_MICROBLAZE_TLSDTPMOD32 25 +#define R_MICROBLAZE_TLSDTPREL32 26 +#define R_MICROBLAZE_TLSDTPREL64 27 +#define R_MICROBLAZE_TLSGOTTPREL32 28 +#define R_MICROBLAZE_TLSTPREL32 29 + +#define R_OR1K_NONE 0 +#define R_OR1K_32 1 +#define R_OR1K_16 2 +#define R_OR1K_8 3 +#define R_OR1K_LO_16_IN_INSN 4 +#define R_OR1K_HI_16_IN_INSN 5 +#define R_OR1K_INSN_REL_26 6 +#define R_OR1K_GNU_VTENTRY 7 +#define R_OR1K_GNU_VTINHERIT 8 +#define R_OR1K_32_PCREL 9 +#define R_OR1K_16_PCREL 10 +#define R_OR1K_8_PCREL 11 +#define R_OR1K_GOTPC_HI16 12 +#define R_OR1K_GOTPC_LO16 13 +#define R_OR1K_GOT16 14 +#define R_OR1K_PLT26 15 +#define R_OR1K_GOTOFF_HI16 16 +#define R_OR1K_GOTOFF_LO16 17 +#define R_OR1K_COPY 18 +#define R_OR1K_GLOB_DAT 19 +#define R_OR1K_JMP_SLOT 20 +#define R_OR1K_RELATIVE 21 +#define R_OR1K_TLS_GD_HI16 22 +#define R_OR1K_TLS_GD_LO16 23 +#define R_OR1K_TLS_LDM_HI16 24 +#define R_OR1K_TLS_LDM_LO16 25 +#define R_OR1K_TLS_LDO_HI16 26 +#define R_OR1K_TLS_LDO_LO16 27 +#define R_OR1K_TLS_IE_HI16 28 +#define R_OR1K_TLS_IE_LO16 29 +#define R_OR1K_TLS_LE_HI16 30 +#define R_OR1K_TLS_LE_LO16 31 +#define R_OR1K_TLS_TPOFF 32 +#define R_OR1K_TLS_DTPOFF 33 +#define R_OR1K_TLS_DTPMOD 34 + +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/shared/sentry/external/crashpad/.clang-format b/shared/sentry/external/crashpad/.clang-format new file mode 100644 index 000000000..0be5323f5 --- /dev/null +++ b/shared/sentry/external/crashpad/.clang-format @@ -0,0 +1,19 @@ +# Copyright 2014 The Crashpad Authors. All rights reserved. +# +# 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. + +{ + BasedOnStyle: Chromium, + AlignTrailingComments: false, + BinPackArguments: false, +} diff --git a/shared/sentry/external/crashpad/.gitattributes b/shared/sentry/external/crashpad/.gitattributes new file mode 100644 index 000000000..9bfc20d15 --- /dev/null +++ b/shared/sentry/external/crashpad/.gitattributes @@ -0,0 +1,59 @@ +# Copyright 2019 The Crashpad Authors. All rights reserved. +# +# 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. + +*.S text eol=lf +*.asm text eol=lf +*.c text eol=lf +*.cc text eol=lf +*.cmx text eol=lf +*.css text eol=lf +*.defs text eol=lf +*.doxy text eol=lf +*.gn text eol=lf +*.gni text eol=lf +*.go text eol=lf +*.gyp text eol=lf +*.gypi text eol=lf +*.h text eol=lf +*.m text eol=lf +*.md text eol=lf +*.mm text eol=lf +*.pem text eol=lf +*.plist text eol=lf +*.proctype text eol=lf +*.py text eol=lf +*.sh text eol=lf +*.sym text eol=lf +*.txt text eol=lf +*.yaml text eol=lf +.clang-format text eol=lf +.gitattributes text eol=lf +.gitignore text eol=lf +.vpython text eol=lf +/AUTHORS text eol=lf +/CONTRIBUTORS text eol=lf +/LICENSE text eol=lf +/codereview.settings text eol=lf +APPLE_LICENSE text eol=lf +COPYING.LIB text eol=lf +DEPS text eol=lf +README text eol=lf +README.crashpad text eol=lf + +*.dat binary +*.dll binary +*.ico binary +*.obj binary +*.png binary +*.so binary diff --git a/shared/sentry/external/crashpad/.github/workflows/build.yml b/shared/sentry/external/crashpad/.github/workflows/build.yml new file mode 100644 index 000000000..cbefe8fea --- /dev/null +++ b/shared/sentry/external/crashpad/.github/workflows/build.yml @@ -0,0 +1,45 @@ +name: Build + +on: + push: + branches: + - getsentry + pull_request: + +jobs: + build: + strategy: + fail-fast: false + matrix: + platform: [ubuntu-latest, windows-latest, macos-latest] + runs-on: ${{ matrix.platform }} + steps: + - uses: actions/checkout@v2 + with: + submodules: "recursive" + + - name: Installing Linux Dependencies + if: ${{ runner.os == 'Linux' }} + run: | + sudo apt update + sudo apt install zlib1g-dev libcurl4-openssl-dev libssl-dev libunwind-dev pkg-config + + - name: Build crashpad + run: | + cmake -B cmake-build + cmake --build cmake-build --parallel + + - name: Build crashpad with client-side stack traces + run: | + cmake -B cmake-build-stacks -D CRASHPAD_ENABLE_STACKTRACE=ON + cmake --build cmake-build-stacks --parallel + + build-ios: + runs-on: macos-latest + steps: + - uses: actions/checkout@v2 + with: + submodules: "recursive" + - run: | + cmake -B crashpad-xcode -GXcode -DCMAKE_SYSTEM_NAME=iOS + xcodebuild build -project crashpad-xcode/crashpad.xcodeproj diff --git a/shared/sentry/external/crashpad/.gitignore b/shared/sentry/external/crashpad/.gitignore new file mode 100644 index 000000000..4ad416639 --- /dev/null +++ b/shared/sentry/external/crashpad/.gitignore @@ -0,0 +1,40 @@ +# Copyright 2014 The Crashpad Authors. All rights reserved. +# +# 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. + +*.Makefile +*.ninja +*.pyc +*.target.mk +*.xcodeproj +*~ +.*.sw? +.cache +.DS_Store +.gdb_history +.gdbinit +/Makefile +/out +/third_party/edo/edo +/third_party/fuchsia/.cipd +/third_party/fuchsia/clang +/third_party/fuchsia/qemu +/third_party/fuchsia/sdk +/third_party/googletest/googletest +/third_party/libfuzzer +/third_party/linux/.cipd +/third_party/linux/clang +/third_party/linux/sysroot +/third_party/gyp/gyp +/xcodebuild +tags diff --git a/shared/sentry/external/crashpad/.gitmodules b/shared/sentry/external/crashpad/.gitmodules new file mode 100644 index 000000000..d183aa487 --- /dev/null +++ b/shared/sentry/external/crashpad/.gitmodules @@ -0,0 +1,9 @@ +[submodule "third_party/mini_chromium/mini_chromium"] + path = third_party/mini_chromium/mini_chromium + url = https://chromium.googlesource.com/chromium/mini_chromium +[submodule "third_party/zlib/zlib"] + path = third_party/zlib/zlib + url = https://chromium.googlesource.com/chromium/src/third_party/zlib +[submodule "third_party/lss/lss"] + path = third_party/lss/lss + url = https://chromium.googlesource.com/linux-syscall-support diff --git a/shared/sentry/external/crashpad/.gn b/shared/sentry/external/crashpad/.gn new file mode 100644 index 000000000..e4dc49c31 --- /dev/null +++ b/shared/sentry/external/crashpad/.gn @@ -0,0 +1,16 @@ +# Copyright 2017 The Crashpad Authors. All rights reserved. +# +# 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. + +buildconfig = "//build/BUILDCONFIG.gn" +script_executable = "python3" diff --git a/shared/sentry/external/crashpad/.style.yapf b/shared/sentry/external/crashpad/.style.yapf new file mode 100644 index 000000000..8aaf9b62c --- /dev/null +++ b/shared/sentry/external/crashpad/.style.yapf @@ -0,0 +1,16 @@ +# Copyright 2020 The Crashpad Authors. All rights reserved. +# +# 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. + +[style] +based_on_style = google diff --git a/shared/sentry/external/crashpad/.vpython b/shared/sentry/external/crashpad/.vpython new file mode 100644 index 000000000..c2001fc5d --- /dev/null +++ b/shared/sentry/external/crashpad/.vpython @@ -0,0 +1,32 @@ +# Copyright 2018 The Crashpad Authors. All rights reserved. +# +# 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. + +# This is a vpython "spec" file. +# +# It describes patterns for python wheel dependencies of the python scripts. +# +# Read more about `vpython` and how to modify this file here: +# https://chromium.googlesource.com/infra/infra/+/master/doc/users/vpython.md + +# This is needed for snapshot/win/end_to_end_test.py. +wheel: < + name: "infra/python/wheels/pypiwin32/${vpython_platform}" + version: "version:219" + match_tag: < + platform: "win32" + > + match_tag: < + platform: "win_amd64" + > +> diff --git a/shared/sentry/external/crashpad/.vpython3 b/shared/sentry/external/crashpad/.vpython3 new file mode 100644 index 000000000..0d7baf9ef --- /dev/null +++ b/shared/sentry/external/crashpad/.vpython3 @@ -0,0 +1,32 @@ +# Copyright 2022 The Crashpad Authors. All rights reserved. +# +# 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. + +# This is a vpython "spec" file. +# +# It describes patterns for python wheel dependencies of the python scripts. +# +# Read more about `vpython` and how to modify this file here: +# https://chromium.googlesource.com/infra/infra/+/master/doc/users/vpython.md + +# This is needed for snapshot/win/end_to_end_test.py. +wheel: < + name: "infra/python/wheels/pywin32/${vpython_platform}" + version: "version:300" + match_tag: < + platform: "win32" + > + match_tag: < + platform: "win_amd64" + > +> diff --git a/shared/sentry/external/crashpad/AUTHORS b/shared/sentry/external/crashpad/AUTHORS new file mode 100644 index 000000000..8dcac3238 --- /dev/null +++ b/shared/sentry/external/crashpad/AUTHORS @@ -0,0 +1,14 @@ +# This is the official list of Crashpad authors for copyright purposes. +# This file is distinct from the CONTRIBUTORS files. +# See the latter for an explanation. + +# Names should be added to this file as: +# Name or Organization +# The email address is not required for organizations. + +Google Inc. +Intel Corporation +Opera Software ASA +Vewd Software AS +LG Electronics, Inc. +MIPS Technologies, Inc. diff --git a/shared/sentry/external/crashpad/BUILD.gn b/shared/sentry/external/crashpad/BUILD.gn new file mode 100644 index 000000000..61546be12 --- /dev/null +++ b/shared/sentry/external/crashpad/BUILD.gn @@ -0,0 +1,187 @@ +# Copyright 2017 The Crashpad Authors. All rights reserved. +# +# 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("build/crashpad_buildconfig.gni") +import("build/test.gni") +import("util/net/tls.gni") + +config("crashpad_config") { + include_dirs = [ + ".", + root_gen_dir, + ] +} + +if (crashpad_is_in_chromium || crashpad_is_in_fuchsia) { + test("crashpad_tests") { + deps = [ + "client:client_test", + "minidump:minidump_test", + "snapshot:snapshot_test", + "test:googlemock_main", + "test:test_test", + "util:util_test", + ] + if (!crashpad_is_ios && !crashpad_is_fuchsia) { + deps += [ "handler:handler_test" ] + } + if (crashpad_is_in_fuchsia) { + # TODO(fuchsia:46559): Fix the leaks and remove this. + deps += [ "//build/config/sanitizers:suppress-lsan.DO-NOT-USE-THIS" ] + } + if (crashpad_is_android) { + use_raw_android_executable = true + + copy("crashpad_test_data") { + testonly = true + sources = [ + "test/test_paths_test_data_root.txt", + "util/net/testdata/ascii_http_body.txt", + "util/net/testdata/binary_http_body.dat", + ] + + outputs = [ "$root_out_dir/crashpad_test_data/{{source}}" ] + } + + deps += [ ":crashpad_test_data" ] + + extra_dist_files = [ + "$root_out_dir/crashpad_handler", + "$root_out_dir/crashpad_test_test_multiprocess_exec_test_child", + "$root_out_dir/crashpad_test_data", + ] + } + } + + if (crashpad_is_in_fuchsia) { + import("//build/components.gni") + fuchsia_test_component("crashpad-test-component") { + manifest = "test/fuchsia_crashpad_tests.cmx" + deps = [ + ":crashpad-test-resources", + ":crashpad_tests", + "snapshot:crashpad_snapshot_test_both_dt_hash_styles", + "snapshot:crashpad_snapshot_test_module", + "snapshot:crashpad_snapshot_test_module_large", + "snapshot:crashpad_snapshot_test_module_small", + "test:crashpad_test_test_multiprocess_exec_test_child", + "util:http_transport_test_server", + ] + } + + fuchsia_test_package("crashpad-test") { + test_components = [ ":crashpad-test-component" ] + + deps = [ + "//src/connectivity/network/dns:component-legacy", + "//src/connectivity/network/netstack:component-legacy", + ] + + test_specs = { + log_settings = { + max_severity = "FATAL" + } + } + } + + _resource_files = [ + "test/test_paths_test_data_root.txt", + "util/net/testdata/ascii_http_body.txt", + "util/net/testdata/binary_http_body.dat", + ] + if (crashpad_use_boringssl_for_http_transport_socket) { + _resource_files += [ + "util/net/testdata/crashpad_util_test_cert.pem", + "util/net/testdata/crashpad_util_test_key.pem", + ] + } + + _resources = [] + foreach(resource_file, _resource_files) { + _resource_file_target = string_replace(resource_file, "/", "_") + resource("${_resource_file_target}") { + sources = [ "${resource_file}" ] + outputs = [ "data/${resource_file}" ] + } + _resources += [ ":${_resource_file_target}" ] + } + + group("crashpad-test-resources") { + deps = _resources + } + + fuchsia_shell_package("crashpad-database-util") { + package_name = "crashpad_database_util" + deps = [ "tools:crashpad_database_util" ] + } + + group("tests") { + testonly = true + + deps = [ ":crashpad-test" ] + } + } +} else if (crashpad_is_standalone || crashpad_is_external) { + test("crashpad_client_test") { + deps = [ + "client:client_test", + "test:googlemock_main", + ] + } + + test("crashpad_handler_test") { + deps = [ + "handler:handler_test", + "test:googletest_main", + ] + if (crashpad_is_ios || crashpad_is_fuchsia) { + deps -= [ "handler:handler_test" ] + } + } + + test("crashpad_minidump_test") { + deps = [ + "minidump:minidump_test", + "test:googletest_main", + ] + } + + test("crashpad_snapshot_test") { + deps = [ + "snapshot:snapshot_test", + "test:googlemock_main", + ] + } + + test("crashpad_test_test") { + deps = [ + "test:googlemock_main", + "test:test_test", + ] + } + + test("crashpad_util_test") { + deps = [ + "test:googlemock_main", + "util:util_test", + ] + } +} + +if (crashpad_is_ios) { + group("ios_xcuitests") { + testonly = true + deps = [ "test/ios:all_tests" ] + } +} diff --git a/shared/sentry/external/crashpad/CMakeLists.txt b/shared/sentry/external/crashpad/CMakeLists.txt new file mode 100644 index 000000000..b13b78fae --- /dev/null +++ b/shared/sentry/external/crashpad/CMakeLists.txt @@ -0,0 +1,137 @@ +cmake_minimum_required(VERSION 3.12) +project(crashpad LANGUAGES C CXX) + +if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(LINUX TRUE) +endif() + +set(CRASHPAD_MAIN_PROJECT OFF) +if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) + set(CRASHPAD_MAIN_PROJECT ON) +endif() + +option(CRASHPAD_ENABLE_INSTALL "Enable crashpad installation" "${CRASHPAD_MAIN_PROJECT}") +option(CRASHPAD_ENABLE_INSTALL_DEV "Enable crashpad development installation" "${CRASHPAD_MAIN_PROJECT}") +option(CRASHPAD_ENABLE_STACKTRACE "Enable client-side stack trace recording" OFF) + +if(MSVC) + set(CRASHPAD_ZLIB_SYSTEM_DEFAULT OFF) +else() + set(CRASHPAD_ZLIB_SYSTEM_DEFAULT ON) +endif() +option(CRASHPAD_ZLIB_SYSTEM "Use system zlib library" "${CRASHPAD_ZLIB_SYSTEM_DEFAULT}") + +if(CRASHPAD_ZLIB_SYSTEM) + find_package(ZLIB REQUIRED) +endif() + +if(LINUX OR ANDROID) + find_package(OpenSSL) + if(OPENSSL_FOUND) + set(CRASHPAD_USE_BORINGSSL ON) + endif() +endif() + +include(GNUInstallDirs) +set(CMAKE_INSTALL_CMAKEDIR "${CMAKE_INSTALL_LIBDIR}/cmake/crashpad") + +function(crashpad_install_target) + if(CRASHPAD_ENABLE_INSTALL) + install(TARGETS ${ARGN} EXPORT crashpad_export + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + ) + endif() +endfunction() +function(crashpad_install_dev) + if(CRASHPAD_ENABLE_INSTALL_DEV) + install(${ARGN}) + endif() +endfunction() + +if(WIN32) + enable_language(ASM_MASM) + if(MINGW) + if(NOT CMAKE_ASM_MASM_COMPILER OR CMAKE_ASM_MASM_COMPILER STREQUAL "ml" OR CMAKE_ASM_MASM_COMPILER STREQUAL "ml64") + message(WARNING "No custom ASM_MASM compiler defined via 'CMAKE_ASM_MASM_COMPILER'. Trying to use UASM...") + set(CMAKE_ASM_MASM_COMPILER "uasm") + endif() + if(NOT CMAKE_ASM_MASM_FLAGS) + set(CMAKE_ASM_MASM_FLAGS "-win64 -10") #use default compatibility flags + endif() + endif() +else() + enable_language(ASM) +endif() + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +add_library(crashpad_interface INTERFACE) +target_include_directories(crashpad_interface INTERFACE + $ + $ +) +target_compile_definitions(crashpad_interface INTERFACE + CRASHPAD_LSS_SOURCE_EMBEDDED +) + +if(MSVC) + target_compile_definitions(crashpad_interface INTERFACE + NOMINMAX + UNICODE + WIN32_LEAN_AND_MEAN + _CRT_SECURE_NO_WARNINGS + _HAS_EXCEPTIONS=0 + _UNICODE + ) + string(REGEX REPLACE "/[Ww][0123]" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") + string(REGEX REPLACE "/[Ww][0123]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + target_compile_options(crashpad_interface INTERFACE + $<$:/FS> + $<$:/W4> + $<$:/WX> + $<$:/bigobj> # Support larger number of sections in obj file. + $<$:/wd4100> # Unreferenced formal parameter. + $<$:/wd4127> # Conditional expression is constant. + $<$:/wd4324> # Structure was padded due to alignment specifier. + $<$:/wd4351> # New behavior: elements of array will be default initialized. + $<$:/wd4577> # 'noexcept' used with no exception handling mode specified. + $<$:/wd4996> # 'X' was declared deprecated. + ) +elseif(MINGW) + # redirect to wmain + # FIXME: cmake 3.13 added target_link_options + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -municode") +endif() +add_library(crashpad::interface ALIAS crashpad_interface) + +add_subdirectory(compat) +add_subdirectory(minidump) +add_subdirectory(snapshot) +add_subdirectory(util) +add_subdirectory(third_party/mini_chromium) +add_subdirectory(client) + +add_subdirectory(third_party/zlib) +add_subdirectory(third_party/getopt) + +add_subdirectory(tools) +add_subdirectory(handler) + +if(CRASHPAD_ENABLE_STACKTRACE AND APPLE AND NOT IOS) + set(LIBUNWIND_ENABLE_SHARED OFF) + add_subdirectory(libunwind) + crashpad_install_target(unwind_static) +endif() + +if(CRASHPAD_ENABLE_INSTALL_DEV) + install(EXPORT crashpad_export NAMESPACE crashpad:: FILE crashpad-targets.cmake + DESTINATION "${CMAKE_INSTALL_CMAKEDIR}") + include(CMakePackageConfigHelpers) + configure_package_config_file(crashpad-config.cmake.in crashpad-config.cmake + INSTALL_DESTINATION "${CMAKE_INSTALL_CMAKEDIR}" + ) + install(FILES "${CMAKE_CURRENT_BINARY_DIR}/crashpad-config.cmake" DESTINATION "${CMAKE_INSTALL_CMAKEDIR}") +endif() diff --git a/shared/sentry/external/crashpad/CONTRIBUTORS b/shared/sentry/external/crashpad/CONTRIBUTORS new file mode 100644 index 000000000..8724b7f32 --- /dev/null +++ b/shared/sentry/external/crashpad/CONTRIBUTORS @@ -0,0 +1,15 @@ +# People who have agreed to one of the CLAs and can contribute patches. +# The AUTHORS file lists the copyright holders; this file +# lists people. For example, Google employees are listed here +# but not in AUTHORS, because Google holds the copyright. +# +# https://developers.google.com/open-source/cla/individual +# https://developers.google.com/open-source/cla/corporate +# +# Names should be added to this file as: +# Name + +Mark Mentovai +Robert Sesek +Scott Graham +Joshua Peraza diff --git a/shared/sentry/external/crashpad/DEPS b/shared/sentry/external/crashpad/DEPS new file mode 100644 index 000000000..229922376 --- /dev/null +++ b/shared/sentry/external/crashpad/DEPS @@ -0,0 +1,180 @@ +# Copyright 2014 The Crashpad Authors. All rights reserved. +# +# 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. + +vars = { + 'chromium_git': 'https://chromium.googlesource.com', + 'pull_linux_clang': False, + 'pull_win_toolchain': False, + # Controls whether crashpad/build/ios/setup-ios-gn.py is run as part of + # gclient hooks. It is enabled by default for developer's convenience. It can + # be disabled with custom_vars (done automatically on the bots). + 'run_setup_ios_gn': True, +} + +deps = { + 'buildtools': + Var('chromium_git') + '/chromium/src/buildtools.git@' + + '9e121212d42be62a7cce38072f925f8398d11e49', + 'crashpad/third_party/edo/edo': { + 'url': Var('chromium_git') + '/external/github.com/google/eDistantObject.git@' + + '727e556705278598fce683522beedbb9946bfda0', + 'condition': 'checkout_ios', + }, + 'crashpad/third_party/googletest/googletest': + Var('chromium_git') + '/external/github.com/google/googletest@' + + 'af29db7ec28d6df1c7f0f745186884091e602e07', + 'crashpad/third_party/lss/lss': + Var('chromium_git') + '/linux-syscall-support.git@' + + 'e1e7b0ad8ee99a875b272c8e33e308472e897660', + 'crashpad/third_party/mini_chromium/mini_chromium': + Var('chromium_git') + '/chromium/mini_chromium@' + + '5654edb4225bcad13901155c819febb5748e502b', + 'crashpad/third_party/libfuzzer/src': + Var('chromium_git') + '/chromium/llvm-project/compiler-rt/lib/fuzzer.git@' + + 'fda403cf93ecb8792cb1d061564d89a6553ca020', + 'crashpad/third_party/zlib/zlib': + Var('chromium_git') + '/chromium/src/third_party/zlib@' + + '13dc246a58e4b72104d35f9b1809af95221ebda7', + + # CIPD packages below. + 'crashpad/third_party/linux/clang/linux-amd64': { + 'packages': [ + { + 'package': 'fuchsia/clang/linux-amd64', + 'version': 'goma', + }, + ], + 'condition': 'checkout_linux and pull_linux_clang', + 'dep_type': 'cipd' + }, + 'crashpad/third_party/fuchsia/clang/mac-amd64': { + 'packages': [ + { + 'package': 'fuchsia/clang/mac-amd64', + 'version': 'goma', + }, + ], + 'condition': 'checkout_fuchsia and host_os == "mac"', + 'dep_type': 'cipd' + }, + 'crashpad/third_party/fuchsia/clang/linux-amd64': { + 'packages': [ + { + 'package': 'fuchsia/clang/linux-amd64', + 'version': 'goma', + }, + ], + 'condition': 'checkout_fuchsia and host_os == "linux"', + 'dep_type': 'cipd' + }, + 'crashpad/third_party/fuchsia/sdk/mac-amd64': { + 'packages': [ + { + 'package': 'fuchsia/sdk/gn/mac-amd64', + 'version': 'latest' + }, + ], + 'condition': 'checkout_fuchsia and host_os == "mac"', + 'dep_type': 'cipd' + }, + 'crashpad/third_party/fuchsia/sdk/linux-amd64': { + 'packages': [ + { + 'package': 'fuchsia/sdk/gn/linux-amd64', + 'version': 'latest' + }, + ], + 'condition': 'checkout_fuchsia and host_os == "linux"', + 'dep_type': 'cipd' + }, + 'crashpad/third_party/win/toolchain': { + # This package is only updated when the solution in .gclient includes an + # entry like: + # "custom_vars": { "pull_win_toolchain": True } + # This is because the contained bits are not redistributable. + 'packages': [ + { + 'package': 'chrome_internal/third_party/sdk/windows', + 'version': 'uploaded:2021-04-28' + }, + ], + 'condition': 'checkout_win and pull_win_toolchain', + 'dep_type': 'cipd' + }, +} + +hooks = [ + { + 'name': 'clang_format_mac', + 'pattern': '.', + 'condition': 'host_os == "mac"', + 'action': [ + 'download_from_google_storage', + '--no_resume', + '--no_auth', + '--bucket=chromium-clang-format', + '--sha1_file', + 'buildtools/mac/clang-format.sha1', + ], + }, + { + 'name': 'clang_format_linux', + 'pattern': '.', + 'condition': 'host_os == "linux"', + 'action': [ + 'download_from_google_storage', + '--no_resume', + '--no_auth', + '--bucket=chromium-clang-format', + '--sha1_file', + 'buildtools/linux64/clang-format.sha1', + ], + }, + { + 'name': 'clang_format_win', + 'pattern': '.', + 'condition': 'host_os == "win"', + 'action': [ + 'download_from_google_storage', + '--no_resume', + '--no_auth', + '--bucket=chromium-clang-format', + '--sha1_file', + 'buildtools/win/clang-format.exe.sha1', + ], + }, + { + # If using a local clang ("pull_linux_clang" above), also pull down a + # sysroot. + 'name': 'sysroot_linux', + 'pattern': '.', + 'condition': 'checkout_linux and pull_linux_clang', + 'action': [ + 'crashpad/build/install_linux_sysroot.py', + ], + }, + { + 'name': 'setup_gn_ios', + 'pattern': '.', + 'condition': 'run_setup_ios_gn and checkout_ios', + 'action': [ + 'python3', + 'crashpad/build/ios/setup_ios_gn.py' + ], + }, +] + +recursedeps = [ + 'buildtools', +] diff --git a/shared/sentry/external/crashpad/LICENSE b/shared/sentry/external/crashpad/LICENSE new file mode 100644 index 000000000..d64569567 --- /dev/null +++ b/shared/sentry/external/crashpad/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/shared/sentry/external/crashpad/README.getsentry.md b/shared/sentry/external/crashpad/README.getsentry.md new file mode 100644 index 000000000..c7824e1ce --- /dev/null +++ b/shared/sentry/external/crashpad/README.getsentry.md @@ -0,0 +1,71 @@ +# Sentry Modifications + +- File attachments support for MacOS and Windows. Based on changes made in + https://github.com/Youw/crashpad/, distributed with Apache 2.0 License. +- Add `throws` declaration to `memfd_create` for compatibility with different + libc versions. +- Build System Changes Listed Below. +- MinGW build support. + +# Build System Changes + +In order to minimize external dependencies, and to better integrate with +`sentry-native`, this fork replaced usage of `depo_tools` with explicit +submodules, and added CMake files for building. + +Both submodules and CMake files currently only support building on macOS and +Windows, and do only export the necessary libraries and executables to +integrate the crashpad client. + +When updating this fork, make sure to keep the files in sync, as explained +below. + +## Submodules + +For macOS and Windows support, only `third_party/mini_chromium` and +`third_party/zlib` are needed. + +The specific submodule commit hashes can be found in the `./DEPS` file. + +## CMake Integration + +To allow building crashpad with CMake, the following CMake files were created +by manually translating the `BUILD.gn` files in the same folders (and following +included files): + +- `./CMakeLists.txt` +- `./client/CMakeLists.txt` +- `./compat/CMakeLists.txt` +- `./handler/CMakeLists.txt` +- `./minidump/CMakeLists.txt` +- `./snapshot/CMakeLists.txt` +- `./third_party/getopt/CMakeLists.txt` +- `./third_party/mini_chromium/CMakeLists.txt` +- `./third_party/zlib/CMakeLists.txt` +- `./tools/CMakeLists.txt` +- `./util/CMakeLists.txt` + +The important thing here is to keep the list of source files in sync when +updating. + +## MinGW Changes + +MinGW support adds the following files which need to be kept in sync. + +- `compat/mingw/` +- `third_party/mini_chromium/utf_string_conversion_utils.mingw.cc` + +## Building for iOS + +Build support for iOS, or Xcode in general is still a work in progress. +Once complete, creating a iOS compatible Xcode project should be as easy as: + + cmake -B cmakebuild -GXcode -DCMAKE_SYSTEM_NAME=iOS + +See the [upstream CMake Docs on iOS](https://cmake.org/cmake/help/v3.17/manual/cmake-toolchains.7.html#cross-compiling-for-ios-tvos-or-watchos) for further info. + +# How To Update + +- Bump the submodules to the commit hashes specified in `./DEPS` +- Go through the changes in `BUILD.gn` files, and apply them to the + corresponding `CMakeLists.txt`. See the list above. diff --git a/shared/sentry/external/crashpad/README.md b/shared/sentry/external/crashpad/README.md new file mode 100644 index 000000000..06a5d046e --- /dev/null +++ b/shared/sentry/external/crashpad/README.md @@ -0,0 +1,51 @@ + + +# Crashpad + +[Crashpad](https://crashpad.chromium.org/) is a crash-reporting system. + +## Documentation + + * [Project status](doc/status.md) + * [Developing Crashpad](doc/developing.md): instructions for getting the source + code, building, testing, and contributing to the project. + * [Crashpad interface documentation](https://crashpad.chromium.org/doxygen/) + * [Crashpad tool man pages](doc/man.md) + * [Crashpad overview design](doc/overview_design.md) + +## Source Code + +Crashpad’s source code is hosted in a Git repository at +https://chromium.googlesource.com/crashpad/crashpad. + +## Other Links + + * Bugs can be reported at the [Crashpad issue + tracker](https://crashpad.chromium.org/bug/). + * The [Crashpad bots](https://ci.chromium.org/p/crashpad/g/main/console) + perform automated builds and tests. + * [crashpad-dev](https://groups.google.com/a/chromium.org/group/crashpad-dev) + is the Crashpad developers’ mailing list. + +## Sentry modifications + +See [README.getsentry.md](README.getsentry.md) for more information on the +changes, and on maintaining the fork. + +Generating patch: + + git format-patch --stdout master...HEAD > getsentry.patch diff --git a/shared/sentry/external/crashpad/build/BUILD.gn b/shared/sentry/external/crashpad/build/BUILD.gn new file mode 100644 index 000000000..9f959ec93 --- /dev/null +++ b/shared/sentry/external/crashpad/build/BUILD.gn @@ -0,0 +1,68 @@ +# Copyright 2017 The Crashpad Authors. All rights reserved. +# +# 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. + +# When building in Chromium, these configs is used to set #defines that indicate +# whether code is being built standalone, or in Chromium, or potentially in some +# other configutation. + +import("crashpad_buildconfig.gni") + +config("crashpad_is_in_chromium") { + if (crashpad_is_in_chromium) { + defines = [ "CRASHPAD_IS_IN_CHROMIUM" ] + } +} + +config("crashpad_is_in_fuchsia") { + if (crashpad_is_in_fuchsia) { + defines = [ "CRASHPAD_IS_IN_FUCHSIA" ] + } +} + +group("default_exe_manifest_win") { + if (crashpad_is_in_chromium) { + deps = [ "//build/win:default_exe_manifest" ] + } +} + +config("crashpad_fuzzer_flags") { + cflags = [ + "-fsanitize=address", + "-fsanitize-address-use-after-scope", + "-fsanitize=fuzzer", + ] + + ldflags = [ "-fsanitize=address" ] +} + +if (crashpad_is_ios) { + group("ios_enable_arc") { + if (crashpad_is_in_chromium) { + public_configs = [ "//build/config/compiler:enable_arc" ] + } else if (crashpad_is_standalone) { + public_configs = + [ "//third_party/mini_chromium/mini_chromium/build/config:ios_enable_arc" ] + } + } + + group("ios_xctest") { + if (crashpad_is_in_chromium) { + public_configs = [ "//build/config/ios:xctest_config" ] + } else if (crashpad_is_standalone) { + public_configs = [ + "//third_party/mini_chromium/mini_chromium/build/ios:xctest_config", + ] + } + } +} diff --git a/shared/sentry/external/crashpad/build/BUILDCONFIG.gn b/shared/sentry/external/crashpad/build/BUILDCONFIG.gn new file mode 100644 index 000000000..d40a6ad2d --- /dev/null +++ b/shared/sentry/external/crashpad/build/BUILDCONFIG.gn @@ -0,0 +1,99 @@ +# Copyright 2017 The Crashpad Authors. All rights reserved. +# +# 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. + +# Intentionally very minimal, so that Crashpad can build in-tree in a variety of +# other projects, unrelated to the variables that are set in those projects' +# BUILDCONFIG.gn. Do not add more variables here. Instead, make them available +# in build/crashpad_buildconfig.gni if they must be globally available. + +if (target_os == "") { + target_os = host_os +} + +if (current_os == "") { + current_os = target_os +} + +if (target_cpu == "") { + target_cpu = host_cpu +} + +if (current_cpu == "") { + current_cpu = target_cpu +} + +import("//build/crashpad_buildconfig.gni") + +if (crashpad_is_standalone) { + _mini_chromium_dir = "//third_party/mini_chromium/mini_chromium" +} else if (crashpad_is_external) { + _mini_chromium_dir = "//../../mini_chromium/mini_chromium" +} + +if (current_os == "win") { + set_default_toolchain( + "$_mini_chromium_dir/build/config:msvc_toolchain_$current_cpu") +} else { + set_default_toolchain("$_mini_chromium_dir/build/config:gcc_like_toolchain") +} + +declare_args() { + # When true, enables the debug configuration, with additional run-time checks + # and logging. When false, enables the release configuration, with additional + # optimizations. + is_debug = false + + # When true, build all code with -fsanitize=fuzzer, and enable various + # *_fuzzer targets. + crashpad_use_libfuzzer = false +} + +_default_configs = [ + "$_mini_chromium_dir/build/config:default", + "$_mini_chromium_dir/build/config:Wexit_time_destructors", + "$_mini_chromium_dir/build/config:Wimplicit_fallthrough", +] + +if (crashpad_use_libfuzzer) { + _default_configs += [ "//build/config:crashpad_fuzzer_flags" ] +} + +_default_executable_configs = _default_configs + [ + "$_mini_chromium_dir/build/config:executable", + "$_mini_chromium_dir/build/config:win_console", + ] + +set_defaults("source_set") { + configs = _default_configs +} + +set_defaults("static_library") { + configs = _default_configs +} + +set_defaults("executable") { + configs = _default_executable_configs +} + +set_defaults("loadable_module") { + configs = _default_configs +} + +set_defaults("shared_library") { + configs = _default_configs +} + +set_defaults("test") { + configs = _default_executable_configs +} diff --git a/shared/sentry/external/crashpad/build/crashpad_buildconfig.gni b/shared/sentry/external/crashpad/build/crashpad_buildconfig.gni new file mode 100644 index 000000000..e6e51e855 --- /dev/null +++ b/shared/sentry/external/crashpad/build/crashpad_buildconfig.gni @@ -0,0 +1,160 @@ +# Copyright 2017 The Crashpad Authors. All rights reserved. +# +# 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. + +declare_args() { + # Determines various flavors of build configuration, and which concrete + # targets to use for dependencies. Valid values are "standalone", "chromium", + # "fuchsia", "dart" or "external". + crashpad_dependencies = "standalone" + + if (defined(is_fuchsia_tree) && is_fuchsia_tree) { + crashpad_dependencies = "fuchsia" + } +} + +assert( + crashpad_dependencies == "chromium" || crashpad_dependencies == "fuchsia" || + crashpad_dependencies == "standalone" || + crashpad_dependencies == "external" || crashpad_dependencies == "dart") + +crashpad_is_in_chromium = crashpad_dependencies == "chromium" +crashpad_is_in_fuchsia = crashpad_dependencies == "fuchsia" +crashpad_is_in_dart = crashpad_dependencies == "dart" +crashpad_is_external = crashpad_dependencies == "external" +crashpad_is_standalone = crashpad_dependencies == "standalone" + +# This is the parent directory that contains the mini_chromium source dir. +# This variable is not used when crashpad_is_in_chromium. +if (crashpad_is_in_fuchsia) { + mini_chromium_source_parent = "//third_party/crashpad/third_party/mini_chromium" +} else { + mini_chromium_source_parent = "../third_party/mini_chromium" +} + +# This is the source directory of mini_chromium (what is checked out). +_mini_chromium_source_root = "$mini_chromium_source_parent/mini_chromium" + +# This references the mini_chromium location for importing GN files. +if (crashpad_is_external || crashpad_is_in_dart) { + mini_chromium_import_root = "../../../$_mini_chromium_source_root" +} else if (crashpad_is_in_fuchsia) { + mini_chromium_import_root = "//third_party/mini_chromium" +} else { + mini_chromium_import_root = _mini_chromium_source_root +} + +if (crashpad_is_in_chromium) { + crashpad_is_mac = is_mac + crashpad_is_ios = is_ios + crashpad_is_win = is_win + crashpad_is_linux = is_linux || is_chromeos + crashpad_is_android = is_android + crashpad_is_fuchsia = is_fuchsia + + crashpad_is_posix = is_posix + + crashpad_is_clang = is_clang +} else { + import("$mini_chromium_import_root/build/compiler.gni") + import("$mini_chromium_import_root/build/platform.gni") + + crashpad_is_mac = mini_chromium_is_mac + crashpad_is_ios = mini_chromium_is_ios + crashpad_is_win = mini_chromium_is_win + crashpad_is_linux = mini_chromium_is_linux + crashpad_is_android = mini_chromium_is_android + crashpad_is_fuchsia = mini_chromium_is_fuchsia + + crashpad_is_posix = mini_chromium_is_posix + + crashpad_is_clang = mini_chromium_is_clang +} + +template("crashpad_executable") { + executable(target_name) { + forward_variables_from(invoker, + "*", + [ + "configs", + "remove_configs", + ]) + if (defined(invoker.remove_configs)) { + configs -= invoker.remove_configs + } + + if (defined(invoker.configs)) { + configs += invoker.configs + } + + if (crashpad_is_in_fuchsia) { + conversion_config = [ "//build/config:Wno-conversion" ] + if (configs + conversion_config - conversion_config == configs) { + # TODO(https://fxbug.dev/58162): Decide if these are worth enabling. + configs += conversion_config + } + } + } +} + +template("crashpad_loadable_module") { + loadable_module(target_name) { + forward_variables_from(invoker, + "*", + [ + "configs", + "remove_configs", + ]) + if (defined(invoker.remove_configs)) { + configs -= invoker.remove_configs + } + + if (defined(invoker.configs)) { + configs += invoker.configs + } + + if (crashpad_is_in_fuchsia) { + conversion_config = [ "//build/config:Wno-conversion" ] + if (configs + conversion_config - conversion_config == configs) { + # TODO(https://fxbug.dev/58162): Decide if these are worth enabling. + configs += conversion_config + } + } + } +} + +template("crashpad_static_library") { + static_library(target_name) { + forward_variables_from(invoker, + "*", + [ + "configs", + "remove_configs", + ]) + if (defined(invoker.remove_configs)) { + configs -= invoker.remove_configs + } + + if (defined(invoker.configs)) { + configs += invoker.configs + } + + if (crashpad_is_in_fuchsia) { + conversion_config = [ "//build/config:Wno-conversion" ] + if (configs + conversion_config - conversion_config == configs) { + # TODO(https://fxbug.dev/58162): Decide if these are worth enabling. + configs += conversion_config + } + } + } +} diff --git a/shared/sentry/external/crashpad/build/crashpad_fuzzer_test.gni b/shared/sentry/external/crashpad/build/crashpad_fuzzer_test.gni new file mode 100644 index 000000000..cc709b0d1 --- /dev/null +++ b/shared/sentry/external/crashpad/build/crashpad_fuzzer_test.gni @@ -0,0 +1,58 @@ +# Copyright 2018 The Crashpad Authors. All rights reserved. +# +# 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("crashpad_buildconfig.gni") +import("test.gni") +if (crashpad_is_in_chromium) { + import("//testing/libfuzzer/fuzzer_test.gni") +} + +template("crashpad_fuzzer_test") { + if (crashpad_is_standalone && crashpad_use_libfuzzer) { + test(target_name) { + forward_variables_from(invoker, + [ + "cflags", + "cflags_cc", + "check_includes", + "defines", + "include_dirs", + "sources", + ]) + configs += [ "..:crashpad_config" ] + if (defined(invoker.deps)) { + deps = invoker.deps + } + deps += [ "../third_party/libfuzzer" ] + + if (!defined(invoker.cflags)) { + cflags = [] + } + cflags += [ "-fsanitize=fuzzer" ] + } + if (defined(invoker.seed_corpus)) { + not_needed(invoker, [ "seed_corpus" ]) + } + } else if (crashpad_is_in_chromium && use_fuzzing_engine) { + # Append "crashpad_" to the beginning of the fuzzer's name to make it easier + # in Chromium to recognize where fuzzer came from. + forward_variables_from(invoker, "*") + fuzzer_test("crashpad_" + target_name) { + } + } else { + not_needed(invoker, "*") + group(target_name) { + } + } +} diff --git a/shared/sentry/external/crashpad/build/install_linux_sysroot.py b/shared/sentry/external/crashpad/build/install_linux_sysroot.py new file mode 100755 index 000000000..c05c4d5db --- /dev/null +++ b/shared/sentry/external/crashpad/build/install_linux_sysroot.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 + +# Copyright 2018 The Crashpad Authors. All rights reserved. +# +# 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. + +# Various code adapted from: +# https://cs.chromium.org/chromium/src/build/linux/sysroot_scripts/install-sysroot.py + +import os +import shutil +import subprocess +import sys +import urllib.request + +SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) + +# Sysroot revision from: +# https://cs.chromium.org/chromium/src/build/linux/sysroot_scripts/sysroots.json +SERVER = 'https://commondatastorage.googleapis.com' +PATH = 'chrome-linux-sysroot/toolchain' +REVISION = '43a87bbebccad99325fdcf34166295b121ee15c7' +FILENAME = 'debian_sid_amd64_sysroot.tar.xz' + + +def main(): + url = '%s/%s/%s/%s' % (SERVER, PATH, REVISION, FILENAME) + + sysroot = os.path.join(SCRIPT_DIR, os.pardir, 'third_party', 'linux', + 'sysroot') + + stamp = os.path.join(sysroot, '.stamp') + if os.path.exists(stamp): + with open(stamp) as s: + if s.read() == url: + return + + print('Installing Debian root image from %s' % url) + + if os.path.isdir(sysroot): + shutil.rmtree(sysroot) + os.mkdir(sysroot) + tarball = os.path.join(sysroot, FILENAME) + print('Downloading %s' % url) + + for _ in range(3): + response = urllib.request.urlopen(url) + with open(tarball, 'wb') as f: + f.write(response.read()) + break + else: + raise Exception('Failed to download %s' % url) + + subprocess.check_call(['tar', 'xf', tarball, '-C', sysroot]) + + os.remove(tarball) + + with open(stamp, 'w') as s: + s.write(url) + + +if __name__ == '__main__': + main() + sys.exit(0) diff --git a/shared/sentry/external/crashpad/build/ios/Default.png b/shared/sentry/external/crashpad/build/ios/Default.png new file mode 100644 index 0000000000000000000000000000000000000000..8c9089d5c6789aae633902ca93ad552f14bfadd7 GIT binary patch literal 1707 zcmdT^J!sQ$5PkW8epw_Ss70g%1t+PTIEWyT1Py4dZ6iWKT5V1oN>mC_5D67aT-a0POUYLJLVmvcLvhefUF=P{-`%}?@7CMPasftvXzuo4HXPug=WgyZx?u3dmsW z{A4M5gcvPGjZtERXCcI3F=z}D1H5PnqvtSxbg27p^s1{U%N>Df{_KF43AQA-R$k6}` z&;SYGRV}%L(Rktu8(V(shWx@TrOF#C~6`mpn8&GuR#rB zpgj6Pnw+K`)K3yrNY(@+X|iN1d2lhY2~8}xb6#f75LLL5)W@~VL{vauDVHo*REUB; z(rFq%UTmk)R7)%A2WfJec90VVQz2OsjHEe9TI9jS#3nTSMBdFZa|U=Z8cBUz%S`wg zC0~QfO%BY9`aP2)IjO{VKW}2Cb4=XvYbS!8a-zYHpU`pz$tFS*i&vs~)YJt!KpSWQ zO`r}`fFh6wa-^}1a<|*)vfuz8z{{eKplb zkj7Gc{xNsN|7&(qtXxXJ{Bw^J4^RL59y>|Wf6n#u!0=b3O5d-JIMH%xwD|1I^uljm C#i;ZE literal 0 HcmV?d00001 diff --git a/shared/sentry/external/crashpad/build/ios/Unittest-Info.plist b/shared/sentry/external/crashpad/build/ios/Unittest-Info.plist new file mode 100644 index 000000000..fdca91fb1 --- /dev/null +++ b/shared/sentry/external/crashpad/build/ios/Unittest-Info.plist @@ -0,0 +1,10 @@ + + + + + CFBundleIdentifier + ${IOS_BUNDLE_ID_PREFIX}.googletest.${GTEST_BUNDLE_ID_SUFFIX:rfc1034identifier} + UIApplicationDelegate + CrashpadUnitTestDelegate + + diff --git a/shared/sentry/external/crashpad/build/ios/convert_gn_xcodeproj.py b/shared/sentry/external/crashpad/build/ios/convert_gn_xcodeproj.py new file mode 100755 index 000000000..8c5c6d073 --- /dev/null +++ b/shared/sentry/external/crashpad/build/ios/convert_gn_xcodeproj.py @@ -0,0 +1,549 @@ +#!/usr/bin/env python3 + +# Copyright 2020 The Crashpad Authors. All rights reserved. +# +# 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. +"""Convert GN Xcode projects to platform and configuration independent targets. + +GN generates Xcode projects that build one configuration only. However, typical +iOS development involves using the Xcode IDE to toggle the platform and +configuration. This script replaces the 'gn' configuration with 'Debug', +'Release' and 'Profile', and changes the ninja invocation to honor these +configurations. +""" + +import argparse +import collections +import copy +import filecmp +import functools +import hashlib +import json +import os +import re +import shutil +import string +import subprocess +import sys +import tempfile +import xml.etree.ElementTree + + +LLDBINIT_PATH = '$(PROJECT_DIR)/.lldbinit' + +PYTHON_RE = re.compile('[ /]python[23]?$') + +XCTEST_PRODUCT_TYPES = frozenset(( + 'com.apple.product-type.bundle.unit-test', + 'com.apple.product-type.bundle.ui-testing', +)) + +SCHEME_PRODUCT_TYPES = frozenset(( + 'com.apple.product-type.app-extension', + 'com.apple.product-type.application', + 'com.apple.product-type.framework' +)) + + +class Template(string.Template): + + """A subclass of string.Template that changes delimiter.""" + + delimiter = '@' + + +@functools.lru_cache +def LoadSchemeTemplate(root, name): + """Return a string.Template object for scheme file loaded relative to root.""" + path = os.path.join(root, 'build', 'ios', name) + with open(path) as file: + return Template(file.read()) + + +def CreateIdentifier(str_id): + """Return a 24 characters string that can be used as an identifier.""" + return hashlib.sha1(str_id.encode("utf-8")).hexdigest()[:24].upper() + + +def GenerateSchemeForTarget(root, project, old_project, name, path, tests): + """Generates the .xcsheme file for target named |name|. + + The file is generated in the new project schemes directory from a template. + If there is an existing previous project, then the old scheme file is copied + and the lldbinit setting is set. If lldbinit setting is already correct, the + file is not modified, just copied. + """ + project_name = os.path.basename(project) + relative_path = os.path.join('xcshareddata', 'xcschemes', name + '.xcscheme') + identifier = CreateIdentifier('%s %s' % (name, path)) + + scheme_path = os.path.join(project, relative_path) + if not os.path.isdir(os.path.dirname(scheme_path)): + os.makedirs(os.path.dirname(scheme_path)) + + old_scheme_path = os.path.join(old_project, relative_path) + if os.path.exists(old_scheme_path): + made_changes = False + + tree = xml.etree.ElementTree.parse(old_scheme_path) + tree_root = tree.getroot() + + for reference in tree_root.findall('.//BuildableReference'): + for (attr, value) in ( + ('BuildableName', path), + ('BlueprintName', name), + ('BlueprintIdentifier', identifier)): + if reference.get(attr) != value: + reference.set(attr, value) + made_changes = True + + for child in tree_root: + if child.tag not in ('TestAction', 'LaunchAction'): + continue + + if child.get('customLLDBInitFile') != LLDBINIT_PATH: + child.set('customLLDBInitFile', LLDBINIT_PATH) + made_changes = True + + # Override the list of testables. + if child.tag == 'TestAction': + for subchild in child: + if subchild.tag != 'Testables': + continue + + for elt in list(subchild): + subchild.remove(elt) + + if tests: + template = LoadSchemeTemplate(root, 'xcodescheme-testable.template') + for (key, test_path, test_name) in sorted(tests): + testable = ''.join(template.substitute( + BLUEPRINT_IDENTIFIER=key, + BUILDABLE_NAME=test_path, + BLUEPRINT_NAME=test_name, + PROJECT_NAME=project_name)) + + testable_elt = xml.etree.ElementTree.fromstring(testable) + subchild.append(testable_elt) + + if made_changes: + tree.write(scheme_path, xml_declaration=True, encoding='UTF-8') + + else: + shutil.copyfile(old_scheme_path, scheme_path) + + else: + + testables = '' + if tests: + template = LoadSchemeTemplate(root, 'xcodescheme-testable.template') + testables = '\n' + ''.join( + template.substitute( + BLUEPRINT_IDENTIFIER=key, + BUILDABLE_NAME=test_path, + BLUEPRINT_NAME=test_name, + PROJECT_NAME=project_name) + for (key, test_path, test_name) in sorted(tests)).rstrip() + + template = LoadSchemeTemplate(root, 'xcodescheme.template') + + with open(scheme_path, 'w') as scheme_file: + scheme_file.write( + template.substitute( + TESTABLES=testables, + LLDBINIT_PATH=LLDBINIT_PATH, + BLUEPRINT_IDENTIFIER=identifier, + BUILDABLE_NAME=path, + BLUEPRINT_NAME=name, + PROJECT_NAME=project_name)) + + +class XcodeProject(object): + + def __init__(self, objects, counter = 0): + self.objects = objects + self.counter = 0 + + def AddObject(self, parent_name, obj): + while True: + self.counter += 1 + str_id = "%s %s %d" % (parent_name, obj['isa'], self.counter) + new_id = CreateIdentifier(str_id) + + # Make sure ID is unique. It's possible there could be an id conflict + # since this is run after GN runs. + if new_id not in self.objects: + self.objects[new_id] = obj + return new_id + + def IterObjectsByIsa(self, isa): + """Iterates overs objects of the |isa| type.""" + for key, obj in self.objects.items(): + if obj['isa'] == isa: + yield (key, obj) + + def IterNativeTargetByProductType(self, product_types): + """Iterates over PBXNativeTarget objects of any |product_types| types.""" + for key, obj in self.IterObjectsByIsa('PBXNativeTarget'): + if obj['productType'] in product_types: + yield (key, obj) + + def UpdateBuildScripts(self): + """Update build scripts to respect configuration and platforms.""" + for key, obj in self.IterObjectsByIsa('PBXShellScriptBuildPhase'): + + shell_path = obj['shellPath'] + shell_code = obj['shellScript'] + if shell_path.endswith('/sh'): + shell_code = shell_code.replace( + 'ninja -C .', + 'ninja -C "../${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}"') + elif PYTHON_RE.search(shell_path): + shell_code = shell_code.replace( + '''ninja_params = [ '-C', '.' ]''', + '''ninja_params = [ '-C', '../' + os.environ['CONFIGURATION']''' + ''' + os.environ['EFFECTIVE_PLATFORM_NAME'] ]''') + + # Replace the build script in the object. + obj['shellScript'] = shell_code + + + def UpdateBuildConfigurations(self, configurations): + """Add new configurations, using the first one as default.""" + + # Create a list with all the objects of interest. This is needed + # because objects will be added to/removed from the project upon + # iterating this list and python dictionaries cannot be mutated + # during iteration. + for key, obj in list(self.IterObjectsByIsa('XCConfigurationList')): + # Use the first build configuration as template for creating all the + # new build configurations. + build_config_template = self.objects[obj['buildConfigurations'][0]] + build_config_template['buildSettings']['CONFIGURATION_BUILD_DIR'] = \ + '$(PROJECT_DIR)/../$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)' + + + # Remove the existing build configurations from the project before + # creating the new ones. + for build_config_id in obj['buildConfigurations']: + del self.objects[build_config_id] + obj['buildConfigurations'] = [] + + for configuration in configurations: + build_config = copy.copy(build_config_template) + build_config['name'] = configuration + build_config_id = self.AddObject('products', build_config) + obj['buildConfigurations'].append(build_config_id) + + def GetHostMappingForXCTests(self): + """Returns a dict from targets to the list of their xctests modules.""" + mapping = collections.defaultdict(list) + for key, obj in self.IterNativeTargetByProductType(XCTEST_PRODUCT_TYPES): + build_config_lists_id = obj['buildConfigurationList'] + build_configs = self.objects[build_config_lists_id]['buildConfigurations'] + + # Use the first build configuration to get the name of the host target. + # This is arbitrary, but since the build configuration are all identical + # after UpdateBuildConfiguration, except for their 'name', it is fine. + build_config = self.objects[build_configs[0]] + if obj['productType'] == 'com.apple.product-type.bundle.unit-test': + # The test_host value will look like this: + # `$(BUILD_PRODUCTS_DIR)/host_app_name.app/host_app_name` + # + # Extract the `host_app_name.app` part as key for the output. + test_host_path = build_config['buildSettings']['TEST_HOST'] + test_host_name = os.path.basename(os.path.dirname(test_host_path)) + else: + test_host_name = build_config['buildSettings']['TEST_TARGET_NAME'] + + test_name = obj['name'] + test_path = self.objects[obj['productReference']]['path'] + + mapping[test_host_name].append((key, test_name, test_path)) + + return dict(mapping) + + +def check_output(command): + """Wrapper around subprocess.check_output that decode output as utf-8.""" + return subprocess.check_output(command).decode('utf-8') + + +def CopyFileIfChanged(source_path, target_path): + """Copy |source_path| to |target_path| if different.""" + target_dir = os.path.dirname(target_path) + if not os.path.isdir(target_dir): + os.makedirs(target_dir) + if not os.path.exists(target_path) or \ + not filecmp.cmp(source_path, target_path): + shutil.copyfile(source_path, target_path) + + +def CopyTreeIfChanged(source, target): + """Copy |source| to |target| recursively; files are copied iff changed.""" + if os.path.isfile(source): + return CopyFileIfChanged(source, target) + if not os.path.isdir(target): + os.makedirs(target) + for name in os.listdir(source): + CopyTreeIfChanged( + os.path.join(source, name), + os.path.join(target, name)) + + +def LoadXcodeProjectAsJSON(project_dir): + """Return Xcode project at |path| as a JSON string.""" + return check_output([ + 'plutil', '-convert', 'json', '-o', '-', + os.path.join(project_dir, 'project.pbxproj')]) + + +def WriteXcodeProject(output_path, json_string): + """Save Xcode project to |output_path| as XML.""" + with tempfile.NamedTemporaryFile() as temp_file: + temp_file.write(json_string.encode("utf-8")) + temp_file.flush() + subprocess.check_call(['plutil', '-convert', 'xml1', temp_file.name]) + CopyFileIfChanged( + temp_file.name, + os.path.join(output_path, 'project.pbxproj')) + + +def UpdateXcodeProject(project_dir, old_project_dir, configurations, root_dir): + """Update inplace Xcode project to support multiple configurations. + + Args: + project_dir: path to the input Xcode project + configurations: list of string corresponding to the configurations that + need to be supported by the tweaked Xcode projects, must contains at + least one value. + root_dir: path to the root directory used to find markdown files + """ + json_data = json.loads(LoadXcodeProjectAsJSON(project_dir)) + project = XcodeProject(json_data['objects']) + + project.UpdateBuildScripts() + project.UpdateBuildConfigurations(configurations) + + mapping = project.GetHostMappingForXCTests() + + # Generate schemes for application, extensions and framework targets + for key, obj in project.IterNativeTargetByProductType(SCHEME_PRODUCT_TYPES): + product = project.objects[obj['productReference']] + product_path = product['path'] + + # For XCTests, the key is the product path, while for XCUITests, the key + # is the target name. Use a sum of both possible keys (there should not + # be overlaps since different hosts are used for XCTests and XCUITests + # but this make the code simpler). + tests = mapping.get(product_path, []) + mapping.get(obj['name'], []) + GenerateSchemeForTarget( + root_dir, project_dir, old_project_dir, + obj['name'], product_path, tests) + + + source = GetOrCreateRootGroup(project, json_data['rootObject'], 'Source') + AddMarkdownToProject(project, root_dir, source) + SortFileReferencesByName(project, source) + + objects = collections.OrderedDict(sorted(project.objects.items())) + WriteXcodeProject(project_dir, json.dumps(json_data)) + + +def CreateGroup(project, parent_group, group_name, path=None): + group_object = { + 'children': [], + 'isa': 'PBXGroup', + 'name': group_name, + 'sourceTree': '', + } + if path is not None: + group_object['path'] = path + parent_group_name = parent_group.get('name', '') + group_object_key = project.AddObject(parent_group_name, group_object) + parent_group['children'].append(group_object_key) + return group_object + + +def GetOrCreateRootGroup(project, root_object, group_name): + main_group = project.objects[project.objects[root_object]['mainGroup']] + for child_key in main_group['children']: + child = project.objects[child_key] + if child['name'] == group_name: + return child + return CreateGroup(project, main_group, group_name, path='../..') + + +class ObjectKey(object): + + """Wrapper around PBXFileReference and PBXGroup for sorting. + + A PBXGroup represents a "directory" containing a list of files in an + Xcode project; it can contain references to a list of directories or + files. + + A PBXFileReference represents a "file". + + The type is stored in the object "isa" property as a string. Since we + want to sort all directories before all files, the < and > operators + are defined so that if "isa" is different, they are sorted in the + reverse of alphabetic ordering, otherwise the name (or path) property + is checked and compared in alphabetic order. + """ + + def __init__(self, obj): + self.isa = obj['isa'] + if 'name' in obj: + self.name = obj['name'] + else: + self.name = obj['path'] + + def __lt__(self, other): + if self.isa != other.isa: + return self.isa > other.isa + return self.name < other.name + + def __gt__(self, other): + if self.isa != other.isa: + return self.isa < other.isa + return self.name > other.name + + def __eq__(self, other): + return self.isa == other.isa and self.name == other.name + + +def SortFileReferencesByName(project, group_object): + SortFileReferencesByNameWithSortKey( + project, group_object, lambda ref: ObjectKey(project.objects[ref])) + + +def SortFileReferencesByNameWithSortKey(project, group_object, sort_key): + group_object['children'].sort(key=sort_key) + for key in group_object['children']: + child = project.objects[key] + if child['isa'] == 'PBXGroup': + SortFileReferencesByNameWithSortKey(project, child, sort_key) + + +def AddMarkdownToProject(project, root_dir, group_object): + list_files_cmd = ['git', '-C', root_dir, 'ls-files', '*.md'] + paths = check_output(list_files_cmd).splitlines() + ios_internal_dir = os.path.join(root_dir, 'ios_internal') + if os.path.exists(ios_internal_dir): + list_files_cmd = ['git', '-C', ios_internal_dir, 'ls-files', '*.md'] + ios_paths = check_output(list_files_cmd).splitlines() + paths.extend([os.path.join("ios_internal", path) for path in ios_paths]) + for path in paths: + new_markdown_entry = { + "fileEncoding": "4", + "isa": "PBXFileReference", + "lastKnownFileType": "net.daringfireball.markdown", + "name": os.path.basename(path), + "path": path, + "sourceTree": "" + } + new_markdown_entry_id = project.AddObject('sources', new_markdown_entry) + folder = GetFolderForPath(project, group_object, os.path.dirname(path)) + folder['children'].append(new_markdown_entry_id) + + +def GetFolderForPath(project, group_object, path): + objects = project.objects + if not path: + return group_object + for folder in path.split('/'): + children = group_object['children'] + new_root = None + for child in children: + if objects[child]['isa'] == 'PBXGroup' and \ + objects[child]['name'] == folder: + new_root = objects[child] + break + if not new_root: + # If the folder isn't found we could just cram it into the leaf existing + # folder, but that leads to folders with tons of README.md inside. + new_root = CreateGroup(project, group_object, folder) + group_object = new_root + return group_object + + +def ConvertGnXcodeProject(root_dir, proj_name, input_dir, output_dir, configs): + '''Tweak the Xcode project generated by gn to support multiple configurations. + + The Xcode projects generated by "gn gen --ide" only supports a single + platform and configuration (as the platform and configuration are set + per output directory). This method takes as input such projects and + add support for multiple configurations and platforms (to allow devs + to select them in Xcode). + + Args: + root_dir: directory that is the root of the project + proj_name: name of the Xcode project "file" (usually `all.xcodeproj`) + input_dir: directory containing the XCode projects created by "gn gen --ide" + output_dir: directory where the tweaked Xcode projects will be saved + configs: list of string corresponding to the configurations that need to be + supported by the tweaked Xcode projects, must contains at least one + value. + ''' + + UpdateXcodeProject( + os.path.join(input_dir, proj_name), + os.path.join(output_dir, proj_name), + configs, root_dir) + + CopyTreeIfChanged(os.path.join(input_dir, proj_name), + os.path.join(output_dir, proj_name)) + + +def Main(args): + parser = argparse.ArgumentParser( + description='Convert GN Xcode projects for iOS.') + parser.add_argument( + 'input', + help='directory containing [product|all] Xcode projects.') + parser.add_argument( + 'output', + help='directory where to generate the iOS configuration.') + parser.add_argument( + '--add-config', dest='configurations', default=[], action='append', + help='configuration to add to the Xcode project') + parser.add_argument( + '--root', type=os.path.abspath, required=True, + help='root directory of the project') + parser.add_argument( + '--project-name', default='all.xcodeproj', dest='proj_name', + help='name of the Xcode project (default: %(default)s)') + args = parser.parse_args(args) + + if not os.path.isdir(args.input): + sys.stderr.write('Input directory does not exists.\n') + return 1 + + if args.proj_name not in os.listdir(args.input): + sys.stderr.write( + 'Input directory does not contain the Xcode project.\n') + return 1 + + if not args.configurations: + sys.stderr.write('At least one configuration required, see --add-config.\n') + return 1 + + ConvertGnXcodeProject( + args.root, + args.proj_name, + args.input, + args.output, + args.configurations) + +if __name__ == '__main__': + sys.exit(Main(sys.argv[1:])) diff --git a/shared/sentry/external/crashpad/build/ios/setup_ios_gn.config b/shared/sentry/external/crashpad/build/ios/setup_ios_gn.config new file mode 100644 index 000000000..30c31115e --- /dev/null +++ b/shared/sentry/external/crashpad/build/ios/setup_ios_gn.config @@ -0,0 +1,39 @@ +# Copyright 2020 The Crashpad Authors. All rights reserved. +# +# 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. + +[goma] +# Controls whether goma is enabled or not. If you generally use goma but +# want to disable goma for a single build, consider using the environment +# variable GOMA_DISABLED. +enabled = False +install = "$GOMA_DIR" + +[xcode] +# Controls settings for the generated Xcode project. If jobs is non-zero +# it will be passed to the ninja invocation in Xcode project. +jobs = 0 + +[build] +# Controls the build output. The only supported values are "64-bit", "32-bit" +# and "fat" (for a fat binary supporting both "32-bit" and "64-bit" cpus). +arch = "64-bit" + +[gn_args] +# Values in that section will be copied verbatim in the generated args.gn file. +target_os = "ios" + +[filters] +# List of target files to pass to --filters argument of gn gen when generating +# the Xcode project. By default, list all targets from ios/ and ios_internal/ +# and the targets corresponding to the unit tests run on the bots. diff --git a/shared/sentry/external/crashpad/build/ios/setup_ios_gn.py b/shared/sentry/external/crashpad/build/ios/setup_ios_gn.py new file mode 100755 index 000000000..aacc8ec7b --- /dev/null +++ b/shared/sentry/external/crashpad/build/ios/setup_ios_gn.py @@ -0,0 +1,405 @@ +#!/usr/bin/env python3 + +# Copyright 2020 The Crashpad Authors. All rights reserved. +# +# 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 argparse +import configparser +import convert_gn_xcodeproj +import errno +import io +import os +import platform +import re +import shutil +import subprocess +import sys +import tempfile + + +SUPPORTED_TARGETS = ('iphoneos', 'iphonesimulator', 'maccatalyst') +SUPPORTED_CONFIGS = ('Debug', 'Release', 'Profile', 'Official') + +# Pattern matching lines from ~/.lldbinit that must not be copied to the +# generated .lldbinit file. They match what the user were told to add to +# their global ~/.lldbinit file before setup-gn.py was updated to generate +# a project specific file and thus must not be copied as they would cause +# the settings to be overwritten. +LLDBINIT_SKIP_PATTERNS = ( + re.compile('^script sys.path\\[:0\\] = \\[\'.*/src/tools/lldb\'\\]$'), + re.compile('^script import lldbinit$'), + re.compile('^settings append target.source-map .* /google/src/.*$'), +) + + +def HostCpuArch(): + '''Returns the arch of the host cpu for GN.''' + HOST_CPU_ARCH = { + 'arm64': '"arm64"', + 'x86_64': '"x64"', + } + return HOST_CPU_ARCH[platform.machine()] + + +class ConfigParserWithStringInterpolation(configparser.ConfigParser): + + '''A .ini file parser that supports strings and environment variables.''' + + ENV_VAR_PATTERN = re.compile(r'\$([A-Za-z0-9_]+)') + + def values(self, section): + return filter( + lambda val: val != '', + map(lambda kv: self._UnquoteString(self._ExpandEnvVar(kv[1])), + configparser.ConfigParser.items(self, section))) + + def getstring(self, section, option, fallback=''): + try: + raw_value = self.get(section, option) + except configparser.NoOptionError: + return fallback + return self._UnquoteString(self._ExpandEnvVar(raw_value)) + + def _UnquoteString(self, string): + if not string or string[0] != '"' or string[-1] != '"': + return string + return string[1:-1] + + def _ExpandEnvVar(self, value): + match = self.ENV_VAR_PATTERN.search(value) + if not match: + return value + name, (begin, end) = match.group(1), match.span(0) + prefix, suffix = value[:begin], self._ExpandEnvVar(value[end:]) + return prefix + os.environ.get(name, '') + suffix + + +class GnGenerator(object): + + '''Holds configuration for a build and method to generate gn default files.''' + + FAT_BUILD_DEFAULT_ARCH = '64-bit' + + TARGET_CPU_VALUES = { + 'iphoneos': '"arm64"', + 'iphonesimulator': HostCpuArch(), + 'maccatalyst': HostCpuArch(), + } + + TARGET_ENVIRONMENT_VALUES = { + 'iphoneos': '"device"', + 'iphonesimulator': '"simulator"', + 'maccatalyst': '"catalyst"' + } + + def __init__(self, settings, config, target): + assert target in SUPPORTED_TARGETS + assert config in SUPPORTED_CONFIGS + self._settings = settings + self._config = config + self._target = target + + def _GetGnArgs(self, extra_args=None): + """Build the list of arguments to pass to gn. + + Returns: + A list of tuple containing gn variable names and variable values (it + is not a dictionary as the order needs to be preserved). + """ + args = [] + + is_debug = self._config == 'Debug' + official = self._config == 'Official' + is_optim = self._config in ('Profile', 'Official') + + args.append(('target_os', '"ios"')) + args.append(('is_debug', is_debug)) + + if os.environ.get('FORCE_MAC_TOOLCHAIN', '0') == '1': + args.append(('use_system_xcode', False)) + + args.append(('target_cpu', self.TARGET_CPU_VALUES[self._target])) + args.append(( + 'target_environment', + self.TARGET_ENVIRONMENT_VALUES[self._target])) + + # If extra arguments are passed to the function, pass them before the + # user overrides (if any). + if extra_args is not None: + args.extend(extra_args) + + # Add user overrides after the other configurations so that they can + # refer to them and override them. + args.extend(self._settings.items('gn_args')) + return args + + + def Generate(self, gn_path, proj_name, root_path, build_dir): + self.WriteArgsGn(build_dir, xcode_project_name=proj_name) + subprocess.check_call(self.GetGnCommand( + gn_path, root_path, build_dir, xcode_project_name=proj_name)) + + def CreateGnRules(self, gn_path, root_path, build_dir): + gn_command = self.GetGnCommand(gn_path, root_path, build_dir) + self.WriteArgsGn(build_dir) + self.WriteBuildNinja(gn_command, build_dir) + self.WriteBuildNinjaDeps(build_dir) + + def WriteArgsGn(self, build_dir, xcode_project_name=None): + with open(os.path.join(build_dir, 'args.gn'), 'w') as stream: + stream.write('# This file was generated by setup-gn.py. Do not edit\n') + stream.write('# but instead use ~/.setup-gn or $repo/.setup-gn files\n') + stream.write('# to configure settings.\n') + stream.write('\n') + + if self._target != 'maccatalyst': + if self._settings.has_section('$imports$'): + for import_rule in self._settings.values('$imports$'): + stream.write('import("%s")\n' % import_rule) + stream.write('\n') + + gn_args = self._GetGnArgs() + + for name, value in gn_args: + if isinstance(value, bool): + stream.write('%s = %s\n' % (name, str(value).lower())) + elif isinstance(value, list): + stream.write('%s = [%s' % (name, '\n' if len(value) > 1 else '')) + if len(value) == 1: + prefix = ' ' + suffix = ' ' + else: + prefix = ' ' + suffix = ',\n' + for item in value: + if isinstance(item, bool): + stream.write('%s%s%s' % (prefix, str(item).lower(), suffix)) + else: + stream.write('%s%s%s' % (prefix, item, suffix)) + stream.write(']\n') + else: + # ConfigParser removes quote around empty string which confuse + # `gn gen` so restore them. + if not value: + value = '""' + stream.write('%s = %s\n' % (name, value)) + + def WriteBuildNinja(self, gn_command, build_dir): + with open(os.path.join(build_dir, 'build.ninja'), 'w') as stream: + stream.write('ninja_required_version = 1.7.2\n') + stream.write('\n') + stream.write('rule gn\n') + stream.write(' command = %s\n' % NinjaEscapeCommand(gn_command)) + stream.write(' description = Regenerating ninja files\n') + stream.write('\n') + stream.write('build build.ninja: gn\n') + stream.write(' generator = 1\n') + stream.write(' depfile = build.ninja.d\n') + + def WriteBuildNinjaDeps(self, build_dir): + with open(os.path.join(build_dir, 'build.ninja.d'), 'w') as stream: + stream.write('build.ninja: nonexistant_file.gn\n') + + def GetGnCommand(self, gn_path, src_path, out_path, xcode_project_name=None): + gn_command = [ gn_path, '--root=%s' % os.path.realpath(src_path), '-q' ] + if xcode_project_name is not None: + gn_command.append('--ide=xcode') + gn_command.append('--ninja-executable=autoninja') + gn_command.append('--xcode-build-system=new') + gn_command.append('--xcode-project=%s' % xcode_project_name) + if self._settings.has_section('filters'): + target_filters = self._settings.values('filters') + if target_filters: + gn_command.append('--filters=%s' % ';'.join(target_filters)) + else: + gn_command.append('--check') + gn_command.append('gen') + gn_command.append('//%s' % + os.path.relpath(os.path.abspath(out_path), os.path.abspath(src_path))) + return gn_command + + +def NinjaNeedEscape(arg): + '''Returns True if |arg| needs to be escaped when written to .ninja file.''' + return ':' in arg or '*' in arg or ';' in arg + + +def NinjaEscapeCommand(command): + '''Escapes |command| in order to write it to .ninja file.''' + result = [] + for arg in command: + if NinjaNeedEscape(arg): + arg = arg.replace(':', '$:') + arg = arg.replace(';', '\\;') + arg = arg.replace('*', '\\*') + else: + result.append(arg) + return ' '.join(result) + + +def FindGn(): + '''Returns absolute path to gn binary looking at the PATH env variable.''' + for path in os.environ['PATH'].split(os.path.pathsep): + gn_path = os.path.join(path, 'gn') + if os.path.isfile(gn_path) and os.access(gn_path, os.X_OK): + return gn_path + return None + + +def GenerateXcodeProject(gn_path, root_dir, proj_name, out_dir, settings): + '''Generate Xcode project with Xcode and convert to multi-configurations.''' + prefix = os.path.abspath(os.path.join(out_dir, '_temp')) + temp_path = tempfile.mkdtemp(prefix=prefix) + try: + generator = GnGenerator(settings, 'Debug', 'iphonesimulator') + generator.Generate(gn_path, proj_name, root_dir, temp_path) + convert_gn_xcodeproj.ConvertGnXcodeProject( + root_dir, + '%s.xcodeproj' % proj_name, + os.path.join(temp_path), + os.path.join(out_dir, 'build'), + SUPPORTED_CONFIGS) + finally: + if os.path.exists(temp_path): + shutil.rmtree(temp_path) + +def CreateLLDBInitFile(root_dir, out_dir, settings): + ''' + Generate an .lldbinit file for the project that load the script that fixes + the mapping of source files (see docs/ios/build_instructions.md#debugging). + ''' + with open(os.path.join(out_dir, 'build', '.lldbinit'), 'w') as lldbinit: + lldb_script_dir = os.path.join(os.path.abspath(root_dir), 'tools', 'lldb') + lldbinit.write('script sys.path[:0] = [\'%s\']\n' % lldb_script_dir) + lldbinit.write('script import lldbinit\n') + + workspace_name = settings.getstring( + 'gn_args', + 'ios_internal_citc_workspace_name') + + if workspace_name != '': + username = os.environ['USER'] + for shortname in ('googlemac', 'third_party', 'blaze-out'): + lldbinit.write('settings append target.source-map %s %s\n' % ( + shortname, + '/google/src/cloud/%s/%s/google3/%s' % ( + username, workspace_name, shortname))) + + # Append the content of //ios/build/tools/lldbinit.defaults if it exists. + tools_dir = os.path.join(root_dir, 'ios', 'build', 'tools') + defaults_lldbinit_path = os.path.join(tools_dir, 'lldbinit.defaults') + if os.path.isfile(defaults_lldbinit_path): + with open(defaults_lldbinit_path) as defaults_lldbinit: + for line in defaults_lldbinit: + lldbinit.write(line) + + # Append the content of ~/.lldbinit if it exists. Line that look like they + # are trying to configure source mapping are skipped as they probably date + # back from when setup-gn.py was not generating an .lldbinit file. + global_lldbinit_path = os.path.join(os.environ['HOME'], '.lldbinit') + if os.path.isfile(global_lldbinit_path): + with open(global_lldbinit_path) as global_lldbinit: + for line in global_lldbinit: + if any(pattern.match(line) for pattern in LLDBINIT_SKIP_PATTERNS): + continue + lldbinit.write(line) + + +def GenerateGnBuildRules(gn_path, root_dir, out_dir, settings): + '''Generates all template configurations for gn.''' + for config in SUPPORTED_CONFIGS: + for target in SUPPORTED_TARGETS: + build_dir = os.path.join(out_dir, '%s-%s' % (config, target)) + if not os.path.isdir(build_dir): + os.makedirs(build_dir) + + generator = GnGenerator(settings, config, target) + generator.CreateGnRules(gn_path, root_dir, build_dir) + + +def Main(args): + default_root = os.path.normpath(os.path.join( + os.path.dirname(__file__), os.pardir, os.pardir)) + + parser = argparse.ArgumentParser( + description='Generate build directories for use with gn.') + parser.add_argument( + 'root', default=default_root, nargs='?', + help='root directory where to generate multiple out configurations') + parser.add_argument( + '--import', action='append', dest='import_rules', default=[], + help='path to file defining default gn variables') + parser.add_argument( + '--gn-path', default=None, + help='path to gn binary (default: look up in $PATH)') + parser.add_argument( + '--build-dir', default='out', + help='path where the build should be created (default: %(default)s)') + parser.add_argument( + '--config-path', default=os.path.expanduser('~/.setup-gn'), + help='path to the user config file (default: %(default)s)') + parser.add_argument( + '--system-config-path', default=os.path.splitext(__file__)[0] + '.config', + help='path to the default config file (default: %(default)s)') + parser.add_argument( + '--project-name', default='all', dest='proj_name', + help='name of the generated Xcode project (default: %(default)s)') + parser.add_argument( + '--no-xcode-project', action='store_true', default=False, + help='do not generate the build directory with XCode project') + args = parser.parse_args(args) + + # Load configuration (first global and then any user overrides). + settings = ConfigParserWithStringInterpolation() + settings.read([ + args.system_config_path, + args.config_path, + ]) + + # Add private sections corresponding to --import argument. + if args.import_rules: + settings.add_section('$imports$') + for i, import_rule in enumerate(args.import_rules): + if not import_rule.startswith('//'): + import_rule = '//%s' % os.path.relpath( + os.path.abspath(import_rule), os.path.abspath(args.root)) + settings.set('$imports$', '$rule%d$' % i, import_rule) + + # Validate settings. + if settings.getstring('build', 'arch') not in ('64-bit', '32-bit', 'fat'): + sys.stderr.write('ERROR: invalid value for build.arch: %s\n' % + settings.getstring('build', 'arch')) + sys.exit(1) + + # Find path to gn binary either from command-line or in PATH. + if args.gn_path: + gn_path = args.gn_path + else: + gn_path = FindGn() + if gn_path is None: + sys.stderr.write('ERROR: cannot find gn in PATH\n') + sys.exit(1) + + out_dir = os.path.join(args.root, args.build_dir) + if not os.path.isdir(out_dir): + os.makedirs(out_dir) + + if not args.no_xcode_project: + GenerateXcodeProject(gn_path, args.root, args.proj_name, out_dir, settings) + CreateLLDBInitFile(args.root, out_dir, settings) + GenerateGnBuildRules(gn_path, args.root, out_dir, settings) + + +if __name__ == '__main__': + sys.exit(Main(sys.argv[1:])) diff --git a/shared/sentry/external/crashpad/build/ios/xcodescheme-testable.template b/shared/sentry/external/crashpad/build/ios/xcodescheme-testable.template new file mode 100644 index 000000000..61b6f471b --- /dev/null +++ b/shared/sentry/external/crashpad/build/ios/xcodescheme-testable.template @@ -0,0 +1,10 @@ + + + + diff --git a/shared/sentry/external/crashpad/build/ios/xcodescheme.template b/shared/sentry/external/crashpad/build/ios/xcodescheme.template new file mode 100644 index 000000000..514bea4ef --- /dev/null +++ b/shared/sentry/external/crashpad/build/ios/xcodescheme.template @@ -0,0 +1,80 @@ + + + + + + + + + + + + @{TESTABLES} + + + + + + + + + + + + + + + + + + + diff --git a/shared/sentry/external/crashpad/build/run_tests.py b/shared/sentry/external/crashpad/build/run_tests.py new file mode 100755 index 000000000..71d1d9c34 --- /dev/null +++ b/shared/sentry/external/crashpad/build/run_tests.py @@ -0,0 +1,480 @@ +#!/usr/bin/env python3 + +# Copyright 2014 The Crashpad Authors. All rights reserved. +# +# 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 argparse +import os +import pipes +import posixpath +import re +import subprocess +import sys +import tempfile +import uuid + +CRASHPAD_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), + os.pardir) +IS_WINDOWS_HOST = sys.platform.startswith('win') + + +def _FindGNFromBinaryDir(binary_dir): + """Attempts to determine the path to a GN binary used to generate the build + files in the given binary_dir. This is necessary because `gn` might not be + in the path or might be in a non-standard location, particularly on build + machines.""" + + build_ninja = os.path.join(binary_dir, 'build.ninja') + if os.path.isfile(build_ninja): + with open(build_ninja, 'r') as f: + # Look for the always-generated regeneration rule of the form: + # + # rule gn + # command = ... arguments ... + # + # to extract the gn binary's full path. + found_rule_gn = False + for line in f: + if line.strip() == 'rule gn': + found_rule_gn = True + continue + if found_rule_gn: + if len(line) == 0 or line[0] != ' ': + return None + if line.startswith(' command = '): + gn_command_line_parts = line.strip().split(' ') + if len(gn_command_line_parts) > 2: + return os.path.join(binary_dir, + gn_command_line_parts[2]) + + return None + + +def _BinaryDirTargetOS(binary_dir): + """Returns the apparent target OS of binary_dir, or None if none appear to + be explicitly specified.""" + + gn_path = _FindGNFromBinaryDir(binary_dir) + + if gn_path: + # Look for a GN “target_osâ€. + popen = subprocess.Popen([ + gn_path, '--root=' + CRASHPAD_DIR, 'args', binary_dir, + '--list=target_os', '--short' + ], + shell=IS_WINDOWS_HOST, + stdout=subprocess.PIPE, + stderr=open(os.devnull), + text=True) + value = popen.communicate()[0] + if popen.returncode == 0: + match = re.match('target_os = "(.*)"$', value) + if match: + return match.group(1) + + # For GYP with Ninja, look for the appearance of “linux-android†in the path + # to ar. This path is configured by gyp_crashpad_android.py. + build_ninja_path = os.path.join(binary_dir, 'build.ninja') + if os.path.exists(build_ninja_path): + with open(build_ninja_path) as build_ninja_file: + build_ninja_content = build_ninja_file.read() + match = re.search('-linux-android(eabi)?-ar$', build_ninja_content, + re.MULTILINE) + if match: + return 'android' + + return None + + +def _EnableVTProcessingOnWindowsConsole(): + """Enables virtual terminal processing for ANSI/VT100-style escape sequences + on a Windows console attached to standard output. Returns True on success. + Returns False if standard output is not a console or if virtual terminal + processing is not supported. The feature was introduced in Windows 10. + """ + + import pywintypes + import win32console + import winerror + + stdout_console = win32console.GetStdHandle(win32console.STD_OUTPUT_HANDLE) + try: + console_mode = stdout_console.GetConsoleMode() + except pywintypes.error as e: + if e.winerror == winerror.ERROR_INVALID_HANDLE: + # Standard output is not a console. + return False + raise + + try: + # From . This would be + # win32console.ENABLE_VIRTUAL_TERMINAL_PROCESSING, but it’s too new to + # be defined there. + ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 + + stdout_console.SetConsoleMode(console_mode | + ENABLE_VIRTUAL_TERMINAL_PROCESSING) + except pywintypes.error as e: + if e.winerror == winerror.ERROR_INVALID_PARAMETER: + # ANSI/VT100-style escape sequence processing isn’t supported before + # Windows 10. + return False + raise + + return True + + +def _RunOnAndroidTarget(binary_dir, test, android_device, extra_command_line): + local_test_path = os.path.join(binary_dir, test) + MAYBE_UNSUPPORTED_TESTS = ( + 'crashpad_client_test', + 'crashpad_handler_test', + 'crashpad_minidump_test', + 'crashpad_snapshot_test', + ) + if not os.path.exists(local_test_path) and test in MAYBE_UNSUPPORTED_TESTS: + print('This test is not present and may not be supported, skipping') + return + + def _adb(*args): + # Flush all of this script’s own buffered stdout output before running + # adb, which will likely produce its own output on stdout. + sys.stdout.flush() + + adb_command = ['adb', '-s', android_device] + adb_command.extend(args) + subprocess.check_call(adb_command, shell=IS_WINDOWS_HOST) + + def _adb_push(sources, destination): + args = list(sources) + args.append(destination) + _adb('push', *args) + + def _adb_shell(command_args, env={}): + # Build a command to execute via “sh -c†instead of invoking it + # directly. Here’s why: + # + # /system/bin/env isn’t normally present prior to Android 6.0 (M), where + # toybox was introduced (Android platform/manifest 9a2c01e8450b). + # Instead, set environment variables by using the shell’s internal + # “export†command. + # + # adbd prior to Android 7.0 (N), and the adb client prior to SDK + # platform-tools version 24, don’t know how to communicate a shell + # command’s exit status. This was added in Android platform/system/core + # 606835ae5c4b). With older adb servers and clients, adb will “exit 0†+ # indicating success even if the command failed on the device. This + # makes subprocess.check_call() semantics difficult to implement + # directly. As a workaround, have the device send the command’s exit + # status over stdout and pick it back up in this function. + # + # Both workarounds are implemented by giving the device a simple script, + # which adbd will run as an “sh -c†argument. + adb_command = ['adb', '-s', android_device, 'shell'] + script_commands = [] + for k, v in env.items(): + script_commands.append('export %s=%s' % + (pipes.quote(k), pipes.quote(v))) + script_commands.extend([ + ' '.join(pipes.quote(x) for x in command_args), 'status=${?}', + 'echo "status=${status}"', 'exit ${status}' + ]) + adb_command.append('; '.join(script_commands)) + child = subprocess.Popen(adb_command, + shell=IS_WINDOWS_HOST, + stdin=open(os.devnull), + stdout=subprocess.PIPE, + text=True) + + FINAL_LINE_RE = re.compile('status=(\d+)$') + final_line = None + while True: + # Use readline so that the test output appears “live†when running. + data = child.stdout.readline() + if data == '': + break + if final_line is not None: + # It wasn’t really the final line. + print(final_line, end='') + final_line = None + if FINAL_LINE_RE.match(data.rstrip()): + final_line = data + else: + print(data, end='') + + if final_line is None: + # Maybe there was some stderr output after the end of stdout. Old + # versions of adb, prior to when the exit status could be + # communicated, smush the two together. + raise subprocess.CalledProcessError(-1, adb_command) + status = int(FINAL_LINE_RE.match(final_line.rstrip()).group(1)) + if status != 0: + raise subprocess.CalledProcessError(status, adb_command) + + child.wait() + if child.returncode != 0: + raise subprocess.CalledProcessError(subprocess.returncode, + adb_command) + + # /system/bin/mktemp isn’t normally present prior to Android 6.0 (M), where + # toybox was introduced (Android platform/manifest 9a2c01e8450b). Fake it + # with a host-generated name. This won’t retry if the name is in use, but + # with 122 bits of randomness, it should be OK. This uses “mkdir†instead of + # “mkdir -pâ€because the latter will not indicate failure if the directory + # already exists. + device_temp_dir = '/data/local/tmp/%s.%s' % (test, uuid.uuid4().hex) + _adb_shell(['mkdir', device_temp_dir]) + + try: + # Specify test dependencies that must be pushed to the device. This + # could be determined automatically in a GN build, following the example + # used for Fuchsia. Since nothing like that exists for GYP, hard-code it + # for supported tests. + test_build_artifacts = [test, 'crashpad_handler'] + test_data = ['test/test_paths_test_data_root.txt'] + + if test == 'crashpad_test_test': + test_build_artifacts.append( + 'crashpad_test_test_multiprocess_exec_test_child') + elif test == 'crashpad_util_test': + test_data.append('util/net/testdata/') + + # Establish the directory structure on the device. + device_out_dir = posixpath.join(device_temp_dir, 'out') + device_mkdirs = [device_out_dir] + for source_path in test_data: + # A trailing slash could reasonably mean to copy an entire + # directory, but will interfere with what’s needed from the path + # split. All parent directories of any source_path need to be be + # represented in device_mkdirs, but it’s important that no + # source_path itself wind up in device_mkdirs, even if source_path + # names a directory, because that would cause the “adb pushâ€Â of the + # directory below to behave incorrectly. + if source_path.endswith(posixpath.sep): + source_path = source_path[:-1] + + device_source_path = posixpath.join(device_temp_dir, source_path) + device_mkdir = posixpath.split(device_source_path)[0] + if device_mkdir not in device_mkdirs: + device_mkdirs.append(device_mkdir) + adb_mkdir_command = ['mkdir', '-p'] + adb_mkdir_command.extend(device_mkdirs) + _adb_shell(adb_mkdir_command) + + # Push the test binary and any other build output to the device. + local_test_build_artifacts = [] + for artifact in test_build_artifacts: + local_test_build_artifacts.append(os.path.join( + binary_dir, artifact)) + _adb_push(local_test_build_artifacts, device_out_dir) + + # Push test data to the device. + for source_path in test_data: + _adb_push([os.path.join(CRASHPAD_DIR, source_path)], + posixpath.join(device_temp_dir, source_path)) + + # Run the test on the device. Pass the test data root in the + # environment. + # + # Because the test will not run with its standard output attached to a + # pseudo-terminal device, Google Test will not normally enable colored + # output, so mimic Google Test’s own logic for deciding whether to + # enable color by checking this script’s own standard output connection. + # The list of TERM values comes from Google Test’s + # googletest/src/gtest.cc testing::internal::ShouldUseColor(). + env = {'CRASHPAD_TEST_DATA_ROOT': device_temp_dir} + gtest_color = os.environ.get('GTEST_COLOR') + if gtest_color in ('auto', None): + if (sys.stdout.isatty() and + (os.environ.get('TERM') + in ('xterm', 'xterm-color', 'xterm-256color', 'screen', + 'screen-256color', 'tmux', 'tmux-256color', 'rxvt-unicode', + 'rxvt-unicode-256color', 'linux', 'cygwin') or + (IS_WINDOWS_HOST and _EnableVTProcessingOnWindowsConsole()))): + gtest_color = 'yes' + else: + gtest_color = 'no' + env['GTEST_COLOR'] = gtest_color + _adb_shell([posixpath.join(device_out_dir, test)] + extra_command_line, + env) + finally: + _adb_shell(['rm', '-rf', device_temp_dir]) + + +def _RunOnIOSTarget(binary_dir, test, is_xcuitest=False): + """Runs the given iOS |test| app on iPhone 8 with the default OS version.""" + + def xctest(binary_dir, test): + """Returns a dict containing the xctestrun data needed to run an + XCTest-based test app.""" + test_path = os.path.join(CRASHPAD_DIR, binary_dir) + module_data = { + 'TestBundlePath': os.path.join(test_path, test + '_module.xctest'), + 'TestHostPath': os.path.join(test_path, test + '.app'), + 'TestingEnvironmentVariables': { + 'DYLD_FRAMEWORK_PATH': '__TESTROOT__/Debug-iphonesimulator:', + 'DYLD_INSERT_LIBRARIES': + ('__PLATFORMS__/iPhoneSimulator.platform/Developer/' + 'usr/lib/libXCTestBundleInject.dylib'), + 'DYLD_LIBRARY_PATH': '__TESTROOT__/Debug-iphonesimulator', + 'IDEiPhoneInternalTestBundleName': test + '.app', + 'XCInjectBundleInto': '__TESTHOST__/' + test, + } + } + return {test: module_data} + + def xcuitest(binary_dir, test): + """Returns a dict containing the xctestrun data needed to run an + XCUITest-based test app.""" + + test_path = os.path.join(CRASHPAD_DIR, binary_dir) + runner_path = os.path.join(test_path, test + '_module-Runner.app') + bundle_path = os.path.join(runner_path, 'PlugIns', + test + '_module.xctest') + target_app_path = os.path.join(test_path, test + '.app') + module_data = { + 'IsUITestBundle': True, + 'IsXCTRunnerHostedTestBundle': True, + 'TestBundlePath': bundle_path, + 'TestHostPath': runner_path, + 'UITargetAppPath': target_app_path, + 'DependentProductPaths': [ + bundle_path, runner_path, target_app_path + ], + 'TestingEnvironmentVariables': { + 'DYLD_FRAMEWORK_PATH': '__TESTROOT__/Debug-iphonesimulator:', + 'DYLD_INSERT_LIBRARIES': + ('__PLATFORMS__/iPhoneSimulator.platform/Developer/' + 'usr/lib/libXCTestBundleInject.dylib'), + 'DYLD_LIBRARY_PATH': '__TESTROOT__/Debug-iphonesimulator', + 'XCInjectBundleInto': '__TESTHOST__/' + test + '_module-Runner', + }, + } + return {test: module_data} + + with tempfile.NamedTemporaryFile() as f: + import plistlib + + xctestrun_path = f.name + print(xctestrun_path) + with open(xctestrun_path, 'wb') as fp: + if is_xcuitest: + plistlib.dump(xcuitest(binary_dir, test), fp) + else: + plistlib.dump(xctest(binary_dir, test), fp) + + subprocess.check_call([ + 'xcodebuild', 'test-without-building', '-xctestrun', xctestrun_path, + '-destination', 'platform=iOS Simulator,name=iPhone 8' + ]) + + +# This script is primarily used from the waterfall so that the list of tests +# that are run is maintained in-tree, rather than in a separate infrastructure +# location in the recipe. +def main(args): + parser = argparse.ArgumentParser(description='Run Crashpad unittests.') + parser.add_argument('binary_dir', help='Root of build dir') + parser.add_argument('test', nargs='*', help='Specific test(s) to run.') + parser.add_argument( + '--gtest_filter', + help='Google Test filter applied to Google Test binary runs.') + args = parser.parse_args() + + # Tell 64-bit Windows tests where to find 32-bit test executables, for + # cross-bitted testing. This relies on the fact that the GYP build by + # default uses {Debug,Release} for the 32-bit build and {Debug,Release}_x64 + # for the 64-bit build. This is not a universally valid assumption, and if + # it’s not met, 64-bit tests that require 32-bit build output will disable + # themselves dynamically. + if (sys.platform == 'win32' and args.binary_dir.endswith('_x64') and + 'CRASHPAD_TEST_32_BIT_OUTPUT' not in os.environ): + binary_dir_32 = args.binary_dir[:-4] + if os.path.isdir(binary_dir_32): + os.environ['CRASHPAD_TEST_32_BIT_OUTPUT'] = binary_dir_32 + + target_os = _BinaryDirTargetOS(args.binary_dir) + is_android = target_os == 'android' + is_ios = target_os == 'ios' + + tests = [ + 'crashpad_client_test', + 'crashpad_handler_test', + 'crashpad_minidump_test', + 'crashpad_snapshot_test', + 'crashpad_test_test', + 'crashpad_util_test', + ] + + if is_android: + android_device = os.environ.get('ANDROID_DEVICE') + if not android_device: + adb_devices = subprocess.check_output(['adb', 'devices'], + shell=IS_WINDOWS_HOST, + text=True) + devices = [] + for line in adb_devices.splitlines(): + line = line + if (line == 'List of devices attached' or + re.match('^\* daemon .+ \*$', line) or line == ''): + continue + (device, ignore) = line.split('\t') + devices.append(device) + if len(devices) != 1: + print("Please set ANDROID_DEVICE to your device's id", + file=sys.stderr) + return 2 + android_device = devices[0] + print('Using autodetected Android device:', android_device) + elif is_ios: + tests.append('ios_crash_xcuitests') + elif IS_WINDOWS_HOST: + tests.append('snapshot/win/end_to_end_test.py') + + if args.test: + for t in args.test: + if t not in tests: + print('Unrecognized test:', t, file=sys.stderr) + return 3 + tests = args.test + + for test in tests: + print('-' * 80) + print(test) + print('-' * 80) + if test.endswith('.py'): + subprocess.check_call([ + sys.executable, + os.path.join(CRASHPAD_DIR, test), args.binary_dir + ]) + else: + extra_command_line = [] + if args.gtest_filter: + extra_command_line.append('--gtest_filter=' + args.gtest_filter) + if is_android: + _RunOnAndroidTarget(args.binary_dir, test, android_device, + extra_command_line) + elif is_ios: + _RunOnIOSTarget(args.binary_dir, + test, + is_xcuitest=test.startswith('ios')) + else: + subprocess.check_call([os.path.join(args.binary_dir, test)] + + extra_command_line) + + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/shared/sentry/external/crashpad/build/test.gni b/shared/sentry/external/crashpad/build/test.gni new file mode 100644 index 000000000..01fd7e797 --- /dev/null +++ b/shared/sentry/external/crashpad/build/test.gni @@ -0,0 +1,49 @@ +# Copyright 2017 The Crashpad Authors. All rights reserved. +# +# 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("crashpad_buildconfig.gni") + +if (crashpad_is_in_chromium) { + import("//testing/test.gni") +} else { + template("test") { + if (crashpad_is_ios) { + import("//third_party/mini_chromium/mini_chromium/build/ios/rules.gni") + + _launch_image_bundle_target = target_name + "_launch_image" + bundle_data(_launch_image_bundle_target) { + forward_variables_from(invoker, [ "testonly" ]) + sources = [ "//build/ios/Default.png" ] + outputs = [ "{{bundle_contents_dir}}/{{source_file_part}}" ] + } + + ios_xctest_test(target_name) { + testonly = true + xctest_module_target = "//test/ios:google_test_runner" + info_plist = "//build/ios/Unittest-Info.plist" + extra_substitutions = [ "GTEST_BUNDLE_ID_SUFFIX=$target_name" ] + forward_variables_from(invoker, "*") + if (!defined(deps)) { + deps = [] + } + deps += [ ":$_launch_image_bundle_target" ] + } + } else { + executable(target_name) { + testonly = true + forward_variables_from(invoker, "*") + } + } + } +} diff --git a/shared/sentry/external/crashpad/client/BUILD.gn b/shared/sentry/external/crashpad/client/BUILD.gn new file mode 100644 index 000000000..1a83efa2b --- /dev/null +++ b/shared/sentry/external/crashpad/client/BUILD.gn @@ -0,0 +1,208 @@ +# Copyright 2015 The Crashpad Authors. All rights reserved. +# +# 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("../build/crashpad_buildconfig.gni") + +crashpad_static_library("client") { + sources = [ + "crashpad_client.h", + "prune_crash_reports.cc", + "prune_crash_reports.h", + "simulate_crash.h", + ] + + if (crashpad_is_mac) { + sources += [ + "crashpad_client_mac.cc", + "simulate_crash_mac.cc", + "simulate_crash_mac.h", + ] + } + + if (crashpad_is_ios) { + sources += [ + "crashpad_client_ios.cc", + "ios_handler/exception_processor.h", + "ios_handler/exception_processor.mm", + "ios_handler/in_process_handler.cc", + "ios_handler/in_process_handler.h", + "ios_handler/in_process_intermediate_dump_handler.cc", + "ios_handler/in_process_intermediate_dump_handler.h", + "ios_handler/prune_intermediate_dumps_and_crash_reports_thread.cc", + "ios_handler/prune_intermediate_dumps_and_crash_reports_thread.h", + "simulate_crash_ios.h", + ] + } + + if (crashpad_is_linux || crashpad_is_android) { + sources += [ + "crashpad_client_linux.cc", + "simulate_crash_linux.h", + ] + } + + if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { + sources += [ + "client_argv_handling.cc", + "client_argv_handling.h", + ] + } + + if (crashpad_is_win) { + sources += [ + "crashpad_client_win.cc", + "simulate_crash_win.h", + ] + } + + if (crashpad_is_fuchsia) { + sources += [ "crashpad_client_fuchsia.cc" ] + } + + public_configs = [ "..:crashpad_config" ] + + public_deps = [ + ":common", + "$mini_chromium_source_parent:base", + "../util", + ] + + deps = [ + ":common", + "$mini_chromium_source_parent:chromeos_buildflags", + ] + + if (crashpad_is_win) { + libs = [ "rpcrt4.lib" ] + cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union + } + + if (crashpad_is_ios) { + deps += [ + "../handler:common", + "../minidump", + "../snapshot", + ] + } + + if (crashpad_is_linux || crashpad_is_android) { + deps += [ "../third_party/lss" ] + } + + if (crashpad_is_fuchsia) { + deps += [ "../third_party/fuchsia" ] + if (crashpad_is_in_fuchsia) { + deps += [ "//sdk/lib/fdio" ] + } + } +} + +static_library("common") { + sources = [ + "annotation.cc", + "annotation.h", + "annotation_list.cc", + "annotation_list.h", + "crash_report_database.cc", + "crash_report_database.h", + "crashpad_info.cc", + "crashpad_info.h", + "settings.cc", + "settings.h", + "simple_address_range_bag.h", + "simple_string_dictionary.h", + ] + + if (crashpad_is_mac || crashpad_is_ios) { + sources += [ "crash_report_database_mac.mm" ] + } + if (crashpad_is_win) { + sources += [ "crash_report_database_win.cc" ] + } + if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { + sources += [ + "crash_report_database_generic.cc", + "crashpad_info_note.S", + ] + } + + public_configs = [ "..:crashpad_config" ] + public_deps = [ + "$mini_chromium_source_parent:base", + "../util", + ] + deps = [ "../util" ] +} + +source_set("client_test") { + testonly = true + + sources = [ + "annotation_list_test.cc", + "annotation_test.cc", + "crash_report_database_test.cc", + "prune_crash_reports_test.cc", + "settings_test.cc", + "simple_address_range_bag_test.cc", + "simple_string_dictionary_test.cc", + ] + + if (crashpad_is_mac) { + sources += [ "simulate_crash_mac_test.cc" ] + } + + if (crashpad_is_win) { + sources += [ "crashpad_client_win_test.cc" ] + } + + if (crashpad_is_ios) { + sources += [ + "crashpad_client_ios_test.mm", + "ios_handler/exception_processor_test.mm", + "ios_handler/in_process_handler_test.cc", + "ios_handler/in_process_intermediate_dump_handler_test.cc", + ] + } + + if (crashpad_is_linux || crashpad_is_android) { + sources += [ "crashpad_client_linux_test.cc" ] + } + + deps = [ + ":client", + "$mini_chromium_source_parent:base", + "../compat", + "../snapshot", + "../test", + "../third_party/googletest:googlemock", + "../third_party/googletest:googletest", + "../util", + ] + + if (!crashpad_is_ios && !crashpad_is_fuchsia) { + data_deps = [ "../handler:crashpad_handler" ] + } + + if (crashpad_is_win) { + data_deps += [ "../handler:crashpad_handler_console" ] + } +} + +if (crashpad_is_linux || crashpad_is_android) { + source_set("pthread_create") { + sources = [ "pthread_create_linux.cc" ] + + deps = [ ":client" ] + } +} diff --git a/shared/sentry/external/crashpad/client/CMakeLists.txt b/shared/sentry/external/crashpad/client/CMakeLists.txt new file mode 100644 index 000000000..4b35a0f18 --- /dev/null +++ b/shared/sentry/external/crashpad/client/CMakeLists.txt @@ -0,0 +1,120 @@ +add_library(crashpad_client STATIC + annotation.cc + annotation.h + annotation_list.cc + annotation_list.h + crash_report_database.cc + crash_report_database.h + crashpad_client.h + crashpad_info.cc + crashpad_info.h + prune_crash_reports.cc + prune_crash_reports.h + settings.cc + settings.h + simple_address_range_bag.h + simple_string_dictionary.h + simulate_crash.h +) + +if(APPLE AND NOT IOS) + target_sources(crashpad_client PRIVATE + crash_report_database_mac.mm + crashpad_client_mac.cc + simulate_crash_mac.cc + simulate_crash_mac.h + ) +endif() + +if(IOS) + target_sources(crashpad_client PRIVATE + crash_report_database_mac.mm + crashpad_client_ios.cc + ios_handler/exception_processor.h + ios_handler/exception_processor.mm + ios_handler/in_process_handler.cc + ios_handler/in_process_handler.h + ios_handler/in_process_intermediate_dump_handler.cc + ios_handler/in_process_intermediate_dump_handler.h + ios_handler/prune_intermediate_dumps_and_crash_reports_thread.cc + ios_handler/prune_intermediate_dumps_and_crash_reports_thread.h + simulate_crash_ios.h + ) +endif() + +if(LINUX OR ANDROID) + target_sources(crashpad_client PRIVATE + crashpad_client_linux.cc + simulate_crash_linux.h + client_argv_handling.cc + client_argv_handling.h + crash_report_database_generic.cc + crashpad_info_note.S + ) +endif() + +if(WIN32) + target_sources(crashpad_client PRIVATE + crash_report_database_win.cc + crashpad_client_win.cc + simulate_crash_win.h + ) +endif() + +target_include_directories(crashpad_client INTERFACE + "$" + "$" +) +target_link_libraries(crashpad_client + PRIVATE + $ + PUBLIC + crashpad_compat + crashpad_util + mini_chromium +) +target_compile_features(crashpad_client PUBLIC cxx_std_14) + +set_property(TARGET crashpad_client PROPERTY EXPORT_NAME client) +add_library(crashpad::client ALIAS crashpad_client) + +if(WIN32) + target_link_libraries(crashpad_client PRIVATE rpcrt4) + if(MSVC) + target_compile_options(crashpad_client PRIVATE "/wd4201") + elseif(MINGW) + target_compile_options(crashpad_client PUBLIC + "-municode" + ) + endif() + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(crashpad_client PRIVATE + "-Wno-attributes" + ) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + target_compile_options(crashpad_client PRIVATE + "-Wno-unknown-attributes" + "-Wno-unknown-pragmas" + ) + endif() +endif() + +if(IOS) + target_link_libraries(crashpad_client + PUBLIC + crashpad_minidump + crashpad_snapshot + ) +endif() + +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(crashpad_client PRIVATE + "-Wno-multichar" + ) +endif() + +crashpad_install_target(crashpad_client) +crashpad_install_dev(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/crashpad/client" + FILES_MATCHING PATTERN "*.h" +) diff --git a/shared/sentry/external/crashpad/client/annotation.cc b/shared/sentry/external/crashpad/client/annotation.cc new file mode 100644 index 000000000..71b64989c --- /dev/null +++ b/shared/sentry/external/crashpad/client/annotation.cc @@ -0,0 +1,43 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "client/annotation.h" + +#include + +#include "base/check_op.h" +#include "client/annotation_list.h" + +namespace crashpad { + +static_assert(std::is_standard_layout::value, + "Annotation must be POD"); + +// static +constexpr size_t Annotation::kNameMaxLength; +constexpr size_t Annotation::kValueMaxSize; + +void Annotation::SetSize(ValueSizeType size) { + DCHECK_LT(size, kValueMaxSize); + size_ = size; + // Use Register() instead of Get() in case the calling module has not + // explicitly initialized the annotation list, to avoid crashing. + AnnotationList::Register()->Add(this); +} + +void Annotation::Clear() { + size_ = 0; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/annotation.h b/shared/sentry/external/crashpad/client/annotation.h new file mode 100644 index 000000000..aa110bd67 --- /dev/null +++ b/shared/sentry/external/crashpad/client/annotation.h @@ -0,0 +1,272 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_CLIENT_ANNOTATION_H_ +#define CRASHPAD_CLIENT_ANNOTATION_H_ + +#include +#include + +#include +#include +#include + +#include "base/check.h" +#include "base/numerics/safe_conversions.h" +#include "base/strings/string_piece.h" +#include "build/build_config.h" + +namespace crashpad { +#if BUILDFLAG(IS_IOS) +namespace internal { +class InProcessIntermediateDumpHandler; +} // namespace internal +#endif +class AnnotationList; + +//! \brief Base class for an annotation, which records a name-value pair of +//! arbitrary data when set. +//! +//! After an annotation is declared, its `value_ptr_` will not be captured in a +//! crash report until a call to \a SetSize() specifies how much data from the +//! value should be recorded. +//! +//! Annotations should be declared with static storage duration. +//! +//! An example declaration and usage: +//! +//! \code +//! // foo.cc: +//! +//! namespace { +//! char g_buffer[1024]; +//! crashpad::Annotation g_buffer_annotation( +//! crashpad::Annotation::Type::kString, "buffer_head", g_buffer); +//! } // namespace +//! +//! void OnBufferProduced(size_t n) { +//! // Capture the head of the buffer, in case we crash when parsing it. +//! g_buffer_annotation.SetSize(std::min(64, n)); +//! +//! // Start parsing the header. +//! Frobinate(g_buffer, n); +//! } +//! \endcode +//! +//! Annotation objects are not inherently thread-safe. To manipulate them +//! from multiple threads, external synchronization must be used. +//! +//! Annotation objects should never be destroyed. Once they are Set(), they +//! are permanently referenced by a global object. +class Annotation { + public: + //! \brief The maximum length of an annotation’s name, in bytes. + static constexpr size_t kNameMaxLength = 64; + + //! \brief The maximum size of an annotation’s value, in bytes. + static constexpr size_t kValueMaxSize = 5 * 4096; + + //! \brief The type used for \a SetSize(). + using ValueSizeType = uint32_t; + + //! \brief The type of data stored in the annotation. + enum class Type : uint16_t { + //! \brief An invalid annotation. Reserved for internal use. + kInvalid = 0, + + //! \brief A `NUL`-terminated C-string. + kString = 1, + + //! \brief Clients may declare their own custom types by using values + //! greater than this. + kUserDefinedStart = 0x8000, + }; + + //! \brief Creates a user-defined Annotation::Type. + //! + //! This exists to remove the casting overhead of `enum class`. + //! + //! \param[in] value A value used to create a user-defined type. + //! + //! \returns The value added to Type::kUserDefinedStart and casted. + constexpr static Type UserDefinedType(uint16_t value) { + using UnderlyingType = std::underlying_type::type; + // MSVS 2015 doesn't have full C++14 support and complains about local + // variables defined in a constexpr function, which is valid. Avoid them + // and the also-problematic DCHECK until all the infrastructure is updated: + // https://crbug.com/crashpad/201. +#if !BUILDFLAG(IS_WIN) || (defined(_MSC_VER) && _MSC_VER >= 1910) + const UnderlyingType start = + static_cast(Type::kUserDefinedStart); + const UnderlyingType user_type = start + value; + DCHECK(user_type > start) << "User-defined Type is 0 or overflows"; + return static_cast(user_type); +#else + return static_cast( + static_cast(Type::kUserDefinedStart) + value); +#endif + } + + //! \brief Constructs a new annotation. + //! + //! Upon construction, the annotation will not be included in any crash + //! reports until \sa SetSize() is called with a value greater than `0`. + //! + //! \param[in] type The data type of the value of the annotation. + //! \param[in] name A `NUL`-terminated C-string name for the annotation. Names + //! do not have to be unique, though not all crash processors may handle + //! Annotations with the same name. Names should be constexpr data with + //! static storage duration. + //! \param[in] value_ptr A pointer to the value for the annotation. The + //! pointer may not be changed once associated with an annotation, but + //! the data may be mutated. + constexpr Annotation(Type type, const char name[], void* const value_ptr) + : link_node_(nullptr), + name_(name), + value_ptr_(value_ptr), + size_(0), + type_(type) {} + + Annotation(const Annotation&) = delete; + Annotation& operator=(const Annotation&) = delete; + + //! \brief Specifies the number of bytes in \a value_ptr_ to include when + //! generating a crash report. + //! + //! A size of `0` indicates that no value should be recorded and is the + //! equivalent of calling \sa Clear(). + //! + //! This method does not mutate the data referenced by the annotation, it + //! merely updates the annotation system's bookkeeping. + //! + //! Subclasses of this base class that provide additional Set methods to + //! mutate the value of the annotation must call always call this method. + //! + //! \param[in] size The number of bytes. + void SetSize(ValueSizeType size); + + //! \brief Marks the annotation as cleared, indicating the \a value_ptr_ + //! should not be included in a crash report. + //! + //! This method does not mutate the data referenced by the annotation, it + //! merely updates the annotation system's bookkeeping. + void Clear(); + + //! \brief Tests whether the annotation has been set. + bool is_set() const { return size_ > 0; } + + Type type() const { return type_; } + ValueSizeType size() const { return size_; } + const char* name() const { return name_; } + const void* value() const { return value_ptr_; } + + protected: + friend class AnnotationList; +#if BUILDFLAG(IS_IOS) + friend class internal::InProcessIntermediateDumpHandler; +#endif + + std::atomic& link_node() { return link_node_; } + + private: + //! \brief Linked list next-node pointer. Accessed only by \sa AnnotationList. + //! + //! This will be null until the first call to \sa SetSize(), after which the + //! presence of the pointer will prevent the node from being added to the + //! list again. + std::atomic link_node_; + + const char* const name_; + void* const value_ptr_; + ValueSizeType size_; + const Type type_; +}; + +//! \brief An \sa Annotation that stores a `NUL`-terminated C-string value. +//! +//! The storage for the value is allocated by the annotation and the template +//! parameter \a MaxSize controls the maxmium length for the value. +//! +//! It is expected that the string value be valid UTF-8, although this is not +//! validated. +template +class StringAnnotation : public Annotation { + public: + //! \brief A constructor tag that enables braced initialization in C arrays. + //! + //! \sa StringAnnotation() + enum class Tag { kArray }; + + //! \brief Constructs a new StringAnnotation with the given \a name. + //! + //! \param[in] name The Annotation name. + constexpr explicit StringAnnotation(const char name[]) + : Annotation(Type::kString, name, value_), value_() {} + + StringAnnotation(const StringAnnotation&) = delete; + StringAnnotation& operator=(const StringAnnotation&) = delete; + + //! \brief Constructs a new StringAnnotation with the given \a name. + //! + //! This constructor takes the ArrayInitializerTag for use when + //! initializing a C array of annotations. The main constructor is + //! explicit and cannot be brace-initialized. As an example: + //! + //! \code + //! static crashpad::StringAnnotation<32> annotations[] = { + //! {"name-1", crashpad::StringAnnotation<32>::Tag::kArray}, + //! {"name-2", crashpad::StringAnnotation<32>::Tag::kArray}, + //! {"name-3", crashpad::StringAnnotation<32>::Tag::kArray}, + //! }; + //! \endcode + //! + //! \param[in] name The Annotation name. + //! \param[in] tag A constructor tag. + constexpr StringAnnotation(const char name[], Tag tag) + : StringAnnotation(name) {} + + //! \brief Sets the Annotation's string value. + //! + //! \param[in] value The `NUL`-terminated C-string value. + void Set(const char* value) { + strncpy(value_, value, MaxSize); + SetSize( + std::min(MaxSize, base::saturated_cast(strlen(value)))); + } + + //! \brief Sets the Annotation's string value. + //! + //! \param[in] string The string value. + void Set(base::StringPiece string) { + Annotation::ValueSizeType size = + std::min(MaxSize, base::saturated_cast(string.size())); + memcpy(value_, string.data(), size); + // Check for no embedded `NUL` characters. + DCHECK(!memchr(value_, '\0', size)) << "embedded NUL"; + SetSize(size); + } + + const base::StringPiece value() const { + return base::StringPiece(value_, size()); + } + + private: + // This value is not `NUL`-terminated, since the size is stored by the base + // annotation. + char value_[MaxSize]; +}; + +} // namespace crashpad + +#endif // CRASHPAD_CLIENT_ANNOTATION_H_ diff --git a/shared/sentry/external/crashpad/client/annotation_list.cc b/shared/sentry/external/crashpad/client/annotation_list.cc new file mode 100644 index 000000000..f847d7cf5 --- /dev/null +++ b/shared/sentry/external/crashpad/client/annotation_list.cc @@ -0,0 +1,97 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "client/annotation_list.h" + +#include "base/check_op.h" +#include "client/crashpad_info.h" + +namespace crashpad { + +AnnotationList::AnnotationList() + : tail_pointer_(&tail_), + head_(Annotation::Type::kInvalid, nullptr, nullptr), + tail_(Annotation::Type::kInvalid, nullptr, nullptr) { + head_.link_node().store(&tail_); +} + +AnnotationList::~AnnotationList() {} + +// static +AnnotationList* AnnotationList::Get() { + return CrashpadInfo::GetCrashpadInfo()->annotations_list(); +} + +// static +AnnotationList* AnnotationList::Register() { + AnnotationList* list = Get(); + if (!list) { + list = new AnnotationList(); + CrashpadInfo::GetCrashpadInfo()->set_annotations_list(list); + } + return list; +} + +void AnnotationList::Add(Annotation* annotation) { + Annotation* null = nullptr; + Annotation* head_next = head_.link_node().load(std::memory_order_relaxed); + if (!annotation->link_node().compare_exchange_strong(null, head_next)) { + // If |annotation|'s link node is not null, then it has been added to the + // list already and no work needs to be done. + return; + } + + // Check that the annotation's name is less than the maximum size. This is + // done here, since the Annotation constructor must be constexpr and this + // path is taken once per annotation. + DCHECK_LT(strlen(annotation->name_), Annotation::kNameMaxLength); + + // Update the head link to point to the new |annotation|. + while (!head_.link_node().compare_exchange_weak(head_next, annotation)) { + // Another thread has updated the head-next pointer, so try again with the + // re-loaded |head_next|. + annotation->link_node().store(head_next, std::memory_order_relaxed); + } +} + +AnnotationList::Iterator::Iterator(Annotation* head, const Annotation* tail) + : curr_(head), tail_(tail) {} + +AnnotationList::Iterator::~Iterator() = default; + +Annotation* AnnotationList::Iterator::operator*() const { + CHECK_NE(curr_, tail_); + return curr_; +} + +AnnotationList::Iterator& AnnotationList::Iterator::operator++() { + CHECK_NE(curr_, tail_); + curr_ = curr_->link_node(); + return *this; +} + +bool AnnotationList::Iterator::operator==( + const AnnotationList::Iterator& other) const { + return curr_ == other.curr_; +} + +AnnotationList::Iterator AnnotationList::begin() { + return Iterator(head_.link_node(), tail_pointer_); +} + +AnnotationList::Iterator AnnotationList::end() { + return Iterator(&tail_, tail_pointer_); +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/annotation_list.h b/shared/sentry/external/crashpad/client/annotation_list.h new file mode 100644 index 000000000..1c2141fe6 --- /dev/null +++ b/shared/sentry/external/crashpad/client/annotation_list.h @@ -0,0 +1,112 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_CLIENT_ANNOTATION_LIST_H_ +#define CRASHPAD_CLIENT_ANNOTATION_LIST_H_ + +#include "build/build_config.h" +#include "client/annotation.h" + +namespace crashpad { +#if BUILDFLAG(IS_IOS) +namespace internal { +class InProcessIntermediateDumpHandler; +} // namespace internal +#endif + +//! \brief A list that contains all the currently set annotations. +//! +//! An instance of this class must be registered on the \a CrashpadInfo +//! structure in order to use the annotations system. Once a list object has +//! been registered on the CrashpadInfo, a different instance should not +//! be used instead. +class AnnotationList { + public: + AnnotationList(); + + AnnotationList(const AnnotationList&) = delete; + AnnotationList& operator=(const AnnotationList&) = delete; + + ~AnnotationList(); + + //! \brief Returns the instance of the list that has been registered on the + //! CrashapdInfo structure. + static AnnotationList* Get(); + + //! \brief Returns the instace of the list, creating and registering + //! it if one is not already set on the CrashapdInfo structure. + static AnnotationList* Register(); + + //! \brief Adds \a annotation to the global list. This method does not need + //! to be called by clients directly. The Annotation object will do so + //! automatically. + //! + //! Once an annotation is added to the list, it is not removed. This is + //! because the AnnotationList avoids the use of locks/mutexes, in case it is + //! being manipulated in a compromised context. Instead, an Annotation keeps + //! track of when it has been cleared, which excludes it from a crash report. + //! This design also avoids linear scans of the list when repeatedly setting + //! and/or clearing the value. + void Add(Annotation* annotation); + + //! \brief An InputIterator for the AnnotationList. + class Iterator { + public: + ~Iterator(); + + Annotation* operator*() const; + Iterator& operator++(); + bool operator==(const Iterator& other) const; + bool operator!=(const Iterator& other) const { return !(*this == other); } + + private: + friend class AnnotationList; + Iterator(Annotation* head, const Annotation* tail); + + Annotation* curr_; + const Annotation* const tail_; + + // Copy and assign are required. + }; + + //! \brief Returns an iterator to the first element of the annotation list. + Iterator begin(); + + //! \brief Returns an iterator past the last element of the annotation list. + Iterator end(); + + protected: +#if BUILDFLAG(IS_IOS) + friend class internal::InProcessIntermediateDumpHandler; +#endif + + //! \brief Returns a pointer to the tail node. + const Annotation* tail_pointer() const { return tail_pointer_; } + + //! \brief Returns a pointer to the head element. + const Annotation* head() const { return &head_; } + + private: + // To make it easier for the handler to locate the dummy tail node, store the + // pointer. Placed first for packing. + const Annotation* const tail_pointer_; + + // Dummy linked-list head and tail elements of \a Annotation::Type::kInvalid. + Annotation head_; + Annotation tail_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_CLIENT_ANNOTATION_LIST_H_ diff --git a/shared/sentry/external/crashpad/client/annotation_list_test.cc b/shared/sentry/external/crashpad/client/annotation_list_test.cc new file mode 100644 index 000000000..ef3039c5f --- /dev/null +++ b/shared/sentry/external/crashpad/client/annotation_list_test.cc @@ -0,0 +1,183 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "client/annotation.h" + +#include +#include + +#include "base/rand_util.h" +#include "client/crashpad_info.h" +#include "gtest/gtest.h" +#include "util/misc/clock.h" +#include "util/thread/thread.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(AnnotationListStatic, Register) { + ASSERT_FALSE(AnnotationList::Get()); + EXPECT_TRUE(AnnotationList::Register()); + EXPECT_TRUE(AnnotationList::Get()); + EXPECT_EQ(AnnotationList::Get(), AnnotationList::Register()); + + // This isn't expected usage of the AnnotationList API, but it is necessary + // for testing. + AnnotationList* list = AnnotationList::Get(); + CrashpadInfo::GetCrashpadInfo()->set_annotations_list(nullptr); + delete list; + + EXPECT_FALSE(AnnotationList::Get()); +} + +class AnnotationList : public testing::Test { + public: + void SetUp() override { + CrashpadInfo::GetCrashpadInfo()->set_annotations_list(&annotations_); + } + + void TearDown() override { + CrashpadInfo::GetCrashpadInfo()->set_annotations_list(nullptr); + } + + // NOTE: Annotations should be declared at file-scope, but in order to test + // them, they are declared as part of the test. These members are public so + // they are accessible from global helpers. + crashpad::StringAnnotation<8> one_{"First"}; + crashpad::StringAnnotation<256> two_{"Second"}; + crashpad::StringAnnotation<101> three_{"First"}; + + protected: + using AllAnnotations = std::vector>; + + AllAnnotations CollectAnnotations() { + AllAnnotations annotations; + + for (Annotation* curr : annotations_) { + if (!curr->is_set()) + continue; + std::string value(static_cast(curr->value()), curr->size()); + annotations.push_back(std::make_pair(curr->name(), value)); + } + + return annotations; + } + + bool ContainsNameValue(const AllAnnotations& annotations, + const std::string& name, + const std::string& value) { + return std::find(annotations.begin(), + annotations.end(), + std::make_pair(name, value)) != annotations.end(); + } + + crashpad::AnnotationList annotations_; +}; + +TEST_F(AnnotationList, SetAndClear) { + one_.Set("this is a value longer than 8 bytes"); + AllAnnotations annotations = CollectAnnotations(); + + EXPECT_EQ(1u, annotations.size()); + EXPECT_TRUE(ContainsNameValue(annotations, "First", "this is ")); + + one_.Clear(); + + EXPECT_EQ(0u, CollectAnnotations().size()); + + one_.Set("short"); + two_.Set(std::string(500, 'A').data()); + + annotations = CollectAnnotations(); + EXPECT_EQ(2u, annotations.size()); + + EXPECT_EQ(5u, one_.size()); + EXPECT_EQ(256u, two_.size()); + + EXPECT_TRUE(ContainsNameValue(annotations, "First", "short")); + EXPECT_TRUE(ContainsNameValue(annotations, "Second", std::string(256, 'A'))); +} + +TEST_F(AnnotationList, DuplicateKeys) { + ASSERT_EQ(0u, CollectAnnotations().size()); + + one_.Set("1"); + three_.Set("2"); + + AllAnnotations annotations = CollectAnnotations(); + EXPECT_EQ(2u, annotations.size()); + + EXPECT_TRUE(ContainsNameValue(annotations, "First", "1")); + EXPECT_TRUE(ContainsNameValue(annotations, "First", "2")); + + one_.Clear(); + + annotations = CollectAnnotations(); + EXPECT_EQ(1u, annotations.size()); +} + +class RaceThread : public Thread { + public: + explicit RaceThread(test::AnnotationList* test) : Thread(), test_(test) {} + + private: + void ThreadMain() override { + for (int i = 0; i <= 50; ++i) { + if (i % 2 == 0) { + test_->three_.Set("three"); + test_->two_.Clear(); + } else { + test_->three_.Clear(); + } + SleepNanoseconds(base::RandInt(1, 1000)); + } + } + + test::AnnotationList* test_; +}; + +TEST_F(AnnotationList, MultipleThreads) { + ASSERT_EQ(0u, CollectAnnotations().size()); + + RaceThread other_thread(this); + other_thread.Start(); + + for (int i = 0; i <= 50; ++i) { + if (i % 2 == 0) { + one_.Set("one"); + two_.Set("two"); + } else { + one_.Clear(); + } + SleepNanoseconds(base::RandInt(1, 1000)); + } + + other_thread.Join(); + + AllAnnotations annotations = CollectAnnotations(); + EXPECT_GE(annotations.size(), 2u); + EXPECT_LE(annotations.size(), 3u); + + EXPECT_TRUE(ContainsNameValue(annotations, "First", "one")); + EXPECT_TRUE(ContainsNameValue(annotations, "First", "three")); + + if (annotations.size() == 3) { + EXPECT_TRUE(ContainsNameValue(annotations, "Second", "two")); + } +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/annotation_test.cc b/shared/sentry/external/crashpad/client/annotation_test.cc new file mode 100644 index 000000000..365c0c22b --- /dev/null +++ b/shared/sentry/external/crashpad/client/annotation_test.cc @@ -0,0 +1,135 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "client/annotation.h" + +#include +#include + +#include "client/annotation_list.h" +#include "client/crashpad_info.h" +#include "gtest/gtest.h" +#include "test/gtest_death.h" + +namespace crashpad { +namespace test { +namespace { + +class Annotation : public testing::Test { + public: + void SetUp() override { + CrashpadInfo::GetCrashpadInfo()->set_annotations_list(&annotations_); + } + + void TearDown() override { + CrashpadInfo::GetCrashpadInfo()->set_annotations_list(nullptr); + } + + size_t AnnotationsCount() { + size_t result = 0; + for (auto* annotation : annotations_) { + if (annotation->is_set()) + ++result; + } + return result; + } + + protected: + crashpad::AnnotationList annotations_; +}; + +TEST_F(Annotation, Basics) { + constexpr crashpad::Annotation::Type kType = + crashpad::Annotation::UserDefinedType(1); + + const char kName[] = "annotation 1"; + char buffer[1024]; + crashpad::Annotation annotation(kType, kName, buffer); + + EXPECT_FALSE(annotation.is_set()); + EXPECT_EQ(0u, AnnotationsCount()); + + EXPECT_EQ(kType, annotation.type()); + EXPECT_EQ(0u, annotation.size()); + EXPECT_EQ(std::string(kName), annotation.name()); + EXPECT_EQ(buffer, annotation.value()); + + annotation.SetSize(10); + + EXPECT_TRUE(annotation.is_set()); + EXPECT_EQ(1u, AnnotationsCount()); + + EXPECT_EQ(10u, annotation.size()); + EXPECT_EQ(&annotation, *annotations_.begin()); + + annotation.Clear(); + + EXPECT_FALSE(annotation.is_set()); + EXPECT_EQ(0u, AnnotationsCount()); + + EXPECT_EQ(0u, annotation.size()); +} + +TEST_F(Annotation, StringType) { + crashpad::StringAnnotation<5> annotation("name"); + + EXPECT_FALSE(annotation.is_set()); + + EXPECT_EQ(crashpad::Annotation::Type::kString, annotation.type()); + EXPECT_EQ(0u, annotation.size()); + EXPECT_EQ(std::string("name"), annotation.name()); + EXPECT_EQ(0u, annotation.value().size()); + + annotation.Set("test"); + + EXPECT_TRUE(annotation.is_set()); + EXPECT_EQ(1u, AnnotationsCount()); + + EXPECT_EQ(4u, annotation.size()); + EXPECT_EQ("test", annotation.value()); + + annotation.Set(std::string("loooooooooooong")); + + EXPECT_TRUE(annotation.is_set()); + EXPECT_EQ(1u, AnnotationsCount()); + + EXPECT_EQ(5u, annotation.size()); + EXPECT_EQ("loooo", annotation.value()); +} + +TEST(StringAnnotation, ArrayOfString) { + static crashpad::StringAnnotation<4> annotations[] = { + {"test-1", crashpad::StringAnnotation<4>::Tag::kArray}, + {"test-2", crashpad::StringAnnotation<4>::Tag::kArray}, + {"test-3", crashpad::StringAnnotation<4>::Tag::kArray}, + {"test-4", crashpad::StringAnnotation<4>::Tag::kArray}, + }; + + for (auto& annotation : annotations) { + EXPECT_FALSE(annotation.is_set()); + } +} + +#if DCHECK_IS_ON() + +TEST(AnnotationDeathTest, EmbeddedNUL) { + crashpad::StringAnnotation<5> annotation("name"); + EXPECT_DEATH_CHECK(annotation.Set(std::string("te\0st", 5)), "embedded NUL"); +} + +#endif + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/client_argv_handling.cc b/shared/sentry/external/crashpad/client/client_argv_handling.cc new file mode 100644 index 000000000..a3b9a5f3e --- /dev/null +++ b/shared/sentry/external/crashpad/client/client_argv_handling.cc @@ -0,0 +1,80 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "client/client_argv_handling.h" + +#include "base/strings/stringprintf.h" + +namespace crashpad { + +namespace { + +std::string FormatArgumentString(const std::string& name, + const std::string& value) { + return base::StringPrintf("--%s=%s", name.c_str(), value.c_str()); +} + +} // namespace + +std::vector BuildHandlerArgvStrings( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments, + const std::vector& attachments) { + std::vector argv_strings(1, handler.value()); + + for (const auto& argument : arguments) { + argv_strings.push_back(argument); + } + + if (!database.empty()) { + argv_strings.push_back(FormatArgumentString("database", database.value())); + } + + if (!metrics_dir.empty()) { + argv_strings.push_back( + FormatArgumentString("metrics-dir", metrics_dir.value())); + } + + if (!url.empty()) { + argv_strings.push_back(FormatArgumentString("url", url)); + } + + for (const auto& kv : annotations) { + argv_strings.push_back( + FormatArgumentString("annotation", kv.first + '=' + kv.second)); + } + + for (const auto& attachment : attachments) { + argv_strings.push_back( + FormatArgumentString("attachment", attachment.value())); + } + + return argv_strings; +} + +void StringVectorToCStringVector(const std::vector& strings, + std::vector* c_strings) { + c_strings->clear(); + c_strings->reserve(strings.size() + 1); + for (const auto& str : strings) { + c_strings->push_back(str.c_str()); + } + c_strings->push_back(nullptr); +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/client_argv_handling.h b/shared/sentry/external/crashpad/client/client_argv_handling.h new file mode 100644 index 000000000..bfb54006e --- /dev/null +++ b/shared/sentry/external/crashpad/client/client_argv_handling.h @@ -0,0 +1,52 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_CLIENT_CLIENT_ARGV_HANDLING_H_ +#define CRASHPAD_CLIENT_CLIENT_ARGV_HANDLING_H_ + +#include +#include +#include + +#include "base/files/file_path.h" + +namespace crashpad { + +//! \brief Builds a vector of arguments suitable for invoking a handler process +//! based on arguments passed to StartHandler-type(). +//! +//! See StartHandlerAtCrash() for documentation on the input arguments. +//! +//! \return A vector of arguments suitable for starting the handler with. +std::vector BuildHandlerArgvStrings( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments, + const std::vector& attachments = {}); + +//! \brief Flattens a string vector into a const char* vector suitable for use +//! in an exec() call. +//! +//! \param[in] strings A vector of string data. This vector must remain valid +//! for the lifetime of \a c_strings. +//! \param[out] c_strings A vector of pointers to the string data in \a strings. +void StringVectorToCStringVector(const std::vector& strings, + std::vector* c_strings); + +} // namespace crashpad + +#endif // CRASHPAD_CLIENT_CLIENT_ARGV_HANDLING_H_ diff --git a/shared/sentry/external/crashpad/client/crash_report_database.cc b/shared/sentry/external/crashpad/client/crash_report_database.cc new file mode 100644 index 000000000..7a3732b8d --- /dev/null +++ b/shared/sentry/external/crashpad/client/crash_report_database.cc @@ -0,0 +1,215 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "client/crash_report_database.h" + +#include + +#include "base/logging.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" +#include "util/file/directory_reader.h" +#include "util/file/filesystem.h" + +namespace crashpad { + +namespace { +constexpr base::FilePath::CharType kAttachmentsDirectory[] = + FILE_PATH_LITERAL("attachments"); + +bool AttachmentNameIsOK(const std::string& name) { + for (const char c : name) { + if (c != '_' && c != '-' && c != '.' && !isalnum(c)) + return false; + } + return true; +} +} // namespace + +CrashReportDatabase::Report::Report() + : uuid(), + file_path(), + id(), + creation_time(0), + uploaded(false), + last_upload_attempt_time(0), + upload_attempts(0), + upload_explicitly_requested(false), + total_size(0u) {} + +CrashReportDatabase::NewReport::NewReport() + : writer_(std::make_unique()), + file_remover_(), + attachment_writers_(), + attachment_removers_(), + uuid_(), + database_() {} + +CrashReportDatabase::NewReport::~NewReport() = default; + +bool CrashReportDatabase::NewReport::Initialize( + CrashReportDatabase* database, + const base::FilePath& directory, + const base::FilePath::StringType& extension) { + database_ = database; + + if (!uuid_.InitializeWithNew()) { + return false; + } + +#if BUILDFLAG(IS_WIN) + const std::wstring uuid_string = uuid_.ToWString(); +#else + const std::string uuid_string = uuid_.ToString(); +#endif + + const base::FilePath path = directory.Append(uuid_string + extension); + if (!writer_->Open( + path, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly)) { + return false; + } + file_remover_.reset(path); + return true; +} + +FileReaderInterface* CrashReportDatabase::NewReport::Reader() { + auto reader = std::make_unique(); + if (!reader->Open(file_remover_.get())) { + return nullptr; + } + reader_ = std::move(reader); + return reader_.get(); +} + +FileWriter* CrashReportDatabase::NewReport::AddAttachment( + const std::string& name) { + if (!AttachmentNameIsOK(name)) { + LOG(ERROR) << "invalid name for attachment " << name; + return nullptr; + } + base::FilePath report_attachments_dir = database_->AttachmentsPath(uuid_); + if (!LoggingCreateDirectory( + report_attachments_dir, FilePermissions::kOwnerOnly, true)) { + return nullptr; + } +#if BUILDFLAG(IS_WIN) + const std::wstring name_string = base::UTF8ToWide(name); +#else + const std::string name_string = name; +#endif + base::FilePath attachment_path = report_attachments_dir.Append(name_string); + auto writer = std::make_unique(); + if (!writer->Open(attachment_path, + FileWriteMode::kCreateOrFail, + FilePermissions::kOwnerOnly)) { + return nullptr; + } + attachment_writers_.emplace_back(std::move(writer)); + attachment_removers_.emplace_back(ScopedRemoveFile(attachment_path)); + return attachment_writers_.back().get(); +} + +void CrashReportDatabase::UploadReport::InitializeAttachments() { + base::FilePath report_attachments_dir = database_->AttachmentsPath(uuid); + DirectoryReader dir_reader; + if (!dir_reader.Open(report_attachments_dir)) { + return; + } + + base::FilePath filename; + DirectoryReader::Result dir_result; + while ((dir_result = dir_reader.NextFile(&filename)) == + DirectoryReader::Result::kSuccess) { + const base::FilePath filepath(report_attachments_dir.Append(filename)); + std::unique_ptr file_reader(std::make_unique()); + if (!file_reader->Open(filepath)) { + continue; + } + attachment_readers_.emplace_back(std::move(file_reader)); +#if BUILDFLAG(IS_WIN) + const std::string name_string = base::WideToUTF8(filename.value()); +#else + const std::string name_string = filename.value(); +#endif + attachment_map_[name_string] = attachment_readers_.back().get(); + } +} + +CrashReportDatabase::UploadReport::UploadReport() + : Report(), + reader_(std::make_unique()), + database_(nullptr), + attachment_readers_(), + attachment_map_(), + report_metrics_(false) {} + +CrashReportDatabase::UploadReport::~UploadReport() { + if (database_) { + database_->RecordUploadAttempt(this, false, std::string()); + } +} + +bool CrashReportDatabase::UploadReport::Initialize(const base::FilePath& path, + CrashReportDatabase* db) { + database_ = db; + InitializeAttachments(); + return reader_->Open(path); +} + +CrashReportDatabase::OperationStatus CrashReportDatabase::RecordUploadComplete( + std::unique_ptr report_in, + const std::string& id) { + UploadReport* report = const_cast(report_in.get()); + + report->database_ = nullptr; + return RecordUploadAttempt(report, true, id); +} + +base::FilePath CrashReportDatabase::AttachmentsPath(const UUID& uuid) { +#if BUILDFLAG(IS_WIN) + const std::wstring uuid_string = uuid.ToWString(); +#else + const std::string uuid_string = uuid.ToString(); +#endif + + return DatabasePath().Append(kAttachmentsDirectory).Append(uuid_string); +} + +base::FilePath CrashReportDatabase::AttachmentsRootPath() { + return DatabasePath().Append(kAttachmentsDirectory); +} + +void CrashReportDatabase::RemoveAttachmentsByUUID(const UUID& uuid) { + base::FilePath report_attachment_dir = AttachmentsPath(uuid); + if (!IsDirectory(report_attachment_dir, /*allow_symlinks=*/false)) { + return; + } + DirectoryReader reader; + if (!reader.Open(report_attachment_dir)) { + return; + } + + base::FilePath filename; + DirectoryReader::Result result; + while ((result = reader.NextFile(&filename)) == + DirectoryReader::Result::kSuccess) { + const base::FilePath attachment_path( + report_attachment_dir.Append(filename)); + LoggingRemoveFile(attachment_path); + } + + LoggingRemoveDirectory(report_attachment_dir); +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/crash_report_database.h b/shared/sentry/external/crashpad/client/crash_report_database.h new file mode 100644 index 000000000..fea49853a --- /dev/null +++ b/shared/sentry/external/crashpad/client/crash_report_database.h @@ -0,0 +1,457 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_CLIENT_CRASH_REPORT_DATABASE_H_ +#define CRASHPAD_CLIENT_CRASH_REPORT_DATABASE_H_ + +#include +#include + +#include +#include +#include +#include + +#include "base/files/file_path.h" +#include "util/file/file_io.h" +#include "util/file/file_reader.h" +#include "util/file/file_writer.h" +#include "util/file/scoped_remove_file.h" +#include "util/misc/metrics.h" +#include "util/misc/uuid.h" + +namespace crashpad { + +class Settings; + +//! \brief An interface for managing a collection of crash report files and +//! metadata associated with the crash reports. +//! +//! All Report objects that are returned by this class are logically const. +//! They are snapshots of the database at the time the query was run, and the +//! data returned is liable to change after the query is executed. +//! +//! The lifecycle of a crash report has three stages: +//! +//! 1. New: A crash report is created with PrepareNewCrashReport(), the +//! the client then writes the report, and then calls +//! FinishedWritingCrashReport() to make the report Pending. +//! 2. Pending: The report has been written but has not been locally +//! processed, or it was has been brought back from 'Completed' state by +//! user request. +//! 3. Completed: The report has been locally processed, either by uploading +//! it to a collection server and calling RecordUploadComplete(), or by +//! calling SkipReportUpload(). +class CrashReportDatabase { + public: + //! \brief A crash report record. + //! + //! This represents the metadata for a crash report, as well as the location + //! of the report itself. A CrashReportDatabase maintains at least this + //! information. + struct Report { + Report(); + + //! A unique identifier by which this report will always be known to the + //! database. + UUID uuid; + + //! The current location of the crash report on the client’s filesystem. + //! The location of a crash report may change over time, so the UUID should + //! be used as the canonical identifier. + base::FilePath file_path; + + //! An identifier issued to this crash report by a collection server. + std::string id; + + //! The time at which the report was generated. + time_t creation_time; + + //! Whether this crash report was successfully uploaded to a collection + //! server. + bool uploaded; + + //! The last timestamp at which an attempt was made to submit this crash + //! report to a collection server. If this is zero, then the report has + //! never been uploaded. If #uploaded is true, then this timestamp is the + //! time at which the report was uploaded, and no other attempts to upload + //! this report will be made. + time_t last_upload_attempt_time; + + //! The number of times an attempt was made to submit this report to + //! a collection server. If this is more than zero, then + //! #last_upload_attempt_time will be set to the timestamp of the most + //! recent attempt. + int upload_attempts; + + //! Whether this crash report was explicitly requested by user to be + //! uploaded. This can be true only if report is in the 'pending' state. + bool upload_explicitly_requested; + + //! The total size in bytes taken by the report, including any potential + //! attachments. + uint64_t total_size; + }; + + //! \brief A crash report that is in the process of being written. + //! + //! An instance of this class should be created via PrepareNewCrashReport(). + class NewReport { + public: + NewReport(); + + NewReport(const NewReport&) = delete; + NewReport& operator=(const NewReport&) = delete; + + ~NewReport(); + + //! \brief An open FileWriter with which to write the report. + FileWriter* Writer() const { return writer_.get(); } + + //! \brief Returns a FileReaderInterface to the report, or `nullptr` with a + //! message logged. + FileReaderInterface* Reader(); + + //! A unique identifier by which this report will always be known to the + //! database. + const UUID& ReportID() const { return uuid_; } + + //! \brief Adds an attachment to the report. + //! + //! \param[in] name The key and name for the attachment, which will be + //! included in the http upload. The attachment will not appear in the + //! minidump report. \a name should only use characters from the set + //! `[a-zA-Z0-9._-]`. + //! \return A FileWriter that the caller should use to write the contents of + //! the attachment, or `nullptr` on failure with an error logged. + FileWriter* AddAttachment(const std::string& name); + + private: + friend class CrashReportDatabaseGeneric; + friend class CrashReportDatabaseMac; + friend class CrashReportDatabaseWin; + + bool Initialize(CrashReportDatabase* database, + const base::FilePath& directory, + const base::FilePath::StringType& extension); + + std::unique_ptr writer_; + std::unique_ptr reader_; + ScopedRemoveFile file_remover_; + std::vector> attachment_writers_; + std::vector attachment_removers_; + UUID uuid_; + CrashReportDatabase* database_; + }; + + //! \brief A crash report that is in the process of being uploaded. + //! + //! An instance of this class should be created via GetReportForUploading(). + class UploadReport : public Report { + public: + UploadReport(); + + UploadReport(const UploadReport&) = delete; + UploadReport& operator=(const UploadReport&) = delete; + + virtual ~UploadReport(); + + //! \brief An open FileReader with which to read the report. + FileReader* Reader() const { return reader_.get(); } + + //! \brief Obtains a mapping of names to file readers for any attachments + //! for the report. + std::map GetAttachments() const { + return attachment_map_; + } + + private: + friend class CrashReportDatabase; + friend class CrashReportDatabaseGeneric; + friend class CrashReportDatabaseMac; + friend class CrashReportDatabaseWin; + + bool Initialize(const base::FilePath& path, CrashReportDatabase* database); + void InitializeAttachments(); + + std::unique_ptr reader_; + CrashReportDatabase* database_; + std::vector> attachment_readers_; + std::map attachment_map_; + bool report_metrics_; + }; + + //! \brief The result code for operations performed on a database. + enum OperationStatus { + //! \brief No error occurred. + kNoError = 0, + + //! \brief The report that was requested could not be located. + //! + //! This may occur when the report is present in the database but not in a + //! state appropriate for the requested operation, for example, if + //! GetReportForUploading() is called to obtain report that’s already in the + //! completed state. + kReportNotFound, + + //! \brief An error occured while performing a file operation on a crash + //! report. + //! + //! A database is responsible for managing both the metadata about a report + //! and the actual crash report itself. This error is returned when an + //! error occurred when managing the report file. Additional information + //! will be logged. + kFileSystemError, + + //! \brief An error occured while recording metadata for a crash report or + //! database-wide settings. + //! + //! A database is responsible for managing both the metadata about a report + //! and the actual crash report itself. This error is returned when an + //! error occurred when managing the metadata about a crash report or + //! database-wide settings. Additional information will be logged. + kDatabaseError, + + //! \brief The operation could not be completed because a concurrent + //! operation affecting the report is occurring. + kBusyError, + + //! \brief The report cannot be uploaded by user request as it has already + //! been uploaded. + kCannotRequestUpload, + }; + + CrashReportDatabase(const CrashReportDatabase&) = delete; + CrashReportDatabase& operator=(const CrashReportDatabase&) = delete; + + virtual ~CrashReportDatabase() {} + + //! \brief Opens a database of crash reports, possibly creating it. + //! + //! \param[in] path A path to the database to be created or opened. If the + //! database does not yet exist, it will be created if possible. Note that + //! for databases implemented as directory structures, existence refers + //! solely to the outermost directory. + //! + //! \return A database object on success, `nullptr` on failure with an error + //! logged. + //! + //! \sa InitializeWithoutCreating + static std::unique_ptr Initialize( + const base::FilePath& path); + + //! \brief Opens an existing database of crash reports. + //! + //! \param[in] path A path to the database to be opened. If the database does + //! not yet exist, it will not be created. Note that for databases + //! implemented as directory structures, existence refers solely to the + //! outermost directory. On such databases, as long as the outermost + //! directory is present, this method will create the inner structure. + //! + //! \return A database object on success, `nullptr` on failure with an error + //! logged. + //! + //! \sa Initialize + static std::unique_ptr InitializeWithoutCreating( + const base::FilePath& path); + + //! \brief Returns the Settings object for this database. + //! + //! \return A weak pointer to the Settings object, which is owned by the + //! database. + virtual Settings* GetSettings() = 0; + + //! \brief Creates a record of a new crash report. + //! + //! Callers should write the crash report using the FileWriter provided. + //! Callers should then call FinishedWritingCrashReport() to complete report + //! creation. If an error is encountered while writing the crash report, no + //! special action needs to be taken. If FinishedWritingCrashReport() is not + //! called, the report will be removed from the database when \a report is + //! destroyed. + //! + //! \param[out] report A NewReport object containing a FileWriter with which + //! to write the report data. Only valid if this returns #kNoError. + //! + //! \return The operation status code. + virtual OperationStatus PrepareNewCrashReport( + std::unique_ptr* report) = 0; + + //! \brief Informs the database that a crash report has been successfully + //! written. + //! + //! \param[in] report A NewReport obtained with PrepareNewCrashReport(). The + //! NewReport object will be invalidated as part of this call. + //! \param[out] uuid The UUID of this crash report. + //! + //! \return The operation status code. + virtual OperationStatus FinishedWritingCrashReport( + std::unique_ptr report, + UUID* uuid) = 0; + + //! \brief Returns the crash report record for the unique identifier. + //! + //! \param[in] uuid The crash report record unique identifier. + //! \param[out] report A crash report record. Only valid if this returns + //! #kNoError. + //! + //! \return The operation status code. + virtual OperationStatus LookUpCrashReport(const UUID& uuid, + Report* report) = 0; + + //! \brief Returns a list of crash report records that have not been uploaded. + //! + //! \param[out] reports A list of crash report record objects. This must be + //! empty on entry. Only valid if this returns #kNoError. + //! + //! \return The operation status code. + virtual OperationStatus GetPendingReports(std::vector* reports) = 0; + + //! \brief Returns a list of crash report records that have been completed, + //! either by being uploaded or by skipping upload. + //! + //! \param[out] reports A list of crash report record objects. This must be + //! empty on entry. Only valid if this returns #kNoError. + //! + //! \return The operation status code. + virtual OperationStatus GetCompletedReports(std::vector* reports) = 0; + + //! \brief Obtains and locks a report object for uploading to a collection + //! server. + //! + //! Callers should upload the crash report using the FileReader provided. + //! Callers should then call RecordUploadComplete() to record a successful + //! upload. If RecordUploadComplete() is not called, the upload attempt will + //! be recorded as unsuccessful and the report lock released when \a report is + //! destroyed. + //! + //! \param[in] uuid The unique identifier for the crash report record. + //! \param[out] report A crash report record for the report to be uploaded. + //! Only valid if this returns #kNoError. + //! \param[in] report_metrics If `false`, metrics will not be recorded for + //! this upload attempt when RecordUploadComplete() is called or \a report + //! is destroyed. Metadata for the upload attempt will still be recorded + //! in the database. + //! + //! \return The operation status code. + virtual OperationStatus GetReportForUploading( + const UUID& uuid, + std::unique_ptr* report, + bool report_metrics = true) = 0; + + //! \brief Records a successful upload for a report and updates the last + //! upload attempt time as returned by + //! Settings::GetLastUploadAttemptTime(). + //! + //! \param[in] report A UploadReport object obtained from + //! GetReportForUploading(). The UploadReport object will be invalidated + //! and the report unlocked as part of this call. + //! \param[in] id The possibly empty identifier assigned to this crash report + //! by the collection server. + //! + //! \return The operation status code. + OperationStatus RecordUploadComplete( + std::unique_ptr report, + const std::string& id); + + //! \brief Moves a report from the pending state to the completed state, but + //! without the report being uploaded. + //! + //! This can be used if the user has disabled crash report collection, but + //! crash generation is still enabled in the product. + //! + //! \param[in] uuid The unique identifier for the crash report record. + //! \param[in] reason The reason the report upload is being skipped for + //! metrics tracking purposes. + //! + //! \return The operation status code. + virtual OperationStatus SkipReportUpload( + const UUID& uuid, + Metrics::CrashSkippedReason reason) = 0; + + //! \brief Deletes a crash report file and its associated metadata. + //! + //! \param[in] uuid The UUID of the report to delete. + //! + //! \return The operation status code. + virtual OperationStatus DeleteReport(const UUID& uuid) = 0; + + //! \brief Marks a crash report as explicitly requested to be uploaded by the + //! user and moves it to 'pending' state. + //! + //! \param[in] uuid The unique identifier for the crash report record. + //! + //! \return The operation status code. + virtual OperationStatus RequestUpload(const UUID& uuid) = 0; + + //! \brief Cleans the database of expired lockfiles, metadata without report + //! files, report files without metadata, and attachments without report + //! files. + //! + //! As the macOS implementation does not use lock or metadata files, the + //! cleaning is limited to attachments without report files. + //! + //! \param[in] lockfile_ttl The number of seconds at which lockfiles or new + //! report files are considered expired. + //! \return The number of reports cleaned. + virtual int CleanDatabase(time_t lockfile_ttl) { return 0; } + + protected: + CrashReportDatabase() {} + + //! \brief The path to the database passed to Initialize. + //! + //! \return The filepath of the database; + virtual base::FilePath DatabasePath() = 0; + + //! \brief Build a filepath for the root attachments directory. + //! + //! \return The filepath to the attachments directory. + base::FilePath AttachmentsRootPath(); + + //! \brief Build a filepath for the directory for the report to hold + //! attachments. + //! + //! \param[in] uuid The unique identifier for the crash report record. + //! + //! \return The filepath to the report attachments directory. + base::FilePath AttachmentsPath(const UUID& uuid); + + //! \brief Attempts to remove any attachments associated with the given + //! report UUID. There may not be any, so failing is not an error. + //! + //! \param[in] uuid The unique identifier for the crash report record. + void RemoveAttachmentsByUUID(const UUID& uuid); + + private: + //! \brief Adjusts a crash report record’s metadata to account for an upload + //! attempt, and updates the last upload attempt time as returned by + //! Settings::GetLastUploadAttemptTime(). + //! + //! \param[in] report The report object obtained from + //! GetReportForUploading(). + //! \param[in] successful Whether the upload attempt was successful. + //! \param[in] id The identifier assigned to this crash report by the + //! collection server. Must be empty if \a successful is `false`; may be + //! empty if it is `true`. + //! + //! \return The operation status code. + virtual OperationStatus RecordUploadAttempt(UploadReport* report, + bool successful, + const std::string& id) = 0; +}; + +} // namespace crashpad + +#endif // CRASHPAD_CLIENT_CRASH_REPORT_DATABASE_H_ diff --git a/shared/sentry/external/crashpad/client/crash_report_database_generic.cc b/shared/sentry/external/crashpad/client/crash_report_database_generic.cc new file mode 100644 index 000000000..9d48b68be --- /dev/null +++ b/shared/sentry/external/crashpad/client/crash_report_database_generic.cc @@ -0,0 +1,931 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "client/crash_report_database.h" + +#include +#include +#include + +#include +#include + +#include "base/logging.h" +#include "build/build_config.h" +#include "client/settings.h" +#include "util/file/directory_reader.h" +#include "util/file/filesystem.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/misc/memory_sanitizer.h" + +namespace crashpad { + +namespace { + +base::FilePath ReplaceFinalExtension( + const base::FilePath& path, + const base::FilePath::StringType extension) { + return base::FilePath(path.RemoveFinalExtension().value() + extension); +} + +UUID UUIDFromReportPath(const base::FilePath& path) { + UUID uuid; + uuid.InitializeFromString(path.RemoveFinalExtension().BaseName().value()); + return uuid; +} + +using OperationStatus = CrashReportDatabase::OperationStatus; + +constexpr base::FilePath::CharType kSettings[] = + FILE_PATH_LITERAL("settings.dat"); + +constexpr base::FilePath::CharType kCrashReportExtension[] = + FILE_PATH_LITERAL(".dmp"); +constexpr base::FilePath::CharType kMetadataExtension[] = + FILE_PATH_LITERAL(".meta"); +constexpr base::FilePath::CharType kLockExtension[] = + FILE_PATH_LITERAL(".lock"); + +constexpr base::FilePath::CharType kNewDirectory[] = FILE_PATH_LITERAL("new"); +constexpr base::FilePath::CharType kPendingDirectory[] = + FILE_PATH_LITERAL("pending"); +constexpr base::FilePath::CharType kCompletedDirectory[] = + FILE_PATH_LITERAL("completed"); + +constexpr const base::FilePath::CharType* kReportDirectories[] = { + kNewDirectory, + kPendingDirectory, + kCompletedDirectory, +}; + +enum { + //! \brief Corresponds to uploaded bit of the report state. + kAttributeUploaded = 1 << 0, + + //! \brief Corresponds to upload_explicity_requested bit of the report state. + kAttributeUploadExplicitlyRequested = 1 << 1, +}; + +struct ReportMetadata { + static constexpr int32_t kVersion = 1; + + int32_t version = kVersion; + int32_t upload_attempts = 0; + int64_t last_upload_attempt_time = 0; + time_t creation_time = 0; + uint8_t attributes = 0; +}; + +// A lock held while using database resources. +class ScopedLockFile { + public: + ScopedLockFile() = default; + + ScopedLockFile(const ScopedLockFile&) = delete; + ScopedLockFile& operator=(const ScopedLockFile&) = delete; + + ~ScopedLockFile() = default; + + ScopedLockFile& operator=(ScopedLockFile&& other) { + lock_file_.reset(other.lock_file_.release()); + return *this; + } + + // Attempt to acquire a lock for the report at report_path. + // Return `true` on success, otherwise `false`. + bool ResetAcquire(const base::FilePath& report_path) { + lock_file_.reset(); + + base::FilePath lock_path(report_path.RemoveFinalExtension().value() + + kLockExtension); + ScopedFileHandle lock_fd(LoggingOpenFileForWrite( + lock_path, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly)); + if (!lock_fd.is_valid()) { + return false; + } + lock_file_.reset(lock_path); + + time_t timestamp = time(nullptr); + if (!LoggingWriteFile(lock_fd.get(), ×tamp, sizeof(timestamp))) { + return false; + } + + return true; + } + + // Returns `true` if the lock is held. + bool is_valid() const { return lock_file_.is_valid(); } + + // Returns `true` if the lockfile at lock_path has expired. + static bool IsExpired(const base::FilePath& lock_path, time_t lockfile_ttl) { + time_t now = time(nullptr); + + timespec filetime; + if (FileModificationTime(lock_path, &filetime) && + filetime.tv_sec > now + lockfile_ttl) { + return false; + } + + ScopedFileHandle lock_fd(LoggingOpenFileForReadAndWrite( + lock_path, FileWriteMode::kReuseOrFail, FilePermissions::kOwnerOnly)); + if (!lock_fd.is_valid()) { + return false; + } + + time_t timestamp; + if (!LoggingReadFileExactly(lock_fd.get(), ×tamp, sizeof(timestamp))) { + return false; + } + + return now >= timestamp + lockfile_ttl; + } + + private: + ScopedRemoveFile lock_file_; +}; +} // namespace + +class CrashReportDatabaseGeneric : public CrashReportDatabase { + public: + CrashReportDatabaseGeneric(); + + CrashReportDatabaseGeneric(const CrashReportDatabaseGeneric&) = delete; + CrashReportDatabaseGeneric& operator=(const CrashReportDatabaseGeneric&) = + delete; + + ~CrashReportDatabaseGeneric() override; + + bool Initialize(const base::FilePath& path, bool may_create); + + // CrashReportDatabase: + Settings* GetSettings() override; + OperationStatus PrepareNewCrashReport( + std::unique_ptr* report) override; + OperationStatus FinishedWritingCrashReport(std::unique_ptr report, + UUID* uuid) override; + OperationStatus LookUpCrashReport(const UUID& uuid, Report* report) override; + OperationStatus GetPendingReports(std::vector* reports) override; + OperationStatus GetCompletedReports(std::vector* reports) override; + OperationStatus GetReportForUploading( + const UUID& uuid, + std::unique_ptr* report, + bool report_metrics) override; + OperationStatus SkipReportUpload(const UUID& uuid, + Metrics::CrashSkippedReason reason) override; + OperationStatus DeleteReport(const UUID& uuid) override; + OperationStatus RequestUpload(const UUID& uuid) override; + int CleanDatabase(time_t lockfile_ttl) override; + base::FilePath DatabasePath() override; + + private: + struct LockfileUploadReport : public UploadReport { + ScopedLockFile lock_file; + }; + + enum ReportState : int32_t { + kUninitialized = -1, + + // Being created by a caller of PrepareNewCrashReport(). + kNew, + + // Created by FinishedWritingCrashReport(), but not yet uploaded. + kPending, + + // Upload completed or skipped. + kCompleted, + + // Specifies either kPending or kCompleted. + kSearchable, + }; + + // CrashReportDatabase: + OperationStatus RecordUploadAttempt(UploadReport* report, + bool successful, + const std::string& id) override; + + // Builds a filepath for the report with the specified uuid and state. + base::FilePath ReportPath(const UUID& uuid, ReportState state); + + // Locates the report with id uuid and returns its file path in path and a + // lock for the report in lock_file. This method succeeds as long as the + // report file exists and the lock can be acquired. No validation is done on + // the existence or content of the metadata file. + OperationStatus LocateAndLockReport(const UUID& uuid, + ReportState state, + base::FilePath* path, + ScopedLockFile* lock_file); + + // Locates, locks, and reads the metadata for the report with the specified + // uuid and state. This method will fail and may remove reports if invalid + // metadata is detected. state may be kPending, kCompleted, or kSearchable. + OperationStatus CheckoutReport(const UUID& uuid, + ReportState state, + base::FilePath* path, + ScopedLockFile* lock_file, + Report* report); + + // Reads metadata for all reports in state and returns it in reports. + OperationStatus ReportsInState(ReportState state, + std::vector* reports); + + // Cleans lone metadata, reports, or expired locks in a particular state. + int CleanReportsInState(ReportState state, time_t lockfile_ttl); + + // Cleans any attachments that have no associated report in any state. + void CleanOrphanedAttachments(); + + // Reads the metadata for a report from path and returns it in report. + bool ReadMetadata(const base::FilePath& path, Report* report); + + // Wraps ReadMetadata and removes the report from the database on failure. + bool CleaningReadMetadata(const base::FilePath& path, Report* report); + + // Writes metadata for a new report to the filesystem at path. + static bool WriteNewMetadata(const base::FilePath& path); + + // Writes the metadata for report to the filesystem at path. + static bool WriteMetadata(const base::FilePath& path, const Report& report); + + base::FilePath base_dir_; + Settings settings_; + InitializationStateDcheck initialized_; +}; + +CrashReportDatabaseGeneric::CrashReportDatabaseGeneric() = default; + +CrashReportDatabaseGeneric::~CrashReportDatabaseGeneric() = default; + +bool CrashReportDatabaseGeneric::Initialize(const base::FilePath& path, + bool may_create) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + base_dir_ = path; + + if (!IsDirectory(base_dir_, true) && + !(may_create && + LoggingCreateDirectory(base_dir_, FilePermissions::kOwnerOnly, true))) { + return false; + } + + for (const base::FilePath::CharType* subdir : kReportDirectories) { + if (!LoggingCreateDirectory( + base_dir_.Append(subdir), FilePermissions::kOwnerOnly, true)) { + return false; + } + } + + if (!LoggingCreateDirectory( + AttachmentsRootPath(), FilePermissions::kOwnerOnly, true)) { + return false; + } + + if (!settings_.Initialize(base_dir_.Append(kSettings))) { + return false; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +// static +std::unique_ptr CrashReportDatabase::Initialize( + const base::FilePath& path) { + auto database = std::make_unique(); + return database->Initialize(path, true) ? std::move(database) : nullptr; +} + +// static +std::unique_ptr +CrashReportDatabase::InitializeWithoutCreating(const base::FilePath& path) { + auto database = std::make_unique(); + return database->Initialize(path, false) ? std::move(database) : nullptr; +} + +base::FilePath CrashReportDatabaseGeneric::DatabasePath() { + return base_dir_; +} + +Settings* CrashReportDatabaseGeneric::GetSettings() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &settings_; +} + +OperationStatus CrashReportDatabaseGeneric::PrepareNewCrashReport( + std::unique_ptr* report) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + auto new_report = std::make_unique(); + if (!new_report->Initialize( + this, base_dir_.Append(kNewDirectory), kCrashReportExtension)) { + return kFileSystemError; + } + + report->reset(new_report.release()); + return kNoError; +} + +OperationStatus CrashReportDatabaseGeneric::FinishedWritingCrashReport( + std::unique_ptr report, + UUID* uuid) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + base::FilePath path = ReportPath(report->ReportID(), kPending); + ScopedLockFile lock_file; + if (!lock_file.ResetAcquire(path)) { + return kBusyError; + } + + if (!WriteNewMetadata(ReplaceFinalExtension(path, kMetadataExtension))) { + return kDatabaseError; + } + + FileOffset size = report->Writer()->Seek(0, SEEK_END); + + report->Writer()->Close(); + if (!MoveFileOrDirectory(report->file_remover_.get(), path)) { + return kFileSystemError; + } + // We've moved the report to pending, so it no longer needs to be removed. + std::ignore = report->file_remover_.release(); + + // Close all the attachments and disarm their removers too. + for (auto& writer : report->attachment_writers_) { + writer->Close(); + } + for (auto& remover : report->attachment_removers_) { + std::ignore = remover.release(); + } + + *uuid = report->ReportID(); + + Metrics::CrashReportPending(Metrics::PendingReportReason::kNewlyCreated); + Metrics::CrashReportSize(size); + + return kNoError; +} + +OperationStatus CrashReportDatabaseGeneric::LookUpCrashReport(const UUID& uuid, + Report* report) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + ScopedLockFile lock_file; + base::FilePath path; + return CheckoutReport(uuid, kSearchable, &path, &lock_file, report); +} + +OperationStatus CrashReportDatabaseGeneric::GetPendingReports( + std::vector* reports) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return ReportsInState(kPending, reports); +} + +OperationStatus CrashReportDatabaseGeneric::GetCompletedReports( + std::vector* reports) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return ReportsInState(kCompleted, reports); +} + +OperationStatus CrashReportDatabaseGeneric::GetReportForUploading( + const UUID& uuid, + std::unique_ptr* report, + bool report_metrics) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + auto upload_report = std::make_unique(); + + base::FilePath path; + OperationStatus os = CheckoutReport( + uuid, kPending, &path, &upload_report->lock_file, upload_report.get()); + if (os != kNoError) { + return os; + } + + if (!upload_report->Initialize(path, this)) { + return kFileSystemError; + } + upload_report->report_metrics_ = report_metrics; + + report->reset(upload_report.release()); + return kNoError; +} + +OperationStatus CrashReportDatabaseGeneric::SkipReportUpload( + const UUID& uuid, + Metrics::CrashSkippedReason reason) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + Metrics::CrashUploadSkipped(reason); + + base::FilePath path; + ScopedLockFile lock_file; + Report report; + OperationStatus os = + CheckoutReport(uuid, kPending, &path, &lock_file, &report); + if (os != kNoError) { + return os; + } + + base::FilePath completed_path(ReportPath(uuid, kCompleted)); + ScopedLockFile completed_lock_file; + if (!completed_lock_file.ResetAcquire(completed_path)) { + return kBusyError; + } + + report.upload_explicitly_requested = false; + if (!WriteMetadata(completed_path, report)) { + return kDatabaseError; + } + + if (!MoveFileOrDirectory(path, completed_path)) { + return kFileSystemError; + } + + if (!LoggingRemoveFile(ReplaceFinalExtension(path, kMetadataExtension))) { + return kDatabaseError; + } + + return kNoError; +} + +OperationStatus CrashReportDatabaseGeneric::DeleteReport(const UUID& uuid) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + base::FilePath path; + ScopedLockFile lock_file; + OperationStatus os = + LocateAndLockReport(uuid, kSearchable, &path, &lock_file); + if (os != kNoError) { + return os; + } + + if (!LoggingRemoveFile(path)) { + return kFileSystemError; + } + + if (!LoggingRemoveFile(ReplaceFinalExtension(path, kMetadataExtension))) { + return kDatabaseError; + } + + RemoveAttachmentsByUUID(uuid); + + return kNoError; +} + +OperationStatus CrashReportDatabaseGeneric::RequestUpload(const UUID& uuid) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + base::FilePath path; + ScopedLockFile lock_file; + Report report; + OperationStatus os = + CheckoutReport(uuid, kSearchable, &path, &lock_file, &report); + if (os != kNoError) { + return os; + } + + if (report.uploaded) { + return kCannotRequestUpload; + } + + report.upload_explicitly_requested = true; + base::FilePath pending_path = ReportPath(uuid, kPending); + if (!MoveFileOrDirectory(path, pending_path)) { + return kFileSystemError; + } + + if (!WriteMetadata(pending_path, report)) { + return kDatabaseError; + } + + if (pending_path != path) { + if (!LoggingRemoveFile(ReplaceFinalExtension(path, kMetadataExtension))) { + return kDatabaseError; + } + } + + Metrics::CrashReportPending(Metrics::PendingReportReason::kUserInitiated); + return kNoError; +} + +int CrashReportDatabaseGeneric::CleanDatabase(time_t lockfile_ttl) { + int removed = 0; + time_t now = time(nullptr); + + DirectoryReader reader; + const base::FilePath new_dir(base_dir_.Append(kNewDirectory)); + if (reader.Open(new_dir)) { + base::FilePath filename; + DirectoryReader::Result result; + while ((result = reader.NextFile(&filename)) == + DirectoryReader::Result::kSuccess) { + const base::FilePath filepath(new_dir.Append(filename)); + timespec filetime; + if (!FileModificationTime(filepath, &filetime)) { + continue; + } + if (filetime.tv_sec <= now - lockfile_ttl) { + if (LoggingRemoveFile(filepath)) { + ++removed; + } + } + } + } + + removed += CleanReportsInState(kPending, lockfile_ttl); + removed += CleanReportsInState(kCompleted, lockfile_ttl); + CleanOrphanedAttachments(); + return removed; +} + +OperationStatus CrashReportDatabaseGeneric::RecordUploadAttempt( + UploadReport* report, + bool successful, + const std::string& id) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (report->report_metrics_) { + Metrics::CrashUploadAttempted(successful); + } + time_t now = time(nullptr); + + report->id = id; + report->uploaded = successful; + report->last_upload_attempt_time = now; + ++report->upload_attempts; + + base::FilePath report_path(report->file_path); + + ScopedLockFile lock_file; + if (successful) { + report->upload_explicitly_requested = false; + + base::FilePath completed_report_path = ReportPath(report->uuid, kCompleted); + + if (!lock_file.ResetAcquire(completed_report_path)) { + return kBusyError; + } + + report->Reader()->Close(); + if (!MoveFileOrDirectory(report_path, completed_report_path)) { + return kFileSystemError; + } + + LoggingRemoveFile(ReplaceFinalExtension(report_path, kMetadataExtension)); + report_path = completed_report_path; + } + + if (!WriteMetadata(report_path, *report)) { + return kDatabaseError; + } + + if (!settings_.SetLastUploadAttemptTime(now)) { + return kDatabaseError; + } + + return kNoError; +} + +base::FilePath CrashReportDatabaseGeneric::ReportPath(const UUID& uuid, + ReportState state) { + DCHECK_NE(state, kUninitialized); + DCHECK_NE(state, kSearchable); + +#if BUILDFLAG(IS_WIN) + const std::wstring uuid_string = uuid.ToWString(); +#else + const std::string uuid_string = uuid.ToString(); +#endif + + return base_dir_.Append(kReportDirectories[state]) + .Append(uuid_string + kCrashReportExtension); +} + +OperationStatus CrashReportDatabaseGeneric::LocateAndLockReport( + const UUID& uuid, + ReportState desired_state, + base::FilePath* path, + ScopedLockFile* lock_file) { + std::vector searchable_states; + if (desired_state == kSearchable) { + searchable_states.push_back(kPending); + searchable_states.push_back(kCompleted); + } else { + DCHECK(desired_state == kPending || desired_state == kCompleted); + searchable_states.push_back(desired_state); + } + + for (const ReportState state : searchable_states) { + base::FilePath local_path(ReportPath(uuid, state)); + ScopedLockFile local_lock; + if (!local_lock.ResetAcquire(local_path)) { + return kBusyError; + } + + if (!IsRegularFile(local_path)) { + continue; + } + + *path = local_path; + *lock_file = std::move(local_lock); + return kNoError; + } + + return kReportNotFound; +} + +OperationStatus CrashReportDatabaseGeneric::CheckoutReport( + const UUID& uuid, + ReportState state, + base::FilePath* path, + ScopedLockFile* lock_file, + Report* report) { + ScopedLockFile local_lock; + base::FilePath local_path; + OperationStatus os = + LocateAndLockReport(uuid, state, &local_path, &local_lock); + if (os != kNoError) { + return os; + } + + if (!CleaningReadMetadata(local_path, report)) { + return kDatabaseError; + } + + *path = local_path; + *lock_file = std::move(local_lock); + return kNoError; +} + +OperationStatus CrashReportDatabaseGeneric::ReportsInState( + ReportState state, + std::vector* reports) { + DCHECK(reports->empty()); + DCHECK_NE(state, kUninitialized); + DCHECK_NE(state, kSearchable); + DCHECK_NE(state, kNew); + + const base::FilePath dir_path(base_dir_.Append(kReportDirectories[state])); + DirectoryReader reader; + if (!reader.Open(dir_path)) { + return kDatabaseError; + } + + base::FilePath filename; + DirectoryReader::Result result; + while ((result = reader.NextFile(&filename)) == + DirectoryReader::Result::kSuccess) { + const base::FilePath::StringType extension(filename.FinalExtension()); + if (extension.compare(kCrashReportExtension) != 0) { + continue; + } + + const base::FilePath filepath(dir_path.Append(filename)); + ScopedLockFile lock_file; + if (!lock_file.ResetAcquire(filepath)) { + continue; + } + + Report report; + if (!CleaningReadMetadata(filepath, &report)) { + continue; + } + reports->push_back(report); + reports->back().file_path = filepath; + } + return kNoError; +} + +int CrashReportDatabaseGeneric::CleanReportsInState(ReportState state, + time_t lockfile_ttl) { + const base::FilePath dir_path(base_dir_.Append(kReportDirectories[state])); + DirectoryReader reader; + if (!reader.Open(dir_path)) { + return 0; + } + + int removed = 0; + base::FilePath filename; + DirectoryReader::Result result; + while ((result = reader.NextFile(&filename)) == + DirectoryReader::Result::kSuccess) { + const base::FilePath::StringType extension(filename.FinalExtension()); + const base::FilePath filepath(dir_path.Append(filename)); + + // Remove any report files without metadata. + if (extension.compare(kCrashReportExtension) == 0) { + const base::FilePath metadata_path( + ReplaceFinalExtension(filepath, kMetadataExtension)); + ScopedLockFile report_lock; + if (report_lock.ResetAcquire(filepath) && !IsRegularFile(metadata_path) && + LoggingRemoveFile(filepath)) { + ++removed; + RemoveAttachmentsByUUID(UUIDFromReportPath(filepath)); + } + continue; + } + + // Remove any metadata files without report files. + if (extension.compare(kMetadataExtension) == 0) { + const base::FilePath report_path( + ReplaceFinalExtension(filepath, kCrashReportExtension)); + ScopedLockFile report_lock; + if (report_lock.ResetAcquire(report_path) && + !IsRegularFile(report_path) && LoggingRemoveFile(filepath)) { + ++removed; + RemoveAttachmentsByUUID(UUIDFromReportPath(filepath)); + } + continue; + } + + // Remove any expired locks only if we can remove the report and metadata. + if (extension.compare(kLockExtension) == 0 && + ScopedLockFile::IsExpired(filepath, lockfile_ttl)) { + const base::FilePath no_ext(filepath.RemoveFinalExtension()); + const base::FilePath report_path(no_ext.value() + kCrashReportExtension); + const base::FilePath metadata_path(no_ext.value() + kMetadataExtension); + if ((IsRegularFile(report_path) && !LoggingRemoveFile(report_path)) || + (IsRegularFile(metadata_path) && !LoggingRemoveFile(metadata_path))) { + continue; + } + + if (LoggingRemoveFile(filepath)) { + ++removed; + RemoveAttachmentsByUUID(UUIDFromReportPath(filepath)); + } + continue; + } + } + + return removed; +} + +void CrashReportDatabaseGeneric::CleanOrphanedAttachments() { + base::FilePath root_attachments_dir(AttachmentsRootPath()); + DirectoryReader reader; + if (!reader.Open(root_attachments_dir)) { + return; + } + + base::FilePath filename; + DirectoryReader::Result result; + while ((result = reader.NextFile(&filename)) == + DirectoryReader::Result::kSuccess) { + const base::FilePath report_attachment_dir( + root_attachments_dir.Append(filename)); + if (IsDirectory(report_attachment_dir, false)) { + UUID uuid; + if (!uuid.InitializeFromString(filename.value())) { + LOG(ERROR) << "unexpected attachment dir name " << filename.value(); + continue; + } + + // Check to see if the report is being created in "new". + base::FilePath new_dir_path = + base_dir_.Append(kNewDirectory) + .Append(uuid.ToString() + kCrashReportExtension); + if (IsRegularFile(new_dir_path)) { + continue; + } + + // Check to see if the report is in "pending" or "completed". + ScopedLockFile local_lock; + base::FilePath local_path; + OperationStatus os = + LocateAndLockReport(uuid, kSearchable, &local_path, &local_lock); + if (os != kReportNotFound) { + continue; + } + + // Couldn't find a report, assume these attachments are orphaned. + RemoveAttachmentsByUUID(uuid); + } + } +} + +bool CrashReportDatabaseGeneric::ReadMetadata(const base::FilePath& path, + Report* report) { + const base::FilePath metadata_path( + ReplaceFinalExtension(path, kMetadataExtension)); + + ScopedFileHandle handle(LoggingOpenFileForRead(metadata_path)); + if (!handle.is_valid()) { + return false; + } + + UUID uuid; + if (!uuid.InitializeFromString( + path.BaseName().RemoveFinalExtension().value())) { + LOG(ERROR) << "Couldn't interpret report uuid"; + return false; + } + + ReportMetadata metadata; + if (!LoggingReadFileExactly(handle.get(), &metadata, sizeof(metadata))) { + return false; + } + + if (metadata.version != ReportMetadata::kVersion) { + LOG(ERROR) << "metadata version mismatch"; + return false; + } + + if (!LoggingReadToEOF(handle.get(), &report->id)) { + return false; + } + + // Seed the total size with the main report size and then add the sizes of any + // potential attachments. + uint64_t total_size = GetFileSize(path); + total_size += GetDirectorySize(AttachmentsPath(uuid)); + + report->uuid = uuid; + report->upload_attempts = metadata.upload_attempts; + report->last_upload_attempt_time = metadata.last_upload_attempt_time; + report->creation_time = metadata.creation_time; + report->uploaded = (metadata.attributes & kAttributeUploaded) != 0; + report->upload_explicitly_requested = + (metadata.attributes & kAttributeUploadExplicitlyRequested) != 0; + report->file_path = path; + report->total_size = total_size; + return true; +} + +bool CrashReportDatabaseGeneric::CleaningReadMetadata( + const base::FilePath& path, + Report* report) { + if (ReadMetadata(path, report)) { + return true; + } + + LoggingRemoveFile(path); + LoggingRemoveFile(ReplaceFinalExtension(path, kMetadataExtension)); + RemoveAttachmentsByUUID(report->uuid); + return false; +} + +// static +bool CrashReportDatabaseGeneric::WriteNewMetadata(const base::FilePath& path) { + const base::FilePath metadata_path( + ReplaceFinalExtension(path, kMetadataExtension)); + + ScopedFileHandle handle(LoggingOpenFileForWrite(metadata_path, + FileWriteMode::kCreateOrFail, + FilePermissions::kOwnerOnly)); + if (!handle.is_valid()) { + return false; + } + + ReportMetadata metadata; +#if defined(MEMORY_SANITIZER) + // memset() + re-initialization is required to zero padding bytes for MSan. + memset(&metadata, 0, sizeof(metadata)); +#endif // defined(MEMORY_SANITIZER) + metadata = {}; + metadata.creation_time = time(nullptr); + + return LoggingWriteFile(handle.get(), &metadata, sizeof(metadata)); +} + +// static +bool CrashReportDatabaseGeneric::WriteMetadata(const base::FilePath& path, + const Report& report) { + const base::FilePath metadata_path( + ReplaceFinalExtension(path, kMetadataExtension)); + + ScopedFileHandle handle( + LoggingOpenFileForWrite(metadata_path, + FileWriteMode::kTruncateOrCreate, + FilePermissions::kOwnerOnly)); + if (!handle.is_valid()) { + return false; + } + + ReportMetadata metadata; +#if defined(MEMORY_SANITIZER) + // memset() + re-initialization is required to zero padding bytes for MSan. + memset(&metadata, 0, sizeof(metadata)); +#endif // defined(MEMORY_SANITIZER) + metadata = {}; + metadata.creation_time = report.creation_time; + metadata.last_upload_attempt_time = report.last_upload_attempt_time; + metadata.upload_attempts = report.upload_attempts; + metadata.attributes = + (report.uploaded ? kAttributeUploaded : 0) | + (report.upload_explicitly_requested ? kAttributeUploadExplicitlyRequested + : 0); + + return LoggingWriteFile(handle.get(), &metadata, sizeof(metadata)) && + LoggingWriteFile(handle.get(), report.id.c_str(), report.id.size()); +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/crash_report_database_mac.mm b/shared/sentry/external/crashpad/client/crash_report_database_mac.mm new file mode 100644 index 000000000..e72838a03 --- /dev/null +++ b/shared/sentry/external/crashpad/client/crash_report_database_mac.mm @@ -0,0 +1,876 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "client/crash_report_database.h" + +#import +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "base/logging.h" +#include "base/mac/scoped_nsautorelease_pool.h" +#include "base/posix/eintr_wrapper.h" +#include "base/scoped_generic.h" +#include "base/strings/string_piece.h" +#include "base/strings/stringprintf.h" +#include "base/strings/sys_string_conversions.h" +#include "client/settings.h" +#include "util/file/directory_reader.h" +#include "util/file/file_io.h" +#include "util/file/filesystem.h" +#include "util/mac/xattr.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/misc/metrics.h" + +#if BUILDFLAG(IS_IOS) +#include "util/ios/scoped_background_task.h" +#endif // BUILDFLAG(IS_IOS) + +namespace crashpad { + +namespace { + +constexpr char kWriteDirectory[] = "new"; +constexpr char kUploadPendingDirectory[] = "pending"; +constexpr char kCompletedDirectory[] = "completed"; + +constexpr char kSettings[] = "settings.dat"; + +constexpr const char* kReportDirectories[] = { + kWriteDirectory, + kUploadPendingDirectory, + kCompletedDirectory, +}; + +constexpr char kCrashReportFileExtension[] = "dmp"; + +constexpr char kXattrUUID[] = "uuid"; +constexpr char kXattrCollectorID[] = "id"; +constexpr char kXattrCreationTime[] = "creation_time"; +constexpr char kXattrIsUploaded[] = "uploaded"; +constexpr char kXattrLastUploadTime[] = "last_upload_time"; +constexpr char kXattrUploadAttemptCount[] = "upload_count"; +constexpr char kXattrIsUploadExplicitlyRequested[] = + "upload_explicitly_requested"; + +constexpr char kXattrDatabaseInitialized[] = "initialized"; + +// Ensures that the node at |path| is a directory. If the |path| refers to a +// file, rather than a directory, returns false. Otherwise, returns true, +// indicating that |path| already was a directory. +bool EnsureDirectoryExists(const base::FilePath& path) { + struct stat st; + if (stat(path.value().c_str(), &st) != 0) { + PLOG(ERROR) << "stat " << path.value(); + return false; + } + if (!S_ISDIR(st.st_mode)) { + LOG(ERROR) << "stat " << path.value() << ": not a directory"; + return false; + } + return true; +} + +// Ensures that the node at |path| is a directory, and creates it if it does +// not exist. If the |path| refers to a file, rather than a directory, or the +// directory could not be created, returns false. Otherwise, returns true, +// indicating that |path| already was or now is a directory. +bool CreateOrEnsureDirectoryExists(const base::FilePath& path) { + if (mkdir(path.value().c_str(), 0755) == 0) { + return true; + } + if (errno != EEXIST) { + PLOG(ERROR) << "mkdir " << path.value(); + return false; + } + return EnsureDirectoryExists(path); +} + +// Creates a long database xattr name from the short constant name. These names +// have changed, and new_name determines whether the returned xattr name will be +// the old name or its new equivalent. +std::string XattrNameInternal(const base::StringPiece& name, bool new_name) { + return base::StringPrintf(new_name ? "org.chromium.crashpad.database.%s" + : "com.googlecode.crashpad.%s", + name.data()); +} + +} // namespace + +//! \brief A CrashReportDatabase that uses HFS+ extended attributes to store +//! report metadata. +//! +//! The database maintains three directories of reports: `"new"` to hold crash +//! reports that are in the process of being written, `"completed"` to hold +//! reports that have been written and are awaiting upload, and `"uploaded"` to +//! hold reports successfully uploaded to a collection server. If the user has +//! opted out of report collection, reports will still be written and moved +//! to the completed directory, but they just will not be uploaded. +//! +//! The database stores its metadata in extended filesystem attributes. To +//! ensure safe access, the report file is locked using `O_EXLOCK` during all +//! extended attribute operations. The lock should be obtained using +//! ObtainReportLock(). +class CrashReportDatabaseMac : public CrashReportDatabase { + public: + explicit CrashReportDatabaseMac(const base::FilePath& path); + + CrashReportDatabaseMac(const CrashReportDatabaseMac&) = delete; + CrashReportDatabaseMac& operator=(const CrashReportDatabaseMac&) = delete; + + virtual ~CrashReportDatabaseMac(); + + bool Initialize(bool may_create); + + // CrashReportDatabase: + Settings* GetSettings() override; + OperationStatus PrepareNewCrashReport( + std::unique_ptr* report) override; + OperationStatus FinishedWritingCrashReport(std::unique_ptr report, + UUID* uuid) override; + OperationStatus LookUpCrashReport(const UUID& uuid, Report* report) override; + OperationStatus GetPendingReports(std::vector* reports) override; + OperationStatus GetCompletedReports(std::vector* reports) override; + OperationStatus GetReportForUploading( + const UUID& uuid, + std::unique_ptr* report, + bool report_metrics) override; + OperationStatus SkipReportUpload(const UUID& uuid, + Metrics::CrashSkippedReason reason) override; + OperationStatus DeleteReport(const UUID& uuid) override; + OperationStatus RequestUpload(const UUID& uuid) override; + int CleanDatabase(time_t lockfile_ttl) override; + base::FilePath DatabasePath() override; + + private: + // CrashReportDatabase: + OperationStatus RecordUploadAttempt(UploadReport* report, + bool successful, + const std::string& id) override; + + //! \brief Report states for use with LocateCrashReport(). + //! + //! ReportState may be considered to be a bitfield. + enum ReportState : uint8_t { + kReportStateWrite = 1 << 0, // in kWriteDirectory + kReportStatePending = 1 << 1, // in kUploadPendingDirectory + kReportStateCompleted = 1 << 2, // in kCompletedDirectory + kReportStateAny = + kReportStateWrite | kReportStatePending | kReportStateCompleted, + }; + + //! \brief A private extension of the Report class that maintains bookkeeping + //! information of the database. + struct UploadReportMac : public UploadReport { + //! \brief Stores the flock of the file for the duration of + //! GetReportForUploading() and RecordUploadAttempt(). + base::ScopedFD lock_fd; +#if BUILDFLAG(IS_IOS) + //! \brief Obtain a background task assertion while a flock is in use. + internal::ScopedBackgroundTask ios_background_task{"UploadReportMac"}; +#endif // BUILDFLAG(IS_IOS) + }; + + //! \brief Locates a crash report in the database by UUID. + //! + //! \param[in] uuid The UUID of the crash report to locate. + //! \param[in] desired_state The state of the report to locate, composed of + //! ReportState values. + //! + //! \return The full path to the report file, or an empty path if it cannot be + //! found. + base::FilePath LocateCrashReport(const UUID& uuid, uint8_t desired_state); + + //! \brief Obtains an exclusive advisory lock on a file. + //! + //! The flock is used to prevent cross-process concurrent metadata reads or + //! writes. While xattrs do not observe the lock, if the lock-then-mutate + //! protocol is observed by all clients of the database, it still enforces + //! synchronization. + //! + //! This does not block, and so callers must ensure that the lock is valid + //! after calling. + //! + //! \param[in] path The path of the file to lock. + //! + //! \return A scoped lock object. If the result is not valid, an error is + //! logged. + static base::ScopedFD ObtainReportLock(const base::FilePath& path); + + //! \brief Reads all the database xattrs from a file into a Report. The file + //! must be locked with ObtainReportLock. + //! + //! \param[in] path The path of the report. + //! \param[out] report The object into which data will be read. + //! + //! \return `true` if all the metadata was read successfully, `false` + //! otherwise. + bool ReadReportMetadataLocked(const base::FilePath& path, Report* report); + + //! \brief Reads the metadata from all the reports in a database subdirectory. + //! Invalid reports are skipped. + //! + //! \param[in] path The database subdirectory path. + //! \param[out] reports An empty vector of reports, which will be filled. + //! + //! \return The operation status code. + OperationStatus ReportsInDirectory(const base::FilePath& path, + std::vector* reports); + + //! \brief Creates a database xattr name from the short constant name. + //! + //! \param[in] name The short name of the extended attribute. + //! + //! \return The long name of the extended attribute. + std::string XattrName(const base::StringPiece& name); + + //! \brief Marks a report with a given path as completed. + //! + //! Assumes that the report is locked. + //! + //! \param[in] report_path The path of the file to mark completed. + //! \param[out] out_path The path of the new file. This parameter is optional. + //! + //! \return The operation status code. + CrashReportDatabase::OperationStatus MarkReportCompletedLocked( + const base::FilePath& report_path, + base::FilePath* out_path); + + // Cleans any attachments that have no associated report in any state. + void CleanOrphanedAttachments(); + + base::FilePath base_dir_; + Settings settings_; + bool xattr_new_names_; + InitializationStateDcheck initialized_; +}; + + +CrashReportDatabaseMac::CrashReportDatabaseMac(const base::FilePath& path) + : CrashReportDatabase(), + base_dir_(path), + settings_(), + xattr_new_names_(false), + initialized_() { +} + +CrashReportDatabaseMac::~CrashReportDatabaseMac() {} + +bool CrashReportDatabaseMac::Initialize(bool may_create) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + // Check if the database already exists. + if (may_create) { + if (!CreateOrEnsureDirectoryExists(base_dir_)) { + return false; + } + } else if (!EnsureDirectoryExists(base_dir_)) { + return false; + } + + // Create the three processing directories for the database. + for (size_t i = 0; i < std::size(kReportDirectories); ++i) { + if (!CreateOrEnsureDirectoryExists(base_dir_.Append(kReportDirectories[i]))) + return false; + } + + if (!CreateOrEnsureDirectoryExists(AttachmentsRootPath())) { + return false; + } + + if (!settings_.Initialize(base_dir_.Append(kSettings))) + return false; + + // Do an xattr operation as the last step, to ensure the filesystem has + // support for them. This xattr also serves as a marker for whether the + // database uses old or new xattr names. + bool value; + if (ReadXattrBool(base_dir_, + XattrNameInternal(kXattrDatabaseInitialized, true), + &value) == XattrStatus::kOK && + value) { + xattr_new_names_ = true; + } else if (ReadXattrBool(base_dir_, + XattrNameInternal(kXattrDatabaseInitialized, false), + &value) == XattrStatus::kOK && + value) { + xattr_new_names_ = false; + } else { + xattr_new_names_ = true; + if (!WriteXattrBool(base_dir_, XattrName(kXattrDatabaseInitialized), true)) + return false; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +base::FilePath CrashReportDatabaseMac::DatabasePath() { + return base_dir_; +} + +Settings* CrashReportDatabaseMac::GetSettings() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &settings_; +} + +CrashReportDatabase::OperationStatus +CrashReportDatabaseMac::PrepareNewCrashReport( + std::unique_ptr* out_report) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + std::unique_ptr report(new NewReport()); + if (!report->Initialize(this, + base_dir_.Append(kWriteDirectory), + std::string(".") + kCrashReportFileExtension)) { + return kFileSystemError; + } + + // TODO(rsesek): Potentially use an fsetxattr() here instead. + if (!WriteXattr(report->file_remover_.get(), + XattrName(kXattrUUID), + report->ReportID().ToString())) { + return kDatabaseError; + } + + out_report->reset(report.release()); + return kNoError; +} + +CrashReportDatabase::OperationStatus +CrashReportDatabaseMac::FinishedWritingCrashReport( + std::unique_ptr report, + UUID* uuid) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + const base::FilePath& path = report->file_remover_.get(); + + // Get the report's UUID to return. + std::string uuid_string; + if (ReadXattr(path, XattrName(kXattrUUID), &uuid_string) != + XattrStatus::kOK || + !uuid->InitializeFromString(uuid_string)) { + LOG(ERROR) << "Failed to read UUID for crash report " << path.value(); + return kDatabaseError; + } + + if (*uuid != report->ReportID()) { + LOG(ERROR) << "UUID mismatch for crash report " << path.value(); + return kDatabaseError; + } + + // Record the creation time of this report. + if (!WriteXattrTimeT(path, XattrName(kXattrCreationTime), time(nullptr))) { + return kDatabaseError; + } + + FileOffset size = report->Writer()->Seek(0, SEEK_END); + + // Move the report to its new location for uploading. + base::FilePath new_path = + base_dir_.Append(kUploadPendingDirectory).Append(path.BaseName()); + if (rename(path.value().c_str(), new_path.value().c_str()) != 0) { + PLOG(ERROR) << "rename " << path.value() << " to " << new_path.value(); + return kFileSystemError; + } + std::ignore = report->file_remover_.release(); + + // Close all the attachments and disarm their removers too. + for (auto& writer : report->attachment_writers_) { + writer->Close(); + } + for (auto& remover : report->attachment_removers_) { + std::ignore = remover.release(); + } + + Metrics::CrashReportPending(Metrics::PendingReportReason::kNewlyCreated); + Metrics::CrashReportSize(size); + + return kNoError; +} + +CrashReportDatabase::OperationStatus +CrashReportDatabaseMac::LookUpCrashReport(const UUID& uuid, + CrashReportDatabase::Report* report) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + base::FilePath path = LocateCrashReport(uuid, kReportStateAny); + if (path.empty()) + return kReportNotFound; + + base::ScopedFD lock(ObtainReportLock(path)); + if (!lock.is_valid()) + return kBusyError; + + *report = Report(); + report->file_path = path; + if (!ReadReportMetadataLocked(path, report)) + return kDatabaseError; + + return kNoError; +} + +CrashReportDatabase::OperationStatus +CrashReportDatabaseMac::GetPendingReports( + std::vector* reports) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + return ReportsInDirectory(base_dir_.Append(kUploadPendingDirectory), reports); +} + +CrashReportDatabase::OperationStatus +CrashReportDatabaseMac::GetCompletedReports( + std::vector* reports) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + return ReportsInDirectory(base_dir_.Append(kCompletedDirectory), reports); +} + +CrashReportDatabase::OperationStatus +CrashReportDatabaseMac::GetReportForUploading( + const UUID& uuid, + std::unique_ptr* report, + bool report_metrics) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + auto upload_report = std::make_unique(); + + upload_report->file_path = LocateCrashReport(uuid, kReportStatePending); + if (upload_report->file_path.empty()) + return kReportNotFound; + + base::ScopedFD lock(ObtainReportLock(upload_report->file_path)); + if (!lock.is_valid()) + return kBusyError; + + if (!ReadReportMetadataLocked(upload_report->file_path, upload_report.get())) + return kDatabaseError; + + if (!upload_report->Initialize(upload_report->file_path, this)) { + return kFileSystemError; + } + + upload_report->database_ = this; + upload_report->lock_fd.reset(lock.release()); + upload_report->report_metrics_ = report_metrics; + report->reset(upload_report.release()); + return kNoError; +} + +CrashReportDatabase::OperationStatus +CrashReportDatabaseMac::RecordUploadAttempt(UploadReport* report, + bool successful, + const std::string& id) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (report->report_metrics_) { + Metrics::CrashUploadAttempted(successful); + } + + DCHECK(report); + DCHECK(successful || id.empty()); + + base::FilePath report_path = + LocateCrashReport(report->uuid, kReportStatePending); + if (report_path.empty()) + return kReportNotFound; + + if (successful) { + CrashReportDatabase::OperationStatus os = + MarkReportCompletedLocked(report_path, &report_path); + if (os != kNoError) + return os; + } + + if (!WriteXattrBool(report_path, XattrName(kXattrIsUploaded), successful)) { + return kDatabaseError; + } + if (!WriteXattr(report_path, XattrName(kXattrCollectorID), id)) { + return kDatabaseError; + } + + time_t now = time(nullptr); + if (!WriteXattrTimeT(report_path, XattrName(kXattrLastUploadTime), now)) { + return kDatabaseError; + } + + int upload_attempts = 0; + std::string name = XattrName(kXattrUploadAttemptCount); + if (ReadXattrInt(report_path, name, &upload_attempts) == + XattrStatus::kOtherError) { + return kDatabaseError; + } + if (!WriteXattrInt(report_path, name, ++upload_attempts)) { + return kDatabaseError; + } + + if (!settings_.SetLastUploadAttemptTime(now)) { + return kDatabaseError; + } + + return kNoError; +} + +CrashReportDatabase::OperationStatus CrashReportDatabaseMac::SkipReportUpload( + const UUID& uuid, + Metrics::CrashSkippedReason reason) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + Metrics::CrashUploadSkipped(reason); + + base::FilePath report_path = LocateCrashReport(uuid, kReportStatePending); + if (report_path.empty()) + return kReportNotFound; + + base::ScopedFD lock(ObtainReportLock(report_path)); + if (!lock.is_valid()) + return kBusyError; + + return MarkReportCompletedLocked(report_path, nullptr); +} + +CrashReportDatabase::OperationStatus CrashReportDatabaseMac::DeleteReport( + const UUID& uuid) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + base::FilePath report_path = LocateCrashReport(uuid, kReportStateAny); + if (report_path.empty()) + return kReportNotFound; + + base::ScopedFD lock(ObtainReportLock(report_path)); + if (!lock.is_valid()) + return kBusyError; + + if (unlink(report_path.value().c_str()) != 0) { + PLOG(ERROR) << "unlink " << report_path.value(); + return kFileSystemError; + } + + RemoveAttachmentsByUUID(uuid); + + return kNoError; +} + +base::FilePath CrashReportDatabaseMac::LocateCrashReport( + const UUID& uuid, + uint8_t desired_state) { + const std::string target_uuid = uuid.ToString(); + + std::vector report_directories; + if (desired_state & kReportStateWrite) { + report_directories.push_back(kWriteDirectory); + } + if (desired_state & kReportStatePending) { + report_directories.push_back(kUploadPendingDirectory); + } + if (desired_state & kReportStateCompleted) { + report_directories.push_back(kCompletedDirectory); + } + + for (const std::string& report_directory : report_directories) { + base::FilePath path = + base_dir_.Append(report_directory) + .Append(target_uuid + "." + kCrashReportFileExtension); + + // Test if the path exists. + struct stat st; + if (lstat(path.value().c_str(), &st)) { + continue; + } + + // Check that the UUID of the report matches. + std::string uuid_string; + if (ReadXattr(path, XattrName(kXattrUUID), + &uuid_string) == XattrStatus::kOK && + uuid_string == target_uuid) { + return path; + } + } + + return base::FilePath(); +} + +CrashReportDatabase::OperationStatus CrashReportDatabaseMac::RequestUpload( + const UUID& uuid) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + base::FilePath report_path = + LocateCrashReport(uuid, kReportStatePending | kReportStateCompleted); + if (report_path.empty()) + return kReportNotFound; + + base::ScopedFD lock(ObtainReportLock(report_path)); + if (!lock.is_valid()) + return kBusyError; + + // If the crash report has already been uploaded, don't request new upload. + bool uploaded = false; + XattrStatus status = + ReadXattrBool(report_path, XattrName(kXattrIsUploaded), &uploaded); + if (status == XattrStatus::kOtherError) + return kDatabaseError; + if (uploaded) + return kCannotRequestUpload; + + // Mark the crash report as having upload explicitly requested by the user, + // and move it to the pending state. + if (!WriteXattrBool( + report_path, XattrName(kXattrIsUploadExplicitlyRequested), true)) { + return kDatabaseError; + } + + base::FilePath new_path = + base_dir_.Append(kUploadPendingDirectory).Append(report_path.BaseName()); + if (rename(report_path.value().c_str(), new_path.value().c_str()) != 0) { + PLOG(ERROR) << "rename " << report_path.value() << " to " + << new_path.value(); + return kFileSystemError; + } + + Metrics::CrashReportPending(Metrics::PendingReportReason::kUserInitiated); + + return kNoError; +} + +int CrashReportDatabaseMac::CleanDatabase(time_t lockfile_ttl) { + int removed = 0; + time_t now = time(nullptr); + + DirectoryReader reader; + const base::FilePath new_dir(base_dir_.Append(kWriteDirectory)); + if (reader.Open(new_dir)) { + base::FilePath filename; + DirectoryReader::Result result; + while ((result = reader.NextFile(&filename)) == + DirectoryReader::Result::kSuccess) { + const base::FilePath filepath(new_dir.Append(filename)); + timespec filetime; + if (!FileModificationTime(filepath, &filetime)) { + continue; + } + if (filetime.tv_sec <= now - lockfile_ttl) { + if (LoggingRemoveFile(filepath)) { + ++removed; + } + } + } + } + + CleanOrphanedAttachments(); + return removed; +} + +// static +base::ScopedFD CrashReportDatabaseMac::ObtainReportLock( + const base::FilePath& path) { + int fd = HANDLE_EINTR( + open(path.value().c_str(), + O_RDONLY | O_NONBLOCK | O_EXLOCK | O_NOCTTY | O_CLOEXEC)); + PLOG_IF(ERROR, fd < 0) << "open lock " << path.value(); + return base::ScopedFD(fd); +} + +bool CrashReportDatabaseMac::ReadReportMetadataLocked( + const base::FilePath& path, Report* report) { + std::string uuid_string; + if (ReadXattr(path, XattrName(kXattrUUID), + &uuid_string) != XattrStatus::kOK || + !report->uuid.InitializeFromString(uuid_string)) { + return false; + } + + if (ReadXattrTimeT(path, XattrName(kXattrCreationTime), + &report->creation_time) != XattrStatus::kOK) { + return false; + } + + report->id = std::string(); + if (ReadXattr(path, XattrName(kXattrCollectorID), + &report->id) == XattrStatus::kOtherError) { + return false; + } + + report->uploaded = false; + if (ReadXattrBool(path, XattrName(kXattrIsUploaded), + &report->uploaded) == XattrStatus::kOtherError) { + return false; + } + + report->last_upload_attempt_time = 0; + if (ReadXattrTimeT(path, XattrName(kXattrLastUploadTime), + &report->last_upload_attempt_time) == + XattrStatus::kOtherError) { + return false; + } + + report->upload_attempts = 0; + if (ReadXattrInt(path, XattrName(kXattrUploadAttemptCount), + &report->upload_attempts) == XattrStatus::kOtherError) { + return false; + } + + report->upload_explicitly_requested = false; + if (ReadXattrBool(path, + XattrName(kXattrIsUploadExplicitlyRequested), + &report->upload_explicitly_requested) == + XattrStatus::kOtherError) { + return false; + } + + // Seed the total size with the main report size and then add the sizes of any + // potential attachments. + uint64_t total_size = GetFileSize(path); + total_size += GetDirectorySize(AttachmentsPath(report->uuid)); + report->total_size = total_size; + + return true; +} + +CrashReportDatabase::OperationStatus CrashReportDatabaseMac::ReportsInDirectory( + const base::FilePath& path, + std::vector* reports) { + base::mac::ScopedNSAutoreleasePool pool; + + DCHECK(reports->empty()); + + NSError* error = nil; + NSArray* paths = [[NSFileManager defaultManager] + contentsOfDirectoryAtPath:base::SysUTF8ToNSString(path.value()) + error:&error]; + if (error) { + LOG(ERROR) << "Failed to enumerate reports in directory " << path.value() + << ": " << [[error description] UTF8String]; + return kFileSystemError; + } + + reports->reserve([paths count]); + for (NSString* entry in paths) { + Report report; + report.file_path = path.Append([entry fileSystemRepresentation]); + base::ScopedFD lock(ObtainReportLock(report.file_path)); + if (!lock.is_valid()) + continue; + + if (!ReadReportMetadataLocked(report.file_path, &report)) { + LOG(WARNING) << "Failed to read report metadata for " + << report.file_path.value(); + continue; + } + reports->push_back(report); + } + + return kNoError; +} + +std::string CrashReportDatabaseMac::XattrName(const base::StringPiece& name) { + return XattrNameInternal(name, xattr_new_names_); +} + +CrashReportDatabase::OperationStatus +CrashReportDatabaseMac::MarkReportCompletedLocked( + const base::FilePath& report_path, + base::FilePath* out_path) { + if (RemoveXattr(report_path, XattrName(kXattrIsUploadExplicitlyRequested)) == + XattrStatus::kOtherError) { + return kDatabaseError; + } + + base::FilePath new_path = + base_dir_.Append(kCompletedDirectory).Append(report_path.BaseName()); + if (rename(report_path.value().c_str(), new_path.value().c_str()) != 0) { + PLOG(ERROR) << "rename " << report_path.value() << " to " + << new_path.value(); + return kFileSystemError; + } + + if (out_path) + *out_path = new_path; + return kNoError; +} + +void CrashReportDatabaseMac::CleanOrphanedAttachments() { + base::FilePath root_attachments_dir(AttachmentsRootPath()); + DirectoryReader reader; + if (!reader.Open(root_attachments_dir)) { + return; + } + + base::FilePath filename; + DirectoryReader::Result result; + while ((result = reader.NextFile(&filename)) == + DirectoryReader::Result::kSuccess) { + const base::FilePath report_attachment_dir( + root_attachments_dir.Append(filename)); + if (IsDirectory(report_attachment_dir, false)) { + UUID uuid; + if (!uuid.InitializeFromString(filename.value())) { + LOG(ERROR) << "unexpected attachment dir name " << filename.value(); + continue; + } + + // Check to see if the report is being created in "new". + base::FilePath new_dir_path = + base_dir_.Append(kWriteDirectory) + .Append(uuid.ToString() + "." + kCrashReportFileExtension); + if (IsRegularFile(new_dir_path)) { + continue; + } + + // Check to see if the report is in "pending" or "completed". + base::FilePath local_path = + LocateCrashReport(uuid, kReportStatePending | kReportStateCompleted); + if (!local_path.empty()) { + continue; + } + + // Couldn't find a report, assume these attachments are orphaned. + RemoveAttachmentsByUUID(uuid); + } + } +} + +std::unique_ptr InitializeInternal( + const base::FilePath& path, + bool may_create) { + std::unique_ptr database_mac( + new CrashReportDatabaseMac(path)); + if (!database_mac->Initialize(may_create)) + database_mac.reset(); + + return std::unique_ptr(database_mac.release()); +} + +// static +std::unique_ptr CrashReportDatabase::Initialize( + const base::FilePath& path) { + return InitializeInternal(path, true); +} + +// static +std::unique_ptr +CrashReportDatabase::InitializeWithoutCreating(const base::FilePath& path) { + return InitializeInternal(path, false); +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/crash_report_database_test.cc b/shared/sentry/external/crashpad/client/crash_report_database_test.cc new file mode 100644 index 000000000..d75ab355b --- /dev/null +++ b/shared/sentry/external/crashpad/client/crash_report_database_test.cc @@ -0,0 +1,905 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "client/crash_report_database.h" + +#include "build/build_config.h" +#include "client/settings.h" +#include "gtest/gtest.h" +#include "test/errors.h" +#include "test/file.h" +#include "test/filesystem.h" +#include "test/scoped_temp_dir.h" +#include "util/file/file_io.h" +#include "util/file/filesystem.h" + +namespace crashpad { +namespace test { +namespace { + +class CrashReportDatabaseTest : public testing::Test { + public: + CrashReportDatabaseTest() {} + + CrashReportDatabaseTest(const CrashReportDatabaseTest&) = delete; + CrashReportDatabaseTest& operator=(const CrashReportDatabaseTest&) = delete; + + protected: + // testing::Test: + void SetUp() override { + db_ = CrashReportDatabase::Initialize(path()); + ASSERT_TRUE(db_); + } + + void ResetDatabase() { db_.reset(); } + + CrashReportDatabase* db() { return db_.get(); } + base::FilePath path() const { + return temp_dir_.path().Append(FILE_PATH_LITERAL("crashpad_test_database")); + } + + void CreateCrashReport(CrashReportDatabase::Report* report) { + std::unique_ptr new_report; + ASSERT_EQ(db_->PrepareNewCrashReport(&new_report), + CrashReportDatabase::kNoError); + static constexpr char kTest[] = "test"; + ASSERT_TRUE(new_report->Writer()->Write(kTest, sizeof(kTest))); + + char contents[sizeof(kTest)]; + FileReaderInterface* reader = new_report->Reader(); + ASSERT_TRUE(reader->ReadExactly(contents, sizeof(contents))); + EXPECT_EQ(memcmp(contents, kTest, sizeof(contents)), 0); + EXPECT_EQ(reader->ReadExactly(contents, 1), 0); + + UUID uuid; + EXPECT_EQ(db_->FinishedWritingCrashReport(std::move(new_report), &uuid), + CrashReportDatabase::kNoError); + + EXPECT_EQ(db_->LookUpCrashReport(uuid, report), + CrashReportDatabase::kNoError); + ExpectPreparedCrashReport(*report); + } + + void UploadReport(const UUID& uuid, bool successful, const std::string& id) { + Settings* const settings = db_->GetSettings(); + ASSERT_TRUE(settings); + time_t times[2]; + ASSERT_TRUE(settings->GetLastUploadAttemptTime(×[0])); + + std::unique_ptr report; + ASSERT_EQ(db_->GetReportForUploading(uuid, &report), + CrashReportDatabase::kNoError); + EXPECT_NE(report->uuid, UUID()); + EXPECT_FALSE(report->file_path.empty()); + EXPECT_TRUE(FileExists(report->file_path)) << report->file_path.value(); + EXPECT_GT(report->creation_time, 0); + if (successful) { + EXPECT_EQ(db_->RecordUploadComplete(std::move(report), id), + CrashReportDatabase::kNoError); + } else { + report.reset(); + } + + ASSERT_TRUE(settings->GetLastUploadAttemptTime(×[1])); + EXPECT_NE(times[1], 0); + EXPECT_GE(times[1], times[0]); + } + + void ExpectPreparedCrashReport(const CrashReportDatabase::Report& report) { + EXPECT_NE(report.uuid, UUID()); + EXPECT_FALSE(report.file_path.empty()); + EXPECT_TRUE(FileExists(report.file_path)) << report.file_path.value(); + EXPECT_TRUE(report.id.empty()); + EXPECT_GT(report.creation_time, 0); + EXPECT_FALSE(report.uploaded); + EXPECT_EQ(report.last_upload_attempt_time, 0); + EXPECT_EQ(report.upload_attempts, 0); + EXPECT_FALSE(report.upload_explicitly_requested); + EXPECT_GE(report.total_size, 0u); + } + + void RelocateDatabase() { + ResetDatabase(); + temp_dir_.Rename(); + SetUp(); + } + + CrashReportDatabase::OperationStatus RequestUpload(const UUID& uuid) { + CrashReportDatabase::OperationStatus os = db()->RequestUpload(uuid); + + CrashReportDatabase::Report report; + EXPECT_EQ(db_->LookUpCrashReport(uuid, &report), + CrashReportDatabase::kNoError); + + return os; + } + + private: + ScopedTempDir temp_dir_; + std::unique_ptr db_; +}; + +TEST_F(CrashReportDatabaseTest, Initialize) { + // Initialize the database for the first time, creating it. + ASSERT_TRUE(db()); + + Settings* settings = db()->GetSettings(); + + UUID client_ids[3]; + ASSERT_TRUE(settings->GetClientID(&client_ids[0])); + EXPECT_NE(client_ids[0], UUID()); + + time_t last_upload_attempt_time; + ASSERT_TRUE(settings->GetLastUploadAttemptTime(&last_upload_attempt_time)); + EXPECT_EQ(last_upload_attempt_time, 0); + + // Close and reopen the database at the same path. + ResetDatabase(); + EXPECT_FALSE(db()); + auto db = CrashReportDatabase::InitializeWithoutCreating(path()); + ASSERT_TRUE(db); + + settings = db->GetSettings(); + + ASSERT_TRUE(settings->GetClientID(&client_ids[1])); + EXPECT_EQ(client_ids[1], client_ids[0]); + + ASSERT_TRUE(settings->GetLastUploadAttemptTime(&last_upload_attempt_time)); + EXPECT_EQ(last_upload_attempt_time, 0); + + // Check that the database can also be opened by the method that is permitted + // to create it. + db = CrashReportDatabase::Initialize(path()); + ASSERT_TRUE(db); + + settings = db->GetSettings(); + + ASSERT_TRUE(settings->GetClientID(&client_ids[2])); + EXPECT_EQ(client_ids[2], client_ids[0]); + + ASSERT_TRUE(settings->GetLastUploadAttemptTime(&last_upload_attempt_time)); + EXPECT_EQ(last_upload_attempt_time, 0); + + std::vector reports; + EXPECT_EQ(db->GetPendingReports(&reports), CrashReportDatabase::kNoError); + EXPECT_TRUE(reports.empty()); + reports.clear(); + EXPECT_EQ(db->GetCompletedReports(&reports), CrashReportDatabase::kNoError); + EXPECT_TRUE(reports.empty()); + + // InitializeWithoutCreating() shouldn’t create a nonexistent database. + base::FilePath non_database_path = + path().DirName().Append(FILE_PATH_LITERAL("not_a_database")); + db = CrashReportDatabase::InitializeWithoutCreating(non_database_path); + EXPECT_FALSE(db); +} + +TEST_F(CrashReportDatabaseTest, NewCrashReport) { + std::unique_ptr new_report; + EXPECT_EQ(db()->PrepareNewCrashReport(&new_report), + CrashReportDatabase::kNoError); + UUID expect_uuid = new_report->ReportID(); + UUID uuid; + EXPECT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid), + CrashReportDatabase::kNoError); + EXPECT_EQ(uuid, expect_uuid); + + CrashReportDatabase::Report report; + EXPECT_EQ(db()->LookUpCrashReport(uuid, &report), + CrashReportDatabase::kNoError); + ExpectPreparedCrashReport(report); + + std::vector reports; + EXPECT_EQ(db()->GetPendingReports(&reports), CrashReportDatabase::kNoError); + ASSERT_EQ(reports.size(), 1u); + EXPECT_EQ(reports[0].uuid, report.uuid); + + reports.clear(); + EXPECT_EQ(db()->GetCompletedReports(&reports), CrashReportDatabase::kNoError); + EXPECT_TRUE(reports.empty()); +} + +TEST_F(CrashReportDatabaseTest, LookUpCrashReport) { + UUID uuid; + + { + CrashReportDatabase::Report report; + CreateCrashReport(&report); + uuid = report.uuid; + } + + { + CrashReportDatabase::Report report; + EXPECT_EQ(db()->LookUpCrashReport(uuid, &report), + CrashReportDatabase::kNoError); + EXPECT_EQ(report.uuid, uuid); + EXPECT_NE(report.file_path.value().find(path().value()), std::string::npos); + EXPECT_EQ(report.id, std::string()); + EXPECT_FALSE(report.uploaded); + EXPECT_EQ(report.last_upload_attempt_time, 0); + EXPECT_EQ(report.upload_attempts, 0); + EXPECT_FALSE(report.upload_explicitly_requested); + } + + UploadReport(uuid, true, "test"); + + { + CrashReportDatabase::Report report; + EXPECT_EQ(db()->LookUpCrashReport(uuid, &report), + CrashReportDatabase::kNoError); + EXPECT_EQ(report.uuid, uuid); + EXPECT_NE(report.file_path.value().find(path().value()), std::string::npos); + EXPECT_EQ(report.id, "test"); + EXPECT_TRUE(report.uploaded); + EXPECT_NE(report.last_upload_attempt_time, 0); + EXPECT_EQ(report.upload_attempts, 1); + EXPECT_FALSE(report.upload_explicitly_requested); + } +} + +TEST_F(CrashReportDatabaseTest, RecordUploadAttempt) { + std::vector reports(3); + CreateCrashReport(&reports[0]); + CreateCrashReport(&reports[1]); + CreateCrashReport(&reports[2]); + + // Record two attempts: one successful, one not. + UploadReport(reports[1].uuid, false, std::string()); + UploadReport(reports[2].uuid, true, "abc123"); + + std::vector query(3); + EXPECT_EQ(db()->LookUpCrashReport(reports[0].uuid, &query[0]), + CrashReportDatabase::kNoError); + EXPECT_EQ(db()->LookUpCrashReport(reports[1].uuid, &query[1]), + CrashReportDatabase::kNoError); + EXPECT_EQ(db()->LookUpCrashReport(reports[2].uuid, &query[2]), + CrashReportDatabase::kNoError); + + EXPECT_EQ(query[0].id, std::string()); + EXPECT_EQ(query[1].id, std::string()); + EXPECT_EQ(query[2].id, "abc123"); + + EXPECT_FALSE(query[0].uploaded); + EXPECT_FALSE(query[1].uploaded); + EXPECT_TRUE(query[2].uploaded); + + EXPECT_EQ(query[0].last_upload_attempt_time, 0); + EXPECT_NE(query[1].last_upload_attempt_time, 0); + EXPECT_NE(query[2].last_upload_attempt_time, 0); + + EXPECT_EQ(query[0].upload_attempts, 0); + EXPECT_EQ(query[1].upload_attempts, 1); + EXPECT_EQ(query[2].upload_attempts, 1); + + // Attempt to upload and fail again. + UploadReport(reports[1].uuid, false, std::string()); + + time_t report_2_upload_time = query[2].last_upload_attempt_time; + + EXPECT_EQ(db()->LookUpCrashReport(reports[0].uuid, &query[0]), + CrashReportDatabase::kNoError); + EXPECT_EQ(db()->LookUpCrashReport(reports[1].uuid, &query[1]), + CrashReportDatabase::kNoError); + EXPECT_EQ(db()->LookUpCrashReport(reports[2].uuid, &query[2]), + CrashReportDatabase::kNoError); + + EXPECT_FALSE(query[0].uploaded); + EXPECT_FALSE(query[1].uploaded); + EXPECT_TRUE(query[2].uploaded); + + EXPECT_EQ(query[0].last_upload_attempt_time, 0); + EXPECT_GE(query[1].last_upload_attempt_time, report_2_upload_time); + EXPECT_EQ(query[2].last_upload_attempt_time, report_2_upload_time); + + EXPECT_EQ(query[0].upload_attempts, 0); + EXPECT_EQ(query[1].upload_attempts, 2); + EXPECT_EQ(query[2].upload_attempts, 1); + + // Third time's the charm: upload and succeed. + UploadReport(reports[1].uuid, true, "666hahaha"); + + time_t report_1_upload_time = query[1].last_upload_attempt_time; + + EXPECT_EQ(db()->LookUpCrashReport(reports[0].uuid, &query[0]), + CrashReportDatabase::kNoError); + EXPECT_EQ(db()->LookUpCrashReport(reports[1].uuid, &query[1]), + CrashReportDatabase::kNoError); + EXPECT_EQ(db()->LookUpCrashReport(reports[2].uuid, &query[2]), + CrashReportDatabase::kNoError); + + EXPECT_FALSE(query[0].uploaded); + EXPECT_TRUE(query[1].uploaded); + EXPECT_TRUE(query[2].uploaded); + + EXPECT_EQ(query[0].last_upload_attempt_time, 0); + EXPECT_GE(query[1].last_upload_attempt_time, report_1_upload_time); + EXPECT_EQ(query[2].last_upload_attempt_time, report_2_upload_time); + + EXPECT_EQ(query[0].upload_attempts, 0); + EXPECT_EQ(query[1].upload_attempts, 3); + EXPECT_EQ(query[2].upload_attempts, 1); +} + +// This test covers both query functions since they are related. +TEST_F(CrashReportDatabaseTest, GetCompletedAndNotUploadedReports) { + std::vector reports(5); + CreateCrashReport(&reports[0]); + CreateCrashReport(&reports[1]); + CreateCrashReport(&reports[2]); + CreateCrashReport(&reports[3]); + CreateCrashReport(&reports[4]); + + const UUID& report_0_uuid = reports[0].uuid; + const UUID& report_1_uuid = reports[1].uuid; + const UUID& report_2_uuid = reports[2].uuid; + const UUID& report_3_uuid = reports[3].uuid; + const UUID& report_4_uuid = reports[4].uuid; + + std::vector pending; + EXPECT_EQ(db()->GetPendingReports(&pending), CrashReportDatabase::kNoError); + + std::vector completed; + EXPECT_EQ(db()->GetCompletedReports(&completed), + CrashReportDatabase::kNoError); + + EXPECT_EQ(pending.size(), reports.size()); + EXPECT_EQ(completed.size(), 0u); + + // Upload one report successfully. + UploadReport(report_1_uuid, true, "report1"); + + pending.clear(); + EXPECT_EQ(db()->GetPendingReports(&pending), CrashReportDatabase::kNoError); + completed.clear(); + EXPECT_EQ(db()->GetCompletedReports(&completed), + CrashReportDatabase::kNoError); + + EXPECT_EQ(pending.size(), 4u); + ASSERT_EQ(completed.size(), 1u); + + for (const auto& report : pending) { + EXPECT_NE(report.uuid, report_1_uuid); + EXPECT_FALSE(report.file_path.empty()); + } + EXPECT_EQ(completed[0].uuid, report_1_uuid); + EXPECT_EQ(completed[0].id, "report1"); + EXPECT_EQ(completed[0].uploaded, true); + EXPECT_GT(completed[0].last_upload_attempt_time, 0); + EXPECT_EQ(completed[0].upload_attempts, 1); + + // Fail to upload one report. + UploadReport(report_2_uuid, false, std::string()); + + pending.clear(); + EXPECT_EQ(db()->GetPendingReports(&pending), CrashReportDatabase::kNoError); + completed.clear(); + EXPECT_EQ(db()->GetCompletedReports(&completed), + CrashReportDatabase::kNoError); + + EXPECT_EQ(pending.size(), 4u); + ASSERT_EQ(completed.size(), 1u); + + for (const auto& report : pending) { + if (report.upload_attempts != 0) { + EXPECT_EQ(report.uuid, report_2_uuid); + EXPECT_GT(report.last_upload_attempt_time, 0); + EXPECT_FALSE(report.uploaded); + EXPECT_TRUE(report.id.empty()); + } + EXPECT_FALSE(report.file_path.empty()); + } + + // Upload a second report. + UploadReport(report_4_uuid, true, "report_4"); + + pending.clear(); + EXPECT_EQ(db()->GetPendingReports(&pending), CrashReportDatabase::kNoError); + completed.clear(); + EXPECT_EQ(db()->GetCompletedReports(&completed), + CrashReportDatabase::kNoError); + + EXPECT_EQ(pending.size(), 3u); + ASSERT_EQ(completed.size(), 2u); + + // Succeed the failed report. + UploadReport(report_2_uuid, true, "report 2"); + + pending.clear(); + EXPECT_EQ(db()->GetPendingReports(&pending), CrashReportDatabase::kNoError); + completed.clear(); + EXPECT_EQ(db()->GetCompletedReports(&completed), + CrashReportDatabase::kNoError); + + EXPECT_EQ(pending.size(), 2u); + ASSERT_EQ(completed.size(), 3u); + + for (const auto& report : pending) { + EXPECT_TRUE(report.uuid == report_0_uuid || report.uuid == report_3_uuid); + EXPECT_FALSE(report.file_path.empty()); + } + + // Skip upload for one report. + EXPECT_EQ(db()->SkipReportUpload( + report_3_uuid, Metrics::CrashSkippedReason::kUploadsDisabled), + CrashReportDatabase::kNoError); + + pending.clear(); + EXPECT_EQ(db()->GetPendingReports(&pending), CrashReportDatabase::kNoError); + completed.clear(); + EXPECT_EQ(db()->GetCompletedReports(&completed), + CrashReportDatabase::kNoError); + + ASSERT_EQ(pending.size(), 1u); + ASSERT_EQ(completed.size(), 4u); + + EXPECT_EQ(pending[0].uuid, report_0_uuid); + + for (const auto& report : completed) { + if (report.uuid == report_3_uuid) { + EXPECT_FALSE(report.uploaded); + EXPECT_EQ(report.upload_attempts, 0); + EXPECT_EQ(report.last_upload_attempt_time, 0); + } else { + EXPECT_TRUE(report.uploaded); + EXPECT_GT(report.upload_attempts, 0); + EXPECT_GT(report.last_upload_attempt_time, 0); + } + EXPECT_FALSE(report.file_path.empty()); + } +} + +TEST_F(CrashReportDatabaseTest, DuelingUploads) { + CrashReportDatabase::Report report; + CreateCrashReport(&report); + + std::unique_ptr upload_report; + EXPECT_EQ(db()->GetReportForUploading(report.uuid, &upload_report), + CrashReportDatabase::kNoError); + + std::unique_ptr upload_report_2; + EXPECT_EQ(db()->GetReportForUploading(report.uuid, &upload_report_2), + CrashReportDatabase::kBusyError); + EXPECT_FALSE(upload_report_2); + + EXPECT_EQ(db()->RecordUploadComplete(std::move(upload_report), std::string()), + CrashReportDatabase::kNoError); +} + +TEST_F(CrashReportDatabaseTest, UploadAlreadyUploaded) { + CrashReportDatabase::Report report; + CreateCrashReport(&report); + + std::unique_ptr upload_report; + EXPECT_EQ(db()->GetReportForUploading(report.uuid, &upload_report), + CrashReportDatabase::kNoError); + EXPECT_EQ(db()->RecordUploadComplete(std::move(upload_report), std::string()), + CrashReportDatabase::kNoError); + + std::unique_ptr upload_report_2; + EXPECT_EQ(db()->GetReportForUploading(report.uuid, &upload_report_2), + CrashReportDatabase::kReportNotFound); + EXPECT_FALSE(upload_report_2.get()); +} + +TEST_F(CrashReportDatabaseTest, MoveDatabase) { + std::unique_ptr new_report; + EXPECT_EQ(db()->PrepareNewCrashReport(&new_report), + CrashReportDatabase::kNoError); + UUID uuid; + EXPECT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid), + CrashReportDatabase::kNoError); + + RelocateDatabase(); + + CrashReportDatabase::Report report; + EXPECT_EQ(db()->LookUpCrashReport(uuid, &report), + CrashReportDatabase::kNoError); + ExpectPreparedCrashReport(report); +} + +TEST_F(CrashReportDatabaseTest, ReportRemoved) { + std::unique_ptr new_report; + EXPECT_EQ(db()->PrepareNewCrashReport(&new_report), + CrashReportDatabase::kNoError); + + UUID uuid; + EXPECT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid), + CrashReportDatabase::kNoError); + + CrashReportDatabase::Report report; + EXPECT_EQ(db()->LookUpCrashReport(uuid, &report), + CrashReportDatabase::kNoError); + + EXPECT_TRUE(LoggingRemoveFile(report.file_path)); + + EXPECT_EQ(db()->LookUpCrashReport(uuid, &report), + CrashReportDatabase::kReportNotFound); +} + +TEST_F(CrashReportDatabaseTest, DeleteReport) { + CrashReportDatabase::Report keep_pending; + CrashReportDatabase::Report delete_pending; + CrashReportDatabase::Report keep_completed; + CrashReportDatabase::Report delete_completed; + + CreateCrashReport(&keep_pending); + CreateCrashReport(&delete_pending); + CreateCrashReport(&keep_completed); + CreateCrashReport(&delete_completed); + + EXPECT_TRUE(FileExists(keep_pending.file_path)); + EXPECT_TRUE(FileExists(delete_pending.file_path)); + EXPECT_TRUE(FileExists(keep_completed.file_path)); + EXPECT_TRUE(FileExists(delete_completed.file_path)); + + UploadReport(keep_completed.uuid, true, "1"); + UploadReport(delete_completed.uuid, true, "2"); + + EXPECT_EQ(db()->LookUpCrashReport(keep_completed.uuid, &keep_completed), + CrashReportDatabase::kNoError); + EXPECT_EQ(db()->LookUpCrashReport(delete_completed.uuid, &delete_completed), + CrashReportDatabase::kNoError); + + EXPECT_TRUE(FileExists(keep_completed.file_path)); + EXPECT_TRUE(FileExists(delete_completed.file_path)); + + EXPECT_EQ(db()->DeleteReport(delete_pending.uuid), + CrashReportDatabase::kNoError); + EXPECT_FALSE(FileExists(delete_pending.file_path)); + EXPECT_EQ(db()->LookUpCrashReport(delete_pending.uuid, &delete_pending), + CrashReportDatabase::kReportNotFound); + EXPECT_EQ(db()->DeleteReport(delete_pending.uuid), + CrashReportDatabase::kReportNotFound); + + EXPECT_EQ(db()->DeleteReport(delete_completed.uuid), + CrashReportDatabase::kNoError); + EXPECT_FALSE(FileExists(delete_completed.file_path)); + EXPECT_EQ(db()->LookUpCrashReport(delete_completed.uuid, &delete_completed), + CrashReportDatabase::kReportNotFound); + EXPECT_EQ(db()->DeleteReport(delete_completed.uuid), + CrashReportDatabase::kReportNotFound); + + EXPECT_EQ(db()->LookUpCrashReport(keep_pending.uuid, &keep_pending), + CrashReportDatabase::kNoError); + EXPECT_EQ(db()->LookUpCrashReport(keep_completed.uuid, &keep_completed), + CrashReportDatabase::kNoError); +} + +TEST_F(CrashReportDatabaseTest, DeleteReportEmptyingDatabase) { + CrashReportDatabase::Report report; + CreateCrashReport(&report); + + EXPECT_TRUE(FileExists(report.file_path)); + + UploadReport(report.uuid, true, "1"); + + EXPECT_EQ(db()->LookUpCrashReport(report.uuid, &report), + CrashReportDatabase::kNoError); + + EXPECT_TRUE(FileExists(report.file_path)); + + // This causes an empty database to be written, make sure this is handled. + EXPECT_EQ(db()->DeleteReport(report.uuid), CrashReportDatabase::kNoError); + EXPECT_FALSE(FileExists(report.file_path)); +} + +TEST_F(CrashReportDatabaseTest, ReadEmptyDatabase) { + CrashReportDatabase::Report report; + CreateCrashReport(&report); + EXPECT_EQ(db()->DeleteReport(report.uuid), CrashReportDatabase::kNoError); + + // Deleting and the creating another report causes an empty database to be + // loaded. Make sure this is handled. + + CrashReportDatabase::Report report2; + CreateCrashReport(&report2); +} + +TEST_F(CrashReportDatabaseTest, RequestUpload) { + std::vector reports(2); + CreateCrashReport(&reports[0]); + CreateCrashReport(&reports[1]); + + const UUID& report_0_uuid = reports[0].uuid; + const UUID& report_1_uuid = reports[1].uuid; + + // Skipped report gets back to pending state after RequestUpload is called. + EXPECT_EQ(db()->SkipReportUpload( + report_1_uuid, Metrics::CrashSkippedReason::kUploadsDisabled), + CrashReportDatabase::kNoError); + + std::vector pending_reports; + CrashReportDatabase::OperationStatus os = + db()->GetPendingReports(&pending_reports); + EXPECT_EQ(os, CrashReportDatabase::kNoError); + ASSERT_EQ(pending_reports.size(), 1u); + EXPECT_EQ(report_0_uuid, pending_reports[0].uuid); + + pending_reports.clear(); + EXPECT_EQ(RequestUpload(report_1_uuid), CrashReportDatabase::kNoError); + os = db()->GetPendingReports(&pending_reports); + EXPECT_EQ(os, CrashReportDatabase::kNoError); + ASSERT_EQ(pending_reports.size(), 2u); + + // Check individual reports. + const CrashReportDatabase::Report* explicitly_requested_report; + const CrashReportDatabase::Report* pending_report; + if (pending_reports[0].uuid == report_0_uuid) { + pending_report = &pending_reports[0]; + explicitly_requested_report = &pending_reports[1]; + } else { + pending_report = &pending_reports[1]; + explicitly_requested_report = &pending_reports[0]; + } + + EXPECT_EQ(pending_report->uuid, report_0_uuid); + EXPECT_FALSE(pending_report->upload_explicitly_requested); + + EXPECT_EQ(explicitly_requested_report->uuid, report_1_uuid); + EXPECT_TRUE(explicitly_requested_report->upload_explicitly_requested); + + // Explicitly requested reports will not have upload_explicitly_requested bit + // after getting skipped. + EXPECT_EQ(db()->SkipReportUpload( + report_1_uuid, Metrics::CrashSkippedReason::kUploadsDisabled), + CrashReportDatabase::kNoError); + CrashReportDatabase::Report report; + EXPECT_EQ(db()->LookUpCrashReport(report_1_uuid, &report), + CrashReportDatabase::kNoError); + EXPECT_FALSE(report.upload_explicitly_requested); + + // Pending report gets correctly affected after RequestUpload is called. + pending_reports.clear(); + EXPECT_EQ(RequestUpload(report_0_uuid), CrashReportDatabase::kNoError); + os = db()->GetPendingReports(&pending_reports); + EXPECT_EQ(os, CrashReportDatabase::kNoError); + EXPECT_EQ(pending_reports.size(), 1u); + EXPECT_EQ(report_0_uuid, pending_reports[0].uuid); + EXPECT_TRUE(pending_reports[0].upload_explicitly_requested); + + // Already uploaded report cannot be requested for the new upload. + UploadReport(report_0_uuid, true, "1"); + EXPECT_EQ(RequestUpload(report_0_uuid), + CrashReportDatabase::kCannotRequestUpload); +} + +TEST_F(CrashReportDatabaseTest, Attachments) { + std::unique_ptr new_report; + ASSERT_EQ(db()->PrepareNewCrashReport(&new_report), + CrashReportDatabase::kNoError); + + FileWriter* attach_some_file = new_report->AddAttachment("some_file"); + ASSERT_NE(attach_some_file, nullptr); + static constexpr char test_data[] = "test data"; + attach_some_file->Write(test_data, sizeof(test_data)); + + FileWriter* failed_attach = new_report->AddAttachment("not/a valid fi!e"); + EXPECT_EQ(failed_attach, nullptr); + + UUID expect_uuid = new_report->ReportID(); + UUID uuid; + ASSERT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid), + CrashReportDatabase::kNoError); + EXPECT_EQ(uuid, expect_uuid); + + CrashReportDatabase::Report report; + EXPECT_EQ(db()->LookUpCrashReport(uuid, &report), + CrashReportDatabase::kNoError); + ExpectPreparedCrashReport(report); + + std::vector reports; + EXPECT_EQ(db()->GetPendingReports(&reports), CrashReportDatabase::kNoError); + ASSERT_EQ(reports.size(), 1u); + EXPECT_EQ(reports[0].uuid, report.uuid); + + std::unique_ptr upload_report; + ASSERT_EQ(db()->GetReportForUploading(reports[0].uuid, &upload_report), + CrashReportDatabase::kNoError); + std::map result_attachments = + upload_report->GetAttachments(); + EXPECT_EQ(result_attachments.size(), 1u); + EXPECT_NE(result_attachments.find("some_file"), result_attachments.end()); + char result_buffer[sizeof(test_data)]; + result_attachments["some_file"]->Read(result_buffer, sizeof(result_buffer)); + EXPECT_EQ(memcmp(test_data, result_buffer, sizeof(test_data)), 0); +} + +TEST_F(CrashReportDatabaseTest, OrphanedAttachments) { + std::unique_ptr new_report; + ASSERT_EQ(db()->PrepareNewCrashReport(&new_report), + CrashReportDatabase::kNoError); + + FileWriter* file1 = new_report->AddAttachment("file1"); + ASSERT_NE(file1, nullptr); + FileWriter* file2 = new_report->AddAttachment("file2"); + ASSERT_NE(file2, nullptr); + + UUID expect_uuid = new_report->ReportID(); + UUID uuid; + ASSERT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid), + CrashReportDatabase::kNoError); + EXPECT_EQ(uuid, expect_uuid); + + CrashReportDatabase::Report report; + ASSERT_EQ(db()->LookUpCrashReport(uuid, &report), + CrashReportDatabase::kNoError); + + ASSERT_TRUE(LoggingRemoveFile(report.file_path)); + +#if !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_WIN) + // CrashReportDatabaseMac stores metadata in xattrs and does not have .meta + // files. + // CrashReportDatabaseWin stores metadata in a global metadata file and not + // per report. + ASSERT_TRUE(LoggingRemoveFile(base::FilePath( + report.file_path.RemoveFinalExtension().value() + ".meta"))); +#endif + + ASSERT_EQ(db()->LookUpCrashReport(uuid, &report), + CrashReportDatabase::kReportNotFound); + +#if BUILDFLAG(IS_WIN) + const std::wstring uuid_string = uuid.ToWString(); +#else + const std::string uuid_string = uuid.ToString(); +#endif + base::FilePath report_attachments_dir( + path().Append(FILE_PATH_LITERAL("attachments")).Append(uuid_string)); + base::FilePath file_path1( + report_attachments_dir.Append(FILE_PATH_LITERAL("file1"))); + base::FilePath file_path2( + report_attachments_dir.Append(FILE_PATH_LITERAL("file2"))); + EXPECT_TRUE(FileExists(file_path1)); + EXPECT_TRUE(FileExists(file_path1)); + +#if BUILDFLAG(IS_WIN) + // On Windows, reports removed from metadata are counted, even if the file + // is not on the disk. + EXPECT_EQ(db()->CleanDatabase(0), 1); +#else + EXPECT_EQ(db()->CleanDatabase(0), 0); +#endif + + EXPECT_FALSE(FileExists(file_path1)); + EXPECT_FALSE(FileExists(file_path2)); + EXPECT_FALSE(FileExists(report_attachments_dir)); +} + +// This test uses knowledge of the database format to break it, so it only +// applies to the unfified database implementation. +#if !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_WIN) +TEST_F(CrashReportDatabaseTest, CleanBrokenDatabase) { + // Remove report files if metadata goes missing. + CrashReportDatabase::Report report; + ASSERT_NO_FATAL_FAILURE(CreateCrashReport(&report)); + + const base::FilePath metadata( + report.file_path.RemoveFinalExtension().value() + + FILE_PATH_LITERAL(".meta")); + ASSERT_TRUE(PathExists(report.file_path)); + ASSERT_TRUE(PathExists(metadata)); + + ASSERT_TRUE(LoggingRemoveFile(metadata)); + EXPECT_EQ(db()->CleanDatabase(0), 1); + + EXPECT_FALSE(PathExists(report.file_path)); + EXPECT_FALSE(PathExists(metadata)); + + // Remove metadata files if reports go missing. + ASSERT_NO_FATAL_FAILURE(CreateCrashReport(&report)); + const base::FilePath metadata2( + report.file_path.RemoveFinalExtension().value() + + FILE_PATH_LITERAL(".meta")); + ASSERT_TRUE(PathExists(report.file_path)); + ASSERT_TRUE(PathExists(metadata2)); + + ASSERT_TRUE(LoggingRemoveFile(report.file_path)); + EXPECT_EQ(db()->CleanDatabase(0), 1); + + EXPECT_FALSE(PathExists(report.file_path)); + EXPECT_FALSE(PathExists(metadata2)); + + // Remove stale new files. + std::unique_ptr new_report; + EXPECT_EQ(db()->PrepareNewCrashReport(&new_report), + CrashReportDatabase::kNoError); + new_report->Writer()->Close(); + EXPECT_EQ(db()->CleanDatabase(0), 1); + + // Remove stale lock files and their associated reports. + ASSERT_NO_FATAL_FAILURE(CreateCrashReport(&report)); + const base::FilePath metadata3( + report.file_path.RemoveFinalExtension().value() + + FILE_PATH_LITERAL(".meta")); + ASSERT_TRUE(PathExists(report.file_path)); + ASSERT_TRUE(PathExists(metadata3)); + + const base::FilePath lockpath( + report.file_path.RemoveFinalExtension().value() + + FILE_PATH_LITERAL(".lock")); + ScopedFileHandle handle(LoggingOpenFileForWrite( + lockpath, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly)); + ASSERT_TRUE(handle.is_valid()); + + time_t expired_timestamp = time(nullptr) - 60 * 60 * 24 * 3; + + ASSERT_TRUE(LoggingWriteFile( + handle.get(), &expired_timestamp, sizeof(expired_timestamp))); + ASSERT_TRUE(LoggingCloseFile(handle.release())); + + EXPECT_EQ(db()->CleanDatabase(0), 1); + + EXPECT_FALSE(PathExists(report.file_path)); + EXPECT_FALSE(PathExists(metadata3)); +} +#endif // !BUILDFLAG(IS_APPLE) && !BUILDFLAG(IS_WIN) + +TEST_F(CrashReportDatabaseTest, TotalSize_MainReportOnly) { + std::unique_ptr new_report; + ASSERT_EQ(db()->PrepareNewCrashReport(&new_report), + CrashReportDatabase::kNoError); + + // Main report. + static constexpr char main_report_data[] = "dlbvandslhb"; + ASSERT_TRUE( + new_report->Writer()->Write(main_report_data, sizeof(main_report_data))); + + UUID uuid; + ASSERT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid), + CrashReportDatabase::kNoError); + + CrashReportDatabase::Report report; + ASSERT_EQ(db()->LookUpCrashReport(uuid, &report), + CrashReportDatabase::kNoError); + + EXPECT_EQ(report.total_size, sizeof(main_report_data)); +} + +TEST_F(CrashReportDatabaseTest, GetReportSize_RightSizeWithAttachments) { + std::unique_ptr new_report; + ASSERT_EQ(db()->PrepareNewCrashReport(&new_report), + CrashReportDatabase::kNoError); + + // Main report. + static constexpr char main_report_data[] = "dlbvandslhb"; + ASSERT_TRUE( + new_report->Writer()->Write(main_report_data, sizeof(main_report_data))); + + // First attachment. + FileWriter* attachment_1 = new_report->AddAttachment("my_attachment_1"); + ASSERT_NE(attachment_1, nullptr); + static constexpr char attachment_1_data[] = "vKDnidhvbiudshoihbvdsoiuh nhh"; + attachment_1->Write(attachment_1_data, sizeof(attachment_1_data)); + + // Second attachment. + FileWriter* attachment_2 = new_report->AddAttachment("my_attachment_2"); + ASSERT_NE(attachment_2, nullptr); + static constexpr char attachment_2_data[] = "sgvsvgusiyguysigfkhpmo-["; + attachment_2->Write(attachment_2_data, sizeof(attachment_2_data)); + + UUID uuid; + ASSERT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid), + CrashReportDatabase::kNoError); + + CrashReportDatabase::Report report; + ASSERT_EQ(db()->LookUpCrashReport(uuid, &report), + CrashReportDatabase::kNoError); + EXPECT_EQ(report.total_size, + sizeof(main_report_data) + sizeof(attachment_1_data) + + sizeof(attachment_2_data)); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/crash_report_database_win.cc b/shared/sentry/external/crashpad/client/crash_report_database_win.cc new file mode 100644 index 000000000..6331f6508 --- /dev/null +++ b/shared/sentry/external/crashpad/client/crash_report_database_win.cc @@ -0,0 +1,1041 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "client/crash_report_database.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "base/logging.h" +#include "base/numerics/safe_math.h" +#include "base/strings/utf_string_conversions.h" +#include "client/settings.h" +#include "util/file/directory_reader.h" +#include "util/file/filesystem.h" +#include "util/misc/implicit_cast.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/misc/metrics.h" + +namespace crashpad { + +namespace { + +constexpr wchar_t kReportsDirectory[] = L"reports"; +constexpr wchar_t kMetadataFileName[] = L"metadata"; + +constexpr wchar_t kSettings[] = L"settings.dat"; + +constexpr wchar_t kCrashReportFileExtension[] = L"dmp"; + +constexpr uint32_t kMetadataFileHeaderMagic = 'CPAD'; +constexpr uint32_t kMetadataFileVersion = 1; + +using OperationStatus = CrashReportDatabase::OperationStatus; + +// Helpers --------------------------------------------------------------------- + +// Adds a string to the string table and returns the byte index where it was +// added. +uint32_t AddStringToTable(std::string* string_table, const std::string& str) { + uint32_t offset = base::checked_cast(string_table->size()); + *string_table += str; + *string_table += '\0'; + return offset; +} + +// Converts |str| to UTF8, adds the result to the string table and returns the +// byte index where it was added. +uint32_t AddStringToTable(std::string* string_table, const std::wstring& str) { + return AddStringToTable(string_table, base::WideToUTF8(str)); +} + +// Reads from the current file position to EOF and returns as a string of bytes. +std::string ReadRestOfFileAsString(FileHandle file) { + FileOffset read_from = LoggingSeekFile(file, 0, SEEK_CUR); + FileOffset end = LoggingSeekFile(file, 0, SEEK_END); + FileOffset original = LoggingSeekFile(file, read_from, SEEK_SET); + if (read_from == -1 || end == -1 || original == -1 || read_from == end) + return std::string(); + DCHECK_EQ(read_from, original); + DCHECK_GT(end, read_from); + size_t data_length = static_cast(end - read_from); + std::string buffer(data_length, '\0'); + return LoggingReadFileExactly(file, &buffer[0], data_length) ? buffer + : std::string(); +} + +bool UUIDFromReportPath(const base::FilePath& path, UUID* uuid) { + return uuid->InitializeFromString( + path.RemoveFinalExtension().BaseName().value()); +} + +// Helper structures, and conversions ------------------------------------------ + +// The format of the on disk metadata file is a MetadataFileHeader, followed by +// a number of fixed size records of MetadataFileReportRecord, followed by a +// string table in UTF8 format, where each string is \0 terminated. +struct MetadataFileHeader { + uint32_t magic; + uint32_t version; + uint32_t num_records; + uint32_t padding; +}; + +struct ReportDisk; + +enum class ReportState { + //! \brief Created and filled out by caller, owned by database. + kPending, + //! \brief In the process of uploading, owned by caller. + kUploading, + //! \brief Upload completed or skipped, owned by database. + kCompleted, +}; + +enum { + //! \brief Corresponds to uploaded bit of the report state. + kAttributeUploaded = 1 << 0, + + //! \brief Corresponds to upload_explicity_requested bit of the report state. + kAttributeUploadExplicitlyRequested = 1 << 1, +}; + +struct MetadataFileReportRecord { + // Note that this default constructor does no initialization. It is used only + // to create an array of records that are immediately initialized by reading + // from disk in Metadata::Read(). + MetadataFileReportRecord() {} + + // Constructs from a ReportDisk, adding to |string_table| and storing indices + // as strings into that table. + MetadataFileReportRecord(const ReportDisk& report, std::string* string_table); + + UUID uuid; // UUID is a 16 byte, standard layout structure. + uint32_t file_path_index; // Index into string table. File name is relative + // to the reports directory when on disk. + uint32_t id_index; // Index into string table. + int64_t creation_time; // Holds a time_t. + int64_t last_upload_attempt_time; // Holds a time_t. + int32_t upload_attempts; + int32_t state; // A ReportState. + uint8_t attributes; // Bitfield of kAttribute*. + uint8_t padding[7]; +}; + +//! \brief A private extension of the Report class that includes additional data +//! that's stored on disk in the metadata file. +struct ReportDisk : public CrashReportDatabase::Report { + ReportDisk(const MetadataFileReportRecord& record, + const base::FilePath& report_dir, + const std::string& string_table); + + ReportDisk(const UUID& uuid, + const base::FilePath& path, + time_t creation_tim, + ReportState state); + + //! \brief The current state of the report. + ReportState state; +}; + +MetadataFileReportRecord::MetadataFileReportRecord(const ReportDisk& report, + std::string* string_table) + : uuid(report.uuid), + file_path_index( + AddStringToTable(string_table, report.file_path.BaseName().value())), + id_index(AddStringToTable(string_table, report.id)), + creation_time(report.creation_time), + last_upload_attempt_time(report.last_upload_attempt_time), + upload_attempts(report.upload_attempts), + state(static_cast(report.state)), + attributes((report.uploaded ? kAttributeUploaded : 0) | + (report.upload_explicitly_requested + ? kAttributeUploadExplicitlyRequested + : 0)) { + memset(&padding, 0, sizeof(padding)); +} + +ReportDisk::ReportDisk(const MetadataFileReportRecord& record, + const base::FilePath& report_dir, + const std::string& string_table) + : Report() { + uuid = record.uuid; + file_path = report_dir.Append( + base::UTF8ToWide(&string_table[record.file_path_index])); + id = &string_table[record.id_index]; + creation_time = record.creation_time; + last_upload_attempt_time = record.last_upload_attempt_time; + upload_attempts = record.upload_attempts; + state = static_cast(record.state); + uploaded = (record.attributes & kAttributeUploaded) != 0; + upload_explicitly_requested = + (record.attributes & kAttributeUploadExplicitlyRequested) != 0; +} + +ReportDisk::ReportDisk(const UUID& uuid, + const base::FilePath& path, + time_t creation_time, + ReportState state) + : Report() { + this->uuid = uuid; + this->file_path = path; + this->creation_time = creation_time; + this->state = state; +} + +// Metadata -------------------------------------------------------------------- + +//! \brief Manages the metadata for the set of reports, handling serialization +//! to disk, and queries. +class Metadata { + public: + Metadata(const Metadata&) = delete; + Metadata& operator=(const Metadata&) = delete; + + //! \brief Writes any changes if necessary, unlocks and closes the file + //! handle. + ~Metadata(); + + static std::unique_ptr Create( + const base::FilePath& metadata_file, + const base::FilePath& report_dir, + const base::FilePath& attachments_dir); + + //! \brief Adds a new report to the set. + //! + //! \param[in] new_report_disk The record to add. The #state field must be set + //! to kPending. + void AddNewRecord(const ReportDisk& new_report_disk); + + //! \brief Finds all reports in a given state. The \a reports vector is only + //! valid when CrashReportDatabase::kNoError is returned. + //! + //! \param[in] desired_state The state to match. + //! \param[out] reports Matching reports, must be empty on entry. + OperationStatus FindReports( + ReportState desired_state, + std::vector* reports) const; + + //! \brief Finds the report matching the given UUID. + //! + //! The returned report is only valid if CrashReportDatabase::kNoError is + //! returned. + //! + //! \param[in] uuid The report identifier. + //! \param[out] report_disk The found report, valid only if + //! CrashReportDatabase::kNoError is returned. Ownership is not + //! transferred to the caller, and the report may not be modified. + OperationStatus FindSingleReport(const UUID& uuid, + const ReportDisk** report_disk) const; + + //! \brief Finds a single report matching the given UUID and in the desired + //! state, and returns a mutable ReportDisk* if found. + //! + //! This marks the metadata as dirty, and on destruction, changes will be + //! written to disk via Write(). + //! + //! \return #kNoError on success. #kReportNotFound if there was no report with + //! the specified UUID, or if the report was not in the specified state + //! and was not uploading. #kBusyError if the report was not in the + //! specified state and was uploading. + OperationStatus FindSingleReportAndMarkDirty(const UUID& uuid, + ReportState desired_state, + ReportDisk** report_disk); + + //! \brief Removes a report from the metadata database, without touching the + //! on-disk file. + //! + //! The returned report is only valid if CrashReportDatabase::kNoError is + //! returned. This will mark the database as dirty. Future metadata + //! operations for this report will not succeed. + //! + //! \param[in] uuid The report identifier to remove. + //! \param[out] report_path The found report's file_path, valid only if + //! CrashReportDatabase::kNoError is returned. + OperationStatus DeleteReport(const UUID& uuid, + base::FilePath* report_path); + + //! \brief Removes reports from the metadata database, that don't have + //! corresponding report files. + //! + //! \return number of metadata entries removed + int CleanDatabase(); + + private: + Metadata(FileHandle handle, + const base::FilePath& report_dir, + const base::FilePath& attachments_dir); + + bool Rewind(); + + void Read(); + void Write(); + + //! \brief Confirms that the corresponding report actually exists on disk + //! (that is, the dump file has not been removed), and that the report is + //! in the given state. + static OperationStatus VerifyReport(const ReportDisk& report_disk, + ReportState desired_state); + //! \brief Confirms that the corresponding report actually exists on disk + //! (that is, the dump file has not been removed). + static OperationStatus VerifyReportAnyState(const ReportDisk& report_disk); + + ScopedFileHandle handle_; + const base::FilePath report_dir_; + const base::FilePath attachments_dir_; + bool dirty_; //! \brief `true` when a Write() is required on destruction. + std::vector reports_; +}; + +Metadata::~Metadata() { + if (dirty_) + Write(); + // Not actually async, UnlockFileEx requires the Offset fields. + OVERLAPPED overlapped = {0}; + if (!UnlockFileEx(handle_.get(), 0, MAXDWORD, MAXDWORD, &overlapped)) + PLOG(ERROR) << "UnlockFileEx"; +} + +// static +std::unique_ptr Metadata::Create( + const base::FilePath& metadata_file, + const base::FilePath& report_dir, + const base::FilePath& attachments_dir) { + // It is important that dwShareMode be non-zero so that concurrent access to + // this file results in a successful open. This allows us to get to LockFileEx + // which then blocks to guard access. + FileHandle handle = CreateFile(metadata_file.value().c_str(), + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + nullptr, + OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, + nullptr); + if (handle == kInvalidFileHandle) + return std::unique_ptr(); + // Not actually async, LockFileEx requires the Offset fields. + OVERLAPPED overlapped = {0}; + if (!LockFileEx(handle, + LOCKFILE_EXCLUSIVE_LOCK, + 0, + MAXDWORD, + MAXDWORD, + &overlapped)) { + PLOG(ERROR) << "LockFileEx"; + return std::unique_ptr(); + } + + std::unique_ptr metadata( + new Metadata(handle, report_dir, attachments_dir)); + // If Read() fails, for whatever reason (corruption, etc.) metadata will not + // have been modified and will be in a clean empty state. We continue on and + // return an empty database to hopefully recover. This means that existing + // crash reports have been orphaned. + metadata->Read(); + return metadata; +} + +void Metadata::AddNewRecord(const ReportDisk& new_report_disk) { + DCHECK(new_report_disk.state == ReportState::kPending); + reports_.push_back(new_report_disk); + dirty_ = true; +} + +OperationStatus Metadata::FindReports( + ReportState desired_state, + std::vector* reports) const { + DCHECK(reports->empty()); + for (const auto& report : reports_) { + if (report.state == desired_state && + VerifyReport(report, desired_state) == CrashReportDatabase::kNoError) { + reports->push_back(report); + } + } + return CrashReportDatabase::kNoError; +} + +OperationStatus Metadata::FindSingleReport( + const UUID& uuid, + const ReportDisk** out_report) const { + auto report_iter = std::find_if( + reports_.begin(), reports_.end(), [uuid](const ReportDisk& report) { + return report.uuid == uuid; + }); + if (report_iter == reports_.end()) + return CrashReportDatabase::kReportNotFound; + OperationStatus os = VerifyReportAnyState(*report_iter); + if (os == CrashReportDatabase::kNoError) + *out_report = &*report_iter; + return os; +} + +OperationStatus Metadata::FindSingleReportAndMarkDirty( + const UUID& uuid, + ReportState desired_state, + ReportDisk** report_disk) { + auto report_iter = std::find_if( + reports_.begin(), reports_.end(), [uuid](const ReportDisk& report) { + return report.uuid == uuid; + }); + if (report_iter == reports_.end()) + return CrashReportDatabase::kReportNotFound; + OperationStatus os = VerifyReport(*report_iter, desired_state); + if (os == CrashReportDatabase::kNoError) { + dirty_ = true; + *report_disk = &*report_iter; + } + return os; +} + +OperationStatus Metadata::DeleteReport(const UUID& uuid, + base::FilePath* report_path) { + auto report_iter = std::find_if( + reports_.begin(), reports_.end(), [uuid](const ReportDisk& report) { + return report.uuid == uuid; + }); + if (report_iter == reports_.end()) + return CrashReportDatabase::kReportNotFound; + *report_path = report_iter->file_path; + reports_.erase(report_iter); + dirty_ = true; + return CrashReportDatabase::kNoError; +} + +int Metadata::CleanDatabase() { + int removed = 0; + for (auto report_iter = reports_.begin(); report_iter != reports_.end();) { + if (!IsRegularFile(report_iter->file_path)) { + report_iter = reports_.erase(report_iter); + ++removed; + dirty_ = true; + } else { + ++report_iter; + } + } + return removed; +} + +Metadata::Metadata(FileHandle handle, + const base::FilePath& report_dir, + const base::FilePath& attachments_dir) + : handle_(handle), + report_dir_(report_dir), + attachments_dir_(attachments_dir), + dirty_(false), + reports_() {} + +bool Metadata::Rewind() { + FileOffset result = LoggingSeekFile(handle_.get(), 0, SEEK_SET); + DCHECK_EQ(result, 0); + return result == 0; +} + +void Metadata::Read() { + FileOffset length = LoggingSeekFile(handle_.get(), 0, SEEK_END); + if (length <= 0) // Failed, or empty: Abort. + return; + if (!Rewind()) { + LOG(ERROR) << "failed to rewind to read"; + return; + } + + MetadataFileHeader header; + if (!LoggingReadFileExactly(handle_.get(), &header, sizeof(header))) { + LOG(ERROR) << "failed to read header"; + return; + } + if (header.magic != kMetadataFileHeaderMagic || + header.version != kMetadataFileVersion) { + LOG(ERROR) << "unexpected header"; + return; + } + + base::CheckedNumeric records_size = + base::CheckedNumeric(header.num_records) * + static_cast(sizeof(MetadataFileReportRecord)); + if (!records_size.IsValid()) { + LOG(ERROR) << "record size out of range"; + return; + } + + std::vector reports; + if (header.num_records > 0) { + std::vector records(header.num_records); + if (!LoggingReadFileExactly( + handle_.get(), &records[0], records_size.ValueOrDie())) { + LOG(ERROR) << "failed to read records"; + return; + } + + std::string string_table = ReadRestOfFileAsString(handle_.get()); + if (string_table.empty() || string_table.back() != '\0') { + LOG(ERROR) << "bad string table"; + return; + } + + for (const auto& record : records) { + if (record.file_path_index >= string_table.size() || + record.id_index >= string_table.size()) { + LOG(ERROR) << "invalid string table index"; + return; + } + ReportDisk report_disk(record, report_dir_, string_table); + + report_disk.total_size = GetFileSize(report_disk.file_path); + base::FilePath report_attachment_dir = + attachments_dir_.Append(report_disk.uuid.ToWString()); + report_disk.total_size += GetDirectorySize(report_attachment_dir); + reports.push_back(report_disk); + } + } + reports_.swap(reports); +} + +void Metadata::Write() { + if (!Rewind()) { + LOG(ERROR) << "failed to rewind to write"; + return; + } + + // Truncate to ensure that a partial write doesn't cause a mix of old and new + // data causing an incorrect interpretation on read. + if (!SetEndOfFile(handle_.get())) { + PLOG(ERROR) << "failed to truncate"; + return; + } + + size_t num_records = reports_.size(); + + // Fill and write out the header. + MetadataFileHeader header = {0}; + header.magic = kMetadataFileHeaderMagic; + header.version = kMetadataFileVersion; + header.num_records = base::checked_cast(num_records); + if (!LoggingWriteFile(handle_.get(), &header, sizeof(header))) { + LOG(ERROR) << "failed to write header"; + return; + } + + if (num_records == 0) + return; + + // Build the records and string table we're going to write. + std::string string_table; + std::vector records; + records.reserve(num_records); + for (const auto& report : reports_) { + const base::FilePath& path = report.file_path; + if (path.DirName() != report_dir_) { + LOG(ERROR) << path.value().c_str() << " expected to start with " + << base::WideToUTF8(report_dir_.value()); + return; + } + records.push_back(MetadataFileReportRecord(report, &string_table)); + } + + if (!LoggingWriteFile(handle_.get(), + &records[0], + records.size() * sizeof(MetadataFileReportRecord))) { + LOG(ERROR) << "failed to write records"; + return; + } + if (!LoggingWriteFile( + handle_.get(), string_table.c_str(), string_table.size())) { + LOG(ERROR) << "failed to write string table"; + return; + } +} + +// static +OperationStatus Metadata::VerifyReportAnyState(const ReportDisk& report_disk) { + DWORD fileattr = GetFileAttributes(report_disk.file_path.value().c_str()); + if (fileattr == INVALID_FILE_ATTRIBUTES) + return CrashReportDatabase::kReportNotFound; + return (fileattr & FILE_ATTRIBUTE_DIRECTORY) + ? CrashReportDatabase::kFileSystemError + : CrashReportDatabase::kNoError; +} + +// static +OperationStatus Metadata::VerifyReport(const ReportDisk& report_disk, + ReportState desired_state) { + if (report_disk.state == desired_state) { + return VerifyReportAnyState(report_disk); + } + + return report_disk.state == ReportState::kUploading + ? CrashReportDatabase::kBusyError + : CrashReportDatabase::kReportNotFound; +} + +bool EnsureDirectory(const base::FilePath& path) { + DWORD fileattr = GetFileAttributes(path.value().c_str()); + if (fileattr == INVALID_FILE_ATTRIBUTES) { + PLOG(ERROR) << "GetFileAttributes " << base::WideToUTF8(path.value()); + return false; + } + if ((fileattr & FILE_ATTRIBUTE_DIRECTORY) == 0) { + LOG(ERROR) << "GetFileAttributes " << base::WideToUTF8(path.value()) + << ": not a directory"; + return false; + } + return true; +} + +//! \brief Ensures that the node at path is a directory, and creates it if it +//! does not exist. +//! +//! \return If the path points to a file, rather than a directory, or the +//! directory could not be created, returns `false`. Otherwise, returns +//! `true`, indicating that path already was or now is a directory. +bool CreateDirectoryIfNecessary(const base::FilePath& path) { + if (CreateDirectory(path.value().c_str(), nullptr)) + return true; + if (GetLastError() != ERROR_ALREADY_EXISTS) { + PLOG(ERROR) << "CreateDirectory " << base::WideToUTF8(path.value()); + return false; + } + return EnsureDirectory(path); +} + +} // namespace + +// CrashReportDatabaseWin ------------------------------------------------------ + +class CrashReportDatabaseWin : public CrashReportDatabase { + public: + explicit CrashReportDatabaseWin(const base::FilePath& path); + + CrashReportDatabaseWin(const CrashReportDatabaseWin&) = delete; + CrashReportDatabaseWin& operator=(const CrashReportDatabaseWin&) = delete; + + ~CrashReportDatabaseWin() override; + + bool Initialize(bool may_create); + + // CrashReportDatabase: + Settings* GetSettings() override; + OperationStatus PrepareNewCrashReport( + std::unique_ptr* report) override; + OperationStatus FinishedWritingCrashReport(std::unique_ptr report, + UUID* uuid) override; + OperationStatus LookUpCrashReport(const UUID& uuid, Report* report) override; + OperationStatus GetPendingReports(std::vector* reports) override; + OperationStatus GetCompletedReports(std::vector* reports) override; + OperationStatus GetReportForUploading( + const UUID& uuid, + std::unique_ptr* report, + bool report_metrics) override; + OperationStatus SkipReportUpload(const UUID& uuid, + Metrics::CrashSkippedReason reason) override; + OperationStatus DeleteReport(const UUID& uuid) override; + OperationStatus RequestUpload(const UUID& uuid) override; + int CleanDatabase(time_t lockfile_ttl) override; + base::FilePath DatabasePath() override; + + private: + // CrashReportDatabase: + OperationStatus RecordUploadAttempt(UploadReport* report, + bool successful, + const std::string& id) override; + + // Cleans any attachments that have no associated report. + void CleanOrphanedAttachments(); + + std::unique_ptr AcquireMetadata(); + + base::FilePath base_dir_; + Settings settings_; + InitializationStateDcheck initialized_; +}; + +CrashReportDatabaseWin::CrashReportDatabaseWin(const base::FilePath& path) + : CrashReportDatabase(), base_dir_(path), settings_(), initialized_() {} + +CrashReportDatabaseWin::~CrashReportDatabaseWin() { +} + +bool CrashReportDatabaseWin::Initialize(bool may_create) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + // Ensure the database directory exists. + if (may_create) { + if (!CreateDirectoryIfNecessary(base_dir_)) + return false; + } else if (!EnsureDirectory(base_dir_)) { + return false; + } + + // Ensure that the report subdirectory exists. + if (!CreateDirectoryIfNecessary(base_dir_.Append(kReportsDirectory))) + return false; + + if (!CreateDirectoryIfNecessary(AttachmentsRootPath())) + return false; + + if (!settings_.Initialize(base_dir_.Append(kSettings))) + return false; + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +base::FilePath CrashReportDatabaseWin::DatabasePath() { + return base_dir_; +} + +Settings* CrashReportDatabaseWin::GetSettings() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &settings_; +} + +OperationStatus CrashReportDatabaseWin::PrepareNewCrashReport( + std::unique_ptr* report) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + std::unique_ptr new_report(new NewReport()); + if (!new_report->Initialize(this, + base_dir_.Append(kReportsDirectory), + std::wstring(L".") + kCrashReportFileExtension)) { + return kFileSystemError; + } + + report->reset(new_report.release()); + return kNoError; +} + +OperationStatus CrashReportDatabaseWin::FinishedWritingCrashReport( + std::unique_ptr report, + UUID* uuid) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + std::unique_ptr metadata(AcquireMetadata()); + if (!metadata) + return kDatabaseError; + metadata->AddNewRecord(ReportDisk(report->ReportID(), + report->file_remover_.get(), + time(nullptr), + ReportState::kPending)); + + std::ignore = report->file_remover_.release(); + + // Close all the attachments and disarm their removers too. + for (auto& writer : report->attachment_writers_) { + writer->Close(); + } + for (auto& remover : report->attachment_removers_) { + std::ignore = remover.release(); + } + + *uuid = report->ReportID(); + + Metrics::CrashReportPending(Metrics::PendingReportReason::kNewlyCreated); + Metrics::CrashReportSize(report->Writer()->Seek(0, SEEK_END)); + + return kNoError; +} + +OperationStatus CrashReportDatabaseWin::LookUpCrashReport(const UUID& uuid, + Report* report) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + std::unique_ptr metadata(AcquireMetadata()); + if (!metadata) + return kDatabaseError; + // Find and return a copy of the matching report. + const ReportDisk* report_disk; + OperationStatus os = metadata->FindSingleReport(uuid, &report_disk); + if (os == kNoError) + *report = *report_disk; + return os; +} + +OperationStatus CrashReportDatabaseWin::GetPendingReports( + std::vector* reports) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + std::unique_ptr metadata(AcquireMetadata()); + return metadata ? metadata->FindReports(ReportState::kPending, reports) + : kDatabaseError; +} + +OperationStatus CrashReportDatabaseWin::GetCompletedReports( + std::vector* reports) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + std::unique_ptr metadata(AcquireMetadata()); + return metadata ? metadata->FindReports(ReportState::kCompleted, reports) + : kDatabaseError; +} + +OperationStatus CrashReportDatabaseWin::GetReportForUploading( + const UUID& uuid, + std::unique_ptr* report, + bool report_metrics) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + std::unique_ptr metadata(AcquireMetadata()); + if (!metadata) + return kDatabaseError; + + ReportDisk* report_disk; + OperationStatus os = metadata->FindSingleReportAndMarkDirty( + uuid, ReportState::kPending, &report_disk); + if (os == kNoError) { + report_disk->state = ReportState::kUploading; + auto upload_report = std::make_unique(); + *implicit_cast(upload_report.get()) = *report_disk; + + if (!upload_report->Initialize(upload_report->file_path, this)) { + return kFileSystemError; + } + upload_report->report_metrics_ = report_metrics; + + report->reset(upload_report.release()); + } + return os; +} + +OperationStatus CrashReportDatabaseWin::RecordUploadAttempt( + UploadReport* report, + bool successful, + const std::string& id) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (report->report_metrics_) { + Metrics::CrashUploadAttempted(successful); + } + + std::unique_ptr metadata(AcquireMetadata()); + if (!metadata) + return kDatabaseError; + ReportDisk* report_disk; + OperationStatus os = metadata->FindSingleReportAndMarkDirty( + report->uuid, ReportState::kUploading, &report_disk); + if (os != kNoError) + return os; + + time_t now = time(nullptr); + + report_disk->uploaded = successful; + report_disk->id = id; + report_disk->last_upload_attempt_time = now; + report_disk->upload_attempts++; + if (successful) { + report_disk->state = ReportState::kCompleted; + report_disk->upload_explicitly_requested = false; + } else { + report_disk->state = ReportState::kPending; + report_disk->upload_explicitly_requested = + report->upload_explicitly_requested; + } + + if (!settings_.SetLastUploadAttemptTime(now)) + return kDatabaseError; + + return kNoError; +} + +OperationStatus CrashReportDatabaseWin::DeleteReport(const UUID& uuid) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + std::unique_ptr metadata(AcquireMetadata()); + if (!metadata) + return kDatabaseError; + + base::FilePath report_path; + OperationStatus os = metadata->DeleteReport(uuid, &report_path); + if (os != kNoError) + return os; + + if (!DeleteFile(report_path.value().c_str())) { + PLOG(ERROR) << "DeleteFile " << base::WideToUTF8(report_path.value()); + return kFileSystemError; + } + + RemoveAttachmentsByUUID(uuid); + + return kNoError; +} + +OperationStatus CrashReportDatabaseWin::SkipReportUpload( + const UUID& uuid, + Metrics::CrashSkippedReason reason) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + Metrics::CrashUploadSkipped(reason); + + std::unique_ptr metadata(AcquireMetadata()); + if (!metadata) + return kDatabaseError; + ReportDisk* report_disk; + OperationStatus os = metadata->FindSingleReportAndMarkDirty( + uuid, ReportState::kPending, &report_disk); + if (os == kNoError) { + report_disk->state = ReportState::kCompleted; + report_disk->upload_explicitly_requested = false; + } + return os; +} + +std::unique_ptr CrashReportDatabaseWin::AcquireMetadata() { + base::FilePath metadata_file = base_dir_.Append(kMetadataFileName); + return Metadata::Create(metadata_file, + base_dir_.Append(kReportsDirectory), + AttachmentsRootPath()); +} + +std::unique_ptr InitializeInternal( + const base::FilePath& path, + bool may_create) { + std::unique_ptr database_win( + new CrashReportDatabaseWin(path)); + return database_win->Initialize(may_create) + ? std::move(database_win) + : std::unique_ptr(); +} + +OperationStatus CrashReportDatabaseWin::RequestUpload(const UUID& uuid) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + std::unique_ptr metadata(AcquireMetadata()); + if (!metadata) + return kDatabaseError; + + ReportDisk* report_disk; + // TODO(gayane): Search for the report only once regardless of its state. + OperationStatus os = metadata->FindSingleReportAndMarkDirty( + uuid, ReportState::kCompleted, &report_disk); + if (os == kReportNotFound) { + os = metadata->FindSingleReportAndMarkDirty( + uuid, ReportState::kPending, &report_disk); + } + + if (os != kNoError) + return os; + + // If the crash report has already been uploaded, don't request new upload. + if (report_disk->uploaded) + return kCannotRequestUpload; + + // Mark the crash report as having upload explicitly requested by the user, + // and move it to the pending state. + report_disk->upload_explicitly_requested = true; + report_disk->state = ReportState::kPending; + + Metrics::CrashReportPending(Metrics::PendingReportReason::kUserInitiated); + + return kNoError; +} + +int CrashReportDatabaseWin::CleanDatabase(time_t lockfile_ttl) { + int removed = 0; + const base::FilePath dir_path(base_dir_.Append(kReportsDirectory)); + DirectoryReader reader; + if (!reader.Open(dir_path)) { + return removed; + } + + base::FilePath filename; + DirectoryReader::Result result; + time_t now = time(nullptr); + + std::unique_ptr metadata(AcquireMetadata()); + + // Remove old reports without metadata. + while ((result = reader.NextFile(&filename)) == + DirectoryReader::Result::kSuccess) { + timespec filetime; + const base::FilePath report_path(dir_path.Append(filename)); + if (!FileModificationTime(report_path, &filetime) || + filetime.tv_sec > now - lockfile_ttl) { + continue; + } + + const ReportDisk* report_disk; + UUID uuid; + bool is_uuid = UUIDFromReportPath(report_path, &uuid); + // ignore files whose base name is not uuid + if (!is_uuid) { + continue; + } + OperationStatus os = metadata->FindSingleReport(uuid, &report_disk); + + if (os == OperationStatus::kReportNotFound) { + if (LoggingRemoveFile(report_path)) { + ++removed; + RemoveAttachmentsByUUID(uuid); + } + continue; + } + } + + // Remove any metadata records without report files. + removed += metadata->CleanDatabase(); + + CleanOrphanedAttachments(); + return removed; +} + +void CrashReportDatabaseWin::CleanOrphanedAttachments() { + base::FilePath root_attachments_dir = AttachmentsRootPath(); + DirectoryReader reader; + if (!reader.Open(root_attachments_dir)) { + return; + } + + base::FilePath filename; + DirectoryReader::Result result; + base::FilePath reports_dir = base_dir_.Append(kReportsDirectory); + while ((result = reader.NextFile(&filename)) == + DirectoryReader::Result::kSuccess) { + const base::FilePath path(root_attachments_dir.Append(filename)); + if (IsDirectory(path, false)) { + UUID uuid; + if (!uuid.InitializeFromString(filename.value())) { + LOG(ERROR) << "unexpected attachment dir name " + << filename.value().c_str(); + continue; + } + + // Remove attachments if corresponding report doen't exist. + base::FilePath report_path = + reports_dir.Append(uuid.ToWString() + kCrashReportFileExtension); + if (!IsRegularFile(report_path)) { + RemoveAttachmentsByUUID(uuid); + } + } + } +} + +// static +std::unique_ptr CrashReportDatabase::Initialize( + const base::FilePath& path) { + return InitializeInternal(path, true); +} + +// static +std::unique_ptr +CrashReportDatabase::InitializeWithoutCreating(const base::FilePath& path) { + return InitializeInternal(path, false); +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/crashpad_client.h b/shared/sentry/external/crashpad/client/crashpad_client.h new file mode 100644 index 000000000..e8d6b06e1 --- /dev/null +++ b/shared/sentry/external/crashpad/client/crashpad_client.h @@ -0,0 +1,781 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_CLIENT_CRASHPAD_CLIENT_H_ +#define CRASHPAD_CLIENT_CRASHPAD_CLIENT_H_ + +#include +#include +#include +#include + +#include + +#include "base/files/file_path.h" +#include "build/build_config.h" +#include "build/chromeos_buildflags.h" +#include "util/file/file_io.h" +#include "util/misc/capture_context.h" + +#if BUILDFLAG(IS_APPLE) +#include "base/mac/scoped_mach_port.h" +#elif BUILDFLAG(IS_WIN) +#include +#include "util/win/scoped_handle.h" +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) +#include +#include +#endif + +namespace crashpad { + +//! \brief The primary interface for an application to have Crashpad monitor +//! it for crashes. +class CrashpadClient { + public: + CrashpadClient(); + + CrashpadClient(const CrashpadClient&) = delete; + CrashpadClient& operator=(const CrashpadClient&) = delete; + + ~CrashpadClient(); + + //! \brief Starts a Crashpad handler process, performing any necessary + //! handshake to configure it. + //! + //! This method directs crashes to the Crashpad handler. On macOS, this is + //! applicable to this process and all subsequent child processes. On Windows, + //! child processes must also register by using SetHandlerIPCPipe(). + //! + //! On macOS, this method starts a Crashpad handler and obtains a Mach send + //! right corresponding to a receive right held by the handler process. The + //! handler process runs an exception server on this port. This method sets + //! the task’s exception port for `EXC_CRASH`, `EXC_RESOURCE`, and `EXC_GUARD` + //! exceptions to the Mach send right obtained. The handler will be installed + //! with behavior `EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES` and thread + //! state flavor `MACHINE_THREAD_STATE`. Exception ports are inherited, so a + //! Crashpad handler started here will remain the handler for any child + //! processes created after StartHandler() is called. These child processes do + //! not need to call StartHandler() or be aware of Crashpad in any way. The + //! Crashpad handler will receive crashes from child processes that have + //! inherited it as their exception handler even after the process that called + //! StartHandler() exits. + //! + //! On Windows, if \a asynchronous_start is `true`, this function will not + //! directly call `CreateProcess()`, making it suitable for use in a + //! `DllMain()`. In that case, the handler is started from a background + //! thread, deferring the handler's startup. Nevertheless, regardless of the + //! value of \a asynchronous_start, after calling this method, the global + //! unhandled exception filter is set up, and all crashes will be handled by + //! Crashpad. Optionally, use WaitForHandlerStart() to join with the + //! background thread and retrieve the status of handler startup. + //! + //! On Fuchsia, this method binds to the exception port of the current default + //! job, and starts a Crashpad handler to monitor that port. + //! + //! On Linux, this method starts a Crashpad handler, connected to this process + //! via an `AF_UNIX` socket pair and installs signal handlers to request crash + //! dumps on the client's socket end. + //! + //! \param[in] handler The path to a Crashpad handler executable. + //! \param[in] database The path to a Crashpad database. The handler will be + //! started with this path as its `--database` argument. + //! \param[in] metrics_dir The path to an already existing directory where + //! metrics files can be stored. The handler will be started with this + //! path as its `--metrics-dir` argument. + //! \param[in] url The URL of an upload server. The handler will be started + //! with this URL as its `--url` argument. + //! \param[in] annotations Process annotations to set in each crash report. + //! The handler will be started with an `--annotation` argument for each + //! element in this map. + //! \param[in] arguments Additional arguments to pass to the Crashpad handler. + //! Arguments passed in other parameters and arguments required to perform + //! the handshake are the responsibility of this method, and must not be + //! specified in this parameter. + //! \param[in] restartable If `true`, the handler will be restarted if it + //! dies, if this behavior is supported. This option is not available on + //! all platforms, and does not function on all OS versions. If it is + //! not supported, it will be ignored. + //! \param[out] asynchronous_start If `true`, the handler will be started from + //! a background thread. Optionally, WaitForHandlerStart() can be used at + //! a suitable time to retreive the result of background startup. This + //! option is only used on Windows. + //! \param[in] attachments Vector that stores file paths that should be + //! captured with each report at the time of the crash. + //! + //! \return `true` on success, `false` on failure with a message logged. + bool StartHandler(const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments, + bool restartable, + bool asynchronous_start, + const std::vector& attachments = {}); + +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \ + DOXYGEN + //! \brief Retrieve the socket and process ID for the handler. + //! + //! `StartHandler()` must have successfully been called before calling this + //! method. + //! + //! \param[out] sock The socket connected to the handler, if not `nullptr`. + //! \param[out] pid The handler's process ID, if not `nullptr`. + //! \return `true` on success. Otherwise `false` with a message logged. + static bool GetHandlerSocket(int* sock, pid_t* pid); + + //! \brief Sets the socket to a presumably-running Crashpad handler process + //! which was started with StartHandler(). + //! + //! This method installs a signal handler to request crash dumps on \a sock. + //! + //! \param[in] sock A socket connected to a Crashpad handler. + //! \param[in] pid The process ID of the handler, used to set the handler as + //! this process' ptracer. 0 indicates it is not necessary to set the + //! handler as this process' ptracer. -1 indicates that the handler's + //! process ID should be determined by communicating over the socket. + bool SetHandlerSocket(ScopedFileHandle sock, pid_t pid); + + //! \brief Uses `sigaltstack()` to allocate a signal stack for the calling + //! thread. + //! + //! This method allocates an alternate stack to handle signals delivered to + //! the calling thread and should be called early in the lifetime of each + //! thread. Installing an alternate stack allows signals to be delivered in + //! the event that the call stack's stack pointer points to invalid memory, + //! as in the case of stack overflow. + //! + //! This method is called automatically by SetHandlerSocket() and + //! the various StartHandler() methods. It is harmless to call multiple times. + //! A new signal stack will be allocated only if there is no existing stack or + //! the existing stack is too small. The stack will be automatically freed + //! when the thread exits. + //! + //! An application might choose to diligently call this method from the start + //! routine for each thread, call it from a `pthread_create()` wrapper which + //! the application provides, or link the provided "client:pthread_create" + //! target. + //! + //! \return `true` on success. Otherwise `false` with a message logged. + static bool InitializeSignalStackForThread(); +#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || + // BUILDFLAG(IS_CHROMEOS) || DOXYGEN + +#if BUILDFLAG(IS_ANDROID) || DOXYGEN + //! \brief Installs a signal handler to execute `/system/bin/app_process` and + //! load a Java class in response to a crash. + //! + //! \param[in] class_name The fully qualified class name to load, which must + //! define a `main()` method to be invoked by `app_process`. Arguments + //! will be passed to this method as though it were the Crashpad handler. + //! This class is expected to load a native library defining + //! crashpad::HandlerMain() and pass the arguments to it. + //! \param[in] env A vector of environment variables of the form `var=value` + //! defining the environment in which to execute `app_process`. If this + //! value is `nullptr`, the application's environment at the time of the + //! crash will be used. + //! \param[in] database The path to a Crashpad database. The handler will be + //! started with this path as its `--database` argument. + //! \param[in] metrics_dir The path to an already existing directory where + //! metrics files can be stored. The handler will be started with this + //! path as its `--metrics-dir` argument. + //! \param[in] url The URL of an upload server. The handler will be started + //! with this URL as its `--url` argument. + //! \param[in] annotations Process annotations to set in each crash report. + //! The handler will be started with an `--annotation` argument for each + //! element in this map. + //! \param[in] arguments Additional arguments to pass to the Crashpad handler. + //! Arguments passed in other parameters and arguments required to perform + //! the handshake are the responsibility of this method, and must not be + //! specified in this parameter. + //! + //! \return `true` on success, `false` on failure with a message logged. + bool StartJavaHandlerAtCrash( + const std::string& class_name, + const std::vector* env, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments); + + //! \brief Executes `/system/bin/app_process` and loads a Java class. + //! + //! \param[in] class_name The fully qualified class name to load, which must + //! define a `main()` method to be invoked by `app_process`. Arguments + //! will be passed to this method as though it were the Crashpad handler. + //! This class is expected to load a native library defining + //! crashpad::HandlerMain() and pass the arguments to it. + //! \param[in] env A vector of environment variables of the form `var=value` + //! defining the environment in which to execute `app_process`. If this + //! value is `nullptr`, the application's current environment will be + //! used. + //! \param[in] database The path to a Crashpad database. The handler will be + //! started with this path as its `--database` argument. + //! \param[in] metrics_dir The path to an already existing directory where + //! metrics files can be stored. The handler will be started with this + //! path as its `--metrics-dir` argument. + //! \param[in] url The URL of an upload server. The handler will be started + //! with this URL as its `--url` argument. + //! \param[in] annotations Process annotations to set in each crash report. + //! The handler will be started with an `--annotation` argument for each + //! element in this map. + //! \param[in] arguments Additional arguments to pass to the Crashpad handler. + //! Arguments passed in other parameters and arguments required to perform + //! the handshake are the responsibility of this method, and must not be + //! specified in this parameter. + //! \param[in] socket The server end of a socket pair. The client end should + //! be used with an ExceptionHandlerClient. + //! + //! \return `true` on success, `false` on failure with a message logged. + static bool StartJavaHandlerForClient( + const std::string& class_name, + const std::vector* env, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments, + int socket); + + //! \brief Installs a signal handler to start a Crashpad handler process by + //! loading it with `/system/bin/linker`. + //! + //! This method is only supported by Android Q+. + //! + //! \param[in] handler_trampoline The path to a Crashpad handler trampoline + //! executable, possibly located within an apk, e.g. + //! "/data/app/myapk.apk!/myabi/libcrashpad_handler_trampoline.so". + //! \param[in] handler_library The name of a library exporting the symbol + //! `CrashpadHandlerMain()`. The path to this library must be present in + //! `LD_LIBRARY_PATH`. + //! \param[in] is_64_bit `true` if \a handler_trampoline and \a + //! handler_library are 64-bit objects. They must have the same bitness. + //! \param[in] env A vector of environment variables of the form `var=value` + //! defining the environment in which to execute `app_process`. If this + //! value is `nullptr`, the application's environment at the time of the + //! crash will be used. + //! \param[in] database The path to a Crashpad database. The handler will be + //! started with this path as its `--database` argument. + //! \param[in] metrics_dir The path to an already existing directory where + //! metrics files can be stored. The handler will be started with this + //! path as its `--metrics-dir` argument. + //! \param[in] url The URL of an upload server. The handler will be started + //! with this URL as its `--url` argument. + //! \param[in] annotations Process annotations to set in each crash report. + //! The handler will be started with an `--annotation` argument for each + //! element in this map. + //! \param[in] arguments Additional arguments to pass to the Crashpad handler. + //! Arguments passed in other parameters and arguments required to perform + //! the handshake are the responsibility of this method, and must not be + //! specified in this parameter. + //! + //! \return `true` on success, `false` on failure with a message logged. + bool StartHandlerWithLinkerAtCrash( + const std::string& handler_trampoline, + const std::string& handler_library, + bool is_64_bit, + const std::vector* env, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments); + + //! \brief Starts a Crashpad handler process with an initial client by loading + //! it with `/system/bin/linker`. + //! + //! This method is only supported by Android Q+. + //! + //! \param[in] handler_trampoline The path to a Crashpad handler trampoline + //! executable, possibly located within an apk, e.g. + //! "/data/app/myapk.apk!/myabi/libcrashpad_handler_trampoline.so". + //! \param[in] handler_library The name of a library exporting the symbol + //! `CrashpadHandlerMain()`. The path to this library must be present in + //! `LD_LIBRARY_PATH`. + //! \param[in] is_64_bit `true` if \a handler_trampoline and \a + //! handler_library are 64-bit objects. They must have the same bitness. + //! \param[in] env A vector of environment variables of the form `var=value` + //! defining the environment in which to execute `app_process`. If this + //! value is `nullptr`, the application's current environment will be + //! used. + //! \param[in] database The path to a Crashpad database. The handler will be + //! started with this path as its `--database` argument. + //! \param[in] metrics_dir The path to an already existing directory where + //! metrics files can be stored. The handler will be started with this + //! path as its `--metrics-dir` argument. + //! \param[in] url The URL of an upload server. The handler will be started + //! with this URL as its `--url` argument. + //! \param[in] annotations Process annotations to set in each crash report. + //! The handler will be started with an `--annotation` argument for each + //! element in this map. + //! \param[in] arguments Additional arguments to pass to the Crashpad handler. + //! Arguments passed in other parameters and arguments required to perform + //! the handshake are the responsibility of this method, and must not be + //! specified in this parameter. + //! \param[in] socket The server end of a socket pair. The client end should + //! be used with an ExceptionHandlerClient. + //! + //! \return `true` on success, `false` on failure with a message logged. + static bool StartHandlerWithLinkerForClient( + const std::string& handler_trampoline, + const std::string& handler_library, + bool is_64_bit, + const std::vector* env, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments, + int socket); +#endif // BUILDFLAG(IS_ANDROID) || DOXYGEN + +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS) || \ + DOXYGEN + //! \brief Installs a signal handler to launch a handler process in reponse to + //! a crash. + //! + //! The handler process will create a crash dump for this process and exit. + //! + //! \param[in] handler The path to a Crashpad handler executable. + //! \param[in] database The path to a Crashpad database. The handler will be + //! started with this path as its `--database` argument. + //! \param[in] metrics_dir The path to an already existing directory where + //! metrics files can be stored. The handler will be started with this + //! path as its `--metrics-dir` argument. + //! \param[in] url The URL of an upload server. The handler will be started + //! with this URL as its `--url` argument. + //! \param[in] annotations Process annotations to set in each crash report. + //! The handler will be started with an `--annotation` argument for each + //! element in this map. + //! \param[in] arguments Additional arguments to pass to the Crashpad handler. + //! Arguments passed in other parameters and arguments required to perform + //! the handshake are the responsibility of this method, and must not be + //! specified in this parameter. + //! + //! \return `true` on success, `false` on failure with a message logged. + bool StartHandlerAtCrash( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments, + const std::vector& attachments = {}); + + //! \brief Starts a handler process with an initial client. + //! + //! This method allows a process to launch the handler process on behalf of + //! another process. + //! + //! \param[in] handler The path to a Crashpad handler executable. + //! \param[in] database The path to a Crashpad database. The handler will be + //! started with this path as its `--database` argument. + //! \param[in] metrics_dir The path to an already existing directory where + //! metrics files can be stored. The handler will be started with this + //! path as its `--metrics-dir` argument. + //! \param[in] url The URL of an upload server. The handler will be started + //! with this URL as its `--url` argument. + //! \param[in] annotations Process annotations to set in each crash report. + //! The handler will be started with an `--annotation` argument for each + //! element in this map. + //! \param[in] arguments Additional arguments to pass to the Crashpad handler. + //! Arguments passed in other parameters and arguments required to perform + //! the handshake are the responsibility of this method, and must not be + //! specified in this parameter. + //! \param[in] socket The server end of a socket pair. The client end should + //! be used with an ExceptionHandlerClient. + //! + //! \return `true` on success, `false` on failure with a message logged. + static bool StartHandlerForClient( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments, + int socket); + + //! \brief Requests that the handler capture a dump even though there hasn't + //! been a crash. + //! + //! A handler must have already been installed before calling this method. + //! + //! TODO(jperaza): Floating point information in the context is zeroed out + //! until CaptureContext() supports collecting that information. + //! + //! \param[in] context A NativeCPUContext, generally captured by + //! CaptureContext() or similar. + static void DumpWithoutCrash(NativeCPUContext* context); + + //! \brief Disables any installed crash handler, not including any + //! FirstChanceHandler and crashes the current process. + //! + //! \param[in] message A message to be logged before crashing. + static void CrashWithoutDump(const std::string& message); + + //! \brief The type for custom handlers installed by clients. + using FirstChanceHandlerLinux = bool (*)(int, siginfo_t*, ucontext_t*); + + //! \brief Installs a custom crash signal handler which runs before the + //! currently installed Crashpad handler. + //! + //! Handling signals appropriately can be tricky and use of this method + //! should be avoided, if possible. + //! + //! A handler must have already been installed before calling this method. + //! + //! The custom handler runs in a signal handler context and must be safe for + //! that purpose. + //! + //! If the custom handler returns `true`, the signal is considered handled and + //! the signal handler returns. Otherwise, the currently installed Crashpad + //! signal handler is run. + //! + //! \param[in] handler The custom crash signal handler to install. + static void SetFirstChanceExceptionHandler(FirstChanceHandlerLinux handler); + + //! \brief Configures a set of signals that shouldn't have Crashpad signal + //! handlers installed. + //! + //! This method should be called before calling StartHandler(), + //! SetHandlerSocket(), or other methods that install Crashpad signal + //! handlers. + //! + //! \param[in] unhandled_signals The set of unhandled signals + void SetUnhandledSignals(const std::set& unhandled_signals); +#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_ANDROID) || + // BUILDFLAG(IS_CHROMEOS) || DOXYGEN + +#if BUILDFLAG(IS_IOS) || DOXYGEN + //! \brief Configures the process to direct its crashes to the iOS in-process + //! Crashpad handler. + //! + //! This method is only defined on iOS. + //! + //! \param[in] database The path to a Crashpad database. + //! \param[in] url The URL of an upload server. + //! \param[in] annotations Process annotations to set in each crash report. + //! \return `true` on success, `false` on failure with a message logged. + static bool StartCrashpadInProcessHandler( + const base::FilePath& database, + const std::string& url, + const std::map& annotations); + + //! \brief Requests that the handler convert intermediate dumps into + //! minidumps and trigger an upload if possible. + //! + //! A handler must have already been installed before calling this method. + //! This method should be called when an application is ready to start + //! processing previously created intermediate dumps. Processing will block, + //! so this should not be called on the main UI thread. No intermediate dumps + //! will be processed until this method is called. + //! + //! \param[in] annotations Process annotations to set in each crash report. + //! Useful when adding crash annotations detected on the next run after a + //! crash but before upload. + static void ProcessIntermediateDumps( + const std::map& annotations = {}); + + //! \brief Requests that the handler convert a single intermediate dump at \a + //! file generated by DumpWithoutCrashAndDeferProcessingAtPath into a + //! minidump and trigger an upload if possible. + //! + //! A handler must have already been installed before calling this method. + //! This method should be called when an application is ready to start + //! processing previously created intermediate dumps. Processing will block, + //! so this should not be called on the main UI thread. + //! + //! \param[in] file The intermediate dump to process. + //! \param[in] annotations Process annotations to set in each crash report. + //! Useful when adding crash annotations detected on the next run after a + //! crash but before upload. + static void ProcessIntermediateDump( + const base::FilePath& file, + const std::map& annotations = {}); + + //! \brief Requests that the handler begin in-process uploading of any + //! pending reports. + //! + //! Once called the handler will start looking for pending reports to upload + //! on another thread. This method does not block. + //! + //! A handler must have already been installed before calling this method. + static void StartProcessingPendingReports(); + + //! \brief Requests that the handler capture an intermediate dump even though + //! there hasn't been a crash. The intermediate dump will be converted + //! to a mindump immediately. If StartProcessingPendingReports() has been + //! called, this will also trigger an upload. + //! + //! For internal use only. Clients should use CRASHPAD_SIMULATE_CRASH(). + //! + //! A handler must have already been installed before calling this method. + //! + //! \param[in] context A NativeCPUContext, generally captured by + //! CaptureContext() or similar. + static void DumpWithoutCrash(NativeCPUContext* context); + + //! \brief Requests that the handler capture an intermediate dump even though + //! there hasn't been a crash. The intermediate dump will not be converted + //! to a mindump until ProcessIntermediateDumps() is called. + //! + //! For internal use only. Clients should use + //! CRASHPAD_SIMULATE_CRASH_AND_DEFER_PROCESSING(). + //! + //! A handler must have already been installed before calling this method. + //! + //! \param[in] context A NativeCPUContext, generally captured by + //! CaptureContext() or similar. + static void DumpWithoutCrashAndDeferProcessing(NativeCPUContext* context); + + //! \brief Requests that the handler capture an intermediate dump and store it + //! in path, even though there hasn't been a crash. The intermediate dump + //! will not be converted to a mindump until ProcessIntermediateDump() is + //! called. + //! + //! For internal use only. Clients should use + //! CRASHPAD_SIMULATE_CRASH_AND_DEFER_PROCESSING_AT_PATH(). + //! + //! A handler must have already been installed before calling this method. + //! + //! \param[in] context A NativeCPUContext, generally captured by + //! CaptureContext() or similar. + //! \param[in] path The path for writing the intermediate dump. + static void DumpWithoutCrashAndDeferProcessingAtPath( + NativeCPUContext* context, + const base::FilePath path); + + //! \brief Unregister the Crashpad client. Intended to be used by tests so + //! multiple Crashpad clients can be started and stopped. Not expected to + //! be used in a shipping application. + static void ResetForTesting(); + + //! \brief Inject a callback into Mach handling. Intended to be used by + //! tests to trigger a reentrant exception. + static void SetMachExceptionCallbackForTesting(void (*callback)()); + + //! \brief Returns the thread id of the Mach exception thread, used by tests. + static uint64_t GetThreadIdForTesting(); +#endif + +#if BUILDFLAG(IS_APPLE) || DOXYGEN + //! \brief Sets the process’ crash handler to a Mach service registered with + //! the bootstrap server. + //! + //! This method is only defined on macOS. + //! + //! See StartHandler() for more detail on how the port and handler are + //! configured. + //! + //! \param[in] service_name The service name of a Crashpad exception handler + //! service previously registered with the bootstrap server. + //! + //! \return `true` on success, `false` on failure with a message logged. + bool SetHandlerMachService(const std::string& service_name); + + //! \brief Sets the process’ crash handler to a Mach port. + //! + //! This method is only defined on macOS. + //! + //! See StartHandler() for more detail on how the port and handler are + //! configured. + //! + //! \param[in] exception_port An `exception_port_t` corresponding to a + //! Crashpad exception handler service. + //! + //! \return `true` on success, `false` on failure with a message logged. + bool SetHandlerMachPort(base::mac::ScopedMachSendRight exception_port); + + //! \brief Retrieves a send right to the process’ crash handler Mach port. + //! + //! This method is only defined on macOS. + //! + //! This method can be used to obtain the crash handler Mach port when a + //! Crashpad client process wishes to provide a send right to this port to + //! another process. The IPC mechanism used to convey the right is under the + //! application’s control. If the other process wishes to become a client of + //! the same crash handler, it can provide the transferred right to + //! SetHandlerMachPort(). + //! + //! See StartHandler() for more detail on how the port and handler are + //! configured. + //! + //! \return The Mach port set by SetHandlerMachPort(), possibly indirectly by + //! a call to another method such as StartHandler() or + //! SetHandlerMachService(). This method must only be called after a + //! successful call to one of those methods. `MACH_PORT_NULL` on failure + //! with a message logged. + base::mac::ScopedMachSendRight GetHandlerMachPort() const; +#endif + +#if BUILDFLAG(IS_WIN) || DOXYGEN + //! \brief The type for custom handlers installed by clients. + using FirstChanceHandlerWin = bool (*)(EXCEPTION_POINTERS*); + + //! \brief Installs a custom unhandled exception filter which runs before the + //! currently installed Crashpad handler. + //! + //! Handling exceptions appropriately can be tricky and use of this method + //! should be avoided, if possible. + //! + //! A handler must have already been installed before calling this method. + //! + //! The custom handler runs in an unhandled exception filter context and must + //! be safe for that purpose. + //! + //! If the custom handler returns `true`, the exception is considered handled + //! and the handler returns. Otherwise, the currently installed Crashpad + //! unhandled exception handler is run. + //! + //! \param[in] handler The custom unhandled exception handler to install. + static void SetFirstChanceExceptionHandler(FirstChanceHandlerWin handler); + + //! \brief Sets the IPC pipe of a presumably-running Crashpad handler process + //! which was started with StartHandler() or by other compatible means + //! and does an IPC message exchange to register this process with the + //! handler. Crashes will be serviced once this method returns. + //! + //! This method is only defined on Windows. + //! + //! This method sets the unhandled exception handler to a local + //! function that when reached will "signal and wait" for the crash handler + //! process to create the dump. + //! + //! \param[in] ipc_pipe The full name of the crash handler IPC pipe. This is + //! a string of the form `"\\.\pipe\NAME"`. + //! + //! \return `true` on success and `false` on failure. + bool SetHandlerIPCPipe(const std::wstring& ipc_pipe); + + //! \brief Retrieves the IPC pipe name used to register with the Crashpad + //! handler. + //! + //! This method is only defined on Windows. + //! + //! This method retrieves the IPC pipe name set by SetHandlerIPCPipe(), or a + //! suitable IPC pipe name chosen by StartHandler(). It must only be called + //! after a successful call to one of those methods. It is intended to be used + //! to obtain the IPC pipe name so that it may be passed to other processes, + //! so that they may register with an existing Crashpad handler by calling + //! SetHandlerIPCPipe(). + //! + //! \return The full name of the crash handler IPC pipe, a string of the form + //! `"\\.\pipe\NAME"`. + std::wstring GetHandlerIPCPipe() const; + + //! \brief When `asynchronous_start` is used with StartHandler(), this method + //! can be used to block until the handler launch has been completed to + //! retrieve status information. + //! + //! This method should not be used unless `asynchronous_start` was `true`. + //! + //! \param[in] timeout_ms The number of milliseconds to wait for a result from + //! the background launch, or `0xffffffff` to block indefinitely. + //! + //! \return `true` if the hander startup succeeded, `false` otherwise, and an + //! error message will have been logged. + bool WaitForHandlerStart(unsigned int timeout_ms); + + //! \brief Requests that the handler capture a dump even though there hasn't + //! been a crash. + //! + //! \param[in] context A `CONTEXT`, generally captured by CaptureContext() or + //! similar. + static void DumpWithoutCrash(const CONTEXT& context); + + //! \brief Requests that the handler capture a dump using the given \a + //! exception_pointers to get the `EXCEPTION_RECORD` and `CONTEXT`. + //! + //! This function is not necessary in general usage as an unhandled exception + //! filter is installed by StartHandler() or SetHandlerIPCPipe(). + //! + //! \param[in] exception_pointers An `EXCEPTION_POINTERS`, as would generally + //! passed to an unhandled exception filter. + static void DumpAndCrash(EXCEPTION_POINTERS* exception_pointers); + + //! \brief Requests that the handler capture a dump of a different process. + //! + //! The target process must be an already-registered Crashpad client. An + //! exception will be triggered in the target process, and the regular dump + //! mechanism used. This function will block until the exception in the target + //! process has been handled by the Crashpad handler. + //! + //! This function is unavailable when running on Windows XP and will return + //! `false`. + //! + //! \param[in] process A `HANDLE` identifying the process to be dumped. + //! \param[in] blame_thread If non-null, a `HANDLE` valid in the caller's + //! process, referring to a thread in the target process. If this is + //! supplied, instead of the exception referring to the location where the + //! exception was injected, an exception record will be fabricated that + //! refers to the current location of the given thread. + //! \param[in] exception_code If \a blame_thread is non-null, this will be + //! used as the exception code in the exception record. + //! + //! \return `true` if the exception was triggered successfully. + static bool DumpAndCrashTargetProcess(HANDLE process, + HANDLE blame_thread, + DWORD exception_code); +#endif + +#if BUILDFLAG(IS_APPLE) || DOXYGEN + //! \brief Configures the process to direct its crashes to the default handler + //! for the operating system. + //! + //! On macOS, this sets the task’s exception port as in SetHandlerMachPort(), + //! but the exception handler used is obtained from + //! SystemCrashReporterHandler(). If the system’s crash reporter handler + //! cannot be determined or set, the task’s exception ports for crash-type + //! exceptions are cleared. + //! + //! Use of this function is strongly discouraged. + //! + //! \warning After a call to this function, Crashpad will no longer monitor + //! the process for crashes until a subsequent call to + //! SetHandlerMachPort(). + //! + //! \note This is provided as a static function to allow it to be used in + //! situations where a CrashpadClient object is not otherwise available. + //! This may be useful when a child process inherits its parent’s Crashpad + //! handler, but wants to sever this tie. + static void UseSystemDefaultHandler(); +#endif + +#if BUILDFLAG(IS_CHROMEOS_ASH) + //! \brief Sets a timestamp on the signal handler to be passed on to + //! crashpad_handler and then eventually Chrome OS's crash_reporter. + //! + //! \note This method is used by clients that use `StartHandler()` to start + //! a handler and not by clients that use any other handler starting + //! methods. + static void SetCrashLoopBefore(uint64_t crash_loop_before_time); +#endif + + private: +#if BUILDFLAG(IS_APPLE) + base::mac::ScopedMachSendRight exception_port_; +#elif BUILDFLAG(IS_WIN) + std::wstring ipc_pipe_; + ScopedKernelHANDLE handler_start_thread_; +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) + std::set unhandled_signals_; +#endif // BUILDFLAG(IS_APPLE) +}; + +} // namespace crashpad + +#endif // CRASHPAD_CLIENT_CRASHPAD_CLIENT_H_ diff --git a/shared/sentry/external/crashpad/client/crashpad_client_fuchsia.cc b/shared/sentry/external/crashpad/client/crashpad_client_fuchsia.cc new file mode 100644 index 000000000..46afe0031 --- /dev/null +++ b/shared/sentry/external/crashpad/client/crashpad_client_fuchsia.cc @@ -0,0 +1,105 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "client/crashpad_client.h" + +#include +#include +#include +#include +#include + +#include "base/fuchsia/fuchsia_logging.h" +#include "base/logging.h" +#include "client/client_argv_handling.h" + +namespace crashpad { + +CrashpadClient::CrashpadClient() {} + +CrashpadClient::~CrashpadClient() {} + +bool CrashpadClient::StartHandler( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments, + bool restartable, + bool asynchronous_start, + const std::vector& attachments) { + DCHECK(attachments.empty()); // Attachments are not implemented on Fuchsia yet. + DCHECK_EQ(restartable, false); // Not used on Fuchsia. + DCHECK_EQ(asynchronous_start, false); // Not used on Fuchsia. + + std::vector argv_strings = BuildHandlerArgvStrings( + handler, database, metrics_dir, url, annotations, arguments); + + std::vector argv; + StringVectorToCStringVector(argv_strings, &argv); + + // Set up handles to send to the spawned process: + // 0. PA_USER0 job + // 1. PA_USER0 exception channel + // + // Currently it is assumed that this process's default job handle is the + // exception channel that should be monitored. In the future, it might be + // useful for this to be configurable by the client. + zx::job job; + zx_status_t status = + zx::job::default_job()->duplicate(ZX_RIGHT_SAME_RIGHTS, &job); + if (status != ZX_OK) { + ZX_LOG(ERROR, status) << "zx_handle_duplicate"; + return false; + } + + zx::channel exception_channel; + status = job.create_exception_channel(0, &exception_channel); + if (status != ZX_OK) { + ZX_LOG(ERROR, status) << "zx_task_create_exception_channel"; + return false; + } + + constexpr size_t kActionCount = 2; + fdio_spawn_action_t actions[] = { + {.action = FDIO_SPAWN_ACTION_ADD_HANDLE, + .h = {.id = PA_HND(PA_USER0, 0), .handle = job.release()}}, + {.action = FDIO_SPAWN_ACTION_ADD_HANDLE, + .h = {.id = PA_HND(PA_USER0, 1), .handle = exception_channel.release()}}, + }; + + char error_message[FDIO_SPAWN_ERR_MSG_MAX_LENGTH]; + zx::process child; + // TODO(scottmg): https://crashpad.chromium.org/bug/196, FDIO_SPAWN_CLONE_ALL + // is useful during bringup, but should probably be made minimal for real + // usage. + status = fdio_spawn_etc(ZX_HANDLE_INVALID, + FDIO_SPAWN_CLONE_ALL, + argv[0], + argv.data(), + nullptr, + kActionCount, + actions, + child.reset_and_get_address(), + error_message); + if (status != ZX_OK) { + ZX_LOG(ERROR, status) << "fdio_spawn_etc: " << error_message; + return false; + } + + return true; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/crashpad_client_ios.cc b/shared/sentry/external/crashpad/client/crashpad_client_ios.cc new file mode 100644 index 000000000..2a737f3cc --- /dev/null +++ b/shared/sentry/external/crashpad/client/crashpad_client_ios.cc @@ -0,0 +1,475 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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 "client/crashpad_client.h" + +#include +#include + +#include +#include + +#include "base/logging.h" +#include "base/mac/mach_logging.h" +#include "base/mac/scoped_mach_port.h" +#include "client/ios_handler/exception_processor.h" +#include "client/ios_handler/in_process_handler.h" +#include "util/ios/raw_logging.h" +#include "util/mach/exc_server_variants.h" +#include "util/mach/exception_ports.h" +#include "util/mach/mach_extensions.h" +#include "util/mach/mach_message.h" +#include "util/mach/mach_message_server.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/posix/signals.h" +#include "util/thread/thread.h" + +namespace { + +bool IsBeingDebugged() { + kinfo_proc kern_proc_info; + int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()}; + size_t len = sizeof(kern_proc_info); + if (sysctl(mib, std::size(mib), &kern_proc_info, &len, nullptr, 0) == 0) + return kern_proc_info.kp_proc.p_flag & P_TRACED; + return false; +} + +} // namespace + +namespace crashpad { + +namespace { + +// A base class for signal handler and Mach exception server. +class CrashHandler : public Thread, + public UniversalMachExcServer::Interface, + public ObjcExceptionDelegate { + public: + CrashHandler(const CrashHandler&) = delete; + CrashHandler& operator=(const CrashHandler&) = delete; + + static CrashHandler* Get() { + if (!instance_) + instance_ = new CrashHandler(); + return instance_; + } + + static void ResetForTesting() { + delete instance_; + instance_ = nullptr; + } + + bool Initialize(const base::FilePath& database, + const std::string& url, + const std::map& annotations) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + if (!in_process_handler_.Initialize(database, url, annotations) || + !InstallMachExceptionHandler() || + // xnu turns hardware faults into Mach exceptions, so the only signal + // left to register is SIGABRT, which never starts off as a hardware + // fault. Installing a handler for other signals would lead to + // recording exceptions twice. As a consequence, Crashpad will not + // generate intermediate dumps for anything manually calling + // raise(SIG*). In practice, this doesn’t actually happen for crash + // signals that originate as hardware faults. + !Signals::InstallHandler( + SIGABRT, CatchAndReraiseSignal, 0, &old_action_)) { + LOG(ERROR) << "Unable to initialize Crashpad."; + return false; + } + + // For applications that haven't ignored or set a handler for SIGPIPE: + // It’s OK for an application to set its own SIGPIPE handler (including + // SIG_IGN) before initializing Crashpad, because Crashpad will discover the + // existing handler and not install its own. + // It’s OK for Crashpad to install its own SIGPIPE handler and for the + // application to subsequently install its own (including SIG_IGN) + // afterwards, because its handler will replace Crashpad’s. + // This is useful to cover the default situation where nobody installs a + // SIGPIPE handler and the disposition is at SIG_DFL, because SIGPIPE is a + // “kill†signal (bsd/sys/signalvar.h sigprop). In that case, without + // Crashpad, SIGPIPE results in a silent and unreported kill (and not even + // ReportCrash will record it), but developers probably want to be alerted + // to the conditon. + struct sigaction sa; + if (sigaction(SIGPIPE, nullptr, &sa) == 0 && sa.sa_handler == SIG_DFL) { + Signals::InstallHandler( + SIGPIPE, CatchAndReraiseSignalDefaultAction, 0, nullptr); + } + + InstallObjcExceptionPreprocessor(this); + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; + } + + void ProcessIntermediateDumps( + const std::map& annotations) { + in_process_handler_.ProcessIntermediateDumps(annotations); + } + + void ProcessIntermediateDump( + const base::FilePath& file, + const std::map& annotations) { + in_process_handler_.ProcessIntermediateDump(file, annotations); + } + + void DumpWithoutCrash(NativeCPUContext* context, bool process_dump) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + base::FilePath path; + if (!in_process_handler_.DumpExceptionFromSimulatedMachException( + context, kMachExceptionSimulated, &path)) { + return; + } + + if (process_dump) { + in_process_handler_.ProcessIntermediateDump(path); + } + } + + void DumpWithoutCrashAtPath(NativeCPUContext* context, + const base::FilePath& path) { + in_process_handler_.DumpExceptionFromSimulatedMachExceptionAtPath( + context, kMachExceptionSimulated, path); + } + + void StartProcessingPendingReports() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + in_process_handler_.StartProcessingPendingReports(); + } + + void SetMachExceptionCallbackForTesting(void (*callback)()) { + in_process_handler_.SetMachExceptionCallbackForTesting(callback); + } + + uint64_t GetThreadIdForTesting() { return Thread::GetThreadIdForTesting(); } + + private: + CrashHandler() = default; + + ~CrashHandler() { + UninstallObjcExceptionPreprocessor(); + Signals::InstallDefaultHandler(SIGABRT); + UninstallMachExceptionHandler(); + } + + bool InstallMachExceptionHandler() { + exception_port_.reset(NewMachPort(MACH_PORT_RIGHT_RECEIVE)); + if (!exception_port_.is_valid()) { + return false; + } + + kern_return_t kr = mach_port_insert_right(mach_task_self(), + exception_port_.get(), + exception_port_.get(), + MACH_MSG_TYPE_MAKE_SEND); + if (kr != KERN_SUCCESS) { + MACH_LOG(ERROR, kr) << "mach_port_insert_right"; + return false; + } + + // TODO: Use SwapExceptionPort instead and put back EXC_MASK_BREAKPOINT. + // Until then, remove |EXC_MASK_BREAKPOINT| while attached to a debugger. + exception_mask_t mask = + ExcMaskAll() & + ~(EXC_MASK_EMULATION | EXC_MASK_SOFTWARE | EXC_MASK_RPC_ALERT | + EXC_MASK_GUARD | (IsBeingDebugged() ? EXC_MASK_BREAKPOINT : 0)); + + ExceptionPorts exception_ports(ExceptionPorts::kTargetTypeTask, TASK_NULL); + if (!exception_ports.GetExceptionPorts(mask, &original_handlers_) || + !exception_ports.SetExceptionPort( + mask, + exception_port_.get(), + EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, + MACHINE_THREAD_STATE)) { + return false; + } + + mach_handler_running_ = true; + Start(); + return true; + } + + void UninstallMachExceptionHandler() { + mach_handler_running_ = false; + exception_port_.reset(); + Join(); + } + + // Thread: + + void ThreadMain() override { + UniversalMachExcServer universal_mach_exc_server(this); + while (mach_handler_running_) { + mach_msg_return_t mr = + MachMessageServer::Run(&universal_mach_exc_server, + exception_port_.get(), + MACH_MSG_OPTION_NONE, + MachMessageServer::kPersistent, + MachMessageServer::kReceiveLargeIgnore, + kMachMessageTimeoutWaitIndefinitely); + MACH_CHECK(mr == (mach_handler_running_ ? MACH_SEND_INVALID_DEST + : MACH_RCV_PORT_CHANGED), + mr) + << "MachMessageServer::Run"; + } + } + + // UniversalMachExcServer::Interface: + + kern_return_t CatchMachException(exception_behavior_t behavior, + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count, + const mach_msg_trailer_t* trailer, + bool* destroy_complex_request) override { + *destroy_complex_request = true; + + // TODO(justincohen): Forward exceptions to original_handlers_ with + // UniversalExceptionRaise. + + // iOS shouldn't have any child processes, but just in case, those will + // inherit the task exception ports, and this process isn’t prepared to + // handle them + if (task != mach_task_self()) { + CRASHPAD_RAW_LOG("MachException task != mach_task_self()"); + return KERN_FAILURE; + } + + HandleMachException(behavior, + thread, + exception, + code, + code_count, + *flavor, + old_state, + old_state_count); + + // Respond with KERN_FAILURE so the system will continue to handle this + // exception. xnu will turn this Mach exception into a signal and take the + // default action to terminate the process. However, if sigprocmask is + // called before this Mach exception returns (such as by another thread + // calling abort, see: Libc-1506.40.4/stdlib/FreeBSD/abort.c), the Mach + // exception will be converted into a signal but delivery will be blocked. + // Since concurrent exceptions lead to the losing thread sleeping + // indefinitely, if the abort thread never returns, the thread that + // triggered this Mach exception will repeatedly trap and the process will + // never terminate. If the abort thread didn’t have a user-space signal + // handler that slept forever, the abort would terminate the process even if + // all other signals had been blocked. Instead, unblock all signals + // corresponding to all Mach exceptions Crashpad is registered for before + // returning KERN_FAILURE. There is still racy behavior possible with this + // call to sigprocmask, but the repeated calls to CatchMachException here + // will eventually lead to termination. + sigset_t unblock_set; + sigemptyset(&unblock_set); + sigaddset(&unblock_set, SIGILL); // EXC_BAD_INSTRUCTION + sigaddset(&unblock_set, SIGTRAP); // EXC_BREAKPOINT + sigaddset(&unblock_set, SIGFPE); // EXC_ARITHMETIC + sigaddset(&unblock_set, SIGBUS); // EXC_BAD_ACCESS + sigaddset(&unblock_set, SIGSEGV); // EXC_BAD_ACCESS + if (sigprocmask(SIG_UNBLOCK, &unblock_set, nullptr) != 0) { + CRASHPAD_RAW_LOG("sigprocmask"); + } + return KERN_FAILURE; + } + + void HandleMachException(exception_behavior_t behavior, + thread_t thread, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count) { + in_process_handler_.DumpExceptionFromMachException(behavior, + thread, + exception, + code, + code_count, + flavor, + old_state, + old_state_count); + } + + void HandleUncaughtNSException(const uint64_t* frames, + const size_t num_frames) override { + in_process_handler_.DumpExceptionFromNSExceptionWithFrames(frames, + num_frames); + // After uncaught exceptions are reported, the system immediately triggers a + // call to std::terminate()/abort(). Remove the abort handler so a second + // dump isn't generated. + CHECK(Signals::InstallDefaultHandler(SIGABRT)); + } + + void HandleUncaughtNSExceptionWithContext( + NativeCPUContext* context) override { + base::FilePath path; + in_process_handler_.DumpExceptionFromSimulatedMachException( + context, kMachExceptionFromNSException, &path); + + // After uncaught exceptions are reported, the system immediately triggers a + // call to std::terminate()/abort(). Remove the abort handler so a second + // dump isn't generated. + CHECK(Signals::InstallDefaultHandler(SIGABRT)); + } + + void HandleUncaughtNSExceptionWithContextAtPath( + NativeCPUContext* context, + const base::FilePath& path) override { + in_process_handler_.DumpExceptionFromSimulatedMachExceptionAtPath( + context, kMachExceptionFromNSException, path); + } + + bool MoveIntermediateDumpAtPathToPending( + const base::FilePath& path) override { + if (in_process_handler_.MoveIntermediateDumpAtPathToPending(path)) { + // After uncaught exceptions are reported, the system immediately triggers + // a call to std::terminate()/abort(). Remove the abort handler so a + // second dump isn't generated. + CHECK(Signals::InstallDefaultHandler(SIGABRT)); + return true; + } + return false; + } + + // The signal handler installed at OS-level. + static void CatchAndReraiseSignal(int signo, + siginfo_t* siginfo, + void* context) { + Get()->HandleAndReraiseSignal(signo, + siginfo, + reinterpret_cast(context), + &(Get()->old_action_)); + } + + static void CatchAndReraiseSignalDefaultAction(int signo, + siginfo_t* siginfo, + void* context) { + Get()->HandleAndReraiseSignal( + signo, siginfo, reinterpret_cast(context), nullptr); + } + + void HandleAndReraiseSignal(int signo, + siginfo_t* siginfo, + ucontext_t* context, + struct sigaction* old_action) { + in_process_handler_.DumpExceptionFromSignal(siginfo, context); + + // Always call system handler. + Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, old_action); + } + + base::mac::ScopedMachReceiveRight exception_port_; + ExceptionPorts::ExceptionHandlerVector original_handlers_; + struct sigaction old_action_ = {}; + internal::InProcessHandler in_process_handler_; + static CrashHandler* instance_; + bool mach_handler_running_ = false; + InitializationStateDcheck initialized_; +}; + +CrashHandler* CrashHandler::instance_ = nullptr; + +} // namespace + +CrashpadClient::CrashpadClient() {} + +CrashpadClient::~CrashpadClient() {} + +// static +bool CrashpadClient::StartCrashpadInProcessHandler( + const base::FilePath& database, + const std::string& url, + const std::map& annotations) { + CrashHandler* crash_handler = CrashHandler::Get(); + DCHECK(crash_handler); + return crash_handler->Initialize(database, url, annotations); +} + +// static +void CrashpadClient::ProcessIntermediateDumps( + const std::map& annotations) { + CrashHandler* crash_handler = CrashHandler::Get(); + DCHECK(crash_handler); + crash_handler->ProcessIntermediateDumps(annotations); +} + +// static +void CrashpadClient::ProcessIntermediateDump( + const base::FilePath& file, + const std::map& annotations) { + CrashHandler* crash_handler = CrashHandler::Get(); + DCHECK(crash_handler); + crash_handler->ProcessIntermediateDump(file, annotations); +} + +// static +void CrashpadClient::StartProcessingPendingReports() { + CrashHandler* crash_handler = CrashHandler::Get(); + DCHECK(crash_handler); + crash_handler->StartProcessingPendingReports(); +} + +// static +void CrashpadClient::DumpWithoutCrash(NativeCPUContext* context) { + CrashHandler* crash_handler = CrashHandler::Get(); + DCHECK(crash_handler); + crash_handler->DumpWithoutCrash(context, /*process_dump=*/true); +} + +// static +void CrashpadClient::DumpWithoutCrashAndDeferProcessing( + NativeCPUContext* context) { + CrashHandler* crash_handler = CrashHandler::Get(); + DCHECK(crash_handler); + crash_handler->DumpWithoutCrash(context, /*process_dump=*/false); +} + +// static +void CrashpadClient::DumpWithoutCrashAndDeferProcessingAtPath( + NativeCPUContext* context, + const base::FilePath path) { + CrashHandler* crash_handler = CrashHandler::Get(); + DCHECK(crash_handler); + crash_handler->DumpWithoutCrashAtPath(context, path); +} + +void CrashpadClient::ResetForTesting() { + CrashHandler* crash_handler = CrashHandler::Get(); + DCHECK(crash_handler); + crash_handler->ResetForTesting(); +} + +void CrashpadClient::SetMachExceptionCallbackForTesting(void (*callback)()) { + CrashHandler* crash_handler = CrashHandler::Get(); + DCHECK(crash_handler); + crash_handler->SetMachExceptionCallbackForTesting(callback); +} + +uint64_t CrashpadClient::GetThreadIdForTesting() { + CrashHandler* crash_handler = CrashHandler::Get(); + DCHECK(crash_handler); + return crash_handler->GetThreadIdForTesting(); +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/crashpad_client_ios_test.mm b/shared/sentry/external/crashpad/client/crashpad_client_ios_test.mm new file mode 100644 index 000000000..596f4b848 --- /dev/null +++ b/shared/sentry/external/crashpad/client/crashpad_client_ios_test.mm @@ -0,0 +1,154 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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 "client/crashpad_client.h" + +#import + +#include + +#include "base/strings/sys_string_conversions.h" +#include "client/crash_report_database.h" +#include "client/ios_handler/exception_processor.h" +#include "client/simulate_crash.h" +#include "gtest/gtest.h" +#include "test/scoped_temp_dir.h" +#include "testing/platform_test.h" +#include "util/thread/thread.h" + +namespace crashpad { +namespace test { +namespace { + +class CrashpadIOSClient : public PlatformTest { + protected: + // testing::Test: + + void SetUp() override { + ASSERT_TRUE(client_.StartCrashpadInProcessHandler( + base::FilePath(database_dir.path()), "", {})); + database_ = CrashReportDatabase::Initialize(database_dir.path()); + } + + void TearDown() override { client_.ResetForTesting(); } + + auto& Client() { return client_; } + auto& Database() { return database_; } + + private: + std::unique_ptr database_; + CrashpadClient client_; + ScopedTempDir database_dir; +}; + +TEST_F(CrashpadIOSClient, DumpWithoutCrash) { + std::vector reports; + EXPECT_EQ(Database()->GetPendingReports(&reports), + CrashReportDatabase::kNoError); + ASSERT_EQ(reports.size(), 0u); + CRASHPAD_SIMULATE_CRASH(); + + EXPECT_EQ(Database()->GetPendingReports(&reports), + CrashReportDatabase::kNoError); + ASSERT_EQ(reports.size(), 1u); +} + +TEST_F(CrashpadIOSClient, DumpWithoutCrashAndDefer) { + std::vector reports; + CRASHPAD_SIMULATE_CRASH_AND_DEFER_PROCESSING(); + EXPECT_EQ(Database()->GetPendingReports(&reports), + CrashReportDatabase::kNoError); + ASSERT_EQ(reports.size(), 0u); + Client().ProcessIntermediateDumps(); + EXPECT_EQ(Database()->GetPendingReports(&reports), + CrashReportDatabase::kNoError); + ASSERT_EQ(reports.size(), 1u); +} + +TEST_F(CrashpadIOSClient, DumpWithoutCrashAndDeferAtPath) { + std::vector reports; + ScopedTempDir crash_dir; + UUID uuid; + uuid.InitializeWithNew(); + CRASHPAD_SIMULATE_CRASH_AND_DEFER_PROCESSING_AT_PATH( + crash_dir.path().Append(uuid.ToString())); + EXPECT_EQ(Database()->GetPendingReports(&reports), + CrashReportDatabase::kNoError); + ASSERT_EQ(reports.size(), 0u); + + NSError* error = nil; + NSArray* paths = [[NSFileManager defaultManager] + contentsOfDirectoryAtPath:base::SysUTF8ToNSString( + crash_dir.path().value()) + error:&error]; + ASSERT_EQ([paths count], 1u); + Client().ProcessIntermediateDump( + crash_dir.path().Append([paths[0] fileSystemRepresentation])); + reports.clear(); + EXPECT_EQ(Database()->GetPendingReports(&reports), + CrashReportDatabase::kNoError); + ASSERT_EQ(reports.size(), 1u); +} + +class RaceThread : public Thread { + public: + explicit RaceThread() : Thread() {} + + private: + void ThreadMain() override { + for (int i = 0; i < 10; ++i) { + CRASHPAD_SIMULATE_CRASH(); + } + } +}; + +TEST_F(CrashpadIOSClient, MultipleThreadsSimulateCrash) { + RaceThread race_threads[2]; + for (RaceThread& race_thread : race_threads) { + race_thread.Start(); + } + + for (int i = 0; i < 10; ++i) { + CRASHPAD_SIMULATE_CRASH(); + } + for (RaceThread& race_thread : race_threads) { + race_thread.Join(); + } + + std::vector reports; + ASSERT_EQ(Database()->GetPendingReports(&reports), + CrashReportDatabase::kNoError); + EXPECT_EQ(reports.size(), 30u); +} + +// This test is covered by a similar XCUITest, but for development purposes it's +// sometimes easier and faster to run in Google Test. However, there's no way +// to correctly run this in Google Test. Leave the test here, disabled, for use +// during development only. +TEST_F(CrashpadIOSClient, DISABLED_ThrowNSException) { + [NSException raise:@"GoogleTestNSException" format:@"ThrowException"]; +} + +// This test is covered by a similar XCUITest, but for development purposes it's +// sometimes easier and faster to run in Google Test. However, there's no way +// to correctly run this in Google Test. Leave the test here, disabled, for use +// during development only. +TEST_F(CrashpadIOSClient, DISABLED_ThrowException) { + std::vector empty_vector; + empty_vector.at(42); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/crashpad_client_linux.cc b/shared/sentry/external/crashpad/client/crashpad_client_linux.cc new file mode 100644 index 000000000..5bb27c2ea --- /dev/null +++ b/shared/sentry/external/crashpad/client/crashpad_client_linux.cc @@ -0,0 +1,747 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "client/crashpad_client.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "build/build_config.h" +#include "build/chromeos_buildflags.h" +#include "client/client_argv_handling.h" +#include "third_party/lss/lss.h" +#include "util/file/file_io.h" +#include "util/file/filesystem.h" +#include "util/linux/exception_handler_client.h" +#include "util/linux/exception_information.h" +#include "util/linux/scoped_pr_set_dumpable.h" +#include "util/linux/scoped_pr_set_ptracer.h" +#include "util/linux/socket.h" +#include "util/misc/address_sanitizer.h" +#include "util/misc/from_pointer_cast.h" +#include "util/posix/double_fork_and_exec.h" +#include "util/posix/scoped_mmap.h" +#include "util/posix/signals.h" + +namespace crashpad { + +namespace { + +std::string FormatArgumentInt(const std::string& name, int value) { + return base::StringPrintf("--%s=%d", name.c_str(), value); +} + +std::string FormatArgumentAddress(const std::string& name, const void* addr) { + return base::StringPrintf("--%s=%p", name.c_str(), addr); +} + +#if BUILDFLAG(IS_ANDROID) + +std::vector BuildAppProcessArgs( + const std::string& class_name, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments, + int socket) { +#if defined(ARCH_CPU_64_BITS) + static constexpr char kAppProcess[] = "/system/bin/app_process64"; +#else + static constexpr char kAppProcess[] = "/system/bin/app_process32"; +#endif + + std::vector argv; + argv.push_back(kAppProcess); + argv.push_back("/system/bin"); + argv.push_back("--application"); + argv.push_back(class_name); + + std::vector handler_argv = + BuildHandlerArgvStrings(base::FilePath(kAppProcess), + database, + metrics_dir, + url, + annotations, + arguments); + + if (socket != kInvalidFileHandle) { + handler_argv.push_back(FormatArgumentInt("initial-client-fd", socket)); + } + + argv.insert(argv.end(), handler_argv.begin(), handler_argv.end()); + return argv; +} + +std::vector BuildArgsToLaunchWithLinker( + const std::string& handler_trampoline, + const std::string& handler_library, + bool is_64_bit, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments, + int socket) { + std::vector argv; + if (is_64_bit) { + argv.push_back("/system/bin/linker64"); + } else { + argv.push_back("/system/bin/linker"); + } + argv.push_back(handler_trampoline); + argv.push_back(handler_library); + + std::vector handler_argv = BuildHandlerArgvStrings( + base::FilePath(), database, metrics_dir, url, annotations, arguments); + + if (socket != kInvalidFileHandle) { + handler_argv.push_back(FormatArgumentInt("initial-client-fd", socket)); + } + + argv.insert(argv.end(), handler_argv.begin() + 1, handler_argv.end()); + return argv; +} + +#endif // BUILDFLAG(IS_ANDROID) + +// A base class for Crashpad signal handler implementations. +class SignalHandler { + public: + SignalHandler(const SignalHandler&) = delete; + SignalHandler& operator=(const SignalHandler&) = delete; + + // Returns the currently installed signal hander. May be `nullptr` if no + // handler has been installed. + static SignalHandler* Get() { return handler_; } + + // Disables any installed Crashpad signal handler. If a crash signal is + // received, any previously installed (non-Crashpad) signal handler will be + // restored and the signal reraised. + static void Disable() { + if (!handler_->disabled_.test_and_set()) { + handler_->WakeThreads(); + } + } + + void SetFirstChanceHandler(CrashpadClient::FirstChanceHandlerLinux handler) { + first_chance_handler_ = handler; + } + + // The base implementation for all signal handlers, suitable for calling + // directly to simulate signal delivery. + void HandleCrash(int signo, siginfo_t* siginfo, void* context) { + exception_information_.siginfo_address = + FromPointerCast( + siginfo); + exception_information_.context_address = + FromPointerCast( + context); + exception_information_.thread_id = sys_gettid(); + + ScopedPrSetDumpable set_dumpable(false); + HandleCrashImpl(); + } + + protected: + SignalHandler() = default; + ~SignalHandler() = default; + + bool Install(const std::set* unhandled_signals) { + bool signal_stack_initialized = + CrashpadClient::InitializeSignalStackForThread(); + DCHECK(signal_stack_initialized); + + DCHECK(!handler_); + handler_ = this; + return Signals::InstallCrashHandlers( + HandleOrReraiseSignal, SA_ONSTACK, &old_actions_, unhandled_signals); + } + + const ExceptionInformation& GetExceptionInfo() { + return exception_information_; + } + + virtual void HandleCrashImpl() = 0; + + private: + static constexpr int32_t kDumpNotDone = 0; + static constexpr int32_t kDumpDone = 1; + + // The signal handler installed at OS-level. + static void HandleOrReraiseSignal(int signo, + siginfo_t* siginfo, + void* context) { + if (handler_->first_chance_handler_ && + handler_->first_chance_handler_( + signo, siginfo, static_cast(context))) { + return; + } + + // Only handle the first fatal signal observed. If another thread receives a + // crash signal, it waits for the first dump to complete instead of + // requesting another. + if (!handler_->disabled_.test_and_set()) { + handler_->HandleCrash(signo, siginfo, context); + handler_->WakeThreads(); + } else { + // Processes on Android normally have several chained signal handlers that + // co-operate to report crashes. e.g. WebView will have this signal + // handler installed, the app embedding WebView may have a signal handler + // installed, and Bionic will have a signal handler. Each signal handler + // runs in succession, possibly managed by libsigchain. This wait is + // intended to avoid ill-effects from multiple signal handlers from + // different layers (possibly all trying to use ptrace()) from running + // simultaneously. It does not block forever so that in most conditions, + // those signal handlers will still have a chance to run and ensures + // process termination in case the first crashing thread crashes again in + // its signal handler. Though less typical, this situation also occurs on + // other Linuxes, e.g. to produce in-process stack traces for debug + // builds. + handler_->WaitForDumpDone(); + } + + Signals::RestoreHandlerAndReraiseSignalOnReturn( + siginfo, handler_->old_actions_.ActionForSignal(signo)); + } + + void WaitForDumpDone() { + kernel_timespec timeout; + timeout.tv_sec = 5; + timeout.tv_nsec = 0; + sys_futex(&dump_done_futex_, + FUTEX_WAIT_PRIVATE, + kDumpNotDone, + &timeout, + nullptr, + 0); + } + + void WakeThreads() { + dump_done_futex_ = kDumpDone; + sys_futex( + &dump_done_futex_, FUTEX_WAKE_PRIVATE, INT_MAX, nullptr, nullptr, 0); + } + + Signals::OldActions old_actions_ = {}; + ExceptionInformation exception_information_ = {}; + CrashpadClient::FirstChanceHandlerLinux first_chance_handler_ = nullptr; + int32_t dump_done_futex_ = kDumpNotDone; + std::atomic_flag disabled_ = ATOMIC_FLAG_INIT; + + static SignalHandler* handler_; +}; +SignalHandler* SignalHandler::handler_ = nullptr; + +// Launches a single use handler to snapshot this process. +class LaunchAtCrashHandler : public SignalHandler { + public: + LaunchAtCrashHandler(const LaunchAtCrashHandler&) = delete; + LaunchAtCrashHandler& operator=(const LaunchAtCrashHandler&) = delete; + + static LaunchAtCrashHandler* Get() { + static LaunchAtCrashHandler* instance = new LaunchAtCrashHandler(); + return instance; + } + + bool Initialize(std::vector* argv_in, + const std::vector* envp, + const std::set* unhandled_signals) { + argv_strings_.swap(*argv_in); + + if (envp) { + envp_strings_ = *envp; + StringVectorToCStringVector(envp_strings_, &envp_); + set_envp_ = true; + } + + argv_strings_.push_back(FormatArgumentAddress("trace-parent-with-exception", + &GetExceptionInfo())); + + StringVectorToCStringVector(argv_strings_, &argv_); + return Install(unhandled_signals); + } + + void HandleCrashImpl() override { + ScopedPrSetPtracer set_ptracer(sys_getpid(), /* may_log= */ false); + + pid_t pid = fork(); + if (pid < 0) { + return; + } + if (pid == 0) { + if (set_envp_) { + execve(argv_[0], + const_cast(argv_.data()), + const_cast(envp_.data())); + } else { + execv(argv_[0], const_cast(argv_.data())); + } + _exit(EXIT_FAILURE); + } + + int status; + waitpid(pid, &status, 0); + } + + private: + LaunchAtCrashHandler() = default; + + ~LaunchAtCrashHandler() = delete; + + std::vector argv_strings_; + std::vector argv_; + std::vector envp_strings_; + std::vector envp_; + bool set_envp_ = false; +}; + +class RequestCrashDumpHandler : public SignalHandler { + public: + RequestCrashDumpHandler(const RequestCrashDumpHandler&) = delete; + RequestCrashDumpHandler& operator=(const RequestCrashDumpHandler&) = delete; + + static RequestCrashDumpHandler* Get() { + static RequestCrashDumpHandler* instance = new RequestCrashDumpHandler(); + return instance; + } + + // pid < 0 indicates the handler pid should be determined by communicating + // over the socket. + // pid == 0 indicates it is not necessary to set the handler as this process' + // ptracer. e.g. if the handler has CAP_SYS_PTRACE or if this process is in a + // user namespace and the handler's uid matches the uid of the process that + // created the namespace. + // pid > 0 directly indicates what the handler's pid is expected to be, so + // retrieving this information from the handler is not necessary. + bool Initialize(ScopedFileHandle sock, + pid_t pid, + const std::set* unhandled_signals) { + ExceptionHandlerClient client(sock.get(), true); + if (pid < 0) { + ucred creds; + if (!client.GetHandlerCredentials(&creds)) { + return false; + } + pid = creds.pid; + } + if (pid > 0) { + pthread_atfork(nullptr, nullptr, SetPtracerAtFork); + if (prctl(PR_SET_PTRACER, pid, 0, 0, 0) != 0) { + PLOG(WARNING) << "prctl"; + } + } + sock_to_handler_.reset(sock.release()); + handler_pid_ = pid; + return Install(unhandled_signals); + } + + bool GetHandlerSocket(int* sock, pid_t* pid) { + if (!sock_to_handler_.is_valid()) { + return false; + } + if (sock) { + *sock = sock_to_handler_.get(); + } + if (pid) { + *pid = handler_pid_; + } + return true; + } + + void HandleCrashImpl() override { + // Attempt to set the ptracer again, in case a crash occurs after a fork, + // before SetPtracerAtFork() has been called. Ignore errors because the + // system call may be disallowed if the sandbox is engaged. + if (handler_pid_ > 0) { + sys_prctl(PR_SET_PTRACER, handler_pid_, 0, 0, 0); + } + + ExceptionHandlerProtocol::ClientInformation info = {}; + info.exception_information_address = + FromPointerCast(&GetExceptionInfo()); +#if BUILDFLAG(IS_CHROMEOS_ASH) + info.crash_loop_before_time = crash_loop_before_time_; +#endif + + ExceptionHandlerClient client(sock_to_handler_.get(), true); + client.RequestCrashDump(info); + } + +#if BUILDFLAG(IS_CHROMEOS_ASH) + void SetCrashLoopBefore(uint64_t crash_loop_before_time) { + crash_loop_before_time_ = crash_loop_before_time; + } +#endif + + private: + RequestCrashDumpHandler() = default; + + ~RequestCrashDumpHandler() = delete; + + static void SetPtracerAtFork() { + auto handler = RequestCrashDumpHandler::Get(); + if (handler->handler_pid_ > 0 && + prctl(PR_SET_PTRACER, handler->handler_pid_, 0, 0, 0) != 0) { + PLOG(WARNING) << "prctl"; + } + } + + ScopedFileHandle sock_to_handler_; + pid_t handler_pid_ = -1; + +#if BUILDFLAG(IS_CHROMEOS_ASH) + // An optional UNIX timestamp passed to us from Chrome. + // This will pass to crashpad_handler and then to Chrome OS crash_reporter. + // This should really be a time_t, but it's basically an opaque value (we + // don't anything with it except pass it along). + uint64_t crash_loop_before_time_ = 0; +#endif +}; + +} // namespace + +CrashpadClient::CrashpadClient() {} + +CrashpadClient::~CrashpadClient() {} + +bool CrashpadClient::StartHandler( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments, + bool restartable, + bool asynchronous_start, + const std::vector& attachments) { + DCHECK(!asynchronous_start); + + ScopedFileHandle client_sock, handler_sock; + if (!UnixCredentialSocket::CreateCredentialSocketpair(&client_sock, + &handler_sock)) { + return false; + } + + std::vector argv = BuildHandlerArgvStrings( + handler, database, metrics_dir, url, annotations, arguments, attachments); + + argv.push_back(FormatArgumentInt("initial-client-fd", handler_sock.get())); + argv.push_back("--shared-client-connection"); + if (!DoubleForkAndExec(argv, nullptr, handler_sock.get(), false, nullptr)) { + return false; + } + + pid_t handler_pid = -1; + if (!IsRegularFile(base::FilePath("/proc/sys/kernel/yama/ptrace_scope"))) { + handler_pid = 0; + } + + auto signal_handler = RequestCrashDumpHandler::Get(); + return signal_handler->Initialize( + std::move(client_sock), handler_pid, &unhandled_signals_); +} + +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) +// static +bool CrashpadClient::GetHandlerSocket(int* sock, pid_t* pid) { + auto signal_handler = RequestCrashDumpHandler::Get(); + return signal_handler->GetHandlerSocket(sock, pid); +} + +bool CrashpadClient::SetHandlerSocket(ScopedFileHandle sock, pid_t pid) { + auto signal_handler = RequestCrashDumpHandler::Get(); + return signal_handler->Initialize(std::move(sock), pid, &unhandled_signals_); +} + +// static +bool CrashpadClient::InitializeSignalStackForThread() { + stack_t stack; + if (sigaltstack(nullptr, &stack) != 0) { + PLOG(ERROR) << "sigaltstack"; + return false; + } + + DCHECK_EQ(stack.ss_flags & SS_ONSTACK, 0); + + const size_t page_size = getpagesize(); +#if defined(ADDRESS_SANITIZER) + const size_t kStackSize = 2 * ((SIGSTKSZ + page_size - 1) & ~(page_size - 1)); +#else + const size_t kStackSize = (SIGSTKSZ + page_size - 1) & ~(page_size - 1); +#endif // ADDRESS_SANITIZER + if (stack.ss_flags & SS_DISABLE || stack.ss_size < kStackSize) { + const size_t kGuardPageSize = page_size; + const size_t kStackAllocSize = kStackSize + 2 * kGuardPageSize; + + static void (*stack_destructor)(void*) = [](void* stack_mem) { + const size_t page_size = getpagesize(); + const size_t kGuardPageSize = page_size; +#if defined(ADDRESS_SANITIZER) + const size_t kStackSize = + 2 * ((SIGSTKSZ + page_size - 1) & ~(page_size - 1)); +#else + const size_t kStackSize = (SIGSTKSZ + page_size - 1) & ~(page_size - 1); +#endif // ADDRESS_SANITIZER + const size_t kStackAllocSize = kStackSize + 2 * kGuardPageSize; + + stack_t stack; + stack.ss_flags = SS_DISABLE; + if (sigaltstack(&stack, &stack) != 0) { + PLOG(ERROR) << "sigaltstack"; + } else if (stack.ss_sp != + static_cast(stack_mem) + kGuardPageSize) { + PLOG_IF(ERROR, sigaltstack(&stack, nullptr) != 0) << "sigaltstack"; + } + + if (munmap(stack_mem, kStackAllocSize) != 0) { + PLOG(ERROR) << "munmap"; + } + }; + + static pthread_key_t stack_key; + static int key_error = []() { + errno = pthread_key_create(&stack_key, stack_destructor); + PLOG_IF(ERROR, errno) << "pthread_key_create"; + return errno; + }(); + if (key_error) { + return false; + } + + auto old_stack = static_cast(pthread_getspecific(stack_key)); + if (old_stack) { + stack.ss_sp = old_stack + kGuardPageSize; + } else { + ScopedMmap stack_mem; + if (!stack_mem.ResetMmap(nullptr, + kStackAllocSize, + PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS, + -1, + 0)) { + return false; + } + + if (mprotect(stack_mem.addr_as() + kGuardPageSize, + kStackSize, + PROT_READ | PROT_WRITE) != 0) { + PLOG(ERROR) << "mprotect"; + return false; + } + + stack.ss_sp = stack_mem.addr_as() + kGuardPageSize; + + errno = pthread_setspecific(stack_key, stack_mem.release()); + PCHECK(errno == 0) << "pthread_setspecific"; + } + + stack.ss_size = kStackSize; + stack.ss_flags = + (stack.ss_flags & SS_DISABLE) ? 0 : stack.ss_flags & SS_AUTODISARM; + if (sigaltstack(&stack, nullptr) != 0) { + PLOG(ERROR) << "sigaltstack"; + return false; + } + } + return true; +} +#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || + // BUILDFLAG(IS_CHROMEOS) + +#if BUILDFLAG(IS_ANDROID) + +bool CrashpadClient::StartJavaHandlerAtCrash( + const std::string& class_name, + const std::vector* env, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments) { + std::vector argv = BuildAppProcessArgs(class_name, + database, + metrics_dir, + url, + annotations, + arguments, + kInvalidFileHandle); + + auto signal_handler = LaunchAtCrashHandler::Get(); + return signal_handler->Initialize(&argv, env, &unhandled_signals_); +} + +// static +bool CrashpadClient::StartJavaHandlerForClient( + const std::string& class_name, + const std::vector* env, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments, + int socket) { + std::vector argv = BuildAppProcessArgs( + class_name, database, metrics_dir, url, annotations, arguments, socket); + return DoubleForkAndExec(argv, env, socket, false, nullptr); +} + +bool CrashpadClient::StartHandlerWithLinkerAtCrash( + const std::string& handler_trampoline, + const std::string& handler_library, + bool is_64_bit, + const std::vector* env, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments) { + std::vector argv = + BuildArgsToLaunchWithLinker(handler_trampoline, + handler_library, + is_64_bit, + database, + metrics_dir, + url, + annotations, + arguments, + kInvalidFileHandle); + auto signal_handler = LaunchAtCrashHandler::Get(); + return signal_handler->Initialize(&argv, env, &unhandled_signals_); +} + +// static +bool CrashpadClient::StartHandlerWithLinkerForClient( + const std::string& handler_trampoline, + const std::string& handler_library, + bool is_64_bit, + const std::vector* env, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments, + int socket) { + std::vector argv = + BuildArgsToLaunchWithLinker(handler_trampoline, + handler_library, + is_64_bit, + database, + metrics_dir, + url, + annotations, + arguments, + socket); + return DoubleForkAndExec(argv, env, socket, false, nullptr); +} + +#endif + +bool CrashpadClient::StartHandlerAtCrash( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments, + const std::vector& attachments) { + std::vector argv = BuildHandlerArgvStrings( + handler, database, metrics_dir, url, annotations, arguments, attachments); + + auto signal_handler = LaunchAtCrashHandler::Get(); + return signal_handler->Initialize(&argv, nullptr, &unhandled_signals_); +} + +// static +bool CrashpadClient::StartHandlerForClient( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments, + int socket) { + std::vector argv = BuildHandlerArgvStrings( + handler, database, metrics_dir, url, annotations, arguments); + + argv.push_back(FormatArgumentInt("initial-client-fd", socket)); + + return DoubleForkAndExec(argv, nullptr, socket, true, nullptr); +} + +// static +void CrashpadClient::DumpWithoutCrash(NativeCPUContext* context) { + if (!SignalHandler::Get()) { + DLOG(ERROR) << "Crashpad isn't enabled"; + return; + } + +#if defined(ARCH_CPU_ARMEL) + memset(context->uc_regspace, 0, sizeof(context->uc_regspace)); +#elif defined(ARCH_CPU_ARM64) + memset(context->uc_mcontext.__reserved, + 0, + sizeof(context->uc_mcontext.__reserved)); +#endif + + siginfo_t siginfo; + siginfo.si_signo = Signals::kSimulatedSigno; + siginfo.si_errno = 0; + siginfo.si_code = 0; + SignalHandler::Get()->HandleCrash( + siginfo.si_signo, &siginfo, reinterpret_cast(context)); +} + +// static +void CrashpadClient::CrashWithoutDump(const std::string& message) { + SignalHandler::Disable(); + LOG(FATAL) << message; +} + +// static +void CrashpadClient::SetFirstChanceExceptionHandler( + FirstChanceHandlerLinux handler) { + DCHECK(SignalHandler::Get()); + SignalHandler::Get()->SetFirstChanceHandler(handler); +} + +void CrashpadClient::SetUnhandledSignals(const std::set& signals) { + DCHECK(!SignalHandler::Get()); + unhandled_signals_ = signals; +} + +#if BUILDFLAG(IS_CHROMEOS_ASH) +// static +void CrashpadClient::SetCrashLoopBefore(uint64_t crash_loop_before_time) { + auto request_crash_dump_handler = RequestCrashDumpHandler::Get(); + request_crash_dump_handler->SetCrashLoopBefore(crash_loop_before_time); +} +#endif + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/crashpad_client_linux_test.cc b/shared/sentry/external/crashpad/client/crashpad_client_linux_test.cc new file mode 100644 index 000000000..4a2bb4189 --- /dev/null +++ b/shared/sentry/external/crashpad/client/crashpad_client_linux_test.cc @@ -0,0 +1,690 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "client/crashpad_client.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "base/check_op.h" +#include "base/notreached.h" +#include "build/build_config.h" +#include "client/annotation.h" +#include "client/annotation_list.h" +#include "client/crash_report_database.h" +#include "client/crashpad_info.h" +#include "client/simulate_crash.h" +#include "gtest/gtest.h" +#include "snapshot/annotation_snapshot.h" +#include "snapshot/minidump/process_snapshot_minidump.h" +#include "snapshot/sanitized/sanitization_information.h" +#include "test/errors.h" +#include "test/multiprocess.h" +#include "test/multiprocess_exec.h" +#include "test/scoped_temp_dir.h" +#include "test/test_paths.h" +#include "util/file/file_io.h" +#include "util/file/filesystem.h" +#include "util/linux/exception_handler_client.h" +#include "util/linux/exception_information.h" +#include "util/linux/socket.h" +#include "util/misc/address_sanitizer.h" +#include "util/misc/address_types.h" +#include "util/misc/from_pointer_cast.h" +#include "util/misc/memory_sanitizer.h" +#include "util/posix/scoped_mmap.h" +#include "util/posix/signals.h" +#include "util/thread/thread.h" + +#if BUILDFLAG(IS_ANDROID) +#include +#include "dlfcn_internal.h" + +// Normally this comes from set_abort_message.h, but only at API level 21. +extern "C" void android_set_abort_message(const char* msg) + __attribute__((weak)); +#endif + +namespace crashpad { +namespace test { +namespace { + +enum class CrashType : uint32_t { + kSimulated, + kBuiltinTrap, + kInfiniteRecursion, +}; + +struct StartHandlerForSelfTestOptions { + bool start_handler_at_crash; + bool set_first_chance_handler; + bool crash_non_main_thread; + bool client_uses_signals; + bool gather_indirectly_referenced_memory; + CrashType crash_type; +}; + +class StartHandlerForSelfTest + : public testing::TestWithParam< + std::tuple> { + public: + StartHandlerForSelfTest() = default; + + StartHandlerForSelfTest(const StartHandlerForSelfTest&) = delete; + StartHandlerForSelfTest& operator=(const StartHandlerForSelfTest&) = delete; + + ~StartHandlerForSelfTest() = default; + + void SetUp() override { + // MSAN requires that padding bytes have been initialized for structs that + // are written to files. + memset(&options_, 0, sizeof(options_)); + std::tie(options_.start_handler_at_crash, + options_.set_first_chance_handler, + options_.crash_non_main_thread, + options_.client_uses_signals, + options_.gather_indirectly_referenced_memory, + options_.crash_type) = GetParam(); + } + + const StartHandlerForSelfTestOptions& Options() const { return options_; } + + private: + StartHandlerForSelfTestOptions options_; +}; + +bool InstallHandler(CrashpadClient* client, + bool start_at_crash, + const base::FilePath& handler_path, + const base::FilePath& database_path, + const std::vector& attachments) { + return start_at_crash + ? client->StartHandlerAtCrash(handler_path, + database_path, + base::FilePath(), + "", + std::map(), + std::vector(), + attachments) + : client->StartHandler(handler_path, + database_path, + base::FilePath(), + "", + std::map(), + std::vector(), + false, + false, + attachments); +} + +constexpr char kTestAnnotationName[] = "name_of_annotation"; +constexpr char kTestAnnotationValue[] = "value_of_annotation"; +constexpr char kTestAttachmentName[] = "test_attachment"; +constexpr char kTestAttachmentContent[] = "attachment_content"; + +#if BUILDFLAG(IS_ANDROID) +constexpr char kTestAbortMessage[] = "test abort message"; +#endif + +void ValidateAttachment(const CrashReportDatabase::UploadReport* report) { + auto attachments = report->GetAttachments(); + ASSERT_EQ(attachments.size(), 1u); + char buf[sizeof(kTestAttachmentContent)]; + attachments.at(kTestAttachmentName)->Read(buf, sizeof(buf)); + ASSERT_EQ(memcmp(kTestAttachmentContent, buf, sizeof(kTestAttachmentContent)), + 0); +} + +void ValidateExtraMemory(const StartHandlerForSelfTestOptions& options, + const ProcessSnapshotMinidump& minidump) { + // Verify that if we have an exception, then the code around the instruction + // pointer is included in the extra memory. + const ExceptionSnapshot* exception = minidump.Exception(); + if (exception == nullptr) + return; + uint64_t pc = exception->Context()->InstructionPointer(); + std::vector snippets = minidump.ExtraMemory(); + bool pc_found = false; + for (const MemorySnapshot* snippet : snippets) { + uint64_t start = snippet->Address(); + uint64_t end = start + snippet->Size(); + if (pc >= start && pc < end) { + pc_found = true; + break; + } + } + EXPECT_EQ(pc_found, options.gather_indirectly_referenced_memory); +} + +void ValidateDump(const StartHandlerForSelfTestOptions& options, + const CrashReportDatabase::UploadReport* report) { + ProcessSnapshotMinidump minidump_snapshot; + ASSERT_TRUE(minidump_snapshot.Initialize(report->Reader())); + +#if BUILDFLAG(IS_ANDROID) + // This part of the test requires Q. The API level on Q devices will be 28 + // until the API is finalized, so we can't check API level yet. For now, test + // for the presence of a libc symbol which was introduced in Q. + if (crashpad::internal::Dlsym(RTLD_DEFAULT, "android_fdsan_close_with_tag")) { + const auto& annotations = minidump_snapshot.AnnotationsSimpleMap(); + auto abort_message = annotations.find("abort_message"); + ASSERT_NE(annotations.end(), abort_message); + EXPECT_EQ(kTestAbortMessage, abort_message->second); + } +#endif + ValidateAttachment(report); + + ValidateExtraMemory(options, minidump_snapshot); + + for (const ModuleSnapshot* module : minidump_snapshot.Modules()) { + for (const AnnotationSnapshot& annotation : module->AnnotationObjects()) { + if (static_cast(annotation.type) != + Annotation::Type::kString) { + continue; + } + + if (annotation.name == kTestAnnotationName) { + std::string value( + reinterpret_cast(annotation.value.data()), + annotation.value.size()); + EXPECT_EQ(value, kTestAnnotationValue); + return; + } + } + } + ADD_FAILURE(); +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Winfinite-recursion" +int RecurseInfinitely(int* ptr) { + int buf[1 << 20]; + return *ptr + RecurseInfinitely(buf); +} +#pragma clang diagnostic pop + +sigjmp_buf do_crash_sigjmp_env; + +bool HandleCrashSuccessfully(int, siginfo_t*, ucontext_t*) { + siglongjmp(do_crash_sigjmp_env, 1); +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunreachable-code-return" + return true; +#pragma clang diagnostic pop +} + +void DoCrash(const StartHandlerForSelfTestOptions& options, + CrashpadClient* client) { + if (sigsetjmp(do_crash_sigjmp_env, 1) != 0) { + return; + } + + switch (options.crash_type) { + case CrashType::kSimulated: + CRASHPAD_SIMULATE_CRASH(); + break; + + case CrashType::kBuiltinTrap: + __builtin_trap(); + + case CrashType::kInfiniteRecursion: + int val = 42; + exit(RecurseInfinitely(&val)); + } +} + +class ScopedAltSignalStack { + public: + ScopedAltSignalStack() = default; + + ScopedAltSignalStack(const ScopedAltSignalStack&) = delete; + ScopedAltSignalStack& operator=(const ScopedAltSignalStack&) = delete; + + ~ScopedAltSignalStack() { + if (stack_mem_.is_valid()) { + stack_t stack; + stack.ss_flags = SS_DISABLE; + if (sigaltstack(&stack, nullptr) != 0) { + ADD_FAILURE() << ErrnoMessage("sigaltstack"); + } + } + } + + void Initialize() { + ScopedMmap local_stack_mem; + constexpr size_t stack_size = MINSIGSTKSZ; + ASSERT_TRUE(local_stack_mem.ResetMmap(nullptr, + stack_size, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, + -1, + 0)); + + stack_t stack; + stack.ss_sp = local_stack_mem.addr(); + stack.ss_size = stack_size; + stack.ss_flags = 0; + ASSERT_EQ(sigaltstack(&stack, nullptr), 0) << ErrnoMessage("sigaltstack"); + stack_mem_.ResetAddrLen(local_stack_mem.release(), stack_size); + } + + private: + ScopedMmap stack_mem_; +}; + +class CrashThread : public Thread { + public: + CrashThread(const StartHandlerForSelfTestOptions& options, + CrashpadClient* client) + : client_signal_stack_(), options_(options), client_(client) {} + + CrashThread(const CrashThread&) = delete; + CrashThread& operator=(const CrashThread&) = delete; + + private: + void ThreadMain() override { + // It is only necessary to call InitializeSignalStackForThread() once, but + // should be harmless to call multiple times and durable against the client + // using sigaltstack() either before or after it is called. + CrashpadClient::InitializeSignalStackForThread(); + if (options_.client_uses_signals) { + client_signal_stack_.Initialize(); + } + CrashpadClient::InitializeSignalStackForThread(); + + DoCrash(options_, client_); + } + + ScopedAltSignalStack client_signal_stack_; + const StartHandlerForSelfTestOptions& options_; + CrashpadClient* client_; +}; + +CRASHPAD_CHILD_TEST_MAIN(StartHandlerForSelfTestChild) { + FileHandle in = StdioFileHandle(StdioStream::kStandardInput); + + VMSize temp_dir_length; + CheckedReadFileExactly(in, &temp_dir_length, sizeof(temp_dir_length)); + + std::string temp_dir(temp_dir_length, '\0'); + CheckedReadFileExactly(in, &temp_dir[0], temp_dir_length); + + StartHandlerForSelfTestOptions options; + CheckedReadFileExactly(in, &options, sizeof(options)); + + ScopedAltSignalStack client_signal_stack; + if (options.client_uses_signals) { + client_signal_stack.Initialize(); + + static Signals::OldActions old_actions; + static Signals::Handler client_handler = + [](int signo, siginfo_t* siginfo, void*) { + FileHandle out = StdioFileHandle(StdioStream::kStandardOutput); + char c = 0; + WriteFile(out, &c, sizeof(c)); + + Signals::RestoreHandlerAndReraiseSignalOnReturn( + siginfo, old_actions.ActionForSignal(signo)); + }; + + CHECK(Signals::InstallCrashHandlers( + client_handler, SA_ONSTACK, &old_actions)); + } + + if (options.gather_indirectly_referenced_memory) { + CrashpadInfo::GetCrashpadInfo()->set_gather_indirectly_referenced_memory( + TriState::kEnabled, 1024 * 1024 * 4); + } + + base::FilePath handler_path = TestPaths::Executable().DirName().Append( + FILE_PATH_LITERAL("crashpad_handler")); + + crashpad::AnnotationList::Register(); + + static StringAnnotation<32> test_annotation(kTestAnnotationName); + test_annotation.Set(kTestAnnotationValue); + + const std::vector attachments = { + base::FilePath(temp_dir).Append(kTestAttachmentName)}; + + crashpad::CrashpadClient client; + if (!InstallHandler(&client, + options.start_handler_at_crash, + handler_path, + base::FilePath(temp_dir), + attachments)) { + return EXIT_FAILURE; + } + + if (options.set_first_chance_handler) { + client.SetFirstChanceExceptionHandler(HandleCrashSuccessfully); + } + +#if BUILDFLAG(IS_ANDROID) + if (android_set_abort_message) { + android_set_abort_message(kTestAbortMessage); + } +#endif + + if (options.crash_non_main_thread) { + CrashThread thread(options, &client); + thread.Start(); + thread.Join(); + } else { + DoCrash(options, &client); + } + + return EXIT_SUCCESS; +} + +class StartHandlerForSelfInChildTest : public MultiprocessExec { + public: + StartHandlerForSelfInChildTest(const StartHandlerForSelfTestOptions& options) + : MultiprocessExec(), options_(options) { + SetChildTestMainFunction("StartHandlerForSelfTestChild"); + if (!options.set_first_chance_handler) { + switch (options.crash_type) { + case CrashType::kSimulated: + // kTerminationNormal, EXIT_SUCCESS + break; + case CrashType::kBuiltinTrap: + SetExpectedChildTerminationBuiltinTrap(); + break; + case CrashType::kInfiniteRecursion: + SetExpectedChildTermination(TerminationReason::kTerminationSignal, + SIGSEGV); + break; + } + } + } + + StartHandlerForSelfInChildTest(const StartHandlerForSelfInChildTest&) = + delete; + StartHandlerForSelfInChildTest& operator=( + const StartHandlerForSelfInChildTest&) = delete; + + private: + void MultiprocessParent() override { + ScopedTempDir temp_dir; + VMSize temp_dir_length = temp_dir.path().value().size(); + ASSERT_TRUE(LoggingWriteFile( + WritePipeHandle(), &temp_dir_length, sizeof(temp_dir_length))); + ASSERT_TRUE(LoggingWriteFile( + WritePipeHandle(), temp_dir.path().value().data(), temp_dir_length)); + ASSERT_TRUE( + LoggingWriteFile(WritePipeHandle(), &options_, sizeof(options_))); + + FileWriter writer; + base::FilePath test_attachment_path = + temp_dir.path().Append(kTestAttachmentName); + bool is_created = writer.Open(test_attachment_path, + FileWriteMode::kCreateOrFail, + FilePermissions::kOwnerOnly); + ASSERT_TRUE(is_created); + writer.Write(kTestAttachmentContent, sizeof(kTestAttachmentContent)); + writer.Close(); + + if (options_.client_uses_signals && !options_.set_first_chance_handler && + options_.crash_type != CrashType::kSimulated) { + // Wait for child's client signal handler. + char c; + EXPECT_TRUE(LoggingReadFileExactly(ReadPipeHandle(), &c, sizeof(c))); + } + + // Wait for child to finish. + CheckedReadFileAtEOF(ReadPipeHandle()); + + auto database = CrashReportDatabase::Initialize(temp_dir.path()); + ASSERT_TRUE(database); + + std::vector reports; + ASSERT_EQ(database->GetCompletedReports(&reports), + CrashReportDatabase::kNoError); + EXPECT_EQ(reports.size(), 0u); + + reports.clear(); + ASSERT_EQ(database->GetPendingReports(&reports), + CrashReportDatabase::kNoError); + + bool report_expected = !options_.set_first_chance_handler || + options_.crash_type == CrashType::kSimulated; + ASSERT_EQ(reports.size(), report_expected ? 1u : 0u); + + if (!report_expected) { + return; + } + + std::unique_ptr report; + ASSERT_EQ(database->GetReportForUploading(reports[0].uuid, &report), + CrashReportDatabase::kNoError); + ValidateDump(options_, report.get()); + } + + StartHandlerForSelfTestOptions options_; +}; + +TEST_P(StartHandlerForSelfTest, StartHandlerInChild) { +#if defined(ADDRESS_SANITIZER) || defined(MEMORY_SANITIZER) || \ + defined(UNDEFINED_SANITIZER) + if (Options().crash_type == CrashType::kInfiniteRecursion) { + GTEST_SKIP(); + } +#endif // defined(ADDRESS_SANITIZER) + StartHandlerForSelfInChildTest test(Options()); + test.Run(); +} + +INSTANTIATE_TEST_SUITE_P( + StartHandlerForSelfTestSuite, + StartHandlerForSelfTest, + testing::Combine(testing::Bool(), + testing::Bool(), + testing::Bool(), + testing::Bool(), + testing::Bool(), + testing::Values(CrashType::kSimulated, + CrashType::kBuiltinTrap, + CrashType::kInfiniteRecursion))); + +// Test state for starting the handler for another process. +class StartHandlerForClientTest { + public: + StartHandlerForClientTest() = default; + + StartHandlerForClientTest(const StartHandlerForClientTest&) = delete; + StartHandlerForClientTest& operator=(const StartHandlerForClientTest&) = + delete; + + ~StartHandlerForClientTest() = default; + + bool Initialize(bool sanitize) { + sanitize_ = sanitize; + return UnixCredentialSocket::CreateCredentialSocketpair(&client_sock_, + &server_sock_); + } + + bool StartHandlerOnDemand() { + char c; + if (!LoggingReadFileExactly(server_sock_.get(), &c, sizeof(c))) { + ADD_FAILURE(); + return false; + } + + base::FilePath handler_path = TestPaths::Executable().DirName().Append( + FILE_PATH_LITERAL("crashpad_handler")); + + CrashpadClient client; + if (!client.StartHandlerForClient(handler_path, + temp_dir_.path(), + base::FilePath(), + "", + std::map(), + std::vector(), + server_sock_.get())) { + ADD_FAILURE(); + return false; + } + + return true; + } + + void ExpectReport() { + auto database = + CrashReportDatabase::InitializeWithoutCreating(temp_dir_.path()); + ASSERT_TRUE(database); + + std::vector reports; + ASSERT_EQ(database->GetCompletedReports(&reports), + CrashReportDatabase::kNoError); + EXPECT_EQ(reports.size(), 0u); + + reports.clear(); + ASSERT_EQ(database->GetPendingReports(&reports), + CrashReportDatabase::kNoError); + if (sanitize_) { + EXPECT_EQ(reports.size(), 0u); + } else { + EXPECT_EQ(reports.size(), 1u); + } + } + + bool InstallHandler() { + auto signal_handler = SandboxedHandler::Get(); + return signal_handler->Initialize(client_sock_.get(), sanitize_); + } + + private: + // A signal handler that defers handler process startup to another, presumably + // more privileged, process. + class SandboxedHandler { + public: + SandboxedHandler(const SandboxedHandler&) = delete; + SandboxedHandler& operator=(const SandboxedHandler&) = delete; + + static SandboxedHandler* Get() { + static SandboxedHandler* instance = new SandboxedHandler(); + return instance; + } + + bool Initialize(FileHandle client_sock, bool sanitize) { + client_sock_ = client_sock; + sanitize_ = sanitize; + return Signals::InstallCrashHandlers(HandleCrash, 0, nullptr); + } + + private: + SandboxedHandler() = default; + ~SandboxedHandler() = delete; + + static void HandleCrash(int signo, siginfo_t* siginfo, void* context) { + auto state = Get(); + + char c = 0; + CHECK(LoggingWriteFile(state->client_sock_, &c, sizeof(c))); + + ExceptionInformation exception_information; + exception_information.siginfo_address = + FromPointerCast( + siginfo); + exception_information.context_address = + FromPointerCast( + context); + exception_information.thread_id = syscall(SYS_gettid); + + ExceptionHandlerProtocol::ClientInformation info; + info.exception_information_address = + FromPointerCast( + &exception_information); + + SanitizationInformation sanitization_info = {}; + if (state->sanitize_) { + info.sanitization_information_address = + FromPointerCast(&sanitization_info); + // Target a non-module address to prevent a crash dump. + sanitization_info.target_module_address = + FromPointerCast(&sanitization_info); + } + + ExceptionHandlerClient handler_client(state->client_sock_, false); + CHECK_EQ(handler_client.RequestCrashDump(info), 0); + + Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr); + } + + FileHandle client_sock_; + bool sanitize_; + }; + + ScopedTempDir temp_dir_; + ScopedFileHandle client_sock_; + ScopedFileHandle server_sock_; + bool sanitize_; +}; + +// Tests starting the handler for a child process. +class StartHandlerForChildTest : public Multiprocess { + public: + StartHandlerForChildTest() = default; + + StartHandlerForChildTest(const StartHandlerForChildTest&) = delete; + StartHandlerForChildTest& operator=(const StartHandlerForChildTest&) = delete; + + ~StartHandlerForChildTest() = default; + + bool Initialize(bool sanitize) { + SetExpectedChildTerminationBuiltinTrap(); + return test_state_.Initialize(sanitize); + } + + private: + void MultiprocessParent() { + ASSERT_TRUE(test_state_.StartHandlerOnDemand()); + + // Wait for chlid to finish. + CheckedReadFileAtEOF(ReadPipeHandle()); + + test_state_.ExpectReport(); + } + + void MultiprocessChild() { + CHECK(test_state_.InstallHandler()); + + __builtin_trap(); + + NOTREACHED(); + } + + StartHandlerForClientTest test_state_; +}; + +TEST(CrashpadClient, StartHandlerForChild) { + StartHandlerForChildTest test; + ASSERT_TRUE(test.Initialize(/* sanitize= */ false)); + test.Run(); +} + +TEST(CrashpadClient, SanitizedChild) { + StartHandlerForChildTest test; + ASSERT_TRUE(test.Initialize(/* sanitize= */ true)); + test.Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/crashpad_client_mac.cc b/shared/sentry/external/crashpad/client/crashpad_client_mac.cc new file mode 100644 index 000000000..0d174f5d2 --- /dev/null +++ b/shared/sentry/external/crashpad/client/crashpad_client_mac.cc @@ -0,0 +1,551 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "client/crashpad_client.h" + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "base/logging.h" +#include "base/mac/mach_logging.h" +#include "base/strings/stringprintf.h" +#include "util/mac/mac_util.h" +#include "util/mach/bootstrap.h" +#include "util/mach/child_port_handshake.h" +#include "util/mach/exception_ports.h" +#include "util/mach/mach_extensions.h" +#include "util/mach/mach_message.h" +#include "util/mach/notify_server.h" +#include "util/misc/clock.h" +#include "util/misc/implicit_cast.h" +#include "util/posix/double_fork_and_exec.h" + +namespace crashpad { + +namespace { + +std::string FormatArgumentString(const std::string& name, + const std::string& value) { + return base::StringPrintf("--%s=%s", name.c_str(), value.c_str()); +} + +std::string FormatArgumentInt(const std::string& name, int value) { + return base::StringPrintf("--%s=%d", name.c_str(), value); +} + +// Set the exception handler for EXC_CRASH, EXC_RESOURCE, and EXC_GUARD. +// +// EXC_CRASH is how most crashes are received. Most other exception types such +// as EXC_BAD_ACCESS are delivered to a host-level exception handler in the +// kernel where they are converted to POSIX signals. See 10.9.5 +// xnu-2422.115.4/bsd/uxkern/ux_exception.c catch_mach_exception_raise(). If a +// core-generating signal (triggered through this hardware mechanism or a +// software mechanism such as abort() sending SIGABRT) is unhandled and the +// process exits, or if the process is killed with SIGKILL for code-signing +// reasons, an EXC_CRASH exception will be sent. See 10.9.5 +// xnu-2422.115.4/bsd/kern/kern_exit.c proc_prepareexit(). +// +// EXC_RESOURCE and EXC_GUARD do not become signals or EXC_CRASH exceptions. The +// host-level exception handler in the kernel does not receive these exception +// types, and even if it did, it would not map them to signals. Instead, the +// first Mach service loaded by the root (process ID 1) launchd with a boolean +// “ExceptionServer†property in its job dictionary (regardless of its value) or +// with any subdictionary property will become the host-level exception handler +// for EXC_CRASH, EXC_RESOURCE, and EXC_GUARD. See 10.9.5 +// launchd-842.92.1/src/core.c job_setup_exception_port(). Normally, this job is +// com.apple.ReportCrash.Root, the systemwide Apple Crash Reporter. Since it is +// impossible to receive EXC_RESOURCE and EXC_GUARD exceptions through the +// EXC_CRASH mechanism, an exception handler must be registered for them by name +// if it is to receive these exception types. The default task-level handler for +// these exception types is set by launchd in a similar manner. +// +// EXC_MASK_RESOURCE and EXC_MASK_GUARD are not available on all systems, and +// the kernel will reject attempts to use them if it does not understand them, +// so AND them with ExcMaskValid(). EXC_MASK_CRASH is always supported. +bool SetCrashExceptionPorts(exception_handler_t exception_handler) { + ExceptionPorts exception_ports(ExceptionPorts::kTargetTypeTask, TASK_NULL); + return exception_ports.SetExceptionPort( + (EXC_MASK_CRASH | EXC_MASK_RESOURCE | EXC_MASK_GUARD) & ExcMaskValid(), + exception_handler, + EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, + MACHINE_THREAD_STATE); +} + +class ScopedPthreadAttrDestroy { + public: + explicit ScopedPthreadAttrDestroy(pthread_attr_t* pthread_attr) + : pthread_attr_(pthread_attr) { + } + + ScopedPthreadAttrDestroy(const ScopedPthreadAttrDestroy&) = delete; + ScopedPthreadAttrDestroy& operator=(const ScopedPthreadAttrDestroy&) = delete; + + ~ScopedPthreadAttrDestroy() { + errno = pthread_attr_destroy(pthread_attr_); + PLOG_IF(WARNING, errno != 0) << "pthread_attr_destroy"; + } + + private: + pthread_attr_t* pthread_attr_; +}; + +//! \brief Starts a Crashpad handler, possibly restarting it if it dies. +class HandlerStarter final : public NotifyServer::DefaultInterface { + public: + HandlerStarter(const HandlerStarter&) = delete; + HandlerStarter& operator=(const HandlerStarter&) = delete; + + ~HandlerStarter() {} + + //! \brief Starts a Crashpad handler initially, as opposed to starting it for + //! subsequent restarts. + //! + //! All parameters are as in CrashpadClient::StartHandler(). + //! + //! \return On success, a send right to the Crashpad handler that has been + //! started. On failure, `MACH_PORT_NULL` with a message logged. + static base::mac::ScopedMachSendRight InitialStart( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments, + const std::vector& attachments, + bool restartable) { + base::mac::ScopedMachReceiveRight receive_right( + NewMachPort(MACH_PORT_RIGHT_RECEIVE)); + if (!receive_right.is_valid()) { + return base::mac::ScopedMachSendRight(); + } + + mach_port_t port; + mach_msg_type_name_t right_type; + kern_return_t kr = mach_port_extract_right(mach_task_self(), + receive_right.get(), + MACH_MSG_TYPE_MAKE_SEND, + &port, + &right_type); + if (kr != KERN_SUCCESS) { + MACH_LOG(ERROR, kr) << "mach_port_extract_right"; + return base::mac::ScopedMachSendRight(); + } + base::mac::ScopedMachSendRight send_right(port); + DCHECK_EQ(port, receive_right.get()); + DCHECK_EQ(right_type, + implicit_cast(MACH_MSG_TYPE_PORT_SEND)); + + std::unique_ptr handler_restarter; + if (restartable) { + handler_restarter.reset(new HandlerStarter()); + if (!handler_restarter->notify_port_.is_valid()) { + // This is an error that NewMachPort() would have logged. Proceed anyway + // without the ability to restart. + handler_restarter.reset(); + } + } + + if (!CommonStart(handler, + database, + metrics_dir, + url, + annotations, + arguments, + attachments, + std::move(receive_right), + handler_restarter.get(), + false)) { + return base::mac::ScopedMachSendRight(); + } + + if (handler_restarter && + handler_restarter->StartRestartThread( + handler, database, metrics_dir, url, annotations, arguments, attachments)) { + // The thread owns the object now. + std::ignore = handler_restarter.release(); + } + + // If StartRestartThread() failed, proceed without the ability to restart. + // handler_restarter will be released when this function returns. + + return send_right; + } + + // NotifyServer::DefaultInterface: + + kern_return_t DoMachNotifyPortDestroyed(notify_port_t notify, + mach_port_t rights, + const mach_msg_trailer_t* trailer, + bool* destroy_request) override { + // The receive right corresponding to this process’ crash exception port is + // now owned by this process. Any crashes that occur before the receive + // right is moved to a new handler process will cause the process to hang in + // an unkillable state prior to OS X 10.10. + + if (notify != notify_port_) { + LOG(WARNING) << "notify port mismatch"; + return KERN_FAILURE; + } + + // If CommonStart() fails, the receive right will die, and this will just + // be called again for another try. + CommonStart(handler_, + database_, + metrics_dir_, + url_, + annotations_, + arguments_, + attachments_, + base::mac::ScopedMachReceiveRight(rights), + this, + true); + + return KERN_SUCCESS; + } + + private: + HandlerStarter() + : NotifyServer::DefaultInterface(), + handler_(), + database_(), + metrics_dir_(), + url_(), + annotations_(), + arguments_(), + attachments_(), + notify_port_(NewMachPort(MACH_PORT_RIGHT_RECEIVE)), + last_start_time_(0) { + } + + //! \brief Starts a Crashpad handler. + //! + //! All parameters are as in CrashpadClient::StartHandler(), with these + //! additions: + //! + //! \param[in] receive_right The receive right to move to the Crashpad + //! handler. The handler will use this receive right to run its exception + //! server. + //! \param[in] handler_restarter If CrashpadClient::StartHandler() was invoked + //! with \a restartable set to `true`, this is the restart state object. + //! Otherwise, this is `nullptr`. + //! \param[in] restart If CrashpadClient::StartHandler() was invoked with \a + //! restartable set to `true` and CommonStart() is being called to restart + //! a previously-started handler, this is `true`. Otherwise, this is + //! `false`. + //! + //! \return `true` on success, `false` on failure, with a message logged. + //! Failures include failure to start the handler process and failure to + //! rendezvous with it via ChildPortHandshake. + static bool CommonStart(const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments, + const std::vector& attachments, + base::mac::ScopedMachReceiveRight receive_right, + HandlerStarter* handler_restarter, + bool restart) { + DCHECK(!restart || handler_restarter); + + if (handler_restarter) { + // The port-destroyed notification must be requested each time. It uses + // a send-once right, so once the notification is received, it won’t be + // sent again unless re-requested. + mach_port_t previous; + kern_return_t kr = + mach_port_request_notification(mach_task_self(), + receive_right.get(), + MACH_NOTIFY_PORT_DESTROYED, + 0, + handler_restarter->notify_port_.get(), + MACH_MSG_TYPE_MAKE_SEND_ONCE, + &previous); + if (kr != KERN_SUCCESS) { + MACH_LOG(WARNING, kr) << "mach_port_request_notification"; + + // This will cause the restart thread to terminate after this restart + // attempt. There’s no longer any need for it, because no more + // port-destroyed notifications can be delivered. + handler_restarter->notify_port_.reset(); + } else { + base::mac::ScopedMachSendRight previous_owner(previous); + DCHECK(restart || !previous_owner.is_valid()); + } + + if (restart) { + // If the handler was ever started before, don’t restart it too quickly. + constexpr uint64_t kNanosecondsPerSecond = 1E9; + constexpr uint64_t kMinimumStartInterval = 1 * kNanosecondsPerSecond; + + const uint64_t earliest_next_start_time = + handler_restarter->last_start_time_ + kMinimumStartInterval; + const uint64_t now_time = ClockMonotonicNanoseconds(); + if (earliest_next_start_time > now_time) { + const uint64_t sleep_time = earliest_next_start_time - now_time; + LOG(INFO) << "restarting handler" + << base::StringPrintf(" in %.3fs", + static_cast(sleep_time) / + kNanosecondsPerSecond); + SleepNanoseconds(earliest_next_start_time - now_time); + } else { + LOG(INFO) << "restarting handler"; + } + } + + handler_restarter->last_start_time_ = ClockMonotonicNanoseconds(); + } + + ChildPortHandshake child_port_handshake; + base::ScopedFD server_write_fd = child_port_handshake.ServerWriteFD(); + + // Use handler as argv[0], followed by arguments directed by this method’s + // parameters and a --handshake-fd argument. |arguments| are added first so + // that if it erroneously contains an argument such as --url, the actual + // |url| argument passed to this method will supersede it. In normal + // command-line processing, the last parameter wins in the case of a + // conflict. + std::vector argv(1, handler.value()); + argv.reserve(1 + arguments.size() + 2 + annotations.size() + 1); + for (const std::string& argument : arguments) { + argv.push_back(argument); + } + if (!database.value().empty()) { + argv.push_back(FormatArgumentString("database", database.value())); + } + if (!metrics_dir.value().empty()) { + argv.push_back(FormatArgumentString("metrics-dir", metrics_dir.value())); + } + if (!url.empty()) { + argv.push_back(FormatArgumentString("url", url)); + } + for (const auto& kv : annotations) { + argv.push_back( + FormatArgumentString("annotation", kv.first + '=' + kv.second)); + } + + for (const auto& attachment : attachments) { + argv.push_back(FormatArgumentString("attachment", attachment.value())); + } + + argv.push_back(FormatArgumentInt("handshake-fd", server_write_fd.get())); + + // When restarting, reset the system default crash handler first. Otherwise, + // the crash exception port in the handler will have been inherited from + // this parent process, which was probably using the exception server now + // being restarted. The handler can’t monitor itself for its own crashes via + // this interface. + if (!DoubleForkAndExec( + argv, + nullptr, + server_write_fd.get(), + true, + restart ? CrashpadClient::UseSystemDefaultHandler : nullptr)) { + return false; + } + + // Close the write side of the pipe, so that the handler process is the only + // process that can write to it. + server_write_fd.reset(); + + // Rendezvous with the handler running in the grandchild process. + if (!child_port_handshake.RunClient(receive_right.get(), + MACH_MSG_TYPE_MOVE_RECEIVE)) { + return false; + } + + std::ignore = receive_right.release(); + return true; + } + + bool StartRestartThread(const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments, + const std::vector& attachments) { + handler_ = handler; + database_ = database; + metrics_dir_ = metrics_dir; + url_ = url; + annotations_ = annotations; + arguments_ = arguments; + attachments_ = attachments; + + pthread_attr_t pthread_attr; + errno = pthread_attr_init(&pthread_attr); + if (errno != 0) { + PLOG(WARNING) << "pthread_attr_init"; + return false; + } + ScopedPthreadAttrDestroy pthread_attr_owner(&pthread_attr); + + errno = pthread_attr_setdetachstate(&pthread_attr, PTHREAD_CREATE_DETACHED); + if (errno != 0) { + PLOG(WARNING) << "pthread_attr_setdetachstate"; + return false; + } + + pthread_t pthread; + errno = pthread_create(&pthread, &pthread_attr, RestartThreadMain, this); + if (errno != 0) { + PLOG(WARNING) << "pthread_create"; + return false; + } + + return true; + } + + static void* RestartThreadMain(void* argument) { + HandlerStarter* self = reinterpret_cast(argument); + + NotifyServer notify_server(self); + mach_msg_return_t mr; + do { + mr = MachMessageServer::Run(¬ify_server, + self->notify_port_.get(), + 0, + MachMessageServer::kPersistent, + MachMessageServer::kReceiveLargeError, + kMachMessageTimeoutWaitIndefinitely); + MACH_LOG_IF(ERROR, mr != MACH_MSG_SUCCESS, mr) + << "MachMessageServer::Run"; + } while (self->notify_port_.is_valid() && mr == MACH_MSG_SUCCESS); + + delete self; + + return nullptr; + } + + base::FilePath handler_; + base::FilePath database_; + base::FilePath metrics_dir_; + std::string url_; + std::map annotations_; + std::vector arguments_; + std::vector attachments_; + base::mac::ScopedMachReceiveRight notify_port_; + uint64_t last_start_time_; +}; + +} // namespace + +CrashpadClient::CrashpadClient() : exception_port_(MACH_PORT_NULL) { +} + +CrashpadClient::~CrashpadClient() { +} + +bool CrashpadClient::StartHandler( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments, + bool restartable, + bool asynchronous_start, + const std::vector& attachments) { + // The “restartable†behavior can only be selected on OS X 10.10 and later. In + // previous OS versions, if the initial client were to crash while attempting + // to restart the handler, it would become an unkillable process. + base::mac::ScopedMachSendRight exception_port(HandlerStarter::InitialStart( + handler, + database, + metrics_dir, + url, + annotations, + arguments, + attachments, + restartable && (__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_10 || + MacOSVersionNumber() >= 10'10'00))); + if (!exception_port.is_valid()) { + return false; + } + + SetHandlerMachPort(std::move(exception_port)); + return true; +} + +bool CrashpadClient::SetHandlerMachService(const std::string& service_name) { + base::mac::ScopedMachSendRight exception_port(BootstrapLookUp(service_name)); + if (!exception_port.is_valid()) { + return false; + } + + SetHandlerMachPort(std::move(exception_port)); + return true; +} + +bool CrashpadClient::SetHandlerMachPort( + base::mac::ScopedMachSendRight exception_port) { + DCHECK(!exception_port_.is_valid()); + DCHECK(exception_port.is_valid()); + + if (!SetCrashExceptionPorts(exception_port.get())) { + return false; + } + + exception_port_.swap(exception_port); + return true; +} + +base::mac::ScopedMachSendRight CrashpadClient::GetHandlerMachPort() const { + DCHECK(exception_port_.is_valid()); + + // For the purposes of this method, only return a port set by + // SetHandlerMachPort(). + // + // It would be possible to use task_get_exception_ports() to look up the + // EXC_CRASH task exception port, but that’s probably not what users of this + // interface really want. If CrashpadClient is asked for the handler Mach + // port, it should only return a port that it knows about by virtue of having + // set it. It shouldn’t return any EXC_CRASH task exception port in effect if + // SetHandlerMachPort() was never called, and it shouldn’t return any + // EXC_CRASH task exception port that might be set by other code after + // SetHandlerMachPort() is called. + // + // The caller is accepting its own new ScopedMachSendRight, so increment the + // reference count of the underlying right. + kern_return_t kr = mach_port_mod_refs( + mach_task_self(), exception_port_.get(), MACH_PORT_RIGHT_SEND, 1); + if (kr != KERN_SUCCESS) { + MACH_LOG(ERROR, kr) << "mach_port_mod_refs"; + return base::mac::ScopedMachSendRight(MACH_PORT_NULL); + } + + return base::mac::ScopedMachSendRight(exception_port_.get()); +} + +// static +void CrashpadClient::UseSystemDefaultHandler() { + base::mac::ScopedMachSendRight + system_crash_reporter_handler(SystemCrashReporterHandler()); + + // Proceed even if SystemCrashReporterHandler() failed, setting MACH_PORT_NULL + // to clear the current exception ports. + if (!SetCrashExceptionPorts(system_crash_reporter_handler.get())) { + SetCrashExceptionPorts(MACH_PORT_NULL); + } +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/crashpad_client_win.cc b/shared/sentry/external/crashpad/client/crashpad_client_win.cc new file mode 100644 index 000000000..bdf15797c --- /dev/null +++ b/shared/sentry/external/crashpad/client/crashpad_client_win.cc @@ -0,0 +1,1097 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "client/crashpad_client.h" + +#include + +#include +#include +#include + +#include + +#include "base/atomicops.h" +#include "base/logging.h" +#include "base/scoped_generic.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "base/synchronization/lock.h" +#include "util/file/file_io.h" +#include "util/misc/capture_context.h" +#include "util/misc/from_pointer_cast.h" +#include "util/misc/random_string.h" +#include "util/win/address_types.h" +#include "util/win/command_line.h" +#include "util/win/context_wrappers.h" +#include "util/win/critical_section_with_debug_info.h" +#include "util/win/exception_codes.h" +#include "util/win/get_function.h" +#include "util/win/handle.h" +#include "util/win/initial_client_data.h" +#include "util/win/loader_lock.h" +#include "util/win/nt_internals.h" +#include "util/win/ntstatus_logging.h" +#include "util/win/process_info.h" +#include "util/win/registration_protocol_win.h" +#include "util/win/safe_terminate_process.h" +#include "util/win/scoped_process_suspend.h" +#include "util/win/termination_codes.h" +#include "util/win/xp_compat.h" + +namespace crashpad { + +namespace { + +// This handle is never closed. This is used to signal to the server that a dump +// should be taken in the event of a crash. +HANDLE g_signal_exception = INVALID_HANDLE_VALUE; + +// Where we store the exception information that the crash handler reads. +ExceptionInformation g_crash_exception_information; + +// These handles are never closed. g_signal_non_crash_dump is used to signal to +// the server to take a dump (not due to an exception), and the server will +// signal g_non_crash_dump_done when the dump is completed. +HANDLE g_signal_non_crash_dump = INVALID_HANDLE_VALUE; +HANDLE g_non_crash_dump_done = INVALID_HANDLE_VALUE; + +CrashpadClient::FirstChanceHandlerWin first_chance_handler_ = nullptr; + +// Guards multiple simultaneous calls to DumpWithoutCrash(). This is leaked. +base::Lock* g_non_crash_dump_lock = nullptr; + +class CrashDumpAutoReleaser { + public: + ~CrashDumpAutoReleaser() { + if (g_non_crash_dump_lock) { + delete g_non_crash_dump_lock; + g_non_crash_dump_lock = nullptr; + } + } +}; +static CrashDumpAutoReleaser gAutoReleaser; + +// Where we store a pointer to the context information when taking a non-crash +// dump. +ExceptionInformation g_non_crash_exception_information; + +enum class StartupState : int { + kNotReady = 0, // This must be value 0 because it is the initial value of a + // global AtomicWord. + kSucceeded = 1, // The CreateProcess() for the handler succeeded. + kFailed = 2, // The handler failed to start. +}; + +// This is a tri-state of type StartupState. It starts at 0 == kNotReady, and +// when the handler is known to have started successfully, or failed to start +// the value will be updated. The unhandled exception filter will not proceed +// until one of those two cases happens. +base::subtle::AtomicWord g_handler_startup_state; + +// A CRITICAL_SECTION initialized with +// RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO to force it to be allocated with a +// valid .DebugInfo field. The address of this critical section is given to the +// handler. All critical sections with debug info are linked in a doubly-linked +// list, so this allows the handler to capture all of them. +CRITICAL_SECTION g_critical_section_with_debug_info; + +void SetHandlerStartupState(StartupState state) { + DCHECK(state == StartupState::kSucceeded || state == StartupState::kFailed); + base::subtle::Release_Store(&g_handler_startup_state, + static_cast(state)); +} + +StartupState BlockUntilHandlerStartedOrFailed() { + // Wait until we know the handler has either succeeded or failed to start. + base::subtle::AtomicWord startup_state; + while ( + (startup_state = base::subtle::Acquire_Load(&g_handler_startup_state)) == + static_cast(StartupState::kNotReady)) { + Sleep(1); + } + + return static_cast(startup_state); +} + +#if defined(ADDRESS_SANITIZER) +extern "C" LONG __asan_unhandled_exception_filter(EXCEPTION_POINTERS* info); +#endif + +LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) { +#if defined(ADDRESS_SANITIZER) + // In ASan builds, delegate to the ASan exception filter. + LONG status = __asan_unhandled_exception_filter(exception_pointers); + if (status != EXCEPTION_CONTINUE_SEARCH) + return status; +#endif + + if (BlockUntilHandlerStartedOrFailed() == StartupState::kFailed) { + // If we know for certain that the handler has failed to start, then abort + // here, rather than trying to signal to a handler that will never arrive, + // and then sleeping unnecessarily. + LOG(ERROR) << "crash server failed to launch, self-terminating"; + SafeTerminateProcess(GetCurrentProcess(), kTerminationCodeCrashNoDump); + return EXCEPTION_CONTINUE_SEARCH; + } + + if (first_chance_handler_ && first_chance_handler_(exception_pointers)) { + return EXCEPTION_CONTINUE_SEARCH; + } + + // Otherwise, we know the handler startup has succeeded, and we can continue. + + // Tracks whether a thread has already entered UnhandledExceptionHandler. + static base::subtle::AtomicWord have_crashed; + + // This is a per-process handler. While this handler is being invoked, other + // threads are still executing as usual, so multiple threads could enter at + // the same time. Because we're in a crashing state, we shouldn't be doing + // anything that might cause allocations, call into kernel mode, etc. So, we + // don't want to take a critical section here to avoid simultaneous access to + // the global exception pointers in ExceptionInformation. Because the crash + // handler will record all threads, it's fine to simply have the second and + // subsequent entrants block here. They will soon be suspended by the crash + // handler, and then the entire process will be terminated below. This means + // that we won't save the exception pointers from the second and further + // crashes, but contention here is very unlikely, and we'll still have a stack + // that's blocked at this location. + if (base::subtle::Barrier_AtomicIncrement(&have_crashed, 1) > 1) { + SleepEx(INFINITE, false); + } + + // Otherwise, we're the first thread, so record the exception pointer and + // signal the crash handler. + g_crash_exception_information.thread_id = GetCurrentThreadId(); + g_crash_exception_information.exception_pointers = + FromPointerCast(exception_pointers); + + // Now signal the crash server, which will take a dump and then terminate us + // when it's complete. + SetEvent(g_signal_exception); + + // Time to wait for the handler to create a dump. + constexpr DWORD kMillisecondsUntilTerminate = 60 * 1000; + + // Sleep for a while to allow it to process us. Eventually, we terminate + // ourselves in case the crash server is gone, so that we don't leave zombies + // around. This would ideally never happen. + Sleep(kMillisecondsUntilTerminate); + + LOG(ERROR) << "crash server did not respond, self-terminating"; + + SafeTerminateProcess(GetCurrentProcess(), kTerminationCodeCrashNoDump); + + return EXCEPTION_CONTINUE_SEARCH; +} + +void HandleAbortSignal(int signum) { + DCHECK_EQ(signum, SIGABRT); + + CONTEXT context; + CaptureContext(&context); + + EXCEPTION_RECORD record = {}; + record.ExceptionCode = STATUS_FATAL_APP_EXIT; + record.ExceptionFlags = EXCEPTION_NONCONTINUABLE; + record.ExceptionAddress = ProgramCounterFromCONTEXT(&context); + + EXCEPTION_POINTERS exception_pointers; + exception_pointers.ContextRecord = &context; + exception_pointers.ExceptionRecord = &record; + + UnhandledExceptionHandler(&exception_pointers); +} + +std::wstring FormatArgumentString(const std::string& name, + const std::wstring& value) { + return std::wstring(L"--") + base::UTF8ToWide(name) + L"=" + value; +} + +struct ScopedProcThreadAttributeListTraits { + static PPROC_THREAD_ATTRIBUTE_LIST InvalidValue() { return nullptr; } + + static void Free(PPROC_THREAD_ATTRIBUTE_LIST proc_thread_attribute_list) { + // This is able to use GET_FUNCTION_REQUIRED() instead of GET_FUNCTION() + // because it will only be called if InitializeProcThreadAttributeList() and + // UpdateProcThreadAttribute() are present. + static const auto delete_proc_thread_attribute_list = + GET_FUNCTION_REQUIRED(L"kernel32.dll", ::DeleteProcThreadAttributeList); + delete_proc_thread_attribute_list(proc_thread_attribute_list); + } +}; + +using ScopedProcThreadAttributeList = + base::ScopedGeneric; + +bool IsInheritableHandle(HANDLE handle) { + if (!handle || handle == INVALID_HANDLE_VALUE) + return false; + + // File handles (FILE_TYPE_DISK) and pipe handles (FILE_TYPE_PIPE) are known + // to be inheritable. Console handles (FILE_TYPE_CHAR) are not inheritable via + // PROC_THREAD_ATTRIBUTE_HANDLE_LIST. See + // https://crashpad.chromium.org/bug/77. + DWORD handle_type = GetFileType(handle); + return handle_type == FILE_TYPE_DISK || handle_type == FILE_TYPE_PIPE; +} + +// Adds |handle| to |handle_list| if it appears valid, and is not already in +// |handle_list|. +// +// Invalid handles (including INVALID_HANDLE_VALUE and null handles) cannot be +// added to a PPROC_THREAD_ATTRIBUTE_LIST’s PROC_THREAD_ATTRIBUTE_HANDLE_LIST. +// If INVALID_HANDLE_VALUE appears, CreateProcess() will fail with +// ERROR_INVALID_PARAMETER. If a null handle appears, the child process will +// silently not inherit any handles. +// +// Use this function to add handles with uncertain validities. +void AddHandleToListIfValidAndInheritable(std::vector* handle_list, + HANDLE handle) { + // There doesn't seem to be any documentation of this, but if there's a handle + // duplicated in this list, CreateProcess() fails with + // ERROR_INVALID_PARAMETER. + if (IsInheritableHandle(handle) && + std::find(handle_list->begin(), handle_list->end(), handle) == + handle_list->end()) { + handle_list->push_back(handle); + } +} + +void AddUint32(std::vector* data_vector, uint32_t data) { + data_vector->push_back(static_cast(data & 0xff)); + data_vector->push_back(static_cast((data & 0xff00) >> 8)); + data_vector->push_back(static_cast((data & 0xff0000) >> 16)); + data_vector->push_back(static_cast((data & 0xff000000) >> 24)); +} + +void AddUint64(std::vector* data_vector, uint64_t data) { + AddUint32(data_vector, static_cast(data & 0xffffffffULL)); + AddUint32(data_vector, + static_cast((data & 0xffffffff00000000ULL) >> 32)); +} + +//! \brief Creates a randomized pipe name to listen for client registrations +//! on and returns its name. +//! +//! \param[out] pipe_name The pipe name that will be listened on. +//! \param[out] pipe_handle The first pipe instance corresponding for the pipe. +void CreatePipe(std::wstring* pipe_name, ScopedFileHANDLE* pipe_instance) { + int tries = 5; + std::string pipe_name_base = base::StringPrintf( +#if defined(WINDOWS_UWP) + "\\\\.\\pipe\\LOCAL\\crashpad_%lu_", +#else + "\\\\.\\pipe\\crashpad_%lu_", +#endif + GetCurrentProcessId()); + do { + *pipe_name = base::UTF8ToWide(pipe_name_base + RandomString()); + + pipe_instance->reset(CreateNamedPipeInstance(*pipe_name, true)); + + // CreateNamedPipe() is documented as setting the error to + // ERROR_ACCESS_DENIED if FILE_FLAG_FIRST_PIPE_INSTANCE is specified and the + // pipe name is already in use. However it may set the error to other codes + // such as ERROR_PIPE_BUSY (if the pipe already exists and has reached its + // maximum instance count) or ERROR_INVALID_PARAMETER (if the pipe already + // exists and its attributes differ from those specified to + // CreateNamedPipe()). Some of these errors may be ambiguous: for example, + // ERROR_INVALID_PARAMETER may also occur if CreateNamedPipe() is called + // incorrectly even in the absence of an existing pipe by the same name. + // Rather than chasing down all of the possible errors that might indicate + // that a pipe name is already in use, retry up to a few times on any error. + } while (!pipe_instance->is_valid() && --tries); + + PCHECK(pipe_instance->is_valid()) << "CreateNamedPipe"; +} + +struct BackgroundHandlerStartThreadData { + BackgroundHandlerStartThreadData( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments, + const std::vector& attachments, + const std::wstring& ipc_pipe, + ScopedFileHANDLE ipc_pipe_handle) + : handler(handler), + database(database), + metrics_dir(metrics_dir), + url(url), + annotations(annotations), + arguments(arguments), + attachments(attachments), + ipc_pipe(ipc_pipe), + ipc_pipe_handle(std::move(ipc_pipe_handle)) {} + + base::FilePath handler; + base::FilePath database; + base::FilePath metrics_dir; + std::string url; + std::map annotations; + std::vector arguments; + std::vector attachments; + std::wstring ipc_pipe; + ScopedFileHANDLE ipc_pipe_handle; +}; + +// Ensures that SetHandlerStartupState() is called on scope exit. Assumes +// failure, and on success, SetSuccessful() should be called. +class ScopedCallSetHandlerStartupState { + public: + ScopedCallSetHandlerStartupState() : successful_(false) {} + + ScopedCallSetHandlerStartupState(const ScopedCallSetHandlerStartupState&) = + delete; + ScopedCallSetHandlerStartupState& operator=( + const ScopedCallSetHandlerStartupState&) = delete; + + ~ScopedCallSetHandlerStartupState() { + SetHandlerStartupState(successful_ ? StartupState::kSucceeded + : StartupState::kFailed); + } + + void SetSuccessful() { successful_ = true; } + + private: + bool successful_; +}; + +bool StartHandlerProcess( + std::unique_ptr data) { + CHECK(!IsThreadInLoaderLock()); + + ScopedCallSetHandlerStartupState scoped_startup_state_caller; + + std::wstring command_line; + AppendCommandLineArgument(data->handler.value(), &command_line); + for (const std::string& argument : data->arguments) { + AppendCommandLineArgument(base::UTF8ToWide(argument), &command_line); + } + if (!data->database.value().empty()) { + AppendCommandLineArgument( + FormatArgumentString("database", data->database.value()), + &command_line); + } + if (!data->metrics_dir.value().empty()) { + AppendCommandLineArgument( + FormatArgumentString("metrics-dir", data->metrics_dir.value()), + &command_line); + } + if (!data->url.empty()) { + AppendCommandLineArgument( + FormatArgumentString("url", base::UTF8ToWide(data->url)), + &command_line); + } + for (const auto& kv : data->annotations) { + AppendCommandLineArgument( + FormatArgumentString("annotation", + base::UTF8ToWide(kv.first + '=' + kv.second)), + &command_line); + } + for (const base::FilePath& attachment : data->attachments) { + AppendCommandLineArgument( + FormatArgumentString("attachment", attachment.value()), &command_line); + } + + ScopedKernelHANDLE this_process( + OpenProcess(kXPProcessAllAccess, true, GetCurrentProcessId())); + if (!this_process.is_valid()) { + PLOG(ERROR) << "OpenProcess"; + return false; + } + + InitialClientData initial_client_data( + g_signal_exception, + g_signal_non_crash_dump, + g_non_crash_dump_done, + data->ipc_pipe_handle.get(), + this_process.get(), + FromPointerCast(&g_crash_exception_information), + FromPointerCast(&g_non_crash_exception_information), + FromPointerCast(&g_critical_section_with_debug_info)); + AppendCommandLineArgument( + base::UTF8ToWide(std::string("--initial-client-data=") + + initial_client_data.StringRepresentation()), + &command_line); + + BOOL rv; + DWORD creation_flags; + STARTUPINFOEX startup_info = {}; + startup_info.StartupInfo.dwFlags = STARTF_USESTDHANDLES; + startup_info.StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + startup_info.StartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + startup_info.StartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE); + + std::vector handle_list; + std::unique_ptr proc_thread_attribute_list_storage; + ScopedProcThreadAttributeList proc_thread_attribute_list_owner; + + static const auto initialize_proc_thread_attribute_list = + GET_FUNCTION(L"kernel32.dll", ::InitializeProcThreadAttributeList); + static const auto update_proc_thread_attribute = + initialize_proc_thread_attribute_list + ? GET_FUNCTION(L"kernel32.dll", ::UpdateProcThreadAttribute) + : nullptr; + if (!initialize_proc_thread_attribute_list || !update_proc_thread_attribute) { + // The OS doesn’t allow handle inheritance to be restricted, so the handler + // will inherit every inheritable handle. + creation_flags = 0; + startup_info.StartupInfo.cb = sizeof(startup_info.StartupInfo); + } else { + // Restrict handle inheritance to just those needed in the handler. + + creation_flags = EXTENDED_STARTUPINFO_PRESENT; + startup_info.StartupInfo.cb = sizeof(startup_info); + SIZE_T size; + rv = initialize_proc_thread_attribute_list(nullptr, 1, 0, &size); + if (rv) { + LOG(ERROR) << "InitializeProcThreadAttributeList (size) succeeded, " + "expected failure"; + return false; + } else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + PLOG(ERROR) << "InitializeProcThreadAttributeList (size)"; + return false; + } + + proc_thread_attribute_list_storage.reset(new uint8_t[size]); + startup_info.lpAttributeList = + reinterpret_cast( + proc_thread_attribute_list_storage.get()); + rv = initialize_proc_thread_attribute_list( + startup_info.lpAttributeList, 1, 0, &size); + if (!rv) { + PLOG(ERROR) << "InitializeProcThreadAttributeList"; + return false; + } + proc_thread_attribute_list_owner.reset(startup_info.lpAttributeList); + + handle_list.reserve(8); + handle_list.push_back(g_signal_exception); + handle_list.push_back(g_signal_non_crash_dump); + handle_list.push_back(g_non_crash_dump_done); + handle_list.push_back(data->ipc_pipe_handle.get()); + handle_list.push_back(this_process.get()); + AddHandleToListIfValidAndInheritable(&handle_list, + startup_info.StartupInfo.hStdInput); + AddHandleToListIfValidAndInheritable(&handle_list, + startup_info.StartupInfo.hStdOutput); + AddHandleToListIfValidAndInheritable(&handle_list, + startup_info.StartupInfo.hStdError); + rv = update_proc_thread_attribute( + startup_info.lpAttributeList, + 0, + PROC_THREAD_ATTRIBUTE_HANDLE_LIST, + &handle_list[0], + handle_list.size() * sizeof(handle_list[0]), + nullptr, + nullptr); + if (!rv) { + PLOG(ERROR) << "UpdateProcThreadAttribute"; + return false; + } + } + + // If the embedded crashpad handler is being started via an entry point in a + // DLL (the handler executable is rundll32.exe), then don't pass + // the application name to CreateProcess as this appears to generate an + // invalid command line where the first argument needed by rundll32 is not in + // the correct format as required in: + // https://support.microsoft.com/en-ca/help/164787/info-windows-rundll-and-rundll32-interface + const base::WStringPiece kRunDll32Exe(L"rundll32.exe"); + bool is_embedded_in_dll = false; + if (data->handler.value().size() >= kRunDll32Exe.size() && + _wcsicmp(data->handler.value() + .substr(data->handler.value().size() - kRunDll32Exe.size()) + .c_str(), + kRunDll32Exe.data()) == 0) { + is_embedded_in_dll = true; + } + + PROCESS_INFORMATION process_info; + rv = CreateProcess( + is_embedded_in_dll ? nullptr : data->handler.value().c_str(), + &command_line[0], + nullptr, + nullptr, + true, + creation_flags, + nullptr, + nullptr, + &startup_info.StartupInfo, + &process_info); + if (!rv) { + PLOG(ERROR) << "CreateProcess"; + return false; + } + + rv = CloseHandle(process_info.hThread); + PLOG_IF(WARNING, !rv) << "CloseHandle thread"; + + rv = CloseHandle(process_info.hProcess); + PLOG_IF(WARNING, !rv) << "CloseHandle process"; + + // It is important to close our side of the pipe here before confirming that + // we can communicate with the server. By doing so, the only remaining copy of + // the server side of the pipe belongs to the exception handler process we + // just spawned. Otherwise, the pipe will continue to exist indefinitely, so + // the connection loop will not detect that it will never be serviced. + data->ipc_pipe_handle.reset(); + + // Confirm that the server is waiting for connections before continuing. + ClientToServerMessage message = {}; + message.type = ClientToServerMessage::kPing; + ServerToClientMessage response = {}; + if (!SendToCrashHandlerServer(data->ipc_pipe, message, &response)) { + return false; + } + + scoped_startup_state_caller.SetSuccessful(); + return true; +} + +DWORD WINAPI BackgroundHandlerStartThreadProc(void* data) { + std::unique_ptr data_as_ptr( + reinterpret_cast(data)); + return StartHandlerProcess(std::move(data_as_ptr)) ? 0 : 1; +} + +void CommonInProcessInitialization() { + // We create this dummy CRITICAL_SECTION with the + // RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO flag set to have an entry point + // into the doubly-linked list of RTL_CRITICAL_SECTION_DEBUG objects. This + // allows us to walk the list at crash time to gather data for !locks. A + // debugger would instead inspect ntdll!RtlCriticalSectionList to get the head + // of the list. But that is not an exported symbol, so on an arbitrary client + // machine, we don't have a way of getting that pointer. + InitializeCriticalSectionWithDebugInfoIfPossible( + &g_critical_section_with_debug_info); + + g_non_crash_dump_lock = new base::Lock(); +} + +void RegisterHandlers() { + SetUnhandledExceptionFilter(&UnhandledExceptionHandler); + + // The Windows CRT's signal.h lists: + // - SIGINT + // - SIGILL + // - SIGFPE + // - SIGSEGV + // - SIGTERM + // - SIGBREAK + // - SIGABRT + // SIGILL and SIGTERM are documented as not being generated. SIGBREAK and + // SIGINT are for Ctrl-Break and Ctrl-C, and aren't something for which + // capturing a dump is warranted. SIGFPE and SIGSEGV are captured as regular + // exceptions through the unhandled exception filter. This leaves SIGABRT. In + // the standard CRT, abort() is implemented as a synchronous call to the + // SIGABRT signal handler if installed, but after doing so, the unhandled + // exception filter is not triggered (it instead __fastfail()s). So, register + // to handle SIGABRT to catch abort() calls, as client code might use this and + // expect it to cause a crash dump. This will only work when the abort() + // that's called in client code is the same (or has the same behavior) as the + // one in use here. + void (*rv)(int) = signal(SIGABRT, HandleAbortSignal); + DCHECK_NE(rv, SIG_ERR); +} + +} // namespace + +CrashpadClient::CrashpadClient() : ipc_pipe_(), handler_start_thread_() {} + +CrashpadClient::~CrashpadClient() {} + +bool CrashpadClient::StartHandler( + const base::FilePath& handler, + const base::FilePath& database, + const base::FilePath& metrics_dir, + const std::string& url, + const std::map& annotations, + const std::vector& arguments, + bool restartable, + bool asynchronous_start, + const std::vector& attachments) { + DCHECK(ipc_pipe_.empty()); + + // Both the pipe and the signalling events have to be created on the main + // thread (not the spawning thread) so that they're valid after we return from + // this function. + ScopedFileHANDLE ipc_pipe_handle; + CreatePipe(&ipc_pipe_, &ipc_pipe_handle); + + SECURITY_ATTRIBUTES security_attributes = {0}; + security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); + security_attributes.bInheritHandle = true; + + g_signal_exception = + CreateEvent(&security_attributes, false /* auto reset */, false, nullptr); + g_signal_non_crash_dump = + CreateEvent(&security_attributes, false /* auto reset */, false, nullptr); + g_non_crash_dump_done = + CreateEvent(&security_attributes, false /* auto reset */, false, nullptr); + + CommonInProcessInitialization(); + + RegisterHandlers(); + + auto data = new BackgroundHandlerStartThreadData(handler, + database, + metrics_dir, + url, + annotations, + arguments, + attachments, + ipc_pipe_, + std::move(ipc_pipe_handle)); + + if (asynchronous_start) { + // It is important that the current thread not be synchronized with the + // thread that is created here. StartHandler() needs to be callable inside a + // DllMain(). In that case, the background thread will not start until the + // current DllMain() completes, which would cause deadlock if it was waited + // upon. + handler_start_thread_.reset(CreateThread(nullptr, + 0, + &BackgroundHandlerStartThreadProc, + reinterpret_cast(data), + 0, + nullptr)); + if (!handler_start_thread_.is_valid()) { + PLOG(ERROR) << "CreateThread"; + SetHandlerStartupState(StartupState::kFailed); + return false; + } + + // In asynchronous mode, we can't report on the overall success or failure + // of initialization at this point. + return true; + } else { + return StartHandlerProcess( + std::unique_ptr(data)); + } +} + +bool CrashpadClient::SetHandlerIPCPipe(const std::wstring& ipc_pipe) { + DCHECK(ipc_pipe_.empty()); + DCHECK(!ipc_pipe.empty()); + + ipc_pipe_ = ipc_pipe; + + DCHECK(!ipc_pipe_.empty()); + DCHECK_EQ(g_signal_exception, INVALID_HANDLE_VALUE); + DCHECK_EQ(g_signal_non_crash_dump, INVALID_HANDLE_VALUE); + DCHECK_EQ(g_non_crash_dump_done, INVALID_HANDLE_VALUE); + DCHECK(!g_critical_section_with_debug_info.DebugInfo); + DCHECK(!g_non_crash_dump_lock); + + ClientToServerMessage message; + memset(&message, 0, sizeof(message)); + message.type = ClientToServerMessage::kRegister; + message.registration.version = RegistrationRequest::kMessageVersion; + message.registration.client_process_id = GetCurrentProcessId(); + message.registration.crash_exception_information = + FromPointerCast(&g_crash_exception_information); + message.registration.non_crash_exception_information = + FromPointerCast(&g_non_crash_exception_information); + + CommonInProcessInitialization(); + + message.registration.critical_section_address = + FromPointerCast(&g_critical_section_with_debug_info); + + ServerToClientMessage response = {}; + + if (!SendToCrashHandlerServer(ipc_pipe_, message, &response)) { + return false; + } + + SetHandlerStartupState(StartupState::kSucceeded); + + RegisterHandlers(); + + // The server returns these already duplicated to be valid in this process. + g_signal_exception = + IntToHandle(response.registration.request_crash_dump_event); + g_signal_non_crash_dump = + IntToHandle(response.registration.request_non_crash_dump_event); + g_non_crash_dump_done = + IntToHandle(response.registration.non_crash_dump_completed_event); + + return true; +} + +std::wstring CrashpadClient::GetHandlerIPCPipe() const { + DCHECK(!ipc_pipe_.empty()); + return ipc_pipe_; +} + +bool CrashpadClient::WaitForHandlerStart(unsigned int timeout_ms) { + DCHECK(handler_start_thread_.is_valid()); + DWORD result = WaitForSingleObject(handler_start_thread_.get(), timeout_ms); + if (result == WAIT_TIMEOUT) { + LOG(ERROR) << "WaitForSingleObject timed out"; + return false; + } else if (result == WAIT_ABANDONED) { + LOG(ERROR) << "WaitForSingleObject abandoned"; + return false; + } else if (result != WAIT_OBJECT_0) { + PLOG(ERROR) << "WaitForSingleObject"; + return false; + } + + DWORD exit_code; + if (!GetExitCodeThread(handler_start_thread_.get(), &exit_code)) { + PLOG(ERROR) << "GetExitCodeThread"; + return false; + } + + handler_start_thread_.reset(); + return exit_code == 0; +} + +// static +void CrashpadClient::DumpWithoutCrash(const CONTEXT& context) { + if (g_signal_non_crash_dump == INVALID_HANDLE_VALUE || + g_non_crash_dump_done == INVALID_HANDLE_VALUE) { + LOG(ERROR) << "not connected"; + return; + } + + if (BlockUntilHandlerStartedOrFailed() == StartupState::kFailed) { + // If we know for certain that the handler has failed to start, then abort + // here, as we would otherwise wait indefinitely for the + // g_non_crash_dump_done event that would never be signalled. + LOG(ERROR) << "crash server failed to launch, no dump captured"; + return; + } + + // In the non-crashing case, we aren't concerned about avoiding calls into + // Win32 APIs, so just use regular locking here in case of multiple threads + // calling this function. If a crash occurs while we're in here, the worst + // that can happen is that the server captures a partial dump for this path + // because another thread’s crash processing finished and the process was + // terminated before this thread’s non-crash processing could be completed. + base::AutoLock lock(*g_non_crash_dump_lock); + + // Create a fake EXCEPTION_POINTERS to give the handler something to work + // with. + EXCEPTION_POINTERS exception_pointers = {}; + + // This is logically const, but EXCEPTION_POINTERS does not declare it as + // const, so we have to cast that away from the argument. + exception_pointers.ContextRecord = const_cast(&context); + + // We include a fake exception and use a code of '0x517a7ed' (something like + // "simulated") so that it's relatively obvious in windbg that it's not + // actually an exception. Most values in + // https://msdn.microsoft.com/library/aa363082.aspx have some of the top + // nibble set, so we make sure to pick a value that doesn't, so as to be + // unlikely to conflict. + constexpr uint32_t kSimulatedExceptionCode = 0x517a7ed; + EXCEPTION_RECORD record = {}; + record.ExceptionCode = kSimulatedExceptionCode; + record.ExceptionAddress = ProgramCounterFromCONTEXT(&context); + + exception_pointers.ExceptionRecord = &record; + + g_non_crash_exception_information.thread_id = GetCurrentThreadId(); + g_non_crash_exception_information.exception_pointers = + FromPointerCast(&exception_pointers); + + bool set_event_result = !!SetEvent(g_signal_non_crash_dump); + PLOG_IF(ERROR, !set_event_result) << "SetEvent"; + + DWORD wfso_result = WaitForSingleObject(g_non_crash_dump_done, INFINITE); + PLOG_IF(ERROR, wfso_result != WAIT_OBJECT_0) << "WaitForSingleObject"; +} + +// static +void CrashpadClient::DumpAndCrash(EXCEPTION_POINTERS* exception_pointers) { + if (g_signal_exception == INVALID_HANDLE_VALUE) { + LOG(ERROR) << "not connected"; + SafeTerminateProcess(GetCurrentProcess(), + kTerminationCodeNotConnectedToHandler); + return; + } + + // We don't need to check for handler startup here, as + // UnhandledExceptionHandler() necessarily does that. + + UnhandledExceptionHandler(exception_pointers); +} + +// static +bool CrashpadClient::DumpAndCrashTargetProcess(HANDLE process, + HANDLE blame_thread, + DWORD exception_code) { + // Confirm we're on Vista or later. + const DWORD version = GetVersion(); + const DWORD major_version = LOBYTE(LOWORD(version)); + if (major_version < 6) { + LOG(ERROR) << "unavailable before Vista"; + return false; + } + + // Confirm that our bitness is the same as the process we're crashing. + ProcessInfo process_info; + if (!process_info.Initialize(process)) { + LOG(ERROR) << "ProcessInfo::Initialize"; + return false; + } +#if defined(ARCH_CPU_64_BITS) + if (!process_info.Is64Bit()) { + LOG(ERROR) << "DumpAndCrashTargetProcess currently not supported x64->x86"; + return false; + } +#endif // ARCH_CPU_64_BITS + + ScopedProcessSuspend suspend(process); + + // If no thread handle was provided, or the thread has already exited, we pass + // 0 to the handler, which indicates no fake exception record to be created. + DWORD thread_id = 0; + if (blame_thread) { + // Now that we've suspended the process, if our thread hasn't exited, we + // know we're relatively safe to pass the thread id through. + if (WaitForSingleObject(blame_thread, 0) == WAIT_TIMEOUT) { + static const auto get_thread_id = + GET_FUNCTION_REQUIRED(L"kernel32.dll", ::GetThreadId); + thread_id = get_thread_id(blame_thread); + } + } + + constexpr size_t kInjectBufferSize = 4 * 1024; + WinVMAddress inject_memory = + FromPointerCast(VirtualAllocEx(process, + nullptr, + kInjectBufferSize, + MEM_RESERVE | MEM_COMMIT, + PAGE_READWRITE)); + if (!inject_memory) { + PLOG(ERROR) << "VirtualAllocEx"; + return false; + } + + // Because we're the same bitness as our target, we can rely kernel32 being + // loaded at the same address in our process as the target, and just look up + // its address here. + WinVMAddress raise_exception_address = + FromPointerCast(&RaiseException); + + WinVMAddress code_entry_point = 0; + std::vector data_to_write; + if (process_info.Is64Bit()) { + // Data written is first, the data for the 4th argument (lpArguments) to + // RaiseException(). A two element array: + // + // DWORD64: thread_id + // DWORD64: exception_code + // + // Following that, code which sets the arguments to RaiseException() and + // then calls it: + // + // mov r9, + // mov r8d, 2 ; nNumberOfArguments + // mov edx, 1 ; dwExceptionFlags = EXCEPTION_NONCONTINUABLE + // mov ecx, 0xcca11ed ; dwExceptionCode, interpreted specially by the + // ; handler. + // jmp + // + // Note that the first three arguments to RaiseException() are DWORDs even + // on x64, so only the 4th argument (a pointer) is a full-width register. + // + // We also don't need to set up a stack or use call, since the only + // registers modified are volatile ones, and we can just jmp straight to + // RaiseException(). + + // The data array. + AddUint64(&data_to_write, thread_id); + AddUint64(&data_to_write, exception_code); + + // The thread entry point. + code_entry_point = inject_memory + data_to_write.size(); + + // r9 = pointer to data. + data_to_write.push_back(0x49); + data_to_write.push_back(0xb9); + AddUint64(&data_to_write, inject_memory); + + // r8d = 2 for nNumberOfArguments. + data_to_write.push_back(0x41); + data_to_write.push_back(0xb8); + AddUint32(&data_to_write, 2); + + // edx = 1 for dwExceptionFlags. + data_to_write.push_back(0xba); + AddUint32(&data_to_write, 1); + + // ecx = kTriggeredExceptionCode for dwExceptionCode. + data_to_write.push_back(0xb9); + AddUint32(&data_to_write, ExceptionCodes::kTriggeredExceptionCode); + + // jmp to RaiseException() via rax. + data_to_write.push_back(0x48); // mov rax, imm. + data_to_write.push_back(0xb8); + AddUint64(&data_to_write, raise_exception_address); + data_to_write.push_back(0xff); // jmp rax. + data_to_write.push_back(0xe0); + } else { + // Data written is first, the data for the 4th argument (lpArguments) to + // RaiseException(). A two element array: + // + // DWORD: thread_id + // DWORD: exception_code + // + // Following that, code which pushes our arguments to RaiseException() and + // then calls it: + // + // push + // push 2 ; nNumberOfArguments + // push 1 ; dwExceptionFlags = EXCEPTION_NONCONTINUABLE + // push 0xcca11ed ; dwExceptionCode, interpreted specially by the handler. + // call + // ud2 ; Generate invalid opcode to make sure we still crash if we return + // ; for some reason. + // + // No need to clean up the stack, as RaiseException() is __stdcall. + + // The data array. + AddUint32(&data_to_write, thread_id); + AddUint32(&data_to_write, exception_code); + + // The thread entry point. + code_entry_point = inject_memory + data_to_write.size(); + + // Push data address. + data_to_write.push_back(0x68); + AddUint32(&data_to_write, static_cast(inject_memory)); + + // Push 2 for nNumberOfArguments. + data_to_write.push_back(0x6a); + data_to_write.push_back(2); + + // Push 1 for dwExceptionCode. + data_to_write.push_back(0x6a); + data_to_write.push_back(1); + + // Push dwExceptionFlags. + data_to_write.push_back(0x68); + AddUint32(&data_to_write, kTriggeredExceptionCode); + + // Relative call to RaiseException(). + int64_t relative_address_to_raise_exception = + raise_exception_address - (inject_memory + data_to_write.size() + 5); + data_to_write.push_back(0xe8); + AddUint32(&data_to_write, + static_cast(relative_address_to_raise_exception)); + + // ud2. + data_to_write.push_back(0x0f); + data_to_write.push_back(0x0b); + } + + DCHECK_LT(data_to_write.size(), kInjectBufferSize); + + SIZE_T bytes_written; + if (!WriteProcessMemory(process, + reinterpret_cast(inject_memory), + data_to_write.data(), + data_to_write.size(), + &bytes_written)) { + PLOG(ERROR) << "WriteProcessMemory"; + return false; + } + + if (bytes_written != data_to_write.size()) { + LOG(ERROR) << "WriteProcessMemory unexpected number of bytes"; + return false; + } + + if (!FlushInstructionCache( + process, reinterpret_cast(inject_memory), bytes_written)) { + PLOG(ERROR) << "FlushInstructionCache"; + return false; + } + + DWORD old_protect; + if (!VirtualProtectEx(process, + reinterpret_cast(inject_memory), + kInjectBufferSize, + PAGE_EXECUTE_READ, + &old_protect)) { + PLOG(ERROR) << "VirtualProtectEx"; + return false; + } + + // Cause an exception in the target process by creating a thread which calls + // RaiseException with our arguments above. Note that we cannot get away with + // using DebugBreakProcess() (nothing happens unless a debugger is attached) + // and we cannot get away with CreateRemoteThread() because it doesn't work if + // the target is hung waiting for the loader lock. We use NtCreateThreadEx() + // with the SKIP_THREAD_ATTACH flag, which skips various notifications, + // letting this cause an exception, even when the target is stuck in the + // loader lock. + HANDLE injected_thread; + + // This is what DebugBreakProcess() uses. + constexpr size_t kStackSize = 0x4000; + + NTSTATUS status = NtCreateThreadEx(&injected_thread, + STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL, + nullptr, + process, + reinterpret_cast(code_entry_point), + nullptr, + THREAD_CREATE_FLAGS_SKIP_THREAD_ATTACH, + 0, + kStackSize, + 0, + nullptr); + if (!NT_SUCCESS(status)) { + NTSTATUS_LOG(ERROR, status) << "NtCreateThreadEx"; + return false; + } + + // The injected thread raises an exception and ultimately results in process + // termination. The suspension must be made aware that the process may be + // terminating, otherwise it’ll log an extraneous error. + suspend.TolerateTermination(); + + bool result = true; + if (WaitForSingleObject(injected_thread, 60 * 1000) != WAIT_OBJECT_0) { + PLOG(ERROR) << "WaitForSingleObject"; + result = false; + } + + status = NtClose(injected_thread); + if (!NT_SUCCESS(status)) { + NTSTATUS_LOG(ERROR, status) << "NtClose"; + result = false; + } + + return result; +} + +// static +void CrashpadClient::SetFirstChanceExceptionHandler( + FirstChanceHandlerWin handler) { + first_chance_handler_ = handler; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/crashpad_client_win_test.cc b/shared/sentry/external/crashpad/client/crashpad_client_win_test.cc new file mode 100644 index 000000000..f15887b9f --- /dev/null +++ b/shared/sentry/external/crashpad/client/crashpad_client_win_test.cc @@ -0,0 +1,197 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "client/crashpad_client.h" + +#include + +#include "base/files/file_path.h" +#include "gtest/gtest.h" +#include "test/test_paths.h" +#include "test/scoped_temp_dir.h" +#include "test/win/win_multiprocess.h" +#include "test/win/win_multiprocess_with_temp_dir.h" +#include "util/win/scoped_handle.h" +#include "util/win/termination_codes.h" + +namespace crashpad { +namespace test { +namespace { + +void StartAndUseHandler(const base::FilePath& temp_dir) { + base::FilePath handler_path = TestPaths::Executable().DirName().Append( + FILE_PATH_LITERAL("crashpad_handler.com")); + + CrashpadClient client; + ASSERT_TRUE(client.StartHandler(handler_path, + temp_dir, + base::FilePath(), + "", + std::map(), + std::vector(), + true, + true)); + ASSERT_TRUE(client.WaitForHandlerStart(INFINITE)); +} + +class StartWithInvalidHandles final : public WinMultiprocessWithTempDir { + public: + StartWithInvalidHandles() : WinMultiprocessWithTempDir() {} + ~StartWithInvalidHandles() {} + + private: + void WinMultiprocessParent() override {} + + void WinMultiprocessChild() override { + HANDLE original_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + HANDLE original_stderr = GetStdHandle(STD_ERROR_HANDLE); + SetStdHandle(STD_OUTPUT_HANDLE, INVALID_HANDLE_VALUE); + SetStdHandle(STD_ERROR_HANDLE, INVALID_HANDLE_VALUE); + + StartAndUseHandler(GetTempDirPath()); + + SetStdHandle(STD_OUTPUT_HANDLE, original_stdout); + SetStdHandle(STD_ERROR_HANDLE, original_stderr); + } +}; + +TEST(CrashpadClient, StartWithInvalidHandles) { + WinMultiprocessWithTempDir::Run(); +} + +class StartWithSameStdoutStderr final : public WinMultiprocessWithTempDir { + public: + StartWithSameStdoutStderr() : WinMultiprocessWithTempDir() {} + ~StartWithSameStdoutStderr() {} + + private: + void WinMultiprocessParent() override {} + + void WinMultiprocessChild() override { + HANDLE original_stdout = GetStdHandle(STD_OUTPUT_HANDLE); + HANDLE original_stderr = GetStdHandle(STD_ERROR_HANDLE); + SetStdHandle(STD_OUTPUT_HANDLE, original_stderr); + + StartAndUseHandler(GetTempDirPath()); + + SetStdHandle(STD_OUTPUT_HANDLE, original_stdout); + } +}; + +TEST(CrashpadClient, StartWithSameStdoutStderr) { + WinMultiprocessWithTempDir::Run(); +} + +void StartAndUseBrokenHandler(CrashpadClient* client) { + ScopedTempDir temp_dir; + base::FilePath handler_path = TestPaths::Executable().DirName().Append( + FILE_PATH_LITERAL("fake_handler_that_crashes_at_startup.exe")); + ASSERT_TRUE(client->StartHandler(handler_path, + temp_dir.path(), + base::FilePath(), + "", + std::map(), + std::vector(), + false, + true)); +} + +class HandlerLaunchFailureCrash : public WinMultiprocess { + public: + HandlerLaunchFailureCrash() : WinMultiprocess() {} + + private: + void WinMultiprocessParent() override { + SetExpectedChildExitCode(crashpad::kTerminationCodeCrashNoDump); + } + + void WinMultiprocessChild() override { + CrashpadClient client; + StartAndUseBrokenHandler(&client); + __debugbreak(); + exit(0); + } +}; + +#if defined(ADDRESS_SANITIZER) +// https://crbug.com/845011 +#define MAYBE_HandlerLaunchFailureCrash DISABLED_HandlerLaunchFailureCrash +#else +#define MAYBE_HandlerLaunchFailureCrash HandlerLaunchFailureCrash +#endif +TEST(CrashpadClient, MAYBE_HandlerLaunchFailureCrash) { + WinMultiprocess::Run(); +} + +class HandlerLaunchFailureDumpAndCrash : public WinMultiprocess { + public: + HandlerLaunchFailureDumpAndCrash() : WinMultiprocess() {} + + private: + void WinMultiprocessParent() override { + SetExpectedChildExitCode(crashpad::kTerminationCodeCrashNoDump); + } + + void WinMultiprocessChild() override { + CrashpadClient client; + StartAndUseBrokenHandler(&client); + // We don't need to fill this out as we're only checking that we're + // terminated with the correct failure code. + EXCEPTION_POINTERS info = {}; + client.DumpAndCrash(&info); + exit(0); + } +}; + +#if defined(ADDRESS_SANITIZER) +// https://crbug.com/845011 +#define MAYBE_HandlerLaunchFailureDumpAndCrash \ + DISABLED_HandlerLaunchFailureDumpAndCrash +#else +#define MAYBE_HandlerLaunchFailureDumpAndCrash HandlerLaunchFailureDumpAndCrash +#endif +TEST(CrashpadClient, MAYBE_HandlerLaunchFailureDumpAndCrash) { + WinMultiprocess::Run(); +} + +class HandlerLaunchFailureDumpWithoutCrash : public WinMultiprocess { + public: + HandlerLaunchFailureDumpWithoutCrash() : WinMultiprocess() {} + + private: + void WinMultiprocessParent() override { + // DumpWithoutCrash() normally blocks indefinitely. There's no return value, + // but confirm that it exits cleanly because it'll return right away if the + // handler didn't start. + SetExpectedChildExitCode(0); + } + + void WinMultiprocessChild() override { + CrashpadClient client; + StartAndUseBrokenHandler(&client); + // We don't need to fill this out as we're only checking that we're + // terminated with the correct failure code. + CONTEXT context = {}; + client.DumpWithoutCrash(context); + exit(0); + } +}; + +TEST(CrashpadClient, HandlerLaunchFailureDumpWithoutCrash) { + WinMultiprocess::Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/crashpad_info.cc b/shared/sentry/external/crashpad/client/crashpad_info.cc new file mode 100644 index 000000000..8f13276fd --- /dev/null +++ b/shared/sentry/external/crashpad/client/crashpad_info.cc @@ -0,0 +1,138 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "client/crashpad_info.h" + +#include + +#include "build/build_config.h" +#include "util/misc/address_sanitizer.h" +#include "util/misc/from_pointer_cast.h" + +#if BUILDFLAG(IS_APPLE) +#include +#endif + +namespace { + +// Don’t change this when simply adding fields. Readers will size-check the +// structure and ignore fields they’re aware of when not present, as well as +// fields they’re not aware of. Only change this when introducing an +// incompatible layout, with the understanding that existing readers will not +// understand new versions. +constexpr uint32_t kCrashpadInfoVersion = 1; + +} // namespace + +namespace crashpad { + +static_assert(std::is_standard_layout::value, + "CrashpadInfo must be standard layout"); + +// This structure needs to be stored somewhere that is easy to find without +// external information. +// +// It isn’t placed in an unnamed namespace: hopefully, this will catch attempts +// to place multiple copies of this structure into the same module. If that’s +// attempted, and the name of the symbol is the same in each translation unit, +// it will result in a linker error, which is better than having multiple +// structures show up. +// +// This may result in a static module initializer in debug-mode builds, but +// because it’s POD, no code should need to run to initialize this under +// release-mode optimization. + +#if BUILDFLAG(IS_POSIX) +__attribute__(( + +#if BUILDFLAG(IS_APPLE) + // Put the structure in a well-known section name where it can be easily + // found without having to consult the symbol table. + section(SEG_DATA ",crashpad_info"), +#endif + +#if defined(ADDRESS_SANITIZER) + // AddressSanitizer would add a trailing red zone of at least 32 bytes, + // which would be reflected in the size of the custom section. This confuses + // MachOImageReader::GetCrashpadInfo(), which finds that the section’s size + // disagrees with the structure’s size_ field. By specifying an alignment + // greater than the red zone size, the red zone will be suppressed. + aligned(64), +#endif // defined(ADDRESS_SANITIZER) + + // There's no need to expose this as a public symbol from the symbol table. + // All accesses from the outside can locate the well-known section name. + visibility("hidden"), + + // The “used†attribute prevents the structure from being dead-stripped. + used)) + +#elif BUILDFLAG(IS_WIN) + +// Put the struct in a section name CPADinfo where it can be found without the +// symbol table. +#pragma section("CPADinfo", read, write) +__declspec(allocate("CPADinfo")) + +#else // !BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_WIN) +#error Port +#endif // !BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_WIN) + +CrashpadInfo g_crashpad_info; + +extern "C" int* CRASHPAD_NOTE_REFERENCE; + +// static +CrashpadInfo* CrashpadInfo::GetCrashpadInfo() { +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) || \ + BUILDFLAG(IS_FUCHSIA) + // This otherwise-unused reference is used so that any module that + // references GetCrashpadInfo() will also include the note in the + // .note.crashpad.info section. That note in turn contains the address of + // g_crashpad_info. This allows the module reader to find the CrashpadInfo + // structure without requiring the use of the dynamic symbol table. + static volatile int* pointer_to_note_section = CRASHPAD_NOTE_REFERENCE; + (void)pointer_to_note_section; +#endif + return &g_crashpad_info; +} + +CrashpadInfo::CrashpadInfo() + : signature_(kSignature), + size_(sizeof(*this)), + version_(kCrashpadInfoVersion), + indirectly_referenced_memory_cap_(0), + padding_0_(0), + crashpad_handler_behavior_(TriState::kUnset), + system_crash_reporter_forwarding_(TriState::kUnset), + gather_indirectly_referenced_memory_(TriState::kUnset), + padding_1_(0), + extra_memory_ranges_(nullptr), + simple_annotations_(nullptr), + user_data_minidump_stream_head_(nullptr), + annotations_list_(nullptr) {} + +void CrashpadInfo::AddUserDataMinidumpStream(uint32_t stream_type, + const void* data, + size_t size) { + auto to_be_added = new internal::UserDataMinidumpStreamListEntry(); + to_be_added->next = + FromPointerCast(user_data_minidump_stream_head_); + to_be_added->stream_type = stream_type; + to_be_added->base_address = FromPointerCast(data); + to_be_added->size = base::checked_cast(size); + user_data_minidump_stream_head_ = to_be_added; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/crashpad_info.h b/shared/sentry/external/crashpad/client/crashpad_info.h new file mode 100644 index 000000000..6c7ce3277 --- /dev/null +++ b/shared/sentry/external/crashpad/client/crashpad_info.h @@ -0,0 +1,279 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_CLIENT_CRASHPAD_INFO_H_ +#define CRASHPAD_CLIENT_CRASHPAD_INFO_H_ + +#include + +#include "build/build_config.h" +#include "client/annotation_list.h" +#include "client/simple_address_range_bag.h" +#include "client/simple_string_dictionary.h" +#include "util/misc/tri_state.h" + +#if BUILDFLAG(IS_WIN) +#include +#endif // BUILDFLAG(IS_WIN) + +namespace crashpad { + +namespace internal { + +#if BUILDFLAG(IS_IOS) +class InProcessIntermediateDumpHandler; +#endif + +//! \brief A linked list of blocks representing custom streams in the minidump, +//! with addresses (and size) stored as uint64_t to simplify reading from +//! the handler process. +struct UserDataMinidumpStreamListEntry { + //! \brief The address of the next entry in the linked list. + uint64_t next; + + //! \brief The base address of the memory block in the target process' address + //! space that represents the user data stream. + uint64_t base_address; + + //! \brief The size of memory block in the target process' address space that + //! represents the user data stream. + uint64_t size; + + //! \brief The stream type identifier. + uint32_t stream_type; +}; + +} // namespace internal + +//! \brief A structure that can be used by a Crashpad-enabled program to +//! provide information to the Crashpad crash handler. +//! +//! It is possible for one CrashpadInfo structure to appear in each loaded code +//! module in a process, but from the perspective of the user of the client +//! interface, there is only one global CrashpadInfo structure, located in the +//! module that contains the client interface code. +struct CrashpadInfo { + public: + //! \brief Returns the global CrashpadInfo structure. + static CrashpadInfo* GetCrashpadInfo(); + + CrashpadInfo(); + + CrashpadInfo(const CrashpadInfo&) = delete; + CrashpadInfo& operator=(const CrashpadInfo&) = delete; + + //! \brief Sets the bag of extra memory ranges to be included in the snapshot. + //! + //! Extra memory ranges may exist in \a address_range_bag at the time that + //! this method is called, or they may be added, removed, or modified in \a + //! address_range_bag after this method is called. + //! + //! TODO(scottmg) This is currently only supported on Windows. + //! + //! \param[in] address_range_bag A bag of address ranges. The CrashpadInfo + //! object does not take ownership of the SimpleAddressRangeBag object. + //! It is the caller’s responsibility to ensure that this pointer remains + //! valid while it is in effect for a CrashpadInfo object. + void set_extra_memory_ranges(SimpleAddressRangeBag* address_range_bag) { + extra_memory_ranges_ = address_range_bag; + } + + //! \brief Sets the simple annotations dictionary. + //! + //! Simple annotations set on a CrashpadInfo structure are interpreted by + //! Crashpad as module-level annotations. + //! + //! Annotations may exist in \a simple_annotations at the time that this + //! method is called, or they may be added, removed, or modified in \a + //! simple_annotations after this method is called. + //! + //! \param[in] simple_annotations A dictionary that maps string keys to string + //! values. The CrashpadInfo object does not take ownership of the + //! SimpleStringDictionary object. It is the caller’s responsibility to + //! ensure that this pointer remains valid while it is in effect for a + //! CrashpadInfo object. + //! + //! \sa simple_annotations() + void set_simple_annotations(SimpleStringDictionary* simple_annotations) { + simple_annotations_ = simple_annotations; + } + + //! \return The simple annotations dictionary. + //! + //! \sa set_simple_annotations() + SimpleStringDictionary* simple_annotations() const { + return simple_annotations_; + } + + //! \brief Sets the annotations list. + //! + //! Unlike the \a simple_annotations structure, the \a annotations can + //! typed data and it is not limited to a dictionary form. Annotations are + //! interpreted by Crashpad as module-level annotations. + //! + //! Annotations may exist in \a list at the time that this method is called, + //! or they may be added, removed, or modified in \a list after this method is + //! called. + //! + //! \param[in] list A list of set Annotation objects that maintain arbitrary, + //! typed key-value state. The CrashpadInfo object does not take ownership + //! of the AnnotationsList object. It is the caller’s responsibility to + //! ensure that this pointer remains valid while it is in effect for a + //! CrashpadInfo object. + //! + //! \sa annotations_list() + //! \sa AnnotationList::Register() + void set_annotations_list(AnnotationList* list) { annotations_list_ = list; } + + //! \return The annotations list. + //! + //! \sa set_annotations_list() + //! \sa AnnotationList::Get() + //! \sa AnnotationList::Register() + AnnotationList* annotations_list() const { return annotations_list_; } + + //! \brief Enables or disables Crashpad handler processing. + //! + //! When handling an exception, the Crashpad handler will scan all modules in + //! a process. The first one that has a CrashpadInfo structure populated with + //! a value other than TriState::kUnset for this field will dictate whether + //! the handler is functional or not. If all modules with a CrashpadInfo + //! structure specify TriState::kUnset, the handler will be enabled. If + //! disabled, the Crashpad handler will still run and receive exceptions, but + //! will not take any action on an exception on its own behalf, except for the + //! action necessary to determine that it has been disabled. + //! + //! The Crashpad handler should not normally be disabled. More commonly, it is + //! appropriate to disable crash report upload by calling + //! Settings::SetUploadsEnabled(). + void set_crashpad_handler_behavior(TriState crashpad_handler_behavior) { + crashpad_handler_behavior_ = crashpad_handler_behavior; + } + + //! \brief Enables or disables Crashpad forwarding of exceptions to the + //! system’s crash reporter. + //! + //! When handling an exception, the Crashpad handler will scan all modules in + //! a process. The first one that has a CrashpadInfo structure populated with + //! a value other than TriState::kUnset for this field will dictate whether + //! the exception is forwarded to the system’s crash reporter. If all modules + //! with a CrashpadInfo structure specify TriState::kUnset, forwarding will be + //! enabled. Unless disabled, forwarding may still occur if the Crashpad + //! handler is disabled by SetCrashpadHandlerState(). Even when forwarding is + //! enabled, the Crashpad handler may choose not to forward all exceptions to + //! the system’s crash reporter in cases where it has reason to believe that + //! the system’s crash reporter would not normally have handled the exception + //! in Crashpad’s absence. + void set_system_crash_reporter_forwarding( + TriState system_crash_reporter_forwarding) { + system_crash_reporter_forwarding_ = system_crash_reporter_forwarding; + } + + //! \brief Enables or disables Crashpad capturing indirectly referenced memory + //! in the minidump. + //! + //! When handling an exception, the Crashpad handler will scan all modules in + //! a process. The first one that has a CrashpadInfo structure populated with + //! a value other than TriState::kUnset for this field will dictate whether + //! the extra memory is captured. + //! + //! This causes Crashpad to include pages of data referenced by locals or + //! other stack memory. Turning this on can increase the size of the minidump + //! significantly. + //! + //! \param[in] gather_indirectly_referenced_memory Whether extra memory should + //! be gathered. + //! \param[in] limit The amount of memory in bytes after which no more + //! indirectly gathered memory should be captured. This value is only used + //! when \a gather_indirectly_referenced_memory is TriState::kEnabled. + void set_gather_indirectly_referenced_memory( + TriState gather_indirectly_referenced_memory, + uint32_t limit) { + gather_indirectly_referenced_memory_ = gather_indirectly_referenced_memory; + indirectly_referenced_memory_cap_ = limit; + } + + //! \brief Adds a custom stream to the minidump. + //! + //! The memory block referenced by \a data and \a size will added to the + //! minidump as separate stream with type \a stream_type. The memory referred + //! to by \a data and \a size is owned by the caller and must remain valid + //! while it is in effect for the CrashpadInfo object. + //! + //! Note that streams will appear in the minidump in the reverse order to + //! which they are added. + //! + //! TODO(scottmg) This is currently not supported on Mac. + //! + //! \param[in] stream_type The stream type identifier to use. This should be + //! normally be larger than `MINIDUMP_STREAM_TYPE::LastReservedStream` + //! which is `0xffff`. + //! \param[in] data The base pointer of the stream data. + //! \param[in] size The size of the stream data. + void AddUserDataMinidumpStream(uint32_t stream_type, + const void* data, + size_t size); + + enum : uint32_t { + kSignature = 'CPad', + }; + + protected: +#if BUILDFLAG(IS_IOS) + friend class internal::InProcessIntermediateDumpHandler; +#endif + + uint32_t signature() const { return signature_; } + uint32_t version() const { return version_; } + uint32_t size() const { return size_; } + + private: + // The compiler won’t necessarily see anyone using these fields, but it + // shouldn’t warn about that. These fields aren’t intended for use by the + // process they’re found in, they’re supposed to be read by the crash + // reporting process. +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-private-field" +#endif + + // Fields present in version 1, subject to a check of the size_ field: + uint32_t signature_; // kSignature + uint32_t size_; // The size of the entire CrashpadInfo structure. + uint32_t version_; // kCrashpadInfoVersion + uint32_t indirectly_referenced_memory_cap_; + uint32_t padding_0_; + TriState crashpad_handler_behavior_; + TriState system_crash_reporter_forwarding_; + TriState gather_indirectly_referenced_memory_; + uint8_t padding_1_; + SimpleAddressRangeBag* extra_memory_ranges_; // weak + SimpleStringDictionary* simple_annotations_; // weak + internal::UserDataMinidumpStreamListEntry* user_data_minidump_stream_head_; + AnnotationList* annotations_list_; // weak + + // It’s generally safe to add new fields without changing + // kCrashpadInfoVersion, because readers should check size_ and ignore fields + // that aren’t present, as well as unknown fields. + // + // Adding fields? Consider snapshot/crashpad_info_size_test_module.cc too. + +#if defined(__clang__) +#pragma clang diagnostic pop +#endif +}; + +} // namespace crashpad + +#endif // CRASHPAD_CLIENT_CRASHPAD_INFO_H_ diff --git a/shared/sentry/external/crashpad/client/crashpad_info_note.S b/shared/sentry/external/crashpad/client/crashpad_info_note.S new file mode 100644 index 000000000..80de22d60 --- /dev/null +++ b/shared/sentry/external/crashpad/client/crashpad_info_note.S @@ -0,0 +1,68 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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. + +// This note section is used on ELF platforms to give ElfImageReader a method +// of finding the instance of CrashpadInfo g_crashpad_info without requiring +// that symbol to be in the dynamic symbol table. + +#include "util/misc/elf_note_types.h" +#include "util/misc/arm64_pac_bti.S" + +// namespace crashpad { +// CrashpadInfo g_crashpad_info; +// } // namespace crashpad +#define CRASHPAD_INFO_SYMBOL _ZN8crashpad15g_crashpad_infoE + +#define NOTE_ALIGN 4 + + // This section must be "a"llocated so that it appears in the final binary at + // runtime. The reference to CRASHPAD_INFO_SYMBOL uses an offset relative to + // this note to avoid making this note writable, which triggers a bug in GNU + // ld, or adding text relocations which require the target system to allow + // making text segments writable. https://crbug.com/crashpad/260. + .section .note.crashpad.info,"a",%note + .balign NOTE_ALIGN +CRASHPAD_NOTE: + .long name_end - name // namesz + .long desc_end - desc // descsz + .long CRASHPAD_ELF_NOTE_TYPE_CRASHPAD_INFO // type +name: + .asciz CRASHPAD_ELF_NOTE_NAME +name_end: + .balign NOTE_ALIGN +desc: +#if defined(__LP64__) + .quad CRASHPAD_INFO_SYMBOL - desc +#else + .long CRASHPAD_INFO_SYMBOL - desc +#endif // __LP64__ +desc_end: + .size CRASHPAD_NOTE, .-CRASHPAD_NOTE + + // CRASHPAD_NOTE can't be referenced directly by GetCrashpadInfo() because the + // relocation used to make the reference may require that the address be + // 8-byte aligned and notes must have 4-byte alignment. + .section .rodata,"a",%progbits + .balign 8 + # .globl indicates that it's available to link against other .o files. .hidden + # indicates that it will not appear in the executable's symbol table. + .globl CRASHPAD_NOTE_REFERENCE + .hidden CRASHPAD_NOTE_REFERENCE + .type CRASHPAD_NOTE_REFERENCE, %object +CRASHPAD_NOTE_REFERENCE: + // The value of this quad isn't important. It exists to reference + // CRASHPAD_NOTE, causing the linker to include the note into the binary + // linking Crashpad. The subtraction from |name| is a convenience to allow the + // value to be computed statically. + .quad name - CRASHPAD_NOTE diff --git a/shared/sentry/external/crashpad/client/ios_handler/exception_processor.h b/shared/sentry/external/crashpad/client/ios_handler/exception_processor.h new file mode 100644 index 000000000..233f7e45a --- /dev/null +++ b/shared/sentry/external/crashpad/client/ios_handler/exception_processor.h @@ -0,0 +1,89 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_UTIL_IOS_EXCEPTION_PROCESSOR_H_ +#define CRASHPAD_UTIL_IOS_EXCEPTION_PROCESSOR_H_ + +#include "base/files/file_path.h" +#include "util/misc/capture_context.h" + +namespace crashpad { + +//! \brief An interface for notifying the CrashpadClient of NSExceptions. +class ObjcExceptionDelegate { + public: + //! \brief The exception processor detected an exception as it was thrown and + //! captured the cpu context. + //! + //! \param context The cpu context of the thread throwing an exception. + virtual void HandleUncaughtNSExceptionWithContext( + NativeCPUContext* context) = 0; + + //! \brief The exception processor did not detect the exception as it was + //! thrown, and instead caught the exception via the + //! NSUncaughtExceptionHandler. + //! + //! \param frames An array of call stack frame addresses. + //! \param num_frames The number of frames in |frames|. + virtual void HandleUncaughtNSException(const uint64_t* frames, + const size_t num_frames) = 0; + + //! \brief Generate an intermediate dump from an NSException caught with its + //! associated CPU context. Because the method for intercepting + //! exceptions is imperfect, write the the intermediate dump to a + //! temporary location specified by \a path. If the NSException matches + //! the one used in the UncaughtExceptionHandler, call + //! MoveIntermediateDumpAtPathToPending to move to the proper Crashpad + //! database pending location. + //! + //! \param[in] context The cpu context of the thread throwing an exception. + //! \param[in] path Path to write the intermediate dump. + virtual void HandleUncaughtNSExceptionWithContextAtPath( + NativeCPUContext* context, + const base::FilePath& path) = 0; + + //! \brief Moves an intermediate dump to the pending directory. This is meant + //! to be used by the UncaughtExceptionHandler, when the NSException + //! caught by the preprocessor matches the UncaughtExceptionHandler. + //! + //! \param[in] path Path to the specific intermediate dump. + virtual bool MoveIntermediateDumpAtPathToPending( + const base::FilePath& path) = 0; + + protected: + ~ObjcExceptionDelegate() {} +}; + +//! \brief Installs the Objective-C exception preprocessor. +//! +//! When code raises an Objective-C exception, unwind the stack looking for +//! any exception handlers. If an exception handler is encountered, test to +//! see if it is a function known to be a catch-and-rethrow 'sinkhole' exception +//! handler. Various routines in UIKit do this, and they obscure the +//! crashing stack, since the original throw location is no longer present +//! on the stack (just the re-throw) when Crashpad captures the crash +//! report. In the case of sinkholes, trigger an immediate exception to +//! capture the original stack. +//! +//! This should be installed at the same time the CrashpadClient installs the +//! signal handler. It should only be installed once. +void InstallObjcExceptionPreprocessor(ObjcExceptionDelegate* delegate); + +//! \brief Uninstalls the Objective-C exception preprocessor. Expected to be +//! used by tests only. +void UninstallObjcExceptionPreprocessor(); + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_IOS_EXCEPTION_PROCESSOR_H_ diff --git a/shared/sentry/external/crashpad/client/ios_handler/exception_processor.mm b/shared/sentry/external/crashpad/client/ios_handler/exception_processor.mm new file mode 100644 index 000000000..3211228c2 --- /dev/null +++ b/shared/sentry/external/crashpad/client/ios_handler/exception_processor.mm @@ -0,0 +1,614 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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. + +// std::unexpected_handler is deprecated starting in C++11, and removed in +// C++17. But macOS versions we run on still ship it. This define makes +// std::unexpected_handler reappear. If that define ever stops working, +// we hopefully no longer run on macOS versions that still have it. +// (...or we'll have to define it in this file instead of getting it from +// ). This define must before all includes. +#define _LIBCPP_ENABLE_CXX17_REMOVED_UNEXPECTED_FUNCTIONS + +#include "client/ios_handler/exception_processor.h" + +#include +#import +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "base/bit_cast.h" +#include "base/format_macros.h" +#include "base/logging.h" +#include "base/memory/free_deleter.h" +#include "base/numerics/safe_conversions.h" +#include "base/strings/stringprintf.h" +#include "base/strings/sys_string_conversions.h" +#include "build/build_config.h" +#include "client/annotation.h" +#include "client/simulate_crash_ios.h" + +namespace crashpad { + +namespace { + +// From 10.15.0 objc4-779.1/runtime/objc-exception.mm. +struct objc_typeinfo { + const void* const* vtable; + const char* name; + Class cls_unremapped; +}; +struct objc_exception { + id obj; + objc_typeinfo tinfo; +}; + +// From 10.15.0 objc4-779.1/runtime/objc-abi.h. +extern "C" const void* const objc_ehtype_vtable[]; + +// https://github.com/llvm/llvm-project/blob/09dc884eb2e4/libcxxabi/src/cxa_exception.h +static const uint64_t kOurExceptionClass = 0x434c4e47432b2b00; +struct __cxa_exception { +#if defined(ARCH_CPU_64_BITS) + void* reserve; + size_t referenceCount; +#endif + std::type_info* exceptionType; + void (*exceptionDestructor)(void*); + std::unexpected_handler unexpectedHandler; + std::terminate_handler terminateHandler; + __cxa_exception* nextException; + int handlerCount; + int handlerSwitchValue; + const unsigned char* actionRecord; + const unsigned char* languageSpecificData; + void* catchTemp; + void* adjustedPtr; +#if !defined(ARCH_CPU_64_BITS) + size_t referenceCount; +#endif + _Unwind_Exception unwindHeader; +}; + +int LoggingUnwStep(unw_cursor_t* cursor) { + int rv = unw_step(cursor); + if (rv < 0) { + LOG(ERROR) << "unw_step: " << rv; + } + return rv; +} + +std::string FormatStackTrace(const std::vector& addresses, + size_t max_length) { + std::string stack_string; + for (uint64_t address : addresses) { + std::string address_string = base::StringPrintf("0x%" PRIx64, address); + if (stack_string.size() + address_string.size() > max_length) + break; + stack_string += address_string + " "; + } + + if (!stack_string.empty() && stack_string.back() == ' ') { + stack_string.resize(stack_string.size() - 1); + } + + return stack_string; +} + +std::string GetTraceString() { + std::vector addresses; + unw_context_t context; + unw_getcontext(&context); + unw_cursor_t cursor; + unw_init_local(&cursor, &context); + while (LoggingUnwStep(&cursor) > 0) { + unw_word_t ip = 0; + unw_get_reg(&cursor, UNW_REG_IP, &ip); + addresses.push_back(ip); + } + return FormatStackTrace(addresses, 1024); +} + +static void SetNSExceptionAnnotations(NSException* exception, + std::string& name, + std::string& reason) { + @try { + name = base::SysNSStringToUTF8(exception.name); + static StringAnnotation<256> nameKey("exceptionName"); + nameKey.Set(name); + } @catch (id name_exception) { + LOG(ERROR) << "Unable to read uncaught Objective-C exception name."; + } + + @try { + reason = base::SysNSStringToUTF8(exception.reason); + static StringAnnotation<512> reasonKey("exceptionReason"); + reasonKey.Set(reason); + } @catch (id reason_exception) { + LOG(ERROR) << "Unable to read uncaught Objective-C exception reason."; + } + + @try { + if (exception.userInfo) { + static StringAnnotation<512> userInfoKey("exceptionUserInfo"); + userInfoKey.Set(base::SysNSStringToUTF8( + [NSString stringWithFormat:@"%@", exception.userInfo])); + } + } @catch (id user_info_exception) { + LOG(ERROR) << "Unable to read uncaught Objective-C exception user info."; + } +} + +//! \brief Helper class to own the complex types used by the Objective-C +//! exception preprocessor. +class ExceptionPreprocessorState { + public: + ExceptionPreprocessorState(const ExceptionPreprocessorState&) = delete; + ExceptionPreprocessorState& operator=(const ExceptionPreprocessorState&) = + delete; + + static ExceptionPreprocessorState* Get() { + static ExceptionPreprocessorState* instance = []() { + return new ExceptionPreprocessorState(); + }(); + return instance; + } + + // Writes an intermediate dumps to a temporary location to be used by the + // final UncaughtExceptionHandler and notifies the preprocessor chain. + id HandleUncaughtException(NativeCPUContext* cpu_context, id exception) { + // If this isn't the first time the preprocessor has detected an uncaught + // NSException, note this in the second intermediate dump. + objc_exception_preprocessor next_preprocessor = next_preprocessor_; + static bool handled_first_exception; + if (handled_first_exception) { + static StringAnnotation<5> name_key("MultipleHandledUncaughtNSException"); + name_key.Set("true"); + + // Unregister so we stop getting in the way of the exception processor if + // we aren't correctly identifying sinkholes. The final uncaught exception + // handler is still active. + objc_setExceptionPreprocessor(next_preprocessor_); + next_preprocessor_ = nullptr; + } + handled_first_exception = true; + + // Use tmp/ for this intermediate dump path. Normally these dumps are + // written to the "pending-serialized-ios-dump" folder and are eligable for + // the next pass to convert pending intermediate dumps to minidump files. + // Since this intermediate dump isn't eligable until the uncaught handler, + // use tmp/. + base::FilePath path(base::SysNSStringToUTF8([NSTemporaryDirectory() + stringByAppendingPathComponent:[[NSUUID UUID] UUIDString]])); + exception_delegate_->HandleUncaughtNSExceptionWithContextAtPath(cpu_context, + path); + last_handled_intermediate_dump_ = path; + + return next_preprocessor ? next_preprocessor(exception) : exception; + } + + // If the PreprocessException already captured this exception via + // HANDLE_UNCAUGHT_NSEXCEPTION. Move last_handled_intermediate_dump_ to + // the pending intermediate dump directory and return true. Otherwise the + // preprocessor didn't catch anything, so pass the frames or just the context + // to the exception_delegate. + void FinalizeUncaughtNSException(id exception) { + if ([last_exception_ isEqual:exception] && + !last_handled_intermediate_dump_.empty() && + exception_delegate_->MoveIntermediateDumpAtPathToPending( + last_handled_intermediate_dump_)) { + last_handled_intermediate_dump_ = base::FilePath(); + return; + } + + std::string name, reason; + SetNSExceptionAnnotations(exception, name, reason); + NSArray* address_array = [exception callStackReturnAddresses]; + + if ([address_array count] > 0) { + static StringAnnotation<256> name_key("UncaughtNSException"); + name_key.Set("true"); + std::vector addresses; + for (NSNumber* address in address_array) + addresses.push_back([address unsignedLongLongValue]); + exception_delegate_->HandleUncaughtNSException(&addresses[0], + addresses.size()); + } else { + LOG(WARNING) << "Uncaught Objective-C exception name: " << name + << " reason: " << reason << " with no " + << " -callStackReturnAddresses."; + NativeCPUContext cpu_context; + CaptureContext(&cpu_context); + exception_delegate_->HandleUncaughtNSExceptionWithContext(&cpu_context); + } + } + + id MaybeCallNextPreprocessor(id exception) { + return next_preprocessor_ ? next_preprocessor_(exception) : exception; + } + + // Register the objc_setExceptionPreprocessor and NSUncaughtExceptionHandler. + void Install(ObjcExceptionDelegate* delegate); + + // Restore the objc_setExceptionPreprocessor and NSUncaughtExceptionHandler. + void Uninstall(); + + NSException* last_exception() { return last_exception_; } + void set_last_exception(NSException* exception) { + [last_exception_ release]; + last_exception_ = [exception retain]; + } + + private: + ExceptionPreprocessorState() = default; + ~ExceptionPreprocessorState() = default; + + // Location of the intermediate dump generated after an exception triggered + // HANDLE_UNCAUGHT_NSEXCEPTION. + base::FilePath last_handled_intermediate_dump_; + + // Recorded last NSException in case the exception is caught and thrown again + // (without using objc_exception_rethrow.) + NSException* last_exception_ = nil; + + ObjcExceptionDelegate* exception_delegate_ = nullptr; + objc_exception_preprocessor next_preprocessor_ = nullptr; + NSUncaughtExceptionHandler* next_uncaught_exception_handler_ = nullptr; +}; + +static void ObjcUncaughtExceptionHandler(NSException* exception) { + ExceptionPreprocessorState::Get()->FinalizeUncaughtNSException(exception); +} + +// This function is used to make it clear to the crash processor that an +// uncaught NSException was recorded here. +static __attribute__((noinline)) id HANDLE_UNCAUGHT_NSEXCEPTION( + id exception, + const char* sinkhole) { + std::string name, reason; + SetNSExceptionAnnotations(exception, name, reason); + LOG(WARNING) << "Handling Objective-C exception name: " << name + << " reason: " << reason << " with sinkhole: " << sinkhole; + NativeCPUContext cpu_context; + CaptureContext(&cpu_context); + + ExceptionPreprocessorState* preprocessor_state = + ExceptionPreprocessorState::Get(); + return preprocessor_state->HandleUncaughtException(&cpu_context, exception); +} + +// Returns true if |path| equals |sinkhole| on device. Simulator paths prepend +// much of Xcode's internal structure, so check that |path| ends with |sinkhole| +// for simulator. +bool ModulePathMatchesSinkhole(const char* path, const char* sinkhole) { +#if TARGET_OS_SIMULATOR + size_t path_length = strlen(path); + size_t sinkhole_length = strlen(sinkhole); + if (sinkhole_length > path_length) + return false; + return strncmp(path + path_length - sinkhole_length, + sinkhole, + sinkhole_length) == 0; +#else + return strcmp(path, sinkhole) == 0; +#endif +} + +id ObjcExceptionPreprocessor(id exception) { + // Some sinkholes don't use objc_exception_rethrow when they should, which + // would otherwise prevent the exception_preprocessor from getting called + // again. Because of this, track the most recently seen exception and + // ignore it. + ExceptionPreprocessorState* preprocessor_state = + ExceptionPreprocessorState::Get(); + if ([preprocessor_state->last_exception() isEqual:exception]) { + return preprocessor_state->MaybeCallNextPreprocessor(exception); + } + preprocessor_state->set_last_exception(exception); + + static bool seen_first_exception; + + static StringAnnotation<256> firstexception("firstexception"); + static StringAnnotation<256> lastexception("lastexception"); + static StringAnnotation<1024> firstexception_bt("firstexception_bt"); + static StringAnnotation<1024> lastexception_bt("lastexception_bt"); + auto* key = seen_first_exception ? &lastexception : &firstexception; + auto* bt_key = seen_first_exception ? &lastexception_bt : &firstexception_bt; + NSString* value = [NSString + stringWithFormat:@"%@ reason %@", [exception name], [exception reason]]; + key->Set(base::SysNSStringToUTF8(value)); + + // This exception preprocessor runs prior to the one in libobjc, which sets + // the -[NSException callStackReturnAddresses]. + bt_key->Set(GetTraceString()); + seen_first_exception = true; + + // Unwind the stack looking for any exception handlers. If an exception + // handler is encountered, test to see if it is a function known to catch- + // and-rethrow as a "top-level" exception handler. Various routines in + // Cocoa/UIKit do this, and it obscures the crashing stack, since the original + // throw location is no longer present on the stack (just the re-throw) when + // Crashpad captures the crash report. + unw_context_t context; + unw_getcontext(&context); + + unw_cursor_t cursor; + unw_init_local(&cursor, &context); + + static const void* this_base_address = []() -> const void* { + Dl_info dl_info; + if (!dladdr(reinterpret_cast(&ObjcExceptionPreprocessor), + &dl_info)) { + LOG(ERROR) << "dladdr: " << dlerror(); + return nullptr; + } + return dl_info.dli_fbase; + }(); + + // Generate an exception_header for the __personality_routine. + // From 10.15.0 objc4-779.1/runtime/objc-exception.mm objc_exception_throw. + objc_exception* exception_objc = reinterpret_cast( + __cxxabiv1::__cxa_allocate_exception(sizeof(objc_exception))); + exception_objc->obj = exception; + exception_objc->tinfo.vtable = objc_ehtype_vtable + 2; + exception_objc->tinfo.name = object_getClassName(exception); + exception_objc->tinfo.cls_unremapped = object_getClass(exception); + + // https://github.com/llvm/llvm-project/blob/c5d2746fbea7/libcxxabi/src/cxa_exception.cpp + // __cxa_throw + __cxa_exception* exception_header = + reinterpret_cast<__cxa_exception*>(exception_objc) - 1; + exception_header->unexpectedHandler = std::get_unexpected(); + exception_header->terminateHandler = std::get_terminate(); + exception_header->exceptionType = + reinterpret_cast(&exception_objc->tinfo); + exception_header->unwindHeader.exception_class = kOurExceptionClass; + + bool handler_found = false; + while (LoggingUnwStep(&cursor) > 0) { + unw_proc_info_t frame_info; + if (unw_get_proc_info(&cursor, &frame_info) != UNW_ESUCCESS) { + continue; + } + + if (frame_info.handler == 0) { + continue; + } + + // Check to see if the handler is really an exception handler. +#if defined(__IPHONE_14_5) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_14_5 + using personality_routine = _Unwind_Personality_Fn; +#else + using personality_routine = __personality_routine; +#endif + personality_routine p = + reinterpret_cast(frame_info.handler); + + // From 10.15.0 libunwind-35.4/src/UnwindLevel1.c. + _Unwind_Reason_Code personalityResult = (*p)( + 1, + _UA_SEARCH_PHASE, + exception_header->unwindHeader.exception_class, + reinterpret_cast<_Unwind_Exception*>(&exception_header->unwindHeader), + reinterpret_cast<_Unwind_Context*>(&cursor)); + switch (personalityResult) { + case _URC_HANDLER_FOUND: + break; + case _URC_CONTINUE_UNWIND: + continue; + default: + break; + } + + char proc_name[512]; + unw_word_t offset; + if (unw_get_proc_name(&cursor, proc_name, sizeof(proc_name), &offset) != + UNW_ESUCCESS) { + // The symbol has no name, so see if it belongs to the same image as + // this function. + Dl_info dl_info; + if (dladdr(reinterpret_cast(frame_info.start_ip), + &dl_info)) { + if (dl_info.dli_fbase == this_base_address) { + // This is a handler in our image, so allow it to run. + handler_found = true; + break; + } + } + + // This handler does not belong to us, so continue the search. + continue; + } + + // Check if the function is one that is known to obscure (by way of + // catch-and-rethrow) exception stack traces. If it is, sinkhole it + // by crashing here at the point of throw. + static constexpr const char* kExceptionSymbolNameSinkholes[] = { + // The two CF symbol names will also be captured by the CoreFoundation + // library path check below, but for completeness they are listed here, + // since they appear unredacted. + "CFRunLoopRunSpecific", + "_CFXNotificationPost", + "__NSFireDelayedPerform", + // If this exception is going to end up at EHFrame, record the uncaught + // exception instead. + "_ZN4base3mac15CallWithEHFrameEU13block_pointerFvvE", + }; + for (const char* sinkhole : kExceptionSymbolNameSinkholes) { + if (strcmp(sinkhole, proc_name) == 0) { + return HANDLE_UNCAUGHT_NSEXCEPTION(exception, sinkhole); + } + } + + // On iOS, function names are often reported as "", although they + // do appear when attached to the debugger. When this happens, use the path + // of the image to determine if the handler is an exception sinkhole. + static constexpr const char* kExceptionLibraryPathSinkholes[] = { + // Everything in this library is a sinkhole, specifically + // _dispatch_client_callout. Both are needed here depending on whether + // the debugger is attached (introspection only appears when a simulator + // is attached to a debugger). + "/usr/lib/system/introspection/libdispatch.dylib", + "/usr/lib/system/libdispatch.dylib", + + // __CFRunLoopDoTimers and __CFRunLoopRun are sinkholes. Consider also + // checking that a few frames up is CFRunLoopRunSpecific(). + "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation", + }; + + Dl_info dl_info; + if (dladdr(reinterpret_cast(frame_info.start_ip), &dl_info) != + 0) { + for (const char* sinkhole : kExceptionLibraryPathSinkholes) { + if (ModulePathMatchesSinkhole(dl_info.dli_fname, sinkhole)) { + return HANDLE_UNCAUGHT_NSEXCEPTION(exception, sinkhole); + } + } + + // Another set of iOS redacted sinkholes appear in CoreAutoLayout. + // However, this is often called by client code, so it's unsafe to simply + // handle an uncaught nsexception here. Instead, skip the frame and + // continue searching for either a handler that belongs to us, or another + // sinkhole. See: + // -[NSISEngine + // performModifications:withUnsatisfiableConstraintsHandler:]: + // -[NSISEngine withBehaviors:performModifications:] + // +[NSLayoutConstraintParser + // constraintsWithVisualFormat:options:metrics:views:]: + static constexpr const char* kCoreAutoLayoutSinkhole = + "/System/Library/PrivateFrameworks/CoreAutoLayout.framework/" + "CoreAutoLayout"; + if (ModulePathMatchesSinkhole(dl_info.dli_fname, + kCoreAutoLayoutSinkhole)) { + continue; + } + } + + // Some sinkholes are harder to find. _UIGestureEnvironmentUpdate + // in UIKitCore is an example. UIKitCore can't be added to + // kExceptionLibraryPathSinkholes because it uses Objective-C exceptions + // internally and also has has non-sinkhole handlers. While all the + // calling methods in UIKit are marked starting in iOS14, it's + // currently true that all callers to _UIGestureEnvironmentUpdate are within + // UIWindow sendEvent -> UIGestureEnvironment. That means a very hacky way + // to detect this is to check if the calling (2x) method IMP is within the + // range of all UIWindow methods. + static constexpr const char kUIKitCorePath[] = + "/System/Library/PrivateFrameworks/UIKitCore.framework/UIKitCore"; + if (ModulePathMatchesSinkhole(dl_info.dli_fname, kUIKitCorePath)) { + unw_proc_info_t caller_frame_info; + if (LoggingUnwStep(&cursor) > 0 && + unw_get_proc_info(&cursor, &caller_frame_info) == UNW_ESUCCESS && + LoggingUnwStep(&cursor) > 0 && + unw_get_proc_info(&cursor, &caller_frame_info) == UNW_ESUCCESS) { + auto uiwindowimp_lambda = [](IMP* max) { + IMP min = *max = bit_cast(nullptr); + unsigned int method_count = 0; + std::unique_ptr method_list( + class_copyMethodList(NSClassFromString(@"UIWindow"), + &method_count)); + if (method_count > 0) { + min = *max = method_getImplementation(method_list[0]); + for (unsigned int method_index = 1; method_index < method_count; + method_index++) { + IMP method_imp = + method_getImplementation(method_list[method_index]); + *max = std::max(method_imp, *max); + min = std::min(method_imp, min); + } + } + return min; + }; + + static IMP uiwindow_max_imp; + static IMP uiwindow_min_imp = uiwindowimp_lambda(&uiwindow_max_imp); + + if (uiwindow_min_imp && uiwindow_max_imp && + caller_frame_info.start_ip >= + reinterpret_cast(uiwindow_min_imp) && + caller_frame_info.start_ip <= + reinterpret_cast(uiwindow_max_imp)) { + return HANDLE_UNCAUGHT_NSEXCEPTION(exception, + "_UIGestureEnvironmentUpdate"); + } + } + } + + handler_found = true; + + break; + } + + // If no handler is found, __cxa_throw would call failed_throw and terminate. + // See: + // https://github.com/llvm/llvm-project/blob/c5d2746fbea7/libcxxabi/src/cxa_exception.cpp + // __cxa_throw. Instead, call HANDLE_UNCAUGHT_NSEXCEPTION so the exception + // name and reason are properly recorded. + if (!handler_found) { + return HANDLE_UNCAUGHT_NSEXCEPTION(exception, "__cxa_throw"); + } + + // Forward to the next preprocessor. + return preprocessor_state->MaybeCallNextPreprocessor(exception); +} + +void ExceptionPreprocessorState::Install(ObjcExceptionDelegate* delegate) { + DCHECK(!next_preprocessor_); + exception_delegate_ = delegate; + + // Preprocessor. + next_preprocessor_ = + objc_setExceptionPreprocessor(&ObjcExceptionPreprocessor); + + // Uncaught processor. + next_uncaught_exception_handler_ = NSGetUncaughtExceptionHandler(); + NSSetUncaughtExceptionHandler(&ObjcUncaughtExceptionHandler); +} + +void ExceptionPreprocessorState::Uninstall() { + DCHECK(next_preprocessor_); + objc_setExceptionPreprocessor(next_preprocessor_); + next_preprocessor_ = nullptr; + + NSSetUncaughtExceptionHandler(next_uncaught_exception_handler_); + next_uncaught_exception_handler_ = nullptr; + + exception_delegate_ = nullptr; +} + +} // namespace + +void InstallObjcExceptionPreprocessor(ObjcExceptionDelegate* delegate) { + ExceptionPreprocessorState::Get()->Install(delegate); +} + +void UninstallObjcExceptionPreprocessor() { + ExceptionPreprocessorState::Get()->Uninstall(); +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/ios_handler/exception_processor_test.mm b/shared/sentry/external/crashpad/client/ios_handler/exception_processor_test.mm new file mode 100644 index 000000000..d86507698 --- /dev/null +++ b/shared/sentry/external/crashpad/client/ios_handler/exception_processor_test.mm @@ -0,0 +1,47 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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 +#include +#include + +#include "gtest/gtest.h" +#include "testing/platform_test.h" + +namespace crashpad { +namespace test { +namespace { + +using IOSExceptionProcessor = PlatformTest; + +TEST_F(IOSExceptionProcessor, SelectorExists) { + IMP init_imp = + class_getMethodImplementation(NSClassFromString(@"UIGestureEnvironment"), + NSSelectorFromString(@"init")); + + IMP destruct_imp = + class_getMethodImplementation(NSClassFromString(@"UIGestureEnvironment"), + NSSelectorFromString(@".cxx_destruct")); + + // From 10.15.0 objc4-779.1/runtime/objc-class.mm + // class_getMethodImplementation returns nil or _objc_msgForward on failure. + ASSERT_TRUE(init_imp); + EXPECT_NE(init_imp, _objc_msgForward); + ASSERT_TRUE(destruct_imp); + EXPECT_NE(destruct_imp, _objc_msgForward); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/ios_handler/in_process_handler.cc b/shared/sentry/external/crashpad/client/ios_handler/in_process_handler.cc new file mode 100644 index 000000000..abde07f60 --- /dev/null +++ b/shared/sentry/external/crashpad/client/ios_handler/in_process_handler.cc @@ -0,0 +1,474 @@ +// Copyright 2021 The Crashpad Authors. All rights reserved. +// +// 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 "client/ios_handler/in_process_handler.h" + +#include +#include + +#include + +#include "base/cxx17_backports.h" +#include "base/logging.h" +#include "client/ios_handler/in_process_intermediate_dump_handler.h" +#include "client/prune_crash_reports.h" +#include "client/settings.h" +#include "minidump/minidump_file_writer.h" +#include "util/file/directory_reader.h" +#include "util/file/filesystem.h" +#include "util/ios/raw_logging.h" + +namespace { + +// Creates directory at |path|. +bool CreateDirectory(const base::FilePath& path) { + if (mkdir(path.value().c_str(), 0755) == 0) { + return true; + } + if (errno != EEXIST) { + PLOG(ERROR) << "mkdir " << path.value(); + return false; + } + return true; +} + +// The file extension used to indicate a file is locked. +constexpr char kLockedExtension[] = ".locked"; + +// The seperator used to break the bundle id (e.g. com.chromium.ios) from the +// uuid in the intermediate dump file name. +constexpr char kBundleSeperator[] = "@"; + +// Zero-ed codes used by kMachExceptionFromNSException and +// kMachExceptionSimulated. +constexpr mach_exception_data_type_t kEmulatedMachExceptionCodes[2] = {}; + +} // namespace + +namespace crashpad { +namespace internal { + +InProcessHandler::InProcessHandler() = default; + +InProcessHandler::~InProcessHandler() { + UpdatePruneAndUploadThreads(false); +} + +bool InProcessHandler::Initialize( + const base::FilePath& database, + const std::string& url, + const std::map& annotations) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + annotations_ = annotations; + database_ = CrashReportDatabase::Initialize(database); + if (!database_) { + return false; + } + bundle_identifier_and_seperator_ = + system_data_.BundleIdentifier() + kBundleSeperator; + + if (!url.empty()) { + // TODO(scottmg): options.rate_limit should be removed when we have a + // configurable database setting to control upload limiting. + // See https://crashpad.chromium.org/bug/23. + CrashReportUploadThread::Options upload_thread_options; + upload_thread_options.rate_limit = false; + upload_thread_options.upload_gzip = true; + upload_thread_options.watch_pending_reports = true; + upload_thread_options.identify_client_via_url = true; + + upload_thread_.reset(new CrashReportUploadThread( + database_.get(), url, upload_thread_options)); + } + + if (!CreateDirectory(database)) + return false; + static constexpr char kPendingSerializediOSDump[] = + "pending-serialized-ios-dump"; + base_dir_ = database.Append(kPendingSerializediOSDump); + if (!CreateDirectory(base_dir_)) + return false; + + bool is_app_extension = system_data_.IsExtension(); + prune_thread_.reset(new PruneIntermediateDumpsAndCrashReportsThread( + database_.get(), + PruneCondition::GetDefault(), + base_dir_, + bundle_identifier_and_seperator_, + is_app_extension)); + if (is_app_extension || system_data_.IsApplicationActive()) + prune_thread_->Start(); + + if (!is_app_extension) { + system_data_.SetActiveApplicationCallback( + [this](bool active) { UpdatePruneAndUploadThreads(active); }); + } + + base::FilePath cached_writer_path = NewLockedFilePath(); + cached_writer_ = CreateWriterWithPath(cached_writer_path); + if (!cached_writer_.get()) + return false; + + // Cache the locked and unlocked path here so no allocations are needed during + // any exceptions. + cached_writer_path_ = cached_writer_path.value(); + cached_writer_unlocked_path_ = + cached_writer_path.RemoveFinalExtension().value(); + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +void InProcessHandler::DumpExceptionFromSignal(siginfo_t* siginfo, + ucontext_t* context) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + ScopedLockedWriter writer(GetCachedWriter(), + cached_writer_path_.c_str(), + cached_writer_unlocked_path_.c_str()); + if (!writer.GetWriter()) { + CRASHPAD_RAW_LOG("Cannot DumpExceptionFromSignal without writer"); + return; + } + ScopedReport report(writer.GetWriter(), system_data_, annotations_); + InProcessIntermediateDumpHandler::WriteExceptionFromSignal( + writer.GetWriter(), system_data_, siginfo, context); +} + +void InProcessHandler::DumpExceptionFromMachException( + exception_behavior_t behavior, + thread_t thread, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + ScopedLockedWriter writer(GetCachedWriter(), + cached_writer_path_.c_str(), + cached_writer_unlocked_path_.c_str()); + if (!writer.GetWriter()) { + CRASHPAD_RAW_LOG("Cannot DumpExceptionFromMachException without writer"); + return; + } + + if (mach_exception_callback_for_testing_) { + mach_exception_callback_for_testing_(); + } + + ScopedReport report(writer.GetWriter(), system_data_, annotations_); + InProcessIntermediateDumpHandler::WriteExceptionFromMachException( + writer.GetWriter(), + behavior, + thread, + exception, + code, + code_count, + flavor, + old_state, + old_state_count); +} + +void InProcessHandler::DumpExceptionFromNSExceptionWithFrames( + const uint64_t* frames, + const size_t num_frames) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + ScopedLockedWriter writer(GetCachedWriter(), + cached_writer_path_.c_str(), + cached_writer_unlocked_path_.c_str()); + if (!writer.GetWriter()) { + CRASHPAD_RAW_LOG( + "Cannot DumpExceptionFromNSExceptionWithFrames without writer"); + return; + } + ScopedReport report( + writer.GetWriter(), system_data_, annotations_, frames, num_frames); + InProcessIntermediateDumpHandler::WriteExceptionFromNSException( + writer.GetWriter()); +} + +bool InProcessHandler::DumpExceptionFromSimulatedMachException( + const NativeCPUContext* context, + exception_type_t exception, + base::FilePath* path) { + base::FilePath locked_path = NewLockedFilePath(); + *path = locked_path.RemoveFinalExtension(); + return DumpExceptionFromSimulatedMachExceptionAtPath( + context, exception, locked_path); +} + +bool InProcessHandler::DumpExceptionFromSimulatedMachExceptionAtPath( + const NativeCPUContext* context, + exception_type_t exception, + const base::FilePath& path) { + // This does not use the cached writer. It's expected that simulated + // exceptions can be called multiple times and there is no expectation that + // the application is in an unsafe state, or will be terminated after this + // call. + std::unique_ptr unsafe_writer = + CreateWriterWithPath(path); + base::FilePath writer_path_unlocked = path.RemoveFinalExtension(); + ScopedLockedWriter writer(unsafe_writer.get(), + path.value().c_str(), + writer_path_unlocked.value().c_str()); + if (!writer.GetWriter()) { + CRASHPAD_RAW_LOG( + "Cannot DumpExceptionFromSimulatedMachExceptionAtPath without writer"); + return false; + } + ScopedReport report(writer.GetWriter(), system_data_, annotations_); + InProcessIntermediateDumpHandler::WriteExceptionFromMachException( + writer.GetWriter(), + MACH_EXCEPTION_CODES, + mach_thread_self(), + exception, + kEmulatedMachExceptionCodes, + std::size(kEmulatedMachExceptionCodes), + MACHINE_THREAD_STATE, + reinterpret_cast(context), + MACHINE_THREAD_STATE_COUNT); + return true; +} + +bool InProcessHandler::MoveIntermediateDumpAtPathToPending( + const base::FilePath& path) { + base::FilePath new_path_unlocked = NewLockedFilePath().RemoveFinalExtension(); + return MoveFileOrDirectory(path, new_path_unlocked); +} +void InProcessHandler::ProcessIntermediateDumps( + const std::map& annotations) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + for (auto& file : PendingFiles()) + ProcessIntermediateDump(file, annotations); +} + +void InProcessHandler::ProcessIntermediateDump( + const base::FilePath& file, + const std::map& annotations) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + ProcessSnapshotIOSIntermediateDump process_snapshot; + if (process_snapshot.InitializeWithFilePath(file, annotations)) { + SaveSnapshot(process_snapshot); + } +} + +void InProcessHandler::StartProcessingPendingReports() { + if (!upload_thread_) + return; + + upload_thread_enabled_ = true; + UpdatePruneAndUploadThreads(true); +} + +void InProcessHandler::UpdatePruneAndUploadThreads(bool active) { + base::AutoLock lock_owner(prune_and_upload_lock_); + // TODO(crbug.com/crashpad/400): Consider moving prune and upload thread to + // BackgroundTasks and/or NSURLSession. This might allow uploads to continue + // in the background. + if (active) { + if (!prune_thread_->is_running()) + prune_thread_->Start(); + if (upload_thread_enabled_ && !upload_thread_->is_running()) { + upload_thread_->Start(); + } + } else { + if (prune_thread_->is_running()) + prune_thread_->Stop(); + if (upload_thread_enabled_ && upload_thread_->is_running()) + upload_thread_->Stop(); + } +} + +void InProcessHandler::SaveSnapshot( + ProcessSnapshotIOSIntermediateDump& process_snapshot) { + std::unique_ptr new_report; + CrashReportDatabase::OperationStatus database_status = + database_->PrepareNewCrashReport(&new_report); + if (database_status != CrashReportDatabase::kNoError) { + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kPrepareNewCrashReportFailed); + } + process_snapshot.SetReportID(new_report->ReportID()); + + UUID client_id; + Settings* const settings = database_->GetSettings(); + if (settings && settings->GetClientID(&client_id)) { + process_snapshot.SetClientID(client_id); + } + + MinidumpFileWriter minidump; + minidump.InitializeFromSnapshot(&process_snapshot); + if (!minidump.WriteEverything(new_report->Writer())) { + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kMinidumpWriteFailed); + } + UUID uuid; + database_status = + database_->FinishedWritingCrashReport(std::move(new_report), &uuid); + if (database_status != CrashReportDatabase::kNoError) { + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kFinishedWritingCrashReportFailed); + } + + if (upload_thread_) { + upload_thread_->ReportPending(uuid); + } +} + +std::vector InProcessHandler::PendingFiles() { + DirectoryReader reader; + std::vector files; + if (!reader.Open(base_dir_)) { + return files; + } + base::FilePath file; + DirectoryReader::Result result; + + // Because the intermediate dump directory is expected to be shared, + // mitigate any spamming by limiting this to |kMaxPendingFiles|. + constexpr size_t kMaxPendingFiles = 20; + + // Track other application bundles separately, so they don't spam our + // intermediate dumps into never getting processed. + std::vector other_files; + + base::FilePath cached_writer_path(cached_writer_path_); + while ((result = reader.NextFile(&file)) == + DirectoryReader::Result::kSuccess) { + // Don't try to process files marked as 'locked' from a different bundle id. + bool bundle_match = + file.value().compare(0, + bundle_identifier_and_seperator_.size(), + bundle_identifier_and_seperator_) == 0; + if (!bundle_match && file.FinalExtension() == kLockedExtension) { + continue; + } + + // Never process the current cached writer path. + file = base_dir_.Append(file); + if (file == cached_writer_path) + continue; + + // Otherwise, include any other unlocked, or locked files matching + // |bundle_identifier_and_seperator_|. + if (bundle_match) { + files.push_back(file); + if (files.size() >= kMaxPendingFiles) + return files; + } else { + other_files.push_back(file); + } + } + + auto end_iterator = + other_files.begin() + + std::min(kMaxPendingFiles - files.size(), other_files.size()); + files.insert(files.end(), other_files.begin(), end_iterator); + return files; +} + +IOSIntermediateDumpWriter* InProcessHandler::GetCachedWriter() { + static_assert( + std::atomic::is_always_lock_free, + "std::atomic_compare_exchange_strong uint64_t may not be signal-safe"); + uint64_t thread_self; + // This is only safe when passing pthread_self(), otherwise this can lock. + pthread_threadid_np(pthread_self(), &thread_self); + uint64_t expected = 0; + if (!std::atomic_compare_exchange_strong( + &exception_thread_id_, &expected, thread_self)) { + if (expected == thread_self) { + // Another exception came in from this thread, which means it's likely + // that our own handler crashed. We could open up a new intermediate dump + // and try to save this dump, but we could end up endlessly writing dumps. + // Instead, give up. + } else { + // Another thread is handling a crash. Sleep forever. + while (1) { + sleep(std::numeric_limits::max()); + } + } + return nullptr; + } + + return cached_writer_.get(); +} + +std::unique_ptr +InProcessHandler::CreateWriterWithPath(const base::FilePath& writer_path) { + std::unique_ptr writer = + std::make_unique(); + if (!writer->Open(writer_path)) { + DLOG(ERROR) << "Unable to open intermediate dump file: " + << writer_path.value(); + return nullptr; + } + return writer; +} + +const base::FilePath InProcessHandler::NewLockedFilePath() { + UUID uuid; + uuid.InitializeWithNew(); + const std::string file_string = + bundle_identifier_and_seperator_ + uuid.ToString() + kLockedExtension; + return base_dir_.Append(file_string); +} + +InProcessHandler::ScopedReport::ScopedReport( + IOSIntermediateDumpWriter* writer, + const IOSSystemDataCollector& system_data, + const std::map& annotations, + const uint64_t* frames, + const size_t num_frames) + : writer_(writer), + frames_(frames), + num_frames_(num_frames), + rootMap_(writer) { + DCHECK(writer); + InProcessIntermediateDumpHandler::WriteHeader(writer); + InProcessIntermediateDumpHandler::WriteProcessInfo(writer, annotations); + InProcessIntermediateDumpHandler::WriteSystemInfo(writer, system_data); +} + +InProcessHandler::ScopedReport::~ScopedReport() { + // Write threads and modules last (after the exception itself is written by + // DumpExceptionFrom*.) + InProcessIntermediateDumpHandler::WriteThreadInfo( + writer_, frames_, num_frames_); + InProcessIntermediateDumpHandler::WriteModuleInfo(writer_); +} + +InProcessHandler::ScopedLockedWriter::ScopedLockedWriter( + IOSIntermediateDumpWriter* writer, + const char* writer_path, + const char* writer_unlocked_path) + : writer_path_(writer_path), + writer_unlocked_path_(writer_unlocked_path), + writer_(writer) {} + +InProcessHandler::ScopedLockedWriter::~ScopedLockedWriter() { + if (!writer_) + return; + + writer_->Close(); + if (rename(writer_path_, writer_unlocked_path_) != 0) { + CRASHPAD_RAW_LOG("Could not remove locked extension."); + CRASHPAD_RAW_LOG(writer_path_); + CRASHPAD_RAW_LOG(writer_unlocked_path_); + } +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/ios_handler/in_process_handler.h b/shared/sentry/external/crashpad/client/ios_handler/in_process_handler.h new file mode 100644 index 000000000..0e6b70b3c --- /dev/null +++ b/shared/sentry/external/crashpad/client/ios_handler/in_process_handler.h @@ -0,0 +1,258 @@ +// Copyright 2021 The Crashpad Authors. All rights reserved. +// +// 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 +#include + +#include +#include +#include +#include + +#include "base/files/file_path.h" +#include "base/synchronization/lock.h" +#include "client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.h" +#include "handler/crash_report_upload_thread.h" +#include "snapshot/ios/process_snapshot_ios_intermediate_dump.h" +#include "util/ios/ios_intermediate_dump_writer.h" +#include "util/ios/ios_system_data_collector.h" +#include "util/misc/capture_context.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { +namespace internal { + +//! \brief Manage intermediate minidump generation, and own the crash report +//! upload thread and database. +class InProcessHandler { + public: + InProcessHandler(); + ~InProcessHandler(); + InProcessHandler(const InProcessHandler&) = delete; + InProcessHandler& operator=(const InProcessHandler&) = delete; + + //! \brief Initializes the in-process handler. + //! + //! This method must be called only once, and must be successfully called + //! before any other method in this class may be called. + //! + //! \param[in] database The path to a Crashpad database. + //! \param[in] url The URL of an upload server. + //! \param[in] annotations Process annotations to set in each crash report. + //! \return `true` if a handler to a pending intermediate dump could be + //! opened. + bool Initialize(const base::FilePath& database, + const std::string& url, + const std::map& annotations); + + //! \brief Generate an intermediate dump from a signal handler exception. + //! Writes the dump with the cached writer does not allow concurrent + //! exceptions to be written. It is expected the system will terminate + //! the application after this call. + //! + //! \param[in] siginfo A pointer to a `siginfo_t` object received by a signal + //! handler. + //! \param[in] context A pointer to a `ucontext_t` object received by a + //! signal. + void DumpExceptionFromSignal(siginfo_t* siginfo, ucontext_t* context); + + //! \brief Generate an intermediate dump from a mach exception. Writes the + //! dump with the cached writer does not allow concurrent exceptions to be + //! written. It is expected the system will terminate the application + //! after this call. + //! + //! \param[in] behavior + //! \param[in] thread + //! \param[in] exception + //! \param[in] code + //! \param[in] code_count + //! \param[in,out] flavor + //! \param[in] old_state + //! \param[in] old_state_count + void DumpExceptionFromMachException(exception_behavior_t behavior, + thread_t thread, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count); + + //! \brief Generate an intermediate dump from an uncaught NSException. + //! + //! When the ObjcExceptionPreprocessor does not detect an NSException as it is + //! thrown, the last-chance uncaught exception handler passes a list of call + //! stack frame addresses. Record them in the intermediate dump so a minidump + //! with a 'fake' call stack is generated. Writes the dump with the cached + //! writer does not allow concurrent exceptions to be written. It is expected + //! the system will terminate the application after this call. + + //! + //! \param[in] frames An array of call stack frame addresses. + //! \param[in] num_frames The number of frames in |frames|. + void DumpExceptionFromNSExceptionWithFrames(const uint64_t* frames, + const size_t num_frames); + + //! \brief Generate a simulated intermediate dump similar to a Mach exception + //! in the same base directory as other exceptions. Does not use the + //! cached writer. + //! + //! \param[in] context A pointer to a NativeCPUContext object for this + //! simulated exception. + //! \param[in] exception + //! \param[out] path The path of the intermediate dump generated. + //! \return `true` if the pending intermediate dump could be written. + bool DumpExceptionFromSimulatedMachException(const NativeCPUContext* context, + exception_type_t exception, + base::FilePath* path); + + //! \brief Generate a simulated intermediate dump similar to a Mach exception + //! at a specific path. Does not use the cached writer. + //! + //! \param[in] context A pointer to a NativeCPUContext object for this + //! simulated exception. + //! \param[in] exception + //! \param[in] path Path to where the intermediate dump should be written. + //! \return `true` if the pending intermediate dump could be written. + bool DumpExceptionFromSimulatedMachExceptionAtPath( + const NativeCPUContext* context, + exception_type_t exception, + const base::FilePath& path); + + //! \brief Moves an intermediate dump to the pending directory. This is meant + //! to be used by the UncaughtExceptionHandler, when NSException caught + //! by the preprocessor matches the UncaughtExceptionHandler. + //! + //! \param[in] path Path to the specific intermediate dump. + bool MoveIntermediateDumpAtPathToPending(const base::FilePath& path); + + //! \brief Requests that the handler convert all intermediate dumps into + //! minidumps and trigger an upload if possible. + //! + //! \param[in] annotations Process annotations to set in each crash report. + void ProcessIntermediateDumps( + const std::map& annotations); + + //! \brief Requests that the handler convert a specific intermediate dump into + //! a minidump and trigger an upload if possible. + //! + //! \param[in] path Path to the specific intermediate dump. + //! \param[in] annotations Process annotations to set in each crash report. + void ProcessIntermediateDump( + const base::FilePath& path, + const std::map& annotations = {}); + + //! \brief Requests that the handler begin in-process uploading of any + //! pending reports. + void StartProcessingPendingReports(); + + //! \brief Inject a callback into Mach handling. Intended to be used by + //! tests to trigger a reentrant exception. + void SetMachExceptionCallbackForTesting(void (*callback)()) { + mach_exception_callback_for_testing_ = callback; + } + + private: + //! \brief Helper to start and end intermediate reports. + class ScopedReport { + public: + ScopedReport(IOSIntermediateDumpWriter* writer, + const IOSSystemDataCollector& system_data, + const std::map& annotations, + const uint64_t* frames = nullptr, + const size_t num_frames = 0); + ~ScopedReport(); + ScopedReport(const ScopedReport&) = delete; + ScopedReport& operator=(const ScopedReport&) = delete; + + private: + IOSIntermediateDumpWriter* writer_; + const uint64_t* frames_; + const size_t num_frames_; + IOSIntermediateDumpWriter::ScopedRootMap rootMap_; + }; + + //! \brief Helper to manage closing the intermediate dump writer and unlocking + //! the dump file (renaming the file) after the report is written. + class ScopedLockedWriter { + public: + ScopedLockedWriter(IOSIntermediateDumpWriter* writer, + const char* writer_path, + const char* writer_unlocked_path); + + //! \brief Close the writer_ and rename to the file with path without the + //! .locked extension. + ~ScopedLockedWriter(); + + ScopedLockedWriter(const ScopedLockedWriter&) = delete; + ScopedLockedWriter& operator=(const ScopedLockedWriter&) = delete; + + IOSIntermediateDumpWriter* GetWriter() { return writer_; } + + private: + const char* writer_path_; + const char* writer_unlocked_path_; + IOSIntermediateDumpWriter* writer_; + }; + + //! \brief Manage the prune and upload thread when the active state changes. + void UpdatePruneAndUploadThreads(bool active); + + //! \brief Writes a minidump to the Crashpad database from the + //! \a process_snapshot, and triggers the upload_thread_ if started. + void SaveSnapshot(ProcessSnapshotIOSIntermediateDump& process_snapshot); + + //! \brief Process a maximum of 20 pending intermediate dumps. Dumps named + //! with our bundle id get first priority to prevent spamming. + std::vector PendingFiles(); + + //! \brief Lock access to the cached intermediate dump writer from + //! concurrent signal, Mach exception and uncaught NSExceptions so that + //! the first exception wins. If the same thread triggers another + //! reentrant exception, ignore it. If a different thread triggers a + //! concurrent exception, sleep indefinitely. + IOSIntermediateDumpWriter* GetCachedWriter(); + + //! \brief Open a new intermediate dump writer from \a writer_path. + std::unique_ptr CreateWriterWithPath( + const base::FilePath& writer_path); + + //! \brief Generates a new file path to be used by an intermediate dump + //! writer built from base_dir_,, bundle_identifier_and_seperator_, a new + //! UUID, with a .locked extension. + const base::FilePath NewLockedFilePath(); + + // Intended to be used by tests triggering a reentrant exception. Called + // in DumpExceptionFromMachException after aquiring the cached_writer_. + void (*mach_exception_callback_for_testing_)() = nullptr; + + // Used to synchronize access to UpdatePruneAndUploadThreads(). + base::Lock prune_and_upload_lock_; + std::atomic_bool upload_thread_enabled_ = false; + std::map annotations_; + base::FilePath base_dir_; + std::string cached_writer_path_; + std::string cached_writer_unlocked_path_; + std::unique_ptr cached_writer_; + std::atomic exception_thread_id_ = 0; + std::unique_ptr upload_thread_; + std::unique_ptr prune_thread_; + std::unique_ptr database_; + std::string bundle_identifier_and_seperator_; + IOSSystemDataCollector system_data_; + InitializationStateDcheck initialized_; +}; + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/ios_handler/in_process_handler_test.cc b/shared/sentry/external/crashpad/client/ios_handler/in_process_handler_test.cc new file mode 100644 index 000000000..110d86c75 --- /dev/null +++ b/shared/sentry/external/crashpad/client/ios_handler/in_process_handler_test.cc @@ -0,0 +1,148 @@ +// Copyright 2021 The Crashpad Authors. All rights reserved. +// +// 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 "client/ios_handler/in_process_handler.h" + +#include "gtest/gtest.h" +#include "test/scoped_temp_dir.h" +#include "test/test_paths.h" +#include "util/file/directory_reader.h" +#include "util/file/filesystem.h" + +namespace crashpad { +namespace test { +namespace { + +bool CreateFile(const base::FilePath& file) { + ScopedFileHandle fd(LoggingOpenFileForWrite( + file, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly)); + EXPECT_TRUE(fd.is_valid()); + return fd.is_valid(); +} + +class InProcessHandlerTest : public testing::Test { + protected: + // testing::Test: + + void SetUp() override { + ASSERT_TRUE(in_process_handler_.Initialize(temp_dir_.path(), "", {})); + pending_dir_ = temp_dir_.path().Append("pending-serialized-ios-dump"); + bundle_identifier_and_seperator_ = system_data_.BundleIdentifier() + "@"; + } + + const auto& path() const { return pending_dir_; } + auto& handler() { return in_process_handler_; } + + void CreateFiles(int files, int other_files) { + base::FilePath::StringType file_prepend = + FILE_PATH_LITERAL(bundle_identifier_and_seperator_); + base::FilePath::StringType file_name = FILE_PATH_LITERAL("file"); + for (int i = 0; i < files; i++) { + std::string i_str = std::to_string(i); + base::FilePath file(file_prepend + file_name + i_str); + CreateFile(path().Append(file)); + } + + for (int i = 0; i < other_files; i++) { + std::string i_str = std::to_string(i); + base::FilePath file(file_name + i_str); + CreateFile(path().Append(file)); + } + } + + void VerifyRemainingFileCount(int expected_files_count, + int expected_other_files_count) { + DirectoryReader reader; + ASSERT_TRUE(reader.Open(path())); + DirectoryReader::Result result; + base::FilePath filename; + int files_count = 0; + int other_files_count = 0; + while ((result = reader.NextFile(&filename)) == + DirectoryReader::Result::kSuccess) { + bool bundle_match = + filename.value().compare(0, + bundle_identifier_and_seperator_.size(), + bundle_identifier_and_seperator_) == 0; + if (bundle_match) { + files_count++; + } else { + other_files_count++; + } + } + EXPECT_EQ(expected_files_count, files_count); + EXPECT_EQ(expected_other_files_count, other_files_count); + } + + void ClearFiles() { + DirectoryReader reader; + ASSERT_TRUE(reader.Open(path())); + DirectoryReader::Result result; + base::FilePath filename; + while ((result = reader.NextFile(&filename)) == + DirectoryReader::Result::kSuccess) { + LoggingRemoveFile(path().Append(filename)); + } + } + + private: + ScopedTempDir temp_dir_; + base::FilePath pending_dir_; + std::string bundle_identifier_and_seperator_; + internal::IOSSystemDataCollector system_data_; + internal::InProcessHandler in_process_handler_; +}; + +TEST_F(InProcessHandlerTest, TestPendingFileLimit) { + // Clear this first to blow away the pending file held by InProcessHandler. + ClearFiles(); + + // Only process other app files. + CreateFiles(0, 20); + handler().ProcessIntermediateDumps({}); + VerifyRemainingFileCount(0, 0); + ClearFiles(); + + // Only process our app files. + CreateFiles(20, 20); + handler().ProcessIntermediateDumps({}); + VerifyRemainingFileCount(0, 20); + ClearFiles(); + + // Process all of our files and 10 remaining. + CreateFiles(10, 30); + handler().ProcessIntermediateDumps({}); + VerifyRemainingFileCount(0, 20); + ClearFiles(); + + // Process 20 our files, leaving 10 remaining, and all other files remaining. + CreateFiles(30, 10); + handler().ProcessIntermediateDumps({}); + VerifyRemainingFileCount(10, 10); + ClearFiles(); + + CreateFiles(0, 0); + handler().ProcessIntermediateDumps({}); + VerifyRemainingFileCount(0, 0); + ClearFiles(); + + CreateFiles(10, 0); + handler().ProcessIntermediateDumps({}); + VerifyRemainingFileCount(0, 0); + ClearFiles(); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/ios_handler/in_process_intermediate_dump_handler.cc b/shared/sentry/external/crashpad/client/ios_handler/in_process_intermediate_dump_handler.cc new file mode 100644 index 000000000..88d5eb0a6 --- /dev/null +++ b/shared/sentry/external/crashpad/client/ios_handler/in_process_intermediate_dump_handler.cc @@ -0,0 +1,1285 @@ +// Copyright 2021 The Crashpad Authors. All rights reserved. +// +// 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 "client/ios_handler/in_process_intermediate_dump_handler.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include "build/build_config.h" +#include "snapshot/snapshot_constants.h" +#include "util/ios/ios_intermediate_dump_writer.h" +#include "util/ios/raw_logging.h" +#include "util/ios/scoped_vm_read.h" + +namespace crashpad { +namespace internal { + +namespace { + +#if defined(ARCH_CPU_X86_64) +const thread_state_flavor_t kThreadStateFlavor = x86_THREAD_STATE64; +const thread_state_flavor_t kFloatStateFlavor = x86_FLOAT_STATE64; +const thread_state_flavor_t kDebugStateFlavor = x86_DEBUG_STATE64; +using thread_state_type = x86_thread_state64_t; +#elif defined(ARCH_CPU_ARM64) +const thread_state_flavor_t kThreadStateFlavor = ARM_THREAD_STATE64; +const thread_state_flavor_t kFloatStateFlavor = ARM_NEON_STATE64; +const thread_state_flavor_t kDebugStateFlavor = ARM_DEBUG_STATE64; +using thread_state_type = arm_thread_state64_t; +#endif + +// From snapshot/mac/process_types/crashreporterclient.proctype +struct crashreporter_annotations_t { + uint64_t version; + uint64_t message; + uint64_t signature_string; + uint64_t backtrace; + uint64_t message2; + uint64_t thread; + uint64_t dialog_mode; + uint64_t abort_cause; +}; + +//! \brief Manage memory and ports after calling `task_threads`. +class ScopedTaskThreads { + public: + explicit ScopedTaskThreads(thread_act_array_t threads, + mach_msg_type_number_t thread_count) + : threads_(threads), thread_count_(thread_count) {} + + ScopedTaskThreads(const ScopedTaskThreads&) = delete; + ScopedTaskThreads& operator=(const ScopedTaskThreads&) = delete; + + ~ScopedTaskThreads() { + for (uint32_t thread_index = 0; thread_index < thread_count_; + ++thread_index) { + mach_port_deallocate(mach_task_self(), threads_[thread_index]); + } + vm_deallocate(mach_task_self(), + reinterpret_cast(threads_), + sizeof(thread_t) * thread_count_); + } + + private: + thread_act_array_t threads_; + mach_msg_type_number_t thread_count_; +}; + +//! \brief Log \a key as a string. +void WriteError(IntermediateDumpKey key) { + CRASHPAD_RAW_LOG("Unable to write key"); + switch (key) { +// clang-format off +#define CASE_KEY(Name, Value) \ + case IntermediateDumpKey::Name: \ + CRASHPAD_RAW_LOG(#Name); \ + break; + INTERMEDIATE_DUMP_KEYS(CASE_KEY) +#undef CASE_KEY + // clang-format on + } +} + +//! \brief Call AddProperty with raw error log. +//! +//! \param[in] writer The dump writer +//! \param[in] key The key to write. +//! \param[in] value Memory to be written. +//! \param[in] count Length of \a value. +template +void WriteProperty(IOSIntermediateDumpWriter* writer, + IntermediateDumpKey key, + const T* value, + size_t count = 1) { + if (!writer->AddProperty(key, value, count)) + WriteError(key); +} + +//! \brief Call AddPropertyBytes with raw error log. +//! +//! \param[in] writer The dump writer +//! \param[in] key The key to write. +//! \param[in] value Memory to be written. +//! \param[in] count Length of \a data. +void WritePropertyBytes(IOSIntermediateDumpWriter* writer, + IntermediateDumpKey key, + const void* value, + size_t value_length) { + if (!writer->AddPropertyBytes(key, value, value_length)) + WriteError(key); +} + +kern_return_t MachVMRegionRecurseDeepest(task_t task, + vm_address_t* address, + vm_size_t* size, + natural_t* depth, + vm_prot_t* protection, + unsigned int* user_tag) { + vm_region_submap_short_info_64 submap_info; + mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64; + while (true) { + // Note: vm_region_recurse() would be fine here, but it does not provide + // VM_REGION_SUBMAP_SHORT_INFO_COUNT. + kern_return_t kr = vm_region_recurse_64( + task, + address, + size, + depth, + reinterpret_cast(&submap_info), + &count); + if (kr != KERN_SUCCESS) { + CRASHPAD_RAW_LOG_ERROR(kr, "vm_region_recurse_64"); + return kr; + } + + if (!submap_info.is_submap) { + *protection = submap_info.protection; + *user_tag = submap_info.user_tag; + return KERN_SUCCESS; + } + + ++*depth; + } +} + +//! \brief Adjusts the region for the red zone, if the ABI requires one. +//! +//! This method performs red zone calculation for CalculateStackRegion(). Its +//! parameters are local variables used within that method, and may be +//! modified as needed. +//! +//! Where a red zone is required, the region of memory captured for a thread’s +//! stack will be extended to include the red zone below the stack pointer, +//! provided that such memory is mapped, readable, and has the correct user +//! tag value. If these conditions cannot be met fully, as much of the red +//! zone will be captured as is possible while meeting these conditions. +//! +//! \param[in,out] start_address The base address of the region to begin +//! capturing stack memory from. On entry, \a start_address is the stack +//! pointer. On return, \a start_address may be decreased to encompass a +//! red zone. +//! \param[in,out] region_base The base address of the region that contains +//! stack memory. This is distinct from \a start_address in that \a +//! region_base will be page-aligned. On entry, \a region_base is the +//! base address of a region that contains \a start_address. On return, +//! if \a start_address is decremented and is outside of the region +//! originally described by \a region_base, \a region_base will also be +//! decremented appropriately. +//! \param[in,out] region_size The size of the region that contains stack +//! memory. This region begins at \a region_base. On return, if \a +//! region_base is decremented, \a region_size will be incremented +//! appropriately. +//! \param[in] user_tag The Mach VM system’s user tag for the region described +//! by the initial values of \a region_base and \a region_size. The red +//! zone will only be allowed to extend out of the region described by +//! these initial values if the user tag is appropriate for stack memory +//! and the expanded region has the same user tag value. +void LocateRedZone(vm_address_t* const start_address, + vm_address_t* const region_base, + vm_address_t* const region_size, + const unsigned int user_tag) { + // x86_64 has a red zone. See AMD64 ABI 0.99.8, + // https://gitlab.com/x86-psABIs/x86-64-ABI/-/wikis/uploads/01de35b2c8adc7545de52604cc45d942/x86-64-psABI-2021-05-20.pdf#page=23. + // section 3.2.2, “The Stack Frameâ€. + // So does ARM64, + // https://developer.apple.com/documentation/xcode/writing-arm64-code-for-apple-platforms#Respect-the-Stacks-Red-Zone + // section "Respect the Stack’s Red Zone". + constexpr vm_size_t kRedZoneSize = 128; + vm_address_t red_zone_base = + *start_address >= kRedZoneSize ? *start_address - kRedZoneSize : 0; + bool red_zone_ok = false; + if (red_zone_base >= *region_base) { + // The red zone is within the region already discovered. + red_zone_ok = true; + } else if (red_zone_base < *region_base && user_tag == VM_MEMORY_STACK) { + // Probe to see if there’s a region immediately below the one already + // discovered. + vm_address_t red_zone_region_base = red_zone_base; + vm_size_t red_zone_region_size; + natural_t red_zone_depth = 0; + vm_prot_t red_zone_protection; + unsigned int red_zone_user_tag; + kern_return_t kr = MachVMRegionRecurseDeepest(mach_task_self(), + &red_zone_region_base, + &red_zone_region_size, + &red_zone_depth, + &red_zone_protection, + &red_zone_user_tag); + if (kr != KERN_SUCCESS) { + *start_address = *region_base; + } else if (red_zone_region_base + red_zone_region_size == *region_base && + (red_zone_protection & VM_PROT_READ) != 0 && + red_zone_user_tag == user_tag) { + // The region containing the red zone is immediately below the region + // already found, it’s readable (not the guard region), and it has the + // same user tag as the region already found, so merge them. + red_zone_ok = true; + *region_base -= red_zone_region_size; + *region_size += red_zone_region_size; + } + } + + if (red_zone_ok) { + // Begin capturing from the base of the red zone (but not the entire + // region that encompasses the red zone). + *start_address = red_zone_base; + } else { + // The red zone would go lower into another region in memory, but no + // region was found. Memory can only be captured to an address as low as + // the base address of the region already found. + *start_address = *region_base; + } +} + +//! \brief Calculates the base address and size of the region used as a +//! thread’s stack. +//! +//! The region returned by this method may be formed by merging multiple +//! adjacent regions in a process’ memory map if appropriate. The base address +//! of the returned region may be lower than the \a stack_pointer passed in +//! when the ABI mandates a red zone below the stack pointer. +//! +//! \param[in] stack_pointer The stack pointer, referring to the top (lowest +//! address) of a thread’s stack. +//! \param[out] stack_region_size The size of the memory region used as the +//! thread’s stack. +//! +//! \return The base address (lowest address) of the memory region used as the +//! thread’s stack. +vm_address_t CalculateStackRegion(vm_address_t stack_pointer, + vm_size_t* stack_region_size) { + // For pthreads, it may be possible to compute the stack region based on the + // internal _pthread::stackaddr and _pthread::stacksize. The _pthread struct + // for a thread can be located at TSD slot 0, or the known offsets of + // stackaddr and stacksize from the TSD area could be used. + vm_address_t region_base = stack_pointer; + vm_size_t region_size; + natural_t depth = 0; + vm_prot_t protection; + unsigned int user_tag; + kern_return_t kr = MachVMRegionRecurseDeepest(mach_task_self(), + ®ion_base, + ®ion_size, + &depth, + &protection, + &user_tag); + if (kr != KERN_SUCCESS) { + CRASHPAD_RAW_LOG_ERROR(kr, "MachVMRegionRecurseDeepest"); + *stack_region_size = 0; + return 0; + } + + if (region_base > stack_pointer) { + // There’s nothing mapped at the stack pointer’s address. Something may have + // trashed the stack pointer. Note that this shouldn’t happen for a normal + // stack guard region violation because the guard region is mapped but has + // VM_PROT_NONE protection. + *stack_region_size = 0; + return 0; + } + + vm_address_t start_address = stack_pointer; + + if ((protection & VM_PROT_READ) == 0) { + // If the region isn’t readable, the stack pointer probably points to the + // guard region. Don’t include it as part of the stack, and don’t include + // anything at any lower memory address. The code below may still possibly + // find the real stack region at a memory address higher than this region. + start_address = region_base + region_size; + } else { + // If the ABI requires a red zone, adjust the region to include it if + // possible. + LocateRedZone(&start_address, ®ion_base, ®ion_size, user_tag); + + // Regardless of whether the ABI requires a red zone, capture up to + // kExtraCaptureSize additional bytes of stack, but only if present in the + // region that was already found. + constexpr vm_size_t kExtraCaptureSize = 128; + start_address = std::max(start_address >= kExtraCaptureSize + ? start_address - kExtraCaptureSize + : start_address, + region_base); + + // Align start_address to a 16-byte boundary, which can help readers by + // ensuring that data is aligned properly. This could page-align instead, + // but that might be wasteful. + constexpr vm_size_t kDesiredAlignment = 16; + start_address &= ~(kDesiredAlignment - 1); + DCHECK_GE(start_address, region_base); + } + + region_size -= (start_address - region_base); + region_base = start_address; + + vm_size_t total_region_size = region_size; + + // The stack region may have gotten split up into multiple abutting regions. + // Try to coalesce them. This frequently happens for the main thread’s stack + // when setrlimit(RLIMIT_STACK, …) is called. It may also happen if a region + // is split up due to an mprotect() or vm_protect() call. + // + // Stack regions created by the kernel and the pthreads library will be marked + // with the VM_MEMORY_STACK user tag. Scanning for multiple adjacent regions + // with the same tag should find an entire stack region. Checking that the + // protection on individual regions is not VM_PROT_NONE should guarantee that + // this algorithm doesn’t collect map entries belonging to another thread’s + // stack: well-behaved stacks (such as those created by the kernel and the + // pthreads library) have VM_PROT_NONE guard regions at their low-address + // ends. + // + // Other stack regions may not be so well-behaved and thus if user_tag is not + // VM_MEMORY_STACK, the single region that was found is used as-is without + // trying to merge it with other adjacent regions. + if (user_tag == VM_MEMORY_STACK) { + vm_address_t try_address = region_base; + vm_address_t original_try_address; + + while (try_address += region_size, + original_try_address = try_address, + (kr = MachVMRegionRecurseDeepest(mach_task_self(), + &try_address, + ®ion_size, + &depth, + &protection, + &user_tag) == KERN_SUCCESS) && + try_address == original_try_address && + (protection & VM_PROT_READ) != 0 && + user_tag == VM_MEMORY_STACK) { + total_region_size += region_size; + } + + if (kr != KERN_SUCCESS && kr != KERN_INVALID_ADDRESS) { + // Tolerate KERN_INVALID_ADDRESS because it will be returned when there + // are no more regions in the map at or above the specified |try_address|. + CRASHPAD_RAW_LOG_ERROR(kr, "MachVMRegionRecurseDeepest"); + } + } + + *stack_region_size = total_region_size; + return region_base; +} + +//! \brief Write data around \a address to intermediate dump. Must be called +//! from within a ScopedArray. +void MaybeCaptureMemoryAround(IOSIntermediateDumpWriter* writer, + uint64_t address) { + constexpr uint64_t non_address_offset = 0x10000; + if (address < non_address_offset) + return; + + constexpr uint64_t max_address = std::numeric_limits::max(); + + if (address > max_address - non_address_offset) + return; + + constexpr uint64_t kRegisterByteOffset = 128; + const uint64_t target = address - kRegisterByteOffset; + constexpr uint64_t size = 512; + static_assert(kRegisterByteOffset <= size / 2, "negative offset too large"); + + IOSIntermediateDumpWriter::ScopedArrayMap memory_region(writer); + WriteProperty( + writer, IntermediateDumpKey::kThreadContextMemoryRegionAddress, &address); + // Don't use WritePropertyBytes, this one will fail regularly if |target| + // cannot be read. + writer->AddPropertyBytes(IntermediateDumpKey::kThreadContextMemoryRegionData, + reinterpret_cast(target), + size); +} + +void CaptureMemoryPointedToByThreadState(IOSIntermediateDumpWriter* writer, + thread_state_type thread_state) { + IOSIntermediateDumpWriter::ScopedArray memory_regions( + writer, IntermediateDumpKey::kThreadContextMemoryRegions); + +#if defined(ARCH_CPU_X86_64) + MaybeCaptureMemoryAround(writer, thread_state.__rax); + MaybeCaptureMemoryAround(writer, thread_state.__rbx); + MaybeCaptureMemoryAround(writer, thread_state.__rcx); + MaybeCaptureMemoryAround(writer, thread_state.__rdx); + MaybeCaptureMemoryAround(writer, thread_state.__rdi); + MaybeCaptureMemoryAround(writer, thread_state.__rsi); + MaybeCaptureMemoryAround(writer, thread_state.__rbp); + MaybeCaptureMemoryAround(writer, thread_state.__r8); + MaybeCaptureMemoryAround(writer, thread_state.__r9); + MaybeCaptureMemoryAround(writer, thread_state.__r10); + MaybeCaptureMemoryAround(writer, thread_state.__r11); + MaybeCaptureMemoryAround(writer, thread_state.__r12); + MaybeCaptureMemoryAround(writer, thread_state.__r13); + MaybeCaptureMemoryAround(writer, thread_state.__r14); + MaybeCaptureMemoryAround(writer, thread_state.__r15); + MaybeCaptureMemoryAround(writer, thread_state.__rip); +#elif defined(ARCH_CPU_ARM_FAMILY) + MaybeCaptureMemoryAround(writer, thread_state.__pc); + for (size_t i = 0; i < std::size(thread_state.__x); ++i) { + MaybeCaptureMemoryAround(writer, thread_state.__x[i]); + } +#endif +} + +void WriteCrashpadSimpleAnnotationsDictionary(IOSIntermediateDumpWriter* writer, + CrashpadInfo* crashpad_info) { + if (!crashpad_info->simple_annotations()) + return; + + ScopedVMRead simple_annotations; + if (!simple_annotations.Read(crashpad_info->simple_annotations())) { + CRASHPAD_RAW_LOG("Unable to read simple annotations."); + return; + } + + const size_t count = simple_annotations->GetCount(); + if (!count) + return; + + IOSIntermediateDumpWriter::ScopedArray annotations_array( + writer, IntermediateDumpKey::kAnnotationsSimpleMap); + + SimpleStringDictionary::Entry* entries = + reinterpret_cast( + simple_annotations.get()); + for (size_t index = 0; index < count; index++) { + IOSIntermediateDumpWriter::ScopedArrayMap annotation_map(writer); + const auto& entry = entries[index]; + size_t key_length = strnlen(entry.key, sizeof(entry.key)); + WritePropertyBytes(writer, + IntermediateDumpKey::kAnnotationName, + reinterpret_cast(entry.key), + key_length); + size_t value_length = strnlen(entry.value, sizeof(entry.value)); + WritePropertyBytes(writer, + IntermediateDumpKey::kAnnotationValue, + reinterpret_cast(entry.value), + value_length); + } +} + +void WriteAppleCrashReporterAnnotations( + IOSIntermediateDumpWriter* writer, + crashreporter_annotations_t* crash_info) { + // This number was totally made up out of nowhere, but it seems prudent to + // enforce some limit. + constexpr size_t kMaxMessageSize = 1024; + IOSIntermediateDumpWriter::ScopedMap annotation_map( + writer, IntermediateDumpKey::kAnnotationsCrashInfo); + if (crash_info->message) { + const size_t message_len = strnlen( + reinterpret_cast(crash_info->message), kMaxMessageSize); + WritePropertyBytes(writer, + IntermediateDumpKey::kAnnotationsCrashInfoMessage1, + reinterpret_cast(crash_info->message), + message_len); + } + if (crash_info->message2) { + const size_t message_len = strnlen( + reinterpret_cast(crash_info->message2), kMaxMessageSize); + WritePropertyBytes(writer, + IntermediateDumpKey::kAnnotationsCrashInfoMessage2, + reinterpret_cast(crash_info->message2), + message_len); + } +} + +void WriteDyldErrorStringAnnotation( + IOSIntermediateDumpWriter* writer, + const uint64_t address, + const symtab_command* symtab_command_ptr, + const dysymtab_command* dysymtab_command_ptr, + const segment_command_64* text_seg_ptr, + const segment_command_64* linkedit_seg_ptr, + vm_size_t slide) { + if (text_seg_ptr == nullptr || linkedit_seg_ptr == nullptr || + symtab_command_ptr == nullptr) { + return; + } + + ScopedVMRead symtab_command; + ScopedVMRead dysymtab_command; + ScopedVMRead text_seg; + ScopedVMRead linkedit_seg; + if (!symtab_command.Read(symtab_command_ptr) || + !text_seg.Read(text_seg_ptr) || !linkedit_seg.Read(linkedit_seg_ptr) || + (dysymtab_command_ptr && !dysymtab_command.Read(dysymtab_command_ptr))) { + CRASHPAD_RAW_LOG("Unable to load dyld symbol table."); + } + + uint64_t file_slide = + (linkedit_seg->vmaddr - text_seg->vmaddr) - linkedit_seg->fileoff; + uint64_t strings = address + (symtab_command->stroff + file_slide); + nlist_64* symbol_ptr = reinterpret_cast( + address + (symtab_command->symoff + file_slide)); + + // If a dysymtab is present, use it to filter the symtab for just the + // portion used for extdefsym. If no dysymtab is present, the entire symtab + // will need to be consulted. + uint32_t symbol_count = symtab_command->nsyms; + if (dysymtab_command_ptr) { + symbol_ptr += dysymtab_command->iextdefsym; + symbol_count = dysymtab_command->nextdefsym; + } + + for (uint32_t i = 0; i < symbol_count; i++, symbol_ptr++) { + ScopedVMRead symbol; + if (!symbol.Read(symbol_ptr)) { + CRASHPAD_RAW_LOG("Unable to load dyld symbol table symbol."); + return; + } + + if (!symbol->n_value) + continue; + + ScopedVMRead symbol_name; + if (!symbol_name.Read(strings + symbol->n_un.n_strx)) { + CRASHPAD_RAW_LOG("Unable to load dyld symbol name."); + } + + if (strcmp(symbol_name.get(), "_error_string") == 0) { + ScopedVMRead symbol_value; + if (!symbol_value.Read(symbol->n_value + slide)) { + CRASHPAD_RAW_LOG("Unable to load dyld symbol value."); + } + // 1024 here is distinct from kMaxMessageSize above, because it refers to + // a precisely-sized buffer inside dyld. + const size_t value_len = strnlen(symbol_value.get(), 1024); + if (value_len) { + WriteProperty(writer, + IntermediateDumpKey::kAnnotationsDyldErrorString, + symbol_value.get(), + value_len); + } + return; + } + + continue; + } +} + +} // namespace + +// static +void InProcessIntermediateDumpHandler::WriteHeader( + IOSIntermediateDumpWriter* writer) { + static constexpr uint8_t version = 1; + WriteProperty(writer, IntermediateDumpKey::kVersion, &version); +} + +// static +void InProcessIntermediateDumpHandler::WriteProcessInfo( + IOSIntermediateDumpWriter* writer, + const std::map& annotations) { + IOSIntermediateDumpWriter::ScopedMap process_map( + writer, IntermediateDumpKey::kProcessInfo); + + timeval snapshot_time; + if (gettimeofday(&snapshot_time, nullptr) == 0) { + WriteProperty(writer, IntermediateDumpKey::kSnapshotTime, &snapshot_time); + } else { + CRASHPAD_RAW_LOG("gettimeofday"); + } + + // Used by pid, parent pid and snapshot time. + kinfo_proc kern_proc_info; + int mib[] = {CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid()}; + size_t len = sizeof(kern_proc_info); + if (sysctl(mib, std::size(mib), &kern_proc_info, &len, nullptr, 0) == 0) { + WriteProperty( + writer, IntermediateDumpKey::kPID, &kern_proc_info.kp_proc.p_pid); + WriteProperty(writer, + IntermediateDumpKey::kParentPID, + &kern_proc_info.kp_eproc.e_ppid); + WriteProperty(writer, + IntermediateDumpKey::kStartTime, + &kern_proc_info.kp_proc.p_starttime); + } else { + CRASHPAD_RAW_LOG("sysctl kern_proc_info"); + } + + // Used by user time and system time. + mach_task_basic_info task_basic_info; + mach_msg_type_number_t task_basic_info_count = MACH_TASK_BASIC_INFO_COUNT; + kern_return_t kr = task_info(mach_task_self(), + MACH_TASK_BASIC_INFO, + reinterpret_cast(&task_basic_info), + &task_basic_info_count); + if (kr == KERN_SUCCESS) { + IOSIntermediateDumpWriter::ScopedMap task_info( + writer, IntermediateDumpKey::kTaskBasicInfo); + + WriteProperty( + writer, IntermediateDumpKey::kUserTime, &task_basic_info.user_time); + WriteProperty( + writer, IntermediateDumpKey::kSystemTime, &task_basic_info.system_time); + } else { + CRASHPAD_RAW_LOG("task_info task_basic_info"); + } + + task_thread_times_info_data_t task_thread_times; + mach_msg_type_number_t task_thread_times_count = TASK_THREAD_TIMES_INFO_COUNT; + kr = task_info(mach_task_self(), + TASK_THREAD_TIMES_INFO, + reinterpret_cast(&task_thread_times), + &task_thread_times_count); + if (kr == KERN_SUCCESS) { + IOSIntermediateDumpWriter::ScopedMap task_thread_times_map( + writer, IntermediateDumpKey::kTaskThreadTimes); + + WriteProperty( + writer, IntermediateDumpKey::kUserTime, &task_thread_times.user_time); + WriteProperty(writer, + IntermediateDumpKey::kSystemTime, + &task_thread_times.system_time); + } else { + CRASHPAD_RAW_LOG("task_info thread_times_info"); + } + + if (!annotations.empty()) { + IOSIntermediateDumpWriter::ScopedArray simple_annotations_array( + writer, IntermediateDumpKey::kAnnotationsSimpleMap); + for (const auto& annotation_pair : annotations) { + const std::string& key = annotation_pair.first; + const std::string& value = annotation_pair.second; + IOSIntermediateDumpWriter::ScopedArrayMap annotation_map(writer); + WriteProperty(writer, + IntermediateDumpKey::kAnnotationName, + key.c_str(), + key.length()); + WriteProperty(writer, + IntermediateDumpKey::kAnnotationValue, + value.c_str(), + value.length()); + } + } +} + +// static +void InProcessIntermediateDumpHandler::WriteSystemInfo( + IOSIntermediateDumpWriter* writer, + const IOSSystemDataCollector& system_data) { + IOSIntermediateDumpWriter::ScopedMap system_map( + writer, IntermediateDumpKey::kSystemInfo); + + const std::string& machine_description = system_data.MachineDescription(); + WriteProperty(writer, + IntermediateDumpKey::kMachineDescription, + machine_description.c_str(), + machine_description.length()); + int os_version_major; + int os_version_minor; + int os_version_bugfix; + system_data.OSVersion( + &os_version_major, &os_version_minor, &os_version_bugfix); + WriteProperty( + writer, IntermediateDumpKey::kOSVersionMajor, &os_version_major); + WriteProperty( + writer, IntermediateDumpKey::kOSVersionMinor, &os_version_minor); + WriteProperty( + writer, IntermediateDumpKey::kOSVersionBugfix, &os_version_bugfix); + const std::string& os_version_build = system_data.Build(); + WriteProperty(writer, + IntermediateDumpKey::kOSVersionBuild, + os_version_build.c_str(), + os_version_build.length()); + + int cpu_count = system_data.ProcessorCount(); + WriteProperty(writer, IntermediateDumpKey::kCpuCount, &cpu_count); + const std::string& cpu_vendor = system_data.CPUVendor(); + WriteProperty(writer, + IntermediateDumpKey::kCpuVendor, + cpu_vendor.c_str(), + cpu_vendor.length()); + + bool has_daylight_saving_time = system_data.HasDaylightSavingTime(); + WriteProperty(writer, + IntermediateDumpKey::kHasDaylightSavingTime, + &has_daylight_saving_time); + bool is_daylight_saving_time = system_data.IsDaylightSavingTime(); + WriteProperty(writer, + IntermediateDumpKey::kIsDaylightSavingTime, + &is_daylight_saving_time); + int standard_offset_seconds = system_data.StandardOffsetSeconds(); + WriteProperty(writer, + IntermediateDumpKey::kStandardOffsetSeconds, + &standard_offset_seconds); + int daylight_offset_seconds = system_data.DaylightOffsetSeconds(); + WriteProperty(writer, + IntermediateDumpKey::kDaylightOffsetSeconds, + &daylight_offset_seconds); + const std::string& standard_name = system_data.StandardName(); + WriteProperty(writer, + IntermediateDumpKey::kStandardName, + standard_name.c_str(), + standard_name.length()); + const std::string& daylight_name = system_data.DaylightName(); + WriteProperty(writer, + IntermediateDumpKey::kDaylightName, + daylight_name.c_str(), + daylight_name.length()); + + vm_size_t page_size; + host_page_size(mach_host_self(), &page_size); + WriteProperty(writer, IntermediateDumpKey::kPageSize, &page_size); + + mach_msg_type_number_t host_size = + sizeof(vm_statistics_data_t) / sizeof(integer_t); + vm_statistics_data_t vm_stat; + kern_return_t kr = host_statistics(mach_host_self(), + HOST_VM_INFO, + reinterpret_cast(&vm_stat), + &host_size); + if (kr == KERN_SUCCESS) { + IOSIntermediateDumpWriter::ScopedMap vm_stat_map( + writer, IntermediateDumpKey::kVMStat); + + WriteProperty(writer, IntermediateDumpKey::kActive, &vm_stat.active_count); + WriteProperty( + writer, IntermediateDumpKey::kInactive, &vm_stat.inactive_count); + WriteProperty(writer, IntermediateDumpKey::kWired, &vm_stat.wire_count); + WriteProperty(writer, IntermediateDumpKey::kFree, &vm_stat.free_count); + } else { + CRASHPAD_RAW_LOG("host_statistics"); + } +} + +// static +void InProcessIntermediateDumpHandler::WriteThreadInfo( + IOSIntermediateDumpWriter* writer, + const uint64_t* frames, + const size_t num_frames) { + IOSIntermediateDumpWriter::ScopedArray thread_array( + writer, IntermediateDumpKey::kThreads); + + // Exception thread ID. +#if defined(ARCH_CPU_ARM64) + uint64_t exception_thread_id = 0; +#endif + thread_identifier_info identifier_info; + mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT; + kern_return_t kr = + thread_info(mach_thread_self(), + THREAD_IDENTIFIER_INFO, + reinterpret_cast(&identifier_info), + &count); + if (kr == KERN_SUCCESS) { +#if defined(ARCH_CPU_ARM64) + exception_thread_id = identifier_info.thread_id; +#endif + } else { + CRASHPAD_RAW_LOG_ERROR(kr, "thread_info::THREAD_IDENTIFIER_INFO"); + } + + mach_msg_type_number_t thread_count = 0; + thread_act_array_t threads; + kr = task_threads(mach_task_self(), &threads, &thread_count); + if (kr != KERN_SUCCESS) { + CRASHPAD_RAW_LOG_ERROR(kr, "task_threads"); + } + ScopedTaskThreads threads_vm_owner(threads, thread_count); + + for (uint32_t thread_index = 0; thread_index < thread_count; ++thread_index) { + IOSIntermediateDumpWriter::ScopedArrayMap thread_map(writer); + thread_t thread = threads[thread_index]; + + thread_basic_info basic_info; + count = THREAD_BASIC_INFO_COUNT; + kr = thread_info(thread, + THREAD_BASIC_INFO, + reinterpret_cast(&basic_info), + &count); + if (kr == KERN_SUCCESS) { + WriteProperty(writer, + IntermediateDumpKey::kSuspendCount, + &basic_info.suspend_count); + } else { + CRASHPAD_RAW_LOG_ERROR(kr, "thread_info::THREAD_BASIC_INFO"); + } + + thread_precedence_policy precedence; + count = THREAD_PRECEDENCE_POLICY_COUNT; + boolean_t get_default = FALSE; + kr = thread_policy_get(thread, + THREAD_PRECEDENCE_POLICY, + reinterpret_cast(&precedence), + &count, + &get_default); + if (kr == KERN_SUCCESS) { + WriteProperty( + writer, IntermediateDumpKey::kPriority, &precedence.importance); + } else { + CRASHPAD_RAW_LOG_ERROR(kr, "thread_policy_get"); + } + + // Thread ID. +#if defined(ARCH_CPU_ARM64) + uint64_t thread_id; +#endif + count = THREAD_IDENTIFIER_INFO_COUNT; + kr = thread_info(thread, + THREAD_IDENTIFIER_INFO, + reinterpret_cast(&identifier_info), + &count); + if (kr == KERN_SUCCESS) { +#if defined(ARCH_CPU_ARM64) + thread_id = identifier_info.thread_id; +#endif + WriteProperty( + writer, IntermediateDumpKey::kThreadID, &identifier_info.thread_id); + WriteProperty(writer, + IntermediateDumpKey::kThreadDataAddress, + &identifier_info.thread_handle); + } else { + CRASHPAD_RAW_LOG_ERROR(kr, "thread_info::THREAD_IDENTIFIER_INFO"); + } + + // thread_snapshot_ios_intermediate_dump::GenerateStackMemoryFromFrames is + // only implemented for arm64, so no x86_64 block here. +#if defined(ARCH_CPU_ARM64) + // For uncaught NSExceptions, use the frames passed from the system rather + // than the current thread state. + if (num_frames > 0 && exception_thread_id == thread_id) { + WriteProperty(writer, + IntermediateDumpKey::kThreadUncaughtNSExceptionFrames, + frames, + num_frames); + continue; + } +#endif + +#if defined(ARCH_CPU_X86_64) + x86_thread_state64_t thread_state; + x86_float_state64_t float_state; + x86_debug_state64_t debug_state; + mach_msg_type_number_t thread_state_count = x86_THREAD_STATE64_COUNT; + mach_msg_type_number_t float_state_count = x86_FLOAT_STATE64_COUNT; + mach_msg_type_number_t debug_state_count = x86_DEBUG_STATE64_COUNT; +#elif defined(ARCH_CPU_ARM64) + arm_thread_state64_t thread_state; + arm_neon_state64_t float_state; + arm_debug_state64_t debug_state; + mach_msg_type_number_t thread_state_count = ARM_THREAD_STATE64_COUNT; + mach_msg_type_number_t float_state_count = ARM_NEON_STATE64_COUNT; + mach_msg_type_number_t debug_state_count = ARM_DEBUG_STATE64_COUNT; +#endif + + kr = thread_get_state(thread, + kThreadStateFlavor, + reinterpret_cast(&thread_state), + &thread_state_count); + if (kr != KERN_SUCCESS) { + CRASHPAD_RAW_LOG_ERROR(kr, "thread_get_state::kThreadStateFlavor"); + } + WriteProperty(writer, IntermediateDumpKey::kThreadState, &thread_state); + + kr = thread_get_state(thread, + kFloatStateFlavor, + reinterpret_cast(&float_state), + &float_state_count); + if (kr != KERN_SUCCESS) { + CRASHPAD_RAW_LOG_ERROR(kr, "thread_get_state::kFloatStateFlavor"); + } + WriteProperty(writer, IntermediateDumpKey::kFloatState, &float_state); + + kr = thread_get_state(thread, + kDebugStateFlavor, + reinterpret_cast(&debug_state), + &debug_state_count); + if (kr != KERN_SUCCESS) { + CRASHPAD_RAW_LOG_ERROR(kr, "thread_get_state::kDebugStateFlavor"); + } + WriteProperty(writer, IntermediateDumpKey::kDebugState, &debug_state); + +#if defined(ARCH_CPU_X86_64) + vm_address_t stack_pointer = thread_state.__rsp; +#elif defined(ARCH_CPU_ARM64) + vm_address_t stack_pointer = thread_state.__sp; +#endif + + vm_size_t stack_region_size; + const vm_address_t stack_region_address = + CalculateStackRegion(stack_pointer, &stack_region_size); + WriteProperty(writer, + IntermediateDumpKey::kStackRegionAddress, + &stack_region_address); + WritePropertyBytes(writer, + IntermediateDumpKey::kStackRegionData, + reinterpret_cast(stack_region_address), + stack_region_size); + + // Grab extra memory from context. + CaptureMemoryPointedToByThreadState(writer, thread_state); + } +} + +// static +void InProcessIntermediateDumpHandler::WriteModuleInfo( + IOSIntermediateDumpWriter* writer) { +#ifndef ARCH_CPU_64_BITS +#error Only 64-bit Mach-O is supported +#endif + + IOSIntermediateDumpWriter::ScopedArray module_array( + writer, IntermediateDumpKey::kModules); + + task_dyld_info_data_t dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + kern_return_t kr = task_info(mach_task_self(), + TASK_DYLD_INFO, + reinterpret_cast(&dyld_info), + &count); + if (kr != KERN_SUCCESS) { + CRASHPAD_RAW_LOG_ERROR(kr, "task_info"); + } + + ScopedVMRead image_infos; + if (!image_infos.Read(dyld_info.all_image_info_addr)) { + CRASHPAD_RAW_LOG("Unable to dyld_info.all_image_info_addr"); + return; + } + + uint32_t image_count = image_infos->infoArrayCount; + const dyld_image_info* image_array = image_infos->infoArray; + for (uint32_t image_index = 0; image_index < image_count; ++image_index) { + IOSIntermediateDumpWriter::ScopedArrayMap modules(writer); + ScopedVMRead image; + if (!image.Read(&image_array[image_index])) { + CRASHPAD_RAW_LOG("Unable to dyld_image_info"); + return; + } + + WriteProperty(writer, + IntermediateDumpKey::kName, + image->imageFilePath, + strlen(image->imageFilePath)); + uint64_t address = FromPointerCast(image->imageLoadAddress); + WriteProperty(writer, IntermediateDumpKey::kAddress, &address); + WriteProperty( + writer, IntermediateDumpKey::kTimestamp, &image->imageFileModDate); + WriteModuleInfoAtAddress(writer, address, false /*is_dyld=false*/); + } + + { + IOSIntermediateDumpWriter::ScopedArrayMap modules(writer); + WriteProperty(writer, IntermediateDumpKey::kName, image_infos->dyldPath); + uint64_t address = + FromPointerCast(image_infos->dyldImageLoadAddress); + WriteProperty(writer, IntermediateDumpKey::kAddress, &address); + WriteModuleInfoAtAddress(writer, address, true /*is_dyld=true*/); + } +} + +// static +void InProcessIntermediateDumpHandler::WriteExceptionFromSignal( + IOSIntermediateDumpWriter* writer, + const IOSSystemDataCollector& system_data, + siginfo_t* siginfo, + ucontext_t* context) { + IOSIntermediateDumpWriter::ScopedMap signal_exception_map( + writer, IntermediateDumpKey::kSignalException); + + WriteProperty(writer, IntermediateDumpKey::kSignalNumber, &siginfo->si_signo); + WriteProperty(writer, IntermediateDumpKey::kSignalCode, &siginfo->si_code); + WriteProperty(writer, IntermediateDumpKey::kSignalAddress, &siginfo->si_addr); +#if defined(ARCH_CPU_X86_64) + WriteProperty( + writer, IntermediateDumpKey::kThreadState, &context->uc_mcontext->__ss); + WriteProperty( + writer, IntermediateDumpKey::kFloatState, &context->uc_mcontext->__fs); +#elif defined(ARCH_CPU_ARM64) + WriteProperty( + writer, IntermediateDumpKey::kThreadState, &context->uc_mcontext->__ss); + WriteProperty( + writer, IntermediateDumpKey::kFloatState, &context->uc_mcontext->__ns); +#else +#error Port to your CPU architecture +#endif + + // Thread ID. + thread_identifier_info identifier_info; + mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT; + kern_return_t kr = + thread_info(mach_thread_self(), + THREAD_IDENTIFIER_INFO, + reinterpret_cast(&identifier_info), + &count); + if (kr == KERN_SUCCESS) { + WriteProperty( + writer, IntermediateDumpKey::kThreadID, &identifier_info.thread_id); + } else { + CRASHPAD_RAW_LOG_ERROR(kr, "thread_info::self"); + } +} + +// static +void InProcessIntermediateDumpHandler::WriteExceptionFromMachException( + IOSIntermediateDumpWriter* writer, + exception_behavior_t behavior, + thread_t exception_thread, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t flavor, + ConstThreadState state, + mach_msg_type_number_t state_count) { + IOSIntermediateDumpWriter::ScopedMap mach_exception_map( + writer, IntermediateDumpKey::kMachException); + + WriteProperty(writer, IntermediateDumpKey::kException, &exception); + WriteProperty(writer, IntermediateDumpKey::kCodes, code, code_count); + WriteProperty(writer, IntermediateDumpKey::kFlavor, &flavor); + WritePropertyBytes(writer, + IntermediateDumpKey::kState, + state, + state_count * sizeof(uint32_t)); + + thread_identifier_info identifier_info; + mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT; + kern_return_t kr = + thread_info(exception_thread, + THREAD_IDENTIFIER_INFO, + reinterpret_cast(&identifier_info), + &count); + if (kr == KERN_SUCCESS) { + WriteProperty( + writer, IntermediateDumpKey::kThreadID, &identifier_info.thread_id); + } else { + CRASHPAD_RAW_LOG_ERROR(kr, "thread_info"); + } +} + +// static +void InProcessIntermediateDumpHandler::WriteExceptionFromNSException( + IOSIntermediateDumpWriter* writer) { + IOSIntermediateDumpWriter::ScopedMap nsexception_map( + writer, IntermediateDumpKey::kNSException); + + thread_identifier_info identifier_info; + mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT; + kern_return_t kr = + thread_info(mach_thread_self(), + THREAD_IDENTIFIER_INFO, + reinterpret_cast(&identifier_info), + &count); + if (kr == KERN_SUCCESS) { + WriteProperty( + writer, IntermediateDumpKey::kThreadID, &identifier_info.thread_id); + } else { + CRASHPAD_RAW_LOG_ERROR(kr, "thread_info::self"); + } +} + +void InProcessIntermediateDumpHandler::WriteModuleInfoAtAddress( + IOSIntermediateDumpWriter* writer, + uint64_t address, + bool is_dyld) { + ScopedVMRead header; + if (!header.Read(address) || header->magic != MH_MAGIC_64) { + CRASHPAD_RAW_LOG("Invalid module header"); + return; + } + + const load_command* command_ptr = reinterpret_cast( + reinterpret_cast(address) + 1); + + ScopedVMRead command; + if (!command.Read(command_ptr)) { + CRASHPAD_RAW_LOG("Invalid module command"); + return; + } + + // Make sure that the basic load command structure doesn’t overflow the + // space allotted for load commands, as well as iterating through ncmds. + vm_size_t slide = 0; + const symtab_command* symtab_command = nullptr; + const dysymtab_command* dysymtab_command = nullptr; + const segment_command_64* linkedit_seg = nullptr; + const segment_command_64* text_seg = nullptr; + for (uint32_t cmd_index = 0, cumulative_cmd_size = 0; + cmd_index <= header->ncmds && cumulative_cmd_size < header->sizeofcmds; + ++cmd_index, cumulative_cmd_size += command->cmdsize) { + if (command->cmd == LC_SEGMENT_64) { + ScopedVMRead segment; + if (!segment.Read(command_ptr)) { + CRASHPAD_RAW_LOG("Invalid LC_SEGMENT_64 segment"); + return; + } + const segment_command_64* segment_ptr = + reinterpret_cast(command_ptr); + if (strcmp(segment->segname, SEG_TEXT) == 0) { + text_seg = segment_ptr; + WriteProperty(writer, IntermediateDumpKey::kSize, &segment->vmsize); + slide = address - segment->vmaddr; + } else if (strcmp(segment->segname, SEG_DATA) == 0) { + WriteDataSegmentAnnotations(writer, segment_ptr, slide); + } else if (strcmp(segment->segname, SEG_LINKEDIT) == 0) { + linkedit_seg = segment_ptr; + } + } else if (command->cmd == LC_SYMTAB) { + symtab_command = + reinterpret_cast(command_ptr); + } else if (command->cmd == LC_DYSYMTAB) { + dysymtab_command = + reinterpret_cast(command_ptr); + } else if (command->cmd == LC_ID_DYLIB) { + ScopedVMRead dylib; + if (!dylib.Read(command_ptr)) { + CRASHPAD_RAW_LOG("Invalid LC_ID_DYLIB segment"); + return; + } + WriteProperty(writer, + IntermediateDumpKey::kDylibCurrentVersion, + &dylib->dylib.current_version); + } else if (command->cmd == LC_SOURCE_VERSION) { + ScopedVMRead source_version; + if (!source_version.Read(command_ptr)) { + CRASHPAD_RAW_LOG("Invalid LC_SOURCE_VERSION segment"); + return; + } + WriteProperty(writer, + IntermediateDumpKey::kSourceVersion, + &source_version->version); + } else if (command->cmd == LC_UUID) { + ScopedVMRead uuid; + if (!uuid.Read(command_ptr)) { + CRASHPAD_RAW_LOG("Invalid LC_UUID segment"); + return; + } + WriteProperty(writer, IntermediateDumpKey::kUUID, &uuid->uuid); + } + + command_ptr = reinterpret_cast( + reinterpret_cast(command_ptr) + command->cmdsize); + if (!command.Read(command_ptr)) { + CRASHPAD_RAW_LOG("Invalid module command"); + return; + } + } + + WriteProperty(writer, IntermediateDumpKey::kFileType, &header->filetype); + + if (is_dyld && header->filetype == MH_DYLINKER) { + WriteDyldErrorStringAnnotation(writer, + address, + symtab_command, + dysymtab_command, + text_seg, + linkedit_seg, + slide); + } +} + +void InProcessIntermediateDumpHandler::WriteDataSegmentAnnotations( + IOSIntermediateDumpWriter* writer, + const segment_command_64* segment_ptr, + vm_size_t slide) { + ScopedVMRead segment; + if (!segment.Read(segment_ptr)) { + CRASHPAD_RAW_LOG("Unable to read SEG_DATA."); + return; + } + const section_64* section_ptr = reinterpret_cast( + reinterpret_cast(segment_ptr) + sizeof(segment_command_64)); + for (uint32_t sect_index = 0; sect_index <= segment->nsects; ++sect_index) { + ScopedVMRead section; + if (!section.Read(section_ptr)) { + CRASHPAD_RAW_LOG("Unable to read SEG_DATA section."); + return; + } + if (strcmp(section->sectname, "crashpad_info") == 0) { + ScopedVMRead crashpad_info; + if (crashpad_info.Read(section->addr + slide) && + crashpad_info->size() == sizeof(CrashpadInfo) && + crashpad_info->signature() == CrashpadInfo::kSignature && + crashpad_info->version() == 1) { + WriteCrashpadAnnotationsList(writer, crashpad_info.get()); + WriteCrashpadSimpleAnnotationsDictionary(writer, crashpad_info.get()); + } + } else if (strcmp(section->sectname, "__crash_info") == 0) { + ScopedVMRead crash_info; + if (!crash_info.Read(section->addr + slide) || + (crash_info->version != 4 && crash_info->version != 5)) { + continue; + } + WriteAppleCrashReporterAnnotations(writer, crash_info.get()); + } + section_ptr = reinterpret_cast( + reinterpret_cast(section_ptr) + sizeof(section_64)); + } +} + +void InProcessIntermediateDumpHandler::WriteCrashpadAnnotationsList( + IOSIntermediateDumpWriter* writer, + CrashpadInfo* crashpad_info) { + if (!crashpad_info->annotations_list()) { + return; + } + ScopedVMRead annotation_list; + if (!annotation_list.Read(crashpad_info->annotations_list())) { + CRASHPAD_RAW_LOG("Unable to read annotations list object"); + return; + } + + IOSIntermediateDumpWriter::ScopedArray annotations_array( + writer, IntermediateDumpKey::kAnnotationObjects); + ScopedVMRead current; + if (!current.Read(annotation_list->head())) { + CRASHPAD_RAW_LOG("Unable to read annotation"); + return; + } + + for (size_t index = 0; + current->link_node() != annotation_list.get()->tail_pointer() && + index < kMaxNumberOfAnnotations; + ++index) { + ScopedVMRead node; + if (!node.Read(current->link_node())) { + CRASHPAD_RAW_LOG("Unable to read annotation"); + return; + } + current.Read(current->link_node()); + + if (node->size() == 0) + continue; + + if (node->size() > Annotation::kValueMaxSize) { + CRASHPAD_RAW_LOG("Incorrect annotation length"); + continue; + } + + IOSIntermediateDumpWriter::ScopedArrayMap annotation_map(writer); + const size_t name_len = strnlen(reinterpret_cast(node->name()), + Annotation::kNameMaxLength); + WritePropertyBytes(writer, + IntermediateDumpKey::kAnnotationName, + reinterpret_cast(node->name()), + name_len); + WritePropertyBytes(writer, + IntermediateDumpKey::kAnnotationValue, + reinterpret_cast(node->value()), + node->size()); + Annotation::Type type = node->type(); + WritePropertyBytes(writer, + IntermediateDumpKey::kAnnotationType, + reinterpret_cast(&type), + sizeof(type)); + } +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/ios_handler/in_process_intermediate_dump_handler.h b/shared/sentry/external/crashpad/client/ios_handler/in_process_intermediate_dump_handler.h new file mode 100644 index 000000000..f6db28d51 --- /dev/null +++ b/shared/sentry/external/crashpad/client/ios_handler/in_process_intermediate_dump_handler.h @@ -0,0 +1,155 @@ +// Copyright 2021 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_CLIENT_IOS_HANDLER_IN_PROCESS_INTERMEDIATE_DUMP_HANDLER_H_ +#define CRASHPAD_CLIENT_IOS_HANDLER_IN_PROCESS_INTERMEDIATE_DUMP_HANDLER_H_ + +#include +#include +#include +#include + +#include + +#include "client/crashpad_info.h" +#include "util/ios/ios_intermediate_dump_writer.h" +#include "util/ios/ios_system_data_collector.h" +#include "util/mach/mach_extensions.h" + +namespace crashpad { +namespace internal { + +//! \brief Dump all in-process data to iOS intermediate dump. +//! Note: All methods are `RUNS-DURING-CRASH`. +class InProcessIntermediateDumpHandler final { + public: + InProcessIntermediateDumpHandler() = delete; + InProcessIntermediateDumpHandler(const InProcessIntermediateDumpHandler&) = + delete; + InProcessIntermediateDumpHandler& operator=( + const InProcessIntermediateDumpHandler&) = delete; + + //! \brief Set kVersion to 1. + //! + //! \param[in] writer The dump writer + static void WriteHeader(IOSIntermediateDumpWriter* writer); + + //! \brief Write ProcessSnapshot data to the intermediate dump. + //! + //! \param[in] writer The dump writer + //! \param[in] annotations The simple map annotations. + static void WriteProcessInfo( + IOSIntermediateDumpWriter* writer, + const std::map& annotations); + + //! \brief Write SystemSnapshot data to the intermediate dump. + //! + //! \param[in] writer The dump writer + static void WriteSystemInfo(IOSIntermediateDumpWriter* writer, + const IOSSystemDataCollector& system_data); + + //! \brief Write ThreadSnapshot data to the intermediate dump. + //! + //! For uncaught NSExceptions, \a frames and \a num_frames will be added to + //! the intermediate dump for the exception thread. Otherwise, or for the + //! remaining threads, use `thread_get_state`. + //! + //! \param[in] writer The dump writer + //! \param[in] frames An array of callstack return addresses. + //! \param[in] num_frames The number of callstack return address in \a frames. + static void WriteThreadInfo(IOSIntermediateDumpWriter* writer, + const uint64_t* frames, + const size_t num_frames); + + //! \brief Write ModuleSnapshot data to the intermediate dump. + //! + //! This includes both modules and annotations. + //! + //! \param[in] writer The dump writer + static void WriteModuleInfo(IOSIntermediateDumpWriter* writer); + + //! \brief Write an ExceptionSnapshot from a signal to the intermediate dump. + //! + //! Only one of the WriteExceptionFromSignal, WriteExceptionFromMachException + //! and WriteExceptionFromNSException should be called per intermediate dump. + //! + //! \param[in] writer The dump writer + //! \param[in] system_data An object containing various system data points. + //! \param[in] siginfo A pointer to a `siginfo_t` object received by a signal + //! handler. + //! \param[in] context A pointer to a `ucontext_t` object received by a + //! signal. + static void WriteExceptionFromSignal( + IOSIntermediateDumpWriter* writer, + const IOSSystemDataCollector& system_data, + siginfo_t* siginfo, + ucontext_t* context); + + //! \brief Write an ExceptionSnapshot from a mach exception to the + //! intermediate dump. + //! + //! Only one of the WriteExceptionFromSignal, WriteExceptionFromMachException + //! and WriteExceptionFromNSException should be called per intermediate dump. + //! + //! \param[in] writer The dump writer + //! \param[in] system_data An object containing various system data points. + //! \param[in] behavior + //! \param[in] thread + //! \param[in] exception + //! \param[in] code + //! \param[in] code_count + //! \param[in] flavor + //! \param[in] old_state + //! \param[in] old_state_count + static void WriteExceptionFromMachException( + IOSIntermediateDumpWriter* writer, + exception_behavior_t behavior, + thread_t thread, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count); + + //! \brief Write an ExceptionSnapshot from an NSException to the + //! intermediate dump. + //! + //! Only one of the WriteExceptionFromSignal, WriteExceptionFromMachException + //! and WriteExceptionFromNSException should be called per intermediate dump. + //! + //! \param[in] writer The dump writer + static void WriteExceptionFromNSException(IOSIntermediateDumpWriter* writer); + + private: + //! \brief Parse and extract module and annotation information from header. + static void WriteModuleInfoAtAddress(IOSIntermediateDumpWriter* writer, + uint64_t address, + bool is_dyld); + + //! \brief Extract and write Apple crashreporter_annotations_t data and + //! Crashpad annotations. + static void WriteDataSegmentAnnotations(IOSIntermediateDumpWriter* writer, + const segment_command_64* segment_ptr, + vm_size_t slide); + + //! \brief Write Crashpad annotations list. + static void WriteCrashpadAnnotationsList(IOSIntermediateDumpWriter* writer, + CrashpadInfo* crashpad_info); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_CLIENT_IOS_HANDLER_IN_PROCESS_INTERMEDIATE_DUMP_HANDLER_H_ diff --git a/shared/sentry/external/crashpad/client/ios_handler/in_process_intermediate_dump_handler_test.cc b/shared/sentry/external/crashpad/client/ios_handler/in_process_intermediate_dump_handler_test.cc new file mode 100644 index 000000000..e61a604d9 --- /dev/null +++ b/shared/sentry/external/crashpad/client/ios_handler/in_process_intermediate_dump_handler_test.cc @@ -0,0 +1,252 @@ +// Copyright 2021 The Crashpad Authors. All rights reserved. +// +// 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 "client/ios_handler/in_process_intermediate_dump_handler.h" + +#include + +#include + +#include "base/files/file_path.h" +#include "build/build_config.h" +#include "client/annotation.h" +#include "client/annotation_list.h" +#include "client/crashpad_info.h" +#include "client/simple_string_dictionary.h" +#include "gtest/gtest.h" +#include "snapshot/ios/process_snapshot_ios_intermediate_dump.h" +#include "test/scoped_temp_dir.h" +#include "test/test_paths.h" +#include "util/file/filesystem.h" +#include "util/misc/capture_context.h" + +namespace crashpad { +namespace test { +namespace { + +using internal::InProcessIntermediateDumpHandler; + +class InProcessIntermediateDumpHandlerTest : public testing::Test { + protected: + // testing::Test: + + void SetUp() override { + path_ = temp_dir_.path().Append("dump_file"); + writer_ = std::make_unique(); + EXPECT_TRUE(writer_->Open(path_)); + ASSERT_TRUE(IsRegularFile(path_)); + } + + void TearDown() override { + writer_.reset(); + EXPECT_FALSE(IsRegularFile(path_)); + } + + void WriteReport() { + internal::IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer_.get()); + InProcessIntermediateDumpHandler::WriteHeader(writer_.get()); + InProcessIntermediateDumpHandler::WriteProcessInfo( + writer_.get(), {{"before_dump", "pre"}}); + InProcessIntermediateDumpHandler::WriteSystemInfo(writer_.get(), + system_data_); + InProcessIntermediateDumpHandler::WriteThreadInfo(writer_.get(), 0, 0); + InProcessIntermediateDumpHandler::WriteModuleInfo(writer_.get()); + } + + void WriteMachException() { + crashpad::NativeCPUContext cpu_context; + crashpad::CaptureContext(&cpu_context); + const mach_exception_data_type_t code[2] = {}; + static constexpr int kSimulatedException = -1; + InProcessIntermediateDumpHandler::WriteExceptionFromMachException( + writer_.get(), + MACH_EXCEPTION_CODES, + mach_thread_self(), + kSimulatedException, + code, + std::size(code), + MACHINE_THREAD_STATE, + reinterpret_cast(&cpu_context), + MACHINE_THREAD_STATE_COUNT); + } + + const auto& path() const { return path_; } + auto writer() const { return writer_.get(); } + + private: + std::unique_ptr writer_; + internal::IOSSystemDataCollector system_data_; + ScopedTempDir temp_dir_; + base::FilePath path_; +}; + +TEST_F(InProcessIntermediateDumpHandlerTest, TestSystem) { + WriteReport(); + internal::ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), {})); + + // Snpahot + const SystemSnapshot* system = process_snapshot.System(); + ASSERT_NE(system, nullptr); +#if defined(ARCH_CPU_X86_64) + EXPECT_EQ(system->GetCPUArchitecture(), kCPUArchitectureX86_64); + EXPECT_STREQ(system->CPUVendor().c_str(), "GenuineIntel"); +#elif defined(ARCH_CPU_ARM64) + EXPECT_EQ(system->GetCPUArchitecture(), kCPUArchitectureARM64); +#else +#error Port to your CPU architecture +#endif +#if TARGET_OS_SIMULATOR + EXPECT_EQ(system->MachineDescription().substr(0, 13), + std::string("iOS Simulator")); +#elif TARGET_OS_IPHONE + utsname uts; + ASSERT_EQ(uname(&uts), 0); + EXPECT_STREQ(system->MachineDescription().c_str(), uts.machine); +#endif + + EXPECT_EQ(system->GetOperatingSystem(), SystemSnapshot::kOperatingSystemIOS); +} + +TEST_F(InProcessIntermediateDumpHandlerTest, TestAnnotations) { + // This is “leaked†to crashpad_info. + crashpad::SimpleStringDictionary* simple_annotations = + new crashpad::SimpleStringDictionary(); + simple_annotations->SetKeyValue("#TEST# pad", "break"); + simple_annotations->SetKeyValue("#TEST# key", "value"); + simple_annotations->SetKeyValue("#TEST# pad", "crash"); + simple_annotations->SetKeyValue("#TEST# x", "y"); + simple_annotations->SetKeyValue("#TEST# longer", "shorter"); + simple_annotations->SetKeyValue("#TEST# empty_value", ""); + + crashpad::CrashpadInfo* crashpad_info = + crashpad::CrashpadInfo::GetCrashpadInfo(); + + crashpad_info->set_simple_annotations(simple_annotations); + + crashpad::AnnotationList::Register(); // This is “leaked†to crashpad_info. + + static crashpad::StringAnnotation<32> test_annotation_one{"#TEST# one"}; + static crashpad::StringAnnotation<32> test_annotation_two{"#TEST# two"}; + static crashpad::StringAnnotation<32> test_annotation_three{ + "#TEST# same-name"}; + static crashpad::StringAnnotation<32> test_annotation_four{ + "#TEST# same-name"}; + + test_annotation_one.Set("moocow"); + test_annotation_two.Set("this will be cleared"); + test_annotation_three.Set("same-name 3"); + test_annotation_four.Set("same-name 4"); + test_annotation_two.Clear(); + + WriteReport(); + internal::ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_TRUE(process_snapshot.InitializeWithFilePath( + path(), {{"after_dump", "post"}})); + + auto process_map = process_snapshot.AnnotationsSimpleMap(); + EXPECT_EQ(process_map.size(), 2u); + EXPECT_EQ(process_map["before_dump"], "pre"); + EXPECT_EQ(process_map["after_dump"], "post"); + + std::map all_annotations_simple_map; + std::vector all_annotations; + for (const auto* module : process_snapshot.Modules()) { + std::map module_annotations_simple_map = + module->AnnotationsSimpleMap(); + all_annotations_simple_map.insert(module_annotations_simple_map.begin(), + module_annotations_simple_map.end()); + + std::vector annotations = module->AnnotationObjects(); + all_annotations.insert( + all_annotations.end(), annotations.begin(), annotations.end()); + } + + EXPECT_EQ(all_annotations_simple_map.size(), 5u); + EXPECT_EQ(all_annotations_simple_map["#TEST# pad"], "crash"); + EXPECT_EQ(all_annotations_simple_map["#TEST# key"], "value"); + EXPECT_EQ(all_annotations_simple_map["#TEST# x"], "y"); + EXPECT_EQ(all_annotations_simple_map["#TEST# longer"], "shorter"); + EXPECT_EQ(all_annotations_simple_map["#TEST# empty_value"], ""); + + bool saw_same_name_3 = false, saw_same_name_4 = false; + for (const auto& annotation : all_annotations) { + EXPECT_EQ(annotation.type, + static_cast(Annotation::Type::kString)); + std::string value(reinterpret_cast(annotation.value.data()), + annotation.value.size()); + if (annotation.name == "#TEST# one") { + EXPECT_EQ(value, "moocow"); + } else if (annotation.name == "#TEST# same-name") { + if (value == "same-name 3") { + EXPECT_FALSE(saw_same_name_3); + saw_same_name_3 = true; + } else if (value == "same-name 4") { + EXPECT_FALSE(saw_same_name_4); + saw_same_name_4 = true; + } else { + ADD_FAILURE() << "unexpected annotation value " << value; + } + } else { + ADD_FAILURE() << "unexpected annotation " << annotation.name; + } + } +} + +TEST_F(InProcessIntermediateDumpHandlerTest, TestThreads) { + WriteReport(); + internal::ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), {})); + + const auto& threads = process_snapshot.Threads(); + ASSERT_GT(threads.size(), 0u); + + thread_identifier_info identifier_info; + mach_msg_type_number_t count = THREAD_IDENTIFIER_INFO_COUNT; + ASSERT_EQ(thread_info(mach_thread_self(), + THREAD_IDENTIFIER_INFO, + reinterpret_cast(&identifier_info), + &count), + 0); + EXPECT_EQ(threads[0]->ThreadID(), identifier_info.thread_id); +} + +TEST_F(InProcessIntermediateDumpHandlerTest, TestProcess) { + WriteReport(); + internal::ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), {})); + EXPECT_EQ(process_snapshot.ProcessID(), getpid()); +} + +TEST_F(InProcessIntermediateDumpHandlerTest, TestMachException) { + WriteReport(); + internal::ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), {})); +} + +TEST_F(InProcessIntermediateDumpHandlerTest, TestSignalException) { + WriteReport(); + internal::ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), {})); +} + +TEST_F(InProcessIntermediateDumpHandlerTest, TestNSException) { + WriteReport(); + internal::ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), {})); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.cc b/shared/sentry/external/crashpad/client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.cc new file mode 100644 index 000000000..651b39077 --- /dev/null +++ b/shared/sentry/external/crashpad/client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.cc @@ -0,0 +1,148 @@ +// Copyright 2021 The Crashpad Authors. All rights reserved. +// +// 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 "client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.h" + +#include + +#include "client/prune_crash_reports.h" +#include "util/file/directory_reader.h" +#include "util/file/filesystem.h" +#include "util/ios/scoped_background_task.h" + +namespace crashpad { + +namespace { + +// The file extension used to indicate a file is locked. +constexpr char kLockedExtension[] = ".locked"; + +// Prune onces a day. +constexpr time_t prune_interval = 60 * 60 * 24; + +// If the client finds a locked file matching it's own bundle id, unlock it +// after 24 hours. +constexpr time_t matching_bundle_locked_ttl = 60 * 60 * 24; + +// Unlock any locked intermediate dump after 60 days. +constexpr time_t max_locked_ttl = 60 * 60 * 24 * 60; + +// The initial thread delay for applications. Delay the thread's file i/o to +// not interfere with application startup. +constexpr double app_delay = 60; + +// The initial thread delay for app extensions. Because iOS extensions are often +// very short lived, do not wait the full |app_delay|, and instead use a shorter +// time. +constexpr double extension_delay = 5; + + +//! \brief Unlocks old intermediate dumps. +//! +//! This function can unlock (remove the .locked extension) intermediate dumps +//! that are either too old to be useful, or are likely leftover dumps from +//! clean app exits. +//! +//! \param[in] pending_path The path to any locked intermediate dump files. +//! \param[in] bundle_identifier_and_seperator The identifier for this client, +//! used to determine when locked files are considered stale. +void UnlockOldIntermediateDumps(base::FilePath pending_path, + std::string bundle_identifier_and_seperator) { + DirectoryReader reader; + std::vector files; + if (!reader.Open(pending_path)) { + return; + } + base::FilePath file; + DirectoryReader::Result result; + while ((result = reader.NextFile(&file)) == + DirectoryReader::Result::kSuccess) { + if (file.FinalExtension() != kLockedExtension) + continue; + + const base::FilePath file_path(pending_path.Append(file)); + timespec file_time; + time_t now = time(nullptr); + if (!FileModificationTime(file_path, &file_time)) { + continue; + } + + if ((file.value().compare(0, + bundle_identifier_and_seperator.size(), + bundle_identifier_and_seperator) == 0 && + file_time.tv_sec <= now - matching_bundle_locked_ttl) || + (file_time.tv_sec <= now - max_locked_ttl)) { + base::FilePath new_path = file_path.RemoveFinalExtension(); + MoveFileOrDirectory(file_path, new_path); + continue; + } + } +} + +} // namespace + +PruneIntermediateDumpsAndCrashReportsThread:: + PruneIntermediateDumpsAndCrashReportsThread( + CrashReportDatabase* database, + std::unique_ptr condition, + base::FilePath pending_path, + std::string bundle_identifier_and_seperator, + bool is_extension) + : thread_(prune_interval, this), + condition_(std::move(condition)), + pending_path_(pending_path), + bundle_identifier_and_seperator_(bundle_identifier_and_seperator), + initial_work_delay_(is_extension ? extension_delay : app_delay), + last_start_time_(0), + database_(database) {} + +PruneIntermediateDumpsAndCrashReportsThread:: + ~PruneIntermediateDumpsAndCrashReportsThread() {} + +void PruneIntermediateDumpsAndCrashReportsThread::Start() { + thread_.Start(initial_work_delay_); +} + +void PruneIntermediateDumpsAndCrashReportsThread::Stop() { + thread_.Stop(); +} + +void PruneIntermediateDumpsAndCrashReportsThread::DoWork( + const WorkerThread* thread) { + // This thread may be stopped and started a number of times throughout the + // lifetime of the process to prevent 0xdead10cc kills (see + // crbug.com/crashpad/400), but it should only run once per prune_interval + // after initial_work_delay_. + time_t now = time(nullptr); + if (now - last_start_time_ < prune_interval) + return; + last_start_time_ = now; + + internal::ScopedBackgroundTask scoper("PruneThread"); + database_->CleanDatabase(60 * 60 * 24 * 3); + + // Here and below, respect Stop() being called after each task. + if (!thread_.is_running()) + return; + PruneCrashReportDatabase(database_, condition_.get()); + + if (!thread_.is_running()) + return; + if (!clean_old_intermediate_dumps_) { + clean_old_intermediate_dumps_ = true; + UnlockOldIntermediateDumps(pending_path_, bundle_identifier_and_seperator_); + } +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.h b/shared/sentry/external/crashpad/client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.h new file mode 100644 index 000000000..cc40980d5 --- /dev/null +++ b/shared/sentry/external/crashpad/client/ios_handler/prune_intermediate_dumps_and_crash_reports_thread.h @@ -0,0 +1,106 @@ +// Copyright 2021 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_HANDLER_PRUNE_CRASH_REPORTS_THREAD_H_ +#define CRASHPAD_HANDLER_PRUNE_CRASH_REPORTS_THREAD_H_ + +#include + +#include "base/files/file_path.h" +#include "util/thread/stoppable.h" +#include "util/thread/worker_thread.h" + +namespace crashpad { + +class CrashReportDatabase; +class PruneCondition; + +//! \brief A thread that periodically prunes crash reports from the database +//! using the specified condition, and any leftover locked intermediate +//! dumps. +//! +//! After the thread is started, the database is pruned using the condition +//! every 24 hours. Upon calling Start(), the thread waits 5 seconds before +//! performing the initial prune operation. +//! +//! Locked intermediate dump files are unlocked only once, not periodically. +//! Locked dumps that match this bundle id can be unlocked if they are over a +//! day old. Otherwise, unlock dumps that are over 60 days old. +class PruneIntermediateDumpsAndCrashReportsThread + : public WorkerThread::Delegate, + public Stoppable { + public: + //! \brief Constructs a new object. + //! + //! \param[in] database The database to prune crash reports from. + //! \param[in] condition The condition used to evaluate crash reports for + //! pruning. + //! \param[in] pending_path The path to any locked intermediate dump files. + //! \param[in] bundle_identifier_and_seperator The identifier for this client, + //! used to determine when locked files are considered stale, with a + //! seperator at the end to allow for substring searches. + PruneIntermediateDumpsAndCrashReportsThread( + CrashReportDatabase* database, + std::unique_ptr condition, + base::FilePath pending_path, + std::string bundle_identifier_and_seperator, + bool is_extension); + + PruneIntermediateDumpsAndCrashReportsThread( + const PruneIntermediateDumpsAndCrashReportsThread&) = delete; + PruneIntermediateDumpsAndCrashReportsThread& operator=( + const PruneIntermediateDumpsAndCrashReportsThread&) = delete; + + ~PruneIntermediateDumpsAndCrashReportsThread(); + + // Stoppable: + + //! \brief Starts a dedicated pruning thread. + //! + //! The thread waits before running the initial prune, so as to not interfere + //! with any startup-related IO performed by the client. + //! + //! This method may only be be called on a newly-constructed object or after + //! a call to Stop(). + void Start() override; + + //! \brief Stops the pruning thread. + //! + //! This method must only be called after Start(). If Start() has been called, + //! this method must be called before destroying an object of this class. + //! + //! This method may be called from any thread other than the pruning thread. + //! It is expected to only be called from the same thread that called Start(). + void Stop() override; + + //! \return `true` if the thread is running, `false` if it is not. + bool is_running() const { return thread_.is_running(); } + + private: + // WorkerThread::Delegate: + void DoWork(const WorkerThread* thread) override; + + WorkerThread thread_; + std::unique_ptr condition_; + base::FilePath pending_path_; + std::string bundle_identifier_and_seperator_; + bool clean_old_intermediate_dumps_; + double initial_work_delay_; + time_t last_start_time_; + CrashReportDatabase* database_; // weak +}; + +} // namespace crashpad + +#endif // CRASHPAD_HANDLER_PRUNE_CRASH_REPORTS_THREAD_H_ diff --git a/shared/sentry/external/crashpad/client/prune_crash_reports.cc b/shared/sentry/external/crashpad/client/prune_crash_reports.cc new file mode 100644 index 000000000..492d46623 --- /dev/null +++ b/shared/sentry/external/crashpad/client/prune_crash_reports.cc @@ -0,0 +1,131 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "client/prune_crash_reports.h" + +#include +#include + +#include +#include + +#include "base/logging.h" +#include "base/notreached.h" +#include "build/build_config.h" + +namespace crashpad { + +size_t PruneCrashReportDatabase(CrashReportDatabase* database, + PruneCondition* condition) { + std::vector all_reports; + CrashReportDatabase::OperationStatus status; + + status = database->GetPendingReports(&all_reports); + if (status != CrashReportDatabase::kNoError) { + LOG(ERROR) << "PruneCrashReportDatabase: Failed to get pending reports"; + return 0; + } + + std::vector completed_reports; + status = database->GetCompletedReports(&completed_reports); + if (status != CrashReportDatabase::kNoError) { + LOG(ERROR) << "PruneCrashReportDatabase: Failed to get completed reports"; + return 0; + } + all_reports.insert(all_reports.end(), completed_reports.begin(), + completed_reports.end()); + + std::sort(all_reports.begin(), all_reports.end(), + [](const CrashReportDatabase::Report& lhs, + const CrashReportDatabase::Report& rhs) { + return lhs.creation_time > rhs.creation_time; + }); + + size_t num_pruned = 0; + for (const auto& report : all_reports) { + if (condition->ShouldPruneReport(report)) { + status = database->DeleteReport(report.uuid); + if (status != CrashReportDatabase::kNoError) { + LOG(ERROR) << "Database Pruning: Failed to remove report " + << report.uuid.ToString(); + } else { + num_pruned++; + } + } + } + + return num_pruned; + + // TODO(rsesek): For databases that do not use a directory structure, it is + // possible for the metadata sidecar to become corrupted and thus leave + // orphaned crash report files on-disk. https://crashpad.chromium.org/bug/66 +} + +// static +std::unique_ptr PruneCondition::GetDefault() { + // DatabaseSizePruneCondition must be the LHS so that it is always evaluated, + // due to the short-circuting behavior of BinaryPruneCondition. + return std::make_unique( + BinaryPruneCondition::OR, + new DatabaseSizePruneCondition(1024 * 128), + new AgePruneCondition(365)); +} + +static const time_t kSecondsInDay = 60 * 60 * 24; + +AgePruneCondition::AgePruneCondition(int max_age_in_days) + : oldest_report_time_( + ((time(nullptr) - (max_age_in_days * kSecondsInDay)) + / kSecondsInDay) * kSecondsInDay) {} + +AgePruneCondition::~AgePruneCondition() {} + +bool AgePruneCondition::ShouldPruneReport( + const CrashReportDatabase::Report& report) { + return report.creation_time < oldest_report_time_; +} + +DatabaseSizePruneCondition::DatabaseSizePruneCondition(size_t max_size_in_kb) + : max_size_in_kb_(max_size_in_kb), measured_size_in_kb_(0) {} + +DatabaseSizePruneCondition::~DatabaseSizePruneCondition() {} + +bool DatabaseSizePruneCondition::ShouldPruneReport( + const CrashReportDatabase::Report& report) { + // Round up fractional KB to the next 1-KB boundary. + measured_size_in_kb_ += + static_cast((report.total_size + 1023) / 1024); + return measured_size_in_kb_ > max_size_in_kb_; +} + +BinaryPruneCondition::BinaryPruneCondition( + Operator op, PruneCondition* lhs, PruneCondition* rhs) + : op_(op), lhs_(lhs), rhs_(rhs) {} + +BinaryPruneCondition::~BinaryPruneCondition() {} + +bool BinaryPruneCondition::ShouldPruneReport( + const CrashReportDatabase::Report& report) { + switch (op_) { + case AND: + return lhs_->ShouldPruneReport(report) && rhs_->ShouldPruneReport(report); + case OR: + return lhs_->ShouldPruneReport(report) || rhs_->ShouldPruneReport(report); + default: + NOTREACHED(); + return false; + } +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/prune_crash_reports.h b/shared/sentry/external/crashpad/client/prune_crash_reports.h new file mode 100644 index 000000000..b80cf7a59 --- /dev/null +++ b/shared/sentry/external/crashpad/client/prune_crash_reports.h @@ -0,0 +1,156 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_CLIENT_PRUNE_CRASH_REPORTS_H_ +#define CRASHPAD_CLIENT_PRUNE_CRASH_REPORTS_H_ + +#include +#include + +#include + +#include "client/crash_report_database.h" + +namespace crashpad { + +class PruneCondition; + +//! \brief Deletes crash reports from \a database that match \a condition. +//! +//! This function can be used to remove old or large reports from the database. +//! The \a condition will be evaluated against each report in the \a database, +//! sorted in descending order by CrashReportDatabase::Report::creation_time. +//! This guarantee allows conditions to be stateful. +//! +//! \param[in] database The database from which crash reports will be deleted. +//! \param[in] condition The condition against which all reports in the database +//! will be evaluated. +//! +//! \return The number of deleted crash reports. +size_t PruneCrashReportDatabase(CrashReportDatabase* database, + PruneCondition* condition); + +std::unique_ptr GetDefaultDatabasePruneCondition(); + +//! \brief An abstract base class for evaluating crash reports for deletion. +//! +//! When passed to PruneCrashReportDatabase(), each crash report in the +//! database will be evaluated according to ShouldPruneReport(). The reports +//! are evaluated serially in descending sort order by +//! CrashReportDatabase::Report::creation_time. +class PruneCondition { + public: + //! \brief Returns a sensible default condition for removing obsolete crash + //! reports. + //! + //! The default is to keep reports for one year or a maximum database size + //! of 128 MB. + //! + //! \return A PruneCondition for use with PruneCrashReportDatabase(). + static std::unique_ptr GetDefault(); + + virtual ~PruneCondition() {} + + //! \brief Evaluates a crash report for deletion. + //! + //! \param[in] report The crash report to evaluate. + //! + //! \return `true` if the crash report should be deleted, `false` if it + //! should be kept. + virtual bool ShouldPruneReport(const CrashReportDatabase::Report& report) = 0; +}; + +//! \brief A PruneCondition that deletes reports older than the specified number +//! days. +class AgePruneCondition final : public PruneCondition { + public: + //! \brief Creates a PruneCondition based on Report::creation_time. + //! + //! \param[in] max_age_in_days Reports created more than this many days ago + //! will be deleted. + explicit AgePruneCondition(int max_age_in_days); + + AgePruneCondition(const AgePruneCondition&) = delete; + AgePruneCondition& operator=(const AgePruneCondition&) = delete; + + ~AgePruneCondition(); + + bool ShouldPruneReport(const CrashReportDatabase::Report& report) override; + + private: + const time_t oldest_report_time_; +}; + +//! \brief A PruneCondition that deletes older reports to keep the total +//! Crashpad database size under the specified limit. +class DatabaseSizePruneCondition final : public PruneCondition { + public: + //! \brief Creates a PruneCondition that will keep newer reports, until the + //! sum of the size of all reports is not smaller than \a max_size_in_kb. + //! After the limit is reached, older reports will be pruned. + //! + //! \param[in] max_size_in_kb The maximum number of kilobytes that all crash + //! reports should consume. + explicit DatabaseSizePruneCondition(size_t max_size_in_kb); + + DatabaseSizePruneCondition(const DatabaseSizePruneCondition&) = delete; + DatabaseSizePruneCondition& operator=(const DatabaseSizePruneCondition&) = + delete; + + ~DatabaseSizePruneCondition(); + + bool ShouldPruneReport(const CrashReportDatabase::Report& report) override; + + private: + const size_t max_size_in_kb_; + size_t measured_size_in_kb_; +}; + +//! \brief A PruneCondition that conjoins two other PruneConditions. +class BinaryPruneCondition final : public PruneCondition { + public: + enum Operator { + AND, + OR, + }; + + //! \brief Evaluates two sub-conditions according to the specified logical + //! operator. + //! + //! This implements left-to-right evaluation. For Operator::AND, this means + //! if the \a lhs is `false`, the \a rhs will not be consulted. Similarly, + //! with Operator::OR, if the \a lhs is `true`, the \a rhs will not be + //! consulted. + //! + //! \param[in] op The logical operator to apply on \a lhs and \a rhs. + //! \param[in] lhs The left-hand side of \a op. This class takes ownership. + //! \param[in] rhs The right-hand side of \a op. This class takes ownership. + BinaryPruneCondition(Operator op, PruneCondition* lhs, PruneCondition* rhs); + + BinaryPruneCondition(const BinaryPruneCondition&) = delete; + BinaryPruneCondition& operator=(const BinaryPruneCondition&) = delete; + + ~BinaryPruneCondition(); + + bool ShouldPruneReport(const CrashReportDatabase::Report& report) override; + + private: + const Operator op_; + std::unique_ptr lhs_; + std::unique_ptr rhs_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_CLIENT_PRUNE_CRASH_REPORTS_H_ diff --git a/shared/sentry/external/crashpad/client/prune_crash_reports_test.cc b/shared/sentry/external/crashpad/client/prune_crash_reports_test.cc new file mode 100644 index 000000000..cce26aa05 --- /dev/null +++ b/shared/sentry/external/crashpad/client/prune_crash_reports_test.cc @@ -0,0 +1,263 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "client/prune_crash_reports.h" + +#include +#include +#include + +#include +#include +#include +#include + +#include "base/numerics/safe_conversions.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "test/scoped_temp_dir.h" +#include "util/file/file_io.h" + +namespace crashpad { +namespace test { +namespace { + +class MockDatabase : public CrashReportDatabase { + public: + // CrashReportDatabase: + MOCK_METHOD(Settings*, GetSettings, (), (override)); + MOCK_METHOD(OperationStatus, + PrepareNewCrashReport, + (std::unique_ptr*), + (override)); + MOCK_METHOD(OperationStatus, + LookUpCrashReport, + (const UUID&, Report*), + (override)); + MOCK_METHOD(OperationStatus, + GetPendingReports, + (std::vector*), + (override)); + MOCK_METHOD(OperationStatus, + GetCompletedReports, + (std::vector*), + (override)); + MOCK_METHOD(OperationStatus, + GetReportForUploading, + (const UUID&, + std::unique_ptr*, + bool report_metrics), + (override)); + MOCK_METHOD(OperationStatus, + RecordUploadAttempt, + (UploadReport*, bool, const std::string&), + (override)); + MOCK_METHOD(OperationStatus, + SkipReportUpload, + (const UUID&, Metrics::CrashSkippedReason), + (override)); + MOCK_METHOD(OperationStatus, DeleteReport, (const UUID&), (override)); + MOCK_METHOD(OperationStatus, RequestUpload, (const UUID&), (override)); + MOCK_METHOD(base::FilePath, DatabasePath, (), (override)); + + // Google Mock doesn't support mocking methods with non-copyable types such as + // unique_ptr. + OperationStatus FinishedWritingCrashReport(std::unique_ptr report, + UUID* uuid) override { + return kNoError; + } +}; + +time_t NDaysAgo(int num_days) { + return time(nullptr) - (num_days * 60 * 60 * 24); +} + +TEST(PruneCrashReports, AgeCondition) { + CrashReportDatabase::Report report_80_days; + report_80_days.creation_time = NDaysAgo(80); + + CrashReportDatabase::Report report_10_days; + report_10_days.creation_time = NDaysAgo(10); + + CrashReportDatabase::Report report_30_days; + report_30_days.creation_time = NDaysAgo(30); + + AgePruneCondition condition(30); + EXPECT_TRUE(condition.ShouldPruneReport(report_80_days)); + EXPECT_FALSE(condition.ShouldPruneReport(report_10_days)); + EXPECT_FALSE(condition.ShouldPruneReport(report_30_days)); +} + +TEST(PruneCrashReports, SizeCondition) { + CrashReportDatabase::Report report_1k; + report_1k.total_size = 1024u; + CrashReportDatabase::Report report_3k; + report_3k.total_size = 1024u * 3u; + CrashReportDatabase::Report report_unset_size; + + { + DatabaseSizePruneCondition condition(/*max_size_in_kb=*/1); + // |report_1k| should not be pruned as the cumulated size is not past 1kB + // yet. + EXPECT_FALSE(condition.ShouldPruneReport(report_1k)); + // |report_3k| should be pruned as the cumulated size is now past 1kB. + EXPECT_TRUE(condition.ShouldPruneReport(report_3k)); + } + + { + DatabaseSizePruneCondition condition(/*max_size_in_kb=*/1); + // |report_3k| should be pruned as the cumulated size is already past 1kB. + EXPECT_TRUE(condition.ShouldPruneReport(report_3k)); + } + + { + DatabaseSizePruneCondition condition(/*max_size_in_kb=*/6); + // |report_3k| should not be pruned as the cumulated size is not past 6kB + // yet. + EXPECT_FALSE(condition.ShouldPruneReport(report_3k)); + // |report_3k| should not be pruned as the cumulated size is not past 6kB + // yet. + EXPECT_FALSE(condition.ShouldPruneReport(report_3k)); + // |report_1k| should be pruned as the cumulated size is now past 6kB. + EXPECT_TRUE(condition.ShouldPruneReport(report_1k)); + } + + { + DatabaseSizePruneCondition condition(/*max_size_in_kb=*/0); + // |report_unset_size| should not be pruned as its size is 0, regardless of + // how many times we try to prune it. + EXPECT_FALSE(condition.ShouldPruneReport(report_unset_size)); + EXPECT_FALSE(condition.ShouldPruneReport(report_unset_size)); + EXPECT_FALSE(condition.ShouldPruneReport(report_unset_size)); + EXPECT_FALSE(condition.ShouldPruneReport(report_unset_size)); + EXPECT_FALSE(condition.ShouldPruneReport(report_unset_size)); + // |report_1k| should be pruned as the cumulated size is now past 0kB. + EXPECT_TRUE(condition.ShouldPruneReport(report_1k)); + } +} + +class StaticCondition final : public PruneCondition { + public: + explicit StaticCondition(bool value) : value_(value), did_execute_(false) {} + + StaticCondition(const StaticCondition&) = delete; + StaticCondition& operator=(const StaticCondition&) = delete; + + ~StaticCondition() {} + + bool ShouldPruneReport(const CrashReportDatabase::Report& report) override { + did_execute_ = true; + return value_; + } + + bool did_execute() const { return did_execute_; } + + private: + const bool value_; + bool did_execute_; +}; + +TEST(PruneCrashReports, BinaryCondition) { + static constexpr struct { + const char* name; + BinaryPruneCondition::Operator op; + bool lhs_value; + bool rhs_value; + bool cond_result; + bool lhs_executed; + bool rhs_executed; + } kTests[] = { + // clang-format off + {"false && false", + BinaryPruneCondition::AND, false, false, + false, true, false}, + {"false && true", + BinaryPruneCondition::AND, false, true, + false, true, false}, + {"true && false", + BinaryPruneCondition::AND, true, false, + false, true, true}, + {"true && true", + BinaryPruneCondition::AND, true, true, + true, true, true}, + {"false || false", + BinaryPruneCondition::OR, false, false, + false, true, true}, + {"false || true", + BinaryPruneCondition::OR, false, true, + true, true, true}, + {"true || false", + BinaryPruneCondition::OR, true, false, + true, true, false}, + {"true || true", + BinaryPruneCondition::OR, true, true, + true, true, false}, + // clang-format on + }; + for (const auto& test : kTests) { + SCOPED_TRACE(test.name); + auto lhs = new StaticCondition(test.lhs_value); + auto rhs = new StaticCondition(test.rhs_value); + BinaryPruneCondition condition(test.op, lhs, rhs); + CrashReportDatabase::Report report; + EXPECT_EQ(condition.ShouldPruneReport(report), test.cond_result); + EXPECT_EQ(lhs->did_execute(), test.lhs_executed); + EXPECT_EQ(rhs->did_execute(), test.rhs_executed); + } +} + +MATCHER_P(TestUUID, data_1, "") { + return arg.data_1 == data_1; +} + +TEST(PruneCrashReports, PruneOrder) { + using ::testing::_; + using ::testing::DoAll; + using ::testing::Return; + using ::testing::SetArgPointee; + + const size_t kNumReports = 10; + std::vector reports; + for (size_t i = 0; i < kNumReports; ++i) { + CrashReportDatabase::Report temp; + temp.uuid.data_1 = static_cast(i); + temp.creation_time = NDaysAgo(static_cast(i) * 10); + reports.push_back(temp); + } + std::mt19937 urng(std::random_device{}()); + std::shuffle(reports.begin(), reports.end(), urng); + std::vector pending_reports(reports.begin(), + reports.begin() + 5); + std::vector completed_reports( + reports.begin() + 5, reports.end()); + + MockDatabase db; + EXPECT_CALL(db, GetPendingReports(_)) + .WillOnce(DoAll(SetArgPointee<0>(pending_reports), + Return(CrashReportDatabase::kNoError))); + EXPECT_CALL(db, GetCompletedReports(_)) + .WillOnce(DoAll(SetArgPointee<0>(completed_reports), + Return(CrashReportDatabase::kNoError))); + for (size_t i = 0; i < reports.size(); ++i) { + EXPECT_CALL(db, DeleteReport(TestUUID(i))) + .WillOnce(Return(CrashReportDatabase::kNoError)); + } + + StaticCondition delete_all(true); + EXPECT_EQ(PruneCrashReportDatabase(&db, &delete_all), kNumReports); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/pthread_create_linux.cc b/shared/sentry/external/crashpad/client/pthread_create_linux.cc new file mode 100644 index 000000000..d4a255c16 --- /dev/null +++ b/shared/sentry/external/crashpad/client/pthread_create_linux.cc @@ -0,0 +1,72 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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 +#include + +#include "base/logging.h" +#include "client/crashpad_client.h" +#include "util/misc/no_cfi_icall.h" + +namespace { + +using StartRoutineType = void* (*)(void*); + +struct StartParams { + StartRoutineType start_routine; + void* arg; +}; + +void* InitializeSignalStackAndStart(StartParams* params) { + crashpad::CrashpadClient::InitializeSignalStackForThread(); + + crashpad::NoCfiIcall start_routine(params->start_routine); + void* arg = params->arg; + delete params; + + return start_routine(arg); +} + +} // namespace + +extern "C" { + +__attribute__((visibility("default"))) int pthread_create( + pthread_t* thread, + const pthread_attr_t* attr, + StartRoutineType start_routine, + void* arg) { + static const crashpad::NoCfiIcall + next_pthread_create([]() { + const auto next_pthread_create = dlsym(RTLD_NEXT, "pthread_create"); + CHECK(next_pthread_create) << "dlsym: " << dlerror(); + return next_pthread_create; + }()); + + StartParams* params = new StartParams; + params->start_routine = start_routine; + params->arg = arg; + + int result = next_pthread_create( + thread, + attr, + reinterpret_cast(InitializeSignalStackAndStart), + params); + if (result != 0) { + delete params; + } + return result; +} + +} // extern "C" diff --git a/shared/sentry/external/crashpad/client/settings.cc b/shared/sentry/external/crashpad/client/settings.cc new file mode 100644 index 000000000..8fe578f92 --- /dev/null +++ b/shared/sentry/external/crashpad/client/settings.cc @@ -0,0 +1,407 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "client/settings.h" + +#include + +#include + +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" +#include "build/build_config.h" +#include "util/file/filesystem.h" +#include "util/numeric/in_range_cast.h" + +namespace crashpad { + +#if BUILDFLAG(IS_FUCHSIA) + +Settings::ScopedLockedFileHandle::ScopedLockedFileHandle() + : handle_(kInvalidFileHandle), lockfile_path_() { + } + +Settings::ScopedLockedFileHandle::ScopedLockedFileHandle( + FileHandle handle, + const base::FilePath& lockfile_path) + : handle_(handle), lockfile_path_(lockfile_path) { +} + +Settings::ScopedLockedFileHandle::ScopedLockedFileHandle( + ScopedLockedFileHandle&& other) + : handle_(other.handle_), lockfile_path_(other.lockfile_path_) { + other.handle_ = kInvalidFileHandle; + other.lockfile_path_ = base::FilePath(); +} + +Settings::ScopedLockedFileHandle& Settings::ScopedLockedFileHandle::operator=( + ScopedLockedFileHandle&& other) { + handle_ = other.handle_; + lockfile_path_ = other.lockfile_path_; + + other.handle_ = kInvalidFileHandle; + other.lockfile_path_ = base::FilePath(); + return *this; +} + +Settings::ScopedLockedFileHandle::~ScopedLockedFileHandle() { + Destroy(); +} + +void Settings::ScopedLockedFileHandle::Destroy() { + if (handle_ != kInvalidFileHandle) { + CheckedCloseFile(handle_); + } + if (!lockfile_path_.empty()) { + const bool success = LoggingRemoveFile(lockfile_path_); + DCHECK(success); + } +} + +#else // BUILDFLAG(IS_FUCHSIA) + +#if BUILDFLAG(IS_IOS) + +Settings::ScopedLockedFileHandle::ScopedLockedFileHandle( + const FileHandle& value) + : ScopedGeneric(value) { + ios_background_task_ = std::make_unique( + "ScopedLockedFileHandle"); +} + +Settings::ScopedLockedFileHandle::ScopedLockedFileHandle( + Settings::ScopedLockedFileHandle&& rvalue) { + ios_background_task_.reset(rvalue.ios_background_task_.release()); + reset(rvalue.release()); +} + +Settings::ScopedLockedFileHandle& Settings::ScopedLockedFileHandle::operator=( + Settings::ScopedLockedFileHandle&& rvalue) { + ios_background_task_.reset(rvalue.ios_background_task_.release()); + reset(rvalue.release()); + return *this; +} + +#endif // BUILDFLAG(IS_IOS) + +namespace internal { + +// static +void ScopedLockedFileHandleTraits::Free(FileHandle handle) { + if (handle != kInvalidFileHandle) { + LoggingUnlockFile(handle); + CheckedCloseFile(handle); + } +} + +} // namespace internal + +#endif // BUILDFLAG(IS_FUCHSIA) + +struct Settings::Data { + static constexpr uint32_t kSettingsMagic = 'CPds'; + static constexpr uint32_t kSettingsVersion = 1; + + enum Options : uint32_t { + kUploadsEnabled = 1 << 0, + }; + + Data() : magic(kSettingsMagic), + version(kSettingsVersion), + options(0), + padding_0(0), + last_upload_attempt_time(0), + client_id() {} + + uint32_t magic; + uint32_t version; + uint32_t options; + uint32_t padding_0; + int64_t last_upload_attempt_time; // time_t + UUID client_id; +}; + +Settings::Settings() = default; + +Settings::~Settings() = default; + +bool Settings::Initialize(const base::FilePath& file_path) { + DCHECK(initialized_.is_uninitialized()); + initialized_.set_invalid(); + file_path_ = file_path; + + Data settings; + if (!OpenForWritingAndReadSettings(&settings).is_valid()) + return false; + + initialized_.set_valid(); + return true; +} + +bool Settings::GetClientID(UUID* client_id) { + DCHECK(initialized_.is_valid()); + + Data settings; + if (!OpenAndReadSettings(&settings)) + return false; + + *client_id = settings.client_id; + return true; +} + +bool Settings::GetUploadsEnabled(bool* enabled) { + DCHECK(initialized_.is_valid()); + + Data settings; + if (!OpenAndReadSettings(&settings)) + return false; + + *enabled = (settings.options & Data::Options::kUploadsEnabled) != 0; + return true; +} + +bool Settings::SetUploadsEnabled(bool enabled) { + DCHECK(initialized_.is_valid()); + + Data settings; + ScopedLockedFileHandle handle = OpenForWritingAndReadSettings(&settings); + if (!handle.is_valid()) + return false; + + if (enabled) + settings.options |= Data::Options::kUploadsEnabled; + else + settings.options &= ~Data::Options::kUploadsEnabled; + + return WriteSettings(handle.get(), settings); +} + +bool Settings::GetLastUploadAttemptTime(time_t* time) { + DCHECK(initialized_.is_valid()); + + Data settings; + if (!OpenAndReadSettings(&settings)) + return false; + + *time = InRangeCast(settings.last_upload_attempt_time, + std::numeric_limits::max()); + return true; +} + +bool Settings::SetLastUploadAttemptTime(time_t time) { + DCHECK(initialized_.is_valid()); + + Data settings; + ScopedLockedFileHandle handle = OpenForWritingAndReadSettings(&settings); + if (!handle.is_valid()) + return false; + + settings.last_upload_attempt_time = InRangeCast(time, 0); + + return WriteSettings(handle.get(), settings); +} + +// static +Settings::ScopedLockedFileHandle Settings::MakeScopedLockedFileHandle( + FileHandle file, + FileLocking locking, + const base::FilePath& file_path) { + ScopedFileHandle scoped(file); +#if BUILDFLAG(IS_FUCHSIA) + base::FilePath lockfile_path(file_path.value() + ".__lock__"); + if (scoped.is_valid()) { + ScopedFileHandle lockfile_scoped( + LoggingOpenFileForWrite(lockfile_path, + FileWriteMode::kCreateOrFail, + FilePermissions::kWorldReadable)); + // This is a lightweight attempt to try to catch racy behavior. + DCHECK(lockfile_scoped.is_valid()); + return ScopedLockedFileHandle(scoped.release(), lockfile_path); + } + return ScopedLockedFileHandle(scoped.release(), base::FilePath()); +#else + // It's important to create the ScopedLockedFileHandle before calling + // LoggingLockFile on iOS, so a ScopedBackgroundTask is created *before* + // the LoggingLockFile call below. + ScopedLockedFileHandle handle(kInvalidFileHandle); + if (scoped.is_valid()) { + if (LoggingLockFile( + scoped.get(), locking, FileLockingBlocking::kBlocking) != + FileLockingResult::kSuccess) { + scoped.reset(); + } + } + handle.reset(scoped.release()); + return handle; +#endif +} + +Settings::ScopedLockedFileHandle Settings::OpenForReading() { + return MakeScopedLockedFileHandle( + LoggingOpenFileForRead(file_path()), FileLocking::kShared, file_path()); +} + +Settings::ScopedLockedFileHandle Settings::OpenForReadingAndWriting( + FileWriteMode mode, bool log_open_error) { + DCHECK(mode != FileWriteMode::kTruncateOrCreate); + + FileHandle handle; + if (log_open_error) { + handle = LoggingOpenFileForReadAndWrite( + file_path(), mode, FilePermissions::kOwnerOnly); + } else { + handle = OpenFileForReadAndWrite( + file_path(), mode, FilePermissions::kOwnerOnly); + } + + return MakeScopedLockedFileHandle( + handle, FileLocking::kExclusive, file_path()); +} + +bool Settings::OpenAndReadSettings(Data* out_data) { + ScopedLockedFileHandle handle = OpenForReading(); + if (!handle.is_valid()) + return false; + + if (ReadSettings(handle.get(), out_data, true)) + return true; + + // The settings file is corrupt, so reinitialize it. + handle.reset(); + + // The settings failed to be read, so re-initialize them. + return RecoverSettings(kInvalidFileHandle, out_data); +} + +Settings::ScopedLockedFileHandle Settings::OpenForWritingAndReadSettings( + Data* out_data) { + ScopedLockedFileHandle handle; + bool created = false; + if (!initialized_.is_valid()) { + // If this object is initializing, it hasn’t seen a settings file already, + // so go easy on errors. Creating a new settings file for the first time + // shouldn’t spew log messages. + // + // First, try to use an existing settings file. + handle = OpenForReadingAndWriting(FileWriteMode::kReuseOrFail, false); + + if (!handle.is_valid()) { + // Create a new settings file if it didn’t already exist. + handle = OpenForReadingAndWriting(FileWriteMode::kCreateOrFail, false); + + if (handle.is_valid()) { + created = true; + } + + // There may have been a race to create the file, and something else may + // have won. There will be one more attempt to try to open or create the + // file below. + } + } + + if (!handle.is_valid()) { + // Either the object is initialized, meaning it’s already seen a valid + // settings file, or the object is initializing and none of the above + // attempts to create the settings file succeeded. Either way, this is the + // last chance for success, so if this fails, log a message. + handle = OpenForReadingAndWriting(FileWriteMode::kReuseOrCreate, true); + } + + if (!handle.is_valid()) + return ScopedLockedFileHandle(); + + // Attempt reading the settings even if the file is known to have just been + // created. The file-create and file-lock operations don’t occur atomically, + // and something else may have written the settings before this invocation + // took the lock. If the settings file was definitely just created, though, + // don’t log any read errors. The expected non-race behavior in this case is a + // zero-length read, with ReadSettings() failing. + if (!ReadSettings(handle.get(), out_data, !created)) { + if (!RecoverSettings(handle.get(), out_data)) + return ScopedLockedFileHandle(); + } + + return handle; +} + +bool Settings::ReadSettings(FileHandle handle, + Data* out_data, + bool log_read_error) { + if (LoggingSeekFile(handle, 0, SEEK_SET) != 0) + return false; + + bool read_result = + log_read_error + ? LoggingReadFileExactly(handle, out_data, sizeof(*out_data)) + : ReadFileExactly(handle, out_data, sizeof(*out_data)); + + if (!read_result) + return false; + + if (out_data->magic != Data::kSettingsMagic) { + LOG(ERROR) << "Settings magic is not " << Data::kSettingsMagic; + return false; + } + + if (out_data->version != Data::kSettingsVersion) { + LOG(ERROR) << "Settings version is not " << Data::kSettingsVersion; + return false; + } + + return true; +} + +bool Settings::WriteSettings(FileHandle handle, const Data& data) { + if (LoggingSeekFile(handle, 0, SEEK_SET) != 0) + return false; + + if (!LoggingTruncateFile(handle)) + return false; + + return LoggingWriteFile(handle, &data, sizeof(Data)); +} + +bool Settings::RecoverSettings(FileHandle handle, Data* out_data) { + ScopedLockedFileHandle scoped_handle; + if (handle == kInvalidFileHandle) { + scoped_handle = + OpenForReadingAndWriting(FileWriteMode::kReuseOrCreate, true); + handle = scoped_handle.get(); + + // Test if the file has already been recovered now that the exclusive lock + // is held. + if (ReadSettings(handle, out_data, true)) + return true; + } + + if (handle == kInvalidFileHandle) { + LOG(ERROR) << "Invalid file handle"; + return false; + } + + if (!InitializeSettings(handle)) + return false; + + return ReadSettings(handle, out_data, true); +} + +bool Settings::InitializeSettings(FileHandle handle) { + Data settings; + if (!settings.client_id.InitializeWithNew()) + return false; + + return WriteSettings(handle, settings); +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/settings.h b/shared/sentry/external/crashpad/client/settings.h new file mode 100644 index 000000000..aedf30cd8 --- /dev/null +++ b/shared/sentry/external/crashpad/client/settings.h @@ -0,0 +1,252 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_CLIENT_SETTINGS_H_ +#define CRASHPAD_CLIENT_SETTINGS_H_ + +#include + +#include "base/files/file_path.h" +#include "base/scoped_generic.h" +#include "build/build_config.h" +#include "util/file/file_io.h" +#include "util/misc/initialization_state.h" +#include "util/misc/uuid.h" + +#if BUILDFLAG(IS_IOS) +#include "util/ios/scoped_background_task.h" +#endif // BUILDFLAG(IS_IOS) + +namespace crashpad { + +namespace internal { + +struct ScopedLockedFileHandleTraits { + static FileHandle InvalidValue() { return kInvalidFileHandle; } + static void Free(FileHandle handle); +}; + +} // namespace internal + +//! \brief An interface for accessing and modifying the settings of a +//! CrashReportDatabase. +//! +//! This class must not be instantiated directly, but rather an instance of it +//! should be retrieved via CrashReportDatabase::GetSettings(). +class Settings { + public: + Settings(); + + Settings(const Settings&) = delete; + Settings& operator=(const Settings&) = delete; + + ~Settings(); + + //! \brief Initializes the settings data store. + //! + //! This method must be called only once, and must be successfully called + //! before any other method in this class may be called. + //! + //! \param[in] path The location to store the settings data. + //! \return `true` if the data store was initialized successfully, otherwise + //! `false` with an error logged. + bool Initialize(const base::FilePath& path); + + //! \brief Retrieves the immutable identifier for this client, which is used + //! on a server to locate all crash reports from a specific Crashpad + //! database. + //! + //! This is automatically initialized when the database is created. + //! + //! \param[out] client_id The unique client identifier. + //! + //! \return On success, returns `true`, otherwise returns `false` with an + //! error logged. + bool GetClientID(UUID* client_id); + + //! \brief Retrieves the user’s preference for submitting crash reports to a + //! collection server. + //! + //! The default value is `false`. + //! + //! \note + //! This setting is ignored if --use-cros-crash-reporter is present + //! (which it will be if invoked by Chrome on ChromeOS). + //! + //! \param[out] enabled Whether crash reports should be uploaded. + //! + //! \return On success, returns `true`, otherwise returns `false` with an + //! error logged. + bool GetUploadsEnabled(bool* enabled); + + //! \brief Sets the user’s preference for submitting crash reports to a + //! collection server. + //! + //! \param[in] enabled Whether crash reports should be uploaded. + //! + //! \return On success, returns `true`, otherwise returns `false` with an + //! error logged. + bool SetUploadsEnabled(bool enabled); + + //! \brief Retrieves the last time at which a report was attempted to be + //! uploaded. + //! + //! The default value is `0` if it has never been set before. + //! + //! \param[out] time The last time at which a report was uploaded. + //! + //! \return On success, returns `true`, otherwise returns `false` with an + //! error logged. + bool GetLastUploadAttemptTime(time_t* time); + + //! \brief Sets the last time at which a report was attempted to be uploaded. + //! + //! This is only meant to be used internally by the CrashReportDatabase. + //! + //! \param[in] time The last time at which a report was uploaded. + //! + //! \return On success, returns `true`, otherwise returns `false` with an + //! error logged. + bool SetLastUploadAttemptTime(time_t time); + + private: + struct Data; + + // This must be constructed with MakeScopedLockedFileHandle(). It both unlocks + // and closes the file on destruction. Note that on Fuchsia, this handle DOES + // NOT offer correct operation, only an attempt to DCHECK if racy behavior is + // detected. +#if BUILDFLAG(IS_FUCHSIA) + struct ScopedLockedFileHandle { + public: + ScopedLockedFileHandle(); + ScopedLockedFileHandle(FileHandle handle, + const base::FilePath& lockfile_path); + ScopedLockedFileHandle(ScopedLockedFileHandle&& other); + ScopedLockedFileHandle& operator=(ScopedLockedFileHandle&& other); + + ScopedLockedFileHandle(const ScopedLockedFileHandle&) = delete; + ScopedLockedFileHandle& operator=(const ScopedLockedFileHandle&) = delete; + + ~ScopedLockedFileHandle(); + + // These mirror the non-Fuchsia ScopedLockedFileHandle via ScopedGeneric so + // that calling code can pretend this implementation is the same. + bool is_valid() const { return handle_ != kInvalidFileHandle; } + FileHandle get() { return handle_; } + void reset() { + Destroy(); + handle_ = kInvalidFileHandle; + lockfile_path_ = base::FilePath(); + } + + private: + void Destroy(); + + FileHandle handle_; + base::FilePath lockfile_path_; + }; +#elif BUILDFLAG(IS_IOS) + // iOS needs to use ScopedBackgroundTask anytime a file lock is used. + class ScopedLockedFileHandle + : public base::ScopedGeneric { + public: + using base::ScopedGeneric< + FileHandle, + internal::ScopedLockedFileHandleTraits>::ScopedGeneric; + + ScopedLockedFileHandle(const FileHandle& value); + ScopedLockedFileHandle(ScopedLockedFileHandle&& rvalue); + ScopedLockedFileHandle& operator=(ScopedLockedFileHandle&& rvalue); + + private: + std::unique_ptr ios_background_task_; + }; +#else + using ScopedLockedFileHandle = + base::ScopedGeneric; +#endif // BUILDFLAG(IS_FUCHSIA) + static ScopedLockedFileHandle MakeScopedLockedFileHandle( + FileHandle file, + FileLocking locking, + const base::FilePath& file_path); + + // Opens the settings file for reading. On error, logs a message and returns + // the invalid handle. + ScopedLockedFileHandle OpenForReading(); + + // Opens the settings file for reading and writing. On error, logs a message + // and returns the invalid handle. |mode| determines how the file will be + // opened. |mode| must not be FileWriteMode::kTruncateOrCreate. + // + // If |log_open_error| is false, nothing will be logged for an error + // encountered when attempting to open the file, but this method will still + // return false. This is intended to be used to suppress error messages when + // attempting to create a new settings file when multiple attempts are made. + ScopedLockedFileHandle OpenForReadingAndWriting(FileWriteMode mode, + bool log_open_error); + + // Opens the settings file and reads the data. If that fails, an error will + // be logged and the settings will be recovered and re-initialized. If that + // also fails, returns false with additional log data from recovery. + bool OpenAndReadSettings(Data* out_data); + + // Opens the settings file for writing and reads the data. If reading fails, + // recovery is attempted. Returns the opened file handle on success, or the + // invalid file handle on failure, with an error logged. + ScopedLockedFileHandle OpenForWritingAndReadSettings(Data* out_data); + + // Reads the settings from |handle|. Logs an error and returns false on + // failure. This does not perform recovery. + // + // |handle| must be the result of OpenForReading() or + // OpenForReadingAndWriting(). + // + // If |log_read_error| is false, nothing will be logged for a read error, but + // this method will still return false. This is intended to be used to + // suppress error messages when attempting to read a newly created settings + // file. + bool ReadSettings(FileHandle handle, Data* out_data, bool log_read_error); + + // Writes the settings to |handle|. Logs an error and returns false on + // failure. This does not perform recovery. + // + // |handle| must be the result of OpenForReadingAndWriting(). + bool WriteSettings(FileHandle handle, const Data& data); + + // Recovers the settings file by re-initializing the data. If |handle| is the + // invalid handle, this will open the file; if it is not, then it must be the + // result of OpenForReadingAndWriting(). If the invalid handle is passed, the + // caller must not be holding the handle. The new settings data are stored in + // |out_data|. Returns true on success and false on failure, with an error + // logged. + bool RecoverSettings(FileHandle handle, Data* out_data); + + // Initializes a settings file and writes the data to |handle|. Returns true + // on success and false on failure, with an error logged. + // + // |handle| must be the result of OpenForReadingAndWriting(). + bool InitializeSettings(FileHandle handle); + + const base::FilePath& file_path() const { return file_path_; } + + base::FilePath file_path_; + + InitializationState initialized_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_CLIENT_SETTINGS_H_ diff --git a/shared/sentry/external/crashpad/client/settings_test.cc b/shared/sentry/external/crashpad/client/settings_test.cc new file mode 100644 index 000000000..74a201471 --- /dev/null +++ b/shared/sentry/external/crashpad/client/settings_test.cc @@ -0,0 +1,183 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "client/settings.h" + +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "test/errors.h" +#include "test/scoped_temp_dir.h" +#include "util/file/file_io.h" + +namespace crashpad { +namespace test { +namespace { + +class SettingsTest : public testing::Test { + public: + SettingsTest() = default; + + SettingsTest(const SettingsTest&) = delete; + SettingsTest& operator=(const SettingsTest&) = delete; + + base::FilePath settings_path() { + return temp_dir_.path().Append(FILE_PATH_LITERAL("settings")); + } + + Settings* settings() { return &settings_; } + + void InitializeBadFile() { + ScopedFileHandle handle( + LoggingOpenFileForWrite(settings_path(), + FileWriteMode::kTruncateOrCreate, + FilePermissions::kWorldReadable)); + ASSERT_TRUE(handle.is_valid()); + + static constexpr char kBuf[] = "test bad file"; + ASSERT_TRUE(LoggingWriteFile(handle.get(), kBuf, sizeof(kBuf))); + handle.reset(); + } + + protected: + // testing::Test: + void SetUp() override { + ASSERT_TRUE(settings()->Initialize(settings_path())); + } + + private: + ScopedTempDir temp_dir_; + Settings settings_; +}; + +TEST_F(SettingsTest, ClientID) { + UUID client_id; + EXPECT_TRUE(settings()->GetClientID(&client_id)); + EXPECT_NE(client_id, UUID()); + + Settings local_settings; + EXPECT_TRUE(local_settings.Initialize(settings_path())); + UUID actual; + EXPECT_TRUE(local_settings.GetClientID(&actual)); + EXPECT_EQ(actual, client_id); +} + +TEST_F(SettingsTest, UploadsEnabled) { + bool enabled = true; + // Default value is false. + EXPECT_TRUE(settings()->GetUploadsEnabled(&enabled)); + EXPECT_FALSE(enabled); + + EXPECT_TRUE(settings()->SetUploadsEnabled(true)); + EXPECT_TRUE(settings()->GetUploadsEnabled(&enabled)); + EXPECT_TRUE(enabled); + + Settings local_settings; + EXPECT_TRUE(local_settings.Initialize(settings_path())); + enabled = false; + EXPECT_TRUE(local_settings.GetUploadsEnabled(&enabled)); + EXPECT_TRUE(enabled); + + EXPECT_TRUE(settings()->SetUploadsEnabled(false)); + EXPECT_TRUE(settings()->GetUploadsEnabled(&enabled)); + EXPECT_FALSE(enabled); + + enabled = true; + EXPECT_TRUE(local_settings.GetUploadsEnabled(&enabled)); + EXPECT_FALSE(enabled); +} + +TEST_F(SettingsTest, LastUploadAttemptTime) { + time_t actual = -1; + EXPECT_TRUE(settings()->GetLastUploadAttemptTime(&actual)); + // Default value is 0. + EXPECT_EQ(actual, 0); + + const time_t expected = time(nullptr); + EXPECT_TRUE(settings()->SetLastUploadAttemptTime(expected)); + EXPECT_TRUE(settings()->GetLastUploadAttemptTime(&actual)); + EXPECT_EQ(actual, expected); + + Settings local_settings; + EXPECT_TRUE(local_settings.Initialize(settings_path())); + actual = -1; + EXPECT_TRUE(local_settings.GetLastUploadAttemptTime(&actual)); + EXPECT_EQ(actual, expected); +} + +// The following tests write a corrupt settings file and test the recovery +// operation. + +TEST_F(SettingsTest, BadFileOnInitialize) { + InitializeBadFile(); + + Settings settings; + EXPECT_TRUE(settings.Initialize(settings_path())); +} + +TEST_F(SettingsTest, BadFileOnGet) { + InitializeBadFile(); + + UUID client_id; + EXPECT_TRUE(settings()->GetClientID(&client_id)); + EXPECT_NE(client_id, UUID()); + + Settings local_settings; + EXPECT_TRUE(local_settings.Initialize(settings_path())); + UUID actual; + EXPECT_TRUE(local_settings.GetClientID(&actual)); + EXPECT_EQ(actual, client_id); +} + +TEST_F(SettingsTest, BadFileOnSet) { + InitializeBadFile(); + + EXPECT_TRUE(settings()->SetUploadsEnabled(true)); + bool enabled = false; + EXPECT_TRUE(settings()->GetUploadsEnabled(&enabled)); + EXPECT_TRUE(enabled); +} + +TEST_F(SettingsTest, UnlinkFile) { + UUID client_id; + EXPECT_TRUE(settings()->GetClientID(&client_id)); + EXPECT_TRUE(settings()->SetUploadsEnabled(true)); + EXPECT_TRUE(settings()->SetLastUploadAttemptTime(time(nullptr))); + +#if BUILDFLAG(IS_WIN) + EXPECT_EQ(_wunlink(settings_path().value().c_str()), 0) + << ErrnoMessage("_wunlink"); +#else + EXPECT_EQ(unlink(settings_path().value().c_str()), 0) + << ErrnoMessage("unlink"); +#endif + + Settings local_settings; + EXPECT_TRUE(local_settings.Initialize(settings_path())); + UUID new_client_id; + EXPECT_TRUE(local_settings.GetClientID(&new_client_id)); + EXPECT_NE(new_client_id, client_id); + + // Check that all values are reset. + bool enabled = true; + EXPECT_TRUE(local_settings.GetUploadsEnabled(&enabled)); + EXPECT_FALSE(enabled); + + time_t time = -1; + EXPECT_TRUE(local_settings.GetLastUploadAttemptTime(&time)); + EXPECT_EQ(time, 0); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/simple_address_range_bag.h b/shared/sentry/external/crashpad/client/simple_address_range_bag.h new file mode 100644 index 000000000..1bdfa8249 --- /dev/null +++ b/shared/sentry/external/crashpad/client/simple_address_range_bag.h @@ -0,0 +1,200 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_CLIENT_SIMPLE_ADDRESS_RANGE_BAG_H_ +#define CRASHPAD_CLIENT_SIMPLE_ADDRESS_RANGE_BAG_H_ + +#include +#include + +#include + +#include "base/check_op.h" +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "util/misc/from_pointer_cast.h" +#include "util/numeric/checked_range.h" + +namespace crashpad { + +//! \brief A bag implementation using a fixed amount of storage, so that it does +//! not perform any dynamic allocations for its operations. +//! +//! The actual bag storage (TSimpleAddressRangeBag::Entry) is POD, so that it +//! can be transmitted over various IPC mechanisms. +template +class TSimpleAddressRangeBag { + public: + //! Constant and publicly accessible version of the template parameter. + static const size_t num_entries = NumEntries; + + //! \brief A single entry in the bag. + struct Entry { + //! \brief The base address of the range. + uint64_t base; + + //! \brief The size of the range in bytes. + uint64_t size; + + //! \brief Returns the validity of the entry. + //! + //! If #base and #size are both zero, the entry is considered inactive, and + //! this method returns `false`. Otherwise, returns `true`. + bool is_active() const { + return base != 0 || size != 0; + } + }; + + //! \brief An iterator to traverse all of the active entries in a + //! TSimpleAddressRangeBag. + class Iterator { + public: + explicit Iterator(const TSimpleAddressRangeBag& bag) + : bag_(bag), + current_(0) { + } + + Iterator(const Iterator&) = delete; + Iterator& operator=(const Iterator&) = delete; + + //! \brief Returns the next entry in the bag, or `nullptr` if at the end of + //! the collection. + const Entry* Next() { + while (current_ < bag_.num_entries) { + const Entry* entry = &bag_.entries_[current_++]; + if (entry->is_active()) { + return entry; + } + } + return nullptr; + } + + private: + const TSimpleAddressRangeBag& bag_; + size_t current_; + }; + + TSimpleAddressRangeBag() + : entries_() { + } + + TSimpleAddressRangeBag(const TSimpleAddressRangeBag& other) { + *this = other; + } + + TSimpleAddressRangeBag& operator=(const TSimpleAddressRangeBag& other) { + memcpy(entries_, other.entries_, sizeof(entries_)); + return *this; + } + + //! \brief Returns the number of active entries. The upper limit for this is + //! \a 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; + } + + //! \brief Inserts the given range into the bag. Duplicates and overlapping + //! ranges are supported and allowed, but not coalesced. + //! + //! \param[in] range The range to be inserted. The range must have either a + //! non-zero base address or size. + //! + //! \return `true` if there was space to insert the range into the bag, + //! otherwise `false` with an error logged. + bool Insert(CheckedRange range) { + DCHECK(range.base() != 0 || range.size() != 0); + + for (size_t i = 0; i < num_entries; ++i) { + if (!entries_[i].is_active()) { + entries_[i].base = range.base(); + entries_[i].size = range.size(); + return true; + } + } + + LOG(ERROR) << "no space available to insert range"; + return false; + } + + //! \brief Inserts the given range into the bag. Duplicates and overlapping + //! ranges are supported and allowed, but not coalesced. + //! + //! \param[in] base The base of the range to be inserted. May not be null. + //! \param[in] size The size of the range to be inserted. May not be zero. + //! + //! \return `true` if there was space to insert the range into the bag, + //! otherwise `false` with an error logged. + bool Insert(void* base, size_t size) { + DCHECK(base != nullptr); + DCHECK_NE(0u, size); + return Insert(CheckedRange(FromPointerCast(base), + base::checked_cast(size))); + } + + //! \brief Removes the given range from the bag. + //! + //! \param[in] range The range to be removed. The range must have either a + //! non-zero base address or size. + //! + //! \return `true` if the range was found and removed, otherwise `false` with + //! an error logged. + bool Remove(CheckedRange range) { + DCHECK(range.base() != 0 || range.size() != 0); + + for (size_t i = 0; i < num_entries; ++i) { + if (entries_[i].base == range.base() && + entries_[i].size == range.size()) { + entries_[i].base = entries_[i].size = 0; + return true; + } + } + + LOG(ERROR) << "did not find range to remove"; + return false; + } + + //! \brief Removes the given range from the bag. + //! + //! \param[in] base The base of the range to be removed. May not be null. + //! \param[in] size The size of the range to be removed. May not be zero. + //! + //! \return `true` if the range was found and removed, otherwise `false` with + //! an error logged. + bool Remove(void* base, size_t size) { + DCHECK(base != nullptr); + DCHECK_NE(0u, size); + return Remove(CheckedRange(FromPointerCast(base), + base::checked_cast(size))); + } + + + private: + Entry entries_[NumEntries]; +}; + +//! \brief A TSimpleAddressRangeBag with default template parameters. +using SimpleAddressRangeBag = TSimpleAddressRangeBag<64>; + +static_assert(std::is_standard_layout::value, + "SimpleAddressRangeBag must be standard layout"); + +} // namespace crashpad + +#endif // CRASHPAD_CLIENT_SIMPLE_ADDRESS_RANGE_BAG_H_ diff --git a/shared/sentry/external/crashpad/client/simple_address_range_bag_test.cc b/shared/sentry/external/crashpad/client/simple_address_range_bag_test.cc new file mode 100644 index 000000000..4c3f34d1e --- /dev/null +++ b/shared/sentry/external/crashpad/client/simple_address_range_bag_test.cc @@ -0,0 +1,108 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// 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 "client/simple_address_range_bag.h" + +#include "gtest/gtest.h" +#include "test/gtest_death.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(SimpleAddressRangeBag, Entry) { + using TestBag = TSimpleAddressRangeBag<15>; + TestBag bag; + + const TestBag::Entry* entry = TestBag::Iterator(bag).Next(); + EXPECT_FALSE(entry); + + bag.Insert(reinterpret_cast(0x1000), 200); + entry = TestBag::Iterator(bag).Next(); + ASSERT_TRUE(entry); + EXPECT_EQ(0x1000u, entry->base); + EXPECT_EQ(200u, entry->size); + + bag.Remove(reinterpret_cast(0x1000), 200); + EXPECT_FALSE(entry->is_active()); + EXPECT_EQ(0u, entry->base); + EXPECT_EQ(0u, entry->size); +} + +TEST(SimpleAddressRangeBag, SimpleAddressRangeBag) { + SimpleAddressRangeBag bag; + + EXPECT_TRUE(bag.Insert(reinterpret_cast(0x1000), 10)); + EXPECT_TRUE(bag.Insert(reinterpret_cast(0x2000), 20)); + EXPECT_TRUE(bag.Insert(CheckedRange(0x3000, 30))); + + EXPECT_EQ(3u, bag.GetCount()); + + // Duplicates added too. + EXPECT_TRUE(bag.Insert(CheckedRange(0x3000, 30))); + EXPECT_TRUE(bag.Insert(CheckedRange(0x3000, 30))); + EXPECT_EQ(5u, bag.GetCount()); + + // Can be removed 3 times, but not the 4th time. + EXPECT_TRUE(bag.Remove(CheckedRange(0x3000, 30))); + EXPECT_TRUE(bag.Remove(CheckedRange(0x3000, 30))); + EXPECT_TRUE(bag.Remove(CheckedRange(0x3000, 30))); + EXPECT_EQ(2u, bag.GetCount()); + EXPECT_FALSE(bag.Remove(CheckedRange(0x3000, 30))); + EXPECT_EQ(2u, bag.GetCount()); + + EXPECT_TRUE(bag.Remove(reinterpret_cast(0x1000), 10)); + EXPECT_TRUE(bag.Remove(reinterpret_cast(0x2000), 20)); + EXPECT_EQ(0u, bag.GetCount()); +} + +TEST(SimpleAddressRangeBag, CopyAndAssign) { + TSimpleAddressRangeBag<10> bag; + EXPECT_TRUE(bag.Insert(CheckedRange(1, 2))); + EXPECT_TRUE(bag.Insert(CheckedRange(3, 4))); + EXPECT_TRUE(bag.Insert(CheckedRange(5, 6))); + EXPECT_TRUE(bag.Remove(CheckedRange(3, 4))); + EXPECT_EQ(bag.GetCount(), 2u); + + // Test copy. + TSimpleAddressRangeBag<10> bag_copy(bag); + EXPECT_EQ(bag_copy.GetCount(), 2u); + EXPECT_TRUE(bag_copy.Remove(CheckedRange(1, 2))); + EXPECT_TRUE(bag_copy.Remove(CheckedRange(5, 6))); + EXPECT_EQ(bag_copy.GetCount(), 0u); + EXPECT_EQ(bag.GetCount(), 2u); + + // Test assign. + TSimpleAddressRangeBag<10> bag_assign; + bag_assign = bag; + EXPECT_EQ(bag_assign.GetCount(), 2u); + EXPECT_TRUE(bag_assign.Remove(CheckedRange(1, 2))); + EXPECT_TRUE(bag_assign.Remove(CheckedRange(5, 6))); + EXPECT_EQ(bag_assign.GetCount(), 0u); + EXPECT_EQ(bag.GetCount(), 2u); +} + +// Running out of space shouldn't crash. +TEST(SimpleAddressRangeBag, OutOfSpace) { + TSimpleAddressRangeBag<2> bag; + EXPECT_TRUE(bag.Insert(CheckedRange(1, 2))); + EXPECT_TRUE(bag.Insert(CheckedRange(3, 4))); + EXPECT_FALSE(bag.Insert(CheckedRange(5, 6))); + EXPECT_EQ(bag.GetCount(), 2u); + EXPECT_FALSE(bag.Remove(CheckedRange(5, 6))); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/simple_string_dictionary.h b/shared/sentry/external/crashpad/client/simple_string_dictionary.h new file mode 100644 index 000000000..43483dd87 --- /dev/null +++ b/shared/sentry/external/crashpad/client/simple_string_dictionary.h @@ -0,0 +1,288 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_CLIENT_SIMPLE_STRING_DICTIONARY_H_ +#define CRASHPAD_CLIENT_SIMPLE_STRING_DICTIONARY_H_ + +#include +#include + +#include +#include + +#include "base/check_op.h" +#include "base/strings/string_piece.h" +#include "util/misc/implicit_cast.h" + +namespace crashpad { + +//! \brief A map/dictionary collection implementation using a fixed amount of +//! storage, so that it does not perform any dynamic allocations for its +//! operations. +//! +//! The actual map storage (TSimpleStringDictionary::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 \a KeySize and \a ValueSize are measured in bytes, not +//! glyphs, and include space for a trailing `NUL` byte. This gives space for +//! `KeySize - 1` and `ValueSize - 1` characters in an entry. \a NumEntries is +//! the total number of entries that will fit in the map. +template +class TSimpleStringDictionary { + public: + //! \brief 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; + //! \} + + //! \brief A single entry in the map. + struct Entry { + //! \brief The entry’s key. + //! + //! This string is always `NUL`-terminated. If this is a 0-length + //! `NUL`-terminated string, the entry is inactive. + char key[KeySize]; + + //! \brief The entry’s value. + //! + //! This string is always `NUL`-terminated. + char value[ValueSize]; + + //! \brief Returns the validity of the entry. + //! + //! If #key is an empty string, the entry is considered inactive, and this + //! method returns `false`. Otherwise, returns `true`. + bool is_active() const { + return key[0] != '\0'; + } + }; + + //! \brief An iterator to traverse all of the active entries in a + //! TSimpleStringDictionary. + class Iterator { + public: + explicit Iterator(const TSimpleStringDictionary& map) + : map_(map), + current_(0) { + } + + Iterator(const Iterator&) = delete; + Iterator& operator=(const Iterator&) = delete; + + //! \brief Returns the next entry in the map, or `nullptr` 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 nullptr; + } + + private: + const TSimpleStringDictionary& map_; + size_t current_; + }; + + TSimpleStringDictionary() + : entries_() { + } + + TSimpleStringDictionary(const TSimpleStringDictionary& other) { + *this = other; + } + + TSimpleStringDictionary& operator=(const TSimpleStringDictionary& other) { + memcpy(entries_, other.entries_, sizeof(entries_)); + return *this; + } + + //! \brief Returns the number of active key/value pairs. The upper limit for + //! this is \a 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; + } + + //! \brief Given \a key, returns its corresponding value. + //! + //! \param[in] key The key to look up. This must not be `nullptr`, nor an + //! empty string. It must not contain embedded `NUL`s. + //! + //! \return The corresponding value for \a key, or if \a key is not found, + //! `nullptr`. + const char* GetValueForKey(base::StringPiece key) const { + DCHECK(key.data()); + DCHECK(key.size()); + DCHECK_EQ(key.find('\0', 0), base::StringPiece::npos); + if (!key.data() || !key.size()) { + return nullptr; + } + + const Entry* entry = GetConstEntryForKey(key); + if (!entry) { + return nullptr; + } + + return entry->value; + } + + //! \brief Stores \a value into \a key, replacing the existing value if \a key + //! is already present. + //! + //! If \a key is not yet in the map and the map is already full (containing + //! \a NumEntries active entries), this operation silently fails. + //! + //! \param[in] key The key to store. This must not be `nullptr`, nor an empty + //! string. It must not contain embedded `NUL`s. + //! \param[in] value The value to store. If `nullptr`, \a key is removed from + //! the map. Must not contain embedded `NUL`s. + void SetKeyValue(base::StringPiece key, base::StringPiece value) { + if (!value.data()) { + RemoveKey(key); + return; + } + + DCHECK(key.data()); + DCHECK(key.size()); + DCHECK_EQ(key.find('\0', 0), base::StringPiece::npos); + if (!key.data() || !key.size()) { + return; + } + + // |key| must not be an empty string. + DCHECK_NE(key[0], '\0'); + if (key[0] == '\0') { + return; + } + + // |value| must not contain embedded NULs. + DCHECK_EQ(value.find('\0', 0), base::StringPiece::npos); + + Entry* entry = GetEntryForKey(key); + + // If it does not yet exist, attempt to insert it. + if (!entry) { + for (size_t i = 0; i < num_entries; ++i) { + if (!entries_[i].is_active()) { + entry = &entries_[i]; + SetFromStringPiece(key, entry->key, key_size); + break; + } + } + } + + // If the map is out of space, |entry| will be nullptr. + if (!entry) { + return; + } + +#ifndef NDEBUG + // Sanity check that the key only appears once. + int count = 0; + for (size_t i = 0; i < num_entries; ++i) { + if (EntryKeyEquals(key, entries_[i])) { + ++count; + } + } + DCHECK_EQ(count, 1); +#endif + + SetFromStringPiece(value, entry->value, value_size); + } + + //! \brief Removes \a key from the map. + //! + //! If \a key is not found, this is a no-op. + //! + //! \param[in] key The key of the entry to remove. This must not be `nullptr`, + //! nor an empty string. It must not contain embedded `NUL`s. + void RemoveKey(base::StringPiece key) { + DCHECK(key.data()); + DCHECK(key.size()); + DCHECK_EQ(key.find('\0', 0), base::StringPiece::npos); + if (!key.data() || !key.size()) { + return; + } + + Entry* entry = GetEntryForKey(key); + if (entry) { + entry->key[0] = '\0'; + entry->value[0] = '\0'; + } + + DCHECK_EQ(GetEntryForKey(key), implicit_cast(nullptr)); + } + + private: + static void SetFromStringPiece(base::StringPiece src, + char* dst, + size_t dst_size) { + size_t copy_len = std::min(dst_size - 1, src.size()); + src.copy(dst, copy_len); + dst[copy_len] = '\0'; + } + + static bool EntryKeyEquals(base::StringPiece key, const Entry& entry) { + if (key.size() >= KeySize) + return false; + + // Test for a NUL terminator and early out if it's absent. + if (entry.key[key.size()] != '\0') + return false; + + // As there's a NUL terminator at the right position in the entries + // string, strncmp can do the rest. + return strncmp(key.data(), entry.key, key.size()) == 0; + } + + const Entry* GetConstEntryForKey(base::StringPiece key) const { + for (size_t i = 0; i < num_entries; ++i) { + if (EntryKeyEquals(key, entries_[i])) { + return &entries_[i]; + } + } + return nullptr; + } + + Entry* GetEntryForKey(base::StringPiece key) { + return const_cast(GetConstEntryForKey(key)); + } + + Entry entries_[NumEntries]; +}; + +//! \brief A TSimpleStringDictionary with default template parameters. +//! +//! For historical reasons this specialized version is available with the same +//! size factors as a previous implementation. +using SimpleStringDictionary = TSimpleStringDictionary<256, 256, 64>; + +static_assert(std::is_standard_layout::value, + "SimpleStringDictionary must be standard layout"); + +} // namespace crashpad + +#endif // CRASHPAD_CLIENT_SIMPLE_STRING_DICTIONARY_H_ diff --git a/shared/sentry/external/crashpad/client/simple_string_dictionary_test.cc b/shared/sentry/external/crashpad/client/simple_string_dictionary_test.cc new file mode 100644 index 000000000..cc025a174 --- /dev/null +++ b/shared/sentry/external/crashpad/client/simple_string_dictionary_test.cc @@ -0,0 +1,282 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "client/simple_string_dictionary.h" + +#include "base/check_op.h" +#include "gtest/gtest.h" +#include "test/gtest_death.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(SimpleStringDictionary, Entry) { + using TestMap = TSimpleStringDictionary<5, 9, 15>; + 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(0u, strlen(entry->key)); + EXPECT_EQ(0u, strlen(entry->value)); +} + +TEST(SimpleStringDictionary, 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(3u, dict.GetCount()); + // 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 nullptr + dict.SetKeyValue("key2", base::StringPiece(nullptr, 0)); + + // Now make sure it's not there anymore + EXPECT_FALSE(dict.GetValueForKey("key2")); +} + +TEST(SimpleStringDictionary, CopyAndAssign) { + TSimpleStringDictionary<10, 10, 10> map; + map.SetKeyValue("one", "a"); + map.SetKeyValue("two", "b"); + map.SetKeyValue("three", "c"); + map.RemoveKey("two"); + EXPECT_EQ(map.GetCount(), 2u); + + // Test copy. + TSimpleStringDictionary<10, 10, 10> map_copy(map); + EXPECT_EQ(map_copy.GetCount(), 2u); + 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. + TSimpleStringDictionary<10, 10, 10> map_assign; + map_assign = map; + EXPECT_EQ(map_assign.GetCount(), 2u); + 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(SimpleStringDictionary, Iterator) { + SimpleStringDictionary dict; + + char key[SimpleStringDictionary::key_size]; + char value[SimpleStringDictionary::value_size]; + + constexpr int kDictionaryCapacity = SimpleStringDictionary::num_entries; + constexpr 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 expected_dictionary_size = 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); + } + expected_dictionary_size = 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"); + expected_dictionary_size -= 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); + } + expected_dictionary_size += 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 total_count = 0; + + for (;;) { + const SimpleStringDictionary::Entry* entry = iter.Next(); + if (!entry) + break; + total_count++; + + // Extract key_number from a string of the form key + int key_number; + sscanf(entry->key, "key%d", &key_number); + + // Extract value_number from a string of the form value + int value_number; + sscanf(entry->value, "value%d", &value_number); + + // The value number should equal the key number since that's how we set them + EXPECT_EQ(value_number, key_number); + + // Key and value numbers should be in proper range: 0 <= key_number < + // kDictionaryCapacity + bool key_in_good_range = + key_number >= 0 && key_number < kDictionaryCapacity; + bool value_in_good_range = + value_number >= 0 && value_number < kDictionaryCapacity; + EXPECT_TRUE(key_in_good_range); + EXPECT_TRUE(value_in_good_range); + + if (key_in_good_range && value_in_good_range) { + ++count[key_number]; + } + } + + // 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(1, count[i]); + } + } + + // Make sure the number of iterations matches the expected dictionary size. + EXPECT_EQ(total_count, expected_dictionary_size); +} + +TEST(SimpleStringDictionary, AddRemove) { + TSimpleStringDictionary<5, 7, 6> map; + map.SetKeyValue("rob", "ert"); + map.SetKeyValue("mike", "pink"); + map.SetKeyValue("mark", "allays"); + + EXPECT_EQ(map.GetCount(), 3u); + EXPECT_STREQ("ert", map.GetValueForKey("rob")); + EXPECT_STREQ("pink", map.GetValueForKey("mike")); + EXPECT_STREQ("allays", map.GetValueForKey("mark")); + + map.RemoveKey("mike"); + + EXPECT_EQ(map.GetCount(), 2u); + EXPECT_FALSE(map.GetValueForKey("mike")); + + map.SetKeyValue("mark", "mal"); + EXPECT_EQ(map.GetCount(), 2u); + EXPECT_STREQ("mal", map.GetValueForKey("mark")); + + map.RemoveKey("mark"); + EXPECT_EQ(map.GetCount(), 1u); + EXPECT_FALSE(map.GetValueForKey("mark")); +} + +// Running out of space shouldn't crash. +TEST(SimpleStringDictionary, OutOfSpace) { + TSimpleStringDictionary<3, 2, 2> map; + map.SetKeyValue("a", "1"); + map.SetKeyValue("b", "2"); + map.SetKeyValue("c", "3"); + EXPECT_EQ(map.GetCount(), 2u); + EXPECT_FALSE(map.GetValueForKey("c")); +} + +#if DCHECK_IS_ON() + +TEST(SimpleStringDictionaryDeathTest, SetKeyValueWithNullKey) { + TSimpleStringDictionary<4, 6, 6> map; + ASSERT_DEATH_CHECK(map.SetKeyValue(base::StringPiece(nullptr, 0), "hello"), + "key"); +} + +TEST(SimpleStringDictionaryDeathTest, GetValueForKeyWithNullKey) { + TSimpleStringDictionary<4, 6, 6> map; + map.SetKeyValue("hi", "there"); + ASSERT_DEATH_CHECK(map.GetValueForKey(base::StringPiece(nullptr, 0)), "key"); + EXPECT_STREQ("there", map.GetValueForKey("hi")); +} + +#endif + +// The tests above, without DEATH_CHECK assertions. +TEST(SimpleStringDictionaryDeathTest, GetValueForKeyWithoutNullKey) { + TSimpleStringDictionary<4, 6, 6> map; + + map.SetKeyValue("hi", "there"); + EXPECT_STREQ("there", map.GetValueForKey("hi")); + map.RemoveKey("hi"); + EXPECT_EQ(map.GetCount(), 0u); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/simulate_crash.h b/shared/sentry/external/crashpad/client/simulate_crash.h new file mode 100644 index 000000000..62a2ff938 --- /dev/null +++ b/shared/sentry/external/crashpad/client/simulate_crash.h @@ -0,0 +1,30 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_CLIENT_SIMULATE_CRASH_H_ +#define CRASHPAD_CLIENT_SIMULATE_CRASH_H_ + +#include "build/build_config.h" + +#if BUILDFLAG(IS_MAC) +#include "client/simulate_crash_mac.h" +#elif BUILDFLAG(IS_IOS) +#include "client/simulate_crash_ios.h" +#elif BUILDFLAG(IS_WIN) +#include "client/simulate_crash_win.h" +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) +#include "client/simulate_crash_linux.h" +#endif + +#endif // CRASHPAD_CLIENT_SIMULATE_CRASH_H_ diff --git a/shared/sentry/external/crashpad/client/simulate_crash_ios.h b/shared/sentry/external/crashpad/client/simulate_crash_ios.h new file mode 100644 index 000000000..14e1e153b --- /dev/null +++ b/shared/sentry/external/crashpad/client/simulate_crash_ios.h @@ -0,0 +1,58 @@ +// Copyright 2021 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_CLIENT_SIMULATE_CRASH_IOS_H_ +#define CRASHPAD_CLIENT_SIMULATE_CRASH_IOS_H_ + +#include "client/crashpad_client.h" +#include "util/misc/capture_context.h" + +//! \file + +//! \brief Captures the CPU context and creates a minidump dump without an +//! exception. The minidump will immediately become eligible for further +//! processing, including upload. +//! +//! \sa CRASHPAD_SIMULATE_CRASH_AND_DEFER_PROCESSING +#define CRASHPAD_SIMULATE_CRASH() \ + do { \ + crashpad::NativeCPUContext cpu_context; \ + crashpad::CaptureContext(&cpu_context); \ + crashpad::CrashpadClient::DumpWithoutCrash(&cpu_context); \ + } while (false) + +//! \brief Captures the CPU context and captures an intermediate dump without an +//! exception. Does not convert the intermediate dump into a minidump. +//! +//! Deferring processing is useful when the application may be in an unstable +//! state, such as during a hang. +//! +//! \sa CRASHPAD_SIMULATE_CRASH +#define CRASHPAD_SIMULATE_CRASH_AND_DEFER_PROCESSING() \ + do { \ + crashpad::NativeCPUContext cpu_context; \ + crashpad::CaptureContext(&cpu_context); \ + crashpad::CrashpadClient::DumpWithoutCrashAndDeferProcessing( \ + &cpu_context); \ + } while (false) + +#define CRASHPAD_SIMULATE_CRASH_AND_DEFER_PROCESSING_AT_PATH(path) \ + do { \ + crashpad::NativeCPUContext cpu_context; \ + crashpad::CaptureContext(&cpu_context); \ + crashpad::CrashpadClient::DumpWithoutCrashAndDeferProcessingAtPath( \ + &cpu_context, path); \ + } while (false) + +#endif // CRASHPAD_CLIENT_SIMULATE_CRASH_IOS_H_ diff --git a/shared/sentry/external/crashpad/client/simulate_crash_linux.h b/shared/sentry/external/crashpad/client/simulate_crash_linux.h new file mode 100644 index 000000000..e6c3e4874 --- /dev/null +++ b/shared/sentry/external/crashpad/client/simulate_crash_linux.h @@ -0,0 +1,31 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_CLIENT_SIMULATE_CRASH_LINUX_H_ +#define CRASHPAD_CLIENT_SIMULATE_CRASH_LINUX_H_ + +#include "client/crashpad_client.h" +#include "util/misc/capture_context.h" + +//! \file + +//! \brief Captures the CPU context and simulates an exception without crashing. +#define CRASHPAD_SIMULATE_CRASH() \ + do { \ + crashpad::NativeCPUContext simulate_crash_cpu_context; \ + crashpad::CaptureContext(&simulate_crash_cpu_context); \ + crashpad::CrashpadClient::DumpWithoutCrash(&simulate_crash_cpu_context); \ + } while (false) + +#endif // CRASHPAD_CLIENT_SIMULATE_CRASH_LINUX_H_ diff --git a/shared/sentry/external/crashpad/client/simulate_crash_mac.cc b/shared/sentry/external/crashpad/client/simulate_crash_mac.cc new file mode 100644 index 000000000..86f4043d6 --- /dev/null +++ b/shared/sentry/external/crashpad/client/simulate_crash_mac.cc @@ -0,0 +1,258 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "client/simulate_crash_mac.h" + +#include +#include + +#include + +#include "base/check_op.h" +#include "base/logging.h" +#include "base/mac/mach_logging.h" +#include "base/mac/scoped_mach_port.h" +#include "build/build_config.h" +#include "util/mach/exc_client_variants.h" +#include "util/mach/exception_behaviors.h" +#include "util/mach/exception_ports.h" +#include "util/mach/mach_extensions.h" +#include "util/misc/implicit_cast.h" + +namespace crashpad { + +namespace { + +//! \brief Sends an exception message to an exception port in accordance with +//! the behavior and thread state flavor it’s registered to receive. +//! +//! \param[in] thread, task, exception, code, code_count These parameters will +//! be passed to the exception handler as appropriate. +//! \param[in] cpu_context The value to use for the thread state, if \a behavior +//! indicates that the handler should receive a thread state and if the +//! supplied thread state matches or can be converted to \a flavor. If \a +//! behavior requires a thread state but this argument cannot be converted +//! to match \a flavor, `thread_get_state()` will be called to obtain a +//! suitable thread state value. +//! \param[in] handler The Mach exception handler to deliver the exception to. +//! \param[in] set_state If `true` and \a behavior indicates that the handler +//! should receive and return a thread state, a new thread state will be set +//! by `thread_set_state()` upon successful completion of the exception +//! handler. If `false`, this will be suppressed, even when \a behavior +//! indicates that the handler receives and returns a thread state. +//! +//! \return `true` if the exception was delivered to the handler and the handler +//! indicated success. `false` otherwise, with a warning message logged. +bool DeliverException(thread_t thread, + task_t task, + exception_type_t exception, + const mach_exception_data_t code, + mach_msg_type_number_t code_count, + const NativeCPUContext& cpu_context, + const ExceptionPorts::ExceptionHandler& handler, + bool set_state) { + kern_return_t kr; + + bool handler_wants_state = ExceptionBehaviorHasState(handler.behavior); + if (!handler_wants_state) { + // Regardless of the passed-in value of |set_state|, if the handler won’t be + // dealing with any state at all, no state should be set. + set_state = false; + } + + // old_state is only used if the context already captured doesn’t match (or + // can’t be converted to) what’s registered for the handler. + thread_state_data_t old_state; + + thread_state_flavor_t flavor = handler.flavor; + ConstThreadState state; + mach_msg_type_number_t state_count; + switch (flavor) { +#if defined(ARCH_CPU_X86_FAMILY) + case x86_THREAD_STATE: + state = reinterpret_cast(&cpu_context); + state_count = x86_THREAD_STATE_COUNT; + break; +#if defined(ARCH_CPU_X86) + case x86_THREAD_STATE32: + state = reinterpret_cast(&cpu_context.uts.ts32); + state_count = cpu_context.tsh.count; + break; +#elif defined(ARCH_CPU_X86_64) + case x86_THREAD_STATE64: + state = reinterpret_cast(&cpu_context.uts.ts64); + state_count = cpu_context.tsh.count; + break; +#endif +#elif defined(ARCH_CPU_ARM64) + case ARM_UNIFIED_THREAD_STATE: + state = reinterpret_cast(&cpu_context); + state_count = ARM_UNIFIED_THREAD_STATE_COUNT; + break; + case ARM_THREAD_STATE64: + state = reinterpret_cast(&cpu_context.ts_64); + state_count = cpu_context.ash.count; + break; +#else +#error Port to your CPU architecture +#endif + + case THREAD_STATE_NONE: + // This is only acceptable if the handler doesn’t have one of the “state†+ // behaviors. Otherwise, if the kernel were attempting to send an + // exception message to this port, it would call thread_getstatus() (known + // outside the kernel as thread_get_state()) which would fail because + // THREAD_STATE_NONE is not a valid state to get. See 10.9.5 + // xnu-2422.115.4/osfmk/kern/exception.c exception_deliver() and + // xnu-2422.115.4/osfmk/i386/pcb.c machine_thread_get_state(). + if (!handler_wants_state) { + state = nullptr; + state_count = 0; + break; + } + + LOG(WARNING) << "exception handler has unexpected state flavor" << flavor; + return false; + + default: + if (!handler_wants_state) { + // Don’t bother getting any thread state if the handler’s not actually + // going to use it. + state = nullptr; + state_count = 0; + } else { + state = old_state; + state_count = THREAD_STATE_MAX; + kr = thread_get_state(thread, flavor, old_state, &state_count); + if (kr != KERN_SUCCESS) { + MACH_LOG(WARNING, kr) << "thread_get_state"; + return false; + } + } + break; + } + + // new_state is supposed to be an out parameter only, but in case the handler + // doesn't touch it, make sure it's initialized to a valid thread state. + // Otherwise, the thread_set_state() call below would set a garbage thread + // state. + thread_state_data_t new_state; + size_t state_size = + sizeof(natural_t) * + std::min(state_count, implicit_cast(THREAD_STATE_MAX)); + memcpy(new_state, state, state_size); + mach_msg_type_number_t new_state_count = THREAD_STATE_MAX; + + kr = UniversalExceptionRaise(handler.behavior, + handler.port, + thread, + task, + exception, + code, + code_count, + &flavor, + state, + state_count, + new_state, + &new_state_count); + + // The kernel treats a return value of MACH_RCV_PORT_DIED as successful, + // although it will not set a new thread state in that case. See 10.9.5 + // xnu-2422.115.4/osfmk/kern/exception.c exception_deliver(), and the more + // elaborate comment at util/mach/exc_server_variants.h + // ExcServerSuccessfulReturnValue(). Duplicate that behavior. + bool success = kr == KERN_SUCCESS || kr == MACH_RCV_PORT_DIED; + MACH_LOG_IF(WARNING, !success, kr) << "UniversalExceptionRaise"; + + if (kr == KERN_SUCCESS && set_state) { + kr = thread_set_state(thread, flavor, new_state, new_state_count); + MACH_LOG_IF(WARNING, kr != KERN_SUCCESS, kr) << "thread_set_state"; + } + + return success; +} + +} // namespace + +void SimulateCrash(const NativeCPUContext& cpu_context) { +#if defined(ARCH_CPU_X86) + DCHECK_EQ(implicit_cast(cpu_context.tsh.flavor), + implicit_cast(x86_THREAD_STATE32)); + DCHECK_EQ(implicit_cast(cpu_context.tsh.count), + x86_THREAD_STATE32_COUNT); +#elif defined(ARCH_CPU_X86_64) + DCHECK_EQ(implicit_cast(cpu_context.tsh.flavor), + implicit_cast(x86_THREAD_STATE64)); + DCHECK_EQ(implicit_cast(cpu_context.tsh.count), + x86_THREAD_STATE64_COUNT); +#elif defined(ARCH_CPU_ARM64) + DCHECK_EQ(implicit_cast(cpu_context.ash.flavor), + implicit_cast(ARM_THREAD_STATE64)); + DCHECK_EQ(implicit_cast(cpu_context.ash.count), + ARM_THREAD_STATE64_COUNT); +#else +#error Port to your CPU architecture +#endif + + base::mac::ScopedMachSendRight thread(mach_thread_self()); + exception_type_t exception = kMachExceptionSimulated; + mach_exception_data_type_t codes[] = {0, 0}; + mach_msg_type_number_t code_count = std::size(codes); + + // Look up the handler for EXC_CRASH exceptions in the same way that the + // kernel would: try a thread handler, then a task handler, and finally a host + // handler. 10.9.5 xnu-2422.115.4/osfmk/kern/exception.c exception_triage(). + static constexpr ExceptionPorts::TargetType kTargetTypes[] = { + ExceptionPorts::kTargetTypeThread, + ExceptionPorts::kTargetTypeTask, + + // This is not expected to succeed, because mach_host_self() doesn’t + // return the host_priv port to non-root users, and this is the port + // that’s required for host_get_exception_ports(). + // + // See 10.9.5 xnu-2422.115.4/bsd/kern/kern_prot.c set_security_token(), + // xnu-2422.115.4/osfmk/kern/task.c host_security_set_task_token(), and + // xnu-2422.115.4/osfmk/kern/ipc_host.c host_get_exception_ports(). + ExceptionPorts::kTargetTypeHost, + }; + + bool success = false; + + for (size_t target_type_index = 0; + !success && target_type_index < std::size(kTargetTypes); + ++target_type_index) { + ExceptionPorts::ExceptionHandlerVector handlers; + ExceptionPorts exception_ports(kTargetTypes[target_type_index], + MACH_PORT_NULL); + if (exception_ports.GetExceptionPorts(EXC_MASK_CRASH, &handlers)) { + DCHECK_LE(handlers.size(), 1u); + if (handlers.size() == 1) { + DCHECK(handlers[0].mask & EXC_MASK_CRASH); + success = DeliverException(thread.get(), + mach_task_self(), + exception, + codes, + code_count, + cpu_context, + handlers[0], + false); + } + } + } + + LOG_IF(WARNING, !success) + << "SimulateCrash did not find an appropriate exception handler"; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/simulate_crash_mac.h b/shared/sentry/external/crashpad/client/simulate_crash_mac.h new file mode 100644 index 000000000..dcbcaae3b --- /dev/null +++ b/shared/sentry/external/crashpad/client/simulate_crash_mac.h @@ -0,0 +1,60 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_CLIENT_SIMULATE_CRASH_MAC_H_ +#define CRASHPAD_CLIENT_SIMULATE_CRASH_MAC_H_ + +#include + +#include "util/misc/capture_context.h" + +//! \file + +namespace crashpad { + +//! \brief Simulates a exception without crashing. +//! +//! This function searches for an `EXC_CRASH` handler in the same manner that +//! the kernel does, and sends it an exception message to that handler in the +//! format that the handler expects, considering the behavior and thread state +//! flavor that are registered for it. The exception sent to the handler will be +//! ::kMachExceptionSimulated, not `EXC_CRASH`. +//! +//! Typically, the CRASHPAD_SIMULATE_CRASH() macro will be used in preference to +//! this function, because it combines the context-capture operation with the +//! raising of a simulated exception. +//! +//! This function returns normally after the exception message is processed. If +//! no valid handler was found, or no handler processed the exception +//! successfully, a warning will be logged, but these conditions are not +//! considered fatal. +//! +//! \param[in] cpu_context The thread state to pass to the exception handler as +//! the exception context, provided that it is compatible with the thread +//! state flavor that the exception handler accepts. If it is not +//! compatible, the correct thread state for the handler will be obtained by +//! calling `thread_get_state()`. +void SimulateCrash(const NativeCPUContext& cpu_context); + +} // namespace crashpad + +//! \brief Captures the CPU context and simulates an exception without crashing. +#define CRASHPAD_SIMULATE_CRASH() \ + do { \ + crashpad::NativeCPUContext cpu_context; \ + crashpad::CaptureContext(&cpu_context); \ + crashpad::SimulateCrash(cpu_context); \ + } while (false) + +#endif // CRASHPAD_CLIENT_SIMULATE_CRASH_MAC_H_ diff --git a/shared/sentry/external/crashpad/client/simulate_crash_mac_test.cc b/shared/sentry/external/crashpad/client/simulate_crash_mac_test.cc new file mode 100644 index 000000000..ccd0267c3 --- /dev/null +++ b/shared/sentry/external/crashpad/client/simulate_crash_mac_test.cc @@ -0,0 +1,414 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "client/simulate_crash.h" + +#include +#include +#include + +#include + +#include "base/strings/stringprintf.h" +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "test/mac/mach_errors.h" +#include "test/mac/mach_multiprocess.h" +#include "util/mach/exc_server_variants.h" +#include "util/mach/exception_behaviors.h" +#include "util/mach/exception_ports.h" +#include "util/mach/mach_extensions.h" +#include "util/mach/mach_message.h" +#include "util/mach/mach_message_server.h" +#include "util/mach/symbolic_constants_mach.h" +#include "util/misc/implicit_cast.h" + +namespace crashpad { +namespace test { +namespace { + +class TestSimulateCrashMac final : public MachMultiprocess, + public UniversalMachExcServer::Interface { + public: + // Defines which targets the child should set an EXC_CRASH exception handler + // for. + enum ExceptionPortsTarget { + // The child should clear its EXC_CRASH handler for both its task and thread + // targets. SimulateCrash() will attempt to deliver the exception to the + // host target, which will fail if not running as root. In any case, the + // parent should not expect to receive any exception message from the child. + kExceptionPortsTargetNone = 0, + + // The child will set an EXC_CRASH handler for its task target, and clear it + // for its thread target. The parent runs an exception server to receive + // the child’s simulated crash message. + kExceptionPortsTargetTask, + + // The child will set an EXC_CRASH handler for its thread target, and clear + // it for its task target. The parent runs an exception server to receive + // the child’s simulated crash message. + kExceptionPortsTargetThread, + + // The child sets an EXC_CRASH handler for both its task and thread targets. + // The parent runs an exception server to receive the message expected to be + // delivered to the thread target, but returns an error code. The child will + // then fall back to trying the server registered for the task target, + // sending a second message to the parent. The server in the parent will + // handle this one successfully. + kExceptionPortsTargetBoth, + }; + + TestSimulateCrashMac(ExceptionPortsTarget target, + exception_behavior_t behavior, + thread_state_flavor_t flavor) + : MachMultiprocess(), + UniversalMachExcServer::Interface(), + target_(target), + behavior_(behavior), + flavor_(flavor), + succeed_(true) { + } + + TestSimulateCrashMac(const TestSimulateCrashMac&) = delete; + TestSimulateCrashMac& operator=(const TestSimulateCrashMac&) = delete; + + ~TestSimulateCrashMac() {} + + // UniversalMachExcServer::Interface: + kern_return_t CatchMachException(exception_behavior_t behavior, + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count, + const mach_msg_trailer_t* trailer, + bool* destroy_complex_request) override { + *destroy_complex_request = true; + + // Check the entire exception message, because most or all of it was + // generated by SimulateCrash() instead of the kernel. + + EXPECT_EQ(behavior, behavior_); + EXPECT_EQ(exception_port, LocalPort()); + if (ExceptionBehaviorHasIdentity(behavior)) { + EXPECT_NE(thread, THREAD_NULL); + EXPECT_EQ(task, ChildTask()); + } else { + EXPECT_EQ(thread, THREAD_NULL); + EXPECT_EQ(task, TASK_NULL); + } + EXPECT_EQ(exception, kMachExceptionSimulated); + EXPECT_EQ(code_count, 2u); + if (code_count >= 1) { + EXPECT_EQ(code[0], 0); + } + if (code_count >= 2) { + EXPECT_EQ(code[1], 0); + } + if (!ExceptionBehaviorHasState(behavior)) { + EXPECT_EQ(*flavor, THREAD_STATE_NONE); + } else { + EXPECT_EQ(*flavor, flavor_); + switch (*flavor) { +#if defined(ARCH_CPU_X86_FAMILY) + case x86_THREAD_STATE: { + EXPECT_EQ(old_state_count, x86_THREAD_STATE_COUNT); + const x86_thread_state* state = + reinterpret_cast(old_state); + switch (state->tsh.flavor) { + case x86_THREAD_STATE32: + EXPECT_EQ(implicit_cast(state->tsh.count), + implicit_cast(x86_THREAD_STATE32_COUNT)); + break; + case x86_THREAD_STATE64: + EXPECT_EQ(implicit_cast(state->tsh.count), + implicit_cast(x86_THREAD_STATE64_COUNT)); + break; + default: + ADD_FAILURE() << "unexpected tsh.flavor " << state->tsh.flavor; + break; + } + break; + } + case x86_FLOAT_STATE: { + EXPECT_EQ(old_state_count, x86_FLOAT_STATE_COUNT); + const x86_float_state* state = + reinterpret_cast(old_state); + switch (state->fsh.flavor) { + case x86_FLOAT_STATE32: + EXPECT_EQ(implicit_cast(state->fsh.count), + implicit_cast(x86_FLOAT_STATE32_COUNT)); + break; + case x86_FLOAT_STATE64: + EXPECT_EQ(implicit_cast(state->fsh.count), + implicit_cast(x86_FLOAT_STATE64_COUNT)); + break; + default: + ADD_FAILURE() << "unexpected fsh.flavor " << state->fsh.flavor; + break; + } + break; + } + case x86_DEBUG_STATE: { + EXPECT_EQ(old_state_count, x86_DEBUG_STATE_COUNT); + const x86_debug_state* state = + reinterpret_cast(old_state); + switch (state->dsh.flavor) { + case x86_DEBUG_STATE32: + EXPECT_EQ(implicit_cast(state->dsh.count), + implicit_cast(x86_DEBUG_STATE32_COUNT)); + break; + case x86_DEBUG_STATE64: + EXPECT_EQ(implicit_cast(state->dsh.count), + implicit_cast(x86_DEBUG_STATE64_COUNT)); + break; + default: + ADD_FAILURE() << "unexpected dsh.flavor " << state->dsh.flavor; + break; + } + break; + } + case x86_THREAD_STATE32: + EXPECT_EQ(old_state_count, x86_THREAD_STATE32_COUNT); + break; + case x86_FLOAT_STATE32: + EXPECT_EQ(old_state_count, x86_FLOAT_STATE32_COUNT); + break; + case x86_DEBUG_STATE32: + EXPECT_EQ(old_state_count, x86_DEBUG_STATE32_COUNT); + break; + case x86_THREAD_STATE64: + EXPECT_EQ(old_state_count, x86_THREAD_STATE64_COUNT); + break; + case x86_FLOAT_STATE64: + EXPECT_EQ(old_state_count, x86_FLOAT_STATE64_COUNT); + break; + case x86_DEBUG_STATE64: + EXPECT_EQ(old_state_count, x86_DEBUG_STATE64_COUNT); + break; +#elif defined(ARCH_CPU_ARM64) + case ARM_UNIFIED_THREAD_STATE: { + EXPECT_EQ(old_state_count, ARM_UNIFIED_THREAD_STATE_COUNT); + const arm_unified_thread_state* state = + reinterpret_cast(old_state); + EXPECT_EQ(state->ash.flavor, + implicit_cast(ARM_THREAD_STATE64)); + if (state->ash.flavor == ARM_THREAD_STATE64) { + EXPECT_EQ(state->ash.count, + implicit_cast(ARM_THREAD_STATE64_COUNT)); + } + break; + } + case ARM_THREAD_STATE64: + EXPECT_EQ(old_state_count, ARM_THREAD_STATE64_COUNT); + break; + case ARM_NEON_STATE64: + EXPECT_EQ(old_state_count, ARM_NEON_STATE64_COUNT); + break; + case ARM_DEBUG_STATE64: + EXPECT_EQ(old_state_count, ARM_DEBUG_STATE64_COUNT); + break; +#else +#error Port to your CPU architecture +#endif + default: + ADD_FAILURE() << "unexpected flavor " << *flavor; + break; + } + + // Attempt to set a garbage thread state, which would cause the child to + // crash inside SimulateCrash() if it actually succeeded. This tests that + // SimulateCrash() ignores new_state instead of attempting to set the + // state as the kernel would do. This operates in conjunction with the + // |true| argument to ExcServerSuccessfulReturnValue() below. + *new_state_count = old_state_count; + size_t new_state_size = sizeof(natural_t) * old_state_count; + memset(new_state, 0xa5, new_state_size); + } + + if (!succeed_) { + // The client has registered EXC_CRASH handlers for both its thread and + // task targets, and sent a simulated exception message to its + // thread-level EXC_CRASH handler. To test that it will fall back to + // trying the task-level EXC_CRASH handler, return a failure code, which + // should cause SimulateCrash() to try the next target. + EXPECT_EQ(target_, kExceptionPortsTargetBoth); + return KERN_ABORTED; + } + + ExcServerCopyState( + behavior, old_state, old_state_count, new_state, new_state_count); + + return ExcServerSuccessfulReturnValue(exception, behavior, true); + } + + private: + // MachMultiprocess: + + void MachMultiprocessParent() override { + if (target_ == kExceptionPortsTargetNone) { + // The child does not have any EXC_CRASH handlers registered for its + // thread or task targets, so no exception message is expected to be + // generated. Don’t run the server at all. + return; + } + + UniversalMachExcServer universal_mach_exc_server(this); + + mach_msg_return_t mr; + if (target_ == kExceptionPortsTargetBoth) { + // The client has registered EXC_CRASH handlers for both its thread and + // task targets. Run a server that will return a failure code when the + // exception message is sent to the thread target, which will cause the + // client to fall back to the task target and send another message. + succeed_ = false; + mr = MachMessageServer::Run(&universal_mach_exc_server, + LocalPort(), + MACH_MSG_OPTION_NONE, + MachMessageServer::kOneShot, + MachMessageServer::kReceiveLargeError, + kMachMessageTimeoutWaitIndefinitely); + EXPECT_EQ(mr, MACH_MSG_SUCCESS) + << MachErrorMessage(mr, "MachMessageServer::Run"); + } + + succeed_ = true; + mr = MachMessageServer::Run(&universal_mach_exc_server, + LocalPort(), + MACH_MSG_OPTION_NONE, + MachMessageServer::kOneShot, + MachMessageServer::kReceiveLargeError, + kMachMessageTimeoutWaitIndefinitely); + EXPECT_EQ(mr, MACH_MSG_SUCCESS) + << MachErrorMessage(mr, "MachMessageServer::Run"); + } + + void MachMultiprocessChild() override { + bool task_valid = target_ == kExceptionPortsTargetTask || + target_ == kExceptionPortsTargetBoth; + ExceptionPorts task_exception_ports(ExceptionPorts::kTargetTypeTask, + TASK_NULL); + ASSERT_TRUE(task_exception_ports.SetExceptionPort( + EXC_MASK_CRASH, + task_valid ? RemotePort() : MACH_PORT_NULL, + behavior_, + flavor_)); + + bool thread_valid = target_ == kExceptionPortsTargetThread || + target_ == kExceptionPortsTargetBoth; + ExceptionPorts thread_exception_ports(ExceptionPorts::kTargetTypeThread, + THREAD_NULL); + ASSERT_TRUE(thread_exception_ports.SetExceptionPort( + EXC_MASK_CRASH, + thread_valid ? RemotePort() : MACH_PORT_NULL, + behavior_, + flavor_)); + + CRASHPAD_SIMULATE_CRASH(); + } + + ExceptionPortsTarget target_; + exception_behavior_t behavior_; + thread_state_flavor_t flavor_; + bool succeed_; +}; + +TEST(SimulateCrash, SimulateCrash) { + static constexpr TestSimulateCrashMac::ExceptionPortsTarget kTargets[] = { + TestSimulateCrashMac::kExceptionPortsTargetNone, + TestSimulateCrashMac::kExceptionPortsTargetTask, + TestSimulateCrashMac::kExceptionPortsTargetThread, + TestSimulateCrashMac::kExceptionPortsTargetBoth, + }; + + static constexpr exception_behavior_t kBehaviors[] = { + EXCEPTION_DEFAULT, + EXCEPTION_STATE, + EXCEPTION_STATE_IDENTITY, + EXCEPTION_DEFAULT | kMachExceptionCodes, + EXCEPTION_STATE | kMachExceptionCodes, + EXCEPTION_STATE_IDENTITY | kMachExceptionCodes, + }; + + static constexpr thread_state_flavor_t kFlavors[] = { +#if defined(ARCH_CPU_X86_FAMILY) + x86_THREAD_STATE, + x86_FLOAT_STATE, + x86_DEBUG_STATE, +#if defined(ARCH_CPU_X86) + x86_THREAD_STATE32, + x86_FLOAT_STATE32, + x86_DEBUG_STATE32, +#elif defined(ARCH_CPU_X86_64) + x86_THREAD_STATE64, + x86_FLOAT_STATE64, + x86_DEBUG_STATE64, +#endif +#elif defined(ARCH_CPU_ARM64) + ARM_UNIFIED_THREAD_STATE, + ARM_THREAD_STATE64, + ARM_NEON_STATE64, + ARM_DEBUG_STATE64, +#else +#error Port to your CPU architecture +#endif + }; + + for (size_t target_index = 0; target_index < std::size(kTargets); + ++target_index) { + TestSimulateCrashMac::ExceptionPortsTarget target = kTargets[target_index]; + SCOPED_TRACE(base::StringPrintf( + "target_index %zu, target %d", target_index, target)); + + for (size_t behavior_index = 0; behavior_index < std::size(kBehaviors); + ++behavior_index) { + exception_behavior_t behavior = kBehaviors[behavior_index]; + SCOPED_TRACE(base::StringPrintf( + "behavior_index %zu, behavior %s", + behavior_index, + ExceptionBehaviorToString(behavior, kUseFullName | kUnknownIsNumeric) + .c_str())); + + if (!ExceptionBehaviorHasState(behavior)) { + TestSimulateCrashMac test_simulate_crash_mac( + target, behavior, THREAD_STATE_NONE); + test_simulate_crash_mac.Run(); + } else { + for (size_t flavor_index = 0; flavor_index < std::size(kFlavors); + ++flavor_index) { + thread_state_flavor_t flavor = kFlavors[flavor_index]; + SCOPED_TRACE(base::StringPrintf( + "flavor_index %zu, flavor %s", + flavor_index, + ThreadStateFlavorToString( + flavor, kUseFullName | kUnknownIsNumeric).c_str())); + + TestSimulateCrashMac test_simulate_crash_mac( + target, behavior, flavor); + test_simulate_crash_mac.Run(); + } + } + } + } +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/client/simulate_crash_win.h b/shared/sentry/external/crashpad/client/simulate_crash_win.h new file mode 100644 index 000000000..140424f32 --- /dev/null +++ b/shared/sentry/external/crashpad/client/simulate_crash_win.h @@ -0,0 +1,34 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_CLIENT_SIMULATE_CRASH_WIN_H_ +#define CRASHPAD_CLIENT_SIMULATE_CRASH_WIN_H_ + +#include + +#include "client/crashpad_client.h" +#include "util/misc/capture_context.h" + +//! \file + +//! \brief Captures the CPU context and captures a dump without an exception. +#define CRASHPAD_SIMULATE_CRASH() \ + do { \ + /* Not "context" to avoid variable shadowing warnings. */ \ + CONTEXT simulate_crash_cpu_context; \ + crashpad::CaptureContext(&simulate_crash_cpu_context); \ + crashpad::CrashpadClient::DumpWithoutCrash(simulate_crash_cpu_context); \ + } while (false) + +#endif // CRASHPAD_CLIENT_SIMULATE_CRASH_WIN_H_ diff --git a/shared/sentry/external/crashpad/codereview.settings b/shared/sentry/external/crashpad/codereview.settings new file mode 100644 index 000000000..976f8e532 --- /dev/null +++ b/shared/sentry/external/crashpad/codereview.settings @@ -0,0 +1,19 @@ +# Copyright 2014 The Crashpad Authors. All rights reserved. +# +# 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. + +GERRIT_HOST: True +CODE_REVIEW_SERVER: https://chromium-review.googlesource.com/ +VIEW_VC: https://chromium.googlesource.com/crashpad/crashpad/+/ +PROJECT: crashpad +BUG_PREFIX: crashpad: diff --git a/shared/sentry/external/crashpad/compat/BUILD.gn b/shared/sentry/external/crashpad/compat/BUILD.gn new file mode 100644 index 000000000..8d76325db --- /dev/null +++ b/shared/sentry/external/crashpad/compat/BUILD.gn @@ -0,0 +1,156 @@ +# Copyright 2015 The Crashpad Authors. All rights reserved. +# +# 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("../build/crashpad_buildconfig.gni") + +config("compat_config") { + include_dirs = [] + + if (crashpad_is_mac || crashpad_is_ios) { + include_dirs += [ "mac" ] + } + + if (crashpad_is_ios) { + include_dirs += [ "ios" ] + } + + if (crashpad_is_linux || crashpad_is_android) { + include_dirs += [ "linux" ] + } + + if (crashpad_is_android) { + include_dirs += [ "android" ] + } + + if (crashpad_is_win) { + include_dirs += [ "win" ] + } else { + include_dirs += [ "non_win" ] + } +} + +template("compat_target") { + if (crashpad_is_mac || crashpad_is_ios) { + # There are no sources to compile, which doesn’t mix will with a + # static_library. + group(target_name) { + forward_variables_from(invoker, "*") + not_needed([ "configs" ]) + } + } else { + crashpad_static_library(target_name) { + forward_variables_from(invoker, "*", [ "configs" ]) + if (!defined(configs)) { + configs = [] + } + if (defined(invoker.configs)) { + configs += invoker.configs + } + } + } +} + +compat_target("compat") { + sources = [] + + if (crashpad_is_mac || crashpad_is_ios) { + sources += [ + "mac/Availability.h", + "mac/AvailabilityVersions.h", + "mac/kern/exc_resource.h", + "mac/mach-o/loader.h", + "mac/mach/i386/thread_state.h", + "mac/mach/mach.h", + "mac/sys/resource.h", + ] + } else { + sources += [ "non_mac/mach/mach.h" ] + } + + if (crashpad_is_ios) { + sources += [ + "ios/mach/exc.defs", + "ios/mach/mach_exc.defs", + "ios/mach/mach_types.defs", + "ios/mach/machine/machine_types.defs", + "ios/mach/std_types.defs", + ] + } + + if (crashpad_is_linux || crashpad_is_android) { + sources += [ + "linux/signal.h", + "linux/sys/mman.h", + "linux/sys/mman_memfd_create.cc", + "linux/sys/ptrace.h", + "linux/sys/user.h", + ] + } + + if (crashpad_is_android) { + sources += [ + "android/dlfcn_internal.cc", + "android/dlfcn_internal.h", + "android/elf.h", + "android/linux/elf.h", + "android/linux/prctl.h", + "android/linux/ptrace.h", + "android/sched.h", + "android/sys/epoll.cc", + "android/sys/epoll.h", + "android/sys/mman.h", + "android/sys/mman_mmap.cc", + "android/sys/syscall.h", + "android/sys/user.h", + ] + } + + if (crashpad_is_win) { + sources += [ + "win/getopt.h", + "win/strings.cc", + "win/strings.h", + "win/sys/types.h", + "win/time.cc", + "win/time.h", + "win/winbase.h", + "win/winnt.h", + "win/winternl.h", + ] + } else { + sources += [ + "non_win/dbghelp.h", + "non_win/minwinbase.h", + "non_win/timezoneapi.h", + "non_win/verrsrc.h", + "non_win/windows.h", + "non_win/winnt.h", + ] + } + + public_configs = [ + ":compat_config", + "..:crashpad_config", + ] + + if (!crashpad_is_win) { + public_deps = [ "$mini_chromium_source_parent:base" ] + } + + deps = [ "../util:no_cfi_icall" ] + + if (crashpad_is_win) { + deps += [ "../third_party/getopt" ] + } +} diff --git a/shared/sentry/external/crashpad/compat/CMakeLists.txt b/shared/sentry/external/crashpad/compat/CMakeLists.txt new file mode 100644 index 000000000..e4f430360 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/CMakeLists.txt @@ -0,0 +1,128 @@ +set(COMPAT_SOURCES) + +if(APPLE) + list(APPEND COMPAT_SOURCES + mac/Availability.h + mac/AvailabilityVersions.h + mac/kern/exc_resource.h + mac/mach-o/loader.h + mac/mach/i386/thread_state.h + mac/mach/mach.h + mac/sys/resource.h + ) +else() + list(APPEND COMPAT_SOURCES + non_mac/mach/mach.h + ) +endif() + +if(IOS) + list(APPEND COMPAT_SOURCES + ios/mach/exc.defs + ios/mach/mach_exc.defs + ios/mach/mach_types.defs + ios/mach/machine/machine_types.defs + ios/mach/std_types.defs + ) +endif() + +if(LINUX OR ANDROID) + list(APPEND COMPAT_SOURCES + linux/signal.h + linux/sys/mman.h + linux/sys/mman_memfd_create.cc + linux/sys/ptrace.h + linux/sys/user.h + ) +endif() + +if(ANDROID) + list(APPEND COMPAT_SOURCES + android/dlfcn_internal.cc + android/dlfcn_internal.h + android/elf.h + android/linux/elf.h + android/linux/prctl.h + android/linux/ptrace.h + android/sched.h + android/sys/epoll.cc + android/sys/epoll.h + android/sys/mman.h + android/sys/mman_mmap.cc + android/sys/syscall.h + android/sys/user.h + ) +endif() + +if(MSVC) + list(APPEND COMPAT_SOURCES + win/getopt.h + win/strings.cc + win/strings.h + win/sys/types.h + win/time.cc + win/time.h + win/winbase.h + win/winnt.h + win/winternl.h + ) +elseif(MINGW) + list(APPEND COMPAT_SOURCES + mingw/dbghelp.h + mingw/winnt.h + ) +else() + list(APPEND COMPAT_SOURCES + non_win/dbghelp.h + non_win/minwinbase.h + non_win/timezoneapi.h + non_win/verrsrc.h + non_win/windows.h + non_win/winnt.h + ) +endif() + +if(APPLE) + add_library(crashpad_compat INTERFACE) + set(TI_TYPE "INTERFACE") +else() + add_library(crashpad_compat STATIC ${COMPAT_SOURCES}) + set_target_properties(crashpad_compat PROPERTIES LINKER_LANGUAGE CXX) + set(TI_TYPE "PUBLIC") + target_link_libraries(crashpad_compat PRIVATE + $ + ) +endif() + +if(LINUX) + target_link_libraries(crashpad_compat PRIVATE dl) +endif() + +if(MSVC) + target_include_directories(crashpad_compat ${TI_TYPE} "$") +elseif(MINGW) + target_include_directories(crashpad_compat ${TI_TYPE} "$") +else() + target_include_directories(crashpad_compat ${TI_TYPE} "$") +endif() + +if(APPLE) + target_include_directories(crashpad_compat ${TI_TYPE} "$") +endif() + +if(IOS) + target_include_directories(crashpad_compat ${TI_TYPE} "$") +endif() + +if(LINUX OR ANDROID) + target_include_directories(crashpad_compat ${TI_TYPE} "$") +endif() + +if(ANDROID) + target_include_directories(crashpad_compat ${TI_TYPE} "$") +endif() + +set_property(TARGET crashpad_compat PROPERTY EXPORT_NAME compat) +add_library(crashpad::compat ALIAS crashpad_compat) + +crashpad_install_target(crashpad_compat) diff --git a/shared/sentry/external/crashpad/compat/android/dlfcn_internal.cc b/shared/sentry/external/crashpad/compat/android/dlfcn_internal.cc new file mode 100644 index 000000000..5d98fe847 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/android/dlfcn_internal.cc @@ -0,0 +1,166 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "dlfcn_internal.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace crashpad { +namespace internal { + +// KitKat supports API levels up to 20. +#if __ANDROID_API__ < 21 + +namespace { + +class ScopedSigactionRestore { + public: + ScopedSigactionRestore() : old_action_(), signo_(-1), valid_(false) {} + + ~ScopedSigactionRestore() { Reset(); } + + bool Reset() { + bool result = true; + if (valid_) { + result = sigaction(signo_, &old_action_, nullptr) == 0; + if (!result) { + PrintErrmsg(errno); + } + } + valid_ = false; + signo_ = -1; + return result; + } + + bool ResetAndInstallHandler(int signo, + void (*handler)(int, siginfo_t*, void*)) { + Reset(); + + struct sigaction act; + act.sa_sigaction = handler; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_SIGINFO; + if (sigaction(signo, &act, &old_action_) != 0) { + PrintErrmsg(errno); + return false; + } + signo_ = signo; + valid_ = true; + return true; + } + + private: + void PrintErrmsg(int err) { + char errmsg[256]; + + if (strerror_r(err, errmsg, sizeof(errmsg)) != 0) { + snprintf(errmsg, + sizeof(errmsg), + "%s:%d: Couldn't set errmsg for %d: %d", + __FILE__, + __LINE__, + err, + errno); + return; + } + + fprintf(stderr, "%s:%d: sigaction: %s", __FILE__, __LINE__, errmsg); + } + + struct sigaction old_action_; + int signo_; + bool valid_; +}; + +bool IsKitKat() { + char prop_buf[PROP_VALUE_MAX]; + int length = __system_property_get("ro.build.version.sdk", prop_buf); + if (length <= 0) { + fprintf(stderr, "%s:%d: Couldn't get version", __FILE__, __LINE__); + // It's safer to assume this is KitKat and execute dlsym with a signal + // handler installed. + return true; + } + if (strcmp(prop_buf, "19") == 0 || strcmp(prop_buf, "20") == 0) { + return true; + } + return false; +} + +class ScopedSetTID { + public: + explicit ScopedSetTID(pid_t* tid) : tid_(tid) { *tid_ = syscall(SYS_gettid); } + + ~ScopedSetTID() { *tid_ = -1; } + + private: + pid_t* tid_; +}; + +sigjmp_buf dlsym_sigjmp_env; + +pid_t dlsym_tid = -1; + +void HandleSIGFPE(int signo, siginfo_t* siginfo, void* context) { + if (siginfo->si_code != FPE_INTDIV || syscall(SYS_gettid) != dlsym_tid) { + return; + } + siglongjmp(dlsym_sigjmp_env, 1); +} + +} // namespace + +void* Dlsym(void* handle, const char* symbol) { + if (!IsKitKat()) { + return dlsym(handle, symbol); + } + + static std::mutex* signal_handler_mutex = new std::mutex(); + std::lock_guard lock(*signal_handler_mutex); + + ScopedSetTID set_tid(&dlsym_tid); + + ScopedSigactionRestore sig_restore; + if (!sig_restore.ResetAndInstallHandler(SIGFPE, HandleSIGFPE)) { + return nullptr; + } + + if (sigsetjmp(dlsym_sigjmp_env, 1) != 0) { + return nullptr; + } + + return dlsym(handle, symbol); +} + +#else + +void* Dlsym(void* handle, const char* symbol) { + return dlsym(handle, symbol); +} + +#endif // __ANDROID_API__ < 21 + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/compat/android/dlfcn_internal.h b/shared/sentry/external/crashpad/compat/android/dlfcn_internal.h new file mode 100644 index 000000000..ed4083dbc --- /dev/null +++ b/shared/sentry/external/crashpad/compat/android/dlfcn_internal.h @@ -0,0 +1,35 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_ANDROID_DLFCN_INTERNAL_H_ +#define CRASHPAD_COMPAT_ANDROID_DLFCN_INTERNAL_H_ + +namespace crashpad { +namespace internal { + +//! \brief Provide a wrapper for `dlsym`. +//! +//! dlsym on Android KitKat (4.4.*) raises SIGFPE when searching for a +//! non-existent symbol. This wrapper avoids crashing in this circumstance. +//! https://code.google.com/p/android/issues/detail?id=61799 +//! +//! The parameters and return value for this function are the same as for +//! `dlsym`, but a return value for `dlerror` may not be set in the event of an +//! error. +void* Dlsym(void* handle, const char* symbol); + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_COMPAT_ANDROID_DLFCN_INTERNAL_H_ diff --git a/shared/sentry/external/crashpad/compat/android/elf.h b/shared/sentry/external/crashpad/compat/android/elf.h new file mode 100644 index 000000000..9fa923875 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/android/elf.h @@ -0,0 +1,58 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_ANDROID_ELF_H_ +#define CRASHPAD_COMPAT_ANDROID_ELF_H_ + +#include_next + +#include + +#if !defined(ELF32_ST_VISIBILITY) +#define ELF32_ST_VISIBILITY(other) ((other) & 0x3) +#endif + +#if !defined(ELF64_ST_VISIBILITY) +#define ELF64_ST_VISIBILITY(other) ELF32_ST_VISIBILITY(other) +#endif + +// Android 5.0.0 (API 21) NDK + +#if !defined(STT_COMMON) +#define STT_COMMON 5 +#endif + +#if !defined(STT_TLS) +#define STT_TLS 6 +#endif + +// ELF note header types are normally provided by . While unified +// headers include in , traditional headers do not, prior +// to API 21. and can't both be included in the same +// translation unit due to collisions, so we provide these types here. +#if __ANDROID_API__ < 21 && !defined(__ANDROID_API_N__) +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; +#endif // __ANDROID_API__ < 21 && !defined(NT_PRSTATUS) + +#endif // CRASHPAD_COMPAT_ANDROID_ELF_H_ diff --git a/shared/sentry/external/crashpad/compat/android/linux/elf.h b/shared/sentry/external/crashpad/compat/android/linux/elf.h new file mode 100644 index 000000000..e65d15327 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/android/linux/elf.h @@ -0,0 +1,38 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_ANDROID_LINUX_ELF_H_ +#define CRASHPAD_COMPAT_ANDROID_LINUX_ELF_H_ + +#include_next + +// Android 5.0.0 (API 21) NDK + +#if defined(__i386__) || defined(__x86_64__) +#if !defined(NT_386_TLS) +#define NT_386_TLS 0x200 +#endif +#endif // __i386__ || __x86_64__ + +#if defined(__ARMEL__) || defined(__aarch64__) +#if !defined(NT_ARM_VFP) +#define NT_ARM_VFP 0x400 +#endif + +#if !defined(NT_ARM_TLS) +#define NT_ARM_TLS 0x401 +#endif +#endif // __ARMEL__ || __aarch64__ + +#endif // CRASHPAD_COMPAT_ANDROID_LINUX_ELF_H_ diff --git a/shared/sentry/external/crashpad/compat/android/linux/prctl.h b/shared/sentry/external/crashpad/compat/android/linux/prctl.h new file mode 100644 index 000000000..046f900f0 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/android/linux/prctl.h @@ -0,0 +1,25 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_ANDROID_LINUX_PRCTL_H_ +#define CRASHPAD_COMPAT_ANDROID_LINUX_PRCTL_H_ + +#include_next + +// Android 5.0.0 (API 21) NDK +#if !defined(PR_SET_PTRACER) +#define PR_SET_PTRACER 0x59616d61 +#endif + +#endif // CRASHPAD_COMPAT_ANDROID_LINUX_PRCTL_H_ diff --git a/shared/sentry/external/crashpad/compat/android/linux/ptrace.h b/shared/sentry/external/crashpad/compat/android/linux/ptrace.h new file mode 100644 index 000000000..7db46aa3f --- /dev/null +++ b/shared/sentry/external/crashpad/compat/android/linux/ptrace.h @@ -0,0 +1,25 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_ANDROID_LINUX_PTRACE_H_ +#define CRASHPAD_COMPAT_ANDROID_LINUX_PTRACE_H_ + +#include_next + +// Android 5.0.0 (API 21) NDK +#if !defined(PTRACE_GETREGSET) +#define PTRACE_GETREGSET 0x4204 +#endif + +#endif // CRASHPAD_COMPAT_ANDROID_LINUX_PTRACE_H_ diff --git a/shared/sentry/external/crashpad/compat/android/sched.h b/shared/sentry/external/crashpad/compat/android/sched.h new file mode 100644 index 000000000..c4a027ae4 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/android/sched.h @@ -0,0 +1,30 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_ANDROID_SCHED_H_ +#define CRASHPAD_COMPAT_ANDROID_SCHED_H_ + +#include_next + +// Android 5.0.0 (API 21) NDK + +#if !defined(SCHED_BATCH) +#define SCHED_BATCH 3 +#endif + +#if !defined(SCHED_IDLE) +#define SCHED_IDLE 5 +#endif + +#endif // CRASHPAD_COMPAT_ANDROID_SCHED_H_ diff --git a/shared/sentry/external/crashpad/compat/android/sys/epoll.cc b/shared/sentry/external/crashpad/compat/android/sys/epoll.cc new file mode 100644 index 000000000..6c1a47663 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/android/sys/epoll.cc @@ -0,0 +1,37 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 + +#include +#include +#include + +#include "dlfcn_internal.h" +#include "util/misc/no_cfi_icall.h" + +#if __ANDROID_API__ < 21 + +extern "C" { + +int epoll_create1(int flags) { + static const crashpad::NoCfiIcall epoll_create1_p( + crashpad::internal::Dlsym(RTLD_DEFAULT, "epoll_create1")); + return epoll_create1_p ? epoll_create1_p(flags) + : syscall(SYS_epoll_create1, flags); +} + +} // extern "C" + +#endif // __ANDROID_API__ < 21 diff --git a/shared/sentry/external/crashpad/compat/android/sys/epoll.h b/shared/sentry/external/crashpad/compat/android/sys/epoll.h new file mode 100644 index 000000000..387813e6a --- /dev/null +++ b/shared/sentry/external/crashpad/compat/android/sys/epoll.h @@ -0,0 +1,50 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_ANDROID_SYS_EPOLL_H_ +#define CRASHPAD_COMPAT_ANDROID_SYS_EPOLL_H_ + +#include_next + +#include +#include + +// This is missing from traditional headers before API 21. +#if !defined(EPOLLRDHUP) +#define EPOLLRDHUP 0x00002000 +#endif + +// EPOLL_CLOEXEC is undefined in traditional headers before API 21 and removed +// from unified headers at API levels < 21 as a means to indicate that +// epoll_create1 is missing from the C library, but the raw system call should +// still be available. +#if !defined(EPOLL_CLOEXEC) +#define EPOLL_CLOEXEC O_CLOEXEC +#endif + +#if __ANDROID_API__ < 21 + +#ifdef __cplusplus +extern "C" { +#endif + +int epoll_create1(int flags); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // __ANDROID_API__ < 21 + +#endif // CRASHPAD_COMPAT_ANDROID_SYS_EPOLL_H_ diff --git a/shared/sentry/external/crashpad/compat/android/sys/mman.h b/shared/sentry/external/crashpad/compat/android/sys/mman.h new file mode 100644 index 000000000..5e7cd69f1 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/android/sys/mman.h @@ -0,0 +1,43 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_ANDROID_SYS_MMAN_H_ +#define CRASHPAD_COMPAT_ANDROID_SYS_MMAN_H_ + +#include_next + +#include +#include + +// There’s no mmap() wrapper compatible with a 64-bit off_t for 32-bit code +// until API 21 (Android 5.0/“Lollipopâ€). A custom mmap() wrapper is provided +// here. Note that this scenario is only possible with NDK unified headers. +// +// https://android.googlesource.com/platform/bionic/+/0bfcbaf4d069e005d6e959d97f8d11c77722b70d/docs/32-bit-abi.md#is-32_bit-1 + +#if defined(__USE_FILE_OFFSET64) && __ANDROID_API__ < 21 + +#ifdef __cplusplus +extern "C" { +#endif + +void* mmap(void* addr, size_t size, int prot, int flags, int fd, off_t offset); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // defined(__USE_FILE_OFFSET64) && __ANDROID_API__ < 21 + +#endif // CRASHPAD_COMPAT_ANDROID_SYS_MMAN_H_ diff --git a/shared/sentry/external/crashpad/compat/android/sys/mman_mmap.cc b/shared/sentry/external/crashpad/compat/android/sys/mman_mmap.cc new file mode 100644 index 000000000..c929cfe73 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/android/sys/mman_mmap.cc @@ -0,0 +1,105 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 + +#include +#include +#include +#include + +#include "dlfcn_internal.h" +#include "util/misc/no_cfi_icall.h" + +#if defined(__USE_FILE_OFFSET64) && __ANDROID_API__ < 21 + +// Bionic has provided a wrapper for __mmap2() since the beginning of time. See +// bionic/libc/SYSCALLS.TXT in any Android version. +extern "C" void* __mmap2(void* addr, + size_t size, + int prot, + int flags, + int fd, + size_t pgoff); + +namespace { + +template +T Align(T value, size_t alignment) { + return (value + alignment - 1) & ~(alignment - 1); +} + +// Adapted from Android 8.0.0 bionic/libc/bionic/mmap.cpp. +void* LocalMmap64(void* addr, + size_t size, + int prot, + int flags, + int fd, + off64_t offset) { + constexpr int kMmap2Shift = 12; + + if (offset < 0 || (offset & ((1UL << kMmap2Shift) - 1)) != 0) { + errno = EINVAL; + return MAP_FAILED; + } + + const size_t rounded = Align(size, getpagesize()); + if (rounded < size || rounded > PTRDIFF_MAX) { + errno = ENOMEM; + return MAP_FAILED; + } + + const bool is_private_anonymous = + (flags & (MAP_PRIVATE | MAP_ANONYMOUS)) == (MAP_PRIVATE | MAP_ANONYMOUS); + const bool is_stack_or_grows_down = + (flags & (MAP_STACK | MAP_GROWSDOWN)) != 0; + + void* const result = + __mmap2(addr, size, prot, flags, fd, offset >> kMmap2Shift); + + static bool kernel_has_MADV_MERGEABLE = true; + if (result != MAP_FAILED && kernel_has_MADV_MERGEABLE && + is_private_anonymous && !is_stack_or_grows_down) { + const int saved_errno = errno; + const int rc = madvise(result, size, MADV_MERGEABLE); + if (rc == -1 && errno == EINVAL) { + kernel_has_MADV_MERGEABLE = false; + } + errno = saved_errno; + } + + return result; +} + +} // namespace + +extern "C" { + +void* mmap(void* addr, size_t size, int prot, int flags, int fd, off_t offset) { + // Use the system’s mmap64() wrapper if available. It will be available on + // Android 5.0 (“Lollipopâ€) and later. + static const crashpad::NoCfiIcall mmap64( + crashpad::internal::Dlsym(RTLD_DEFAULT, "mmap64")); + if (mmap64) { + return mmap64(addr, size, prot, flags, fd, offset); + } + + // Otherwise, use the local implementation, which should amount to exactly the + // same thing. + return LocalMmap64(addr, size, prot, flags, fd, offset); +} + +} // extern "C" + +#endif // defined(__USE_FILE_OFFSET64) && __ANDROID_API__ < 21 diff --git a/shared/sentry/external/crashpad/compat/android/sys/syscall.h b/shared/sentry/external/crashpad/compat/android/sys/syscall.h new file mode 100644 index 000000000..facce20f7 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/android/sys/syscall.h @@ -0,0 +1,42 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_ANDROID_SYS_SYSCALL_H_ +#define CRASHPAD_COMPAT_ANDROID_SYS_SYSCALL_H_ + +#include_next + +// Android 5.0.0 (API 21) NDK + +#if !defined(SYS_epoll_create1) +#define SYS_epoll_create1 __NR_epoll_create1 +#endif + +#if !defined(SYS_gettid) +#define SYS_gettid __NR_gettid +#endif + +#if !defined(SYS_timer_create) +#define SYS_timer_create __NR_timer_create +#endif + +#if !defined(SYS_timer_getoverrun) +#define SYS_timer_getoverrun __NR_timer_getoverrun +#endif + +#if !defined(SYS_timer_settime) +#define SYS_timer_settime __NR_timer_settime +#endif + +#endif // CRASHPAD_COMPAT_ANDROID_SYS_SYSCALL_H_ diff --git a/shared/sentry/external/crashpad/compat/android/sys/user.h b/shared/sentry/external/crashpad/compat/android/sys/user.h new file mode 100644 index 000000000..4352a2067 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/android/sys/user.h @@ -0,0 +1,23 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_ANDROID_SYS_USER_H_ +#define CRASHPAD_COMPAT_ANDROID_SYS_USER_H_ + +// This is needed for traditional headers. +#include + +#include_next + +#endif // CRASHPAD_COMPAT_ANDROID_SYS_USER_H_ diff --git a/shared/sentry/external/crashpad/compat/ios/mach/exc.defs b/shared/sentry/external/crashpad/compat/ios/mach/exc.defs new file mode 100644 index 000000000..d1648e97c --- /dev/null +++ b/shared/sentry/external/crashpad/compat/ios/mach/exc.defs @@ -0,0 +1,20 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_IOS_MACH_EXC_DEFS_ +#define CRASHPAD_COMPAT_IOS_MACH_EXC_DEFS_ + +#include "third_party/xnu/osfmk/mach/exc.defs" + +#endif // CRASHPAD_COMPAT_IOS_MACH_EXC_DEFS_ diff --git a/shared/sentry/external/crashpad/compat/ios/mach/mach_exc.defs b/shared/sentry/external/crashpad/compat/ios/mach/mach_exc.defs new file mode 100644 index 000000000..c562128e8 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/ios/mach/mach_exc.defs @@ -0,0 +1,20 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_IOS_MACH_MACH_EXC_DEFS_ +#define CRASHPAD_COMPAT_IOS_MACH_MACH_EXC_DEFS_ + +#include "third_party/xnu/osfmk/mach/mach_exc.defs" + +#endif // CRASHPAD_COMPAT_IOS_MACH_MACH_EXC_DEFS_ diff --git a/shared/sentry/external/crashpad/compat/ios/mach/mach_types.defs b/shared/sentry/external/crashpad/compat/ios/mach/mach_types.defs new file mode 100644 index 000000000..dc18b8eb0 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/ios/mach/mach_types.defs @@ -0,0 +1,20 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_IOS_MACH_MACH_TYPES_DEFS_ +#define CRASHPAD_COMPAT_IOS_MACH_MACH_TYPES_DEFS_ + +#include "third_party/xnu/osfmk/mach/mach_types.defs" + +#endif // CRASHPAD_COMPAT_IOS_MACH_MACH_TYPES_DEFS_ diff --git a/shared/sentry/external/crashpad/compat/ios/mach/machine/machine_types.defs b/shared/sentry/external/crashpad/compat/ios/mach/machine/machine_types.defs new file mode 100644 index 000000000..e906466f5 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/ios/mach/machine/machine_types.defs @@ -0,0 +1,20 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_IOS_MACH_MACHINE_MACHINE_TYPES_DEFS_ +#define CRASHPAD_COMPAT_IOS_MACH_MACHINE_MACHINE_TYPES_DEFS_ + +#include "third_party/xnu/osfmk/mach/machine/machine_types.defs" + +#endif // CRASHPAD_COMPAT_IOS_MACH_MACHINE_MACHINE_TYPES_DEFS_ diff --git a/shared/sentry/external/crashpad/compat/ios/mach/std_types.defs b/shared/sentry/external/crashpad/compat/ios/mach/std_types.defs new file mode 100644 index 000000000..e49c6e46b --- /dev/null +++ b/shared/sentry/external/crashpad/compat/ios/mach/std_types.defs @@ -0,0 +1,20 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_IOS_MACH_STD_TYPES_DEFS_ +#define CRASHPAD_COMPAT_IOS_MACH_STD_TYPES_DEFS_ + +#include "third_party/xnu/osfmk/mach/std_types.defs" + +#endif // CRASHPAD_COMPAT_IOS_MACH_STD_TYPES_DEFS_ diff --git a/shared/sentry/external/crashpad/compat/linux/signal.h b/shared/sentry/external/crashpad/compat/linux/signal.h new file mode 100644 index 000000000..a00724663 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/linux/signal.h @@ -0,0 +1,65 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_LINUX_SIGNAL_H_ +#define CRASHPAD_COMPAT_LINUX_SIGNAL_H_ + +#include_next + +// Missing from glibc and bionic +#if !defined(SS_AUTODISARM) +#define SS_AUTODISARM (1u << 31) +#endif + +// Missing from glibc and bionic-x86_64 + +#if defined(__x86_64__) || defined(__i386__) +#if !defined(X86_FXSR_MAGIC) +#define X86_FXSR_MAGIC 0x0000 +#endif +#endif // __x86_64__ || __i386__ + +#if defined(__aarch64__) || defined(__arm__) + +#if !defined(FPSIMD_MAGIC) +#define FPSIMD_MAGIC 0x46508001 +#endif + +#if !defined(ESR_MAGIC) +#define ESR_MAGIC 0x45535201 +#endif + +#if !defined(EXTRA_MAGIC) +#define EXTRA_MAGIC 0x45585401 +#endif + +#if !defined(VFP_MAGIC) +#define VFP_MAGIC 0x56465001 +#endif + +#if !defined(CRUNCH_MAGIC) +#define CRUNCH_MAGIC 0x5065cf03 +#endif + +#if !defined(DUMMY_MAGIC) +#define DUMMY_MAGIC 0xb0d9ed01 +#endif + +#if !defined(IWMMXT_MAGIC) +#define IWMMXT_MAGIC 0x12ef842a +#endif + +#endif // __aarch64__ || __arm__ + +#endif // CRASHPAD_COMPAT_LINUX_SIGNAL_H_ diff --git a/shared/sentry/external/crashpad/compat/linux/sys/mman.h b/shared/sentry/external/crashpad/compat/linux/sys/mman.h new file mode 100644 index 000000000..cc5e745e6 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/linux/sys/mman.h @@ -0,0 +1,44 @@ +// Copyright 2019 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_LINUX_SYS_MMAN_H_ +#define CRASHPAD_COMPAT_LINUX_SYS_MMAN_H_ + +#include_next + +#include + +// There's no memfd_create() wrapper before glibc 2.27. +// This can't select for glibc < 2.27 because linux-chromeos-rel bots build this +// code using a sysroot which has glibc 2.27, but then run it on Ubuntu 16.04, +// which doesn't. +#if defined(__GLIBC__) + +#ifndef __THROW +#define __THROW +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +int memfd_create(const char* name, unsigned int flags) __THROW; + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // __GLIBC__ + +#endif // CRASHPAD_COMPAT_LINUX_SYS_MMAN_H_ diff --git a/shared/sentry/external/crashpad/compat/linux/sys/mman_memfd_create.cc b/shared/sentry/external/crashpad/compat/linux/sys/mman_memfd_create.cc new file mode 100644 index 000000000..619431e49 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/linux/sys/mman_memfd_create.cc @@ -0,0 +1,36 @@ +// Copyright 2019 The Crashpad Authors. All rights reserved. +// +// 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 + +#include +#include +#include + +#include "util/misc/no_cfi_icall.h" + +#if defined(__GLIBC__) + +extern "C" { + +int memfd_create(const char* name, unsigned int flags) __THROW { + static const crashpad::NoCfiIcall next_memfd_create( + dlsym(RTLD_NEXT, "memfd_create")); + return next_memfd_create ? next_memfd_create(name, flags) + : syscall(SYS_memfd_create, name, flags); +} + +} // extern "C" + +#endif // __GLIBC__ diff --git a/shared/sentry/external/crashpad/compat/linux/sys/ptrace.h b/shared/sentry/external/crashpad/compat/linux/sys/ptrace.h new file mode 100644 index 000000000..f8be372cc --- /dev/null +++ b/shared/sentry/external/crashpad/compat/linux/sys/ptrace.h @@ -0,0 +1,52 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_LINUX_SYS_PTRACE_H_ +#define CRASHPAD_COMPAT_LINUX_SYS_PTRACE_H_ + +#include_next + +#include + +// https://sourceware.org/bugzilla/show_bug.cgi?id=22433 +#if !defined(PTRACE_GET_THREAD_AREA) && !defined(PT_GET_THREAD_AREA) && \ + defined(__GLIBC__) +#if defined(__i386__) || defined(__x86_64__) +static constexpr __ptrace_request PTRACE_GET_THREAD_AREA = + static_cast<__ptrace_request>(25); +#define PTRACE_GET_THREAD_AREA PTRACE_GET_THREAD_AREA +// https://bugs.chromium.org/p/chromium/issues/detail?id=873168 +#elif defined(__arm__) || (defined(__aarch64__) && __GLIBC_PREREQ(2,28)) +static constexpr __ptrace_request PTRACE_GET_THREAD_AREA = + static_cast<__ptrace_request>(22); +#define PTRACE_GET_THREAD_AREA PTRACE_GET_THREAD_AREA +#elif defined(__mips__) +static constexpr __ptrace_request PTRACE_GET_THREAD_AREA = + static_cast<__ptrace_request>(25); +#define PTRACE_GET_THREAD_AREA PTRACE_GET_THREAD_AREA +static constexpr __ptrace_request PTRACE_GET_THREAD_AREA_3264 = + static_cast<__ptrace_request>(0xc4); +#define PTRACE_GET_THREAD_AREA_3264 PTRACE_GET_THREAD_AREA_3264 +#endif +#endif // !PTRACE_GET_THREAD_AREA && !PT_GET_THREAD_AREA && defined(__GLIBC__) + +// https://sourceware.org/bugzilla/show_bug.cgi?id=22433 +#if !defined(PTRACE_GETVFPREGS) && !defined(PT_GETVFPREGS) && \ + defined(__GLIBC__) && (defined(__arm__) || defined(__aarch64__)) +static constexpr __ptrace_request PTRACE_GETVFPREGS = + static_cast<__ptrace_request>(27); +#define PTRACE_GETVFPREGS PTRACE_GETVFPREGS +#endif + +#endif // CRASHPAD_COMPAT_LINUX_SYS_PTRACE_H_ diff --git a/shared/sentry/external/crashpad/compat/linux/sys/user.h b/shared/sentry/external/crashpad/compat/linux/sys/user.h new file mode 100644 index 000000000..d698c351e --- /dev/null +++ b/shared/sentry/external/crashpad/compat/linux/sys/user.h @@ -0,0 +1,33 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_LINUX_SYS_USER_H_ +#define CRASHPAD_COMPAT_LINUX_SYS_USER_H_ + +#include_next + +#include + +// glibc for 64-bit ARM uses different names for these structs prior to 2.20. +// However, Debian's glibc 2.19-8 backported the change so it's not sufficient +// to only test the version. user_pt_regs and user_fpsimd_state are actually +// defined in so we use the include guard here. +#if defined(__aarch64__) && defined(__GLIBC__) +#if !__GLIBC_PREREQ(2, 20) && defined(__ASM_PTRACE_H) +using user_regs_struct = user_pt_regs; +using user_fpsimd_struct = user_fpsimd_state; +#endif +#endif + +#endif // CRASHPAD_COMPAT_LINUX_SYS_USER_H_ diff --git a/shared/sentry/external/crashpad/compat/mac/Availability.h b/shared/sentry/external/crashpad/compat/mac/Availability.h new file mode 100644 index 000000000..b8f8f2d6b --- /dev/null +++ b/shared/sentry/external/crashpad/compat/mac/Availability.h @@ -0,0 +1,28 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_MAC_AVAILABILITY_H_ +#define CRASHPAD_COMPAT_MAC_AVAILABILITY_H_ + +// Until the 10.15 SDK, the contents of was in-line in +// , but since then, it was broken out into its own header. +// This compat version of allows these macros to always appear +// to be provided by the new header, , even when an +// older SDK is in use. + +#include_next + +#include + +#endif // CRASHPAD_COMPAT_MAC_AVAILABILITY_H_ diff --git a/shared/sentry/external/crashpad/compat/mac/AvailabilityVersions.h b/shared/sentry/external/crashpad/compat/mac/AvailabilityVersions.h new file mode 100644 index 000000000..b9bb74ec7 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/mac/AvailabilityVersions.h @@ -0,0 +1,96 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_MAC_AVAILABILITYVERSIONS_H_ +#define CRASHPAD_COMPAT_MAC_AVAILABILITYVERSIONS_H_ + +#if __has_include_next() +#include_next +#endif + +// 10.7 SDK + +#ifndef __MAC_10_7 +#define __MAC_10_7 1070 +#endif + +// 10.8 SDK + +#ifndef __MAC_10_8 +#define __MAC_10_8 1080 +#endif + +// 10.9 SDK + +#ifndef __MAC_10_9 +#define __MAC_10_9 1090 +#endif + +// 10.10 SDK + +#ifndef __MAC_10_10 +#define __MAC_10_10 101000 +#endif + +// 10.11 SDK + +#ifndef __MAC_10_11 +#define __MAC_10_11 101100 +#endif + +// 10.12 SDK + +#ifndef __MAC_10_12 +#define __MAC_10_12 101200 +#endif + +// 10.13 SDK + +#ifndef __MAC_10_13 +#define __MAC_10_13 101300 +#endif + +#ifndef __MAC_10_13_4 +#define __MAC_10_13_4 101304 +#endif + +// 10.14 SDK + +#ifndef __MAC_10_14 +#define __MAC_10_14 101400 +#endif + +// 10.15 SDK + +#ifndef __MAC_10_15 +#define __MAC_10_15 101500 +#endif + +// 11.0 SDK + +#ifndef __MAC_10_16 +#define __MAC_10_16 101600 +#endif + +#ifndef __MAC_11_0 +#define __MAC_11_0 110000 +#endif + +// 12.0 SDK + +#ifndef __MAC_12_0 +#define __MAC_12_0 120000 +#endif + +#endif // CRASHPAD_COMPAT_MAC_AVAILABILITYVERSIONS_H_ diff --git a/shared/sentry/external/crashpad/compat/mac/kern/exc_resource.h b/shared/sentry/external/crashpad/compat/mac/kern/exc_resource.h new file mode 100644 index 000000000..ca8943d37 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/mac/kern/exc_resource.h @@ -0,0 +1,76 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_MAC_KERN_EXC_RESOURCE_H_ +#define CRASHPAD_COMPAT_MAC_KERN_EXC_RESOURCE_H_ + +#if __has_include_next() +#include_next +#endif + +// 10.9 SDK + +#ifndef EXC_RESOURCE_DECODE_RESOURCE_TYPE +#define EXC_RESOURCE_DECODE_RESOURCE_TYPE(code) (((code) >> 61) & 0x7ull) +#endif + +#ifndef EXC_RESOURCE_DECODE_FLAVOR +#define EXC_RESOURCE_DECODE_FLAVOR(code) (((code) >> 58) & 0x7ull) +#endif + +#ifndef RESOURCE_TYPE_CPU +#define RESOURCE_TYPE_CPU 1 +#endif + +#ifndef RESOURCE_TYPE_WAKEUPS +#define RESOURCE_TYPE_WAKEUPS 2 +#endif + +#ifndef RESOURCE_TYPE_MEMORY +#define RESOURCE_TYPE_MEMORY 3 +#endif + +#ifndef FLAVOR_CPU_MONITOR +#define FLAVOR_CPU_MONITOR 1 +#endif + +#ifndef FLAVOR_WAKEUPS_MONITOR +#define FLAVOR_WAKEUPS_MONITOR 1 +#endif + +#ifndef FLAVOR_HIGH_WATERMARK +#define FLAVOR_HIGH_WATERMARK 1 +#endif + +// 10.10 SDK + +#ifndef FLAVOR_CPU_MONITOR_FATAL +#define FLAVOR_CPU_MONITOR_FATAL 2 +#endif + +// 10.12 SDK + +#ifndef RESOURCE_TYPE_IO +#define RESOURCE_TYPE_IO 4 +#endif + +#ifndef FLAVOR_IO_PHYSICAL_WRITES +#define FLAVOR_IO_PHYSICAL_WRITES 1 +#endif + +#ifndef FLAVOR_IO_LOGICAL_WRITES +#define FLAVOR_IO_LOGICAL_WRITES 2 +#endif + +#endif // CRASHPAD_COMPAT_MAC_KERN_EXC_RESOURCE_H_ diff --git a/shared/sentry/external/crashpad/compat/mac/mach-o/loader.h b/shared/sentry/external/crashpad/compat/mac/mach-o/loader.h new file mode 100644 index 000000000..95a735747 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/mac/mach-o/loader.h @@ -0,0 +1,32 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_MAC_MACH_O_LOADER_H_ +#define CRASHPAD_COMPAT_MAC_MACH_O_LOADER_H_ + +#include_next + +// 10.7 SDK + +#ifndef S_THREAD_LOCAL_ZEROFILL +#define S_THREAD_LOCAL_ZEROFILL 0x12 +#endif + +// 10.8 SDK + +#ifndef LC_SOURCE_VERSION +#define LC_SOURCE_VERSION 0x2a +#endif + +#endif // CRASHPAD_COMPAT_MAC_MACH_O_LOADER_H_ diff --git a/shared/sentry/external/crashpad/compat/mac/mach/i386/thread_state.h b/shared/sentry/external/crashpad/compat/mac/mach/i386/thread_state.h new file mode 100644 index 000000000..de744d64d --- /dev/null +++ b/shared/sentry/external/crashpad/compat/mac/mach/i386/thread_state.h @@ -0,0 +1,28 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_MAC_MACH_I386_THREAD_STATE_H_ +#define CRASHPAD_COMPAT_MAC_MACH_I386_THREAD_STATE_H_ + +#include_next + +// 10.13 SDK +// +// This was defined as 244 in the 10.7 through 10.12 SDKs, and 144 previously. +#if I386_THREAD_STATE_MAX < 614 +#undef I386_THREAD_STATE_MAX +#define I386_THREAD_STATE_MAX (614) +#endif + +#endif // CRASHPAD_COMPAT_MAC_MACH_I386_THREAD_STATE_H_ diff --git a/shared/sentry/external/crashpad/compat/mac/mach/mach.h b/shared/sentry/external/crashpad/compat/mac/mach/mach.h new file mode 100644 index 000000000..55f5fdd2e --- /dev/null +++ b/shared/sentry/external/crashpad/compat/mac/mach/mach.h @@ -0,0 +1,120 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_MAC_MACH_MACH_H_ +#define CRASHPAD_COMPAT_MAC_MACH_MACH_H_ + +#include_next + +// + +// 10.8 SDK + +#ifndef EXC_RESOURCE +#define EXC_RESOURCE 11 +#endif + +#ifndef EXC_MASK_RESOURCE +#define EXC_MASK_RESOURCE (1 << EXC_RESOURCE) +#endif + +// 10.9 SDK + +#ifndef EXC_GUARD +#define EXC_GUARD 12 +#endif + +#ifndef EXC_MASK_GUARD +#define EXC_MASK_GUARD (1 << EXC_GUARD) +#endif + +// 10.11 SDK + +#ifndef EXC_CORPSE_NOTIFY +#define EXC_CORPSE_NOTIFY 13 +#endif + +#ifndef EXC_MASK_CORPSE_NOTIFY +#define EXC_MASK_CORPSE_NOTIFY (1 << EXC_CORPSE_NOTIFY) +#endif + +// Don’t expose EXC_MASK_ALL at all, because its definition varies with SDK, and +// older kernels will reject values that they don’t understand. Instead, use +// crashpad::ExcMaskAll(), which computes the correct value of EXC_MASK_ALL for +// the running system. +#undef EXC_MASK_ALL + +#if defined(__i386__) || defined(__x86_64__) + +// + +// 10.11 SDK + +#if EXC_TYPES_COUNT > 14 // Definition varies with SDK +#error Update this file for new exception types +#elif EXC_TYPES_COUNT != 14 +#undef EXC_TYPES_COUNT +#define EXC_TYPES_COUNT 14 +#endif + +// + +// 10.6 SDK +// +// Earlier versions of this SDK didn’t have AVX definitions. They didn’t appear +// until the version of the 10.6 SDK that shipped with Xcode 4.2, although +// versions of this SDK appeared with Xcode releases as early as Xcode 3.2. +// Similarly, the kernel didn’t handle AVX state until Mac OS X 10.6.8 +// (xnu-1504.15.3) and presumably the hardware-specific versions of Mac OS X +// 10.6.7 intended to run on processors with AVX. + +#ifndef x86_AVX_STATE32 +#define x86_AVX_STATE32 16 +#endif + +#ifndef x86_AVX_STATE64 +#define x86_AVX_STATE64 17 +#endif + +// 10.8 SDK + +#ifndef x86_AVX_STATE +#define x86_AVX_STATE 18 +#endif + +// 10.13 SDK + +#ifndef x86_AVX512_STATE32 +#define x86_AVX512_STATE32 19 +#endif + +#ifndef x86_AVX512_STATE64 +#define x86_AVX512_STATE64 20 +#endif + +#ifndef x86_AVX512_STATE +#define x86_AVX512_STATE 21 +#endif + +#endif // defined(__i386__) || defined(__x86_64__) + +// + +// 10.8 SDK + +#ifndef THREAD_STATE_FLAVOR_LIST_10_9 +#define THREAD_STATE_FLAVOR_LIST_10_9 129 +#endif + +#endif // CRASHPAD_COMPAT_MAC_MACH_MACH_H_ diff --git a/shared/sentry/external/crashpad/compat/mac/sys/resource.h b/shared/sentry/external/crashpad/compat/mac/sys/resource.h new file mode 100644 index 000000000..0697e169b --- /dev/null +++ b/shared/sentry/external/crashpad/compat/mac/sys/resource.h @@ -0,0 +1,26 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_MAC_SYS_RESOURCE_H_ +#define CRASHPAD_COMPAT_MAC_SYS_RESOURCE_H_ + +#include_next + +// 10.9 SDK + +#ifndef WAKEMON_MAKE_FATAL +#define WAKEMON_MAKE_FATAL 0x10 +#endif + +#endif // CRASHPAD_COMPAT_MAC_SYS_RESOURCE_H_ diff --git a/shared/sentry/external/crashpad/compat/mingw/dbghelp.h b/shared/sentry/external/crashpad/compat/mingw/dbghelp.h new file mode 100644 index 000000000..b49219848 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/mingw/dbghelp.h @@ -0,0 +1,334 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_MINGW_DBGHELP_H_ +#define CRASHPAD_COMPAT_MINGW_DBGHELP_H_ + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wgnu-include-next" + +#include_next +#include +#include +#include +#include + +//! \file + +//! \brief Information about XSAVE-managed state stored within CPU-specific +//! context structures. +struct __attribute__((packed, aligned(4))) XSTATE_CONFIG_FEATURE_MSC_INFO { + //! \brief The size of this structure, in bytes. This value is + //! `sizeof(XSTATE_CONFIG_FEATURE_MSC_INFO)`. + uint32_t SizeOfInfo; + + //! \brief The size of a CPU-specific context structure carrying all XSAVE + //! state components described by this structure. + //! + //! Equivalent to the value returned by `InitializeContext()` in \a + //! ContextLength. + uint32_t ContextSize; + + //! \brief The XSAVE state-component bitmap, XSAVE_BV. + //! + //! See Intel Software Developer’s Manual, Volume 1: Basic Architecture + //! (253665-060), 13.4.2 “XSAVE Headerâ€. + uint64_t EnabledFeatures; + + //! \brief The location of each state component within a CPU-specific context + //! structure. + //! + //! This array is indexed by bit position numbers used in #EnabledFeatures. + XSTATE_FEATURE Features[MAXIMUM_XSTATE_FEATURES]; +}; + +//! \anchor MINIDUMP_MISCx +//! \name MINIDUMP_MISC* +//! +//! \brief Field validity flag values for MINIDUMP_MISC_INFO::Flags1. +//! \{ + +//! \brief MINIDUMP_MISC_INFO::ProcessId is valid. +#define MINIDUMP_MISC1_PROCESS_ID 0x00000001 + +//! \brief The time-related fields in MINIDUMP_MISC_INFO are valid. +//! +//! The following fields are valid: +//! - MINIDUMP_MISC_INFO::ProcessCreateTime +//! - MINIDUMP_MISC_INFO::ProcessUserTime +//! - MINIDUMP_MISC_INFO::ProcessKernelTime +#define MINIDUMP_MISC1_PROCESS_TIMES 0x00000002 + +//! \brief The CPU-related fields in MINIDUMP_MISC_INFO_2 are valid. +//! +//! The following fields are valid: +//! - MINIDUMP_MISC_INFO_2::ProcessorMaxMhz +//! - MINIDUMP_MISC_INFO_2::ProcessorCurrentMhz +//! - MINIDUMP_MISC_INFO_2::ProcessorMhzLimit +//! - MINIDUMP_MISC_INFO_2::ProcessorMaxIdleState +//! - MINIDUMP_MISC_INFO_2::ProcessorCurrentIdleState +//! +//! \note This macro should likely have been named +//! MINIDUMP_MISC2_PROCESSOR_POWER_INFO. +#define MINIDUMP_MISC1_PROCESSOR_POWER_INFO 0x00000004 + +//! \brief MINIDUMP_MISC_INFO_3::ProcessIntegrityLevel is valid. +#define MINIDUMP_MISC3_PROCESS_INTEGRITY 0x00000010 + +//! \brief MINIDUMP_MISC_INFO_3::ProcessExecuteFlags is valid. +#define MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS 0x00000020 + +//! \brief The time zone-related fields in MINIDUMP_MISC_INFO_3 are valid. +//! +//! The following fields are valid: +//! - MINIDUMP_MISC_INFO_3::TimeZoneId +//! - MINIDUMP_MISC_INFO_3::TimeZone +#define MINIDUMP_MISC3_TIMEZONE 0x00000040 + +//! \brief MINIDUMP_MISC_INFO_3::ProtectedProcess is valid. +#define MINIDUMP_MISC3_PROTECTED_PROCESS 0x00000080 + +//! \brief The build string-related fields in MINIDUMP_MISC_INFO_4 are valid. +//! +//! The following fields are valid: +//! - MINIDUMP_MISC_INFO_4::BuildString +//! - MINIDUMP_MISC_INFO_4::DbgBldStr +#define MINIDUMP_MISC4_BUILDSTRING 0x00000100 + +//! \brief MINIDUMP_MISC_INFO_5::ProcessCookie is valid. +#define MINIDUMP_MISC5_PROCESS_COOKIE 0x00000200 + +//! \} + +#ifdef __cplusplus + +//! \brief Contains the state of an individual system handle at the time the +//! snapshot was taken. This structure is Windows-specific. +//! +//! \sa MINIDUMP_HANDLE_DESCRIPTOR +struct __attribute__((packed, aligned(4))) MINIDUMP_HANDLE_DESCRIPTOR_2 + : public MINIDUMP_HANDLE_DESCRIPTOR { + //! \brief An RVA to a MINIDUMP_HANDLE_OBJECT_INFORMATION structure that + //! specifies object-specific information. This member can be zero if + //! there is no extra information. + RVA ObjectInfoRva; + + //! \brief Must be zero. + uint32_t Reserved0; +}; + +//! \brief Information about the process that the minidump file contains a +//! snapshot of, as well as the system that hosted that process. +//! +//! This structure variant is used on Windows 7 (NT 6.1) and later. +//! +//! \sa \ref MINIDUMP_MISCx "MINIDUMP_MISC*" +//! \sa MINIDUMP_MISC_INFO +//! \sa MINIDUMP_MISC_INFO_2 +//! \sa MINIDUMP_MISC_INFO_4 +//! \sa MINIDUMP_MISC_INFO_5 +//! \sa MINIDUMP_MISC_INFO_N +struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_3 + : public MINIDUMP_MISC_INFO_2 { + //! \brief The process’ integrity level. + //! + //! Windows typically uses `SECURITY_MANDATORY_MEDIUM_RID` (0x2000) for + //! processes belonging to normal authenticated users and + //! `SECURITY_MANDATORY_HIGH_RID` (0x3000) for elevated processes. + //! + //! This field is Windows-specific, and has no meaning on other operating + //! systems. + uint32_t ProcessIntegrityLevel; + + //! \brief The process’ execute flags. + //! + //! On Windows, this appears to be returned by `NtQueryInformationProcess()` + //! with an argument of `ProcessExecuteFlags` (34). + //! + //! This field is Windows-specific, and has no meaning on other operating + //! systems. + uint32_t ProcessExecuteFlags; + + //! \brief Whether the process is protected. + //! + //! This field is Windows-specific, and has no meaning on other operating + //! systems. + uint32_t ProtectedProcess; + + //! \brief Whether daylight saving time was being observed in the system’s + //! location at the time of the snapshot. + //! + //! This field can contain the following values: + //! - `0` if the location does not observe daylight saving time at all. The + //! TIME_ZONE_INFORMATION::StandardName field of #TimeZoneId contains the + //! time zone name. + //! - `1` if the location observes daylight saving time, but standard time + //! was in effect at the time of the snapshot. The + //! TIME_ZONE_INFORMATION::StandardName field of #TimeZoneId contains the + //! time zone name. + //! - `2` if the location observes daylight saving time, and it was in effect + //! at the time of the snapshot. The TIME_ZONE_INFORMATION::DaylightName + //! field of #TimeZoneId contains the time zone name. + //! + //! \sa #TimeZone + uint32_t TimeZoneId; + + //! \brief Information about the time zone at the system’s location. + //! + //! \sa #TimeZoneId + TIME_ZONE_INFORMATION TimeZone; +}; + +//! \brief Information about the process that the minidump file contains a +//! snapshot of, as well as the system that hosted that process. +//! +//! This structure variant is used on Windows 8 (NT 6.2) and later. +//! +//! \sa \ref MINIDUMP_MISCx "MINIDUMP_MISC*" +//! \sa MINIDUMP_MISC_INFO +//! \sa MINIDUMP_MISC_INFO_2 +//! \sa MINIDUMP_MISC_INFO_3 +//! \sa MINIDUMP_MISC_INFO_5 +//! \sa MINIDUMP_MISC_INFO_N +struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_4 + : public MINIDUMP_MISC_INFO_3 { + //! \brief The operating system’s “build stringâ€, a string identifying a + //! specific build of the operating system. + //! + //! This string is UTF-16-encoded and terminated by a UTF-16 `NUL` code unit. + //! + //! On Windows 8.1 (NT 6.3), this is “6.3.9600.17031 + //! (winblue_gdr.140221-1952)â€. + wchar_t BuildString[260]; + + //! \brief The minidump producer’s “build stringâ€, a string identifying the + //! module that produced a minidump file. + //! + //! This string is UTF-16-encoded and terminated by a UTF-16 `NUL` code unit. + //! + //! On Windows 8.1 (NT 6.3), this may be “dbghelp.i386,6.3.9600.16520†or + //! “dbghelp.amd64,6.3.9600.16520†depending on CPU architecture. + wchar_t DbgBldStr[40]; +}; + +//! \brief Information about the process that the minidump file contains a +//! snapshot of, as well as the system that hosted that process. +//! +//! This structure variant is used on Windows 10 and later. +//! +//! \sa \ref MINIDUMP_MISCx "MINIDUMP_MISC*" +//! \sa MINIDUMP_MISC_INFO +//! \sa MINIDUMP_MISC_INFO_2 +//! \sa MINIDUMP_MISC_INFO_3 +//! \sa MINIDUMP_MISC_INFO_4 +//! \sa MINIDUMP_MISC_INFO_N +struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_5 + : public MINIDUMP_MISC_INFO_4 { + //! \brief Information about XSAVE-managed state stored within CPU-specific + //! context structures. + //! + //! This information can be used to locate state components within + //! CPU-specific context structures. + XSTATE_CONFIG_FEATURE_MSC_INFO XStateData; + + uint32_t ProcessCookie; +}; + +//! \brief The latest known version of the MINIDUMP_MISC_INFO structure. +typedef MINIDUMP_MISC_INFO_5 MINIDUMP_MISC_INFO_N; + +#else + +struct MINIDUMP_HANDLE_DESCRIPTOR_2 { + ULONG64 Handle; + RVA TypeNameRva; + RVA ObjectNameRva; + ULONG32 Attributes; + ULONG32 GrantedAccess; + ULONG32 HandleCount; + ULONG32 PointerCount; + RVA ObjectInfoRva; + uint32_t Reserved0; +}; + +struct MINIDUMP_MISC_INFO_3 { + ULONG32 SizeOfInfo; + ULONG32 Flags1; + ULONG32 ProcessId; + ULONG32 ProcessCreateTime; + ULONG32 ProcessUserTime; + ULONG32 ProcessKernelTime; + ULONG32 ProcessorMaxMhz; + ULONG32 ProcessorCurrentMhz; + ULONG32 ProcessorMhzLimit; + ULONG32 ProcessorMaxIdleState; + ULONG32 ProcessorCurrentIdleState; + uint32_t ProcessIntegrityLevel; + uint32_t ProcessExecuteFlags; + uint32_t ProtectedProcess; + uint32_t TimeZoneId; + TIME_ZONE_INFORMATION TimeZone; +}; + +struct MINIDUMP_MISC_INFO_4 { + ULONG32 SizeOfInfo; + ULONG32 Flags1; + ULONG32 ProcessId; + ULONG32 ProcessCreateTime; + ULONG32 ProcessUserTime; + ULONG32 ProcessKernelTime; + ULONG32 ProcessorMaxMhz; + ULONG32 ProcessorCurrentMhz; + ULONG32 ProcessorMhzLimit; + ULONG32 ProcessorMaxIdleState; + ULONG32 ProcessorCurrentIdleState; + uint32_t ProcessIntegrityLevel; + uint32_t ProcessExecuteFlags; + uint32_t ProtectedProcess; + uint32_t TimeZoneId; + TIME_ZONE_INFORMATION TimeZone; + wchar_t BuildString[260]; + wchar_t DbgBldStr[40]; +}; + +struct MINIDUMP_MISC_INFO_5 { + ULONG32 SizeOfInfo; + ULONG32 Flags1; + ULONG32 ProcessId; + ULONG32 ProcessCreateTime; + ULONG32 ProcessUserTime; + ULONG32 ProcessKernelTime; + ULONG32 ProcessorMaxMhz; + ULONG32 ProcessorCurrentMhz; + ULONG32 ProcessorMhzLimit; + ULONG32 ProcessorMaxIdleState; + ULONG32 ProcessorCurrentIdleState; + uint32_t ProcessIntegrityLevel; + uint32_t ProcessExecuteFlags; + uint32_t ProtectedProcess; + uint32_t TimeZoneId; + TIME_ZONE_INFORMATION TimeZone; + wchar_t BuildString[260]; + wchar_t DbgBldStr[40]; + struct XSTATE_CONFIG_FEATURE_MSC_INFO XStateData; + uint32_t ProcessCookie; +}; + +typedef struct MINIDUMP_MISC_INFO_5 MINIDUMP_MISC_INFO_N; + +#endif + +#pragma clang diagnostic pop + +#endif // CRASHPAD_COMPAT_MINGW_DBGHELP_H_ diff --git a/shared/sentry/external/crashpad/compat/mingw/winnt.h b/shared/sentry/external/crashpad/compat/mingw/winnt.h new file mode 100644 index 000000000..268af1b3b --- /dev/null +++ b/shared/sentry/external/crashpad/compat/mingw/winnt.h @@ -0,0 +1,60 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_MINGW_WINNT_H_ +#define CRASHPAD_COMPAT_MINGW_WINNT_H_ + +#include_next +#include + +// https://msdn.microsoft.com/library/aa373184.aspx: "Note that this structure +// definition was accidentally omitted from WinNT.h." +struct PROCESSOR_POWER_INFORMATION { + ULONG Number; + ULONG MaxMhz; + ULONG CurrentMhz; + ULONG MhzLimit; + ULONG MaxIdleState; + ULONG CurrentIdleState; +}; + +// 10.0.10240.0 SDK + +#ifndef PROCESSOR_ARCHITECTURE_ARM64 +#define PROCESSOR_ARCHITECTURE_ARM64 12 +#endif + +#ifndef PF_ARM_V8_INSTRUCTIONS_AVAILABLE +#define PF_ARM_V8_INSTRUCTIONS_AVAILABLE 29 +#endif + +#ifndef PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE +#define PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE 30 +#endif + +#ifndef PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE +#define PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE 31 +#endif + +#ifndef PF_RDTSCP_INSTRUCTION_AVAILABLE +#define PF_RDTSCP_INSTRUCTION_AVAILABLE 32 +#endif + +// 10.0.14393.0 SDK + +#ifndef PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64 +#define PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64 13 +#endif + +#endif // CRASHPAD_COMPAT_MINGW_WINNT_H_ diff --git a/shared/sentry/external/crashpad/compat/non_elf/elf.h b/shared/sentry/external/crashpad/compat/non_elf/elf.h new file mode 100644 index 000000000..1245f16dd --- /dev/null +++ b/shared/sentry/external/crashpad/compat/non_elf/elf.h @@ -0,0 +1,20 @@ +// Copyright 2019 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_NON_ELF_ELF_H_ +#define CRASHPAD_COMPAT_NON_ELF_ELF_H_ + +#include "third_party/glibc/elf/elf.h" + +#endif // CRASHPAD_COMPAT_NON_ELF_ELF_H_ diff --git a/shared/sentry/external/crashpad/compat/non_mac/mach-o/loader.h b/shared/sentry/external/crashpad/compat/non_mac/mach-o/loader.h new file mode 100644 index 000000000..98a8b5fa4 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/non_mac/mach-o/loader.h @@ -0,0 +1,20 @@ +// Copyright 2019 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_NON_MAC_MACH_O_LOADER_H_ +#define CRASHPAD_COMPAT_NON_MAC_MACH_O_LOADER_H_ + +#include "third_party/xnu/EXTERNAL_HEADERS/mach-o/loader.h" + +#endif // CRASHPAD_COMPAT_NON_MAC_MACH_O_LOADER_H_ diff --git a/shared/sentry/external/crashpad/compat/non_mac/mach/mach.h b/shared/sentry/external/crashpad/compat/non_mac/mach/mach.h new file mode 100644 index 000000000..f33bb10f3 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/non_mac/mach/mach.h @@ -0,0 +1,44 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_NON_MAC_MACH_MACH_H_ +#define CRASHPAD_COMPAT_NON_MAC_MACH_MACH_H_ + +//! \file + +// + +//! \anchor EXC_x +//! \name EXC_* +//! +//! \brief Mach exception type definitions. +//! \{ +#define EXC_BAD_ACCESS 1 +#define EXC_BAD_INSTRUCTION 2 +#define EXC_ARITHMETIC 3 +#define EXC_EMULATION 4 +#define EXC_SOFTWARE 5 +#define EXC_BREAKPOINT 6 +#define EXC_SYSCALL 7 +#define EXC_MACH_SYSCALL 8 +#define EXC_RPC_ALERT 9 +#define EXC_CRASH 10 +#define EXC_RESOURCE 11 +#define EXC_GUARD 12 +#define EXC_CORPSE_NOTIFY 13 + +#define EXC_TYPES_COUNT 14 +//! \} + +#endif // CRASHPAD_COMPAT_NON_MAC_MACH_MACH_H_ diff --git a/shared/sentry/external/crashpad/compat/non_mac/mach/machine.h b/shared/sentry/external/crashpad/compat/non_mac/mach/machine.h new file mode 100644 index 000000000..54d4742b9 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/non_mac/mach/machine.h @@ -0,0 +1,21 @@ +// Copyright 2019 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_NON_MAC_MACH_MACHINE_H_ +#define CRASHPAD_COMPAT_NON_MAC_MACH_MACHINE_H_ + +typedef int cpu_type_t; +typedef int cpu_subtype_t; + +#endif // CRASHPAD_COMPAT_NON_MAC_MACH_MACHINE_H_ diff --git a/shared/sentry/external/crashpad/compat/non_mac/mach/vm_prot.h b/shared/sentry/external/crashpad/compat/non_mac/mach/vm_prot.h new file mode 100644 index 000000000..12001791f --- /dev/null +++ b/shared/sentry/external/crashpad/compat/non_mac/mach/vm_prot.h @@ -0,0 +1,20 @@ +// Copyright 2019 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_NON_MAC_MACH_VM_PROT_H_ +#define CRASHPAD_COMPAT_NON_MAC_MACH_VM_PROT_H_ + +typedef int vm_prot_t; + +#endif // CRASHPAD_COMPAT_NON_MAC_MACH_VM_PROT_H_ diff --git a/shared/sentry/external/crashpad/compat/non_win/dbghelp.h b/shared/sentry/external/crashpad/compat/non_win/dbghelp.h new file mode 100644 index 000000000..0d9852bb6 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/non_win/dbghelp.h @@ -0,0 +1,1105 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_NON_WIN_DBGHELP_H_ +#define CRASHPAD_COMPAT_NON_WIN_DBGHELP_H_ + +#include + +#include "compat/non_win/timezoneapi.h" +#include "compat/non_win/verrsrc.h" +#include "compat/non_win/winnt.h" + +//! \file + +//! \brief The magic number for a minidump file, stored in +//! MINIDUMP_HEADER::Signature. +//! +//! A hex dump of a little-endian minidump file will begin with the string +//! “MDMPâ€. +#define MINIDUMP_SIGNATURE ('PMDM') // 0x4d444d50 + +//! \brief The version of a minidump file, stored in MINIDUMP_HEADER::Version. +#define MINIDUMP_VERSION (42899) + +//! \brief An offset within a minidump file, relative to the start of its +//! MINIDUMP_HEADER. +//! +//! RVA stands for “relative virtual addressâ€. Within a minidump file, RVAs are +//! used as pointers to link structures together. +//! +//! \sa MINIDUMP_LOCATION_DESCRIPTOR +typedef uint32_t RVA; + +//! \brief A pointer to a structure or union within a minidump file. +struct __attribute__((packed, aligned(4))) MINIDUMP_LOCATION_DESCRIPTOR { + //! \brief The size of the referenced structure or union, in bytes. + uint32_t DataSize; + + //! \brief The relative virtual address of the structure or union within the + //! minidump file. + RVA Rva; +}; + +//! \brief A pointer to a snapshot of a region of memory contained within a +//! minidump file. +//! +//! \sa MINIDUMP_MEMORY_LIST +struct __attribute__((packed, aligned(4))) MINIDUMP_MEMORY_DESCRIPTOR { + //! \brief The base address of the memory region in the address space of the + //! process that the minidump file contains a snapshot of. + uint64_t StartOfMemoryRange; + + //! \brief The contents of the memory region. + MINIDUMP_LOCATION_DESCRIPTOR Memory; +}; + +//! \brief The top-level structure identifying a minidump file. +//! +//! This structure contains a pointer to the stream directory, a second-level +//! structure which in turn contains pointers to third-level structures +//! (“streamsâ€) containing the data within the minidump file. This structure +//! also contains the minidump file’s magic numbers, and other bookkeeping data. +//! +//! This structure must be present at the beginning of a minidump file (at ::RVA +//! 0). +struct __attribute__((packed, aligned(4))) MINIDUMP_HEADER { + //! \brief The minidump file format magic number, ::MINIDUMP_SIGNATURE. + uint32_t Signature; + + //! \brief The minidump file format version number, ::MINIDUMP_VERSION. + uint32_t Version; + + //! \brief The number of MINIDUMP_DIRECTORY elements present in the directory + //! referenced by #StreamDirectoryRva. + uint32_t NumberOfStreams; + + //! \brief A pointer to an array of MINIDUMP_DIRECTORY structures that + //! identify all of the streams within this minidump file. The array has + //! #NumberOfStreams elements present. + RVA StreamDirectoryRva; + + //! \brief The minidump file’s checksum. This can be `0`, and in practice, `0` + //! is the only value that has ever been seen in this field. + uint32_t CheckSum; + + //! \brief The time that the minidump file was generated, in `time_t` format, + //! the number of seconds since the POSIX epoch. + uint32_t TimeDateStamp; + + //! \brief A bitfield containing members of ::MINIDUMP_TYPE, describing the + //! types of data carried within this minidump file. + uint64_t Flags; +}; + +//! \brief A pointer to a stream within a minidump file. +//! +//! Each stream present in a minidump file will have a corresponding +//! MINIDUMP_DIRECTORY entry in the stream directory referenced by +//! MINIDUMP_HEADER::StreamDirectoryRva. +struct __attribute__((packed, aligned(4))) MINIDUMP_DIRECTORY { + //! \brief The type of stream referenced, a value of ::MINIDUMP_STREAM_TYPE. + uint32_t StreamType; + + //! \brief A pointer to the stream data within the minidump file. + MINIDUMP_LOCATION_DESCRIPTOR Location; +}; + +//! \brief A variable-length UTF-16-encoded string carried within a minidump +//! file. +//! +//! The UTF-16 string is stored as UTF-16LE or UTF-16BE according to the byte +//! ordering of the minidump file itself. +//! +//! \sa crashpad::MinidumpUTF8String +struct __attribute__((packed, aligned(4))) MINIDUMP_STRING { + //! \brief The length of the #Buffer field in bytes, not including the `NUL` + //! terminator. + //! + //! \note This field is interpreted as a byte count, not a count of UTF-16 + //! code units or Unicode code points. + uint32_t Length; + + //! \brief The string, encoded in UTF-16, and terminated with a UTF-16 `NUL` + //! code unit (two `NUL` bytes). + char16_t Buffer[0]; +}; + +//! \brief Minidump stream type values for MINIDUMP_DIRECTORY::StreamType. Each +//! stream structure has a corresponding stream type value to identify it. +//! +//! \sa crashpad::MinidumpStreamType +enum MINIDUMP_STREAM_TYPE { + //! \brief The stream type for MINIDUMP_THREAD_LIST. + ThreadListStream = 3, + + //! \brief The stream type for MINIDUMP_MODULE_LIST. + ModuleListStream = 4, + + //! \brief The stream type for MINIDUMP_MEMORY_LIST. + MemoryListStream = 5, + + //! \brief The stream type for MINIDUMP_EXCEPTION_STREAM. + ExceptionStream = 6, + + //! \brief The stream type for MINIDUMP_SYSTEM_INFO. + SystemInfoStream = 7, + + //! \brief The stream contains information about active `HANDLE`s. + HandleDataStream = 12, + + //! \brief The stream type for MINIDUMP_UNLOADED_MODULE_LIST. + UnloadedModuleListStream = 14, + + //! \brief The stream type for MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2, + //! MINIDUMP_MISC_INFO_3, MINIDUMP_MISC_INFO_4, and MINIDUMP_MISC_INFO_5. + //! + //! More recent versions of this stream are supersets of earlier versions. + //! + //! The exact version of the stream that is present is implied by the stream’s + //! size. Furthermore, this stream contains a field, + //! MINIDUMP_MISC_INFO::Flags1, that indicates which data is present and + //! valid. + MiscInfoStream = 15, + + //! \brief The stream type for MINIDUMP_MEMORY_INFO_LIST. + MemoryInfoListStream = 16, + + //! \brief Values greater than this value will not be used by the system + //! and can be used for custom user data streams. + LastReservedStream = 0xffff, +}; + +//! \brief Information about the CPU (or CPUs) that ran the process that the +//! minidump file contains a snapshot of. +//! +//! This union only appears as MINIDUMP_SYSTEM_INFO::Cpu. Its interpretation is +//! controlled by MINIDUMP_SYSTEM_INFO::ProcessorArchitecture. +union __attribute__((packed, aligned(4))) CPU_INFORMATION { + //! \brief Information about 32-bit x86 CPUs, or x86_64 CPUs when running + //! 32-bit x86 processes. + struct __attribute__((packed, aligned(4))) { + //! \brief The CPU’s vendor identification string as encoded in `cpuid 0` + //! `ebx`, `edx`, and `ecx`, represented as it appears in these + //! registers. + //! + //! For Intel CPUs, `[0]` will encode “Genuâ€, `[1]` will encode “ineIâ€, and + //! `[2]` will encode “ntelâ€, for a vendor ID string “GenuineIntelâ€. + //! + //! \note The Windows documentation incorrectly states that these fields are + //! to be interpreted as `cpuid 0` `eax`, `ebx`, and `ecx`. + uint32_t VendorId[3]; + + //! \brief Family, model, and stepping ID values as encoded in `cpuid 1` + //! `eax`. + uint32_t VersionInformation; + + //! \brief A bitfield containing supported CPU capabilities as encoded in + //! `cpuid 1` `edx`. + uint32_t FeatureInformation; + + //! \brief A bitfield containing supported CPU capabalities as encoded in + //! `cpuid 0x80000001` `edx`. + //! + //! This field is only valid if #VendorId identifies the CPU vendor as + //! “AuthenticAMD†or "HygonGenuine". + uint32_t AMDExtendedCpuFeatures; + } X86CpuInfo; + + //! \brief Information about non-x86 CPUs, and x86_64 CPUs when not running + //! 32-bit x86 processes. + struct __attribute__((packed, aligned(4))) { + //! \brief Bitfields containing supported CPU capabilities as identified by + //! bits corresponding to \ref PF_x "PF_*" values passed to + //! `IsProcessorFeaturePresent()`. + uint64_t ProcessorFeatures[2]; + } OtherCpuInfo; +}; + +//! \brief Information about the system that hosted the process that the +//! minidump file contains a snapshot of. +struct __attribute__((packed, aligned(4))) MINIDUMP_SYSTEM_INFO { + // The next 4 fields are from the SYSTEM_INFO structure returned by + // GetSystemInfo(). + + //! \brief The system’s CPU architecture. This may be a \ref + //! PROCESSOR_ARCHITECTURE_x "PROCESSOR_ARCHITECTURE_*" value, or a member + //! of crashpad::MinidumpCPUArchitecture. + //! + //! In some cases, a system may be able to run processes of multiple specific + //! architecture types. For example, systems based on 64-bit architectures + //! such as x86_64 are often able to run 32-bit code of another architecture + //! in the same family, such as 32-bit x86. On these systems, this field will + //! identify the architecture of the process that the minidump file contains a + //! snapshot of. + uint16_t ProcessorArchitecture; + + //! \brief General CPU version information. + //! + //! The precise interpretation of this field is specific to each CPU + //! architecture. For x86-family CPUs (including x86_64 and 32-bit x86), this + //! field contains the CPU family ID value from `cpuid 1` `eax`, adjusted to + //! take the extended family ID into account. + uint16_t ProcessorLevel; + + //! \brief Specific CPU version information. + //! + //! The precise interpretation of this field is specific to each CPU + //! architecture. For x86-family CPUs (including x86_64 and 32-bit x86), this + //! field contains values obtained from `cpuid 1` `eax`: the high byte + //! contains the CPU model ID value adjusted to take the extended model ID + //! into account, and the low byte contains the CPU stepping ID value. + uint16_t ProcessorRevision; + + //! \brief The total number of CPUs present in the system. + uint8_t NumberOfProcessors; + + // The next 7 fields are from the OSVERSIONINFOEX structure returned by + // GetVersionEx(). + + //! \brief The system’s operating system type, which distinguishes between + //! “desktop†or “workstation†systems and “server†systems. This may be a + //! \ref VER_NT_x "VER_NT_*" value, or a member of + //! crashpad::MinidumpOSType. + uint8_t ProductType; + + //! \brief The system’s operating system version number’s first (major) + //! component. + //! + //! - For Windows 7 (NT 6.1) SP1, version 6.1.7601, this would be `6`. + //! - For macOS 10.12.1, this would be `10`. + uint32_t MajorVersion; + + //! \brief The system’s operating system version number’s second (minor) + //! component. + //! + //! - For Windows 7 (NT 6.1) SP1, version 6.1.7601, this would be `1`. + //! - For macOS 10.12.1, this would be `12`. + uint32_t MinorVersion; + + //! \brief The system’s operating system version number’s third (build or + //! patch) component. + //! + //! - For Windows 7 (NT 6.1) SP1, version 6.1.7601, this would be `7601`. + //! - For macOS 10.12.1, this would be `1`. + uint32_t BuildNumber; + + //! \brief The system’s operating system family. This may be a \ref + //! VER_PLATFORM_x "VER_PLATFORM_*" value, or a member of + //! crashpad::MinidumpOS. + uint32_t PlatformId; + + //! \brief ::RVA of a MINIDUMP_STRING containing operating system-specific + //! version information. + //! + //! This field further identifies an operating system version beyond its + //! version number fields. Historically, “CSD†stands for “corrective service + //! diskette.†+ //! + //! - On Windows, this is the name of the installed operating system service + //! pack, such as “Service Pack 1â€. If no service pack is installed, this + //! field references an empty string. + //! - On macOS, this is the operating system build number from `sw_vers + //! -buildVersion`. For macOS 10.12.1 on most hardware types, this would + //! be `16B2657`. + //! - On Linux and other Unix-like systems, this is the kernel version from + //! `uname -srvm`, possibly with additional information appended. On + //! Android, the `ro.build.fingerprint` system property is appended. + RVA CSDVersionRva; + + //! \brief A bitfield identifying products installed on the system. This is + //! composed of \ref VER_SUITE_x "VER_SUITE_*" values. + //! + //! This field is Windows-specific, and has no meaning on other operating + //! systems. + uint16_t SuiteMask; + + uint16_t Reserved2; + + //! \brief Information about the system’s CPUs. + //! + //! This field is a union. Which of its members should be expressed is + //! controlled by the #ProcessorArchitecture field. If it is set to + //! crashpad::kMinidumpCPUArchitectureX86, the CPU_INFORMATION::X86CpuInfo + //! field is expressed. Otherwise, the CPU_INFORMATION::OtherCpuInfo field is + //! expressed. + //! + //! \note Older Breakpad implementations produce minidump files that express + //! CPU_INFORMATION::X86CpuInfo when #ProcessorArchitecture is set to + //! crashpad::kMinidumpCPUArchitectureAMD64. Minidump files produced by + //! `dbghelp.dll` on Windows express CPU_INFORMATION::OtherCpuInfo in this + //! case. + CPU_INFORMATION Cpu; +}; + +//! \brief Information about a specific thread within the process. +//! +//! \sa MINIDUMP_THREAD_LIST +struct __attribute__((packed, aligned(4))) MINIDUMP_THREAD { + //! \brief The thread’s ID. This may be referenced by + //! MINIDUMP_EXCEPTION_STREAM::ThreadId. + uint32_t ThreadId; + + //! \brief The thread’s suspend count. + //! + //! This field will be `0` if the thread is schedulable (not suspended). + uint32_t SuspendCount; + + //! \brief The thread’s priority class. + //! + //! On Windows, this is a `*_PRIORITY_CLASS` value. `NORMAL_PRIORITY_CLASS` + //! has value `0x20`; higher priority classes have higher values. + uint32_t PriorityClass; + + //! \brief The thread’s priority level. + //! + //! On Windows, this is a `THREAD_PRIORITY_*` value. `THREAD_PRIORITY_NORMAL` + //! has value `0`; higher priorities have higher values, and lower priorities + //! have lower (negative) values. + uint32_t Priority; + + //! \brief The address of the thread’s thread environment block in the address + //! space of the process that the minidump file contains a snapshot of. + //! + //! The thread environment block contains thread-local data. + //! + //! A MINIDUMP_MEMORY_DESCRIPTOR may be present in the MINIDUMP_MEMORY_LIST + //! stream containing the thread-local data pointed to by this field. + uint64_t Teb; + + //! \brief A snapshot of the thread’s stack. + //! + //! A MINIDUMP_MEMORY_DESCRIPTOR may be present in the MINIDUMP_MEMORY_LIST + //! stream containing a pointer to the same memory range referenced by this + //! field. + MINIDUMP_MEMORY_DESCRIPTOR Stack; + + //! \brief A pointer to a CPU-specific CONTEXT structure containing the + //! thread’s context at the time the snapshot was taken. + //! + //! If the minidump file was generated as a result of an exception taken on + //! this thread, this field may identify a different context than the + //! exception context. For these minidump files, a MINIDUMP_EXCEPTION_STREAM + //! stream will be present, and the context contained within that stream will + //! be the exception context. + //! + //! The interpretation of the context structure is dependent on the CPU + //! architecture identified by MINIDUMP_SYSTEM_INFO::ProcessorArchitecture. + //! For crashpad::kMinidumpCPUArchitectureX86, this will be + //! crashpad::MinidumpContextX86. For crashpad::kMinidumpCPUArchitectureAMD64, + //! this will be crashpad::MinidumpContextAMD64. + MINIDUMP_LOCATION_DESCRIPTOR ThreadContext; +}; + +//! \brief Information about all threads within the process. +struct __attribute__((packed, aligned(4))) MINIDUMP_THREAD_LIST { + //! \brief The number of threads present in the #Threads array. + uint32_t NumberOfThreads; + + //! \brief Structures identifying each thread within the process. + MINIDUMP_THREAD Threads[0]; +}; + +//! \brief Information about an exception that occurred in the process. +struct __attribute__((packed, aligned(4))) MINIDUMP_EXCEPTION { + //! \brief The top-level exception code identifying the exception, in + //! operating system-specific values. + //! + //! For macOS minidumps, this will be an \ref EXC_x "EXC_*" exception type, + //! such as `EXC_BAD_ACCESS`. `EXC_CRASH` will not appear here for exceptions + //! processed as `EXC_CRASH` when generated from another preceding exception: + //! the original exception code will appear instead. The exception type as it + //! was received will appear at index 0 of #ExceptionInformation. + //! + //! For Windows minidumps, this will be an `EXCEPTION_*` exception type, such + //! as `EXCEPTION_ACCESS_VIOLATION`. + //! + //! \note This field is named ExceptionCode, but what is known as the + //! “exception code†on macOS/Mach is actually stored in the + //! #ExceptionFlags field of a minidump file. + //! + //! \todo Document the possible values by OS. There may be OS-specific enums + //! in minidump_extensions.h. + uint32_t ExceptionCode; + + //! \brief Additional exception flags that further identify the exception, in + //! operating system-specific values. + //! + //! For macOS minidumps, this will be the value of the exception code at index + //! 0 as received by a Mach exception handler, except: + //! * For exception type `EXC_CRASH` generated from another preceding + //! exception, the original exception code will appear here, not the code + //! as received by the Mach exception handler. + //! * For exception types `EXC_RESOURCE` and `EXC_GUARD`, the high 32 bits of + //! the code received by the Mach exception handler will appear here. + //! + //! In all cases for macOS minidumps, the code as it was received by the Mach + //! exception handler will appear at index 1 of #ExceptionInformation. + //! + //! For Windows minidumps, this will either be `0` if the exception is + //! continuable, or `EXCEPTION_NONCONTINUABLE` to indicate a noncontinuable + //! exception. + //! + //! \todo Document the possible values by OS. There may be OS-specific enums + //! in minidump_extensions.h. + uint32_t ExceptionFlags; + + //! \brief An address, in the address space of the process that this minidump + //! file contains a snapshot of, of another MINIDUMP_EXCEPTION. This field + //! is used for nested exceptions. + uint64_t ExceptionRecord; + + //! \brief The address that caused the exception. + //! + //! This may be the address that caused a fault on data access, or it may be + //! the instruction pointer that contained an offending instruction. + uint64_t ExceptionAddress; + + //! \brief The number of valid elements in #ExceptionInformation. + uint32_t NumberParameters; + + uint32_t __unusedAlignment; + + //! \brief Additional information about the exception, specific to the + //! operating system and possibly the #ExceptionCode. + //! + //! For macOS minidumps, this will contain the exception type as received by a + //! Mach exception handler and the values of the `codes[0]` and `codes[1]` + //! (exception code and subcode) parameters supplied to the Mach exception + //! handler. Unlike #ExceptionCode and #ExceptionFlags, the values received by + //! a Mach exception handler are used directly here even for the `EXC_CRASH`, + //! `EXC_RESOURCE`, and `EXC_GUARD` exception types. + + //! For Windows, these are additional arguments (if any) as provided to + //! `RaiseException()`. + uint64_t ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; +}; + +//! \brief Information about the exception that triggered a minidump file’s +//! generation. +struct __attribute__((packed, aligned(4))) MINIDUMP_EXCEPTION_STREAM { + //! \brief The ID of the thread that caused the exception. + //! + //! \sa MINIDUMP_THREAD::ThreadId + uint32_t ThreadId; + + uint32_t __alignment; + + //! \brief Information about the exception. + MINIDUMP_EXCEPTION ExceptionRecord; + + //! \brief A pointer to a CPU-specific CONTEXT structure containing the + //! thread’s context at the time the exception was caused. + //! + //! The interpretation of the context structure is dependent on the CPU + //! architecture identified by MINIDUMP_SYSTEM_INFO::ProcessorArchitecture. + //! For crashpad::kMinidumpCPUArchitectureX86, this will be + //! crashpad::MinidumpContextX86. For crashpad::kMinidumpCPUArchitectureAMD64, + //! this will be crashpad::MinidumpContextAMD64. + MINIDUMP_LOCATION_DESCRIPTOR ThreadContext; +}; + +//! \brief Information about a specific module loaded within the process at the +//! time the snapshot was taken. +//! +//! A module may be the main executable, a shared library, or a loadable module. +//! +//! \sa MINIDUMP_MODULE_LIST +struct __attribute__((packed, aligned(4))) MINIDUMP_MODULE { + //! \brief The base address of the loaded module in the address space of the + //! process that the minidump file contains a snapshot of. + uint64_t BaseOfImage; + + //! \brief The size of the loaded module. + uint32_t SizeOfImage; + + //! \brief The loaded module’s checksum, or `0` if unknown. + //! + //! On Windows, this field comes from the `CheckSum` field of the module’s + //! `IMAGE_OPTIONAL_HEADER` structure, if present. It reflects the checksum at + //! the time the module was linked. + uint32_t CheckSum; + + //! \brief The module’s timestamp, in `time_t` units, seconds since the POSIX + //! epoch, or `0` if unknown. + //! + //! On Windows, this field comes from the `TimeDateStamp` field of the + //! module’s `IMAGE_FILE_HEADER` structure. It reflects the timestamp at the + //! time the module was linked. + uint32_t TimeDateStamp; + + //! \brief ::RVA of a MINIDUMP_STRING containing the module’s path or file + //! name. + RVA ModuleNameRva; + + //! \brief The module’s version information. + VS_FIXEDFILEINFO VersionInfo; + + //! \brief A pointer to the module’s CodeView record, typically a link to its + //! debugging information in crashpad::CodeViewRecordPDB70 format. + //! + //! The specific format of the CodeView record is indicated by its signature, + //! the first 32-bit value in the structure. For links to debugging + //! information in contemporary usage, this is normally a + //! crashpad::CodeViewRecordPDB70 structure, but may be a + //! crashpad::CodeViewRecordPDB20 structure instead. These structures identify + //! a link to debugging data within a `.pdb` (Program Database) file. See Matching + //! Debug Information, PDB Files. + //! + //! On Windows, it is also possible for the CodeView record to contain + //! debugging information itself, as opposed to a link to a `.pdb` file. See + //! Microsoft + //! Symbol and Type Information, section 7.2, “Debug Information Format†+ //! for a list of debug information formats, and Undocumented Windows 2000 + //! Secrets, Windows 2000 Debugging Support/Microsoft Symbol File + //! Internals/CodeView Subsections for an in-depth description of the CodeView + //! 4.1 format. Signatures seen in the wild include “NB09†(0x3930424e) for + //! CodeView 4.1 and “NB11†(0x3131424e) for CodeView 5.0. This form of + //! debugging information within the module, as opposed to a link to an + //! external `.pdb` file, is chosen by building with `/Z7` in Visual Studio + //! 6.0 (1998) and earlier. This embedded form of debugging information is now + //! considered obsolete. + //! + //! On Windows, the CodeView record is taken from a module’s + //! IMAGE_DEBUG_DIRECTORY entry whose Type field has the value + //! IMAGE_DEBUG_TYPE_CODEVIEW (`2`), if any. Records in + //! crashpad::CodeViewRecordPDB70 format are generated by Visual Studio .NET + //! (2002) (version 7.0) and later. + //! + //! When the CodeView record is not present, the fields of this + //! MINIDUMP_LOCATION_DESCRIPTOR will be `0`. + MINIDUMP_LOCATION_DESCRIPTOR CvRecord; + + //! \brief A pointer to the module’s miscellaneous debugging record, a + //! structure of type IMAGE_DEBUG_MISC. + //! + //! This field is Windows-specific, and has no meaning on other operating + //! systems. It is largely obsolete on Windows, where it was used to link to + //! debugging information stored in a `.dbg` file. `.dbg` files have been + //! superseded by `.pdb` files. + //! + //! On Windows, the miscellaneous debugging record is taken from module’s + //! IMAGE_DEBUG_DIRECTORY entry whose Type field has the value + //! IMAGE_DEBUG_TYPE_MISC (`4`), if any. + //! + //! When the miscellaneous debugging record is not present, the fields of this + //! MINIDUMP_LOCATION_DESCRIPTOR will be `0`. + //! + //! \sa #CvRecord + MINIDUMP_LOCATION_DESCRIPTOR MiscRecord; + + uint64_t Reserved0; + uint64_t Reserved1; +}; + +//! \brief Information about all modules loaded within the process at the time +//! the snapshot was taken. +struct __attribute__((packed, aligned(4))) MINIDUMP_MODULE_LIST { + //! \brief The number of modules present in the #Modules array. + uint32_t NumberOfModules; + + //! \brief Structures identifying each module present in the minidump file. + MINIDUMP_MODULE Modules[0]; +}; + +//! \brief Information about memory regions within the process. +//! +//! Typically, a minidump file will not contain a snapshot of a process’ entire +//! memory image. For minidump files identified as ::MiniDumpNormal in +//! MINIDUMP_HEADER::Flags, memory regions are limited to those referenced by +//! MINIDUMP_THREAD::Stack fields, and a small number of others possibly related +//! to the exception that triggered the snapshot to be taken. +struct __attribute__((packed, aligned(4))) MINIDUMP_MEMORY_LIST { + //! \brief The number of memory regions present in the #MemoryRanges array. + uint32_t NumberOfMemoryRanges; + + //! \brief Structures identifying each memory region present in the minidump + //! file. + MINIDUMP_MEMORY_DESCRIPTOR MemoryRanges[0]; +}; + +//! \brief Contains the state of an individual system handle at the time the +//! snapshot was taken. This structure is Windows-specific. +//! +//! \sa MINIDUMP_HANDLE_DESCRIPTOR_2 +struct __attribute__((packed, aligned(4))) MINIDUMP_HANDLE_DESCRIPTOR { + //! \brief The Windows `HANDLE` value. + uint64_t Handle; + + //! \brief An RVA to a MINIDUMP_STRING structure that specifies the object + //! type of the handle. This member can be zero. + RVA TypeNameRva; + + //! \brief An RVA to a MINIDUMP_STRING structure that specifies the object + //! name of the handle. This member can be zero. + RVA ObjectNameRva; + + //! \brief The attributes for the handle, this corresponds to `OBJ_INHERIT`, + //! `OBJ_CASE_INSENSITIVE`, etc. + uint32_t Attributes; + + //! \brief The `ACCESS_MASK` for the handle. + uint32_t GrantedAccess; + + //! \brief This is the number of open handles to the object that this handle + //! refers to. + uint32_t HandleCount; + + //! \brief This is the number kernel references to the object that this + //! handle refers to. + uint32_t PointerCount; +}; + +//! \brief Contains the state of an individual system handle at the time the +//! snapshot was taken. This structure is Windows-specific. +//! +//! \sa MINIDUMP_HANDLE_DESCRIPTOR +struct __attribute__((packed, aligned(4))) MINIDUMP_HANDLE_DESCRIPTOR_2 + : public MINIDUMP_HANDLE_DESCRIPTOR { + //! \brief An RVA to a MINIDUMP_HANDLE_OBJECT_INFORMATION structure that + //! specifies object-specific information. This member can be zero if + //! there is no extra information. + RVA ObjectInfoRva; + + //! \brief Must be zero. + uint32_t Reserved0; +}; + +//! \brief Represents the header for a handle data stream. +//! +//! A list of MINIDUMP_HANDLE_DESCRIPTOR or MINIDUMP_HANDLE_DESCRIPTOR_2 +//! structures will immediately follow in the stream. +struct __attribute((packed, aligned(4))) MINIDUMP_HANDLE_DATA_STREAM { + //! \brief The size of the header information for the stream, in bytes. This + //! value is `sizeof(MINIDUMP_HANDLE_DATA_STREAM)`. + uint32_t SizeOfHeader; + + //! \brief The size of a descriptor in the stream, in bytes. This value is + //! `sizeof(MINIDUMP_HANDLE_DESCRIPTOR)` or + //! `sizeof(MINIDUMP_HANDLE_DESCRIPTOR_2)`. + uint32_t SizeOfDescriptor; + + //! \brief The number of descriptors in the stream. + uint32_t NumberOfDescriptors; + + //! \brief Must be zero. + uint32_t Reserved; +}; + +//! \brief Information about a specific module that was recorded as being +//! unloaded at the time the snapshot was taken. +//! +//! An unloaded module may be a shared library or a loadable module. +//! +//! \sa MINIDUMP_UNLOADED_MODULE_LIST +struct __attribute__((packed, aligned(4))) MINIDUMP_UNLOADED_MODULE { + //! \brief The base address where the module was loaded in the address space + //! of the process that the minidump file contains a snapshot of. + uint64_t BaseOfImage; + + //! \brief The size of the unloaded module. + uint32_t SizeOfImage; + + //! \brief The module’s checksum, or `0` if unknown. + //! + //! On Windows, this field comes from the `CheckSum` field of the module’s + //! `IMAGE_OPTIONAL_HEADER` structure, if present. It reflects the checksum at + //! the time the module was linked. + uint32_t CheckSum; + + //! \brief The module’s timestamp, in `time_t` units, seconds since the POSIX + //! epoch, or `0` if unknown. + //! + //! On Windows, this field comes from the `TimeDateStamp` field of the + //! module’s `IMAGE_FILE_HEADER` structure. It reflects the timestamp at the + //! time the module was linked. + uint32_t TimeDateStamp; + + //! \brief ::RVA of a MINIDUMP_STRING containing the module’s path or file + //! name. + RVA ModuleNameRva; +}; + +//! \brief Information about all modules recorded as unloaded when the snapshot +//! was taken. +//! +//! A list of MINIDUMP_UNLOADED_MODULE structures will immediately follow in the +//! stream. +struct __attribute__((packed, aligned(4))) MINIDUMP_UNLOADED_MODULE_LIST { + //! \brief The size of the header information for the stream, in bytes. This + //! value is `sizeof(MINIDUMP_UNLOADED_MODULE_LIST)`. + uint32_t SizeOfHeader; + + //! \brief The size of a descriptor in the stream, in bytes. This value is + //! `sizeof(MINIDUMP_UNLOADED_MODULE)`. + uint32_t SizeOfEntry; + + //! \brief The number of entries in the stream. + uint32_t NumberOfEntries; +}; + +//! \brief Information about XSAVE-managed state stored within CPU-specific +//! context structures. +struct __attribute__((packed, aligned(4))) XSTATE_CONFIG_FEATURE_MSC_INFO { + //! \brief The size of this structure, in bytes. This value is + //! `sizeof(XSTATE_CONFIG_FEATURE_MSC_INFO)`. + uint32_t SizeOfInfo; + + //! \brief The size of a CPU-specific context structure carrying all XSAVE + //! state components described by this structure. + //! + //! Equivalent to the value returned by `InitializeContext()` in \a + //! ContextLength. + uint32_t ContextSize; + + //! \brief The XSAVE state-component bitmap, XSAVE_BV. + //! + //! See Intel Software Developer’s Manual, Volume 1: Basic Architecture + //! (253665-060), 13.4.2 “XSAVE Headerâ€. + uint64_t EnabledFeatures; + + //! \brief The location of each state component within a CPU-specific context + //! structure. + //! + //! This array is indexed by bit position numbers used in #EnabledFeatures. + XSTATE_FEATURE Features[MAXIMUM_XSTATE_FEATURES]; +}; + +//! \anchor MINIDUMP_MISCx +//! \name MINIDUMP_MISC* +//! +//! \brief Field validity flag values for MINIDUMP_MISC_INFO::Flags1. +//! \{ + +//! \brief MINIDUMP_MISC_INFO::ProcessId is valid. +#define MINIDUMP_MISC1_PROCESS_ID 0x00000001 + +//! \brief The time-related fields in MINIDUMP_MISC_INFO are valid. +//! +//! The following fields are valid: +//! - MINIDUMP_MISC_INFO::ProcessCreateTime +//! - MINIDUMP_MISC_INFO::ProcessUserTime +//! - MINIDUMP_MISC_INFO::ProcessKernelTime +#define MINIDUMP_MISC1_PROCESS_TIMES 0x00000002 + +//! \brief The CPU-related fields in MINIDUMP_MISC_INFO_2 are valid. +//! +//! The following fields are valid: +//! - MINIDUMP_MISC_INFO_2::ProcessorMaxMhz +//! - MINIDUMP_MISC_INFO_2::ProcessorCurrentMhz +//! - MINIDUMP_MISC_INFO_2::ProcessorMhzLimit +//! - MINIDUMP_MISC_INFO_2::ProcessorMaxIdleState +//! - MINIDUMP_MISC_INFO_2::ProcessorCurrentIdleState +//! +//! \note This macro should likely have been named +//! MINIDUMP_MISC2_PROCESSOR_POWER_INFO. +#define MINIDUMP_MISC1_PROCESSOR_POWER_INFO 0x00000004 + +//! \brief MINIDUMP_MISC_INFO_3::ProcessIntegrityLevel is valid. +#define MINIDUMP_MISC3_PROCESS_INTEGRITY 0x00000010 + +//! \brief MINIDUMP_MISC_INFO_3::ProcessExecuteFlags is valid. +#define MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS 0x00000020 + +//! \brief The time zone-related fields in MINIDUMP_MISC_INFO_3 are valid. +//! +//! The following fields are valid: +//! - MINIDUMP_MISC_INFO_3::TimeZoneId +//! - MINIDUMP_MISC_INFO_3::TimeZone +#define MINIDUMP_MISC3_TIMEZONE 0x00000040 + +//! \brief MINIDUMP_MISC_INFO_3::ProtectedProcess is valid. +#define MINIDUMP_MISC3_PROTECTED_PROCESS 0x00000080 + +//! \brief The build string-related fields in MINIDUMP_MISC_INFO_4 are valid. +//! +//! The following fields are valid: +//! - MINIDUMP_MISC_INFO_4::BuildString +//! - MINIDUMP_MISC_INFO_4::DbgBldStr +#define MINIDUMP_MISC4_BUILDSTRING 0x00000100 + +//! \brief MINIDUMP_MISC_INFO_5::ProcessCookie is valid. +#define MINIDUMP_MISC5_PROCESS_COOKIE 0x00000200 + +//! \} + +//! \brief Information about the process that the minidump file contains a +//! snapshot of, as well as the system that hosted that process. +//! +//! \sa \ref MINIDUMP_MISCx "MINIDUMP_MISC*" +//! \sa MINIDUMP_MISC_INFO_2 +//! \sa MINIDUMP_MISC_INFO_3 +//! \sa MINIDUMP_MISC_INFO_4 +//! \sa MINIDUMP_MISC_INFO_5 +//! \sa MINIDUMP_MISC_INFO_N +struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO { + //! \brief The size of the structure. + //! + //! This field can be used to distinguish between different versions of this + //! structure: MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2, MINIDUMP_MISC_INFO_3, + //! and MINIDUMP_MISC_INFO_4. + //! + //! \sa Flags1 + uint32_t SizeOfInfo; + + //! \brief A bit field of \ref MINIDUMP_MISCx "MINIDUMP_MISC*" values + //! indicating which fields of this structure contain valid data. + uint32_t Flags1; + + //! \brief The process ID of the process. + uint32_t ProcessId; + + //! \brief The time that the process started, in `time_t` units, seconds since + //! the POSIX epoch. + uint32_t ProcessCreateTime; + + //! \brief The amount of user-mode CPU time used by the process, in seconds, + //! at the time of the snapshot. + uint32_t ProcessUserTime; + + //! \brief The amount of system-mode (kernel) CPU time used by the process, in + //! seconds, at the time of the snapshot. + uint32_t ProcessKernelTime; +}; + +//! \brief Information about the process that the minidump file contains a +//! snapshot of, as well as the system that hosted that process. +//! +//! This structure variant is used on Windows Vista (NT 6.0) and later. +//! +//! \sa \ref MINIDUMP_MISCx "MINIDUMP_MISC*" +//! \sa MINIDUMP_MISC_INFO +//! \sa MINIDUMP_MISC_INFO_3 +//! \sa MINIDUMP_MISC_INFO_4 +//! \sa MINIDUMP_MISC_INFO_5 +//! \sa MINIDUMP_MISC_INFO_N +struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_2 + : public MINIDUMP_MISC_INFO { + //! \brief The maximum clock rate of the system’s CPU or CPUs, in MHz. + uint32_t ProcessorMaxMhz; + + //! \brief The clock rate of the system’s CPU or CPUs, in MHz, at the time of + //! the snapshot. + uint32_t ProcessorCurrentMhz; + + //! \brief The maximum clock rate of the system’s CPU or CPUs, in MHz, reduced + //! by any thermal limitations, at the time of the snapshot. + uint32_t ProcessorMhzLimit; + + //! \brief The maximum idle state of the system’s CPU or CPUs. + uint32_t ProcessorMaxIdleState; + + //! \brief The idle state of the system’s CPU or CPUs at the time of the + //! snapshot. + uint32_t ProcessorCurrentIdleState; +}; + +//! \brief Information about the process that the minidump file contains a +//! snapshot of, as well as the system that hosted that process. +//! +//! This structure variant is used on Windows 7 (NT 6.1) and later. +//! +//! \sa \ref MINIDUMP_MISCx "MINIDUMP_MISC*" +//! \sa MINIDUMP_MISC_INFO +//! \sa MINIDUMP_MISC_INFO_2 +//! \sa MINIDUMP_MISC_INFO_4 +//! \sa MINIDUMP_MISC_INFO_5 +//! \sa MINIDUMP_MISC_INFO_N +struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_3 + : public MINIDUMP_MISC_INFO_2 { + //! \brief The process’ integrity level. + //! + //! Windows typically uses `SECURITY_MANDATORY_MEDIUM_RID` (0x2000) for + //! processes belonging to normal authenticated users and + //! `SECURITY_MANDATORY_HIGH_RID` (0x3000) for elevated processes. + //! + //! This field is Windows-specific, and has no meaning on other operating + //! systems. + uint32_t ProcessIntegrityLevel; + + //! \brief The process’ execute flags. + //! + //! On Windows, this appears to be returned by `NtQueryInformationProcess()` + //! with an argument of `ProcessExecuteFlags` (34). + //! + //! This field is Windows-specific, and has no meaning on other operating + //! systems. + uint32_t ProcessExecuteFlags; + + //! \brief Whether the process is protected. + //! + //! This field is Windows-specific, and has no meaning on other operating + //! systems. + uint32_t ProtectedProcess; + + //! \brief Whether daylight saving time was being observed in the system’s + //! location at the time of the snapshot. + //! + //! This field can contain the following values: + //! - `0` if the location does not observe daylight saving time at all. The + //! TIME_ZONE_INFORMATION::StandardName field of #TimeZoneId contains the + //! time zone name. + //! - `1` if the location observes daylight saving time, but standard time + //! was in effect at the time of the snapshot. The + //! TIME_ZONE_INFORMATION::StandardName field of #TimeZoneId contains the + //! time zone name. + //! - `2` if the location observes daylight saving time, and it was in effect + //! at the time of the snapshot. The TIME_ZONE_INFORMATION::DaylightName + //! field of #TimeZoneId contains the time zone name. + //! + //! \sa #TimeZone + uint32_t TimeZoneId; + + //! \brief Information about the time zone at the system’s location. + //! + //! \sa #TimeZoneId + TIME_ZONE_INFORMATION TimeZone; +}; + +//! \brief Information about the process that the minidump file contains a +//! snapshot of, as well as the system that hosted that process. +//! +//! This structure variant is used on Windows 8 (NT 6.2) and later. +//! +//! \sa \ref MINIDUMP_MISCx "MINIDUMP_MISC*" +//! \sa MINIDUMP_MISC_INFO +//! \sa MINIDUMP_MISC_INFO_2 +//! \sa MINIDUMP_MISC_INFO_3 +//! \sa MINIDUMP_MISC_INFO_5 +//! \sa MINIDUMP_MISC_INFO_N +struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_4 + : public MINIDUMP_MISC_INFO_3 { + //! \brief The operating system’s “build stringâ€, a string identifying a + //! specific build of the operating system. + //! + //! This string is UTF-16-encoded and terminated by a UTF-16 `NUL` code unit. + //! + //! On Windows 8.1 (NT 6.3), this is “6.3.9600.17031 + //! (winblue_gdr.140221-1952)â€. + char16_t BuildString[260]; + + //! \brief The minidump producer’s “build stringâ€, a string identifying the + //! module that produced a minidump file. + //! + //! This string is UTF-16-encoded and terminated by a UTF-16 `NUL` code unit. + //! + //! On Windows 8.1 (NT 6.3), this may be “dbghelp.i386,6.3.9600.16520†or + //! “dbghelp.amd64,6.3.9600.16520†depending on CPU architecture. + char16_t DbgBldStr[40]; +}; + +//! \brief Information about the process that the minidump file contains a +//! snapshot of, as well as the system that hosted that process. +//! +//! This structure variant is used on Windows 10 and later. +//! +//! \sa \ref MINIDUMP_MISCx "MINIDUMP_MISC*" +//! \sa MINIDUMP_MISC_INFO +//! \sa MINIDUMP_MISC_INFO_2 +//! \sa MINIDUMP_MISC_INFO_3 +//! \sa MINIDUMP_MISC_INFO_4 +//! \sa MINIDUMP_MISC_INFO_N +struct __attribute__((packed, aligned(4))) MINIDUMP_MISC_INFO_5 + : public MINIDUMP_MISC_INFO_4 { + //! \brief Information about XSAVE-managed state stored within CPU-specific + //! context structures. + //! + //! This information can be used to locate state components within + //! CPU-specific context structures. + XSTATE_CONFIG_FEATURE_MSC_INFO XStateData; + + uint32_t ProcessCookie; +}; + +//! \brief The latest known version of the MINIDUMP_MISC_INFO structure. +typedef MINIDUMP_MISC_INFO_5 MINIDUMP_MISC_INFO_N; + +//! \brief Describes a region of memory. +struct __attribute__((packed, aligned(4))) MINIDUMP_MEMORY_INFO { + //! \brief The base address of the region of pages. + uint64_t BaseAddress; + + //! \brief The base address of a range of pages in this region. The page is + //! contained within this memory region. + uint64_t AllocationBase; + + //! \brief The memory protection when the region was initially allocated. This + //! member can be one of the memory protection options (such as + //! \ref PAGE_x "PAGE_EXECUTE", \ref PAGE_x "PAGE_NOACCESS", etc.), along + //! with \ref PAGE_x "PAGE_GUARD" or \ref PAGE_x "PAGE_NOCACHE", as + //! needed. + uint32_t AllocationProtect; + + uint32_t __alignment1; + + //! \brief The size of the region beginning at the base address in which all + //! pages have identical attributes, in bytes. + uint64_t RegionSize; + + //! \brief The state of the pages in the region. This can be one of + //! \ref MEM_x "MEM_COMMIT", \ref MEM_x "MEM_FREE", or \ref MEM_x + //! "MEM_RESERVE". + uint32_t State; + + //! \brief The access protection of the pages in the region. This member is + //! one of the values listed for the #AllocationProtect member. + uint32_t Protect; + + //! \brief The type of pages in the region. This can be one of \ref MEM_x + //! "MEM_IMAGE", \ref MEM_x "MEM_MAPPED", or \ref MEM_x "MEM_PRIVATE". + uint32_t Type; + + uint32_t __alignment2; +}; + +//! \brief Contains a list of memory regions. +struct __attribute__((packed, aligned(4))) MINIDUMP_MEMORY_INFO_LIST { + //! \brief The size of the header data for the stream, in bytes. This is + //! generally sizeof(MINIDUMP_MEMORY_INFO_LIST). + uint32_t SizeOfHeader; + + //! \brief The size of each entry following the header, in bytes. This is + //! generally sizeof(MINIDUMP_MEMORY_INFO). + uint32_t SizeOfEntry; + + //! \brief The number of entries in the stream. These are generally + //! MINIDUMP_MEMORY_INFO structures. The entries follow the header. + uint64_t NumberOfEntries; +}; + +//! \brief Minidump file type values for MINIDUMP_HEADER::Flags. These bits +//! describe the types of data carried within a minidump file. +enum MINIDUMP_TYPE { + //! \brief A minidump file without any additional data. + //! + //! This type of minidump file contains: + //! - A MINIDUMP_SYSTEM_INFO stream. + //! - A MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2, MINIDUMP_MISC_INFO_3, or + //! MINIDUMP_MISC_INFO_4 stream, depending on which fields are present. + //! - A MINIDUMP_THREAD_LIST stream. All threads are present, along with a + //! snapshot of each thread’s stack memory sufficient to obtain backtraces. + //! - If the minidump file was generated as a result of an exception, a + //! MINIDUMP_EXCEPTION_STREAM describing the exception. + //! - A MINIDUMP_MODULE_LIST stream. All loaded modules are present. + //! - Typically, a MINIDUMP_MEMORY_LIST stream containing duplicate pointers + //! to the stack memory regions also referenced by the MINIDUMP_THREAD_LIST + //! stream. This type of minidump file also includes a + //! MINIDUMP_MEMORY_DESCRIPTOR containing the 256 bytes centered around + //! the exception address or the instruction pointer. + MiniDumpNormal = 0x00000000, +}; + +#endif // CRASHPAD_COMPAT_NON_WIN_DBGHELP_H_ diff --git a/shared/sentry/external/crashpad/compat/non_win/minwinbase.h b/shared/sentry/external/crashpad/compat/non_win/minwinbase.h new file mode 100644 index 000000000..2761b1ccc --- /dev/null +++ b/shared/sentry/external/crashpad/compat/non_win/minwinbase.h @@ -0,0 +1,49 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_NON_WIN_MINWINBASE_H_ +#define CRASHPAD_COMPAT_NON_WIN_MINWINBASE_H_ + +#include + +//! \brief Represents a date and time. +struct SYSTEMTIME { + //! \brief The year, represented fully. + //! + //! The year 2014 would be represented in this field as `2014`. + uint16_t wYear; + + //! \brief The month of the year, `1` for January and `12` for December. + uint16_t wMonth; + + //! \brief The day of the week, `0` for Sunday and `6` for Saturday. + uint16_t wDayOfWeek; + + //! \brief The day of the month, `1` through `31`. + uint16_t wDay; + + //! \brief The hour of the day, `0` through `23`. + uint16_t wHour; + + //! \brief The minute of the hour, `0` through `59`. + uint16_t wMinute; + + //! \brief The second of the minute, `0` through `60`. + uint16_t wSecond; + + //! \brief The millisecond of the second, `0` through `999`. + uint16_t wMilliseconds; +}; + +#endif // CRASHPAD_COMPAT_NON_WIN_MINWINBASE_H_ diff --git a/shared/sentry/external/crashpad/compat/non_win/timezoneapi.h b/shared/sentry/external/crashpad/compat/non_win/timezoneapi.h new file mode 100644 index 000000000..6e7b09517 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/non_win/timezoneapi.h @@ -0,0 +1,60 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_NON_WIN_TIMEZONEAPI_H_ +#define CRASHPAD_COMPAT_NON_WIN_TIMEZONEAPI_H_ + +#include + +#include "compat/non_win/minwinbase.h" + +//! \brief Information about a time zone and its daylight saving rules. +struct TIME_ZONE_INFORMATION { + //! \brief The number of minutes west of UTC. + int32_t Bias; + + //! \brief The UTF-16-encoded name of the time zone when observing standard + //! time. + char16_t StandardName[32]; + + //! \brief The date and time to switch from daylight saving time to standard + //! time. + //! + //! This can be a specific time, or with SYSTEMTIME::wYear set to `0`, it can + //! reflect an annual recurring transition. In that case, SYSTEMTIME::wDay in + //! the range `1` to `5` is interpreted as the given occurrence of + //! SYSTEMTIME::wDayOfWeek within the month, `1` being the first occurrence + //! and `5` being the last (even if there are fewer than 5). + SYSTEMTIME StandardDate; + + //! \brief The bias relative to #Bias to be applied when observing standard + //! time. + int32_t StandardBias; + + //! \brief The UTF-16-encoded name of the time zone when observing daylight + //! saving time. + char16_t DaylightName[32]; + + //! \brief The date and time to switch from standard time to daylight saving + //! time. + //! + //! This field is specified in the same manner as #StandardDate. + SYSTEMTIME DaylightDate; + + //! \brief The bias relative to #Bias to be applied when observing daylight + //! saving time. + int32_t DaylightBias; +}; + +#endif // CRASHPAD_COMPAT_NON_WIN_TIMEZONEAPI_H_ diff --git a/shared/sentry/external/crashpad/compat/non_win/verrsrc.h b/shared/sentry/external/crashpad/compat/non_win/verrsrc.h new file mode 100644 index 000000000..70f534424 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/non_win/verrsrc.h @@ -0,0 +1,184 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_NON_WIN_VERRSRC_H_ +#define CRASHPAD_COMPAT_NON_WIN_VERRSRC_H_ + +#include + +//! \file + +//! \brief The magic number for a VS_FIXEDFILEINFO structure, stored in +//! VS_FIXEDFILEINFO::dwSignature. +#define VS_FFI_SIGNATURE 0xfeef04bd + +//! \brief The version of a VS_FIXEDFILEINFO structure, stored in +//! VS_FIXEDFILEINFO::dwStrucVersion. +#define VS_FFI_STRUCVERSION 0x00010000 + +//! \anchor VS_FF_x +//! \name VS_FF_* +//! +//! \brief File attribute values for VS_FIXEDFILEINFO::dwFileFlags and +//! VS_FIXEDFILEINFO::dwFileFlagsMask. +//! \{ +#define VS_FF_DEBUG 0x00000001 +#define VS_FF_PRERELEASE 0x00000002 +#define VS_FF_PATCHED 0x00000004 +#define VS_FF_PRIVATEBUILD 0x00000008 +#define VS_FF_INFOINFERRED 0x00000010 +#define VS_FF_SPECIALBUILD 0x00000020 +//! \} + +//! \anchor VOS_x +//! \name VOS_* +//! +//! \brief Operating system values for VS_FIXEDFILEINFO::dwFileOS. +//! \{ +#define VOS_UNKNOWN 0x00000000 +#define VOS_DOS 0x00010000 +#define VOS_OS216 0x00020000 +#define VOS_OS232 0x00030000 +#define VOS_NT 0x00040000 +#define VOS_WINCE 0x00050000 +#define VOS__BASE 0x00000000 +#define VOS__WINDOWS16 0x00000001 +#define VOS__PM16 0x00000002 +#define VOS__PM32 0x00000003 +#define VOS__WINDOWS32 0x00000004 +#define VOS_DOS_WINDOWS16 0x00010001 +#define VOS_DOS_WINDOWS32 0x00010004 +#define VOS_OS216_PM16 0x00020002 +#define VOS_OS232_PM32 0x00030003 +#define VOS_NT_WINDOWS32 0x00040004 +//! \} + +//! \anchor VFT_x +//! \name VFT_* +//! +//! \brief File type values for VS_FIXEDFILEINFO::dwFileType. +//! \{ +#define VFT_UNKNOWN 0x00000000 +#define VFT_APP 0x00000001 +#define VFT_DLL 0x00000002 +#define VFT_DRV 0x00000003 +#define VFT_FONT 0x00000004 +#define VFT_VXD 0x00000005 +#define VFT_STATIC_LIB 0x00000007 +//! \} + +//! \anchor VFT2_x +//! \name VFT2_* +//! +//! \brief File subtype values for VS_FIXEDFILEINFO::dwFileSubtype. +//! \{ +#define VFT2_UNKNOWN 0x00000000 +#define VFT2_DRV_PRINTER 0x00000001 +#define VFT2_DRV_KEYBOARD 0x00000002 +#define VFT2_DRV_LANGUAGE 0x00000003 +#define VFT2_DRV_DISPLAY 0x00000004 +#define VFT2_DRV_MOUSE 0x00000005 +#define VFT2_DRV_NETWORK 0x00000006 +#define VFT2_DRV_SYSTEM 0x00000007 +#define VFT2_DRV_INSTALLABLE 0x00000008 +#define VFT2_DRV_SOUND 0x00000009 +#define VFT2_DRV_COMM 0x0000000A +#define VFT2_DRV_INPUTMETHOD 0x0000000B +#define VFT2_DRV_VERSIONED_PRINTER 0x0000000C +#define VFT2_FONT_RASTER 0x00000001 +#define VFT2_FONT_VECTOR 0x00000002 +#define VFT2_FONT_TRUETYPE 0x00000003 +//! \} + +//! \brief Version information for a file. +//! +//! On Windows, this information is derived from a file’s version information +//! resource, and is obtained by calling `VerQueryValue()` with an `lpSubBlock` +//! argument of `"\"` (a single backslash). +struct VS_FIXEDFILEINFO { + //! \brief The structure’s magic number, ::VS_FFI_SIGNATURE. + uint32_t dwSignature; + + //! \brief The structure’s version, ::VS_FFI_STRUCVERSION. + uint32_t dwStrucVersion; + + //! \brief The more-significant portion of the file’s version number. + //! + //! This field contains the first two components of a four-component version + //! number. For a file whose version is 1.2.3.4, this field would be + //! `0x00010002`. + //! + //! \sa dwFileVersionLS + uint32_t dwFileVersionMS; + + //! \brief The less-significant portion of the file’s version number. + //! + //! This field contains the last two components of a four-component version + //! number. For a file whose version is 1.2.3.4, this field would be + //! `0x00030004`. + //! + //! \sa dwFileVersionMS + uint32_t dwFileVersionLS; + + //! \brief The more-significant portion of the product’s version number. + //! + //! This field contains the first two components of a four-component version + //! number. For a product whose version is 1.2.3.4, this field would be + //! `0x00010002`. + //! + //! \sa dwProductVersionLS + uint32_t dwProductVersionMS; + + //! \brief The less-significant portion of the product’s version number. + //! + //! This field contains the last two components of a four-component version + //! number. For a product whose version is 1.2.3.4, this field would be + //! `0x00030004`. + //! + //! \sa dwProductVersionMS + uint32_t dwProductVersionLS; + + //! \brief A bitmask of \ref VS_FF_x "VS_FF_*" values indicating which bits in + //! #dwFileFlags are valid. + uint32_t dwFileFlagsMask; + + //! \brief A bitmask of \ref VS_FF_x "VS_FF_*" values identifying attributes + //! of the file. Only bits present in #dwFileFlagsMask are valid. + uint32_t dwFileFlags; + + //! \brief The file’s intended operating system, a value of \ref VOS_x + //! "VOS_*". + uint32_t dwFileOS; + + //! \brief The file’s type, a value of \ref VFT_x "VFT_*". + uint32_t dwFileType; + + //! \brief The file’s subtype, a value of \ref VFT2_x "VFT2_*" corresponding + //! to its #dwFileType, if the file type has subtypes. + uint32_t dwFileSubtype; + + //! \brief The more-significant portion of the file’s creation date. + //! + //! The intended encoding of this field is unknown. This field is unused and + //! always has the value `0`. + uint32_t dwFileDateMS; + + //! \brief The less-significant portion of the file’s creation date. + //! + //! The intended encoding of this field is unknown. This field is unused and + //! always has the value `0`. + uint32_t dwFileDateLS; +}; + +#endif // CRASHPAD_COMPAT_NON_WIN_VERRSRC_H_ diff --git a/shared/sentry/external/crashpad/compat/non_win/windows.h b/shared/sentry/external/crashpad/compat/non_win/windows.h new file mode 100644 index 000000000..457745149 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/non_win/windows.h @@ -0,0 +1,17 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +// dbghelp.h on Windows requires inclusion of windows.h before it. To avoid +// cluttering all inclusions of dbghelp.h with #ifdefs, always include windows.h +// and have an empty one on non-Windows platforms. diff --git a/shared/sentry/external/crashpad/compat/non_win/winnt.h b/shared/sentry/external/crashpad/compat/non_win/winnt.h new file mode 100644 index 000000000..f2ed6ace0 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/non_win/winnt.h @@ -0,0 +1,250 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_NON_WIN_WINNT_H_ +#define CRASHPAD_COMPAT_NON_WIN_WINNT_H_ + +#include + +//! \file + +//! \anchor VER_SUITE_x +//! \name VER_SUITE_* +//! +//! \brief Installable product values for MINIDUMP_SYSTEM_INFO::SuiteMask. +//! \{ +#define VER_SUITE_SMALLBUSINESS 0x0001 +#define VER_SUITE_ENTERPRISE 0x0002 +#define VER_SUITE_BACKOFFICE 0x0004 +#define VER_SUITE_COMMUNICATIONS 0x0008 +#define VER_SUITE_TERMINAL 0x0010 +#define VER_SUITE_SMALLBUSINESS_RESTRICTED 0x0020 +#define VER_SUITE_EMBEDDEDNT 0x0040 +#define VER_SUITE_DATACENTER 0x0080 +#define VER_SUITE_SINGLEUSERTS 0x0100 +#define VER_SUITE_PERSONAL 0x0200 +#define VER_SUITE_BLADE 0x0400 +#define VER_SUITE_EMBEDDED_RESTRICTED 0x0800 +#define VER_SUITE_SECURITY_APPLIANCE 0x1000 +#define VER_SUITE_STORAGE_SERVER 0x2000 +#define VER_SUITE_COMPUTE_SERVER 0x4000 +#define VER_SUITE_WH_SERVER 0x8000 +//! \} + +//! \brief The maximum number of exception parameters present in the +//! MINIDUMP_EXCEPTION::ExceptionInformation array. +#define EXCEPTION_MAXIMUM_PARAMETERS 15 + +//! \anchor PROCESSOR_ARCHITECTURE_x +//! \name PROCESSOR_ARCHITECTURE_* +//! +//! \brief CPU type values for MINIDUMP_SYSTEM_INFO::ProcessorArchitecture. +//! +//! \sa crashpad::MinidumpCPUArchitecture +//! \{ +#define PROCESSOR_ARCHITECTURE_INTEL 0 +#define PROCESSOR_ARCHITECTURE_MIPS 1 +#define PROCESSOR_ARCHITECTURE_ALPHA 2 +#define PROCESSOR_ARCHITECTURE_PPC 3 +#define PROCESSOR_ARCHITECTURE_SHX 4 +#define PROCESSOR_ARCHITECTURE_ARM 5 +#define PROCESSOR_ARCHITECTURE_IA64 6 +#define PROCESSOR_ARCHITECTURE_ALPHA64 7 +#define PROCESSOR_ARCHITECTURE_MSIL 8 +#define PROCESSOR_ARCHITECTURE_AMD64 9 +#define PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 10 +#define PROCESSOR_ARCHITECTURE_NEUTRAL 11 +#define PROCESSOR_ARCHITECTURE_ARM64 12 +#define PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64 13 +#define PROCESSOR_ARCHITECTURE_UNKNOWN 0xffff +//! \} + +//! \anchor PF_x +//! \name PF_* +//! +//! \brief CPU feature values for \ref CPU_INFORMATION::ProcessorFeatures +//! "CPU_INFORMATION::OtherCpuInfo::ProcessorFeatures". +//! +//! \{ +#define PF_FLOATING_POINT_PRECISION_ERRATA 0 +#define PF_FLOATING_POINT_EMULATED 1 +#define PF_COMPARE_EXCHANGE_DOUBLE 2 +#define PF_MMX_INSTRUCTIONS_AVAILABLE 3 +#define PF_PPC_MOVEMEM_64BIT_OK 4 +#define PF_ALPHA_BYTE_INSTRUCTIONS 5 +#define PF_XMMI_INSTRUCTIONS_AVAILABLE 6 +#define PF_3DNOW_INSTRUCTIONS_AVAILABLE 7 +#define PF_RDTSC_INSTRUCTION_AVAILABLE 8 +#define PF_PAE_ENABLED 9 +#define PF_XMMI64_INSTRUCTIONS_AVAILABLE 10 +#define PF_SSE_DAZ_MODE_AVAILABLE 11 +#define PF_NX_ENABLED 12 +#define PF_SSE3_INSTRUCTIONS_AVAILABLE 13 +#define PF_COMPARE_EXCHANGE128 14 +#define PF_COMPARE64_EXCHANGE128 15 +#define PF_CHANNELS_ENABLED 16 +#define PF_XSAVE_ENABLED 17 +#define PF_ARM_VFP_32_REGISTERS_AVAILABLE 18 +#define PF_ARM_NEON_INSTRUCTIONS_AVAILABLE 19 +#define PF_SECOND_LEVEL_ADDRESS_TRANSLATION 20 +#define PF_VIRT_FIRMWARE_ENABLED 21 +#define PF_RDWRFSGSBASE_AVAILABLE 22 +#define PF_FASTFAIL_AVAILABLE 23 +#define PF_ARM_DIVIDE_INSTRUCTION_AVAILABLE 24 +#define PF_ARM_64BIT_LOADSTORE_ATOMIC 25 +#define PF_ARM_EXTERNAL_CACHE_AVAILABLE 26 +#define PF_ARM_FMAC_INSTRUCTIONS_AVAILABLE 27 +#define PF_RDRAND_INSTRUCTION_AVAILABLE 28 +#define PF_ARM_V8_INSTRUCTIONS_AVAILABLE 29 +#define PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE 30 +#define PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE 31 +#define PF_RDTSCP_INSTRUCTION_AVAILABLE 32 +//! \} + +//! \anchor PAGE_x +//! \name PAGE_* +//! +//! \brief Memory protection constants for MINIDUMP_MEMORY_INFO::Protect and +//! MINIDUMP_MEMORY_INFO::AllocationProtect. +//! \{ +#define PAGE_NOACCESS 0x1 +#define PAGE_READONLY 0x2 +#define PAGE_READWRITE 0x4 +#define PAGE_WRITECOPY 0x8 +#define PAGE_EXECUTE 0x10 +#define PAGE_EXECUTE_READ 0x20 +#define PAGE_EXECUTE_READWRITE 0x40 +#define PAGE_EXECUTE_WRITECOPY 0x80 +#define PAGE_GUARD 0x100 +#define PAGE_NOCACHE 0x200 +#define PAGE_WRITECOMBINE 0x400 +//! \} + +//! \anchor MEM_x +//! \name MEM_* +//! +//! \brief Memory state and type constants for MINIDUMP_MEMORY_INFO::State and +//! MINIDUMP_MEMORY_INFO::Type. +//! \{ +#define MEM_COMMIT 0x1000 +#define MEM_RESERVE 0x2000 +#define MEM_DECOMMIT 0x4000 +#define MEM_RELEASE 0x8000 +#define MEM_FREE 0x10000 +#define MEM_PRIVATE 0x20000 +#define MEM_MAPPED 0x40000 +#define MEM_RESET 0x80000 +//! \} + +//! \brief The maximum number of distinct identifiable features that could +//! possibly be carried in an XSAVE area. +//! +//! This corresponds to the number of bits in the XSAVE state-component bitmap, +//! XSAVE_BV. See Intel Software Developer’s Manual, Volume 1: Basic +//! Architecture (253665-060), 13.4.2 “XSAVE Headerâ€. +#define MAXIMUM_XSTATE_FEATURES (64) + +//! \brief The location of a single state component within an XSAVE area. +struct XSTATE_FEATURE { + //! \brief The location of a state component within a CPU-specific context + //! structure. + //! + //! This is equivalent to the difference (`ptrdiff_t`) between the return + //! value of `LocateXStateFeature()` and its \a Context argument. + uint32_t Offset; + + //! \brief The size of a state component with a CPU-specific context + //! structure. + //! + //! This is equivalent to the size returned by `LocateXStateFeature()` in \a + //! Length. + uint32_t Size; +}; + +//! \anchor IMAGE_DEBUG_MISC_x +//! \name IMAGE_DEBUG_MISC_* +//! +//! Data type values for IMAGE_DEBUG_MISC::DataType. +//! \{ + +//! \brief A pointer to a `.dbg` file. +//! +//! IMAGE_DEBUG_MISC::Data will contain the path or file name of the `.dbg` file +//! associated with the module. +#define IMAGE_DEBUG_MISC_EXENAME 1 + +//! \} + +//! \brief Miscellaneous debugging record. +//! +//! This structure is referenced by MINIDUMP_MODULE::MiscRecord. It is obsolete, +//! superseded by the CodeView record. +struct IMAGE_DEBUG_MISC { + //! \brief The type of data carried in the #Data field. + //! + //! This is a value of \ref IMAGE_DEBUG_MISC_x "IMAGE_DEBUG_MISC_*". + uint32_t DataType; + + //! \brief The length of this structure in bytes, including the entire #Data + //! field and its `NUL` terminator. + //! + //! \note The Windows documentation states that this field is rounded up to + //! nearest nearest 4-byte multiple. + uint32_t Length; + + //! \brief The encoding of the #Data field. + //! + //! If this field is `0`, #Data contains narrow or multibyte character data. + //! If this field is `1`, #Data is UTF-16-encoded. + //! + //! On Windows, with this field set to `0`, #Data will be encoded in the code + //! page of the system that linked the module. On other operating systems, + //! UTF-8 may be used. + uint8_t Unicode; + + uint8_t Reserved[3]; + + //! \brief The data carried within this structure. + //! + //! For string data, this field will be `NUL`-terminated. If #Unicode is `1`, + //! this field is UTF-16-encoded, and will be terminated by a UTF-16 `NUL` + //! code unit (two `NUL` bytes). + uint8_t Data[1]; +}; + +//! \anchor VER_NT_x +//! \name VER_NT_* +//! +//! \brief Operating system type values for MINIDUMP_SYSTEM_INFO::ProductType. +//! +//! \sa crashpad::MinidumpOSType +//! \{ +#define VER_NT_WORKSTATION 1 +#define VER_NT_DOMAIN_CONTROLLER 2 +#define VER_NT_SERVER 3 +//! \} + +//! \anchor VER_PLATFORM_x +//! \name VER_PLATFORM_* +//! +//! \brief Operating system family values for MINIDUMP_SYSTEM_INFO::PlatformId. +//! +//! \sa crashpad::MinidumpOS +//! \{ +#define VER_PLATFORM_WIN32s 0 +#define VER_PLATFORM_WIN32_WINDOWS 1 +#define VER_PLATFORM_WIN32_NT 2 +//! \} + +#endif // CRASHPAD_COMPAT_NON_WIN_WINNT_H_ diff --git a/shared/sentry/external/crashpad/compat/win/getopt.h b/shared/sentry/external/crashpad/compat/win/getopt.h new file mode 100644 index 000000000..45fcbccc3 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/win/getopt.h @@ -0,0 +1,20 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_WIN_GETOPT_H_ +#define CRASHPAD_COMPAT_WIN_GETOPT_H_ + +#include "third_party/getopt/getopt.h" + +#endif // CRASHPAD_COMPAT_WIN_GETOPT_H_ diff --git a/shared/sentry/external/crashpad/compat/win/strings.cc b/shared/sentry/external/crashpad/compat/win/strings.cc new file mode 100644 index 000000000..6af256e65 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/win/strings.cc @@ -0,0 +1,25 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 + +#include + +extern "C" { + +int strcasecmp(const char* s1, const char* s2) { + return _stricmp(s1, s2); +} + +} // extern "C" diff --git a/shared/sentry/external/crashpad/compat/win/strings.h b/shared/sentry/external/crashpad/compat/win/strings.h new file mode 100644 index 000000000..50360b16d --- /dev/null +++ b/shared/sentry/external/crashpad/compat/win/strings.h @@ -0,0 +1,28 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_WIN_STRINGS_H_ +#define CRASHPAD_COMPAT_WIN_STRINGS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +int strcasecmp(const char* s1, const char* s2); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // CRASHPAD_COMPAT_WIN_STRINGS_H_ diff --git a/shared/sentry/external/crashpad/compat/win/sys/time.h b/shared/sentry/external/crashpad/compat/win/sys/time.h new file mode 100644 index 000000000..71ddcdc21 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/win/sys/time.h @@ -0,0 +1,20 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_WIN_SYS_TIME_H_ +#define CRASHPAD_COMPAT_WIN_SYS_TIME_H_ + +#include + +#endif // CRASHPAD_COMPAT_WIN_SYS_TIME_H_ diff --git a/shared/sentry/external/crashpad/compat/win/sys/types.h b/shared/sentry/external/crashpad/compat/win/sys/types.h new file mode 100644 index 000000000..e8fae88f4 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/win/sys/types.h @@ -0,0 +1,23 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_WIN_SYS_TYPES_H_ +#define CRASHPAD_COMPAT_WIN_SYS_TYPES_H_ + +// This is intended to be roughly equivalent to #include_next. +#include <../ucrt/sys/types.h> + +#include + +#endif // CRASHPAD_COMPAT_WIN_SYS_TYPES_H_ diff --git a/shared/sentry/external/crashpad/compat/win/time.cc b/shared/sentry/external/crashpad/compat/win/time.cc new file mode 100644 index 000000000..e6738879a --- /dev/null +++ b/shared/sentry/external/crashpad/compat/win/time.cc @@ -0,0 +1,40 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 + +extern "C" { + +struct tm* gmtime_r(const time_t* timep, struct tm* result) { + if (gmtime_s(result, timep) != 0) + return nullptr; + return result; +} + +struct tm* localtime_r(const time_t* timep, struct tm* result) { + if (localtime_s(result, timep) != 0) + return nullptr; + return result; +} + +const char* strptime(const char* buf, const char* format, struct tm* tm) { + // TODO(scottmg): strptime implementation. + return nullptr; +} + +time_t timegm(struct tm* tm) { + return _mkgmtime(tm); +} + +} // extern "C" diff --git a/shared/sentry/external/crashpad/compat/win/time.h b/shared/sentry/external/crashpad/compat/win/time.h new file mode 100644 index 000000000..37048daf7 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/win/time.h @@ -0,0 +1,37 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_WIN_TIME_H_ +#define CRASHPAD_COMPAT_WIN_TIME_H_ + +// This is intended to be roughly equivalent to #include_next. +#include <../ucrt/time.h> + +#ifdef __cplusplus +extern "C" { +#endif + +struct tm* gmtime_r(const time_t* timep, struct tm* result); + +struct tm* localtime_r(const time_t* timep, struct tm* result); + +const char* strptime(const char* buf, const char* format, struct tm* tm); + +time_t timegm(struct tm* tm); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // CRASHPAD_COMPAT_WIN_TIME_H_ diff --git a/shared/sentry/external/crashpad/compat/win/winbase.h b/shared/sentry/external/crashpad/compat/win/winbase.h new file mode 100644 index 000000000..ffd850bf2 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/win/winbase.h @@ -0,0 +1,27 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_WIN_WINBASE_H_ +#define CRASHPAD_COMPAT_WIN_WINBASE_H_ + +// include_next +#include <../um/winbase.h> + +// 10.0.15063.0 SDK + +#ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE +#define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE (0x2) +#endif + +#endif // CRASHPAD_COMPAT_WIN_WINBASE_H_ diff --git a/shared/sentry/external/crashpad/compat/win/winnt.h b/shared/sentry/external/crashpad/compat/win/winnt.h new file mode 100644 index 000000000..34ea80f91 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/win/winnt.h @@ -0,0 +1,60 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_WIN_WINNT_H_ +#define CRASHPAD_COMPAT_WIN_WINNT_H_ + +// include_next +#include <../um/winnt.h> + +// https://msdn.microsoft.com/library/aa373184.aspx: "Note that this structure +// definition was accidentally omitted from WinNT.h." +struct PROCESSOR_POWER_INFORMATION { + ULONG Number; + ULONG MaxMhz; + ULONG CurrentMhz; + ULONG MhzLimit; + ULONG MaxIdleState; + ULONG CurrentIdleState; +}; + +// 10.0.10240.0 SDK + +#ifndef PROCESSOR_ARCHITECTURE_ARM64 +#define PROCESSOR_ARCHITECTURE_ARM64 12 +#endif + +#ifndef PF_ARM_V8_INSTRUCTIONS_AVAILABLE +#define PF_ARM_V8_INSTRUCTIONS_AVAILABLE 29 +#endif + +#ifndef PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE +#define PF_ARM_V8_CRYPTO_INSTRUCTIONS_AVAILABLE 30 +#endif + +#ifndef PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE +#define PF_ARM_V8_CRC32_INSTRUCTIONS_AVAILABLE 31 +#endif + +#ifndef PF_RDTSCP_INSTRUCTION_AVAILABLE +#define PF_RDTSCP_INSTRUCTION_AVAILABLE 32 +#endif + +// 10.0.14393.0 SDK + +#ifndef PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64 +#define PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64 13 +#endif + +#endif // CRASHPAD_COMPAT_WIN_WINNT_H_ diff --git a/shared/sentry/external/crashpad/compat/win/winternl.h b/shared/sentry/external/crashpad/compat/win/winternl.h new file mode 100644 index 000000000..ee4f53c65 --- /dev/null +++ b/shared/sentry/external/crashpad/compat/win/winternl.h @@ -0,0 +1,25 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_COMPAT_WIN_WINTERNL_H_ +#define CRASHPAD_COMPAT_WIN_WINTERNL_H_ + +// include_next +#include <../um/winternl.h> + +// 10.0.16299.0 SDK + +typedef struct _CLIENT_ID CLIENT_ID; + +#endif // CRASHPAD_COMPAT_WIN_WINTERNL_H_ diff --git a/shared/sentry/external/crashpad/crashpad-config.cmake.in b/shared/sentry/external/crashpad/crashpad-config.cmake.in new file mode 100644 index 000000000..846797d77 --- /dev/null +++ b/shared/sentry/external/crashpad/crashpad-config.cmake.in @@ -0,0 +1,10 @@ +include("${CMAKE_CURRENT_LIST_DIR}/crashpad-targets.cmake") + +if(@CRASHPAD_ZLIB_SYSTEM@) + find_package(ZLIB REQUIRED) + target_include_directories(crashpad::zlib INTERFACE ${ZLIB_INCLUDE_DIRS}) + target_compile_definitions(crashpad::zlib INTERFACE ${ZLIB_COMPILE_DEFINITIONS}) + target_link_libraries(crashpad::zlib INTERFACE ${ZLIB_LIBRARIES}) +endif() + +@PACKAGE_INIT@ diff --git a/shared/sentry/external/crashpad/doc/.gitignore b/shared/sentry/external/crashpad/doc/.gitignore new file mode 100644 index 000000000..e324eac91 --- /dev/null +++ b/shared/sentry/external/crashpad/doc/.gitignore @@ -0,0 +1 @@ +/generated diff --git a/shared/sentry/external/crashpad/doc/appengine/README b/shared/sentry/external/crashpad/doc/appengine/README new file mode 100644 index 000000000..ee0e319ad --- /dev/null +++ b/shared/sentry/external/crashpad/doc/appengine/README @@ -0,0 +1,51 @@ +This is the App Engine app that serves https://crashpad.chromium.org/. + +To work on this app, obtain the following packages: + + - Go, from https://golang.org/dl/. This is only necessary for local development + and testing. The directory containing the “goâ€Â executable, such as + /usr/local/go/bin, must appear in $PATH. It does not appear critical for the + Go version used to match the Go runtime version used (for example, these + instructions were tested with Go 1.14 locally but a Go 1.11 runtime when + deployed), but if problems are encountered, it would be wise to use the same + version for both local development and AppEngine deployment. + - The Google Cloud SDK from, https://cloud.google.com/sdk/docs. This is + necessary for both local development and for AppEngine deployment. Unpacking + this package produces a google-cloud-sdk directory, whose bin child directory + may be added to $PATH for convenience, although this is not strictly + necessary. + +The commands in this README are expected to be run from the directory containing +it. $GOPATH must also be set to include this directory: + +% export GOPATH="$(go env GOPATH):$(pwd)" + +To test locally: + +% go get -d crashpad-home +% …/google-cloud-sdk/bin/dev_appserver.py src/crashpad-home + +Look for the “Starting module "default" running at: http://localhost:8080†line, +which tells you the URL of the local running instance of the app. Test +http://localhost:8080/ and http://localhost:8080/doxygen to ensure that they +work. + +To deploy: + +% version=$(git rev-parse --short=12 HEAD) +% [[ -n "$(git status --porcelain)" ]] && version+=-dirty +% …/google-cloud-sdk/bin/gcloud app deploy \ + --project=crashpad-home --version="${version}" --no-promote \ + "$(pwd)/src/crashpad-home" + +(Note: the $(pwd) is necessary for “gcloud app deploy†to recognize that the +application is in GOPATH, putting it into “GOPATH modeâ€. This normally happens +correctly on its own even with a relative path, but will fail for relative +paths when $(pwd) is a symbolic link. Using an absolute path here will save you +from this frustration, freeing you up to undoubtedly experience other +frustrations.) + +Activate a newly-deployed version by visiting the App Engine console at +https://console.cloud.google.com/appengine/versions?project=crashpad-home, +selecting it, and choosing “Migrate Trafficâ€. It is also possible to delete old +versions from this page when they are no longer needed. diff --git a/shared/sentry/external/crashpad/doc/appengine/src/crashpad-home/app.yaml b/shared/sentry/external/crashpad/doc/appengine/src/crashpad-home/app.yaml new file mode 100644 index 000000000..82f61675e --- /dev/null +++ b/shared/sentry/external/crashpad/doc/appengine/src/crashpad-home/app.yaml @@ -0,0 +1,20 @@ +# Copyright 2015 The Crashpad Authors. All rights reserved. +# +# 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. + +runtime: go111 + +handlers: +- url: /.* + script: auto + secure: always diff --git a/shared/sentry/external/crashpad/doc/appengine/src/crashpad-home/main.go b/shared/sentry/external/crashpad/doc/appengine/src/crashpad-home/main.go new file mode 100644 index 000000000..5ce7d0ab9 --- /dev/null +++ b/shared/sentry/external/crashpad/doc/appengine/src/crashpad-home/main.go @@ -0,0 +1,161 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +package main + +import ( + "encoding/base64" + "fmt" + "io" + "io/ioutil" + "net/http" + "net/url" + "path" + "strings" + "time" + + "google.golang.org/appengine" + "google.golang.org/appengine/memcache" + "google.golang.org/appengine/urlfetch" +) + +func main() { + appengine.Main() +} + +func init() { + http.HandleFunc("/", handler) +} + +func handler(w http.ResponseWriter, r *http.Request) { + const ( + baseURL = "https://chromium.googlesource.com/crashpad/crashpad/+/" + masterBaseURL = baseURL + "master/" + generatedDocBaseURL = baseURL + "doc/doc/generated/?format=TEXT" + bugBaseURL = "https://bugs.chromium.org/p/crashpad/" + ) + + redirectMap := map[string]string{ + "/": masterBaseURL + "README.md", + "/bug": bugBaseURL, + "/bug/": bugBaseURL, + "/bug/new": bugBaseURL + "issues/entry", + "/doc/developing.html": masterBaseURL + "/doc/developing.md", + "/doc/status.html": masterBaseURL + "/doc/status.md", + "/index.html": masterBaseURL + "README.md", + "/man": masterBaseURL + "doc/man.md", + "/man/": masterBaseURL + "doc/man.md", + "/man/catch_exception_tool.html": masterBaseURL + "tools/mac/catch_exception_tool.md", + "/man/crashpad_database_util.html": masterBaseURL + "tools/crashpad_database_util.md", + "/man/crashpad_handler.html": masterBaseURL + "handler/crashpad_handler.md", + "/man/exception_port_tool.html": masterBaseURL + "tools/mac/exception_port_tool.md", + "/man/generate_dump.html": masterBaseURL + "tools/generate_dump.md", + "/man/index.html": masterBaseURL + "doc/man.md", + "/man/on_demand_service_tool.html": masterBaseURL + "tools/mac/on_demand_service_tool.md", + "/man/run_with_crashpad.html": masterBaseURL + "tools/mac/run_with_crashpad.md", + } + + ctx := appengine.NewContext(r) + client := urlfetch.Client(ctx) + + destinationURL, exists := redirectMap[r.URL.Path] + if exists { + http.Redirect(w, r, destinationURL, http.StatusFound) + return + } + + if strings.HasPrefix(r.URL.Path, "/bug/") { + http.Redirect(w, r, bugBaseURL+"issues/detail?id="+r.URL.Path[5:], http.StatusFound) + return + } + + // Don’t show dotfiles. + if strings.HasPrefix(path.Base(r.URL.Path), ".") { + http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound) + return + } + + u, err := url.Parse(generatedDocBaseURL) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + u.Path = path.Join(u.Path, r.URL.Path) + urlStr := u.String() + + item, err := memcache.Get(ctx, urlStr) + if err == memcache.ErrCacheMiss { + resp, err := client.Get(urlStr) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + w.WriteHeader(resp.StatusCode) + for k, v := range w.Header() { + w.Header()[k] = v + } + io.Copy(w, resp.Body) + return + } + + // Redirect directories to their index pages (/doc/ -> /doc/index.html). + if resp.Header.Get("X-Gitiles-Object-Type") == "tree" { + http.Redirect(w, r, path.Join(r.URL.Path, "/index.html"), http.StatusFound) + return + } + + decoder := base64.NewDecoder(base64.StdEncoding, resp.Body) + b, err := ioutil.ReadAll(decoder) + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + item = &memcache.Item{ + Key: urlStr, + Value: b, + Expiration: 1 * time.Hour, + } + if err := memcache.Set(ctx, item); err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + } else if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", contentType(path.Base(u.Path))) + fmt.Fprintf(w, "%s", item.Value) +} + +// contentType returns the appropriate content type header for file. +func contentType(file string) string { + contentTypes := map[string]string{ + ".html": "text/html; charset=UTF-8", + ".css": "text/css; charset=UTF-8", + ".js": "text/javascript; charset=UTF-8", + ".png": "image/png", + ".ico": "image/x-icon", + } + for suffix, typ := range contentTypes { + if strings.HasSuffix(file, suffix) { + return typ + } + } + return "text/plain; charset=UTF-8" +} diff --git a/shared/sentry/external/crashpad/doc/developing.md b/shared/sentry/external/crashpad/doc/developing.md new file mode 100644 index 000000000..a4017fb78 --- /dev/null +++ b/shared/sentry/external/crashpad/doc/developing.md @@ -0,0 +1,327 @@ + + +# Developing Crashpad + +## Status + +[Project status](status.md) information has moved to its own page. + +## Introduction + +Crashpad is a [Chromium project](https://www.chromium.org/Home). Most of its +development practices follow Chromium’s. In order to function on its own in +other projects, Crashpad uses +[mini_chromium](https://chromium.googlesource.com/chromium/mini_chromium/), a +small, self-contained library that provides many of Chromium’s useful low-level +base routines. [mini_chromium’s +README](https://chromium.googlesource.com/chromium/mini_chromium/+/master/README.md) +provides more detail. + +## Prerequisites + +To develop Crashpad, the following tools are necessary, and must be present in +the `$PATH` environment variable: + + * Appropriate development tools. + * On macOS, install [Xcode](https://developer.apple.com/xcode/). The latest + version is generally recommended. + * On Windows, install [Visual Studio](https://www.visualstudio.com/) with + C++ support and the Windows SDK. The latest version is generally + recommended. Some tests also require the CDB debugger, installed with + [Debugging Tools for + Windows](https://docs.microsoft.com/en-us/windows-hardware/drivers/debugger/). + * On Linux, obtain appropriate tools for C++ development through any + appropriate means including the system’s package manager. On Debian and + Debian-based distributions, the `build-essential`, `zlib1g-dev`, and any + one of the `libcurl4-*-dev` packages such as `libcurl4-openssl-dev` should + suffice. + * Chromium’s + [depot_tools](https://www.chromium.org/developers/how-tos/depottools). + * [Git](https://git-scm.com/). This is provided by Xcode on macOS, by + depot_tools on Windows, and through any appropriate means including the + system’s package manager on Linux. + * [Python](https://www.python.org/). This is provided by the operating system + on macOS, by depot_tools on Windows, and through any appropriate means + including the system’s package manager on Linux. + +## Getting the Source Code + +The main source code repository is a Git repository hosted at +https://chromium.googlesource.com/crashpad/crashpad. Although it is possible to +check out this repository directly with `git clone`, Crashpad’s dependencies are +managed by +[`gclient`](https://www.chromium.org/developers/how-tos/depottools#TOC-gclient) +instead of Git submodules, so to work on Crashpad, it is best to use `fetch` to +get the source code. + +`fetch` and `gclient` are part of the +[depot_tools](https://www.chromium.org/developers/how-tos/depottools). There’s +no need to install them separately. + +### Initial Checkout + +``` +$ mkdir ~/crashpad +$ cd ~/crashpad +$ fetch crashpad +``` + +`fetch crashpad` performs the initial `git clone` and `gclient sync`, +establishing a fully-functional local checkout. + +### Subsequent Checkouts + +``` +$ cd ~/crashpad/crashpad +$ git pull -r +$ gclient sync +``` + +## Building + +### Windows, Mac, Linux, Fuchsia + +On Windows, Mac, Linux, and Fuchsia, Crashpad uses +[GN](https://gn.googlesource.com/gn) to generate +[Ninja](https://ninja-build.org/) build files. For example, + +``` +$ cd ~/crashpad/crashpad +$ gn gen out/Default +$ ninja -C out/Default +``` + +You can then use `gn args out/Default` or edit `out/Default/args.gn` to +configure the build, for example things like `is_debug=true` or +`target_cpu="x86"`. + +GN and Ninja are part of the +[depot_tools](https://www.chromium.org/developers/how-tos/depottools). There’s +no need to install them separately. + +#### Fuchsia + +In order to instruct gclient to download the Fuchsia SDK, you need to add the +following to `~/crashpad/.gclient`: + +``` +target_os=["fuchsia"] +``` + +If you're using this tree to develop for multiple targets, you can also add +other entries to the the list (e.g. `target_os=["fuchsia", "mac"]`). + +#### Optional Linux Configs + +To pull and use Crashpad's version of clang and sysroot, make the following +changes. + +Add the following to `~/crashpad/.gclient`. +``` +"custom_vars": { "pull_linux_clang": True }, +``` +Add these args to `out/Default/args.gn`. +``` +clang_path = "//third_party/linux/clang/linux-amd64" +target_sysroot = "//third_party/linux/sysroot" +``` + +### Android + +This build relies on cross-compilation. It’s possible to develop Crashpad for +Android on any platform that the [Android NDK (Native Development +Kit)](https://developer.android.com/ndk/) runs on. + +If it’s not already present on your system, [download the NDK package for your +system](https://developer.android.com/ndk/downloads/) and expand it to a +suitable location. These instructions assume that it’s been expanded to +`~/android-ndk-r21b`. + +Note that Chrome uses Android API level 21 for both 64-bit platforms and +32-bit platforms. See Chrome’s +[`build/config/android/config.gni`](https://chromium.googlesource.com/chromium/src/+/master/build/config/android/config.gni) +which sets `android32_ndk_api_level` and `android64_ndk_api_level`. + +Set these gn args +``` +target_os = "android" +android_ndk_root = ~/android-ndk-r21b +android_api_level = 21 +``` + +## Testing + +Crashpad uses [Google Test](https://github.com/google/googletest/) as its +unit-testing framework, and some tests use [Google +Mock](https://github.com/google/googletest/tree/master/googlemock/) as well. Its +tests are currently split up into several test executables, each dedicated to +testing a different component. This may change in the future. After a successful +build, the test executables will be found at `out/Debug/crashpad_*_test`. + +``` +$ cd ~/crashpad/crashpad +$ out/Debug/crashpad_minidump_test +$ out/Debug/crashpad_util_test +``` + +A script is provided to run all of Crashpad’s tests. It accepts a single +argument, a path to the directory containing the test executables. + +``` +$ cd ~/crashpad/crashpad +$ python build/run_tests.py out/Debug +``` + +To run a subset of the tests, use the `--gtest_filter` flag, e.g., to run all +the tests for MinidumpStringWriter: + +``` +$ python build/run_tests.py out/Debug --gtest_filter MinidumpStringWriter\* +``` + +### Windows + +On Windows, `end_to_end_test.py` requires the CDB debugger, installed with +[Debugging Tools for +Windows](https://docs.microsoft.com/windows-hardware/drivers/debugger/). This +can be installed either as part of the [Windows Driver +Kit](https://docs.microsoft.com/windows-hardware/drivers/download-the-wdk) or +the [Windows +SDK](https://developer.microsoft.com/windows/downloads/windows-10-sdk/). If the +Windows SDK has already been installed (possibly with Visual Studio) but +Debugging Tools for Windows is not present, it can be installed from Add or +remove programs→Windows Software Development Kit. + +### Android + +To test on Android, [ADB (Android Debug +Bridge)](https://developer.android.com/studio/command-line/adb.html) from the +[Android SDK](https://developer.android.com/sdk/) must be in the `PATH`. Note +that it is sufficient to install just the command-line tools from the Android +SDK. The entire Android Studio IDE is not necessary to obtain ADB. + +When asked to test an Android build directory, `run_tests.py` will detect a +single connected Android device (including an emulator). If multiple devices are +connected, one may be chosen explicitly with the `ANDROID_DEVICE` environment +variable. `run_tests.py` will upload test executables and data to a temporary +location on the detected or selected device, run them, and clean up after itself +when done. + +### Fuchsia + +To test on Fuchsia, you need a connected device running Fuchsia. Run: + +``` +$ gn gen out/fuchsia --args 'target_os="fuchsia" target_cpu="x64" is_debug=true' +$ ninja -C out/fuchsia +$ python build/run_tests.py out/fuchsia +``` + +If you have multiple devices running, you will need to specify which device you +want using their hostname, for instance: + +``` +$ ZIRCON_NODENAME=scare-brook-skip-dried python build/run_tests.py out/fuchsia +``` + +## Contributing + +Crashpad’s contribution process is very similar to [Chromium’s contribution +process](https://chromium.googlesource.com/chromium/src/+/master/docs/contributing.md). + +### Code Review + +A code review must be conducted for every change to Crashpad’s source code. Code +review is conducted on [Chromium’s +Gerrit](https://chromium-review.googlesource.com/) system, and all code reviews +must be sent to an appropriate reviewer, with a Cc sent to +[crashpad-dev](https://groups.google.com/a/chromium.org/group/crashpad-dev). The +[`codereview.settings`](https://chromium.googlesource.com/crashpad/crashpad/+/master/codereview.settings) +file specifies this environment to `git-cl`. + +`git-cl` is part of the +[depot_tools](https://www.chromium.org/developers/how-tos/depottools). There’s +no need to install it separately. + +``` +$ cd ~/crashpad/crashpad +$ git checkout -b work_branch origin/master +…do some work… +$ git add … +$ git commit +$ git cl upload +``` + +Uploading a patch to Gerrit does not automatically request a review. You must +select a reviewer on the Gerrit review page after running `git cl upload`. This +action notifies your reviewer of the code review request. If you have lost track +of the review page, `git cl issue` will remind you of its URL. Alternatively, +you can request review when uploading to Gerrit by using `git cl upload +--send-mail`. + +Git branches maintain their association with Gerrit reviews, so if you need to +make changes based on review feedback, you can do so on the correct Git branch, +committing your changes locally with `git commit`. You can then upload a new +patch set with `git cl upload` and let your reviewer know you’ve addressed the +feedback. + +The most recently uploaded patch set on a review may be tested on a +[trybot](https://chromium.googlesource.com/chromium/src/+/master/docs/infra/trybot_usage.md) +by running `git cl try` or by clicking the “CQ Dry Runâ€Â button in Gerrit. These +set the “Commit-Queue: +1†label. This does not mean that the patch will be +committed, but the trybot and commit queue share infrastructure and a Gerrit +label. The patch will be tested on trybots in a variety of configurations. +Status information will be available on Gerrit. Trybot access is available to +Crashpad and Chromium committers. + +### Landing Changes + +After code review is complete and “Code-Review: +1†has been received from all +reviewers, the patch can be submitted to Crashpad’s [commit +queue](https://chromium.googlesource.com/chromium/src/+/master/docs/infra/cq.md) +by clicking the “Submit to CQ†button in Gerrit. This sets the “Commit-Queue: ++2†label, which tests the patch on trybots before landing it. Commit queue +access is available to Crashpad and Chromium committers. + +Although the commit queue is recommended, if needed, project members can bypass +the commit queue and land patches without testing by using the “Submit†button +in Gerrit or by committing via `git cl land`: + +``` +$ cd ~/crashpad/crashpad +$ git checkout work_branch +$ git cl land +``` + +### External Contributions + +Copyright holders must complete the [Individual Contributor License +Agreement](https://cla.developers.google.com/about/google-individual) or +[Corporate Contributor License +Agreement](https://cla.developers.google.com/about/google-corporate) as +appropriate before any submission can be accepted, and must be listed in the +[`AUTHORS`](https://chromium.googlesource.com/crashpad/crashpad/+/master/AUTHORS) +file. Contributors may be listed in the +[`CONTRIBUTORS`](https://chromium.googlesource.com/crashpad/crashpad/+/master/CONTRIBUTORS) +file. + +## Buildbot + +The [Crashpad Buildbot](https://ci.chromium.org/p/crashpad/g/main/console) +performs automated builds and tests of Crashpad. Before checking out or updating +the Crashpad source code, and after checking in a new change, it is prudent to +check the Buildbot to ensure that “the tree is green.†diff --git a/shared/sentry/external/crashpad/doc/favicon.ico b/shared/sentry/external/crashpad/doc/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..23c553a2966ca4cecf146093f33d8114b4f1e368 GIT binary patch literal 8348 zcmeI0du&tJ8Ng3Vwv|ewX4Hu@my&|vCD%DN@CLnxyptBT_%HjatoYr7%N3zGPed#@ckvA><;HWecDU4&2}*2(h%gm&WgCftWj zkQfW;&mXr@0S&(csd+cjaImbXSy=NC<10}z(efGwgi=;B$dt=HkKYCvp-#3KX+qIu zxx$>zcwk+N=c%1^J9)sO44lmS0##~wzU(43f>wW-p_YXwj>s>3{gHI*^oo1lmJ!X*bw{3UBAKRut zUh5Uy4_94IIkdxC^v{_g%FjuEI+f&;9KQ3i+nK7pU_RivFjP_DTl-&gP_qr$nD=M{ z@z;C3#y5Jsh77svGQ4u$ZX)t}=e52{#Xvk;4qIxNh86kR$OhCAie%&e`U{r{ACQZ@ z-)1#sItef{2L za&U;nm+_lo@lbR7vat&^EcECsH)uSnhlht@etsV4LE5B`j#GW*iur~}xpYlTtc;FC{nl|LK^miZ0y3WU>MIM zmc}94VzEFx9?#&?4l+h;ggzvOeCI$ob=`tBp*&EtirH2xU z4a#KB)IMa{_YdYrnxq>-DrrQ>rfpYe8@|Nc-oHnG*L(HR$}d4Eo2&X@o1~9l@%@W) zU{%rv$`tBAOHJH+?zcv7`!T~x;=<#7@4R5T^5$-%Pz-NAYt+5{d;>R+9W24y&`;ZkZqFGQ6_)<35Z_$5V#ga#=N9985-M0KR*fl@h4M0BxWvbYQr6t zULtYBAMJ%iU>x|?U8z_Zyv1jg_VcZ^kO)pd_)jk`_~2MHZmybbloW?l)lnMrb~TAX zV&%#e+VLvM4!%js+%B6}N!=udFlN5Jv;yQm0sdW({6ny+{{$_b`%pI&q3%#p3R?q( z%3@zpafPo4-GA}ErIfU@j?gpfdgeyI);;S-otz(GefQbXvG3J6hbwD_0*YOuqb1V8 zpQm{(oT|g$!tcw;Ck6l>=mo$z0J@0f0^U0{x?+mD8}QB{9yV7qQ;&$5^%*fb@qUB& zX(O<{D-aZ493K;1xH%!}9+}vt8Sshs4os9)d132glTa=lIJv}MJyU_Y!ZFl62j9@l zggg2y{y~c&Vm0cMa@}r@bbn?HR4Sa|66p;nlMl_6D%_%EjCNRq)NBvx!R!t`@zUoW zKV#O#EhZy4>~?VU+rfg@_W_4Kt~zS<|3JjVMcZ#exy;pDUypq|xjpD&0#H{BHgrvM zI=z9nnTN~NCX?mf@DOU;!82-#gXGsw5CSSf22lJ>u8duE&igGuZq4lVZf*LH2%QqOqkv@O{w`Y}q~yWm0EP zeSP~Hau49ZBU@&hWwH5YG0dnGVN1^iztQFh>rIvj5$iQ;q^nyuSuXo`U~{E8DpuIS zA{kR5oC9o=_>jhfRX@QTs1KRm{@F31 zFKP1!w?51@R^I~kA%H*B0W^sKnyVIsv`_2;=zbUGR9pOTqUhV{{^UH=RQ2@S?`uaQ zEy`)Gsd}1IEedW&4lAe0Sg5h`nQXqaZ}RyEzX{FT?hmF3>0_QQT1V~jI$wc&1@aZh ZS0G=3d706zJ{{SaIO|Jj| literal 0 HcmV?d00001 diff --git a/shared/sentry/external/crashpad/doc/ios_overview_design.md b/shared/sentry/external/crashpad/doc/ios_overview_design.md new file mode 100644 index 000000000..e3c1b7d1f --- /dev/null +++ b/shared/sentry/external/crashpad/doc/ios_overview_design.md @@ -0,0 +1,177 @@ + + +# iOS Crashpad Overview Design + +[TOC] + +## iOS Limitations + +Crashpad on other platforms captures exceptions out-of-process. The iOS sandbox, +however, restricts applications from delegating work to separate processes. +This limitation means Crashpad on iOS must combine the work of the handler and +the client into the same process as the main application. + +## The Crashpad In-Process Handler + +In-process handling comes with a number of limitations and difficulties. It is +not possible to catch the specific Mach exception `EXC_CRASH`, so certain groups +of crashes cannot be captured. This includes some major ones, like out-of-memory +crashes. This also introduces difficulties in capturing all the relevant crash +data and writing the minidump, as the process itself is in an unsafe state. + +While handling an exception, the handler may not, for example: + + - Allocate memory. + - Use libc, or most any library call. + +While handling an exception, the handler may only: + + - Use audited syscalls. + - access memory via `vm_read`. + +In conjunction with Crashpad’s existing minidump writer and structural +limitations of the minidump format, it is not possible to write a minidump +immediately from the crash handler. Instead, an intermediate dump is written +when a handler would normally write a minidump (such as during an exception or a +forced dump without crashing). The intermediate dump file will be converted to +a minidump on the next run (or when the application decides it's safe to do so). + +During Crashpad initialization, the handler gathers basic system information +and opens a pending intermediate dump adjacent to the Crashpad database. + +## The Crashpad IntermediateDump Format + +Due to the limitations of in-process handling, an intermediate dump file is +written during exceptions. The data is streamed to a file, which will be used to +generate a final minidump when appropriate. + +The file format is similar to binary JSON, supporting keyed properties, maps and +arrays. + + - `Property` [key:int, length:int, value:intarray] + - `StartMap` [key:int], followed by repeating Properties until `EndMap` + - `StartArray` [key:int], followed by repeating Maps until `EndArray` + - `EndMap`, `EndArray`, `EndDocument` + +Similar to JSON, maps can contain other maps, arrays and properties. + +## The life of an iOS crash report + +Immediately upon calling StartCrashpadInProcessHandler, the iOS in-process +handler is installed. This will open a temporary file within the database +directory, in a subdirectory named `pending-serialized-ios-dump`. This file will +be used to write an intermediate dump in the event of a crash. This must happen +before installing the various types of crash handlers, as each depends on having +a valid handler with an intermediate dump ready to be written to. + +After the in-process handler is initialized, the Mach exception, POSIX signal +and Objective-C exception preprocessor handlers are installed. + +### Intermediate Dump File Locking + +It is expected that multiple Crashpad clients may share the same database +directory, and this directory may be inside an iOS app group directory. While +it's possible for each Crashpad client to write to its own private directory, +if a shared directory is used, it's possible for different applications to +upload a crash report from any application in a shared group. This might be +used, for example, by an application and its various app extensions, where each +client may generate a crash report but only the main application uploads +reports. Alternatively, a suite of applications may upload each other's crash +reports. Otherwise, the only opportunity to upload a report would be when a +specific app that crashed relaunches. + +To prevent multiple clients from processing a pending intermediate dump, files +must be locked. However, POSIX locks on app group files will trigger app +termination on app backgrounding, so a custom file locking protocol is used. +Locked temporary files are named `@.locked`. The `.locked` +extension is removed when the file is unlocked. The `bundle-id` is used to +determine which Crashpad clients can process leftover locked files. + +### Writing Crashes to Intermediate Dumps + +When an app encounters a crash (via a Mach exception, Objective-C exception, or +a POSIX signal), an intermediate dump is written to the temporary locked file, +the .locked extension is removed, and a new temporary locked file is opened. + +App terminations not handled by Crashpad will leave behind a temporary +locked file, to be cleaned up on next launch. These files are still processed, +because it is possible for the app to be terminated while writing an +intermediate dump, and if enough data is written this may still be valuable. + +Note: Generally iOS apps are single-process, so it's safe for the client to +consider any files matching its `bundle-id`, but there are edge-cases (such as +if a share-to app extension is opened at the same time in two different apps) so +old locked files won't be cleared until after 24 hours. Any locked file found +after 60 days is unlocked regardless of `bundle-id`. + +### Writing to Intermediate Dumps without a Crash + +Apps may also generate intermediate dumps without a crash, often used for +debugging. Chromium makes heavy use of this for detecting main thread hangs, +something that can appear as a crash for the user, but is uncatchable for crash +handlers like Crashpad. When an app requests this (via DumpWithoutCrash, +DumpWithoutCrashAndDeferProcessing), an intermediate dump is written to the +temporary locked file, the .locked extension is removed, and a new temporary +locked file is opened. + +Note: DumpWithoutCrashAndDeferProcessingAtPath writes an intermediate dump to +the requested location, not the previously opened temporary file. This is useful +because Chromium's main thread hang detection will throw away hang reports in +certain circumstances (if the app recovers, if a different crash report is +written, etc). + +## The Crashpad In-Process Client + +Other Crashpad platforms handle exceptions and upload minidumps out-of-process. +On iOS, everything must happen in-process. Once started, the client will +automatically handle exceptions and capture the crashed process state in an +intermediate dump file. Converting that intermediate dump file into a minidump +is likely not safe to do from within a crashed process, and uploading a minidump +is definitely unsafe to do at crash time. Applications are expected to process +intermediate dumps into pending minidumps and begin processing pending +minidumps, possibly for upload, at suitable times following the next application +restart. + +Note: Applications are not required to call either of these methods. For +example, application extensions may choose to generate dumps but leave +processing and uploading to the main applications. Clients that share the +same database directory between apps can take advantage of processing and +uploading crash reports from different applications. + +### `ProcessIntermediateDumps` +For performance and stability reasons applications may choose the correct time +to convert intermediate dumps, as well as append metadata to the pending +intermediate dumps. This is expected to happen during application startup, when +suitable. After converting, a minidump will be written to the Crashpad database, +similar to how other platforms write a minidump on exception handling. If +uploading is enabled, this minidump will also be immediately uploaded. New +intermediate dumps generated by exceptions or by +`CRASHPAD_SIMULATE_CRASH_AND_DEFER_PROCESSING` will not be processed until +the next call to `ProcessIntermediateDumps`. Conversely, +`CRASHPAD_SIMULATE_CRASH` can be called when the client has no performance or +stability concerns. In this case, intermediate dumps are automatically +converted to minidumps and immediately eligible for uploading. + +Applications can include annotations here as well. Chromium uses this for its +insta-crash logic, which detects if an app is crashing repeatedly on startup. + +### `StartProcessingPendingReports` +For similar reasons, applications may choose the correct time to begin uploading +pending reports, such as when ideal network conditions exist. By default, +clients start with uploading disabled. Applications should call this API when +it is determined that it is appropriate to do so (such as on a few seconds after +startup, or when network connectivity is appropriate). diff --git a/shared/sentry/external/crashpad/doc/layering.png b/shared/sentry/external/crashpad/doc/layering.png new file mode 100644 index 0000000000000000000000000000000000000000..791bea5383d404e3bbfe08b0d1884e0e031e425d GIT binary patch literal 23460 zcmb5WbyStn*Dk7Jg0yrANOw0#H%LoM*QRCDpa{|>EhQ!0&8C!YX*NiA_om@2{C(dU zcZ_?_9pCxG;o#+6?^G%7M7@`DEtP-SH#R3AKe6#d}ALz(A~z>~lz z_BHU|LuXZKu?HoC#GBxU#}=ZBq7NRFN1|LCJpn(zaFEe@0^Odw^cFdDZrb;pciwXxtY7CFn&CV1IX+zOdL$ts zk$r+JcZY?E8SP}!PfbCgiW&w7PYXV?B?kq`aIui=GDuVY3@hgLa zg4B^rxWORn5C6|WyJhPjrXnsoLABqTO}B`Rtgb6CAv zsv7XaQL|0m``fr?Dxl%9=v;fvOj(tML5leJy#wPFveheQjRu9KPzi}x<}i3Hl@?1H z5vNy*&-fOX^+Xz*eUc`d?Xb^H1-jo!gkoV-P}Y|7+Dd4U!~C&<6tBy}kGiX-u1irwe%>p`4pnZXLa)v7Z`P_e#3f?qSBlx0d*c;EZB< zUY;QpGxMPFJJ+h$f-LDgg$5>Bo}V8s4|rtcb{R}kUb%Ad*xhZJWy_2P2OW`jLVB@) zw5601N!=#rt;bp;6n!M>B$rEyw;_0?R`421)dOp;myOCK9M-eV>IC|N%NhCF+4>2j z#!eag3z#IZGtow0tm_9Fli`K!E7(Q53|m@~@@`XVE?m`_IvtF;gXP*~6pj=WnO=cx z$?W^$e?7B*Kd)7?sd1mJTQBqa()O`0FCkim#gJEEItXPn)1arbbM&X$R;38U#7V#D zC{wSk6=O(TO8HNzCK3U3a;>`E>LI3zaO`+ucox*M+vGyrTCr9^;ag_+`l6LyxdQSL z@A>DB*D+Ix_$sy|f6oO5GKcLAY;ftU&6exnyN^jts0WH>%91vsl%7xhI{PBQ*LtV9 zbUbh?E=_jU1nWHgvE3Ob1M4W1khqZ3G^x2$Y18|C z>T2}fgWd_D*AqWD-F94XW{bL#O=Kk~R}+E62)S+esw{MCcazJQ%8-8KWcuJ#vSAbd zj6CDpb|3SetST2D8yl|=63vnP66aJ^a?&ezU-93R3EjV(hs;;6>r8&J+?kvFIQ>_$c`nxs?>h;vMu`~J z&Kq3KN{mk~a@*64?+LtE^7J}-sc#g935IBC28$kCF4UXNM1Z3sH{9p@kSDr(X-UB8 zrC0h;T$$|b?Sd0vg=xqZu)$p-b#a`ojqpK~$esD% zPHqyIF1%m5qT05pa>kyhWt4HGz}uoK3)3CpI1h8RyX1o_g?n6x!A$9F0&{+$`$d^A z^4hh0yrG!M@19X2nXU;K98tzb z`cq+r&2hw3h=)$bL-1?HX_`2_k|sLG2o3m_17cFdt0Z_dN^Q0L7O}`m&Fu0{Pob|X z=jL{EAC*-t&|F8y{llM=rRPCB;T{CRZ_}cp92Wa!D~GXJoh2M66$`QOl41#!*2-!Z zyzKJ7oPIkMc!gOO9Hg$o-3~D=U!~WiSFby`P1Q}Vv8*)x!YCw}J@)6%RL!7!-xgKg zrAKNhD`K5QCp9KYFm>tF-Ixxq%w5E)7?_P%e88MAN_92J22fZ z;*Nny6^+UU1CAoQam}Qddq=p!W?RQp#!}+r7+qHA`-o>^F7v-Aze#DkaJl5iJ|7MF zXx;OdAl|$wXF|Cuq_0_LTlV0~oGsDomp0b8#gnwF+fmq9OXFkX67}!MQB~fl#pncq z;G+Y!T@TERvB(^*SpK$^yJ(*G^3aPP=e|6~nC8U@kYJ z?~ZV(xlhuo*3!D&HNocvtX<_yUo{{oXoOtAg!?6-uu`SWFzO)6RdRSlcsScO9GdAa zGDNv&%m2}KswhWg8{zG1$Nt0l*tF{qq3@wY%Jn>Y#yBdt!xSFZ#`V&g5^!F42bb>cT`3>=z1s2TmH_A6! zbM2tvkaOW9vc-qltKPk_N2W8kwb$+X2A^GTzT7r^8Tfq60sL1=)03xvlg!t%4XgP_ z4h!*`z`+r5+6L>3{dHs=pLlU2i9)R$6xrg@LnKswLCC`+g~Hu1L@+cpReL0Cc_`#d z&AnzDgWI{JCB+^%8IB#@+p{JpKtSx?;fNdMJCw$YHB1<(uRD24{DFG<$pD4q9}X+l z&MH|-wjbY%(7oWOOl~qbs)7)?#~)tR6d5d8a=cjAFz6EcdbWvyhZlHF^pN-bRs^_~ zB=CUn=o}~-aO~}8AKsoQ?V& zGZH^ff}wfPM(7I_)}l$;bAun|(G+GUy~>>XfqaZf0^%o0+^)kQSn+dR9R4xTa&d8K zOej6uERo_md`o6Jmq~=F;uDp{mVA`0CXLPsp@)S|j}*BULQ{EQsSc;Slm_2)O$Dfq z+|Pl>Lfrbu`V<=c&7E1Lr2JhSVgs_8b;0?k!RJ75m<7WTCnW0+X-aRKI!`~0^>qWY zLcw8%jMyK5h~P%d?`qPvmt~9wqKhcC2~^BWke%LCM8}Ef6uqCaY5kLzmk_|o8C9BP zHq)>=)wQ&Y&8VPwV2}gG;3VjKPW=?0Tn%4C56EwxMR{D zi%j`m(vOgsN?y6z2|0EvdN9pq8Z-xmdoyUTKMDR`$9M0^4$blm#qy-`-j--)ncLVC@-*8X1lPrdQ$iC} zZeSmrc^T*5$z(a?vo~xk`}Wx*vQ>(wUG8IEUk%9>Dk+=oPSkkl8pTW}VuVrEG_KDl z1v3-V$aAH1^t4b@x}w>)OH(0$CzVwkTJlyRR@rpTfF+xkx_HZDwvML7pXh@5H%-J~ z;9ouQbU`t{)kgO-=7dv_QVh?PS;_b1JO8oW9dQR??e!TUE952GpYE6_&nNTf9*2U& z&$jox6{A&V(JxWdGIDH6AM!?+&<|xji`bdRIc2W^!Np-i`T^QOOZ~7rxhhZFWw^@R z4)oFZpD1ZWA2&pIU$5X}Qj9cOW2n209bZS8op`cr76wfNg7kwK2bopUI;Ly?XmFR>T-Hifjlo)U3ECfK>T&3CZ^Nav3@XzbX1?36|2oJ;wl&0mYfL%CYlbh+-g)!-|M)hKCnO{OZz zi0X+`j3rL$U+@QORxnGhM;v~fgD;FtjHwgvjQK~LR%>p4ojvM!)J;A|CbF?s2D#~e zF`64e5GKKNm`lolN+^uTyDAa)tQj&aCn&d`oN!63=R^glhrU~(|MuXv>Fd`M+qoV~ zYg__$k4H`0%r6fkRu7FR>(> zFc7>z!rACXnxR$@_EpLVB;ysyU%M2g9`0N68>gK2C;?2U5uKE%U)`yE_6SKLlkxG~ zDc(E@lX5er_Op+6&EVh+#418@CE!#G{FWV3A6uQ#!Sp6xuf$jS{jdGKlTh<>hQg2^eD?k1zlr4e4)( z+!TzT7HYW8HtMKjN~e-oh%}zTd6ewu>#RYl`+UTt$UJ1jXRUp4veW>iv*hv><71e8 zl0&8|^N;`niaUg$#}BJMZX11c9e6)p#wAUghpU|d_VFu2ShNz(Y2oWlyww?>1RMN= zZ=3)YQzbRp{WXF=JtNU&e3~?+SkAgTN619!o62l}^}U7_I$y5=?I`WQFs)Kyp=8jy zF}m#!E=<{ppVsva@=TxB_zSF{KaCUF+iNg|nA3~6Ays(q^964|J{kT386KWi@mJ9{ zb0($%4--vK-RfUYIJiMry!I``aEeVAkH0e z@cwv%lK+PYH$hE=7ZyZrNBu~)T`5PSk(awo++Cbpf=kyX}FItGOLND6sDS^6-3uYkoEPES5gY#%cnl7a+&$Ji?s6 zTxC8JEN(}Xf;VO})_?bNKJUdS>}0E@w`cDXCmmRJIT7Q0qpnuzb+=)|SdrSDr2h`{ zH7YDJsJ0Im5FXi1?0noMug87aA*1Cmb@K7UH|IVeieV&T27997Z#j1!gLDA^0oyZP z0pE_b7aSyef18_aQ!6(p#q5D-bNSS`VX3Y8NILODwW4yn?(MUV9k>d9&%~}y8SpS` zRHOo_nPz@`aTB1(yq2vu=5}HSSwIeQKAP$HQe@8RIrI!driZR}1QRxII%@@> z`=gT|-9CJ3IhTdJwT$$+;b&&H0}edv()AYy`)S~rA24R_AC;Ioejo{RC#<%ML?BQL zKt7yj^dS+E&Jh%JB)A#^&(0J?G?eHseNuOu@WZ)z8u>O9ShKqh@r9sZ>)p@q-!EaV z<(aF4iY*%(lfdF%_u!(}#w0YCYI^@UaLg^8=OHn2@0qdrJn#l6P;`(z-1QNSGdH%h zf%0{@EdchRY{?Z5oh(hqnP#A4jH+YX{?Bx_xFK7iv7bWs%s6J-O;HX-!~6?H1g?0kbM+S-}8Q^F;eO$+wW0h zGgU=3_@aFV1Z?GAm)W|b1(#)a(o!NCinunx{~04Db74?w;C z9Xzqr-trAaT#Qq{rV@iz38ThE*LT$K@~2_C%bZ<3b3fJdRaH}C`)N_+<&z~r;-fi` zx>Dg1s!mSPtE+AW@#;@xBlWD2jZ?6iE9v_sUSU7+W;GVTxj1%^k-6oB-~et-B`r&i z4d$@;n>=#UI7f*=mihYNu`6}#WFWeuDxDgISh{-Ad(i?6%vC;mC@P`qZkuOi ztTi5;!BIyjo@zjBSWQihdT%V=V#ej=B|t~CLn`Ho;p#B6W5%3mVp1)0Ha7bl)t`c% zmvvqjNB?;t$L5d!+5UVafsNH_nQDQ>-^`?VPpDl2eYV0^G!k zfE~+E6O~_A_^p8ZT+&C5juy#~ zwcpG@e9Nd_V4BidoAeSPAwe%nWddFN9bhxb{v7REt-#`)zJX9l5wg#5IM^_wl9ELB ziaNWwIIh|*MTgsI9ecsMjNgFmP^J0;B7f9Rc^rz&Mi~tgZdKbaWQ8K#kb>Eiwq-e4 zeSM)VNq_a8A3+w%g_;)z_p+5NAgnZM55nA=nUs%vxpYwHB-VdRUAa6n)F$@dUkWe* zqK2MYYp|zUJG<$oDau4;1q5 z2kLvoJsttqmaXO!Eb*^6a-jv-uP>g7K?8US3hHP26%!h)hT#Bw{&XLgL0;^>(K>Z#zGXPm;Np z-PV|!GkA#P&SV~{E^u;)l%FOH7DJjNZ{Y?J1bBm~I1^~Oy4=d~!NQfOYR=ow=#KlP zhJ*dX?}gI^cy3s+hZ6}ucET3^kdKQru}tS&uCv$5ghWjmbH5&kI%_j%jD!OIpF)FOOqgu@u3-D`k&w3 zHNOS$YitiwaxR6@Maqt8WSfx7#xwQ&j_vN8oCoBAs#K0?Zg}p^K7Y?jXdh53*U0eE zp?5+O`CWROU;aB7ERWR_c8^d*XwW;K3}hA3<2UD`E$PXO)#-pmeLZSd+%a|fX>efR z>Z1hpY_&U6yZT1}6mc#N0oH`t>eEoa0`|i=;GtZJ@7E=9>|=bi=*|Bj4G=|tGQNH` zg7oV)qV!U10Wa^z?qU49C+q1wD_-6c2{?)2_od%((|GtlZPgh#Ug&vqagX>eYqoe}aGT^TFMi3U>6O&)xfV5n%N45q>!;1E@JKebEt z8o6_ri~rXeGbKaKPp}SZhfC$-di(WfC9+>myKcGznY+DyAJW@1fk&_qe4XM75sWpK2P4AD3s%01#@JHjraCk{=^p*DjlOc=@9L*Mir%w;gPWIL}f9^S-uYFbapm($QwtuCucs` z2qOq_Qo3AQ?ut}pWAQ-jz)!SmVs`I*kLR_R(#+~TyP)}Ee1kJHSJSD!{3pb;<1N&j zE9L2vqAfq54Pet(yLVq?Ja~V{~@R zHraCg4sL?3i=2h6zAsd%NGK?O)2pA~@sMMv42Fh;R8%GlK-%$}wooAn_Qmr>)hCb4 zY8%NaIlfyTZyo;CsB>7fFvhTf+k zL0_i}@#yY8^d;~LriEgBns15G&r4Fe%W-n0#-!`sovD46jH+Ad4%@6tGO_4q0J!ty z5gnjp;2*fGmL5)qtFGFBkuU=(^M$KU_bfb>h(iQo@2c;?nTf%&ITInbkJQNitE`l0 zLu06v+HPit3wT|3Y@FSpIA6yH9uVvl)3w+pqgM9$2idxpS`VRdvbA{4C&q82&f)QOH0egNz~fil|%ZPM>gyJ9CqTQ@7km^uX{`I z?G1fusU;Pp)EHT5FzUDwxv8qI&haqUDA9xe`XySBot%@iI#Wwf*PbfV)8u)!x0prK zAslq7n}tUww3yLivvuyH=7WEeeTAx^I`(Yi z+@U7tQ#P^rSM9RU2TcE~_g0y0H@4&=xsbT$H&7gTo{^YOS8xLq@WvW`e z)9UX4cag-*m99k83UyEM2A_N1$ETcY8ti{w>Pty?Y58g?;Rj|8-r(0r5)nIFj@%FB z6-q=<*L|KhqddCHqwAh)u7N$26aDE3Z$6U1`kJ;tbQZOrVJGZ#3c}$)F-@YabC(T3 zCbixu7JwW8>Ve9h0bd1~)f%dRZjE`L9~cc8@aR61ZX(-af;SG;nVc!iLzu{rduPy~zhX)6gs_ruvI_Z*uN}DUou8+x%jkm|vrtn7NV8P2NJyHj`ZRNM z7`AHn)~bB8mT_&f@z^)|*bW9$1(r{AJd&K`Zo}c5%GJ(pZi2M58=c`qM>D;kHs1l0 z1|1D|=uL*d6~shjp+gN7!{g&OppvF8tB-%Qqk0|uXw|QYVbg$+fFS8J19D8O*FhKw zm-rREFlDen-Ai`)e6E(?y@vH31JjB#{AXlVEyjhSsh1-t1i#oE%wqQUhLq%>rHDf{ z&6?004|+R&*vUbpypYF9)D|)}2F3>yldPBx#O_SnS5zWihlO&JE{xy1J39&r3fz#X zcO`laJ_tMD4y&!l**Q65x1JzR&CGaudWH&QIpA}#vN||9jf{=GDy7G{PUyRv`jHV` zuCCnCxf(%DqGQ&#_~p#PA_07vl+Q_`IU$08<0mk+*#;Nv@sl>M62q2et5ND$=}1y0 z!WQK8=<{>8({>fZ$DWWMz`R2fj^cp1&tlovq(yCd){;JbLOff0(@~il4p`I468%fH zEwjG(-MJ>+SQz3uC+h20+I3~B5Sx044vj{Icyk22%&S8VWjjEpBSijGeWf*_Z~awJ?iB3ge6Fc9w*>rsjLY3c@8Z_0|&S+L! zYaCkpV7z)&)iuzR;#2)_ZJFJ5U-M*JD=iT5T4lIY{UH%E_IkUl3AY*ST7aARWP5bR zcAGH&TLUpZsfh1wV{>!!^Qlkjo~K*mmY5oEW5a`j-adTBPMrJ!0K%YV%Xaskv3xby z$4$SyNHxex{`tmI%ohtbTXw)}$oz7AC*XW=*yQc1O5H)IxgBl1R8caT#{_0)9D`=IGjj)O!JIaGt=zi=%aqp9ThQk=6qv3 zz16}1ya4Gca*Cm>_VMG#Kb~A2f9JCK6Ev%=w?;P)gV@?)(_D&N*nN%p^rzj(Ss{X? zzq^~ZI+Zy6^^VN>REb_amQXUMm0!~(u!<=cHnIz}uvwqKb&kfr+Km^q8R@4F>2>t< z^xi%3QV{k!|D;Tei=?68v^7QtPo{eaADF0dw!a~P>{bmL_ojqBg+zQV47nz)WY8=+ z-?|trl?o!R^B|VaB;>M|iIW&F)Kb1woaR++@9Ls_)=|Fpjv{;Q85;51tkO~zr)X>% zr3H){iy_h!5|e2uOXD6Zk_pL{b3@`V6fxUPX(2g$JI8I**t?n70Jc6!2AsAZU1x|a(Gm7l_F^| zT?mQs{hsPihdpopCp!D)uQas2K`7QFzNl{Ue*x@itflwA}?UPJoEI%u2ndq0N zyl6}3dAYfhS<>h!NQ-%aUL=P;BiXV^SEp!j!@&{9S>9vFDyPH3+mxvnQw)DHOG{g< z$LaldL87^IjpZ4!0k5)B6TCZUL!YuL=UDbv4$Gvhqn>pxV=zwdVh$uh_xvHK0NwcW zaJI}hq~MdY=Et^Nr1dO-(J-|j_(ViDR#rws5`S=fS%2F_Dx*Tp`xP7Mj75GQkojJJ zP9r5HU1}<;azlBm)8c&v^?LjA9|Sfo?nEg%jxzZokgcAZ)*F%3P`fVQaX(ZS(cBV-xytbDMCT#HkS&s9&=^o zisJ)~G>SR%{BC(n){{QQITRJIi6U(`988a?GNb1gYiny3Lus}XMM;H)41(F&wZk2nd2_UtHlpTNLhI5FB@{OM02I z-YqPAsQU2h`uh401@j}ofUyf}+RoH;c#?m+I6n^(d0`aTwYeEZF5VvJDANTTP@TuA z_2ZqCYava>m9=7KC3&q0`|)wBo57c}kO8)X#?NfJPie+7BKLAqtryUmQ5pY<^2uNY@ZQu4i4KQL9j3k)F>QC2fKlYJ`x~{j`Ki&gD}qE* zQ}gEP%zmnj94-3h@)QLHg^0(FIR4oMs;YA~FR#z%{QP|Uq;`(q+#{H}y1FMtYPm}9 z-eKU9h6Y-3oKZfkSlO{LSvDe9?u})p(vYbqUzlz7CK7moK$fT*Q{u#Yg~wtL)?@>s z(NuNzM0<1@CZ*H|ns#PeTie-OWwOnHm%pukrSLg3GBF+EPD!eCrhT0y&A@Z+ye>yt z#tyuo^pqmo**&i#*OrbKvHbJ8%FV+gvtw$?4Jrb|qS&wXQRr;NI&=D$eMjI_xQhv=>qio2jJNB(lHj8iz%|6D2 zeH<|p=SPvfS|~1muqr&;Molg1D@lgBAS=^dGHds}LmrLr!Uil~>AnT!wrbMx%znIi z^Wb=hFG=;-fF-~?7S9Ls&9^Y zXYX_OiO(+XUPvug4|PB!x_;v7{~WCO@z(d_h~+2bisGzo&incsFKe< zP52F1O)j+to&=dy5Vei=gAr>0YYk^d8HsFsgOSG!bWy>t$2#{mn!GA8CmLtx+^8X+GL(UKNKVM;j9Nt7s% zfmWV=cd@C3?4R^Gc&;-&tsZTFe>Yi(ePseg9#Ohq7Sc$wo#08&@<n1ox7pKVOLOw4EDmmZ!y}|b=;+?65r%ql z^i;I8-9ER!m4U1kcJIBT+BANp+E;bYmckvpEVcGUM@a_t4c+|MZwF;j*@nkr+5L8Z z8<}8m{)6(<-_<76GuhBeuC7)(p?|{!cn_4U@0nK|RB}Ghd#iqdz=rvGTM9 z1bONAKi&SlFAm-xhvmu={(GAVVOx;!&}Rj&APP%fMuztmmU{|;`ANE{!TxQWFASb9 zr%-*TzyxqnomkFIQ#R-uj#2$K3u(IhwA&b9Ryb}@VO8qbWoP(LyuHYsJ5?up@LBIJ zb3n_%59&m~q9k>71&%K6d`0>40Yk4KEd%eitASn&SiuWCm<0HWXNK$Q%TIpO0I5NK zC8pwLPv819=jTjQ$;B-m)GakedNafc<6+|Jln~nwQk!939If_p5t;Jno^jN=&ZX@9 z6ClF4|2AZIi4WnhN9IzBGYDh&(UcefBkQHgCF{D z8#w>*{y`BDXlQ<_tIjVD@7QfJQe1B{OM%!e>~CG(mve0nOaM8gv?r!A2j?Sj^S^-# z2hL*^K2X9`m;K7)p@JsY2)~Ry-Kl>P8tewejM|IEc3+eX$_svU()4B6f=AY6~VqiAUq=>?c9 zWc%Y+a=fm-Uv|ik{EJj|ISS4d$4%9oY-APa^UL+-Ha|(A#>FJX<}5+sKKBQp69y;v zqJGtVOQO1p4XP<(C&@gjbiqM?a#wrVCo^^&#eq3D>iUYLEY^AaIVd-f{>%GRNiKp- zz$D6gzRCTxPd5-)^e!i-Dz9F%$Aun0Qnd`(rV4d?>ow}$x=vBJalCfxSUE_y51du`GKzvm4ET*H9)8i4qa z@@wWhIC}0?%%XH^>V5u+tY!DIlgDZ{^6;cYvsbnCZl~2mrQahYlv_Nlns!&PLXED( zRM}3Zuy6Ne)FFcxFM3!`P|!Vfekxeyi^Vq3D7Wp9qlXpSY_-i(W*mq>5#>};s3d-LYa58{vezs3kS6rUlq zt`f_v$9u!KCmmm=9AS0t^l$%NTsOVf!OCIRd2c*Yghas`WM5pJc;F=sL7pi-`cpB1 z*_G6U4H|HK1nn$tY-}_is#(P6T@?z0C!Lwy)5!tI-bXUEoGp*TzY+0&%qvdSKQz`k zCf=DW)^7u<-iBXUxzhJ5bAsZC&dBWh@;|&_2|Jg)9 zTH4$U&z)g7xDkLi$;^CT_@wIGfkgc~f2e3myxh_w&fDs5K}S6Q`*Dp7bkknK4IlWn zOfR6Hp5W|(X1eh6^Q%N-s#38HF{{Z*-`5!B!{BhFd_>%FZVoaKne0d@F*>>@Ff5a0 ziP%!GyySZc2}OpmgWoEtsc~#7TS~V?jL_?-|MzeZWb5bAWpTjvl0c>RyplDa;tFRF zT{Sp}w#B%AP^nm#`wSsS-3#=gAzNg(!~QKL#ebgPirg#l1o=4b6FAq!H5MmD?->xS zHcA%t(Y!6S#8On-Wa}1z|yk%&hq!F}L@YR;k}I6(nNq!5&+( z^J^S))7FR5@pfY!=YxDfypJ0l#WDNWDWXfhk`Ha79GDo`Vwb#u&sjRJaeGemgBE4-` zOcRM09>QurCy0)Yp8w5GF-*tjOUw(^N2T}tc~5DU7Bb;{LAr>mRKYu_Nxx>SzyD(* zn7cgpZoSLti1^(<9?sy$$7B)T=2^MPNAq6GtS(^#Cq1f#k4%}(&j>8JEe`oX_dqc2 zP{rRS6PS9&PA8+N%GlVL4!8Z9_ZDwp7e}!|5<5@?P>FVti~IH+s1#c#%&MiX!YcQQaI7P z=RO#u-MRZuo(2iOI6MUX_CqPW*NfsQmeE(B7Pg!C<@sVBMQ)4pDEf`$Gm5y@EF(10 z0`L8O5ny%X0mwKF$6duvt9|k=z0a1G<|yg=$TBg1jTcGJTz!7{@U~U~RxKt#vG@4c z2lU^8cH1(1(=dqeGeDEKHfmBdys zvQ9oVNd2J+6ijOS{y6vQY#)dK)X2pE3}e!uC42k68kE(2gF0RJjdv1k@p)VZK{LHa z`1e@^EKJIDTz+Ezt;zmpam=y9=jfgdrfm_R1IgTkI^;#bEc134xRp``Y7Ph@LF2BU z8kSyoSJ1XS%401Eu6L07BNOQ0=4_VdSfLg%sj`T^{_H~F*=g^Q7VmL1sKMJq1@0$K zf4NN_Lpt6f4=U>C^C$8$LDDAmA#tG9`~HH2&@++e1^O+~MXsM8U26DXJb74t-F=&? zA8x(0SYcO>FF(ngp(c}6w(&Nb9>3CADMKvaDJtoXyAF4d^kxRAZ&s$Tni#+Z?v7$94Op|RF^c_g>!RIS6lLI4@z5bptlkVk~QkW$eyjJA`8iSmV$O=RU z$D{;G99vsk{8S;}luAJ(S3G@jp}uO@deJ)7e(aD35f2ZLdJtOlP@6tauT;w_KXkwH ztTr4}?+?F?=Y1BoP?BT8$S3UFAr4UY?EYhvtDFvXbi8jA(V@d3;)by1Y1>?Jj(5|0HMay2P<4AExG$u-!!#;0=>_#_xKs-KgBkHyD08MKz7R++S%RHB!Lk-Ggbm- z5O5i$y>J(X7#U+6Dbwz=a>gswvPbB^^#0j&pIboLP|=h;(McGn+H5HfELSFB$*Zlt`*^gxI>l13xs^<8kux!UgER9 zwa!%E*w|1n)R7#1m>eXd!D_z#$UhSh!yKUYG&|(?H(-iHtyPD`ER+&n)wHpXt7;=ez?`D z6!_AIeofkl@EFy;As{ST;4S}0_fG!@Vu2#A+9vy4dcuk$uvu=J_gnnks;<5J@X%kO z!59^HzA=~E8+J8?f_?E9X&mx4cuTgrkmYkx-T4Hyi5GaG5p7FOVdsm zAMm}+P+Gp(UQeH@m+~MNxSvp$vsu}iD1z3|2xJ7^Vt#kcLbj{Tox2`mY}O)HhY@Ac zXKBLcx*qj!Kd3ik_FbzU+G|QA2Z?b%{9bd-jkg$sk)ga#8JY zL&&;wN3rYfKZky*+x7iIg@18@9tK7?ve43GC$4-&kS^GJdK^>DTkx+h@?gzf?q?AFo8sK1+)gq zo0#){G8fF_jwH6U!Mx(gByn)V_ropP&99s z4$cwYQjgCsuHO&ksl!9w=j5Ew$LAm1yuU(CeHuXmseWs^_{V)=YEPWTh@sIJ@5_TP zy#Ci%-mKV7z2m=0=f#y@>3qC+GFl@;%nomI8nm3L-uRMF0q%!X_R3Q;IgS@zx6N@O zmiZ@IWoi8KEKBDLI3`|dVq)zjU>e=|oPT21{NO7;Q&3I#nhAHdtYrF>ZJ!5<2u`)3 zNE4PoIda)qZ!1oqc@v>={SH=Mf6Cs!704fcSMNM9W736Qo&4hoS=8H(RU4Z&&8|KUQ!tauyJze^A*qK=dxRZ2W zTEohkyn0`o+RKE-be8u^79j7R+3OvKg?S;HsH3s<#VXJC7-D_X)B%_wp& zqe-Jy)N$X~ch~rn#C41R~MF35=a+7H}~ zR*Ode>Truc^afakS`)>ZNW({8j7&^tK=ut#U#d@$y+1yq1{O=5KKo{ZjRl(gsdAKR zWR-S(`z$&-teD0#GVQaJ<3bjrSl)TtS(Rv;CSJrILckSL7Dwr)7|M*4oq*l*sCb}T z35jV9!f$8PB5ECXb0eVnD$}rK>|Duth5}v5JQfNP$Ya{>r@_^JbI^;HnD_a172inc4TsjRq&2#}VCv-tD1g6Y3Jv zECwB~IyTB)rIG3;ECofrBofsZo;uI*-P2ok;n{TDJo9#Q^Q@yl8M`Zw5N(TpZ~tAV zLoSvs@YTiju)bEWIur=yh^;Ztm~{a?eP4nvx9w=0X@0=G3;c9r?gk|+CaMY(Pg8@ZT}C^Q0r>KjG_83 zEjaYo`h|91${HMWu$@F+-_HA%qk+iqwV9O`S%$06@-sOzDcUBBKE{JZJN4I9tTM>r zMB7L-EYsj(Vj%l~Ew?!S~7#`n$t zuTswYsj0PH!zxmwqf{aEAWadGjvysS5!fIl^dd?zf>Z-iwiM|oB@qQgAR;vhk)lXb zdM`@vMG%k@YN$Cc`-kt$nK|=)XXg9?nOO@~vYz*T?&~6Ur1#RvA;NT!>%L1`30#WyKy%-|EJ z)2&IHRIgzkW6EC6ZFq1LG&IEV8fHFi!526-q}85`s{mBmOqR6s@ow4PRb(3 z1hxMNG{LMr!ug%sT2H#1Nf=I!5h2H zlQI~V#K~1@Q~_f~Co}4ZCSvUZ0AQ-c^+?%LTx3%QTtI*|0gZ^V%}EYHogrI`3XMq^ zn_M~YOVv5=XP7;Ah5um_ZZHUxCHE!WC&!j4bUMoH&9LZAu z%(qlt(g=j?)jQ?(g41M|#}zFhhXluav4mp+!^Gj+*c+FBt-9%qUL7Gzx5!Z!+-&~S zlUnqbuBN8uyiqiBL$g(+YJg-IpOc*6f%ak~^8IK*+soGbAhLLh*v#E4m?Oln6&SR5 za8w({x<=fjY~hf+(hiq%rT~TbjZ?6j)CrESxX_OafMADLZ}Z$qwOl<&(8yA`NT$a$q!2lHdGQ&(s%i^v?qzdO0myjqH` zx;&sY;1NrSZmc$Hv3q=3l8`B$Zj$sQB-{uclb7K7fj+0cUQQ-qalwNX@3ov*skF7==KVHdoj zw*!;FX`s%-=u?TaZ@$ApJp~LBPcRMY=c$#0UU-csDM3RD4MMo8SB3*t0y=h2DvaID zI00M+=ag1Sy!jzl*S;_D0U*Ra6t#>Tf5f@7NhFFV#rLv;QJy>U1k7m*JD05E!CDDHvruCo-;Hzc zq(-4enGos%3i(>f=ExyC3LC}@CGQ!gjK`#@&8>su>jXOlqe`!O_TnW2s|uE+_2f#6 zl!ayk>oWDTW+Q1@E4wka!o${hEdCXo#KW2FHdX$Sl-pdcKS@=-0utTq_?KoIHMfh| zi?K846c#cw79o;o^W`!5VdbS17b%5#w2xXtabBK>^SE7*UH4H4C%19gl}H(#ph0{Y zH(Kh|TVTnE0d@n%%$n!qX#Wcka6le~>N{?Z(3w{iTh|6{z0+LuaOjHjN@DWz!t%OY zk9sQY`Lb%A{Wzu;tjufx;(F|PV5_=5fQghYq?rMD=U1N&+!ta`PI!iKkv~%Idhwnx zsn*pvAD|OW8=wD>(n*88A1p$7;=Bf5A}$0h0=h~bYl9fUUebWrWzOJPAzDTQ17(rK zl1Hz-l)`8t#J^u`2h49{9t-4rW`9-mtf*v2L7a>d-j>7W zUiDo9oFgPAwIQD9^qyCpRadclf1?oS8iv1V8Npn8F_cbc{3$Y^oP(Y}i~XMcsR5gV z)5soYWxJe8Geg~@u%wrf(>4N622QB9%?fTUbr4q5LarXcmBq)3adCe|I zc;W9$f29(6UXxm6o%fv>A1nFS{aTDWXB-Rm& z!?fyGGM4`)Kg7PgYHZg)p^pXPO=qqHJ1!h{SNOo3#PkI1HkffFPISW( zqu~pTULv!L3P}0NW8LD?-o`&n$^iw3Wp_6S11)=M!ZnP}>W>KG1)`$Aj({0>V0cuE zNS&m>EbTIOv!K6?n-(p7Td%?|&TH#KihsG$+UzI}d2Y}|P#+h{df299z60Ku@8D<2 z*-8{=tHh*x*Xa)1i&l<Oqq;$amzebs6@wnuTx zE9L8+?7)Bo-*o7ut2>uvHC;JEF}`W4lY3hd37>3J*dN=-V_C&A^LaI(;%O)8U@ZwD8?Scn1D> zjhX5(z~3t;Z@>1W{HDv@e{bI&XRKq@1#5;>3C2Tj)w%u1e&p~sc|#Sl(JOAQ0^)o* zVwjn=$lE>5j!`IMMVyZ>g6D}7al`JF7xhx?_wDP&Q>~b(>9d&$Ae%?q(|Ra&u^*nX zTDzjXwtb4xXIcKy*R^0VzA+VaL^2j%fR(Qd`rLZkG3A>i2-F>kHYCkLXG<__Da`Al zv=kig)F$_B(T~}8{aF~+VQa!?)zv$ib=$&I4P}}cY|E~Vp~ag}QrfDGepE~HTNYc9 zMU`L_mCm;=nrOMEgOY4;%Rf4TuXWfS6#X+d8ewg{_J8f7esx_SE{)92nx`h!8FnrYV+XPc9h40LiT~dpep2# zJ@h_wl5g#0UvxqDhXaeAsJ!30OKg;>fZipb%ro{7x5zf>Uk~Be``-+D{~9;{gLbcV zu8ze-yAlDuZzixeDuGDc=H37%wm&+E*RH}Qpdg1cTfj(fWf|Nz%ic+FpOcjdbON+` zHw>P8Vq%@D1GcKtHiNhPhkr2FY_gqxkcLSjH|VQ5UIfIYIxVC9(#{}fG|KTG4ha2$!a%W7Js(2L-*YSJqa;^Og=`OEFJhems-0%=g^4Bb zRB-bmfmr3$vvN=a7BrTOODJYOzMV}N;*WJfil6`*?7wx`e!aIZsoIB2fJ%uSMdz}d zySQ0@M0u2-s2QO%WQ3|9RZQ=5a*S!>==c|(4^DQz8?T~_zg`2}^8gst210Ifs(7ok zQ_`^)Phs`}O0RZg+Ob#7k+^wiQ^_-aJl67(Ja9cfxMO|OEXya|T6_85rkY{fA&0df8Xx5erqoE4!3;hr5*Q0 zba@@$Wa#qm=M|ZEfikDqo1kfS#0PW~0mmVntP1z`Glm)DSkM zcrX(q_offYzX4TQ*>E5Q#`k5j`3p?)Jwnv@D!aPaqxCxSLQh6?0loW>6xLQlk`veDA4xX!-n>{Q(oCCTNS-cZ?7igA`JXC<1mA2kAUZdUZA#DNJ@Lkk3p8gFYwl zAw=^4sMBsG5dY%&%rcB1V_RfQ!J^*x@!{$LQ1WRL-1}@Y1Qkj~UEAvGSew#$Dna zWYo8=SV!CnNlT;k(a%u#2ExeujvJv`0R;{8Du?gXj(vvS$M*tJh9sd zE?)+6K(FQ}&Sb-4L5cKzc#(qU_w;LTlyeOaQ4b3S3wT@NClNPxv9J$K z3(a~bU@p#iSg`#iB>c{_LxS%m z-Q9*~>aOZ=*|pvQe25TGvYE>zwvw$XR)@z)F1SWDq99A7zIMr&(3w-6M4Ln&Of~8* z){23`_ja}0XbyuRrL&Kb6VT!*=>Jx%r|*MgQyRMfEfSE+!Bpr5dV6n+7uaenW-J<4 z>+jX(f&HyECs=8k0{u$k0xhcUj^z=9T|L17Uoj+B68E%4P#^#%)E_LVNHFTVbNf$Y zC|>;s8p%=p40bH8a3wnA60|0RhRjm7x9o}DzFO-Gf`e;ow;qO(X$I*}?zrA`KlYhh zXr_6t-K?foEoJ?j^b*+T7!y7kA>-d(rzoO%)a)ApMRVbD;&)veF`QTU?uc&fn+RO0yJc|M7Hm`Q&!WVI#_WK$s?MfOEO~{(?pPFlg&~VC+j?h=!UW z5b>hZf9|mMi`Cm#7~+$q9S1H`4Yy;XdSX$M7k}GcTj8#}-mEtnda#Twc|=tE9t!5B zQUSdnr6z%YL@Ik+f{EnunzNlzG0 z8JKMQ>BQ7Cnq>k7i_+!%qdhFeiqO{Ot&CEZ^f%ORM5S`{o%8XZ`W06B3fk_q?#1~0 z#(_fDo6+*UGuvFi{dJK0AZ%Z1-RZtoo+#WfeNMqgaN+v1{(KNaRc#-@0@agV5r8D@ zXMeFg`-QmCT79FXNYnTuzqi1~+MW2RboAEC&rFw9%>xAthT&1WM-@QrQ?z*AUau-S zsdujPA@GcOjLEl|d+UzqY4epP;B`*TrD?vY){pYBT*2ae|y-Fr-V%s z$Bct=$Y-ApCQ8gFL1~6szKCAYF!_ru-fR+c_(Vwqn=*P(it)4|sxML*Sn(s`W_;(T zkhLKrA01^z{2p&=W12wfCX^*pJfx#aJp=qXARP+oc?K$zP2eY|Y&s#R8=3QW8I!I+ zzp^CNjfcW{o0Sbk@WS_XYrj!A;YkUxyMA-+u#(wP?UK=KnMwJjWV%f<|0H8fE%H%c z^fW7=u)&+I6akz|v2f_gJza~A*S%^a^j}p${Wmw>AqSWSyfd9kH49xk-PCrFA=z#f+p!GOOy3cgRoa< z+&oZ!UHX{9VEP`~gu1KeN|fxFP_6)|st0f*iCL9ezW|SLqk;ec literal 0 HcmV?d00001 diff --git a/shared/sentry/external/crashpad/doc/man.md b/shared/sentry/external/crashpad/doc/man.md new file mode 100644 index 000000000..0344fe7e1 --- /dev/null +++ b/shared/sentry/external/crashpad/doc/man.md @@ -0,0 +1,34 @@ + + +# Man Pages + +## Section 1: User Commands + + * [crashpad_database_util](../tools/crashpad_database_util.md) + * [crashpad_http_upload](../tools/crashpad_http_upload.md) + * [generate_dump](../tools/generate_dump.md) + +### macOS-Specific + + * [catch_exception_tool](../tools/mac/catch_exception_tool.md) + * [exception_port_tool](../tools/mac/exception_port_tool.md) + * [on_demand_service_tool](../tools/mac/on_demand_service_tool.md) + * [run_with_crashpad](../tools/mac/run_with_crashpad.md) + +## Section 8: DÓ•mons + + * [crashpad_handler](../handler/crashpad_handler.md) diff --git a/shared/sentry/external/crashpad/doc/overview.png b/shared/sentry/external/crashpad/doc/overview.png new file mode 100644 index 0000000000000000000000000000000000000000..78f849efffcf48b3887df080361592be7b4e0a5e GIT binary patch literal 26517 zcmce;WmJ`2+cpY+@AMYnY0 zo_Ig^`@DPq*uTE4N`7^AJDDEmBdj{Dr2y(%erl;{vtB)J8W!8y-!mt?%>RpVMH(_sjq?hwBF?(n-Xr!cYNoeKYqbOkxzr`ST z;IJ!3y(1I#;4@W}1!cBW6gfAi@J!@MXxPYSSob-SXZKMXTUmUy_RYGh_Jqg9_JEhV zbMCsP)92ly-J;F4`n2)l`TF^$oF8*--dhttyd*GbgLH}XQwbz6E17EeXoCpMwQOME zYiH3gGKfGTOe+&{AABvAfD?hQ+QAS8@U;XBs(d@~;s3pf@;Q)&7O%tJ9C|@PgP-WP zq)Y%*W!&H z(lPeB`^ck)ScYV0yiDlnEF<0O8*iKp)Z}wtkueJ$u^Qr*tAoA*MHQ8RtQP z6^Il1ppbq|4gPi41|PNKi}~HB%Bh&}Yz&1?EU`Zp-O&$?1I`vhM^IcFmZAkKLJDL# zBa;O^%94g69x`(^W4`%EHRe@(7q#ZPwP$Zo(WG#W}&nlh1`DX=r`Sw4lul`ZP6K{C-VMJLqX^4rL zJx&-N*PBOXs-A?c21l8pIAktYXE3vY-&UkOy}b{P-wOvt{cOLkII~PN3c;vo(2Zbn zjzWx7cT;sll5x`5u9FEnaPXoOT0CN>(Kp8(5o@62{H89&`KI-rdpy@)&r(!4f`q21 z-B)Qlo%dP)m9+$ymy45{Br{sA z;6q5saf#8QF$b@sQv}0`|IoF3u03?xlxFlpxSTR1t&@_Sx?ibZTMq*On|_Od&Hf-J zM&MhqjE07(l&v}Bblj$V>!|yh;puFO5U)~y3g0~MO+*!Q$VI3;cbq1J9Ic$d!Xa1R zsJ24lB^$Lik3*{SY(ony=?}|f38D;f+PFKjJnh!$lT%PoP*qh`S*fPSDS;_R{uh0; z?CkA<-JLX@W+~u?&!@T`9_LQtiwNr4Ian`v=E*gcTvUx%X6LUdoBT5T{buX!-0s}n zyLYXJ(j1+fw6#AINH|~t3CTI_hlF6H57qyyC7Vvu=X3A;NeI2Rh>{&hq~(^h`Z%b& zuGjFbmX?9xp760MW6qTvz&|=b!3$+LFAtAM%IUU|p2t9Mio4c;7P+iGGg=_pTH;V! zpWo!`wG#jro2dlm47cNX;OW9XuZw-Qg!WockI}dUYP>H}_o|26e$$>x8kg5+W{RH& z-ly&Cm9pE9XV#gI<#TGKudtiNVw9S3xYcJxr{tFJt%W%` zQs;eORu!1l8JCcdheK###49a9u)GxJ|| zvkmBR$=*f|b8#Ek4~e$gYx+?7yE9&cU4@Ci8OoInN*(TiC`ePoXPX(I|0hAV!m?Iq zSJPh5AWtez2s#v;3nE8C2-vJfm(mGaPCc!Jmx9`e?E`%xeLRyi2sP%mVBvS>uvja( z-bDI*`gqw(=BG~;CAX+ooHbN{{j%UfmGx~bV1#GQ61#PerJY zW^ek_EXh2&n$!22AHc~uOz;p?w5I_+SI zzB3-xLWcCsLcR`@`wHs%&9}p5Zkps&B>$Cq*f=t9S|p=X!e2VyH|V+6nnQ6KeSQKiCr!ZYvul0?h?Xp6*<#g^Tse?lrU;_ zYPLNt8k<`yh$96O0KA3PLnHhfi%ta2-wR%hu8VabhuLdzi6nmq7B_vuB_oBe@}C`$fHHPvw|CSQ;|^d4E4Anybgp zt)NvB4%gUTf0sR(Myv89Z2%9R9*#%KG~CB5ZgO3)9+1utm!@b*FK`SS*WmrBjE7jqN0y3l;5)K?d|_;|EaF; zY3BCWa#=fgnD)@s$d$wsLenSQry=z0F@6Kp6mt4NahFq9t8KwVN5?v|8l_TSNa@Qq zW%<^Vjm<&)a5hIBx(D4vi^;cPO~C>e9tZ%te6pArXZ3J%==AJ_#SPjbvUQPJmPzu^ zc+4e5qt>_2MG)6!lo7X>oTt(kS<=k&mt)*BcR;EV%D_?m@JxNSXxsO z0Tzk%?8x2Ip3g2V5nor#rweX#7?!Abmie)yQ%)L8$@qM6H%hsK*z*N z`OMC&k%>upiQ&89Q6!SKg!D~y$}(yL&nyzIPd%YMYqx(>3e8-@I&WxNH|zMBG|aX~ zkP#+-_C-o$KHB_+c5@Q@^uK~cOYF z9;GTqe6GrrRyRnG!19u8B7l02Bf~BX^}b41t&XrL@Ca<~H))o+eMcC2kwQ5PbScwh z&=leFM@t}0??_eakA-u9unPPQt~a~$S~kCFb#--ze=Ds-CK>GkYvaarOj#jFDvgtBhF@k>>ey51h&^RFCY)z9VP z+{w273E!RO5NA8P-tXVluwwU(fBFy!5h>2xoy_@je)DYi;~Cu7`{pRfa3yidaHXxN zq(t$b5YA!+8_sU-T43_Bx4Y3C@bHi-bh!MG_~f8f$)7Ikp!W) zoX+bv?;`F=jIUDbw3&O3Cz6^@VD4CA4NL4BpPelMb=!vsmAqx85q}R^QkZRxK5?P} z+#rQL^$GsRx(OV+(r@%h1cb+&%h+|}pN{C6tF^n%Lk>iFZ_exW+Q#=`yjFvVCnRjg zEVaXQF8673BulLKK@!!rP);n6|K*E+f@{KRbMrl0XPCREc(cz(4(-zW=@nVW%0+3` zrlVhLO1M^H!by%Zy|+&dmm7NqWheV`n2mI(*o4cj20+SU3Dz@ZC;eWy&rN23fqrSz z_Ki-(Gm$*J9ZBmYK`iYz#M$I<)NHxO6re(RUU}Y)IEP>IU)Bmi#~)7;xus)lAg|#0 zeEGy@*dN&v`s$i66)cAuOp{>NBvLg4UrL%hO0(f@ul+l3@CS5b$=kKV)C~% zmQDqB$Nf`Ix$F}4=_9^0P&M-WnNCh7Un!TT+)MZSla}i9t37i!vsK>P_K&Z|T_1{v zKP>0_y7;`d%e}7;B*KgVxkl;aLw$WI+M-IA?hB)y1_AdC=(!#EisxVlzv7#|E3ruN`^`rR|jM=%umHcYLU1@%yPiInAX)UUl{jk)0RS zv^YV*LplBC@utqpk{72r@UE24DV5D3{H7vF6sw;K7{&mr{FOzBIVTV6HNpMya>C49 z&Iw`d^N#JiA6x9ki;P>n#fK7&UjJMjNQox|ot4L3MG&?`H7rN|ZT^zm(LcD=!Dwke zKR<78Z#z4tkQgdq@}=@|LWvhH=2~!GUS1y|s@!2n{^-v&UmTB!6qeDsQNV`G*w`2! zCEgX$2ptJGND>+w8_kmlBwhsRjw3Uw=P9;DX!*pK+Fxiqmh&=0j3rP+vk{N?JL-(O zr+)6wpV5^6l=}&2nS{pSYHwQfx+W*5bM5fiUC@XkG&O^-Eib!y+5roGT*{(U+=03D zfx00NLgMB!qk9%*9ZSDCGc^07eqezqAPLII#^%vHC!hJ~{QUgv^zRaLW@hlq>zhI4 zC}(%4sDtOnc+lv1uLyc*T=TCi1t^O#>m<#4w-;Zm^|?~$8NZ=^yD+a&Nd)YR5Un z-80>{l51)@=7Ht=rg-WVw-m43r#lAE}9!^*^@j`f)cE0_{b z*3Yk1{b>-v4}M4;-}?3#A9@?B#azZ=V2$xM&kof1*<^4&=&P%%+M8N^ z@z;-FM-(EgAIF)gTCm_Tv072Awd;j&D*i5kl?yVc%Au3jb7pl~xh;dcZ7jf+Wv3B1~9fFF#yxgfeXEW2+0~*!P6=@Q|LX$MUd}bN;_W1P0)}hh<%6tv6 zB)o*LMTM1CpAw2?`SD$>smH+LtKYWlK?IM%68oQb-2~z#NRN+?JzmabKD6_+)5o(n z*=%u>G?kn?Z|V(^mkqaw@aYws{F;zoq4h%DuUYP~;s+tGG`HBpED&QxUGi2YmwD5j zwI}+GkJdMeDm9FZ9A{xZpw@@HuHdr?uoD6 z!S$)@>W%cvzEidV6M+$KSdRTm_@q&3jQMdbw!l8=N}RW#5B>p8Q@a-;b(WvZk}RFu z-%7`|SggQ5poN-KM@=m@F)<%N1ATa9IAEuYpF1rfmM6hWqDGe0Ij>46Tx+~w1U&g z1AbgCms&fw(l|FB&h?GhgH#DWSwxjQmK1cf@IvW;V!M=`oSakwUIv54QHT-?-dNz~ zdTuF`@x%FZDkNnULq-0+Z?=t?xs)NZQ%K5}RAp=PQ+hyd<uTpHSrqXGG-Updd!`D_;{Eo+U4ig5qmUdl(4B7qrBaZ>3CBA~o{Z_|}Kk56)hS3y}B&syvBI$UzO zB|McNE@-M7NyFUtRFu(NqH|PB$-Xx^IoXN2Qvs9qq(d9BcG@7`xd>Dlc#U&dPEQ-O z4liXfR#jEi(a9$cQ$F+ka&_gKYwA={|F%{OD~KQteX1A9&CPxFV3Oo)Mjv128yLI0>(Cc|bfE8Tv1bXq33r6JiA1uB! z=Y=1Rd2Vy@@-kd)6mdLGW9?4qxldcy?F0$+p>fF-^N#+Vj(ZXWN*OOAe%j6xAj&r$ zQL<5wN-3j zSyA_r8rI1Z1_lNK%)hK+*LbRHsH>aD1EYu@-6$z7y_JnQ_O>l7jXVbZthu1z#WQ-; zOw}G`onN5YT9J>~+T4uGhm$=rF6X_k^6>E1D^tu=;Ar!Vvt;XW`lq)l_|jlh-nB(U7&>yq0sXU{9Qp49lO0F-YO-@c8cV(|1IFMFG zE+{Q6J#n$&=9h^e>lz+L3VgjB(4=uT9v@Geot@2T6?p@(CY6+gZ=pb03knOx*;29g z2=YfirRI~`z(P2dkFcQbY#s=mm7Ql5s;Ct`Z@zX&VGeWn^aF+WuKB|4B;P-``(aI#|7e?LYML4)ZF7Xe{|Q z6-ECNwz!lX82x9{InXYsy)N9(%gZ}DauYGqV(U~|1{f;;Gy31PE3OMcTpsW0lF3y( zqOmzOd7S?bGv4M)&0Y3(cEk3Hbqooe_k0$ll;NC_nVE>=&J`DKUS8(A6lz*pcY_($ zaQF8!>*^-Tn&13~9+lqS-Yfq$^$Ki|?fEv@QDXjM)8AzNv#s!bj6j~t$E?qu$<yBB45C>11YA{oLCY4iGj{fYVm-8QE zSv#LpN%!8OQ_5Yb;&B&^n zb(iBFBI9FpHJ-DW!4hO-WG9|Yc+f%ikPyhEC`H}&*qH85Gv*kby?C!b77rQGvC_b=81T?d?^Hk;$aNLb#NFY_4n8~;(;_2Jc4KA}?XcMaHX z_xKj?qLv0VmA=r8rjCxmwUysLX6R2_{^f9Ezumkxsqs(PpPBS=^LqXr(bIL#A&a=3 zpP`(u9}P!3QnT12{I;Y3-OcuaxFX#`A6OrX#5QDPRxM|sxP-G20` zzF^Na#7ycjOe!c7!M5B}`R+GVaA{&jE)ok|Ex3dLrZ#S7i4Y}LycI5@5+l$k|E}H8C0bx zM#5!bC+`H=wM&*)`&$`V^(vdT9>wY`oD&y63U4RW7kUfQVW4uRU_J?8+k}AYjB-SI zi#L7eGnb7wZ&hiwJ%1Mxkz$icej;5TZ$6z#|9pN$-Ih_QdX_yTgimNLR(l4Q&Xo)` zdb`M38}(|3!t&1@vf_w`;<(fBFMO}DX^b@6R~Xxmh@4VSexy*F)=hSIP zc#7vH>jXo04YcPlaBE(4?l;@)H{jsKVbiM=sL#MOawO(Z>8D0ppICahDwBUTWczKVXwx=3^L7}y*_O4j@6M@5nN(|o2c>HlWoOXig8L?^VFHJ2cK!pi z0n2IJ6yxdiUK<1WszHGWsM&gKb_2g_dBY<-i%OPl6;s|j(u%>A$rMu+MK&m-#oWSJ zP^SlWc6O=C`ReT8IN@Jf%AAKEg_?86-^U@IM~rIG-G@3+F)=fP;wb)I>@5UK+3aH{ zO(0*;dqj0JApQAaaQ(4Ngp#zJT=>wM23Cmpf4sre3U0kCdf>&77}m}CkWk2oJ8l?h zMaRZgo|Pr>j1=u(@qdEe=x>;0H%BXWe@s7L1bj66HQCXFCBtTR%F4>Lu`tB`yJWz% zhK9LHRC+>sSrHt#;cQTl1Z`-C8t99DJidj>TLb^v50QP~X?F$S7V)E!A+s<>pr(PJ z<0!Gm7KHD|HD`AfonZUy+)9>|2f(w|{Yl?Ic%fZ$f1}i`C!4y!+q5qx8Pnn*7B1Kk z-WP7;FC^e8pQfj$OG;RBGpKJNo>2el&eQq^&*6x?AH!c4l0!Q;9_r67qu+U(no60Q zLG<4-d=G{+mYU~O3WmH#g=-qlpFyEe78Xq`@Bd-UA8zfF7CltB4?sTK|1bMcR2IBM z=HR5;93^ZRNy6TSfQ#O=uWKxU5-xeIW((L76FAgIzk`}fK=JQptMz>5>*DSpEbCIe zr!upFVaLYDYZ(|66Pw=#y<6$fe24D-Ia%|WaEKft^wFP{2$IN*jEn?F3%Qigf7eN< z@T^KyY2p_ZohbU6hLwWyat}Vr%70?k zUqw8C$WAy;mlcLTasGIXZB?-Jk3~rr6&KGr)vK@b|GSuWWkYm+mx5la4RO%LI898{V^Cqj<1|Byvn=QQ(W_ghr5Vk+19Zv3<4 z-ZD4CE*Gg6rkkw*8p7TybEt!5O43)FaCJ%A&4t#^fwCuEa*`9dM}1ZSr9H-0^n zqTHp%ZO>>$8 zTkopokDyXx1$0jNK)m1?nXW#2#*l^c@c+>5jYmr!^kExS~#6>lRYI?%*=mi zZmQsWciqnSXIr>qilwH(Nw*If8{jT6ZzUKk2R8^SgGRSp4CaS7S;m?<5a=87mRRo7 zbz?o|7mDV2D&%!L#0eU`3?UYVWvOa>XIBKC=iZ?Y`SZJPuUVM;=ET~%-)lM!@E5B~ z38L&Za2Ok=Ppt3!wg5i+ZI;Fij`3uL>FhV(fC!}JNaV#dcpaukn>4F=Z$8NhB+((Y zEYWU$Z9j|md?PScTMoj&T7>$P-!5?lqm$+HI>6pWqWdxq9NX3)p3GjR2ZUZ9kk!wJ zs3~Zd;5q-)<8k;&=AYdgYjKxOCaQ}>;PGlnw7DV`;AQ*e{!%c)N<{0HjUH&Ukqlq> zqi;@!=c4|N2b-~*A@4>ef%)kh?!epBPT1zx{IbZ5V-x4Ee&v*-&yaPfa(6`}V6v3? zr(_$50<>S>vd~TnPg(@V!=OxDgzVO3(eZOb*S;@(%%iE+iIn0+fp=6rxJkX=e5gIoCc66G(_RZ25tuuYlV$O2% zK4^7>)B;h1NpAW;Q#p-?2v=3gvV6W3A$Mw~WB3GPm^*YZ^W|}2`c&?ll*)iNjkGp; z{;T);QBCM{Arq)%9Ck;C<>?`V9P)~YbyA=q7HgQ4>m;AZCkDSKi`=U?P$ydN+Y?^d zd8v76kQI(>-}NYv?s?F<{0c6aur9MSqla!`W;x#cS+C`~zR`wE&7_z2RXEd32DHwHWN8ELu1 z(9~P81;6^s`z2@*>*W?tZJynD=njb|;F9@Ncj%2wP`~retH0VNloj9`>x*ESoM-kn}gOS)YVzm)T6*C z5>a!H=+w1~2M=0Cp4%KCblJG-9}V7ine4Bg}+fe-PaKr9{U#{HomxOFxs zd?De>%g&3Dyn$RI3}df7?qvB7FE{3+_k42^P$?FOY@Ic=w)1Cm(mX$4MeF^@$?{e; z0X7ab^c3Z44(VG9&ql<$r_{keRN-{g;Dfzu8g?@gj$+%8KNtCVAgCM#Tvq3$nUMwo zvFIC~4kLMJUc>23KtB#=6kyt12ErX5>V)Q2$*1nW8mg&r!-L;J&jWx9pc{ZuRBt`X zXE$fqFq7vU>~{Zad=1}itgZ=gVDF%J1>c9iA!)%SnH=f38nHN+<*Yn$lERNUKVHWZ zb}`FEeJ193lO@0uM=Y-%)$&~T$v^>B2w40=pOW9Dgl6(6=E6FuAsCSOh-LSE4vt=` zxSV`qPRVQBrZAN_p&ceVoY&-=0?*7*q}?^?zMgLnc||Rt}gltrF<(E@N08r0Vp+G3)u74wBHc zWmt%tpXxrgdbhYM0iQvkV~G~3ajXwcBkO1H0=z_lOC5*zP3(RcOwd!NPBZB=G`4U$^a>Ay!MEi5Md8e7%m8836}76+6bD_>&XCg!ck4htUdlGY=!C#&J^ zUr~f`&Fmqetq~jt(U{YwCQ`M033sh69#wplJ6O(M4%{)Z+?gxIRn4yCD~TaTM>BV-bxbVNqpXRF{*pwAR)nZ*rh$aw#N}L6h%&qb z2KAK&P6wKNX1C$YycS>avU^n}unOjaxh&C<3>%#}bc-X7ddnw}VokBciPP{sFJyn@{P0n3Y8zLIpddU2-Z zwpfVD^T=wxcVGCNA6v%>{lgymXDiHFMLY~usoY_zD_*I%qL(J^yIwA*P)KJgIA|&J7K9qPB zko!4}BiDPE@cEh9?&1!ofsJu$`9LGUsUzWYvV4eK8gP%yKm_3#zR}AtGPEq2)GHZN zsiQ!(AmWkq5WY;WFv@b$kY5P;Va}s))UyYmuHA+|cH4^c*&%4068%brlAy)Ns}X}r zvncKEG|ZV0eZP`iSM?IX-NQ*@y->+Af^lracEVY~{3h#SO^QH&7K79gwhU5eP+drY z#6a!=koFNs(>CPFCsS`-o|5H%&XjcwgA8!(e-=?`3ouf$1TMd9b z?q8UI)d97fZ62q167hey%~=8=lOVUo4QoRX&Pxr_tNa%_9+<>frwu89|93q>VYW$@;EhCJ7n?(v<_u#?fO3ir|eERD1t&mf_@m;^DSQ) zRNyvDWL?Vt;Ax7Gzzy~i-05SChAk;n|AJo+yMc~yJ|pf3cv9OIhL zBi#XJVL++sK~~W20Fh1OQ%f>X%>kl;--a@MA*W0PH0VAw`XwmeK(Ms~+(!I&7s6l! z$wIyDt`R=z=VFvrZG@#^jo7T_N=ExEbf2QF|E`qsC)Z0!1} zLpw4Z+{~T8)!@X^7M(-2J)RJKU$cuPo#^zp<(J1p{%&*qg1^UOdQm0IyO_TmEj}D8 zjuPz82|f)KB<*NxJ$ioJFEI9F3%MYsY!`U)?Omq}H(@bhy_EGnP*$f2iAC9`=jDGa zJ7ng#0{Wo}p^=oLo}GM}Cy)5h%juk39JfoF#}YoTM8Gu4)A$si=5Y%CGk>a>%e3q- zj82+S+bA+2Gi%d@_3#zDck+=xG|3a0WsCyWirjY&U*#zrQg5)-cvf}zLr3mnHn+40 ziHa^fk})$|Jn-2a^&M-DfZnwJHEb9_+#;7Z?a#v@vcdE zed$6B-U3j0MhvE0Hb%P)Z1}f54Q(x5STC$P(d6}tdsbNVSRdAPik!|1TK9e>YHbTFUnTDJ+cAbUwhO2A+mqx~Uj2lE>=qI>us0g)xfG#MX)PPUZP=6TooySuzeH3x zn)a^QsH3mnRMxY!x~|34+w}-` zH_9s8Y6-bo$s5n$5e|qCsV-{e2QyV07-uMC?~Z;8ICR)}M|HnhvfpZ-xqE0>^YJ1i z6qe62hcYtnW(^-))Su#QA>#^>R|~`>Ti9U{6Qu|VS<18<%V3BKv+ka2!nxi%DU+^jMQ|`5wTxhskZ5J*VZ+&`4>Tts-B_k95BwVwK$18>so#PP zoO2i{7bgDCl8wi2=hh9%15UDWyG6dCTt&!ds+jG|VeiSNJaWEr{jo<)`g$&E$4C&k z+;GKem;grCLfwPMp@YGQ%CO9Arzt~%siaeqqdkr{P8Scl)wU%gQ-$t9NJ3B5I%kK) zKDRa9@kCVUMu4Wye|JB5ng({S)}wrDxwvPa*RQb$dq9#npqFypQ&>H`IZ#~JvCgS& zHT%iS`~TqndnE~7x}VYqWm4H7%nC`x z;5LD<3$q>!I}a^?<305iG;XZ$pz{E)z>}hx_pi)6!tO)ovsn}ad=XzXlI49mYam0O zD=9uJGpJ*aPpl{%okwGYl9;X%w<;D+hFt?R@N?agl-RC}Gywpm{0U$SBgb*j83arRq%+pW-98R5ivQy0G| zUGm$RtyOXS1jtG*j>e@{{4z{~A~`7CARIv8Kl)Cg#0D1SUXupeVm*Ll8#qbj?c3A>^ASzK5?1r zN8E}9qZA>is!5S;?8vejnB}>NjiFESR=Pg2y+o?-_d|9@3n$~q0P)bD>r*5*=~;*m zkMw7Sbd9DSnd8H=8F+0MulI#A$V1ni_E}{(1Y{0lvTuh7(N3xV9YRM|?u!;HX#B@m z!+8!wG;lv2UjG)to4ic;I&`WF*^@_dBVzeI(8z?;GV{N`pNyp^0l)u_?9t!5$6F3s zRN&4R3QUZ-j(n9e84o1A*dZ-aJTG1e_OvI!=qA~%&aR?%VTg#2`bKsr7c|YRdT4)Bf zkp=S4&YV~~`1@>Fnv0@*Ydb)S_*s_@YEsn1c-}2I!_&6Q{Zp;vr*yFhuC_Q6pepYI>wvPZc_r_ zKt%{?9a6`#6t8=BPoXW;xWnUVmCu)^ZLvrEOptt7a`@!_1Kb*`g}m1t$fVy@%;iMV zM|+hw?a1{b6MlaTt0z=n;5Qc_A`mFDHZ2wPA+mxI+_%UNUKl`}0|!`~)XXNqpRqBq zrp;K#Ff#kH2tDFfW45j$**a(?wz&nI?yQREf_bJvw+G2yM)2BG6OJ7&QCU1&Zf5$e zgNl*t!`I=nNpOFE%;K2K4>krgkjuyUPVd`51z$g|8JzBfK(ypGkZz-AfCQW_Is8+Y zG!Xm4Lz4f};SP23Hw&fy32t>UF*2%N_Exwwd?Dk<{`W8V7$B`}IC6OPL<($SrUI%wjQvRo`zeEWBTa_%TEMvn!(Tkxh&Fla3gNPzhQ~ zh+JSRG`d9KjNJQevb;j41jGXv!^J4@NV*6l!wffMPtc~ovx@3s!G<6c!EwK7dtD%3 z#7lqOYjE1(SXttX^x9`6UUt>l4I2(GW^zu;c_q|@B3zUrvl;?)XT6-TnvR_;UnZZJxe}E5MMc?yJ&C15+ z27WE-W@vO1Dq1^|fPJ;ie4+43bap}LYBz{H$ImYR&?I0lSfIRi{z*=!Ul(F^_m_VO ziX9GkbpWrYeG7?6sTe_%5p5_cD!Pt!RJtd?#>RHl!*em)%krXT-E!YPVerGCf+F!o zqJSposAPfTV%}HBpbx_xqEFd4q~Qya8$y_xfG^AL<%84RY--=NL84)(u^PyiRh{n= z<7QYZ!oAfBdBj=;Dkd&bF?c91(V=GMuxFI3k$#v*BbA0D-$RxvY1Uid57}93_@MJ4 zXS%aAX2r5~i$+rIIV_PuPl!G;ohMffr0_Ba zv#XXf>dREtN1heRKip4=XWH1<=zTv611Bc(-tDO0ES#JBTA!6q~~&m&`2p1+=MN=;P0XT5HajM{n=hc_~%M>zO(BK6yqu zQB|Fz5Z7j8i5?z!oO!+AK*YKj&AF2Zvfb34>*RtbnDwUN)!R&O z#l#33A|Jp0S`4F;YCgIV+I=Qn|0YSuS}atuXS16R~t(*usqVGgemZS=MYDlcxvS*vSdMf=P^Z zVV;Ph60M~C)*)3u_bVpFlERV?IX5nC5HwEuLgNXhrVZ4e$xo`Jq)#7>mN~_$cfn$HkB zAUhN{IGZS)4V$#%ZYgd%G7*h3}q`4wNh#%4PxC6beV`qNlC$9 zrc8?GRcL&wtt93G5sa7VeDQ8TTsC$ItNS5TTWnHk{)XuJ)2Xv(6}>m_`jO4McsdQI zUJKv^$nN>;N2=9keS@G$L`wR4CrGcDlVPFqbyqJAPS}-G*E06rJk{L;$)c5A?%D7^ z@9bbb4bQU50 zQQ>M24d;PSy#d5INr2X2{Ey^ySqs;trg!dv)fmi^{Pi;jJv}9{-%KAox?35K=n&kW z0Ee;Qkn(k%r@Q;{9;d%3AG_`$$HdDxi@4{gBjGA`qK!ZOT&Z=^6FSm%cp8%73aqS* z%&K{Ecn+2@`=^xamZyibGrYk!LD zlv(%fFO?Gt#hqD zuWKtzrcG*0K1n|phckdyv$Q1KL*_9QZ$legbk)=pTJv#=@fZRRH;7ew?<8u|_tYHO z^5)KQb#qwf+OMpp4m5r-1P=)f3@GNK{HuO{-?2|T!WoxMukdvt>An`S#? zI-p|le3f87fxAl)(imd2-JO)xQDYK_t5CoV%^@0QI;t8NG)xy@3*4GINAS9PlNToVV2`hBP&4%7vs9h z!u{Nc#NEM0iaB%4%LkKBpf154w7*^lxVMjXkFFIr(YHQ(15xq(TdVEiSU`=9jIK53 zHNd-=;N4)}+AD)C4AL^EcK?`#D85#%n{{{0R9zp4 z;3a8<7_GDvtrU0;Q5(kpJ{nl}Y4><+Th2Jvhs<7O`p99$DrE_ z2{pa)|HE_2!?${owKXu$<`Boz%*$NthyV9Z@ttJVKrS!aSuZcI`akuqI%1IdC~hkf33-vCy)oJccdoemm$15 zym-I)ZmE5)2l!tMz@gf!{{5?^!$*hZ(AK(c5`ia-lPe}V8XDPGs83hHqde^foyb=) zHjNJkkOvb_>2XU)`R4eAM^k=+hl^L=UV~n$BY0RBdes55BQ$z>6nVI8-Pck6n~^-H zN7#LYMdxPxlXPj@LpaZSj;MPMndu`1PLQSHymb zPyV0s&N8mau>JcgiXbHoA|N3lC^1TqkQ7l6rIm)Ez(7Je zhV)>JhEW?5`=2QH|94k<4dMBT{*gYxWD zD*&%Z0n`UFz_aegJRfZ#=jRcOKzCdePw}Jr&-?7TvpR!sM=w^=v~gG`a^}qUU!sB0 zSS%Tvf+U!I{LagN1w_-2TkC$*N)ksxPHl_}eWI*56c9D)YzvCJsDI#=e!Npr-Y*AJV&%GfKXC1bJ2*b82LMUIj53>V2nguuz#JmiJHr3Dra$}9c?`WHU9ce!gJVy>7r`}AOP zqfw%n5~h#Dtl6%8j)G9b8guVTsI2W5IQv(zi)(dP)uXHe0o{Y9rxdBZnw%qRm`N9j zF{#-2DVoXX^PL;c;1V1qTLTu+UMU1PN{=N3brVzZsVhz^(s%0X>shg4j^{3yu@dj0 zv}A0@(K&fT=xg;?;J?~ZpdETQ#9V(Hbz;s-?Vs~&%AwUx9q|A5xZL-1fnC%hp9~kD z@>0HmYlJl%5$m=RoHI;|$T5DYkacqd@7KvvA3R{0{o1JRkvT6*vLJT?>j4oB&LNs~ zqCt|RS;@7siEO=KO#knE#~a4x#)kR(xRu8O6`+e6&Dyg#Szr8^>Wli=kiQ%4 zW0Mjm-FIzcrad-M%!bO|yOCf3GBWjb zYH^gplFhws_@U~`UE-L^!Ha|;W)mjt@)VduLCz;rEV;3c#tn}th3ct6pHc}3k(xiE8qk@`$xp(UGqy1eFK zY@c`rrT(qdH;{ZlKpA<$q9uXYq%63XXJMdkxgtunsyEoF1w_-XFOMpgcx3eSJXoG| zu4{a~!=e=ie;u%M`C%oFeCyJ+zU`PLn!qwN+-6NzBCW_wyk9B$eyX7Eh$BE{{AW9f z(i5Y7S*&NFC@QP-RXF-~6-uk^EkC!iR&>?%`!4L21GhXgJQIIeogmna7n@l#8TX`O zH?k48#4p&#t<4P>@Q%CAwhY;cMB;8VLz4X@_Nu4tY?_SOO%$d-}NuDVL?0IDrL#PHC_BD*4~d%gfolco=T@%Pck8-npj4w@{P~KX!sTc0paW zWtL&>vYTOjp)>2j`ffmilb}-)vu>jdtB-3^I~lf6|0+uwQ-&Kp^ojEmHzSLS8V1%3 z?#lMu)d|rKo{g(4r~unGF$gn=bYi0={$S>l^L`RytzCJlCoXrpXfC-vdae-Md2{Rq zgK1Jt(v{J!_Wv4Az0XmAg;42N=}_@*`ng6kOc6`YvomrYAz z4Jlqgv}o)lJM;t-LU#6m<5T55A2D-O`9a*$pP^Bej1BJ>M&gT0SR5p~tY-Ue*gRr} zn=R>q?bx&9fLuwE=|boqLRLaIwrPin)yCyrB7PF%N;DgyLR7Cg*vBOQTtF;onvd@A!26l-nX{otn?_L%LTA0OOxKw zS0$*m3olJCP7R)>i_p-la6-cKP=@2!1k(M}~O_IFG! zq54=8T|O{=z4%r7PK3Z`)a%D1xjll&7=+D9=!pYMU*N~-E4Yf7VTO`C^}1=g znU~8h_EH}#pW1zM<2Uq>y(|R4Wfa*r^GQ=>y*=nn{PI&L+j^Bws3T zd-rb!xG0&#DxOm}KL*kH_YGqzC(9i{#M&zb@0sMp@&ovHA3NbWyEs#26!|2_A!Z|* za(JUdX!0Yo&&Ic787KT23DB5*<_I^%Vuf>)y`NfTE*Y2z)m}Ffj}*Vg6muOofJ|(qPTxV*X5VMpS1~&h4H2f<4gajc(bWhDv{@&h{zoe`XxPd0YBCOKWI~;9QigZ_`w?^y5srxrp5y zu3yc8c@QlN1ne~}$vp~$-^y7m)=Y~1`U$dnyI-THW+LgHhO*{Ndx}41;7UaR-<+BA-ej&!gtb49ExQ&~p>H&esr7 zImAr;^LiQG%cA1^(LflZ0}tQ#_jY169+-iON0waMq33}v3%n19UDLeQ;ikYuUq`rN z13?6;>lBsBB2V6JrWNv`81R>7lu|DVe%E@2zm(TI**Z%@xvIjEQVs9@5R z2=jqhdB1c17VWnAv=Ke^g+u8vqNN<7Y3QYu#R-aL2Fe=MEIv4LY-Ii4@0eKQ%+}L@r7q?*Foyu+&jHbLtmU;ysNLdL$7ADr|*Hf zw9{=XKTZoO+z68@T;N<*?0|IWojaQgvaS>@mqlj2lIb><5#QvqNF*r7-xcJSu7ZD5yMy#iDl2+@u=sw2QiDu zXM9KyF{N(e{qIK6C`h*d95q#=(YC8luL`wRvfP5hWPiJDUmBpBjC75#7SZ1M^Vkfw z9l6B}#B%NAWmLA`WMAluC~+>@on1AcqqS1z$hjLDpY0NmUMfS)j@Y!YW;M=G|H($v z+~#rmft81}8ku|7oq=}{xvlr%e(!}^c&#(vbCLO?FzVbYv41@@Mdd{6HS43%_FUqj znp&kbtLgGj=m~DKfJ^mO!#C*S&xWEd@A{xA(3#E%+=R)+tC?-bXAYu8Wr&|Mm9!)_xg% zTw^}|YJ72eBgQW#pq8S!&VUQS>Ee5VR}1Ps2y{9Hu}K(OTP8>pIon;EC~@D?bo>V4 z%p94iTDNK+g+TKxy9dqhB|E$Wb3cfwmc6CsV>>4zA!(-Jj>;$de7_Huf-pb(xbFB5xrS#YDFJ0-2p$QM^! z7DF9F?4@MvT*r#u99A#jMs9qC%fpv^FZB*kY7Zxw*ZwS>&*+1_(Y1Ww?CLJU@5*Aw zz+ke!pLW^J+}y8vF0eRwYQVTG>g#OKSK9(%PuS3S`}IgxWoc5$2VZS&t*j(7cJ~W? zL6q8jfqcm~l2FsKVeJ2WCO~Xt%B3NxEHan&i&r}_z@9V1)}01XE%nW3zO69<+(zOT zF3q!h&U_7d$UCedCdZo0YNW{HV2S;nznbrq?gRo%ZBU^4vARs)$%Vt_!< z)!sJ~CfCSlU;jtv!_tERPNmkuKu3(1@Go?3>09Hs?L&~0G&qi!!Ha1LGIk|SPZ7OT zjjqcOD^)R4kH}_m`a&wDU+{grHo$8g#?OZQHMT&CpP`VjMD+mcCY^Vv-JAWX@NYxm zPGfS2Ds!_3BTD8Ygw)$l9BNb1WkX*p01a?NT7XQm`^&G|ao8TVtHX2^>Dh)8?h^QO zf7&G9?cQYRYALY9hpUG7OJ`$uh++xofAJsCsuFQ?+Di{d?`)n7S1PH}?3|B5AiC+@ z9TkKu-kDE0y1#xu$UCBiEhD^sKC6XC}{nFXX~n zb_31;H)iU;roPtP@m0LxXW;9NxY~2OZ@Fr?L*0@{RM`8IFOW@RO|DNTuZ}9h-#QuV zS?K|9og|@J{(o!UpweOEy0Gb8n{uWc4YbDc5fWDg=^0paX2J(B`zR3nWsk%Bi2CE8Ypp4PkUGlz*0OgO zJHN};n!=Wq+!w#2HDKjV!3Kh`>yQFbO)C|ni;3@ZgN@*)$%H<$v|hb}3b^_%3{^~L z*1!V|anKKAL;!^r$Rk$-Cw!F$B?~Q49HR;`Vv$}>c>mo>S~ZWWmqf+omQGf~Vhr(& zkv~PN(J#SXMb0?{0RPb)@I;+s{4vubP`1Zf3MjdAc-YQ0Vuxjm z9d0BP?nxhTJ_PXQtTl5yy zPHo{=%bLO<)OCJxh2d#5484KyvedQhqIHDT| zfPNSkXmjYltMOGE51S0nJU^vXI-|Fi(Qnq@THRun0iqoW4IH!Vq10Y97pK@Rz0%OE z(VUn_=W;AGjnuwbh{2E1ahQSyKuzR1HjP#%TMEChttWE zd`Gfw5XkcfD$tmVA}X{>-n`J^ldnOn%y%4#v-Iubgme9_d$^0{pcd1+wg0z&n(gsG zY(;)nxy~C`VOCTqpHaKR>5mzyW0YWFVQ$zwY-Q#S+DKkQlCpDmCP{WH4J74XBwWMc zUPB_Ck1C*OM*NjSrm~4;dXW6FUmZCCHW737-;fgwWuy52@l&o7Vz1#~ccr^4{fanT zat)iFBk{zRTRk&yb)YCPO#Qmscy_>}LI5W|?uPG}!G`@wi-YxsJIZ^Qmxkuu?Gst$ zc5W3*-%k>z)xx*~r%|kc`nw+#Y-VN#axpeazX8$!4?g5*5`4A%aS8w`($Y*#OiUg; z0N|%L-I)My@t?l=_DvewlyroRZl?Rk0MMg=0G{I!l;Z!fPc>aQ5)li+*jna5P5Qts z(5uhTW7gu~w5_tz1t!Xzgrweq_MMkLJdr(P0ED)oYxEKh|qR&<4xQ9r%ZJwstL; z*s2_?ez-f+K+5U~c6cAq8BcAJ9!CU<=!$OEYYj-!E^K{^QScnBHcN%%yO$XfyNv#N zxxd=QZij0afE&$){nnZ!y3+Sz|B$vx2gAW4Su_kllKZ!lomqh3UJVV<6s}!kX-TDw z2McEKaX$%`!|HF;nTOpLhm{*cfOWa6oZg zb_6gr|NVI*fQTh%#W1Uk&O6%45lHyx%C1L&1kx+jcKMVPFuwj)S)dcBD7CWul*W7e zMt9~ii5s0a%sp*+ju(d%ejrtF2ZBh@1~cfy!2N30L;OL5@Y+4=YsY`#zhXfeIPD3P z(XXnde3t$3Si_)wLe?9?#^dh{f$$zJrAZlk zlHj@d>qm?e?6_t;*>T_p7U*6h4SXXGrC+i1jhkT z^k^L9BI>s0&N{@(as@B4gYRDhTi=d6-q-k3Axg;60w|t0FftyXem^P-p1(7~FjB^5S$4af z+jo==a_V0(1oc6Ca0Ot{e4{=A8zXk@4O!>O{xz_#7{>F}V$TviQmrMLD z}%f37FfVv|9Sl;2n$3L}s=2rA>MtQ6&Fr5=7U02mPu7PeEU{l157marX z4l4NSiJtjav0p4+$gEDdpVM3sqhQv|4^L~L+&tQnT{G);r*Wn*IYJF>x+471F5xfA zTot3W_}lwN_iUiSZgh)~&e#SJP~X^^Ep>TYYq3ifyq<;atE1Et zc9=g*M%rnRPPK~q`Sy29LNT)3yR|4JjH?j9Mh@9Sjkv)jq4X4|>&)GTRuxC~=tv7* zyCd8riD-BV+a|!hFbp>`S?XC&kWcS2YM`SGViegL*i$l3AQuY?s(nc||CsxJ?3wCd0N~OS`i(SLEUn>l@aiVM?T7Qjd_B_I-fY$YnHvhtv6I%KizM=E&<%E+~i$nG)$bFxv0M2~thUZmF;C6fASrmH{2%+oo8h#m(K4 zS9J=}aX-PR)%F3nHGmh{4^rk|-1avUAI@%yeJ@f+nz90uc8~UA(34SG+@YFQofZ$A zUxqZ$FK7`|PCR(uk99QPpUJuxp>VkDDNo=UD_=VgK&qyY>4}iAzDnoOHKjUvc$ViH za;7yFs(&iO**sqJ)-q^$!0Z1%ziOs!cN3VbQV;|=`J+PcDxDMB8hYwQs+KSQ2M{-n A%K!iX literal 0 HcmV?d00001 diff --git a/shared/sentry/external/crashpad/doc/overview_design.md b/shared/sentry/external/crashpad/doc/overview_design.md new file mode 100644 index 000000000..01dba1736 --- /dev/null +++ b/shared/sentry/external/crashpad/doc/overview_design.md @@ -0,0 +1,567 @@ + + +# Crashpad Overview Design + +[TOC] + +## Objective + +Crashpad is a library for capturing, storing and transmitting postmortem crash +reports from a client to an upstream collection server. Crashpad aims to make it +possible for clients to capture process state at the time of crash with the best +possible fidelity and coverage, with the minimum of fuss. + +Crashpad also provides a facility for clients to capture dumps of process state +on-demand for diagnostic purposes. + +Crashpad additionally provides minimal facilities for clients to adorn their +crashes with application-specific metadata in the form of per-process key/value +pairs. More sophisticated clients are able to adorn crash reports further +through extensibility points that allow the embedder to augment the crash report +with application-specific metadata. + +## Background + +It’s an unfortunate truth that any large piece of software will contain bugs +that will cause it to occasionally crash. Even in the absence of bugs, software +incompatibilities can cause program instability. + +Fixing bugs and incompatibilities in client software that ships to millions of +users around the world is a daunting task. User reports and manual reproduction +of crashes can work, but even given a user report, often times the problem is +not readily reproducible. This is for various reasons, such as e.g. system +version or third-party software incompatibility, or the problem can happen due +to a race of some sort. Users are also unlikely to report problems they +encounter, and user reports are often of poor quality, as unfortunately most +users don’t have experience with making good bug reports. + +Automatic crash telemetry has been the best solution to the problem so far, as +this relieves the burden of manual reporting from users, while capturing the +hardware and software state at the time of crash. + +TODO(siggi): examples of this? + +Crash telemetry involves capturing postmortem crash dumps and transmitting them +to a backend collection server. On the server they can be stackwalked and +symbolized, and evaluated and aggregated in various ways. Stackwalking and +symbolizing the reports on an upstream server has several benefits over +performing these tasks on the client. High-fidelity stackwalking requires access +to bulky unwind data, and it may be desirable to not ship this to end users out +of concern for the application size. The process of symbolization requires +access to debugging symbols, which can be quite large, and the symbolization +process can consume considerable other resources. Transmitting un-stackwalked +and un-symbolized postmortem dumps to the collection server also allows deep +analysis of individual dumps, which is often necessary to resolve the bug +causing the crash. + +Transmitting reports to the collection server allows aggregating crashes by +cause, which in turn allows assessing the importance of different crashes in +terms of the occurrence rate and e.g. the potential security impact. + +A postmortem crash dump must contain the program state at the time of crash +with sufficient fidelity to allow diagnosing and fixing the problem. As the full +program state is usually too large to transmit to an upstream server, the +postmortem dump captures a heuristic subset of the full state. + +The crashed program is in an indeterminate state and, in fact, has often crashed +because of corrupt global state - such as heap. It’s therefore important to +generate crash reports with as little execution in the crashed process as +possible. Different operating systems vary in the facilities they provide for +this. + +## Overview + +Crashpad is a client-side library that focuses on capturing machine and program +state in a postmortem crash report, and transmitting this report to a backend +server - a “collection serverâ€. The Crashpad library is embedded by the client +application. Conceptually, Crashpad breaks down into the handler and the client. +The handler runs in a separate process from the client or clients. It is +responsible for snapshotting the crashing client process’ state on a crash, +saving it to a crash dump, and transmitting the crash dump to an upstream +server. Clients register with the handler to allow it to capture and upload +their crashes. On iOS, there is no separate process for the handler. +[This is a limitation of iOS.](ios_overview_design.md#ios-limitations) + +### The Crashpad handler + +The Crashpad handler is instantiated in a process supplied by the embedding +application. It provides means for clients to register themselves by some means +of IPC, or where operating system support is available, by taking advantage of +such support to cause crash notifications to be delivered to the handler. On +crash, the handler snapshots the crashed client process’ state, writes it to a +postmortem dump in a database, and may also transmit the dump to an upstream +server if so configured. + +The Crashpad handler is able to handle cross-bitted requests and generate crash +dumps across bitness, where e.g. the handler is a 64-bit process while the +client is a 32-bit process or vice versa. In the case of Windows, this is +limited by the OS such that a 32-bit handler can only generate crash dumps for +32-bit clients, but a 64-bit handler can acquire nearly all of the detail for a +32-bit process. + +### The Crashpad client + +The Crashpad client provides two main facilities. +1. Registration with the Crashpad handler. +2. Metadata communication to the Crashpad handler on crash. + +A Crashpad embedder links the Crashpad client library into one or more +executables, whether a loadable library or a program file. The client process +then registers with the Crashpad handler through some mode of IPC or other +operating system-specific support. + +On crash, metadata is communicated to the Crashpad handler via the CrashpadInfo +structure. Each client executable module linking the Crashpad client library +embeds a CrashpadInfo structure, which can be updated by the client with +whatever state the client wishes to record with a crash. + +![Overview image](overview.png) + +Here is an overview picture of the conceptual relationships between embedder (in +light blue), client modules (darker blue), and Crashpad (in green). Note that +multiple client modules can contain a CrashpadInfo structure, but only one +registration is necessary. + +## Detailed Design + +### Requirements + +The purpose of Crashpad is to capture machine, OS and application state in +sufficient detail and fidelity to allow developers to diagnose and, where +possible, fix the issue causing the crash. + +Each distinct crash report is assigned a globally unique ID, in order to allow +users to associate them with a user report, report in bug reports and so on. + +It’s critical to safeguard the user’s privacy by ensuring that no crash report +is ever uploaded without user consent. Likewise it’s important to ensure that +Crashpad never captures or uploads reports from non-client processes. + +### Concepts + +* **Client ID**. A UUID tied to a single instance of a Crashpad database. When + creating a crash report, the Crashpad handler includes the client ID stored + in the database. This provides a means to determine how many individual end + users are affected by a specific crash signature. + +* **Crash ID**. A UUID representing a single crash report. Uploaded crash + reports also receive a “server ID.†The Crashpad database indexes both the + locally-generated and server-generated IDs. + +* **Collection Server**. See [crash server documentation.]( + https://goto.google.com/crash-server-overview) + +* **Client Process**. Any process that has registered with a Crashpad handler. + +* **Handler process**. A process hosting the Crashpad handler library. This may + be a dedicated executable, or it may be hosted within a client executable + with control passed to it based on special signaling under the client’s + control, such as a command-line parameter. + +* **CrashpadInfo**. A structure used by client modules to provide information to + the handler. + +* **Annotations**. Each CrashpadInfo structure points to a dictionary of + {string, string} annotations that the client can use to communicate + application state in the case of crash. + +* **Database**. The Crashpad database contains persistent client settings as + well as crash dumps pending upload. + +TODO(siggi): moar concepts? + +### Overview Picture + +Here is a rough overview picture of the various Crashpad constructs, their +layering and intended use by clients. + +![Layering image](layering.png) + +Dark blue boxes are interfaces, light blue boxes are implementation. Gray is the +embedding client application. Note that wherever possible, implementation that +necessarily has to be OS-specific, exposes OS-agnostic interfaces to the rest of +Crashpad and the client. + +### Registration + +The particulars of how a client registers with the handler varies across +operating systems. + +#### macOS + +At registration time, the client designates a Mach port monitored by the +Crashpad handler as the EXC_CRASH exception port for the client. The port may be +acquired by launching a new handler process or by retrieving service already +registered with the system. The registration is maintained by the kernel and is +inherited by subprocesses at creation time by default, so only the topmost +process of a process tree need register. + +Crashpad provides a facility for a process to disassociate (unregister) with an +existing crash handler, which can be necessary when an older client spawns an +updated version. + +#### iOS + +iOS registers both a signal handler for `SIGABRT` and a Mach exception handler +with a subset of available exceptions. [This is a limitation of +iOS.](ios_overview_design.md#ios-limitations) + +#### Windows + +There are two modes of registration on Windows. In both cases the handler is +advised of the address of a set of structures in the client process’ address +space. These structures include a pair of ExceptionInformation structs, one for +generating a postmortem dump for a crashing process, and another one for +generating a dump for a non- crashing process. + +##### Normal registration + +In the normal registration mode, the client connects to a named pipe by a +pre-arranged name. A registration request is written to the pipe. During +registration, the handler creates a set of events, duplicates them to the +registering client, then returns the handle values in the registration response. +This is a blocking process. + +##### Initial Handler Creation + +In order to avoid blocking client startup for the creation and initialization of +the handler, a different mode of registration can be used for the handler +creation. In this mode, the client creates a set of event handles and inherits +them into the newly created handler process. The handler process is advised of +the handle values and the location of the ExceptionInformation structures by way +of command line arguments in this mode. + +#### Linux/Android + +On Linux, a registration is a connected socket pair between a client process and +the Crashpad handler. This socket pair may be private or shared among many +client processes. + +##### Private Connections + +Private connections are the default registration mode when starting the handler +process in response to a crash or on behalf of another client. This mode is +required to use a ptrace broker, which is in turn required to trace Android +isolated processes. + +##### Shared Connections + +Shared connections are the default mode when using a long-lived handler. The +same connected socket pair may be shared among any number of clients. The socket +pair is created by the first process to start the handler at which point the +client socket end may be shared with other clients by any convenient means (e.g. +inheritance). + +### Capturing Exceptions + +The details of how Crashpad captures the exceptions leading to crashes varies +between operating systems. + +#### macOS + +On macOS, the operating system will notify the handler of client crashes via the +Mach port set as the client process’ exception port. As exceptions are +dispatched to the Mach port by the kernel, on macOS, exceptions can be handled +entirely from the Crashpad handler without the need to run any code in the crash +process at the time of the exception. + +#### iOS + +On iOS, the operating system will notify the handler of crashes via the Mach +exception port or the signal handler. As exceptions are handled in-process, an +intermediate dump file is generated rather than a minidump. See more +information about the [iOS in-process +handler.](ios_overview_design.md#ios-in-process-handler) + +#### Windows + +On Windows, the OS dispatches exceptions in the context of the crashing thread. +To notify the handler of exceptions, the Crashpad client registers an +UnhandledExceptionFilter (UEF) in the client process. When an exception trickles +up to the UEF, it stores the exception information and the crashing thread’s ID +in the ExceptionInformation structure registered with the handler. It then sets +an event handle to signal the handler to go ahead and process the exception. + +##### Caveats + +* If the crashing thread’s stack is smashed when an exception occurs, the + exception cannot be dispatched. In this case the OS will summarily terminate + the process, without the handler having an opportunity to generate a crash + report. +* If an exception is handled in the crashing thread, it will never propagate + to the UEF, and thus a crash report won’t be generated. This happens a fair + bit in Windows as system libraries will often dispatch callbacks under a + structured exception handler. This occurs during Window message dispatching + on some system configurations, as well as during e.g. DLL entry point + notifications. +* A growing number of conditions in the system and runtime exist where + detected corruption or illegal calls result in summary termination of the + process, in which case no crash report will be generated. + +###### Out-Of-Process Exception Handling + +There exists a mechanism in Windows Error Reporting (WER) that allows a client +process to register for handling client exceptions out of the crashing process. +Unfortunately this mechanism is difficult to use, and doesn’t provide coverage +for many of the caveats above. [Details +here.](https://crashpad.chromium.org/bug/133) + +#### Linux/Android + +On Linux, exceptions are dispatched as signals to the crashing thread. Crashpad +signal handlers will send a message over the socket to the Crashpad handler +notifying it of the crash and the location of exception information to be read +from the crashing process. When using a shared socket connection, communication +is entirely one-way. The client sends its dump request to the handler and then +waits until the handler responds with a SIGCONT or a timeout occurs. When using +a private socket connection, the handler may respond over the socket to +communicate with a ptrace broker process. The broker is forked from the crashing +process, executes ptrace requests against the crashing process, and sends the +information over the socket to the handler. + +### The CrashpadInfo structure + +The CrashpadInfo structure is used to communicate information from the client to +the handler. Each executable module in a client process can contain a +CrashpadInfo structure. On a crash, the handler crawls all modules in the +crashing process to locate all CrashpadInfo structures present. The CrashpadInfo +structures are linked into a special, named section of the executable, where the +handler can readily find them. + +The CrashpadInfo structure has a magic signature, and contains a size and a +version field. The intent is to allow backwards compatibility from older client +modules to newer handler. It may also be necessary to provide forwards +compatibility from newer clients to older handler, though this hasn’t occurred +yet. + +The CrashpadInfo structure contains such properties as the cap for how much +memory to include in the crash dump, some tristate flags for controlling the +handler’s behavior, a pointer to an annotation dictionary and so on. + +### Snapshot + +Snapshot is a layer of interfaces that represent the machine and OS entities +that Crashpad cares about. Different concrete implementations of snapshot can +then be backed different ways, such as e.g. from the in-memory representation of +a crashed process, or e.g. from the contents of a minidump. + +### Crash Dump Creation + +To create a crash dump, a subset of the machine, OS and application state is +grabbed from the crashed process into an in-memory snapshot structure in the +handler process. Since the full application state is typically too large for +capturing to disk and transmitting to an upstream server, the snapshot contains +a heuristically selected subset of the full state. + +The precise details of what’s captured varies between operating systems, but +generally includes the following +* The set of modules (executable, shared libraries) that are loaded into the + crashing process. +* An enumeration of the threads running in the crashing process, including the + register contents and the contents of stack memory of each thread. +* A selection of the OS-related state of the process, such as e.g. the command + line, environment and so on. +* A selection of memory potentially referenced from registers and from stack. + +To capture a crash dump, the crashing process is first suspended, then a +snapshot is created in the handler process. The snapshot includes the +CrashpadInfo structures of the modules loaded into the process, and the contents +of those is used to control the level of detail captured for the crash dump. + +Once the snapshot has been constructed, it is then written to a minidump file, +which is added to the database. The process is un-suspended after the minidump +file has been written. In the case of a crash (as opposed to a client request to +produce a dump without crashing), it is then either killed by the operating +system or the Crashpad handler. + +In general the snapshotting process has to be very intimate with the operating +system it’s working with, so there will be a set of concrete implementation +classes, many deriving from the snapshot interfaces, doing this for each +operating system. + +### Minidump + +The minidump implementation is responsible for writing a snapshot to a +serialized on-disk file in the minidump format. The minidump implementation is +OS-agnostic, as it works on an OS-agnostic Snapshot interface. + +TODO(siggi): Talk about two-phase writes and contents ordering here. + +### Database + +The Crashpad database contains persistent client settings, including a unique +crash client identifier and the upload-enabled bit. Note that the crash client +identifier is assigned by Crashpad, and is distinct from any identifiers the +client application uses to identify users, installs, machines or such - if any. +The expectation is that the client application will manage the user’s upload +consent, and inform Crashpad of changes in consent. + +The unique client identifier is set at the time of database creation. It is then +recorded into every crash report collected by the handler and communicated to +the upstream server. + +The database stores a configurable number of recorded crash dumps to a +configurable maximum aggregate size. For each crash dump it stores annotations +relating to whether the crash dumps have been uploaded. For successfully +uploaded crash dumps it also stores their server-assigned ID. + +The database consists of a settings file, named "settings.dat" with binary +contents (see crashpad::Settings::Data for the file format), as well as +directory containing the crash dumps. Additionally each crash dump is adorned +with properties relating to the state of the dump for upload and such. The +details of how these properties are stored vary between platforms. + +#### macOS + +The macOS implementation simply stores database properties on the minidump files +in filesystem extended attributes. + +#### iOS + +The iOS implementation also stores database properties of minidump files in +filesystem extended attributes. Separate from the database, iOS also stores its +intermediate dump files adjacent to the database. See more information about +[iOS intermediate +dumps.](ios_overview_design.md#the-crashpad-intermediatedump-format) + +#### Windows + +The Windows implementation stores database properties in a binary file named +“metadata†at the top level of the database directory. + +### Report Format + +Crash reports are recorded in the Windows minidump format with +extensions to support Crashpad additions, such as e.g. Annotations. + +### Upload to collection server + +#### Wire Format + +For the time being, Crashpad uses the Breakpad wire protocol, which is +essentially a MIME multipart message communicated over HTTP(S). To support this, +the annotations from all the CrashpadInfo structures found in the crashing +process are merged to create the Breakpad “crash keys†as form data. The +postmortem minidump is then attached as an “application/octet- stream†+attachment with the name “upload_file_minidumpâ€. The entirety of the request +body, including the minidump, can be gzip-compressed to reduce transmission time +and increase transmission reliability. Note that by convention there is a set of +“crash keys†that are used to communicate the product, version, client ID and +other relevant data about the client, to the server. Crashpad normally stores +these values in the minidump file itself, but retrieves them from the minidump +and supplies them as form data for compatibility with the Breakpad-style server. + +This is a temporary compatibility measure to allow the current Breakpad-based +upstream server to handle Crashpad reports. In the fullness of time, the wire +protocol is expected to change to remove this redundant transmission and +processing of the Annotations. + +#### Transport + +The embedding client controls the URL of the collection server by the command +line passed to the handler. The handler can upload crashes with HTTP or HTTPS, +depending on client’s preference. It’s strongly suggested use HTTPS transport +for crash uploads to protect the user’s privacy against man-in-the-middle +snoopers. + +TODO(mmentovai): Certificate pinning. + +#### Throttling & Retry Strategy + +To protect both the collection server from DDoS as well as to protect the +clients from unreasonable data transfer demands, the handler implements a +client-side throttling strategy. At the moment, the strategy is very simplistic, +it simply limits uploads to one upload per hour, and failed uploads are aborted. + +An experiment has been conducted to lift all throttling. Analysis on the +aggregate data this produced shows that multiple crashes within a short timespan +on the same client are nearly always due to the same cause. Therefore there is +very little loss of signal due to the throttling, though the ability to +reconstruct at least the full crash count is highly desirable. + +The lack of retry is expected to [change +soon](https://crashpad.chromium.org/bug/23), as this creates blind spots for +client crashes that exclusively occur on e.g. network down events, during +suspend and resume and such. + +### Extensibility + +#### Client Extensibility + +Clients are able to extend the generated crash reports in two ways, by +manipulating their CrashpadInfo structure. +The two extensibility points are: +1. Nominating a set of address ranges for inclusion in the crash report. +2. Adding user-defined minidump streams for inclusion in the crash report. + +In both cases the CrashpadInfo structure has to be updated before a crash +occurs. + +##### Embedder Extensibility + +Additionally, embedders of the handler can provide "user stream data source" +instances to the handler's main function. Any time a minidump is written, these +instances get called. + +Each data source may contribute a custom stream to the minidump, which can be +computed from e.g. system or application state relevant to the crash. + +As a case in point, it can be handy to know whether the system was under memory +or other resource duress at the time of crash. + +### Dependencies + +Aside from system headers and APIs, when used outside of Chromium, Crashpad has +a dependency on “mini_chromiumâ€, which is a subset of the Chromium base library. +This is to allow non-Chromium clients to use Crashpad, without taking a direct +dependency on the Chromium base, while allowing Chromium projects to use +Crashpad with minimum code duplication or hassle. When using Crashpad as part of +Chromium, Chromium’s own copy of the base library is used instead of +mini_chromium. + +The downside to this is that mini_chromium must be kept up to date with +interface and implementation changes in Chromium base, for the subset of +functionality used by Crashpad. + +## Caveats + +TODO(anyone): You may need to describe what you did not do or why simpler +approaches don't work. Mention other things to watch out for (if any). + +## Security Considerations + +Crashpad may be used to capture the state of sandboxed processes and it writes +minidumps to disk. It may therefore straddle security boundaries, so it’s +important that Crashpad handle all data it reads out of the crashed process with +extreme care. The Crashpad handler takes care to access client address spaces +through specially-designed accessors that check pointer validity and enforce +accesses within prescribed bounds. The flow of information into the Crashpad +handler is exclusively one-way: Crashpad never communicates anything back to +its clients, aside from providing single-bit indications of completion. + +## Privacy Considerations + +Crashpad may capture arbitrary contents from crashed process’ memory, including +user IDs and passwords, credit card information, URLs and whatever other content +users have trusted the crashing program with. The client program must acquire +and honor the user’s consent to upload crash reports, and appropriately manage +the upload state in Crashpad’s database. + +Crashpad must also be careful not to upload crashes for arbitrary processes on +the user’s system. To this end, Crashpad will never upload a process that hasn’t +registered with the handler, but note that registrations are inherited by child +processes on some operating systems. diff --git a/shared/sentry/external/crashpad/doc/status.md b/shared/sentry/external/crashpad/doc/status.md new file mode 100644 index 000000000..3948b4498 --- /dev/null +++ b/shared/sentry/external/crashpad/doc/status.md @@ -0,0 +1,44 @@ + + +# Project Status + +## Completed + +Crashpad has complete crash-reporting clients and some related tools for macOS, +Windows, Fuchsia, and Linux (including Android and Chromium OS). Crashpad became +the crash reporter client for [Chromium](https://www.chromium.org/Home) on macOS +as of [March +2015](https://chromium.googlesource.com/chromium/src/\+/d413b2dcb54d523811d386f1ff4084f677a6d089), +Windows as of [November +2015](https://chromium.googlesource.com/chromium/src/\+/cfa5b01bb1d06bf96967bd37e21a44752801948c), +and Android as of [January +2019](https://chromium.googlesource.com/chromium/src/+/f890e4b5495ab693d2d37aec3c378239946154f7). + + +## In Progress + +Chromium is transitioning to Crashpad for [Chromium OS and Desktop +Linux](https://crbug.com/942279). + +Work has begun on a Crashpad client for +[iOS](https://crashpad.chromium.org/bug/31). + +## Future + +There are also plans to implement a [crash report +processor](https://crashpad.chromium.org/bug/29) as part of Crashpad. No +timeline for completing this work has been set yet. diff --git a/shared/sentry/external/crashpad/doc/support/compat.sh b/shared/sentry/external/crashpad/doc/support/compat.sh new file mode 100644 index 000000000..8c5d30d32 --- /dev/null +++ b/shared/sentry/external/crashpad/doc/support/compat.sh @@ -0,0 +1,37 @@ +# Copyright 2015 The Crashpad Authors. All rights reserved. +# +# 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. + +if [[ "${BASH_SOURCE[0]}" = "${0}" ]]; then + echo "${0}: this file must be sourced, not run directly" >& 2 + exit 1 +fi + +# Some extensions of command-line tools behave differently on different systems. +# $sed_ext should be a sed invocation that enables extended regular expressions. +# $date_time_t should be a date invocation that causes it to print the date and +# time corresponding to a time_t string that immediately follows it. +case "$(uname -s)" in + Darwin) + sed_ext="sed -E" + date_time_t="date -r" + ;; + Linux) + sed_ext="sed -r" + date_time_t="date -d@" + ;; + *) + echo "${0}: unknown operating system" >& 2 + exit 1 + ;; +esac diff --git a/shared/sentry/external/crashpad/doc/support/crashpad.doxy b/shared/sentry/external/crashpad/doc/support/crashpad.doxy new file mode 100644 index 000000000..ca1ea2517 --- /dev/null +++ b/shared/sentry/external/crashpad/doc/support/crashpad.doxy @@ -0,0 +1,2510 @@ +# Doxyfile 1.8.18 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "Crashpad" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = out/doc/doxygen + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all generated output in the proper direction. +# Possible values are: None, LTR, RTL and Context. +# The default value is: None. + +OUTPUT_TEXT_DIRECTION = None + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = . \ + compat/non_mac \ + compat/non_win + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines (in the resulting output). You can put ^^ in the value part of an +# alias to insert a newline as if a physical newline was in the original file. +# When you need a literal { or } or , in the value part of an alias you have to +# escape them by means of a backslash (\), this can lead to conflicts with the +# commands \{ and \} for these it is advised to use the version @{ and @} or use +# a double escape (\\{ and \\}) + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, +# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files). For instance to make doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See https://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 5. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 0 + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = YES + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# declarations. If set to NO, these declarations will be included in the +# documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file +# names in lower-case letters. If set to YES, upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# (including Cygwin) ands Mac users are advised to set this option to NO. +# The default value is: system dependent. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = YES + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = YES + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = NO + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. If +# EXTRACT_ALL is set to YES then this flag will automatically be disabled. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: https://www.gnu.org/software/libiconv/) for the list of +# possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), +# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen +# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, +# *.vhdl, *.ucf, *.qsf and *.ice. + +FILE_PATTERNS = *.c \ + *.cc \ + *.h \ + *.m \ + *.mm + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = compat/mac \ + compat/non_cxx11_lib \ + compat/win \ + third_party + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = out* + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# entity all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see https://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = NO + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in +# which the alphabetical index list will be split. +# Minimum value: 1, maximum value: 20, default value: 5. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = doc/support/crashpad_doxygen.css + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = NO + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: https://developer.apple.com/xcode/), introduced with OSX +# 10.5 (Leopard). To create a documentation set, doxygen will generate a +# Makefile in the HTML output directory. Running make will produce the docset in +# that directory and running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on +# Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the master .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- +# folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- +# filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location of Qt's +# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the +# generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 0 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png The default and svg Looks nicer but requires the +# pdf2svg tool. +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANSPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side JavaScript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from https://www.mathjax.org before deployment. +# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /Namespaces, Classes +//! (includes `struct`s, `union`s, and interfaces), or Files (includes macros). +//! +//! Additional documentation is available at the Crashpad home page. diff --git a/shared/sentry/external/crashpad/doc/support/crashpad_doxygen.css b/shared/sentry/external/crashpad/doc/support/crashpad_doxygen.css new file mode 100644 index 000000000..f21cfe904 --- /dev/null +++ b/shared/sentry/external/crashpad/doc/support/crashpad_doxygen.css @@ -0,0 +1,60 @@ +/* Copyright 2016 The Crashpad Authors. All rights reserved. + * + * 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 "https://fonts.googleapis.com/css?family=Open+Sans:300,400,700&subset=latin,cyrillic-ext,greek-ext,cyrillic,greek,vietnamese,latin-ext"; +@import "https://fonts.googleapis.com/css?family=Source+Code+Pro"; + +body, +table, +div, +p, +dl, +.title, +.icon, +table.directory, +.navpath li.navelem a, +#projectname, +#projectbrief, +#projectnumber, +div.toc li, +div.toc h3, +#powerTip div, +.sm-dox a, +.sm-dox a:focus, +.sm-dox a:hover, +.sm-dox a:active { + font-family: 'Open Sans', + 'Lucida Grande', + 'Lucida Sans Unicode', + Helvetica, + Arial, + sans-serif; +} + +code, +tt, +pre.fragment, +div.line, +.overload, +.params .paramdir, +.sm-dox a span.sub-arrow { + font-family: 'Source Code Pro', + Monaco, + 'Lucida Console', + monospace; +} + +.icon { + height: auto; +} diff --git a/shared/sentry/external/crashpad/doc/support/generate.sh b/shared/sentry/external/crashpad/doc/support/generate.sh new file mode 100755 index 000000000..c3065c1df --- /dev/null +++ b/shared/sentry/external/crashpad/doc/support/generate.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +# Copyright 2015 The Crashpad Authors. All rights reserved. +# +# 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. + +set -e + +function maybe_mkdir() { + local dir="${1}" + if [[ ! -d "${dir}" ]]; then + mkdir "${dir}" + fi +} + +# Run from the Crashpad project root directory. +cd "$(dirname "${0}")/../.." + +source doc/support/compat.sh + +python doc/support/generate_doxygen.py + +output_dir=doc/generated +maybe_mkdir "${output_dir}" + +maybe_mkdir "${output_dir}/doxygen" +rsync -Ilr --delete --exclude .git "out/doc/doxygen/html/" \ + "${output_dir}/doxygen" + +# Ensure a favicon exists at the root since the browser will always request it. +cp doc/favicon.ico "${output_dir}/" diff --git a/shared/sentry/external/crashpad/doc/support/generate_doxygen.py b/shared/sentry/external/crashpad/doc/support/generate_doxygen.py new file mode 100755 index 000000000..7b28aa93d --- /dev/null +++ b/shared/sentry/external/crashpad/doc/support/generate_doxygen.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 + +# Copyright 2017 The Crashpad Authors. All rights reserved. +# +# 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. + +# Generating Doxygen documentation requires Doxygen, http://www.doxygen.org/. + +import os +import shutil +import subprocess +import sys + + +def main(args): + script_dir = os.path.dirname(__file__) + crashpad_dir = os.path.join(script_dir, os.pardir, os.pardir) + + # Run from the Crashpad project root directory. + os.chdir(crashpad_dir) + + output_dir = os.path.join('out', 'doc', 'doxygen') + + if os.path.isdir(output_dir) and not os.path.islink(output_dir): + shutil.rmtree(output_dir) + elif os.path.exists(output_dir): + os.unlink(output_dir) + + os.makedirs(output_dir, 0o755) + + doxy_file = os.path.join('doc', 'support', 'crashpad.doxy') + subprocess.check_call(['doxygen', doxy_file]) + + return 0 + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/shared/sentry/external/crashpad/doc/support/generate_git.sh b/shared/sentry/external/crashpad/doc/support/generate_git.sh new file mode 100755 index 000000000..209c11b95 --- /dev/null +++ b/shared/sentry/external/crashpad/doc/support/generate_git.sh @@ -0,0 +1,85 @@ +#!/bin/bash + +# Copyright 2015 The Crashpad Authors. All rights reserved. +# +# 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. + +set -e + +# Run from the Crashpad project root directory. +cd "$(dirname "${0}")/../.." + +source doc/support/compat.sh + +basename="$(basename "${0}")" + +status="$(git status --porcelain)" +if [[ -n "${status}" ]]; then + echo "${basename}: the working directory must be clean" >& 2 + git status + exit 1 +fi + +# git symbolic-ref gives the current branch name, but fails for detached HEAD. +# In that case, git rev-parse will give the current hash. +original_branch="$(git symbolic-ref --short --quiet HEAD || git rev-parse HEAD)" + +local_branch="\ +$(${sed_ext} -e 's/(.*)\..*/\1/' <<< "${basename}").${$}.${RANDOM}" + +remote_name=origin +remote_master_branch_name=master +remote_master_branch="${remote_name}/${remote_master_branch_name}" +remote_doc_branch_name=doc +remote_doc_branch="${remote_name}/${remote_doc_branch_name}" + +git fetch + +git checkout -b "${local_branch}" "${remote_doc_branch}" + +dirty= + +function cleanup() { + if [[ "${dirty}" ]]; then + git reset --hard HEAD + git clean --force + fi + + git checkout "${original_branch}" + git branch -D "${local_branch}" +} + +trap cleanup EXIT + +master_hash=$(git rev-parse --short=12 "${remote_master_branch}") +git merge "${remote_master_branch}" \ + -m "Merge ${remote_master_branch_name} ${master_hash} into doc" + +dirty=y + +doc/support/generate.sh + +git add -A doc/generated + +count="$(git diff --staged --numstat | wc -l)" +if [[ $count -gt 0 ]]; then + git commit \ + -m "Update documentation to ${remote_master_branch_name} ${master_hash}" + dirty= + + git push "${remote_name}" "HEAD:${remote_doc_branch_name}" +else + dirty= +fi + +# cleanup will run on exit diff --git a/shared/sentry/external/crashpad/example.cpp b/shared/sentry/external/crashpad/example.cpp new file mode 100644 index 000000000..474ae999b --- /dev/null +++ b/shared/sentry/external/crashpad/example.cpp @@ -0,0 +1,81 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "client/crash_report_database.h" +#include "client/crashpad_client.h" +#include "client/crashpad_info.h" +#include "client/settings.h" + +using namespace crashpad; + +int init_crashpad() { + // Cache directory that will store crashpad information and minidumps + base::FilePath database("crashpad.db"); + // Path to the out-of-process handler executable + base::FilePath handler("./out/Default/crashpad_handler"); + // URL used to submit minidumps to + std::string url( + "http://localhost:8000/api/5/minidump/" + "?sentry_key=36811373240a4fc6b25f3040693462d5"); + // Optional annotations passed via --annotations to the handler + std::map annotations; + // Optional arguments to pass to the handler + std::vector arguments; + + arguments.push_back("--no-rate-limit"); + + std::map attachments; + attachments["attch_log_bla.txt"] = base::FilePath("/tmp/log.txt"); + + CrashpadClient client; + bool success = client.StartHandlerWithAttachments(handler, + database, + database, + url, + annotations, + attachments, + arguments, + /* restartable */ true, + /* asynchronous_start */ false); + + if (success) { + printf("Started client handler.\n"); + } else { + printf("Failed to start client handler.\n"); + } + + if (!success) { + return 1; + } + + std::unique_ptr db = + CrashReportDatabase::Initialize(database); + + if (db != nullptr && db->GetSettings() != nullptr) { + db->GetSettings()->SetUploadsEnabled(true); + } + + // Ensure that the simple annotations dictionary is set in the client. + CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo(); + + return 0; +} + +void crash(uint sleep_sec) { + std::cerr << "Prepare to crash, sleeping for " << sleep_sec << " second(s)\n"; + std::this_thread::sleep_for(std::chrono::seconds(sleep_sec)); + memset((char*)0x0, 1, 100); +} + +int main(int args, char* argv[]) { + init_crashpad(); + + const uint sleep_sec = args > 1 ? std::stoi(argv[1]) : 1; + crash(sleep_sec); +} diff --git a/shared/sentry/external/crashpad/handler/BUILD.gn b/shared/sentry/external/crashpad/handler/BUILD.gn new file mode 100644 index 000000000..0fe4760db --- /dev/null +++ b/shared/sentry/external/crashpad/handler/BUILD.gn @@ -0,0 +1,356 @@ +# Copyright 2015 The Crashpad Authors. All rights reserved. +# +# 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("../build/crashpad_buildconfig.gni") + +static_library("handler") { + sources = [ + "handler_main.cc", + "handler_main.h", + "prune_crash_reports_thread.cc", + "prune_crash_reports_thread.h", + "user_stream_data_source.cc", + "user_stream_data_source.h", + ] + + if (crashpad_is_mac) { + sources += [ + "mac/crash_report_exception_handler.cc", + "mac/crash_report_exception_handler.h", + "mac/exception_handler_server.cc", + "mac/exception_handler_server.h", + ] + } + + if (crashpad_is_linux || crashpad_is_android) { + sources += [ + "linux/capture_snapshot.cc", + "linux/capture_snapshot.h", + "linux/crash_report_exception_handler.cc", + "linux/crash_report_exception_handler.h", + "linux/exception_handler_server.cc", + "linux/exception_handler_server.h", + ] + } + + if (crashpad_is_linux) { + sources += [ + "linux/cros_crash_report_exception_handler.cc", + "linux/cros_crash_report_exception_handler.h", + ] + } + + if (crashpad_is_win) { + sources += [ + "win/crash_report_exception_handler.cc", + "win/crash_report_exception_handler.h", + ] + } + + public_configs = [ "..:crashpad_config" ] + + public_deps = [ + ":common", + "../client", + "../third_party/mini_chromium:base", + "../util", + ] + + deps = [ + ":common", + "../minidump", + "../snapshot", + "../third_party/mini_chromium:chromeos_buildflags", + "../tools:tool_support", + ] + + if (crashpad_is_win) { + cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union + } +} + +if (crashpad_is_android) { + # CrashpadHandlerMain is defined in a separate target so that it can be + # overriden by implementers + source_set("crashpad_handler_main") { + sources = [ "crashpad_handler_main.cc" ] + + deps = [ ":handler" ] + } +} + +static_library("common") { + sources = [ + "crash_report_upload_thread.cc", + "crash_report_upload_thread.h", + "minidump_to_upload_parameters.cc", + "minidump_to_upload_parameters.h", + ] + if (crashpad_is_mac || crashpad_is_ios) { + sources += [ + "mac/file_limit_annotation.cc", + "mac/file_limit_annotation.h", + ] + } + public_configs = [ "..:crashpad_config" ] + public_deps = [ + "../third_party/mini_chromium:base", + "../util", + ] + deps = [ + "../client:common", + "../snapshot", + "../util", + "../util:net", + ] + if (crashpad_is_win) { + cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union + } +} + +source_set("handler_test") { + testonly = true + + sources = [ "minidump_to_upload_parameters_test.cc" ] + + if (crashpad_is_linux || crashpad_is_android) { + sources += [ "linux/exception_handler_server_test.cc" ] + } + + if (crashpad_is_win) { + sources += [ "crashpad_handler_test.cc" ] + } + + deps = [ + ":handler", + "../client", + "../compat", + "../snapshot", + "../snapshot:test_support", + "../test", + "../third_party/googletest:googletest", + "../third_party/mini_chromium:base", + "../util", + ] + + if (crashpad_is_win) { + deps += [ "../minidump:test_support" ] + + data_deps = [ + ":crashpad_handler_test_extended_handler", + ":fake_handler_that_crashes_at_startup", + ] + } +} + +if (!crashpad_is_ios) { + crashpad_executable("crashpad_handler") { + sources = [ "main.cc" ] + + deps = [ + ":handler", + "../build:default_exe_manifest_win", + "../compat", + "../third_party/mini_chromium:base", + "../tools:tool_support", + ] + + if (crashpad_is_win) { + if (crashpad_is_in_chromium || crashpad_is_in_dart) { + remove_configs = [ "//build/config/win:console" ] + configs = [ "//build/config/win:windowed" ] + } else if (crashpad_is_external) { + remove_configs = + [ "//../../mini_chromium/mini_chromium/build/config:win_console" ] + configs = + [ "//../../mini_chromium/mini_chromium/build/config:win_windowed" ] + } else { + remove_configs = [ + "//third_party/mini_chromium/mini_chromium/build/config:win_console", + ] + configs = [ + "//third_party/mini_chromium/mini_chromium/build/config:win_windowed", + ] + } + } + + if (crashpad_is_linux) { + deps += [ "../client:pthread_create" ] + } + } +} + +# There is not any normal way to package native executables in an Android APK. +# It is normal to package native code as a loadable module but Android's APK +# installer will ignore files not named like a shared object, so give the +# handler executable an acceptable name. +if (crashpad_is_android) { + copy("crashpad_handler_named_as_so") { + deps = [ ":crashpad_handler" ] + + sources = [ "$root_out_dir/crashpad_handler" ] + + outputs = [ "$root_out_dir/libcrashpad_handler.so" ] + } + + crashpad_executable("crashpad_handler_trampoline") { + output_name = "libcrashpad_handler_trampoline.so" + + sources = [ "linux/handler_trampoline.cc" ] + + deps = [ "../util:no_cfi_icall" ] + + libs = [ "log" ] + + if (crashpad_is_in_chromium) { + # Chromium's sanitizer runtime libraries do not include an unwinder, + # so add Chromium's standard dependencies to link against the in-tree + # libunwind. + import("//build/config/sanitizers/sanitizers.gni") + no_default_deps = !using_sanitizer + remove_configs = + [ "//build/config/android:default_orderfile_instrumentation" ] + } + } +} + +if (!crashpad_is_ios) { + crashpad_executable("crashpad_handler_test_extended_handler") { + testonly = true + + sources = [ "crashpad_handler_test_extended_handler.cc" ] + + deps = [ + ":handler", + "../build:default_exe_manifest_win", + "../compat", + "../minidump:test_support", + "../third_party/mini_chromium:base", + "../tools:tool_support", + ] + } +} + +if (crashpad_is_win) { + crashpad_executable("crashpad_handler_com") { + sources = [ "main.cc" ] + + # Avoid .exp, .ilk, and .lib file collisions with crashpad_handler.exe by + # having this target produce crashpad_handler_com.com. Don’t use this target + # directly. Instead, use crashpad_handler_console. + output_extension = "com" + + deps = [ + ":handler", + "../build:default_exe_manifest_win", + "../compat", + "../third_party/mini_chromium:base", + "../tools:tool_support", + ] + } + + copy("crashpad_handler_console") { + deps = [ ":crashpad_handler_com" ] + sources = [ "$root_out_dir/crashpad_handler_com.com" ] + outputs = [ "$root_out_dir/crashpad_handler.com" ] + } + + crashpad_executable("crash_other_program") { + testonly = true + + sources = [ "win/crash_other_program.cc" ] + + deps = [ + "../client", + "../test", + "../third_party/googletest:googletest", + "../third_party/mini_chromium:base", + ] + } + + crashpad_executable("crashy_program") { + testonly = true + + sources = [ "win/crashy_test_program.cc" ] + + deps = [ + "../client", + "../third_party/mini_chromium:base", + ] + } + + crashpad_executable("crashy_signal") { + testonly = true + + sources = [ "win/crashy_signal.cc" ] + + cflags = [ "/wd4702" ] # Unreachable code. + + deps = [ + "../client", + "../third_party/mini_chromium:base", + ] + } + + crashpad_executable("fake_handler_that_crashes_at_startup") { + testonly = true + + sources = [ "win/fake_handler_that_crashes_at_startup.cc" ] + } + + crashpad_executable("hanging_program") { + testonly = true + + sources = [ "win/hanging_program.cc" ] + + deps = [ + "../client", + "../third_party/mini_chromium:base", + ] + } + + crashpad_loadable_module("loader_lock_dll") { + testonly = true + + sources = [ "win/loader_lock_dll.cc" ] + } + + crashpad_executable("self_destroying_program") { + testonly = true + + sources = [ "win/self_destroying_test_program.cc" ] + + deps = [ + "../client", + "../compat", + "../snapshot", + "../third_party/mini_chromium:base", + ] + } + + if (current_cpu == "x86") { + # Cannot create an x64 DLL with embedded debug info. + crashpad_executable("crashy_z7_loader") { + testonly = true + + sources = [ "win/crashy_test_z7_loader.cc" ] + + deps = [ + "../client", + "../test", + "../third_party/mini_chromium:base", + ] + } + } +} diff --git a/shared/sentry/external/crashpad/handler/CMakeLists.txt b/shared/sentry/external/crashpad/handler/CMakeLists.txt new file mode 100644 index 000000000..86dcdff9a --- /dev/null +++ b/shared/sentry/external/crashpad/handler/CMakeLists.txt @@ -0,0 +1,124 @@ +add_library(crashpad_handler_lib STATIC + crash_report_upload_thread.cc + crash_report_upload_thread.h + handler_main.cc + handler_main.h + minidump_to_upload_parameters.cc + minidump_to_upload_parameters.h + prune_crash_reports_thread.cc + prune_crash_reports_thread.h + user_stream_data_source.cc + user_stream_data_source.h +) + +if(APPLE) + target_sources(crashpad_handler_lib PRIVATE + mac/file_limit_annotation.cc + mac/file_limit_annotation.h + ) +endif() +if(APPLE AND NOT IOS) + target_sources(crashpad_handler_lib PRIVATE + mac/crash_report_exception_handler.cc + mac/crash_report_exception_handler.h + mac/exception_handler_server.cc + mac/exception_handler_server.h + ) +endif() + +if(LINUX OR ANDROID) + target_sources(crashpad_handler_lib PRIVATE + linux/capture_snapshot.cc + linux/capture_snapshot.h + linux/crash_report_exception_handler.cc + linux/crash_report_exception_handler.h + linux/exception_handler_server.cc + linux/exception_handler_server.h + ) +endif() + +if(LINUX) + target_sources(crashpad_handler_lib PRIVATE + linux/cros_crash_report_exception_handler.cc + linux/cros_crash_report_exception_handler.h + ) +endif() + +if(WIN32) + target_sources(crashpad_handler_lib PRIVATE + win/crash_report_exception_handler.cc + win/crash_report_exception_handler.h + ) +endif() + +target_link_libraries(crashpad_handler_lib + PRIVATE + $ + PUBLIC + crashpad_compat + crashpad_minidump + crashpad_snapshot + crashpad_util + mini_chromium +) + +if(WIN32) + if(MSVC) + target_link_libraries(crashpad_handler_lib PUBLIC crashpad_getopt) + target_compile_options(crashpad_handler_lib PRIVATE "/wd4201") + endif() +endif() + +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(crashpad_handler_lib PRIVATE + "-Wno-multichar" + ) +endif() + +set_property(TARGET crashpad_handler_lib PROPERTY EXPORT_NAME handler) +add_library(crashpad::handler_lib ALIAS crashpad_handler_lib) + +crashpad_install_target(crashpad_handler_lib) +crashpad_install_dev(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/" + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/crashpad/handler" + FILES_MATCHING PATTERN "*.h" +) + +if(NOT IOS) + add_executable(crashpad_handler WIN32 + main.cc + ) + + if(LINUX) + target_sources(crashpad_handler PRIVATE + ../client/pthread_create_linux.cc + ) + endif() + + target_link_libraries(crashpad_handler + PRIVATE + $ + PUBLIC + crashpad_client + crashpad_getopt + crashpad_handler_lib + crashpad_minidump + crashpad_snapshot + crashpad_tools + crashpad_util + mini_chromium + ) + + if(WIN32) + if(MSVC) + target_link_options(crashpad_handler PRIVATE "/SUBSYSTEM:WINDOWS") + endif() + endif() + + set_property(TARGET crashpad_handler PROPERTY EXPORT_NAME crashpad_handler) + add_executable(crashpad::handler ALIAS crashpad_handler) + + install(TARGETS crashpad_handler EXPORT crashpad_export + RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" + ) +endif() diff --git a/shared/sentry/external/crashpad/handler/crash_report_upload_thread.cc b/shared/sentry/external/crashpad/handler/crash_report_upload_thread.cc new file mode 100644 index 000000000..efbeab780 --- /dev/null +++ b/shared/sentry/external/crashpad/handler/crash_report_upload_thread.cc @@ -0,0 +1,403 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "handler/crash_report_upload_thread.h" + +#include +#include + +#include +#include +#include +#include + +#include "base/logging.h" +#include "base/notreached.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" +#include "client/settings.h" +#include "handler/minidump_to_upload_parameters.h" +#include "snapshot/minidump/process_snapshot_minidump.h" +#include "snapshot/module_snapshot.h" +#include "util/file/file_reader.h" +#include "util/misc/metrics.h" +#include "util/misc/uuid.h" +#include "util/net/http_body.h" +#include "util/net/http_multipart_builder.h" +#include "util/net/http_transport.h" +#include "util/net/url.h" +#include "util/stdlib/map_insert.h" + +#if BUILDFLAG(IS_APPLE) +#include "handler/mac/file_limit_annotation.h" +#endif // BUILDFLAG(IS_APPLE) + +#if BUILDFLAG(IS_IOS) +#include "util/ios/scoped_background_task.h" +#endif // BUILDFLAG(IS_IOS) + +namespace crashpad { + +namespace { + +// The number of seconds to wait between checking for pending reports. +const int kRetryWorkIntervalSeconds = 15 * 60; + +#if BUILDFLAG(IS_IOS) +// The number of times to attempt to upload a pending report, repeated on +// failure. Attempts will happen once per launch, once per call to +// ReportPending(), and, if Options.watch_pending_reports is true, once every +// kRetryWorkIntervalSeconds. Currently iOS only. +const int kRetryAttempts = 5; +#endif + +} // namespace + +CrashReportUploadThread::CrashReportUploadThread(CrashReportDatabase* database, + const std::string& url, + const Options& options) + : options_(options), + url_(url), + // When watching for pending reports, check every 15 minutes, even in the + // absence of a signal from the handler thread. This allows for failed + // uploads to be retried periodically, and for pending reports written by + // other processes to be recognized. + thread_(options.watch_pending_reports ? kRetryWorkIntervalSeconds + : WorkerThread::kIndefiniteWait, + this), + known_pending_report_uuids_(), + database_(database) { + DCHECK(!url_.empty()); +} + +CrashReportUploadThread::~CrashReportUploadThread() { +} + +void CrashReportUploadThread::ReportPending(const UUID& report_uuid) { + known_pending_report_uuids_.PushBack(report_uuid); + if (thread_.is_running()) + thread_.DoWorkNow(); +} + +void CrashReportUploadThread::Start() { + thread_.Start( + options_.watch_pending_reports ? 0.0 : WorkerThread::kIndefiniteWait); +} + +void CrashReportUploadThread::Stop() { + thread_.Stop(); +} + +void CrashReportUploadThread::ProcessPendingReports() { +#if BUILDFLAG(IS_IOS) + internal::ScopedBackgroundTask scoper("CrashReportUploadThread"); +#endif // BUILDFLAG(IS_IOS) + + std::vector known_report_uuids = known_pending_report_uuids_.Drain(); + for (const UUID& report_uuid : known_report_uuids) { + CrashReportDatabase::Report report; + if (database_->LookUpCrashReport(report_uuid, &report) != + CrashReportDatabase::kNoError) { + continue; + } + + ProcessPendingReport(report); + + // Respect Stop() being called after at least one attempt to process a + // report. + if (!thread_.is_running()) { + return; + } + } + + // Known pending reports are always processed (above). The rest of this + // function is concerned with scanning for pending reports not already known + // to this thread. + if (!options_.watch_pending_reports) { + return; + } + + std::vector reports; + if (database_->GetPendingReports(&reports) != CrashReportDatabase::kNoError) { + // The database is sick. It might be prudent to stop trying to poke it from + // this thread by abandoning the thread altogether. On the other hand, if + // the problem is transient, it might be possible to talk to it again on the + // next pass. For now, take the latter approach. + return; + } + + for (const CrashReportDatabase::Report& report : reports) { + if (std::find(known_report_uuids.begin(), + known_report_uuids.end(), + report.uuid) != known_report_uuids.end()) { + // An attempt to process the report already occurred above. The report is + // still pending, so upload must have failed. Don’t retry it immediately, + // it can wait until at least the next pass through this method. + continue; + } + + ProcessPendingReport(report); + + // Respect Stop() being called after at least one attempt to process a + // report. + if (!thread_.is_running()) { + return; + } + } +} + +void CrashReportUploadThread::ProcessPendingReport( + const CrashReportDatabase::Report& report) { +#if BUILDFLAG(IS_APPLE) + RecordFileLimitAnnotation(); +#endif // BUILDFLAG(IS_APPLE) + + Settings* const settings = database_->GetSettings(); + + bool uploads_enabled; + if (!report.upload_explicitly_requested && + (!settings->GetUploadsEnabled(&uploads_enabled) || !uploads_enabled)) { + // Don’t attempt an upload if there’s no URL to upload to. Allow upload if + // it has been explicitly requested by the user, otherwise, respect the + // upload-enabled state stored in the database’s settings. + database_->SkipReportUpload(report.uuid, + Metrics::CrashSkippedReason::kUploadsDisabled); + return; + } + + if (ShouldRateLimitUpload(report)) + return; + +#if BUILDFLAG(IS_IOS) + if (ShouldRateLimitRetry(report)) + return; +#endif // BUILDFLAG(IS_IOS) + + std::unique_ptr upload_report; + CrashReportDatabase::OperationStatus status = + database_->GetReportForUploading(report.uuid, &upload_report); + switch (status) { + case CrashReportDatabase::kNoError: + break; + + case CrashReportDatabase::kBusyError: + case CrashReportDatabase::kReportNotFound: + // Someone else may have gotten to it first. If they’re working on it now, + // this will be kBusyError. If they’ve already finished with it, it’ll be + // kReportNotFound. + return; + + case CrashReportDatabase::kFileSystemError: + case CrashReportDatabase::kDatabaseError: + // In these cases, SkipReportUpload() might not work either, but it’s best + // to at least try to get the report out of the way. + database_->SkipReportUpload(report.uuid, + Metrics::CrashSkippedReason::kDatabaseError); + return; + + case CrashReportDatabase::kCannotRequestUpload: + NOTREACHED(); + return; + } + + std::string response_body; + UploadResult upload_result = + UploadReport(upload_report.get(), &response_body); + switch (upload_result) { + case UploadResult::kSuccess: + database_->RecordUploadComplete(std::move(upload_report), response_body); + break; + case UploadResult::kPermanentFailure: + upload_report.reset(); + database_->SkipReportUpload( + report.uuid, Metrics::CrashSkippedReason::kPrepareForUploadFailed); + break; + case UploadResult::kRetry: +#if BUILDFLAG(IS_IOS) + if (upload_report->upload_attempts > kRetryAttempts) { + upload_report.reset(); + database_->SkipReportUpload(report.uuid, + Metrics::CrashSkippedReason::kUploadFailed); + } else { + Metrics::CrashUploadSkipped( + Metrics::CrashSkippedReason::kUploadFailedButCanRetry); + retry_uuid_time_map_[report.uuid] = + time(nullptr) + + (1 << upload_report->upload_attempts) * kRetryWorkIntervalSeconds; + } +#else + upload_report.reset(); + + // TODO(mark): Deal with retries properly: don’t call SkipReportUplaod() + // if the result was kRetry and the report hasn’t already been retried + // too many times. + database_->SkipReportUpload(report.uuid, + Metrics::CrashSkippedReason::kUploadFailed); +#endif + break; + } +} + +CrashReportUploadThread::UploadResult CrashReportUploadThread::UploadReport( + const CrashReportDatabase::UploadReport* report, + std::string* response_body) { + std::map parameters; + + FileReader* reader = report->Reader(); + FileOffset start_offset = reader->SeekGet(); + if (start_offset < 0) { + return UploadResult::kPermanentFailure; + } + + // Ignore any errors that might occur when attempting to interpret the + // minidump file. This may result in its being uploaded with few or no + // parameters, but as long as there’s a dump file, the server can decide what + // to do with it. + ProcessSnapshotMinidump minidump_process_snapshot; + if (minidump_process_snapshot.Initialize(reader)) { + parameters = + BreakpadHTTPFormParametersFromMinidump(&minidump_process_snapshot); + } + + if (!reader->SeekSet(start_offset)) { + return UploadResult::kPermanentFailure; + } + + HTTPMultipartBuilder http_multipart_builder; + http_multipart_builder.SetGzipEnabled(options_.upload_gzip); + + static constexpr char kMinidumpKey[] = "upload_file_minidump"; + + for (const auto& kv : parameters) { + if (kv.first == kMinidumpKey) { + LOG(WARNING) << "reserved key " << kv.first << ", discarding value " + << kv.second; + } else { + http_multipart_builder.SetFormData(kv.first, kv.second); + } + } + + for (const auto& it : report->GetAttachments()) { + http_multipart_builder.SetFileAttachment( + it.first, it.first, it.second, "application/octet-stream"); + } + + http_multipart_builder.SetFileAttachment(kMinidumpKey, + report->uuid.ToString() + ".dmp", + reader, + "application/octet-stream"); + + std::unique_ptr http_transport(HTTPTransport::Create()); + if (!http_transport) { + return UploadResult::kPermanentFailure; + } + + HTTPHeaders content_headers; + http_multipart_builder.PopulateContentHeaders(&content_headers); + for (const auto& content_header : content_headers) { + http_transport->SetHeader(content_header.first, content_header.second); + } + http_transport->SetBodyStream(http_multipart_builder.GetBodyStream()); + // TODO(mark): The timeout should be configurable by the client. + http_transport->SetTimeout(60.0); // 1 minute. + + std::string url = url_; + if (options_.identify_client_via_url) { + // Add parameters to the URL which identify the client to the server. + static constexpr struct { + const char* key; + const char* url_field_name; + } kURLParameterMappings[] = { + {"prod", "product"}, + {"ver", "version"}, + {"guid", "guid"}, + }; + + for (const auto& parameter_mapping : kURLParameterMappings) { + const auto it = parameters.find(parameter_mapping.key); + if (it != parameters.end()) { + url.append( + base::StringPrintf("%c%s=%s", + url.find('?') == std::string::npos ? '?' : '&', + parameter_mapping.url_field_name, + URLEncode(it->second).c_str())); + } + } + } + http_transport->SetURL(url); + + if (!http_transport->ExecuteSynchronously(response_body)) { + return UploadResult::kRetry; + } + + return UploadResult::kSuccess; +} + +void CrashReportUploadThread::DoWork(const WorkerThread* thread) { + ProcessPendingReports(); +} + +bool CrashReportUploadThread::ShouldRateLimitUpload( + const CrashReportDatabase::Report& report) { + if (report.upload_explicitly_requested || !options_.rate_limit) + return false; + + Settings* const settings = database_->GetSettings(); + time_t last_upload_attempt_time; + if (settings->GetLastUploadAttemptTime(&last_upload_attempt_time)) { + time_t now = time(nullptr); + if (now >= last_upload_attempt_time) { + // If the most recent upload attempt occurred within the past hour, + // don’t attempt to upload the new report. If it happened longer ago, + // attempt to upload the report. + constexpr int kUploadAttemptIntervalSeconds = 60 * 60; // 1 hour + if (now - last_upload_attempt_time < kUploadAttemptIntervalSeconds) { + database_->SkipReportUpload( + report.uuid, Metrics::CrashSkippedReason::kUploadThrottled); + return true; + } + } else { + // The most recent upload attempt purportedly occurred in the future. If + // it “happened†at least one day in the future, assume that the last + // upload attempt time is bogus, and attempt to upload the report. If + // the most recent upload time is in the future but within one day, + // accept it and don’t attempt to upload the report. + constexpr int kBackwardsClockTolerance = 60 * 60 * 24; // 1 day + if (last_upload_attempt_time - now < kBackwardsClockTolerance) { + database_->SkipReportUpload( + report.uuid, Metrics::CrashSkippedReason::kUnexpectedTime); + return true; + } + } + } + return false; +} + +#if BUILDFLAG(IS_IOS) +bool CrashReportUploadThread::ShouldRateLimitRetry( + const CrashReportDatabase::Report& report) { + if (retry_uuid_time_map_.find(report.uuid) != retry_uuid_time_map_.end()) { + time_t now = time(nullptr); + if (now < retry_uuid_time_map_[report.uuid]) { + return true; + } else { + retry_uuid_time_map_.erase(report.uuid); + } + } + return false; +} +#endif + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/handler/crash_report_upload_thread.h b/shared/sentry/external/crashpad/handler/crash_report_upload_thread.h new file mode 100644 index 000000000..2af958d7d --- /dev/null +++ b/shared/sentry/external/crashpad/handler/crash_report_upload_thread.h @@ -0,0 +1,222 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_HANDLER_CRASH_REPORT_UPLOAD_THREAD_H_ +#define CRASHPAD_HANDLER_CRASH_REPORT_UPLOAD_THREAD_H_ + +#include +#include +#include + +#include "build/build_config.h" +#include "client/crash_report_database.h" +#include "util/misc/uuid.h" +#include "util/stdlib/thread_safe_vector.h" +#include "util/thread/stoppable.h" +#include "util/thread/worker_thread.h" + +namespace crashpad { + +//! \brief A thread that processes pending crash reports in a +//! CrashReportDatabase by uploading them or marking them as completed +//! without upload, as desired. +//! +//! A producer of crash reports should notify an object of this class that a new +//! report has been added to the database by calling ReportPending(). +//! +//! Independently of being triggered by ReportPending(), objects of this class +//! can periodically examine the database for pending reports. This allows +//! failed upload attempts for reports left in the pending state to be retried. +//! It also catches reports that are added without a ReportPending() signal +//! being caught. This may happen if crash reports are added to the database by +//! other processes. +class CrashReportUploadThread : public WorkerThread::Delegate, + public Stoppable { + public: + //! \brief Options to be passed to the CrashReportUploadThread constructor. + struct Options { + //! Whether client identifying parameters like product name or version + //! should be added to the URL. + bool identify_client_via_url; + + //! Whether uploads should be throttled to a (currently hardcoded) rate. + bool rate_limit; + + //! Whether uploads should use `gzip` compression. + bool upload_gzip; + + //! Whether to periodically check for new pending reports not already known + //! to exist. When `false`, only an initial upload attempt will be made for + //! reports known to exist by having been added by the ReportPending() + //! method. No scans for new pending reports will be conducted. + bool watch_pending_reports; + }; + + //! \brief Constructs a new object. + //! + //! \param[in] database The database to upload crash reports from. + //! \param[in] url The URL of the server to upload crash reports to. + //! \param[in] options Options for the report uploads. + CrashReportUploadThread(CrashReportDatabase* database, + const std::string& url, + const Options& options); + + CrashReportUploadThread(const CrashReportUploadThread&) = delete; + CrashReportUploadThread& operator=(const CrashReportUploadThread&) = delete; + + ~CrashReportUploadThread(); + + //! \brief Informs the upload thread that a new pending report has been added + //! to the database. + //! + //! \param[in] report_uuid The unique identifier of the newly added pending + //! report. + //! + //! This method may be called from any thread. + void ReportPending(const UUID& report_uuid); + + // Stoppable: + + //! \brief Starts a dedicated upload thread, which executes ThreadMain(). + //! + //! This method may only be be called on a newly-constructed object or after + //! a call to Stop(). + void Start() override; + + //! \brief Stops the upload thread. + //! + //! The upload thread will terminate after completing whatever task it is + //! performing. If it is not performing any task, it will terminate + //! immediately. This method blocks while waiting for the upload thread to + //! terminate. + //! + //! This method must only be called after Start(). If Start() has been called, + //! this method must be called before destroying an object of this class. + //! + //! This method may be called from any thread other than the upload thread. + //! It is expected to only be called from the same thread that called Start(). + void Stop() override; + + //! \return `true` if the thread is running, `false` if it is not. + bool is_running() const { return thread_.is_running(); } + + private: + //! \brief The result code from UploadReport(). + enum class UploadResult { + //! \brief The crash report was uploaded successfully. + kSuccess, + + //! \brief The crash report upload failed in such a way that recovery is + //! impossible. + //! + //! No further upload attempts should be made for the report. + kPermanentFailure, + + //! \brief The crash report upload failed, but it might succeed again if + //! retried in the future. + //! + //! If the report has not already been retried too many times, the caller + //! may arrange to call UploadReport() for the report again in the future, + //! after a suitable delay. + kRetry, + }; + + //! \brief Calls ProcessPendingReport() on pending reports. + //! + //! Assuming Stop() has not been called, this will process reports that the + //! object has been made aware of in ReportPending(). Additionally, if the + //! object was constructed with \a watch_pending_reports, it will also scan + //! the crash report database for other pending reports, and process those as + //! well. + void ProcessPendingReports(); + + //! \brief Processes a single pending report from the database. + //! + //! \param[in] report The crash report to process. + //! + //! If report upload is enabled, this method attempts to upload \a report by + //! calling UplaodReport(). If the upload is successful, the report will be + //! marked as “completed†in the database. If the upload fails and more + //! retries are desired, the report’s upload-attempt count and + //! last-upload-attempt time will be updated in the database and it will + //! remain in the “pending†state. If the upload fails and no more retries are + //! desired, or report upload is disabled, it will be marked as “completed†in + //! the database without ever having been uploaded. + void ProcessPendingReport(const CrashReportDatabase::Report& report); + + //! \brief Attempts to upload a crash report. + //! + //! \param[in] report The report to upload. The caller is responsible for + //! calling CrashReportDatabase::GetReportForUploading() before calling + //! this method, and for calling + //! CrashReportDatabase::RecordUploadComplete() after calling this method. + //! \param[out] response_body If the upload attempt is successful, this will + //! be set to the response body sent by the server. Breakpad-type servers + //! provide the crash ID assigned by the server in the response body. + //! + //! \return A member of UploadResult indicating the result of the upload + //! attempt. + UploadResult UploadReport(const CrashReportDatabase::UploadReport* report, + std::string* response_body); + + // WorkerThread::Delegate: + //! \brief Calls ProcessPendingReports() in response to ReportPending() having + //! been called on any thread, as well as periodically on a timer. + void DoWork(const WorkerThread* thread) override; + + //! \brief Rate-limit uploads. + //! + //! \param[in] report The crash report to process. + //! + //! This currently implements very simplistic rate-limiting, compatible with + //! the Breakpad client, where the strategy is to permit one upload attempt + //! per hour, and retire reports that would exceed this limit or for which the + //! upload fails on the first attempt. + //! If upload was requested explicitly (i.e. by user action), do not throttle + //! the upload. + //! + //! TODO(mark): Provide a proper rate-limiting strategy and allow for failed + //! upload attempts to be retried. + bool ShouldRateLimitUpload(const CrashReportDatabase::Report& report); + +#if BUILDFLAG(IS_IOS) + //! \brief Rate-limit report retries. + //! + //! \param[in] report The crash report to process. + //! + //! This implements a per-report retry rate limit (as opposed to per upload + //! rate limit in ShouldRateLimitUpload). When a report upload ends in a retry + //! state, an in-memory only timestamp is stored in |retry_uuid_time_map_| + //! with the next possible retry time. This timestamp is a backoff from the + //! main thread work interval, doubling on each attemt. Because this is only + //! stored in memory, on restart reports in the retry state will always be + //! tried once, and then fall back into the next backoff. This continues until + //! kRetryAttempts is reached. + bool ShouldRateLimitRetry(const CrashReportDatabase::Report& report); +#endif + + const Options options_; + const std::string url_; + WorkerThread thread_; + ThreadSafeVector known_pending_report_uuids_; +#if BUILDFLAG(IS_IOS) + // This is not thread-safe, and only used by the worker thread. + std::map retry_uuid_time_map_; +#endif + CrashReportDatabase* database_; // weak +}; + +} // namespace crashpad + +#endif // CRASHPAD_HANDLER_CRASH_REPORT_UPLOAD_THREAD_H_ diff --git a/shared/sentry/external/crashpad/handler/crashpad_handler.md b/shared/sentry/external/crashpad/handler/crashpad_handler.md new file mode 100644 index 000000000..6d055ed17 --- /dev/null +++ b/shared/sentry/external/crashpad/handler/crashpad_handler.md @@ -0,0 +1,349 @@ + + +# crashpad_handler(8) + +## Name + +crashpad_handler—Crashpad’s exception handler server + +## Synopsis + +**crashpad_handler** [_OPTION…_] + +## Description + +This program is Crashpad’s main exception-handling server. It is responsible for +catching exceptions, writing crash reports, and uploading them to a crash report +collection server. Uploads are disabled by default, and can only be enabled by a +Crashpad client using the Crashpad client library, typically in response to a +user requesting this behavior. + +On macOS, this server may be started by its initial client, in which case it +performs a handshake with this client via a pipe established by the client that +is inherited by the server, referenced by the **--handshake-fd** argument. +During the handshake, the server furnishes the client with a send right that the +client may use as an exception port. The server retains the corresponding +receive right, which it monitors for exception messages. When the receive right +loses all senders, the server exits after allowing any upload in progress to +complete. + +Alternatively, on macOS, this server may be started from launchd(8), where it +receives the Mach service name in a **--mach-service** argument. It checks in +with the bootstrap server under this service name, and clients may look it up +with the bootstrap server under this service name. It monitors this service for +exception messages. Upon receipt of `SIGTERM`, the server exits after allowing +any upload in progress to complete. `SIGTERM` is normally sent by launchd(8) +when it determines that the server should exit. + +On Windows, clients register with this server by communicating with it via the +named pipe identified by the **--pipe-name** argument. Alternatively, the server +can inherit an already-created pipe from a parent process by using the +**--initial-client-data** mechanism. That argument also takes all of the +arguments that would normally be passed in a registration message, and so +constitutes registration of the first client. Subsequent clients may then +register by communicating with the server via the pipe. During registration, a +client provides the server with an OS event object that it will signal should it +crash. The server obtains the client’s process handle and waits on the crash +event object for a crash, as well as the client’s process handle for the client +to exit cleanly without crashing. When a server started via the +**--initial-client-data** mechanism loses all of its clients, it exits after +allowing any upload in progress to complete. + +On Windows, this executable is built by default as a Windows GUI app, so no +console will appear in normal usage. This is the version that will typically be +used. A second copy is also made with a `.com` extension, rather than `.exe`. In +this second copy, the PE header is modified to indicate that it’s a console app. +This is useful because the `.com` is found in the path before the `.exe`, so +when run normally from a shell using only the basename (without an explicit +`.com` or `.exe` extension), the `.com` console version will be chosen, and so +stdio will be hooked up as expected to the parent console so that logging output +will be visible. + +On Linux/Android, the handler may create a crash dump for its parent process +using **--trace-parent-with-exception**. In this mode, the handler process +creates a crash dump for its parent and exits. Alternatively, the handler may +be launched with **--initial-client-fd** which will start the server connected +to an initial client. The server will exit when all connected client sockets are +closed. + +It is not normally appropriate to invoke this program directly. Usually, it will +be invoked by a Crashpad client using the Crashpad client library, or started by +another system service. On macOS, arbitrary programs may be run with a Crashpad +handler by using [run_with_crashpad(1)](../tools/mac/run_with_crashpad.md) to +establish the Crashpad client environment before running a program. + +## Options + + * **--annotation**=_KEY_=_VALUE_ + + Sets a process-level annotation mapping _KEY_ to _VALUE_ in each crash report + that is written. This option may appear zero, one, or multiple times. + + Most annotations should be provided by the Crashpad client as module-level + annotations instead of process-level annotations. Module-level annotations + are more flexible in that they can be modified and cleared during the client + program’s lifetime. Module-level annotations can be set via the Crashpad + client library. Process-level annotations are useful for annotations that the + collection server requires be present, that have fixed values, and for cases + where a program that does not use the Crashpad client library is being + monitored. + + Breakpad-type collection servers only require the `"prod"` and `"ver"` + annotations, which should be set to the product name or identifier and + product version, respectively. It is unusual to specify other annotations as + process-level annotations via this argument. + + * **--database**=_PATH_ + + Use _PATH_ as the path to the Crashpad crash report database. This option is + required. Crash reports are written to this database, and if uploads are + enabled, uploaded from this database to a crash report collection server. If + the database does not exist, it will be created, provided that the parent + directory of _PATH_ exists. + + * **--handshake-fd**=_FD_ + + Perform the handshake with the initial client on the file descriptor at _FD_. + Either this option or **--mach-service**, but not both, is required. This + option is only valid on macOS. + + * **--initial-client-data**=*HANDLE_request_crash_dump*,*HANDLE_request_non_crash_dump*,*HANDLE_non_crash_dump_completed*,*HANDLE_first_pipe_instance*,*HANDLE_client_process*,*Address_crash_exception_information*,*Address_non_crash_exception_information*,*Address_debug_critical_section* + + Register the initial client using the inherited handles and data provided. + For more information on the argument’s format, see the implementations of + `CrashpadClient` and `ExceptionHandlerServer`. Either this option or + **--pipe-name**, but not both, is required. This option is only valid on + Windows. + + When this option is present, the server creates a new named pipe at a random + name and informs its client of the name. The server waits for at least one + client to register, and exits when all clients have exited, after waiting for + any uploads in progress to complete. + + * **--initial-client-fd**=_FD_ + + Wait for client requests on _FD_. Either this option or + **--trace-parent-with-exception**, but not both, is required. The handler + exits when all client connections have been closed. This option is only valid + on Linux platforms. + + * **--mach-service**=_SERVICE_ + + Check in with the bootstrap server under the name _SERVICE_. Either this + option or **--handshake-fd**, but not both, is required. This option is only + valid on macOS. + + _SERVICE_ may already be reserved with the bootstrap server in cases where + this tool is started by launchd(8) as a result of a message being sent to a + service declared in a job’s `MachServices` dictionary (see launchd.plist(5)). + The service name may also be completely unknown to the system. + + * **--metrics-dir**=_DIR_ + + Metrics information will be written to _DIR_. This option only has an effect + when built as part of Chromium. In non-Chromium builds, and in the absence of + this option, metrics information will not be written. + + * **--monitor-self** + + Causes a second instance of the Crashpad handler program to be started, + monitoring the original instance for exceptions. The original instance will + become a client of the second one. The second instance will be started with + the same **--annotation**, **--database**, **--monitor-self-annotation**, + **--no-rate-limit**, **--no-upload-gzip**, and **--url** arguments as the + original one. The second instance will always be started with a + **--no-periodic-tasks** argument, and will not be started with a + **--metrics-dir** argument even if the original instance was. + + Where supported by the underlying operating system, the second instance will + be restarted should it exit before the first instance. The second instance + will not be eligible to be started asynchronously. + + * **--monitor-self-annotation**=_KEY_=_VALUE_ + + Sets a module-level annotation mapping _KEY_ to _VALUE_ in the Crashpad + handler. This option may appear zero, one, or more times. + + If **--monitor-self** is in use, the second instance of the Crashpad handler + program will find these annotations stored in the original instance and will + include them in any crash reports written for the original instance. + + These annotations will only appear in crash reports written for the Crashpad + handler itself. To apply a process-level annotation to all crash reports + written by an instance of the Crashpad handler, use **--annotation** instead. + + * **--monitor-self-argument**=_ARGUMENT_ + + When directed by **--monitor-self** to start a second instance of the + Crashpad handler program, the second instance will be started with _ARGUMENT_ + as one of its arguments. This option may appear zero, one, or more times. + This option has no effect in the absence of **--monitor-self**. + + This supports embedding the Crashpad handler into a multi-purpose executable + that dispatches to the desired entry point based on a command-line argument. + To prevent excessive accumulation of handler processes, _ARGUMENT_ must not + be `--monitor-self`. + +* **--no-identify-client-via-url** + + Do not add client-identifying fields to the URL. By default, `"prod"`, + `"ver"`, and `"guid"` annotations are added to the upload URL as name-value + pairs `"product"`, `"version"`, and `"guid"`, respectively. Using this + option disables that behavior. + + * **--no-periodic-tasks** + + Do not scan for new pending crash reports or prune the crash report database. + Only crash reports recorded by this instance of the Crashpad handler will + become eligible for upload in this instance, and only a single initial upload + attempt will be made. + + This option is not intended for general use. It is provided to prevent + multiple instances of the Crashpad handler from duplicating the effort of + performing the same periodic tasks. In normal use, the first instance of the + Crashpad handler will assume the responsibility for performing these tasks, + and will provide this argument to any second instance. See + **--monitor-self**. + + * **--no-rate-limit** + + Do not rate limit the upload of crash reports. By default uploads are + throttled to one per hour. Using this option disables that behavior, and + Crashpad will attempt to upload all captured reports. + + * **--no-upload-gzip** + + Do not use `gzip` compression for uploaded crash reports. Normally, the + entire request body is compressed into a `gzip` stream and transmitted with + `Content-Encoding: gzip`. This option disables compression, and is intended + for use with collection servers that don’t accept uploads compressed in this + way. + + * **--no-write-minidump-to-database** + + Do not write the minidump to database. Normally, the minidump is written to + database for upload. Use this option with **--write-minidump-to-log** to + only write the minidump to log. This option is only available to Android. + + * **--pipe-name**=_PIPE_ + + Listen on the given pipe name for connections from clients. _PIPE_ must be of + the form `\\.\pipe\`. Either this option or + **--initial-client-data**, but not both, is required. This option is only + valid on Windows. + + When this option is present, the server creates a named pipe at _PIPE_, a + name known to both the server and its clients. The server continues running + even after all clients have exited. + + * **--reset-own-crash-exception-port-to-system-default** + + Causes the exception handler server to set its own crash handler to the + system default before beginning operation. This is only expected to be useful + in cases where the server inherits an inappropriate crash handler from its + parent process. This option is only valid on macOS. Use of this option is + discouraged. It should not be used absent extraordinary circumstances. + + * **--sanitization-information**=_SANITIZATION-INFORMATION-ADDRESS_ + + Provides sanitization settings in a SanitizationInformation struct at + _SANITIZATION-INFORMATION-ADDRESS_. This option requires + **--trace-parent-with-exception** and is only valid on Linux platforms. + + * **--shared-client-connection** + + Indicates that the file descriptor provided by **--initial-client-fd** is + shared among mulitple clients. Using a broker process is not supported for + clients using this option. This option is only valid on Linux platforms. + + * **--trace-parent-with-exception**=_EXCEPTION-INFORMATION-ADDRESS_ + + Causes the handler process to trace its parent process and exit. The parent + process should have an ExceptionInformation struct at + _EXCEPTION-INFORMATION-ADDRESS_. This option is only valid on Linux + platforms. + + * **--url**=_URL_ + + If uploads are enabled, sends crash reports to the Breakpad-type crash report + collection server at _URL_. Uploads are disabled by default, and can only be + enabled for a database by a Crashpad client using the Crashpad client + library, typically in response to a user requesting this behavior. If this + option is not specified, this program will behave as if uploads are disabled. + + * **--use-cros-crash-reporter** + + Causes crash reports to be passed via an in-memory file to + `/sbin/crash_reporter` instead of storing them in the database. The database + is still used for Crashpad settings. This option is only valid on Chromium + OS. + +* **--write-minidump-to-log** + + Write the minidump to log. By default the minidump is only written to + database. Use this option with **--no-write-minidump-to-database** to only + write the minidump to log. This option is only available to Android. + + * **--help** + + Display help and exit. + + * **--version** + + Output version information and exit. + +## Exit Status + + * **0** + + Success. + + * **1** + + Failure, with a message printed to the standard error stream. + +## See Also + +[catch_exception_tool(1)](../tools/mac/catch_exception_tool.md), +[crashpad_database_util(1)](../tools/crashpad_database_util.md), +[generate_dump(1)](../tools/generate_dump.md), +[run_with_crashpad(1)](../tools/mac/run_with_crashpad.md) + +## Resources + +Crashpad home page: https://crashpad.chromium.org/. + +Report bugs at https://crashpad.chromium.org/bug/new. + +## Copyright + +Copyright 2014 [The Crashpad +Authors](https://chromium.googlesource.com/crashpad/crashpad/+/master/AUTHORS). + +## License + +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. diff --git a/shared/sentry/external/crashpad/handler/crashpad_handler_main.cc b/shared/sentry/external/crashpad/handler/crashpad_handler_main.cc new file mode 100644 index 000000000..b47488ef7 --- /dev/null +++ b/shared/sentry/external/crashpad/handler/crashpad_handler_main.cc @@ -0,0 +1,29 @@ +// Copyright 2019 The Crashpad Authors. All rights reserved. +// +// 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 "handler/handler_main.h" + +namespace crashpad { + +extern "C" { + +__attribute__((visibility("default"), used)) int CrashpadHandlerMain( + int argc, + char* argv[]) { + return HandlerMain(argc, argv, nullptr); +} + +} // extern "C" + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/handler/crashpad_handler_test.cc b/shared/sentry/external/crashpad/handler/crashpad_handler_test.cc new file mode 100644 index 000000000..70e5da927 --- /dev/null +++ b/shared/sentry/external/crashpad/handler/crashpad_handler_test.cc @@ -0,0 +1,148 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 +#include +#include + +#include +#include +#include + +#include "base/files/file_path.h" +#include "client/crash_report_database.h" +#include "client/crashpad_client.h" +#include "gtest/gtest.h" +#include "minidump/test/minidump_file_writer_test_util.h" +#include "test/test_paths.h" +#include "test/win/win_multiprocess_with_temp_dir.h" +#include "util/file/file_reader.h" +#include "util/misc/capture_context.h" + +namespace crashpad { +namespace test { +namespace { + +constexpr DWORD kExpectedExitCode = 0x1CEB00DA; + +void StartAndCrashWithExtendedHandler(const base::FilePath& temp_dir) { + base::FilePath handler_path = TestPaths::BuildArtifact( + L"handler", L"extended_handler", TestPaths::FileType::kExecutable); + + CrashpadClient client; + ASSERT_TRUE(client.StartHandler(handler_path, + temp_dir, + base::FilePath(), + "", + std::map(), + std::vector(), + false, + false)); + + // It appears that the Google Test fixture will catch and handle exceptions + // from here. Hence the fabricated crash in favor of raising an exception. + EXCEPTION_RECORD exception_record = {kExpectedExitCode, + EXCEPTION_NONCONTINUABLE}; + CONTEXT context; + CaptureContext(&context); + EXCEPTION_POINTERS exception_pointers = {&exception_record, &context}; + CrashpadClient::DumpAndCrash(&exception_pointers); +} + +class CrashWithExtendedHandler final : public WinMultiprocessWithTempDir { + public: + CrashWithExtendedHandler() : WinMultiprocessWithTempDir() {} + ~CrashWithExtendedHandler() {} + + private: + void ValidateGeneratedDump(); + + void WinMultiprocessParent() override { + SetExpectedChildExitCode(kExpectedExitCode); + } + + void WinMultiprocessChild() override { + StartAndCrashWithExtendedHandler(GetTempDirPath()); + } + + void WinMultiprocessParentAfterChild(HANDLE child) override { + // At this point the child has exited, which means the crash report should + // have been written. + ValidateGeneratedDump(); + + // Delegate the cleanup to the superclass. + WinMultiprocessWithTempDir::WinMultiprocessParentAfterChild(child); + } +}; + +void CrashWithExtendedHandler::ValidateGeneratedDump() { + // Open the database and find the sole dump that should have been created. + std::unique_ptr database( + CrashReportDatabase::Initialize(GetTempDirPath())); + ASSERT_TRUE(database); + + std::vector reports; + ASSERT_EQ(database->GetPendingReports(&reports), + CrashReportDatabase::kNoError); + ASSERT_EQ(reports.size(), 1u); + + // Open the dump and validate that it has the extension stream with the + // expected contents. + FileReader reader; + ASSERT_TRUE(reader.Open(reports[0].file_path)); + + // Read the header. + MINIDUMP_HEADER header = {}; + ASSERT_TRUE(reader.ReadExactly(&header, sizeof(header))); + + // Read the directory. + std::vector directory(header.NumberOfStreams); + ASSERT_TRUE(reader.SeekSet(header.StreamDirectoryRva)); + ASSERT_TRUE(reader.ReadExactly(directory.data(), + directory.size() * sizeof(directory[0]))); + + // Search for the extension stream. + size_t found_extension_streams = 0; + for (const auto& entry : directory) { + if (entry.StreamType == 0xCAFEBABE) { + ++found_extension_streams; + + ASSERT_TRUE(reader.SeekSet(entry.Location.Rva)); + + std::vector data; + data.resize(entry.Location.DataSize); + + ASSERT_TRUE(reader.ReadExactly(data.data(), data.size())); + + static constexpr char kExpectedData[] = "Injected extension stream!"; + EXPECT_EQ(memcmp(kExpectedData, data.data(), sizeof(kExpectedData)), 0); + } + } + + EXPECT_EQ(found_extension_streams, 1u); +} + +#if defined(ADDRESS_SANITIZER) +// https://crbug.com/845011 +#define MAYBE_ExtensibilityCalloutsWork DISABLED_ExtensibilityCalloutsWork +#else +#define MAYBE_ExtensibilityCalloutsWork ExtensibilityCalloutsWork +#endif +TEST(CrashpadHandler, MAYBE_ExtensibilityCalloutsWork) { + WinMultiprocessWithTempDir::Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/handler/crashpad_handler_test_extended_handler.cc b/shared/sentry/external/crashpad/handler/crashpad_handler_test_extended_handler.cc new file mode 100644 index 000000000..9901aba2a --- /dev/null +++ b/shared/sentry/external/crashpad/handler/crashpad_handler_test_extended_handler.cc @@ -0,0 +1,70 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 + +#include "build/build_config.h" +#include "handler/handler_main.h" +#include "minidump/test/minidump_user_extension_stream_util.h" +#include "tools/tool_support.h" + +#if BUILDFLAG(IS_WIN) +#include +#endif + +namespace { + +class TestUserStreamDataSource : public crashpad::UserStreamDataSource { + public: + TestUserStreamDataSource() {} + + TestUserStreamDataSource(const TestUserStreamDataSource&) = delete; + TestUserStreamDataSource& operator=(const TestUserStreamDataSource&) = delete; + + std::unique_ptr + ProduceStreamData(crashpad::ProcessSnapshot* process_snapshot) override; +}; + +std::unique_ptr +TestUserStreamDataSource::ProduceStreamData( + crashpad::ProcessSnapshot* process_snapshot) { + static constexpr char kTestData[] = "Injected extension stream!"; + + return std::make_unique( + 0xCAFEBABE, kTestData, sizeof(kTestData)); +} + +int ExtendedHandlerMain(int argc, char* argv[]) { + crashpad::UserStreamDataSources user_stream_data_sources; + user_stream_data_sources.push_back( + std::make_unique()); + + return crashpad::HandlerMain(argc, argv, &user_stream_data_sources); +} + +} // namespace + +#if BUILDFLAG(IS_POSIX) + +int main(int argc, char* argv[]) { + return ExtendedHandlerMain(argc, argv); +} + +#elif BUILDFLAG(IS_WIN) + +int wmain(int argc, wchar_t* argv[]) { + return crashpad::ToolSupport::Wmain(argc, argv, &ExtendedHandlerMain); +} + +#endif // BUILDFLAG(IS_POSIX) diff --git a/shared/sentry/external/crashpad/handler/handler_main.cc b/shared/sentry/external/crashpad/handler/handler_main.cc new file mode 100644 index 000000000..7cd2fc987 --- /dev/null +++ b/shared/sentry/external/crashpad/handler/handler_main.cc @@ -0,0 +1,1190 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "handler/handler_main.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "base/auto_reset.h" +#include "base/files/file_path.h" +#include "base/files/scoped_file.h" +#include "base/logging.h" +#include "base/metrics/persistent_histogram_allocator.h" +#include "base/scoped_generic.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" +#include "build/chromeos_buildflags.h" +#include "client/crash_report_database.h" +#include "client/crashpad_client.h" +#include "client/crashpad_info.h" +#include "client/prune_crash_reports.h" +#include "client/simple_string_dictionary.h" +#include "handler/crash_report_upload_thread.h" +#include "handler/prune_crash_reports_thread.h" +#include "tools/tool_support.h" +#include "util/file/file_io.h" +#include "util/misc/address_types.h" +#include "util/misc/metrics.h" +#include "util/misc/paths.h" +#include "util/numeric/in_range_cast.h" +#include "util/stdlib/map_insert.h" +#include "util/stdlib/string_number_conversion.h" +#include "util/string/split_string.h" +#include "util/synchronization/semaphore.h" + +#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) +#include "handler/linux/cros_crash_report_exception_handler.h" +#endif + +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) +#include + +#include "handler/linux/crash_report_exception_handler.h" +#include "handler/linux/exception_handler_server.h" +#include "util/posix/signals.h" +#elif BUILDFLAG(IS_APPLE) +#include +#include + +#include "base/mac/scoped_mach_port.h" +#include "handler/mac/crash_report_exception_handler.h" +#include "handler/mac/exception_handler_server.h" +#include "handler/mac/file_limit_annotation.h" +#include "util/mach/bootstrap.h" +#include "util/mach/child_port_handshake.h" +#include "util/posix/close_stdio.h" +#include "util/posix/signals.h" +#elif BUILDFLAG(IS_WIN) +#include + +#include "handler/win/crash_report_exception_handler.h" +#include "util/win/exception_handler_server.h" +#include "util/win/handle.h" +#include "util/win/initial_client_data.h" +#include "util/win/session_end_watcher.h" +#endif // BUILDFLAG(IS_APPLE) + +namespace crashpad { + +namespace { + +#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \ + BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_APPLE) +#define ATTACHMENTS_SUPPORTED 1 +#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || + // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_APPLE) + +void Usage(const base::FilePath& me) { + // clang-format off + fprintf(stderr, +"Usage: %" PRFilePath " [OPTION]...\n" +"Crashpad's exception handler server.\n" +"\n" +" --annotation=KEY=VALUE set a process annotation in each crash report\n" + // clang-format on +#if defined(ATTACHMENTS_SUPPORTED) + // clang-format off +" --attachment=FILE_PATH attach specified file to each crash report\n" +" at the time of the crash\n" + // clang-format on +#endif // ATTACHMENTS_SUPPORTED + // clang-format off +" --database=PATH store the crash report database at PATH\n" + // clang-format on +#if BUILDFLAG(IS_APPLE) + // clang-format off +" --handshake-fd=FD establish communication with the client over FD\n" + // clang-format on +#endif // BUILDFLAG(IS_APPLE) +#if BUILDFLAG(IS_WIN) + // clang-format off +" --initial-client-data=HANDLE_request_crash_dump,\n" +" HANDLE_request_non_crash_dump,\n" +" HANDLE_non_crash_dump_completed,\n" +" HANDLE_pipe,\n" +" HANDLE_client_process,\n" +" Address_crash_exception_information,\n" +" Address_non_crash_exception_information,\n" +" Address_debug_critical_section\n" +" use precreated data to register initial client\n" + // clang-format on +#endif // BUILDFLAG(IS_WIN) +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) + // clang-format off +" --initial-client-fd=FD a socket connected to a client.\n" + // clang-format on +#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || + // BUILDFLAG(IS_CHROMEOS) +#if BUILDFLAG(IS_APPLE) + // clang-format off +" --mach-service=SERVICE register SERVICE with the bootstrap server\n" + // clang-format on +#endif // BUILDFLAG(IS_APPLE) + // clang-format off +" --metrics-dir=DIR store metrics files in DIR (only in Chromium)\n" +" --monitor-self run a second handler to catch crashes in the first\n" +" --monitor-self-annotation=KEY=VALUE\n" +" set a module annotation in the handler\n" +" --monitor-self-argument=ARGUMENT\n" +" provide additional arguments to the second handler\n" +" --no-identify-client-via-url\n" +" when uploading crash report, don't add\n" +" client-identifying arguments to URL\n" +" --no-periodic-tasks don't scan for new reports or prune the database\n" +" --no-rate-limit don't rate limit crash uploads\n" +" --no-upload-gzip don't use gzip compression when uploading\n" + // clang-format on +#if BUILDFLAG(IS_ANDROID) + // clang-format off +" --no-write-minidump-to-database\n" +" don't write minidump to database\n" + // clang-format on +#endif // BUILDFLAG(IS_ANDROID) +#if BUILDFLAG(IS_WIN) + // clang-format off +" --pipe-name=PIPE communicate with the client over PIPE\n" + // clang-format on +#endif // BUILDFLAG(IS_WIN) +#if BUILDFLAG(IS_APPLE) + // clang-format off +" --reset-own-crash-exception-port-to-system-default\n" +" reset the server's exception handler to default\n" + // clang-format on +#endif // BUILDFLAG(IS_APPLE) +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) + // clang-format off +" --sanitization-information=SANITIZATION_INFORMATION_ADDRESS\n" +" the address of a SanitizationInformation struct.\n" +" --shared-client-connection the file descriptor provided by\n" +" --initial-client-fd is shared among multiple\n" +" clients\n" +" --trace-parent-with-exception=EXCEPTION_INFORMATION_ADDRESS\n" +" request a dump for the handler's parent process\n" + // clang-format on +#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || + // BUILDFLAG(IS_ANDROID) + // clang-format off +" --url=URL send crash reports to this Breakpad server URL,\n" +" only if uploads are enabled for the database\n" + // clang-format on +#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) + // clang-format off +" --use-cros-crash-reporter\n" +" pass crash reports to /sbin/crash_reporter\n" +" instead of storing them in the database\n" +" --minidump-dir-for-tests=TEST_MINIDUMP_DIR\n" +" causes /sbin/crash_reporter to leave dumps in\n" +" this directory instead of the normal location\n" +" --always-allow-feedback\n" +" pass the --always_allow_feedback flag to\n" +" crash_reporter, thus skipping metrics consent\n" +" checks\n" + // clang-format on +#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) +#if BUILDFLAG(IS_ANDROID) + // clang-format off +" --write-minidump-to-log write minidump to log\n" + // clang-format on +#endif // BUILDFLAG(IS_ANDROID) + // clang-format off +" --help display this help and exit\n" +" --version output version information and exit\n", + me.value().c_str()); + // clang-format on + ToolSupport::UsageTail(me); +} + +struct Options { + std::map annotations; + std::map monitor_self_annotations; + std::string url; + base::FilePath database; + base::FilePath metrics_dir; + std::vector monitor_self_arguments; +#if BUILDFLAG(IS_APPLE) + std::string mach_service; + int handshake_fd; + bool reset_own_crash_exception_port_to_system_default; +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) + VMAddress exception_information_address; + VMAddress sanitization_information_address; + int initial_client_fd; + bool shared_client_connection; +#if BUILDFLAG(IS_ANDROID) + bool write_minidump_to_log; + bool write_minidump_to_database; +#endif // BUILDFLAG(IS_ANDROID) +#elif BUILDFLAG(IS_WIN) + std::string pipe_name; + InitialClientData initial_client_data; +#endif // BUILDFLAG(IS_APPLE) + bool identify_client_via_url; + bool monitor_self; + bool periodic_tasks; + bool rate_limit; + bool upload_gzip; +#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) + bool use_cros_crash_reporter = false; + base::FilePath minidump_dir_for_tests; + bool always_allow_feedback = false; +#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) +#if defined(ATTACHMENTS_SUPPORTED) + std::vector attachments; +#endif // ATTACHMENTS_SUPPORTED +}; + +// Splits |key_value| on '=' and inserts the resulting key and value into |map|. +// If |key_value| has the wrong format, logs an error and returns false. If the +// key is already in the map, logs a warning, replaces the existing value, and +// returns true. If the key and value were inserted into the map, returns true. +// |argument| is used to give context to logged messages. +bool AddKeyValueToMap(std::map* map, + const std::string& key_value, + const char* argument) { + std::string key; + std::string value; + if (!SplitStringFirst(key_value, '=', &key, &value)) { + LOG(ERROR) << argument << " requires KEY=VALUE"; + return false; + } + + std::string old_value; + if (!MapInsertOrReplace(map, key, value, &old_value)) { + LOG(WARNING) << argument << " has duplicate key " << key + << ", discarding value " << old_value; + } + return true; +} + +// Calls Metrics::HandlerLifetimeMilestone, but only on the first call. This is +// to prevent multiple exit events from inadvertently being recorded, which +// might happen if a crash occurs during destruction in what would otherwise be +// a normal exit, or if a CallMetricsRecordNormalExit object is destroyed after +// something else logs an exit event. +void MetricsRecordExit(Metrics::LifetimeMilestone milestone) { + static std::atomic_flag metrics_exit_recorded = ATOMIC_FLAG_INIT; + if (!metrics_exit_recorded.test_and_set()) { + Metrics::HandlerLifetimeMilestone(milestone); + } +} + +// Calls MetricsRecordExit() to record a failure, and returns EXIT_FAILURE for +// the convenience of callers in main() which can simply write “return +// ExitFailure();â€. +int ExitFailure() { + MetricsRecordExit(Metrics::LifetimeMilestone::kFailed); + return EXIT_FAILURE; +} + +class CallMetricsRecordNormalExit { + public: + CallMetricsRecordNormalExit() {} + + CallMetricsRecordNormalExit(const CallMetricsRecordNormalExit&) = delete; + CallMetricsRecordNormalExit& operator=(const CallMetricsRecordNormalExit&) = + delete; + + ~CallMetricsRecordNormalExit() { + MetricsRecordExit(Metrics::LifetimeMilestone::kExitedNormally); + } +}; + +#if BUILDFLAG(IS_APPLE) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \ + BUILDFLAG(IS_ANDROID) + +void HandleCrashSignal(int sig, siginfo_t* siginfo, void* context) { + MetricsRecordExit(Metrics::LifetimeMilestone::kCrashed); + + // Is siginfo->si_code useful? The only interesting values on macOS are 0 (not + // useful, signals generated asynchronously such as by kill() or raise()) and + // small positive numbers (useful, signal generated via a hardware fault). The + // standard specifies these other constants, and while xnu never uses them, + // they are intended to denote signals generated asynchronously and are + // included here. Additionally, existing practice on other systems + // (acknowledged by the standard) is for negative numbers to indicate that a + // signal was generated asynchronously. Although xnu does not do this, allow + // for the possibility for completeness. + bool si_code_valid = !(siginfo->si_code <= 0 || + siginfo->si_code == SI_USER || + siginfo->si_code == SI_QUEUE || + siginfo->si_code == SI_TIMER || + siginfo->si_code == SI_ASYNCIO || + siginfo->si_code == SI_MESGQ); + + // 0x5343 = 'SC', signifying “signal and codeâ€, disambiguates from the schema + // used by ExceptionCodeForMetrics(). That system primarily uses Mach + // exception types and codes, which are not available to a POSIX signal + // handler. It does provide a way to encode only signal numbers, but does so + // with the understanding that certain “raw†signals would not be encountered + // without a Mach exception. Furthermore, it does not allow siginfo->si_code + // to be encoded, because that’s not available to Mach exception handlers. It + // would be a shame to lose that information available to a POSIX signal + // handler. + int metrics_code = 0x53430000 | (InRangeCast(sig, 0xff) << 8); + if (si_code_valid) { + metrics_code |= InRangeCast(siginfo->si_code, 0xff); + } + Metrics::HandlerCrashed(metrics_code); + + Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr); +} + +void HandleTerminateSignal(int sig, siginfo_t* siginfo, void* context) { + MetricsRecordExit(Metrics::LifetimeMilestone::kTerminated); + Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr); +} + +void ReinstallCrashHandler() { + // This is used to re-enable the metrics-recording crash handler after + // MonitorSelf() sets up a Crashpad exception handler. On macOS, the + // metrics-recording handler uses signals and the Crashpad handler uses Mach + // exceptions, so there’s nothing to re-enable. + // On Linux, the signal handler installed by StartHandler() restores the + // previously installed signal handler by default. +} + +void InstallCrashHandler() { + Signals::InstallCrashHandlers(HandleCrashSignal, 0, nullptr); + + // Not a crash handler, but close enough. + Signals::InstallTerminateHandlers(HandleTerminateSignal, 0, nullptr); +} + +#if BUILDFLAG(IS_APPLE) + +struct ResetSIGTERMTraits { + static struct sigaction* InvalidValue() { + return nullptr; + } + + static void Free(struct sigaction* sa) { + int rv = sigaction(SIGTERM, sa, nullptr); + PLOG_IF(ERROR, rv != 0) << "sigaction"; + } +}; +using ScopedResetSIGTERM = + base::ScopedGeneric; + +ExceptionHandlerServer* g_exception_handler_server; + +// This signal handler is only operative when being run from launchd. +void HandleSIGTERM(int sig, siginfo_t* siginfo, void* context) { + // Don’t call MetricsRecordExit(). This is part of the normal exit path when + // running from launchd. + + DCHECK(g_exception_handler_server); + g_exception_handler_server->Stop(); +} + +#endif // BUILDFLAG(IS_APPLE) + +#elif BUILDFLAG(IS_WIN) + +LONG(WINAPI* g_original_exception_filter)(EXCEPTION_POINTERS*) = nullptr; + +LONG WINAPI UnhandledExceptionHandler(EXCEPTION_POINTERS* exception_pointers) { + MetricsRecordExit(Metrics::LifetimeMilestone::kCrashed); + Metrics::HandlerCrashed(exception_pointers->ExceptionRecord->ExceptionCode); + + if (g_original_exception_filter) + return g_original_exception_filter(exception_pointers); + else + return EXCEPTION_CONTINUE_SEARCH; +} + +// Handles events like Control-C and Control-Break on a console. +BOOL WINAPI ConsoleHandler(DWORD console_event) { + MetricsRecordExit(Metrics::LifetimeMilestone::kTerminated); + return false; +} + +// Handles a WM_ENDSESSION message sent when the user session is ending. +class TerminateHandler final : public SessionEndWatcher { + public: + TerminateHandler() : SessionEndWatcher() {} + + TerminateHandler(const TerminateHandler&) = delete; + TerminateHandler& operator=(const TerminateHandler&) = delete; + + ~TerminateHandler() override {} + + private: + // SessionEndWatcher: + void SessionEnding() override { + MetricsRecordExit(Metrics::LifetimeMilestone::kTerminated); + } +}; + +void ReinstallCrashHandler() { + // This is used to re-enable the metrics-recording crash handler after + // MonitorSelf() sets up a Crashpad exception handler. The Crashpad handler + // takes over the UnhandledExceptionFilter, so reinstall the metrics-recording + // one. + g_original_exception_filter = + SetUnhandledExceptionFilter(&UnhandledExceptionHandler); +} + +void InstallCrashHandler() { + ReinstallCrashHandler(); + + // These are termination handlers, not crash handlers, but that’s close + // enough. Note that destroying the TerminateHandler would wait for its thread + // to exit, which isn’t necessary or desirable. + SetConsoleCtrlHandler(ConsoleHandler, true); + [[maybe_unused]] static TerminateHandler* terminate_handler = + new TerminateHandler(); +} + +#endif // BUILDFLAG(IS_APPLE) + +void MonitorSelf(const Options& options) { + base::FilePath executable_path; + if (!Paths::Executable(&executable_path)) { + return; + } + + if (std::find(options.monitor_self_arguments.begin(), + options.monitor_self_arguments.end(), + "--monitor-self") != options.monitor_self_arguments.end()) { + LOG(WARNING) << "--monitor-self-argument=--monitor-self is not supported"; + return; + } + std::vector extra_arguments(options.monitor_self_arguments); + if (!options.identify_client_via_url) { + extra_arguments.push_back("--no-identify-client-via-url"); + } + extra_arguments.push_back("--no-periodic-tasks"); + if (!options.rate_limit) { + extra_arguments.push_back("--no-rate-limit"); + } + if (!options.upload_gzip) { + extra_arguments.push_back("--no-upload-gzip"); + } + for (const auto& iterator : options.monitor_self_annotations) { + extra_arguments.push_back( + base::StringPrintf("--monitor-self-annotation=%s=%s", + iterator.first.c_str(), + iterator.second.c_str())); + } + + // Don’t use options.metrics_dir. The current implementation only allows one + // instance of crashpad_handler to be writing metrics at a time, and it should + // be the primary instance. + CrashpadClient crashpad_client; +#if BUILDFLAG(IS_ANDROID) + if (!crashpad_client.StartHandlerAtCrash(executable_path, + options.database, + base::FilePath(), + options.url, + options.annotations, + extra_arguments)) { + return; + } +#else + if (!crashpad_client.StartHandler(executable_path, + options.database, + base::FilePath(), + options.url, + options.annotations, + extra_arguments, + true, + false)) { + return; + } +#endif + + // Make sure that appropriate metrics will be recorded on crash before this + // process is terminated. + ReinstallCrashHandler(); +} + +class ScopedStoppable { + public: + ScopedStoppable() = default; + + ScopedStoppable(const ScopedStoppable&) = delete; + ScopedStoppable& operator=(const ScopedStoppable&) = delete; + + ~ScopedStoppable() { + if (stoppable_) { + stoppable_->Stop(); + } + } + + void Reset(Stoppable* stoppable) { stoppable_.reset(stoppable); } + + Stoppable* Get() { return stoppable_.get(); } + + private: + std::unique_ptr stoppable_; +}; + +void InitCrashpadLogging() { + logging::LoggingSettings settings; +#if BUILDFLAG(IS_CHROMEOS_ASH) + settings.logging_dest = logging::LOG_TO_FILE; + settings.log_file_path = "/var/log/chrome/chrome"; +#elif BUILDFLAG(IS_WIN) + settings.logging_dest = logging::LOG_TO_SYSTEM_DEBUG_LOG; +#else + settings.logging_dest = + logging::LOG_TO_SYSTEM_DEBUG_LOG | logging::LOG_TO_STDERR; +#endif + logging::InitLogging(settings); +} + +} // namespace + +int HandlerMain(int argc, + char* argv[], + const UserStreamDataSources* user_stream_sources) { + InitCrashpadLogging(); + + InstallCrashHandler(); + CallMetricsRecordNormalExit metrics_record_normal_exit; + + const base::FilePath argv0( + ToolSupport::CommandLineArgumentToFilePathStringType(argv[0])); + const base::FilePath me(argv0.BaseName()); + + enum OptionFlags { + // Long options without short equivalents. + kOptionLastChar = 255, + kOptionAnnotation, +#if defined(ATTACHMENTS_SUPPORTED) + kOptionAttachment, +#endif // ATTACHMENTS_SUPPORTED + kOptionDatabase, +#if BUILDFLAG(IS_APPLE) + kOptionHandshakeFD, +#endif // BUILDFLAG(IS_APPLE) +#if BUILDFLAG(IS_WIN) + kOptionInitialClientData, +#endif // BUILDFLAG(IS_WIN) +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) + kOptionInitialClientFD, +#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || + // BUILDFLAG(IS_CHROMEOS) +#if BUILDFLAG(IS_APPLE) + kOptionMachService, +#endif // BUILDFLAG(IS_APPLE) + kOptionMetrics, + kOptionMonitorSelf, + kOptionMonitorSelfAnnotation, + kOptionMonitorSelfArgument, + kOptionNoIdentifyClientViaUrl, + kOptionNoPeriodicTasks, + kOptionNoRateLimit, + kOptionNoUploadGzip, +#if BUILDFLAG(IS_ANDROID) + kOptionNoWriteMinidumpToDatabase, +#endif // BUILDFLAG(IS_ANDROID) +#if BUILDFLAG(IS_WIN) + kOptionPipeName, +#endif // BUILDFLAG(IS_WIN) +#if BUILDFLAG(IS_APPLE) + kOptionResetOwnCrashExceptionPortToSystemDefault, +#endif // BUILDFLAG(IS_APPLE) +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) + kOptionSanitizationInformation, + kOptionSharedClientConnection, + kOptionTraceParentWithException, +#endif + kOptionURL, +#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) + kOptionUseCrosCrashReporter, + kOptionMinidumpDirForTests, + kOptionAlwaysAllowFeedback, +#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) +#if BUILDFLAG(IS_ANDROID) + kOptionWriteMinidumpToLog, +#endif // BUILDFLAG(IS_ANDROID) + + // Standard options. + kOptionHelp = -2, + kOptionVersion = -3, + }; + + static constexpr option long_options[] = { + {"annotation", required_argument, nullptr, kOptionAnnotation}, +#if defined(ATTACHMENTS_SUPPORTED) + {"attachment", required_argument, nullptr, kOptionAttachment}, +#endif // ATTACHMENTS_SUPPORTED + {"database", required_argument, nullptr, kOptionDatabase}, +#if BUILDFLAG(IS_APPLE) + {"handshake-fd", required_argument, nullptr, kOptionHandshakeFD}, +#endif // BUILDFLAG(IS_APPLE) +#if BUILDFLAG(IS_WIN) + {"initial-client-data", + required_argument, + nullptr, + kOptionInitialClientData}, +#endif // BUILDFLAG(IS_APPLE) +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) + {"initial-client-fd", required_argument, nullptr, kOptionInitialClientFD}, +#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || + // BUILDFLAG(IS_CHROMEOS) +#if BUILDFLAG(IS_APPLE) + {"mach-service", required_argument, nullptr, kOptionMachService}, +#endif // BUILDFLAG(IS_APPLE) + {"metrics-dir", required_argument, nullptr, kOptionMetrics}, + {"monitor-self", no_argument, nullptr, kOptionMonitorSelf}, + {"monitor-self-annotation", + required_argument, + nullptr, + kOptionMonitorSelfAnnotation}, + {"monitor-self-argument", + required_argument, + nullptr, + kOptionMonitorSelfArgument}, + {"no-identify-client-via-url", + no_argument, + nullptr, + kOptionNoIdentifyClientViaUrl}, + {"no-periodic-tasks", no_argument, nullptr, kOptionNoPeriodicTasks}, + {"no-rate-limit", no_argument, nullptr, kOptionNoRateLimit}, + {"no-upload-gzip", no_argument, nullptr, kOptionNoUploadGzip}, +#if BUILDFLAG(IS_ANDROID) + {"no-write-minidump-to-database", + no_argument, + nullptr, + kOptionNoWriteMinidumpToDatabase}, +#endif // BUILDFLAG(IS_ANDROID) +#if BUILDFLAG(IS_WIN) + {"pipe-name", required_argument, nullptr, kOptionPipeName}, +#endif // BUILDFLAG(IS_WIN) +#if BUILDFLAG(IS_APPLE) + {"reset-own-crash-exception-port-to-system-default", + no_argument, + nullptr, + kOptionResetOwnCrashExceptionPortToSystemDefault}, +#endif // BUILDFLAG(IS_APPLE) +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) + {"sanitization-information", + required_argument, + nullptr, + kOptionSanitizationInformation}, + {"shared-client-connection", + no_argument, + nullptr, + kOptionSharedClientConnection}, + {"trace-parent-with-exception", + required_argument, + nullptr, + kOptionTraceParentWithException}, +#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || + // BUILDFLAG(IS_ANDROID) + {"url", required_argument, nullptr, kOptionURL}, +#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) + {"use-cros-crash-reporter", + no_argument, + nullptr, + kOptionUseCrosCrashReporter}, + {"minidump-dir-for-tests", + required_argument, + nullptr, + kOptionMinidumpDirForTests}, + {"always-allow-feedback", no_argument, nullptr, kOptionAlwaysAllowFeedback}, +#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) +#if BUILDFLAG(IS_ANDROID) + {"write-minidump-to-log", no_argument, nullptr, kOptionWriteMinidumpToLog}, +#endif // BUILDFLAG(IS_ANDROID) + {"help", no_argument, nullptr, kOptionHelp}, + {"version", no_argument, nullptr, kOptionVersion}, + {nullptr, 0, nullptr, 0}, + }; + + Options options = {}; +#if BUILDFLAG(IS_APPLE) + options.handshake_fd = -1; +#endif + options.identify_client_via_url = true; +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) + options.initial_client_fd = kInvalidFileHandle; +#endif + options.periodic_tasks = true; + options.rate_limit = true; + options.upload_gzip = true; +#if BUILDFLAG(IS_ANDROID) + options.write_minidump_to_database = true; +#endif + + int opt; + while ((opt = getopt_long(argc, argv, "", long_options, nullptr)) != -1) { + switch (opt) { + case kOptionAnnotation: { + if (!AddKeyValueToMap(&options.annotations, optarg, "--annotation")) { + return ExitFailure(); + } + break; + } +#if defined(ATTACHMENTS_SUPPORTED) + case kOptionAttachment: { + options.attachments.push_back(base::FilePath( + ToolSupport::CommandLineArgumentToFilePathStringType(optarg))); + break; + } +#endif // ATTACHMENTS_SUPPORTED + case kOptionDatabase: { + options.database = base::FilePath( + ToolSupport::CommandLineArgumentToFilePathStringType(optarg)); + break; + } +#if BUILDFLAG(IS_APPLE) + case kOptionHandshakeFD: { + if (!StringToNumber(optarg, &options.handshake_fd) || + options.handshake_fd < 0) { + ToolSupport::UsageHint(me, + "--handshake-fd requires a file descriptor"); + return ExitFailure(); + } + break; + } + case kOptionMachService: { + options.mach_service = optarg; + break; + } +#endif // BUILDFLAG(IS_APPLE) +#if BUILDFLAG(IS_WIN) + case kOptionInitialClientData: { + if (!options.initial_client_data.InitializeFromString(optarg)) { + ToolSupport::UsageHint( + me, "failed to parse --initial-client-data"); + return ExitFailure(); + } + break; + } +#endif // BUILDFLAG(IS_WIN) +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) + case kOptionInitialClientFD: { + if (!base::StringToInt(optarg, &options.initial_client_fd)) { + ToolSupport::UsageHint(me, "failed to parse --initial-client-fd"); + return ExitFailure(); + } + break; + } +#endif // BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || + // BUILDFLAG(IS_CHROMEOS) + case kOptionMetrics: { + options.metrics_dir = base::FilePath( + ToolSupport::CommandLineArgumentToFilePathStringType(optarg)); + break; + } + case kOptionMonitorSelf: { + options.monitor_self = true; + break; + } + case kOptionMonitorSelfAnnotation: { + if (!AddKeyValueToMap(&options.monitor_self_annotations, + optarg, + "--monitor-self-annotation")) { + return ExitFailure(); + } + break; + } + case kOptionMonitorSelfArgument: { + options.monitor_self_arguments.push_back(optarg); + break; + } + case kOptionNoIdentifyClientViaUrl: { + options.identify_client_via_url = false; + break; + } + case kOptionNoPeriodicTasks: { + options.periodic_tasks = false; + break; + } + case kOptionNoRateLimit: { + options.rate_limit = false; + break; + } + case kOptionNoUploadGzip: { + options.upload_gzip = false; + break; + } +#if BUILDFLAG(IS_ANDROID) + case kOptionNoWriteMinidumpToDatabase: { + options.write_minidump_to_database = false; + break; + } +#endif // BUILDFLAG(IS_ANDROID) +#if BUILDFLAG(IS_WIN) + case kOptionPipeName: { + options.pipe_name = optarg; + break; + } +#endif // BUILDFLAG(IS_WIN) +#if BUILDFLAG(IS_APPLE) + case kOptionResetOwnCrashExceptionPortToSystemDefault: { + options.reset_own_crash_exception_port_to_system_default = true; + break; + } +#endif // BUILDFLAG(IS_APPLE) +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) + case kOptionSanitizationInformation: { + if (!StringToNumber(optarg, + &options.sanitization_information_address)) { + ToolSupport::UsageHint(me, + "failed to parse --sanitization-information"); + return ExitFailure(); + } + break; + } + case kOptionSharedClientConnection: { + options.shared_client_connection = true; + break; + } + case kOptionTraceParentWithException: { + if (!StringToNumber(optarg, &options.exception_information_address)) { + ToolSupport::UsageHint( + me, "failed to parse --trace-parent-with-exception"); + return ExitFailure(); + } + break; + } +#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || + // BUILDFLAG(IS_ANDROID) + case kOptionURL: { + options.url = optarg; + break; + } +#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) + case kOptionUseCrosCrashReporter: { + options.use_cros_crash_reporter = true; + break; + } + case kOptionMinidumpDirForTests: { + options.minidump_dir_for_tests = base::FilePath( + ToolSupport::CommandLineArgumentToFilePathStringType(optarg)); + break; + } + case kOptionAlwaysAllowFeedback: { + options.always_allow_feedback = true; + break; + } +#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) +#if BUILDFLAG(IS_ANDROID) + case kOptionWriteMinidumpToLog: { + options.write_minidump_to_log = true; + break; + } +#endif // BUILDFLAG(IS_ANDROID) + case kOptionHelp: { + Usage(me); + MetricsRecordExit(Metrics::LifetimeMilestone::kExitedEarly); + return EXIT_SUCCESS; + } + case kOptionVersion: { + ToolSupport::Version(me); + MetricsRecordExit(Metrics::LifetimeMilestone::kExitedEarly); + return EXIT_SUCCESS; + } + default: { + ToolSupport::UsageHint(me, nullptr); + return ExitFailure(); + } + } + } + argc -= optind; + argv += optind; + +#if BUILDFLAG(IS_APPLE) + if (options.handshake_fd < 0 && options.mach_service.empty()) { + ToolSupport::UsageHint(me, "--handshake-fd or --mach-service is required"); + return ExitFailure(); + } + if (options.handshake_fd >= 0 && !options.mach_service.empty()) { + ToolSupport::UsageHint( + me, "--handshake-fd and --mach-service are incompatible"); + return ExitFailure(); + } +#elif BUILDFLAG(IS_WIN) + if (!options.initial_client_data.IsValid() && options.pipe_name.empty()) { + ToolSupport::UsageHint(me, + "--initial-client-data or --pipe-name is required"); + return ExitFailure(); + } + if (options.initial_client_data.IsValid() && !options.pipe_name.empty()) { + ToolSupport::UsageHint( + me, "--initial-client-data and --pipe-name are incompatible"); + return ExitFailure(); + } +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) + if (!options.exception_information_address && + options.initial_client_fd == kInvalidFileHandle) { + ToolSupport::UsageHint( + me, "--trace-parent-with-exception or --initial-client-fd is required"); + return ExitFailure(); + } + if (options.sanitization_information_address && + !options.exception_information_address) { + ToolSupport::UsageHint( + me, + "--sanitization_information requires --trace-parent-with-exception"); + return ExitFailure(); + } + if (options.shared_client_connection && + options.initial_client_fd == kInvalidFileHandle) { + ToolSupport::UsageHint( + me, "--shared-client-connection requires --initial-client-fd"); + return ExitFailure(); + } +#if BUILDFLAG(IS_ANDROID) + if (!options.write_minidump_to_log && !options.write_minidump_to_database) { + ToolSupport::UsageHint(me, + "--no_write_minidump_to_database is required to use " + "with --write_minidump_to_log."); + ExitFailure(); + } +#endif // BUILDFLAG(IS_ANDROID) +#endif // BUILDFLAG(IS_APPLE) + + if (options.database.empty()) { + ToolSupport::UsageHint(me, "--database is required"); + return ExitFailure(); + } + + if (argc) { + ToolSupport::UsageHint(me, nullptr); + return ExitFailure(); + } + +#if BUILDFLAG(IS_APPLE) + if (options.reset_own_crash_exception_port_to_system_default) { + CrashpadClient::UseSystemDefaultHandler(); + } +#endif // BUILDFLAG(IS_APPLE) + + if (options.monitor_self) { + MonitorSelf(options); + } + + if (!options.monitor_self_annotations.empty()) { + // Establish these annotations even if --monitor-self is not present, in + // case something such as generate_dump wants to try to access them later. + // + // If the handler is part of a multi-purpose executable, simple annotations + // may already be present for this module. If they are, use them. + CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo(); + SimpleStringDictionary* module_annotations = + crashpad_info->simple_annotations(); + if (!module_annotations) { + module_annotations = new SimpleStringDictionary(); + crashpad_info->set_simple_annotations(module_annotations); + } + + for (const auto& iterator : options.monitor_self_annotations) { + module_annotations->SetKeyValue(iterator.first.c_str(), + iterator.second.c_str()); + } + } + + std::unique_ptr database( + CrashReportDatabase::Initialize(options.database)); + if (!database) { + return ExitFailure(); + } + + ScopedStoppable upload_thread; + if (!options.url.empty()) { + // TODO(scottmg): options.rate_limit should be removed when we have a + // configurable database setting to control upload limiting. + // See https://crashpad.chromium.org/bug/23. + CrashReportUploadThread::Options upload_thread_options; + upload_thread_options.identify_client_via_url = + options.identify_client_via_url; + upload_thread_options.rate_limit = options.rate_limit; + upload_thread_options.upload_gzip = options.upload_gzip; + upload_thread_options.watch_pending_reports = options.periodic_tasks; + + upload_thread.Reset(new CrashReportUploadThread( + database.get(), options.url, upload_thread_options)); + upload_thread.Get()->Start(); + } + +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) + std::unique_ptr exception_handler; +#else + std::unique_ptr exception_handler; +#endif + +#if BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) + if (options.use_cros_crash_reporter) { + auto cros_handler = std::make_unique( + database.get(), + &options.annotations, + user_stream_sources); + + if (!options.minidump_dir_for_tests.empty()) { + cros_handler->SetDumpDir(options.minidump_dir_for_tests); + } + + if (options.always_allow_feedback) { + cros_handler->SetAlwaysAllowFeedback(); + } + + exception_handler = std::move(cros_handler); + } else { + exception_handler = std::make_unique( + database.get(), + static_cast(upload_thread.Get()), + &options.annotations, + &options.attachments, + true, + false, + user_stream_sources); + } +#else + exception_handler = std::make_unique( + database.get(), + static_cast(upload_thread.Get()), + &options.annotations, +#if defined(ATTACHMENTS_SUPPORTED) + &options.attachments, +#endif // ATTACHMENTS_SUPPORTED +#if BUILDFLAG(IS_ANDROID) + options.write_minidump_to_database, + options.write_minidump_to_log, +#endif // BUILDFLAG(IS_ANDROID) +#if BUILDFLAG(IS_LINUX) + true, + false, +#endif // BUILDFLAG(IS_LINUX) + user_stream_sources); +#endif // BUILDFLAG(IS_CHROMEOS_ASH) || BUILDFLAG(IS_CHROMEOS_LACROS) + +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) + if (options.exception_information_address) { + ExceptionHandlerProtocol::ClientInformation info; + info.exception_information_address = options.exception_information_address; + info.sanitization_information_address = + options.sanitization_information_address; + return exception_handler->HandleException(getppid(), geteuid(), info) + ? EXIT_SUCCESS + : ExitFailure(); + } +#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || + // BUILDFLAG(IS_ANDROID) + + ScopedStoppable prune_thread; + if (options.periodic_tasks) { + prune_thread.Reset(new PruneCrashReportThread( + database.get(), PruneCondition::GetDefault())); + prune_thread.Get()->Start(); + } + +#if BUILDFLAG(IS_APPLE) + if (options.mach_service.empty()) { + // Don’t do this when being run by launchd. See launchd.plist(5). + CloseStdinAndStdout(); + } + + base::mac::ScopedMachReceiveRight receive_right; + + if (options.handshake_fd >= 0) { + receive_right.reset( + ChildPortHandshake::RunServerForFD( + base::ScopedFD(options.handshake_fd), + ChildPortHandshake::PortRightType::kReceiveRight)); + } else if (!options.mach_service.empty()) { + receive_right = BootstrapCheckIn(options.mach_service); + } + + if (!receive_right.is_valid()) { + return ExitFailure(); + } + + ExceptionHandlerServer exception_handler_server( + std::move(receive_right), !options.mach_service.empty()); + base::AutoReset reset_g_exception_handler_server( + &g_exception_handler_server, &exception_handler_server); + + struct sigaction old_sigterm_action; + ScopedResetSIGTERM reset_sigterm; + if (!options.mach_service.empty()) { + // When running from launchd, no no-senders notification could ever be + // triggered, because launchd maintains a send right to the service. When + // launchd wants the job to exit, it will send a SIGTERM. See + // launchd.plist(5). + // + // Set up a SIGTERM handler that will call exception_handler_server.Stop(). + // This replaces the HandleTerminateSignal handler for SIGTERM. + if (Signals::InstallHandler( + SIGTERM, HandleSIGTERM, 0, &old_sigterm_action)) { + reset_sigterm.reset(&old_sigterm_action); + } + } + + RecordFileLimitAnnotation(); +#elif BUILDFLAG(IS_WIN) + // Shut down as late as possible relative to programs we're watching. + if (!SetProcessShutdownParameters(0x100, SHUTDOWN_NORETRY)) + PLOG(ERROR) << "SetProcessShutdownParameters"; + + ExceptionHandlerServer exception_handler_server(!options.pipe_name.empty()); + + if (!options.pipe_name.empty()) { + exception_handler_server.SetPipeName(base::UTF8ToWide(options.pipe_name)); + } +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) + ExceptionHandlerServer exception_handler_server; +#endif // BUILDFLAG(IS_APPLE) + + base::GlobalHistogramAllocator* histogram_allocator = nullptr; + if (!options.metrics_dir.empty()) { + static constexpr char kMetricsName[] = "CrashpadMetrics"; + constexpr size_t kMetricsFileSize = 1 << 20; + if (base::GlobalHistogramAllocator::CreateWithActiveFileInDir( + options.metrics_dir, kMetricsFileSize, 0, kMetricsName)) { + histogram_allocator = base::GlobalHistogramAllocator::Get(); + histogram_allocator->CreateTrackingHistograms(kMetricsName); + } + } + + Metrics::HandlerLifetimeMilestone(Metrics::LifetimeMilestone::kStarted); + +#if BUILDFLAG(IS_WIN) + if (options.initial_client_data.IsValid()) { + exception_handler_server.InitializeWithInheritedDataForInitialClient( + options.initial_client_data, exception_handler.get()); + } +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) + if (options.initial_client_fd == kInvalidFileHandle || + !exception_handler_server.InitializeWithClient( + ScopedFileHandle(options.initial_client_fd), + options.shared_client_connection)) { + return ExitFailure(); + } +#endif // BUILDFLAG(IS_WIN) + + exception_handler_server.Run(exception_handler.get()); + + return EXIT_SUCCESS; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/handler/handler_main.h b/shared/sentry/external/crashpad/handler/handler_main.h new file mode 100644 index 000000000..b6cc467cd --- /dev/null +++ b/shared/sentry/external/crashpad/handler/handler_main.h @@ -0,0 +1,50 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_HANDLER_HANDLER_MAIN_H_ +#define CRASHPAD_HANDLER_HANDLER_MAIN_H_ + +#include "build/build_config.h" +#include "handler/user_stream_data_source.h" + +namespace crashpad { + +//! \brief The `main()` of the `crashpad_handler` binary. +//! +//! This is exposed so that `crashpad_handler` can be embedded into another +//! binary, but called and used as if it were a standalone executable. +//! +//! \param[in] argc \a argc as passed to `main()`. +//! \param[in] argv \a argv as passed to `main()`. +//! \param[in] user_stream_sources An optional vector containing the +//! extensibility data sources to call on crash. Each time a minidump is +//! created, the sources are called in turn. Any streams returned are added +//! to the minidump. +int HandlerMain(int argc, + char* argv[], + const UserStreamDataSources* user_stream_sources); + +#if BUILDFLAG(IS_ANDROID) +//! \brief The `main()` entry point for Android libraries. +//! +//! This symbol is the entry point for crashpad when it is dynamically loaded +//! using /system/bin/linker. +//! +//! \sa CrashpadClient::StartHandlerWithLinkerAtCrash() +extern "C" int CrashpadHandlerMain(int argc, char* argv[]); +#endif + +} // namespace crashpad + +#endif // CRASHPAD_HANDLER_HANDLER_MAIN_H_ diff --git a/shared/sentry/external/crashpad/handler/linux/capture_snapshot.cc b/shared/sentry/external/crashpad/handler/linux/capture_snapshot.cc new file mode 100644 index 000000000..12beddfcc --- /dev/null +++ b/shared/sentry/external/crashpad/handler/linux/capture_snapshot.cc @@ -0,0 +1,118 @@ +// Copyright 2019 The Crashpad Authors. All rights reserved. +// +// 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 "handler/linux/capture_snapshot.h" + +#include + +#include "snapshot/crashpad_info_client_options.h" +#include "snapshot/sanitized/sanitization_information.h" +#include "util/misc/metrics.h" +#include "util/misc/tri_state.h" + +namespace crashpad { + +bool CaptureSnapshot( + PtraceConnection* connection, + const ExceptionHandlerProtocol::ClientInformation& info, + const std::map& process_annotations, + uid_t client_uid, + VMAddress requesting_thread_stack_address, + pid_t* requesting_thread_id, + std::unique_ptr* snapshot, + std::unique_ptr* sanitized_snapshot) { + std::unique_ptr process_snapshot( + new ProcessSnapshotLinux()); + if (!process_snapshot->Initialize(connection)) { + Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSnapshotFailed); + return false; + } + + pid_t local_requesting_thread_id = -1; + if (requesting_thread_stack_address) { + local_requesting_thread_id = process_snapshot->FindThreadWithStackAddress( + requesting_thread_stack_address); + } + + if (requesting_thread_id) { + *requesting_thread_id = local_requesting_thread_id; + } + + if (!process_snapshot->InitializeException(info.exception_information_address, + local_requesting_thread_id)) { + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kExceptionInitializationFailed); + return false; + } + + Metrics::ExceptionCode(process_snapshot->Exception()->Exception()); + + CrashpadInfoClientOptions client_options; + process_snapshot->GetCrashpadOptions(&client_options); + if (client_options.crashpad_handler_behavior == TriState::kDisabled) { + return false; + } + + for (auto& p : process_annotations) { + process_snapshot->AddAnnotation(p.first, p.second); + } + + if (info.sanitization_information_address) { + SanitizationInformation sanitization_info; + ProcessMemoryRange range; + if (!range.Initialize(connection->Memory(), connection->Is64Bit()) || + !range.Read(info.sanitization_information_address, + sizeof(sanitization_info), + &sanitization_info)) { + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kSanitizationInitializationFailed); + return false; + } + + auto allowed_annotations = std::make_unique>(); + auto allowed_memory_ranges = + std::make_unique>>(); + if (!ReadAllowedAnnotations(range, + sanitization_info.allowed_annotations_address, + allowed_annotations.get()) || + !ReadAllowedMemoryRanges( + range, + sanitization_info.allowed_memory_ranges_address, + allowed_memory_ranges.get())) { + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kSanitizationInitializationFailed); + return false; + } + + std::unique_ptr sanitized( + new ProcessSnapshotSanitized()); + if (!sanitized->Initialize(process_snapshot.get(), + sanitization_info.allowed_annotations_address + ? std::move(allowed_annotations) + : nullptr, + std::move(allowed_memory_ranges), + sanitization_info.target_module_address, + sanitization_info.sanitize_stacks)) { + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kSkippedDueToSanitization); + return false; + } + *sanitized_snapshot = std::move(sanitized); + } + + *snapshot = std::move(process_snapshot); + return true; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/handler/linux/capture_snapshot.h b/shared/sentry/external/crashpad/handler/linux/capture_snapshot.h new file mode 100644 index 000000000..78886dc5a --- /dev/null +++ b/shared/sentry/external/crashpad/handler/linux/capture_snapshot.h @@ -0,0 +1,67 @@ +// Copyright 2019 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_HANDLER_LINUX_CAPTURE_SNAPSHOT_H_ +#define CRASHPAD_HANDLER_LINUX_CAPTURE_SNAPSHOT_H_ + +#include + +#include +#include +#include + +#include "snapshot/linux/process_snapshot_linux.h" +#include "snapshot/sanitized/process_snapshot_sanitized.h" +#include "util/linux/exception_handler_protocol.h" +#include "util/linux/ptrace_connection.h" +#include "util/misc/address_types.h" + +namespace crashpad { + +//! \brief Captures a snapshot of a client over \a connection. +//! +//! \param[in] connection A PtraceConnection to the client to snapshot. +//! \param[in] info Information about the client configuring the snapshot. +//! \param[in] process_annotations A map of annotations to insert as +//! process-level annotations into the snapshot. +//! \param[in] client_uid The client's user ID. +//! \param[in] requesting_thread_stack_address An address on the stack of the +//! thread requesting the snapshot. If \a info includes an exception +//! address, the exception will be assigned to the thread whose stack +//! address range contains this address. If 0, \a requesting_thread_id will +//! be -1. +//! \param[out] requesting_thread_id The thread ID of the thread corresponding +//! to \a requesting_thread_stack_address. Set to -1 if the thread ID could +//! not be determined. Optional. +//! \param[out] process_snapshot A snapshot of the client process, valid if this +//! function returns `true`. +//! \param[out] sanitized_snapshot A sanitized snapshot of the client process, +//! valid if this function returns `true` and sanitization was requested in +//! \a info. +//! \return `true` if \a process_snapshot was successfully created. A message +//! will be logged on failure, but not if the snapshot was skipped because +//! handling was disabled by CrashpadInfoClientOptions. +bool CaptureSnapshot( + PtraceConnection* connection, + const ExceptionHandlerProtocol::ClientInformation& info, + const std::map& process_annotations, + uid_t client_uid, + VMAddress requesting_thread_stack_address, + pid_t* requesting_thread_id, + std::unique_ptr* process_snapshot, + std::unique_ptr* sanitized_snapshot); + +} // namespace crashpad + +#endif // CRASHPAD_HANDLER_LINUX_CAPTURE_SNAPSHOT_H_ diff --git a/shared/sentry/external/crashpad/handler/linux/crash_report_exception_handler.cc b/shared/sentry/external/crashpad/handler/linux/crash_report_exception_handler.cc new file mode 100644 index 000000000..88378a9fa --- /dev/null +++ b/shared/sentry/external/crashpad/handler/linux/crash_report_exception_handler.cc @@ -0,0 +1,306 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "handler/linux/crash_report_exception_handler.h" + +#include +#include + +#include "base/logging.h" +#include "build/build_config.h" +#include "client/settings.h" +#include "handler/linux/capture_snapshot.h" +#include "minidump/minidump_file_writer.h" +#include "snapshot/linux/process_snapshot_linux.h" +#include "snapshot/sanitized/process_snapshot_sanitized.h" +#include "util/file/file_helper.h" +#include "util/file/file_reader.h" +#include "util/file/output_stream_file_writer.h" +#include "util/linux/direct_ptrace_connection.h" +#include "util/linux/ptrace_client.h" +#include "util/misc/implicit_cast.h" +#include "util/misc/metrics.h" +#include "util/misc/uuid.h" +#include "util/stream/base94_output_stream.h" +#include "util/stream/log_output_stream.h" +#include "util/stream/zlib_output_stream.h" + +#if BUILDFLAG(IS_ANDROID) +#include +#endif + +namespace crashpad { +namespace { + +class Logger final : public LogOutputStream::Delegate { + public: + Logger() = default; + + Logger(const Logger&) = delete; + Logger& operator=(const Logger&) = delete; + + ~Logger() override = default; + +#if BUILDFLAG(IS_ANDROID) + int Log(const char* buf) override { + return __android_log_buf_write( + LOG_ID_CRASH, ANDROID_LOG_FATAL, "crashpad", buf); + } + + size_t OutputCap() override { + // Most minidumps are expected to be compressed and encoded into less than + // 128k. + return 128 * 1024; + } + + size_t LineWidth() override { + // From Android NDK r20 , log message text may be truncated + // to less than an implementation-specific limit (1023 bytes), for sake of + // safe and being easy to read in logcat, choose 512. + return 512; + } +#else + // TODO(jperaza): Log to an appropriate location on Linux. + int Log(const char* buf) override { return -ENOTCONN; } + size_t OutputCap() override { return 0; } + size_t LineWidth() override { return 0; } +#endif +}; + +bool WriteMinidumpLogFromFile(FileReaderInterface* file_reader) { + ZlibOutputStream stream( + ZlibOutputStream::Mode::kCompress, + std::make_unique( + Base94OutputStream::Mode::kEncode, + std::make_unique(std::make_unique()))); + FileOperationResult read_result; + do { + uint8_t buffer[4096]; + read_result = file_reader->Read(buffer, sizeof(buffer)); + if (read_result < 0) + return false; + + if (read_result > 0 && (!stream.Write(buffer, read_result))) + return false; + } while (read_result > 0); + return stream.Flush(); +} + +} // namespace + +CrashReportExceptionHandler::CrashReportExceptionHandler( + CrashReportDatabase* database, + CrashReportUploadThread* upload_thread, + const std::map* process_annotations, + const std::vector* attachments, + bool write_minidump_to_database, + bool write_minidump_to_log, + const UserStreamDataSources* user_stream_data_sources) + : database_(database), + upload_thread_(upload_thread), + process_annotations_(process_annotations), + attachments_(attachments), + write_minidump_to_database_(write_minidump_to_database), + write_minidump_to_log_(write_minidump_to_log), + user_stream_data_sources_(user_stream_data_sources) { + DCHECK(write_minidump_to_database_ | write_minidump_to_log_); +} + +CrashReportExceptionHandler::~CrashReportExceptionHandler() = default; + +bool CrashReportExceptionHandler::HandleException( + pid_t client_process_id, + uid_t client_uid, + const ExceptionHandlerProtocol::ClientInformation& info, + VMAddress requesting_thread_stack_address, + pid_t* requesting_thread_id, + UUID* local_report_id) { + Metrics::ExceptionEncountered(); + + DirectPtraceConnection connection; + if (!connection.Initialize(client_process_id)) { + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kDirectPtraceFailed); + return false; + } + + return HandleExceptionWithConnection(&connection, + info, + client_uid, + requesting_thread_stack_address, + requesting_thread_id, + local_report_id); +} + +bool CrashReportExceptionHandler::HandleExceptionWithBroker( + pid_t client_process_id, + uid_t client_uid, + const ExceptionHandlerProtocol::ClientInformation& info, + int broker_sock, + UUID* local_report_id) { + Metrics::ExceptionEncountered(); + + PtraceClient client; + if (!client.Initialize(broker_sock, client_process_id)) { + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kBrokeredPtraceFailed); + return false; + } + + return HandleExceptionWithConnection( + &client, info, client_uid, 0, nullptr, local_report_id); +} + +bool CrashReportExceptionHandler::HandleExceptionWithConnection( + PtraceConnection* connection, + const ExceptionHandlerProtocol::ClientInformation& info, + uid_t client_uid, + VMAddress requesting_thread_stack_address, + pid_t* requesting_thread_id, + UUID* local_report_id) { + std::unique_ptr process_snapshot; + std::unique_ptr sanitized_snapshot; + if (!CaptureSnapshot(connection, + info, + *process_annotations_, + client_uid, + requesting_thread_stack_address, + requesting_thread_id, + &process_snapshot, + &sanitized_snapshot)) { + return false; + } + + UUID client_id; + Settings* const settings = database_->GetSettings(); + if (settings && settings->GetClientID(&client_id)) { + process_snapshot->SetClientID(client_id); + } + + return write_minidump_to_database_ + ? WriteMinidumpToDatabase(process_snapshot.get(), + sanitized_snapshot.get(), + write_minidump_to_log_, + local_report_id) + : WriteMinidumpToLog(process_snapshot.get(), + sanitized_snapshot.get()); +} + +bool CrashReportExceptionHandler::WriteMinidumpToDatabase( + ProcessSnapshotLinux* process_snapshot, + ProcessSnapshotSanitized* sanitized_snapshot, + bool write_minidump_to_log, + UUID* local_report_id) { + std::unique_ptr new_report; + CrashReportDatabase::OperationStatus database_status = + database_->PrepareNewCrashReport(&new_report); + if (database_status != CrashReportDatabase::kNoError) { + LOG(ERROR) << "PrepareNewCrashReport failed"; + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kPrepareNewCrashReportFailed); + return false; + } + + process_snapshot->SetReportID(new_report->ReportID()); + + ProcessSnapshot* snapshot = + sanitized_snapshot ? implicit_cast(sanitized_snapshot) + : implicit_cast(process_snapshot); + + MinidumpFileWriter minidump; + minidump.InitializeFromSnapshot(snapshot); + AddUserExtensionStreams(user_stream_data_sources_, snapshot, &minidump); + + if (!minidump.WriteEverything(new_report->Writer())) { + LOG(ERROR) << "WriteEverything failed"; + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kMinidumpWriteFailed); + return false; + } + + bool write_minidump_to_log_succeed = false; + if (write_minidump_to_log) { + if (auto* file_reader = new_report->Reader()) { + if (WriteMinidumpLogFromFile(file_reader)) + write_minidump_to_log_succeed = true; + else + LOG(ERROR) << "WriteMinidumpLogFromFile failed"; + } + } + + for (const auto& attachment : (*attachments_)) { + FileReader file_reader; + if (!file_reader.Open(attachment)) { + LOG(ERROR) << "attachment " << attachment.value().c_str() + << " couldn't be opened, skipping"; + continue; + } + + base::FilePath filename = attachment.BaseName(); + FileWriter* file_writer = new_report->AddAttachment(filename.value()); + if (file_writer == nullptr) { + LOG(ERROR) << "attachment " << filename.value().c_str() + << " couldn't be created, skipping"; + continue; + } + + CopyFileContent(&file_reader, file_writer); + } + + UUID uuid; + database_status = + database_->FinishedWritingCrashReport(std::move(new_report), &uuid); + if (database_status != CrashReportDatabase::kNoError) { + LOG(ERROR) << "FinishedWritingCrashReport failed"; + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kFinishedWritingCrashReportFailed); + return false; + } + + if (upload_thread_) { + upload_thread_->ReportPending(uuid); + } + + if (local_report_id != nullptr) { + *local_report_id = uuid; + } + + Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSuccess); + + return write_minidump_to_log ? write_minidump_to_log_succeed : true; +} + +bool CrashReportExceptionHandler::WriteMinidumpToLog( + ProcessSnapshotLinux* process_snapshot, + ProcessSnapshotSanitized* sanitized_snapshot) { + ProcessSnapshot* snapshot = + sanitized_snapshot ? implicit_cast(sanitized_snapshot) + : implicit_cast(process_snapshot); + MinidumpFileWriter minidump; + minidump.InitializeFromSnapshot(snapshot); + AddUserExtensionStreams(user_stream_data_sources_, snapshot, &minidump); + + OutputStreamFileWriter writer(std::make_unique( + ZlibOutputStream::Mode::kCompress, + std::make_unique( + Base94OutputStream::Mode::kEncode, + std::make_unique(std::make_unique())))); + if (!minidump.WriteMinidump(&writer, false /* allow_seek */)) { + LOG(ERROR) << "WriteMinidump failed"; + return false; + } + return writer.Flush(); +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/handler/linux/crash_report_exception_handler.h b/shared/sentry/external/crashpad/handler/linux/crash_report_exception_handler.h new file mode 100644 index 000000000..76fce92fb --- /dev/null +++ b/shared/sentry/external/crashpad/handler/linux/crash_report_exception_handler.h @@ -0,0 +1,123 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_HANDLER_LINUX_CRASH_REPORT_EXCEPTION_HANDLER_H_ +#define CRASHPAD_HANDLER_LINUX_CRASH_REPORT_EXCEPTION_HANDLER_H_ + +#include +#include + +#include "client/crash_report_database.h" +#include "handler/crash_report_upload_thread.h" +#include "handler/linux/exception_handler_server.h" +#include "handler/user_stream_data_source.h" +#include "util/linux/exception_handler_protocol.h" +#include "util/linux/ptrace_connection.h" +#include "util/misc/address_types.h" +#include "util/misc/uuid.h" + +namespace crashpad { + +class ProcessSnapshotLinux; +class ProcessSnapshotSanitized; + +//! \brief An exception handler that writes crash reports for exceptions +//! to a CrashReportDatabase. +class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate { + public: + //! \brief Creates a new object that will store crash reports in \a database. + //! + //! \param[in] database The database to store crash reports in. Weak. + //! \param[in] upload_thread The upload thread to notify when a new crash + //! report is written into \a database. Report upload is skipped if this + //! value is `nullptr`. + //! \param[in] process_annotations A map of annotations to insert as + //! process-level annotations into each crash report that is written. Do + //! not confuse this with module-level annotations, which are under the + //! control of the crashing process, and are used to implement Chrome’s + //! “crash keys.†Process-level annotations are those that are beyond the + //! control of the crashing process, which must reliably be set even if + //! the process crashes before it’s able to establish its own annotations. + //! To interoperate with Breakpad servers, the recommended practice is to + //! specify values for the `"prod"` and `"ver"` keys as process + //! annotations. + //! \param[in] attachments A vector of file paths that should be captured with + //! each report at the time of the crash. + //! \param[in] write_minidump_to_database Whether the minidump shall be + //! written to database. + //! \param[in] write_minidump_to_log Whether the minidump shall be written to + //! log. + //! \param[in] user_stream_data_sources Data sources to be used to extend + //! crash reports. For each crash report that is written, the data sources + //! are called in turn. These data sources may contribute additional + //! minidump streams. `nullptr` if not required. + CrashReportExceptionHandler( + CrashReportDatabase* database, + CrashReportUploadThread* upload_thread, + const std::map* process_annotations, + const std::vector* attachments, + bool write_minidump_to_database, + bool write_minidump_to_log, + const UserStreamDataSources* user_stream_data_sources); + + CrashReportExceptionHandler(const CrashReportExceptionHandler&) = delete; + CrashReportExceptionHandler& operator=(const CrashReportExceptionHandler&) = + delete; + + ~CrashReportExceptionHandler() override; + + // ExceptionHandlerServer::Delegate: + + bool HandleException(pid_t client_process_id, + uid_t client_uid, + const ExceptionHandlerProtocol::ClientInformation& info, + VMAddress requesting_thread_stack_address = 0, + pid_t* requesting_thread_id = nullptr, + UUID* local_report_id = nullptr) override; + + bool HandleExceptionWithBroker( + pid_t client_process_id, + uid_t client_uid, + const ExceptionHandlerProtocol::ClientInformation& info, + int broker_sock, + UUID* local_report_id = nullptr) override; + + private: + bool HandleExceptionWithConnection( + PtraceConnection* connection, + const ExceptionHandlerProtocol::ClientInformation& info, + uid_t client_uid, + VMAddress requesting_thread_stack_address, + pid_t* requesting_thread_id, + UUID* local_report_id = nullptr); + + bool WriteMinidumpToDatabase(ProcessSnapshotLinux* process_snapshot, + ProcessSnapshotSanitized* sanitized_snapshot, + bool write_minidump_to_log, + UUID* local_report_id); + bool WriteMinidumpToLog(ProcessSnapshotLinux* process_snapshot, + ProcessSnapshotSanitized* sanitized_snapshot); + + CrashReportDatabase* database_; // weak + CrashReportUploadThread* upload_thread_; // weak + const std::map* process_annotations_; // weak + const std::vector* attachments_; // weak + bool write_minidump_to_database_; + bool write_minidump_to_log_; + const UserStreamDataSources* user_stream_data_sources_; // weak +}; + +} // namespace crashpad + +#endif // CRASHPAD_HANDLER_LINUX_CRASH_REPORT_EXCEPTION_HANDLER_H_ diff --git a/shared/sentry/external/crashpad/handler/linux/cros_crash_report_exception_handler.cc b/shared/sentry/external/crashpad/handler/linux/cros_crash_report_exception_handler.cc new file mode 100644 index 000000000..9e58d94aa --- /dev/null +++ b/shared/sentry/external/crashpad/handler/linux/cros_crash_report_exception_handler.cc @@ -0,0 +1,289 @@ +// Copyright 2019 The Crashpad Authors. All rights reserved. +// +// 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 "handler/linux/cros_crash_report_exception_handler.h" + +#include + +#include "base/logging.h" +#include "client/settings.h" +#include "handler/linux/capture_snapshot.h" +#include "handler/minidump_to_upload_parameters.h" +#include "minidump/minidump_file_writer.h" +#include "snapshot/linux/process_snapshot_linux.h" +#include "snapshot/minidump/process_snapshot_minidump.h" +#include "snapshot/sanitized/process_snapshot_sanitized.h" +#include "util/file/file_writer.h" +#include "util/linux/direct_ptrace_connection.h" +#include "util/linux/ptrace_client.h" +#include "util/misc/metrics.h" +#include "util/misc/uuid.h" +#include "util/posix/double_fork_and_exec.h" + +namespace crashpad { + +namespace { + +// Returns the process name for a pid. +const std::string GetProcessNameFromPid(pid_t pid) { + // Symlink to process binary is at /proc/###/exe. + std::string link_path = "/proc/" + std::to_string(pid) + "/exe"; + + constexpr int kMaxSize = 4096; + std::unique_ptr buf(new char[kMaxSize]); + ssize_t size = readlink(link_path.c_str(), buf.get(), kMaxSize); + std::string result; + if (size < 0) { + PLOG(ERROR) << "Failed to readlink " << link_path; + } else { + result.assign(buf.get(), size); + size_t last_slash_pos = result.rfind('/'); + if (last_slash_pos != std::string::npos) { + result = result.substr(last_slash_pos + 1); + } + } + return result; +} + +bool WriteAnnotationsAndMinidump( + const std::map& parameters, + MinidumpFileWriter& minidump, + FileWriter& file_writer) { + for (const auto& kv : parameters) { + if (kv.first.find(':') != std::string::npos) { + LOG(ERROR) << "Annotation key cannot have ':' in it " << kv.first; + return false; + } + if (!file_writer.Write(kv.first.c_str(), strlen(kv.first.c_str()))) { + return false; + } + if (!file_writer.Write(":", 1)) { + return false; + } + size_t value_size = strlen(kv.second.c_str()); + std::string value_size_str = std::to_string(value_size); + if (!file_writer.Write(value_size_str.c_str(), value_size_str.size())) { + return false; + } + if (!file_writer.Write(":", 1)) { + return false; + } + if (!file_writer.Write(kv.second.c_str(), strlen(kv.second.c_str()))) { + return false; + } + } + + static constexpr char kMinidumpName[] = + "upload_file_minidump\"; filename=\"dump\":"; + if (!file_writer.Write(kMinidumpName, sizeof(kMinidumpName) - 1)) { + return false; + } + crashpad::FileOffset dump_size_start_offset = file_writer.Seek(0, SEEK_CUR); + if (dump_size_start_offset < 0) { + LOG(ERROR) << "Failed to get minidump size start offset"; + return false; + } + static constexpr char kMinidumpLengthFilling[] = "00000000000000000000:"; + if (!file_writer.Write(kMinidumpLengthFilling, + sizeof(kMinidumpLengthFilling) - 1)) { + return false; + } + crashpad::FileOffset dump_start_offset = file_writer.Seek(0, SEEK_CUR); + if (dump_start_offset < 0) { + LOG(ERROR) << "Failed to get minidump start offset"; + return false; + } + if (!minidump.WriteEverything(&file_writer)) { + return false; + } + crashpad::FileOffset dump_end_offset = file_writer.Seek(0, SEEK_CUR); + if (dump_end_offset < 0) { + LOG(ERROR) << "Failed to get minidump end offset"; + return false; + } + + size_t dump_data_size = dump_end_offset - dump_start_offset; + std::string dump_data_size_str = std::to_string(dump_data_size); + file_writer.Seek(dump_size_start_offset + strlen(kMinidumpLengthFilling) - 1 - + dump_data_size_str.size(), + SEEK_SET); + if (!file_writer.Write(dump_data_size_str.c_str(), + dump_data_size_str.size())) { + return false; + } + return true; +} + +} // namespace + +CrosCrashReportExceptionHandler::CrosCrashReportExceptionHandler( + CrashReportDatabase* database, + const std::map* process_annotations, + const UserStreamDataSources* user_stream_data_sources) + : database_(database), + process_annotations_(process_annotations), + user_stream_data_sources_(user_stream_data_sources), + always_allow_feedback_(false) {} + +CrosCrashReportExceptionHandler::~CrosCrashReportExceptionHandler() = default; + +bool CrosCrashReportExceptionHandler::HandleException( + pid_t client_process_id, + uid_t client_uid, + const ExceptionHandlerProtocol::ClientInformation& info, + VMAddress requesting_thread_stack_address, + pid_t* requesting_thread_id, + UUID* local_report_id) { + Metrics::ExceptionEncountered(); + + DirectPtraceConnection connection; + if (!connection.Initialize(client_process_id)) { + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kDirectPtraceFailed); + return false; + } + + return HandleExceptionWithConnection(&connection, + info, + client_uid, + requesting_thread_stack_address, + requesting_thread_id, + local_report_id); +} + +bool CrosCrashReportExceptionHandler::HandleExceptionWithBroker( + pid_t client_process_id, + uid_t client_uid, + const ExceptionHandlerProtocol::ClientInformation& info, + int broker_sock, + UUID* local_report_id) { + Metrics::ExceptionEncountered(); + + PtraceClient client; + if (!client.Initialize(broker_sock, client_process_id)) { + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kBrokeredPtraceFailed); + return false; + } + + return HandleExceptionWithConnection( + &client, info, client_uid, 0, nullptr, local_report_id); +} + +bool CrosCrashReportExceptionHandler::HandleExceptionWithConnection( + PtraceConnection* connection, + const ExceptionHandlerProtocol::ClientInformation& info, + uid_t client_uid, + VMAddress requesting_thread_stack_address, + pid_t* requesting_thread_id, + UUID* local_report_id) { + std::unique_ptr process_snapshot; + std::unique_ptr sanitized_snapshot; + if (!CaptureSnapshot(connection, + info, + *process_annotations_, + client_uid, + requesting_thread_stack_address, + requesting_thread_id, + &process_snapshot, + &sanitized_snapshot)) { + return false; + } + + UUID client_id; + Settings* const settings = database_->GetSettings(); + if (settings) { + // If GetSettings() or GetClientID() fails, something else will log a + // message and client_id will be left at its default value, all zeroes, + // which is appropriate. + settings->GetClientID(&client_id); + } + process_snapshot->SetClientID(client_id); + + UUID uuid; + uuid.InitializeWithNew(); + process_snapshot->SetReportID(uuid); + + ProcessSnapshot* snapshot = + sanitized_snapshot + ? implicit_cast(sanitized_snapshot.get()) + : implicit_cast(process_snapshot.get()); + + MinidumpFileWriter minidump; + minidump.InitializeFromSnapshot(snapshot); + AddUserExtensionStreams(user_stream_data_sources_, snapshot, &minidump); + + FileWriter file_writer; + if (!file_writer.OpenMemfd(base::FilePath("minidump"))) { + Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kOpenMemfdFailed); + return false; + } + + std::map parameters = + BreakpadHTTPFormParametersFromMinidump(snapshot); + // Used to differentiate between breakpad and crashpad while the switch is + // ramping up. + parameters.emplace("crash_library", "crashpad"); + + if (!WriteAnnotationsAndMinidump(parameters, minidump, file_writer)) { + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kMinidumpWriteFailed); + return false; + } + + // CrOS uses crash_reporter instead of Crashpad to report crashes. + // crash_reporter needs to know the pid and uid of the crashing process. + std::vector argv({"/sbin/crash_reporter"}); + + argv.push_back("--chrome_memfd=" + std::to_string(file_writer.fd())); + + const pid_t pid = process_snapshot->ProcessID(); + argv.push_back("--pid=" + std::to_string(pid)); + argv.push_back("--uid=" + std::to_string(client_uid)); + + std::string process_name = GetProcessNameFromPid(pid); + argv.push_back("--exe=" + (process_name.empty() ? "chrome" : process_name)); + + if (info.crash_loop_before_time != 0) { + argv.push_back("--crash_loop_before=" + + std::to_string(info.crash_loop_before_time)); + } + if (!dump_dir_.empty()) { + argv.push_back("--chrome_dump_dir=" + dump_dir_.value()); + } + if (always_allow_feedback_) { + argv.push_back("--always_allow_feedback"); + } + + if (!DoubleForkAndExec(argv, + nullptr /* envp */, + file_writer.fd() /* preserve_fd */, + false /* use_path */, + nullptr /* child_function */)) { + LOG(ERROR) << "DoubleForkAndExec failed"; + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kFinishedWritingCrashReportFailed); + return false; + } + + if (local_report_id != nullptr) { + *local_report_id = uuid; + } + LOG(INFO) << "Successfully wrote report " << uuid.ToString(); + + Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSuccess); + return true; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/handler/linux/cros_crash_report_exception_handler.h b/shared/sentry/external/crashpad/handler/linux/cros_crash_report_exception_handler.h new file mode 100644 index 000000000..eaa6eb165 --- /dev/null +++ b/shared/sentry/external/crashpad/handler/linux/cros_crash_report_exception_handler.h @@ -0,0 +1,103 @@ +// Copyright 2019 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_HANDLER_LINUX_CROS_CRASH_REPORT_EXCEPTION_HANDLER_H_ +#define CRASHPAD_HANDLER_LINUX_CROS_CRASH_REPORT_EXCEPTION_HANDLER_H_ + +#include +#include + +#include "client/crash_report_database.h" +#include "handler/linux/exception_handler_server.h" +#include "handler/user_stream_data_source.h" +#include "util/linux/exception_handler_protocol.h" +#include "util/linux/ptrace_connection.h" +#include "util/misc/address_types.h" +#include "util/misc/uuid.h" + +namespace crashpad { + +//! \brief An exception handler that writes crash reports to the ChromeOS +//! crash_reporter. +class CrosCrashReportExceptionHandler + : public ExceptionHandlerServer::Delegate { + public: + //! \brief Creates a new object that will pass reports to + //! `/sbin/crash_reporter`. + //! + //! \param[in] database The database that supplies settings for this client. + //! This object does not write its reports to this database. + //! \param[in] process_annotations A map of annotations to insert as + //! process-level annotations into each crash report that is written. Do + //! not confuse this with module-level annotations, which are under the + //! control of the crashing process, and are used to implement Chrome’s + //! “crash keys.†Process-level annotations are those that are beyond the + //! control of the crashing process, which must reliably be set even if + //! the process crashes before it’s able to establish its own annotations. + //! To interoperate with Breakpad servers, the recommended practice is to + //! specify values for the `"prod"` and `"ver"` keys as process + //! annotations. + //! \param[in] user_stream_data_sources Data sources to be used to extend + //! crash reports. For each crash report that is written, the data sources + //! are called in turn. These data sources may contribute additional + //! minidump streams. `nullptr` if not required. + CrosCrashReportExceptionHandler( + CrashReportDatabase* database, + const std::map* process_annotations, + const UserStreamDataSources* user_stream_data_sources); + + CrosCrashReportExceptionHandler(const CrosCrashReportExceptionHandler&) = + delete; + CrosCrashReportExceptionHandler& operator=( + const CrosCrashReportExceptionHandler&) = delete; + + ~CrosCrashReportExceptionHandler() override; + + // ExceptionHandlerServer::Delegate: + + bool HandleException(pid_t client_process_id, + uid_t client_uid, + const ExceptionHandlerProtocol::ClientInformation& info, + VMAddress requesting_thread_stack_address = 0, + pid_t* requesting_thread_id = nullptr, + UUID* local_report_id = nullptr) override; + + bool HandleExceptionWithBroker( + pid_t client_process_id, + uid_t client_uid, + const ExceptionHandlerProtocol::ClientInformation& info, + int broker_sock, + UUID* local_report_id = nullptr) override; + + void SetDumpDir(const base::FilePath& dump_dir) { dump_dir_ = dump_dir; } + void SetAlwaysAllowFeedback() { always_allow_feedback_ = true; } + private: + bool HandleExceptionWithConnection( + PtraceConnection* connection, + const ExceptionHandlerProtocol::ClientInformation& info, + uid_t client_uid, + VMAddress requesting_thread_stack_address, + pid_t* requesting_thread_id, + UUID* local_report_id = nullptr); + + CrashReportDatabase* database_; // weak + const std::map* process_annotations_; // weak + const UserStreamDataSources* user_stream_data_sources_; // weak + base::FilePath dump_dir_; + bool always_allow_feedback_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_HANDLER_LINUX_CROS_CRASH_REPORT_EXCEPTION_HANDLER_H_ diff --git a/shared/sentry/external/crashpad/handler/linux/exception_handler_server.cc b/shared/sentry/external/crashpad/handler/linux/exception_handler_server.cc new file mode 100644 index 000000000..aef252fa4 --- /dev/null +++ b/shared/sentry/external/crashpad/handler/linux/exception_handler_server.cc @@ -0,0 +1,493 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "handler/linux/exception_handler_server.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" +#include "base/strings/string_number_conversions.h" +#include "build/build_config.h" +#include "util/file/file_io.h" +#include "util/file/filesystem.h" +#include "util/linux/proc_task_reader.h" +#include "util/linux/socket.h" +#include "util/misc/as_underlying_type.h" + +namespace crashpad { + +namespace { + +// Log an error for a socket after an EPOLLERR. +void LogSocketError(int sock) { + int err; + socklen_t err_len = sizeof(err); + if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &err_len) != 0) { + PLOG(ERROR) << "getsockopt"; + } else { + errno = err; + PLOG(ERROR) << "EPOLLERR"; + } +} + +enum class PtraceScope { + kClassic = 0, + kRestricted, + kAdminOnly, + kNoAttach, + kUnknown +}; + +PtraceScope GetPtraceScope() { + const base::FilePath settings_file("/proc/sys/kernel/yama/ptrace_scope"); + if (!IsRegularFile(base::FilePath(settings_file))) { + return PtraceScope::kClassic; + } + + std::string contents; + if (!LoggingReadEntireFile(settings_file, &contents)) { + return PtraceScope::kUnknown; + } + + if (contents.back() != '\n') { + LOG(ERROR) << "format error"; + return PtraceScope::kUnknown; + } + contents.pop_back(); + + int ptrace_scope; + if (!base::StringToInt(contents, &ptrace_scope)) { + LOG(ERROR) << "format error"; + return PtraceScope::kUnknown; + } + + if (ptrace_scope < static_cast(PtraceScope::kClassic) || + ptrace_scope >= static_cast(PtraceScope::kUnknown)) { + LOG(ERROR) << "invalid ptrace scope"; + return PtraceScope::kUnknown; + } + + return static_cast(ptrace_scope); +} + +bool HaveCapSysPtrace() { + __user_cap_header_struct cap_header; + cap_header.pid = getpid(); + cap_header.version = _LINUX_CAPABILITY_VERSION_3; + + __user_cap_data_struct cap_data[_LINUX_CAPABILITY_U32S_3]; + if (syscall(SYS_capget, &cap_header, &cap_data) != 0) { + PLOG(ERROR) << "capget"; + LOG_IF(ERROR, errno == EINVAL) << "cap_header.version " << std::hex + << cap_header.version; + return false; + } + + return (cap_data[CAP_TO_INDEX(CAP_SYS_PTRACE)].effective & + CAP_TO_MASK(CAP_SYS_PTRACE)) != 0; +} + +bool SendMessageToClient( + int client_sock, + ExceptionHandlerProtocol::ServerToClientMessage::Type type) { + ExceptionHandlerProtocol::ServerToClientMessage message = {}; + message.type = type; + if (type == + ExceptionHandlerProtocol::ServerToClientMessage::kTypeSetPtracer) { + message.pid = getpid(); + } + return LoggingWriteFile(client_sock, &message, sizeof(message)); +} + +int tgkill(pid_t pid, pid_t tid, int signo) { + return syscall(SYS_tgkill, pid, tid, signo); +} + +void SendSIGCONT(pid_t pid, pid_t tid) { + if (tid > 0) { + if (tgkill(pid, tid, ExceptionHandlerProtocol::kDumpDoneSignal) != 0) { + PLOG(ERROR) << "tgkill"; + } + return; + } + + std::vector threads; + if (!ReadThreadIDs(pid, &threads)) { + return; + } + for (const auto& thread : threads) { + if (tgkill(pid, thread, ExceptionHandlerProtocol::kDumpDoneSignal) != 0) { + PLOG(ERROR) << "tgkill"; + } + } +} + +bool SendCredentials(int client_sock) { + ExceptionHandlerProtocol::ServerToClientMessage message = {}; + message.type = + ExceptionHandlerProtocol::ServerToClientMessage::kTypeCredentials; + return UnixCredentialSocket::SendMsg( + client_sock, &message, sizeof(message)) == 0; +} + +class PtraceStrategyDeciderImpl : public PtraceStrategyDecider { + public: + PtraceStrategyDeciderImpl() : PtraceStrategyDecider() {} + ~PtraceStrategyDeciderImpl() = default; + + Strategy ChooseStrategy(int sock, + bool multiple_clients, + const ucred& client_credentials) override { + if (client_credentials.pid <= 0) { + LOG(ERROR) << "invalid credentials"; + return Strategy::kNoPtrace; + } + + switch (GetPtraceScope()) { + case PtraceScope::kClassic: + if (getuid() == client_credentials.uid || HaveCapSysPtrace()) { + return Strategy::kDirectPtrace; + } + return multiple_clients ? Strategy::kNoPtrace : TryForkingBroker(sock); + + case PtraceScope::kRestricted: + if (multiple_clients) { + return Strategy::kDirectPtrace; + } + if (!SendMessageToClient(sock, + ExceptionHandlerProtocol:: + ServerToClientMessage::kTypeSetPtracer)) { + return Strategy::kError; + } + + ExceptionHandlerProtocol::Errno status; + if (!LoggingReadFileExactly(sock, &status, sizeof(status))) { + return Strategy::kError; + } + + if (status != 0) { + errno = status; + PLOG(ERROR) << "Handler Client SetPtracer"; + return TryForkingBroker(sock); + } + return Strategy::kDirectPtrace; + + case PtraceScope::kAdminOnly: + if (HaveCapSysPtrace()) { + return Strategy::kDirectPtrace; + } + [[fallthrough]]; + case PtraceScope::kNoAttach: + LOG(WARNING) << "no ptrace"; + return Strategy::kNoPtrace; + + case PtraceScope::kUnknown: + LOG(WARNING) << "Unknown ptrace scope"; + return Strategy::kError; + } + + DCHECK(false); + return Strategy::kError; + } + + private: + static Strategy TryForkingBroker(int client_sock) { + if (!SendMessageToClient( + client_sock, + ExceptionHandlerProtocol::ServerToClientMessage::kTypeForkBroker)) { + return Strategy::kError; + } + + ExceptionHandlerProtocol::Errno status; + if (!LoggingReadFileExactly(client_sock, &status, sizeof(status))) { + return Strategy::kError; + } + + if (status != 0) { + errno = status; + PLOG(ERROR) << "Handler Client ForkBroker"; + return Strategy::kNoPtrace; + } + return Strategy::kUseBroker; + } +}; + +} // namespace + +ExceptionHandlerServer::ExceptionHandlerServer() + : clients_(), + shutdown_event_(), + strategy_decider_(new PtraceStrategyDeciderImpl()), + delegate_(nullptr), + pollfd_(), + keep_running_(true) {} + +ExceptionHandlerServer::~ExceptionHandlerServer() = default; + +void ExceptionHandlerServer::SetPtraceStrategyDecider( + std::unique_ptr decider) { + strategy_decider_ = std::move(decider); +} + +bool ExceptionHandlerServer::InitializeWithClient(ScopedFileHandle sock, + bool multiple_clients) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + pollfd_.reset(epoll_create1(EPOLL_CLOEXEC)); + if (!pollfd_.is_valid()) { + PLOG(ERROR) << "epoll_create1"; + return false; + } + + shutdown_event_ = std::make_unique(); + shutdown_event_->type = Event::Type::kShutdown; + shutdown_event_->fd.reset(eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)); + if (!shutdown_event_->fd.is_valid()) { + PLOG(ERROR) << "eventfd"; + return false; + } + + epoll_event poll_event; + poll_event.events = EPOLLIN; + poll_event.data.ptr = shutdown_event_.get(); + if (epoll_ctl(pollfd_.get(), + EPOLL_CTL_ADD, + shutdown_event_->fd.get(), + &poll_event) != 0) { + PLOG(ERROR) << "epoll_ctl"; + return false; + } + + if (!InstallClientSocket(std::move(sock), + multiple_clients ? Event::Type::kSharedSocketMessage + : Event::Type::kClientMessage)) { + return false; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +void ExceptionHandlerServer::Run(Delegate* delegate) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + delegate_ = delegate; + + while (keep_running_ && clients_.size() > 0) { + epoll_event poll_event; + int res = HANDLE_EINTR(epoll_wait(pollfd_.get(), &poll_event, 1, -1)); + if (res < 0) { + PLOG(ERROR) << "epoll_wait"; + return; + } + DCHECK_EQ(res, 1); + + Event* eventp = reinterpret_cast(poll_event.data.ptr); + if (eventp->type == Event::Type::kShutdown) { + if (poll_event.events & EPOLLERR) { + LogSocketError(eventp->fd.get()); + } + keep_running_ = false; + } else { + HandleEvent(eventp, poll_event.events); + } + } +} + +void ExceptionHandlerServer::Stop() { + keep_running_ = false; + if (shutdown_event_ && shutdown_event_->fd.is_valid()) { + uint64_t value = 1; + LoggingWriteFile(shutdown_event_->fd.get(), &value, sizeof(value)); + } +} + +void ExceptionHandlerServer::HandleEvent(Event* event, uint32_t event_type) { + DCHECK_NE(AsUnderlyingType(event->type), + AsUnderlyingType(Event::Type::kShutdown)); + + if (event_type & EPOLLERR) { + LogSocketError(event->fd.get()); + UninstallClientSocket(event); + return; + } + + if (event_type & EPOLLIN) { + if (!ReceiveClientMessage(event)) { + UninstallClientSocket(event); + } + return; + } + + if (event_type & EPOLLHUP || event_type & EPOLLRDHUP) { + UninstallClientSocket(event); + return; + } + + LOG(ERROR) << "Unexpected event 0x" << std::hex << event_type; + return; +} + +bool ExceptionHandlerServer::InstallClientSocket(ScopedFileHandle socket, + Event::Type type) { + // The handler may not have permission to set SO_PASSCRED on the socket, but + // it doesn't need to if the client has already set it. + // https://bugs.chromium.org/p/crashpad/issues/detail?id=252 + int optval; + socklen_t optlen = sizeof(optval); + if (getsockopt(socket.get(), SOL_SOCKET, SO_PASSCRED, &optval, &optlen) != + 0) { + PLOG(ERROR) << "getsockopt"; + return false; + } + if (!optval) { + optval = 1; + optlen = sizeof(optval); + if (setsockopt(socket.get(), SOL_SOCKET, SO_PASSCRED, &optval, optlen) != + 0) { + PLOG(ERROR) << "setsockopt"; + return false; + } + } + + auto event = std::make_unique(); + event->type = type; + event->fd.reset(socket.release()); + + Event* eventp = event.get(); + + if (!clients_.insert(std::make_pair(event->fd.get(), std::move(event))) + .second) { + LOG(ERROR) << "duplicate descriptor"; + return false; + } + + epoll_event poll_event; + poll_event.events = EPOLLIN | EPOLLRDHUP; + poll_event.data.ptr = eventp; + + if (epoll_ctl(pollfd_.get(), EPOLL_CTL_ADD, eventp->fd.get(), &poll_event) != + 0) { + PLOG(ERROR) << "epoll_ctl"; + clients_.erase(eventp->fd.get()); + return false; + } + + return true; +} + +bool ExceptionHandlerServer::UninstallClientSocket(Event* event) { + if (epoll_ctl(pollfd_.get(), EPOLL_CTL_DEL, event->fd.get(), nullptr) != 0) { + PLOG(ERROR) << "epoll_ctl"; + return false; + } + + if (clients_.erase(event->fd.get()) != 1) { + LOG(ERROR) << "event not found"; + return false; + } + + return true; +} + +bool ExceptionHandlerServer::ReceiveClientMessage(Event* event) { + ExceptionHandlerProtocol::ClientToServerMessage message; + ucred creds; + if (!UnixCredentialSocket::RecvMsg( + event->fd.get(), &message, sizeof(message), &creds)) { + return false; + } + + switch (message.type) { + case ExceptionHandlerProtocol::ClientToServerMessage::kTypeCheckCredentials: + return SendCredentials(event->fd.get()); + + case ExceptionHandlerProtocol::ClientToServerMessage::kTypeCrashDumpRequest: + return HandleCrashDumpRequest( + creds, + message.client_info, + message.requesting_thread_stack_address, + event->fd.get(), + event->type == Event::Type::kSharedSocketMessage); + } + + DCHECK(false); + LOG(ERROR) << "Unknown message type"; + return false; +} + +bool ExceptionHandlerServer::HandleCrashDumpRequest( + const ucred& creds, + const ExceptionHandlerProtocol::ClientInformation& client_info, + VMAddress requesting_thread_stack_address, + int client_sock, + bool multiple_clients) { + pid_t client_process_id = creds.pid; + pid_t requesting_thread_id = -1; + uid_t client_uid = creds.uid; + + switch ( + strategy_decider_->ChooseStrategy(client_sock, multiple_clients, creds)) { + case PtraceStrategyDecider::Strategy::kError: + if (multiple_clients) { + SendSIGCONT(client_process_id, requesting_thread_id); + } + return false; + + case PtraceStrategyDecider::Strategy::kNoPtrace: + if (multiple_clients) { + SendSIGCONT(client_process_id, requesting_thread_id); + return true; + } + return SendMessageToClient( + client_sock, + ExceptionHandlerProtocol::ServerToClientMessage:: + kTypeCrashDumpFailed); + + case PtraceStrategyDecider::Strategy::kDirectPtrace: { + delegate_->HandleException(client_process_id, + client_uid, + client_info, + requesting_thread_stack_address, + &requesting_thread_id); + if (multiple_clients) { + SendSIGCONT(client_process_id, requesting_thread_id); + return true; + } + break; + } + + case PtraceStrategyDecider::Strategy::kUseBroker: + DCHECK(!multiple_clients); + delegate_->HandleExceptionWithBroker( + client_process_id, client_uid, client_info, client_sock); + break; + } + + return SendMessageToClient( + client_sock, + ExceptionHandlerProtocol::ServerToClientMessage::kTypeCrashDumpComplete); +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/handler/linux/exception_handler_server.h b/shared/sentry/external/crashpad/handler/linux/exception_handler_server.h new file mode 100644 index 000000000..60bf496d9 --- /dev/null +++ b/shared/sentry/external/crashpad/handler/linux/exception_handler_server.h @@ -0,0 +1,197 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_HANDLER_LINUX_EXCEPTION_HANDLER_SERVER_H_ +#define CRASHPAD_HANDLER_LINUX_EXCEPTION_HANDLER_SERVER_H_ + +#include +#include + +#include +#include +#include + +#include "util/file/file_io.h" +#include "util/linux/exception_handler_protocol.h" +#include "util/misc/address_types.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/misc/uuid.h" + +namespace crashpad { + +//! \brief Abstract base class for deciding how the handler should `ptrace` a +//! client. +class PtraceStrategyDecider { + public: + virtual ~PtraceStrategyDecider() = default; + + //! \brief The possible return values for ChooseStrategy(). + enum class Strategy { + //! \brief An error occurred, with a message logged. + kError, + + //! \brief Ptrace cannot be used. + kNoPtrace, + + //! \brief The handler should `ptrace`-attach the client directly. + kDirectPtrace, + + //! \brief The client has `fork`ed a PtraceBroker for the handler. + kUseBroker, + }; + + //! \brief Chooses an appropriate `ptrace` strategy. + //! + //! \param[in] sock A socket conncted to a ExceptionHandlerClient. + //! \param[in] multiple_clients `true` if the socket is connected to multiple + //! clients. The broker is not supported in this configuration. + //! \param[in] client_credentials The credentials for the connected client. + //! \return the chosen #Strategy. + virtual Strategy ChooseStrategy(int sock, + bool multiple_clients, + const ucred& client_credentials) = 0; + + protected: + PtraceStrategyDecider() = default; +}; + +//! \brief Runs the main exception-handling server in Crashpad’s handler +//! process. +class ExceptionHandlerServer { + public: + class Delegate { + public: + //! \brief Called on receipt of a crash dump request from a client. + //! + //! \param[in] client_process_id The process ID of the crashing client. + //! \param[in] client_uid The user ID of the crashing client. + //! \param[in] info Information on the client. + //! \param[in] requesting_thread_stack_address Any address within the stack + //! range for the the thread that sent the crash dump request. Optional. + //! If unspecified or 0, \a requesting_thread_id will be -1. + //! \param[out] requesting_thread_id The thread ID of the thread which + //! requested the crash dump if not `nullptr`. Set to -1 if the thread + //! ID could not be determined. Optional. + //! \param[out] local_report_id The unique identifier for the report created + //! in the local report database. Optional. + //! \return `true` on success. `false` on failure with a message logged. + virtual bool HandleException( + pid_t client_process_id, + uid_t client_uid, + const ExceptionHandlerProtocol::ClientInformation& info, + VMAddress requesting_thread_stack_address = 0, + pid_t* requesting_thread_id = nullptr, + UUID* local_report_id = nullptr) = 0; + + //! \brief Called on the receipt of a crash dump request from a client for a + //! crash that should be mediated by a PtraceBroker. + //! + //! \param[in] client_process_id The process ID of the crashing client. + //! \param[in] client_uid The uid of the crashing client. + //! \param[in] info Information on the client. + //! \param[in] broker_sock A socket connected to the PtraceBroker. + //! \param[out] local_report_id The unique identifier for the report created + //! in the local report database. Optional. + //! \return `true` on success. `false` on failure with a message logged. + virtual bool HandleExceptionWithBroker( + pid_t client_process_id, + uid_t client_uid, + const ExceptionHandlerProtocol::ClientInformation& info, + int broker_sock, + UUID* local_report_id = nullptr) = 0; + + virtual ~Delegate() {} + }; + + ExceptionHandlerServer(); + + ExceptionHandlerServer(const ExceptionHandlerServer&) = delete; + ExceptionHandlerServer& operator=(const ExceptionHandlerServer&) = delete; + + ~ExceptionHandlerServer(); + + //! \brief Sets the handler's PtraceStrategyDecider. + //! + //! If this method is not called, a default PtraceStrategyDecider will be + //! used. + void SetPtraceStrategyDecider(std::unique_ptr decider); + + //! \brief Initializes this object. + //! + //! This method must be successfully called before Run(). + //! + //! \param[in] sock A socket on which to receive client requests. + //! \param[in] multiple_clients `true` if this socket is used by multiple + //! clients. Using a broker process is not supported in this + //! configuration. + //! \return `true` on success. `false` on failure with a message logged. + bool InitializeWithClient(ScopedFileHandle sock, bool multiple_clients); + + //! \brief Runs the exception-handling server. + //! + //! This method must only be called once on an ExceptionHandlerServer object. + //! This method returns when there are no more client connections or Stop() + //! has been called. + //! + //! \param[in] delegate An object to send exceptions to. + void Run(Delegate* delegate); + + //! \brief Stops a running exception-handling server. + //! + //! Stop() may be called at any time, and may be called from a signal handler. + //! If Stop() is called before Run() it will cause Run() to return as soon as + //! it is called. It is harmless to call Stop() after Run() has already + //! returned, or to call Stop() after it has already been called. + void Stop(); + + private: + struct Event { + enum class Type { + // Used by Stop() to shutdown the server. + kShutdown, + + // A message from a client on a private socket connection. + kClientMessage, + + // A message from a client on a shared socket connection. + kSharedSocketMessage + }; + + Type type; + ScopedFileHandle fd; + }; + + void HandleEvent(Event* event, uint32_t event_type); + bool InstallClientSocket(ScopedFileHandle socket, Event::Type type); + bool UninstallClientSocket(Event* event); + bool ReceiveClientMessage(Event* event); + bool HandleCrashDumpRequest( + const ucred& creds, + const ExceptionHandlerProtocol::ClientInformation& client_info, + VMAddress requesting_thread_stack_address, + int client_sock, + bool multiple_clients); + + std::unordered_map> clients_; + std::unique_ptr shutdown_event_; + std::unique_ptr strategy_decider_; + Delegate* delegate_; + ScopedFileHandle pollfd_; + std::atomic keep_running_; + InitializationStateDcheck initialized_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_HANDLER_LINUX_EXCEPTION_HANDLER_SERVER_H_ diff --git a/shared/sentry/external/crashpad/handler/linux/exception_handler_server_test.cc b/shared/sentry/external/crashpad/handler/linux/exception_handler_server_test.cc new file mode 100644 index 000000000..c7fa90d89 --- /dev/null +++ b/shared/sentry/external/crashpad/handler/linux/exception_handler_server_test.cc @@ -0,0 +1,383 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "handler/linux/exception_handler_server.h" + +#include +#include + +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "snapshot/linux/process_snapshot_linux.h" +#include "test/errors.h" +#include "test/multiprocess.h" +#include "util/linux/direct_ptrace_connection.h" +#include "util/linux/exception_handler_client.h" +#include "util/linux/ptrace_client.h" +#include "util/linux/scoped_pr_set_ptracer.h" +#include "util/misc/uuid.h" +#include "util/synchronization/semaphore.h" +#include "util/thread/thread.h" + +#if BUILDFLAG(IS_ANDROID) +#include +#endif + +namespace crashpad { +namespace test { +namespace { + +// Runs the ExceptionHandlerServer on a background thread. +class RunServerThread : public Thread { + public: + RunServerThread(ExceptionHandlerServer* server, + ExceptionHandlerServer::Delegate* delegate) + : server_(server), delegate_(delegate), join_sem_(0) {} + + RunServerThread(const RunServerThread&) = delete; + RunServerThread& operator=(const RunServerThread&) = delete; + + ~RunServerThread() override {} + + bool JoinWithTimeout(double timeout) { + if (!join_sem_.TimedWait(timeout)) { + return false; + } + Join(); + return true; + } + + private: + // Thread: + void ThreadMain() override { + server_->Run(delegate_); + join_sem_.Signal(); + } + + ExceptionHandlerServer* server_; + ExceptionHandlerServer::Delegate* delegate_; + Semaphore join_sem_; +}; + +class ScopedStopServerAndJoinThread { + public: + ScopedStopServerAndJoinThread(ExceptionHandlerServer* server, + RunServerThread* thread) + : server_(server), thread_(thread) {} + + ScopedStopServerAndJoinThread(const ScopedStopServerAndJoinThread&) = delete; + ScopedStopServerAndJoinThread& operator=( + const ScopedStopServerAndJoinThread&) = delete; + + ~ScopedStopServerAndJoinThread() { + server_->Stop(); + EXPECT_TRUE(thread_->JoinWithTimeout(5.0)); + } + + private: + ExceptionHandlerServer* server_; + RunServerThread* thread_; +}; + +class TestDelegate : public ExceptionHandlerServer::Delegate { + public: + TestDelegate() + : Delegate(), last_exception_address_(0), last_client_(-1), sem_(0) {} + + TestDelegate(const TestDelegate&) = delete; + TestDelegate& operator=(const TestDelegate&) = delete; + + ~TestDelegate() {} + + bool WaitForException(double timeout_seconds, + pid_t* last_client, + VMAddress* last_address) { + if (sem_.TimedWait(timeout_seconds)) { + *last_client = last_client_; + *last_address = last_exception_address_; + return true; + } + + return false; + } + + bool HandleException(pid_t client_process_id, + uid_t client_uid, + const ExceptionHandlerProtocol::ClientInformation& info, + VMAddress requesting_thread_stack_address, + pid_t* requesting_thread_id = nullptr, + UUID* local_report_id = nullptr) override { + DirectPtraceConnection connection; + bool connected = connection.Initialize(client_process_id); + EXPECT_TRUE(connected); + + last_exception_address_ = info.exception_information_address; + last_client_ = client_process_id; + sem_.Signal(); + if (!connected) { + return false; + } + + if (requesting_thread_id) { + if (requesting_thread_stack_address) { + ProcessSnapshotLinux process_snapshot; + if (!process_snapshot.Initialize(&connection)) { + ADD_FAILURE(); + return false; + } + *requesting_thread_id = process_snapshot.FindThreadWithStackAddress( + requesting_thread_stack_address); + } else { + *requesting_thread_id = -1; + } + } + return true; + } + + bool HandleExceptionWithBroker( + pid_t client_process_id, + uid_t client_uid, + const ExceptionHandlerProtocol::ClientInformation& info, + int broker_sock, + UUID* local_report_id = nullptr) override { + PtraceClient client; + bool connected = client.Initialize(broker_sock, client_process_id); + EXPECT_TRUE(connected); + + last_exception_address_ = info.exception_information_address, + last_client_ = client_process_id; + sem_.Signal(); + return connected; + } + + private: + VMAddress last_exception_address_; + pid_t last_client_; + Semaphore sem_; +}; + +class MockPtraceStrategyDecider : public PtraceStrategyDecider { + public: + MockPtraceStrategyDecider(PtraceStrategyDecider::Strategy strategy) + : PtraceStrategyDecider(), strategy_(strategy) {} + + MockPtraceStrategyDecider(const MockPtraceStrategyDecider&) = delete; + MockPtraceStrategyDecider& operator=(const MockPtraceStrategyDecider&) = + delete; + + ~MockPtraceStrategyDecider() {} + + Strategy ChooseStrategy(int sock, + bool multiple_clients, + const ucred& client_credentials) override { + if (strategy_ == Strategy::kUseBroker) { + ExceptionHandlerProtocol::ServerToClientMessage message = {}; + message.type = + ExceptionHandlerProtocol::ServerToClientMessage::kTypeForkBroker; + + ExceptionHandlerProtocol::Errno status; + bool result = LoggingWriteFile(sock, &message, sizeof(message)) && + LoggingReadFileExactly(sock, &status, sizeof(status)); + EXPECT_TRUE(result); + + if (!result) { + return Strategy::kError; + } + + if (status != 0) { + errno = status; + ADD_FAILURE() << ErrnoMessage("Handler Client ForkBroker"); + return Strategy::kNoPtrace; + } + } + return strategy_; + } + + private: + Strategy strategy_; +}; + +class ExceptionHandlerServerTest : public testing::TestWithParam { + public: + ExceptionHandlerServerTest() + : server_(), + delegate_(), + server_thread_(&server_, &delegate_), + sock_to_handler_(), + use_multi_client_socket_(GetParam()) {} + + ExceptionHandlerServerTest(const ExceptionHandlerServerTest&) = delete; + ExceptionHandlerServerTest& operator=(const ExceptionHandlerServerTest&) = + delete; + + ~ExceptionHandlerServerTest() = default; + + int SockToHandler() { return sock_to_handler_.get(); } + + TestDelegate* Delegate() { return &delegate_; } + + void Hangup() { sock_to_handler_.reset(); } + + RunServerThread* ServerThread() { return &server_thread_; } + + ExceptionHandlerServer* Server() { return &server_; } + + class CrashDumpTest : public Multiprocess { + public: + CrashDumpTest(ExceptionHandlerServerTest* server_test, bool succeeds) + : Multiprocess(), server_test_(server_test), succeeds_(succeeds) {} + + CrashDumpTest(const CrashDumpTest&) = delete; + CrashDumpTest& operator=(const CrashDumpTest&) = delete; + + ~CrashDumpTest() = default; + + void MultiprocessParent() override { + ExceptionHandlerProtocol::ClientInformation info; + ASSERT_TRUE( + LoggingReadFileExactly(ReadPipeHandle(), &info, sizeof(info))); + + if (succeeds_) { + VMAddress last_address; + pid_t last_client; + ASSERT_TRUE(server_test_->Delegate()->WaitForException( + 5.0, &last_client, &last_address)); + EXPECT_EQ(last_address, info.exception_information_address); + EXPECT_EQ(last_client, ChildPID()); + } else { + CheckedReadFileAtEOF(ReadPipeHandle()); + } + } + + void MultiprocessChild() override { + ASSERT_EQ(close(server_test_->sock_to_client_), 0); + + ExceptionHandlerProtocol::ClientInformation info; + info.exception_information_address = 42; + ASSERT_TRUE(LoggingWriteFile(WritePipeHandle(), &info, sizeof(info))); + + // If the current ptrace_scope is restricted, the broker needs to be set + // as the ptracer for this process. Setting this process as its own + // ptracer allows the broker to inherit this condition. + ScopedPrSetPtracer set_ptracer(getpid(), /* may_log= */ true); + + ExceptionHandlerClient client(server_test_->SockToHandler(), + server_test_->use_multi_client_socket_); + ASSERT_EQ(client.RequestCrashDump(info), 0); + } + + private: + ExceptionHandlerServerTest* server_test_; + bool succeeds_; + }; + + void ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy strategy, + bool succeeds) { + Server()->SetPtraceStrategyDecider( + std::make_unique(strategy)); + + ScopedStopServerAndJoinThread stop_server(Server(), ServerThread()); + ServerThread()->Start(); + + CrashDumpTest test(this, succeeds); + test.Run(); + } + + bool UsingMultiClientSocket() const { return use_multi_client_socket_; } + + protected: + void SetUp() override { + int socks[2]; + ASSERT_EQ(socketpair(AF_UNIX, SOCK_STREAM, 0, socks), 0); + sock_to_handler_.reset(socks[0]); + sock_to_client_ = socks[1]; + + ASSERT_TRUE(server_.InitializeWithClient(ScopedFileHandle(socks[1]), + use_multi_client_socket_)); + } + + private: + ExceptionHandlerServer server_; + TestDelegate delegate_; + RunServerThread server_thread_; + ScopedFileHandle sock_to_handler_; + int sock_to_client_; + bool use_multi_client_socket_; +}; + +TEST_P(ExceptionHandlerServerTest, ShutdownWithNoClients) { + ServerThread()->Start(); + Hangup(); + ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0)); +} + +TEST_P(ExceptionHandlerServerTest, StopWithClients) { + ServerThread()->Start(); + Server()->Stop(); + ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0)); +} + +TEST_P(ExceptionHandlerServerTest, StopBeforeRun) { + Server()->Stop(); + ServerThread()->Start(); + ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0)); +} + +TEST_P(ExceptionHandlerServerTest, MultipleStops) { + ServerThread()->Start(); + Server()->Stop(); + Server()->Stop(); + ASSERT_TRUE(ServerThread()->JoinWithTimeout(5.0)); +} + +TEST_P(ExceptionHandlerServerTest, RequestCrashDumpDefault) { + ScopedStopServerAndJoinThread stop_server(Server(), ServerThread()); + ServerThread()->Start(); + + CrashDumpTest test(this, true); + test.Run(); +} + +TEST_P(ExceptionHandlerServerTest, RequestCrashDumpNoPtrace) { + ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kNoPtrace, + false); +} + +TEST_P(ExceptionHandlerServerTest, RequestCrashDumpForkBroker) { + if (UsingMultiClientSocket()) { + // The broker is not supported with multiple clients connected on a single + // socket. + return; + } + ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kUseBroker, + true); +} + +TEST_P(ExceptionHandlerServerTest, RequestCrashDumpDirectPtrace) { + ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kDirectPtrace, + true); +} + +TEST_P(ExceptionHandlerServerTest, RequestCrashDumpError) { + ExpectCrashDumpUsingStrategy(PtraceStrategyDecider::Strategy::kError, false); +} + +INSTANTIATE_TEST_SUITE_P(ExceptionHandlerServerTestSuite, + ExceptionHandlerServerTest, + testing::Bool() +); + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/handler/linux/handler_trampoline.cc b/shared/sentry/external/crashpad/handler/linux/handler_trampoline.cc new file mode 100644 index 000000000..1596d77f4 --- /dev/null +++ b/shared/sentry/external/crashpad/handler/linux/handler_trampoline.cc @@ -0,0 +1,47 @@ +// Copyright 2019 The Crashpad Authors. All rights reserved. +// +// 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 +#include +#include + +#include "util/misc/no_cfi_icall.h" + +// The first argument passed to the trampoline is the name of the native library +// exporting the symbol `CrashpadHandlerMain`. The remaining arguments are the +// same as for `HandlerMain()`. +int main(int argc, char* argv[]) { + static constexpr char kTag[] = "crashpad"; + + if (argc < 2) { + __android_log_print(ANDROID_LOG_FATAL, kTag, "usage: %s ", argv[0]); + return EXIT_FAILURE; + } + + void* handle = dlopen(argv[1], RTLD_LAZY | RTLD_GLOBAL); + if (!handle) { + __android_log_print(ANDROID_LOG_FATAL, kTag, "dlopen: %s", dlerror()); + return EXIT_FAILURE; + } + + using MainType = int (*)(int, char*[]); + const crashpad::NoCfiIcall crashpad_main( + dlsym(handle, "CrashpadHandlerMain")); + if (!crashpad_main) { + __android_log_print(ANDROID_LOG_FATAL, kTag, "dlsym: %s", dlerror()); + return EXIT_FAILURE; + } + + return crashpad_main(argc - 1, argv + 1); +} diff --git a/shared/sentry/external/crashpad/handler/mac/crash_report_exception_handler.cc b/shared/sentry/external/crashpad/handler/mac/crash_report_exception_handler.cc new file mode 100644 index 000000000..dc5a24046 --- /dev/null +++ b/shared/sentry/external/crashpad/handler/mac/crash_report_exception_handler.cc @@ -0,0 +1,268 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "handler/mac/crash_report_exception_handler.h" + +#include +#include + +#include "base/logging.h" +#include "base/mac/mach_logging.h" +#include "base/mac/scoped_mach_port.h" +#include "base/strings/stringprintf.h" +#include "client/settings.h" +#include "handler/mac/file_limit_annotation.h" +#include "minidump/minidump_file_writer.h" +#include "minidump/minidump_user_extension_stream_data_source.h" +#include "snapshot/crashpad_info_client_options.h" +#include "snapshot/mac/process_snapshot_mac.h" +#include "util/file/file_helper.h" +#include "util/file/file_writer.h" +#include "util/mach/bootstrap.h" +#include "util/mach/exc_client_variants.h" +#include "util/mach/exception_behaviors.h" +#include "util/mach/exception_types.h" +#include "util/mach/mach_extensions.h" +#include "util/mach/mach_message.h" +#include "util/mach/scoped_task_suspend.h" +#include "util/mach/symbolic_constants_mach.h" +#include "util/misc/metrics.h" +#include "util/misc/tri_state.h" +#include "util/misc/uuid.h" + +namespace crashpad { + +CrashReportExceptionHandler::CrashReportExceptionHandler( + CrashReportDatabase* database, + CrashReportUploadThread* upload_thread, + const std::map* process_annotations, + const std::vector* attachments, + const UserStreamDataSources* user_stream_data_sources) + : database_(database), + upload_thread_(upload_thread), + process_annotations_(process_annotations), + attachments_(attachments), + user_stream_data_sources_(user_stream_data_sources) {} + +CrashReportExceptionHandler::~CrashReportExceptionHandler() { +} + +kern_return_t CrashReportExceptionHandler::CatchMachException( + exception_behavior_t behavior, + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count, + const mach_msg_trailer_t* trailer, + bool* destroy_complex_request) { + RecordFileLimitAnnotation(); + Metrics::ExceptionEncountered(); + Metrics::ExceptionCode(ExceptionCodeForMetrics(exception, code[0])); + *destroy_complex_request = true; + + // The expected behavior is EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, + // but it’s possible to deal with any exception behavior as long as it + // carries identity information (valid thread and task ports). + if (!ExceptionBehaviorHasIdentity(behavior)) { + LOG(ERROR) << base::StringPrintf( + "unexpected exception behavior %s, rejecting", + ExceptionBehaviorToString( + behavior, kUseFullName | kUnknownIsNumeric | kUseOr).c_str()); + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kUnexpectedExceptionBehavior); + return KERN_FAILURE; + } else if (behavior != (EXCEPTION_STATE_IDENTITY | kMachExceptionCodes)) { + LOG(WARNING) << base::StringPrintf( + "unexpected exception behavior %s, proceeding", + ExceptionBehaviorToString( + behavior, kUseFullName | kUnknownIsNumeric | kUseOr).c_str()); + } + + if (task == mach_task_self()) { + LOG(ERROR) << "cannot suspend myself"; + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kFailedDueToSuspendSelf); + return KERN_FAILURE; + } + + ScopedTaskSuspend suspend(task); + + ProcessSnapshotMac process_snapshot; + if (!process_snapshot.Initialize(task)) { + Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSnapshotFailed); + return KERN_FAILURE; + } + + // Check for suspicious message sources. A suspicious exception message comes + // from a source other than the kernel or the process that the exception + // purportedly occurred in. + // + // TODO(mark): Consider exceptions outside of the range (0, 32) from the + // kernel to be suspicious, and exceptions other than kMachExceptionSimulated + // from the process itself to be suspicious. + const pid_t pid = process_snapshot.ProcessID(); + pid_t audit_pid = AuditPIDFromMachMessageTrailer(trailer); + if (audit_pid != -1 && audit_pid != 0) { + if (audit_pid != pid) { + LOG(WARNING) << "exception for pid " << pid << " sent by pid " + << audit_pid; + } + } + + CrashpadInfoClientOptions client_options; + process_snapshot.GetCrashpadOptions(&client_options); + + if (client_options.crashpad_handler_behavior != TriState::kDisabled && + !IsExceptionNonfatalResource(exception, code[0], pid)) { + // Non-fatal resource exceptions are never user-visible and are not + // currently of interest to Crashpad. + + if (!process_snapshot.InitializeException(behavior, + thread, + exception, + code, + code_count, + *flavor, + old_state, + old_state_count)) { + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kExceptionInitializationFailed); + return KERN_FAILURE; + } + + UUID client_id; + Settings* const settings = database_->GetSettings(); + if (settings && settings->GetClientID(&client_id)) { + process_snapshot.SetClientID(client_id); + } + process_snapshot.SetAnnotationsSimpleMap(*process_annotations_); + + std::unique_ptr new_report; + CrashReportDatabase::OperationStatus database_status = + database_->PrepareNewCrashReport(&new_report); + if (database_status != CrashReportDatabase::kNoError) { + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kPrepareNewCrashReportFailed); + return KERN_FAILURE; + } + + process_snapshot.SetReportID(new_report->ReportID()); + + MinidumpFileWriter minidump; + minidump.InitializeFromSnapshot(&process_snapshot); + AddUserExtensionStreams( + user_stream_data_sources_, &process_snapshot, &minidump); + + if (!minidump.WriteEverything(new_report->Writer())) { + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kMinidumpWriteFailed); + return KERN_FAILURE; + } + + for (const auto& attachment : (*attachments_)) { + FileReader file_reader; + if (!file_reader.Open(attachment)) { + LOG(ERROR) << "attachment " << attachment.value().c_str() + << " couldn't be opened, skipping"; + continue; + } + + base::FilePath filename = attachment.BaseName(); + FileWriter* file_writer = new_report->AddAttachment(filename.value()); + if (file_writer == nullptr) { + LOG(ERROR) << "attachment " << filename.value().c_str() + << " couldn't be created, skipping"; + continue; + } + + CopyFileContent(&file_reader, file_writer); + } + + UUID uuid; + database_status = + database_->FinishedWritingCrashReport(std::move(new_report), &uuid); + if (database_status != CrashReportDatabase::kNoError) { + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kFinishedWritingCrashReportFailed); + return KERN_FAILURE; + } + + if (upload_thread_) { + upload_thread_->ReportPending(uuid); + } + } + + if (client_options.system_crash_reporter_forwarding != TriState::kDisabled && + (exception == EXC_CRASH || + exception == EXC_RESOURCE || + exception == EXC_GUARD)) { + // Don’t forward simulated exceptions such as kMachExceptionSimulated to the + // system crash reporter. Only forward the types of exceptions that it would + // receive under normal conditions. Although the system crash reporter is + // able to deal with other exceptions including simulated ones, forwarding + // them to the system crash reporter could present the system’s crash UI for + // processes that haven’t actually crashed, and could result in reports not + // actually associated with crashes being sent to the operating system + // vendor. + base::mac::ScopedMachSendRight + system_crash_reporter_handler(SystemCrashReporterHandler()); + if (system_crash_reporter_handler.get()) { + // Make copies of mutable out parameters so that the system crash reporter + // can’t influence the state returned by this method. + thread_state_flavor_t flavor_forward = *flavor; + mach_msg_type_number_t new_state_forward_count = *new_state_count; + std::vector new_state_forward( + new_state, new_state + new_state_forward_count); + + // The system crash reporter requires the behavior to be + // EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES. It uses the identity + // parameters but doesn’t appear to use the state parameters, including + // |flavor|, and doesn’t care if they are 0 or invalid. As long as an + // identity is available (checked above), any other exception behavior is + // converted to what the system crash reporter wants, with the caveat that + // problems may arise if the state wasn’t available and the system crash + // reporter changes in the future to use it. However, normally, the state + // will be available. + kern_return_t kr = UniversalExceptionRaise( + EXCEPTION_STATE_IDENTITY | MACH_EXCEPTION_CODES, + system_crash_reporter_handler.get(), + thread, + task, + exception, + code, + code_count, + &flavor_forward, + old_state, + old_state_count, + new_state_forward_count ? &new_state_forward[0] : nullptr, + &new_state_forward_count); + MACH_LOG_IF(WARNING, kr != KERN_SUCCESS, kr) << "UniversalExceptionRaise"; + } + } + + ExcServerCopyState( + behavior, old_state, old_state_count, new_state, new_state_count); + + Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSuccess); + return ExcServerSuccessfulReturnValue(exception, behavior, false); +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/handler/mac/crash_report_exception_handler.h b/shared/sentry/external/crashpad/handler/mac/crash_report_exception_handler.h new file mode 100644 index 000000000..d5d969cec --- /dev/null +++ b/shared/sentry/external/crashpad/handler/mac/crash_report_exception_handler.h @@ -0,0 +1,100 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_HANDLER_MAC_CRASH_REPORT_EXCEPTION_HANDLER_H_ +#define CRASHPAD_HANDLER_MAC_CRASH_REPORT_EXCEPTION_HANDLER_H_ + +#include + +#include +#include + +#include "client/crash_report_database.h" +#include "handler/crash_report_upload_thread.h" +#include "handler/user_stream_data_source.h" +#include "util/mach/exc_server_variants.h" + +namespace crashpad { + +//! \brief An exception handler that writes crash reports for exception messages +//! to a CrashReportDatabase. +class CrashReportExceptionHandler final + : public UniversalMachExcServer::Interface { + public: + //! \brief Creates a new object that will store crash reports in \a database. + //! + //! \param[in] database The database to store crash reports in. Weak. + //! \param[in] upload_thread The upload thread to notify when a new crash + //! report is written into \a database. Report upload is skipped if this + //! value is `nullptr`. + //! \param[in] process_annotations A map of annotations to insert as + //! process-level annotations into each crash report that is written. Do + //! not confuse this with module-level annotations, which are under the + //! control of the crashing process, and are used to implement Chrome’s + //! “crash keys.†Process-level annotations are those that are beyond the + //! control of the crashing process, which must reliably be set even if + //! the process crashes before it’s able to establish its own annotations. + //! To interoperate with Breakpad servers, the recommended practice is to + //! specify values for the `"prod"` and `"ver"` keys as process + //! annotations. + //! \param[in] attachments A vector of file paths that should be captured with + //! each report at the time of the crash. + //! \param[in] user_stream_data_sources Data sources to be used to extend + //! crash reports. For each crash report that is written, the data sources + //! are called in turn. These data sources may contribute additional + //! minidump streams. `nullptr` if not required. + CrashReportExceptionHandler( + CrashReportDatabase* database, + CrashReportUploadThread* upload_thread, + const std::map* process_annotations, + const std::vector* attachments, + const UserStreamDataSources* user_stream_data_sources); + + CrashReportExceptionHandler(const CrashReportExceptionHandler&) = delete; + CrashReportExceptionHandler& operator=(const CrashReportExceptionHandler&) = + delete; + + ~CrashReportExceptionHandler(); + + // UniversalMachExcServer::Interface: + + //! \brief Processes an exception message by writing a crash report to this + //! object’s CrashReportDatabase. + kern_return_t CatchMachException( + exception_behavior_t behavior, + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count, + const mach_msg_trailer_t* trailer, + bool* destroy_complex_request) override; + + private: + CrashReportDatabase* database_; // weak + CrashReportUploadThread* upload_thread_; // weak + const std::map* process_annotations_; // weak + const std::vector* attachments_; // weak + const UserStreamDataSources* user_stream_data_sources_; // weak +}; + +} // namespace crashpad + +#endif // CRASHPAD_HANDLER_MAC_CRASH_REPORT_EXCEPTION_HANDLER_H_ diff --git a/shared/sentry/external/crashpad/handler/mac/exception_handler_server.cc b/shared/sentry/external/crashpad/handler/mac/exception_handler_server.cc new file mode 100644 index 000000000..83bb26968 --- /dev/null +++ b/shared/sentry/external/crashpad/handler/mac/exception_handler_server.cc @@ -0,0 +1,244 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "handler/mac/exception_handler_server.h" + +#include + +#include "base/logging.h" +#include "base/mac/mach_logging.h" +#include "util/mach/composite_mach_message_server.h" +#include "util/mach/mach_extensions.h" +#include "util/mach/mach_message.h" +#include "util/mach/mach_message_server.h" +#include "util/mach/notify_server.h" + +namespace crashpad { + +namespace { + +class ExceptionHandlerServerRun : public UniversalMachExcServer::Interface, + public NotifyServer::DefaultInterface { + public: + ExceptionHandlerServerRun( + mach_port_t exception_port, + mach_port_t notify_port, + bool launchd, + UniversalMachExcServer::Interface* exception_interface) + : UniversalMachExcServer::Interface(), + NotifyServer::DefaultInterface(), + mach_exc_server_(this), + notify_server_(this), + composite_mach_message_server_(), + exception_interface_(exception_interface), + exception_port_(exception_port), + notify_port_(notify_port), + running_(true), + launchd_(launchd) { + composite_mach_message_server_.AddHandler(&mach_exc_server_); + composite_mach_message_server_.AddHandler(¬ify_server_); + } + + ExceptionHandlerServerRun(const ExceptionHandlerServerRun&) = delete; + ExceptionHandlerServerRun& operator=(const ExceptionHandlerServerRun&) = + delete; + + ~ExceptionHandlerServerRun() { + } + + void Run() { + DCHECK(running_); + + kern_return_t kr; + if (!launchd_) { + // Request that a no-senders notification for exception_port_ be sent to + // notify_port_. + mach_port_t previous; + kr = mach_port_request_notification(mach_task_self(), + exception_port_, + MACH_NOTIFY_NO_SENDERS, + 0, + notify_port_, + MACH_MSG_TYPE_MAKE_SEND_ONCE, + &previous); + MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_request_notification"; + base::mac::ScopedMachSendRight previous_owner(previous); + } + + // A single CompositeMachMessageServer will dispatch both exception messages + // and the no-senders notification. Put both receive rights into a port set. + // + // A single receive right can’t be used because the notification request + // requires a send-once right, which would prevent the no-senders condition + // from ever existing. Using distinct receive rights also allows the handler + // methods to ensure that the messages they process were sent by a holder of + // the proper send right. + base::mac::ScopedMachPortSet server_port_set( + NewMachPort(MACH_PORT_RIGHT_PORT_SET)); + CHECK(server_port_set.is_valid()); + + kr = mach_port_insert_member( + mach_task_self(), exception_port_, server_port_set.get()); + MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_member"; + + kr = mach_port_insert_member( + mach_task_self(), notify_port_, server_port_set.get()); + MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_member"; + + // Run the server in kOneShot mode so that running_ can be reevaluated after + // each message. Receipt of a valid no-senders notification causes it to be + // set to false. + while (running_) { + // This will result in a call to CatchMachException() or + // DoMachNotifyNoSenders() as appropriate. + mach_msg_return_t mr = + MachMessageServer::Run(&composite_mach_message_server_, + server_port_set.get(), + kMachMessageReceiveAuditTrailer, + MachMessageServer::kOneShot, + MachMessageServer::kReceiveLargeIgnore, + kMachMessageTimeoutWaitIndefinitely); + + // MACH_SEND_INVALID_DEST occurs when attempting to reply to a dead name. + // This can happen if a mach_exc or exc client disappears before a reply + // can be sent to it. That’s unusal for kernel-generated requests, but can + // easily happen if a task sends its own exception request (as + // SimulateCrash() does) and dies before the reply is sent. + MACH_CHECK(mr == MACH_MSG_SUCCESS || mr == MACH_SEND_INVALID_DEST, mr) + << "MachMessageServer::Run"; + } + } + + // UniversalMachExcServer::Interface: + + kern_return_t CatchMachException(exception_behavior_t behavior, + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count, + const mach_msg_trailer_t* trailer, + bool* destroy_complex_request) override { + if (exception_port != exception_port_) { + LOG(WARNING) << "exception port mismatch"; + return KERN_FAILURE; + } + + return exception_interface_->CatchMachException(behavior, + exception_port, + thread, + task, + exception, + code, + code_count, + flavor, + old_state, + old_state_count, + new_state, + new_state_count, + trailer, + destroy_complex_request); + } + + // NotifyServer::DefaultInterface: + + kern_return_t DoMachNotifyNoSenders( + notify_port_t notify, + mach_port_mscount_t mscount, + const mach_msg_trailer_t* trailer) override { + if (notify != notify_port_) { + // The message was received as part of a port set. This check ensures that + // only the authorized sender of the no-senders notification is able to + // stop the exception server. Otherwise, a malicious client would be able + // to craft and send a no-senders notification via its exception port, and + // cause the handler to stop processing exceptions and exit. + LOG(WARNING) << "notify port mismatch"; + return KERN_FAILURE; + } + + running_ = false; + + return KERN_SUCCESS; + } + + private: + UniversalMachExcServer mach_exc_server_; + NotifyServer notify_server_; + CompositeMachMessageServer composite_mach_message_server_; + UniversalMachExcServer::Interface* exception_interface_; // weak + mach_port_t exception_port_; // weak + mach_port_t notify_port_; // weak + bool running_; + bool launchd_; +}; + +} // namespace + +ExceptionHandlerServer::ExceptionHandlerServer( + base::mac::ScopedMachReceiveRight receive_port, + bool launchd) + : receive_port_(std::move(receive_port)), + notify_port_(NewMachPort(MACH_PORT_RIGHT_RECEIVE)), + launchd_(launchd) { + CHECK(receive_port_.is_valid()); + CHECK(notify_port_.is_valid()); +} + +ExceptionHandlerServer::~ExceptionHandlerServer() { +} + +void ExceptionHandlerServer::Run( + UniversalMachExcServer::Interface* exception_interface) { + ExceptionHandlerServerRun run( + receive_port_.get(), notify_port_.get(), launchd_, exception_interface); + run.Run(); +} + +void ExceptionHandlerServer::Stop() { + // Cause the exception handler server to stop running by sending it a + // synthesized no-senders notification. + // + // mach_no_senders_notification_t defines the receive side of this structure, + // with a trailer element that’s undesirable for the send side. + struct { + mach_msg_header_t header; + NDR_record_t ndr; + mach_msg_type_number_t mscount; + } no_senders_notification = {}; + no_senders_notification.header.msgh_bits = + MACH_MSGH_BITS(MACH_MSG_TYPE_MAKE_SEND_ONCE, 0); + no_senders_notification.header.msgh_size = sizeof(no_senders_notification); + no_senders_notification.header.msgh_remote_port = notify_port_.get(); + no_senders_notification.header.msgh_local_port = MACH_PORT_NULL; + no_senders_notification.header.msgh_id = MACH_NOTIFY_NO_SENDERS; + no_senders_notification.ndr = NDR_record; + no_senders_notification.mscount = 0; + + kern_return_t kr = mach_msg(&no_senders_notification.header, + MACH_SEND_MSG, + sizeof(no_senders_notification), + 0, + MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_msg"; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/handler/mac/exception_handler_server.h b/shared/sentry/external/crashpad/handler/mac/exception_handler_server.h new file mode 100644 index 000000000..c27ecf340 --- /dev/null +++ b/shared/sentry/external/crashpad/handler/mac/exception_handler_server.h @@ -0,0 +1,83 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_HANDLER_MAC_EXCEPTION_HANDLER_SERVER_H_ +#define CRASHPAD_HANDLER_MAC_EXCEPTION_HANDLER_SERVER_H_ + +#include + +#include "base/mac/scoped_mach_port.h" +#include "util/mach/exc_server_variants.h" + +namespace crashpad { + +//! \brief Runs the main exception-handling server in Crashpad’s handler +//! process. +class ExceptionHandlerServer { + public: + //! \brief Constructs an ExceptionHandlerServer object. + //! + //! \param[in] receive_port The port that exception messages and no-senders + //! notifications will be received on. + //! \param[in] launchd If `true`, the exception handler is being run from + //! launchd. \a receive_port is not monitored for no-senders + //! notifications, and instead, Stop() must be called to provide a “quit†+ //! signal. + ExceptionHandlerServer(base::mac::ScopedMachReceiveRight receive_port, + bool launchd); + + ExceptionHandlerServer(const ExceptionHandlerServer&) = delete; + ExceptionHandlerServer& operator=(const ExceptionHandlerServer&) = delete; + + ~ExceptionHandlerServer(); + + //! \brief Runs the exception-handling server. + //! + //! \param[in] exception_interface An object to send exception messages to. + //! + //! This method monitors the receive port for exception messages and, if + //! not being run by launchd, no-senders notifications. It continues running + //! until it has no more clients, indicated by the receipt of a no-senders + //! notification, or until Stop() is called. When not being run by launchd, it + //! is important to assure that a send right exists in a client (or has been + //! queued by `mach_msg()` to be sent to a client) prior to calling this + //! method, or it will detect that it is sender-less and return immediately. + //! + //! All exception messages will be passed to \a exception_interface. + //! + //! This method must only be called once on an ExceptionHandlerServer object. + //! + //! If an unexpected condition that prevents this method from functioning is + //! encountered, it will log a message and terminate execution. Receipt of an + //! invalid message on the receive port will cause a message to be logged, but + //! this method will continue running normally. + void Run(UniversalMachExcServer::Interface* exception_interface); + + //! \brief Stops a running exception-handling server. + //! + //! Stop() may be called at any time, and may be called from a signal handler. + //! If Stop() is called before Run() it will cause Run() to return as soon as + //! it is called. It is harmless to call Stop() after Run() has already + //! returned, or to call Stop() after it has already been called. + void Stop(); + + private: + base::mac::ScopedMachReceiveRight receive_port_; + base::mac::ScopedMachReceiveRight notify_port_; + bool launchd_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_HANDLER_MAC_EXCEPTION_HANDLER_SERVER_H_ diff --git a/shared/sentry/external/crashpad/handler/mac/file_limit_annotation.cc b/shared/sentry/external/crashpad/handler/mac/file_limit_annotation.cc new file mode 100644 index 000000000..063345343 --- /dev/null +++ b/shared/sentry/external/crashpad/handler/mac/file_limit_annotation.cc @@ -0,0 +1,132 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "handler/mac/file_limit_annotation.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "base/format_macros.h" +#include "base/strings/stringprintf.h" +#include "client/crashpad_info.h" +#include "client/simple_string_dictionary.h" +#include "util/posix/scoped_dir.h" + +namespace crashpad { + +namespace { + +// rv is the return value from sysctl() or sysctlbyname(), and value and size +// are the pointers passed as oldp and oldlenp. If sysctl() failed, the returned +// string will be "E" followed by the error number. If there was a size +// mismatch, the returned string will be "Z" followed by the size indicated by +// sysctl(). Normally, a string representation of *value will be returned. +std::string FormatFromSysctl(int rv, const int* value, const size_t* size) { + if (rv != 0) { + return base::StringPrintf("E%d", errno); + } + if (*size != sizeof(*value)) { + return base::StringPrintf("Z%zu", *size); + } + return base::StringPrintf("%d", *value); +} + +// Counts the number of open file descriptors in the process and returns it as a +// string. This /dev/fd and the value returned will include the open file +// descriptor for that directory. If opendir() fails, the returned string will +// be "E" followed by the error number. If readdir() fails, it will be "R" +// followed by the error number. +std::string CountOpenFileDescriptors() { + DIR* dir = opendir("/dev/fd"); + if (!dir) { + return base::StringPrintf("E%d", errno); + } + + ScopedDIR dir_owner(dir); + + dirent* entry; + int count = 0; + while ((errno = 0, entry = readdir(dir)) != nullptr) { + const char* entry_name = entry->d_name; + if (strcmp(entry_name, ".") == 0 || strcmp(entry_name, "..") == 0) { + continue; + } + + ++count; + } + + if (errno != 0) { + return base::StringPrintf("R%d", errno); + } + + return base::StringPrintf("%d", count); +} + +// Returns a string for |limit|, or "inf" if |limit| is RLIM_INFINITY. +std::string StringForRLim(rlim_t limit) { + if (limit == RLIM_INFINITY) { + return std::string("inf"); + } + + return base::StringPrintf("%" PRIu64, limit); +} + +} // namespace + +void RecordFileLimitAnnotation() { + CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo(); + SimpleStringDictionary* simple_annotations = + crashpad_info->simple_annotations(); + if (!simple_annotations) { + simple_annotations = new SimpleStringDictionary(); + crashpad_info->set_simple_annotations(simple_annotations); + } + + int value; + size_t size = sizeof(value); + std::string num_files = FormatFromSysctl( + sysctlbyname("kern.num_files", &value, &size, nullptr, 0), &value, &size); + + int mib[] = {CTL_KERN, KERN_MAXFILES}; + size = sizeof(value); + std::string max_files = FormatFromSysctl( + sysctl(mib, std::size(mib), &value, &size, nullptr, 0), &value, &size); + + std::string open_files = CountOpenFileDescriptors(); + + rlimit limit; + std::string nofile; + if (getrlimit(RLIMIT_NOFILE, &limit) != 0) { + nofile = base::StringPrintf("E%d,E%d", errno, errno); + } else { + nofile = + StringForRLim(limit.rlim_cur) + "," + StringForRLim(limit.rlim_max); + } + + std::string annotation = base::StringPrintf("%s,%s,%s,%s", + num_files.c_str(), + max_files.c_str(), + open_files.c_str(), + nofile.c_str()); + simple_annotations->SetKeyValue("file-limits", annotation.c_str()); +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/handler/mac/file_limit_annotation.h b/shared/sentry/external/crashpad/handler/mac/file_limit_annotation.h new file mode 100644 index 000000000..1131986e6 --- /dev/null +++ b/shared/sentry/external/crashpad/handler/mac/file_limit_annotation.h @@ -0,0 +1,38 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_HANDLER_MAC_FILE_LIMIT_ANNOTATION_H_ +#define CRASHPAD_HANDLER_MAC_FILE_LIMIT_ANNOTATION_H_ + +namespace crashpad { + +//! \brief Records a `"file-limits"` simple annotation for the process. +//! +//! This annotation will be used to confirm the theory that certain crashes are +//! caused by systems at or near their file descriptor table size limits. +//! +//! The format of the annotation is four comma-separated values: the system-wide +//! `kern.num_files` and `kern.maxfiles` values from `sysctl()`, and the +//! process-specific current and maximum file descriptor limits from +//! `getrlimit(RLIMIT_NOFILE, …)`. +//! +//! See https://crashpad.chromium.org/bug/180. +//! +//! TODO(mark): Remove this annotation after sufficient data has been collected +//! for analysis. +void RecordFileLimitAnnotation(); + +} // namespace crashpad + +#endif // CRASHPAD_HANDLER_MAC_FILE_LIMIT_ANNOTATION_H_ diff --git a/shared/sentry/external/crashpad/handler/main.cc b/shared/sentry/external/crashpad/handler/main.cc new file mode 100644 index 000000000..855a5a7de --- /dev/null +++ b/shared/sentry/external/crashpad/handler/main.cc @@ -0,0 +1,53 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "handler/handler_main.h" + +#include "build/build_config.h" +#include "tools/tool_support.h" + +#if BUILDFLAG(IS_WIN) +#include +#endif + +#if BUILDFLAG(IS_POSIX) + +int main(int argc, char* argv[]) { + return crashpad::HandlerMain(argc, argv, nullptr); +} + +#elif BUILDFLAG(IS_WIN) + +namespace { + +int HandlerMainAdaptor(int argc, char* argv[]) { + return crashpad::HandlerMain(argc, argv, nullptr); +} + +} // namespace + +// The default entry point for /subsystem:windows. In Crashpad’s own build, this +// is used by crashpad_handler.exe. It’s also used by crashpad_handler.com when +// produced by editbin from a copy of crashpad_handler.exe. +int APIENTRY wWinMain(HINSTANCE, HINSTANCE, wchar_t*, int) { + return crashpad::ToolSupport::Wmain(__argc, __wargv, HandlerMainAdaptor); +} + +// The default entry point for /subsystem:console. This is not currently used by +// Crashpad’s own build, but may be used by other builds. +int wmain(int argc, wchar_t* argv[]) { + return crashpad::ToolSupport::Wmain(argc, argv, HandlerMainAdaptor); +} + +#endif // BUILDFLAG(IS_POSIX) diff --git a/shared/sentry/external/crashpad/handler/minidump_to_upload_parameters.cc b/shared/sentry/external/crashpad/handler/minidump_to_upload_parameters.cc new file mode 100644 index 000000000..6b64081ea --- /dev/null +++ b/shared/sentry/external/crashpad/handler/minidump_to_upload_parameters.cc @@ -0,0 +1,87 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "handler/minidump_to_upload_parameters.h" + +#include "base/logging.h" +#include "client/annotation.h" +#include "snapshot/module_snapshot.h" +#include "snapshot/process_snapshot.h" +#include "util/stdlib/map_insert.h" + +namespace crashpad { + +namespace { + +void InsertOrReplaceMapEntry(std::map* map, + const std::string& key, + const std::string& value) { + std::string old_value; + if (!MapInsertOrReplace(map, key, value, &old_value)) { + LOG(WARNING) << "duplicate key " << key << ", discarding value " + << old_value; + } +} + +} // namespace + +std::map BreakpadHTTPFormParametersFromMinidump( + const ProcessSnapshot* process_snapshot) { + std::map parameters = + process_snapshot->AnnotationsSimpleMap(); + + std::string list_annotations; + for (const ModuleSnapshot* module : process_snapshot->Modules()) { + for (const auto& kv : module->AnnotationsSimpleMap()) { + if (!parameters.insert(kv).second) { + LOG(WARNING) << "duplicate key " << kv.first << ", discarding value " + << kv.second; + } + } + + for (const std::string& annotation : module->AnnotationsVector()) { + list_annotations.append(annotation); + list_annotations.append("\n"); + } + + for (const AnnotationSnapshot& annotation : module->AnnotationObjects()) { + if (annotation.type != static_cast(Annotation::Type::kString)) { + continue; + } + + std::string value(reinterpret_cast(annotation.value.data()), + annotation.value.size()); + std::pair entry(annotation.name, value); + if (!parameters.insert(entry).second) { + LOG(WARNING) << "duplicate annotation name " << annotation.name + << ", discarding value " << value; + } + } + } + + if (!list_annotations.empty()) { + // Remove the final newline character. + list_annotations.resize(list_annotations.size() - 1); + + InsertOrReplaceMapEntry(¶meters, "list_annotations", list_annotations); + } + + UUID client_id; + process_snapshot->ClientID(&client_id); + InsertOrReplaceMapEntry(¶meters, "guid", client_id.ToString()); + + return parameters; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/handler/minidump_to_upload_parameters.h b/shared/sentry/external/crashpad/handler/minidump_to_upload_parameters.h new file mode 100644 index 000000000..94d396fa7 --- /dev/null +++ b/shared/sentry/external/crashpad/handler/minidump_to_upload_parameters.h @@ -0,0 +1,61 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef HANDLER_MINIDUMP_TO_UPLOAD_PARAMETERS_H_ +#define HANDLER_MINIDUMP_TO_UPLOAD_PARAMETERS_H_ + +#include +#include + +namespace crashpad { + +class ProcessSnapshot; + +//! \brief Given a ProcessSnapshot, returns a map of key-value pairs to use as +//! HTTP form parameters for upload to a Breakpad crash report colleciton +//! server. +//! +//! The map is built by combining the process simple annotations map with +//! each module’s simple annotations map and annotation objects. +//! +//! In the case of duplicate simple map keys or annotation names, the map will +//! retain the first value found for any key, and will log a warning about +//! discarded values. The precedence rules for annotation names are: the two +//! reserved keys discussed below, process simple annotations, module simple +//! annotations, and module annotation objects. +//! +//! For annotation objects, only ones of that are Annotation::Type::kString are +//! included. +//! +//! Each module’s annotations vector is also examined and built into a single +//! string value, with distinct elements separated by newlines, and stored at +//! the key named “list_annotationsâ€, which supersedes any other key found by +//! that name. +//! +//! The client ID stored in the minidump is converted to a string and stored at +//! the key named “guidâ€, which supersedes any other key found by that name. +//! +//! In the event of an error reading the minidump file, a message will be +//! logged. +//! +//! \param[in] process_snapshot The process snapshot from which annotations +//! will be extracted. +//! +//! \returns A string map of the annotations. +std::map BreakpadHTTPFormParametersFromMinidump( + const ProcessSnapshot* process_snapshot); + +} // namespace crashpad + +#endif // HANDLER_MINIDUMP_TO_UPLOAD_PARAMETERS_H_ diff --git a/shared/sentry/external/crashpad/handler/minidump_to_upload_parameters_test.cc b/shared/sentry/external/crashpad/handler/minidump_to_upload_parameters_test.cc new file mode 100644 index 000000000..0c3a0e7c3 --- /dev/null +++ b/shared/sentry/external/crashpad/handler/minidump_to_upload_parameters_test.cc @@ -0,0 +1,99 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "handler/minidump_to_upload_parameters.h" + +#include "gtest/gtest.h" +#include "snapshot/test/test_module_snapshot.h" +#include "snapshot/test/test_process_snapshot.h" +#include "util/misc/uuid.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(MinidumpToUploadParameters, PrecedenceRules) { + const std::string guid = "00112233-4455-6677-8899-aabbccddeeff"; + UUID uuid; + ASSERT_TRUE(uuid.InitializeFromString(guid)); + + TestProcessSnapshot process_snapshot; + process_snapshot.SetClientID(uuid); + process_snapshot.SetAnnotationsSimpleMap({ + {"process-1", "abcdefg"}, + {"list_annotations", "BAD: process_annotations"}, + {"guid", "BAD: process_annotations"}, + {"first", "process"}, + }); + + auto module_snapshot_0 = std::make_unique(); + module_snapshot_0->SetAnnotationsVector( + {"list-module-0-1", "list-module-0-2"}); + module_snapshot_0->SetAnnotationsSimpleMap({ + {"module-0-1", "goat"}, + {"module-0-2", "doge"}, + {"list_annotations", "BAD: module 0"}, + {"guid", "BAD: module 0"}, + {"first", "BAD: module 0"}, + {"second", "module 0"}, + }); + module_snapshot_0->SetAnnotationObjects({ + {"module-0-3", 1, {'s', 't', 'a', 'r'}}, + {"module-0-4", 0xFFFA, {0x42}}, + {"guid", 1, {'B', 'A', 'D', '*', '0', '-', '0'}}, + {"list_annotations", 1, {'B', 'A', 'D', '*', '0', '-', '1'}}, + {"first", 1, {'B', 'A', 'D', '*', '0', '-', '2'}}, + }); + process_snapshot.AddModule(std::move(module_snapshot_0)); + + auto module_snapshot_1 = std::make_unique(); + module_snapshot_1->SetAnnotationsVector( + {"list-module-1-1", "list-module-1-2"}); + module_snapshot_1->SetAnnotationsSimpleMap({ + {"module-1-1", "bear"}, + {"list_annotations", "BAD: module 1"}, + {"guid", "BAD: module 1"}, + {"first", "BAD: module 1"}, + {"second", "BAD: module 1"}, + }); + module_snapshot_1->SetAnnotationObjects({ + {"module-1-3", 0xBEEF, {'a', 'b', 'c'}}, + {"module-1-4", 1, {'m', 'o', 'o', 'n'}}, + {"guid", 1, {'B', 'A', 'D', '*', '1', '-', '0'}}, + {"list_annotations", 1, {'B', 'A', 'D', '*', '1', '-', '1'}}, + {"second", 1, {'B', 'A', 'D', '*', '1', '-', '2'}}, + }); + process_snapshot.AddModule(std::move(module_snapshot_1)); + + auto upload_parameters = + BreakpadHTTPFormParametersFromMinidump(&process_snapshot); + + EXPECT_EQ(upload_parameters.size(), 10u); + EXPECT_EQ(upload_parameters["process-1"], "abcdefg"); + EXPECT_EQ(upload_parameters["first"], "process"); + EXPECT_EQ(upload_parameters["module-0-1"], "goat"); + EXPECT_EQ(upload_parameters["module-0-2"], "doge"); + EXPECT_EQ(upload_parameters["module-0-3"], "star"); + EXPECT_EQ(upload_parameters["second"], "module 0"); + EXPECT_EQ(upload_parameters["module-1-1"], "bear"); + EXPECT_EQ(upload_parameters["module-1-4"], "moon"); + EXPECT_EQ(upload_parameters["list_annotations"], + "list-module-0-1\nlist-module-0-2\n" + "list-module-1-1\nlist-module-1-2"); + EXPECT_EQ(upload_parameters["guid"], guid); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/handler/prune_crash_reports_thread.cc b/shared/sentry/external/crashpad/handler/prune_crash_reports_thread.cc new file mode 100644 index 000000000..7876c2fef --- /dev/null +++ b/shared/sentry/external/crashpad/handler/prune_crash_reports_thread.cc @@ -0,0 +1,45 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// 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 "handler/prune_crash_reports_thread.h" + +#include + +#include "client/prune_crash_reports.h" + +namespace crashpad { + +PruneCrashReportThread::PruneCrashReportThread( + CrashReportDatabase* database, + std::unique_ptr condition) + : thread_(60 * 60 * 24, this), + condition_(std::move(condition)), + database_(database) {} + +PruneCrashReportThread::~PruneCrashReportThread() {} + +void PruneCrashReportThread::Start() { + thread_.Start(60 * 10); +} + +void PruneCrashReportThread::Stop() { + thread_.Stop(); +} + +void PruneCrashReportThread::DoWork(const WorkerThread* thread) { + database_->CleanDatabase(60 * 60 * 24 * 3); + PruneCrashReportDatabase(database_, condition_.get()); +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/handler/prune_crash_reports_thread.h b/shared/sentry/external/crashpad/handler/prune_crash_reports_thread.h new file mode 100644 index 000000000..5009ed655 --- /dev/null +++ b/shared/sentry/external/crashpad/handler/prune_crash_reports_thread.h @@ -0,0 +1,80 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_HANDLER_PRUNE_CRASH_REPORTS_THREAD_H_ +#define CRASHPAD_HANDLER_PRUNE_CRASH_REPORTS_THREAD_H_ + +#include + +#include "util/thread/stoppable.h" +#include "util/thread/worker_thread.h" + +namespace crashpad { + +class CrashReportDatabase; +class PruneCondition; + +//! \brief A thread that periodically prunes crash reports from the database +//! using the specified condition. +//! +//! After the thread is started, the database is pruned using the condition +//! every 24 hours. Upon calling Start(), the thread waits 10 minutes before +//! performing the initial prune operation. +class PruneCrashReportThread : public WorkerThread::Delegate, public Stoppable { + public: + //! \brief Constructs a new object. + //! + //! \param[in] database The database to prune crash reports from. + //! \param[in] condition The condition used to evaluate crash reports for + //! pruning. + PruneCrashReportThread(CrashReportDatabase* database, + std::unique_ptr condition); + + PruneCrashReportThread(const PruneCrashReportThread&) = delete; + PruneCrashReportThread& operator=(const PruneCrashReportThread&) = delete; + + ~PruneCrashReportThread(); + + // Stoppable: + + //! \brief Starts a dedicated pruning thread. + //! + //! The thread waits before running the initial prune, so as to not interfere + //! with any startup-related IO performed by the client. + //! + //! This method may only be be called on a newly-constructed object or after + //! a call to Stop(). + void Start() override; + + //! \brief Stops the pruning thread. + //! + //! This method must only be called after Start(). If Start() has been called, + //! this method must be called before destroying an object of this class. + //! + //! This method may be called from any thread other than the pruning thread. + //! It is expected to only be called from the same thread that called Start(). + void Stop() override; + + private: + // WorkerThread::Delegate: + void DoWork(const WorkerThread* thread) override; + + WorkerThread thread_; + std::unique_ptr condition_; + CrashReportDatabase* database_; // weak +}; + +} // namespace crashpad + +#endif // CRASHPAD_HANDLER_PRUNE_CRASH_REPORTS_THREAD_H_ diff --git a/shared/sentry/external/crashpad/handler/user_stream_data_source.cc b/shared/sentry/external/crashpad/handler/user_stream_data_source.cc new file mode 100644 index 000000000..7e30fa8ca --- /dev/null +++ b/shared/sentry/external/crashpad/handler/user_stream_data_source.cc @@ -0,0 +1,43 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "handler/user_stream_data_source.h" + +#include "base/logging.h" +#include "minidump/minidump_file_writer.h" +#include "minidump/minidump_user_extension_stream_data_source.h" +#include "snapshot/process_snapshot.h" + +namespace crashpad { + +void AddUserExtensionStreams( + const UserStreamDataSources* user_stream_data_sources, + ProcessSnapshot* process_snapshot, + MinidumpFileWriter* minidump_file_writer) { + if (!user_stream_data_sources) + return; + for (const auto& source : *user_stream_data_sources) { + std::unique_ptr data_source( + source->ProduceStreamData(process_snapshot)); + if (data_source && + !minidump_file_writer->AddUserExtensionStream(std::move(data_source))) { + // This should only happen if multiple user stream sources yield the + // same stream type. It's the user's responsibility to make sure + // sources don't collide on the same stream type. + LOG(ERROR) << "AddUserExtensionStream failed"; + } + } +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/handler/user_stream_data_source.h b/shared/sentry/external/crashpad/handler/user_stream_data_source.h new file mode 100644 index 000000000..11bb9c3d4 --- /dev/null +++ b/shared/sentry/external/crashpad/handler/user_stream_data_source.h @@ -0,0 +1,68 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_HANDLER_USER_STREAM_DATA_SOURCE_H_ +#define CRASHPAD_HANDLER_USER_STREAM_DATA_SOURCE_H_ + +#include +#include + +namespace crashpad { + +class MinidumpFileWriter; +class MinidumpUserExtensionStreamDataSource; +class ProcessSnapshot; + +//! \brief Extensibility interface for embedders who wish to add custom streams +//! to minidumps. +class UserStreamDataSource { + public: + virtual ~UserStreamDataSource() {} + + //! \brief Produce the contents for an extension stream for a crashed program. + //! + //! Called after \a process_snapshot has been initialized for the crashed + //! process to (optionally) produce the contents of a user extension stream + //! that will be attached to the minidump. + //! + //! \param[in] process_snapshot An initialized snapshot for the crashed + //! process. + //! + //! \return A new data source for the stream to add to the minidump or + //! `nullptr` on failure or to opt out of adding a stream. + virtual std::unique_ptr + ProduceStreamData(ProcessSnapshot* process_snapshot) = 0; +}; + +using UserStreamDataSources = + std::vector>; + +//! \brief Adds user extension streams to a minidump. +//! +//! Dispatches to each source in \a user_stream_data_sources and adds returned +//! extension streams to \a minidump_file_writer. +//! +//! \param[in] user_stream_data_sources A pointer to the data sources, or +//! `nullptr`. +//! \param[in] process_snapshot An initialized snapshot to the crashing process. +//! \param[in] minidump_file_writer Any extension streams will be added to this +//! minidump. +void AddUserExtensionStreams( + const UserStreamDataSources* user_stream_data_sources, + ProcessSnapshot* process_snapshot, + MinidumpFileWriter* minidump_file_writer); + +} // namespace crashpad + +#endif // CRASHPAD_HANDLER_USER_STREAM_DATA_SOURCE_H_ diff --git a/shared/sentry/external/crashpad/handler/win/.gitattributes b/shared/sentry/external/crashpad/handler/win/.gitattributes new file mode 100644 index 000000000..6495e5126 --- /dev/null +++ b/shared/sentry/external/crashpad/handler/win/.gitattributes @@ -0,0 +1,21 @@ +# Copyright 2019 The Crashpad Authors. All rights reserved. +# +# 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. + +# This should be a .cc file, which would allow its attributes to be controlled +# by the *.cc pattern in the root .gitattributes file, but it’s named with a +# .cpp extension instead. This file needs to be built with VC++6, a vintage 1998 +# compiler, which might not understand .cc to mean C++. Rather than setting +# attributes globally for .cpp files, which are undesirable (.cc should be used +# in its place), provide a file-specific mapping here. +/z7_test.cpp text eol=lf diff --git a/shared/sentry/external/crashpad/handler/win/crash_other_program.cc b/shared/sentry/external/crashpad/handler/win/crash_other_program.cc new file mode 100644 index 000000000..3020f6621 --- /dev/null +++ b/shared/sentry/external/crashpad/handler/win/crash_other_program.cc @@ -0,0 +1,141 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// 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 +#include +#include + +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "client/crashpad_client.h" +#include "gtest/gtest.h" +#include "test/test_paths.h" +#include "test/win/child_launcher.h" +#include "util/file/file_io.h" +#include "util/win/exception_codes.h" +#include "util/win/scoped_handle.h" +#include "util/win/xp_compat.h" + +namespace crashpad { +namespace test { +namespace { + +constexpr DWORD kCrashAndDumpTargetExitCode = 0xdeadbea7; + +bool CrashAndDumpTarget(HANDLE process) { + DWORD target_pid = GetProcessId(process); + + ScopedFileHANDLE thread_snap(CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0)); + if (!thread_snap.is_valid()) { + PLOG(ERROR) << "CreateToolhelp32Snapshot"; + return false; + } + + THREADENTRY32 te32; + te32.dwSize = sizeof(THREADENTRY32); + if (!Thread32First(thread_snap.get(), &te32)) { + PLOG(ERROR) << "Thread32First"; + return false; + } + + do { + if (te32.th32OwnerProcessID == target_pid) { + // We set the thread priority of "Thread1" to a non-default value before + // going to sleep. Dump and blame this thread. For an explanation of "9", + // see https://msdn.microsoft.com/library/ms685100.aspx. + if (te32.tpBasePri == 9) { + ScopedKernelHANDLE thread( + OpenThread(kXPThreadAllAccess, false, te32.th32ThreadID)); + if (!thread.is_valid()) { + PLOG(ERROR) << "OpenThread"; + return false; + } + if (!CrashpadClient::DumpAndCrashTargetProcess( + process, thread.get(), kCrashAndDumpTargetExitCode)) { + return false; + } + return true; + } + } + } while (Thread32Next(thread_snap.get(), &te32)); + + LOG(ERROR) << "target not found"; + return false; +} + +int CrashOtherProgram(int argc, wchar_t* argv[]) { + CrashpadClient client; + + if (argc == 2 || argc == 3) { + if (!client.SetHandlerIPCPipe(argv[1])) { + LOG(ERROR) << "SetHandlerIPCPipe"; + return EXIT_FAILURE; + } + } else { + fprintf(stderr, "Usage: %ls [noexception]\n", argv[0]); + return EXIT_FAILURE; + } + + // Launch another process that hangs. + base::FilePath test_executable = TestPaths::Executable(); + base::FilePath child_test_executable = + test_executable.DirName().Append(L"hanging_program.exe"); + ChildLauncher child(child_test_executable, argv[1]); + child.Start(); + if (testing::Test::HasFatalFailure()) { + LOG(ERROR) << "failed to start child"; + return EXIT_FAILURE; + } + + // Wait until it's ready. + char c; + if (!LoggingReadFileExactly(child.stdout_read_handle(), &c, sizeof(c)) || + c != ' ') { + LOG(ERROR) << "failed child communication"; + return EXIT_FAILURE; + } + + DWORD expect_exit_code; + if (argc == 3 && wcscmp(argv[2], L"noexception") == 0) { + expect_exit_code = ExceptionCodes::kTriggeredExceptionCode; + if (!CrashpadClient::DumpAndCrashTargetProcess( + child.process_handle(), 0, 0)) + return EXIT_FAILURE; + } else { + expect_exit_code = kCrashAndDumpTargetExitCode; + if (!CrashAndDumpTarget(child.process_handle())) { + return EXIT_FAILURE; + } + } + + DWORD exit_code = child.WaitForExit(); + if (exit_code != expect_exit_code) { + LOG(ERROR) << base::StringPrintf( + "incorrect exit code, expected 0x%lx, observed 0x%lx", + expect_exit_code, + exit_code); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +} // namespace +} // namespace test +} // namespace crashpad + +int wmain(int argc, wchar_t* argv[]) { + return crashpad::test::CrashOtherProgram(argc, argv); +} diff --git a/shared/sentry/external/crashpad/handler/win/crash_report_exception_handler.cc b/shared/sentry/external/crashpad/handler/win/crash_report_exception_handler.cc new file mode 100644 index 000000000..2098a574e --- /dev/null +++ b/shared/sentry/external/crashpad/handler/win/crash_report_exception_handler.cc @@ -0,0 +1,154 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "handler/win/crash_report_exception_handler.h" + +#include +#include + +#include "base/strings/utf_string_conversions.h" +#include "client/crash_report_database.h" +#include "client/settings.h" +#include "handler/crash_report_upload_thread.h" +#include "minidump/minidump_file_writer.h" +#include "minidump/minidump_user_extension_stream_data_source.h" +#include "snapshot/win/process_snapshot_win.h" +#include "util/file/file_helper.h" +#include "util/file/file_writer.h" +#include "util/misc/metrics.h" +#include "util/win/registration_protocol_win.h" +#include "util/win/scoped_process_suspend.h" +#include "util/win/termination_codes.h" + +namespace crashpad { + +CrashReportExceptionHandler::CrashReportExceptionHandler( + CrashReportDatabase* database, + CrashReportUploadThread* upload_thread, + const std::map* process_annotations, + const std::vector* attachments, + const UserStreamDataSources* user_stream_data_sources) + : database_(database), + upload_thread_(upload_thread), + process_annotations_(process_annotations), + attachments_(attachments), + user_stream_data_sources_(user_stream_data_sources) {} + +CrashReportExceptionHandler::~CrashReportExceptionHandler() {} + +void CrashReportExceptionHandler::ExceptionHandlerServerStarted() {} + +unsigned int CrashReportExceptionHandler::ExceptionHandlerServerException( + HANDLE process, + WinVMAddress exception_information_address, + WinVMAddress debug_critical_section_address) { + Metrics::ExceptionEncountered(); + + ScopedProcessSuspend suspend(process); + + ProcessSnapshotWin process_snapshot; + if (!process_snapshot.Initialize(process, + ProcessSuspensionState::kSuspended, + exception_information_address, + debug_critical_section_address)) { + Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSnapshotFailed); + return kTerminationCodeSnapshotFailed; + } + + // Now that we have the exception information, even if something else fails we + // can terminate the process with the correct exit code. + const unsigned int termination_code = + process_snapshot.Exception()->Exception(); + static_assert( + std::is_same::type, + decltype(process_snapshot.Exception()->Exception())>::value, + "expected ExceptionCode() and process termination code to match"); + + Metrics::ExceptionCode(termination_code); + + CrashpadInfoClientOptions client_options; + process_snapshot.GetCrashpadOptions(&client_options); + if (client_options.crashpad_handler_behavior != TriState::kDisabled) { + UUID client_id; + Settings* const settings = database_->GetSettings(); + if (settings && settings->GetClientID(&client_id)) { + process_snapshot.SetClientID(client_id); + } + + process_snapshot.SetAnnotationsSimpleMap(*process_annotations_); + + std::unique_ptr new_report; + CrashReportDatabase::OperationStatus database_status = + database_->PrepareNewCrashReport(&new_report); + if (database_status != CrashReportDatabase::kNoError) { + LOG(ERROR) << "PrepareNewCrashReport failed"; + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kPrepareNewCrashReportFailed); + return termination_code; + } + + process_snapshot.SetReportID(new_report->ReportID()); + + MinidumpFileWriter minidump; + minidump.InitializeFromSnapshot(&process_snapshot); + AddUserExtensionStreams( + user_stream_data_sources_, &process_snapshot, &minidump); + + if (!minidump.WriteEverything(new_report->Writer())) { + LOG(ERROR) << "WriteEverything failed"; + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kMinidumpWriteFailed); + return termination_code; + } + + for (const auto& attachment : (*attachments_)) { + FileReader file_reader; + if (!file_reader.Open(attachment)) { + LOG(ERROR) << "attachment " << attachment.value().c_str() + << " couldn't be opened, skipping"; + continue; + } + + base::FilePath filename = attachment.BaseName(); + FileWriter* file_writer = + new_report->AddAttachment(base::WideToUTF8(filename.value())); + if (file_writer == nullptr) { + LOG(ERROR) << "attachment " << filename.value().c_str() + << " couldn't be created, skipping"; + continue; + } + + CopyFileContent(&file_reader, file_writer); + } + + UUID uuid; + database_status = + database_->FinishedWritingCrashReport(std::move(new_report), &uuid); + if (database_status != CrashReportDatabase::kNoError) { + LOG(ERROR) << "FinishedWritingCrashReport failed"; + Metrics::ExceptionCaptureResult( + Metrics::CaptureResult::kFinishedWritingCrashReportFailed); + return termination_code; + } + + if (upload_thread_) { + upload_thread_->ReportPending(uuid); + } + } + + Metrics::ExceptionCaptureResult(Metrics::CaptureResult::kSuccess); + return termination_code; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/handler/win/crash_report_exception_handler.h b/shared/sentry/external/crashpad/handler/win/crash_report_exception_handler.h new file mode 100644 index 000000000..d322370f1 --- /dev/null +++ b/shared/sentry/external/crashpad/handler/win/crash_report_exception_handler.h @@ -0,0 +1,91 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_HANDLER_WIN_CRASH_REPORT_EXCEPTION_HANDLER_H_ +#define CRASHPAD_HANDLER_WIN_CRASH_REPORT_EXCEPTION_HANDLER_H_ + +#include + +#include +#include + +#include "handler/user_stream_data_source.h" +#include "util/win/exception_handler_server.h" + +namespace crashpad { + +class CrashReportDatabase; +class CrashReportUploadThread; + +//! \brief An exception handler that writes crash reports for exception messages +//! to a CrashReportDatabase. +class CrashReportExceptionHandler final + : public ExceptionHandlerServer::Delegate { + public: + //! \brief Creates a new object that will store crash reports in \a database. + //! + //! \param[in] database The database to store crash reports in. Weak. + //! \param[in] upload_thread The upload thread to notify when a new crash + //! report is written into \a database. Report upload is skipped if this + //! value is `nullptr`. + //! \param[in] process_annotations A map of annotations to insert as + //! process-level annotations into each crash report that is written. Do + //! not confuse this with module-level annotations, which are under the + //! control of the crashing process, and are used to implement Chrome's + //! "crash keys." Process-level annotations are those that are beyond the + //! control of the crashing process, which must reliably be set even if + //! the process crashes before it's able to establish its own annotations. + //! To interoperate with Breakpad servers, the recommended practice is to + //! specify values for the `"prod"` and `"ver"` keys as process + //! annotations. + //! \param[in] attachments A vector of file paths that should be captured with + //! each report at the time of the crash. + //! \param[in] user_stream_data_sources Data sources to be used to extend + //! crash reports. For each crash report that is written, the data sources + //! are called in turn. These data sources may contribute additional + //! minidump streams. `nullptr` if not required. + CrashReportExceptionHandler( + CrashReportDatabase* database, + CrashReportUploadThread* upload_thread, + const std::map* process_annotations, + const std::vector* attachments, + const UserStreamDataSources* user_stream_data_sources); + + CrashReportExceptionHandler(const CrashReportExceptionHandler&) = delete; + CrashReportExceptionHandler& operator=(const CrashReportExceptionHandler&) = + delete; + + ~CrashReportExceptionHandler(); + + // ExceptionHandlerServer::Delegate: + + //! \brief Processes an exception message by writing a crash report to this + //! object's CrashReportDatabase. + void ExceptionHandlerServerStarted() override; + unsigned int ExceptionHandlerServerException( + HANDLE process, + WinVMAddress exception_information_address, + WinVMAddress debug_critical_section_address) override; + + private: + CrashReportDatabase* database_; // weak + CrashReportUploadThread* upload_thread_; // weak + const std::map* process_annotations_; // weak + const std::vector* attachments_; // weak + const UserStreamDataSources* user_stream_data_sources_; // weak +}; + +} // namespace crashpad + +#endif // CRASHPAD_HANDLER_WIN_CRASH_REPORT_EXCEPTION_HANDLER_H_ diff --git a/shared/sentry/external/crashpad/handler/win/crashy_signal.cc b/shared/sentry/external/crashpad/handler/win/crashy_signal.cc new file mode 100644 index 000000000..e2deacaf6 --- /dev/null +++ b/shared/sentry/external/crashpad/handler/win/crashy_signal.cc @@ -0,0 +1,89 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// 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 +#include +#include +#include + +#include "base/logging.h" +#include "client/crashpad_client.h" + +namespace crashpad { +namespace { + +enum WhereToSignalFrom { + kUnknown = -1, + kMain = 0, + kBackground = 1, +}; + +WhereToSignalFrom MainOrBackground(wchar_t* name) { + if (wcscmp(name, L"main") == 0) + return kMain; + if (wcscmp(name, L"background") == 0) + return kBackground; + return kUnknown; +} + +DWORD WINAPI BackgroundThread(void* arg) { + abort(); +} + +int CrashySignalMain(int argc, wchar_t* argv[]) { + CrashpadClient client; + + WhereToSignalFrom from; + if (argc == 3 && (from = MainOrBackground(argv[2])) != kUnknown) { + if (!client.SetHandlerIPCPipe(argv[1])) { + LOG(ERROR) << "SetHandler"; + return EXIT_FAILURE; + } + } else { + fprintf(stderr, "Usage: %ls main|background\n", argv[0]); + return EXIT_FAILURE; + } + + // In debug builds part of abort() is to open a dialog. We don't want tests to + // block at that dialog, so disable it. + _set_abort_behavior(0, _WRITE_ABORT_MSG); + + if (from == kBackground) { + HANDLE thread = CreateThread(nullptr, + 0, + &BackgroundThread, + nullptr, + 0, + nullptr); + if (!thread) { + PLOG(ERROR) << "CreateThread"; + return EXIT_FAILURE; + } + if (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0) { + PLOG(ERROR) << "WaitForSingleObject"; + return EXIT_FAILURE; + } + } else { + abort(); + } + + return EXIT_SUCCESS; +} + +} // namespace +} // namespace crashpad + +int wmain(int argc, wchar_t* argv[]) { + return crashpad::CrashySignalMain(argc, argv); +} diff --git a/shared/sentry/external/crashpad/handler/win/crashy_test_program.cc b/shared/sentry/external/crashpad/handler/win/crashy_test_program.cc new file mode 100644 index 000000000..f5cf04877 --- /dev/null +++ b/shared/sentry/external/crashpad/handler/win/crashy_test_program.cc @@ -0,0 +1,265 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "base/files/file_path.h" +#include "base/logging.h" +#include "build/build_config.h" +#include "client/crashpad_client.h" +#include "client/crashpad_info.h" +#include "util/win/critical_section_with_debug_info.h" +#include "util/win/get_function.h" + +// ntstatus.h conflicts with windows.h so define this locally. +#ifndef STATUS_NO_SUCH_FILE +#define STATUS_NO_SUCH_FILE static_cast(0xC000000F) +#endif + +namespace crashpad { + +uint32_t* g_extra_memory_pointer; +uint32_t* g_extra_memory_not_saved; + +namespace { + +CRITICAL_SECTION g_test_critical_section; + +unsigned char g_test_memory[] = { + 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, + 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, +}; + +ULONG RtlNtStatusToDosError(NTSTATUS status) { + static const auto rtl_nt_status_to_dos_error = + GET_FUNCTION_REQUIRED(L"ntdll.dll", ::RtlNtStatusToDosError); + return rtl_nt_status_to_dos_error(status); +} + +void AllocateMemoryOfVariousProtections() { + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + + const size_t kPageSize = system_info.dwPageSize; + + static constexpr uint32_t kPageTypes[] = { + PAGE_NOACCESS, + PAGE_READONLY, + PAGE_READWRITE, + PAGE_EXECUTE, + PAGE_EXECUTE_READ, + PAGE_EXECUTE_READWRITE, + + // PAGE_NOACCESS is invalid with PAGE_GUARD. + PAGE_READONLY | PAGE_GUARD, + PAGE_READWRITE | PAGE_GUARD, + PAGE_EXECUTE | PAGE_GUARD, + PAGE_EXECUTE_READ | PAGE_GUARD, + PAGE_EXECUTE_READWRITE | PAGE_GUARD, + }; + + // All of these allocations are leaked, we want to view them in windbg via + // !vprot. + void* reserve = VirtualAlloc( + nullptr, std::size(kPageTypes) * kPageSize, MEM_RESERVE, PAGE_READWRITE); + PCHECK(reserve) << "VirtualAlloc MEM_RESERVE"; + uintptr_t reserve_as_int = reinterpret_cast(reserve); + + for (size_t i = 0; i < std::size(kPageTypes); ++i) { + void* result = + VirtualAlloc(reinterpret_cast(reserve_as_int + (kPageSize * i)), + kPageSize, + MEM_COMMIT, + kPageTypes[i]); + PCHECK(result) << "VirtualAlloc MEM_COMMIT " << i; + } +} + +DWORD WINAPI NullThreadProc(void* param) { + return 0; +} + +// Creates a suspended background thread, and sets EDI/RDI/X17 to point at +// g_test_memory so we can confirm it's available in the minidump. +bool CreateThreadWithRegisterPointingToTestMemory() { + HANDLE thread = CreateThread( + nullptr, 0, &NullThreadProc, nullptr, CREATE_SUSPENDED, nullptr); + if (!thread) { + PLOG(ERROR) << "CreateThread"; + return false; + } + + CONTEXT context = {0}; + context.ContextFlags = CONTEXT_INTEGER; + if (!GetThreadContext(thread, &context)) { + PLOG(ERROR) << "GetThreadContext"; + return false; + } +#if defined(ARCH_CPU_X86_64) + context.Rdi = reinterpret_cast(g_test_memory); +#elif defined(ARCH_CPU_X86) + context.Edi = reinterpret_cast(g_test_memory); +#elif defined(ARCH_CPU_ARM64) + context.X17 = reinterpret_cast(g_test_memory); +#endif + if (!SetThreadContext(thread, &context)) { + PLOG(ERROR) << "SetThreadContext"; + return false; + } + + return true; +} + +void SomeCrashyFunction() { + // SetLastError and NTSTATUS so that we have something to view in !gle in + // windbg. RtlNtStatusToDosError() stores STATUS_NO_SUCH_FILE into the + // LastStatusError of the TEB as a side-effect, and we'll be setting + // ERROR_FILE_NOT_FOUND for GetLastError(). + SetLastError(RtlNtStatusToDosError(STATUS_NO_SUCH_FILE)); + + volatile int* foo = reinterpret_cast(7); + *foo = 42; +} + +void AllocateExtraMemoryToBeSaved( + crashpad::SimpleAddressRangeBag* extra_ranges) { + constexpr size_t kNumVals = 2000; + auto extra_memory = new uint32_t[kNumVals]; + g_extra_memory_pointer = extra_memory; + for (size_t i = 0; i < kNumVals; ++i) + extra_memory[i] = + static_cast::type>( + i * 13 + 2); + extra_ranges->Insert(extra_memory, sizeof(extra_memory[0]) * kNumVals); + extra_ranges->Insert(&g_extra_memory_pointer, sizeof(g_extra_memory_pointer)); +} + +void AllocateExtraUnsavedMemory(crashpad::SimpleAddressRangeBag* extra_ranges) { + // Allocate some extra memory, and then Insert() but also Remove() it so we + // can confirm it doesn't get saved. + constexpr size_t kNumVals = 2000; + auto extra_memory = new uint32_t[kNumVals]; + g_extra_memory_not_saved = extra_memory; + for (size_t i = 0; i < kNumVals; ++i) + extra_memory[i] = + static_cast::type>( + i * 17 + 7); + extra_ranges->Insert(extra_memory, sizeof(extra_memory[0]) * kNumVals); + extra_ranges->Insert(&g_extra_memory_not_saved, + sizeof(g_extra_memory_not_saved)); + + // We keep the pointer's memory, but remove the pointed-to memory. + extra_ranges->Remove(extra_memory, sizeof(extra_memory[0]) * kNumVals); +} + +int CrashyMain(int argc, wchar_t* argv[]) { + CrashpadClient client; + + if (argc == 2) { + if (!client.SetHandlerIPCPipe(argv[1])) { + LOG(ERROR) << "SetHandler"; + return EXIT_FAILURE; + } + } else if (argc == 3) { + if (!client.StartHandler(base::FilePath(argv[1]), + base::FilePath(argv[2]), + base::FilePath(), + std::string(), + std::map(), + std::vector(), + false, + true)) { + LOG(ERROR) << "StartHandler"; + return EXIT_FAILURE; + } + } else { + fprintf(stderr, "Usage: %ls \n", argv[0]); + fprintf(stderr, " %ls \n", argv[0]); + return EXIT_FAILURE; + } + + crashpad::SimpleAddressRangeBag* extra_ranges = + new crashpad::SimpleAddressRangeBag(); + crashpad::CrashpadInfo::GetCrashpadInfo()->set_extra_memory_ranges( + extra_ranges); + AllocateExtraMemoryToBeSaved(extra_ranges); + AllocateExtraUnsavedMemory(extra_ranges); + + // Load and unload some uncommonly used modules so we can see them in the list + // reported by `lm`. At least two so that we confirm we got the element size + // advancement of RTL_UNLOAD_EVENT_TRACE correct. + CHECK(GetModuleHandle(L"lz32.dll") == nullptr); + CHECK(GetModuleHandle(L"wmerror.dll") == nullptr); + HMODULE lz32 = LoadLibrary(L"lz32.dll"); + HMODULE wmerror = LoadLibrary(L"wmerror.dll"); + FreeLibrary(lz32); + FreeLibrary(wmerror); + + // Make sure data pointed to by the stack is captured. + constexpr int kDataSize = 512; + int* pointed_to_data = new int[kDataSize]; + for (int i = 0; i < kDataSize; ++i) + pointed_to_data[i] = i | ((i % 2 == 0) ? 0x80000000 : 0); + int* offset_pointer = &pointed_to_data[128]; + // Encourage the compiler to keep this variable around. + printf("%p, %p\n", offset_pointer, &offset_pointer); + + crashpad::CrashpadInfo::GetCrashpadInfo() + ->set_gather_indirectly_referenced_memory( + TriState::kEnabled, std::numeric_limits::max()); + + std::vector data_stream1(128, 'x'); + crashpad::CrashpadInfo::GetCrashpadInfo()->AddUserDataMinidumpStream( + 222222, + reinterpret_cast(data_stream1.data()), + data_stream1.size()); + + std::vector data_stream2(4096, 'z'); + crashpad::CrashpadInfo::GetCrashpadInfo()->AddUserDataMinidumpStream( + 333333, + reinterpret_cast(data_stream2.data()), + data_stream2.size()); + + AllocateMemoryOfVariousProtections(); + + if (InitializeCriticalSectionWithDebugInfoIfPossible( + &g_test_critical_section)) { + EnterCriticalSection(&g_test_critical_section); + } + + if (!CreateThreadWithRegisterPointingToTestMemory()) + return EXIT_FAILURE; + + SomeCrashyFunction(); + + return EXIT_SUCCESS; +} + +} // namespace +} // namespace crashpad + +int wmain(int argc, wchar_t* argv[]) { + return crashpad::CrashyMain(argc, argv); +} diff --git a/shared/sentry/external/crashpad/handler/win/crashy_test_z7_loader.cc b/shared/sentry/external/crashpad/handler/win/crashy_test_z7_loader.cc new file mode 100644 index 000000000..4ffcc031f --- /dev/null +++ b/shared/sentry/external/crashpad/handler/win/crashy_test_z7_loader.cc @@ -0,0 +1,68 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 +#include + +#include "base/files/file_path.h" +#include "base/logging.h" +#include "build/build_config.h" +#include "client/crashpad_client.h" +#include "test/test_paths.h" + +#if !defined(ARCH_CPU_X86) +#error This test is only supported on x86. +#endif // !ARCH_CPU_X86 + +namespace crashpad { +namespace { + +int CrashyLoadZ7Main(int argc, wchar_t* argv[]) { + if (argc != 2) { + fprintf(stderr, "Usage: %ls \n", argv[0]); + return EXIT_FAILURE; + } + + CrashpadClient client; + if (!client.SetHandlerIPCPipe(argv[1])) { + LOG(ERROR) << "SetHandler"; + return EXIT_FAILURE; + } + + // The DLL has /Z7 symbols embedded in the binary (rather than in a .pdb). + // There's only an x86 version of this dll as newer x64 toolchains can't + // generate this format any more. + base::FilePath z7_path = test::TestPaths::TestDataRoot().Append( + FILE_PATH_LITERAL("handler/win/z7_test.dll")); + HMODULE z7_test = LoadLibrary(z7_path.value().c_str()); + if (!z7_test) { + PLOG(ERROR) << "LoadLibrary"; + return EXIT_FAILURE; + } + FARPROC crash_me = GetProcAddress(z7_test, "CrashMe"); + if (!crash_me) { + PLOG(ERROR) << "GetProcAddress"; + return EXIT_FAILURE; + } + reinterpret_cast(crash_me)(); + + return EXIT_SUCCESS; +} + +} // namespace +} // namespace crashpad + +int wmain(int argc, wchar_t* argv[]) { + return crashpad::CrashyLoadZ7Main(argc, argv); +} diff --git a/shared/sentry/external/crashpad/handler/win/fake_handler_that_crashes_at_startup.cc b/shared/sentry/external/crashpad/handler/win/fake_handler_that_crashes_at_startup.cc new file mode 100644 index 000000000..028190a46 --- /dev/null +++ b/shared/sentry/external/crashpad/handler/win/fake_handler_that_crashes_at_startup.cc @@ -0,0 +1,20 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// 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. + +// This is used to test a crashpad_handler that launches successfully, but then +// crashes before setting up. +int wmain() { + __debugbreak(); + return 0; +} diff --git a/shared/sentry/external/crashpad/handler/win/hanging_program.cc b/shared/sentry/external/crashpad/handler/win/hanging_program.cc new file mode 100644 index 000000000..aff3ae28e --- /dev/null +++ b/shared/sentry/external/crashpad/handler/win/hanging_program.cc @@ -0,0 +1,137 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// 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 +#include + +#include + +#include "base/debug/alias.h" +#include "base/logging.h" +#include "base/notreached.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "client/crashpad_client.h" +#include "client/crashpad_info.h" + +namespace { + +DWORD WINAPI Thread1(LPVOID context) { + HANDLE event = context; + + // Increase the thread priority as a hacky way to signal to + // crash_other_program.exe that this is the thread to dump. + PCHECK(SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL)); + + // Let the main thread proceed. + PCHECK(SetEvent(event)); + + Sleep(INFINITE); + + NOTREACHED(); + return 0; +} + +DWORD WINAPI Thread2(LPVOID dummy) { + Sleep(INFINITE); + NOTREACHED(); + return 0; +} + +DWORD WINAPI Thread3(LPVOID context) { + // This is a convenient way to pass the event handle to loader_lock_dll.dll. + HANDLE event = context; + PCHECK(SetEnvironmentVariable( + L"CRASHPAD_TEST_DLL_EVENT", + base::UTF8ToWide(base::StringPrintf("%p", event)).c_str())); + + HMODULE dll = LoadLibrary(L"loader_lock_dll.dll"); + if (!dll) + PLOG(FATAL) << "LoadLibrary"; + + // This call is not expected to return. + if (!FreeLibrary(dll)) + PLOG(FATAL) << "FreeLibrary"; + + NOTREACHED(); + return 0; +} + +} // namespace + +int wmain(int argc, wchar_t* argv[]) { + crashpad::CrashpadClient client; + + if (argc == 2) { + if (!client.SetHandlerIPCPipe(argv[1])) { + LOG(ERROR) << "SetHandlerIPCPipe"; + return EXIT_FAILURE; + } + } else { + fprintf(stderr, "Usage: %ls \n", argv[0]); + return EXIT_FAILURE; + } + + // Make sure this module has a CrashpadInfo structure. + crashpad::CrashpadInfo* crashpad_info = + crashpad::CrashpadInfo::GetCrashpadInfo(); + base::debug::Alias(crashpad_info); + + HANDLE event = CreateEvent(nullptr, + false, // bManualReset + false, + nullptr); + if (!event) { + PLOG(ERROR) << "CreateEvent"; + return EXIT_FAILURE; + } + + HANDLE threads[3]; + threads[0] = CreateThread(nullptr, 0, Thread1, event, 0, nullptr); + + threads[1] = CreateThread(nullptr, 0, Thread2, nullptr, 0, nullptr); + + // Wait for Thread1() to complete its work and reach its Sleep() before + // starting the next thread, which will hold the loader lock and potentially + // block any further progress. + if (WaitForSingleObject(event, INFINITE) != WAIT_OBJECT_0) { + PLOG(ERROR) << "WaitForSingleObject"; + return EXIT_FAILURE; + } + + // Use the same event object, which was automatically reset. + threads[2] = CreateThread(nullptr, 0, Thread3, event, 0, nullptr); + + // Wait for loader_lock_dll.dll to signal that the loader lock is held and + // won’t be released. + if (WaitForSingleObject(event, INFINITE) != WAIT_OBJECT_0) { + PLOG(ERROR) << "WaitForSingleObject"; + return EXIT_FAILURE; + } + + // Signal to the parent that everything is ready. + fprintf(stdout, " "); + fflush(stdout); + + // This is not expected to return. + DWORD count = WaitForMultipleObjects( + static_cast(std::size(threads)), threads, true, INFINITE); + if (count == WAIT_FAILED) { + PLOG(ERROR) << "WaitForMultipleObjects"; + } else { + LOG(ERROR) << "WaitForMultipleObjects: " << count; + } + + return EXIT_FAILURE; +} diff --git a/shared/sentry/external/crashpad/handler/win/loader_lock_dll.cc b/shared/sentry/external/crashpad/handler/win/loader_lock_dll.cc new file mode 100644 index 000000000..63c145cfa --- /dev/null +++ b/shared/sentry/external/crashpad/handler/win/loader_lock_dll.cc @@ -0,0 +1,118 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// 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 +#include +#include +#include + +namespace { + +// This custom logging function is to avoid taking a dependency on base in this +// standalone DLL. Don’t call this directly, use the LOG_FATAL() and +// PLOG_FATAL() macros below. +__declspec(noreturn) void LogFatal(const char* file, + int line, + bool get_last_error, + const char* format, + ...) { + DWORD last_error = ERROR_SUCCESS; + if (get_last_error) { + last_error = GetLastError(); + } + + char fname[_MAX_FNAME]; + char ext[_MAX_EXT]; + if (_splitpath_s(file, + nullptr, + 0, + nullptr, + 0, + fname, + sizeof(fname), + ext, + sizeof(ext)) == 0) { + fprintf(stderr, "%s%s", fname, ext); + } else { + fputs(file, stderr); + } + fprintf(stderr, ":%d: ", line); + + va_list va; + va_start(va, format); + vfprintf(stderr, format, va); + va_end(va); + + if (get_last_error) { + fprintf(stderr, ": error %lu", last_error); + } + + fputs("\n", stderr); + fflush(stderr); + + TerminateProcess(GetCurrentProcess(), 1); + __fastfail(FAST_FAIL_FATAL_APP_EXIT); +} + +#define LOG_FATAL(...) \ + do { \ + LogFatal(__FILE__, __LINE__, false, __VA_ARGS__); \ + } while (false) +#define PLOG_FATAL(...) \ + do { \ + LogFatal(__FILE__, __LINE__, true, __VA_ARGS__); \ + } while (false) + +} // namespace + +// This program intentionally blocks in DllMain which is executed with the +// loader lock locked. This allows us to test that +// CrashpadClient::DumpAndCrashTargetProcess() can still dump the target in this +// case. +BOOL WINAPI DllMain(HINSTANCE, DWORD reason, LPVOID) { + switch (reason) { + case DLL_PROCESS_DETACH: + case DLL_THREAD_DETACH: { + // Recover the event handle stashed by the main executable. + static constexpr size_t kEventStringSize = 19; + wchar_t event_string[kEventStringSize]; + SetLastError(ERROR_SUCCESS); + DWORD size = GetEnvironmentVariable( + L"CRASHPAD_TEST_DLL_EVENT", event_string, kEventStringSize); + if (size == 0 && GetLastError() != ERROR_SUCCESS) { + PLOG_FATAL("GetEnvironmentVariable"); + } + if (size == 0 || size >= kEventStringSize) { + LOG_FATAL("GetEnvironmentVariable: size %u", size); + } + + HANDLE event; + int converted = swscanf(event_string, L"%p", &event); + if (converted != 1) { + LOG_FATAL("swscanf: converted %d", converted); + } + + // Let the main thread know that the loader lock is and will remain held. + if (!SetEvent(event)) { + PLOG_FATAL("SetEvent"); + } + + Sleep(INFINITE); + + break; + } + } + + return TRUE; +} diff --git a/shared/sentry/external/crashpad/handler/win/self_destroying_test_program.cc b/shared/sentry/external/crashpad/handler/win/self_destroying_test_program.cc new file mode 100644 index 000000000..8466ff265 --- /dev/null +++ b/shared/sentry/external/crashpad/handler/win/self_destroying_test_program.cc @@ -0,0 +1,91 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 +#include +#include +#include + +#include "base/logging.h" +#include "client/crashpad_client.h" +#include "snapshot/win/process_reader_win.h" + +namespace crashpad { +namespace { + +// We VirtualFree a region in ourselves (the stack) to confirm that the +// exception reporter captures as much as possible in the minidump and doesn't +// abort. __debugbreak() immediately after doing so because the process is +// clearly in a very broken state at this point. +bool FreeOwnStackAndBreak() { + ProcessReaderWin process_reader; + if (!process_reader.Initialize(GetCurrentProcess(), + ProcessSuspensionState::kRunning)) { + LOG(ERROR) << "ProcessReaderWin Initialize"; + return false; + } + + const std::vector threads = + process_reader.Threads(); + if (threads.empty()) { + LOG(ERROR) << "no threads"; + return false; + } + + // Push the stack up a bit so that hopefully the crash handler can succeed, + // but won't be able to read the base of the stack. + _alloca(16384); + + // We can't succeed at MEM_RELEASEing this memory, but MEM_DECOMMIT is good + // enough to make it inaccessible. + if (!VirtualFree(reinterpret_cast(threads[0].stack_region_address), + 100, + MEM_DECOMMIT)) { + PLOG(ERROR) << "VirtualFree"; + return false; + } + + // If the VirtualFree() succeeds, we may have already crashed. __debugbreak() + // just to be sure. + __debugbreak(); + + return true; +} + +int SelfDestroyingMain(int argc, wchar_t* argv[]) { + if (argc != 2) { + fprintf(stderr, "Usage: %ls \n", argv[0]); + return EXIT_FAILURE; + } + + CrashpadClient client; + if (!client.SetHandlerIPCPipe(argv[1])) { + LOG(ERROR) << "SetHandler"; + return EXIT_FAILURE; + } + + if (!FreeOwnStackAndBreak()) + return EXIT_FAILURE; + + // This will never be reached. On success, we'll have crashed above, or + // otherwise returned before here. + return EXIT_SUCCESS; +} + +} // namespace +} // namespace crashpad + +int wmain(int argc, wchar_t* argv[]) { + return crashpad::SelfDestroyingMain(argc, argv); +} diff --git a/shared/sentry/external/crashpad/handler/win/z7_test.cpp b/shared/sentry/external/crashpad/handler/win/z7_test.cpp new file mode 100644 index 000000000..ad7b5d982 --- /dev/null +++ b/shared/sentry/external/crashpad/handler/win/z7_test.cpp @@ -0,0 +1,32 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +// Build in VC++6 or older command prompt with: +// +// cl /nologo /W4 /MT /Z7 z7_test.cpp /link /dll /out:z7_test.dll /debugtype:cv /pdb:none +// +// Given that this is quite tedious to build, the result is also checked in. + +#include +#include + +extern "C" __declspec(dllexport) void CrashMe() { + volatile int* foo = reinterpret_cast(7); + *foo = 42; +} + +BOOL WINAPI DllMain(HINSTANCE hinstance, DWORD reason, LPVOID) { + printf("%p %d\n", hinstance, reason); + return TRUE; +} diff --git a/shared/sentry/external/crashpad/handler/win/z7_test.dll b/shared/sentry/external/crashpad/handler/win/z7_test.dll new file mode 100755 index 0000000000000000000000000000000000000000..d470742e8c3f7282cbe373575708eeb593b67871 GIT binary patch literal 168264 zcmeFae|%KMxj%k3dlF8_CTD>}f(8i?6a_UJ(8MLUk!(nm;9_K#6%(|@+;p`q!a0C# z0vk`(=H#%2_Szrb>ut@&-rCmtDZRBw{ecNf6Hu%CYDM8{tXwhc=AmU?Lg0KrN{dxKK-~VEW&YAXX zj_`Em@2Br_EcyNPJDR?-u5iuTum8i^d%jxu<$J#NwXZ9MU%Iz&t@^dXuY9er^0vCd zuYP^?y;o;vk1tB0UjEk5p$RX3xApwzA6sunc-`ZztrOv%ef-f@H+O&7x&rQZw?EdJ z#=~tX`1iSc`&Yi)L^%Ey#eSb4EOCfJ=lJ=7ku-fm&h#uth9Eo+$lW%yZUS64K0A2W z&HV@yf9P+-72FOx9bjoZ28k#ieR0=z`r?961!&(3f-sw>`&(ZQfn_1juLR+azm??k z;5(29r2MVFtCf2?@{g%gv)XP1x^9L6c6CB|GelH zSKG;ir{(~50H4B?3uoldD+pazuf6yFuYXw(id_T+fmC)s`Op7(1>xrNp)Y)2aNr9L ze8GV)IPe7rzTm(Y9Qc9*UvS_H4t&9ZFF5f3k^{?)lgoO2L*r0};T!4|t|ZTY)yDxh zd@jFHD=lj(J|MUm?c=n)u3gy53H5tiLS10@-GB!JVF&vicEABP4|)V68LKQR^jk_% zVSv2}KpP|Sx`?1-B%IFz5O`8_3S=;clSxo?(Ik6W4{8C(JnQU?FE98E_1Dw zhdr!&`xEC7dGjPe*#0ynn~@_3>^*Eq*=+hM$F|!_-+>r&Vaa|^w>q~hn?jpju((^6 z?fNglt){atBO&XQ@%0!AVyjSy>C%!;b=)#GBU=zcVaEz^2CSZO38x`IZFJ363d zYUPAbFfY@F9ZI@3oTkp`d=f-OyOhjepw2n~j|(35=V8l24MY&Xl)@^)$fNsQstfQN zg8n)dr!)hE#;IOLiT=aRRt9u@=oe2QaVy&ysgU(CK~SyrfI|1K|(8+nvP7bj?4g<7{*-gQWDH@{!9 z|5-mpo-(?-htsvbX~w+YMBW$NF|o@KAJ&qVB7%0OHr~?q6jro;yaS1w4>rVE??r+T zDVNY#f|8!tjH0QNiNB?yJl$(2mp=~t8{BicyOZf!SAm}$oeZ+$DBN%5Njn5LPp&0% zTe5icZ+SHDrxzyq+Zg{m2BYh<825b&BTo`Alp@Jm?`B`lx4Zp@)?pf2yccA(4oArU zW%Mm8eJ@(8aj?qea;Ud!3!?j|Q`H4yw(mL8v8aW0Y%5AZJLu!oNwKQjaGG8v=@~2Z zs(iglG_#GH^oD$WlcX=y8^p%erLMFIz`bf^L%gJ`A+9wSi9$dr1Wm4is%*D%QGiVX zCnJs7A83vFf_9mpHA+ILfo6!-lhx=Abr|CFOC>J*l_2yU4 zPmWErlZb;y5ln~(836BoIVMJ8l@$lfguc8#@t1Tv`=vbl&XMf>a_lf-i2lS&@We!& zaw41V>rd?BnQD})Bj=iw!pOP%m5Gsa8|3KY1h4%>O8kK59dakWZF>q6QHr|hdNnt) z*)dPeS-cqOBH(-CenjZY4;a}^M=-XQAD4H=2P!KZ5oJouchYaE5*X!Yn~|zx8Yc9` z$TK#gkqX%BO3Z>wEKUA{ytRPl?c}|3?E9D(wB!nTs|ktx!3m!rHI4}(Y)T+3%fqCEYC}NOHz)# z26(TpAN&^*2k|wQ8oOK~L-DX;5aN$TDXX8gqpPF#y6;T1tL3_0-%&zuUZJr&mFqN? zsr;^5ZexHYBvgs>SGjW$u9Q6G@pY zzbG7Js3S^BRWtD$;^w`_1!BgFLSj3Doirl?n?LbwAXijrZX*pwC5-Bx6xvjy4N_W#fvSqD;Qw)fN1B+sGLnsXZ;bwm$Yh=?fK?!fN%~1 zz9h;|LoZdP7`4erS+PosZ#EKn5$3G!XYHmksnflngY*Rc7u$ z-1g2fGY8^nN*17nXdX1AV5og_)Dv^mGXrs#Ix#f~1;T}FGGev8Zi2*o?_jzwdM%2+BNNabpA*()E`yy&E}mSy-( zLfG0LeZ9F(!2Qr1mt+A@A%mvxqSE zu1G8Z71Ti%@!tU@jbL24@7O_VKuD>~1gQsTL&38EojO0nf4 zK{6zxB55oe2_v$RVmlwpOUQmOG8s3(Vb6p zFG6XdP}6Jz6cOM$4u~FGsf=S&;a?Re3Up45mFM0exL3u)*{qp11mrowJzU81px*1U zM+@u`&A&9n1MGS1Cak<7-9(eD7bsQxik{dt*pFx zNMaXaB(1z+gp7Wy1~+k1p2!>}<+4wck{4hqi`(7sK806R(!y5bnFHpH!2#7t{eDZVj0wUgBlZkrh z`3N=B=hL>NH$8$*Q;7P+9;TF@{&kl|8bv|Nu}D(xsY)N;N$IqnqI8Nk-?G=_YLwBa zwY(2;<{&a-)`)h=Z9U{Ijja%4qDS`)?GmtVd6La(MzXA&%4rGgZUZu#rhmhRlthaK znoWbA5o_lpbgwIj+zk>WOS>B+c+s3VyP`zOl&o3hm1^J>d0!Z7B$S0 zRf{}>%}l>EVGD=6EAsJ$UqzF0JdYXn!w?Fp`>O zDU;D*QU$vBmS10}CDYW5$Wqa3%Cy}c53gj3_>L^n&N-WZV9h7iSa}P8gipO_wcS)U zA8I!kNzQmra^3XUDwp=nAwd~eXXP!SA~UpJyIHh&ktezKPuBHpH+J~g=7hi{Y`%cF zc^6HHbtT8jrnVy(2x7ndJhsWkd{QTrSRENR#+8W+$`!}r0DBo@HWX7b943p2FVL*z zXT$d3@toG@_ag^-P$pZ~j~--{6Nb`hO4;%t`woqE7V<@IDpzJKXZw-K6A$SbJb5K0 z4}!Y;Kpl}5a(+@eP2FE_aUxhIf%U0`PQP?jLvMN;D5asjk~V6~mR(5wFecaCxtJB3 zM5KWNBF_(IbdsJ#pDzGz4439B=yWm2o-yB$zpgIGYI!eBqP1gUyPsXbE2Ztt_nZcW z68qN=LE%VxbflR0us;xF*o&fXljvEk&u1WSRm1iZaIiq~1u)<6<<5vB8w2$m1 zV*%ulqLw1*H)y>gHYkzO8ER#R-{FmXJVUvn?T|7{-g#)?p?ea7TU1<@1L}~B4tM)S zRbKA@1NWP+@_-n*OsFCRRbF!uhotl94!2j{`R0H&7D;+Xe*2|WD5X%zL@Ak(k2BR* z+77ACYEwcPVx-Z6h@LM%B$eCxKxc=W(xy{75>YvPAK+t&HQ!qP64;7#1MqCXk}ssI^Ffay1f88B@g!n?E*hh#K@dDiO&^O(wY%h~s_+oK^#q` z*3~U1J!BD5S+A3_3jsHK2UBK9zY8K1J^?Fl1(6pw>u8Eu;yDK@Mk!Vnluk2xmGR!v zeDxfC4b18F8f-bc2E|}00L@~_fSI)%a`Ydt0fBUasp}3x*h89(RSoqkp*}0=3JYb> zbvBWDMt-CVlimGD6YbjGgc(Z7V4Z1c2RcdZYYcY3 zmu840eOC;4U7C{ZXSWjXlU7J^EYhsp<}2)kVur1vr3ITqta%wMAA%7NgtVdzVX=qf zZvII_IE{UY%EKYU%J_(cnHgZeMh}A45@VGmEGxiBV-P@n=f2N(O+>VvJiulmit3fF z?Xw!<(1Lmune?;NGX{@_8ZPPLs#2TPCC7qBmCM+tz2l6WO;c}Z{F&rXt~IJ8W8oYy zY|~{yKjm$un(cLYdes~xe=?%v6n&ysxy*6eQdgn&vV{y9ghoAMl@SN`ApD~e7*mjB zh=(N>n}g9(G7u``iuFxOiZwgHzEzxkA%B1^K-FxfgbZ1c5-NIe)7xNZ1;%=G^xNJ73b(k_KGh2r#lX6MxFlACE zwGLAzCChKUjsY?UGp;RJ-HdZ`_(sqlPw$4C4zk6j!wOZJ0qBSOGxjaOy?0 zPAHU2WV5E(u(vR%?akwS8nVF+(=leJfz$x2!WM>)*$0Sn8ncRM^qK`14EVt4JfBB{ z7@VOq5f9%mtaSo8x^CYzNcD8QVoUWMm93)I=Z(LDKbn9ByNPBcx5H7<)}?supn3|7 z0UI69TEoMeoCmait7H!jaegK^9IXm7Du$5uy0#Na(a$hD3d$rn*!ZWJzmHW|gmq4L zJXSG0>icBWH(#!P$ef|y&j&#CSr=m(c4N3i#HS} zm!cf#>s`@fA!R(G@1SVYiL|LxuvFK;(veml*UF!w=}esn9iXeR^{gdqC<{BYj#1V|Z3@&ixum}{I_8BUsj(TDsCW*?T z{H!iDAGOMb-(7NO@RV^BJ{R1>W`(hjR29Y@5J><#m^gUs;QI%UX6;wc44#HAaCK2W zQdAb@4!-9+xHiXwbQ`3#<2*>a-m$dgkkQkya*tb}wxYi}N}4K;Bjm-QTcNN$IEwM} z;@l|Cg_P2#ira_ca=(Kwcd9HG%F01m(t2qQXusr+qFlHVT-V^6P*=VwP~p6#aNJ1~ zVT1e0hvc>aG{Tc!Ir;&)4&@@6+n#jGZEqoLER~9e^&HH8NKX;ky)L2XLswek&s+{b z)fx8rmm;OkzOw3AH5AA+LdFGH26!BzLomq^RQ@3~(Z;GBdXBL)zb0zwOT|rYIw#G6 zb_SyYUBSFwJ7+pkK_`)v31WGDM zxlM-8SRqA^>GhJ_=Aeo7T)OI_9Xn*d#96RPJLizw{=|89hmKu#txI2e2hI_+vyvQr zp5S>&s5Ol2A$&Vu3VIHyR_A1LxNGx(@do_H#^_<9B%#-j;Ep6 z+q9w_#|k(v@2y&)0h!rQzHKDjO5vsD-x&$FQFvkbKaPZ>6u!DVHWGe_!k3mqsJHPw zOyQjJ?^1Xi@OL0=tzQ9&;&4dcv?8?1l>15XA0Kuet*)Gc#!j=WWB7BK;})sAOb| zuS4F2Oe2+D7c}-L6JiT$Vhf(ty0Db46QPd&Ys^&>b9H#SDWq_*dt}*H$}v@*fkrmK zeg|oRxUNhJ_}RM%Ajb1CpEVoe@LvOeXv|be!gd3VUsAV9W;pgl5h@@UeC7(KCPdku z5IrLk;@_VOXJRhA*q#e>&d-GpT4r|a!C_O1J@|>~j6Fz`pDTrbO2>o>?Cw^#PhOlV z;sY~XpNuUrpK}dIYSuL-jLy67ApN|y0p;?+J$%-kNG_jtd-cgQ;8(>PZBNhQG^j39 zfmQMLR4j(Fb~2x&jw$T7NvtdAvfrbQdNkyjT8vpYgx;!-D;Y?v0W^9{ZVSMLh?FJT9(~&mp)n={6Mh6EVT`%5^9ekO}PXFTP zI>#q)OHgr;F8H?-3)rhf+0FvwebI~XPks{GPBr?8c{>n1 zXyGtqF*s-R*c3|Ma@r)VJyplP2iH!k&nKJ^|7MX3V-=3+7r*$038Rb%m#}5_D3UW= z91#**u!w1|T4_B~>JH5~0?t$G~+h`N@UAA9CQ3DtFTI zbv`-Abk!4B=9L*56P3vt%T`>~3dsk%MC?xDyC(8)5kn`wYXbR$=Q)n%OI3jLXFFAi za|HESWp4{}AuZroCE&N_6ASHL!MFbxQUB@MO=_R^w)B&zsMpi8_DqNGqn;>*3L!__ z03(Oxr{$gFaX#Yd-Y|8}zS#3TX~nKW{26a_C(=&;!gv`%`01bDd~?UmRvQj-R;>(U z19lQa+nP`9V;7B+Q9>K=L&mP1iT+4wu)Nr@fzfDaxhWHSvoMur=(A~>gf#HlPWK;3 zr|rF~q$?JqA{&hf1ADOY*?qv>@klxq(!T9(YW9MGJ!y*Djw+-m;5OflJWi2cn%3jgdO7k}Mv=#AgweD- zl|$=E(|Xg0gnt`Nwardbg%pb^X3J;{@~;9BOYo`0X8}I*@R^HG5k3X@7?s~Xp;vA_ z0hhsLa8JTL3AZ0^Kio5L&%jN>O~M_53(Y5%ML}@(JCh@vu&BZje74~8IN|n54XgIY zw^41!`t6#hAfWLAZh#ddX=BQ@L$T(vp%nj%&htO}lhZ#LLb*a2O0fpt0GD@^LN!q% zjI_sDyFKPmdq{{v3QD{!AO2E&?#8F1$~p63`(o!(Aij+_w{r|yqAL}DzKuRe(>~k4X)_rYF%AiCKiohrt&67l(dqsF9XMIwCPy#W=xo%6m zO|x!V?+o9x3WpEcJD}^N9DN)p<}8OyJj5x~mnUoPgzZnQYkj(oI=|$sA2QAX0cH1m zygtqN*z=llAB+}^Wuj-<80zVKKXUkePTQgBcbunw0;fI|JwpAV>5p7M{W6iGl#Cit zzs2^8MvbK3A{on*TFXg6&BR7QZqrc;%-~|Kvm+_Vk7PP9+#>4BBz${rk<>4DEEy+i zR$+UsJlx$5|GHU`78%Am&k>!*YllrREpy6fC|0?nGBe-2f+upvZtLSQcB+tw@JQ>l zak?{04KUT$uV1X+HEGDgJ_6lF=%j8(=Z|j_nb`#h*(&DlcVuU^>p3ApT4paah0Cyy z=J7mBiaD8ni3d|Ty>9J^c4}f9ui`_hbkJR!t@B@<3Ai&f_?^Rq+Pm|?tPZ)RoFAn~MA zoz^=wmTP2FRy~^rlSzYmJ4Na9shV7nvWLo;V(J1Y2!@ppAu)}O>dD$W{TLVMYBlKc zEjbXz4u7E4CM_5|nr?J*>|aU-4l}ZU^d&o~B;%;(Z3xTjrWm)1hG%ANa^`T>YigDj z&-K&~A!6;H;i?ZBZ+ebWvb+nDr6Za7p693O8e6te zvl-3XDo5zO!LUJVDrYWF&Q#BktR1Va%=)9Do>3l7MV& zyi{V@lgr4&QhUZ&0=%!P*{Nc}g(zfIbboXD!FLv$g9Z}ZaaJ_O&%yk23Ug2Qu((tk zzG)K%euHOFZp+5>lo}-WV1TsD865+3CfQsn4j$I)PtLrny;8?#SXR;1VS0LUdX{OV7tS9dJQ5Vc_p;jES`hPxvzSwcG?Y8D@g#;b=v%=C2Jn1tD} zr6a>O^P8AdhGNfiY60Gj@*{^o$h-m54$KL&^zr(V2}2+m6Pw!aJQ!Q*AkcL~Xdfdd zx}HV<1bYZ%(|UuWJqyP0#j#4=k0A%a zod!{GTYNf>^rt=?up063O<(Dvv-J)Gc7oxv<#%XW99ns|i$qmWVJy z3zUNrMsx!D4k7jfVGYg|Ki;q)w%DyzCoQf0Bu?9{So;v1Uhhyp9I&u`2q)lrwb&bB z01z;p(8j9!Juj{Ar4#YnT#P3h=<8$pKE$*-o!-gHl#m4KgHIwHob78H= zQBU{#vhmVqhTv^OH69EuoPU;a0aC9YlE1wQRN*LEj@|~3^9|2wd8-dTbPapLy9uLl zeMb1E5L{!G=vjs3lUDv|qiao)FQ12}-?eRY@t?w%&(EZIjL#<{i+@NoZj*pgCy!`s zra^|i#++9jSm|4a1o_)P2TzO^(tXiN9$Rwl(5Ns_e+c@QzNk&#ZHy5skVwKln z2q|vzwX(c?3A*xlK|RV`^D6j@} zoR4AVLG)+XUhg8#ZxHndIOjJ=`U5c2c01J}U3Fhj>iOC3o5WaZs$0Qu3E!SuIfqlT z9T!+K#C6~>2`po#I4QI2JcnN;4kH2hFfz-LNb{f=OL2JV7!Lc~x>K*rAEG-7r1Rhl zKS?Ta;t!w7xL{%nDtrvXVu?=2|L=<_OSE#DejJ+?{hx^_*Wi$KR6a>fzqWd00s01? zJu#4N5hpd@T6s^RDmY@`)6omGv`?k^6*eQB7QKF--qI(3yAv@mIqV}u{wn|p)KiqKSD7T`%5uS@FSq^p; z##v=zP3u`f#Gu$p^C83svjCTUl47-%z23t5h(|{WjXqLcv{cW z{3>tlhQF*CTVe59Il2P@n4o;L_J?wm%n(}l(EJM%F-hLi2$Br-C}!cZ3_1E;#A^2@ zq4}XwU}tAk!HT+Hh;5RLKYDs_!$fZT4fQYVA!_?QS;{6X`JQh1p|23yZdxc1(pFM| z`x;x{5&EHQk9~(|^1LLs-2k{}pOR%v(cT?)zU+BPJw@O`N-m{lRFY~!9+>(^xmZ*C zN?A2z@pL4yeEr4&!z+Rc(wS}q)@mqaosJ^YfqqLMBy!WK#I{^DLt>r)=dW^i~C8)Iw-w}^fU1j)~XOXA|A*Wit zK3tAL@AU8$Ti$XHl95dehH!Y;ygoc+UKjq@ygHmTuMD3xFAJYDXM_jMOT&LPFAkqE zFAASFCx!>iobV}geE1_XBiwJMg+E+j?&gMUZO~rA4DaTqBn&I8yd5;!VPk~jp!rY1 z-*gh-p!G-Wt0|@8Z#r%Rn)Z>qy&XX7kK4Dv=_Egf*G1L=l9`p9PJ#lEj$3)gQ5?mR zaW(%yl}*@C1swmINn1lk>iQt$ux|wFj5lyhkvrH!IxjNPR>witp?=+*-jPw&K5lW1 zG7Fl_E9`jH|0QO&&AQerKzIA0!K%~Inya*xgHGRDCKW}vcIbSSsi zu@$&`WQ+^h;VbI+{Zd#3Rw%)BFnAe{l%RT+hG~%MJzohSQo?Y@1-AaTj52 zbNG^;-sUW<9&>yykI%if11l}TA0syWmfODIbabxaq`u)*=sItt^kTW~ z=UBX$ix&swhrq^Dai!*=Ilg{-qD^D2y%;UFjM0uH&;zR2BSFC5G8?nGHk>YRodyr= zBwjv_l_1>>*&pcJ>jtOcA?LiA9zj&m7=nG7mQO>SAczlb_|6Q2jIlS^~SvD zyf*}QB2KLf3tot?D9QuL9Q);;!XLS5i5$%%&t}IWIhw_NX{+R@1HPu}-(XH=GYgTK zj4E+5{t~jImmf2F=JcYe<+k4f5LxJvVO9g)`dr8+JhuR4k~U|^AGAu?_G!GgVB?mJ zkncY&tIRhPQgSR@ijKQl&)tvIn+{bqRid;2n~&7o0w|=W1F^ai7pK!VUlWY+c#XuJ5`UBgqh#jkfS#uTgfr- z_P{Z;0?tf-2D-^q#O^i#Q17X$vkR#62iaHA7DhjY7z}AQ>?W=n!=7Q~8taA>5zK^D z13$^mPeJO~Y9Jwynm!$p6cMKNx)!xsYpBZIGzN#1OIQd@v*vS@{p?MeMD=nCnKHU> zA`DX5^+;m$gWf`-*UF~OfL%74qKnpT?6q4@K0y@IZd1bXE-Wm4{TMfQjqvB+fdSN$ ze7+9}Q!#8O@Ok}g;7uwOra5H(@Fd00+Z-!+;R#IR82#Z4<6$BQ!^ChxDvgPoW1^2C zl5Rl8e0_fQHO#X7=n}nROsPTCM(26M8S*pGZ90i8y1P}543J8+y@i^UrlxPFV^7V( zh4<}U@LAt4MR{nyJ;Du0G2`Z-A+-FLh#-T&x=c_w_Ee6^M;*vdoU75cT+6Qt#9!z znu>{`9BaKx>v5q|_g_NHC3@3J{$gEkycM+84~-(JwOQXfOwHc>?5c+7U+Pz?8JsfM zpAi-8T`XtXEbf>64LH=RFxy~9Ug&ptUg{ARDOrvB zghqW?8a~iW<&t1a<5i!KpdHzZPl87<`>zD8yqhY|6%nM4T{i;xlHGAJ$X-MS-f^d; z`m*gb{Sa*fV^GX#e68gd;lCn3ll~as+aJeQDtVCApwbc8?1dC(z;p!b*b>wb*XCMa zp*k^~!7*X7W%J+Vi@es7OK`D?kXULin`*8Z6Wm}q%@2Pdo(oB^h#E+?+>8&z>H_Ft0Mye)Gc4Sew^+mO9&pYz9dLi6wj^F*&^go`0Y~@qW;W!i`5-m@uo^Z0%@D^D5#T z&N#4@fJFB^$#Hbq2{&80P)9Q?hPBJjUIybrXb^Ln@v8P7*<~6%DYML&*cDmbDt;1! zkj5o2`#p#z+T^l-{sVPh62!ZpSX8pL8kf{tkxy~v1O&ubMJ^%$N&}amDAn$PwXuWx zZ`f*TLd!L6*(kVIcOq8suhwAzhkBeWK}Y;>F>UZQn5i-y?OW*~eoGxqAfsmJXS zAQX+@MU&N{vOLPBl2~E%>*`U@pmN=SFI!Z+Wi17A+kc>7k6N(#QoCxO&Vuzax)n)a zJaX?oXdG7SGztVh6Q#tdn0UQS^Rv@!nvd)CeVpcrqcoQs<0PZ>x0R=Ht4lrcDbhgj zW1uG(RK;M>%&>_G)Y&GjR4cM)g$f0SEo4({^d#;lhYazsDT^Ko#BQ;0Q3)6FAy1#G zfuZqf1RB)zIkaap_N2N~Hr7;l_EAU zQzZ0P*b#OYOsC4U%9rOx>AxMW7@i`800Vmd1YU-d8@=>i<*w8+Ioe@^kEO1i)eM zk%U9`H_ha_@wEV-yYU&dsrh^MH-tR)zqP-iRG)8ulgj@#61;-XUVNU%=V^SN!siKm z9>WKA8z*45@jtY`;heCj!oo_y?ZT%JAFP-EkL_#Qz=p8;)~Ky8pfXO$zrs6tMuGN_>{!a|b?m;jv}#_Bmr|F)iED)b!hLQX%g-AEaq=KmK%<9N`(!%q?Qq3N%ry{M-7eBaQdf}u1~Mg=3dYzs(={SZnfj!p zOtIEWOgapk!`4RE;5lgR^$dFRsY-0XeNDbAnFwtaOT0qcQGWXoHz0Fg;HoH9hz`hY zWN8XX4%Nr?b!DJRbf}fuCfFRz#MPHyAs+=G)5NM=s0P37Q$=@Y1`!pCHMlT8OQ;)V zRPqsUAwV}Q4WFQm+w zx}qs>X~61ZF5OLZ1=uc>McTcEsoU-`*S}B=V%sQMcX;-!%m4JHcRVas(t)y$tzLDbnRJ@;x_n$9ba}`79j^f#w(c<5O_k8hKw(YAx z>!yA79uU_C;(ov4G=w;N;yjLj9mT=-Ki3@_@VTUb;>0^lka@iDyc_{2LLRJk5OOQ+ zjxXEduAiNtmm$n+Y5h)*yzIqg0Nm?GYKh(Y2?P%QSX4f{mKRA^PT4i1MVi+DBQ8Hd zP(DAjBQHBrQZfv8y4vq*6~8M zZ5%$o!)M@@Sjce3Dobe~P@jj8#?g;pNBJWHdu1ynqOkyb+g%V(gn)8q(^DV}w#T>k zP(|r+54?xqz{4E@PBQgyD5!qH=cSU@P+G|B!~Hho#(NWg!X|zjqHF-0MDaS044GHh zKv;DnRXe3@JfAZ0DylS#ut3Y}W#MZfrmd`F>t@0I~pZ`NJg~uq*{|`@{1%zzcx3*Gq|V z{o(l>HaC?h-ygo7114JsK6h_00&@O3Q+0~S8~AS6kvfr?Bf91 zHl%9g^@poC?9No8N`H7U2h^kh)&6ib2b87&OZ?#)4!Hb08oxh$3x`cgCAz~OzLf*g zQh*iy@DdI<1^J(%YVK0YGO2M{pSOte!g(DSwnDnLo@XZ{|00tu-Cb8ZhUc_NXQm`L# z*lZ5#O2KIJ=MO_HN9~^i%;wp{k8|)eXbW*a17Je=1cw!JSSw(yJx>CXl?*gFv7zSR zFu>a0;;df0oVedsG0;NV>OlN@kv;e_mM$#I4{+DTzQSEMyNA2EEX3V>wv62C*)2R+ z$ZqCt5xbGQv)Ogroy)G|ZZVs|-FfU{?iR3#+%09}xx0X+!9~5S^SRWpgXD6StIvSW3!dqOKr^j+)M4seB4XT$!_FcYDIPp_fq4r zBJO>hyi>TB+KJ_GFEtQz!iyXBc=d`di9U_lZh26CraX=2xV9ux1ey*27|wwfqkcL2 zw@Y#SaD)JIaJkXXzE9z!6m|!fXZ5p(5w_+N00F4*(BShtryG=o)_g+49>F?c%_js* z=Uz(6Hqq*VdxujgjXss4^{EttB|k9_SUT6d3*kU0FNLGb^s`!I;4o{A$h_wR5-YJ$ z8QGMhljC#PxwARiYB3ce^e`n$VeSz0tJs@@`(IDsD?wV;6v6M}tTRRMQSRjg{~lhv z68af>SydcM6fYb4K0Q@3Uf7V^t_2>h@AI=ad2V*`n6xgKwArM1MM~{N>Tp_nKKtt) zT0J>ND5FwIBudhU<-9$Al>Xjyq942xcpYFkfQKnaSn6;xCMLGV(T;-)s3z1?<~w+N7u>$bj{?&L7T0w`K>S zXE%< znvKH~y!ysbaB8mdhoNm0n2<_xwLg42h5Ml**LtqWIvNP$feC^2Q5Z9Ed}T_Y{y*p= zoUu4mUxZy%c`2p~rOeNM^E>og9Qyw|cXQcKxSP-Zg}Vjpd)zH#G42+zC|qrC0hg_` zlSODl0^60DkiE{p+eG0e5z7iL7w7S+Kmi6*9VFhJmPWnt z#TU_IlK~u}v!~&0qEl0taBg6p+49acAY{Mi)aHwzON%XeAg!_2Aw;S`4ldMvwCR*h(jmJ0P=5arOvhbm z+v$zm&OTHfdO5re7sn)q`^f#<3DkLF6#fever7v?P#5-30DwgHT^&gDrvNzN#(SJ9ZX<}=9&!8$^^k$> zUwqpoT?g@+19VY)(3bR$L)y(ilBht2zQH}{VuJEyqmicVA>4-meTi`I2kvgIJI&lO zqKhOK+lD|%mp}DK)~?V+r6JF_o8@1KMdoSjA+-J0wu+7n0#?b}#iDf|8QnO6g_xAO zPgsZZ#CI=)y%cT~4wiI%^uGq*aK3B|Ete3k7*gle6W#d zi-0${CIV>brAlrSdBWbNyZg{;SJDXC3u9leC%(N95z16NnKq?~yy1(Qo_rf=XlCvt z0PRDwDYrhg2;ZKUm5WQKC>NpAWg*izHXjMiOW1GG#%As)lwOZB)7iRonl>n(+(rg` ztvy6mfT@7PhJl{e`T+p)Gribs_VJCj+!h4{YUlYbWV7;)bEvWoT6{u3rP0Wy2)ua< z-)!!y#)0le5f#UKZi+AL4Kz7O)J-8E#(-Vdp}JwOzbTDJQARxOXh%)9$qJ9h;qNkR z*->TeYDu|G8)P&aRR0jl>IvGs2)J&-xw13<=$t)xeAXu&9Jc25#}>frwu;%#DNxj4 zMOMe7r@-z^FEg`RGM4J93wPuj9G-gDrWg2;rNtYE1)ihP#5v8Jq{{bh&j(p-A>ep# zIK;L4v#82~S1YF|T*G$5q1VcC;nE;5C#0~gAd8#r1P(izsve+9GBB`{nqXkBL&+T2 zo2H0Qj^kG-028Qs+R2TmAygBr5A6~%`eVNK@J4?pl|scg&Pwl3C%$1aOz})^n}l{p zI!wPrJ9`m)3q6;HGX>QBMdH8x%0mhj(M_mk#+|)mvHTvy+gPZJu?GnYF>i8XYc|m= zI`#R~RyuD?^Q!~t1Wi*{cTyeE6gR@Uf{5VR$AkB1JQ&i_Ttl4j#z=5O)DHs@JL zooPO0<8r8Yj`UXaUXr4+?F^&U0D<{|G2g9o}o|O z4{YF0(i%Y{XghTRTXkkD%am(jeWJ{5azBrCh256KN=_wQ(&XZRg(-kK8xLcKC4V@} z%(8u4;n^D>He5wL)K68R>Q zPavN}zB5QiA|_e+(Th75;Q{ zZbHs`DE&7!;|0<8NYJraxSU?WZhC41h6x7$!YlE!J75a^HV^fQ!3z5Q31Jr+QBY*8 z>k=@#^|W2+1_Her2M~6E!%a^E!D@@FX@rzj2Vx5G?vSy@g`Z?luEwL(Mat~hy!D3b z*E%Fkz1pQU<3|hBiF~7BRQ_6GUgWm!__bulS8v7Y#-{`jgMPT$;Fb!l4~kR}>@RTp z>~>cf?pLRC+TwM~o@;_G+8m$q;HD4VAzCEr_qjqVF>k)|N?_4-#VZyS7xONStLj1A z9>wh>pET&@m6W3op-Aj)ARLh4pD(E|8Zy?ojBiR*gL`7G^;+{*QBZt=<+1zR)*V~L z`UYUg>QYKJEcq8nw`6- zu4VzRiJ3>p!?3Xak)6MugGU-=IZ5fp6-GsY->AsOyR*tz=h#~FT z+*Uk0BM4W~SJc7b>rT$uXY7qSjCb(Vx>xkuQw@xEx#mBptI3jMsb6ct{+zA)4|K)r zan=}s#~G&=@~qmSQ0%VKH1+L8b?o6^rBofdI)onsXi0|0t?bFT(Ym1uj~rrB_Oq#! zv7cZY3INREKnXx?Z@#l1lGm;3t9U;bc968n+2qg-VCrDybk5N0OTsuxh;1mf))%u^ zQ6LC9%6pio zr4Mr0I>0_nRC0tU?IdnMrkDiN9Th(vas~oE?(Zb6bp`AOBCEtWfZu0fa|n+wDTYGM zUhEUC+B30cDNX%Dtf~~5s4mpeC|@zNKk%t|jq&rDViQ4z_z~cxw zdt=S{X>2D4khU_`JkG&>%7dh_)9cv1&mv1z0n`eZnXXWi^lAfszRTCoYYkiSQZ$*h=0uQrenT9ck0P+$rs5Ex zFK}4JK~~74E=H8*JHub@Mxprq2uh_s8(Y)_d^SVQfIGm?t*wDZUm#KbzNZQGn;_T5 zJ5nm5e#fI;L=?YkMCiw$)}JGu*_;cqM|s+ROrg=ABPQCc46+s;w_!ByaUMtCL3R(1 zTR9p>?6r2%caZsb+)Wf$S?YjxNY{wn{BcbzZ)AvBFH=P26pU$0PzI;e)lD`ACT)BQ$YS*HQ-h!{1wer?|z--%WYiyR>W>7(_ zK>1p=Qm3&D<=SdF`V*w>B;vxZ?JhV#9)f-oNBz-5YTB|pQ|5DNwhKQaex3^@jiJn_&JBx3cX;)q4THns)!4Ci zKZ?0+(%{+q239*i!u!w22Mt_;u3@70q*}~xSD8#0cTo3BTsne&1h-M0j~~5B4`(#s zS4SSOO!oyO8*B27>H^QJEn8 zQxHNIps?9moVXKFTJo#%)?D7Mr$CLy?y-M*09+vZ#A*e6n2pwyqjb^p^Vr}m2s^$0 zME2h-XpJk;PYYi;@3G3IMt#14HfwRGIKsEcF6-AcO3l2{*b(sK0J93t%UnFgDz}mF zYK*g^dxS!3oRwq!nzNGH50xF$9jKd>Cg`_gE`;Y>^RUwXtr~%Ii7SXXY>nLZERbq! zoc+o}wQ^gvas@iYD@0W#W#r`nWuN$0r1)HZ;n&yDY0y-O z3yyQ%xBAI@7T!brvSw?C5WDAo^rurfD>5$2m zm2?Hm(rH9v8dN5s2%smK^fnuvx!7pQ1#3L=I!_I@MebMyE>M+NI$3MDq@SKqm z*;Cw}i-!s@8=S-CEWDwDTep(>pDVE%IWn#o*I=gUHBp=14}=dobQ z0-6aGgd+!b&OT}Lf5OH_uIb3{n9x3@rX7#?b+7%1SC8oCzrq;A`+suV;~eS(E_hrQv;U^aH%UR%>yJLA)g#|~ragPG=z^&Kf7t{7=$<^}K`_VQy z`=S;~UM{yCqWUK*o8R+<#>_J)*RYD!6xtAWB>kImXU$`#O-b?!;*~* z{91-&Wh}B}qAyC%&1XZHELInFZbreUr-u6UrL;SrZy8B;BD_oBpfWOBZdxEM~2 zGOb3rtOn*va+EF*pty>}1QZ9)Jqh}4gbOOLI`LQHDQmJPYK?wZ?3Wypk(#oVD+n0< zA#fSpn7AfjGLLrHm_vzYY!*G3AcZN$1q}*5B8-SVB8&))i0J-JWvJfzc^#55)n1kl z4pi1*Sx!~#qW1Ath8Gpf>2SAC;9d*Q0^ChD6=N$4`?!Y1l_syWCRqWXa}Z4IZDeUuH5?~bxj2K z$66v7mbh|>#C3pPawD*(eyoBI za~J#hn56irpKFmc{T)GKAQxT^h82u4uUmVEFBSR|Q zAy#O8xa^=@)G_W?;|CIH@wvrSxMtltTngOHFW_d)8gx5;_DZt~E;&@A-~YW!4xI~- z6K~%1{O8xrueWHw@vZ+RRS45=7RaEWym%{-*pPt-RxTnM~K%OZC>-s0AnvO97){K#rZu+akTvR33<)T9j`Bh!D5i*8u&Zxu-Gmq%L$of0z%r3;P~@_S2YHV%e0{b> z{HO0hpb>GcYdmgNp%=Ae;77s6o9U3`xrIF~N3^=pP~VGp*m>eyTCmZbv8Jhz4zH>h zd<#1mRWi;RJ$M!yYh;h{CiiIPGL8QA>5+KQgb`zL`;?04z=k&?(}kLj$sLaN3GL%* z7JJT~Us_7KI4KhJYAsHvzOYbZMWm)?524qMP{H>Z^qf2Yuz#Sa%A$NwNCXm+-0`Yh z=5uWx-<%Fy>r%Mv1(u^DZ3`BqRE_fa1zvYhnM5yCwYYjRuBsY5roAoMs{>VFpY29MQ!+jS7OZ${V7k3p{^6oBkW51P7 z8!xS#wp+qxysvNO^;_8l3r}LXk7I5Xi!h>uT^7Ddj%`CZms2^@7MC7XKEkm&-rvHu z9Qq%&4nDZ0apg_?^3+jqV+9^d&rG4iQxlU|5fCDY5$ETQs4&&1)}6IqZc9*Op#R_( zQ_ojz=`$6-zH?_Gor7LB{zS((Wje~ATD7=zXl+L6@eOHd@0BLuI|^S<>0ULvMt!|x zf9YPi?Vo`ShZN8cK1AZk;2Q}Y&gay2N{T$=4I=NyqxCgjF6qK0Y5e|FR#(R~WoFhf zWm?tZWvK8LTtc%e{1AMo4JK?gvqt>`e&0H-Wb#tirVYMvLFIEe{#n3J)qgp4@Ji<6 zxCog$a_Wv5c^ov^{MtFq$Ozh#GM|wp%*f8g<{^3K4S1#KD&qtCnd&*JNMXzwsgrvb z=6?1v2_PXetCQB<4sne=ooC0ye~#T%b$~LMuW6rLWyICA!J}lV&3{~hkt#YA=T_|? zYt$HEge=v@`uEAwF(NM@tmH2jkXHP;q+JoKIuI5f#Rci{%YmV&&iIIZk_kpHDav>5 zoAeRRvp?Wu|C)YUg9y(WJZk&_Kj}I6rkQ5^ft#Dd5IBh?LQspsS!~6>fY)|7q-L>M zXnrf5q7nBmh{Nv(X0Z}@7+O()zny^m(k|@!VK3uag`XdR5{TsxrtI0+i@?bzN1y5i zB8-g3vFx#*6PP>K1{OGbI1EYtM8Hcr`{AH}O`OxM=d~d<@^5d4Yr+_g!unJS z;rGOh`Lw$E>%up^ScoEV$_$dRL@xmXT#n|pD0sc)|6}i6;G-UFEr+?amEmhi5 z8Uz&O9^o%rnnC zGxNMN=`uS`IvoE5yWdNOn3Z0QYe8c<(;>;U)|0~dN!yOa6A|roB2jeBBgNt|KtArwCIx~ZtW%^*IqCc*mzr4M$fc;Un4Y5}!y{M@!y#0Y!2L%V2-jbIVeJbCUm&lMypO(c z@O>^Zl@?&mTsC*oJv0(wDGl?&yTfHgYY%6}(`8Mcb0GG?vD}%U0=|lSHzL0r1TydU z9GmyTk;1hfFCJ5Ls~DKPQ^KaA0Heid_}vk zA~`B!qYw5mus-Xe3`VbgHVwPxXSW;ferL?VOHhyxUn;`I;HCHQ0*rx!vM*{k&~zws z9X|TKcLJe}>g%0;@1;ao+&x37e394q6jl5n#8pHkrUtM)_Q^`{3-g zyRbb$-WPD>^|P2`&MjDVOM2p}@F#2yH1WLuTwHi??5glTab-1~ba#I{&R(JYzZ&io>2H7FF_w~+*RIEzFY#Xx%osB8(gf&Kes&!O5_upSs=p#%vz3u z`+~jVoTP&ns6n*pO0xi3T%(B$rO7jqp_K1@j!5-LVdjpUNsJ-_AG?5H-HQ{%ev-af zo3s?YtxQ_*4)0fPY8IL&SNjZ3&~iC?=&67(PFrE=pERu(aH;GILgX$d5mxfC6R7S7gv zlVfecq%S@}j$y+)^c^B5F+Zjrw78(=ty^xp+RiUux_vx0Za324cH_Zus>nG1s+Z9p zAgLb=E6Y-J#umDly0bEARd^goc@2(Hy?N|>>Rw~n8cdsB#yv0hx8vXx>Rcr_JjhCj z82PW~sfQWRExrx17(MA(>~~GT^PiKR#WAS+OUA#z7wGh@T<#+5K>#cpzYEV&@F*bo zqu)bwq3fgH^IM)uJ_=1MFQWUNN1+kv^cr;dOO5|AcUl{Q->Y>FfO?weaE_ z)TI|<3D#k&%}kot$hUb+yJj{$Gq;$x&oe(Y6T z*!h0CZqCTtU*^T*lym*Od2{@Wv3<;Apm3MXqaC`D7a$=#~{TtT`UM>T=TXs!tn%t+~tJtREwYCj1bp%)BA&0&D%}~SEw%; ze>rJ2LkWH|iXy;)2_OzGQANxS@+UH*i<^wf)1`2`6Ca$*E{tIN<=K< z67{GBfl2Tl-PE7Z4cEbiBnK(G@nV^I-?!IkjVe-oRy|37AekFY|K8t|5_GHXt?;BfP zk}R%%W$Cw|5j`dIr^Q7Fzec4u@dRH}6MR)k@DMiPc!HAZS0;UqG-P<9KfJR7&aN)P z!s4;off&Typl43X-B5ZL9IoUoZM{l4W#dyA;`k{WxcW*tWg~+dwgUE>_bF@X%E1)` z8;sAe)=Fo1&}ok8<+OfDC|2_P6=8lqP!;5ddZ-6v{L7mfL*2{Q#Iyr4aE+p}B(@(5 zoij;Wlf%k7(Bo4SET5Y}B%(0WZ8<{?G;aO}=VI^~8^mFp6*q^zl`9zu)6OL(Wgp#$ zjba=Wk|{~oJaHI(-13I)mV!weUaA@O0el%83-cAU>5Wg(J1n+!<3fS-MfO_G4$d37 z4_pzrsT0euIOyb_Qp8_eJaW$$dH>=G({J5tQ_^H6A@O#x(*D+e5~TR`yJ1Xe1*6S1&&|aRNTmqhCFz9?aTa_aA?POQ)x*L z&T-HRR)25ta|g}$ypdUP&xf}ae)Q)3#owc)Ivj#DLyb3SwF}flnn9&o?+$G(X1u^$ zT@9PuB_qpFRu`sV&wc^1c#+cho)aGd--2@BD9G!e-d5y2vdX|*AH8wO8y~%T)m~8M z1tn;?xg+m`uhKzF^o5aNsGL2{#QFgV=)`2k{5NO5U}h7rhf!?9W!{(JL!*lo#)IHr zfr0fuhwGid+60;M$BqKz`Zlq|g5b`68cp*P+Lb{E((^;tV#@ z=BwfNN-nFPi;>}9u|TMd48?2z``~pr9^o;Rm2S#RU%A$Ev>BHP6|Q~Rj~UN_hxyWO z>BvKXM~nBzGApyKXTjJcV9Zuvpp4%@8E-uJX}G=$7kwKo zL8MC})H{-&RzYSi75CAW<5a@`iz`k~x~CY_^v)5qH+|!;$mVy~`oM}Yq-G|m3$-tz z?hq!H;!dqSYY{b5a%115XD{mmnYUus;lovQD_AiYXJbJlNCC6VpW^nZ7k*zdewV!+ z{qJDOq~~73+HHDj&JJD;_kMEY%Y%bc;CYpxLl*)7u6u#>w?Eo*75^w* zd~^iW)CW;p5cunqjyleG(8--&f(OT((%0IW)Nz0Pv725}Z=<_EG!38g(3z`UYxhDz zA%3KMCz75F)kNEG(!YRu;tlGjBYzpccOC?!KbFlMzjy7pAJeU!xX$S#%TIAn(Z_fH z^%z~?`E$$5_;TdV-_sqQnWDv0ESX`|HV&1py zOMe=7+x;2ep&;l21noo6Xav1>@S$4*N{Q<~LAsajum7iF9VP47)r=VX)y4ct-_jqu zx|Bc5__Lfpr|{=g{xtCCH2$2anYB{ORCN7k@VJXET5L_|wmy-TXP9 zKS@G4cJ(6u4Dsgxe@6K;!JldV9O6%+>0?)~;?LFmxrRU2@#lK}+`yk3`SVf!+{B+x z^XF#%+`^w*`Ewh8Zs*S({5i~@JNa`Lf9~ebJ^c9^f9~baef+tfKM(Nd2!9^p&%^xr z0e>Fh&!hZFG6?X`pTsTE!smYHFmMh7=P+;%1LrVs4g=>fa1I0KFmMh7=P+;%1OG>2 zU<$UbuZ6L}_+bWMmcrZz^BBxFm=|Gw2D2B2wz@xr89mI{#V|8q95Agg3t`eQ_rW|3 z^E}LJFo$49zra{I%#|=k7za!lJPhd*1Tr(AB7EB$? zY?uWwQJA}6z6|pu%r=;pVP1nd0P_LN=$9Cq0y7Jy0j3*f5zGphbuf>?Y=sfOC&pRl zlAG4+_?I^PMFD?`cSZ1RqGe3{%IH8A{KUILxHrL=#qIj`*+7OP#GyMaG zR3g|LHuNFyXna{jN*GK>;xRp42vr@W5CK)y?jU>~VVARl;iS-g}LvJt| z9!i9J z)8P=ujE+UnYNgS{@smN0YMjALG#yC~B*VcFFx0ywH~xw7q9zIj(?Jw>!nj~hES?+$ z0usr1e=;~|NQaYykytjKjl4d2+b0|Y2IH~8a4ZdYD65gzCvV(fvOhx+Qv$z6GvTQ( z8b@}qenTQ2iKRIiAS4tHDfn!U^d{q}cwgEuHLyGr&7WGaD9Bp%`&NQ(qf(!ibfgpc>>)n?Ow7w=Cpffw|ID1duNBY+dcb6 z3iov1==V|?$=&Jmxx3+m+wJpV1s&nFc+|oU%$mVwkqsmNup)h|Y#u8shavY)*t=AB z^Nl!X8O9$>2KzHXj*B_T40n14BGE7ad3!U#P&~=p$$sD;&5JO9IGLe5Jn?9JuqQ%6 zY;HIfUXck$$1KA5^SgGzTT3x~ppM>eHz z=|#Oz4%MdWu*PIC){D4}$#AT90CptCm6z^Xf_;gr8KNbd~NyJ&oA za0ys?CY(&iXQCP6ERmeqJD@Zv+y>1ZTftZ?ylm!*FmR4~55!~r$TEf|f;-B?`cZ(i zp(hzhEAYY;XJhM;5A$U09aOP5p zrbJjqrBC5k>SN&b(ZW>v)cg{C>tPE02|v**?*&vIHUB|QS(2aV1q3AC123UWGDua> z!q9m%S{3oaQ9~*iUCR5B7N+(q;YWk3eI}{(8S$={_Y00bEuUVg?`iSWJ}dk^@n~cz z=wA52MYTShKzBh;52wO2!3S4F&|`>P8F>1#a6ddVW6R-Ap_!zxp7;{oAlfCeb} zOwpc^B()w<1oZ^Q`gy<8UJHLz>iSw(q<0`4j-}FJ^d2=`kh(M#uaEaN9ggbHf}@6^ znb6zxG(FVisfKE)W)dChZYdlU7U5d|Tdwy%@lNS$djAsP)OUn?F*qDCGvSXU!hXR@jS40xxQ!N;0%HlH;fb$P z|D<@S$Y3HG>5GIzdKmS0wY!UWWWx+F4Y7bBa^(%gNq{j?HmUiwFoRk-B-O&m-Rbc& zH%xQ&;9xa4LI}SZhIaf6Zo<4)v5@EPC|OpGP{ z1P3ODbjt|_rDsUXkcM>y)0t#Q8A}_J;NHlwdmxicsbTZNA>du{bff!`s~voV;&o+I zJDA3}5}|Yq@fqS#9D2`!*C&9x>k}9r_>ac)UwodGy$(a}S!-CC83u8Y1StUkZ~zV^ zLkg4;MMa5GaFid_Ky6Sd)D2ZcO{v28buZxH8Npn(ft7t0hC;}{{y|o@0Y>3Vh+<((rNNw=pkf8IWdsA%wEW3cFPyJuHIQM?A+&63kVL z_YWzon@<*6z#qO=*d0xEp)kQHDDp}2xucjwF!mI=Aqs`rt%Zm{5-}H~F(0QlQ~{pm zVycVu_T%I7VQi2fG^6^_uqO^-9kMy4>Pm-{K*la0ej3ahSSF(V;aSTxR{BCDRl>Um7B5eps$6)UVS#wMSQ#h|CdcdJ z{2h0)w+iQ>`FS1KzwmIS8NON2qD_-?3E>{rh>2U&ok$QpxY@T57j#5Zz;FnCJ{hL@ z6}qV$-V)Qozg0LVHozMtcW{^4a&vP0XB`sMej{c5?qv)J3J2 zCwZx7hZXPkf|gW$FvKh6PbXO&r6l@vheAmrTlQjs5A!;8%7!-kCTomlQUlbA^_jjt zbPmQID(s4e!J!uu{m>gfO$w=e2MV+{>W+(sV?QqNdD?>srL_TbSoWiBx~LS&TcR_> zn42AfIF~R_G@c5pgt?M(mgw|()C+VU3Hjpas$upc;rBqW^auOHY$`!i-YTTamZ5)V z2D_F+0vc3afDqZO6mJfujt$|yAn48KX=#8AO`$4h@M3Oxec|kMbWUs;W}PfS@4B%P zvjEEt1RGNyg;QLbohNE>1!i7@iC{9U;o{2d5X9-Bx=FM>7xD|OT)0`AcXo%@C+4&= zZx`!Q=kH{!0560ofxi@{42DLL%gAOcssfk>rTNs@1F3qToe$FV>fG-f2F_vN90tx| z;2Z|dVc;AF&SBsj2F_vN{}>Ehbv`RQVpZ0Ys8Tq=44f_o_Ta09VfuanKT5-GQf@fOk4Tf~%&`sY zqp-_h<9rl$;Jd7hk`BYBI2&M}2Rj7&eAq77lVO*`o&tN{cUaj)u(!d!81_oom%wg- zJr(v;*h8>OVbg;5QIr$g66_G{qp(M)+_3k;hQ$o{44Lc`{roxa2Xg$j9RG>noXzmx zwX?E(hOWOMgIXTGuW6{YA;WUUK6s1}*-VCHDi8y9ltL zX~n32%Iu<`u$X7{G$JgzYx!MQ;644m{+41t@ioQ1^YQ)Eg_-O2Rovc;_%A&5+ouf5 z>*Jnxexd}qRQr3U8Q-nOe-`n7{^WrtFJ^B)Ir8L9Cx(Cf6FkKYK%VJZo^(y_Q)VB zo2I(wEWy~Iy8k$U`{q^mulw*Zsp=jGDr$we=mWe%F@$z<6aCZJg63lrV4yq5Xlx={ zErlWCB%8v>CfcQU)Q7}7f=g-1o&-a1sn8Tg;~9-*WGI~Io5JaW2=cpCKjEAFei%wK z8;0U_sWz6PIs6-8Q{J?HOYa9^3@`~88UvFsR9317m1h+UmHAH9z6&VF-Y!_ zXIYuI%g^?!whj82^fQ^QX0pdCaeK(U7jYU}I@li7&W&5D#OQMz4MQQTdKsB>Q>KIB5u9ccU;^tK)SKlSViScBTj?Y&7!c~bGna< zJ64Hnr??@+@pib`d>*&!xVYn#xV+t56t}D6gmo)b;!5T9FdNc%+#ScmMaM81Wjr*o z8Hn4|-F{r$QYEegw~HAFZu9YNF|HhNRXS7f>tW>-cXrG1aFw_cTpuezT;H7I+hQD; zgDI=s+>9ZvKTqApq6381)z4=8VSAc8*j}<1 zc-bB`d|0)&!|w1kur08g+nU%W@-OhQM^$@0Y)ZdcwTEE)L7NHKU31&mBG^PH-LTs_ zXR`*?Z&Ur#V0U+S;?Py4Jmv6Hxr$*^IghF~!#!Bujydem4;6c_YVYQ@o9%?%;j3rc zx!=RK!CuhZ%(im7g>8oIY3^bh)bLfRKdQc;&;8x388*SGfKB;L=eC z{D9uqvqRi&Vf(n<%Jy)30o%^)F7_z5yV-hfH!)ldz~K(5_5ke0+4XEbw>_+Z+dfvo z?JhPAHu1hPZhP3#?~~uh4spAS?St*@Xkoiyw{&;0Vb~qO^A^?K$n6fc3bwzyo<(7M zySmsyZr3v(w?UKK?qt)r?O`R{ZeT~yF5a$2c7WTU4{kTH9o%kaTe#iM9_4liTf^-Z zHpFc=3vqh^>*n@s=Hm7oX5{vbY$~_svQpSxa~oMP>;^BS0}AhG;_XlEa0GNfwqeu* z7ZIP&{jI_6Dil_MBbW;@I_n5{5dU^c^Sg4qDG4rVnB#a{`NhKa%~f|(EFgK@z)U~Di(n5i)3FjQWW zDdd%GvN=oIqDxt_IhjGrfzpGn*^*?s|8~&fAUx%{oR?8>WQ$!(h z{c`wa@Id{Yk*XTaryhp+9?WYne}g%%05Sng2MnDRNw6qQH4Mp*%U~XY*#`3}%zwd@ zA>Q>c?J#uG)w$m}44lKjISib`z&Q+@!@xNVoWsC544lKj8NdK$BZmQIn$cmr*SOyJ zxbYjt?-;impEvF@?l-<|eBU_MbfM{T)77Q{(+bnCOn)^o^HlRRbDh~|USJNJmz(b} z-*0}%{0;L<=3kl*nEzrPYnfrGv^Xs{S#GyHYbgd95@yBt4vyybY?amewZV^Zyf zwNq=S*UqeMt@YR5SNrAKEw$gNeW`X|?MUr=wf|i^zV5ubi|VTCYU?`c!gcYwHFfXS z6*^0tWzJ7I>zobFKIaPO7Uy@JyPW@UG6U9zF`p5iznK2pRAQcBo@|bpGv*(e3oUP3KCxITYAWVb1S@W>*jmA? zldU&cGuD;X`>hXHpR#__`i%Aa*8SE))(@?pSVvcuR+d*@RcWofsdAulY2}@j8!CTT z`ATJJm7%Jxsa$f}s@hof^{Vey4Oi`|`nc)}+ox?7Tgbt7%tNwEJBh_20w^t8W@2cKY{m1Hq)l+IL zHJ+N5nt3%nHIbSvHGir(RP%97nd1q^zdHWS@tmWu_S3cYT4(LUb&uD5TsPYJg7YWN zx19%_%!sobfR_i1PZ-Zbi_bK-nCF^%%}dd~Yf<-~o8L4aGmo-dV=-E4EG|p4C16Qg zR#@(`+-G^v@)Ayt{Eg*+kqB}X??@`j`eTWe^|#?&aX^VE~~twa&_gJ%5|0Bt9+yK@0CX@SygdWNmXf8 zSyg$}l&YSpfvRX#qAFeW+p4##m~D)$)K+FIx0!4=+3vS(ux+$GYTIOc+O`>(+iLrV zjoFLsCH7MLTzl01S^J&#HTEyrH`+JZzixlV{(XCi<5P~Qj!H+3qZL%Q$g#w6n`6D> zE5Os&9a|jVcl^q+&+%u+UmPDf{^^)dJGpjV?LTYZb^hJ?PbWHyOZkkd%=jte45P(p zH+qaM#tvh*F=QMxe%`pz_?Yn-<7Cr!Duye5UGck$KUDm+;-iW&*7L2?fQ341gSExl zX1) z+~+*({DjKuSK9Vc<7(p<(Z1g`{uKTCW8*R7B-53qnV|V*Q?Ds%y45rU+P}}V!Ssmf z-%YQY{$P5?^nvLgrW-&951PMa{*L)A^GD|UEYmA|6|suXR&1&`Sn)|k#(J0a8`kGR z?c*!2t*okSsO&&{E~<>7CI7YZh05Po+N(ON?y7pXYLab^Z2@TeUfX)xleTZ#eqj5l z?M>TQL-k1Y zq3XlcA5<@>3D>Nu`Fzc~n)NjsYM!Ziqh^Am+%W}hYH&<*On1z17#%Ads~oExYaHv) zmhU@awL`TVYLC`h>sssP)orMIw(j}5an6gJX6Jn8=bR5Zzvuil;c5{K=MPhi{l;6t z89p{L(>1`+eBfxYX|w6urtbqgKQp~%+H3lw={-|{xx~D$^6kpMRu)#BUv*WLt;$o? zQMHiMYo=;t)xA{@Rehss3;Ou?t6r%3N!8D*epB^k)jL%mR2{1-wViL9V!O&V)8?>s z0yi1kZMHAk{sq{1+jhBqy4_-T+P!waeW5*MUu;i;j_Gnqq1s4otTt79J2>W-YTvIbaE^9Pbe`{==A7xQa5g$` zK)rjMOPsel|JAw2x!?IG=f_n41kMma{fmrO7(b0Mq2Bm{akud|#zV#rz#)rFlS~(z zt}uPtG{fXF1x-s$YfM{AJAjS-rV-%cL(?aw3FgbqSDR;;tIYM_AUA_{Zo>%iu=$(j zADVZXcbi`W6}@NvySdnMmE~HC*-~ZkftU1KQW!7pv8=Ux$?}Nh`<9B6=VB>u1kzp z8Eb*1`NqY@zZm~+beq~tU8cC{v!?Hwwwpd<{-^mC%T~)zEx)t;&0?-tS25n|w=S`M z&ib}>Y30L}&s6@ha)0GPl&Q4p@~UeMhkjs2*Em z2c17sv$+PB1UVWVamU?`7ahNIjH|s2ZG1Pz#Q&^)t@e%DKhz$p9aT3O#?8A`@tmRMWBR`@iyZ<#xH@heckvi1VSJ!-wM^76{7E3d1JRz6+% zY~_Dcj;eYXt@*90=P`O(ZS!n>;GeJ9-m!gPJ7ybeKhHkZeyu%VUjgp_Q+sRm*Q&?W zTv_v_n#aLGcVKipTvOnf=y(_$;;)XGX#L{4Tk1k}&(u9v_Y$OtOPp*IBUG^4!8`AR zeDsj3uUiBx_ zA=7`Ej-X!^o5#_v^j7e4SW#=jI)w3KwRMfv2dQX&?LtUJTWfdJ4%hCk-BY_4(h=Lk z*b&4nHI^BtVzbv}Y&H&~4<1F|E47qcrdp;!R;#c$EDh*`-Ij%x5LzQ`S!r2qS!dZ| z*?j3KwmB9;zFUK_b{nL;{f-ewX>EC}t=0v86|G%g zyAd_m3AyfYZF$|)I#*qDU9>J;x3O+h-Ojq*b%*PY)J+8kYX-fhohzN2om-vTox_mq z_Bi)~hmBywJL05{N!MI6V+m?L#b_{2H{w(qUVlG&>LTNSF<~4su7dQp9;5XpwC7gH zyu*-riKh1(N6?Z-!1o1RPd6FShAxbgevAW)(0&O%R<1FvN84?}sIV2ieHiVw2crV< z!oz4YhLNGnJjHAoAQfw(f3zne`r=S%LXvgVjNh9R4 z21sT;iyy5yAMLpaEt;^DR+Lo$8|QxKFmMh7=P;0k0b-N1*Tza7$old(E7JduJoYk0 z2ac)?v=ps?|2Jy=i(nsyvYAEo6WZ|21lz3oVHMz;30AKA*<0{mulgze#K}mb`bl5S z1i$=#QO^6j;lE!`UsML**XrpDwnE|Dr23)$Rrq`Op_amb@E2VKy&lq&qaWpe4g9DQ z|B=5E{)v*m0sfh)pR8NqC;dA5QG9&&k>!5?e$p`%zbAA2&%m#$%rfAwXfBj|N#9TW z0y+Kx_(_+Ze!x$`UGR4+UV0Y%3Vzx<7QYYR2a5QQ$~Oit`&B=z!WjIUR6pJqOuSst zMHErWbNs(DWaU?M?G?DBS42kB8cClCfT->8EAP1)I-!c2`Jrq|So!2@aATAD zo~*||t+yAIcMtrceF3xRX6QiOsmCvR6Ml(43MYI<*F8Y`f*SZ2s`243dKi8gznkEH zT6*6A;z5uIDHw!5D!i<)u=6@a|CEBLeEsl)g!zyB%i$;alYWGQufgBJeLQ~Q-{2SU zDZT-bKdZlIUok5C{uG4tg|EZktiLZXf|&QKeuDoa3(m^X-?K50WDvxE1iu=7ksrJT zBT!@&^i7eDzz-7CesjCzkH^G0Q>LGYZ`0K<=*jwR8mH3a|PjWF_nxXv)pJ`z1 z7n&FEP?>vGKhe9Z1?QLQe&#}D=1cyK@PnlI5AX|iz%Q5YMflOB#e4QU_<3f0{9=${Y8I)KdkyuoFAmfFCI3Kf?DkAWPsM`4_wnV!v5`UucHsZpr^W z_(gmw&wlu?f`e!Yzw)90zSB|M6jpQx{CB8+iuMWo^}3(^d69mWBiE=uB;SqzzMj4aH%@28AG0K@ zzZF3DJgdDn!M|FCM|FNT#@KBtJm9zJ#|g$hujj{F(Rgzu|IKMVKMKA$qw{0(KLY>d zRzZ0OU4;M0pMd`g$^UmCThMPY!au%~F|a)TqxOF5bJ_HMGdhomkN50~b-3F~ zjSu+cTi_qB``L5w{{ViCUG1BkXTmB#sO$yq#woMt7`RGc_XD3dro z&D&p4^u+SzGuV^Lo6on+dQRARayCx{(A>pogit>HN)I9kI1HFS1e3u&mG^VL5Z z#fioOl~VoRzX#tTouJLqI6HG$SP)bp_?SY2zW@Wf3~31e+kyWw*i=XQFot+YzWjp$ zz4a-jfc5;{kPlT@n@P3T)ZX!`@L! zT{aU&g|DQK_hs@sLKP#@1%uk;E~sf1VR(}b5rfaOxtuR{L^+sRulIs{yYpwrkD z;LCpCB_F*6)A6BPdNF(!%n0%G(J}Y}j%^j>ujk=k0r~a>o{rqB6kU=0@hoAMY0#Qj}16&7VeY4isDdkjfJ2d zg6Dn%ghITEU>T8~+C3lcaM)~mARJX$MD{)98glK&`mjPN>A-R6>g9YcN?W} z8{E`4D2_vNyWqw@5BEv#`EU!Ei=^fRCHEf5{hH+73pf6GIS;@g@Nh^9KO(t{)qYF3EtTBm zl6xxLqK*d1Jxy{?m)tWXw^4FeNN$_tcEC+#CVE4~T3 z<99s0NRAe|Q|%A$-h$HxQE6&}b>OFj3vzFSTeROM$-P-}Z0_jb4i%wf1iTkny=_e$>ll6ypQAC}xl z;1>BFmE4q(2&b>iMZWZXrO0#B!wS= zTa>dDlUkA26u3qF=~8$F+#-In6lcB^9+ljyB=-i%y%}yaE9XC3;TANxO>%FCTa;&~ z6o0oAz87v$oIeyctP$BlN31Q-VFD4*yLUWB$R+jlDiZ# z)HK-n=6;y3+^S3e`kZJ46MssRZ)}W3apZS8sE$;+%(@lh41JfBFCUDjiJqhw9Q8B_ zq;Z`%O-vJwfpIYg8&965HT&H9U#*16b3HW85aX*{$Y(qVngHkM_4iu0rB`dB#(?9iva3eoJ`% z9@1QQ0z3u!aIkexrcdBW(7@|H#`YskJ~^nD&NEhPMm<%e+0=rw8ctBQ!SLW9j^Nku zA?7um&B`1cC}pcuIfrnwOT|sT{DXn)InpRq4x@#pP5>fU3jvGTmfVZr7CdDDZsLQK z$Ig@HF&NCZX0pSptO4i4e*m3bL#2e50}YsGBforgiKfC~(Q5@RscidIzMPLXRQ_O+ z+lZbY_0Fd^N$!$d4Kx*8* z5qP4q<--%!BfzeM8s8DL{X5hXAoAj-H<>o}X~d%UhriTdEJ6PH+I<#1$^KzUq0XhvEIaT6^n%!OB9-X#>N~p`CFJmAd={=%NqJ`3(3CWCKc1hqh53`T9_* zhwh~ljjrRxaE-z@^Q8e^^eQ4pv36H>CGtVJd7j7*PQ?wGp72HCt4`8JgFV@6f`ZNs zS15Dgd}X0=JeY_6UNLGCDy$rnV1hg6Gi>C68 z1$!=4)@k$M7q=tgiiP9pV-eCMP_`4K2}QI;KD~brpu$tYeDm=hx#h)}gKDs^V6yP< zC&*_R?#vnN$u1kUg`e_)ofmI|Sb2sdn~OIC^1+gA-ui&XqG2I+f!+nK|8|L@`;iX^ zkyJ397HmLD(~mUkPLL0-sM5u9q!IE+!^O&;EY*$VtX;6lZ9rdI1Unx-2LUN}3@3cP z3B>flk*~~seRMfRHa;oM%}8@6?7a9SxeMja#pfQ(Kki4GeEDD~%c|$aDvz9u`uqqP z()hd=w&05+a8qBQ@IzAgQOQm6uZTYtZt6P}zX1=M>Ru_iw@GpKN#W%{>g_~#@pxF@=WWDO1 zdJ;ZLykRiauQsGUCiGmO=wcA84*-ku&ex_iBFMZ&j44JiFw*6r^i$OQ^U+=~pl#A2 zrk?*F&%^l?$d}TWqb$XMm2XVLO{Tc=O)wC>zg>&;-$&XLq(P=)Ak*`_7-L|Wn&%$a z(~v$NO-69(ir}VlneW0n@MC{&&JhVt1K?0U zJ{g=hB{+v9I3GxGW&zHr=x474XGDT?Sb{U~3eQhUaCS;?c1v*X zIt@6FN^piHIJ+b`QNTG>zj#Q3vqOTjQ-ZVLH1M-tg0o$MGc3WG4LCG*ovgp#C&Ae! z!Pz0fsRNu7jW2@#td`(xmEdfb;LHM?Q?=La5}Yj(oNW@Esi%RTAqmcA3C>mtPU&fs zCn>>sT7t7hg7e9Fr|w@-3C<=7&SnYDdw_E){Pan19+lucEy4K%;GAl_SR}#OD8bnz z!PyHqScW|*{Vb5+Y>?nQD#6)(8s+Jh;H;P6Y?R<^1)Qm;C{Kq3XPpFRg9K+I;7mCM zoMs8m8VSyN3C^n1fKxBQSuMd?C&5Vo&Z*knA;DQC!C520=?0ur;ipo9vr>YyT7uII zIH#f?qXcJ2g0o73<2VgC*Gh2G5}cJ19OG%gnI^$WNN|QEI9CD=yeFmS%Op5a2~Jvq zQ+^t7rbuuGBsd8P&gj#CbG`&8B*BSFa1McZP9kl^?vI1a#p_oQ(4NpM^e zoMs8m48XzvN#XoTg5!|jG)QnR29Q(LYmWrSCc$w@a7F{pspOVdBsdik9ESwwL(J&_ z>ZJABDZw#HaBLEs5x{}>q;Q^>;LMQVR7i04o<@1LOK_%3aEub1p8?LP#`o_?aHdIc zW=L>e1e{Zi7h5Da1_{n|3C_0Dz|YquI5gH3!_Zp9GzrdQfOD#Tx=DgVeNn(MNO0~0 zoKw-yBN7~nE8t9(;4D22I2$B5WfGhz5}bk4z|R*XIMgPhJmnG`Kj55-|E!VVlt^&O zBsjLyfOD4whsq+#Q!2r^_B86XQi2088k`ad&J@7O$2W*R&R4&D)4r|2cR1Iw$z`-% zNb7|3qw#3u^7Gpxy~%hg-j_B^pZytwucd?b_6Wu}tdwqojpZkNheo^MI9u6My|5wP zo1vS*QwCiAZRiT8({wlbLU(8o>%7>|fzEz%Aucdql8z@9itm8%rA{j#jRG)Fc8(VF z%$`Wv;JMy&-F1de+#)_0Spk3sx_}xBmPvzpowdPao@F%7vY9Gvvy8LY_%bTbewYI= zf~JWk3-}uXc8>*&`BXvtu7DxjVEc`gARfUa7!A>A zdoU719~;f@=psHlb|4ZKl{PwA{r@NVU!&%qphDnB>%%<%iCOon&m+1xTX`OZ?3pJ| z_u0&o)}56+ap$~`-*rzn&Y#hG&QftZq@b1^7I}UOd3GOv&;6OK@0Dua)Til3>)O1& zV`rzMb7D|s2=Tstw%U}|zj=NWP*;^fdWYO+G|y|*I;NE>GFmI=d7h6v=cK~PhHzgn z6HWU(E!YY^bM%{=FRiUB`Ff#Z*|j{C4i73Ml{%|5Ol$Ex--%f?d`9!ktt+kD^E@ZD zq~->rk&p+9S^i)@hM6RKc)Q?rCrfy z8X+G0pkfsI;Z*8Owqs#JJ>=gvNBphmq^+I76v&FNUH|@n8 zmv=+>tmK{BS84x_=RKhjpQJ_IiG+73l0LKZirluQy*{4jMJ>=1j6mvL5%z#FMWD>u z74D@bKclpk+iz))kmo;^_>;mY&Sqbty+kEH&VFf(JFE4hJx88j3DvI=8v19JesgJv z_9l6L<5k|PWO-KWNc)&P&v8^o4_4vQ;WOEWxpk!dPNj~N9~L{)$@ua!Jf`LHB-$J0 zdD8lZw05Nk+Nm=-pUBOd_EUM@7v#1R2A+V4GC{t!{mDDHFRaoQ*5`O}(DdpwkhhKXNw1P8-sbS|0O=CrTO^Sz)U z9K{(>XLCHrtuyUK^Smda&Ryvs6dWIS7%+bGGS-_VJZ^qD{|IJ#*zY+VkgmjzK;D z7swc-8^H4`L4IdpKAGE&q(h*!BhY#_+A+7EkiG%WllXmBKWPb_&HhRH2|VBP^n7Va z1j`|3b$laz2A*$OcQ}dtYlw{^+cTPPZrhUngVMG%ztXZiqj~0zsiZ5x^DOs;gG-Ma zQqOAMq+g-r?WOs2?r?ra^UmcHq?5t(9!Ja&ONp3Jo=sXN-3^`}$q(4(9mKwdFA@t2 z`C%Nihm1Edwh!OweSmK{E@orPFw8S$o>m$Vvox2<%&t-2(HTvJ>_VqA;Bf_vR$S-a z;z})xq*BglZnq2HXsPjbV#BeBO$-DA%VPMx7}FB$HURdO>U%mnp^%lUa7_MIPn!!@ z?r6qjP*xC9r3if4=c(ujpOD#z5V!DG zhp-xzL#uR-oh{`nvT0pd=!S!l*IDIcp;mWt+TxW}i zziy!LQT(VL0fWwz;V}3CGCGX(=aq1f9=_4#dRiO?ecN2f%mT;Fu8s~*gUdOsp*@j| z_a}pcPHPNvIHxreTM~;eL(LjGRZ`R1iOyatWKuhMy*_Xq(N5v0)0zrKmj*-eWL7&V zYa9z0Or)w7yMe4d;*J)@Xbca91SymAg0H z9qiHg%wdkNPw6zS>xFRqi?Wn*-iWez0z&A)h{#!_HjdD_m_<~d4qsD%8j(aJ#1i%0 ziZf7@`r?!md{`U{1$t2ZWMUK?5KK|LSJvwk8c|m3XtdMuzKM@hI*s@n zJuc4x=EN%AMW4Te^Ir6M3@c&Id}AR&4|Kj}A^Vit&K15kceBeMPG(dPI9pBWpLAAU zA-h)el}4AZt=;FsU>4>4D1~pk2b|Vm9~hF_v*}!SC9Q$e@?~n;c2~Wxwb9kJERtS< zO#_VkA}>)UI%gQB<^ub^{E z3)!ay4jWt^pV#MVh?7bvF;463a3a$ajr6Kz5wtji)2tY427XuEpTgXMdvjZvMbawcDg!S8rxivByVKV+Jq@V zTP_dLusWV#EWsJ!y`mlI%+f-3kqWD-#oN~CYUvGYO)kbdIt#LpT_f<~akY3FTV4H_ zYXB+sXl5vkBZe|bRqD{&a$PpxX>9U0xY~m_^F9ZE%39U=0k9N#r7DZx(%H#UKk!4S zaY=TmYMc%d^dv+&a1PzsE>`) zKsQA+?goa^0V#aC9FD^eME&Uu3(i{uJ&;Tx(qfNO)QirXmT-tx^aF=6!H%?ebT+Mo zr^e1sud6W<)96C*4LVD+kX^3QMZ2rf*W9MFoM1-C^#WgXCTbSG>N`8z^-hT{45heW zRO^=_pCOKCJs)3dr^MPaDUm?%WgldcB9dhTkrd91g7R5{GY7#N=zQ2LImy@JZp2I^ z9OB&w1A$gm+zq9)**e~TFID?*zWr2Jg(xqbRa(ddKlQo1O@6Tob!U{O6LILgQ7KMi3xx090j0Nzve7xhGXH4uHP^c` z*s=_VoOURKr^8CC37B+#whXfwW$Vf)QjUm2=WR=I{4I#n9#`UseCeE4nO6Mmtt~oE zB1u7nf{y9@nIbkW5C{(TM3xejD36JUT3m?6f|i?E9Aze^GY*T{cZd zptC6pXs*dwg*x~mFF{Mn`qf05L5kHYq}KZzot0O>##1>{{k%u(@8$w81yl!;9;mZR z>E1&9csfP0pzTvlhH@bu@Trd#nqHw!V<8^ray?F-Z5^5$h`vwfeHZZgE~M>xrruj3H;bN4;E_| z=7xHo-{o&tDlTAc16;sT+csB+%vB+H5}ms*%>f$SZOyK(U^VB&?NR#HBOU3)v-VTiM$>ylx?@X!4jSo1#O15%u>x z8uU8;*6=#y;pv*5iasx+^WdfSZEbcp(R`23_DS9n?MrtgWbr}|#?EjLZw?WM&R#5F zla;j=>LzJy;-m)RQFLk%ht68p*SO4`p4K*(7&$~7ItyCLtD&jY>+1CQd0qk+o3h$T z)+6J~C0f6yae>d3Xstt;lkxp5k=IS4%qg&soH7d^vRs3=(d%kb zB-gApvf*r8`n)YoBpYS58+{-p;Tz&|lM72QJdThp=v=x&c9lw}epgddTazn^jX`|M z0X{+-XHKhHK~a9XQ;G9Ans)=gvFZxVR8!nXZKP6NqsKWAp5edt_#iC+9I zzGlb}N=70M-Ps|Pzs1|q=xS9LghctL@ivs!JKNhDJesTznU17ob&*4qf4H1z2V-C$ zm`DWZ+8%v8p?fNL+x17|J>bL{{#l7x;{oNoOrl>`d0N}Ou4boYaL{QvZe3W>d!HDP zwBEDU1a@cFuc@QiP4f+o1i?pqJdU<*($R|5pbi}JjEkd`i0Ch(UUV0TB1>Z}jv6q~ ztG%N;rFff(3#6h^?H!$Itff;IQbil=%r0YlQ*%AasIWVcukszvgn;%H4!WvHPj}Hs z?dBA7UO6Y3c41sr{Q<+C0Q>IetVO<@=ZmhIx^a;Lsg1QeN$?tte+gL9xhdOLiQ81L}gd$bv(6-AHi!O_=wAJ!i$C zd!Qtov^KZ6UEK?K6isF<&6%q-cp95^-lQ#yYW$ZbYJjPZ!LrtZ!?fc`J|8sLCO9c4Q&5sXW8e;cE1In@A2ZRUw`#hhUyIlz<{3 zxQgcjdc`^8=XZkmip1RfZxb2;QXVeNG5yTAwbnQOxh@{*A2peRE5P zSMFrVNKa2#j2`+L$6?-H;8z&<=v!7%Zn~RCmbu&@t9n{C%nb5!i+KAvtxi#CHRqc% z^jCpTI`=gTpI&!|BJbODd}d<79<0z>Y4?VX{8UtVoz{V{OVFR9UtAW*m3jpp=&qP7 zJk-~N`)PX)w3wI-Vzq-;0%aGpr+hbee&F~T=wlMysYdfgW!^@YrRndA_&PH7t4Kzp zJv@BBDPV8qI1+P8pY(l*KBuJf#pQnNZ$rnF_5nnB1s;^|O(q4hq3Ls?eLPPwN67jn zkIxZA*%ZBray~y-w3-C3HELt3OUxs%DTYd8B-UjNx*Lwm7_@n*Acn7d37Vb2_pTtH zD4SG)=n@@g6KfQF29!hRv)&!ybcy-sU(40@!x~*}?d|QZL>&5o{TbYBh}mE=xFQl2LY`=AMb~2@--s5iQ-N~h zi!D%%UbY86ui_I;0jzO+ihfFWA?WhHsm+glaqQW32)YvW`v8Z8@U;=;0B;UMlw%#Q zJLaWf{v|~~5wDUK@m|r}o+QQw(x~E_GJ^jO^ZE;U)9xkyixyZ0aavapUc!J zN~iFmU_|87zARPQ;;l>T(+5pc%N18Th`yJ>BnB$PDT@+Tm(#srTvZIHB#PiTH~+ z%**t4_3CFVba7>r6vwY*Zn(J)w+diYy`e&TYJ(f)DY%*9anGsY!yl zkoQArp4nO7(WJxdiAN(#)omHA?sQkAB)fW=eHfnNy6h_CGmx|*p9O}n1*FRv200va zBtb)p{@i43BUxzA(I6%8PWMIXd0XonupR>i1XDs0MYMI4Bwr^Xof4X_>m5$u`C&d5 zVNBtA6qwJWOaf;NZN~YHz6R5U&xo{kF!FYga8}>ZF5xUAk%3ljx@%0LmzIu}R+qfq zC+bIcW9t0I(%#mD`k{J)H;XbV`rQ+0d=lE-S#$6yoS#6>;PuD4eHc5Lp>WcNLv;j< zRp(PZX@5qWS7X@M+hZZ8aiK$G59QQH z9XYL#9a3=;?_3hz64Zx<`Z0%W>cY-I^h2VmaTl1P_i41h85S2}aL*G4Dr^>8Kw;O)Ap2Tx(n9`@RbU z`FEpq7<4zBvL2D__r-fjVM7}mcJO{r^DMqL1wkeh7BrFIJVfJHZH?IVO-18NgQ`N8 zsMl{Urh1K5_Bnu85pKAK!p8)Z`(X5Nx>H9Dhoq{9BdO@Oy>h=L0Ylvb?G2#c3i!q= z6gafZ5tQK2eJ}){!V}n674hl5HxWLFHWcBH-=>5I_~p=g`0y9?_yeF%LBH$y{EuWT z@Mf$G^gwu^eFf2;FD<5YXird6nlzMB`nwh{y`w!Lxo7eSQJ^9w(TOle4?z?Y14HS2XiW%@C2?uD9WyUD=~?RChaQebyLo>m_&0x{k_Bo z>8=f(51Rcg?H&>dsKfE~R59MuJ(pQ~to}~7SLg5H-i)SlrQsDF0=NNR7)*!w2Q%mi zOMp8urr9_>>M~S)0~VeX%@$}qqLwPN5_NqXc;b6|P2TRn>~8Pu+1N({js+fxuAr-^ z<6G4=lK0Kl@V3Sr2J#WVgNBcf%A_zP!J5u_gxL4$jiq(Ar_n{(pxX8jBKSqZNH1uH z-v>@~f#%|uf8$-H%Il(>hfDew9;kb9jT0qOu^o-u>R)5e9idZ;Wkc9KP+REQ z3gX>t9JtB(1;2bvDT{a)qeuLov@qq)+?Ct=hn{G_H1PvX* zJLnwf{X6e&pw!Q}Oe*y=Z=+jUw?W0ShIMToxSrFhXlt9N8B!URx~;NKoWL>N^(@g( zXDc*dpj)HuBZ@vx_W>6&t)Dfxnu*f1_UnaSaFDAPt4vSO-JWcDq_e?W@0uHqg;!*t z*QN4DK|jj3K;ucbLD@pbWJ>VHl^l11H-ecd-<0#Y^!a&o0R&7FZ$Gq$(3!+SqTt7i z_&bc7(mONq5x7yl4d(l{$IrOtxr`#-PF^;#t|Xn)<93-VBzU_{6mMR3JmtIKDa0p_ z&liJ%z{w9yL~Fnamu<0S3+YzT7}fDc_jwku^AQJod2-RDa?w5G`WRYS-{EV;G8>e$ z6;*M4{tbLdJgpuy0O?X0ZG~zBmVJdTw4~&q#Bm`{(cczv`5y8>AjBmS(MJw*dXe~Y zLzBoP*Wk)IvnG77q!#`Dqk-F%HrUfLq?bl>Rr zs${9*8KNBO4IFY+w4=3NR+)!1oK$x^b)Exp2>cK7U67=|Kr#*CPt;R6^W&1hi8Z}-TG^0QPf(wx zoutR1yFq~qb-Xoobap^hhD%y{+ddwlI-+d>0*eVLQ5GztA+6ZMA^7q)qb(tlbE;LI zK8^D*O@G+qYHY6WbkSl5)+eybgk_y3S`_aI_N!E?=f8%pS0Mion06$X68*H9=P${# zIoh3qri!^PH_|Ul^baP|Nzva1czUt^Lv)~P;@7F=raRONnNi3iK9{edO=`f9uDqMf zQr;DL%-~}$^;ep&CnTv&Z`1vp=BEY%E$#kJ3^NVhF3;>1e|P8XfVkmFqf6!cvz=SNN!QyiUA&X%D){Bo7x_zBp zT|y&5pFjxO-OcmY_sM)7kK4u751_fGuG9)#{*s?>z}G-Ay@fg(&2ZhyzLkjcVZJ!? zviG*=?nK_NY2KkKzKJ@}J+cLmr}*9qDPX7z&o0-EZdFNJwOk7XKHk4a#RrtGsrt^MNr8HFM~z=^D_7@lL9E50PY9UXML6wAAY6>QZ*cjD)^Ty`u_`0As(pm+ zHPpuu=(#nv;cFGrbE8R{x>j;R**KMUpC6pXPaxbP;l{b1(#`_ zdeY;;J^}wq$D(Sy2wZN+rLER>tlf~b#OE{g8JqSIo8TqNx0e^<8#WAcIbU8g{Ohm*p^dd&H1{jY#vM7rBJA37>v z(A}UE7i5pm2Xz?No=4%}YnVt;Yl-+{CQ>*~RPGO_6X}rF<|9|D@ez)hfEJ(b%@uIu zaJtu9z#(B-^d05Ajf(>`Y924pxcPdWh^w3pa-z8Ncw8}-(*2u!KHbFz>ae&ympE&NyDw>*3>ofV(%v*zJ_Abc#s z349J<-GHxkK;#<0C?U%G4CsQFS6hpP5-7?m-oNn~dQUoz#QXWjH~jk_0e36cnJuAq zP4py}X!B~ir;)GEX9k0*B};>}hAYy@x;K&DL9Go}^0^go17M+yUUnqqbrNTMi*^}C zIP?Tm8Av-RMn9twBCN>s?JuZ!fi6-n=4m3F?)p{m#qagi#*Dojw%7~KUg;71gzj|Z zYgo#@C|{@+yi_?0G>3nwyj1kp%{&hwqi3Dd$M=3k8z^VCmWe%Yg?}T4z{5H&|7db! zXMJmf%P(&@ARiI8gv-_Xm{H%}-0AXVf1V=BXX7#pegB)ahNf=x={mx6KWrB7^m_13 z9`Q~|H|K=BGa7fHe1h*)V!g&wLz@VlOK1IWoMDigqjEvJ}>l zNHpz>2K$j-;F=D8SN1cB-gTu@fj=YYx`6gGY3>4XF%R$1Xh=EZa&q8!?PPsEcYv2& zlK*l>XF;d8^7U8r4Rrxlgwq}I#8(v()ce@$KnjHi`1*_cYQf7U73PJp=zYW9I``Ws&{=M?pYALq#Q}LdCK&Lqo%|S7M;CqZ7dic5+k<>Ea^3ZRN}HAC2&p zZTo2r)wuCmTKw$_H0xC#uOwwAan6^+JSNX?5CF+nb{=4;ddhgQ67a!*2Uwe)0 zOTKu0XWNY36>O|RHz)sQc*yW|s@udnZF8X$8oRKaUff;dyrXY^+j78s)Z525w$IW& z`>DNdg7{^D$IK7@eW5CmJ1mZLc!?OCK#RTV7M^*H7~<_qN^M+cC=cbnp5l3LT)&*~HXL&Kmpd%Bzku z75mE4x7nRFhy=33w;nTAqrH0S&#yR~^!fiqdaXJfooTOGZ#wrWk`fbUr*mRovjsj? zSC!7^hS|!iy>i}}V{LzbBQ-Woaw5yGFGd)-KN?wCr~Bj8CESNY&Xpi}i%ss-)sd4Q z&Qtr%ol>3m<2J4>_S&25>v29k*7hYE z#lF6C{Pe^HuOo-Uj6UJ`fyqe7;<^d?L{?sAN=|yRO_+2FHmji+3Wrv5O%)G=}wzy=`Qm#7s$~XP#tW!>I^>Sz!ZrUyyYG=_; zWM|C2ZuC8LS1rb|v6PXwQ_cZXPA^;L3AYpc<5S;0=e(DfCt2V0Qe=JK$YIWP`sA3` zHpF&2XPHWKjhW56m*Ax7|10(Xu);oRy!!~3dFk8S*Iti7Iw{@QS*H9qc9z<*1ls8E zu$a1-Qow@cKa98)W-a6&%G<5ByquZ^Q*G6NJ za?)|oQ!>fRJJYk~B+X_WhKTlVDmnI%HZyM+`$)ONJGDlpqh}k9)i3{UH1dyltyQRp zlfKH>5hNGwyF^Yrq6=A1d|`cg@ADyK?~lL6pU+ojt|h(MJN3u6m^nlFRI!8Ux9b<0 zIfk<~wmxENFC)?V*zo=PD+$oD*Imktbj*BAUVZL0`Rck+WewFCk2KY# z)ZFy*-*pT#N=S%z@(M9BX0-a{XC#)NcH2z3&bZ}`<8Ev?GO5JS;3Dt1P3FR%8_w{3 zG;P+jdF3@+2A?&Jxgwd<#lSS5HJ49FBv6hs{)$fX@~F0{r#$Q{qi5>k5Y9XRPUhW&i*@!r?=pTu<1%{1w-+nBw<<2l$*pDkuy^p%^Q zK07rbrPrY&r%!I2W{(Bi1}N(x+EnS{<-R^KEbBvDJ);8BV{>PH?$1ZM&3imwIjbRdpN+ zn#P?PeLR9|>vfgJ5j`gp5*&K&GVOEth|>J>->kLH{4Z?*4!xTD${{yaV%>kP_FXoj zJV&>SFglapKTvu)m#rLM6wBG_V)xFg+WFQ!4e0g8_A1$JA6NJ32wkSnoi%BERtk69 zlG%D$!KS2>k5l$BBjcR2v{?z+nHlVRr)ROr$tu`51M{6}bNK&GGhb<5vL`G@HwZdU zJml;lnsu^sEWlUq$;*_+J`>1gk=ioOwK?y*OdmM=ob*AjleG?SB^cderqka^2^mS* z=6utXUcA-&j-Xh%Pg@dv<<*<=oV~~1H@W1|F!x3b=<}lWP=6IVU zAGR=07(a&i`@_#3dqvf&9eyb?edawoGvxyRSzmRSx6^!S4R1N;Y-Vsq*`<4@IE`_3 z{r%&|{-#oRVecatI`!ey=>cp9ra#P(H`@$8`fN6BckJV75Yq2q&>1(Ij+?u(m|1iV zj6=p|?s9mh-q;vfqp^sVmC@<*-)~U)IxmQ=wU1Jn=##e%Mo&Q}@P0kS$+H4EWA;0$ zuymrRIq~W1Z2IH&rcZp)sowNbTS)K7n4`48*n?BcIq6l%ty7j9CH21YkD2F`pH;U) zPn|W~HlwFU&%%vJY4z&DlkbM+FZ8vw(X>P3qm&y#TI>4HPdei<(%85BvcNUFw=W@1 ztaH}J8N&`gr1cr^QW8H6f%jfqn zBZ2$0+|up6PV-db-ATUeB-ee5HTc%y?_);i)_yRC8t;V;hj(E-<|#+!_hmkf5hq=n z83)c>d%V`-*R%3X-}%m};qbzpa+%LMIfK1AUtP`EaK-@Qf_gY|Pvgw*MbB3UGp9Q1 zjtjC9@ehEj82vMOIlLTZ)|NWwQ@Ii+uEiyVIImk-ae>{9r}6} zaK_d7tSO9cUS|(tr%d}>aeZWrSrIz#H9Ko&Mo8~-_04@{hbxg4TT*ciqp5G3{^3@+ zUfZnC`=DRP=|s6S_Di#WY0om?Kj<=8JA{l&H%`O>d?vdA8*c_`=3*Hrw<<6sCF2i1ASIE zI-%t7K`uON+Ugx!9QX8|CNgGMUq6=^{eXVvEN#W&a-Sc?3ez5kkEXj5U2fftudA`m z9KO2RHZ^E&$XSfdjw5Bz(ivB7BYPR=*wJejuc|F%9q2qObg7PvIgVg(Nd!H@|K51? zHQUFBh9jFoYeheEPbm~+#w|BY5i-oT( zJE)WGx1+X#L*yl;CntV`AP=~s{EsFUu#qe@cL-^w$bR)0bty4j>-7w*vVv0y<+nk@>C#kU!aI3@00b4qy)uNuQ(v8-aa*&h+O4TYx=) z{Q9|pDnR>A>hE|!XD;i3eF{Ur2A~6oMz&S~TY!zwd{;Q%^d4$i8-RU4{4m}FI)I~q z_TD!FTJw?Aq6#X);&qGHERi>UF|66fRTBlrLy(^%(rX0Pmc!iyESRPvw0kuW-bwFkI2F2@r^Vj;}S<&=u zXZy`b@-dZAyzKD=5;xp%cfT@&Uumm=hMm=m|2GX~smj~t$zV@e~u{q$%FA{g= zQr_1WUDho;dGSA7dE|5H*PpjhpTuX2Kh_<>e&ePr+e5<=la{Q}{!-$&Z!hUYhfZ8v zHazfbp3ncu3OUcRUc33WXJaV0-@?)TX9|C`rp9CAr4_>0gpO-Ne@uL0*+=;<(!cWd zz&+HfU(}j2Bj=$|92;JbN%w{t9R4MT{k38?txCOfbR<%X@B5b ztG_RzzOGja=N+Wofs@`E5}gXpczZ}B{StWE_EU=7y!Tb(EA{L-1b#O9P!Hv~-W~dn zhe>*__DTi+8?_#j5&pGx{IC@4(z_PKi!ntebl;F2~d(Ry?QPJ>n>C@S1V~Ky^ z)zkOEFDXmT%bW?nq&zp3Wye0+MZ4GE_t;fc^w0KJ z-tIq>cCX+0&8RRK{ms|nziftnH+}HdZPE05(CpTZO|&O<%;>#qsrQ>JYD41a$DmIi z`Yew6rrzP2P`i-$e~r1efPVNx`%hoD(|$g!BGRGZ!Kx>wrL!TMhK)zwyYk$>n3E_p;-r%AhO`%Dr(Vf=d_SYv# z*8l0x%d*2y1>gDB<=teE8Tird(a>S=#MtWHv?sItx$YRszpZ@1)Nbg0+p<{~Y$Cru zy_NYy3Hc1E8huL{{CE4JaksSyKbJQ*hVeBd_qLzXgx7uKX@C#k+5EqcbipsT*Hz!o z!otcr^UW6bS;U|D&Md8&Zhxt~u7ZAl=fZbpZG^tJpI>%l1Noog{$b00==AQe1K$_G zuh}2oGA0D$-yJXC{+^rha!SUhU&%MA`|y!%7JME4mm$CJBEOCQ7&mtk^a@`%Xpl>I z(3+NzS>QFH(>Fjb_mJtUwg{hoG@*_1HlBaS7ftZzu35>al=Zi)Uq(;8paJ^)`Kq~J zSI~~3;~%`DfN?eVk=8xkqj*04n|qt#o4F&t_(c@$_~l>LL^M*Lp?jZ+XfycM11|b| zZbs%kdoy|dr!gPQWSk89ZsX(DG|S4l=laJg=#O7rKDw@pdgMHPCf7CM!f4tv{Ng*dHS`V7X$T^$Dx6hPMz*IbrVL_Q}wWIjJZ|1!>H%K zenMyWf4%sbDY8q?-#zcV9{B9n6GDeYjTfG|cLVhvb>X0eJr{t_e%oC|x${?VcqtY> z`t`zHquZe0uh%R+W4G{_`yS2D=lS*T?$N$Q{ufs^>uf<}+>w7q!(WkS-klQz|K`6K z_GT;jMg~vl>@>LaPuT(AQoL%OL#Jp!)DE!siX>zXg6ib=S<+UBa=I z)8y0r)VaUQUQ0hM=wGu+XMPssUY{G1Y+3g_d3Ja^^!#g7_Z*#d8vXjf5goL5^z);? z-XqNTIwl-&;FFouYxGTDUg@G<3+J64XvKnCCchVD@XYt3$?x7<-wuu!UK2fOGyVM0 zD{l|pjQkkA;<7`NsOQ4-;@)bXM>u;#v(^-2qT{=^(e5!ZFMgbcJQ!2T#-nb7sL@oX5P)07{AIID>3BLKHderKDKD1(8 zAmhB~o63LgWS(lRZLDd5?^`2p4NXHHww5mpEVA*iGU(cR`TNC)tNozLnQOxr>Vq-{*!Ni-*qrS0i*B|L~LFMh+o7?(((Tj~!q0<*OTo$Dh?+ zLVk-|V)i!~?E2@nODyY)WoiBMnfD(#cG@!ev@B`;dRKdZWpzha^z5J=-NTptwMY1Z zt);DJfVTh;^Hi8!e1{RwuAQ{efwf7o^kW&C)a)34ILl7e%{$z$!|&V1u-Fs;N|n4 zZKPh0WyA-qrM*k8d@G~HL-@*}@O>PyB?44>t&x4!O7>hUXI+1|8-7^4aU+_tC!Jz&B0E zhqJ%@?)uGnJkPpxZv%3s{Ilh6w=s^M=zQ;deRtrTp&yr&P>*G=cMpz>BHXy9eLwv7 zL{DpH#aWj1<+oQxZ)5y^`G0xQR*+?#JM`ngX3{TD8hcL*{IY!WKRVK&>yuaiw5m1Q zvL@9HADRJOC!P8~uZE$Q92k^z+CJ#pcJZK^Zs@t<&v$>}qP;7=Ss4^U|2@@p>Nhb4 z-}8J2?Rjc_=Ae!6-+5=Q+Pn?E`|1zxtUXG5o|^dmzR0QIvIloM5FYlzj zlefRIYbSKAoUkq^avZqshX-Ah^K{x%KkiB8`L^g6cS66fe|+)vcG|u2_50pyq+gy{ zbmrZ<6LbC}FR!U3-}58R*t>^z{A=rkFS?=Ezb^j8D@S?%{7Jimin2(5N&Bxh2%i`F zry}~l{n@B*5=h_fa!-w7oK;ubsdbMJ4YomY*&b^bBr+Ep{=jH;i=!V7 zEgLkZ3psIB=9}xj^wA?R^z+oux9*eg^uwRm zF5JvGp8BhI@7qJUhsTwEuJeymbK}#~7+;6qm~=%md~$f+Ct175_qqEY{`{Emc^@we zM{Ym&*@rvZpx5(R} zEv(%Wz&E~Hr#Hz4(Gz=NQ%E26A_6$U{$bmD%{SIOsp^o0QiZq34lD+Lvq+PCozIPT}Xj$?7(^VszN)mQ}ZP z!Q;CbS1)|$0XnwdYYzVfZKj6m#CvG3KD!O!u({QA|FNNiQP3uhjq|BeQ%3Jakf9Y?#a*=jw`V23w2}V#ZQ1Ct`MkgS+!ZSugr9un;)&G%x99d-K9hQ`9=zqN zcIfll({`WRMtxVmbKS>br&!iC4;St@%6NWx>YNxHD_PfUy>y(LdVSyVsH< zap2MxgPUe;6aLNos68fJHftOG{eLsh&)HAAw#DwxjbmJIdo%El7W2Z|v^yqS36?eU zz7?mq7;ncue(-38@bb~$?%};-JGUO1M18jXFzb0Yt&+nSf zySR+@Ui-U%cQ*=OyEvwY^gmn?8Sb73p8U=KRlpCIyqo^-jr7-#;iHb~J090fxjL?a z@%ZE157MHCTGp#mcaN_p|4VC%F4+g2Up=ez7Y*kVZn^2{DCYB5U#>bY0s6i==T<8M zzJ2xUrzeJ7&ikiKirz#%KPA3>hl_lEUwx>46Zy>g=H-=p=>MNm+*8}>uiw`X+~6jk z-;aLm#YWoq(<^6hK1%z3ifSAZkwf|+U*CzCG=HtZ*wg5v9zs$5n4`fOgZk520rFdt zQ8U2mgKNNAgJ`cqJfm+*$cCl81L>5q$*b@5?Fi{`@_R0wUp^P4^A~|71LQ+PGC}82 zv=-JqJIO$uHD<7 ze7eUd+l%(rw1$?wUA9G?3)5Od=d5MB_zN_UEmHR(W$V*fV(FN=Z=|)f{1a+Tu046# ze00xHx}3iMpf#)f_(=N1-zizC`$ev3;=o$lYuzau zfc$z%7Nk%Yjd^`HP;=x5q?HVpKPt&|`KyvGRQEY$8`XVJjSZbG)Z7@2e3T7E-v`iI zPd@i$+mKH=*<$s*vNq_cdy5(yI(MS6bV@AwGd8pzsJT;qDx`Bh8^=3Y^xrh-J%BK6 zk&M(?ea*r0!=t= zjCIddGDf~Nw1&^7j#?}0j)mrW-3OCQmERuO1a+2DI-9X=SrMW@=amA~jt)jiqPkse7=j&da=6v0Q zlkTQ(P{{_RGb_^JweKw*Sa({b+X_oJmai)5T)GP_ol0kZwRY2;5$U+P&nI2wL9lGU zGaKj!^40kit#w`nYYn4s1I&pt!QjdVA)FKdtY;> ze0#`NrL#2B#q=Eo*{*eGNjk#cZ-+i$-M!sOedNzgYZ>`DlI>T%Q)T;?pDeAVb;n#b zI{9$W8bUsArNi6{mhRCioX=VlEL}(629mA%Ik0pP-KCSQUi;zFdE{46Hhb-VYmFh_ zzOudO+wrm?$rszc39Q}0vfb_n%hs&3-?Ay{9)tD_z6Z-jue;h>i|bpgTI0%Z%}lWT z=4g*Z->lT$!0F%y=p|pc`qMXgWs8yTZmnhY?Ht)Cb#t+jQxO}f=}V6810!CFJ>uBdE|y3;7zlKhOz zcDV^Go2<@iX^o}x6tX!#4VKMW_l#wG)ps9cYwHBdcBebTGs%A^SUQXRKgiay2P~c9 zQ?PXVZm`z4`ksQ;?gzorZR86=x}nbfOE=Wne(72|e=A$9{$#6yq?XoFAz6AoSvfV>UOM6n$;56t0>sT9riGXYmh$u^YPjs2tgGD@BvgyZzwKk<` zmiBZKz}iEhODx&m(!kO=D9Snz&2O;wjyz!P9l>Ij)|3Up1%u-2{zz_O9(tf_2_I>)ZHk^InW z?V`KoTGPruht@zk)2lW5UWLyj4``cDe+H8|r~1y0_B5q8OP6i~Yt7RR$Yv?op*GzqUW4u|%QmmO zMzZIBOMzXPm2c71nIdvH3xu^-y+18XmS zKUg;HK2(7vrK4tUbq#VC~iE zTT`;tw1QucLVzrhQ>N$qP6Hju=eDRg0%+IH;F8G zSppPtS%F~Lu(WQ|Sp?lhl#N?{jkTs!-r8ICfMqKw0c+1+-*VO7mwsyvwh64g)Hbke z;_7qlmFxnG$GgEg6Cod@+M|_^DD7p)r=HFh=set>1o{xHGXU~Mt-az3ux!~4VA;%c zc1e5u>burx=m2hrho)fJ4)+3DLmmKYum2bzTfNTH=xjs`Shi8k?XuyyfL+i*`>&#b zzK^Rtj(V_czl~t+?Q8^VPfa|hKI;JM%*8ISY@>UHNv}M$Cvp@ln?)Erq&*a!rPUs_ z&hco?E58%k^G*ZHR-^NpvK>``wU?sxv-Z5@mq)e;o%zvTy1s9tJ*ur>?ZI_`WfK;! z$|koHti7B)VD06oOxf%;QEKlr3@qEIzMG}92@&8T=&ruf9=`67Yt0_7_)*9Yu=chS zz}oxNx0+SL!VZ#_Ez*QA?+Eq zfn}>}2X|BMRnzDGuxtdo!8)U(IZ}I% z-C*s39RTaB$WgHNh}4JLBak0+?FB`EWh;yV%hnVPmhChK979;&$8bZJy_9_ZI32f zvQ6@;CEH{)ST;|7TC#cSRh==47e0@E0?YQ?*`NIR-38X3`VO#oO5dW@-ip5EDO-~K zYsr@94y3&^$P=tRs|v7eja6XTyc+=7x{iW%Hc98uwbyBBEI{`_@Fw_L{!F6DGnR3u zy{CQj{my9W2HryQ{ov-akV|0M?3+)ajquSHu+CN$A+sd2^__6-?aD8?_I~xYj0fNHq_p-{NKr@q`T|d6PJ$;*_dUc(VpQ}#e>CDva!qO zw$21~f^}v|z8Q2zK|VvYm%Izy2wiuBwP&L{_u8Y;SkfNNKCo=&^0_LTxqPgqCBSds zI0WrMu=a3zz}mw+3f7sZW8jJ;2t(Q0bCcgaoe`1G7}-AcO?90m2nTCVG74JEgti;O zVeqleGY7(-`o38M_1Fg19#*HGXV|opEmUhXof(jy>K%*=jUAn33Ij)_kv~{xC3OE< zXF}vNUP`sT9lDiv$RCH!%;>JP_R=PTb;d?xS7(-D6&?$n!5Z@!VA+<7z_RJ;zC%Mk zb%2ICV<7)fMaY8&u=c1MkO?|lpnD10>ywXX*=(D@-4kgqSZALkJ7nY5ebH{-Z3XL0 zfqZN0%z*qtXwOTwjzLzKb&PkLXh$40f$iCkugIaf#;!NS3J8yezX!OJ7VsHU^L$A3 zdm(yW4JiGP=q?k!ituQwe|Q*q1#%K5Cp&*ZVIDUYa&prWp|kRW{j34G$@q7*!!t-b zAUPv9oqFqe1POw0%E6gc6Mlz&7?7Npnn8W_ypH#Sa9(8zH07H;VL>fIVC?) z@rzE3pRcy(wLf>GXz{T3{d39xlpHr6LNn4+vOI;UIVmZ_cwg-ZsV!dWZ3;e#1H#|; z#ql{+-dFn)zyne%EnFOtnymVUXhT>|275J$8n>dwquM-YGY{D&s_0DV1NVITg z^PWi)5MCQdyEEJ-LUb6Xe#~(D`mxjwCp-Nrp44+OrP50V6ZSaI$J*ifcGwDh+xoFa zb-KY@r}wIcH!EE04ZmMCyhq_0;B@jEMjb~Lsw1;d6q!?;hw4XIs^vs91d>?}`8zDLhBt_Rd{$%+n9TkClt)Ri-jp`DSG zm^^1RapzNZIDC_mm6oXw^ra-5VQpkiIQelmJi}9%kv%^p7X*Q_k{76rwR2B@Q*B%e zD4(auJKRfyoL)3n`#vL|!LFP{8cYUio!y^bD&CA=2DV$b$!%FDdBS;l5V( zZ4z${NJ~x6@X9)>Zw03@;PNs&YU4k6H&l6gb5vb7PHEYzx<2Zy>)E6U?fF1Xa$~AJUaCV;LmNO?%YY57mK&K|>rZ0pnW~_-vBiy+u z$$5M#dqFQZwcd6PGd!7*oyFX$`i!MQgHy#jUK!0CuQ9uYHjK>AOy{EswjeB=tAUV* zc;wCw?~&h_lN5sp&GO`AXNdmO7QOqt=zk}mvMz^!gL6}|T#300vXX6nP+3|xj-p@s zNC$7RdT+GeGyEbEmXwj5Wal?ebtfC&{6gJDFEfwj*=#h~~P1zB24MwK2rLURfyq6JLnF19OuL?NM*X{Za3#?{e%mDV=y_ zkjfPA=jEDrC1*`sQeLVgyxOMx!qSa!bh0#Z|Jg;aZ4vFt0P)mcC}XJd!~0=MvfSLy zHFY<6kY;cu9q0%%=%6|@K-WQuIXQ_7NS>OFeqzrB0l8U8dVVf>hGj3llaIC7&B*HdU6Va zIz6jU^PXk`s%v=a`TvlH;bZcDk~CqeuOrNeSG|8r{D_>C+3C4_<3l3XDc?KpBm=|d zC(i5rp6a^+8VzyVt)#DVm^lpXPNM^jJW0%OOJFD*9ANfLmD4oVuV4^2G)0pgq)0*gRjf+US;gU*j>*D?d$s)FaSnIClQx;~@AUH!;)Ds)M$NWw?8b zY7;+-ezRqZe;}wo$C7>+>R@JJY9doiVRmY2Zi*+@)TLN;OHRxa-Bk8sZ#dl>zS0|Z z^w4#7IGJU!NnZsHG!#MZE@@dN9zDZ;F#3wBziBiuhK%G}x#eqPaUg*<;Hql3IgO^WC1Qu58Z*`tD~y z_-`mX9B1ouJbp2+`stZu&GFA9OVkFByVE+7OuLn(rp8x=0r^@;SwAWV0j8RTs4@n1QR& zul6giQ&RGoqm3SHv?K9BXHRs2_~2T=&XMNxb-=Vu`3Kt5oe3M>&oG}= zFm2TuGSrq@Xll6}QLy_U!pu*(c=gFiNp$(9G*h;0)M2wV*?9E=$`}WYq{|0bBcV~2 zK17CBLft@@tEhB+vdou8Gx~{Xvdo7JMH53mw^^#1Iv*h4KzsT$VXZ*|4HwzG@f^=X z+Z==kU&Py<=`rNhtwtKtDqCcRp zd%};CmH$H~?B(M;qxu=Th>rv9C5fqf9CaKlB|g>PQL4Mv{b% zqxQNJ({m_CG#Ny?LAjp1q(U={3=Jq+Yh@!>RQJE$m2;=sQVyuin!8Ua>^pgn^xAd$ z8>xy8?_IvCk6tAGuoPFiM|4hNT+Y#;NHlb@R=s(f^1j)dcNS^23a}gPPMn=G-=rHp zvbs*`N&(er_{b6yE+MRTNVf@rMn<6bN z8tqwW#Csto-<%XogvM6WLYmY2to5A^nwyvC$}#H_y&I;s`Iia4ch4yN&o4VNDL8F5 z>x)D)4@w3NNVA2Ip0A=U1JYPXLlcExRlQBPuuyz-4rNDSH_3JvX29GT*msgs+(yPT z(V>^p7WKz-Z@N0vKlLUIZ8eVQ4XwbHuPN7_LXAutMLE(f=^ktXnG8v~ns>!t&rqjR zq*t>vOUuqS+@d_jDNna;tP@?dJ`V9|e)gR8Fmb~QQT8&>eBo**XQj`kEdvviura9T zY#uQEYqE6u*9wgMaH+9*tf4-7?+xlRsFyHOdtJZ2Dq8Km&uK3>D9<)vsIJk32iPj3 zY13fx7(#nx&p=^AmL!_^s1xGF?>4^#rDo^lBqy6Xd``PUamkOLA-)t*U#mLQj*1we zI$RIv9mBgL*4|Ap>wmRgeXBa2?pqgm=b=JR4(1fexJ(&gopChM zEazC`9ygGbuWWC@8SZ2m*A$jbNMWrpM8BPc2cf5Cre_<%tBbV?yX|3Ei(RE_`R z;^#|-EGPa1`vdch@7eKo2A*k+mOz`$IyrMTh6_y+**WP8ZDXC*$yz_|g|-1{F6>lR z;3*yU+TliMqq+|e-P6*un2)nq_jeN>XMmFYTJ7c;eKF_QNZlOG9T%uE?<~W1WOC!lY^0SRd z-nik4(~#<=!`XAIv38pLBm)K?CjeBxA;b?fD@t@Jgo5T4#r3yBO)NtmL2k=<$%GMp z+fbjyz4DR{OCLC@tLf8$YQJ9vGI5c;;$R&U_XO|SbEEMQ>7xO%NfQ$todr8(qeobQ zZ*7>kUt>|UP(N?w-J!jNrDKc~9iQbHYuV&pi&>Mm;>o*rycrWGiJvdtc9DLxe+}-* z#s-v{?kY@?K!rd`lcF^Kb@&Ns#Q$1X1f-|4jc>0(18A4{%-G!B_JY*ue{b7;vRO1{ zh|&KknMtTfE-&q5-(p?vo3)(x;h*HxUi_nSv@X`#O-p0tw}-I3zOpxdL{HiD!m~46 z$C>pEt+gIM{+Y&z@)FO5+P0`nY)Sdb>-V&GxZ6LGnSBN(Z$0~+p7rWC=b8Gr;*Yzh ztB+f~c{fuoyv5*7VFlJ_j3g)bX`>a`aP|igdUqM1a%3B2E$)Jguz)yvZlX_~o%qbw zgP!_L`QMdKC7?RJ{@2^aEBq^O_|S|iu2T4RKr~n1491!|JCWvN^-AWU=z?IjLfKPD zak*sOF>*mR_Yh<3WRmi}FZvC3dzEHS_MDU~d#(|m2bm8A)*2b5bivpV(VUH5EqM|o z8*ZV;x34j-kGkU9`0KN?A$~CPSY2UF8P1wj*O#eRXrX^8qd3{A2lcXih!)GQiCnC< zF9)1_f^ZXtGb0mDr@w;J(wXQA3zdiFse$mLBcSmKY1V=RtT889=V|@CsCY4%JG`v1 zfN{;p0MSr$Sy$By=Ukt`Ja{RTdXL45AO|;C6S#U?@VIqto<7FD6=|Jn`G-I zY^17_ANA;MDNPhD-v7w9@S+=B1z|RV!=Y3H6uq8R1xPwdl_C z_lwFoOuj*BC{2Zhrk#e*(`LKUF(c^tD?CSkPUf+pv(5wZ^%!l?s_J?wWk!l;jIG~Z#jp?Jo6}Se$!hq*2y2D6QBO zvzuo2=w(OG!>Fh78{p9a%vUmgs$WwHN1zvFCAy?r`!!nfQhh$+xpyDNY>p`pt%rhF z6&F^N*Z6exU`*`VGu1l>cqfFtyOq_&OPw|Eim-9Up7CpMzLM<&k?m}H^OjwfcqPPk zSlwrTCnU`*v$FDdNA!P;{LOy7_uG%!w^00djPOXgPwihB-P|j^9wB@}#2<>8rY${^@LcCPjU^3J|@_ zoIn$_no<9#KEuskURp|q+b=X7{tP^+tuF0qT$!1=tDYg)2vcp+nKHw_>NNX4jXYD` z)~Rl88C2cr_z;!)Z0e$SkwqDLRp}Pcz9F;Ol7dGV^qLAxI`vPG>@)DG(v1~u;T!RY zvzsDboPGa8v1)4rAiDlpecwxe(7Un3g{7zPeFn!eX!EJ=pbh71_0l3d8M(eSh4PWz zK>Iz85~i|#aYcBO%K9ase1EUHlg1V%W(UW_YYje_wM%N2zao17kxwd%^!^I(`{Fn0 z@LJC}0!d{TP_K~myvpU3E3d93t;UJwkdfFDY}0JtNko0DypUlMS-w6G(%}=rdS7dP;^_joazf{-Ml?EWz_Qp^%$r zwngnSK2|?LyWYEm>W|?+Hvd}vu@q2vMAoHOD10lRa?Vvbh5mgY#nllfzR<{#{8yap z!h^E>hGES|gQ$OhE6lc|cn@i*y@|w!xz!Plb;Yh@g5nMR`^I1Fi}#tx%$%+Dag=}U zrt#(d{H$ydY`D_~hxXydB->k4wx4MpJN}v0Z`%7%{h^Afzfarr=2^+Jq3NhR$q38J z(u$4)aH zn$|^%m!8!--lkbVxSRNJ&1LRfw=)4a&ni#y%+9ZWxX#y*BfE+&`VKLaW7{UQCuQ@B zcTwy&vX1lViRbN4&E?&G{KbCN|~SDISg)q{`^1JT{&3irKfg z+na~xh%m7_#>&);#Mx{`!)G=h1aPuL_CU3%(5q)>Wfyu++xYo~Z)zW(&I&>Yl5fjc z^3z`#&<-30;`>=vLVs`owogE3fp-D>fpGXc9?)Fh3G4xM_eA$YI)Ht^#2}srlP-jO zfmUD_a1gKt+Gl|C!5e^fU_YQU;_ZOW0Y``OZWzCT@WIptXaRNrB2qL^1hfLW+ox!< zFrXqtGmgBPv(hxf#8Y*Psu`ea*vPa$2EhS}F`(zl=WO6qfM!@qE39#tVM(4C6T*Yl3eq=3%d zdBP)WI?s3u33f)-u+xmYpKfEP|dw*YL zRz#l3J>*xTBD%m24>@q9?*6=g)eA#AsL%NRLoU$0Kkb3+rd`i(37zVp9d9fP{(3W5 zYd;tH2DJUJ91UB!m)^X&iRQ*7lyt3yUrRRjL~MhOf&K34-YGoxNVD!IN6dXCLHAN) zo_*mKmltbY;d$t;ZWrbDO!3^MGv|R3OB;97QE?HT^)48~<+&uT#NZ28=-jf)Gxl2D zJ#a<1^7eu~TbDM99~+x@$Cc1OT|M2kk(^ZzoOYKh8hUEKa3{F-NKDEO+7xLEEpu@C;RdGAPDX~XlbWcxbbT`AW+ucRYx~sO`J!;wk z=%TZa$AqUGiSa;(X@BmC(cQCY&u$CfNq=m=>eQmO@ZAT8H@vt3dQ2!Oem#)(-L*CR z@p#%dU|PxqH|@%Ud_}@}jkC7VuJ!Y#rIgT)(w9a}h=xD6&o4eRjecAonHSc^dF;}E zzI){s+EMpbyQ_)*-2Ty@cW;Kjw$E7`y_Dg1{= zo-CsLd1t>?(?kA`70gVrcwh5fBki95+6OZ>Qohz(5wyR)U(#Bg+1GxF?!oN&Zd6$< zc>kz#OFGG?>=$imdkuahtrh-0Iw|z+FzUJGPr*;ekZ(ujRoxgJtzQhkd6w=W%SNDc z{j2|Pa*OVYos;yhOx;OZc4yt(TIjXxA3qdCFn(lf4I#fL3I_aPH~povyK%H<`I6BS zbx(FqS;w_ejITAnc{OVfICRh-2JNN4Gpt93cSDD!_Iney1#?HF|4Tz_DZeRTfGY+* z)tMFDT`6b|KD&{A+}Ry?$1&c^jDGWj2;Kv~5eGl$EN?6I8=RB9zJPo(r#{eZ(Z83h z$0-u^ydwDQ7uHg)Y;{re+ihcx>~5cke}T_-ByWX|m7lL$y@PUp{AS(cE%5u~H^)Dp zKsjevXZ*F5c%5GgW3Ij9gUo_1`r*NXJ83O{oV#zKeCy9qe^?9sbrw`-I<<$oOF|eM z?p}klJRy`{b*k&;D#~wP^Y)vq(a5)o2WPaC-^QpXBlnYI^bgN3sD)p`hfE%(dvC?V zDmnrw|AuwvZPneJt0J~VY=&RPt`GQOGxd3Bd-@+EDev)nnuB9{A4XiI3ZC@Ys3`?^ah_ zvrGO^0w)}alTPi)yUXQ*{>`QT^Jp7%Xq>*jqn>dWI4btY7Wl1pX=8-$lXpKp^8Eeu zhxTpap;Kf2LoWG==(_C2o@0!w#*2eKZKj{P=9KsB1NY4Nprimg1~%ng-UXITLW3JnJCO zb-#qR8XI_{Dea;)c>;oxjgI(F{yXd#fm+e81$XSFUD{*Kr{3ppNRKU` zzURMk=lf02v+##qx5YruXMQ*KN$JtL->{K-S)A>iME*KU76YAhwkA%PGc~)(r>N}3 z6}ltUI`OXq%>BTLZ{Bc>aOcF!?yI7`MQ@!xvIKrGHl?$`th@4s9|(P=4IF)b-fffM z--p~`%cY0woU-oX_Iv;S$adlJp1Dor_fW@mo+9MxFBcRyYyxYqrHA?+x^2_$iO~Dd zS09d$pNY?J&-8SG$DW_}mGtrIt-B(((Qo7az9lq*e$!crPV#$hOK46T{fa*LL<|I# z&6EX;m6H_}QNVjOTX)qrLHEUZ_YY(v1bwf%?jUr0%xXXMSyxLSk9u4=aNT64$JbC+uu z_0_pB?Tt6C?}*z=`EieZ@V6N1-!*T_5f`{SD1Y+?aK!wOf*9)MK@M#I>s*4%mPa!h z=pR?d&tJ4t7Z-s& zFE9Ob1$@`t zX^jyvd+3k#uG#^*ADeRb#j|%BxfD~niT00p{GO>Dw7c%5(AnX%8+~qM8~yueNOM#^ z?L=P1RoQr48+>!*ua|flX;$GvZlHauGX63zo_d9Y`A%KRQ9r$V17q884POTrYEQ);Y`RIRMjiH~kC$x=xWP{XQ`ejKYgLPm0nw+Jx_knfpen0iUr91i3 z1Mu5Lf9ZH~2XwmSvXY>D${Y0R>NQ&1uJL@E-$K1Vj``ymEj-u0UkUA8UT~JRyC3=- zciD6|e$wz|FSx-ohwq!zN`FoYeDj}2p`-S3cT$f{gF_=a$v5iLfcv*F?$!<|e;_76 z`rUKIWxRLEoikh;gvS?c>Y%)@BC=DO7#F8)f98dH%9A}l3c5aZ?32y0!cAAqj%D2G zzN36&+}`kVLOA2@>pwrUwH~^?b6Zth8s%Jho#!6iQ_ebLU3vt3_@Miyajle-ot!%D z0Q_}29{2+AFMU+*I@BL4_IsM@4;zc>zDKg&x63?l`yc@ z1j7_hA4P(t+nf&8+zFTMvBkM9A?cJV1#xfob24|E-09nJR`m!BR#30 zuCBafaz*)~iT^nnNs}`@N_ftHPRQ{M-J;3;FOn|jYt5w<7fe-B3-TF%OsYRkHayO7 zvP8!;paQ4|nt)A!{My6-T61&&TJtHBSU@}*37ryvjlfo*3n-$mHvrARW}qGD1jGaK z9}_YFo&u_XO+Xva2w%4Yoj?^8+XzT+XahQc9l<d?md~{x~F8Isy6CI0|UJ6%IrJ zlYlBfvbhuJ2DF~g+Q9=<(T|#^TLH}r@~0r*2_=BmNgaR;Z1KP*U0NubrKDkPm=Zpa3WXWH)F4nt{zgJJ1E}2KE8cr(}Q(11bRd0B8r~1E3o?h(MJt8N!&3 z1f++@0tLWcSQBY05Cg;l`M{*J!GQdvagfO3qrz4ikPnmrwLmM-3CQPx z`~%Gd~i4`=~41KWU|z#gC* z2)}@O0yBX!U=y$(2#I4107XFjH1Y(>fVDs?FljpV13W+#&;)D&wgR2NPGBE!3<$Xp zngP+kOdua90UCfNU=z>=bO2qzF5n<=42Xz_mOv~ZKY$x&kRE6QI)R^-f7t;p70~7)BOV|i(1U3O}KnJi7h`fY)0C7MX-~r^H zqZOEVDdhn9KrPS+Yy?^X`Ec9@>;(1zVY8?oFbT*9|T0I0l5J@GhV;9tA)> zumNZWHUnFMPC!0r_X9mZSSn=#69ErU3v2*dfX%=z;2>}e2%iltfDAzX(Bvy@8?Xb| z4ai5JCfaad5|9RH{(m!#9`Fhk$&-_5k5?$On*5Qx8xJ zYy`Fd@<+E9I0{5$(5FBKPzE#tt-v;54{!ho$s`>R4NL;kfD)hzXaTkXdx4NFXaQsZ z`9LGE8R!D!3sHXNqO-{Z$OqN}TY#OwK_JpizX5Kb9ypduo*v2uVt`m+CXfc`@MH;4 z3p4;5fiBF# z8`8$tfv+CN?{(YtJN{mk>U^?$+loKYw~k$>0p2==Qm5W`C(rTLsqfiCr`+CW?(KZ~ zd-EAgKK^t1hYwBmwXM%N055${^u2L9baL`Kg}nOg|3i;Aefj;|y-TO>e)jCDw+~Nt zx0ADqUYh^x-B0iHpFc}xm;ay73m-moChZLJ(rhU0{5iidS-$oje_z*W=kb0-oIW~< zzar{Bj&jBE8VAEE_k^=eq;bw&p`Ke2m@x094jPjhL-cNSX-#SMs?w4viqqH%w1$-w z*B0lLR_Re?t+^^oKF&KKR*-#^#v7-1!mNRKG|KcrAeStJc=2ltt%8L0btG#Vfd?1pG4z8cmG_gIw@YpEjz@NVFy5o#ZyZ(7q@uET?oShhJNu2e1Sh4q40EnI8xyY?Qo1~=fKp8mBqEW#j8r4GMn&> zRJwF_jT7Ez!l}M+vk9m8!YwB3@`X290VyTr{xVw$*YpZ+CcLQL|9p$m_X@WWUfe6( zPPn*Nc&py8^1t6fxV%?*o2gH-uRfhtP>P{yFP`e+otj?n>@e@-ns+8Zmz~zol=@m+ zD3z8td{A>b{C_HaaAvQ*-r3)GgT*HY>PTb8wpR{JB)#N-d?!hN@Q-`HjF6u;JySZ# z2mQXRk8BLA%3ie0@S*Y#v;uR>uPHTQ$q2^C2{IzAmyDQ9-O1LPOj_qJ{O4|(LnqZk zchOakdER>HZX08_DzUD1Wm<7%Nd^2b9eYOR+d(m zFP0E1uUwj5xn!lrP7w1&h&6>Yf!rGFdo25+iwXHHALXba6edNyojrl36`&ga_S|)7f0EL6>@CAB)A^C;a;keOW z8padn{DpIKK`t^Jd8+rBfijDit*o9`T3v%<2SW$NquXVc^ObHVUV3ML;xFozU!eCy ztM}yUAtztmnWHyyS2G3Ht3WR=>#Qp>&yM$ItiD#hGkfJ5L0{_x?Flz(y?uQNY1P-G zy)sEW2qo;E>@{wfk43xxdOjAfhFM|uY@AVAxwN*-&_nUe$?ocvi?ixh*nD&;b%?Nr zc&dvlYd9Qhx5-Jw?s*ry{UtCtinE)^41uP_RM{l3yfaqGydL!_>(&Z)zJ#fs8i zc|>!ItGJRo4%I1Dt9r%9ScB77RB-{!D}^1|hd#ieU0Wurze1_^tX_1T)b|9k88-=D zS~}y`9v=hqD~fA9#fvJ8oQ-_T@{w@u0By&5upXJOpNu-oTZX%cy#sg9A> zK+(e2)*+;ivO?6yx#cCLMpqK8FY8se=%4XWCECm(ooF-28Zb=yvn|iOzxzoZdw=)u z^*f-iJ}o)t=#6ttxdBzhUj0h_6=H?5;;r^__~ApE&m<>udexCR!zXrCuUucf`iFab zzXy?59)E`q1ySPwE6=U8^L@|znLECYOkP0zIh5n*zY7U#OsM=|r0o^_UcyRV?+5yC zahSF1Ko$ww4+xDIV_gZ>-;lX=rPZr_a^R4j6`6ODScxS}*h_qI(8LPPK^ZKrDGg@x zvzT;)J=Ox^3jyipmxJ}Eb}D`l^i(?WfBe|dUVM!J*d zZP(e(uYdR`Q-;4k$_cAaoc>W*_xGGSDeOE`y!uXSqk;6@N`C8a9ystMHymDMtmy*` zsuw3uOutm|JB-l4*`>Ai#9+r)6R%{Ua$V_lhpWD=ArdyguT^zvsdrtccs+^iChHCaij2 z19tdI>3j2)|9yXX%KKW<`JeU9+oprVS4uO8S0DDuS7V&B2D+GmE2Rh-84mvhS|jXU zq<72|Ub(n5v$%S>^E|{FWgwXv&U@ijsJ*^3 zvEDg@^buBg;^Nx6V%(8AuN=}`E*r`9)Y`gb>OXqV4OD|ti znOIS=a?V)h_FKH=sQj_~cFJ$^#yfUU$pqC` zbBxwD`Yk)??IvCS@Hd(+dxe zlK(%CNTOrE@D|>2Xm=04HAciwl4+ZI<>x-ZdkXLO?`@3PFrM{{<1-C+`jK_$NvW67rvud1Mhay>r=W}n~ z8+aCNg;k-%qvFdqUX6K2x>1Z3wj{Z%bn)^e#&d}sFP@ud1$$ROcDc8b$0TcLRVKZ? za+|4OwzPX z%d`!pK!qt3DCHHPK#__`U!>4S=z|ul(quAeLMO?XOqvD}%hQUe6;Z1q6eu5AbzOCF zmvw!ht1Ii1^|7KKb{8M3^-U2KpX~R0&ON{1Zzhu}yZ*@!(mB6#&pr3t*SY7Odw=)J zk)4}hM1@v#`ABE?5XE+$OJuUoKhAbsYmC!5CevOX<8+=$TziaL?k9U6_s&#r5>fri}wYxT@Z9uzQGrT?2 z4f|`R%I``X?90&U%AG@Hs{`U;QyPB7fXxel%gPkPQSk@9ti$FSb4B8zo7WWj2ZzSk zp%Zej{EDk?iX4eQu zMiS|@$m~_yF|RMIz@)7k9-}a8@P5KFScEfgZ7vaKn{}Lnyo=GE4SPjrv9DWYCjLTc z*yL+eq-8lQ!3ty;`;?BjR%saL*IbU12jel%@UoX17e##TMvMoxGzzGl+bsx3|KggL|<%=`%V2S+9kAZu)d2m^uZeND#_HIRRhc6R*m)ETE)-kA$A$` z0z*~}469C9r(sQqvu>a`*3miC*{^AID1J&0CZ6jH<&mL*kY|>CS*CT5s_|*wThH?@ zl*P^}{3h_ZPG)IYgh`kc$*|QAb--?!stMZ!*zngQ-p$Zs=!6wt4>PSB5Vl9Al+@(m zh~ToJ+;5i|>@6tE*zjQ-j@Nr-W(Z4|Oo_O6M&ahPQT`#Eb-E9}#kQgCn2C*OJ+mzC zlBwOJtkI5M%(hJSn;_qQ(025d3an_Re+zI27=L(dfNF)fTY-BJzLwy-QQPYQ;7r%0 z^xOs?L*J9YKM0t3_iz3U-u=l=_*M1OhlYZ^@Lr{#iJ?hbE~f8=je{JJLS7l3k>+Ih z%Avu2QfS*?IP@OE*^-F1d=rl~ihySeGn-0vHUy-NHxIwG@zUe`lMO@N2|Y(T_=<+Q zSB?zic~oqW?R`k2K|qTZ(+?bNT8;zn2i_mpHuzFJKDdbM!P*9tpnkYKW@8uY4}O>K zQTKXpV2I^~={HIy-yQNynPJ!LA|JC+Q)+_~#`!JzT|qv+XYoE3WaC=e2NJT)thhbG zY^A?LxYzRs0sB~YN`RBL@e-69?I_yuA4%}-sBse7dn@uiZN;v9p~RwG2Bq)ik&)>U zKDl1-58w@-Toi_>X|!&|zR)DuC=BigXvf$l$>Qca_2p@=#=R~>7WVhx*^FDOU5zYXv}MSdF9C>#iOmDOdF7 zkMwjl)DOII@PBonTzn41@?!Z?x6f`z({T^QgVmwFldwdmPwfr8R1OD^) zBXDTi zx)Sq%KBG84tCjkU_(o|0{^~AFgs(1@hobS}N$~XtV-#ufX1!P+`A$gpIMfSuD|vkv zIJQ~BKHvAbK1ZSNX`Hm4DayQFT%n&D@?DAX%=+2nks@sL(#n3dy>cq=Ta%P{OqUqia)>EjZ{ zp6*f1NO><9hOEm9W5(kbNC#_c#nQT+g`r|+sZ{7bbTRS^aQ6v zUk&+UK9DcAT($MA&L(f@+dhC@DnrqnAGXoz!TgSh25VkwK6pRO+B}Ctywa#_xTOqk zgbLgonHm->iMBds9_C8D4#VU(6Yn|L6jc`oDBnLKY`&yMNcU^NixT!U*V^k*@_zz8 zE5{zDJv@DgV-0yc2U?V5TgNh!<&eFvS{L4brSBP@Rv6_AeJ`>Z$m2IkGb<@?a1h09 zLo1g_;dwr7!)=Pgr9l~3-mn9DH}+yD57xunuSh)q*p5ukrzM4k#&_0rat~J>@N9F{ zdM2sYsKdA~YiV(?i#p8hh5s)}5wQ;H%N>ujodfQ>BYwWjguH#*u~+BXLU-G6 z2`gk<66q|c5q@Q&?oLNp_OFC{w@gXmdOL&Zt?o2*g8od*K~X+E+)Kz9-UiMhpQY%N zJ1Y6KFfUc@@oZ@-_ebq9_K}@K9_rTJQPCdH#Ximc8to;A{4ZjCF-f#l_rvphGJh89 z35AK1^N|;}7oMr_?PYpsFSB7Iq1($;ydQx->J65$+X$1O5wc2~&dvyN+;${wJcgxY zR-kJRbV(mU>kIlJ(jCu7Kg>p1PNW}>LYP1BQ9>_=6ZJM*ee+B`ukWKnoxGqQxXv_HT1)=iMFz& zNc%bEkxbW?D zk1|IdfI9Q?T3C^<`1I2M2z*%=NVzncAEt$&dWiLiJ-B5obLji2DqH0|muI13e`r6q zAW{y#&yjk#aO3wJP6P3G^28eE_Mx6N@JiaaN*vZOf#Vcoc*97En-mMWj&>nz>lW}U z4)^IhX~=O?{@Uf=oT<-&uP0G40JxPBL6cH z$L9a_S@;s=?7-tB{9^BF^2Fdnx1s#1eOBjyPJLF~J%@DYv*I`0oF-&8?C2{6+F8(T*dB-BMdvrQ!cuZU#NYf|i4J^>%q~J0eP%DkFVo}v*3+ZU zYzgpu+=h6G@=9kf#_vMIYhIE*vp7Bo67seoP8^oxZRJ6J^y!N129$gs9(5I8WzaWx5%; zETp?S#G$RndBHlo`_lq_!t=59&NIFK3;LwuyNm{`X~k>K>;srfc8}@|-}-0+WPqOS zZkXrU*1SHTPCaj`cVafz`s z$Pe>0^6M)XP3Q1lFszGXjcIvtJ9ko}?K3{h0qf#LU2+Jx4$$TxPtO2XJ$bK$(el2B z`M~4)`5w??1{0^Z0BgO&*S2HwQW(-y124+LeH9+IVWdlg*|sl77{@KzwX75Nv-QBa z4)OZhpmgc%+CZNSCx4%pn@_Y(HU|1lRrKylC0%TD*U9EUza`Ln`=m^)!&ttwToq_v z7HBKS#!6ozbziM9_vMJ|kInIMx#?<;oK>x6f1Ro6+6(xtQ81HJ1crXSZuO8NrbHG$6iVL#se8P^XnNB?W| zL(2Vs?T3t8-4BWT|MbHF#D6~hkm*g-4;eO5KO96@bw4Ztm#kCvg;y$FTJJ~ub8R#? zLAx3P4}YSz5C7J!!G2;o^vNfEW$dr87y80tAGeFB?jjyM`j(Z4imMWP*HMSPtICD` z-WT-nEDf*6#<}ws!G6%RUjrWa27@;LmE^fQ@I0JOy8$${^u>+9 z`{VVx>`MAAryxJv|BnZxIqxQ7$wpS1_8SG9`~hc?VK*u`%|IG>yG z3wr@KtH95N@0^|vHZj*KeLwu0K+}x3%!WdNd*NskzZp2%x|86eqWMAG9l&v4d&9E9 z(%8m9rF$!IIm7jHPYUVY2Hbq&))(OXGu+#OTL9b?P9yk5Bfd)`E%B!V-?V7}dn7|h zkYOLfS`e1ubRyv10o)?sk~a8VzTlhYva|r?ma=)2Lr9^TI$)u z>Du>#K50|9uk`&LK&r|Impd`Rij*$;_eh~R$tN_7;fzyt3lI7?%juJWzt;=x>qmfl7{APaWqsDmr<6XO z{d9fhRke>3mO#G$4{qrHt7ebuM z@u|{QZUvuOW8aq$*B{&av#f39>W$GD!Lso5DX*nnCtnF^KlT@--8MnmU#*cgE@RdD zq0h&3g`Gk3b35b({wPxCl>37pv>&mfF;$W)7?A+#HQ4ScmN@+@#U@>w&uvp`2R zya^kzkAd{RL)d(T!FGx4l-~nKn`Vf3|uWci1qsm{7u3x!YA>|C#rX}b+;>$bl(Yd zwZfi`!fLnwUm~3Rf13E%$J;yOe3xAM`bFs?#JashA(TMi$r9nS6$OjPfEv~O}70UL$_}WY5_|TwhRjbf{4mZ|t ztV@nv&TD!_dd6!tc=^~?8E!W|>jkf%VY%%=`Ev|9T-q~`My<4G)=0Y@d|kH6G2O;N zT;8{W>7Mo%q)4ET!*k7S-{$cZe6mVIAAY#w@~~|uSCvluK56h#6?Z!!8on@ z-q2Gl0};}-IQ7H%@J$5!19&yCHpYKEsF>jn;K#qK;){e9yfV--uBKlYkbJX+6i(UrMM|bH`GwTKdx~>%SsBkA8WmU)+;99 z=Q}M+IN*+PzFz79KUeX{SFr?Q%h!t(*GSwbkXnYvDQ#>N5q@mLRMAU@i=ezrxO!*Tsg;QYb&M9^s~*0#r=ZDD*$WzPbQ&oNqp3_h>% zdHUJF*P5rFqx3j8E5vi34(B-j=XnK2&I|PC2YOG3Yn}9I@Q->3!CHPyllJW55bwee z&uv1F$39jWCnFbyI7>pDq--oZpW9j*b#if_e`ytcF64!Am>2#sFKvN-X%)Tu9e_^c zp-z?u`W035W1x4r*&cwZkmZU%yE)K$y?Pn4Ts)4IOGd5? zajpt+ybX~*^(y0VUhnm5@#^*5e)ij!hj@fjpkrT8rb`~=MLu!4Wn^1Ow=={~#$h?s zijxmy_oyYcIGOA1;;8%$G`8c%FWh zV4X{)FwTkO_?JqnuZO;!TnhU+@OzHJ8GOI)aJ_sif*a&1z_{K3^po^{6{ByIEvP^o z^A7xEIRJQ(!c*k&2%aiU=tnJzZ;~Z|E$%ca0X|#t)8znQ<9~!a4%m2R^&T99XGjT3 z$!KQE0l=Fy?kwRwt+)aJGBnE)C<;sKNGSp~d1lLgz!vu?`Fcb%M>1GkHvUITM+6_E z_q?TulJ{aeD6yt~3CAy!;gSbFs*sOXVbYgv&5b zHUc&oPLMqje4_AvIK$7ErvMxOlVm=0oXLN(p1ann{7(^{VPpASp!cvCe5%ZW9yFST zvIVg5JWbcZjL+%1eqd#OhR~mAG-s-|lxK=`YFaINM~l&5*Z(Z9uXCgou<<-s z$`QOs9t3P0ezEKaZ2T|M`&ulIOQd9`&+R9**$KaIm&2 z%?jBHnEfG_S}ETLY-wGhcfnX4waaQ8LTm6U*$vq8b*VfQ;V%>3duMrBEpv}?`ZdD) z?=0?GxjBN@$-{uHE;?}Eozq+{ivb&-^-_%R8)QG=tI^+bsg1ax%;n!Ct$?jfULm^y zTN!SaM*v%1w&?veR#sQ)om-aotE7CK)4xm}0^FtLuvO+9@A#Js@4dEm@(TGF;LDWe zYH7^5%-duM;4>88DMi5Z6z-C}5k4=E0A8W^ZfOL8!3F7v;2zl*!Q17@2=0{yCpdkd zlp^>Vc{qZ1;6P}nDN1_;_siZ09+1Z)cu;cloxUW!5&TNM565I1l4m3Qu(Y1!^kvx- z!6Wim1n(5y>1}aGWm^RA65c&$_%V4Zg0I#4(G348y(`Dy>*Uc>oc`5%Uyk9g*ZXn| zehu!zbei3AFoJK8mQx*nqwI>{oAiDii+i&)E_9k(q$7fF#l5ADf2}+j!Fyx@Y$uER zIw?i)ZSrsgzg`y+jOKQ|o5$cc$X*OU2ES1rkKi{+4h_ced!;vm-z*=D;5+2m2!4yS zp6T>=%AN>*t2`FLZ<9G^InCQ;TLkZu2P60$@>B%BQ||*Z8Qvwkp&SgpOCF8jcS{zX z-SBtImI%H__DAr3IT*qBN((x>(cdS#BKUrJB!Uk}BRZzhJRltbJ_%#@KETV-M^3`n z{RH5xDO}wLn0E|yD}I~Y1-Mn=PI)4NyJYV9j?c?hz`K>ETb=-1Rv3Nl0*8C_E+NBj zm&I7DxlU<%;t?@@jLV$A%lyu=fzIbulEKSen9UFGX8^d@FJ%v z$yO}>82**=SOgErawsvw56i<5T$aj!`~noRMu`y_eQxH zu*r6lR<6M}%XO$|<9~}h1=#3smC{P*gT5BQd!*wM$G=V<0c>$^lSS>0f4v+4Z1~${ z4h9iR>kV>e1iw+TmpcAUaywv4cds;E=J1>4cEDCvci@X^r+)&uegVDem;dhWT!o3I>n3-&JoZvQsU zpGO;A1h{!NZi)fT0>JYDzX$MXfWHWMK^Mk3z|ZVTNiXo{0^T~7k_XeccXH|3XYYG$ GO8ysNlo`7K literal 0 HcmV?d00001 diff --git a/shared/sentry/external/crashpad/infra/config/PRESUBMIT.py b/shared/sentry/external/crashpad/infra/config/PRESUBMIT.py new file mode 100644 index 000000000..3961942c3 --- /dev/null +++ b/shared/sentry/external/crashpad/infra/config/PRESUBMIT.py @@ -0,0 +1,28 @@ +# Copyright 2018 The Crashpad Authors. All rights reserved. +# +# 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. + + +USE_PYTHON3 = True +PRESUBMIT_VERSION = '2.0.0' + + +def CheckChangedLUCIConfigs(input_api, output_api): + return input_api.canned_checks.CheckChangedLUCIConfigs( + input_api, output_api) + + +def CheckLucicfgGenOutputMain(input_api, output_api): + return input_api.RunTests( + input_api.canned_checks.CheckLucicfgGenOutput(input_api, output_api, + 'main.star')) diff --git a/shared/sentry/external/crashpad/infra/config/generated/commit-queue.cfg b/shared/sentry/external/crashpad/infra/config/generated/commit-queue.cfg new file mode 100644 index 000000000..95b31824f --- /dev/null +++ b/shared/sentry/external/crashpad/infra/config/generated/commit-queue.cfg @@ -0,0 +1,80 @@ +# Auto-generated by lucicfg. +# Do not modify manually. +# +# For the schema of this file, see Config message: +# https://luci-config.appspot.com/schemas/projects:commit-queue.cfg + +cq_status_host: "chromium-cq-status.appspot.com" +submit_options { + max_burst: 4 + burst_delay { + seconds: 480 + } +} +config_groups { + name: "crashpad" + gerrit { + url: "https://chromium-review.googlesource.com" + projects { + name: "crashpad/crashpad" + ref_regexp: "refs/heads/.+" + } + } + verifiers { + gerrit_cq_ability { + committer_list: "project-crashpad-tryjob-access" + dry_run_access_list: "project-crashpad-tryjob-access" + } + tryjob { + builders { + name: "crashpad/try/crashpad_fuchsia_arm64_dbg" + } + builders { + name: "crashpad/try/crashpad_fuchsia_arm64_rel" + } + builders { + name: "crashpad/try/crashpad_fuchsia_x64_dbg" + } + builders { + name: "crashpad/try/crashpad_fuchsia_x64_rel" + } + builders { + name: "crashpad/try/crashpad_ios_arm64_dbg" + } + builders { + name: "crashpad/try/crashpad_ios_arm64_rel" + } + builders { + name: "crashpad/try/crashpad_ios_x64_dbg" + } + builders { + name: "crashpad/try/crashpad_ios_x64_rel" + } + builders { + name: "crashpad/try/crashpad_linux_x64_dbg" + } + builders { + name: "crashpad/try/crashpad_linux_x64_rel" + } + builders { + name: "crashpad/try/crashpad_mac_x64_dbg" + } + builders { + name: "crashpad/try/crashpad_mac_x64_rel" + } + builders { + name: "crashpad/try/crashpad_win_x64_dbg" + } + builders { + name: "crashpad/try/crashpad_win_x64_rel" + } + retry_config { + single_quota: 1 + global_quota: 2 + failure_weight: 1 + transient_failure_weight: 1 + timeout_weight: 2 + } + } + } +} diff --git a/shared/sentry/external/crashpad/infra/config/generated/cr-buildbucket.cfg b/shared/sentry/external/crashpad/infra/config/generated/cr-buildbucket.cfg new file mode 100644 index 000000000..a79e5cfa4 --- /dev/null +++ b/shared/sentry/external/crashpad/infra/config/generated/cr-buildbucket.cfg @@ -0,0 +1,984 @@ +# Auto-generated by lucicfg. +# Do not modify manually. +# +# For the schema of this file, see BuildbucketCfg message: +# https://luci-config.appspot.com/schemas/projects:buildbucket.cfg + +buckets { + name: "ci" + acls { + role: WRITER + group: "project-crashpad-admins" + } + acls { + group: "all" + } + acls { + role: SCHEDULER + identity: "user:luci-scheduler@appspot.gserviceaccount.com" + } + swarming { + builders { + name: "crashpad_fuchsia_arm64_dbg" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "cores:8" + dimensions: "cpu:x86-64" + dimensions: "os:Ubuntu-18.04" + dimensions: "pool:luci.flex.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$gatekeeper": {' + ' "group": "client.crashpad"' + ' },' + ' "$kitchen": {' + ' "devshell": true,' + ' "git_auth": true' + ' },' + ' "config": "Debug",' + ' "recipe": "crashpad/build",' + ' "target_cpu": "arm64",' + ' "target_os": "fuchsia"' + '}' + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + } + builders { + name: "crashpad_fuchsia_arm64_rel" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "cores:8" + dimensions: "cpu:x86-64" + dimensions: "os:Ubuntu-18.04" + dimensions: "pool:luci.flex.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$gatekeeper": {' + ' "group": "client.crashpad"' + ' },' + ' "$kitchen": {' + ' "devshell": true,' + ' "git_auth": true' + ' },' + ' "config": "Release",' + ' "recipe": "crashpad/build",' + ' "target_cpu": "arm64",' + ' "target_os": "fuchsia"' + '}' + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + } + builders { + name: "crashpad_fuchsia_x64_dbg" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "cores:8" + dimensions: "cpu:x86-64" + dimensions: "os:Ubuntu-18.04" + dimensions: "pool:luci.flex.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$gatekeeper": {' + ' "group": "client.crashpad"' + ' },' + ' "$kitchen": {' + ' "devshell": true,' + ' "git_auth": true' + ' },' + ' "config": "Debug",' + ' "recipe": "crashpad/build",' + ' "target_os": "fuchsia"' + '}' + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + } + builders { + name: "crashpad_fuchsia_x64_rel" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "cores:8" + dimensions: "cpu:x86-64" + dimensions: "os:Ubuntu-18.04" + dimensions: "pool:luci.flex.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$gatekeeper": {' + ' "group": "client.crashpad"' + ' },' + ' "$kitchen": {' + ' "devshell": true,' + ' "git_auth": true' + ' },' + ' "config": "Release",' + ' "recipe": "crashpad/build",' + ' "target_os": "fuchsia"' + '}' + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + } + builders { + name: "crashpad_ios_arm64_dbg" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-11" + dimensions: "pool:luci.flex.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$gatekeeper": {' + ' "group": "client.crashpad"' + ' },' + ' "$kitchen": {' + ' "devshell": true,' + ' "git_auth": true' + ' },' + ' "config": "Debug",' + ' "recipe": "crashpad/build",' + ' "target_cpu": "arm64",' + ' "target_os": "ios"' + '}' + execution_timeout_secs: 10800 + caches { + name: "osx_sdk_ios" + path: "osx_sdk" + } + build_numbers: YES + service_account: "crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + } + builders { + name: "crashpad_ios_arm64_rel" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-11" + dimensions: "pool:luci.flex.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$gatekeeper": {' + ' "group": "client.crashpad"' + ' },' + ' "$kitchen": {' + ' "devshell": true,' + ' "git_auth": true' + ' },' + ' "config": "Release",' + ' "recipe": "crashpad/build",' + ' "target_cpu": "arm64",' + ' "target_os": "ios"' + '}' + execution_timeout_secs: 10800 + caches { + name: "osx_sdk_ios" + path: "osx_sdk" + } + build_numbers: YES + service_account: "crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + } + builders { + name: "crashpad_ios_x64_dbg" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-11" + dimensions: "pool:luci.flex.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$gatekeeper": {' + ' "group": "client.crashpad"' + ' },' + ' "$kitchen": {' + ' "devshell": true,' + ' "git_auth": true' + ' },' + ' "config": "Debug",' + ' "recipe": "crashpad/build",' + ' "target_os": "ios"' + '}' + execution_timeout_secs: 10800 + caches { + name: "osx_sdk_ios" + path: "osx_sdk" + } + build_numbers: YES + service_account: "crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + } + builders { + name: "crashpad_ios_x64_rel" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-11" + dimensions: "pool:luci.flex.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$gatekeeper": {' + ' "group": "client.crashpad"' + ' },' + ' "$kitchen": {' + ' "devshell": true,' + ' "git_auth": true' + ' },' + ' "config": "Release",' + ' "recipe": "crashpad/build",' + ' "target_os": "ios"' + '}' + execution_timeout_secs: 10800 + caches { + name: "osx_sdk_ios" + path: "osx_sdk" + } + build_numbers: YES + service_account: "crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + } + builders { + name: "crashpad_linux_x64_dbg" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "cores:8" + dimensions: "cpu:x86-64" + dimensions: "os:Ubuntu-18.04" + dimensions: "pool:luci.flex.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$gatekeeper": {' + ' "group": "client.crashpad"' + ' },' + ' "$kitchen": {' + ' "devshell": true,' + ' "git_auth": true' + ' },' + ' "config": "Debug",' + ' "recipe": "crashpad/build",' + ' "target_os": "linux"' + '}' + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + } + builders { + name: "crashpad_linux_x64_rel" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "cores:8" + dimensions: "cpu:x86-64" + dimensions: "os:Ubuntu-18.04" + dimensions: "pool:luci.flex.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$gatekeeper": {' + ' "group": "client.crashpad"' + ' },' + ' "$kitchen": {' + ' "devshell": true,' + ' "git_auth": true' + ' },' + ' "config": "Release",' + ' "recipe": "crashpad/build",' + ' "target_os": "linux"' + '}' + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + } + builders { + name: "crashpad_mac_x64_dbg" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-11" + dimensions: "pool:luci.flex.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$gatekeeper": {' + ' "group": "client.crashpad"' + ' },' + ' "$kitchen": {' + ' "devshell": true,' + ' "git_auth": true' + ' },' + ' "config": "Debug",' + ' "recipe": "crashpad/build",' + ' "target_os": "mac"' + '}' + execution_timeout_secs: 10800 + caches { + name: "osx_sdk_mac" + path: "osx_sdk" + } + build_numbers: YES + service_account: "crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + } + builders { + name: "crashpad_mac_x64_rel" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-11" + dimensions: "pool:luci.flex.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$gatekeeper": {' + ' "group": "client.crashpad"' + ' },' + ' "$kitchen": {' + ' "devshell": true,' + ' "git_auth": true' + ' },' + ' "config": "Release",' + ' "recipe": "crashpad/build",' + ' "target_os": "mac"' + '}' + execution_timeout_secs: 10800 + caches { + name: "osx_sdk_mac" + path: "osx_sdk" + } + build_numbers: YES + service_account: "crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + } + builders { + name: "crashpad_win_x64_dbg" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "cores:8" + dimensions: "cpu:x86-64" + dimensions: "os:Windows-10" + dimensions: "pool:luci.flex.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$depot_tools/windows_sdk": {' + ' "version": "uploaded:2021-04-28"' + ' },' + ' "$gatekeeper": {' + ' "group": "client.crashpad"' + ' },' + ' "$kitchen": {' + ' "devshell": true,' + ' "git_auth": true' + ' },' + ' "config": "Debug",' + ' "recipe": "crashpad/build",' + ' "target_os": "win"' + '}' + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + } + builders { + name: "crashpad_win_x64_rel" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "cores:8" + dimensions: "cpu:x86-64" + dimensions: "os:Windows-10" + dimensions: "pool:luci.flex.ci" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$depot_tools/windows_sdk": {' + ' "version": "uploaded:2021-04-28"' + ' },' + ' "$gatekeeper": {' + ' "group": "client.crashpad"' + ' },' + ' "$kitchen": {' + ' "devshell": true,' + ' "git_auth": true' + ' },' + ' "config": "Release",' + ' "recipe": "crashpad/build",' + ' "target_os": "win"' + '}' + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + } + } +} +buckets { + name: "try" + acls { + role: WRITER + group: "project-crashpad-admins" + } + acls { + role: WRITER + group: "service-account-crashpad-cq" + } + acls { + group: "all" + } + acls { + role: SCHEDULER + group: "project-crashpad-tryjob-access" + } + acls { + role: SCHEDULER + group: "service-account-cq" + } + swarming { + builders { + name: "crashpad_fuchsia_arm64_dbg" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "cores:8" + dimensions: "cpu:x86-64" + dimensions: "os:Ubuntu-18.04" + dimensions: "pool:luci.flex.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$kitchen": {' + ' "devshell": true,' + ' "git_auth": true' + ' },' + ' "config": "Debug",' + ' "recipe": "crashpad/build",' + ' "target_cpu": "arm64",' + ' "target_os": "fuchsia"' + '}' + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + } + builders { + name: "crashpad_fuchsia_arm64_rel" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "cores:8" + dimensions: "cpu:x86-64" + dimensions: "os:Ubuntu-18.04" + dimensions: "pool:luci.flex.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$kitchen": {' + ' "devshell": true,' + ' "git_auth": true' + ' },' + ' "config": "Release",' + ' "recipe": "crashpad/build",' + ' "target_cpu": "arm64",' + ' "target_os": "fuchsia"' + '}' + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + } + builders { + name: "crashpad_fuchsia_x64_dbg" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "cores:8" + dimensions: "cpu:x86-64" + dimensions: "os:Ubuntu-18.04" + dimensions: "pool:luci.flex.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$kitchen": {' + ' "devshell": true,' + ' "git_auth": true' + ' },' + ' "config": "Debug",' + ' "recipe": "crashpad/build",' + ' "target_os": "fuchsia"' + '}' + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + } + builders { + name: "crashpad_fuchsia_x64_rel" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "cores:8" + dimensions: "cpu:x86-64" + dimensions: "os:Ubuntu-18.04" + dimensions: "pool:luci.flex.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$kitchen": {' + ' "devshell": true,' + ' "git_auth": true' + ' },' + ' "config": "Release",' + ' "recipe": "crashpad/build",' + ' "target_os": "fuchsia"' + '}' + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + } + builders { + name: "crashpad_ios_arm64_dbg" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-11" + dimensions: "pool:luci.flex.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$kitchen": {' + ' "devshell": true,' + ' "git_auth": true' + ' },' + ' "config": "Debug",' + ' "recipe": "crashpad/build",' + ' "target_cpu": "arm64",' + ' "target_os": "ios"' + '}' + execution_timeout_secs: 10800 + caches { + name: "osx_sdk_ios" + path: "osx_sdk" + } + build_numbers: YES + service_account: "crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + } + builders { + name: "crashpad_ios_arm64_rel" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-11" + dimensions: "pool:luci.flex.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$kitchen": {' + ' "devshell": true,' + ' "git_auth": true' + ' },' + ' "config": "Release",' + ' "recipe": "crashpad/build",' + ' "target_cpu": "arm64",' + ' "target_os": "ios"' + '}' + execution_timeout_secs: 10800 + caches { + name: "osx_sdk_ios" + path: "osx_sdk" + } + build_numbers: YES + service_account: "crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + } + builders { + name: "crashpad_ios_x64_dbg" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-11" + dimensions: "pool:luci.flex.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$kitchen": {' + ' "devshell": true,' + ' "git_auth": true' + ' },' + ' "config": "Debug",' + ' "recipe": "crashpad/build",' + ' "target_os": "ios"' + '}' + execution_timeout_secs: 10800 + caches { + name: "osx_sdk_ios" + path: "osx_sdk" + } + build_numbers: YES + service_account: "crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + } + builders { + name: "crashpad_ios_x64_rel" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-11" + dimensions: "pool:luci.flex.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$kitchen": {' + ' "devshell": true,' + ' "git_auth": true' + ' },' + ' "config": "Release",' + ' "recipe": "crashpad/build",' + ' "target_os": "ios"' + '}' + execution_timeout_secs: 10800 + caches { + name: "osx_sdk_ios" + path: "osx_sdk" + } + build_numbers: YES + service_account: "crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + } + builders { + name: "crashpad_linux_x64_dbg" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "cores:8" + dimensions: "cpu:x86-64" + dimensions: "os:Ubuntu-18.04" + dimensions: "pool:luci.flex.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$kitchen": {' + ' "devshell": true,' + ' "git_auth": true' + ' },' + ' "config": "Debug",' + ' "recipe": "crashpad/build",' + ' "target_os": "linux"' + '}' + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + } + builders { + name: "crashpad_linux_x64_rel" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "cores:8" + dimensions: "cpu:x86-64" + dimensions: "os:Ubuntu-18.04" + dimensions: "pool:luci.flex.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$kitchen": {' + ' "devshell": true,' + ' "git_auth": true' + ' },' + ' "config": "Release",' + ' "recipe": "crashpad/build",' + ' "target_os": "linux"' + '}' + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + } + builders { + name: "crashpad_mac_x64_dbg" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-11" + dimensions: "pool:luci.flex.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$kitchen": {' + ' "devshell": true,' + ' "git_auth": true' + ' },' + ' "config": "Debug",' + ' "recipe": "crashpad/build",' + ' "target_os": "mac"' + '}' + execution_timeout_secs: 10800 + caches { + name: "osx_sdk_mac" + path: "osx_sdk" + } + build_numbers: YES + service_account: "crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + } + builders { + name: "crashpad_mac_x64_rel" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "cpu:x86-64" + dimensions: "os:Mac-11" + dimensions: "pool:luci.flex.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$kitchen": {' + ' "devshell": true,' + ' "git_auth": true' + ' },' + ' "config": "Release",' + ' "recipe": "crashpad/build",' + ' "target_os": "mac"' + '}' + execution_timeout_secs: 10800 + caches { + name: "osx_sdk_mac" + path: "osx_sdk" + } + build_numbers: YES + service_account: "crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + } + builders { + name: "crashpad_win_x64_dbg" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "cores:8" + dimensions: "cpu:x86-64" + dimensions: "os:Windows-10" + dimensions: "pool:luci.flex.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$depot_tools/windows_sdk": {' + ' "version": "uploaded:2021-04-28"' + ' },' + ' "$kitchen": {' + ' "devshell": true,' + ' "git_auth": true' + ' },' + ' "config": "Debug",' + ' "recipe": "crashpad/build",' + ' "target_os": "win"' + '}' + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + } + builders { + name: "crashpad_win_x64_rel" + swarming_host: "chromium-swarm.appspot.com" + dimensions: "cores:8" + dimensions: "cpu:x86-64" + dimensions: "os:Windows-10" + dimensions: "pool:luci.flex.try" + exe { + cipd_package: "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build" + cipd_version: "refs/heads/main" + cmd: "luciexe" + } + properties: + '{' + ' "$depot_tools/windows_sdk": {' + ' "version": "uploaded:2021-04-28"' + ' },' + ' "$kitchen": {' + ' "devshell": true,' + ' "git_auth": true' + ' },' + ' "config": "Release",' + ' "recipe": "crashpad/build",' + ' "target_os": "win"' + '}' + execution_timeout_secs: 10800 + build_numbers: YES + service_account: "crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com" + experiments { + key: "luci.recipes.use_python3" + value: 100 + } + } + } +} diff --git a/shared/sentry/external/crashpad/infra/config/generated/luci-logdog.cfg b/shared/sentry/external/crashpad/infra/config/generated/luci-logdog.cfg new file mode 100644 index 000000000..adc75bef4 --- /dev/null +++ b/shared/sentry/external/crashpad/infra/config/generated/luci-logdog.cfg @@ -0,0 +1,9 @@ +# Auto-generated by lucicfg. +# Do not modify manually. +# +# For the schema of this file, see ProjectConfig message: +# https://luci-config.appspot.com/schemas/projects:luci-logdog.cfg + +reader_auth_groups: "all" +writer_auth_groups: "luci-logdog-chromium-writers" +archive_gs_bucket: "chromium-luci-logdog" diff --git a/shared/sentry/external/crashpad/infra/config/generated/luci-milo.cfg b/shared/sentry/external/crashpad/infra/config/generated/luci-milo.cfg new file mode 100644 index 000000000..6c891b14d --- /dev/null +++ b/shared/sentry/external/crashpad/infra/config/generated/luci-milo.cfg @@ -0,0 +1,131 @@ +# Auto-generated by lucicfg. +# Do not modify manually. +# +# For the schema of this file, see Project message: +# https://luci-config.appspot.com/schemas/projects:luci-milo.cfg + +consoles { + id: "main" + name: "Crashpad Main Console" + repo_url: "https://chromium.googlesource.com/crashpad/crashpad" + refs: "regexp:refs/heads/main" + manifest_name: "REVISION" + builders { + name: "buildbucket/luci.crashpad.ci/crashpad_fuchsia_arm64_dbg" + category: "fuchsia|arm64" + short_name: "dbg" + } + builders { + name: "buildbucket/luci.crashpad.ci/crashpad_fuchsia_arm64_rel" + category: "fuchsia|arm64" + short_name: "rel" + } + builders { + name: "buildbucket/luci.crashpad.ci/crashpad_fuchsia_x64_dbg" + category: "fuchsia|x64" + short_name: "dbg" + } + builders { + name: "buildbucket/luci.crashpad.ci/crashpad_fuchsia_x64_rel" + category: "fuchsia|x64" + short_name: "rel" + } + builders { + name: "buildbucket/luci.crashpad.ci/crashpad_ios_arm64_dbg" + category: "ios|arm64" + short_name: "dbg" + } + builders { + name: "buildbucket/luci.crashpad.ci/crashpad_ios_arm64_rel" + category: "ios|arm64" + short_name: "rel" + } + builders { + name: "buildbucket/luci.crashpad.ci/crashpad_ios_x64_dbg" + category: "ios|x64" + short_name: "dbg" + } + builders { + name: "buildbucket/luci.crashpad.ci/crashpad_ios_x64_rel" + category: "ios|x64" + short_name: "rel" + } + builders { + name: "buildbucket/luci.crashpad.ci/crashpad_linux_x64_dbg" + category: "linux|x64" + short_name: "dbg" + } + builders { + name: "buildbucket/luci.crashpad.ci/crashpad_linux_x64_rel" + category: "linux|x64" + short_name: "rel" + } + builders { + name: "buildbucket/luci.crashpad.ci/crashpad_mac_x64_dbg" + category: "mac|x64" + short_name: "dbg" + } + builders { + name: "buildbucket/luci.crashpad.ci/crashpad_mac_x64_rel" + category: "mac|x64" + short_name: "rel" + } + builders { + name: "buildbucket/luci.crashpad.ci/crashpad_win_x64_dbg" + category: "win|x64" + short_name: "dbg" + } + builders { + name: "buildbucket/luci.crashpad.ci/crashpad_win_x64_rel" + category: "win|x64" + short_name: "rel" + } +} +consoles { + id: "try" + name: "Crashpad Try Builders" + builders { + name: "buildbucket/luci.crashpad.try/crashpad_fuchsia_arm64_dbg" + } + builders { + name: "buildbucket/luci.crashpad.try/crashpad_fuchsia_arm64_rel" + } + builders { + name: "buildbucket/luci.crashpad.try/crashpad_fuchsia_x64_dbg" + } + builders { + name: "buildbucket/luci.crashpad.try/crashpad_fuchsia_x64_rel" + } + builders { + name: "buildbucket/luci.crashpad.try/crashpad_ios_arm64_dbg" + } + builders { + name: "buildbucket/luci.crashpad.try/crashpad_ios_arm64_rel" + } + builders { + name: "buildbucket/luci.crashpad.try/crashpad_ios_x64_dbg" + } + builders { + name: "buildbucket/luci.crashpad.try/crashpad_ios_x64_rel" + } + builders { + name: "buildbucket/luci.crashpad.try/crashpad_linux_x64_dbg" + } + builders { + name: "buildbucket/luci.crashpad.try/crashpad_linux_x64_rel" + } + builders { + name: "buildbucket/luci.crashpad.try/crashpad_mac_x64_dbg" + } + builders { + name: "buildbucket/luci.crashpad.try/crashpad_mac_x64_rel" + } + builders { + name: "buildbucket/luci.crashpad.try/crashpad_win_x64_dbg" + } + builders { + name: "buildbucket/luci.crashpad.try/crashpad_win_x64_rel" + } + builder_view_only: true +} +logo_url: "https://storage.googleapis.com/chrome-infra-public/logo/crashpad-logo.svg" diff --git a/shared/sentry/external/crashpad/infra/config/generated/luci-scheduler.cfg b/shared/sentry/external/crashpad/infra/config/generated/luci-scheduler.cfg new file mode 100644 index 000000000..a2251eb89 --- /dev/null +++ b/shared/sentry/external/crashpad/infra/config/generated/luci-scheduler.cfg @@ -0,0 +1,179 @@ +# Auto-generated by lucicfg. +# Do not modify manually. +# +# For the schema of this file, see ProjectConfig message: +# https://luci-config.appspot.com/schemas/projects:luci-scheduler.cfg + +job { + id: "crashpad_fuchsia_arm64_dbg" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "crashpad_fuchsia_arm64_dbg" + } +} +job { + id: "crashpad_fuchsia_arm64_rel" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "crashpad_fuchsia_arm64_rel" + } +} +job { + id: "crashpad_fuchsia_x64_dbg" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "crashpad_fuchsia_x64_dbg" + } +} +job { + id: "crashpad_fuchsia_x64_rel" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "crashpad_fuchsia_x64_rel" + } +} +job { + id: "crashpad_ios_arm64_dbg" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "crashpad_ios_arm64_dbg" + } +} +job { + id: "crashpad_ios_arm64_rel" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "crashpad_ios_arm64_rel" + } +} +job { + id: "crashpad_ios_x64_dbg" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "crashpad_ios_x64_dbg" + } +} +job { + id: "crashpad_ios_x64_rel" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "crashpad_ios_x64_rel" + } +} +job { + id: "crashpad_linux_x64_dbg" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "crashpad_linux_x64_dbg" + } +} +job { + id: "crashpad_linux_x64_rel" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "crashpad_linux_x64_rel" + } +} +job { + id: "crashpad_mac_x64_dbg" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "crashpad_mac_x64_dbg" + } +} +job { + id: "crashpad_mac_x64_rel" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "crashpad_mac_x64_rel" + } +} +job { + id: "crashpad_win_x64_dbg" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "crashpad_win_x64_dbg" + } +} +job { + id: "crashpad_win_x64_rel" + realm: "ci" + acl_sets: "ci" + buildbucket { + server: "cr-buildbucket.appspot.com" + bucket: "ci" + builder: "crashpad_win_x64_rel" + } +} +trigger { + id: "master-gitiles-trigger" + realm: "ci" + acl_sets: "ci" + triggers: "crashpad_fuchsia_arm64_dbg" + triggers: "crashpad_fuchsia_arm64_rel" + triggers: "crashpad_fuchsia_x64_dbg" + triggers: "crashpad_fuchsia_x64_rel" + triggers: "crashpad_ios_arm64_dbg" + triggers: "crashpad_ios_arm64_rel" + triggers: "crashpad_ios_x64_dbg" + triggers: "crashpad_ios_x64_rel" + triggers: "crashpad_linux_x64_dbg" + triggers: "crashpad_linux_x64_rel" + triggers: "crashpad_mac_x64_dbg" + triggers: "crashpad_mac_x64_rel" + triggers: "crashpad_win_x64_dbg" + triggers: "crashpad_win_x64_rel" + gitiles { + repo: "https://chromium.googlesource.com/crashpad/crashpad" + refs: "regexp:refs/heads/main" + } +} +acl_sets { + name: "ci" + acls { + role: OWNER + granted_to: "group:project-crashpad-admins" + } + acls { + granted_to: "group:all" + } +} diff --git a/shared/sentry/external/crashpad/infra/config/generated/project.cfg b/shared/sentry/external/crashpad/infra/config/generated/project.cfg new file mode 100644 index 000000000..28e9f2e62 --- /dev/null +++ b/shared/sentry/external/crashpad/infra/config/generated/project.cfg @@ -0,0 +1,15 @@ +# Auto-generated by lucicfg. +# Do not modify manually. +# +# For the schema of this file, see ProjectCfg message: +# https://luci-config.appspot.com/schemas/projects:project.cfg + +name: "crashpad" +access: "group:all" +lucicfg { + version: "1.30.9" + package_dir: ".." + config_dir: "generated" + entry_point: "main.star" + experiments: "crbug.com/1182002" +} diff --git a/shared/sentry/external/crashpad/infra/config/generated/realms.cfg b/shared/sentry/external/crashpad/infra/config/generated/realms.cfg new file mode 100644 index 000000000..8dc05f6b5 --- /dev/null +++ b/shared/sentry/external/crashpad/infra/config/generated/realms.cfg @@ -0,0 +1,65 @@ +# Auto-generated by lucicfg. +# Do not modify manually. +# +# For the schema of this file, see RealmsCfg message: +# https://luci-config.appspot.com/schemas/projects:realms.cfg + +realms { + name: "@root" + bindings { + role: "role/buildbucket.reader" + principals: "group:all" + } + bindings { + role: "role/configs.reader" + principals: "group:all" + } + bindings { + role: "role/logdog.reader" + principals: "group:all" + } + bindings { + role: "role/logdog.writer" + principals: "group:luci-logdog-chromium-writers" + } + bindings { + role: "role/scheduler.owner" + principals: "group:project-crashpad-admins" + } + bindings { + role: "role/scheduler.reader" + principals: "group:all" + } +} +realms { + name: "ci" + bindings { + role: "role/buildbucket.builderServiceAccount" + principals: "user:crashpad-ci-builder@chops-service-accounts.iam.gserviceaccount.com" + } + bindings { + role: "role/buildbucket.owner" + principals: "group:project-crashpad-admins" + } + bindings { + role: "role/buildbucket.triggerer" + principals: "user:luci-scheduler@appspot.gserviceaccount.com" + } +} +realms { + name: "try" + bindings { + role: "role/buildbucket.builderServiceAccount" + principals: "user:crashpad-try-builder@chops-service-accounts.iam.gserviceaccount.com" + } + bindings { + role: "role/buildbucket.owner" + principals: "group:project-crashpad-admins" + principals: "group:service-account-crashpad-cq" + } + bindings { + role: "role/buildbucket.triggerer" + principals: "group:project-crashpad-tryjob-access" + principals: "group:service-account-cq" + } +} diff --git a/shared/sentry/external/crashpad/infra/config/main.star b/shared/sentry/external/crashpad/infra/config/main.star new file mode 100755 index 000000000..fade25230 --- /dev/null +++ b/shared/sentry/external/crashpad/infra/config/main.star @@ -0,0 +1,265 @@ +#!/usr/bin/env lucicfg +# Copyright 2021 The Crashpad Authors. All rights reserved. +# +# 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. + +lucicfg.check_version("1.30.9", "Please update depot_tools") + +REPO_URL = "https://chromium.googlesource.com/crashpad/crashpad" +REVIEW_URL = "https://chromium-review.googlesource.com/crashpad/crashpad" + +# Use LUCI Scheduler BBv2 names and add Scheduler realms configs. +lucicfg.enable_experiment("crbug.com/1182002") + +luci.project( + name = "crashpad", + buildbucket = "cr-buildbucket.appspot.com", + swarming = "chromium-swarm.appspot.com", + acls = [ + acl.entry( + roles = [ + acl.LOGDOG_READER, + acl.PROJECT_CONFIGS_READER, + acl.SCHEDULER_READER, + acl.BUILDBUCKET_READER, + ], + groups = "all", + ), + acl.entry( + roles = acl.LOGDOG_WRITER, + groups = "luci-logdog-chromium-writers", + ), + acl.entry( + roles = acl.SCHEDULER_OWNER, + groups = "project-crashpad-admins", + ), + ], + logdog = "luci-logdog.appspot.com", + milo = "luci-milo.appspot.com", + scheduler = "luci-scheduler.appspot.com", +) + +luci.cq( + status_host = "chromium-cq-status.appspot.com", + submit_max_burst = 4, + submit_burst_delay = 8 * time.minute, +) + +luci.cq_group( + name = "crashpad", + watch = cq.refset(repo = REVIEW_URL, refs = ["refs/heads/.+"]), + retry_config = cq.retry_config( + single_quota = 1, + global_quota = 2, + failure_weight = 1, + transient_failure_weight = 1, + timeout_weight = 2, + ), + acls = [ + acl.entry( + roles = acl.CQ_COMMITTER, + groups = "project-crashpad-tryjob-access", + ), + acl.entry( + roles = acl.CQ_DRY_RUNNER, + groups = "project-crashpad-tryjob-access", + ), + ], +) + +luci.gitiles_poller( + name = "master-gitiles-trigger", + bucket = "ci", + repo = REPO_URL, +) + +luci.logdog( + gs_bucket = "chromium-luci-logdog", +) + +luci.milo( + logo = "https://storage.googleapis.com/chrome-infra-public/logo/crashpad-logo.svg", +) + +luci.console_view( + name = "main", + repo = REPO_URL, + title = "Crashpad Main Console", +) + +luci.list_view( + name = "try", + title = "Crashpad Try Builders", +) + +luci.bucket( + name = "ci", + acls = [ + acl.entry( + acl.BUILDBUCKET_OWNER, + groups = "project-crashpad-admins", + ), + acl.entry( + acl.BUILDBUCKET_TRIGGERER, + users = "luci-scheduler@appspot.gserviceaccount.com", + ), + ], +) + +luci.bucket( + name = "try", + acls = [ + acl.entry( + acl.BUILDBUCKET_OWNER, + groups = [ + "service-account-crashpad-cq", + "project-crashpad-admins", + ], + ), + acl.entry( + acl.BUILDBUCKET_TRIGGERER, + groups = "service-account-cq", + ), + acl.entry( + acl.BUILDBUCKET_TRIGGERER, + groups = "project-crashpad-tryjob-access", + ), + ], +) + +def crashpad_recipe(): + return luci.recipe( + name = "crashpad/build", + cipd_package = "infra/recipe_bundles/chromium.googlesource.com/chromium/tools/build", + use_python3=True, + ) + +def crashpad_caches(platform): + if platform == "ios": + return [swarming.cache("osx_sdk", name = "osx_sdk_ios")] + elif platform == "mac": + return [swarming.cache("osx_sdk", name = "osx_sdk_mac")] + +def crashpad_dimensions(platform, bucket): + dimensions = {} + dimensions["cpu"] = "x86-64" + dimensions["pool"] = "luci.flex." + bucket + + if platform == "fuchsia": + dimensions["os"] = "Ubuntu-18.04" + elif platform == "ios": + dimensions["os"] = "Mac-11" + elif platform == "linux": + dimensions["os"] = "Ubuntu-18.04" + elif platform == "mac": + dimensions["os"] = "Mac-11" + elif platform == "win": + dimensions["os"] = "Windows-10" + + if platform == "fuchsia" or platform == "linux" or platform == "win": + dimensions["cores"] = "8" + + return dimensions + +def crashpad_properties(platform, cpu, config, bucket): + properties = {} + properties["target_os"] = platform + properties["$kitchen"] = { + "devshell": True, + "git_auth": True, + } + + if cpu != "x64": + properties["target_cpu"] = cpu + + if platform == "win": + properties["$depot_tools/windows_sdk"] = { + "version": "uploaded:2021-04-28", + } + + if bucket == "ci": + properties["$gatekeeper"] = { + "group": "client.crashpad", + } + + if config == "dbg": + properties["config"] = "Debug" + elif config == "rel": + properties["config"] = "Release" + + return properties + +def crashpad_builder(platform, cpu, config, bucket): + name = "_".join(["crashpad", platform, cpu, config]) + triggered_by = None + + if bucket == "ci": + luci.console_view_entry( + builder = "ci/" + name, + console_view = "main", + short_name = config, + category = platform + "|" + cpu, + ) + triggered_by = ["master-gitiles-trigger"] + elif bucket == "try": + luci.list_view_entry( + builder = "try/" + name, + list_view = "try", + ) + luci.cq_tryjob_verifier( + "try/" + name, + cq_group = "crashpad", + ) + + return luci.builder( + name = name, + bucket = bucket, + executable = crashpad_recipe(), + build_numbers = True, + caches = crashpad_caches(platform), + dimensions = crashpad_dimensions(platform, bucket), + execution_timeout = 3 * time.hour, + properties = crashpad_properties(platform, cpu, config, bucket), + service_account = "crashpad-" + bucket + "-builder@chops-service-accounts.iam.gserviceaccount.com", + triggered_by = triggered_by, + ) + +crashpad_builder("fuchsia", "arm64", "dbg", "ci") +crashpad_builder("fuchsia", "arm64", "rel", "ci") +crashpad_builder("fuchsia", "x64", "dbg", "ci") +crashpad_builder("fuchsia", "x64", "rel", "ci") +crashpad_builder("ios", "arm64", "dbg", "ci") +crashpad_builder("ios", "arm64", "rel", "ci") +crashpad_builder("ios", "x64", "dbg", "ci") +crashpad_builder("ios", "x64", "rel", "ci") +crashpad_builder("linux", "x64", "dbg", "ci") +crashpad_builder("linux", "x64", "rel", "ci") +crashpad_builder("mac", "x64", "dbg", "ci") +crashpad_builder("mac", "x64", "rel", "ci") +crashpad_builder("win", "x64", "dbg", "ci") +crashpad_builder("win", "x64", "rel", "ci") + +crashpad_builder("fuchsia", "arm64", "dbg", "try") +crashpad_builder("fuchsia", "arm64", "rel", "try") +crashpad_builder("fuchsia", "x64", "dbg", "try") +crashpad_builder("fuchsia", "x64", "rel", "try") +crashpad_builder("ios", "arm64", "dbg", "try") +crashpad_builder("ios", "arm64", "rel", "try") +crashpad_builder("ios", "x64", "dbg", "try") +crashpad_builder("ios", "x64", "rel", "try") +crashpad_builder("linux", "x64", "dbg", "try") +crashpad_builder("linux", "x64", "rel", "try") +crashpad_builder("mac", "x64", "dbg", "try") +crashpad_builder("mac", "x64", "rel", "try") +crashpad_builder("win", "x64", "dbg", "try") +crashpad_builder("win", "x64", "rel", "try") diff --git a/shared/sentry/external/crashpad/libunwind/.clang-format b/shared/sentry/external/crashpad/libunwind/.clang-format new file mode 100644 index 000000000..5bead5f39 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/.clang-format @@ -0,0 +1,2 @@ +BasedOnStyle: LLVM + diff --git a/shared/sentry/external/crashpad/libunwind/CMakeLists.txt b/shared/sentry/external/crashpad/libunwind/CMakeLists.txt new file mode 100644 index 000000000..ec0049ddb --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/CMakeLists.txt @@ -0,0 +1,369 @@ +#=============================================================================== +# Setup Project +#=============================================================================== + +cmake_minimum_required(VERSION 3.13.4) + +# Add path for custom modules +set(CMAKE_MODULE_PATH + "${CMAKE_CURRENT_SOURCE_DIR}/cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/Modules" + ${CMAKE_MODULE_PATH} + ) + +set(LIBUNWIND_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) +set(LIBUNWIND_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) +set(LIBUNWIND_LIBCXX_PATH "${CMAKE_CURRENT_LIST_DIR}/../libcxx" CACHE PATH + "Specify path to libc++ source.") + +if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR OR LIBUNWIND_STANDALONE_BUILD) + project(libunwind LANGUAGES C CXX ASM) + + set(PACKAGE_NAME libunwind) + set(PACKAGE_VERSION 14.0.0git) + set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}") + set(PACKAGE_BUGREPORT "llvm-bugs@lists.llvm.org") + + # Add the CMake module path of libcxx so we can reuse HandleOutOfTreeLLVM.cmake + set(LIBUNWIND_LIBCXX_CMAKE_PATH "${LIBUNWIND_LIBCXX_PATH}/cmake/Modules") + list(APPEND CMAKE_MODULE_PATH "${LIBUNWIND_LIBCXX_CMAKE_PATH}") + + # In a standalone build, we don't have llvm to automatically generate the + # llvm-lit script for us. So we need to provide an explicit directory that + # the configurator should write the script into. + set(LIBUNWIND_STANDALONE_BUILD 1) + set(LLVM_LIT_OUTPUT_DIR "${LIBUNWIND_BINARY_DIR}/bin") + + # Find the LLVM sources and simulate LLVM CMake options. + include(HandleOutOfTreeLLVM) +else() + set(LLVM_LIT "${CMAKE_SOURCE_DIR}/utils/lit/lit.py") +endif() + +#=============================================================================== +# Setup CMake Options +#=============================================================================== +include(CMakeDependentOption) +include(HandleCompilerRT) + +# Define options. +option(LIBUNWIND_BUILD_32_BITS "Build 32 bit libunwind" ${LLVM_BUILD_32_BITS}) +option(LIBUNWIND_ENABLE_CET "Build libunwind with CET enabled." OFF) +option(LIBUNWIND_ENABLE_ASSERTIONS "Enable assertions independent of build mode." ON) +option(LIBUNWIND_ENABLE_PEDANTIC "Compile with pedantic enabled." ON) +option(LIBUNWIND_ENABLE_WERROR "Fail and stop if a warning is triggered." OFF) +option(LIBUNWIND_ENABLE_SHARED "Build libunwind as a shared library." ON) +option(LIBUNWIND_ENABLE_STATIC "Build libunwind as a static library." ON) +option(LIBUNWIND_ENABLE_CROSS_UNWINDING "Enable cross-platform unwinding support." OFF) +option(LIBUNWIND_ENABLE_ARM_WMMX "Enable unwinding support for ARM WMMX registers." OFF) +option(LIBUNWIND_ENABLE_THREADS "Build libunwind with threading support." ON) +option(LIBUNWIND_WEAK_PTHREAD_LIB "Use weak references to refer to pthread functions." OFF) +option(LIBUNWIND_USE_COMPILER_RT "Use compiler-rt instead of libgcc" OFF) +option(LIBUNWIND_INCLUDE_DOCS "Build the libunwind documentation." ${LLVM_INCLUDE_DOCS}) +option(LIBUNWIND_INCLUDE_TESTS "Build the libunwind tests." ${LLVM_INCLUDE_TESTS}) +option(LIBUNWIND_IS_BAREMETAL "Build libunwind for baremetal targets." OFF) +option(LIBUNWIND_USE_FRAME_HEADER_CACHE "Cache frame headers for unwinding. Requires locking dl_iterate_phdr." OFF) +option(LIBUNWIND_REMEMBER_HEAP_ALLOC "Use heap instead of the stack for .cfi_remember_state." OFF) + +set(LIBUNWIND_LIBDIR_SUFFIX "${LLVM_LIBDIR_SUFFIX}" CACHE STRING + "Define suffix of library directory name (32/64)") +option(LIBUNWIND_INSTALL_LIBRARY "Install the libunwind library." ON) +cmake_dependent_option(LIBUNWIND_INSTALL_STATIC_LIBRARY + "Install the static libunwind library." ON + "LIBUNWIND_ENABLE_STATIC;LIBUNWIND_INSTALL_LIBRARY" OFF) +cmake_dependent_option(LIBUNWIND_INSTALL_SHARED_LIBRARY + "Install the shared libunwind library." ON + "LIBUNWIND_ENABLE_SHARED;LIBUNWIND_INSTALL_LIBRARY" OFF) +set(LIBUNWIND_TARGET_TRIPLE "${LLVM_DEFAULT_TARGET_TRIPLE}" CACHE STRING "Target triple for cross compiling.") +set(LIBUNWIND_GCC_TOOLCHAIN "" CACHE PATH "GCC toolchain for cross compiling.") +set(LIBUNWIND_SYSROOT "" CACHE PATH "Sysroot for cross compiling.") +set(LIBUNWIND_TEST_LINKER_FLAGS "" CACHE STRING + "Additional linker flags for test programs.") +set(LIBUNWIND_TEST_COMPILER_FLAGS "" CACHE STRING + "Additional compiler flags for test programs.") +set(LIBUNWIND_TEST_CONFIG "${CMAKE_CURRENT_SOURCE_DIR}/test/lit.site.cfg.in" CACHE STRING + "The path to the Lit testing configuration to use when running the tests. + If a relative path is provided, it is assumed to be relative to '/libcxx/test/configs'.") +if (NOT IS_ABSOLUTE "${LIBUNWIND_TEST_CONFIG}") + set(LIBUNWIND_TEST_CONFIG "${LIBUNWIND_LIBCXX_PATH}/test/configs/${LIBUNWIND_TEST_CONFIG}") +endif() +set(LIBUNWIND_TEST_PARAMS "" CACHE STRING + "A list of parameters to run the Lit test suite with.") + +if (NOT LIBUNWIND_ENABLE_SHARED AND NOT LIBUNWIND_ENABLE_STATIC) + message(FATAL_ERROR "libunwind must be built as either a shared or static library.") +endif() + +if (LIBUNWIND_ENABLE_CET AND MSVC) + message(FATAL_ERROR "libunwind CET support is not available for MSVC!") +endif() + +# Check that we can build with 32 bits if requested. +if (CMAKE_SIZEOF_VOID_P EQUAL 8 AND NOT WIN32) + if (LIBUNWIND_BUILD_32_BITS AND NOT LLVM_BUILD_32_BITS) # Don't duplicate the output from LLVM + message(STATUS "Building 32 bits executables and libraries.") + endif() +elseif(LIBUNWIND_BUILD_32_BITS) + message(FATAL_ERROR "LIBUNWIND_BUILD_32_BITS=ON is not supported on this platform.") +endif() + +option(LIBUNWIND_HIDE_SYMBOLS + "Do not export any symbols from the static library." OFF) + +#=============================================================================== +# Configure System +#=============================================================================== + +# Add path for custom modules +set(CMAKE_MODULE_PATH + "${CMAKE_CURRENT_SOURCE_DIR}/cmake" + ${CMAKE_MODULE_PATH}) + +if(LLVM_ENABLE_PER_TARGET_RUNTIME_DIR AND NOT APPLE) + set(LIBUNWIND_LIBRARY_DIR ${LLVM_LIBRARY_OUTPUT_INTDIR}/${LLVM_DEFAULT_TARGET_TRIPLE}) + set(LIBUNWIND_INSTALL_LIBRARY_DIR lib${LLVM_LIBDIR_SUFFIX}/${LLVM_DEFAULT_TARGET_TRIPLE} CACHE PATH + "Path where built libunwind libraries should be installed.") + set(LIBUNWIND_INSTALL_RUNTIME_DIR bin CACHE PATH + "Path where built libunwind runtime libraries should be installed.") + if(LIBCXX_LIBDIR_SUBDIR) + string(APPEND LIBUNWIND_LIBRARY_DIR /${LIBUNWIND_LIBDIR_SUBDIR}) + string(APPEND LIBUNWIND_INSTALL_LIBRARY_DIR /${LIBUNWIND_LIBDIR_SUBDIR}) + endif() +elseif(LLVM_LIBRARY_OUTPUT_INTDIR) + set(LIBUNWIND_LIBRARY_DIR ${LLVM_LIBRARY_OUTPUT_INTDIR}) + set(LIBUNWIND_INSTALL_LIBRARY_DIR lib${LIBUNWIND_LIBDIR_SUFFIX} CACHE PATH + "Path where built libunwind libraries should be installed.") + set(LIBUNWIND_INSTALL_RUNTIME_DIR bin CACHE PATH + "Path where built libunwind runtime libraries should be installed.") +else() + set(LIBUNWIND_LIBRARY_DIR ${CMAKE_BINARY_DIR}/lib${LIBUNWIND_LIBDIR_SUFFIX}) + set(LIBUNWIND_INSTALL_LIBRARY_DIR lib${LIBUNWIND_LIBDIR_SUFFIX} CACHE PATH + "Path where built libunwind libraries should be installed.") + set(LIBUNWIND_INSTALL_RUNTIME_DIR bin CACHE PATH + "Path where built libunwind runtime libraries should be installed.") +endif() + +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${LIBUNWIND_LIBRARY_DIR}) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${LIBUNWIND_LIBRARY_DIR}) +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${LIBUNWIND_LIBRARY_DIR}) + +set(LIBUNWIND_C_FLAGS "") +set(LIBUNWIND_CXX_FLAGS "") +set(LIBUNWIND_COMPILE_FLAGS "") +set(LIBUNWIND_LINK_FLAGS "") + +# Include macros for adding and removing libunwind flags. +include(HandleLibunwindFlags) + +#=============================================================================== +# Setup Compiler Flags +#=============================================================================== + +# Get required flags. +add_target_flags_if(LIBUNWIND_BUILD_32_BITS "-m32") + +if(LIBUNWIND_TARGET_TRIPLE) + add_target_flags_if_supported("--target=${LIBUNWIND_TARGET_TRIPLE}") +elseif(CMAKE_CXX_COMPILER_TARGET) + set(LIBUNWIND_TARGET_TRIPLE "${CMAKE_CXX_COMPILER_TARGET}") +endif() +if(LIBUNWIND_GCC_TOOLCHAIN) + add_target_flags_if_supported("--gcc-toolchain=${LIBUNWIND_GCC_TOOLCHAIN}") +elseif(CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN) + set(LIBUNWIND_GCC_TOOLCHAIN "${CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN}") +endif() +if(LIBUNWIND_SYSROOT) + add_target_flags_if_supported("--sysroot=${LIBUNWIND_SYSROOT}") +elseif(CMAKE_SYSROOT) + set(LIBUNWIND_SYSROOT "${CMAKE_SYSROOT}") +endif() + +# Configure compiler. +include(config-ix) + +if (LIBUNWIND_USE_COMPILER_RT AND NOT LIBUNWIND_HAS_NODEFAULTLIBS_FLAG) + list(APPEND LIBUNWIND_LINK_FLAGS "-rtlib=compiler-rt") +endif() + +add_compile_flags_if_supported(-Werror=return-type) + +if (LIBUNWIND_ENABLE_CET) + add_compile_flags_if_supported(-fcf-protection=full) + add_compile_flags_if_supported(-mshstk) + if (NOT LIBUNWIND_SUPPORTS_FCF_PROTECTION_EQ_FULL_FLAG) + message(SEND_ERROR "Compiler doesn't support CET -fcf-protection option!") + endif() + if (NOT LIBUNWIND_SUPPORTS_MSHSTK_FLAG) + message(SEND_ERROR "Compiler doesn't support CET -mshstk option!") + endif() +endif() + +# Get warning flags +add_compile_flags_if_supported(-W) +add_compile_flags_if_supported(-Wall) +add_compile_flags_if_supported(-Wchar-subscripts) +add_compile_flags_if_supported(-Wconversion) +add_compile_flags_if_supported(-Wmismatched-tags) +add_compile_flags_if_supported(-Wmissing-braces) +add_compile_flags_if_supported(-Wnewline-eof) +add_compile_flags_if_supported(-Wno-unused-function) +add_compile_flags_if_supported(-Wshadow) +add_compile_flags_if_supported(-Wshorten-64-to-32) +add_compile_flags_if_supported(-Wsign-compare) +add_compile_flags_if_supported(-Wsign-conversion) +add_compile_flags_if_supported(-Wstrict-aliasing=2) +add_compile_flags_if_supported(-Wstrict-overflow=4) +add_compile_flags_if_supported(-Wunused-parameter) +add_compile_flags_if_supported(-Wunused-variable) +add_compile_flags_if_supported(-Wwrite-strings) +add_compile_flags_if_supported(-Wundef) + +add_compile_flags_if_supported(-Wno-suggest-override) + +if (WIN32) + # The headers lack matching dllexport attributes (_LIBUNWIND_EXPORT); + # silence the warning instead of cluttering the headers (which aren't + # necessarily the ones that the callers will use anyway) with the + # attributes. + add_compile_flags_if_supported(-Wno-dll-attribute-on-redeclaration) +endif() + +if (LIBUNWIND_ENABLE_WERROR) + add_compile_flags_if_supported(-Werror) + add_compile_flags_if_supported(-WX) +else() + add_compile_flags_if_supported(-Wno-error) + add_compile_flags_if_supported(-WX-) +endif() + +if (LIBUNWIND_ENABLE_PEDANTIC) + add_compile_flags_if_supported(-pedantic) +endif() + +# Get feature flags. +# Exceptions +# Catches C++ exceptions only and tells the compiler to assume that extern C +# functions never throw a C++ exception. +add_cxx_compile_flags_if_supported(-fstrict-aliasing) +add_cxx_compile_flags_if_supported(-EHsc) + +# Don't run the linker in this CMake check. +# +# The reason why this was added is that when building libunwind for +# ARM Linux, we need to pass the -funwind-tables flag in order for it to +# work properly with ARM EHABI. +# +# However, when performing CMake checks, adding this flag causes the check +# to produce a false negative, because the compiler generates calls +# to __aeabi_unwind_cpp_pr0, which is defined in libunwind itself, +# which isn't built yet, so the linker complains about undefined symbols. +# +# This leads to libunwind not being built with this flag, which makes +# libunwind quite useless in this setup. +set(_previous_CMAKE_TRY_COMPILE_TARGET_TYPE ${CMAKE_TRY_COMPILE_TARGET_TYPE}) +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +add_compile_flags_if_supported(-funwind-tables) +set(CMAKE_TRY_COMPILE_TARGET_TYPE ${_previous_CMAKE_TRY_COMPILE_TARGET_TYPE}) + +if (LIBUNWIND_USES_ARM_EHABI AND NOT LIBUNWIND_SUPPORTS_FUNWIND_TABLES_FLAG) + message(SEND_ERROR "The -funwind-tables flag must be supported " + "because this target uses ARM Exception Handling ABI") +endif() + +add_cxx_compile_flags_if_supported(-fno-exceptions) +add_cxx_compile_flags_if_supported(-fno-rtti) + +# Ensure that we don't depend on C++ standard library. +if (LIBUNWIND_HAS_NOSTDINCXX_FLAG) + list(APPEND LIBUNWIND_COMPILE_FLAGS -nostdinc++) + # Remove -stdlib flags to prevent them from causing an unused flag warning. + string(REPLACE "--stdlib=libc++" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + string(REPLACE "--stdlib=libstdc++" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + string(REPLACE "-stdlib=libc++" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + string(REPLACE "-stdlib=libstdc++" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") +endif() + +# Assert +string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE) +if (LIBUNWIND_ENABLE_ASSERTIONS) + # MSVC doesn't like _DEBUG on release builds. See PR 4379. + if (NOT MSVC) + add_compile_flags(-D_DEBUG) + endif() + + # On Release builds cmake automatically defines NDEBUG, so we + # explicitly undefine it: + if (uppercase_CMAKE_BUILD_TYPE STREQUAL "RELEASE") + add_compile_flags(-UNDEBUG) + endif() +else() + if (NOT uppercase_CMAKE_BUILD_TYPE STREQUAL "RELEASE") + add_compile_flags(-DNDEBUG) + endif() +endif() + +# Cross-unwinding +if (NOT LIBUNWIND_ENABLE_CROSS_UNWINDING) + add_compile_flags(-D_LIBUNWIND_IS_NATIVE_ONLY) +endif() + +# Threading-support +if (NOT LIBUNWIND_ENABLE_THREADS) + add_compile_flags(-D_LIBUNWIND_HAS_NO_THREADS) +endif() + +# ARM WMMX register support +if (LIBUNWIND_ENABLE_ARM_WMMX) + # __ARM_WMMX is a compiler pre-define (as per the ACLE 2.0). Clang does not + # define this macro for any supported target at present. Therefore, here we + # provide the option to explicitly enable support for WMMX registers in the + # unwinder. + add_compile_flags(-D__ARM_WMMX) +endif() + +if(LIBUNWIND_IS_BAREMETAL) + add_compile_definitions(_LIBUNWIND_IS_BAREMETAL) +endif() + +if(LIBUNWIND_USE_FRAME_HEADER_CACHE) + add_compile_definitions(_LIBUNWIND_USE_FRAME_HEADER_CACHE) +endif() + +if(LIBUNWIND_REMEMBER_HEAP_ALLOC) + add_compile_definitions(_LIBUNWIND_REMEMBER_HEAP_ALLOC) +endif() + +# This is the _ONLY_ place where add_definitions is called. +if (MSVC) + add_definitions(-D_CRT_SECURE_NO_WARNINGS) +endif() + +# Disable DLL annotations on Windows for static builds. +if (WIN32 AND LIBUNWIND_ENABLE_STATIC AND NOT LIBUNWIND_ENABLE_SHARED) + add_definitions(-D_LIBUNWIND_HIDE_SYMBOLS) +endif() + +if (LIBUNWIND_HAS_COMMENT_LIB_PRAGMA) + if (LIBUNWIND_HAS_DL_LIB) + add_definitions(-D_LIBUNWIND_LINK_DL_LIB) + endif() + if (LIBUNWIND_HAS_PTHREAD_LIB) + add_definitions(-D_LIBUNWIND_LINK_PTHREAD_LIB) + endif() +endif() + +#=============================================================================== +# Setup Source Code +#=============================================================================== + +include_directories(include) + +add_subdirectory(src) + +if (LIBUNWIND_INCLUDE_DOCS) + add_subdirectory(docs) +endif() + +if (LIBUNWIND_INCLUDE_TESTS AND EXISTS ${LLVM_CMAKE_DIR}) + add_subdirectory(test) +endif() diff --git a/shared/sentry/external/crashpad/libunwind/LICENSE.TXT b/shared/sentry/external/crashpad/libunwind/LICENSE.TXT new file mode 100644 index 000000000..1e3120621 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/LICENSE.TXT @@ -0,0 +1,311 @@ +============================================================================== +The LLVM Project is under the Apache License v2.0 with LLVM Exceptions: +============================================================================== + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + +---- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + +============================================================================== +Software from third parties included in the LLVM Project: +============================================================================== +The LLVM Project contains third party software which is under different license +terms. All such code will be identified clearly using at least one of two +mechanisms: +1) It will be in a separate directory tree with its own `LICENSE.txt` or + `LICENSE` file at the top containing the specific license and restrictions + which apply to that software, or +2) It will contain specific license and restriction terms at the top of every + file. + +============================================================================== +Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy): +============================================================================== + +The libunwind library is dual licensed under both the University of Illinois +"BSD-Like" license and the MIT license. As a user of this code you may choose +to use it under either license. As a contributor, you agree to allow your code +to be used under both. + +Full text of the relevant licenses is included below. + +============================================================================== + +University of Illinois/NCSA +Open Source License + +Copyright (c) 2009-2019 by the contributors listed in CREDITS.TXT + +All rights reserved. + +Developed by: + + LLVM Team + + University of Illinois at Urbana-Champaign + + http://llvm.org + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +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: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLVM Team, University of Illinois at + Urbana-Champaign, nor the names of its contributors may be used to + endorse or promote products derived from this Software without specific + prior written permission. + +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 +CONTRIBUTORS 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 WITH THE +SOFTWARE. + +============================================================================== + +Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT + +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. diff --git a/shared/sentry/external/crashpad/libunwind/cmake/Modules/HandleCompilerRT.cmake b/shared/sentry/external/crashpad/libunwind/cmake/Modules/HandleCompilerRT.cmake new file mode 100644 index 000000000..77168e599 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/cmake/Modules/HandleCompilerRT.cmake @@ -0,0 +1,64 @@ +function(find_compiler_rt_library name dest) + if (NOT DEFINED LIBUNWIND_COMPILE_FLAGS) + message(FATAL_ERROR "LIBUNWIND_COMPILE_FLAGS must be defined when using this function") + endif() + set(dest "" PARENT_SCOPE) + set(CLANG_COMMAND ${CMAKE_CXX_COMPILER} ${LIBUNWIND_COMPILE_FLAGS} + "--rtlib=compiler-rt" "--print-libgcc-file-name") + if (CMAKE_CXX_COMPILER_ID MATCHES Clang AND CMAKE_CXX_COMPILER_TARGET) + list(APPEND CLANG_COMMAND "--target=${CMAKE_CXX_COMPILER_TARGET}") + endif() + get_property(LIBUNWIND_CXX_FLAGS CACHE CMAKE_CXX_FLAGS PROPERTY VALUE) + string(REPLACE " " ";" LIBUNWIND_CXX_FLAGS "${LIBUNWIND_CXX_FLAGS}") + list(APPEND CLANG_COMMAND ${LIBUNWIND_CXX_FLAGS}) + execute_process( + COMMAND ${CLANG_COMMAND} + RESULT_VARIABLE HAD_ERROR + OUTPUT_VARIABLE LIBRARY_FILE + ) + string(STRIP "${LIBRARY_FILE}" LIBRARY_FILE) + file(TO_CMAKE_PATH "${LIBRARY_FILE}" LIBRARY_FILE) + string(REPLACE "builtins" "${name}" LIBRARY_FILE "${LIBRARY_FILE}") + if (NOT HAD_ERROR AND EXISTS "${LIBRARY_FILE}") + message(STATUS "Found compiler-rt library: ${LIBRARY_FILE}") + set(${dest} "${LIBRARY_FILE}" PARENT_SCOPE) + else() + message(STATUS "Failed to find compiler-rt library") + endif() +endfunction() + +function(find_compiler_rt_dir dest) + if (NOT DEFINED LIBUNWIND_COMPILE_FLAGS) + message(FATAL_ERROR "LIBUNWIND_COMPILE_FLAGS must be defined when using this function") + endif() + set(dest "" PARENT_SCOPE) + if (APPLE) + set(CLANG_COMMAND ${CMAKE_CXX_COMPILER} ${LIBUNWIND_COMPILE_FLAGS} + "-print-file-name=lib") + execute_process( + COMMAND ${CLANG_COMMAND} + RESULT_VARIABLE HAD_ERROR + OUTPUT_VARIABLE LIBRARY_DIR + ) + string(STRIP "${LIBRARY_DIR}" LIBRARY_DIR) + file(TO_CMAKE_PATH "${LIBRARY_DIR}" LIBRARY_DIR) + set(LIBRARY_DIR "${LIBRARY_DIR}/darwin") + else() + set(CLANG_COMMAND ${CMAKE_CXX_COMPILER} ${LIBUNWIND_COMPILE_FLAGS} + "--rtlib=compiler-rt" "--print-libgcc-file-name") + execute_process( + COMMAND ${CLANG_COMMAND} + RESULT_VARIABLE HAD_ERROR + OUTPUT_VARIABLE LIBRARY_FILE + ) + string(STRIP "${LIBRARY_FILE}" LIBRARY_FILE) + file(TO_CMAKE_PATH "${LIBRARY_FILE}" LIBRARY_FILE) + get_filename_component(LIBRARY_DIR "${LIBRARY_FILE}" DIRECTORY) + endif() + if (NOT HAD_ERROR AND EXISTS "${LIBRARY_DIR}") + message(STATUS "Found compiler-rt directory: ${LIBRARY_DIR}") + set(${dest} "${LIBRARY_DIR}" PARENT_SCOPE) + else() + message(STATUS "Failed to find compiler-rt directory") + endif() +endfunction() diff --git a/shared/sentry/external/crashpad/libunwind/cmake/Modules/HandleLibunwindFlags.cmake b/shared/sentry/external/crashpad/libunwind/cmake/Modules/HandleLibunwindFlags.cmake new file mode 100644 index 000000000..675071f94 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/cmake/Modules/HandleLibunwindFlags.cmake @@ -0,0 +1,283 @@ +# HandleLibcxxFlags - A set of macros used to setup the flags used to compile +# and link libc++abi. These macros add flags to the following CMake variables. +# - LIBUNWIND_COMPILE_FLAGS: flags used to compile libunwind +# - LIBUNWIND_LINK_FLAGS: flags used to link libunwind +# - LIBUNWIND_LIBRARIES: libraries to link libunwind to. + +include(CheckCCompilerFlag) +include(CheckCXXCompilerFlag) + +unset(add_flag_if_supported) + +# Mangle the name of a compiler flag into a valid CMake identifier. +# Ex: --std=c++11 -> STD_EQ_CXX11 +macro(mangle_name str output) + string(STRIP "${str}" strippedStr) + string(REGEX REPLACE "^/" "" strippedStr "${strippedStr}") + string(REGEX REPLACE "^-+" "" strippedStr "${strippedStr}") + string(REGEX REPLACE "-+$" "" strippedStr "${strippedStr}") + string(REPLACE "-" "_" strippedStr "${strippedStr}") + string(REPLACE "=" "_EQ_" strippedStr "${strippedStr}") + string(REPLACE "+" "X" strippedStr "${strippedStr}") + string(TOUPPER "${strippedStr}" ${output}) +endmacro() + +# Remove a list of flags from all CMake variables that affect compile flags. +# This can be used to remove unwanted flags specified on the command line +# or added in other parts of LLVM's cmake configuration. +macro(remove_flags) + foreach(var ${ARGN}) + string(REPLACE "${var}" "" CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG}") + string(REPLACE "${var}" "" CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL}") + string(REPLACE "${var}" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") + string(REPLACE "${var}" "" CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO}") + string(REPLACE "${var}" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") + string(REPLACE "${var}" "" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") + string(REPLACE "${var}" "" CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}") + string(REPLACE "${var}" "" CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS}") + string(REPLACE "${var}" "" CMAKE_SHARED_MODULE_FLAGS "${CMAKE_SHARED_MODULE_FLAGS}") + remove_definitions(${var}) + endforeach() +endmacro(remove_flags) + +macro(check_flag_supported flag) + mangle_name("${flag}" flagname) + check_cxx_compiler_flag("${flag}" "LIBUNWIND_SUPPORTS_${flagname}_FLAG") +endmacro() + +macro(append_flags DEST) + foreach(value ${ARGN}) + list(APPEND ${DEST} ${value}) + list(APPEND ${DEST} ${value}) + endforeach() +endmacro() + +# If the specified 'condition' is true then append the specified list of flags to DEST +macro(append_flags_if condition DEST) + if (${condition}) + list(APPEND ${DEST} ${ARGN}) + endif() +endmacro() + +# Add each flag in the list specified by DEST if that flag is supported by the current compiler. +macro(append_flags_if_supported DEST) + foreach(flag ${ARGN}) + mangle_name("${flag}" flagname) + check_cxx_compiler_flag("${flag}" "LIBUNWIND_SUPPORTS_${flagname}_FLAG") + append_flags_if(LIBUNWIND_SUPPORTS_${flagname}_FLAG ${DEST} ${flag}) + endforeach() +endmacro() + +# Add a macro definition if condition is true. +macro(define_if condition def) + if (${condition}) + add_definitions(${def}) + endif() +endmacro() + +# Add a macro definition if condition is not true. +macro(define_if_not condition def) + if (NOT ${condition}) + add_definitions(${def}) + endif() +endmacro() + +# Add a macro definition to the __config_site file if the specified condition +# is 'true'. Note that '-D${def}' is not added. Instead it is expected that +# the build include the '__config_site' header. +macro(config_define_if condition def) + if (${condition}) + set(${def} ON) + set(LIBUNWIND_NEEDS_SITE_CONFIG ON) + endif() +endmacro() + +macro(config_define_if_not condition def) + if (NOT ${condition}) + set(${def} ON) + set(LIBUNWIND_NEEDS_SITE_CONFIG ON) + endif() +endmacro() + +macro(config_define value def) + set(${def} ${value}) + set(LIBUNWIND_NEEDS_SITE_CONFIG ON) +endmacro() + +# Add a list of flags to all of 'CMAKE_CXX_FLAGS', 'CMAKE_C_FLAGS', +# 'LIBUNWIND_COMPILE_FLAGS' and 'LIBUNWIND_LINK_FLAGS'. +macro(add_target_flags) + foreach(value ${ARGN}) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${value}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${value}") + list(APPEND LIBUNWIND_COMPILE_FLAGS ${value}) + list(APPEND LIBUNWIND_LINK_FLAGS ${value}) + endforeach() +endmacro() + +# If the specified 'condition' is true then add a list of flags to +# all of 'CMAKE_CXX_FLAGS', 'CMAKE_C_FLAGS', 'LIBUNWIND_COMPILE_FLAGS' +# and 'LIBUNWIND_LINK_FLAGS'. +macro(add_target_flags_if condition) + if (${condition}) + add_target_flags(${ARGN}) + endif() +endmacro() + +# Add all the flags supported by the compiler to all of +# 'CMAKE_CXX_FLAGS', 'CMAKE_C_FLAGS', 'LIBUNWIND_COMPILE_FLAGS' +# and 'LIBUNWIND_LINK_FLAGS'. +macro(add_target_flags_if_supported) + foreach(flag ${ARGN}) + mangle_name("${flag}" flagname) + check_cxx_compiler_flag("${flag}" "LIBUNWIND_SUPPORTS_${flagname}_FLAG") + add_target_flags_if(LIBUNWIND_SUPPORTS_${flagname}_FLAG ${flag}) + endforeach() +endmacro() + +# Add a specified list of flags to both 'LIBUNWIND_COMPILE_FLAGS' and +# 'LIBUNWIND_LINK_FLAGS'. +macro(add_flags) + foreach(value ${ARGN}) + list(APPEND LIBUNWIND_COMPILE_FLAGS ${value}) + list(APPEND LIBUNWIND_LINK_FLAGS ${value}) + endforeach() +endmacro() + +# If the specified 'condition' is true then add a list of flags to both +# 'LIBUNWIND_COMPILE_FLAGS' and 'LIBUNWIND_LINK_FLAGS'. +macro(add_flags_if condition) + if (${condition}) + add_flags(${ARGN}) + endif() +endmacro() + +# Add each flag in the list to LIBUNWIND_COMPILE_FLAGS and LIBUNWIND_LINK_FLAGS +# if that flag is supported by the current compiler. +macro(add_flags_if_supported) + foreach(flag ${ARGN}) + mangle_name("${flag}" flagname) + check_cxx_compiler_flag("${flag}" "LIBUNWIND_SUPPORTS_${flagname}_FLAG") + add_flags_if(LIBUNWIND_SUPPORTS_${flagname}_FLAG ${flag}) + endforeach() +endmacro() + +# Add a list of flags to 'LIBUNWIND_COMPILE_FLAGS'. +macro(add_compile_flags) + foreach(f ${ARGN}) + list(APPEND LIBUNWIND_COMPILE_FLAGS ${f}) + endforeach() +endmacro() + +# If 'condition' is true then add the specified list of flags to +# 'LIBUNWIND_COMPILE_FLAGS' +macro(add_compile_flags_if condition) + if (${condition}) + add_compile_flags(${ARGN}) + endif() +endmacro() + +# For each specified flag, add that flag to 'LIBUNWIND_COMPILE_FLAGS' if the +# flag is supported by the C++ compiler. +macro(add_compile_flags_if_supported) + foreach(flag ${ARGN}) + mangle_name("${flag}" flagname) + check_cxx_compiler_flag("${flag}" "LIBUNWIND_SUPPORTS_${flagname}_FLAG") + add_compile_flags_if(LIBUNWIND_SUPPORTS_${flagname}_FLAG ${flag}) + endforeach() +endmacro() + +# Add a list of flags to 'LIBUNWIND_C_FLAGS'. +macro(add_c_flags) + foreach(f ${ARGN}) + list(APPEND LIBUNWIND_C_FLAGS ${f}) + endforeach() +endmacro() + +# If 'condition' is true then add the specified list of flags to +# 'LIBUNWIND_C_FLAGS' +macro(add_c_flags_if condition) + if (${condition}) + add_c_flags(${ARGN}) + endif() +endmacro() + +# For each specified flag, add that flag to 'LIBUNWIND_C_FLAGS' if the +# flag is supported by the C compiler. +macro(add_c_compile_flags_if_supported) + foreach(flag ${ARGN}) + mangle_name("${flag}" flagname) + check_c_compiler_flag("${flag}" "LIBUNWIND_SUPPORTS_${flagname}_FLAG") + add_c_flags_if(LIBUNWIND_SUPPORTS_${flagname}_FLAG ${flag}) + endforeach() +endmacro() + +# Add a list of flags to 'LIBUNWIND_CXX_FLAGS'. +macro(add_cxx_flags) + foreach(f ${ARGN}) + list(APPEND LIBUNWIND_CXX_FLAGS ${f}) + endforeach() +endmacro() + +# If 'condition' is true then add the specified list of flags to +# 'LIBUNWIND_CXX_FLAGS' +macro(add_cxx_flags_if condition) + if (${condition}) + add_cxx_flags(${ARGN}) + endif() +endmacro() + +# For each specified flag, add that flag to 'LIBUNWIND_CXX_FLAGS' if the +# flag is supported by the C compiler. +macro(add_cxx_compile_flags_if_supported) + foreach(flag ${ARGN}) + mangle_name("${flag}" flagname) + check_cxx_compiler_flag("${flag}" "LIBUNWIND_SUPPORTS_${flagname}_FLAG") + add_cxx_flags_if(LIBUNWIND_SUPPORTS_${flagname}_FLAG ${flag}) + endforeach() +endmacro() + +# Add a list of flags to 'LIBUNWIND_LINK_FLAGS'. +macro(add_link_flags) + foreach(f ${ARGN}) + list(APPEND LIBUNWIND_LINK_FLAGS ${f}) + endforeach() +endmacro() + +# If 'condition' is true then add the specified list of flags to +# 'LIBUNWIND_LINK_FLAGS' +macro(add_link_flags_if condition) + if (${condition}) + add_link_flags(${ARGN}) + endif() +endmacro() + +# For each specified flag, add that flag to 'LIBUNWIND_LINK_FLAGS' if the +# flag is supported by the C++ compiler. +macro(add_link_flags_if_supported) + foreach(flag ${ARGN}) + mangle_name("${flag}" flagname) + check_cxx_compiler_flag("${flag}" "LIBUNWIND_SUPPORTS_${flagname}_FLAG") + add_link_flags_if(LIBUNWIND_SUPPORTS_${flagname}_FLAG ${flag}) + endforeach() +endmacro() + +# Add a list of libraries or link flags to 'LIBUNWIND_LIBRARIES'. +macro(add_library_flags) + foreach(lib ${ARGN}) + list(APPEND LIBUNWIND_LIBRARIES ${lib}) + endforeach() +endmacro() + +# if 'condition' is true then add the specified list of libraries and flags +# to 'LIBUNWIND_LIBRARIES'. +macro(add_library_flags_if condition) + if(${condition}) + add_library_flags(${ARGN}) + endif() +endmacro() + +# Turn a comma separated CMake list into a space separated string. +macro(split_list listname) + string(REPLACE ";" " " ${listname} "${${listname}}") +endmacro() diff --git a/shared/sentry/external/crashpad/libunwind/cmake/config-ix.cmake b/shared/sentry/external/crashpad/libunwind/cmake/config-ix.cmake new file mode 100644 index 000000000..4ca6bdd8e --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/cmake/config-ix.cmake @@ -0,0 +1,105 @@ +include(CMakePushCheckState) +include(CheckCCompilerFlag) +include(CheckCXXCompilerFlag) +include(CheckLibraryExists) +include(CheckSymbolExists) +include(CheckCSourceCompiles) + +check_library_exists(c fopen "" LIBUNWIND_HAS_C_LIB) + +if (NOT LIBUNWIND_USE_COMPILER_RT) + if (ANDROID) + check_library_exists(gcc __gcc_personality_v0 "" LIBUNWIND_HAS_GCC_LIB) + else () + check_library_exists(gcc_s __gcc_personality_v0 "" LIBUNWIND_HAS_GCC_S_LIB) + check_library_exists(gcc __absvdi2 "" LIBUNWIND_HAS_GCC_LIB) + endif () +endif() + +# libunwind is using -nostdlib++ at the link step when available, +# otherwise -nodefaultlibs is used. We want all our checks to also +# use one of these options, otherwise we may end up with an inconsistency between +# the flags we think we require during configuration (if the checks are +# performed without one of those options) and the flags that are actually +# required during compilation (which has the -nostdlib++ or -nodefaultlibs). libc is +# required for the link to go through. We remove sanitizers from the +# configuration checks to avoid spurious link errors. + +check_c_compiler_flag(-nostdlib++ LIBUNWIND_SUPPORTS_NOSTDLIBXX_FLAG) +if (LIBUNWIND_SUPPORTS_NOSTDLIBXX_FLAG) + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -nostdlib++") +else() + check_c_compiler_flag(-nodefaultlibs LIBUNWIND_SUPPORTS_NODEFAULTLIBS_FLAG) + if (LIBUNWIND_SUPPORTS_NODEFAULTLIBS_FLAG) + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -nodefaultlibs") + endif() +endif() + +if (LIBUNWIND_SUPPORTS_NOSTDLIBXX_FLAG OR LIBUNWIND_SUPPORTS_NODEFAULTLIBS_FLAG) + if (LIBUNWIND_HAS_C_LIB) + list(APPEND CMAKE_REQUIRED_LIBRARIES c) + endif () + if (LIBUNWIND_USE_COMPILER_RT) + find_compiler_rt_library(builtins LIBUNWIND_BUILTINS_LIBRARY) + list(APPEND CMAKE_REQUIRED_LIBRARIES "${LIBUNWIND_BUILTINS_LIBRARY}") + else () + if (LIBUNWIND_HAS_GCC_S_LIB) + list(APPEND CMAKE_REQUIRED_LIBRARIES gcc_s) + endif () + if (LIBUNWIND_HAS_GCC_LIB) + list(APPEND CMAKE_REQUIRED_LIBRARIES gcc) + endif () + endif () + if (MINGW) + # Mingw64 requires quite a few "C" runtime libraries in order for basic + # programs to link successfully with -nodefaultlibs. + if (LIBUNWIND_USE_COMPILER_RT) + set(MINGW_RUNTIME ${LIBUNWIND_BUILTINS_LIBRARY}) + else () + set(MINGW_RUNTIME gcc_s gcc) + endif() + set(MINGW_LIBRARIES mingw32 ${MINGW_RUNTIME} moldname mingwex msvcrt advapi32 + shell32 user32 kernel32 mingw32 ${MINGW_RUNTIME} + moldname mingwex msvcrt) + list(APPEND CMAKE_REQUIRED_LIBRARIES ${MINGW_LIBRARIES}) + endif() + if (CMAKE_C_FLAGS MATCHES -fsanitize OR CMAKE_CXX_FLAGS MATCHES -fsanitize) + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -fno-sanitize=all") + endif () + if (CMAKE_C_FLAGS MATCHES -fsanitize-coverage OR CMAKE_CXX_FLAGS MATCHES -fsanitize-coverage) + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -fno-sanitize-coverage=edge,trace-cmp,indirect-calls,8bit-counters") + endif () +endif () + +# Check compiler pragmas +if(CMAKE_CXX_COMPILER_ID MATCHES "Clang") + cmake_push_check_state() + set(CMAKE_REQUIRED_FLAGS "${CMAKE_REQUIRED_FLAGS} -Werror=unknown-pragmas") + check_c_source_compiles(" +#pragma comment(lib, \"c\") +int main() { return 0; } +" LIBUNWIND_HAS_COMMENT_LIB_PRAGMA) + cmake_pop_check_state() +endif() + +# Check compiler flags +check_cxx_compiler_flag(-nostdinc++ LIBUNWIND_HAS_NOSTDINCXX_FLAG) + +# Check symbols +check_symbol_exists(__arm__ "" LIBUNWIND_TARGET_ARM) +check_symbol_exists(__USING_SJLJ_EXCEPTIONS__ "" LIBUNWIND_USES_SJLJ_EXCEPTIONS) +check_symbol_exists(__ARM_DWARF_EH__ "" LIBUNWIND_USES_DWARF_EH) + +if(LIBUNWIND_TARGET_ARM AND NOT LIBUNWIND_USES_SJLJ_EXCEPTIONS AND NOT LIBUNWIND_USES_DWARF_EH) + # This condition is copied from __libunwind_config.h + set(LIBUNWIND_USES_ARM_EHABI ON) +endif() + +# Check libraries +if(FUCHSIA) + set(LIBUNWIND_HAS_DL_LIB NO) + set(LIBUNWIND_HAS_PTHREAD_LIB NO) +else() + check_library_exists(dl dladdr "" LIBUNWIND_HAS_DL_LIB) + check_library_exists(pthread pthread_once "" LIBUNWIND_HAS_PTHREAD_LIB) +endif() diff --git a/shared/sentry/external/crashpad/libunwind/docs/BuildingLibunwind.rst b/shared/sentry/external/crashpad/libunwind/docs/BuildingLibunwind.rst new file mode 100644 index 000000000..9b11042a6 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/docs/BuildingLibunwind.rst @@ -0,0 +1,168 @@ +.. _BuildingLibunwind: + +================== +Building libunwind +================== + +.. contents:: + :local: + +.. _build instructions: + +Getting Started +=============== + +On Mac OS, the easiest way to get this library is to link with -lSystem. +However if you want to build tip-of-trunk from here (getting the bleeding +edge), read on. + +The basic steps needed to build libc++ are: + +#. Checkout LLVM, libunwind, and related projects: + + * ``cd where-you-want-llvm-to-live`` + * ``git clone https://github.com/llvm/llvm-project.git`` + +#. Configure and build libunwind: + + CMake is the only supported configuration system. + + Clang is the preferred compiler when building and using libunwind. + + * ``cd where you want to build llvm`` + * ``mkdir build`` + * ``cd build`` + * ``cmake -G -DLLVM_ENABLE_PROJECTS=libunwind [options] `` + + For more information about configuring libunwind see :ref:`CMake Options`. + + * ``make unwind`` --- will build libunwind. + * ``make check-unwind`` --- will run the test suite. + + Shared and static libraries for libunwind should now be present in llvm/build/lib. + +#. **Optional**: Install libunwind + + If your system already provides an unwinder, it is important to be careful + not to replace it. Remember Use the CMake option ``CMAKE_INSTALL_PREFIX`` to + select a safe place to install libunwind. + + * ``make install-unwind`` --- Will install the libraries and the headers + + +It is sometimes beneficial to build outside of the LLVM tree. An out-of-tree +build would look like this: + +.. code-block:: bash + + $ cd where-you-want-libunwind-to-live + $ # Check out llvm, and libunwind + $ ``svn co https://llvm.org/svn/llvm-project/llvm/trunk llvm`` + $ ``svn co https://llvm.org/svn/llvm-project/libunwind/trunk libunwind`` + $ cd where-you-want-to-build + $ mkdir build && cd build + $ export CC=clang CXX=clang++ + $ cmake -DLLVM_PATH=path/to/llvm \ + path/to/libunwind + $ make + + +.. _CMake Options: + +CMake Options +============= + +Here are some of the CMake variables that are used often, along with a +brief explanation and LLVM-specific notes. For full documentation, check the +CMake docs or execute ``cmake --help-variable VARIABLE_NAME``. + +**CMAKE_BUILD_TYPE**:STRING + Sets the build type for ``make`` based generators. Possible values are + Release, Debug, RelWithDebInfo and MinSizeRel. On systems like Visual Studio + the user sets the build type with the IDE settings. + +**CMAKE_INSTALL_PREFIX**:PATH + Path where LLVM will be installed if "make install" is invoked or the + "INSTALL" target is built. + +**CMAKE_CXX_COMPILER**:STRING + The C++ compiler to use when building and testing libunwind. + + +.. _libunwind-specific options: + +libunwind specific options +-------------------------- + +.. option:: LIBUNWIND_BUILD_32_BITS:BOOL + + **Default**: Same as LLVM_BUILD_32_BITS + + Toggle whether libunwind should be built with -m32. + +.. option:: LIBUNWIND_ENABLE_ASSERTIONS:BOOL + + **Default**: ``ON`` + + Toggle assertions independent of the build mode. + +.. option:: LIBUNWIND_ENABLE_PEDANTIC:BOOL + + **Default**: ``ON`` + + Compile with -Wpedantic. + +.. option:: LIBUNWIND_ENABLE_WERROR:BOOL + + **Default**: ``ON`` + + Compile with -Werror + +.. option:: LIBUNWIND_ENABLE_SHARED:BOOL + + **Default**: ``ON`` + + Build libunwind as a shared library. + +.. option:: LIBUNWIND_ENABLE_STATIC:BOOL + + **Default**: ``ON`` + + Build libunwind as a static archive. + +.. option:: LIBUNWIND_ENABLE_CROSS_UNWINDING:BOOL + + **Default**: ``OFF`` + + Enable cross-platform unwinding support. + +.. option:: LIBUNWIND_ENABLE_ARM_WMMX:BOOL + + **Default**: ``OFF`` + + Enable unwinding support for ARM WMMX registers. + +.. option:: LIBUNWIND_ENABLE_THREADS:BOOL + + **Default**: ``ON`` + + Build libunwind with threading support. + +.. option:: LIBUNWIND_TARGET_TRIPLE:STRING + + Target triple for cross compiling + +.. option:: LIBUNWIND_GCC_TOOLCHAIN:PATH + + GCC toolchain for cross compiling + +.. option:: LIBUNWIND_SYSROOT + + Sysroot for cross compiling + +.. option:: LIBUNWIND_INSTALL_LIBRARY_DIR:PATH + + **Default**: ``lib${LIBUNWIND_LIBDIR_SUFFIX}`` + + Path where built libunwind libraries should be installed. If a relative path, + relative to ``CMAKE_INSTALL_PREFIX``. diff --git a/shared/sentry/external/crashpad/libunwind/docs/CMakeLists.txt b/shared/sentry/external/crashpad/libunwind/docs/CMakeLists.txt new file mode 100644 index 000000000..79b87eb03 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/docs/CMakeLists.txt @@ -0,0 +1,7 @@ +include(FindSphinx) +if (SPHINX_FOUND AND LLVM_ENABLE_SPHINX) + include(AddSphinxTarget) + if (${SPHINX_OUTPUT_HTML}) + add_sphinx_target(html libunwind) + endif() +endif() diff --git a/shared/sentry/external/crashpad/libunwind/docs/README.txt b/shared/sentry/external/crashpad/libunwind/docs/README.txt new file mode 100644 index 000000000..968982fce --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/docs/README.txt @@ -0,0 +1,13 @@ +libunwind Documentation +==================== + +The libunwind documentation is written using the Sphinx documentation generator. It is +currently tested with Sphinx 1.1.3. + +To build the documents into html configure libunwind with the following cmake options: + + * -DLLVM_ENABLE_SPHINX=ON + * -DLIBUNWIND_INCLUDE_DOCS=ON + +After configuring libunwind with these options the make rule `docs-libunwind-html` +should be available. diff --git a/shared/sentry/external/crashpad/libunwind/docs/conf.py b/shared/sentry/external/crashpad/libunwind/docs/conf.py new file mode 100644 index 000000000..bc91d90fe --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/docs/conf.py @@ -0,0 +1,252 @@ +# -*- coding: utf-8 -*- +# +# libunwind documentation build configuration file. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os +from datetime import date + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +#sys.path.insert(0, os.path.abspath('.')) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.intersphinx', 'sphinx.ext.todo'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'libunwind' +copyright = u'2011-%d, LLVM Project' % date.today().year + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '14.0' +# The full version, including alpha/beta/rc tags. +release = '14.0' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +today_fmt = '%Y-%m-%d' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = ['_build'] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +show_authors = True + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'friendly' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'haiku' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +#html_logo = None + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = [] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +#html_additional_pages = {} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +#html_show_sphinx = True + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'libunwinddoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +latex_elements = { +# The paper size ('letterpaper' or 'a4paper'). +#'papersize': 'letterpaper', + +# The font size ('10pt', '11pt' or '12pt'). +#'pointsize': '10pt', + +# Additional stuff for the LaTeX preamble. +#'preamble': '', +} + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('contents', 'libunwind.tex', u'libunwind Documentation', + u'LLVM project', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('contents', 'libunwind', u'libunwind Documentation', + [u'LLVM project'], 1) +] + +# If true, show URL addresses after external links. +#man_show_urls = False + + +# -- Options for Texinfo output ------------------------------------------------ + +# Grouping the document tree into Texinfo files. List of tuples +# (source start file, target name, title, author, +# dir menu entry, description, category) +texinfo_documents = [ + ('contents', 'libunwind', u'libunwind Documentation', + u'LLVM project', 'libunwind', 'LLVM Unwinder', + 'Miscellaneous'), +] + +# Documents to append as an appendix to all manuals. +#texinfo_appendices = [] + +# If false, no module index is generated. +#texinfo_domain_indices = True + +# How to display URL addresses: 'footnote', 'no', or 'inline'. +#texinfo_show_urls = 'footnote' + + +# FIXME: Define intersphinx configration. +intersphinx_mapping = {} + + +# -- Options for extensions ---------------------------------------------------- + +# Enable this if you want TODOs to show up in the generated documentation. +todo_include_todos = True diff --git a/shared/sentry/external/crashpad/libunwind/docs/index.rst b/shared/sentry/external/crashpad/libunwind/docs/index.rst new file mode 100644 index 000000000..f7ff29d09 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/docs/index.rst @@ -0,0 +1,104 @@ +.. _index: + +======================= +libunwind LLVM Unwinder +======================= + +Overview +======== + +libunwind is an implementation of the interface defined by the HP libunwind +project. It was contributed by Apple as a way to enable clang++ to port to +platforms that do not have a system unwinder. It is intended to be a small and +fast implementation of the ABI, leaving off some features of HP's libunwind +that never materialized (e.g. remote unwinding). + +The unwinder has two levels of API. The high level APIs are the `_Unwind_*` +functions which implement functionality required by `__cxa_*` exception +functions. The low level APIs are the `unw_*` functions which are an interface +defined by the old HP libunwind project. + +Getting Started with libunwind +------------------------------ + +.. toctree:: + :maxdepth: 2 + + BuildingLibunwind + +Current Status +-------------- + +libunwind is a production-quality unwinder, with platform support for DWARF +unwind info, SjLj, and ARM EHABI. + +The low level libunwind API was designed to work either in-process (aka local) +or to operate on another process (aka remote), but only the local path has been +implemented. Remote unwinding remains as future work. + +Platform and Compiler Support +----------------------------- + +libunwind is known to work on the following platforms: + +============ ======================== ============ ======================== +OS Arch Compilers Unwind Info +============ ======================== ============ ======================== +Any i386, x86_64, ARM Clang SjLj +Bare Metal ARM Clang, GCC EHABI +FreeBSD i386, x86_64, ARM64 Clang DWARF CFI +iOS ARM Clang SjLj +Linux ARM Clang, GCC EHABI +Linux i386, x86_64, ARM64 Clang, GCC DWARF CFI +macOS i386, x86_64 Clang, GCC DWARF CFI +NetBSD x86_64 Clang, GCC DWARF CFI +Windows i386, x86_64, ARM, ARM64 Clang DWARF CFI +============ ======================== ============ ======================== + +The following minimum compiler versions are strongly recommended. + +* Clang 3.5 and above +* GCC 4.7 and above. + +Anything older *may* work. + +Notes and Known Issues +---------------------- + +* TODO + + +Getting Involved +================ + +First please review our `Developer's Policy `__ +and `Getting started with LLVM `__. + +**Bug Reports** + +If you think you've found a bug in libunwind, please report it using +the `LLVM Bugzilla`_. If you're not sure, you +can post a message to the `cfe-dev mailing list`_ or on IRC. +Please include "libunwind" in your subject. + +**Patches** + +If you want to contribute a patch to libunwind, the best place for that is +`Phabricator `_. Please include [libunwind] in the subject and +add `cfe-commits` as a subscriber. Also make sure you are subscribed to the +`cfe-commits mailing list `_. + +**Discussion and Questions** + +Send discussions and questions to the +`cfe-dev mailing list `_. +Please include [libunwind] in the subject. + + +Quick Links +=========== +* `LLVM Homepage `_ +* `LLVM Bugzilla `_ +* `cfe-commits Mailing List`_ +* `cfe-dev Mailing List`_ +* `Browse libunwind Sources `_ diff --git a/shared/sentry/external/crashpad/libunwind/include/__libunwind_config.h b/shared/sentry/external/crashpad/libunwind/include/__libunwind_config.h new file mode 100644 index 000000000..a50ba0538 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/include/__libunwind_config.h @@ -0,0 +1,176 @@ +//===------------------------- __libunwind_config.h -----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef ____LIBUNWIND_CONFIG_H__ +#define ____LIBUNWIND_CONFIG_H__ + +#if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) && \ + !defined(__ARM_DWARF_EH__) +#define _LIBUNWIND_ARM_EHABI +#endif + +#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_X86 8 +#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_X86_64 32 +#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC 112 +#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC64 116 +#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM64 95 +#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM 287 +#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_OR1K 32 +#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_MIPS 65 +#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_SPARC 31 +#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_HEXAGON 34 +#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_RISCV 64 +#define _LIBUNWIND_HIGHEST_DWARF_REGISTER_VE 143 + +#if defined(_LIBUNWIND_IS_NATIVE_ONLY) +# if defined(__linux__) +# define _LIBUNWIND_TARGET_LINUX 1 +# endif +# if defined(__i386__) +# define _LIBUNWIND_TARGET_I386 +# define _LIBUNWIND_CONTEXT_SIZE 8 +# define _LIBUNWIND_CURSOR_SIZE 15 +# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_X86 +# elif defined(__x86_64__) +# define _LIBUNWIND_TARGET_X86_64 1 +# if defined(_WIN64) +# define _LIBUNWIND_CONTEXT_SIZE 54 +# ifdef __SEH__ +# define _LIBUNWIND_CURSOR_SIZE 204 +# else +# define _LIBUNWIND_CURSOR_SIZE 66 +# endif +# else +# define _LIBUNWIND_CONTEXT_SIZE 21 +# define _LIBUNWIND_CURSOR_SIZE 33 +# endif +# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_X86_64 +# elif defined(__powerpc64__) +# define _LIBUNWIND_TARGET_PPC64 1 +# define _LIBUNWIND_CONTEXT_SIZE 167 +# define _LIBUNWIND_CURSOR_SIZE 179 +# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC64 +# elif defined(__ppc__) +# define _LIBUNWIND_TARGET_PPC 1 +# define _LIBUNWIND_CONTEXT_SIZE 117 +# define _LIBUNWIND_CURSOR_SIZE 124 +# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC +# elif defined(__aarch64__) +# define _LIBUNWIND_TARGET_AARCH64 1 +# define _LIBUNWIND_CONTEXT_SIZE 66 +# if defined(__SEH__) +# define _LIBUNWIND_CURSOR_SIZE 164 +# else +# define _LIBUNWIND_CURSOR_SIZE 78 +# endif +# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM64 +# elif defined(__arm__) +# define _LIBUNWIND_TARGET_ARM 1 +# if defined(__SEH__) +# define _LIBUNWIND_CONTEXT_SIZE 42 +# define _LIBUNWIND_CURSOR_SIZE 80 +# elif defined(__ARM_WMMX) +# define _LIBUNWIND_CONTEXT_SIZE 61 +# define _LIBUNWIND_CURSOR_SIZE 68 +# else +# define _LIBUNWIND_CONTEXT_SIZE 42 +# define _LIBUNWIND_CURSOR_SIZE 49 +# endif +# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM +# elif defined(__or1k__) +# define _LIBUNWIND_TARGET_OR1K 1 +# define _LIBUNWIND_CONTEXT_SIZE 16 +# define _LIBUNWIND_CURSOR_SIZE 24 +# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_OR1K +# elif defined(__hexagon__) +# define _LIBUNWIND_TARGET_HEXAGON 1 +// Values here change when : Registers.hpp - hexagon_thread_state_t change +# define _LIBUNWIND_CONTEXT_SIZE 18 +# define _LIBUNWIND_CURSOR_SIZE 24 +# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_HEXAGON +# elif defined(__mips__) +# if defined(_ABIO32) && _MIPS_SIM == _ABIO32 +# define _LIBUNWIND_TARGET_MIPS_O32 1 +# if defined(__mips_hard_float) +# define _LIBUNWIND_CONTEXT_SIZE 50 +# define _LIBUNWIND_CURSOR_SIZE 57 +# else +# define _LIBUNWIND_CONTEXT_SIZE 18 +# define _LIBUNWIND_CURSOR_SIZE 24 +# endif +# elif defined(_ABIN32) && _MIPS_SIM == _ABIN32 +# define _LIBUNWIND_TARGET_MIPS_NEWABI 1 +# if defined(__mips_hard_float) +# define _LIBUNWIND_CONTEXT_SIZE 67 +# define _LIBUNWIND_CURSOR_SIZE 74 +# else +# define _LIBUNWIND_CONTEXT_SIZE 35 +# define _LIBUNWIND_CURSOR_SIZE 42 +# endif +# elif defined(_ABI64) && _MIPS_SIM == _ABI64 +# define _LIBUNWIND_TARGET_MIPS_NEWABI 1 +# if defined(__mips_hard_float) +# define _LIBUNWIND_CONTEXT_SIZE 67 +# define _LIBUNWIND_CURSOR_SIZE 79 +# else +# define _LIBUNWIND_CONTEXT_SIZE 35 +# define _LIBUNWIND_CURSOR_SIZE 47 +# endif +# else +# error "Unsupported MIPS ABI and/or environment" +# endif +# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_MIPS +# elif defined(__sparc__) + #define _LIBUNWIND_TARGET_SPARC 1 + #define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_SPARC + #define _LIBUNWIND_CONTEXT_SIZE 16 + #define _LIBUNWIND_CURSOR_SIZE 23 +# elif defined(__riscv) +# define _LIBUNWIND_TARGET_RISCV 1 +# if defined(__riscv_flen) +# define RISCV_FLEN __riscv_flen +# else +# define RISCV_FLEN 0 +# endif +# define _LIBUNWIND_CONTEXT_SIZE (32 * (__riscv_xlen + RISCV_FLEN) / 64) +# if __riscv_xlen == 32 +# define _LIBUNWIND_CURSOR_SIZE (_LIBUNWIND_CONTEXT_SIZE + 7) +# elif __riscv_xlen == 64 +# define _LIBUNWIND_CURSOR_SIZE (_LIBUNWIND_CONTEXT_SIZE + 12) +# else +# error "Unsupported RISC-V ABI" +# endif +# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_RISCV +# elif defined(__ve__) +# define _LIBUNWIND_TARGET_VE 1 +# define _LIBUNWIND_CONTEXT_SIZE 67 +# define _LIBUNWIND_CURSOR_SIZE 79 +# define _LIBUNWIND_HIGHEST_DWARF_REGISTER _LIBUNWIND_HIGHEST_DWARF_REGISTER_VE +# else +# error "Unsupported architecture." +# endif +#else // !_LIBUNWIND_IS_NATIVE_ONLY +# define _LIBUNWIND_TARGET_I386 +# define _LIBUNWIND_TARGET_X86_64 1 +# define _LIBUNWIND_TARGET_PPC 1 +# define _LIBUNWIND_TARGET_PPC64 1 +# define _LIBUNWIND_TARGET_AARCH64 1 +# define _LIBUNWIND_TARGET_ARM 1 +# define _LIBUNWIND_TARGET_OR1K 1 +# define _LIBUNWIND_TARGET_MIPS_O32 1 +# define _LIBUNWIND_TARGET_MIPS_NEWABI 1 +# define _LIBUNWIND_TARGET_SPARC 1 +# define _LIBUNWIND_TARGET_HEXAGON 1 +# define _LIBUNWIND_TARGET_RISCV 1 +# define _LIBUNWIND_TARGET_VE 1 +# define _LIBUNWIND_CONTEXT_SIZE 167 +# define _LIBUNWIND_CURSOR_SIZE 179 +# define _LIBUNWIND_HIGHEST_DWARF_REGISTER 287 +#endif // _LIBUNWIND_IS_NATIVE_ONLY + +#endif // ____LIBUNWIND_CONFIG_H__ diff --git a/shared/sentry/external/crashpad/libunwind/include/libunwind.h b/shared/sentry/external/crashpad/libunwind/include/libunwind.h new file mode 100644 index 000000000..019fa9a76 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/include/libunwind.h @@ -0,0 +1,1186 @@ +//===---------------------------- libunwind.h -----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Compatible with libunwind API documented at: +// http://www.nongnu.org/libunwind/man/libunwind(3).html +// +//===----------------------------------------------------------------------===// + +#ifndef __LIBUNWIND__ +#define __LIBUNWIND__ + +#include <__libunwind_config.h> + +#include +#include + +#ifdef __APPLE__ + + #include + + #if __clang__ + #if __has_include() + #include + #endif + #elif __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 1050 + #include + #endif + + #ifdef __arm__ + #define LIBUNWIND_AVAIL __attribute__((unavailable)) + #elif defined(__OSX_AVAILABLE_STARTING) + #define LIBUNWIND_AVAIL __OSX_AVAILABLE_STARTING(__MAC_10_6, __IPHONE_5_0) + #else + #include + #ifdef AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER + #define LIBUNWIND_AVAIL AVAILABLE_MAC_OS_X_VERSION_10_6_AND_LATER + #else + #define LIBUNWIND_AVAIL __attribute__((unavailable)) + #endif + #endif +#else + #define LIBUNWIND_AVAIL +#endif + +#if defined(_WIN32) && defined(__SEH__) + #define LIBUNWIND_CURSOR_ALIGNMENT_ATTR __attribute__((__aligned__(16))) +#else + #define LIBUNWIND_CURSOR_ALIGNMENT_ATTR +#endif + +/* error codes */ +enum { + UNW_ESUCCESS = 0, /* no error */ + UNW_EUNSPEC = -6540, /* unspecified (general) error */ + UNW_ENOMEM = -6541, /* out of memory */ + UNW_EBADREG = -6542, /* bad register number */ + UNW_EREADONLYREG = -6543, /* attempt to write read-only register */ + UNW_ESTOPUNWIND = -6544, /* stop unwinding */ + UNW_EINVALIDIP = -6545, /* invalid IP */ + UNW_EBADFRAME = -6546, /* bad frame */ + UNW_EINVAL = -6547, /* unsupported operation or bad value */ + UNW_EBADVERSION = -6548, /* unwind info has unsupported version */ + UNW_ENOINFO = -6549 /* no unwind info found */ +#if defined(_LIBUNWIND_TARGET_AARCH64) && !defined(_LIBUNWIND_IS_NATIVE_ONLY) + , UNW_ECROSSRASIGNING = -6550 /* cross unwind with return address signing */ +#endif +}; + +struct unw_context_t { + uint64_t data[_LIBUNWIND_CONTEXT_SIZE]; +}; +typedef struct unw_context_t unw_context_t; + +struct unw_cursor_t { + uint64_t data[_LIBUNWIND_CURSOR_SIZE]; +} LIBUNWIND_CURSOR_ALIGNMENT_ATTR; +typedef struct unw_cursor_t unw_cursor_t; + +typedef struct unw_addr_space *unw_addr_space_t; + +typedef int unw_regnum_t; +typedef uintptr_t unw_word_t; +#if defined(__arm__) && !defined(__ARM_DWARF_EH__) +typedef uint64_t unw_fpreg_t; +#else +typedef double unw_fpreg_t; +#endif + +struct unw_proc_info_t { + unw_word_t start_ip; /* start address of function */ + unw_word_t end_ip; /* address after end of function */ + unw_word_t lsda; /* address of language specific data area, */ + /* or zero if not used */ + unw_word_t handler; /* personality routine, or zero if not used */ + unw_word_t gp; /* not used */ + unw_word_t flags; /* not used */ + uint32_t format; /* compact unwind encoding, or zero if none */ + uint32_t unwind_info_size; /* size of DWARF unwind info, or zero if none */ + unw_word_t unwind_info; /* address of DWARF unwind info, or zero */ + unw_word_t extra; /* mach_header of mach-o image containing func */ +}; +typedef struct unw_proc_info_t unw_proc_info_t; + +#ifdef __cplusplus +extern "C" { +#endif + +extern int unw_getcontext(unw_context_t *) LIBUNWIND_AVAIL; +extern int unw_init_local(unw_cursor_t *, unw_context_t *) LIBUNWIND_AVAIL; +extern int unw_step(unw_cursor_t *) LIBUNWIND_AVAIL; +extern int unw_get_reg(unw_cursor_t *, unw_regnum_t, unw_word_t *) LIBUNWIND_AVAIL; +extern int unw_get_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t *) LIBUNWIND_AVAIL; +extern int unw_set_reg(unw_cursor_t *, unw_regnum_t, unw_word_t) LIBUNWIND_AVAIL; +extern int unw_set_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t) LIBUNWIND_AVAIL; +extern int unw_resume(unw_cursor_t *) LIBUNWIND_AVAIL; + +#ifdef __arm__ +/* Save VFP registers in FSTMX format (instead of FSTMD). */ +extern void unw_save_vfp_as_X(unw_cursor_t *) LIBUNWIND_AVAIL; +#endif + + +extern const char *unw_regname(unw_cursor_t *, unw_regnum_t) LIBUNWIND_AVAIL; +extern int unw_get_proc_info(unw_cursor_t *, unw_proc_info_t *) LIBUNWIND_AVAIL; +extern int unw_is_fpreg(unw_cursor_t *, unw_regnum_t) LIBUNWIND_AVAIL; +extern int unw_is_signal_frame(unw_cursor_t *) LIBUNWIND_AVAIL; +extern int unw_get_proc_name(unw_cursor_t *, char *, size_t, unw_word_t *) LIBUNWIND_AVAIL; +//extern int unw_get_save_loc(unw_cursor_t*, int, unw_save_loc_t*); + +extern unw_addr_space_t unw_local_addr_space; + +/* + * Mac OS X "remote" API for unwinding other processes on same machine + */ +extern unw_addr_space_t unw_create_addr_space_for_task(task_t); +extern void unw_destroy_addr_space(unw_addr_space_t); +extern int unw_init_remote_thread(unw_cursor_t *, unw_addr_space_t, thread_t); + +#ifdef __cplusplus +} +#endif + +// architecture independent register numbers +enum { + UNW_REG_IP = -1, // instruction pointer + UNW_REG_SP = -2, // stack pointer +}; + +// 32-bit x86 registers +enum { + UNW_X86_EAX = 0, + UNW_X86_ECX = 1, + UNW_X86_EDX = 2, + UNW_X86_EBX = 3, + UNW_X86_EBP = 4, + UNW_X86_ESP = 5, + UNW_X86_ESI = 6, + UNW_X86_EDI = 7 +}; + +// 64-bit x86_64 registers +enum { + UNW_X86_64_RAX = 0, + UNW_X86_64_RDX = 1, + UNW_X86_64_RCX = 2, + UNW_X86_64_RBX = 3, + UNW_X86_64_RSI = 4, + UNW_X86_64_RDI = 5, + UNW_X86_64_RBP = 6, + UNW_X86_64_RSP = 7, + UNW_X86_64_R8 = 8, + UNW_X86_64_R9 = 9, + UNW_X86_64_R10 = 10, + UNW_X86_64_R11 = 11, + UNW_X86_64_R12 = 12, + UNW_X86_64_R13 = 13, + UNW_X86_64_R14 = 14, + UNW_X86_64_R15 = 15, + UNW_X86_64_RIP = 16, + UNW_X86_64_XMM0 = 17, + UNW_X86_64_XMM1 = 18, + UNW_X86_64_XMM2 = 19, + UNW_X86_64_XMM3 = 20, + UNW_X86_64_XMM4 = 21, + UNW_X86_64_XMM5 = 22, + UNW_X86_64_XMM6 = 23, + UNW_X86_64_XMM7 = 24, + UNW_X86_64_XMM8 = 25, + UNW_X86_64_XMM9 = 26, + UNW_X86_64_XMM10 = 27, + UNW_X86_64_XMM11 = 28, + UNW_X86_64_XMM12 = 29, + UNW_X86_64_XMM13 = 30, + UNW_X86_64_XMM14 = 31, + UNW_X86_64_XMM15 = 32, +}; + + +// 32-bit ppc register numbers +enum { + UNW_PPC_R0 = 0, + UNW_PPC_R1 = 1, + UNW_PPC_R2 = 2, + UNW_PPC_R3 = 3, + UNW_PPC_R4 = 4, + UNW_PPC_R5 = 5, + UNW_PPC_R6 = 6, + UNW_PPC_R7 = 7, + UNW_PPC_R8 = 8, + UNW_PPC_R9 = 9, + UNW_PPC_R10 = 10, + UNW_PPC_R11 = 11, + UNW_PPC_R12 = 12, + UNW_PPC_R13 = 13, + UNW_PPC_R14 = 14, + UNW_PPC_R15 = 15, + UNW_PPC_R16 = 16, + UNW_PPC_R17 = 17, + UNW_PPC_R18 = 18, + UNW_PPC_R19 = 19, + UNW_PPC_R20 = 20, + UNW_PPC_R21 = 21, + UNW_PPC_R22 = 22, + UNW_PPC_R23 = 23, + UNW_PPC_R24 = 24, + UNW_PPC_R25 = 25, + UNW_PPC_R26 = 26, + UNW_PPC_R27 = 27, + UNW_PPC_R28 = 28, + UNW_PPC_R29 = 29, + UNW_PPC_R30 = 30, + UNW_PPC_R31 = 31, + UNW_PPC_F0 = 32, + UNW_PPC_F1 = 33, + UNW_PPC_F2 = 34, + UNW_PPC_F3 = 35, + UNW_PPC_F4 = 36, + UNW_PPC_F5 = 37, + UNW_PPC_F6 = 38, + UNW_PPC_F7 = 39, + UNW_PPC_F8 = 40, + UNW_PPC_F9 = 41, + UNW_PPC_F10 = 42, + UNW_PPC_F11 = 43, + UNW_PPC_F12 = 44, + UNW_PPC_F13 = 45, + UNW_PPC_F14 = 46, + UNW_PPC_F15 = 47, + UNW_PPC_F16 = 48, + UNW_PPC_F17 = 49, + UNW_PPC_F18 = 50, + UNW_PPC_F19 = 51, + UNW_PPC_F20 = 52, + UNW_PPC_F21 = 53, + UNW_PPC_F22 = 54, + UNW_PPC_F23 = 55, + UNW_PPC_F24 = 56, + UNW_PPC_F25 = 57, + UNW_PPC_F26 = 58, + UNW_PPC_F27 = 59, + UNW_PPC_F28 = 60, + UNW_PPC_F29 = 61, + UNW_PPC_F30 = 62, + UNW_PPC_F31 = 63, + UNW_PPC_MQ = 64, + UNW_PPC_LR = 65, + UNW_PPC_CTR = 66, + UNW_PPC_AP = 67, + UNW_PPC_CR0 = 68, + UNW_PPC_CR1 = 69, + UNW_PPC_CR2 = 70, + UNW_PPC_CR3 = 71, + UNW_PPC_CR4 = 72, + UNW_PPC_CR5 = 73, + UNW_PPC_CR6 = 74, + UNW_PPC_CR7 = 75, + UNW_PPC_XER = 76, + UNW_PPC_V0 = 77, + UNW_PPC_V1 = 78, + UNW_PPC_V2 = 79, + UNW_PPC_V3 = 80, + UNW_PPC_V4 = 81, + UNW_PPC_V5 = 82, + UNW_PPC_V6 = 83, + UNW_PPC_V7 = 84, + UNW_PPC_V8 = 85, + UNW_PPC_V9 = 86, + UNW_PPC_V10 = 87, + UNW_PPC_V11 = 88, + UNW_PPC_V12 = 89, + UNW_PPC_V13 = 90, + UNW_PPC_V14 = 91, + UNW_PPC_V15 = 92, + UNW_PPC_V16 = 93, + UNW_PPC_V17 = 94, + UNW_PPC_V18 = 95, + UNW_PPC_V19 = 96, + UNW_PPC_V20 = 97, + UNW_PPC_V21 = 98, + UNW_PPC_V22 = 99, + UNW_PPC_V23 = 100, + UNW_PPC_V24 = 101, + UNW_PPC_V25 = 102, + UNW_PPC_V26 = 103, + UNW_PPC_V27 = 104, + UNW_PPC_V28 = 105, + UNW_PPC_V29 = 106, + UNW_PPC_V30 = 107, + UNW_PPC_V31 = 108, + UNW_PPC_VRSAVE = 109, + UNW_PPC_VSCR = 110, + UNW_PPC_SPE_ACC = 111, + UNW_PPC_SPEFSCR = 112 +}; + +// 64-bit ppc register numbers +enum { + UNW_PPC64_R0 = 0, + UNW_PPC64_R1 = 1, + UNW_PPC64_R2 = 2, + UNW_PPC64_R3 = 3, + UNW_PPC64_R4 = 4, + UNW_PPC64_R5 = 5, + UNW_PPC64_R6 = 6, + UNW_PPC64_R7 = 7, + UNW_PPC64_R8 = 8, + UNW_PPC64_R9 = 9, + UNW_PPC64_R10 = 10, + UNW_PPC64_R11 = 11, + UNW_PPC64_R12 = 12, + UNW_PPC64_R13 = 13, + UNW_PPC64_R14 = 14, + UNW_PPC64_R15 = 15, + UNW_PPC64_R16 = 16, + UNW_PPC64_R17 = 17, + UNW_PPC64_R18 = 18, + UNW_PPC64_R19 = 19, + UNW_PPC64_R20 = 20, + UNW_PPC64_R21 = 21, + UNW_PPC64_R22 = 22, + UNW_PPC64_R23 = 23, + UNW_PPC64_R24 = 24, + UNW_PPC64_R25 = 25, + UNW_PPC64_R26 = 26, + UNW_PPC64_R27 = 27, + UNW_PPC64_R28 = 28, + UNW_PPC64_R29 = 29, + UNW_PPC64_R30 = 30, + UNW_PPC64_R31 = 31, + UNW_PPC64_F0 = 32, + UNW_PPC64_F1 = 33, + UNW_PPC64_F2 = 34, + UNW_PPC64_F3 = 35, + UNW_PPC64_F4 = 36, + UNW_PPC64_F5 = 37, + UNW_PPC64_F6 = 38, + UNW_PPC64_F7 = 39, + UNW_PPC64_F8 = 40, + UNW_PPC64_F9 = 41, + UNW_PPC64_F10 = 42, + UNW_PPC64_F11 = 43, + UNW_PPC64_F12 = 44, + UNW_PPC64_F13 = 45, + UNW_PPC64_F14 = 46, + UNW_PPC64_F15 = 47, + UNW_PPC64_F16 = 48, + UNW_PPC64_F17 = 49, + UNW_PPC64_F18 = 50, + UNW_PPC64_F19 = 51, + UNW_PPC64_F20 = 52, + UNW_PPC64_F21 = 53, + UNW_PPC64_F22 = 54, + UNW_PPC64_F23 = 55, + UNW_PPC64_F24 = 56, + UNW_PPC64_F25 = 57, + UNW_PPC64_F26 = 58, + UNW_PPC64_F27 = 59, + UNW_PPC64_F28 = 60, + UNW_PPC64_F29 = 61, + UNW_PPC64_F30 = 62, + UNW_PPC64_F31 = 63, + // 64: reserved + UNW_PPC64_LR = 65, + UNW_PPC64_CTR = 66, + // 67: reserved + UNW_PPC64_CR0 = 68, + UNW_PPC64_CR1 = 69, + UNW_PPC64_CR2 = 70, + UNW_PPC64_CR3 = 71, + UNW_PPC64_CR4 = 72, + UNW_PPC64_CR5 = 73, + UNW_PPC64_CR6 = 74, + UNW_PPC64_CR7 = 75, + UNW_PPC64_XER = 76, + UNW_PPC64_V0 = 77, + UNW_PPC64_V1 = 78, + UNW_PPC64_V2 = 79, + UNW_PPC64_V3 = 80, + UNW_PPC64_V4 = 81, + UNW_PPC64_V5 = 82, + UNW_PPC64_V6 = 83, + UNW_PPC64_V7 = 84, + UNW_PPC64_V8 = 85, + UNW_PPC64_V9 = 86, + UNW_PPC64_V10 = 87, + UNW_PPC64_V11 = 88, + UNW_PPC64_V12 = 89, + UNW_PPC64_V13 = 90, + UNW_PPC64_V14 = 91, + UNW_PPC64_V15 = 92, + UNW_PPC64_V16 = 93, + UNW_PPC64_V17 = 94, + UNW_PPC64_V18 = 95, + UNW_PPC64_V19 = 96, + UNW_PPC64_V20 = 97, + UNW_PPC64_V21 = 98, + UNW_PPC64_V22 = 99, + UNW_PPC64_V23 = 100, + UNW_PPC64_V24 = 101, + UNW_PPC64_V25 = 102, + UNW_PPC64_V26 = 103, + UNW_PPC64_V27 = 104, + UNW_PPC64_V28 = 105, + UNW_PPC64_V29 = 106, + UNW_PPC64_V30 = 107, + UNW_PPC64_V31 = 108, + // 109, 111-113: OpenPOWER ELF V2 ABI: reserved + // Borrowing VRSAVE number from PPC32. + UNW_PPC64_VRSAVE = 109, + UNW_PPC64_VSCR = 110, + UNW_PPC64_TFHAR = 114, + UNW_PPC64_TFIAR = 115, + UNW_PPC64_TEXASR = 116, + UNW_PPC64_VS0 = UNW_PPC64_F0, + UNW_PPC64_VS1 = UNW_PPC64_F1, + UNW_PPC64_VS2 = UNW_PPC64_F2, + UNW_PPC64_VS3 = UNW_PPC64_F3, + UNW_PPC64_VS4 = UNW_PPC64_F4, + UNW_PPC64_VS5 = UNW_PPC64_F5, + UNW_PPC64_VS6 = UNW_PPC64_F6, + UNW_PPC64_VS7 = UNW_PPC64_F7, + UNW_PPC64_VS8 = UNW_PPC64_F8, + UNW_PPC64_VS9 = UNW_PPC64_F9, + UNW_PPC64_VS10 = UNW_PPC64_F10, + UNW_PPC64_VS11 = UNW_PPC64_F11, + UNW_PPC64_VS12 = UNW_PPC64_F12, + UNW_PPC64_VS13 = UNW_PPC64_F13, + UNW_PPC64_VS14 = UNW_PPC64_F14, + UNW_PPC64_VS15 = UNW_PPC64_F15, + UNW_PPC64_VS16 = UNW_PPC64_F16, + UNW_PPC64_VS17 = UNW_PPC64_F17, + UNW_PPC64_VS18 = UNW_PPC64_F18, + UNW_PPC64_VS19 = UNW_PPC64_F19, + UNW_PPC64_VS20 = UNW_PPC64_F20, + UNW_PPC64_VS21 = UNW_PPC64_F21, + UNW_PPC64_VS22 = UNW_PPC64_F22, + UNW_PPC64_VS23 = UNW_PPC64_F23, + UNW_PPC64_VS24 = UNW_PPC64_F24, + UNW_PPC64_VS25 = UNW_PPC64_F25, + UNW_PPC64_VS26 = UNW_PPC64_F26, + UNW_PPC64_VS27 = UNW_PPC64_F27, + UNW_PPC64_VS28 = UNW_PPC64_F28, + UNW_PPC64_VS29 = UNW_PPC64_F29, + UNW_PPC64_VS30 = UNW_PPC64_F30, + UNW_PPC64_VS31 = UNW_PPC64_F31, + UNW_PPC64_VS32 = UNW_PPC64_V0, + UNW_PPC64_VS33 = UNW_PPC64_V1, + UNW_PPC64_VS34 = UNW_PPC64_V2, + UNW_PPC64_VS35 = UNW_PPC64_V3, + UNW_PPC64_VS36 = UNW_PPC64_V4, + UNW_PPC64_VS37 = UNW_PPC64_V5, + UNW_PPC64_VS38 = UNW_PPC64_V6, + UNW_PPC64_VS39 = UNW_PPC64_V7, + UNW_PPC64_VS40 = UNW_PPC64_V8, + UNW_PPC64_VS41 = UNW_PPC64_V9, + UNW_PPC64_VS42 = UNW_PPC64_V10, + UNW_PPC64_VS43 = UNW_PPC64_V11, + UNW_PPC64_VS44 = UNW_PPC64_V12, + UNW_PPC64_VS45 = UNW_PPC64_V13, + UNW_PPC64_VS46 = UNW_PPC64_V14, + UNW_PPC64_VS47 = UNW_PPC64_V15, + UNW_PPC64_VS48 = UNW_PPC64_V16, + UNW_PPC64_VS49 = UNW_PPC64_V17, + UNW_PPC64_VS50 = UNW_PPC64_V18, + UNW_PPC64_VS51 = UNW_PPC64_V19, + UNW_PPC64_VS52 = UNW_PPC64_V20, + UNW_PPC64_VS53 = UNW_PPC64_V21, + UNW_PPC64_VS54 = UNW_PPC64_V22, + UNW_PPC64_VS55 = UNW_PPC64_V23, + UNW_PPC64_VS56 = UNW_PPC64_V24, + UNW_PPC64_VS57 = UNW_PPC64_V25, + UNW_PPC64_VS58 = UNW_PPC64_V26, + UNW_PPC64_VS59 = UNW_PPC64_V27, + UNW_PPC64_VS60 = UNW_PPC64_V28, + UNW_PPC64_VS61 = UNW_PPC64_V29, + UNW_PPC64_VS62 = UNW_PPC64_V30, + UNW_PPC64_VS63 = UNW_PPC64_V31 +}; + +// 64-bit ARM64 registers +enum { + UNW_AARCH64_X0 = 0, + UNW_AARCH64_X1 = 1, + UNW_AARCH64_X2 = 2, + UNW_AARCH64_X3 = 3, + UNW_AARCH64_X4 = 4, + UNW_AARCH64_X5 = 5, + UNW_AARCH64_X6 = 6, + UNW_AARCH64_X7 = 7, + UNW_AARCH64_X8 = 8, + UNW_AARCH64_X9 = 9, + UNW_AARCH64_X10 = 10, + UNW_AARCH64_X11 = 11, + UNW_AARCH64_X12 = 12, + UNW_AARCH64_X13 = 13, + UNW_AARCH64_X14 = 14, + UNW_AARCH64_X15 = 15, + UNW_AARCH64_X16 = 16, + UNW_AARCH64_X17 = 17, + UNW_AARCH64_X18 = 18, + UNW_AARCH64_X19 = 19, + UNW_AARCH64_X20 = 20, + UNW_AARCH64_X21 = 21, + UNW_AARCH64_X22 = 22, + UNW_AARCH64_X23 = 23, + UNW_AARCH64_X24 = 24, + UNW_AARCH64_X25 = 25, + UNW_AARCH64_X26 = 26, + UNW_AARCH64_X27 = 27, + UNW_AARCH64_X28 = 28, + UNW_AARCH64_X29 = 29, + UNW_AARCH64_FP = 29, + UNW_AARCH64_X30 = 30, + UNW_AARCH64_LR = 30, + UNW_AARCH64_X31 = 31, + UNW_AARCH64_SP = 31, + UNW_AARCH64_PC = 32, + + // reserved block + UNW_AARCH64_RA_SIGN_STATE = 34, + + // FP/vector registers + UNW_AARCH64_V0 = 64, + UNW_AARCH64_V1 = 65, + UNW_AARCH64_V2 = 66, + UNW_AARCH64_V3 = 67, + UNW_AARCH64_V4 = 68, + UNW_AARCH64_V5 = 69, + UNW_AARCH64_V6 = 70, + UNW_AARCH64_V7 = 71, + UNW_AARCH64_V8 = 72, + UNW_AARCH64_V9 = 73, + UNW_AARCH64_V10 = 74, + UNW_AARCH64_V11 = 75, + UNW_AARCH64_V12 = 76, + UNW_AARCH64_V13 = 77, + UNW_AARCH64_V14 = 78, + UNW_AARCH64_V15 = 79, + UNW_AARCH64_V16 = 80, + UNW_AARCH64_V17 = 81, + UNW_AARCH64_V18 = 82, + UNW_AARCH64_V19 = 83, + UNW_AARCH64_V20 = 84, + UNW_AARCH64_V21 = 85, + UNW_AARCH64_V22 = 86, + UNW_AARCH64_V23 = 87, + UNW_AARCH64_V24 = 88, + UNW_AARCH64_V25 = 89, + UNW_AARCH64_V26 = 90, + UNW_AARCH64_V27 = 91, + UNW_AARCH64_V28 = 92, + UNW_AARCH64_V29 = 93, + UNW_AARCH64_V30 = 94, + UNW_AARCH64_V31 = 95, + + // Compatibility aliases + UNW_ARM64_X0 = UNW_AARCH64_X0, + UNW_ARM64_X1 = UNW_AARCH64_X1, + UNW_ARM64_X2 = UNW_AARCH64_X2, + UNW_ARM64_X3 = UNW_AARCH64_X3, + UNW_ARM64_X4 = UNW_AARCH64_X4, + UNW_ARM64_X5 = UNW_AARCH64_X5, + UNW_ARM64_X6 = UNW_AARCH64_X6, + UNW_ARM64_X7 = UNW_AARCH64_X7, + UNW_ARM64_X8 = UNW_AARCH64_X8, + UNW_ARM64_X9 = UNW_AARCH64_X9, + UNW_ARM64_X10 = UNW_AARCH64_X10, + UNW_ARM64_X11 = UNW_AARCH64_X11, + UNW_ARM64_X12 = UNW_AARCH64_X12, + UNW_ARM64_X13 = UNW_AARCH64_X13, + UNW_ARM64_X14 = UNW_AARCH64_X14, + UNW_ARM64_X15 = UNW_AARCH64_X15, + UNW_ARM64_X16 = UNW_AARCH64_X16, + UNW_ARM64_X17 = UNW_AARCH64_X17, + UNW_ARM64_X18 = UNW_AARCH64_X18, + UNW_ARM64_X19 = UNW_AARCH64_X19, + UNW_ARM64_X20 = UNW_AARCH64_X20, + UNW_ARM64_X21 = UNW_AARCH64_X21, + UNW_ARM64_X22 = UNW_AARCH64_X22, + UNW_ARM64_X23 = UNW_AARCH64_X23, + UNW_ARM64_X24 = UNW_AARCH64_X24, + UNW_ARM64_X25 = UNW_AARCH64_X25, + UNW_ARM64_X26 = UNW_AARCH64_X26, + UNW_ARM64_X27 = UNW_AARCH64_X27, + UNW_ARM64_X28 = UNW_AARCH64_X28, + UNW_ARM64_X29 = UNW_AARCH64_X29, + UNW_ARM64_FP = UNW_AARCH64_FP, + UNW_ARM64_X30 = UNW_AARCH64_X30, + UNW_ARM64_LR = UNW_AARCH64_LR, + UNW_ARM64_X31 = UNW_AARCH64_X31, + UNW_ARM64_SP = UNW_AARCH64_SP, + UNW_ARM64_PC = UNW_AARCH64_PC, + UNW_ARM64_RA_SIGN_STATE = UNW_AARCH64_RA_SIGN_STATE, + UNW_ARM64_D0 = UNW_AARCH64_V0, + UNW_ARM64_D1 = UNW_AARCH64_V1, + UNW_ARM64_D2 = UNW_AARCH64_V2, + UNW_ARM64_D3 = UNW_AARCH64_V3, + UNW_ARM64_D4 = UNW_AARCH64_V4, + UNW_ARM64_D5 = UNW_AARCH64_V5, + UNW_ARM64_D6 = UNW_AARCH64_V6, + UNW_ARM64_D7 = UNW_AARCH64_V7, + UNW_ARM64_D8 = UNW_AARCH64_V8, + UNW_ARM64_D9 = UNW_AARCH64_V9, + UNW_ARM64_D10 = UNW_AARCH64_V10, + UNW_ARM64_D11 = UNW_AARCH64_V11, + UNW_ARM64_D12 = UNW_AARCH64_V12, + UNW_ARM64_D13 = UNW_AARCH64_V13, + UNW_ARM64_D14 = UNW_AARCH64_V14, + UNW_ARM64_D15 = UNW_AARCH64_V15, + UNW_ARM64_D16 = UNW_AARCH64_V16, + UNW_ARM64_D17 = UNW_AARCH64_V17, + UNW_ARM64_D18 = UNW_AARCH64_V18, + UNW_ARM64_D19 = UNW_AARCH64_V19, + UNW_ARM64_D20 = UNW_AARCH64_V20, + UNW_ARM64_D21 = UNW_AARCH64_V21, + UNW_ARM64_D22 = UNW_AARCH64_V22, + UNW_ARM64_D23 = UNW_AARCH64_V23, + UNW_ARM64_D24 = UNW_AARCH64_V24, + UNW_ARM64_D25 = UNW_AARCH64_V25, + UNW_ARM64_D26 = UNW_AARCH64_V26, + UNW_ARM64_D27 = UNW_AARCH64_V27, + UNW_ARM64_D28 = UNW_AARCH64_V28, + UNW_ARM64_D29 = UNW_AARCH64_V29, + UNW_ARM64_D30 = UNW_AARCH64_V30, + UNW_ARM64_D31 = UNW_AARCH64_V31, +}; + +// 32-bit ARM registers. Numbers match DWARF for ARM spec #3.1 Table 1. +// Naming scheme uses recommendations given in Note 4 for VFP-v2 and VFP-v3. +// In this scheme, even though the 64-bit floating point registers D0-D31 +// overlap physically with the 32-bit floating pointer registers S0-S31, +// they are given a non-overlapping range of register numbers. +// +// Commented out ranges are not preserved during unwinding. +enum { + UNW_ARM_R0 = 0, + UNW_ARM_R1 = 1, + UNW_ARM_R2 = 2, + UNW_ARM_R3 = 3, + UNW_ARM_R4 = 4, + UNW_ARM_R5 = 5, + UNW_ARM_R6 = 6, + UNW_ARM_R7 = 7, + UNW_ARM_R8 = 8, + UNW_ARM_R9 = 9, + UNW_ARM_R10 = 10, + UNW_ARM_R11 = 11, + UNW_ARM_R12 = 12, + UNW_ARM_SP = 13, // Logical alias for UNW_REG_SP + UNW_ARM_R13 = 13, + UNW_ARM_LR = 14, + UNW_ARM_R14 = 14, + UNW_ARM_IP = 15, // Logical alias for UNW_REG_IP + UNW_ARM_R15 = 15, + // 16-63 -- OBSOLETE. Used in VFP1 to represent both S0-S31 and D0-D31. + UNW_ARM_S0 = 64, + UNW_ARM_S1 = 65, + UNW_ARM_S2 = 66, + UNW_ARM_S3 = 67, + UNW_ARM_S4 = 68, + UNW_ARM_S5 = 69, + UNW_ARM_S6 = 70, + UNW_ARM_S7 = 71, + UNW_ARM_S8 = 72, + UNW_ARM_S9 = 73, + UNW_ARM_S10 = 74, + UNW_ARM_S11 = 75, + UNW_ARM_S12 = 76, + UNW_ARM_S13 = 77, + UNW_ARM_S14 = 78, + UNW_ARM_S15 = 79, + UNW_ARM_S16 = 80, + UNW_ARM_S17 = 81, + UNW_ARM_S18 = 82, + UNW_ARM_S19 = 83, + UNW_ARM_S20 = 84, + UNW_ARM_S21 = 85, + UNW_ARM_S22 = 86, + UNW_ARM_S23 = 87, + UNW_ARM_S24 = 88, + UNW_ARM_S25 = 89, + UNW_ARM_S26 = 90, + UNW_ARM_S27 = 91, + UNW_ARM_S28 = 92, + UNW_ARM_S29 = 93, + UNW_ARM_S30 = 94, + UNW_ARM_S31 = 95, + // 96-103 -- OBSOLETE. F0-F7. Used by the FPA system. Superseded by VFP. + // 104-111 -- wCGR0-wCGR7, ACC0-ACC7 (Intel wireless MMX) + UNW_ARM_WR0 = 112, + UNW_ARM_WR1 = 113, + UNW_ARM_WR2 = 114, + UNW_ARM_WR3 = 115, + UNW_ARM_WR4 = 116, + UNW_ARM_WR5 = 117, + UNW_ARM_WR6 = 118, + UNW_ARM_WR7 = 119, + UNW_ARM_WR8 = 120, + UNW_ARM_WR9 = 121, + UNW_ARM_WR10 = 122, + UNW_ARM_WR11 = 123, + UNW_ARM_WR12 = 124, + UNW_ARM_WR13 = 125, + UNW_ARM_WR14 = 126, + UNW_ARM_WR15 = 127, + // 128-133 -- SPSR, SPSR_{FIQ|IRQ|ABT|UND|SVC} + // 134-143 -- Reserved + // 144-150 -- R8_USR-R14_USR + // 151-157 -- R8_FIQ-R14_FIQ + // 158-159 -- R13_IRQ-R14_IRQ + // 160-161 -- R13_ABT-R14_ABT + // 162-163 -- R13_UND-R14_UND + // 164-165 -- R13_SVC-R14_SVC + // 166-191 -- Reserved + UNW_ARM_WC0 = 192, + UNW_ARM_WC1 = 193, + UNW_ARM_WC2 = 194, + UNW_ARM_WC3 = 195, + // 196-199 -- wC4-wC7 (Intel wireless MMX control) + // 200-255 -- Reserved + UNW_ARM_D0 = 256, + UNW_ARM_D1 = 257, + UNW_ARM_D2 = 258, + UNW_ARM_D3 = 259, + UNW_ARM_D4 = 260, + UNW_ARM_D5 = 261, + UNW_ARM_D6 = 262, + UNW_ARM_D7 = 263, + UNW_ARM_D8 = 264, + UNW_ARM_D9 = 265, + UNW_ARM_D10 = 266, + UNW_ARM_D11 = 267, + UNW_ARM_D12 = 268, + UNW_ARM_D13 = 269, + UNW_ARM_D14 = 270, + UNW_ARM_D15 = 271, + UNW_ARM_D16 = 272, + UNW_ARM_D17 = 273, + UNW_ARM_D18 = 274, + UNW_ARM_D19 = 275, + UNW_ARM_D20 = 276, + UNW_ARM_D21 = 277, + UNW_ARM_D22 = 278, + UNW_ARM_D23 = 279, + UNW_ARM_D24 = 280, + UNW_ARM_D25 = 281, + UNW_ARM_D26 = 282, + UNW_ARM_D27 = 283, + UNW_ARM_D28 = 284, + UNW_ARM_D29 = 285, + UNW_ARM_D30 = 286, + UNW_ARM_D31 = 287, + // 288-319 -- Reserved for VFP/Neon + // 320-8191 -- Reserved + // 8192-16383 -- Unspecified vendor co-processor register. +}; + +// OpenRISC1000 register numbers +enum { + UNW_OR1K_R0 = 0, + UNW_OR1K_R1 = 1, + UNW_OR1K_R2 = 2, + UNW_OR1K_R3 = 3, + UNW_OR1K_R4 = 4, + UNW_OR1K_R5 = 5, + UNW_OR1K_R6 = 6, + UNW_OR1K_R7 = 7, + UNW_OR1K_R8 = 8, + UNW_OR1K_R9 = 9, + UNW_OR1K_R10 = 10, + UNW_OR1K_R11 = 11, + UNW_OR1K_R12 = 12, + UNW_OR1K_R13 = 13, + UNW_OR1K_R14 = 14, + UNW_OR1K_R15 = 15, + UNW_OR1K_R16 = 16, + UNW_OR1K_R17 = 17, + UNW_OR1K_R18 = 18, + UNW_OR1K_R19 = 19, + UNW_OR1K_R20 = 20, + UNW_OR1K_R21 = 21, + UNW_OR1K_R22 = 22, + UNW_OR1K_R23 = 23, + UNW_OR1K_R24 = 24, + UNW_OR1K_R25 = 25, + UNW_OR1K_R26 = 26, + UNW_OR1K_R27 = 27, + UNW_OR1K_R28 = 28, + UNW_OR1K_R29 = 29, + UNW_OR1K_R30 = 30, + UNW_OR1K_R31 = 31, + UNW_OR1K_EPCR = 32, +}; + +// MIPS registers +enum { + UNW_MIPS_R0 = 0, + UNW_MIPS_R1 = 1, + UNW_MIPS_R2 = 2, + UNW_MIPS_R3 = 3, + UNW_MIPS_R4 = 4, + UNW_MIPS_R5 = 5, + UNW_MIPS_R6 = 6, + UNW_MIPS_R7 = 7, + UNW_MIPS_R8 = 8, + UNW_MIPS_R9 = 9, + UNW_MIPS_R10 = 10, + UNW_MIPS_R11 = 11, + UNW_MIPS_R12 = 12, + UNW_MIPS_R13 = 13, + UNW_MIPS_R14 = 14, + UNW_MIPS_R15 = 15, + UNW_MIPS_R16 = 16, + UNW_MIPS_R17 = 17, + UNW_MIPS_R18 = 18, + UNW_MIPS_R19 = 19, + UNW_MIPS_R20 = 20, + UNW_MIPS_R21 = 21, + UNW_MIPS_R22 = 22, + UNW_MIPS_R23 = 23, + UNW_MIPS_R24 = 24, + UNW_MIPS_R25 = 25, + UNW_MIPS_R26 = 26, + UNW_MIPS_R27 = 27, + UNW_MIPS_R28 = 28, + UNW_MIPS_R29 = 29, + UNW_MIPS_R30 = 30, + UNW_MIPS_R31 = 31, + UNW_MIPS_F0 = 32, + UNW_MIPS_F1 = 33, + UNW_MIPS_F2 = 34, + UNW_MIPS_F3 = 35, + UNW_MIPS_F4 = 36, + UNW_MIPS_F5 = 37, + UNW_MIPS_F6 = 38, + UNW_MIPS_F7 = 39, + UNW_MIPS_F8 = 40, + UNW_MIPS_F9 = 41, + UNW_MIPS_F10 = 42, + UNW_MIPS_F11 = 43, + UNW_MIPS_F12 = 44, + UNW_MIPS_F13 = 45, + UNW_MIPS_F14 = 46, + UNW_MIPS_F15 = 47, + UNW_MIPS_F16 = 48, + UNW_MIPS_F17 = 49, + UNW_MIPS_F18 = 50, + UNW_MIPS_F19 = 51, + UNW_MIPS_F20 = 52, + UNW_MIPS_F21 = 53, + UNW_MIPS_F22 = 54, + UNW_MIPS_F23 = 55, + UNW_MIPS_F24 = 56, + UNW_MIPS_F25 = 57, + UNW_MIPS_F26 = 58, + UNW_MIPS_F27 = 59, + UNW_MIPS_F28 = 60, + UNW_MIPS_F29 = 61, + UNW_MIPS_F30 = 62, + UNW_MIPS_F31 = 63, + UNW_MIPS_HI = 64, + UNW_MIPS_LO = 65, +}; + +// SPARC registers +enum { + UNW_SPARC_G0 = 0, + UNW_SPARC_G1 = 1, + UNW_SPARC_G2 = 2, + UNW_SPARC_G3 = 3, + UNW_SPARC_G4 = 4, + UNW_SPARC_G5 = 5, + UNW_SPARC_G6 = 6, + UNW_SPARC_G7 = 7, + UNW_SPARC_O0 = 8, + UNW_SPARC_O1 = 9, + UNW_SPARC_O2 = 10, + UNW_SPARC_O3 = 11, + UNW_SPARC_O4 = 12, + UNW_SPARC_O5 = 13, + UNW_SPARC_O6 = 14, + UNW_SPARC_O7 = 15, + UNW_SPARC_L0 = 16, + UNW_SPARC_L1 = 17, + UNW_SPARC_L2 = 18, + UNW_SPARC_L3 = 19, + UNW_SPARC_L4 = 20, + UNW_SPARC_L5 = 21, + UNW_SPARC_L6 = 22, + UNW_SPARC_L7 = 23, + UNW_SPARC_I0 = 24, + UNW_SPARC_I1 = 25, + UNW_SPARC_I2 = 26, + UNW_SPARC_I3 = 27, + UNW_SPARC_I4 = 28, + UNW_SPARC_I5 = 29, + UNW_SPARC_I6 = 30, + UNW_SPARC_I7 = 31, +}; + +// Hexagon register numbers +enum { + UNW_HEXAGON_R0, + UNW_HEXAGON_R1, + UNW_HEXAGON_R2, + UNW_HEXAGON_R3, + UNW_HEXAGON_R4, + UNW_HEXAGON_R5, + UNW_HEXAGON_R6, + UNW_HEXAGON_R7, + UNW_HEXAGON_R8, + UNW_HEXAGON_R9, + UNW_HEXAGON_R10, + UNW_HEXAGON_R11, + UNW_HEXAGON_R12, + UNW_HEXAGON_R13, + UNW_HEXAGON_R14, + UNW_HEXAGON_R15, + UNW_HEXAGON_R16, + UNW_HEXAGON_R17, + UNW_HEXAGON_R18, + UNW_HEXAGON_R19, + UNW_HEXAGON_R20, + UNW_HEXAGON_R21, + UNW_HEXAGON_R22, + UNW_HEXAGON_R23, + UNW_HEXAGON_R24, + UNW_HEXAGON_R25, + UNW_HEXAGON_R26, + UNW_HEXAGON_R27, + UNW_HEXAGON_R28, + UNW_HEXAGON_R29, + UNW_HEXAGON_R30, + UNW_HEXAGON_R31, + UNW_HEXAGON_P3_0, + UNW_HEXAGON_PC, +}; + +// RISC-V registers. These match the DWARF register numbers defined by section +// 4 of the RISC-V ELF psABI specification, which can be found at: +// +// https://github.com/riscv/riscv-elf-psabi-doc/blob/master/riscv-elf.md +enum { + UNW_RISCV_X0 = 0, + UNW_RISCV_X1 = 1, + UNW_RISCV_X2 = 2, + UNW_RISCV_X3 = 3, + UNW_RISCV_X4 = 4, + UNW_RISCV_X5 = 5, + UNW_RISCV_X6 = 6, + UNW_RISCV_X7 = 7, + UNW_RISCV_X8 = 8, + UNW_RISCV_X9 = 9, + UNW_RISCV_X10 = 10, + UNW_RISCV_X11 = 11, + UNW_RISCV_X12 = 12, + UNW_RISCV_X13 = 13, + UNW_RISCV_X14 = 14, + UNW_RISCV_X15 = 15, + UNW_RISCV_X16 = 16, + UNW_RISCV_X17 = 17, + UNW_RISCV_X18 = 18, + UNW_RISCV_X19 = 19, + UNW_RISCV_X20 = 20, + UNW_RISCV_X21 = 21, + UNW_RISCV_X22 = 22, + UNW_RISCV_X23 = 23, + UNW_RISCV_X24 = 24, + UNW_RISCV_X25 = 25, + UNW_RISCV_X26 = 26, + UNW_RISCV_X27 = 27, + UNW_RISCV_X28 = 28, + UNW_RISCV_X29 = 29, + UNW_RISCV_X30 = 30, + UNW_RISCV_X31 = 31, + UNW_RISCV_F0 = 32, + UNW_RISCV_F1 = 33, + UNW_RISCV_F2 = 34, + UNW_RISCV_F3 = 35, + UNW_RISCV_F4 = 36, + UNW_RISCV_F5 = 37, + UNW_RISCV_F6 = 38, + UNW_RISCV_F7 = 39, + UNW_RISCV_F8 = 40, + UNW_RISCV_F9 = 41, + UNW_RISCV_F10 = 42, + UNW_RISCV_F11 = 43, + UNW_RISCV_F12 = 44, + UNW_RISCV_F13 = 45, + UNW_RISCV_F14 = 46, + UNW_RISCV_F15 = 47, + UNW_RISCV_F16 = 48, + UNW_RISCV_F17 = 49, + UNW_RISCV_F18 = 50, + UNW_RISCV_F19 = 51, + UNW_RISCV_F20 = 52, + UNW_RISCV_F21 = 53, + UNW_RISCV_F22 = 54, + UNW_RISCV_F23 = 55, + UNW_RISCV_F24 = 56, + UNW_RISCV_F25 = 57, + UNW_RISCV_F26 = 58, + UNW_RISCV_F27 = 59, + UNW_RISCV_F28 = 60, + UNW_RISCV_F29 = 61, + UNW_RISCV_F30 = 62, + UNW_RISCV_F31 = 63, +}; + +// VE register numbers +enum { + UNW_VE_S0 = 0, + UNW_VE_S1 = 1, + UNW_VE_S2 = 2, + UNW_VE_S3 = 3, + UNW_VE_S4 = 4, + UNW_VE_S5 = 5, + UNW_VE_S6 = 6, + UNW_VE_S7 = 7, + UNW_VE_S8 = 8, + UNW_VE_S9 = 9, + UNW_VE_S10 = 10, + UNW_VE_S11 = 11, + UNW_VE_S12 = 12, + UNW_VE_S13 = 13, + UNW_VE_S14 = 14, + UNW_VE_S15 = 15, + UNW_VE_S16 = 16, + UNW_VE_S17 = 17, + UNW_VE_S18 = 18, + UNW_VE_S19 = 19, + UNW_VE_S20 = 20, + UNW_VE_S21 = 21, + UNW_VE_S22 = 22, + UNW_VE_S23 = 23, + UNW_VE_S24 = 24, + UNW_VE_S25 = 25, + UNW_VE_S26 = 26, + UNW_VE_S27 = 27, + UNW_VE_S28 = 28, + UNW_VE_S29 = 29, + UNW_VE_S30 = 30, + UNW_VE_S31 = 31, + UNW_VE_S32 = 32, + UNW_VE_S33 = 33, + UNW_VE_S34 = 34, + UNW_VE_S35 = 35, + UNW_VE_S36 = 36, + UNW_VE_S37 = 37, + UNW_VE_S38 = 38, + UNW_VE_S39 = 39, + UNW_VE_S40 = 40, + UNW_VE_S41 = 41, + UNW_VE_S42 = 42, + UNW_VE_S43 = 43, + UNW_VE_S44 = 44, + UNW_VE_S45 = 45, + UNW_VE_S46 = 46, + UNW_VE_S47 = 47, + UNW_VE_S48 = 48, + UNW_VE_S49 = 49, + UNW_VE_S50 = 50, + UNW_VE_S51 = 51, + UNW_VE_S52 = 52, + UNW_VE_S53 = 53, + UNW_VE_S54 = 54, + UNW_VE_S55 = 55, + UNW_VE_S56 = 56, + UNW_VE_S57 = 57, + UNW_VE_S58 = 58, + UNW_VE_S59 = 59, + UNW_VE_S60 = 60, + UNW_VE_S61 = 61, + UNW_VE_S62 = 62, + UNW_VE_S63 = 63, + UNW_VE_V0 = 64 + 0, + UNW_VE_V1 = 64 + 1, + UNW_VE_V2 = 64 + 2, + UNW_VE_V3 = 64 + 3, + UNW_VE_V4 = 64 + 4, + UNW_VE_V5 = 64 + 5, + UNW_VE_V6 = 64 + 6, + UNW_VE_V7 = 64 + 7, + UNW_VE_V8 = 64 + 8, + UNW_VE_V9 = 64 + 9, + UNW_VE_V10 = 64 + 10, + UNW_VE_V11 = 64 + 11, + UNW_VE_V12 = 64 + 12, + UNW_VE_V13 = 64 + 13, + UNW_VE_V14 = 64 + 14, + UNW_VE_V15 = 64 + 15, + UNW_VE_V16 = 64 + 16, + UNW_VE_V17 = 64 + 17, + UNW_VE_V18 = 64 + 18, + UNW_VE_V19 = 64 + 19, + UNW_VE_V20 = 64 + 20, + UNW_VE_V21 = 64 + 21, + UNW_VE_V22 = 64 + 22, + UNW_VE_V23 = 64 + 23, + UNW_VE_V24 = 64 + 24, + UNW_VE_V25 = 64 + 25, + UNW_VE_V26 = 64 + 26, + UNW_VE_V27 = 64 + 27, + UNW_VE_V28 = 64 + 28, + UNW_VE_V29 = 64 + 29, + UNW_VE_V30 = 64 + 30, + UNW_VE_V31 = 64 + 31, + UNW_VE_V32 = 64 + 32, + UNW_VE_V33 = 64 + 33, + UNW_VE_V34 = 64 + 34, + UNW_VE_V35 = 64 + 35, + UNW_VE_V36 = 64 + 36, + UNW_VE_V37 = 64 + 37, + UNW_VE_V38 = 64 + 38, + UNW_VE_V39 = 64 + 39, + UNW_VE_V40 = 64 + 40, + UNW_VE_V41 = 64 + 41, + UNW_VE_V42 = 64 + 42, + UNW_VE_V43 = 64 + 43, + UNW_VE_V44 = 64 + 44, + UNW_VE_V45 = 64 + 45, + UNW_VE_V46 = 64 + 46, + UNW_VE_V47 = 64 + 47, + UNW_VE_V48 = 64 + 48, + UNW_VE_V49 = 64 + 49, + UNW_VE_V50 = 64 + 50, + UNW_VE_V51 = 64 + 51, + UNW_VE_V52 = 64 + 52, + UNW_VE_V53 = 64 + 53, + UNW_VE_V54 = 64 + 54, + UNW_VE_V55 = 64 + 55, + UNW_VE_V56 = 64 + 56, + UNW_VE_V57 = 64 + 57, + UNW_VE_V58 = 64 + 58, + UNW_VE_V59 = 64 + 59, + UNW_VE_V60 = 64 + 60, + UNW_VE_V61 = 64 + 61, + UNW_VE_V62 = 64 + 62, + UNW_VE_V63 = 64 + 63, + UNW_VE_VM0 = 128 + 0, + UNW_VE_VM1 = 128 + 1, + UNW_VE_VM2 = 128 + 2, + UNW_VE_VM3 = 128 + 3, + UNW_VE_VM4 = 128 + 4, + UNW_VE_VM5 = 128 + 5, + UNW_VE_VM6 = 128 + 6, + UNW_VE_VM7 = 128 + 7, + UNW_VE_VM8 = 128 + 8, + UNW_VE_VM9 = 128 + 9, + UNW_VE_VM10 = 128 + 10, + UNW_VE_VM11 = 128 + 11, + UNW_VE_VM12 = 128 + 12, + UNW_VE_VM13 = 128 + 13, + UNW_VE_VM14 = 128 + 14, + UNW_VE_VM15 = 128 + 15, // = 143 + + // Following registers don't have DWARF register numbers. + UNW_VE_VIXR = 144, + UNW_VE_VL = 145, +}; + +#endif diff --git a/shared/sentry/external/crashpad/libunwind/include/mach-o/compact_unwind_encoding.h b/shared/sentry/external/crashpad/libunwind/include/mach-o/compact_unwind_encoding.h new file mode 100644 index 000000000..5301b1055 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/include/mach-o/compact_unwind_encoding.h @@ -0,0 +1,477 @@ +//===------------------ mach-o/compact_unwind_encoding.h ------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Darwin's alternative to DWARF based unwind encodings. +// +//===----------------------------------------------------------------------===// + + +#ifndef __COMPACT_UNWIND_ENCODING__ +#define __COMPACT_UNWIND_ENCODING__ + +#include + +// +// Compilers can emit standard DWARF FDEs in the __TEXT,__eh_frame section +// of object files. Or compilers can emit compact unwind information in +// the __LD,__compact_unwind section. +// +// When the linker creates a final linked image, it will create a +// __TEXT,__unwind_info section. This section is a small and fast way for the +// runtime to access unwind info for any given function. If the compiler +// emitted compact unwind info for the function, that compact unwind info will +// be encoded in the __TEXT,__unwind_info section. If the compiler emitted +// DWARF unwind info, the __TEXT,__unwind_info section will contain the offset +// of the FDE in the __TEXT,__eh_frame section in the final linked image. +// +// Note: Previously, the linker would transform some DWARF unwind infos into +// compact unwind info. But that is fragile and no longer done. + + +// +// The compact unwind endoding is a 32-bit value which encoded in an +// architecture specific way, which registers to restore from where, and how +// to unwind out of the function. +// +typedef uint32_t compact_unwind_encoding_t; + + +// architecture independent bits +enum { + UNWIND_IS_NOT_FUNCTION_START = 0x80000000, + UNWIND_HAS_LSDA = 0x40000000, + UNWIND_PERSONALITY_MASK = 0x30000000, +}; + + + + +// +// x86 +// +// 1-bit: start +// 1-bit: has lsda +// 2-bit: personality index +// +// 4-bits: 0=old, 1=ebp based, 2=stack-imm, 3=stack-ind, 4=DWARF +// ebp based: +// 15-bits (5*3-bits per reg) register permutation +// 8-bits for stack offset +// frameless: +// 8-bits stack size +// 3-bits stack adjust +// 3-bits register count +// 10-bits register permutation +// +enum { + UNWIND_X86_MODE_MASK = 0x0F000000, + UNWIND_X86_MODE_EBP_FRAME = 0x01000000, + UNWIND_X86_MODE_STACK_IMMD = 0x02000000, + UNWIND_X86_MODE_STACK_IND = 0x03000000, + UNWIND_X86_MODE_DWARF = 0x04000000, + + UNWIND_X86_EBP_FRAME_REGISTERS = 0x00007FFF, + UNWIND_X86_EBP_FRAME_OFFSET = 0x00FF0000, + + UNWIND_X86_FRAMELESS_STACK_SIZE = 0x00FF0000, + UNWIND_X86_FRAMELESS_STACK_ADJUST = 0x0000E000, + UNWIND_X86_FRAMELESS_STACK_REG_COUNT = 0x00001C00, + UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION = 0x000003FF, + + UNWIND_X86_DWARF_SECTION_OFFSET = 0x00FFFFFF, +}; + +enum { + UNWIND_X86_REG_NONE = 0, + UNWIND_X86_REG_EBX = 1, + UNWIND_X86_REG_ECX = 2, + UNWIND_X86_REG_EDX = 3, + UNWIND_X86_REG_EDI = 4, + UNWIND_X86_REG_ESI = 5, + UNWIND_X86_REG_EBP = 6, +}; + +// +// For x86 there are four modes for the compact unwind encoding: +// UNWIND_X86_MODE_EBP_FRAME: +// EBP based frame where EBP is push on stack immediately after return address, +// then ESP is moved to EBP. Thus, to unwind ESP is restored with the current +// EPB value, then EBP is restored by popping off the stack, and the return +// is done by popping the stack once more into the pc. +// All non-volatile registers that need to be restored must have been saved +// in a small range in the stack that starts EBP-4 to EBP-1020. The offset/4 +// is encoded in the UNWIND_X86_EBP_FRAME_OFFSET bits. The registers saved +// are encoded in the UNWIND_X86_EBP_FRAME_REGISTERS bits as five 3-bit entries. +// Each entry contains which register to restore. +// UNWIND_X86_MODE_STACK_IMMD: +// A "frameless" (EBP not used as frame pointer) function with a small +// constant stack size. To return, a constant (encoded in the compact +// unwind encoding) is added to the ESP. Then the return is done by +// popping the stack into the pc. +// All non-volatile registers that need to be restored must have been saved +// on the stack immediately after the return address. The stack_size/4 is +// encoded in the UNWIND_X86_FRAMELESS_STACK_SIZE (max stack size is 1024). +// The number of registers saved is encoded in UNWIND_X86_FRAMELESS_STACK_REG_COUNT. +// UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION constains which registers were +// saved and their order. +// UNWIND_X86_MODE_STACK_IND: +// A "frameless" (EBP not used as frame pointer) function large constant +// stack size. This case is like the previous, except the stack size is too +// large to encode in the compact unwind encoding. Instead it requires that +// the function contains "subl $nnnnnnnn,ESP" in its prolog. The compact +// encoding contains the offset to the nnnnnnnn value in the function in +// UNWIND_X86_FRAMELESS_STACK_SIZE. +// UNWIND_X86_MODE_DWARF: +// No compact unwind encoding is available. Instead the low 24-bits of the +// compact encoding is the offset of the DWARF FDE in the __eh_frame section. +// This mode is never used in object files. It is only generated by the +// linker in final linked images which have only DWARF unwind info for a +// function. +// +// The permutation encoding is a Lehmer code sequence encoded into a +// single variable-base number so we can encode the ordering of up to +// six registers in a 10-bit space. +// +// The following is the algorithm used to create the permutation encoding used +// with frameless stacks. It is passed the number of registers to be saved and +// an array of the register numbers saved. +// +//uint32_t permute_encode(uint32_t registerCount, const uint32_t registers[6]) +//{ +// uint32_t renumregs[6]; +// for (int i=6-registerCount; i < 6; ++i) { +// int countless = 0; +// for (int j=6-registerCount; j < i; ++j) { +// if ( registers[j] < registers[i] ) +// ++countless; +// } +// renumregs[i] = registers[i] - countless -1; +// } +// uint32_t permutationEncoding = 0; +// switch ( registerCount ) { +// case 6: +// permutationEncoding |= (120*renumregs[0] + 24*renumregs[1] +// + 6*renumregs[2] + 2*renumregs[3] +// + renumregs[4]); +// break; +// case 5: +// permutationEncoding |= (120*renumregs[1] + 24*renumregs[2] +// + 6*renumregs[3] + 2*renumregs[4] +// + renumregs[5]); +// break; +// case 4: +// permutationEncoding |= (60*renumregs[2] + 12*renumregs[3] +// + 3*renumregs[4] + renumregs[5]); +// break; +// case 3: +// permutationEncoding |= (20*renumregs[3] + 4*renumregs[4] +// + renumregs[5]); +// break; +// case 2: +// permutationEncoding |= (5*renumregs[4] + renumregs[5]); +// break; +// case 1: +// permutationEncoding |= (renumregs[5]); +// break; +// } +// return permutationEncoding; +//} +// + + + + +// +// x86_64 +// +// 1-bit: start +// 1-bit: has lsda +// 2-bit: personality index +// +// 4-bits: 0=old, 1=rbp based, 2=stack-imm, 3=stack-ind, 4=DWARF +// rbp based: +// 15-bits (5*3-bits per reg) register permutation +// 8-bits for stack offset +// frameless: +// 8-bits stack size +// 3-bits stack adjust +// 3-bits register count +// 10-bits register permutation +// +enum { + UNWIND_X86_64_MODE_MASK = 0x0F000000, + UNWIND_X86_64_MODE_RBP_FRAME = 0x01000000, + UNWIND_X86_64_MODE_STACK_IMMD = 0x02000000, + UNWIND_X86_64_MODE_STACK_IND = 0x03000000, + UNWIND_X86_64_MODE_DWARF = 0x04000000, + + UNWIND_X86_64_RBP_FRAME_REGISTERS = 0x00007FFF, + UNWIND_X86_64_RBP_FRAME_OFFSET = 0x00FF0000, + + UNWIND_X86_64_FRAMELESS_STACK_SIZE = 0x00FF0000, + UNWIND_X86_64_FRAMELESS_STACK_ADJUST = 0x0000E000, + UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT = 0x00001C00, + UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION = 0x000003FF, + + UNWIND_X86_64_DWARF_SECTION_OFFSET = 0x00FFFFFF, +}; + +enum { + UNWIND_X86_64_REG_NONE = 0, + UNWIND_X86_64_REG_RBX = 1, + UNWIND_X86_64_REG_R12 = 2, + UNWIND_X86_64_REG_R13 = 3, + UNWIND_X86_64_REG_R14 = 4, + UNWIND_X86_64_REG_R15 = 5, + UNWIND_X86_64_REG_RBP = 6, +}; +// +// For x86_64 there are four modes for the compact unwind encoding: +// UNWIND_X86_64_MODE_RBP_FRAME: +// RBP based frame where RBP is push on stack immediately after return address, +// then RSP is moved to RBP. Thus, to unwind RSP is restored with the current +// EPB value, then RBP is restored by popping off the stack, and the return +// is done by popping the stack once more into the pc. +// All non-volatile registers that need to be restored must have been saved +// in a small range in the stack that starts RBP-8 to RBP-2040. The offset/8 +// is encoded in the UNWIND_X86_64_RBP_FRAME_OFFSET bits. The registers saved +// are encoded in the UNWIND_X86_64_RBP_FRAME_REGISTERS bits as five 3-bit entries. +// Each entry contains which register to restore. +// UNWIND_X86_64_MODE_STACK_IMMD: +// A "frameless" (RBP not used as frame pointer) function with a small +// constant stack size. To return, a constant (encoded in the compact +// unwind encoding) is added to the RSP. Then the return is done by +// popping the stack into the pc. +// All non-volatile registers that need to be restored must have been saved +// on the stack immediately after the return address. The stack_size/8 is +// encoded in the UNWIND_X86_64_FRAMELESS_STACK_SIZE (max stack size is 2048). +// The number of registers saved is encoded in UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT. +// UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION constains which registers were +// saved and their order. +// UNWIND_X86_64_MODE_STACK_IND: +// A "frameless" (RBP not used as frame pointer) function large constant +// stack size. This case is like the previous, except the stack size is too +// large to encode in the compact unwind encoding. Instead it requires that +// the function contains "subq $nnnnnnnn,RSP" in its prolog. The compact +// encoding contains the offset to the nnnnnnnn value in the function in +// UNWIND_X86_64_FRAMELESS_STACK_SIZE. +// UNWIND_X86_64_MODE_DWARF: +// No compact unwind encoding is available. Instead the low 24-bits of the +// compact encoding is the offset of the DWARF FDE in the __eh_frame section. +// This mode is never used in object files. It is only generated by the +// linker in final linked images which have only DWARF unwind info for a +// function. +// + + +// ARM64 +// +// 1-bit: start +// 1-bit: has lsda +// 2-bit: personality index +// +// 4-bits: 4=frame-based, 3=DWARF, 2=frameless +// frameless: +// 12-bits of stack size +// frame-based: +// 4-bits D reg pairs saved +// 5-bits X reg pairs saved +// DWARF: +// 24-bits offset of DWARF FDE in __eh_frame section +// +enum { + UNWIND_ARM64_MODE_MASK = 0x0F000000, + UNWIND_ARM64_MODE_FRAMELESS = 0x02000000, + UNWIND_ARM64_MODE_DWARF = 0x03000000, + UNWIND_ARM64_MODE_FRAME = 0x04000000, + + UNWIND_ARM64_FRAME_X19_X20_PAIR = 0x00000001, + UNWIND_ARM64_FRAME_X21_X22_PAIR = 0x00000002, + UNWIND_ARM64_FRAME_X23_X24_PAIR = 0x00000004, + UNWIND_ARM64_FRAME_X25_X26_PAIR = 0x00000008, + UNWIND_ARM64_FRAME_X27_X28_PAIR = 0x00000010, + UNWIND_ARM64_FRAME_D8_D9_PAIR = 0x00000100, + UNWIND_ARM64_FRAME_D10_D11_PAIR = 0x00000200, + UNWIND_ARM64_FRAME_D12_D13_PAIR = 0x00000400, + UNWIND_ARM64_FRAME_D14_D15_PAIR = 0x00000800, + + UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK = 0x00FFF000, + UNWIND_ARM64_DWARF_SECTION_OFFSET = 0x00FFFFFF, +}; +// For arm64 there are three modes for the compact unwind encoding: +// UNWIND_ARM64_MODE_FRAME: +// This is a standard arm64 prolog where FP/LR are immediately pushed on the +// stack, then SP is copied to FP. If there are any non-volatile registers +// saved, then are copied into the stack frame in pairs in a contiguous +// range right below the saved FP/LR pair. Any subset of the five X pairs +// and four D pairs can be saved, but the memory layout must be in register +// number order. +// UNWIND_ARM64_MODE_FRAMELESS: +// A "frameless" leaf function, where FP/LR are not saved. The return address +// remains in LR throughout the function. If any non-volatile registers +// are saved, they must be pushed onto the stack before any stack space is +// allocated for local variables. The stack sized (including any saved +// non-volatile registers) divided by 16 is encoded in the bits +// UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK. +// UNWIND_ARM64_MODE_DWARF: +// No compact unwind encoding is available. Instead the low 24-bits of the +// compact encoding is the offset of the DWARF FDE in the __eh_frame section. +// This mode is never used in object files. It is only generated by the +// linker in final linked images which have only DWARF unwind info for a +// function. +// + + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// Relocatable Object Files: __LD,__compact_unwind +// +//////////////////////////////////////////////////////////////////////////////// + +// +// A compiler can generated compact unwind information for a function by adding +// a "row" to the __LD,__compact_unwind section. This section has the +// S_ATTR_DEBUG bit set, so the section will be ignored by older linkers. +// It is removed by the new linker, so never ends up in final executables. +// This section is a table, initially with one row per function (that needs +// unwind info). The table columns and some conceptual entries are: +// +// range-start pointer to start of function/range +// range-length +// compact-unwind-encoding 32-bit encoding +// personality-function or zero if no personality function +// lsda or zero if no LSDA data +// +// The length and encoding fields are 32-bits. The other are all pointer sized. +// +// In x86_64 assembly, these entry would look like: +// +// .section __LD,__compact_unwind,regular,debug +// +// #compact unwind for _foo +// .quad _foo +// .set L1,LfooEnd-_foo +// .long L1 +// .long 0x01010001 +// .quad 0 +// .quad 0 +// +// #compact unwind for _bar +// .quad _bar +// .set L2,LbarEnd-_bar +// .long L2 +// .long 0x01020011 +// .quad __gxx_personality +// .quad except_tab1 +// +// +// Notes: There is no need for any labels in the the __compact_unwind section. +// The use of the .set directive is to force the evaluation of the +// range-length at assembly time, instead of generating relocations. +// +// To support future compiler optimizations where which non-volatile registers +// are saved changes within a function (e.g. delay saving non-volatiles until +// necessary), there can by multiple lines in the __compact_unwind table for one +// function, each with a different (non-overlapping) range and each with +// different compact unwind encodings that correspond to the non-volatiles +// saved at that range of the function. +// +// If a particular function is so wacky that there is no compact unwind way +// to encode it, then the compiler can emit traditional DWARF unwind info. +// The runtime will use which ever is available. +// +// Runtime support for compact unwind encodings are only available on 10.6 +// and later. So, the compiler should not generate it when targeting pre-10.6. + + + + +//////////////////////////////////////////////////////////////////////////////// +// +// Final Linked Images: __TEXT,__unwind_info +// +//////////////////////////////////////////////////////////////////////////////// + +// +// The __TEXT,__unwind_info section is laid out for an efficient two level lookup. +// The header of the section contains a coarse index that maps function address +// to the page (4096 byte block) containing the unwind info for that function. +// + +#define UNWIND_SECTION_VERSION 1 +struct unwind_info_section_header +{ + uint32_t version; // UNWIND_SECTION_VERSION + uint32_t commonEncodingsArraySectionOffset; + uint32_t commonEncodingsArrayCount; + uint32_t personalityArraySectionOffset; + uint32_t personalityArrayCount; + uint32_t indexSectionOffset; + uint32_t indexCount; + // compact_unwind_encoding_t[] + // uint32_t personalities[] + // unwind_info_section_header_index_entry[] + // unwind_info_section_header_lsda_index_entry[] +}; + +struct unwind_info_section_header_index_entry +{ + uint32_t functionOffset; + uint32_t secondLevelPagesSectionOffset; // section offset to start of regular or compress page + uint32_t lsdaIndexArraySectionOffset; // section offset to start of lsda_index array for this range +}; + +struct unwind_info_section_header_lsda_index_entry +{ + uint32_t functionOffset; + uint32_t lsdaOffset; +}; + +// +// There are two kinds of second level index pages: regular and compressed. +// A compressed page can hold up to 1021 entries, but it cannot be used +// if too many different encoding types are used. The regular page holds +// 511 entries. +// + +struct unwind_info_regular_second_level_entry +{ + uint32_t functionOffset; + compact_unwind_encoding_t encoding; +}; + +#define UNWIND_SECOND_LEVEL_REGULAR 2 +struct unwind_info_regular_second_level_page_header +{ + uint32_t kind; // UNWIND_SECOND_LEVEL_REGULAR + uint16_t entryPageOffset; + uint16_t entryCount; + // entry array +}; + +#define UNWIND_SECOND_LEVEL_COMPRESSED 3 +struct unwind_info_compressed_second_level_page_header +{ + uint32_t kind; // UNWIND_SECOND_LEVEL_COMPRESSED + uint16_t entryPageOffset; + uint16_t entryCount; + uint16_t encodingsPageOffset; + uint16_t encodingsCount; + // 32-bit entry array + // encodings array +}; + +#define UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET(entry) (entry & 0x00FFFFFF) +#define UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX(entry) ((entry >> 24) & 0xFF) + + + +#endif + diff --git a/shared/sentry/external/crashpad/libunwind/include/unwind.h b/shared/sentry/external/crashpad/libunwind/include/unwind.h new file mode 100644 index 000000000..210140133 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/include/unwind.h @@ -0,0 +1,207 @@ +//===------------------------------- unwind.h -----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// C++ ABI Level 1 ABI documented at: +// https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html +// +//===----------------------------------------------------------------------===// + +#ifndef __UNWIND_H__ +#define __UNWIND_H__ + +#include <__libunwind_config.h> + +#include +#include + +#if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__) && defined(_WIN32) +#include +#include +#endif + +#if defined(__APPLE__) +#define LIBUNWIND_UNAVAIL __attribute__ (( unavailable )) +#else +#define LIBUNWIND_UNAVAIL +#endif + +typedef enum { + _URC_NO_REASON = 0, + _URC_OK = 0, + _URC_FOREIGN_EXCEPTION_CAUGHT = 1, + _URC_FATAL_PHASE2_ERROR = 2, + _URC_FATAL_PHASE1_ERROR = 3, + _URC_NORMAL_STOP = 4, + _URC_END_OF_STACK = 5, + _URC_HANDLER_FOUND = 6, + _URC_INSTALL_CONTEXT = 7, + _URC_CONTINUE_UNWIND = 8, +#if defined(_LIBUNWIND_ARM_EHABI) + _URC_FAILURE = 9 +#endif +} _Unwind_Reason_Code; + +typedef enum { + _UA_SEARCH_PHASE = 1, + _UA_CLEANUP_PHASE = 2, + _UA_HANDLER_FRAME = 4, + _UA_FORCE_UNWIND = 8, + _UA_END_OF_STACK = 16 // gcc extension to C++ ABI +} _Unwind_Action; + +typedef struct _Unwind_Context _Unwind_Context; // opaque + +#if defined(_LIBUNWIND_ARM_EHABI) +#include "unwind_arm_ehabi.h" +#else +#include "unwind_itanium.h" +#endif + +typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn) + (int version, + _Unwind_Action actions, + _Unwind_Exception_Class exceptionClass, + _Unwind_Exception* exceptionObject, + struct _Unwind_Context* context, + void* stop_parameter); + +#ifdef __cplusplus +extern "C" { +#endif + +extern uintptr_t _Unwind_GetRegionStart(struct _Unwind_Context *context); +extern uintptr_t + _Unwind_GetLanguageSpecificData(struct _Unwind_Context *context); +#ifdef __USING_SJLJ_EXCEPTIONS__ +extern _Unwind_Reason_Code + _Unwind_SjLj_ForcedUnwind(_Unwind_Exception *exception_object, + _Unwind_Stop_Fn stop, void *stop_parameter); +#else +extern _Unwind_Reason_Code + _Unwind_ForcedUnwind(_Unwind_Exception *exception_object, + _Unwind_Stop_Fn stop, void *stop_parameter); +#endif + +#ifdef __USING_SJLJ_EXCEPTIONS__ +typedef struct _Unwind_FunctionContext *_Unwind_FunctionContext_t; +extern void _Unwind_SjLj_Register(_Unwind_FunctionContext_t fc); +extern void _Unwind_SjLj_Unregister(_Unwind_FunctionContext_t fc); +#endif + +// +// The following are semi-suppoted extensions to the C++ ABI +// + +// +// called by __cxa_rethrow(). +// +#ifdef __USING_SJLJ_EXCEPTIONS__ +extern _Unwind_Reason_Code + _Unwind_SjLj_Resume_or_Rethrow(_Unwind_Exception *exception_object); +#else +extern _Unwind_Reason_Code + _Unwind_Resume_or_Rethrow(_Unwind_Exception *exception_object); +#endif + +// _Unwind_Backtrace() is a gcc extension that walks the stack and calls the +// _Unwind_Trace_Fn once per frame until it reaches the bottom of the stack +// or the _Unwind_Trace_Fn function returns something other than _URC_NO_REASON. +typedef _Unwind_Reason_Code (*_Unwind_Trace_Fn)(struct _Unwind_Context *, + void *); +extern _Unwind_Reason_Code _Unwind_Backtrace(_Unwind_Trace_Fn, void *); + +// _Unwind_GetCFA is a gcc extension that can be called from within a +// personality handler to get the CFA (stack pointer before call) of +// current frame. +extern uintptr_t _Unwind_GetCFA(struct _Unwind_Context *); + + +// _Unwind_GetIPInfo is a gcc extension that can be called from within a +// personality handler. Similar to _Unwind_GetIP() but also returns in +// *ipBefore a non-zero value if the instruction pointer is at or before the +// instruction causing the unwind. Normally, in a function call, the IP returned +// is the return address which is after the call instruction and may be past the +// end of the function containing the call instruction. +extern uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context *context, + int *ipBefore); + + +// __register_frame() is used with dynamically generated code to register the +// FDE for a generated (JIT) code. The FDE must use pc-rel addressing to point +// to its function and optional LSDA. +// __register_frame() has existed in all versions of Mac OS X, but in 10.4 and +// 10.5 it was buggy and did not actually register the FDE with the unwinder. +// In 10.6 and later it does register properly. +extern void __register_frame(const void *fde); +extern void __deregister_frame(const void *fde); + +// _Unwind_Find_FDE() will locate the FDE if the pc is in some function that has +// an associated FDE. Note, Mac OS X 10.6 and later, introduces "compact unwind +// info" which the runtime uses in preference to DWARF unwind info. This +// function will only work if the target function has an FDE but no compact +// unwind info. +struct dwarf_eh_bases { + uintptr_t tbase; + uintptr_t dbase; + uintptr_t func; +}; +extern const void *_Unwind_Find_FDE(const void *pc, struct dwarf_eh_bases *); + + +// This function attempts to find the start (address of first instruction) of +// a function given an address inside the function. It only works if the +// function has an FDE (DWARF unwind info). +// This function is unimplemented on Mac OS X 10.6 and later. Instead, use +// _Unwind_Find_FDE() and look at the dwarf_eh_bases.func result. +extern void *_Unwind_FindEnclosingFunction(void *pc); + +// Mac OS X does not support text-rel and data-rel addressing so these functions +// are unimplemented +extern uintptr_t _Unwind_GetDataRelBase(struct _Unwind_Context *context) + LIBUNWIND_UNAVAIL; +extern uintptr_t _Unwind_GetTextRelBase(struct _Unwind_Context *context) + LIBUNWIND_UNAVAIL; + +// Mac OS X 10.4 and 10.5 had implementations of these functions in +// libgcc_s.dylib, but they never worked. +/// These functions are no longer available on Mac OS X. +extern void __register_frame_info_bases(const void *fde, void *ob, void *tb, + void *db) LIBUNWIND_UNAVAIL; +extern void __register_frame_info(const void *fde, void *ob) + LIBUNWIND_UNAVAIL; +extern void __register_frame_info_table_bases(const void *fde, void *ob, + void *tb, void *db) + LIBUNWIND_UNAVAIL; +extern void __register_frame_info_table(const void *fde, void *ob) + LIBUNWIND_UNAVAIL; +extern void __register_frame_table(const void *fde) + LIBUNWIND_UNAVAIL; +extern void *__deregister_frame_info(const void *fde) + LIBUNWIND_UNAVAIL; +extern void *__deregister_frame_info_bases(const void *fde) + LIBUNWIND_UNAVAIL; + +#if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__) +#ifndef _WIN32 +typedef struct _EXCEPTION_RECORD EXCEPTION_RECORD; +typedef struct _CONTEXT CONTEXT; +typedef struct _DISPATCHER_CONTEXT DISPATCHER_CONTEXT; +#elif !defined(__MINGW32__) && VER_PRODUCTBUILD < 8000 +typedef struct _DISPATCHER_CONTEXT DISPATCHER_CONTEXT; +#endif +// This is the common wrapper for GCC-style personality functions with SEH. +extern EXCEPTION_DISPOSITION _GCC_specific_handler(EXCEPTION_RECORD *exc, + void *frame, CONTEXT *ctx, + DISPATCHER_CONTEXT *disp, + _Unwind_Personality_Fn pers); +#endif + +#ifdef __cplusplus +} +#endif + +#endif // __UNWIND_H__ diff --git a/shared/sentry/external/crashpad/libunwind/include/unwind_arm_ehabi.h b/shared/sentry/external/crashpad/libunwind/include/unwind_arm_ehabi.h new file mode 100644 index 000000000..747129af5 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/include/unwind_arm_ehabi.h @@ -0,0 +1,169 @@ +//===------------------------------- unwind.h -----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// C++ ABI Level 1 ABI documented at: +// https://github.com/ARM-software/abi-aa/blob/main/ehabi32/ehabi32.rst +// +//===----------------------------------------------------------------------===// + +#ifndef __ARM_EHABI_UNWIND_H__ +#define __ARM_EHABI_UNWIND_H__ + +typedef uint32_t _Unwind_State; + +static const _Unwind_State _US_VIRTUAL_UNWIND_FRAME = 0; +static const _Unwind_State _US_UNWIND_FRAME_STARTING = 1; +static const _Unwind_State _US_UNWIND_FRAME_RESUME = 2; +static const _Unwind_State _US_ACTION_MASK = 3; +/* Undocumented flag for force unwinding. */ +static const _Unwind_State _US_FORCE_UNWIND = 8; + +typedef uint32_t _Unwind_EHT_Header; + +struct _Unwind_Control_Block; +typedef struct _Unwind_Control_Block _Unwind_Control_Block; +#define _Unwind_Exception _Unwind_Control_Block /* Alias */ +typedef uint8_t _Unwind_Exception_Class[8]; + +struct _Unwind_Control_Block { + _Unwind_Exception_Class exception_class; + void (*exception_cleanup)(_Unwind_Reason_Code, _Unwind_Control_Block*); + + /* Unwinder cache, private fields for the unwinder's use */ + struct { + uint32_t reserved1; /* init reserved1 to 0, then don't touch */ + uint32_t reserved2; + uint32_t reserved3; + uint32_t reserved4; + uint32_t reserved5; + } unwinder_cache; + + /* Propagation barrier cache (valid after phase 1): */ + struct { + uint32_t sp; + uint32_t bitpattern[5]; + } barrier_cache; + + /* Cleanup cache (preserved over cleanup): */ + struct { + uint32_t bitpattern[4]; + } cleanup_cache; + + /* Pr cache (for pr's benefit): */ + struct { + uint32_t fnstart; /* function start address */ + _Unwind_EHT_Header* ehtp; /* pointer to EHT entry header word */ + uint32_t additional; + uint32_t reserved1; + } pr_cache; + + long long int :0; /* Enforce the 8-byte alignment */ +} __attribute__((__aligned__(8))); + +typedef _Unwind_Reason_Code (*_Unwind_Personality_Fn)( + _Unwind_State state, _Unwind_Exception *exceptionObject, + struct _Unwind_Context *context); + +#ifdef __cplusplus +extern "C" { +#endif + +// +// The following are the base functions documented by the C++ ABI +// +#ifdef __USING_SJLJ_EXCEPTIONS__ +extern _Unwind_Reason_Code + _Unwind_SjLj_RaiseException(_Unwind_Exception *exception_object); +extern void _Unwind_SjLj_Resume(_Unwind_Exception *exception_object); +#else +extern _Unwind_Reason_Code + _Unwind_RaiseException(_Unwind_Exception *exception_object); +extern void _Unwind_Resume(_Unwind_Exception *exception_object); +#endif +extern void _Unwind_DeleteException(_Unwind_Exception *exception_object); + +typedef enum { + _UVRSC_CORE = 0, /* integer register */ + _UVRSC_VFP = 1, /* vfp */ + _UVRSC_WMMXD = 3, /* Intel WMMX data register */ + _UVRSC_WMMXC = 4 /* Intel WMMX control register */ +} _Unwind_VRS_RegClass; + +typedef enum { + _UVRSD_UINT32 = 0, + _UVRSD_VFPX = 1, + _UVRSD_UINT64 = 3, + _UVRSD_FLOAT = 4, + _UVRSD_DOUBLE = 5 +} _Unwind_VRS_DataRepresentation; + +typedef enum { + _UVRSR_OK = 0, + _UVRSR_NOT_IMPLEMENTED = 1, + _UVRSR_FAILED = 2 +} _Unwind_VRS_Result; + +extern void _Unwind_Complete(_Unwind_Exception* exception_object); + +extern _Unwind_VRS_Result +_Unwind_VRS_Get(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, + uint32_t regno, _Unwind_VRS_DataRepresentation representation, + void *valuep); + +extern _Unwind_VRS_Result +_Unwind_VRS_Set(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, + uint32_t regno, _Unwind_VRS_DataRepresentation representation, + void *valuep); + +extern _Unwind_VRS_Result +_Unwind_VRS_Pop(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, + uint32_t discriminator, + _Unwind_VRS_DataRepresentation representation); + +#if defined(_LIBUNWIND_UNWIND_LEVEL1_EXTERNAL_LINKAGE) +#define _LIBUNWIND_EXPORT_UNWIND_LEVEL1 extern +#else +#define _LIBUNWIND_EXPORT_UNWIND_LEVEL1 static __inline__ +#endif + +// These are de facto helper functions for ARM, which delegate the function +// calls to _Unwind_VRS_Get/Set(). These are not a part of ARM EHABI +// specification, thus these function MUST be inlined. Please don't replace +// these with the "extern" function declaration; otherwise, the program +// including this header won't be ABI compatible and will result in +// link error when we are linking the program with libgcc. + +_LIBUNWIND_EXPORT_UNWIND_LEVEL1 +uintptr_t _Unwind_GetGR(struct _Unwind_Context *context, int index) { + uintptr_t value = 0; + _Unwind_VRS_Get(context, _UVRSC_CORE, (uint32_t)index, _UVRSD_UINT32, &value); + return value; +} + +_LIBUNWIND_EXPORT_UNWIND_LEVEL1 +void _Unwind_SetGR(struct _Unwind_Context *context, int index, + uintptr_t value) { + _Unwind_VRS_Set(context, _UVRSC_CORE, (uint32_t)index, _UVRSD_UINT32, &value); +} + +_LIBUNWIND_EXPORT_UNWIND_LEVEL1 +uintptr_t _Unwind_GetIP(struct _Unwind_Context *context) { + // remove the thumb-bit before returning + return _Unwind_GetGR(context, 15) & (~(uintptr_t)0x1); +} + +_LIBUNWIND_EXPORT_UNWIND_LEVEL1 +void _Unwind_SetIP(struct _Unwind_Context *context, uintptr_t value) { + uintptr_t thumb_bit = _Unwind_GetGR(context, 15) & ((uintptr_t)0x1); + _Unwind_SetGR(context, 15, value | thumb_bit); +} + +#ifdef __cplusplus +} +#endif + +#endif // __ARM_EHABI_UNWIND_H__ diff --git a/shared/sentry/external/crashpad/libunwind/include/unwind_itanium.h b/shared/sentry/external/crashpad/libunwind/include/unwind_itanium.h new file mode 100644 index 000000000..794e990dc --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/include/unwind_itanium.h @@ -0,0 +1,76 @@ +//===------------------------------- unwind.h -----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// C++ ABI Level 1 ABI documented at: +// https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html +// +//===----------------------------------------------------------------------===// + +#ifndef __ITANIUM_UNWIND_H__ +#define __ITANIUM_UNWIND_H__ + +struct _Unwind_Context; // opaque +struct _Unwind_Exception; // forward declaration +typedef struct _Unwind_Exception _Unwind_Exception; +typedef uint64_t _Unwind_Exception_Class; + +struct _Unwind_Exception { + _Unwind_Exception_Class exception_class; + void (*exception_cleanup)(_Unwind_Reason_Code reason, + _Unwind_Exception *exc); +#if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__) + uintptr_t private_[6]; +#else + uintptr_t private_1; // non-zero means forced unwind + uintptr_t private_2; // holds sp that phase1 found for phase2 to use +#endif +#if __SIZEOF_POINTER__ == 4 + // The implementation of _Unwind_Exception uses an attribute mode on the + // above fields which has the side effect of causing this whole struct to + // round up to 32 bytes in size (48 with SEH). To be more explicit, we add + // pad fields added for binary compatibility. + uint32_t reserved[3]; +#endif + // The Itanium ABI requires that _Unwind_Exception objects are "double-word + // aligned". GCC has interpreted this to mean "use the maximum useful + // alignment for the target"; so do we. +} __attribute__((__aligned__)); + +typedef _Unwind_Reason_Code (*_Unwind_Personality_Fn)( + int version, _Unwind_Action actions, uint64_t exceptionClass, + _Unwind_Exception *exceptionObject, struct _Unwind_Context *context); + +#ifdef __cplusplus +extern "C" { +#endif + +// +// The following are the base functions documented by the C++ ABI +// +#ifdef __USING_SJLJ_EXCEPTIONS__ +extern _Unwind_Reason_Code + _Unwind_SjLj_RaiseException(_Unwind_Exception *exception_object); +extern void _Unwind_SjLj_Resume(_Unwind_Exception *exception_object); +#else +extern _Unwind_Reason_Code + _Unwind_RaiseException(_Unwind_Exception *exception_object); +extern void _Unwind_Resume(_Unwind_Exception *exception_object); +#endif +extern void _Unwind_DeleteException(_Unwind_Exception *exception_object); + + +extern uintptr_t _Unwind_GetGR(struct _Unwind_Context *context, int index); +extern void _Unwind_SetGR(struct _Unwind_Context *context, int index, + uintptr_t new_value); +extern uintptr_t _Unwind_GetIP(struct _Unwind_Context *context); +extern void _Unwind_SetIP(struct _Unwind_Context *, uintptr_t new_value); + +#ifdef __cplusplus +} +#endif + +#endif // __ITANIUM_UNWIND_H__ diff --git a/shared/sentry/external/crashpad/libunwind/src/AddressSpace.hpp b/shared/sentry/external/crashpad/libunwind/src/AddressSpace.hpp new file mode 100644 index 000000000..e856f6416 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/src/AddressSpace.hpp @@ -0,0 +1,1072 @@ +//===------------------------- AddressSpace.hpp ---------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Abstracts accessing local vs remote address spaces. +// +//===----------------------------------------------------------------------===// + +#ifndef __ADDRESSSPACE_HPP__ +#define __ADDRESSSPACE_HPP__ + +#include +#include +#include +#include + +#include "libunwind.h" +#include "config.h" +#include "dwarf2.h" +#include "EHHeaderParser.hpp" +#include "Registers.hpp" + +#ifndef _LIBUNWIND_USE_DLADDR + #if !defined(_LIBUNWIND_IS_BAREMETAL) && !defined(_WIN32) + #define _LIBUNWIND_USE_DLADDR 1 + #else + #define _LIBUNWIND_USE_DLADDR 0 + #endif +#endif + +#if _LIBUNWIND_USE_DLADDR +#include +#if defined(__ELF__) && defined(_LIBUNWIND_LINK_DL_LIB) +#pragma comment(lib, "dl") +#endif +#endif + +#if defined(_LIBUNWIND_ARM_EHABI) +struct EHABIIndexEntry { + uint32_t functionOffset; + uint32_t data; +}; +#endif + +#ifdef __APPLE__ + + #include + #include + #include + #include + #include + + struct dyld_unwind_sections + { + const struct mach_header* mh; + const void* dwarf_section; + uintptr_t dwarf_section_length; + const void* compact_unwind_section; + uintptr_t compact_unwind_section_length; + }; + + // In 10.7.0 or later, libSystem.dylib implements this function. + extern "C" bool _dyld_find_unwind_sections(void *, dyld_unwind_sections *); + +#elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && defined(_LIBUNWIND_IS_BAREMETAL) + +// When statically linked on bare-metal, the symbols for the EH table are looked +// up without going through the dynamic loader. + +// The following linker script may be used to produce the necessary sections and symbols. +// Unless the --eh-frame-hdr linker option is provided, the section is not generated +// and does not take space in the output file. +// +// .eh_frame : +// { +// __eh_frame_start = .; +// KEEP(*(.eh_frame)) +// __eh_frame_end = .; +// } +// +// .eh_frame_hdr : +// { +// KEEP(*(.eh_frame_hdr)) +// } +// +// __eh_frame_hdr_start = SIZEOF(.eh_frame_hdr) > 0 ? ADDR(.eh_frame_hdr) : 0; +// __eh_frame_hdr_end = SIZEOF(.eh_frame_hdr) > 0 ? . : 0; + +extern char __eh_frame_start; +extern char __eh_frame_end; + +#if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) +extern char __eh_frame_hdr_start; +extern char __eh_frame_hdr_end; +#endif + +#elif defined(_LIBUNWIND_ARM_EHABI) && defined(_LIBUNWIND_IS_BAREMETAL) + +// When statically linked on bare-metal, the symbols for the EH table are looked +// up without going through the dynamic loader. +extern char __exidx_start; +extern char __exidx_end; + +#elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && defined(_WIN32) + +#include +#include + +#elif defined(_LIBUNWIND_USE_DL_ITERATE_PHDR) || \ + defined(_LIBUNWIND_USE_DL_UNWIND_FIND_EXIDX) + +#include + +#endif + +namespace libunwind { + +/// Used by findUnwindSections() to return info about needed sections. +struct UnwindInfoSections { +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) || \ + defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) || \ + defined(_LIBUNWIND_USE_DL_ITERATE_PHDR) + // No dso_base for SEH. + uintptr_t dso_base; +#endif +#if defined(_LIBUNWIND_USE_DL_ITERATE_PHDR) + uintptr_t text_segment_length; +#endif +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + uintptr_t dwarf_section; + uintptr_t dwarf_section_length; +#endif +#if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) + uintptr_t dwarf_index_section; + uintptr_t dwarf_index_section_length; +#endif +#if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) + uintptr_t compact_unwind_section; + uintptr_t compact_unwind_section_length; +#endif +#if defined(_LIBUNWIND_ARM_EHABI) + uintptr_t arm_section; + uintptr_t arm_section_length; +#endif +}; + + +/// LocalAddressSpace is used as a template parameter to UnwindCursor when +/// unwinding a thread in the same process. The wrappers compile away, +/// making local unwinds fast. +class _LIBUNWIND_HIDDEN LocalAddressSpace { +public: + typedef uintptr_t pint_t; + typedef intptr_t sint_t; + uint8_t get8(pint_t addr) { + uint8_t val; + memcpy(&val, (void *)addr, sizeof(val)); + return val; + } + uint16_t get16(pint_t addr) { + uint16_t val; + memcpy(&val, (void *)addr, sizeof(val)); + return val; + } + uint32_t get32(pint_t addr) { + uint32_t val; + memcpy(&val, (void *)addr, sizeof(val)); + return val; + } + uint64_t get64(pint_t addr) { + uint64_t val; + memcpy(&val, (void *)addr, sizeof(val)); + return val; + } + double getDouble(pint_t addr) { + double val; + memcpy(&val, (void *)addr, sizeof(val)); + return val; + } + v128 getVector(pint_t addr) { + v128 val; + memcpy(&val, (void *)addr, sizeof(val)); + return val; + } + uintptr_t getP(pint_t addr); + uint64_t getRegister(pint_t addr); + static uint64_t getULEB128(pint_t &addr, pint_t end); + static int64_t getSLEB128(pint_t &addr, pint_t end); + + pint_t getEncodedP(pint_t &addr, pint_t end, uint8_t encoding, + pint_t datarelBase = 0); + bool findFunctionName(pint_t addr, char *buf, size_t bufLen, + unw_word_t *offset); + bool findUnwindSections(pint_t targetAddr, UnwindInfoSections &info); + bool findOtherFDE(pint_t targetAddr, pint_t &fde); + + static LocalAddressSpace sThisAddressSpace; +}; + +inline uintptr_t LocalAddressSpace::getP(pint_t addr) { +#if __SIZEOF_POINTER__ == 8 + return get64(addr); +#else + return get32(addr); +#endif +} + +inline uint64_t LocalAddressSpace::getRegister(pint_t addr) { +#if __SIZEOF_POINTER__ == 8 || defined(__mips64) + return get64(addr); +#else + return get32(addr); +#endif +} + +/// Read a ULEB128 into a 64-bit word. +inline uint64_t LocalAddressSpace::getULEB128(pint_t &addr, pint_t end) { + const uint8_t *p = (uint8_t *)addr; + const uint8_t *pend = (uint8_t *)end; + uint64_t result = 0; + int bit = 0; + do { + uint64_t b; + + if (p == pend) + _LIBUNWIND_ABORT("truncated uleb128 expression"); + + b = *p & 0x7f; + + if (bit >= 64 || b << bit >> bit != b) { + _LIBUNWIND_ABORT("malformed uleb128 expression"); + } else { + result |= b << bit; + bit += 7; + } + } while (*p++ >= 0x80); + addr = (pint_t) p; + return result; +} + +/// Read a SLEB128 into a 64-bit word. +inline int64_t LocalAddressSpace::getSLEB128(pint_t &addr, pint_t end) { + const uint8_t *p = (uint8_t *)addr; + const uint8_t *pend = (uint8_t *)end; + int64_t result = 0; + int bit = 0; + uint8_t byte; + do { + if (p == pend) + _LIBUNWIND_ABORT("truncated sleb128 expression"); + byte = *p++; + result |= (uint64_t)(byte & 0x7f) << bit; + bit += 7; + } while (byte & 0x80); + // sign extend negative numbers + if ((byte & 0x40) != 0 && bit < 64) + result |= (-1ULL) << bit; + addr = (pint_t) p; + return result; +} + +inline LocalAddressSpace::pint_t +LocalAddressSpace::getEncodedP(pint_t &addr, pint_t end, uint8_t encoding, + pint_t datarelBase) { + pint_t startAddr = addr; + const uint8_t *p = (uint8_t *)addr; + pint_t result; + + // first get value + switch (encoding & 0x0F) { + case DW_EH_PE_ptr: + result = getP(addr); + p += sizeof(pint_t); + addr = (pint_t) p; + break; + case DW_EH_PE_uleb128: + result = (pint_t)getULEB128(addr, end); + break; + case DW_EH_PE_udata2: + result = get16(addr); + p += 2; + addr = (pint_t) p; + break; + case DW_EH_PE_udata4: + result = get32(addr); + p += 4; + addr = (pint_t) p; + break; + case DW_EH_PE_udata8: + result = (pint_t)get64(addr); + p += 8; + addr = (pint_t) p; + break; + case DW_EH_PE_sleb128: + result = (pint_t)getSLEB128(addr, end); + break; + case DW_EH_PE_sdata2: + // Sign extend from signed 16-bit value. + result = (pint_t)(int16_t)get16(addr); + p += 2; + addr = (pint_t) p; + break; + case DW_EH_PE_sdata4: + // Sign extend from signed 32-bit value. + result = (pint_t)(int32_t)get32(addr); + p += 4; + addr = (pint_t) p; + break; + case DW_EH_PE_sdata8: + result = (pint_t)get64(addr); + p += 8; + addr = (pint_t) p; + break; + default: + _LIBUNWIND_ABORT("unknown pointer encoding"); + } + + // then add relative offset + switch (encoding & 0x70) { + case DW_EH_PE_absptr: + // do nothing + break; + case DW_EH_PE_pcrel: + result += startAddr; + break; + case DW_EH_PE_textrel: + _LIBUNWIND_ABORT("DW_EH_PE_textrel pointer encoding not supported"); + break; + case DW_EH_PE_datarel: + // DW_EH_PE_datarel is only valid in a few places, so the parameter has a + // default value of 0, and we abort in the event that someone calls this + // function with a datarelBase of 0 and DW_EH_PE_datarel encoding. + if (datarelBase == 0) + _LIBUNWIND_ABORT("DW_EH_PE_datarel is invalid with a datarelBase of 0"); + result += datarelBase; + break; + case DW_EH_PE_funcrel: + _LIBUNWIND_ABORT("DW_EH_PE_funcrel pointer encoding not supported"); + break; + case DW_EH_PE_aligned: + _LIBUNWIND_ABORT("DW_EH_PE_aligned pointer encoding not supported"); + break; + default: + _LIBUNWIND_ABORT("unknown pointer encoding"); + break; + } + + if (encoding & DW_EH_PE_indirect) + result = getP(result); + + return result; +} + +#if defined(_LIBUNWIND_USE_DL_ITERATE_PHDR) + +// The ElfW() macro for pointer-size independent ELF header traversal is not +// provided by on some systems (e.g., FreeBSD). On these systems the +// data structures are just called Elf_XXX. Define ElfW() locally. +#if !defined(ElfW) + #define ElfW(type) Elf_##type +#endif +#if !defined(Elf_Half) + typedef ElfW(Half) Elf_Half; +#endif +#if !defined(Elf_Phdr) + typedef ElfW(Phdr) Elf_Phdr; +#endif +#if !defined(Elf_Addr) + typedef ElfW(Addr) Elf_Addr; +#endif + +static Elf_Addr calculateImageBase(struct dl_phdr_info *pinfo) { + Elf_Addr image_base = pinfo->dlpi_addr; +#if defined(__ANDROID__) && __ANDROID_API__ < 18 + if (image_base == 0) { + // Normally, an image base of 0 indicates a non-PIE executable. On + // versions of Android prior to API 18, the dynamic linker reported a + // dlpi_addr of 0 for PIE executables. Compute the true image base + // using the PT_PHDR segment. + // See https://github.com/android/ndk/issues/505. + for (Elf_Half i = 0; i < pinfo->dlpi_phnum; i++) { + const Elf_Phdr *phdr = &pinfo->dlpi_phdr[i]; + if (phdr->p_type == PT_PHDR) { + image_base = reinterpret_cast(pinfo->dlpi_phdr) - + phdr->p_vaddr; + break; + } + } + } +#endif + return image_base; +} + +struct _LIBUNWIND_HIDDEN dl_iterate_cb_data { + LocalAddressSpace *addressSpace; + UnwindInfoSections *sects; + uintptr_t targetAddr; +}; + +#if defined(_LIBUNWIND_USE_FRAME_HEADER_CACHE) +#include "FrameHeaderCache.hpp" + +// Typically there is one cache per process, but when libunwind is built as a +// hermetic static library, then each shared object may have its own cache. +static FrameHeaderCache TheFrameHeaderCache; +#endif + +static bool checkAddrInSegment(const Elf_Phdr *phdr, size_t image_base, + dl_iterate_cb_data *cbdata) { + if (phdr->p_type == PT_LOAD) { + uintptr_t begin = image_base + phdr->p_vaddr; + uintptr_t end = begin + phdr->p_memsz; + if (cbdata->targetAddr >= begin && cbdata->targetAddr < end) { + cbdata->sects->dso_base = begin; + cbdata->sects->text_segment_length = phdr->p_memsz; + return true; + } + } + return false; +} + +static bool checkForUnwindInfoSegment(const Elf_Phdr *phdr, size_t image_base, + dl_iterate_cb_data *cbdata) { +#if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) + if (phdr->p_type == PT_GNU_EH_FRAME) { + EHHeaderParser::EHHeaderInfo hdrInfo; + uintptr_t eh_frame_hdr_start = image_base + phdr->p_vaddr; + cbdata->sects->dwarf_index_section = eh_frame_hdr_start; + cbdata->sects->dwarf_index_section_length = phdr->p_memsz; + if (EHHeaderParser::decodeEHHdr( + *cbdata->addressSpace, eh_frame_hdr_start, phdr->p_memsz, + hdrInfo)) { + // .eh_frame_hdr records the start of .eh_frame, but not its size. + // Rely on a zero terminator to find the end of the section. + cbdata->sects->dwarf_section = hdrInfo.eh_frame_ptr; + cbdata->sects->dwarf_section_length = UINTPTR_MAX; + return true; + } + } + return false; +#elif defined(_LIBUNWIND_ARM_EHABI) + if (phdr->p_type == PT_ARM_EXIDX) { + uintptr_t exidx_start = image_base + phdr->p_vaddr; + cbdata->sects->arm_section = exidx_start; + cbdata->sects->arm_section_length = phdr->p_memsz; + return true; + } + return false; +#else +#error Need one of _LIBUNWIND_SUPPORT_DWARF_INDEX or _LIBUNWIND_ARM_EHABI +#endif +} + +static int findUnwindSectionsByPhdr(struct dl_phdr_info *pinfo, + size_t pinfo_size, void *data) { + auto cbdata = static_cast(data); + if (pinfo->dlpi_phnum == 0 || cbdata->targetAddr < pinfo->dlpi_addr) + return 0; +#if defined(_LIBUNWIND_USE_FRAME_HEADER_CACHE) + if (TheFrameHeaderCache.find(pinfo, pinfo_size, data)) + return 1; +#else + // Avoid warning about unused variable. + (void)pinfo_size; +#endif + + Elf_Addr image_base = calculateImageBase(pinfo); + + // Most shared objects seen in this callback function likely don't contain the + // target address, so optimize for that. Scan for a matching PT_LOAD segment + // first and bail when it isn't found. + bool found_text = false; + for (Elf_Half i = 0; i < pinfo->dlpi_phnum; ++i) { + if (checkAddrInSegment(&pinfo->dlpi_phdr[i], image_base, cbdata)) { + found_text = true; + break; + } + } + if (!found_text) + return 0; + + // PT_GNU_EH_FRAME and PT_ARM_EXIDX are usually near the end. Iterate + // backward. + bool found_unwind = false; + for (Elf_Half i = pinfo->dlpi_phnum; i > 0; i--) { + const Elf_Phdr *phdr = &pinfo->dlpi_phdr[i - 1]; + if (checkForUnwindInfoSegment(phdr, image_base, cbdata)) { + found_unwind = true; + break; + } + } + if (!found_unwind) + return 0; + +#if defined(_LIBUNWIND_USE_FRAME_HEADER_CACHE) + TheFrameHeaderCache.add(cbdata->sects); +#endif + return 1; +} + +#endif // defined(_LIBUNWIND_USE_DL_ITERATE_PHDR) + + +inline bool LocalAddressSpace::findUnwindSections(pint_t targetAddr, + UnwindInfoSections &info) { +#ifdef __APPLE__ + dyld_unwind_sections dyldInfo; + if (_dyld_find_unwind_sections((void *)targetAddr, &dyldInfo)) { + info.dso_base = (uintptr_t)dyldInfo.mh; + #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + info.dwarf_section = (uintptr_t)dyldInfo.dwarf_section; + info.dwarf_section_length = dyldInfo.dwarf_section_length; + #endif + info.compact_unwind_section = (uintptr_t)dyldInfo.compact_unwind_section; + info.compact_unwind_section_length = dyldInfo.compact_unwind_section_length; + return true; + } +#elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && defined(_LIBUNWIND_IS_BAREMETAL) + info.dso_base = 0; + // Bare metal is statically linked, so no need to ask the dynamic loader + info.dwarf_section_length = (uintptr_t)(&__eh_frame_end - &__eh_frame_start); + info.dwarf_section = (uintptr_t)(&__eh_frame_start); + _LIBUNWIND_TRACE_UNWINDING("findUnwindSections: section %p length %p", + (void *)info.dwarf_section, (void *)info.dwarf_section_length); +#if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) + info.dwarf_index_section = (uintptr_t)(&__eh_frame_hdr_start); + info.dwarf_index_section_length = (uintptr_t)(&__eh_frame_hdr_end - &__eh_frame_hdr_start); + _LIBUNWIND_TRACE_UNWINDING("findUnwindSections: index section %p length %p", + (void *)info.dwarf_index_section, (void *)info.dwarf_index_section_length); +#endif + if (info.dwarf_section_length) + return true; +#elif defined(_LIBUNWIND_ARM_EHABI) && defined(_LIBUNWIND_IS_BAREMETAL) + // Bare metal is statically linked, so no need to ask the dynamic loader + info.arm_section = (uintptr_t)(&__exidx_start); + info.arm_section_length = (uintptr_t)(&__exidx_end - &__exidx_start); + _LIBUNWIND_TRACE_UNWINDING("findUnwindSections: section %p length %p", + (void *)info.arm_section, (void *)info.arm_section_length); + if (info.arm_section && info.arm_section_length) + return true; +#elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) && defined(_WIN32) + HMODULE mods[1024]; + HANDLE process = GetCurrentProcess(); + DWORD needed; + + if (!EnumProcessModules(process, mods, sizeof(mods), &needed)) { + DWORD err = GetLastError(); + _LIBUNWIND_TRACE_UNWINDING("findUnwindSections: EnumProcessModules failed, " + "returned error %d", (int)err); + return false; + } + + for (unsigned i = 0; i < (needed / sizeof(HMODULE)); i++) { + PIMAGE_DOS_HEADER pidh = (PIMAGE_DOS_HEADER)mods[i]; + PIMAGE_NT_HEADERS pinh = (PIMAGE_NT_HEADERS)((BYTE *)pidh + pidh->e_lfanew); + PIMAGE_FILE_HEADER pifh = (PIMAGE_FILE_HEADER)&pinh->FileHeader; + PIMAGE_SECTION_HEADER pish = IMAGE_FIRST_SECTION(pinh); + bool found_obj = false; + bool found_hdr = false; + + info.dso_base = (uintptr_t)mods[i]; + for (unsigned j = 0; j < pifh->NumberOfSections; j++, pish++) { + uintptr_t begin = pish->VirtualAddress + (uintptr_t)mods[i]; + uintptr_t end = begin + pish->Misc.VirtualSize; + if (!strncmp((const char *)pish->Name, ".text", + IMAGE_SIZEOF_SHORT_NAME)) { + if (targetAddr >= begin && targetAddr < end) + found_obj = true; + } else if (!strncmp((const char *)pish->Name, ".eh_frame", + IMAGE_SIZEOF_SHORT_NAME)) { + info.dwarf_section = begin; + info.dwarf_section_length = pish->Misc.VirtualSize; + found_hdr = true; + } + if (found_obj && found_hdr) + return true; + } + } + return false; +#elif defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) && defined(_WIN32) + // Don't even bother, since Windows has functions that do all this stuff + // for us. + (void)targetAddr; + (void)info; + return true; +#elif defined(_LIBUNWIND_USE_DL_UNWIND_FIND_EXIDX) + int length = 0; + info.arm_section = + (uintptr_t)dl_unwind_find_exidx((_Unwind_Ptr)targetAddr, &length); + info.arm_section_length = (uintptr_t)length * sizeof(EHABIIndexEntry); + if (info.arm_section && info.arm_section_length) + return true; +#elif defined(_LIBUNWIND_USE_DL_ITERATE_PHDR) + dl_iterate_cb_data cb_data = {this, &info, targetAddr}; + int found = dl_iterate_phdr(findUnwindSectionsByPhdr, &cb_data); + return static_cast(found); +#endif + + return false; +} + + +inline bool LocalAddressSpace::findOtherFDE(pint_t targetAddr, pint_t &fde) { + // TO DO: if OS has way to dynamically register FDEs, check that. + (void)targetAddr; + (void)fde; + return false; +} + +inline bool LocalAddressSpace::findFunctionName(pint_t addr, char *buf, + size_t bufLen, + unw_word_t *offset) { +#if _LIBUNWIND_USE_DLADDR + Dl_info dyldInfo; + if (dladdr((void *)addr, &dyldInfo)) { + if (dyldInfo.dli_sname != NULL) { + snprintf(buf, bufLen, "%s", dyldInfo.dli_sname); + *offset = (addr - (pint_t) dyldInfo.dli_saddr); + return true; + } + } +#else + (void)addr; + (void)buf; + (void)bufLen; + (void)offset; +#endif + return false; +} + +struct found_mach_info { + struct mach_header_64 header; + struct segment_command_64 segment; + uintptr_t ptr_after_segment; + uintptr_t load_addr; + uintptr_t slide; + uintptr_t text_size; + bool header_valid; + bool segment_valid; +}; + +/// RemoteAddressSpace is used as a template parameter to UnwindCursor when +/// unwinding a thread in the another process. +/// In theory, the other process can be a different endianness and a different +/// pointer size which was handled by the P template parameter in the original +/// implementation. +/// However, we assume that we are only dealing with x64 and arm64 here, which +/// have both the same endianness and pointer size. +class RemoteAddressSpace { +public: + RemoteAddressSpace(task_t task) : task_(task), last_found_image(found_mach_info()) {} + static void *operator new(size_t, RemoteAddressSpace *p) { return p; } + + typedef uintptr_t pint_t; + typedef intptr_t sint_t; + uint8_t get8(pint_t addr) { + uint8_t val = 0; + memcpy_from_remote(&val, (void *)addr, sizeof(val)); + return val; + } + uint16_t get16(pint_t addr) { + uint16_t val = 0; + memcpy_from_remote(&val, (void *)addr, sizeof(val)); + return val; + } + uint32_t get32(pint_t addr) { + uint32_t val = 0; + memcpy_from_remote(&val, (void *)addr, sizeof(val)); + return val; + } + uint64_t get64(pint_t addr) { + uint64_t val = 0; + memcpy_from_remote(&val, (void *)addr, sizeof(val)); + return val; + } + double getDouble(pint_t addr) { + double val = 0; + memcpy_from_remote(&val, (void *)addr, sizeof(val)); + return val; + } + v128 getVector(pint_t addr) { + v128 val = {0}; + memcpy_from_remote(&val, (void *)addr, sizeof(val)); + return val; + } + + uintptr_t getP(pint_t addr) { + return get64(addr); + } + uint64_t getRegister(pint_t addr) { + return get64(addr); + } + + uint64_t getULEB128(pint_t &addr, pint_t end); + int64_t getSLEB128(pint_t &addr, pint_t end); + + pint_t getEncodedP(pint_t &addr, pint_t end, uint8_t encoding, + pint_t datarelBase = 0); + bool findFunctionName(pint_t addr, char *buf, size_t bufLen, + unw_word_t *offset); + bool findUnwindSections(pint_t targetAddr, UnwindInfoSections &info); + bool findOtherFDE(pint_t targetAddr, pint_t &fde); + +private: + kern_return_t memcpy_from_remote(void *dest, void *src, size_t size); + + // Finds the mach image that contains `targetAddr`, and saves it and the + // corresponding `segment` in the local `last_found_image`, returning `true` + // on success. + bool findMachSegment(pint_t targetAddr, const char *segment); + // Similar to the above, except it assumes the `header` of `last_found_image` + // is valid. + bool findMachSegmentInImage(pint_t targetAddr, const char *segment); + + task_t task_; + found_mach_info last_found_image; +}; + +uint64_t RemoteAddressSpace::getULEB128(pint_t &addr, pint_t end) { + uintptr_t size = (end - addr); + char buf[16] = {0}; + memcpy_from_remote(buf, (void *)addr, 16); + LocalAddressSpace::pint_t laddr = (LocalAddressSpace::pint_t)buf; + LocalAddressSpace::pint_t sladdr = laddr; + uint64_t result = LocalAddressSpace::getULEB128(laddr, laddr + size); + addr += (laddr - sladdr); + return result; +} + +int64_t RemoteAddressSpace::getSLEB128(pint_t &addr, pint_t end) { + uintptr_t size = (end - addr); + char buf[16] = {0}; + memcpy_from_remote(buf, (void *)addr, 16); + LocalAddressSpace::pint_t laddr = + (LocalAddressSpace::pint_t)buf; + LocalAddressSpace::pint_t sladdr = laddr; + int64_t result = LocalAddressSpace::getSLEB128(laddr, laddr + size); + addr += (laddr - sladdr); + return result; +} + +kern_return_t RemoteAddressSpace::memcpy_from_remote(void *dest, void *src, size_t size) { + size_t read_bytes = 0; + kern_return_t kr = mach_vm_read_overwrite( + task_, (mach_vm_address_t)src, (mach_vm_size_t)size, + (mach_vm_address_t)dest, (mach_vm_size_t *)&read_bytes); + return kr; +} + +// we needed to copy this whole function since we can’t reuse the one from +// `LocalAddressSpace`. :-( +RemoteAddressSpace::pint_t +RemoteAddressSpace::getEncodedP(pint_t &addr, pint_t end, uint8_t encoding, + pint_t datarelBase) { + pint_t startAddr = addr; + const uint8_t *p = (uint8_t *)addr; + pint_t result; + + // first get value + switch (encoding & 0x0F) { + case DW_EH_PE_ptr: + result = getP(addr); + p += sizeof(pint_t); + addr = (pint_t)p; + break; + case DW_EH_PE_uleb128: + result = (pint_t)getULEB128(addr, end); + break; + case DW_EH_PE_udata2: + result = get16(addr); + p += 2; + addr = (pint_t)p; + break; + case DW_EH_PE_udata4: + result = get32(addr); + p += 4; + addr = (pint_t)p; + break; + case DW_EH_PE_udata8: + result = (pint_t)get64(addr); + p += 8; + addr = (pint_t)p; + break; + case DW_EH_PE_sleb128: + result = (pint_t)getSLEB128(addr, end); + break; + case DW_EH_PE_sdata2: + // Sign extend from signed 16-bit value. + result = (pint_t)(int16_t)get16(addr); + p += 2; + addr = (pint_t)p; + break; + case DW_EH_PE_sdata4: + // Sign extend from signed 32-bit value. + result = (pint_t)(int32_t)get32(addr); + p += 4; + addr = (pint_t)p; + break; + case DW_EH_PE_sdata8: + result = (pint_t)get64(addr); + p += 8; + addr = (pint_t)p; + break; + default: + _LIBUNWIND_ABORT("unknown pointer encoding"); + } + + // then add relative offset + switch (encoding & 0x70) { + case DW_EH_PE_absptr: + // do nothing + break; + case DW_EH_PE_pcrel: + result += startAddr; + break; + case DW_EH_PE_textrel: + _LIBUNWIND_ABORT("DW_EH_PE_textrel pointer encoding not supported"); + break; + case DW_EH_PE_datarel: + // DW_EH_PE_datarel is only valid in a few places, so the parameter has a + // default value of 0, and we abort in the event that someone calls this + // function with a datarelBase of 0 and DW_EH_PE_datarel encoding. + if (datarelBase == 0) + _LIBUNWIND_ABORT("DW_EH_PE_datarel is invalid with a datarelBase of 0"); + result += datarelBase; + break; + case DW_EH_PE_funcrel: + _LIBUNWIND_ABORT("DW_EH_PE_funcrel pointer encoding not supported"); + break; + case DW_EH_PE_aligned: + _LIBUNWIND_ABORT("DW_EH_PE_aligned pointer encoding not supported"); + break; + default: + _LIBUNWIND_ABORT("unknown pointer encoding"); + break; + } + + if (encoding & DW_EH_PE_indirect) + result = getP(result); + + return result; +} + +bool RemoteAddressSpace::findMachSegment(pint_t targetAddr, + const char *segment) { + if (!last_found_image.header_valid || !(last_found_image.load_addr <= targetAddr && last_found_image.load_addr + last_found_image.text_size > targetAddr)) { + last_found_image.segment_valid = false; + // enumerate all images and find the one we are looking for. + + task_dyld_info_data_t task_dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + if (task_info(task_, TASK_DYLD_INFO, (task_info_t)&task_dyld_info, + &count) != KERN_SUCCESS) { + return false; + } + if (task_dyld_info.all_image_info_format != TASK_DYLD_ALL_IMAGE_INFO_64) { + return false; + } + + dyld_all_image_infos all_images_info; + if (memcpy_from_remote(&all_images_info, + (void *)task_dyld_info.all_image_info_addr, + sizeof(dyld_all_image_infos)) != KERN_SUCCESS) { + return false; + }; + + for (size_t i = 0; i < all_images_info.infoArrayCount; i++) { + dyld_image_info image; + if (memcpy_from_remote(&image, (void *)&all_images_info.infoArray[i], + sizeof(dyld_image_info)) != KERN_SUCCESS) { + continue; + }; + + // image is out of range of `targetAddr` + if ((pint_t)image.imageLoadAddress > targetAddr) { + continue; + } + + if (memcpy_from_remote(&last_found_image.header, + (void *)image.imageLoadAddress, + sizeof(struct mach_header_64)) != KERN_SUCCESS) { + continue; + }; + + if (last_found_image.header.magic != MH_MAGIC_64) { + continue; + } + + last_found_image.load_addr = (size_t)image.imageLoadAddress; + if (findMachSegmentInImage(targetAddr, "__TEXT")) { + last_found_image.header_valid = true; + break; + } + } + } + + if (!last_found_image.header_valid) { + return false; + } + + if (!last_found_image.segment_valid || + strcmp(last_found_image.segment.segname, segment) != 0) { + // search for the segment in the image + if (!findMachSegmentInImage(targetAddr, segment)) { + return false; + } + } + return true; +} + +bool RemoteAddressSpace::findMachSegmentInImage(pint_t targetAddr, const char*segment) { + // This section here is basically a remote-rewrite of + // `dyld_exceptions_init` from: + // https://opensource.apple.com/source/dyld/dyld-195.6/src/dyldExceptions.c.auto.html + struct load_command cmd; + pint_t cmd_ptr = + (pint_t)last_found_image.load_addr + sizeof(struct mach_header_64); + bool found_text = false; + bool found_searched = false; + for (size_t c = 0; c < last_found_image.header.ncmds; c++) { + if (memcpy_from_remote(&cmd, (void *)cmd_ptr, + sizeof(struct load_command)) != KERN_SUCCESS) { + return false; + }; + if (cmd.cmd == LC_SEGMENT_64) { + struct segment_command_64 seg; + if (memcpy_from_remote(&seg, (void *)cmd_ptr, + sizeof(struct segment_command_64)) != + KERN_SUCCESS) { + return false; + }; + + if (strcmp(seg.segname, "__TEXT") == 0) { + pint_t slide = last_found_image.load_addr - seg.vmaddr; + + // text section out of range of `targetAddr` + pint_t text_end = seg.vmaddr + seg.vmsize + slide; + if (text_end < targetAddr) { + return false; + } + last_found_image.slide = slide; + last_found_image.text_size = seg.vmsize; + found_text = true; + } + if (strncmp(seg.segname, segment, 16) == 0) { + pint_t sect_ptr = cmd_ptr + sizeof(struct segment_command_64); + last_found_image.segment_valid = true; + last_found_image.segment = seg; + last_found_image.ptr_after_segment = sect_ptr; + found_searched = true; + } + if (found_text && found_searched) { + return true; + } + } + cmd_ptr += cmd.cmdsize; + } + return false; +} + +inline bool RemoteAddressSpace::findUnwindSections(pint_t targetAddr, + UnwindInfoSections &info) { + if (!findMachSegment(targetAddr, "__TEXT")) { + return false; + } + + info.dso_base = last_found_image.load_addr; + info.dwarf_section = 0; + info.compact_unwind_section = 0; + + for (size_t s = 0; s < last_found_image.segment.nsects; s++) { + struct section_64 sect; + if (memcpy_from_remote(§, + (void *)(last_found_image.ptr_after_segment + + s * sizeof(struct section_64)), + sizeof(struct section_64)) != KERN_SUCCESS) { + continue; + }; + + if (strcmp(sect.sectname, "__eh_frame") == 0) { + info.dwarf_section = sect.addr + last_found_image.slide; + info.dwarf_section_length = sect.size; + } else if (strcmp(sect.sectname, "__unwind_info") == 0) { + info.compact_unwind_section = sect.addr + last_found_image.slide; + info.compact_unwind_section_length = sect.size; + } + } + return true; +} + +bool RemoteAddressSpace::findOtherFDE(pint_t targetAddr, pint_t & fde) { + // TO DO: if OS has way to dynamically register FDEs, check that. + (void)targetAddr; + (void)fde; + return false; +} + +bool RemoteAddressSpace::findFunctionName(pint_t addr, char *buf, + size_t bufLen, unw_word_t *offset) { + // This is essentially a remote re-implementation of this snippet: + // https://gist.github.com/integeruser/b0d3ea6c4e8387d036acf6c77c0ec406 + + if (!findMachSegment(addr, "__TEXT")) { + return false; + } + + struct load_command cmd; + pint_t cmd_ptr = + (pint_t)last_found_image.load_addr + sizeof(struct mach_header_64); + for (size_t c = 0; c < last_found_image.header.ncmds; c++) { + if (memcpy_from_remote(&cmd, (void *)cmd_ptr, + sizeof(struct load_command)) != KERN_SUCCESS) { + return false; + }; + + if (cmd.cmd == LC_SYMTAB) { + struct symtab_command seg; + if (memcpy_from_remote(&seg, (void *)cmd_ptr, + sizeof(struct symtab_command)) != KERN_SUCCESS) { + return false; + }; + + pint_t strtab = last_found_image.load_addr + seg.stroff; + pint_t nearest_sym = 0; + for (size_t s = 0; s < seg.nsyms; s++) { + struct nlist_64 nlist; + if (memcpy_from_remote(&nlist, + (void *)(last_found_image.load_addr + + seg.symoff + + s * sizeof(struct nlist_64)), + sizeof(struct nlist_64)) != KERN_SUCCESS) { + return false; + }; + + if ((nlist.n_type & N_STAB) != 0 || (nlist.n_type & N_TYPE) != N_SECT || + nlist.n_un.n_strx == 0) { + continue; + } + + pint_t sym_addr = nlist.n_value + last_found_image.slide; + if (sym_addr > nearest_sym && sym_addr < addr) { + pint_t symbol_start = strtab + nlist.n_un.n_strx; + pint_t bytes_to_copy = strtab + seg.strsize - symbol_start; + if (bytes_to_copy > bufLen) { + bytes_to_copy = bufLen; + } + if (memcpy_from_remote(buf, (void *)(symbol_start), bytes_to_copy) != + KERN_SUCCESS) { + return false; + } + buf[bufLen - 1] = '\0'; + nearest_sym = sym_addr; + } + } + if (nearest_sym > 0) { + return true; + } + break; + } + cmd_ptr += cmd.cmdsize; + } + + (void)offset; + return false; +} + +} // namespace libunwind + +#endif // __ADDRESSSPACE_HPP__ diff --git a/shared/sentry/external/crashpad/libunwind/src/CMakeLists.txt b/shared/sentry/external/crashpad/libunwind/src/CMakeLists.txt new file mode 100644 index 000000000..ce3217fa8 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/src/CMakeLists.txt @@ -0,0 +1,211 @@ +# Get sources + +set(LIBUNWIND_CXX_SOURCES + libunwind.cpp + Unwind-EHABI.cpp + Unwind-seh.cpp + ) +if(APPLE) + list(APPEND LIBUNWIND_CXX_SOURCES + Unwind_AppleExtras.cpp + ) +endif() + +set(LIBUNWIND_C_SOURCES + UnwindLevel1.c + UnwindLevel1-gcc-ext.c + Unwind-sjlj.c + ) +set_source_files_properties(${LIBUNWIND_C_SOURCES} + PROPERTIES + COMPILE_FLAGS "-std=c99") + +set(LIBUNWIND_ASM_SOURCES + UnwindRegistersRestore.S + UnwindRegistersSave.S + ) + +# See add_asm_sources() in compiler-rt for explanation of this workaround. +if((APPLE AND CMAKE_VERSION VERSION_LESS 3.19) OR (MINGW AND CMAKE_VERSION VERSION_LESS 3.17)) + set_source_files_properties(${LIBUNWIND_ASM_SOURCES} PROPERTIES LANGUAGE C) +endif() + +set(LIBUNWIND_HEADERS + AddressSpace.hpp + assembly.h + CompactUnwinder.hpp + cet_unwind.h + config.h + dwarf2.h + DwarfInstructions.hpp + DwarfParser.hpp + EHHeaderParser.hpp + FrameHeaderCache.hpp + libunwind_ext.h + Registers.hpp + RWMutex.hpp + Unwind-EHABI.h + UnwindCursor.hpp + ../include/libunwind.h + ../include/unwind.h + ../include/unwind_itanium.h + ../include/unwind_arm_ehabi.h + ) +if(APPLE) + list(APPEND LIBUNWIND_HEADERS + ../include/mach-o/compact_unwind_encoding.h + ) +endif() + +if (MSVC_IDE) + # Force them all into the headers dir on MSVC, otherwise they end up at + # project scope because they don't have extensions. + source_group("Header Files" FILES ${LIBUNWIND_HEADERS}) +endif() + +set(LIBUNWIND_SOURCES + ${LIBUNWIND_CXX_SOURCES} + ${LIBUNWIND_C_SOURCES} + ${LIBUNWIND_ASM_SOURCES}) + +# Generate library list. +add_library_flags_if(LIBUNWIND_HAS_C_LIB c) +if (LIBUNWIND_USE_COMPILER_RT) + add_library_flags("${LIBUNWIND_BUILTINS_LIBRARY}") +else() + add_library_flags_if(LIBUNWIND_HAS_GCC_S_LIB gcc_s) + add_library_flags_if(LIBUNWIND_HAS_GCC_LIB gcc) +endif() +add_library_flags_if(LIBUNWIND_HAS_DL_LIB dl) +if (LIBUNWIND_ENABLE_THREADS) + add_library_flags_if(LIBUNWIND_HAS_PTHREAD_LIB pthread) + add_compile_flags_if(LIBUNWIND_WEAK_PTHREAD_LIB -DLIBUNWIND_USE_WEAK_PTHREAD=1) +endif() + +# Setup flags. +if (LIBUNWIND_SUPPORTS_NOSTDLIBXX_FLAG) + add_link_flags_if_supported(-nostdlib++) +else() + add_link_flags_if_supported(-nodefaultlibs) +endif() + +# MINGW_LIBRARIES is defined in config-ix.cmake +add_library_flags_if(MINGW "${MINGW_LIBRARIES}") + +if (LIBUNWIND_ENABLE_SHARED AND + NOT (LIBUNWIND_SUPPORTS_FNO_EXCEPTIONS_FLAG AND + LIBUNWIND_SUPPORTS_FUNWIND_TABLES_FLAG)) + message(FATAL_ERROR + "Compiler doesn't support generation of unwind tables if exception " + "support is disabled. Building libunwind DSO with runtime dependency " + "on C++ ABI library is not supported.") +endif() + +if (APPLE) + add_compile_flags("-U__STRICT_ANSI__") + add_link_flags("-compatibility_version 1" "-install_name /usr/lib/libunwind.1.dylib") + + if (CMAKE_OSX_DEPLOYMENT_TARGET STREQUAL "10.6") + add_link_flags("-current_version ${LIBUNWIND_VERSION}" "/usr/lib/libSystem.B.dylib") + endif () +endif () + +string(REPLACE ";" " " LIBUNWIND_COMPILE_FLAGS "${LIBUNWIND_COMPILE_FLAGS}") +string(REPLACE ";" " " LIBUNWIND_CXX_FLAGS "${LIBUNWIND_CXX_FLAGS}") +string(REPLACE ";" " " LIBUNWIND_C_FLAGS "${LIBUNWIND_C_FLAGS}") +string(REPLACE ";" " " LIBUNWIND_LINK_FLAGS "${LIBUNWIND_LINK_FLAGS}") + +set_property(SOURCE ${LIBUNWIND_CXX_SOURCES} + APPEND_STRING PROPERTY COMPILE_FLAGS " ${LIBUNWIND_CXX_FLAGS}") +set_property(SOURCE ${LIBUNWIND_C_SOURCES} + APPEND_STRING PROPERTY COMPILE_FLAGS " ${LIBUNWIND_C_FLAGS}") + +# NOTE: avoid implicit dependencies on C++ runtimes. libunwind uses C++ for +# ease, but does not rely on C++ at runtime. +set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "") + +# Build the shared library. +if (LIBUNWIND_ENABLE_SHARED) + add_library(unwind_shared SHARED ${LIBUNWIND_SOURCES} ${LIBUNWIND_HEADERS}) + if(CMAKE_C_COMPILER_ID STREQUAL MSVC) + target_compile_options(unwind_shared PRIVATE /GR-) + else() + target_compile_options(unwind_shared PRIVATE -fno-rtti) + endif() + target_link_libraries(unwind_shared PRIVATE ${LIBUNWIND_LIBRARIES}) + set_target_properties(unwind_shared + PROPERTIES + CXX_EXTENSIONS OFF + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED ON + COMPILE_FLAGS "${LIBUNWIND_COMPILE_FLAGS}" + LINK_FLAGS "${LIBUNWIND_LINK_FLAGS}" + LINKER_LANGUAGE C + OUTPUT_NAME "unwind" + VERSION "1.0" + SOVERSION "1" + POSITION_INDEPENDENT_CODE ON + ) + list(APPEND LIBUNWIND_BUILD_TARGETS "unwind_shared") + if (LIBUNWIND_INSTALL_SHARED_LIBRARY) + list(APPEND LIBUNWIND_INSTALL_TARGETS "unwind_shared") + endif() +endif() + +# Build the static library. +if (LIBUNWIND_ENABLE_STATIC) + add_library(unwind_static STATIC ${LIBUNWIND_SOURCES} ${LIBUNWIND_HEADERS}) + if(CMAKE_C_COMPILER_ID STREQUAL MSVC) + target_compile_options(unwind_static PRIVATE /GR-) + else() + target_compile_options(unwind_static PRIVATE -fno-rtti) + endif() + target_link_libraries(unwind_static PRIVATE ${LIBUNWIND_LIBRARIES}) + set_target_properties(unwind_static + PROPERTIES + CXX_EXTENSIONS OFF + CXX_STANDARD 11 + CXX_STANDARD_REQUIRED ON + COMPILE_FLAGS "${LIBUNWIND_COMPILE_FLAGS}" + LINK_FLAGS "${LIBUNWIND_LINK_FLAGS}" + LINKER_LANGUAGE C + OUTPUT_NAME "unwind" + POSITION_INDEPENDENT_CODE ON + ) + + if(LIBUNWIND_HIDE_SYMBOLS) + append_flags_if_supported(UNWIND_STATIC_LIBRARY_FLAGS -fvisibility=hidden) + append_flags_if_supported(UNWIND_STATIC_LIBRARY_FLAGS -fvisibility-global-new-delete-hidden) + target_compile_options(unwind_static PRIVATE ${UNWIND_STATIC_LIBRARY_FLAGS}) + target_compile_definitions(unwind_static PRIVATE _LIBUNWIND_HIDE_SYMBOLS) + endif() + + list(APPEND LIBUNWIND_BUILD_TARGETS "unwind_static") + if (LIBUNWIND_INSTALL_STATIC_LIBRARY) + list(APPEND LIBUNWIND_INSTALL_TARGETS "unwind_static") + endif() +endif() + +# Add a meta-target for both libraries. +add_custom_target(unwind DEPENDS ${LIBUNWIND_BUILD_TARGETS}) + +if (LIBUNWIND_INSTALL_LIBRARY) + install(TARGETS ${LIBUNWIND_INSTALL_TARGETS} + LIBRARY DESTINATION ${LIBUNWIND_INSTALL_LIBRARY_DIR} COMPONENT unwind + ARCHIVE DESTINATION ${LIBUNWIND_INSTALL_LIBRARY_DIR} COMPONENT unwind + RUNTIME DESTINATION ${LIBUNWIND_INSTALL_RUNTIME_DIR} COMPONENT unwind) +endif() + +if (NOT CMAKE_CONFIGURATION_TYPES AND LIBUNWIND_INSTALL_LIBRARY) + add_custom_target(install-unwind + DEPENDS unwind + COMMAND "${CMAKE_COMMAND}" + -DCMAKE_INSTALL_COMPONENT=unwind + -P "${LIBUNWIND_BINARY_DIR}/cmake_install.cmake") + add_custom_target(install-unwind-stripped + DEPENDS unwind + COMMAND "${CMAKE_COMMAND}" + -DCMAKE_INSTALL_COMPONENT=unwind + -DCMAKE_INSTALL_DO_STRIP=1 + -P "${LIBUNWIND_BINARY_DIR}/cmake_install.cmake") +endif() diff --git a/shared/sentry/external/crashpad/libunwind/src/CompactUnwinder.hpp b/shared/sentry/external/crashpad/libunwind/src/CompactUnwinder.hpp new file mode 100644 index 000000000..bcf042447 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/src/CompactUnwinder.hpp @@ -0,0 +1,787 @@ +//===-------------------------- CompactUnwinder.hpp -----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Does runtime stack unwinding using compact unwind encodings. +// +//===----------------------------------------------------------------------===// + +#ifndef __COMPACT_UNWINDER_HPP__ +#define __COMPACT_UNWINDER_HPP__ + +#include +#include + +#include +#include + +#include "Registers.hpp" + +#define EXTRACT_BITS(value, mask) \ + ((value >> __builtin_ctz(mask)) & (((1 << __builtin_popcount(mask))) - 1)) + +namespace libunwind { + +#if defined(_LIBUNWIND_TARGET_I386) +/// CompactUnwinder_x86 uses a compact unwind info to virtually "step" (aka +/// unwind) by modifying a Registers_x86 register set +template +class CompactUnwinder_x86 { +public: + + static int stepWithCompactEncoding(compact_unwind_encoding_t info, + uint32_t functionStart, A &addressSpace, + Registers_x86 ®isters); + +private: + typename A::pint_t pint_t; + + static void frameUnwind(A &addressSpace, Registers_x86 ®isters); + static void framelessUnwind(A &addressSpace, + typename A::pint_t returnAddressLocation, + Registers_x86 ®isters); + static int + stepWithCompactEncodingEBPFrame(compact_unwind_encoding_t compactEncoding, + uint32_t functionStart, A &addressSpace, + Registers_x86 ®isters); + static int stepWithCompactEncodingFrameless( + compact_unwind_encoding_t compactEncoding, uint32_t functionStart, + A &addressSpace, Registers_x86 ®isters, bool indirectStackSize); +}; + +template +int CompactUnwinder_x86::stepWithCompactEncoding( + compact_unwind_encoding_t compactEncoding, uint32_t functionStart, + A &addressSpace, Registers_x86 ®isters) { + switch (compactEncoding & UNWIND_X86_MODE_MASK) { + case UNWIND_X86_MODE_EBP_FRAME: + return stepWithCompactEncodingEBPFrame(compactEncoding, functionStart, + addressSpace, registers); + case UNWIND_X86_MODE_STACK_IMMD: + return stepWithCompactEncodingFrameless(compactEncoding, functionStart, + addressSpace, registers, false); + case UNWIND_X86_MODE_STACK_IND: + return stepWithCompactEncodingFrameless(compactEncoding, functionStart, + addressSpace, registers, true); + } + _LIBUNWIND_ABORT("invalid compact unwind encoding"); +} + +template +int CompactUnwinder_x86::stepWithCompactEncodingEBPFrame( + compact_unwind_encoding_t compactEncoding, uint32_t functionStart, + A &addressSpace, Registers_x86 ®isters) { + uint32_t savedRegistersOffset = + EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_OFFSET); + uint32_t savedRegistersLocations = + EXTRACT_BITS(compactEncoding, UNWIND_X86_EBP_FRAME_REGISTERS); + + uint32_t savedRegisters = registers.getEBP() - 4 * savedRegistersOffset; + for (int i = 0; i < 5; ++i) { + switch (savedRegistersLocations & 0x7) { + case UNWIND_X86_REG_NONE: + // no register saved in this slot + break; + case UNWIND_X86_REG_EBX: + registers.setEBX(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_ECX: + registers.setECX(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_EDX: + registers.setEDX(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_EDI: + registers.setEDI(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_ESI: + registers.setESI(addressSpace.get32(savedRegisters)); + break; + default: + (void)functionStart; + _LIBUNWIND_DEBUG_LOG("bad register for EBP frame, encoding=%08X for " + "function starting at 0x%X", + compactEncoding, functionStart); + _LIBUNWIND_ABORT("invalid compact unwind encoding"); + } + savedRegisters += 4; + savedRegistersLocations = (savedRegistersLocations >> 3); + } + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; +} + +template +int CompactUnwinder_x86::stepWithCompactEncodingFrameless( + compact_unwind_encoding_t encoding, uint32_t functionStart, + A &addressSpace, Registers_x86 ®isters, bool indirectStackSize) { + uint32_t stackSizeEncoded = + EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_SIZE); + uint32_t stackAdjust = + EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_ADJUST); + uint32_t regCount = + EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_COUNT); + uint32_t permutation = + EXTRACT_BITS(encoding, UNWIND_X86_FRAMELESS_STACK_REG_PERMUTATION); + uint32_t stackSize = stackSizeEncoded * 4; + if (indirectStackSize) { + // stack size is encoded in subl $xxx,%esp instruction + uint32_t subl = addressSpace.get32(functionStart + stackSizeEncoded); + stackSize = subl + 4 * stackAdjust; + } + // decompress permutation + uint32_t permunreg[6]; + switch (regCount) { + case 6: + permunreg[0] = permutation / 120; + permutation -= (permunreg[0] * 120); + permunreg[1] = permutation / 24; + permutation -= (permunreg[1] * 24); + permunreg[2] = permutation / 6; + permutation -= (permunreg[2] * 6); + permunreg[3] = permutation / 2; + permutation -= (permunreg[3] * 2); + permunreg[4] = permutation; + permunreg[5] = 0; + break; + case 5: + permunreg[0] = permutation / 120; + permutation -= (permunreg[0] * 120); + permunreg[1] = permutation / 24; + permutation -= (permunreg[1] * 24); + permunreg[2] = permutation / 6; + permutation -= (permunreg[2] * 6); + permunreg[3] = permutation / 2; + permutation -= (permunreg[3] * 2); + permunreg[4] = permutation; + break; + case 4: + permunreg[0] = permutation / 60; + permutation -= (permunreg[0] * 60); + permunreg[1] = permutation / 12; + permutation -= (permunreg[1] * 12); + permunreg[2] = permutation / 3; + permutation -= (permunreg[2] * 3); + permunreg[3] = permutation; + break; + case 3: + permunreg[0] = permutation / 20; + permutation -= (permunreg[0] * 20); + permunreg[1] = permutation / 4; + permutation -= (permunreg[1] * 4); + permunreg[2] = permutation; + break; + case 2: + permunreg[0] = permutation / 5; + permutation -= (permunreg[0] * 5); + permunreg[1] = permutation; + break; + case 1: + permunreg[0] = permutation; + break; + } + // re-number registers back to standard numbers + int registersSaved[6]; + bool used[7] = { false, false, false, false, false, false, false }; + for (uint32_t i = 0; i < regCount; ++i) { + uint32_t renum = 0; + for (int u = 1; u < 7; ++u) { + if (!used[u]) { + if (renum == permunreg[i]) { + registersSaved[i] = u; + used[u] = true; + break; + } + ++renum; + } + } + } + uint32_t savedRegisters = registers.getSP() + stackSize - 4 - 4 * regCount; + for (uint32_t i = 0; i < regCount; ++i) { + switch (registersSaved[i]) { + case UNWIND_X86_REG_EBX: + registers.setEBX(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_ECX: + registers.setECX(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_EDX: + registers.setEDX(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_EDI: + registers.setEDI(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_ESI: + registers.setESI(addressSpace.get32(savedRegisters)); + break; + case UNWIND_X86_REG_EBP: + registers.setEBP(addressSpace.get32(savedRegisters)); + break; + default: + _LIBUNWIND_DEBUG_LOG("bad register for frameless, encoding=%08X for " + "function starting at 0x%X", + encoding, functionStart); + _LIBUNWIND_ABORT("invalid compact unwind encoding"); + } + savedRegisters += 4; + } + framelessUnwind(addressSpace, savedRegisters, registers); + return UNW_STEP_SUCCESS; +} + + +template +void CompactUnwinder_x86::frameUnwind(A &addressSpace, + Registers_x86 ®isters) { + typename A::pint_t bp = registers.getEBP(); + // ebp points to old ebp + registers.setEBP(addressSpace.get32(bp)); + // old esp is ebp less saved ebp and return address + registers.setSP((uint32_t)bp + 8); + // pop return address into eip + registers.setIP(addressSpace.get32(bp + 4)); +} + +template +void CompactUnwinder_x86::framelessUnwind( + A &addressSpace, typename A::pint_t returnAddressLocation, + Registers_x86 ®isters) { + // return address is on stack after last saved register + registers.setIP(addressSpace.get32(returnAddressLocation)); + // old esp is before return address + registers.setSP((uint32_t)returnAddressLocation + 4); +} +#endif // _LIBUNWIND_TARGET_I386 + + +#if defined(_LIBUNWIND_TARGET_X86_64) +/// CompactUnwinder_x86_64 uses a compact unwind info to virtually "step" (aka +/// unwind) by modifying a Registers_x86_64 register set +template +class CompactUnwinder_x86_64 { +public: + + static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding, + uint64_t functionStart, A &addressSpace, + Registers_x86_64 ®isters); + +private: + typename A::pint_t pint_t; + + static void frameUnwind(A &addressSpace, Registers_x86_64 ®isters); + static void framelessUnwind(A &addressSpace, uint64_t returnAddressLocation, + Registers_x86_64 ®isters); + static int stepSpeculatively( + A &addressSpace, Registers_x86_64 ®isters); + static int + stepWithCompactEncodingRBPFrame(compact_unwind_encoding_t compactEncoding, + uint64_t functionStart, A &addressSpace, + Registers_x86_64 ®isters); + static int stepWithCompactEncodingFrameless( + compact_unwind_encoding_t compactEncoding, uint64_t functionStart, + A &addressSpace, Registers_x86_64 ®isters, bool indirectStackSize); +}; + +template +int CompactUnwinder_x86_64::stepWithCompactEncoding( + compact_unwind_encoding_t compactEncoding, uint64_t functionStart, + A &addressSpace, Registers_x86_64 ®isters) { + switch (compactEncoding & UNWIND_X86_64_MODE_MASK) { + case 0: + return stepSpeculatively(addressSpace, registers); + case UNWIND_X86_64_MODE_RBP_FRAME: + return stepWithCompactEncodingRBPFrame(compactEncoding, functionStart, + addressSpace, registers); + case UNWIND_X86_64_MODE_STACK_IMMD: + return stepWithCompactEncodingFrameless(compactEncoding, functionStart, + addressSpace, registers, false); + case UNWIND_X86_64_MODE_STACK_IND: + return stepWithCompactEncodingFrameless(compactEncoding, functionStart, + addressSpace, registers, true); + } + _LIBUNWIND_ABORT("invalid compact unwind encoding"); +} + +template +int CompactUnwinder_x86_64::stepSpeculatively( + A &addressSpace, Registers_x86_64 ®isters) { + uint64_t rsp = registers.getSP(); + uint64_t rbp = registers.getRBP(); + if (rsp == rbp) { + // In this case we assume this was a standard `push rbp, rbp = rsp` + // preamble, so the stack should only have the old rbp, and the return + // address on it. This is the case in, for example: + // - `libsystem_platform.dylib/_platform_bzero$VARIANT$Haswell` + frameUnwind(addressSpace, registers); + } else { + // Here, we assume that the function has no stack space of its own, so we + // assume the return address is right there at the top. This happens for + // for example in: + // - `libsystem_kernel.dylib/__psynch_cvwait` + // - and other functions which appear to be syscall wrappers + framelessUnwind(addressSpace, rsp, registers); + } + return UNW_STEP_SUCCESS; +} + +template +int CompactUnwinder_x86_64::stepWithCompactEncodingRBPFrame( + compact_unwind_encoding_t compactEncoding, uint64_t functionStart, + A &addressSpace, Registers_x86_64 ®isters) { + uint32_t savedRegistersOffset = + EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_OFFSET); + uint32_t savedRegistersLocations = + EXTRACT_BITS(compactEncoding, UNWIND_X86_64_RBP_FRAME_REGISTERS); + + uint64_t savedRegisters = registers.getRBP() - 8 * savedRegistersOffset; + for (int i = 0; i < 5; ++i) { + switch (savedRegistersLocations & 0x7) { + case UNWIND_X86_64_REG_NONE: + // no register saved in this slot + break; + case UNWIND_X86_64_REG_RBX: + registers.setRBX(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R12: + registers.setR12(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R13: + registers.setR13(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R14: + registers.setR14(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R15: + registers.setR15(addressSpace.get64(savedRegisters)); + break; + default: + (void)functionStart; + _LIBUNWIND_DEBUG_LOG("bad register for RBP frame, encoding=%08X for " + "function starting at 0x%llX", + compactEncoding, functionStart); + _LIBUNWIND_ABORT("invalid compact unwind encoding"); + } + savedRegisters += 8; + savedRegistersLocations = (savedRegistersLocations >> 3); + } + frameUnwind(addressSpace, registers); + return UNW_STEP_SUCCESS; +} + +template +int CompactUnwinder_x86_64::stepWithCompactEncodingFrameless( + compact_unwind_encoding_t encoding, uint64_t functionStart, A &addressSpace, + Registers_x86_64 ®isters, bool indirectStackSize) { + uint32_t stackSizeEncoded = + EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_SIZE); + uint32_t stackAdjust = + EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_ADJUST); + uint32_t regCount = + EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_COUNT); + uint32_t permutation = + EXTRACT_BITS(encoding, UNWIND_X86_64_FRAMELESS_STACK_REG_PERMUTATION); + uint32_t stackSize = stackSizeEncoded * 8; + if (indirectStackSize) { + // stack size is encoded in subl $xxx,%esp instruction + uint32_t subl = addressSpace.get32(functionStart + stackSizeEncoded); + stackSize = subl + 8 * stackAdjust; + } + // decompress permutation + uint32_t permunreg[6]; + switch (regCount) { + case 6: + permunreg[0] = permutation / 120; + permutation -= (permunreg[0] * 120); + permunreg[1] = permutation / 24; + permutation -= (permunreg[1] * 24); + permunreg[2] = permutation / 6; + permutation -= (permunreg[2] * 6); + permunreg[3] = permutation / 2; + permutation -= (permunreg[3] * 2); + permunreg[4] = permutation; + permunreg[5] = 0; + break; + case 5: + permunreg[0] = permutation / 120; + permutation -= (permunreg[0] * 120); + permunreg[1] = permutation / 24; + permutation -= (permunreg[1] * 24); + permunreg[2] = permutation / 6; + permutation -= (permunreg[2] * 6); + permunreg[3] = permutation / 2; + permutation -= (permunreg[3] * 2); + permunreg[4] = permutation; + break; + case 4: + permunreg[0] = permutation / 60; + permutation -= (permunreg[0] * 60); + permunreg[1] = permutation / 12; + permutation -= (permunreg[1] * 12); + permunreg[2] = permutation / 3; + permutation -= (permunreg[2] * 3); + permunreg[3] = permutation; + break; + case 3: + permunreg[0] = permutation / 20; + permutation -= (permunreg[0] * 20); + permunreg[1] = permutation / 4; + permutation -= (permunreg[1] * 4); + permunreg[2] = permutation; + break; + case 2: + permunreg[0] = permutation / 5; + permutation -= (permunreg[0] * 5); + permunreg[1] = permutation; + break; + case 1: + permunreg[0] = permutation; + break; + } + // re-number registers back to standard numbers + int registersSaved[6]; + bool used[7] = { false, false, false, false, false, false, false }; + for (uint32_t i = 0; i < regCount; ++i) { + uint32_t renum = 0; + for (int u = 1; u < 7; ++u) { + if (!used[u]) { + if (renum == permunreg[i]) { + registersSaved[i] = u; + used[u] = true; + break; + } + ++renum; + } + } + } + uint64_t savedRegisters = registers.getSP() + stackSize - 8 - 8 * regCount; + for (uint32_t i = 0; i < regCount; ++i) { + switch (registersSaved[i]) { + case UNWIND_X86_64_REG_RBX: + registers.setRBX(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R12: + registers.setR12(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R13: + registers.setR13(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R14: + registers.setR14(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_R15: + registers.setR15(addressSpace.get64(savedRegisters)); + break; + case UNWIND_X86_64_REG_RBP: + registers.setRBP(addressSpace.get64(savedRegisters)); + break; + default: + _LIBUNWIND_DEBUG_LOG("bad register for frameless, encoding=%08X for " + "function starting at 0x%llX", + encoding, functionStart); + _LIBUNWIND_ABORT("invalid compact unwind encoding"); + } + savedRegisters += 8; + } + framelessUnwind(addressSpace, savedRegisters, registers); + return UNW_STEP_SUCCESS; +} + + +template +void CompactUnwinder_x86_64::frameUnwind(A &addressSpace, + Registers_x86_64 ®isters) { + uint64_t rbp = registers.getRBP(); + // ebp points to old ebp + registers.setRBP(addressSpace.get64(rbp)); + // old esp is ebp less saved ebp and return address + registers.setSP(rbp + 16); + // pop return address into eip + registers.setIP(addressSpace.get64(rbp + 8)); +} + +template +void CompactUnwinder_x86_64::framelessUnwind(A &addressSpace, + uint64_t returnAddressLocation, + Registers_x86_64 ®isters) { + // return address is on stack after last saved register + registers.setIP(addressSpace.get64(returnAddressLocation)); + // old esp is before return address + registers.setSP(returnAddressLocation + 8); +} +#endif // _LIBUNWIND_TARGET_X86_64 + + + +#if defined(_LIBUNWIND_TARGET_AARCH64) +uint64_t strip_ptr_auth(uint64_t pointer) { + // mask is taken from: + // https://github.com/dotnet/runtime/pull/40435/files/af4db134ddd9deea10e75d3f732cc35d3b61119e#r479544995 + uint64_t mask = 0x7fffffffffffull; + return pointer & mask; +} + +/// CompactUnwinder_arm64 uses a compact unwind info to virtually "step" (aka +/// unwind) by modifying a Registers_arm64 register set +template +class CompactUnwinder_arm64 { +public: + + static int stepWithCompactEncoding(compact_unwind_encoding_t compactEncoding, + uint64_t functionStart, A &addressSpace, + Registers_arm64 ®isters); + +private: + typename A::pint_t pint_t; + + static int stepSpeculatively( + A &addressSpace, Registers_arm64 ®isters); + static int + stepWithCompactEncodingFrame(compact_unwind_encoding_t compactEncoding, + uint64_t functionStart, A &addressSpace, + Registers_arm64 ®isters); + static int stepWithCompactEncodingFrameless( + compact_unwind_encoding_t compactEncoding, uint64_t functionStart, + A &addressSpace, Registers_arm64 ®isters); +}; + +template +int CompactUnwinder_arm64::stepWithCompactEncoding( + compact_unwind_encoding_t compactEncoding, uint64_t functionStart, + A &addressSpace, Registers_arm64 ®isters) { + int result = 0; + switch (compactEncoding & UNWIND_ARM64_MODE_MASK) { + case 0: + result = stepSpeculatively(addressSpace, registers); + registers.setRegister(UNW_AARCH64_LR, 0); + return result; + case UNWIND_ARM64_MODE_FRAME: + result = stepWithCompactEncodingFrame(compactEncoding, functionStart, + addressSpace, registers); + registers.setRegister(UNW_AARCH64_LR, 0); + return result; + case UNWIND_ARM64_MODE_FRAMELESS: + result = stepWithCompactEncodingFrameless(compactEncoding, functionStart, + addressSpace, registers); + registers.setRegister(UNW_AARCH64_LR, 0); + return result; + } + _LIBUNWIND_ABORT("invalid compact unwind encoding"); +} + +template +int CompactUnwinder_arm64::stepSpeculatively( + A &addressSpace, Registers_arm64 ®isters) { + // XXX: breakpad sets the IP from the LR, which is only correct if we do + // framepointer unwinding all the way. + // However, compact unwinding code never actually restores the LR, so we + // might have some bogus values in this case. We could do that at the bottom + // of `stepWithCompactEncodingFrame` but that wouldn't really solve the + // problem, as that is also a duplicated/bogus LR then. Long story short, + // what this means is, that we use the LR (correctly) when we are missing + // compact unwind info for the *first* frame. We are lucky though, as it + // is mostly the top frames which are missing unwind info + // (they are what appears to be syscall wrappers mostly). + // To overcome this, we use the LR only if we are at the first frame. + // (we reset it to 0 after a `step` call) + // All other frames use frame-pointer based unwinding, fetching the return + // address from the stack. + + uint64_t lr = strip_ptr_auth(registers.getRegister(UNW_AARCH64_LR)); + + if (lr) { + registers.setIP(lr); + } else { + // this is a recreation of: + // https://github.com/getsentry/breakpad/blob/master/src/processor/stackwalker_arm64.cc#L208-L252 + uint64_t last_fp = registers.getFP(); + uint64_t caller_fp = 0; + uint64_t caller_lr = 0; + uint64_t caller_sp = registers.getSP(); + + if (last_fp) { + // fp points to old fp + caller_fp = addressSpace.get64(last_fp); + // old sp is fp less saved fp and lr + caller_sp = last_fp + 16; + // pop return address into pc + caller_lr = strip_ptr_auth(addressSpace.get64(last_fp + 8)); + } + + registers.setFP(caller_fp); + registers.setSP(caller_sp); + registers.setIP(caller_lr); + } + return UNW_STEP_SUCCESS; +} + +template +int CompactUnwinder_arm64::stepWithCompactEncodingFrameless( + compact_unwind_encoding_t encoding, uint64_t, A &addressSpace, + Registers_arm64 ®isters) { + uint32_t stackSize = + 16 * EXTRACT_BITS(encoding, UNWIND_ARM64_FRAMELESS_STACK_SIZE_MASK); + + uint64_t savedRegisterLoc = registers.getSP() + stackSize; + + if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) { + registers.setRegister(UNW_AARCH64_X19, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setRegister(UNW_AARCH64_X20, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) { + registers.setRegister(UNW_AARCH64_X21, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setRegister(UNW_AARCH64_X22, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) { + registers.setRegister(UNW_AARCH64_X23, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setRegister(UNW_AARCH64_X24, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) { + registers.setRegister(UNW_AARCH64_X25, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setRegister(UNW_AARCH64_X26, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) { + registers.setRegister(UNW_AARCH64_X27, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setRegister(UNW_AARCH64_X28, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + + if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) { + registers.setFloatRegister(UNW_AARCH64_V8, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_AARCH64_V9, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) { + registers.setFloatRegister(UNW_AARCH64_V10, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_AARCH64_V11, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) { + registers.setFloatRegister(UNW_AARCH64_V12, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_AARCH64_V13, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) { + registers.setFloatRegister(UNW_AARCH64_V14, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_AARCH64_V15, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + + // subtract stack size off of sp + registers.setSP(savedRegisterLoc); + + // set pc to be value in lr + registers.setIP(strip_ptr_auth(registers.getRegister(UNW_AARCH64_LR))); + + return UNW_STEP_SUCCESS; +} + +template +int CompactUnwinder_arm64::stepWithCompactEncodingFrame( + compact_unwind_encoding_t encoding, uint64_t, A &addressSpace, + Registers_arm64 ®isters) { + uint64_t savedRegisterLoc = registers.getFP() - 8; + + if (encoding & UNWIND_ARM64_FRAME_X19_X20_PAIR) { + registers.setRegister(UNW_AARCH64_X19, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setRegister(UNW_AARCH64_X20, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X21_X22_PAIR) { + registers.setRegister(UNW_AARCH64_X21, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setRegister(UNW_AARCH64_X22, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X23_X24_PAIR) { + registers.setRegister(UNW_AARCH64_X23, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setRegister(UNW_AARCH64_X24, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X25_X26_PAIR) { + registers.setRegister(UNW_AARCH64_X25, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setRegister(UNW_AARCH64_X26, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_X27_X28_PAIR) { + registers.setRegister(UNW_AARCH64_X27, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setRegister(UNW_AARCH64_X28, addressSpace.get64(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + + if (encoding & UNWIND_ARM64_FRAME_D8_D9_PAIR) { + registers.setFloatRegister(UNW_AARCH64_V8, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_AARCH64_V9, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_D10_D11_PAIR) { + registers.setFloatRegister(UNW_AARCH64_V10, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_AARCH64_V11, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_D12_D13_PAIR) { + registers.setFloatRegister(UNW_AARCH64_V12, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_AARCH64_V13, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + if (encoding & UNWIND_ARM64_FRAME_D14_D15_PAIR) { + registers.setFloatRegister(UNW_AARCH64_V14, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + registers.setFloatRegister(UNW_AARCH64_V15, + addressSpace.getDouble(savedRegisterLoc)); + savedRegisterLoc -= 8; + } + + uint64_t fp = registers.getFP(); + // fp points to old fp + registers.setFP(addressSpace.get64(fp)); + // old sp is fp less saved fp and lr + registers.setSP(fp + 16); + // pop return address into pc + registers.setIP(strip_ptr_auth(addressSpace.get64(fp + 8))); + + return UNW_STEP_SUCCESS; +} +#endif // _LIBUNWIND_TARGET_AARCH64 + + +} // namespace libunwind + +#endif // __COMPACT_UNWINDER_HPP__ diff --git a/shared/sentry/external/crashpad/libunwind/src/DwarfInstructions.hpp b/shared/sentry/external/crashpad/libunwind/src/DwarfInstructions.hpp new file mode 100644 index 000000000..b58c51bb7 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/src/DwarfInstructions.hpp @@ -0,0 +1,838 @@ +//===-------------------------- DwarfInstructions.hpp ---------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Processor specific interpretation of DWARF unwind info. +// +//===----------------------------------------------------------------------===// + +#ifndef __DWARF_INSTRUCTIONS_HPP__ +#define __DWARF_INSTRUCTIONS_HPP__ + +#include +#include +#include + +#include "dwarf2.h" +#include "Registers.hpp" +#include "DwarfParser.hpp" +#include "config.h" + + +namespace libunwind { + + +/// DwarfInstructions maps abtract DWARF unwind instructions to a particular +/// architecture +template +class DwarfInstructions { +public: + typedef typename A::pint_t pint_t; + typedef typename A::sint_t sint_t; + + static int stepWithDwarf(A &addressSpace, pint_t pc, pint_t fdeStart, + R ®isters, bool &isSignalFrame); + +private: + + enum { + DW_X86_64_RET_ADDR = 16 + }; + + enum { + DW_X86_RET_ADDR = 8 + }; + + typedef typename CFI_Parser::RegisterLocation RegisterLocation; + typedef typename CFI_Parser::PrologInfo PrologInfo; + typedef typename CFI_Parser::FDE_Info FDE_Info; + typedef typename CFI_Parser::CIE_Info CIE_Info; + + static pint_t evaluateExpression(pint_t expression, A &addressSpace, + const R ®isters, + pint_t initialStackValue); + static pint_t getSavedRegister(A &addressSpace, const R ®isters, + pint_t cfa, const RegisterLocation &savedReg); + static double getSavedFloatRegister(A &addressSpace, const R ®isters, + pint_t cfa, const RegisterLocation &savedReg); + static v128 getSavedVectorRegister(A &addressSpace, const R ®isters, + pint_t cfa, const RegisterLocation &savedReg); + + static pint_t getCFA(A &addressSpace, const PrologInfo &prolog, + const R ®isters) { + if (prolog.cfaRegister != 0) + return (pint_t)((sint_t)registers.getRegister((int)prolog.cfaRegister) + + prolog.cfaRegisterOffset); + if (prolog.cfaExpression != 0) + return evaluateExpression((pint_t)prolog.cfaExpression, addressSpace, + registers, 0); + assert(0 && "getCFA(): unknown location"); + __builtin_unreachable(); + } +}; + + +template +typename A::pint_t DwarfInstructions::getSavedRegister( + A &addressSpace, const R ®isters, pint_t cfa, + const RegisterLocation &savedReg) { + switch (savedReg.location) { + case CFI_Parser::kRegisterInCFA: + return (pint_t)addressSpace.getRegister(cfa + (pint_t)savedReg.value); + + case CFI_Parser::kRegisterAtExpression: + return (pint_t)addressSpace.getRegister(evaluateExpression( + (pint_t)savedReg.value, addressSpace, registers, cfa)); + + case CFI_Parser::kRegisterIsExpression: + return evaluateExpression((pint_t)savedReg.value, addressSpace, + registers, cfa); + + case CFI_Parser::kRegisterInRegister: + return registers.getRegister((int)savedReg.value); + case CFI_Parser::kRegisterUndefined: + return 0; + case CFI_Parser::kRegisterUnused: + case CFI_Parser::kRegisterOffsetFromCFA: + // FIX ME + break; + } + _LIBUNWIND_ABORT("unsupported restore location for register"); +} + +template +double DwarfInstructions::getSavedFloatRegister( + A &addressSpace, const R ®isters, pint_t cfa, + const RegisterLocation &savedReg) { + switch (savedReg.location) { + case CFI_Parser::kRegisterInCFA: + return addressSpace.getDouble(cfa + (pint_t)savedReg.value); + + case CFI_Parser::kRegisterAtExpression: + return addressSpace.getDouble( + evaluateExpression((pint_t)savedReg.value, addressSpace, + registers, cfa)); + case CFI_Parser::kRegisterUndefined: + return 0.0; + case CFI_Parser::kRegisterInRegister: +#ifndef _LIBUNWIND_TARGET_ARM + return registers.getFloatRegister((int)savedReg.value); +#endif + case CFI_Parser::kRegisterIsExpression: + case CFI_Parser::kRegisterUnused: + case CFI_Parser::kRegisterOffsetFromCFA: + // FIX ME + break; + } + _LIBUNWIND_ABORT("unsupported restore location for float register"); +} + +template +v128 DwarfInstructions::getSavedVectorRegister( + A &addressSpace, const R ®isters, pint_t cfa, + const RegisterLocation &savedReg) { + switch (savedReg.location) { + case CFI_Parser::kRegisterInCFA: + return addressSpace.getVector(cfa + (pint_t)savedReg.value); + + case CFI_Parser::kRegisterAtExpression: + return addressSpace.getVector( + evaluateExpression((pint_t)savedReg.value, addressSpace, + registers, cfa)); + + case CFI_Parser::kRegisterIsExpression: + case CFI_Parser::kRegisterUnused: + case CFI_Parser::kRegisterUndefined: + case CFI_Parser::kRegisterOffsetFromCFA: + case CFI_Parser::kRegisterInRegister: + // FIX ME + break; + } + _LIBUNWIND_ABORT("unsupported restore location for vector register"); +} + +template +int DwarfInstructions::stepWithDwarf(A &addressSpace, pint_t pc, + pint_t fdeStart, R ®isters, + bool &isSignalFrame) { + FDE_Info fdeInfo; + CIE_Info cieInfo; + if (CFI_Parser::decodeFDE(addressSpace, fdeStart, &fdeInfo, + &cieInfo) == NULL) { + PrologInfo prolog; + if (CFI_Parser::parseFDEInstructions(addressSpace, fdeInfo, cieInfo, pc, + R::getArch(), &prolog)) { + // get pointer to cfa (architecture specific) + pint_t cfa = getCFA(addressSpace, prolog, registers); + + // restore registers that DWARF says were saved + R newRegisters = registers; + + // Typically, the CFA is the stack pointer at the call site in + // the previous frame. However, there are scenarios in which this is not + // true. For example, if we switched to a new stack. In that case, the + // value of the previous SP might be indicated by a CFI directive. + // + // We set the SP here to the CFA, allowing for it to be overridden + // by a CFI directive later on. + newRegisters.setSP(cfa); + + pint_t returnAddress = 0; + const int lastReg = R::lastDwarfRegNum(); + assert(static_cast(CFI_Parser::kMaxRegisterNumber) >= lastReg && + "register range too large"); + assert(lastReg >= (int)cieInfo.returnAddressRegister && + "register range does not contain return address register"); + for (int i = 0; i <= lastReg; ++i) { + if (prolog.savedRegisters[i].location != + CFI_Parser::kRegisterUnused) { + if (registers.validFloatRegister(i)) + newRegisters.setFloatRegister( + i, getSavedFloatRegister(addressSpace, registers, cfa, + prolog.savedRegisters[i])); + else if (registers.validVectorRegister(i)) + newRegisters.setVectorRegister( + i, getSavedVectorRegister(addressSpace, registers, cfa, + prolog.savedRegisters[i])); + else if (i == (int)cieInfo.returnAddressRegister) + returnAddress = getSavedRegister(addressSpace, registers, cfa, + prolog.savedRegisters[i]); + else if (registers.validRegister(i)) + newRegisters.setRegister( + i, getSavedRegister(addressSpace, registers, cfa, + prolog.savedRegisters[i])); + else + return UNW_EBADREG; + } else if (i == (int)cieInfo.returnAddressRegister) { + // Leaf function keeps the return address in register and there is no + // explicit intructions how to restore it. + returnAddress = registers.getRegister(cieInfo.returnAddressRegister); + } + } + + isSignalFrame = cieInfo.isSignalFrame; + +#if defined(_LIBUNWIND_TARGET_AARCH64) + // If the target is aarch64 then the return address may have been signed + // using the v8.3 pointer authentication extensions. The original + // return address needs to be authenticated before the return address is + // restored. autia1716 is used instead of autia as autia1716 assembles + // to a NOP on pre-v8.3a architectures. + if ((R::getArch() == REGISTERS_ARM64) && + prolog.savedRegisters[UNW_AARCH64_RA_SIGN_STATE].value && + returnAddress != 0) { +#if !defined(_LIBUNWIND_IS_NATIVE_ONLY) + return UNW_ECROSSRASIGNING; +#else + register unsigned long long x17 __asm("x17") = returnAddress; + register unsigned long long x16 __asm("x16") = cfa; + + // These are the autia1716/autib1716 instructions. The hint instructions + // are used here as gcc does not assemble autia1716/autib1716 for pre + // armv8.3a targets. + if (cieInfo.addressesSignedWithBKey) + asm("hint 0xe" : "+r"(x17) : "r"(x16)); // autib1716 + else + asm("hint 0xc" : "+r"(x17) : "r"(x16)); // autia1716 + returnAddress = x17; +#endif + } +#endif + +#if defined(_LIBUNWIND_TARGET_SPARC) + if (R::getArch() == REGISTERS_SPARC) { + // Skip call site instruction and delay slot + returnAddress += 8; + // Skip unimp instruction if function returns a struct + if ((addressSpace.get32(returnAddress) & 0xC1C00000) == 0) + returnAddress += 4; + } +#endif + +#if defined(_LIBUNWIND_TARGET_PPC64) +#define PPC64_ELFV1_R2_LOAD_INST_ENCODING 0xe8410028u // ld r2,40(r1) +#define PPC64_ELFV1_R2_OFFSET 40 +#define PPC64_ELFV2_R2_LOAD_INST_ENCODING 0xe8410018u // ld r2,24(r1) +#define PPC64_ELFV2_R2_OFFSET 24 + // If the instruction at return address is a TOC (r2) restore, + // then r2 was saved and needs to be restored. + // ELFv2 ABI specifies that the TOC Pointer must be saved at SP + 24, + // while in ELFv1 ABI it is saved at SP + 40. + if (R::getArch() == REGISTERS_PPC64 && returnAddress != 0) { + pint_t sp = newRegisters.getRegister(UNW_REG_SP); + pint_t r2 = 0; + switch (addressSpace.get32(returnAddress)) { + case PPC64_ELFV1_R2_LOAD_INST_ENCODING: + r2 = addressSpace.get64(sp + PPC64_ELFV1_R2_OFFSET); + break; + case PPC64_ELFV2_R2_LOAD_INST_ENCODING: + r2 = addressSpace.get64(sp + PPC64_ELFV2_R2_OFFSET); + break; + } + if (r2) + newRegisters.setRegister(UNW_PPC64_R2, r2); + } +#endif + + // Return address is address after call site instruction, so setting IP to + // that does simualates a return. + newRegisters.setIP(returnAddress); + + // Simulate the step by replacing the register set with the new ones. + registers = newRegisters; + + return UNW_STEP_SUCCESS; + } + } + return UNW_EBADFRAME; +} + +template +typename A::pint_t +DwarfInstructions::evaluateExpression(pint_t expression, A &addressSpace, + const R ®isters, + pint_t initialStackValue) { + const bool log = false; + pint_t p = expression; + pint_t expressionEnd = expression + 20; // temp, until len read + pint_t length = (pint_t)addressSpace.getULEB128(p, expressionEnd); + expressionEnd = p + length; + if (log) + fprintf(stderr, "evaluateExpression(): length=%" PRIu64 "\n", + (uint64_t)length); + pint_t stack[100]; + pint_t *sp = stack; + *(++sp) = initialStackValue; + + while (p < expressionEnd) { + if (log) { + for (pint_t *t = sp; t > stack; --t) { + fprintf(stderr, "sp[] = 0x%" PRIx64 "\n", (uint64_t)(*t)); + } + } + uint8_t opcode = addressSpace.get8(p++); + sint_t svalue, svalue2; + pint_t value; + uint32_t reg; + switch (opcode) { + case DW_OP_addr: + // push immediate address sized value + value = addressSpace.getP(p); + p += sizeof(pint_t); + *(++sp) = value; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_deref: + // pop stack, dereference, push result + value = *sp--; + *(++sp) = addressSpace.getP(value); + if (log) + fprintf(stderr, "dereference 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_const1u: + // push immediate 1 byte value + value = addressSpace.get8(p); + p += 1; + *(++sp) = value; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_const1s: + // push immediate 1 byte signed value + svalue = (int8_t) addressSpace.get8(p); + p += 1; + *(++sp) = (pint_t)svalue; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)svalue); + break; + + case DW_OP_const2u: + // push immediate 2 byte value + value = addressSpace.get16(p); + p += 2; + *(++sp) = value; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_const2s: + // push immediate 2 byte signed value + svalue = (int16_t) addressSpace.get16(p); + p += 2; + *(++sp) = (pint_t)svalue; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)svalue); + break; + + case DW_OP_const4u: + // push immediate 4 byte value + value = addressSpace.get32(p); + p += 4; + *(++sp) = value; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_const4s: + // push immediate 4 byte signed value + svalue = (int32_t)addressSpace.get32(p); + p += 4; + *(++sp) = (pint_t)svalue; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)svalue); + break; + + case DW_OP_const8u: + // push immediate 8 byte value + value = (pint_t)addressSpace.get64(p); + p += 8; + *(++sp) = value; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_const8s: + // push immediate 8 byte signed value + value = (pint_t)addressSpace.get64(p); + p += 8; + *(++sp) = value; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_constu: + // push immediate ULEB128 value + value = (pint_t)addressSpace.getULEB128(p, expressionEnd); + *(++sp) = value; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_consts: + // push immediate SLEB128 value + svalue = (sint_t)addressSpace.getSLEB128(p, expressionEnd); + *(++sp) = (pint_t)svalue; + if (log) + fprintf(stderr, "push 0x%" PRIx64 "\n", (uint64_t)svalue); + break; + + case DW_OP_dup: + // push top of stack + value = *sp; + *(++sp) = value; + if (log) + fprintf(stderr, "duplicate top of stack\n"); + break; + + case DW_OP_drop: + // pop + --sp; + if (log) + fprintf(stderr, "pop top of stack\n"); + break; + + case DW_OP_over: + // dup second + value = sp[-1]; + *(++sp) = value; + if (log) + fprintf(stderr, "duplicate second in stack\n"); + break; + + case DW_OP_pick: + // pick from + reg = addressSpace.get8(p); + p += 1; + value = sp[-(int)reg]; + *(++sp) = value; + if (log) + fprintf(stderr, "duplicate %d in stack\n", reg); + break; + + case DW_OP_swap: + // swap top two + value = sp[0]; + sp[0] = sp[-1]; + sp[-1] = value; + if (log) + fprintf(stderr, "swap top of stack\n"); + break; + + case DW_OP_rot: + // rotate top three + value = sp[0]; + sp[0] = sp[-1]; + sp[-1] = sp[-2]; + sp[-2] = value; + if (log) + fprintf(stderr, "rotate top three of stack\n"); + break; + + case DW_OP_xderef: + // pop stack, dereference, push result + value = *sp--; + *sp = *((pint_t*)value); + if (log) + fprintf(stderr, "x-dereference 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_abs: + svalue = (sint_t)*sp; + if (svalue < 0) + *sp = (pint_t)(-svalue); + if (log) + fprintf(stderr, "abs\n"); + break; + + case DW_OP_and: + value = *sp--; + *sp &= value; + if (log) + fprintf(stderr, "and\n"); + break; + + case DW_OP_div: + svalue = (sint_t)(*sp--); + svalue2 = (sint_t)*sp; + *sp = (pint_t)(svalue2 / svalue); + if (log) + fprintf(stderr, "div\n"); + break; + + case DW_OP_minus: + value = *sp--; + *sp = *sp - value; + if (log) + fprintf(stderr, "minus\n"); + break; + + case DW_OP_mod: + svalue = (sint_t)(*sp--); + svalue2 = (sint_t)*sp; + *sp = (pint_t)(svalue2 % svalue); + if (log) + fprintf(stderr, "module\n"); + break; + + case DW_OP_mul: + svalue = (sint_t)(*sp--); + svalue2 = (sint_t)*sp; + *sp = (pint_t)(svalue2 * svalue); + if (log) + fprintf(stderr, "mul\n"); + break; + + case DW_OP_neg: + *sp = 0 - *sp; + if (log) + fprintf(stderr, "neg\n"); + break; + + case DW_OP_not: + svalue = (sint_t)(*sp); + *sp = (pint_t)(~svalue); + if (log) + fprintf(stderr, "not\n"); + break; + + case DW_OP_or: + value = *sp--; + *sp |= value; + if (log) + fprintf(stderr, "or\n"); + break; + + case DW_OP_plus: + value = *sp--; + *sp += value; + if (log) + fprintf(stderr, "plus\n"); + break; + + case DW_OP_plus_uconst: + // pop stack, add uelb128 constant, push result + *sp += static_cast(addressSpace.getULEB128(p, expressionEnd)); + if (log) + fprintf(stderr, "add constant\n"); + break; + + case DW_OP_shl: + value = *sp--; + *sp = *sp << value; + if (log) + fprintf(stderr, "shift left\n"); + break; + + case DW_OP_shr: + value = *sp--; + *sp = *sp >> value; + if (log) + fprintf(stderr, "shift left\n"); + break; + + case DW_OP_shra: + value = *sp--; + svalue = (sint_t)*sp; + *sp = (pint_t)(svalue >> value); + if (log) + fprintf(stderr, "shift left arithmetric\n"); + break; + + case DW_OP_xor: + value = *sp--; + *sp ^= value; + if (log) + fprintf(stderr, "xor\n"); + break; + + case DW_OP_skip: + svalue = (int16_t) addressSpace.get16(p); + p += 2; + p = (pint_t)((sint_t)p + svalue); + if (log) + fprintf(stderr, "skip %" PRIu64 "\n", (uint64_t)svalue); + break; + + case DW_OP_bra: + svalue = (int16_t) addressSpace.get16(p); + p += 2; + if (*sp--) + p = (pint_t)((sint_t)p + svalue); + if (log) + fprintf(stderr, "bra %" PRIu64 "\n", (uint64_t)svalue); + break; + + case DW_OP_eq: + value = *sp--; + *sp = (*sp == value); + if (log) + fprintf(stderr, "eq\n"); + break; + + case DW_OP_ge: + value = *sp--; + *sp = (*sp >= value); + if (log) + fprintf(stderr, "ge\n"); + break; + + case DW_OP_gt: + value = *sp--; + *sp = (*sp > value); + if (log) + fprintf(stderr, "gt\n"); + break; + + case DW_OP_le: + value = *sp--; + *sp = (*sp <= value); + if (log) + fprintf(stderr, "le\n"); + break; + + case DW_OP_lt: + value = *sp--; + *sp = (*sp < value); + if (log) + fprintf(stderr, "lt\n"); + break; + + case DW_OP_ne: + value = *sp--; + *sp = (*sp != value); + if (log) + fprintf(stderr, "ne\n"); + break; + + case DW_OP_lit0: + case DW_OP_lit1: + case DW_OP_lit2: + case DW_OP_lit3: + case DW_OP_lit4: + case DW_OP_lit5: + case DW_OP_lit6: + case DW_OP_lit7: + case DW_OP_lit8: + case DW_OP_lit9: + case DW_OP_lit10: + case DW_OP_lit11: + case DW_OP_lit12: + case DW_OP_lit13: + case DW_OP_lit14: + case DW_OP_lit15: + case DW_OP_lit16: + case DW_OP_lit17: + case DW_OP_lit18: + case DW_OP_lit19: + case DW_OP_lit20: + case DW_OP_lit21: + case DW_OP_lit22: + case DW_OP_lit23: + case DW_OP_lit24: + case DW_OP_lit25: + case DW_OP_lit26: + case DW_OP_lit27: + case DW_OP_lit28: + case DW_OP_lit29: + case DW_OP_lit30: + case DW_OP_lit31: + value = static_cast(opcode - DW_OP_lit0); + *(++sp) = value; + if (log) + fprintf(stderr, "push literal 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_reg0: + case DW_OP_reg1: + case DW_OP_reg2: + case DW_OP_reg3: + case DW_OP_reg4: + case DW_OP_reg5: + case DW_OP_reg6: + case DW_OP_reg7: + case DW_OP_reg8: + case DW_OP_reg9: + case DW_OP_reg10: + case DW_OP_reg11: + case DW_OP_reg12: + case DW_OP_reg13: + case DW_OP_reg14: + case DW_OP_reg15: + case DW_OP_reg16: + case DW_OP_reg17: + case DW_OP_reg18: + case DW_OP_reg19: + case DW_OP_reg20: + case DW_OP_reg21: + case DW_OP_reg22: + case DW_OP_reg23: + case DW_OP_reg24: + case DW_OP_reg25: + case DW_OP_reg26: + case DW_OP_reg27: + case DW_OP_reg28: + case DW_OP_reg29: + case DW_OP_reg30: + case DW_OP_reg31: + reg = static_cast(opcode - DW_OP_reg0); + *(++sp) = registers.getRegister((int)reg); + if (log) + fprintf(stderr, "push reg %d\n", reg); + break; + + case DW_OP_regx: + reg = static_cast(addressSpace.getULEB128(p, expressionEnd)); + *(++sp) = registers.getRegister((int)reg); + if (log) + fprintf(stderr, "push reg %d + 0x%" PRIx64 "\n", reg, (uint64_t)svalue); + break; + + case DW_OP_breg0: + case DW_OP_breg1: + case DW_OP_breg2: + case DW_OP_breg3: + case DW_OP_breg4: + case DW_OP_breg5: + case DW_OP_breg6: + case DW_OP_breg7: + case DW_OP_breg8: + case DW_OP_breg9: + case DW_OP_breg10: + case DW_OP_breg11: + case DW_OP_breg12: + case DW_OP_breg13: + case DW_OP_breg14: + case DW_OP_breg15: + case DW_OP_breg16: + case DW_OP_breg17: + case DW_OP_breg18: + case DW_OP_breg19: + case DW_OP_breg20: + case DW_OP_breg21: + case DW_OP_breg22: + case DW_OP_breg23: + case DW_OP_breg24: + case DW_OP_breg25: + case DW_OP_breg26: + case DW_OP_breg27: + case DW_OP_breg28: + case DW_OP_breg29: + case DW_OP_breg30: + case DW_OP_breg31: + reg = static_cast(opcode - DW_OP_breg0); + svalue = (sint_t)addressSpace.getSLEB128(p, expressionEnd); + svalue += static_cast(registers.getRegister((int)reg)); + *(++sp) = (pint_t)(svalue); + if (log) + fprintf(stderr, "push reg %d + 0x%" PRIx64 "\n", reg, (uint64_t)svalue); + break; + + case DW_OP_bregx: + reg = static_cast(addressSpace.getULEB128(p, expressionEnd)); + svalue = (sint_t)addressSpace.getSLEB128(p, expressionEnd); + svalue += static_cast(registers.getRegister((int)reg)); + *(++sp) = (pint_t)(svalue); + if (log) + fprintf(stderr, "push reg %d + 0x%" PRIx64 "\n", reg, (uint64_t)svalue); + break; + + case DW_OP_fbreg: + _LIBUNWIND_ABORT("DW_OP_fbreg not implemented"); + break; + + case DW_OP_piece: + _LIBUNWIND_ABORT("DW_OP_piece not implemented"); + break; + + case DW_OP_deref_size: + // pop stack, dereference, push result + value = *sp--; + switch (addressSpace.get8(p++)) { + case 1: + value = addressSpace.get8(value); + break; + case 2: + value = addressSpace.get16(value); + break; + case 4: + value = addressSpace.get32(value); + break; + case 8: + value = (pint_t)addressSpace.get64(value); + break; + default: + _LIBUNWIND_ABORT("DW_OP_deref_size with bad size"); + } + *(++sp) = value; + if (log) + fprintf(stderr, "sized dereference 0x%" PRIx64 "\n", (uint64_t)value); + break; + + case DW_OP_xderef_size: + case DW_OP_nop: + case DW_OP_push_object_addres: + case DW_OP_call2: + case DW_OP_call4: + case DW_OP_call_ref: + default: + _LIBUNWIND_ABORT("DWARF opcode not implemented"); + } + + } + if (log) + fprintf(stderr, "expression evaluates to 0x%" PRIx64 "\n", (uint64_t)*sp); + return *sp; +} + + + +} // namespace libunwind + +#endif // __DWARF_INSTRUCTIONS_HPP__ diff --git a/shared/sentry/external/crashpad/libunwind/src/DwarfParser.hpp b/shared/sentry/external/crashpad/libunwind/src/DwarfParser.hpp new file mode 100644 index 000000000..2a7155ba9 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/src/DwarfParser.hpp @@ -0,0 +1,813 @@ +//===--------------------------- DwarfParser.hpp --------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Parses DWARF CFIs (FDEs and CIEs). +// +//===----------------------------------------------------------------------===// + +#ifndef __DWARF_PARSER_HPP__ +#define __DWARF_PARSER_HPP__ + +#include +#include +#include +#include + +#include "libunwind.h" +#include "dwarf2.h" +#include "Registers.hpp" + +#include "config.h" + +namespace libunwind { + +/// CFI_Parser does basic parsing of a CFI (Call Frame Information) records. +/// See DWARF Spec for details: +/// http://refspecs.linuxbase.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html +/// +template +class CFI_Parser { +public: + typedef typename A::pint_t pint_t; + + /// Information encoded in a CIE (Common Information Entry) + struct CIE_Info { + pint_t cieStart; + pint_t cieLength; + pint_t cieInstructions; + uint8_t pointerEncoding; + uint8_t lsdaEncoding; + uint8_t personalityEncoding; + uint8_t personalityOffsetInCIE; + pint_t personality; + uint32_t codeAlignFactor; + int dataAlignFactor; + bool isSignalFrame; + bool fdesHaveAugmentationData; + uint8_t returnAddressRegister; +#if defined(_LIBUNWIND_TARGET_AARCH64) + bool addressesSignedWithBKey; +#endif + }; + + /// Information about an FDE (Frame Description Entry) + struct FDE_Info { + pint_t fdeStart; + pint_t fdeLength; + pint_t fdeInstructions; + pint_t pcStart; + pint_t pcEnd; + pint_t lsda; + }; + + enum { + kMaxRegisterNumber = _LIBUNWIND_HIGHEST_DWARF_REGISTER + }; + enum RegisterSavedWhere { + kRegisterUnused, + kRegisterUndefined, + kRegisterInCFA, + kRegisterOffsetFromCFA, + kRegisterInRegister, + kRegisterAtExpression, + kRegisterIsExpression + }; + struct RegisterLocation { + RegisterSavedWhere location; + bool initialStateSaved; + int64_t value; + }; + /// Information about a frame layout and registers saved determined + /// by "running" the DWARF FDE "instructions" + struct PrologInfo { + uint32_t cfaRegister; + int32_t cfaRegisterOffset; // CFA = (cfaRegister)+cfaRegisterOffset + int64_t cfaExpression; // CFA = expression + uint32_t spExtraArgSize; + RegisterLocation savedRegisters[kMaxRegisterNumber + 1]; + enum class InitializeTime { kLazy, kNormal }; + + // When saving registers, this data structure is lazily initialized. + PrologInfo(InitializeTime IT = InitializeTime::kNormal) { + if (IT == InitializeTime::kNormal) + memset(this, 0, sizeof(*this)); + } + void checkSaveRegister(uint64_t reg, PrologInfo &initialState) { + if (!savedRegisters[reg].initialStateSaved) { + initialState.savedRegisters[reg] = savedRegisters[reg]; + savedRegisters[reg].initialStateSaved = true; + } + } + void setRegister(uint64_t reg, RegisterSavedWhere newLocation, + int64_t newValue, PrologInfo &initialState) { + checkSaveRegister(reg, initialState); + savedRegisters[reg].location = newLocation; + savedRegisters[reg].value = newValue; + } + void setRegisterLocation(uint64_t reg, RegisterSavedWhere newLocation, + PrologInfo &initialState) { + checkSaveRegister(reg, initialState); + savedRegisters[reg].location = newLocation; + } + void setRegisterValue(uint64_t reg, int64_t newValue, + PrologInfo &initialState) { + checkSaveRegister(reg, initialState); + savedRegisters[reg].value = newValue; + } + void restoreRegisterToInitialState(uint64_t reg, PrologInfo &initialState) { + if (savedRegisters[reg].initialStateSaved) + savedRegisters[reg] = initialState.savedRegisters[reg]; + // else the register still holds its initial state + } + }; + + struct PrologInfoStackEntry { + PrologInfoStackEntry(PrologInfoStackEntry *n, const PrologInfo &i) + : next(n), info(i) {} + PrologInfoStackEntry *next; + PrologInfo info; + }; + + struct RememberStack { + PrologInfoStackEntry *entry; + RememberStack() : entry(nullptr) {} + ~RememberStack() { +#if defined(_LIBUNWIND_REMEMBER_CLEANUP_NEEDED) + // Clean up rememberStack. Even in the case where every + // DW_CFA_remember_state is paired with a DW_CFA_restore_state, + // parseInstructions can skip restore opcodes if it reaches the target PC + // and stops interpreting, so we have to make sure we don't leak memory. + while (entry) { + PrologInfoStackEntry *next = entry->next; + _LIBUNWIND_REMEMBER_FREE(entry); + entry = next; + } +#endif + } + }; + + static bool findFDE(A &addressSpace, pint_t pc, pint_t ehSectionStart, + uintptr_t sectionLength, pint_t fdeHint, FDE_Info *fdeInfo, + CIE_Info *cieInfo); + static const char *decodeFDE(A &addressSpace, pint_t fdeStart, + FDE_Info *fdeInfo, CIE_Info *cieInfo); + static bool parseFDEInstructions(A &addressSpace, const FDE_Info &fdeInfo, + const CIE_Info &cieInfo, pint_t upToPC, + int arch, PrologInfo *results); + + static const char *parseCIE(A &addressSpace, pint_t cie, CIE_Info *cieInfo); +}; + +/// Parse a FDE into a CIE_Info and an FDE_Info +template +const char *CFI_Parser::decodeFDE(A &addressSpace, pint_t fdeStart, + FDE_Info *fdeInfo, CIE_Info *cieInfo) { + pint_t p = fdeStart; + pint_t cfiLength = (pint_t)addressSpace.get32(p); + p += 4; + if (cfiLength == 0xffffffff) { + // 0xffffffff means length is really next 8 bytes + cfiLength = (pint_t)addressSpace.get64(p); + p += 8; + } + if (cfiLength == 0) + return "FDE has zero length"; // zero terminator + uint32_t ciePointer = addressSpace.get32(p); + if (ciePointer == 0) + return "FDE is really a CIE"; // this is a CIE not an FDE + pint_t nextCFI = p + cfiLength; + pint_t cieStart = p - ciePointer; + const char *err = parseCIE(addressSpace, cieStart, cieInfo); + if (err != NULL) + return err; + p += 4; + // Parse pc begin and range. + pint_t pcStart = + addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding); + pint_t pcRange = + addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding & 0x0F); + // Parse rest of info. + fdeInfo->lsda = 0; + // Check for augmentation length. + if (cieInfo->fdesHaveAugmentationData) { + pint_t augLen = (pint_t)addressSpace.getULEB128(p, nextCFI); + pint_t endOfAug = p + augLen; + if (cieInfo->lsdaEncoding != DW_EH_PE_omit) { + // Peek at value (without indirection). Zero means no LSDA. + pint_t lsdaStart = p; + if (addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding & 0x0F) != + 0) { + // Reset pointer and re-parse LSDA address. + p = lsdaStart; + fdeInfo->lsda = + addressSpace.getEncodedP(p, nextCFI, cieInfo->lsdaEncoding); + } + } + p = endOfAug; + } + fdeInfo->fdeStart = fdeStart; + fdeInfo->fdeLength = nextCFI - fdeStart; + fdeInfo->fdeInstructions = p; + fdeInfo->pcStart = pcStart; + fdeInfo->pcEnd = pcStart + pcRange; + return NULL; // success +} + +/// Scan an eh_frame section to find an FDE for a pc +template +bool CFI_Parser::findFDE(A &addressSpace, pint_t pc, pint_t ehSectionStart, + uintptr_t sectionLength, pint_t fdeHint, + FDE_Info *fdeInfo, CIE_Info *cieInfo) { + //fprintf(stderr, "findFDE(0x%llX)\n", (long long)pc); + pint_t p = (fdeHint != 0) ? fdeHint : ehSectionStart; + const pint_t ehSectionEnd = (sectionLength == UINTPTR_MAX) + ? static_cast(-1) + : (ehSectionStart + sectionLength); + while (p < ehSectionEnd) { + pint_t currentCFI = p; + //fprintf(stderr, "findFDE() CFI at 0x%llX\n", (long long)p); + pint_t cfiLength = addressSpace.get32(p); + p += 4; + if (cfiLength == 0xffffffff) { + // 0xffffffff means length is really next 8 bytes + cfiLength = (pint_t)addressSpace.get64(p); + p += 8; + } + if (cfiLength == 0) + return false; // zero terminator + uint32_t id = addressSpace.get32(p); + if (id == 0) { + // Skip over CIEs. + p += cfiLength; + } else { + // Process FDE to see if it covers pc. + pint_t nextCFI = p + cfiLength; + uint32_t ciePointer = addressSpace.get32(p); + pint_t cieStart = p - ciePointer; + // Validate pointer to CIE is within section. + if ((ehSectionStart <= cieStart) && (cieStart < ehSectionEnd)) { + if (parseCIE(addressSpace, cieStart, cieInfo) == NULL) { + p += 4; + // Parse pc begin and range. + pint_t pcStart = + addressSpace.getEncodedP(p, nextCFI, cieInfo->pointerEncoding); + pint_t pcRange = addressSpace.getEncodedP( + p, nextCFI, cieInfo->pointerEncoding & 0x0F); + // Test if pc is within the function this FDE covers. + if ((pcStart < pc) && (pc <= pcStart + pcRange)) { + // parse rest of info + fdeInfo->lsda = 0; + // check for augmentation length + if (cieInfo->fdesHaveAugmentationData) { + pint_t augLen = (pint_t)addressSpace.getULEB128(p, nextCFI); + pint_t endOfAug = p + augLen; + if (cieInfo->lsdaEncoding != DW_EH_PE_omit) { + // Peek at value (without indirection). Zero means no LSDA. + pint_t lsdaStart = p; + if (addressSpace.getEncodedP( + p, nextCFI, cieInfo->lsdaEncoding & 0x0F) != 0) { + // Reset pointer and re-parse LSDA address. + p = lsdaStart; + fdeInfo->lsda = addressSpace + .getEncodedP(p, nextCFI, cieInfo->lsdaEncoding); + } + } + p = endOfAug; + } + fdeInfo->fdeStart = currentCFI; + fdeInfo->fdeLength = nextCFI - currentCFI; + fdeInfo->fdeInstructions = p; + fdeInfo->pcStart = pcStart; + fdeInfo->pcEnd = pcStart + pcRange; + return true; + } else { + // pc is not in begin/range, skip this FDE + } + } else { + // Malformed CIE, now augmentation describing pc range encoding. + } + } else { + // malformed FDE. CIE is bad + } + p = nextCFI; + } + } + return false; +} + +/// Extract info from a CIE +template +const char *CFI_Parser::parseCIE(A &addressSpace, pint_t cie, + CIE_Info *cieInfo) { + cieInfo->pointerEncoding = 0; + cieInfo->lsdaEncoding = DW_EH_PE_omit; + cieInfo->personalityEncoding = 0; + cieInfo->personalityOffsetInCIE = 0; + cieInfo->personality = 0; + cieInfo->codeAlignFactor = 0; + cieInfo->dataAlignFactor = 0; + cieInfo->isSignalFrame = false; + cieInfo->fdesHaveAugmentationData = false; +#if defined(_LIBUNWIND_TARGET_AARCH64) + cieInfo->addressesSignedWithBKey = false; +#endif + cieInfo->cieStart = cie; + pint_t p = cie; + pint_t cieLength = (pint_t)addressSpace.get32(p); + p += 4; + pint_t cieContentEnd = p + cieLength; + if (cieLength == 0xffffffff) { + // 0xffffffff means length is really next 8 bytes + cieLength = (pint_t)addressSpace.get64(p); + p += 8; + cieContentEnd = p + cieLength; + } + if (cieLength == 0) + return NULL; + // CIE ID is always 0 + if (addressSpace.get32(p) != 0) + return "CIE ID is not zero"; + p += 4; + // Version is always 1 or 3 + uint8_t version = addressSpace.get8(p); + if ((version != 1) && (version != 3)) + return "CIE version is not 1 or 3"; + ++p; + // save start of augmentation string and find end + pint_t strStart = p; + while (addressSpace.get8(p) != 0) + ++p; + ++p; + // parse code aligment factor + cieInfo->codeAlignFactor = (uint32_t)addressSpace.getULEB128(p, cieContentEnd); + // parse data alignment factor + cieInfo->dataAlignFactor = (int)addressSpace.getSLEB128(p, cieContentEnd); + // parse return address register + uint64_t raReg = (version == 1) ? addressSpace.get8(p++) + : addressSpace.getULEB128(p, cieContentEnd); + assert(raReg < 255 && "return address register too large"); + cieInfo->returnAddressRegister = (uint8_t)raReg; + // parse augmentation data based on augmentation string + const char *result = NULL; + if (addressSpace.get8(strStart) == 'z') { + // parse augmentation data length + addressSpace.getULEB128(p, cieContentEnd); + for (pint_t s = strStart; addressSpace.get8(s) != '\0'; ++s) { + switch (addressSpace.get8(s)) { + case 'z': + cieInfo->fdesHaveAugmentationData = true; + break; + case 'P': + cieInfo->personalityEncoding = addressSpace.get8(p); + ++p; + cieInfo->personalityOffsetInCIE = (uint8_t)(p - cie); + cieInfo->personality = addressSpace + .getEncodedP(p, cieContentEnd, cieInfo->personalityEncoding); + break; + case 'L': + cieInfo->lsdaEncoding = addressSpace.get8(p); + ++p; + break; + case 'R': + cieInfo->pointerEncoding = addressSpace.get8(p); + ++p; + break; + case 'S': + cieInfo->isSignalFrame = true; + break; +#if defined(_LIBUNWIND_TARGET_AARCH64) + case 'B': + cieInfo->addressesSignedWithBKey = true; + break; +#endif + default: + // ignore unknown letters + break; + } + } + } + cieInfo->cieLength = cieContentEnd - cieInfo->cieStart; + cieInfo->cieInstructions = p; + return result; +} + + +/// "run" the DWARF instructions and create the abstact PrologInfo for an FDE +template +bool CFI_Parser::parseFDEInstructions(A &addressSpace, + const FDE_Info &fdeInfo, + const CIE_Info &cieInfo, pint_t upToPC, + int arch, PrologInfo *results) { + // Alloca is used for the allocation of the rememberStack entries. It removes + // the dependency on new/malloc but the below for loop can not be refactored + // into functions. Entry could be saved during the processing of a CIE and + // restored by an FDE. + RememberStack rememberStack; + + struct ParseInfo { + pint_t instructions; + pint_t instructionsEnd; + pint_t pcoffset; + }; + + ParseInfo parseInfoArray[] = { + {cieInfo.cieInstructions, cieInfo.cieStart + cieInfo.cieLength, + (pint_t)(-1)}, + {fdeInfo.fdeInstructions, fdeInfo.fdeStart + fdeInfo.fdeLength, + upToPC - fdeInfo.pcStart}}; + + for (const auto &info : parseInfoArray) { + pint_t p = info.instructions; + pint_t instructionsEnd = info.instructionsEnd; + pint_t pcoffset = info.pcoffset; + pint_t codeOffset = 0; + + // initialState initialized as registers in results are modified. Use + // PrologInfo accessor functions to avoid reading uninitialized data. + PrologInfo initialState(PrologInfo::InitializeTime::kLazy); + + _LIBUNWIND_TRACE_DWARF("parseFDEInstructions(instructions=0x%0" PRIx64 + ")\n", + static_cast(instructionsEnd)); + + // see DWARF Spec, section 6.4.2 for details on unwind opcodes + while ((p < instructionsEnd) && (codeOffset < pcoffset)) { + uint64_t reg; + uint64_t reg2; + int64_t offset; + uint64_t length; + uint8_t opcode = addressSpace.get8(p); + uint8_t operand; + + ++p; + switch (opcode) { + case DW_CFA_nop: + _LIBUNWIND_TRACE_DWARF("DW_CFA_nop\n"); + break; + case DW_CFA_set_loc: + codeOffset = addressSpace.getEncodedP(p, instructionsEnd, + cieInfo.pointerEncoding); + _LIBUNWIND_TRACE_DWARF("DW_CFA_set_loc\n"); + break; + case DW_CFA_advance_loc1: + codeOffset += (addressSpace.get8(p) * cieInfo.codeAlignFactor); + p += 1; + _LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc1: new offset=%" PRIu64 "\n", + static_cast(codeOffset)); + break; + case DW_CFA_advance_loc2: + codeOffset += (addressSpace.get16(p) * cieInfo.codeAlignFactor); + p += 2; + _LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc2: new offset=%" PRIu64 "\n", + static_cast(codeOffset)); + break; + case DW_CFA_advance_loc4: + codeOffset += (addressSpace.get32(p) * cieInfo.codeAlignFactor); + p += 4; + _LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc4: new offset=%" PRIu64 "\n", + static_cast(codeOffset)); + break; + case DW_CFA_offset_extended: + reg = addressSpace.getULEB128(p, instructionsEnd); + offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) * + cieInfo.dataAlignFactor; + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG0( + "malformed DW_CFA_offset_extended DWARF unwind, reg too big"); + return false; + } + results->setRegister(reg, kRegisterInCFA, offset, initialState); + _LIBUNWIND_TRACE_DWARF("DW_CFA_offset_extended(reg=%" PRIu64 ", " + "offset=%" PRId64 ")\n", + reg, offset); + break; + case DW_CFA_restore_extended: + reg = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG0( + "malformed DW_CFA_restore_extended DWARF unwind, reg too big"); + return false; + } + results->restoreRegisterToInitialState(reg, initialState); + _LIBUNWIND_TRACE_DWARF("DW_CFA_restore_extended(reg=%" PRIu64 ")\n", + reg); + break; + case DW_CFA_undefined: + reg = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG0( + "malformed DW_CFA_undefined DWARF unwind, reg too big"); + return false; + } + results->setRegisterLocation(reg, kRegisterUndefined, initialState); + _LIBUNWIND_TRACE_DWARF("DW_CFA_undefined(reg=%" PRIu64 ")\n", reg); + break; + case DW_CFA_same_value: + reg = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG0( + "malformed DW_CFA_same_value DWARF unwind, reg too big"); + return false; + } + // DW_CFA_same_value unsupported + // "same value" means register was stored in frame, but its current + // value has not changed, so no need to restore from frame. + // We model this as if the register was never saved. + results->setRegisterLocation(reg, kRegisterUnused, initialState); + _LIBUNWIND_TRACE_DWARF("DW_CFA_same_value(reg=%" PRIu64 ")\n", reg); + break; + case DW_CFA_register: + reg = addressSpace.getULEB128(p, instructionsEnd); + reg2 = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG0( + "malformed DW_CFA_register DWARF unwind, reg too big"); + return false; + } + if (reg2 > kMaxRegisterNumber) { + _LIBUNWIND_LOG0( + "malformed DW_CFA_register DWARF unwind, reg2 too big"); + return false; + } + results->setRegister(reg, kRegisterInRegister, (int64_t)reg2, + initialState); + _LIBUNWIND_TRACE_DWARF( + "DW_CFA_register(reg=%" PRIu64 ", reg2=%" PRIu64 ")\n", reg, reg2); + break; + case DW_CFA_remember_state: { + // Avoid operator new because that would be an upward dependency. + // Avoid malloc because it needs heap allocation. + PrologInfoStackEntry *entry = + (PrologInfoStackEntry *)_LIBUNWIND_REMEMBER_ALLOC( + sizeof(PrologInfoStackEntry)); + if (entry != NULL) { + entry->next = rememberStack.entry; + entry->info = *results; + rememberStack.entry = entry; + } else { + return false; + } + _LIBUNWIND_TRACE_DWARF("DW_CFA_remember_state\n"); + break; + } + case DW_CFA_restore_state: + if (rememberStack.entry != NULL) { + PrologInfoStackEntry *top = rememberStack.entry; + *results = top->info; + rememberStack.entry = top->next; + _LIBUNWIND_REMEMBER_FREE(top); + } else { + return false; + } + _LIBUNWIND_TRACE_DWARF("DW_CFA_restore_state\n"); + break; + case DW_CFA_def_cfa: + reg = addressSpace.getULEB128(p, instructionsEnd); + offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG0("malformed DW_CFA_def_cfa DWARF unwind, reg too big"); + return false; + } + results->cfaRegister = (uint32_t)reg; + results->cfaRegisterOffset = (int32_t)offset; + _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa(reg=%" PRIu64 ", offset=%" PRIu64 + ")\n", + reg, offset); + break; + case DW_CFA_def_cfa_register: + reg = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG0( + "malformed DW_CFA_def_cfa_register DWARF unwind, reg too big"); + return false; + } + results->cfaRegister = (uint32_t)reg; + _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_register(%" PRIu64 ")\n", reg); + break; + case DW_CFA_def_cfa_offset: + results->cfaRegisterOffset = + (int32_t)addressSpace.getULEB128(p, instructionsEnd); + _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_offset(%d)\n", + results->cfaRegisterOffset); + break; + case DW_CFA_def_cfa_expression: + results->cfaRegister = 0; + results->cfaExpression = (int64_t)p; + length = addressSpace.getULEB128(p, instructionsEnd); + assert(length < static_cast(~0) && "pointer overflow"); + p += static_cast(length); + _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_expression(expression=0x%" PRIx64 + ", length=%" PRIu64 ")\n", + results->cfaExpression, length); + break; + case DW_CFA_expression: + reg = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG0( + "malformed DW_CFA_expression DWARF unwind, reg too big"); + return false; + } + results->setRegister(reg, kRegisterAtExpression, (int64_t)p, + initialState); + length = addressSpace.getULEB128(p, instructionsEnd); + assert(length < static_cast(~0) && "pointer overflow"); + p += static_cast(length); + _LIBUNWIND_TRACE_DWARF("DW_CFA_expression(reg=%" PRIu64 ", " + "expression=0x%" PRIx64 ", " + "length=%" PRIu64 ")\n", + reg, results->savedRegisters[reg].value, length); + break; + case DW_CFA_offset_extended_sf: + reg = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG0( + "malformed DW_CFA_offset_extended_sf DWARF unwind, reg too big"); + return false; + } + offset = addressSpace.getSLEB128(p, instructionsEnd) * + cieInfo.dataAlignFactor; + results->setRegister(reg, kRegisterInCFA, offset, initialState); + _LIBUNWIND_TRACE_DWARF("DW_CFA_offset_extended_sf(reg=%" PRIu64 ", " + "offset=%" PRId64 ")\n", + reg, offset); + break; + case DW_CFA_def_cfa_sf: + reg = addressSpace.getULEB128(p, instructionsEnd); + offset = addressSpace.getSLEB128(p, instructionsEnd) * + cieInfo.dataAlignFactor; + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG0( + "malformed DW_CFA_def_cfa_sf DWARF unwind, reg too big"); + return false; + } + results->cfaRegister = (uint32_t)reg; + results->cfaRegisterOffset = (int32_t)offset; + _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_sf(reg=%" PRIu64 ", " + "offset=%" PRId64 ")\n", + reg, offset); + break; + case DW_CFA_def_cfa_offset_sf: + results->cfaRegisterOffset = + (int32_t)(addressSpace.getSLEB128(p, instructionsEnd) * + cieInfo.dataAlignFactor); + _LIBUNWIND_TRACE_DWARF("DW_CFA_def_cfa_offset_sf(%d)\n", + results->cfaRegisterOffset); + break; + case DW_CFA_val_offset: + reg = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG( + "malformed DW_CFA_val_offset DWARF unwind, reg (%" PRIu64 + ") out of range\n", + reg); + return false; + } + offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) * + cieInfo.dataAlignFactor; + results->setRegister(reg, kRegisterOffsetFromCFA, offset, initialState); + _LIBUNWIND_TRACE_DWARF("DW_CFA_val_offset(reg=%" PRIu64 ", " + "offset=%" PRId64 "\n", + reg, offset); + break; + case DW_CFA_val_offset_sf: + reg = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG0( + "malformed DW_CFA_val_offset_sf DWARF unwind, reg too big"); + return false; + } + offset = addressSpace.getSLEB128(p, instructionsEnd) * + cieInfo.dataAlignFactor; + results->setRegister(reg, kRegisterOffsetFromCFA, offset, initialState); + _LIBUNWIND_TRACE_DWARF("DW_CFA_val_offset_sf(reg=%" PRIu64 ", " + "offset=%" PRId64 "\n", + reg, offset); + break; + case DW_CFA_val_expression: + reg = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG0( + "malformed DW_CFA_val_expression DWARF unwind, reg too big"); + return false; + } + results->setRegister(reg, kRegisterIsExpression, (int64_t)p, + initialState); + length = addressSpace.getULEB128(p, instructionsEnd); + assert(length < static_cast(~0) && "pointer overflow"); + p += static_cast(length); + _LIBUNWIND_TRACE_DWARF("DW_CFA_val_expression(reg=%" PRIu64 ", " + "expression=0x%" PRIx64 ", length=%" PRIu64 + ")\n", + reg, results->savedRegisters[reg].value, length); + break; + case DW_CFA_GNU_args_size: + length = addressSpace.getULEB128(p, instructionsEnd); + results->spExtraArgSize = (uint32_t)length; + _LIBUNWIND_TRACE_DWARF("DW_CFA_GNU_args_size(%" PRIu64 ")\n", length); + break; + case DW_CFA_GNU_negative_offset_extended: + reg = addressSpace.getULEB128(p, instructionsEnd); + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG0("malformed DW_CFA_GNU_negative_offset_extended DWARF " + "unwind, reg too big"); + return false; + } + offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) * + cieInfo.dataAlignFactor; + results->setRegister(reg, kRegisterInCFA, -offset, initialState); + _LIBUNWIND_TRACE_DWARF( + "DW_CFA_GNU_negative_offset_extended(%" PRId64 ")\n", offset); + break; + +#if defined(_LIBUNWIND_TARGET_AARCH64) || defined(_LIBUNWIND_TARGET_SPARC) + // The same constant is used to represent different instructions on + // AArch64 (negate_ra_state) and SPARC (window_save). + static_assert(DW_CFA_AARCH64_negate_ra_state == DW_CFA_GNU_window_save, + "uses the same constant"); + case DW_CFA_AARCH64_negate_ra_state: + switch (arch) { +#if defined(_LIBUNWIND_TARGET_AARCH64) + case REGISTERS_ARM64: { + int64_t value = + results->savedRegisters[UNW_AARCH64_RA_SIGN_STATE].value ^ 0x1; + results->setRegisterValue(UNW_AARCH64_RA_SIGN_STATE, value, + initialState); + _LIBUNWIND_TRACE_DWARF("DW_CFA_AARCH64_negate_ra_state\n"); + } break; +#endif + +#if defined(_LIBUNWIND_TARGET_SPARC) + // case DW_CFA_GNU_window_save: + case REGISTERS_SPARC: + _LIBUNWIND_TRACE_DWARF("DW_CFA_GNU_window_save()\n"); + for (reg = UNW_SPARC_O0; reg <= UNW_SPARC_O7; reg++) { + results->setRegister(reg, kRegisterInRegister, + ((int64_t)reg - UNW_SPARC_O0) + UNW_SPARC_I0, + initialState); + } + + for (reg = UNW_SPARC_L0; reg <= UNW_SPARC_I7; reg++) { + results->setRegister(reg, kRegisterInCFA, + ((int64_t)reg - UNW_SPARC_L0) * 4, + initialState); + } + break; +#endif + } + break; +#else + (void)arch; +#endif + + default: + operand = opcode & 0x3F; + switch (opcode & 0xC0) { + case DW_CFA_offset: + reg = operand; + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG("malformed DW_CFA_offset DWARF unwind, reg (%" PRIu64 + ") out of range", + reg); + return false; + } + offset = (int64_t)addressSpace.getULEB128(p, instructionsEnd) * + cieInfo.dataAlignFactor; + results->setRegister(reg, kRegisterInCFA, offset, initialState); + _LIBUNWIND_TRACE_DWARF("DW_CFA_offset(reg=%d, offset=%" PRId64 ")\n", + operand, offset); + break; + case DW_CFA_advance_loc: + codeOffset += operand * cieInfo.codeAlignFactor; + _LIBUNWIND_TRACE_DWARF("DW_CFA_advance_loc: new offset=%" PRIu64 "\n", + static_cast(codeOffset)); + break; + case DW_CFA_restore: + reg = operand; + if (reg > kMaxRegisterNumber) { + _LIBUNWIND_LOG( + "malformed DW_CFA_restore DWARF unwind, reg (%" PRIu64 + ") out of range", + reg); + return false; + } + results->restoreRegisterToInitialState(reg, initialState); + _LIBUNWIND_TRACE_DWARF("DW_CFA_restore(reg=%" PRIu64 ")\n", + static_cast(operand)); + break; + default: + _LIBUNWIND_TRACE_DWARF("unknown CFA opcode 0x%02X\n", opcode); + return false; + } + } + } + } + return true; +} + +} // namespace libunwind + +#endif // __DWARF_PARSER_HPP__ diff --git a/shared/sentry/external/crashpad/libunwind/src/EHHeaderParser.hpp b/shared/sentry/external/crashpad/libunwind/src/EHHeaderParser.hpp new file mode 100644 index 000000000..f97cca548 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/src/EHHeaderParser.hpp @@ -0,0 +1,169 @@ +//===------------------------- EHHeaderParser.hpp -------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Parses ELF .eh_frame_hdr sections. +// +//===----------------------------------------------------------------------===// + +#ifndef __EHHEADERPARSER_HPP__ +#define __EHHEADERPARSER_HPP__ + +#include "libunwind.h" + +#include "DwarfParser.hpp" + +namespace libunwind { + +/// \brief EHHeaderParser does basic parsing of an ELF .eh_frame_hdr section. +/// +/// See DWARF spec for details: +/// http://refspecs.linuxbase.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/ehframechpt.html +/// +template class EHHeaderParser { +public: + typedef typename A::pint_t pint_t; + + /// Information encoded in the EH frame header. + struct EHHeaderInfo { + pint_t eh_frame_ptr; + size_t fde_count; + pint_t table; + uint8_t table_enc; + }; + + static bool decodeEHHdr(A &addressSpace, pint_t ehHdrStart, pint_t ehHdrEnd, + EHHeaderInfo &ehHdrInfo); + static bool findFDE(A &addressSpace, pint_t pc, pint_t ehHdrStart, + uint32_t sectionLength, + typename CFI_Parser::FDE_Info *fdeInfo, + typename CFI_Parser::CIE_Info *cieInfo); + +private: + static bool decodeTableEntry(A &addressSpace, pint_t &tableEntry, + pint_t ehHdrStart, pint_t ehHdrEnd, + uint8_t tableEnc, + typename CFI_Parser::FDE_Info *fdeInfo, + typename CFI_Parser::CIE_Info *cieInfo); + static size_t getTableEntrySize(uint8_t tableEnc); +}; + +template +bool EHHeaderParser::decodeEHHdr(A &addressSpace, pint_t ehHdrStart, + pint_t ehHdrEnd, EHHeaderInfo &ehHdrInfo) { + pint_t p = ehHdrStart; + uint8_t version = addressSpace.get8(p++); + if (version != 1) { + _LIBUNWIND_LOG0("Unsupported .eh_frame_hdr version"); + return false; + } + + uint8_t eh_frame_ptr_enc = addressSpace.get8(p++); + uint8_t fde_count_enc = addressSpace.get8(p++); + ehHdrInfo.table_enc = addressSpace.get8(p++); + + ehHdrInfo.eh_frame_ptr = + addressSpace.getEncodedP(p, ehHdrEnd, eh_frame_ptr_enc, ehHdrStart); + ehHdrInfo.fde_count = + fde_count_enc == DW_EH_PE_omit + ? 0 + : addressSpace.getEncodedP(p, ehHdrEnd, fde_count_enc, ehHdrStart); + ehHdrInfo.table = p; + + return true; +} + +template +bool EHHeaderParser::decodeTableEntry( + A &addressSpace, pint_t &tableEntry, pint_t ehHdrStart, pint_t ehHdrEnd, + uint8_t tableEnc, typename CFI_Parser::FDE_Info *fdeInfo, + typename CFI_Parser::CIE_Info *cieInfo) { + // Have to decode the whole FDE for the PC range anyway, so just throw away + // the PC start. + addressSpace.getEncodedP(tableEntry, ehHdrEnd, tableEnc, ehHdrStart); + pint_t fde = + addressSpace.getEncodedP(tableEntry, ehHdrEnd, tableEnc, ehHdrStart); + const char *message = + CFI_Parser::decodeFDE(addressSpace, fde, fdeInfo, cieInfo); + if (message != NULL) { + _LIBUNWIND_DEBUG_LOG("EHHeaderParser::decodeTableEntry: bad fde: %s", + message); + return false; + } + + return true; +} + +template +bool EHHeaderParser::findFDE(A &addressSpace, pint_t pc, pint_t ehHdrStart, + uint32_t sectionLength, + typename CFI_Parser::FDE_Info *fdeInfo, + typename CFI_Parser::CIE_Info *cieInfo) { + pint_t ehHdrEnd = ehHdrStart + sectionLength; + + EHHeaderParser::EHHeaderInfo hdrInfo; + if (!EHHeaderParser::decodeEHHdr(addressSpace, ehHdrStart, ehHdrEnd, + hdrInfo)) + return false; + + if (hdrInfo.fde_count == 0) return false; + + size_t tableEntrySize = getTableEntrySize(hdrInfo.table_enc); + pint_t tableEntry; + + size_t low = 0; + for (size_t len = hdrInfo.fde_count; len > 1;) { + size_t mid = low + (len / 2); + tableEntry = hdrInfo.table + mid * tableEntrySize; + pint_t start = addressSpace.getEncodedP(tableEntry, ehHdrEnd, + hdrInfo.table_enc, ehHdrStart); + + if (start == pc) { + low = mid; + break; + } else if (start < pc) { + low = mid; + len -= (len / 2); + } else { + len /= 2; + } + } + + tableEntry = hdrInfo.table + low * tableEntrySize; + if (decodeTableEntry(addressSpace, tableEntry, ehHdrStart, ehHdrEnd, + hdrInfo.table_enc, fdeInfo, cieInfo)) { + if (pc >= fdeInfo->pcStart && pc < fdeInfo->pcEnd) + return true; + } + + return false; +} + +template +size_t EHHeaderParser::getTableEntrySize(uint8_t tableEnc) { + switch (tableEnc & 0x0f) { + case DW_EH_PE_sdata2: + case DW_EH_PE_udata2: + return 4; + case DW_EH_PE_sdata4: + case DW_EH_PE_udata4: + return 8; + case DW_EH_PE_sdata8: + case DW_EH_PE_udata8: + return 16; + case DW_EH_PE_sleb128: + case DW_EH_PE_uleb128: + _LIBUNWIND_ABORT("Can't binary search on variable length encoded data."); + case DW_EH_PE_omit: + return 0; + default: + _LIBUNWIND_ABORT("Unknown DWARF encoding for search table."); + } +} + +} + +#endif diff --git a/shared/sentry/external/crashpad/libunwind/src/FrameHeaderCache.hpp b/shared/sentry/external/crashpad/libunwind/src/FrameHeaderCache.hpp new file mode 100644 index 000000000..54d5d33c3 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/src/FrameHeaderCache.hpp @@ -0,0 +1,149 @@ +//===-FrameHeaderCache.hpp ------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Cache the elf program headers necessary to unwind the stack more efficiently +// in the presence of many dsos. +// +//===----------------------------------------------------------------------===// + +#ifndef __FRAMEHEADER_CACHE_HPP__ +#define __FRAMEHEADER_CACHE_HPP__ + +#include "config.h" +#include + +#ifdef _LIBUNWIND_DEBUG_FRAMEHEADER_CACHE +#define _LIBUNWIND_FRAMEHEADERCACHE_TRACE0(x) _LIBUNWIND_LOG0(x) +#define _LIBUNWIND_FRAMEHEADERCACHE_TRACE(msg, ...) \ + _LIBUNWIND_LOG(msg, __VA_ARGS__) +#else +#define _LIBUNWIND_FRAMEHEADERCACHE_TRACE0(x) +#define _LIBUNWIND_FRAMEHEADERCACHE_TRACE(msg, ...) +#endif + +// This cache should only be be used from within a dl_iterate_phdr callback. +// dl_iterate_phdr does the necessary synchronization to prevent problems +// with concurrent access via the libc load lock. Adding synchronization +// for other uses is possible, but not currently done. + +class _LIBUNWIND_HIDDEN FrameHeaderCache { + struct CacheEntry { + uintptr_t LowPC() { return Info.dso_base; }; + uintptr_t HighPC() { return Info.dso_base + Info.text_segment_length; }; + UnwindInfoSections Info; + CacheEntry *Next; + }; + + static const size_t kCacheEntryCount = 8; + + // Can't depend on the C++ standard library in libunwind, so use an array to + // allocate the entries, and two linked lists for ordering unused and recently + // used entries. FIXME: Would the the extra memory for a doubly-linked list + // be better than the runtime cost of traversing a very short singly-linked + // list on a cache miss? The entries themselves are all small and consecutive, + // so unlikely to cause page faults when following the pointers. The memory + // spent on additional pointers could also be spent on more entries. + + CacheEntry Entries[kCacheEntryCount]; + CacheEntry *MostRecentlyUsed; + CacheEntry *Unused; + + void resetCache() { + _LIBUNWIND_FRAMEHEADERCACHE_TRACE0("FrameHeaderCache reset"); + MostRecentlyUsed = nullptr; + Unused = &Entries[0]; + for (size_t i = 0; i < kCacheEntryCount - 1; i++) { + Entries[i].Next = &Entries[i + 1]; + } + Entries[kCacheEntryCount - 1].Next = nullptr; + } + + bool cacheNeedsReset(dl_phdr_info *PInfo) { + // C libraries increment dl_phdr_info.adds and dl_phdr_info.subs when + // loading and unloading shared libraries. If these values change between + // iterations of dl_iterate_phdr, then invalidate the cache. + + // These are static to avoid needing an initializer, and unsigned long long + // because that is their type within the extended dl_phdr_info. Initialize + // these to something extremely unlikely to be found upon the first call to + // dl_iterate_phdr. + static unsigned long long LastAdds = ULLONG_MAX; + static unsigned long long LastSubs = ULLONG_MAX; + if (PInfo->dlpi_adds != LastAdds || PInfo->dlpi_subs != LastSubs) { + // Resetting the entire cache is a big hammer, but this path is rare-- + // usually just on the very first call, when the cache is empty anyway--so + // added complexity doesn't buy much. + LastAdds = PInfo->dlpi_adds; + LastSubs = PInfo->dlpi_subs; + resetCache(); + return true; + } + return false; + } + +public: + bool find(dl_phdr_info *PInfo, size_t, void *data) { + if (cacheNeedsReset(PInfo) || MostRecentlyUsed == nullptr) + return false; + + auto *CBData = static_cast(data); + CacheEntry *Current = MostRecentlyUsed; + CacheEntry *Previous = nullptr; + while (Current != nullptr) { + _LIBUNWIND_FRAMEHEADERCACHE_TRACE( + "FrameHeaderCache check %lx in [%lx - %lx)", CBData->targetAddr, + Current->LowPC(), Current->HighPC()); + if (Current->LowPC() <= CBData->targetAddr && + CBData->targetAddr < Current->HighPC()) { + _LIBUNWIND_FRAMEHEADERCACHE_TRACE( + "FrameHeaderCache hit %lx in [%lx - %lx)", CBData->targetAddr, + Current->LowPC(), Current->HighPC()); + if (Previous) { + // If there is no Previous, then Current is already the + // MostRecentlyUsed, and no need to move it up. + Previous->Next = Current->Next; + Current->Next = MostRecentlyUsed; + MostRecentlyUsed = Current; + } + *CBData->sects = Current->Info; + return true; + } + Previous = Current; + Current = Current->Next; + } + _LIBUNWIND_FRAMEHEADERCACHE_TRACE("FrameHeaderCache miss for address %lx", + CBData->targetAddr); + return false; + } + + void add(const UnwindInfoSections *UIS) { + CacheEntry *Current = nullptr; + + if (Unused != nullptr) { + Current = Unused; + Unused = Unused->Next; + } else { + Current = MostRecentlyUsed; + CacheEntry *Previous = nullptr; + while (Current->Next != nullptr) { + Previous = Current; + Current = Current->Next; + } + Previous->Next = nullptr; + _LIBUNWIND_FRAMEHEADERCACHE_TRACE("FrameHeaderCache evict [%lx - %lx)", + Current->LowPC(), Current->HighPC()); + } + + Current->Info = *UIS; + Current->Next = MostRecentlyUsed; + MostRecentlyUsed = Current; + _LIBUNWIND_FRAMEHEADERCACHE_TRACE("FrameHeaderCache add [%lx - %lx)", + MostRecentlyUsed->LowPC(), + MostRecentlyUsed->HighPC()); + } +}; + +#endif // __FRAMEHEADER_CACHE_HPP__ diff --git a/shared/sentry/external/crashpad/libunwind/src/RWMutex.hpp b/shared/sentry/external/crashpad/libunwind/src/RWMutex.hpp new file mode 100644 index 000000000..fcd3f4967 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/src/RWMutex.hpp @@ -0,0 +1,114 @@ +//===----------------------------- Registers.hpp --------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Abstract interface to shared reader/writer log, hiding platform and +// configuration differences. +// +//===----------------------------------------------------------------------===// + +#ifndef __RWMUTEX_HPP__ +#define __RWMUTEX_HPP__ + +#if defined(_WIN32) +#include +#elif !defined(_LIBUNWIND_HAS_NO_THREADS) +#include +#if defined(__ELF__) && defined(_LIBUNWIND_LINK_PTHREAD_LIB) +#pragma comment(lib, "pthread") +#endif +#endif + +namespace libunwind { + +#if defined(_LIBUNWIND_HAS_NO_THREADS) + +class _LIBUNWIND_HIDDEN RWMutex { +public: + bool lock_shared() { return true; } + bool unlock_shared() { return true; } + bool lock() { return true; } + bool unlock() { return true; } +}; + +#elif defined(_WIN32) + +class _LIBUNWIND_HIDDEN RWMutex { +public: + bool lock_shared() { + AcquireSRWLockShared(&_lock); + return true; + } + bool unlock_shared() { + ReleaseSRWLockShared(&_lock); + return true; + } + bool lock() { + AcquireSRWLockExclusive(&_lock); + return true; + } + bool unlock() { + ReleaseSRWLockExclusive(&_lock); + return true; + } + +private: + SRWLOCK _lock = SRWLOCK_INIT; +}; + +#elif !defined(LIBUNWIND_USE_WEAK_PTHREAD) + +class _LIBUNWIND_HIDDEN RWMutex { +public: + bool lock_shared() { return pthread_rwlock_rdlock(&_lock) == 0; } + bool unlock_shared() { return pthread_rwlock_unlock(&_lock) == 0; } + bool lock() { return pthread_rwlock_wrlock(&_lock) == 0; } + bool unlock() { return pthread_rwlock_unlock(&_lock) == 0; } + +private: + pthread_rwlock_t _lock = PTHREAD_RWLOCK_INITIALIZER; +}; + +#else + +extern "C" int __attribute__((weak)) +pthread_create(pthread_t *thread, const pthread_attr_t *attr, + void *(*start_routine)(void *), void *arg); +extern "C" int __attribute__((weak)) +pthread_rwlock_rdlock(pthread_rwlock_t *lock); +extern "C" int __attribute__((weak)) +pthread_rwlock_wrlock(pthread_rwlock_t *lock); +extern "C" int __attribute__((weak)) +pthread_rwlock_unlock(pthread_rwlock_t *lock); + +// Calls to the locking functions are gated on pthread_create, and not the +// functions themselves, because the data structure should only be locked if +// another thread has been created. This is what similar libraries do. + +class _LIBUNWIND_HIDDEN RWMutex { +public: + bool lock_shared() { + return !pthread_create || (pthread_rwlock_rdlock(&_lock) == 0); + } + bool unlock_shared() { + return !pthread_create || (pthread_rwlock_unlock(&_lock) == 0); + } + bool lock() { + return !pthread_create || (pthread_rwlock_wrlock(&_lock) == 0); + } + bool unlock() { + return !pthread_create || (pthread_rwlock_unlock(&_lock) == 0); + } + +private: + pthread_rwlock_t _lock = PTHREAD_RWLOCK_INITIALIZER; +}; + +#endif + +} // namespace libunwind + +#endif // __RWMUTEX_HPP__ diff --git a/shared/sentry/external/crashpad/libunwind/src/Registers.hpp b/shared/sentry/external/crashpad/libunwind/src/Registers.hpp new file mode 100644 index 000000000..5e2f11fbe --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/src/Registers.hpp @@ -0,0 +1,4509 @@ +//===----------------------------- Registers.hpp --------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Models register sets for supported processors. +// +//===----------------------------------------------------------------------===// + +#ifndef __REGISTERS_HPP__ +#define __REGISTERS_HPP__ + +#include +#include + +#include "cet_unwind.h" +#include "config.h" +#include "libunwind.h" + +namespace libunwind { + +// For emulating 128-bit registers +struct v128 { uint32_t vec[4]; }; + +enum { + REGISTERS_X86, + REGISTERS_X86_64, + REGISTERS_PPC, + REGISTERS_PPC64, + REGISTERS_ARM64, + REGISTERS_ARM, + REGISTERS_OR1K, + REGISTERS_MIPS_O32, + REGISTERS_MIPS_NEWABI, + REGISTERS_SPARC, + REGISTERS_HEXAGON, + REGISTERS_RISCV, + REGISTERS_VE, +}; + +#if defined(_LIBUNWIND_TARGET_I386) +class _LIBUNWIND_HIDDEN Registers_x86; +extern "C" void __libunwind_Registers_x86_jumpto(Registers_x86 *); + +#if defined(_LIBUNWIND_USE_CET) +extern "C" void *__libunwind_cet_get_jump_target() { + return reinterpret_cast(&__libunwind_Registers_x86_jumpto); +} +#endif + +/// Registers_x86 holds the register state of a thread in a 32-bit intel +/// process. +class _LIBUNWIND_HIDDEN Registers_x86 { +public: + Registers_x86(); + Registers_x86(const void *registers); + + bool validRegister(int num) const; + uint32_t getRegister(int num) const; + void setRegister(int num, uint32_t value); + bool validFloatRegister(int) const { return false; } + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int) const { return false; } + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto() { __libunwind_Registers_x86_jumpto(this); } + static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_X86; } + static int getArch() { return REGISTERS_X86; } + + uint32_t getSP() const { return _registers.__esp; } + void setSP(uint32_t value) { _registers.__esp = value; } + uint32_t getIP() const { return _registers.__eip; } + void setIP(uint32_t value) { _registers.__eip = value; } + uint32_t getEBP() const { return _registers.__ebp; } + void setEBP(uint32_t value) { _registers.__ebp = value; } + uint32_t getEBX() const { return _registers.__ebx; } + void setEBX(uint32_t value) { _registers.__ebx = value; } + uint32_t getECX() const { return _registers.__ecx; } + void setECX(uint32_t value) { _registers.__ecx = value; } + uint32_t getEDX() const { return _registers.__edx; } + void setEDX(uint32_t value) { _registers.__edx = value; } + uint32_t getESI() const { return _registers.__esi; } + void setESI(uint32_t value) { _registers.__esi = value; } + uint32_t getEDI() const { return _registers.__edi; } + void setEDI(uint32_t value) { _registers.__edi = value; } + +private: + struct GPRs { + unsigned int __eax; + unsigned int __ebx; + unsigned int __ecx; + unsigned int __edx; + unsigned int __edi; + unsigned int __esi; + unsigned int __ebp; + unsigned int __esp; + unsigned int __ss; + unsigned int __eflags; + unsigned int __eip; + unsigned int __cs; + unsigned int __ds; + unsigned int __es; + unsigned int __fs; + unsigned int __gs; + }; + + GPRs _registers; +}; + +inline Registers_x86::Registers_x86(const void *registers) { + static_assert((check_fit::does_fit), + "x86 registers do not fit into unw_context_t"); + memcpy(&_registers, registers, sizeof(_registers)); +} + +inline Registers_x86::Registers_x86() { + memset(&_registers, 0, sizeof(_registers)); +} + +inline bool Registers_x86::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum > 7) + return false; + return true; +} + +inline uint32_t Registers_x86::getRegister(int regNum) const { + switch (regNum) { + case UNW_REG_IP: + return _registers.__eip; + case UNW_REG_SP: + return _registers.__esp; + case UNW_X86_EAX: + return _registers.__eax; + case UNW_X86_ECX: + return _registers.__ecx; + case UNW_X86_EDX: + return _registers.__edx; + case UNW_X86_EBX: + return _registers.__ebx; +#if !defined(__APPLE__) + case UNW_X86_ESP: +#else + case UNW_X86_EBP: +#endif + return _registers.__ebp; +#if !defined(__APPLE__) + case UNW_X86_EBP: +#else + case UNW_X86_ESP: +#endif + return _registers.__esp; + case UNW_X86_ESI: + return _registers.__esi; + case UNW_X86_EDI: + return _registers.__edi; + } + _LIBUNWIND_ABORT("unsupported x86 register"); +} + +inline void Registers_x86::setRegister(int regNum, uint32_t value) { + switch (regNum) { + case UNW_REG_IP: + _registers.__eip = value; + return; + case UNW_REG_SP: + _registers.__esp = value; + return; + case UNW_X86_EAX: + _registers.__eax = value; + return; + case UNW_X86_ECX: + _registers.__ecx = value; + return; + case UNW_X86_EDX: + _registers.__edx = value; + return; + case UNW_X86_EBX: + _registers.__ebx = value; + return; +#if !defined(__APPLE__) + case UNW_X86_ESP: +#else + case UNW_X86_EBP: +#endif + _registers.__ebp = value; + return; +#if !defined(__APPLE__) + case UNW_X86_EBP: +#else + case UNW_X86_ESP: +#endif + _registers.__esp = value; + return; + case UNW_X86_ESI: + _registers.__esi = value; + return; + case UNW_X86_EDI: + _registers.__edi = value; + return; + } + _LIBUNWIND_ABORT("unsupported x86 register"); +} + +inline const char *Registers_x86::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + return "ip"; + case UNW_REG_SP: + return "esp"; + case UNW_X86_EAX: + return "eax"; + case UNW_X86_ECX: + return "ecx"; + case UNW_X86_EDX: + return "edx"; + case UNW_X86_EBX: + return "ebx"; + case UNW_X86_EBP: + return "ebp"; + case UNW_X86_ESP: + return "esp"; + case UNW_X86_ESI: + return "esi"; + case UNW_X86_EDI: + return "edi"; + default: + return "unknown register"; + } +} + +inline double Registers_x86::getFloatRegister(int) const { + _LIBUNWIND_ABORT("no x86 float registers"); +} + +inline void Registers_x86::setFloatRegister(int, double) { + _LIBUNWIND_ABORT("no x86 float registers"); +} + +inline v128 Registers_x86::getVectorRegister(int) const { + _LIBUNWIND_ABORT("no x86 vector registers"); +} + +inline void Registers_x86::setVectorRegister(int, v128) { + _LIBUNWIND_ABORT("no x86 vector registers"); +} +#endif // _LIBUNWIND_TARGET_I386 + + +#if defined(_LIBUNWIND_TARGET_X86_64) +/// Registers_x86_64 holds the register state of a thread in a 64-bit intel +/// process. +class _LIBUNWIND_HIDDEN Registers_x86_64; +extern "C" void __libunwind_Registers_x86_64_jumpto(Registers_x86_64 *); + +#if defined(_LIBUNWIND_USE_CET) +extern "C" void *__libunwind_cet_get_jump_target() { + return reinterpret_cast(&__libunwind_Registers_x86_64_jumpto); +} +#endif + +class _LIBUNWIND_HIDDEN Registers_x86_64 { +public: + Registers_x86_64(); + Registers_x86_64(const void *registers); + + bool validRegister(int num) const; + uint64_t getRegister(int num) const; + void setRegister(int num, uint64_t value); + bool validFloatRegister(int) const { return false; } + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto() { __libunwind_Registers_x86_64_jumpto(this); } + static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_X86_64; } + static int getArch() { return REGISTERS_X86_64; } + + uint64_t getSP() const { return _registers.__rsp; } + void setSP(uint64_t value) { _registers.__rsp = value; } + uint64_t getIP() const { return _registers.__rip; } + void setIP(uint64_t value) { _registers.__rip = value; } + uint64_t getRBP() const { return _registers.__rbp; } + void setRBP(uint64_t value) { _registers.__rbp = value; } + uint64_t getRBX() const { return _registers.__rbx; } + void setRBX(uint64_t value) { _registers.__rbx = value; } + uint64_t getR12() const { return _registers.__r12; } + void setR12(uint64_t value) { _registers.__r12 = value; } + uint64_t getR13() const { return _registers.__r13; } + void setR13(uint64_t value) { _registers.__r13 = value; } + uint64_t getR14() const { return _registers.__r14; } + void setR14(uint64_t value) { _registers.__r14 = value; } + uint64_t getR15() const { return _registers.__r15; } + void setR15(uint64_t value) { _registers.__r15 = value; } + +private: + struct GPRs { + uint64_t __rax; + uint64_t __rbx; + uint64_t __rcx; + uint64_t __rdx; + uint64_t __rdi; + uint64_t __rsi; + uint64_t __rbp; + uint64_t __rsp; + uint64_t __r8; + uint64_t __r9; + uint64_t __r10; + uint64_t __r11; + uint64_t __r12; + uint64_t __r13; + uint64_t __r14; + uint64_t __r15; + uint64_t __rip; + uint64_t __rflags; + uint64_t __cs; + uint64_t __fs; + uint64_t __gs; +#if defined(_WIN64) + uint64_t __padding; // 16-byte align +#endif + }; + GPRs _registers; +#if defined(_WIN64) + v128 _xmm[16]; +#endif +}; + +inline Registers_x86_64::Registers_x86_64(const void *registers) { + static_assert((check_fit::does_fit), + "x86_64 registers do not fit into unw_context_t"); + memcpy(&_registers, registers, sizeof(_registers)); +} + +inline Registers_x86_64::Registers_x86_64() { + memset(&_registers, 0, sizeof(_registers)); +} + +inline bool Registers_x86_64::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum > 16) + return false; + return true; +} + +inline uint64_t Registers_x86_64::getRegister(int regNum) const { + switch (regNum) { + case UNW_REG_IP: + case UNW_X86_64_RIP: + return _registers.__rip; + case UNW_REG_SP: + return _registers.__rsp; + case UNW_X86_64_RAX: + return _registers.__rax; + case UNW_X86_64_RDX: + return _registers.__rdx; + case UNW_X86_64_RCX: + return _registers.__rcx; + case UNW_X86_64_RBX: + return _registers.__rbx; + case UNW_X86_64_RSI: + return _registers.__rsi; + case UNW_X86_64_RDI: + return _registers.__rdi; + case UNW_X86_64_RBP: + return _registers.__rbp; + case UNW_X86_64_RSP: + return _registers.__rsp; + case UNW_X86_64_R8: + return _registers.__r8; + case UNW_X86_64_R9: + return _registers.__r9; + case UNW_X86_64_R10: + return _registers.__r10; + case UNW_X86_64_R11: + return _registers.__r11; + case UNW_X86_64_R12: + return _registers.__r12; + case UNW_X86_64_R13: + return _registers.__r13; + case UNW_X86_64_R14: + return _registers.__r14; + case UNW_X86_64_R15: + return _registers.__r15; + } + _LIBUNWIND_ABORT("unsupported x86_64 register"); +} + +inline void Registers_x86_64::setRegister(int regNum, uint64_t value) { + switch (regNum) { + case UNW_REG_IP: + case UNW_X86_64_RIP: + _registers.__rip = value; + return; + case UNW_REG_SP: + _registers.__rsp = value; + return; + case UNW_X86_64_RAX: + _registers.__rax = value; + return; + case UNW_X86_64_RDX: + _registers.__rdx = value; + return; + case UNW_X86_64_RCX: + _registers.__rcx = value; + return; + case UNW_X86_64_RBX: + _registers.__rbx = value; + return; + case UNW_X86_64_RSI: + _registers.__rsi = value; + return; + case UNW_X86_64_RDI: + _registers.__rdi = value; + return; + case UNW_X86_64_RBP: + _registers.__rbp = value; + return; + case UNW_X86_64_RSP: + _registers.__rsp = value; + return; + case UNW_X86_64_R8: + _registers.__r8 = value; + return; + case UNW_X86_64_R9: + _registers.__r9 = value; + return; + case UNW_X86_64_R10: + _registers.__r10 = value; + return; + case UNW_X86_64_R11: + _registers.__r11 = value; + return; + case UNW_X86_64_R12: + _registers.__r12 = value; + return; + case UNW_X86_64_R13: + _registers.__r13 = value; + return; + case UNW_X86_64_R14: + _registers.__r14 = value; + return; + case UNW_X86_64_R15: + _registers.__r15 = value; + return; + } + _LIBUNWIND_ABORT("unsupported x86_64 register"); +} + +inline const char *Registers_x86_64::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + case UNW_X86_64_RIP: + return "rip"; + case UNW_REG_SP: + return "rsp"; + case UNW_X86_64_RAX: + return "rax"; + case UNW_X86_64_RDX: + return "rdx"; + case UNW_X86_64_RCX: + return "rcx"; + case UNW_X86_64_RBX: + return "rbx"; + case UNW_X86_64_RSI: + return "rsi"; + case UNW_X86_64_RDI: + return "rdi"; + case UNW_X86_64_RBP: + return "rbp"; + case UNW_X86_64_RSP: + return "rsp"; + case UNW_X86_64_R8: + return "r8"; + case UNW_X86_64_R9: + return "r9"; + case UNW_X86_64_R10: + return "r10"; + case UNW_X86_64_R11: + return "r11"; + case UNW_X86_64_R12: + return "r12"; + case UNW_X86_64_R13: + return "r13"; + case UNW_X86_64_R14: + return "r14"; + case UNW_X86_64_R15: + return "r15"; + case UNW_X86_64_XMM0: + return "xmm0"; + case UNW_X86_64_XMM1: + return "xmm1"; + case UNW_X86_64_XMM2: + return "xmm2"; + case UNW_X86_64_XMM3: + return "xmm3"; + case UNW_X86_64_XMM4: + return "xmm4"; + case UNW_X86_64_XMM5: + return "xmm5"; + case UNW_X86_64_XMM6: + return "xmm6"; + case UNW_X86_64_XMM7: + return "xmm7"; + case UNW_X86_64_XMM8: + return "xmm8"; + case UNW_X86_64_XMM9: + return "xmm9"; + case UNW_X86_64_XMM10: + return "xmm10"; + case UNW_X86_64_XMM11: + return "xmm11"; + case UNW_X86_64_XMM12: + return "xmm12"; + case UNW_X86_64_XMM13: + return "xmm13"; + case UNW_X86_64_XMM14: + return "xmm14"; + case UNW_X86_64_XMM15: + return "xmm15"; + default: + return "unknown register"; + } +} + +inline double Registers_x86_64::getFloatRegister(int) const { + _LIBUNWIND_ABORT("no x86_64 float registers"); +} + +inline void Registers_x86_64::setFloatRegister(int, double) { + _LIBUNWIND_ABORT("no x86_64 float registers"); +} + +inline bool Registers_x86_64::validVectorRegister(int regNum) const { +#if defined(_WIN64) + if (regNum < UNW_X86_64_XMM0) + return false; + if (regNum > UNW_X86_64_XMM15) + return false; + return true; +#else + (void)regNum; // suppress unused parameter warning + return false; +#endif +} + +inline v128 Registers_x86_64::getVectorRegister(int regNum) const { +#if defined(_WIN64) + assert(validVectorRegister(regNum)); + return _xmm[regNum - UNW_X86_64_XMM0]; +#else + (void)regNum; // suppress unused parameter warning + _LIBUNWIND_ABORT("no x86_64 vector registers"); +#endif +} + +inline void Registers_x86_64::setVectorRegister(int regNum, v128 value) { +#if defined(_WIN64) + assert(validVectorRegister(regNum)); + _xmm[regNum - UNW_X86_64_XMM0] = value; +#else + (void)regNum; (void)value; // suppress unused parameter warnings + _LIBUNWIND_ABORT("no x86_64 vector registers"); +#endif +} +#endif // _LIBUNWIND_TARGET_X86_64 + + +#if defined(_LIBUNWIND_TARGET_PPC) +/// Registers_ppc holds the register state of a thread in a 32-bit PowerPC +/// process. +class _LIBUNWIND_HIDDEN Registers_ppc { +public: + Registers_ppc(); + Registers_ppc(const void *registers); + + bool validRegister(int num) const; + uint32_t getRegister(int num) const; + void setRegister(int num, uint32_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto(); + static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC; } + static int getArch() { return REGISTERS_PPC; } + + uint64_t getSP() const { return _registers.__r1; } + void setSP(uint32_t value) { _registers.__r1 = value; } + uint64_t getIP() const { return _registers.__srr0; } + void setIP(uint32_t value) { _registers.__srr0 = value; } + +private: + struct ppc_thread_state_t { + unsigned int __srr0; /* Instruction address register (PC) */ + unsigned int __srr1; /* Machine state register (supervisor) */ + unsigned int __r0; + unsigned int __r1; + unsigned int __r2; + unsigned int __r3; + unsigned int __r4; + unsigned int __r5; + unsigned int __r6; + unsigned int __r7; + unsigned int __r8; + unsigned int __r9; + unsigned int __r10; + unsigned int __r11; + unsigned int __r12; + unsigned int __r13; + unsigned int __r14; + unsigned int __r15; + unsigned int __r16; + unsigned int __r17; + unsigned int __r18; + unsigned int __r19; + unsigned int __r20; + unsigned int __r21; + unsigned int __r22; + unsigned int __r23; + unsigned int __r24; + unsigned int __r25; + unsigned int __r26; + unsigned int __r27; + unsigned int __r28; + unsigned int __r29; + unsigned int __r30; + unsigned int __r31; + unsigned int __cr; /* Condition register */ + unsigned int __xer; /* User's integer exception register */ + unsigned int __lr; /* Link register */ + unsigned int __ctr; /* Count register */ + unsigned int __mq; /* MQ register (601 only) */ + unsigned int __vrsave; /* Vector Save Register */ + }; + + struct ppc_float_state_t { + double __fpregs[32]; + + unsigned int __fpscr_pad; /* fpscr is 64 bits, 32 bits of rubbish */ + unsigned int __fpscr; /* floating point status register */ + }; + + ppc_thread_state_t _registers; + ppc_float_state_t _floatRegisters; + v128 _vectorRegisters[32]; // offset 424 +}; + +inline Registers_ppc::Registers_ppc(const void *registers) { + static_assert((check_fit::does_fit), + "ppc registers do not fit into unw_context_t"); + memcpy(&_registers, static_cast(registers), + sizeof(_registers)); + static_assert(sizeof(ppc_thread_state_t) == 160, + "expected float register offset to be 160"); + memcpy(&_floatRegisters, + static_cast(registers) + sizeof(ppc_thread_state_t), + sizeof(_floatRegisters)); + static_assert(sizeof(ppc_thread_state_t) + sizeof(ppc_float_state_t) == 424, + "expected vector register offset to be 424 bytes"); + memcpy(_vectorRegisters, + static_cast(registers) + sizeof(ppc_thread_state_t) + + sizeof(ppc_float_state_t), + sizeof(_vectorRegisters)); +} + +inline Registers_ppc::Registers_ppc() { + memset(&_registers, 0, sizeof(_registers)); + memset(&_floatRegisters, 0, sizeof(_floatRegisters)); + memset(&_vectorRegisters, 0, sizeof(_vectorRegisters)); +} + +inline bool Registers_ppc::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum == UNW_PPC_VRSAVE) + return true; + if (regNum < 0) + return false; + if (regNum <= UNW_PPC_R31) + return true; + if (regNum == UNW_PPC_MQ) + return true; + if (regNum == UNW_PPC_LR) + return true; + if (regNum == UNW_PPC_CTR) + return true; + if ((UNW_PPC_CR0 <= regNum) && (regNum <= UNW_PPC_CR7)) + return true; + return false; +} + +inline uint32_t Registers_ppc::getRegister(int regNum) const { + switch (regNum) { + case UNW_REG_IP: + return _registers.__srr0; + case UNW_REG_SP: + return _registers.__r1; + case UNW_PPC_R0: + return _registers.__r0; + case UNW_PPC_R1: + return _registers.__r1; + case UNW_PPC_R2: + return _registers.__r2; + case UNW_PPC_R3: + return _registers.__r3; + case UNW_PPC_R4: + return _registers.__r4; + case UNW_PPC_R5: + return _registers.__r5; + case UNW_PPC_R6: + return _registers.__r6; + case UNW_PPC_R7: + return _registers.__r7; + case UNW_PPC_R8: + return _registers.__r8; + case UNW_PPC_R9: + return _registers.__r9; + case UNW_PPC_R10: + return _registers.__r10; + case UNW_PPC_R11: + return _registers.__r11; + case UNW_PPC_R12: + return _registers.__r12; + case UNW_PPC_R13: + return _registers.__r13; + case UNW_PPC_R14: + return _registers.__r14; + case UNW_PPC_R15: + return _registers.__r15; + case UNW_PPC_R16: + return _registers.__r16; + case UNW_PPC_R17: + return _registers.__r17; + case UNW_PPC_R18: + return _registers.__r18; + case UNW_PPC_R19: + return _registers.__r19; + case UNW_PPC_R20: + return _registers.__r20; + case UNW_PPC_R21: + return _registers.__r21; + case UNW_PPC_R22: + return _registers.__r22; + case UNW_PPC_R23: + return _registers.__r23; + case UNW_PPC_R24: + return _registers.__r24; + case UNW_PPC_R25: + return _registers.__r25; + case UNW_PPC_R26: + return _registers.__r26; + case UNW_PPC_R27: + return _registers.__r27; + case UNW_PPC_R28: + return _registers.__r28; + case UNW_PPC_R29: + return _registers.__r29; + case UNW_PPC_R30: + return _registers.__r30; + case UNW_PPC_R31: + return _registers.__r31; + case UNW_PPC_LR: + return _registers.__lr; + case UNW_PPC_CR0: + return (_registers.__cr & 0xF0000000); + case UNW_PPC_CR1: + return (_registers.__cr & 0x0F000000); + case UNW_PPC_CR2: + return (_registers.__cr & 0x00F00000); + case UNW_PPC_CR3: + return (_registers.__cr & 0x000F0000); + case UNW_PPC_CR4: + return (_registers.__cr & 0x0000F000); + case UNW_PPC_CR5: + return (_registers.__cr & 0x00000F00); + case UNW_PPC_CR6: + return (_registers.__cr & 0x000000F0); + case UNW_PPC_CR7: + return (_registers.__cr & 0x0000000F); + case UNW_PPC_VRSAVE: + return _registers.__vrsave; + } + _LIBUNWIND_ABORT("unsupported ppc register"); +} + +inline void Registers_ppc::setRegister(int regNum, uint32_t value) { + //fprintf(stderr, "Registers_ppc::setRegister(%d, 0x%08X)\n", regNum, value); + switch (regNum) { + case UNW_REG_IP: + _registers.__srr0 = value; + return; + case UNW_REG_SP: + _registers.__r1 = value; + return; + case UNW_PPC_R0: + _registers.__r0 = value; + return; + case UNW_PPC_R1: + _registers.__r1 = value; + return; + case UNW_PPC_R2: + _registers.__r2 = value; + return; + case UNW_PPC_R3: + _registers.__r3 = value; + return; + case UNW_PPC_R4: + _registers.__r4 = value; + return; + case UNW_PPC_R5: + _registers.__r5 = value; + return; + case UNW_PPC_R6: + _registers.__r6 = value; + return; + case UNW_PPC_R7: + _registers.__r7 = value; + return; + case UNW_PPC_R8: + _registers.__r8 = value; + return; + case UNW_PPC_R9: + _registers.__r9 = value; + return; + case UNW_PPC_R10: + _registers.__r10 = value; + return; + case UNW_PPC_R11: + _registers.__r11 = value; + return; + case UNW_PPC_R12: + _registers.__r12 = value; + return; + case UNW_PPC_R13: + _registers.__r13 = value; + return; + case UNW_PPC_R14: + _registers.__r14 = value; + return; + case UNW_PPC_R15: + _registers.__r15 = value; + return; + case UNW_PPC_R16: + _registers.__r16 = value; + return; + case UNW_PPC_R17: + _registers.__r17 = value; + return; + case UNW_PPC_R18: + _registers.__r18 = value; + return; + case UNW_PPC_R19: + _registers.__r19 = value; + return; + case UNW_PPC_R20: + _registers.__r20 = value; + return; + case UNW_PPC_R21: + _registers.__r21 = value; + return; + case UNW_PPC_R22: + _registers.__r22 = value; + return; + case UNW_PPC_R23: + _registers.__r23 = value; + return; + case UNW_PPC_R24: + _registers.__r24 = value; + return; + case UNW_PPC_R25: + _registers.__r25 = value; + return; + case UNW_PPC_R26: + _registers.__r26 = value; + return; + case UNW_PPC_R27: + _registers.__r27 = value; + return; + case UNW_PPC_R28: + _registers.__r28 = value; + return; + case UNW_PPC_R29: + _registers.__r29 = value; + return; + case UNW_PPC_R30: + _registers.__r30 = value; + return; + case UNW_PPC_R31: + _registers.__r31 = value; + return; + case UNW_PPC_MQ: + _registers.__mq = value; + return; + case UNW_PPC_LR: + _registers.__lr = value; + return; + case UNW_PPC_CTR: + _registers.__ctr = value; + return; + case UNW_PPC_CR0: + _registers.__cr &= 0x0FFFFFFF; + _registers.__cr |= (value & 0xF0000000); + return; + case UNW_PPC_CR1: + _registers.__cr &= 0xF0FFFFFF; + _registers.__cr |= (value & 0x0F000000); + return; + case UNW_PPC_CR2: + _registers.__cr &= 0xFF0FFFFF; + _registers.__cr |= (value & 0x00F00000); + return; + case UNW_PPC_CR3: + _registers.__cr &= 0xFFF0FFFF; + _registers.__cr |= (value & 0x000F0000); + return; + case UNW_PPC_CR4: + _registers.__cr &= 0xFFFF0FFF; + _registers.__cr |= (value & 0x0000F000); + return; + case UNW_PPC_CR5: + _registers.__cr &= 0xFFFFF0FF; + _registers.__cr |= (value & 0x00000F00); + return; + case UNW_PPC_CR6: + _registers.__cr &= 0xFFFFFF0F; + _registers.__cr |= (value & 0x000000F0); + return; + case UNW_PPC_CR7: + _registers.__cr &= 0xFFFFFFF0; + _registers.__cr |= (value & 0x0000000F); + return; + case UNW_PPC_VRSAVE: + _registers.__vrsave = value; + return; + // not saved + return; + case UNW_PPC_XER: + _registers.__xer = value; + return; + case UNW_PPC_AP: + case UNW_PPC_VSCR: + case UNW_PPC_SPEFSCR: + // not saved + return; + } + _LIBUNWIND_ABORT("unsupported ppc register"); +} + +inline bool Registers_ppc::validFloatRegister(int regNum) const { + if (regNum < UNW_PPC_F0) + return false; + if (regNum > UNW_PPC_F31) + return false; + return true; +} + +inline double Registers_ppc::getFloatRegister(int regNum) const { + assert(validFloatRegister(regNum)); + return _floatRegisters.__fpregs[regNum - UNW_PPC_F0]; +} + +inline void Registers_ppc::setFloatRegister(int regNum, double value) { + assert(validFloatRegister(regNum)); + _floatRegisters.__fpregs[regNum - UNW_PPC_F0] = value; +} + +inline bool Registers_ppc::validVectorRegister(int regNum) const { + if (regNum < UNW_PPC_V0) + return false; + if (regNum > UNW_PPC_V31) + return false; + return true; +} + +inline v128 Registers_ppc::getVectorRegister(int regNum) const { + assert(validVectorRegister(regNum)); + v128 result = _vectorRegisters[regNum - UNW_PPC_V0]; + return result; +} + +inline void Registers_ppc::setVectorRegister(int regNum, v128 value) { + assert(validVectorRegister(regNum)); + _vectorRegisters[regNum - UNW_PPC_V0] = value; +} + +inline const char *Registers_ppc::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + return "ip"; + case UNW_REG_SP: + return "sp"; + case UNW_PPC_R0: + return "r0"; + case UNW_PPC_R1: + return "r1"; + case UNW_PPC_R2: + return "r2"; + case UNW_PPC_R3: + return "r3"; + case UNW_PPC_R4: + return "r4"; + case UNW_PPC_R5: + return "r5"; + case UNW_PPC_R6: + return "r6"; + case UNW_PPC_R7: + return "r7"; + case UNW_PPC_R8: + return "r8"; + case UNW_PPC_R9: + return "r9"; + case UNW_PPC_R10: + return "r10"; + case UNW_PPC_R11: + return "r11"; + case UNW_PPC_R12: + return "r12"; + case UNW_PPC_R13: + return "r13"; + case UNW_PPC_R14: + return "r14"; + case UNW_PPC_R15: + return "r15"; + case UNW_PPC_R16: + return "r16"; + case UNW_PPC_R17: + return "r17"; + case UNW_PPC_R18: + return "r18"; + case UNW_PPC_R19: + return "r19"; + case UNW_PPC_R20: + return "r20"; + case UNW_PPC_R21: + return "r21"; + case UNW_PPC_R22: + return "r22"; + case UNW_PPC_R23: + return "r23"; + case UNW_PPC_R24: + return "r24"; + case UNW_PPC_R25: + return "r25"; + case UNW_PPC_R26: + return "r26"; + case UNW_PPC_R27: + return "r27"; + case UNW_PPC_R28: + return "r28"; + case UNW_PPC_R29: + return "r29"; + case UNW_PPC_R30: + return "r30"; + case UNW_PPC_R31: + return "r31"; + case UNW_PPC_F0: + return "fp0"; + case UNW_PPC_F1: + return "fp1"; + case UNW_PPC_F2: + return "fp2"; + case UNW_PPC_F3: + return "fp3"; + case UNW_PPC_F4: + return "fp4"; + case UNW_PPC_F5: + return "fp5"; + case UNW_PPC_F6: + return "fp6"; + case UNW_PPC_F7: + return "fp7"; + case UNW_PPC_F8: + return "fp8"; + case UNW_PPC_F9: + return "fp9"; + case UNW_PPC_F10: + return "fp10"; + case UNW_PPC_F11: + return "fp11"; + case UNW_PPC_F12: + return "fp12"; + case UNW_PPC_F13: + return "fp13"; + case UNW_PPC_F14: + return "fp14"; + case UNW_PPC_F15: + return "fp15"; + case UNW_PPC_F16: + return "fp16"; + case UNW_PPC_F17: + return "fp17"; + case UNW_PPC_F18: + return "fp18"; + case UNW_PPC_F19: + return "fp19"; + case UNW_PPC_F20: + return "fp20"; + case UNW_PPC_F21: + return "fp21"; + case UNW_PPC_F22: + return "fp22"; + case UNW_PPC_F23: + return "fp23"; + case UNW_PPC_F24: + return "fp24"; + case UNW_PPC_F25: + return "fp25"; + case UNW_PPC_F26: + return "fp26"; + case UNW_PPC_F27: + return "fp27"; + case UNW_PPC_F28: + return "fp28"; + case UNW_PPC_F29: + return "fp29"; + case UNW_PPC_F30: + return "fp30"; + case UNW_PPC_F31: + return "fp31"; + case UNW_PPC_LR: + return "lr"; + default: + return "unknown register"; + } + +} +#endif // _LIBUNWIND_TARGET_PPC + +#if defined(_LIBUNWIND_TARGET_PPC64) +/// Registers_ppc64 holds the register state of a thread in a 64-bit PowerPC +/// process. +class _LIBUNWIND_HIDDEN Registers_ppc64 { +public: + Registers_ppc64(); + Registers_ppc64(const void *registers); + + bool validRegister(int num) const; + uint64_t getRegister(int num) const; + void setRegister(int num, uint64_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto(); + static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_PPC64; } + static int getArch() { return REGISTERS_PPC64; } + + uint64_t getSP() const { return _registers.__r1; } + void setSP(uint64_t value) { _registers.__r1 = value; } + uint64_t getIP() const { return _registers.__srr0; } + void setIP(uint64_t value) { _registers.__srr0 = value; } + +private: + struct ppc64_thread_state_t { + uint64_t __srr0; // Instruction address register (PC) + uint64_t __srr1; // Machine state register (supervisor) + uint64_t __r0; + uint64_t __r1; + uint64_t __r2; + uint64_t __r3; + uint64_t __r4; + uint64_t __r5; + uint64_t __r6; + uint64_t __r7; + uint64_t __r8; + uint64_t __r9; + uint64_t __r10; + uint64_t __r11; + uint64_t __r12; + uint64_t __r13; + uint64_t __r14; + uint64_t __r15; + uint64_t __r16; + uint64_t __r17; + uint64_t __r18; + uint64_t __r19; + uint64_t __r20; + uint64_t __r21; + uint64_t __r22; + uint64_t __r23; + uint64_t __r24; + uint64_t __r25; + uint64_t __r26; + uint64_t __r27; + uint64_t __r28; + uint64_t __r29; + uint64_t __r30; + uint64_t __r31; + uint64_t __cr; // Condition register + uint64_t __xer; // User's integer exception register + uint64_t __lr; // Link register + uint64_t __ctr; // Count register + uint64_t __vrsave; // Vector Save Register + }; + + union ppc64_vsr_t { + struct asfloat_s { + double f; + uint64_t v2; + } asfloat; + v128 v; + }; + + ppc64_thread_state_t _registers; + ppc64_vsr_t _vectorScalarRegisters[64]; + + static int getVectorRegNum(int num); +}; + +inline Registers_ppc64::Registers_ppc64(const void *registers) { + static_assert((check_fit::does_fit), + "ppc64 registers do not fit into unw_context_t"); + memcpy(&_registers, static_cast(registers), + sizeof(_registers)); + static_assert(sizeof(_registers) == 312, + "expected vector scalar register offset to be 312"); + memcpy(&_vectorScalarRegisters, + static_cast(registers) + sizeof(_registers), + sizeof(_vectorScalarRegisters)); + static_assert(sizeof(_registers) + + sizeof(_vectorScalarRegisters) == 1336, + "expected vector register offset to be 1336 bytes"); +} + +inline Registers_ppc64::Registers_ppc64() { + memset(&_registers, 0, sizeof(_registers)); + memset(&_vectorScalarRegisters, 0, sizeof(_vectorScalarRegisters)); +} + +inline bool Registers_ppc64::validRegister(int regNum) const { + switch (regNum) { + case UNW_REG_IP: + case UNW_REG_SP: + case UNW_PPC64_XER: + case UNW_PPC64_LR: + case UNW_PPC64_CTR: + case UNW_PPC64_VRSAVE: + return true; + } + + if (regNum >= UNW_PPC64_R0 && regNum <= UNW_PPC64_R31) + return true; + if (regNum >= UNW_PPC64_CR0 && regNum <= UNW_PPC64_CR7) + return true; + + return false; +} + +inline uint64_t Registers_ppc64::getRegister(int regNum) const { + switch (regNum) { + case UNW_REG_IP: + return _registers.__srr0; + case UNW_PPC64_R0: + return _registers.__r0; + case UNW_PPC64_R1: + case UNW_REG_SP: + return _registers.__r1; + case UNW_PPC64_R2: + return _registers.__r2; + case UNW_PPC64_R3: + return _registers.__r3; + case UNW_PPC64_R4: + return _registers.__r4; + case UNW_PPC64_R5: + return _registers.__r5; + case UNW_PPC64_R6: + return _registers.__r6; + case UNW_PPC64_R7: + return _registers.__r7; + case UNW_PPC64_R8: + return _registers.__r8; + case UNW_PPC64_R9: + return _registers.__r9; + case UNW_PPC64_R10: + return _registers.__r10; + case UNW_PPC64_R11: + return _registers.__r11; + case UNW_PPC64_R12: + return _registers.__r12; + case UNW_PPC64_R13: + return _registers.__r13; + case UNW_PPC64_R14: + return _registers.__r14; + case UNW_PPC64_R15: + return _registers.__r15; + case UNW_PPC64_R16: + return _registers.__r16; + case UNW_PPC64_R17: + return _registers.__r17; + case UNW_PPC64_R18: + return _registers.__r18; + case UNW_PPC64_R19: + return _registers.__r19; + case UNW_PPC64_R20: + return _registers.__r20; + case UNW_PPC64_R21: + return _registers.__r21; + case UNW_PPC64_R22: + return _registers.__r22; + case UNW_PPC64_R23: + return _registers.__r23; + case UNW_PPC64_R24: + return _registers.__r24; + case UNW_PPC64_R25: + return _registers.__r25; + case UNW_PPC64_R26: + return _registers.__r26; + case UNW_PPC64_R27: + return _registers.__r27; + case UNW_PPC64_R28: + return _registers.__r28; + case UNW_PPC64_R29: + return _registers.__r29; + case UNW_PPC64_R30: + return _registers.__r30; + case UNW_PPC64_R31: + return _registers.__r31; + case UNW_PPC64_CR0: + return (_registers.__cr & 0xF0000000); + case UNW_PPC64_CR1: + return (_registers.__cr & 0x0F000000); + case UNW_PPC64_CR2: + return (_registers.__cr & 0x00F00000); + case UNW_PPC64_CR3: + return (_registers.__cr & 0x000F0000); + case UNW_PPC64_CR4: + return (_registers.__cr & 0x0000F000); + case UNW_PPC64_CR5: + return (_registers.__cr & 0x00000F00); + case UNW_PPC64_CR6: + return (_registers.__cr & 0x000000F0); + case UNW_PPC64_CR7: + return (_registers.__cr & 0x0000000F); + case UNW_PPC64_XER: + return _registers.__xer; + case UNW_PPC64_LR: + return _registers.__lr; + case UNW_PPC64_CTR: + return _registers.__ctr; + case UNW_PPC64_VRSAVE: + return _registers.__vrsave; + } + _LIBUNWIND_ABORT("unsupported ppc64 register"); +} + +inline void Registers_ppc64::setRegister(int regNum, uint64_t value) { + switch (regNum) { + case UNW_REG_IP: + _registers.__srr0 = value; + return; + case UNW_PPC64_R0: + _registers.__r0 = value; + return; + case UNW_PPC64_R1: + case UNW_REG_SP: + _registers.__r1 = value; + return; + case UNW_PPC64_R2: + _registers.__r2 = value; + return; + case UNW_PPC64_R3: + _registers.__r3 = value; + return; + case UNW_PPC64_R4: + _registers.__r4 = value; + return; + case UNW_PPC64_R5: + _registers.__r5 = value; + return; + case UNW_PPC64_R6: + _registers.__r6 = value; + return; + case UNW_PPC64_R7: + _registers.__r7 = value; + return; + case UNW_PPC64_R8: + _registers.__r8 = value; + return; + case UNW_PPC64_R9: + _registers.__r9 = value; + return; + case UNW_PPC64_R10: + _registers.__r10 = value; + return; + case UNW_PPC64_R11: + _registers.__r11 = value; + return; + case UNW_PPC64_R12: + _registers.__r12 = value; + return; + case UNW_PPC64_R13: + _registers.__r13 = value; + return; + case UNW_PPC64_R14: + _registers.__r14 = value; + return; + case UNW_PPC64_R15: + _registers.__r15 = value; + return; + case UNW_PPC64_R16: + _registers.__r16 = value; + return; + case UNW_PPC64_R17: + _registers.__r17 = value; + return; + case UNW_PPC64_R18: + _registers.__r18 = value; + return; + case UNW_PPC64_R19: + _registers.__r19 = value; + return; + case UNW_PPC64_R20: + _registers.__r20 = value; + return; + case UNW_PPC64_R21: + _registers.__r21 = value; + return; + case UNW_PPC64_R22: + _registers.__r22 = value; + return; + case UNW_PPC64_R23: + _registers.__r23 = value; + return; + case UNW_PPC64_R24: + _registers.__r24 = value; + return; + case UNW_PPC64_R25: + _registers.__r25 = value; + return; + case UNW_PPC64_R26: + _registers.__r26 = value; + return; + case UNW_PPC64_R27: + _registers.__r27 = value; + return; + case UNW_PPC64_R28: + _registers.__r28 = value; + return; + case UNW_PPC64_R29: + _registers.__r29 = value; + return; + case UNW_PPC64_R30: + _registers.__r30 = value; + return; + case UNW_PPC64_R31: + _registers.__r31 = value; + return; + case UNW_PPC64_CR0: + _registers.__cr &= 0x0FFFFFFF; + _registers.__cr |= (value & 0xF0000000); + return; + case UNW_PPC64_CR1: + _registers.__cr &= 0xF0FFFFFF; + _registers.__cr |= (value & 0x0F000000); + return; + case UNW_PPC64_CR2: + _registers.__cr &= 0xFF0FFFFF; + _registers.__cr |= (value & 0x00F00000); + return; + case UNW_PPC64_CR3: + _registers.__cr &= 0xFFF0FFFF; + _registers.__cr |= (value & 0x000F0000); + return; + case UNW_PPC64_CR4: + _registers.__cr &= 0xFFFF0FFF; + _registers.__cr |= (value & 0x0000F000); + return; + case UNW_PPC64_CR5: + _registers.__cr &= 0xFFFFF0FF; + _registers.__cr |= (value & 0x00000F00); + return; + case UNW_PPC64_CR6: + _registers.__cr &= 0xFFFFFF0F; + _registers.__cr |= (value & 0x000000F0); + return; + case UNW_PPC64_CR7: + _registers.__cr &= 0xFFFFFFF0; + _registers.__cr |= (value & 0x0000000F); + return; + case UNW_PPC64_XER: + _registers.__xer = value; + return; + case UNW_PPC64_LR: + _registers.__lr = value; + return; + case UNW_PPC64_CTR: + _registers.__ctr = value; + return; + case UNW_PPC64_VRSAVE: + _registers.__vrsave = value; + return; + } + _LIBUNWIND_ABORT("unsupported ppc64 register"); +} + +inline bool Registers_ppc64::validFloatRegister(int regNum) const { + return regNum >= UNW_PPC64_F0 && regNum <= UNW_PPC64_F31; +} + +inline double Registers_ppc64::getFloatRegister(int regNum) const { + assert(validFloatRegister(regNum)); + return _vectorScalarRegisters[regNum - UNW_PPC64_F0].asfloat.f; +} + +inline void Registers_ppc64::setFloatRegister(int regNum, double value) { + assert(validFloatRegister(regNum)); + _vectorScalarRegisters[regNum - UNW_PPC64_F0].asfloat.f = value; +} + +inline bool Registers_ppc64::validVectorRegister(int regNum) const { +#if defined(__VSX__) + if (regNum >= UNW_PPC64_VS0 && regNum <= UNW_PPC64_VS31) + return true; + if (regNum >= UNW_PPC64_VS32 && regNum <= UNW_PPC64_VS63) + return true; +#elif defined(__ALTIVEC__) + if (regNum >= UNW_PPC64_V0 && regNum <= UNW_PPC64_V31) + return true; +#endif + return false; +} + +inline int Registers_ppc64::getVectorRegNum(int num) +{ + if (num >= UNW_PPC64_VS0 && num <= UNW_PPC64_VS31) + return num - UNW_PPC64_VS0; + else + return num - UNW_PPC64_VS32 + 32; +} + +inline v128 Registers_ppc64::getVectorRegister(int regNum) const { + assert(validVectorRegister(regNum)); + return _vectorScalarRegisters[getVectorRegNum(regNum)].v; +} + +inline void Registers_ppc64::setVectorRegister(int regNum, v128 value) { + assert(validVectorRegister(regNum)); + _vectorScalarRegisters[getVectorRegNum(regNum)].v = value; +} + +inline const char *Registers_ppc64::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + return "ip"; + case UNW_REG_SP: + return "sp"; + case UNW_PPC64_R0: + return "r0"; + case UNW_PPC64_R1: + return "r1"; + case UNW_PPC64_R2: + return "r2"; + case UNW_PPC64_R3: + return "r3"; + case UNW_PPC64_R4: + return "r4"; + case UNW_PPC64_R5: + return "r5"; + case UNW_PPC64_R6: + return "r6"; + case UNW_PPC64_R7: + return "r7"; + case UNW_PPC64_R8: + return "r8"; + case UNW_PPC64_R9: + return "r9"; + case UNW_PPC64_R10: + return "r10"; + case UNW_PPC64_R11: + return "r11"; + case UNW_PPC64_R12: + return "r12"; + case UNW_PPC64_R13: + return "r13"; + case UNW_PPC64_R14: + return "r14"; + case UNW_PPC64_R15: + return "r15"; + case UNW_PPC64_R16: + return "r16"; + case UNW_PPC64_R17: + return "r17"; + case UNW_PPC64_R18: + return "r18"; + case UNW_PPC64_R19: + return "r19"; + case UNW_PPC64_R20: + return "r20"; + case UNW_PPC64_R21: + return "r21"; + case UNW_PPC64_R22: + return "r22"; + case UNW_PPC64_R23: + return "r23"; + case UNW_PPC64_R24: + return "r24"; + case UNW_PPC64_R25: + return "r25"; + case UNW_PPC64_R26: + return "r26"; + case UNW_PPC64_R27: + return "r27"; + case UNW_PPC64_R28: + return "r28"; + case UNW_PPC64_R29: + return "r29"; + case UNW_PPC64_R30: + return "r30"; + case UNW_PPC64_R31: + return "r31"; + case UNW_PPC64_CR0: + return "cr0"; + case UNW_PPC64_CR1: + return "cr1"; + case UNW_PPC64_CR2: + return "cr2"; + case UNW_PPC64_CR3: + return "cr3"; + case UNW_PPC64_CR4: + return "cr4"; + case UNW_PPC64_CR5: + return "cr5"; + case UNW_PPC64_CR6: + return "cr6"; + case UNW_PPC64_CR7: + return "cr7"; + case UNW_PPC64_XER: + return "xer"; + case UNW_PPC64_LR: + return "lr"; + case UNW_PPC64_CTR: + return "ctr"; + case UNW_PPC64_VRSAVE: + return "vrsave"; + case UNW_PPC64_F0: + return "fp0"; + case UNW_PPC64_F1: + return "fp1"; + case UNW_PPC64_F2: + return "fp2"; + case UNW_PPC64_F3: + return "fp3"; + case UNW_PPC64_F4: + return "fp4"; + case UNW_PPC64_F5: + return "fp5"; + case UNW_PPC64_F6: + return "fp6"; + case UNW_PPC64_F7: + return "fp7"; + case UNW_PPC64_F8: + return "fp8"; + case UNW_PPC64_F9: + return "fp9"; + case UNW_PPC64_F10: + return "fp10"; + case UNW_PPC64_F11: + return "fp11"; + case UNW_PPC64_F12: + return "fp12"; + case UNW_PPC64_F13: + return "fp13"; + case UNW_PPC64_F14: + return "fp14"; + case UNW_PPC64_F15: + return "fp15"; + case UNW_PPC64_F16: + return "fp16"; + case UNW_PPC64_F17: + return "fp17"; + case UNW_PPC64_F18: + return "fp18"; + case UNW_PPC64_F19: + return "fp19"; + case UNW_PPC64_F20: + return "fp20"; + case UNW_PPC64_F21: + return "fp21"; + case UNW_PPC64_F22: + return "fp22"; + case UNW_PPC64_F23: + return "fp23"; + case UNW_PPC64_F24: + return "fp24"; + case UNW_PPC64_F25: + return "fp25"; + case UNW_PPC64_F26: + return "fp26"; + case UNW_PPC64_F27: + return "fp27"; + case UNW_PPC64_F28: + return "fp28"; + case UNW_PPC64_F29: + return "fp29"; + case UNW_PPC64_F30: + return "fp30"; + case UNW_PPC64_F31: + return "fp31"; + case UNW_PPC64_V0: + return "v0"; + case UNW_PPC64_V1: + return "v1"; + case UNW_PPC64_V2: + return "v2"; + case UNW_PPC64_V3: + return "v3"; + case UNW_PPC64_V4: + return "v4"; + case UNW_PPC64_V5: + return "v5"; + case UNW_PPC64_V6: + return "v6"; + case UNW_PPC64_V7: + return "v7"; + case UNW_PPC64_V8: + return "v8"; + case UNW_PPC64_V9: + return "v9"; + case UNW_PPC64_V10: + return "v10"; + case UNW_PPC64_V11: + return "v11"; + case UNW_PPC64_V12: + return "v12"; + case UNW_PPC64_V13: + return "v13"; + case UNW_PPC64_V14: + return "v14"; + case UNW_PPC64_V15: + return "v15"; + case UNW_PPC64_V16: + return "v16"; + case UNW_PPC64_V17: + return "v17"; + case UNW_PPC64_V18: + return "v18"; + case UNW_PPC64_V19: + return "v19"; + case UNW_PPC64_V20: + return "v20"; + case UNW_PPC64_V21: + return "v21"; + case UNW_PPC64_V22: + return "v22"; + case UNW_PPC64_V23: + return "v23"; + case UNW_PPC64_V24: + return "v24"; + case UNW_PPC64_V25: + return "v25"; + case UNW_PPC64_V26: + return "v26"; + case UNW_PPC64_V27: + return "v27"; + case UNW_PPC64_V28: + return "v28"; + case UNW_PPC64_V29: + return "v29"; + case UNW_PPC64_V30: + return "v30"; + case UNW_PPC64_V31: + return "v31"; + } + return "unknown register"; +} +#endif // _LIBUNWIND_TARGET_PPC64 + + +#if defined(_LIBUNWIND_TARGET_AARCH64) +/// Registers_arm64 holds the register state of a thread in a 64-bit arm +/// process. +class _LIBUNWIND_HIDDEN Registers_arm64; +extern "C" void __libunwind_Registers_arm64_jumpto(Registers_arm64 *); +class _LIBUNWIND_HIDDEN Registers_arm64 { +public: + Registers_arm64(); + Registers_arm64(const void *registers); + + bool validRegister(int num) const; + uint64_t getRegister(int num) const; + void setRegister(int num, uint64_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto() { __libunwind_Registers_arm64_jumpto(this); } + static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM64; } + static int getArch() { return REGISTERS_ARM64; } + + uint64_t getSP() const { return _registers.__sp; } + void setSP(uint64_t value) { _registers.__sp = value; } + uint64_t getIP() const { return _registers.__pc; } + void setIP(uint64_t value) { _registers.__pc = value; } + uint64_t getFP() const { return _registers.__fp; } + void setFP(uint64_t value) { _registers.__fp = value; } + +private: + struct GPRs { + uint64_t __x[29]; // x0-x28 + uint64_t __fp; // Frame pointer x29 + uint64_t __lr; // Link register x30 + uint64_t __sp; // Stack pointer x31 + uint64_t __pc; // Program counter + uint64_t __ra_sign_state; // RA sign state register + }; + + GPRs _registers; + double _vectorHalfRegisters[32]; + // Currently only the lower double in 128-bit vectore registers + // is perserved during unwinding. We could define new register + // numbers (> 96) which mean whole vector registers, then this + // struct would need to change to contain whole vector registers. +}; + +inline Registers_arm64::Registers_arm64(const void *registers) { + static_assert((check_fit::does_fit), + "arm64 registers do not fit into unw_context_t"); + memcpy(&_registers, registers, sizeof(_registers)); + static_assert(sizeof(GPRs) == 0x110, + "expected VFP registers to be at offset 272"); + memcpy(_vectorHalfRegisters, + static_cast(registers) + sizeof(GPRs), + sizeof(_vectorHalfRegisters)); +} + +inline Registers_arm64::Registers_arm64() { + memset(&_registers, 0, sizeof(_registers)); + memset(&_vectorHalfRegisters, 0, sizeof(_vectorHalfRegisters)); +} + +inline bool Registers_arm64::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum > 95) + return false; + if (regNum == UNW_AARCH64_RA_SIGN_STATE) + return true; + if ((regNum > 32) && (regNum < 64)) + return false; + return true; +} + +inline uint64_t Registers_arm64::getRegister(int regNum) const { + if (regNum == UNW_REG_IP || regNum == UNW_AARCH64_PC) + return _registers.__pc; + if (regNum == UNW_REG_SP || regNum == UNW_AARCH64_SP) + return _registers.__sp; + if (regNum == UNW_AARCH64_RA_SIGN_STATE) + return _registers.__ra_sign_state; + if (regNum == UNW_AARCH64_FP) + return _registers.__fp; + if (regNum == UNW_AARCH64_LR) + return _registers.__lr; + if ((regNum >= 0) && (regNum < 29)) + return _registers.__x[regNum]; + _LIBUNWIND_ABORT("unsupported arm64 register"); +} + +inline void Registers_arm64::setRegister(int regNum, uint64_t value) { + if (regNum == UNW_REG_IP || regNum == UNW_AARCH64_PC) + _registers.__pc = value; + else if (regNum == UNW_REG_SP || regNum == UNW_AARCH64_SP) + _registers.__sp = value; + else if (regNum == UNW_AARCH64_RA_SIGN_STATE) + _registers.__ra_sign_state = value; + else if (regNum == UNW_AARCH64_FP) + _registers.__fp = value; + else if (regNum == UNW_AARCH64_LR) + _registers.__lr = value; + else if ((regNum >= 0) && (regNum < 29)) + _registers.__x[regNum] = value; + else + _LIBUNWIND_ABORT("unsupported arm64 register"); +} + +inline const char *Registers_arm64::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + return "pc"; + case UNW_REG_SP: + return "sp"; + case UNW_AARCH64_X0: + return "x0"; + case UNW_AARCH64_X1: + return "x1"; + case UNW_AARCH64_X2: + return "x2"; + case UNW_AARCH64_X3: + return "x3"; + case UNW_AARCH64_X4: + return "x4"; + case UNW_AARCH64_X5: + return "x5"; + case UNW_AARCH64_X6: + return "x6"; + case UNW_AARCH64_X7: + return "x7"; + case UNW_AARCH64_X8: + return "x8"; + case UNW_AARCH64_X9: + return "x9"; + case UNW_AARCH64_X10: + return "x10"; + case UNW_AARCH64_X11: + return "x11"; + case UNW_AARCH64_X12: + return "x12"; + case UNW_AARCH64_X13: + return "x13"; + case UNW_AARCH64_X14: + return "x14"; + case UNW_AARCH64_X15: + return "x15"; + case UNW_AARCH64_X16: + return "x16"; + case UNW_AARCH64_X17: + return "x17"; + case UNW_AARCH64_X18: + return "x18"; + case UNW_AARCH64_X19: + return "x19"; + case UNW_AARCH64_X20: + return "x20"; + case UNW_AARCH64_X21: + return "x21"; + case UNW_AARCH64_X22: + return "x22"; + case UNW_AARCH64_X23: + return "x23"; + case UNW_AARCH64_X24: + return "x24"; + case UNW_AARCH64_X25: + return "x25"; + case UNW_AARCH64_X26: + return "x26"; + case UNW_AARCH64_X27: + return "x27"; + case UNW_AARCH64_X28: + return "x28"; + case UNW_AARCH64_FP: + return "fp"; + case UNW_AARCH64_LR: + return "lr"; + case UNW_AARCH64_SP: + return "sp"; + case UNW_AARCH64_PC: + return "pc"; + case UNW_AARCH64_V0: + return "d0"; + case UNW_AARCH64_V1: + return "d1"; + case UNW_AARCH64_V2: + return "d2"; + case UNW_AARCH64_V3: + return "d3"; + case UNW_AARCH64_V4: + return "d4"; + case UNW_AARCH64_V5: + return "d5"; + case UNW_AARCH64_V6: + return "d6"; + case UNW_AARCH64_V7: + return "d7"; + case UNW_AARCH64_V8: + return "d8"; + case UNW_AARCH64_V9: + return "d9"; + case UNW_AARCH64_V10: + return "d10"; + case UNW_AARCH64_V11: + return "d11"; + case UNW_AARCH64_V12: + return "d12"; + case UNW_AARCH64_V13: + return "d13"; + case UNW_AARCH64_V14: + return "d14"; + case UNW_AARCH64_V15: + return "d15"; + case UNW_AARCH64_V16: + return "d16"; + case UNW_AARCH64_V17: + return "d17"; + case UNW_AARCH64_V18: + return "d18"; + case UNW_AARCH64_V19: + return "d19"; + case UNW_AARCH64_V20: + return "d20"; + case UNW_AARCH64_V21: + return "d21"; + case UNW_AARCH64_V22: + return "d22"; + case UNW_AARCH64_V23: + return "d23"; + case UNW_AARCH64_V24: + return "d24"; + case UNW_AARCH64_V25: + return "d25"; + case UNW_AARCH64_V26: + return "d26"; + case UNW_AARCH64_V27: + return "d27"; + case UNW_AARCH64_V28: + return "d28"; + case UNW_AARCH64_V29: + return "d29"; + case UNW_AARCH64_V30: + return "d30"; + case UNW_AARCH64_V31: + return "d31"; + default: + return "unknown register"; + } +} + +inline bool Registers_arm64::validFloatRegister(int regNum) const { + if (regNum < UNW_AARCH64_V0) + return false; + if (regNum > UNW_AARCH64_V31) + return false; + return true; +} + +inline double Registers_arm64::getFloatRegister(int regNum) const { + assert(validFloatRegister(regNum)); + return _vectorHalfRegisters[regNum - UNW_AARCH64_V0]; +} + +inline void Registers_arm64::setFloatRegister(int regNum, double value) { + assert(validFloatRegister(regNum)); + _vectorHalfRegisters[regNum - UNW_AARCH64_V0] = value; +} + +inline bool Registers_arm64::validVectorRegister(int) const { + return false; +} + +inline v128 Registers_arm64::getVectorRegister(int) const { + _LIBUNWIND_ABORT("no arm64 vector register support yet"); +} + +inline void Registers_arm64::setVectorRegister(int, v128) { + _LIBUNWIND_ABORT("no arm64 vector register support yet"); +} +#endif // _LIBUNWIND_TARGET_AARCH64 + +#if defined(_LIBUNWIND_TARGET_ARM) +/// Registers_arm holds the register state of a thread in a 32-bit arm +/// process. +/// +/// NOTE: Assumes VFPv3. On ARM processors without a floating point unit, +/// this uses more memory than required. +class _LIBUNWIND_HIDDEN Registers_arm { +public: + Registers_arm(); + Registers_arm(const void *registers); + + bool validRegister(int num) const; + uint32_t getRegister(int num) const; + void setRegister(int num, uint32_t value); + bool validFloatRegister(int num) const; + unw_fpreg_t getFloatRegister(int num); + void setFloatRegister(int num, unw_fpreg_t value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto() { + restoreSavedFloatRegisters(); + restoreCoreAndJumpTo(); + } + static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_ARM; } + static int getArch() { return REGISTERS_ARM; } + + uint32_t getSP() const { return _registers.__sp; } + void setSP(uint32_t value) { _registers.__sp = value; } + uint32_t getIP() const { return _registers.__pc; } + void setIP(uint32_t value) { _registers.__pc = value; } + + void saveVFPAsX() { + assert(_use_X_for_vfp_save || !_saved_vfp_d0_d15); + _use_X_for_vfp_save = true; + } + + void restoreSavedFloatRegisters() { + if (_saved_vfp_d0_d15) { + if (_use_X_for_vfp_save) + restoreVFPWithFLDMX(_vfp_d0_d15_pad); + else + restoreVFPWithFLDMD(_vfp_d0_d15_pad); + } + if (_saved_vfp_d16_d31) + restoreVFPv3(_vfp_d16_d31); +#if defined(__ARM_WMMX) + if (_saved_iwmmx) + restoreiWMMX(_iwmmx); + if (_saved_iwmmx_control) + restoreiWMMXControl(_iwmmx_control); +#endif + } + +private: + struct GPRs { + uint32_t __r[13]; // r0-r12 + uint32_t __sp; // Stack pointer r13 + uint32_t __lr; // Link register r14 + uint32_t __pc; // Program counter r15 + }; + + static void saveVFPWithFSTMD(void*); + static void saveVFPWithFSTMX(void*); + static void saveVFPv3(void*); + static void restoreVFPWithFLDMD(void*); + static void restoreVFPWithFLDMX(void*); + static void restoreVFPv3(void*); +#if defined(__ARM_WMMX) + static void saveiWMMX(void*); + static void saveiWMMXControl(uint32_t*); + static void restoreiWMMX(void*); + static void restoreiWMMXControl(uint32_t*); +#endif + void restoreCoreAndJumpTo(); + + // ARM registers + GPRs _registers; + + // We save floating point registers lazily because we can't know ahead of + // time which ones are used. See EHABI #4.7. + + // Whether D0-D15 are saved in the FTSMX instead of FSTMD format. + // + // See EHABI #7.5 that explains how matching instruction sequences for load + // and store need to be used to correctly restore the exact register bits. + bool _use_X_for_vfp_save; + // Whether VFP D0-D15 are saved. + bool _saved_vfp_d0_d15; + // Whether VFPv3 D16-D31 are saved. + bool _saved_vfp_d16_d31; + // VFP registers D0-D15, + padding if saved using FSTMX + unw_fpreg_t _vfp_d0_d15_pad[17]; + // VFPv3 registers D16-D31, always saved using FSTMD + unw_fpreg_t _vfp_d16_d31[16]; +#if defined(__ARM_WMMX) + // Whether iWMMX data registers are saved. + bool _saved_iwmmx; + // Whether iWMMX control registers are saved. + mutable bool _saved_iwmmx_control; + // iWMMX registers + unw_fpreg_t _iwmmx[16]; + // iWMMX control registers + mutable uint32_t _iwmmx_control[4]; +#endif +}; + +inline Registers_arm::Registers_arm(const void *registers) + : _use_X_for_vfp_save(false), + _saved_vfp_d0_d15(false), + _saved_vfp_d16_d31(false) { + static_assert((check_fit::does_fit), + "arm registers do not fit into unw_context_t"); + // See __unw_getcontext() note about data. + memcpy(&_registers, registers, sizeof(_registers)); + memset(&_vfp_d0_d15_pad, 0, sizeof(_vfp_d0_d15_pad)); + memset(&_vfp_d16_d31, 0, sizeof(_vfp_d16_d31)); +#if defined(__ARM_WMMX) + _saved_iwmmx = false; + _saved_iwmmx_control = false; + memset(&_iwmmx, 0, sizeof(_iwmmx)); + memset(&_iwmmx_control, 0, sizeof(_iwmmx_control)); +#endif +} + +inline Registers_arm::Registers_arm() + : _use_X_for_vfp_save(false), + _saved_vfp_d0_d15(false), + _saved_vfp_d16_d31(false) { + memset(&_registers, 0, sizeof(_registers)); + memset(&_vfp_d0_d15_pad, 0, sizeof(_vfp_d0_d15_pad)); + memset(&_vfp_d16_d31, 0, sizeof(_vfp_d16_d31)); +#if defined(__ARM_WMMX) + _saved_iwmmx = false; + _saved_iwmmx_control = false; + memset(&_iwmmx, 0, sizeof(_iwmmx)); + memset(&_iwmmx_control, 0, sizeof(_iwmmx_control)); +#endif +} + +inline bool Registers_arm::validRegister(int regNum) const { + // Returns true for all non-VFP registers supported by the EHABI + // virtual register set (VRS). + if (regNum == UNW_REG_IP) + return true; + + if (regNum == UNW_REG_SP) + return true; + + if (regNum >= UNW_ARM_R0 && regNum <= UNW_ARM_R15) + return true; + +#if defined(__ARM_WMMX) + if (regNum >= UNW_ARM_WC0 && regNum <= UNW_ARM_WC3) + return true; +#endif + + return false; +} + +inline uint32_t Registers_arm::getRegister(int regNum) const { + if (regNum == UNW_REG_SP || regNum == UNW_ARM_SP) + return _registers.__sp; + + if (regNum == UNW_ARM_LR) + return _registers.__lr; + + if (regNum == UNW_REG_IP || regNum == UNW_ARM_IP) + return _registers.__pc; + + if (regNum >= UNW_ARM_R0 && regNum <= UNW_ARM_R12) + return _registers.__r[regNum]; + +#if defined(__ARM_WMMX) + if (regNum >= UNW_ARM_WC0 && regNum <= UNW_ARM_WC3) { + if (!_saved_iwmmx_control) { + _saved_iwmmx_control = true; + saveiWMMXControl(_iwmmx_control); + } + return _iwmmx_control[regNum - UNW_ARM_WC0]; + } +#endif + + _LIBUNWIND_ABORT("unsupported arm register"); +} + +inline void Registers_arm::setRegister(int regNum, uint32_t value) { + if (regNum == UNW_REG_SP || regNum == UNW_ARM_SP) { + _registers.__sp = value; + return; + } + + if (regNum == UNW_ARM_LR) { + _registers.__lr = value; + return; + } + + if (regNum == UNW_REG_IP || regNum == UNW_ARM_IP) { + _registers.__pc = value; + return; + } + + if (regNum >= UNW_ARM_R0 && regNum <= UNW_ARM_R12) { + _registers.__r[regNum] = value; + return; + } + +#if defined(__ARM_WMMX) + if (regNum >= UNW_ARM_WC0 && regNum <= UNW_ARM_WC3) { + if (!_saved_iwmmx_control) { + _saved_iwmmx_control = true; + saveiWMMXControl(_iwmmx_control); + } + _iwmmx_control[regNum - UNW_ARM_WC0] = value; + return; + } +#endif + + _LIBUNWIND_ABORT("unsupported arm register"); +} + +inline const char *Registers_arm::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + case UNW_ARM_IP: // UNW_ARM_R15 is alias + return "pc"; + case UNW_ARM_LR: // UNW_ARM_R14 is alias + return "lr"; + case UNW_REG_SP: + case UNW_ARM_SP: // UNW_ARM_R13 is alias + return "sp"; + case UNW_ARM_R0: + return "r0"; + case UNW_ARM_R1: + return "r1"; + case UNW_ARM_R2: + return "r2"; + case UNW_ARM_R3: + return "r3"; + case UNW_ARM_R4: + return "r4"; + case UNW_ARM_R5: + return "r5"; + case UNW_ARM_R6: + return "r6"; + case UNW_ARM_R7: + return "r7"; + case UNW_ARM_R8: + return "r8"; + case UNW_ARM_R9: + return "r9"; + case UNW_ARM_R10: + return "r10"; + case UNW_ARM_R11: + return "r11"; + case UNW_ARM_R12: + return "r12"; + case UNW_ARM_S0: + return "s0"; + case UNW_ARM_S1: + return "s1"; + case UNW_ARM_S2: + return "s2"; + case UNW_ARM_S3: + return "s3"; + case UNW_ARM_S4: + return "s4"; + case UNW_ARM_S5: + return "s5"; + case UNW_ARM_S6: + return "s6"; + case UNW_ARM_S7: + return "s7"; + case UNW_ARM_S8: + return "s8"; + case UNW_ARM_S9: + return "s9"; + case UNW_ARM_S10: + return "s10"; + case UNW_ARM_S11: + return "s11"; + case UNW_ARM_S12: + return "s12"; + case UNW_ARM_S13: + return "s13"; + case UNW_ARM_S14: + return "s14"; + case UNW_ARM_S15: + return "s15"; + case UNW_ARM_S16: + return "s16"; + case UNW_ARM_S17: + return "s17"; + case UNW_ARM_S18: + return "s18"; + case UNW_ARM_S19: + return "s19"; + case UNW_ARM_S20: + return "s20"; + case UNW_ARM_S21: + return "s21"; + case UNW_ARM_S22: + return "s22"; + case UNW_ARM_S23: + return "s23"; + case UNW_ARM_S24: + return "s24"; + case UNW_ARM_S25: + return "s25"; + case UNW_ARM_S26: + return "s26"; + case UNW_ARM_S27: + return "s27"; + case UNW_ARM_S28: + return "s28"; + case UNW_ARM_S29: + return "s29"; + case UNW_ARM_S30: + return "s30"; + case UNW_ARM_S31: + return "s31"; + case UNW_ARM_D0: + return "d0"; + case UNW_ARM_D1: + return "d1"; + case UNW_ARM_D2: + return "d2"; + case UNW_ARM_D3: + return "d3"; + case UNW_ARM_D4: + return "d4"; + case UNW_ARM_D5: + return "d5"; + case UNW_ARM_D6: + return "d6"; + case UNW_ARM_D7: + return "d7"; + case UNW_ARM_D8: + return "d8"; + case UNW_ARM_D9: + return "d9"; + case UNW_ARM_D10: + return "d10"; + case UNW_ARM_D11: + return "d11"; + case UNW_ARM_D12: + return "d12"; + case UNW_ARM_D13: + return "d13"; + case UNW_ARM_D14: + return "d14"; + case UNW_ARM_D15: + return "d15"; + case UNW_ARM_D16: + return "d16"; + case UNW_ARM_D17: + return "d17"; + case UNW_ARM_D18: + return "d18"; + case UNW_ARM_D19: + return "d19"; + case UNW_ARM_D20: + return "d20"; + case UNW_ARM_D21: + return "d21"; + case UNW_ARM_D22: + return "d22"; + case UNW_ARM_D23: + return "d23"; + case UNW_ARM_D24: + return "d24"; + case UNW_ARM_D25: + return "d25"; + case UNW_ARM_D26: + return "d26"; + case UNW_ARM_D27: + return "d27"; + case UNW_ARM_D28: + return "d28"; + case UNW_ARM_D29: + return "d29"; + case UNW_ARM_D30: + return "d30"; + case UNW_ARM_D31: + return "d31"; + default: + return "unknown register"; + } +} + +inline bool Registers_arm::validFloatRegister(int regNum) const { + // NOTE: Consider the intel MMX registers floating points so the + // __unw_get_fpreg can be used to transmit the 64-bit data back. + return ((regNum >= UNW_ARM_D0) && (regNum <= UNW_ARM_D31)) +#if defined(__ARM_WMMX) + || ((regNum >= UNW_ARM_WR0) && (regNum <= UNW_ARM_WR15)) +#endif + ; +} + +inline unw_fpreg_t Registers_arm::getFloatRegister(int regNum) { + if (regNum >= UNW_ARM_D0 && regNum <= UNW_ARM_D15) { + if (!_saved_vfp_d0_d15) { + _saved_vfp_d0_d15 = true; + if (_use_X_for_vfp_save) + saveVFPWithFSTMX(_vfp_d0_d15_pad); + else + saveVFPWithFSTMD(_vfp_d0_d15_pad); + } + return _vfp_d0_d15_pad[regNum - UNW_ARM_D0]; + } + + if (regNum >= UNW_ARM_D16 && regNum <= UNW_ARM_D31) { + if (!_saved_vfp_d16_d31) { + _saved_vfp_d16_d31 = true; + saveVFPv3(_vfp_d16_d31); + } + return _vfp_d16_d31[regNum - UNW_ARM_D16]; + } + +#if defined(__ARM_WMMX) + if (regNum >= UNW_ARM_WR0 && regNum <= UNW_ARM_WR15) { + if (!_saved_iwmmx) { + _saved_iwmmx = true; + saveiWMMX(_iwmmx); + } + return _iwmmx[regNum - UNW_ARM_WR0]; + } +#endif + + _LIBUNWIND_ABORT("Unknown ARM float register"); +} + +inline void Registers_arm::setFloatRegister(int regNum, unw_fpreg_t value) { + if (regNum >= UNW_ARM_D0 && regNum <= UNW_ARM_D15) { + if (!_saved_vfp_d0_d15) { + _saved_vfp_d0_d15 = true; + if (_use_X_for_vfp_save) + saveVFPWithFSTMX(_vfp_d0_d15_pad); + else + saveVFPWithFSTMD(_vfp_d0_d15_pad); + } + _vfp_d0_d15_pad[regNum - UNW_ARM_D0] = value; + return; + } + + if (regNum >= UNW_ARM_D16 && regNum <= UNW_ARM_D31) { + if (!_saved_vfp_d16_d31) { + _saved_vfp_d16_d31 = true; + saveVFPv3(_vfp_d16_d31); + } + _vfp_d16_d31[regNum - UNW_ARM_D16] = value; + return; + } + +#if defined(__ARM_WMMX) + if (regNum >= UNW_ARM_WR0 && regNum <= UNW_ARM_WR15) { + if (!_saved_iwmmx) { + _saved_iwmmx = true; + saveiWMMX(_iwmmx); + } + _iwmmx[regNum - UNW_ARM_WR0] = value; + return; + } +#endif + + _LIBUNWIND_ABORT("Unknown ARM float register"); +} + +inline bool Registers_arm::validVectorRegister(int) const { + return false; +} + +inline v128 Registers_arm::getVectorRegister(int) const { + _LIBUNWIND_ABORT("ARM vector support not implemented"); +} + +inline void Registers_arm::setVectorRegister(int, v128) { + _LIBUNWIND_ABORT("ARM vector support not implemented"); +} +#endif // _LIBUNWIND_TARGET_ARM + + +#if defined(_LIBUNWIND_TARGET_OR1K) +/// Registers_or1k holds the register state of a thread in an OpenRISC1000 +/// process. +class _LIBUNWIND_HIDDEN Registers_or1k { +public: + Registers_or1k(); + Registers_or1k(const void *registers); + + bool validRegister(int num) const; + uint32_t getRegister(int num) const; + void setRegister(int num, uint32_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto(); + static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_OR1K; } + static int getArch() { return REGISTERS_OR1K; } + + uint64_t getSP() const { return _registers.__r[1]; } + void setSP(uint32_t value) { _registers.__r[1] = value; } + uint64_t getIP() const { return _registers.__pc; } + void setIP(uint32_t value) { _registers.__pc = value; } + +private: + struct or1k_thread_state_t { + unsigned int __r[32]; // r0-r31 + unsigned int __pc; // Program counter + unsigned int __epcr; // Program counter at exception + }; + + or1k_thread_state_t _registers; +}; + +inline Registers_or1k::Registers_or1k(const void *registers) { + static_assert((check_fit::does_fit), + "or1k registers do not fit into unw_context_t"); + memcpy(&_registers, static_cast(registers), + sizeof(_registers)); +} + +inline Registers_or1k::Registers_or1k() { + memset(&_registers, 0, sizeof(_registers)); +} + +inline bool Registers_or1k::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum <= UNW_OR1K_R31) + return true; + if (regNum == UNW_OR1K_EPCR) + return true; + return false; +} + +inline uint32_t Registers_or1k::getRegister(int regNum) const { + if (regNum >= UNW_OR1K_R0 && regNum <= UNW_OR1K_R31) + return _registers.__r[regNum - UNW_OR1K_R0]; + + switch (regNum) { + case UNW_REG_IP: + return _registers.__pc; + case UNW_REG_SP: + return _registers.__r[1]; + case UNW_OR1K_EPCR: + return _registers.__epcr; + } + _LIBUNWIND_ABORT("unsupported or1k register"); +} + +inline void Registers_or1k::setRegister(int regNum, uint32_t value) { + if (regNum >= UNW_OR1K_R0 && regNum <= UNW_OR1K_R31) { + _registers.__r[regNum - UNW_OR1K_R0] = value; + return; + } + + switch (regNum) { + case UNW_REG_IP: + _registers.__pc = value; + return; + case UNW_REG_SP: + _registers.__r[1] = value; + return; + case UNW_OR1K_EPCR: + _registers.__epcr = value; + return; + } + _LIBUNWIND_ABORT("unsupported or1k register"); +} + +inline bool Registers_or1k::validFloatRegister(int /* regNum */) const { + return false; +} + +inline double Registers_or1k::getFloatRegister(int /* regNum */) const { + _LIBUNWIND_ABORT("or1k float support not implemented"); +} + +inline void Registers_or1k::setFloatRegister(int /* regNum */, + double /* value */) { + _LIBUNWIND_ABORT("or1k float support not implemented"); +} + +inline bool Registers_or1k::validVectorRegister(int /* regNum */) const { + return false; +} + +inline v128 Registers_or1k::getVectorRegister(int /* regNum */) const { + _LIBUNWIND_ABORT("or1k vector support not implemented"); +} + +inline void Registers_or1k::setVectorRegister(int /* regNum */, v128 /* value */) { + _LIBUNWIND_ABORT("or1k vector support not implemented"); +} + +inline const char *Registers_or1k::getRegisterName(int regNum) { + switch (regNum) { + case UNW_OR1K_R0: + return "r0"; + case UNW_OR1K_R1: + return "r1"; + case UNW_OR1K_R2: + return "r2"; + case UNW_OR1K_R3: + return "r3"; + case UNW_OR1K_R4: + return "r4"; + case UNW_OR1K_R5: + return "r5"; + case UNW_OR1K_R6: + return "r6"; + case UNW_OR1K_R7: + return "r7"; + case UNW_OR1K_R8: + return "r8"; + case UNW_OR1K_R9: + return "r9"; + case UNW_OR1K_R10: + return "r10"; + case UNW_OR1K_R11: + return "r11"; + case UNW_OR1K_R12: + return "r12"; + case UNW_OR1K_R13: + return "r13"; + case UNW_OR1K_R14: + return "r14"; + case UNW_OR1K_R15: + return "r15"; + case UNW_OR1K_R16: + return "r16"; + case UNW_OR1K_R17: + return "r17"; + case UNW_OR1K_R18: + return "r18"; + case UNW_OR1K_R19: + return "r19"; + case UNW_OR1K_R20: + return "r20"; + case UNW_OR1K_R21: + return "r21"; + case UNW_OR1K_R22: + return "r22"; + case UNW_OR1K_R23: + return "r23"; + case UNW_OR1K_R24: + return "r24"; + case UNW_OR1K_R25: + return "r25"; + case UNW_OR1K_R26: + return "r26"; + case UNW_OR1K_R27: + return "r27"; + case UNW_OR1K_R28: + return "r28"; + case UNW_OR1K_R29: + return "r29"; + case UNW_OR1K_R30: + return "r30"; + case UNW_OR1K_R31: + return "r31"; + case UNW_OR1K_EPCR: + return "EPCR"; + default: + return "unknown register"; + } + +} +#endif // _LIBUNWIND_TARGET_OR1K + +#if defined(_LIBUNWIND_TARGET_MIPS_O32) +/// Registers_mips_o32 holds the register state of a thread in a 32-bit MIPS +/// process. +class _LIBUNWIND_HIDDEN Registers_mips_o32 { +public: + Registers_mips_o32(); + Registers_mips_o32(const void *registers); + + bool validRegister(int num) const; + uint32_t getRegister(int num) const; + void setRegister(int num, uint32_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto(); + static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_MIPS; } + static int getArch() { return REGISTERS_MIPS_O32; } + + uint32_t getSP() const { return _registers.__r[29]; } + void setSP(uint32_t value) { _registers.__r[29] = value; } + uint32_t getIP() const { return _registers.__pc; } + void setIP(uint32_t value) { _registers.__pc = value; } + +private: + struct mips_o32_thread_state_t { + uint32_t __r[32]; + uint32_t __pc; + uint32_t __hi; + uint32_t __lo; + }; + + mips_o32_thread_state_t _registers; +#ifdef __mips_hard_float + /// O32 with 32-bit floating point registers only uses half of this + /// space. However, using the same layout for 32-bit vs 64-bit + /// floating point registers results in a single context size for + /// O32 with hard float. + uint32_t _padding; + double _floats[32]; +#endif +}; + +inline Registers_mips_o32::Registers_mips_o32(const void *registers) { + static_assert((check_fit::does_fit), + "mips_o32 registers do not fit into unw_context_t"); + memcpy(&_registers, static_cast(registers), + sizeof(_registers)); +} + +inline Registers_mips_o32::Registers_mips_o32() { + memset(&_registers, 0, sizeof(_registers)); +} + +inline bool Registers_mips_o32::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum <= UNW_MIPS_R31) + return true; +#if __mips_isa_rev != 6 + if (regNum == UNW_MIPS_HI) + return true; + if (regNum == UNW_MIPS_LO) + return true; +#endif +#if defined(__mips_hard_float) && __mips_fpr == 32 + if (regNum >= UNW_MIPS_F0 && regNum <= UNW_MIPS_F31) + return true; +#endif + // FIXME: DSP accumulator registers, MSA registers + return false; +} + +inline uint32_t Registers_mips_o32::getRegister(int regNum) const { + if (regNum >= UNW_MIPS_R0 && regNum <= UNW_MIPS_R31) + return _registers.__r[regNum - UNW_MIPS_R0]; +#if defined(__mips_hard_float) && __mips_fpr == 32 + if (regNum >= UNW_MIPS_F0 && regNum <= UNW_MIPS_F31) { + uint32_t *p; + + if (regNum % 2 == 0) + p = (uint32_t *)&_floats[regNum - UNW_MIPS_F0]; + else + p = (uint32_t *)&_floats[(regNum - 1) - UNW_MIPS_F0] + 1; + return *p; + } +#endif + + switch (regNum) { + case UNW_REG_IP: + return _registers.__pc; + case UNW_REG_SP: + return _registers.__r[29]; + case UNW_MIPS_HI: + return _registers.__hi; + case UNW_MIPS_LO: + return _registers.__lo; + } + _LIBUNWIND_ABORT("unsupported mips_o32 register"); +} + +inline void Registers_mips_o32::setRegister(int regNum, uint32_t value) { + if (regNum >= UNW_MIPS_R0 && regNum <= UNW_MIPS_R31) { + _registers.__r[regNum - UNW_MIPS_R0] = value; + return; + } +#if defined(__mips_hard_float) && __mips_fpr == 32 + if (regNum >= UNW_MIPS_F0 && regNum <= UNW_MIPS_F31) { + uint32_t *p; + + if (regNum % 2 == 0) + p = (uint32_t *)&_floats[regNum - UNW_MIPS_F0]; + else + p = (uint32_t *)&_floats[(regNum - 1) - UNW_MIPS_F0] + 1; + *p = value; + return; + } +#endif + + switch (regNum) { + case UNW_REG_IP: + _registers.__pc = value; + return; + case UNW_REG_SP: + _registers.__r[29] = value; + return; + case UNW_MIPS_HI: + _registers.__hi = value; + return; + case UNW_MIPS_LO: + _registers.__lo = value; + return; + } + _LIBUNWIND_ABORT("unsupported mips_o32 register"); +} + +inline bool Registers_mips_o32::validFloatRegister(int regNum) const { +#if defined(__mips_hard_float) && __mips_fpr == 64 + if (regNum >= UNW_MIPS_F0 && regNum <= UNW_MIPS_F31) + return true; +#else + (void)regNum; +#endif + return false; +} + +inline double Registers_mips_o32::getFloatRegister(int regNum) const { +#if defined(__mips_hard_float) && __mips_fpr == 64 + assert(validFloatRegister(regNum)); + return _floats[regNum - UNW_MIPS_F0]; +#else + (void)regNum; + _LIBUNWIND_ABORT("mips_o32 float support not implemented"); +#endif +} + +inline void Registers_mips_o32::setFloatRegister(int regNum, + double value) { +#if defined(__mips_hard_float) && __mips_fpr == 64 + assert(validFloatRegister(regNum)); + _floats[regNum - UNW_MIPS_F0] = value; +#else + (void)regNum; + (void)value; + _LIBUNWIND_ABORT("mips_o32 float support not implemented"); +#endif +} + +inline bool Registers_mips_o32::validVectorRegister(int /* regNum */) const { + return false; +} + +inline v128 Registers_mips_o32::getVectorRegister(int /* regNum */) const { + _LIBUNWIND_ABORT("mips_o32 vector support not implemented"); +} + +inline void Registers_mips_o32::setVectorRegister(int /* regNum */, v128 /* value */) { + _LIBUNWIND_ABORT("mips_o32 vector support not implemented"); +} + +inline const char *Registers_mips_o32::getRegisterName(int regNum) { + switch (regNum) { + case UNW_MIPS_R0: + return "$0"; + case UNW_MIPS_R1: + return "$1"; + case UNW_MIPS_R2: + return "$2"; + case UNW_MIPS_R3: + return "$3"; + case UNW_MIPS_R4: + return "$4"; + case UNW_MIPS_R5: + return "$5"; + case UNW_MIPS_R6: + return "$6"; + case UNW_MIPS_R7: + return "$7"; + case UNW_MIPS_R8: + return "$8"; + case UNW_MIPS_R9: + return "$9"; + case UNW_MIPS_R10: + return "$10"; + case UNW_MIPS_R11: + return "$11"; + case UNW_MIPS_R12: + return "$12"; + case UNW_MIPS_R13: + return "$13"; + case UNW_MIPS_R14: + return "$14"; + case UNW_MIPS_R15: + return "$15"; + case UNW_MIPS_R16: + return "$16"; + case UNW_MIPS_R17: + return "$17"; + case UNW_MIPS_R18: + return "$18"; + case UNW_MIPS_R19: + return "$19"; + case UNW_MIPS_R20: + return "$20"; + case UNW_MIPS_R21: + return "$21"; + case UNW_MIPS_R22: + return "$22"; + case UNW_MIPS_R23: + return "$23"; + case UNW_MIPS_R24: + return "$24"; + case UNW_MIPS_R25: + return "$25"; + case UNW_MIPS_R26: + return "$26"; + case UNW_MIPS_R27: + return "$27"; + case UNW_MIPS_R28: + return "$28"; + case UNW_MIPS_R29: + return "$29"; + case UNW_MIPS_R30: + return "$30"; + case UNW_MIPS_R31: + return "$31"; + case UNW_MIPS_F0: + return "$f0"; + case UNW_MIPS_F1: + return "$f1"; + case UNW_MIPS_F2: + return "$f2"; + case UNW_MIPS_F3: + return "$f3"; + case UNW_MIPS_F4: + return "$f4"; + case UNW_MIPS_F5: + return "$f5"; + case UNW_MIPS_F6: + return "$f6"; + case UNW_MIPS_F7: + return "$f7"; + case UNW_MIPS_F8: + return "$f8"; + case UNW_MIPS_F9: + return "$f9"; + case UNW_MIPS_F10: + return "$f10"; + case UNW_MIPS_F11: + return "$f11"; + case UNW_MIPS_F12: + return "$f12"; + case UNW_MIPS_F13: + return "$f13"; + case UNW_MIPS_F14: + return "$f14"; + case UNW_MIPS_F15: + return "$f15"; + case UNW_MIPS_F16: + return "$f16"; + case UNW_MIPS_F17: + return "$f17"; + case UNW_MIPS_F18: + return "$f18"; + case UNW_MIPS_F19: + return "$f19"; + case UNW_MIPS_F20: + return "$f20"; + case UNW_MIPS_F21: + return "$f21"; + case UNW_MIPS_F22: + return "$f22"; + case UNW_MIPS_F23: + return "$f23"; + case UNW_MIPS_F24: + return "$f24"; + case UNW_MIPS_F25: + return "$f25"; + case UNW_MIPS_F26: + return "$f26"; + case UNW_MIPS_F27: + return "$f27"; + case UNW_MIPS_F28: + return "$f28"; + case UNW_MIPS_F29: + return "$f29"; + case UNW_MIPS_F30: + return "$f30"; + case UNW_MIPS_F31: + return "$f31"; + case UNW_MIPS_HI: + return "$hi"; + case UNW_MIPS_LO: + return "$lo"; + default: + return "unknown register"; + } +} +#endif // _LIBUNWIND_TARGET_MIPS_O32 + +#if defined(_LIBUNWIND_TARGET_MIPS_NEWABI) +/// Registers_mips_newabi holds the register state of a thread in a +/// MIPS process using NEWABI (the N32 or N64 ABIs). +class _LIBUNWIND_HIDDEN Registers_mips_newabi { +public: + Registers_mips_newabi(); + Registers_mips_newabi(const void *registers); + + bool validRegister(int num) const; + uint64_t getRegister(int num) const; + void setRegister(int num, uint64_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto(); + static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_MIPS; } + static int getArch() { return REGISTERS_MIPS_NEWABI; } + + uint64_t getSP() const { return _registers.__r[29]; } + void setSP(uint64_t value) { _registers.__r[29] = value; } + uint64_t getIP() const { return _registers.__pc; } + void setIP(uint64_t value) { _registers.__pc = value; } + +private: + struct mips_newabi_thread_state_t { + uint64_t __r[32]; + uint64_t __pc; + uint64_t __hi; + uint64_t __lo; + }; + + mips_newabi_thread_state_t _registers; +#ifdef __mips_hard_float + double _floats[32]; +#endif +}; + +inline Registers_mips_newabi::Registers_mips_newabi(const void *registers) { + static_assert((check_fit::does_fit), + "mips_newabi registers do not fit into unw_context_t"); + memcpy(&_registers, static_cast(registers), + sizeof(_registers)); +} + +inline Registers_mips_newabi::Registers_mips_newabi() { + memset(&_registers, 0, sizeof(_registers)); +} + +inline bool Registers_mips_newabi::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum <= UNW_MIPS_R31) + return true; +#if __mips_isa_rev != 6 + if (regNum == UNW_MIPS_HI) + return true; + if (regNum == UNW_MIPS_LO) + return true; +#endif + // FIXME: Hard float, DSP accumulator registers, MSA registers + return false; +} + +inline uint64_t Registers_mips_newabi::getRegister(int regNum) const { + if (regNum >= UNW_MIPS_R0 && regNum <= UNW_MIPS_R31) + return _registers.__r[regNum - UNW_MIPS_R0]; + + switch (regNum) { + case UNW_REG_IP: + return _registers.__pc; + case UNW_REG_SP: + return _registers.__r[29]; + case UNW_MIPS_HI: + return _registers.__hi; + case UNW_MIPS_LO: + return _registers.__lo; + } + _LIBUNWIND_ABORT("unsupported mips_newabi register"); +} + +inline void Registers_mips_newabi::setRegister(int regNum, uint64_t value) { + if (regNum >= UNW_MIPS_R0 && regNum <= UNW_MIPS_R31) { + _registers.__r[regNum - UNW_MIPS_R0] = value; + return; + } + + switch (regNum) { + case UNW_REG_IP: + _registers.__pc = value; + return; + case UNW_REG_SP: + _registers.__r[29] = value; + return; + case UNW_MIPS_HI: + _registers.__hi = value; + return; + case UNW_MIPS_LO: + _registers.__lo = value; + return; + } + _LIBUNWIND_ABORT("unsupported mips_newabi register"); +} + +inline bool Registers_mips_newabi::validFloatRegister(int regNum) const { +#ifdef __mips_hard_float + if (regNum >= UNW_MIPS_F0 && regNum <= UNW_MIPS_F31) + return true; +#else + (void)regNum; +#endif + return false; +} + +inline double Registers_mips_newabi::getFloatRegister(int regNum) const { +#ifdef __mips_hard_float + assert(validFloatRegister(regNum)); + return _floats[regNum - UNW_MIPS_F0]; +#else + (void)regNum; + _LIBUNWIND_ABORT("mips_newabi float support not implemented"); +#endif +} + +inline void Registers_mips_newabi::setFloatRegister(int regNum, + double value) { +#ifdef __mips_hard_float + assert(validFloatRegister(regNum)); + _floats[regNum - UNW_MIPS_F0] = value; +#else + (void)regNum; + (void)value; + _LIBUNWIND_ABORT("mips_newabi float support not implemented"); +#endif +} + +inline bool Registers_mips_newabi::validVectorRegister(int /* regNum */) const { + return false; +} + +inline v128 Registers_mips_newabi::getVectorRegister(int /* regNum */) const { + _LIBUNWIND_ABORT("mips_newabi vector support not implemented"); +} + +inline void Registers_mips_newabi::setVectorRegister(int /* regNum */, v128 /* value */) { + _LIBUNWIND_ABORT("mips_newabi vector support not implemented"); +} + +inline const char *Registers_mips_newabi::getRegisterName(int regNum) { + switch (regNum) { + case UNW_MIPS_R0: + return "$0"; + case UNW_MIPS_R1: + return "$1"; + case UNW_MIPS_R2: + return "$2"; + case UNW_MIPS_R3: + return "$3"; + case UNW_MIPS_R4: + return "$4"; + case UNW_MIPS_R5: + return "$5"; + case UNW_MIPS_R6: + return "$6"; + case UNW_MIPS_R7: + return "$7"; + case UNW_MIPS_R8: + return "$8"; + case UNW_MIPS_R9: + return "$9"; + case UNW_MIPS_R10: + return "$10"; + case UNW_MIPS_R11: + return "$11"; + case UNW_MIPS_R12: + return "$12"; + case UNW_MIPS_R13: + return "$13"; + case UNW_MIPS_R14: + return "$14"; + case UNW_MIPS_R15: + return "$15"; + case UNW_MIPS_R16: + return "$16"; + case UNW_MIPS_R17: + return "$17"; + case UNW_MIPS_R18: + return "$18"; + case UNW_MIPS_R19: + return "$19"; + case UNW_MIPS_R20: + return "$20"; + case UNW_MIPS_R21: + return "$21"; + case UNW_MIPS_R22: + return "$22"; + case UNW_MIPS_R23: + return "$23"; + case UNW_MIPS_R24: + return "$24"; + case UNW_MIPS_R25: + return "$25"; + case UNW_MIPS_R26: + return "$26"; + case UNW_MIPS_R27: + return "$27"; + case UNW_MIPS_R28: + return "$28"; + case UNW_MIPS_R29: + return "$29"; + case UNW_MIPS_R30: + return "$30"; + case UNW_MIPS_R31: + return "$31"; + case UNW_MIPS_F0: + return "$f0"; + case UNW_MIPS_F1: + return "$f1"; + case UNW_MIPS_F2: + return "$f2"; + case UNW_MIPS_F3: + return "$f3"; + case UNW_MIPS_F4: + return "$f4"; + case UNW_MIPS_F5: + return "$f5"; + case UNW_MIPS_F6: + return "$f6"; + case UNW_MIPS_F7: + return "$f7"; + case UNW_MIPS_F8: + return "$f8"; + case UNW_MIPS_F9: + return "$f9"; + case UNW_MIPS_F10: + return "$f10"; + case UNW_MIPS_F11: + return "$f11"; + case UNW_MIPS_F12: + return "$f12"; + case UNW_MIPS_F13: + return "$f13"; + case UNW_MIPS_F14: + return "$f14"; + case UNW_MIPS_F15: + return "$f15"; + case UNW_MIPS_F16: + return "$f16"; + case UNW_MIPS_F17: + return "$f17"; + case UNW_MIPS_F18: + return "$f18"; + case UNW_MIPS_F19: + return "$f19"; + case UNW_MIPS_F20: + return "$f20"; + case UNW_MIPS_F21: + return "$f21"; + case UNW_MIPS_F22: + return "$f22"; + case UNW_MIPS_F23: + return "$f23"; + case UNW_MIPS_F24: + return "$f24"; + case UNW_MIPS_F25: + return "$f25"; + case UNW_MIPS_F26: + return "$f26"; + case UNW_MIPS_F27: + return "$f27"; + case UNW_MIPS_F28: + return "$f28"; + case UNW_MIPS_F29: + return "$f29"; + case UNW_MIPS_F30: + return "$f30"; + case UNW_MIPS_F31: + return "$f31"; + case UNW_MIPS_HI: + return "$hi"; + case UNW_MIPS_LO: + return "$lo"; + default: + return "unknown register"; + } +} +#endif // _LIBUNWIND_TARGET_MIPS_NEWABI + +#if defined(_LIBUNWIND_TARGET_SPARC) +/// Registers_sparc holds the register state of a thread in a 32-bit Sparc +/// process. +class _LIBUNWIND_HIDDEN Registers_sparc { +public: + Registers_sparc(); + Registers_sparc(const void *registers); + + bool validRegister(int num) const; + uint32_t getRegister(int num) const; + void setRegister(int num, uint32_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto(); + static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_SPARC; } + static int getArch() { return REGISTERS_SPARC; } + + uint64_t getSP() const { return _registers.__regs[UNW_SPARC_O6]; } + void setSP(uint32_t value) { _registers.__regs[UNW_SPARC_O6] = value; } + uint64_t getIP() const { return _registers.__regs[UNW_SPARC_O7]; } + void setIP(uint32_t value) { _registers.__regs[UNW_SPARC_O7] = value; } + +private: + struct sparc_thread_state_t { + unsigned int __regs[32]; + }; + + sparc_thread_state_t _registers; +}; + +inline Registers_sparc::Registers_sparc(const void *registers) { + static_assert((check_fit::does_fit), + "sparc registers do not fit into unw_context_t"); + memcpy(&_registers, static_cast(registers), + sizeof(_registers)); +} + +inline Registers_sparc::Registers_sparc() { + memset(&_registers, 0, sizeof(_registers)); +} + +inline bool Registers_sparc::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum <= UNW_SPARC_I7) + return true; + return false; +} + +inline uint32_t Registers_sparc::getRegister(int regNum) const { + if ((UNW_SPARC_G0 <= regNum) && (regNum <= UNW_SPARC_I7)) { + return _registers.__regs[regNum]; + } + + switch (regNum) { + case UNW_REG_IP: + return _registers.__regs[UNW_SPARC_O7]; + case UNW_REG_SP: + return _registers.__regs[UNW_SPARC_O6]; + } + _LIBUNWIND_ABORT("unsupported sparc register"); +} + +inline void Registers_sparc::setRegister(int regNum, uint32_t value) { + if ((UNW_SPARC_G0 <= regNum) && (regNum <= UNW_SPARC_I7)) { + _registers.__regs[regNum] = value; + return; + } + + switch (regNum) { + case UNW_REG_IP: + _registers.__regs[UNW_SPARC_O7] = value; + return; + case UNW_REG_SP: + _registers.__regs[UNW_SPARC_O6] = value; + return; + } + _LIBUNWIND_ABORT("unsupported sparc register"); +} + +inline bool Registers_sparc::validFloatRegister(int) const { return false; } + +inline double Registers_sparc::getFloatRegister(int) const { + _LIBUNWIND_ABORT("no Sparc float registers"); +} + +inline void Registers_sparc::setFloatRegister(int, double) { + _LIBUNWIND_ABORT("no Sparc float registers"); +} + +inline bool Registers_sparc::validVectorRegister(int) const { return false; } + +inline v128 Registers_sparc::getVectorRegister(int) const { + _LIBUNWIND_ABORT("no Sparc vector registers"); +} + +inline void Registers_sparc::setVectorRegister(int, v128) { + _LIBUNWIND_ABORT("no Sparc vector registers"); +} + +inline const char *Registers_sparc::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + return "pc"; + case UNW_SPARC_G0: + return "g0"; + case UNW_SPARC_G1: + return "g1"; + case UNW_SPARC_G2: + return "g2"; + case UNW_SPARC_G3: + return "g3"; + case UNW_SPARC_G4: + return "g4"; + case UNW_SPARC_G5: + return "g5"; + case UNW_SPARC_G6: + return "g6"; + case UNW_SPARC_G7: + return "g7"; + case UNW_SPARC_O0: + return "o0"; + case UNW_SPARC_O1: + return "o1"; + case UNW_SPARC_O2: + return "o2"; + case UNW_SPARC_O3: + return "o3"; + case UNW_SPARC_O4: + return "o4"; + case UNW_SPARC_O5: + return "o5"; + case UNW_REG_SP: + case UNW_SPARC_O6: + return "sp"; + case UNW_SPARC_O7: + return "o7"; + case UNW_SPARC_L0: + return "l0"; + case UNW_SPARC_L1: + return "l1"; + case UNW_SPARC_L2: + return "l2"; + case UNW_SPARC_L3: + return "l3"; + case UNW_SPARC_L4: + return "l4"; + case UNW_SPARC_L5: + return "l5"; + case UNW_SPARC_L6: + return "l6"; + case UNW_SPARC_L7: + return "l7"; + case UNW_SPARC_I0: + return "i0"; + case UNW_SPARC_I1: + return "i1"; + case UNW_SPARC_I2: + return "i2"; + case UNW_SPARC_I3: + return "i3"; + case UNW_SPARC_I4: + return "i4"; + case UNW_SPARC_I5: + return "i5"; + case UNW_SPARC_I6: + return "fp"; + case UNW_SPARC_I7: + return "i7"; + default: + return "unknown register"; + } +} +#endif // _LIBUNWIND_TARGET_SPARC + +#if defined(_LIBUNWIND_TARGET_HEXAGON) +/// Registers_hexagon holds the register state of a thread in a Hexagon QDSP6 +/// process. +class _LIBUNWIND_HIDDEN Registers_hexagon { +public: + Registers_hexagon(); + Registers_hexagon(const void *registers); + + bool validRegister(int num) const; + uint32_t getRegister(int num) const; + void setRegister(int num, uint32_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + const char *getRegisterName(int num); + void jumpto(); + static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_HEXAGON; } + static int getArch() { return REGISTERS_HEXAGON; } + + uint32_t getSP() const { return _registers.__r[UNW_HEXAGON_R29]; } + void setSP(uint32_t value) { _registers.__r[UNW_HEXAGON_R29] = value; } + uint32_t getIP() const { return _registers.__r[UNW_HEXAGON_PC]; } + void setIP(uint32_t value) { _registers.__r[UNW_HEXAGON_PC] = value; } + +private: + struct hexagon_thread_state_t { + unsigned int __r[35]; + }; + + hexagon_thread_state_t _registers; +}; + +inline Registers_hexagon::Registers_hexagon(const void *registers) { + static_assert((check_fit::does_fit), + "hexagon registers do not fit into unw_context_t"); + memcpy(&_registers, static_cast(registers), + sizeof(_registers)); +} + +inline Registers_hexagon::Registers_hexagon() { + memset(&_registers, 0, sizeof(_registers)); +} + +inline bool Registers_hexagon::validRegister(int regNum) const { + if (regNum <= UNW_HEXAGON_R31) + return true; + return false; +} + +inline uint32_t Registers_hexagon::getRegister(int regNum) const { + if (regNum >= UNW_HEXAGON_R0 && regNum <= UNW_HEXAGON_R31) + return _registers.__r[regNum - UNW_HEXAGON_R0]; + + switch (regNum) { + case UNW_REG_IP: + return _registers.__r[UNW_HEXAGON_PC]; + case UNW_REG_SP: + return _registers.__r[UNW_HEXAGON_R29]; + } + _LIBUNWIND_ABORT("unsupported hexagon register"); +} + +inline void Registers_hexagon::setRegister(int regNum, uint32_t value) { + if (regNum >= UNW_HEXAGON_R0 && regNum <= UNW_HEXAGON_R31) { + _registers.__r[regNum - UNW_HEXAGON_R0] = value; + return; + } + + switch (regNum) { + case UNW_REG_IP: + _registers.__r[UNW_HEXAGON_PC] = value; + return; + case UNW_REG_SP: + _registers.__r[UNW_HEXAGON_R29] = value; + return; + } + _LIBUNWIND_ABORT("unsupported hexagon register"); +} + +inline bool Registers_hexagon::validFloatRegister(int /* regNum */) const { + return false; +} + +inline double Registers_hexagon::getFloatRegister(int /* regNum */) const { + _LIBUNWIND_ABORT("hexagon float support not implemented"); +} + +inline void Registers_hexagon::setFloatRegister(int /* regNum */, + double /* value */) { + _LIBUNWIND_ABORT("hexagon float support not implemented"); +} + +inline bool Registers_hexagon::validVectorRegister(int /* regNum */) const { + return false; +} + +inline v128 Registers_hexagon::getVectorRegister(int /* regNum */) const { + _LIBUNWIND_ABORT("hexagon vector support not implemented"); +} + +inline void Registers_hexagon::setVectorRegister(int /* regNum */, v128 /* value */) { + _LIBUNWIND_ABORT("hexagon vector support not implemented"); +} + +inline const char *Registers_hexagon::getRegisterName(int regNum) { + switch (regNum) { + case UNW_HEXAGON_R0: + return "r0"; + case UNW_HEXAGON_R1: + return "r1"; + case UNW_HEXAGON_R2: + return "r2"; + case UNW_HEXAGON_R3: + return "r3"; + case UNW_HEXAGON_R4: + return "r4"; + case UNW_HEXAGON_R5: + return "r5"; + case UNW_HEXAGON_R6: + return "r6"; + case UNW_HEXAGON_R7: + return "r7"; + case UNW_HEXAGON_R8: + return "r8"; + case UNW_HEXAGON_R9: + return "r9"; + case UNW_HEXAGON_R10: + return "r10"; + case UNW_HEXAGON_R11: + return "r11"; + case UNW_HEXAGON_R12: + return "r12"; + case UNW_HEXAGON_R13: + return "r13"; + case UNW_HEXAGON_R14: + return "r14"; + case UNW_HEXAGON_R15: + return "r15"; + case UNW_HEXAGON_R16: + return "r16"; + case UNW_HEXAGON_R17: + return "r17"; + case UNW_HEXAGON_R18: + return "r18"; + case UNW_HEXAGON_R19: + return "r19"; + case UNW_HEXAGON_R20: + return "r20"; + case UNW_HEXAGON_R21: + return "r21"; + case UNW_HEXAGON_R22: + return "r22"; + case UNW_HEXAGON_R23: + return "r23"; + case UNW_HEXAGON_R24: + return "r24"; + case UNW_HEXAGON_R25: + return "r25"; + case UNW_HEXAGON_R26: + return "r26"; + case UNW_HEXAGON_R27: + return "r27"; + case UNW_HEXAGON_R28: + return "r28"; + case UNW_HEXAGON_R29: + return "r29"; + case UNW_HEXAGON_R30: + return "r30"; + case UNW_HEXAGON_R31: + return "r31"; + default: + return "unknown register"; + } + +} +#endif // _LIBUNWIND_TARGET_HEXAGON + + +#if defined(_LIBUNWIND_TARGET_RISCV) +/// Registers_riscv holds the register state of a thread in a RISC-V +/// process. + +// This check makes it safe when LIBUNWIND_ENABLE_CROSS_UNWINDING enabled. +# ifdef __riscv +# if __riscv_xlen == 32 +typedef uint32_t reg_t; +# elif __riscv_xlen == 64 +typedef uint64_t reg_t; +# else +# error "Unsupported __riscv_xlen" +# endif + +# if defined(__riscv_flen) +# if __riscv_flen == 64 +typedef double fp_t; +# elif __riscv_flen == 32 +typedef float fp_t; +# else +# error "Unsupported __riscv_flen" +# endif +# else +// This is just for supressing undeclared error of fp_t. +typedef double fp_t; +# endif +# else +// Use Max possible width when cross unwinding +typedef uint64_t reg_t; +typedef double fp_t; +# define __riscv_xlen 64 +# define __riscv_flen 64 +#endif + +/// Registers_riscv holds the register state of a thread. +class _LIBUNWIND_HIDDEN Registers_riscv { +public: + Registers_riscv(); + Registers_riscv(const void *registers); + + bool validRegister(int num) const; + reg_t getRegister(int num) const; + void setRegister(int num, reg_t value); + bool validFloatRegister(int num) const; + fp_t getFloatRegister(int num) const; + void setFloatRegister(int num, fp_t value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto(); + static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_RISCV; } + static int getArch() { return REGISTERS_RISCV; } + + reg_t getSP() const { return _registers[2]; } + void setSP(reg_t value) { _registers[2] = value; } + reg_t getIP() const { return _registers[0]; } + void setIP(reg_t value) { _registers[0] = value; } + +private: + // _registers[0] holds the pc + reg_t _registers[32]; +# if defined(__riscv_flen) + fp_t _floats[32]; +# endif +}; + +inline Registers_riscv::Registers_riscv(const void *registers) { + static_assert((check_fit::does_fit), + "riscv registers do not fit into unw_context_t"); + memcpy(&_registers, registers, sizeof(_registers)); +# if __riscv_xlen == 32 + static_assert(sizeof(_registers) == 0x80, + "expected float registers to be at offset 128"); +# elif __riscv_xlen == 64 + static_assert(sizeof(_registers) == 0x100, + "expected float registers to be at offset 256"); +# else +# error "Unexpected float registers." +# endif + +# if defined(__riscv_flen) + memcpy(_floats, + static_cast(registers) + sizeof(_registers), + sizeof(_floats)); +# endif +} + +inline Registers_riscv::Registers_riscv() { + memset(&_registers, 0, sizeof(_registers)); +# if defined(__riscv_flen) + memset(&_floats, 0, sizeof(_floats)); +# endif +} + +inline bool Registers_riscv::validRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return true; + if (regNum == UNW_REG_SP) + return true; + if (regNum < 0) + return false; + if (regNum > UNW_RISCV_F31) + return false; + return true; +} + +inline reg_t Registers_riscv::getRegister(int regNum) const { + if (regNum == UNW_REG_IP) + return _registers[0]; + if (regNum == UNW_REG_SP) + return _registers[2]; + if (regNum == UNW_RISCV_X0) + return 0; + if ((regNum > 0) && (regNum < 32)) + return _registers[regNum]; + _LIBUNWIND_ABORT("unsupported riscv register"); +} + +inline void Registers_riscv::setRegister(int regNum, reg_t value) { + if (regNum == UNW_REG_IP) + _registers[0] = value; + else if (regNum == UNW_REG_SP) + _registers[2] = value; + else if (regNum == UNW_RISCV_X0) + /* x0 is hardwired to zero */ + return; + else if ((regNum > 0) && (regNum < 32)) + _registers[regNum] = value; + else + _LIBUNWIND_ABORT("unsupported riscv register"); +} + +inline const char *Registers_riscv::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + return "pc"; + case UNW_REG_SP: + return "sp"; + case UNW_RISCV_X0: + return "zero"; + case UNW_RISCV_X1: + return "ra"; + case UNW_RISCV_X2: + return "sp"; + case UNW_RISCV_X3: + return "gp"; + case UNW_RISCV_X4: + return "tp"; + case UNW_RISCV_X5: + return "t0"; + case UNW_RISCV_X6: + return "t1"; + case UNW_RISCV_X7: + return "t2"; + case UNW_RISCV_X8: + return "s0"; + case UNW_RISCV_X9: + return "s1"; + case UNW_RISCV_X10: + return "a0"; + case UNW_RISCV_X11: + return "a1"; + case UNW_RISCV_X12: + return "a2"; + case UNW_RISCV_X13: + return "a3"; + case UNW_RISCV_X14: + return "a4"; + case UNW_RISCV_X15: + return "a5"; + case UNW_RISCV_X16: + return "a6"; + case UNW_RISCV_X17: + return "a7"; + case UNW_RISCV_X18: + return "s2"; + case UNW_RISCV_X19: + return "s3"; + case UNW_RISCV_X20: + return "s4"; + case UNW_RISCV_X21: + return "s5"; + case UNW_RISCV_X22: + return "s6"; + case UNW_RISCV_X23: + return "s7"; + case UNW_RISCV_X24: + return "s8"; + case UNW_RISCV_X25: + return "s9"; + case UNW_RISCV_X26: + return "s10"; + case UNW_RISCV_X27: + return "s11"; + case UNW_RISCV_X28: + return "t3"; + case UNW_RISCV_X29: + return "t4"; + case UNW_RISCV_X30: + return "t5"; + case UNW_RISCV_X31: + return "t6"; + case UNW_RISCV_F0: + return "ft0"; + case UNW_RISCV_F1: + return "ft1"; + case UNW_RISCV_F2: + return "ft2"; + case UNW_RISCV_F3: + return "ft3"; + case UNW_RISCV_F4: + return "ft4"; + case UNW_RISCV_F5: + return "ft5"; + case UNW_RISCV_F6: + return "ft6"; + case UNW_RISCV_F7: + return "ft7"; + case UNW_RISCV_F8: + return "fs0"; + case UNW_RISCV_F9: + return "fs1"; + case UNW_RISCV_F10: + return "fa0"; + case UNW_RISCV_F11: + return "fa1"; + case UNW_RISCV_F12: + return "fa2"; + case UNW_RISCV_F13: + return "fa3"; + case UNW_RISCV_F14: + return "fa4"; + case UNW_RISCV_F15: + return "fa5"; + case UNW_RISCV_F16: + return "fa6"; + case UNW_RISCV_F17: + return "fa7"; + case UNW_RISCV_F18: + return "fs2"; + case UNW_RISCV_F19: + return "fs3"; + case UNW_RISCV_F20: + return "fs4"; + case UNW_RISCV_F21: + return "fs5"; + case UNW_RISCV_F22: + return "fs6"; + case UNW_RISCV_F23: + return "fs7"; + case UNW_RISCV_F24: + return "fs8"; + case UNW_RISCV_F25: + return "fs9"; + case UNW_RISCV_F26: + return "fs10"; + case UNW_RISCV_F27: + return "fs11"; + case UNW_RISCV_F28: + return "ft8"; + case UNW_RISCV_F29: + return "ft9"; + case UNW_RISCV_F30: + return "ft10"; + case UNW_RISCV_F31: + return "ft11"; + default: + return "unknown register"; + } +} + +inline bool Registers_riscv::validFloatRegister(int regNum) const { +# if defined(__riscv_flen) + if (regNum < UNW_RISCV_F0) + return false; + if (regNum > UNW_RISCV_F31) + return false; + return true; +# else + (void)regNum; + return false; +# endif +} + +inline fp_t Registers_riscv::getFloatRegister(int regNum) const { +# if defined(__riscv_flen) + assert(validFloatRegister(regNum)); + return _floats[regNum - UNW_RISCV_F0]; +# else + (void)regNum; + _LIBUNWIND_ABORT("libunwind not built with float support"); +# endif +} + +inline void Registers_riscv::setFloatRegister(int regNum, fp_t value) { +# if defined(__riscv_flen) + assert(validFloatRegister(regNum)); + _floats[regNum - UNW_RISCV_F0] = value; +# else + (void)regNum; + (void)value; + _LIBUNWIND_ABORT("libunwind not built with float support"); +# endif +} + +inline bool Registers_riscv::validVectorRegister(int) const { + return false; +} + +inline v128 Registers_riscv::getVectorRegister(int) const { + _LIBUNWIND_ABORT("no riscv vector register support yet"); +} + +inline void Registers_riscv::setVectorRegister(int, v128) { + _LIBUNWIND_ABORT("no riscv vector register support yet"); +} +#endif // _LIBUNWIND_TARGET_RISCV + +#if defined(_LIBUNWIND_TARGET_VE) +/// Registers_ve holds the register state of a thread in a VE process. +class _LIBUNWIND_HIDDEN Registers_ve { +public: + Registers_ve(); + Registers_ve(const void *registers); + + bool validRegister(int num) const; + uint64_t getRegister(int num) const; + void setRegister(int num, uint64_t value); + bool validFloatRegister(int num) const; + double getFloatRegister(int num) const; + void setFloatRegister(int num, double value); + bool validVectorRegister(int num) const; + v128 getVectorRegister(int num) const; + void setVectorRegister(int num, v128 value); + static const char *getRegisterName(int num); + void jumpto(); + static int lastDwarfRegNum() { return _LIBUNWIND_HIGHEST_DWARF_REGISTER_VE; } + static int getArch() { return REGISTERS_VE; } + + uint64_t getSP() const { return _registers.__s[11]; } + void setSP(uint64_t value) { _registers.__s[11] = value; } + uint64_t getIP() const { return _registers.__ic; } + void setIP(uint64_t value) { _registers.__ic = value; } + +private: + // FIXME: Need to store not only scalar registers but also vector and vector + // mask registers. VEOS uses mcontext_t defined in ucontext.h. It takes + // 524288 bytes (65536*8 bytes), though. Currently, we use libunwind for + // SjLj exception support only, so Registers_ve is not implemented completely. + struct ve_thread_state_t { + uint64_t __s[64]; // s0-s64 + uint64_t __ic; // Instruction counter (IC) + uint64_t __vixr; // Vector Index Register + uint64_t __vl; // Vector Length Register + }; + + ve_thread_state_t _registers; // total 67 registers + + // Currently no vector register is preserved. +}; + +inline Registers_ve::Registers_ve(const void *registers) { + static_assert((check_fit::does_fit), + "ve registers do not fit into unw_context_t"); + memcpy(&_registers, static_cast(registers), + sizeof(_registers)); + static_assert(sizeof(_registers) == 536, + "expected vector register offset to be 536"); +} + +inline Registers_ve::Registers_ve() { + memset(&_registers, 0, sizeof(_registers)); +} + +inline bool Registers_ve::validRegister(int regNum) const { + if (regNum >= UNW_VE_S0 && regNum <= UNW_VE_S63) + return true; + + switch (regNum) { + case UNW_REG_IP: + case UNW_REG_SP: + case UNW_VE_VIXR: + case UNW_VE_VL: + return true; + default: + return false; + } +} + +inline uint64_t Registers_ve::getRegister(int regNum) const { + if (regNum >= UNW_VE_S0 && regNum <= UNW_VE_S63) + return _registers.__s[regNum - UNW_VE_S0]; + + switch (regNum) { + case UNW_REG_IP: + return _registers.__ic; + case UNW_REG_SP: + return _registers.__s[11]; + case UNW_VE_VIXR: + return _registers.__vixr; + case UNW_VE_VL: + return _registers.__vl; + } + _LIBUNWIND_ABORT("unsupported ve register"); +} + +inline void Registers_ve::setRegister(int regNum, uint64_t value) { + if (regNum >= UNW_VE_S0 && regNum <= UNW_VE_S63) { + _registers.__s[regNum - UNW_VE_S0] = value; + return; + } + + switch (regNum) { + case UNW_REG_IP: + _registers.__ic = value; + return; + case UNW_REG_SP: + _registers.__s[11] = value; + return; + case UNW_VE_VIXR: + _registers.__vixr = value; + return; + case UNW_VE_VL: + _registers.__vl = value; + return; + } + _LIBUNWIND_ABORT("unsupported ve register"); +} + +inline bool Registers_ve::validFloatRegister(int /* regNum */) const { + return false; +} + +inline double Registers_ve::getFloatRegister(int /* regNum */) const { + _LIBUNWIND_ABORT("VE doesn't have float registers"); +} + +inline void Registers_ve::setFloatRegister(int /* regNum */, + double /* value */) { + _LIBUNWIND_ABORT("VE doesn't have float registers"); +} + +inline bool Registers_ve::validVectorRegister(int /* regNum */) const { + return false; +} + +inline v128 Registers_ve::getVectorRegister(int /* regNum */) const { + _LIBUNWIND_ABORT("VE vector support not implemented"); +} + +inline void Registers_ve::setVectorRegister(int /* regNum */, + v128 /* value */) { + _LIBUNWIND_ABORT("VE vector support not implemented"); +} + +inline const char *Registers_ve::getRegisterName(int regNum) { + switch (regNum) { + case UNW_REG_IP: + return "ip"; + case UNW_REG_SP: + return "sp"; + case UNW_VE_VIXR: + return "vixr"; + case UNW_VE_VL: + return "vl"; + case UNW_VE_S0: + return "s0"; + case UNW_VE_S1: + return "s1"; + case UNW_VE_S2: + return "s2"; + case UNW_VE_S3: + return "s3"; + case UNW_VE_S4: + return "s4"; + case UNW_VE_S5: + return "s5"; + case UNW_VE_S6: + return "s6"; + case UNW_VE_S7: + return "s7"; + case UNW_VE_S8: + return "s8"; + case UNW_VE_S9: + return "s9"; + case UNW_VE_S10: + return "s10"; + case UNW_VE_S11: + return "s11"; + case UNW_VE_S12: + return "s12"; + case UNW_VE_S13: + return "s13"; + case UNW_VE_S14: + return "s14"; + case UNW_VE_S15: + return "s15"; + case UNW_VE_S16: + return "s16"; + case UNW_VE_S17: + return "s17"; + case UNW_VE_S18: + return "s18"; + case UNW_VE_S19: + return "s19"; + case UNW_VE_S20: + return "s20"; + case UNW_VE_S21: + return "s21"; + case UNW_VE_S22: + return "s22"; + case UNW_VE_S23: + return "s23"; + case UNW_VE_S24: + return "s24"; + case UNW_VE_S25: + return "s25"; + case UNW_VE_S26: + return "s26"; + case UNW_VE_S27: + return "s27"; + case UNW_VE_S28: + return "s28"; + case UNW_VE_S29: + return "s29"; + case UNW_VE_S30: + return "s30"; + case UNW_VE_S31: + return "s31"; + case UNW_VE_S32: + return "s32"; + case UNW_VE_S33: + return "s33"; + case UNW_VE_S34: + return "s34"; + case UNW_VE_S35: + return "s35"; + case UNW_VE_S36: + return "s36"; + case UNW_VE_S37: + return "s37"; + case UNW_VE_S38: + return "s38"; + case UNW_VE_S39: + return "s39"; + case UNW_VE_S40: + return "s40"; + case UNW_VE_S41: + return "s41"; + case UNW_VE_S42: + return "s42"; + case UNW_VE_S43: + return "s43"; + case UNW_VE_S44: + return "s44"; + case UNW_VE_S45: + return "s45"; + case UNW_VE_S46: + return "s46"; + case UNW_VE_S47: + return "s47"; + case UNW_VE_S48: + return "s48"; + case UNW_VE_S49: + return "s49"; + case UNW_VE_S50: + return "s50"; + case UNW_VE_S51: + return "s51"; + case UNW_VE_S52: + return "s52"; + case UNW_VE_S53: + return "s53"; + case UNW_VE_S54: + return "s54"; + case UNW_VE_S55: + return "s55"; + case UNW_VE_S56: + return "s56"; + case UNW_VE_S57: + return "s57"; + case UNW_VE_S58: + return "s58"; + case UNW_VE_S59: + return "s59"; + case UNW_VE_S60: + return "s60"; + case UNW_VE_S61: + return "s61"; + case UNW_VE_S62: + return "s62"; + case UNW_VE_S63: + return "s63"; + case UNW_VE_V0: + return "v0"; + case UNW_VE_V1: + return "v1"; + case UNW_VE_V2: + return "v2"; + case UNW_VE_V3: + return "v3"; + case UNW_VE_V4: + return "v4"; + case UNW_VE_V5: + return "v5"; + case UNW_VE_V6: + return "v6"; + case UNW_VE_V7: + return "v7"; + case UNW_VE_V8: + return "v8"; + case UNW_VE_V9: + return "v9"; + case UNW_VE_V10: + return "v10"; + case UNW_VE_V11: + return "v11"; + case UNW_VE_V12: + return "v12"; + case UNW_VE_V13: + return "v13"; + case UNW_VE_V14: + return "v14"; + case UNW_VE_V15: + return "v15"; + case UNW_VE_V16: + return "v16"; + case UNW_VE_V17: + return "v17"; + case UNW_VE_V18: + return "v18"; + case UNW_VE_V19: + return "v19"; + case UNW_VE_V20: + return "v20"; + case UNW_VE_V21: + return "v21"; + case UNW_VE_V22: + return "v22"; + case UNW_VE_V23: + return "v23"; + case UNW_VE_V24: + return "v24"; + case UNW_VE_V25: + return "v25"; + case UNW_VE_V26: + return "v26"; + case UNW_VE_V27: + return "v27"; + case UNW_VE_V28: + return "v28"; + case UNW_VE_V29: + return "v29"; + case UNW_VE_V30: + return "v30"; + case UNW_VE_V31: + return "v31"; + case UNW_VE_V32: + return "v32"; + case UNW_VE_V33: + return "v33"; + case UNW_VE_V34: + return "v34"; + case UNW_VE_V35: + return "v35"; + case UNW_VE_V36: + return "v36"; + case UNW_VE_V37: + return "v37"; + case UNW_VE_V38: + return "v38"; + case UNW_VE_V39: + return "v39"; + case UNW_VE_V40: + return "v40"; + case UNW_VE_V41: + return "v41"; + case UNW_VE_V42: + return "v42"; + case UNW_VE_V43: + return "v43"; + case UNW_VE_V44: + return "v44"; + case UNW_VE_V45: + return "v45"; + case UNW_VE_V46: + return "v46"; + case UNW_VE_V47: + return "v47"; + case UNW_VE_V48: + return "v48"; + case UNW_VE_V49: + return "v49"; + case UNW_VE_V50: + return "v50"; + case UNW_VE_V51: + return "v51"; + case UNW_VE_V52: + return "v52"; + case UNW_VE_V53: + return "v53"; + case UNW_VE_V54: + return "v54"; + case UNW_VE_V55: + return "v55"; + case UNW_VE_V56: + return "v56"; + case UNW_VE_V57: + return "v57"; + case UNW_VE_V58: + return "v58"; + case UNW_VE_V59: + return "v59"; + case UNW_VE_V60: + return "v60"; + case UNW_VE_V61: + return "v61"; + case UNW_VE_V62: + return "v62"; + case UNW_VE_V63: + return "v63"; + case UNW_VE_VM0: + return "vm0"; + case UNW_VE_VM1: + return "vm1"; + case UNW_VE_VM2: + return "vm2"; + case UNW_VE_VM3: + return "vm3"; + case UNW_VE_VM4: + return "vm4"; + case UNW_VE_VM5: + return "vm5"; + case UNW_VE_VM6: + return "vm6"; + case UNW_VE_VM7: + return "vm7"; + case UNW_VE_VM8: + return "vm8"; + case UNW_VE_VM9: + return "vm9"; + case UNW_VE_VM10: + return "vm10"; + case UNW_VE_VM11: + return "vm11"; + case UNW_VE_VM12: + return "vm12"; + case UNW_VE_VM13: + return "vm13"; + case UNW_VE_VM14: + return "vm14"; + case UNW_VE_VM15: + return "vm15"; + } + return "unknown register"; +} +#endif // _LIBUNWIND_TARGET_VE + +} // namespace libunwind + +#endif // __REGISTERS_HPP__ diff --git a/shared/sentry/external/crashpad/libunwind/src/Unwind-EHABI.cpp b/shared/sentry/external/crashpad/libunwind/src/Unwind-EHABI.cpp new file mode 100644 index 000000000..a564fd524 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/src/Unwind-EHABI.cpp @@ -0,0 +1,1141 @@ +//===--------------------------- Unwind-EHABI.cpp -------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Implements ARM zero-cost C++ exceptions +// +//===----------------------------------------------------------------------===// + +#include "Unwind-EHABI.h" + +#if defined(_LIBUNWIND_ARM_EHABI) + +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "libunwind.h" +#include "libunwind_ext.h" +#include "unwind.h" + +namespace { + +// Strange order: take words in order, but inside word, take from most to least +// signinficant byte. +uint8_t getByte(const uint32_t* data, size_t offset) { + const uint8_t* byteData = reinterpret_cast(data); +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + return byteData[(offset & ~(size_t)0x03) + (3 - (offset & (size_t)0x03))]; +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + return byteData[offset]; +#else +#error "Unable to determine endianess" +#endif +} + +const char* getNextWord(const char* data, uint32_t* out) { + *out = *reinterpret_cast(data); + return data + 4; +} + +const char* getNextNibble(const char* data, uint32_t* out) { + *out = *reinterpret_cast(data); + return data + 2; +} + +struct Descriptor { + // See # 9.2 + typedef enum { + SU16 = 0, // Short descriptor, 16-bit entries + LU16 = 1, // Long descriptor, 16-bit entries + LU32 = 3, // Long descriptor, 32-bit entries + RESERVED0 = 4, RESERVED1 = 5, RESERVED2 = 6, RESERVED3 = 7, + RESERVED4 = 8, RESERVED5 = 9, RESERVED6 = 10, RESERVED7 = 11, + RESERVED8 = 12, RESERVED9 = 13, RESERVED10 = 14, RESERVED11 = 15 + } Format; + + // See # 9.2 + typedef enum { + CLEANUP = 0x0, + FUNC = 0x1, + CATCH = 0x2, + INVALID = 0x4 + } Kind; +}; + +_Unwind_Reason_Code ProcessDescriptors( + _Unwind_State state, + _Unwind_Control_Block* ucbp, + struct _Unwind_Context* context, + Descriptor::Format format, + const char* descriptorStart, + uint32_t flags) { + + // EHT is inlined in the index using compact form. No descriptors. #5 + if (flags & 0x1) + return _URC_CONTINUE_UNWIND; + + // TODO: We should check the state here, and determine whether we need to + // perform phase1 or phase2 unwinding. + (void)state; + + const char* descriptor = descriptorStart; + uint32_t descriptorWord; + getNextWord(descriptor, &descriptorWord); + while (descriptorWord) { + // Read descriptor based on # 9.2. + uint32_t length; + uint32_t offset; + switch (format) { + case Descriptor::LU32: + descriptor = getNextWord(descriptor, &length); + descriptor = getNextWord(descriptor, &offset); + break; + case Descriptor::LU16: + descriptor = getNextNibble(descriptor, &length); + descriptor = getNextNibble(descriptor, &offset); + break; + default: + assert(false); + return _URC_FAILURE; + } + + // See # 9.2 table for decoding the kind of descriptor. It's a 2-bit value. + Descriptor::Kind kind = + static_cast((length & 0x1) | ((offset & 0x1) << 1)); + + // Clear off flag from last bit. + length &= ~1u; + offset &= ~1u; + uintptr_t scopeStart = ucbp->pr_cache.fnstart + offset; + uintptr_t scopeEnd = scopeStart + length; + uintptr_t pc = _Unwind_GetIP(context); + bool isInScope = (scopeStart <= pc) && (pc < scopeEnd); + + switch (kind) { + case Descriptor::CLEANUP: { + // TODO(ajwong): Handle cleanup descriptors. + break; + } + case Descriptor::FUNC: { + // TODO(ajwong): Handle function descriptors. + break; + } + case Descriptor::CATCH: { + // Catch descriptors require gobbling one more word. + uint32_t landing_pad; + descriptor = getNextWord(descriptor, &landing_pad); + + if (isInScope) { + // TODO(ajwong): This is only phase1 compatible logic. Implement + // phase2. + landing_pad = signExtendPrel31(landing_pad & ~0x80000000); + if (landing_pad == 0xffffffff) { + return _URC_HANDLER_FOUND; + } else if (landing_pad == 0xfffffffe) { + return _URC_FAILURE; + } else { + /* + bool is_reference_type = landing_pad & 0x80000000; + void* matched_object; + if (__cxxabiv1::__cxa_type_match( + ucbp, reinterpret_cast(landing_pad), + is_reference_type, + &matched_object) != __cxxabiv1::ctm_failed) + return _URC_HANDLER_FOUND; + */ + _LIBUNWIND_ABORT("Type matching not implemented"); + } + } + break; + } + default: + _LIBUNWIND_ABORT("Invalid descriptor kind found."); + } + + getNextWord(descriptor, &descriptorWord); + } + + return _URC_CONTINUE_UNWIND; +} + +static _Unwind_Reason_Code unwindOneFrame(_Unwind_State state, + _Unwind_Control_Block* ucbp, + struct _Unwind_Context* context) { + // Read the compact model EHT entry's header # 6.3 + const uint32_t* unwindingData = ucbp->pr_cache.ehtp; + assert((*unwindingData & 0xf0000000) == 0x80000000 && "Must be a compact entry"); + Descriptor::Format format = + static_cast((*unwindingData & 0x0f000000) >> 24); + + const char *lsda = + reinterpret_cast(_Unwind_GetLanguageSpecificData(context)); + + // Handle descriptors before unwinding so they are processed in the context + // of the correct stack frame. + _Unwind_Reason_Code result = + ProcessDescriptors(state, ucbp, context, format, lsda, + ucbp->pr_cache.additional); + + if (result != _URC_CONTINUE_UNWIND) + return result; + + if (__unw_step(reinterpret_cast(context)) != UNW_STEP_SUCCESS) + return _URC_FAILURE; + return _URC_CONTINUE_UNWIND; +} + +// Generates mask discriminator for _Unwind_VRS_Pop, e.g. for _UVRSC_CORE / +// _UVRSD_UINT32. +uint32_t RegisterMask(uint8_t start, uint8_t count_minus_one) { + return ((1U << (count_minus_one + 1)) - 1) << start; +} + +// Generates mask discriminator for _Unwind_VRS_Pop, e.g. for _UVRSC_VFP / +// _UVRSD_DOUBLE. +uint32_t RegisterRange(uint8_t start, uint8_t count_minus_one) { + return ((uint32_t)start << 16) | ((uint32_t)count_minus_one + 1); +} + +} // end anonymous namespace + +/** + * Decodes an EHT entry. + * + * @param data Pointer to EHT. + * @param[out] off Offset from return value (in bytes) to begin interpretation. + * @param[out] len Number of bytes in unwind code. + * @return Pointer to beginning of unwind code. + */ +extern "C" const uint32_t* +decode_eht_entry(const uint32_t* data, size_t* off, size_t* len) { + if ((*data & 0x80000000) == 0) { + // 6.2: Generic Model + // + // EHT entry is a prel31 pointing to the PR, followed by data understood + // only by the personality routine. Fortunately, all existing assembler + // implementations, including GNU assembler, LLVM integrated assembler, + // and ARM assembler, assume that the unwind opcodes come after the + // personality rountine address. + *off = 1; // First byte is size data. + *len = (((data[1] >> 24) & 0xff) + 1) * 4; + data++; // Skip the first word, which is the prel31 offset. + } else { + // 6.3: ARM Compact Model + // + // EHT entries here correspond to the __aeabi_unwind_cpp_pr[012] PRs indeded + // by format: + Descriptor::Format format = + static_cast((*data & 0x0f000000) >> 24); + switch (format) { + case Descriptor::SU16: + *len = 4; + *off = 1; + break; + case Descriptor::LU16: + case Descriptor::LU32: + *len = 4 + 4 * ((*data & 0x00ff0000) >> 16); + *off = 2; + break; + default: + return nullptr; + } + } + return data; +} + +_LIBUNWIND_EXPORT _Unwind_Reason_Code +_Unwind_VRS_Interpret(_Unwind_Context *context, const uint32_t *data, + size_t offset, size_t len) { + bool wrotePC = false; + bool finish = false; + while (offset < len && !finish) { + uint8_t byte = getByte(data, offset++); + if ((byte & 0x80) == 0) { + uint32_t sp; + _Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, &sp); + if (byte & 0x40) + sp -= (((uint32_t)byte & 0x3f) << 2) + 4; + else + sp += ((uint32_t)byte << 2) + 4; + _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, &sp); + } else { + switch (byte & 0xf0) { + case 0x80: { + if (offset >= len) + return _URC_FAILURE; + uint32_t registers = + (((uint32_t)byte & 0x0f) << 12) | + (((uint32_t)getByte(data, offset++)) << 4); + if (!registers) + return _URC_FAILURE; + if (registers & (1 << 15)) + wrotePC = true; + _Unwind_VRS_Pop(context, _UVRSC_CORE, registers, _UVRSD_UINT32); + break; + } + case 0x90: { + uint8_t reg = byte & 0x0f; + if (reg == 13 || reg == 15) + return _URC_FAILURE; + uint32_t sp; + _Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_R0 + reg, + _UVRSD_UINT32, &sp); + _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, + &sp); + break; + } + case 0xa0: { + uint32_t registers = RegisterMask(4, byte & 0x07); + if (byte & 0x08) + registers |= 1 << 14; + _Unwind_VRS_Pop(context, _UVRSC_CORE, registers, _UVRSD_UINT32); + break; + } + case 0xb0: { + switch (byte) { + case 0xb0: + finish = true; + break; + case 0xb1: { + if (offset >= len) + return _URC_FAILURE; + uint8_t registers = getByte(data, offset++); + if (registers & 0xf0 || !registers) + return _URC_FAILURE; + _Unwind_VRS_Pop(context, _UVRSC_CORE, registers, _UVRSD_UINT32); + break; + } + case 0xb2: { + uint32_t addend = 0; + uint32_t shift = 0; + // This decodes a uleb128 value. + while (true) { + if (offset >= len) + return _URC_FAILURE; + uint32_t v = getByte(data, offset++); + addend |= (v & 0x7f) << shift; + if ((v & 0x80) == 0) + break; + shift += 7; + } + uint32_t sp; + _Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, + &sp); + sp += 0x204 + (addend << 2); + _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, + &sp); + break; + } + case 0xb3: { + uint8_t v = getByte(data, offset++); + _Unwind_VRS_Pop(context, _UVRSC_VFP, + RegisterRange(static_cast(v >> 4), + v & 0x0f), _UVRSD_VFPX); + break; + } + case 0xb4: + case 0xb5: + case 0xb6: + case 0xb7: + return _URC_FAILURE; + default: + _Unwind_VRS_Pop(context, _UVRSC_VFP, + RegisterRange(8, byte & 0x07), _UVRSD_VFPX); + break; + } + break; + } + case 0xc0: { + switch (byte) { +#if defined(__ARM_WMMX) + case 0xc0: + case 0xc1: + case 0xc2: + case 0xc3: + case 0xc4: + case 0xc5: + _Unwind_VRS_Pop(context, _UVRSC_WMMXD, + RegisterRange(10, byte & 0x7), _UVRSD_DOUBLE); + break; + case 0xc6: { + uint8_t v = getByte(data, offset++); + uint8_t start = static_cast(v >> 4); + uint8_t count_minus_one = v & 0xf; + if (start + count_minus_one >= 16) + return _URC_FAILURE; + _Unwind_VRS_Pop(context, _UVRSC_WMMXD, + RegisterRange(start, count_minus_one), + _UVRSD_DOUBLE); + break; + } + case 0xc7: { + uint8_t v = getByte(data, offset++); + if (!v || v & 0xf0) + return _URC_FAILURE; + _Unwind_VRS_Pop(context, _UVRSC_WMMXC, v, _UVRSD_DOUBLE); + break; + } +#endif + case 0xc8: + case 0xc9: { + uint8_t v = getByte(data, offset++); + uint8_t start = + static_cast(((byte == 0xc8) ? 16 : 0) + (v >> 4)); + uint8_t count_minus_one = v & 0xf; + if (start + count_minus_one >= 32) + return _URC_FAILURE; + _Unwind_VRS_Pop(context, _UVRSC_VFP, + RegisterRange(start, count_minus_one), + _UVRSD_DOUBLE); + break; + } + default: + return _URC_FAILURE; + } + break; + } + case 0xd0: { + if (byte & 0x08) + return _URC_FAILURE; + _Unwind_VRS_Pop(context, _UVRSC_VFP, RegisterRange(8, byte & 0x7), + _UVRSD_DOUBLE); + break; + } + default: + return _URC_FAILURE; + } + } + } + if (!wrotePC) { + uint32_t lr; + _Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_LR, _UVRSD_UINT32, &lr); + _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_IP, _UVRSD_UINT32, &lr); + } + return _URC_CONTINUE_UNWIND; +} + +extern "C" _LIBUNWIND_EXPORT _Unwind_Reason_Code +__aeabi_unwind_cpp_pr0(_Unwind_State state, _Unwind_Control_Block *ucbp, + _Unwind_Context *context) { + return unwindOneFrame(state, ucbp, context); +} + +extern "C" _LIBUNWIND_EXPORT _Unwind_Reason_Code +__aeabi_unwind_cpp_pr1(_Unwind_State state, _Unwind_Control_Block *ucbp, + _Unwind_Context *context) { + return unwindOneFrame(state, ucbp, context); +} + +extern "C" _LIBUNWIND_EXPORT _Unwind_Reason_Code +__aeabi_unwind_cpp_pr2(_Unwind_State state, _Unwind_Control_Block *ucbp, + _Unwind_Context *context) { + return unwindOneFrame(state, ucbp, context); +} + +static _Unwind_Reason_Code +unwind_phase1(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *exception_object) { + // EHABI #7.3 discusses preserving the VRS in a "temporary VRS" during + // phase 1 and then restoring it to the "primary VRS" for phase 2. The + // effect is phase 2 doesn't see any of the VRS manipulations from phase 1. + // In this implementation, the phases don't share the VRS backing store. + // Instead, they are passed the original |uc| and they create a new VRS + // from scratch thus achieving the same effect. + __unw_init_local(cursor, uc); + + // Walk each frame looking for a place to stop. + for (bool handlerNotFound = true; handlerNotFound;) { + + // See if frame has code to run (has personality routine). + unw_proc_info_t frameInfo; + if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) { + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): __unw_get_proc_info " + "failed => _URC_FATAL_PHASE1_ERROR", + static_cast(exception_object)); + return _URC_FATAL_PHASE1_ERROR; + } + +#ifndef NDEBUG + // When tracing, print state information. + if (_LIBUNWIND_TRACING_UNWINDING) { + char functionBuf[512]; + const char *functionName = functionBuf; + unw_word_t offset; + if ((__unw_get_proc_name(cursor, functionBuf, sizeof(functionBuf), + &offset) != UNW_ESUCCESS) || + (frameInfo.start_ip + offset > frameInfo.end_ip)) + functionName = ".anonymous."; + unw_word_t pc; + __unw_get_reg(cursor, UNW_REG_IP, &pc); + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): pc=0x%" PRIxPTR ", start_ip=0x%" PRIxPTR ", func=%s, " + "lsda=0x%" PRIxPTR ", personality=0x%" PRIxPTR, + static_cast(exception_object), pc, + frameInfo.start_ip, functionName, + frameInfo.lsda, frameInfo.handler); + } +#endif + + // If there is a personality routine, ask it if it will want to stop at + // this frame. + if (frameInfo.handler != 0) { + _Unwind_Personality_Fn p = + (_Unwind_Personality_Fn)(long)(frameInfo.handler); + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): calling personality function %p", + static_cast(exception_object), + reinterpret_cast(reinterpret_cast(p))); + struct _Unwind_Context *context = (struct _Unwind_Context *)(cursor); + exception_object->pr_cache.fnstart = frameInfo.start_ip; + exception_object->pr_cache.ehtp = + (_Unwind_EHT_Header *)frameInfo.unwind_info; + exception_object->pr_cache.additional = frameInfo.flags; + _Unwind_Reason_Code personalityResult = + (*p)(_US_VIRTUAL_UNWIND_FRAME, exception_object, context); + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): personality result %d start_ip %x ehtp %p " + "additional %x", + static_cast(exception_object), personalityResult, + exception_object->pr_cache.fnstart, + static_cast(exception_object->pr_cache.ehtp), + exception_object->pr_cache.additional); + switch (personalityResult) { + case _URC_HANDLER_FOUND: + // found a catch clause or locals that need destructing in this frame + // stop search and remember stack pointer at the frame + handlerNotFound = false; + // p should have initialized barrier_cache. EHABI #7.3.5 + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): _URC_HANDLER_FOUND", + static_cast(exception_object)); + return _URC_NO_REASON; + + case _URC_CONTINUE_UNWIND: + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): _URC_CONTINUE_UNWIND", + static_cast(exception_object)); + // continue unwinding + break; + + // EHABI #7.3.3 + case _URC_FAILURE: + return _URC_FAILURE; + + default: + // something went wrong + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): _URC_FATAL_PHASE1_ERROR", + static_cast(exception_object)); + return _URC_FATAL_PHASE1_ERROR; + } + } + } + return _URC_NO_REASON; +} + +static _Unwind_Reason_Code unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor, + _Unwind_Exception *exception_object, + bool resume) { + // See comment at the start of unwind_phase1 regarding VRS integrity. + __unw_init_local(cursor, uc); + + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p)", + static_cast(exception_object)); + int frame_count = 0; + + // Walk each frame until we reach where search phase said to stop. + while (true) { + // Ask libunwind to get next frame (skip over first which is + // _Unwind_RaiseException or _Unwind_Resume). + // + // Resume only ever makes sense for 1 frame. + _Unwind_State state = + resume ? _US_UNWIND_FRAME_RESUME : _US_UNWIND_FRAME_STARTING; + if (resume && frame_count == 1) { + // On a resume, first unwind the _Unwind_Resume() frame. The next frame + // is now the landing pad for the cleanup from a previous execution of + // phase2. To continue unwindingly correctly, replace VRS[15] with the + // IP of the frame that the previous run of phase2 installed the context + // for. After this, continue unwinding as if normal. + // + // See #7.4.6 for details. + __unw_set_reg(cursor, UNW_REG_IP, + exception_object->unwinder_cache.reserved2); + resume = false; + } + + // Get info about this frame. + unw_word_t sp; + unw_proc_info_t frameInfo; + __unw_get_reg(cursor, UNW_REG_SP, &sp); + if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) { + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2(ex_ojb=%p): __unw_get_proc_info " + "failed => _URC_FATAL_PHASE2_ERROR", + static_cast(exception_object)); + return _URC_FATAL_PHASE2_ERROR; + } + +#ifndef NDEBUG + // When tracing, print state information. + if (_LIBUNWIND_TRACING_UNWINDING) { + char functionBuf[512]; + const char *functionName = functionBuf; + unw_word_t offset; + if ((__unw_get_proc_name(cursor, functionBuf, sizeof(functionBuf), + &offset) != UNW_ESUCCESS) || + (frameInfo.start_ip + offset > frameInfo.end_ip)) + functionName = ".anonymous."; + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2(ex_ojb=%p): start_ip=0x%" PRIxPTR ", func=%s, sp=0x%" PRIxPTR ", " + "lsda=0x%" PRIxPTR ", personality=0x%" PRIxPTR "", + static_cast(exception_object), frameInfo.start_ip, + functionName, sp, frameInfo.lsda, + frameInfo.handler); + } +#endif + + // If there is a personality routine, tell it we are unwinding. + if (frameInfo.handler != 0) { + _Unwind_Personality_Fn p = + (_Unwind_Personality_Fn)(intptr_t)(frameInfo.handler); + struct _Unwind_Context *context = (struct _Unwind_Context *)(cursor); + // EHABI #7.2 + exception_object->pr_cache.fnstart = frameInfo.start_ip; + exception_object->pr_cache.ehtp = + (_Unwind_EHT_Header *)frameInfo.unwind_info; + exception_object->pr_cache.additional = frameInfo.flags; + _Unwind_Reason_Code personalityResult = + (*p)(state, exception_object, context); + switch (personalityResult) { + case _URC_CONTINUE_UNWIND: + // Continue unwinding + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2(ex_ojb=%p): _URC_CONTINUE_UNWIND", + static_cast(exception_object)); + // EHABI #7.2 + if (sp == exception_object->barrier_cache.sp) { + // Phase 1 said we would stop at this frame, but we did not... + _LIBUNWIND_ABORT("during phase1 personality function said it would " + "stop here, but now in phase2 it did not stop here"); + } + break; + case _URC_INSTALL_CONTEXT: + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2(ex_ojb=%p): _URC_INSTALL_CONTEXT", + static_cast(exception_object)); + // Personality routine says to transfer control to landing pad. + // We may get control back if landing pad calls _Unwind_Resume(). + if (_LIBUNWIND_TRACING_UNWINDING) { + unw_word_t pc; + __unw_get_reg(cursor, UNW_REG_IP, &pc); + __unw_get_reg(cursor, UNW_REG_SP, &sp); + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): re-entering " + "user code with ip=0x%" PRIxPTR ", sp=0x%" PRIxPTR, + static_cast(exception_object), + pc, sp); + } + + { + // EHABI #7.4.1 says we need to preserve pc for when _Unwind_Resume + // is called back, to find this same frame. + unw_word_t pc; + __unw_get_reg(cursor, UNW_REG_IP, &pc); + exception_object->unwinder_cache.reserved2 = (uint32_t)pc; + } + __unw_resume(cursor); + // __unw_resume() only returns if there was an error. + return _URC_FATAL_PHASE2_ERROR; + + // # EHABI #7.4.3 + case _URC_FAILURE: + abort(); + + default: + // Personality routine returned an unknown result code. + _LIBUNWIND_DEBUG_LOG("personality function returned unknown result %d", + personalityResult); + return _URC_FATAL_PHASE2_ERROR; + } + } + frame_count++; + } + + // Clean up phase did not resume at the frame that the search phase + // said it would... + return _URC_FATAL_PHASE2_ERROR; +} + +static _Unwind_Reason_Code +unwind_phase2_forced(unw_context_t *uc, unw_cursor_t *cursor, + _Unwind_Exception *exception_object, _Unwind_Stop_Fn stop, + void *stop_parameter) { + // See comment at the start of unwind_phase1 regarding VRS integrity. + __unw_init_local(cursor, uc); + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_force(ex_ojb=%p)", + static_cast(exception_object)); + // Walk each frame until we reach where search phase said to stop + while (true) { + // Update info about this frame. + unw_proc_info_t frameInfo; + if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) { + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): __unw_step " + "failed => _URC_END_OF_STACK", + (void *)exception_object); + return _URC_FATAL_PHASE2_ERROR; + } + +#ifndef NDEBUG + // When tracing, print state information. + if (_LIBUNWIND_TRACING_UNWINDING) { + char functionBuf[512]; + const char *functionName = functionBuf; + unw_word_t offset; + if ((__unw_get_proc_name(cursor, functionBuf, sizeof(functionBuf), + &offset) != UNW_ESUCCESS) || + (frameInfo.start_ip + offset > frameInfo.end_ip)) + functionName = ".anonymous."; + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2_forced(ex_ojb=%p): start_ip=0x%" PRIxPTR + ", func=%s, lsda=0x%" PRIxPTR ", personality=0x%" PRIxPTR, + (void *)exception_object, frameInfo.start_ip, functionName, + frameInfo.lsda, frameInfo.handler); + } +#endif + + // Call stop function at each frame. + _Unwind_Action action = + (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE); + _Unwind_Reason_Code stopResult = + (*stop)(1, action, exception_object->exception_class, exception_object, + (_Unwind_Context *)(cursor), stop_parameter); + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2_forced(ex_ojb=%p): stop function returned %d", + (void *)exception_object, stopResult); + if (stopResult != _URC_NO_REASON) { + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2_forced(ex_ojb=%p): stopped by stop function", + (void *)exception_object); + return _URC_FATAL_PHASE2_ERROR; + } + + // If there is a personality routine, tell it we are unwinding. + if (frameInfo.handler != 0) { + _Unwind_Personality_Fn p = + (_Unwind_Personality_Fn)(uintptr_t)(frameInfo.handler); + struct _Unwind_Context *context = (struct _Unwind_Context *)(cursor); + // EHABI #7.2 + exception_object->pr_cache.fnstart = frameInfo.start_ip; + exception_object->pr_cache.ehtp = + (_Unwind_EHT_Header *)frameInfo.unwind_info; + exception_object->pr_cache.additional = frameInfo.flags; + _Unwind_Reason_Code personalityResult = + (*p)(_US_FORCE_UNWIND | _US_UNWIND_FRAME_STARTING, exception_object, + context); + switch (personalityResult) { + case _URC_CONTINUE_UNWIND: + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "personality returned " + "_URC_CONTINUE_UNWIND", + (void *)exception_object); + // Destructors called, continue unwinding + break; + case _URC_INSTALL_CONTEXT: + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "personality returned " + "_URC_INSTALL_CONTEXT", + (void *)exception_object); + // We may get control back if landing pad calls _Unwind_Resume(). + __unw_resume(cursor); + break; + default: + // Personality routine returned an unknown result code. + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "personality returned %d, " + "_URC_FATAL_PHASE2_ERROR", + (void *)exception_object, personalityResult); + return _URC_FATAL_PHASE2_ERROR; + } + } + } + + // Call stop function one last time and tell it we've reached the end + // of the stack. + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop " + "function with _UA_END_OF_STACK", + (void *)exception_object); + _Unwind_Action lastAction = + (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | _UA_END_OF_STACK); + (*stop)(1, lastAction, exception_object->exception_class, exception_object, + (struct _Unwind_Context *)(cursor), stop_parameter); + + // Clean up phase did not resume at the frame that the search phase said it + // would. + return _URC_FATAL_PHASE2_ERROR; +} + +/// Called by __cxa_throw. Only returns if there is a fatal error. +_LIBUNWIND_EXPORT _Unwind_Reason_Code +_Unwind_RaiseException(_Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_RaiseException(ex_obj=%p)", + static_cast(exception_object)); + unw_context_t uc; + unw_cursor_t cursor; + __unw_getcontext(&uc); + + // This field for is for compatibility with GCC to say this isn't a forced + // unwind. EHABI #7.2 + exception_object->unwinder_cache.reserved1 = 0; + + // phase 1: the search phase + _Unwind_Reason_Code phase1 = unwind_phase1(&uc, &cursor, exception_object); + if (phase1 != _URC_NO_REASON) + return phase1; + + // phase 2: the clean up phase + return unwind_phase2(&uc, &cursor, exception_object, false); +} + +_LIBUNWIND_EXPORT void _Unwind_Complete(_Unwind_Exception* exception_object) { + // This is to be called when exception handling completes to give us a chance + // to perform any housekeeping. EHABI #7.2. But we have nothing to do here. + (void)exception_object; +} + +/// When _Unwind_RaiseException() is in phase2, it hands control +/// to the personality function at each frame. The personality +/// may force a jump to a landing pad in that function, the landing +/// pad code may then call _Unwind_Resume() to continue with the +/// unwinding. Note: the call to _Unwind_Resume() is from compiler +/// geneated user code. All other _Unwind_* routines are called +/// by the C++ runtime __cxa_* routines. +/// +/// Note: re-throwing an exception (as opposed to continuing the unwind) +/// is implemented by having the code call __cxa_rethrow() which +/// in turn calls _Unwind_Resume_or_Rethrow(). +_LIBUNWIND_EXPORT void +_Unwind_Resume(_Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_Resume(ex_obj=%p)", + static_cast(exception_object)); + unw_context_t uc; + unw_cursor_t cursor; + __unw_getcontext(&uc); + + if (exception_object->unwinder_cache.reserved1) + unwind_phase2_forced( + &uc, &cursor, exception_object, + (_Unwind_Stop_Fn)exception_object->unwinder_cache.reserved1, + (void *)exception_object->unwinder_cache.reserved3); + else + unwind_phase2(&uc, &cursor, exception_object, true); + + // Clients assume _Unwind_Resume() does not return, so all we can do is abort. + _LIBUNWIND_ABORT("_Unwind_Resume() can't return"); +} + +/// Called by personality handler during phase 2 to get LSDA for current frame. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) { + unw_cursor_t *cursor = (unw_cursor_t *)context; + unw_proc_info_t frameInfo; + uintptr_t result = 0; + if (__unw_get_proc_info(cursor, &frameInfo) == UNW_ESUCCESS) + result = (uintptr_t)frameInfo.lsda; + _LIBUNWIND_TRACE_API( + "_Unwind_GetLanguageSpecificData(context=%p) => 0x%llx", + static_cast(context), (long long)result); + return result; +} + +static uint64_t ValueAsBitPattern(_Unwind_VRS_DataRepresentation representation, + void* valuep) { + uint64_t value = 0; + switch (representation) { + case _UVRSD_UINT32: + case _UVRSD_FLOAT: + memcpy(&value, valuep, sizeof(uint32_t)); + break; + + case _UVRSD_VFPX: + case _UVRSD_UINT64: + case _UVRSD_DOUBLE: + memcpy(&value, valuep, sizeof(uint64_t)); + break; + } + return value; +} + +_LIBUNWIND_EXPORT _Unwind_VRS_Result +_Unwind_VRS_Set(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, + uint32_t regno, _Unwind_VRS_DataRepresentation representation, + void *valuep) { + _LIBUNWIND_TRACE_API("_Unwind_VRS_Set(context=%p, regclass=%d, reg=%d, " + "rep=%d, value=0x%llX)", + static_cast(context), regclass, regno, + representation, + ValueAsBitPattern(representation, valuep)); + unw_cursor_t *cursor = (unw_cursor_t *)context; + switch (regclass) { + case _UVRSC_CORE: + if (representation != _UVRSD_UINT32 || regno > 15) + return _UVRSR_FAILED; + return __unw_set_reg(cursor, (unw_regnum_t)(UNW_ARM_R0 + regno), + *(unw_word_t *)valuep) == UNW_ESUCCESS + ? _UVRSR_OK + : _UVRSR_FAILED; + case _UVRSC_VFP: + if (representation != _UVRSD_VFPX && representation != _UVRSD_DOUBLE) + return _UVRSR_FAILED; + if (representation == _UVRSD_VFPX) { + // Can only touch d0-15 with FSTMFDX. + if (regno > 15) + return _UVRSR_FAILED; + __unw_save_vfp_as_X(cursor); + } else { + if (regno > 31) + return _UVRSR_FAILED; + } + return __unw_set_fpreg(cursor, (unw_regnum_t)(UNW_ARM_D0 + regno), + *(unw_fpreg_t *)valuep) == UNW_ESUCCESS + ? _UVRSR_OK + : _UVRSR_FAILED; +#if defined(__ARM_WMMX) + case _UVRSC_WMMXC: + if (representation != _UVRSD_UINT32 || regno > 3) + return _UVRSR_FAILED; + return __unw_set_reg(cursor, (unw_regnum_t)(UNW_ARM_WC0 + regno), + *(unw_word_t *)valuep) == UNW_ESUCCESS + ? _UVRSR_OK + : _UVRSR_FAILED; + case _UVRSC_WMMXD: + if (representation != _UVRSD_DOUBLE || regno > 31) + return _UVRSR_FAILED; + return __unw_set_fpreg(cursor, (unw_regnum_t)(UNW_ARM_WR0 + regno), + *(unw_fpreg_t *)valuep) == UNW_ESUCCESS + ? _UVRSR_OK + : _UVRSR_FAILED; +#else + case _UVRSC_WMMXC: + case _UVRSC_WMMXD: + break; +#endif + } + _LIBUNWIND_ABORT("unsupported register class"); +} + +static _Unwind_VRS_Result +_Unwind_VRS_Get_Internal(_Unwind_Context *context, + _Unwind_VRS_RegClass regclass, uint32_t regno, + _Unwind_VRS_DataRepresentation representation, + void *valuep) { + unw_cursor_t *cursor = (unw_cursor_t *)context; + switch (regclass) { + case _UVRSC_CORE: + if (representation != _UVRSD_UINT32 || regno > 15) + return _UVRSR_FAILED; + return __unw_get_reg(cursor, (unw_regnum_t)(UNW_ARM_R0 + regno), + (unw_word_t *)valuep) == UNW_ESUCCESS + ? _UVRSR_OK + : _UVRSR_FAILED; + case _UVRSC_VFP: + if (representation != _UVRSD_VFPX && representation != _UVRSD_DOUBLE) + return _UVRSR_FAILED; + if (representation == _UVRSD_VFPX) { + // Can only touch d0-15 with FSTMFDX. + if (regno > 15) + return _UVRSR_FAILED; + __unw_save_vfp_as_X(cursor); + } else { + if (regno > 31) + return _UVRSR_FAILED; + } + return __unw_get_fpreg(cursor, (unw_regnum_t)(UNW_ARM_D0 + regno), + (unw_fpreg_t *)valuep) == UNW_ESUCCESS + ? _UVRSR_OK + : _UVRSR_FAILED; +#if defined(__ARM_WMMX) + case _UVRSC_WMMXC: + if (representation != _UVRSD_UINT32 || regno > 3) + return _UVRSR_FAILED; + return __unw_get_reg(cursor, (unw_regnum_t)(UNW_ARM_WC0 + regno), + (unw_word_t *)valuep) == UNW_ESUCCESS + ? _UVRSR_OK + : _UVRSR_FAILED; + case _UVRSC_WMMXD: + if (representation != _UVRSD_DOUBLE || regno > 31) + return _UVRSR_FAILED; + return __unw_get_fpreg(cursor, (unw_regnum_t)(UNW_ARM_WR0 + regno), + (unw_fpreg_t *)valuep) == UNW_ESUCCESS + ? _UVRSR_OK + : _UVRSR_FAILED; +#else + case _UVRSC_WMMXC: + case _UVRSC_WMMXD: + break; +#endif + } + _LIBUNWIND_ABORT("unsupported register class"); +} + +_LIBUNWIND_EXPORT _Unwind_VRS_Result +_Unwind_VRS_Get(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, + uint32_t regno, _Unwind_VRS_DataRepresentation representation, + void *valuep) { + _Unwind_VRS_Result result = + _Unwind_VRS_Get_Internal(context, regclass, regno, representation, + valuep); + _LIBUNWIND_TRACE_API("_Unwind_VRS_Get(context=%p, regclass=%d, reg=%d, " + "rep=%d, value=0x%llX, result = %d)", + static_cast(context), regclass, regno, + representation, + ValueAsBitPattern(representation, valuep), result); + return result; +} + +_Unwind_VRS_Result +_Unwind_VRS_Pop(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, + uint32_t discriminator, + _Unwind_VRS_DataRepresentation representation) { + _LIBUNWIND_TRACE_API("_Unwind_VRS_Pop(context=%p, regclass=%d, " + "discriminator=%d, representation=%d)", + static_cast(context), regclass, discriminator, + representation); + switch (regclass) { + case _UVRSC_WMMXC: +#if !defined(__ARM_WMMX) + break; +#endif + case _UVRSC_CORE: { + if (representation != _UVRSD_UINT32) + return _UVRSR_FAILED; + // When popping SP from the stack, we don't want to override it from the + // computed new stack location. See EHABI #7.5.4 table 3. + bool poppedSP = false; + uint32_t* sp; + if (_Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_SP, + _UVRSD_UINT32, &sp) != _UVRSR_OK) { + return _UVRSR_FAILED; + } + for (uint32_t i = 0; i < 16; ++i) { + if (!(discriminator & static_cast(1 << i))) + continue; + uint32_t value = *sp++; + if (regclass == _UVRSC_CORE && i == 13) + poppedSP = true; + if (_Unwind_VRS_Set(context, regclass, i, + _UVRSD_UINT32, &value) != _UVRSR_OK) { + return _UVRSR_FAILED; + } + } + if (!poppedSP) { + return _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, + _UVRSD_UINT32, &sp); + } + return _UVRSR_OK; + } + case _UVRSC_WMMXD: +#if !defined(__ARM_WMMX) + break; +#endif + case _UVRSC_VFP: { + if (representation != _UVRSD_VFPX && representation != _UVRSD_DOUBLE) + return _UVRSR_FAILED; + uint32_t first = discriminator >> 16; + uint32_t count = discriminator & 0xffff; + uint32_t end = first+count; + uint32_t* sp; + if (_Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_SP, + _UVRSD_UINT32, &sp) != _UVRSR_OK) { + return _UVRSR_FAILED; + } + // For _UVRSD_VFPX, we're assuming the data is stored in FSTMX "standard + // format 1", which is equivalent to FSTMD + a padding word. + for (uint32_t i = first; i < end; ++i) { + // SP is only 32-bit aligned so don't copy 64-bit at a time. + uint64_t w0 = *sp++; + uint64_t w1 = *sp++; +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + uint64_t value = (w1 << 32) | w0; +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + uint64_t value = (w0 << 32) | w1; +#else +#error "Unable to determine endianess" +#endif + if (_Unwind_VRS_Set(context, regclass, i, representation, &value) != + _UVRSR_OK) + return _UVRSR_FAILED; + } + if (representation == _UVRSD_VFPX) + ++sp; + return _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, + &sp); + } + } + _LIBUNWIND_ABORT("unsupported register class"); +} + +/// Not used by C++. +/// Unwinds stack, calling "stop" function at each frame. +/// Could be used to implement longjmp(). +_LIBUNWIND_EXPORT _Unwind_Reason_Code +_Unwind_ForcedUnwind(_Unwind_Exception *exception_object, _Unwind_Stop_Fn stop, + void *stop_parameter) { + _LIBUNWIND_TRACE_API("_Unwind_ForcedUnwind(ex_obj=%p, stop=%p)", + (void *)exception_object, (void *)(uintptr_t)stop); + unw_context_t uc; + unw_cursor_t cursor; + __unw_getcontext(&uc); + + // Mark that this is a forced unwind, so _Unwind_Resume() can do + // the right thing. + exception_object->unwinder_cache.reserved1 = (uintptr_t)stop; + exception_object->unwinder_cache.reserved3 = (uintptr_t)stop_parameter; + + return unwind_phase2_forced(&uc, &cursor, exception_object, stop, + stop_parameter); +} + +/// Called by personality handler during phase 2 to find the start of the +/// function. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetRegionStart(struct _Unwind_Context *context) { + unw_cursor_t *cursor = (unw_cursor_t *)context; + unw_proc_info_t frameInfo; + uintptr_t result = 0; + if (__unw_get_proc_info(cursor, &frameInfo) == UNW_ESUCCESS) + result = (uintptr_t)frameInfo.start_ip; + _LIBUNWIND_TRACE_API("_Unwind_GetRegionStart(context=%p) => 0x%llX", + static_cast(context), (long long)result); + return result; +} + + +/// Called by personality handler during phase 2 if a foreign exception +// is caught. +_LIBUNWIND_EXPORT void +_Unwind_DeleteException(_Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_DeleteException(ex_obj=%p)", + static_cast(exception_object)); + if (exception_object->exception_cleanup != NULL) + (*exception_object->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT, + exception_object); +} + +extern "C" _LIBUNWIND_EXPORT _Unwind_Reason_Code +__gnu_unwind_frame(_Unwind_Exception *exception_object, + struct _Unwind_Context *context) { + unw_cursor_t *cursor = (unw_cursor_t *)context; + if (__unw_step(cursor) != UNW_STEP_SUCCESS) + return _URC_FAILURE; + return _URC_OK; +} + +#endif // defined(_LIBUNWIND_ARM_EHABI) diff --git a/shared/sentry/external/crashpad/libunwind/src/Unwind-EHABI.h b/shared/sentry/external/crashpad/libunwind/src/Unwind-EHABI.h new file mode 100644 index 000000000..6897082a3 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/src/Unwind-EHABI.h @@ -0,0 +1,50 @@ +//===------------------------- Unwind-EHABI.hpp ---------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +//===----------------------------------------------------------------------===// + +#ifndef __UNWIND_EHABI_H__ +#define __UNWIND_EHABI_H__ + +#include <__libunwind_config.h> + +#if defined(_LIBUNWIND_ARM_EHABI) + +#include +#include + +// Unable to unwind in the ARM index table (section 5 EHABI). +#define UNW_EXIDX_CANTUNWIND 0x1 + +static inline uint32_t signExtendPrel31(uint32_t data) { + return data | ((data & 0x40000000u) << 1); +} + +static inline uint32_t readPrel31(const uint32_t *data) { + return (((uint32_t)(uintptr_t)data) + signExtendPrel31(*data)); +} + +#if defined(__cplusplus) +extern "C" { +#endif + +extern _Unwind_Reason_Code __aeabi_unwind_cpp_pr0( + _Unwind_State state, _Unwind_Control_Block *ucbp, _Unwind_Context *context); + +extern _Unwind_Reason_Code __aeabi_unwind_cpp_pr1( + _Unwind_State state, _Unwind_Control_Block *ucbp, _Unwind_Context *context); + +extern _Unwind_Reason_Code __aeabi_unwind_cpp_pr2( + _Unwind_State state, _Unwind_Control_Block *ucbp, _Unwind_Context *context); + +#if defined(__cplusplus) +} // extern "C" +#endif + +#endif // defined(_LIBUNWIND_ARM_EHABI) + +#endif // __UNWIND_EHABI_H__ diff --git a/shared/sentry/external/crashpad/libunwind/src/Unwind-seh.cpp b/shared/sentry/external/crashpad/libunwind/src/Unwind-seh.cpp new file mode 100644 index 000000000..ad0b26765 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/src/Unwind-seh.cpp @@ -0,0 +1,491 @@ +//===--------------------------- Unwind-seh.cpp ---------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implements SEH-based Itanium C++ exceptions. +// +//===----------------------------------------------------------------------===// + +#include "config.h" + +#if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) + +#include + +#include +#include +#include + +#include +#include +#include +#include + +#include "libunwind_ext.h" +#include "UnwindCursor.hpp" + +using namespace libunwind; + +#define STATUS_USER_DEFINED (1u << 29) + +#define STATUS_GCC_MAGIC (('G' << 16) | ('C' << 8) | 'C') + +#define MAKE_CUSTOM_STATUS(s, c) \ + ((NTSTATUS)(((s) << 30) | STATUS_USER_DEFINED | (c))) +#define MAKE_GCC_EXCEPTION(c) \ + MAKE_CUSTOM_STATUS(STATUS_SEVERITY_SUCCESS, STATUS_GCC_MAGIC | ((c) << 24)) + +/// SEH exception raised by libunwind when the program calls +/// \c _Unwind_RaiseException. +#define STATUS_GCC_THROW MAKE_GCC_EXCEPTION(0) // 0x20474343 +/// SEH exception raised by libunwind to initiate phase 2 of exception +/// handling. +#define STATUS_GCC_UNWIND MAKE_GCC_EXCEPTION(1) // 0x21474343 + +static int __unw_init_seh(unw_cursor_t *cursor, CONTEXT *ctx); +static DISPATCHER_CONTEXT *__unw_seh_get_disp_ctx(unw_cursor_t *cursor); +static void __unw_seh_set_disp_ctx(unw_cursor_t *cursor, + DISPATCHER_CONTEXT *disp); + +/// Common implementation of SEH-style handler functions used by Itanium- +/// style frames. Depending on how and why it was called, it may do one of: +/// a) Delegate to the given Itanium-style personality function; or +/// b) Initiate a collided unwind to halt unwinding. +_LIBUNWIND_EXPORT EXCEPTION_DISPOSITION +_GCC_specific_handler(PEXCEPTION_RECORD ms_exc, PVOID frame, PCONTEXT ms_ctx, + DISPATCHER_CONTEXT *disp, _Unwind_Personality_Fn pers) { + unw_cursor_t cursor; + _Unwind_Exception *exc; + _Unwind_Action action; + struct _Unwind_Context *ctx = nullptr; + _Unwind_Reason_Code urc; + uintptr_t retval, target; + bool ours = false; + + _LIBUNWIND_TRACE_UNWINDING("_GCC_specific_handler(%#010lx(%lx), %p)", + ms_exc->ExceptionCode, ms_exc->ExceptionFlags, + (void *)frame); + if (ms_exc->ExceptionCode == STATUS_GCC_UNWIND) { + if (IS_TARGET_UNWIND(ms_exc->ExceptionFlags)) { + // Set up the upper return value (the lower one and the target PC + // were set in the call to RtlUnwindEx()) for the landing pad. +#ifdef __x86_64__ + disp->ContextRecord->Rdx = ms_exc->ExceptionInformation[3]; +#elif defined(__arm__) + disp->ContextRecord->R1 = ms_exc->ExceptionInformation[3]; +#elif defined(__aarch64__) + disp->ContextRecord->X1 = ms_exc->ExceptionInformation[3]; +#endif + } + // This is the collided unwind to the landing pad. Nothing to do. + return ExceptionContinueSearch; + } + + if (ms_exc->ExceptionCode == STATUS_GCC_THROW) { + // This is (probably) a libunwind-controlled exception/unwind. Recover the + // parameters which we set below, and pass them to the personality function. + ours = true; + exc = (_Unwind_Exception *)ms_exc->ExceptionInformation[0]; + if (!IS_UNWINDING(ms_exc->ExceptionFlags) && ms_exc->NumberParameters > 1) { + ctx = (struct _Unwind_Context *)ms_exc->ExceptionInformation[1]; + action = (_Unwind_Action)ms_exc->ExceptionInformation[2]; + } + } else { + // Foreign exception. + // We can't interact with them (we don't know the original target frame + // that we should pass on to RtlUnwindEx in _Unwind_Resume), so just + // pass without calling our destructors here. + return ExceptionContinueSearch; + } + if (!ctx) { + __unw_init_seh(&cursor, disp->ContextRecord); + __unw_seh_set_disp_ctx(&cursor, disp); + __unw_set_reg(&cursor, UNW_REG_IP, disp->ControlPc - 1); + ctx = (struct _Unwind_Context *)&cursor; + + if (!IS_UNWINDING(ms_exc->ExceptionFlags)) { + if (ours && ms_exc->NumberParameters > 1) + action = (_Unwind_Action)(_UA_CLEANUP_PHASE | _UA_FORCE_UNWIND); + else + action = _UA_SEARCH_PHASE; + } else { + if (ours && ms_exc->ExceptionInformation[1] == (ULONG_PTR)frame) + action = (_Unwind_Action)(_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME); + else + action = _UA_CLEANUP_PHASE; + } + } + + _LIBUNWIND_TRACE_UNWINDING("_GCC_specific_handler() calling personality " + "function %p(1, %d, %llx, %p, %p)", + (void *)pers, action, exc->exception_class, + (void *)exc, (void *)ctx); + urc = pers(1, action, exc->exception_class, exc, ctx); + _LIBUNWIND_TRACE_UNWINDING("_GCC_specific_handler() personality returned %d", urc); + switch (urc) { + case _URC_CONTINUE_UNWIND: + // If we're in phase 2, and the personality routine said to continue + // at the target frame, we're in real trouble. + if (action & _UA_HANDLER_FRAME) + _LIBUNWIND_ABORT("Personality continued unwind at the target frame!"); + return ExceptionContinueSearch; + case _URC_HANDLER_FOUND: + // If we were called by __libunwind_seh_personality(), indicate that + // a handler was found; otherwise, initiate phase 2 by unwinding. + if (ours && ms_exc->NumberParameters > 1) + return 4 /* ExecptionExecuteHandler in mingw */; + // This should never happen in phase 2. + if (IS_UNWINDING(ms_exc->ExceptionFlags)) + _LIBUNWIND_ABORT("Personality indicated exception handler in phase 2!"); + exc->private_[1] = (ULONG_PTR)frame; + if (ours) { + ms_exc->NumberParameters = 4; + ms_exc->ExceptionInformation[1] = (ULONG_PTR)frame; + } + // FIXME: Indicate target frame in foreign case! + // phase 2: the clean up phase + RtlUnwindEx(frame, (PVOID)disp->ControlPc, ms_exc, exc, ms_ctx, disp->HistoryTable); + _LIBUNWIND_ABORT("RtlUnwindEx() failed"); + case _URC_INSTALL_CONTEXT: { + // If we were called by __libunwind_seh_personality(), indicate that + // a handler was found; otherwise, it's time to initiate a collided + // unwind to the target. + if (ours && !IS_UNWINDING(ms_exc->ExceptionFlags) && ms_exc->NumberParameters > 1) + return 4 /* ExecptionExecuteHandler in mingw */; + // This should never happen in phase 1. + if (!IS_UNWINDING(ms_exc->ExceptionFlags)) + _LIBUNWIND_ABORT("Personality installed context during phase 1!"); +#ifdef __x86_64__ + exc->private_[2] = disp->TargetIp; + __unw_get_reg(&cursor, UNW_X86_64_RAX, &retval); + __unw_get_reg(&cursor, UNW_X86_64_RDX, &exc->private_[3]); +#elif defined(__arm__) + exc->private_[2] = disp->TargetPc; + __unw_get_reg(&cursor, UNW_ARM_R0, &retval); + __unw_get_reg(&cursor, UNW_ARM_R1, &exc->private_[3]); +#elif defined(__aarch64__) + exc->private_[2] = disp->TargetPc; + __unw_get_reg(&cursor, UNW_AARCH64_X0, &retval); + __unw_get_reg(&cursor, UNW_AARCH64_X1, &exc->private_[3]); +#endif + __unw_get_reg(&cursor, UNW_REG_IP, &target); + ms_exc->ExceptionCode = STATUS_GCC_UNWIND; +#ifdef __x86_64__ + ms_exc->ExceptionInformation[2] = disp->TargetIp; +#elif defined(__arm__) || defined(__aarch64__) + ms_exc->ExceptionInformation[2] = disp->TargetPc; +#endif + ms_exc->ExceptionInformation[3] = exc->private_[3]; + // Give NTRTL some scratch space to keep track of the collided unwind. + // Don't use the one that was passed in; we don't want to overwrite the + // context in the DISPATCHER_CONTEXT. + CONTEXT new_ctx; + RtlUnwindEx(frame, (PVOID)target, ms_exc, (PVOID)retval, &new_ctx, disp->HistoryTable); + _LIBUNWIND_ABORT("RtlUnwindEx() failed"); + } + // Anything else indicates a serious problem. + default: return ExceptionContinueExecution; + } +} + +/// Personality function returned by \c __unw_get_proc_info() in SEH contexts. +/// This is a wrapper that calls the real SEH handler function, which in +/// turn (at least, for Itanium-style frames) calls the real Itanium +/// personality function (see \c _GCC_specific_handler()). +extern "C" _Unwind_Reason_Code +__libunwind_seh_personality(int version, _Unwind_Action state, + uint64_t klass, _Unwind_Exception *exc, + struct _Unwind_Context *context) { + (void)version; + (void)klass; + EXCEPTION_RECORD ms_exc; + bool phase2 = (state & (_UA_SEARCH_PHASE|_UA_CLEANUP_PHASE)) == _UA_CLEANUP_PHASE; + ms_exc.ExceptionCode = STATUS_GCC_THROW; + ms_exc.ExceptionFlags = 0; + ms_exc.NumberParameters = 3; + ms_exc.ExceptionInformation[0] = (ULONG_PTR)exc; + ms_exc.ExceptionInformation[1] = (ULONG_PTR)context; + ms_exc.ExceptionInformation[2] = state; + DISPATCHER_CONTEXT *disp_ctx = + __unw_seh_get_disp_ctx((unw_cursor_t *)context); + EXCEPTION_DISPOSITION ms_act = disp_ctx->LanguageHandler(&ms_exc, + (PVOID)disp_ctx->EstablisherFrame, + disp_ctx->ContextRecord, + disp_ctx); + switch (ms_act) { + case ExceptionContinueSearch: return _URC_CONTINUE_UNWIND; + case 4 /*ExceptionExecuteHandler*/: + return phase2 ? _URC_INSTALL_CONTEXT : _URC_HANDLER_FOUND; + default: + return phase2 ? _URC_FATAL_PHASE2_ERROR : _URC_FATAL_PHASE1_ERROR; + } +} + +static _Unwind_Reason_Code +unwind_phase2_forced(unw_context_t *uc, + _Unwind_Exception *exception_object, + _Unwind_Stop_Fn stop, void *stop_parameter) { + unw_cursor_t cursor2; + __unw_init_local(&cursor2, uc); + + // Walk each frame until we reach where search phase said to stop + while (__unw_step(&cursor2) > 0) { + + // Update info about this frame. + unw_proc_info_t frameInfo; + if (__unw_get_proc_info(&cursor2, &frameInfo) != UNW_ESUCCESS) { + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): __unw_step " + "failed => _URC_END_OF_STACK", + (void *)exception_object); + return _URC_FATAL_PHASE2_ERROR; + } + +#ifndef NDEBUG + // When tracing, print state information. + if (_LIBUNWIND_TRACING_UNWINDING) { + char functionBuf[512]; + const char *functionName = functionBuf; + unw_word_t offset; + if ((__unw_get_proc_name(&cursor2, functionBuf, sizeof(functionBuf), + &offset) != UNW_ESUCCESS) || + (frameInfo.start_ip + offset > frameInfo.end_ip)) + functionName = ".anonymous."; + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2_forced(ex_ojb=%p): start_ip=0x%" PRIx64 + ", func=%s, lsda=0x%" PRIx64 ", personality=0x%" PRIx64, + (void *)exception_object, frameInfo.start_ip, functionName, + frameInfo.lsda, frameInfo.handler); + } +#endif + + // Call stop function at each frame. + _Unwind_Action action = + (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE); + _Unwind_Reason_Code stopResult = + (*stop)(1, action, exception_object->exception_class, exception_object, + (struct _Unwind_Context *)(&cursor2), stop_parameter); + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2_forced(ex_ojb=%p): stop function returned %d", + (void *)exception_object, stopResult); + if (stopResult != _URC_NO_REASON) { + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2_forced(ex_ojb=%p): stopped by stop function", + (void *)exception_object); + return _URC_FATAL_PHASE2_ERROR; + } + + // If there is a personality routine, tell it we are unwinding. + if (frameInfo.handler != 0) { + _Unwind_Personality_Fn p = + (_Unwind_Personality_Fn)(intptr_t)(frameInfo.handler); + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2_forced(ex_ojb=%p): calling personality function %p", + (void *)exception_object, (void *)(uintptr_t)p); + _Unwind_Reason_Code personalityResult = + (*p)(1, action, exception_object->exception_class, exception_object, + (struct _Unwind_Context *)(&cursor2)); + switch (personalityResult) { + case _URC_CONTINUE_UNWIND: + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "personality returned " + "_URC_CONTINUE_UNWIND", + (void *)exception_object); + // Destructors called, continue unwinding + break; + case _URC_INSTALL_CONTEXT: + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "personality returned " + "_URC_INSTALL_CONTEXT", + (void *)exception_object); + // We may get control back if landing pad calls _Unwind_Resume(). + __unw_resume(&cursor2); + break; + default: + // Personality routine returned an unknown result code. + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "personality returned %d, " + "_URC_FATAL_PHASE2_ERROR", + (void *)exception_object, personalityResult); + return _URC_FATAL_PHASE2_ERROR; + } + } + } + + // Call stop function one last time and tell it we've reached the end + // of the stack. + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop " + "function with _UA_END_OF_STACK", + (void *)exception_object); + _Unwind_Action lastAction = + (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | _UA_END_OF_STACK); + (*stop)(1, lastAction, exception_object->exception_class, exception_object, + (struct _Unwind_Context *)(&cursor2), stop_parameter); + + // Clean up phase did not resume at the frame that the search phase said it + // would. + return _URC_FATAL_PHASE2_ERROR; +} + +/// Called by \c __cxa_throw(). Only returns if there is a fatal error. +_LIBUNWIND_EXPORT _Unwind_Reason_Code +_Unwind_RaiseException(_Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_RaiseException(ex_obj=%p)", + (void *)exception_object); + + // Mark that this is a non-forced unwind, so _Unwind_Resume() + // can do the right thing. + memset(exception_object->private_, 0, sizeof(exception_object->private_)); + + // phase 1: the search phase + // We'll let the system do that for us. + RaiseException(STATUS_GCC_THROW, 0, 1, (ULONG_PTR *)&exception_object); + + // If we get here, either something went horribly wrong or we reached the + // top of the stack. Either way, let libc++abi call std::terminate(). + return _URC_END_OF_STACK; +} + +/// When \c _Unwind_RaiseException() is in phase2, it hands control +/// to the personality function at each frame. The personality +/// may force a jump to a landing pad in that function; the landing +/// pad code may then call \c _Unwind_Resume() to continue with the +/// unwinding. Note: the call to \c _Unwind_Resume() is from compiler +/// geneated user code. All other \c _Unwind_* routines are called +/// by the C++ runtime \c __cxa_* routines. +/// +/// Note: re-throwing an exception (as opposed to continuing the unwind) +/// is implemented by having the code call \c __cxa_rethrow() which +/// in turn calls \c _Unwind_Resume_or_Rethrow(). +_LIBUNWIND_EXPORT void +_Unwind_Resume(_Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_Resume(ex_obj=%p)", (void *)exception_object); + + if (exception_object->private_[0] != 0) { + unw_context_t uc; + + __unw_getcontext(&uc); + unwind_phase2_forced(&uc, exception_object, + (_Unwind_Stop_Fn) exception_object->private_[0], + (void *)exception_object->private_[4]); + } else { + // Recover the parameters for the unwind from the exception object + // so we can start unwinding again. + EXCEPTION_RECORD ms_exc; + CONTEXT ms_ctx; + UNWIND_HISTORY_TABLE hist; + + memset(&ms_exc, 0, sizeof(ms_exc)); + memset(&hist, 0, sizeof(hist)); + ms_exc.ExceptionCode = STATUS_GCC_THROW; + ms_exc.ExceptionFlags = EXCEPTION_NONCONTINUABLE; + ms_exc.NumberParameters = 4; + ms_exc.ExceptionInformation[0] = (ULONG_PTR)exception_object; + ms_exc.ExceptionInformation[1] = exception_object->private_[1]; + ms_exc.ExceptionInformation[2] = exception_object->private_[2]; + ms_exc.ExceptionInformation[3] = exception_object->private_[3]; + RtlUnwindEx((PVOID)exception_object->private_[1], + (PVOID)exception_object->private_[2], &ms_exc, + exception_object, &ms_ctx, &hist); + } + + // Clients assume _Unwind_Resume() does not return, so all we can do is abort. + _LIBUNWIND_ABORT("_Unwind_Resume() can't return"); +} + +/// Not used by C++. +/// Unwinds stack, calling "stop" function at each frame. +/// Could be used to implement \c longjmp(). +_LIBUNWIND_EXPORT _Unwind_Reason_Code +_Unwind_ForcedUnwind(_Unwind_Exception *exception_object, + _Unwind_Stop_Fn stop, void *stop_parameter) { + _LIBUNWIND_TRACE_API("_Unwind_ForcedUnwind(ex_obj=%p, stop=%p)", + (void *)exception_object, (void *)(uintptr_t)stop); + unw_context_t uc; + __unw_getcontext(&uc); + + // Mark that this is a forced unwind, so _Unwind_Resume() can do + // the right thing. + exception_object->private_[0] = (uintptr_t) stop; + exception_object->private_[4] = (uintptr_t) stop_parameter; + + // do it + return unwind_phase2_forced(&uc, exception_object, stop, stop_parameter); +} + +/// Called by personality handler during phase 2 to get LSDA for current frame. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) { + uintptr_t result = + (uintptr_t)__unw_seh_get_disp_ctx((unw_cursor_t *)context)->HandlerData; + _LIBUNWIND_TRACE_API( + "_Unwind_GetLanguageSpecificData(context=%p) => 0x%" PRIxPTR, + (void *)context, result); + return result; +} + +/// Called by personality handler during phase 2 to find the start of the +/// function. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetRegionStart(struct _Unwind_Context *context) { + DISPATCHER_CONTEXT *disp = __unw_seh_get_disp_ctx((unw_cursor_t *)context); + uintptr_t result = (uintptr_t)disp->FunctionEntry->BeginAddress + disp->ImageBase; + _LIBUNWIND_TRACE_API("_Unwind_GetRegionStart(context=%p) => 0x%" PRIxPTR, + (void *)context, result); + return result; +} + +static int __unw_init_seh(unw_cursor_t *cursor, CONTEXT *context) { +#ifdef _LIBUNWIND_TARGET_X86_64 + new (reinterpret_cast *>(cursor)) + UnwindCursor( + context, LocalAddressSpace::sThisAddressSpace); + auto *co = reinterpret_cast(cursor); + co->setInfoBasedOnIPRegister(); + return UNW_ESUCCESS; +#elif defined(_LIBUNWIND_TARGET_ARM) + new (reinterpret_cast *>(cursor)) + UnwindCursor( + context, LocalAddressSpace::sThisAddressSpace); + auto *co = reinterpret_cast(cursor); + co->setInfoBasedOnIPRegister(); + return UNW_ESUCCESS; +#elif defined(_LIBUNWIND_TARGET_AARCH64) + new (reinterpret_cast *>(cursor)) + UnwindCursor( + context, LocalAddressSpace::sThisAddressSpace); + auto *co = reinterpret_cast(cursor); + co->setInfoBasedOnIPRegister(); + return UNW_ESUCCESS; +#else + return UNW_EINVAL; +#endif +} + +static DISPATCHER_CONTEXT *__unw_seh_get_disp_ctx(unw_cursor_t *cursor) { +#ifdef _LIBUNWIND_TARGET_X86_64 + return reinterpret_cast *>(cursor)->getDispatcherContext(); +#elif defined(_LIBUNWIND_TARGET_ARM) + return reinterpret_cast *>(cursor)->getDispatcherContext(); +#elif defined(_LIBUNWIND_TARGET_AARCH64) + return reinterpret_cast *>(cursor)->getDispatcherContext(); +#else + return nullptr; +#endif +} + +static void __unw_seh_set_disp_ctx(unw_cursor_t *cursor, + DISPATCHER_CONTEXT *disp) { +#ifdef _LIBUNWIND_TARGET_X86_64 + reinterpret_cast *>(cursor)->setDispatcherContext(disp); +#elif defined(_LIBUNWIND_TARGET_ARM) + reinterpret_cast *>(cursor)->setDispatcherContext(disp); +#elif defined(_LIBUNWIND_TARGET_AARCH64) + reinterpret_cast *>(cursor)->setDispatcherContext(disp); +#endif +} + +#endif // defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) diff --git a/shared/sentry/external/crashpad/libunwind/src/Unwind-sjlj.c b/shared/sentry/external/crashpad/libunwind/src/Unwind-sjlj.c new file mode 100644 index 000000000..fd2a95b74 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/src/Unwind-sjlj.c @@ -0,0 +1,528 @@ +//===--------------------------- Unwind-sjlj.c ----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Implements setjump-longjump based C++ exceptions +// +//===----------------------------------------------------------------------===// + +#include + +#include +#include +#include +#include + +#include "config.h" + +/// With SJLJ based exceptions, any function that has a catch clause or needs to +/// do any clean up when an exception propagates through it, needs to call +/// \c _Unwind_SjLj_Register at the start of the function and +/// \c _Unwind_SjLj_Unregister at the end. The register function is called with +/// the address of a block of memory in the function's stack frame. The runtime +/// keeps a linked list (stack) of these blocks - one per thread. The calling +/// function also sets the personality and lsda fields of the block. + +#if defined(_LIBUNWIND_BUILD_SJLJ_APIS) + +struct _Unwind_FunctionContext { + // next function in stack of handlers + struct _Unwind_FunctionContext *prev; + +#if defined(__ve__) + // VE requires to store 64 bit pointers in the buffer for SjLj execption. + // We expand the size of values defined here. This size must be matched + // to the size returned by TargetMachine::getSjLjDataSize(). + + // set by calling function before registering to be the landing pad + uint64_t resumeLocation; + + // set by personality handler to be parameters passed to landing pad function + uint64_t resumeParameters[4]; +#else + // set by calling function before registering to be the landing pad + uint32_t resumeLocation; + + // set by personality handler to be parameters passed to landing pad function + uint32_t resumeParameters[4]; +#endif + + // set by calling function before registering + _Unwind_Personality_Fn personality; // arm offset=24 + uintptr_t lsda; // arm offset=28 + + // variable length array, contains registers to restore + // 0 = r7, 1 = pc, 2 = sp + void *jbuf[]; +}; + +#if defined(_LIBUNWIND_HAS_NO_THREADS) +# define _LIBUNWIND_THREAD_LOCAL +#else +# if __STDC_VERSION__ >= 201112L +# define _LIBUNWIND_THREAD_LOCAL _Thread_local +# elif defined(_MSC_VER) +# define _LIBUNWIND_THREAD_LOCAL __declspec(thread) +# elif defined(__GNUC__) || defined(__clang__) +# define _LIBUNWIND_THREAD_LOCAL __thread +# else +# error Unable to create thread local storage +# endif +#endif + + +#if !defined(FOR_DYLD) + +#if defined(__APPLE__) +#include +#else +static _LIBUNWIND_THREAD_LOCAL struct _Unwind_FunctionContext *stack = NULL; +#endif + +static struct _Unwind_FunctionContext *__Unwind_SjLj_GetTopOfFunctionStack() { +#if defined(__APPLE__) + return _pthread_getspecific_direct(__PTK_LIBC_DYLD_Unwind_SjLj_Key); +#else + return stack; +#endif +} + +static void +__Unwind_SjLj_SetTopOfFunctionStack(struct _Unwind_FunctionContext *fc) { +#if defined(__APPLE__) + _pthread_setspecific_direct(__PTK_LIBC_DYLD_Unwind_SjLj_Key, fc); +#else + stack = fc; +#endif +} + +#endif + + +/// Called at start of each function that catches exceptions +_LIBUNWIND_EXPORT void +_Unwind_SjLj_Register(struct _Unwind_FunctionContext *fc) { + fc->prev = __Unwind_SjLj_GetTopOfFunctionStack(); + __Unwind_SjLj_SetTopOfFunctionStack(fc); +} + + +/// Called at end of each function that catches exceptions +_LIBUNWIND_EXPORT void +_Unwind_SjLj_Unregister(struct _Unwind_FunctionContext *fc) { + __Unwind_SjLj_SetTopOfFunctionStack(fc->prev); +} + + +static _Unwind_Reason_Code +unwind_phase1(struct _Unwind_Exception *exception_object) { + _Unwind_FunctionContext_t c = __Unwind_SjLj_GetTopOfFunctionStack(); + _LIBUNWIND_TRACE_UNWINDING("unwind_phase1: initial function-context=%p", + (void *)c); + + // walk each frame looking for a place to stop + for (bool handlerNotFound = true; handlerNotFound; c = c->prev) { + + // check for no more frames + if (c == NULL) { + _LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): reached " + "bottom => _URC_END_OF_STACK", + (void *)exception_object); + return _URC_END_OF_STACK; + } + + _LIBUNWIND_TRACE_UNWINDING("unwind_phase1: function-context=%p", (void *)c); + // if there is a personality routine, ask it if it will want to stop at this + // frame + if (c->personality != NULL) { + _LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): calling " + "personality function %p", + (void *)exception_object, + (void *)c->personality); + _Unwind_Reason_Code personalityResult = (*c->personality)( + 1, _UA_SEARCH_PHASE, exception_object->exception_class, + exception_object, (struct _Unwind_Context *)c); + switch (personalityResult) { + case _URC_HANDLER_FOUND: + // found a catch clause or locals that need destructing in this frame + // stop search and remember function context + handlerNotFound = false; + exception_object->private_2 = (uintptr_t) c; + _LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): " + "_URC_HANDLER_FOUND", + (void *)exception_object); + return _URC_NO_REASON; + + case _URC_CONTINUE_UNWIND: + _LIBUNWIND_TRACE_UNWINDING("unwind_phase1(ex_ojb=%p): " + "_URC_CONTINUE_UNWIND", + (void *)exception_object); + // continue unwinding + break; + + default: + // something went wrong + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): _URC_FATAL_PHASE1_ERROR", + (void *)exception_object); + return _URC_FATAL_PHASE1_ERROR; + } + } + } + return _URC_NO_REASON; +} + + +static _Unwind_Reason_Code +unwind_phase2(struct _Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p)", + (void *)exception_object); + + // walk each frame until we reach where search phase said to stop + _Unwind_FunctionContext_t c = __Unwind_SjLj_GetTopOfFunctionStack(); + while (true) { + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2s(ex_ojb=%p): context=%p", + (void *)exception_object, (void *)c); + + // check for no more frames + if (c == NULL) { + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2(ex_ojb=%p): __unw_step() reached " + "bottom => _URC_END_OF_STACK", + (void *)exception_object); + return _URC_END_OF_STACK; + } + + // if there is a personality routine, tell it we are unwinding + if (c->personality != NULL) { + _Unwind_Action action = _UA_CLEANUP_PHASE; + if ((uintptr_t) c == exception_object->private_2) + action = (_Unwind_Action)( + _UA_CLEANUP_PHASE | + _UA_HANDLER_FRAME); // tell personality this was the frame it marked + // in phase 1 + _Unwind_Reason_Code personalityResult = + (*c->personality)(1, action, exception_object->exception_class, + exception_object, (struct _Unwind_Context *)c); + switch (personalityResult) { + case _URC_CONTINUE_UNWIND: + // continue unwinding + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2(ex_ojb=%p): _URC_CONTINUE_UNWIND", + (void *)exception_object); + if ((uintptr_t) c == exception_object->private_2) { + // phase 1 said we would stop at this frame, but we did not... + _LIBUNWIND_ABORT("during phase1 personality function said it would " + "stop here, but now if phase2 it did not stop here"); + } + break; + case _URC_INSTALL_CONTEXT: + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): " + "_URC_INSTALL_CONTEXT, will resume at " + "landing pad %p", + (void *)exception_object, c->jbuf[1]); + // personality routine says to transfer control to landing pad + // we may get control back if landing pad calls _Unwind_Resume() + __Unwind_SjLj_SetTopOfFunctionStack(c); + __builtin_longjmp(c->jbuf, 1); + // __unw_resume() only returns if there was an error + return _URC_FATAL_PHASE2_ERROR; + default: + // something went wrong + _LIBUNWIND_DEBUG_LOG("personality function returned unknown result %d", + personalityResult); + return _URC_FATAL_PHASE2_ERROR; + } + } + c = c->prev; + } + + // clean up phase did not resume at the frame that the search phase said it + // would + return _URC_FATAL_PHASE2_ERROR; +} + + +static _Unwind_Reason_Code +unwind_phase2_forced(struct _Unwind_Exception *exception_object, + _Unwind_Stop_Fn stop, void *stop_parameter) { + // walk each frame until we reach where search phase said to stop + _Unwind_FunctionContext_t c = __Unwind_SjLj_GetTopOfFunctionStack(); + while (true) { + + // get next frame (skip over first which is _Unwind_RaiseException) + if (c == NULL) { + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2(ex_ojb=%p): __unw_step() reached " + "bottom => _URC_END_OF_STACK", + (void *)exception_object); + return _URC_END_OF_STACK; + } + + // call stop function at each frame + _Unwind_Action action = + (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE); + _Unwind_Reason_Code stopResult = + (*stop)(1, action, exception_object->exception_class, exception_object, + (struct _Unwind_Context *)c, stop_parameter); + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "stop function returned %d", + (void *)exception_object, stopResult); + if (stopResult != _URC_NO_REASON) { + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "stopped by stop function", + (void *)exception_object); + return _URC_FATAL_PHASE2_ERROR; + } + + // if there is a personality routine, tell it we are unwinding + if (c->personality != NULL) { + _Unwind_Personality_Fn p = (_Unwind_Personality_Fn)c->personality; + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "calling personality function %p", + (void *)exception_object, (void *)p); + _Unwind_Reason_Code personalityResult = + (*p)(1, action, exception_object->exception_class, exception_object, + (struct _Unwind_Context *)c); + switch (personalityResult) { + case _URC_CONTINUE_UNWIND: + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "personality returned _URC_CONTINUE_UNWIND", + (void *)exception_object); + // destructors called, continue unwinding + break; + case _URC_INSTALL_CONTEXT: + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "personality returned _URC_INSTALL_CONTEXT", + (void *)exception_object); + // we may get control back if landing pad calls _Unwind_Resume() + __Unwind_SjLj_SetTopOfFunctionStack(c); + __builtin_longjmp(c->jbuf, 1); + break; + default: + // something went wrong + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "personality returned %d, " + "_URC_FATAL_PHASE2_ERROR", + (void *)exception_object, personalityResult); + return _URC_FATAL_PHASE2_ERROR; + } + } + c = c->prev; + } + + // call stop function one last time and tell it we've reached the end of the + // stack + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop " + "function with _UA_END_OF_STACK", + (void *)exception_object); + _Unwind_Action lastAction = + (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | _UA_END_OF_STACK); + (*stop)(1, lastAction, exception_object->exception_class, exception_object, + (struct _Unwind_Context *)c, stop_parameter); + + // clean up phase did not resume at the frame that the search phase said it + // would + return _URC_FATAL_PHASE2_ERROR; +} + + +/// Called by __cxa_throw. Only returns if there is a fatal error +_LIBUNWIND_EXPORT _Unwind_Reason_Code +_Unwind_SjLj_RaiseException(struct _Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_SjLj_RaiseException(ex_obj=%p)", + (void *)exception_object); + + // mark that this is a non-forced unwind, so _Unwind_Resume() can do the right + // thing + exception_object->private_1 = 0; + exception_object->private_2 = 0; + + // phase 1: the search phase + _Unwind_Reason_Code phase1 = unwind_phase1(exception_object); + if (phase1 != _URC_NO_REASON) + return phase1; + + // phase 2: the clean up phase + return unwind_phase2(exception_object); +} + + + +/// When _Unwind_RaiseException() is in phase2, it hands control +/// to the personality function at each frame. The personality +/// may force a jump to a landing pad in that function, the landing +/// pad code may then call _Unwind_Resume() to continue with the +/// unwinding. Note: the call to _Unwind_Resume() is from compiler +/// geneated user code. All other _Unwind_* routines are called +/// by the C++ runtime __cxa_* routines. +/// +/// Re-throwing an exception is implemented by having the code call +/// __cxa_rethrow() which in turn calls _Unwind_Resume_or_Rethrow() +_LIBUNWIND_EXPORT void +_Unwind_SjLj_Resume(struct _Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_SjLj_Resume(ex_obj=%p)", + (void *)exception_object); + + if (exception_object->private_1 != 0) + unwind_phase2_forced(exception_object, + (_Unwind_Stop_Fn) exception_object->private_1, + (void *)exception_object->private_2); + else + unwind_phase2(exception_object); + + // clients assume _Unwind_Resume() does not return, so all we can do is abort. + _LIBUNWIND_ABORT("_Unwind_SjLj_Resume() can't return"); +} + + +/// Called by __cxa_rethrow(). +_LIBUNWIND_EXPORT _Unwind_Reason_Code +_Unwind_SjLj_Resume_or_Rethrow(struct _Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("__Unwind_SjLj_Resume_or_Rethrow(ex_obj=%p), " + "private_1=%" PRIuPTR, + (void *)exception_object, exception_object->private_1); + // If this is non-forced and a stopping place was found, then this is a + // re-throw. + // Call _Unwind_RaiseException() as if this was a new exception. + if (exception_object->private_1 == 0) { + return _Unwind_SjLj_RaiseException(exception_object); + // should return if there is no catch clause, so that __cxa_rethrow can call + // std::terminate() + } + + // Call through to _Unwind_Resume() which distiguishes between forced and + // regular exceptions. + _Unwind_SjLj_Resume(exception_object); + _LIBUNWIND_ABORT("__Unwind_SjLj_Resume_or_Rethrow() called " + "_Unwind_SjLj_Resume() which unexpectedly returned"); +} + + +/// Called by personality handler during phase 2 to get LSDA for current frame. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) { + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context; + _LIBUNWIND_TRACE_API("_Unwind_GetLanguageSpecificData(context=%p) " + "=> 0x%" PRIuPTR, + (void *)context, ufc->lsda); + return ufc->lsda; +} + + +/// Called by personality handler during phase 2 to get register values. +_LIBUNWIND_EXPORT uintptr_t _Unwind_GetGR(struct _Unwind_Context *context, + int index) { + _LIBUNWIND_TRACE_API("_Unwind_GetGR(context=%p, reg=%d)", (void *)context, + index); + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context; + return ufc->resumeParameters[index]; +} + + +/// Called by personality handler during phase 2 to alter register values. +_LIBUNWIND_EXPORT void _Unwind_SetGR(struct _Unwind_Context *context, int index, + uintptr_t new_value) { + _LIBUNWIND_TRACE_API("_Unwind_SetGR(context=%p, reg=%d, value=0x%" PRIuPTR + ")", + (void *)context, index, new_value); + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context; + ufc->resumeParameters[index] = new_value; +} + + +/// Called by personality handler during phase 2 to get instruction pointer. +_LIBUNWIND_EXPORT uintptr_t _Unwind_GetIP(struct _Unwind_Context *context) { + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context; + _LIBUNWIND_TRACE_API("_Unwind_GetIP(context=%p) => 0x%" PRIu32, + (void *)context, ufc->resumeLocation + 1); + return ufc->resumeLocation + 1; +} + + +/// Called by personality handler during phase 2 to get instruction pointer. +/// ipBefore is a boolean that says if IP is already adjusted to be the call +/// site address. Normally IP is the return address. +_LIBUNWIND_EXPORT uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context *context, + int *ipBefore) { + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context; + *ipBefore = 0; + _LIBUNWIND_TRACE_API("_Unwind_GetIPInfo(context=%p, %p) => 0x%" PRIu32, + (void *)context, (void *)ipBefore, + ufc->resumeLocation + 1); + return ufc->resumeLocation + 1; +} + + +/// Called by personality handler during phase 2 to alter instruction pointer. +_LIBUNWIND_EXPORT void _Unwind_SetIP(struct _Unwind_Context *context, + uintptr_t new_value) { + _LIBUNWIND_TRACE_API("_Unwind_SetIP(context=%p, value=0x%" PRIuPTR ")", + (void *)context, new_value); + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context; + ufc->resumeLocation = new_value - 1; +} + + +/// Called by personality handler during phase 2 to find the start of the +/// function. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetRegionStart(struct _Unwind_Context *context) { + // Not supported or needed for sjlj based unwinding + (void)context; + _LIBUNWIND_TRACE_API("_Unwind_GetRegionStart(context=%p)", (void *)context); + return 0; +} + + +/// Called by personality handler during phase 2 if a foreign exception +/// is caught. +_LIBUNWIND_EXPORT void +_Unwind_DeleteException(struct _Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_DeleteException(ex_obj=%p)", + (void *)exception_object); + if (exception_object->exception_cleanup != NULL) + (*exception_object->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT, + exception_object); +} + + + +/// Called by personality handler during phase 2 to get base address for data +/// relative encodings. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetDataRelBase(struct _Unwind_Context *context) { + // Not supported or needed for sjlj based unwinding + (void)context; + _LIBUNWIND_TRACE_API("_Unwind_GetDataRelBase(context=%p)", (void *)context); + _LIBUNWIND_ABORT("_Unwind_GetDataRelBase() not implemented"); +} + + +/// Called by personality handler during phase 2 to get base address for text +/// relative encodings. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetTextRelBase(struct _Unwind_Context *context) { + // Not supported or needed for sjlj based unwinding + (void)context; + _LIBUNWIND_TRACE_API("_Unwind_GetTextRelBase(context=%p)", (void *)context); + _LIBUNWIND_ABORT("_Unwind_GetTextRelBase() not implemented"); +} + + +/// Called by personality handler to get "Call Frame Area" for current frame. +_LIBUNWIND_EXPORT uintptr_t _Unwind_GetCFA(struct _Unwind_Context *context) { + _LIBUNWIND_TRACE_API("_Unwind_GetCFA(context=%p)", (void *)context); + if (context != NULL) { + _Unwind_FunctionContext_t ufc = (_Unwind_FunctionContext_t) context; + // Setjmp/longjmp based exceptions don't have a true CFA. + // Instead, the SP in the jmpbuf is the closest approximation. + return (uintptr_t) ufc->jbuf[2]; + } + return 0; +} + +#endif // defined(_LIBUNWIND_BUILD_SJLJ_APIS) diff --git a/shared/sentry/external/crashpad/libunwind/src/UnwindCursor.hpp b/shared/sentry/external/crashpad/libunwind/src/UnwindCursor.hpp new file mode 100644 index 000000000..f8dd3f272 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/src/UnwindCursor.hpp @@ -0,0 +1,2165 @@ +//===------------------------- UnwindCursor.hpp ---------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// C++ interface to lower levels of libunwind +//===----------------------------------------------------------------------===// + +#ifndef __UNWINDCURSOR_HPP__ +#define __UNWINDCURSOR_HPP__ + +#include "cet_unwind.h" +#include +#include +#include +#include + +#ifdef _WIN32 + #include + #include +#endif +#ifdef __APPLE__ + #include + #include +#endif + +#if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) +// Provide a definition for the DISPATCHER_CONTEXT struct for old (Win7 and +// earlier) SDKs. +// MinGW-w64 has always provided this struct. + #if defined(_WIN32) && defined(_LIBUNWIND_TARGET_X86_64) && \ + !defined(__MINGW32__) && VER_PRODUCTBUILD < 8000 +struct _DISPATCHER_CONTEXT { + ULONG64 ControlPc; + ULONG64 ImageBase; + PRUNTIME_FUNCTION FunctionEntry; + ULONG64 EstablisherFrame; + ULONG64 TargetIp; + PCONTEXT ContextRecord; + PEXCEPTION_ROUTINE LanguageHandler; + PVOID HandlerData; + PUNWIND_HISTORY_TABLE HistoryTable; + ULONG ScopeIndex; + ULONG Fill0; +}; + #endif + +struct UNWIND_INFO { + uint8_t Version : 3; + uint8_t Flags : 5; + uint8_t SizeOfProlog; + uint8_t CountOfCodes; + uint8_t FrameRegister : 4; + uint8_t FrameOffset : 4; + uint16_t UnwindCodes[2]; +}; + +extern "C" _Unwind_Reason_Code __libunwind_seh_personality( + int, _Unwind_Action, uint64_t, _Unwind_Exception *, + struct _Unwind_Context *); + +#endif + +#include "config.h" + +#include "AddressSpace.hpp" +#include "CompactUnwinder.hpp" +#include "config.h" +#include "DwarfInstructions.hpp" +#include "EHHeaderParser.hpp" +#include "libunwind.h" +#include "Registers.hpp" +#include "RWMutex.hpp" +#include "Unwind-EHABI.h" + +namespace libunwind { + +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) +/// Cache of recently found FDEs. +template +class _LIBUNWIND_HIDDEN DwarfFDECache { + typedef typename A::pint_t pint_t; +public: + static constexpr pint_t kSearchAll = static_cast(-1); + static pint_t findFDE(pint_t mh, pint_t pc); + static void add(pint_t mh, pint_t ip_start, pint_t ip_end, pint_t fde); + static void removeAllIn(pint_t mh); + static void iterateCacheEntries(void (*func)(unw_word_t ip_start, + unw_word_t ip_end, + unw_word_t fde, unw_word_t mh)); + +private: + + struct entry { + pint_t mh; + pint_t ip_start; + pint_t ip_end; + pint_t fde; + }; + + // These fields are all static to avoid needing an initializer. + // There is only one instance of this class per process. + static RWMutex _lock; +#ifdef __APPLE__ + static void dyldUnloadHook(const struct mach_header *mh, intptr_t slide); + static bool _registeredForDyldUnloads; +#endif + static entry *_buffer; + static entry *_bufferUsed; + static entry *_bufferEnd; + static entry _initialBuffer[64]; +}; + +template +typename DwarfFDECache::entry * +DwarfFDECache::_buffer = _initialBuffer; + +template +typename DwarfFDECache::entry * +DwarfFDECache::_bufferUsed = _initialBuffer; + +template +typename DwarfFDECache::entry * +DwarfFDECache::_bufferEnd = &_initialBuffer[64]; + +template +typename DwarfFDECache::entry DwarfFDECache::_initialBuffer[64]; + +template +RWMutex DwarfFDECache::_lock; + +#ifdef __APPLE__ +template +bool DwarfFDECache::_registeredForDyldUnloads = false; +#endif + +template +typename A::pint_t DwarfFDECache::findFDE(pint_t mh, pint_t pc) { + pint_t result = 0; + _LIBUNWIND_LOG_IF_FALSE(_lock.lock_shared()); + for (entry *p = _buffer; p < _bufferUsed; ++p) { + if ((mh == p->mh) || (mh == kSearchAll)) { + if ((p->ip_start <= pc) && (pc < p->ip_end)) { + result = p->fde; + break; + } + } + } + _LIBUNWIND_LOG_IF_FALSE(_lock.unlock_shared()); + return result; +} + +template +void DwarfFDECache::add(pint_t mh, pint_t ip_start, pint_t ip_end, + pint_t fde) { +#if !defined(_LIBUNWIND_NO_HEAP) + _LIBUNWIND_LOG_IF_FALSE(_lock.lock()); + if (_bufferUsed >= _bufferEnd) { + size_t oldSize = (size_t)(_bufferEnd - _buffer); + size_t newSize = oldSize * 4; + // Can't use operator new (we are below it). + entry *newBuffer = (entry *)malloc(newSize * sizeof(entry)); + memcpy(newBuffer, _buffer, oldSize * sizeof(entry)); + if (_buffer != _initialBuffer) + free(_buffer); + _buffer = newBuffer; + _bufferUsed = &newBuffer[oldSize]; + _bufferEnd = &newBuffer[newSize]; + } + _bufferUsed->mh = mh; + _bufferUsed->ip_start = ip_start; + _bufferUsed->ip_end = ip_end; + _bufferUsed->fde = fde; + ++_bufferUsed; +#ifdef __APPLE__ + if (!_registeredForDyldUnloads) { + _dyld_register_func_for_remove_image(&dyldUnloadHook); + _registeredForDyldUnloads = true; + } +#endif + _LIBUNWIND_LOG_IF_FALSE(_lock.unlock()); +#endif +} + +template +void DwarfFDECache::removeAllIn(pint_t mh) { + _LIBUNWIND_LOG_IF_FALSE(_lock.lock()); + entry *d = _buffer; + for (const entry *s = _buffer; s < _bufferUsed; ++s) { + if (s->mh != mh) { + if (d != s) + *d = *s; + ++d; + } + } + _bufferUsed = d; + _LIBUNWIND_LOG_IF_FALSE(_lock.unlock()); +} + +#ifdef __APPLE__ +template +void DwarfFDECache::dyldUnloadHook(const struct mach_header *mh, intptr_t ) { + removeAllIn((pint_t) mh); +} +#endif + +template +void DwarfFDECache::iterateCacheEntries(void (*func)( + unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh)) { + _LIBUNWIND_LOG_IF_FALSE(_lock.lock()); + for (entry *p = _buffer; p < _bufferUsed; ++p) { + (*func)(p->ip_start, p->ip_end, p->fde, p->mh); + } + _LIBUNWIND_LOG_IF_FALSE(_lock.unlock()); +} +#endif // defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + + +#define arrayoffsetof(type, index, field) ((size_t)(&((type *)0)[index].field)) + +#if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) +template class UnwindSectionHeader { +public: + UnwindSectionHeader(A &addressSpace, typename A::pint_t addr) + : _addressSpace(addressSpace), _addr(addr) {} + + uint32_t version() const { + return _addressSpace.get32(_addr + + offsetof(unwind_info_section_header, version)); + } + uint32_t commonEncodingsArraySectionOffset() const { + return _addressSpace.get32(_addr + + offsetof(unwind_info_section_header, + commonEncodingsArraySectionOffset)); + } + uint32_t commonEncodingsArrayCount() const { + return _addressSpace.get32(_addr + offsetof(unwind_info_section_header, + commonEncodingsArrayCount)); + } + uint32_t personalityArraySectionOffset() const { + return _addressSpace.get32(_addr + offsetof(unwind_info_section_header, + personalityArraySectionOffset)); + } + uint32_t personalityArrayCount() const { + return _addressSpace.get32( + _addr + offsetof(unwind_info_section_header, personalityArrayCount)); + } + uint32_t indexSectionOffset() const { + return _addressSpace.get32( + _addr + offsetof(unwind_info_section_header, indexSectionOffset)); + } + uint32_t indexCount() const { + return _addressSpace.get32( + _addr + offsetof(unwind_info_section_header, indexCount)); + } + +private: + A &_addressSpace; + typename A::pint_t _addr; +}; + +template class UnwindSectionIndexArray { +public: + UnwindSectionIndexArray(A &addressSpace, typename A::pint_t addr) + : _addressSpace(addressSpace), _addr(addr) {} + + uint32_t functionOffset(uint32_t index) const { + return _addressSpace.get32( + _addr + arrayoffsetof(unwind_info_section_header_index_entry, index, + functionOffset)); + } + uint32_t secondLevelPagesSectionOffset(uint32_t index) const { + return _addressSpace.get32( + _addr + arrayoffsetof(unwind_info_section_header_index_entry, index, + secondLevelPagesSectionOffset)); + } + uint32_t lsdaIndexArraySectionOffset(uint32_t index) const { + return _addressSpace.get32( + _addr + arrayoffsetof(unwind_info_section_header_index_entry, index, + lsdaIndexArraySectionOffset)); + } + +private: + A &_addressSpace; + typename A::pint_t _addr; +}; + +template class UnwindSectionRegularPageHeader { +public: + UnwindSectionRegularPageHeader(A &addressSpace, typename A::pint_t addr) + : _addressSpace(addressSpace), _addr(addr) {} + + uint32_t kind() const { + return _addressSpace.get32( + _addr + offsetof(unwind_info_regular_second_level_page_header, kind)); + } + uint16_t entryPageOffset() const { + return _addressSpace.get16( + _addr + offsetof(unwind_info_regular_second_level_page_header, + entryPageOffset)); + } + uint16_t entryCount() const { + return _addressSpace.get16( + _addr + + offsetof(unwind_info_regular_second_level_page_header, entryCount)); + } + +private: + A &_addressSpace; + typename A::pint_t _addr; +}; + +template class UnwindSectionRegularArray { +public: + UnwindSectionRegularArray(A &addressSpace, typename A::pint_t addr) + : _addressSpace(addressSpace), _addr(addr) {} + + uint32_t functionOffset(uint32_t index) const { + return _addressSpace.get32( + _addr + arrayoffsetof(unwind_info_regular_second_level_entry, index, + functionOffset)); + } + uint32_t encoding(uint32_t index) const { + return _addressSpace.get32( + _addr + + arrayoffsetof(unwind_info_regular_second_level_entry, index, encoding)); + } + +private: + A &_addressSpace; + typename A::pint_t _addr; +}; + +template class UnwindSectionCompressedPageHeader { +public: + UnwindSectionCompressedPageHeader(A &addressSpace, typename A::pint_t addr) + : _addressSpace(addressSpace), _addr(addr) {} + + uint32_t kind() const { + return _addressSpace.get32( + _addr + + offsetof(unwind_info_compressed_second_level_page_header, kind)); + } + uint16_t entryPageOffset() const { + return _addressSpace.get16( + _addr + offsetof(unwind_info_compressed_second_level_page_header, + entryPageOffset)); + } + uint16_t entryCount() const { + return _addressSpace.get16( + _addr + + offsetof(unwind_info_compressed_second_level_page_header, entryCount)); + } + uint16_t encodingsPageOffset() const { + return _addressSpace.get16( + _addr + offsetof(unwind_info_compressed_second_level_page_header, + encodingsPageOffset)); + } + uint16_t encodingsCount() const { + return _addressSpace.get16( + _addr + offsetof(unwind_info_compressed_second_level_page_header, + encodingsCount)); + } + +private: + A &_addressSpace; + typename A::pint_t _addr; +}; + +template class UnwindSectionCompressedArray { +public: + UnwindSectionCompressedArray(A &addressSpace, typename A::pint_t addr) + : _addressSpace(addressSpace), _addr(addr) {} + + uint32_t functionOffset(uint32_t index) const { + return UNWIND_INFO_COMPRESSED_ENTRY_FUNC_OFFSET( + _addressSpace.get32(_addr + index * sizeof(uint32_t))); + } + uint16_t encodingIndex(uint32_t index) const { + return UNWIND_INFO_COMPRESSED_ENTRY_ENCODING_INDEX( + _addressSpace.get32(_addr + index * sizeof(uint32_t))); + } + +private: + A &_addressSpace; + typename A::pint_t _addr; +}; + +template class UnwindSectionLsdaArray { +public: + UnwindSectionLsdaArray(A &addressSpace, typename A::pint_t addr) + : _addressSpace(addressSpace), _addr(addr) {} + + uint32_t functionOffset(uint32_t index) const { + return _addressSpace.get32( + _addr + arrayoffsetof(unwind_info_section_header_lsda_index_entry, + index, functionOffset)); + } + uint32_t lsdaOffset(uint32_t index) const { + return _addressSpace.get32( + _addr + arrayoffsetof(unwind_info_section_header_lsda_index_entry, + index, lsdaOffset)); + } + +private: + A &_addressSpace; + typename A::pint_t _addr; +}; +#endif // defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) + +class _LIBUNWIND_HIDDEN AbstractUnwindCursor { +public: + // NOTE: provide a class specific placement deallocation function (S5.3.4 p20) + // This avoids an unnecessary dependency to libc++abi. + void operator delete(void *, size_t) {} + + virtual ~AbstractUnwindCursor() {} + virtual bool validReg(int) { _LIBUNWIND_ABORT("validReg not implemented"); } + virtual unw_word_t getReg(int) { _LIBUNWIND_ABORT("getReg not implemented"); } + virtual void setReg(int, unw_word_t) { + _LIBUNWIND_ABORT("setReg not implemented"); + } + virtual bool validFloatReg(int) { + _LIBUNWIND_ABORT("validFloatReg not implemented"); + } + virtual unw_fpreg_t getFloatReg(int) { + _LIBUNWIND_ABORT("getFloatReg not implemented"); + } + virtual void setFloatReg(int, unw_fpreg_t) { + _LIBUNWIND_ABORT("setFloatReg not implemented"); + } + virtual int step() { _LIBUNWIND_ABORT("step not implemented"); } + virtual void getInfo(unw_proc_info_t *) { + _LIBUNWIND_ABORT("getInfo not implemented"); + } + virtual void jumpto() { _LIBUNWIND_ABORT("jumpto not implemented"); } + virtual bool isSignalFrame() { + _LIBUNWIND_ABORT("isSignalFrame not implemented"); + } + virtual bool getFunctionName(char *, size_t, unw_word_t *) { + _LIBUNWIND_ABORT("getFunctionName not implemented"); + } + virtual void setInfoBasedOnIPRegister(bool = false) { + _LIBUNWIND_ABORT("setInfoBasedOnIPRegister not implemented"); + } + virtual const char *getRegisterName(int) { + _LIBUNWIND_ABORT("getRegisterName not implemented"); + } +#ifdef __arm__ + virtual void saveVFPAsX() { _LIBUNWIND_ABORT("saveVFPAsX not implemented"); } +#endif + +#if defined(_LIBUNWIND_USE_CET) + virtual void *get_registers() { + _LIBUNWIND_ABORT("get_registers not implemented"); + } +#endif +}; + +#if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) && defined(_WIN32) + +/// \c UnwindCursor contains all state (including all register values) during +/// an unwind. This is normally stack-allocated inside a unw_cursor_t. +template +class UnwindCursor : public AbstractUnwindCursor { + typedef typename A::pint_t pint_t; +public: + UnwindCursor(unw_context_t *context, A &as); + UnwindCursor(CONTEXT *context, A &as); + UnwindCursor(A &as, void *threadArg); + virtual ~UnwindCursor() {} + virtual bool validReg(int); + virtual unw_word_t getReg(int); + virtual void setReg(int, unw_word_t); + virtual bool validFloatReg(int); + virtual unw_fpreg_t getFloatReg(int); + virtual void setFloatReg(int, unw_fpreg_t); + virtual int step(); + virtual void getInfo(unw_proc_info_t *); + virtual void jumpto(); + virtual bool isSignalFrame(); + virtual bool getFunctionName(char *buf, size_t len, unw_word_t *off); + virtual void setInfoBasedOnIPRegister(bool isReturnAddress = false); + virtual const char *getRegisterName(int num); +#ifdef __arm__ + virtual void saveVFPAsX(); +#endif + + DISPATCHER_CONTEXT *getDispatcherContext() { return &_dispContext; } + void setDispatcherContext(DISPATCHER_CONTEXT *disp) { _dispContext = *disp; } + + // libunwind does not and should not depend on C++ library which means that we + // need our own defition of inline placement new. + static void *operator new(size_t, UnwindCursor *p) { return p; } + +private: + + pint_t getLastPC() const { return _dispContext.ControlPc; } + void setLastPC(pint_t pc) { _dispContext.ControlPc = pc; } + RUNTIME_FUNCTION *lookUpSEHUnwindInfo(pint_t pc, pint_t *base) { + _dispContext.FunctionEntry = RtlLookupFunctionEntry(pc, + &_dispContext.ImageBase, + _dispContext.HistoryTable); + *base = _dispContext.ImageBase; + return _dispContext.FunctionEntry; + } + bool getInfoFromSEH(pint_t pc); + int stepWithSEHData() { + _dispContext.LanguageHandler = RtlVirtualUnwind(UNW_FLAG_UHANDLER, + _dispContext.ImageBase, + _dispContext.ControlPc, + _dispContext.FunctionEntry, + _dispContext.ContextRecord, + &_dispContext.HandlerData, + &_dispContext.EstablisherFrame, + NULL); + // Update some fields of the unwind info now, since we have them. + _info.lsda = reinterpret_cast(_dispContext.HandlerData); + if (_dispContext.LanguageHandler) { + _info.handler = reinterpret_cast(__libunwind_seh_personality); + } else + _info.handler = 0; + return UNW_STEP_SUCCESS; + } + + A &_addressSpace; + unw_proc_info_t _info; + DISPATCHER_CONTEXT _dispContext; + CONTEXT _msContext; + UNWIND_HISTORY_TABLE _histTable; + bool _unwindInfoMissing; +}; + + +template +UnwindCursor::UnwindCursor(unw_context_t *context, A &as) + : _addressSpace(as), _unwindInfoMissing(false) { + static_assert((check_fit, unw_cursor_t>::does_fit), + "UnwindCursor<> does not fit in unw_cursor_t"); + static_assert((alignof(UnwindCursor) <= alignof(unw_cursor_t)), + "UnwindCursor<> requires more alignment than unw_cursor_t"); + memset(&_info, 0, sizeof(_info)); + memset(&_histTable, 0, sizeof(_histTable)); + _dispContext.ContextRecord = &_msContext; + _dispContext.HistoryTable = &_histTable; + // Initialize MS context from ours. + R r(context); + _msContext.ContextFlags = CONTEXT_CONTROL|CONTEXT_INTEGER|CONTEXT_FLOATING_POINT; +#if defined(_LIBUNWIND_TARGET_X86_64) + _msContext.Rax = r.getRegister(UNW_X86_64_RAX); + _msContext.Rcx = r.getRegister(UNW_X86_64_RCX); + _msContext.Rdx = r.getRegister(UNW_X86_64_RDX); + _msContext.Rbx = r.getRegister(UNW_X86_64_RBX); + _msContext.Rsp = r.getRegister(UNW_X86_64_RSP); + _msContext.Rbp = r.getRegister(UNW_X86_64_RBP); + _msContext.Rsi = r.getRegister(UNW_X86_64_RSI); + _msContext.Rdi = r.getRegister(UNW_X86_64_RDI); + _msContext.R8 = r.getRegister(UNW_X86_64_R8); + _msContext.R9 = r.getRegister(UNW_X86_64_R9); + _msContext.R10 = r.getRegister(UNW_X86_64_R10); + _msContext.R11 = r.getRegister(UNW_X86_64_R11); + _msContext.R12 = r.getRegister(UNW_X86_64_R12); + _msContext.R13 = r.getRegister(UNW_X86_64_R13); + _msContext.R14 = r.getRegister(UNW_X86_64_R14); + _msContext.R15 = r.getRegister(UNW_X86_64_R15); + _msContext.Rip = r.getRegister(UNW_REG_IP); + union { + v128 v; + M128A m; + } t; + t.v = r.getVectorRegister(UNW_X86_64_XMM0); + _msContext.Xmm0 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM1); + _msContext.Xmm1 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM2); + _msContext.Xmm2 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM3); + _msContext.Xmm3 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM4); + _msContext.Xmm4 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM5); + _msContext.Xmm5 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM6); + _msContext.Xmm6 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM7); + _msContext.Xmm7 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM8); + _msContext.Xmm8 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM9); + _msContext.Xmm9 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM10); + _msContext.Xmm10 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM11); + _msContext.Xmm11 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM12); + _msContext.Xmm12 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM13); + _msContext.Xmm13 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM14); + _msContext.Xmm14 = t.m; + t.v = r.getVectorRegister(UNW_X86_64_XMM15); + _msContext.Xmm15 = t.m; +#elif defined(_LIBUNWIND_TARGET_ARM) + _msContext.R0 = r.getRegister(UNW_ARM_R0); + _msContext.R1 = r.getRegister(UNW_ARM_R1); + _msContext.R2 = r.getRegister(UNW_ARM_R2); + _msContext.R3 = r.getRegister(UNW_ARM_R3); + _msContext.R4 = r.getRegister(UNW_ARM_R4); + _msContext.R5 = r.getRegister(UNW_ARM_R5); + _msContext.R6 = r.getRegister(UNW_ARM_R6); + _msContext.R7 = r.getRegister(UNW_ARM_R7); + _msContext.R8 = r.getRegister(UNW_ARM_R8); + _msContext.R9 = r.getRegister(UNW_ARM_R9); + _msContext.R10 = r.getRegister(UNW_ARM_R10); + _msContext.R11 = r.getRegister(UNW_ARM_R11); + _msContext.R12 = r.getRegister(UNW_ARM_R12); + _msContext.Sp = r.getRegister(UNW_ARM_SP); + _msContext.Lr = r.getRegister(UNW_ARM_LR); + _msContext.Pc = r.getRegister(UNW_ARM_IP); + for (int i = UNW_ARM_D0; i <= UNW_ARM_D31; ++i) { + union { + uint64_t w; + double d; + } d; + d.d = r.getFloatRegister(i); + _msContext.D[i - UNW_ARM_D0] = d.w; + } +#elif defined(_LIBUNWIND_TARGET_AARCH64) + for (int i = UNW_AARCH64_X0; i <= UNW_ARM64_X30; ++i) + _msContext.X[i - UNW_AARCH64_X0] = r.getRegister(i); + _msContext.Sp = r.getRegister(UNW_REG_SP); + _msContext.Pc = r.getRegister(UNW_REG_IP); + for (int i = UNW_AARCH64_V0; i <= UNW_ARM64_D31; ++i) + _msContext.V[i - UNW_AARCH64_V0].D[0] = r.getFloatRegister(i); +#endif +} + +template +UnwindCursor::UnwindCursor(CONTEXT *context, A &as) + : _addressSpace(as), _unwindInfoMissing(false) { + static_assert((check_fit, unw_cursor_t>::does_fit), + "UnwindCursor<> does not fit in unw_cursor_t"); + memset(&_info, 0, sizeof(_info)); + memset(&_histTable, 0, sizeof(_histTable)); + _dispContext.ContextRecord = &_msContext; + _dispContext.HistoryTable = &_histTable; + _msContext = *context; +} + + +template +bool UnwindCursor::validReg(int regNum) { + if (regNum == UNW_REG_IP || regNum == UNW_REG_SP) return true; +#if defined(_LIBUNWIND_TARGET_X86_64) + if (regNum >= UNW_X86_64_RAX && regNum <= UNW_X86_64_R15) return true; +#elif defined(_LIBUNWIND_TARGET_ARM) + if (regNum >= UNW_ARM_R0 && regNum <= UNW_ARM_R15) return true; +#elif defined(_LIBUNWIND_TARGET_AARCH64) + if (regNum >= UNW_AARCH64_X0 && regNum <= UNW_ARM64_X30) return true; +#endif + return false; +} + +template +unw_word_t UnwindCursor::getReg(int regNum) { + switch (regNum) { +#if defined(_LIBUNWIND_TARGET_X86_64) + case UNW_REG_IP: return _msContext.Rip; + case UNW_X86_64_RAX: return _msContext.Rax; + case UNW_X86_64_RDX: return _msContext.Rdx; + case UNW_X86_64_RCX: return _msContext.Rcx; + case UNW_X86_64_RBX: return _msContext.Rbx; + case UNW_REG_SP: + case UNW_X86_64_RSP: return _msContext.Rsp; + case UNW_X86_64_RBP: return _msContext.Rbp; + case UNW_X86_64_RSI: return _msContext.Rsi; + case UNW_X86_64_RDI: return _msContext.Rdi; + case UNW_X86_64_R8: return _msContext.R8; + case UNW_X86_64_R9: return _msContext.R9; + case UNW_X86_64_R10: return _msContext.R10; + case UNW_X86_64_R11: return _msContext.R11; + case UNW_X86_64_R12: return _msContext.R12; + case UNW_X86_64_R13: return _msContext.R13; + case UNW_X86_64_R14: return _msContext.R14; + case UNW_X86_64_R15: return _msContext.R15; +#elif defined(_LIBUNWIND_TARGET_ARM) + case UNW_ARM_R0: return _msContext.R0; + case UNW_ARM_R1: return _msContext.R1; + case UNW_ARM_R2: return _msContext.R2; + case UNW_ARM_R3: return _msContext.R3; + case UNW_ARM_R4: return _msContext.R4; + case UNW_ARM_R5: return _msContext.R5; + case UNW_ARM_R6: return _msContext.R6; + case UNW_ARM_R7: return _msContext.R7; + case UNW_ARM_R8: return _msContext.R8; + case UNW_ARM_R9: return _msContext.R9; + case UNW_ARM_R10: return _msContext.R10; + case UNW_ARM_R11: return _msContext.R11; + case UNW_ARM_R12: return _msContext.R12; + case UNW_REG_SP: + case UNW_ARM_SP: return _msContext.Sp; + case UNW_ARM_LR: return _msContext.Lr; + case UNW_REG_IP: + case UNW_ARM_IP: return _msContext.Pc; +#elif defined(_LIBUNWIND_TARGET_AARCH64) + case UNW_REG_SP: return _msContext.Sp; + case UNW_REG_IP: return _msContext.Pc; + default: return _msContext.X[regNum - UNW_AARCH64_X0]; +#endif + } + _LIBUNWIND_ABORT("unsupported register"); +} + +template +void UnwindCursor::setReg(int regNum, unw_word_t value) { + switch (regNum) { +#if defined(_LIBUNWIND_TARGET_X86_64) + case UNW_REG_IP: _msContext.Rip = value; break; + case UNW_X86_64_RAX: _msContext.Rax = value; break; + case UNW_X86_64_RDX: _msContext.Rdx = value; break; + case UNW_X86_64_RCX: _msContext.Rcx = value; break; + case UNW_X86_64_RBX: _msContext.Rbx = value; break; + case UNW_REG_SP: + case UNW_X86_64_RSP: _msContext.Rsp = value; break; + case UNW_X86_64_RBP: _msContext.Rbp = value; break; + case UNW_X86_64_RSI: _msContext.Rsi = value; break; + case UNW_X86_64_RDI: _msContext.Rdi = value; break; + case UNW_X86_64_R8: _msContext.R8 = value; break; + case UNW_X86_64_R9: _msContext.R9 = value; break; + case UNW_X86_64_R10: _msContext.R10 = value; break; + case UNW_X86_64_R11: _msContext.R11 = value; break; + case UNW_X86_64_R12: _msContext.R12 = value; break; + case UNW_X86_64_R13: _msContext.R13 = value; break; + case UNW_X86_64_R14: _msContext.R14 = value; break; + case UNW_X86_64_R15: _msContext.R15 = value; break; +#elif defined(_LIBUNWIND_TARGET_ARM) + case UNW_ARM_R0: _msContext.R0 = value; break; + case UNW_ARM_R1: _msContext.R1 = value; break; + case UNW_ARM_R2: _msContext.R2 = value; break; + case UNW_ARM_R3: _msContext.R3 = value; break; + case UNW_ARM_R4: _msContext.R4 = value; break; + case UNW_ARM_R5: _msContext.R5 = value; break; + case UNW_ARM_R6: _msContext.R6 = value; break; + case UNW_ARM_R7: _msContext.R7 = value; break; + case UNW_ARM_R8: _msContext.R8 = value; break; + case UNW_ARM_R9: _msContext.R9 = value; break; + case UNW_ARM_R10: _msContext.R10 = value; break; + case UNW_ARM_R11: _msContext.R11 = value; break; + case UNW_ARM_R12: _msContext.R12 = value; break; + case UNW_REG_SP: + case UNW_ARM_SP: _msContext.Sp = value; break; + case UNW_ARM_LR: _msContext.Lr = value; break; + case UNW_REG_IP: + case UNW_ARM_IP: _msContext.Pc = value; break; +#elif defined(_LIBUNWIND_TARGET_AARCH64) + case UNW_REG_SP: _msContext.Sp = value; break; + case UNW_REG_IP: _msContext.Pc = value; break; + case UNW_AARCH64_X0: + case UNW_AARCH64_X1: + case UNW_AARCH64_X2: + case UNW_AARCH64_X3: + case UNW_AARCH64_X4: + case UNW_AARCH64_X5: + case UNW_AARCH64_X6: + case UNW_AARCH64_X7: + case UNW_AARCH64_X8: + case UNW_AARCH64_X9: + case UNW_AARCH64_X10: + case UNW_AARCH64_X11: + case UNW_AARCH64_X12: + case UNW_AARCH64_X13: + case UNW_AARCH64_X14: + case UNW_AARCH64_X15: + case UNW_AARCH64_X16: + case UNW_AARCH64_X17: + case UNW_AARCH64_X18: + case UNW_AARCH64_X19: + case UNW_AARCH64_X20: + case UNW_AARCH64_X21: + case UNW_AARCH64_X22: + case UNW_AARCH64_X23: + case UNW_AARCH64_X24: + case UNW_AARCH64_X25: + case UNW_AARCH64_X26: + case UNW_AARCH64_X27: + case UNW_AARCH64_X28: + case UNW_AARCH64_FP: + case UNW_AARCH64_LR: _msContext.X[regNum - UNW_ARM64_X0] = value; break; +#endif + default: + _LIBUNWIND_ABORT("unsupported register"); + } +} + +template +bool UnwindCursor::validFloatReg(int regNum) { +#if defined(_LIBUNWIND_TARGET_ARM) + if (regNum >= UNW_ARM_S0 && regNum <= UNW_ARM_S31) return true; + if (regNum >= UNW_ARM_D0 && regNum <= UNW_ARM_D31) return true; +#elif defined(_LIBUNWIND_TARGET_AARCH64) + if (regNum >= UNW_AARCH64_V0 && regNum <= UNW_ARM64_D31) return true; +#else + (void)regNum; +#endif + return false; +} + +template +unw_fpreg_t UnwindCursor::getFloatReg(int regNum) { +#if defined(_LIBUNWIND_TARGET_ARM) + if (regNum >= UNW_ARM_S0 && regNum <= UNW_ARM_S31) { + union { + uint32_t w; + float f; + } d; + d.w = _msContext.S[regNum - UNW_ARM_S0]; + return d.f; + } + if (regNum >= UNW_ARM_D0 && regNum <= UNW_ARM_D31) { + union { + uint64_t w; + double d; + } d; + d.w = _msContext.D[regNum - UNW_ARM_D0]; + return d.d; + } + _LIBUNWIND_ABORT("unsupported float register"); +#elif defined(_LIBUNWIND_TARGET_AARCH64) + return _msContext.V[regNum - UNW_AARCH64_V0].D[0]; +#else + (void)regNum; + _LIBUNWIND_ABORT("float registers unimplemented"); +#endif +} + +template +void UnwindCursor::setFloatReg(int regNum, unw_fpreg_t value) { +#if defined(_LIBUNWIND_TARGET_ARM) + if (regNum >= UNW_ARM_S0 && regNum <= UNW_ARM_S31) { + union { + uint32_t w; + float f; + } d; + d.f = value; + _msContext.S[regNum - UNW_ARM_S0] = d.w; + } + if (regNum >= UNW_ARM_D0 && regNum <= UNW_ARM_D31) { + union { + uint64_t w; + double d; + } d; + d.d = value; + _msContext.D[regNum - UNW_ARM_D0] = d.w; + } + _LIBUNWIND_ABORT("unsupported float register"); +#elif defined(_LIBUNWIND_TARGET_AARCH64) + _msContext.V[regNum - UNW_AARCH64_V0].D[0] = value; +#else + (void)regNum; + (void)value; + _LIBUNWIND_ABORT("float registers unimplemented"); +#endif +} + +template void UnwindCursor::jumpto() { + RtlRestoreContext(&_msContext, nullptr); +} + +#ifdef __arm__ +template void UnwindCursor::saveVFPAsX() {} +#endif + +template +const char *UnwindCursor::getRegisterName(int regNum) { + return R::getRegisterName(regNum); +} + +template bool UnwindCursor::isSignalFrame() { + return false; +} + +#else // !defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) || !defined(_WIN32) + +/// UnwindCursor contains all state (including all register values) during +/// an unwind. This is normally stack allocated inside a unw_cursor_t. +template +class UnwindCursor : public AbstractUnwindCursor{ + typedef typename A::pint_t pint_t; +public: + UnwindCursor(unw_context_t *context, A &as); + UnwindCursor(A &as, thread_t thread); + virtual ~UnwindCursor() {} + virtual bool validReg(int); + virtual unw_word_t getReg(int); + virtual void setReg(int, unw_word_t); + virtual bool validFloatReg(int); + virtual unw_fpreg_t getFloatReg(int); + virtual void setFloatReg(int, unw_fpreg_t); + virtual int step(); + virtual void getInfo(unw_proc_info_t *); + virtual void jumpto(); + virtual bool isSignalFrame(); + virtual bool getFunctionName(char *buf, size_t len, unw_word_t *off); + virtual void setInfoBasedOnIPRegister(bool isReturnAddress = false); + virtual const char *getRegisterName(int num); +#ifdef __arm__ + virtual void saveVFPAsX(); +#endif + +#if defined(_LIBUNWIND_USE_CET) + virtual void *get_registers() { return &_registers; } +#endif + // libunwind does not and should not depend on C++ library which means that we + // need our own defition of inline placement new. + static void *operator new(size_t, UnwindCursor *p) { return p; } + +private: + +#if defined(_LIBUNWIND_ARM_EHABI) + bool getInfoFromEHABISection(pint_t pc, const UnwindInfoSections §s); + + int stepWithEHABI() { + size_t len = 0; + size_t off = 0; + // FIXME: Calling decode_eht_entry() here is violating the libunwind + // abstraction layer. + const uint32_t *ehtp = + decode_eht_entry(reinterpret_cast(_info.unwind_info), + &off, &len); + if (_Unwind_VRS_Interpret((_Unwind_Context *)this, ehtp, off, len) != + _URC_CONTINUE_UNWIND) + return UNW_STEP_END; + return UNW_STEP_SUCCESS; + } +#endif + +#if defined(_LIBUNWIND_TARGET_LINUX) && defined(_LIBUNWIND_TARGET_AARCH64) + bool setInfoForSigReturn() { + R dummy; + return setInfoForSigReturn(dummy); + } + int stepThroughSigReturn() { + R dummy; + return stepThroughSigReturn(dummy); + } + bool setInfoForSigReturn(Registers_arm64 &); + int stepThroughSigReturn(Registers_arm64 &); + template bool setInfoForSigReturn(Registers &) { + return false; + } + template int stepThroughSigReturn(Registers &) { + return UNW_STEP_END; + } +#endif + +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + bool getInfoFromFdeCie(const typename CFI_Parser::FDE_Info &fdeInfo, + const typename CFI_Parser::CIE_Info &cieInfo, + pint_t pc, uintptr_t dso_base); + bool getInfoFromDwarfSection(pint_t pc, const UnwindInfoSections §s, + uint32_t fdeSectionOffsetHint=0); + int stepWithDwarfFDE() { + return DwarfInstructions::stepWithDwarf(_addressSpace, + (pint_t)this->getReg(UNW_REG_IP), + (pint_t)_info.unwind_info, + _registers, _isSignalFrame); + } +#endif + +#if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) + bool getInfoFromCompactEncodingSection(pint_t pc, + const UnwindInfoSections §s); + int stepWithCompactEncoding() { + #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + if ( compactSaysUseDwarf() ) + return stepWithDwarfFDE(); + #endif + R dummy; + return stepWithCompactEncoding(dummy); + } + +#if defined(_LIBUNWIND_TARGET_X86_64) + int stepWithCompactEncoding(Registers_x86_64 &) { + return CompactUnwinder_x86_64::stepWithCompactEncoding( + _info.format, _info.start_ip, _addressSpace, _registers); + } +#endif + +#if defined(_LIBUNWIND_TARGET_I386) + int stepWithCompactEncoding(Registers_x86 &) { + return CompactUnwinder_x86::stepWithCompactEncoding( + _info.format, (uint32_t)_info.start_ip, _addressSpace, _registers); + } +#endif + +#if defined(_LIBUNWIND_TARGET_PPC) + int stepWithCompactEncoding(Registers_ppc &) { + return UNW_EINVAL; + } +#endif + +#if defined(_LIBUNWIND_TARGET_PPC64) + int stepWithCompactEncoding(Registers_ppc64 &) { + return UNW_EINVAL; + } +#endif + + +#if defined(_LIBUNWIND_TARGET_AARCH64) + int stepWithCompactEncoding(Registers_arm64 &) { + return CompactUnwinder_arm64::stepWithCompactEncoding( + _info.format, _info.start_ip, _addressSpace, _registers); + } +#endif + +#if defined(_LIBUNWIND_TARGET_MIPS_O32) + int stepWithCompactEncoding(Registers_mips_o32 &) { + return UNW_EINVAL; + } +#endif + +#if defined(_LIBUNWIND_TARGET_MIPS_NEWABI) + int stepWithCompactEncoding(Registers_mips_newabi &) { + return UNW_EINVAL; + } +#endif + +#if defined(_LIBUNWIND_TARGET_SPARC) + int stepWithCompactEncoding(Registers_sparc &) { return UNW_EINVAL; } +#endif + +#if defined (_LIBUNWIND_TARGET_RISCV) + int stepWithCompactEncoding(Registers_riscv &) { + return UNW_EINVAL; + } +#endif + + bool compactSaysUseDwarf(uint32_t *offset=NULL) const { + R dummy; + return compactSaysUseDwarf(dummy, offset); + } + +#if defined(_LIBUNWIND_TARGET_X86_64) + bool compactSaysUseDwarf(Registers_x86_64 &, uint32_t *offset) const { + if ((_info.format & UNWIND_X86_64_MODE_MASK) == UNWIND_X86_64_MODE_DWARF) { + if (offset) + *offset = (_info.format & UNWIND_X86_64_DWARF_SECTION_OFFSET); + return true; + } + return false; + } +#endif + +#if defined(_LIBUNWIND_TARGET_I386) + bool compactSaysUseDwarf(Registers_x86 &, uint32_t *offset) const { + if ((_info.format & UNWIND_X86_MODE_MASK) == UNWIND_X86_MODE_DWARF) { + if (offset) + *offset = (_info.format & UNWIND_X86_DWARF_SECTION_OFFSET); + return true; + } + return false; + } +#endif + +#if defined(_LIBUNWIND_TARGET_PPC) + bool compactSaysUseDwarf(Registers_ppc &, uint32_t *) const { + return true; + } +#endif + +#if defined(_LIBUNWIND_TARGET_PPC64) + bool compactSaysUseDwarf(Registers_ppc64 &, uint32_t *) const { + return true; + } +#endif + +#if defined(_LIBUNWIND_TARGET_AARCH64) + bool compactSaysUseDwarf(Registers_arm64 &, uint32_t *offset) const { + if ((_info.format & UNWIND_ARM64_MODE_MASK) == UNWIND_ARM64_MODE_DWARF) { + if (offset) + *offset = (_info.format & UNWIND_ARM64_DWARF_SECTION_OFFSET); + return true; + } + return false; + } +#endif + +#if defined(_LIBUNWIND_TARGET_MIPS_O32) + bool compactSaysUseDwarf(Registers_mips_o32 &, uint32_t *) const { + return true; + } +#endif + +#if defined(_LIBUNWIND_TARGET_MIPS_NEWABI) + bool compactSaysUseDwarf(Registers_mips_newabi &, uint32_t *) const { + return true; + } +#endif + +#if defined(_LIBUNWIND_TARGET_SPARC) + bool compactSaysUseDwarf(Registers_sparc &, uint32_t *) const { return true; } +#endif + +#if defined (_LIBUNWIND_TARGET_RISCV) + bool compactSaysUseDwarf(Registers_riscv &, uint32_t *) const { + return true; + } +#endif + +#endif // defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) + +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + compact_unwind_encoding_t dwarfEncoding() const { + R dummy; + return dwarfEncoding(dummy); + } + +#if defined(_LIBUNWIND_TARGET_X86_64) + compact_unwind_encoding_t dwarfEncoding(Registers_x86_64 &) const { + return UNWIND_X86_64_MODE_DWARF; + } +#endif + +#if defined(_LIBUNWIND_TARGET_I386) + compact_unwind_encoding_t dwarfEncoding(Registers_x86 &) const { + return UNWIND_X86_MODE_DWARF; + } +#endif + +#if defined(_LIBUNWIND_TARGET_PPC) + compact_unwind_encoding_t dwarfEncoding(Registers_ppc &) const { + return 0; + } +#endif + +#if defined(_LIBUNWIND_TARGET_PPC64) + compact_unwind_encoding_t dwarfEncoding(Registers_ppc64 &) const { + return 0; + } +#endif + +#if defined(_LIBUNWIND_TARGET_AARCH64) + compact_unwind_encoding_t dwarfEncoding(Registers_arm64 &) const { + return UNWIND_ARM64_MODE_DWARF; + } +#endif + +#if defined(_LIBUNWIND_TARGET_ARM) + compact_unwind_encoding_t dwarfEncoding(Registers_arm &) const { + return 0; + } +#endif + +#if defined (_LIBUNWIND_TARGET_OR1K) + compact_unwind_encoding_t dwarfEncoding(Registers_or1k &) const { + return 0; + } +#endif + +#if defined (_LIBUNWIND_TARGET_HEXAGON) + compact_unwind_encoding_t dwarfEncoding(Registers_hexagon &) const { + return 0; + } +#endif + +#if defined (_LIBUNWIND_TARGET_MIPS_O32) + compact_unwind_encoding_t dwarfEncoding(Registers_mips_o32 &) const { + return 0; + } +#endif + +#if defined (_LIBUNWIND_TARGET_MIPS_NEWABI) + compact_unwind_encoding_t dwarfEncoding(Registers_mips_newabi &) const { + return 0; + } +#endif + +#if defined(_LIBUNWIND_TARGET_SPARC) + compact_unwind_encoding_t dwarfEncoding(Registers_sparc &) const { return 0; } +#endif + +#if defined (_LIBUNWIND_TARGET_RISCV) + compact_unwind_encoding_t dwarfEncoding(Registers_riscv &) const { + return 0; + } +#endif + +#endif // defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + +#if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) + // For runtime environments using SEH unwind data without Windows runtime + // support. + pint_t getLastPC() const { /* FIXME: Implement */ return 0; } + void setLastPC(pint_t pc) { /* FIXME: Implement */ } + RUNTIME_FUNCTION *lookUpSEHUnwindInfo(pint_t pc, pint_t *base) { + /* FIXME: Implement */ + *base = 0; + return nullptr; + } + bool getInfoFromSEH(pint_t pc); + int stepWithSEHData() { /* FIXME: Implement */ return 0; } +#endif // defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) + + + A &_addressSpace; + R _registers; + unw_proc_info_t _info; + bool _unwindInfoMissing; + bool _isSignalFrame; +#if defined(_LIBUNWIND_TARGET_LINUX) && defined(_LIBUNWIND_TARGET_AARCH64) + bool _isSigReturn = false; +#endif +}; + + +template +UnwindCursor::UnwindCursor(unw_context_t *context, A &as) + : _addressSpace(as), _registers(context), _unwindInfoMissing(false), + _isSignalFrame(false) { + static_assert((check_fit, unw_cursor_t>::does_fit), + "UnwindCursor<> does not fit in unw_cursor_t"); + static_assert((alignof(UnwindCursor) <= alignof(unw_cursor_t)), + "UnwindCursor<> requires more alignment than unw_cursor_t"); + memset(&_info, 0, sizeof(_info)); +} + +template +UnwindCursor::UnwindCursor(A &as, thread_t thread) + : _addressSpace(as), _unwindInfoMissing(false), _isSignalFrame(false) { + memset(&_info, 0, sizeof(_info)); + +#if defined(__x86_64__) + thread_state_flavor_t thread_state_flavor = x86_THREAD_STATE64; + mach_msg_type_number_t thread_state_count = x86_THREAD_STATE64_COUNT; +#elif defined(__aarch64__) + thread_state_flavor_t thread_state_flavor = ARM_THREAD_STATE64; + mach_msg_type_number_t thread_state_count = ARM_THREAD_STATE64_COUNT; +#else +#error Architecture not supported +#endif + + // lucky us: the layout of the various `Registers_X` classes matches whatever + // the mach kernel is writing here. + + kern_return_t kr = + thread_get_state(thread, thread_state_flavor, + (thread_state_t)&_registers, + &thread_state_count); + + // FIXME: the function is infallible + (void)kr; +} + +template +bool UnwindCursor::validReg(int regNum) { + return _registers.validRegister(regNum); +} + +template +unw_word_t UnwindCursor::getReg(int regNum) { + return _registers.getRegister(regNum); +} + +template +void UnwindCursor::setReg(int regNum, unw_word_t value) { + _registers.setRegister(regNum, (typename A::pint_t)value); +} + +template +bool UnwindCursor::validFloatReg(int regNum) { + return _registers.validFloatRegister(regNum); +} + +template +unw_fpreg_t UnwindCursor::getFloatReg(int regNum) { + return _registers.getFloatRegister(regNum); +} + +template +void UnwindCursor::setFloatReg(int regNum, unw_fpreg_t value) { + _registers.setFloatRegister(regNum, value); +} + +template void UnwindCursor::jumpto() { + _registers.jumpto(); +} + +#ifdef __arm__ +template void UnwindCursor::saveVFPAsX() { + _registers.saveVFPAsX(); +} +#endif + +template +const char *UnwindCursor::getRegisterName(int regNum) { + return _registers.getRegisterName(regNum); +} + +template bool UnwindCursor::isSignalFrame() { + return _isSignalFrame; +} + +#endif // defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) + +#if defined(_LIBUNWIND_ARM_EHABI) +template +struct EHABISectionIterator { + typedef EHABISectionIterator _Self; + + typedef typename A::pint_t value_type; + typedef typename A::pint_t* pointer; + typedef typename A::pint_t& reference; + typedef size_t size_type; + typedef size_t difference_type; + + static _Self begin(A& addressSpace, const UnwindInfoSections& sects) { + return _Self(addressSpace, sects, 0); + } + static _Self end(A& addressSpace, const UnwindInfoSections& sects) { + return _Self(addressSpace, sects, + sects.arm_section_length / sizeof(EHABIIndexEntry)); + } + + EHABISectionIterator(A& addressSpace, const UnwindInfoSections& sects, size_t i) + : _i(i), _addressSpace(&addressSpace), _sects(§s) {} + + _Self& operator++() { ++_i; return *this; } + _Self& operator+=(size_t a) { _i += a; return *this; } + _Self& operator--() { assert(_i > 0); --_i; return *this; } + _Self& operator-=(size_t a) { assert(_i >= a); _i -= a; return *this; } + + _Self operator+(size_t a) { _Self out = *this; out._i += a; return out; } + _Self operator-(size_t a) { assert(_i >= a); _Self out = *this; out._i -= a; return out; } + + size_t operator-(const _Self& other) const { return _i - other._i; } + + bool operator==(const _Self& other) const { + assert(_addressSpace == other._addressSpace); + assert(_sects == other._sects); + return _i == other._i; + } + + bool operator!=(const _Self& other) const { + assert(_addressSpace == other._addressSpace); + assert(_sects == other._sects); + return _i != other._i; + } + + typename A::pint_t operator*() const { return functionAddress(); } + + typename A::pint_t functionAddress() const { + typename A::pint_t indexAddr = _sects->arm_section + arrayoffsetof( + EHABIIndexEntry, _i, functionOffset); + return indexAddr + signExtendPrel31(_addressSpace->get32(indexAddr)); + } + + typename A::pint_t dataAddress() { + typename A::pint_t indexAddr = _sects->arm_section + arrayoffsetof( + EHABIIndexEntry, _i, data); + return indexAddr; + } + + private: + size_t _i; + A* _addressSpace; + const UnwindInfoSections* _sects; +}; + +namespace { + +template +EHABISectionIterator EHABISectionUpperBound( + EHABISectionIterator first, + EHABISectionIterator last, + typename A::pint_t value) { + size_t len = last - first; + while (len > 0) { + size_t l2 = len / 2; + EHABISectionIterator m = first + l2; + if (value < *m) { + len = l2; + } else { + first = ++m; + len -= l2 + 1; + } + } + return first; +} + +} + +template +bool UnwindCursor::getInfoFromEHABISection( + pint_t pc, + const UnwindInfoSections §s) { + EHABISectionIterator begin = + EHABISectionIterator::begin(_addressSpace, sects); + EHABISectionIterator end = + EHABISectionIterator::end(_addressSpace, sects); + if (begin == end) + return false; + + EHABISectionIterator itNextPC = EHABISectionUpperBound(begin, end, pc); + if (itNextPC == begin) + return false; + EHABISectionIterator itThisPC = itNextPC - 1; + + pint_t thisPC = itThisPC.functionAddress(); + // If an exception is thrown from a function, corresponding to the last entry + // in the table, we don't really know the function extent and have to choose a + // value for nextPC. Choosing max() will allow the range check during trace to + // succeed. + pint_t nextPC = (itNextPC == end) ? UINTPTR_MAX : itNextPC.functionAddress(); + pint_t indexDataAddr = itThisPC.dataAddress(); + + if (indexDataAddr == 0) + return false; + + uint32_t indexData = _addressSpace.get32(indexDataAddr); + if (indexData == UNW_EXIDX_CANTUNWIND) + return false; + + // If the high bit is set, the exception handling table entry is inline inside + // the index table entry on the second word (aka |indexDataAddr|). Otherwise, + // the table points at an offset in the exception handling table (section 5 + // EHABI). + pint_t exceptionTableAddr; + uint32_t exceptionTableData; + bool isSingleWordEHT; + if (indexData & 0x80000000) { + exceptionTableAddr = indexDataAddr; + // TODO(ajwong): Should this data be 0? + exceptionTableData = indexData; + isSingleWordEHT = true; + } else { + exceptionTableAddr = indexDataAddr + signExtendPrel31(indexData); + exceptionTableData = _addressSpace.get32(exceptionTableAddr); + isSingleWordEHT = false; + } + + // Now we know the 3 things: + // exceptionTableAddr -- exception handler table entry. + // exceptionTableData -- the data inside the first word of the eht entry. + // isSingleWordEHT -- whether the entry is in the index. + unw_word_t personalityRoutine = 0xbadf00d; + bool scope32 = false; + uintptr_t lsda; + + // If the high bit in the exception handling table entry is set, the entry is + // in compact form (section 6.3 EHABI). + if (exceptionTableData & 0x80000000) { + // Grab the index of the personality routine from the compact form. + uint32_t choice = (exceptionTableData & 0x0f000000) >> 24; + uint32_t extraWords = 0; + switch (choice) { + case 0: + personalityRoutine = (unw_word_t) &__aeabi_unwind_cpp_pr0; + extraWords = 0; + scope32 = false; + lsda = isSingleWordEHT ? 0 : (exceptionTableAddr + 4); + break; + case 1: + personalityRoutine = (unw_word_t) &__aeabi_unwind_cpp_pr1; + extraWords = (exceptionTableData & 0x00ff0000) >> 16; + scope32 = false; + lsda = exceptionTableAddr + (extraWords + 1) * 4; + break; + case 2: + personalityRoutine = (unw_word_t) &__aeabi_unwind_cpp_pr2; + extraWords = (exceptionTableData & 0x00ff0000) >> 16; + scope32 = true; + lsda = exceptionTableAddr + (extraWords + 1) * 4; + break; + default: + _LIBUNWIND_ABORT("unknown personality routine"); + return false; + } + + if (isSingleWordEHT) { + if (extraWords != 0) { + _LIBUNWIND_ABORT("index inlined table detected but pr function " + "requires extra words"); + return false; + } + } + } else { + pint_t personalityAddr = + exceptionTableAddr + signExtendPrel31(exceptionTableData); + personalityRoutine = personalityAddr; + + // ARM EHABI # 6.2, # 9.2 + // + // +---- ehtp + // v + // +--------------------------------------+ + // | +--------+--------+--------+-------+ | + // | |0| prel31 to personalityRoutine | | + // | +--------+--------+--------+-------+ | + // | | N | unwind opcodes | | <-- UnwindData + // | +--------+--------+--------+-------+ | + // | | Word 2 unwind opcodes | | + // | +--------+--------+--------+-------+ | + // | ... | + // | +--------+--------+--------+-------+ | + // | | Word N unwind opcodes | | + // | +--------+--------+--------+-------+ | + // | | LSDA | | <-- lsda + // | | ... | | + // | +--------+--------+--------+-------+ | + // +--------------------------------------+ + + uint32_t *UnwindData = reinterpret_cast(exceptionTableAddr) + 1; + uint32_t FirstDataWord = *UnwindData; + size_t N = ((FirstDataWord >> 24) & 0xff); + size_t NDataWords = N + 1; + lsda = reinterpret_cast(UnwindData + NDataWords); + } + + _info.start_ip = thisPC; + _info.end_ip = nextPC; + _info.handler = personalityRoutine; + _info.unwind_info = exceptionTableAddr; + _info.lsda = lsda; + // flags is pr_cache.additional. See EHABI #7.2 for definition of bit 0. + _info.flags = (isSingleWordEHT ? 1 : 0) | (scope32 ? 0x2 : 0); // Use enum? + + return true; +} +#endif + +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) +template +bool UnwindCursor::getInfoFromFdeCie( + const typename CFI_Parser::FDE_Info &fdeInfo, + const typename CFI_Parser::CIE_Info &cieInfo, pint_t pc, + uintptr_t dso_base) { + typename CFI_Parser::PrologInfo prolog; + if (CFI_Parser::parseFDEInstructions(_addressSpace, fdeInfo, cieInfo, pc, + R::getArch(), &prolog)) { + // Save off parsed FDE info + _info.start_ip = fdeInfo.pcStart; + _info.end_ip = fdeInfo.pcEnd; + _info.lsda = fdeInfo.lsda; + _info.handler = cieInfo.personality; + // Some frameless functions need SP altered when resuming in function, so + // propagate spExtraArgSize. + _info.gp = prolog.spExtraArgSize; + _info.flags = 0; + _info.format = dwarfEncoding(); + _info.unwind_info = fdeInfo.fdeStart; + _info.unwind_info_size = static_cast(fdeInfo.fdeLength); + _info.extra = static_cast(dso_base); + return true; + } + return false; +} + +template +bool UnwindCursor::getInfoFromDwarfSection(pint_t pc, + const UnwindInfoSections §s, + uint32_t fdeSectionOffsetHint) { + typename CFI_Parser::FDE_Info fdeInfo; + typename CFI_Parser::CIE_Info cieInfo; + bool foundFDE = false; + bool foundInCache = false; + // If compact encoding table gave offset into dwarf section, go directly there + if (fdeSectionOffsetHint != 0) { + foundFDE = CFI_Parser::findFDE(_addressSpace, pc, sects.dwarf_section, + sects.dwarf_section_length, + sects.dwarf_section + fdeSectionOffsetHint, + &fdeInfo, &cieInfo); + } +#if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) + if (!foundFDE && (sects.dwarf_index_section != 0)) { + foundFDE = EHHeaderParser::findFDE( + _addressSpace, pc, sects.dwarf_index_section, + (uint32_t)sects.dwarf_index_section_length, &fdeInfo, &cieInfo); + } +#endif + if (!foundFDE) { + // otherwise, search cache of previously found FDEs. + pint_t cachedFDE = DwarfFDECache::findFDE(sects.dso_base, pc); + if (cachedFDE != 0) { + foundFDE = + CFI_Parser::findFDE(_addressSpace, pc, sects.dwarf_section, + sects.dwarf_section_length, + cachedFDE, &fdeInfo, &cieInfo); + foundInCache = foundFDE; + } + } + if (!foundFDE) { + // Still not found, do full scan of __eh_frame section. + foundFDE = CFI_Parser::findFDE(_addressSpace, pc, sects.dwarf_section, + sects.dwarf_section_length, 0, + &fdeInfo, &cieInfo); + } + if (foundFDE) { + if (getInfoFromFdeCie(fdeInfo, cieInfo, pc, sects.dso_base)) { + // Add to cache (to make next lookup faster) if we had no hint + // and there was no index. + if (!foundInCache && (fdeSectionOffsetHint == 0)) { + #if defined(_LIBUNWIND_SUPPORT_DWARF_INDEX) + if (sects.dwarf_index_section == 0) + #endif + DwarfFDECache::add(sects.dso_base, fdeInfo.pcStart, fdeInfo.pcEnd, + fdeInfo.fdeStart); + } + return true; + } + } + //_LIBUNWIND_DEBUG_LOG("can't find/use FDE for pc=0x%llX", (uint64_t)pc); + return false; +} +#endif // defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + + +#if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) +template +bool UnwindCursor::getInfoFromCompactEncodingSection(pint_t pc, + const UnwindInfoSections §s) { + const bool log = false; + if (log) + fprintf(stderr, "getInfoFromCompactEncodingSection(pc=0x%llX, mh=0x%llX)\n", + (uint64_t)pc, (uint64_t)sects.dso_base); + + const UnwindSectionHeader sectionHeader(_addressSpace, + sects.compact_unwind_section); + if (sectionHeader.version() != UNWIND_SECTION_VERSION) + return false; + + // do a binary search of top level index to find page with unwind info + pint_t targetFunctionOffset = pc - sects.dso_base; + const UnwindSectionIndexArray topIndex(_addressSpace, + sects.compact_unwind_section + + sectionHeader.indexSectionOffset()); + uint32_t low = 0; + uint32_t high = sectionHeader.indexCount(); + uint32_t last = high - 1; + while (low < high) { + uint32_t mid = (low + high) / 2; + //if ( log ) fprintf(stderr, "\tmid=%d, low=%d, high=%d, *mid=0x%08X\n", + //mid, low, high, topIndex.functionOffset(mid)); + if (topIndex.functionOffset(mid) <= targetFunctionOffset) { + if ((mid == last) || + (topIndex.functionOffset(mid + 1) > targetFunctionOffset)) { + low = mid; + break; + } else { + low = mid + 1; + } + } else { + high = mid; + } + } + const uint32_t firstLevelFunctionOffset = topIndex.functionOffset(low); + const uint32_t firstLevelNextPageFunctionOffset = + topIndex.functionOffset(low + 1); + const pint_t secondLevelAddr = + sects.compact_unwind_section + topIndex.secondLevelPagesSectionOffset(low); + const pint_t lsdaArrayStartAddr = + sects.compact_unwind_section + topIndex.lsdaIndexArraySectionOffset(low); + const pint_t lsdaArrayEndAddr = + sects.compact_unwind_section + topIndex.lsdaIndexArraySectionOffset(low+1); + if (log) + fprintf(stderr, "\tfirst level search for result index=%d " + "to secondLevelAddr=0x%llX\n", + low, (uint64_t) secondLevelAddr); + // do a binary search of second level page index + uint32_t encoding = 0; + pint_t funcStart = 0; + pint_t funcEnd = 0; + pint_t lsda = 0; + pint_t personality = 0; + uint32_t pageKind = _addressSpace.get32(secondLevelAddr); + if (pageKind == UNWIND_SECOND_LEVEL_REGULAR) { + // regular page + UnwindSectionRegularPageHeader pageHeader(_addressSpace, + secondLevelAddr); + UnwindSectionRegularArray pageIndex( + _addressSpace, secondLevelAddr + pageHeader.entryPageOffset()); + // binary search looks for entry with e where index[e].offset <= pc < + // index[e+1].offset + if (log) + fprintf(stderr, "\tbinary search for targetFunctionOffset=0x%08llX in " + "regular page starting at secondLevelAddr=0x%llX\n", + (uint64_t) targetFunctionOffset, (uint64_t) secondLevelAddr); + low = 0; + high = pageHeader.entryCount(); + while (low < high) { + uint32_t mid = (low + high) / 2; + if (pageIndex.functionOffset(mid) <= targetFunctionOffset) { + if (mid == (uint32_t)(pageHeader.entryCount() - 1)) { + // at end of table + low = mid; + funcEnd = firstLevelNextPageFunctionOffset + sects.dso_base; + break; + } else if (pageIndex.functionOffset(mid + 1) > targetFunctionOffset) { + // next is too big, so we found it + low = mid; + funcEnd = pageIndex.functionOffset(low + 1) + sects.dso_base; + break; + } else { + low = mid + 1; + } + } else { + high = mid; + } + } + encoding = pageIndex.encoding(low); + funcStart = pageIndex.functionOffset(low) + sects.dso_base; + if (pc < funcStart) { + if (log) + fprintf( + stderr, + "\tpc not in table, pc=0x%llX, funcStart=0x%llX, funcEnd=0x%llX\n", + (uint64_t) pc, (uint64_t) funcStart, (uint64_t) funcEnd); + return false; + } + if (pc > funcEnd) { + if (log) + fprintf( + stderr, + "\tpc not in table, pc=0x%llX, funcStart=0x%llX, funcEnd=0x%llX\n", + (uint64_t) pc, (uint64_t) funcStart, (uint64_t) funcEnd); + return false; + } + } else if (pageKind == UNWIND_SECOND_LEVEL_COMPRESSED) { + // compressed page + UnwindSectionCompressedPageHeader pageHeader(_addressSpace, + secondLevelAddr); + UnwindSectionCompressedArray pageIndex( + _addressSpace, secondLevelAddr + pageHeader.entryPageOffset()); + const uint32_t targetFunctionPageOffset = + (uint32_t)(targetFunctionOffset - firstLevelFunctionOffset); + // binary search looks for entry with e where index[e].offset <= pc < + // index[e+1].offset + if (log) + fprintf(stderr, "\tbinary search of compressed page starting at " + "secondLevelAddr=0x%llX\n", + (uint64_t) secondLevelAddr); + low = 0; + last = pageHeader.entryCount() - 1; + high = pageHeader.entryCount(); + while (low < high) { + uint32_t mid = (low + high) / 2; + if (pageIndex.functionOffset(mid) <= targetFunctionPageOffset) { + if ((mid == last) || + (pageIndex.functionOffset(mid + 1) > targetFunctionPageOffset)) { + low = mid; + break; + } else { + low = mid + 1; + } + } else { + high = mid; + } + } + funcStart = pageIndex.functionOffset(low) + firstLevelFunctionOffset + + sects.dso_base; + if (low < last) + funcEnd = + pageIndex.functionOffset(low + 1) + firstLevelFunctionOffset + + sects.dso_base; + else + funcEnd = firstLevelNextPageFunctionOffset + sects.dso_base; + if (pc < funcStart) { + _LIBUNWIND_DEBUG_LOG("malformed __unwind_info, pc=0x%llX " + "not in second level compressed unwind table. " + "funcStart=0x%llX", + (uint64_t) pc, (uint64_t) funcStart); + return false; + } + if (pc > funcEnd) { + _LIBUNWIND_DEBUG_LOG("malformed __unwind_info, pc=0x%llX " + "not in second level compressed unwind table. " + "funcEnd=0x%llX", + (uint64_t) pc, (uint64_t) funcEnd); + return false; + } + uint16_t encodingIndex = pageIndex.encodingIndex(low); + if (encodingIndex < sectionHeader.commonEncodingsArrayCount()) { + // encoding is in common table in section header + encoding = _addressSpace.get32( + sects.compact_unwind_section + + sectionHeader.commonEncodingsArraySectionOffset() + + encodingIndex * sizeof(uint32_t)); + } else { + // encoding is in page specific table + uint16_t pageEncodingIndex = + encodingIndex - (uint16_t)sectionHeader.commonEncodingsArrayCount(); + encoding = _addressSpace.get32(secondLevelAddr + + pageHeader.encodingsPageOffset() + + pageEncodingIndex * sizeof(uint32_t)); + } + } else { + _LIBUNWIND_DEBUG_LOG( + "malformed __unwind_info at 0x%0llX bad second level page", + (uint64_t)sects.compact_unwind_section); + return false; + } + + // look up LSDA, if encoding says function has one + if (encoding & UNWIND_HAS_LSDA) { + UnwindSectionLsdaArray lsdaIndex(_addressSpace, lsdaArrayStartAddr); + uint32_t funcStartOffset = (uint32_t)(funcStart - sects.dso_base); + low = 0; + high = (uint32_t)(lsdaArrayEndAddr - lsdaArrayStartAddr) / + sizeof(unwind_info_section_header_lsda_index_entry); + // binary search looks for entry with exact match for functionOffset + if (log) + fprintf(stderr, + "\tbinary search of lsda table for targetFunctionOffset=0x%08X\n", + funcStartOffset); + while (low < high) { + uint32_t mid = (low + high) / 2; + if (lsdaIndex.functionOffset(mid) == funcStartOffset) { + lsda = lsdaIndex.lsdaOffset(mid) + sects.dso_base; + break; + } else if (lsdaIndex.functionOffset(mid) < funcStartOffset) { + low = mid + 1; + } else { + high = mid; + } + } + if (lsda == 0) { + _LIBUNWIND_DEBUG_LOG("found encoding 0x%08X with HAS_LSDA bit set for " + "pc=0x%0llX, but lsda table has no entry", + encoding, (uint64_t) pc); + return false; + } + } + + // extract personality routine, if encoding says function has one + uint32_t personalityIndex = (encoding & UNWIND_PERSONALITY_MASK) >> + (__builtin_ctz(UNWIND_PERSONALITY_MASK)); + if (personalityIndex != 0) { + --personalityIndex; // change 1-based to zero-based index + if (personalityIndex >= sectionHeader.personalityArrayCount()) { + _LIBUNWIND_DEBUG_LOG("found encoding 0x%08X with personality index %d, " + "but personality table has only %d entries", + encoding, personalityIndex, + sectionHeader.personalityArrayCount()); + return false; + } + int32_t personalityDelta = (int32_t)_addressSpace.get32( + sects.compact_unwind_section + + sectionHeader.personalityArraySectionOffset() + + personalityIndex * sizeof(uint32_t)); + pint_t personalityPointer = sects.dso_base + (pint_t)personalityDelta; + personality = _addressSpace.getP(personalityPointer); + if (log) + fprintf(stderr, "getInfoFromCompactEncodingSection(pc=0x%llX), " + "personalityDelta=0x%08X, personality=0x%08llX\n", + (uint64_t) pc, personalityDelta, (uint64_t) personality); + } + + if (log) + fprintf(stderr, "getInfoFromCompactEncodingSection(pc=0x%llX), " + "encoding=0x%08X, lsda=0x%08llX for funcStart=0x%llX\n", + (uint64_t) pc, encoding, (uint64_t) lsda, (uint64_t) funcStart); + _info.start_ip = funcStart; + _info.end_ip = funcEnd; + _info.lsda = lsda; + _info.handler = personality; + _info.gp = 0; + _info.flags = 0; + _info.format = encoding; + _info.unwind_info = 0; + _info.unwind_info_size = 0; + _info.extra = sects.dso_base; + return true; +} +#endif // defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) + + +#if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) +template +bool UnwindCursor::getInfoFromSEH(pint_t pc) { + pint_t base; + RUNTIME_FUNCTION *unwindEntry = lookUpSEHUnwindInfo(pc, &base); + if (!unwindEntry) { + _LIBUNWIND_DEBUG_LOG("\tpc not in table, pc=0x%llX", (uint64_t) pc); + return false; + } + _info.gp = 0; + _info.flags = 0; + _info.format = 0; + _info.unwind_info_size = sizeof(RUNTIME_FUNCTION); + _info.unwind_info = reinterpret_cast(unwindEntry); + _info.extra = base; + _info.start_ip = base + unwindEntry->BeginAddress; +#ifdef _LIBUNWIND_TARGET_X86_64 + _info.end_ip = base + unwindEntry->EndAddress; + // Only fill in the handler and LSDA if they're stale. + if (pc != getLastPC()) { + UNWIND_INFO *xdata = reinterpret_cast(base + unwindEntry->UnwindData); + if (xdata->Flags & (UNW_FLAG_EHANDLER|UNW_FLAG_UHANDLER)) { + // The personality is given in the UNWIND_INFO itself. The LSDA immediately + // follows the UNWIND_INFO. (This follows how both Clang and MSVC emit + // these structures.) + // N.B. UNWIND_INFO structs are DWORD-aligned. + uint32_t lastcode = (xdata->CountOfCodes + 1) & ~1; + const uint32_t *handler = reinterpret_cast(&xdata->UnwindCodes[lastcode]); + _info.lsda = reinterpret_cast(handler+1); + if (*handler) { + _info.handler = reinterpret_cast(__libunwind_seh_personality); + } else + _info.handler = 0; + } else { + _info.lsda = 0; + _info.handler = 0; + } + } +#elif defined(_LIBUNWIND_TARGET_ARM) + _info.end_ip = _info.start_ip + unwindEntry->FunctionLength; + _info.lsda = 0; // FIXME + _info.handler = 0; // FIXME +#endif + setLastPC(pc); + return true; +} +#endif + + +template +void UnwindCursor::setInfoBasedOnIPRegister(bool isReturnAddress) { +#if defined(_LIBUNWIND_TARGET_LINUX) && defined(_LIBUNWIND_TARGET_AARCH64) + _isSigReturn = false; +#endif + + pint_t pc = static_cast(this->getReg(UNW_REG_IP)); +#if defined(_LIBUNWIND_ARM_EHABI) + // Remove the thumb bit so the IP represents the actual instruction address. + // This matches the behaviour of _Unwind_GetIP on arm. + pc &= (pint_t)~0x1; +#endif + + // Exit early if at the top of the stack. + if (pc == 0) { + _unwindInfoMissing = true; + return; + } + + // If the last line of a function is a "throw" the compiler sometimes + // emits no instructions after the call to __cxa_throw. This means + // the return address is actually the start of the next function. + // To disambiguate this, back up the pc when we know it is a return + // address. + if (isReturnAddress) + --pc; + + // Ask address space object to find unwind sections for this pc. + UnwindInfoSections sects; + if (_addressSpace.findUnwindSections(pc, sects)) { +#if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) + // If there is a compact unwind encoding table, look there first. + if (sects.compact_unwind_section != 0) { + if (this->getInfoFromCompactEncodingSection(pc, sects)) { + #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + // Found info in table, done unless encoding says to use dwarf. + uint32_t dwarfOffset; + if ((sects.dwarf_section != 0) && compactSaysUseDwarf(&dwarfOffset)) { + if (this->getInfoFromDwarfSection(pc, sects, dwarfOffset)) { + // found info in dwarf, done + return; + } + } + #endif + // If unwind table has entry, but entry says there is no unwind info, + // record that we have no unwind info. + // if (_info.format == 0) + // _unwindInfoMissing = true; + return; + } + } +#endif // defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) + +#if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) + // If there is SEH unwind info, look there next. + if (this->getInfoFromSEH(pc)) + return; +#endif + +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + // If there is dwarf unwind info, look there next. + if (sects.dwarf_section != 0) { + if (this->getInfoFromDwarfSection(pc, sects)) { + // found info in dwarf, done + return; + } + } +#endif + +#if defined(_LIBUNWIND_ARM_EHABI) + // If there is ARM EHABI unwind info, look there next. + if (sects.arm_section != 0 && this->getInfoFromEHABISection(pc, sects)) + return; +#endif + } + +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + // There is no static unwind info for this pc. Look to see if an FDE was + // dynamically registered for it. + pint_t cachedFDE = DwarfFDECache::findFDE(DwarfFDECache::kSearchAll, + pc); + if (cachedFDE != 0) { + typename CFI_Parser::FDE_Info fdeInfo; + typename CFI_Parser::CIE_Info cieInfo; + if (!CFI_Parser::decodeFDE(_addressSpace, cachedFDE, &fdeInfo, &cieInfo)) + if (getInfoFromFdeCie(fdeInfo, cieInfo, pc, 0)) + return; + } + + // Lastly, ask AddressSpace object about platform specific ways to locate + // other FDEs. + pint_t fde; + if (_addressSpace.findOtherFDE(pc, fde)) { + typename CFI_Parser::FDE_Info fdeInfo; + typename CFI_Parser::CIE_Info cieInfo; + if (!CFI_Parser::decodeFDE(_addressSpace, fde, &fdeInfo, &cieInfo)) { + // Double check this FDE is for a function that includes the pc. + if ((fdeInfo.pcStart <= pc) && (pc < fdeInfo.pcEnd)) + if (getInfoFromFdeCie(fdeInfo, cieInfo, pc, 0)) + return; + } + } +#endif // #if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + +#if defined(_LIBUNWIND_TARGET_LINUX) && defined(_LIBUNWIND_TARGET_AARCH64) + if (setInfoForSigReturn()) + return; +#endif + + // no unwind info, flag that we can't reliably unwind + _unwindInfoMissing = true; +} + +#if defined(_LIBUNWIND_TARGET_LINUX) && defined(_LIBUNWIND_TARGET_AARCH64) +template +bool UnwindCursor::setInfoForSigReturn(Registers_arm64 &) { + // Look for the sigreturn trampoline. The trampoline's body is two + // specific instructions (see below). Typically the trampoline comes from the + // vDSO[1] (i.e. the __kernel_rt_sigreturn function). A libc might provide its + // own restorer function, though, or user-mode QEMU might write a trampoline + // onto the stack. + // + // This special code path is a fallback that is only used if the trampoline + // lacks proper (e.g. DWARF) unwind info. On AArch64, a new DWARF register + // constant for the PC needs to be defined before DWARF can handle a signal + // trampoline. This code may segfault if the target PC is unreadable, e.g.: + // - The PC points at a function compiled without unwind info, and which is + // part of an execute-only mapping (e.g. using -Wl,--execute-only). + // - The PC is invalid and happens to point to unreadable or unmapped memory. + // + // [1] https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/vdso/sigreturn.S + const pint_t pc = static_cast(this->getReg(UNW_REG_IP)); + // Look for instructions: mov x8, #0x8b; svc #0x0 + if (_addressSpace.get32(pc) == 0xd2801168 && + _addressSpace.get32(pc + 4) == 0xd4000001) { + _info = {}; + _isSigReturn = true; + return true; + } + return false; +} + +template +int UnwindCursor::stepThroughSigReturn(Registers_arm64 &) { + // In the signal trampoline frame, sp points to an rt_sigframe[1], which is: + // - 128-byte siginfo struct + // - ucontext struct: + // - 8-byte long (uc_flags) + // - 8-byte pointer (uc_link) + // - 24-byte stack_t + // - 128-byte signal set + // - 8 bytes of padding because sigcontext has 16-byte alignment + // - sigcontext/mcontext_t + // [1] https://github.com/torvalds/linux/blob/master/arch/arm64/kernel/signal.c + const pint_t kOffsetSpToSigcontext = (128 + 8 + 8 + 24 + 128 + 8); // 304 + + // Offsets from sigcontext to each register. + const pint_t kOffsetGprs = 8; // offset to "__u64 regs[31]" field + const pint_t kOffsetSp = 256; // offset to "__u64 sp" field + const pint_t kOffsetPc = 264; // offset to "__u64 pc" field + + pint_t sigctx = _registers.getSP() + kOffsetSpToSigcontext; + + for (int i = 0; i <= 30; ++i) { + uint64_t value = _addressSpace.get64(sigctx + kOffsetGprs + + static_cast(i * 8)); + _registers.setRegister(UNW_AARCH64_X0 + i, value); + } + _registers.setSP(_addressSpace.get64(sigctx + kOffsetSp)); + _registers.setIP(_addressSpace.get64(sigctx + kOffsetPc)); + _isSignalFrame = true; + return UNW_STEP_SUCCESS; +} +#endif // defined(_LIBUNWIND_TARGET_LINUX) && defined(_LIBUNWIND_TARGET_AARCH64) + +template +int UnwindCursor::step() { + // Bottom of stack is defined is when unwind info cannot be found. + if (_unwindInfoMissing) + return UNW_STEP_END; + + // Use unwinding info to modify register set as if function returned. + int result; +#if defined(_LIBUNWIND_TARGET_LINUX) && defined(_LIBUNWIND_TARGET_AARCH64) + if (_isSigReturn) { + result = this->stepThroughSigReturn(); + } else +#endif + { +#if defined(_LIBUNWIND_SUPPORT_COMPACT_UNWIND) + result = this->stepWithCompactEncoding(); +#elif defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) + result = this->stepWithSEHData(); +#elif defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + result = this->stepWithDwarfFDE(); +#elif defined(_LIBUNWIND_ARM_EHABI) + result = this->stepWithEHABI(); +#else + #error Need _LIBUNWIND_SUPPORT_COMPACT_UNWIND or \ + _LIBUNWIND_SUPPORT_SEH_UNWIND or \ + _LIBUNWIND_SUPPORT_DWARF_UNWIND or \ + _LIBUNWIND_ARM_EHABI +#endif + } + + // update info based on new PC + if (result == UNW_STEP_SUCCESS) { + this->setInfoBasedOnIPRegister(true); + if (_unwindInfoMissing) + return UNW_STEP_END; + } + + return result; +} + +template +void UnwindCursor::getInfo(unw_proc_info_t *info) { + if (_unwindInfoMissing) + memset(info, 0, sizeof(*info)); + else + *info = _info; +} + +template +bool UnwindCursor::getFunctionName(char *buf, size_t bufLen, + unw_word_t *offset) { + return _addressSpace.findFunctionName((pint_t)this->getReg(UNW_REG_IP), + buf, bufLen, offset); +} + +#if defined(_LIBUNWIND_USE_CET) +extern "C" void *__libunwind_cet_get_registers(unw_cursor_t *cursor) { + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + return co->get_registers(); +} +#endif +} // namespace libunwind + +#endif // __UNWINDCURSOR_HPP__ diff --git a/shared/sentry/external/crashpad/libunwind/src/UnwindLevel1-gcc-ext.c b/shared/sentry/external/crashpad/libunwind/src/UnwindLevel1-gcc-ext.c new file mode 100644 index 000000000..4172540ef --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/src/UnwindLevel1-gcc-ext.c @@ -0,0 +1,317 @@ +//===--------------------- UnwindLevel1-gcc-ext.c -------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Implements gcc extensions to the C++ ABI Exception Handling Level 1. +// +//===----------------------------------------------------------------------===// + +#include +#include +#include +#include +#include +#include + +#include "config.h" +#include "libunwind_ext.h" +#include "libunwind.h" +#include "Unwind-EHABI.h" +#include "unwind.h" + +#if defined(_LIBUNWIND_BUILD_ZERO_COST_APIS) + +#if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) +#define PRIVATE_1 private_[0] +#elif defined(_LIBUNWIND_ARM_EHABI) +#define PRIVATE_1 unwinder_cache.reserved1 +#else +#define PRIVATE_1 private_1 +#endif + +/// Called by __cxa_rethrow(). +_LIBUNWIND_EXPORT _Unwind_Reason_Code +_Unwind_Resume_or_Rethrow(_Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API( + "_Unwind_Resume_or_Rethrow(ex_obj=%p), private_1=%" PRIdPTR, + (void *)exception_object, (intptr_t)exception_object->PRIVATE_1); + + // If this is non-forced and a stopping place was found, then this is a + // re-throw. + // Call _Unwind_RaiseException() as if this was a new exception + if (exception_object->PRIVATE_1 == 0) { + return _Unwind_RaiseException(exception_object); + // Will return if there is no catch clause, so that __cxa_rethrow can call + // std::terminate(). + } + + // Call through to _Unwind_Resume() which distiguishes between forced and + // regular exceptions. + _Unwind_Resume(exception_object); + _LIBUNWIND_ABORT("_Unwind_Resume_or_Rethrow() called _Unwind_RaiseException()" + " which unexpectedly returned"); +} + +/// Called by personality handler during phase 2 to get base address for data +/// relative encodings. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetDataRelBase(struct _Unwind_Context *context) { + (void)context; + _LIBUNWIND_TRACE_API("_Unwind_GetDataRelBase(context=%p)", (void *)context); + _LIBUNWIND_ABORT("_Unwind_GetDataRelBase() not implemented"); +} + + +/// Called by personality handler during phase 2 to get base address for text +/// relative encodings. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetTextRelBase(struct _Unwind_Context *context) { + (void)context; + _LIBUNWIND_TRACE_API("_Unwind_GetTextRelBase(context=%p)", (void *)context); + _LIBUNWIND_ABORT("_Unwind_GetTextRelBase() not implemented"); +} + + +/// Scans unwind information to find the function that contains the +/// specified code address "pc". +_LIBUNWIND_EXPORT void *_Unwind_FindEnclosingFunction(void *pc) { + _LIBUNWIND_TRACE_API("_Unwind_FindEnclosingFunction(pc=%p)", pc); + // This is slow, but works. + // We create an unwind cursor then alter the IP to be pc + unw_cursor_t cursor; + unw_context_t uc; + unw_proc_info_t info; + __unw_getcontext(&uc); + __unw_init_local(&cursor, &uc); + __unw_set_reg(&cursor, UNW_REG_IP, (unw_word_t)(intptr_t)pc); + if (__unw_get_proc_info(&cursor, &info) == UNW_ESUCCESS) + return (void *)(intptr_t) info.start_ip; + else + return NULL; +} + +/// Walk every frame and call trace function at each one. If trace function +/// returns anything other than _URC_NO_REASON, then walk is terminated. +_LIBUNWIND_EXPORT _Unwind_Reason_Code +_Unwind_Backtrace(_Unwind_Trace_Fn callback, void *ref) { + unw_cursor_t cursor; + unw_context_t uc; + __unw_getcontext(&uc); + __unw_init_local(&cursor, &uc); + + _LIBUNWIND_TRACE_API("_Unwind_Backtrace(callback=%p)", + (void *)(uintptr_t)callback); + +#if defined(_LIBUNWIND_ARM_EHABI) + // Create a mock exception object for force unwinding. + _Unwind_Exception ex; + memset(&ex, '\0', sizeof(ex)); + strcpy((char *)&ex.exception_class, "CLNGUNW"); +#endif + + // walk each frame + while (true) { + _Unwind_Reason_Code result; + +#if !defined(_LIBUNWIND_ARM_EHABI) + // ask libunwind to get next frame (skip over first frame which is + // _Unwind_Backtrace()) + if (__unw_step(&cursor) <= 0) { + _LIBUNWIND_TRACE_UNWINDING(" _backtrace: ended because cursor reached " + "bottom of stack, returning %d", + _URC_END_OF_STACK); + return _URC_END_OF_STACK; + } +#else + // Get the information for this frame. + unw_proc_info_t frameInfo; + if (__unw_get_proc_info(&cursor, &frameInfo) != UNW_ESUCCESS) { + return _URC_END_OF_STACK; + } + + // Update the pr_cache in the mock exception object. + const uint32_t* unwindInfo = (uint32_t *) frameInfo.unwind_info; + ex.pr_cache.fnstart = frameInfo.start_ip; + ex.pr_cache.ehtp = (_Unwind_EHT_Header *) unwindInfo; + ex.pr_cache.additional= frameInfo.flags; + + struct _Unwind_Context *context = (struct _Unwind_Context *)&cursor; + // Get and call the personality function to unwind the frame. + _Unwind_Personality_Fn handler = (_Unwind_Personality_Fn)frameInfo.handler; + if (handler == NULL) { + return _URC_END_OF_STACK; + } + if (handler(_US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND, &ex, context) != + _URC_CONTINUE_UNWIND) { + return _URC_END_OF_STACK; + } +#endif // defined(_LIBUNWIND_ARM_EHABI) + + // debugging + if (_LIBUNWIND_TRACING_UNWINDING) { + char functionName[512]; + unw_proc_info_t frame; + unw_word_t offset; + __unw_get_proc_name(&cursor, functionName, 512, &offset); + __unw_get_proc_info(&cursor, &frame); + _LIBUNWIND_TRACE_UNWINDING( + " _backtrace: start_ip=0x%" PRIxPTR ", func=%s, lsda=0x%" PRIxPTR ", context=%p", + frame.start_ip, functionName, frame.lsda, + (void *)&cursor); + } + + // call trace function with this frame + result = (*callback)((struct _Unwind_Context *)(&cursor), ref); + if (result != _URC_NO_REASON) { + _LIBUNWIND_TRACE_UNWINDING( + " _backtrace: ended because callback returned %d", result); + return result; + } + } +} + + +/// Find DWARF unwind info for an address 'pc' in some function. +_LIBUNWIND_EXPORT const void *_Unwind_Find_FDE(const void *pc, + struct dwarf_eh_bases *bases) { + // This is slow, but works. + // We create an unwind cursor then alter the IP to be pc + unw_cursor_t cursor; + unw_context_t uc; + unw_proc_info_t info; + __unw_getcontext(&uc); + __unw_init_local(&cursor, &uc); + __unw_set_reg(&cursor, UNW_REG_IP, (unw_word_t)(intptr_t)pc); + __unw_get_proc_info(&cursor, &info); + bases->tbase = (uintptr_t)info.extra; + bases->dbase = 0; // dbase not used on Mac OS X + bases->func = (uintptr_t)info.start_ip; + _LIBUNWIND_TRACE_API("_Unwind_Find_FDE(pc=%p) => %p", pc, + (void *)(intptr_t) info.unwind_info); + return (void *)(intptr_t) info.unwind_info; +} + +/// Returns the CFA (call frame area, or stack pointer at start of function) +/// for the current context. +_LIBUNWIND_EXPORT uintptr_t _Unwind_GetCFA(struct _Unwind_Context *context) { + unw_cursor_t *cursor = (unw_cursor_t *)context; + unw_word_t result; + __unw_get_reg(cursor, UNW_REG_SP, &result); + _LIBUNWIND_TRACE_API("_Unwind_GetCFA(context=%p) => 0x%" PRIxPTR, + (void *)context, result); + return (uintptr_t)result; +} + + +/// Called by personality handler during phase 2 to get instruction pointer. +/// ipBefore is a boolean that says if IP is already adjusted to be the call +/// site address. Normally IP is the return address. +_LIBUNWIND_EXPORT uintptr_t _Unwind_GetIPInfo(struct _Unwind_Context *context, + int *ipBefore) { + _LIBUNWIND_TRACE_API("_Unwind_GetIPInfo(context=%p)", (void *)context); + int isSignalFrame = __unw_is_signal_frame((unw_cursor_t *)context); + // Negative means some kind of error (probably UNW_ENOINFO), but we have no + // good way to report that, and this maintains backward compatibility with the + // implementation that hard-coded zero in every case, even signal frames. + if (isSignalFrame <= 0) + *ipBefore = 0; + else + *ipBefore = 1; + return _Unwind_GetIP(context); +} + +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + +/// Called by programs with dynamic code generators that want +/// to register a dynamically generated FDE. +/// This function has existed on Mac OS X since 10.4, but +/// was broken until 10.6. +_LIBUNWIND_EXPORT void __register_frame(const void *fde) { + _LIBUNWIND_TRACE_API("__register_frame(%p)", fde); + __unw_add_dynamic_fde((unw_word_t)(uintptr_t)fde); +} + + +/// Called by programs with dynamic code generators that want +/// to unregister a dynamically generated FDE. +/// This function has existed on Mac OS X since 10.4, but +/// was broken until 10.6. +_LIBUNWIND_EXPORT void __deregister_frame(const void *fde) { + _LIBUNWIND_TRACE_API("__deregister_frame(%p)", fde); + __unw_remove_dynamic_fde((unw_word_t)(uintptr_t)fde); +} + + +// The following register/deregister functions are gcc extensions. +// They have existed on Mac OS X, but have never worked because Mac OS X +// before 10.6 used keymgr to track known FDEs, but these functions +// never got updated to use keymgr. +// For now, we implement these as do-nothing functions to keep any existing +// applications working. We also add the not in 10.6 symbol so that nwe +// application won't be able to use them. + +#if defined(_LIBUNWIND_SUPPORT_FRAME_APIS) +_LIBUNWIND_EXPORT void __register_frame_info_bases(const void *fde, void *ob, + void *tb, void *db) { + (void)fde; + (void)ob; + (void)tb; + (void)db; + _LIBUNWIND_TRACE_API("__register_frame_info_bases(%p,%p, %p, %p)", + fde, ob, tb, db); + // do nothing, this function never worked in Mac OS X +} + +_LIBUNWIND_EXPORT void __register_frame_info(const void *fde, void *ob) { + (void)fde; + (void)ob; + _LIBUNWIND_TRACE_API("__register_frame_info(%p, %p)", fde, ob); + // do nothing, this function never worked in Mac OS X +} + +_LIBUNWIND_EXPORT void __register_frame_info_table_bases(const void *fde, + void *ob, void *tb, + void *db) { + (void)fde; + (void)ob; + (void)tb; + (void)db; + _LIBUNWIND_TRACE_API("__register_frame_info_table_bases" + "(%p,%p, %p, %p)", fde, ob, tb, db); + // do nothing, this function never worked in Mac OS X +} + +_LIBUNWIND_EXPORT void __register_frame_info_table(const void *fde, void *ob) { + (void)fde; + (void)ob; + _LIBUNWIND_TRACE_API("__register_frame_info_table(%p, %p)", fde, ob); + // do nothing, this function never worked in Mac OS X +} + +_LIBUNWIND_EXPORT void __register_frame_table(const void *fde) { + (void)fde; + _LIBUNWIND_TRACE_API("__register_frame_table(%p)", fde); + // do nothing, this function never worked in Mac OS X +} + +_LIBUNWIND_EXPORT void *__deregister_frame_info(const void *fde) { + (void)fde; + _LIBUNWIND_TRACE_API("__deregister_frame_info(%p)", fde); + // do nothing, this function never worked in Mac OS X + return NULL; +} + +_LIBUNWIND_EXPORT void *__deregister_frame_info_bases(const void *fde) { + (void)fde; + _LIBUNWIND_TRACE_API("__deregister_frame_info_bases(%p)", fde); + // do nothing, this function never worked in Mac OS X + return NULL; +} +#endif // defined(_LIBUNWIND_SUPPORT_FRAME_APIS) + +#endif // defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) + +#endif // defined(_LIBUNWIND_BUILD_ZERO_COST_APIS) diff --git a/shared/sentry/external/crashpad/libunwind/src/UnwindLevel1.c b/shared/sentry/external/crashpad/libunwind/src/UnwindLevel1.c new file mode 100644 index 000000000..9203ac771 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/src/UnwindLevel1.c @@ -0,0 +1,561 @@ +//===------------------------- UnwindLevel1.c -----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Implements C++ ABI Exception Handling Level 1 as documented at: +// https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html +// using libunwind +// +//===----------------------------------------------------------------------===// + +// ARM EHABI does not specify _Unwind_{Get,Set}{GR,IP}(). Thus, we are +// defining inline functions to delegate the function calls to +// _Unwind_VRS_{Get,Set}(). However, some applications might declare the +// function protetype directly (instead of including ), thus we need +// to export these functions from libunwind.so as well. +#define _LIBUNWIND_UNWIND_LEVEL1_EXTERNAL_LINKAGE 1 + +#include +#include +#include +#include +#include +#include + +#include "cet_unwind.h" +#include "config.h" +#include "libunwind.h" +#include "libunwind_ext.h" +#include "unwind.h" + +#if !defined(_LIBUNWIND_ARM_EHABI) && !defined(__USING_SJLJ_EXCEPTIONS__) + +#ifndef _LIBUNWIND_SUPPORT_SEH_UNWIND + +// When CET is enabled, each "call" instruction will push return address to +// CET shadow stack, each "ret" instruction will pop current CET shadow stack +// top and compare it with target address which program will return. +// In exception handing, some stack frames will be skipped before jumping to +// landing pad and we must adjust CET shadow stack accordingly. +// _LIBUNWIND_POP_CET_SSP is used to adjust CET shadow stack pointer and we +// directly jump to __libunwind_Registerts_x86/x86_64_jumpto instead of using +// a regular function call to avoid pushing to CET shadow stack again. +#if !defined(_LIBUNWIND_USE_CET) +#define __unw_phase2_resume(cursor, fn) __unw_resume((cursor)) +#elif defined(_LIBUNWIND_TARGET_I386) +#define __unw_phase2_resume(cursor, fn) \ + do { \ + _LIBUNWIND_POP_CET_SSP((fn)); \ + void *cetRegContext = __libunwind_cet_get_registers((cursor)); \ + void *cetJumpAddress = __libunwind_cet_get_jump_target(); \ + __asm__ volatile("push %%edi\n\t" \ + "sub $4, %%esp\n\t" \ + "jmp *%%edx\n\t" :: "D"(cetRegContext), \ + "d"(cetJumpAddress)); \ + } while (0) +#elif defined(_LIBUNWIND_TARGET_X86_64) +#define __unw_phase2_resume(cursor, fn) \ + do { \ + _LIBUNWIND_POP_CET_SSP((fn)); \ + void *cetRegContext = __libunwind_cet_get_registers((cursor)); \ + void *cetJumpAddress = __libunwind_cet_get_jump_target(); \ + __asm__ volatile("jmpq *%%rdx\n\t" :: "D"(cetRegContext), \ + "d"(cetJumpAddress)); \ + } while (0) +#endif + +static _Unwind_Reason_Code +unwind_phase1(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *exception_object) { + __unw_init_local(cursor, uc); + + // Walk each frame looking for a place to stop. + while (true) { + // Ask libunwind to get next frame (skip over first which is + // _Unwind_RaiseException). + int stepResult = __unw_step(cursor); + if (stepResult == 0) { + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): __unw_step() reached " + "bottom => _URC_END_OF_STACK", + (void *)exception_object); + return _URC_END_OF_STACK; + } else if (stepResult < 0) { + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): __unw_step failed => " + "_URC_FATAL_PHASE1_ERROR", + (void *)exception_object); + return _URC_FATAL_PHASE1_ERROR; + } + + // See if frame has code to run (has personality routine). + unw_proc_info_t frameInfo; + unw_word_t sp; + if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) { + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): __unw_get_proc_info " + "failed => _URC_FATAL_PHASE1_ERROR", + (void *)exception_object); + return _URC_FATAL_PHASE1_ERROR; + } + +#ifndef NDEBUG + // When tracing, print state information. + if (_LIBUNWIND_TRACING_UNWINDING) { + char functionBuf[512]; + const char *functionName = functionBuf; + unw_word_t offset; + if ((__unw_get_proc_name(cursor, functionBuf, sizeof(functionBuf), + &offset) != UNW_ESUCCESS) || + (frameInfo.start_ip + offset > frameInfo.end_ip)) + functionName = ".anonymous."; + unw_word_t pc; + __unw_get_reg(cursor, UNW_REG_IP, &pc); + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): pc=0x%" PRIxPTR ", start_ip=0x%" PRIxPTR + ", func=%s, lsda=0x%" PRIxPTR ", personality=0x%" PRIxPTR "", + (void *)exception_object, pc, frameInfo.start_ip, functionName, + frameInfo.lsda, frameInfo.handler); + } +#endif + + // If there is a personality routine, ask it if it will want to stop at + // this frame. + if (frameInfo.handler != 0) { + _Unwind_Personality_Fn p = + (_Unwind_Personality_Fn)(uintptr_t)(frameInfo.handler); + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): calling personality function %p", + (void *)exception_object, (void *)(uintptr_t)p); + _Unwind_Reason_Code personalityResult = + (*p)(1, _UA_SEARCH_PHASE, exception_object->exception_class, + exception_object, (struct _Unwind_Context *)(cursor)); + switch (personalityResult) { + case _URC_HANDLER_FOUND: + // found a catch clause or locals that need destructing in this frame + // stop search and remember stack pointer at the frame + __unw_get_reg(cursor, UNW_REG_SP, &sp); + exception_object->private_2 = (uintptr_t)sp; + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): _URC_HANDLER_FOUND", + (void *)exception_object); + return _URC_NO_REASON; + + case _URC_CONTINUE_UNWIND: + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): _URC_CONTINUE_UNWIND", + (void *)exception_object); + // continue unwinding + break; + + default: + // something went wrong + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase1(ex_ojb=%p): _URC_FATAL_PHASE1_ERROR", + (void *)exception_object); + return _URC_FATAL_PHASE1_ERROR; + } + } + } + return _URC_NO_REASON; +} + + +static _Unwind_Reason_Code +unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *exception_object) { + __unw_init_local(cursor, uc); + + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p)", + (void *)exception_object); + + // uc is initialized by __unw_getcontext in the parent frame. The first stack + // frame walked is unwind_phase2. + unsigned framesWalked = 1; + // Walk each frame until we reach where search phase said to stop. + while (true) { + + // Ask libunwind to get next frame (skip over first which is + // _Unwind_RaiseException). + int stepResult = __unw_step(cursor); + if (stepResult == 0) { + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2(ex_ojb=%p): __unw_step() reached " + "bottom => _URC_END_OF_STACK", + (void *)exception_object); + return _URC_END_OF_STACK; + } else if (stepResult < 0) { + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2(ex_ojb=%p): __unw_step failed => " + "_URC_FATAL_PHASE1_ERROR", + (void *)exception_object); + return _URC_FATAL_PHASE2_ERROR; + } + + // Get info about this frame. + unw_word_t sp; + unw_proc_info_t frameInfo; + __unw_get_reg(cursor, UNW_REG_SP, &sp); + if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) { + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2(ex_ojb=%p): __unw_get_proc_info " + "failed => _URC_FATAL_PHASE1_ERROR", + (void *)exception_object); + return _URC_FATAL_PHASE2_ERROR; + } + +#ifndef NDEBUG + // When tracing, print state information. + if (_LIBUNWIND_TRACING_UNWINDING) { + char functionBuf[512]; + const char *functionName = functionBuf; + unw_word_t offset; + if ((__unw_get_proc_name(cursor, functionBuf, sizeof(functionBuf), + &offset) != UNW_ESUCCESS) || + (frameInfo.start_ip + offset > frameInfo.end_ip)) + functionName = ".anonymous."; + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): start_ip=0x%" PRIxPTR + ", func=%s, sp=0x%" PRIxPTR ", lsda=0x%" PRIxPTR + ", personality=0x%" PRIxPTR, + (void *)exception_object, frameInfo.start_ip, + functionName, sp, frameInfo.lsda, + frameInfo.handler); + } +#endif + + ++framesWalked; + // If there is a personality routine, tell it we are unwinding. + if (frameInfo.handler != 0) { + _Unwind_Personality_Fn p = + (_Unwind_Personality_Fn)(uintptr_t)(frameInfo.handler); + _Unwind_Action action = _UA_CLEANUP_PHASE; + if (sp == exception_object->private_2) { + // Tell personality this was the frame it marked in phase 1. + action = (_Unwind_Action)(_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME); + } + _Unwind_Reason_Code personalityResult = + (*p)(1, action, exception_object->exception_class, exception_object, + (struct _Unwind_Context *)(cursor)); + switch (personalityResult) { + case _URC_CONTINUE_UNWIND: + // Continue unwinding + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2(ex_ojb=%p): _URC_CONTINUE_UNWIND", + (void *)exception_object); + if (sp == exception_object->private_2) { + // Phase 1 said we would stop at this frame, but we did not... + _LIBUNWIND_ABORT("during phase1 personality function said it would " + "stop here, but now in phase2 it did not stop here"); + } + break; + case _URC_INSTALL_CONTEXT: + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2(ex_ojb=%p): _URC_INSTALL_CONTEXT", + (void *)exception_object); + // Personality routine says to transfer control to landing pad. + // We may get control back if landing pad calls _Unwind_Resume(). + if (_LIBUNWIND_TRACING_UNWINDING) { + unw_word_t pc; + __unw_get_reg(cursor, UNW_REG_IP, &pc); + __unw_get_reg(cursor, UNW_REG_SP, &sp); + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): re-entering " + "user code with ip=0x%" PRIxPTR + ", sp=0x%" PRIxPTR, + (void *)exception_object, pc, sp); + } + + __unw_phase2_resume(cursor, framesWalked); + // __unw_phase2_resume() only returns if there was an error. + return _URC_FATAL_PHASE2_ERROR; + default: + // Personality routine returned an unknown result code. + _LIBUNWIND_DEBUG_LOG("personality function returned unknown result %d", + personalityResult); + return _URC_FATAL_PHASE2_ERROR; + } + } + } + + // Clean up phase did not resume at the frame that the search phase + // said it would... + return _URC_FATAL_PHASE2_ERROR; +} + +static _Unwind_Reason_Code +unwind_phase2_forced(unw_context_t *uc, unw_cursor_t *cursor, + _Unwind_Exception *exception_object, + _Unwind_Stop_Fn stop, void *stop_parameter) { + __unw_init_local(cursor, uc); + + // uc is initialized by __unw_getcontext in the parent frame. The first stack + // frame walked is unwind_phase2_forced. + unsigned framesWalked = 1; + // Walk each frame until we reach where search phase said to stop + while (__unw_step(cursor) > 0) { + + // Update info about this frame. + unw_proc_info_t frameInfo; + if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) { + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): __unw_step " + "failed => _URC_END_OF_STACK", + (void *)exception_object); + return _URC_FATAL_PHASE2_ERROR; + } + +#ifndef NDEBUG + // When tracing, print state information. + if (_LIBUNWIND_TRACING_UNWINDING) { + char functionBuf[512]; + const char *functionName = functionBuf; + unw_word_t offset; + if ((__unw_get_proc_name(cursor, functionBuf, sizeof(functionBuf), + &offset) != UNW_ESUCCESS) || + (frameInfo.start_ip + offset > frameInfo.end_ip)) + functionName = ".anonymous."; + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2_forced(ex_ojb=%p): start_ip=0x%" PRIxPTR + ", func=%s, lsda=0x%" PRIxPTR ", personality=0x%" PRIxPTR, + (void *)exception_object, frameInfo.start_ip, functionName, + frameInfo.lsda, frameInfo.handler); + } +#endif + + // Call stop function at each frame. + _Unwind_Action action = + (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE); + _Unwind_Reason_Code stopResult = + (*stop)(1, action, exception_object->exception_class, exception_object, + (struct _Unwind_Context *)(cursor), stop_parameter); + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2_forced(ex_ojb=%p): stop function returned %d", + (void *)exception_object, stopResult); + if (stopResult != _URC_NO_REASON) { + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2_forced(ex_ojb=%p): stopped by stop function", + (void *)exception_object); + return _URC_FATAL_PHASE2_ERROR; + } + + ++framesWalked; + // If there is a personality routine, tell it we are unwinding. + if (frameInfo.handler != 0) { + _Unwind_Personality_Fn p = + (_Unwind_Personality_Fn)(intptr_t)(frameInfo.handler); + _LIBUNWIND_TRACE_UNWINDING( + "unwind_phase2_forced(ex_ojb=%p): calling personality function %p", + (void *)exception_object, (void *)(uintptr_t)p); + _Unwind_Reason_Code personalityResult = + (*p)(1, action, exception_object->exception_class, exception_object, + (struct _Unwind_Context *)(cursor)); + switch (personalityResult) { + case _URC_CONTINUE_UNWIND: + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "personality returned " + "_URC_CONTINUE_UNWIND", + (void *)exception_object); + // Destructors called, continue unwinding + break; + case _URC_INSTALL_CONTEXT: + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "personality returned " + "_URC_INSTALL_CONTEXT", + (void *)exception_object); + // We may get control back if landing pad calls _Unwind_Resume(). + __unw_phase2_resume(cursor, framesWalked); + break; + default: + // Personality routine returned an unknown result code. + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " + "personality returned %d, " + "_URC_FATAL_PHASE2_ERROR", + (void *)exception_object, personalityResult); + return _URC_FATAL_PHASE2_ERROR; + } + } + } + + // Call stop function one last time and tell it we've reached the end + // of the stack. + _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop " + "function with _UA_END_OF_STACK", + (void *)exception_object); + _Unwind_Action lastAction = + (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | _UA_END_OF_STACK); + (*stop)(1, lastAction, exception_object->exception_class, exception_object, + (struct _Unwind_Context *)(cursor), stop_parameter); + + // Clean up phase did not resume at the frame that the search phase said it + // would. + return _URC_FATAL_PHASE2_ERROR; +} + + +/// Called by __cxa_throw. Only returns if there is a fatal error. +_LIBUNWIND_EXPORT _Unwind_Reason_Code +_Unwind_RaiseException(_Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_RaiseException(ex_obj=%p)", + (void *)exception_object); + unw_context_t uc; + unw_cursor_t cursor; + __unw_getcontext(&uc); + + // Mark that this is a non-forced unwind, so _Unwind_Resume() + // can do the right thing. + exception_object->private_1 = 0; + exception_object->private_2 = 0; + + // phase 1: the search phase + _Unwind_Reason_Code phase1 = unwind_phase1(&uc, &cursor, exception_object); + if (phase1 != _URC_NO_REASON) + return phase1; + + // phase 2: the clean up phase + return unwind_phase2(&uc, &cursor, exception_object); +} + + + +/// When _Unwind_RaiseException() is in phase2, it hands control +/// to the personality function at each frame. The personality +/// may force a jump to a landing pad in that function, the landing +/// pad code may then call _Unwind_Resume() to continue with the +/// unwinding. Note: the call to _Unwind_Resume() is from compiler +/// geneated user code. All other _Unwind_* routines are called +/// by the C++ runtime __cxa_* routines. +/// +/// Note: re-throwing an exception (as opposed to continuing the unwind) +/// is implemented by having the code call __cxa_rethrow() which +/// in turn calls _Unwind_Resume_or_Rethrow(). +_LIBUNWIND_EXPORT void +_Unwind_Resume(_Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_Resume(ex_obj=%p)", (void *)exception_object); + unw_context_t uc; + unw_cursor_t cursor; + __unw_getcontext(&uc); + + if (exception_object->private_1 != 0) + unwind_phase2_forced(&uc, &cursor, exception_object, + (_Unwind_Stop_Fn) exception_object->private_1, + (void *)exception_object->private_2); + else + unwind_phase2(&uc, &cursor, exception_object); + + // Clients assume _Unwind_Resume() does not return, so all we can do is abort. + _LIBUNWIND_ABORT("_Unwind_Resume() can't return"); +} + + + +/// Not used by C++. +/// Unwinds stack, calling "stop" function at each frame. +/// Could be used to implement longjmp(). +_LIBUNWIND_EXPORT _Unwind_Reason_Code +_Unwind_ForcedUnwind(_Unwind_Exception *exception_object, + _Unwind_Stop_Fn stop, void *stop_parameter) { + _LIBUNWIND_TRACE_API("_Unwind_ForcedUnwind(ex_obj=%p, stop=%p)", + (void *)exception_object, (void *)(uintptr_t)stop); + unw_context_t uc; + unw_cursor_t cursor; + __unw_getcontext(&uc); + + // Mark that this is a forced unwind, so _Unwind_Resume() can do + // the right thing. + exception_object->private_1 = (uintptr_t) stop; + exception_object->private_2 = (uintptr_t) stop_parameter; + + // do it + return unwind_phase2_forced(&uc, &cursor, exception_object, stop, stop_parameter); +} + + +/// Called by personality handler during phase 2 to get LSDA for current frame. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) { + unw_cursor_t *cursor = (unw_cursor_t *)context; + unw_proc_info_t frameInfo; + uintptr_t result = 0; + if (__unw_get_proc_info(cursor, &frameInfo) == UNW_ESUCCESS) + result = (uintptr_t)frameInfo.lsda; + _LIBUNWIND_TRACE_API( + "_Unwind_GetLanguageSpecificData(context=%p) => 0x%" PRIxPTR, + (void *)context, result); + if (result != 0) { + if (*((uint8_t *)result) != 0xFF) + _LIBUNWIND_DEBUG_LOG("lsda at 0x%" PRIxPTR " does not start with 0xFF", + result); + } + return result; +} + + +/// Called by personality handler during phase 2 to find the start of the +/// function. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetRegionStart(struct _Unwind_Context *context) { + unw_cursor_t *cursor = (unw_cursor_t *)context; + unw_proc_info_t frameInfo; + uintptr_t result = 0; + if (__unw_get_proc_info(cursor, &frameInfo) == UNW_ESUCCESS) + result = (uintptr_t)frameInfo.start_ip; + _LIBUNWIND_TRACE_API("_Unwind_GetRegionStart(context=%p) => 0x%" PRIxPTR, + (void *)context, result); + return result; +} + +#endif // !_LIBUNWIND_SUPPORT_SEH_UNWIND + +/// Called by personality handler during phase 2 if a foreign exception +// is caught. +_LIBUNWIND_EXPORT void +_Unwind_DeleteException(_Unwind_Exception *exception_object) { + _LIBUNWIND_TRACE_API("_Unwind_DeleteException(ex_obj=%p)", + (void *)exception_object); + if (exception_object->exception_cleanup != NULL) + (*exception_object->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT, + exception_object); +} + +/// Called by personality handler during phase 2 to get register values. +_LIBUNWIND_EXPORT uintptr_t +_Unwind_GetGR(struct _Unwind_Context *context, int index) { + unw_cursor_t *cursor = (unw_cursor_t *)context; + unw_word_t result; + __unw_get_reg(cursor, index, &result); + _LIBUNWIND_TRACE_API("_Unwind_GetGR(context=%p, reg=%d) => 0x%" PRIxPTR, + (void *)context, index, result); + return (uintptr_t)result; +} + +/// Called by personality handler during phase 2 to alter register values. +_LIBUNWIND_EXPORT void _Unwind_SetGR(struct _Unwind_Context *context, int index, + uintptr_t value) { + _LIBUNWIND_TRACE_API("_Unwind_SetGR(context=%p, reg=%d, value=0x%0" PRIxPTR + ")", + (void *)context, index, value); + unw_cursor_t *cursor = (unw_cursor_t *)context; + __unw_set_reg(cursor, index, value); +} + +/// Called by personality handler during phase 2 to get instruction pointer. +_LIBUNWIND_EXPORT uintptr_t _Unwind_GetIP(struct _Unwind_Context *context) { + unw_cursor_t *cursor = (unw_cursor_t *)context; + unw_word_t result; + __unw_get_reg(cursor, UNW_REG_IP, &result); + _LIBUNWIND_TRACE_API("_Unwind_GetIP(context=%p) => 0x%" PRIxPTR, + (void *)context, result); + return (uintptr_t)result; +} + +/// Called by personality handler during phase 2 to alter instruction pointer, +/// such as setting where the landing pad is, so _Unwind_Resume() will +/// start executing in the landing pad. +_LIBUNWIND_EXPORT void _Unwind_SetIP(struct _Unwind_Context *context, + uintptr_t value) { + _LIBUNWIND_TRACE_API("_Unwind_SetIP(context=%p, value=0x%0" PRIxPTR ")", + (void *)context, value); + unw_cursor_t *cursor = (unw_cursor_t *)context; + __unw_set_reg(cursor, UNW_REG_IP, value); +} + +#endif // !defined(_LIBUNWIND_ARM_EHABI) && !defined(__USING_SJLJ_EXCEPTIONS__) diff --git a/shared/sentry/external/crashpad/libunwind/src/UnwindRegistersRestore.S b/shared/sentry/external/crashpad/libunwind/src/UnwindRegistersRestore.S new file mode 100644 index 000000000..955ec3355 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/src/UnwindRegistersRestore.S @@ -0,0 +1,1167 @@ +//===-------------------- UnwindRegistersRestore.S ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "assembly.h" + + .text + +#if !defined(__USING_SJLJ_EXCEPTIONS__) + +#if defined(__i386__) +DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_x86_jumpto) +# +# extern "C" void __libunwind_Registers_x86_jumpto(Registers_x86 *); +# +# On entry: +# + + +# +-----------------------+ +# + thread_state pointer + +# +-----------------------+ +# + return address + +# +-----------------------+ <-- SP +# + + + + _LIBUNWIND_CET_ENDBR + movl 4(%esp), %eax + # set up eax and ret on new stack location + movl 28(%eax), %edx # edx holds new stack pointer + subl $8,%edx + movl %edx, 28(%eax) + movl 0(%eax), %ebx + movl %ebx, 0(%edx) + movl 40(%eax), %ebx + movl %ebx, 4(%edx) + # we now have ret and eax pushed onto where new stack will be + # restore all registers + movl 4(%eax), %ebx + movl 8(%eax), %ecx + movl 12(%eax), %edx + movl 16(%eax), %edi + movl 20(%eax), %esi + movl 24(%eax), %ebp + movl 28(%eax), %esp + # skip ss + # skip eflags + pop %eax # eax was already pushed on new stack + pop %ecx + jmp *%ecx + # skip cs + # skip ds + # skip es + # skip fs + # skip gs + +#elif defined(__x86_64__) + +DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_x86_64_jumpto) +# +# extern "C" void __libunwind_Registers_x86_64_jumpto(Registers_x86_64 *); +# +#if defined(_WIN64) +# On entry, thread_state pointer is in rcx; move it into rdi +# to share restore code below. Since this routine restores and +# overwrites all registers, we can use the same registers for +# pointers and temporaries as on unix even though win64 normally +# mustn't clobber some of them. + movq %rcx, %rdi +#else +# On entry, thread_state pointer is in rdi +#endif + + _LIBUNWIND_CET_ENDBR + movq 56(%rdi), %rax # rax holds new stack pointer + subq $16, %rax + movq %rax, 56(%rdi) + movq 32(%rdi), %rbx # store new rdi on new stack + movq %rbx, 0(%rax) + movq 128(%rdi), %rbx # store new rip on new stack + movq %rbx, 8(%rax) + # restore all registers + movq 0(%rdi), %rax + movq 8(%rdi), %rbx + movq 16(%rdi), %rcx + movq 24(%rdi), %rdx + # restore rdi later + movq 40(%rdi), %rsi + movq 48(%rdi), %rbp + # restore rsp later + movq 64(%rdi), %r8 + movq 72(%rdi), %r9 + movq 80(%rdi), %r10 + movq 88(%rdi), %r11 + movq 96(%rdi), %r12 + movq 104(%rdi), %r13 + movq 112(%rdi), %r14 + movq 120(%rdi), %r15 + # skip rflags + # skip cs + # skip fs + # skip gs + +#if defined(_WIN64) + movdqu 176(%rdi),%xmm0 + movdqu 192(%rdi),%xmm1 + movdqu 208(%rdi),%xmm2 + movdqu 224(%rdi),%xmm3 + movdqu 240(%rdi),%xmm4 + movdqu 256(%rdi),%xmm5 + movdqu 272(%rdi),%xmm6 + movdqu 288(%rdi),%xmm7 + movdqu 304(%rdi),%xmm8 + movdqu 320(%rdi),%xmm9 + movdqu 336(%rdi),%xmm10 + movdqu 352(%rdi),%xmm11 + movdqu 368(%rdi),%xmm12 + movdqu 384(%rdi),%xmm13 + movdqu 400(%rdi),%xmm14 + movdqu 416(%rdi),%xmm15 +#endif + movq 56(%rdi), %rsp # cut back rsp to new location + pop %rdi # rdi was saved here earlier + pop %rcx + jmpq *%rcx + + +#elif defined(__powerpc64__) + +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind15Registers_ppc646jumptoEv) +// +// void libunwind::Registers_ppc64::jumpto() +// +// On entry: +// thread_state pointer is in r3 +// + +// load register (GPR) +#define PPC64_LR(n) \ + ld n, (8 * (n + 2))(3) + + // restore integral registers + // skip r0 for now + // skip r1 for now + PPC64_LR(2) + // skip r3 for now + // skip r4 for now + // skip r5 for now + PPC64_LR(6) + PPC64_LR(7) + PPC64_LR(8) + PPC64_LR(9) + PPC64_LR(10) + PPC64_LR(11) + PPC64_LR(12) + PPC64_LR(13) + PPC64_LR(14) + PPC64_LR(15) + PPC64_LR(16) + PPC64_LR(17) + PPC64_LR(18) + PPC64_LR(19) + PPC64_LR(20) + PPC64_LR(21) + PPC64_LR(22) + PPC64_LR(23) + PPC64_LR(24) + PPC64_LR(25) + PPC64_LR(26) + PPC64_LR(27) + PPC64_LR(28) + PPC64_LR(29) + PPC64_LR(30) + PPC64_LR(31) + +#if defined(__VSX__) + + // restore VS registers + // (note that this also restores floating point registers and V registers, + // because part of VS is mapped to these registers) + + addi 4, 3, PPC64_OFFS_FP + +// load VS register +#define PPC64_LVS(n) \ + lxvd2x n, 0, 4 ;\ + addi 4, 4, 16 + + // restore the first 32 VS regs (and also all floating point regs) + PPC64_LVS(0) + PPC64_LVS(1) + PPC64_LVS(2) + PPC64_LVS(3) + PPC64_LVS(4) + PPC64_LVS(5) + PPC64_LVS(6) + PPC64_LVS(7) + PPC64_LVS(8) + PPC64_LVS(9) + PPC64_LVS(10) + PPC64_LVS(11) + PPC64_LVS(12) + PPC64_LVS(13) + PPC64_LVS(14) + PPC64_LVS(15) + PPC64_LVS(16) + PPC64_LVS(17) + PPC64_LVS(18) + PPC64_LVS(19) + PPC64_LVS(20) + PPC64_LVS(21) + PPC64_LVS(22) + PPC64_LVS(23) + PPC64_LVS(24) + PPC64_LVS(25) + PPC64_LVS(26) + PPC64_LVS(27) + PPC64_LVS(28) + PPC64_LVS(29) + PPC64_LVS(30) + PPC64_LVS(31) + + // use VRSAVE to conditionally restore the remaining VS regs, + // that are where the V regs are mapped + + ld 5, PPC64_OFFS_VRSAVE(3) // test VRsave + cmpwi 5, 0 + beq Lnovec + +// conditionally load VS +#define PPC64_CLVS_BOTTOM(n) \ + beq Ldone##n ;\ + addi 4, 3, PPC64_OFFS_FP + n * 16 ;\ + lxvd2x n, 0, 4 ;\ +Ldone##n: + +#define PPC64_CLVSl(n) \ + andis. 0, 5, (1 PPC_LEFT_SHIFT(47-n)) ;\ +PPC64_CLVS_BOTTOM(n) + +#define PPC64_CLVSh(n) \ + andi. 0, 5, (1 PPC_LEFT_SHIFT(63-n)) ;\ +PPC64_CLVS_BOTTOM(n) + + PPC64_CLVSl(32) + PPC64_CLVSl(33) + PPC64_CLVSl(34) + PPC64_CLVSl(35) + PPC64_CLVSl(36) + PPC64_CLVSl(37) + PPC64_CLVSl(38) + PPC64_CLVSl(39) + PPC64_CLVSl(40) + PPC64_CLVSl(41) + PPC64_CLVSl(42) + PPC64_CLVSl(43) + PPC64_CLVSl(44) + PPC64_CLVSl(45) + PPC64_CLVSl(46) + PPC64_CLVSl(47) + PPC64_CLVSh(48) + PPC64_CLVSh(49) + PPC64_CLVSh(50) + PPC64_CLVSh(51) + PPC64_CLVSh(52) + PPC64_CLVSh(53) + PPC64_CLVSh(54) + PPC64_CLVSh(55) + PPC64_CLVSh(56) + PPC64_CLVSh(57) + PPC64_CLVSh(58) + PPC64_CLVSh(59) + PPC64_CLVSh(60) + PPC64_CLVSh(61) + PPC64_CLVSh(62) + PPC64_CLVSh(63) + +#else + +// load FP register +#define PPC64_LF(n) \ + lfd n, (PPC64_OFFS_FP + n * 16)(3) + + // restore float registers + PPC64_LF(0) + PPC64_LF(1) + PPC64_LF(2) + PPC64_LF(3) + PPC64_LF(4) + PPC64_LF(5) + PPC64_LF(6) + PPC64_LF(7) + PPC64_LF(8) + PPC64_LF(9) + PPC64_LF(10) + PPC64_LF(11) + PPC64_LF(12) + PPC64_LF(13) + PPC64_LF(14) + PPC64_LF(15) + PPC64_LF(16) + PPC64_LF(17) + PPC64_LF(18) + PPC64_LF(19) + PPC64_LF(20) + PPC64_LF(21) + PPC64_LF(22) + PPC64_LF(23) + PPC64_LF(24) + PPC64_LF(25) + PPC64_LF(26) + PPC64_LF(27) + PPC64_LF(28) + PPC64_LF(29) + PPC64_LF(30) + PPC64_LF(31) + +#if defined(__ALTIVEC__) + // restore vector registers if any are in use + ld 5, PPC64_OFFS_VRSAVE(3) // test VRsave + cmpwi 5, 0 + beq Lnovec + + subi 4, 1, 16 + // r4 is now a 16-byte aligned pointer into the red zone + // the _vectorScalarRegisters may not be 16-byte aligned + // so copy via red zone temp buffer + +#define PPC64_CLV_UNALIGNED_BOTTOM(n) \ + beq Ldone##n ;\ + ld 0, (PPC64_OFFS_V + n * 16)(3) ;\ + std 0, 0(4) ;\ + ld 0, (PPC64_OFFS_V + n * 16 + 8)(3) ;\ + std 0, 8(4) ;\ + lvx n, 0, 4 ;\ +Ldone ## n: + +#define PPC64_CLV_UNALIGNEDl(n) \ + andis. 0, 5, (1 PPC_LEFT_SHIFT(15-n)) ;\ +PPC64_CLV_UNALIGNED_BOTTOM(n) + +#define PPC64_CLV_UNALIGNEDh(n) \ + andi. 0, 5, (1 PPC_LEFT_SHIFT(31-n)) ;\ +PPC64_CLV_UNALIGNED_BOTTOM(n) + + PPC64_CLV_UNALIGNEDl(0) + PPC64_CLV_UNALIGNEDl(1) + PPC64_CLV_UNALIGNEDl(2) + PPC64_CLV_UNALIGNEDl(3) + PPC64_CLV_UNALIGNEDl(4) + PPC64_CLV_UNALIGNEDl(5) + PPC64_CLV_UNALIGNEDl(6) + PPC64_CLV_UNALIGNEDl(7) + PPC64_CLV_UNALIGNEDl(8) + PPC64_CLV_UNALIGNEDl(9) + PPC64_CLV_UNALIGNEDl(10) + PPC64_CLV_UNALIGNEDl(11) + PPC64_CLV_UNALIGNEDl(12) + PPC64_CLV_UNALIGNEDl(13) + PPC64_CLV_UNALIGNEDl(14) + PPC64_CLV_UNALIGNEDl(15) + PPC64_CLV_UNALIGNEDh(16) + PPC64_CLV_UNALIGNEDh(17) + PPC64_CLV_UNALIGNEDh(18) + PPC64_CLV_UNALIGNEDh(19) + PPC64_CLV_UNALIGNEDh(20) + PPC64_CLV_UNALIGNEDh(21) + PPC64_CLV_UNALIGNEDh(22) + PPC64_CLV_UNALIGNEDh(23) + PPC64_CLV_UNALIGNEDh(24) + PPC64_CLV_UNALIGNEDh(25) + PPC64_CLV_UNALIGNEDh(26) + PPC64_CLV_UNALIGNEDh(27) + PPC64_CLV_UNALIGNEDh(28) + PPC64_CLV_UNALIGNEDh(29) + PPC64_CLV_UNALIGNEDh(30) + PPC64_CLV_UNALIGNEDh(31) + +#endif +#endif + +Lnovec: + ld 0, PPC64_OFFS_CR(3) + mtcr 0 + ld 0, PPC64_OFFS_SRR0(3) + mtctr 0 + + PPC64_LR(0) + PPC64_LR(5) + PPC64_LR(4) + PPC64_LR(1) + PPC64_LR(3) + bctr + +#elif defined(__ppc__) + +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_ppc6jumptoEv) +// +// void libunwind::Registers_ppc::jumpto() +// +// On entry: +// thread_state pointer is in r3 +// + + // restore integral registerrs + // skip r0 for now + // skip r1 for now + lwz 2, 16(3) + // skip r3 for now + // skip r4 for now + // skip r5 for now + lwz 6, 32(3) + lwz 7, 36(3) + lwz 8, 40(3) + lwz 9, 44(3) + lwz 10, 48(3) + lwz 11, 52(3) + lwz 12, 56(3) + lwz 13, 60(3) + lwz 14, 64(3) + lwz 15, 68(3) + lwz 16, 72(3) + lwz 17, 76(3) + lwz 18, 80(3) + lwz 19, 84(3) + lwz 20, 88(3) + lwz 21, 92(3) + lwz 22, 96(3) + lwz 23,100(3) + lwz 24,104(3) + lwz 25,108(3) + lwz 26,112(3) + lwz 27,116(3) + lwz 28,120(3) + lwz 29,124(3) + lwz 30,128(3) + lwz 31,132(3) + +#ifndef __NO_FPRS__ + // restore float registers + lfd 0, 160(3) + lfd 1, 168(3) + lfd 2, 176(3) + lfd 3, 184(3) + lfd 4, 192(3) + lfd 5, 200(3) + lfd 6, 208(3) + lfd 7, 216(3) + lfd 8, 224(3) + lfd 9, 232(3) + lfd 10,240(3) + lfd 11,248(3) + lfd 12,256(3) + lfd 13,264(3) + lfd 14,272(3) + lfd 15,280(3) + lfd 16,288(3) + lfd 17,296(3) + lfd 18,304(3) + lfd 19,312(3) + lfd 20,320(3) + lfd 21,328(3) + lfd 22,336(3) + lfd 23,344(3) + lfd 24,352(3) + lfd 25,360(3) + lfd 26,368(3) + lfd 27,376(3) + lfd 28,384(3) + lfd 29,392(3) + lfd 30,400(3) + lfd 31,408(3) +#endif + +#if defined(__ALTIVEC__) + // restore vector registers if any are in use + lwz 5, 156(3) // test VRsave + cmpwi 5, 0 + beq Lnovec + + subi 4, 1, 16 + rlwinm 4, 4, 0, 0, 27 // mask low 4-bits + // r4 is now a 16-byte aligned pointer into the red zone + // the _vectorRegisters may not be 16-byte aligned so copy via red zone temp buffer + + +#define LOAD_VECTOR_UNALIGNEDl(_index) \ + andis. 0, 5, (1 PPC_LEFT_SHIFT(15-_index)) SEPARATOR \ + beq Ldone ## _index SEPARATOR \ + lwz 0, 424+_index*16(3) SEPARATOR \ + stw 0, 0(%r4) SEPARATOR \ + lwz 0, 424+_index*16+4(%r3) SEPARATOR \ + stw 0, 4(%r4) SEPARATOR \ + lwz 0, 424+_index*16+8(%r3) SEPARATOR \ + stw 0, 8(%r4) SEPARATOR \ + lwz 0, 424+_index*16+12(%r3) SEPARATOR \ + stw 0, 12(%r4) SEPARATOR \ + lvx _index, 0, 4 SEPARATOR \ + Ldone ## _index: + +#define LOAD_VECTOR_UNALIGNEDh(_index) \ + andi. 0, 5, (1 PPC_LEFT_SHIFT(31-_index)) SEPARATOR \ + beq Ldone ## _index SEPARATOR \ + lwz 0, 424+_index*16(3) SEPARATOR \ + stw 0, 0(4) SEPARATOR \ + lwz 0, 424+_index*16+4(3) SEPARATOR \ + stw 0, 4(4) SEPARATOR \ + lwz 0, 424+_index*16+8(3) SEPARATOR \ + stw 0, 8(%r4) SEPARATOR \ + lwz 0, 424+_index*16+12(3) SEPARATOR \ + stw 0, 12(4) SEPARATOR \ + lvx _index, 0, 4 SEPARATOR \ + Ldone ## _index: + + + LOAD_VECTOR_UNALIGNEDl(0) + LOAD_VECTOR_UNALIGNEDl(1) + LOAD_VECTOR_UNALIGNEDl(2) + LOAD_VECTOR_UNALIGNEDl(3) + LOAD_VECTOR_UNALIGNEDl(4) + LOAD_VECTOR_UNALIGNEDl(5) + LOAD_VECTOR_UNALIGNEDl(6) + LOAD_VECTOR_UNALIGNEDl(7) + LOAD_VECTOR_UNALIGNEDl(8) + LOAD_VECTOR_UNALIGNEDl(9) + LOAD_VECTOR_UNALIGNEDl(10) + LOAD_VECTOR_UNALIGNEDl(11) + LOAD_VECTOR_UNALIGNEDl(12) + LOAD_VECTOR_UNALIGNEDl(13) + LOAD_VECTOR_UNALIGNEDl(14) + LOAD_VECTOR_UNALIGNEDl(15) + LOAD_VECTOR_UNALIGNEDh(16) + LOAD_VECTOR_UNALIGNEDh(17) + LOAD_VECTOR_UNALIGNEDh(18) + LOAD_VECTOR_UNALIGNEDh(19) + LOAD_VECTOR_UNALIGNEDh(20) + LOAD_VECTOR_UNALIGNEDh(21) + LOAD_VECTOR_UNALIGNEDh(22) + LOAD_VECTOR_UNALIGNEDh(23) + LOAD_VECTOR_UNALIGNEDh(24) + LOAD_VECTOR_UNALIGNEDh(25) + LOAD_VECTOR_UNALIGNEDh(26) + LOAD_VECTOR_UNALIGNEDh(27) + LOAD_VECTOR_UNALIGNEDh(28) + LOAD_VECTOR_UNALIGNEDh(29) + LOAD_VECTOR_UNALIGNEDh(30) + LOAD_VECTOR_UNALIGNEDh(31) +#endif + +Lnovec: + lwz 0, 136(3) // __cr + mtcr 0 + lwz 0, 148(3) // __ctr + mtctr 0 + lwz 0, 0(3) // __ssr0 + mtctr 0 + lwz 0, 8(3) // do r0 now + lwz 5, 28(3) // do r5 now + lwz 4, 24(3) // do r4 now + lwz 1, 12(3) // do sp now + lwz 3, 20(3) // do r3 last + bctr + +#elif defined(__aarch64__) + +// +// extern "C" void __libunwind_Registers_arm64_jumpto(Registers_arm64 *); +// +// On entry: +// thread_state pointer is in x0 +// + .p2align 2 +DEFINE_LIBUNWIND_FUNCTION(__libunwind_Registers_arm64_jumpto) + // skip restore of x0,x1 for now + ldp x2, x3, [x0, #0x010] + ldp x4, x5, [x0, #0x020] + ldp x6, x7, [x0, #0x030] + ldp x8, x9, [x0, #0x040] + ldp x10,x11, [x0, #0x050] + ldp x12,x13, [x0, #0x060] + ldp x14,x15, [x0, #0x070] + // x16 and x17 were clobbered by the call into the unwinder, so no point in + // restoring them. + ldp x18,x19, [x0, #0x090] + ldp x20,x21, [x0, #0x0A0] + ldp x22,x23, [x0, #0x0B0] + ldp x24,x25, [x0, #0x0C0] + ldp x26,x27, [x0, #0x0D0] + ldp x28,x29, [x0, #0x0E0] + ldr x30, [x0, #0x100] // restore pc into lr + + ldp d0, d1, [x0, #0x110] + ldp d2, d3, [x0, #0x120] + ldp d4, d5, [x0, #0x130] + ldp d6, d7, [x0, #0x140] + ldp d8, d9, [x0, #0x150] + ldp d10,d11, [x0, #0x160] + ldp d12,d13, [x0, #0x170] + ldp d14,d15, [x0, #0x180] + ldp d16,d17, [x0, #0x190] + ldp d18,d19, [x0, #0x1A0] + ldp d20,d21, [x0, #0x1B0] + ldp d22,d23, [x0, #0x1C0] + ldp d24,d25, [x0, #0x1D0] + ldp d26,d27, [x0, #0x1E0] + ldp d28,d29, [x0, #0x1F0] + ldr d30, [x0, #0x200] + ldr d31, [x0, #0x208] + + // Finally, restore sp. This must be done after the the last read from the + // context struct, because it is allocated on the stack, and an exception + // could clobber the de-allocated portion of the stack after sp has been + // restored. + ldr x16, [x0, #0x0F8] + ldp x0, x1, [x0, #0x000] // restore x0,x1 + mov sp,x16 // restore sp + ret x30 // jump to pc + +#elif defined(__arm__) && !defined(__APPLE__) + +#if !defined(__ARM_ARCH_ISA_ARM) +#if (__ARM_ARCH_ISA_THUMB == 2) + .syntax unified +#endif + .thumb +#endif + +@ +@ void libunwind::Registers_arm::restoreCoreAndJumpTo() +@ +@ On entry: +@ thread_state pointer is in r0 +@ + .p2align 2 +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_arm20restoreCoreAndJumpToEv) +#if !defined(__ARM_ARCH_ISA_ARM) && __ARM_ARCH_ISA_THUMB == 1 + @ r8-r11: ldm into r1-r4, then mov to r8-r11 + adds r0, #0x20 + ldm r0!, {r1-r4} + subs r0, #0x30 + mov r8, r1 + mov r9, r2 + mov r10, r3 + mov r11, r4 + @ r12 does not need loading, it it the intra-procedure-call scratch register + ldr r2, [r0, #0x34] + ldr r3, [r0, #0x3c] + mov sp, r2 + mov lr, r3 @ restore pc into lr + ldm r0, {r0-r7} +#else + @ Use lr as base so that r0 can be restored. + mov lr, r0 + @ 32bit thumb-2 restrictions for ldm: + @ . the sp (r13) cannot be in the list + @ . the pc (r15) and lr (r14) cannot both be in the list in an LDM instruction + ldm lr, {r0-r12} + ldr sp, [lr, #52] + ldr lr, [lr, #60] @ restore pc into lr +#endif + JMP(lr) + +@ +@ static void libunwind::Registers_arm::restoreVFPWithFLDMD(unw_fpreg_t* values) +@ +@ On entry: +@ values pointer is in r0 +@ + .p2align 2 +#if defined(__ELF__) + .fpu vfpv3-d16 +#endif +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_arm19restoreVFPWithFLDMDEPv) + @ VFP and iwMMX instructions are only available when compiling with the flags + @ that enable them. We do not want to do that in the library (because we do not + @ want the compiler to generate instructions that access those) but this is + @ only accessed if the personality routine needs these registers. Use of + @ these registers implies they are, actually, available on the target, so + @ it's ok to execute. + @ So, generate the instruction using the corresponding coprocessor mnemonic. + vldmia r0, {d0-d15} + JMP(lr) + +@ +@ static void libunwind::Registers_arm::restoreVFPWithFLDMX(unw_fpreg_t* values) +@ +@ On entry: +@ values pointer is in r0 +@ + .p2align 2 +#if defined(__ELF__) + .fpu vfpv3-d16 +#endif +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_arm19restoreVFPWithFLDMXEPv) + vldmia r0, {d0-d15} @ fldmiax is deprecated in ARMv7+ and now behaves like vldmia + JMP(lr) + +@ +@ static void libunwind::Registers_arm::restoreVFPv3(unw_fpreg_t* values) +@ +@ On entry: +@ values pointer is in r0 +@ + .p2align 2 +#if defined(__ELF__) + .fpu vfpv3 +#endif +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_arm12restoreVFPv3EPv) + vldmia r0, {d16-d31} + JMP(lr) + +#if defined(__ARM_WMMX) + +@ +@ static void libunwind::Registers_arm::restoreiWMMX(unw_fpreg_t* values) +@ +@ On entry: +@ values pointer is in r0 +@ + .p2align 2 +#if defined(__ELF__) + .arch armv5te +#endif +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_arm12restoreiWMMXEPv) + ldcl p1, cr0, [r0], #8 @ wldrd wR0, [r0], #8 + ldcl p1, cr1, [r0], #8 @ wldrd wR1, [r0], #8 + ldcl p1, cr2, [r0], #8 @ wldrd wR2, [r0], #8 + ldcl p1, cr3, [r0], #8 @ wldrd wR3, [r0], #8 + ldcl p1, cr4, [r0], #8 @ wldrd wR4, [r0], #8 + ldcl p1, cr5, [r0], #8 @ wldrd wR5, [r0], #8 + ldcl p1, cr6, [r0], #8 @ wldrd wR6, [r0], #8 + ldcl p1, cr7, [r0], #8 @ wldrd wR7, [r0], #8 + ldcl p1, cr8, [r0], #8 @ wldrd wR8, [r0], #8 + ldcl p1, cr9, [r0], #8 @ wldrd wR9, [r0], #8 + ldcl p1, cr10, [r0], #8 @ wldrd wR10, [r0], #8 + ldcl p1, cr11, [r0], #8 @ wldrd wR11, [r0], #8 + ldcl p1, cr12, [r0], #8 @ wldrd wR12, [r0], #8 + ldcl p1, cr13, [r0], #8 @ wldrd wR13, [r0], #8 + ldcl p1, cr14, [r0], #8 @ wldrd wR14, [r0], #8 + ldcl p1, cr15, [r0], #8 @ wldrd wR15, [r0], #8 + JMP(lr) + +@ +@ static void libunwind::Registers_arm::restoreiWMMXControl(unw_uint32_t* values) +@ +@ On entry: +@ values pointer is in r0 +@ + .p2align 2 +#if defined(__ELF__) + .arch armv5te +#endif +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_arm19restoreiWMMXControlEPj) + ldc2 p1, cr8, [r0], #4 @ wldrw wCGR0, [r0], #4 + ldc2 p1, cr9, [r0], #4 @ wldrw wCGR1, [r0], #4 + ldc2 p1, cr10, [r0], #4 @ wldrw wCGR2, [r0], #4 + ldc2 p1, cr11, [r0], #4 @ wldrw wCGR3, [r0], #4 + JMP(lr) + +#endif + +#elif defined(__or1k__) + +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind14Registers_or1k6jumptoEv) +# +# void libunwind::Registers_or1k::jumpto() +# +# On entry: +# thread_state pointer is in r3 +# + + # restore integral registers + l.lwz r0, 0(r3) + l.lwz r1, 4(r3) + l.lwz r2, 8(r3) + # skip r3 for now + l.lwz r4, 16(r3) + l.lwz r5, 20(r3) + l.lwz r6, 24(r3) + l.lwz r7, 28(r3) + l.lwz r8, 32(r3) + # skip r9 + l.lwz r10, 40(r3) + l.lwz r11, 44(r3) + l.lwz r12, 48(r3) + l.lwz r13, 52(r3) + l.lwz r14, 56(r3) + l.lwz r15, 60(r3) + l.lwz r16, 64(r3) + l.lwz r17, 68(r3) + l.lwz r18, 72(r3) + l.lwz r19, 76(r3) + l.lwz r20, 80(r3) + l.lwz r21, 84(r3) + l.lwz r22, 88(r3) + l.lwz r23, 92(r3) + l.lwz r24, 96(r3) + l.lwz r25,100(r3) + l.lwz r26,104(r3) + l.lwz r27,108(r3) + l.lwz r28,112(r3) + l.lwz r29,116(r3) + l.lwz r30,120(r3) + l.lwz r31,124(r3) + + # load new pc into ra + l.lwz r9, 128(r3) + + # at last, restore r3 + l.lwz r3, 12(r3) + + # jump to pc + l.jr r9 + l.nop + +#elif defined(__hexagon__) +# On entry: +# thread_state pointer is in r2 +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind17Registers_hexagon6jumptoEv) +# +# void libunwind::Registers_hexagon::jumpto() +# + r8 = memw(r0+#32) + r9 = memw(r0+#36) + r10 = memw(r0+#40) + r11 = memw(r0+#44) + + r12 = memw(r0+#48) + r13 = memw(r0+#52) + r14 = memw(r0+#56) + r15 = memw(r0+#60) + + r16 = memw(r0+#64) + r17 = memw(r0+#68) + r18 = memw(r0+#72) + r19 = memw(r0+#76) + + r20 = memw(r0+#80) + r21 = memw(r0+#84) + r22 = memw(r0+#88) + r23 = memw(r0+#92) + + r24 = memw(r0+#96) + r25 = memw(r0+#100) + r26 = memw(r0+#104) + r27 = memw(r0+#108) + + r28 = memw(r0+#112) + r29 = memw(r0+#116) + r30 = memw(r0+#120) + r31 = memw(r0+#132) + + r1 = memw(r0+#128) + c4 = r1 // Predicate register + r1 = memw(r0+#4) + r0 = memw(r0) + jumpr r31 +#elif defined(__mips__) && defined(_ABIO32) && _MIPS_SIM == _ABIO32 + +// +// void libunwind::Registers_mips_o32::jumpto() +// +// On entry: +// thread state pointer is in a0 ($4) +// +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind18Registers_mips_o326jumptoEv) + .set push + .set noat + .set noreorder + .set nomacro +#ifdef __mips_hard_float +#if __mips_fpr != 64 + ldc1 $f0, (4 * 36 + 8 * 0)($4) + ldc1 $f2, (4 * 36 + 8 * 2)($4) + ldc1 $f4, (4 * 36 + 8 * 4)($4) + ldc1 $f6, (4 * 36 + 8 * 6)($4) + ldc1 $f8, (4 * 36 + 8 * 8)($4) + ldc1 $f10, (4 * 36 + 8 * 10)($4) + ldc1 $f12, (4 * 36 + 8 * 12)($4) + ldc1 $f14, (4 * 36 + 8 * 14)($4) + ldc1 $f16, (4 * 36 + 8 * 16)($4) + ldc1 $f18, (4 * 36 + 8 * 18)($4) + ldc1 $f20, (4 * 36 + 8 * 20)($4) + ldc1 $f22, (4 * 36 + 8 * 22)($4) + ldc1 $f24, (4 * 36 + 8 * 24)($4) + ldc1 $f26, (4 * 36 + 8 * 26)($4) + ldc1 $f28, (4 * 36 + 8 * 28)($4) + ldc1 $f30, (4 * 36 + 8 * 30)($4) +#else + ldc1 $f0, (4 * 36 + 8 * 0)($4) + ldc1 $f1, (4 * 36 + 8 * 1)($4) + ldc1 $f2, (4 * 36 + 8 * 2)($4) + ldc1 $f3, (4 * 36 + 8 * 3)($4) + ldc1 $f4, (4 * 36 + 8 * 4)($4) + ldc1 $f5, (4 * 36 + 8 * 5)($4) + ldc1 $f6, (4 * 36 + 8 * 6)($4) + ldc1 $f7, (4 * 36 + 8 * 7)($4) + ldc1 $f8, (4 * 36 + 8 * 8)($4) + ldc1 $f9, (4 * 36 + 8 * 9)($4) + ldc1 $f10, (4 * 36 + 8 * 10)($4) + ldc1 $f11, (4 * 36 + 8 * 11)($4) + ldc1 $f12, (4 * 36 + 8 * 12)($4) + ldc1 $f13, (4 * 36 + 8 * 13)($4) + ldc1 $f14, (4 * 36 + 8 * 14)($4) + ldc1 $f15, (4 * 36 + 8 * 15)($4) + ldc1 $f16, (4 * 36 + 8 * 16)($4) + ldc1 $f17, (4 * 36 + 8 * 17)($4) + ldc1 $f18, (4 * 36 + 8 * 18)($4) + ldc1 $f19, (4 * 36 + 8 * 19)($4) + ldc1 $f20, (4 * 36 + 8 * 20)($4) + ldc1 $f21, (4 * 36 + 8 * 21)($4) + ldc1 $f22, (4 * 36 + 8 * 22)($4) + ldc1 $f23, (4 * 36 + 8 * 23)($4) + ldc1 $f24, (4 * 36 + 8 * 24)($4) + ldc1 $f25, (4 * 36 + 8 * 25)($4) + ldc1 $f26, (4 * 36 + 8 * 26)($4) + ldc1 $f27, (4 * 36 + 8 * 27)($4) + ldc1 $f28, (4 * 36 + 8 * 28)($4) + ldc1 $f29, (4 * 36 + 8 * 29)($4) + ldc1 $f30, (4 * 36 + 8 * 30)($4) + ldc1 $f31, (4 * 36 + 8 * 31)($4) +#endif +#endif + // restore hi and lo + lw $8, (4 * 33)($4) + mthi $8 + lw $8, (4 * 34)($4) + mtlo $8 + // r0 is zero + lw $1, (4 * 1)($4) + lw $2, (4 * 2)($4) + lw $3, (4 * 3)($4) + // skip a0 for now + lw $5, (4 * 5)($4) + lw $6, (4 * 6)($4) + lw $7, (4 * 7)($4) + lw $8, (4 * 8)($4) + lw $9, (4 * 9)($4) + lw $10, (4 * 10)($4) + lw $11, (4 * 11)($4) + lw $12, (4 * 12)($4) + lw $13, (4 * 13)($4) + lw $14, (4 * 14)($4) + lw $15, (4 * 15)($4) + lw $16, (4 * 16)($4) + lw $17, (4 * 17)($4) + lw $18, (4 * 18)($4) + lw $19, (4 * 19)($4) + lw $20, (4 * 20)($4) + lw $21, (4 * 21)($4) + lw $22, (4 * 22)($4) + lw $23, (4 * 23)($4) + lw $24, (4 * 24)($4) + lw $25, (4 * 25)($4) + lw $26, (4 * 26)($4) + lw $27, (4 * 27)($4) + lw $28, (4 * 28)($4) + lw $29, (4 * 29)($4) + lw $30, (4 * 30)($4) + // load new pc into ra + lw $31, (4 * 32)($4) + // jump to ra, load a0 in the delay slot + jr $31 + lw $4, (4 * 4)($4) + .set pop + +#elif defined(__mips64) + +// +// void libunwind::Registers_mips_newabi::jumpto() +// +// On entry: +// thread state pointer is in a0 ($4) +// +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind21Registers_mips_newabi6jumptoEv) + .set push + .set noat + .set noreorder + .set nomacro +#ifdef __mips_hard_float + ldc1 $f0, (8 * 35)($4) + ldc1 $f1, (8 * 36)($4) + ldc1 $f2, (8 * 37)($4) + ldc1 $f3, (8 * 38)($4) + ldc1 $f4, (8 * 39)($4) + ldc1 $f5, (8 * 40)($4) + ldc1 $f6, (8 * 41)($4) + ldc1 $f7, (8 * 42)($4) + ldc1 $f8, (8 * 43)($4) + ldc1 $f9, (8 * 44)($4) + ldc1 $f10, (8 * 45)($4) + ldc1 $f11, (8 * 46)($4) + ldc1 $f12, (8 * 47)($4) + ldc1 $f13, (8 * 48)($4) + ldc1 $f14, (8 * 49)($4) + ldc1 $f15, (8 * 50)($4) + ldc1 $f16, (8 * 51)($4) + ldc1 $f17, (8 * 52)($4) + ldc1 $f18, (8 * 53)($4) + ldc1 $f19, (8 * 54)($4) + ldc1 $f20, (8 * 55)($4) + ldc1 $f21, (8 * 56)($4) + ldc1 $f22, (8 * 57)($4) + ldc1 $f23, (8 * 58)($4) + ldc1 $f24, (8 * 59)($4) + ldc1 $f25, (8 * 60)($4) + ldc1 $f26, (8 * 61)($4) + ldc1 $f27, (8 * 62)($4) + ldc1 $f28, (8 * 63)($4) + ldc1 $f29, (8 * 64)($4) + ldc1 $f30, (8 * 65)($4) + ldc1 $f31, (8 * 66)($4) +#endif + // restore hi and lo + ld $8, (8 * 33)($4) + mthi $8 + ld $8, (8 * 34)($4) + mtlo $8 + // r0 is zero + ld $1, (8 * 1)($4) + ld $2, (8 * 2)($4) + ld $3, (8 * 3)($4) + // skip a0 for now + ld $5, (8 * 5)($4) + ld $6, (8 * 6)($4) + ld $7, (8 * 7)($4) + ld $8, (8 * 8)($4) + ld $9, (8 * 9)($4) + ld $10, (8 * 10)($4) + ld $11, (8 * 11)($4) + ld $12, (8 * 12)($4) + ld $13, (8 * 13)($4) + ld $14, (8 * 14)($4) + ld $15, (8 * 15)($4) + ld $16, (8 * 16)($4) + ld $17, (8 * 17)($4) + ld $18, (8 * 18)($4) + ld $19, (8 * 19)($4) + ld $20, (8 * 20)($4) + ld $21, (8 * 21)($4) + ld $22, (8 * 22)($4) + ld $23, (8 * 23)($4) + ld $24, (8 * 24)($4) + ld $25, (8 * 25)($4) + ld $26, (8 * 26)($4) + ld $27, (8 * 27)($4) + ld $28, (8 * 28)($4) + ld $29, (8 * 29)($4) + ld $30, (8 * 30)($4) + // load new pc into ra + ld $31, (8 * 32)($4) + // jump to ra, load a0 in the delay slot + jr $31 + ld $4, (8 * 4)($4) + .set pop + +#elif defined(__sparc__) + +// +// void libunwind::Registers_sparc_o32::jumpto() +// +// On entry: +// thread_state pointer is in o0 +// +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind15Registers_sparc6jumptoEv) + ta 3 + ldd [%o0 + 64], %l0 + ldd [%o0 + 72], %l2 + ldd [%o0 + 80], %l4 + ldd [%o0 + 88], %l6 + ldd [%o0 + 96], %i0 + ldd [%o0 + 104], %i2 + ldd [%o0 + 112], %i4 + ldd [%o0 + 120], %i6 + ld [%o0 + 60], %o7 + jmp %o7 + nop + +#elif defined(__riscv) + +// +// void libunwind::Registers_riscv::jumpto() +// +// On entry: +// thread_state pointer is in a0 +// + .p2align 2 +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind15Registers_riscv6jumptoEv) +# if defined(__riscv_flen) + FLOAD f0, (RISCV_FOFFSET + RISCV_FSIZE * 0)(a0) + FLOAD f1, (RISCV_FOFFSET + RISCV_FSIZE * 1)(a0) + FLOAD f2, (RISCV_FOFFSET + RISCV_FSIZE * 2)(a0) + FLOAD f3, (RISCV_FOFFSET + RISCV_FSIZE * 3)(a0) + FLOAD f4, (RISCV_FOFFSET + RISCV_FSIZE * 4)(a0) + FLOAD f5, (RISCV_FOFFSET + RISCV_FSIZE * 5)(a0) + FLOAD f6, (RISCV_FOFFSET + RISCV_FSIZE * 6)(a0) + FLOAD f7, (RISCV_FOFFSET + RISCV_FSIZE * 7)(a0) + FLOAD f8, (RISCV_FOFFSET + RISCV_FSIZE * 8)(a0) + FLOAD f9, (RISCV_FOFFSET + RISCV_FSIZE * 9)(a0) + FLOAD f10, (RISCV_FOFFSET + RISCV_FSIZE * 10)(a0) + FLOAD f11, (RISCV_FOFFSET + RISCV_FSIZE * 11)(a0) + FLOAD f12, (RISCV_FOFFSET + RISCV_FSIZE * 12)(a0) + FLOAD f13, (RISCV_FOFFSET + RISCV_FSIZE * 13)(a0) + FLOAD f14, (RISCV_FOFFSET + RISCV_FSIZE * 14)(a0) + FLOAD f15, (RISCV_FOFFSET + RISCV_FSIZE * 15)(a0) + FLOAD f16, (RISCV_FOFFSET + RISCV_FSIZE * 16)(a0) + FLOAD f17, (RISCV_FOFFSET + RISCV_FSIZE * 17)(a0) + FLOAD f18, (RISCV_FOFFSET + RISCV_FSIZE * 18)(a0) + FLOAD f19, (RISCV_FOFFSET + RISCV_FSIZE * 19)(a0) + FLOAD f20, (RISCV_FOFFSET + RISCV_FSIZE * 20)(a0) + FLOAD f21, (RISCV_FOFFSET + RISCV_FSIZE * 21)(a0) + FLOAD f22, (RISCV_FOFFSET + RISCV_FSIZE * 22)(a0) + FLOAD f23, (RISCV_FOFFSET + RISCV_FSIZE * 23)(a0) + FLOAD f24, (RISCV_FOFFSET + RISCV_FSIZE * 24)(a0) + FLOAD f25, (RISCV_FOFFSET + RISCV_FSIZE * 25)(a0) + FLOAD f26, (RISCV_FOFFSET + RISCV_FSIZE * 26)(a0) + FLOAD f27, (RISCV_FOFFSET + RISCV_FSIZE * 27)(a0) + FLOAD f28, (RISCV_FOFFSET + RISCV_FSIZE * 28)(a0) + FLOAD f29, (RISCV_FOFFSET + RISCV_FSIZE * 29)(a0) + FLOAD f30, (RISCV_FOFFSET + RISCV_FSIZE * 30)(a0) + FLOAD f31, (RISCV_FOFFSET + RISCV_FSIZE * 31)(a0) +# endif + + // x0 is zero + ILOAD x1, (RISCV_ISIZE * 0)(a0) // restore pc into ra + ILOAD x2, (RISCV_ISIZE * 2)(a0) + ILOAD x3, (RISCV_ISIZE * 3)(a0) + ILOAD x4, (RISCV_ISIZE * 4)(a0) + ILOAD x5, (RISCV_ISIZE * 5)(a0) + ILOAD x6, (RISCV_ISIZE * 6)(a0) + ILOAD x7, (RISCV_ISIZE * 7)(a0) + ILOAD x8, (RISCV_ISIZE * 8)(a0) + ILOAD x9, (RISCV_ISIZE * 9)(a0) + // skip a0 for now + ILOAD x11, (RISCV_ISIZE * 11)(a0) + ILOAD x12, (RISCV_ISIZE * 12)(a0) + ILOAD x13, (RISCV_ISIZE * 13)(a0) + ILOAD x14, (RISCV_ISIZE * 14)(a0) + ILOAD x15, (RISCV_ISIZE * 15)(a0) + ILOAD x16, (RISCV_ISIZE * 16)(a0) + ILOAD x17, (RISCV_ISIZE * 17)(a0) + ILOAD x18, (RISCV_ISIZE * 18)(a0) + ILOAD x19, (RISCV_ISIZE * 19)(a0) + ILOAD x20, (RISCV_ISIZE * 20)(a0) + ILOAD x21, (RISCV_ISIZE * 21)(a0) + ILOAD x22, (RISCV_ISIZE * 22)(a0) + ILOAD x23, (RISCV_ISIZE * 23)(a0) + ILOAD x24, (RISCV_ISIZE * 24)(a0) + ILOAD x25, (RISCV_ISIZE * 25)(a0) + ILOAD x26, (RISCV_ISIZE * 26)(a0) + ILOAD x27, (RISCV_ISIZE * 27)(a0) + ILOAD x28, (RISCV_ISIZE * 28)(a0) + ILOAD x29, (RISCV_ISIZE * 29)(a0) + ILOAD x30, (RISCV_ISIZE * 30)(a0) + ILOAD x31, (RISCV_ISIZE * 31)(a0) + ILOAD x10, (RISCV_ISIZE * 10)(a0) // restore a0 + + ret // jump to ra + +#endif + +#endif /* !defined(__USING_SJLJ_EXCEPTIONS__) */ + +NO_EXEC_STACK_DIRECTIVE + diff --git a/shared/sentry/external/crashpad/libunwind/src/UnwindRegistersSave.S b/shared/sentry/external/crashpad/libunwind/src/UnwindRegistersSave.S new file mode 100644 index 000000000..e565c8ffc --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/src/UnwindRegistersSave.S @@ -0,0 +1,1117 @@ +//===------------------------ UnwindRegistersSave.S -----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "assembly.h" + + .text + +#if !defined(__USING_SJLJ_EXCEPTIONS__) + +#if defined(__i386__) + +# +# extern int __unw_getcontext(unw_context_t* thread_state) +# +# On entry: +# + + +# +-----------------------+ +# + thread_state pointer + +# +-----------------------+ +# + return address + +# +-----------------------+ <-- SP +# + + +# +DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) + + _LIBUNWIND_CET_ENDBR + push %eax + movl 8(%esp), %eax + movl %ebx, 4(%eax) + movl %ecx, 8(%eax) + movl %edx, 12(%eax) + movl %edi, 16(%eax) + movl %esi, 20(%eax) + movl %ebp, 24(%eax) + movl %esp, %edx + addl $8, %edx + movl %edx, 28(%eax) # store what sp was at call site as esp + # skip ss + # skip eflags + movl 4(%esp), %edx + movl %edx, 40(%eax) # store return address as eip + # skip cs + # skip ds + # skip es + # skip fs + # skip gs + movl (%esp), %edx + movl %edx, (%eax) # store original eax + popl %eax + xorl %eax, %eax # return UNW_ESUCCESS + ret + +#elif defined(__x86_64__) + +# +# extern int __unw_getcontext(unw_context_t* thread_state) +# +# On entry: +# thread_state pointer is in rdi +# +DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) +#if defined(_WIN64) +#define PTR %rcx +#define TMP %rdx +#else +#define PTR %rdi +#define TMP %rsi +#endif + + _LIBUNWIND_CET_ENDBR + movq %rax, (PTR) + movq %rbx, 8(PTR) + movq %rcx, 16(PTR) + movq %rdx, 24(PTR) + movq %rdi, 32(PTR) + movq %rsi, 40(PTR) + movq %rbp, 48(PTR) + movq %rsp, 56(PTR) + addq $8, 56(PTR) + movq %r8, 64(PTR) + movq %r9, 72(PTR) + movq %r10, 80(PTR) + movq %r11, 88(PTR) + movq %r12, 96(PTR) + movq %r13,104(PTR) + movq %r14,112(PTR) + movq %r15,120(PTR) + movq (%rsp),TMP + movq TMP,128(PTR) # store return address as rip + # skip rflags + # skip cs + # skip fs + # skip gs + +#if defined(_WIN64) + movdqu %xmm0,176(PTR) + movdqu %xmm1,192(PTR) + movdqu %xmm2,208(PTR) + movdqu %xmm3,224(PTR) + movdqu %xmm4,240(PTR) + movdqu %xmm5,256(PTR) + movdqu %xmm6,272(PTR) + movdqu %xmm7,288(PTR) + movdqu %xmm8,304(PTR) + movdqu %xmm9,320(PTR) + movdqu %xmm10,336(PTR) + movdqu %xmm11,352(PTR) + movdqu %xmm12,368(PTR) + movdqu %xmm13,384(PTR) + movdqu %xmm14,400(PTR) + movdqu %xmm15,416(PTR) +#endif + xorl %eax, %eax # return UNW_ESUCCESS + ret + +#elif defined(__mips__) && defined(_ABIO32) && _MIPS_SIM == _ABIO32 + +# +# extern int __unw_getcontext(unw_context_t* thread_state) +# +# On entry: +# thread_state pointer is in a0 ($4) +# +DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) + .set push + .set noat + .set noreorder + .set nomacro + sw $1, (4 * 1)($4) + sw $2, (4 * 2)($4) + sw $3, (4 * 3)($4) + sw $4, (4 * 4)($4) + sw $5, (4 * 5)($4) + sw $6, (4 * 6)($4) + sw $7, (4 * 7)($4) + sw $8, (4 * 8)($4) + sw $9, (4 * 9)($4) + sw $10, (4 * 10)($4) + sw $11, (4 * 11)($4) + sw $12, (4 * 12)($4) + sw $13, (4 * 13)($4) + sw $14, (4 * 14)($4) + sw $15, (4 * 15)($4) + sw $16, (4 * 16)($4) + sw $17, (4 * 17)($4) + sw $18, (4 * 18)($4) + sw $19, (4 * 19)($4) + sw $20, (4 * 20)($4) + sw $21, (4 * 21)($4) + sw $22, (4 * 22)($4) + sw $23, (4 * 23)($4) + sw $24, (4 * 24)($4) + sw $25, (4 * 25)($4) + sw $26, (4 * 26)($4) + sw $27, (4 * 27)($4) + sw $28, (4 * 28)($4) + sw $29, (4 * 29)($4) + sw $30, (4 * 30)($4) + sw $31, (4 * 31)($4) + # Store return address to pc + sw $31, (4 * 32)($4) + # hi and lo + mfhi $8 + sw $8, (4 * 33)($4) + mflo $8 + sw $8, (4 * 34)($4) +#ifdef __mips_hard_float +#if __mips_fpr != 64 + sdc1 $f0, (4 * 36 + 8 * 0)($4) + sdc1 $f2, (4 * 36 + 8 * 2)($4) + sdc1 $f4, (4 * 36 + 8 * 4)($4) + sdc1 $f6, (4 * 36 + 8 * 6)($4) + sdc1 $f8, (4 * 36 + 8 * 8)($4) + sdc1 $f10, (4 * 36 + 8 * 10)($4) + sdc1 $f12, (4 * 36 + 8 * 12)($4) + sdc1 $f14, (4 * 36 + 8 * 14)($4) + sdc1 $f16, (4 * 36 + 8 * 16)($4) + sdc1 $f18, (4 * 36 + 8 * 18)($4) + sdc1 $f20, (4 * 36 + 8 * 20)($4) + sdc1 $f22, (4 * 36 + 8 * 22)($4) + sdc1 $f24, (4 * 36 + 8 * 24)($4) + sdc1 $f26, (4 * 36 + 8 * 26)($4) + sdc1 $f28, (4 * 36 + 8 * 28)($4) + sdc1 $f30, (4 * 36 + 8 * 30)($4) +#else + sdc1 $f0, (4 * 36 + 8 * 0)($4) + sdc1 $f1, (4 * 36 + 8 * 1)($4) + sdc1 $f2, (4 * 36 + 8 * 2)($4) + sdc1 $f3, (4 * 36 + 8 * 3)($4) + sdc1 $f4, (4 * 36 + 8 * 4)($4) + sdc1 $f5, (4 * 36 + 8 * 5)($4) + sdc1 $f6, (4 * 36 + 8 * 6)($4) + sdc1 $f7, (4 * 36 + 8 * 7)($4) + sdc1 $f8, (4 * 36 + 8 * 8)($4) + sdc1 $f9, (4 * 36 + 8 * 9)($4) + sdc1 $f10, (4 * 36 + 8 * 10)($4) + sdc1 $f11, (4 * 36 + 8 * 11)($4) + sdc1 $f12, (4 * 36 + 8 * 12)($4) + sdc1 $f13, (4 * 36 + 8 * 13)($4) + sdc1 $f14, (4 * 36 + 8 * 14)($4) + sdc1 $f15, (4 * 36 + 8 * 15)($4) + sdc1 $f16, (4 * 36 + 8 * 16)($4) + sdc1 $f17, (4 * 36 + 8 * 17)($4) + sdc1 $f18, (4 * 36 + 8 * 18)($4) + sdc1 $f19, (4 * 36 + 8 * 19)($4) + sdc1 $f20, (4 * 36 + 8 * 20)($4) + sdc1 $f21, (4 * 36 + 8 * 21)($4) + sdc1 $f22, (4 * 36 + 8 * 22)($4) + sdc1 $f23, (4 * 36 + 8 * 23)($4) + sdc1 $f24, (4 * 36 + 8 * 24)($4) + sdc1 $f25, (4 * 36 + 8 * 25)($4) + sdc1 $f26, (4 * 36 + 8 * 26)($4) + sdc1 $f27, (4 * 36 + 8 * 27)($4) + sdc1 $f28, (4 * 36 + 8 * 28)($4) + sdc1 $f29, (4 * 36 + 8 * 29)($4) + sdc1 $f30, (4 * 36 + 8 * 30)($4) + sdc1 $f31, (4 * 36 + 8 * 31)($4) +#endif +#endif + jr $31 + # return UNW_ESUCCESS + or $2, $0, $0 + .set pop + +#elif defined(__mips64) + +# +# extern int __unw_getcontext(unw_context_t* thread_state) +# +# On entry: +# thread_state pointer is in a0 ($4) +# +DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) + .set push + .set noat + .set noreorder + .set nomacro + sd $1, (8 * 1)($4) + sd $2, (8 * 2)($4) + sd $3, (8 * 3)($4) + sd $4, (8 * 4)($4) + sd $5, (8 * 5)($4) + sd $6, (8 * 6)($4) + sd $7, (8 * 7)($4) + sd $8, (8 * 8)($4) + sd $9, (8 * 9)($4) + sd $10, (8 * 10)($4) + sd $11, (8 * 11)($4) + sd $12, (8 * 12)($4) + sd $13, (8 * 13)($4) + sd $14, (8 * 14)($4) + sd $15, (8 * 15)($4) + sd $16, (8 * 16)($4) + sd $17, (8 * 17)($4) + sd $18, (8 * 18)($4) + sd $19, (8 * 19)($4) + sd $20, (8 * 20)($4) + sd $21, (8 * 21)($4) + sd $22, (8 * 22)($4) + sd $23, (8 * 23)($4) + sd $24, (8 * 24)($4) + sd $25, (8 * 25)($4) + sd $26, (8 * 26)($4) + sd $27, (8 * 27)($4) + sd $28, (8 * 28)($4) + sd $29, (8 * 29)($4) + sd $30, (8 * 30)($4) + sd $31, (8 * 31)($4) + # Store return address to pc + sd $31, (8 * 32)($4) + # hi and lo + mfhi $8 + sd $8, (8 * 33)($4) + mflo $8 + sd $8, (8 * 34)($4) +#ifdef __mips_hard_float + sdc1 $f0, (8 * 35)($4) + sdc1 $f1, (8 * 36)($4) + sdc1 $f2, (8 * 37)($4) + sdc1 $f3, (8 * 38)($4) + sdc1 $f4, (8 * 39)($4) + sdc1 $f5, (8 * 40)($4) + sdc1 $f6, (8 * 41)($4) + sdc1 $f7, (8 * 42)($4) + sdc1 $f8, (8 * 43)($4) + sdc1 $f9, (8 * 44)($4) + sdc1 $f10, (8 * 45)($4) + sdc1 $f11, (8 * 46)($4) + sdc1 $f12, (8 * 47)($4) + sdc1 $f13, (8 * 48)($4) + sdc1 $f14, (8 * 49)($4) + sdc1 $f15, (8 * 50)($4) + sdc1 $f16, (8 * 51)($4) + sdc1 $f17, (8 * 52)($4) + sdc1 $f18, (8 * 53)($4) + sdc1 $f19, (8 * 54)($4) + sdc1 $f20, (8 * 55)($4) + sdc1 $f21, (8 * 56)($4) + sdc1 $f22, (8 * 57)($4) + sdc1 $f23, (8 * 58)($4) + sdc1 $f24, (8 * 59)($4) + sdc1 $f25, (8 * 60)($4) + sdc1 $f26, (8 * 61)($4) + sdc1 $f27, (8 * 62)($4) + sdc1 $f28, (8 * 63)($4) + sdc1 $f29, (8 * 64)($4) + sdc1 $f30, (8 * 65)($4) + sdc1 $f31, (8 * 66)($4) +#endif + jr $31 + # return UNW_ESUCCESS + or $2, $0, $0 + .set pop + +# elif defined(__mips__) + +# +# extern int __unw_getcontext(unw_context_t* thread_state) +# +# Just trap for the time being. +DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) + teq $0, $0 + +#elif defined(__powerpc64__) + +// +// extern int __unw_getcontext(unw_context_t* thread_state) +// +// On entry: +// thread_state pointer is in r3 +// +DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) + +// store register (GPR) +#define PPC64_STR(n) \ + std n, (8 * (n + 2))(3) + + // save GPRs + PPC64_STR(0) + mflr 0 + std 0, PPC64_OFFS_SRR0(3) // store lr as ssr0 + PPC64_STR(1) + PPC64_STR(2) + PPC64_STR(3) + PPC64_STR(4) + PPC64_STR(5) + PPC64_STR(6) + PPC64_STR(7) + PPC64_STR(8) + PPC64_STR(9) + PPC64_STR(10) + PPC64_STR(11) + PPC64_STR(12) + PPC64_STR(13) + PPC64_STR(14) + PPC64_STR(15) + PPC64_STR(16) + PPC64_STR(17) + PPC64_STR(18) + PPC64_STR(19) + PPC64_STR(20) + PPC64_STR(21) + PPC64_STR(22) + PPC64_STR(23) + PPC64_STR(24) + PPC64_STR(25) + PPC64_STR(26) + PPC64_STR(27) + PPC64_STR(28) + PPC64_STR(29) + PPC64_STR(30) + PPC64_STR(31) + + mfcr 0 + std 0, PPC64_OFFS_CR(3) + mfxer 0 + std 0, PPC64_OFFS_XER(3) + mflr 0 + std 0, PPC64_OFFS_LR(3) + mfctr 0 + std 0, PPC64_OFFS_CTR(3) + mfvrsave 0 + std 0, PPC64_OFFS_VRSAVE(3) + +#if defined(__VSX__) + // save VS registers + // (note that this also saves floating point registers and V registers, + // because part of VS is mapped to these registers) + + addi 4, 3, PPC64_OFFS_FP + +// store VS register +#define PPC64_STVS(n) \ + stxvd2x n, 0, 4 ;\ + addi 4, 4, 16 + + PPC64_STVS(0) + PPC64_STVS(1) + PPC64_STVS(2) + PPC64_STVS(3) + PPC64_STVS(4) + PPC64_STVS(5) + PPC64_STVS(6) + PPC64_STVS(7) + PPC64_STVS(8) + PPC64_STVS(9) + PPC64_STVS(10) + PPC64_STVS(11) + PPC64_STVS(12) + PPC64_STVS(13) + PPC64_STVS(14) + PPC64_STVS(15) + PPC64_STVS(16) + PPC64_STVS(17) + PPC64_STVS(18) + PPC64_STVS(19) + PPC64_STVS(20) + PPC64_STVS(21) + PPC64_STVS(22) + PPC64_STVS(23) + PPC64_STVS(24) + PPC64_STVS(25) + PPC64_STVS(26) + PPC64_STVS(27) + PPC64_STVS(28) + PPC64_STVS(29) + PPC64_STVS(30) + PPC64_STVS(31) + PPC64_STVS(32) + PPC64_STVS(33) + PPC64_STVS(34) + PPC64_STVS(35) + PPC64_STVS(36) + PPC64_STVS(37) + PPC64_STVS(38) + PPC64_STVS(39) + PPC64_STVS(40) + PPC64_STVS(41) + PPC64_STVS(42) + PPC64_STVS(43) + PPC64_STVS(44) + PPC64_STVS(45) + PPC64_STVS(46) + PPC64_STVS(47) + PPC64_STVS(48) + PPC64_STVS(49) + PPC64_STVS(50) + PPC64_STVS(51) + PPC64_STVS(52) + PPC64_STVS(53) + PPC64_STVS(54) + PPC64_STVS(55) + PPC64_STVS(56) + PPC64_STVS(57) + PPC64_STVS(58) + PPC64_STVS(59) + PPC64_STVS(60) + PPC64_STVS(61) + PPC64_STVS(62) + PPC64_STVS(63) + +#else + +// store FP register +#define PPC64_STF(n) \ + stfd n, (PPC64_OFFS_FP + n * 16)(3) + + // save float registers + PPC64_STF(0) + PPC64_STF(1) + PPC64_STF(2) + PPC64_STF(3) + PPC64_STF(4) + PPC64_STF(5) + PPC64_STF(6) + PPC64_STF(7) + PPC64_STF(8) + PPC64_STF(9) + PPC64_STF(10) + PPC64_STF(11) + PPC64_STF(12) + PPC64_STF(13) + PPC64_STF(14) + PPC64_STF(15) + PPC64_STF(16) + PPC64_STF(17) + PPC64_STF(18) + PPC64_STF(19) + PPC64_STF(20) + PPC64_STF(21) + PPC64_STF(22) + PPC64_STF(23) + PPC64_STF(24) + PPC64_STF(25) + PPC64_STF(26) + PPC64_STF(27) + PPC64_STF(28) + PPC64_STF(29) + PPC64_STF(30) + PPC64_STF(31) + +#if defined(__ALTIVEC__) + // save vector registers + + // Use 16-bytes below the stack pointer as an + // aligned buffer to save each vector register. + // Note that the stack pointer is always 16-byte aligned. + subi 4, 1, 16 + +#define PPC64_STV_UNALIGNED(n) \ + stvx n, 0, 4 ;\ + ld 5, 0(4) ;\ + std 5, (PPC64_OFFS_V + n * 16)(3) ;\ + ld 5, 8(4) ;\ + std 5, (PPC64_OFFS_V + n * 16 + 8)(3) + + PPC64_STV_UNALIGNED(0) + PPC64_STV_UNALIGNED(1) + PPC64_STV_UNALIGNED(2) + PPC64_STV_UNALIGNED(3) + PPC64_STV_UNALIGNED(4) + PPC64_STV_UNALIGNED(5) + PPC64_STV_UNALIGNED(6) + PPC64_STV_UNALIGNED(7) + PPC64_STV_UNALIGNED(8) + PPC64_STV_UNALIGNED(9) + PPC64_STV_UNALIGNED(10) + PPC64_STV_UNALIGNED(11) + PPC64_STV_UNALIGNED(12) + PPC64_STV_UNALIGNED(13) + PPC64_STV_UNALIGNED(14) + PPC64_STV_UNALIGNED(15) + PPC64_STV_UNALIGNED(16) + PPC64_STV_UNALIGNED(17) + PPC64_STV_UNALIGNED(18) + PPC64_STV_UNALIGNED(19) + PPC64_STV_UNALIGNED(20) + PPC64_STV_UNALIGNED(21) + PPC64_STV_UNALIGNED(22) + PPC64_STV_UNALIGNED(23) + PPC64_STV_UNALIGNED(24) + PPC64_STV_UNALIGNED(25) + PPC64_STV_UNALIGNED(26) + PPC64_STV_UNALIGNED(27) + PPC64_STV_UNALIGNED(28) + PPC64_STV_UNALIGNED(29) + PPC64_STV_UNALIGNED(30) + PPC64_STV_UNALIGNED(31) + +#endif +#endif + + li 3, 0 // return UNW_ESUCCESS + blr + + +#elif defined(__ppc__) + +// +// extern int unw_getcontext(unw_context_t* thread_state) +// +// On entry: +// thread_state pointer is in r3 +// +DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) + stw 0, 8(3) + mflr 0 + stw 0, 0(3) // store lr as ssr0 + stw 1, 12(3) + stw 2, 16(3) + stw 3, 20(3) + stw 4, 24(3) + stw 5, 28(3) + stw 6, 32(3) + stw 7, 36(3) + stw 8, 40(3) + stw 9, 44(3) + stw 10, 48(3) + stw 11, 52(3) + stw 12, 56(3) + stw 13, 60(3) + stw 14, 64(3) + stw 15, 68(3) + stw 16, 72(3) + stw 17, 76(3) + stw 18, 80(3) + stw 19, 84(3) + stw 20, 88(3) + stw 21, 92(3) + stw 22, 96(3) + stw 23,100(3) + stw 24,104(3) + stw 25,108(3) + stw 26,112(3) + stw 27,116(3) + stw 28,120(3) + stw 29,124(3) + stw 30,128(3) + stw 31,132(3) + + // save VRSave register + mfspr 0, 256 + stw 0, 156(3) + // save CR registers + mfcr 0 + stw 0, 136(3) + // save CTR register + mfctr 0 + stw 0, 148(3) + +#if !defined(__NO_FPRS__) + // save float registers + stfd 0, 160(3) + stfd 1, 168(3) + stfd 2, 176(3) + stfd 3, 184(3) + stfd 4, 192(3) + stfd 5, 200(3) + stfd 6, 208(3) + stfd 7, 216(3) + stfd 8, 224(3) + stfd 9, 232(3) + stfd 10,240(3) + stfd 11,248(3) + stfd 12,256(3) + stfd 13,264(3) + stfd 14,272(3) + stfd 15,280(3) + stfd 16,288(3) + stfd 17,296(3) + stfd 18,304(3) + stfd 19,312(3) + stfd 20,320(3) + stfd 21,328(3) + stfd 22,336(3) + stfd 23,344(3) + stfd 24,352(3) + stfd 25,360(3) + stfd 26,368(3) + stfd 27,376(3) + stfd 28,384(3) + stfd 29,392(3) + stfd 30,400(3) + stfd 31,408(3) +#endif + +#if defined(__ALTIVEC__) + // save vector registers + + subi 4, 1, 16 + rlwinm 4, 4, 0, 0, 27 // mask low 4-bits + // r4 is now a 16-byte aligned pointer into the red zone + +#define SAVE_VECTOR_UNALIGNED(_vec, _offset) \ + stvx _vec, 0, 4 SEPARATOR \ + lwz 5, 0(4) SEPARATOR \ + stw 5, _offset(3) SEPARATOR \ + lwz 5, 4(4) SEPARATOR \ + stw 5, _offset+4(3) SEPARATOR \ + lwz 5, 8(4) SEPARATOR \ + stw 5, _offset+8(3) SEPARATOR \ + lwz 5, 12(4) SEPARATOR \ + stw 5, _offset+12(3) + + SAVE_VECTOR_UNALIGNED( 0, 424+0x000) + SAVE_VECTOR_UNALIGNED( 1, 424+0x010) + SAVE_VECTOR_UNALIGNED( 2, 424+0x020) + SAVE_VECTOR_UNALIGNED( 3, 424+0x030) + SAVE_VECTOR_UNALIGNED( 4, 424+0x040) + SAVE_VECTOR_UNALIGNED( 5, 424+0x050) + SAVE_VECTOR_UNALIGNED( 6, 424+0x060) + SAVE_VECTOR_UNALIGNED( 7, 424+0x070) + SAVE_VECTOR_UNALIGNED( 8, 424+0x080) + SAVE_VECTOR_UNALIGNED( 9, 424+0x090) + SAVE_VECTOR_UNALIGNED(10, 424+0x0A0) + SAVE_VECTOR_UNALIGNED(11, 424+0x0B0) + SAVE_VECTOR_UNALIGNED(12, 424+0x0C0) + SAVE_VECTOR_UNALIGNED(13, 424+0x0D0) + SAVE_VECTOR_UNALIGNED(14, 424+0x0E0) + SAVE_VECTOR_UNALIGNED(15, 424+0x0F0) + SAVE_VECTOR_UNALIGNED(16, 424+0x100) + SAVE_VECTOR_UNALIGNED(17, 424+0x110) + SAVE_VECTOR_UNALIGNED(18, 424+0x120) + SAVE_VECTOR_UNALIGNED(19, 424+0x130) + SAVE_VECTOR_UNALIGNED(20, 424+0x140) + SAVE_VECTOR_UNALIGNED(21, 424+0x150) + SAVE_VECTOR_UNALIGNED(22, 424+0x160) + SAVE_VECTOR_UNALIGNED(23, 424+0x170) + SAVE_VECTOR_UNALIGNED(24, 424+0x180) + SAVE_VECTOR_UNALIGNED(25, 424+0x190) + SAVE_VECTOR_UNALIGNED(26, 424+0x1A0) + SAVE_VECTOR_UNALIGNED(27, 424+0x1B0) + SAVE_VECTOR_UNALIGNED(28, 424+0x1C0) + SAVE_VECTOR_UNALIGNED(29, 424+0x1D0) + SAVE_VECTOR_UNALIGNED(30, 424+0x1E0) + SAVE_VECTOR_UNALIGNED(31, 424+0x1F0) +#endif + + li 3, 0 // return UNW_ESUCCESS + blr + + +#elif defined(__aarch64__) + +// +// extern int __unw_getcontext(unw_context_t* thread_state) +// +// On entry: +// thread_state pointer is in x0 +// + .p2align 2 +DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) + stp x0, x1, [x0, #0x000] + stp x2, x3, [x0, #0x010] + stp x4, x5, [x0, #0x020] + stp x6, x7, [x0, #0x030] + stp x8, x9, [x0, #0x040] + stp x10,x11, [x0, #0x050] + stp x12,x13, [x0, #0x060] + stp x14,x15, [x0, #0x070] + stp x16,x17, [x0, #0x080] + stp x18,x19, [x0, #0x090] + stp x20,x21, [x0, #0x0A0] + stp x22,x23, [x0, #0x0B0] + stp x24,x25, [x0, #0x0C0] + stp x26,x27, [x0, #0x0D0] + stp x28,x29, [x0, #0x0E0] + str x30, [x0, #0x0F0] + mov x1,sp + str x1, [x0, #0x0F8] + str x30, [x0, #0x100] // store return address as pc + // skip cpsr + stp d0, d1, [x0, #0x110] + stp d2, d3, [x0, #0x120] + stp d4, d5, [x0, #0x130] + stp d6, d7, [x0, #0x140] + stp d8, d9, [x0, #0x150] + stp d10,d11, [x0, #0x160] + stp d12,d13, [x0, #0x170] + stp d14,d15, [x0, #0x180] + stp d16,d17, [x0, #0x190] + stp d18,d19, [x0, #0x1A0] + stp d20,d21, [x0, #0x1B0] + stp d22,d23, [x0, #0x1C0] + stp d24,d25, [x0, #0x1D0] + stp d26,d27, [x0, #0x1E0] + stp d28,d29, [x0, #0x1F0] + str d30, [x0, #0x200] + str d31, [x0, #0x208] + mov x0, #0 // return UNW_ESUCCESS + ret + +#elif defined(__arm__) && !defined(__APPLE__) + +#if !defined(__ARM_ARCH_ISA_ARM) +#if (__ARM_ARCH_ISA_THUMB == 2) + .syntax unified +#endif + .thumb +#endif + +@ +@ extern int __unw_getcontext(unw_context_t* thread_state) +@ +@ On entry: +@ thread_state pointer is in r0 +@ +@ Per EHABI #4.7 this only saves the core integer registers. +@ EHABI #7.4.5 notes that in general all VRS registers should be restored +@ however this is very hard to do for VFP registers because it is unknown +@ to the library how many registers are implemented by the architecture. +@ Instead, VFP registers are demand saved by logic external to __unw_getcontext. +@ + .p2align 2 +DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) +#if !defined(__ARM_ARCH_ISA_ARM) && __ARM_ARCH_ISA_THUMB == 1 + stm r0!, {r0-r7} + mov r1, r8 + mov r2, r9 + mov r3, r10 + stm r0!, {r1-r3} + mov r1, r11 + mov r2, sp + mov r3, lr + str r1, [r0, #0] @ r11 + @ r12 does not need storing, it it the intra-procedure-call scratch register + str r2, [r0, #8] @ sp + str r3, [r0, #12] @ lr + str r3, [r0, #16] @ store return address as pc + @ T1 does not have a non-cpsr-clobbering register-zeroing instruction. + @ It is safe to use here though because we are about to return, and cpsr is + @ not expected to be preserved. + movs r0, #0 @ return UNW_ESUCCESS +#else + @ 32bit thumb-2 restrictions for stm: + @ . the sp (r13) cannot be in the list + @ . the pc (r15) cannot be in the list in an STM instruction + stm r0, {r0-r12} + str sp, [r0, #52] + str lr, [r0, #56] + str lr, [r0, #60] @ store return address as pc + mov r0, #0 @ return UNW_ESUCCESS +#endif + JMP(lr) + +@ +@ static void libunwind::Registers_arm::saveVFPWithFSTMD(unw_fpreg_t* values) +@ +@ On entry: +@ values pointer is in r0 +@ + .p2align 2 +#if defined(__ELF__) + .fpu vfpv3-d16 +#endif +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_arm16saveVFPWithFSTMDEPv) + vstmia r0, {d0-d15} + JMP(lr) + +@ +@ static void libunwind::Registers_arm::saveVFPWithFSTMX(unw_fpreg_t* values) +@ +@ On entry: +@ values pointer is in r0 +@ + .p2align 2 +#if defined(__ELF__) + .fpu vfpv3-d16 +#endif +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_arm16saveVFPWithFSTMXEPv) + vstmia r0, {d0-d15} @ fstmiax is deprecated in ARMv7+ and now behaves like vstmia + JMP(lr) + +@ +@ static void libunwind::Registers_arm::saveVFPv3(unw_fpreg_t* values) +@ +@ On entry: +@ values pointer is in r0 +@ + .p2align 2 +#if defined(__ELF__) + .fpu vfpv3 +#endif +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_arm9saveVFPv3EPv) + @ VFP and iwMMX instructions are only available when compiling with the flags + @ that enable them. We do not want to do that in the library (because we do not + @ want the compiler to generate instructions that access those) but this is + @ only accessed if the personality routine needs these registers. Use of + @ these registers implies they are, actually, available on the target, so + @ it's ok to execute. + @ So, generate the instructions using the corresponding coprocessor mnemonic. + vstmia r0, {d16-d31} + JMP(lr) + +#if defined(_LIBUNWIND_ARM_WMMX) + +@ +@ static void libunwind::Registers_arm::saveiWMMX(unw_fpreg_t* values) +@ +@ On entry: +@ values pointer is in r0 +@ + .p2align 2 +#if defined(__ELF__) + .arch armv5te +#endif +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_arm9saveiWMMXEPv) + stcl p1, cr0, [r0], #8 @ wstrd wR0, [r0], #8 + stcl p1, cr1, [r0], #8 @ wstrd wR1, [r0], #8 + stcl p1, cr2, [r0], #8 @ wstrd wR2, [r0], #8 + stcl p1, cr3, [r0], #8 @ wstrd wR3, [r0], #8 + stcl p1, cr4, [r0], #8 @ wstrd wR4, [r0], #8 + stcl p1, cr5, [r0], #8 @ wstrd wR5, [r0], #8 + stcl p1, cr6, [r0], #8 @ wstrd wR6, [r0], #8 + stcl p1, cr7, [r0], #8 @ wstrd wR7, [r0], #8 + stcl p1, cr8, [r0], #8 @ wstrd wR8, [r0], #8 + stcl p1, cr9, [r0], #8 @ wstrd wR9, [r0], #8 + stcl p1, cr10, [r0], #8 @ wstrd wR10, [r0], #8 + stcl p1, cr11, [r0], #8 @ wstrd wR11, [r0], #8 + stcl p1, cr12, [r0], #8 @ wstrd wR12, [r0], #8 + stcl p1, cr13, [r0], #8 @ wstrd wR13, [r0], #8 + stcl p1, cr14, [r0], #8 @ wstrd wR14, [r0], #8 + stcl p1, cr15, [r0], #8 @ wstrd wR15, [r0], #8 + JMP(lr) + +@ +@ static void libunwind::Registers_arm::saveiWMMXControl(unw_uint32_t* values) +@ +@ On entry: +@ values pointer is in r0 +@ + .p2align 2 +#if defined(__ELF__) + .arch armv5te +#endif +DEFINE_LIBUNWIND_FUNCTION(_ZN9libunwind13Registers_arm16saveiWMMXControlEPj) + stc2 p1, cr8, [r0], #4 @ wstrw wCGR0, [r0], #4 + stc2 p1, cr9, [r0], #4 @ wstrw wCGR1, [r0], #4 + stc2 p1, cr10, [r0], #4 @ wstrw wCGR2, [r0], #4 + stc2 p1, cr11, [r0], #4 @ wstrw wCGR3, [r0], #4 + JMP(lr) + +#endif + +#elif defined(__or1k__) + +# +# extern int __unw_getcontext(unw_context_t* thread_state) +# +# On entry: +# thread_state pointer is in r3 +# +DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) + l.sw 0(r3), r0 + l.sw 4(r3), r1 + l.sw 8(r3), r2 + l.sw 12(r3), r3 + l.sw 16(r3), r4 + l.sw 20(r3), r5 + l.sw 24(r3), r6 + l.sw 28(r3), r7 + l.sw 32(r3), r8 + l.sw 36(r3), r9 + l.sw 40(r3), r10 + l.sw 44(r3), r11 + l.sw 48(r3), r12 + l.sw 52(r3), r13 + l.sw 56(r3), r14 + l.sw 60(r3), r15 + l.sw 64(r3), r16 + l.sw 68(r3), r17 + l.sw 72(r3), r18 + l.sw 76(r3), r19 + l.sw 80(r3), r20 + l.sw 84(r3), r21 + l.sw 88(r3), r22 + l.sw 92(r3), r23 + l.sw 96(r3), r24 + l.sw 100(r3), r25 + l.sw 104(r3), r26 + l.sw 108(r3), r27 + l.sw 112(r3), r28 + l.sw 116(r3), r29 + l.sw 120(r3), r30 + l.sw 124(r3), r31 + # store ra to pc + l.sw 128(r3), r9 + # zero epcr + l.sw 132(r3), r0 + +#elif defined(__hexagon__) +# +# extern int unw_getcontext(unw_context_t* thread_state) +# +# On entry: +# thread_state pointer is in r0 +# +#define OFFSET(offset) (offset/4) +DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) + memw(r0+#32) = r8 + memw(r0+#36) = r9 + memw(r0+#40) = r10 + memw(r0+#44) = r11 + + memw(r0+#48) = r12 + memw(r0+#52) = r13 + memw(r0+#56) = r14 + memw(r0+#60) = r15 + + memw(r0+#64) = r16 + memw(r0+#68) = r17 + memw(r0+#72) = r18 + memw(r0+#76) = r19 + + memw(r0+#80) = r20 + memw(r0+#84) = r21 + memw(r0+#88) = r22 + memw(r0+#92) = r23 + + memw(r0+#96) = r24 + memw(r0+#100) = r25 + memw(r0+#104) = r26 + memw(r0+#108) = r27 + + memw(r0+#112) = r28 + memw(r0+#116) = r29 + memw(r0+#120) = r30 + memw(r0+#124) = r31 + r1 = c4 // Predicate register + memw(r0+#128) = r1 + r1 = memw(r30) // *FP == Saved FP + r1 = r31 + memw(r0+#132) = r1 + + jumpr r31 + +#elif defined(__sparc__) + +# +# extern int __unw_getcontext(unw_context_t* thread_state) +# +# On entry: +# thread_state pointer is in o0 +# +DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) + ta 3 + add %o7, 8, %o7 + std %g0, [%o0 + 0] + std %g2, [%o0 + 8] + std %g4, [%o0 + 16] + std %g6, [%o0 + 24] + std %o0, [%o0 + 32] + std %o2, [%o0 + 40] + std %o4, [%o0 + 48] + std %o6, [%o0 + 56] + std %l0, [%o0 + 64] + std %l2, [%o0 + 72] + std %l4, [%o0 + 80] + std %l6, [%o0 + 88] + std %i0, [%o0 + 96] + std %i2, [%o0 + 104] + std %i4, [%o0 + 112] + std %i6, [%o0 + 120] + jmp %o7 + clr %o0 // return UNW_ESUCCESS + +#elif defined(__riscv) + +# +# extern int __unw_getcontext(unw_context_t* thread_state) +# +# On entry: +# thread_state pointer is in a0 +# +DEFINE_LIBUNWIND_FUNCTION(__unw_getcontext) + ISTORE x1, (RISCV_ISIZE * 0)(a0) // store ra as pc + ISTORE x1, (RISCV_ISIZE * 1)(a0) + ISTORE x2, (RISCV_ISIZE * 2)(a0) + ISTORE x3, (RISCV_ISIZE * 3)(a0) + ISTORE x4, (RISCV_ISIZE * 4)(a0) + ISTORE x5, (RISCV_ISIZE * 5)(a0) + ISTORE x6, (RISCV_ISIZE * 6)(a0) + ISTORE x7, (RISCV_ISIZE * 7)(a0) + ISTORE x8, (RISCV_ISIZE * 8)(a0) + ISTORE x9, (RISCV_ISIZE * 9)(a0) + ISTORE x10, (RISCV_ISIZE * 10)(a0) + ISTORE x11, (RISCV_ISIZE * 11)(a0) + ISTORE x12, (RISCV_ISIZE * 12)(a0) + ISTORE x13, (RISCV_ISIZE * 13)(a0) + ISTORE x14, (RISCV_ISIZE * 14)(a0) + ISTORE x15, (RISCV_ISIZE * 15)(a0) + ISTORE x16, (RISCV_ISIZE * 16)(a0) + ISTORE x17, (RISCV_ISIZE * 17)(a0) + ISTORE x18, (RISCV_ISIZE * 18)(a0) + ISTORE x19, (RISCV_ISIZE * 19)(a0) + ISTORE x20, (RISCV_ISIZE * 20)(a0) + ISTORE x21, (RISCV_ISIZE * 21)(a0) + ISTORE x22, (RISCV_ISIZE * 22)(a0) + ISTORE x23, (RISCV_ISIZE * 23)(a0) + ISTORE x24, (RISCV_ISIZE * 24)(a0) + ISTORE x25, (RISCV_ISIZE * 25)(a0) + ISTORE x26, (RISCV_ISIZE * 26)(a0) + ISTORE x27, (RISCV_ISIZE * 27)(a0) + ISTORE x28, (RISCV_ISIZE * 28)(a0) + ISTORE x29, (RISCV_ISIZE * 29)(a0) + ISTORE x30, (RISCV_ISIZE * 30)(a0) + ISTORE x31, (RISCV_ISIZE * 31)(a0) + +# if defined(__riscv_flen) + FSTORE f0, (RISCV_FOFFSET + RISCV_FSIZE * 0)(a0) + FSTORE f1, (RISCV_FOFFSET + RISCV_FSIZE * 1)(a0) + FSTORE f2, (RISCV_FOFFSET + RISCV_FSIZE * 2)(a0) + FSTORE f3, (RISCV_FOFFSET + RISCV_FSIZE * 3)(a0) + FSTORE f4, (RISCV_FOFFSET + RISCV_FSIZE * 4)(a0) + FSTORE f5, (RISCV_FOFFSET + RISCV_FSIZE * 5)(a0) + FSTORE f6, (RISCV_FOFFSET + RISCV_FSIZE * 6)(a0) + FSTORE f7, (RISCV_FOFFSET + RISCV_FSIZE * 7)(a0) + FSTORE f8, (RISCV_FOFFSET + RISCV_FSIZE * 8)(a0) + FSTORE f9, (RISCV_FOFFSET + RISCV_FSIZE * 9)(a0) + FSTORE f10, (RISCV_FOFFSET + RISCV_FSIZE * 10)(a0) + FSTORE f11, (RISCV_FOFFSET + RISCV_FSIZE * 11)(a0) + FSTORE f12, (RISCV_FOFFSET + RISCV_FSIZE * 12)(a0) + FSTORE f13, (RISCV_FOFFSET + RISCV_FSIZE * 13)(a0) + FSTORE f14, (RISCV_FOFFSET + RISCV_FSIZE * 14)(a0) + FSTORE f15, (RISCV_FOFFSET + RISCV_FSIZE * 15)(a0) + FSTORE f16, (RISCV_FOFFSET + RISCV_FSIZE * 16)(a0) + FSTORE f17, (RISCV_FOFFSET + RISCV_FSIZE * 17)(a0) + FSTORE f18, (RISCV_FOFFSET + RISCV_FSIZE * 18)(a0) + FSTORE f19, (RISCV_FOFFSET + RISCV_FSIZE * 19)(a0) + FSTORE f20, (RISCV_FOFFSET + RISCV_FSIZE * 20)(a0) + FSTORE f21, (RISCV_FOFFSET + RISCV_FSIZE * 21)(a0) + FSTORE f22, (RISCV_FOFFSET + RISCV_FSIZE * 22)(a0) + FSTORE f23, (RISCV_FOFFSET + RISCV_FSIZE * 23)(a0) + FSTORE f24, (RISCV_FOFFSET + RISCV_FSIZE * 24)(a0) + FSTORE f25, (RISCV_FOFFSET + RISCV_FSIZE * 25)(a0) + FSTORE f26, (RISCV_FOFFSET + RISCV_FSIZE * 26)(a0) + FSTORE f27, (RISCV_FOFFSET + RISCV_FSIZE * 27)(a0) + FSTORE f28, (RISCV_FOFFSET + RISCV_FSIZE * 28)(a0) + FSTORE f29, (RISCV_FOFFSET + RISCV_FSIZE * 29)(a0) + FSTORE f30, (RISCV_FOFFSET + RISCV_FSIZE * 30)(a0) + FSTORE f31, (RISCV_FOFFSET + RISCV_FSIZE * 31)(a0) +# endif + + li a0, 0 // return UNW_ESUCCESS + ret // jump to ra +#endif + + WEAK_ALIAS(__unw_getcontext, unw_getcontext) + +#endif /* !defined(__USING_SJLJ_EXCEPTIONS__) */ + +NO_EXEC_STACK_DIRECTIVE diff --git a/shared/sentry/external/crashpad/libunwind/src/Unwind_AppleExtras.cpp b/shared/sentry/external/crashpad/libunwind/src/Unwind_AppleExtras.cpp new file mode 100644 index 000000000..e3d41ca2b --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/src/Unwind_AppleExtras.cpp @@ -0,0 +1,113 @@ +//===--------------------- Unwind_AppleExtras.cpp -------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +//===----------------------------------------------------------------------===// + +#include "config.h" + + +// static linker symbols to prevent wrong two level namespace for _Unwind symbols +#if defined(__arm__) + #define NOT_HERE_BEFORE_5_0(sym) \ + extern const char sym##_tmp30 __asm("$ld$hide$os3.0$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp30 = 0; \ + extern const char sym##_tmp31 __asm("$ld$hide$os3.1$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp31 = 0; \ + extern const char sym##_tmp32 __asm("$ld$hide$os3.2$_" #sym );\ + __attribute__((visibility("default"))) const char sym##_tmp32 = 0; \ + extern const char sym##_tmp40 __asm("$ld$hide$os4.0$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp40 = 0; \ + extern const char sym##_tmp41 __asm("$ld$hide$os4.1$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp41 = 0; \ + extern const char sym##_tmp42 __asm("$ld$hide$os4.2$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp42 = 0; \ + extern const char sym##_tmp43 __asm("$ld$hide$os4.3$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp43 = 0; +#elif defined(__aarch64__) + #define NOT_HERE_BEFORE_10_6(sym) + #define NEVER_HERE(sym) +#else + #define NOT_HERE_BEFORE_10_6(sym) \ + extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \ + extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp5 = 0; + #define NEVER_HERE(sym) \ + extern const char sym##_tmp4 __asm("$ld$hide$os10.4$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp4 = 0; \ + extern const char sym##_tmp5 __asm("$ld$hide$os10.5$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp5 = 0; \ + extern const char sym##_tmp6 __asm("$ld$hide$os10.6$_" #sym ); \ + __attribute__((visibility("default"))) const char sym##_tmp6 = 0; +#endif + + +#if defined(_LIBUNWIND_BUILD_ZERO_COST_APIS) + +// +// symbols in libSystem.dylib in 10.6 and later, but are in libgcc_s.dylib in +// earlier versions +// +NOT_HERE_BEFORE_10_6(_Unwind_DeleteException) +NOT_HERE_BEFORE_10_6(_Unwind_Find_FDE) +NOT_HERE_BEFORE_10_6(_Unwind_ForcedUnwind) +NOT_HERE_BEFORE_10_6(_Unwind_GetGR) +NOT_HERE_BEFORE_10_6(_Unwind_GetIP) +NOT_HERE_BEFORE_10_6(_Unwind_GetLanguageSpecificData) +NOT_HERE_BEFORE_10_6(_Unwind_GetRegionStart) +NOT_HERE_BEFORE_10_6(_Unwind_RaiseException) +NOT_HERE_BEFORE_10_6(_Unwind_Resume) +NOT_HERE_BEFORE_10_6(_Unwind_SetGR) +NOT_HERE_BEFORE_10_6(_Unwind_SetIP) +NOT_HERE_BEFORE_10_6(_Unwind_Backtrace) +NOT_HERE_BEFORE_10_6(_Unwind_FindEnclosingFunction) +NOT_HERE_BEFORE_10_6(_Unwind_GetCFA) +NOT_HERE_BEFORE_10_6(_Unwind_GetDataRelBase) +NOT_HERE_BEFORE_10_6(_Unwind_GetTextRelBase) +NOT_HERE_BEFORE_10_6(_Unwind_Resume_or_Rethrow) +NOT_HERE_BEFORE_10_6(_Unwind_GetIPInfo) +NOT_HERE_BEFORE_10_6(__register_frame) +NOT_HERE_BEFORE_10_6(__deregister_frame) + +// +// symbols in libSystem.dylib for compatibility, but we don't want any new code +// using them +// +NEVER_HERE(__register_frame_info_bases) +NEVER_HERE(__register_frame_info) +NEVER_HERE(__register_frame_info_table_bases) +NEVER_HERE(__register_frame_info_table) +NEVER_HERE(__register_frame_table) +NEVER_HERE(__deregister_frame_info) +NEVER_HERE(__deregister_frame_info_bases) + +#endif // defined(_LIBUNWIND_BUILD_ZERO_COST_APIS) + + + + +#if defined(_LIBUNWIND_BUILD_SJLJ_APIS) +// +// symbols in libSystem.dylib in iOS 5.0 and later, but are in libgcc_s.dylib in +// earlier versions +// +NOT_HERE_BEFORE_5_0(_Unwind_GetLanguageSpecificData) +NOT_HERE_BEFORE_5_0(_Unwind_GetRegionStart) +NOT_HERE_BEFORE_5_0(_Unwind_GetIP) +NOT_HERE_BEFORE_5_0(_Unwind_SetGR) +NOT_HERE_BEFORE_5_0(_Unwind_SetIP) +NOT_HERE_BEFORE_5_0(_Unwind_DeleteException) +NOT_HERE_BEFORE_5_0(_Unwind_SjLj_Register) +NOT_HERE_BEFORE_5_0(_Unwind_GetGR) +NOT_HERE_BEFORE_5_0(_Unwind_GetIPInfo) +NOT_HERE_BEFORE_5_0(_Unwind_GetCFA) +NOT_HERE_BEFORE_5_0(_Unwind_SjLj_Resume) +NOT_HERE_BEFORE_5_0(_Unwind_SjLj_RaiseException) +NOT_HERE_BEFORE_5_0(_Unwind_SjLj_Resume_or_Rethrow) +NOT_HERE_BEFORE_5_0(_Unwind_SjLj_Unregister) + +#endif // defined(_LIBUNWIND_BUILD_SJLJ_APIS) diff --git a/shared/sentry/external/crashpad/libunwind/src/assembly.h b/shared/sentry/external/crashpad/libunwind/src/assembly.h new file mode 100644 index 000000000..e38d32336 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/src/assembly.h @@ -0,0 +1,230 @@ +/* ===-- assembly.h - libUnwind assembler support macros -------------------=== + * + * Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. + * See https://llvm.org/LICENSE.txt for license information. + * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + * + * ===----------------------------------------------------------------------=== + * + * This file defines macros for use in libUnwind assembler source. + * This file is not part of the interface of this library. + * + * ===----------------------------------------------------------------------=== + */ + +#ifndef UNWIND_ASSEMBLY_H +#define UNWIND_ASSEMBLY_H + +#if (defined(__i386__) || defined(__x86_64__)) && defined(__linux__) +#include +#define _LIBUNWIND_CET_ENDBR _CET_ENDBR +#else +#define _LIBUNWIND_CET_ENDBR +#endif + +#if defined(__powerpc64__) +#define SEPARATOR ; +#define PPC64_OFFS_SRR0 0 +#define PPC64_OFFS_CR 272 +#define PPC64_OFFS_XER 280 +#define PPC64_OFFS_LR 288 +#define PPC64_OFFS_CTR 296 +#define PPC64_OFFS_VRSAVE 304 +#define PPC64_OFFS_FP 312 +#define PPC64_OFFS_V 824 +#elif defined(__APPLE__) && defined(__aarch64__) +#define SEPARATOR %% +#elif defined(__riscv) +# define RISCV_ISIZE (__riscv_xlen / 8) +# define RISCV_FOFFSET (RISCV_ISIZE * 32) +# if defined(__riscv_flen) +# define RISCV_FSIZE (__riscv_flen / 8) +# endif + +# if __riscv_xlen == 64 +# define ILOAD ld +# define ISTORE sd +# elif __riscv_xlen == 32 +# define ILOAD lw +# define ISTORE sw +# else +# error "Unsupported __riscv_xlen" +# endif + +# if defined(__riscv_flen) +# if __riscv_flen == 64 +# define FLOAD fld +# define FSTORE fsd +# elif __riscv_flen == 32 +# define FLOAD flw +# define FSTORE fsw +# else +# error "Unsupported __riscv_flen" +# endif +# endif +# define SEPARATOR ; +#else +#define SEPARATOR ; +#endif + +#if defined(__powerpc64__) && (!defined(_CALL_ELF) || _CALL_ELF == 1) +#define PPC64_OPD1 .section .opd,"aw",@progbits SEPARATOR +#define PPC64_OPD2 SEPARATOR \ + .p2align 3 SEPARATOR \ + .quad .Lfunc_begin0 SEPARATOR \ + .quad .TOC.@tocbase SEPARATOR \ + .quad 0 SEPARATOR \ + .text SEPARATOR \ +.Lfunc_begin0: +#else +#define PPC64_OPD1 +#define PPC64_OPD2 +#endif + +#if defined(__ARM_FEATURE_BTI_DEFAULT) + .pushsection ".note.gnu.property", "a" SEPARATOR \ + .balign 8 SEPARATOR \ + .long 4 SEPARATOR \ + .long 0x10 SEPARATOR \ + .long 0x5 SEPARATOR \ + .asciz "GNU" SEPARATOR \ + .long 0xc0000000 SEPARATOR /* GNU_PROPERTY_AARCH64_FEATURE_1_AND */ \ + .long 4 SEPARATOR \ + .long 3 SEPARATOR /* GNU_PROPERTY_AARCH64_FEATURE_1_BTI AND */ \ + /* GNU_PROPERTY_AARCH64_FEATURE_1_PAC */ \ + .long 0 SEPARATOR \ + .popsection SEPARATOR +#define AARCH64_BTI bti c +#else +#define AARCH64_BTI +#endif + +#define GLUE2(a, b) a ## b +#define GLUE(a, b) GLUE2(a, b) +#define SYMBOL_NAME(name) GLUE(__USER_LABEL_PREFIX__, name) + +#if defined(__APPLE__) + +#define SYMBOL_IS_FUNC(name) +#define HIDDEN_SYMBOL(name) .private_extern name +#if defined(_LIBUNWIND_HIDE_SYMBOLS) +#define EXPORT_SYMBOL(name) HIDDEN_SYMBOL(name) +#else +#define EXPORT_SYMBOL(name) +#endif +#define WEAK_ALIAS(name, aliasname) \ + .globl SYMBOL_NAME(aliasname) SEPARATOR \ + EXPORT_SYMBOL(SYMBOL_NAME(aliasname)) SEPARATOR \ + SYMBOL_NAME(aliasname) = SYMBOL_NAME(name) + +#define NO_EXEC_STACK_DIRECTIVE + +#elif defined(__ELF__) + +#if defined(__arm__) +#define SYMBOL_IS_FUNC(name) .type name,%function +#else +#define SYMBOL_IS_FUNC(name) .type name,@function +#endif +#define HIDDEN_SYMBOL(name) .hidden name +#if defined(_LIBUNWIND_HIDE_SYMBOLS) +#define EXPORT_SYMBOL(name) HIDDEN_SYMBOL(name) +#else +#define EXPORT_SYMBOL(name) +#endif +#define WEAK_SYMBOL(name) .weak name + +#if defined(__hexagon__) +#define WEAK_ALIAS(name, aliasname) \ + EXPORT_SYMBOL(SYMBOL_NAME(aliasname)) SEPARATOR \ + WEAK_SYMBOL(SYMBOL_NAME(aliasname)) SEPARATOR \ + .equiv SYMBOL_NAME(aliasname), SYMBOL_NAME(name) +#else +#define WEAK_ALIAS(name, aliasname) \ + EXPORT_SYMBOL(SYMBOL_NAME(aliasname)) SEPARATOR \ + WEAK_SYMBOL(SYMBOL_NAME(aliasname)) SEPARATOR \ + SYMBOL_NAME(aliasname) = SYMBOL_NAME(name) +#endif + +#if defined(__GNU__) || defined(__FreeBSD__) || defined(__Fuchsia__) || \ + defined(__linux__) +#define NO_EXEC_STACK_DIRECTIVE .section .note.GNU-stack,"",%progbits +#else +#define NO_EXEC_STACK_DIRECTIVE +#endif + +#elif defined(_WIN32) + +#define SYMBOL_IS_FUNC(name) \ + .def name SEPARATOR \ + .scl 2 SEPARATOR \ + .type 32 SEPARATOR \ + .endef +#define EXPORT_SYMBOL2(name) \ + .section .drectve,"yn" SEPARATOR \ + .ascii "-export:", #name, "\0" SEPARATOR \ + .text +#if defined(_LIBUNWIND_HIDE_SYMBOLS) +#define EXPORT_SYMBOL(name) +#else +#define EXPORT_SYMBOL(name) EXPORT_SYMBOL2(name) +#endif +#define HIDDEN_SYMBOL(name) + +#if defined(__MINGW32__) +#define WEAK_ALIAS(name, aliasname) \ + .globl SYMBOL_NAME(aliasname) SEPARATOR \ + EXPORT_SYMBOL(aliasname) SEPARATOR \ + SYMBOL_NAME(aliasname) = SYMBOL_NAME(name) +#else +#define WEAK_ALIAS3(name, aliasname) \ + .section .drectve,"yn" SEPARATOR \ + .ascii "-alternatename:", #aliasname, "=", #name, "\0" SEPARATOR \ + .text +#define WEAK_ALIAS2(name, aliasname) \ + WEAK_ALIAS3(name, aliasname) +#define WEAK_ALIAS(name, aliasname) \ + EXPORT_SYMBOL(SYMBOL_NAME(aliasname)) SEPARATOR \ + WEAK_ALIAS2(SYMBOL_NAME(name), SYMBOL_NAME(aliasname)) +#endif + +#define NO_EXEC_STACK_DIRECTIVE + +#elif defined(__sparc__) + +#else + +#error Unsupported target + +#endif + +#define DEFINE_LIBUNWIND_FUNCTION(name) \ + .globl SYMBOL_NAME(name) SEPARATOR \ + HIDDEN_SYMBOL(SYMBOL_NAME(name)) SEPARATOR \ + SYMBOL_IS_FUNC(SYMBOL_NAME(name)) SEPARATOR \ + PPC64_OPD1 \ + SYMBOL_NAME(name): \ + PPC64_OPD2 \ + AARCH64_BTI + +#if defined(__arm__) +#if !defined(__ARM_ARCH) +#define __ARM_ARCH 4 +#endif + +#if defined(__ARM_ARCH_4T__) || __ARM_ARCH >= 5 +#define ARM_HAS_BX +#endif + +#ifdef ARM_HAS_BX +#define JMP(r) bx r +#else +#define JMP(r) mov pc, r +#endif +#endif /* __arm__ */ + +#if defined(__ppc__) || defined(__powerpc64__) +#define PPC_LEFT_SHIFT(index) << (index) +#endif + +#endif /* UNWIND_ASSEMBLY_H */ diff --git a/shared/sentry/external/crashpad/libunwind/src/cet_unwind.h b/shared/sentry/external/crashpad/libunwind/src/cet_unwind.h new file mode 100644 index 000000000..482e0c808 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/src/cet_unwind.h @@ -0,0 +1,41 @@ +//===--------------------------- cet_unwind.h -----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +//===----------------------------------------------------------------------===// + +#ifndef LIBUNWIND_CET_UNWIND_H +#define LIBUNWIND_CET_UNWIND_H + +#include "libunwind.h" + +// Currently, CET is implemented on Linux x86 platforms. +#if defined(_LIBUNWIND_TARGET_LINUX) && defined(__CET__) && defined(__SHSTK__) +#define _LIBUNWIND_USE_CET 1 +#endif + +#if defined(_LIBUNWIND_USE_CET) +#include +#include + +#define _LIBUNWIND_POP_CET_SSP(x) \ + do { \ + unsigned long ssp = _get_ssp(); \ + if (ssp != 0) { \ + unsigned int tmp = (x); \ + while (tmp > 255) { \ + _inc_ssp(255); \ + tmp -= 255; \ + } \ + _inc_ssp(tmp); \ + } \ + } while (0) +#endif + +extern void *__libunwind_cet_get_registers(unw_cursor_t *); +extern void *__libunwind_cet_get_jump_target(); + +#endif diff --git a/shared/sentry/external/crashpad/libunwind/src/config.h b/shared/sentry/external/crashpad/libunwind/src/config.h new file mode 100644 index 000000000..2ab9d2f5e --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/src/config.h @@ -0,0 +1,241 @@ +//===----------------------------- config.h -------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Defines macros used within libunwind project. +// +//===----------------------------------------------------------------------===// + + +#ifndef LIBUNWIND_CONFIG_H +#define LIBUNWIND_CONFIG_H + +#include +#include +#include +#include + +#include <__libunwind_config.h> + +// Platform specific configuration defines. +#ifdef __APPLE__ + #if defined(FOR_DYLD) + #define _LIBUNWIND_SUPPORT_COMPACT_UNWIND 1 + #else + #define _LIBUNWIND_SUPPORT_COMPACT_UNWIND 1 + #define _LIBUNWIND_SUPPORT_DWARF_UNWIND 1 + #endif +#elif defined(_WIN32) + #ifdef __SEH__ + #define _LIBUNWIND_SUPPORT_SEH_UNWIND 1 + #else + #define _LIBUNWIND_SUPPORT_DWARF_UNWIND 1 + #endif +#elif defined(_LIBUNWIND_IS_BAREMETAL) + #if !defined(_LIBUNWIND_ARM_EHABI) + #define _LIBUNWIND_SUPPORT_DWARF_UNWIND 1 + #define _LIBUNWIND_SUPPORT_DWARF_INDEX 1 + #endif +#elif defined(__BIONIC__) && defined(_LIBUNWIND_ARM_EHABI) + // For ARM EHABI, Bionic didn't implement dl_iterate_phdr until API 21. After + // API 21, dl_iterate_phdr exists, but dl_unwind_find_exidx is much faster. + #define _LIBUNWIND_USE_DL_UNWIND_FIND_EXIDX 1 +#else + // Assume an ELF system with a dl_iterate_phdr function. + #define _LIBUNWIND_USE_DL_ITERATE_PHDR 1 + #if !defined(_LIBUNWIND_ARM_EHABI) + #define _LIBUNWIND_SUPPORT_DWARF_UNWIND 1 + #define _LIBUNWIND_SUPPORT_DWARF_INDEX 1 + #endif +#endif + +#if defined(_LIBUNWIND_HIDE_SYMBOLS) + // The CMake file passes -fvisibility=hidden to control ELF/Mach-O visibility. + #define _LIBUNWIND_EXPORT + #define _LIBUNWIND_HIDDEN +#else + #if !defined(__ELF__) && !defined(__MACH__) + #define _LIBUNWIND_EXPORT __declspec(dllexport) + #define _LIBUNWIND_HIDDEN + #else + #define _LIBUNWIND_EXPORT __attribute__((visibility("default"))) + #define _LIBUNWIND_HIDDEN __attribute__((visibility("hidden"))) + #endif +#endif + +#define STR(a) #a +#define XSTR(a) STR(a) +#define SYMBOL_NAME(name) XSTR(__USER_LABEL_PREFIX__) #name + +#if defined(__APPLE__) +#if defined(_LIBUNWIND_HIDE_SYMBOLS) +#define _LIBUNWIND_ALIAS_VISIBILITY(name) __asm__(".private_extern " name); +#else +#define _LIBUNWIND_ALIAS_VISIBILITY(name) +#endif +#define _LIBUNWIND_WEAK_ALIAS(name, aliasname) \ + __asm__(".globl " SYMBOL_NAME(aliasname)); \ + __asm__(SYMBOL_NAME(aliasname) " = " SYMBOL_NAME(name)); \ + _LIBUNWIND_ALIAS_VISIBILITY(SYMBOL_NAME(aliasname)) +#elif defined(__ELF__) +#define _LIBUNWIND_WEAK_ALIAS(name, aliasname) \ + extern "C" _LIBUNWIND_EXPORT __typeof(name) aliasname \ + __attribute__((weak, alias(#name))); +#elif defined(_WIN32) +#if defined(__MINGW32__) +#define _LIBUNWIND_WEAK_ALIAS(name, aliasname) \ + extern "C" _LIBUNWIND_EXPORT __typeof(name) aliasname \ + __attribute__((alias(#name))); +#else +#define _LIBUNWIND_WEAK_ALIAS(name, aliasname) \ + __pragma(comment(linker, "/alternatename:" SYMBOL_NAME(aliasname) "=" \ + SYMBOL_NAME(name))) \ + extern "C" _LIBUNWIND_EXPORT __typeof(name) aliasname; +#endif +#else +#error Unsupported target +#endif + +// Apple/armv7k defaults to DWARF/Compact unwinding, but its libunwind also +// needs to include the SJLJ APIs. +#if (defined(__APPLE__) && defined(__arm__)) || defined(__USING_SJLJ_EXCEPTIONS__) +#define _LIBUNWIND_BUILD_SJLJ_APIS +#endif + +#if defined(__i386__) || defined(__x86_64__) || defined(__ppc__) || defined(__ppc64__) || defined(__powerpc64__) +#define _LIBUNWIND_SUPPORT_FRAME_APIS +#endif + +#if defined(__i386__) || defined(__x86_64__) || \ + defined(__ppc__) || defined(__ppc64__) || defined(__powerpc64__) || \ + (!defined(__APPLE__) && defined(__arm__)) || \ + defined(__aarch64__) || \ + defined(__mips__) || \ + defined(__riscv) || \ + defined(__hexagon__) +#if !defined(_LIBUNWIND_BUILD_SJLJ_APIS) +#define _LIBUNWIND_BUILD_ZERO_COST_APIS +#endif +#endif + +#ifndef _LIBUNWIND_REMEMBER_HEAP_ALLOC +#if defined(_LIBUNWIND_REMEMBER_STACK_ALLOC) || defined(__APPLE__) || \ + defined(__linux__) || defined(__ANDROID__) || defined(__MINGW32__) || \ + defined(_LIBUNWIND_IS_BAREMETAL) +#define _LIBUNWIND_REMEMBER_ALLOC(_size) alloca(_size) +#define _LIBUNWIND_REMEMBER_FREE(_ptr) \ + do { \ + } while (0) +#elif defined(_WIN32) +#define _LIBUNWIND_REMEMBER_ALLOC(_size) _malloca(_size) +#define _LIBUNWIND_REMEMBER_FREE(_ptr) _freea(_ptr) +#define _LIBUNWIND_REMEMBER_CLEANUP_NEEDED +#else +#define _LIBUNWIND_REMEMBER_ALLOC(_size) malloc(_size) +#define _LIBUNWIND_REMEMBER_FREE(_ptr) free(_ptr) +#define _LIBUNWIND_REMEMBER_CLEANUP_NEEDED +#endif +#else /* _LIBUNWIND_REMEMBER_HEAP_ALLOC */ +#define _LIBUNWIND_REMEMBER_ALLOC(_size) malloc(_size) +#define _LIBUNWIND_REMEMBER_FREE(_ptr) free(_ptr) +#define _LIBUNWIND_REMEMBER_CLEANUP_NEEDED +#endif + +#if defined(NDEBUG) && defined(_LIBUNWIND_IS_BAREMETAL) +#define _LIBUNWIND_ABORT(msg) \ + do { \ + abort(); \ + } while (0) +#else +#define _LIBUNWIND_ABORT(msg) \ + do { \ + fprintf(stderr, "libunwind: %s - %s\n", __func__, msg); \ + fflush(stderr); \ + abort(); \ + } while (0) +#endif + +#if defined(NDEBUG) && defined(_LIBUNWIND_IS_BAREMETAL) +#define _LIBUNWIND_LOG0(msg) +#define _LIBUNWIND_LOG(msg, ...) +#else +#define _LIBUNWIND_LOG0(msg) \ + fprintf(stderr, "libunwind: " msg "\n") +#define _LIBUNWIND_LOG(msg, ...) \ + fprintf(stderr, "libunwind: " msg "\n", __VA_ARGS__) +#endif + +#if defined(NDEBUG) + #define _LIBUNWIND_LOG_IF_FALSE(x) x +#else + #define _LIBUNWIND_LOG_IF_FALSE(x) \ + do { \ + bool _ret = x; \ + if (!_ret) \ + _LIBUNWIND_LOG("" #x " failed in %s", __FUNCTION__); \ + } while (0) +#endif + +// Macros that define away in non-Debug builds +#ifdef NDEBUG + #define _LIBUNWIND_DEBUG_LOG(msg, ...) + #define _LIBUNWIND_TRACE_API(msg, ...) + #define _LIBUNWIND_TRACING_UNWINDING (0) + #define _LIBUNWIND_TRACING_DWARF (0) + #define _LIBUNWIND_TRACE_UNWINDING(msg, ...) + #define _LIBUNWIND_TRACE_DWARF(...) +#else + #ifdef __cplusplus + extern "C" { + #endif + extern bool logAPIs(); + extern bool logUnwinding(); + extern bool logDWARF(); + #ifdef __cplusplus + } + #endif + #define _LIBUNWIND_DEBUG_LOG(msg, ...) _LIBUNWIND_LOG(msg, __VA_ARGS__) + #define _LIBUNWIND_TRACE_API(msg, ...) \ + do { \ + if (logAPIs()) \ + _LIBUNWIND_LOG(msg, __VA_ARGS__); \ + } while (0) + #define _LIBUNWIND_TRACING_UNWINDING logUnwinding() + #define _LIBUNWIND_TRACING_DWARF logDWARF() + #define _LIBUNWIND_TRACE_UNWINDING(msg, ...) \ + do { \ + if (logUnwinding()) \ + _LIBUNWIND_LOG(msg, __VA_ARGS__); \ + } while (0) + #define _LIBUNWIND_TRACE_DWARF(...) \ + do { \ + if (logDWARF()) \ + fprintf(stderr, __VA_ARGS__); \ + } while (0) +#endif + +#ifdef __cplusplus +// Used to fit UnwindCursor and Registers_xxx types against unw_context_t / +// unw_cursor_t sized memory blocks. +#if defined(_LIBUNWIND_IS_NATIVE_ONLY) +# define COMP_OP == +#else +# define COMP_OP <= +#endif +template +struct check_fit { + template + struct blk_count { + static const size_t count = + (sizeof(T) + sizeof(uint64_t) - 1) / sizeof(uint64_t); + }; + static const bool does_fit = + (blk_count<_Type>::count COMP_OP blk_count<_Mem>::count); +}; +#undef COMP_OP +#endif // __cplusplus + +#endif // LIBUNWIND_CONFIG_H diff --git a/shared/sentry/external/crashpad/libunwind/src/dwarf2.h b/shared/sentry/external/crashpad/libunwind/src/dwarf2.h new file mode 100644 index 000000000..40f0daf46 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/src/dwarf2.h @@ -0,0 +1,239 @@ +//===------------------------------- dwarf2.h -----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + + +/* + These constants were taken from version 3 of the DWARF standard, + which is Copyright (c) 2005 Free Standards Group, and + Copyright (c) 1992, 1993 UNIX International, Inc. +*/ + +#ifndef __DWARF2__ +#define __DWARF2__ + +// DWARF unwind instructions +enum { + DW_CFA_nop = 0x0, + DW_CFA_set_loc = 0x1, + DW_CFA_advance_loc1 = 0x2, + DW_CFA_advance_loc2 = 0x3, + DW_CFA_advance_loc4 = 0x4, + DW_CFA_offset_extended = 0x5, + DW_CFA_restore_extended = 0x6, + DW_CFA_undefined = 0x7, + DW_CFA_same_value = 0x8, + DW_CFA_register = 0x9, + DW_CFA_remember_state = 0xA, + DW_CFA_restore_state = 0xB, + DW_CFA_def_cfa = 0xC, + DW_CFA_def_cfa_register = 0xD, + DW_CFA_def_cfa_offset = 0xE, + DW_CFA_def_cfa_expression = 0xF, + 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, + DW_CFA_advance_loc = 0x40, // high 2 bits are 0x1, lower 6 bits are delta + DW_CFA_offset = 0x80, // high 2 bits are 0x2, lower 6 bits are register + DW_CFA_restore = 0xC0, // high 2 bits are 0x3, lower 6 bits are register + + // GNU extensions + DW_CFA_GNU_window_save = 0x2D, + DW_CFA_GNU_args_size = 0x2E, + DW_CFA_GNU_negative_offset_extended = 0x2F, + + // AARCH64 extensions + DW_CFA_AARCH64_negate_ra_state = 0x2D +}; + + +// FSF exception handling Pointer-Encoding constants +// Used in CFI augmentation by GCC +enum { + DW_EH_PE_ptr = 0x00, + DW_EH_PE_uleb128 = 0x01, + DW_EH_PE_udata2 = 0x02, + DW_EH_PE_udata4 = 0x03, + DW_EH_PE_udata8 = 0x04, + DW_EH_PE_signed = 0x08, + DW_EH_PE_sleb128 = 0x09, + DW_EH_PE_sdata2 = 0x0A, + DW_EH_PE_sdata4 = 0x0B, + DW_EH_PE_sdata8 = 0x0C, + DW_EH_PE_absptr = 0x00, + 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, + DW_EH_PE_indirect = 0x80, + DW_EH_PE_omit = 0xFF +}; + + +// DWARF expressions +enum { + DW_OP_addr = 0x03, // constant address (size target specific) + DW_OP_deref = 0x06, + DW_OP_const1u = 0x08, // 1-byte constant + DW_OP_const1s = 0x09, // 1-byte constant + DW_OP_const2u = 0x0A, // 2-byte constant + DW_OP_const2s = 0x0B, // 2-byte constant + DW_OP_const4u = 0x0C, // 4-byte constant + DW_OP_const4s = 0x0D, // 4-byte constant + DW_OP_const8u = 0x0E, // 8-byte constant + DW_OP_const8s = 0x0F, // 8-byte constant + DW_OP_constu = 0x10, // ULEB128 constant + DW_OP_consts = 0x11, // SLEB128 constant + DW_OP_dup = 0x12, + DW_OP_drop = 0x13, + DW_OP_over = 0x14, + DW_OP_pick = 0x15, // 1-byte stack index + 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, // ULEB128 addend + DW_OP_shl = 0x24, + DW_OP_shr = 0x25, + DW_OP_shra = 0x26, + DW_OP_xor = 0x27, + DW_OP_skip = 0x2F, // signed 2-byte constant + DW_OP_bra = 0x28, // signed 2-byte constant + 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_lit0 = 0x30, // Literal 0 + DW_OP_lit1 = 0x31, // Literal 1 + DW_OP_lit2 = 0x32, // Literal 2 + DW_OP_lit3 = 0x33, // Literal 3 + DW_OP_lit4 = 0x34, // Literal 4 + DW_OP_lit5 = 0x35, // Literal 5 + DW_OP_lit6 = 0x36, // Literal 6 + DW_OP_lit7 = 0x37, // Literal 7 + DW_OP_lit8 = 0x38, // Literal 8 + DW_OP_lit9 = 0x39, // Literal 9 + DW_OP_lit10 = 0x3A, // Literal 10 + DW_OP_lit11 = 0x3B, // Literal 11 + DW_OP_lit12 = 0x3C, // Literal 12 + DW_OP_lit13 = 0x3D, // Literal 13 + DW_OP_lit14 = 0x3E, // Literal 14 + DW_OP_lit15 = 0x3F, // Literal 15 + DW_OP_lit16 = 0x40, // Literal 16 + DW_OP_lit17 = 0x41, // Literal 17 + DW_OP_lit18 = 0x42, // Literal 18 + DW_OP_lit19 = 0x43, // Literal 19 + DW_OP_lit20 = 0x44, // Literal 20 + DW_OP_lit21 = 0x45, // Literal 21 + DW_OP_lit22 = 0x46, // Literal 22 + DW_OP_lit23 = 0x47, // Literal 23 + DW_OP_lit24 = 0x48, // Literal 24 + DW_OP_lit25 = 0x49, // Literal 25 + DW_OP_lit26 = 0x4A, // Literal 26 + DW_OP_lit27 = 0x4B, // Literal 27 + DW_OP_lit28 = 0x4C, // Literal 28 + DW_OP_lit29 = 0x4D, // Literal 29 + DW_OP_lit30 = 0x4E, // Literal 30 + DW_OP_lit31 = 0x4F, // Literal 31 + DW_OP_reg0 = 0x50, // Contents of reg0 + DW_OP_reg1 = 0x51, // Contents of reg1 + DW_OP_reg2 = 0x52, // Contents of reg2 + DW_OP_reg3 = 0x53, // Contents of reg3 + DW_OP_reg4 = 0x54, // Contents of reg4 + DW_OP_reg5 = 0x55, // Contents of reg5 + DW_OP_reg6 = 0x56, // Contents of reg6 + DW_OP_reg7 = 0x57, // Contents of reg7 + DW_OP_reg8 = 0x58, // Contents of reg8 + DW_OP_reg9 = 0x59, // Contents of reg9 + DW_OP_reg10 = 0x5A, // Contents of reg10 + DW_OP_reg11 = 0x5B, // Contents of reg11 + DW_OP_reg12 = 0x5C, // Contents of reg12 + DW_OP_reg13 = 0x5D, // Contents of reg13 + DW_OP_reg14 = 0x5E, // Contents of reg14 + DW_OP_reg15 = 0x5F, // Contents of reg15 + DW_OP_reg16 = 0x60, // Contents of reg16 + DW_OP_reg17 = 0x61, // Contents of reg17 + DW_OP_reg18 = 0x62, // Contents of reg18 + DW_OP_reg19 = 0x63, // Contents of reg19 + DW_OP_reg20 = 0x64, // Contents of reg20 + DW_OP_reg21 = 0x65, // Contents of reg21 + DW_OP_reg22 = 0x66, // Contents of reg22 + DW_OP_reg23 = 0x67, // Contents of reg23 + DW_OP_reg24 = 0x68, // Contents of reg24 + DW_OP_reg25 = 0x69, // Contents of reg25 + DW_OP_reg26 = 0x6A, // Contents of reg26 + DW_OP_reg27 = 0x6B, // Contents of reg27 + DW_OP_reg28 = 0x6C, // Contents of reg28 + DW_OP_reg29 = 0x6D, // Contents of reg29 + DW_OP_reg30 = 0x6E, // Contents of reg30 + DW_OP_reg31 = 0x6F, // Contents of reg31 + DW_OP_breg0 = 0x70, // base register 0 + SLEB128 offset + DW_OP_breg1 = 0x71, // base register 1 + SLEB128 offset + DW_OP_breg2 = 0x72, // base register 2 + SLEB128 offset + DW_OP_breg3 = 0x73, // base register 3 + SLEB128 offset + DW_OP_breg4 = 0x74, // base register 4 + SLEB128 offset + DW_OP_breg5 = 0x75, // base register 5 + SLEB128 offset + DW_OP_breg6 = 0x76, // base register 6 + SLEB128 offset + DW_OP_breg7 = 0x77, // base register 7 + SLEB128 offset + DW_OP_breg8 = 0x78, // base register 8 + SLEB128 offset + DW_OP_breg9 = 0x79, // base register 9 + SLEB128 offset + DW_OP_breg10 = 0x7A, // base register 10 + SLEB128 offset + DW_OP_breg11 = 0x7B, // base register 11 + SLEB128 offset + DW_OP_breg12 = 0x7C, // base register 12 + SLEB128 offset + DW_OP_breg13 = 0x7D, // base register 13 + SLEB128 offset + DW_OP_breg14 = 0x7E, // base register 14 + SLEB128 offset + DW_OP_breg15 = 0x7F, // base register 15 + SLEB128 offset + DW_OP_breg16 = 0x80, // base register 16 + SLEB128 offset + DW_OP_breg17 = 0x81, // base register 17 + SLEB128 offset + DW_OP_breg18 = 0x82, // base register 18 + SLEB128 offset + DW_OP_breg19 = 0x83, // base register 19 + SLEB128 offset + DW_OP_breg20 = 0x84, // base register 20 + SLEB128 offset + DW_OP_breg21 = 0x85, // base register 21 + SLEB128 offset + DW_OP_breg22 = 0x86, // base register 22 + SLEB128 offset + DW_OP_breg23 = 0x87, // base register 23 + SLEB128 offset + DW_OP_breg24 = 0x88, // base register 24 + SLEB128 offset + DW_OP_breg25 = 0x89, // base register 25 + SLEB128 offset + DW_OP_breg26 = 0x8A, // base register 26 + SLEB128 offset + DW_OP_breg27 = 0x8B, // base register 27 + SLEB128 offset + DW_OP_breg28 = 0x8C, // base register 28 + SLEB128 offset + DW_OP_breg29 = 0x8D, // base register 29 + SLEB128 offset + DW_OP_breg30 = 0x8E, // base register 30 + SLEB128 offset + DW_OP_breg31 = 0x8F, // base register 31 + SLEB128 offset + DW_OP_regx = 0x90, // ULEB128 register + DW_OP_fbreg = 0x91, // SLEB128 offset + DW_OP_bregx = 0x92, // ULEB128 register followed by SLEB128 offset + DW_OP_piece = 0x93, // ULEB128 size of piece addressed + DW_OP_deref_size = 0x94, // 1-byte size of data retrieved + DW_OP_xderef_size = 0x95, // 1-byte size of data retrieved + DW_OP_nop = 0x96, + DW_OP_push_object_addres = 0x97, + DW_OP_call2 = 0x98, // 2-byte offset of DIE + DW_OP_call4 = 0x99, // 4-byte offset of DIE + DW_OP_call_ref = 0x9A, // 4- or 8-byte offset of DIE + DW_OP_lo_user = 0xE0, + DW_OP_APPLE_uninit = 0xF0, + DW_OP_hi_user = 0xFF +}; + + +#endif diff --git a/shared/sentry/external/crashpad/libunwind/src/libunwind.cpp b/shared/sentry/external/crashpad/libunwind/src/libunwind.cpp new file mode 100644 index 000000000..fffffa76d --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/src/libunwind.cpp @@ -0,0 +1,378 @@ +//===--------------------------- libunwind.cpp ----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Implements unw_* functions from +// +//===----------------------------------------------------------------------===// + +#include + +#include "config.h" +#include "libunwind_ext.h" + +#include + +// Define the __has_feature extension for compilers that do not support it so +// that we can later check for the presence of ASan in a compiler-neutral way. +#if !defined(__has_feature) +#define __has_feature(feature) 0 +#endif + +#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) +#include +#endif + +#if !defined(__USING_SJLJ_EXCEPTIONS__) +#include "AddressSpace.hpp" +#include "UnwindCursor.hpp" + +using namespace libunwind; + +/// internal object to represent this processes address space +LocalAddressSpace LocalAddressSpace::sThisAddressSpace; + +_LIBUNWIND_EXPORT unw_addr_space_t unw_local_addr_space = + (unw_addr_space_t)&LocalAddressSpace::sThisAddressSpace; + +/// Create a cursor of a thread in this process given 'context' recorded by +/// __unw_getcontext(). +_LIBUNWIND_HIDDEN int __unw_init_local(unw_cursor_t *cursor, + unw_context_t *context) { + _LIBUNWIND_TRACE_API("__unw_init_local(cursor=%p, context=%p)", + static_cast(cursor), + static_cast(context)); +#if defined(__i386__) +# define REGISTER_KIND Registers_x86 +#elif defined(__x86_64__) +# define REGISTER_KIND Registers_x86_64 +#elif defined(__powerpc64__) +# define REGISTER_KIND Registers_ppc64 +#elif defined(__ppc__) +# define REGISTER_KIND Registers_ppc +#elif defined(__aarch64__) +# define REGISTER_KIND Registers_arm64 +#elif defined(__arm__) +# define REGISTER_KIND Registers_arm +#elif defined(__or1k__) +# define REGISTER_KIND Registers_or1k +#elif defined(__hexagon__) +# define REGISTER_KIND Registers_hexagon +#elif defined(__mips__) && defined(_ABIO32) && _MIPS_SIM == _ABIO32 +# define REGISTER_KIND Registers_mips_o32 +#elif defined(__mips64) +# define REGISTER_KIND Registers_mips_newabi +#elif defined(__mips__) +# warning The MIPS architecture is not supported with this ABI and environment! +#elif defined(__sparc__) +# define REGISTER_KIND Registers_sparc +#elif defined(__riscv) +# define REGISTER_KIND Registers_riscv +#elif defined(__ve__) +# define REGISTER_KIND Registers_ve +#else +# error Architecture not supported +#endif + // Use "placement new" to allocate UnwindCursor in the cursor buffer. + new (reinterpret_cast *>(cursor)) + UnwindCursor( + context, LocalAddressSpace::sThisAddressSpace); +#undef REGISTER_KIND + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + co->setInfoBasedOnIPRegister(); + + return UNW_ESUCCESS; +} +_LIBUNWIND_WEAK_ALIAS(__unw_init_local, unw_init_local) + +/// Create a cursor into a thread in another process. +_LIBUNWIND_EXPORT int unw_init_remote_thread(unw_cursor_t *cursor, + unw_addr_space_t as, thread_t thread) { +#if defined(__x86_64__) +# define REGISTER_KIND Registers_x86_64 +#elif defined(__aarch64__) +# define REGISTER_KIND Registers_arm64 +#else +# error Architecture not supported +#endif + // Use "placement new" to allocate UnwindCursor in the cursor buffer. + new (reinterpret_cast *>( + cursor)) + UnwindCursor(*(RemoteAddressSpace*)as, + thread); +#undef REGISTER_KIND + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + co->setInfoBasedOnIPRegister(); + + return UNW_ESUCCESS; +} + +/// Create an address_space object for use in examining another task. +_LIBUNWIND_EXPORT unw_addr_space_t unw_create_addr_space_for_task(task_t task) { + RemoteAddressSpace *as = + (RemoteAddressSpace *)malloc(sizeof(RemoteAddressSpace)); + new (as) RemoteAddressSpace(task); + return (unw_addr_space_t)as; +} + +/// Delete an address_space object. +_LIBUNWIND_EXPORT void unw_destroy_addr_space(unw_addr_space_t asp) { + RemoteAddressSpace *as = (RemoteAddressSpace *)asp; + as->~RemoteAddressSpace(); + free(as); +} + +/// Get value of specified register at cursor position in stack frame. +_LIBUNWIND_HIDDEN int __unw_get_reg(unw_cursor_t *cursor, unw_regnum_t regNum, + unw_word_t *value) { + _LIBUNWIND_TRACE_API("__unw_get_reg(cursor=%p, regNum=%d, &value=%p)", + static_cast(cursor), regNum, + static_cast(value)); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + if (co->validReg(regNum)) { + *value = co->getReg(regNum); + return UNW_ESUCCESS; + } + return UNW_EBADREG; +} +_LIBUNWIND_WEAK_ALIAS(__unw_get_reg, unw_get_reg) + +/// Set value of specified register at cursor position in stack frame. +_LIBUNWIND_HIDDEN int __unw_set_reg(unw_cursor_t *cursor, unw_regnum_t regNum, + unw_word_t value) { + _LIBUNWIND_TRACE_API("__unw_set_reg(cursor=%p, regNum=%d, value=0x%" PRIxPTR + ")", + static_cast(cursor), regNum, value); + typedef LocalAddressSpace::pint_t pint_t; + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + if (co->validReg(regNum)) { + co->setReg(regNum, (pint_t)value); + // specical case altering IP to re-find info (being called by personality + // function) + if (regNum == UNW_REG_IP) { + unw_proc_info_t info; + // First, get the FDE for the old location and then update it. + co->getInfo(&info); + co->setInfoBasedOnIPRegister(false); + // If the original call expects stack adjustment, perform this now. + // Normal frame unwinding would have included the offset already in the + // CFA computation. + // Note: for PA-RISC and other platforms where the stack grows up, + // this should actually be - info.gp. LLVM doesn't currently support + // any such platforms and Clang doesn't export a macro for them. + if (info.gp) + co->setReg(UNW_REG_SP, co->getReg(UNW_REG_SP) + info.gp); + } + return UNW_ESUCCESS; + } + return UNW_EBADREG; +} +_LIBUNWIND_WEAK_ALIAS(__unw_set_reg, unw_set_reg) + +/// Get value of specified float register at cursor position in stack frame. +_LIBUNWIND_HIDDEN int __unw_get_fpreg(unw_cursor_t *cursor, unw_regnum_t regNum, + unw_fpreg_t *value) { + _LIBUNWIND_TRACE_API("__unw_get_fpreg(cursor=%p, regNum=%d, &value=%p)", + static_cast(cursor), regNum, + static_cast(value)); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + if (co->validFloatReg(regNum)) { + *value = co->getFloatReg(regNum); + return UNW_ESUCCESS; + } + return UNW_EBADREG; +} +_LIBUNWIND_WEAK_ALIAS(__unw_get_fpreg, unw_get_fpreg) + +/// Set value of specified float register at cursor position in stack frame. +_LIBUNWIND_HIDDEN int __unw_set_fpreg(unw_cursor_t *cursor, unw_regnum_t regNum, + unw_fpreg_t value) { +#if defined(_LIBUNWIND_ARM_EHABI) + _LIBUNWIND_TRACE_API("__unw_set_fpreg(cursor=%p, regNum=%d, value=%llX)", + static_cast(cursor), regNum, value); +#else + _LIBUNWIND_TRACE_API("__unw_set_fpreg(cursor=%p, regNum=%d, value=%g)", + static_cast(cursor), regNum, value); +#endif + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + if (co->validFloatReg(regNum)) { + co->setFloatReg(regNum, value); + return UNW_ESUCCESS; + } + return UNW_EBADREG; +} +_LIBUNWIND_WEAK_ALIAS(__unw_set_fpreg, unw_set_fpreg) + +/// Move cursor to next frame. +_LIBUNWIND_HIDDEN int __unw_step(unw_cursor_t *cursor) { + _LIBUNWIND_TRACE_API("__unw_step(cursor=%p)", static_cast(cursor)); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + return co->step(); +} +_LIBUNWIND_WEAK_ALIAS(__unw_step, unw_step) + +/// Get unwind info at cursor position in stack frame. +_LIBUNWIND_HIDDEN int __unw_get_proc_info(unw_cursor_t *cursor, + unw_proc_info_t *info) { + _LIBUNWIND_TRACE_API("__unw_get_proc_info(cursor=%p, &info=%p)", + static_cast(cursor), static_cast(info)); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + co->getInfo(info); + if (info->end_ip == 0) + return UNW_ENOINFO; + return UNW_ESUCCESS; +} +_LIBUNWIND_WEAK_ALIAS(__unw_get_proc_info, unw_get_proc_info) + +/// Resume execution at cursor position (aka longjump). +_LIBUNWIND_HIDDEN int __unw_resume(unw_cursor_t *cursor) { + _LIBUNWIND_TRACE_API("__unw_resume(cursor=%p)", static_cast(cursor)); +#if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) + // Inform the ASan runtime that now might be a good time to clean stuff up. + __asan_handle_no_return(); +#endif + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + co->jumpto(); + return UNW_EUNSPEC; +} +_LIBUNWIND_WEAK_ALIAS(__unw_resume, unw_resume) + +/// Get name of function at cursor position in stack frame. +_LIBUNWIND_HIDDEN int __unw_get_proc_name(unw_cursor_t *cursor, char *buf, + size_t bufLen, unw_word_t *offset) { + _LIBUNWIND_TRACE_API("__unw_get_proc_name(cursor=%p, &buf=%p, bufLen=%lu)", + static_cast(cursor), static_cast(buf), + static_cast(bufLen)); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + if (co->getFunctionName(buf, bufLen, offset)) + return UNW_ESUCCESS; + return UNW_EUNSPEC; +} +_LIBUNWIND_WEAK_ALIAS(__unw_get_proc_name, unw_get_proc_name) + +/// Checks if a register is a floating-point register. +_LIBUNWIND_HIDDEN int __unw_is_fpreg(unw_cursor_t *cursor, + unw_regnum_t regNum) { + _LIBUNWIND_TRACE_API("__unw_is_fpreg(cursor=%p, regNum=%d)", + static_cast(cursor), regNum); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + return co->validFloatReg(regNum); +} +_LIBUNWIND_WEAK_ALIAS(__unw_is_fpreg, unw_is_fpreg) + +/// Checks if a register is a floating-point register. +_LIBUNWIND_HIDDEN const char *__unw_regname(unw_cursor_t *cursor, + unw_regnum_t regNum) { + _LIBUNWIND_TRACE_API("__unw_regname(cursor=%p, regNum=%d)", + static_cast(cursor), regNum); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + return co->getRegisterName(regNum); +} +_LIBUNWIND_WEAK_ALIAS(__unw_regname, unw_regname) + +/// Checks if current frame is signal trampoline. +_LIBUNWIND_HIDDEN int __unw_is_signal_frame(unw_cursor_t *cursor) { + _LIBUNWIND_TRACE_API("__unw_is_signal_frame(cursor=%p)", + static_cast(cursor)); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + return co->isSignalFrame(); +} +_LIBUNWIND_WEAK_ALIAS(__unw_is_signal_frame, unw_is_signal_frame) + +#ifdef __arm__ +// Save VFP registers d0-d15 using FSTMIADX instead of FSTMIADD +_LIBUNWIND_HIDDEN void __unw_save_vfp_as_X(unw_cursor_t *cursor) { + _LIBUNWIND_TRACE_API("__unw_get_fpreg_save_vfp_as_X(cursor=%p)", + static_cast(cursor)); + AbstractUnwindCursor *co = (AbstractUnwindCursor *)cursor; + return co->saveVFPAsX(); +} +_LIBUNWIND_WEAK_ALIAS(__unw_save_vfp_as_X, unw_save_vfp_as_X) +#endif + + +#if defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) +/// SPI: walks cached DWARF entries +_LIBUNWIND_HIDDEN void __unw_iterate_dwarf_unwind_cache(void (*func)( + unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh)) { + _LIBUNWIND_TRACE_API("__unw_iterate_dwarf_unwind_cache(func=%p)", + reinterpret_cast(func)); + DwarfFDECache::iterateCacheEntries(func); +} +_LIBUNWIND_WEAK_ALIAS(__unw_iterate_dwarf_unwind_cache, + unw_iterate_dwarf_unwind_cache) + +/// IPI: for __register_frame() +void __unw_add_dynamic_fde(unw_word_t fde) { + CFI_Parser::FDE_Info fdeInfo; + CFI_Parser::CIE_Info cieInfo; + const char *message = CFI_Parser::decodeFDE( + LocalAddressSpace::sThisAddressSpace, + (LocalAddressSpace::pint_t) fde, &fdeInfo, &cieInfo); + if (message == NULL) { + // dynamically registered FDEs don't have a mach_header group they are in. + // Use fde as mh_group + unw_word_t mh_group = fdeInfo.fdeStart; + DwarfFDECache::add((LocalAddressSpace::pint_t)mh_group, + fdeInfo.pcStart, fdeInfo.pcEnd, + fdeInfo.fdeStart); + } else { + _LIBUNWIND_DEBUG_LOG("__unw_add_dynamic_fde: bad fde: %s", message); + } +} + +/// IPI: for __deregister_frame() +void __unw_remove_dynamic_fde(unw_word_t fde) { + // fde is own mh_group + DwarfFDECache::removeAllIn((LocalAddressSpace::pint_t)fde); +} +#endif // defined(_LIBUNWIND_SUPPORT_DWARF_UNWIND) +#endif // !defined(__USING_SJLJ_EXCEPTIONS__) + + + +// Add logging hooks in Debug builds only +#ifndef NDEBUG +#include + +_LIBUNWIND_HIDDEN +bool logAPIs() { + // do manual lock to avoid use of _cxa_guard_acquire or initializers + static bool checked = false; + static bool log = false; + if (!checked) { + log = (getenv("LIBUNWIND_PRINT_APIS") != NULL); + checked = true; + } + return log; +} + +_LIBUNWIND_HIDDEN +bool logUnwinding() { + // do manual lock to avoid use of _cxa_guard_acquire or initializers + static bool checked = false; + static bool log = false; + if (!checked) { + log = (getenv("LIBUNWIND_PRINT_UNWINDING") != NULL); + checked = true; + } + return log; +} + +_LIBUNWIND_HIDDEN +bool logDWARF() { + // do manual lock to avoid use of _cxa_guard_acquire or initializers + static bool checked = false; + static bool log = false; + if (!checked) { + log = (getenv("LIBUNWIND_PRINT_DWARF") != NULL); + checked = true; + } + return log; +} + +#endif // NDEBUG + diff --git a/shared/sentry/external/crashpad/libunwind/src/libunwind_ext.h b/shared/sentry/external/crashpad/libunwind/src/libunwind_ext.h new file mode 100644 index 000000000..316dee298 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/src/libunwind_ext.h @@ -0,0 +1,65 @@ +//===------------------------ libunwind_ext.h -----------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// Extensions to libunwind API. +// +//===----------------------------------------------------------------------===// + +#ifndef __LIBUNWIND_EXT__ +#define __LIBUNWIND_EXT__ + +#include "config.h" +#include +#include + +#define UNW_STEP_SUCCESS 1 +#define UNW_STEP_END 0 + +#ifdef __cplusplus +extern "C" { +#endif + +extern int __unw_getcontext(unw_context_t *); +extern int __unw_init_local(unw_cursor_t *, unw_context_t *); +extern int __unw_step(unw_cursor_t *); +extern int __unw_get_reg(unw_cursor_t *, unw_regnum_t, unw_word_t *); +extern int __unw_get_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t *); +extern int __unw_set_reg(unw_cursor_t *, unw_regnum_t, unw_word_t); +extern int __unw_set_fpreg(unw_cursor_t *, unw_regnum_t, unw_fpreg_t); +extern int __unw_resume(unw_cursor_t *); + +#ifdef __arm__ +/* Save VFP registers in FSTMX format (instead of FSTMD). */ +extern void __unw_save_vfp_as_X(unw_cursor_t *); +#endif + +extern const char *__unw_regname(unw_cursor_t *, unw_regnum_t); +extern int __unw_get_proc_info(unw_cursor_t *, unw_proc_info_t *); +extern int __unw_is_fpreg(unw_cursor_t *, unw_regnum_t); +extern int __unw_is_signal_frame(unw_cursor_t *); +extern int __unw_get_proc_name(unw_cursor_t *, char *, size_t, unw_word_t *); + +// SPI +extern void __unw_iterate_dwarf_unwind_cache(void (*func)( + unw_word_t ip_start, unw_word_t ip_end, unw_word_t fde, unw_word_t mh)); + +// IPI +extern void __unw_add_dynamic_fde(unw_word_t fde); +extern void __unw_remove_dynamic_fde(unw_word_t fde); + +#if defined(_LIBUNWIND_ARM_EHABI) +extern const uint32_t* decode_eht_entry(const uint32_t*, size_t*, size_t*); +extern _Unwind_Reason_Code _Unwind_VRS_Interpret(_Unwind_Context *context, + const uint32_t *data, + size_t offset, size_t len); +#endif + +#ifdef __cplusplus +} +#endif + +#endif // __LIBUNWIND_EXT__ diff --git a/shared/sentry/external/crashpad/libunwind/test/CMakeLists.txt b/shared/sentry/external/crashpad/libunwind/test/CMakeLists.txt new file mode 100644 index 000000000..932a6e336 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/test/CMakeLists.txt @@ -0,0 +1,61 @@ +include(AddLLVM) # for add_lit_testsuite +macro(pythonize_bool var) + if (${var}) + set(${var} True) + else() + set(${var} False) + endif() +endmacro() + +if (NOT DEFINED LIBCXX_ENABLE_SHARED) + set(LIBCXX_ENABLE_SHARED ON) +endif() + +pythonize_bool(LIBUNWIND_BUILD_32_BITS) +pythonize_bool(LIBUNWIND_ENABLE_CET) +pythonize_bool(LIBCXX_ENABLE_SHARED) +pythonize_bool(LIBUNWIND_ENABLE_SHARED) +pythonize_bool(LIBUNWIND_ENABLE_THREADS) +pythonize_bool(LIBUNWIND_USES_ARM_EHABI) +pythonize_bool(LIBUNWIND_USE_COMPILER_RT) +pythonize_bool(LIBUNWIND_BUILD_EXTERNAL_THREAD_LIBRARY) +set(LIBUNWIND_TARGET_INFO "libcxx.test.target_info.LocalTI" CACHE STRING + "TargetInfo to use when setting up test environment.") +set(LIBUNWIND_EXECUTOR "${Python3_EXECUTABLE} ${LIBUNWIND_LIBCXX_PATH}/utils/run.py" CACHE STRING + "Executor to use when running tests.") + +set(AUTO_GEN_COMMENT "## Autogenerated by libunwind configuration.\n# Do not edit!") +set(SERIALIZED_LIT_PARAMS "# Lit parameters serialized here for llvm-lit to pick them up\n") + +macro(serialize_lit_param param value) + string(APPEND SERIALIZED_LIT_PARAMS "config.${param} = ${value}\n") +endmacro() + +serialize_lit_param(enable_experimental False) + +if (LLVM_USE_SANITIZER) + serialize_lit_param(use_sanitizer "\"${LLVM_USE_SANITIZER}\"") +endif() + +if (LIBUNWIND_TARGET_TRIPLE) + serialize_lit_param(target_triple "\"${LIBUNWIND_TARGET_TRIPLE}\"") +endif() + +if (LIBUNWIND_BUILD_32_BITS) + serialize_lit_param(enable_32bit True) +endif() + +foreach(param IN LISTS LIBUNWIND_TEST_PARAMS) + string(REGEX REPLACE "(.+)=(.+)" "\\1" name "${param}") + string(REGEX REPLACE "(.+)=(.+)" "\\2" value "${param}") + serialize_lit_param("${name}" "\"${value}\"") +endforeach() + +configure_lit_site_cfg( + "${LIBUNWIND_TEST_CONFIG}" + ${CMAKE_CURRENT_BINARY_DIR}/lit.site.cfg + MAIN_CONFIG "${CMAKE_CURRENT_SOURCE_DIR}/lit.cfg.py") + +add_lit_testsuite(check-unwind "Running libunwind tests" + ${CMAKE_CURRENT_BINARY_DIR} + DEPENDS unwind ${LIBUNWIND_TEST_DEPS}) diff --git a/shared/sentry/external/crashpad/libunwind/test/alignment.compile.pass.cpp b/shared/sentry/external/crashpad/libunwind/test/alignment.compile.pass.cpp new file mode 100644 index 000000000..4606dc5e5 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/test/alignment.compile.pass.cpp @@ -0,0 +1,24 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// The Itanium ABI requires that _Unwind_Exception objects are "double-word +// aligned". + +#include + +// EHABI : 8-byte aligned +// itanium: largest supported alignment for the system +#if defined(_LIBUNWIND_ARM_EHABI) +static_assert(alignof(_Unwind_Control_Block) == 8, + "_Unwind_Control_Block must be double-word aligned"); +#else +struct MaxAligned {} __attribute__((__aligned__)); +static_assert(alignof(_Unwind_Exception) == alignof(MaxAligned), + "_Unwind_Exception must be maximally aligned"); +#endif diff --git a/shared/sentry/external/crashpad/libunwind/test/floatregister.pass.cpp b/shared/sentry/external/crashpad/libunwind/test/floatregister.pass.cpp new file mode 100644 index 000000000..64107e6d4 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/test/floatregister.pass.cpp @@ -0,0 +1,51 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: linux && target={{aarch64-.+}} + +// Basic test for float registers number are accepted. + +#include +#include +#include +#include + +_Unwind_Reason_Code frame_handler(struct _Unwind_Context *ctx, void *arg) { + (void)arg; + Dl_info info = {0, 0, 0, 0}; + + // Unwind util the main is reached, above frames depend on the platform and + // architecture. + if (dladdr(reinterpret_cast(_Unwind_GetIP(ctx)), &info) && + info.dli_sname && !strcmp("main", info.dli_sname)) + _Exit(0); + + return _URC_NO_REASON; +} + +__attribute__((noinline)) void foo() { + // Provide some CFI directives that instructs the unwinder where given + // float register is. +#if defined(__aarch64__) + // DWARF register number for V0-V31 registers are 64-95. + // Previous value of V0 is saved at offset 0 from CFA. + asm volatile(".cfi_offset 64, 0"); + // From now on the previous value of register can't be restored anymore. + asm volatile(".cfi_undefined 65"); + asm volatile(".cfi_undefined 95"); + // Previous value of V2 is in V30. + asm volatile(".cfi_register 66, 94"); +#endif + _Unwind_Backtrace(frame_handler, NULL); +} + +int main() { + foo(); + return -2; +} diff --git a/shared/sentry/external/crashpad/libunwind/test/forceunwind.pass.cpp b/shared/sentry/external/crashpad/libunwind/test/forceunwind.pass.cpp new file mode 100644 index 000000000..a3191f8f7 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/test/forceunwind.pass.cpp @@ -0,0 +1,74 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// REQUIRES: linux + +// TODO: Investigate these failures +// XFAIL: asan, tsan, ubsan + +// TODO: Investigate this failure +// XFAIL: 32bits-on-64bits + +// Basic test for _Unwind_ForcedUnwind. +// See libcxxabi/test/forced_unwind* tests too. + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void foo(); +_Unwind_Exception ex; + +_Unwind_Reason_Code stop(int version, _Unwind_Action actions, + _Unwind_Exception_Class exceptionClass, + _Unwind_Exception *exceptionObject, + struct _Unwind_Context *context, + void *stop_parameter) { + assert(version == 1); + assert((actions & _UA_FORCE_UNWIND) != 0); + (void)exceptionClass; + assert(exceptionObject == &ex); + assert(stop_parameter == &foo); + + Dl_info info = {0, 0, 0, 0}; + + // Unwind util the main is reached, above frames depend on the platform and + // architecture. + if (dladdr(reinterpret_cast(_Unwind_GetIP(context)), &info) && + info.dli_sname && !strcmp("main", info.dli_sname)) { + _Exit(0); + } + return _URC_NO_REASON; +} + +__attribute__((noinline)) void foo() { + + // Arm EHABI defines struct _Unwind_Control_Block as exception + // object. Ensure struct _Unwind_Exception* work there too, + // because _Unwind_Exception in this case is just an alias. + struct _Unwind_Exception *e = &ex; +#if defined(_LIBUNWIND_ARM_EHABI) + // Create a mock exception object. + memset(e, '\0', sizeof(*e)); + strcpy(reinterpret_cast(&e->exception_class), "CLNGUNW"); +#endif + _Unwind_ForcedUnwind(e, stop, (void *)&foo); +} + +int main() { + foo(); + return -2; +} diff --git a/shared/sentry/external/crashpad/libunwind/test/frameheadercache_test.pass.cpp b/shared/sentry/external/crashpad/libunwind/test/frameheadercache_test.pass.cpp new file mode 100644 index 000000000..dd1d3dd93 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/test/frameheadercache_test.pass.cpp @@ -0,0 +1,78 @@ +// TODO: Investigate these failures +// XFAIL: asan, tsan, ubsan + +// TODO: Investigate this failure +// XFAIL: 32bits-on-64bits + +// The other libunwind tests don't test internal interfaces, so the include path +// is a little wonky. +#include "../src/config.h" + +// Only run this test under supported configurations. + +#if defined(_LIBUNWIND_USE_DL_ITERATE_PHDR) && \ + defined(_LIBUNWIND_USE_FRAME_HEADER_CACHE) + +#include +#include + +// This file defines several of the data structures needed here, +// and includes FrameHeaderCache.hpp as well. +#include "../src/AddressSpace.hpp" + +#define kBaseAddr 0xFFF000 +#define kTextSegmentLength 0xFF + +using namespace libunwind; + +int main(int, char**) { + FrameHeaderCache FHC; + struct dl_phdr_info PInfo; + memset(&PInfo, 0, sizeof(PInfo)); + // The cache itself should only care about these two fields--they + // tell the cache to invalidate or not; everything else is handled + // by AddressSpace.hpp. + PInfo.dlpi_adds = 6; + PInfo.dlpi_subs = 7; + + UnwindInfoSections UIS; + UIS.dso_base = kBaseAddr; + UIS.text_segment_length = kTextSegmentLength; + dl_iterate_cb_data CBData; + // Unused by the cache. + CBData.addressSpace = nullptr; + CBData.sects = &UIS; + CBData.targetAddr = kBaseAddr + 1; + + // Nothing present, shouldn't find. + if (FHC.find(&PInfo, 0, &CBData)) + abort(); + FHC.add(&UIS); + // Just added. Should find. + if (!FHC.find(&PInfo, 0, &CBData)) + abort(); + // Cache is invalid. Shouldn't find. + PInfo.dlpi_adds++; + if (FHC.find(&PInfo, 0, &CBData)) + abort(); + + FHC.add(&UIS); + CBData.targetAddr = kBaseAddr - 1; + // Shouldn't find something outside of the addresses. + if (FHC.find(&PInfo, 0, &CBData)) + abort(); + // Add enough things to the cache that the entry is evicted. + for (int i = 0; i < 9; i++) { + UIS.dso_base = kBaseAddr + (kTextSegmentLength * i); + FHC.add(&UIS); + } + CBData.targetAddr = kBaseAddr; + // Should have been evicted. + if (FHC.find(&PInfo, 0, &CBData)) + abort(); + return 0; +} + +#else +int main(int, char**) { return 0;} +#endif diff --git a/shared/sentry/external/crashpad/libunwind/test/libunwind/__init__.py b/shared/sentry/external/crashpad/libunwind/test/libunwind/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/shared/sentry/external/crashpad/libunwind/test/libunwind/test/__init__.py b/shared/sentry/external/crashpad/libunwind/test/libunwind/test/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/shared/sentry/external/crashpad/libunwind/test/libunwind/test/config.py b/shared/sentry/external/crashpad/libunwind/test/libunwind/test/config.py new file mode 100644 index 000000000..87a810b49 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/test/libunwind/test/config.py @@ -0,0 +1,71 @@ +#===----------------------------------------------------------------------===## +# +# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +# See https://llvm.org/LICENSE.txt for license information. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +# +#===----------------------------------------------------------------------===## +import os +import sys + +from libcxx.test.config import Configuration as LibcxxConfiguration + + +class Configuration(LibcxxConfiguration): + # pylint: disable=redefined-outer-name + def __init__(self, lit_config, config): + super(Configuration, self).__init__(lit_config, config) + self.libunwind_src_root = None + self.libunwind_obj_root = None + self.abi_library_root = None + self.libcxx_src_root = None + + def configure_src_root(self): + self.libunwind_src_root = (self.get_lit_conf('libunwind_src_root') + or os.path.dirname(self.config.test_source_root)) + self.libcxx_src_root = (self.get_lit_conf('libcxx_src_root') + or os.path.join(self.libunwind_src_root, '..', 'libcxx')) + + def configure_obj_root(self): + self.libunwind_obj_root = self.get_lit_conf('libunwind_obj_root') + super(Configuration, self).configure_obj_root() + + def has_cpp_feature(self, feature, required_value): + return int(self.cxx.dumpMacros().get('__cpp_' + feature, 0)) >= required_value + + def configure_features(self): + super(Configuration, self).configure_features() + if self.get_lit_bool('arm_ehabi', False): + self.config.available_features.add('libunwind-arm-ehabi') + + def configure_compile_flags(self): + # Stack unwinding tests need unwinding tables and these are not + # generated by default on all Targets. + self.cxx.compile_flags += ['-funwind-tables'] + # Make symbols available in the tests. + triple = self.get_lit_conf('target_triple', None) + if triple is not None and 'linux' in triple: + self.cxx.link_flags += ['-Wl,--export-dynamic'] + if not self.get_lit_bool('enable_threads', True): + self.cxx.compile_flags += ['-D_LIBUNWIND_HAS_NO_THREADS'] + self.config.available_features.add('libunwind-no-threads') + if self.get_lit_bool('x86_cet', False): + self.cxx.compile_flags += ['-fcf-protection=full'] + super(Configuration, self).configure_compile_flags() + + def configure_compile_flags_header_includes(self): + libunwind_headers = self.get_lit_conf( + 'libunwind_headers', + os.path.join(self.libunwind_src_root, 'include')) + if not os.path.isdir(libunwind_headers): + self.lit_config.fatal("libunwind_headers='%s' is not a directory." + % libunwind_headers) + self.cxx.compile_flags += ['-I' + libunwind_headers] + + def configure_link_flags_cxx_library(self): + # libunwind tests should not link with libc++ + pass + + def configure_link_flags_abi_library(self): + # libunwind tests should not link with libc++abi + pass diff --git a/shared/sentry/external/crashpad/libunwind/test/libunwind_01.pass.cpp b/shared/sentry/external/crashpad/libunwind/test/libunwind_01.pass.cpp new file mode 100644 index 000000000..aceb8bedf --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/test/libunwind_01.pass.cpp @@ -0,0 +1,147 @@ +// TODO: Investigate these failures +// XFAIL: asan, tsan, ubsan + +// TODO: Investigate these failures on x86_64 macOS +// XFAIL: target=x86_64-apple-darwin{{.+}} + +// TODO: Investigate this failure +// XFAIL: 32bits-on-64bits + +#include +#include +#include + +void backtrace(int lower_bound) { + unw_context_t context; + unw_getcontext(&context); + + unw_cursor_t cursor; + unw_init_local(&cursor, &context); + + int n = 0; + do { + ++n; + if (n > 100) { + abort(); + } + } while (unw_step(&cursor) > 0); + + if (n < lower_bound) { + abort(); + } +} + +void test1(int i) { + backtrace(i); +} + +void test2(int i, int j) { + backtrace(i); + test1(j); +} + +void test3(int i, int j, int k) { + backtrace(i); + test2(j, k); +} + +void test_no_info() { + unw_context_t context; + unw_getcontext(&context); + + unw_cursor_t cursor; + unw_init_local(&cursor, &context); + + unw_proc_info_t info; + int ret = unw_get_proc_info(&cursor, &info); + if (ret != UNW_ESUCCESS) + abort(); + + // Set the IP to an address clearly outside any function. + unw_set_reg(&cursor, UNW_REG_IP, (unw_word_t)0); + + ret = unw_get_proc_info(&cursor, &info); + if (ret != UNW_ENOINFO) + abort(); +} + +void test_reg_names() { + unw_context_t context; + unw_getcontext(&context); + + unw_cursor_t cursor; + unw_init_local(&cursor, &context); + + int max_reg_num = -100; +#if defined(__i386__) + max_reg_num = 7; +#elif defined(__x86_64__) + max_reg_num = 32; +#endif + + const char prefix[] = "unknown"; + for (int i = -2; i < max_reg_num; ++i) { + if (strncmp(prefix, unw_regname(&cursor, i), sizeof(prefix) - 1) == 0) + abort(); + } + + if (strncmp(prefix, unw_regname(&cursor, max_reg_num + 1), + sizeof(prefix) - 1) != 0) + abort(); +} + +#if defined(__x86_64__) +void test_reg_get_set() { + unw_context_t context; + unw_getcontext(&context); + + unw_cursor_t cursor; + unw_init_local(&cursor, &context); + + for (int i = 0; i < 17; ++i) { + const unw_word_t set_value = 7; + if (unw_set_reg(&cursor, i, set_value) != UNW_ESUCCESS) + abort(); + + unw_word_t get_value = 0; + if (unw_get_reg(&cursor, i, &get_value) != UNW_ESUCCESS) + abort(); + + if (set_value != get_value) + abort(); + } +} + +void test_fpreg_get_set() { + unw_context_t context; + unw_getcontext(&context); + + unw_cursor_t cursor; + unw_init_local(&cursor, &context); + + // get/set is not implemented for x86_64 fpregs. + for (int i = 17; i < 33; ++i) { + const unw_fpreg_t set_value = 7; + if (unw_set_fpreg(&cursor, i, set_value) != UNW_EBADREG) + abort(); + + unw_fpreg_t get_value = 0; + if (unw_get_fpreg(&cursor, i, &get_value) != UNW_EBADREG) + abort(); + } +} +#else +void test_reg_get_set() {} +void test_fpreg_get_set() {} +#endif + +int main(int, char**) { + test1(1); + test2(1, 2); + test3(1, 2, 3); + test_no_info(); + test_reg_names(); + test_reg_get_set(); + test_fpreg_get_set(); + return 0; +} diff --git a/shared/sentry/external/crashpad/libunwind/test/libunwind_02.pass.cpp b/shared/sentry/external/crashpad/libunwind/test/libunwind_02.pass.cpp new file mode 100644 index 000000000..64d8d5883 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/test/libunwind_02.pass.cpp @@ -0,0 +1,45 @@ +// TODO: Investigate these failures +// XFAIL: asan, tsan, ubsan + +// TODO: Investigate this failure +// XFAIL: 32bits-on-64bits + +#include +#include +#include + +#define EXPECTED_NUM_FRAMES 50 +#define NUM_FRAMES_UPPER_BOUND 100 + +_Unwind_Reason_Code callback(_Unwind_Context *context, void *cnt) { + (void)context; + int *i = (int *)cnt; + ++*i; + if (*i > NUM_FRAMES_UPPER_BOUND) { + abort(); + } + return _URC_NO_REASON; +} + +void test_backtrace() { + int n = 0; + _Unwind_Backtrace(&callback, &n); + if (n < EXPECTED_NUM_FRAMES) { + abort(); + } +} + +int test(int i) { + if (i == 0) { + test_backtrace(); + return 0; + } else { + return i + test(i - 1); + } +} + +int main(int, char**) { + int total = test(50); + assert(total == 1275); + return 0; +} diff --git a/shared/sentry/external/crashpad/libunwind/test/lit.cfg.py b/shared/sentry/external/crashpad/libunwind/test/lit.cfg.py new file mode 100644 index 000000000..647464abe --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/test/lit.cfg.py @@ -0,0 +1,10 @@ +# All the Lit configuration is handled in the site configs -- this file is only +# left as a canary to catch invocations of Lit that do not go through llvm-lit. +# +# Invocations that go through llvm-lit will automatically use the right Lit +# site configuration inside the build directory. + +lit_config.fatal( + "You seem to be running Lit directly -- you should be running Lit through " + "/bin/llvm-lit, which will ensure that the right Lit configuration " + "file is used.") diff --git a/shared/sentry/external/crashpad/libunwind/test/lit.site.cfg.in b/shared/sentry/external/crashpad/libunwind/test/lit.site.cfg.in new file mode 100644 index 000000000..4fd633f87 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/test/lit.site.cfg.in @@ -0,0 +1,58 @@ +@AUTO_GEN_COMMENT@ + +@SERIALIZED_LIT_PARAMS@ + +import os +import site + +config.cxx_under_test = "@CMAKE_CXX_COMPILER@" +config.project_obj_root = "@CMAKE_BINARY_DIR@" +config.install_root = "@CMAKE_BINARY_DIR@" +config.libunwind_src_root = "@LIBUNWIND_SOURCE_DIR@" +config.libunwind_obj_root = "@LIBUNWIND_BINARY_DIR@" +config.abi_library_root = "@LIBUNWIND_LIBRARY_DIR@" +config.libcxx_src_root = "@LIBUNWIND_LIBCXX_PATH@" +config.libunwind_headers = "@LIBUNWIND_SOURCE_DIR@/include" +config.cxx_library_root = "@LIBUNWIND_LIBCXX_LIBRARY_PATH@" +config.llvm_unwinder = True +config.builtins_library = "@LIBUNWIND_BUILTINS_LIBRARY@" +config.enable_threads = @LIBUNWIND_ENABLE_THREADS@ +config.target_info = "@LIBUNWIND_TARGET_INFO@" +config.test_linker_flags = "@LIBUNWIND_TEST_LINKER_FLAGS@" +config.test_compiler_flags = "@LIBUNWIND_TEST_COMPILER_FLAGS@" +config.executor = "@LIBUNWIND_EXECUTOR@" +config.libunwind_shared = @LIBUNWIND_ENABLE_SHARED@ +config.enable_shared = @LIBCXX_ENABLE_SHARED@ +config.arm_ehabi = @LIBUNWIND_USES_ARM_EHABI@ +config.host_triple = "@LLVM_HOST_TRIPLE@" +config.sysroot = "@LIBUNWIND_SYSROOT@" +config.gcc_toolchain = "@LIBUNWIND_GCC_TOOLCHAIN@" +config.cxx_ext_threads = @LIBUNWIND_BUILD_EXTERNAL_THREAD_LIBRARY@ +config.x86_cet = @LIBUNWIND_ENABLE_CET@ + +site.addsitedir(os.path.join(config.libunwind_src_root, 'test')) +site.addsitedir(os.path.join(config.libcxx_src_root, 'utils')) + +# name: The name of this test suite. +config.name = 'libunwind' + +# suffixes: A list of file extensions to treat as test files. +config.suffixes = ['.cpp', '.s'] + +# test_source_root: The root path where tests are located. +config.test_source_root = os.path.join(config.libunwind_src_root, 'test') + +# Allow expanding substitutions that are based on other substitutions +config.recursiveExpansionLimit = 10 + +# Infer the test_exec_root from the build directory. +config.test_exec_root = os.path.join(config.libunwind_obj_root, 'test') + +import libcxx.test.format +config.test_format = libcxx.test.format.CxxStandardLibraryTest() + +lit_config.note('Using configuration variant: libunwind') +import libunwind.test.config +configuration = libunwind.test.config.Configuration(lit_config, config) +configuration.configure() +configuration.print_config_info() diff --git a/shared/sentry/external/crashpad/libunwind/test/remember_state_leak.pass.sh.s b/shared/sentry/external/crashpad/libunwind/test/remember_state_leak.pass.sh.s new file mode 100644 index 000000000..fceac212c --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/test/remember_state_leak.pass.sh.s @@ -0,0 +1,65 @@ +# REQUIRES: target={{x86_64-.+-linux-gnu}} +# RUN: %{build} +# RUN: %{run} + +// TODO: Investigate these failures +// XFAIL: asan, tsan, ubsan + +// TODO: Investigate this failure +// XFAIL: 32bits-on-64bits + +# TODO: Investigate this failure on GCC. +# XFAIL: gcc + +# The following assembly is a translation of this code: +# +# _Unwind_Reason_Code callback(int, _Unwind_Action, long unsigned int, +# _Unwind_Exception*, _Unwind_Context*, void*) { +# return _Unwind_Reason_Code(0); +# } +# +# int main() { +# asm(".cfi_remember_state\n\t"); +# _Unwind_Exception exc; +# _Unwind_ForcedUnwind(&exc, callback, 0); +# asm(".cfi_restore_state\n\t"); +# } +# +# When unwinding, the CFI parser will stop parsing opcodes after the current PC, +# so in this case the DW_CFA_restore_state opcode will never be processed and, +# if the library doesn't clean up properly, the store allocated by +# DW_CFA_remember_state will be leaked. +# +# This test will fail when linked with an asan-enabled libunwind if the +# remembered state is leaked. + + SIZEOF_UNWIND_EXCEPTION = 32 + + .text +callback: + xorl %eax, %eax + retq + + .globl main # -- Begin function main + .p2align 4, 0x90 + .type main,@function +main: # @main + .cfi_startproc + subq $8, %rsp # Adjust stack alignment + subq $SIZEOF_UNWIND_EXCEPTION, %rsp + .cfi_def_cfa_offset 48 + .cfi_remember_state + movq %rsp, %rdi + movabsq $callback, %rsi + xorl %edx, %edx + callq _Unwind_ForcedUnwind + .cfi_restore_state + xorl %eax, %eax + addq $SIZEOF_UNWIND_EXCEPTION, %rsp + addq $8, %rsp # Undo stack alignment adjustment + .cfi_def_cfa_offset 8 + retq +.Lfunc_end1: + .size main, .Lfunc_end1-main + .cfi_endproc + # -- End function diff --git a/shared/sentry/external/crashpad/libunwind/test/signal_frame.pass.cpp b/shared/sentry/external/crashpad/libunwind/test/signal_frame.pass.cpp new file mode 100644 index 000000000..fea189794 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/test/signal_frame.pass.cpp @@ -0,0 +1,40 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Ensure that functions marked as signal frames are reported as such. + +// TODO: Investigate these failures +// XFAIL: asan, tsan, ubsan + +// TODO: Investigate this failure on macOS +// XFAIL: target={{.+}}-apple-darwin{{.+}} + +// TODO: Investigate this failure +// XFAIL: 32bits-on-64bits + +// UNSUPPORTED: libunwind-arm-ehabi + +#include +#include +#include + +void test() { + asm(".cfi_signal_frame"); + unw_cursor_t cursor; + unw_context_t uc; + unw_getcontext(&uc); + unw_init_local(&cursor, &uc); + assert(unw_step(&cursor) > 0); + assert(unw_is_signal_frame(&cursor)); +} + +int main(int, char**) { + test(); + return 0; +} diff --git a/shared/sentry/external/crashpad/libunwind/test/signal_unwind.pass.cpp b/shared/sentry/external/crashpad/libunwind/test/signal_unwind.pass.cpp new file mode 100644 index 000000000..5468c7f67 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/test/signal_unwind.pass.cpp @@ -0,0 +1,52 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Ensure that the unwinder can cope with the signal handler. +// REQUIRES: linux && (target={{aarch64-.+}} || target={{x86_64-.+}}) + +// TODO: Investigate these failures +// XFAIL: asan, tsan, ubsan + +// TODO: Investigate this failure +// XFAIL: 32bits-on-64bits + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +_Unwind_Reason_Code frame_handler(struct _Unwind_Context* ctx, void* arg) { + (void)arg; + Dl_info info = { 0, 0, 0, 0 }; + + // Unwind util the main is reached, above frames depend on the platform and + // architecture. + if (dladdr(reinterpret_cast(_Unwind_GetIP(ctx)), &info) && + info.dli_sname && !strcmp("main", info.dli_sname)) { + _Exit(0); + } + return _URC_NO_REASON; +} + +void signal_handler(int signum) { + (void)signum; + _Unwind_Backtrace(frame_handler, NULL); + _Exit(-1); +} + +int main(int, char**) { + signal(SIGUSR1, signal_handler); + kill(getpid(), SIGUSR1); + return -2; +} diff --git a/shared/sentry/external/crashpad/libunwind/test/unw_getcontext.pass.cpp b/shared/sentry/external/crashpad/libunwind/test/unw_getcontext.pass.cpp new file mode 100644 index 000000000..a02c8e540 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/test/unw_getcontext.pass.cpp @@ -0,0 +1,12 @@ +// TODO: Investigate these failures +// XFAIL: asan, tsan, ubsan + +#include +#include + +int main(int, char**) { + unw_context_t context; + int ret = unw_getcontext(&context); + assert(ret == UNW_ESUCCESS); + return 0; +} diff --git a/shared/sentry/external/crashpad/libunwind/test/unwind_leaffunction.pass.cpp b/shared/sentry/external/crashpad/libunwind/test/unwind_leaffunction.pass.cpp new file mode 100644 index 000000000..8fc343304 --- /dev/null +++ b/shared/sentry/external/crashpad/libunwind/test/unwind_leaffunction.pass.cpp @@ -0,0 +1,57 @@ +// -*- C++ -*- +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Ensure that leaf function can be unwund. +// REQUIRES: linux && (target={{aarch64-.+}} || target={{x86_64-.+}}) + +// TODO: Investigate these failures +// XFAIL: asan, tsan, ubsan + +// TODO: Investigate this failure +// XFAIL: 32bits-on-64bits + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +_Unwind_Reason_Code frame_handler(struct _Unwind_Context* ctx, void* arg) { + (void)arg; + Dl_info info = { 0, 0, 0, 0 }; + + // Unwind util the main is reached, above frames deeped on the platfrom and architecture. + if (dladdr(reinterpret_cast(_Unwind_GetIP(ctx)), &info) && + info.dli_sname && !strcmp("main", info.dli_sname)) { + _Exit(0); + } + return _URC_NO_REASON; +} + +void signal_handler(int signum) { + (void)signum; + _Unwind_Backtrace(frame_handler, NULL); + _Exit(-1); +} + +int* faultyPointer = NULL; + +__attribute__((noinline)) void crashing_leaf_func(void) { + *faultyPointer = 0; +} + +int main(int, char**) { + signal(SIGSEGV, signal_handler); + crashing_leaf_func(); + return -2; +} \ No newline at end of file diff --git a/shared/sentry/external/crashpad/minidump/BUILD.gn b/shared/sentry/external/crashpad/minidump/BUILD.gn new file mode 100644 index 000000000..515ec0f37 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/BUILD.gn @@ -0,0 +1,189 @@ +# Copyright 2015 The Crashpad Authors. All rights reserved. +# +# 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("../build/crashpad_buildconfig.gni") + +crashpad_static_library("minidump") { + sources = [ + "minidump_annotation_writer.cc", + "minidump_annotation_writer.h", + "minidump_byte_array_writer.cc", + "minidump_byte_array_writer.h", + "minidump_context_writer.cc", + "minidump_context_writer.h", + "minidump_crashpad_info_writer.cc", + "minidump_crashpad_info_writer.h", + "minidump_exception_writer.cc", + "minidump_exception_writer.h", + "minidump_file_writer.cc", + "minidump_file_writer.h", + "minidump_handle_writer.cc", + "minidump_handle_writer.h", + "minidump_memory_info_writer.cc", + "minidump_memory_info_writer.h", + "minidump_memory_writer.cc", + "minidump_memory_writer.h", + "minidump_misc_info_writer.cc", + "minidump_misc_info_writer.h", + "minidump_module_crashpad_info_writer.cc", + "minidump_module_crashpad_info_writer.h", + "minidump_module_writer.cc", + "minidump_module_writer.h", + "minidump_rva_list_writer.cc", + "minidump_rva_list_writer.h", + "minidump_simple_string_dictionary_writer.cc", + "minidump_simple_string_dictionary_writer.h", + "minidump_stream_writer.cc", + "minidump_stream_writer.h", + "minidump_string_writer.cc", + "minidump_string_writer.h", + "minidump_system_info_writer.cc", + "minidump_system_info_writer.h", + "minidump_thread_id_map.cc", + "minidump_thread_id_map.h", + "minidump_thread_writer.cc", + "minidump_thread_writer.h", + "minidump_unloaded_module_writer.cc", + "minidump_unloaded_module_writer.h", + "minidump_user_extension_stream_data_source.cc", + "minidump_user_extension_stream_data_source.h", + "minidump_user_stream_writer.cc", + "minidump_user_stream_writer.h", + "minidump_writable.cc", + "minidump_writable.h", + "minidump_writer_util.cc", + "minidump_writer_util.h", + ] + + public_configs = [ "..:crashpad_config" ] + + public_deps = [ + ":format", + "../compat", + ] + + deps = [ + "../snapshot", + "$mini_chromium_source_parent:base", + "../util", + ] + + if (crashpad_is_win) { + cflags = [ + "/wd4201", # nonstandard extension used : nameless struct/union + "/wd4324", # 'struct' : structure was padded due to __declspec(align()) + ] + } +} + +# :format is the only part of minidump that snapshot may depend on. +static_library("format") { + sources = [ + "minidump_context.h", + "minidump_extensions.cc", + "minidump_extensions.h", + ] + + public_configs = [ "..:crashpad_config" ] + + public_deps = [ "../compat" ] + + deps = [ + "../snapshot:context", + "$mini_chromium_source_parent:base", + "../util", + ] +} + +static_library("test_support") { + testonly = true + + sources = [ + "test/minidump_byte_array_writer_test_util.cc", + "test/minidump_byte_array_writer_test_util.h", + "test/minidump_context_test_util.cc", + "test/minidump_context_test_util.h", + "test/minidump_file_writer_test_util.cc", + "test/minidump_file_writer_test_util.h", + "test/minidump_memory_writer_test_util.cc", + "test/minidump_memory_writer_test_util.h", + "test/minidump_rva_list_test_util.cc", + "test/minidump_rva_list_test_util.h", + "test/minidump_string_writer_test_util.cc", + "test/minidump_string_writer_test_util.h", + "test/minidump_user_extension_stream_util.cc", + "test/minidump_user_extension_stream_util.h", + "test/minidump_writable_test_util.cc", + "test/minidump_writable_test_util.h", + ] + + public_configs = [ "..:crashpad_config" ] + + public_deps = [ ":minidump" ] + + deps = [ + "../snapshot:test_support", + "../test", + "../third_party/googletest:googletest", + "$mini_chromium_source_parent:base", + "../util", + ] + + if (crashpad_is_win) { + cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union + } +} + +source_set("minidump_test") { + testonly = true + + sources = [ + "minidump_annotation_writer_test.cc", + "minidump_byte_array_writer_test.cc", + "minidump_context_writer_test.cc", + "minidump_crashpad_info_writer_test.cc", + "minidump_exception_writer_test.cc", + "minidump_file_writer_test.cc", + "minidump_handle_writer_test.cc", + "minidump_memory_info_writer_test.cc", + "minidump_memory_writer_test.cc", + "minidump_misc_info_writer_test.cc", + "minidump_module_crashpad_info_writer_test.cc", + "minidump_module_writer_test.cc", + "minidump_rva_list_writer_test.cc", + "minidump_simple_string_dictionary_writer_test.cc", + "minidump_string_writer_test.cc", + "minidump_system_info_writer_test.cc", + "minidump_thread_id_map_test.cc", + "minidump_thread_writer_test.cc", + "minidump_unloaded_module_writer_test.cc", + "minidump_user_stream_writer_test.cc", + "minidump_writable_test.cc", + ] + + configs += [ "../build:crashpad_is_in_fuchsia" ] + + deps = [ + ":test_support", + "../snapshot:test_support", + "../test", + "../third_party/googletest:googletest", + "$mini_chromium_source_parent:base", + "../util", + ] + + if (crashpad_is_win) { + cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union + } +} diff --git a/shared/sentry/external/crashpad/minidump/CMakeLists.txt b/shared/sentry/external/crashpad/minidump/CMakeLists.txt new file mode 100644 index 000000000..b964add2a --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/CMakeLists.txt @@ -0,0 +1,86 @@ +add_library(crashpad_minidump STATIC + minidump_annotation_writer.cc + minidump_annotation_writer.h + minidump_byte_array_writer.cc + minidump_byte_array_writer.h + minidump_context.h + minidump_context_writer.cc + minidump_context_writer.h + minidump_crashpad_info_writer.cc + minidump_crashpad_info_writer.h + minidump_exception_writer.cc + minidump_exception_writer.h + minidump_extensions.cc + minidump_extensions.h + minidump_file_writer.cc + minidump_file_writer.h + minidump_handle_writer.cc + minidump_handle_writer.h + minidump_memory_info_writer.cc + minidump_memory_info_writer.h + minidump_memory_writer.cc + minidump_memory_writer.h + minidump_misc_info_writer.cc + minidump_misc_info_writer.h + minidump_module_crashpad_info_writer.cc + minidump_module_crashpad_info_writer.h + minidump_module_writer.cc + minidump_module_writer.h + minidump_rva_list_writer.cc + minidump_rva_list_writer.h + minidump_simple_string_dictionary_writer.cc + minidump_simple_string_dictionary_writer.h + minidump_stream_writer.cc + minidump_stream_writer.h + minidump_string_writer.cc + minidump_string_writer.h + minidump_system_info_writer.cc + minidump_system_info_writer.h + minidump_thread_id_map.cc + minidump_thread_id_map.h + minidump_thread_writer.cc + minidump_thread_writer.h + minidump_unloaded_module_writer.cc + minidump_unloaded_module_writer.h + minidump_user_extension_stream_data_source.cc + minidump_user_extension_stream_data_source.h + minidump_user_stream_writer.cc + minidump_user_stream_writer.h + minidump_writable.cc + minidump_writable.h + minidump_writer_util.cc + minidump_writer_util.h +) + +target_link_libraries(crashpad_minidump + PRIVATE + $ + PUBLIC + crashpad_compat + crashpad_snapshot + crashpad_util + mini_chromium +) + +if(CRASHPAD_ENABLE_STACKTRACE) + target_sources(crashpad_minidump PRIVATE + minidump_stacktrace_writer.cc + minidump_stacktrace_writer.h + ) + target_compile_definitions(crashpad_minidump PRIVATE CLIENT_STACKTRACES_ENABLED) +endif() + +if(MSVC) + target_compile_options(crashpad_minidump PRIVATE "/wd4201" "/wd4324") +endif() + +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(crashpad_minidump PRIVATE + "-Wno-multichar" + ) +endif() + +set_property(TARGET crashpad_minidump PROPERTY EXPORT_NAME minidump) +add_library(crashpad::minidump ALIAS crashpad_minidump) + +crashpad_install_target(crashpad_minidump) diff --git a/shared/sentry/external/crashpad/minidump/minidump_annotation_writer.cc b/shared/sentry/external/crashpad/minidump/minidump_annotation_writer.cc new file mode 100644 index 000000000..54a3ecba1 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_annotation_writer.cc @@ -0,0 +1,162 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_annotation_writer.h" + +#include + +#include "base/logging.h" +#include "util/file/file_writer.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { + +MinidumpAnnotationWriter::MinidumpAnnotationWriter() = default; + +MinidumpAnnotationWriter::~MinidumpAnnotationWriter() = default; + +void MinidumpAnnotationWriter::InitializeFromSnapshot( + const AnnotationSnapshot& snapshot) { + DCHECK_EQ(state(), kStateMutable); + + name_.SetUTF8(snapshot.name); + annotation_.type = snapshot.type; + annotation_.reserved = 0; + value_.set_data(snapshot.value); +} + +void MinidumpAnnotationWriter::InitializeWithData( + const std::string& name, + uint16_t type, + const std::vector& data) { + DCHECK_EQ(state(), kStateMutable); + + name_.SetUTF8(name); + annotation_.type = type; + annotation_.reserved = 0; + value_.set_data(data); +} + +bool MinidumpAnnotationWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + name_.RegisterRVA(&annotation_.name); + value_.RegisterRVA(&annotation_.value); + + return true; +} + +size_t MinidumpAnnotationWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + // This object is written by the MinidumpAnnotationListWriter, and its + // children write themselves. + return 0; +} + +std::vector MinidumpAnnotationWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + + return {&name_, &value_}; +} + +bool MinidumpAnnotationWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + // This object is written by the MinidumpAnnotationListWriter, and its + // children write themselves. + return true; +} + +MinidumpAnnotationListWriter::MinidumpAnnotationListWriter() + : minidump_list_(new MinidumpAnnotationList()) {} + +MinidumpAnnotationListWriter::~MinidumpAnnotationListWriter() = default; + +void MinidumpAnnotationListWriter::InitializeFromList( + const std::vector& list) { + DCHECK_EQ(state(), kStateMutable); + for (const auto& annotation : list) { + auto writer = std::make_unique(); + writer->InitializeFromSnapshot(annotation); + AddObject(std::move(writer)); + } +} + +void MinidumpAnnotationListWriter::AddObject( + std::unique_ptr annotation_writer) { + DCHECK_EQ(state(), kStateMutable); + + objects_.push_back(std::move(annotation_writer)); +} + +bool MinidumpAnnotationListWriter::IsUseful() const { + return !objects_.empty(); +} + +bool MinidumpAnnotationListWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + if (!AssignIfInRange(&minidump_list_->count, objects_.size())) { + LOG(ERROR) << "annotation list size " << objects_.size() + << " is out of range"; + return false; + } + + return true; +} + +size_t MinidumpAnnotationListWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return sizeof(*minidump_list_) + sizeof(MinidumpAnnotation) * objects_.size(); +} + +std::vector +MinidumpAnnotationListWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + + std::vector children(objects_.size()); + for (size_t i = 0; i < objects_.size(); ++i) { + children[i] = objects_[i].get(); + } + + return children; +} + +bool MinidumpAnnotationListWriter::WriteObject( + FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + std::vector iov(1 + objects_.size()); + iov[0].iov_base = minidump_list_.get(); + iov[0].iov_len = sizeof(*minidump_list_); + + for (const auto& object : objects_) { + iov.emplace_back(WritableIoVec{object->minidump_annotation(), + sizeof(MinidumpAnnotation)}); + } + + return file_writer->WriteIoVec(&iov); +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_annotation_writer.h b/shared/sentry/external/crashpad/minidump/minidump_annotation_writer.h new file mode 100644 index 000000000..ffb931de4 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_annotation_writer.h @@ -0,0 +1,114 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_ANNOTATION_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_ANNOTATION_WRITER_H_ + +#include +#include + +#include "minidump/minidump_byte_array_writer.h" +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_string_writer.h" +#include "minidump/minidump_writable.h" +#include "snapshot/annotation_snapshot.h" + +namespace crashpad { + +//! \brief The writer for a MinidumpAnnotation object in a minidump file. +//! +//! Because MinidumpAnnotation objects only appear as elements +//! of MinidumpAnnotationList objects, this class does not write any +//! data on its own. It makes its MinidumpAnnotation data available to its +//! MinidumpAnnotationList parent, which writes it as part of a +//! MinidumpAnnotationList. +class MinidumpAnnotationWriter final : public internal::MinidumpWritable { + public: + MinidumpAnnotationWriter(); + + MinidumpAnnotationWriter(const MinidumpAnnotationWriter&) = delete; + MinidumpAnnotationWriter& operator=(const MinidumpAnnotationWriter&) = delete; + + ~MinidumpAnnotationWriter(); + + //! \brief Initializes the annotation writer with data from an + //! AnnotationSnapshot. + void InitializeFromSnapshot(const AnnotationSnapshot& snapshot); + + //! \brief Initializes the annotation writer with data values. + void InitializeWithData(const std::string& name, + uint16_t type, + const std::vector& data); + + //! \brief Returns the MinidumpAnnotation referencing this object’s data. + const MinidumpAnnotation* minidump_annotation() const { return &annotation_; } + + protected: + // MinidumpWritable: + + bool Freeze() override; + size_t SizeOfObject() override; + std::vector Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + MinidumpAnnotation annotation_; + internal::MinidumpUTF8StringWriter name_; + MinidumpByteArrayWriter value_; +}; + +//! \brief The writer for a MinidumpAnnotationList object in a minidump file, +//! containing a list of MinidumpAnnotation objects. +class MinidumpAnnotationListWriter final : public internal::MinidumpWritable { + public: + MinidumpAnnotationListWriter(); + + MinidumpAnnotationListWriter(const MinidumpAnnotationListWriter&) = delete; + MinidumpAnnotationListWriter& operator=(const MinidumpAnnotationListWriter&) = + delete; + + ~MinidumpAnnotationListWriter(); + + //! \brief Initializes the annotation list writer with a list of + //! AnnotationSnapshot objects. + void InitializeFromList(const std::vector& list); + + //! \brief Adds a single MinidumpAnnotationWriter to the list to be written. + void AddObject(std::unique_ptr annotation_writer); + + //! \brief Determines whether the object is useful. + //! + //! A useful object is one that carries data that makes a meaningful + //! contribution to a minidump file. An object carrying entries would be + //! considered useful. + //! + //! \return `true` if the object is useful, `false` otherwise. + bool IsUseful() const; + + protected: + // MinidumpWritable: + + bool Freeze() override; + size_t SizeOfObject() override; + std::vector Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + std::unique_ptr minidump_list_; + std::vector> objects_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_ANNOTATION_WRITER_H_ diff --git a/shared/sentry/external/crashpad/minidump/minidump_annotation_writer_test.cc b/shared/sentry/external/crashpad/minidump/minidump_annotation_writer_test.cc new file mode 100644 index 000000000..ff3c2a8e3 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_annotation_writer_test.cc @@ -0,0 +1,192 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_annotation_writer.h" + +#include +#include + +#include "gtest/gtest.h" +#include "minidump/minidump_extensions.h" +#include "minidump/test/minidump_byte_array_writer_test_util.h" +#include "minidump/test/minidump_string_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "util/file/string_file.h" + +namespace crashpad { +namespace test { +namespace { + +const MinidumpAnnotationList* MinidumpAnnotationListAtStart( + const std::string& file_contents, + uint32_t count) { + MINIDUMP_LOCATION_DESCRIPTOR location_descriptor; + location_descriptor.DataSize = static_cast( + sizeof(MinidumpAnnotationList) + count * sizeof(MinidumpAnnotation)); + location_descriptor.Rva = 0; + return MinidumpWritableAtLocationDescriptor( + file_contents, location_descriptor); +} + +TEST(MinidumpAnnotationWriter, EmptyList) { + StringFile string_file; + + MinidumpAnnotationListWriter list_writer; + + EXPECT_FALSE(list_writer.IsUseful()); + + EXPECT_TRUE(list_writer.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), sizeof(MinidumpAnnotationList)); + + auto* list = MinidumpAnnotationListAtStart(string_file.string(), 0); + ASSERT_TRUE(list); + EXPECT_EQ(list->count, 0u); +} + +TEST(MinidumpAnnotationWriter, OneItem) { + StringFile string_file; + + const char kName[] = "name"; + const uint16_t kType = 0xFFFF; + const std::vector kValue{'v', 'a', 'l', 'u', 'e', '\0'}; + + auto annotation_writer = std::make_unique(); + annotation_writer->InitializeWithData(kName, kType, kValue); + + MinidumpAnnotationListWriter list_writer; + list_writer.AddObject(std::move(annotation_writer)); + + EXPECT_TRUE(list_writer.IsUseful()); + + EXPECT_TRUE(list_writer.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), + sizeof(MinidumpAnnotationList) + sizeof(MinidumpAnnotation) + + sizeof(MinidumpUTF8String) + sizeof(kName) + + sizeof(MinidumpByteArray) + kValue.size() + + 3); // 3 for padding. + + auto* list = MinidumpAnnotationListAtStart(string_file.string(), 1); + ASSERT_TRUE(list); + EXPECT_EQ(list->count, 1u); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + list->objects[0].name), + kName); + EXPECT_EQ(list->objects[0].type, kType); + EXPECT_EQ(list->objects[0].reserved, 0u); + EXPECT_EQ( + + MinidumpByteArrayAtRVA(string_file.string(), list->objects[0].value), + kValue); +} + +TEST(MinidumpAnnotationWriter, ThreeItems) { + StringFile string_file; + + const char* kNames[] = { + "~~FIRST~~", " second + ", "3", + }; + const uint16_t kTypes[] = { + 0x1, 0xABCD, 0x42, + }; + const std::vector kValues[] = { + {'\0'}, {0xB0, 0xA0, 0xD0, 0xD0, 0xD0}, {'T'}, + }; + + MinidumpAnnotationListWriter list_writer; + + for (size_t i = 0; i < std::size(kNames); ++i) { + auto annotation = std::make_unique(); + annotation->InitializeWithData(kNames[i], kTypes[i], kValues[i]); + list_writer.AddObject(std::move(annotation)); + } + + EXPECT_TRUE(list_writer.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), + sizeof(MinidumpAnnotationList) + 3 * sizeof(MinidumpAnnotation) + + 3 * sizeof(MinidumpUTF8String) + 3 * sizeof(MinidumpByteArray) + + strlen(kNames[0]) + 1 + kValues[0].size() + 2 + + strlen(kNames[1]) + 1 + 3 + kValues[1].size() + 1 + + strlen(kNames[2]) + 1 + 3 + kValues[2].size() + 2); + + auto* list = MinidumpAnnotationListAtStart(string_file.string(), 3); + ASSERT_TRUE(list); + EXPECT_EQ(list->count, 3u); + + for (size_t i = 0; i < 3; ++i) { + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + list->objects[i].name), + kNames[i]); + EXPECT_EQ(list->objects[i].type, kTypes[i]); + EXPECT_EQ(list->objects[i].reserved, 0u); + EXPECT_EQ( + + MinidumpByteArrayAtRVA(string_file.string(), list->objects[i].value), + kValues[i]); + } +} + +TEST(MinidumpAnnotationWriter, DuplicateNames) { + StringFile string_file; + + const char kName[] = "@@name!"; + const uint16_t kType = 0x1; + const std::vector kValue1{'r', 'e', 'd', '\0'}; + const std::vector kValue2{'m', 'a', 'g', 'e', 'n', 't', 'a', '\0'}; + + MinidumpAnnotationListWriter list_writer; + + auto annotation = std::make_unique(); + annotation->InitializeWithData(kName, kType, kValue1); + list_writer.AddObject(std::move(annotation)); + + annotation = std::make_unique(); + annotation->InitializeWithData(kName, kType, kValue2); + list_writer.AddObject(std::move(annotation)); + + EXPECT_TRUE(list_writer.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), + sizeof(MinidumpAnnotationList) + 2 * sizeof(MinidumpAnnotation) + + 2 * sizeof(MinidumpUTF8String) + 2 * sizeof(MinidumpByteArray) + + 2 * sizeof(kName) + kValue1.size() + kValue2.size()); + + auto* list = MinidumpAnnotationListAtStart(string_file.string(), 2); + ASSERT_TRUE(list); + EXPECT_EQ(list->count, 2u); + + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + list->objects[0].name), + kName); + EXPECT_EQ(list->objects[0].type, kType); + EXPECT_EQ( + + MinidumpByteArrayAtRVA(string_file.string(), list->objects[0].value), + kValue1); + + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + list->objects[1].name), + kName); + EXPECT_EQ(list->objects[1].type, kType); + EXPECT_EQ( + + MinidumpByteArrayAtRVA(string_file.string(), list->objects[1].value), + kValue2); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_byte_array_writer.cc b/shared/sentry/external/crashpad/minidump/minidump_byte_array_writer.cc new file mode 100644 index 000000000..05b698dbe --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_byte_array_writer.cc @@ -0,0 +1,73 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_byte_array_writer.h" + +#include "base/logging.h" +#include "util/file/file_writer.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { + +MinidumpByteArrayWriter::MinidumpByteArrayWriter() + : minidump_array_(new MinidumpByteArray()) {} + +MinidumpByteArrayWriter::~MinidumpByteArrayWriter() = default; + +void MinidumpByteArrayWriter::set_data(const uint8_t* data, size_t size) { + data_.clear(); + data_.insert(data_.begin(), data, data + size); +} + +bool MinidumpByteArrayWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + size_t size = data_.size(); + if (!AssignIfInRange(&minidump_array_->length, size)) { + LOG(ERROR) << "data size " << size << " is out of range"; + return false; + } + + return true; +} + +size_t MinidumpByteArrayWriter::SizeOfObject() { + DCHECK_EQ(state(), kStateFrozen); + + return sizeof(*minidump_array_) + data_.size(); +} + +bool MinidumpByteArrayWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + WritableIoVec iov; + iov.iov_base = minidump_array_.get(); + iov.iov_len = sizeof(*minidump_array_); + + std::vector iovecs(1, iov); + + if (!data_.empty()) { + iov.iov_base = data_.data(); + iov.iov_len = data_.size(); + iovecs.push_back(iov); + } + + return file_writer->WriteIoVec(&iovecs); +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_byte_array_writer.h b/shared/sentry/external/crashpad/minidump/minidump_byte_array_writer.h new file mode 100644 index 000000000..2cbc279c5 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_byte_array_writer.h @@ -0,0 +1,66 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_BYTE_ARRAY_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_BYTE_ARRAY_WRITER_H_ + +#include +#include + +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_writable.h" + +namespace crashpad { + +//! \brief Writes a variable-length byte array for a minidump into a +//! \sa MinidumpByteArray. +class MinidumpByteArrayWriter final : public internal::MinidumpWritable { + public: + MinidumpByteArrayWriter(); + + MinidumpByteArrayWriter(const MinidumpByteArrayWriter&) = delete; + MinidumpByteArrayWriter& operator=(const MinidumpByteArrayWriter&) = delete; + + ~MinidumpByteArrayWriter() override; + + //! \brief Sets the data to be written. + //! + //! \note Valid in #kStateMutable. + void set_data(const std::vector& data) { data_ = data; } + + //! \brief Sets the data to be written. + //! + //! \note Valid in #kStateMutable. + void set_data(const uint8_t* data, size_t size); + + //! \brief Gets the data to be written. + //! + //! \note Valid in any state. + const std::vector& data() const { return data_; } + + protected: + // MinidumpWritable: + + bool Freeze() override; + size_t SizeOfObject() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + std::unique_ptr minidump_array_; + std::vector data_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_BYTE_ARRAY_WRITER_H_ diff --git a/shared/sentry/external/crashpad/minidump/minidump_byte_array_writer_test.cc b/shared/sentry/external/crashpad/minidump/minidump_byte_array_writer_test.cc new file mode 100644 index 000000000..8742ea448 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_byte_array_writer_test.cc @@ -0,0 +1,81 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_byte_array_writer.h" + +#include +#include + +#include "base/format_macros.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "util/file/string_file.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(MinidumpByteArrayWriter, Write) { + const std::vector kTests[] = { + {'h', 'e', 'l', 'l', 'o'}, + {0x42, 0x99, 0x00, 0xbe}, + {0x00}, + {}, + }; + + for (size_t i = 0; i < std::size(kTests); ++i) { + SCOPED_TRACE(base::StringPrintf("index %" PRIuS, i)); + + StringFile string_file; + + crashpad::MinidumpByteArrayWriter writer; + writer.set_data(kTests[i]); + EXPECT_TRUE(writer.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), + sizeof(MinidumpByteArray) + kTests[i].size()); + + auto byte_array = std::make_unique(); + EXPECT_EQ(string_file.Seek(0, SEEK_SET), 0); + string_file.Read(byte_array.get(), sizeof(*byte_array)); + + EXPECT_EQ(byte_array->length, kTests[i].size()); + + std::vector data(byte_array->length); + string_file.Read(data.data(), byte_array->length); + + EXPECT_EQ(data, kTests[i]); + } +} + +TEST(MinidumpByteArrayWriter, SetData) { + const std::vector kTests[] = { + {1, 2, 3, 4, 5}, + {0x0}, + {}, + }; + + for (size_t i = 0; i < std::size(kTests); ++i) { + SCOPED_TRACE(base::StringPrintf("index %" PRIuS, i)); + + crashpad::MinidumpByteArrayWriter writer; + writer.set_data(kTests[i].data(), kTests[i].size()); + EXPECT_EQ(writer.data(), kTests[i]); + } +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_context.h b/shared/sentry/external/crashpad/minidump/minidump_context.h new file mode 100644 index 000000000..3a3e603cb --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_context.h @@ -0,0 +1,597 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_H_ + +#include + +#include "base/compiler_specific.h" +#include "snapshot/cpu_context.h" +#include "util/numeric/int128.h" + +namespace crashpad { + +//! \brief Architecture-independent flags for `context_flags` fields in Minidump +//! context structures. +// +// https://zachsaw.blogspot.com/2010/11/wow64-bug-getthreadcontext-may-return.html#c5639760895973344002 +enum MinidumpContextFlags : uint32_t { + //! \brief The thread was executing a trap handler in kernel mode + //! (`CONTEXT_EXCEPTION_ACTIVE`). + //! + //! If this bit is set, it indicates that the context is from a thread that + //! was executing a trap handler in the kernel. This bit is only valid when + //! ::kMinidumpContextExceptionReporting is also set. This bit is only used on + //! Windows. + kMinidumpContextExceptionActive = 0x08000000, + + //! \brief The thread was executing a system call in kernel mode + //! (`CONTEXT_SERVICE_ACTIVE`). + //! + //! If this bit is set, it indicates that the context is from a thread that + //! was executing a system call in the kernel. This bit is only valid when + //! ::kMinidumpContextExceptionReporting is also set. This bit is only used on + //! Windows. + kMinidumpContextServiceActive = 0x10000000, + + //! \brief Kernel-mode state reporting is desired + //! (`CONTEXT_EXCEPTION_REQUEST`). + //! + //! This bit is not used in context structures containing snapshots of thread + //! CPU context. It used when calling `GetThreadContext()` on Windows to + //! specify that kernel-mode state reporting + //! (::kMinidumpContextExceptionReporting) is desired in the returned context + //! structure. + kMinidumpContextExceptionRequest = 0x40000000, + + //! \brief Kernel-mode state reporting is provided + //! (`CONTEXT_EXCEPTION_REPORTING`). + //! + //! If this bit is set, it indicates that the bits indicating how the thread + //! had entered kernel mode (::kMinidumpContextExceptionActive and + //! ::kMinidumpContextServiceActive) are valid. This bit is only used on + //! Windows. + kMinidumpContextExceptionReporting = 0x80000000, +}; + +//! \brief 32-bit x86-specifc flags for MinidumpContextX86::context_flags. +enum MinidumpContextX86Flags : uint32_t { + //! \brief Identifies the context structure as 32-bit x86. This is the same as + //! `CONTEXT_i386` and `CONTEXT_i486` on Windows for this architecture. + kMinidumpContextX86 = 0x00010000, + + //! \brief Indicates the validity of control registers (`CONTEXT_CONTROL`). + //! + //! The `ebp`, `eip`, `cs`, `eflags`, `esp`, and `ss` fields are valid. + kMinidumpContextX86Control = kMinidumpContextX86 | 0x00000001, + + //! \brief Indicates the validity of non-control integer registers + //! (`CONTEXT_INTEGER`). + //! + //! The `edi`, `esi`, `ebx`, `edx`, `ecx, and `eax` fields are valid. + kMinidumpContextX86Integer = kMinidumpContextX86 | 0x00000002, + + //! \brief Indicates the validity of non-control segment registers + //! (`CONTEXT_SEGMENTS`). + //! + //! The `gs`, `fs`, `es`, and `ds` fields are valid. + kMinidumpContextX86Segment = kMinidumpContextX86 | 0x00000004, + + //! \brief Indicates the validity of floating-point state + //! (`CONTEXT_FLOATING_POINT`). + //! + //! The `fsave` field is valid. The `float_save` field is included in this + //! definition, but its members have no practical use asdie from `fsave`. + kMinidumpContextX86FloatingPoint = kMinidumpContextX86 | 0x00000008, + + //! \brief Indicates the validity of debug registers + //! (`CONTEXT_DEBUG_REGISTERS`). + //! + //! The `dr0` through `dr3`, `dr6`, and `dr7` fields are valid. + kMinidumpContextX86Debug = kMinidumpContextX86 | 0x00000010, + + //! \brief Indicates the validity of extended registers in `fxsave` format + //! (`CONTEXT_EXTENDED_REGISTERS`). + //! + //! The `extended_registers` field is valid and contains `fxsave` data. + kMinidumpContextX86Extended = kMinidumpContextX86 | 0x00000020, + + //! \brief Indicates the validity of `xsave` data (`CONTEXT_XSTATE`). + //! + //! The context contains `xsave` data. This is used with an extended context + //! structure not currently defined here. + kMinidumpContextX86Xstate = kMinidumpContextX86 | 0x00000040, + + //! \brief Indicates the validity of control, integer, and segment registers. + //! (`CONTEXT_FULL`). + kMinidumpContextX86Full = kMinidumpContextX86Control | + kMinidumpContextX86Integer | + kMinidumpContextX86Segment, + + //! \brief Indicates the validity of all registers except `xsave` data. + //! (`CONTEXT_ALL`). + kMinidumpContextX86All = kMinidumpContextX86Full | + kMinidumpContextX86FloatingPoint | + kMinidumpContextX86Debug | + kMinidumpContextX86Extended, +}; + +//! \brief A 32-bit x86 CPU context (register state) carried in a minidump file. +//! +//! This is analogous to the `CONTEXT` structure on Windows when targeting +//! 32-bit x86, and the `WOW64_CONTEXT` structure when targeting an x86-family +//! CPU, either 32- or 64-bit. This structure is used instead of `CONTEXT` or +//! `WOW64_CONTEXT` to make it available when targeting other architectures. +//! +//! \note This structure doesn’t carry `dr4` or `dr5`, which are obsolete and +//! normally alias `dr6` and `dr7`, respectively. See Intel Software +//! Developer’s Manual, Volume 3B: System Programming, Part 2 (253669-052), +//! 17.2.2 “Debug Registers DR4 and DR5â€. +struct MinidumpContextX86 { + //! \brief A bitfield composed of values of #MinidumpContextFlags and + //! #MinidumpContextX86Flags. + //! + //! This field identifies the context structure as a 32-bit x86 CPU context, + //! and indicates which other fields in the structure are valid. + uint32_t context_flags; + + uint32_t dr0; + uint32_t dr1; + uint32_t dr2; + uint32_t dr3; + uint32_t dr6; + uint32_t dr7; + + // CPUContextX86::Fsave has identical layout to what the x86 CONTEXT structure + // places here. + CPUContextX86::Fsave fsave; + union { + uint32_t spare_0; // As in the native x86 CONTEXT structure since Windows 8 + uint32_t cr0_npx_state; // As in WOW64_CONTEXT and older SDKs’ x86 CONTEXT + } float_save; + + uint32_t gs; + uint32_t fs; + uint32_t es; + uint32_t ds; + + uint32_t edi; + uint32_t esi; + uint32_t ebx; + uint32_t edx; + uint32_t ecx; + uint32_t eax; + + uint32_t ebp; + uint32_t eip; + uint32_t cs; + uint32_t eflags; + uint32_t esp; + uint32_t ss; + + // CPUContextX86::Fxsave has identical layout to what the x86 CONTEXT + // structure places here. + CPUContextX86::Fxsave fxsave; +}; + +//! \brief x86_64-specific flags for MinidumpContextAMD64::context_flags. +enum MinidumpContextAMD64Flags : uint32_t { + //! \brief Identifies the context structure as x86_64. This is the same as + //! `CONTEXT_AMD64` on Windows for this architecture. + kMinidumpContextAMD64 = 0x00100000, + + //! \brief Indicates the validity of control registers (`CONTEXT_CONTROL`). + //! + //! The `cs`, `ss`, `eflags`, `rsp`, and `rip` fields are valid. + kMinidumpContextAMD64Control = kMinidumpContextAMD64 | 0x00000001, + + //! \brief Indicates the validity of non-control integer registers + //! (`CONTEXT_INTEGER`). + //! + //! The `rax`, `rcx`, `rdx`, `rbx`, `rbp`, `rsi`, `rdi`, and `r8` through + //! `r15` fields are valid. + kMinidumpContextAMD64Integer = kMinidumpContextAMD64 | 0x00000002, + + //! \brief Indicates the validity of non-control segment registers + //! (`CONTEXT_SEGMENTS`). + //! + //! The `ds`, `es`, `fs`, and `gs` fields are valid. + kMinidumpContextAMD64Segment = kMinidumpContextAMD64 | 0x00000004, + + //! \brief Indicates the validity of floating-point state + //! (`CONTEXT_FLOATING_POINT`). + //! + //! The `xmm0` through `xmm15` fields are valid. + kMinidumpContextAMD64FloatingPoint = kMinidumpContextAMD64 | 0x00000008, + + //! \brief Indicates the validity of debug registers + //! (`CONTEXT_DEBUG_REGISTERS`). + //! + //! The `dr0` through `dr3`, `dr6`, and `dr7` fields are valid. + kMinidumpContextAMD64Debug = kMinidumpContextAMD64 | 0x00000010, + + //! \brief Indicates the validity of `xsave` data (`CONTEXT_XSTATE`). + //! + //! The context contains `xsave` data. This is used with an extended context + //! structure not currently defined here. + kMinidumpContextAMD64Xstate = kMinidumpContextAMD64 | 0x00000040, + + //! \brief Indicates the validity of control, integer, and floating-point + //! registers (`CONTEXT_FULL`). + kMinidumpContextAMD64Full = kMinidumpContextAMD64Control | + kMinidumpContextAMD64Integer | + kMinidumpContextAMD64FloatingPoint, + + //! \brief Indicates the validity of all registers except `xsave` data + //! (`CONTEXT_ALL`). + kMinidumpContextAMD64All = kMinidumpContextAMD64Full | + kMinidumpContextAMD64Segment | + kMinidumpContextAMD64Debug, +}; + +//! \brief An x86_64 (AMD64) CPU context (register state) carried in a minidump +//! file. +//! +//! This is analogous to the `CONTEXT` structure on Windows when targeting +//! x86_64. This structure is used instead of `CONTEXT` to make it available +//! when targeting other architectures. +//! +//! \note This structure doesn’t carry `dr4` or `dr5`, which are obsolete and +//! normally alias `dr6` and `dr7`, respectively. See Intel Software +//! Developer’s Manual, Volume 3B: System Programming, Part 2 (253669-052), +//! 17.2.2 “Debug Registers DR4 and DR5â€. +struct alignas(16) MinidumpContextAMD64 { + //! \brief Register parameter home address. + //! + //! On Windows, this field may contain the “home†address (on-stack, in the + //! shadow area) of a parameter passed by register. This field is present for + //! convenience but is not necessarily populated, even if a corresponding + //! parameter was passed by register. + //! + //! \{ + uint64_t p1_home; + uint64_t p2_home; + uint64_t p3_home; + uint64_t p4_home; + uint64_t p5_home; + uint64_t p6_home; + //! \} + + //! \brief A bitfield composed of values of #MinidumpContextFlags and + //! #MinidumpContextAMD64Flags. + //! + //! This field identifies the context structure as an x86_64 CPU context, and + //! indicates which other fields in the structure are valid. + uint32_t context_flags; + + uint32_t mx_csr; + + uint16_t cs; + uint16_t ds; + uint16_t es; + uint16_t fs; + uint16_t gs; + uint16_t ss; + + uint32_t eflags; + + uint64_t dr0; + uint64_t dr1; + uint64_t dr2; + uint64_t dr3; + uint64_t dr6; + uint64_t dr7; + + uint64_t rax; + uint64_t rcx; + uint64_t rdx; + uint64_t rbx; + uint64_t rsp; + uint64_t rbp; + uint64_t rsi; + uint64_t rdi; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + + uint64_t rip; + + // CPUContextX86_64::Fxsave has identical layout to what the x86_64 CONTEXT + // structure places here. + CPUContextX86_64::Fxsave fxsave; + + uint128_struct vector_register[26]; + uint64_t vector_control; + + //! \brief Model-specific debug extension register. + //! + //! See Intel Software Developer’s Manual, Volume 3B: System Programming, Part + //! 2 (253669-051), 17.4 “Last Branch, Interrupt, and Exception Recording + //! Overviewâ€, and AMD Architecture Programmer’s Manual, Volume 2: System + //! Programming (24593-3.24), 13.1.6 “Control-Transfer Breakpoint Featuresâ€. + //! + //! \{ + uint64_t debug_control; + uint64_t last_branch_to_rip; + uint64_t last_branch_from_rip; + uint64_t last_exception_to_rip; + uint64_t last_exception_from_rip; + //! \} +}; + +//! \brief 32-bit ARM-specifc flags for MinidumpContextARM::context_flags. +enum MinidumpContextARMFlags : uint32_t { + //! \brief Identifies the context structure as 32-bit ARM. + kMinidumpContextARM = 0x40000000, + + //! \brief Indicates the validity of integer regsiters. + //! + //! Regsiters `r0`-`r15` and `cpsr` are valid. + kMinidumpContextARMInteger = kMinidumpContextARM | 0x00000002, + + //! \brief Inidicates the validity of VFP regsiters. + //! + //! Registers `d0`-`d31` and `fpscr` are valid. + kMinidumpContextARMVFP = kMinidumpContextARM | 0x00000004, + + //! \brief Indicates the validity of all registers. + kMinidumpContextARMAll = kMinidumpContextARMInteger | kMinidumpContextARMVFP, +}; + +//! \brief A 32-bit ARM CPU context (register state) carried in a minidump file. +struct MinidumpContextARM { + //! \brief A bitfield composed of values of #MinidumpContextFlags and + //! #MinidumpContextARMFlags. + //! + //! This field identifies the context structure as a 32-bit ARM CPU context, + //! and indicates which other fields in the structure are valid. + uint32_t context_flags; + + //! \brief General-purpose registers `r0`-`r15`. + uint32_t regs[11]; + uint32_t fp; // r11 + uint32_t ip; // r12 + uint32_t sp; // r13 + uint32_t lr; // r14 + uint32_t pc; // r15 + + //! \brief Current program status register. + uint32_t cpsr; + + //! \brief Floating-point status and control register. + uint32_t fpscr; + + //! \brief VFP registers `d0`-`d31`. + uint64_t vfp[32]; + + //! \brief This space is unused. It is included for compatibility with + //! breakpad (which also doesn't use it). + uint32_t extra[8]; +}; + +//! \brief 64-bit ARM-specifc flags for MinidumpContextARM64::context_flags. +enum MinidumpContextARM64Flags : uint32_t { + //! \brief Identifies the context structure as 64-bit ARM. + kMinidumpContextARM64 = 0x00400000, + + //! \brief Indicates the validity of control registers. + //! + //! Registers `fp`, `lr`, `sp`, `pc`, and `cpsr`. + kMinidumpContextARM64Control = kMinidumpContextARM64 | 0x00000001, + + //! \brief Indicates the validty of integer registers. + //! + //! Registers `x0`-`x28`. + kMinidumpContextARM64Integer = kMinidumpContextARM64 | 0x00000002, + + //! \brief Indicates the validity of fpsimd registers. + //! + //! Registers `v0`-`v31`, `fpsr`, and `fpcr` are valid. + kMinidumpContextARM64Fpsimd = kMinidumpContextARM64 | 0x00000004, + + //! \brief Indicates the validity of debug registers. + //! + //! `bcr`, `bvr`, `wcr`, and `wvr` are valid. + kMinidumpContextARM64Debug = kMinidumpContextARM64 | 0x00000008, + + //! \brief Indicates the validity of control, integer and floating point + //! registers. + kMinidumpContextARM64Full = kMinidumpContextARM64Control | + kMinidumpContextARM64Integer | + kMinidumpContextARM64Fpsimd, + + //! \brief Indicates the validity of all registers. + kMinidumpContextARM64All = + kMinidumpContextARM64Full | kMinidumpContextARM64Debug, +}; + +//! \brief A 64-bit ARM CPU context (register state) carried in a minidump file. +struct MinidumpContextARM64 { + uint32_t context_flags; + + //! \brief Current program status register. + uint32_t cpsr; + + //! \brief General-purpose registers `x0`-`x28`. + uint64_t regs[29]; + + //! \brief Frame pointer or `x29`. + uint64_t fp; + + //! \brief Link register or `x30`. + uint64_t lr; + + //! \brief Stack pointer or `x31`. + uint64_t sp; + + //! \brief Program counter. + uint64_t pc; + + //! \brief NEON registers `v0`-`v31`. + uint128_struct fpsimd[32]; + + //! \brief Floating-point control register. + uint32_t fpcr; + + //! \brief Floating-point status register. + uint32_t fpsr; + + //! \brief Debug registers. + uint32_t bcr[8]; + uint64_t bvr[8]; + uint32_t wcr[2]; + uint64_t wvr[2]; +}; + +//! \brief 32bit MIPS-specifc flags for MinidumpContextMIPS::context_flags. +//! Based on minidump_cpu_mips.h from breakpad +enum MinidumpContextMIPSFlags : uint32_t { + //! \brief Identifies the context structure as MIPSEL. + kMinidumpContextMIPS = 0x00040000, + + //! \brief Indicates the validity of integer registers. + //! + //! Registers `0`-`31`, `mdhi`, `mdlo`, `epc`, `badvaddr`, `status` and + //! `cause` are valid. + kMinidumpContextMIPSInteger = kMinidumpContextMIPS | 0x00000002, + + //! \brief Indicates the validity of floating point registers. + //! + //! Floating point registers `0`-`31`, `fpcsr` and `fir` are valid + kMinidumpContextMIPSFloatingPoint = kMinidumpContextMIPS | 0x00000004, + + //! \brief Indicates the validity of DSP registers. + //! + //! Registers `hi0`-`hi2`, `lo0`-`lo2` and `dsp_control` are valid + kMinidumpContextMIPSDSP = kMinidumpContextMIPS | 0x00000008, + + //! \brief Indicates the validity of all registers. + kMinidumpContextMIPSAll = kMinidumpContextMIPSInteger | + kMinidumpContextMIPSFloatingPoint | + kMinidumpContextMIPSDSP, +}; + +//! \brief A 32bit MIPS CPU context (register state) carried in a minidump file. +struct MinidumpContextMIPS { + uint32_t context_flags; + + //! \brief This padding field is included for breakpad compatibility. + uint32_t _pad0; + //! \brief General purpose registers `0`-`31`. + uint64_t regs[32]; + + //! \brief Multiply/divide result. + uint64_t mdhi, mdlo; + + //! \brief DSP registers. + uint32_t hi[3]; + uint32_t lo[3]; + uint32_t dsp_control; + //! \brief This padding field is included for breakpad compatibility. + uint32_t _pad1; + + // \brief cp0 registers. + uint64_t epc; + uint64_t badvaddr; + uint32_t status; + uint32_t cause; + + //! \brief FPU registers. + union { + struct { + float _fp_fregs; + uint32_t _fp_pad; + } fregs[32]; + double dregs[32]; + } fpregs; + + //! \brief FPU status register. + uint32_t fpcsr; + //! \brief FPU implementation register. + uint32_t fir; +}; + +//! \brief 64bit MIPS-specifc flags for MinidumpContextMIPS64::context_flags. +//! Based on minidump_cpu_mips.h from breakpad +enum MinidumpContextMIPS64Flags : uint32_t { + //! \brief Identifies the context structure as MIPS64EL. + kMinidumpContextMIPS64 = 0x00080000, + + //! \brief Indicates the validity of integer registers. + //! + //! Registers `0`-`31`, `mdhi`, `mdlo`, `epc`, `badvaddr`, `status` and + //! `cause` are valid. + kMinidumpContextMIPS64Integer = kMinidumpContextMIPS64 | 0x00000002, + + //! \brief Indicates the validity of floating point registers. + //! + //! Floating point registers `0`-`31`, `fpcsr` and `fir` are valid + kMinidumpContextMIPS64FloatingPoint = kMinidumpContextMIPS64 | 0x00000004, + + //! \brief Indicates the validity of DSP registers. + //! + //! Registers `hi0`-`hi2`, `lo0`-`lo2` and `dsp_control` are valid. + kMinidumpContextMIPS64DSP = kMinidumpContextMIPS64 | 0x00000008, + + //! \brief Indicates the validity of all registers. + kMinidumpContextMIPS64All = kMinidumpContextMIPS64Integer | + kMinidumpContextMIPS64FloatingPoint | + kMinidumpContextMIPS64DSP, +}; + +//! \brief A 32bit MIPS CPU context (register state) carried in a minidump file. +struct MinidumpContextMIPS64 { + uint64_t context_flags; + + //! \brief General purpose registers. + uint64_t regs[32]; + + //! \brief Multiply/divide result. + uint64_t mdhi, mdlo; + + //! \brief DSP registers. + uint64_t hi[3]; + uint64_t lo[3]; + uint64_t dsp_control; + + //! \brief cp0 registers. + uint64_t epc; + uint64_t badvaddr; + uint64_t status; + uint64_t cause; + + //! \brief FPU registers. + union { + struct { + float _fp_fregs; + uint32_t _fp_pad; + } fregs[32]; + double dregs[32]; + } fpregs; + + //! \brief FPU status register. + uint64_t fpcsr; + //! \brief FPU implementation register. + uint64_t fir; +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_H_ diff --git a/shared/sentry/external/crashpad/minidump/minidump_context_writer.cc b/shared/sentry/external/crashpad/minidump/minidump_context_writer.cc new file mode 100644 index 000000000..2f2d90ba4 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_context_writer.cc @@ -0,0 +1,457 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_context_writer.h" + +#include +#include +#include +#include + +#include "base/compiler_specific.h" +#include "base/logging.h" +#include "build/build_config.h" +#include "snapshot/cpu_context.h" +#include "util/file/file_writer.h" +#include "util/stdlib/aligned_allocator.h" + +namespace crashpad { + +namespace { + +// Sanity-check complex structures to ensure interoperability. +static_assert(sizeof(MinidumpContextX86) == 716, "MinidumpContextX86 size"); +static_assert(sizeof(MinidumpContextAMD64) == 1232, + "MinidumpContextAMD64 size"); + +// These structures can also be checked against definitions in the Windows SDK. +#if BUILDFLAG(IS_WIN) +#if defined(ARCH_CPU_X86_FAMILY) +static_assert(sizeof(MinidumpContextX86) == sizeof(WOW64_CONTEXT), + "WOW64_CONTEXT size"); +#if defined(ARCH_CPU_X86) +static_assert(sizeof(MinidumpContextX86) == sizeof(CONTEXT), "CONTEXT size"); +#elif defined(ARCH_CPU_X86_64) +static_assert(sizeof(MinidumpContextAMD64) == sizeof(CONTEXT), "CONTEXT size"); +#endif +#endif // ARCH_CPU_X86_FAMILY +#endif // BUILDFLAG(IS_WIN) + +} // namespace + +MinidumpContextWriter::~MinidumpContextWriter() { +} + +// static +std::unique_ptr +MinidumpContextWriter::CreateFromSnapshot(const CPUContext* context_snapshot) { + std::unique_ptr context; + + switch (context_snapshot->architecture) { + case kCPUArchitectureX86: { + MinidumpContextX86Writer* context_x86 = new MinidumpContextX86Writer(); + context.reset(context_x86); + context_x86->InitializeFromSnapshot(context_snapshot->x86); + break; + } + + case kCPUArchitectureX86_64: { + MinidumpContextAMD64Writer* context_amd64 = + new MinidumpContextAMD64Writer(); + context.reset(context_amd64); + context_amd64->InitializeFromSnapshot(context_snapshot->x86_64); + break; + } + + case kCPUArchitectureARM: { + context = std::make_unique(); + reinterpret_cast(context.get()) + ->InitializeFromSnapshot(context_snapshot->arm); + break; + } + + case kCPUArchitectureARM64: { + context = std::make_unique(); + reinterpret_cast(context.get()) + ->InitializeFromSnapshot(context_snapshot->arm64); + break; + } + + case kCPUArchitectureMIPSEL: { + context = std::make_unique(); + reinterpret_cast(context.get()) + ->InitializeFromSnapshot(context_snapshot->mipsel); + break; + } + + case kCPUArchitectureMIPS64EL: { + context = std::make_unique(); + reinterpret_cast(context.get()) + ->InitializeFromSnapshot(context_snapshot->mips64); + break; + } + + default: { + LOG(ERROR) << "unknown context architecture " + << context_snapshot->architecture; + break; + } + } + + return context; +} + +size_t MinidumpContextWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return ContextSize(); +} + +MinidumpContextX86Writer::MinidumpContextX86Writer() + : MinidumpContextWriter(), context_() { + context_.context_flags = kMinidumpContextX86; +} + +MinidumpContextX86Writer::~MinidumpContextX86Writer() { +} + + +void MinidumpContextX86Writer::InitializeFromSnapshot( + const CPUContextX86* context_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK_EQ(context_.context_flags, kMinidumpContextX86); + + context_.context_flags = kMinidumpContextX86All; + + context_.dr0 = context_snapshot->dr0; + context_.dr1 = context_snapshot->dr1; + context_.dr2 = context_snapshot->dr2; + context_.dr3 = context_snapshot->dr3; + context_.dr6 = context_snapshot->dr6; + context_.dr7 = context_snapshot->dr7; + + // The contents of context_.fsave effectively alias everything in + // context_.fxsave that’s related to x87 FPU state. context_.fsave doesn’t + // carry state specific to SSE (or later), such as mxcsr and the xmm + // registers. + CPUContextX86::FxsaveToFsave(context_snapshot->fxsave, &context_.fsave); + + context_.gs = context_snapshot->gs; + context_.fs = context_snapshot->fs; + context_.es = context_snapshot->es; + context_.ds = context_snapshot->ds; + context_.edi = context_snapshot->edi; + context_.esi = context_snapshot->esi; + context_.ebx = context_snapshot->ebx; + context_.edx = context_snapshot->edx; + context_.ecx = context_snapshot->ecx; + context_.eax = context_snapshot->eax; + context_.ebp = context_snapshot->ebp; + context_.eip = context_snapshot->eip; + context_.cs = context_snapshot->cs; + context_.eflags = context_snapshot->eflags; + context_.esp = context_snapshot->esp; + context_.ss = context_snapshot->ss; + + // This is effectively a memcpy() of a big structure. + context_.fxsave = context_snapshot->fxsave; +} + +bool MinidumpContextX86Writer::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + return file_writer->Write(&context_, sizeof(context_)); +} + +size_t MinidumpContextX86Writer::ContextSize() const { + DCHECK_GE(state(), kStateFrozen); + + return sizeof(context_); +} + +static_assert(alignof(MinidumpContextAMD64) >= 16, + "MinidumpContextAMD64 alignment"); +static_assert(alignof(MinidumpContextAMD64Writer) >= + alignof(MinidumpContextAMD64), + "MinidumpContextAMD64Writer alignment"); + +MinidumpContextAMD64Writer::MinidumpContextAMD64Writer() + : MinidumpContextWriter(), context_() { + context_.context_flags = kMinidumpContextAMD64; +} + +MinidumpContextAMD64Writer::~MinidumpContextAMD64Writer() { +} + +// static +void* MinidumpContextAMD64Writer::operator new(size_t size) { + // MinidumpContextAMD64 requests an alignment of 16, which can be larger than + // what standard new provides. This may trigger MSVC warning C4316. As a + // workaround to this language deficiency, provide a custom allocation + // function to allocate a block meeting the alignment requirement. + return AlignedAllocate(alignof(MinidumpContextAMD64Writer), size); +} + +// static +void MinidumpContextAMD64Writer::operator delete(void* pointer) { + return AlignedFree(pointer); +} + +void MinidumpContextAMD64Writer::InitializeFromSnapshot( + const CPUContextX86_64* context_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK_EQ(context_.context_flags, kMinidumpContextAMD64); + + context_.context_flags = kMinidumpContextAMD64All; + + context_.mx_csr = context_snapshot->fxsave.mxcsr; + context_.cs = context_snapshot->cs; + context_.fs = context_snapshot->fs; + context_.gs = context_snapshot->gs; + // The top 32 bits of rflags are reserved/unused. + context_.eflags = static_cast(context_snapshot->rflags); + context_.dr0 = context_snapshot->dr0; + context_.dr1 = context_snapshot->dr1; + context_.dr2 = context_snapshot->dr2; + context_.dr3 = context_snapshot->dr3; + context_.dr6 = context_snapshot->dr6; + context_.dr7 = context_snapshot->dr7; + context_.rax = context_snapshot->rax; + context_.rcx = context_snapshot->rcx; + context_.rdx = context_snapshot->rdx; + context_.rbx = context_snapshot->rbx; + context_.rsp = context_snapshot->rsp; + context_.rbp = context_snapshot->rbp; + context_.rsi = context_snapshot->rsi; + context_.rdi = context_snapshot->rdi; + context_.r8 = context_snapshot->r8; + context_.r9 = context_snapshot->r9; + context_.r10 = context_snapshot->r10; + context_.r11 = context_snapshot->r11; + context_.r12 = context_snapshot->r12; + context_.r13 = context_snapshot->r13; + context_.r14 = context_snapshot->r14; + context_.r15 = context_snapshot->r15; + context_.rip = context_snapshot->rip; + + // This is effectively a memcpy() of a big structure. + context_.fxsave = context_snapshot->fxsave; +} + +size_t MinidumpContextAMD64Writer::Alignment() { + DCHECK_GE(state(), kStateFrozen); + + // Match the alignment of MinidumpContextAMD64. + return 16; +} + +bool MinidumpContextAMD64Writer::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + return file_writer->Write(&context_, sizeof(context_)); +} + +size_t MinidumpContextAMD64Writer::ContextSize() const { + DCHECK_GE(state(), kStateFrozen); + + return sizeof(context_); +} + +MinidumpContextARMWriter::MinidumpContextARMWriter() + : MinidumpContextWriter(), context_() { + context_.context_flags = kMinidumpContextARM; +} + +MinidumpContextARMWriter::~MinidumpContextARMWriter() = default; + +void MinidumpContextARMWriter::InitializeFromSnapshot( + const CPUContextARM* context_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK_EQ(context_.context_flags, kMinidumpContextARM); + + context_.context_flags = kMinidumpContextARMAll; + + static_assert(sizeof(context_.regs) == sizeof(context_snapshot->regs), + "GPRS size mismatch"); + memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs)); + context_.fp = context_snapshot->fp; + context_.ip = context_snapshot->ip; + context_.sp = context_snapshot->sp; + context_.lr = context_snapshot->lr; + context_.pc = context_snapshot->pc; + context_.cpsr = context_snapshot->cpsr; + + context_.fpscr = context_snapshot->vfp_regs.fpscr; + static_assert(sizeof(context_.vfp) == sizeof(context_snapshot->vfp_regs.vfp), + "VFP size mismatch"); + memcpy(context_.vfp, context_snapshot->vfp_regs.vfp, sizeof(context_.vfp)); + + memset(context_.extra, 0, sizeof(context_.extra)); +} + +bool MinidumpContextARMWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + return file_writer->Write(&context_, sizeof(context_)); +} + +size_t MinidumpContextARMWriter::ContextSize() const { + DCHECK_GE(state(), kStateFrozen); + return sizeof(context_); +} + +MinidumpContextARM64Writer::MinidumpContextARM64Writer() + : MinidumpContextWriter(), context_() { + context_.context_flags = kMinidumpContextARM64; +} + +MinidumpContextARM64Writer::~MinidumpContextARM64Writer() = default; + +void MinidumpContextARM64Writer::InitializeFromSnapshot( + const CPUContextARM64* context_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK_EQ(context_.context_flags, kMinidumpContextARM64); + + context_.context_flags = kMinidumpContextARM64Full; + + static_assert( + sizeof(context_.regs) == sizeof(context_snapshot->regs) - + 2 * sizeof(context_snapshot->regs[0]), + "GPRs size mismatch"); + memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs)); + context_.fp = context_snapshot->regs[29]; + context_.lr = context_snapshot->regs[30]; + context_.sp = context_snapshot->sp; + context_.pc = context_snapshot->pc; + context_.cpsr = context_snapshot->spsr; + + static_assert(sizeof(context_.fpsimd) == sizeof(context_snapshot->fpsimd), + "FPSIMD size mismatch"); + memcpy(context_.fpsimd, context_snapshot->fpsimd, sizeof(context_.fpsimd)); + context_.fpcr = context_snapshot->fpcr; + context_.fpsr = context_snapshot->fpsr; + + memset(context_.bcr, 0, sizeof(context_.bcr)); + memset(context_.bvr, 0, sizeof(context_.bvr)); + memset(context_.wcr, 0, sizeof(context_.wcr)); + memset(context_.wvr, 0, sizeof(context_.wvr)); +} + +bool MinidumpContextARM64Writer::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + return file_writer->Write(&context_, sizeof(context_)); +} + +size_t MinidumpContextARM64Writer::ContextSize() const { + DCHECK_GE(state(), kStateFrozen); + return sizeof(context_); +} + +MinidumpContextMIPSWriter::MinidumpContextMIPSWriter() + : MinidumpContextWriter(), context_() { + context_.context_flags = kMinidumpContextMIPS; +} + +MinidumpContextMIPSWriter::~MinidumpContextMIPSWriter() = default; + +void MinidumpContextMIPSWriter::InitializeFromSnapshot( + const CPUContextMIPS* context_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK_EQ(context_.context_flags, kMinidumpContextMIPS); + + context_.context_flags = kMinidumpContextMIPSAll; + + static_assert(sizeof(context_.regs) == sizeof(context_snapshot->regs), + "GPRs size mismatch"); + memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs)); + context_.mdhi = context_snapshot->mdhi; + context_.mdlo = context_snapshot->mdlo; + context_.epc = context_snapshot->cp0_epc; + context_.badvaddr = context_snapshot->cp0_badvaddr; + context_.status = context_snapshot->cp0_status; + context_.cause = context_snapshot->cp0_cause; + + static_assert(sizeof(context_.fpregs) == sizeof(context_snapshot->fpregs), + "FPRs size mismatch"); + memcpy(&context_.fpregs, &context_snapshot->fpregs, sizeof(context_.fpregs)); + context_.fpcsr = context_snapshot->fpcsr; + context_.fir = context_snapshot->fir; + + for (size_t index = 0; index < 3; ++index) { + context_.hi[index] = context_snapshot->hi[index]; + context_.lo[index] = context_snapshot->lo[index]; + } + context_.dsp_control = context_snapshot->dsp_control; +} + +bool MinidumpContextMIPSWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + return file_writer->Write(&context_, sizeof(context_)); +} + +size_t MinidumpContextMIPSWriter::ContextSize() const { + DCHECK_GE(state(), kStateFrozen); + return sizeof(context_); +} + +MinidumpContextMIPS64Writer::MinidumpContextMIPS64Writer() + : MinidumpContextWriter(), context_() { + context_.context_flags = kMinidumpContextMIPS64; +} + +MinidumpContextMIPS64Writer::~MinidumpContextMIPS64Writer() = default; + +void MinidumpContextMIPS64Writer::InitializeFromSnapshot( + const CPUContextMIPS64* context_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK_EQ(context_.context_flags, kMinidumpContextMIPS64); + + context_.context_flags = kMinidumpContextMIPS64All; + + static_assert(sizeof(context_.regs) == sizeof(context_snapshot->regs), + "GPRs size mismatch"); + memcpy(context_.regs, context_snapshot->regs, sizeof(context_.regs)); + context_.mdhi = context_snapshot->mdhi; + context_.mdlo = context_snapshot->mdlo; + context_.epc = context_snapshot->cp0_epc; + context_.badvaddr = context_snapshot->cp0_badvaddr; + context_.status = context_snapshot->cp0_status; + context_.cause = context_snapshot->cp0_cause; + + static_assert(sizeof(context_.fpregs) == sizeof(context_snapshot->fpregs), + "FPRs size mismatch"); + memcpy(context_.fpregs.dregs, + context_snapshot->fpregs.dregs, + sizeof(context_.fpregs.dregs)); + context_.fpcsr = context_snapshot->fpcsr; + context_.fir = context_snapshot->fir; + + for (size_t index = 0; index < 3; ++index) { + context_.hi[index] = context_snapshot->hi[index]; + context_.lo[index] = context_snapshot->lo[index]; + } + context_.dsp_control = context_snapshot->dsp_control; +} + +bool MinidumpContextMIPS64Writer::WriteObject( + FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + return file_writer->Write(&context_, sizeof(context_)); +} + +size_t MinidumpContextMIPS64Writer::ContextSize() const { + DCHECK_GE(state(), kStateFrozen); + return sizeof(context_); +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_context_writer.h b/shared/sentry/external/crashpad/minidump/minidump_context_writer.h new file mode 100644 index 000000000..80b312b23 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_context_writer.h @@ -0,0 +1,335 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_WRITER_H_ + +#include + +#include + +#include "minidump/minidump_context.h" +#include "minidump/minidump_writable.h" + +namespace crashpad { + +struct CPUContext; +struct CPUContextX86; +struct CPUContextX86_64; + +//! \brief The base class for writers of CPU context structures in minidump +//! files. +class MinidumpContextWriter : public internal::MinidumpWritable { + public: + MinidumpContextWriter(const MinidumpContextWriter&) = delete; + MinidumpContextWriter& operator=(const MinidumpContextWriter&) = delete; + + ~MinidumpContextWriter() override; + + //! \brief Creates a MinidumpContextWriter based on \a context_snapshot. + //! + //! \param[in] context_snapshot The context snapshot to use as source data. + //! + //! \return A MinidumpContextWriter subclass, such as MinidumpContextWriterX86 + //! or MinidumpContextWriterAMD64, appropriate to the CPU type of \a + //! context_snapshot. The returned object is initialized using the source + //! data in \a context_snapshot. If \a context_snapshot is an unknown CPU + //! type’s context, logs a message and returns `nullptr`. + static std::unique_ptr CreateFromSnapshot( + const CPUContext* context_snapshot); + + protected: + MinidumpContextWriter() : MinidumpWritable() {} + + //! \brief Returns the size of the context structure that this object will + //! write. + //! + //! \note This method will only be called in #kStateFrozen or a subsequent + //! state. + virtual size_t ContextSize() const = 0; + + // MinidumpWritable: + size_t SizeOfObject() final; +}; + +//! \brief The writer for a MinidumpContextX86 structure in a minidump file. +class MinidumpContextX86Writer final : public MinidumpContextWriter { + public: + MinidumpContextX86Writer(); + + MinidumpContextX86Writer(const MinidumpContextX86Writer&) = delete; + MinidumpContextX86Writer& operator=(const MinidumpContextX86Writer&) = delete; + + ~MinidumpContextX86Writer() override; + + //! \brief Initializes the MinidumpContextX86 based on \a context_snapshot. + //! + //! \param[in] context_snapshot The context snapshot to use as source data. + //! + //! \note Valid in #kStateMutable. No mutation of context() may be done before + //! calling this method, and it is not normally necessary to alter + //! context() after calling this method. + void InitializeFromSnapshot(const CPUContextX86* context_snapshot); + + //! \brief Returns a pointer to the context structure that this object will + //! write. + //! + //! \attention This returns a non-`const` pointer to this object’s private + //! data so that a caller can populate the context structure directly. + //! This is done because providing setter interfaces to each field in the + //! context structure would be unwieldy and cumbersome. Care must be taken + //! to populate the context structure correctly. The context structure + //! must only be modified while this object is in the #kStateMutable + //! state. + MinidumpContextX86* context() { return &context_; } + + protected: + // MinidumpWritable: + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpContextWriter: + size_t ContextSize() const override; + + private: + MinidumpContextX86 context_; +}; + +//! \brief The writer for a MinidumpContextAMD64 structure in a minidump file. +class MinidumpContextAMD64Writer final : public MinidumpContextWriter { + public: + MinidumpContextAMD64Writer(); + + MinidumpContextAMD64Writer(const MinidumpContextAMD64Writer&) = delete; + MinidumpContextAMD64Writer& operator=(const MinidumpContextAMD64Writer&) = + delete; + + ~MinidumpContextAMD64Writer() override; + + // Ensure proper alignment of heap-allocated objects. This should not be + // necessary in C++17. + static void* operator new(size_t size); + static void operator delete(void* ptr); + + // Prevent unaligned heap-allocated arrays. Provisions could be made to allow + // these if necessary, but there is currently no use for them. + static void* operator new[](size_t size) = delete; + static void operator delete[](void* ptr) = delete; + + //! \brief Initializes the MinidumpContextAMD64 based on \a context_snapshot. + //! + //! \param[in] context_snapshot The context snapshot to use as source data. + //! + //! \note Valid in #kStateMutable. No mutation of context() may be done before + //! calling this method, and it is not normally necessary to alter + //! context() after calling this method. + void InitializeFromSnapshot(const CPUContextX86_64* context_snapshot); + + //! \brief Returns a pointer to the context structure that this object will + //! write. + //! + //! \attention This returns a non-`const` pointer to this object’s private + //! data so that a caller can populate the context structure directly. + //! This is done because providing setter interfaces to each field in the + //! context structure would be unwieldy and cumbersome. Care must be taken + //! to populate the context structure correctly. The context structure + //! must only be modified while this object is in the #kStateMutable + //! state. + MinidumpContextAMD64* context() { return &context_; } + + protected: + // MinidumpWritable: + size_t Alignment() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpContextWriter: + size_t ContextSize() const override; + + private: + MinidumpContextAMD64 context_; +}; + +//! \brief The writer for a MinidumpContextARM structure in a minidump file. +class MinidumpContextARMWriter final : public MinidumpContextWriter { + public: + MinidumpContextARMWriter(); + + MinidumpContextARMWriter(const MinidumpContextARMWriter&) = delete; + MinidumpContextARMWriter& operator=(const MinidumpContextARMWriter&) = delete; + + ~MinidumpContextARMWriter() override; + + //! \brief Initializes the MinidumpContextARM based on \a context_snapshot. + //! + //! \param[in] context_snapshot The context snapshot to use as source data. + //! + //! \note Valid in #kStateMutable. No mutation of context() may be done before + //! calling this method, and it is not normally necessary to alter + //! context() after calling this method. + void InitializeFromSnapshot(const CPUContextARM* context_snapshot); + + //! \brief Returns a pointer to the context structure that this object will + //! write. + //! + //! \attention This returns a non-`const` pointer to this object’s private + //! data so that a caller can populate the context structure directly. + //! This is done because providing setter interfaces to each field in the + //! context structure would be unwieldy and cumbersome. Care must be taken + //! to populate the context structure correctly. The context structure + //! must only be modified while this object is in the #kStateMutable + //! state. + MinidumpContextARM* context() { return &context_; } + + protected: + // MinidumpWritable: + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpContextWriter: + size_t ContextSize() const override; + + private: + MinidumpContextARM context_; +}; + +//! \brief The writer for a MinidumpContextARM64 structure in a minidump file. +class MinidumpContextARM64Writer final : public MinidumpContextWriter { + public: + MinidumpContextARM64Writer(); + + MinidumpContextARM64Writer(const MinidumpContextARM64Writer&) = delete; + MinidumpContextARM64Writer& operator=(const MinidumpContextARM64Writer&) = + delete; + + ~MinidumpContextARM64Writer() override; + + //! \brief Initializes the MinidumpContextARM64 based on \a context_snapshot. + //! + //! \param[in] context_snapshot The context snapshot to use as source data. + //! + //! \note Valid in #kStateMutable. No mutation of context() may be done before + //! calling this method, and it is not normally necessary to alter + //! context() after calling this method. + void InitializeFromSnapshot(const CPUContextARM64* context_snapshot); + + //! \brief Returns a pointer to the context structure that this object will + //! write. + //! + //! \attention This returns a non-`const` pointer to this object’s private + //! data so that a caller can populate the context structure directly. + //! This is done because providing setter interfaces to each field in the + //! context structure would be unwieldy and cumbersome. Care must be taken + //! to populate the context structure correctly. The context structure + //! must only be modified while this object is in the #kStateMutable + //! state. + MinidumpContextARM64* context() { return &context_; } + + protected: + // MinidumpWritable: + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpContextWriter: + size_t ContextSize() const override; + + private: + MinidumpContextARM64 context_; +}; + +//! \brief The writer for a MinidumpContextMIPS structure in a minidump file. +class MinidumpContextMIPSWriter final : public MinidumpContextWriter { + public: + MinidumpContextMIPSWriter(); + + MinidumpContextMIPSWriter(const MinidumpContextMIPSWriter&) = delete; + MinidumpContextMIPSWriter& operator=(const MinidumpContextMIPSWriter&) = + delete; + + ~MinidumpContextMIPSWriter() override; + + //! \brief Initializes the MinidumpContextMIPS based on \a context_snapshot. + //! + //! \param[in] context_snapshot The context snapshot to use as source data. + //! + //! \note Valid in #kStateMutable. No mutation of context() may be done before + //! calling this method, and it is not normally necessary to alter + //! context() after calling this method. + void InitializeFromSnapshot(const CPUContextMIPS* context_snapshot); + + //! \brief Returns a pointer to the context structure that this object will + //! write. + //! + //! \attention This returns a non-`const` pointer to this object’s private + //! data so that a caller can populate the context structure directly. + //! This is done because providing setter interfaces to each field in the + //! context structure would be unwieldy and cumbersome. Care must be taken + //! to populate the context structure correctly. The context structure + //! must only be modified while this object is in the #kStateMutable + //! state. + MinidumpContextMIPS* context() { return &context_; } + + protected: + // MinidumpWritable: + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpContextWriter: + size_t ContextSize() const override; + + private: + MinidumpContextMIPS context_; +}; + +//! \brief The writer for a MinidumpContextMIPS64 structure in a minidump file. +class MinidumpContextMIPS64Writer final : public MinidumpContextWriter { + public: + MinidumpContextMIPS64Writer(); + + MinidumpContextMIPS64Writer(const MinidumpContextMIPS64Writer&) = delete; + MinidumpContextMIPS64Writer& operator=(const MinidumpContextMIPS64Writer&) = + delete; + + ~MinidumpContextMIPS64Writer() override; + + //! \brief Initializes the MinidumpContextMIPS based on \a context_snapshot. + //! + //! \param[in] context_snapshot The context snapshot to use as source data. + //! + //! \note Valid in #kStateMutable. No mutation of context() may be done before + //! calling this method, and it is not normally necessary to alter + //! context() after calling this method. + void InitializeFromSnapshot(const CPUContextMIPS64* context_snapshot); + + //! \brief Returns a pointer to the context structure that this object will + //! write. + //! + //! \attention This returns a non-`const` pointer to this object’s private + //! data so that a caller can populate the context structure directly. + //! This is done because providing setter interfaces to each field in the + //! context structure would be unwieldy and cumbersome. Care must be taken + //! to populate the context structure correctly. The context structure + //! must only be modified while this object is in the #kStateMutable + //! state. + MinidumpContextMIPS64* context() { return &context_; } + + protected: + // MinidumpWritable: + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpContextWriter: + size_t ContextSize() const override; + + private: + MinidumpContextMIPS64 context_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_CONTEXT_WRITER_H_ diff --git a/shared/sentry/external/crashpad/minidump/minidump_context_writer_test.cc b/shared/sentry/external/crashpad/minidump/minidump_context_writer_test.cc new file mode 100644 index 000000000..3216a906b --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_context_writer_test.cc @@ -0,0 +1,218 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_context_writer.h" + +#include + +#include "gtest/gtest.h" +#include "minidump/minidump_context.h" +#include "minidump/test/minidump_context_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "snapshot/cpu_context.h" +#include "snapshot/test/test_cpu_context.h" +#include "util/file/string_file.h" + +namespace crashpad { +namespace test { +namespace { + +template +void EmptyContextTest(void (*expect_context)(uint32_t, const Context*, bool)) { + Writer context_writer; + StringFile string_file; + EXPECT_TRUE(context_writer.WriteEverything(&string_file)); + ASSERT_EQ(string_file.string().size(), sizeof(Context)); + + const Context* observed = + MinidumpWritableAtRVA(string_file.string(), 0); + ASSERT_TRUE(observed); + + expect_context(0, observed, false); +} + +TEST(MinidumpContextWriter, MinidumpContextX86Writer) { + StringFile string_file; + + { + // Make sure that a context writer that’s untouched writes a zeroed-out + // context. + SCOPED_TRACE("zero"); + + EmptyContextTest( + ExpectMinidumpContextX86); + } + + { + SCOPED_TRACE("nonzero"); + + string_file.Reset(); + constexpr uint32_t kSeed = 0x8086; + + MinidumpContextX86Writer context_writer; + InitializeMinidumpContextX86(context_writer.context(), kSeed); + + EXPECT_TRUE(context_writer.WriteEverything(&string_file)); + ASSERT_EQ(string_file.string().size(), sizeof(MinidumpContextX86)); + + const MinidumpContextX86* observed = + MinidumpWritableAtRVA(string_file.string(), 0); + ASSERT_TRUE(observed); + + ExpectMinidumpContextX86(kSeed, observed, false); + } +} + +TEST(MinidumpContextWriter, MinidumpContextAMD64Writer) { + { + // Make sure that a heap-allocated context writer has the proper alignment, + // because it may be nonstandard. + auto context_writer = std::make_unique(); + EXPECT_EQ(reinterpret_cast(context_writer.get()) & + (alignof(MinidumpContextAMD64Writer) - 1), + 0u); + } + + StringFile string_file; + + { + // Make sure that a context writer that’s untouched writes a zeroed-out + // context. + SCOPED_TRACE("zero"); + + EmptyContextTest( + ExpectMinidumpContextAMD64); + } + + { + SCOPED_TRACE("nonzero"); + + string_file.Reset(); + constexpr uint32_t kSeed = 0x808664; + + MinidumpContextAMD64Writer context_writer; + InitializeMinidumpContextAMD64(context_writer.context(), kSeed); + + EXPECT_TRUE(context_writer.WriteEverything(&string_file)); + ASSERT_EQ(string_file.string().size(), sizeof(MinidumpContextAMD64)); + + const MinidumpContextAMD64* observed = + MinidumpWritableAtRVA(string_file.string(), 0); + ASSERT_TRUE(observed); + + ExpectMinidumpContextAMD64(kSeed, observed, false); + } +} + +template +void FromSnapshotTest(const CPUContext& snapshot_context, + void (*expect_context)(uint32_t, const Context*, bool), + uint32_t seed) { + std::unique_ptr context_writer = + MinidumpContextWriter::CreateFromSnapshot(&snapshot_context); + ASSERT_TRUE(context_writer); + + StringFile string_file; + ASSERT_TRUE(context_writer->WriteEverything(&string_file)); + + const Context* observed = + MinidumpWritableAtRVA(string_file.string(), 0); + ASSERT_TRUE(observed); + + expect_context(seed, observed, true); +} + +TEST(MinidumpContextWriter, X86_FromSnapshot) { + constexpr uint32_t kSeed = 32; + CPUContextX86 context_x86; + CPUContext context; + context.x86 = &context_x86; + InitializeCPUContextX86(&context, kSeed); + FromSnapshotTest( + context, ExpectMinidumpContextX86, kSeed); +} + +TEST(MinidumpContextWriter, AMD64_FromSnapshot) { + constexpr uint32_t kSeed = 64; + CPUContextX86_64 context_x86_64; + CPUContext context; + context.x86_64 = &context_x86_64; + InitializeCPUContextX86_64(&context, kSeed); + FromSnapshotTest( + context, ExpectMinidumpContextAMD64, kSeed); +} + +TEST(MinidumpContextWriter, ARM_Zeros) { + EmptyContextTest( + ExpectMinidumpContextARM); +} + +TEST(MinidumpContextWRiter, ARM64_Zeros) { + EmptyContextTest( + ExpectMinidumpContextARM64); +} + +TEST(MinidumpContextWriter, ARM_FromSnapshot) { + constexpr uint32_t kSeed = 32; + CPUContextARM context_arm; + CPUContext context; + context.arm = &context_arm; + InitializeCPUContextARM(&context, kSeed); + FromSnapshotTest( + context, ExpectMinidumpContextARM, kSeed); +} + +TEST(MinidumpContextWriter, ARM64_FromSnapshot) { + constexpr uint32_t kSeed = 64; + CPUContextARM64 context_arm64; + CPUContext context; + context.arm64 = &context_arm64; + InitializeCPUContextARM64(&context, kSeed); + FromSnapshotTest( + context, ExpectMinidumpContextARM64, kSeed); +} + +TEST(MinidumpContextWriter, MIPS_Zeros) { + EmptyContextTest( + ExpectMinidumpContextMIPS); +} + +TEST(MinidumpContextWriter, MIPS64_Zeros) { + EmptyContextTest( + ExpectMinidumpContextMIPS64); +} + +TEST(MinidumpContextWriter, MIPS_FromSnapshot) { + constexpr uint32_t kSeed = 32; + CPUContextMIPS context_mips; + CPUContext context; + context.mipsel = &context_mips; + InitializeCPUContextMIPS(&context, kSeed); + FromSnapshotTest( + context, ExpectMinidumpContextMIPS, kSeed); +} + +TEST(MinidumpContextWriter, MIPS64_FromSnapshot) { + constexpr uint32_t kSeed = 64; + CPUContextMIPS64 context_mips; + CPUContext context; + context.mips64 = &context_mips; + InitializeCPUContextMIPS64(&context, kSeed); + FromSnapshotTest( + context, ExpectMinidumpContextMIPS64, kSeed); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_crashpad_info_writer.cc b/shared/sentry/external/crashpad/minidump/minidump_crashpad_info_writer.cc new file mode 100644 index 000000000..e84f29b8a --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_crashpad_info_writer.cc @@ -0,0 +1,149 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_crashpad_info_writer.h" + +#include + +#include "base/check_op.h" +#include "minidump/minidump_module_crashpad_info_writer.h" +#include "minidump/minidump_simple_string_dictionary_writer.h" +#include "snapshot/process_snapshot.h" +#include "util/file/file_writer.h" + +namespace crashpad { + +MinidumpCrashpadInfoWriter::MinidumpCrashpadInfoWriter() + : MinidumpStreamWriter(), + crashpad_info_(), + simple_annotations_(nullptr), + module_list_(nullptr) { + crashpad_info_.version = MinidumpCrashpadInfo::kVersion; +} + +MinidumpCrashpadInfoWriter::~MinidumpCrashpadInfoWriter() { +} + +void MinidumpCrashpadInfoWriter::InitializeFromSnapshot( + const ProcessSnapshot* process_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(!module_list_); + + UUID report_id; + process_snapshot->ReportID(&report_id); + SetReportID(report_id); + + UUID client_id; + process_snapshot->ClientID(&client_id); + SetClientID(client_id); + + auto simple_annotations = + std::make_unique(); + simple_annotations->InitializeFromMap( + process_snapshot->AnnotationsSimpleMap()); + if (simple_annotations->IsUseful()) { + SetSimpleAnnotations(std::move(simple_annotations)); + } + + auto modules = std::make_unique(); + modules->InitializeFromSnapshot(process_snapshot->Modules()); + + if (modules->IsUseful()) { + SetModuleList(std::move(modules)); + } +} + +void MinidumpCrashpadInfoWriter::SetReportID(const UUID& report_id) { + DCHECK_EQ(state(), kStateMutable); + + crashpad_info_.report_id = report_id; +} + +void MinidumpCrashpadInfoWriter::SetClientID(const UUID& client_id) { + DCHECK_EQ(state(), kStateMutable); + + crashpad_info_.client_id = client_id; +} + +void MinidumpCrashpadInfoWriter::SetSimpleAnnotations( + std::unique_ptr simple_annotations) { + DCHECK_EQ(state(), kStateMutable); + + simple_annotations_ = std::move(simple_annotations); +} + +void MinidumpCrashpadInfoWriter::SetModuleList( + std::unique_ptr module_list) { + DCHECK_EQ(state(), kStateMutable); + + module_list_ = std::move(module_list); +} + +bool MinidumpCrashpadInfoWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpStreamWriter::Freeze()) { + return false; + } + + if (simple_annotations_) { + simple_annotations_->RegisterLocationDescriptor( + &crashpad_info_.simple_annotations); + } + if (module_list_) { + module_list_->RegisterLocationDescriptor(&crashpad_info_.module_list); + } + + return true; +} + +size_t MinidumpCrashpadInfoWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return sizeof(crashpad_info_); +} + +std::vector +MinidumpCrashpadInfoWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + + std::vector children; + if (simple_annotations_) { + children.push_back(simple_annotations_.get()); + } + if (module_list_) { + children.push_back(module_list_.get()); + } + + return children; +} + +bool MinidumpCrashpadInfoWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + return file_writer->Write(&crashpad_info_, sizeof(crashpad_info_)); +} + +MinidumpStreamType MinidumpCrashpadInfoWriter::StreamType() const { + return kMinidumpStreamTypeCrashpadInfo; +} + +bool MinidumpCrashpadInfoWriter::IsUseful() const { + return crashpad_info_.report_id != UUID() || + crashpad_info_.client_id != UUID() || + simple_annotations_ || + module_list_; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_crashpad_info_writer.h b/shared/sentry/external/crashpad/minidump/minidump_crashpad_info_writer.h new file mode 100644 index 000000000..b7aa2d895 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_crashpad_info_writer.h @@ -0,0 +1,116 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_CRASHPAD_INFO_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_CRASHPAD_INFO_WRITER_H_ + +#include + +#include +#include + +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_stream_writer.h" +#include "util/misc/uuid.h" + +namespace crashpad { + +class MinidumpModuleCrashpadInfoListWriter; +class MinidumpSimpleStringDictionaryWriter; +class ProcessSnapshot; + +//! \brief The writer for a MinidumpCrashpadInfo stream in a minidump file. +class MinidumpCrashpadInfoWriter final : public internal::MinidumpStreamWriter { + public: + MinidumpCrashpadInfoWriter(); + + MinidumpCrashpadInfoWriter(const MinidumpCrashpadInfoWriter&) = delete; + MinidumpCrashpadInfoWriter& operator=(const MinidumpCrashpadInfoWriter&) = + delete; + + ~MinidumpCrashpadInfoWriter() override; + + //! \brief Initializes MinidumpCrashpadInfo based on \a process_snapshot. + //! + //! This method may add additional structures to the minidump file as children + //! of the MinidumpCrashpadInfo stream. To do so, it may obtain other + //! snapshot information from \a process_snapshot, such as a list of + //! ModuleSnapshot objects used to initialize + //! MinidumpCrashpadInfo::module_list. Only data that is considered useful + //! will be included. For module information, usefulness is determined by + //! MinidumpModuleCrashpadInfoListWriter::IsUseful(). + //! + //! \param[in] process_snapshot The process snapshot to use as source data. + //! + //! \note Valid in #kStateMutable. No mutator methods may be called before + //! this method, and it is not normally necessary to call any mutator + //! methods after this method. + void InitializeFromSnapshot(const ProcessSnapshot* process_snapshot); + + //! \brief Sets MinidumpCrashpadInfo::report_id. + void SetReportID(const UUID& report_id); + + //! \brief Sets MinidumpCrashpadInfo::client_id. + void SetClientID(const UUID& client_id); + + //! \brief Arranges for MinidumpCrashpadInfo::simple_annotations to point to + //! the MinidumpSimpleStringDictionaryWriter object to be written by \a + //! simple_annotations. + //! + //! This object takes ownership of \a simple_annotations and becomes its + //! parent in the overall tree of internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void SetSimpleAnnotations( + std::unique_ptr simple_annotations); + + //! \brief Arranges for MinidumpCrashpadInfo::module_list to point to the + //! MinidumpModuleCrashpadInfoList object to be written by \a + //! module_list. + //! + //! This object takes ownership of \a module_list and becomes its parent in + //! the overall tree of internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void SetModuleList( + std::unique_ptr module_list); + + //! \brief Determines whether the object is useful. + //! + //! A useful object is one that carries data that makes a meaningful + //! contribution to a minidump file. An object carrying children would be + //! considered useful. + //! + //! \return `true` if the object is useful, `false` otherwise. + bool IsUseful() const; + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpStreamWriter: + MinidumpStreamType StreamType() const override; + + private: + MinidumpCrashpadInfo crashpad_info_; + std::unique_ptr simple_annotations_; + std::unique_ptr module_list_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_CRASHPAD_INFO_WRITER_H_ diff --git a/shared/sentry/external/crashpad/minidump/minidump_crashpad_info_writer_test.cc b/shared/sentry/external/crashpad/minidump/minidump_crashpad_info_writer_test.cc new file mode 100644 index 000000000..19d460685 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_crashpad_info_writer_test.cc @@ -0,0 +1,317 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_crashpad_info_writer.h" + +#include +#include + +#include +#include +#include + +#include "gtest/gtest.h" +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_file_writer.h" +#include "minidump/minidump_module_crashpad_info_writer.h" +#include "minidump/minidump_simple_string_dictionary_writer.h" +#include "minidump/test/minidump_file_writer_test_util.h" +#include "minidump/test/minidump_string_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "snapshot/test/test_module_snapshot.h" +#include "snapshot/test/test_process_snapshot.h" +#include "util/file/string_file.h" + +namespace crashpad { +namespace test { +namespace { + +void GetCrashpadInfoStream( + const std::string& file_contents, + const MinidumpCrashpadInfo** crashpad_info, + const MinidumpSimpleStringDictionary** simple_annotations, + const MinidumpModuleCrashpadInfoList** module_list) { + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(file_contents, &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0)); + ASSERT_TRUE(directory); + + ASSERT_EQ(directory[0].StreamType, kMinidumpStreamTypeCrashpadInfo); + + *crashpad_info = MinidumpWritableAtLocationDescriptor( + file_contents, directory[0].Location); + ASSERT_TRUE(*crashpad_info); + + *simple_annotations = + MinidumpWritableAtLocationDescriptor( + file_contents, (*crashpad_info)->simple_annotations); + + *module_list = + MinidumpWritableAtLocationDescriptor( + file_contents, (*crashpad_info)->module_list); +} + +TEST(MinidumpCrashpadInfoWriter, Empty) { + MinidumpFileWriter minidump_file_writer; + auto crashpad_info_writer = std::make_unique(); + EXPECT_FALSE(crashpad_info_writer->IsUseful()); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(crashpad_info_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MinidumpCrashpadInfo* crashpad_info = nullptr; + const MinidumpSimpleStringDictionary* simple_annotations = nullptr; + const MinidumpModuleCrashpadInfoList* module_list = nullptr; + + ASSERT_NO_FATAL_FAILURE(GetCrashpadInfoStream( + string_file.string(), &crashpad_info, &simple_annotations, &module_list)); + + EXPECT_EQ(crashpad_info->version, MinidumpCrashpadInfo::kVersion); + EXPECT_EQ(crashpad_info->report_id, UUID()); + EXPECT_EQ(crashpad_info->client_id, UUID()); + EXPECT_FALSE(simple_annotations); + EXPECT_FALSE(module_list); +} + +TEST(MinidumpCrashpadInfoWriter, ReportAndClientID) { + MinidumpFileWriter minidump_file_writer; + auto crashpad_info_writer = std::make_unique(); + + UUID report_id; + ASSERT_TRUE( + report_id.InitializeFromString("01234567-89ab-cdef-0123-456789abcdef")); + crashpad_info_writer->SetReportID(report_id); + + UUID client_id; + ASSERT_TRUE( + client_id.InitializeFromString("00112233-4455-6677-8899-aabbccddeeff")); + crashpad_info_writer->SetClientID(client_id); + + EXPECT_TRUE(crashpad_info_writer->IsUseful()); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(crashpad_info_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MinidumpCrashpadInfo* crashpad_info = nullptr; + const MinidumpSimpleStringDictionary* simple_annotations = nullptr; + const MinidumpModuleCrashpadInfoList* module_list = nullptr; + + ASSERT_NO_FATAL_FAILURE(GetCrashpadInfoStream( + string_file.string(), &crashpad_info, &simple_annotations, &module_list)); + + EXPECT_EQ(crashpad_info->version, MinidumpCrashpadInfo::kVersion); + EXPECT_EQ(crashpad_info->report_id, report_id); + EXPECT_EQ(crashpad_info->client_id, client_id); + EXPECT_FALSE(simple_annotations); + EXPECT_FALSE(module_list); +} + +TEST(MinidumpCrashpadInfoWriter, SimpleAnnotations) { + MinidumpFileWriter minidump_file_writer; + auto crashpad_info_writer = std::make_unique(); + + static constexpr char kKey[] = + "a thing that provides a means of gaining access to or understanding " + "something"; + static constexpr char kValue[] = + "the numerical amount denoted by an algebraic term; a magnitude, " + "quantity, or number"; + auto simple_string_dictionary_writer = + std::make_unique(); + auto simple_string_dictionary_entry_writer = + std::make_unique(); + simple_string_dictionary_entry_writer->SetKeyValue(kKey, kValue); + simple_string_dictionary_writer->AddEntry( + std::move(simple_string_dictionary_entry_writer)); + crashpad_info_writer->SetSimpleAnnotations( + std::move(simple_string_dictionary_writer)); + + EXPECT_TRUE(crashpad_info_writer->IsUseful()); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(crashpad_info_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MinidumpCrashpadInfo* crashpad_info = nullptr; + const MinidumpSimpleStringDictionary* simple_annotations = nullptr; + const MinidumpModuleCrashpadInfoList* module_list = nullptr; + + ASSERT_NO_FATAL_FAILURE(GetCrashpadInfoStream( + string_file.string(), &crashpad_info, &simple_annotations, &module_list)); + + EXPECT_EQ(crashpad_info->version, MinidumpCrashpadInfo::kVersion); + EXPECT_FALSE(module_list); + + ASSERT_TRUE(simple_annotations); + ASSERT_EQ(simple_annotations->count, 1u); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + simple_annotations->entries[0].key), + kKey); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations->entries[0].value), + kValue); +} + +TEST(MinidumpCrashpadInfoWriter, CrashpadModuleList) { + constexpr uint32_t kMinidumpModuleListIndex = 3; + + MinidumpFileWriter minidump_file_writer; + auto crashpad_info_writer = std::make_unique(); + + auto module_list_writer = + std::make_unique(); + auto module_writer = std::make_unique(); + module_list_writer->AddModule(std::move(module_writer), + kMinidumpModuleListIndex); + crashpad_info_writer->SetModuleList(std::move(module_list_writer)); + + EXPECT_TRUE(crashpad_info_writer->IsUseful()); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(crashpad_info_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MinidumpCrashpadInfo* crashpad_info = nullptr; + const MinidumpSimpleStringDictionary* simple_annotations = nullptr; + const MinidumpModuleCrashpadInfoList* module_list = nullptr; + + ASSERT_NO_FATAL_FAILURE(GetCrashpadInfoStream( + string_file.string(), &crashpad_info, &simple_annotations, &module_list)); + + EXPECT_EQ(crashpad_info->version, MinidumpCrashpadInfo::kVersion); + EXPECT_FALSE(simple_annotations); + + ASSERT_TRUE(module_list); + ASSERT_EQ(module_list->count, 1u); + + EXPECT_EQ(module_list->modules[0].minidump_module_list_index, + kMinidumpModuleListIndex); + const MinidumpModuleCrashpadInfo* module = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_list->modules[0].location); + ASSERT_TRUE(module); + + EXPECT_EQ(module->version, MinidumpModuleCrashpadInfo::kVersion); + EXPECT_EQ(module->list_annotations.DataSize, 0u); + EXPECT_EQ(module->list_annotations.Rva, 0u); + EXPECT_EQ(module->simple_annotations.DataSize, 0u); + EXPECT_EQ(module->simple_annotations.Rva, 0u); +} + +TEST(MinidumpCrashpadInfoWriter, InitializeFromSnapshot) { + UUID report_id; + ASSERT_TRUE( + report_id.InitializeFromString("fedcba98-7654-3210-fedc-ba9876543210")); + + UUID client_id; + ASSERT_TRUE( + client_id.InitializeFromString("fedcba98-7654-3210-0123-456789abcdef")); + + static constexpr char kKey[] = "version"; + static constexpr char kValue[] = "40.0.2214.111"; + static constexpr char kEntry[] = "This is a simple annotation in a list."; + + // Test with a useless module, one that doesn’t carry anything that would + // require MinidumpCrashpadInfo or any child object. + auto process_snapshot = std::make_unique(); + + auto module_snapshot = std::make_unique(); + process_snapshot->AddModule(std::move(module_snapshot)); + + auto info_writer = std::make_unique(); + info_writer->InitializeFromSnapshot(process_snapshot.get()); + EXPECT_FALSE(info_writer->IsUseful()); + + // Try again with a useful module. + process_snapshot.reset(new TestProcessSnapshot()); + + process_snapshot->SetReportID(report_id); + process_snapshot->SetClientID(client_id); + + std::map annotations_simple_map; + annotations_simple_map[kKey] = kValue; + process_snapshot->SetAnnotationsSimpleMap(annotations_simple_map); + + module_snapshot.reset(new TestModuleSnapshot()); + std::vector annotations_list(1, std::string(kEntry)); + module_snapshot->SetAnnotationsVector(annotations_list); + process_snapshot->AddModule(std::move(module_snapshot)); + + info_writer.reset(new MinidumpCrashpadInfoWriter()); + info_writer->InitializeFromSnapshot(process_snapshot.get()); + EXPECT_TRUE(info_writer->IsUseful()); + + MinidumpFileWriter minidump_file_writer; + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(info_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MinidumpCrashpadInfo* info = nullptr; + const MinidumpSimpleStringDictionary* simple_annotations; + const MinidumpModuleCrashpadInfoList* module_list; + ASSERT_NO_FATAL_FAILURE(GetCrashpadInfoStream( + string_file.string(), &info, &simple_annotations, &module_list)); + + EXPECT_EQ(info->version, MinidumpCrashpadInfo::kVersion); + + EXPECT_EQ(info->report_id, report_id); + EXPECT_EQ(info->client_id, client_id); + + ASSERT_TRUE(simple_annotations); + ASSERT_EQ(simple_annotations->count, 1u); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + simple_annotations->entries[0].key), + kKey); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations->entries[0].value), + kValue); + + ASSERT_TRUE(module_list); + ASSERT_EQ(module_list->count, 1u); + + EXPECT_EQ(module_list->modules[0].minidump_module_list_index, 0u); + const MinidumpModuleCrashpadInfo* module = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_list->modules[0].location); + ASSERT_TRUE(module); + + EXPECT_EQ(module->version, MinidumpModuleCrashpadInfo::kVersion); + + const MinidumpRVAList* list_annotations = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module->list_annotations); + ASSERT_TRUE(list_annotations); + + ASSERT_EQ(list_annotations->count, 1u); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + list_annotations->children[0]), + kEntry); + + const MinidumpSimpleStringDictionary* module_simple_annotations = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module->simple_annotations); + EXPECT_FALSE(module_simple_annotations); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_exception_writer.cc b/shared/sentry/external/crashpad/minidump/minidump_exception_writer.cc new file mode 100644 index 000000000..e1a02da2e --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_exception_writer.cc @@ -0,0 +1,122 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_exception_writer.h" + +#include + +#include "base/check_op.h" +#include "base/numerics/safe_conversions.h" +#include "minidump/minidump_context_writer.h" +#include "snapshot/exception_snapshot.h" +#include "util/file/file_writer.h" +#include "util/misc/arraysize.h" + +namespace crashpad { + +MinidumpExceptionWriter::MinidumpExceptionWriter() + : MinidumpStreamWriter(), exception_(), context_(nullptr) { +} + +MinidumpExceptionWriter::~MinidumpExceptionWriter() { +} + +void MinidumpExceptionWriter::InitializeFromSnapshot( + const ExceptionSnapshot* exception_snapshot, + const MinidumpThreadIDMap& thread_id_map) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(!context_); + + auto thread_id_it = thread_id_map.find(exception_snapshot->ThreadID()); + DCHECK(thread_id_it != thread_id_map.end()); + SetThreadID(thread_id_it->second); + + SetExceptionCode(exception_snapshot->Exception()); + SetExceptionFlags(exception_snapshot->ExceptionInfo()); + SetExceptionAddress(exception_snapshot->ExceptionAddress()); + SetExceptionInformation(exception_snapshot->Codes()); + + std::unique_ptr context = + MinidumpContextWriter::CreateFromSnapshot(exception_snapshot->Context()); + SetContext(std::move(context)); +} + +void MinidumpExceptionWriter::SetContext( + std::unique_ptr context) { + DCHECK_EQ(state(), kStateMutable); + + context_ = std::move(context); +} + +void MinidumpExceptionWriter::SetExceptionInformation( + const std::vector& exception_information) { + DCHECK_EQ(state(), kStateMutable); + + const size_t parameters = exception_information.size(); + constexpr size_t kMaxParameters = + ArraySize(exception_.ExceptionRecord.ExceptionInformation); + CHECK_LE(parameters, kMaxParameters); + + exception_.ExceptionRecord.NumberParameters = + base::checked_cast(parameters); + size_t parameter = 0; + for (; parameter < parameters; ++parameter) { + exception_.ExceptionRecord.ExceptionInformation[parameter] = + exception_information[parameter]; + } + for (; parameter < kMaxParameters; ++parameter) { + exception_.ExceptionRecord.ExceptionInformation[parameter] = 0; + } +} + +bool MinidumpExceptionWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + CHECK(context_); + + if (!MinidumpStreamWriter::Freeze()) { + return false; + } + + context_->RegisterLocationDescriptor(&exception_.ThreadContext); + + return true; +} + +size_t MinidumpExceptionWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return sizeof(exception_); +} + +std::vector MinidumpExceptionWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + DCHECK(context_); + + std::vector children; + children.push_back(context_.get()); + + return children; +} + +bool MinidumpExceptionWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + return file_writer->Write(&exception_, sizeof(exception_)); +} + +MinidumpStreamType MinidumpExceptionWriter::StreamType() const { + return kMinidumpStreamTypeException; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_exception_writer.h b/shared/sentry/external/crashpad/minidump/minidump_exception_writer.h new file mode 100644 index 000000000..82299a5a7 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_exception_writer.h @@ -0,0 +1,127 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_EXCEPTION_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_EXCEPTION_WRITER_H_ + +#include +#include +#include +#include + +#include +#include + +#include "minidump/minidump_stream_writer.h" +#include "minidump/minidump_thread_id_map.h" + +namespace crashpad { + +class ExceptionSnapshot; +class MinidumpContextWriter; +class MinidumpMemoryListWriter; + +//! \brief The writer for a MINIDUMP_EXCEPTION_STREAM stream in a minidump file. +class MinidumpExceptionWriter final : public internal::MinidumpStreamWriter { + public: + MinidumpExceptionWriter(); + + MinidumpExceptionWriter(const MinidumpExceptionWriter&) = delete; + MinidumpExceptionWriter& operator=(const MinidumpExceptionWriter&) = delete; + + ~MinidumpExceptionWriter() override; + + //! \brief Initializes the MINIDUMP_EXCEPTION_STREAM based on \a + //! exception_snapshot. + //! + //! \param[in] exception_snapshot The exception snapshot to use as source + //! data. + //! \param[in] thread_id_map A MinidumpThreadIDMap to be consulted to + //! determine the 32-bit minidump thread ID to use for the thread + //! identified by \a exception_snapshot. + //! + //! \note Valid in #kStateMutable. No mutator methods may be called before + //! this method, and it is not normally necessary to call any mutator + //! methods after this method. + void InitializeFromSnapshot(const ExceptionSnapshot* exception_snapshot, + const MinidumpThreadIDMap& thread_id_map); + + //! \brief Arranges for MINIDUMP_EXCEPTION_STREAM::ThreadContext to point to + //! the CPU context to be written by \a context. + //! + //! A context is required in all MINIDUMP_EXCEPTION_STREAM objects. + //! + //! This object takes ownership of \a context and becomes its parent in the + //! overall tree of internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void SetContext(std::unique_ptr context); + + //! \brief Sets MINIDUMP_EXCEPTION_STREAM::ThreadId. + void SetThreadID(uint32_t thread_id) { exception_.ThreadId = thread_id; } + + //! \brief Sets MINIDUMP_EXCEPTION::ExceptionCode. + void SetExceptionCode(uint32_t exception_code) { + exception_.ExceptionRecord.ExceptionCode = exception_code; + } + + //! \brief Sets MINIDUMP_EXCEPTION::ExceptionFlags. + void SetExceptionFlags(uint32_t exception_flags) { + exception_.ExceptionRecord.ExceptionFlags = exception_flags; + } + + //! \brief Sets MINIDUMP_EXCEPTION::ExceptionRecord. + void SetExceptionRecord(uint64_t exception_record) { + exception_.ExceptionRecord.ExceptionRecord = exception_record; + } + + //! \brief Sets MINIDUMP_EXCEPTION::ExceptionAddress. + void SetExceptionAddress(uint64_t exception_address) { + exception_.ExceptionRecord.ExceptionAddress = exception_address; + } + + //! \brief Sets MINIDUMP_EXCEPTION::ExceptionInformation and + //! MINIDUMP_EXCEPTION::NumberParameters. + //! + //! MINIDUMP_EXCEPTION::NumberParameters is set to the number of elements in + //! \a exception_information. The elements of + //! MINIDUMP_EXCEPTION::ExceptionInformation are set to the elements of \a + //! exception_information. Unused elements in + //! MINIDUMP_EXCEPTION::ExceptionInformation are set to `0`. + //! + //! \a exception_information must have no more than + //! #EXCEPTION_MAXIMUM_PARAMETERS elements. + //! + //! \note Valid in #kStateMutable. + void SetExceptionInformation( + const std::vector& exception_information); + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpStreamWriter: + MinidumpStreamType StreamType() const override; + + private: + MINIDUMP_EXCEPTION_STREAM exception_; + std::unique_ptr context_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_EXCEPTION_WRITER_H_ diff --git a/shared/sentry/external/crashpad/minidump/minidump_exception_writer_test.cc b/shared/sentry/external/crashpad/minidump/minidump_exception_writer_test.cc new file mode 100644 index 000000000..e09232fe7 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_exception_writer_test.cc @@ -0,0 +1,281 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_exception_writer.h" + +#include +#include +#include + +#include "gtest/gtest.h" +#include "minidump/minidump_context.h" +#include "minidump/minidump_context_writer.h" +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_file_writer.h" +#include "minidump/test/minidump_context_test_util.h" +#include "minidump/test/minidump_file_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "snapshot/test/test_cpu_context.h" +#include "snapshot/test/test_exception_snapshot.h" +#include "test/gtest_death.h" +#include "util/file/string_file.h" + +namespace crashpad { +namespace test { +namespace { + +// This returns the MINIDUMP_EXCEPTION_STREAM stream in |exception_stream|. +void GetExceptionStream(const std::string& file_contents, + const MINIDUMP_EXCEPTION_STREAM** exception_stream) { + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kExceptionStreamOffset = + kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + constexpr size_t kContextOffset = + kExceptionStreamOffset + sizeof(MINIDUMP_EXCEPTION_STREAM); + constexpr size_t kFileSize = kContextOffset + sizeof(MinidumpContextX86); + ASSERT_EQ(kFileSize, file_contents.size()); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(file_contents, &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0)); + + ASSERT_EQ(directory[0].StreamType, kMinidumpStreamTypeException); + EXPECT_EQ(directory[0].Location.Rva, kExceptionStreamOffset); + + *exception_stream = + MinidumpWritableAtLocationDescriptor( + file_contents, directory[0].Location); + ASSERT_TRUE(exception_stream); +} + +// The MINIDUMP_EXCEPTION_STREAMs |expected| and |observed| are compared against +// each other using Google Test assertions. The context will be recovered from +// |file_contents| and stored in |context|. +void ExpectExceptionStream(const MINIDUMP_EXCEPTION_STREAM* expected, + const MINIDUMP_EXCEPTION_STREAM* observed, + const std::string& file_contents, + const MinidumpContextX86** context) { + EXPECT_EQ(observed->ThreadId, expected->ThreadId); + EXPECT_EQ(observed->__alignment, 0u); + + // Copy the ExceptionRecords so that their uint64_t members can be accessed + // with the proper alignment. + const MINIDUMP_EXCEPTION observed_exception = observed->ExceptionRecord; + const MINIDUMP_EXCEPTION expected_exception = expected->ExceptionRecord; + + EXPECT_EQ(observed_exception.ExceptionCode, expected_exception.ExceptionCode); + EXPECT_EQ(observed_exception.ExceptionFlags, + expected_exception.ExceptionFlags); + EXPECT_EQ(observed_exception.ExceptionRecord, + expected_exception.ExceptionRecord); + EXPECT_EQ(observed_exception.ExceptionAddress, + expected_exception.ExceptionAddress); + EXPECT_EQ(observed_exception.NumberParameters, + expected_exception.NumberParameters); + EXPECT_EQ(observed->ExceptionRecord.__unusedAlignment, 0u); + for (size_t index = 0; + index < std::size(observed_exception.ExceptionInformation); + ++index) { + EXPECT_EQ(observed_exception.ExceptionInformation[index], + expected_exception.ExceptionInformation[index]); + } + *context = MinidumpWritableAtLocationDescriptor( + file_contents, observed->ThreadContext); + ASSERT_TRUE(context); +} + +TEST(MinidumpExceptionWriter, Minimal) { + MinidumpFileWriter minidump_file_writer; + auto exception_writer = std::make_unique(); + + constexpr uint32_t kSeed = 100; + + auto context_x86_writer = std::make_unique(); + InitializeMinidumpContextX86(context_x86_writer->context(), kSeed); + exception_writer->SetContext(std::move(context_x86_writer)); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(exception_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_EXCEPTION_STREAM* observed_exception_stream = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetExceptionStream(string_file.string(), &observed_exception_stream)); + + MINIDUMP_EXCEPTION_STREAM expected_exception_stream = {}; + expected_exception_stream.ThreadContext.DataSize = sizeof(MinidumpContextX86); + + const MinidumpContextX86* observed_context = nullptr; + ASSERT_NO_FATAL_FAILURE(ExpectExceptionStream(&expected_exception_stream, + observed_exception_stream, + string_file.string(), + &observed_context)); + + ASSERT_NO_FATAL_FAILURE( + ExpectMinidumpContextX86(kSeed, observed_context, false)); +} + +TEST(MinidumpExceptionWriter, Standard) { + MinidumpFileWriter minidump_file_writer; + auto exception_writer = std::make_unique(); + + constexpr uint32_t kSeed = 200; + constexpr uint32_t kThreadID = 1; + constexpr uint32_t kExceptionCode = 2; + constexpr uint32_t kExceptionFlags = 3; + constexpr uint32_t kExceptionRecord = 4; + constexpr uint32_t kExceptionAddress = 5; + constexpr uint64_t kExceptionInformation0 = 6; + constexpr uint64_t kExceptionInformation1 = 7; + constexpr uint64_t kExceptionInformation2 = 7; + + auto context_x86_writer = std::make_unique(); + InitializeMinidumpContextX86(context_x86_writer->context(), kSeed); + exception_writer->SetContext(std::move(context_x86_writer)); + + exception_writer->SetThreadID(kThreadID); + exception_writer->SetExceptionCode(kExceptionCode); + exception_writer->SetExceptionFlags(kExceptionFlags); + exception_writer->SetExceptionRecord(kExceptionRecord); + exception_writer->SetExceptionAddress(kExceptionAddress); + + // Set a lot of exception information at first, and then replace it with less. + // This tests that the exception that is written does not contain the + // “garbage†from the initial SetExceptionInformation() call. + std::vector exception_information(EXCEPTION_MAXIMUM_PARAMETERS, + 0x5a5a5a5a5a5a5a5a); + exception_writer->SetExceptionInformation(exception_information); + + exception_information.clear(); + exception_information.push_back(kExceptionInformation0); + exception_information.push_back(kExceptionInformation1); + exception_information.push_back(kExceptionInformation2); + exception_writer->SetExceptionInformation(exception_information); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(exception_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_EXCEPTION_STREAM* observed_exception_stream = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetExceptionStream(string_file.string(), &observed_exception_stream)); + + MINIDUMP_EXCEPTION_STREAM expected_exception_stream = {}; + expected_exception_stream.ThreadId = kThreadID; + expected_exception_stream.ExceptionRecord.ExceptionCode = kExceptionCode; + expected_exception_stream.ExceptionRecord.ExceptionFlags = kExceptionFlags; + expected_exception_stream.ExceptionRecord.ExceptionRecord = kExceptionRecord; + expected_exception_stream.ExceptionRecord.ExceptionAddress = + kExceptionAddress; + expected_exception_stream.ExceptionRecord.NumberParameters = + static_cast(exception_information.size()); + for (size_t index = 0; index < exception_information.size(); ++index) { + expected_exception_stream.ExceptionRecord.ExceptionInformation[index] = + exception_information[index]; + } + expected_exception_stream.ThreadContext.DataSize = sizeof(MinidumpContextX86); + + const MinidumpContextX86* observed_context = nullptr; + ASSERT_NO_FATAL_FAILURE(ExpectExceptionStream(&expected_exception_stream, + observed_exception_stream, + string_file.string(), + &observed_context)); + + ASSERT_NO_FATAL_FAILURE( + ExpectMinidumpContextX86(kSeed, observed_context, false)); +} + +TEST(MinidumpExceptionWriter, InitializeFromSnapshot) { + std::vector exception_codes; + exception_codes.push_back(0x1000000000000000); + exception_codes.push_back(0x5555555555555555); + + MINIDUMP_EXCEPTION_STREAM expect_exception = {}; + + expect_exception.ThreadId = 123; + expect_exception.ExceptionRecord.ExceptionCode = 100; + expect_exception.ExceptionRecord.ExceptionFlags = 1; + expect_exception.ExceptionRecord.ExceptionAddress = 0xfedcba9876543210; + expect_exception.ExceptionRecord.NumberParameters = + static_cast(exception_codes.size()); + for (size_t index = 0; index < exception_codes.size(); ++index) { + expect_exception.ExceptionRecord.ExceptionInformation[index] = + exception_codes[index]; + } + constexpr uint64_t kThreadID = 0xaaaaaaaaaaaaaaaa; + constexpr uint32_t kSeed = 65; + + TestExceptionSnapshot exception_snapshot; + exception_snapshot.SetThreadID(kThreadID); + exception_snapshot.SetException( + expect_exception.ExceptionRecord.ExceptionCode); + exception_snapshot.SetExceptionInfo( + expect_exception.ExceptionRecord.ExceptionFlags); + exception_snapshot.SetExceptionAddress( + expect_exception.ExceptionRecord.ExceptionAddress); + exception_snapshot.SetCodes(exception_codes); + + InitializeCPUContextX86(exception_snapshot.MutableContext(), kSeed); + + MinidumpThreadIDMap thread_id_map; + thread_id_map[kThreadID] = expect_exception.ThreadId; + + auto exception_writer = std::make_unique(); + exception_writer->InitializeFromSnapshot(&exception_snapshot, thread_id_map); + + MinidumpFileWriter minidump_file_writer; + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(exception_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_EXCEPTION_STREAM* exception = nullptr; + ASSERT_NO_FATAL_FAILURE(GetExceptionStream(string_file.string(), &exception)); + + const MinidumpContextX86* observed_context = nullptr; + ASSERT_NO_FATAL_FAILURE(ExpectExceptionStream(&expect_exception, + exception, + string_file.string(), + &observed_context)); + + ASSERT_NO_FATAL_FAILURE( + ExpectMinidumpContextX86(kSeed, observed_context, true)); +} + +TEST(MinidumpExceptionWriterDeathTest, NoContext) { + MinidumpFileWriter minidump_file_writer; + auto exception_writer = std::make_unique(); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(exception_writer))); + + StringFile string_file; + ASSERT_DEATH_CHECK(minidump_file_writer.WriteEverything(&string_file), + "context_"); +} + +TEST(MinidumpExceptionWriterDeathTest, TooMuchInformation) { + MinidumpExceptionWriter exception_writer; + std::vector exception_information(EXCEPTION_MAXIMUM_PARAMETERS + 1, + 0x5a5a5a5a5a5a5a5a); + ASSERT_DEATH_CHECK( + exception_writer.SetExceptionInformation(exception_information), + "kMaxParameters"); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_extensions.cc b/shared/sentry/external/crashpad/minidump/minidump_extensions.cc new file mode 100644 index 000000000..bfb0e60e9 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_extensions.cc @@ -0,0 +1,22 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_extensions.h" + +namespace crashpad { + +constexpr uint32_t MinidumpModuleCrashpadInfo::kVersion; +constexpr uint32_t MinidumpCrashpadInfo::kVersion; + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_extensions.h b/shared/sentry/external/crashpad/minidump/minidump_extensions.h new file mode 100644 index 000000000..db0c33ec5 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_extensions.h @@ -0,0 +1,515 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_EXTENSIONS_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_EXTENSIONS_H_ + +#include +#include +#include +#include + +#include "base/compiler_specific.h" +#include "build/build_config.h" +#include "util/misc/pdb_structures.h" +#include "util/misc/uuid.h" + +#if defined(COMPILER_MSVC) +// C4200 is "nonstandard extension used : zero-sized array in struct/union". +// We would like to globally disable this warning, but unfortunately, the +// compiler is buggy and only supports disabling it with a pragma, so we can't +// disable it with other silly warnings in the build files. See: +// https://connect.microsoft.com/VisualStudio/feedback/details/1114440 +#pragma warning(push) +#pragma warning(disable: 4200) + +#define PACKED +#pragma pack(push, 1) +#else +#define PACKED __attribute__((packed)) +#endif // COMPILER_MSVC + +namespace crashpad { + +//! \brief Minidump stream type values for MINIDUMP_DIRECTORY::StreamType. Each +//! stream structure has a corresponding stream type value to identify it. +//! +//! \sa MINIDUMP_STREAM_TYPE +enum MinidumpStreamType : uint32_t { + //! \brief The stream type for MINIDUMP_THREAD_LIST. + //! + //! \sa ThreadListStream + kMinidumpStreamTypeThreadList = ThreadListStream, + + //! \brief The stream type for MINIDUMP_MODULE_LIST. + //! + //! \sa ModuleListStream + kMinidumpStreamTypeModuleList = ModuleListStream, + + //! \brief The stream type for MINIDUMP_MEMORY_LIST. + //! + //! \sa MemoryListStream + kMinidumpStreamTypeMemoryList = MemoryListStream, + + //! \brief The stream type for MINIDUMP_EXCEPTION_STREAM. + //! + //! \sa ExceptionStream + kMinidumpStreamTypeException = ExceptionStream, + + //! \brief The stream type for MINIDUMP_SYSTEM_INFO. + //! + //! \sa SystemInfoStream + kMinidumpStreamTypeSystemInfo = SystemInfoStream, + + //! \brief The stream type for MINIDUMP_HANDLE_DATA_STREAM. + //! + //! \sa HandleDataStream + kMinidumpStreamTypeHandleData = HandleDataStream, + + //! \brief The stream type for MINIDUMP_UNLOADED_MODULE_LIST. + //! + //! \sa UnloadedModuleListStream + kMinidumpStreamTypeUnloadedModuleList = UnloadedModuleListStream, + + //! \brief The stream type for MINIDUMP_MISC_INFO, MINIDUMP_MISC_INFO_2, + //! MINIDUMP_MISC_INFO_3, and MINIDUMP_MISC_INFO_4. + //! + //! \sa MiscInfoStream + kMinidumpStreamTypeMiscInfo = MiscInfoStream, + + //! \brief The stream type for MINIDUMP_MEMORY_INFO_LIST. + //! + //! \sa MemoryInfoListStream + kMinidumpStreamTypeMemoryInfoList = MemoryInfoListStream, + + //! \brief The last reserved minidump stream. + //! + //! \sa MemoryInfoListStream + kMinidumpStreamTypeLastReservedStream = LastReservedStream, + + // 0x4350 = "CP" + + //! \brief The stream type for MinidumpCrashpadInfo. + kMinidumpStreamTypeCrashpadInfo = 0x43500001, + + //! \brief The last reserved crashpad stream. + kMinidumpStreamTypeCrashpadLastReservedStream = 0x4350ffff, + + // 0x5379 = "Sy" + + //! \brief The stream type for client-side stack traces. + kMinidumpStreamTypeSentryStackTraces = 0x53790001, + + //! \brief The last reserved Sentry stream. + kMinidumpStreamTypeSentryLastReservedStream = 0x5379ffff, +}; + +//! \brief A variable-length UTF-8-encoded string carried within a minidump +//! file. +//! +//! \sa MINIDUMP_STRING +struct ALIGNAS(4) PACKED MinidumpUTF8String { + // The field names do not conform to typical style, they match the names used + // in MINIDUMP_STRING. This makes it easier to operate on MINIDUMP_STRING (for + // UTF-16 strings) and MinidumpUTF8String using templates. + + //! \brief The length of the #Buffer field in bytes, not including the `NUL` + //! terminator. + //! + //! \note This field is interpreted as a byte count, not a count of Unicode + //! code points. + uint32_t Length; + + //! \brief The string, encoded in UTF-8, and terminated with a `NUL` byte. + uint8_t Buffer[0]; +}; + +//! \brief A variable-length array of bytes carried within a minidump file. +//! The data have no intrinsic type and should be interpreted according +//! to their referencing context. +struct ALIGNAS(4) PACKED MinidumpByteArray { + //! \brief The length of the #data field. + uint32_t length; + + //! \brief The bytes of data. + uint8_t data[0]; +}; + +//! \brief CPU type values for MINIDUMP_SYSTEM_INFO::ProcessorArchitecture. +//! +//! \sa \ref PROCESSOR_ARCHITECTURE_x "PROCESSOR_ARCHITECTURE_*" +enum MinidumpCPUArchitecture : uint16_t { + //! \brief 32-bit x86. + //! + //! These systems identify their CPUs generically as “x86†or “ia32â€, or with + //! more specific names such as “i386â€, “i486â€, “i586â€, and “i686â€. + kMinidumpCPUArchitectureX86 = PROCESSOR_ARCHITECTURE_INTEL, + + kMinidumpCPUArchitectureMIPS = PROCESSOR_ARCHITECTURE_MIPS, + kMinidumpCPUArchitectureAlpha = PROCESSOR_ARCHITECTURE_ALPHA, + + //! \brief 32-bit PowerPC. + //! + //! These systems identify their CPUs generically as “ppcâ€, or with more + //! specific names such as “ppc6xxâ€, “ppc7xxâ€, and “ppc74xxâ€. + kMinidumpCPUArchitecturePPC = PROCESSOR_ARCHITECTURE_PPC, + + kMinidumpCPUArchitectureSHx = PROCESSOR_ARCHITECTURE_SHX, + + //! \brief 32-bit ARM. + //! + //! These systems identify their CPUs generically as “armâ€, or with more + //! specific names such as “armv6†and “armv7â€. + kMinidumpCPUArchitectureARM = PROCESSOR_ARCHITECTURE_ARM, + + kMinidumpCPUArchitectureIA64 = PROCESSOR_ARCHITECTURE_IA64, + kMinidumpCPUArchitectureAlpha64 = PROCESSOR_ARCHITECTURE_ALPHA64, + kMinidumpCPUArchitectureMSIL = PROCESSOR_ARCHITECTURE_MSIL, + + //! \brief 64-bit x86. + //! + //! These systems identify their CPUs as “x86_64â€, “amd64â€, or “x64â€. + kMinidumpCPUArchitectureAMD64 = PROCESSOR_ARCHITECTURE_AMD64, + + //! \brief A 32-bit x86 process running on IA-64 (Itanium). + //! + //! \note This value is not used in minidump files for 32-bit x86 processes + //! running on a 64-bit-capable x86 CPU and operating system. In that + //! configuration, #kMinidumpCPUArchitectureX86 is used instead. + kMinidumpCPUArchitectureX86Win64 = PROCESSOR_ARCHITECTURE_IA32_ON_WIN64, + + kMinidumpCPUArchitectureNeutral = PROCESSOR_ARCHITECTURE_NEUTRAL, + + //! \brief 64-bit ARM. + //! + //! These systems identify their CPUs generically as “arm64†or “aarch64â€, or + //! with more specific names such as “armv8â€. + //! + //! \sa #kMinidumpCPUArchitectureARM64Breakpad + kMinidumpCPUArchitectureARM64 = PROCESSOR_ARCHITECTURE_ARM64, + + kMinidumpCPUArchitectureARM32Win64 = PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64, + kMinidumpCPUArchitectureSPARC = 0x8001, + + //! \brief 64-bit PowerPC. + //! + //! These systems identify their CPUs generically as “ppc64â€, or with more + //! specific names such as “ppc970â€. + kMinidumpCPUArchitecturePPC64 = 0x8002, + + //! \brief Used by Breakpad for 64-bit ARM. + //! + //! \deprecated Use #kMinidumpCPUArchitectureARM64 instead. + kMinidumpCPUArchitectureARM64Breakpad = 0x8003, + + //! \brief Unknown CPU architecture. + kMinidumpCPUArchitectureUnknown = PROCESSOR_ARCHITECTURE_UNKNOWN, +}; + +//! \brief Operating system type values for MINIDUMP_SYSTEM_INFO::ProductType. +//! +//! \sa \ref VER_NT_x "VER_NT_*" +enum MinidumpOSType : uint8_t { + //! \brief A “desktop†or “workstation†system. + kMinidumpOSTypeWorkstation = VER_NT_WORKSTATION, + + //! \brief A “domain controller†system. Windows-specific. + kMinidumpOSTypeDomainController = VER_NT_DOMAIN_CONTROLLER, + + //! \brief A “server†system. + kMinidumpOSTypeServer = VER_NT_SERVER, +}; + +//! \brief Operating system family values for MINIDUMP_SYSTEM_INFO::PlatformId. +//! +//! \sa \ref VER_PLATFORM_x "VER_PLATFORM_*" +enum MinidumpOS : uint32_t { + //! \brief Windows 3.1. + kMinidumpOSWin32s = VER_PLATFORM_WIN32s, + + //! \brief Windows 95, Windows 98, and Windows Me. + kMinidumpOSWin32Windows = VER_PLATFORM_WIN32_WINDOWS, + + //! \brief Windows NT, Windows 2000, and later. + kMinidumpOSWin32NT = VER_PLATFORM_WIN32_NT, + + kMinidumpOSUnix = 0x8000, + + //! \brief macOS, Darwin for traditional systems. + kMinidumpOSMacOSX = 0x8101, + + //! \brief iOS, Darwin for mobile devices. + kMinidumpOSIOS = 0x8102, + + //! \brief Linux, not including Android. + kMinidumpOSLinux = 0x8201, + + kMinidumpOSSolaris = 0x8202, + + //! \brief Android. + kMinidumpOSAndroid = 0x8203, + + kMinidumpOSPS3 = 0x8204, + + //! \brief Native Client (NaCl). + kMinidumpOSNaCl = 0x8205, + + //! \brief Fuchsia. + kMinidumpOSFuchsia = 0x8206, + + //! \brief Unknown operating system. + kMinidumpOSUnknown = 0xffffffff, +}; + +//! \brief A list of ::RVA pointers. +struct ALIGNAS(4) PACKED MinidumpRVAList { + //! \brief The number of children present in the #children array. + uint32_t count; + + //! \brief Pointers to other structures in the minidump file. + RVA children[0]; +}; + +//! \brief A key-value pair. +struct ALIGNAS(4) PACKED MinidumpSimpleStringDictionaryEntry { + //! \brief ::RVA of a MinidumpUTF8String containing the key of a key-value + //! pair. + RVA key; + + //! \brief ::RVA of a MinidumpUTF8String containing the value of a key-value + //! pair. + RVA value; +}; + +//! \brief A list of key-value pairs. +struct ALIGNAS(4) PACKED MinidumpSimpleStringDictionary { + //! \brief The number of key-value pairs present. + uint32_t count; + + //! \brief A list of MinidumpSimpleStringDictionaryEntry entries. + MinidumpSimpleStringDictionaryEntry entries[0]; +}; + +//! \brief A typed annotation object. +struct ALIGNAS(4) PACKED MinidumpAnnotation { + //! \brief ::RVA of a MinidumpUTF8String containing the name of the + //! annotation. + RVA name; + + //! \brief The type of data stored in the \a value of the annotation. This + //! may correspond to an \a Annotation::Type or it may be user-defined. + uint16_t type; + + //! \brief This field is always `0`. + uint16_t reserved; + + //! \brief ::RVA of a MinidumpByteArray to the data for the annotation. + RVA value; +}; + +//! \brief A list of annotation objects. +struct ALIGNAS(4) PACKED MinidumpAnnotationList { + //! \brief The number of annotation objects present. + uint32_t count; + + //! \brief A list of MinidumpAnnotation objects. + MinidumpAnnotation objects[0]; +}; + +//! \brief Additional Crashpad-specific information about a module carried +//! within a minidump file. +//! +//! This structure augments the information provided by MINIDUMP_MODULE. The +//! minidump file must contain a module list stream +//! (::kMinidumpStreamTypeModuleList) in order for this structure to appear. +//! +//! This structure is versioned. When changing this structure, leave the +//! existing structure intact so that earlier parsers will be able to understand +//! the fields they are aware of, and make additions at the end of the +//! structure. Revise #kVersion and document each field’s validity based on +//! #version, so that newer parsers will be able to determine whether the added +//! fields are valid or not. +//! +//! \sa MinidumpModuleCrashpadInfoList +struct ALIGNAS(4) PACKED MinidumpModuleCrashpadInfo { + //! \brief The structure’s currently-defined version number. + //! + //! \sa version + static constexpr uint32_t kVersion = 1; + + //! \brief The structure’s version number. + //! + //! Readers can use this field to determine which other fields in the + //! structure are valid. Upon encountering a value greater than #kVersion, a + //! reader should assume that the structure’s layout is compatible with the + //! structure defined as having value #kVersion. + //! + //! Writers may produce values less than #kVersion in this field if there is + //! no need for any fields present in later versions. + uint32_t version; + + //! \brief A MinidumpRVAList pointing to MinidumpUTF8String objects. The + //! module controls the data that appears here. + //! + //! These strings correspond to ModuleSnapshot::AnnotationsVector() and do not + //! duplicate anything in #simple_annotations or #annotation_objects. + //! + //! This field is present when #version is at least `1`. + MINIDUMP_LOCATION_DESCRIPTOR list_annotations; + + //! \brief A MinidumpSimpleStringDictionary pointing to strings interpreted as + //! key-value pairs. The module controls the data that appears here. + //! + //! These key-value pairs correspond to + //! ModuleSnapshot::AnnotationsSimpleMap() and do not duplicate anything in + //! #list_annotations or #annotation_objects. + //! + //! This field is present when #version is at least `1`. + MINIDUMP_LOCATION_DESCRIPTOR simple_annotations; + + //! \brief A MinidumpAnnotationList object containing the annotation objects + //! stored within the module. The module controls the data that appears + //! here. + //! + //! These key-value pairs correspond to ModuleSnapshot::AnnotationObjects() + //! and do not duplicate anything in #list_annotations or #simple_annotations. + //! + //! This field may be present when #version is at least `1`. + MINIDUMP_LOCATION_DESCRIPTOR annotation_objects; +}; + +//! \brief A link between a MINIDUMP_MODULE structure and additional +//! Crashpad-specific information about a module carried within a minidump +//! file. +struct ALIGNAS(4) PACKED MinidumpModuleCrashpadInfoLink { + //! \brief A link to a MINIDUMP_MODULE structure in the module list stream. + //! + //! This field is an index into MINIDUMP_MODULE_LIST::Modules. This field’s + //! value must be in the range of MINIDUMP_MODULE_LIST::NumberOfEntries. + uint32_t minidump_module_list_index; + + //! \brief A link to a MinidumpModuleCrashpadInfo structure. + //! + //! MinidumpModuleCrashpadInfo structures are accessed indirectly through + //! MINIDUMP_LOCATION_DESCRIPTOR pointers to allow for future growth of the + //! MinidumpModuleCrashpadInfo structure. + MINIDUMP_LOCATION_DESCRIPTOR location; +}; + +//! \brief Additional Crashpad-specific information about modules carried within +//! a minidump file. +//! +//! This structure augments the information provided by +//! MINIDUMP_MODULE_LIST. The minidump file must contain a module list stream +//! (::kMinidumpStreamTypeModuleList) in order for this structure to appear. +//! +//! MinidumpModuleCrashpadInfoList::count may be less than the value of +//! MINIDUMP_MODULE_LIST::NumberOfModules because not every MINIDUMP_MODULE +//! structure carried within the minidump file will necessarily have +//! Crashpad-specific information provided by a MinidumpModuleCrashpadInfo +//! structure. +struct ALIGNAS(4) PACKED MinidumpModuleCrashpadInfoList { + //! \brief The number of children present in the #modules array. + uint32_t count; + + //! \brief Crashpad-specific information about modules, along with links to + //! MINIDUMP_MODULE structures that contain module information + //! traditionally carried within minidump files. + MinidumpModuleCrashpadInfoLink modules[0]; +}; + +//! \brief Additional Crashpad-specific information carried within a minidump +//! file. +//! +//! This structure is versioned. When changing this structure, leave the +//! existing structure intact so that earlier parsers will be able to understand +//! the fields they are aware of, and make additions at the end of the +//! structure. Revise #kVersion and document each field’s validity based on +//! #version, so that newer parsers will be able to determine whether the added +//! fields are valid or not. +struct ALIGNAS(4) PACKED MinidumpCrashpadInfo { + // UUID has a constructor, which makes it non-POD, which makes this structure + // non-POD. In order for the default constructor to zero-initialize other + // members, an explicit constructor must be provided. + MinidumpCrashpadInfo() + : version(), + report_id(), + client_id(), + simple_annotations(), + module_list() { + } + + //! \brief The structure’s currently-defined version number. + //! + //! \sa version + static constexpr uint32_t kVersion = 1; + + //! \brief The structure’s version number. + //! + //! Readers can use this field to determine which other fields in the + //! structure are valid. Upon encountering a value greater than #kVersion, a + //! reader should assume that the structure’s layout is compatible with the + //! structure defined as having value #kVersion. + //! + //! Writers may produce values less than #kVersion in this field if there is + //! no need for any fields present in later versions. + uint32_t version; + + //! \brief A %UUID identifying an individual crash report. + //! + //! This provides a stable identifier for a crash even as the report is + //! converted to different formats, provided that all formats support storing + //! a crash report ID. + //! + //! If no identifier is available, this field will contain zeroes. + //! + //! This field is present when #version is at least `1`. + UUID report_id; + + //! \brief A %UUID identifying the client that crashed. + //! + //! Client identification is within the scope of the application, but it is + //! expected that the identifier will be unique for an instance of Crashpad + //! monitoring an application or set of applications for a user. The + //! identifier shall remain stable over time. + //! + //! If no identifier is available, this field will contain zeroes. + //! + //! This field is present when #version is at least `1`. + UUID client_id; + + //! \brief A MinidumpSimpleStringDictionary pointing to strings interpreted as + //! key-value pairs. + //! + //! These key-value pairs correspond to + //! ProcessSnapshot::AnnotationsSimpleMap(). + //! + //! This field is present when #version is at least `1`. + MINIDUMP_LOCATION_DESCRIPTOR simple_annotations; + + //! \brief A pointer to a MinidumpModuleCrashpadInfoList structure. + //! + //! This field is present when #version is at least `1`. + MINIDUMP_LOCATION_DESCRIPTOR module_list; +}; + +#if defined(COMPILER_MSVC) +#pragma pack(pop) +#pragma warning(pop) // C4200 +#endif // COMPILER_MSVC +#undef PACKED + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_EXTENSIONS_H_ diff --git a/shared/sentry/external/crashpad/minidump/minidump_file_writer.cc b/shared/sentry/external/crashpad/minidump/minidump_file_writer.cc new file mode 100644 index 000000000..99ee2da06 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_file_writer.cc @@ -0,0 +1,340 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_file_writer.h" + +#include + +#include "base/logging.h" +#include "minidump/minidump_crashpad_info_writer.h" +#include "minidump/minidump_exception_writer.h" +#include "minidump/minidump_handle_writer.h" +#include "minidump/minidump_memory_info_writer.h" +#include "minidump/minidump_memory_writer.h" +#include "minidump/minidump_misc_info_writer.h" +#include "minidump/minidump_module_writer.h" +#ifdef CLIENT_STACKTRACES_ENABLED +#include "minidump/minidump_stacktrace_writer.h" +#endif +#include "minidump/minidump_system_info_writer.h" +#include "minidump/minidump_thread_id_map.h" +#include "minidump/minidump_thread_writer.h" +#include "minidump/minidump_unloaded_module_writer.h" +#include "minidump/minidump_user_extension_stream_data_source.h" +#include "minidump/minidump_user_stream_writer.h" +#include "minidump/minidump_writer_util.h" +#include "snapshot/exception_snapshot.h" +#include "snapshot/module_snapshot.h" +#include "snapshot/process_snapshot.h" +#include "util/file/file_writer.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { + +MinidumpFileWriter::MinidumpFileWriter() + : MinidumpWritable(), header_(), streams_(), stream_types_() { + // Don’t set the signature field right away. Leave it set to 0, so that a + // partially-written minidump file isn’t confused for a complete and valid + // one. The header will be rewritten in WriteToFile(). + header_.Signature = 0; + + header_.Version = MINIDUMP_VERSION; + header_.CheckSum = 0; + header_.Flags = MiniDumpNormal; +} + +MinidumpFileWriter::~MinidumpFileWriter() {} + +void MinidumpFileWriter::InitializeFromSnapshot( + const ProcessSnapshot* process_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK_EQ(header_.Signature, 0u); + DCHECK_EQ(header_.TimeDateStamp, 0u); + DCHECK_EQ(static_cast(header_.Flags), MiniDumpNormal); + DCHECK(streams_.empty()); + + // This time is truncated to an integer number of seconds, not rounded, for + // compatibility with the truncation of process_snapshot->ProcessStartTime() + // done by MinidumpMiscInfoWriter::InitializeFromSnapshot(). Handling both + // timestamps in the same way allows the highest-fidelity computation of + // process uptime as the difference between the two values. + timeval snapshot_time; + process_snapshot->SnapshotTime(&snapshot_time); + SetTimestamp(snapshot_time.tv_sec); + + const SystemSnapshot* system_snapshot = process_snapshot->System(); + auto system_info = std::make_unique(); + system_info->InitializeFromSnapshot(system_snapshot); + bool add_stream_result = AddStream(std::move(system_info)); + DCHECK(add_stream_result); + + auto misc_info = std::make_unique(); + misc_info->InitializeFromSnapshot(process_snapshot); + add_stream_result = AddStream(std::move(misc_info)); + DCHECK(add_stream_result); + + auto memory_list = std::make_unique(); + auto thread_list = std::make_unique(); + thread_list->SetMemoryListWriter(memory_list.get()); + MinidumpThreadIDMap thread_id_map; + thread_list->InitializeFromSnapshot(process_snapshot->Threads(), + &thread_id_map); + add_stream_result = AddStream(std::move(thread_list)); + DCHECK(add_stream_result); + + const ExceptionSnapshot* exception_snapshot = process_snapshot->Exception(); + if (exception_snapshot) { + auto exception = std::make_unique(); + exception->InitializeFromSnapshot(exception_snapshot, thread_id_map); + add_stream_result = AddStream(std::move(exception)); + DCHECK(add_stream_result); + } + + auto module_list = std::make_unique(); + module_list->InitializeFromSnapshot(process_snapshot->Modules()); + add_stream_result = AddStream(std::move(module_list)); + DCHECK(add_stream_result); + +#ifdef CLIENT_STACKTRACES_ENABLED + auto stacktrace_list = std::make_unique(); + stacktrace_list->InitializeFromSnapshot( + process_snapshot->Threads(), thread_id_map, exception_snapshot); + add_stream_result = AddStream(std::move(stacktrace_list)); + DCHECK(add_stream_result); +#endif + + auto unloaded_modules = process_snapshot->UnloadedModules(); + if (!unloaded_modules.empty()) { + auto unloaded_module_list = + std::make_unique(); + unloaded_module_list->InitializeFromSnapshot(unloaded_modules); + add_stream_result = AddStream(std::move(unloaded_module_list)); + DCHECK(add_stream_result); + } + + auto crashpad_info = std::make_unique(); + crashpad_info->InitializeFromSnapshot(process_snapshot); + + // Since the MinidumpCrashpadInfo stream is an extension, it’s safe to not add + // it to the minidump file if it wouldn’t carry any useful information. + if (crashpad_info->IsUseful()) { + add_stream_result = AddStream(std::move(crashpad_info)); + DCHECK(add_stream_result); + } + + std::vector memory_map_snapshot = + process_snapshot->MemoryMap(); + if (!memory_map_snapshot.empty()) { + auto memory_info_list = std::make_unique(); + memory_info_list->InitializeFromSnapshot(memory_map_snapshot); + add_stream_result = AddStream(std::move(memory_info_list)); + DCHECK(add_stream_result); + } + + std::vector handles_snapshot = process_snapshot->Handles(); + if (!handles_snapshot.empty()) { + auto handle_data_writer = std::make_unique(); + handle_data_writer->InitializeFromSnapshot(handles_snapshot); + add_stream_result = AddStream(std::move(handle_data_writer)); + DCHECK(add_stream_result); + } + + memory_list->AddFromSnapshot(process_snapshot->ExtraMemory()); + if (exception_snapshot) { + memory_list->AddFromSnapshot(exception_snapshot->ExtraMemory()); + } + + // These user streams must be added last. Otherwise, a user stream with the + // same type as a well-known stream could preempt the well-known stream. As it + // stands now, earlier-discovered user streams can still preempt + // later-discovered ones. The well-known memory list stream is added after + // these user streams, but only with a check here to avoid adding a user + // stream that would preempt the memory list stream. + for (const auto& module : process_snapshot->Modules()) { + for (const UserMinidumpStream* stream : module->CustomMinidumpStreams()) { + if (stream->stream_type() == kMinidumpStreamTypeMemoryList) { + LOG(WARNING) << "discarding duplicate stream of type " + << stream->stream_type(); + continue; + } + auto user_stream = std::make_unique(); + user_stream->InitializeFromSnapshot(stream); + AddStream(std::move(user_stream)); + } + } + + // The memory list stream should be added last. This keeps the “extra memory†+ // at the end so that if the minidump file is truncated, other, more critical + // data is more likely to be preserved. Note that non-“extra†memory regions + // will not have to ride at the end of the file. Thread stack memory, for + // example, exists as a children of threads, and appears alongside them in the + // file, despite also being mentioned by the memory list stream. + add_stream_result = AddStream(std::move(memory_list)); + DCHECK(add_stream_result); +} + +void MinidumpFileWriter::SetTimestamp(time_t timestamp) { + DCHECK_EQ(state(), kStateMutable); + + internal::MinidumpWriterUtil::AssignTimeT(&header_.TimeDateStamp, timestamp); +} + +bool MinidumpFileWriter::AddStream( + std::unique_ptr stream) { + DCHECK_EQ(state(), kStateMutable); + + MinidumpStreamType stream_type = stream->StreamType(); + + auto rv = stream_types_.insert(stream_type); + if (!rv.second) { + LOG(WARNING) << "discarding duplicate stream of type " << stream_type; + return false; + } + + streams_.push_back(std::move(stream)); + + DCHECK_EQ(streams_.size(), stream_types_.size()); + return true; +} + +bool MinidumpFileWriter::AddUserExtensionStream( + std::unique_ptr + user_extension_stream_data) { + DCHECK_EQ(state(), kStateMutable); + + auto user_stream = std::make_unique(); + user_stream->InitializeFromUserExtensionStream( + std::move(user_extension_stream_data)); + + return AddStream(std::move(user_stream)); +} + +bool MinidumpFileWriter::WriteEverything(FileWriterInterface* file_writer) { + return WriteMinidump(file_writer, true); +} + +bool MinidumpFileWriter::WriteMinidump(FileWriterInterface* file_writer, + bool allow_seek) { + DCHECK_EQ(state(), kStateMutable); + + FileOffset start_offset = -1; + if (allow_seek) { + start_offset = file_writer->Seek(0, SEEK_CUR); + if (start_offset < 0) { + return false; + } + } else { + header_.Signature = MINIDUMP_SIGNATURE; + } + + if (!MinidumpWritable::WriteEverything(file_writer)) { + return false; + } + + if (!allow_seek) + return true; + + FileOffset end_offset = file_writer->Seek(0, SEEK_CUR); + if (end_offset < 0) { + return false; + } + + // Now that the entire minidump file has been completely written, go back to + // the beginning and rewrite the header with the correct signature to identify + // it as a valid minidump file. + header_.Signature = MINIDUMP_SIGNATURE; + + if (file_writer->Seek(start_offset, SEEK_SET) < 0) { + return false; + } + + if (!file_writer->Write(&header_, sizeof(header_))) { + return false; + } + + // Seek back to the end of the file, in case some non-minidump content will be + // written to the file after the minidump content. + return file_writer->Seek(end_offset, SEEK_SET) >= 0; +} + +bool MinidumpFileWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + size_t stream_count = streams_.size(); + CHECK_EQ(stream_count, stream_types_.size()); + + if (!AssignIfInRange(&header_.NumberOfStreams, stream_count)) { + LOG(ERROR) << "stream_count " << stream_count << " out of range"; + return false; + } + + return true; +} + +size_t MinidumpFileWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + DCHECK_EQ(streams_.size(), stream_types_.size()); + + return sizeof(header_) + streams_.size() * sizeof(MINIDUMP_DIRECTORY); +} + +std::vector MinidumpFileWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + DCHECK_EQ(streams_.size(), stream_types_.size()); + + std::vector children; + for (const auto& stream : streams_) { + children.push_back(stream.get()); + } + + return children; +} + +bool MinidumpFileWriter::WillWriteAtOffsetImpl(FileOffset offset) { + DCHECK_EQ(state(), kStateFrozen); + DCHECK_EQ(offset, 0); + DCHECK_EQ(streams_.size(), stream_types_.size()); + + auto directory_offset = streams_.empty() ? 0 : offset + sizeof(header_); + if (!AssignIfInRange(&header_.StreamDirectoryRva, directory_offset)) { + LOG(ERROR) << "offset " << directory_offset << " out of range"; + return false; + } + + return MinidumpWritable::WillWriteAtOffsetImpl(offset); +} + +bool MinidumpFileWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + DCHECK_EQ(streams_.size(), stream_types_.size()); + + WritableIoVec iov; + iov.iov_base = &header_; + iov.iov_len = sizeof(header_); + std::vector iovecs(1, iov); + + for (const auto& stream : streams_) { + iov.iov_base = stream->DirectoryListEntry(); + iov.iov_len = sizeof(MINIDUMP_DIRECTORY); + iovecs.push_back(iov); + } + + return file_writer->WriteIoVec(&iovecs); +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_file_writer.h b/shared/sentry/external/crashpad/minidump/minidump_file_writer.h new file mode 100644 index 000000000..443a947e9 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_file_writer.h @@ -0,0 +1,173 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_FILE_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_FILE_WRITER_H_ + +#include +#include +#include + +#include +#include +#include + +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_stream_writer.h" +#include "minidump/minidump_writable.h" +#include "util/file/file_io.h" + +namespace crashpad { + +class ProcessSnapshot; +class MinidumpUserExtensionStreamDataSource; + +//! \brief The root-level object in a minidump file. +//! +//! This object writes a MINIDUMP_HEADER and list of MINIDUMP_DIRECTORY entries +//! to a minidump file. +class MinidumpFileWriter final : public internal::MinidumpWritable { + public: + MinidumpFileWriter(); + + MinidumpFileWriter(const MinidumpFileWriter&) = delete; + MinidumpFileWriter& operator=(const MinidumpFileWriter&) = delete; + + ~MinidumpFileWriter() override; + + //! \brief Initializes the MinidumpFileWriter and populates it with + //! appropriate child streams based on \a process_snapshot. + //! + //! This method will add additional streams to the minidump file as children + //! of the MinidumpFileWriter object and as pointees of the top-level + //! MINIDUMP_DIRECTORY. To do so, it will obtain other snapshot information + //! from \a process_snapshot, such as a SystemSnapshot, lists of + //! ThreadSnapshot and ModuleSnapshot objects, and, if available, an + //! ExceptionSnapshot. + //! + //! The streams are added in the order that they are expected to be most + //! useful to minidump readers, to improve data locality and minimize seeking. + //! The streams are added in this order: + //! - kMinidumpStreamTypeSystemInfo + //! - kMinidumpStreamTypeMiscInfo + //! - kMinidumpStreamTypeThreadList + //! - kMinidumpStreamTypeException (if present) + //! - kMinidumpStreamTypeModuleList + //! - kMinidumpStreamTypeUnloadedModuleList (if present) + //! - kMinidumpStreamTypeCrashpadInfo (if present) + //! - kMinidumpStreamTypeMemoryInfoList (if present) + //! - kMinidumpStreamTypeHandleData (if present) + //! - User streams (if present) + //! - kMinidumpStreamTypeMemoryList + //! + //! \param[in] process_snapshot The process snapshot to use as source data. + //! + //! \note Valid in #kStateMutable. No mutator methods may be called before + //! this method, and it is not normally necessary to call any mutator + //! methods after this method. + void InitializeFromSnapshot(const ProcessSnapshot* process_snapshot); + + //! \brief Sets MINIDUMP_HEADER::Timestamp. + //! + //! \note Valid in #kStateMutable. + void SetTimestamp(time_t timestamp); + + //! \brief Adds a stream to the minidump file and arranges for a + //! MINIDUMP_DIRECTORY entry to point to it. + //! + //! This object takes ownership of \a stream and becomes its parent in the + //! overall tree of internal::MinidumpWritable objects. + //! + //! At most one object of each stream type (as obtained from + //! internal::MinidumpStreamWriter::StreamType()) may be added to a + //! MinidumpFileWriter object. If an attempt is made to add a stream whose + //! type matches an existing stream’s type, this method discards the new + //! stream. + //! + //! \note Valid in #kStateMutable. + //! + //! \return `true` on success. `false` on failure, as occurs when an attempt + //! is made to add a stream whose type matches an existing stream’s type, + //! with a message logged. + bool AddStream(std::unique_ptr stream); + + //! \brief Adds a user extension stream to the minidump file and arranges for + //! a MINIDUMP_DIRECTORY entry to point to it. + //! + //! This object takes ownership of \a user_extension_stream_data. + //! + //! At most one object of each stream type (as obtained from + //! internal::MinidumpStreamWriter::StreamType()) may be added to a + //! MinidumpFileWriter object. If an attempt is made to add a stream whose + //! type matches an existing stream’s type, this method discards the new + //! stream. + //! + //! \param[in] user_extension_stream_data The stream data to add to the + //! minidump file. Note that the buffer this object points to must be valid + //! through WriteEverything(). + //! + //! \note Valid in #kStateMutable. + //! + //! \return `true` on success. `false` on failure, as occurs when an attempt + //! is made to add a stream whose type matches an existing stream’s type, + //! with a message logged. + bool AddUserExtensionStream( + std::unique_ptr + user_extension_stream_data); + + // MinidumpWritable: + + //! \copydoc internal::MinidumpWritable::WriteEverything() + //! + //! This method does not initially write the final value for + //! MINIDUMP_HEADER::Signature. After all child objects have been written, it + //! rewinds to the beginning of the file and writes the correct value for this + //! field. This prevents incompletely-written minidump files from being + //! mistaken for valid ones. + bool WriteEverything(FileWriterInterface* file_writer) override; + + //! \brief Writes this object to a minidump file. + //! + //! Same as \a WriteEverything, but give the option to disable the seek. It + //! is typically used to write to stream backed \a FileWriterInterface which + //! doesn't support seek. + //! + //! \param[in] file_writer The file writer to receive the minidump file’s + //! content. + //! + //! \param[in] allow_seek Whether seek is allowed. + //! + //! \return `true` on success. `false` on failure, with an appropriate message + //! logged. + bool WriteMinidump(FileWriterInterface* file_writer, bool allow_seek); + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector Children() override; + bool WillWriteAtOffsetImpl(FileOffset offset) override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + MINIDUMP_HEADER header_; + std::vector> streams_; + + // Protects against multiple streams with the same ID being added. + std::set stream_types_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_WRITER_H_ diff --git a/shared/sentry/external/crashpad/minidump/minidump_file_writer_test.cc b/shared/sentry/external/crashpad/minidump/minidump_file_writer_test.cc new file mode 100644 index 000000000..75c8525c8 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_file_writer_test.cc @@ -0,0 +1,650 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_file_writer.h" + +#include +#include + +#include +#include +#include + +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "minidump/minidump_stream_writer.h" +#include "minidump/minidump_user_extension_stream_data_source.h" +#include "minidump/test/minidump_file_writer_test_util.h" +#include "minidump/test/minidump_user_extension_stream_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "snapshot/test/test_cpu_context.h" +#include "snapshot/test/test_exception_snapshot.h" +#include "snapshot/test/test_memory_snapshot.h" +#include "snapshot/test/test_module_snapshot.h" +#include "snapshot/test/test_process_snapshot.h" +#include "snapshot/test/test_system_snapshot.h" +#include "snapshot/test/test_thread_snapshot.h" +#include "test/gtest_death.h" +#include "util/file/output_stream_file_writer.h" +#include "util/file/string_file.h" +#include "util/stream/output_stream_interface.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(MinidumpFileWriter, Empty) { + MinidumpFileWriter minidump_file; + StringFile string_file; + ASSERT_TRUE(minidump_file.WriteEverything(&string_file)); + ASSERT_EQ(string_file.string().size(), sizeof(MINIDUMP_HEADER)); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(string_file.string(), &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 0, 0)); + EXPECT_FALSE(directory); +} + +class TestStream final : public internal::MinidumpStreamWriter { + public: + TestStream(MinidumpStreamType stream_type, + size_t stream_size, + uint8_t stream_value) + : stream_data_(stream_size, stream_value), stream_type_(stream_type) {} + + TestStream(const TestStream&) = delete; + TestStream& operator=(const TestStream&) = delete; + + ~TestStream() override {} + + // MinidumpStreamWriter: + MinidumpStreamType StreamType() const override { + return stream_type_; + } + + protected: + // MinidumpWritable: + size_t SizeOfObject() override { + EXPECT_GE(state(), kStateFrozen); + return stream_data_.size(); + } + + bool WriteObject(FileWriterInterface* file_writer) override { + EXPECT_EQ(kStateWritable, state()); + return file_writer->Write(&stream_data_[0], stream_data_.size()); + } + + private: + std::string stream_data_; + MinidumpStreamType stream_type_; +}; + +class StringFileOutputStream : public OutputStreamInterface { + public: + StringFileOutputStream() = default; + + StringFileOutputStream(const StringFileOutputStream&) = delete; + StringFileOutputStream& operator=(const StringFileOutputStream&) = delete; + + ~StringFileOutputStream() override = default; + bool Write(const uint8_t* data, size_t size) override { + return string_file_.Write(data, size); + } + bool Flush() override { return true; } + const StringFile& string_file() const { return string_file_; } + + private: + StringFile string_file_; +}; + +TEST(MinidumpFileWriter, OneStream) { + MinidumpFileWriter minidump_file; + constexpr time_t kTimestamp = 0x155d2fb8; + minidump_file.SetTimestamp(kTimestamp); + + constexpr size_t kStreamSize = 5; + constexpr MinidumpStreamType kStreamType = + static_cast(0x4d); + constexpr uint8_t kStreamValue = 0x5a; + auto stream = + std::make_unique(kStreamType, kStreamSize, kStreamValue); + ASSERT_TRUE(minidump_file.AddStream(std::move(stream))); + + StringFile string_file; + ASSERT_TRUE(minidump_file.WriteEverything(&string_file)); + + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kStreamOffset = + kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + constexpr size_t kFileSize = kStreamOffset + kStreamSize; + + ASSERT_EQ(string_file.string().size(), kFileSize); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(string_file.string(), &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, kTimestamp)); + ASSERT_TRUE(directory); + + EXPECT_EQ(directory[0].StreamType, kStreamType); + EXPECT_EQ(directory[0].Location.DataSize, kStreamSize); + EXPECT_EQ(directory[0].Location.Rva, kStreamOffset); + + const uint8_t* stream_data = MinidumpWritableAtLocationDescriptor( + string_file.string(), directory[0].Location); + ASSERT_TRUE(stream_data); + + std::string expected_stream(kStreamSize, kStreamValue); + EXPECT_EQ(memcmp(stream_data, expected_stream.c_str(), kStreamSize), 0); +} + +TEST(MinidumpFileWriter, AddUserExtensionStream) { + MinidumpFileWriter minidump_file; + constexpr time_t kTimestamp = 0x155d2fb8; + minidump_file.SetTimestamp(kTimestamp); + + static constexpr uint8_t kStreamData[] = "Hello World!"; + constexpr size_t kStreamSize = std::size(kStreamData); + constexpr MinidumpStreamType kStreamType = + static_cast(0x4d); + + auto data_source = std::make_unique( + kStreamType, kStreamData, kStreamSize); + ASSERT_TRUE(minidump_file.AddUserExtensionStream(std::move(data_source))); + + // Adding the same stream type a second time should fail. + data_source = std::make_unique( + kStreamType, kStreamData, kStreamSize); + ASSERT_FALSE(minidump_file.AddUserExtensionStream(std::move(data_source))); + + StringFile string_file; + ASSERT_TRUE(minidump_file.WriteEverything(&string_file)); + + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kStreamOffset = + kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + constexpr size_t kFileSize = kStreamOffset + kStreamSize; + + ASSERT_EQ(string_file.string().size(), kFileSize); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(string_file.string(), &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, kTimestamp)); + ASSERT_TRUE(directory); + + EXPECT_EQ(directory[0].StreamType, kStreamType); + EXPECT_EQ(directory[0].Location.DataSize, kStreamSize); + EXPECT_EQ(directory[0].Location.Rva, kStreamOffset); + + const uint8_t* stream_data = MinidumpWritableAtLocationDescriptor( + string_file.string(), directory[0].Location); + ASSERT_TRUE(stream_data); + + EXPECT_EQ(memcmp(stream_data, kStreamData, kStreamSize), 0); +} + +TEST(MinidumpFileWriter, AddEmptyUserExtensionStream) { + MinidumpFileWriter minidump_file; + constexpr time_t kTimestamp = 0x155d2fb8; + minidump_file.SetTimestamp(kTimestamp); + + constexpr MinidumpStreamType kStreamType = + static_cast(0x4d); + + auto data_source = std::make_unique( + kStreamType, nullptr, 0); + ASSERT_TRUE(minidump_file.AddUserExtensionStream(std::move(data_source))); + + StringFile string_file; + ASSERT_TRUE(minidump_file.WriteEverything(&string_file)); + + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kStreamOffset = + kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + constexpr size_t kFileSize = kStreamOffset; + + ASSERT_EQ(string_file.string().size(), kFileSize); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(string_file.string(), &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, kTimestamp)); + ASSERT_TRUE(directory); + + EXPECT_EQ(directory[0].StreamType, kStreamType); + EXPECT_EQ(directory[0].Location.DataSize, 0u); + EXPECT_EQ(directory[0].Location.Rva, kStreamOffset); +} + +TEST(MinidumpFileWriter, ThreeStreams) { + MinidumpFileWriter minidump_file; + constexpr time_t kTimestamp = 0x155d2fb8; + minidump_file.SetTimestamp(kTimestamp); + + constexpr size_t kStream0Size = 5; + constexpr MinidumpStreamType kStream0Type = + static_cast(0x6d); + constexpr uint8_t kStream0Value = 0x5a; + auto stream0 = + std::make_unique(kStream0Type, kStream0Size, kStream0Value); + ASSERT_TRUE(minidump_file.AddStream(std::move(stream0))); + + // Make the second stream’s type be a smaller quantity than the first stream’s + // to test that the streams show up in the order that they were added, not in + // numeric order. + constexpr size_t kStream1Size = 3; + constexpr MinidumpStreamType kStream1Type = + static_cast(0x4d); + constexpr uint8_t kStream1Value = 0xa5; + auto stream1 = + std::make_unique(kStream1Type, kStream1Size, kStream1Value); + ASSERT_TRUE(minidump_file.AddStream(std::move(stream1))); + + constexpr size_t kStream2Size = 1; + constexpr MinidumpStreamType kStream2Type = + static_cast(0x7e); + constexpr uint8_t kStream2Value = 0x36; + auto stream2 = + std::make_unique(kStream2Type, kStream2Size, kStream2Value); + ASSERT_TRUE(minidump_file.AddStream(std::move(stream2))); + + StringFile string_file; + ASSERT_TRUE(minidump_file.WriteEverything(&string_file)); + + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kStream0Offset = + kDirectoryOffset + 3 * sizeof(MINIDUMP_DIRECTORY); + constexpr size_t kStream1Padding = 3; + constexpr size_t kStream1Offset = + kStream0Offset + kStream0Size + kStream1Padding; + constexpr size_t kStream2Padding = 1; + constexpr size_t kStream2Offset = + kStream1Offset + kStream1Size + kStream2Padding; + constexpr size_t kFileSize = kStream2Offset + kStream2Size; + + ASSERT_EQ(string_file.string().size(), kFileSize); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(string_file.string(), &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 3, kTimestamp)); + ASSERT_TRUE(directory); + + EXPECT_EQ(directory[0].StreamType, kStream0Type); + EXPECT_EQ(directory[0].Location.DataSize, kStream0Size); + EXPECT_EQ(directory[0].Location.Rva, kStream0Offset); + EXPECT_EQ(directory[1].StreamType, kStream1Type); + EXPECT_EQ(directory[1].Location.DataSize, kStream1Size); + EXPECT_EQ(directory[1].Location.Rva, kStream1Offset); + EXPECT_EQ(directory[2].StreamType, kStream2Type); + EXPECT_EQ(directory[2].Location.DataSize, kStream2Size); + EXPECT_EQ(directory[2].Location.Rva, kStream2Offset); + + const uint8_t* stream0_data = MinidumpWritableAtLocationDescriptor( + string_file.string(), directory[0].Location); + ASSERT_TRUE(stream0_data); + + std::string expected_stream0(kStream0Size, kStream0Value); + EXPECT_EQ(memcmp(stream0_data, expected_stream0.c_str(), kStream0Size), 0); + + static constexpr int kZeroes[16] = {}; + ASSERT_GE(sizeof(kZeroes), kStream1Padding); + EXPECT_EQ(memcmp(stream0_data + kStream0Size, kZeroes, kStream1Padding), 0); + + const uint8_t* stream1_data = MinidumpWritableAtLocationDescriptor( + string_file.string(), directory[1].Location); + ASSERT_TRUE(stream1_data); + + std::string expected_stream1(kStream1Size, kStream1Value); + EXPECT_EQ(memcmp(stream1_data, expected_stream1.c_str(), kStream1Size), 0); + + ASSERT_GE(sizeof(kZeroes), kStream2Padding); + EXPECT_EQ(memcmp(stream1_data + kStream1Size, kZeroes, kStream2Padding), 0); + + const uint8_t* stream2_data = MinidumpWritableAtLocationDescriptor( + string_file.string(), directory[2].Location); + ASSERT_TRUE(stream2_data); + + std::string expected_stream2(kStream2Size, kStream2Value); + EXPECT_EQ(memcmp(stream2_data, expected_stream2.c_str(), kStream2Size), 0); +} + +TEST(MinidumpFileWriter, ZeroLengthStream) { + MinidumpFileWriter minidump_file; + + constexpr size_t kStreamSize = 0; + constexpr MinidumpStreamType kStreamType = + static_cast(0x4d); + auto stream = std::make_unique( + kStreamType, kStreamSize, static_cast(0)); + ASSERT_TRUE(minidump_file.AddStream(std::move(stream))); + + StringFile string_file; + ASSERT_TRUE(minidump_file.WriteEverything(&string_file)); + + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kStreamOffset = + kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + constexpr size_t kFileSize = kStreamOffset + kStreamSize; + + ASSERT_EQ(string_file.string().size(), kFileSize); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(string_file.string(), &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0)); + ASSERT_TRUE(directory); + + EXPECT_EQ(directory[0].StreamType, kStreamType); + EXPECT_EQ(directory[0].Location.DataSize, kStreamSize); + EXPECT_EQ(directory[0].Location.Rva, kStreamOffset); +} + +TEST(MinidumpFileWriter, InitializeFromSnapshot_Basic) { + constexpr uint32_t kSnapshotTime = 0x4976043c; + constexpr timeval kSnapshotTimeval = {static_cast(kSnapshotTime), 0}; + + TestProcessSnapshot process_snapshot; + process_snapshot.SetSnapshotTime(kSnapshotTimeval); + + auto system_snapshot = std::make_unique(); + system_snapshot->SetCPUArchitecture(kCPUArchitectureX86_64); + system_snapshot->SetOperatingSystem(SystemSnapshot::kOperatingSystemMacOSX); + process_snapshot.SetSystem(std::move(system_snapshot)); + + auto peb_snapshot = std::make_unique(); + constexpr uint64_t kPebAddress = 0x07f90000; + peb_snapshot->SetAddress(kPebAddress); + constexpr size_t kPebSize = 0x280; + peb_snapshot->SetSize(kPebSize); + peb_snapshot->SetValue('p'); + process_snapshot.AddExtraMemory(std::move(peb_snapshot)); + + MinidumpFileWriter minidump_file_writer; + minidump_file_writer.InitializeFromSnapshot(&process_snapshot); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(string_file.string(), &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 5, kSnapshotTime)); + ASSERT_TRUE(directory); + + EXPECT_EQ(directory[0].StreamType, kMinidumpStreamTypeSystemInfo); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + string_file.string(), directory[0].Location)); + + EXPECT_EQ(directory[1].StreamType, kMinidumpStreamTypeMiscInfo); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + string_file.string(), directory[1].Location)); + + EXPECT_EQ(directory[2].StreamType, kMinidumpStreamTypeThreadList); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + string_file.string(), directory[2].Location)); + + EXPECT_EQ(directory[3].StreamType, kMinidumpStreamTypeModuleList); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + string_file.string(), directory[3].Location)); + + EXPECT_EQ(directory[4].StreamType, kMinidumpStreamTypeMemoryList); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + string_file.string(), directory[4].Location)); + + const MINIDUMP_MEMORY_LIST* memory_list = + MinidumpWritableAtLocationDescriptor( + string_file.string(), directory[4].Location); + EXPECT_EQ(memory_list->NumberOfMemoryRanges, 1u); + EXPECT_EQ(memory_list->MemoryRanges[0].StartOfMemoryRange, kPebAddress); + EXPECT_EQ(memory_list->MemoryRanges[0].Memory.DataSize, kPebSize); +} + +TEST(MinidumpFileWriter, InitializeFromSnapshot_Exception) { + // In a 32-bit environment, this will give a “timestamp out of range†warning, + // but the test should complete without failure. + constexpr uint32_t kSnapshotTime = 0xfd469ab8; + constexpr timeval kSnapshotTimeval = { +#if BUILDFLAG(IS_WIN) + static_cast(kSnapshotTime), +#else + static_cast(kSnapshotTime), +#endif + 0 + }; + + TestProcessSnapshot process_snapshot; + process_snapshot.SetSnapshotTime(kSnapshotTimeval); + + auto system_snapshot = std::make_unique(); + system_snapshot->SetCPUArchitecture(kCPUArchitectureX86_64); + system_snapshot->SetOperatingSystem(SystemSnapshot::kOperatingSystemMacOSX); + process_snapshot.SetSystem(std::move(system_snapshot)); + + auto thread_snapshot = std::make_unique(); + InitializeCPUContextX86_64(thread_snapshot->MutableContext(), 5); + process_snapshot.AddThread(std::move(thread_snapshot)); + + auto exception_snapshot = std::make_unique(); + InitializeCPUContextX86_64(exception_snapshot->MutableContext(), 11); + process_snapshot.SetException(std::move(exception_snapshot)); + + // The module does not have anything that needs to be represented in a + // MinidumpModuleCrashpadInfo structure, so no such structure is expected to + // be present, which will in turn suppress the addition of a + // MinidumpCrashpadInfo stream. + auto module_snapshot = std::make_unique(); + process_snapshot.AddModule(std::move(module_snapshot)); + + MinidumpFileWriter minidump_file_writer; + minidump_file_writer.InitializeFromSnapshot(&process_snapshot); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(string_file.string(), &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 6, kSnapshotTime)); + ASSERT_TRUE(directory); + + EXPECT_EQ(directory[0].StreamType, kMinidumpStreamTypeSystemInfo); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + string_file.string(), directory[0].Location)); + + EXPECT_EQ(directory[1].StreamType, kMinidumpStreamTypeMiscInfo); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + string_file.string(), directory[1].Location)); + + EXPECT_EQ(directory[2].StreamType, kMinidumpStreamTypeThreadList); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + string_file.string(), directory[2].Location)); + + EXPECT_EQ(directory[3].StreamType, kMinidumpStreamTypeException); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + string_file.string(), directory[3].Location)); + + EXPECT_EQ(directory[4].StreamType, kMinidumpStreamTypeModuleList); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + string_file.string(), directory[4].Location)); + + EXPECT_EQ(directory[5].StreamType, kMinidumpStreamTypeMemoryList); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + string_file.string(), directory[5].Location)); +} + +TEST(MinidumpFileWriter, InitializeFromSnapshot_CrashpadInfo) { + constexpr uint32_t kSnapshotTime = 0x15393bd3; + constexpr timeval kSnapshotTimeval = {static_cast(kSnapshotTime), 0}; + + TestProcessSnapshot process_snapshot; + process_snapshot.SetSnapshotTime(kSnapshotTimeval); + + auto system_snapshot = std::make_unique(); + system_snapshot->SetCPUArchitecture(kCPUArchitectureX86_64); + system_snapshot->SetOperatingSystem(SystemSnapshot::kOperatingSystemMacOSX); + process_snapshot.SetSystem(std::move(system_snapshot)); + + auto thread_snapshot = std::make_unique(); + InitializeCPUContextX86_64(thread_snapshot->MutableContext(), 5); + process_snapshot.AddThread(std::move(thread_snapshot)); + + auto exception_snapshot = std::make_unique(); + InitializeCPUContextX86_64(exception_snapshot->MutableContext(), 11); + process_snapshot.SetException(std::move(exception_snapshot)); + + // The module needs an annotation for the MinidumpCrashpadInfo stream to be + // considered useful and be included. + auto module_snapshot = std::make_unique(); + std::vector annotations_list(1, std::string("annotation")); + module_snapshot->SetAnnotationsVector(annotations_list); + process_snapshot.AddModule(std::move(module_snapshot)); + + MinidumpFileWriter minidump_file_writer; + minidump_file_writer.InitializeFromSnapshot(&process_snapshot); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(string_file.string(), &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 7, kSnapshotTime)); + ASSERT_TRUE(directory); + + EXPECT_EQ(directory[0].StreamType, kMinidumpStreamTypeSystemInfo); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + string_file.string(), directory[0].Location)); + + EXPECT_EQ(directory[1].StreamType, kMinidumpStreamTypeMiscInfo); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + string_file.string(), directory[1].Location)); + + EXPECT_EQ(directory[2].StreamType, kMinidumpStreamTypeThreadList); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + string_file.string(), directory[2].Location)); + + EXPECT_EQ(directory[3].StreamType, kMinidumpStreamTypeException); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + string_file.string(), directory[3].Location)); + + EXPECT_EQ(directory[4].StreamType, kMinidumpStreamTypeModuleList); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + string_file.string(), directory[4].Location)); + + EXPECT_EQ(directory[5].StreamType, kMinidumpStreamTypeCrashpadInfo); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + string_file.string(), directory[5].Location)); + + EXPECT_EQ(directory[6].StreamType, kMinidumpStreamTypeMemoryList); + EXPECT_TRUE(MinidumpWritableAtLocationDescriptor( + string_file.string(), directory[6].Location)); +} + +TEST(MinidumpFileWriter, SameStreamType) { + MinidumpFileWriter minidump_file; + + constexpr size_t kStream0Size = 3; + constexpr MinidumpStreamType kStreamType = + static_cast(0x4d); + constexpr uint8_t kStream0Value = 0x5a; + auto stream0 = + std::make_unique(kStreamType, kStream0Size, kStream0Value); + ASSERT_TRUE(minidump_file.AddStream(std::move(stream0))); + + // An attempt to add a second stream of the same type should fail. + constexpr size_t kStream1Size = 5; + constexpr uint8_t kStream1Value = 0xa5; + auto stream1 = + std::make_unique(kStreamType, kStream1Size, kStream1Value); + ASSERT_FALSE(minidump_file.AddStream(std::move(stream1))); + + StringFile string_file; + ASSERT_TRUE(minidump_file.WriteEverything(&string_file)); + + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kStream0Offset = + kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + constexpr size_t kFileSize = kStream0Offset + kStream0Size; + + ASSERT_EQ(string_file.string().size(), kFileSize); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(string_file.string(), &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0)); + ASSERT_TRUE(directory); + + EXPECT_EQ(directory[0].StreamType, kStreamType); + EXPECT_EQ(directory[0].Location.DataSize, kStream0Size); + EXPECT_EQ(directory[0].Location.Rva, kStream0Offset); + + const uint8_t* stream_data = MinidumpWritableAtLocationDescriptor( + string_file.string(), directory[0].Location); + ASSERT_TRUE(stream_data); + + std::string expected_stream(kStream0Size, kStream0Value); + EXPECT_EQ(memcmp(stream_data, expected_stream.c_str(), kStream0Size), 0); +} + +TEST(MinidumpFileWriter, WriteMinidumpDisallowSeek) { + MinidumpFileWriter minidump_file; + constexpr time_t kTimestamp = 0x155d2fb8; + minidump_file.SetTimestamp(kTimestamp); + + constexpr size_t kStreamSize = 5; + constexpr MinidumpStreamType kStreamType = + static_cast(0x4d); + constexpr uint8_t kStreamValue = 0x5a; + auto stream = + std::make_unique(kStreamType, kStreamSize, kStreamValue); + ASSERT_TRUE(minidump_file.AddStream(std::move(stream))); + + std::unique_ptr string_file_output_stream = + std::make_unique(); + const StringFile& string_file = string_file_output_stream->string_file(); + OutputStreamFileWriter output_stream(std::move(string_file_output_stream)); + ASSERT_TRUE(minidump_file.WriteMinidump(&output_stream, false)); + ASSERT_TRUE(output_stream.Flush()); + + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kStreamOffset = + kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + constexpr size_t kFileSize = kStreamOffset + kStreamSize; + + ASSERT_EQ(string_file.string().size(), kFileSize); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(string_file.string(), &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, kTimestamp)); + ASSERT_TRUE(directory); + + EXPECT_EQ(directory[0].StreamType, kStreamType); + EXPECT_EQ(directory[0].Location.DataSize, kStreamSize); + EXPECT_EQ(directory[0].Location.Rva, kStreamOffset); + + const uint8_t* stream_data = MinidumpWritableAtLocationDescriptor( + string_file.string(), directory[0].Location); + ASSERT_TRUE(stream_data); + + std::string expected_stream(kStreamSize, kStreamValue); + EXPECT_EQ(memcmp(stream_data, expected_stream.c_str(), kStreamSize), 0); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_handle_writer.cc b/shared/sentry/external/crashpad/minidump/minidump_handle_writer.cc new file mode 100644 index 000000000..098b40a16 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_handle_writer.cc @@ -0,0 +1,127 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_handle_writer.h" + +#include + +#include "base/logging.h" +#include "minidump/minidump_extensions.h" +#include "util/file/file_writer.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { + +MinidumpHandleDataWriter::MinidumpHandleDataWriter() + : handle_data_stream_base_(), handle_descriptors_(), strings_() { +} + +MinidumpHandleDataWriter::~MinidumpHandleDataWriter() { + for (auto& item : strings_) + delete item.second; +} + +void MinidumpHandleDataWriter::InitializeFromSnapshot( + const std::vector& handle_snapshots) { + DCHECK_EQ(state(), kStateMutable); + + DCHECK(handle_descriptors_.empty()); + // Because we RegisterRVA() on the string writer below, we preallocate and + // never resize the handle_descriptors_ vector. + handle_descriptors_.resize(handle_snapshots.size()); + for (size_t i = 0; i < handle_snapshots.size(); ++i) { + const HandleSnapshot& handle_snapshot = handle_snapshots[i]; + MINIDUMP_HANDLE_DESCRIPTOR& descriptor = handle_descriptors_[i]; + + descriptor.Handle = handle_snapshot.handle; + + if (handle_snapshot.type_name.empty()) { + descriptor.TypeNameRva = 0; + } else { + auto it = strings_.lower_bound(handle_snapshot.type_name); + internal::MinidumpUTF16StringWriter* writer; + if (it != strings_.end() && it->first == handle_snapshot.type_name) { + writer = it->second; + } else { + writer = new internal::MinidumpUTF16StringWriter(); + strings_.insert(it, std::make_pair(handle_snapshot.type_name, writer)); + writer->SetUTF8(handle_snapshot.type_name); + } + writer->RegisterRVA(&descriptor.TypeNameRva); + } + + descriptor.ObjectNameRva = 0; + descriptor.Attributes = handle_snapshot.attributes; + descriptor.GrantedAccess = handle_snapshot.granted_access; + descriptor.HandleCount = handle_snapshot.handle_count; + descriptor.PointerCount = handle_snapshot.pointer_count; + } +} + +bool MinidumpHandleDataWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpStreamWriter::Freeze()) + return false; + + handle_data_stream_base_.SizeOfHeader = sizeof(handle_data_stream_base_); + handle_data_stream_base_.SizeOfDescriptor = sizeof(handle_descriptors_[0]); + const size_t handle_count = handle_descriptors_.size(); + if (!AssignIfInRange(&handle_data_stream_base_.NumberOfDescriptors, + handle_count)) { + LOG(ERROR) << "handle_count " << handle_count << " out of range"; + return false; + } + handle_data_stream_base_.Reserved = 0; + + return true; +} + +size_t MinidumpHandleDataWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + return sizeof(handle_data_stream_base_) + + sizeof(handle_descriptors_[0]) * handle_descriptors_.size(); +} + +std::vector MinidumpHandleDataWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + + std::vector children; + for (const auto& pair : strings_) + children.push_back(pair.second); + return children; +} + +bool MinidumpHandleDataWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + WritableIoVec iov; + iov.iov_base = &handle_data_stream_base_; + iov.iov_len = sizeof(handle_data_stream_base_); + std::vector iovecs(1, iov); + + for (const auto& descriptor : handle_descriptors_) { + iov.iov_base = &descriptor; + iov.iov_len = sizeof(descriptor); + iovecs.push_back(iov); + } + + return file_writer->WriteIoVec(&iovecs); +} + +MinidumpStreamType MinidumpHandleDataWriter::StreamType() const { + return kMinidumpStreamTypeHandleData; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_handle_writer.h b/shared/sentry/external/crashpad/minidump/minidump_handle_writer.h new file mode 100644 index 000000000..348f55e81 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_handle_writer.h @@ -0,0 +1,79 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_HANDLE_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_HANDLE_WRITER_H_ + +#include +#include +#include + +#include +#include +#include + +#include "minidump/minidump_stream_writer.h" +#include "minidump/minidump_string_writer.h" +#include "minidump/minidump_writable.h" +#include "snapshot/handle_snapshot.h" + +namespace crashpad { + +//! \brief The writer for a MINIDUMP_HANDLE_DATA_STREAM stream in a minidump +//! and its contained MINIDUMP_HANDLE_DESCRIPTOR s. +//! +//! As we currently do not track any data beyond what MINIDUMP_HANDLE_DESCRIPTOR +//! supports, we only write that type of record rather than the newer +//! MINIDUMP_HANDLE_DESCRIPTOR_2. +//! +//! Note that this writer writes both the header (MINIDUMP_HANDLE_DATA_STREAM) +//! and the list of objects (MINIDUMP_HANDLE_DESCRIPTOR), which is different +//! from some of the other list writers. +class MinidumpHandleDataWriter final : public internal::MinidumpStreamWriter { + public: + MinidumpHandleDataWriter(); + + MinidumpHandleDataWriter(const MinidumpHandleDataWriter&) = delete; + MinidumpHandleDataWriter& operator=(const MinidumpHandleDataWriter&) = delete; + + ~MinidumpHandleDataWriter() override; + + //! \brief Adds a MINIDUMP_HANDLE_DESCRIPTOR for each handle in \a + //! handle_snapshot to the MINIDUMP_HANDLE_DATA_STREAM. + //! + //! \param[in] handle_snapshots The handle snapshots to use as source data. + //! + //! \note Valid in #kStateMutable. + void InitializeFromSnapshot( + const std::vector& handle_snapshots); + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpStreamWriter: + MinidumpStreamType StreamType() const override; + + private: + MINIDUMP_HANDLE_DATA_STREAM handle_data_stream_base_; + std::vector handle_descriptors_; + std::map strings_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_HANDLE_WRITER_H_ diff --git a/shared/sentry/external/crashpad/minidump/minidump_handle_writer_test.cc b/shared/sentry/external/crashpad/minidump/minidump_handle_writer_test.cc new file mode 100644 index 000000000..855be2a3c --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_handle_writer_test.cc @@ -0,0 +1,202 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_handle_writer.h" + +#include +#include +#include + +#include "base/strings/utf_string_conversions.h" +#include "gtest/gtest.h" +#include "minidump/minidump_file_writer.h" +#include "minidump/test/minidump_file_writer_test_util.h" +#include "minidump/test/minidump_string_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "util/file/string_file.h" + +namespace crashpad { +namespace test { +namespace { + +// The handle data stream is expected to be the only stream. +void GetHandleDataStream( + const std::string& file_contents, + const MINIDUMP_HANDLE_DATA_STREAM** handle_data_stream) { + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kHandleDataStreamOffset = + kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(file_contents, &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0)); + ASSERT_TRUE(directory); + + constexpr size_t kDirectoryIndex = 0; + + ASSERT_EQ(directory[kDirectoryIndex].StreamType, + kMinidumpStreamTypeHandleData); + EXPECT_EQ(directory[kDirectoryIndex].Location.Rva, kHandleDataStreamOffset); + + *handle_data_stream = + MinidumpWritableAtLocationDescriptor( + file_contents, directory[kDirectoryIndex].Location); + ASSERT_TRUE(*handle_data_stream); +} + +TEST(MinidumpHandleDataWriter, Empty) { + MinidumpFileWriter minidump_file_writer; + auto handle_data_writer = std::make_unique(); + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(handle_data_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_HANDLE_DATA_STREAM)); + + const MINIDUMP_HANDLE_DATA_STREAM* handle_data_stream = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetHandleDataStream(string_file.string(), &handle_data_stream)); + + EXPECT_EQ(handle_data_stream->NumberOfDescriptors, 0u); +} + +TEST(MinidumpHandleDataWriter, OneHandle) { + MinidumpFileWriter minidump_file_writer; + auto handle_data_writer = std::make_unique(); + + HandleSnapshot handle_snapshot; + handle_snapshot.handle = 0x1234; + handle_snapshot.type_name = "Something"; + handle_snapshot.attributes = 0x12345678; + handle_snapshot.granted_access = 0x9abcdef0; + handle_snapshot.pointer_count = 4567; + handle_snapshot.handle_count = 9876; + + std::vector snapshot; + snapshot.push_back(handle_snapshot); + + handle_data_writer->InitializeFromSnapshot(snapshot); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(handle_data_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const size_t kTypeNameStringDataLength = + (handle_snapshot.type_name.size() + 1) * sizeof(char16_t); + ASSERT_EQ(string_file.string().size(), + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_HANDLE_DATA_STREAM) + + sizeof(MINIDUMP_HANDLE_DESCRIPTOR) + sizeof(MINIDUMP_STRING) + + kTypeNameStringDataLength); + + const MINIDUMP_HANDLE_DATA_STREAM* handle_data_stream = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetHandleDataStream(string_file.string(), &handle_data_stream)); + + EXPECT_EQ(handle_data_stream->NumberOfDescriptors, 1u); + MINIDUMP_HANDLE_DESCRIPTOR handle_descriptor; + memcpy(&handle_descriptor, &handle_data_stream[1], sizeof(handle_descriptor)); + EXPECT_EQ(handle_descriptor.Handle, handle_snapshot.handle); + EXPECT_EQ(base::UTF16ToUTF8(MinidumpStringAtRVAAsString( + string_file.string(), handle_descriptor.TypeNameRva)), + handle_snapshot.type_name); + EXPECT_EQ(handle_descriptor.ObjectNameRva, 0u); + EXPECT_EQ(handle_descriptor.Attributes, handle_snapshot.attributes); + EXPECT_EQ(handle_descriptor.GrantedAccess, handle_snapshot.granted_access); + EXPECT_EQ(handle_descriptor.HandleCount, handle_snapshot.handle_count); + EXPECT_EQ(handle_descriptor.PointerCount, handle_snapshot.pointer_count); +} + +TEST(MinidumpHandleDataWriter, RepeatedTypeName) { + MinidumpFileWriter minidump_file_writer; + auto handle_data_writer = std::make_unique(); + + HandleSnapshot handle_snapshot; + handle_snapshot.handle = 0x1234; + handle_snapshot.type_name = "Something"; + handle_snapshot.attributes = 0x12345678; + handle_snapshot.granted_access = 0x9abcdef0; + handle_snapshot.pointer_count = 4567; + handle_snapshot.handle_count = 9876; + + HandleSnapshot handle_snapshot2; + handle_snapshot2.handle = 0x4321; + handle_snapshot2.type_name = "Something"; // Note: same as above. + handle_snapshot2.attributes = 0x87654321; + handle_snapshot2.granted_access = 0x0fedcba9; + handle_snapshot2.pointer_count = 7654; + handle_snapshot2.handle_count = 6789; + + std::vector snapshot; + snapshot.push_back(handle_snapshot); + snapshot.push_back(handle_snapshot2); + + handle_data_writer->InitializeFromSnapshot(snapshot); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(handle_data_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const size_t kTypeNameStringDataLength = + (handle_snapshot.type_name.size() + 1) * sizeof(char16_t); + ASSERT_EQ(string_file.string().size(), + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_HANDLE_DATA_STREAM) + + (sizeof(MINIDUMP_HANDLE_DESCRIPTOR) * 2) + + sizeof(MINIDUMP_STRING) + kTypeNameStringDataLength); + + const MINIDUMP_HANDLE_DATA_STREAM* handle_data_stream = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetHandleDataStream(string_file.string(), &handle_data_stream)); + + EXPECT_EQ(handle_data_stream->NumberOfDescriptors, 2u); + MINIDUMP_HANDLE_DESCRIPTOR handle_descriptor; + memcpy(&handle_descriptor, &handle_data_stream[1], sizeof(handle_descriptor)); + EXPECT_EQ(handle_descriptor.Handle, handle_snapshot.handle); + EXPECT_EQ(base::UTF16ToUTF8(MinidumpStringAtRVAAsString( + string_file.string(), handle_descriptor.TypeNameRva)), + handle_snapshot.type_name); + EXPECT_EQ(handle_descriptor.ObjectNameRva, 0u); + EXPECT_EQ(handle_descriptor.Attributes, handle_snapshot.attributes); + EXPECT_EQ(handle_descriptor.GrantedAccess, handle_snapshot.granted_access); + EXPECT_EQ(handle_descriptor.HandleCount, handle_snapshot.handle_count); + EXPECT_EQ(handle_descriptor.PointerCount, handle_snapshot.pointer_count); + + MINIDUMP_HANDLE_DESCRIPTOR handle_descriptor2; + memcpy(&handle_descriptor2, + reinterpret_cast(&handle_data_stream[1]) + + sizeof(MINIDUMP_HANDLE_DESCRIPTOR), + sizeof(handle_descriptor2)); + EXPECT_EQ(handle_descriptor2.Handle, handle_snapshot2.handle); + EXPECT_EQ(base::UTF16ToUTF8(MinidumpStringAtRVAAsString( + string_file.string(), handle_descriptor2.TypeNameRva)), + handle_snapshot2.type_name); + EXPECT_EQ(handle_descriptor2.ObjectNameRva, 0u); + EXPECT_EQ(handle_descriptor2.Attributes, handle_snapshot2.attributes); + EXPECT_EQ(handle_descriptor2.GrantedAccess, handle_snapshot2.granted_access); + EXPECT_EQ(handle_descriptor2.HandleCount, handle_snapshot2.handle_count); + EXPECT_EQ(handle_descriptor2.PointerCount, handle_snapshot2.pointer_count); + + EXPECT_EQ(handle_descriptor2.TypeNameRva, handle_descriptor.TypeNameRva); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_memory_info_writer.cc b/shared/sentry/external/crashpad/minidump/minidump_memory_info_writer.cc new file mode 100644 index 000000000..fe0922516 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_memory_info_writer.cc @@ -0,0 +1,85 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_memory_info_writer.h" + +#include "base/check_op.h" +#include "snapshot/memory_map_region_snapshot.h" +#include "util/file/file_writer.h" + +namespace crashpad { + +MinidumpMemoryInfoListWriter::MinidumpMemoryInfoListWriter() + : memory_info_list_base_(), items_() { +} + +MinidumpMemoryInfoListWriter::~MinidumpMemoryInfoListWriter() { +} + +void MinidumpMemoryInfoListWriter::InitializeFromSnapshot( + const std::vector& memory_map) { + DCHECK_EQ(state(), kStateMutable); + + DCHECK(items_.empty()); + for (const auto& region : memory_map) + items_.push_back(region->AsMinidumpMemoryInfo()); +} + +bool MinidumpMemoryInfoListWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpStreamWriter::Freeze()) + return false; + + memory_info_list_base_.SizeOfHeader = sizeof(MINIDUMP_MEMORY_INFO_LIST); + memory_info_list_base_.SizeOfEntry = sizeof(MINIDUMP_MEMORY_INFO); + memory_info_list_base_.NumberOfEntries = items_.size(); + + return true; +} + +size_t MinidumpMemoryInfoListWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + return sizeof(memory_info_list_base_) + sizeof(items_[0]) * items_.size(); +} + +std::vector +MinidumpMemoryInfoListWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + return std::vector(); +} + +bool MinidumpMemoryInfoListWriter::WriteObject( + FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + WritableIoVec iov; + iov.iov_base = &memory_info_list_base_; + iov.iov_len = sizeof(memory_info_list_base_); + std::vector iovecs(1, iov); + + for (const auto& minidump_memory_info : items_) { + iov.iov_base = &minidump_memory_info; + iov.iov_len = sizeof(minidump_memory_info); + iovecs.push_back(iov); + } + + return file_writer->WriteIoVec(&iovecs); +} + +MinidumpStreamType MinidumpMemoryInfoListWriter::StreamType() const { + return kMinidumpStreamTypeMemoryInfoList; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_memory_info_writer.h b/shared/sentry/external/crashpad/minidump/minidump_memory_info_writer.h new file mode 100644 index 000000000..a4159a7d3 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_memory_info_writer.h @@ -0,0 +1,74 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_MEMORY_INFO_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_MEMORY_INFO_WRITER_H_ + +#include +#include +#include +#include + +#include + +#include "minidump/minidump_stream_writer.h" +#include "minidump/minidump_writable.h" + +namespace crashpad { + +class MemoryMapRegionSnapshot; +class MinidumpContextWriter; +class MinidumpMemoryListWriter; +class MinidumpMemoryWriter; + +//! \brief The writer for a MINIDUMP_MEMORY_INFO_LIST stream in a minidump file, +//! containing a list of MINIDUMP_MEMORY_INFO objects. +class MinidumpMemoryInfoListWriter final + : public internal::MinidumpStreamWriter { + public: + MinidumpMemoryInfoListWriter(); + + MinidumpMemoryInfoListWriter(const MinidumpMemoryInfoListWriter&) = delete; + MinidumpMemoryInfoListWriter& operator=(const MinidumpMemoryInfoListWriter&) = + delete; + + ~MinidumpMemoryInfoListWriter() override; + + //! \brief Initializes a MINIDUMP_MEMORY_INFO_LIST based on \a memory_map. + //! + //! \param[in] memory_map The vector of memory map region snapshots to use as + //! source data. + //! + //! \note Valid in #kStateMutable. + void InitializeFromSnapshot( + const std::vector& memory_map); + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpStreamWriter: + MinidumpStreamType StreamType() const override; + + private: + MINIDUMP_MEMORY_INFO_LIST memory_info_list_base_; + std::vector items_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_MEMORY_INFO_WRITER_H_ diff --git a/shared/sentry/external/crashpad/minidump/minidump_memory_info_writer_test.cc b/shared/sentry/external/crashpad/minidump/minidump_memory_info_writer_test.cc new file mode 100644 index 000000000..e45ea5733 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_memory_info_writer_test.cc @@ -0,0 +1,141 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_memory_info_writer.h" + +#include +#include +#include + +#include "gtest/gtest.h" +#include "minidump/minidump_file_writer.h" +#include "minidump/test/minidump_file_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "snapshot/test/test_memory_map_region_snapshot.h" +#include "util/file/string_file.h" + +namespace crashpad { +namespace test { +namespace { + +// The memory info list is expected to be the only stream. +void GetMemoryInfoListStream( + const std::string& file_contents, + const MINIDUMP_MEMORY_INFO_LIST** memory_info_list) { + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kMemoryInfoListStreamOffset = + kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(file_contents, &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0)); + ASSERT_TRUE(directory); + + constexpr size_t kDirectoryIndex = 0; + + ASSERT_EQ(directory[kDirectoryIndex].StreamType, + kMinidumpStreamTypeMemoryInfoList); + EXPECT_EQ(directory[kDirectoryIndex].Location.Rva, + kMemoryInfoListStreamOffset); + + *memory_info_list = + MinidumpWritableAtLocationDescriptor( + file_contents, directory[kDirectoryIndex].Location); + ASSERT_TRUE(*memory_info_list); +} + +TEST(MinidumpMemoryInfoWriter, Empty) { + MinidumpFileWriter minidump_file_writer; + auto memory_info_list_writer = + std::make_unique(); + ASSERT_TRUE( + minidump_file_writer.AddStream(std::move(memory_info_list_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_MEMORY_INFO_LIST)); + + const MINIDUMP_MEMORY_INFO_LIST* memory_info_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetMemoryInfoListStream(string_file.string(), &memory_info_list)); + + uint64_t number_of_entries; + memcpy(&number_of_entries, + &memory_info_list->NumberOfEntries, + sizeof(number_of_entries)); + EXPECT_EQ(number_of_entries, 0u); +} + +TEST(MinidumpMemoryInfoWriter, OneRegion) { + MinidumpFileWriter minidump_file_writer; + auto memory_info_list_writer = + std::make_unique(); + + auto memory_map_region = std::make_unique(); + + MINIDUMP_MEMORY_INFO mmi; + mmi.BaseAddress = 0x12340000; + mmi.AllocationBase = 0x12000000; + mmi.AllocationProtect = PAGE_READWRITE; + mmi.__alignment1 = 0; + mmi.RegionSize = 0x6000; + mmi.State = MEM_COMMIT; + mmi.Protect = PAGE_NOACCESS; + mmi.Type = MEM_PRIVATE; + mmi.__alignment2 = 0; + memory_map_region->SetMindumpMemoryInfo(mmi); + + std::vector memory_map; + memory_map.push_back(memory_map_region.get()); + memory_info_list_writer->InitializeFromSnapshot(memory_map); + + ASSERT_TRUE( + minidump_file_writer.AddStream(std::move(memory_info_list_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_MEMORY_INFO_LIST) + + sizeof(MINIDUMP_MEMORY_INFO)); + + const MINIDUMP_MEMORY_INFO_LIST* memory_info_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetMemoryInfoListStream(string_file.string(), &memory_info_list)); + + uint64_t number_of_entries; + memcpy(&number_of_entries, + &memory_info_list->NumberOfEntries, + sizeof(number_of_entries)); + EXPECT_EQ(number_of_entries, 1u); + + MINIDUMP_MEMORY_INFO memory_info; + memcpy(&memory_info, &memory_info_list[1], sizeof(memory_info)); + EXPECT_EQ(memory_info.BaseAddress, mmi.BaseAddress); + EXPECT_EQ(memory_info.AllocationBase, mmi.AllocationBase); + EXPECT_EQ(memory_info.AllocationProtect, mmi.AllocationProtect); + EXPECT_EQ(memory_info.RegionSize, mmi.RegionSize); + EXPECT_EQ(memory_info.State, mmi.State); + EXPECT_EQ(memory_info.Protect, mmi.Protect); + EXPECT_EQ(memory_info.Type, mmi.Type); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_memory_writer.cc b/shared/sentry/external/crashpad/minidump/minidump_memory_writer.cc new file mode 100644 index 000000000..a3b1f7521 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_memory_writer.cc @@ -0,0 +1,311 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_memory_writer.h" + +#include +#include +#include + +#include "base/auto_reset.h" +#include "base/logging.h" +#include "util/file/file_writer.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { + +SnapshotMinidumpMemoryWriter::SnapshotMinidumpMemoryWriter( + const MemorySnapshot* memory_snapshot) + : internal::MinidumpWritable(), + MemorySnapshot::Delegate(), + memory_descriptor_(), + registered_memory_descriptors_(), + memory_snapshot_(memory_snapshot), + file_writer_(nullptr) {} + +SnapshotMinidumpMemoryWriter::~SnapshotMinidumpMemoryWriter() {} + +bool SnapshotMinidumpMemoryWriter::MemorySnapshotDelegateRead(void* data, + size_t size) { + DCHECK_EQ(state(), kStateWritable); + DCHECK_EQ(size, UnderlyingSnapshot()->Size()); + return file_writer_->Write(data, size); +} + +bool SnapshotMinidumpMemoryWriter::WriteObject( + FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + DCHECK(!file_writer_); + + base::AutoReset file_writer_reset(&file_writer_, + file_writer); + + // This will result in MemorySnapshotDelegateRead() being called. + if (!memory_snapshot_->Read(this)) { + // If the Read() fails (perhaps because the process' memory map has changed + // since it the range was captured), write an empty block of memory. It + // would be nice to instead not include this memory, but at this point in + // the writing process, it would be difficult to amend the minidump's + // structure. See https://crashpad.chromium.org/234 for background. + std::vector empty(memory_snapshot_->Size(), 0xfe); + MemorySnapshotDelegateRead(empty.data(), empty.size()); + } + + return true; +} + +const MINIDUMP_MEMORY_DESCRIPTOR* +SnapshotMinidumpMemoryWriter::MinidumpMemoryDescriptor() const { + DCHECK_EQ(state(), kStateWritable); + + return &memory_descriptor_; +} + +void SnapshotMinidumpMemoryWriter::RegisterMemoryDescriptor( + MINIDUMP_MEMORY_DESCRIPTOR* memory_descriptor) { + DCHECK_LE(state(), kStateFrozen); + + registered_memory_descriptors_.push_back(memory_descriptor); + RegisterLocationDescriptor(&memory_descriptor->Memory); +} + +bool SnapshotMinidumpMemoryWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + RegisterMemoryDescriptor(&memory_descriptor_); + + return true; +} + +size_t SnapshotMinidumpMemoryWriter::Alignment() { + DCHECK_GE(state(), kStateFrozen); + + return 16; +} + +size_t SnapshotMinidumpMemoryWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return UnderlyingSnapshot()->Size(); +} + +bool SnapshotMinidumpMemoryWriter::WillWriteAtOffsetImpl(FileOffset offset) { + DCHECK_EQ(state(), kStateFrozen); + + // There will always be at least one registered descriptor, the one for this + // object’s own memory_descriptor_ field. + DCHECK_GE(registered_memory_descriptors_.size(), 1u); + + uint64_t base_address = UnderlyingSnapshot()->Address(); + decltype(registered_memory_descriptors_[0]->StartOfMemoryRange) local_address; + if (!AssignIfInRange(&local_address, base_address)) { + LOG(ERROR) << "base_address " << base_address << " out of range"; + return false; + } + + for (MINIDUMP_MEMORY_DESCRIPTOR* memory_descriptor : + registered_memory_descriptors_) { + memory_descriptor->StartOfMemoryRange = local_address; + } + + return MinidumpWritable::WillWriteAtOffsetImpl(offset); +} + +internal::MinidumpWritable::Phase SnapshotMinidumpMemoryWriter::WritePhase() { + // Memory dumps are large and are unlikely to be consumed in their entirety. + // Data accesses are expected to be sparse and sporadic, and are expected to + // occur after all of the other structural and informational data from the + // minidump file has been read. Put memory dumps at the end of the minidump + // file to improve spatial locality. + return kPhaseLate; +} + +MinidumpMemoryListWriter::MinidumpMemoryListWriter() + : MinidumpStreamWriter(), + non_owned_memory_writers_(), + children_(), + snapshots_created_during_merge_(), + all_memory_writers_(), + memory_list_base_() {} + +MinidumpMemoryListWriter::~MinidumpMemoryListWriter() { +} + +void MinidumpMemoryListWriter::AddFromSnapshot( + const std::vector& memory_snapshots) { + DCHECK_EQ(state(), kStateMutable); + + for (const MemorySnapshot* memory_snapshot : memory_snapshots) { + std::unique_ptr memory( + new SnapshotMinidumpMemoryWriter(memory_snapshot)); + AddMemory(std::move(memory)); + } +} + +void MinidumpMemoryListWriter::AddMemory( + std::unique_ptr memory_writer) { + DCHECK_EQ(state(), kStateMutable); + + children_.push_back(std::move(memory_writer)); +} + +void MinidumpMemoryListWriter::AddNonOwnedMemory( + SnapshotMinidumpMemoryWriter* memory_writer) { + DCHECK_EQ(state(), kStateMutable); + + non_owned_memory_writers_.push_back(memory_writer); +} + +void MinidumpMemoryListWriter::CoalesceOwnedMemory() { + DropRangesThatOverlapNonOwned(); + + if (children_.empty()) + return; + + std::sort(children_.begin(), + children_.end(), + [](const std::unique_ptr& a_ptr, + const std::unique_ptr& b_ptr) { + const MemorySnapshot* a = a_ptr->UnderlyingSnapshot(); + const MemorySnapshot* b = b_ptr->UnderlyingSnapshot(); + if (a->Address() == b->Address()) { + return a->Size() < b->Size(); + } + return a->Address() < b->Address(); + }); + + // Remove any empty ranges. + children_.erase( + std::remove_if(children_.begin(), + children_.end(), + [](const auto& snapshot) { + return snapshot->UnderlyingSnapshot()->Size() == 0; + }), + children_.end()); + + std::vector> all_merged; + all_merged.push_back(std::move(children_.front())); + for (size_t i = 1; i < children_.size(); ++i) { + SnapshotMinidumpMemoryWriter* top = all_merged.back().get(); + auto& child = children_[i]; + if (!DetermineMergedRange( + child->UnderlyingSnapshot(), top->UnderlyingSnapshot(), nullptr)) { + // If it doesn't overlap with the current range, push it. + all_merged.push_back(std::move(child)); + } else { + // Otherwise, merge and update the current element. + std::unique_ptr merged( + top->UnderlyingSnapshot()->MergeWithOtherSnapshot( + child->UnderlyingSnapshot())); + top->SetSnapshot(merged.get()); + snapshots_created_during_merge_.push_back(std::move(merged)); + } + } + std::swap(children_, all_merged); +} + +bool MinidumpMemoryListWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + CoalesceOwnedMemory(); + + std::copy(non_owned_memory_writers_.begin(), + non_owned_memory_writers_.end(), + std::back_inserter(all_memory_writers_)); + for (const auto& ptr : children_) + all_memory_writers_.push_back(ptr.get()); + + if (!MinidumpStreamWriter::Freeze()) { + return false; + } + + size_t memory_region_count = all_memory_writers_.size(); + CHECK_LE(children_.size(), memory_region_count); + + if (!AssignIfInRange(&memory_list_base_.NumberOfMemoryRanges, + memory_region_count)) { + LOG(ERROR) << "memory_region_count " << memory_region_count + << " out of range"; + return false; + } + + return true; +} + +size_t MinidumpMemoryListWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + DCHECK_LE(children_.size(), all_memory_writers_.size()); + + return sizeof(memory_list_base_) + + all_memory_writers_.size() * sizeof(MINIDUMP_MEMORY_DESCRIPTOR); +} + +std::vector MinidumpMemoryListWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + DCHECK_LE(children_.size(), all_memory_writers_.size()); + + std::vector children; + for (const auto& child : children_) { + children.push_back(child.get()); + } + + return children; +} + +bool MinidumpMemoryListWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + WritableIoVec iov; + iov.iov_base = &memory_list_base_; + iov.iov_len = sizeof(memory_list_base_); + std::vector iovecs(1, iov); + + for (const SnapshotMinidumpMemoryWriter* memory_writer : + all_memory_writers_) { + iov.iov_base = memory_writer->MinidumpMemoryDescriptor(); + iov.iov_len = sizeof(MINIDUMP_MEMORY_DESCRIPTOR); + iovecs.push_back(iov); + } + + return file_writer->WriteIoVec(&iovecs); +} + +MinidumpStreamType MinidumpMemoryListWriter::StreamType() const { + return kMinidumpStreamTypeMemoryList; +} + +void MinidumpMemoryListWriter::DropRangesThatOverlapNonOwned() { + std::vector> non_overlapping; + non_overlapping.reserve(children_.size()); + for (auto& child_ptr : children_) { + bool overlaps = false; + for (const auto* non_owned : non_owned_memory_writers_) { + if (DetermineMergedRange(child_ptr->UnderlyingSnapshot(), + non_owned->UnderlyingSnapshot(), + nullptr)) { + overlaps = true; + break; + } + } + if (!overlaps) + non_overlapping.push_back(std::move(child_ptr)); + } + std::swap(children_, non_overlapping); +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_memory_writer.h b/shared/sentry/external/crashpad/minidump/minidump_memory_writer.h new file mode 100644 index 000000000..ef18de24b --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_memory_writer.h @@ -0,0 +1,210 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_MEMORY_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_MEMORY_WRITER_H_ + +#include +#include +#include +#include + +#include +#include + +#include "minidump/minidump_stream_writer.h" +#include "minidump/minidump_writable.h" +#include "snapshot/memory_snapshot.h" +#include "util/file/file_io.h" + +namespace crashpad { + +//! \brief The base class for writers of memory ranges pointed to by +//! MINIDUMP_MEMORY_DESCRIPTOR objects in a minidump file. +class SnapshotMinidumpMemoryWriter : public internal::MinidumpWritable, + public MemorySnapshot::Delegate { + public: + explicit SnapshotMinidumpMemoryWriter(const MemorySnapshot* memory_snapshot); + + SnapshotMinidumpMemoryWriter(const SnapshotMinidumpMemoryWriter&) = delete; + SnapshotMinidumpMemoryWriter& operator=(const SnapshotMinidumpMemoryWriter&) = + delete; + + ~SnapshotMinidumpMemoryWriter() override; + + //! \brief Returns a MINIDUMP_MEMORY_DESCRIPTOR referencing the data that this + //! object writes. + //! + //! This method is expected to be called by a MinidumpMemoryListWriter in + //! order to obtain a MINIDUMP_MEMORY_DESCRIPTOR to include in its list. + //! + //! \note Valid in #kStateWritable. + const MINIDUMP_MEMORY_DESCRIPTOR* MinidumpMemoryDescriptor() const; + + //! \brief Registers a memory descriptor as one that should point to the + //! object on which this method is called. + //! + //! This method is expected to be called by objects of other classes, when + //! those other classes have their own memory descriptors that need to point + //! to memory ranges within a minidump file. MinidumpThreadWriter is one such + //! class. This method is public for this reason, otherwise it would suffice + //! to be private. + //! + //! \note Valid in #kStateFrozen or any preceding state. + void RegisterMemoryDescriptor(MINIDUMP_MEMORY_DESCRIPTOR* memory_descriptor); + + //! \brief Sets the underlying memory snapshot. Does not take ownership of \a + //! memory_snapshot. + void SetSnapshot(const MemorySnapshot* memory_snapshot) { + memory_snapshot_ = memory_snapshot; + } + + private: + friend class MinidumpMemoryListWriter; + + // MemorySnapshot::Delegate: + bool MemorySnapshotDelegateRead(void* data, size_t size) override; + + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() final; + bool WriteObject(FileWriterInterface* file_writer) override; + + //! \brief Returns the object’s desired byte-boundary alignment. + //! + //! Memory regions are aligned to a 16-byte boundary. The actual alignment + //! requirements of any data within the memory region are unknown, and may be + //! more or less strict than this depending on the platform. + //! + //! \return `16`. + //! + //! \note Valid in #kStateFrozen or any subsequent state. + size_t Alignment() override; + + bool WillWriteAtOffsetImpl(FileOffset offset) override; + + //! \brief Returns the object’s desired write phase. + //! + //! Memory regions are written at the end of minidump files, because it is + //! expected that unlike most other data in a minidump file, the contents of + //! memory regions will be accessed sparsely. + //! + //! \return #kPhaseLate. + //! + //! \note Valid in any state. + Phase WritePhase() final; + + //! \brief Gets the underlying memory snapshot that the memory writer will + //! write to the minidump. + const MemorySnapshot* UnderlyingSnapshot() const { return memory_snapshot_; } + + MINIDUMP_MEMORY_DESCRIPTOR memory_descriptor_; + + // weak + std::vector registered_memory_descriptors_; + const MemorySnapshot* memory_snapshot_; + FileWriterInterface* file_writer_; +}; + +//! \brief The writer for a MINIDUMP_MEMORY_LIST stream in a minidump file, +//! containing a list of MINIDUMP_MEMORY_DESCRIPTOR objects. +class MinidumpMemoryListWriter final : public internal::MinidumpStreamWriter { + public: + MinidumpMemoryListWriter(); + + MinidumpMemoryListWriter(const MinidumpMemoryListWriter&) = delete; + MinidumpMemoryListWriter& operator=(const MinidumpMemoryListWriter&) = delete; + + ~MinidumpMemoryListWriter() override; + + //! \brief Adds a concrete initialized SnapshotMinidumpMemoryWriter for each + //! memory snapshot in \a memory_snapshots to the MINIDUMP_MEMORY_LIST. + //! + //! Memory snapshots are added in the fashion of AddMemory(). + //! + //! \param[in] memory_snapshots The memory snapshots to use as source data. + //! + //! \note Valid in #kStateMutable. + void AddFromSnapshot( + const std::vector& memory_snapshots); + + //! \brief Adds a SnapshotMinidumpMemoryWriter to the MINIDUMP_MEMORY_LIST. + //! + //! This object takes ownership of \a memory_writer and becomes its parent in + //! the overall tree of internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void AddMemory(std::unique_ptr memory_writer); + + //! \brief Adds a SnapshotMinidumpMemoryWriter that’s a child of another + //! internal::MinidumpWritable object to the MINIDUMP_MEMORY_LIST. + //! + //! \a memory_writer does not become a child of this object, but the + //! MINIDUMP_MEMORY_LIST will still contain a MINIDUMP_MEMORY_DESCRIPTOR for + //! it. \a memory_writer must be a child of another object in the + //! internal::MinidumpWritable tree. + //! + //! This method exists to be called by objects that have their own + //! SnapshotMinidumpMemoryWriter children but wish for them to also appear in + //! the minidump file’s MINIDUMP_MEMORY_LIST. MinidumpThreadWriter, which has + //! a SnapshotMinidumpMemoryWriter for thread stack memory, is an example. + //! + //! \note Valid in #kStateMutable. + void AddNonOwnedMemory(SnapshotMinidumpMemoryWriter* memory_writer); + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpStreamWriter: + MinidumpStreamType StreamType() const override; + + //! \brief Merges any overlapping and abutting memory ranges that were added + //! via AddFromSnapshot() and AddMemory() into single entries. + //! + //! This is expected to be called once just before writing, generally from + //! Freeze(). + //! + //! This function has the side-effect of merging owned ranges, dropping any + //! owned ranges that overlap with non-owned ranges, removing empty ranges, + //! and sorting all ranges by address. + //! + //! Per its name, this coalesces owned memory, however, this is not a complete + //! solution for ensuring that no overlapping memory ranges are emitted in the + //! minidump. In particular, if AddNonOwnedMemory() is used to add multiple + //! overlapping ranges, then overlapping ranges will still be emitted to the + //! minidump. Currently, AddNonOwnedMemory() is used only for adding thread + //! stacks, so overlapping shouldn't be a problem in practice. For more + //! details see https://crashpad.chromium.org/bug/61 and + //! https://crrev.com/c/374539. + void CoalesceOwnedMemory(); + + private: + //! \brief Drops children_ ranges that overlap non_owned_memory_writers_. + void DropRangesThatOverlapNonOwned(); + + std::vector non_owned_memory_writers_; // weak + std::vector> children_; + std::vector> + snapshots_created_during_merge_; + std::vector all_memory_writers_; // weak + MINIDUMP_MEMORY_LIST memory_list_base_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_MEMORY_WRITER_H_ diff --git a/shared/sentry/external/crashpad/minidump/minidump_memory_writer_test.cc b/shared/sentry/external/crashpad/minidump/minidump_memory_writer_test.cc new file mode 100644 index 000000000..0ca0652d0 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_memory_writer_test.cc @@ -0,0 +1,692 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_memory_writer.h" + +#include +#include + +#include "base/format_macros.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_file_writer.h" +#include "minidump/test/minidump_file_writer_test_util.h" +#include "minidump/test/minidump_memory_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "snapshot/test/test_memory_snapshot.h" +#include "util/file/string_file.h" + +namespace crashpad { +namespace test { +namespace { + +constexpr MinidumpStreamType kBogusStreamType = + static_cast(1234); + +// expected_streams is the expected number of streams in the file. The memory +// list must be the last stream. If there is another stream, it must come first, +// have stream type kBogusStreamType, and have zero-length data. +void GetMemoryListStream(const std::string& file_contents, + const MINIDUMP_MEMORY_LIST** memory_list, + const uint32_t expected_streams) { + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + const size_t kMemoryListStreamOffset = + kDirectoryOffset + expected_streams * sizeof(MINIDUMP_DIRECTORY); + const size_t kMemoryDescriptorsOffset = + kMemoryListStreamOffset + sizeof(MINIDUMP_MEMORY_LIST); + + ASSERT_GE(file_contents.size(), kMemoryDescriptorsOffset); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(file_contents, &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, expected_streams, 0)); + ASSERT_TRUE(directory); + + size_t directory_index = 0; + if (expected_streams > 1) { + ASSERT_EQ(directory[directory_index].StreamType, kBogusStreamType); + ASSERT_EQ(directory[directory_index].Location.DataSize, 0u); + ASSERT_EQ(directory[directory_index].Location.Rva, kMemoryListStreamOffset); + ++directory_index; + } + + ASSERT_EQ(directory[directory_index].StreamType, + kMinidumpStreamTypeMemoryList); + EXPECT_EQ(directory[directory_index].Location.Rva, kMemoryListStreamOffset); + + *memory_list = MinidumpWritableAtLocationDescriptor( + file_contents, directory[directory_index].Location); + ASSERT_TRUE(memory_list); +} + +TEST(MinidumpMemoryWriter, EmptyMemoryList) { + MinidumpFileWriter minidump_file_writer; + auto memory_list_writer = std::make_unique(); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(memory_list_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_MEMORY_LIST)); + + const MINIDUMP_MEMORY_LIST* memory_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetMemoryListStream(string_file.string(), &memory_list, 1)); + + EXPECT_EQ(memory_list->NumberOfMemoryRanges, 0u); +} + +TEST(MinidumpMemoryWriter, OneMemoryRegion) { + MinidumpFileWriter minidump_file_writer; + auto memory_list_writer = std::make_unique(); + + constexpr uint64_t kBaseAddress = 0xfedcba9876543210; + constexpr size_t kSize = 0x1000; + constexpr uint8_t kValue = 'm'; + + auto memory_writer = + std::make_unique(kBaseAddress, kSize, kValue); + memory_list_writer->AddMemory(std::move(memory_writer)); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(memory_list_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MEMORY_LIST* memory_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetMemoryListStream(string_file.string(), &memory_list, 1)); + + MINIDUMP_MEMORY_DESCRIPTOR expected; + expected.StartOfMemoryRange = kBaseAddress; + expected.Memory.DataSize = kSize; + expected.Memory.Rva = + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_MEMORY_LIST) + + memory_list->NumberOfMemoryRanges * sizeof(MINIDUMP_MEMORY_DESCRIPTOR); + ExpectMinidumpMemoryDescriptorAndContents(&expected, + &memory_list->MemoryRanges[0], + string_file.string(), + kValue, + true); +} + +TEST(MinidumpMemoryWriter, TwoMemoryRegions) { + MinidumpFileWriter minidump_file_writer; + auto memory_list_writer = std::make_unique(); + + constexpr uint64_t kBaseAddress0 = 0xc0ffee; + constexpr size_t kSize0 = 0x0100; + constexpr uint8_t kValue0 = '6'; + constexpr uint64_t kBaseAddress1 = 0xfac00fac; + constexpr size_t kSize1 = 0x0200; + constexpr uint8_t kValue1 = '!'; + + auto memory_writer_0 = std::make_unique( + kBaseAddress0, kSize0, kValue0); + memory_list_writer->AddMemory(std::move(memory_writer_0)); + auto memory_writer_1 = std::make_unique( + kBaseAddress1, kSize1, kValue1); + memory_list_writer->AddMemory(std::move(memory_writer_1)); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(memory_list_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MEMORY_LIST* memory_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetMemoryListStream(string_file.string(), &memory_list, 1)); + + EXPECT_EQ(memory_list->NumberOfMemoryRanges, 2u); + + MINIDUMP_MEMORY_DESCRIPTOR expected; + + { + SCOPED_TRACE("region 0"); + + expected.StartOfMemoryRange = kBaseAddress0; + expected.Memory.DataSize = kSize0; + expected.Memory.Rva = + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_MEMORY_LIST) + + memory_list->NumberOfMemoryRanges * sizeof(MINIDUMP_MEMORY_DESCRIPTOR); + ExpectMinidumpMemoryDescriptorAndContents(&expected, + &memory_list->MemoryRanges[0], + string_file.string(), + kValue0, + false); + } + + { + SCOPED_TRACE("region 1"); + + expected.StartOfMemoryRange = kBaseAddress1; + expected.Memory.DataSize = kSize1; + expected.Memory.Rva = memory_list->MemoryRanges[0].Memory.Rva + + memory_list->MemoryRanges[0].Memory.DataSize; + ExpectMinidumpMemoryDescriptorAndContents(&expected, + &memory_list->MemoryRanges[1], + string_file.string(), + kValue1, + true); + } +} + +TEST(MinidumpMemoryWriter, RegionReadFails) { + MinidumpFileWriter minidump_file_writer; + auto memory_list_writer = std::make_unique(); + + constexpr uint64_t kBaseAddress = 0xfedcba9876543210; + constexpr size_t kSize = 0x1000; + constexpr uint8_t kValue = 'm'; + + auto memory_writer = + std::make_unique(kBaseAddress, kSize, kValue); + + // Make the read of that memory fail. + memory_writer->SetShouldFailRead(true); + + memory_list_writer->AddMemory(std::move(memory_writer)); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(memory_list_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MEMORY_LIST* memory_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetMemoryListStream(string_file.string(), &memory_list, 1)); + + MINIDUMP_MEMORY_DESCRIPTOR expected; + expected.StartOfMemoryRange = kBaseAddress; + expected.Memory.DataSize = kSize; + expected.Memory.Rva = + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_MEMORY_LIST) + + memory_list->NumberOfMemoryRanges * sizeof(MINIDUMP_MEMORY_DESCRIPTOR); + ExpectMinidumpMemoryDescriptorAndContents( + &expected, + &memory_list->MemoryRanges[0], + string_file.string(), + 0xfe, // Not kValue ('m'), but the value that the implementation inserts + // if memory is unreadable. + true); +} + +class TestMemoryStream final : public internal::MinidumpStreamWriter { + public: + TestMemoryStream(uint64_t base_address, size_t size, uint8_t value) + : MinidumpStreamWriter(), memory_(base_address, size, value) {} + + TestMemoryStream(const TestMemoryStream&) = delete; + TestMemoryStream& operator=(const TestMemoryStream&) = delete; + + ~TestMemoryStream() override {} + + TestMinidumpMemoryWriter* memory() { + return &memory_; + } + + // MinidumpStreamWriter: + MinidumpStreamType StreamType() const override { + return kBogusStreamType; + } + + protected: + // MinidumpWritable: + size_t SizeOfObject() override { + EXPECT_GE(state(), kStateFrozen); + return 0; + } + + std::vector Children() override { + EXPECT_GE(state(), kStateFrozen); + std::vector children(1, memory()); + return children; + } + + bool WriteObject(FileWriterInterface* file_writer) override { + EXPECT_EQ(state(), kStateWritable); + return true; + } + + private: + TestMinidumpMemoryWriter memory_; +}; + +TEST(MinidumpMemoryWriter, ExtraMemory) { + // This tests MinidumpMemoryListWriter::AddExtraMemory(). That method adds + // a MinidumpMemoryWriter to the MinidumpMemoryListWriter without making the + // memory writer a child of the memory list writer. + MinidumpFileWriter minidump_file_writer; + + constexpr uint64_t kBaseAddress0 = 0x1000; + constexpr size_t kSize0 = 0x0400; + constexpr uint8_t kValue0 = '1'; + auto test_memory_stream = + std::make_unique(kBaseAddress0, kSize0, kValue0); + + auto memory_list_writer = std::make_unique(); + memory_list_writer->AddNonOwnedMemory(test_memory_stream->memory()); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(test_memory_stream))); + + constexpr uint64_t kBaseAddress1 = 0x2000; + constexpr size_t kSize1 = 0x0400; + constexpr uint8_t kValue1 = 'm'; + + auto memory_writer = std::make_unique( + kBaseAddress1, kSize1, kValue1); + memory_list_writer->AddMemory(std::move(memory_writer)); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(memory_list_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MEMORY_LIST* memory_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetMemoryListStream(string_file.string(), &memory_list, 2)); + + EXPECT_EQ(memory_list->NumberOfMemoryRanges, 2u); + + MINIDUMP_MEMORY_DESCRIPTOR expected; + + { + SCOPED_TRACE("region 0"); + + expected.StartOfMemoryRange = kBaseAddress0; + expected.Memory.DataSize = kSize0; + expected.Memory.Rva = + sizeof(MINIDUMP_HEADER) + 2 * sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_MEMORY_LIST) + + memory_list->NumberOfMemoryRanges * sizeof(MINIDUMP_MEMORY_DESCRIPTOR); + ExpectMinidumpMemoryDescriptorAndContents(&expected, + &memory_list->MemoryRanges[0], + string_file.string(), + kValue0, + false); + } + + { + SCOPED_TRACE("region 1"); + + expected.StartOfMemoryRange = kBaseAddress1; + expected.Memory.DataSize = kSize1; + expected.Memory.Rva = memory_list->MemoryRanges[0].Memory.Rva + + memory_list->MemoryRanges[0].Memory.DataSize; + ExpectMinidumpMemoryDescriptorAndContents(&expected, + &memory_list->MemoryRanges[1], + string_file.string(), + kValue1, + true); + } +} + +TEST(MinidumpMemoryWriter, AddFromSnapshot) { + MINIDUMP_MEMORY_DESCRIPTOR expect_memory_descriptors[3] = {}; + uint8_t values[std::size(expect_memory_descriptors)] = {}; + + expect_memory_descriptors[0].StartOfMemoryRange = 0; + expect_memory_descriptors[0].Memory.DataSize = 0x1000; + values[0] = 0x01; + + expect_memory_descriptors[1].StartOfMemoryRange = 0x2000; + expect_memory_descriptors[1].Memory.DataSize = 0x2000; + values[1] = 0xf4; + + expect_memory_descriptors[2].StartOfMemoryRange = 0x7654321000000000; + expect_memory_descriptors[2].Memory.DataSize = 0x800; + values[2] = 0xa9; + + std::vector> memory_snapshots_owner; + std::vector memory_snapshots; + for (size_t index = 0; index < std::size(expect_memory_descriptors); + ++index) { + memory_snapshots_owner.push_back(std::make_unique()); + TestMemorySnapshot* memory_snapshot = memory_snapshots_owner.back().get(); + memory_snapshot->SetAddress( + expect_memory_descriptors[index].StartOfMemoryRange); + memory_snapshot->SetSize(expect_memory_descriptors[index].Memory.DataSize); + memory_snapshot->SetValue(values[index]); + memory_snapshots.push_back(memory_snapshot); + } + + auto memory_list_writer = std::make_unique(); + memory_list_writer->AddFromSnapshot(memory_snapshots); + + MinidumpFileWriter minidump_file_writer; + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(memory_list_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MEMORY_LIST* memory_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetMemoryListStream(string_file.string(), &memory_list, 1)); + + ASSERT_EQ(memory_list->NumberOfMemoryRanges, 3u); + + for (size_t index = 0; index < memory_list->NumberOfMemoryRanges; ++index) { + SCOPED_TRACE(base::StringPrintf("index %" PRIuS, index)); + ExpectMinidumpMemoryDescriptorAndContents( + &expect_memory_descriptors[index], + &memory_list->MemoryRanges[index], + string_file.string(), + values[index], + index == memory_list->NumberOfMemoryRanges - 1); + } +} + +TEST(MinidumpMemoryWriter, CoalesceExplicitMultiple) { + MINIDUMP_MEMORY_DESCRIPTOR expect_memory_descriptors[4] = {}; + uint8_t values[std::size(expect_memory_descriptors)] = {}; + + expect_memory_descriptors[0].StartOfMemoryRange = 0; + expect_memory_descriptors[0].Memory.DataSize = 1000; + values[0] = 0x01; + + expect_memory_descriptors[1].StartOfMemoryRange = 10000; + expect_memory_descriptors[1].Memory.DataSize = 2000; + values[1] = 0xf4; + + expect_memory_descriptors[2].StartOfMemoryRange = 0x1111111111111111; + expect_memory_descriptors[2].Memory.DataSize = 1024; + values[2] = 0x99; + + expect_memory_descriptors[3].StartOfMemoryRange = 0xfedcba9876543210; + expect_memory_descriptors[3].Memory.DataSize = 1024; + values[3] = 0x88; + + struct { + uint64_t base; + size_t size; + uint8_t value; + } snapshots_to_add[] = { + // Various overlapping. + {0, 500, 0x01}, + {0, 500, 0x01}, + {250, 500, 0x01}, + {600, 400, 0x01}, + + // Empty removed. + {0, 0, 0xbb}, + {300, 0, 0xcc}, + {1000, 0, 0xdd}, + {12000, 0, 0xee}, + + // Abutting. + {10000, 500, 0xf4}, + {10500, 500, 0xf4}, + {11000, 1000, 0xf4}, + + // Large base addresses. + { 0xfedcba9876543210, 1024, 0x88 }, + { 0x1111111111111111, 1024, 0x99 }, + }; + + std::vector> memory_snapshots_owner; + std::vector memory_snapshots; + for (const auto& to_add : snapshots_to_add) { + memory_snapshots_owner.push_back(std::make_unique()); + TestMemorySnapshot* memory_snapshot = memory_snapshots_owner.back().get(); + memory_snapshot->SetAddress(to_add.base); + memory_snapshot->SetSize(to_add.size); + memory_snapshot->SetValue(to_add.value); + memory_snapshots.push_back(memory_snapshot); + } + + auto memory_list_writer = std::make_unique(); + memory_list_writer->AddFromSnapshot(memory_snapshots); + + MinidumpFileWriter minidump_file_writer; + minidump_file_writer.AddStream(std::move(memory_list_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MEMORY_LIST* memory_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetMemoryListStream(string_file.string(), &memory_list, 1)); + + ASSERT_EQ(4u, memory_list->NumberOfMemoryRanges); + + for (size_t index = 0; index < memory_list->NumberOfMemoryRanges; ++index) { + SCOPED_TRACE(base::StringPrintf("index %" PRIuS, index)); + ExpectMinidumpMemoryDescriptorAndContents( + &expect_memory_descriptors[index], + &memory_list->MemoryRanges[index], + string_file.string(), + values[index], + index == memory_list->NumberOfMemoryRanges - 1); + } +} + +struct TestRange { + TestRange(uint64_t base, size_t size) : base(base), size(size) {} + + uint64_t base; + size_t size; +}; + +// Parses a string spec to build a list of ranges suitable for CoalesceTest(). +std::vector ParseCoalesceSpec(const char* spec) { + std::vector result; + enum { kNone, kSpace, kDot } state = kNone; + const char* range_started_at = nullptr; + for (const char* p = spec;; ++p) { + EXPECT_TRUE(*p == ' ' || *p == '.' || *p == 0); + if (*p == ' ' || *p == 0) { + if (state == kDot) { + result.push_back( + TestRange(range_started_at - spec, p - range_started_at)); + } + state = kSpace; + range_started_at = nullptr; + } else if (*p == '.') { + if (state != kDot) { + range_started_at = p; + state = kDot; + } + } + + if (*p == 0) + break; + } + + return result; +} + +TEST(MinidumpMemoryWriter, CoalesceSpecHelperParse) { + const auto empty = ParseCoalesceSpec(""); + ASSERT_EQ(empty.size(), 0u); + + const auto a = ParseCoalesceSpec("..."); + ASSERT_EQ(a.size(), 1u); + EXPECT_EQ(a[0].base, 0u); + EXPECT_EQ(a[0].size, 3u); + + const auto b = ParseCoalesceSpec(" ..."); + ASSERT_EQ(b.size(), 1u); + EXPECT_EQ(b[0].base, 2u); + EXPECT_EQ(b[0].size, 3u); + + const auto c = ParseCoalesceSpec(" ... "); + ASSERT_EQ(c.size(), 1u); + EXPECT_EQ(c[0].base, 2u); + EXPECT_EQ(c[0].size, 3u); + + const auto d = ParseCoalesceSpec(" ... ...."); + ASSERT_EQ(d.size(), 2u); + EXPECT_EQ(d[0].base, 2u); + EXPECT_EQ(d[0].size, 3u); + EXPECT_EQ(d[1].base, 7u); + EXPECT_EQ(d[1].size, 4u); + + const auto e = ParseCoalesceSpec(" ... ...... ... "); + ASSERT_EQ(e.size(), 3u); + EXPECT_EQ(e[0].base, 2u); + EXPECT_EQ(e[0].size, 3u); + EXPECT_EQ(e[1].base, 7u); + EXPECT_EQ(e[1].size, 6u); + EXPECT_EQ(e[2].base, 14u); + EXPECT_EQ(e[2].size, 3u); +} + +constexpr uint8_t kMemoryValue = 0xcd; + +// Builds a coalesce test out of specs of ' ' and '.'. Tests that when the two +// ranges are added and coalesced, the result is equal to expected. +void CoalesceTest(const char* r1_spec, + const char* r2_spec, + const char* expected_spec) { + auto r1 = ParseCoalesceSpec(r1_spec); + auto r2 = ParseCoalesceSpec(r2_spec); + auto expected = ParseCoalesceSpec(expected_spec); + + std::vector expect_memory_descriptors; + for (const auto& range : expected) { + MINIDUMP_MEMORY_DESCRIPTOR mmd = {}; + mmd.StartOfMemoryRange = range.base; + mmd.Memory.DataSize = static_cast(range.size); + expect_memory_descriptors.push_back(mmd); + } + + std::vector> memory_snapshots_owner; + std::vector memory_snapshots; + + const auto add_test_memory_snapshots = [&memory_snapshots_owner, + &memory_snapshots]( + std::vector ranges) { + for (const auto& r : ranges) { + memory_snapshots_owner.push_back(std::make_unique()); + TestMemorySnapshot* memory_snapshot = memory_snapshots_owner.back().get(); + memory_snapshot->SetAddress(r.base); + memory_snapshot->SetSize(r.size); + memory_snapshot->SetValue(kMemoryValue); + memory_snapshots.push_back(memory_snapshot); + } + }; + add_test_memory_snapshots(r1); + add_test_memory_snapshots(r2); + + auto memory_list_writer = std::make_unique(); + memory_list_writer->AddFromSnapshot(memory_snapshots); + + MinidumpFileWriter minidump_file_writer; + minidump_file_writer.AddStream(std::move(memory_list_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MEMORY_LIST* memory_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetMemoryListStream(string_file.string(), &memory_list, 1)); + + ASSERT_EQ(expected.size(), memory_list->NumberOfMemoryRanges); + + for (size_t index = 0; index < memory_list->NumberOfMemoryRanges; ++index) { + SCOPED_TRACE(base::StringPrintf("index %" PRIuS, index)); + ExpectMinidumpMemoryDescriptorAndContents( + &expect_memory_descriptors[index], + &memory_list->MemoryRanges[index], + string_file.string(), + kMemoryValue, + index == memory_list->NumberOfMemoryRanges - 1); + } +} + +TEST(MinidumpMemoryWriter, CoalescePairsVariousCases) { + // clang-format off + + CoalesceTest(" .........", + " .......", + /* result */ " .............."); + + CoalesceTest(" .......", + " .........", + /* result */ " .............."); + + CoalesceTest(" ...", + " .........", + /* result */ " ........."); + + CoalesceTest(" .........", + " ......", + /* result */ " ........."); + + CoalesceTest(" ...", + " ........", + /* result */ " ........"); + + CoalesceTest(" ........", + " ...", + /* result */ " ........"); + + CoalesceTest(" ...", + " ........", + /* result */ " ........"); + + CoalesceTest(" ........", + " ...", + /* result */ " ........"); + + CoalesceTest(" ... ", + " ...", + /* result */ " ... ..."); + + CoalesceTest(" ...", + " ... ", + /* result */ " ... ..."); + + CoalesceTest("...", + ".....", + /* result */ "....."); + + CoalesceTest("...", + " ..", + /* result */ "....."); + + CoalesceTest(" .....", + " ..", + /* result */ " ......."); + + CoalesceTest(" ......... ......", + " .......", + /* result */ " .................."); + + CoalesceTest(" .......", + " ......... ......", + /* result */ " .................."); + + CoalesceTest(" .....", + " ......... ......", + /* result */ " ......... ......"); + + CoalesceTest(" ......... ....... .... .", + " ......... ...... ....", + /* result */ " .......................... ......."); + + // clang-format on +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_misc_info_writer.cc b/shared/sentry/external/crashpad/minidump/minidump_misc_info_writer.cc new file mode 100644 index 000000000..78847c119 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_misc_info_writer.cc @@ -0,0 +1,415 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_misc_info_writer.h" + +#include +#include + +#include "base/check_op.h" +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" +#include "minidump/minidump_writer_util.h" +#include "package.h" +#include "snapshot/process_snapshot.h" +#include "snapshot/system_snapshot.h" +#include "util/file/file_writer.h" +#include "util/numeric/in_range_cast.h" +#include "util/numeric/safe_assignment.h" + +#if BUILDFLAG(IS_MAC) +#include +#elif BUILDFLAG(IS_ANDROID) +#include +#endif + +namespace crashpad { +namespace { + +uint32_t TimevalToRoundedSeconds(const timeval& tv) { + uint32_t seconds = + InRangeCast(tv.tv_sec, std::numeric_limits::max()); + constexpr int kMicrosecondsPerSecond = static_cast(1E6); + if (tv.tv_usec >= kMicrosecondsPerSecond / 2 && + seconds != std::numeric_limits::max()) { + ++seconds; + } + return seconds; +} + +// For MINIDUMP_MISC_INFO_4::BuildString. dbghelp only places OS version +// information here, but if a machine description is also available, this is the +// only reasonable place in a minidump file to put it. +std::string BuildString(const SystemSnapshot* system_snapshot) { + std::string os_version_full = system_snapshot->OSVersionFull(); + std::string machine_description = system_snapshot->MachineDescription(); + if (!os_version_full.empty()) { + if (!machine_description.empty()) { + return base::StringPrintf( + "%s; %s", os_version_full.c_str(), machine_description.c_str()); + } + return os_version_full; + } + return machine_description; +} + +#if BUILDFLAG(IS_MAC) +// Converts the value of the __MAC_OS_X_VERSION_MIN_REQUIRED or +// __MAC_OS_X_VERSION_MAX_ALLOWED macro from to a number +// identifying the macOS version that it represents, in the same format used by +// MacOSVersionNumber(). For example, with an argument of __MAC_10_15, this +// function will return 10'15'00, which is incidentally the same as __MAC_10_15. +// With an argument of __MAC_10_9, this function will return 10'09'00, different +// from __MAC_10_9, which is 10'9'0. +int AvailabilityVersionToMacOSVersionNumber(int availability) { +#if __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_10 + DCHECK_GE(availability, 10'0'0); + + // Until __MAC_10_10, the format is major * 1'0'0 + minor * 1'0 + bugfix. + if (availability >= 10'0'0 && availability <= 10'9'9) { + int minor = (availability / 1'0) % 1'0; + int bugfix = availability % 1'0; + return 10'00'00 + minor * 1'00 + bugfix; + } +#endif + + // Since __MAC_10_10, the format is major * 1'00'00 + minor * 1'00 + bugfix. + DCHECK_GE(availability, 10'10'00); + DCHECK_LE(availability, 99'99'99); + + return availability; +} +#endif // BUILDFLAG(IS_MAC) + +} // namespace + +namespace internal { + +// For MINIDUMP_MISC_INFO_4::DbgBldStr. dbghelp produces strings like +// “dbghelp.i386,6.3.9600.16520†and “dbghelp.amd64,6.3.9600.16520â€. Mimic that +// format, and add the OS that wrote the minidump along with any relevant +// platform-specific data describing the compilation environment. +std::string MinidumpMiscInfoDebugBuildString() { + // Caution: the minidump file format only has room for 39 UTF-16 code units + // plus a UTF-16 NUL terminator. Don’t let strings get longer than this, or + // they will be truncated and a message will be logged. +#if BUILDFLAG(IS_MAC) + static constexpr char kOS[] = "mac"; +#elif BUILDFLAG(IS_IOS) + static constexpr char kOS[] = "ios"; +#elif BUILDFLAG(IS_ANDROID) + static constexpr char kOS[] = "android"; +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) + static constexpr char kOS[] = "linux"; +#elif BUILDFLAG(IS_WIN) + static constexpr char kOS[] = "win"; +#elif BUILDFLAG(IS_FUCHSIA) + static constexpr char kOS[] = "fuchsia"; +#else +#error define kOS for this operating system +#endif + +#if defined(ARCH_CPU_X86) + static constexpr char kCPU[] = "i386"; +#elif defined(ARCH_CPU_X86_64) + static constexpr char kCPU[] = "amd64"; +#elif defined(ARCH_CPU_ARMEL) + static constexpr char kCPU[] = "arm"; +#elif defined(ARCH_CPU_ARM64) + static constexpr char kCPU[] = "arm64"; +#elif defined(ARCH_CPU_MIPSEL) + static constexpr char kCPU[] = "mips"; +#elif defined(ARCH_CPU_MIPS64EL) + static constexpr char kCPU[] = "mips64"; +#else +#error define kCPU for this CPU +#endif + + std::string debug_build_string = base::StringPrintf("%s.%s,%s,%s", + PACKAGE_TARNAME, + kCPU, + PACKAGE_VERSION, + kOS); + +#if BUILDFLAG(IS_MAC) + debug_build_string += base::StringPrintf( + ",%d,%d", + AvailabilityVersionToMacOSVersionNumber(__MAC_OS_X_VERSION_MIN_REQUIRED), + AvailabilityVersionToMacOSVersionNumber(__MAC_OS_X_VERSION_MAX_ALLOWED)); +#elif BUILDFLAG(IS_ANDROID) + debug_build_string += base::StringPrintf(",%d", __ANDROID_API__); +#endif + + return debug_build_string; +} + +} // namespace internal + +MinidumpMiscInfoWriter::MinidumpMiscInfoWriter() + : MinidumpStreamWriter(), misc_info_(), has_xstate_data_(false) { +} + +MinidumpMiscInfoWriter::~MinidumpMiscInfoWriter() { +} + +void MinidumpMiscInfoWriter::InitializeFromSnapshot( + const ProcessSnapshot* process_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK_EQ(misc_info_.Flags1, 0u); + + SetProcessID(InRangeCast(process_snapshot->ProcessID(), 0)); + + const SystemSnapshot* system_snapshot = process_snapshot->System(); + + uint64_t current_hz; + uint64_t max_hz; + system_snapshot->CPUFrequency(¤t_hz, &max_hz); + constexpr uint32_t kHzPerMHz = static_cast(1E6); + SetProcessorPowerInfo( + InRangeCast(current_hz / kHzPerMHz, + std::numeric_limits::max()), + InRangeCast(max_hz / kHzPerMHz, + std::numeric_limits::max()), + 0, + 0, + 0); + + timeval start_time; + process_snapshot->ProcessStartTime(&start_time); + + timeval user_time; + timeval system_time; + process_snapshot->ProcessCPUTimes(&user_time, &system_time); + + // Round the resource usage fields to the nearest second, because the minidump + // format only has one-second resolution. The start_time field is truncated + // instead of rounded so that the process uptime is reflected more accurately + // when the start time is compared to the snapshot time in the + // MINIDUMP_HEADER, which is also truncated, not rounded. + uint32_t user_seconds = TimevalToRoundedSeconds(user_time); + uint32_t system_seconds = TimevalToRoundedSeconds(system_time); + + SetProcessTimes(start_time.tv_sec, user_seconds, system_seconds); + + // This determines the system’s time zone, which may be different than the + // process’ notion of the time zone. + SystemSnapshot::DaylightSavingTimeStatus dst_status; + int standard_offset_seconds; + int daylight_offset_seconds; + std::string standard_name; + std::string daylight_name; + system_snapshot->TimeZone(&dst_status, + &standard_offset_seconds, + &daylight_offset_seconds, + &standard_name, + &daylight_name); + + // standard_offset_seconds is seconds east of UTC, but the minidump file wants + // minutes west of UTC. daylight_offset_seconds is also seconds east of UTC, + // but the minidump file wants minutes west of the standard offset. The empty + // ({}) arguments are for the transition times in and out of daylight saving + // time. These are not determined because no API exists to do so, and the + // transition times may vary from year to year. + SetTimeZone(dst_status, + standard_offset_seconds / -60, + standard_name, + {}, + 0, + daylight_name, + {}, + (standard_offset_seconds - daylight_offset_seconds) / 60); + + SetBuildString(BuildString(system_snapshot), + internal::MinidumpMiscInfoDebugBuildString()); +} + +void MinidumpMiscInfoWriter::SetProcessID(uint32_t process_id) { + DCHECK_EQ(state(), kStateMutable); + + misc_info_.ProcessId = process_id; + misc_info_.Flags1 |= MINIDUMP_MISC1_PROCESS_ID; +} + +void MinidumpMiscInfoWriter::SetProcessTimes(time_t process_create_time, + uint32_t process_user_time, + uint32_t process_kernel_time) { + DCHECK_EQ(state(), kStateMutable); + + internal::MinidumpWriterUtil::AssignTimeT(&misc_info_.ProcessCreateTime, + process_create_time); + + misc_info_.ProcessUserTime = process_user_time; + misc_info_.ProcessKernelTime = process_kernel_time; + misc_info_.Flags1 |= MINIDUMP_MISC1_PROCESS_TIMES; +} + +void MinidumpMiscInfoWriter::SetProcessorPowerInfo( + uint32_t processor_max_mhz, + uint32_t processor_current_mhz, + uint32_t processor_mhz_limit, + uint32_t processor_max_idle_state, + uint32_t processor_current_idle_state) { + DCHECK_EQ(state(), kStateMutable); + + misc_info_.ProcessorMaxMhz = processor_max_mhz; + misc_info_.ProcessorCurrentMhz = processor_current_mhz; + misc_info_.ProcessorMhzLimit = processor_mhz_limit; + misc_info_.ProcessorMaxIdleState = processor_max_idle_state; + misc_info_.ProcessorCurrentIdleState = processor_current_idle_state; + misc_info_.Flags1 |= MINIDUMP_MISC1_PROCESSOR_POWER_INFO; +} + +void MinidumpMiscInfoWriter::SetProcessIntegrityLevel( + uint32_t process_integrity_level) { + DCHECK_EQ(state(), kStateMutable); + + misc_info_.ProcessIntegrityLevel = process_integrity_level; + misc_info_.Flags1 |= MINIDUMP_MISC3_PROCESS_INTEGRITY; +} + +void MinidumpMiscInfoWriter::SetProcessExecuteFlags( + uint32_t process_execute_flags) { + DCHECK_EQ(state(), kStateMutable); + + misc_info_.ProcessExecuteFlags = process_execute_flags; + misc_info_.Flags1 |= MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS; +} + +void MinidumpMiscInfoWriter::SetProtectedProcess(uint32_t protected_process) { + DCHECK_EQ(state(), kStateMutable); + + misc_info_.ProtectedProcess = protected_process; + misc_info_.Flags1 |= MINIDUMP_MISC3_PROTECTED_PROCESS; +} + +void MinidumpMiscInfoWriter::SetTimeZone(uint32_t time_zone_id, + int32_t bias, + const std::string& standard_name, + const SYSTEMTIME& standard_date, + int32_t standard_bias, + const std::string& daylight_name, + const SYSTEMTIME& daylight_date, + int32_t daylight_bias) { + DCHECK_EQ(state(), kStateMutable); + + misc_info_.TimeZoneId = time_zone_id; + misc_info_.TimeZone.Bias = bias; + + internal::MinidumpWriterUtil::AssignUTF8ToUTF16( + AsU16CStr(misc_info_.TimeZone.StandardName), + std::size(misc_info_.TimeZone.StandardName), + standard_name); + + misc_info_.TimeZone.StandardDate = standard_date; + misc_info_.TimeZone.StandardBias = standard_bias; + + internal::MinidumpWriterUtil::AssignUTF8ToUTF16( + AsU16CStr(misc_info_.TimeZone.DaylightName), + std::size(misc_info_.TimeZone.DaylightName), + daylight_name); + + misc_info_.TimeZone.DaylightDate = daylight_date; + misc_info_.TimeZone.DaylightBias = daylight_bias; + + misc_info_.Flags1 |= MINIDUMP_MISC3_TIMEZONE; +} + +void MinidumpMiscInfoWriter::SetBuildString( + const std::string& build_string, + const std::string& debug_build_string) { + DCHECK_EQ(state(), kStateMutable); + + misc_info_.Flags1 |= MINIDUMP_MISC4_BUILDSTRING; + + internal::MinidumpWriterUtil::AssignUTF8ToUTF16( + AsU16CStr(misc_info_.BuildString), + std::size(misc_info_.BuildString), + build_string); + internal::MinidumpWriterUtil::AssignUTF8ToUTF16( + AsU16CStr(misc_info_.DbgBldStr), + std::size(misc_info_.DbgBldStr), + debug_build_string); +} + +void MinidumpMiscInfoWriter::SetXStateData( + const XSTATE_CONFIG_FEATURE_MSC_INFO& xstate_data) { + DCHECK_EQ(state(), kStateMutable); + + misc_info_.XStateData = xstate_data; + has_xstate_data_ = true; +} + +void MinidumpMiscInfoWriter::SetProcessCookie(uint32_t process_cookie) { + DCHECK_EQ(state(), kStateMutable); + + misc_info_.ProcessCookie = process_cookie; + misc_info_.Flags1 |= MINIDUMP_MISC5_PROCESS_COOKIE; +} + +bool MinidumpMiscInfoWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpStreamWriter::Freeze()) { + return false; + } + + size_t size = CalculateSizeOfObjectFromFlags(); + if (!AssignIfInRange(&misc_info_.SizeOfInfo, size)) { + LOG(ERROR) << "size " << size << " out of range"; + return false; + } + + return true; +} + +size_t MinidumpMiscInfoWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return CalculateSizeOfObjectFromFlags(); +} + +bool MinidumpMiscInfoWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + return file_writer->Write(&misc_info_, CalculateSizeOfObjectFromFlags()); +} + +MinidumpStreamType MinidumpMiscInfoWriter::StreamType() const { + return kMinidumpStreamTypeMiscInfo; +} + +size_t MinidumpMiscInfoWriter::CalculateSizeOfObjectFromFlags() const { + DCHECK_GE(state(), kStateFrozen); + + if (has_xstate_data_ || (misc_info_.Flags1 & MINIDUMP_MISC5_PROCESS_COOKIE)) { + return sizeof(MINIDUMP_MISC_INFO_5); + } + if (misc_info_.Flags1 & MINIDUMP_MISC4_BUILDSTRING) { + return sizeof(MINIDUMP_MISC_INFO_4); + } + if (misc_info_.Flags1 & + (MINIDUMP_MISC3_PROCESS_INTEGRITY | MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS | + MINIDUMP_MISC3_TIMEZONE | MINIDUMP_MISC3_PROTECTED_PROCESS)) { + return sizeof(MINIDUMP_MISC_INFO_3); + } + if (misc_info_.Flags1 & MINIDUMP_MISC1_PROCESSOR_POWER_INFO) { + return sizeof(MINIDUMP_MISC_INFO_2); + } + return sizeof(MINIDUMP_MISC_INFO); +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_misc_info_writer.h b/shared/sentry/external/crashpad/minidump/minidump_misc_info_writer.h new file mode 100644 index 000000000..9d89fe648 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_misc_info_writer.h @@ -0,0 +1,165 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_MISC_INFO_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_MISC_INFO_WRITER_H_ + +#include +#include +#include +#include +#include +#include + +#include + +#include "build/build_config.h" +#include "minidump/minidump_stream_writer.h" +#include "minidump/minidump_writable.h" + +namespace crashpad { + +class ProcessSnapshot; + +namespace internal { + +//! \brief Returns the string to set in MINIDUMP_MISC_INFO_4::DbgBldStr. +//! +//! dbghelp produces strings like `"dbghelp.i386,6.3.9600.16520"` and +//! `"dbghelp.amd64,6.3.9600.16520"`. This function mimics that format, and adds +//! the OS that wrote the minidump along with any relevant platform-specific +//! data describing the compilation environment. +//! +//! This function is an implementation detail of +//! MinidumpMiscInfoWriter::InitializeFromSnapshot() and is only exposed for +//! testing purposes. +std::string MinidumpMiscInfoDebugBuildString(); + +} // namespace internal + +//! \brief The writer for a stream in the MINIDUMP_MISC_INFO family in a +//! minidump file. +//! +//! The actual stream written will be a MINIDUMP_MISC_INFO, +//! MINIDUMP_MISC_INFO_2, MINIDUMP_MISC_INFO_3, MINIDUMP_MISC_INFO_4, or +//! MINIDUMP_MISC_INFO_5 stream. Later versions of MINIDUMP_MISC_INFO are +//! supersets of earlier versions. The earliest version that supports all of the +//! information that an object of this class contains will be used. +class MinidumpMiscInfoWriter final : public internal::MinidumpStreamWriter { + public: + MinidumpMiscInfoWriter(); + + MinidumpMiscInfoWriter(const MinidumpMiscInfoWriter&) = delete; + MinidumpMiscInfoWriter& operator=(const MinidumpMiscInfoWriter&) = delete; + + ~MinidumpMiscInfoWriter() override; + + //! \brief Initializes MINIDUMP_MISC_INFO_N based on \a process_snapshot. + //! + //! \param[in] process_snapshot The process snapshot to use as source data. + //! + //! \note Valid in #kStateMutable. No mutator methods may be called before + //! this method, and it is not normally necessary to call any mutator + //! methods after this method. + void InitializeFromSnapshot(const ProcessSnapshot* process_snapshot); + + //! \brief Sets the field referenced by #MINIDUMP_MISC1_PROCESS_ID. + void SetProcessID(uint32_t process_id); + + //! \brief Sets the fields referenced by #MINIDUMP_MISC1_PROCESS_TIMES. + void SetProcessTimes(time_t process_create_time, + uint32_t process_user_time, + uint32_t process_kernel_time); + + //! \brief Sets the fields referenced by #MINIDUMP_MISC1_PROCESSOR_POWER_INFO. + void SetProcessorPowerInfo(uint32_t processor_max_mhz, + uint32_t processor_current_mhz, + uint32_t processor_mhz_limit, + uint32_t processor_max_idle_state, + uint32_t processor_current_idle_state); + + //! \brief Sets the field referenced by #MINIDUMP_MISC3_PROCESS_INTEGRITY. + void SetProcessIntegrityLevel(uint32_t process_integrity_level); + + //! \brief Sets the field referenced by #MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS. + void SetProcessExecuteFlags(uint32_t process_execute_flags); + + //! \brief Sets the field referenced by #MINIDUMP_MISC3_PROTECTED_PROCESS. + void SetProtectedProcess(uint32_t protected_process); + + //! \brief Sets the fields referenced by #MINIDUMP_MISC3_TIMEZONE. + void SetTimeZone(uint32_t time_zone_id, + int32_t bias, + const std::string& standard_name, + const SYSTEMTIME& standard_date, + int32_t standard_bias, + const std::string& daylight_name, + const SYSTEMTIME& daylight_date, + int32_t daylight_bias); + + //! \brief Sets the fields referenced by #MINIDUMP_MISC4_BUILDSTRING. + void SetBuildString(const std::string& build_string, + const std::string& debug_build_string); + + // TODO(mark): Provide a better interface than this. Don’t force callers to + // build their own XSTATE_CONFIG_FEATURE_MSC_INFO structure. + // + //! \brief Sets MINIDUMP_MISC_INFO_5::XStateData. + void SetXStateData(const XSTATE_CONFIG_FEATURE_MSC_INFO& xstate_data); + + //! \brief Sets the field referenced by #MINIDUMP_MISC5_PROCESS_COOKIE. + void SetProcessCookie(uint32_t process_cookie); + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + bool WriteObject(FileWriterInterface* file_writer) override; + MinidumpStreamType StreamType() const override; + + private: + //! \brief Returns the size of the object to be written based on + //! MINIDUMP_MISC_INFO_N::Flags1. + //! + //! The smallest defined structure type in the MINIDUMP_MISC_INFO family that + //! can hold all of the data that has been populated will be used. + size_t CalculateSizeOfObjectFromFlags() const; + + MINIDUMP_MISC_INFO_N misc_info_; + bool has_xstate_data_; +}; + +//! \brief Conversion functions from a native UTF16 C-string to a char16_t +//! C-string. No-op where the native UTF16 string is std::u16string. +#if defined(WCHAR_T_IS_UTF16) || DOXYGEN +inline const char16_t* AsU16CStr(const wchar_t* str) { + return reinterpret_cast(str); +} + +inline char16_t* AsU16CStr(wchar_t* str) { + return reinterpret_cast(str); +} +#else +inline const char16_t* AsU16CStr(const char16_t* str) { + return str; +} + +inline char16_t* AsU16CStr(char16_t* str) { + return str; +} +#endif + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_MISC_INFO_WRITER_H_ diff --git a/shared/sentry/external/crashpad/minidump/minidump_misc_info_writer_test.cc b/shared/sentry/external/crashpad/minidump/minidump_misc_info_writer_test.cc new file mode 100644 index 000000000..c98341418 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_misc_info_writer_test.cc @@ -0,0 +1,819 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_misc_info_writer.h" + +#include + +#include +#include +#include + +#include "base/format_macros.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "gtest/gtest.h" +#include "minidump/minidump_file_writer.h" +#include "minidump/test/minidump_file_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "snapshot/test/test_process_snapshot.h" +#include "snapshot/test/test_system_snapshot.h" +#include "util/file/string_file.h" +#include "util/stdlib/strlcpy.h" + +namespace crashpad { +namespace test { +namespace { + +template +void GetMiscInfoStream(const std::string& file_contents, const T** misc_info) { + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kMiscInfoStreamOffset = + kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + constexpr size_t kMiscInfoStreamSize = sizeof(T); + constexpr size_t kFileSize = kMiscInfoStreamOffset + kMiscInfoStreamSize; + + ASSERT_EQ(file_contents.size(), kFileSize); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(file_contents, &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0)); + ASSERT_TRUE(directory); + + ASSERT_EQ(directory[0].StreamType, kMinidumpStreamTypeMiscInfo); + EXPECT_EQ(directory[0].Location.Rva, kMiscInfoStreamOffset); + + *misc_info = MinidumpWritableAtLocationDescriptor(file_contents, + directory[0].Location); + ASSERT_TRUE(misc_info); +} + +void ExpectNULPaddedString16Equal(const char16_t* expected, + const char16_t* observed, + size_t size) { + std::u16string expected_string(expected, size); + std::u16string observed_string(observed, size); + EXPECT_EQ(observed_string, expected_string); +} + +void ExpectSystemTimeEqual(const SYSTEMTIME* expected, + const SYSTEMTIME* observed) { + EXPECT_EQ(observed->wYear, expected->wYear); + EXPECT_EQ(observed->wMonth, expected->wMonth); + EXPECT_EQ(observed->wDayOfWeek, expected->wDayOfWeek); + EXPECT_EQ(observed->wDay, expected->wDay); + EXPECT_EQ(observed->wHour, expected->wHour); + EXPECT_EQ(observed->wMinute, expected->wMinute); + EXPECT_EQ(observed->wSecond, expected->wSecond); + EXPECT_EQ(observed->wMilliseconds, expected->wMilliseconds); +} + +template +void ExpectMiscInfoEqual(const T* expected, const T* observed); + +template <> +void ExpectMiscInfoEqual( + const MINIDUMP_MISC_INFO* expected, + const MINIDUMP_MISC_INFO* observed) { + EXPECT_EQ(observed->Flags1, expected->Flags1); + EXPECT_EQ(observed->ProcessId, expected->ProcessId); + EXPECT_EQ(observed->ProcessCreateTime, expected->ProcessCreateTime); + EXPECT_EQ(observed->ProcessUserTime, expected->ProcessUserTime); + EXPECT_EQ(observed->ProcessKernelTime, expected->ProcessKernelTime); +} + +template <> +void ExpectMiscInfoEqual( + const MINIDUMP_MISC_INFO_2* expected, + const MINIDUMP_MISC_INFO_2* observed) { + ExpectMiscInfoEqual( + reinterpret_cast(expected), + reinterpret_cast(observed)); + EXPECT_EQ(observed->ProcessorMaxMhz, expected->ProcessorMaxMhz); + EXPECT_EQ(observed->ProcessorCurrentMhz, expected->ProcessorCurrentMhz); + EXPECT_EQ(observed->ProcessorMhzLimit, expected->ProcessorMhzLimit); + EXPECT_EQ(observed->ProcessorMaxIdleState, expected->ProcessorMaxIdleState); + EXPECT_EQ(observed->ProcessorCurrentIdleState, + expected->ProcessorCurrentIdleState); +} + +template <> +void ExpectMiscInfoEqual( + const MINIDUMP_MISC_INFO_3* expected, + const MINIDUMP_MISC_INFO_3* observed) { + ExpectMiscInfoEqual( + reinterpret_cast(expected), + reinterpret_cast(observed)); + EXPECT_EQ(observed->ProcessIntegrityLevel, expected->ProcessIntegrityLevel); + EXPECT_EQ(observed->ProcessExecuteFlags, expected->ProcessExecuteFlags); + EXPECT_EQ(observed->ProtectedProcess, expected->ProtectedProcess); + EXPECT_EQ(observed->TimeZoneId, expected->TimeZoneId); + EXPECT_EQ(observed->TimeZone.Bias, expected->TimeZone.Bias); + { + SCOPED_TRACE("Standard"); + ExpectNULPaddedString16Equal(AsU16CStr(expected->TimeZone.StandardName), + AsU16CStr(observed->TimeZone.StandardName), + std::size(expected->TimeZone.StandardName)); + ExpectSystemTimeEqual(&expected->TimeZone.StandardDate, + &observed->TimeZone.StandardDate); + EXPECT_EQ(observed->TimeZone.StandardBias, expected->TimeZone.StandardBias); + } + { + SCOPED_TRACE("Daylight"); + ExpectNULPaddedString16Equal(AsU16CStr(expected->TimeZone.DaylightName), + AsU16CStr(observed->TimeZone.DaylightName), + std::size(expected->TimeZone.DaylightName)); + ExpectSystemTimeEqual(&expected->TimeZone.DaylightDate, + &observed->TimeZone.DaylightDate); + EXPECT_EQ(observed->TimeZone.DaylightBias, expected->TimeZone.DaylightBias); + } +} + +template <> +void ExpectMiscInfoEqual( + const MINIDUMP_MISC_INFO_4* expected, + const MINIDUMP_MISC_INFO_4* observed) { + ExpectMiscInfoEqual( + reinterpret_cast(expected), + reinterpret_cast(observed)); + { + SCOPED_TRACE("BuildString"); + ExpectNULPaddedString16Equal(AsU16CStr(expected->BuildString), + AsU16CStr(observed->BuildString), + std::size(expected->BuildString)); + } + { + SCOPED_TRACE("DbgBldStr"); + ExpectNULPaddedString16Equal(AsU16CStr(expected->DbgBldStr), + AsU16CStr(observed->DbgBldStr), + std::size(expected->DbgBldStr)); + } +} + +template <> +void ExpectMiscInfoEqual( + const MINIDUMP_MISC_INFO_5* expected, + const MINIDUMP_MISC_INFO_5* observed) { + ExpectMiscInfoEqual( + reinterpret_cast(expected), + reinterpret_cast(observed)); + + MINIDUMP_MISC_INFO_5 expected_misc_info, observed_misc_info; + memcpy(&expected_misc_info, expected, sizeof(expected_misc_info)); + memcpy(&observed_misc_info, observed, sizeof(observed_misc_info)); + + EXPECT_EQ(observed_misc_info.XStateData.SizeOfInfo, + expected_misc_info.XStateData.SizeOfInfo); + EXPECT_EQ(observed_misc_info.XStateData.ContextSize, + expected_misc_info.XStateData.ContextSize); + EXPECT_EQ(observed_misc_info.XStateData.EnabledFeatures, + expected_misc_info.XStateData.EnabledFeatures); + for (size_t feature_index = 0; + feature_index < std::size(observed_misc_info.XStateData.Features); + ++feature_index) { + SCOPED_TRACE(base::StringPrintf("feature_index %" PRIuS, feature_index)); + EXPECT_EQ(observed_misc_info.XStateData.Features[feature_index].Offset, + expected_misc_info.XStateData.Features[feature_index].Offset); + EXPECT_EQ(observed_misc_info.XStateData.Features[feature_index].Size, + expected_misc_info.XStateData.Features[feature_index].Size); + } + EXPECT_EQ(observed_misc_info.ProcessCookie, expected_misc_info.ProcessCookie); +} + +// Bypass restrictions on conversion of compile-time constants (added for +// https://crbug.com/1189439). +const char* Crbug1189439Cast(const char str[]) { + return str; +} + +TEST(MinidumpMiscInfoWriter, Empty) { + MinidumpFileWriter minidump_file_writer; + auto misc_info_writer = std::make_unique(); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(misc_info_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MISC_INFO* observed = nullptr; + ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed)); + + MINIDUMP_MISC_INFO expected = {}; + + ExpectMiscInfoEqual(&expected, observed); +} + +TEST(MinidumpMiscInfoWriter, ProcessId) { + MinidumpFileWriter minidump_file_writer; + auto misc_info_writer = std::make_unique(); + + constexpr uint32_t kProcessId = 12345; + + misc_info_writer->SetProcessID(kProcessId); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(misc_info_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MISC_INFO* observed = nullptr; + ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed)); + + MINIDUMP_MISC_INFO expected = {}; + expected.Flags1 = MINIDUMP_MISC1_PROCESS_ID; + expected.ProcessId = kProcessId; + + ExpectMiscInfoEqual(&expected, observed); +} + +TEST(MinidumpMiscInfoWriter, ProcessTimes) { + MinidumpFileWriter minidump_file_writer; + auto misc_info_writer = std::make_unique(); + + constexpr time_t kProcessCreateTime = 0x15252f00; + constexpr uint32_t kProcessUserTime = 10; + constexpr uint32_t kProcessKernelTime = 5; + + misc_info_writer->SetProcessTimes( + kProcessCreateTime, kProcessUserTime, kProcessKernelTime); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(misc_info_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MISC_INFO* observed = nullptr; + ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed)); + + MINIDUMP_MISC_INFO expected = {}; + expected.Flags1 = MINIDUMP_MISC1_PROCESS_TIMES; + expected.ProcessCreateTime = kProcessCreateTime; + expected.ProcessUserTime = kProcessUserTime; + expected.ProcessKernelTime = kProcessKernelTime; + + ExpectMiscInfoEqual(&expected, observed); +} + +TEST(MinidumpMiscInfoWriter, ProcessorPowerInfo) { + MinidumpFileWriter minidump_file_writer; + auto misc_info_writer = std::make_unique(); + + constexpr uint32_t kProcessorMaxMhz = 2800; + constexpr uint32_t kProcessorCurrentMhz = 2300; + constexpr uint32_t kProcessorMhzLimit = 3300; + constexpr uint32_t kProcessorMaxIdleState = 5; + constexpr uint32_t kProcessorCurrentIdleState = 1; + + misc_info_writer->SetProcessorPowerInfo(kProcessorMaxMhz, + kProcessorCurrentMhz, + kProcessorMhzLimit, + kProcessorMaxIdleState, + kProcessorCurrentIdleState); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(misc_info_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MISC_INFO_2* observed = nullptr; + ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed)); + + MINIDUMP_MISC_INFO_2 expected = {}; + expected.Flags1 = MINIDUMP_MISC1_PROCESSOR_POWER_INFO; + expected.ProcessorMaxMhz = kProcessorMaxMhz; + expected.ProcessorCurrentMhz = kProcessorCurrentMhz; + expected.ProcessorMhzLimit = kProcessorMhzLimit; + expected.ProcessorMaxIdleState = kProcessorMaxIdleState; + expected.ProcessorCurrentIdleState = kProcessorCurrentIdleState; + + ExpectMiscInfoEqual(&expected, observed); +} + +TEST(MinidumpMiscInfoWriter, ProcessIntegrityLevel) { + MinidumpFileWriter minidump_file_writer; + auto misc_info_writer = std::make_unique(); + + constexpr uint32_t kProcessIntegrityLevel = 0x2000; + + misc_info_writer->SetProcessIntegrityLevel(kProcessIntegrityLevel); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(misc_info_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MISC_INFO_3* observed = nullptr; + ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed)); + + MINIDUMP_MISC_INFO_3 expected = {}; + expected.Flags1 = MINIDUMP_MISC3_PROCESS_INTEGRITY; + expected.ProcessIntegrityLevel = kProcessIntegrityLevel; + + ExpectMiscInfoEqual(&expected, observed); +} + +TEST(MinidumpMiscInfoWriter, ProcessExecuteFlags) { + MinidumpFileWriter minidump_file_writer; + auto misc_info_writer = std::make_unique(); + + constexpr uint32_t kProcessExecuteFlags = 0x13579bdf; + + misc_info_writer->SetProcessExecuteFlags(kProcessExecuteFlags); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(misc_info_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MISC_INFO_3* observed = nullptr; + ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed)); + + MINIDUMP_MISC_INFO_3 expected = {}; + expected.Flags1 = MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS; + expected.ProcessExecuteFlags = kProcessExecuteFlags; + + ExpectMiscInfoEqual(&expected, observed); +} + +TEST(MinidumpMiscInfoWriter, ProtectedProcess) { + MinidumpFileWriter minidump_file_writer; + auto misc_info_writer = std::make_unique(); + + constexpr uint32_t kProtectedProcess = 1; + + misc_info_writer->SetProtectedProcess(kProtectedProcess); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(misc_info_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MISC_INFO_3* observed = nullptr; + ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed)); + + MINIDUMP_MISC_INFO_3 expected = {}; + expected.Flags1 = MINIDUMP_MISC3_PROTECTED_PROCESS; + expected.ProtectedProcess = kProtectedProcess; + + ExpectMiscInfoEqual(&expected, observed); +} + +TEST(MinidumpMiscInfoWriter, TimeZone) { + MinidumpFileWriter minidump_file_writer; + auto misc_info_writer = std::make_unique(); + + constexpr uint32_t kTimeZoneId = 2; + constexpr int32_t kBias = 300; + static constexpr char kStandardName[] = "EST"; + constexpr SYSTEMTIME kStandardDate = {0, 11, 1, 0, 2, 0, 0, 0}; + constexpr int32_t kStandardBias = 0; + static constexpr char kDaylightName[] = "EDT"; + constexpr SYSTEMTIME kDaylightDate = {0, 3, 2, 0, 2, 0, 0, 0}; + constexpr int32_t kDaylightBias = -60; + + misc_info_writer->SetTimeZone(kTimeZoneId, + kBias, + kStandardName, + kStandardDate, + kStandardBias, + kDaylightName, + kDaylightDate, + kDaylightBias); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(misc_info_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MISC_INFO_3* observed = nullptr; + ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed)); + + MINIDUMP_MISC_INFO_3 expected = {}; + expected.Flags1 = MINIDUMP_MISC3_TIMEZONE; + expected.TimeZoneId = kTimeZoneId; + expected.TimeZone.Bias = kBias; + std::u16string standard_name_utf16 = + base::UTF8ToUTF16(Crbug1189439Cast(kStandardName)); + c16lcpy(AsU16CStr(expected.TimeZone.StandardName), + standard_name_utf16.c_str(), + std::size(expected.TimeZone.StandardName)); + memcpy(&expected.TimeZone.StandardDate, + &kStandardDate, + sizeof(expected.TimeZone.StandardDate)); + expected.TimeZone.StandardBias = kStandardBias; + std::u16string daylight_name_utf16 = + base::UTF8ToUTF16(Crbug1189439Cast(kDaylightName)); + c16lcpy(AsU16CStr(expected.TimeZone.DaylightName), + daylight_name_utf16.c_str(), + std::size(expected.TimeZone.DaylightName)); + memcpy(&expected.TimeZone.DaylightDate, + &kDaylightDate, + sizeof(expected.TimeZone.DaylightDate)); + expected.TimeZone.DaylightBias = kDaylightBias; + + ExpectMiscInfoEqual(&expected, observed); +} + +TEST(MinidumpMiscInfoWriter, TimeZoneStringsOverflow) { + // This test makes sure that the time zone name strings are truncated properly + // to the widths of their fields. + + MinidumpFileWriter minidump_file_writer; + auto misc_info_writer = std::make_unique(); + + constexpr uint32_t kTimeZoneId = 2; + constexpr int32_t kBias = 300; + [[maybe_unused]] MINIDUMP_MISC_INFO_N tmp; + std::string standard_name(std::size(tmp.TimeZone.StandardName) + 1, 's'); + constexpr int32_t kStandardBias = 0; + std::string daylight_name(std::size(tmp.TimeZone.DaylightName), 'd'); + constexpr int32_t kDaylightBias = -60; + + // Test using kSystemTimeZero, because not all platforms will be able to + // provide daylight saving time transition times. + constexpr SYSTEMTIME kSystemTimeZero = {}; + + misc_info_writer->SetTimeZone(kTimeZoneId, + kBias, + standard_name, + kSystemTimeZero, + kStandardBias, + daylight_name, + kSystemTimeZero, + kDaylightBias); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(misc_info_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MISC_INFO_3* observed = nullptr; + ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed)); + + MINIDUMP_MISC_INFO_3 expected = {}; + expected.Flags1 = MINIDUMP_MISC3_TIMEZONE; + expected.TimeZoneId = kTimeZoneId; + expected.TimeZone.Bias = kBias; + std::u16string standard_name_utf16 = base::UTF8ToUTF16(standard_name); + c16lcpy(AsU16CStr(expected.TimeZone.StandardName), + standard_name_utf16.c_str(), + std::size(expected.TimeZone.StandardName)); + memcpy(&expected.TimeZone.StandardDate, + &kSystemTimeZero, + sizeof(expected.TimeZone.StandardDate)); + expected.TimeZone.StandardBias = kStandardBias; + std::u16string daylight_name_utf16 = base::UTF8ToUTF16(daylight_name); + c16lcpy(AsU16CStr(expected.TimeZone.DaylightName), + daylight_name_utf16.c_str(), + std::size(expected.TimeZone.DaylightName)); + memcpy(&expected.TimeZone.DaylightDate, + &kSystemTimeZero, + sizeof(expected.TimeZone.DaylightDate)); + expected.TimeZone.DaylightBias = kDaylightBias; + + ExpectMiscInfoEqual(&expected, observed); +} + +TEST(MinidumpMiscInfoWriter, BuildStrings) { + MinidumpFileWriter minidump_file_writer; + auto misc_info_writer = std::make_unique(); + + static constexpr char kBuildString[] = "build string"; + static constexpr char kDebugBuildString[] = "debug build string"; + + misc_info_writer->SetBuildString(kBuildString, kDebugBuildString); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(misc_info_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MISC_INFO_4* observed = nullptr; + ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed)); + + MINIDUMP_MISC_INFO_4 expected = {}; + expected.Flags1 = MINIDUMP_MISC4_BUILDSTRING; + std::u16string build_string_utf16 = + base::UTF8ToUTF16(Crbug1189439Cast(kBuildString)); + c16lcpy(AsU16CStr(expected.BuildString), + build_string_utf16.c_str(), + std::size(expected.BuildString)); + std::u16string debug_build_string_utf16 = + base::UTF8ToUTF16(Crbug1189439Cast(kDebugBuildString)); + c16lcpy(AsU16CStr(expected.DbgBldStr), + debug_build_string_utf16.c_str(), + std::size(expected.DbgBldStr)); + + ExpectMiscInfoEqual(&expected, observed); +} + +TEST(MinidumpMiscInfoWriter, BuildStringsOverflow) { + // This test makes sure that the build strings are truncated properly to the + // widths of their fields. + + MinidumpFileWriter minidump_file_writer; + auto misc_info_writer = std::make_unique(); + + [[maybe_unused]] MINIDUMP_MISC_INFO_N tmp; + std::string build_string(std::size(tmp.BuildString) + 1, 'B'); + std::string debug_build_string(std::size(tmp.DbgBldStr), 'D'); + + misc_info_writer->SetBuildString(build_string, debug_build_string); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(misc_info_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MISC_INFO_4* observed = nullptr; + ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed)); + + MINIDUMP_MISC_INFO_4 expected = {}; + expected.Flags1 = MINIDUMP_MISC4_BUILDSTRING; + std::u16string build_string_utf16 = base::UTF8ToUTF16(build_string); + c16lcpy(AsU16CStr(expected.BuildString), + build_string_utf16.c_str(), + std::size(expected.BuildString)); + std::u16string debug_build_string_utf16 = + base::UTF8ToUTF16(debug_build_string); + c16lcpy(AsU16CStr(expected.DbgBldStr), + debug_build_string_utf16.c_str(), + std::size(expected.DbgBldStr)); + + ExpectMiscInfoEqual(&expected, observed); +} + +TEST(MinidumpMiscInfoWriter, XStateData) { + MinidumpFileWriter minidump_file_writer; + auto misc_info_writer = std::make_unique(); + + constexpr XSTATE_CONFIG_FEATURE_MSC_INFO kXStateData = { + sizeof(XSTATE_CONFIG_FEATURE_MSC_INFO), + 1024, + 0x000000000000005f, + { + {0, 512}, + {512, 256}, + {768, 128}, + {896, 64}, + {960, 32}, + {0, 0}, + {992, 32}, + }}; + + misc_info_writer->SetXStateData(kXStateData); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(misc_info_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MISC_INFO_5* observed = nullptr; + ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed)); + + MINIDUMP_MISC_INFO_5 expected = {}; + expected.XStateData = kXStateData; + + ExpectMiscInfoEqual(&expected, observed); +} + +TEST(MinidumpMiscInfoWriter, ProcessCookie) { + MinidumpFileWriter minidump_file_writer; + auto misc_info_writer = std::make_unique(); + + constexpr uint32_t kProcessCookie = 0x12345678; + + misc_info_writer->SetProcessCookie(kProcessCookie); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(misc_info_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MISC_INFO_5* observed = nullptr; + ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed)); + + MINIDUMP_MISC_INFO_5 expected = {}; + expected.Flags1 = MINIDUMP_MISC5_PROCESS_COOKIE; + expected.ProcessCookie = kProcessCookie; + + ExpectMiscInfoEqual(&expected, observed); +} + +TEST(MinidumpMiscInfoWriter, Everything) { + MinidumpFileWriter minidump_file_writer; + auto misc_info_writer = std::make_unique(); + + constexpr uint32_t kProcessId = 12345; + constexpr time_t kProcessCreateTime = 0x15252f00; + constexpr uint32_t kProcessUserTime = 10; + constexpr uint32_t kProcessKernelTime = 5; + constexpr uint32_t kProcessorMaxMhz = 2800; + constexpr uint32_t kProcessorCurrentMhz = 2300; + constexpr uint32_t kProcessorMhzLimit = 3300; + constexpr uint32_t kProcessorMaxIdleState = 5; + constexpr uint32_t kProcessorCurrentIdleState = 1; + constexpr uint32_t kProcessIntegrityLevel = 0x2000; + constexpr uint32_t kProcessExecuteFlags = 0x13579bdf; + constexpr uint32_t kProtectedProcess = 1; + constexpr uint32_t kTimeZoneId = 2; + constexpr int32_t kBias = 300; + static constexpr char kStandardName[] = "EST"; + constexpr int32_t kStandardBias = 0; + static constexpr char kDaylightName[] = "EDT"; + constexpr int32_t kDaylightBias = -60; + constexpr SYSTEMTIME kSystemTimeZero = {}; + static constexpr char kBuildString[] = "build string"; + static constexpr char kDebugBuildString[] = "debug build string"; + + misc_info_writer->SetProcessID(kProcessId); + misc_info_writer->SetProcessTimes( + kProcessCreateTime, kProcessUserTime, kProcessKernelTime); + misc_info_writer->SetProcessorPowerInfo(kProcessorMaxMhz, + kProcessorCurrentMhz, + kProcessorMhzLimit, + kProcessorMaxIdleState, + kProcessorCurrentIdleState); + misc_info_writer->SetProcessIntegrityLevel(kProcessIntegrityLevel); + misc_info_writer->SetProcessExecuteFlags(kProcessExecuteFlags); + misc_info_writer->SetProtectedProcess(kProtectedProcess); + misc_info_writer->SetTimeZone(kTimeZoneId, + kBias, + kStandardName, + kSystemTimeZero, + kStandardBias, + kDaylightName, + kSystemTimeZero, + kDaylightBias); + misc_info_writer->SetBuildString(kBuildString, kDebugBuildString); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(misc_info_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MISC_INFO_4* observed = nullptr; + ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &observed)); + + MINIDUMP_MISC_INFO_4 expected = {}; + expected.Flags1 = + MINIDUMP_MISC1_PROCESS_ID | MINIDUMP_MISC1_PROCESS_TIMES | + MINIDUMP_MISC1_PROCESSOR_POWER_INFO | MINIDUMP_MISC3_PROCESS_INTEGRITY | + MINIDUMP_MISC3_PROCESS_EXECUTE_FLAGS | MINIDUMP_MISC3_PROTECTED_PROCESS | + MINIDUMP_MISC3_TIMEZONE | MINIDUMP_MISC4_BUILDSTRING; + expected.ProcessId = kProcessId; + expected.ProcessCreateTime = kProcessCreateTime; + expected.ProcessUserTime = kProcessUserTime; + expected.ProcessKernelTime = kProcessKernelTime; + expected.ProcessorMaxMhz = kProcessorMaxMhz; + expected.ProcessorCurrentMhz = kProcessorCurrentMhz; + expected.ProcessorMhzLimit = kProcessorMhzLimit; + expected.ProcessorMaxIdleState = kProcessorMaxIdleState; + expected.ProcessorCurrentIdleState = kProcessorCurrentIdleState; + expected.ProcessIntegrityLevel = kProcessIntegrityLevel; + expected.ProcessExecuteFlags = kProcessExecuteFlags; + expected.ProtectedProcess = kProtectedProcess; + expected.TimeZoneId = kTimeZoneId; + expected.TimeZone.Bias = kBias; + std::u16string standard_name_utf16 = + base::UTF8ToUTF16(Crbug1189439Cast(kStandardName)); + c16lcpy(AsU16CStr(expected.TimeZone.StandardName), + standard_name_utf16.c_str(), + std::size(expected.TimeZone.StandardName)); + memcpy(&expected.TimeZone.StandardDate, + &kSystemTimeZero, + sizeof(expected.TimeZone.StandardDate)); + expected.TimeZone.StandardBias = kStandardBias; + std::u16string daylight_name_utf16 = + base::UTF8ToUTF16(Crbug1189439Cast(kDaylightName)); + c16lcpy(AsU16CStr(expected.TimeZone.DaylightName), + daylight_name_utf16.c_str(), + std::size(expected.TimeZone.DaylightName)); + memcpy(&expected.TimeZone.DaylightDate, + &kSystemTimeZero, + sizeof(expected.TimeZone.DaylightDate)); + expected.TimeZone.DaylightBias = kDaylightBias; + std::u16string build_string_utf16 = + base::UTF8ToUTF16(Crbug1189439Cast(kBuildString)); + c16lcpy(AsU16CStr(expected.BuildString), + build_string_utf16.c_str(), + std::size(expected.BuildString)); + std::u16string debug_build_string_utf16 = + base::UTF8ToUTF16(Crbug1189439Cast(kDebugBuildString)); + c16lcpy(AsU16CStr(expected.DbgBldStr), + debug_build_string_utf16.c_str(), + std::size(expected.DbgBldStr)); + + ExpectMiscInfoEqual(&expected, observed); +} + +TEST(MinidumpMiscInfoWriter, InitializeFromSnapshot) { + MINIDUMP_MISC_INFO_4 expect_misc_info = {}; + + static constexpr char kStandardTimeName[] = "EST"; + static constexpr char kDaylightTimeName[] = "EDT"; + static constexpr char kOSVersionFull[] = + "Mac OS X 10.9.5 (13F34); " + "Darwin 13.4.0 Darwin Kernel Version 13.4.0: " + "Sun Aug 17 19:50:11 PDT 2014; " + "root:xnu-2422.115.4~1/RELEASE_X86_64 x86_64"; + static constexpr char kMachineDescription[] = + "MacBookPro11,3 (Mac-2BD1B31983FE1663)"; + std::u16string standard_time_name_utf16 = + base::UTF8ToUTF16(Crbug1189439Cast(kStandardTimeName)); + std::u16string daylight_time_name_utf16 = + base::UTF8ToUTF16(Crbug1189439Cast(kDaylightTimeName)); + std::u16string build_string_utf16 = base::UTF8ToUTF16( + std::string(kOSVersionFull) + "; " + kMachineDescription); + std::string debug_build_string = internal::MinidumpMiscInfoDebugBuildString(); + EXPECT_FALSE(debug_build_string.empty()); + std::u16string debug_build_string_utf16 = + base::UTF8ToUTF16(debug_build_string); + + expect_misc_info.SizeOfInfo = sizeof(expect_misc_info); + expect_misc_info.Flags1 = MINIDUMP_MISC1_PROCESS_ID | + MINIDUMP_MISC1_PROCESS_TIMES | + MINIDUMP_MISC1_PROCESSOR_POWER_INFO | + MINIDUMP_MISC3_TIMEZONE | + MINIDUMP_MISC4_BUILDSTRING; + expect_misc_info.ProcessId = 12345; + expect_misc_info.ProcessCreateTime = 0x555c7740; + expect_misc_info.ProcessUserTime = 60; + expect_misc_info.ProcessKernelTime = 15; + expect_misc_info.ProcessorCurrentMhz = 2800; + expect_misc_info.ProcessorMaxMhz = 2800; + expect_misc_info.TimeZoneId = 1; + expect_misc_info.TimeZone.Bias = 300; + c16lcpy(AsU16CStr(expect_misc_info.TimeZone.StandardName), + standard_time_name_utf16.c_str(), + std::size(expect_misc_info.TimeZone.StandardName)); + expect_misc_info.TimeZone.StandardBias = 0; + c16lcpy(AsU16CStr(expect_misc_info.TimeZone.DaylightName), + daylight_time_name_utf16.c_str(), + std::size(expect_misc_info.TimeZone.DaylightName)); + expect_misc_info.TimeZone.DaylightBias = -60; + c16lcpy(AsU16CStr(expect_misc_info.BuildString), + build_string_utf16.c_str(), + std::size(expect_misc_info.BuildString)); + c16lcpy(AsU16CStr(expect_misc_info.DbgBldStr), + debug_build_string_utf16.c_str(), + std::size(expect_misc_info.DbgBldStr)); + + const timeval kStartTime = { + static_cast(expect_misc_info.ProcessCreateTime), 0}; + const timeval kUserCPUTime = { + static_cast(expect_misc_info.ProcessUserTime), 0}; + const timeval kSystemCPUTime = { + static_cast(expect_misc_info.ProcessKernelTime), 0}; + + TestProcessSnapshot process_snapshot; + process_snapshot.SetProcessID(expect_misc_info.ProcessId); + process_snapshot.SetProcessStartTime(kStartTime); + process_snapshot.SetProcessCPUTimes(kUserCPUTime, kSystemCPUTime); + + auto system_snapshot = std::make_unique(); + constexpr uint64_t kHzPerMHz = static_cast(1E6); + system_snapshot->SetCPUFrequency( + expect_misc_info.ProcessorCurrentMhz * kHzPerMHz, + expect_misc_info.ProcessorMaxMhz * kHzPerMHz); + system_snapshot->SetTimeZone(SystemSnapshot::kObservingStandardTime, + expect_misc_info.TimeZone.Bias * -60, + (expect_misc_info.TimeZone.Bias + + expect_misc_info.TimeZone.DaylightBias) * -60, + kStandardTimeName, + kDaylightTimeName); + system_snapshot->SetOSVersionFull(kOSVersionFull); + system_snapshot->SetMachineDescription(kMachineDescription); + + process_snapshot.SetSystem(std::move(system_snapshot)); + + auto misc_info_writer = std::make_unique(); + misc_info_writer->InitializeFromSnapshot(&process_snapshot); + + MinidumpFileWriter minidump_file_writer; + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(misc_info_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MISC_INFO_4* misc_info = nullptr; + ASSERT_NO_FATAL_FAILURE(GetMiscInfoStream(string_file.string(), &misc_info)); + + ExpectMiscInfoEqual(&expect_misc_info, misc_info); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_module_crashpad_info_writer.cc b/shared/sentry/external/crashpad/minidump/minidump_module_crashpad_info_writer.cc new file mode 100644 index 000000000..a31feaea1 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_module_crashpad_info_writer.cc @@ -0,0 +1,262 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_module_crashpad_info_writer.h" + +#include + +#include "base/logging.h" +#include "minidump/minidump_annotation_writer.h" +#include "minidump/minidump_simple_string_dictionary_writer.h" +#include "snapshot/module_snapshot.h" +#include "util/file/file_writer.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { + +MinidumpModuleCrashpadInfoWriter::MinidumpModuleCrashpadInfoWriter() + : MinidumpWritable(), + module_(), + list_annotations_(), + simple_annotations_(), + annotation_objects_() { + module_.version = MinidumpModuleCrashpadInfo::kVersion; +} + +MinidumpModuleCrashpadInfoWriter::~MinidumpModuleCrashpadInfoWriter() { +} + +void MinidumpModuleCrashpadInfoWriter::InitializeFromSnapshot( + const ModuleSnapshot* module_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(!list_annotations_); + DCHECK(!simple_annotations_); + + auto list_annotations = std::make_unique(); + list_annotations->InitializeFromVector(module_snapshot->AnnotationsVector()); + if (list_annotations->IsUseful()) { + SetListAnnotations(std::move(list_annotations)); + } + + auto simple_annotations = + std::make_unique(); + simple_annotations->InitializeFromMap( + module_snapshot->AnnotationsSimpleMap()); + if (simple_annotations->IsUseful()) { + SetSimpleAnnotations(std::move(simple_annotations)); + } + + auto annotation_objects = std::make_unique(); + annotation_objects->InitializeFromList(module_snapshot->AnnotationObjects()); + if (annotation_objects->IsUseful()) { + SetAnnotationObjects(std::move(annotation_objects)); + } +} + +void MinidumpModuleCrashpadInfoWriter::SetListAnnotations( + std::unique_ptr list_annotations) { + DCHECK_EQ(state(), kStateMutable); + + list_annotations_ = std::move(list_annotations); +} + +void MinidumpModuleCrashpadInfoWriter::SetSimpleAnnotations( + std::unique_ptr simple_annotations) { + DCHECK_EQ(state(), kStateMutable); + + simple_annotations_ = std::move(simple_annotations); +} + +void MinidumpModuleCrashpadInfoWriter::SetAnnotationObjects( + std::unique_ptr annotation_objects) { + DCHECK_EQ(state(), kStateMutable); + + annotation_objects_ = std::move(annotation_objects); +} + +bool MinidumpModuleCrashpadInfoWriter::IsUseful() const { + return list_annotations_ || simple_annotations_ || annotation_objects_; +} + +bool MinidumpModuleCrashpadInfoWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + if (list_annotations_) { + list_annotations_->RegisterLocationDescriptor(&module_.list_annotations); + } + + if (simple_annotations_) { + simple_annotations_->RegisterLocationDescriptor( + &module_.simple_annotations); + } + + if (annotation_objects_) { + annotation_objects_->RegisterLocationDescriptor( + &module_.annotation_objects); + } + + return true; +} + +size_t MinidumpModuleCrashpadInfoWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return sizeof(module_); +} + +std::vector +MinidumpModuleCrashpadInfoWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + + std::vector children; + if (list_annotations_) { + children.push_back(list_annotations_.get()); + } + if (simple_annotations_) { + children.push_back(simple_annotations_.get()); + } + if (annotation_objects_) { + children.push_back(annotation_objects_.get()); + } + + return children; +} + +bool MinidumpModuleCrashpadInfoWriter::WriteObject( + FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + return file_writer->Write(&module_, sizeof(module_)); +} + +MinidumpModuleCrashpadInfoListWriter::MinidumpModuleCrashpadInfoListWriter() + : MinidumpWritable(), + module_crashpad_infos_(), + module_crashpad_info_links_(), + module_crashpad_info_list_base_() { +} + +MinidumpModuleCrashpadInfoListWriter::~MinidumpModuleCrashpadInfoListWriter() { +} + +void MinidumpModuleCrashpadInfoListWriter::InitializeFromSnapshot( + const std::vector& module_snapshots) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(module_crashpad_infos_.empty()); + DCHECK(module_crashpad_info_links_.empty()); + + size_t count = module_snapshots.size(); + for (size_t index = 0; index < count; ++index) { + const ModuleSnapshot* module_snapshot = module_snapshots[index]; + + auto module = std::make_unique(); + module->InitializeFromSnapshot(module_snapshot); + if (module->IsUseful()) { + AddModule(std::move(module), index); + } + } +} + +void MinidumpModuleCrashpadInfoListWriter::AddModule( + std::unique_ptr module_crashpad_info, + size_t minidump_module_list_index) { + DCHECK_EQ(state(), kStateMutable); + DCHECK_EQ(module_crashpad_infos_.size(), module_crashpad_info_links_.size()); + + MinidumpModuleCrashpadInfoLink module_crashpad_info_link = {}; + if (!AssignIfInRange(&module_crashpad_info_link.minidump_module_list_index, + minidump_module_list_index)) { + LOG(ERROR) << "minidump_module_list_index " << minidump_module_list_index + << " out of range"; + return; + } + + module_crashpad_info_links_.push_back(module_crashpad_info_link); + module_crashpad_infos_.push_back(std::move(module_crashpad_info)); +} + +bool MinidumpModuleCrashpadInfoListWriter::IsUseful() const { + DCHECK_EQ(module_crashpad_infos_.size(), module_crashpad_info_links_.size()); + return !module_crashpad_infos_.empty(); +} + +bool MinidumpModuleCrashpadInfoListWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + CHECK_EQ(module_crashpad_infos_.size(), module_crashpad_info_links_.size()); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + size_t module_count = module_crashpad_infos_.size(); + if (!AssignIfInRange(&module_crashpad_info_list_base_.count, module_count)) { + LOG(ERROR) << "module_count " << module_count << " out of range"; + return false; + } + + for (size_t index = 0; index < module_count; ++index) { + module_crashpad_infos_[index]->RegisterLocationDescriptor( + &module_crashpad_info_links_[index].location); + } + + return true; +} + +size_t MinidumpModuleCrashpadInfoListWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + DCHECK_EQ(module_crashpad_infos_.size(), module_crashpad_info_links_.size()); + + return sizeof(module_crashpad_info_list_base_) + + module_crashpad_info_links_.size() * + sizeof(module_crashpad_info_links_[0]); +} + +std::vector +MinidumpModuleCrashpadInfoListWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + DCHECK_EQ(module_crashpad_infos_.size(), module_crashpad_info_links_.size()); + + std::vector children; + for (const auto& module : module_crashpad_infos_) { + children.push_back(module.get()); + } + + return children; +} + +bool MinidumpModuleCrashpadInfoListWriter::WriteObject( + FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + DCHECK_EQ(module_crashpad_infos_.size(), module_crashpad_info_links_.size()); + + WritableIoVec iov; + iov.iov_base = &module_crashpad_info_list_base_; + iov.iov_len = sizeof(module_crashpad_info_list_base_); + std::vector iovecs(1, iov); + + if (!module_crashpad_info_links_.empty()) { + iov.iov_base = &module_crashpad_info_links_[0]; + iov.iov_len = module_crashpad_info_links_.size() * + sizeof(module_crashpad_info_links_[0]); + iovecs.push_back(iov); + } + + return file_writer->WriteIoVec(&iovecs); +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_module_crashpad_info_writer.h b/shared/sentry/external/crashpad/minidump/minidump_module_crashpad_info_writer.h new file mode 100644 index 000000000..9af5c83fa --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_module_crashpad_info_writer.h @@ -0,0 +1,187 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_MODULE_CRASHPAD_INFO_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_MODULE_CRASHPAD_INFO_WRITER_H_ + +#include +#include + +#include +#include + +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_string_writer.h" +#include "minidump/minidump_writable.h" + +namespace crashpad { + +class MinidumpAnnotationListWriter; +class MinidumpSimpleStringDictionaryWriter; +class ModuleSnapshot; + +//! \brief The writer for a MinidumpModuleCrashpadInfo object in a minidump +//! file. +class MinidumpModuleCrashpadInfoWriter final + : public internal::MinidumpWritable { + public: + MinidumpModuleCrashpadInfoWriter(); + + MinidumpModuleCrashpadInfoWriter(const MinidumpModuleCrashpadInfoWriter&) = + delete; + MinidumpModuleCrashpadInfoWriter& operator=( + const MinidumpModuleCrashpadInfoWriter&) = delete; + + ~MinidumpModuleCrashpadInfoWriter() override; + + //! \brief Initializes MinidumpModuleCrashpadInfo based on \a module_snapshot. + //! + //! Only data in \a module_snapshot that is considered useful will be + //! included. For simple annotations, usefulness is determined by + //! MinidumpSimpleStringDictionaryWriter::IsUseful(). + //! + //! \param[in] module_snapshot The module snapshot to use as source data. + //! + //! \note Valid in #kStateMutable. No mutator methods may be called before + //! this method, and it is not normally necessary to call any mutator + //! methods after this method. + void InitializeFromSnapshot(const ModuleSnapshot* module_snapshot); + + //! \brief Arranges for MinidumpModuleCrashpadInfo::list_annotations to point + //! to the internal::MinidumpUTF8StringListWriter object to be written by + //! \a list_annotations. + //! + //! This object takes ownership of \a simple_annotations and becomes its + //! parent in the overall tree of internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void SetListAnnotations( + std::unique_ptr list_annotations); + + //! \brief Arranges for MinidumpModuleCrashpadInfo::simple_annotations to + //! point to the MinidumpSimpleStringDictionaryWriter object to be written + //! by \a simple_annotations. + //! + //! This object takes ownership of \a simple_annotations and becomes its + //! parent in the overall tree of internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void SetSimpleAnnotations( + std::unique_ptr simple_annotations); + + //! \brief Arranges for MinidumpModuleCrashpadInfo::annotation_objects to + //! point to the MinidumpAnnotationListWriter object to be written by + //! \a annotation_objects. + //! + //! This object takes ownership of \a annotation_objects and becomes its + //! parent in the overall tree of internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void SetAnnotationObjects( + std::unique_ptr annotation_objects); + + //! \brief Determines whether the object is useful. + //! + //! A useful object is one that carries data that makes a meaningful + //! contribution to a minidump file. An object carrying list annotations or + //! simple annotations would be considered useful. + //! + //! \return `true` if the object is useful, `false` otherwise. + bool IsUseful() const; + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + MinidumpModuleCrashpadInfo module_; + std::unique_ptr list_annotations_; + std::unique_ptr simple_annotations_; + std::unique_ptr annotation_objects_; +}; + +//! \brief The writer for a MinidumpModuleCrashpadInfoList object in a minidump +//! file, containing a list of MinidumpModuleCrashpadInfo objects. +class MinidumpModuleCrashpadInfoListWriter final + : public internal::MinidumpWritable { + public: + MinidumpModuleCrashpadInfoListWriter(); + + MinidumpModuleCrashpadInfoListWriter( + const MinidumpModuleCrashpadInfoListWriter&) = delete; + MinidumpModuleCrashpadInfoListWriter& operator=( + const MinidumpModuleCrashpadInfoListWriter&) = delete; + + ~MinidumpModuleCrashpadInfoListWriter() override; + + //! \brief Adds an initialized MinidumpModuleCrashpadInfo for modules in \a + //! module_snapshots to the MinidumpModuleCrashpadInfoList. + //! + //! Only modules in \a module_snapshots that would produce a useful + //! MinidumpModuleCrashpadInfo structure are included. Usefulness is + //! determined by MinidumpModuleCrashpadInfoWriter::IsUseful(). + //! + //! \param[in] module_snapshots The module snapshots to use as source data. + //! + //! \note Valid in #kStateMutable. AddModule() may not be called before this + //! method, and it is not normally necessary to call AddModule() after + //! this method. + void InitializeFromSnapshot( + const std::vector& module_snapshots); + + //! \brief Adds a MinidumpModuleCrashpadInfo to the + //! MinidumpModuleCrashpadInfoList. + //! + //! \param[in] module_crashpad_info Extended Crashpad-specific information + //! about the module. This object takes ownership of \a + //! module_crashpad_info and becomes its parent in the overall tree of + //! internal::MinidumpWritable objects. + //! \param[in] minidump_module_list_index The index of the MINIDUMP_MODULE in + //! the minidump file’s MINIDUMP_MODULE_LIST stream that corresponds to \a + //! module_crashpad_info. + //! + //! \note Valid in #kStateMutable. + void AddModule( + std::unique_ptr module_crashpad_info, + size_t minidump_module_list_index); + + //! \brief Determines whether the object is useful. + //! + //! A useful object is one that carries data that makes a meaningful + //! contribution to a minidump file. An object carrying children would be + //! considered useful. + //! + //! \return `true` if the object is useful, `false` otherwise. + bool IsUseful() const; + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + std::vector> + module_crashpad_infos_; + std::vector module_crashpad_info_links_; + MinidumpModuleCrashpadInfoList module_crashpad_info_list_base_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_MODULE_CRASHPAD_INFO_WRITER_H_ diff --git a/shared/sentry/external/crashpad/minidump/minidump_module_crashpad_info_writer_test.cc b/shared/sentry/external/crashpad/minidump/minidump_module_crashpad_info_writer_test.cc new file mode 100644 index 000000000..ba5c5f223 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_module_crashpad_info_writer_test.cc @@ -0,0 +1,607 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_module_crashpad_info_writer.h" + +#include +#include + +#include +#include + +#include "gtest/gtest.h" +#include "minidump/minidump_annotation_writer.h" +#include "minidump/minidump_simple_string_dictionary_writer.h" +#include "minidump/test/minidump_byte_array_writer_test_util.h" +#include "minidump/test/minidump_file_writer_test_util.h" +#include "minidump/test/minidump_string_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "snapshot/test/test_module_snapshot.h" +#include "util/file/string_file.h" + +namespace crashpad { +namespace test { +namespace { + +const MinidumpModuleCrashpadInfoList* MinidumpModuleCrashpadInfoListAtStart( + const std::string& file_contents, + size_t count) { + MINIDUMP_LOCATION_DESCRIPTOR location_descriptor; + location_descriptor.DataSize = + static_cast(sizeof(MinidumpModuleCrashpadInfoList) + + count * sizeof(MinidumpModuleCrashpadInfoLink)); + location_descriptor.Rva = 0; + + const MinidumpModuleCrashpadInfoList* list = + MinidumpWritableAtLocationDescriptor( + file_contents, location_descriptor); + if (!list) { + return nullptr; + } + + if (list->count != count) { + EXPECT_EQ(list->count, count); + return nullptr; + } + + return list; +} + +TEST(MinidumpModuleCrashpadInfoWriter, EmptyList) { + StringFile string_file; + + auto module_list_writer = + std::make_unique(); + EXPECT_FALSE(module_list_writer->IsUseful()); + + EXPECT_TRUE(module_list_writer->WriteEverything(&string_file)); + ASSERT_EQ(string_file.string().size(), + sizeof(MinidumpModuleCrashpadInfoList)); + + const MinidumpModuleCrashpadInfoList* module_list = + MinidumpModuleCrashpadInfoListAtStart(string_file.string(), 0); + ASSERT_TRUE(module_list); +} + +TEST(MinidumpModuleCrashpadInfoWriter, EmptyModule) { + StringFile string_file; + + auto module_list_writer = + std::make_unique(); + auto module_writer = std::make_unique(); + EXPECT_FALSE(module_writer->IsUseful()); + module_list_writer->AddModule(std::move(module_writer), 0); + + EXPECT_TRUE(module_list_writer->IsUseful()); + + EXPECT_TRUE(module_list_writer->WriteEverything(&string_file)); + ASSERT_EQ(string_file.string().size(), + sizeof(MinidumpModuleCrashpadInfoList) + + sizeof(MinidumpModuleCrashpadInfoLink) + + sizeof(MinidumpModuleCrashpadInfo)); + + const MinidumpModuleCrashpadInfoList* module_list = + MinidumpModuleCrashpadInfoListAtStart(string_file.string(), 1); + ASSERT_TRUE(module_list); + + EXPECT_EQ(module_list->modules[0].minidump_module_list_index, 0u); + const MinidumpModuleCrashpadInfo* module = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_list->modules[0].location); + ASSERT_TRUE(module); + + EXPECT_EQ(module->version, MinidumpModuleCrashpadInfo::kVersion); + EXPECT_EQ(module->list_annotations.DataSize, 0u); + EXPECT_EQ(module->list_annotations.Rva, 0u); + EXPECT_EQ(module->simple_annotations.DataSize, 0u); + EXPECT_EQ(module->simple_annotations.Rva, 0u); + EXPECT_EQ(module->annotation_objects.DataSize, 0u); + EXPECT_EQ(module->annotation_objects.Rva, 0u); +} + +TEST(MinidumpModuleCrashpadInfoWriter, FullModule) { + constexpr uint32_t kMinidumpModuleListIndex = 1; + static constexpr char kKey[] = "key"; + static constexpr char kValue[] = "value"; + static constexpr char kEntry[] = "entry"; + std::vector vector(1, std::string(kEntry)); + const AnnotationSnapshot annotation("one", 42, {'t', 'e', 's', 't'}); + + StringFile string_file; + + auto module_list_writer = + std::make_unique(); + + auto module_writer = std::make_unique(); + auto string_list_writer = std::make_unique(); + string_list_writer->InitializeFromVector(vector); + module_writer->SetListAnnotations(std::move(string_list_writer)); + auto simple_string_dictionary_writer = + std::make_unique(); + auto simple_string_dictionary_entry_writer = + std::make_unique(); + simple_string_dictionary_entry_writer->SetKeyValue(kKey, kValue); + simple_string_dictionary_writer->AddEntry( + std::move(simple_string_dictionary_entry_writer)); + module_writer->SetSimpleAnnotations( + std::move(simple_string_dictionary_writer)); + auto annotation_list_writer = + std::make_unique(); + annotation_list_writer->InitializeFromList({annotation}); + module_writer->SetAnnotationObjects(std::move(annotation_list_writer)); + EXPECT_TRUE(module_writer->IsUseful()); + module_list_writer->AddModule(std::move(module_writer), + kMinidumpModuleListIndex); + + EXPECT_TRUE(module_list_writer->IsUseful()); + + EXPECT_TRUE(module_list_writer->WriteEverything(&string_file)); + ASSERT_EQ(string_file.string().size(), + sizeof(MinidumpModuleCrashpadInfoList) + + sizeof(MinidumpModuleCrashpadInfoLink) + + sizeof(MinidumpModuleCrashpadInfo) + sizeof(MinidumpRVAList) + + sizeof(RVA) + sizeof(MinidumpSimpleStringDictionary) + + sizeof(MinidumpSimpleStringDictionaryEntry) + + sizeof(MinidumpAnnotationList) + 2 + // padding + sizeof(MinidumpAnnotation) + sizeof(MinidumpUTF8String) + + std::size(kEntry) + 2 + // padding + sizeof(MinidumpUTF8String) + std::size(kKey) + + sizeof(MinidumpUTF8String) + std::size(kValue) + + sizeof(MinidumpUTF8String) + annotation.name.size() + 1 + + sizeof(MinidumpByteArray) + annotation.value.size()); + + const MinidumpModuleCrashpadInfoList* module_list = + MinidumpModuleCrashpadInfoListAtStart(string_file.string(), 1); + ASSERT_TRUE(module_list); + + EXPECT_EQ(module_list->modules[0].minidump_module_list_index, + kMinidumpModuleListIndex); + const MinidumpModuleCrashpadInfo* module = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_list->modules[0].location); + ASSERT_TRUE(module); + + EXPECT_EQ(module->version, MinidumpModuleCrashpadInfo::kVersion); + EXPECT_NE(module->list_annotations.DataSize, 0u); + EXPECT_NE(module->list_annotations.Rva, 0u); + EXPECT_NE(module->simple_annotations.DataSize, 0u); + EXPECT_NE(module->simple_annotations.Rva, 0u); + EXPECT_NE(module->annotation_objects.DataSize, 0u); + EXPECT_NE(module->annotation_objects.Rva, 0u); + + const MinidumpRVAList* list_annotations = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module->list_annotations); + ASSERT_TRUE(list_annotations); + + ASSERT_EQ(list_annotations->count, 1u); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + list_annotations->children[0]), + kEntry); + + const MinidumpSimpleStringDictionary* simple_annotations = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module->simple_annotations); + ASSERT_TRUE(simple_annotations); + + ASSERT_EQ(simple_annotations->count, 1u); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + simple_annotations->entries[0].key), + kKey); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations->entries[0].value), + kValue); + + const MinidumpAnnotationList* annotation_objects = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module->annotation_objects); + ASSERT_TRUE(annotation_objects); + + ASSERT_EQ(annotation_objects->count, 1u); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString( + string_file.string(), annotation_objects->objects[0].name), + annotation.name); + EXPECT_EQ(annotation_objects->objects[0].type, 42u); + EXPECT_EQ(annotation_objects->objects[0].reserved, 0u); + EXPECT_EQ(MinidumpByteArrayAtRVA(string_file.string(), + annotation_objects->objects[0].value), + annotation.value); +} + +TEST(MinidumpModuleCrashpadInfoWriter, ThreeModules) { + constexpr uint32_t kMinidumpModuleListIndex0 = 0; + static constexpr char kKey0[] = "key"; + static constexpr char kValue0[] = "value"; + const AnnotationSnapshot annotation0("name", 0x8FFF, {'t', '\0', 't'}); + constexpr uint32_t kMinidumpModuleListIndex1 = 2; + constexpr uint32_t kMinidumpModuleListIndex2 = 5; + static constexpr char kKey2A[] = "K"; + static constexpr char kValue2A[] = "VVV"; + static constexpr char kKey2B[] = "river"; + static constexpr char kValue2B[] = "hudson"; + const AnnotationSnapshot annotation2a("A2", 0xDDDD, {2, 4, 6, 8}); + const AnnotationSnapshot annotation2b("A3", 0xDDDF, {'m', 'o', 'o'}); + + StringFile string_file; + + auto module_list_writer = + std::make_unique(); + + auto module_writer_0 = std::make_unique(); + auto simple_string_dictionary_writer_0 = + std::make_unique(); + auto simple_string_dictionary_entry_writer_0 = + std::make_unique(); + simple_string_dictionary_entry_writer_0->SetKeyValue(kKey0, kValue0); + simple_string_dictionary_writer_0->AddEntry( + std::move(simple_string_dictionary_entry_writer_0)); + module_writer_0->SetSimpleAnnotations( + std::move(simple_string_dictionary_writer_0)); + auto annotation_list_writer_0 = + std::make_unique(); + auto annotation_writer_0 = std::make_unique(); + annotation_writer_0->InitializeFromSnapshot(annotation0); + annotation_list_writer_0->AddObject(std::move(annotation_writer_0)); + module_writer_0->SetAnnotationObjects(std::move(annotation_list_writer_0)); + EXPECT_TRUE(module_writer_0->IsUseful()); + module_list_writer->AddModule(std::move(module_writer_0), + kMinidumpModuleListIndex0); + + auto module_writer_1 = std::make_unique(); + EXPECT_FALSE(module_writer_1->IsUseful()); + module_list_writer->AddModule(std::move(module_writer_1), + kMinidumpModuleListIndex1); + + auto module_writer_2 = std::make_unique(); + auto simple_string_dictionary_writer_2 = + std::make_unique(); + auto simple_string_dictionary_entry_writer_2a = + std::make_unique(); + simple_string_dictionary_entry_writer_2a->SetKeyValue(kKey2A, kValue2A); + simple_string_dictionary_writer_2->AddEntry( + std::move(simple_string_dictionary_entry_writer_2a)); + auto simple_string_dictionary_entry_writer_2b = + std::make_unique(); + simple_string_dictionary_entry_writer_2b->SetKeyValue(kKey2B, kValue2B); + simple_string_dictionary_writer_2->AddEntry( + std::move(simple_string_dictionary_entry_writer_2b)); + module_writer_2->SetSimpleAnnotations( + std::move(simple_string_dictionary_writer_2)); + auto annotation_list_writer_2 = + std::make_unique(); + auto annotation_writer_2a = std::make_unique(); + annotation_writer_2a->InitializeFromSnapshot(annotation2a); + auto annotation_writer_2b = std::make_unique(); + annotation_writer_2b->InitializeFromSnapshot(annotation2b); + annotation_list_writer_2->AddObject(std::move(annotation_writer_2a)); + annotation_list_writer_2->AddObject(std::move(annotation_writer_2b)); + module_writer_2->SetAnnotationObjects(std::move(annotation_list_writer_2)); + EXPECT_TRUE(module_writer_2->IsUseful()); + module_list_writer->AddModule(std::move(module_writer_2), + kMinidumpModuleListIndex2); + + EXPECT_TRUE(module_list_writer->IsUseful()); + + EXPECT_TRUE(module_list_writer->WriteEverything(&string_file)); + + const MinidumpModuleCrashpadInfoList* module_list = + MinidumpModuleCrashpadInfoListAtStart(string_file.string(), 3); + ASSERT_TRUE(module_list); + + EXPECT_EQ(module_list->modules[0].minidump_module_list_index, + kMinidumpModuleListIndex0); + const MinidumpModuleCrashpadInfo* module_0 = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_list->modules[0].location); + ASSERT_TRUE(module_0); + + EXPECT_EQ(module_0->version, MinidumpModuleCrashpadInfo::kVersion); + + const MinidumpRVAList* list_annotations_0 = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_0->list_annotations); + EXPECT_FALSE(list_annotations_0); + + const MinidumpSimpleStringDictionary* simple_annotations_0 = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_0->simple_annotations); + ASSERT_TRUE(simple_annotations_0); + + ASSERT_EQ(simple_annotations_0->count, 1u); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations_0->entries[0].key), + kKey0); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations_0->entries[0].value), + kValue0); + + const MinidumpAnnotationList* annotation_list_0 = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_0->annotation_objects); + ASSERT_TRUE(annotation_list_0); + + ASSERT_EQ(annotation_list_0->count, 1u); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + annotation_list_0->objects[0].name), + annotation0.name); + EXPECT_EQ(annotation_list_0->objects[0].type, annotation0.type); + EXPECT_EQ(annotation_list_0->objects[0].reserved, 0u); + EXPECT_EQ(MinidumpByteArrayAtRVA(string_file.string(), + annotation_list_0->objects[0].value), + annotation0.value); + + EXPECT_EQ(module_list->modules[1].minidump_module_list_index, + kMinidumpModuleListIndex1); + const MinidumpModuleCrashpadInfo* module_1 = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_list->modules[1].location); + ASSERT_TRUE(module_1); + + EXPECT_EQ(module_1->version, MinidumpModuleCrashpadInfo::kVersion); + + const MinidumpRVAList* list_annotations_1 = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_1->list_annotations); + EXPECT_FALSE(list_annotations_1); + + const MinidumpSimpleStringDictionary* simple_annotations_1 = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_1->simple_annotations); + EXPECT_FALSE(simple_annotations_1); + + const MinidumpAnnotationList* annotation_list_1 = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_1->annotation_objects); + EXPECT_FALSE(annotation_list_1); + + EXPECT_EQ(module_list->modules[2].minidump_module_list_index, + kMinidumpModuleListIndex2); + const MinidumpModuleCrashpadInfo* module_2 = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_list->modules[2].location); + ASSERT_TRUE(module_2); + + EXPECT_EQ(module_2->version, MinidumpModuleCrashpadInfo::kVersion); + + const MinidumpRVAList* list_annotations_2 = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_2->list_annotations); + EXPECT_FALSE(list_annotations_2); + + const MinidumpSimpleStringDictionary* simple_annotations_2 = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_2->simple_annotations); + ASSERT_TRUE(simple_annotations_2); + + ASSERT_EQ(simple_annotations_2->count, 2u); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations_2->entries[0].key), + kKey2A); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations_2->entries[0].value), + kValue2A); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations_2->entries[1].key), + kKey2B); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations_2->entries[1].value), + kValue2B); + + const MinidumpAnnotationList* annotation_list_2 = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_2->annotation_objects); + ASSERT_TRUE(annotation_list_2); + + ASSERT_EQ(annotation_list_2->count, 2u); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + annotation_list_2->objects[0].name), + annotation2a.name); + EXPECT_EQ(annotation_list_2->objects[0].type, annotation2a.type); + EXPECT_EQ(annotation_list_2->objects[0].reserved, 0u); + EXPECT_EQ(MinidumpByteArrayAtRVA(string_file.string(), + annotation_list_2->objects[0].value), + annotation2a.value); + + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + annotation_list_2->objects[1].name), + annotation2b.name); + EXPECT_EQ(annotation_list_2->objects[1].type, annotation2b.type); + EXPECT_EQ(annotation_list_2->objects[1].reserved, 0u); + EXPECT_EQ(MinidumpByteArrayAtRVA(string_file.string(), + annotation_list_2->objects[1].value), + annotation2b.value); +} + +TEST(MinidumpModuleCrashpadInfoWriter, InitializeFromSnapshot) { + static constexpr char kKey0A[] = "k"; + static constexpr char kValue0A[] = "value"; + static constexpr char kKey0B[] = "hudson"; + static constexpr char kValue0B[] = "estuary"; + static constexpr char kKey2[] = "k"; + static constexpr char kValue2[] = "different_value"; + static constexpr char kEntry3A[] = "list"; + static constexpr char kEntry3B[] = "erine"; + const AnnotationSnapshot annotation( + "market", 1, {'2', '3', 'r', 'd', ' ', 'S', 't', '.'}); + + std::vector module_snapshots; + + TestModuleSnapshot module_snapshot_0; + std::map annotations_simple_map_0; + annotations_simple_map_0[kKey0A] = kValue0A; + annotations_simple_map_0[kKey0B] = kValue0B; + module_snapshot_0.SetAnnotationsSimpleMap(annotations_simple_map_0); + module_snapshots.push_back(&module_snapshot_0); + + // module_snapshot_1 is not expected to be written because it would not carry + // any MinidumpModuleCrashpadInfo data. + TestModuleSnapshot module_snapshot_1; + module_snapshots.push_back(&module_snapshot_1); + + TestModuleSnapshot module_snapshot_2; + std::map annotations_simple_map_2; + annotations_simple_map_2[kKey2] = kValue2; + module_snapshot_2.SetAnnotationsSimpleMap(annotations_simple_map_2); + module_snapshots.push_back(&module_snapshot_2); + + TestModuleSnapshot module_snapshot_3; + std::vector annotations_vector_3; + annotations_vector_3.push_back(kEntry3A); + annotations_vector_3.push_back(kEntry3B); + module_snapshot_3.SetAnnotationsVector(annotations_vector_3); + module_snapshots.push_back(&module_snapshot_3); + + TestModuleSnapshot module_snapshot_4; + module_snapshot_4.SetAnnotationObjects({annotation}); + module_snapshots.push_back(&module_snapshot_4); + + auto module_list_writer = + std::make_unique(); + module_list_writer->InitializeFromSnapshot(module_snapshots); + EXPECT_TRUE(module_list_writer->IsUseful()); + + StringFile string_file; + ASSERT_TRUE(module_list_writer->WriteEverything(&string_file)); + + const MinidumpModuleCrashpadInfoList* module_list = + MinidumpModuleCrashpadInfoListAtStart(string_file.string(), 4); + ASSERT_TRUE(module_list); + + EXPECT_EQ(module_list->modules[0].minidump_module_list_index, 0u); + const MinidumpModuleCrashpadInfo* module_0 = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_list->modules[0].location); + ASSERT_TRUE(module_0); + + EXPECT_EQ(module_0->version, MinidumpModuleCrashpadInfo::kVersion); + + const MinidumpRVAList* list_annotations_0 = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_0->list_annotations); + EXPECT_FALSE(list_annotations_0); + + const MinidumpSimpleStringDictionary* simple_annotations_0 = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_0->simple_annotations); + ASSERT_TRUE(simple_annotations_0); + + ASSERT_EQ(simple_annotations_0->count, annotations_simple_map_0.size()); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations_0->entries[0].key), + kKey0B); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations_0->entries[0].value), + kValue0B); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations_0->entries[1].key), + kKey0A); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations_0->entries[1].value), + kValue0A); + + EXPECT_FALSE(MinidumpWritableAtLocationDescriptor( + string_file.string(), module_0->annotation_objects)); + + EXPECT_EQ(module_list->modules[1].minidump_module_list_index, 2u); + const MinidumpModuleCrashpadInfo* module_2 = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_list->modules[1].location); + ASSERT_TRUE(module_2); + + EXPECT_EQ(module_2->version, MinidumpModuleCrashpadInfo::kVersion); + + const MinidumpRVAList* list_annotations_2 = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_2->list_annotations); + EXPECT_FALSE(list_annotations_2); + + const MinidumpSimpleStringDictionary* simple_annotations_2 = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_2->simple_annotations); + ASSERT_TRUE(simple_annotations_2); + + ASSERT_EQ(simple_annotations_2->count, annotations_simple_map_2.size()); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations_2->entries[0].key), + kKey2); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString( + string_file.string(), simple_annotations_2->entries[0].value), + kValue2); + + EXPECT_FALSE(MinidumpWritableAtLocationDescriptor( + string_file.string(), module_2->annotation_objects)); + + EXPECT_EQ(module_list->modules[2].minidump_module_list_index, 3u); + const MinidumpModuleCrashpadInfo* module_3 = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_list->modules[2].location); + ASSERT_TRUE(module_3); + + EXPECT_EQ(module_3->version, MinidumpModuleCrashpadInfo::kVersion); + + const MinidumpRVAList* list_annotations_3 = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_3->list_annotations); + ASSERT_TRUE(list_annotations_3); + + ASSERT_EQ(list_annotations_3->count, annotations_vector_3.size()); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + list_annotations_3->children[0]), + kEntry3A); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + list_annotations_3->children[1]), + kEntry3B); + + const MinidumpSimpleStringDictionary* simple_annotations_3 = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_3->simple_annotations); + EXPECT_FALSE(simple_annotations_3); + + EXPECT_FALSE(MinidumpWritableAtLocationDescriptor( + string_file.string(), module_3->annotation_objects)); + + EXPECT_EQ(module_list->modules[3].minidump_module_list_index, 4u); + const MinidumpModuleCrashpadInfo* module_4 = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_list->modules[3].location); + ASSERT_TRUE(module_4); + + EXPECT_EQ(module_4->version, MinidumpModuleCrashpadInfo::kVersion); + + EXPECT_FALSE(MinidumpWritableAtLocationDescriptor( + string_file.string(), module_4->list_annotations)); + EXPECT_FALSE( + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_4->simple_annotations)); + + auto* annotation_list_4 = + MinidumpWritableAtLocationDescriptor( + string_file.string(), module_4->annotation_objects); + ASSERT_TRUE(annotation_list_4); + + ASSERT_EQ(annotation_list_4->count, 1u); + + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + annotation_list_4->objects[0].name), + annotation.name); + EXPECT_EQ(annotation_list_4->objects[0].type, annotation.type); + EXPECT_EQ(annotation_list_4->objects[0].reserved, 0u); + EXPECT_EQ(MinidumpByteArrayAtRVA(string_file.string(), + annotation_list_4->objects[0].value), + annotation.value); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_module_writer.cc b/shared/sentry/external/crashpad/minidump/minidump_module_writer.cc new file mode 100644 index 000000000..6e533524a --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_module_writer.cc @@ -0,0 +1,496 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_module_writer.h" + +#include + +#include +#include + +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "minidump/minidump_string_writer.h" +#include "minidump/minidump_writer_util.h" +#include "snapshot/module_snapshot.h" +#include "util/file/file_writer.h" +#include "util/misc/implicit_cast.h" +#include "util/numeric/in_range_cast.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { + +MinidumpModuleCodeViewRecordWriter::~MinidumpModuleCodeViewRecordWriter() {} + +namespace internal { + +template +MinidumpModuleCodeViewRecordPDBLinkWriter< + CodeViewRecordType>::MinidumpModuleCodeViewRecordPDBLinkWriter() + : MinidumpModuleCodeViewRecordWriter(), codeview_record_(), pdb_name_() { + codeview_record_.signature = CodeViewRecordType::kSignature; +} + +template +MinidumpModuleCodeViewRecordPDBLinkWriter< + CodeViewRecordType>::~MinidumpModuleCodeViewRecordPDBLinkWriter() {} + +template +size_t +MinidumpModuleCodeViewRecordPDBLinkWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + // NUL-terminate. + return offsetof(decltype(codeview_record_), pdb_name) + + (pdb_name_.size() + 1) * sizeof(pdb_name_[0]); +} + +template +bool MinidumpModuleCodeViewRecordPDBLinkWriter::WriteObject( + FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + WritableIoVec iov; + iov.iov_base = &codeview_record_; + iov.iov_len = offsetof(decltype(codeview_record_), pdb_name); + std::vector iovecs(1, iov); + + // NUL-terminate. + iov.iov_base = &pdb_name_[0]; + iov.iov_len = (pdb_name_.size() + 1) * sizeof(pdb_name_[0]); + iovecs.push_back(iov); + + return file_writer->WriteIoVec(&iovecs); +} + +} // namespace internal + +template class internal::MinidumpModuleCodeViewRecordPDBLinkWriter< + CodeViewRecordPDB20>; + +MinidumpModuleCodeViewRecordPDB20Writer:: + ~MinidumpModuleCodeViewRecordPDB20Writer() {} + +void MinidumpModuleCodeViewRecordPDB20Writer::SetTimestampAndAge( + time_t timestamp, + uint32_t age) { + DCHECK_EQ(state(), kStateMutable); + + internal::MinidumpWriterUtil::AssignTimeT(&codeview_record()->timestamp, + timestamp); + + codeview_record()->age = age; +} + +template class internal::MinidumpModuleCodeViewRecordPDBLinkWriter< + CodeViewRecordPDB70>; + +MinidumpModuleCodeViewRecordPDB70Writer:: + ~MinidumpModuleCodeViewRecordPDB70Writer() {} + +void MinidumpModuleCodeViewRecordPDB70Writer::InitializeFromSnapshot( + const ModuleSnapshot* module_snapshot) { + DCHECK_EQ(state(), kStateMutable); + + SetPDBName(module_snapshot->DebugFileName()); + + UUID uuid; + uint32_t age; + module_snapshot->UUIDAndAge(&uuid, &age); + SetUUIDAndAge(uuid, age); +} + +MinidumpModuleCodeViewRecordBuildIDWriter:: + MinidumpModuleCodeViewRecordBuildIDWriter() + : MinidumpModuleCodeViewRecordWriter(), build_id_() {} + +MinidumpModuleCodeViewRecordBuildIDWriter:: + ~MinidumpModuleCodeViewRecordBuildIDWriter() {} + +size_t MinidumpModuleCodeViewRecordBuildIDWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + return offsetof(CodeViewRecordBuildID, build_id) + build_id_.size(); +} + +void MinidumpModuleCodeViewRecordBuildIDWriter::SetBuildID( + const std::vector& build_id) { + DCHECK_EQ(state(), kStateMutable); + build_id_ = build_id; +} + +bool MinidumpModuleCodeViewRecordBuildIDWriter::WriteObject( + FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + CodeViewRecordBuildID cv; + cv.signature = CodeViewRecordBuildID::kSignature; + + WritableIoVec iov; + iov.iov_base = &cv; + iov.iov_len = offsetof(CodeViewRecordBuildID, build_id); + std::vector iovecs(1, iov); + + if (!build_id_.empty()) { + iov.iov_base = build_id_.data(); + iov.iov_len = build_id_.size(); + iovecs.push_back(iov); + } + + return file_writer->WriteIoVec(&iovecs); +} + +MinidumpModuleMiscDebugRecordWriter::MinidumpModuleMiscDebugRecordWriter() + : internal::MinidumpWritable(), + image_debug_misc_(), + data_(), + data_utf16_() {} + +MinidumpModuleMiscDebugRecordWriter::~MinidumpModuleMiscDebugRecordWriter() {} + +void MinidumpModuleMiscDebugRecordWriter::SetData(const std::string& data, + bool utf16) { + DCHECK_EQ(state(), kStateMutable); + + if (!utf16) { + data_utf16_.clear(); + image_debug_misc_.Unicode = 0; + data_ = data; + } else { + data_.clear(); + image_debug_misc_.Unicode = 1; + data_utf16_ = internal::MinidumpWriterUtil::ConvertUTF8ToUTF16(data); + } +} + +bool MinidumpModuleMiscDebugRecordWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + // NUL-terminate. + if (!image_debug_misc_.Unicode) { + DCHECK(data_utf16_.empty()); + image_debug_misc_.Length = base::checked_cast( + offsetof(decltype(image_debug_misc_), Data) + + (data_.size() + 1) * sizeof(data_[0])); + } else { + DCHECK(data_.empty()); + image_debug_misc_.Length = base::checked_cast( + offsetof(decltype(image_debug_misc_), Data) + + (data_utf16_.size() + 1) * sizeof(data_utf16_[0])); + } + + return true; +} + +size_t MinidumpModuleMiscDebugRecordWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return image_debug_misc_.Length; +} + +bool MinidumpModuleMiscDebugRecordWriter::WriteObject( + FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + const size_t base_length = offsetof(decltype(image_debug_misc_), Data); + + WritableIoVec iov; + iov.iov_base = &image_debug_misc_; + iov.iov_len = base_length; + std::vector iovecs(1, iov); + + if (!image_debug_misc_.Unicode) { + DCHECK(data_utf16_.empty()); + iov.iov_base = &data_[0]; + } else { + DCHECK(data_.empty()); + iov.iov_base = &data_utf16_[0]; + } + iov.iov_len = image_debug_misc_.Length - base_length; + iovecs.push_back(iov); + + return file_writer->WriteIoVec(&iovecs); +} + +MinidumpModuleWriter::MinidumpModuleWriter() + : MinidumpWritable(), + module_(), + name_(), + codeview_record_(nullptr), + misc_debug_record_(nullptr) { + module_.VersionInfo.dwSignature = VS_FFI_SIGNATURE; + module_.VersionInfo.dwStrucVersion = VS_FFI_STRUCVERSION; +} + +MinidumpModuleWriter::~MinidumpModuleWriter() {} + +void MinidumpModuleWriter::InitializeFromSnapshot( + const ModuleSnapshot* module_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(!name_); + DCHECK(!codeview_record_); + DCHECK(!misc_debug_record_); + + SetName(module_snapshot->Name()); + + SetImageBaseAddress(module_snapshot->Address()); + SetImageSize(InRangeCast(module_snapshot->Size(), + std::numeric_limits::max())); + SetTimestamp(module_snapshot->Timestamp()); + + uint16_t v[4]; + module_snapshot->FileVersion(&v[0], &v[1], &v[2], &v[3]); + SetFileVersion(v[0], v[1], v[2], v[3]); + + module_snapshot->SourceVersion(&v[0], &v[1], &v[2], &v[3]); + SetProductVersion(v[0], v[1], v[2], v[3]); + + uint32_t file_type; + switch (module_snapshot->GetModuleType()) { + case ModuleSnapshot::kModuleTypeExecutable: + file_type = VFT_APP; + break; + case ModuleSnapshot::kModuleTypeSharedLibrary: + case ModuleSnapshot::kModuleTypeLoadableModule: + file_type = VFT_DLL; + break; + default: + file_type = VFT_UNKNOWN; + break; + } + SetFileTypeAndSubtype(file_type, VFT2_UNKNOWN); + + auto build_id = module_snapshot->BuildID(); + + std::unique_ptr codeview_record; + if (!build_id.empty()) { + auto cv_record_build_id = + std::make_unique(); + cv_record_build_id->SetBuildID(build_id); + codeview_record = std::move(cv_record_build_id); + } else { + auto cv_record_pdb70 = + std::make_unique(); + cv_record_pdb70->InitializeFromSnapshot(module_snapshot); + codeview_record = std::move(cv_record_pdb70); + } + + SetCodeViewRecord(std::move(codeview_record)); +} + +const MINIDUMP_MODULE* MinidumpModuleWriter::MinidumpModule() const { + DCHECK_EQ(state(), kStateWritable); + + return &module_; +} + +void MinidumpModuleWriter::SetName(const std::string& name) { + DCHECK_EQ(state(), kStateMutable); + + if (!name_) { + name_.reset(new internal::MinidumpUTF16StringWriter()); + } + name_->SetUTF8(name); +} + +void MinidumpModuleWriter::SetCodeViewRecord( + std::unique_ptr codeview_record) { + DCHECK_EQ(state(), kStateMutable); + + codeview_record_ = std::move(codeview_record); +} + +void MinidumpModuleWriter::SetMiscDebugRecord( + std::unique_ptr misc_debug_record) { + DCHECK_EQ(state(), kStateMutable); + + misc_debug_record_ = std::move(misc_debug_record); +} + +void MinidumpModuleWriter::SetTimestamp(time_t timestamp) { + DCHECK_EQ(state(), kStateMutable); + + internal::MinidumpWriterUtil::AssignTimeT(&module_.TimeDateStamp, timestamp); +} + +void MinidumpModuleWriter::SetFileVersion(uint16_t version_0, + uint16_t version_1, + uint16_t version_2, + uint16_t version_3) { + DCHECK_EQ(state(), kStateMutable); + + module_.VersionInfo.dwFileVersionMS = + (implicit_cast(version_0) << 16) | version_1; + module_.VersionInfo.dwFileVersionLS = + (implicit_cast(version_2) << 16) | version_3; +} + +void MinidumpModuleWriter::SetProductVersion(uint16_t version_0, + uint16_t version_1, + uint16_t version_2, + uint16_t version_3) { + DCHECK_EQ(state(), kStateMutable); + + module_.VersionInfo.dwProductVersionMS = + (implicit_cast(version_0) << 16) | version_1; + module_.VersionInfo.dwProductVersionLS = + (implicit_cast(version_2) << 16) | version_3; +} + +void MinidumpModuleWriter::SetFileFlagsAndMask(uint32_t file_flags, + uint32_t file_flags_mask) { + DCHECK_EQ(state(), kStateMutable); + DCHECK_EQ(file_flags & ~file_flags_mask, 0u); + + module_.VersionInfo.dwFileFlags = file_flags; + module_.VersionInfo.dwFileFlagsMask = file_flags_mask; +} + +bool MinidumpModuleWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + CHECK(name_); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + name_->RegisterRVA(&module_.ModuleNameRva); + + if (codeview_record_) { + codeview_record_->RegisterLocationDescriptor(&module_.CvRecord); + } + + if (misc_debug_record_) { + misc_debug_record_->RegisterLocationDescriptor(&module_.MiscRecord); + } + + return true; +} + +size_t MinidumpModuleWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + // This object doesn’t directly write anything itself. Its MINIDUMP_MODULE is + // written by its parent as part of a MINIDUMP_MODULE_LIST, and its children + // are responsible for writing themselves. + return 0; +} + +std::vector MinidumpModuleWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + DCHECK(name_); + + std::vector children; + children.push_back(name_.get()); + if (codeview_record_) { + children.push_back(codeview_record_.get()); + } + if (misc_debug_record_) { + children.push_back(misc_debug_record_.get()); + } + + return children; +} + +bool MinidumpModuleWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + // This object doesn’t directly write anything itself. Its MINIDUMP_MODULE is + // written by its parent as part of a MINIDUMP_MODULE_LIST, and its children + // are responsible for writing themselves. + return true; +} + +MinidumpModuleListWriter::MinidumpModuleListWriter() + : MinidumpStreamWriter(), modules_(), module_list_base_() {} + +MinidumpModuleListWriter::~MinidumpModuleListWriter() {} + +void MinidumpModuleListWriter::InitializeFromSnapshot( + const std::vector& module_snapshots) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(modules_.empty()); + + for (const ModuleSnapshot* module_snapshot : module_snapshots) { + auto module = std::make_unique(); + module->InitializeFromSnapshot(module_snapshot); + AddModule(std::move(module)); + } +} + +void MinidumpModuleListWriter::AddModule( + std::unique_ptr module) { + DCHECK_EQ(state(), kStateMutable); + + modules_.push_back(std::move(module)); +} + +bool MinidumpModuleListWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpStreamWriter::Freeze()) { + return false; + } + + size_t module_count = modules_.size(); + if (!AssignIfInRange(&module_list_base_.NumberOfModules, module_count)) { + LOG(ERROR) << "module_count " << module_count << " out of range"; + return false; + } + + return true; +} + +size_t MinidumpModuleListWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return sizeof(module_list_base_) + modules_.size() * sizeof(MINIDUMP_MODULE); +} + +std::vector MinidumpModuleListWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + + std::vector children; + for (const auto& module : modules_) { + children.push_back(module.get()); + } + + return children; +} + +bool MinidumpModuleListWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + WritableIoVec iov; + iov.iov_base = &module_list_base_; + iov.iov_len = sizeof(module_list_base_); + std::vector iovecs(1, iov); + + for (const auto& module : modules_) { + iov.iov_base = module->MinidumpModule(); + iov.iov_len = sizeof(MINIDUMP_MODULE); + iovecs.push_back(iov); + } + + return file_writer->WriteIoVec(&iovecs); +} + +MinidumpStreamType MinidumpModuleListWriter::StreamType() const { + return kMinidumpStreamTypeModuleList; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_module_writer.h b/shared/sentry/external/crashpad/minidump/minidump_module_writer.h new file mode 100644 index 000000000..d450892c8 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_module_writer.h @@ -0,0 +1,392 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_MODULE_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_MODULE_WRITER_H_ + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_stream_writer.h" +#include "minidump/minidump_writable.h" + +namespace crashpad { + +class ModuleSnapshot; + +namespace internal { +class MinidumpUTF16StringWriter; +} // namespace internal + +//! \brief The base class for writers of CodeView records referenced by +//! MINIDUMP_MODULE::CvRecord in minidump files. +class MinidumpModuleCodeViewRecordWriter : public internal::MinidumpWritable { + public: + MinidumpModuleCodeViewRecordWriter( + const MinidumpModuleCodeViewRecordWriter&) = delete; + MinidumpModuleCodeViewRecordWriter& operator=( + const MinidumpModuleCodeViewRecordWriter&) = delete; + + ~MinidumpModuleCodeViewRecordWriter() override; + + protected: + MinidumpModuleCodeViewRecordWriter() : MinidumpWritable() {} +}; + +namespace internal { + +//! \brief The base class for writers of CodeView records that serve as links to +//! `.pdb` (program database) files. +template +class MinidumpModuleCodeViewRecordPDBLinkWriter + : public MinidumpModuleCodeViewRecordWriter { + public: + MinidumpModuleCodeViewRecordPDBLinkWriter( + const MinidumpModuleCodeViewRecordPDBLinkWriter&) = delete; + MinidumpModuleCodeViewRecordPDBLinkWriter& operator=( + const MinidumpModuleCodeViewRecordPDBLinkWriter&) = delete; + + //! \brief Sets the name of the `.pdb` file being linked to. + void SetPDBName(const std::string& pdb_name) { pdb_name_ = pdb_name; } + + protected: + MinidumpModuleCodeViewRecordPDBLinkWriter(); + ~MinidumpModuleCodeViewRecordPDBLinkWriter() override; + + // MinidumpWritable: + size_t SizeOfObject() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + //! \brief Returns a pointer to the raw CodeView record’s data. + //! + //! Subclasses can use this to set fields in their codeview records other than + //! the `pdb_name` field. + CodeViewRecordType* codeview_record() { return &codeview_record_; } + + private: + CodeViewRecordType codeview_record_; + std::string pdb_name_; +}; + +} // namespace internal + +//! \brief The writer for a CodeViewRecordPDB20 object in a minidump file. +//! +//! Most users will want MinidumpModuleCodeViewRecordPDB70Writer or +//! MinidumpModuleCodeViewRecordBuildIDWriter instead. +class MinidumpModuleCodeViewRecordPDB20Writer final + : public internal::MinidumpModuleCodeViewRecordPDBLinkWriter< + CodeViewRecordPDB20> { + public: + MinidumpModuleCodeViewRecordPDB20Writer() + : internal::MinidumpModuleCodeViewRecordPDBLinkWriter< + CodeViewRecordPDB20>() {} + + MinidumpModuleCodeViewRecordPDB20Writer( + const MinidumpModuleCodeViewRecordPDB20Writer&) = delete; + MinidumpModuleCodeViewRecordPDB20Writer& operator=( + const MinidumpModuleCodeViewRecordPDB20Writer&) = delete; + + ~MinidumpModuleCodeViewRecordPDB20Writer() override; + + //! \brief Sets CodeViewRecordPDB20::timestamp and CodeViewRecordPDB20::age. + void SetTimestampAndAge(time_t timestamp, uint32_t age); +}; + +//! \brief The writer for a CodeViewRecordPDB70 object in a minidump file. +class MinidumpModuleCodeViewRecordPDB70Writer final + : public internal::MinidumpModuleCodeViewRecordPDBLinkWriter< + CodeViewRecordPDB70> { + public: + MinidumpModuleCodeViewRecordPDB70Writer() + : internal::MinidumpModuleCodeViewRecordPDBLinkWriter< + CodeViewRecordPDB70>() {} + + MinidumpModuleCodeViewRecordPDB70Writer( + const MinidumpModuleCodeViewRecordPDB70Writer&) = delete; + MinidumpModuleCodeViewRecordPDB70Writer& operator=( + const MinidumpModuleCodeViewRecordPDB70Writer&) = delete; + + ~MinidumpModuleCodeViewRecordPDB70Writer() override; + + //! \brief Initializes the CodeViewRecordPDB70 based on \a module_snapshot. + //! + //! \param[in] module_snapshot The module snapshot to use as source data. + //! + //! \note Valid in #kStateMutable. No mutator methods may be called before + //! this method, and it is not normally necessary to call any mutator + //! methods after this method. + void InitializeFromSnapshot(const ModuleSnapshot* module_snapshot); + + //! \brief Sets CodeViewRecordPDB70::uuid and CodeViewRecordPDB70::age. + void SetUUIDAndAge(const UUID& uuid, uint32_t age) { + codeview_record()->uuid = uuid; + codeview_record()->age = age; + } +}; + +//! \brief The writer for a CodeViewRecordBuildID object in a minidump file. +class MinidumpModuleCodeViewRecordBuildIDWriter final + : public MinidumpModuleCodeViewRecordWriter { + public: + MinidumpModuleCodeViewRecordBuildIDWriter(); + + MinidumpModuleCodeViewRecordBuildIDWriter( + const MinidumpModuleCodeViewRecordBuildIDWriter&) = delete; + MinidumpModuleCodeViewRecordBuildIDWriter& operator=( + const MinidumpModuleCodeViewRecordBuildIDWriter&) = delete; + + ~MinidumpModuleCodeViewRecordBuildIDWriter() override; + + //! \brief Sets the build ID used for symbol lookup. + void SetBuildID(const std::vector& build_id); + + private: + // MinidumpWritable: + size_t SizeOfObject() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + std::vector build_id_; +}; + +//! \brief The writer for an IMAGE_DEBUG_MISC object in a minidump file. +//! +//! Most users will want MinidumpModuleCodeViewRecordPDB70Writer instead. +class MinidumpModuleMiscDebugRecordWriter final + : public internal::MinidumpWritable { + public: + MinidumpModuleMiscDebugRecordWriter(); + + MinidumpModuleMiscDebugRecordWriter( + const MinidumpModuleMiscDebugRecordWriter&) = delete; + MinidumpModuleMiscDebugRecordWriter& operator=( + const MinidumpModuleMiscDebugRecordWriter&) = delete; + + ~MinidumpModuleMiscDebugRecordWriter() override; + + //! \brief Sets IMAGE_DEBUG_MISC::DataType. + void SetDataType(uint32_t data_type) { + image_debug_misc_.DataType = data_type; + } + + //! \brief Sets IMAGE_DEBUG_MISC::Data, IMAGE_DEBUG_MISC::Length, and + //! IMAGE_DEBUG_MISC::Unicode. + //! + //! If \a utf16 is `true`, \a data will be treated as UTF-8 data and will be + //! converted to UTF-16, and IMAGE_DEBUG_MISC::Unicode will be set to `1`. + //! Otherwise, \a data will be used as-is and IMAGE_DEBUG_MISC::Unicode will + //! be set to `0`. + void SetData(const std::string& data, bool utf16); + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + IMAGE_DEBUG_MISC image_debug_misc_; + std::string data_; + std::u16string data_utf16_; +}; + +//! \brief The writer for a MINIDUMP_MODULE object in a minidump file. +//! +//! Because MINIDUMP_MODULE objects only appear as elements of +//! MINIDUMP_MODULE_LIST objects, this class does not write any data on its own. +//! It makes its MINIDUMP_MODULE data available to its MinidumpModuleListWriter +//! parent, which writes it as part of a MINIDUMP_MODULE_LIST. +class MinidumpModuleWriter final : public internal::MinidumpWritable { + public: + MinidumpModuleWriter(); + + MinidumpModuleWriter(const MinidumpModuleWriter&) = delete; + MinidumpModuleWriter& operator=(const MinidumpModuleWriter&) = delete; + + ~MinidumpModuleWriter() override; + + //! \brief Initializes the MINIDUMP_MODULE based on \a module_snapshot. + //! + //! \param[in] module_snapshot The module snapshot to use as source data. + //! + //! \note Valid in #kStateMutable. No mutator methods may be called before + //! this method, and it is not normally necessary to call any mutator + //! methods after this method. + void InitializeFromSnapshot(const ModuleSnapshot* module_snapshot); + + //! \brief Returns a MINIDUMP_MODULE referencing this object’s data. + //! + //! This method is expected to be called by a MinidumpModuleListWriter in + //! order to obtain a MINIDUMP_MODULE to include in its list. + //! + //! \note Valid in #kStateWritable. + const MINIDUMP_MODULE* MinidumpModule() const; + + //! \brief Arranges for MINIDUMP_MODULE::ModuleNameRva to point to a + //! MINIDUMP_STRING containing \a name. + //! + //! A name is required in all MINIDUMP_MODULE objects. + //! + //! \note Valid in #kStateMutable. + void SetName(const std::string& name); + + //! \brief Arranges for MINIDUMP_MODULE::CvRecord to point to a CodeView + //! record to be written by \a codeview_record. + //! + //! This object takes ownership of \a codeview_record and becomes its parent + //! in the overall tree of internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void SetCodeViewRecord( + std::unique_ptr codeview_record); + + //! \brief Arranges for MINIDUMP_MODULE::MiscRecord to point to an + //! IMAGE_DEBUG_MISC object to be written by \a misc_debug_record. + //! + //! This object takes ownership of \a misc_debug_record and becomes its parent + //! in the overall tree of internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void SetMiscDebugRecord( + std::unique_ptr misc_debug_record); + + //! \brief Sets IMAGE_DEBUG_MISC::BaseOfImage. + void SetImageBaseAddress(uint64_t image_base_address) { + module_.BaseOfImage = image_base_address; + } + + //! \brief Sets IMAGE_DEBUG_MISC::SizeOfImage. + void SetImageSize(uint32_t image_size) { module_.SizeOfImage = image_size; } + + //! \brief Sets IMAGE_DEBUG_MISC::CheckSum. + void SetChecksum(uint32_t checksum) { module_.CheckSum = checksum; } + + //! \brief Sets IMAGE_DEBUG_MISC::TimeDateStamp. + //! + //! \note Valid in #kStateMutable. + void SetTimestamp(time_t timestamp); + + //! \brief Sets \ref VS_FIXEDFILEINFO::dwFileVersionMS + //! "IMAGE_DEBUG_MISC::VersionInfo::dwFileVersionMS" and \ref + //! VS_FIXEDFILEINFO::dwFileVersionLS + //! "IMAGE_DEBUG_MISC::VersionInfo::dwFileVersionLS". + //! + //! \note Valid in #kStateMutable. + void SetFileVersion(uint16_t version_0, + uint16_t version_1, + uint16_t version_2, + uint16_t version_3); + + //! \brief Sets \ref VS_FIXEDFILEINFO::dwProductVersionMS + //! "IMAGE_DEBUG_MISC::VersionInfo::dwProductVersionMS" and \ref + //! VS_FIXEDFILEINFO::dwProductVersionLS + //! "IMAGE_DEBUG_MISC::VersionInfo::dwProductVersionLS". + //! + //! \note Valid in #kStateMutable. + void SetProductVersion(uint16_t version_0, + uint16_t version_1, + uint16_t version_2, + uint16_t version_3); + + //! \brief Sets \ref VS_FIXEDFILEINFO::dwFileFlags + //! "IMAGE_DEBUG_MISC::VersionInfo::dwFileFlags" and \ref + //! VS_FIXEDFILEINFO::dwFileFlagsMask + //! "IMAGE_DEBUG_MISC::VersionInfo::dwFileFlagsMask". + //! + //! \note Valid in #kStateMutable. + void SetFileFlagsAndMask(uint32_t file_flags, uint32_t file_flags_mask); + + //! \brief Sets \ref VS_FIXEDFILEINFO::dwFileOS + //! "IMAGE_DEBUG_MISC::VersionInfo::dwFileOS". + void SetFileOS(uint32_t file_os) { module_.VersionInfo.dwFileOS = file_os; } + + //! \brief Sets \ref VS_FIXEDFILEINFO::dwFileType + //! "IMAGE_DEBUG_MISC::VersionInfo::dwFileType" and \ref + //! VS_FIXEDFILEINFO::dwFileSubtype + //! "IMAGE_DEBUG_MISC::VersionInfo::dwFileSubtype". + void SetFileTypeAndSubtype(uint32_t file_type, uint32_t file_subtype) { + module_.VersionInfo.dwFileType = file_type; + module_.VersionInfo.dwFileSubtype = file_subtype; + } + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + MINIDUMP_MODULE module_; + std::unique_ptr name_; + std::unique_ptr codeview_record_; + std::unique_ptr misc_debug_record_; +}; + +//! \brief The writer for a MINIDUMP_MODULE_LIST stream in a minidump file, +//! containing a list of MINIDUMP_MODULE objects. +class MinidumpModuleListWriter final : public internal::MinidumpStreamWriter { + public: + MinidumpModuleListWriter(); + + MinidumpModuleListWriter(const MinidumpModuleListWriter&) = delete; + MinidumpModuleListWriter& operator=(const MinidumpModuleListWriter&) = delete; + + ~MinidumpModuleListWriter() override; + + //! \brief Adds an initialized MINIDUMP_MODULE for each module in \a + //! module_snapshots to the MINIDUMP_MODULE_LIST. + //! + //! \param[in] module_snapshots The module snapshots to use as source data. + //! + //! \note Valid in #kStateMutable. AddModule() may not be called before this + //! method, and it is not normally necessary to call AddModule() after + //! this method. + void InitializeFromSnapshot( + const std::vector& module_snapshots); + + //! \brief Adds a MinidumpModuleWriter to the MINIDUMP_MODULE_LIST. + //! + //! This object takes ownership of \a module and becomes its parent in the + //! overall tree of internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void AddModule(std::unique_ptr module); + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpStreamWriter: + MinidumpStreamType StreamType() const override; + + private: + std::vector> modules_; + MINIDUMP_MODULE_LIST module_list_base_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_MODULE_WRITER_H_ diff --git a/shared/sentry/external/crashpad/minidump/minidump_module_writer_test.cc b/shared/sentry/external/crashpad/minidump/minidump_module_writer_test.cc new file mode 100644 index 000000000..e189d77e8 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_module_writer_test.cc @@ -0,0 +1,947 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_module_writer.h" + +#include +#include + +#include +#include + +#include "base/format_macros.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "gtest/gtest.h" +#include "minidump/minidump_file_writer.h" +#include "minidump/test/minidump_file_writer_test_util.h" +#include "minidump/test/minidump_string_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "snapshot/test/test_module_snapshot.h" +#include "test/gtest_death.h" +#include "util/file/string_file.h" +#include "util/misc/implicit_cast.h" +#include "util/misc/uuid.h" + +namespace crashpad { +namespace test { +namespace { + +void GetModuleListStream(const std::string& file_contents, + const MINIDUMP_MODULE_LIST** module_list) { + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kModuleListStreamOffset = + kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + constexpr size_t kModulesOffset = + kModuleListStreamOffset + sizeof(MINIDUMP_MODULE_LIST); + + ASSERT_GE(file_contents.size(), kModulesOffset); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(file_contents, &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0)); + ASSERT_TRUE(directory); + + ASSERT_EQ(directory[0].StreamType, kMinidumpStreamTypeModuleList); + EXPECT_EQ(directory[0].Location.Rva, kModuleListStreamOffset); + + *module_list = MinidumpWritableAtLocationDescriptor( + file_contents, directory[0].Location); + ASSERT_TRUE(module_list); +} + +TEST(MinidumpModuleWriter, EmptyModuleList) { + MinidumpFileWriter minidump_file_writer; + auto module_list_writer = std::make_unique(); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(module_list_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_MODULE_LIST)); + + const MINIDUMP_MODULE_LIST* module_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetModuleListStream(string_file.string(), &module_list)); + + EXPECT_EQ(module_list->NumberOfModules, 0u); +} + +// If |expected_pdb_name| is not nullptr, |codeview_record| is used to locate a +// CodeView record in |file_contents|, and its fields are compared against the +// |expected_pdb_*| values. If |expected_pdb_uuid| is supplied, the CodeView +// record must be a PDB 7.0 link, otherwise, it must be a PDB 2.0 link. If +// |expected_pdb_name| is nullptr, |codeview_record| must not point to anything. +void ExpectCodeViewRecord(const MINIDUMP_LOCATION_DESCRIPTOR* codeview_record, + const std::string& file_contents, + const char* expected_pdb_name, + const UUID* expected_pdb_uuid, + time_t expected_pdb_timestamp, + uint32_t expected_pdb_age) { + if (expected_pdb_name) { + EXPECT_NE(codeview_record->Rva, 0u); + + std::string observed_pdb_name; + if (expected_pdb_uuid) { + // The CodeView record should be a PDB 7.0 link. + const CodeViewRecordPDB70* codeview_pdb70_record = + MinidumpWritableAtLocationDescriptor( + file_contents, *codeview_record); + ASSERT_TRUE(codeview_pdb70_record); + EXPECT_EQ(memcmp(expected_pdb_uuid, + &codeview_pdb70_record->uuid, + sizeof(codeview_pdb70_record->uuid)), + 0); + EXPECT_EQ(codeview_pdb70_record->age, expected_pdb_age); + + observed_pdb_name.assign( + reinterpret_cast(&codeview_pdb70_record->pdb_name[0]), + codeview_record->DataSize - offsetof(CodeViewRecordPDB70, pdb_name)); + } else { + // The CodeView record should be a PDB 2.0 link. + const CodeViewRecordPDB20* codeview_pdb20_record = + MinidumpWritableAtLocationDescriptor( + file_contents, *codeview_record); + ASSERT_TRUE(codeview_pdb20_record); + EXPECT_EQ(codeview_pdb20_record->timestamp, + static_cast(expected_pdb_timestamp)); + EXPECT_EQ(codeview_pdb20_record->age, expected_pdb_age); + + observed_pdb_name.assign( + reinterpret_cast(&codeview_pdb20_record->pdb_name[0]), + codeview_record->DataSize - offsetof(CodeViewRecordPDB20, pdb_name)); + } + + // Check for, and then remove, the NUL terminator. + EXPECT_EQ(observed_pdb_name[observed_pdb_name.size() - 1], '\0'); + observed_pdb_name.resize(observed_pdb_name.size() - 1); + + EXPECT_EQ(observed_pdb_name, expected_pdb_name); + } else { + // There should be no CodeView record. + EXPECT_EQ(codeview_record->DataSize, 0u); + EXPECT_EQ(codeview_record->Rva, 0u); + } +} + +// If |expected_debug_name| is not nullptr, |misc_record| is used to locate a +// miscellanous debugging record in |file_contents|, and its fields are compared +// against the the |expected_debug_*| values. If |expected_debug_name| is +// nullptr, |misc_record| must not point to anything. +void ExpectMiscellaneousDebugRecord( + const MINIDUMP_LOCATION_DESCRIPTOR* misc_record, + const std::string& file_contents, + const char* expected_debug_name, + uint32_t expected_debug_type, + bool expected_debug_utf16) { + if (expected_debug_name) { + EXPECT_NE(misc_record->Rva, 0u); + const IMAGE_DEBUG_MISC* misc_debug_record = + MinidumpWritableAtLocationDescriptor(file_contents, + *misc_record); + ASSERT_TRUE(misc_debug_record); + EXPECT_EQ(misc_debug_record->DataType, expected_debug_type); + EXPECT_EQ(misc_debug_record->Unicode != 0, expected_debug_utf16); + EXPECT_EQ(misc_debug_record->Reserved[0], 0u); + EXPECT_EQ(misc_debug_record->Reserved[1], 0u); + EXPECT_EQ(misc_debug_record->Reserved[2], 0u); + + // Check for the NUL terminator. + size_t bytes_available = + misc_debug_record->Length - offsetof(IMAGE_DEBUG_MISC, Data); + EXPECT_EQ(misc_debug_record->Data[bytes_available - 1], '\0'); + std::string observed_data( + reinterpret_cast(misc_debug_record->Data)); + + size_t bytes_used; + if (misc_debug_record->Unicode) { + std::u16string observed_data_utf16( + reinterpret_cast(misc_debug_record->Data)); + bytes_used = (observed_data_utf16.size() + 1) * sizeof(char16_t); + observed_data = base::UTF16ToUTF8(observed_data_utf16); + } else { + observed_data = reinterpret_cast(misc_debug_record->Data); + bytes_used = (observed_data.size() + 1) * sizeof(char); + } + EXPECT_LE(bytes_used, bytes_available); + + // Make sure that any padding bytes after the first NUL are also NUL. + for (size_t index = bytes_used; index < bytes_available; ++index) { + EXPECT_EQ(misc_debug_record->Data[index], '\0'); + } + + EXPECT_EQ(observed_data, expected_debug_name); + } else { + // There should be no miscellaneous debugging record. + EXPECT_EQ(misc_record->DataSize, 0u); + EXPECT_EQ(misc_record->Rva, 0u); + } +} + +// ExpectModule() verifies that |expected| matches |observed|. Fields that are +// supposed to contain constant magic numbers are verified against the expected +// constants instead of |expected|. Reserved fields are verified to be 0. RVA +// and MINIDUMP_LOCATION_DESCRIPTOR fields are not verified against |expected|. +// Instead, |ModuleNameRva| is used to locate the module name, which is compared +// against |expected_module_name|. ExpectCodeViewRecord() and +// ExpectMiscellaneousDebugRecord() are used to verify the |CvRecord| and +// |MiscRecord| fields against |expected_pdb_*| and |expected_debug_*| +// parameters, respectively. +void ExpectModule(const MINIDUMP_MODULE* expected, + const MINIDUMP_MODULE* observed, + const std::string& file_contents, + const std::string& expected_module_name, + const char* expected_pdb_name, + const UUID* expected_pdb_uuid, + time_t expected_pdb_timestamp, + uint32_t expected_pdb_age, + const char* expected_debug_name, + uint32_t expected_debug_type, + bool expected_debug_utf16) { + MINIDUMP_MODULE expected_module, observed_module; + memcpy(&expected_module, expected, sizeof(expected_module)); + memcpy(&observed_module, observed, sizeof(observed_module)); + + EXPECT_EQ(observed_module.BaseOfImage, expected_module.BaseOfImage); + EXPECT_EQ(observed_module.SizeOfImage, expected_module.SizeOfImage); + EXPECT_EQ(observed_module.CheckSum, expected_module.CheckSum); + EXPECT_EQ(observed_module.TimeDateStamp, expected_module.TimeDateStamp); + EXPECT_EQ(observed_module.VersionInfo.dwSignature, + implicit_cast(VS_FFI_SIGNATURE)); + EXPECT_EQ(observed_module.VersionInfo.dwStrucVersion, + implicit_cast(VS_FFI_STRUCVERSION)); + EXPECT_EQ(observed_module.VersionInfo.dwFileVersionMS, + expected_module.VersionInfo.dwFileVersionMS); + EXPECT_EQ(observed_module.VersionInfo.dwFileVersionLS, + expected_module.VersionInfo.dwFileVersionLS); + EXPECT_EQ(observed_module.VersionInfo.dwProductVersionMS, + expected_module.VersionInfo.dwProductVersionMS); + EXPECT_EQ(observed_module.VersionInfo.dwProductVersionLS, + expected_module.VersionInfo.dwProductVersionLS); + EXPECT_EQ(observed_module.VersionInfo.dwFileFlagsMask, + expected_module.VersionInfo.dwFileFlagsMask); + EXPECT_EQ(observed_module.VersionInfo.dwFileFlags, + expected_module.VersionInfo.dwFileFlags); + EXPECT_EQ(observed_module.VersionInfo.dwFileOS, + expected_module.VersionInfo.dwFileOS); + EXPECT_EQ(observed_module.VersionInfo.dwFileType, + expected_module.VersionInfo.dwFileType); + EXPECT_EQ(observed_module.VersionInfo.dwFileSubtype, + expected_module.VersionInfo.dwFileSubtype); + EXPECT_EQ(observed_module.VersionInfo.dwFileDateMS, + expected_module.VersionInfo.dwFileDateMS); + EXPECT_EQ(observed_module.VersionInfo.dwFileDateLS, + expected_module.VersionInfo.dwFileDateLS); + + uint64_t reserved0, reserved1; + memcpy(&reserved0, &observed_module.Reserved0, sizeof(reserved0)); + memcpy(&reserved1, &observed_module.Reserved1, sizeof(reserved1)); + + EXPECT_EQ(reserved0, 0u); + EXPECT_EQ(reserved1, 0u); + + EXPECT_NE(observed_module.ModuleNameRva, 0u); + std::u16string observed_module_name_utf16 = + MinidumpStringAtRVAAsString(file_contents, observed_module.ModuleNameRva); + std::u16string expected_module_name_utf16 = + base::UTF8ToUTF16(expected_module_name); + EXPECT_EQ(observed_module_name_utf16, expected_module_name_utf16); + + ASSERT_NO_FATAL_FAILURE(ExpectCodeViewRecord(&observed_module.CvRecord, + file_contents, + expected_pdb_name, + expected_pdb_uuid, + expected_pdb_timestamp, + expected_pdb_age)); + + ASSERT_NO_FATAL_FAILURE( + ExpectMiscellaneousDebugRecord(&observed_module.MiscRecord, + file_contents, + expected_debug_name, + expected_debug_type, + expected_debug_utf16)); +} + +// ExpectModuleWithBuildIDCv() is like ExpectModule( but expects the module to +// have a BuildID CodeView Record. +void ExpectModuleWithBuildIDCv(const MINIDUMP_MODULE* expected, + const MINIDUMP_MODULE* observed, + const std::string& file_contents, + const std::string& expected_module_name, + const std::vector& expected_build_id) { + EXPECT_EQ(observed->BaseOfImage, expected->BaseOfImage); + EXPECT_EQ(observed->SizeOfImage, expected->SizeOfImage); + EXPECT_EQ(observed->CheckSum, expected->CheckSum); + EXPECT_EQ(observed->TimeDateStamp, expected->TimeDateStamp); + EXPECT_EQ(observed->VersionInfo.dwSignature, + implicit_cast(VS_FFI_SIGNATURE)); + EXPECT_EQ(observed->VersionInfo.dwStrucVersion, + implicit_cast(VS_FFI_STRUCVERSION)); + EXPECT_EQ(observed->VersionInfo.dwFileVersionMS, + expected->VersionInfo.dwFileVersionMS); + EXPECT_EQ(observed->VersionInfo.dwFileVersionLS, + expected->VersionInfo.dwFileVersionLS); + EXPECT_EQ(observed->VersionInfo.dwProductVersionMS, + expected->VersionInfo.dwProductVersionMS); + EXPECT_EQ(observed->VersionInfo.dwProductVersionLS, + expected->VersionInfo.dwProductVersionLS); + EXPECT_EQ(observed->VersionInfo.dwFileFlagsMask, + expected->VersionInfo.dwFileFlagsMask); + EXPECT_EQ(observed->VersionInfo.dwFileFlags, + expected->VersionInfo.dwFileFlags); + EXPECT_EQ(observed->VersionInfo.dwFileOS, expected->VersionInfo.dwFileOS); + EXPECT_EQ(observed->VersionInfo.dwFileType, expected->VersionInfo.dwFileType); + EXPECT_EQ(observed->VersionInfo.dwFileSubtype, + expected->VersionInfo.dwFileSubtype); + EXPECT_EQ(observed->VersionInfo.dwFileDateMS, + expected->VersionInfo.dwFileDateMS); + EXPECT_EQ(observed->VersionInfo.dwFileDateLS, + expected->VersionInfo.dwFileDateLS); + + uint64_t reserved0, reserved1; + memcpy(&reserved0, &observed->Reserved0, sizeof(reserved0)); + memcpy(&reserved1, &observed->Reserved1, sizeof(reserved1)); + + EXPECT_EQ(reserved0, 0u); + EXPECT_EQ(reserved1, 0u); + + EXPECT_NE(observed->ModuleNameRva, 0u); + std::u16string observed_module_name_utf16 = + MinidumpStringAtRVAAsString(file_contents, observed->ModuleNameRva); + std::u16string expected_module_name_utf16 = + base::UTF8ToUTF16(expected_module_name); + EXPECT_EQ(observed_module_name_utf16, expected_module_name_utf16); + + const CodeViewRecordBuildID* codeview_build_id_record = + MinidumpWritableAtLocationDescriptor( + file_contents, observed->CvRecord); + ASSERT_TRUE(codeview_build_id_record); + EXPECT_EQ(memcmp(expected_build_id.data(), + &codeview_build_id_record->build_id, + expected_build_id.size()), + 0); +} + +TEST(MinidumpModuleWriter, EmptyModule) { + MinidumpFileWriter minidump_file_writer; + auto module_list_writer = std::make_unique(); + + static constexpr char kModuleName[] = "test_executable"; + + auto module_writer = std::make_unique(); + module_writer->SetName(kModuleName); + + module_list_writer->AddModule(std::move(module_writer)); + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(module_list_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_GT(string_file.string().size(), + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_MODULE_LIST) + 1 * sizeof(MINIDUMP_MODULE)); + + const MINIDUMP_MODULE_LIST* module_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetModuleListStream(string_file.string(), &module_list)); + + EXPECT_EQ(module_list->NumberOfModules, 1u); + + MINIDUMP_MODULE expected = {}; + ASSERT_NO_FATAL_FAILURE(ExpectModule(&expected, + &module_list->Modules[0], + string_file.string(), + kModuleName, + nullptr, + nullptr, + 0, + 0, + nullptr, + 0, + false)); +} + +TEST(MinidumpModuleWriter, OneModule) { + MinidumpFileWriter minidump_file_writer; + auto module_list_writer = std::make_unique(); + + static constexpr char kModuleName[] = "statically_linked"; + constexpr uint64_t kModuleBase = 0x10da69000; + constexpr uint32_t kModuleSize = 0x1000; + constexpr uint32_t kChecksum = 0x76543210; + constexpr time_t kTimestamp = 0x386d4380; + constexpr uint32_t kFileVersionMS = 0x00010002; + constexpr uint32_t kFileVersionLS = 0x00030004; + constexpr uint32_t kProductVersionMS = 0x00050006; + constexpr uint32_t kProductVersionLS = 0x00070008; + constexpr uint32_t kFileFlagsMask = VS_FF_DEBUG | VS_FF_PRERELEASE | + VS_FF_PATCHED | VS_FF_PRIVATEBUILD | + VS_FF_INFOINFERRED | VS_FF_SPECIALBUILD; + constexpr uint32_t kFileFlags = VS_FF_PRIVATEBUILD | VS_FF_SPECIALBUILD; + constexpr uint32_t kFileOS = VOS_DOS; + constexpr uint32_t kFileType = VFT_DRV; + constexpr uint32_t kFileSubtype = VFT2_DRV_KEYBOARD; + static constexpr char kPDBName[] = "statical.pdb"; + static constexpr uint8_t kPDBUUIDBytes[16] = {0xfe, + 0xdc, + 0xba, + 0x98, + 0x76, + 0x54, + 0x32, + 0x10, + 0x08, + 0x19, + 0x2a, + 0x3b, + 0x4c, + 0x5d, + 0x6e, + 0x7f}; + UUID pdb_uuid; + pdb_uuid.InitializeFromBytes(kPDBUUIDBytes); + constexpr uint32_t kPDBAge = 1; + constexpr uint32_t kDebugType = IMAGE_DEBUG_MISC_EXENAME; + static constexpr char kDebugName[] = "statical.dbg"; + constexpr bool kDebugUTF16 = false; + + auto module_writer = std::make_unique(); + module_writer->SetName(kModuleName); + module_writer->SetImageBaseAddress(kModuleBase); + module_writer->SetImageSize(kModuleSize); + module_writer->SetChecksum(kChecksum); + module_writer->SetTimestamp(kTimestamp); + module_writer->SetFileVersion(kFileVersionMS >> 16, + kFileVersionMS & 0xffff, + kFileVersionLS >> 16, + kFileVersionLS & 0xffff); + module_writer->SetProductVersion(kProductVersionMS >> 16, + kProductVersionMS & 0xffff, + kProductVersionLS >> 16, + kProductVersionLS & 0xffff); + module_writer->SetFileFlagsAndMask(kFileFlags, kFileFlagsMask); + module_writer->SetFileOS(kFileOS); + module_writer->SetFileTypeAndSubtype(kFileType, kFileSubtype); + + auto codeview_pdb70_writer = + std::make_unique(); + codeview_pdb70_writer->SetPDBName(kPDBName); + codeview_pdb70_writer->SetUUIDAndAge(pdb_uuid, kPDBAge); + module_writer->SetCodeViewRecord(std::move(codeview_pdb70_writer)); + + auto misc_debug_writer = + std::make_unique(); + misc_debug_writer->SetDataType(kDebugType); + misc_debug_writer->SetData(kDebugName, kDebugUTF16); + module_writer->SetMiscDebugRecord(std::move(misc_debug_writer)); + + module_list_writer->AddModule(std::move(module_writer)); + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(module_list_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_GT(string_file.string().size(), + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_MODULE_LIST) + 1 * sizeof(MINIDUMP_MODULE)); + + const MINIDUMP_MODULE_LIST* module_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetModuleListStream(string_file.string(), &module_list)); + + EXPECT_EQ(module_list->NumberOfModules, 1u); + + MINIDUMP_MODULE expected = {}; + expected.BaseOfImage = kModuleBase; + expected.SizeOfImage = kModuleSize; + expected.CheckSum = kChecksum; + expected.TimeDateStamp = kTimestamp; + expected.VersionInfo.dwFileVersionMS = kFileVersionMS; + expected.VersionInfo.dwFileVersionLS = kFileVersionLS; + expected.VersionInfo.dwProductVersionMS = kProductVersionMS; + expected.VersionInfo.dwProductVersionLS = kProductVersionLS; + expected.VersionInfo.dwFileFlagsMask = kFileFlagsMask; + expected.VersionInfo.dwFileFlags = kFileFlags; + expected.VersionInfo.dwFileOS = kFileOS; + expected.VersionInfo.dwFileType = kFileType; + expected.VersionInfo.dwFileSubtype = kFileSubtype; + + ASSERT_NO_FATAL_FAILURE(ExpectModule(&expected, + &module_list->Modules[0], + string_file.string(), + kModuleName, + kPDBName, + &pdb_uuid, + 0, + kPDBAge, + kDebugName, + kDebugType, + kDebugUTF16)); +} + +TEST(MinidumpModuleWriter, OneModule_CodeViewUsesPDB20_MiscUsesUTF16) { + // MinidumpModuleWriter.OneModule tested with a PDB 7.0 link as the CodeView + // record and an IMAGE_DEBUG_MISC record in UTF-8. This test exercises the + // alternatives, a PDB 2.0 link as the CodeView record and an IMAGE_DEBUG_MISC + // record with UTF-16 data. + MinidumpFileWriter minidump_file_writer; + auto module_list_writer = std::make_unique(); + + static constexpr char kModuleName[] = "dinosaur"; + static constexpr char kPDBName[] = "d1n05.pdb"; + constexpr time_t kPDBTimestamp = 0x386d4380; + constexpr uint32_t kPDBAge = 1; + constexpr uint32_t kDebugType = IMAGE_DEBUG_MISC_EXENAME; + static constexpr char kDebugName[] = "d1n05.dbg"; + constexpr bool kDebugUTF16 = true; + + auto module_writer = std::make_unique(); + module_writer->SetName(kModuleName); + + auto codeview_pdb20_writer = + std::make_unique(); + codeview_pdb20_writer->SetPDBName(kPDBName); + codeview_pdb20_writer->SetTimestampAndAge(kPDBTimestamp, kPDBAge); + module_writer->SetCodeViewRecord(std::move(codeview_pdb20_writer)); + + auto misc_debug_writer = + std::make_unique(); + misc_debug_writer->SetDataType(kDebugType); + misc_debug_writer->SetData(kDebugName, kDebugUTF16); + module_writer->SetMiscDebugRecord(std::move(misc_debug_writer)); + + module_list_writer->AddModule(std::move(module_writer)); + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(module_list_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_GT(string_file.string().size(), + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_MODULE_LIST) + 1 * sizeof(MINIDUMP_MODULE)); + + const MINIDUMP_MODULE_LIST* module_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetModuleListStream(string_file.string(), &module_list)); + + EXPECT_EQ(module_list->NumberOfModules, 1u); + + MINIDUMP_MODULE expected = {}; + + ASSERT_NO_FATAL_FAILURE(ExpectModule(&expected, + &module_list->Modules[0], + string_file.string(), + kModuleName, + kPDBName, + nullptr, + kPDBTimestamp, + kPDBAge, + kDebugName, + kDebugType, + kDebugUTF16)); +} + +TEST(MinidumpModuleWriter, OneModule_CodeViewBuildID) { + // MinidumpModuleWriter.OneModule tested with a BuildID CodeView + MinidumpFileWriter minidump_file_writer; + auto module_list_writer = std::make_unique(); + + static constexpr char kModuleName[] = "dinosaur"; + static constexpr char kBuildID[] = + "averylonghashcodeormaybeitsjustrandomnumbershardtosay"; + + std::vector build_id_data(kBuildID, kBuildID + 53); + + auto module_writer = std::make_unique(); + module_writer->SetName(kModuleName); + + auto codeview_build_id_writer = + std::make_unique(); + codeview_build_id_writer->SetBuildID(build_id_data); + module_writer->SetCodeViewRecord(std::move(codeview_build_id_writer)); + + module_list_writer->AddModule(std::move(module_writer)); + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(module_list_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_GT(string_file.string().size(), + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_MODULE_LIST) + 1 * sizeof(MINIDUMP_MODULE)); + + const MINIDUMP_MODULE_LIST* module_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetModuleListStream(string_file.string(), &module_list)); + + EXPECT_EQ(module_list->NumberOfModules, 1u); + + MINIDUMP_MODULE expected = {}; + + ASSERT_NO_FATAL_FAILURE(ExpectModuleWithBuildIDCv(&expected, + &module_list->Modules[0], + string_file.string(), + kModuleName, + build_id_data)); +} + +TEST(MinidumpModuleWriter, ThreeModules) { + // As good exercise, this test uses three modules, one with a PDB 7.0 link as + // its CodeView record, one with no CodeView record, and one with a PDB 2.0 + // link as its CodeView record. + MinidumpFileWriter minidump_file_writer; + auto module_list_writer = std::make_unique(); + + static constexpr char kModuleName0[] = "main"; + constexpr uint64_t kModuleBase0 = 0x100101000; + constexpr uint32_t kModuleSize0 = 0xf000; + static constexpr char kPDBName0[] = "main"; + static constexpr uint8_t kPDBUUIDBytes0[16] = {0xaa, + 0xbb, + 0xcc, + 0xdd, + 0xee, + 0xff, + 0x00, + 0x11, + 0x22, + 0x33, + 0x44, + 0x55, + 0x66, + 0x77, + 0x88, + 0x99}; + UUID pdb_uuid_0; + pdb_uuid_0.InitializeFromBytes(kPDBUUIDBytes0); + constexpr uint32_t kPDBAge0 = 0; + + static constexpr char kModuleName1[] = "ld.so"; + constexpr uint64_t kModuleBase1 = 0x200202000; + constexpr uint32_t kModuleSize1 = 0x1e000; + + static constexpr char kModuleName2[] = "libc.so"; + constexpr uint64_t kModuleBase2 = 0x300303000; + constexpr uint32_t kModuleSize2 = 0x2d000; + static constexpr char kPDBName2[] = "libc.so"; + constexpr time_t kPDBTimestamp2 = 0x386d4380; + constexpr uint32_t kPDBAge2 = 2; + + auto module_writer_0 = std::make_unique(); + module_writer_0->SetName(kModuleName0); + module_writer_0->SetImageBaseAddress(kModuleBase0); + module_writer_0->SetImageSize(kModuleSize0); + + auto codeview_pdb70_writer_0 = + std::make_unique(); + codeview_pdb70_writer_0->SetPDBName(kPDBName0); + codeview_pdb70_writer_0->SetUUIDAndAge(pdb_uuid_0, kPDBAge0); + module_writer_0->SetCodeViewRecord(std::move(codeview_pdb70_writer_0)); + + module_list_writer->AddModule(std::move(module_writer_0)); + + auto module_writer_1 = std::make_unique(); + module_writer_1->SetName(kModuleName1); + module_writer_1->SetImageBaseAddress(kModuleBase1); + module_writer_1->SetImageSize(kModuleSize1); + + module_list_writer->AddModule(std::move(module_writer_1)); + + auto module_writer_2 = std::make_unique(); + module_writer_2->SetName(kModuleName2); + module_writer_2->SetImageBaseAddress(kModuleBase2); + module_writer_2->SetImageSize(kModuleSize2); + + auto codeview_pdb70_writer_2 = + std::make_unique(); + codeview_pdb70_writer_2->SetPDBName(kPDBName2); + codeview_pdb70_writer_2->SetTimestampAndAge(kPDBTimestamp2, kPDBAge2); + module_writer_2->SetCodeViewRecord(std::move(codeview_pdb70_writer_2)); + + module_list_writer->AddModule(std::move(module_writer_2)); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(module_list_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_GT(string_file.string().size(), + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_MODULE_LIST) + 1 * sizeof(MINIDUMP_MODULE)); + + const MINIDUMP_MODULE_LIST* module_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetModuleListStream(string_file.string(), &module_list)); + + EXPECT_EQ(module_list->NumberOfModules, 3u); + + MINIDUMP_MODULE expected = {}; + + { + SCOPED_TRACE("module 0"); + + expected.BaseOfImage = kModuleBase0; + expected.SizeOfImage = kModuleSize0; + + ASSERT_NO_FATAL_FAILURE(ExpectModule(&expected, + &module_list->Modules[0], + string_file.string(), + kModuleName0, + kPDBName0, + &pdb_uuid_0, + 0, + kPDBAge0, + nullptr, + 0, + false)); + } + + { + SCOPED_TRACE("module 1"); + + expected.BaseOfImage = kModuleBase1; + expected.SizeOfImage = kModuleSize1; + + ASSERT_NO_FATAL_FAILURE(ExpectModule(&expected, + &module_list->Modules[1], + string_file.string(), + kModuleName1, + nullptr, + nullptr, + 0, + 0, + nullptr, + 0, + false)); + } + + { + SCOPED_TRACE("module 2"); + + expected.BaseOfImage = kModuleBase2; + expected.SizeOfImage = kModuleSize2; + + ASSERT_NO_FATAL_FAILURE(ExpectModule(&expected, + &module_list->Modules[2], + string_file.string(), + kModuleName2, + kPDBName2, + nullptr, + kPDBTimestamp2, + kPDBAge2, + nullptr, + 0, + false)); + } +} + +void InitializeTestModuleSnapshotFromMinidumpModule( + TestModuleSnapshot* module_snapshot, + const MINIDUMP_MODULE& minidump_module, + const std::string& name, + const std::string& pdb_name, + const crashpad::UUID& uuid, + uint32_t age) { + module_snapshot->SetName(name); + + module_snapshot->SetAddressAndSize(minidump_module.BaseOfImage, + minidump_module.SizeOfImage); + module_snapshot->SetTimestamp(minidump_module.TimeDateStamp); + module_snapshot->SetFileVersion( + minidump_module.VersionInfo.dwFileVersionMS >> 16, + minidump_module.VersionInfo.dwFileVersionMS & 0xffff, + minidump_module.VersionInfo.dwFileVersionLS >> 16, + minidump_module.VersionInfo.dwFileVersionLS & 0xffff); + module_snapshot->SetSourceVersion( + minidump_module.VersionInfo.dwProductVersionMS >> 16, + minidump_module.VersionInfo.dwProductVersionMS & 0xffff, + minidump_module.VersionInfo.dwProductVersionLS >> 16, + minidump_module.VersionInfo.dwProductVersionLS & 0xffff); + + ModuleSnapshot::ModuleType module_type; + switch (minidump_module.VersionInfo.dwFileType) { + case VFT_APP: + module_type = ModuleSnapshot::kModuleTypeExecutable; + break; + case VFT_DLL: + module_type = ModuleSnapshot::kModuleTypeSharedLibrary; + break; + default: + module_type = ModuleSnapshot::kModuleTypeUnknown; + break; + } + module_snapshot->SetModuleType(module_type); + + module_snapshot->SetUUIDAndAge(uuid, age); + module_snapshot->SetDebugFileName(pdb_name); +} + +TEST(MinidumpModuleWriter, InitializeFromSnapshot) { + MINIDUMP_MODULE expect_modules[3] = {}; + const char* module_paths[std::size(expect_modules)] = {}; + const char* module_pdbs[std::size(expect_modules)] = {}; + UUID uuids[std::size(expect_modules)] = {}; + uint32_t ages[std::size(expect_modules)] = {}; + + expect_modules[0].BaseOfImage = 0x100101000; + expect_modules[0].SizeOfImage = 0xf000; + expect_modules[0].TimeDateStamp = 0x01234567; + expect_modules[0].VersionInfo.dwFileVersionMS = 0x00010002; + expect_modules[0].VersionInfo.dwFileVersionLS = 0x00030004; + expect_modules[0].VersionInfo.dwProductVersionMS = 0x00050006; + expect_modules[0].VersionInfo.dwProductVersionLS = 0x00070008; + expect_modules[0].VersionInfo.dwFileType = VFT_APP; + module_paths[0] = "/usr/bin/true"; + module_pdbs[0] = "true"; + static constexpr uint8_t kUUIDBytes0[16] = {0x00, + 0x11, + 0x22, + 0x33, + 0x44, + 0x55, + 0x66, + 0x77, + 0x88, + 0x99, + 0xaa, + 0xbb, + 0xcc, + 0xdd, + 0xee, + 0xff}; + uuids[0].InitializeFromBytes(kUUIDBytes0); + ages[0] = 10; + + expect_modules[1].BaseOfImage = 0x200202000; + expect_modules[1].SizeOfImage = 0x1e1000; + expect_modules[1].TimeDateStamp = 0x89abcdef; + expect_modules[1].VersionInfo.dwFileVersionMS = 0x0009000a; + expect_modules[1].VersionInfo.dwFileVersionLS = 0x000b000c; + expect_modules[1].VersionInfo.dwProductVersionMS = 0x000d000e; + expect_modules[1].VersionInfo.dwProductVersionLS = 0x000f0000; + expect_modules[1].VersionInfo.dwFileType = VFT_DLL; + module_paths[1] = "/usr/lib/libSystem.B.dylib"; + module_pdbs[1] = "libSystem.B.dylib.pdb"; + static constexpr uint8_t kUUIDBytes1[16] = {0x00, + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x06, + 0x07, + 0x08, + 0x09, + 0x0a, + 0x0b, + 0x0c, + 0x0d, + 0x0e, + 0x0f}; + uuids[1].InitializeFromBytes(kUUIDBytes1); + ages[1] = 20; + + expect_modules[2].BaseOfImage = 0x300303000; + expect_modules[2].SizeOfImage = 0x2d000; + expect_modules[2].TimeDateStamp = 0x76543210; + expect_modules[2].VersionInfo.dwFileVersionMS = 0x11112222; + expect_modules[2].VersionInfo.dwFileVersionLS = 0x33334444; + expect_modules[2].VersionInfo.dwProductVersionMS = 0x9999aaaa; + expect_modules[2].VersionInfo.dwProductVersionLS = 0xbbbbcccc; + expect_modules[2].VersionInfo.dwFileType = VFT_UNKNOWN; + module_paths[2] = "/usr/lib/dyld"; + module_pdbs[2] = "/usr/lib/dyld.pdb"; + static constexpr uint8_t kUUIDBytes2[16] = {0xff, + 0xfe, + 0xfd, + 0xfc, + 0xfb, + 0xfa, + 0xf9, + 0xf8, + 0xf7, + 0xf6, + 0xf5, + 0xf4, + 0xf3, + 0xf2, + 0xf1, + 0xf0}; + uuids[2].InitializeFromBytes(kUUIDBytes2); + ages[2] = 30; + + std::vector> module_snapshots_owner; + std::vector module_snapshots; + for (size_t index = 0; index < std::size(expect_modules); ++index) { + module_snapshots_owner.push_back(std::make_unique()); + TestModuleSnapshot* module_snapshot = module_snapshots_owner.back().get(); + InitializeTestModuleSnapshotFromMinidumpModule(module_snapshot, + expect_modules[index], + module_paths[index], + module_pdbs[index], + uuids[index], + ages[index]); + module_snapshots.push_back(module_snapshot); + } + + auto module_list_writer = std::make_unique(); + module_list_writer->InitializeFromSnapshot(module_snapshots); + + MinidumpFileWriter minidump_file_writer; + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(module_list_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_MODULE_LIST* module_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetModuleListStream(string_file.string(), &module_list)); + + ASSERT_EQ(module_list->NumberOfModules, 3u); + + for (size_t index = 0; index < module_list->NumberOfModules; ++index) { + SCOPED_TRACE(base::StringPrintf("index %" PRIuS, index)); + ASSERT_NO_FATAL_FAILURE(ExpectModule(&expect_modules[index], + &module_list->Modules[index], + string_file.string(), + module_paths[index], + module_pdbs[index], + &uuids[index], + 0, + ages[index], + nullptr, + 0, + false)); + } +} + +TEST(MinidumpModuleWriterDeathTest, NoModuleName) { + MinidumpFileWriter minidump_file_writer; + auto module_list_writer = std::make_unique(); + auto module_writer = std::make_unique(); + module_list_writer->AddModule(std::move(module_writer)); + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(module_list_writer))); + + StringFile string_file; + ASSERT_DEATH_CHECK(minidump_file_writer.WriteEverything(&string_file), + "name_"); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_rva_list_writer.cc b/shared/sentry/external/crashpad/minidump/minidump_rva_list_writer.cc new file mode 100644 index 000000000..88539869f --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_rva_list_writer.cc @@ -0,0 +1,100 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_rva_list_writer.h" + +#include + +#include "base/logging.h" +#include "util/file/file_writer.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { +namespace internal { + +MinidumpRVAListWriter::MinidumpRVAListWriter() + : MinidumpWritable(), + rva_list_base_(new MinidumpRVAList()), + children_(), + child_rvas_() { +} + +MinidumpRVAListWriter::~MinidumpRVAListWriter() { +} + +void MinidumpRVAListWriter::AddChild(std::unique_ptr child) { + DCHECK_EQ(state(), kStateMutable); + + children_.push_back(std::move(child)); +} + +bool MinidumpRVAListWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + DCHECK(child_rvas_.empty()); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + size_t child_count = children_.size(); + if (!AssignIfInRange(&rva_list_base_->count, child_count)) { + LOG(ERROR) << "child_count " << child_count << " out of range"; + return false; + } + + child_rvas_.resize(child_count); + for (size_t index = 0; index < child_count; ++index) { + children_[index]->RegisterRVA(&child_rvas_[index]); + } + + return true; +} + +size_t MinidumpRVAListWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return sizeof(*rva_list_base_) + children_.size() * sizeof(RVA); +} + +std::vector MinidumpRVAListWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + + std::vector children; + for (const auto& child : children_) { + children.push_back(child.get()); + } + + return children; +} + +bool MinidumpRVAListWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + DCHECK_EQ(children_.size(), child_rvas_.size()); + + WritableIoVec iov; + iov.iov_base = rva_list_base_.get(); + iov.iov_len = sizeof(*rva_list_base_); + std::vector iovecs(1, iov); + + if (!child_rvas_.empty()) { + iov.iov_base = &child_rvas_[0]; + iov.iov_len = child_rvas_.size() * sizeof(RVA); + iovecs.push_back(iov); + } + + return file_writer->WriteIoVec(&iovecs); +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_rva_list_writer.h b/shared/sentry/external/crashpad/minidump/minidump_rva_list_writer.h new file mode 100644 index 000000000..ba7bd6864 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_rva_list_writer.h @@ -0,0 +1,79 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_RVA_LIST_WRITER_H_ +#define CRASHPAD_MINIDUMP_RVA_LIST_WRITER_H_ + +#include +#include + +#include +#include + +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_writable.h" + +namespace crashpad { +namespace internal { + +//! \brief The writer for a MinidumpRVAList object in a minidump file, +//! containing a list of ::RVA pointers. +class MinidumpRVAListWriter : public MinidumpWritable { + protected: + MinidumpRVAListWriter(); + + MinidumpRVAListWriter(const MinidumpRVAListWriter&) = delete; + MinidumpRVAListWriter& operator=(const MinidumpRVAListWriter&) = delete; + + ~MinidumpRVAListWriter() override; + + //! \brief Adds an ::RVA referencing an MinidumpWritable to the + //! MinidumpRVAList. + //! + //! This object takes ownership of \a child and becomes its parent in the + //! overall tree of MinidumpWritable objects. + //! + //! To provide type-correctness, subclasses are expected to provide a public + //! method that accepts a `scoped_ptr`-wrapped argument of the proper + //! MinidumpWritable subclass, and call this method with that argument. + //! + //! \note Valid in #kStateMutable. + void AddChild(std::unique_ptr child); + + //! \brief Returns `true` if no child objects have been added by AddChild(), + //! and `false` if child objects are present. + bool IsEmpty() const { return children_.empty(); } + + //! \brief Returns an object’s ::RVA objects referencing its children. + //! + //! \note The returned vector will be empty until the object advances to + //! #kStateFrozen or beyond. + const std::vector& child_rvas() const { return child_rvas_; } + + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + std::unique_ptr rva_list_base_; + std::vector> children_; + std::vector child_rvas_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_RVA_LIST_WRITER_H_ diff --git a/shared/sentry/external/crashpad/minidump/minidump_rva_list_writer_test.cc b/shared/sentry/external/crashpad/minidump/minidump_rva_list_writer_test.cc new file mode 100644 index 000000000..adfed1128 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_rva_list_writer_test.cc @@ -0,0 +1,107 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_rva_list_writer.h" + +#include +#include + +#include "base/format_macros.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" +#include "minidump/test/minidump_rva_list_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "util/file/string_file.h" + +namespace crashpad { +namespace test { +namespace { + +class TestMinidumpRVAListWriter final : public internal::MinidumpRVAListWriter { + public: + TestMinidumpRVAListWriter() : MinidumpRVAListWriter() {} + + TestMinidumpRVAListWriter(const TestMinidumpRVAListWriter&) = delete; + TestMinidumpRVAListWriter& operator=(const TestMinidumpRVAListWriter&) = + delete; + + ~TestMinidumpRVAListWriter() override {} + + void AddChild(uint32_t value) { + auto child = std::make_unique(value); + MinidumpRVAListWriter::AddChild(std::move(child)); + } +}; + +TEST(MinidumpRVAListWriter, Empty) { + TestMinidumpRVAListWriter list_writer; + + StringFile string_file; + + ASSERT_TRUE(list_writer.WriteEverything(&string_file)); + EXPECT_EQ(string_file.string().size(), sizeof(MinidumpRVAList)); + + const MinidumpRVAList* list = MinidumpRVAListAtStart(string_file.string(), 0); + ASSERT_TRUE(list); +} + +TEST(MinidumpRVAListWriter, OneChild) { + TestMinidumpRVAListWriter list_writer; + + constexpr uint32_t kValue = 0; + list_writer.AddChild(kValue); + + StringFile string_file; + + ASSERT_TRUE(list_writer.WriteEverything(&string_file)); + + const MinidumpRVAList* list = MinidumpRVAListAtStart(string_file.string(), 1); + ASSERT_TRUE(list); + + const uint32_t* child = MinidumpWritableAtRVA( + string_file.string(), list->children[0]); + ASSERT_TRUE(child); + EXPECT_EQ(*child, kValue); +} + +TEST(MinidumpRVAListWriter, ThreeChildren) { + TestMinidumpRVAListWriter list_writer; + + static constexpr uint32_t kValues[] = {0x80000000, 0x55555555, 0x66006600}; + + list_writer.AddChild(kValues[0]); + list_writer.AddChild(kValues[1]); + list_writer.AddChild(kValues[2]); + + StringFile string_file; + + ASSERT_TRUE(list_writer.WriteEverything(&string_file)); + + const MinidumpRVAList* list = + MinidumpRVAListAtStart(string_file.string(), std::size(kValues)); + ASSERT_TRUE(list); + + for (size_t index = 0; index < std::size(kValues); ++index) { + SCOPED_TRACE(base::StringPrintf("index %" PRIuS, index)); + + const uint32_t* child = MinidumpWritableAtRVA( + string_file.string(), list->children[index]); + ASSERT_TRUE(child); + EXPECT_EQ(*child, kValues[index]); + } +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_simple_string_dictionary_writer.cc b/shared/sentry/external/crashpad/minidump/minidump_simple_string_dictionary_writer.cc new file mode 100644 index 000000000..90f943006 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_simple_string_dictionary_writer.cc @@ -0,0 +1,188 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_simple_string_dictionary_writer.h" + +#include + +#include "base/logging.h" +#include "util/file/file_writer.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { + +MinidumpSimpleStringDictionaryEntryWriter:: + MinidumpSimpleStringDictionaryEntryWriter() + : MinidumpWritable(), entry_(), key_(), value_() { +} + +MinidumpSimpleStringDictionaryEntryWriter:: + ~MinidumpSimpleStringDictionaryEntryWriter() { +} + +const MinidumpSimpleStringDictionaryEntry* +MinidumpSimpleStringDictionaryEntryWriter:: + GetMinidumpSimpleStringDictionaryEntry() const { + DCHECK_EQ(state(), kStateWritable); + + return &entry_; +} + +void MinidumpSimpleStringDictionaryEntryWriter::SetKeyValue( + const std::string& key, + const std::string& value) { + DCHECK_EQ(state(), kStateMutable); + + key_.SetUTF8(key); + value_.SetUTF8(value); +} + +bool MinidumpSimpleStringDictionaryEntryWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + key_.RegisterRVA(&entry_.key); + value_.RegisterRVA(&entry_.value); + + return true; +} + +size_t MinidumpSimpleStringDictionaryEntryWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + // This object doesn’t directly write anything itself. Its + // MinidumpSimpleStringDictionaryEntry is written by its parent as part of a + // MinidumpSimpleStringDictionary, and its children are responsible for + // writing themselves. + return 0; +} + +std::vector +MinidumpSimpleStringDictionaryEntryWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + + std::vector children(1, &key_); + children.push_back(&value_); + return children; +} + +bool MinidumpSimpleStringDictionaryEntryWriter::WriteObject( + FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + // This object doesn’t directly write anything itself. Its + // MinidumpSimpleStringDictionaryEntry is written by its parent as part of a + // MinidumpSimpleStringDictionary, and its children are responsible for + // writing themselves. + return true; +} + +MinidumpSimpleStringDictionaryWriter::MinidumpSimpleStringDictionaryWriter() + : MinidumpWritable(), + entries_(), + simple_string_dictionary_base_(new MinidumpSimpleStringDictionary()) { +} + +MinidumpSimpleStringDictionaryWriter::~MinidumpSimpleStringDictionaryWriter() { + for (auto& item : entries_) + delete item.second; +} + +void MinidumpSimpleStringDictionaryWriter::InitializeFromMap( + const std::map& map) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(entries_.empty()); + + for (const auto& iterator : map) { + auto entry = std::make_unique(); + entry->SetKeyValue(iterator.first, iterator.second); + AddEntry(std::move(entry)); + } +} + +void MinidumpSimpleStringDictionaryWriter::AddEntry( + std::unique_ptr entry) { + DCHECK_EQ(state(), kStateMutable); + + const std::string& key = entry->Key(); + auto iterator = entries_.find(key); + if (iterator != entries_.end()) { + delete iterator->second; + iterator->second = entry.release(); + } else { + entries_[key] = entry.release(); + } +} + +bool MinidumpSimpleStringDictionaryWriter::IsUseful() const { + return !entries_.empty(); +} + +bool MinidumpSimpleStringDictionaryWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + size_t entry_count = entries_.size(); + if (!AssignIfInRange(&simple_string_dictionary_base_->count, entry_count)) { + LOG(ERROR) << "entry_count " << entry_count << " out of range"; + return false; + } + + return true; +} + +size_t MinidumpSimpleStringDictionaryWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return sizeof(*simple_string_dictionary_base_) + + entries_.size() * sizeof(MinidumpSimpleStringDictionaryEntry); +} + +std::vector +MinidumpSimpleStringDictionaryWriter::Children() { + DCHECK_GE(state(), kStateMutable); + + std::vector children; + for (const auto& key_entry : entries_) { + children.push_back(key_entry.second); + } + + return children; +} + +bool MinidumpSimpleStringDictionaryWriter::WriteObject( + FileWriterInterface* file_writer) { + DCHECK_GE(state(), kStateWritable); + + WritableIoVec iov; + iov.iov_base = simple_string_dictionary_base_.get(); + iov.iov_len = sizeof(*simple_string_dictionary_base_); + std::vector iovecs(1, iov); + + for (const auto& key_entry : entries_) { + iov.iov_base = key_entry.second->GetMinidumpSimpleStringDictionaryEntry(); + iov.iov_len = sizeof(MinidumpSimpleStringDictionaryEntry); + iovecs.push_back(iov); + } + + return file_writer->WriteIoVec(&iovecs); +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_simple_string_dictionary_writer.h b/shared/sentry/external/crashpad/minidump/minidump_simple_string_dictionary_writer.h new file mode 100644 index 000000000..5e46a6479 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_simple_string_dictionary_writer.h @@ -0,0 +1,154 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_SIMPLE_STRING_DICTIONARY_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_SIMPLE_STRING_DICTIONARY_WRITER_H_ + +#include + +#include +#include +#include +#include + +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_string_writer.h" +#include "minidump/minidump_writable.h" + +namespace crashpad { + +//! \brief The writer for a MinidumpSimpleStringDictionaryEntry object in a +//! minidump file. +//! +//! Because MinidumpSimpleStringDictionaryEntry objects only appear as elements +//! of MinidumpSimpleStringDictionary objects, this class does not write any +//! data on its own. It makes its MinidumpSimpleStringDictionaryEntry data +//! available to its MinidumpSimpleStringDictionaryWriter parent, which writes +//! it as part of a MinidumpSimpleStringDictionary. +class MinidumpSimpleStringDictionaryEntryWriter final + : public internal::MinidumpWritable { + public: + MinidumpSimpleStringDictionaryEntryWriter(); + + MinidumpSimpleStringDictionaryEntryWriter( + const MinidumpSimpleStringDictionaryEntryWriter&) = delete; + MinidumpSimpleStringDictionaryEntryWriter& operator=( + const MinidumpSimpleStringDictionaryEntryWriter&) = delete; + + ~MinidumpSimpleStringDictionaryEntryWriter() override; + + //! \brief Returns a MinidumpSimpleStringDictionaryEntry referencing this + //! object’s data. + //! + //! This method is expected to be called by a + //! MinidumpSimpleStringDictionaryWriter in order to obtain a + //! MinidumpSimpleStringDictionaryEntry to include in its list. + //! + //! \note Valid in #kStateWritable. + const MinidumpSimpleStringDictionaryEntry* + GetMinidumpSimpleStringDictionaryEntry() const; + + //! \brief Sets the strings to be written as the entry object’s key and value. + //! + //! \note Valid in #kStateMutable. + void SetKeyValue(const std::string& key, const std::string& value); + + //! \brief Retrieves the key to be written. + //! + //! \note Valid in any state. + const std::string& Key() const { return key_.UTF8(); } + + protected: + // MinidumpWritable: + + bool Freeze() override; + size_t SizeOfObject() override; + std::vector Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + struct MinidumpSimpleStringDictionaryEntry entry_; + internal::MinidumpUTF8StringWriter key_; + internal::MinidumpUTF8StringWriter value_; +}; + +//! \brief The writer for a MinidumpSimpleStringDictionary object in a minidump +//! file, containing a list of MinidumpSimpleStringDictionaryEntry objects. +//! +//! Because this class writes a representatin of a dictionary, the order of +//! entries is insignificant. Entries may be written in any order. +class MinidumpSimpleStringDictionaryWriter final + : public internal::MinidumpWritable { + public: + MinidumpSimpleStringDictionaryWriter(); + + MinidumpSimpleStringDictionaryWriter( + const MinidumpSimpleStringDictionaryWriter&) = delete; + MinidumpSimpleStringDictionaryWriter& operator=( + const MinidumpSimpleStringDictionaryWriter&) = delete; + + ~MinidumpSimpleStringDictionaryWriter() override; + + //! \brief Adds an initialized MinidumpSimpleStringDictionaryEntryWriter for + //! each key-value pair in \a map to the MinidumpSimpleStringDictionary. + //! + //! \param[in] map The map to use as source data. + //! + //! \note Valid in #kStateMutable. No mutator methods may be called before + //! this method, and it is not normally necessary to call any mutator + //! methods after this method. + void InitializeFromMap(const std::map& map); + + //! \brief Adds a MinidumpSimpleStringDictionaryEntryWriter to the + //! MinidumpSimpleStringDictionary. + //! + //! This object takes ownership of \a entry and becomes its parent in the + //! overall tree of internal::MinidumpWritable objects. + //! + //! If the key contained in \a entry duplicates the key of an entry already + //! present in the MinidumpSimpleStringDictionary, the new \a entry will + //! replace the previous one. + //! + //! \note Valid in #kStateMutable. + void AddEntry( + std::unique_ptr entry); + + //! \brief Determines whether the object is useful. + //! + //! A useful object is one that carries data that makes a meaningful + //! contribution to a minidump file. An object carrying entries would be + //! considered useful. + //! + //! \return `true` if the object is useful, `false` otherwise. + bool IsUseful() const; + + protected: + // MinidumpWritable: + + bool Freeze() override; + size_t SizeOfObject() override; + std::vector Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + // This object owns the MinidumpSimpleStringDictionaryEntryWriter objects. + std::map entries_; + + std::unique_ptr + simple_string_dictionary_base_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_SIMPLE_STRING_DICTIONARY_WRITER_H_ diff --git a/shared/sentry/external/crashpad/minidump/minidump_simple_string_dictionary_writer_test.cc b/shared/sentry/external/crashpad/minidump/minidump_simple_string_dictionary_writer_test.cc new file mode 100644 index 000000000..2c7423d42 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_simple_string_dictionary_writer_test.cc @@ -0,0 +1,288 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_simple_string_dictionary_writer.h" + +#include + +#include + +#include "gtest/gtest.h" +#include "minidump/test/minidump_string_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "util/file/string_file.h" + +namespace crashpad { +namespace test { +namespace { + +const MinidumpSimpleStringDictionary* MinidumpSimpleStringDictionaryAtStart( + const std::string& file_contents, + size_t count) { + MINIDUMP_LOCATION_DESCRIPTOR location_descriptor; + location_descriptor.DataSize = static_cast( + sizeof(MinidumpSimpleStringDictionary) + + count * sizeof(MinidumpSimpleStringDictionaryEntry)); + location_descriptor.Rva = 0; + return MinidumpWritableAtLocationDescriptor( + file_contents, location_descriptor); +} + +TEST(MinidumpSimpleStringDictionaryWriter, EmptySimpleStringDictionary) { + StringFile string_file; + + MinidumpSimpleStringDictionaryWriter dictionary_writer; + + EXPECT_FALSE(dictionary_writer.IsUseful()); + + EXPECT_TRUE(dictionary_writer.WriteEverything(&string_file)); + ASSERT_EQ(string_file.string().size(), + sizeof(MinidumpSimpleStringDictionary)); + + const MinidumpSimpleStringDictionary* dictionary = + MinidumpSimpleStringDictionaryAtStart(string_file.string(), 0); + ASSERT_TRUE(dictionary); + EXPECT_EQ(dictionary->count, 0u); +} + +TEST(MinidumpSimpleStringDictionaryWriter, EmptyKeyValue) { + StringFile string_file; + + MinidumpSimpleStringDictionaryWriter dictionary_writer; + auto entry_writer = + std::make_unique(); + dictionary_writer.AddEntry(std::move(entry_writer)); + + EXPECT_TRUE(dictionary_writer.IsUseful()); + + EXPECT_TRUE(dictionary_writer.WriteEverything(&string_file)); + ASSERT_EQ(string_file.string().size(), + sizeof(MinidumpSimpleStringDictionary) + + sizeof(MinidumpSimpleStringDictionaryEntry) + + 2 * sizeof(MinidumpUTF8String) + 1 + 3 + 1); // 3 for padding + + const MinidumpSimpleStringDictionary* dictionary = + MinidumpSimpleStringDictionaryAtStart(string_file.string(), 1); + ASSERT_TRUE(dictionary); + EXPECT_EQ(dictionary->count, 1u); + EXPECT_EQ(dictionary->entries[0].key, 12u); + EXPECT_EQ(dictionary->entries[0].value, 20u); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[0].key), + ""); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[0].value), + ""); +} + +TEST(MinidumpSimpleStringDictionaryWriter, OneKeyValue) { + StringFile string_file; + + char kKey[] = "key"; + char kValue[] = "value"; + + MinidumpSimpleStringDictionaryWriter dictionary_writer; + auto entry_writer = + std::make_unique(); + entry_writer->SetKeyValue(kKey, kValue); + dictionary_writer.AddEntry(std::move(entry_writer)); + + EXPECT_TRUE(dictionary_writer.IsUseful()); + + EXPECT_TRUE(dictionary_writer.WriteEverything(&string_file)); + ASSERT_EQ(string_file.string().size(), + sizeof(MinidumpSimpleStringDictionary) + + sizeof(MinidumpSimpleStringDictionaryEntry) + + 2 * sizeof(MinidumpUTF8String) + sizeof(kKey) + sizeof(kValue)); + + const MinidumpSimpleStringDictionary* dictionary = + MinidumpSimpleStringDictionaryAtStart(string_file.string(), 1); + ASSERT_TRUE(dictionary); + EXPECT_EQ(dictionary->count, 1u); + EXPECT_EQ(dictionary->entries[0].key, 12u); + EXPECT_EQ(dictionary->entries[0].value, 20u); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[0].key), + kKey); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[0].value), + kValue); +} + +TEST(MinidumpSimpleStringDictionaryWriter, ThreeKeysValues) { + StringFile string_file; + + char kKey0[] = "m0"; + char kValue0[] = "value0"; + char kKey1[] = "zzz1"; + char kValue1[] = "v1"; + char kKey2[] = "aa2"; + char kValue2[] = "val2"; + + MinidumpSimpleStringDictionaryWriter dictionary_writer; + auto entry_writer_0 = + std::make_unique(); + entry_writer_0->SetKeyValue(kKey0, kValue0); + dictionary_writer.AddEntry(std::move(entry_writer_0)); + auto entry_writer_1 = + std::make_unique(); + entry_writer_1->SetKeyValue(kKey1, kValue1); + dictionary_writer.AddEntry(std::move(entry_writer_1)); + auto entry_writer_2 = + std::make_unique(); + entry_writer_2->SetKeyValue(kKey2, kValue2); + dictionary_writer.AddEntry(std::move(entry_writer_2)); + + EXPECT_TRUE(dictionary_writer.IsUseful()); + + EXPECT_TRUE(dictionary_writer.WriteEverything(&string_file)); + ASSERT_EQ(string_file.string().size(), + sizeof(MinidumpSimpleStringDictionary) + + 3 * sizeof(MinidumpSimpleStringDictionaryEntry) + + 6 * sizeof(MinidumpUTF8String) + sizeof(kKey2) + + sizeof(kValue2) + 3 + sizeof(kKey0) + 1 + sizeof(kValue0) + 1 + + sizeof(kKey1) + 3 + sizeof(kValue1)); + + const MinidumpSimpleStringDictionary* dictionary = + MinidumpSimpleStringDictionaryAtStart(string_file.string(), 3); + ASSERT_TRUE(dictionary); + EXPECT_EQ(dictionary->count, 3u); + EXPECT_EQ(dictionary->entries[0].key, 28u); + EXPECT_EQ(dictionary->entries[0].value, 36u); + EXPECT_EQ(dictionary->entries[1].key, 48u); + EXPECT_EQ(dictionary->entries[1].value, 56u); + EXPECT_EQ(dictionary->entries[2].key, 68u); + EXPECT_EQ(dictionary->entries[2].value, 80u); + + // The entries don’t appear in the order they were added. The current + // implementation uses a std::map and sorts keys, so the entires appear in + // alphabetical order. However, this is an implementation detail, and it’s OK + // if the writer stops sorting in this order. Testing for a specific order is + // just the easiest way to write this test while the writer will output things + // in a known order. + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[0].key), + kKey2); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[0].value), + kValue2); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[1].key), + kKey0); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[1].value), + kValue0); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[2].key), + kKey1); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[2].value), + kValue1); +} + +TEST(MinidumpSimpleStringDictionaryWriter, DuplicateKeyValue) { + StringFile string_file; + + char kKey[] = "key"; + char kValue0[] = "fake_value"; + char kValue1[] = "value"; + + MinidumpSimpleStringDictionaryWriter dictionary_writer; + auto entry_writer_0 = + std::make_unique(); + entry_writer_0->SetKeyValue(kKey, kValue0); + dictionary_writer.AddEntry(std::move(entry_writer_0)); + auto entry_writer_1 = + std::make_unique(); + entry_writer_1->SetKeyValue(kKey, kValue1); + dictionary_writer.AddEntry(std::move(entry_writer_1)); + + EXPECT_TRUE(dictionary_writer.IsUseful()); + + EXPECT_TRUE(dictionary_writer.WriteEverything(&string_file)); + ASSERT_EQ(string_file.string().size(), + sizeof(MinidumpSimpleStringDictionary) + + sizeof(MinidumpSimpleStringDictionaryEntry) + + 2 * sizeof(MinidumpUTF8String) + sizeof(kKey) + + sizeof(kValue1)); + + const MinidumpSimpleStringDictionary* dictionary = + MinidumpSimpleStringDictionaryAtStart(string_file.string(), 1); + ASSERT_TRUE(dictionary); + EXPECT_EQ(dictionary->count, 1u); + EXPECT_EQ(dictionary->entries[0].key, 12u); + EXPECT_EQ(dictionary->entries[0].value, 20u); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[0].key), + kKey); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[0].value), + kValue1); +} + +TEST(MinidumpSimpleStringDictionaryWriter, InitializeFromMap) { + char kKey0[] = "Dictionaries"; + char kValue0[] = "USEFUL*"; + char kKey1[] = "#1 Key!"; + char kValue1[] = ""; + char kKey2[] = "key two"; + char kValue2[] = "value two"; + + std::map map; + map[kKey0] = kValue0; + map[kKey1] = kValue1; + map[kKey2] = kValue2; + + MinidumpSimpleStringDictionaryWriter dictionary_writer; + dictionary_writer.InitializeFromMap(map); + + EXPECT_TRUE(dictionary_writer.IsUseful()); + + StringFile string_file; + ASSERT_TRUE(dictionary_writer.WriteEverything(&string_file)); + + const MinidumpSimpleStringDictionary* dictionary = + MinidumpSimpleStringDictionaryAtStart(string_file.string(), map.size()); + ASSERT_TRUE(dictionary); + ASSERT_EQ(dictionary->count, 3u); + + // The entries don’t appear in the order they were added. The current + // implementation uses a std::map and sorts keys, so the entires appear in + // alphabetical order. However, this is an implementation detail, and it’s OK + // if the writer stops sorting in this order. Testing for a specific order is + // just the easiest way to write this test while the writer will output things + // in a known order. + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[0].key), + kKey1); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[0].value), + kValue1); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[1].key), + kKey0); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[1].value), + kValue0); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[2].key), + kKey2); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), + dictionary->entries[2].value), + kValue2); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_stacktrace_writer.cc b/shared/sentry/external/crashpad/minidump/minidump_stacktrace_writer.cc new file mode 100644 index 000000000..74724ca77 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_stacktrace_writer.cc @@ -0,0 +1,169 @@ + +#include "minidump/minidump_stacktrace_writer.h" + +#include + +#include +#include + +#include "base/logging.h" +#include "snapshot/exception_snapshot.h" +#include "snapshot/thread_snapshot.h" +#include "util/file/file_writer.h" + +namespace crashpad { + +size_t align_to_8(size_t size) { + size_t rest = size % 8; + if (rest == 0) { + return 0; + } else { + return 8 - rest; + } +} + +MinidumpStacktraceListWriter::MinidumpStacktraceListWriter() + : MinidumpStreamWriter(), + threads_(), + frames_(), + symbol_bytes_(), + stacktrace_header_() {} + +MinidumpStacktraceListWriter::~MinidumpStacktraceListWriter() {} + +void MinidumpStacktraceListWriter::InitializeFromSnapshot( + const std::vector& thread_snapshots, + const MinidumpThreadIDMap& thread_id_map, + const ExceptionSnapshot* exception_snapshot) { + DCHECK_EQ(state(), kStateMutable); + + DCHECK(threads_.empty()); + DCHECK(frames_.empty()); + DCHECK(symbol_bytes_.empty()); + + for (auto thread_snapshot : thread_snapshots) { + internal::RawThread thread; + + auto thread_id_it = thread_id_map.find(thread_snapshot->ThreadID()); + DCHECK(thread_id_it != thread_id_map.end()); + thread.thread_id = thread_id_it->second; + thread.start_frame = (uint32_t)frames_.size(); + + std::vector frames = thread_snapshot->StackTrace(); + + // filter out the stack frames that are *above* the exception addr, as those + // are related to exception handling, and not really useful. + if (exception_snapshot && + thread_snapshot->ThreadID() == exception_snapshot->ThreadID()) { + auto it = begin(frames); + for (; it != end(frames); it++) + if (it->InstructionAddr() == exception_snapshot->ExceptionAddress()) { + break; + } + if (it < end(frames)) { + frames.erase(begin(frames), it); + } + } + + for (auto frame_snapshot : frames) { + internal::RawFrame frame; + frame.instruction_addr = frame_snapshot.InstructionAddr(); + frame.symbol_offset = (uint32_t)symbol_bytes_.size(); + + auto symbol = frame_snapshot.Symbol(); + + symbol_bytes_.reserve(symbol.size()); + symbol_bytes_.insert(symbol_bytes_.end(), symbol.begin(), symbol.end()); + + frame.symbol_len = (uint32_t)symbol.size(); + + frames_.push_back(frame); + } + + thread.num_frames = (uint32_t)frames_.size() - thread.start_frame; + + threads_.push_back(thread); + } + + stacktrace_header_.version = 1; + stacktrace_header_.num_threads = (uint32_t)threads_.size(); + stacktrace_header_.num_frames = (uint32_t)frames_.size(); + stacktrace_header_.symbol_bytes = (uint32_t)symbol_bytes_.size(); +} + +size_t MinidumpStacktraceListWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + size_t header_size = sizeof(stacktrace_header_); + header_size += align_to_8(header_size); + size_t threads_size = threads_.size() * sizeof(internal::RawThread); + threads_size += align_to_8(threads_size); + size_t frames_size = frames_.size() * sizeof(internal::RawFrame); + frames_size += align_to_8(frames_size); + + return header_size + threads_size + frames_size + symbol_bytes_.size(); +} + +size_t MinidumpStacktraceListWriter::Alignment() { + // because we are writing `uint64_t` that are 8-byte aligned + return 8; +} + +bool MinidumpStacktraceListWriter::WriteObject( + FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + uint64_t padding = 0; + WritableIoVec iov; + // header, threads, frames, symbol_bytes + std::vector iovecs(4); + + iov.iov_base = &stacktrace_header_; + iov.iov_len = sizeof(stacktrace_header_); + iovecs.push_back(iov); + + // align the length of iov to a multiple of 8 and write zeros as padding + iov.iov_base = &padding; + iov.iov_len = align_to_8(iov.iov_len); + if (iov.iov_len > 0) { + iovecs.push_back(iov); + } + + if (!threads_.empty()) { + iov.iov_base = &threads_.front(); + iov.iov_len = threads_.size() * sizeof(internal::RawThread); + iovecs.push_back(iov); + + iov.iov_base = &padding; + iov.iov_len = align_to_8(iov.iov_len); + if (iov.iov_len > 0) { + iovecs.push_back(iov); + } + } + + if (!frames_.empty()) { + iov.iov_base = &frames_.front(); + iov.iov_len = frames_.size() * sizeof(internal::RawFrame); + iovecs.push_back(iov); + + iov.iov_base = &padding; + iov.iov_len = align_to_8(iov.iov_len); + if (iov.iov_len > 0) { + iovecs.push_back(iov); + } + } + + if (!symbol_bytes_.empty()) { + iov.iov_base = &symbol_bytes_.front(); + iov.iov_len = symbol_bytes_.size(); + iovecs.push_back(iov); + } + + return file_writer->WriteIoVec(&iovecs); +} + +MinidumpStreamType MinidumpStacktraceListWriter::StreamType() const { + return kMinidumpStreamTypeSentryStackTraces; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_stacktrace_writer.h b/shared/sentry/external/crashpad/minidump/minidump_stacktrace_writer.h new file mode 100644 index 000000000..2ba5ae901 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_stacktrace_writer.h @@ -0,0 +1,88 @@ +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_STACKTRACE_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_STACKTRACE_WRITER_H_ + +#include +#include + +#include +#include +#include + +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_stream_writer.h" +#include "minidump/minidump_thread_id_map.h" +#include "minidump/minidump_writable.h" + +namespace crashpad { + +namespace internal { + +struct Header { + uint32_t version; + uint32_t num_threads; + uint32_t num_frames; + uint32_t symbol_bytes; +}; + +struct RawThread { + uint32_t thread_id; + uint32_t start_frame; + uint32_t num_frames; +}; + +struct RawFrame { + uint64_t instruction_addr; + uint32_t symbol_offset; + uint32_t symbol_len; +}; + +} // namespace internal + +class ThreadSnapshot; +class ExceptionSnapshot; + +//! \brief The writer for our custom client-side stacktraces stream in a +//! minidump file. +class MinidumpStacktraceListWriter final + : public internal::MinidumpStreamWriter { + public: + MinidumpStacktraceListWriter(); + + MinidumpStacktraceListWriter(const MinidumpStacktraceListWriter&) = delete; + MinidumpStacktraceListWriter& operator=(const MinidumpStacktraceListWriter&) = + delete; + + ~MinidumpStacktraceListWriter() override; + + //! \brief TODO + //! + //! \param[in] thread_snapshots The thread snapshots to use as source data. + //! \param[in] thread_id_map A MinidumpThreadIDMap to be consulted to + //! determine the 32-bit minidump thread ID to use for the thread + //! identified by \a thread_snapshots. + void InitializeFromSnapshot( + const std::vector& thread_snapshots, + const MinidumpThreadIDMap& thread_id_map, + const ExceptionSnapshot* exception_snapshot); + + protected: + // MinidumpWritable: + // bool Freeze() override; + size_t SizeOfObject() override; + size_t Alignment() override; + // std::vector Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpStreamWriter: + MinidumpStreamType StreamType() const override; + + private: + std::vector threads_; + std::vector frames_; + std::vector symbol_bytes_; + internal::Header stacktrace_header_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_STACKTRACE_WRITER_H_ diff --git a/shared/sentry/external/crashpad/minidump/minidump_stream_writer.cc b/shared/sentry/external/crashpad/minidump/minidump_stream_writer.cc new file mode 100644 index 000000000..3084ffed7 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_stream_writer.cc @@ -0,0 +1,49 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_stream_writer.h" + +#include "base/check_op.h" + +namespace crashpad { +namespace internal { + +MinidumpStreamWriter::~MinidumpStreamWriter() { +} + +const MINIDUMP_DIRECTORY* MinidumpStreamWriter::DirectoryListEntry() const { + DCHECK_EQ(state(), kStateWritable); + + return &directory_list_entry_; +} + +MinidumpStreamWriter::MinidumpStreamWriter() + : MinidumpWritable(), directory_list_entry_() { +} + +bool MinidumpStreamWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + directory_list_entry_.StreamType = StreamType(); + RegisterLocationDescriptor(&directory_list_entry_.Location); + + return true; +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_stream_writer.h b/shared/sentry/external/crashpad/minidump/minidump_stream_writer.h new file mode 100644 index 000000000..447566b90 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_stream_writer.h @@ -0,0 +1,66 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_STREAM_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_STREAM_WRITER_H_ + +#include +#include + +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_writable.h" + +namespace crashpad { +namespace internal { + +//! \brief The base class for all second-level objects (“streamsâ€) in a minidump +//! file. +//! +//! Instances of subclasses of this class are children of the root-level +//! MinidumpFileWriter object. +class MinidumpStreamWriter : public MinidumpWritable { + public: + MinidumpStreamWriter(const MinidumpStreamWriter&) = delete; + MinidumpStreamWriter& operator=(const MinidumpStreamWriter&) = delete; + + ~MinidumpStreamWriter() override; + + //! \brief Returns an object’s stream type. + //! + //! \note Valid in any state. + virtual MinidumpStreamType StreamType() const = 0; + + //! \brief Returns a MINIDUMP_DIRECTORY entry that serves as a pointer to this + //! stream. + //! + //! This method is provided for MinidumpFileWriter, which calls it in order to + //! obtain the directory entry for a stream. + //! + //! \note Valid only in #kStateWritable. + const MINIDUMP_DIRECTORY* DirectoryListEntry() const; + + protected: + MinidumpStreamWriter(); + + // MinidumpWritable: + bool Freeze() override; + + private: + MINIDUMP_DIRECTORY directory_list_entry_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_STREAM_WRITER_H_ diff --git a/shared/sentry/external/crashpad/minidump/minidump_string_writer.cc b/shared/sentry/external/crashpad/minidump/minidump_string_writer.cc new file mode 100644 index 000000000..f01fc84f4 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_string_writer.cc @@ -0,0 +1,138 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_string_writer.h" + +#include + +#include "base/logging.h" +#include "minidump/minidump_writer_util.h" +#include "util/file/file_writer.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { +namespace internal { + +template +MinidumpStringWriter::MinidumpStringWriter() + : MinidumpWritable(), string_base_(new MinidumpStringType()), string_() { +} + +template +MinidumpStringWriter::~MinidumpStringWriter() { +} + +template +bool MinidumpStringWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + size_t string_bytes = string_.size() * sizeof(string_[0]); + if (!AssignIfInRange(&string_base_->Length, string_bytes)) { + LOG(ERROR) << "string_bytes " << string_bytes << " out of range"; + return false; + } + + return true; +} + +template +size_t MinidumpStringWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + // Include the NUL terminator. + return sizeof(*string_base_) + (string_.size() + 1) * sizeof(string_[0]); +} + +template +bool MinidumpStringWriter::WriteObject( + FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + // The string’s length is stored in string_base_, and its data is stored in + // string_. Write them both. + WritableIoVec iov; + iov.iov_base = string_base_.get(); + iov.iov_len = sizeof(*string_base_); + std::vector iovecs(1, iov); + + // Include the NUL terminator. + iov.iov_base = &string_[0]; + iov.iov_len = (string_.size() + 1) * sizeof(string_[0]); + iovecs.push_back(iov); + + return file_writer->WriteIoVec(&iovecs); +} + +// Explicit template instantiation of the forms of MinidumpStringWriter<> used +// as base classes. +template class MinidumpStringWriter; +template class MinidumpStringWriter; + +MinidumpUTF16StringWriter::~MinidumpUTF16StringWriter() { +} + +void MinidumpUTF16StringWriter::SetUTF8(const std::string& string_utf8) { + DCHECK_EQ(state(), kStateMutable); + + set_string(MinidumpWriterUtil::ConvertUTF8ToUTF16(string_utf8)); +} + +MinidumpUTF8StringWriter::~MinidumpUTF8StringWriter() { +} + +template +MinidumpStringListWriter::MinidumpStringListWriter() + : MinidumpRVAListWriter() { +} + +template +MinidumpStringListWriter< + MinidumpStringWriterType>::~MinidumpStringListWriter() { +} + +template +void MinidumpStringListWriter::InitializeFromVector( + const std::vector& vector) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(IsEmpty()); + + for (const std::string& string : vector) { + AddStringUTF8(string); + } +} + +template +void MinidumpStringListWriter::AddStringUTF8( + const std::string& string_utf8) { + auto string_writer = std::make_unique(); + string_writer->SetUTF8(string_utf8); + AddChild(std::move(string_writer)); +} + +template +bool MinidumpStringListWriter::IsUseful() const { + return !IsEmpty(); +} + +// Explicit template instantiation of the forms of MinidumpStringListWriter<> +// used as type aliases. +template class MinidumpStringListWriter; +template class MinidumpStringListWriter; + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_string_writer.h b/shared/sentry/external/crashpad/minidump/minidump_string_writer.h new file mode 100644 index 000000000..d11e8cc18 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_string_writer.h @@ -0,0 +1,188 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_STRING_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_STRING_WRITER_H_ + +#include +#include +#include + +#include +#include +#include + +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_rva_list_writer.h" +#include "minidump/minidump_writable.h" + +namespace crashpad { +namespace internal { + +//! \cond + +struct MinidumpStringWriterUTF16Traits { + using StringType = std::u16string; + using MinidumpStringType = MINIDUMP_STRING; +}; + +struct MinidumpStringWriterUTF8Traits { + using StringType = std::string; + using MinidumpStringType = MinidumpUTF8String; +}; + +//! \endcond + +//! \brief Writes a variable-length string to a minidump file in accordance with +//! the string type’s characteristics. +//! +//! MinidumpStringWriter objects should not be instantiated directly. To write +//! strings to minidump file, use the MinidumpUTF16StringWriter and +//! MinidumpUTF8StringWriter subclasses instead. +template +class MinidumpStringWriter : public MinidumpWritable { + public: + MinidumpStringWriter(); + + MinidumpStringWriter(const MinidumpStringWriter&) = delete; + MinidumpStringWriter& operator=(const MinidumpStringWriter&) = delete; + + ~MinidumpStringWriter() override; + + protected: + using MinidumpStringType = typename Traits::MinidumpStringType; + using StringType = typename Traits::StringType; + + bool Freeze() override; + size_t SizeOfObject() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + //! \brief Sets the string to be written. + //! + //! \note Valid in #kStateMutable. + void set_string(const StringType& string) { string_.assign(string); } + + //! \brief Retrieves the string to be written. + //! + //! \note Valid in any state. + const StringType& string() const { return string_; } + + private: + std::unique_ptr string_base_; + StringType string_; +}; + +//! \brief Writes a variable-length UTF-16-encoded MINIDUMP_STRING to a minidump +//! file. +//! +//! MinidumpUTF16StringWriter objects should not be instantiated directly +//! outside of the MinidumpWritable family of classes. +class MinidumpUTF16StringWriter final + : public MinidumpStringWriter { + public: + MinidumpUTF16StringWriter() : MinidumpStringWriter() {} + + MinidumpUTF16StringWriter(const MinidumpUTF16StringWriter&) = delete; + MinidumpUTF16StringWriter& operator=(const MinidumpUTF16StringWriter&) = + delete; + + ~MinidumpUTF16StringWriter() override; + + //! \brief Converts a UTF-8 string to UTF-16 and sets it as the string to be + //! written. + //! + //! \note Valid in #kStateMutable. + void SetUTF8(const std::string& string_utf8); +}; + +//! \brief Writes a variable-length UTF-8-encoded MinidumpUTF8String to a +//! minidump file. +//! +//! MinidumpUTF8StringWriter objects should not be instantiated directly outside +//! of the MinidumpWritable family of classes. +class MinidumpUTF8StringWriter final + : public MinidumpStringWriter { + public: + MinidumpUTF8StringWriter() : MinidumpStringWriter() {} + + MinidumpUTF8StringWriter(const MinidumpUTF8StringWriter&) = delete; + MinidumpUTF8StringWriter& operator=(const MinidumpUTF8StringWriter&) = delete; + + ~MinidumpUTF8StringWriter() override; + + //! \brief Sets the string to be written. + //! + //! \note Valid in #kStateMutable. + void SetUTF8(const std::string& string_utf8) { set_string(string_utf8); } + + //! \brief Retrieves the string to be written. + //! + //! \note Valid in any state. + const std::string& UTF8() const { return string(); } +}; + +//! \brief The writer for a MinidumpRVAList object in a minidump file, +//! containing a list of \a MinidumpStringWriterType objects. +template +class MinidumpStringListWriter final : public MinidumpRVAListWriter { + public: + MinidumpStringListWriter(); + + MinidumpStringListWriter(const MinidumpStringListWriter&) = delete; + MinidumpStringListWriter& operator=(const MinidumpStringListWriter&) = delete; + + ~MinidumpStringListWriter() override; + + //! \brief Adds a new \a Traits::MinidumpStringWriterType for each element in + //! \a vector to the MinidumpRVAList. + //! + //! \param[in] vector The vector to use as source data. Each string in the + //! vector is treated as a UTF-8 string, and a new string writer will be + //! created for each one and made a child of the MinidumpStringListWriter. + //! + //! \note Valid in #kStateMutable. No mutator methods may be called before + //! this method, and it is not normally necessary to call any mutator + //! methods after this method. + void InitializeFromVector(const std::vector& vector); + + //! \brief Creates a new \a Traits::MinidumpStringWriterType object and adds + //! it to the MinidumpRVAList. + //! + //! This object creates a new string writer with string value \a string_utf8, + //! takes ownership of it, and becomes its parent in the overall tree of + //! MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void AddStringUTF8(const std::string& string_utf8); + + //! \brief Determines whether the object is useful. + //! + //! A useful object is one that carries data that makes a meaningful + //! contribution to a minidump file. An object carrying entries would be + //! considered useful. + //! + //! \return `true` if the object is useful, `false` otherwise. + bool IsUseful() const; +}; + +} // namespace internal + +using MinidumpUTF16StringListWriter = internal::MinidumpStringListWriter< + internal::MinidumpUTF16StringWriter>; +using MinidumpUTF8StringListWriter = internal::MinidumpStringListWriter< + internal::MinidumpUTF8StringWriter>; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_STRING_WRITER_H_ diff --git a/shared/sentry/external/crashpad/minidump/minidump_string_writer_test.cc b/shared/sentry/external/crashpad/minidump/minidump_string_writer_test.cc new file mode 100644 index 000000000..f0cdb827f --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_string_writer_test.cc @@ -0,0 +1,258 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_string_writer.h" + +#include +#include + +#include "base/format_macros.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "gtest/gtest.h" +#include "minidump/test/minidump_rva_list_test_util.h" +#include "minidump/test/minidump_string_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "util/file/string_file.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(MinidumpStringWriter, MinidumpUTF16StringWriter) { + StringFile string_file; + + { + SCOPED_TRACE("unset"); + string_file.Reset(); + crashpad::internal::MinidumpUTF16StringWriter string_writer; + EXPECT_TRUE(string_writer.WriteEverything(&string_file)); + ASSERT_EQ(string_file.string().size(), 6u); + + const MINIDUMP_STRING* minidump_string = + MinidumpStringAtRVA(string_file.string(), 0); + EXPECT_TRUE(minidump_string); + EXPECT_EQ(MinidumpStringAtRVAAsString(string_file.string(), 0), + std::u16string()); + } + + static constexpr struct { + size_t input_length; + const char* input_string; + size_t output_length; + char16_t output_string[10]; + } kTestData[] = { + {0, "", 0, {}}, + {1, "a", 1, {'a'}}, + {2, "\0b", 2, {0, 'b'}}, + {3, "cde", 3, {'c', 'd', 'e'}}, + {9, "Hi world!", 9, {'H', 'i', ' ', 'w', 'o', 'r', 'l', 'd', '!'}}, + {7, "ret\nurn", 7, {'r', 'e', 't', '\n', 'u', 'r', 'n'}}, + {2, "\303\251", 1, {0x00e9}}, // é + + // oóöőo + {8, "o\303\263\303\266\305\221o", 5, {'o', 0x00f3, 0x00f6, 0x151, 'o'}}, + {4, "\360\220\204\202", 2, {0xd800, 0xdd02}}, // ð„‚ (non-BMP) + }; + + for (size_t index = 0; index < std::size(kTestData); ++index) { + SCOPED_TRACE(base::StringPrintf( + "index %" PRIuS ", input %s", index, kTestData[index].input_string)); + + // Make sure that the expected output string with its NUL terminator fits in + // the space provided. + ASSERT_EQ(kTestData[index] + .output_string[std::size(kTestData[index].output_string) - 1], + 0); + + string_file.Reset(); + crashpad::internal::MinidumpUTF16StringWriter string_writer; + string_writer.SetUTF8(std::string(kTestData[index].input_string, + kTestData[index].input_length)); + EXPECT_TRUE(string_writer.WriteEverything(&string_file)); + + const size_t expected_utf16_units_with_nul = + kTestData[index].output_length + 1; + [[maybe_unused]] MINIDUMP_STRING* tmp; + const size_t expected_utf16_bytes = + expected_utf16_units_with_nul * sizeof(tmp->Buffer[0]); + ASSERT_EQ(string_file.string().size(), sizeof(*tmp) + expected_utf16_bytes); + + const MINIDUMP_STRING* minidump_string = + MinidumpStringAtRVA(string_file.string(), 0); + EXPECT_TRUE(minidump_string); + std::u16string expect_string = std::u16string( + kTestData[index].output_string, kTestData[index].output_length); + EXPECT_EQ(MinidumpStringAtRVAAsString(string_file.string(), 0), + expect_string); + } +} + +TEST(MinidumpStringWriter, ConvertInvalidUTF8ToUTF16) { + StringFile string_file; + + static constexpr const char* kTestData[] = { + "\200", // continuation byte + "\300", // start byte followed by EOF + "\310\177", // start byte without continuation + "\340\200", // EOF in middle of 3-byte sequence + "\340\200\115", // invalid 3-byte sequence + "\303\0\251", // NUL in middle of valid sequence + }; + + for (size_t index = 0; index < std::size(kTestData); ++index) { + SCOPED_TRACE(base::StringPrintf( + "index %" PRIuS ", input %s", index, kTestData[index])); + string_file.Reset(); + crashpad::internal::MinidumpUTF16StringWriter string_writer; + string_writer.SetUTF8(kTestData[index]); + EXPECT_TRUE(string_writer.WriteEverything(&string_file)); + + // The requirements for conversion of invalid UTF-8 input are lax. Make sure + // that at least enough data was written for a string that has one unit and + // a NUL terminator, make sure that the length field matches the length of + // data written, and make sure that at least one U+FFFD replacement + // character was written. + const MINIDUMP_STRING* minidump_string = + MinidumpStringAtRVA(string_file.string(), 0); + EXPECT_TRUE(minidump_string); + [[maybe_unused]] MINIDUMP_STRING* tmp; + EXPECT_EQ( + minidump_string->Length, + string_file.string().size() - sizeof(*tmp) - sizeof(tmp->Buffer[0])); + std::u16string output_string = + MinidumpStringAtRVAAsString(string_file.string(), 0); + EXPECT_FALSE(output_string.empty()); + EXPECT_NE(output_string.find(0xfffd), std::u16string::npos); + } +} + +TEST(MinidumpStringWriter, MinidumpUTF8StringWriter) { + StringFile string_file; + + { + SCOPED_TRACE("unset"); + string_file.Reset(); + crashpad::internal::MinidumpUTF8StringWriter string_writer; + EXPECT_TRUE(string_writer.WriteEverything(&string_file)); + ASSERT_EQ(string_file.string().size(), 5u); + + const MinidumpUTF8String* minidump_string = + MinidumpUTF8StringAtRVA(string_file.string(), 0); + EXPECT_TRUE(minidump_string); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), 0), + std::string()); + } + + static constexpr struct { + size_t length; + const char* string; + } kTestData[] = { + {0, ""}, + {1, "a"}, + {2, "\0b"}, + {3, "cde"}, + {9, "Hi world!"}, + {7, "ret\nurn"}, + {2, "\303\251"}, // é + + // oóöőo + {8, "o\303\263\303\266\305\221o"}, + {4, "\360\220\204\202"}, // ð„‚ (non-BMP) + }; + + for (size_t index = 0; index < std::size(kTestData); ++index) { + SCOPED_TRACE(base::StringPrintf( + "index %" PRIuS ", input %s", index, kTestData[index].string)); + + string_file.Reset(); + crashpad::internal::MinidumpUTF8StringWriter string_writer; + std::string test_string(kTestData[index].string, kTestData[index].length); + string_writer.SetUTF8(test_string); + EXPECT_EQ(string_writer.UTF8(), test_string); + EXPECT_TRUE(string_writer.WriteEverything(&string_file)); + + const size_t expected_utf8_bytes_with_nul = kTestData[index].length + 1; + ASSERT_EQ(string_file.string().size(), + sizeof(MinidumpUTF8String) + expected_utf8_bytes_with_nul); + + const MinidumpUTF8String* minidump_string = + MinidumpUTF8StringAtRVA(string_file.string(), 0); + EXPECT_TRUE(minidump_string); + EXPECT_EQ(MinidumpUTF8StringAtRVAAsString(string_file.string(), 0), + test_string); + } +} + +struct MinidumpUTF16StringListWriterTraits { + using MinidumpStringListWriterType = MinidumpUTF16StringListWriter; + static std::u16string ExpectationForUTF8(const std::string& utf8) { + return base::UTF8ToUTF16(utf8); + } + static std::u16string ObservationAtRVA(const std::string& file_contents, + RVA rva) { + return MinidumpStringAtRVAAsString(file_contents, rva); + } +}; + +struct MinidumpUTF8StringListWriterTraits { + using MinidumpStringListWriterType = MinidumpUTF8StringListWriter; + static std::string ExpectationForUTF8(const std::string& utf8) { + return utf8; + } + static std::string ObservationAtRVA(const std::string& file_contents, + RVA rva) { + return MinidumpUTF8StringAtRVAAsString(file_contents, rva); + } +}; + +template +void MinidumpStringListTest() { + std::vector strings; + strings.push_back(std::string("One")); + strings.push_back(std::string()); + strings.push_back(std::string("3")); + strings.push_back(std::string("\360\237\222\251")); + + typename Traits::MinidumpStringListWriterType string_list_writer; + EXPECT_FALSE(string_list_writer.IsUseful()); + string_list_writer.InitializeFromVector(strings); + EXPECT_TRUE(string_list_writer.IsUseful()); + + StringFile string_file; + + ASSERT_TRUE(string_list_writer.WriteEverything(&string_file)); + + const MinidumpRVAList* list = + MinidumpRVAListAtStart(string_file.string(), strings.size()); + ASSERT_TRUE(list); + + for (size_t index = 0; index < strings.size(); ++index) { + EXPECT_EQ( + Traits::ObservationAtRVA(string_file.string(), list->children[index]), + Traits::ExpectationForUTF8(strings[index])); + } +} + +TEST(MinidumpStringWriter, MinidumpUTF16StringList) { + MinidumpStringListTest(); +} + +TEST(MinidumpStringWriter, MinidumpUTF8StringList) { + MinidumpStringListTest(); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_system_info_writer.cc b/shared/sentry/external/crashpad/minidump/minidump_system_info_writer.cc new file mode 100644 index 000000000..bc9a5d65d --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_system_info_writer.cc @@ -0,0 +1,323 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_system_info_writer.h" + +#include + +#include + +#include "base/check_op.h" +#include "base/notreached.h" +#include "minidump/minidump_string_writer.h" +#include "snapshot/system_snapshot.h" +#include "util/file/file_writer.h" +#include "util/misc/arraysize.h" +#include "util/misc/implicit_cast.h" + +namespace crashpad { + +namespace { + +uint64_t AMD64FeaturesFromSystemSnapshot( + const SystemSnapshot* system_snapshot) { +#define ADD_FEATURE(minidump_bit) (UINT64_C(1) << (minidump_bit)) + + // Features for which no cpuid bits are present, but that always exist on + // x86_64. cmpxchg is supported on 486 and later. + uint64_t minidump_features = ADD_FEATURE(PF_COMPARE_EXCHANGE_DOUBLE); + +#define MAP_FEATURE(features, cpuid_bit, minidump_bit) \ + do { \ + if ((features) & (implicit_cast(1) << (cpuid_bit))) { \ + minidump_features |= ADD_FEATURE(minidump_bit); \ + } \ + } while (false) + +#define F_TSC 4 +#define F_PAE 6 +#define F_MMX 23 +#define F_SSE 25 +#define F_SSE2 26 +#define F_SSE3 32 +#define F_CX16 45 +#define F_XSAVE 58 +#define F_RDRAND 62 + + uint64_t cpuid_features = system_snapshot->CPUX86Features(); + + MAP_FEATURE(cpuid_features, F_TSC, PF_RDTSC_INSTRUCTION_AVAILABLE); + MAP_FEATURE(cpuid_features, F_PAE, PF_PAE_ENABLED); + MAP_FEATURE(cpuid_features, F_MMX, PF_MMX_INSTRUCTIONS_AVAILABLE); + MAP_FEATURE(cpuid_features, F_SSE, PF_XMMI_INSTRUCTIONS_AVAILABLE); + MAP_FEATURE(cpuid_features, F_SSE2, PF_XMMI64_INSTRUCTIONS_AVAILABLE); + MAP_FEATURE(cpuid_features, F_SSE3, PF_SSE3_INSTRUCTIONS_AVAILABLE); + MAP_FEATURE(cpuid_features, F_CX16, PF_COMPARE_EXCHANGE128); + MAP_FEATURE(cpuid_features, F_XSAVE, PF_XSAVE_ENABLED); + MAP_FEATURE(cpuid_features, F_RDRAND, PF_RDRAND_INSTRUCTION_AVAILABLE); + +#define FX_XD 20 +#define FX_RDTSCP 27 +#define FX_3DNOW 31 + + uint64_t extended_features = system_snapshot->CPUX86ExtendedFeatures(); + + MAP_FEATURE(extended_features, FX_RDTSCP, PF_RDTSCP_INSTRUCTION_AVAILABLE); + MAP_FEATURE(extended_features, FX_3DNOW, PF_3DNOW_INSTRUCTIONS_AVAILABLE); + +#define F7_FSGSBASE 0 + + uint32_t leaf7_features = system_snapshot->CPUX86Leaf7Features(); + + MAP_FEATURE(leaf7_features, F7_FSGSBASE, PF_RDWRFSGSBASE_AVAILABLE); + + // This feature bit should be set if NX (XD, DEP) is enabled, not just if it’s + // available on the CPU as indicated by the FX_XD bit. + if (system_snapshot->NXEnabled()) { + minidump_features |= ADD_FEATURE(PF_NX_ENABLED); + } + + if (system_snapshot->CPUX86SupportsDAZ()) { + minidump_features |= ADD_FEATURE(PF_SSE_DAZ_MODE_AVAILABLE); + } + + // PF_SECOND_LEVEL_ADDRESS_TRANSLATION can’t be determined without consulting + // model-specific registers, a privileged operation. The exact use of + // PF_VIRT_FIRMWARE_ENABLED is unknown. PF_FASTFAIL_AVAILABLE is irrelevant + // outside of Windows. + +#undef MAP_FEATURE +#undef ADD_FEATURE + + return minidump_features; +} + +} // namespace + +MinidumpSystemInfoWriter::MinidumpSystemInfoWriter() + : MinidumpStreamWriter(), system_info_(), csd_version_() { + system_info_.ProcessorArchitecture = kMinidumpCPUArchitectureUnknown; +} + +MinidumpSystemInfoWriter::~MinidumpSystemInfoWriter() { +} + +void MinidumpSystemInfoWriter::InitializeFromSnapshot( + const SystemSnapshot* system_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(!csd_version_); + + MinidumpCPUArchitecture cpu_architecture; + switch (system_snapshot->GetCPUArchitecture()) { + case kCPUArchitectureX86: + cpu_architecture = kMinidumpCPUArchitectureX86; + break; + case kCPUArchitectureX86_64: + cpu_architecture = kMinidumpCPUArchitectureAMD64; + break; + case kCPUArchitectureARM: + cpu_architecture = kMinidumpCPUArchitectureARM; + break; + case kCPUArchitectureARM64: + cpu_architecture = kMinidumpCPUArchitectureARM64; + break; + default: + NOTREACHED(); + cpu_architecture = kMinidumpCPUArchitectureUnknown; + break; + } + SetCPUArchitecture(cpu_architecture); + + uint32_t cpu_revision = system_snapshot->CPURevision(); + SetCPULevelAndRevision((cpu_revision & 0xffff0000) >> 16, + cpu_revision & 0x0000ffff); + SetCPUCount(system_snapshot->CPUCount()); + + if (cpu_architecture == kMinidumpCPUArchitectureX86) { + std::string cpu_vendor = system_snapshot->CPUVendor(); + SetCPUX86VendorString(cpu_vendor); + + // The minidump file format only has room for the bottom 32 bits of CPU + // features and extended CPU features. + SetCPUX86VersionAndFeatures(system_snapshot->CPUX86Signature(), + system_snapshot->CPUX86Features() & 0xffffffff); + + if (cpu_vendor == "AuthenticAMD" || cpu_vendor == "HygonGenuine") { + SetCPUX86AMDExtendedFeatures( + system_snapshot->CPUX86ExtendedFeatures() & 0xffffffff); + } + } else if (cpu_architecture == kMinidumpCPUArchitectureAMD64) { + SetCPUOtherFeatures(AMD64FeaturesFromSystemSnapshot(system_snapshot), 0); + } + + MinidumpOS operating_system; + switch (system_snapshot->GetOperatingSystem()) { + case SystemSnapshot::kOperatingSystemMacOSX: + operating_system = kMinidumpOSMacOSX; + break; + case SystemSnapshot::kOperatingSystemWindows: + operating_system = kMinidumpOSWin32NT; + break; + case SystemSnapshot::kOperatingSystemLinux: + operating_system = kMinidumpOSLinux; + break; + case SystemSnapshot::kOperatingSystemAndroid: + operating_system = kMinidumpOSAndroid; + break; + case SystemSnapshot::kOperatingSystemFuchsia: + operating_system = kMinidumpOSFuchsia; + break; + case SystemSnapshot::kOperatingSystemIOS: + operating_system = kMinidumpOSIOS; + break; + default: + NOTREACHED(); + operating_system = kMinidumpOSUnknown; + break; + } + SetOS(operating_system); + + SetOSType(system_snapshot->OSServer() ? kMinidumpOSTypeServer + : kMinidumpOSTypeWorkstation); + + int major; + int minor; + int bugfix; + std::string build; + system_snapshot->OSVersion(&major, &minor, &bugfix, &build); + SetOSVersion(major, minor, bugfix); + SetCSDVersion(build); +} + +void MinidumpSystemInfoWriter::SetCSDVersion(const std::string& csd_version) { + DCHECK_EQ(state(), kStateMutable); + + if (!csd_version_) { + csd_version_.reset(new internal::MinidumpUTF16StringWriter()); + } + + csd_version_->SetUTF8(csd_version); +} + +void MinidumpSystemInfoWriter::SetCPUX86Vendor(uint32_t ebx, + uint32_t edx, + uint32_t ecx) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(system_info_.ProcessorArchitecture == kMinidumpCPUArchitectureX86 || + system_info_.ProcessorArchitecture == + kMinidumpCPUArchitectureX86Win64); + + static_assert(ArraySize(system_info_.Cpu.X86CpuInfo.VendorId) == 3, + "VendorId must have 3 elements"); + + system_info_.Cpu.X86CpuInfo.VendorId[0] = ebx; + system_info_.Cpu.X86CpuInfo.VendorId[1] = edx; + system_info_.Cpu.X86CpuInfo.VendorId[2] = ecx; +} + +void MinidumpSystemInfoWriter::SetCPUX86VendorString( + const std::string& vendor) { + DCHECK_EQ(state(), kStateMutable); + CHECK_EQ(vendor.size(), sizeof(system_info_.Cpu.X86CpuInfo.VendorId)); + + uint32_t registers[3]; + static_assert( + sizeof(registers) == sizeof(system_info_.Cpu.X86CpuInfo.VendorId), + "VendorId sizes must be equal"); + + for (size_t index = 0; index < std::size(registers); ++index) { + memcpy(®isters[index], + &vendor[index * sizeof(*registers)], + sizeof(*registers)); + } + + SetCPUX86Vendor(registers[0], registers[1], registers[2]); +} + +void MinidumpSystemInfoWriter::SetCPUX86VersionAndFeatures(uint32_t version, + uint32_t features) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(system_info_.ProcessorArchitecture == kMinidumpCPUArchitectureX86 || + system_info_.ProcessorArchitecture == + kMinidumpCPUArchitectureX86Win64); + + system_info_.Cpu.X86CpuInfo.VersionInformation = version; + system_info_.Cpu.X86CpuInfo.FeatureInformation = features; +} + +void MinidumpSystemInfoWriter::SetCPUX86AMDExtendedFeatures( + uint32_t extended_features) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(system_info_.ProcessorArchitecture == kMinidumpCPUArchitectureX86 || + system_info_.ProcessorArchitecture == + kMinidumpCPUArchitectureX86Win64); + DCHECK(system_info_.Cpu.X86CpuInfo.VendorId[0] == 'htuA' && + system_info_.Cpu.X86CpuInfo.VendorId[1] == 'itne' && + system_info_.Cpu.X86CpuInfo.VendorId[2] == 'DMAc'); + + system_info_.Cpu.X86CpuInfo.AMDExtendedCpuFeatures = extended_features; +} + +void MinidumpSystemInfoWriter::SetCPUOtherFeatures(uint64_t features_0, + uint64_t features_1) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(system_info_.ProcessorArchitecture != kMinidumpCPUArchitectureX86 && + system_info_.ProcessorArchitecture != + kMinidumpCPUArchitectureX86Win64); + + static_assert(ArraySize(system_info_.Cpu.OtherCpuInfo.ProcessorFeatures) == 2, + "ProcessorFeatures must have 2 elements"); + + system_info_.Cpu.OtherCpuInfo.ProcessorFeatures[0] = features_0; + system_info_.Cpu.OtherCpuInfo.ProcessorFeatures[1] = features_1; +} + +bool MinidumpSystemInfoWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + CHECK(csd_version_); + + if (!MinidumpStreamWriter::Freeze()) { + return false; + } + + csd_version_->RegisterRVA(&system_info_.CSDVersionRva); + + return true; +} + +size_t MinidumpSystemInfoWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return sizeof(system_info_); +} + +std::vector MinidumpSystemInfoWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + DCHECK(csd_version_); + + std::vector children(1, csd_version_.get()); + return children; +} + +bool MinidumpSystemInfoWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + return file_writer->Write(&system_info_, sizeof(system_info_)); +} + +MinidumpStreamType MinidumpSystemInfoWriter::StreamType() const { + return kMinidumpStreamTypeSystemInfo; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_system_info_writer.h b/shared/sentry/external/crashpad/minidump/minidump_system_info_writer.h new file mode 100644 index 000000000..07dbcf35b --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_system_info_writer.h @@ -0,0 +1,198 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_SYSTEM_INFO_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_SYSTEM_INFO_WRITER_H_ + +#include +#include +#include +#include + +#include +#include +#include + +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_stream_writer.h" +#include "minidump/minidump_writable.h" + +namespace crashpad { + +class SystemSnapshot; + +namespace internal { +class MinidumpUTF16StringWriter; +} // namespace internal + +//! \brief The writer for a MINIDUMP_SYSTEM_INFO stream in a minidump file. +class MinidumpSystemInfoWriter final : public internal::MinidumpStreamWriter { + public: + MinidumpSystemInfoWriter(); + + MinidumpSystemInfoWriter(const MinidumpSystemInfoWriter&) = delete; + MinidumpSystemInfoWriter& operator=(const MinidumpSystemInfoWriter&) = delete; + + ~MinidumpSystemInfoWriter() override; + + //! \brief Initializes MINIDUMP_SYSTEM_INFO based on \a system_snapshot. + //! + //! \param[in] system_snapshot The system snapshot to use as source data. + //! + //! \note Valid in #kStateMutable. No mutator methods may be called before + //! this method, and it is not normally necessary to call any mutator + //! methods after this method. + void InitializeFromSnapshot(const SystemSnapshot* system_snapshot); + + //! \brief Sets MINIDUMP_SYSTEM_INFO::ProcessorArchitecture. + void SetCPUArchitecture(MinidumpCPUArchitecture processor_architecture) { + system_info_.ProcessorArchitecture = processor_architecture; + } + + //! \brief Sets MINIDUMP_SYSTEM_INFO::ProcessorLevel and + //! MINIDUMP_SYSTEM_INFO::ProcessorRevision. + void SetCPULevelAndRevision(uint16_t processor_level, + uint16_t processor_revision) { + system_info_.ProcessorLevel = processor_level; + system_info_.ProcessorRevision = processor_revision; + } + + //! \brief Sets MINIDUMP_SYSTEM_INFO::NumberOfProcessors. + void SetCPUCount(uint8_t number_of_processors) { + system_info_.NumberOfProcessors = number_of_processors; + } + + //! \brief Sets MINIDUMP_SYSTEM_INFO::PlatformId. + void SetOS(MinidumpOS platform_id) { system_info_.PlatformId = platform_id; } + + //! \brief Sets MINIDUMP_SYSTEM_INFO::ProductType. + void SetOSType(MinidumpOSType product_type) { + system_info_.ProductType = product_type; + } + + //! \brief Sets MINIDUMP_SYSTEM_INFO::MajorVersion, + //! MINIDUMP_SYSTEM_INFO::MinorVersion, and + //! MINIDUMP_SYSTEM_INFO::BuildNumber. + void SetOSVersion(uint32_t major_version, + uint32_t minor_version, + uint32_t build_number) { + system_info_.MajorVersion = major_version; + system_info_.MinorVersion = minor_version; + system_info_.BuildNumber = build_number; + } + + //! \brief Arranges for MINIDUMP_SYSTEM_INFO::CSDVersionRva to point to a + //! MINIDUMP_STRING containing the supplied string. + //! + //! This method must be called prior to Freeze(). A CSD version is required + //! in all MINIDUMP_SYSTEM_INFO streams. An empty string is an acceptable + //! value. + void SetCSDVersion(const std::string& csd_version); + + //! \brief Sets MINIDUMP_SYSTEM_INFO::SuiteMask. + void SetSuiteMask(uint16_t suite_mask) { + system_info_.SuiteMask = suite_mask; + } + + //! \brief Sets \ref CPU_INFORMATION::VendorId + //! "MINIDUMP_SYSTEM_INFO::Cpu::X86CpuInfo::VendorId". + //! + //! This is only valid if SetCPUArchitecture() has been used to set the CPU + //! architecture to #kMinidumpCPUArchitectureX86 or + //! #kMinidumpCPUArchitectureX86Win64. + //! + //! \param[in] ebx The first 4 bytes of the CPU vendor string, the value + //! reported in `cpuid 0` `ebx`. + //! \param[in] edx The middle 4 bytes of the CPU vendor string, the value + //! reported in `cpuid 0` `edx`. + //! \param[in] ecx The last 4 bytes of the CPU vendor string, the value + //! reported by `cpuid 0` `ecx`. + //! + //! \note Do not call this method if SetCPUArchitecture() has been used to set + //! the CPU architecture to #kMinidumpCPUArchitectureAMD64. + //! + //! \sa SetCPUX86VendorString() + void SetCPUX86Vendor(uint32_t ebx, uint32_t edx, uint32_t ecx); + + //! \brief Sets \ref CPU_INFORMATION::VendorId + //! "MINIDUMP_SYSTEM_INFO::Cpu::X86CpuInfo::VendorId". + //! + //! This is only valid if SetCPUArchitecture() has been used to set the CPU + //! architecture to #kMinidumpCPUArchitectureX86 or + //! #kMinidumpCPUArchitectureX86Win64. + //! + //! \param[in] vendor The entire CPU vendor string, which must be exactly 12 + //! bytes long. + //! + //! \note Do not call this method if SetCPUArchitecture() has been used to set + //! the CPU architecture to #kMinidumpCPUArchitectureAMD64. + //! + //! \sa SetCPUX86Vendor() + void SetCPUX86VendorString(const std::string& vendor); + + //! \brief Sets \ref CPU_INFORMATION::VersionInformation + //! "MINIDUMP_SYSTEM_INFO::Cpu::X86CpuInfo::VersionInformation" and + //! \ref CPU_INFORMATION::FeatureInformation + //! "MINIDUMP_SYSTEM_INFO::Cpu::X86CpuInfo::FeatureInformation". + //! + //! This is only valid if SetCPUArchitecture() has been used to set the CPU + //! architecture to #kMinidumpCPUArchitectureX86 or + //! #kMinidumpCPUArchitectureX86Win64. + //! + //! \note Do not call this method if SetCPUArchitecture() has been used to set + //! the CPU architecture to #kMinidumpCPUArchitectureAMD64. + void SetCPUX86VersionAndFeatures(uint32_t version, uint32_t features); + + //! \brief Sets \ref CPU_INFORMATION::AMDExtendedCpuFeatures + //! "MINIDUMP_SYSTEM_INFO::Cpu::X86CpuInfo::AMDExtendedCPUFeatures". + //! + //! This is only valid if SetCPUArchitecture() has been used to set the CPU + //! architecture to #kMinidumpCPUArchitectureX86 or + //! #kMinidumpCPUArchitectureX86Win64, and if SetCPUX86Vendor() or + //! SetCPUX86VendorString() has been used to set the CPU vendor to + //! “AuthenticAMDâ€. + //! + //! \note Do not call this method if SetCPUArchitecture() has been used to set + //! the CPU architecture to #kMinidumpCPUArchitectureAMD64. + void SetCPUX86AMDExtendedFeatures(uint32_t extended_features); + + //! \brief Sets \ref CPU_INFORMATION::ProcessorFeatures + //! "MINIDUMP_SYSTEM_INFO::Cpu::OtherCpuInfo::ProcessorFeatures". + //! + //! This is only valid if SetCPUArchitecture() has been used to set the CPU + //! architecture to an architecture other than #kMinidumpCPUArchitectureX86 + //! or #kMinidumpCPUArchitectureX86Win64. + //! + //! \note This method may be called if SetCPUArchitecture() has been used to + //! set the CPU architecture to #kMinidumpCPUArchitectureAMD64. + void SetCPUOtherFeatures(uint64_t features_0, uint64_t features_1); + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpStreamWriter: + MinidumpStreamType StreamType() const override; + + private: + MINIDUMP_SYSTEM_INFO system_info_; + std::unique_ptr csd_version_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_SYSTEM_INFO_WRITER_H_ diff --git a/shared/sentry/external/crashpad/minidump/minidump_system_info_writer_test.cc b/shared/sentry/external/crashpad/minidump/minidump_system_info_writer_test.cc new file mode 100644 index 000000000..81de730bd --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_system_info_writer_test.cc @@ -0,0 +1,487 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_system_info_writer.h" + +#include + +#include +#include +#include + +#include "gtest/gtest.h" +#include "minidump/minidump_file_writer.h" +#include "minidump/test/minidump_file_writer_test_util.h" +#include "minidump/test/minidump_string_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "snapshot/test/test_system_snapshot.h" +#include "test/gtest_death.h" +#include "util/file/string_file.h" + +namespace crashpad { +namespace test { +namespace { + +void GetSystemInfoStream(const std::string& file_contents, + size_t csd_version_length, + const MINIDUMP_SYSTEM_INFO** system_info, + const MINIDUMP_STRING** csd_version) { + // The expected number of bytes for the CSD version’s MINIDUMP_STRING::Buffer. + [[maybe_unused]] MINIDUMP_STRING* tmp; + const size_t kCSDVersionBytes = csd_version_length * sizeof(tmp->Buffer[0]); + const size_t kCSDVersionBytesWithNUL = + kCSDVersionBytes + sizeof(tmp->Buffer[0]); + + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kSystemInfoStreamOffset = + kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + constexpr size_t kCSDVersionOffset = + kSystemInfoStreamOffset + sizeof(MINIDUMP_SYSTEM_INFO); + const size_t kFileSize = + kCSDVersionOffset + sizeof(MINIDUMP_STRING) + kCSDVersionBytesWithNUL; + + ASSERT_EQ(file_contents.size(), kFileSize); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(file_contents, &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0)); + ASSERT_TRUE(directory); + + ASSERT_EQ(directory[0].StreamType, kMinidumpStreamTypeSystemInfo); + EXPECT_EQ(directory[0].Location.Rva, kSystemInfoStreamOffset); + + *system_info = MinidumpWritableAtLocationDescriptor( + file_contents, directory[0].Location); + ASSERT_TRUE(system_info); + + EXPECT_EQ((*system_info)->CSDVersionRva, kCSDVersionOffset); + + *csd_version = + MinidumpStringAtRVA(file_contents, (*system_info)->CSDVersionRva); + EXPECT_EQ((*csd_version)->Length, kCSDVersionBytes); +} + +TEST(MinidumpSystemInfoWriter, Empty) { + MinidumpFileWriter minidump_file_writer; + auto system_info_writer = std::make_unique(); + + system_info_writer->SetCSDVersion(std::string()); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(system_info_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_SYSTEM_INFO* system_info = nullptr; + const MINIDUMP_STRING* csd_version = nullptr; + + ASSERT_NO_FATAL_FAILURE( + GetSystemInfoStream(string_file.string(), 0, &system_info, &csd_version)); + + EXPECT_EQ(system_info->ProcessorArchitecture, + kMinidumpCPUArchitectureUnknown); + EXPECT_EQ(system_info->ProcessorLevel, 0u); + EXPECT_EQ(system_info->ProcessorRevision, 0u); + EXPECT_EQ(system_info->NumberOfProcessors, 0u); + EXPECT_EQ(system_info->ProductType, 0u); + EXPECT_EQ(system_info->MajorVersion, 0u); + EXPECT_EQ(system_info->MinorVersion, 0u); + EXPECT_EQ(system_info->BuildNumber, 0u); + EXPECT_EQ(system_info->PlatformId, 0u); + EXPECT_EQ(system_info->SuiteMask, 0u); + EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[0], 0u); + EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[1], 0u); + EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[2], 0u); + EXPECT_EQ(system_info->Cpu.X86CpuInfo.VersionInformation, 0u); + EXPECT_EQ(system_info->Cpu.X86CpuInfo.FeatureInformation, 0u); + EXPECT_EQ(system_info->Cpu.X86CpuInfo.AMDExtendedCpuFeatures, 0u); + + CPU_INFORMATION other_cpu_info; + memcpy(&other_cpu_info, &system_info->Cpu, sizeof(other_cpu_info)); + EXPECT_EQ(other_cpu_info.OtherCpuInfo.ProcessorFeatures[0], 0u); + EXPECT_EQ(other_cpu_info.OtherCpuInfo.ProcessorFeatures[1], 0u); + + EXPECT_EQ(csd_version->Buffer[0], '\0'); +} + +TEST(MinidumpSystemInfoWriter, X86_Win) { + MinidumpFileWriter minidump_file_writer; + auto system_info_writer = std::make_unique(); + + constexpr MinidumpCPUArchitecture kCPUArchitecture = + kMinidumpCPUArchitectureX86; + constexpr uint16_t kCPULevel = 0x0010; + constexpr uint16_t kCPURevision = 0x0602; + constexpr uint8_t kCPUCount = 1; + constexpr MinidumpOS kOS = kMinidumpOSWin32NT; + constexpr MinidumpOSType kOSType = kMinidumpOSTypeWorkstation; + constexpr uint32_t kOSVersionMajor = 6; + constexpr uint32_t kOSVersionMinor = 1; + constexpr uint32_t kOSVersionBuild = 7601; + static constexpr char kCSDVersion[] = "Service Pack 1"; + constexpr uint16_t kSuiteMask = VER_SUITE_SINGLEUSERTS; + static constexpr char kCPUVendor[] = "AuthenticAMD"; + constexpr uint32_t kCPUVersion = 0x00100f62; + constexpr uint32_t kCPUFeatures = 0x078bfbff; + constexpr uint32_t kAMDFeatures = 0xefd3fbff; + + uint32_t cpu_vendor_registers[3]; + ASSERT_EQ(strlen(kCPUVendor), sizeof(cpu_vendor_registers)); + memcpy(cpu_vendor_registers, kCPUVendor, sizeof(cpu_vendor_registers)); + + system_info_writer->SetCPUArchitecture(kCPUArchitecture); + system_info_writer->SetCPULevelAndRevision(kCPULevel, kCPURevision); + system_info_writer->SetCPUCount(kCPUCount); + system_info_writer->SetOS(kOS); + system_info_writer->SetOSType(kMinidumpOSTypeWorkstation); + system_info_writer->SetOSVersion( + kOSVersionMajor, kOSVersionMinor, kOSVersionBuild); + system_info_writer->SetCSDVersion(kCSDVersion); + system_info_writer->SetSuiteMask(kSuiteMask); + system_info_writer->SetCPUX86VendorString(kCPUVendor); + system_info_writer->SetCPUX86VersionAndFeatures(kCPUVersion, kCPUFeatures); + system_info_writer->SetCPUX86AMDExtendedFeatures(kAMDFeatures); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(system_info_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_SYSTEM_INFO* system_info = nullptr; + const MINIDUMP_STRING* csd_version = nullptr; + + ASSERT_NO_FATAL_FAILURE(GetSystemInfoStream( + string_file.string(), strlen(kCSDVersion), &system_info, &csd_version)); + + EXPECT_EQ(system_info->ProcessorArchitecture, kCPUArchitecture); + EXPECT_EQ(system_info->ProcessorLevel, kCPULevel); + EXPECT_EQ(system_info->ProcessorRevision, kCPURevision); + EXPECT_EQ(system_info->NumberOfProcessors, kCPUCount); + EXPECT_EQ(system_info->ProductType, kOSType); + EXPECT_EQ(system_info->MajorVersion, kOSVersionMajor); + EXPECT_EQ(system_info->MinorVersion, kOSVersionMinor); + EXPECT_EQ(system_info->BuildNumber, kOSVersionBuild); + EXPECT_EQ(system_info->PlatformId, kOS); + EXPECT_EQ(system_info->SuiteMask, kSuiteMask); + EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[0], cpu_vendor_registers[0]); + EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[1], cpu_vendor_registers[1]); + EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[2], cpu_vendor_registers[2]); + EXPECT_EQ(system_info->Cpu.X86CpuInfo.VersionInformation, kCPUVersion); + EXPECT_EQ(system_info->Cpu.X86CpuInfo.FeatureInformation, kCPUFeatures); + EXPECT_EQ(system_info->Cpu.X86CpuInfo.AMDExtendedCpuFeatures, kAMDFeatures); + + for (size_t index = 0; index < strlen(kCSDVersion); ++index) { + EXPECT_EQ(csd_version->Buffer[index], kCSDVersion[index]) << index; + } +} + +TEST(MinidumpSystemInfoWriter, AMD64_Mac) { + MinidumpFileWriter minidump_file_writer; + auto system_info_writer = std::make_unique(); + + constexpr MinidumpCPUArchitecture kCPUArchitecture = + kMinidumpCPUArchitectureAMD64; + constexpr uint16_t kCPULevel = 0x0006; + constexpr uint16_t kCPURevision = 0x3a09; + constexpr uint8_t kCPUCount = 8; + constexpr MinidumpOS kOS = kMinidumpOSMacOSX; + constexpr MinidumpOSType kOSType = kMinidumpOSTypeWorkstation; + constexpr uint32_t kOSVersionMajor = 10; + constexpr uint32_t kOSVersionMinor = 9; + constexpr uint32_t kOSVersionBuild = 4; + static constexpr char kCSDVersion[] = "13E28"; + static constexpr uint64_t kCPUFeatures[2] = {0x10427f4c, 0x00000000}; + + system_info_writer->SetCPUArchitecture(kCPUArchitecture); + system_info_writer->SetCPULevelAndRevision(kCPULevel, kCPURevision); + system_info_writer->SetCPUCount(kCPUCount); + system_info_writer->SetOS(kOS); + system_info_writer->SetOSType(kMinidumpOSTypeWorkstation); + system_info_writer->SetOSVersion( + kOSVersionMajor, kOSVersionMinor, kOSVersionBuild); + system_info_writer->SetCSDVersion(kCSDVersion); + system_info_writer->SetCPUOtherFeatures(kCPUFeatures[0], kCPUFeatures[1]); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(system_info_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_SYSTEM_INFO* system_info = nullptr; + const MINIDUMP_STRING* csd_version; + + ASSERT_NO_FATAL_FAILURE(GetSystemInfoStream( + string_file.string(), strlen(kCSDVersion), &system_info, &csd_version)); + + EXPECT_EQ(system_info->ProcessorArchitecture, kCPUArchitecture); + EXPECT_EQ(system_info->ProcessorLevel, kCPULevel); + EXPECT_EQ(system_info->ProcessorRevision, kCPURevision); + EXPECT_EQ(system_info->NumberOfProcessors, kCPUCount); + EXPECT_EQ(system_info->ProductType, kOSType); + EXPECT_EQ(system_info->MajorVersion, kOSVersionMajor); + EXPECT_EQ(system_info->MinorVersion, kOSVersionMinor); + EXPECT_EQ(system_info->BuildNumber, kOSVersionBuild); + EXPECT_EQ(system_info->PlatformId, kOS); + EXPECT_EQ(system_info->SuiteMask, 0u); + + CPU_INFORMATION other_cpu_info; + memcpy(&other_cpu_info, &system_info->Cpu, sizeof(other_cpu_info)); + EXPECT_EQ(other_cpu_info.OtherCpuInfo.ProcessorFeatures[0], kCPUFeatures[0]); + EXPECT_EQ(other_cpu_info.OtherCpuInfo.ProcessorFeatures[1], kCPUFeatures[1]); +} + +TEST(MinidumpSystemInfoWriter, X86_CPUVendorFromRegisters) { + // MinidumpSystemInfoWriter.X86_Win already tested SetCPUX86VendorString(). + // This test exercises SetCPUX86Vendor() to set the vendor from register + // values. + MinidumpFileWriter minidump_file_writer; + auto system_info_writer = std::make_unique(); + + constexpr MinidumpCPUArchitecture kCPUArchitecture = + kMinidumpCPUArchitectureX86; + static constexpr uint32_t kCPUVendor[] = {'uneG', 'Ieni', 'letn'}; + + system_info_writer->SetCPUArchitecture(kCPUArchitecture); + system_info_writer->SetCPUX86Vendor( + kCPUVendor[0], kCPUVendor[1], kCPUVendor[2]); + system_info_writer->SetCSDVersion(std::string()); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(system_info_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_SYSTEM_INFO* system_info = nullptr; + const MINIDUMP_STRING* csd_version; + + ASSERT_NO_FATAL_FAILURE( + GetSystemInfoStream(string_file.string(), 0, &system_info, &csd_version)); + + EXPECT_EQ(system_info->ProcessorArchitecture, kCPUArchitecture); + EXPECT_EQ(system_info->ProcessorLevel, 0u); + EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[0], kCPUVendor[0]); + EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[1], kCPUVendor[1]); + EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[2], kCPUVendor[2]); + EXPECT_EQ(system_info->Cpu.X86CpuInfo.VersionInformation, 0u); +} + +TEST(MinidumpSystemInfoWriter, InitializeFromSnapshot_X86) { + MINIDUMP_SYSTEM_INFO expect_system_info = {}; + + constexpr uint16_t kCPUFamily = 6; + constexpr uint8_t kCPUModel = 70; + constexpr uint8_t kCPUStepping = 1; + + const uint8_t kCPUBasicFamily = + static_cast(std::min(kCPUFamily, static_cast(15))); + const uint8_t kCPUExtendedFamily = kCPUFamily - kCPUBasicFamily; + + // These checks ensure that even if the constants above change, they represent + // something that can legitimately be encoded in the form used by cpuid 1 eax. + EXPECT_LE(kCPUFamily, 270); + EXPECT_LE(kCPUStepping, 15); + EXPECT_TRUE(kCPUBasicFamily == 6 || kCPUBasicFamily == 15 || kCPUModel <= 15); + + constexpr uint8_t kCPUBasicModel = kCPUModel & 0xf; + constexpr uint8_t kCPUExtendedModel = kCPUModel >> 4; + const uint32_t kCPUSignature = + (kCPUExtendedFamily << 20) | (kCPUExtendedModel << 16) | + (kCPUBasicFamily << 8) | (kCPUBasicModel << 4) | kCPUStepping; + constexpr uint64_t kCPUX86Features = 0x7ffafbffbfebfbff; + expect_system_info.ProcessorArchitecture = kMinidumpCPUArchitectureX86; + expect_system_info.ProcessorLevel = kCPUFamily; + expect_system_info.ProcessorRevision = (kCPUModel << 8) | kCPUStepping; + expect_system_info.NumberOfProcessors = 8; + expect_system_info.ProductType = kMinidumpOSTypeServer; + expect_system_info.MajorVersion = 10; + expect_system_info.MinorVersion = 9; + expect_system_info.BuildNumber = 5; + expect_system_info.PlatformId = kMinidumpOSMacOSX; + expect_system_info.SuiteMask = 0; + expect_system_info.Cpu.X86CpuInfo.VendorId[0] = 'uneG'; + expect_system_info.Cpu.X86CpuInfo.VendorId[1] = 'Ieni'; + expect_system_info.Cpu.X86CpuInfo.VendorId[2] = 'letn'; + expect_system_info.Cpu.X86CpuInfo.VersionInformation = kCPUSignature; + expect_system_info.Cpu.X86CpuInfo.FeatureInformation = + kCPUX86Features & 0xffffffff; + static constexpr char kCPUVendor[] = "GenuineIntel"; + static constexpr char kOSVersionBuild[] = "13F34"; + + TestSystemSnapshot system_snapshot; + system_snapshot.SetCPUArchitecture(kCPUArchitectureX86); + system_snapshot.SetCPURevision( + (kCPUFamily << 16) | (kCPUModel << 8) | kCPUStepping); + system_snapshot.SetCPUCount(expect_system_info.NumberOfProcessors); + system_snapshot.SetCPUVendor(kCPUVendor); + system_snapshot.SetCPUX86Signature(kCPUSignature); + system_snapshot.SetCPUX86Features(kCPUX86Features); + system_snapshot.SetOperatingSystem(SystemSnapshot::kOperatingSystemMacOSX); + system_snapshot.SetOSServer(true); + system_snapshot.SetOSVersion(expect_system_info.MajorVersion, + expect_system_info.MinorVersion, + expect_system_info.BuildNumber, + kOSVersionBuild); + + auto system_info_writer = std::make_unique(); + system_info_writer->InitializeFromSnapshot(&system_snapshot); + + MinidumpFileWriter minidump_file_writer; + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(system_info_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_SYSTEM_INFO* system_info = nullptr; + const MINIDUMP_STRING* csd_version = nullptr; + ASSERT_NO_FATAL_FAILURE(GetSystemInfoStream(string_file.string(), + strlen(kOSVersionBuild), + &system_info, + &csd_version)); + + EXPECT_EQ(system_info->ProcessorArchitecture, + expect_system_info.ProcessorArchitecture); + EXPECT_EQ(system_info->ProcessorLevel, expect_system_info.ProcessorLevel); + EXPECT_EQ(system_info->ProcessorRevision, + expect_system_info.ProcessorRevision); + EXPECT_EQ(system_info->NumberOfProcessors, + expect_system_info.NumberOfProcessors); + EXPECT_EQ(system_info->ProductType, expect_system_info.ProductType); + EXPECT_EQ(system_info->MajorVersion, expect_system_info.MajorVersion); + EXPECT_EQ(system_info->MinorVersion, expect_system_info.MinorVersion); + EXPECT_EQ(system_info->BuildNumber, expect_system_info.BuildNumber); + EXPECT_EQ(system_info->PlatformId, expect_system_info.PlatformId); + EXPECT_EQ(system_info->SuiteMask, expect_system_info.SuiteMask); + EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[0], + expect_system_info.Cpu.X86CpuInfo.VendorId[0]); + EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[1], + expect_system_info.Cpu.X86CpuInfo.VendorId[1]); + EXPECT_EQ(system_info->Cpu.X86CpuInfo.VendorId[2], + expect_system_info.Cpu.X86CpuInfo.VendorId[2]); + EXPECT_EQ(system_info->Cpu.X86CpuInfo.VersionInformation, + expect_system_info.Cpu.X86CpuInfo.VersionInformation); + EXPECT_EQ(system_info->Cpu.X86CpuInfo.FeatureInformation, + expect_system_info.Cpu.X86CpuInfo.FeatureInformation); + + for (size_t index = 0; index < strlen(kOSVersionBuild); ++index) { + EXPECT_EQ(csd_version->Buffer[index], kOSVersionBuild[index]) << index; + } +} + +TEST(MinidumpSystemInfoWriter, InitializeFromSnapshot_AMD64) { + MINIDUMP_SYSTEM_INFO expect_system_info = {}; + + constexpr uint8_t kCPUFamily = 6; + constexpr uint8_t kCPUModel = 70; + constexpr uint8_t kCPUStepping = 1; + expect_system_info.ProcessorArchitecture = kMinidumpCPUArchitectureAMD64; + expect_system_info.ProcessorLevel = kCPUFamily; + expect_system_info.ProcessorRevision = (kCPUModel << 8) | kCPUStepping; + expect_system_info.NumberOfProcessors = 8; + expect_system_info.ProductType = kMinidumpOSTypeServer; + expect_system_info.MajorVersion = 10; + expect_system_info.MinorVersion = 9; + expect_system_info.BuildNumber = 5; + expect_system_info.PlatformId = kMinidumpOSMacOSX; + expect_system_info.SuiteMask = 0; + expect_system_info.Cpu.OtherCpuInfo.ProcessorFeatures[0] = + (1 << PF_COMPARE_EXCHANGE_DOUBLE) | + (1 << PF_MMX_INSTRUCTIONS_AVAILABLE) | + (1 << PF_XMMI_INSTRUCTIONS_AVAILABLE) | + (1 << PF_RDTSC_INSTRUCTION_AVAILABLE) | + (1 << PF_PAE_ENABLED) | + (1 << PF_XMMI64_INSTRUCTIONS_AVAILABLE) | + (1 << PF_SSE_DAZ_MODE_AVAILABLE) | + (1 << PF_NX_ENABLED) | + (1 << PF_SSE3_INSTRUCTIONS_AVAILABLE) | + (1 << PF_COMPARE_EXCHANGE128) | + (1 << PF_XSAVE_ENABLED) | + (1 << PF_RDWRFSGSBASE_AVAILABLE) | + (1 << PF_RDRAND_INSTRUCTION_AVAILABLE) | + (UINT64_C(1) << PF_RDTSCP_INSTRUCTION_AVAILABLE); + expect_system_info.Cpu.OtherCpuInfo.ProcessorFeatures[1] = 0; + static constexpr char kOSVersionBuild[] = "13F34"; + + TestSystemSnapshot system_snapshot; + system_snapshot.SetCPUArchitecture(kCPUArchitectureX86_64); + system_snapshot.SetCPURevision( + (kCPUFamily << 16) | (kCPUModel << 8) | kCPUStepping); + system_snapshot.SetCPUCount(expect_system_info.NumberOfProcessors); + system_snapshot.SetCPUX86Features(0x7ffafbffbfebfbff); + system_snapshot.SetCPUX86ExtendedFeatures(0x000000212c100900); + system_snapshot.SetCPUX86Leaf7Features(0x00002fbb); + system_snapshot.SetCPUX86SupportsDAZ(true); + system_snapshot.SetOperatingSystem(SystemSnapshot::kOperatingSystemMacOSX); + system_snapshot.SetOSServer(true); + system_snapshot.SetOSVersion(expect_system_info.MajorVersion, + expect_system_info.MinorVersion, + expect_system_info.BuildNumber, + kOSVersionBuild); + system_snapshot.SetNXEnabled(true); + + auto system_info_writer = std::make_unique(); + system_info_writer->InitializeFromSnapshot(&system_snapshot); + + MinidumpFileWriter minidump_file_writer; + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(system_info_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_SYSTEM_INFO* system_info = nullptr; + const MINIDUMP_STRING* csd_version = nullptr; + ASSERT_NO_FATAL_FAILURE(GetSystemInfoStream(string_file.string(), + strlen(kOSVersionBuild), + &system_info, + &csd_version)); + + EXPECT_EQ(system_info->ProcessorArchitecture, + expect_system_info.ProcessorArchitecture); + EXPECT_EQ(system_info->ProcessorLevel, expect_system_info.ProcessorLevel); + EXPECT_EQ(system_info->ProcessorRevision, + expect_system_info.ProcessorRevision); + EXPECT_EQ(system_info->NumberOfProcessors, + expect_system_info.NumberOfProcessors); + EXPECT_EQ(system_info->ProductType, expect_system_info.ProductType); + EXPECT_EQ(system_info->MajorVersion, expect_system_info.MajorVersion); + EXPECT_EQ(system_info->MinorVersion, expect_system_info.MinorVersion); + EXPECT_EQ(system_info->BuildNumber, expect_system_info.BuildNumber); + EXPECT_EQ(system_info->PlatformId, expect_system_info.PlatformId); + EXPECT_EQ(system_info->SuiteMask, expect_system_info.SuiteMask); + + CPU_INFORMATION other_cpu_info; + memcpy(&other_cpu_info, &system_info->Cpu, sizeof(other_cpu_info)); + EXPECT_EQ(other_cpu_info.OtherCpuInfo.ProcessorFeatures[0], + expect_system_info.Cpu.OtherCpuInfo.ProcessorFeatures[0]); + EXPECT_EQ(other_cpu_info.OtherCpuInfo.ProcessorFeatures[1], + expect_system_info.Cpu.OtherCpuInfo.ProcessorFeatures[1]); + + for (size_t index = 0; index < strlen(kOSVersionBuild); ++index) { + EXPECT_EQ(csd_version->Buffer[index], kOSVersionBuild[index]) << index; + } +} + +TEST(MinidumpSystemInfoWriterDeathTest, NoCSDVersion) { + MinidumpFileWriter minidump_file_writer; + auto system_info_writer = std::make_unique(); + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(system_info_writer))); + + StringFile string_file; + ASSERT_DEATH_CHECK(minidump_file_writer.WriteEverything(&string_file), + "csd_version_"); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_thread_id_map.cc b/shared/sentry/external/crashpad/minidump/minidump_thread_id_map.cc new file mode 100644 index 000000000..3e9e39298 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_thread_id_map.cc @@ -0,0 +1,67 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_thread_id_map.h" + +#include +#include +#include + +#include "base/check_op.h" +#include "base/numerics/safe_conversions.h" +#include "snapshot/thread_snapshot.h" + +namespace crashpad { + +void BuildMinidumpThreadIDMap( + const std::vector& thread_snapshots, + MinidumpThreadIDMap* thread_id_map) { + DCHECK(thread_id_map->empty()); + + // First, try truncating each 64-bit thread ID to 32 bits. If that’s possible + // for each unique 64-bit thread ID, then this will be used as the mapping. + // This preserves as much of the original thread ID as possible when feasible. + bool collision = false; + std::set thread_ids_32; + for (const ThreadSnapshot* thread_snapshot : thread_snapshots) { + uint64_t thread_id_64 = thread_snapshot->ThreadID(); + if (thread_id_map->find(thread_id_64) == thread_id_map->end()) { + uint32_t thread_id_32 = static_cast(thread_id_64); + if (!thread_ids_32.insert(thread_id_32).second) { + collision = true; + break; + } + thread_id_map->insert(std::make_pair(thread_id_64, thread_id_32)); + } + } + + if (collision) { + // Since there was a collision, go back and assign each unique 64-bit thread + // ID its own sequential 32-bit equivalent. The 32-bit thread IDs will not + // bear any resemblance to the original 64-bit thread IDs. + thread_id_map->clear(); + for (const ThreadSnapshot* thread_snapshot : thread_snapshots) { + uint64_t thread_id_64 = thread_snapshot->ThreadID(); + if (thread_id_map->find(thread_id_64) == thread_id_map->end()) { + uint32_t thread_id_32 = + base::checked_cast(thread_id_map->size()); + thread_id_map->insert(std::make_pair(thread_id_64, thread_id_32)); + } + } + + DCHECK_LE(thread_id_map->size(), std::numeric_limits::max()); + } +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_thread_id_map.h b/shared/sentry/external/crashpad/minidump/minidump_thread_id_map.h new file mode 100644 index 000000000..33b105fba --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_thread_id_map.h @@ -0,0 +1,52 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_THREAD_ID_MAP_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_THREAD_ID_MAP_H_ + +#include + +#include +#include + +namespace crashpad { + +class ThreadSnapshot; + +//! \brief A map that connects 64-bit snapshot thread IDs to 32-bit minidump +//! thread IDs. +//! +//! 64-bit snapshot thread IDs are obtained from ThreadSnapshot::ThreadID(). +//! 32-bit minidump thread IDs are stored in MINIDUMP_THREAD::ThreadId. +//! +//! A ThreadIDMap ensures that there are no collisions among the set of 32-bit +//! minidump thread IDs. +using MinidumpThreadIDMap = std::map; + +//! \brief Builds a MinidumpThreadIDMap for a group of ThreadSnapshot objects. +//! +//! \param[in] thread_snapshots The thread snapshots to use as source data. +//! \param[out] thread_id_map A MinidumpThreadIDMap to be built by this method. +//! This map must be empty when this function is called. +//! +//! The map ensures that for any unique 64-bit thread ID found in a +//! ThreadSnapshot, the 32-bit thread ID used in a minidump file will also be +//! unique. +void BuildMinidumpThreadIDMap( + const std::vector& thread_snapshots, + MinidumpThreadIDMap* thread_id_map); + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_THREAD_ID_MAP_H_ diff --git a/shared/sentry/external/crashpad/minidump/minidump_thread_id_map_test.cc b/shared/sentry/external/crashpad/minidump/minidump_thread_id_map_test.cc new file mode 100644 index 000000000..0883d9a55 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_thread_id_map_test.cc @@ -0,0 +1,193 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_thread_id_map.h" + +#include + +#include +#include + +#include "gtest/gtest.h" +#include "snapshot/test/test_thread_snapshot.h" + +namespace crashpad { +namespace test { +namespace { + +class MinidumpThreadIDMapTest : public testing::Test { + public: + MinidumpThreadIDMapTest() + : Test(), + thread_snapshots_(), + test_thread_snapshots_() { + } + + MinidumpThreadIDMapTest(const MinidumpThreadIDMapTest&) = delete; + MinidumpThreadIDMapTest& operator=(const MinidumpThreadIDMapTest&) = delete; + + ~MinidumpThreadIDMapTest() override {} + + // testing::Test: + void SetUp() override { + for (size_t index = 0; index < std::size(test_thread_snapshots_); ++index) { + thread_snapshots_.push_back(&test_thread_snapshots_[index]); + } + } + + protected: + static bool MapHasKeyValue( + const MinidumpThreadIDMap* map, uint64_t key, uint32_t expected_value) { + auto iterator = map->find(key); + if (iterator == map->end()) { + EXPECT_NE(iterator, map->end()); + return false; + } + if (iterator->second != expected_value) { + EXPECT_EQ(iterator->second, expected_value); + return false; + } + return true; + } + + void SetThreadID(size_t index, uint64_t thread_id) { + ASSERT_LT(index, std::size(test_thread_snapshots_)); + test_thread_snapshots_[index].SetThreadID(thread_id); + } + + const std::vector& thread_snapshots() const { + return thread_snapshots_; + } + + private: + std::vector thread_snapshots_; + TestThreadSnapshot test_thread_snapshots_[5]; +}; + +TEST_F(MinidumpThreadIDMapTest, NoThreads) { + // Don’t use thread_snapshots(), because it’s got some threads in it, and the + // point of this test is to make sure that BuildMinidumpThreadIDMap() works + // with no threads. + std::vector thread_snapshots; + MinidumpThreadIDMap thread_id_map; + BuildMinidumpThreadIDMap(thread_snapshots, &thread_id_map); + + EXPECT_TRUE(thread_id_map.empty()); +} + +TEST_F(MinidumpThreadIDMapTest, SimpleMapping) { + SetThreadID(0, 1); + SetThreadID(1, 3); + SetThreadID(2, 5); + SetThreadID(3, 7); + SetThreadID(4, 9); + + MinidumpThreadIDMap thread_id_map; + BuildMinidumpThreadIDMap(thread_snapshots(), &thread_id_map); + + EXPECT_EQ(thread_id_map.size(), 5u); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 1, 1); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 3, 3); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 5, 5); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 7, 7); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 9, 9); +} + +TEST_F(MinidumpThreadIDMapTest, Truncation) { + SetThreadID(0, 0x0000000000000000); + SetThreadID(1, 0x9999999900000001); + SetThreadID(2, 0x9999999980000001); + SetThreadID(3, 0x99999999fffffffe); + SetThreadID(4, 0x99999999ffffffff); + + MinidumpThreadIDMap thread_id_map; + BuildMinidumpThreadIDMap(thread_snapshots(), &thread_id_map); + + EXPECT_EQ(thread_id_map.size(), 5u); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000000, 0x00000000); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x9999999900000001, 0x00000001); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x9999999980000001, 0x80000001); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x99999999fffffffe, 0xfffffffe); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x99999999ffffffff, 0xffffffff); +} + +TEST_F(MinidumpThreadIDMapTest, DuplicateThreadID) { + SetThreadID(0, 2); + SetThreadID(1, 4); + SetThreadID(2, 4); + SetThreadID(3, 6); + SetThreadID(4, 8); + + MinidumpThreadIDMap thread_id_map; + BuildMinidumpThreadIDMap(thread_snapshots(), &thread_id_map); + + EXPECT_EQ(thread_id_map.size(), 4u); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 2, 2); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 4, 4); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 6, 6); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 8, 8); +} + +TEST_F(MinidumpThreadIDMapTest, Collision) { + SetThreadID(0, 0x0000000000000010); + SetThreadID(1, 0x0000000000000020); + SetThreadID(2, 0x0000000000000030); + SetThreadID(3, 0x0000000000000040); + SetThreadID(4, 0x0000000100000010); + + MinidumpThreadIDMap thread_id_map; + BuildMinidumpThreadIDMap(thread_snapshots(), &thread_id_map); + + EXPECT_EQ(thread_id_map.size(), 5u); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000010, 0); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000020, 1); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000030, 2); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000040, 3); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000100000010, 4); +} + +TEST_F(MinidumpThreadIDMapTest, DuplicateAndCollision) { + SetThreadID(0, 0x0000000100000010); + SetThreadID(1, 0x0000000000000010); + SetThreadID(2, 0x0000000000000020); + SetThreadID(3, 0x0000000000000030); + SetThreadID(4, 0x0000000000000020); + + MinidumpThreadIDMap thread_id_map; + BuildMinidumpThreadIDMap(thread_snapshots(), &thread_id_map); + + EXPECT_EQ(thread_id_map.size(), 4u); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000100000010, 0); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000010, 1); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000020, 2); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 0x0000000000000030, 3); +} + +TEST_F(MinidumpThreadIDMapTest, AllDuplicates) { + SetThreadID(0, 6); + SetThreadID(1, 6); + SetThreadID(2, 6); + SetThreadID(3, 6); + SetThreadID(4, 6); + + MinidumpThreadIDMap thread_id_map; + BuildMinidumpThreadIDMap(thread_snapshots(), &thread_id_map); + + EXPECT_EQ(thread_id_map.size(), 1u); + EXPECT_PRED3(MapHasKeyValue, &thread_id_map, 6, 6); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_thread_writer.cc b/shared/sentry/external/crashpad/minidump/minidump_thread_writer.cc new file mode 100644 index 000000000..67658e18a --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_thread_writer.cc @@ -0,0 +1,237 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_thread_writer.h" + +#include + +#include "base/logging.h" +#include "minidump/minidump_context_writer.h" +#include "minidump/minidump_memory_writer.h" +#include "snapshot/memory_snapshot.h" +#include "snapshot/thread_snapshot.h" +#include "util/file/file_writer.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { + +MinidumpThreadWriter::MinidumpThreadWriter() + : MinidumpWritable(), thread_(), stack_(nullptr), context_(nullptr) { +} + +MinidumpThreadWriter::~MinidumpThreadWriter() { +} + +void MinidumpThreadWriter::InitializeFromSnapshot( + const ThreadSnapshot* thread_snapshot, + const MinidumpThreadIDMap* thread_id_map) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(!stack_); + DCHECK(!context_); + + auto thread_id_it = thread_id_map->find(thread_snapshot->ThreadID()); + DCHECK(thread_id_it != thread_id_map->end()); + SetThreadID(thread_id_it->second); + + SetSuspendCount(thread_snapshot->SuspendCount()); + SetPriority(thread_snapshot->Priority()); + SetTEB(thread_snapshot->ThreadSpecificDataAddress()); + + const MemorySnapshot* stack_snapshot = thread_snapshot->Stack(); + if (stack_snapshot && stack_snapshot->Size() > 0) { + std::unique_ptr stack( + new SnapshotMinidumpMemoryWriter(stack_snapshot)); + SetStack(std::move(stack)); + } + + std::unique_ptr context = + MinidumpContextWriter::CreateFromSnapshot(thread_snapshot->Context()); + SetContext(std::move(context)); +} + +const MINIDUMP_THREAD* MinidumpThreadWriter::MinidumpThread() const { + DCHECK_EQ(state(), kStateWritable); + + return &thread_; +} + +void MinidumpThreadWriter::SetStack( + std::unique_ptr stack) { + DCHECK_EQ(state(), kStateMutable); + + stack_ = std::move(stack); +} + +void MinidumpThreadWriter::SetContext( + std::unique_ptr context) { + DCHECK_EQ(state(), kStateMutable); + + context_ = std::move(context); +} + +bool MinidumpThreadWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + CHECK(context_); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + if (stack_) { + stack_->RegisterMemoryDescriptor(&thread_.Stack); + } + + context_->RegisterLocationDescriptor(&thread_.ThreadContext); + + return true; +} + +size_t MinidumpThreadWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + // This object doesn’t directly write anything itself. Its MINIDUMP_THREAD is + // written by its parent as part of a MINIDUMP_THREAD_LIST, and its children + // are responsible for writing themselves. + return 0; +} + +std::vector MinidumpThreadWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + DCHECK(context_); + + std::vector children; + if (stack_) { + children.push_back(stack_.get()); + } + children.push_back(context_.get()); + + return children; +} + +bool MinidumpThreadWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + // This object doesn’t directly write anything itself. Its MINIDUMP_THREAD is + // written by its parent as part of a MINIDUMP_THREAD_LIST, and its children + // are responsible for writing themselves. + return true; +} + +MinidumpThreadListWriter::MinidumpThreadListWriter() + : MinidumpStreamWriter(), + threads_(), + memory_list_writer_(nullptr), + thread_list_base_() { +} + +MinidumpThreadListWriter::~MinidumpThreadListWriter() { +} + +void MinidumpThreadListWriter::InitializeFromSnapshot( + const std::vector& thread_snapshots, + MinidumpThreadIDMap* thread_id_map) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(threads_.empty()); + + BuildMinidumpThreadIDMap(thread_snapshots, thread_id_map); + + for (const ThreadSnapshot* thread_snapshot : thread_snapshots) { + auto thread = std::make_unique(); + thread->InitializeFromSnapshot(thread_snapshot, thread_id_map); + AddThread(std::move(thread)); + } + + // Do this in a separate loop to keep the thread stacks earlier in the dump, + // and together. + for (const ThreadSnapshot* thread_snapshot : thread_snapshots) + memory_list_writer_->AddFromSnapshot(thread_snapshot->ExtraMemory()); +} + +void MinidumpThreadListWriter::SetMemoryListWriter( + MinidumpMemoryListWriter* memory_list_writer) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(threads_.empty()); + + memory_list_writer_ = memory_list_writer; +} + +void MinidumpThreadListWriter::AddThread( + std::unique_ptr thread) { + DCHECK_EQ(state(), kStateMutable); + + if (memory_list_writer_) { + SnapshotMinidumpMemoryWriter* stack = thread->Stack(); + if (stack) { + memory_list_writer_->AddNonOwnedMemory(stack); + } + } + + threads_.push_back(std::move(thread)); +} + +bool MinidumpThreadListWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpStreamWriter::Freeze()) { + return false; + } + + size_t thread_count = threads_.size(); + if (!AssignIfInRange(&thread_list_base_.NumberOfThreads, thread_count)) { + LOG(ERROR) << "thread_count " << thread_count << " out of range"; + return false; + } + + return true; +} + +size_t MinidumpThreadListWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return sizeof(thread_list_base_) + threads_.size() * sizeof(MINIDUMP_THREAD); +} + +std::vector MinidumpThreadListWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + + std::vector children; + for (const auto& thread : threads_) { + children.push_back(thread.get()); + } + + return children; +} + +bool MinidumpThreadListWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + WritableIoVec iov; + iov.iov_base = &thread_list_base_; + iov.iov_len = sizeof(thread_list_base_); + std::vector iovecs(1, iov); + + for (const auto& thread : threads_) { + iov.iov_base = thread->MinidumpThread(); + iov.iov_len = sizeof(MINIDUMP_THREAD); + iovecs.push_back(iov); + } + + return file_writer->WriteIoVec(&iovecs); +} + +MinidumpStreamType MinidumpThreadListWriter::StreamType() const { + return kMinidumpStreamTypeThreadList; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_thread_writer.h b/shared/sentry/external/crashpad/minidump/minidump_thread_writer.h new file mode 100644 index 000000000..9ea428bfc --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_thread_writer.h @@ -0,0 +1,217 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_THREAD_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_THREAD_WRITER_H_ + +#include +#include +#include +#include + +#include +#include + +#include "minidump/minidump_stream_writer.h" +#include "minidump/minidump_thread_id_map.h" +#include "minidump/minidump_writable.h" + +namespace crashpad { + +class MinidumpContextWriter; +class MinidumpMemoryListWriter; +class SnapshotMinidumpMemoryWriter; +class ThreadSnapshot; + +//! \brief The writer for a MINIDUMP_THREAD object in a minidump file. +//! +//! Because MINIDUMP_THREAD objects only appear as elements of +//! MINIDUMP_THREAD_LIST objects, this class does not write any data on its own. +//! It makes its MINIDUMP_THREAD data available to its MinidumpThreadListWriter +//! parent, which writes it as part of a MINIDUMP_THREAD_LIST. +class MinidumpThreadWriter final : public internal::MinidumpWritable { + public: + MinidumpThreadWriter(); + + MinidumpThreadWriter(const MinidumpThreadWriter&) = delete; + MinidumpThreadWriter& operator=(const MinidumpThreadWriter&) = delete; + + ~MinidumpThreadWriter() override; + + //! \brief Initializes the MINIDUMP_THREAD based on \a thread_snapshot. + //! + //! \param[in] thread_snapshot The thread snapshot to use as source data. + //! \param[in] thread_id_map A MinidumpThreadIDMap to be consulted to + //! determine the 32-bit minidump thread ID to use for \a thread_snapshot. + //! + //! \note Valid in #kStateMutable. No mutator methods may be called before + //! this method, and it is not normally necessary to call any mutator + //! methods after this method. + void InitializeFromSnapshot(const ThreadSnapshot* thread_snapshot, + const MinidumpThreadIDMap* thread_id_map); + + //! \brief Returns a MINIDUMP_THREAD referencing this object’s data. + //! + //! This method is expected to be called by a MinidumpThreadListWriter in + //! order to obtain a MINIDUMP_THREAD to include in its list. + //! + //! \note Valid in #kStateWritable. + const MINIDUMP_THREAD* MinidumpThread() const; + + //! \brief Returns a SnapshotMinidumpMemoryWriter that will write the memory + //! region corresponding to this object’s stack. + //! + //! If the thread does not have a stack, or its stack could not be determined, + //! this will return `nullptr`. + //! + //! This method is provided so that MinidumpThreadListWriter can obtain thread + //! stack memory regions for the purposes of adding them to a + //! MinidumpMemoryListWriter (configured by calling + //! MinidumpThreadListWriter::SetMemoryListWriter()) by calling + //! MinidumpMemoryListWriter::AddExtraMemory(). + //! + //! \note Valid in any state. + SnapshotMinidumpMemoryWriter* Stack() const { return stack_.get(); } + + //! \brief Arranges for MINIDUMP_THREAD::Stack to point to the MINIDUMP_MEMORY + //! object to be written by \a stack. + //! + //! This object takes ownership of \a stack and becomes its parent in the + //! overall tree of internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void SetStack(std::unique_ptr stack); + + //! \brief Arranges for MINIDUMP_THREAD::ThreadContext to point to the CPU + //! context to be written by \a context. + //! + //! A context is required in all MINIDUMP_THREAD objects. + //! + //! This object takes ownership of \a context and becomes its parent in the + //! overall tree of internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void SetContext(std::unique_ptr context); + + //! \brief Sets MINIDUMP_THREAD::ThreadId. + void SetThreadID(uint32_t thread_id) { thread_.ThreadId = thread_id; } + + //! \brief Sets MINIDUMP_THREAD::SuspendCount. + void SetSuspendCount(uint32_t suspend_count) { + thread_.SuspendCount = suspend_count; + } + + //! \brief Sets MINIDUMP_THREAD::PriorityClass. + void SetPriorityClass(uint32_t priority_class) { + thread_.PriorityClass = priority_class; + } + + //! \brief Sets MINIDUMP_THREAD::Priority. + void SetPriority(uint32_t priority) { thread_.Priority = priority; } + + //! \brief Sets MINIDUMP_THREAD::Teb. + void SetTEB(uint64_t teb) { thread_.Teb = teb; } + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + MINIDUMP_THREAD thread_; + std::unique_ptr stack_; + std::unique_ptr context_; +}; + +//! \brief The writer for a MINIDUMP_THREAD_LIST stream in a minidump file, +//! containing a list of MINIDUMP_THREAD objects. +class MinidumpThreadListWriter final : public internal::MinidumpStreamWriter { + public: + MinidumpThreadListWriter(); + + MinidumpThreadListWriter(const MinidumpThreadListWriter&) = delete; + MinidumpThreadListWriter& operator=(const MinidumpThreadListWriter&) = delete; + + ~MinidumpThreadListWriter() override; + + //! \brief Adds an initialized MINIDUMP_THREAD for each thread in \a + //! thread_snapshots to the MINIDUMP_THREAD_LIST. + //! + //! \param[in] thread_snapshots The thread snapshots to use as source data. + //! \param[out] thread_id_map A MinidumpThreadIDMap to be built by this + //! method. This map must be empty when this method is called. + //! + //! \note Valid in #kStateMutable. AddThread() may not be called before this + //! method, and it is not normally necessary to call AddThread() after + //! this method. + void InitializeFromSnapshot( + const std::vector& thread_snapshots, + MinidumpThreadIDMap* thread_id_map); + + //! \brief Sets the MinidumpMemoryListWriter that each thread’s stack memory + //! region should be added to as extra memory. + //! + //! Each MINIDUMP_THREAD object can contain a reference to a + //! SnapshotMinidumpMemoryWriter object that contains a snapshot of its stac + //! memory. In the overall tree of internal::MinidumpWritable objects, these + //! SnapshotMinidumpMemoryWriter objects are considered children of their + //! MINIDUMP_THREAD, and are referenced by a MINIDUMP_MEMORY_DESCRIPTOR + //! contained in the MINIDUMP_THREAD. It is also possible for the same memory + //! regions to have MINIDUMP_MEMORY_DESCRIPTOR objects present in a + //! MINIDUMP_MEMORY_LIST stream. This is accomplished by calling this method, + //! which informs a MinidumpThreadListWriter that it should call + //! MinidumpMemoryListWriter::AddExtraMemory() for each extant thread stack + //! while the thread is being added in AddThread(). When this is done, the + //! MinidumpMemoryListWriter will contain a MINIDUMP_MEMORY_DESCRIPTOR + //! pointing to the thread’s stack memory in its MINIDUMP_MEMORY_LIST. Note + //! that the actual contents of the memory is only written once, as a child of + //! the MinidumpThreadWriter. The MINIDUMP_MEMORY_DESCRIPTOR objects in both + //! the MINIDUMP_THREAD and MINIDUMP_MEMORY_LIST will point to the same copy + //! of the memory’s contents. + //! + //! \note This method must be called before AddThread() is called. Threads + //! added by AddThread() prior to this method being called will not have + //! their stacks added to \a memory_list_writer as extra memory. + //! \note Valid in #kStateMutable. + void SetMemoryListWriter(MinidumpMemoryListWriter* memory_list_writer); + + //! \brief Adds a MinidumpThreadWriter to the MINIDUMP_THREAD_LIST. + //! + //! This object takes ownership of \a thread and becomes its parent in the + //! overall tree of internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void AddThread(std::unique_ptr thread); + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpStreamWriter: + MinidumpStreamType StreamType() const override; + + private: + std::vector> threads_; + MinidumpMemoryListWriter* memory_list_writer_; // weak + MINIDUMP_THREAD_LIST thread_list_base_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_THREAD_WRITER_H_ diff --git a/shared/sentry/external/crashpad/minidump/minidump_thread_writer_test.cc b/shared/sentry/external/crashpad/minidump/minidump_thread_writer_test.cc new file mode 100644 index 000000000..9b0e064ca --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_thread_writer_test.cc @@ -0,0 +1,724 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_thread_writer.h" + +#include +#include +#include + +#include "base/compiler_specific.h" +#include "base/format_macros.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" +#include "minidump/minidump_context_writer.h" +#include "minidump/minidump_file_writer.h" +#include "minidump/minidump_memory_writer.h" +#include "minidump/test/minidump_context_test_util.h" +#include "minidump/test/minidump_file_writer_test_util.h" +#include "minidump/test/minidump_memory_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "snapshot/test/test_cpu_context.h" +#include "snapshot/test/test_memory_snapshot.h" +#include "snapshot/test/test_thread_snapshot.h" +#include "test/gtest_death.h" +#include "util/file/string_file.h" + +namespace crashpad { +namespace test { +namespace { + +// This returns the MINIDUMP_THREAD_LIST stream in |thread_list|. If +// |memory_list| is not nullptr, a MINIDUMP_MEMORY_LIST stream is also expected +// in |file_contents|, and that stream will be returned in |memory_list|. +void GetThreadListStream(const std::string& file_contents, + const MINIDUMP_THREAD_LIST** thread_list, + const MINIDUMP_MEMORY_LIST** memory_list) { + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + const uint32_t kExpectedStreams = memory_list ? 2 : 1; + const size_t kThreadListStreamOffset = + kDirectoryOffset + kExpectedStreams * sizeof(MINIDUMP_DIRECTORY); + const size_t kThreadsOffset = + kThreadListStreamOffset + sizeof(MINIDUMP_THREAD_LIST); + + ASSERT_GE(file_contents.size(), kThreadsOffset); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(file_contents, &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, kExpectedStreams, 0)); + ASSERT_TRUE(directory); + + ASSERT_EQ(directory[0].StreamType, kMinidumpStreamTypeThreadList); + EXPECT_EQ(directory[0].Location.Rva, kThreadListStreamOffset); + + *thread_list = MinidumpWritableAtLocationDescriptor( + file_contents, directory[0].Location); + ASSERT_TRUE(thread_list); + + if (memory_list) { + ASSERT_EQ(directory[1].StreamType, kMinidumpStreamTypeMemoryList); + + *memory_list = MinidumpWritableAtLocationDescriptor( + file_contents, directory[1].Location); + ASSERT_TRUE(*memory_list); + } +} + +TEST(MinidumpThreadWriter, EmptyThreadList) { + MinidumpFileWriter minidump_file_writer; + auto thread_list_writer = std::make_unique(); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(thread_list_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_THREAD_LIST)); + + const MINIDUMP_THREAD_LIST* thread_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetThreadListStream(string_file.string(), &thread_list, nullptr)); + + EXPECT_EQ(thread_list->NumberOfThreads, 0u); +} + +// The MINIDUMP_THREADs |expected| and |observed| are compared against each +// other using Google Test assertions. If |stack| is not nullptr, |observed| is +// expected to contain a populated MINIDUMP_MEMORY_DESCRIPTOR in its Stack +// field, otherwise, its Stack field is expected to be zeroed out. The memory +// descriptor will be placed in |stack|. |observed| must contain a populated +// ThreadContext field. The context will be recovered from |file_contents| and +// stored in |context_base|. +void ExpectThread(const MINIDUMP_THREAD* expected, + const MINIDUMP_THREAD* observed, + const std::string& file_contents, + const MINIDUMP_MEMORY_DESCRIPTOR** stack, + const void** context_base) { + MINIDUMP_THREAD expected_thread, observed_thread; + memcpy(&expected_thread, expected, sizeof(expected_thread)); + memcpy(&observed_thread, observed, sizeof(observed_thread)); + + EXPECT_EQ(observed_thread.ThreadId, expected_thread.ThreadId); + EXPECT_EQ(observed_thread.SuspendCount, expected_thread.SuspendCount); + EXPECT_EQ(observed_thread.PriorityClass, expected_thread.PriorityClass); + EXPECT_EQ(observed_thread.Priority, expected_thread.Priority); + EXPECT_EQ(observed_thread.Teb, expected_thread.Teb); + + EXPECT_EQ(observed_thread.Stack.StartOfMemoryRange, + expected_thread.Stack.StartOfMemoryRange); + EXPECT_EQ(observed_thread.Stack.Memory.DataSize, + expected_thread.Stack.Memory.DataSize); + if (stack) { + ASSERT_NE(observed_thread.Stack.Memory.DataSize, 0u); + ASSERT_NE(observed_thread.Stack.Memory.Rva, 0u); + ASSERT_GE(file_contents.size(), + observed_thread.Stack.Memory.Rva + + observed_thread.Stack.Memory.DataSize); + *stack = &observed->Stack; + } else { + EXPECT_EQ(observed_thread.Stack.StartOfMemoryRange, 0u); + EXPECT_EQ(observed_thread.Stack.Memory.DataSize, 0u); + EXPECT_EQ(observed_thread.Stack.Memory.Rva, 0u); + } + + EXPECT_EQ(observed_thread.ThreadContext.DataSize, + expected_thread.ThreadContext.DataSize); + ASSERT_NE(observed_thread.ThreadContext.DataSize, 0u); + ASSERT_NE(observed_thread.ThreadContext.Rva, 0u); + ASSERT_GE(file_contents.size(), + observed_thread.ThreadContext.Rva + + expected_thread.ThreadContext.DataSize); + *context_base = &file_contents[observed_thread.ThreadContext.Rva]; +} + +TEST(MinidumpThreadWriter, OneThread_x86_NoStack) { + MinidumpFileWriter minidump_file_writer; + auto thread_list_writer = std::make_unique(); + + constexpr uint32_t kThreadID = 0x11111111; + constexpr uint32_t kSuspendCount = 1; + constexpr uint32_t kPriorityClass = 0x20; + constexpr uint32_t kPriority = 10; + constexpr uint64_t kTEB = 0x55555555; + constexpr uint32_t kSeed = 123; + + auto thread_writer = std::make_unique(); + thread_writer->SetThreadID(kThreadID); + thread_writer->SetSuspendCount(kSuspendCount); + thread_writer->SetPriorityClass(kPriorityClass); + thread_writer->SetPriority(kPriority); + thread_writer->SetTEB(kTEB); + + auto context_x86_writer = std::make_unique(); + InitializeMinidumpContextX86(context_x86_writer->context(), kSeed); + thread_writer->SetContext(std::move(context_x86_writer)); + + thread_list_writer->AddThread(std::move(thread_writer)); + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(thread_list_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_THREAD_LIST) + 1 * sizeof(MINIDUMP_THREAD) + + 1 * sizeof(MinidumpContextX86)); + + const MINIDUMP_THREAD_LIST* thread_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetThreadListStream(string_file.string(), &thread_list, nullptr)); + + EXPECT_EQ(thread_list->NumberOfThreads, 1u); + + MINIDUMP_THREAD expected = {}; + expected.ThreadId = kThreadID; + expected.SuspendCount = kSuspendCount; + expected.PriorityClass = kPriorityClass; + expected.Priority = kPriority; + expected.Teb = kTEB; + expected.ThreadContext.DataSize = sizeof(MinidumpContextX86); + + const MinidumpContextX86* observed_context = nullptr; + ASSERT_NO_FATAL_FAILURE( + ExpectThread(&expected, + &thread_list->Threads[0], + string_file.string(), + nullptr, + reinterpret_cast(&observed_context))); + + ASSERT_NO_FATAL_FAILURE( + ExpectMinidumpContextX86(kSeed, observed_context, false)); +} + +TEST(MinidumpThreadWriter, OneThread_AMD64_Stack) { + MinidumpFileWriter minidump_file_writer; + auto thread_list_writer = std::make_unique(); + + constexpr uint32_t kThreadID = 0x22222222; + constexpr uint32_t kSuspendCount = 2; + constexpr uint32_t kPriorityClass = 0x30; + constexpr uint32_t kPriority = 20; + constexpr uint64_t kTEB = 0x5555555555555555; + constexpr uint64_t kMemoryBase = 0x765432100000; + constexpr size_t kMemorySize = 32; + constexpr uint8_t kMemoryValue = 99; + constexpr uint32_t kSeed = 456; + + auto thread_writer = std::make_unique(); + thread_writer->SetThreadID(kThreadID); + thread_writer->SetSuspendCount(kSuspendCount); + thread_writer->SetPriorityClass(kPriorityClass); + thread_writer->SetPriority(kPriority); + thread_writer->SetTEB(kTEB); + + auto memory_writer = std::make_unique( + kMemoryBase, kMemorySize, kMemoryValue); + thread_writer->SetStack(std::move(memory_writer)); + + auto context_amd64_writer = std::make_unique(); + InitializeMinidumpContextAMD64(context_amd64_writer->context(), kSeed); + thread_writer->SetContext(std::move(context_amd64_writer)); + + thread_list_writer->AddThread(std::move(thread_writer)); + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(thread_list_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_THREAD_LIST) + 1 * sizeof(MINIDUMP_THREAD) + + 1 * sizeof(MinidumpContextAMD64) + kMemorySize); + + const MINIDUMP_THREAD_LIST* thread_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetThreadListStream(string_file.string(), &thread_list, nullptr)); + + EXPECT_EQ(thread_list->NumberOfThreads, 1u); + + MINIDUMP_THREAD expected = {}; + expected.ThreadId = kThreadID; + expected.SuspendCount = kSuspendCount; + expected.PriorityClass = kPriorityClass; + expected.Priority = kPriority; + expected.Teb = kTEB; + expected.Stack.StartOfMemoryRange = kMemoryBase; + expected.Stack.Memory.DataSize = kMemorySize; + expected.ThreadContext.DataSize = sizeof(MinidumpContextAMD64); + + const MINIDUMP_MEMORY_DESCRIPTOR* observed_stack = nullptr; + const MinidumpContextAMD64* observed_context = nullptr; + ASSERT_NO_FATAL_FAILURE( + ExpectThread(&expected, + &thread_list->Threads[0], + string_file.string(), + &observed_stack, + reinterpret_cast(&observed_context))); + + ASSERT_NO_FATAL_FAILURE( + ExpectMinidumpMemoryDescriptorAndContents(&expected.Stack, + observed_stack, + string_file.string(), + kMemoryValue, + true)); + ASSERT_NO_FATAL_FAILURE( + ExpectMinidumpContextAMD64(kSeed, observed_context, false)); +} + +TEST(MinidumpThreadWriter, ThreeThreads_x86_MemoryList) { + MinidumpFileWriter minidump_file_writer; + auto thread_list_writer = std::make_unique(); + auto memory_list_writer = std::make_unique(); + thread_list_writer->SetMemoryListWriter(memory_list_writer.get()); + + constexpr uint32_t kThreadID0 = 1111111; + constexpr uint32_t kSuspendCount0 = 111111; + constexpr uint32_t kPriorityClass0 = 11111; + constexpr uint32_t kPriority0 = 1111; + constexpr uint64_t kTEB0 = 111; + constexpr uint64_t kMemoryBase0 = 0x1110; + constexpr size_t kMemorySize0 = 16; + constexpr uint8_t kMemoryValue0 = 11; + constexpr uint32_t kSeed0 = 1; + + auto thread_writer_0 = std::make_unique(); + thread_writer_0->SetThreadID(kThreadID0); + thread_writer_0->SetSuspendCount(kSuspendCount0); + thread_writer_0->SetPriorityClass(kPriorityClass0); + thread_writer_0->SetPriority(kPriority0); + thread_writer_0->SetTEB(kTEB0); + + auto memory_writer_0 = std::make_unique( + kMemoryBase0, kMemorySize0, kMemoryValue0); + thread_writer_0->SetStack(std::move(memory_writer_0)); + + auto context_x86_writer_0 = std::make_unique(); + InitializeMinidumpContextX86(context_x86_writer_0->context(), kSeed0); + thread_writer_0->SetContext(std::move(context_x86_writer_0)); + + thread_list_writer->AddThread(std::move(thread_writer_0)); + + constexpr uint32_t kThreadID1 = 2222222; + constexpr uint32_t kSuspendCount1 = 222222; + constexpr uint32_t kPriorityClass1 = 22222; + constexpr uint32_t kPriority1 = 2222; + constexpr uint64_t kTEB1 = 222; + constexpr uint64_t kMemoryBase1 = 0x2220; + constexpr size_t kMemorySize1 = 32; + constexpr uint8_t kMemoryValue1 = 22; + constexpr uint32_t kSeed1 = 2; + + auto thread_writer_1 = std::make_unique(); + thread_writer_1->SetThreadID(kThreadID1); + thread_writer_1->SetSuspendCount(kSuspendCount1); + thread_writer_1->SetPriorityClass(kPriorityClass1); + thread_writer_1->SetPriority(kPriority1); + thread_writer_1->SetTEB(kTEB1); + + auto memory_writer_1 = std::make_unique( + kMemoryBase1, kMemorySize1, kMemoryValue1); + thread_writer_1->SetStack(std::move(memory_writer_1)); + + auto context_x86_writer_1 = std::make_unique(); + InitializeMinidumpContextX86(context_x86_writer_1->context(), kSeed1); + thread_writer_1->SetContext(std::move(context_x86_writer_1)); + + thread_list_writer->AddThread(std::move(thread_writer_1)); + + constexpr uint32_t kThreadID2 = 3333333; + constexpr uint32_t kSuspendCount2 = 333333; + constexpr uint32_t kPriorityClass2 = 33333; + constexpr uint32_t kPriority2 = 3333; + constexpr uint64_t kTEB2 = 333; + constexpr uint64_t kMemoryBase2 = 0x3330; + constexpr size_t kMemorySize2 = 48; + constexpr uint8_t kMemoryValue2 = 33; + constexpr uint32_t kSeed2 = 3; + + auto thread_writer_2 = std::make_unique(); + thread_writer_2->SetThreadID(kThreadID2); + thread_writer_2->SetSuspendCount(kSuspendCount2); + thread_writer_2->SetPriorityClass(kPriorityClass2); + thread_writer_2->SetPriority(kPriority2); + thread_writer_2->SetTEB(kTEB2); + + auto memory_writer_2 = std::make_unique( + kMemoryBase2, kMemorySize2, kMemoryValue2); + thread_writer_2->SetStack(std::move(memory_writer_2)); + + auto context_x86_writer_2 = std::make_unique(); + InitializeMinidumpContextX86(context_x86_writer_2->context(), kSeed2); + thread_writer_2->SetContext(std::move(context_x86_writer_2)); + + thread_list_writer->AddThread(std::move(thread_writer_2)); + + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(thread_list_writer))); + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(memory_list_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_EQ( + string_file.string().size(), + sizeof(MINIDUMP_HEADER) + 2 * sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_THREAD_LIST) + 3 * sizeof(MINIDUMP_THREAD) + + sizeof(MINIDUMP_MEMORY_LIST) + + 3 * sizeof(MINIDUMP_MEMORY_DESCRIPTOR) + + 3 * sizeof(MinidumpContextX86) + kMemorySize0 + kMemorySize1 + + kMemorySize2 + 12); // 12 for alignment + + const MINIDUMP_THREAD_LIST* thread_list = nullptr; + const MINIDUMP_MEMORY_LIST* memory_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetThreadListStream(string_file.string(), &thread_list, &memory_list)); + + EXPECT_EQ(thread_list->NumberOfThreads, 3u); + EXPECT_EQ(memory_list->NumberOfMemoryRanges, 3u); + + { + SCOPED_TRACE("thread 0"); + + MINIDUMP_THREAD expected = {}; + expected.ThreadId = kThreadID0; + expected.SuspendCount = kSuspendCount0; + expected.PriorityClass = kPriorityClass0; + expected.Priority = kPriority0; + expected.Teb = kTEB0; + expected.Stack.StartOfMemoryRange = kMemoryBase0; + expected.Stack.Memory.DataSize = kMemorySize0; + expected.ThreadContext.DataSize = sizeof(MinidumpContextX86); + + const MINIDUMP_MEMORY_DESCRIPTOR* observed_stack = nullptr; + const MinidumpContextX86* observed_context = nullptr; + ASSERT_NO_FATAL_FAILURE( + ExpectThread(&expected, + &thread_list->Threads[0], + string_file.string(), + &observed_stack, + reinterpret_cast(&observed_context))); + + ASSERT_NO_FATAL_FAILURE( + ExpectMinidumpMemoryDescriptorAndContents(&expected.Stack, + observed_stack, + string_file.string(), + kMemoryValue0, + false)); + ASSERT_NO_FATAL_FAILURE( + ExpectMinidumpContextX86(kSeed0, observed_context, false)); + ASSERT_NO_FATAL_FAILURE(ExpectMinidumpMemoryDescriptor( + observed_stack, &memory_list->MemoryRanges[0])); + } + + { + SCOPED_TRACE("thread 1"); + + MINIDUMP_THREAD expected = {}; + expected.ThreadId = kThreadID1; + expected.SuspendCount = kSuspendCount1; + expected.PriorityClass = kPriorityClass1; + expected.Priority = kPriority1; + expected.Teb = kTEB1; + expected.Stack.StartOfMemoryRange = kMemoryBase1; + expected.Stack.Memory.DataSize = kMemorySize1; + expected.ThreadContext.DataSize = sizeof(MinidumpContextX86); + + const MINIDUMP_MEMORY_DESCRIPTOR* observed_stack = nullptr; + const MinidumpContextX86* observed_context = nullptr; + ASSERT_NO_FATAL_FAILURE( + ExpectThread(&expected, + &thread_list->Threads[1], + string_file.string(), + &observed_stack, + reinterpret_cast(&observed_context))); + + ASSERT_NO_FATAL_FAILURE( + ExpectMinidumpMemoryDescriptorAndContents(&expected.Stack, + observed_stack, + string_file.string(), + kMemoryValue1, + false)); + ASSERT_NO_FATAL_FAILURE( + ExpectMinidumpContextX86(kSeed1, observed_context, false)); + ASSERT_NO_FATAL_FAILURE(ExpectMinidumpMemoryDescriptor( + observed_stack, &memory_list->MemoryRanges[1])); + } + + { + SCOPED_TRACE("thread 2"); + + MINIDUMP_THREAD expected = {}; + expected.ThreadId = kThreadID2; + expected.SuspendCount = kSuspendCount2; + expected.PriorityClass = kPriorityClass2; + expected.Priority = kPriority2; + expected.Teb = kTEB2; + expected.Stack.StartOfMemoryRange = kMemoryBase2; + expected.Stack.Memory.DataSize = kMemorySize2; + expected.ThreadContext.DataSize = sizeof(MinidumpContextX86); + + const MINIDUMP_MEMORY_DESCRIPTOR* observed_stack = nullptr; + const MinidumpContextX86* observed_context = nullptr; + ASSERT_NO_FATAL_FAILURE( + ExpectThread(&expected, + &thread_list->Threads[2], + string_file.string(), + &observed_stack, + reinterpret_cast(&observed_context))); + + ASSERT_NO_FATAL_FAILURE( + ExpectMinidumpMemoryDescriptorAndContents(&expected.Stack, + observed_stack, + string_file.string(), + kMemoryValue2, + true)); + ASSERT_NO_FATAL_FAILURE( + ExpectMinidumpContextX86(kSeed2, observed_context, false)); + ASSERT_NO_FATAL_FAILURE(ExpectMinidumpMemoryDescriptor( + observed_stack, &memory_list->MemoryRanges[2])); + } +} + +struct InitializeFromSnapshotX86Traits { + using MinidumpContextType = MinidumpContextX86; + static void InitializeCPUContext(CPUContext* context, uint32_t seed) { + return InitializeCPUContextX86(context, seed); + } + static void ExpectMinidumpContext( + uint32_t expect_seed, const MinidumpContextX86* observed, bool snapshot) { + return ExpectMinidumpContextX86(expect_seed, observed, snapshot); + } +}; + +struct InitializeFromSnapshotAMD64Traits { + using MinidumpContextType = MinidumpContextAMD64; + static void InitializeCPUContext(CPUContext* context, uint32_t seed) { + return InitializeCPUContextX86_64(context, seed); + } + static void ExpectMinidumpContext(uint32_t expect_seed, + const MinidumpContextAMD64* observed, + bool snapshot) { + return ExpectMinidumpContextAMD64(expect_seed, observed, snapshot); + } +}; + +struct InitializeFromSnapshotNoContextTraits { + using MinidumpContextType = MinidumpContextX86; + static void InitializeCPUContext(CPUContext* context, uint32_t seed) { + context->architecture = kCPUArchitectureUnknown; + } + static void ExpectMinidumpContext(uint32_t expect_seed, + const MinidumpContextX86* observed, + bool snapshot) { + FAIL(); + } +}; + +template +void RunInitializeFromSnapshotTest(bool thread_id_collision) { + using MinidumpContextType = typename Traits::MinidumpContextType; + MINIDUMP_THREAD expect_threads[3] = {}; + uint64_t thread_ids[std::size(expect_threads)] = {}; + uint8_t memory_values[std::size(expect_threads)] = {}; + uint32_t context_seeds[std::size(expect_threads)] = {}; + MINIDUMP_MEMORY_DESCRIPTOR tebs[std::size(expect_threads)] = {}; + + constexpr size_t kTebSize = 1024; + + expect_threads[0].ThreadId = 1; + expect_threads[0].SuspendCount = 2; + expect_threads[0].Priority = 3; + expect_threads[0].Teb = 0x0123456789abcdef; + expect_threads[0].Stack.StartOfMemoryRange = 0x1000; + expect_threads[0].Stack.Memory.DataSize = 0x100; + expect_threads[0].ThreadContext.DataSize = sizeof(MinidumpContextType); + memory_values[0] = 'A'; + context_seeds[0] = 0x80000000; + tebs[0].StartOfMemoryRange = expect_threads[0].Teb; + tebs[0].Memory.DataSize = kTebSize; + + // The thread at index 1 has no stack. + expect_threads[1].ThreadId = 11; + expect_threads[1].SuspendCount = 12; + expect_threads[1].Priority = 13; + expect_threads[1].Teb = 0x1111111111111111; + expect_threads[1].ThreadContext.DataSize = sizeof(MinidumpContextType); + context_seeds[1] = 0x40000001; + tebs[1].StartOfMemoryRange = expect_threads[1].Teb; + tebs[1].Memory.DataSize = kTebSize; + + expect_threads[2].ThreadId = 21; + expect_threads[2].SuspendCount = 22; + expect_threads[2].Priority = 23; + expect_threads[2].Teb = 0xfedcba9876543210; + expect_threads[2].Stack.StartOfMemoryRange = 0x3000; + expect_threads[2].Stack.Memory.DataSize = 0x300; + expect_threads[2].ThreadContext.DataSize = sizeof(MinidumpContextType); + memory_values[2] = 'd'; + context_seeds[2] = 0x20000002; + tebs[2].StartOfMemoryRange = expect_threads[2].Teb; + tebs[2].Memory.DataSize = kTebSize; + + if (thread_id_collision) { + thread_ids[0] = 0x0123456700000001; + thread_ids[1] = 0x89abcdef00000001; + thread_ids[2] = 4; + expect_threads[0].ThreadId = 0; + expect_threads[1].ThreadId = 1; + expect_threads[2].ThreadId = 2; + } else { + thread_ids[0] = 1; + thread_ids[1] = 11; + thread_ids[2] = 22; + expect_threads[0].ThreadId = static_cast(thread_ids[0]); + expect_threads[1].ThreadId = static_cast(thread_ids[1]); + expect_threads[2].ThreadId = static_cast(thread_ids[2]); + } + + std::vector> thread_snapshots_owner; + std::vector thread_snapshots; + for (size_t index = 0; index < std::size(expect_threads); ++index) { + thread_snapshots_owner.push_back(std::make_unique()); + TestThreadSnapshot* thread_snapshot = thread_snapshots_owner.back().get(); + + thread_snapshot->SetThreadID(thread_ids[index]); + thread_snapshot->SetSuspendCount(expect_threads[index].SuspendCount); + thread_snapshot->SetPriority(expect_threads[index].Priority); + thread_snapshot->SetThreadSpecificDataAddress(expect_threads[index].Teb); + + if (expect_threads[index].Stack.Memory.DataSize) { + auto memory_snapshot = std::make_unique(); + memory_snapshot->SetAddress( + expect_threads[index].Stack.StartOfMemoryRange); + memory_snapshot->SetSize(expect_threads[index].Stack.Memory.DataSize); + memory_snapshot->SetValue(memory_values[index]); + thread_snapshot->SetStack(std::move(memory_snapshot)); + } + + Traits::InitializeCPUContext(thread_snapshot->MutableContext(), + context_seeds[index]); + + auto teb_snapshot = std::make_unique(); + teb_snapshot->SetAddress(expect_threads[index].Teb); + teb_snapshot->SetSize(kTebSize); + teb_snapshot->SetValue(static_cast('t' + index)); + thread_snapshot->AddExtraMemory(std::move(teb_snapshot)); + + thread_snapshots.push_back(thread_snapshot); + } + + auto thread_list_writer = std::make_unique(); + auto memory_list_writer = std::make_unique(); + thread_list_writer->SetMemoryListWriter(memory_list_writer.get()); + MinidumpThreadIDMap thread_id_map; + thread_list_writer->InitializeFromSnapshot(thread_snapshots, &thread_id_map); + + MinidumpFileWriter minidump_file_writer; + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(thread_list_writer))); + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(memory_list_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + const MINIDUMP_THREAD_LIST* thread_list = nullptr; + const MINIDUMP_MEMORY_LIST* memory_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetThreadListStream(string_file.string(), &thread_list, &memory_list)); + + ASSERT_EQ(thread_list->NumberOfThreads, 3u); + ASSERT_EQ(memory_list->NumberOfMemoryRanges, 5u); + + size_t memory_index = 0; + for (size_t index = 0; index < thread_list->NumberOfThreads; ++index) { + SCOPED_TRACE(base::StringPrintf("index %" PRIuS, index)); + + const MINIDUMP_MEMORY_DESCRIPTOR* observed_stack = nullptr; + const MINIDUMP_MEMORY_DESCRIPTOR** observed_stack_p = + expect_threads[index].Stack.Memory.DataSize ? &observed_stack : nullptr; + const MinidumpContextType* observed_context = nullptr; + ASSERT_NO_FATAL_FAILURE( + ExpectThread(&expect_threads[index], + &thread_list->Threads[index], + string_file.string(), + observed_stack_p, + reinterpret_cast(&observed_context))); + + ASSERT_NO_FATAL_FAILURE(Traits::ExpectMinidumpContext( + context_seeds[index], observed_context, true)); + + if (observed_stack_p) { + ASSERT_NO_FATAL_FAILURE(ExpectMinidumpMemoryDescriptorAndContents( + &expect_threads[index].Stack, + observed_stack, + string_file.string(), + memory_values[index], + false)); + + ASSERT_NO_FATAL_FAILURE(ExpectMinidumpMemoryDescriptor( + observed_stack, &memory_list->MemoryRanges[memory_index])); + + ++memory_index; + } + } + + for (size_t index = 0; index < thread_list->NumberOfThreads; ++index) { + const MINIDUMP_MEMORY_DESCRIPTOR* memory = + &memory_list->MemoryRanges[memory_index]; + ASSERT_NO_FATAL_FAILURE( + ExpectMinidumpMemoryDescriptor(&tebs[index], memory)); + std::string expected_data(kTebSize, static_cast('t' + index)); + std::string observed_data(&string_file.string()[memory->Memory.Rva], + memory->Memory.DataSize); + EXPECT_EQ(observed_data, expected_data); + ++memory_index; + } +} + +TEST(MinidumpThreadWriter, InitializeFromSnapshot_x86) { + RunInitializeFromSnapshotTest(false); +} + +TEST(MinidumpThreadWriter, InitializeFromSnapshot_AMD64) { + RunInitializeFromSnapshotTest(false); +} + +TEST(MinidumpThreadWriter, InitializeFromSnapshot_ThreadIDCollision) { + RunInitializeFromSnapshotTest(true); +} + +TEST(MinidumpThreadWriterDeathTest, NoContext) { + MinidumpFileWriter minidump_file_writer; + auto thread_list_writer = std::make_unique(); + + auto thread_writer = std::make_unique(); + + thread_list_writer->AddThread(std::move(thread_writer)); + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(thread_list_writer))); + + StringFile string_file; + ASSERT_DEATH_CHECK(minidump_file_writer.WriteEverything(&string_file), + "context_"); +} + +TEST(MinidumpThreadWriterDeathTest, InitializeFromSnapshot_NoContext) { + ASSERT_DEATH_CHECK( + RunInitializeFromSnapshotTest( + false), "context_"); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_unloaded_module_writer.cc b/shared/sentry/external/crashpad/minidump/minidump_unloaded_module_writer.cc new file mode 100644 index 000000000..c8a5f0fd1 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_unloaded_module_writer.cc @@ -0,0 +1,204 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_unloaded_module_writer.h" + +#include +#include + +#include "minidump/minidump_writer_util.h" +#include "util/file/file_writer.h" +#include "util/numeric/in_range_cast.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { + +MinidumpUnloadedModuleWriter::MinidumpUnloadedModuleWriter() + : MinidumpWritable(), unloaded_module_(), name_() {} + +MinidumpUnloadedModuleWriter::~MinidumpUnloadedModuleWriter() { +} + +void MinidumpUnloadedModuleWriter::InitializeFromSnapshot( + const UnloadedModuleSnapshot& unloaded_module_snapshot) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(!name_); + + SetName(unloaded_module_snapshot.Name()); + + SetImageBaseAddress(unloaded_module_snapshot.Address()); + SetImageSize(InRangeCast(unloaded_module_snapshot.Size(), + std::numeric_limits::max())); + SetTimestamp(unloaded_module_snapshot.Timestamp()); + SetChecksum(unloaded_module_snapshot.Checksum()); +} + +const MINIDUMP_UNLOADED_MODULE* +MinidumpUnloadedModuleWriter::MinidumpUnloadedModule() const { + DCHECK_EQ(state(), kStateWritable); + + return &unloaded_module_; +} + +void MinidumpUnloadedModuleWriter::SetName(const std::string& name) { + DCHECK_EQ(state(), kStateMutable); + + if (!name_) { + name_.reset(new internal::MinidumpUTF16StringWriter()); + } + name_->SetUTF8(name); +} + +void MinidumpUnloadedModuleWriter::SetTimestamp(time_t timestamp) { + DCHECK_EQ(state(), kStateMutable); + + internal::MinidumpWriterUtil::AssignTimeT(&unloaded_module_.TimeDateStamp, + timestamp); +} + +bool MinidumpUnloadedModuleWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + CHECK(name_); + + if (!MinidumpWritable::Freeze()) { + return false; + } + + name_->RegisterRVA(&unloaded_module_.ModuleNameRva); + + return true; +} + +size_t MinidumpUnloadedModuleWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + // This object doesn’t directly write anything itself. Its + // MINIDUMP_UNLOADED_MODULE is written by its parent as part of a + // MINIDUMP_UNLOADED_MODULE_LIST, and its children are responsible for writing + // themselves. + return 0; +} + +std::vector +MinidumpUnloadedModuleWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + DCHECK(name_); + + std::vector children(1, name_.get()); + return children; +} + +bool MinidumpUnloadedModuleWriter::WriteObject( + FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + // This object doesn’t directly write anything itself. Its + // MINIDUMP_UNLOADED_MODULE is written by its parent as part of a + // MINIDUMP_UNLOADED_MODULE_LIST, and its children are responsible for writing + // themselves. + return true; +} + +MinidumpUnloadedModuleListWriter::MinidumpUnloadedModuleListWriter() + : MinidumpStreamWriter(), + unloaded_modules_(), + unloaded_module_list_base_() {} + +MinidumpUnloadedModuleListWriter::~MinidumpUnloadedModuleListWriter() { +} + +void MinidumpUnloadedModuleListWriter::InitializeFromSnapshot( + const std::vector& unloaded_module_snapshots) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(unloaded_modules_.empty()); + + for (const UnloadedModuleSnapshot& unloaded_module_snapshot : + unloaded_module_snapshots) { + auto unloaded_module = std::make_unique(); + unloaded_module->InitializeFromSnapshot(unloaded_module_snapshot); + AddUnloadedModule(std::move(unloaded_module)); + } +} + +void MinidumpUnloadedModuleListWriter::AddUnloadedModule( + std::unique_ptr unloaded_module) { + DCHECK_EQ(state(), kStateMutable); + + unloaded_modules_.push_back(std::move(unloaded_module)); +} + +bool MinidumpUnloadedModuleListWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + + if (!MinidumpStreamWriter::Freeze()) { + return false; + } + + unloaded_module_list_base_.SizeOfHeader = + sizeof(MINIDUMP_UNLOADED_MODULE_LIST); + unloaded_module_list_base_.SizeOfEntry = sizeof(MINIDUMP_UNLOADED_MODULE); + + size_t unloaded_module_count = unloaded_modules_.size(); + if (!AssignIfInRange(&unloaded_module_list_base_.NumberOfEntries, + unloaded_module_count)) { + LOG(ERROR) << "unloaded_module_count " << unloaded_module_count + << " out of range"; + return false; + } + + return true; +} + +size_t MinidumpUnloadedModuleListWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return sizeof(unloaded_module_list_base_) + + unloaded_modules_.size() * sizeof(MINIDUMP_UNLOADED_MODULE); +} + +std::vector +MinidumpUnloadedModuleListWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + + std::vector children; + for (const auto& unloaded_module : unloaded_modules_) { + children.push_back(unloaded_module.get()); + } + + return children; +} + +bool MinidumpUnloadedModuleListWriter::WriteObject( + FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + WritableIoVec iov; + iov.iov_base = &unloaded_module_list_base_; + iov.iov_len = sizeof(unloaded_module_list_base_); + std::vector iovecs(1, iov); + + for (const auto& unloaded_module : unloaded_modules_) { + iov.iov_base = unloaded_module->MinidumpUnloadedModule(); + iov.iov_len = sizeof(MINIDUMP_UNLOADED_MODULE); + iovecs.push_back(iov); + } + + return file_writer->WriteIoVec(&iovecs); +} + +MinidumpStreamType MinidumpUnloadedModuleListWriter::StreamType() const { + return kMinidumpStreamTypeUnloadedModuleList; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_unloaded_module_writer.h b/shared/sentry/external/crashpad/minidump/minidump_unloaded_module_writer.h new file mode 100644 index 000000000..6b3ac9059 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_unloaded_module_writer.h @@ -0,0 +1,160 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_UNLOADED_MODULE_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_UNLOADED_MODULE_WRITER_H_ + +#include +#include +#include + +#include +#include +#include + +#include "minidump/minidump_stream_writer.h" +#include "minidump/minidump_string_writer.h" +#include "minidump/minidump_writable.h" +#include "snapshot/unloaded_module_snapshot.h" + +namespace crashpad { + +//! \brief The writer for a MINIDUMP_UNLOADED_MODULE object in a minidump file. +//! +//! Because MINIDUMP_UNLOADED_MODULE objects only appear as elements of +//! MINIDUMP_UNLOADED_MODULE_LIST objects, this class does not write any data on +//! its own. It makes its MINIDUMP_UNLOADED_MODULE data available to its +//! MinidumpUnloadedModuleListWriter parent, which writes it as part of a +//! MINIDUMP_UNLOADED_MODULE_LIST. +class MinidumpUnloadedModuleWriter final : public internal::MinidumpWritable { + public: + MinidumpUnloadedModuleWriter(); + + MinidumpUnloadedModuleWriter(const MinidumpUnloadedModuleWriter&) = delete; + MinidumpUnloadedModuleWriter& operator=(const MinidumpUnloadedModuleWriter&) = + delete; + + ~MinidumpUnloadedModuleWriter() override; + + //! \brief Initializes the MINIDUMP_UNLOADED_MODULE based on \a + //! unloaded_module_snapshot. + //! + //! \param[in] unloaded_module_snapshot The unloaded module snapshot to use as + //! source data. + //! + //! \note Valid in #kStateMutable. No mutator methods may be called before + //! this method, and it is not normally necessary to call any mutator + //! methods after this method. + void InitializeFromSnapshot( + const UnloadedModuleSnapshot& unloaded_module_snapshot); + + //! \brief Returns a MINIDUMP_UNLOADED_MODULE referencing this object’s data. + //! + //! This method is expected to be called by a MinidumpUnloadedModuleListWriter + //! in order to obtain a MINIDUMP_UNLOADED_MODULE to include in its list. + //! + //! \note Valid in #kStateWritable. + const MINIDUMP_UNLOADED_MODULE* MinidumpUnloadedModule() const; + + //! \brief Arranges for MINIDUMP_UNLOADED_MODULE::ModuleNameRva to point to a + //! MINIDUMP_STRING containing \a name. + //! + //! \note Valid in #kStateMutable. + void SetName(const std::string& name); + + //! \brief Sets MINIDUMP_UNLOADED_MODULE::BaseOfImage. + void SetImageBaseAddress(uint64_t image_base_address) { + unloaded_module_.BaseOfImage = image_base_address; + } + + //! \brief Sets MINIDUMP_UNLOADED_MODULE::SizeOfImage. + void SetImageSize(uint32_t image_size) { + unloaded_module_.SizeOfImage = image_size; + } + + //! \brief Sets MINIDUMP_UNLOADED_MODULE::CheckSum. + void SetChecksum(uint32_t checksum) { unloaded_module_.CheckSum = checksum; } + + //! \brief Sets MINIDUMP_UNLOADED_MODULE::TimeDateStamp. + //! + //! \note Valid in #kStateMutable. + void SetTimestamp(time_t timestamp); + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + MINIDUMP_UNLOADED_MODULE unloaded_module_; + std::unique_ptr name_; +}; + +//! \brief The writer for a MINIDUMP_UNLOADED_MODULE_LIST stream in a minidump +//! file, containing a list of MINIDUMP_UNLOADED_MODULE objects. +class MinidumpUnloadedModuleListWriter final + : public internal::MinidumpStreamWriter { + public: + MinidumpUnloadedModuleListWriter(); + + MinidumpUnloadedModuleListWriter(const MinidumpUnloadedModuleListWriter&) = + delete; + MinidumpUnloadedModuleListWriter& operator=( + const MinidumpUnloadedModuleListWriter&) = delete; + + ~MinidumpUnloadedModuleListWriter() override; + + //! \brief Adds an initialized MINIDUMP_UNLOADED_MODULE for each unloaded + //! module in \a unloaded_module_snapshots to the + //! MINIDUMP_UNLOADED_MODULE_LIST. + //! + //! \param[in] unloaded_module_snapshots The unloaded module snapshots to use + //! as source data. + //! + //! \note Valid in #kStateMutable. AddUnloadedModule() may not be called + //! before this this method, and it is not normally necessary to call + //! AddUnloadedModule() after this method. + void InitializeFromSnapshot( + const std::vector& unloaded_module_snapshots); + + //! \brief Adds a MinidumpUnloadedModuleWriter to the + //! MINIDUMP_UNLOADED_MODULE_LIST. + //! + //! This object takes ownership of \a unloaded_module and becomes its parent + //! in the overall tree of internal::MinidumpWritable objects. + //! + //! \note Valid in #kStateMutable. + void AddUnloadedModule( + std::unique_ptr unloaded_module); + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpStreamWriter: + MinidumpStreamType StreamType() const override; + + private: + std::vector> unloaded_modules_; + MINIDUMP_UNLOADED_MODULE_LIST unloaded_module_list_base_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_UNLOADED_MODULE_WRITER_H_ diff --git a/shared/sentry/external/crashpad/minidump/minidump_unloaded_module_writer_test.cc b/shared/sentry/external/crashpad/minidump/minidump_unloaded_module_writer_test.cc new file mode 100644 index 000000000..d7f8b66eb --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_unloaded_module_writer_test.cc @@ -0,0 +1,164 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_unloaded_module_writer.h" + +#include "base/strings/utf_string_conversions.h" +#include "gtest/gtest.h" +#include "minidump/minidump_file_writer.h" +#include "minidump/test/minidump_file_writer_test_util.h" +#include "minidump/test/minidump_string_writer_test_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "util/file/string_file.h" + +namespace crashpad { +namespace test { +namespace { + +void ExpectUnloadedModule(const MINIDUMP_UNLOADED_MODULE* expected, + const MINIDUMP_UNLOADED_MODULE* observed, + const std::string& file_contents, + const std::string& expected_module_name) { + EXPECT_EQ(observed->BaseOfImage, expected->BaseOfImage); + EXPECT_EQ(observed->SizeOfImage, expected->SizeOfImage); + EXPECT_EQ(observed->CheckSum, expected->CheckSum); + EXPECT_EQ(observed->TimeDateStamp, expected->TimeDateStamp); + EXPECT_NE(observed->ModuleNameRva, 0u); + std::u16string observed_module_name_utf16 = + MinidumpStringAtRVAAsString(file_contents, observed->ModuleNameRva); + std::u16string expected_module_name_utf16 = + base::UTF8ToUTF16(expected_module_name); + EXPECT_EQ(observed_module_name_utf16, expected_module_name_utf16); +} + +void GetUnloadedModuleListStream( + const std::string& file_contents, + const MINIDUMP_UNLOADED_MODULE_LIST** unloaded_module_list) { + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kUnloadedModuleListStreamOffset = + kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + constexpr size_t kUnloadedModulesOffset = + kUnloadedModuleListStreamOffset + sizeof(MINIDUMP_UNLOADED_MODULE_LIST); + + ASSERT_GE(file_contents.size(), kUnloadedModulesOffset); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(file_contents, &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0)); + ASSERT_TRUE(directory); + + ASSERT_EQ(directory[0].StreamType, kMinidumpStreamTypeUnloadedModuleList); + EXPECT_EQ(directory[0].Location.Rva, kUnloadedModuleListStreamOffset); + + *unloaded_module_list = + MinidumpWritableAtLocationDescriptor( + file_contents, directory[0].Location); + ASSERT_TRUE(unloaded_module_list); +} + +TEST(MinidumpUnloadedModuleWriter, EmptyModule) { + MinidumpFileWriter minidump_file_writer; + auto unloaded_module_list_writer = + std::make_unique(); + + static constexpr char kModuleName[] = "test_dll"; + + auto unloaded_module_writer = + std::make_unique(); + unloaded_module_writer->SetName(kModuleName); + + unloaded_module_list_writer->AddUnloadedModule( + std::move(unloaded_module_writer)); + ASSERT_TRUE( + minidump_file_writer.AddStream(std::move(unloaded_module_list_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_GT(string_file.string().size(), + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_UNLOADED_MODULE_LIST) + + 1 * sizeof(MINIDUMP_UNLOADED_MODULE)); + + const MINIDUMP_UNLOADED_MODULE_LIST* unloaded_module_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetUnloadedModuleListStream(string_file.string(), &unloaded_module_list)); + + EXPECT_EQ(unloaded_module_list->NumberOfEntries, 1u); + + MINIDUMP_UNLOADED_MODULE expected = {}; + ASSERT_NO_FATAL_FAILURE( + ExpectUnloadedModule(&expected, + reinterpret_cast( + &unloaded_module_list[1]), + string_file.string(), + kModuleName)); +} + +TEST(MinidumpUnloadedModuleWriter, OneModule) { + MinidumpFileWriter minidump_file_writer; + auto unloaded_module_list_writer = + std::make_unique(); + + static constexpr char kModuleName[] = "statically_linked"; + constexpr uint64_t kModuleBase = 0x10da69000; + constexpr uint32_t kModuleSize = 0x1000; + constexpr uint32_t kChecksum = 0x76543210; + constexpr time_t kTimestamp = 0x386d4380; + + auto unloaded_module_writer = + std::make_unique(); + unloaded_module_writer->SetName(kModuleName); + unloaded_module_writer->SetImageBaseAddress(kModuleBase); + unloaded_module_writer->SetImageSize(kModuleSize); + unloaded_module_writer->SetChecksum(kChecksum); + unloaded_module_writer->SetTimestamp(kTimestamp); + + unloaded_module_list_writer->AddUnloadedModule( + std::move(unloaded_module_writer)); + ASSERT_TRUE( + minidump_file_writer.AddStream(std::move(unloaded_module_list_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_GT(string_file.string().size(), + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + + sizeof(MINIDUMP_UNLOADED_MODULE_LIST) + + 1 * sizeof(MINIDUMP_UNLOADED_MODULE)); + + const MINIDUMP_UNLOADED_MODULE_LIST* unloaded_module_list = nullptr; + ASSERT_NO_FATAL_FAILURE( + GetUnloadedModuleListStream(string_file.string(), &unloaded_module_list)); + + EXPECT_EQ(unloaded_module_list->NumberOfEntries, 1u); + + MINIDUMP_UNLOADED_MODULE expected = {}; + expected.BaseOfImage = kModuleBase; + expected.SizeOfImage = kModuleSize; + expected.CheckSum = kChecksum; + expected.TimeDateStamp = kTimestamp; + + ASSERT_NO_FATAL_FAILURE( + ExpectUnloadedModule(&expected, + reinterpret_cast( + &unloaded_module_list[1]), + string_file.string(), + kModuleName)); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_user_extension_stream_data_source.cc b/shared/sentry/external/crashpad/minidump/minidump_user_extension_stream_data_source.cc new file mode 100644 index 000000000..884bf8591 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_user_extension_stream_data_source.cc @@ -0,0 +1,26 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_user_extension_stream_data_source.h" + +namespace crashpad { + +MinidumpUserExtensionStreamDataSource::MinidumpUserExtensionStreamDataSource( + uint32_t stream_type) + : stream_type_(static_cast(stream_type)) {} + +MinidumpUserExtensionStreamDataSource:: + ~MinidumpUserExtensionStreamDataSource() {} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_user_extension_stream_data_source.h b/shared/sentry/external/crashpad/minidump/minidump_user_extension_stream_data_source.h new file mode 100644 index 000000000..4a45e45f1 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_user_extension_stream_data_source.h @@ -0,0 +1,87 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_USER_EXTENSION_STREAM_DATA_SOURCE_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_USER_EXTENSION_STREAM_DATA_SOURCE_H_ + +#include +#include + + +#include "minidump/minidump_extensions.h" + +namespace crashpad { + +//! \brief Describes a user extension data stream in a minidump. +class MinidumpUserExtensionStreamDataSource { + public: + //! \brief An interface implemented by readers of + //! MinidumpUserExtensionStreamDataSource. + class Delegate { + public: + //! \brief Called by MinidumpUserExtensionStreamDataSource::Read() to + //! provide data requested by a call to that method. + //! + //! \param[in] data A pointer to the data that was read. The callee does not + //! take ownership of this data. This data is only valid for the + //! duration of the call to this method. This parameter may be `nullptr` + //! if \a size is `0`. + //! \param[in] size The size of the data that was read. + //! + //! \return `true` on success, `false` on failure. + //! MinidumpUserExtensionStreamDataSource::ReadStreamData() will use + //! this as its own return value. + virtual bool ExtensionStreamDataSourceRead(const void* data, + size_t size) = 0; + + protected: + ~Delegate() {} + }; + + //! \brief Constructs a MinidumpUserExtensionStreamDataSource. + //! + //! \param[in] stream_type The type of the user extension stream. + explicit MinidumpUserExtensionStreamDataSource(uint32_t stream_type); + + MinidumpUserExtensionStreamDataSource( + const MinidumpUserExtensionStreamDataSource&) = delete; + MinidumpUserExtensionStreamDataSource& operator=( + const MinidumpUserExtensionStreamDataSource&) = delete; + + virtual ~MinidumpUserExtensionStreamDataSource(); + + MinidumpStreamType stream_type() const { return stream_type_; } + + //! \brief The size of this data stream. + virtual size_t StreamDataSize() = 0; + + //! \brief Calls Delegate::UserStreamDataSourceRead(), providing it with + //! the stream data. + //! + //! Implementations do not necessarily compute the stream data prior to + //! this method being called. The stream data may be computed or loaded + //! lazily and may be discarded after being passed to the delegate. + //! + //! \return `false` on failure, otherwise, the return value of + //! Delegate::ExtensionStreamDataSourceRead(), which should be `true` on + //! success and `false` on failure. + virtual bool ReadStreamData(Delegate* delegate) = 0; + + private: + MinidumpStreamType stream_type_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_USER_EXTENSION_STREAM_DATA_SOURCE_H_ diff --git a/shared/sentry/external/crashpad/minidump/minidump_user_stream_writer.cc b/shared/sentry/external/crashpad/minidump/minidump_user_stream_writer.cc new file mode 100644 index 000000000..4fbe25257 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_user_stream_writer.cc @@ -0,0 +1,142 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_user_stream_writer.h" + +#include "base/check_op.h" +#include "util/file/file_writer.h" + +namespace crashpad { + +class MinidumpUserStreamWriter::ContentsWriter { + public: + virtual ~ContentsWriter() {} + virtual bool WriteContents(FileWriterInterface* writer) = 0; + virtual size_t GetSize() const = 0; +}; + +class MinidumpUserStreamWriter::SnapshotContentsWriter final + : public MinidumpUserStreamWriter::ContentsWriter, + public MemorySnapshot::Delegate { + public: + explicit SnapshotContentsWriter(const MemorySnapshot* snapshot) + : snapshot_(snapshot), writer_(nullptr) {} + + SnapshotContentsWriter(const SnapshotContentsWriter&) = delete; + SnapshotContentsWriter& operator=(const SnapshotContentsWriter&) = delete; + + bool WriteContents(FileWriterInterface* writer) override { + DCHECK(!writer_); + + writer_ = writer; + if (!snapshot_) + return true; + + return snapshot_->Read(this); + } + + size_t GetSize() const override { return snapshot_ ? snapshot_->Size() : 0; } + + bool MemorySnapshotDelegateRead(void* data, size_t size) override { + return writer_->Write(data, size); + } + + private: + const MemorySnapshot* snapshot_; + FileWriterInterface* writer_; +}; + +class MinidumpUserStreamWriter::ExtensionStreamContentsWriter final + : public MinidumpUserStreamWriter::ContentsWriter, + public MinidumpUserExtensionStreamDataSource::Delegate { + public: + explicit ExtensionStreamContentsWriter( + std::unique_ptr data_source) + : data_source_(std::move(data_source)), writer_(nullptr) {} + + ExtensionStreamContentsWriter(const ExtensionStreamContentsWriter&) = delete; + ExtensionStreamContentsWriter& operator=( + const ExtensionStreamContentsWriter&) = delete; + + bool WriteContents(FileWriterInterface* writer) override { + DCHECK(!writer_); + + writer_ = writer; + return data_source_->ReadStreamData(this); + } + + size_t GetSize() const override { return data_source_->StreamDataSize(); } + + bool ExtensionStreamDataSourceRead(const void* data, size_t size) override { + return writer_->Write(data, size); + } + + private: + std::unique_ptr data_source_; + FileWriterInterface* writer_; +}; + +MinidumpUserStreamWriter::MinidumpUserStreamWriter() : stream_type_() {} + +MinidumpUserStreamWriter::~MinidumpUserStreamWriter() { +} + +void MinidumpUserStreamWriter::InitializeFromSnapshot( + const UserMinidumpStream* stream) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(!contents_writer_.get()); + + stream_type_ = static_cast(stream->stream_type()); + contents_writer_ = std::make_unique(stream->memory()); +} + +void MinidumpUserStreamWriter::InitializeFromUserExtensionStream( + std::unique_ptr data_source) { + DCHECK_EQ(state(), kStateMutable); + DCHECK(!contents_writer_.get()); + + stream_type_ = data_source->stream_type(); + contents_writer_ = + std::make_unique(std::move(data_source)); +} + +bool MinidumpUserStreamWriter::Freeze() { + DCHECK_EQ(state(), kStateMutable); + DCHECK_NE(stream_type_, 0u); + return MinidumpStreamWriter::Freeze(); +} + +size_t MinidumpUserStreamWriter::SizeOfObject() { + DCHECK_GE(state(), kStateFrozen); + + return contents_writer_->GetSize(); +} + +std::vector +MinidumpUserStreamWriter::Children() { + DCHECK_GE(state(), kStateFrozen); + return std::vector(); +} + +bool MinidumpUserStreamWriter::WriteObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state(), kStateWritable); + + return contents_writer_->WriteContents(file_writer); +} + +MinidumpStreamType MinidumpUserStreamWriter::StreamType() const { + return static_cast(stream_type_); +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_user_stream_writer.h b/shared/sentry/external/crashpad/minidump/minidump_user_stream_writer.h new file mode 100644 index 000000000..8f123e2e0 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_user_stream_writer.h @@ -0,0 +1,80 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_USER_STREAM_WRITER_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_USER_STREAM_WRITER_H_ + +#include +#include +#include + +#include +#include + +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_stream_writer.h" +#include "minidump/minidump_writable.h" +#include "minidump/minidump_user_extension_stream_data_source.h" +#include "snapshot/module_snapshot.h" + +namespace crashpad { + +//! \brief The writer for a MINIDUMP_USER_STREAM in a minidump file. +class MinidumpUserStreamWriter final : public internal::MinidumpStreamWriter { + public: + MinidumpUserStreamWriter(); + + MinidumpUserStreamWriter(const MinidumpUserStreamWriter&) = delete; + MinidumpUserStreamWriter& operator=(const MinidumpUserStreamWriter&) = delete; + + ~MinidumpUserStreamWriter() override; + + //! \brief Initializes a MINIDUMP_USER_STREAM based on \a stream. + //! + //! \param[in] stream The memory and stream type to use as source data. + //! + //! \note Valid in #kStateMutable. + void InitializeFromSnapshot(const UserMinidumpStream* stream); + + //! \brief Initializes a MINIDUMP_USER_STREAM based on \a data_source. + //! + //! \param[in] data_source The content and type of the stream. + //! + //! \note Valid in #kStateMutable. + void InitializeFromUserExtensionStream( + std::unique_ptr data_source); + + protected: + // MinidumpWritable: + bool Freeze() override; + size_t SizeOfObject() override; + std::vector Children() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + // MinidumpStreamWriter: + MinidumpStreamType StreamType() const override; + + private: + class ContentsWriter; + class SnapshotContentsWriter; + class ExtensionStreamContentsWriter; + + std::unique_ptr contents_writer_; + + MinidumpStreamType stream_type_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_USER_STREAM_WRITER_H_ diff --git a/shared/sentry/external/crashpad/minidump/minidump_user_stream_writer_test.cc b/shared/sentry/external/crashpad/minidump/minidump_user_stream_writer_test.cc new file mode 100644 index 000000000..c3bbd6e78 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_user_stream_writer_test.cc @@ -0,0 +1,149 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_user_stream_writer.h" + +#include +#include + +#include "gtest/gtest.h" +#include "minidump/minidump_file_writer.h" +#include "minidump/test/minidump_file_writer_test_util.h" +#include "minidump/test/minidump_user_extension_stream_util.h" +#include "minidump/test/minidump_writable_test_util.h" +#include "snapshot/test/test_memory_snapshot.h" +#include "util/file/string_file.h" + +namespace crashpad { +namespace test { +namespace { + +// The user stream is expected to be the only stream. +void GetUserStream(const std::string& file_contents, + MINIDUMP_LOCATION_DESCRIPTOR* user_stream_location, + uint32_t stream_type, + size_t stream_size) { + constexpr size_t kDirectoryOffset = sizeof(MINIDUMP_HEADER); + constexpr size_t kUserStreamOffset = + kDirectoryOffset + sizeof(MINIDUMP_DIRECTORY); + + const MINIDUMP_DIRECTORY* directory; + const MINIDUMP_HEADER* header = + MinidumpHeaderAtStart(file_contents, &directory); + ASSERT_NO_FATAL_FAILURE(VerifyMinidumpHeader(header, 1, 0)); + ASSERT_TRUE(directory); + + constexpr size_t kDirectoryIndex = 0; + + ASSERT_EQ(directory[kDirectoryIndex].StreamType, stream_type); + EXPECT_EQ(directory[kDirectoryIndex].Location.Rva, kUserStreamOffset); + EXPECT_EQ(directory[kDirectoryIndex].Location.DataSize, stream_size); + *user_stream_location = directory[kDirectoryIndex].Location; +} + +constexpr MinidumpStreamType kTestStreamId = + static_cast(0x123456); + +TEST(MinidumpUserStreamWriter, InitializeFromSnapshotNoData) { + MinidumpFileWriter minidump_file_writer; + auto user_stream_writer = std::make_unique(); + auto stream = std::make_unique(kTestStreamId, nullptr); + user_stream_writer->InitializeFromSnapshot(stream.get()); + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(user_stream_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY)); + + MINIDUMP_LOCATION_DESCRIPTOR user_stream_location; + ASSERT_NO_FATAL_FAILURE(GetUserStream( + string_file.string(), &user_stream_location, kTestStreamId, 0u)); +} + +TEST(MinidumpUserStreamWriter, InitializeFromUserExtensionStreamNoData) { + MinidumpFileWriter minidump_file_writer; + auto data_source = std::make_unique( + kTestStreamId, nullptr, 0); + auto user_stream_writer = std::make_unique(); + user_stream_writer->InitializeFromUserExtensionStream(std::move(data_source)); + minidump_file_writer.AddStream(std::move(user_stream_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY)); + + MINIDUMP_LOCATION_DESCRIPTOR user_stream_location; + ASSERT_NO_FATAL_FAILURE(GetUserStream( + string_file.string(), &user_stream_location, kTestStreamId, 0u)); +} + +TEST(MinidumpUserStreamWriter, InitializeFromSnapshotOneStream) { + MinidumpFileWriter minidump_file_writer; + auto user_stream_writer = std::make_unique(); + + TestMemorySnapshot* test_data = new TestMemorySnapshot(); + test_data->SetAddress(97865); + constexpr size_t kStreamSize = 128; + test_data->SetSize(kStreamSize); + test_data->SetValue('c'); + auto stream = std::make_unique(kTestStreamId, test_data); + user_stream_writer->InitializeFromSnapshot(stream.get()); + ASSERT_TRUE(minidump_file_writer.AddStream(std::move(user_stream_writer))); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + kStreamSize); + + MINIDUMP_LOCATION_DESCRIPTOR user_stream_location = {}; + ASSERT_NO_FATAL_FAILURE(GetUserStream( + string_file.string(), &user_stream_location, kTestStreamId, kStreamSize)); + const std::string stream_data = string_file.string().substr( + user_stream_location.Rva, user_stream_location.DataSize); + EXPECT_EQ(stream_data, std::string(kStreamSize, 'c')); +} + +TEST(MinidumpUserStreamWriter, InitializeFromBufferOneStream) { + MinidumpFileWriter minidump_file_writer; + + constexpr size_t kStreamSize = 128; + std::vector data(kStreamSize, 'c'); + auto data_source = std::make_unique( + kTestStreamId, &data[0], data.size()); + auto user_stream_writer = std::make_unique(); + user_stream_writer->InitializeFromUserExtensionStream(std::move(data_source)); + minidump_file_writer.AddStream(std::move(user_stream_writer)); + + StringFile string_file; + ASSERT_TRUE(minidump_file_writer.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), + sizeof(MINIDUMP_HEADER) + sizeof(MINIDUMP_DIRECTORY) + kStreamSize); + + MINIDUMP_LOCATION_DESCRIPTOR user_stream_location = {}; + ASSERT_NO_FATAL_FAILURE(GetUserStream( + string_file.string(), &user_stream_location, kTestStreamId, kStreamSize)); + const std::string stream_data = string_file.string().substr( + user_stream_location.Rva, user_stream_location.DataSize); + EXPECT_EQ(stream_data, std::string(kStreamSize, 'c')); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_writable.cc b/shared/sentry/external/crashpad/minidump/minidump_writable.cc new file mode 100644 index 000000000..9bfba52e0 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_writable.cc @@ -0,0 +1,266 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_writable.h" + +#include + +#include + +#include "base/logging.h" +#include "util/file/file_writer.h" +#include "util/numeric/safe_assignment.h" + +namespace { + +constexpr size_t kMaximumAlignment = 16; + +} // namespace + +namespace crashpad { +namespace internal { + +MinidumpWritable::~MinidumpWritable() { +} + +bool MinidumpWritable::WriteEverything(FileWriterInterface* file_writer) { + DCHECK_EQ(state_, kStateMutable); + + if (!Freeze()) { + return false; + } + + DCHECK_EQ(state_, kStateFrozen); + + FileOffset offset = 0; + std::vector write_sequence; + size_t size = WillWriteAtOffset(kPhaseEarly, &offset, &write_sequence); + if (size == kInvalidSize) { + return false; + } + + offset += size; + if (WillWriteAtOffset(kPhaseLate, &offset, &write_sequence) == kInvalidSize) { + return false; + } + + DCHECK_EQ(state_, kStateWritable); + DCHECK_EQ(write_sequence.front(), this); + + for (MinidumpWritable* writable : write_sequence) { + if (!writable->WritePaddingAndObject(file_writer)) { + return false; + } + } + + DCHECK_EQ(state_, kStateWritten); + + return true; +} + +void MinidumpWritable::RegisterRVA(RVA* rva) { + DCHECK_LE(state_, kStateFrozen); + + registered_rvas_.push_back(rva); +} + +void MinidumpWritable::RegisterLocationDescriptor( + MINIDUMP_LOCATION_DESCRIPTOR* location_descriptor) { + DCHECK_LE(state_, kStateFrozen); + + registered_location_descriptors_.push_back(location_descriptor); +} + +MinidumpWritable::MinidumpWritable() + : registered_rvas_(), + registered_location_descriptors_(), + leading_pad_bytes_(0), + state_(kStateMutable) { +} + +bool MinidumpWritable::Freeze() { + DCHECK_EQ(state_, kStateMutable); + state_ = kStateFrozen; + + std::vector children = Children(); + for (MinidumpWritable* child : children) { + if (!child->Freeze()) { + return false; + } + } + + return true; +} + +size_t MinidumpWritable::Alignment() { + DCHECK_GE(state_, kStateFrozen); + + return 4; +} + +std::vector MinidumpWritable::Children() { + DCHECK_GE(state_, kStateFrozen); + + return std::vector(); +} + +MinidumpWritable::Phase MinidumpWritable::WritePhase() { + return kPhaseEarly; +} + +size_t MinidumpWritable::WillWriteAtOffset( + Phase phase, + FileOffset* offset, + std::vector* write_sequence) { + FileOffset local_offset = *offset; + CHECK_GE(local_offset, 0); + + size_t leading_pad_bytes_this_phase; + size_t size; + if (phase == WritePhase()) { + DCHECK_EQ(state_, kStateFrozen); + + // Add this object to the sequence of MinidumpWritable objects to be + // written. + write_sequence->push_back(this); + + size = SizeOfObject(); + + if (size > 0) { + // Honor this object’s request to be aligned to a specific byte boundary. + // Once the alignment is corrected, this object knows exactly what file + // offset it will be written at. + size_t alignment = Alignment(); + CHECK_LE(alignment, kMaximumAlignment); + + leading_pad_bytes_this_phase = + (alignment - (local_offset % alignment)) % alignment; + local_offset += leading_pad_bytes_this_phase; + *offset = local_offset; + } else { + // If the object is size 0, alignment is of no concern. + leading_pad_bytes_this_phase = 0; + } + leading_pad_bytes_ = leading_pad_bytes_this_phase; + + // Now that the file offset that this object will be written at is known, + // let the subclass implementation know in case it’s interested. + if (!WillWriteAtOffsetImpl(local_offset)) { + return kInvalidSize; + } + + // Populate the RVA fields in other objects that have registered to point to + // this one. Typically, a parent object will have registered to point to its + // children, but this can also occur where no parent-child relationship + // exists. + if (!registered_rvas_.empty() || + !registered_location_descriptors_.empty()) { + RVA local_rva; + if (!AssignIfInRange(&local_rva, local_offset)) { + LOG(ERROR) << "offset " << local_offset << " out of range"; + return kInvalidSize; + } + + for (RVA* rva : registered_rvas_) { + *rva = local_rva; + } + + if (!registered_location_descriptors_.empty()) { + decltype(registered_location_descriptors_[0]->DataSize) local_size; + if (!AssignIfInRange(&local_size, size)) { + LOG(ERROR) << "size " << size << " out of range"; + return kInvalidSize; + } + + for (MINIDUMP_LOCATION_DESCRIPTOR* location_descriptor : + registered_location_descriptors_) { + location_descriptor->DataSize = local_size; + location_descriptor->Rva = local_rva; + } + } + } + + // This object is now considered writable. However, if it contains RVA or + // MINIDUMP_LOCATION_DESCRIPTOR fields, they may not be fully updated yet, + // because it’s the repsonsibility of these fields’ pointees to update them. + // Once WillWriteAtOffset has completed running for both phases on an entire + // tree, and the entire tree has moved into kStateFrozen, all RVA and + // MINIDUMP_LOCATION_DESCRIPTOR fields within that tree will be populated. + state_ = kStateWritable; + } else { + if (phase == kPhaseEarly) { + DCHECK_EQ(state_, kStateFrozen); + } else { + DCHECK_EQ(state_, kStateWritable); + } + + size = 0; + leading_pad_bytes_this_phase = 0; + } + + // Loop over children regardless of whether this object itself will write + // during this phase. An object’s children are not required to be written + // during the same phase as their parent. + std::vector children = Children(); + for (MinidumpWritable* child : children) { + // Use “auto†here because it’s impossible to know whether size_t (size) or + // FileOffset (local_offset) is the wider type, and thus what type the + // result of adding these two variables will have. + auto unaligned_child_offset = local_offset + size; + FileOffset child_offset; + if (!AssignIfInRange(&child_offset, unaligned_child_offset)) { + LOG(ERROR) << "offset " << unaligned_child_offset << " out of range"; + return kInvalidSize; + } + + size_t child_size = + child->WillWriteAtOffset(phase, &child_offset, write_sequence); + if (child_size == kInvalidSize) { + return kInvalidSize; + } + + size += child_size; + } + + return leading_pad_bytes_this_phase + size; +} + +bool MinidumpWritable::WillWriteAtOffsetImpl(FileOffset offset) { + return true; +} + +bool MinidumpWritable::WritePaddingAndObject(FileWriterInterface* file_writer) { + DCHECK_EQ(state_, kStateWritable); + + // The number of elements in kZeroes must be at least one less than the + // maximum Alignment() ever encountered. + static constexpr uint8_t kZeroes[kMaximumAlignment - 1] = {}; + DCHECK_LE(leading_pad_bytes_, std::size(kZeroes)); + + if (leading_pad_bytes_) { + if (!file_writer->Write(&kZeroes, leading_pad_bytes_)) { + return false; + } + } + + if (!WriteObject(file_writer)) { + return false; + } + + state_ = kStateWritten; + return true; +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_writable.h b/shared/sentry/external/crashpad/minidump/minidump_writable.h new file mode 100644 index 000000000..bc36cb5c0 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_writable.h @@ -0,0 +1,280 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_WRITABLE_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_WRITABLE_H_ + +#include +#include +#include + +#include +#include + +#include "util/file/file_io.h" + +namespace crashpad { + +class FileWriterInterface; + +namespace internal { + +//! \brief The base class for all content that might be written to a minidump +//! file. +class MinidumpWritable { + public: + MinidumpWritable(const MinidumpWritable&) = delete; + MinidumpWritable& operator=(const MinidumpWritable&) = delete; + + virtual ~MinidumpWritable(); + + //! \brief Writes an object and all of its children to a minidump file. + //! + //! Use this on the root object of a tree of MinidumpWritable objects, + //! typically on a MinidumpFileWriter object. + //! + //! \param[in] file_writer The file writer to receive the minidump file’s + //! content. + //! + //! \return `true` on success. `false` on failure, with an appropriate message + //! logged. + //! + //! \note Valid in #kStateMutable, and transitions the object and the entire + //! tree beneath it through all states to #kStateWritten. + //! + //! \note This method should rarely be overridden. + virtual bool WriteEverything(FileWriterInterface* file_writer); + + //! \brief Registers a file offset pointer as one that should point to the + //! object on which this method is called. + //! + //! Once the file offset at which an object will be written is known (when it + //! enters #kStateWritable), registered RVA pointers will be updated. + //! + //! \param[in] rva A pointer to storage for the file offset that should + //! contain this object’s writable file offset, once it is known. + //! + //! \note Valid in #kStateFrozen or any preceding state. + // + // This is public instead of protected because objects of derived classes need + // to be able to register their own pointers with distinct objects. + void RegisterRVA(RVA* rva); + + //! \brief Registers a location descriptor as one that should point to the + //! object on which this method is called. + //! + //! Once an object’s size and the file offset at it will be written is known + //! (when it enters #kStateFrozen), the relevant data in registered location + //! descriptors will be updated. + //! + //! \param[in] location_descriptor A pointer to a location descriptor that + //! should contain this object’s writable size and file offset, once they + //! are known. + //! + //! \note Valid in #kStateFrozen or any preceding state. + // + // This is public instead of protected because objects of derived classes need + // to be able to register their own pointers with distinct objects. + void RegisterLocationDescriptor( + MINIDUMP_LOCATION_DESCRIPTOR* location_descriptor); + + protected: + //! \brief Identifies the state of an object. + //! + //! Objects will normally transition through each of these states as they are + //! created, populated with data, and then written to a minidump file. + enum State { + //! \brief The object’s properties can be modified. + kStateMutable = 0, + + //! \brief The object is “frozenâ€. + //! + //! Its properties cannot be modified. Pointers to file offsets of other + //! structures may not yet be valid. + kStateFrozen, + + //! \brief The object is writable. + //! + //! The file offset at which it will be written is known. Pointers to file + //! offsets of other structures are valid when all objects in a tree are in + //! this state. + kStateWritable, + + //! \brief The object has been written to a minidump file. + kStateWritten, + }; + + //! \brief Identifies the phase during which an object will be written to a + //! minidump file. + enum Phase { + //! \brief Objects that are written to a minidump file “earlyâ€. + //! + //! The normal sequence is for an object to write itself and then write all + //! of its children. + kPhaseEarly = 0, + + //! \brief Objects that are written to a minidump file “lateâ€. + //! + //! Some objects, such as those capturing memory region snapshots, are + //! written to minidump files after all other objects. This “late†phase + //! identifies such objects. This is useful to improve spatial locality in + //! minidump files in accordance with expected access patterns: unlike most + //! other data, memory snapshots are large and do not usually need to be + //! consulted in their entirety in order to process a minidump file. + kPhaseLate, + }; + + //! \brief A size value used to signal failure by methods that return + //! `size_t`. + static constexpr size_t kInvalidSize = std::numeric_limits::max(); + + MinidumpWritable(); + + //! \brief The state of the object. + State state() const { return state_; } + + //! \brief Transitions the object from #kStateMutable to #kStateFrozen. + //! + //! The default implementation marks the object as frozen and recursively + //! calls Freeze() on all of its children. Subclasses may override this method + //! to perform processing that should only be done once callers have finished + //! populating an object with data. Typically, a subclass implementation would + //! call RegisterRVA() or RegisterLocationDescriptor() on other objects as + //! appropriate, because at the time Freeze() runs, the in-memory locations of + //! RVAs and location descriptors are known and will not change for the + //! remaining duration of an object’s lifetime. + //! + //! \return `true` on success. `false` on failure, with an appropriate message + //! logged. + virtual bool Freeze(); + + //! \brief Returns the amount of space that this object will consume when + //! written to a minidump file, in bytes, not including any leading or + //! trailing padding necessary to maintain proper alignment. + //! + //! \note Valid in #kStateFrozen or any subsequent state. + virtual size_t SizeOfObject() = 0; + + //! \brief Returns the object’s desired byte-boundary alignment. + //! + //! The default implementation returns `4`. Subclasses may override this as + //! needed. + //! + //! \note Valid in #kStateFrozen or any subsequent state. + virtual size_t Alignment(); + + //! \brief Returns the object’s children. + //! + //! \note Valid in #kStateFrozen or any subsequent state. + virtual std::vector Children(); + + //! \brief Returns the object’s desired write phase. + //! + //! The default implementation returns #kPhaseEarly. Subclasses may override + //! this method to alter their write phase. + //! + //! \note Valid in any state. + virtual Phase WritePhase(); + + //! \brief Prepares the object to be written at a known file offset, + //! transitioning it from #kStateFrozen to #kStateWritable. + //! + //! This method is responsible for determining the final file offset of the + //! object, which may be increased from \a offset to meet alignment + //! requirements. It calls WillWriteAtOffsetImpl() for the benefit of + //! subclasses. It populates all RVAs and location descriptors registered with + //! it via RegisterRVA() and RegisterLocationDescriptor(). It also recurses + //! into all known children. + //! + //! \param[in] phase The phase during which the object will be written. If + //! this does not match Phase(), processing is suppressed, although + //! recursive processing will still occur on all children. This addresses + //! the case where parents and children do not write in the same phase. + //! \param[in] offset The file offset at which the object will be written. The + //! offset may need to be adjusted for alignment. + //! \param[out] write_sequence This object will append itself to this list, + //! such that on return from a recursive tree of WillWriteAtOffset() + //! calls, elements of the vector will be organized in the sequence that + //! the objects will be written to the minidump file. + //! + //! \return The file size consumed by this object and all children, including + //! any padding inserted to meet alignment requirements. On failure, + //! #kInvalidSize, with an appropriate message logged. + //! + //! \note This method cannot be overridden. Subclasses that need to perform + //! processing when an object transitions to #kStateWritable should + //! implement WillWriteAtOffsetImpl(), which is called by this method. + size_t WillWriteAtOffset(Phase phase, + FileOffset* offset, + std::vector* write_sequence); + + //! \brief Called once an object’s writable file offset is determined, as it + //! transitions into #kStateWritable. + //! + //! Subclasses can override this method if they need to provide additional + //! processing once their writable file offset is known. Typically, this will + //! be done by subclasses that handle certain RVAs themselves instead of using + //! the RegisterRVA() interface. + //! + //! \param[in] offset The file offset at which the object will be written. The + //! value passed to this method will already have been adjusted to meet + //! alignment requirements. + //! + //! \return `true` on success. `false` on error, indicating that the minidump + //! file should not be written. + //! + //! \note Valid in #kStateFrozen. The object will transition to + //! #kStateWritable after this method returns. + virtual bool WillWriteAtOffsetImpl(FileOffset offset); + + //! \brief Writes the object, transitioning it from #kStateWritable to + //! #kStateWritten. + //! + //! Writes any padding necessary to meet alignment requirements, and then + //! calls WriteObject() to write the object’s content. + //! + //! \param[in] file_writer The file writer to receive the object’s content. + //! + //! \return `true` on success. `false` on error with an appropriate message + //! logged. + //! + //! \note This method cannot be overridden. Subclasses must override + //! WriteObject(). + bool WritePaddingAndObject(FileWriterInterface* file_writer); + + //! \brief Writes the object’s content. + //! + //! \param[in] file_writer The file writer to receive the object’s content. + //! + //! \return `true` on success. `false` on error, indicating that the content + //! could not be written to the minidump file. + //! + //! \note Valid in #kStateWritable. The object will transition to + //! #kStateWritten after this method returns. + virtual bool WriteObject(FileWriterInterface* file_writer) = 0; + + private: + std::vector registered_rvas_; // weak + + // weak + std::vector registered_location_descriptors_; + + size_t leading_pad_bytes_; + State state_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_WRITABLE_H_ diff --git a/shared/sentry/external/crashpad/minidump/minidump_writable_test.cc b/shared/sentry/external/crashpad/minidump/minidump_writable_test.cc new file mode 100644 index 000000000..ef34e5422 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_writable_test.cc @@ -0,0 +1,844 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_writable.h" + +#include +#include + +#include "gtest/gtest.h" +#include "util/file/string_file.h" + +namespace crashpad { +namespace test { +namespace { + +class BaseTestMinidumpWritable : public crashpad::internal::MinidumpWritable { + public: + BaseTestMinidumpWritable() + : MinidumpWritable(), + children_(), + expected_offset_(-1), + alignment_(0), + phase_(kPhaseEarly), + has_alignment_(false), + has_phase_(false), + verified_(false) {} + + BaseTestMinidumpWritable(const BaseTestMinidumpWritable&) = delete; + BaseTestMinidumpWritable& operator=(const BaseTestMinidumpWritable&) = delete; + + ~BaseTestMinidumpWritable() { EXPECT_TRUE(verified_); } + + void SetAlignment(size_t alignment) { + alignment_ = alignment; + has_alignment_ = true; + } + + void AddChild(BaseTestMinidumpWritable* child) { children_.push_back(child); } + + void SetPhaseLate() { + phase_ = kPhaseLate; + has_phase_ = true; + } + + void Verify() { + verified_ = true; + EXPECT_EQ(state(), kStateWritten); + for (BaseTestMinidumpWritable* child : children_) { + child->Verify(); + } + } + + protected: + bool Freeze() override { + EXPECT_EQ(state(), kStateMutable); + bool rv = MinidumpWritable::Freeze(); + EXPECT_TRUE(rv); + EXPECT_EQ(state(), kStateFrozen); + return rv; + } + + size_t Alignment() override { + EXPECT_GE(state(), kStateFrozen); + return has_alignment_ ? alignment_ : MinidumpWritable::Alignment(); + } + + std::vector Children() override { + EXPECT_GE(state(), kStateFrozen); + if (!children_.empty()) { + std::vector children; + for (BaseTestMinidumpWritable* child : children_) { + children.push_back(child); + } + return children; + } + return MinidumpWritable::Children(); + } + + Phase WritePhase() override { + return has_phase_ ? phase_ : MinidumpWritable::Phase(); + } + + bool WillWriteAtOffsetImpl(FileOffset offset) override { + EXPECT_EQ(kStateFrozen, state()); + expected_offset_ = offset; + bool rv = MinidumpWritable::WillWriteAtOffsetImpl(offset); + EXPECT_TRUE(rv); + return rv; + } + + bool WriteObject(FileWriterInterface* file_writer) override { + EXPECT_EQ(kStateWritable, state()); + EXPECT_EQ(file_writer->Seek(0, SEEK_CUR), expected_offset_); + + // Subclasses must override this. + return false; + } + + private: + std::vector children_; + FileOffset expected_offset_; + size_t alignment_; + Phase phase_; + bool has_alignment_; + bool has_phase_; + bool verified_; +}; + +class TestStringMinidumpWritable final : public BaseTestMinidumpWritable { + public: + TestStringMinidumpWritable() : BaseTestMinidumpWritable(), data_() {} + + TestStringMinidumpWritable(const TestStringMinidumpWritable&) = delete; + TestStringMinidumpWritable& operator=(const TestStringMinidumpWritable&) = + delete; + + ~TestStringMinidumpWritable() {} + + void SetData(const std::string& string) { data_ = string; } + + protected: + size_t SizeOfObject() override { + EXPECT_GE(state(), kStateFrozen); + return data_.size(); + } + + bool WriteObject(FileWriterInterface* file_writer) override { + BaseTestMinidumpWritable::WriteObject(file_writer); + bool rv = file_writer->Write(&data_[0], data_.size()); + EXPECT_TRUE(rv); + return rv; + } + + private: + std::string data_; +}; + +TEST(MinidumpWritable, MinidumpWritable) { + StringFile string_file; + + { + SCOPED_TRACE("empty"); + string_file.Reset(); + TestStringMinidumpWritable string_writable; + EXPECT_TRUE(string_writable.WriteEverything(&string_file)); + EXPECT_TRUE(string_file.string().empty()); + string_writable.Verify(); + } + + { + SCOPED_TRACE("childless"); + string_file.Reset(); + TestStringMinidumpWritable string_writable; + string_writable.SetData("a"); + EXPECT_TRUE(string_writable.WriteEverything(&string_file)); + EXPECT_EQ(string_file.string().size(), 1u); + EXPECT_EQ(string_file.string(), "a"); + string_writable.Verify(); + } + + { + SCOPED_TRACE("parent-child"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("b"); + TestStringMinidumpWritable child; + child.SetData("c"); + parent.AddChild(&child); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(string_file.string().size(), 5u); + EXPECT_EQ(string_file.string(), std::string("b\0\0\0c", 5)); + parent.Verify(); + } + + { + SCOPED_TRACE("base alignment 2"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("de"); + TestStringMinidumpWritable child; + child.SetData("f"); + parent.AddChild(&child); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(string_file.string().size(), 5u); + EXPECT_EQ(string_file.string(), std::string("de\0\0f", 5)); + parent.Verify(); + } + + { + SCOPED_TRACE("base alignment 3"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("ghi"); + TestStringMinidumpWritable child; + child.SetData("j"); + parent.AddChild(&child); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(string_file.string().size(), 5u); + EXPECT_EQ(string_file.string(), std::string("ghi\0j", 5)); + parent.Verify(); + } + + { + SCOPED_TRACE("base alignment 4"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("klmn"); + TestStringMinidumpWritable child; + child.SetData("o"); + parent.AddChild(&child); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(string_file.string().size(), 5u); + EXPECT_EQ(string_file.string(), "klmno"); + parent.Verify(); + } + + { + SCOPED_TRACE("base alignment 5"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("pqrst"); + TestStringMinidumpWritable child; + child.SetData("u"); + parent.AddChild(&child); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(string_file.string().size(), 9u); + EXPECT_EQ(string_file.string(), std::string("pqrst\0\0\0u", 9)); + parent.Verify(); + } + + { + SCOPED_TRACE("two children"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("parent"); + TestStringMinidumpWritable child_0; + child_0.SetData("child_0"); + parent.AddChild(&child_0); + TestStringMinidumpWritable child_1; + child_1.SetData("child_1"); + parent.AddChild(&child_1); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(string_file.string().size(), 23u); + EXPECT_EQ(string_file.string(), + std::string("parent\0\0child_0\0child_1", 23)); + parent.Verify(); + } + + { + SCOPED_TRACE("grandchild"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("parent"); + TestStringMinidumpWritable child; + child.SetData("child"); + parent.AddChild(&child); + TestStringMinidumpWritable grandchild; + grandchild.SetData("grandchild"); + child.AddChild(&grandchild); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(string_file.string().size(), 26u); + EXPECT_EQ(string_file.string(), + std::string("parent\0\0child\0\0\0grandchild", 26)); + parent.Verify(); + } + + { + SCOPED_TRACE("grandchild with empty parent"); + string_file.Reset(); + TestStringMinidumpWritable parent; + TestStringMinidumpWritable child; + child.SetData("child"); + parent.AddChild(&child); + TestStringMinidumpWritable grandchild; + grandchild.SetData("grandchild"); + child.AddChild(&grandchild); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(string_file.string().size(), 18u); + EXPECT_EQ(string_file.string(), std::string("child\0\0\0grandchild", 18)); + parent.Verify(); + } + + { + SCOPED_TRACE("grandchild with empty child"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("parent"); + TestStringMinidumpWritable child; + parent.AddChild(&child); + TestStringMinidumpWritable grandchild; + grandchild.SetData("grandchild"); + child.AddChild(&grandchild); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(string_file.string().size(), 18u); + EXPECT_EQ(string_file.string(), std::string("parent\0\0grandchild", 18)); + parent.Verify(); + } + + { + SCOPED_TRACE("grandchild with empty grandchild"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("parent"); + TestStringMinidumpWritable child; + child.SetData("child"); + parent.AddChild(&child); + TestStringMinidumpWritable grandchild; + child.AddChild(&grandchild); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(string_file.string().size(), 13u); + EXPECT_EQ(string_file.string(), std::string("parent\0\0child", 13)); + parent.Verify(); + } + + { + SCOPED_TRACE("grandchild with late-phase grandchild"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("parent"); + TestStringMinidumpWritable child; + child.SetData("child"); + parent.AddChild(&child); + TestStringMinidumpWritable grandchild; + grandchild.SetData("grandchild"); + grandchild.SetPhaseLate(); + child.AddChild(&grandchild); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(string_file.string().size(), 26u); + EXPECT_EQ(string_file.string(), + std::string("parent\0\0child\0\0\0grandchild", 26)); + parent.Verify(); + } + + { + SCOPED_TRACE("grandchild with late-phase child"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("parent"); + TestStringMinidumpWritable child; + child.SetData("child"); + child.SetPhaseLate(); + parent.AddChild(&child); + TestStringMinidumpWritable grandchild; + grandchild.SetData("grandchild"); + child.AddChild(&grandchild); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(string_file.string().size(), 25u); + EXPECT_EQ(string_file.string(), + std::string("parent\0\0grandchild\0\0child", 25)); + parent.Verify(); + } + + { + SCOPED_TRACE("family tree"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("P.."); + TestStringMinidumpWritable child_0; + child_0.SetData("C0."); + parent.AddChild(&child_0); + TestStringMinidumpWritable child_1; + child_1.SetData("C1."); + parent.AddChild(&child_1); + TestStringMinidumpWritable grandchild_00; + grandchild_00.SetData("G00"); + child_0.AddChild(&grandchild_00); + TestStringMinidumpWritable grandchild_01; + grandchild_01.SetData("G01"); + child_0.AddChild(&grandchild_01); + TestStringMinidumpWritable grandchild_10; + grandchild_10.SetData("G10"); + child_1.AddChild(&grandchild_10); + TestStringMinidumpWritable grandchild_11; + grandchild_11.SetData("G11"); + child_1.AddChild(&grandchild_11); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(string_file.string().size(), 27u); + EXPECT_EQ(string_file.string(), + std::string("P..\0C0.\0G00\0G01\0C1.\0G10\0G11", 27)); + parent.Verify(); + } + + { + SCOPED_TRACE("family tree with C0 late"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("P.."); + TestStringMinidumpWritable child_0; + child_0.SetData("C0."); + child_0.SetPhaseLate(); + parent.AddChild(&child_0); + TestStringMinidumpWritable child_1; + child_1.SetData("C1."); + parent.AddChild(&child_1); + TestStringMinidumpWritable grandchild_00; + grandchild_00.SetData("G00"); + child_0.AddChild(&grandchild_00); + TestStringMinidumpWritable grandchild_01; + grandchild_01.SetData("G01"); + child_0.AddChild(&grandchild_01); + TestStringMinidumpWritable grandchild_10; + grandchild_10.SetData("G10"); + child_1.AddChild(&grandchild_10); + TestStringMinidumpWritable grandchild_11; + grandchild_11.SetData("G11"); + child_1.AddChild(&grandchild_11); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(string_file.string().size(), 27u); + EXPECT_EQ(string_file.string(), + std::string("P..\0G00\0G01\0C1.\0G10\0G11\0C0.", 27)); + parent.Verify(); + } + + { + SCOPED_TRACE("family tree with G0 late"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("P.."); + TestStringMinidumpWritable child_0; + child_0.SetData("C0."); + parent.AddChild(&child_0); + TestStringMinidumpWritable child_1; + child_1.SetData("C1."); + parent.AddChild(&child_1); + TestStringMinidumpWritable grandchild_00; + grandchild_00.SetData("G00"); + grandchild_00.SetPhaseLate(); + child_0.AddChild(&grandchild_00); + TestStringMinidumpWritable grandchild_01; + grandchild_01.SetData("G01"); + grandchild_01.SetPhaseLate(); + child_0.AddChild(&grandchild_01); + TestStringMinidumpWritable grandchild_10; + grandchild_10.SetData("G10"); + child_1.AddChild(&grandchild_10); + TestStringMinidumpWritable grandchild_11; + grandchild_11.SetData("G11"); + child_1.AddChild(&grandchild_11); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(string_file.string().size(), 27u); + EXPECT_EQ(string_file.string(), + std::string("P..\0C0.\0C1.\0G10\0G11\0G00\0G01", 27)); + parent.Verify(); + } + + { + SCOPED_TRACE("align 1"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("p"); + TestStringMinidumpWritable child; + child.SetData("c"); + child.SetAlignment(1); + parent.AddChild(&child); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(string_file.string().size(), 2u); + EXPECT_EQ(string_file.string(), "pc"); + parent.Verify(); + } + + { + SCOPED_TRACE("align 2"); + string_file.Reset(); + TestStringMinidumpWritable parent; + parent.SetData("p"); + TestStringMinidumpWritable child; + child.SetData("c"); + child.SetAlignment(2); + parent.AddChild(&child); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + EXPECT_EQ(string_file.string().size(), 3u); + EXPECT_EQ(string_file.string(), std::string("p\0c", 3)); + parent.Verify(); + } +} + +class TestRVAMinidumpWritable final : public BaseTestMinidumpWritable { + public: + TestRVAMinidumpWritable() : BaseTestMinidumpWritable(), rva_() {} + + TestRVAMinidumpWritable(const TestRVAMinidumpWritable&) = delete; + TestRVAMinidumpWritable& operator=(const TestRVAMinidumpWritable&) = delete; + + ~TestRVAMinidumpWritable() {} + + void SetRVA(MinidumpWritable* other) { other->RegisterRVA(&rva_); } + + protected: + size_t SizeOfObject() override { + EXPECT_GE(state(), kStateFrozen); + return sizeof(rva_); + } + + bool WriteObject(FileWriterInterface* file_writer) override { + BaseTestMinidumpWritable::WriteObject(file_writer); + EXPECT_TRUE(file_writer->Write(&rva_, sizeof(rva_))); + return true; + } + + private: + RVA rva_; +}; + +RVA RVAAtIndex(const std::string& string, size_t index) { + return *reinterpret_cast(&string[index * sizeof(RVA)]); +} + +TEST(MinidumpWritable, RVA) { + StringFile string_file; + + { + SCOPED_TRACE("unset"); + string_file.Reset(); + TestRVAMinidumpWritable rva_writable; + EXPECT_TRUE(rva_writable.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), sizeof(RVA)); + EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 0 * sizeof(RVA)); + rva_writable.Verify(); + } + + { + SCOPED_TRACE("self"); + string_file.Reset(); + TestRVAMinidumpWritable rva_writable; + rva_writable.SetRVA(&rva_writable); + EXPECT_TRUE(rva_writable.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), sizeof(RVA)); + EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 0 * sizeof(RVA)); + rva_writable.Verify(); + } + + { + SCOPED_TRACE("parent-child self"); + string_file.Reset(); + TestRVAMinidumpWritable parent; + parent.SetRVA(&parent); + TestRVAMinidumpWritable child; + child.SetRVA(&child); + parent.AddChild(&child); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), 2 * sizeof(RVA)); + EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 0 * sizeof(RVA)); + EXPECT_EQ(RVAAtIndex(string_file.string(), 1), 1 * sizeof(RVA)); + parent.Verify(); + } + + { + SCOPED_TRACE("parent-child only"); + string_file.Reset(); + TestRVAMinidumpWritable parent; + TestRVAMinidumpWritable child; + parent.SetRVA(&child); + parent.AddChild(&child); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), 2 * sizeof(RVA)); + EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 1 * sizeof(RVA)); + EXPECT_EQ(RVAAtIndex(string_file.string(), 1), 0 * sizeof(RVA)); + parent.Verify(); + } + + { + SCOPED_TRACE("parent-child circular"); + string_file.Reset(); + TestRVAMinidumpWritable parent; + TestRVAMinidumpWritable child; + parent.SetRVA(&child); + child.SetRVA(&parent); + parent.AddChild(&child); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), 2 * sizeof(RVA)); + EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 1 * sizeof(RVA)); + EXPECT_EQ(RVAAtIndex(string_file.string(), 1), 0 * sizeof(RVA)); + parent.Verify(); + } + + { + SCOPED_TRACE("grandchildren"); + string_file.Reset(); + TestRVAMinidumpWritable parent; + TestRVAMinidumpWritable child; + parent.SetRVA(&child); + parent.AddChild(&child); + TestRVAMinidumpWritable grandchild_0; + grandchild_0.SetRVA(&child); + child.AddChild(&grandchild_0); + TestRVAMinidumpWritable grandchild_1; + grandchild_1.SetRVA(&child); + child.AddChild(&grandchild_1); + TestRVAMinidumpWritable grandchild_2; + grandchild_2.SetRVA(&child); + child.AddChild(&grandchild_2); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), 5 * sizeof(RVA)); + EXPECT_EQ(RVAAtIndex(string_file.string(), 0), 1 * sizeof(RVA)); + EXPECT_EQ(RVAAtIndex(string_file.string(), 1), 0 * sizeof(RVA)); + EXPECT_EQ(RVAAtIndex(string_file.string(), 2), 1 * sizeof(RVA)); + EXPECT_EQ(RVAAtIndex(string_file.string(), 3), 1 * sizeof(RVA)); + EXPECT_EQ(RVAAtIndex(string_file.string(), 4), 1 * sizeof(RVA)); + parent.Verify(); + } +} + +class TestLocationDescriptorMinidumpWritable final + : public BaseTestMinidumpWritable { + public: + TestLocationDescriptorMinidumpWritable() + : BaseTestMinidumpWritable(), location_descriptor_(), string_() {} + + TestLocationDescriptorMinidumpWritable( + const TestLocationDescriptorMinidumpWritable&) = delete; + TestLocationDescriptorMinidumpWritable& operator=( + const TestLocationDescriptorMinidumpWritable&) = delete; + + ~TestLocationDescriptorMinidumpWritable() {} + + void SetLocationDescriptor(MinidumpWritable* other) { + other->RegisterLocationDescriptor(&location_descriptor_); + } + + void SetString(const std::string& string) { string_ = string; } + + protected: + size_t SizeOfObject() override { + EXPECT_GE(state(), kStateFrozen); + // NUL-terminate. + return sizeof(location_descriptor_) + string_.size() + 1; + } + + bool WriteObject(FileWriterInterface* file_writer) override { + BaseTestMinidumpWritable::WriteObject(file_writer); + WritableIoVec iov; + iov.iov_base = &location_descriptor_; + iov.iov_len = sizeof(location_descriptor_); + std::vector iovecs(1, iov); + // NUL-terminate. + iov.iov_base = &string_[0]; + iov.iov_len = string_.size() + 1; + iovecs.push_back(iov); + EXPECT_TRUE(file_writer->WriteIoVec(&iovecs)); + return true; + } + + private: + MINIDUMP_LOCATION_DESCRIPTOR location_descriptor_; + std::string string_; +}; + +struct LocationDescriptorAndData { + MINIDUMP_LOCATION_DESCRIPTOR location_descriptor; + char string[1]; +}; + +const LocationDescriptorAndData* LDDAtIndex(const std::string& string, + size_t index) { + return reinterpret_cast(&string[index]); +} + +TEST(MinidumpWritable, LocationDescriptor) { + StringFile string_file; + + { + SCOPED_TRACE("unset"); + string_file.Reset(); + TestLocationDescriptorMinidumpWritable location_descriptor_writable; + EXPECT_TRUE(location_descriptor_writable.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), 9u); + const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0); + EXPECT_EQ(ldd->location_descriptor.DataSize, 0u); + EXPECT_EQ(ldd->location_descriptor.Rva, 0u); + location_descriptor_writable.Verify(); + } + + { + SCOPED_TRACE("self"); + string_file.Reset(); + TestLocationDescriptorMinidumpWritable location_descriptor_writable; + location_descriptor_writable.SetLocationDescriptor( + &location_descriptor_writable); + EXPECT_TRUE(location_descriptor_writable.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), 9u); + const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0); + EXPECT_EQ(ldd->location_descriptor.DataSize, 9u); + EXPECT_EQ(ldd->location_descriptor.Rva, 0u); + location_descriptor_writable.Verify(); + } + + { + SCOPED_TRACE("self with data"); + string_file.Reset(); + TestLocationDescriptorMinidumpWritable location_descriptor_writable; + location_descriptor_writable.SetLocationDescriptor( + &location_descriptor_writable); + location_descriptor_writable.SetString("zz"); + EXPECT_TRUE(location_descriptor_writable.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), 11u); + const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0); + EXPECT_EQ(ldd->location_descriptor.DataSize, 11u); + EXPECT_EQ(ldd->location_descriptor.Rva, 0u); + EXPECT_STREQ("zz", ldd->string); + location_descriptor_writable.Verify(); + } + + { + SCOPED_TRACE("parent-child self"); + string_file.Reset(); + TestLocationDescriptorMinidumpWritable parent; + parent.SetLocationDescriptor(&parent); + parent.SetString("yy"); + TestLocationDescriptorMinidumpWritable child; + child.SetLocationDescriptor(&child); + child.SetString("x"); + parent.AddChild(&child); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), 22u); + const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0); + EXPECT_EQ(ldd->location_descriptor.DataSize, 11u); + EXPECT_EQ(ldd->location_descriptor.Rva, 0u); + EXPECT_STREQ("yy", ldd->string); + ldd = LDDAtIndex(string_file.string(), 12); + EXPECT_EQ(ldd->location_descriptor.DataSize, 10u); + EXPECT_EQ(ldd->location_descriptor.Rva, 12u); + EXPECT_STREQ("x", ldd->string); + parent.Verify(); + } + + { + SCOPED_TRACE("parent-child only"); + string_file.Reset(); + TestLocationDescriptorMinidumpWritable parent; + TestLocationDescriptorMinidumpWritable child; + parent.SetLocationDescriptor(&child); + parent.SetString("www"); + child.SetString("vv"); + parent.AddChild(&child); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), 23u); + const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0); + EXPECT_EQ(ldd->location_descriptor.DataSize, 11u); + EXPECT_EQ(ldd->location_descriptor.Rva, 12u); + EXPECT_STREQ("www", ldd->string); + ldd = LDDAtIndex(string_file.string(), 12); + EXPECT_EQ(ldd->location_descriptor.DataSize, 0u); + EXPECT_EQ(ldd->location_descriptor.Rva, 0u); + EXPECT_STREQ("vv", ldd->string); + parent.Verify(); + } + + { + SCOPED_TRACE("parent-child circular"); + string_file.Reset(); + TestLocationDescriptorMinidumpWritable parent; + TestLocationDescriptorMinidumpWritable child; + parent.SetLocationDescriptor(&child); + parent.SetString("uuuu"); + child.SetLocationDescriptor(&parent); + child.SetString("tttt"); + parent.AddChild(&child); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), 29u); + const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0); + EXPECT_EQ(ldd->location_descriptor.DataSize, 13u); + EXPECT_EQ(ldd->location_descriptor.Rva, 16u); + EXPECT_STREQ("uuuu", ldd->string); + ldd = LDDAtIndex(string_file.string(), 16); + EXPECT_EQ(ldd->location_descriptor.DataSize, 13u); + EXPECT_EQ(ldd->location_descriptor.Rva, 0u); + EXPECT_STREQ("tttt", ldd->string); + parent.Verify(); + } + + { + SCOPED_TRACE("grandchildren"); + string_file.Reset(); + TestLocationDescriptorMinidumpWritable parent; + TestLocationDescriptorMinidumpWritable child; + parent.SetLocationDescriptor(&child); + parent.SetString("s"); + parent.AddChild(&child); + child.SetString("r"); + TestLocationDescriptorMinidumpWritable grandchild_0; + grandchild_0.SetLocationDescriptor(&child); + grandchild_0.SetString("q"); + child.AddChild(&grandchild_0); + TestLocationDescriptorMinidumpWritable grandchild_1; + grandchild_1.SetLocationDescriptor(&child); + grandchild_1.SetString("p"); + child.AddChild(&grandchild_1); + TestLocationDescriptorMinidumpWritable grandchild_2; + grandchild_2.SetLocationDescriptor(&child); + grandchild_2.SetString("o"); + child.AddChild(&grandchild_2); + EXPECT_TRUE(parent.WriteEverything(&string_file)); + + ASSERT_EQ(string_file.string().size(), 58u); + const LocationDescriptorAndData* ldd = LDDAtIndex(string_file.string(), 0); + EXPECT_EQ(ldd->location_descriptor.DataSize, 10u); + EXPECT_EQ(ldd->location_descriptor.Rva, 12u); + EXPECT_STREQ("s", ldd->string); + ldd = LDDAtIndex(string_file.string(), 12); + EXPECT_EQ(ldd->location_descriptor.DataSize, 0u); + EXPECT_EQ(ldd->location_descriptor.Rva, 0u); + EXPECT_STREQ("r", ldd->string); + ldd = LDDAtIndex(string_file.string(), 24); + EXPECT_EQ(ldd->location_descriptor.DataSize, 10u); + EXPECT_EQ(ldd->location_descriptor.Rva, 12u); + EXPECT_STREQ("q", ldd->string); + ldd = LDDAtIndex(string_file.string(), 36); + EXPECT_EQ(ldd->location_descriptor.DataSize, 10u); + EXPECT_EQ(ldd->location_descriptor.Rva, 12u); + EXPECT_STREQ("p", ldd->string); + ldd = LDDAtIndex(string_file.string(), 48); + EXPECT_EQ(ldd->location_descriptor.DataSize, 10u); + EXPECT_EQ(ldd->location_descriptor.Rva, 12u); + EXPECT_STREQ("o", ldd->string); + parent.Verify(); + } +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_writer_util.cc b/shared/sentry/external/crashpad/minidump/minidump_writer_util.cc new file mode 100644 index 000000000..00f4efa43 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_writer_util.cc @@ -0,0 +1,61 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/minidump_writer_util.h" + +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "util/stdlib/strlcpy.h" + +namespace crashpad { +namespace internal { + +// static +void MinidumpWriterUtil::AssignTimeT(uint32_t* destination, time_t source) { + if (!base::IsValueInRangeForNumericType(source)) { + LOG(WARNING) << "timestamp " << source << " out of range"; + } + + *destination = static_cast(source); +} + +// static +std::u16string MinidumpWriterUtil::ConvertUTF8ToUTF16(const std::string& utf8) { + std::u16string utf16; + if (!base::UTF8ToUTF16(utf8.data(), utf8.length(), &utf16)) { + LOG(WARNING) << "string " << utf8 + << " cannot be converted to UTF-16 losslessly"; + } + return utf16; +} + +// static +void MinidumpWriterUtil::AssignUTF8ToUTF16(char16_t* destination, + size_t destination_size, + const std::string& source) { + std::u16string source_utf16 = ConvertUTF8ToUTF16(source); + if (source_utf16.size() > destination_size - 1) { + LOG(WARNING) << "string " << source << " UTF-16 length " + << source_utf16.size() + << " will be truncated to UTF-16 length " + << destination_size - 1; + } + + source_utf16.resize(destination_size - 1); + c16lcpy(destination, source_utf16.c_str(), destination_size); +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/minidump_writer_util.h b/shared/sentry/external/crashpad/minidump/minidump_writer_util.h new file mode 100644 index 000000000..6bb7a5c60 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/minidump_writer_util.h @@ -0,0 +1,89 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_MINIDUMP_WRITER_UTIL_H_ +#define CRASHPAD_MINIDUMP_MINIDUMP_WRITER_UTIL_H_ + +#include +#include +#include + +#include + + +namespace crashpad { +namespace internal { + +//! \brief A collection of utility functions used by the MinidumpWritable family +//! of classes. +class MinidumpWriterUtil final { + public: + MinidumpWriterUtil() = delete; + MinidumpWriterUtil(const MinidumpWriterUtil&) = delete; + MinidumpWriterUtil& operator=(const MinidumpWriterUtil&) = delete; + + //! \brief Assigns a `time_t` value, logging a warning if the result overflows + //! the destination buffer and will be truncated. + //! + //! \param[out] destination A pointer to the variable to be assigned to. + //! \param[in] source The value to assign. + //! + //! The minidump format uses `uint32_t` for many timestamp values, but + //! `time_t` may be wider than this. These year 2038 bugs are a limitation of + //! the minidump format. An out-of-range error will be noted with a warning, + //! but is not considered fatal. \a source will be truncated and assigned to + //! \a destination in this case. + //! + //! For `time_t` values with nonfatal overflow semantics, this function is + //! used in preference to AssignIfInRange(), which fails without performing an + //! assignment when an out-of-range condition is detected. + static void AssignTimeT(uint32_t* destination, time_t source); + + //! \brief Converts a UTF-8 string to UTF-16 and returns it. If the string + //! cannot be converted losslessly, indicating that the input is not + //! well-formed UTF-8, a warning is logged. + //! + //! \param[in] utf8 The UTF-8-encoded string to convert. + //! + //! \return The \a utf8 string, converted to UTF-16 encoding. If the + //! conversion is lossy, U+FFFD “replacement characters†will be + //! introduced. + static std::u16string ConvertUTF8ToUTF16(const std::string& utf8); + + //! \brief Converts a UTF-8 string to UTF-16 and places it into a buffer of + //! fixed size, taking care to `NUL`-terminate the buffer and not to + //! overflow it. If the string will be truncated or if it cannot be + //! converted losslessly, a warning is logged. + //! + //! Any unused portion of the \a destination buffer that is not written to by + //! the converted string will be overwritten with `NUL` UTF-16 code units, + //! thus, this function always writes \a destination_size `char16_t` units. + //! + //! If the conversion is lossy, U+FFFD “replacement characters†will be + //! introduced. + //! + //! \param[out] destination A pointer to the destination buffer, where the + //! UTF-16-encoded string will be written. + //! \param[in] destination_size The size of \a destination in `char16_t` + //! units, including space used by a `NUL` terminator. + //! \param[in] source The UTF-8-encoded input string. + static void AssignUTF8ToUTF16(char16_t* destination, + size_t destination_size, + const std::string& source); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_MINIDUMP_WRITER_UTIL_H_ diff --git a/shared/sentry/external/crashpad/minidump/test/minidump_byte_array_writer_test_util.cc b/shared/sentry/external/crashpad/minidump/test/minidump_byte_array_writer_test_util.cc new file mode 100644 index 000000000..01e9d0a6e --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/test/minidump_byte_array_writer_test_util.cc @@ -0,0 +1,36 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/test/minidump_byte_array_writer_test_util.h" + +#include "minidump/test/minidump_writable_test_util.h" + +namespace crashpad { +namespace test { + +std::vector MinidumpByteArrayAtRVA(const std::string& file_contents, + RVA rva) { + auto* minidump_byte_array = + MinidumpWritableAtRVA(file_contents, rva); + if (!minidump_byte_array) { + return {}; + } + auto* data = static_cast(minidump_byte_array->data); + const uint8_t* data_end = data + minidump_byte_array->length; + return std::vector(data, data_end); +} + + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/test/minidump_byte_array_writer_test_util.h b/shared/sentry/external/crashpad/minidump/test/minidump_byte_array_writer_test_util.h new file mode 100644 index 000000000..d2c926b28 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/test/minidump_byte_array_writer_test_util.h @@ -0,0 +1,43 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef MINIDUMP_TEST_MINIDUMP_BYTE_ARRAY_WRITER_TEST_UTIL_H_ +#define MINIDUMP_TEST_MINIDUMP_BYTE_ARRAY_WRITER_TEST_UTIL_H_ + +#include +#include +#include + +#include +#include + +namespace crashpad { +namespace test { + +//! \brief Returns the bytes referenced by a MinidumpByteArray object located +//! in a minidump file at the specified RVA. +//! +//! \param[in] file_contents The contents of the minidump file. +//! \param[in] rva The offset in the minidump file of the MinidumpByteArray. +//! +//! \return The MinidumpByteArray::data referenced by the \a rva. Note that +//! this function does not check that the data are within the bounds of +//! the \a file_contents. +std::vector MinidumpByteArrayAtRVA(const std::string& file_contents, + RVA rva); + +} // namespace test +} // namespace crashpad + +#endif // MINIDUMP_TEST_MINIDUMP_BYTE_ARRAY_WRITER_TEST_UTIL_H_ diff --git a/shared/sentry/external/crashpad/minidump/test/minidump_context_test_util.cc b/shared/sentry/external/crashpad/minidump/test/minidump_context_test_util.cc new file mode 100644 index 000000000..3836640c0 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/test/minidump_context_test_util.cc @@ -0,0 +1,597 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/test/minidump_context_test_util.h" + +#include +#include + +#include + +#include "base/format_macros.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" +#include "snapshot/cpu_context.h" +#include "snapshot/test/test_cpu_context.h" +#include "test/hex_string.h" + +namespace crashpad { +namespace test { + +void InitializeMinidumpContextX86(MinidumpContextX86* context, uint32_t seed) { + if (seed == 0) { + memset(context, 0, sizeof(*context)); + context->context_flags = kMinidumpContextX86; + return; + } + + context->context_flags = kMinidumpContextX86All; + + uint32_t value = seed; + + context->eax = value++; + context->ebx = value++; + context->ecx = value++; + context->edx = value++; + context->edi = value++; + context->esi = value++; + context->ebp = value++; + context->esp = value++; + context->eip = value++; + context->eflags = value++; + context->cs = value++ & 0xffff; + context->ds = value++ & 0xffff; + context->es = value++ & 0xffff; + context->fs = value++ & 0xffff; + context->gs = value++ & 0xffff; + context->ss = value++ & 0xffff; + + InitializeCPUContextX86Fxsave(&context->fxsave, &value); + CPUContextX86::FxsaveToFsave(context->fxsave, &context->fsave); + + context->dr0 = value++; + context->dr1 = value++; + context->dr2 = value++; + context->dr3 = value++; + value += 2; // Minidumps don’t carry dr4 or dr5. + context->dr6 = value++; + context->dr7 = value++; + + // Set this field last, because it has no analogue in CPUContextX86. + context->float_save.spare_0 = value++; +} + +void InitializeMinidumpContextAMD64(MinidumpContextAMD64* context, + uint32_t seed) { + if (seed == 0) { + memset(context, 0, sizeof(*context)); + context->context_flags = kMinidumpContextAMD64; + return; + } + + context->context_flags = kMinidumpContextAMD64All; + + uint32_t value = seed; + + context->rax = value++; + context->rbx = value++; + context->rcx = value++; + context->rdx = value++; + context->rdi = value++; + context->rsi = value++; + context->rbp = value++; + context->rsp = value++; + context->r8 = value++; + context->r9 = value++; + context->r10 = value++; + context->r11 = value++; + context->r12 = value++; + context->r13 = value++; + context->r14 = value++; + context->r15 = value++; + context->rip = value++; + context->eflags = value++; + context->cs = static_cast(value++); + context->fs = static_cast(value++); + context->gs = static_cast(value++); + + InitializeCPUContextX86_64Fxsave(&context->fxsave, &value); + + // mxcsr appears twice, and the two values should be aliased. + context->mx_csr = context->fxsave.mxcsr; + + context->dr0 = value++; + context->dr1 = value++; + context->dr2 = value++; + context->dr3 = value++; + value += 2; // Minidumps don’t carry dr4 or dr5. + context->dr6 = value++; + context->dr7 = value++; + + // Set these fields last, because they have no analogues in CPUContextX86_64. + context->p1_home = value++; + context->p2_home = value++; + context->p3_home = value++; + context->p4_home = value++; + context->p5_home = value++; + context->p6_home = value++; + context->ds = static_cast(value++); + context->es = static_cast(value++); + context->ss = static_cast(value++); + for (size_t index = 0; index < std::size(context->vector_register); ++index) { + context->vector_register[index].lo = value++; + context->vector_register[index].hi = value++; + } + context->vector_control = value++; + context->debug_control = value++; + context->last_branch_to_rip = value++; + context->last_branch_from_rip = value++; + context->last_exception_to_rip = value++; + context->last_exception_from_rip = value++; +} + +void InitializeMinidumpContextARM(MinidumpContextARM* context, uint32_t seed) { + if (seed == 0) { + memset(context, 0, sizeof(*context)); + context->context_flags = kMinidumpContextARM; + return; + } + + context->context_flags = kMinidumpContextARMAll; + + uint32_t value = seed; + + for (size_t index = 0; index < std::size(context->regs); ++index) { + context->regs[index] = value++; + } + context->fp = value++; + context->ip = value++; + context->ip = value++; + context->sp = value++; + context->lr = value++; + context->pc = value++; + context->cpsr = value++; + + for (size_t index = 0; index < std::size(context->vfp); ++index) { + context->vfp[index] = value++; + } + context->fpscr = value++; +} + +void InitializeMinidumpContextARM64(MinidumpContextARM64* context, + uint32_t seed) { + if (seed == 0) { + memset(context, 0, sizeof(*context)); + context->context_flags = kMinidumpContextARM64; + return; + } + + context->context_flags = kMinidumpContextARM64Full; + + uint32_t value = seed; + + for (size_t index = 0; index < std::size(context->regs); ++index) { + context->regs[index] = value++; + } + context->fp = value++; + context->lr = value++; + context->sp = value++; + context->pc = value++; + context->cpsr = value++; + + for (size_t index = 0; index < std::size(context->fpsimd); ++index) { + context->fpsimd[index].lo = value++; + context->fpsimd[index].hi = value++; + } + context->fpsr = value++; + context->fpcr = value++; +} + +void InitializeMinidumpContextMIPS(MinidumpContextMIPS* context, + uint32_t seed) { + if (seed == 0) { + memset(context, 0, sizeof(*context)); + context->context_flags = kMinidumpContextMIPS; + return; + } + + context->context_flags = kMinidumpContextMIPSAll; + + uint32_t value = seed; + + for (size_t index = 0; index < std::size(context->regs); ++index) { + context->regs[index] = value++; + } + + context->mdlo = value++; + context->mdhi = value++; + context->epc = value++; + context->badvaddr = value++; + context->status = value++; + context->cause = value++; + + for (size_t index = 0; index < std::size(context->fpregs.fregs); ++index) { + context->fpregs.fregs[index]._fp_fregs = static_cast(value++); + } + + context->fpcsr = value++; + context->fir = value++; + + for (size_t index = 0; index < 3; ++index) { + context->hi[index] = value++; + context->lo[index] = value++; + } + + context->dsp_control = value++; +} + +void InitializeMinidumpContextMIPS64(MinidumpContextMIPS64* context, + uint32_t seed) { + if (seed == 0) { + memset(context, 0, sizeof(*context)); + context->context_flags = kMinidumpContextMIPS64; + return; + } + + context->context_flags = kMinidumpContextMIPS64All; + + uint64_t value = seed; + + for (size_t index = 0; index < std::size(context->regs); ++index) { + context->regs[index] = value++; + } + + context->mdlo = value++; + context->mdhi = value++; + context->epc = value++; + context->badvaddr = value++; + context->status = value++; + context->cause = value++; + + for (size_t index = 0; index < std::size(context->fpregs.dregs); ++index) { + context->fpregs.dregs[index] = static_cast(value++); + } + context->fpcsr = value++; + context->fir = value++; + + for (size_t index = 0; index < 3; ++index) { + context->hi[index] = value++; + context->lo[index] = value++; + } + context->dsp_control = value++; +} + +namespace { + +// Using Google Test assertions, compares |expected| to |observed|. This is +// templatized because the CPUContextX86::Fxsave and CPUContextX86_64::Fxsave +// are nearly identical but have different sizes for the members |xmm|, +// |reserved_4|, and |available|. +template +void ExpectMinidumpContextFxsave(const FxsaveType* expected, + const FxsaveType* observed) { + EXPECT_EQ(observed->fcw, expected->fcw); + EXPECT_EQ(observed->fsw, expected->fsw); + EXPECT_EQ(observed->ftw, expected->ftw); + EXPECT_EQ(observed->reserved_1, expected->reserved_1); + EXPECT_EQ(observed->fop, expected->fop); + EXPECT_EQ(observed->fpu_ip, expected->fpu_ip); + EXPECT_EQ(observed->fpu_cs, expected->fpu_cs); + EXPECT_EQ(observed->reserved_2, expected->reserved_2); + EXPECT_EQ(observed->fpu_dp, expected->fpu_dp); + EXPECT_EQ(observed->fpu_ds, expected->fpu_ds); + EXPECT_EQ(observed->reserved_3, expected->reserved_3); + EXPECT_EQ(observed->mxcsr, expected->mxcsr); + EXPECT_EQ(observed->mxcsr_mask, expected->mxcsr_mask); + for (size_t st_mm_index = 0; st_mm_index < std::size(expected->st_mm); + ++st_mm_index) { + SCOPED_TRACE(base::StringPrintf("st_mm_index %" PRIuS, st_mm_index)); + EXPECT_EQ(BytesToHexString(observed->st_mm[st_mm_index].st, + std::size(observed->st_mm[st_mm_index].st)), + BytesToHexString(expected->st_mm[st_mm_index].st, + std::size(expected->st_mm[st_mm_index].st))); + EXPECT_EQ( + BytesToHexString(observed->st_mm[st_mm_index].st_reserved, + std::size(observed->st_mm[st_mm_index].st_reserved)), + BytesToHexString(expected->st_mm[st_mm_index].st_reserved, + std::size(expected->st_mm[st_mm_index].st_reserved))); + } + for (size_t xmm_index = 0; xmm_index < std::size(expected->xmm); + ++xmm_index) { + EXPECT_EQ(BytesToHexString(observed->xmm[xmm_index], + std::size(observed->xmm[xmm_index])), + BytesToHexString(expected->xmm[xmm_index], + std::size(expected->xmm[xmm_index]))) + << "xmm_index " << xmm_index; + } + EXPECT_EQ( + BytesToHexString(observed->reserved_4, std::size(observed->reserved_4)), + BytesToHexString(expected->reserved_4, std::size(expected->reserved_4))); + EXPECT_EQ( + BytesToHexString(observed->available, std::size(observed->available)), + BytesToHexString(expected->available, std::size(expected->available))); +} + +} // namespace + +void ExpectMinidumpContextX86( + uint32_t expect_seed, const MinidumpContextX86* observed, bool snapshot) { + MinidumpContextX86 expected; + InitializeMinidumpContextX86(&expected, expect_seed); + + EXPECT_EQ(observed->context_flags, expected.context_flags); + EXPECT_EQ(observed->dr0, expected.dr0); + EXPECT_EQ(observed->dr1, expected.dr1); + EXPECT_EQ(observed->dr2, expected.dr2); + EXPECT_EQ(observed->dr3, expected.dr3); + EXPECT_EQ(observed->dr6, expected.dr6); + EXPECT_EQ(observed->dr7, expected.dr7); + + EXPECT_EQ(observed->fsave.fcw, expected.fsave.fcw); + EXPECT_EQ(observed->fsave.fsw, expected.fsave.fsw); + EXPECT_EQ(observed->fsave.ftw, expected.fsave.ftw); + EXPECT_EQ(observed->fsave.fpu_ip, expected.fsave.fpu_ip); + EXPECT_EQ(observed->fsave.fpu_cs, expected.fsave.fpu_cs); + EXPECT_EQ(observed->fsave.fpu_dp, expected.fsave.fpu_dp); + EXPECT_EQ(observed->fsave.fpu_ds, expected.fsave.fpu_ds); + for (size_t index = 0; index < std::size(expected.fsave.st); ++index) { + EXPECT_EQ(BytesToHexString(observed->fsave.st[index], + std::size(observed->fsave.st[index])), + BytesToHexString(expected.fsave.st[index], + std::size(expected.fsave.st[index]))) + << "index " << index; + } + if (snapshot) { + EXPECT_EQ(observed->float_save.spare_0, 0u); + } else { + EXPECT_EQ(observed->float_save.spare_0, expected.float_save.spare_0); + } + + EXPECT_EQ(observed->gs, expected.gs); + EXPECT_EQ(observed->fs, expected.fs); + EXPECT_EQ(observed->es, expected.es); + EXPECT_EQ(observed->ds, expected.ds); + EXPECT_EQ(observed->edi, expected.edi); + EXPECT_EQ(observed->esi, expected.esi); + EXPECT_EQ(observed->ebx, expected.ebx); + EXPECT_EQ(observed->edx, expected.edx); + EXPECT_EQ(observed->ecx, expected.ecx); + EXPECT_EQ(observed->eax, expected.eax); + EXPECT_EQ(observed->ebp, expected.ebp); + EXPECT_EQ(observed->eip, expected.eip); + EXPECT_EQ(observed->cs, expected.cs); + EXPECT_EQ(observed->eflags, expected.eflags); + EXPECT_EQ(observed->esp, expected.esp); + EXPECT_EQ(observed->ss, expected.ss); + + ExpectMinidumpContextFxsave(&expected.fxsave, &observed->fxsave); +} + +void ExpectMinidumpContextAMD64( + uint32_t expect_seed, const MinidumpContextAMD64* observed, bool snapshot) { + MinidumpContextAMD64 expected; + InitializeMinidumpContextAMD64(&expected, expect_seed); + + EXPECT_EQ(observed->context_flags, expected.context_flags); + + if (snapshot) { + EXPECT_EQ(observed->p1_home, 0u); + EXPECT_EQ(observed->p2_home, 0u); + EXPECT_EQ(observed->p3_home, 0u); + EXPECT_EQ(observed->p4_home, 0u); + EXPECT_EQ(observed->p5_home, 0u); + EXPECT_EQ(observed->p6_home, 0u); + } else { + EXPECT_EQ(observed->p1_home, expected.p1_home); + EXPECT_EQ(observed->p2_home, expected.p2_home); + EXPECT_EQ(observed->p3_home, expected.p3_home); + EXPECT_EQ(observed->p4_home, expected.p4_home); + EXPECT_EQ(observed->p5_home, expected.p5_home); + EXPECT_EQ(observed->p6_home, expected.p6_home); + } + + EXPECT_EQ(observed->mx_csr, expected.mx_csr); + + EXPECT_EQ(observed->cs, expected.cs); + if (snapshot) { + EXPECT_EQ(observed->ds, 0u); + EXPECT_EQ(observed->es, 0u); + } else { + EXPECT_EQ(observed->ds, expected.ds); + EXPECT_EQ(observed->es, expected.es); + } + EXPECT_EQ(observed->fs, expected.fs); + EXPECT_EQ(observed->gs, expected.gs); + if (snapshot) { + EXPECT_EQ(observed->ss, 0u); + } else { + EXPECT_EQ(observed->ss, expected.ss); + } + + EXPECT_EQ(observed->eflags, expected.eflags); + + EXPECT_EQ(observed->dr0, expected.dr0); + EXPECT_EQ(observed->dr1, expected.dr1); + EXPECT_EQ(observed->dr2, expected.dr2); + EXPECT_EQ(observed->dr3, expected.dr3); + EXPECT_EQ(observed->dr6, expected.dr6); + EXPECT_EQ(observed->dr7, expected.dr7); + + EXPECT_EQ(observed->rax, expected.rax); + EXPECT_EQ(observed->rcx, expected.rcx); + EXPECT_EQ(observed->rdx, expected.rdx); + EXPECT_EQ(observed->rbx, expected.rbx); + EXPECT_EQ(observed->rsp, expected.rsp); + EXPECT_EQ(observed->rbp, expected.rbp); + EXPECT_EQ(observed->rsi, expected.rsi); + EXPECT_EQ(observed->rdi, expected.rdi); + EXPECT_EQ(observed->r8, expected.r8); + EXPECT_EQ(observed->r9, expected.r9); + EXPECT_EQ(observed->r10, expected.r10); + EXPECT_EQ(observed->r11, expected.r11); + EXPECT_EQ(observed->r12, expected.r12); + EXPECT_EQ(observed->r13, expected.r13); + EXPECT_EQ(observed->r14, expected.r14); + EXPECT_EQ(observed->r15, expected.r15); + EXPECT_EQ(observed->rip, expected.rip); + + ExpectMinidumpContextFxsave(&expected.fxsave, &observed->fxsave); + + for (size_t index = 0; index < std::size(expected.vector_register); ++index) { + if (snapshot) { + EXPECT_EQ(observed->vector_register[index].lo, 0u) << "index " << index; + EXPECT_EQ(observed->vector_register[index].hi, 0u) << "index " << index; + } else { + EXPECT_EQ(observed->vector_register[index].lo, + expected.vector_register[index].lo) + << "index " << index; + EXPECT_EQ(observed->vector_register[index].hi, + expected.vector_register[index].hi) + << "index " << index; + } + } + + if (snapshot) { + EXPECT_EQ(observed->vector_control, 0u); + EXPECT_EQ(observed->debug_control, 0u); + EXPECT_EQ(observed->last_branch_to_rip, 0u); + EXPECT_EQ(observed->last_branch_from_rip, 0u); + EXPECT_EQ(observed->last_exception_to_rip, 0u); + EXPECT_EQ(observed->last_exception_from_rip, 0u); + } else { + EXPECT_EQ(observed->vector_control, expected.vector_control); + EXPECT_EQ(observed->debug_control, expected.debug_control); + EXPECT_EQ(observed->last_branch_to_rip, expected.last_branch_to_rip); + EXPECT_EQ(observed->last_branch_from_rip, expected.last_branch_from_rip); + EXPECT_EQ(observed->last_exception_to_rip, expected.last_exception_to_rip); + EXPECT_EQ(observed->last_exception_from_rip, + expected.last_exception_from_rip); + } +} + +void ExpectMinidumpContextARM(uint32_t expect_seed, + const MinidumpContextARM* observed, + bool snapshot) { + MinidumpContextARM expected; + InitializeMinidumpContextARM(&expected, expect_seed); + + EXPECT_EQ(observed->context_flags, expected.context_flags); + + for (size_t index = 0; index < std::size(expected.regs); ++index) { + EXPECT_EQ(observed->regs[index], expected.regs[index]); + } + EXPECT_EQ(observed->fp, expected.fp); + EXPECT_EQ(observed->ip, expected.ip); + EXPECT_EQ(observed->sp, expected.sp); + EXPECT_EQ(observed->lr, expected.lr); + EXPECT_EQ(observed->pc, expected.pc); + EXPECT_EQ(observed->cpsr, expected.cpsr); + + EXPECT_EQ(observed->fpscr, expected.fpscr); + for (size_t index = 0; index < std::size(expected.vfp); ++index) { + EXPECT_EQ(observed->vfp[index], expected.vfp[index]); + } + for (size_t index = 0; index < std::size(expected.extra); ++index) { + EXPECT_EQ(observed->extra[index], snapshot ? 0 : expected.extra[index]); + } +} + +void ExpectMinidumpContextARM64(uint32_t expect_seed, + const MinidumpContextARM64* observed, + bool snapshot) { + MinidumpContextARM64 expected; + InitializeMinidumpContextARM64(&expected, expect_seed); + + EXPECT_EQ(observed->context_flags, expected.context_flags); + + for (size_t index = 0; index < std::size(expected.regs); ++index) { + EXPECT_EQ(observed->regs[index], expected.regs[index]); + } + EXPECT_EQ(observed->cpsr, expected.cpsr); + + EXPECT_EQ(observed->fpsr, expected.fpsr); + EXPECT_EQ(observed->fpcr, expected.fpcr); + for (size_t index = 0; index < std::size(expected.fpsimd); ++index) { + EXPECT_EQ(observed->fpsimd[index].lo, expected.fpsimd[index].lo); + EXPECT_EQ(observed->fpsimd[index].hi, expected.fpsimd[index].hi); + } +} + +void ExpectMinidumpContextMIPS(uint32_t expect_seed, + const MinidumpContextMIPS* observed, + bool snapshot) { + MinidumpContextMIPS expected; + InitializeMinidumpContextMIPS(&expected, expect_seed); + + EXPECT_EQ(observed->context_flags, expected.context_flags); + + for (size_t index = 0; index < std::size(expected.regs); ++index) { + EXPECT_EQ(observed->regs[index], expected.regs[index]); + } + + EXPECT_EQ(observed->mdlo, expected.mdlo); + EXPECT_EQ(observed->mdhi, expected.mdhi); + EXPECT_EQ(observed->epc, expected.epc); + EXPECT_EQ(observed->badvaddr, expected.badvaddr); + EXPECT_EQ(observed->status, expected.status); + EXPECT_EQ(observed->cause, expected.cause); + + for (size_t index = 0; index < std::size(expected.fpregs.fregs); ++index) { + EXPECT_EQ(observed->fpregs.fregs[index]._fp_fregs, + expected.fpregs.fregs[index]._fp_fregs); + } + EXPECT_EQ(observed->fpcsr, expected.fpcsr); + EXPECT_EQ(observed->fir, expected.fir); + + for (size_t index = 0; index < 3; ++index) { + EXPECT_EQ(observed->hi[index], expected.hi[index]); + EXPECT_EQ(observed->lo[index], expected.lo[index]); + } + EXPECT_EQ(observed->dsp_control, expected.dsp_control); +} + +void ExpectMinidumpContextMIPS64(uint32_t expect_seed, + const MinidumpContextMIPS64* observed, + bool snapshot) { + MinidumpContextMIPS64 expected; + InitializeMinidumpContextMIPS64(&expected, expect_seed); + + EXPECT_EQ(observed->context_flags, expected.context_flags); + + for (size_t index = 0; index < std::size(expected.regs); ++index) { + EXPECT_EQ(observed->regs[index], expected.regs[index]); + } + + EXPECT_EQ(observed->mdlo, expected.mdlo); + EXPECT_EQ(observed->mdhi, expected.mdhi); + EXPECT_EQ(observed->epc, expected.epc); + EXPECT_EQ(observed->badvaddr, expected.badvaddr); + EXPECT_EQ(observed->status, expected.status); + EXPECT_EQ(observed->cause, expected.cause); + + for (size_t index = 0; index < std::size(expected.fpregs.dregs); ++index) { + EXPECT_EQ(observed->fpregs.dregs[index], expected.fpregs.dregs[index]); + } + EXPECT_EQ(observed->fpcsr, expected.fpcsr); + EXPECT_EQ(observed->fir, expected.fir); + + for (size_t index = 0; index < 3; ++index) { + EXPECT_EQ(observed->hi[index], expected.hi[index]); + EXPECT_EQ(observed->lo[index], expected.lo[index]); + } + EXPECT_EQ(observed->dsp_control, expected.dsp_control); +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/test/minidump_context_test_util.h b/shared/sentry/external/crashpad/minidump/test/minidump_context_test_util.h new file mode 100644 index 000000000..0c633decb --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/test/minidump_context_test_util.h @@ -0,0 +1,93 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_TEST_MINIDUMP_CONTEXT_TEST_UTIL_H_ +#define CRASHPAD_MINIDUMP_TEST_MINIDUMP_CONTEXT_TEST_UTIL_H_ + +#include + +#include "minidump/minidump_context.h" + +namespace crashpad { +namespace test { + +//! \brief Initializes a context structure for testing. +//! +//! Initialization is compatible with the initialization used by CPUContext test +//! initialization functions such as InitializeCPUContextX86() and +//! InitializeCPUContextX86_64() for identical \a seed values. +//! +//! \param[out] context The structure to initialize. +//! \param[in] seed The seed value. Initializing two context structures of the +//! same type with identical seed values should produce identical context +//! structures. Initialization with a different seed value should produce +//! a different context structure. If \a seed is `0`, \a context is zeroed +//! out entirely except for the flags field, which will identify the context +//! type. If \a seed is nonzero, \a context will be populated entirely with +//! nonzero values. +//! +//! \{ +void InitializeMinidumpContextX86(MinidumpContextX86* context, uint32_t seed); +void InitializeMinidumpContextAMD64(MinidumpContextAMD64* context, + uint32_t seed); +void InitializeMinidumpContextARM(MinidumpContextARM* context, uint32_t seed); +void InitializeMinidumpContextARM64(MinidumpContextARM64* context, + uint32_t seed); +void InitializeMinidumpContextMIPS(MinidumpContextMIPS* context, uint32_t seed); +void InitializeMinidumpContextMIPS64(MinidumpContextMIPS* context, + uint32_t seed); +//! \} + +//! \brief Verifies, via Google Test assertions, that a context structure +//! contains expected values. +//! +//! \param[in] expect_seed The seed value used to initialize a context +//! structure. This is the seed value used with +//! InitializeMinidumpContext*(). +//! \param[in] observed The context structure to check. All fields of this +//! structure will be compared against the expected context structure, one +//! initialized with \a expect_seed. +//! \param[in] snapshot If `true`, compare \a observed to a context structure +//! expected to be produced from a CPUContext snapshot. If `false`, compare +//! \a observed to a native minidump context structure. CPUContext snapshot +//! structures may carry different sets of data than native minidump context +//! structures in meaningless ways. When `true`, fields not found in +//! CPUContext structures are expected to be `0`. When `false`, all fields +//! are compared. This makes it possible to test both that these fields are +//! passed through correctly by the native minidump writer and are zeroed +//! out when creating a minidump context structure from a CPUContext +//! structure. +//! \{ +void ExpectMinidumpContextX86( + uint32_t expect_seed, const MinidumpContextX86* observed, bool snapshot); +void ExpectMinidumpContextAMD64( + uint32_t expect_seed, const MinidumpContextAMD64* observed, bool snapshot); +void ExpectMinidumpContextARM(uint32_t expect_seed, + const MinidumpContextARM* observed, + bool snapshot); +void ExpectMinidumpContextARM64(uint32_t expect_seed, + const MinidumpContextARM64* observed, + bool snapshot); +void ExpectMinidumpContextMIPS(uint32_t expect_seed, + const MinidumpContextMIPS* observed, + bool snapshot); +void ExpectMinidumpContextMIPS64(uint32_t expect_seed, + const MinidumpContextMIPS64* observed, + bool snapshot); +//! \} + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_TEST_MINIDUMP_CONTEXT_TEST_UTIL_H_ diff --git a/shared/sentry/external/crashpad/minidump/test/minidump_file_writer_test_util.cc b/shared/sentry/external/crashpad/minidump/test/minidump_file_writer_test_util.cc new file mode 100644 index 000000000..aae357dcf --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/test/minidump_file_writer_test_util.cc @@ -0,0 +1,59 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/test/minidump_file_writer_test_util.h" + +#include "gtest/gtest.h" +#include "minidump/test/minidump_writable_test_util.h" + +namespace crashpad { +namespace test { + +const MINIDUMP_HEADER* MinidumpHeaderAtStart( + const std::string& file_contents, + const MINIDUMP_DIRECTORY** directory) { + MINIDUMP_LOCATION_DESCRIPTOR location_descriptor; + location_descriptor.DataSize = sizeof(MINIDUMP_HEADER); + location_descriptor.Rva = 0; + + const MINIDUMP_HEADER* header = + MinidumpWritableAtLocationDescriptor( + file_contents, location_descriptor); + + if (header) { + location_descriptor.DataSize = + header->NumberOfStreams * sizeof(MINIDUMP_DIRECTORY); + location_descriptor.Rva = header->StreamDirectoryRva; + *directory = MinidumpWritableAtLocationDescriptor( + file_contents, location_descriptor); + } else { + *directory = nullptr; + } + + return header; +} + +void VerifyMinidumpHeader(const MINIDUMP_HEADER* header, + uint32_t streams, + uint32_t timestamp) { + ASSERT_TRUE(header); + ASSERT_EQ(header->NumberOfStreams, streams); + ASSERT_EQ(header->StreamDirectoryRva, streams ? sizeof(MINIDUMP_HEADER) : 0u); + EXPECT_EQ(header->CheckSum, 0u); + EXPECT_EQ(header->TimeDateStamp, timestamp); + EXPECT_EQ(static_cast(header->Flags), MiniDumpNormal); +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/test/minidump_file_writer_test_util.h b/shared/sentry/external/crashpad/minidump/test/minidump_file_writer_test_util.h new file mode 100644 index 000000000..4efe99e69 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/test/minidump_file_writer_test_util.h @@ -0,0 +1,65 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_TEST_MINIDUMP_FILE_WRITER_TEST_UTIL_H_ +#define CRASHPAD_MINIDUMP_TEST_MINIDUMP_FILE_WRITER_TEST_UTIL_H_ + +#include +#include +#include + +#include + +namespace crashpad { +namespace test { + +//! \brief Returns the MINIDUMP_HEADER at the start of a minidump file, along +//! with the MINIDUMP_DIRECTORY it references. +//! +//! This function validates the MINIDUMP_HEADER::Signature and +//! MINIDUMP_HEADER::Version fields. +//! +//! \param[in] file_contents The contents of the minidump file. +//! \param[out] directory The MINIDUMP_DIRECTORY referenced by the +//! MINIDUMP_HEADER. If the MINIDUMP_HEADER does not reference a +//! MINIDUMP_DIRECTORY, `nullptr` without raising a Google Test assertion. +//! If the referenced MINIDUMP_DIRECTORY is not valid, `nullptr` with a +//! Google Test assertion raised. On failure, `nullptr`. +//! +//! \return On success, the MINIDUMP_HEADER at the beginning of the minidump +//! file. On failure, raises a Google Test assertion and returns `nullptr`. +const MINIDUMP_HEADER* MinidumpHeaderAtStart( + const std::string& file_contents, + const MINIDUMP_DIRECTORY** directory); + +//! \brief Verifies, via Google Test assertions, that a MINIDUMP_HEADER contains +//! expected values. +//! +//! All fields in the MINIDUMP_HEADER will be evaluated except for the Signature +//! and Version fields, because those are checked by MinidumpHeaderAtStart(). +//! Most other fields are are compared to their correct default values. +//! MINIDUMP_HEADER::NumberOfStreams is compared to \a streams, and +//! MINIDUMP_HEADER::TimeDateStamp is compared to \a timestamp. Most fields are +//! checked with nonfatal EXPECT-style assertions, but +//! MINIDUMP_HEADER::NumberOfStreams and MINIDUMP_HEADER::StreamDirectoryRva are +//! checked with fatal ASSERT-style assertions, because they must be correct in +//! order for processing of the minidump to continue. +void VerifyMinidumpHeader(const MINIDUMP_HEADER* header, + uint32_t streams, + uint32_t timestamp); + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_TEST_MINIDUMP_FILE_WRITER_TEST_UTIL_H_ diff --git a/shared/sentry/external/crashpad/minidump/test/minidump_memory_writer_test_util.cc b/shared/sentry/external/crashpad/minidump/test/minidump_memory_writer_test_util.cc new file mode 100644 index 000000000..4e38cfa63 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/test/minidump_memory_writer_test_util.cc @@ -0,0 +1,82 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/test/minidump_memory_writer_test_util.h" + +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { + +TestMinidumpMemoryWriter::TestMinidumpMemoryWriter(uint64_t base_address, + size_t size, + uint8_t value) + : SnapshotMinidumpMemoryWriter(&test_snapshot_) { + test_snapshot_.SetAddress(base_address); + test_snapshot_.SetSize(size); + test_snapshot_.SetValue(value); +} + +TestMinidumpMemoryWriter::~TestMinidumpMemoryWriter() { +} + +void TestMinidumpMemoryWriter::SetShouldFailRead(bool should_fail) { + test_snapshot_.SetShouldFailRead(should_fail); +} + +void ExpectMinidumpMemoryDescriptor( + const MINIDUMP_MEMORY_DESCRIPTOR* expected, + const MINIDUMP_MEMORY_DESCRIPTOR* observed) { + MINIDUMP_MEMORY_DESCRIPTOR expected_descriptor; + MINIDUMP_MEMORY_DESCRIPTOR observed_descriptor; + + memcpy(&expected_descriptor, expected, sizeof(expected_descriptor)); + memcpy(&observed_descriptor, observed, sizeof(observed_descriptor)); + + EXPECT_EQ(observed_descriptor.StartOfMemoryRange, + expected_descriptor.StartOfMemoryRange); + EXPECT_EQ(observed_descriptor.Memory.DataSize, + expected_descriptor.Memory.DataSize); + if (expected_descriptor.Memory.Rva != 0) { + constexpr uint32_t kMemoryAlignment = 16; + EXPECT_EQ(observed_descriptor.Memory.Rva, + (expected_descriptor.Memory.Rva + kMemoryAlignment - 1) & + ~(kMemoryAlignment - 1)); + } +} + +void ExpectMinidumpMemoryDescriptorAndContents( + const MINIDUMP_MEMORY_DESCRIPTOR* expected, + const MINIDUMP_MEMORY_DESCRIPTOR* observed, + const std::string& file_contents, + uint8_t value, + bool at_eof) { + ExpectMinidumpMemoryDescriptor(expected, observed); + + if (at_eof) { + EXPECT_EQ(observed->Memory.Rva + observed->Memory.DataSize, + file_contents.size()); + } else { + EXPECT_GE(file_contents.size(), + observed->Memory.Rva + observed->Memory.DataSize); + } + + std::string expected_data(expected->Memory.DataSize, value); + std::string observed_data(&file_contents[observed->Memory.Rva], + observed->Memory.DataSize); + EXPECT_EQ(observed_data, expected_data); +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/test/minidump_memory_writer_test_util.h b/shared/sentry/external/crashpad/minidump/test/minidump_memory_writer_test_util.h new file mode 100644 index 000000000..83b31384e --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/test/minidump_memory_writer_test_util.h @@ -0,0 +1,98 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_TEST_MINIDUMP_MEMORY_WRITER_TEST_UTIL_H_ +#define CRASHPAD_MINIDUMP_TEST_MINIDUMP_MEMORY_WRITER_TEST_UTIL_H_ + +#include "minidump/minidump_memory_writer.h" + +#include +#include +#include +#include + +#include + +#include "snapshot/test/test_memory_snapshot.h" +#include "util/file/file_writer.h" + +namespace crashpad { +namespace test { + +//! \brief A SnapshotMinidumpMemoryWriter implementation used for testing. +//! +//! TestMinidumpMemoryWriter objects are created with a fixed base address and +//! size, and will write the same byte (\a value) repeatedly, \a size times. +class TestMinidumpMemoryWriter final : public SnapshotMinidumpMemoryWriter { + public: + TestMinidumpMemoryWriter(uint64_t base_address, size_t size, uint8_t value); + + TestMinidumpMemoryWriter(const TestMinidumpMemoryWriter&) = delete; + TestMinidumpMemoryWriter& operator=(const TestMinidumpMemoryWriter&) = delete; + + ~TestMinidumpMemoryWriter(); + + void SetShouldFailRead(bool should_fail); + + private: + TestMemorySnapshot test_snapshot_; +}; + +//! \brief Verifies, via Google Test assertions, that a +//! MINIDUMP_MEMORY_DESCRIPTOR structure contains expected values. +//! +//! In \a expected and \a observed, +//! MINIDUMP_MEMORY_DESCRIPTOR::StartOfMemoryRange and +//! MINIDUMP_LOCATION_DESCRIPTOR::DataSize are compared and must match. If +//! MINIDUMP_LOCATION_DESCRIPTOR::Rva is nonzero in \a expected, the same field +//! in \a observed must match it, subject to a 16-byte alignment augmentation. +//! +//! \param[in] expected A MINIDUMP_MEMORY_DESCRIPTOR structure containing +//! expected values. +//! \param[in] observed A MINIDUMP_MEMORY_DESCRIPTOR structure containing +//! observed values. +void ExpectMinidumpMemoryDescriptor(const MINIDUMP_MEMORY_DESCRIPTOR* expected, + const MINIDUMP_MEMORY_DESCRIPTOR* observed); + +//! \brief Verifies, via Google Test assertions, that a +//! MINIDUMP_MEMORY_DESCRIPTOR structure contains expected values, and that +//! the memory region it points to contains expected values assuming it was +//! written by a TestMinidumpMemoryWriter object. +//! +//! \a expected and \a observed are compared by +//! ExpectMinidumpMemoryDescriptor(). +//! +//! \param[in] expected A MINIDUMP_MEMORY_DESCRIPTOR structure containing +//! expected values. +//! \param[in] observed A MINIDUMP_MEMORY_DESCRIPTOR structure containing +//! observed values. +//! \param[in] file_contents The contents of the minidump file in which \a +//! observed was found. The memory region referenced by \a observed will be +//! read from this string. +//! \param[in] value The \a value used to create a TestMinidumpMemoryWriter. +//! Each byte of memory in the region referenced by \a observed must be this +//! value. +//! \param[in] at_eof If `true`, the region referenced by \a observed must +//! appear at the end of \a file_contents, without any data following it. +void ExpectMinidumpMemoryDescriptorAndContents( + const MINIDUMP_MEMORY_DESCRIPTOR* expected, + const MINIDUMP_MEMORY_DESCRIPTOR* observed, + const std::string& file_contents, + uint8_t value, + bool at_eof); + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_TEST_MINIDUMP_MEMORY_WRITER_TEST_UTIL_H_ diff --git a/shared/sentry/external/crashpad/minidump/test/minidump_rva_list_test_util.cc b/shared/sentry/external/crashpad/minidump/test/minidump_rva_list_test_util.cc new file mode 100644 index 000000000..248161c32 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/test/minidump_rva_list_test_util.cc @@ -0,0 +1,50 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/test/minidump_rva_list_test_util.h" + +#include +#include +#include + +#include "minidump/minidump_extensions.h" +#include "minidump/test/minidump_writable_test_util.h" + +namespace crashpad { +namespace test { + +const MinidumpRVAList* MinidumpRVAListAtStart(const std::string& file_contents, + size_t count) { + MINIDUMP_LOCATION_DESCRIPTOR location_descriptor; + location_descriptor.DataSize = + static_cast(sizeof(MinidumpRVAList) + count * sizeof(RVA)); + location_descriptor.Rva = 0; + + const MinidumpRVAList* list = + MinidumpWritableAtLocationDescriptor( + file_contents, location_descriptor); + if (!list) { + return nullptr; + } + + if (list->count != count) { + EXPECT_EQ(list->count, count); + return nullptr; + } + + return list; +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/test/minidump_rva_list_test_util.h b/shared/sentry/external/crashpad/minidump/test/minidump_rva_list_test_util.h new file mode 100644 index 000000000..6b5a2a92f --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/test/minidump_rva_list_test_util.h @@ -0,0 +1,44 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_TEST_MINIDUMP_RVA_LIST_TEST_UTIL_H_ +#define CRASHPAD_MINIDUMP_TEST_MINIDUMP_RVA_LIST_TEST_UTIL_H_ + +#include + +#include + +namespace crashpad { + +struct MinidumpRVAList; + +namespace test { + +//! \brief Returns the MinidumpRVAList at the start of a minidump file. +//! +//! \param[in] file_contents The contents of the minidump file. +//! \param[in] count The number of ::RVA objects expected in the +//! MinidumpRVAList. This function will only be successful if exactly this +//! many objects are present, and if space for them exists in \a +//! file_contents. +//! +//! \return On success, the MinidumpRVAList at the beginning of the file. On +//! failure, raises a Google Test assertion and returns `nullptr`. +const MinidumpRVAList* MinidumpRVAListAtStart(const std::string& file_contents, + size_t count); + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_TEST_MINIDUMP_RVA_LIST_TEST_UTIL_H_ diff --git a/shared/sentry/external/crashpad/minidump/test/minidump_string_writer_test_util.cc b/shared/sentry/external/crashpad/minidump/test/minidump_string_writer_test_util.cc new file mode 100644 index 000000000..a8eb5aa46 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/test/minidump_string_writer_test_util.cc @@ -0,0 +1,107 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/test/minidump_string_writer_test_util.h" + +#include + +#include "gtest/gtest.h" +#include "minidump/minidump_extensions.h" +#include "minidump/test/minidump_writable_test_util.h" + +namespace crashpad { +namespace test { + +namespace { + +template +const T* TMinidumpStringAtRVA(const std::string& file_contents, RVA rva) { + const T* string_base = MinidumpWritableAtRVA(file_contents, rva); + if (!string_base) { + return nullptr; + } + + // |Length| must indicate the ability to store an integral number of code + // units. + const size_t kCodeUnitSize = sizeof(string_base->Buffer[0]); + if (string_base->Length % kCodeUnitSize != 0) { + EXPECT_EQ(string_base->Length % kCodeUnitSize, 0u); + return nullptr; + } + + // |Length| does not include space for the required NUL terminator. + MINIDUMP_LOCATION_DESCRIPTOR location; + location.DataSize = + sizeof(*string_base) + string_base->Length + kCodeUnitSize; + location.Rva = rva; + const T* string = + MinidumpWritableAtLocationDescriptor(file_contents, location); + if (!string) { + return nullptr; + } + + EXPECT_EQ(string, string_base); + + // Require the NUL terminator to be NUL. + if (string->Buffer[string->Length / kCodeUnitSize] != '\0') { + EXPECT_EQ(string->Buffer[string->Length / kCodeUnitSize], '\0'); + return nullptr; + } + + return string; +} + +template +StringType TMinidumpStringAtRVAAsString(const std::string& file_contents, + RVA rva) { + const MinidumpStringType* minidump_string = + TMinidumpStringAtRVA(file_contents, rva); + if (!minidump_string) { + return StringType(); + } + + StringType minidump_string_data( + reinterpret_cast( + &minidump_string->Buffer[0]), + minidump_string->Length / sizeof(minidump_string->Buffer[0])); + return minidump_string_data; +} + +} // namespace + +const MINIDUMP_STRING* MinidumpStringAtRVA(const std::string& file_contents, + RVA rva) { + return TMinidumpStringAtRVA(file_contents, rva); +} + +const MinidumpUTF8String* MinidumpUTF8StringAtRVA( + const std::string& file_contents, + RVA rva) { + return TMinidumpStringAtRVA(file_contents, rva); +} + +std::u16string MinidumpStringAtRVAAsString(const std::string& file_contents, + RVA rva) { + return TMinidumpStringAtRVAAsString( + file_contents, rva); +} + +std::string MinidumpUTF8StringAtRVAAsString(const std::string& file_contents, + RVA rva) { + return TMinidumpStringAtRVAAsString( + file_contents, rva); +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/test/minidump_string_writer_test_util.h b/shared/sentry/external/crashpad/minidump/test/minidump_string_writer_test_util.h new file mode 100644 index 000000000..c92027a70 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/test/minidump_string_writer_test_util.h @@ -0,0 +1,102 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_TEST_MINIDUMP_STRING_WRITER_TEST_UTIL_H_ +#define CRASHPAD_MINIDUMP_TEST_MINIDUMP_STRING_WRITER_TEST_UTIL_H_ + +#include +#include + +#include + +namespace crashpad { + +struct MinidumpUTF8String; + +namespace test { + +//! \brief Returns a MINIDUMP_STRING located within a minidump file’s contents. +//! +//! If \a rva points outside of the range of \a file_contents, if the string has +//! an incorrect length or is not `NUL`-terminated, or if any of the string data +//! would lie outside of the range of \a file_contents, this function will fail. +//! +//! \param[in] file_contents The contents of the minidump file. +//! \param[in] rva The offset within the minidump file of the desired +//! MINIDUMP_STRING. +//! +//! \return On success, a pointer to the MINIDUMP_STRING in \a file_contents. On +//! failure, raises a Google Test assertion and returns `nullptr`. +//! +//! \sa MinidumpStringAtRVAAsString() +//! \sa MinidumpUTF8StringAtRVA() +const MINIDUMP_STRING* MinidumpStringAtRVA(const std::string& file_contents, + RVA rva); + +//! \brief Returns a MinidumpUTF8String located within a minidump file’s +//! contents. +//! +//! If \a rva points outside of the range of \a file_contents, if the string has +//! an incorrect length or is not `NUL`-terminated, or if any of the string data +//! would lie outside of the range of \a file_contents, this function will fail. +//! +//! \param[in] file_contents The contents of the minidump file. +//! \param[in] rva The offset within the minidump file of the desired +//! MinidumpUTF8String. +//! +//! \return On success, a pointer to the MinidumpUTF8String in \a file_contents. +//! On failure, raises a Google Test assertion and returns `nullptr`. +//! +//! \sa MinidumpUTF8StringAtRVAAsString() +//! \sa MinidumpStringAtRVA() +const MinidumpUTF8String* MinidumpUTF8StringAtRVA( + const std::string& file_contents, + RVA rva); + +//! \brief Returns the contents of a MINIDUMP_STRING as a `std::u16string`. +//! +//! This function uses MinidumpStringAtRVA() to obtain a MINIDUMP_STRING, and +//! returns the string data as a `std::u16string`. +//! +//! \param[in] file_contents The contents of the minidump file. +//! \param[in] rva The offset within the minidump file of the desired +//! MINIDUMP_STRING. +//! +//! \return On success, the string read from \a file_writer at offset \a rva. On +//! failure, raises a Google Test assertion and returns an empty string. +//! +//! \sa MinidumpUTF8StringAtRVAAsString() +std::u16string MinidumpStringAtRVAAsString(const std::string& file_contents, + RVA rva); + +//! \brief Returns the contents of a MinidumpUTF8String as a `std::string`. +//! +//! This function uses MinidumpUTF8StringAtRVA() to obtain a MinidumpUTF8String, +//! and returns the string data as a `std::string`. +//! +//! \param[in] file_contents The contents of the minidump file. +//! \param[in] rva The offset within the minidump file of the desired +//! MinidumpUTF8String. +//! +//! \return On success, the string read from \a file_writer at offset \a rva. On +//! failure, raises a Google Test assertion and returns an empty string. +//! +//! \sa MinidumpStringAtRVAAsString() +std::string MinidumpUTF8StringAtRVAAsString(const std::string& file_contents, + RVA rva); + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_TEST_MINIDUMP_STRING_WRITER_TEST_UTIL_H_ diff --git a/shared/sentry/external/crashpad/minidump/test/minidump_user_extension_stream_util.cc b/shared/sentry/external/crashpad/minidump/test/minidump_user_extension_stream_util.cc new file mode 100644 index 000000000..50100822c --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/test/minidump_user_extension_stream_util.cc @@ -0,0 +1,43 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/test/minidump_user_extension_stream_util.h" + +#include + +namespace crashpad { +namespace test { + +BufferExtensionStreamDataSource::BufferExtensionStreamDataSource( + uint32_t stream_type, + const void* data, + size_t data_size) + : MinidumpUserExtensionStreamDataSource(stream_type) { + data_.resize(data_size); + + if (data_size) + memcpy(data_.data(), data, data_size); +} + +size_t BufferExtensionStreamDataSource::StreamDataSize() { + return data_.size(); +} + +bool BufferExtensionStreamDataSource::ReadStreamData(Delegate* delegate) { + return delegate->ExtensionStreamDataSourceRead( + data_.size() ? data_.data() : nullptr, data_.size()); +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/test/minidump_user_extension_stream_util.h b/shared/sentry/external/crashpad/minidump/test/minidump_user_extension_stream_util.h new file mode 100644 index 000000000..2dc60f4a6 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/test/minidump_user_extension_stream_util.h @@ -0,0 +1,56 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_TEST_MINIDUMP_USER_EXTENSION_STREAM_UTIL_H_ +#define CRASHPAD_MINIDUMP_TEST_MINIDUMP_USER_EXTENSION_STREAM_UTIL_H_ + +#include "minidump/minidump_user_extension_stream_data_source.h" + +#include +#include + +#include + +namespace crashpad { +namespace test { + +//! \brief A user extension data source that wraps a buffer. +class BufferExtensionStreamDataSource final + : public MinidumpUserExtensionStreamDataSource { + public: + //! \brief Creates a data source with \a stream_type. + //! + //! param[in] stream_type The type of the stream. + //! param[in] data The data of the stream. + //! param[in] data_size The length of \a data. + BufferExtensionStreamDataSource(uint32_t stream_type, + const void* data, + size_t data_size); + + BufferExtensionStreamDataSource(const BufferExtensionStreamDataSource&) = + delete; + BufferExtensionStreamDataSource& operator=( + const BufferExtensionStreamDataSource&) = delete; + + size_t StreamDataSize() override; + bool ReadStreamData(Delegate* delegate) override; + + private: + std::vector data_; +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_TEST_MINIDUMP_USER_EXTENSION_STREAM_UTIL_H_ diff --git a/shared/sentry/external/crashpad/minidump/test/minidump_writable_test_util.cc b/shared/sentry/external/crashpad/minidump/test/minidump_writable_test_util.cc new file mode 100644 index 000000000..2ae6a9b14 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/test/minidump_writable_test_util.cc @@ -0,0 +1,414 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "minidump/test/minidump_writable_test_util.h" + +#include + +#include + +#include "gtest/gtest.h" +#include "util/file/file_writer.h" +#include "util/misc/implicit_cast.h" + +namespace crashpad { +namespace test { + +namespace { + +//! \brief Returns an untyped minidump object located within a minidump file’s +//! contents, where the offset of the object is known. +//! +//! \param[in] file_contents The contents of the minidump file. +//! \param[in] rva The offset within the minidump file of the desired object. +//! +//! \return If \a rva is within the range of \a file_contents, returns a pointer +//! into \a file_contents at offset \a rva. Otherwise, raises a Google Test +//! assertion failure and returns `nullptr`. +//! +//! Do not call this function. Use the typed version, MinidumpWritableAtRVA<>(), +//! or another type-specific function. +const void* MinidumpWritableAtRVAInternal(const std::string& file_contents, + RVA rva) { + if (rva >= file_contents.size()) { + EXPECT_LT(rva, file_contents.size()); + return nullptr; + } + + return &file_contents[rva]; +} + +} // namespace + +const void* MinidumpWritableAtLocationDescriptorInternal( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location, + size_t expected_size, + bool allow_oversized_data) { + if (location.DataSize == 0) { + EXPECT_EQ(location.Rva, 0u); + return nullptr; + } + + if (allow_oversized_data) { + if (location.DataSize < expected_size) { + EXPECT_GE(location.DataSize, expected_size); + return nullptr; + } + } else if (location.DataSize != expected_size) { + EXPECT_EQ(location.DataSize, expected_size); + return nullptr; + } + + RVA end = location.Rva + location.DataSize; + if (end > file_contents.size()) { + EXPECT_LE(end, file_contents.size()); + return nullptr; + } + + const void* rv = MinidumpWritableAtRVAInternal(file_contents, location.Rva); + + return rv; +} + +template <> +const IMAGE_DEBUG_MISC* MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + const IMAGE_DEBUG_MISC* misc = + TMinidumpWritableAtLocationDescriptor(file_contents, + location); + if (!misc) { + return nullptr; + } + + if (misc->DataType != IMAGE_DEBUG_MISC_EXENAME) { + EXPECT_EQ(misc->DataType, + implicit_cast(IMAGE_DEBUG_MISC_EXENAME)); + return nullptr; + } + + if (misc->Length != location.DataSize) { + EXPECT_EQ(misc->Length, location.DataSize); + return nullptr; + } + + if (misc->Unicode == 0) { + size_t string_length = misc->Length - offsetof(IMAGE_DEBUG_MISC, Data) - 1; + if (misc->Data[string_length] != '\0') { + EXPECT_EQ(misc->Data[string_length], '\0'); + return nullptr; + } + } else if (misc->Unicode == 1) { + if (misc->Length % sizeof(char16_t) != 0) { + EXPECT_EQ(misc->Length % sizeof(char16_t), 0u); + return nullptr; + } + + size_t string_length = + (misc->Length - offsetof(IMAGE_DEBUG_MISC, Data)) / sizeof(char16_t) - + 1; + const char16_t* data16 = reinterpret_cast(misc->Data); + if (data16[string_length] != '\0') { + EXPECT_EQ(data16[string_length], '\0'); + return nullptr; + } + } else { + ADD_FAILURE() << "misc->Unicode " << misc->Unicode; + return nullptr; + } + + return misc; +} + +template <> +const MINIDUMP_HEADER* MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + const MINIDUMP_HEADER* header = + TMinidumpWritableAtLocationDescriptor(file_contents, + location); + if (!header) { + return nullptr; + } + + if (header->Signature != MINIDUMP_SIGNATURE) { + EXPECT_EQ(header->Signature, implicit_cast(MINIDUMP_SIGNATURE)); + return nullptr; + } + if (header->Version != MINIDUMP_VERSION) { + EXPECT_EQ(header->Version, implicit_cast(MINIDUMP_VERSION)); + return nullptr; + } + + return header; +} + +namespace { + +struct MinidumpMemoryListTraits { + using ListType = MINIDUMP_MEMORY_LIST; + enum : size_t { kElementSize = sizeof(MINIDUMP_MEMORY_DESCRIPTOR) }; + static size_t ElementCount(const ListType* list) { + return list->NumberOfMemoryRanges; + } +}; + +struct MinidumpModuleListTraits { + using ListType = MINIDUMP_MODULE_LIST; + enum : size_t { kElementSize = sizeof(MINIDUMP_MODULE) }; + static size_t ElementCount(const ListType* list) { + return list->NumberOfModules; + } +}; + +struct MinidumpUnloadedModuleListTraits { + using ListType = MINIDUMP_UNLOADED_MODULE_LIST; + enum : size_t { kElementSize = sizeof(MINIDUMP_UNLOADED_MODULE) }; + static size_t ElementCount(const ListType* list) { + return list->NumberOfEntries; + } +}; + +struct MinidumpThreadListTraits { + using ListType = MINIDUMP_THREAD_LIST; + enum : size_t { kElementSize = sizeof(MINIDUMP_THREAD) }; + static size_t ElementCount(const ListType* list) { + return list->NumberOfThreads; + } +}; + +struct MinidumpHandleDataStreamTraits { + using ListType = MINIDUMP_HANDLE_DATA_STREAM; + enum : size_t { kElementSize = sizeof(MINIDUMP_HANDLE_DESCRIPTOR) }; + static size_t ElementCount(const ListType* list) { + return static_cast(list->NumberOfDescriptors); + } +}; + +struct MinidumpMemoryInfoListTraits { + using ListType = MINIDUMP_MEMORY_INFO_LIST; + enum : size_t { kElementSize = sizeof(MINIDUMP_MEMORY_INFO) }; + static size_t ElementCount(const ListType* list) { + return static_cast(list->NumberOfEntries); + } +}; + +struct MinidumpModuleCrashpadInfoListTraits { + using ListType = MinidumpModuleCrashpadInfoList; + enum : size_t { kElementSize = sizeof(MinidumpModuleCrashpadInfoLink) }; + static size_t ElementCount(const ListType* list) { return list->count; } +}; + +struct MinidumpSimpleStringDictionaryListTraits { + using ListType = MinidumpSimpleStringDictionary; + enum : size_t { kElementSize = sizeof(MinidumpSimpleStringDictionaryEntry) }; + static size_t ElementCount(const ListType* list) { return list->count; } +}; + +struct MinidumpAnnotationListObjectsTraits { + using ListType = MinidumpAnnotationList; + enum : size_t { kElementSize = sizeof(MinidumpAnnotation) }; + static size_t ElementCount(const ListType* list) { return list->count; } +}; + +template +const typename T::ListType* MinidumpListAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + const typename T::ListType* list = + TMinidumpWritableAtLocationDescriptor(file_contents, + location); + if (!list) { + return nullptr; + } + + size_t expected_size = + sizeof(typename T::ListType) + T::ElementCount(list) * T::kElementSize; + if (location.DataSize != expected_size) { + EXPECT_EQ(location.DataSize, expected_size); + return nullptr; + } + + return list; +} + +} // namespace + +template <> +const MINIDUMP_MEMORY_LIST* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + return MinidumpListAtLocationDescriptor( + file_contents, location); +} + +template <> +const MINIDUMP_MODULE_LIST* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + return MinidumpListAtLocationDescriptor( + file_contents, location); +} + +template <> +const MINIDUMP_UNLOADED_MODULE_LIST* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + return MinidumpListAtLocationDescriptor( + file_contents, location); +} + +template <> +const MINIDUMP_THREAD_LIST* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + return MinidumpListAtLocationDescriptor( + file_contents, location); +} + +template <> +const MINIDUMP_HANDLE_DATA_STREAM* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + return MinidumpListAtLocationDescriptor( + file_contents, location); +} + +template <> +const MINIDUMP_MEMORY_INFO_LIST* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + return MinidumpListAtLocationDescriptor( + file_contents, location); +} + +template <> +const MinidumpModuleCrashpadInfoList* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + return MinidumpListAtLocationDescriptor( + file_contents, location); +} + +template <> +const MinidumpSimpleStringDictionary* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + return MinidumpListAtLocationDescriptor< + MinidumpSimpleStringDictionaryListTraits>(file_contents, location); +} + +template <> +const MinidumpAnnotationList* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + return MinidumpListAtLocationDescriptor( + file_contents, location); +} + +namespace { + +template +const T* MinidumpCVPDBAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + const T* cv_pdb = + TMinidumpWritableAtLocationDescriptor(file_contents, location); + if (!cv_pdb) { + return nullptr; + } + + if (cv_pdb->signature != T::kSignature) { + EXPECT_EQ(cv_pdb->signature, T::kSignature); + return nullptr; + } + + size_t string_length = location.DataSize - offsetof(T, pdb_name) - 1; + if (cv_pdb->pdb_name[string_length] != '\0') { + EXPECT_EQ(cv_pdb->pdb_name[string_length], '\0'); + return nullptr; + } + + return cv_pdb; +} + +} // namespace + +template <> +const CodeViewRecordPDB20* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + return MinidumpCVPDBAtLocationDescriptor(file_contents, + location); +} + +template <> +const CodeViewRecordPDB70* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + return MinidumpCVPDBAtLocationDescriptor(file_contents, + location); +} + +template <> +const CodeViewRecordBuildID* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + const CodeViewRecordBuildID* cv = + reinterpret_cast( + MinidumpWritableAtLocationDescriptorInternal( + file_contents, + location, + offsetof(CodeViewRecordBuildID, build_id), + true)); + + if (!cv) { + return nullptr; + } + + if (cv->signature != CodeViewRecordBuildID::kSignature) { + return nullptr; + } + + return cv; +} + +TestUInt32MinidumpWritable::TestUInt32MinidumpWritable(uint32_t value) + : MinidumpWritable(), value_(value) {} + +TestUInt32MinidumpWritable::~TestUInt32MinidumpWritable() {} + +size_t TestUInt32MinidumpWritable::SizeOfObject() { + return sizeof(value_); +} + +bool TestUInt32MinidumpWritable::WriteObject(FileWriterInterface* file_writer) { + return file_writer->Write(&value_, sizeof(value_)); +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/minidump/test/minidump_writable_test_util.h b/shared/sentry/external/crashpad/minidump/test/minidump_writable_test_util.h new file mode 100644 index 000000000..9ebb9d026 --- /dev/null +++ b/shared/sentry/external/crashpad/minidump/test/minidump_writable_test_util.h @@ -0,0 +1,297 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_MINIDUMP_TEST_MINIDUMP_WRITABLE_TEST_UTIL_H_ +#define CRASHPAD_MINIDUMP_TEST_MINIDUMP_WRITABLE_TEST_UTIL_H_ + +#include +#include +#include +#include + +#include + +#include "gtest/gtest.h" +#include "minidump/minidump_extensions.h" +#include "minidump/minidump_writable.h" + +namespace crashpad { + +class FileWriterInterface; + +namespace test { + +//! \brief Returns an untyped minidump object located within a minidump file’s +//! contents, where the offset and size of the object are known. +//! +//! \param[in] file_contents The contents of the minidump file. +//! \param[in] location A MINIDUMP_LOCATION_DESCRIPTOR giving the offset within +//! the minidump file of the desired object, as well as its size. +//! \param[in] expected_size The expected size of the object. If \a +//! allow_oversized_data is `true`, \a expected_size is treated as the +//! minimum size of \a location, but it is permitted to be larger. If \a +//! allow_oversized_data is `false`, the size of \a location must match +//! \a expected_size exactly. +//! \param[in] allow_oversized_data Controls whether \a expected_size is a +//! minimum limit (`true`) or an exact match is required (`false`). +//! +//! \return If the size of \a location is agrees with \a expected_size, and if +//! \a location is within the range of \a file_contents, returns a pointer +//! into \a file_contents at offset \a rva. Otherwise, raises a Google Test +//! assertion failure and returns `nullptr`. +//! +//! Do not call this function. Use the typed version, +//! MinidumpWritableAtLocationDescriptor<>(), or another type-specific function. +const void* MinidumpWritableAtLocationDescriptorInternal( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location, + size_t expected_size, + bool allow_oversized_data); + +//! \brief A traits class defining whether a minidump object type is required to +//! appear only as a fixed-size object or if it is variable-sized. +//! +//! Variable-sized data is data referenced by a MINIDUMP_LOCATION_DESCRIPTOR +//! whose DataSize field may be larger than the size of the basic object type’s +//! structure. This can happen for types that appear only as variable-sized +//! lists, or types whose final fields are variable-sized lists or other +//! variable-sized data. +template +struct MinidumpWritableTraits { + //! \brief `true` if \a T should be treated as a variable-sized data type, + //! where its base size is used solely as a minimum bound. `false` if \a + //! T is a fixed-sized type, which should only appear at its base size. + static const bool kAllowOversizedData = false; +}; + +#define MINIDUMP_ALLOW_OVERSIZED_DATA(x) \ + template <> \ + struct MinidumpWritableTraits { \ + static const bool kAllowOversizedData = true; \ + } + +// This type appears only as a variable-sized list. +MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_DIRECTORY); + +// These types are permitted to be oversized because their final fields are +// variable-sized lists. +MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MEMORY_LIST); +MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MODULE_LIST); +MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_UNLOADED_MODULE_LIST); +MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_THREAD_LIST); +MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_HANDLE_DATA_STREAM); +MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_MEMORY_INFO_LIST); +MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpModuleCrashpadInfoList); +MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpRVAList); +MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpSimpleStringDictionary); +MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpAnnotationList); + +// These types have final fields carrying variable-sized data (typically string +// data). +MINIDUMP_ALLOW_OVERSIZED_DATA(IMAGE_DEBUG_MISC); +MINIDUMP_ALLOW_OVERSIZED_DATA(MINIDUMP_STRING); +MINIDUMP_ALLOW_OVERSIZED_DATA(CodeViewRecordPDB20); +MINIDUMP_ALLOW_OVERSIZED_DATA(CodeViewRecordPDB70); +MINIDUMP_ALLOW_OVERSIZED_DATA(CodeViewRecordBuildID); +MINIDUMP_ALLOW_OVERSIZED_DATA(MinidumpUTF8String); + +// minidump_file_writer_test accesses its variable-sized test streams via a +// uint8_t*. +MINIDUMP_ALLOW_OVERSIZED_DATA(uint8_t); + +#undef MINIDUMP_ALLOW_OVERSIZED_DATA + +//! \brief Returns a typed minidump object located within a minidump file’s +//! contents, where the offset and size of the object are known. +//! +//! This function is similar to MinidumpWritableAtLocationDescriptor<>() and is +//! used to implement that function. It exists independently so that template +//! specializations are able to call this function, which provides the default +//! implementation. +//! +//! Do not call this function directly. Use +//! MinidumpWritableAtLocationDescriptor<>() instead. +template +const T* TMinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + return reinterpret_cast( + MinidumpWritableAtLocationDescriptorInternal( + file_contents, + location, + sizeof(T), + MinidumpWritableTraits::kAllowOversizedData)); +} + +//! \brief Returns a typed minidump object located within a minidump file’s +//! contents, where the offset and size of the object are known. +//! +//! This function has template specializations that perform more stringent +//! checking than the default implementation: +//! - With a MINIDUMP_HEADER template parameter, a template specialization +//! ensures that the structure’s magic number and version fields are correct. +//! - With a MINIDUMP_MEMORY_LIST, MINIDUMP_THREAD_LIST, MINIDUMP_MODULE_LIST, +//! MINIDUMP_MEMORY_INFO_LIST, MinidumpSimpleStringDictionary, or +//! MinidumpAnnotationList template parameter, template specializations +//! ensure that the size given by \a location matches the size expected of a +//! stream containing the number of elements it claims to have. +//! - With an IMAGE_DEBUG_MISC, CodeViewRecordPDB20, or CodeViewRecordPDB70 +//! template parameter, template specializations ensure that the structure +//! has the expected format including any magic number and the `NUL`- +//! terminated string. +//! +//! \param[in] file_contents The contents of the minidump file. +//! \param[in] location A MINIDUMP_LOCATION_DESCRIPTOR giving the offset within +//! the minidump file of the desired object, as well as its size. +//! +//! \return If the size of \a location is at least as big as the size of the +//! requested object, and if \a location is within the range of \a +//! file_contents, returns a pointer into \a file_contents at offset \a rva. +//! Otherwise, raises a Google Test assertion failure and returns `nullptr`. +//! +//! \sa MinidumpWritableAtRVA() +template +const T* MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location) { + return TMinidumpWritableAtLocationDescriptor(file_contents, location); +} + +template <> +const IMAGE_DEBUG_MISC* MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); + +template <> +const MINIDUMP_HEADER* MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); + +template <> +const MINIDUMP_MEMORY_LIST* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); + +template <> +const MINIDUMP_MODULE_LIST* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); + +template <> +const MINIDUMP_UNLOADED_MODULE_LIST* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); + +template <> +const MINIDUMP_THREAD_LIST* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); + +template <> +const MINIDUMP_HANDLE_DATA_STREAM* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); + +template <> +const MINIDUMP_MEMORY_INFO_LIST* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); + +template <> +const CodeViewRecordPDB20* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); + +template <> +const CodeViewRecordPDB70* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); + +template <> +const CodeViewRecordBuildID* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); + +template <> +const MinidumpModuleCrashpadInfoList* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); + +template <> +const MinidumpSimpleStringDictionary* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); + +template <> +const MinidumpAnnotationList* +MinidumpWritableAtLocationDescriptor( + const std::string& file_contents, + const MINIDUMP_LOCATION_DESCRIPTOR& location); + +//! \brief Returns a typed minidump object located within a minidump file’s +//! contents, where the offset of the object is known. +//! +//! \param[in] file_contents The contents of the minidump file. +//! \param[in] rva The offset within the minidump file of the desired object. +//! +//! \return If \a rva plus the size of an object of type \a T is within the +//! range of \a file_contents, returns a pointer into \a file_contents at +//! offset \a rva. Otherwise, raises a Google Test assertion failure and +//! returns `nullptr`. +//! +//! \sa MinidumpWritableAtLocationDescriptor<>() +template +const T* MinidumpWritableAtRVA(const std::string& file_contents, RVA rva) { + MINIDUMP_LOCATION_DESCRIPTOR location; + location.DataSize = sizeof(T); + location.Rva = rva; + return MinidumpWritableAtLocationDescriptor(file_contents, location); +} + +//! \brief An internal::MinidumpWritable that carries a `uint32_t` for testing. +class TestUInt32MinidumpWritable final : public internal::MinidumpWritable { + public: + //! \brief Constructs the object to write a `uint32_t` with value \a value. + explicit TestUInt32MinidumpWritable(uint32_t value); + + TestUInt32MinidumpWritable(const TestUInt32MinidumpWritable&) = delete; + TestUInt32MinidumpWritable& operator=(const TestUInt32MinidumpWritable&) = + delete; + + ~TestUInt32MinidumpWritable() override; + + protected: + // MinidumpWritable: + size_t SizeOfObject() override; + bool WriteObject(FileWriterInterface* file_writer) override; + + private: + uint32_t value_; +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_MINIDUMP_TEST_MINIDUMP_WRITABLE_TEST_UTIL_H_ diff --git a/shared/sentry/external/crashpad/navbar.md b/shared/sentry/external/crashpad/navbar.md new file mode 100644 index 000000000..d530414f0 --- /dev/null +++ b/shared/sentry/external/crashpad/navbar.md @@ -0,0 +1,25 @@ + + +# Crashpad + + * [Home][home] + * [Developing](/doc/developing.md) + * [Interface Docs](https://crashpad.chromium.org/doxygen/) + * [Man Pages](/doc/man.md) + * [Source Code](https://chromium.googlesource.com/crashpad/crashpad/) + +[home]: /README.md diff --git a/shared/sentry/external/crashpad/package.h b/shared/sentry/external/crashpad/package.h new file mode 100644 index 000000000..e170dec98 --- /dev/null +++ b/shared/sentry/external/crashpad/package.h @@ -0,0 +1,29 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_PACKAGE_H_ +#define CRASHPAD_PACKAGE_H_ + +#define PACKAGE_BUGREPORT "https://crashpad.chromium.org/bug/new" +#define PACKAGE_COPYRIGHT \ + "Copyright " PACKAGE_COPYRIGHT_YEAR " " PACKAGE_COPYRIGHT_OWNER +#define PACKAGE_COPYRIGHT_OWNER "The Crashpad Authors" +#define PACKAGE_COPYRIGHT_YEAR "2018" +#define PACKAGE_NAME "Crashpad" +#define PACKAGE_STRING PACKAGE_NAME " " PACKAGE_VERSION +#define PACKAGE_TARNAME "crashpad" +#define PACKAGE_VERSION "0.8.0" +#define PACKAGE_URL "https://crashpad.chromium.org/" + +#endif // CRASHPAD_PACKAGE_H_ diff --git a/shared/sentry/external/crashpad/snapshot/BUILD.gn b/shared/sentry/external/crashpad/snapshot/BUILD.gn new file mode 100644 index 000000000..ea2412a0b --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/BUILD.gn @@ -0,0 +1,639 @@ +# Copyright 2015 The Crashpad Authors. All rights reserved. +# +# 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("../build/crashpad_buildconfig.gni") +import("../build/crashpad_fuzzer_test.gni") + +if (crashpad_is_in_chromium) { + import("//build/config/compiler/compiler.gni") +} + +crashpad_static_library("snapshot") { + sources = [ + "annotation_snapshot.cc", + "annotation_snapshot.h", + "capture_memory.cc", + "capture_memory.h", + "crashpad_info_client_options.cc", + "crashpad_info_client_options.h", + "exception_snapshot.h", + "handle_snapshot.cc", + "handle_snapshot.h", + "memory_snapshot.cc", + "memory_snapshot.h", + "memory_snapshot_generic.h", + "minidump/exception_snapshot_minidump.cc", + "minidump/exception_snapshot_minidump.h", + "minidump/memory_snapshot_minidump.cc", + "minidump/memory_snapshot_minidump.h", + "minidump/minidump_annotation_reader.cc", + "minidump/minidump_annotation_reader.h", + "minidump/minidump_context_converter.cc", + "minidump/minidump_context_converter.h", + "minidump/minidump_simple_string_dictionary_reader.cc", + "minidump/minidump_simple_string_dictionary_reader.h", + "minidump/minidump_stream.h", + "minidump/minidump_string_list_reader.cc", + "minidump/minidump_string_list_reader.h", + "minidump/minidump_string_reader.cc", + "minidump/minidump_string_reader.h", + "minidump/module_snapshot_minidump.cc", + "minidump/module_snapshot_minidump.h", + "minidump/process_snapshot_minidump.cc", + "minidump/process_snapshot_minidump.h", + "minidump/system_snapshot_minidump.cc", + "minidump/system_snapshot_minidump.h", + "minidump/thread_snapshot_minidump.cc", + "minidump/thread_snapshot_minidump.h", + "module_snapshot.h", + "process_snapshot.h", + "snapshot_constants.h", + "system_snapshot.h", + "thread_snapshot.h", + "unloaded_module_snapshot.cc", + "unloaded_module_snapshot.h", + ] + + if (crashpad_is_posix || crashpad_is_fuchsia) { + sources += [ + "posix/timezone.cc", + "posix/timezone.h", + ] + } + + if (crashpad_is_mac) { + sources += [ + "mac/cpu_context_mac.cc", + "mac/cpu_context_mac.h", + "mac/exception_snapshot_mac.cc", + "mac/exception_snapshot_mac.h", + "mac/mach_o_image_annotations_reader.cc", + "mac/mach_o_image_annotations_reader.h", + "mac/mach_o_image_reader.cc", + "mac/mach_o_image_reader.h", + "mac/mach_o_image_segment_reader.cc", + "mac/mach_o_image_segment_reader.h", + "mac/mach_o_image_symbol_table_reader.cc", + "mac/mach_o_image_symbol_table_reader.h", + "mac/module_snapshot_mac.cc", + "mac/module_snapshot_mac.h", + "mac/process_reader_mac.cc", + "mac/process_reader_mac.h", + "mac/process_snapshot_mac.cc", + "mac/process_snapshot_mac.h", + "mac/process_types.cc", + "mac/process_types.h", + "mac/process_types/custom.cc", + "mac/process_types/flavors.h", + "mac/process_types/internal.h", + "mac/process_types/traits.h", + "mac/system_snapshot_mac.cc", + "mac/system_snapshot_mac.h", + "mac/thread_snapshot_mac.cc", + "mac/thread_snapshot_mac.h", + ] + } + + if (crashpad_is_ios) { + sources += [ + "ios/exception_snapshot_ios_intermediate_dump.cc", + "ios/exception_snapshot_ios_intermediate_dump.h", + "ios/intermediate_dump_reader_util.cc", + "ios/intermediate_dump_reader_util.h", + "ios/memory_snapshot_ios_intermediate_dump.cc", + "ios/memory_snapshot_ios_intermediate_dump.h", + "ios/module_snapshot_ios_intermediate_dump.cc", + "ios/module_snapshot_ios_intermediate_dump.h", + "ios/process_snapshot_ios_intermediate_dump.cc", + "ios/process_snapshot_ios_intermediate_dump.h", + "ios/system_snapshot_ios_intermediate_dump.cc", + "ios/system_snapshot_ios_intermediate_dump.h", + "ios/thread_snapshot_ios_intermediate_dump.cc", + "ios/thread_snapshot_ios_intermediate_dump.h", + "mac/cpu_context_mac.cc", + "mac/cpu_context_mac.h", + ] + } + + if (crashpad_is_linux || crashpad_is_android) { + sources += [ + "linux/capture_memory_delegate_linux.cc", + "linux/capture_memory_delegate_linux.h", + "linux/cpu_context_linux.cc", + "linux/cpu_context_linux.h", + "linux/debug_rendezvous.cc", + "linux/debug_rendezvous.h", + "linux/exception_snapshot_linux.cc", + "linux/exception_snapshot_linux.h", + "linux/process_reader_linux.cc", + "linux/process_reader_linux.h", + "linux/process_snapshot_linux.cc", + "linux/process_snapshot_linux.h", + "linux/signal_context.h", + "linux/system_snapshot_linux.cc", + "linux/system_snapshot_linux.h", + "linux/thread_snapshot_linux.cc", + "linux/thread_snapshot_linux.h", + "sanitized/memory_snapshot_sanitized.cc", + "sanitized/memory_snapshot_sanitized.h", + "sanitized/module_snapshot_sanitized.cc", + "sanitized/module_snapshot_sanitized.h", + "sanitized/process_snapshot_sanitized.cc", + "sanitized/process_snapshot_sanitized.h", + "sanitized/sanitization_information.cc", + "sanitized/sanitization_information.h", + "sanitized/thread_snapshot_sanitized.cc", + "sanitized/thread_snapshot_sanitized.h", + ] + } + + if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia || + crashpad_is_win) { + sources += [ + "crashpad_types/crashpad_info_reader.cc", + "crashpad_types/crashpad_info_reader.h", + ] + } + + if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { + sources += [ + "crashpad_types/image_annotation_reader.cc", + "crashpad_types/image_annotation_reader.h", + "elf/elf_dynamic_array_reader.cc", + "elf/elf_dynamic_array_reader.h", + "elf/elf_image_reader.cc", + "elf/elf_image_reader.h", + "elf/elf_symbol_table_reader.cc", + "elf/elf_symbol_table_reader.h", + "elf/module_snapshot_elf.cc", + "elf/module_snapshot_elf.h", + ] + } + + if (crashpad_is_win) { + sources += [ + "win/capture_memory_delegate_win.cc", + "win/capture_memory_delegate_win.h", + "win/cpu_context_win.cc", + "win/cpu_context_win.h", + "win/exception_snapshot_win.cc", + "win/exception_snapshot_win.h", + "win/memory_map_region_snapshot_win.cc", + "win/memory_map_region_snapshot_win.h", + "win/module_snapshot_win.cc", + "win/module_snapshot_win.h", + "win/pe_image_annotations_reader.cc", + "win/pe_image_annotations_reader.h", + "win/pe_image_reader.cc", + "win/pe_image_reader.h", + "win/pe_image_resource_reader.cc", + "win/pe_image_resource_reader.h", + "win/process_reader_win.cc", + "win/process_reader_win.h", + "win/process_snapshot_win.cc", + "win/process_snapshot_win.h", + "win/process_subrange_reader.cc", + "win/process_subrange_reader.h", + "win/system_snapshot_win.cc", + "win/system_snapshot_win.h", + "win/thread_snapshot_win.cc", + "win/thread_snapshot_win.h", + ] + } + + if (crashpad_is_fuchsia) { + sources += [ + "fuchsia/cpu_context_fuchsia.cc", + "fuchsia/cpu_context_fuchsia.h", + "fuchsia/exception_snapshot_fuchsia.cc", + "fuchsia/exception_snapshot_fuchsia.h", + "fuchsia/memory_map_fuchsia.cc", + "fuchsia/memory_map_fuchsia.h", + "fuchsia/memory_map_region_snapshot_fuchsia.cc", + "fuchsia/memory_map_region_snapshot_fuchsia.h", + "fuchsia/process_reader_fuchsia.cc", + "fuchsia/process_reader_fuchsia.h", + "fuchsia/process_snapshot_fuchsia.cc", + "fuchsia/process_snapshot_fuchsia.h", + "fuchsia/system_snapshot_fuchsia.cc", + "fuchsia/system_snapshot_fuchsia.h", + "fuchsia/thread_snapshot_fuchsia.cc", + "fuchsia/thread_snapshot_fuchsia.h", + ] + } + + if (current_cpu == "x86" || current_cpu == "x64" || + (crashpad_is_mac && current_cpu == "mac_universal")) { + sources += [ + "x86/cpuid_reader.cc", + "x86/cpuid_reader.h", + ] + } + + public_configs = [ "..:crashpad_config" ] + + public_deps = [ ":context" ] + + deps = [ + "$mini_chromium_source_parent:base", + "../client:common", + "../compat", + "../minidump:format", + "../util", + ] + + if (crashpad_is_win) { + cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union + libs = [ "powrprof.lib" ] + } +} + +# :context is the only part of snapshot that minidump may depend on. +static_library("context") { + sources = [ + "cpu_architecture.h", + "cpu_context.cc", + "cpu_context.h", + ] + + public_configs = [ "..:crashpad_config" ] + + deps = [ + "$mini_chromium_source_parent:base", + "../util", + ] + + if (crashpad_is_win) { + cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union + } +} + +if (crashpad_is_linux) { + crashpad_fuzzer_test("elf_image_reader_fuzzer") { + sources = [ "elf/elf_image_reader_fuzzer.cc" ] + + deps = [ + ":snapshot", + "$mini_chromium_source_parent:base", + "../util:util", + ] + seed_corpus = "elf/elf_image_reader_fuzzer_corpus" + } +} + +static_library("test_support") { + testonly = true + + sources = [ + "test/test_cpu_context.cc", + "test/test_cpu_context.h", + "test/test_exception_snapshot.cc", + "test/test_exception_snapshot.h", + "test/test_memory_map_region_snapshot.cc", + "test/test_memory_map_region_snapshot.h", + "test/test_memory_snapshot.cc", + "test/test_memory_snapshot.h", + "test/test_module_snapshot.cc", + "test/test_module_snapshot.h", + "test/test_process_snapshot.cc", + "test/test_process_snapshot.h", + "test/test_system_snapshot.cc", + "test/test_system_snapshot.h", + "test/test_thread_snapshot.cc", + "test/test_thread_snapshot.h", + ] + + public_configs = [ "..:crashpad_config" ] + + public_deps = [ ":snapshot" ] + + deps = [ + "$mini_chromium_source_parent:base", + "../compat", + "../util", + ] + + if (crashpad_is_win) { + cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union + } +} + +config("snapshot_test_link") { + visibility = [ ":*" ] + if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { + # There’s no way to make the link depend on this file. “inputs†doesn’t have + # the intended effect in a config. https://crbug.com/781858, + # https://crbug.com/796187. + inputs = [ "elf/test_exported_symbols.sym" ] + ldflags = [ "-Wl,--dynamic-list," + rebase_path(inputs[0], root_build_dir) ] + } +} + +source_set("snapshot_test") { + testonly = true + + sources = [ + "cpu_context_test.cc", + "memory_snapshot_test.cc", + "minidump/process_snapshot_minidump_test.cc", + ] + + if (crashpad_is_mac) { + sources += [ + "mac/cpu_context_mac_test.cc", + "mac/mach_o_image_annotations_reader_test.cc", + "mac/mach_o_image_reader_test.cc", + "mac/mach_o_image_segment_reader_test.cc", + "mac/process_reader_mac_test.cc", + "mac/process_types_test.cc", + "mac/system_snapshot_mac_test.cc", + ] + } + + if (crashpad_is_ios) { + sources += [ + "ios/memory_snapshot_ios_intermediate_dump_test.cc", + "ios/process_snapshot_ios_intermediate_dump_test.cc", + "mac/cpu_context_mac_test.cc", + ] + } + + if (crashpad_is_linux || crashpad_is_android) { + sources += [ + "linux/debug_rendezvous_test.cc", + "linux/exception_snapshot_linux_test.cc", + "linux/process_reader_linux_test.cc", + "linux/system_snapshot_linux_test.cc", + "linux/test_modules.cc", + "linux/test_modules.h", + "sanitized/process_snapshot_sanitized_test.cc", + "sanitized/sanitization_information_test.cc", + ] + } else if (!crashpad_is_ios) { + sources += [ "crashpad_info_client_options_test.cc" ] + } + + if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia || + crashpad_is_win) { + sources += [ "crashpad_types/crashpad_info_reader_test.cc" ] + } + + if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { + sources += [ + "crashpad_types/image_annotation_reader_test.cc", + "elf/elf_image_reader_test.cc", + "elf/elf_image_reader_test_note.S", + ] + } + + if (crashpad_is_win) { + sources += [ + "win/cpu_context_win_test.cc", + "win/exception_snapshot_win_test.cc", + "win/extra_memory_ranges_test.cc", + "win/module_snapshot_win_test.cc", + "win/pe_image_reader_test.cc", + "win/process_reader_win_test.cc", + "win/process_snapshot_win_test.cc", + "win/system_snapshot_win_test.cc", + ] + } else if (!crashpad_is_fuchsia) { + # Timezones are currently non-functional on Fuchsia: + # https://fuchsia.googlesource.com/zircon/+/master/third_party/ulib/musl/src/time/__tz.c#9 + # https://crashpad.chromium.org/bug/196. Relevant upstream bugs are ZX-337 + # and ZX-1731. + sources += [ "posix/timezone_test.cc" ] + } + + if (crashpad_is_fuchsia) { + sources += [ + "fuchsia/process_reader_fuchsia_test.cc", + "fuchsia/process_snapshot_fuchsia_test.cc", + ] + } + + # public_configs isn’t quite right. snapshot_test_link sets ldflags, and + # what’s really needed is a way to push ldflags to dependent targets that + # produce linker output. Luckily in this case, all dependents do produce + # linker output. https://crbug.com/796183. + public_configs = [ ":snapshot_test_link" ] + + deps = [ + ":test_support", + "$mini_chromium_source_parent:base", + "../client:common", + "../compat", + "../minidump:format", + "../test", + "../third_party/googletest:googlemock", + "../third_party/googletest:googletest", + "../util", + ] + + if (crashpad_is_ios) { + deps += [ + ":snapshot_test_ios_data", + "../minidump", + ] + } + + data_deps = [ + ":crashpad_snapshot_test_module", + ":crashpad_snapshot_test_module_large", + ":crashpad_snapshot_test_module_small", + ] + + if (crashpad_is_mac) { + frameworks = [ "OpenCL.framework" ] + + data_deps += [ + ":crashpad_snapshot_test_module_crashy_initializer", + ":crashpad_snapshot_test_no_op", + ] + } + + if (crashpad_is_linux || crashpad_is_android) { + libs = [ "dl" ] + } + + if ((crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) && + target_cpu != "mipsel" && target_cpu != "mips64el") { + data_deps += [ ":crashpad_snapshot_test_both_dt_hash_styles" ] + } + + if (crashpad_is_win) { + cflags = [ "/wd4201" ] # nonstandard extension used : nameless struct/union + + data_deps += [ + ":crashpad_snapshot_test_annotations", + ":crashpad_snapshot_test_crashing_child", + ":crashpad_snapshot_test_dump_without_crashing", + ":crashpad_snapshot_test_extra_memory_ranges", + ":crashpad_snapshot_test_image_reader", + ":crashpad_snapshot_test_image_reader_module", + ] + } +} + +bundle_data("snapshot_test_ios_data") { + testonly = true + + sources = [ + "ios/testdata/crash-1fa088dda0adb41459d063078a0f384a0bb8eefa", + "ios/testdata/crash-5726011582644224", + ] + + outputs = [ "{{bundle_resources_dir}}/crashpad_test_data/" + + "{{source_root_relative_dir}}/{{source_file_part}}" ] +} +crashpad_loadable_module("crashpad_snapshot_test_module") { + testonly = true + sources = [ "crashpad_info_client_options_test_module.cc" ] + deps = [ + "$mini_chromium_source_parent:base", + "../client", + ] +} + +crashpad_loadable_module("crashpad_snapshot_test_module_large") { + testonly = true + sources = [ "crashpad_info_size_test_module.cc" ] + + deps = [] + if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { + sources += [ "crashpad_info_size_test_note.S" ] + deps += [ "../util" ] + } + + defines = [ "CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE" ] + deps += [ "$mini_chromium_source_parent:base" ] +} + +crashpad_loadable_module("crashpad_snapshot_test_module_small") { + testonly = true + sources = [ "crashpad_info_size_test_module.cc" ] + + deps = [] + if (crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) { + sources += [ "crashpad_info_size_test_note.S" ] + deps += [ "../util" ] + } + + defines = [ "CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL" ] + deps += [ "$mini_chromium_source_parent:base" ] +} + +if ((crashpad_is_linux || crashpad_is_android || crashpad_is_fuchsia) && + target_cpu != "mipsel" && target_cpu != "mips64el") { + crashpad_loadable_module("crashpad_snapshot_test_both_dt_hash_styles") { + testonly = true + sources = [ "hash_types_test.cc" ] + + # This makes `ld` emit both .hash and .gnu.hash sections. + ldflags = [ "-Wl,--hash-style=both" ] + } +} + +if (crashpad_is_mac || crashpad_is_ios) { + crashpad_loadable_module("crashpad_snapshot_test_module_crashy_initializer") { + testonly = true + sources = [ + "mac/mach_o_image_annotations_reader_test_module_crashy_initializer.cc", + ] + } + + crashpad_executable("crashpad_snapshot_test_no_op") { + testonly = true + sources = [ "mac/mach_o_image_annotations_reader_test_no_op.cc" ] + } +} + +if (crashpad_is_win) { + crashpad_executable("crashpad_snapshot_test_annotations") { + testonly = true + sources = [ "win/crashpad_snapshot_test_annotations.cc" ] + deps = [ + "$mini_chromium_source_parent:base", + "../client", + "../compat", + ] + } + + crashpad_executable("crashpad_snapshot_test_crashing_child") { + testonly = true + sources = [ "win/crashpad_snapshot_test_crashing_child.cc" ] + deps = [ + "$mini_chromium_source_parent:base", + "../client", + "../compat", + "../util", + ] + } + + crashpad_executable("crashpad_snapshot_test_dump_without_crashing") { + testonly = true + sources = [ "win/crashpad_snapshot_test_dump_without_crashing.cc" ] + deps = [ + "$mini_chromium_source_parent:base", + "../client", + "../compat", + "../util", + ] + } + + crashpad_executable("crashpad_snapshot_test_extra_memory_ranges") { + testonly = true + sources = [ "win/crashpad_snapshot_test_extra_memory_ranges.cc" ] + deps = [ + "$mini_chromium_source_parent:base", + "../client", + "../compat", + ] + } + + crashpad_executable("crashpad_snapshot_test_image_reader") { + testonly = true + sources = [ "win/crashpad_snapshot_test_image_reader.cc" ] + deps = [ + "$mini_chromium_source_parent:base", + "../client", + "../compat", + "../util", + ] + if (crashpad_is_in_chromium) { + if (symbol_level == 0) { + # The tests that use this executable rely on at least minimal debug + # info. + remove_configs = [ "//build/config/compiler:default_symbols" ] + configs = [ "//build/config/compiler:minimal_symbols" ] + } + } + } + + crashpad_loadable_module("crashpad_snapshot_test_image_reader_module") { + testonly = true + sources = [ "win/crashpad_snapshot_test_image_reader_module.cc" ] + deps = [ + "$mini_chromium_source_parent:base", + "../client", + ] + if (crashpad_is_in_chromium) { + if (symbol_level == 0) { + # The tests that use this module rely on at least minimal debug info. + remove_configs = [ "//build/config/compiler:default_symbols" ] + configs = [ "//build/config/compiler:minimal_symbols" ] + } + } + } +} diff --git a/shared/sentry/external/crashpad/snapshot/CMakeLists.txt b/shared/sentry/external/crashpad/snapshot/CMakeLists.txt new file mode 100644 index 000000000..f83f4b417 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/CMakeLists.txt @@ -0,0 +1,248 @@ +add_library(crashpad_snapshot STATIC + annotation_snapshot.cc + annotation_snapshot.h + capture_memory.cc + capture_memory.h + cpu_architecture.h + cpu_context.cc + cpu_context.h + crashpad_info_client_options.cc + crashpad_info_client_options.h + exception_snapshot.h + handle_snapshot.cc + handle_snapshot.h + memory_snapshot.cc + memory_snapshot.h + memory_snapshot_generic.h + minidump/exception_snapshot_minidump.cc + minidump/exception_snapshot_minidump.h + minidump/memory_snapshot_minidump.cc + minidump/memory_snapshot_minidump.h + minidump/minidump_annotation_reader.cc + minidump/minidump_annotation_reader.h + minidump/minidump_context_converter.cc + minidump/minidump_context_converter.h + minidump/minidump_simple_string_dictionary_reader.cc + minidump/minidump_simple_string_dictionary_reader.h + minidump/minidump_stream.h + minidump/minidump_string_list_reader.cc + minidump/minidump_string_list_reader.h + minidump/minidump_string_reader.cc + minidump/minidump_string_reader.h + minidump/module_snapshot_minidump.cc + minidump/module_snapshot_minidump.h + minidump/process_snapshot_minidump.cc + minidump/process_snapshot_minidump.h + minidump/system_snapshot_minidump.cc + minidump/system_snapshot_minidump.h + minidump/thread_snapshot_minidump.cc + minidump/thread_snapshot_minidump.h + module_snapshot.h + process_snapshot.h + snapshot_constants.h + system_snapshot.h + thread_snapshot.h + unloaded_module_snapshot.cc + unloaded_module_snapshot.h +) + +if(APPLE AND NOT IOS) + target_sources(crashpad_snapshot PRIVATE + posix/timezone.cc + posix/timezone.h + mac/cpu_context_mac.cc + mac/cpu_context_mac.h + mac/exception_snapshot_mac.cc + mac/exception_snapshot_mac.h + mac/mach_o_image_annotations_reader.cc + mac/mach_o_image_annotations_reader.h + mac/mach_o_image_reader.cc + mac/mach_o_image_reader.h + mac/mach_o_image_segment_reader.cc + mac/mach_o_image_segment_reader.h + mac/mach_o_image_symbol_table_reader.cc + mac/mach_o_image_symbol_table_reader.h + mac/module_snapshot_mac.cc + mac/module_snapshot_mac.h + mac/process_reader_mac.cc + mac/process_reader_mac.h + mac/process_snapshot_mac.cc + mac/process_snapshot_mac.h + mac/process_types.cc + mac/process_types.h + mac/process_types/custom.cc + mac/process_types/flavors.h + mac/process_types/internal.h + mac/process_types/traits.h + mac/system_snapshot_mac.cc + mac/system_snapshot_mac.h + mac/thread_snapshot_mac.cc + mac/thread_snapshot_mac.h + ) +elseif(IOS) + target_sources(crashpad_snapshot PRIVATE + posix/timezone.cc + posix/timezone.h + ios/exception_snapshot_ios_intermediate_dump.cc + ios/exception_snapshot_ios_intermediate_dump.h + ios/intermediate_dump_reader_util.cc + ios/intermediate_dump_reader_util.h + ios/memory_snapshot_ios_intermediate_dump.cc + ios/memory_snapshot_ios_intermediate_dump.h + ios/module_snapshot_ios_intermediate_dump.cc + ios/module_snapshot_ios_intermediate_dump.h + ios/process_snapshot_ios_intermediate_dump.cc + ios/process_snapshot_ios_intermediate_dump.h + ios/system_snapshot_ios_intermediate_dump.cc + ios/system_snapshot_ios_intermediate_dump.h + ios/thread_snapshot_ios_intermediate_dump.cc + ios/thread_snapshot_ios_intermediate_dump.h + mac/cpu_context_mac.cc + mac/cpu_context_mac.h + ) +else() + target_sources(crashpad_snapshot PRIVATE + crashpad_types/crashpad_info_reader.cc + crashpad_types/crashpad_info_reader.h + ) +endif() + +if(LINUX OR ANDROID) + target_sources(crashpad_snapshot PRIVATE + posix/timezone.cc + posix/timezone.h + linux/capture_memory_delegate_linux.cc + linux/capture_memory_delegate_linux.h + linux/cpu_context_linux.cc + linux/cpu_context_linux.h + linux/debug_rendezvous.cc + linux/debug_rendezvous.h + linux/exception_snapshot_linux.cc + linux/exception_snapshot_linux.h + linux/process_reader_linux.cc + linux/process_reader_linux.h + linux/process_snapshot_linux.cc + linux/process_snapshot_linux.h + linux/signal_context.h + linux/system_snapshot_linux.cc + linux/system_snapshot_linux.h + linux/thread_snapshot_linux.cc + linux/thread_snapshot_linux.h + sanitized/memory_snapshot_sanitized.cc + sanitized/memory_snapshot_sanitized.h + sanitized/module_snapshot_sanitized.cc + sanitized/module_snapshot_sanitized.h + sanitized/process_snapshot_sanitized.cc + sanitized/process_snapshot_sanitized.h + sanitized/sanitization_information.cc + sanitized/sanitization_information.h + sanitized/thread_snapshot_sanitized.cc + sanitized/thread_snapshot_sanitized.h + crashpad_types/image_annotation_reader.cc + crashpad_types/image_annotation_reader.h + elf/elf_dynamic_array_reader.cc + elf/elf_dynamic_array_reader.h + elf/elf_image_reader.cc + elf/elf_image_reader.h + elf/elf_symbol_table_reader.cc + elf/elf_symbol_table_reader.h + elf/module_snapshot_elf.cc + elf/module_snapshot_elf.h + ) +endif() + +if(WIN32) + target_sources(crashpad_snapshot PRIVATE + win/capture_memory_delegate_win.cc + win/capture_memory_delegate_win.h + win/cpu_context_win.cc + win/cpu_context_win.h + win/exception_snapshot_win.cc + win/exception_snapshot_win.h + win/memory_map_region_snapshot_win.cc + win/memory_map_region_snapshot_win.h + win/module_snapshot_win.cc + win/module_snapshot_win.h + win/pe_image_annotations_reader.cc + win/pe_image_annotations_reader.h + win/pe_image_reader.cc + win/pe_image_reader.h + win/pe_image_resource_reader.cc + win/pe_image_resource_reader.h + win/process_reader_win.cc + win/process_reader_win.h + win/process_snapshot_win.cc + win/process_snapshot_win.h + win/process_subrange_reader.cc + win/process_subrange_reader.h + win/system_snapshot_win.cc + win/system_snapshot_win.h + win/thread_snapshot_win.cc + win/thread_snapshot_win.h + ) +endif() + +if(CMAKE_SYSTEM_PROCESSOR MATCHES "(x86_64)|(x86)|(i[3-7]86)") + target_sources(crashpad_snapshot PRIVATE + x86/cpuid_reader.cc + x86/cpuid_reader.h + ) +endif() + +target_include_directories(crashpad_snapshot INTERFACE + "$" +) +target_link_libraries(crashpad_snapshot + PRIVATE + $ + PUBLIC + crashpad_client + crashpad_compat + crashpad_util + mini_chromium +) + +if(CRASHPAD_ENABLE_STACKTRACE) + target_compile_definitions(crashpad_snapshot PRIVATE CLIENT_STACKTRACES_ENABLED) +endif() + +if(WIN32) + target_link_libraries(crashpad_snapshot PRIVATE powrprof) + if(CRASHPAD_ENABLE_STACKTRACE) + target_link_libraries(crashpad_snapshot PRIVATE dbghelp) + endif() + if(MSVC) + target_compile_options(crashpad_snapshot PRIVATE "/wd4201") + endif() + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(crashpad_snapshot PRIVATE + "-Wno-attributes" + ) + elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + target_compile_options(crashpad_snapshot PRIVATE + "-Wno-unknown-attributes" + ) + endif() +endif() + +if(APPLE AND NOT IOS AND CRASHPAD_ENABLE_STACKTRACE) + target_include_directories(crashpad_snapshot PRIVATE ../libunwind/include) + target_link_libraries(crashpad_snapshot PRIVATE unwind_static) +endif() + +if(LINUX AND CRASHPAD_ENABLE_STACKTRACE) + find_package(PkgConfig REQUIRED) + pkg_check_modules(UNWIND REQUIRED IMPORTED_TARGET libunwind-ptrace) + target_link_libraries(crashpad_snapshot PRIVATE PkgConfig::UNWIND) +endif() + +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") + target_compile_options(crashpad_snapshot PRIVATE + "-Wno-multichar" + ) +endif() + +set_property(TARGET crashpad_snapshot PROPERTY EXPORT_NAME snapshot) +add_library(crashpad::snapshot ALIAS crashpad_snapshot) + +crashpad_install_target(crashpad_snapshot) diff --git a/shared/sentry/external/crashpad/snapshot/annotation_snapshot.cc b/shared/sentry/external/crashpad/snapshot/annotation_snapshot.cc new file mode 100644 index 000000000..c3350ec64 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/annotation_snapshot.cc @@ -0,0 +1,32 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/annotation_snapshot.h" + +namespace crashpad { + +AnnotationSnapshot::AnnotationSnapshot() : name(), type(0), value() {} + +AnnotationSnapshot::AnnotationSnapshot(const std::string& name, + uint16_t type, + const std::vector& value) + : name(name), type(type), value(value) {} + +AnnotationSnapshot::~AnnotationSnapshot() = default; + +bool AnnotationSnapshot::operator==(const AnnotationSnapshot& other) const { + return name == other.name && type == other.type && value == other.value; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/annotation_snapshot.h b/shared/sentry/external/crashpad/snapshot/annotation_snapshot.h new file mode 100644 index 000000000..11de475dd --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/annotation_snapshot.h @@ -0,0 +1,54 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_ANNOTATION_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_ANNOTATION_SNAPSHOT_H_ + +#include + +#include +#include + +namespace crashpad { + +// \!brief The snapshot representation of a client's Annotation. +struct AnnotationSnapshot { + AnnotationSnapshot(); + AnnotationSnapshot(const std::string& name, + uint16_t type, + const std::vector& value); + ~AnnotationSnapshot(); + + bool operator==(const AnnotationSnapshot& other) const; + bool operator!=(const AnnotationSnapshot& other) const { + return !(*this == other); + } + + //! \brief A non-unique name by which this annotation can be identified. + std::string name; + + //! \brief The Annotation::Type of data stored in the annotation. This value + //! may be client-supplied and need not correspond to a Crashpad-defined + //! type. + uint16_t type; + + //! \brief The data for the annotation. Guranteed to be non-empty, since + //! empty annotations are skipped. The representation of the data should + //! be interpreted as \a #type. + std::vector value; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_ANNOTATION_SNAPSHOT_H_ diff --git a/shared/sentry/external/crashpad/snapshot/capture_memory.cc b/shared/sentry/external/crashpad/snapshot/capture_memory.cc new file mode 100644 index 000000000..06d925813 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/capture_memory.cc @@ -0,0 +1,146 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/capture_memory.h" + +#include + +#include +#include +#include + +#include "base/logging.h" +#include "snapshot/memory_snapshot.h" + +namespace crashpad { +namespace internal { + +namespace { + +void MaybeCaptureMemoryAround(CaptureMemory::Delegate* delegate, + uint64_t address) { + constexpr uint64_t non_address_offset = 0x10000; + if (address < non_address_offset) + return; + + const uint64_t max_address = delegate->Is64Bit() ? + std::numeric_limits::max() : + std::numeric_limits::max(); + if (address > max_address - non_address_offset) + return; + + constexpr uint64_t kRegisterByteOffset = 128; + const uint64_t target = address - kRegisterByteOffset; + constexpr uint64_t size = 512; + static_assert(kRegisterByteOffset <= size / 2, + "negative offset too large"); + auto ranges = + delegate->GetReadableRanges(CheckedRange(target, size)); + for (const auto& range : ranges) { + delegate->AddNewMemorySnapshot(range); + } +} + +template +void CaptureAtPointersInRange(uint8_t* buffer, + uint64_t buffer_size, + CaptureMemory::Delegate* delegate) { + for (uint64_t address_offset = 0; address_offset < buffer_size; + address_offset += sizeof(T)) { + uint64_t target_address = *reinterpret_cast(&buffer[address_offset]); + MaybeCaptureMemoryAround(delegate, target_address); + } +} + +} // namespace + +// static +void CaptureMemory::PointedToByContext(const CPUContext& context, + Delegate* delegate) { +#if defined(ARCH_CPU_X86_FAMILY) + if (context.architecture == kCPUArchitectureX86_64) { + MaybeCaptureMemoryAround(delegate, context.x86_64->rax); + MaybeCaptureMemoryAround(delegate, context.x86_64->rbx); + MaybeCaptureMemoryAround(delegate, context.x86_64->rcx); + MaybeCaptureMemoryAround(delegate, context.x86_64->rdx); + MaybeCaptureMemoryAround(delegate, context.x86_64->rdi); + MaybeCaptureMemoryAround(delegate, context.x86_64->rsi); + MaybeCaptureMemoryAround(delegate, context.x86_64->rbp); + MaybeCaptureMemoryAround(delegate, context.x86_64->r8); + MaybeCaptureMemoryAround(delegate, context.x86_64->r9); + MaybeCaptureMemoryAround(delegate, context.x86_64->r10); + MaybeCaptureMemoryAround(delegate, context.x86_64->r11); + MaybeCaptureMemoryAround(delegate, context.x86_64->r12); + MaybeCaptureMemoryAround(delegate, context.x86_64->r13); + MaybeCaptureMemoryAround(delegate, context.x86_64->r14); + MaybeCaptureMemoryAround(delegate, context.x86_64->r15); + MaybeCaptureMemoryAround(delegate, context.x86_64->rip); + } else { + MaybeCaptureMemoryAround(delegate, context.x86->eax); + MaybeCaptureMemoryAround(delegate, context.x86->ebx); + MaybeCaptureMemoryAround(delegate, context.x86->ecx); + MaybeCaptureMemoryAround(delegate, context.x86->edx); + MaybeCaptureMemoryAround(delegate, context.x86->edi); + MaybeCaptureMemoryAround(delegate, context.x86->esi); + MaybeCaptureMemoryAround(delegate, context.x86->ebp); + MaybeCaptureMemoryAround(delegate, context.x86->eip); + } +#elif defined(ARCH_CPU_ARM_FAMILY) + if (context.architecture == kCPUArchitectureARM64) { + MaybeCaptureMemoryAround(delegate, context.arm64->pc); + for (size_t i = 0; i < std::size(context.arm64->regs); ++i) { + MaybeCaptureMemoryAround(delegate, context.arm64->regs[i]); + } + } else { + MaybeCaptureMemoryAround(delegate, context.arm->pc); + for (size_t i = 0; i < std::size(context.arm->regs); ++i) { + MaybeCaptureMemoryAround(delegate, context.arm->regs[i]); + } + } +#elif defined(ARCH_CPU_MIPS_FAMILY) + for (size_t i = 0; i < std::size(context.mipsel->regs); ++i) { + MaybeCaptureMemoryAround(delegate, context.mipsel->regs[i]); + } +#else +#error Port. +#endif +} + +// static +void CaptureMemory::PointedToByMemoryRange(const MemorySnapshot& memory, + Delegate* delegate) { + if (memory.Size() == 0) + return; + + const size_t alignment = + delegate->Is64Bit() ? sizeof(uint64_t) : sizeof(uint32_t); + if (memory.Address() % alignment != 0 || memory.Size() % alignment != 0) { + LOG(ERROR) << "unaligned range"; + return; + } + + std::unique_ptr buffer(new uint8_t[memory.Size()]); + if (!delegate->ReadMemory(memory.Address(), memory.Size(), buffer.get())) { + LOG(ERROR) << "ReadMemory"; + return; + } + + if (delegate->Is64Bit()) + CaptureAtPointersInRange(buffer.get(), memory.Size(), delegate); + else + CaptureAtPointersInRange(buffer.get(), memory.Size(), delegate); +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/capture_memory.h b/shared/sentry/external/crashpad/snapshot/capture_memory.h new file mode 100644 index 000000000..c4ca1f550 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/capture_memory.h @@ -0,0 +1,99 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_CAPTURE_MEMORY_H_ +#define CRASHPAD_SNAPSHOT_CAPTURE_MEMORY_H_ + +#include + +#include + +#include "snapshot/cpu_context.h" +#include "util/numeric/checked_range.h" + +namespace crashpad { + +class MemorySnapshot; + +namespace internal { + +class CaptureMemory { + public: + //! \brief An interface to a platform-specific process reader. + class Delegate { + public: + virtual ~Delegate() {} + + //! \return `true` if the target process is a 64-bit process. + virtual bool Is64Bit() const = 0; + + //! \brief Attempts to read \a num_bytes bytes from the target process + //! starting at address \a at into \a into. + //! + //! \return `true` if the entire region could be read, or `false` with an + //! error logged. + virtual bool ReadMemory(uint64_t at, + uint64_t num_bytes, + void* into) const = 0; + + //! \brief Given a range to be read from the target process, returns a + //! vector + //! of ranges, representing the readable portions of the original range. + //! + //! \param[in] range The range being identified. + //! + //! \return A vector of ranges corresponding to the portion of \a range that + //! is readable. + virtual std::vector> GetReadableRanges( + const CheckedRange& range) const = 0; + + //! \brief Adds the given range representing a memory snapshot in the target + //! process to the result. + virtual void AddNewMemorySnapshot( + const CheckedRange& range) = 0; + }; + + CaptureMemory() = delete; + CaptureMemory(const CaptureMemory&) = delete; + CaptureMemory& operator=(const CaptureMemory&) = delete; + + //! \brief For all registers that appear to be pointer-like in \a context, + //! captures a small amount of memory near their pointed to location. + //! + //! "Pointer-like" in this context means not too close to zero (signed or + //! unsigned) so that there's a reasonable chance that the value is a pointer. + //! + //! \param[in] context The context to inspect. + //! \param[in] delegate A Delegate that handles reading from the target + //! process and adding new ranges. + static void PointedToByContext(const CPUContext& context, Delegate* delegate); + + //! \brief For all pointer-like values in a memory range of the target + //! process, + //! captures a small amount of memory near the pointed to location. + //! + //! \param[in] memory An existing MemorySnapshot of the range to search. The + //! base address and size must be pointer-aligned and an integral number + //! of + //! pointers long. + //! \param[in] delegate A Delegate that handles reading from the target + //! process and adding new ranges. + static void PointedToByMemoryRange(const MemorySnapshot& memory, + Delegate* delegate); +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_CAPTURE_MEMORY_H_ diff --git a/shared/sentry/external/crashpad/snapshot/cpu_architecture.h b/shared/sentry/external/crashpad/snapshot/cpu_architecture.h new file mode 100644 index 000000000..811a72095 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/cpu_architecture.h @@ -0,0 +1,51 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_SNAPSHOT_CPU_ARCHITECTURE_H_ +#define CRASHPAD_SNAPSHOT_SNAPSHOT_CPU_ARCHITECTURE_H_ + +namespace crashpad { + +//! \brief A system’s CPU architecture. +//! +//! This can be used to represent the CPU architecture of an entire system +//! as in SystemSnapshot::CPUArchitecture(). It can also be used to represent +//! the architecture of a CPUContext structure in its CPUContext::architecture +//! field without reference to external data. +enum CPUArchitecture { + //! \brief The CPU architecture is unknown. + kCPUArchitectureUnknown = 0, + + //! \brief 32-bit x86. + kCPUArchitectureX86, + + //! \brief x86_64. + kCPUArchitectureX86_64, + + //! \brief 32-bit ARM. + kCPUArchitectureARM, + + //! \brief 64-bit ARM. + kCPUArchitectureARM64, + + //! \brief 32-bit MIPSEL. + kCPUArchitectureMIPSEL, + + //! \brief 64-bit MIPSEL. + kCPUArchitectureMIPS64EL +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_SNAPSHOT_CPU_ARCHITECTURE_H_ diff --git a/shared/sentry/external/crashpad/snapshot/cpu_context.cc b/shared/sentry/external/crashpad/snapshot/cpu_context.cc new file mode 100644 index 000000000..2e29f7039 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/cpu_context.cc @@ -0,0 +1,211 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/cpu_context.h" + +#include +#include + +#include + +#include "base/notreached.h" +#include "util/misc/arraysize.h" +#include "util/misc/implicit_cast.h" + +namespace crashpad { + +namespace { + +// Sanity-check complex structures to ensure interoperability. +static_assert(sizeof(CPUContextX86::Fsave) == 108, "CPUContextX86::Fsave size"); +static_assert(sizeof(CPUContextX86::Fxsave) == 512, + "CPUContextX86::Fxsave size"); +static_assert(sizeof(CPUContextX86_64::Fxsave) == 512, + "CPUContextX86_64::Fxsave size"); + +enum { + kX87TagValid = 0, + kX87TagZero, + kX87TagSpecial, + kX87TagEmpty, +}; + +} // namespace + +// static +void CPUContextX86::FxsaveToFsave(const Fxsave& fxsave, Fsave* fsave) { + fsave->fcw = fxsave.fcw; + fsave->reserved_1 = 0; + fsave->fsw = fxsave.fsw; + fsave->reserved_2 = 0; + fsave->ftw = FxsaveToFsaveTagWord(fxsave.fsw, fxsave.ftw, fxsave.st_mm); + fsave->reserved_3 = 0; + fsave->fpu_ip = fxsave.fpu_ip; + fsave->fpu_cs = fxsave.fpu_cs; + fsave->fop = fxsave.fop; + fsave->fpu_dp = fxsave.fpu_dp; + fsave->fpu_ds = fxsave.fpu_ds; + fsave->reserved_4 = 0; + static_assert(ArraySize(fsave->st) == ArraySize(fxsave.st_mm), + "FPU stack registers must be equivalent"); + for (size_t index = 0; index < std::size(fsave->st); ++index) { + memcpy(fsave->st[index], fxsave.st_mm[index].st, sizeof(fsave->st[index])); + } +} + +// static +void CPUContextX86::FsaveToFxsave(const Fsave& fsave, Fxsave* fxsave) { + fxsave->fcw = fsave.fcw; + fxsave->fsw = fsave.fsw; + fxsave->ftw = FsaveToFxsaveTagWord(fsave.ftw); + fxsave->reserved_1 = 0; + fxsave->fop = fsave.fop; + fxsave->fpu_ip = fsave.fpu_ip; + fxsave->fpu_cs = fsave.fpu_cs; + fxsave->reserved_2 = 0; + fxsave->fpu_dp = fsave.fpu_dp; + fxsave->fpu_ds = fsave.fpu_ds; + fxsave->reserved_3 = 0; + fxsave->mxcsr = 0; + fxsave->mxcsr_mask = 0; + static_assert(ArraySize(fxsave->st_mm) == ArraySize(fsave.st), + "FPU stack registers must be equivalent"); + for (size_t index = 0; index < std::size(fsave.st); ++index) { + memcpy(fxsave->st_mm[index].st, fsave.st[index], sizeof(fsave.st[index])); + memset(fxsave->st_mm[index].st_reserved, + 0, + sizeof(fxsave->st_mm[index].st_reserved)); + } + memset(fxsave->xmm, 0, sizeof(*fxsave) - offsetof(Fxsave, xmm)); +} + +// static +uint16_t CPUContextX86::FxsaveToFsaveTagWord( + uint16_t fsw, + uint8_t fxsave_tag, + const CPUContextX86::X87OrMMXRegister st_mm[8]) { + // The x87 tag word (in both abridged and full form) identifies physical + // registers, but |st_mm| is arranged in logical stack order. In order to map + // physical tag word bits to the logical stack registers they correspond to, + // the “stack top†value from the x87 status word is necessary. + int stack_top = (fsw >> 11) & 0x7; + + uint16_t fsave_tag = 0; + for (int physical_index = 0; physical_index < 8; ++physical_index) { + bool fxsave_bit = (fxsave_tag & (1 << physical_index)) != 0; + uint8_t fsave_bits; + + if (fxsave_bit) { + int st_index = (physical_index + 8 - stack_top) % 8; + const CPUContextX86::X87Register& st = st_mm[st_index].st; + + uint32_t exponent = ((st[9] & 0x7f) << 8) | st[8]; + if (exponent == 0x7fff) { + // Infinity, NaN, pseudo-infinity, or pseudo-NaN. If it was important to + // distinguish between these, the J bit and the M bit (the most + // significant bit of |fraction|) could be consulted. + fsave_bits = kX87TagSpecial; + } else { + // The integer bit the “J bitâ€. + bool integer_bit = (st[7] & 0x80) != 0; + if (exponent == 0) { + uint64_t fraction = ((implicit_cast(st[7]) & 0x7f) << 56) | + (implicit_cast(st[6]) << 48) | + (implicit_cast(st[5]) << 40) | + (implicit_cast(st[4]) << 32) | + (implicit_cast(st[3]) << 24) | + (st[2] << 16) | (st[1] << 8) | st[0]; + if (!integer_bit && fraction == 0) { + fsave_bits = kX87TagZero; + } else { + // Denormal (if the J bit is clear) or pseudo-denormal. + fsave_bits = kX87TagSpecial; + } + } else if (integer_bit) { + fsave_bits = kX87TagValid; + } else { + // Unnormal. + fsave_bits = kX87TagSpecial; + } + } + } else { + fsave_bits = kX87TagEmpty; + } + + fsave_tag |= (fsave_bits << (physical_index * 2)); + } + + return fsave_tag; +} + +// static +uint8_t CPUContextX86::FsaveToFxsaveTagWord(uint16_t fsave_tag) { + uint8_t fxsave_tag = 0; + for (int physical_index = 0; physical_index < 8; ++physical_index) { + const uint8_t fsave_bits = (fsave_tag >> (physical_index * 2)) & 0x3; + const bool fxsave_bit = fsave_bits != kX87TagEmpty; + fxsave_tag |= fxsave_bit << physical_index; + } + return fxsave_tag; +} + +uint64_t CPUContext::InstructionPointer() const { + switch (architecture) { + case kCPUArchitectureX86: + return x86->eip; + case kCPUArchitectureX86_64: + return x86_64->rip; + case kCPUArchitectureARM: + return arm->pc; + case kCPUArchitectureARM64: + return arm64->pc; + default: + NOTREACHED(); + return ~0ull; + } +} + +uint64_t CPUContext::StackPointer() const { + switch (architecture) { + case kCPUArchitectureX86: + return x86->esp; + case kCPUArchitectureX86_64: + return x86_64->rsp; + case kCPUArchitectureARM: + return arm->sp; + case kCPUArchitectureARM64: + return arm64->sp; + default: + NOTREACHED(); + return ~0ull; + } +} + +bool CPUContext::Is64Bit() const { + switch (architecture) { + case kCPUArchitectureX86_64: + case kCPUArchitectureARM64: + case kCPUArchitectureMIPS64EL: + return true; + case kCPUArchitectureX86: + case kCPUArchitectureARM: + case kCPUArchitectureMIPSEL: + return false; + default: + NOTREACHED(); + return false; + } +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/cpu_context.h b/shared/sentry/external/crashpad/snapshot/cpu_context.h new file mode 100644 index 000000000..fb23c4679 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/cpu_context.h @@ -0,0 +1,390 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_SNAPSHOT_CPU_CONTEXT_H_ +#define CRASHPAD_SNAPSHOT_SNAPSHOT_CPU_CONTEXT_H_ + +#include + +#include "snapshot/cpu_architecture.h" +#include "util/numeric/int128.h" + +namespace crashpad { + +//! \brief A context structure carrying 32-bit x86 CPU state. +struct CPUContextX86 { + using X87Register = uint8_t[10]; + + struct Fsave { + uint16_t fcw; // FPU control word + uint16_t reserved_1; + uint16_t fsw; // FPU status word + uint16_t reserved_2; + uint16_t ftw; // full FPU tag word + uint16_t reserved_3; + uint32_t fpu_ip; // FPU instruction pointer offset + uint16_t fpu_cs; // FPU instruction pointer segment selector + uint16_t fop; // FPU opcode + uint32_t fpu_dp; // FPU data pointer offset + uint16_t fpu_ds; // FPU data pointer segment selector + uint16_t reserved_4; + X87Register st[8]; + }; + + union X87OrMMXRegister { + struct { + X87Register st; + uint8_t st_reserved[6]; + }; + struct { + uint8_t mm_value[8]; + uint8_t mm_reserved[8]; + }; + }; + + using XMMRegister = uint8_t[16]; + + struct Fxsave { + uint16_t fcw; // FPU control word + uint16_t fsw; // FPU status word + uint8_t ftw; // abridged FPU tag word + uint8_t reserved_1; + uint16_t fop; // FPU opcode + uint32_t fpu_ip; // FPU instruction pointer offset + uint16_t fpu_cs; // FPU instruction pointer segment selector + uint16_t reserved_2; + uint32_t fpu_dp; // FPU data pointer offset + uint16_t fpu_ds; // FPU data pointer segment selector + uint16_t reserved_3; + uint32_t mxcsr; // multimedia extensions status and control register + uint32_t mxcsr_mask; // valid bits in mxcsr + X87OrMMXRegister st_mm[8]; + XMMRegister xmm[8]; + uint8_t reserved_4[176]; + uint8_t available[48]; + }; + + //! \brief Converts an `fxsave` area to an `fsave` area. + //! + //! `fsave` state is restricted to the x87 FPU, while `fxsave` state includes + //! state related to the x87 FPU as well as state specific to SSE. + //! + //! As the `fxsave` format is a superset of the `fsave` format, this operation + //! fully populates the `fsave` area. `fsave` uses the full 16-bit form for + //! the x87 floating-point tag word, so FxsaveToFsaveTagWord() is used to + //! derive Fsave::ftw from the abridged 8-bit form used by `fxsave`. Reserved + //! fields in \a fsave are set to `0`. + //! + //! \param[in] fxsave The `fxsave` area to convert. + //! \param[out] fsave The `fsave` area to populate. + //! + //! \sa FsaveToFxsave() + static void FxsaveToFsave(const Fxsave& fxsave, Fsave* fsave); + + //! \brief Converts an `fsave` area to an `fxsave` area. + //! + //! `fsave` state is restricted to the x87 FPU, while `fxsave` state includes + //! state related to the x87 FPU as well as state specific to SSE. + //! + //! As the `fsave` format is a subset of the `fxsave` format, this operation + //! cannot fully populate the `fxsave` area. Fields in \a fxsave that have no + //! equivalent in \a fsave are set to `0`, including Fxsave::mxcsr, + //! Fxsave::mxcsr_mask, Fxsave::xmm, and Fxsave::available. + //! FsaveToFxsaveTagWord() is used to derive Fxsave::ftw from the full 16-bit + //! form used by `fsave`. Reserved fields in \a fxsave are set to `0`. + //! + //! \param[in] fsave The `fsave` area to convert. + //! \param[out] fxsave The `fxsave` area to populate. + //! + //! \sa FxsaveToFsave() + static void FsaveToFxsave(const Fsave& fsave, Fxsave* fxsave); + + //! \brief Converts x87 floating-point tag words from `fxsave` (abridged, + //! 8-bit) to `fsave` (full, 16-bit) form. + //! + //! `fxsave` stores the x87 floating-point tag word in abridged 8-bit form, + //! and `fsave` stores it in full 16-bit form. Some users, notably + //! CPUContextX86::Fsave::ftw, require the full 16-bit form, where most other + //! contemporary code uses `fxsave` and thus the abridged 8-bit form found in + //! CPUContextX86::Fxsave::ftw. + //! + //! This function converts an abridged tag word to the full version by using + //! the abridged tag word and the contents of the registers it describes. See + //! Intel Software Developer’s Manual, Volume 2A: Instruction Set Reference + //! A-M (253666-052), 3.2 “FXSAVEâ€, specifically, the notes on the abridged + //! FTW and recreating the FSAVE format, and AMD Architecture Programmer’s + //! Manual, Volume 2: System Programming (24593-3.24), “FXSAVE Format for x87 + //! Tag Wordâ€. + //! + //! \sa FsaveToFxsaveTagWord() + //! + //! \param[in] fsw The FPU status word, used to map logical \a st_mm registers + //! to their physical counterparts. This can be taken from + //! CPUContextX86::Fxsave::fsw. + //! \param[in] fxsave_tag The abridged FPU tag word. This can be taken from + //! CPUContextX86::Fxsave::ftw. + //! \param[in] st_mm The floating-point registers in logical order. This can + //! be taken from CPUContextX86::Fxsave::st_mm. + //! + //! \return The full FPU tag word. + static uint16_t FxsaveToFsaveTagWord( + uint16_t fsw, uint8_t fxsave_tag, const X87OrMMXRegister st_mm[8]); + + //! \brief Converts x87 floating-point tag words from `fsave` (full, 16-bit) + //! to `fxsave` (abridged, 8-bit) form. + //! + //! This function performs the inverse operation of FxsaveToFsaveTagWord(). + //! + //! \param[in] fsave_tag The full FPU tag word. + //! + //! \return The abridged FPU tag word. + static uint8_t FsaveToFxsaveTagWord(uint16_t fsave_tag); + + // Integer registers. + uint32_t eax; + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + uint32_t edi; // destination index + uint32_t esi; // source index + uint32_t ebp; // base pointer + uint32_t esp; // stack pointer + uint32_t eip; // instruction pointer + uint32_t eflags; + uint16_t cs; // code segment selector + uint16_t ds; // data segment selector + uint16_t es; // extra segment selector + uint16_t fs; + uint16_t gs; + uint16_t ss; // stack segment selector + + // Floating-point and vector registers. + Fxsave fxsave; + + // Debug registers. + uint32_t dr0; + uint32_t dr1; + uint32_t dr2; + uint32_t dr3; + uint32_t dr4; // obsolete, normally an alias for dr6 + uint32_t dr5; // obsolete, normally an alias for dr7 + uint32_t dr6; + uint32_t dr7; +}; + +//! \brief A context structure carrying x86_64 CPU state. +struct CPUContextX86_64 { + using X87Register = CPUContextX86::X87Register; + using X87OrMMXRegister = CPUContextX86::X87OrMMXRegister; + using XMMRegister = CPUContextX86::XMMRegister; + + struct Fxsave { + uint16_t fcw; // FPU control word + uint16_t fsw; // FPU status word + uint8_t ftw; // abridged FPU tag word + uint8_t reserved_1; + uint16_t fop; // FPU opcode + union { + // The expression of these union members is determined by the use of + // fxsave/fxrstor or fxsave64/fxrstor64 (fxsaveq/fxrstorq). macOS and + // Windows systems use the traditional fxsave/fxrstor structure. + struct { + // fxsave/fxrstor + uint32_t fpu_ip; // FPU instruction pointer offset + uint16_t fpu_cs; // FPU instruction pointer segment selector + uint16_t reserved_2; + uint32_t fpu_dp; // FPU data pointer offset + uint16_t fpu_ds; // FPU data pointer segment selector + uint16_t reserved_3; + }; + struct { + // fxsave64/fxrstor64 (fxsaveq/fxrstorq) + uint64_t fpu_ip_64; // FPU instruction pointer + uint64_t fpu_dp_64; // FPU data pointer + }; + }; + uint32_t mxcsr; // multimedia extensions status and control register + uint32_t mxcsr_mask; // valid bits in mxcsr + X87OrMMXRegister st_mm[8]; + XMMRegister xmm[16]; + uint8_t reserved_4[48]; + uint8_t available[48]; + }; + + // Integer registers. + uint64_t rax; + uint64_t rbx; + uint64_t rcx; + uint64_t rdx; + uint64_t rdi; // destination index + uint64_t rsi; // source index + uint64_t rbp; // base pointer + uint64_t rsp; // stack pointer + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t rip; // instruction pointer + uint64_t rflags; + uint16_t cs; // code segment selector + uint16_t fs; + uint16_t gs; + + // Floating-point and vector registers. + Fxsave fxsave; + + // Debug registers. + uint64_t dr0; + uint64_t dr1; + uint64_t dr2; + uint64_t dr3; + uint64_t dr4; // obsolete, normally an alias for dr6 + uint64_t dr5; // obsolete, normally an alias for dr7 + uint64_t dr6; + uint64_t dr7; +}; + +//! \brief A context structure carrying ARM CPU state. +struct CPUContextARM { + uint32_t regs[11]; + uint32_t fp; // r11 + uint32_t ip; // r12 + uint32_t sp; // r13 + uint32_t lr; // r14 + uint32_t pc; // r15 + uint32_t cpsr; + + struct { + struct fp_reg { + uint32_t sign1 : 1; + uint32_t unused : 15; + uint32_t sign2 : 1; + uint32_t exponent : 14; + uint32_t j : 1; + uint32_t mantissa1 : 31; + uint32_t mantisss0 : 32; + } fpregs[8]; + uint32_t fpsr : 32; + uint32_t fpcr : 32; + uint8_t type[8]; + uint32_t init_flag; + } fpa_regs; + + struct { + uint64_t vfp[32]; + uint32_t fpscr; + } vfp_regs; + + bool have_fpa_regs; + bool have_vfp_regs; +}; + +//! \brief A context structure carrying ARM64 CPU state. +struct CPUContextARM64 { + uint64_t regs[31]; + uint64_t sp; + uint64_t pc; + uint32_t spsr; + + uint128_struct fpsimd[32]; + uint32_t fpsr; + uint32_t fpcr; +}; + +//! \brief A context structure carrying MIPS CPU state. +struct CPUContextMIPS { + uint64_t regs[32]; + uint32_t mdlo; + uint32_t mdhi; + uint32_t cp0_epc; + uint32_t cp0_badvaddr; + uint32_t cp0_status; + uint32_t cp0_cause; + uint32_t hi[3]; + uint32_t lo[3]; + uint32_t dsp_control; + union { + double dregs[32]; + struct { + float _fp_fregs; + uint32_t _fp_pad; + } fregs[32]; + } fpregs; + uint32_t fpcsr; + uint32_t fir; +}; + +//! \brief A context structure carrying MIPS64 CPU state. +struct CPUContextMIPS64 { + uint64_t regs[32]; + uint64_t mdlo; + uint64_t mdhi; + uint64_t cp0_epc; + uint64_t cp0_badvaddr; + uint64_t cp0_status; + uint64_t cp0_cause; + uint64_t hi[3]; + uint64_t lo[3]; + uint64_t dsp_control; + union { + double dregs[32]; + struct { + float _fp_fregs; + uint32_t _fp_pad; + } fregs[32]; + } fpregs; + uint64_t fpcsr; + uint64_t fir; +}; + +//! \brief A context structure capable of carrying the context of any supported +//! CPU architecture. +struct CPUContext { + //! \brief Returns the instruction pointer value from the context structure. + //! + //! This is a CPU architecture-independent method that is capable of + //! recovering the instruction pointer from any supported CPU architecture’s + //! context structure. + uint64_t InstructionPointer() const; + + //! \brief Returns the stack pointer value from the context structure. + //! + //! This is a CPU architecture-independent method that is capable of + //! recovering the stack pointer from any supported CPU architecture’s + //! context structure. + uint64_t StackPointer() const; + + //! \brief Returns `true` if this context is for a 64-bit architecture. + bool Is64Bit() const; + + //! \brief The CPU architecture of a context structure. This field controls + //! the expression of the union. + CPUArchitecture architecture; + union { + CPUContextX86* x86; + CPUContextX86_64* x86_64; + CPUContextARM* arm; + CPUContextARM64* arm64; + CPUContextMIPS* mipsel; + CPUContextMIPS64* mips64; + }; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_SNAPSHOT_CPU_CONTEXT_H_ diff --git a/shared/sentry/external/crashpad/snapshot/cpu_context_test.cc b/shared/sentry/external/crashpad/snapshot/cpu_context_test.cc new file mode 100644 index 000000000..019c7694f --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/cpu_context_test.cc @@ -0,0 +1,348 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/cpu_context.h" + +#include +#include +#include + +#include + +#include "gtest/gtest.h" +#include "test/hex_string.h" + +namespace crashpad { +namespace test { +namespace { + +enum ExponentValue { + kExponentAllZero = 0, + kExponentAllOne, + kExponentNormal, +}; + +enum FractionValue { + kFractionAllZero = 0, + kFractionNormal, +}; + +//! \brief Initializes an x87 register to a known bit pattern. +//! +//! \param[out] st_mm The x87 register to initialize. The reserved portion of +//! the register is always zeroed out. +//! \param[in] exponent_value The bit pattern to use for the exponent. If this +//! is kExponentAllZero, the sign bit will be set to `1`, and if this is +//! kExponentAllOne, the sign bit will be set to `0`. This tests that the +//! implementation doesn’t erroneously consider the sign bit to be part of +//! the exponent. This may also be kExponentNormal, indicating that the +//! exponent shall neither be all zeroes nor all ones. +//! \param[in] j_bit The value to use for the “J bit†(“integer bitâ€). +//! \param[in] fraction_value If kFractionAllZero, the fraction will be zeroed +//! out. If kFractionNormal, the fraction will not be all zeroes. +void SetX87Register(CPUContextX86::X87Register* st, + ExponentValue exponent_value, + bool j_bit, + FractionValue fraction_value) { + switch (exponent_value) { + case kExponentAllZero: + (*st)[9] = 0x80; + (*st)[8] = 0; + break; + case kExponentAllOne: + (*st)[9] = 0x7f; + (*st)[8] = 0xff; + break; + case kExponentNormal: + (*st)[9] = 0x55; + (*st)[8] = 0x55; + break; + } + + uint8_t fraction_pattern = fraction_value == kFractionAllZero ? 0 : 0x55; + memset(st, fraction_pattern, 8); + + if (j_bit) { + (*st)[7] |= 0x80; + } else { + (*st)[7] &= ~0x80; + } +} + +//! \brief Initializes an x87 register to a known bit pattern. +//! +//! This behaves as SetX87Register() but also clears the reserved portion of the +//! field as used in the `fxsave` format. +void SetX87OrMMXRegister(CPUContextX86::X87OrMMXRegister* st_mm, + ExponentValue exponent_value, + bool j_bit, + FractionValue fraction_value) { + SetX87Register(&st_mm->st, exponent_value, j_bit, fraction_value); + memset(st_mm->st_reserved, 0, sizeof(st_mm->st_reserved)); +} + +TEST(CPUContextX86, FxsaveToFsave) { + // Establish a somewhat plausible fxsave state. Use nonzero values for + // reserved fields and things that aren’t present in fsave. + CPUContextX86::Fxsave fxsave; + fxsave.fcw = 0x027f; // mask exceptions, 53-bit precision, round to nearest + fxsave.fsw = 1 << 11; // top = 1: logical 0-7 maps to physical 1-7, 0 + fxsave.ftw = 0x1f; // physical 5-7 (logical 4-6) empty + fxsave.reserved_1 = 0x5a; + fxsave.fop = 0x1fe; // fsin + fxsave.fpu_ip = 0x76543210; + fxsave.fpu_cs = 0x0007; + fxsave.reserved_2 = 0x5a5a; + fxsave.fpu_dp = 0xfedcba98; + fxsave.fpu_ds = 0x000f; + fxsave.reserved_3 = 0x5a5a; + fxsave.mxcsr = 0x1f80; + fxsave.mxcsr_mask = 0xffff; + SetX87Register( + &fxsave.st_mm[0].st, kExponentNormal, true, kFractionAllZero); // valid + SetX87Register( + &fxsave.st_mm[1].st, kExponentAllZero, false, kFractionAllZero); // zero + SetX87Register( + &fxsave.st_mm[2].st, kExponentAllOne, true, kFractionAllZero); // spec. + SetX87Register( + &fxsave.st_mm[3].st, kExponentAllOne, true, kFractionNormal); // spec. + SetX87Register( + &fxsave.st_mm[4].st, kExponentAllZero, false, kFractionAllZero); + SetX87Register( + &fxsave.st_mm[5].st, kExponentAllZero, false, kFractionAllZero); + SetX87Register( + &fxsave.st_mm[6].st, kExponentAllZero, false, kFractionAllZero); + SetX87Register( + &fxsave.st_mm[7].st, kExponentNormal, true, kFractionNormal); // valid + for (size_t index = 0; index < std::size(fxsave.st_mm); ++index) { + memset(&fxsave.st_mm[index].st_reserved, + 0x5a, + sizeof(fxsave.st_mm[index].st_reserved)); + } + memset(&fxsave.xmm, 0x5a, sizeof(fxsave) - offsetof(decltype(fxsave), xmm)); + + CPUContextX86::Fsave fsave; + CPUContextX86::FxsaveToFsave(fxsave, &fsave); + + // Everything should have come over from fxsave. Reserved fields should be + // zero. + EXPECT_EQ(fsave.fcw, fxsave.fcw); + EXPECT_EQ(fsave.reserved_1, 0); + EXPECT_EQ(fsave.fsw, fxsave.fsw); + EXPECT_EQ(fsave.reserved_2, 0); + EXPECT_EQ(fsave.ftw, 0xfe90); // FxsaveToFsaveTagWord + EXPECT_EQ(fsave.reserved_3, 0); + EXPECT_EQ(fsave.fpu_ip, fxsave.fpu_ip); + EXPECT_EQ(fsave.fpu_cs, fxsave.fpu_cs); + EXPECT_EQ(fsave.fop, fxsave.fop); + EXPECT_EQ(fsave.fpu_dp, fxsave.fpu_dp); + EXPECT_EQ(fsave.fpu_ds, fxsave.fpu_ds); + EXPECT_EQ(fsave.reserved_4, 0); + for (size_t index = 0; index < std::size(fsave.st); ++index) { + EXPECT_EQ(BytesToHexString(fsave.st[index], std::size(fsave.st[index])), + BytesToHexString(fxsave.st_mm[index].st, + std::size(fxsave.st_mm[index].st))) + << "index " << index; + } +} + +TEST(CPUContextX86, FsaveToFxsave) { + // Establish a somewhat plausible fsave state. Use nonzero values for + // reserved fields. + CPUContextX86::Fsave fsave; + fsave.fcw = 0x0300; // unmask exceptions, 64-bit precision, round to nearest + fsave.reserved_1 = 0xa5a5; + fsave.fsw = 2 << 11; // top = 2: logical 0-7 maps to physical 2-7, 0-1 + fsave.reserved_2 = 0xa5a5; + fsave.ftw = 0xa9ff; // physical 0-3 (logical 6-7, 0-1) empty; physical 4 + // (logical 2) zero; physical 5-7 (logical 3-5) special + fsave.reserved_3 = 0xa5a5; + fsave.fpu_ip = 0x456789ab; + fsave.fpu_cs = 0x1013; + fsave.fop = 0x01ee; // fldz + fsave.fpu_dp = 0x0123cdef; + fsave.fpu_ds = 0x2017; + fsave.reserved_4 = 0xa5a5; + SetX87Register(&fsave.st[0], kExponentAllZero, false, kFractionNormal); + SetX87Register(&fsave.st[1], kExponentAllZero, true, kFractionNormal); + SetX87Register( + &fsave.st[2], kExponentAllZero, false, kFractionAllZero); // zero + SetX87Register( + &fsave.st[3], kExponentAllZero, true, kFractionAllZero); // spec. + SetX87Register( + &fsave.st[4], kExponentAllZero, false, kFractionNormal); // spec. + SetX87Register( + &fsave.st[5], kExponentAllZero, true, kFractionNormal); // spec. + SetX87Register(&fsave.st[6], kExponentAllZero, false, kFractionAllZero); + SetX87Register(&fsave.st[7], kExponentAllZero, true, kFractionAllZero); + + CPUContextX86::Fxsave fxsave; + CPUContextX86::FsaveToFxsave(fsave, &fxsave); + + // Everything in fsave should have come over from there. Fields not present in + // fsave and reserved fields should be zero. + EXPECT_EQ(fxsave.fcw, fsave.fcw); + EXPECT_EQ(fxsave.fsw, fsave.fsw); + EXPECT_EQ(fxsave.ftw, 0xf0); // FsaveToFxsaveTagWord + EXPECT_EQ(fxsave.reserved_1, 0); + EXPECT_EQ(fxsave.fop, fsave.fop); + EXPECT_EQ(fxsave.fpu_ip, fsave.fpu_ip); + EXPECT_EQ(fxsave.fpu_cs, fsave.fpu_cs); + EXPECT_EQ(fxsave.reserved_2, 0); + EXPECT_EQ(fxsave.fpu_dp, fsave.fpu_dp); + EXPECT_EQ(fxsave.fpu_ds, fsave.fpu_ds); + EXPECT_EQ(fxsave.reserved_3, 0); + EXPECT_EQ(fxsave.mxcsr, 0u); + EXPECT_EQ(fxsave.mxcsr_mask, 0u); + for (size_t index = 0; index < std::size(fxsave.st_mm); ++index) { + EXPECT_EQ(BytesToHexString(fxsave.st_mm[index].st, + std::size(fxsave.st_mm[index].st)), + BytesToHexString(fsave.st[index], std::size(fsave.st[index]))) + << "index " << index; + EXPECT_EQ(BytesToHexString(fxsave.st_mm[index].st_reserved, + std::size(fxsave.st_mm[index].st_reserved)), + std::string(std::size(fxsave.st_mm[index].st_reserved) * 2, '0')) + << "index " << index; + } + size_t unused_len = sizeof(fxsave) - offsetof(decltype(fxsave), xmm); + EXPECT_EQ(BytesToHexString(fxsave.xmm, unused_len), + std::string(unused_len * 2, '0')); + + // Since the fsave format is a subset of the fxsave format, fsave-fxsave-fsave + // should round-trip cleanly. + CPUContextX86::Fsave fsave_2; + CPUContextX86::FxsaveToFsave(fxsave, &fsave_2); + + // Clear the reserved fields in the original fsave structure, since they’re + // expected to be clear in the copy. + fsave.reserved_1 = 0; + fsave.reserved_2 = 0; + fsave.reserved_3 = 0; + fsave.reserved_4 = 0; + EXPECT_EQ(memcmp(&fsave, &fsave_2, sizeof(fsave)), 0); +} + +TEST(CPUContextX86, FxsaveToFsaveTagWord) { + // The fsave tag word uses bit pattern 00 for valid, 01 for zero, 10 for + // “specialâ€, and 11 for empty. Like the fxsave tag word, it is arranged by + // physical register. The fxsave tag word determines whether a register is + // empty, and analysis of the x87 register content distinguishes between + // valid, zero, and special. In the initializations below, comments show + // whether a register is expected to be considered valid, zero, or special, + // except where the tag word is expected to indicate that it is empty. Each + // combination appears twice: once where the fxsave tag word indicates a + // nonempty register, and once again where it indicates an empty register. + + uint16_t fsw = 0 << 11; // top = 0: logical 0-7 maps to physical 0-7 + uint8_t fxsave_tag = 0x0f; // physical 4-7 (logical 4-7) empty + CPUContextX86::X87OrMMXRegister st_mm[8]; + SetX87OrMMXRegister( + &st_mm[0], kExponentNormal, false, kFractionNormal); // spec. + SetX87OrMMXRegister( + &st_mm[1], kExponentNormal, true, kFractionNormal); // valid + SetX87OrMMXRegister( + &st_mm[2], kExponentNormal, false, kFractionAllZero); // spec. + SetX87OrMMXRegister( + &st_mm[3], kExponentNormal, true, kFractionAllZero); // valid + SetX87OrMMXRegister(&st_mm[4], kExponentNormal, false, kFractionNormal); + SetX87OrMMXRegister(&st_mm[5], kExponentNormal, true, kFractionNormal); + SetX87OrMMXRegister(&st_mm[6], kExponentNormal, false, kFractionAllZero); + SetX87OrMMXRegister(&st_mm[7], kExponentNormal, true, kFractionAllZero); + EXPECT_EQ(CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm), + 0xff22); + + fsw = 2 << 11; // top = 2: logical 0-7 maps to physical 2-7, 0-1 + fxsave_tag = 0xf0; // physical 0-3 (logical 6-7, 0-1) empty + SetX87OrMMXRegister(&st_mm[0], kExponentAllZero, false, kFractionNormal); + SetX87OrMMXRegister(&st_mm[1], kExponentAllZero, true, kFractionNormal); + SetX87OrMMXRegister( + &st_mm[2], kExponentAllZero, false, kFractionAllZero); // zero + SetX87OrMMXRegister( + &st_mm[3], kExponentAllZero, true, kFractionAllZero); // spec. + SetX87OrMMXRegister( + &st_mm[4], kExponentAllZero, false, kFractionNormal); // spec. + SetX87OrMMXRegister( + &st_mm[5], kExponentAllZero, true, kFractionNormal); // spec. + SetX87OrMMXRegister(&st_mm[6], kExponentAllZero, false, kFractionAllZero); + SetX87OrMMXRegister(&st_mm[7], kExponentAllZero, true, kFractionAllZero); + EXPECT_EQ(CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm), + 0xa9ff); + + fsw = 5 << 11; // top = 5: logical 0-7 maps to physical 5-7, 0-4 + fxsave_tag = 0x5a; // physical 0, 2, 5, and 7 (logical 5, 0, 2, and 3) empty + SetX87OrMMXRegister(&st_mm[0], kExponentAllOne, false, kFractionNormal); + SetX87OrMMXRegister( + &st_mm[1], kExponentAllOne, true, kFractionNormal); // spec. + SetX87OrMMXRegister(&st_mm[2], kExponentAllOne, false, kFractionAllZero); + SetX87OrMMXRegister(&st_mm[3], kExponentAllOne, true, kFractionAllZero); + SetX87OrMMXRegister( + &st_mm[4], kExponentAllOne, false, kFractionNormal); // spec. + SetX87OrMMXRegister(&st_mm[5], kExponentAllOne, true, kFractionNormal); + SetX87OrMMXRegister( + &st_mm[6], kExponentAllOne, false, kFractionAllZero); // spec. + SetX87OrMMXRegister( + &st_mm[7], kExponentAllOne, true, kFractionAllZero); // spec. + EXPECT_EQ(CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm), + 0xeebb); + + // This set set is just a mix of all of the possible tag types in a single + // register file. + fsw = 1 << 11; // top = 1: logical 0-7 maps to physical 1-7, 0 + fxsave_tag = 0x1f; // physical 5-7 (logical 4-6) empty + SetX87OrMMXRegister( + &st_mm[0], kExponentNormal, true, kFractionAllZero); // valid + SetX87OrMMXRegister( + &st_mm[1], kExponentAllZero, false, kFractionAllZero); // zero + SetX87OrMMXRegister( + &st_mm[2], kExponentAllOne, true, kFractionAllZero); // spec. + SetX87OrMMXRegister( + &st_mm[3], kExponentAllOne, true, kFractionNormal); // spec. + SetX87OrMMXRegister(&st_mm[4], kExponentAllZero, false, kFractionAllZero); + SetX87OrMMXRegister(&st_mm[5], kExponentAllZero, false, kFractionAllZero); + SetX87OrMMXRegister(&st_mm[6], kExponentAllZero, false, kFractionAllZero); + SetX87OrMMXRegister( + &st_mm[7], kExponentNormal, true, kFractionNormal); // valid + EXPECT_EQ(CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm), + 0xfe90); + + // In this set, everything is valid. + fsw = 0 << 11; // top = 0: logical 0-7 maps to physical 0-7 + fxsave_tag = 0xff; // nothing empty + for (size_t index = 0; index < std::size(st_mm); ++index) { + SetX87OrMMXRegister(&st_mm[index], kExponentNormal, true, kFractionAllZero); + } + EXPECT_EQ(CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm), 0); + + // In this set, everything is empty. The registers shouldn’t be consulted at + // all, so they’re left alone from the previous set. + fsw = 0 << 11; // top = 0: logical 0-7 maps to physical 0-7 + fxsave_tag = 0; // everything empty + EXPECT_EQ(CPUContextX86::FxsaveToFsaveTagWord(fsw, fxsave_tag, st_mm), + 0xffff); +} + +TEST(CPUContextX86, FsaveToFxsaveTagWord) { + // The register sets that these x87 tag words might apply to are given in the + // FxsaveToFsaveTagWord test above. + EXPECT_EQ(CPUContextX86::FsaveToFxsaveTagWord(0xff22), 0x0f); + EXPECT_EQ(CPUContextX86::FsaveToFxsaveTagWord(0xa9ff), 0xf0); + EXPECT_EQ(CPUContextX86::FsaveToFxsaveTagWord(0xeebb), 0x5a); + EXPECT_EQ(CPUContextX86::FsaveToFxsaveTagWord(0xfe90), 0x1f); + EXPECT_EQ(CPUContextX86::FsaveToFxsaveTagWord(0x0000), 0xff); + EXPECT_EQ(CPUContextX86::FsaveToFxsaveTagWord(0xffff), 0x00); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/crashpad_info_client_options.cc b/shared/sentry/external/crashpad/snapshot/crashpad_info_client_options.cc new file mode 100644 index 000000000..3a88a4c01 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/crashpad_info_client_options.cc @@ -0,0 +1,45 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/crashpad_info_client_options.h" + +#include "base/logging.h" + +namespace crashpad { + +// static +TriState CrashpadInfoClientOptions::TriStateFromCrashpadInfo( + uint8_t crashpad_info_tri_state) { + switch (crashpad_info_tri_state) { + case static_cast(TriState::kUnset): + return TriState::kUnset; + case static_cast(TriState::kEnabled): + return TriState::kEnabled; + case static_cast(TriState::kDisabled): + return TriState::kDisabled; + default: + LOG(WARNING) << "unknown TriState " + << static_cast(crashpad_info_tri_state); + return TriState::kUnset; + } +} + +CrashpadInfoClientOptions::CrashpadInfoClientOptions() + : crashpad_handler_behavior(TriState::kUnset), + system_crash_reporter_forwarding(TriState::kUnset), + gather_indirectly_referenced_memory(TriState::kUnset), + indirectly_referenced_memory_cap(0) { +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/crashpad_info_client_options.h b/shared/sentry/external/crashpad/snapshot/crashpad_info_client_options.h new file mode 100644 index 000000000..89c947a6c --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/crashpad_info_client_options.h @@ -0,0 +1,72 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_CRASHPAD_INFO_CLIENT_OPTIONS_H_ +#define CRASHPAD_SNAPSHOT_CRASHPAD_INFO_CLIENT_OPTIONS_H_ + +#include + +#include "util/misc/tri_state.h" + +namespace crashpad { + +//! \brief Options represented in a client’s CrashpadInfo structure. +//! +//! The CrashpadInfo structure is not suitable to expose client options in a +//! generic way at the snapshot level. This structure duplicates option-related +//! fields from the client structure for general use within the snapshot layer +//! and by users of this layer. +//! +//! For objects of this type corresponding to a module, option values are taken +//! from the module’s CrashpadInfo structure directly. If the module has no such +//! structure, option values appear unset. +//! +//! For objects of this type corresponding to an entire process, option values +//! are taken from the CrashpadInfo structures of modules within the process. +//! The first module found with a set value (enabled or disabled) will provide +//! an option value for the process. Different modules may provide values for +//! different options. If no module in the process sets a value for an option, +//! the option will appear unset for the process. If no module in the process +//! has a CrashpadInfo structure, all option values will appear unset. +struct CrashpadInfoClientOptions { + public: + //! \brief Converts `uint8_t` value to a TriState value. + //! + //! The process_types layer exposes TriState as a `uint8_t` rather than an + //! enum type. This function converts these values into the equivalent enum + //! values used in the snapshot layer. + //! + //! \return The TriState equivalent of \a crashpad_info_tri_state, if it is a + //! valid TriState value. Otherwise, logs a warning and returns + //! TriState::kUnset. + static TriState TriStateFromCrashpadInfo(uint8_t crashpad_info_tri_state); + + CrashpadInfoClientOptions(); + + //! \sa CrashpadInfo::set_crashpad_handler_behavior() + TriState crashpad_handler_behavior; + + //! \sa CrashpadInfo::set_system_crash_reporter_forwarding() + TriState system_crash_reporter_forwarding; + + //! \sa CrashpadInfo::set_gather_indirectly_referenced_memory() + TriState gather_indirectly_referenced_memory; + + //! \sa CrashpadInfo::set_gather_indirectly_referenced_memory() + uint32_t indirectly_referenced_memory_cap; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_CRASHPAD_INFO_CLIENT_OPTIONS_H_ diff --git a/shared/sentry/external/crashpad/snapshot/crashpad_info_client_options_test.cc b/shared/sentry/external/crashpad/snapshot/crashpad_info_client_options_test.cc new file mode 100644 index 000000000..73df677b5 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/crashpad_info_client_options_test.cc @@ -0,0 +1,338 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/crashpad_info_client_options.h" + +#include "base/auto_reset.h" +#include "base/files/file_path.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" +#include "client/crashpad_info.h" +#include "gtest/gtest.h" +#include "test/errors.h" +#include "test/scoped_module_handle.h" +#include "test/test_paths.h" + +#if BUILDFLAG(IS_APPLE) +#include +#include "snapshot/mac/process_snapshot_mac.h" +#elif BUILDFLAG(IS_WIN) +#include +#include "snapshot/win/process_snapshot_win.h" +#elif BUILDFLAG(IS_FUCHSIA) +#include +#include "snapshot/fuchsia/process_snapshot_fuchsia.h" +#endif + +namespace crashpad { +namespace test { +namespace { + +TEST(CrashpadInfoClientOptions, TriStateFromCrashpadInfo) { + EXPECT_EQ(CrashpadInfoClientOptions::TriStateFromCrashpadInfo(0), + TriState::kUnset); + EXPECT_EQ(CrashpadInfoClientOptions::TriStateFromCrashpadInfo(1), + TriState::kEnabled); + EXPECT_EQ(CrashpadInfoClientOptions::TriStateFromCrashpadInfo(2), + TriState::kDisabled); + + // These will produce log messages but should result in kUnset being returned. + EXPECT_EQ(CrashpadInfoClientOptions::TriStateFromCrashpadInfo(3), + TriState::kUnset); + EXPECT_EQ(CrashpadInfoClientOptions::TriStateFromCrashpadInfo(4), + TriState::kUnset); + EXPECT_EQ(CrashpadInfoClientOptions::TriStateFromCrashpadInfo(0xff), + TriState::kUnset); +} + +class ScopedUnsetCrashpadInfoOptions { + public: + explicit ScopedUnsetCrashpadInfoOptions(CrashpadInfo* crashpad_info) + : crashpad_info_(crashpad_info) { + } + + ScopedUnsetCrashpadInfoOptions(const ScopedUnsetCrashpadInfoOptions&) = + delete; + ScopedUnsetCrashpadInfoOptions& operator=( + const ScopedUnsetCrashpadInfoOptions&) = delete; + + ~ScopedUnsetCrashpadInfoOptions() { + crashpad_info_->set_crashpad_handler_behavior(TriState::kUnset); + crashpad_info_->set_system_crash_reporter_forwarding(TriState::kUnset); + crashpad_info_->set_gather_indirectly_referenced_memory(TriState::kUnset, + 0); + } + + private: + CrashpadInfo* crashpad_info_; +}; + +CrashpadInfoClientOptions SelfProcessSnapshotAndGetCrashpadOptions() { +#if BUILDFLAG(IS_APPLE) + ProcessSnapshotMac process_snapshot; + EXPECT_TRUE(process_snapshot.Initialize(mach_task_self())); +#elif BUILDFLAG(IS_WIN) + ProcessSnapshotWin process_snapshot; + EXPECT_TRUE(process_snapshot.Initialize( + GetCurrentProcess(), ProcessSuspensionState::kRunning, 0, 0)); +#elif BUILDFLAG(IS_FUCHSIA) + ProcessSnapshotFuchsia process_snapshot; + EXPECT_TRUE(process_snapshot.Initialize(*zx::process::self())); +#else +#error Port. +#endif // BUILDFLAG(IS_APPLE) + + CrashpadInfoClientOptions options; + process_snapshot.GetCrashpadOptions(&options); + return options; +} + +TEST(CrashpadInfoClientOptions, OneModule) { + // Make sure that the initial state has all values unset. + auto options = SelfProcessSnapshotAndGetCrashpadOptions(); + + EXPECT_EQ(options.crashpad_handler_behavior, TriState::kUnset); + EXPECT_EQ(options.system_crash_reporter_forwarding, TriState::kUnset); + EXPECT_EQ(options.gather_indirectly_referenced_memory, TriState::kUnset); + EXPECT_EQ(options.indirectly_referenced_memory_cap, 0u); + + CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo(); + ASSERT_TRUE(crashpad_info); + + { + ScopedUnsetCrashpadInfoOptions unset(crashpad_info); + + crashpad_info->set_crashpad_handler_behavior(TriState::kEnabled); + + options = SelfProcessSnapshotAndGetCrashpadOptions(); + EXPECT_EQ(options.crashpad_handler_behavior, TriState::kEnabled); + EXPECT_EQ(options.system_crash_reporter_forwarding, TriState::kUnset); + EXPECT_EQ(options.gather_indirectly_referenced_memory, TriState::kUnset); + EXPECT_EQ(options.indirectly_referenced_memory_cap, 0u); + } + + { + ScopedUnsetCrashpadInfoOptions unset(crashpad_info); + + crashpad_info->set_system_crash_reporter_forwarding(TriState::kDisabled); + + options = SelfProcessSnapshotAndGetCrashpadOptions(); + EXPECT_EQ(options.crashpad_handler_behavior, TriState::kUnset); + EXPECT_EQ(options.system_crash_reporter_forwarding, TriState::kDisabled); + EXPECT_EQ(options.gather_indirectly_referenced_memory, TriState::kUnset); + EXPECT_EQ(options.indirectly_referenced_memory_cap, 0u); + } + + { + ScopedUnsetCrashpadInfoOptions unset(crashpad_info); + + crashpad_info->set_gather_indirectly_referenced_memory(TriState::kEnabled, + 1234); + + options = SelfProcessSnapshotAndGetCrashpadOptions(); + EXPECT_EQ(options.crashpad_handler_behavior, TriState::kUnset); + EXPECT_EQ(options.system_crash_reporter_forwarding, TriState::kUnset); + EXPECT_EQ(options.gather_indirectly_referenced_memory, TriState::kEnabled); + EXPECT_LE(options.indirectly_referenced_memory_cap, 1234u); + } +} + +TEST(CrashpadInfoClientOptions, TwoModules) { + // Open the module, which has its own CrashpadInfo structure. + base::FilePath module_path = + TestPaths::BuildArtifact(FILE_PATH_LITERAL("snapshot"), + FILE_PATH_LITERAL("module"), + TestPaths::FileType::kLoadableModule); +#if BUILDFLAG(IS_POSIX) + ScopedModuleHandle module( + dlopen(module_path.value().c_str(), RTLD_LAZY | RTLD_LOCAL)); + ASSERT_TRUE(module.valid()) << "dlopen " << module_path.value() << ": " + << dlerror(); +#elif BUILDFLAG(IS_WIN) + ScopedModuleHandle module(LoadLibrary(module_path.value().c_str())); + ASSERT_TRUE(module.valid()) + << "LoadLibrary " << base::WideToUTF8(module_path.value()) << ": " + << ErrorMessage(); +#else +#error Port. +#endif // BUILDFLAG(IS_POSIX) + + // Get the function pointer from the module. This wraps GetCrashpadInfo(), but + // because it runs in the module, it returns the remote module’s CrashpadInfo + // structure. + CrashpadInfo* (*TestModule_GetCrashpadInfo)() = + module.LookUpSymbol("TestModule_GetCrashpadInfo"); + ASSERT_TRUE(TestModule_GetCrashpadInfo); + + auto options = SelfProcessSnapshotAndGetCrashpadOptions(); + + // Make sure that the initial state has all values unset. + EXPECT_EQ(options.crashpad_handler_behavior, TriState::kUnset); + EXPECT_EQ(options.system_crash_reporter_forwarding, TriState::kUnset); + EXPECT_EQ(options.gather_indirectly_referenced_memory, TriState::kUnset); + + // Get both CrashpadInfo structures. + CrashpadInfo* local_crashpad_info = CrashpadInfo::GetCrashpadInfo(); + ASSERT_TRUE(local_crashpad_info); + + CrashpadInfo* remote_crashpad_info = TestModule_GetCrashpadInfo(); + ASSERT_TRUE(remote_crashpad_info); + + { + ScopedUnsetCrashpadInfoOptions unset_local(local_crashpad_info); + ScopedUnsetCrashpadInfoOptions unset_remote(remote_crashpad_info); + + // When only one module sets a value, it applies to the entire process. + remote_crashpad_info->set_crashpad_handler_behavior(TriState::kEnabled); + + options = SelfProcessSnapshotAndGetCrashpadOptions(); + EXPECT_EQ(options.crashpad_handler_behavior, TriState::kEnabled); + EXPECT_EQ(options.system_crash_reporter_forwarding, TriState::kUnset); + EXPECT_EQ(options.gather_indirectly_referenced_memory, TriState::kUnset); + + // When more than one module sets a value, the first one in the module list + // applies to the process. The local module should appear before the remote + // module, because the local module loaded the remote module. + local_crashpad_info->set_crashpad_handler_behavior(TriState::kDisabled); + + options = SelfProcessSnapshotAndGetCrashpadOptions(); + EXPECT_EQ(options.crashpad_handler_behavior, TriState::kDisabled); + EXPECT_EQ(options.system_crash_reporter_forwarding, TriState::kUnset); + EXPECT_EQ(options.gather_indirectly_referenced_memory, TriState::kUnset); + } + + { + ScopedUnsetCrashpadInfoOptions unset_local(local_crashpad_info); + ScopedUnsetCrashpadInfoOptions unset_remote(remote_crashpad_info); + + // When only one module sets a value, it applies to the entire process. + remote_crashpad_info->set_system_crash_reporter_forwarding( + TriState::kDisabled); + + options = SelfProcessSnapshotAndGetCrashpadOptions(); + EXPECT_EQ(options.crashpad_handler_behavior, TriState::kUnset); + EXPECT_EQ(options.system_crash_reporter_forwarding, TriState::kDisabled); + EXPECT_EQ(options.gather_indirectly_referenced_memory, TriState::kUnset); + + // When more than one module sets a value, the first one in the module list + // applies to the process. The local module should appear before the remote + // module, because the local module loaded the remote module. + local_crashpad_info->set_system_crash_reporter_forwarding( + TriState::kEnabled); + + options = SelfProcessSnapshotAndGetCrashpadOptions(); + EXPECT_EQ(options.crashpad_handler_behavior, TriState::kUnset); + EXPECT_EQ(options.system_crash_reporter_forwarding, TriState::kEnabled); + EXPECT_EQ(options.gather_indirectly_referenced_memory, TriState::kUnset); + } +} + +class CrashpadInfoSizes_ClientOptions + : public testing::TestWithParam {}; + +TEST_P(CrashpadInfoSizes_ClientOptions, DifferentlySizedStruct) { + base::FilePath::StringType artifact(FILE_PATH_LITERAL("module_")); + artifact += GetParam(); + + // Open the module, which has a CrashpadInfo-like structure that’s smaller or + // larger than the current version’s CrashpadInfo structure defined in the + // client library. + base::FilePath module_path = + TestPaths::BuildArtifact(FILE_PATH_LITERAL("snapshot"), + artifact, + TestPaths::FileType::kLoadableModule); +#if BUILDFLAG(IS_POSIX) + ScopedModuleHandle module( + dlopen(module_path.value().c_str(), RTLD_LAZY | RTLD_LOCAL)); + ASSERT_TRUE(module.valid()) + << "dlopen " << module_path.value() << ": " << dlerror(); +#elif BUILDFLAG(IS_WIN) + ScopedModuleHandle module(LoadLibrary(module_path.value().c_str())); + ASSERT_TRUE(module.valid()) + << "LoadLibrary " << base::WideToUTF8(module_path.value()) << ": " + << ErrorMessage(); +#else +#error Port. +#endif // BUILDFLAG(IS_POSIX) + + // Get the function pointer from the module. + CrashpadInfo* (*TestModule_GetCrashpadInfo)() = + module.LookUpSymbol("TestModule_GetCrashpadInfo"); + ASSERT_TRUE(TestModule_GetCrashpadInfo); + + auto options = SelfProcessSnapshotAndGetCrashpadOptions(); + + // Make sure that the initial state has all values unset. + EXPECT_EQ(options.crashpad_handler_behavior, TriState::kUnset); + EXPECT_EQ(options.system_crash_reporter_forwarding, TriState::kUnset); + EXPECT_EQ(options.gather_indirectly_referenced_memory, TriState::kUnset); + + // Get the remote CrashpadInfo structure. + CrashpadInfo* remote_crashpad_info = TestModule_GetCrashpadInfo(); + ASSERT_TRUE(remote_crashpad_info); + + { + ScopedUnsetCrashpadInfoOptions unset_remote(remote_crashpad_info); + + // Make sure that a change in the remote structure can be read back out, + // even though it’s a different size. + remote_crashpad_info->set_crashpad_handler_behavior(TriState::kEnabled); + remote_crashpad_info->set_system_crash_reporter_forwarding( + TriState::kDisabled); + + options = SelfProcessSnapshotAndGetCrashpadOptions(); + EXPECT_EQ(options.crashpad_handler_behavior, TriState::kEnabled); + EXPECT_EQ(options.system_crash_reporter_forwarding, TriState::kDisabled); + EXPECT_EQ(options.gather_indirectly_referenced_memory, TriState::kUnset); + } + + { + ScopedUnsetCrashpadInfoOptions unset_remote(remote_crashpad_info); + + // Make sure that the portion of the remote structure lying beyond its + // declared size reads as zero. + + // 4 = offsetof(CrashpadInfo, size_), but it’s private. + uint32_t* size = reinterpret_cast( + reinterpret_cast(remote_crashpad_info) + 4); + + // 21 = offsetof(CrashpadInfo, system_crash_reporter_forwarding_, but it’s + // private. + base::AutoReset reset_size(size, 21); + + // system_crash_reporter_forwarding_ is now beyond the struct’s declared + // size. Storage has actually been allocated for it, so it’s safe to set + // here. + remote_crashpad_info->set_crashpad_handler_behavior(TriState::kEnabled); + remote_crashpad_info->set_system_crash_reporter_forwarding( + TriState::kDisabled); + + // Since system_crash_reporter_forwarding_ is beyond the struct’s declared + // size, it should read as 0 (TriState::kUnset), even though it was set to + // a different value above. + options = SelfProcessSnapshotAndGetCrashpadOptions(); + EXPECT_EQ(options.crashpad_handler_behavior, TriState::kEnabled); + EXPECT_EQ(options.system_crash_reporter_forwarding, TriState::kUnset); + EXPECT_EQ(options.gather_indirectly_referenced_memory, TriState::kUnset); + } +} + +INSTANTIATE_TEST_SUITE_P(CrashpadInfoSizes_ClientOptions, + CrashpadInfoSizes_ClientOptions, + testing::Values(FILE_PATH_LITERAL("small"), + FILE_PATH_LITERAL("large"))); + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/crashpad_info_client_options_test_module.cc b/shared/sentry/external/crashpad/snapshot/crashpad_info_client_options_test_module.cc new file mode 100644 index 000000000..48795e3c8 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/crashpad_info_client_options_test_module.cc @@ -0,0 +1,47 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "build/build_config.h" +#include "client/crashpad_info.h" + +#if BUILDFLAG(IS_POSIX) +#define EXPORT __attribute__((visibility("default"))) +#elif BUILDFLAG(IS_WIN) +#include +#define EXPORT __declspec(dllexport) +#endif // BUILDFLAG(IS_POSIX) + +extern "C" { + +// Returns the module’s CrashpadInfo structure. Assuming that this file is built +// into a loadable_module with a distinct static copy of the Crashpad client +// library from the copy built into the loader of this loadable_module, this +// will return a different CrashpadInfo structure than the one that the loader +// uses. Having an extra CrashpadInfo structure makes it possible to test +// behaviors that are relevant in the presence of multiple Crashpad +// client-enabled modules. +// +// This function is used by the CrashpadInfoClientOptions.TwoModules test in +// crashpad_info_client_options_test.cc. +EXPORT crashpad::CrashpadInfo* TestModule_GetCrashpadInfo() { + return crashpad::CrashpadInfo::GetCrashpadInfo(); +} + +} // extern "C" + +#if BUILDFLAG(IS_WIN) +BOOL WINAPI DllMain(HINSTANCE hinstance, DWORD reason, LPVOID reserved) { + return TRUE; +} +#endif // BUILDFLAG(IS_WIN) diff --git a/shared/sentry/external/crashpad/snapshot/crashpad_info_size_test_module.cc b/shared/sentry/external/crashpad/snapshot/crashpad_info_size_test_module.cc new file mode 100644 index 000000000..0a9186c24 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/crashpad_info_size_test_module.cc @@ -0,0 +1,129 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 + +#include "build/build_config.h" + +#if BUILDFLAG(IS_APPLE) +#include +#elif BUILDFLAG(IS_WIN) +#include +#endif // BUILDFLAG(IS_APPLE) + +namespace crashpad { + +#if defined(CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL) == \ + defined(CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE) +#error Define exactly one of these macros +#endif + +// This module contains a CrashpadInfo structure that’s either smaller or larger +// than the one defined in the client library, depending on which macro is +// defined when it’s compiled. This tests the snapshot layer’s ability to read +// smaller structures (as might be found in modules built with older versions of +// the client library than a handler’s snapshot library) and larger ones (the +// “vice-versa†situation). This needs to be done without taking a dependency on +// the client library, which would bring with it a correct copy of the +// CrashpadInfo structure. As a result, all types have been simplified to +// fixed-size integers and void* pointers. +struct TestCrashpadInfo { + uint32_t signature_; + uint32_t size_; + uint32_t version_; + uint32_t indirectly_referenced_memory_cap_; + uint32_t padding_0_; + uint8_t crashpad_handler_behavior_; + uint8_t system_crash_reporter_forwarding_; + uint8_t gather_indirectly_referenced_memory_; + uint8_t padding_1_; + void* extra_memory_ranges_; + void* simple_annotations_; +#if !defined(CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL) + void* user_data_minidump_stream_head_; + void* annotations_list_; +#endif // CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL +#if defined(CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE) + uint8_t trailer_[64 * 1024]; +#endif // CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE +}; + +// Put it in the correct section. +// +// The initializer also duplicates constants from the client library, sufficient +// to get this test version to be interpreted as a genuine CrashpadInfo +// structure. The size is set to the actual size of this structure (that’s kind +// of the point of this test). +#if BUILDFLAG(IS_POSIX) +__attribute__(( +#if BUILDFLAG(IS_APPLE) + section(SEG_DATA ",crashpad_info"), +#endif +#if defined(ADDRESS_SANITIZER) + aligned(64), +#endif // defined(ADDRESS_SANITIZER) + visibility("hidden"), + used)) +#elif BUILDFLAG(IS_WIN) +#pragma section("CPADinfo", read, write) +__declspec(allocate("CPADinfo")) +#else // !BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_WIN) +#error Port +#endif // !BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_WIN) +TestCrashpadInfo g_test_crashpad_info = {'CPad', + sizeof(TestCrashpadInfo), + 1, + 0, + 0, + 0, + 0, + 0, + 0, + nullptr, + nullptr, +#if !defined(CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL) + nullptr, + nullptr, +#endif // CRASHPAD_INFO_SIZE_TEST_MODULE_SMALL +#if defined(CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE) + {} +#endif // CRASHPAD_INFO_SIZE_TEST_MODULE_LARGE +}; + +} // namespace crashpad + +extern "C" { + +#if BUILDFLAG(IS_POSIX) +__attribute__((visibility("default"))) +#elif BUILDFLAG(IS_WIN) +__declspec(dllexport) +#else +#error Port +#endif // BUILDFLAG(IS_POSIX) +crashpad::TestCrashpadInfo* +TestModule_GetCrashpadInfo() { + // Note that there's no need to do the back-reference here to the note on + // POSIX like CrashpadInfo::GetCrashpadInfo() because the note .S file is + // directly included into this test binary. + return &crashpad::g_test_crashpad_info; +} + +} // extern "C" + +#if BUILDFLAG(IS_WIN) +BOOL WINAPI DllMain(HINSTANCE hinstance, DWORD reason, LPVOID reserved) { + return TRUE; +} +#endif // BUILDFLAG(IS_WIN) diff --git a/shared/sentry/external/crashpad/snapshot/crashpad_info_size_test_note.S b/shared/sentry/external/crashpad/snapshot/crashpad_info_size_test_note.S new file mode 100644 index 000000000..ebc0d8f6d --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/crashpad_info_size_test_note.S @@ -0,0 +1,52 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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. + +// This note section is used on ELF platforms to give ElfImageReader a method +// of finding the instance of CrashpadInfo g_crashpad_info without requiring +// that symbol to be in the dynamic symbol table. + +#include "util/misc/elf_note_types.h" +#include "util/misc/arm64_pac_bti.S" + +// namespace crashpad { +// CrashpadInfo g_test_crashpad_info; +// } // namespace crashpad +#define TEST_CRASHPAD_INFO_SYMBOL _ZN8crashpad20g_test_crashpad_infoE + +#define NOTE_ALIGN 4 + + // This section must be "a"llocated so that it appears in the final binary at + // runtime. The reference to TEST_CRASHPAD_INFO_SYMBOL uses an offset relative + // to this note to avoid making this note writable, which triggers a bug in + // GNU ld, or adding text relocations which require the target system to allow + // making text segments writable. https://crbug.com/crashpad/260. + .section .note.crashpad.info,"a",%note + .balign NOTE_ALIGN + .type info_size_test_note, %object +info_size_test_note: + .long name_end - name // namesz + .long desc_end - desc // descsz + .long CRASHPAD_ELF_NOTE_TYPE_CRASHPAD_INFO // type +name: + .asciz CRASHPAD_ELF_NOTE_NAME +name_end: + .balign NOTE_ALIGN +desc: +#if defined(__LP64__) + .quad TEST_CRASHPAD_INFO_SYMBOL - desc +#else + .long TEST_CRASHPAD_INFO_SYMBOL - desc +#endif // __LP64__ +desc_end: + .size info_size_test_note, .-info_size_test_note diff --git a/shared/sentry/external/crashpad/snapshot/crashpad_types/crashpad_info_reader.cc b/shared/sentry/external/crashpad/snapshot/crashpad_types/crashpad_info_reader.cc new file mode 100644 index 000000000..ad75292bd --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/crashpad_types/crashpad_info_reader.cc @@ -0,0 +1,197 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/crashpad_types/crashpad_info_reader.h" + +#include + +#include "build/build_config.h" +#include "client/crashpad_info.h" +#include "util/misc/as_underlying_type.h" + +#if BUILDFLAG(IS_WIN) +#include "util/win/traits.h" +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) +#include "util/linux/traits.h" +#elif BUILDFLAG(IS_FUCHSIA) +#include "util/fuchsia/traits.h" +#endif + +namespace crashpad { + +namespace { + +void UnsetIfNotValidTriState(TriState* value) { + switch (AsUnderlyingType(*value)) { + case AsUnderlyingType(TriState::kUnset): + case AsUnderlyingType(TriState::kEnabled): + case AsUnderlyingType(TriState::kDisabled): + return; + } + LOG(WARNING) << "Unsetting invalid TriState " << AsUnderlyingType(*value); + *value = TriState::kUnset; +} + +} // namespace + +class CrashpadInfoReader::InfoContainer { + public: + virtual ~InfoContainer() = default; + + virtual bool Read(const ProcessMemoryRange* memory, VMAddress address) = 0; + + protected: + InfoContainer() = default; +}; + +template +class CrashpadInfoReader::InfoContainerSpecific : public InfoContainer { + public: + InfoContainerSpecific() : InfoContainer() {} + ~InfoContainerSpecific() override = default; + + bool Read(const ProcessMemoryRange* memory, VMAddress address) override { + if (!memory->Read(address, + offsetof(decltype(info), size) + sizeof(info.size), + &info)) { + return false; + } + + if (info.signature != CrashpadInfo::kSignature) { + LOG(ERROR) << "invalid signature 0x" << std::hex << info.signature; + return false; + } + + if (!memory->Read( + address, std::min(info.size, sizeof(info)), &info)) { + return false; + } + + if (info.size > sizeof(info)) { + LOG(INFO) << "large crashpad info size " << info.size; + } + + if (info.version != 1) { + LOG(ERROR) << "unexpected version " << info.version; + return false; + } + + if (sizeof(info) > info.size) { + memset(reinterpret_cast(&info) + info.size, + 0, + sizeof(info) - info.size); + } + + UnsetIfNotValidTriState(&info.crashpad_handler_behavior); + UnsetIfNotValidTriState(&info.system_crash_reporter_forwarding); + UnsetIfNotValidTriState(&info.gather_indirectly_referenced_memory); + + return true; + } + + struct { + uint32_t signature; + uint32_t size; + uint32_t version; + uint32_t indirectly_referenced_memory_cap; + uint32_t padding_0; + TriState crashpad_handler_behavior; + TriState system_crash_reporter_forwarding; + TriState gather_indirectly_referenced_memory; + uint8_t padding_1; + typename Traits::Address extra_memory_ranges; + typename Traits::Address simple_annotations; + typename Traits::Address user_data_minidump_stream_head; + typename Traits::Address annotations_list; + } info; + +#if defined(ARCH_CPU_64_BITS) +#define NATIVE_TRAITS Traits64 +#else +#define NATIVE_TRAITS Traits32 +#endif + static_assert(!std::is_same::value || + sizeof(decltype(info)) == sizeof(CrashpadInfo), + "CrashpadInfo size mismtach"); +#undef NATIVE_TRAITS +}; + +CrashpadInfoReader::CrashpadInfoReader() + : container_(), is_64_bit_(false), initialized_() {} + +CrashpadInfoReader::~CrashpadInfoReader() = default; + +bool CrashpadInfoReader::Initialize(const ProcessMemoryRange* memory, + VMAddress address) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + is_64_bit_ = memory->Is64Bit(); + + std::unique_ptr new_container; + if (is_64_bit_) { + new_container = std::make_unique>(); + } else { + new_container = std::make_unique>(); + } + + if (!new_container->Read(memory, address)) { + return false; + } + container_ = std::move(new_container); + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +#define GET_MEMBER(name) \ + (is_64_bit_ \ + ? reinterpret_cast*>(container_.get()) \ + ->info.name \ + : reinterpret_cast*>(container_.get()) \ + ->info.name) + +#define DEFINE_GETTER(type, method, member) \ + type CrashpadInfoReader::method() { \ + INITIALIZATION_STATE_DCHECK_VALID(initialized_); \ + return GET_MEMBER(member); \ + } + +DEFINE_GETTER(TriState, CrashpadHandlerBehavior, crashpad_handler_behavior) + +DEFINE_GETTER(TriState, + SystemCrashReporterForwarding, + system_crash_reporter_forwarding) + +DEFINE_GETTER(TriState, + GatherIndirectlyReferencedMemory, + gather_indirectly_referenced_memory) + +DEFINE_GETTER(uint32_t, + IndirectlyReferencedMemoryCap, + indirectly_referenced_memory_cap) + +DEFINE_GETTER(VMAddress, ExtraMemoryRanges, extra_memory_ranges) + +DEFINE_GETTER(VMAddress, SimpleAnnotations, simple_annotations) + +DEFINE_GETTER(VMAddress, AnnotationsList, annotations_list) + +DEFINE_GETTER(VMAddress, + UserDataMinidumpStreamHead, + user_data_minidump_stream_head) + +#undef DEFINE_GETTER +#undef GET_MEMBER + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/crashpad_types/crashpad_info_reader.h b/shared/sentry/external/crashpad/snapshot/crashpad_types/crashpad_info_reader.h new file mode 100644 index 000000000..9494522f7 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/crashpad_types/crashpad_info_reader.h @@ -0,0 +1,76 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_CRASHPAD_TYPES_CRASHPAD_INFO_READER_H_ +#define CRASHPAD_SNAPSHOT_CRASHPAD_TYPES_CRASHPAD_INFO_READER_H_ + +#include + +#include + +#include "util/misc/address_types.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/misc/tri_state.h" +#include "util/process/process_memory_range.h" + +namespace crashpad { + +//! \brief Reads CrashpadInfo structs from another process via a +//! ProcessMemoryRange. +class CrashpadInfoReader { + public: + CrashpadInfoReader(); + + CrashpadInfoReader(const CrashpadInfoReader&) = delete; + CrashpadInfoReader& operator=(const CrashpadInfoReader&) = delete; + + ~CrashpadInfoReader(); + + //! \brief Initializes this object. + //! + //! This method must be successfully called bfore any other method in this + //! class. + //! + //! \param[in] memory The reader for the remote process. + //! \param[in] address The address in the remote process' address space of a + //! CrashpadInfo struct. + //! \return `true` on success. `false` on failure with a message logged. + bool Initialize(const ProcessMemoryRange* memory, VMAddress address); + + //! \{ + //! \see CrashpadInfo + TriState CrashpadHandlerBehavior(); + TriState SystemCrashReporterForwarding(); + TriState GatherIndirectlyReferencedMemory(); + uint32_t IndirectlyReferencedMemoryCap(); + VMAddress ExtraMemoryRanges(); + VMAddress SimpleAnnotations(); + VMAddress AnnotationsList(); + VMAddress UserDataMinidumpStreamHead(); + //! \} + + private: + class InfoContainer; + + template + class InfoContainerSpecific; + + std::unique_ptr container_; + bool is_64_bit_; + InitializationStateDcheck initialized_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_CRASHPAD_TYPES_CRASHPAD_INFO_READER_H_ diff --git a/shared/sentry/external/crashpad/snapshot/crashpad_types/crashpad_info_reader_test.cc b/shared/sentry/external/crashpad/snapshot/crashpad_types/crashpad_info_reader_test.cc new file mode 100644 index 000000000..9c6e3c372 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/crashpad_types/crashpad_info_reader_test.cc @@ -0,0 +1,233 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/crashpad_types/crashpad_info_reader.h" + +#include + +#include + +#include "build/build_config.h" +#include "client/annotation_list.h" +#include "client/crashpad_info.h" +#include "client/simple_address_range_bag.h" +#include "client/simple_string_dictionary.h" +#include "gtest/gtest.h" +#include "test/multiprocess_exec.h" +#include "test/process_type.h" +#include "util/file/file_io.h" +#include "util/misc/from_pointer_cast.h" +#include "util/process/process_memory_native.h" + +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) +#include "test/linux/fake_ptrace_connection.h" +#endif + +namespace crashpad { +namespace test { +namespace { + +constexpr TriState kCrashpadHandlerBehavior = TriState::kEnabled; +constexpr TriState kSystemCrashReporterForwarding = TriState::kDisabled; +constexpr TriState kGatherIndirectlyReferencedMemory = TriState::kUnset; + +constexpr uint32_t kIndirectlyReferencedMemoryCap = 42; + +class ScopedUnsetCrashpadInfo { + public: + explicit ScopedUnsetCrashpadInfo(CrashpadInfo* crashpad_info) + : crashpad_info_(crashpad_info) {} + + ScopedUnsetCrashpadInfo(const ScopedUnsetCrashpadInfo&) = delete; + ScopedUnsetCrashpadInfo& operator=(const ScopedUnsetCrashpadInfo&) = delete; + + ~ScopedUnsetCrashpadInfo() { + crashpad_info_->set_crashpad_handler_behavior(TriState::kUnset); + crashpad_info_->set_system_crash_reporter_forwarding(TriState::kUnset); + crashpad_info_->set_gather_indirectly_referenced_memory(TriState::kUnset, + 0); + crashpad_info_->set_extra_memory_ranges(nullptr); + crashpad_info_->set_simple_annotations(nullptr); + crashpad_info_->set_annotations_list(nullptr); + } + + private: + CrashpadInfo* crashpad_info_; +}; + +class CrashpadInfoTestDataSetup { + public: + CrashpadInfoTestDataSetup() { + CrashpadInfo* info = CrashpadInfo::GetCrashpadInfo(); + unset_.reset(new ScopedUnsetCrashpadInfo(info)); + + info->set_extra_memory_ranges(&extra_memory_); + info->set_simple_annotations(&simple_annotations_); + info->set_annotations_list(&annotation_list_); + info->set_crashpad_handler_behavior(kCrashpadHandlerBehavior); + info->set_system_crash_reporter_forwarding(kSystemCrashReporterForwarding); + info->set_gather_indirectly_referenced_memory( + kGatherIndirectlyReferencedMemory, kIndirectlyReferencedMemoryCap); + } + + CrashpadInfoTestDataSetup(const CrashpadInfoTestDataSetup&) = delete; + CrashpadInfoTestDataSetup& operator=(const CrashpadInfoTestDataSetup&) = + delete; + + void GetAddresses(VMAddress* info_address, + VMAddress* extra_memory_address, + VMAddress* simple_annotations_address, + VMAddress* annotations_list_address) { + *info_address = FromPointerCast(CrashpadInfo::GetCrashpadInfo()); + *extra_memory_address = FromPointerCast(&extra_memory_); + *simple_annotations_address = + FromPointerCast(&simple_annotations_); + *annotations_list_address = FromPointerCast(&annotation_list_); + } + + private: + std::unique_ptr unset_; + SimpleAddressRangeBag extra_memory_; + SimpleStringDictionary simple_annotations_; + AnnotationList annotation_list_; +}; + +void ExpectCrashpadInfo(ProcessType process, + bool is_64_bit, + VMAddress info_address, + VMAddress extra_memory_address, + VMAddress simple_annotations_address, + VMAddress annotations_list_address) { +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) + FakePtraceConnection connection; + ASSERT_TRUE(connection.Initialize(process)); + ProcessMemoryLinux memory(&connection); +#else + ProcessMemoryNative memory; + ASSERT_TRUE(memory.Initialize(process)); +#endif + + ProcessMemoryRange range; + ASSERT_TRUE(range.Initialize(&memory, is_64_bit)); + + CrashpadInfoReader reader; + ASSERT_TRUE(reader.Initialize(&range, info_address)); + EXPECT_EQ(reader.CrashpadHandlerBehavior(), kCrashpadHandlerBehavior); + EXPECT_EQ(reader.SystemCrashReporterForwarding(), + kSystemCrashReporterForwarding); + EXPECT_EQ(reader.GatherIndirectlyReferencedMemory(), + kGatherIndirectlyReferencedMemory); + EXPECT_EQ(reader.IndirectlyReferencedMemoryCap(), + kIndirectlyReferencedMemoryCap); + EXPECT_EQ(reader.ExtraMemoryRanges(), extra_memory_address); + EXPECT_EQ(reader.SimpleAnnotations(), simple_annotations_address); + EXPECT_EQ(reader.AnnotationsList(), annotations_list_address); +} + +TEST(CrashpadInfoReader, ReadFromSelf) { +#if defined(ARCH_CPU_64_BITS) + constexpr bool am_64_bit = true; +#else + constexpr bool am_64_bit = false; +#endif + + CrashpadInfoTestDataSetup test_data_setup; + VMAddress info_address; + VMAddress extra_memory_address; + VMAddress simple_annotations_address; + VMAddress annotations_list_address; + test_data_setup.GetAddresses(&info_address, + &extra_memory_address, + &simple_annotations_address, + &annotations_list_address); + ExpectCrashpadInfo(GetSelfProcess(), + am_64_bit, + info_address, + extra_memory_address, + simple_annotations_address, + annotations_list_address); +} + +CRASHPAD_CHILD_TEST_MAIN(ReadFromChildTestMain) { + CrashpadInfoTestDataSetup test_data_setup; + VMAddress info_address; + VMAddress extra_memory_address; + VMAddress simple_annotations_address; + VMAddress annotations_list_address; + test_data_setup.GetAddresses(&info_address, + &extra_memory_address, + &simple_annotations_address, + &annotations_list_address); + + FileHandle out = StdioFileHandle(StdioStream::kStandardOutput); + CheckedWriteFile(out, &info_address, sizeof(info_address)); + CheckedWriteFile(out, &extra_memory_address, sizeof(extra_memory_address)); + CheckedWriteFile( + out, &simple_annotations_address, sizeof(simple_annotations_address)); + CheckedWriteFile( + out, &annotations_list_address, sizeof(annotations_list_address)); + CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput)); + return 0; +} + +class ReadFromChildTest : public MultiprocessExec { + public: + ReadFromChildTest() : MultiprocessExec() { + SetChildTestMainFunction("ReadFromChildTestMain"); + } + + ReadFromChildTest(const ReadFromChildTest&) = delete; + ReadFromChildTest& operator=(const ReadFromChildTest&) = delete; + + ~ReadFromChildTest() = default; + + private: + void MultiprocessParent() { +#if defined(ARCH_CPU_64_BITS) + constexpr bool am_64_bit = true; +#else + constexpr bool am_64_bit = false; +#endif + + VMAddress info_address; + VMAddress extra_memory_address; + VMAddress simple_annotations_address; + VMAddress annotations_list_address; + ASSERT_TRUE( + ReadFileExactly(ReadPipeHandle(), &info_address, sizeof(info_address))); + ASSERT_TRUE(ReadFileExactly( + ReadPipeHandle(), &extra_memory_address, sizeof(extra_memory_address))); + ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), + &simple_annotations_address, + sizeof(simple_annotations_address))); + ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), + &annotations_list_address, + sizeof(annotations_list_address))); + ExpectCrashpadInfo(ChildProcess(), + am_64_bit, + info_address, + extra_memory_address, + simple_annotations_address, + annotations_list_address); + } +}; + +TEST(CrashpadInfoReader, ReadFromChild) { + ReadFromChildTest test; + test.Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/crashpad_types/image_annotation_reader.cc b/shared/sentry/external/crashpad/snapshot/crashpad_types/image_annotation_reader.cc new file mode 100644 index 000000000..bd904979f --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/crashpad_types/image_annotation_reader.cc @@ -0,0 +1,153 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/crashpad_types/image_annotation_reader.h" + +#include +#include + +#include +#include + +#include "base/logging.h" +#include "build/build_config.h" +#include "client/annotation.h" +#include "client/annotation_list.h" +#include "client/simple_string_dictionary.h" +#include "snapshot/snapshot_constants.h" +#include "util/linux/traits.h" + +namespace crashpad { + +namespace process_types { + +template +struct Annotation { + typename Traits::Address link_node; + typename Traits::Address name; + typename Traits::Address value; + uint32_t size; + uint16_t type; +}; + +template +struct AnnotationList { + typename Traits::Address tail_pointer; + Annotation head; + Annotation tail; +}; + +} // namespace process_types + +#if defined(ARCH_CPU_64_BITS) +#define NATIVE_TRAITS Traits64 +#else +#define NATIVE_TRAITS Traits32 +#endif // ARCH_CPU_64_BITS + +static_assert(sizeof(process_types::Annotation) == + sizeof(Annotation), + "Annotation size mismatch"); + +static_assert(sizeof(process_types::AnnotationList) == + sizeof(AnnotationList), + "AnnotationList size mismatch"); + +#undef NATIVE_TRAITS + +ImageAnnotationReader::ImageAnnotationReader(const ProcessMemoryRange* memory) + : memory_(memory) {} + +ImageAnnotationReader::~ImageAnnotationReader() = default; + +bool ImageAnnotationReader::SimpleMap( + VMAddress address, + std::map* annotations) const { + std::vector simple_annotations( + SimpleStringDictionary::num_entries); + + if (!memory_->Read(address, + simple_annotations.size() * sizeof(simple_annotations[0]), + &simple_annotations[0])) { + return false; + } + + for (const auto& entry : simple_annotations) { + size_t key_length = strnlen(entry.key, sizeof(entry.key)); + if (key_length) { + std::string key(entry.key, key_length); + std::string value(entry.value, strnlen(entry.value, sizeof(entry.value))); + if (!annotations->insert(std::make_pair(key, value)).second) { + LOG(WARNING) << "duplicate simple annotation " << key << " " << value; + } + } + } + return true; +} + +bool ImageAnnotationReader::AnnotationsList( + VMAddress address, + std::vector* annotations) const { + return memory_->Is64Bit() + ? ReadAnnotationList(address, annotations) + : ReadAnnotationList(address, annotations); +} + +template +bool ImageAnnotationReader::ReadAnnotationList( + VMAddress address, + std::vector* annotations) const { + process_types::AnnotationList annotation_list; + if (!memory_->Read(address, sizeof(annotation_list), &annotation_list)) { + LOG(ERROR) << "could not read annotation list"; + return false; + } + + process_types::Annotation current = annotation_list.head; + for (size_t index = 0; current.link_node != annotation_list.tail_pointer && + index < kMaxNumberOfAnnotations; + ++index) { + if (!memory_->Read(current.link_node, sizeof(current), ¤t)) { + LOG(ERROR) << "could not read annotation at index " << index; + return false; + } + + if (current.size == 0) { + continue; + } + + AnnotationSnapshot snapshot; + snapshot.type = current.type; + + if (!memory_->ReadCStringSizeLimited( + current.name, Annotation::kNameMaxLength, &snapshot.name)) { + LOG(WARNING) << "could not read annotation name at index " << index; + continue; + } + + size_t value_length = + std::min(static_cast(current.size), Annotation::kValueMaxSize); + snapshot.value.resize(value_length); + if (!memory_->Read(current.value, value_length, snapshot.value.data())) { + LOG(WARNING) << "could not read annotation value at index " << index; + continue; + } + + annotations->push_back(std::move(snapshot)); + } + + return true; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/crashpad_types/image_annotation_reader.h b/shared/sentry/external/crashpad/snapshot/crashpad_types/image_annotation_reader.h new file mode 100644 index 000000000..f7bb51257 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/crashpad_types/image_annotation_reader.h @@ -0,0 +1,76 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_CRASHPAD_TYPES_IMAGE_ANNOTATION_READER_H_ +#define CRASHPAD_SNAPSHOT_CRASHPAD_TYPES_IMAGE_ANNOTATION_READER_H_ + +#include +#include +#include + +#include "snapshot/annotation_snapshot.h" +#include "util/misc/address_types.h" +#include "util/process/process_memory_range.h" + +namespace crashpad { + +//! \brief Reads Annotations from another process via a ProcessMemoryRange. +//! +//! These annotations are stored for the benefit of crash reporters, and provide +//! information thought to be potentially useful for crash analysis. +class ImageAnnotationReader { + public: + //! \brief Constructs the object. + //! + //! \param[in] memory A memory reader for the remote process. + explicit ImageAnnotationReader(const ProcessMemoryRange* memory); + + ImageAnnotationReader(const ImageAnnotationReader&) = delete; + ImageAnnotationReader& operator=(const ImageAnnotationReader&) = delete; + + ~ImageAnnotationReader(); + + //! \brief Reads annotations that are organized as key-value pairs, where all + //! keys and values are strings. + //! + //! \param[in] address The address in the target process' address space of a + //! SimpleStringDictionary containing the annotations to read. + //! \param[out] annotations The annotations read, valid if this method + //! returns `true`. + //! \return `true` on success. `false` on failure with a message logged. + bool SimpleMap(VMAddress address, + std::map* annotations) const; + + //! \brief Reads the module's annotations that are organized as a list of + //! typed annotation objects. + //! + //! \param[in] address The address in the target process' address space of an + //! AnnotationList. + //! \param[out] annotations The annotations read, valid if this method returns + //! `true`. + //! \return `true` on success. `false` on failure with a message logged. + bool AnnotationsList(VMAddress, + std::vector* annotations) const; + + private: + template + bool ReadAnnotationList(VMAddress address, + std::vector* annotations) const; + + const ProcessMemoryRange* memory_; // weak +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_CRASHPAD_TYPES_IMAGE_ANNOTATION_READER_H_ diff --git a/shared/sentry/external/crashpad/snapshot/crashpad_types/image_annotation_reader_test.cc b/shared/sentry/external/crashpad/snapshot/crashpad_types/image_annotation_reader_test.cc new file mode 100644 index 000000000..f7f18cbd6 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/crashpad_types/image_annotation_reader_test.cc @@ -0,0 +1,197 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/crashpad_types/image_annotation_reader.h" + +#include +#include +#include + +#include + +#include "build/build_config.h" +#include "client/annotation.h" +#include "client/annotation_list.h" +#include "client/simple_string_dictionary.h" +#include "gtest/gtest.h" +#include "test/multiprocess_exec.h" +#include "test/process_type.h" +#include "util/file/file_io.h" +#include "util/misc/as_underlying_type.h" +#include "util/misc/from_pointer_cast.h" +#include "util/process/process_memory_native.h" + +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) +#include "test/linux/fake_ptrace_connection.h" +#endif + +namespace crashpad { +namespace test { +namespace { + +void ExpectSimpleMap(const std::map& map, + const SimpleStringDictionary& expected_map) { + EXPECT_EQ(map.size(), expected_map.GetCount()); + for (const auto& pair : map) { + EXPECT_EQ(pair.second, expected_map.GetValueForKey(pair.first)); + } +} + +void ExpectAnnotationList(const std::vector& list, + AnnotationList& expected_list) { + size_t index = 0; + for (const Annotation* expected_annotation : expected_list) { + const AnnotationSnapshot& annotation = list[index++]; + EXPECT_EQ(annotation.name, expected_annotation->name()); + EXPECT_EQ(annotation.type, AsUnderlyingType(expected_annotation->type())); + EXPECT_EQ(annotation.value.size(), expected_annotation->size()); + EXPECT_EQ(memcmp(annotation.value.data(), + expected_annotation->value(), + std::min(VMSize{annotation.value.size()}, + VMSize{expected_annotation->size()})), + 0); + } +} + +void BuildTestStructures( + std::vector>* annotations_storage, + SimpleStringDictionary* into_map, + AnnotationList* into_annotation_list) { + into_map->SetKeyValue("key", "value"); + into_map->SetKeyValue("key2", "value2"); + + static constexpr char kAnnotationName[] = "test annotation"; + static constexpr char kAnnotationValue[] = "test annotation value"; + annotations_storage->push_back(std::make_unique( + Annotation::Type::kString, + kAnnotationName, + reinterpret_cast(const_cast(kAnnotationValue)))); + annotations_storage->back()->SetSize(sizeof(kAnnotationValue)); + into_annotation_list->Add(annotations_storage->back().get()); + + static constexpr char kAnnotationName2[] = "test annotation2"; + static constexpr char kAnnotationValue2[] = "test annotation value2"; + annotations_storage->push_back(std::make_unique( + Annotation::Type::kString, + kAnnotationName2, + reinterpret_cast(const_cast(kAnnotationValue2)))); + annotations_storage->back()->SetSize(sizeof(kAnnotationValue2)); + into_annotation_list->Add(annotations_storage->back().get()); +} + +void ExpectAnnotations(ProcessType process, + bool is_64_bit, + VMAddress simple_map_address, + VMAddress annotation_list_address) { +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) + FakePtraceConnection connection; + ASSERT_TRUE(connection.Initialize(process)); + ProcessMemoryLinux memory(&connection); +#else + ProcessMemoryNative memory; + ASSERT_TRUE(memory.Initialize(process)); +#endif + + ProcessMemoryRange range; + ASSERT_TRUE(range.Initialize(&memory, is_64_bit)); + + SimpleStringDictionary expected_simple_map; + std::vector> storage; + AnnotationList expected_annotations; + BuildTestStructures(&storage, &expected_simple_map, &expected_annotations); + + ImageAnnotationReader reader(&range); + + std::map simple_map; + ASSERT_TRUE(reader.SimpleMap(simple_map_address, &simple_map)); + ExpectSimpleMap(simple_map, expected_simple_map); + + std::vector annotation_list; + ASSERT_TRUE( + reader.AnnotationsList(annotation_list_address, &annotation_list)); + ExpectAnnotationList(annotation_list, expected_annotations); +} + +TEST(ImageAnnotationReader, ReadFromSelf) { + SimpleStringDictionary map; + std::vector> storage; + AnnotationList annotations; + BuildTestStructures(&storage, &map, &annotations); + +#if defined(ARCH_CPU_64_BITS) + constexpr bool am_64_bit = true; +#else + constexpr bool am_64_bit = false; +#endif + + ExpectAnnotations(GetSelfProcess(), + am_64_bit, + FromPointerCast(&map), + FromPointerCast(&annotations)); +} + +CRASHPAD_CHILD_TEST_MAIN(ReadAnnotationsFromChildTestMain) { + SimpleStringDictionary map; + std::vector> storage; + AnnotationList annotations; + BuildTestStructures(&storage, &map, &annotations); + + VMAddress simple_map_address = FromPointerCast(&map); + VMAddress annotations_address = FromPointerCast(&annotations); + FileHandle out = StdioFileHandle(StdioStream::kStandardOutput); + CheckedWriteFile(out, &simple_map_address, sizeof(simple_map_address)); + CheckedWriteFile(out, &annotations_address, sizeof(annotations_address)); + + CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput)); + return 0; +} + +class ReadFromChildTest : public MultiprocessExec { + public: + ReadFromChildTest() : MultiprocessExec() { + SetChildTestMainFunction("ReadAnnotationsFromChildTestMain"); + } + + ReadFromChildTest(const ReadFromChildTest&) = delete; + ReadFromChildTest& operator=(const ReadFromChildTest&) = delete; + + ~ReadFromChildTest() = default; + + private: + void MultiprocessParent() { +#if defined(ARCH_CPU_64_BITS) + constexpr bool am_64_bit = true; +#else + constexpr bool am_64_bit = false; +#endif + + VMAddress simple_map_address; + VMAddress annotations_address; + ASSERT_TRUE(ReadFileExactly( + ReadPipeHandle(), &simple_map_address, sizeof(simple_map_address))); + ASSERT_TRUE(ReadFileExactly( + ReadPipeHandle(), &annotations_address, sizeof(annotations_address))); + ExpectAnnotations( + ChildProcess(), am_64_bit, simple_map_address, annotations_address); + } +}; + +TEST(ImageAnnotationReader, ReadFromChild) { + ReadFromChildTest test; + test.Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/elf/elf_dynamic_array_reader.cc b/shared/sentry/external/crashpad/snapshot/elf/elf_dynamic_array_reader.cc new file mode 100644 index 000000000..a39712ac4 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/elf/elf_dynamic_array_reader.cc @@ -0,0 +1,80 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/elf/elf_dynamic_array_reader.h" + +#include + +#include + +#include "util/stdlib/map_insert.h" + +namespace crashpad { + +namespace { + +template +bool Read(const ProcessMemoryRange& memory, + VMAddress address, + VMSize size, + std::map* values) { + std::map local_values; + + while (size > 0) { + DynType entry; + if (!memory.Read(address, sizeof(entry), &entry)) { + return false; + } + size -= sizeof(entry); + address += sizeof(entry); + + switch (entry.d_tag) { + case DT_NULL: + values->swap(local_values); + return true; + case DT_NEEDED: + // Skip these entries for now. + break; + default: + static_assert(std::is_unsigned::value, + "type must be unsigned"); + static_assert(static_cast(&entry.d_un.d_ptr) == + static_cast(&entry.d_un.d_val) && + sizeof(entry.d_un.d_ptr) == sizeof(entry.d_un.d_val), + "d_ptr and d_val must be aliases"); + if (!MapInsertOrReplace( + &local_values, entry.d_tag, entry.d_un.d_ptr, nullptr)) { + LOG(ERROR) << "duplicate dynamic array entry"; + return false; + } + } + } + LOG(ERROR) << "missing DT_NULL"; + return false; +} + +} // namespace + +ElfDynamicArrayReader::ElfDynamicArrayReader() : values_() {} + +ElfDynamicArrayReader::~ElfDynamicArrayReader() {} + +bool ElfDynamicArrayReader::Initialize(const ProcessMemoryRange& memory, + VMAddress address, + VMSize size) { + return memory.Is64Bit() ? Read(memory, address, size, &values_) + : Read(memory, address, size, &values_); +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/elf/elf_dynamic_array_reader.h b/shared/sentry/external/crashpad/snapshot/elf/elf_dynamic_array_reader.h new file mode 100644 index 000000000..f5cddf6f0 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/elf/elf_dynamic_array_reader.h @@ -0,0 +1,76 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_ELF_ELF_DYNAMIC_ARRAY_READER_H_ +#define CRASHPAD_SNAPSHOT_ELF_ELF_DYNAMIC_ARRAY_READER_H_ + +#include + +#include + +#include "base/logging.h" +#include "util/misc/address_types.h" +#include "util/misc/reinterpret_bytes.h" +#include "util/process/process_memory_range.h" + +namespace crashpad { + +//! \brief A reader for ELF dynamic arrays mapped into another process. +class ElfDynamicArrayReader { + public: + ElfDynamicArrayReader(); + + ElfDynamicArrayReader(const ElfDynamicArrayReader&) = delete; + ElfDynamicArrayReader& operator=(const ElfDynamicArrayReader&) = delete; + + ~ElfDynamicArrayReader(); + + //! \brief Initializes the reader. + //! + //! This method must be called once on an object and must be successfully + //! called before any other method in this class may be called. + //! + //! \param[in] memory A memory reader for the remote process. + //! \param[in] address The address in the remote process' address space where + //! the ELF dynamic table is loaded. + //! \param[in] size The maximum number of bytes to read. + bool Initialize(const ProcessMemoryRange& memory, + VMAddress address, + VMSize size); + + //! \brief Retrieve a value from the array. + //! + //! \param[in] tag Specifies which value should be retrieved. The possible + //! values for this parameter are the `DT_*` values from ``. + //! \param[in] log Specifies whether an error should be logged if \a tag is + //! not found. + //! \param[out] value The value, casted to an appropriate type, if found. + //! \return `true` if the value is found. + template + bool GetValue(uint64_t tag, bool log, V* value) { + auto iter = values_.find(tag); + if (iter == values_.end()) { + LOG_IF(ERROR, log) << "tag not found"; + return false; + } + return ReinterpretBytes(iter->second, value); + } + + private: + std::map values_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_ELF_ELF_DYNAMIC_ARRAY_READER_H_ diff --git a/shared/sentry/external/crashpad/snapshot/elf/elf_image_reader.cc b/shared/sentry/external/crashpad/snapshot/elf/elf_image_reader.cc new file mode 100644 index 000000000..0b7d0145f --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/elf/elf_image_reader.cc @@ -0,0 +1,858 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/elf/elf_image_reader.h" + +#include + +#include +#include +#include +#include + +#include "base/logging.h" +#include "base/numerics/safe_math.h" +#include "build/build_config.h" +#include "util/numeric/checked_vm_address_range.h" + +namespace crashpad { + +class ElfImageReader::ProgramHeaderTable { + public: + virtual ~ProgramHeaderTable() {} + + virtual bool VerifyLoadSegments(bool verbose) const = 0; + virtual size_t Size() const = 0; + virtual bool GetDynamicSegment(VMAddress* address, VMSize* size) const = 0; + virtual bool GetPreferredElfHeaderAddress(VMAddress* address, + bool verbose) const = 0; + virtual bool GetPreferredLoadedMemoryRange(VMAddress* address, + VMSize* size, + bool verbose) const = 0; + + // Locate the next PT_NOTE segment starting at segment index start_index. If a + // PT_NOTE segment is found, start_index is set to the next index after the + // found segment. + virtual bool GetNoteSegment(size_t* start_index, + VMAddress* address, + VMSize* size) const = 0; + + protected: + ProgramHeaderTable() {} +}; + +template +class ElfImageReader::ProgramHeaderTableSpecific + : public ElfImageReader::ProgramHeaderTable { + public: + ProgramHeaderTableSpecific() {} + + ProgramHeaderTableSpecific( + const ProgramHeaderTableSpecific&) = delete; + ProgramHeaderTableSpecific& operator=( + const ProgramHeaderTableSpecific&) = delete; + + ~ProgramHeaderTableSpecific() {} + + bool Initialize(const ProcessMemoryRange& memory, + VMAddress address, + VMSize num_segments, + bool verbose) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + table_.resize(num_segments); + if (!memory.Read(address, sizeof(PhdrType) * num_segments, table_.data())) { + return false; + } + + if (!VerifyLoadSegments(verbose)) { + return false; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; + } + + bool VerifyLoadSegments(bool verbose) const override { + constexpr bool is_64_bit = std::is_same::value; + VMAddress last_vaddr; + bool load_found = false; + for (const auto& header : table_) { + if (header.p_type == PT_LOAD) { + CheckedVMAddressRange load_range( + is_64_bit, header.p_vaddr, header.p_memsz); + + if (!load_range.IsValid()) { + LOG_IF(ERROR, verbose) << "bad load range"; + return false; + } + + if (load_found && header.p_vaddr <= last_vaddr) { + LOG_IF(ERROR, verbose) << "out of order load segments"; + return false; + } + load_found = true; + last_vaddr = header.p_vaddr; + } + } + return true; + } + + size_t Size() const override { return sizeof(PhdrType) * table_.size(); } + + bool GetPreferredElfHeaderAddress(VMAddress* address, + bool verbose) const override { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + for (const auto& header : table_) { + if (header.p_type == PT_LOAD && header.p_offset == 0) { + *address = header.p_vaddr; + return true; + } + } + LOG_IF(ERROR, verbose) << "no preferred header address"; + return false; + } + + bool GetPreferredLoadedMemoryRange(VMAddress* base, + VMSize* size, + bool verbose) const override { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + VMAddress preferred_base = 0; + VMAddress preferred_end = 0; + bool load_found = false; + for (const auto& header : table_) { + if (header.p_type == PT_LOAD) { + if (!load_found) { + preferred_base = header.p_vaddr; + load_found = true; + } + preferred_end = header.p_vaddr + header.p_memsz; + } + } + if (load_found) { + *base = preferred_base; + *size = preferred_end - preferred_base; + return true; + } + LOG_IF(ERROR, verbose) << "no load segments"; + return false; + } + + bool GetDynamicSegment(VMAddress* address, VMSize* size) const override { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + const PhdrType* phdr; + if (!GetProgramHeader(PT_DYNAMIC, &phdr)) { + return false; + } + *address = phdr->p_vaddr; + *size = phdr->p_memsz; + return true; + } + + bool GetProgramHeader(uint32_t type, const PhdrType** header_out) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + for (const auto& header : table_) { + if (header.p_type == type) { + *header_out = &header; + return true; + } + } + return false; + } + + bool GetNoteSegment(size_t* start_index, + VMAddress* address, + VMSize* size) const override { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + for (size_t index = *start_index; index < table_.size(); ++index) { + if (table_[index].p_type == PT_NOTE && table_[index].p_vaddr != 0) { + *start_index = index + 1; + *address = table_[index].p_vaddr; + *size = table_[index].p_memsz; + return true; + } + } + return false; + } + + private: + std::vector table_; + InitializationStateDcheck initialized_; +}; + +ElfImageReader::NoteReader::~NoteReader() = default; + +ElfImageReader::NoteReader::Result ElfImageReader::NoteReader::NextNote( + std::string* name, + NoteType* type, + std::string* desc, + VMAddress* desc_address) { + if (!is_valid_) { + LOG(ERROR) << "invalid note reader"; + return Result::kError; + } + + Result result = Result::kError; + do { + while (current_address_ == segment_end_address_) { + VMSize segment_size; + if (!phdr_table_->GetNoteSegment( + &phdr_index_, ¤t_address_, &segment_size)) { + return Result::kNoMoreNotes; + } + current_address_ += elf_reader_->GetLoadBias(); + segment_end_address_ = current_address_ + segment_size; + segment_range_ = std::make_unique(); + if (!segment_range_->Initialize(*range_) || + !segment_range_->RestrictRange(current_address_, segment_size)) { + return Result::kError; + } + } + + retry_ = false; + result = range_->Is64Bit() + ? ReadNote(name, type, desc, desc_address) + : ReadNote(name, type, desc, desc_address); + } while (retry_); + + if (result == Result::kSuccess) { + return Result::kSuccess; + } + is_valid_ = false; + return Result::kError; +} + +namespace { + +// The maximum size the user can specify for maximum note size. Clamping this +// ensures that buffer allocations cannot be wildly large. It is not expected +// that a note would be larger than ~1k in normal usage. +constexpr size_t kMaxMaxNoteSize = 16384; + +} // namespace + +ElfImageReader::NoteReader::NoteReader(const ElfImageReader* elf_reader, + const ProcessMemoryRange* range, + const ProgramHeaderTable* phdr_table, + size_t max_note_size, + const std::string& name_filter, + NoteType type_filter, + bool use_filter) + : current_address_(0), + segment_end_address_(0), + elf_reader_(elf_reader), + range_(range), + phdr_table_(phdr_table), + segment_range_(), + phdr_index_(0), + max_note_size_(std::min(kMaxMaxNoteSize, max_note_size)), + name_filter_(name_filter), + type_filter_(type_filter), + use_filter_(use_filter), + is_valid_(true), + retry_(false) { + DCHECK_LT(max_note_size, kMaxMaxNoteSize); +} + +template +ElfImageReader::NoteReader::Result ElfImageReader::NoteReader::ReadNote( + std::string* name, + NoteType* type, + std::string* desc, + VMAddress* desc_address) { + static_assert(sizeof(*type) >= sizeof(NhdrType::n_namesz), + "Note field size mismatch"); + DCHECK_LT(current_address_, segment_end_address_); + + NhdrType note_info; + if (!segment_range_->Read(current_address_, sizeof(note_info), ¬e_info)) { + return Result::kError; + } + current_address_ += sizeof(note_info); + + constexpr size_t align = sizeof(note_info.n_namesz); + +#define CHECKED_PAD(x, into) \ + base::CheckAnd(base::CheckAdd(x, align - 1), ~(align - 1)) \ + .AssignIfValid(&into) + + size_t padded_namesz; + if (!CHECKED_PAD(note_info.n_namesz, padded_namesz)) { + return Result::kError; + } + size_t padded_descsz; + if (!CHECKED_PAD(note_info.n_descsz, padded_descsz)) { + return Result::kError; + } + + size_t note_size; + if (!base::CheckAdd(padded_namesz, padded_descsz).AssignIfValid(¬e_size)) { + return Result::kError; + } + + // Notes typically have 4-byte alignment. However, .note.android.ident may + // inadvertently use 2-byte alignment. + // https://android-review.googlesource.com/c/platform/bionic/+/554986/ + // We can still find .note.android.ident if it appears first in a note segment + // but there may be 4-byte aligned notes following it. If this note was + // aligned at less than 4-bytes, expect that the next note will be aligned at + // 4-bytes and add extra padding, if necessary. + + VMAddress end_of_note_candidate; + if (!base::CheckAdd(current_address_, note_size) + .AssignIfValid(&end_of_note_candidate)) { + return Result::kError; + } + VMAddress end_of_note; + if (!CHECKED_PAD(end_of_note_candidate, end_of_note)) { + return Result::kError; + } + end_of_note = std::min(end_of_note, segment_end_address_); + +#undef CHECKED_PAD + + if (note_size > max_note_size_) { + current_address_ = end_of_note; + retry_ = true; + return Result::kError; + } + + if (use_filter_ && note_info.n_type != type_filter_) { + current_address_ = end_of_note; + retry_ = true; + return Result::kError; + } + + std::string local_name(note_info.n_namesz, '\0'); + if (!segment_range_->Read( + current_address_, note_info.n_namesz, &local_name[0])) { + return Result::kError; + } + if (!local_name.empty()) { + if (local_name.back() != '\0') { + LOG(ERROR) << "unterminated note name"; + return Result::kError; + } + local_name.pop_back(); + } + + if (use_filter_ && local_name != name_filter_) { + current_address_ = end_of_note; + retry_ = true; + return Result::kError; + } + + current_address_ += padded_namesz; + + std::string local_desc(note_info.n_descsz, '\0'); + if (!segment_range_->Read( + current_address_, note_info.n_descsz, &local_desc[0])) { + return Result::kError; + } + *desc_address = current_address_; + + current_address_ = end_of_note; + + if (name) { + name->swap(local_name); + } + if (type) { + *type = note_info.n_type; + } + desc->swap(local_desc); + return Result::kSuccess; +} + +ElfImageReader::ElfImageReader() + : header_64_(), + ehdr_address_(0), + load_bias_(0), + memory_(), + program_headers_(), + dynamic_array_(), + symbol_table_(), + initialized_(), + dynamic_array_initialized_(), + symbol_table_initialized_() {} + +ElfImageReader::~ElfImageReader() {} + +bool ElfImageReader::Initialize(const ProcessMemoryRange& memory, + VMAddress address, + bool verbose) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + ehdr_address_ = address; + if (!memory_.Initialize(memory)) { + return false; + } + + uint8_t e_ident[EI_NIDENT]; + if (!memory_.Read(ehdr_address_, EI_NIDENT, e_ident)) { + return false; + } + + if (e_ident[EI_MAG0] != ELFMAG0 || e_ident[EI_MAG1] != ELFMAG1 || + e_ident[EI_MAG2] != ELFMAG2 || e_ident[EI_MAG3] != ELFMAG3) { + LOG_IF(ERROR, verbose) << "Incorrect ELF magic number"; + return false; + } + + if (!(memory_.Is64Bit() && e_ident[EI_CLASS] == ELFCLASS64) && + !(!memory_.Is64Bit() && e_ident[EI_CLASS] == ELFCLASS32)) { + LOG_IF(ERROR, verbose) << "unexpected bitness"; + return false; + } + +#if defined(ARCH_CPU_LITTLE_ENDIAN) + constexpr uint8_t expected_encoding = ELFDATA2LSB; +#elif defined(ARCH_CPU_BIG_ENDIAN) + constexpr uint8_t expected_encoding = ELFDATA2MSB; +#endif + if (e_ident[EI_DATA] != expected_encoding) { + LOG_IF(ERROR, verbose) << "unexpected encoding"; + return false; + } + + if (e_ident[EI_VERSION] != EV_CURRENT) { + LOG_IF(ERROR, verbose) << "unexpected version"; + return false; + } + + if (!(memory_.Is64Bit() + ? memory_.Read(ehdr_address_, sizeof(header_64_), &header_64_) + : memory_.Read(ehdr_address_, sizeof(header_32_), &header_32_))) { + return false; + } + +#define VERIFY_HEADER(header) \ + do { \ + if (header.e_type != ET_EXEC && header.e_type != ET_DYN) { \ + LOG_IF(ERROR, verbose) << "unexpected image type"; \ + return false; \ + } \ + if (header.e_version != EV_CURRENT) { \ + LOG_IF(ERROR, verbose) << "unexpected version"; \ + return false; \ + } \ + if (header.e_ehsize != sizeof(header)) { \ + LOG_IF(ERROR, verbose) << "unexpected header size"; \ + return false; \ + } \ + } while (false); + + if (memory_.Is64Bit()) { + VERIFY_HEADER(header_64_); + } else { + VERIFY_HEADER(header_32_); + } + + if (!InitializeProgramHeaders(verbose)) { + return false; + } + + VMAddress preferred_ehdr_address; + if (!program_headers_.get()->GetPreferredElfHeaderAddress( + &preferred_ehdr_address, verbose)) { + return false; + } + load_bias_ = ehdr_address_ - preferred_ehdr_address; + + VMAddress base_address; + VMSize loaded_size; + if (!program_headers_.get()->GetPreferredLoadedMemoryRange( + &base_address, &loaded_size, verbose)) { + return false; + } + base_address += load_bias_; + + if (!memory_.RestrictRange(base_address, loaded_size)) { + return false; + } + + VMSize ehdr_size; + VMAddress phdr_address; + if (memory_.Is64Bit()) { + ehdr_size = sizeof(header_64_); + phdr_address = ehdr_address_ + header_64_.e_phoff; + } else { + ehdr_size = sizeof(header_32_); + phdr_address = ehdr_address_ + header_32_.e_phoff; + } + + CheckedVMAddressRange range(memory_.Is64Bit(), base_address, loaded_size); + if (!range.ContainsRange( + CheckedVMAddressRange(memory_.Is64Bit(), ehdr_address_, ehdr_size))) { + LOG_IF(ERROR, verbose) << "ehdr out of range"; + return false; + } + if (!range.ContainsRange(CheckedVMAddressRange( + memory.Is64Bit(), phdr_address, program_headers_->Size()))) { + LOG_IF(ERROR, verbose) << "phdrs out of range"; + return false; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +uint16_t ElfImageReader::FileType() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return memory_.Is64Bit() ? header_64_.e_type : header_32_.e_type; +} + +bool ElfImageReader::SoName(std::string* name) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (!InitializeDynamicArray()) { + return false; + } + + VMSize offset; + if (!dynamic_array_->GetValue(DT_SONAME, true, &offset)) { + return false; + } + + return ReadDynamicStringTableAtOffset(offset, name); +} + +bool ElfImageReader::GetDynamicSymbol(const std::string& name, + VMAddress* address, + VMSize* size) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (!InitializeDynamicSymbolTable()) { + return false; + } + + ElfSymbolTableReader::SymbolInformation info; + if (!symbol_table_->GetSymbol(name, &info)) { + return false; + } + if (info.shndx == SHN_UNDEF || info.shndx == SHN_COMMON) { + return false; + } + + switch (info.binding) { + case STB_GLOBAL: + case STB_WEAK: + break; + + case STB_LOCAL: + default: + return false; + } + + switch (info.type) { + case STT_OBJECT: + case STT_FUNC: + break; + + case STT_COMMON: + case STT_NOTYPE: + case STT_SECTION: + case STT_FILE: + case STT_TLS: + default: + return false; + } + + if (info.shndx != SHN_ABS) { + info.address += GetLoadBias(); + } + + *address = info.address; + *size = info.size; + return true; +} + +bool ElfImageReader::ReadDynamicStringTableAtOffset(VMSize offset, + std::string* string) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (!InitializeDynamicArray()) { + return false; + } + + VMAddress string_table_address; + VMSize string_table_size; + if (!GetAddressFromDynamicArray(DT_STRTAB, true, &string_table_address) || + !dynamic_array_->GetValue(DT_STRSZ, true, &string_table_size)) { + LOG(ERROR) << "missing string table info"; + return false; + } + if (offset >= string_table_size) { + LOG(ERROR) << "bad offset"; + return false; + } + + // GNU ld.so doesn't adjust the vdso's dynamic array entries by the load bias. + // If the address is too small to point into the loaded module range and is + // small enough to be an offset from the base of the module, adjust it now. + if (string_table_address < memory_.Base() && + string_table_address < memory_.Size()) { + string_table_address += GetLoadBias(); + } + + if (!memory_.ReadCStringSizeLimited( + string_table_address + offset, string_table_size - offset, string)) { + LOG(ERROR) << "missing nul-terminator"; + return false; + } + return true; +} + +bool ElfImageReader::GetDebugAddress(VMAddress* debug) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (!InitializeDynamicArray()) { + return false; + } + return GetAddressFromDynamicArray(DT_DEBUG, true, debug); +} + +bool ElfImageReader::GetDynamicArrayAddress(VMAddress* address) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + VMAddress dyn_segment_address; + VMSize dyn_segment_size; + if (!program_headers_.get()->GetDynamicSegment(&dyn_segment_address, + &dyn_segment_size)) { + LOG(ERROR) << "no dynamic segment"; + return false; + } + *address = dyn_segment_address + GetLoadBias(); + return true; +} + +VMAddress ElfImageReader::GetProgramHeaderTableAddress() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return ehdr_address_ + + (memory_.Is64Bit() ? header_64_.e_phoff : header_32_.e_phoff); +} + +bool ElfImageReader::InitializeProgramHeaders(bool verbose) { +#define INITIALIZE_PROGRAM_HEADERS(PhdrType, header) \ + do { \ + if (header.e_phentsize != sizeof(PhdrType)) { \ + LOG_IF(ERROR, verbose) << "unexpected phdr size"; \ + return false; \ + } \ + auto phdrs = new ProgramHeaderTableSpecific(); \ + program_headers_.reset(phdrs); \ + if (!phdrs->Initialize(memory_, \ + ehdr_address_ + header.e_phoff, \ + header.e_phnum, \ + verbose)) { \ + return false; \ + } \ + } while (false); + + if (memory_.Is64Bit()) { + INITIALIZE_PROGRAM_HEADERS(Elf64_Phdr, header_64_); + } else { + INITIALIZE_PROGRAM_HEADERS(Elf32_Phdr, header_32_); + } + return true; +} + +bool ElfImageReader::InitializeDynamicArray() { + if (dynamic_array_initialized_.is_valid()) { + return true; + } + if (!dynamic_array_initialized_.is_uninitialized()) { + return false; + } + dynamic_array_initialized_.set_invalid(); + + VMAddress dyn_segment_address; + VMSize dyn_segment_size; + if (!program_headers_.get()->GetDynamicSegment(&dyn_segment_address, + &dyn_segment_size)) { + LOG(ERROR) << "no dynamic segment"; + return false; + } + dyn_segment_address += GetLoadBias(); + + dynamic_array_.reset(new ElfDynamicArrayReader()); + if (!dynamic_array_->Initialize( + memory_, dyn_segment_address, dyn_segment_size)) { + return false; + } + dynamic_array_initialized_.set_valid(); + return true; +} + +bool ElfImageReader::InitializeDynamicSymbolTable() { + if (symbol_table_initialized_.is_valid()) { + return true; + } + if (!symbol_table_initialized_.is_uninitialized()) { + return false; + } + symbol_table_initialized_.set_invalid(); + + if (!InitializeDynamicArray()) { + return false; + } + + VMAddress symbol_table_address; + if (!GetAddressFromDynamicArray(DT_SYMTAB, true, &symbol_table_address)) { + LOG(ERROR) << "no symbol table"; + return false; + } + + // Try both DT_HASH and DT_GNU_HASH. They're completely different, but both + // circuitously offer a way to find the number of entries in the symbol table. + // DT_HASH is specifically checked first, because depending on the linker, the + // count maybe be incorrect for zero-export cases. In practice, it is believed + // that the zero-export case is probably not particularly useful, so this + // incorrect count will only occur in constructed test cases (see + // ElfImageReader.DtHashAndDtGnuHashMatch). + VMSize number_of_symbol_table_entries; + if (!GetNumberOfSymbolEntriesFromDtHash(&number_of_symbol_table_entries) && + !GetNumberOfSymbolEntriesFromDtGnuHash(&number_of_symbol_table_entries)) { + LOG(ERROR) << "could not retrieve number of symbol table entries"; + return false; + } + + symbol_table_.reset(new ElfSymbolTableReader( + &memory_, this, symbol_table_address, number_of_symbol_table_entries)); + symbol_table_initialized_.set_valid(); + return true; +} + +bool ElfImageReader::GetAddressFromDynamicArray(uint64_t tag, + bool log, + VMAddress* address) { + if (!dynamic_array_->GetValue(tag, log, address)) { + return false; + } +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_FUCHSIA) + // The GNU loader updates the dynamic array according to the load bias. + // The Android and Fuchsia loaders only update the debug address. + if (tag != DT_DEBUG) { + *address += GetLoadBias(); + } +#endif // BUILDFLAG(IS_ANDROID) + return true; +} + +bool ElfImageReader::GetNumberOfSymbolEntriesFromDtHash( + VMSize* number_of_symbol_table_entries) { + if (!InitializeDynamicArray()) { + return false; + } + + VMAddress dt_hash_address; + if (!GetAddressFromDynamicArray(DT_HASH, false, &dt_hash_address)) { + return false; + } + + struct { + uint32_t nbucket; + uint32_t nchain; + } header; + + if (!memory_.Read(dt_hash_address, sizeof(header), &header)) { + LOG(ERROR) << "failed to read DT_HASH header"; + return false; + } + + *number_of_symbol_table_entries = header.nchain; + return true; +} + +bool ElfImageReader::GetNumberOfSymbolEntriesFromDtGnuHash( + VMSize* number_of_symbol_table_entries) { + if (!InitializeDynamicArray()) { + return false; + } + + VMAddress dt_gnu_hash_address; + if (!GetAddressFromDynamicArray(DT_GNU_HASH, false, &dt_gnu_hash_address)) { + return false; + } + + // See https://flapenguin.me/2017/05/10/elf-lookup-dt-gnu-hash/ and + // https://sourceware.org/ml/binutils/2006-10/msg00377.html. + struct { + uint32_t nbuckets; + uint32_t symoffset; + uint32_t bloom_size; + uint32_t bloom_shift; + } header; + if (!memory_.Read(dt_gnu_hash_address, sizeof(header), &header)) { + LOG(ERROR) << "failed to read DT_GNU_HASH header"; + return false; + } + + std::vector buckets(header.nbuckets); + const size_t kNumBytesForBuckets = sizeof(buckets[0]) * buckets.size(); + const size_t kWordSize = + memory_.Is64Bit() ? sizeof(uint64_t) : sizeof(uint32_t); + const VMAddress buckets_address = + dt_gnu_hash_address + sizeof(header) + (kWordSize * header.bloom_size); + if (!memory_.Read(buckets_address, kNumBytesForBuckets, buckets.data())) { + LOG(ERROR) << "read buckets"; + return false; + } + + // Locate the chain that handles the largest index bucket. + uint32_t last_symbol = 0; + for (uint32_t i = 0; i < header.nbuckets; ++i) { + last_symbol = std::max(buckets[i], last_symbol); + } + + if (last_symbol < header.symoffset) { + *number_of_symbol_table_entries = header.symoffset; + return true; + } + + // Walk the bucket's chain to add the chain length to the total. + const VMAddress chains_base_address = buckets_address + kNumBytesForBuckets; + for (;;) { + uint32_t chain_entry; + if (!memory_.Read(chains_base_address + (last_symbol - header.symoffset) * + sizeof(chain_entry), + sizeof(chain_entry), + &chain_entry)) { + LOG(ERROR) << "read chain entry"; + return false; + } + + ++last_symbol; + + // If the low bit is set, this entry is the end of the chain. + if (chain_entry & 1) + break; + } + + *number_of_symbol_table_entries = last_symbol; + return true; +} + +std::unique_ptr ElfImageReader::Notes( + size_t max_note_size) { + return std::make_unique( + this, &memory_, program_headers_.get(), max_note_size); +} + +std::unique_ptr +ElfImageReader::NotesWithNameAndType(const std::string& name, + NoteReader::NoteType type, + size_t max_note_size) { + return std::make_unique( + this, &memory_, program_headers_.get(), max_note_size, name, type, true); +} + +const ProcessMemoryRange* ElfImageReader::Memory() const { + return &memory_; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/elf/elf_image_reader.h b/shared/sentry/external/crashpad/snapshot/elf/elf_image_reader.h new file mode 100644 index 000000000..1fd399fc4 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/elf/elf_image_reader.h @@ -0,0 +1,296 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_ELF_ELF_IMAGE_READER_H_ +#define CRASHPAD_SNAPSHOT_ELF_ELF_IMAGE_READER_H_ + +#include +#include +#include + +#include +#include + +#include "snapshot/elf/elf_dynamic_array_reader.h" +#include "snapshot/elf/elf_symbol_table_reader.h" +#include "util/misc/address_types.h" +#include "util/misc/initialization_state.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/process/process_memory_range.h" + +namespace crashpad { + +//! \brief A reader for ELF images mapped into another process. +//! +//! This class is capable of reading both 32-bit and 64-bit images. +class ElfImageReader { + private: + class ProgramHeaderTable; + + public: + //! \brief This class enables reading note segments from an ELF image. + //! + //! Objects of this class should be created by calling + //! ElfImageReader::Notes() or ElfImageReader::NotesWithNameAndType(). + class NoteReader { + public: + NoteReader(const NoteReader&) = delete; + NoteReader& operator=(const NoteReader&) = delete; + + ~NoteReader(); + + //! \brief The return value for NextNote(). + enum class Result { + //! \brief An error occurred. The NoteReader is invalidated and message is + //! logged. + kError, + + //! \brief A note was found. + kSuccess, + + //! \brief No more notes were found. + kNoMoreNotes, + }; + + //! \brief A type large enough to hold a note type, potentially across + //! bitness. + using NoteType = decltype(Elf64_Nhdr::n_type); + + //! \brief Searches for the next note in the image. + //! + //! \param[out] name The name of the note owner, if not `nullptr`. + //! \param[out] type A type for the note, if not `nullptr`. + //! \param[out] desc The note descriptor. + //! \param[out] desc_addr The address in the remote process' address space + //! \a desc was read from. + //! \return a #Result value. \a name, \a type, \a desc, and \a desc_addr are + //! only valid if this method returns Result::kSuccess. + Result NextNote(std::string* name, + NoteType* type, + std::string* desc, + VMAddress* desc_addr); + + // private + NoteReader(const ElfImageReader* elf_reader_, + const ProcessMemoryRange* range, + const ProgramHeaderTable* phdr_table, + size_t max_note_size, + const std::string& name_filter = std::string(), + NoteType type_filter = 0, + bool use_filter = false); + + private: + // Reads the next note at the current segment address. Sets retry_ to true + // and returns kError if use_filter_ is true and the note's name and type do + // not match name_filter_ and type_filter_. + template + Result ReadNote(std::string* name, + NoteType* type, + std::string* desc, + VMAddress* desc_addr); + + VMAddress current_address_; + VMAddress segment_end_address_; + const ElfImageReader* elf_reader_; // weak + const ProcessMemoryRange* range_; // weak + const ProgramHeaderTable* phdr_table_; // weak + std::unique_ptr segment_range_; + size_t phdr_index_; + size_t max_note_size_; + std::string name_filter_; + NoteType type_filter_; + bool use_filter_; + bool is_valid_; + bool retry_; + }; + + ElfImageReader(); + + ElfImageReader(const ElfImageReader&) = delete; + ElfImageReader& operator=(const ElfImageReader&) = delete; + + ~ElfImageReader(); + + //! \brief Initializes the reader. + //! + //! This method must be called once on an object and must be successfully + //! called before any other method in this class may be called. + //! + //! \param[in] memory A memory reader for the remote process. + //! \param[in] address The address in the remote process' address space where + //! the ELF image is loaded. + //! \param[in] verbose `true` if this method should log error messages during + //! initialization. Setting this value to `false` will reduce the error + //! messages relating to verifying the ELF image, but may not suppress + //! logging entirely. + bool Initialize(const ProcessMemoryRange& memory, + VMAddress address, + bool verbose = true); + + //! \brief Returns the base address of the image's memory range. + //! + //! This may differ from the address passed to Initialize() if the ELF header + //! is not loaded at the start of the first `PT_LOAD` segment. + VMAddress Address() const { return memory_.Base(); } + + //! \brief Returns the size of the range containing all loaded segments for + //! this image. + //! + //! The size may include memory that is unmapped or mapped to other objects if + //! this image's `PT_LOAD` segments are not contiguous. + VMSize Size() const { return memory_.Size(); } + + //! \brief Returns the file type for the image. + //! + //! Possible values include `ET_EXEC` or `ET_DYN` from ``. + uint16_t FileType() const; + + //! \brief Returns the load bias for the image. + //! + //! The load bias is the actual load address minus the preferred load address. + VMOffset GetLoadBias() const { return load_bias_; } + + //! \brief Determines the name of this object using `DT_SONAME`, if present. + //! + //! \param[out] name The name of this object, only valid if this method + //! returns `true`. + //! \return `true` if a name was found for this object. + bool SoName(std::string* name); + + //! \brief Reads information from the dynamic symbol table about the symbol + //! identified by \a name. + //! + //! \param[in] name The name of the symbol to search for. + //! \param[out] address The address of the symbol in the target process' + //! address space, if found. + //! \param[out] size The size of the symbol, if found. + //! \return `true` if the symbol was found. + bool GetDynamicSymbol(const std::string& name, + VMAddress* address, + VMSize* size); + + //! \brief Reads a `NUL`-terminated C string from this image's dynamic string + //! table. + //! + //! \param[in] offset the byte offset in the string table to start reading. + //! \param[out] string the string read. + //! \return `true` on success. Otherwise `false` with a message logged. + bool ReadDynamicStringTableAtOffset(VMSize offset, std::string* string); + + //! \brief Determine the debug address. + //! + //! The debug address is a pointer to an `r_debug` struct defined in + //! ``. + //! + //! \param[out] debug the debug address, if found. + //! \return `true` if the debug address was found. + bool GetDebugAddress(VMAddress* debug); + + //! \brief Determine the address of `PT_DYNAMIC` segment. + //! + //! \param[out] address The address of the array, valid if this method returns + //! `true`. + //! \return `true` on success. Otherwise `false` with a message logged. + bool GetDynamicArrayAddress(VMAddress* address); + + //! \brief Return the address of the program header table. + VMAddress GetProgramHeaderTableAddress(); + + //! \brief Return a NoteReader for this image, which scans all PT_NOTE + //! segments in the image. + //! + //! The returned NoteReader is only valid for the lifetime of the + //! ElfImageReader that created it. + //! + //! \param[in] max_note_size The maximum note size to read. Notes whose + //! combined name, descriptor, and padding size are greater than + //! \a max_note_size will be silently skipped. + //! \return A NoteReader object capable of reading notes in this image. + std::unique_ptr Notes(size_t max_note_size); + + //! \brief Return a NoteReader for this image, which scans all PT_NOTE + //! segments in the image, filtering by name and type. + //! + //! The returned NoteReader is only valid for the lifetime of the + //! ElfImageReader that created it. + //! + //! \param[in] name The note name to match. + //! \param[in] type The note type to match. + //! \param[in] max_note_size The maximum note size to read. Notes whose + //! combined name, descriptor, and padding size are greater than + //! \a max_note_size will be silently skipped. + //! \return A NoteReader object capable of reading notes in this image. + std::unique_ptr NotesWithNameAndType(const std::string& name, + NoteReader::NoteType type, + size_t max_note_size); + + //! \brief Return a ProcessMemoryRange restricted to the range of this image. + //! + //! The caller does not take ownership of the returned object. + const ProcessMemoryRange* Memory() const; + + //! \brief Retrieves the number of symbol table entries in `DT_SYMTAB` + //! according to the data in the `DT_HASH` section. + //! + //! \note Exposed for testing, not normally otherwise useful. + //! + //! \param[out] number_of_symbol_table_entries The number of entries expected + //! in `DT_SYMTAB`. + //! \return `true` if a `DT_HASH` section was found, and was read + //! successfully, otherwise `false` with an error logged. + bool GetNumberOfSymbolEntriesFromDtHash( + VMSize* number_of_symbol_table_entries); + + //! \brief Retrieves the number of symbol table entries in `DT_SYMTAB` + //! according to the data in the `DT_GNU_HASH` section. + //! + //! \note Exposed for testing, not normally otherwise useful. + //! + //! \note Depending on the linker that generated the `DT_GNU_HASH` section, + //! this value may not be as expected if there are zero exported symbols. + //! + //! \param[out] number_of_symbol_table_entries The number of entries expected + //! in `DT_SYMTAB`. + //! \return `true` if a `DT_GNU_HASH` section was found, and was read + //! successfully, otherwise `false` with an error logged. + bool GetNumberOfSymbolEntriesFromDtGnuHash( + VMSize* number_of_symbol_table_entries); + + private: + template + class ProgramHeaderTableSpecific; + + bool InitializeProgramHeaders(bool verbose); + bool InitializeDynamicArray(); + bool InitializeDynamicSymbolTable(); + bool GetAddressFromDynamicArray(uint64_t tag, bool log, VMAddress* address); + + union { + Elf32_Ehdr header_32_; + Elf64_Ehdr header_64_; + }; + VMAddress ehdr_address_; + VMOffset load_bias_; + ProcessMemoryRange memory_; + std::unique_ptr program_headers_; + std::unique_ptr dynamic_array_; + std::unique_ptr symbol_table_; + InitializationStateDcheck initialized_; + InitializationState dynamic_array_initialized_; + InitializationState symbol_table_initialized_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_ELF_ELF_IMAGE_READER_H_ diff --git a/shared/sentry/external/crashpad/snapshot/elf/elf_image_reader_fuzzer.cc b/shared/sentry/external/crashpad/snapshot/elf/elf_image_reader_fuzzer.cc new file mode 100644 index 000000000..b780af4e1 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/elf/elf_image_reader_fuzzer.cc @@ -0,0 +1,78 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 + +#include "base/logging.h" +#include "snapshot/elf/elf_image_reader.h" +#include "util/process/process_memory.h" + +using namespace crashpad; + +class FakeProcessMemory : public ProcessMemory { + public: + FakeProcessMemory(const uint8_t* data, size_t size, VMAddress fake_base) + : data_(data), size_(size), fake_base_(fake_base) {} + + ssize_t ReadUpTo(VMAddress address, + size_t size, + void* buffer) const override { + VMAddress offset_in_data = address - fake_base_; + if (offset_in_data > size_) + return -1; + size_t read_size = + std::min(static_cast(size_ - offset_in_data), size); + memcpy(buffer, &data_[offset_in_data], read_size); + return read_size; + } + + private: + const uint8_t* data_; + size_t size_; + VMAddress fake_base_; +}; + +extern "C" int LLVMFuzzerInitialize(int* argc, char*** argv) { + // Swallow all logs to avoid spam. + logging::SetLogMessageHandler( + [](logging::LogSeverity, const char*, int, size_t, const std::string&) { + return true; + }); + return 0; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { + constexpr size_t kBase = 0x10000; + FakeProcessMemory process_memory(data, size, kBase); + ProcessMemoryRange process_memory_range; + process_memory_range.Initialize(&process_memory, true, kBase, size); + + ElfImageReader reader; + if (!reader.Initialize(process_memory_range, kBase)) + return 0; + + ElfImageReader::NoteReader::Result result; + std::string note_name; + std::string note_desc; + ElfImageReader::NoteReader::NoteType note_type; + VMAddress desc_addr; + auto notes = reader.Notes(9999); + while ((result = notes->NextNote( + ¬e_name, ¬e_type, ¬e_desc, &desc_addr)) == + ElfImageReader::NoteReader::Result::kSuccess) { + LOG(ERROR) << note_name << note_type << note_desc; + } + + return 0; +} diff --git a/shared/sentry/external/crashpad/snapshot/elf/elf_image_reader_fuzzer_corpus/.gitattributes b/shared/sentry/external/crashpad/snapshot/elf/elf_image_reader_fuzzer_corpus/.gitattributes new file mode 100644 index 000000000..69463d136 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/elf/elf_image_reader_fuzzer_corpus/.gitattributes @@ -0,0 +1,17 @@ +# Copyright 2019 The Crashpad Authors. All rights reserved. +# +# 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. + +# ELF executables normally don’t have any extension, so there’s no pattern to +# match in the root .gitattributes file. +/ret42 binary diff --git a/shared/sentry/external/crashpad/snapshot/elf/elf_image_reader_fuzzer_corpus/crashpad_snapshot_test_both_dt_hash_styles.so b/shared/sentry/external/crashpad/snapshot/elf/elf_image_reader_fuzzer_corpus/crashpad_snapshot_test_both_dt_hash_styles.so new file mode 100755 index 0000000000000000000000000000000000000000..4dc7cdbe580c89810de838820baf6cbddc89d9ae GIT binary patch literal 8544 zcmeHMU2Ggz6~61W6GNQ!HiX9cLD^PGNvJY7aom`O5XX+4F||`SIH(mwPfv4Qn%(>^g=bU@)o%?(Civz=hO_7K~imHz(<`!CAq*n?O+ayDzSM{iM^!|X_ zp=DdviUPTxyw`<5l}c!U>sUumWus<^CUgdLp~=Oc6AC?MSx?2>aurLs8KD(v3aB(g zjoxxn%_RBz_qgJIdeDUxD9dtsg7kEKF8hw}xJaCm`Wm$_A?*Scx7AM4e;+*?=waWv z?kGK+SY^G=t-YlClr&2hw9&hr9_jj;@TOYM&7h6^{opsc)hDX%^8@dK=Qgn(`+@KM z)x|_a?V5j>)fwk@vcG5t*U!FlVxLg2-*+zm^Q&*gu3dUN_S?@qx#OpQ`sU}a|MT?b z{(;wSf1&Lw+vgtJ`{VAB!C$akSo$G>XtTHceqs_{K=LmU4fBE9l@J*(`4ll>_NN-) z2MABl<4?~K5N2nX@CW_EBD;xt^QLz|5N^}6iAKq)ZvcIi8W2|k^WquIvdebCB6|+<=hCHemTJpq z%X!;LW~^i;ojhxu>mdP>%{ru4vXaGfR?kG%q0CHysxQMvy~uQOt-!I#OfqxUnzXYy zHs&^mjk2|5!6}u4Fj>%GaoW=L*Nb{lmds@*l18~`Jff_0%64p0ohX;xst78iRI+53 zGt+j;Di`eOa;E55PP*(^6GbOur5r0mr6fL=OP9%xIx#%d*KZv#4jA3FJeEnlCSlY> zRV3usq#o8|!h8bN zyM1Rb<(SW~%5tSjY4@AVv#ZNDHUCHExoegeHUB&2+2!S{n*R;++(paJYyQi`PhKWo zC-c&g>ja!OFST4FYJOzZyl~6eO0M-WORjA^Q>{!I?K_{=@@HL1I5I?HYUab{`6I8dqugosr_H>0 zWE0^_N2m!)CcR#6d4Tu`yR*QK{pALkJno90S)L>bmmf`;7n^tQAqn$B^R9)KSIK5% z4LjVtYo>Ys;!`)4zI(S?ZQrRzsXeItItcqT zJu8IoBdU(Y;*+sfdb@Sb64XpS?3J>qSgZDh2g(k@_4$CLA0zx;y4M5udf?sl0QOJp zm)IY%A7anpJ(^R@C2B2G(dMW*Aj*a@0r5o9n;G)6%7RaZjxX|EQGzLwK}H%>MsKED>7QL(=y`IHj4w*+f^@ zfu61&y&1rAkn(vGRIOJSC-w5J z3gf9B58lVPJW4m?xb$zm{0220inrC`HTCkrd!TDSR*Qqw%eSjICX}ZjN;gEzZx2j< z-ls6H>v6g%hm5NzMK*Aru9v4-DD$%(f4}mNFD{R&;QiCZgZE4q58iKG9IF>|ZsKv< ztlqAI2jz9Ji6Rwc?th10mM45=^zKwY@sPSNb=EV-yZP=O9@^OXx z5AbhOCd_}1cW6Il!NNNJqTrVW$CPW-55FS$2t{aK2pK0~_5MN22fyQ+QZLqveZ+Hm zi=GHaT72VYRhI_G?FMo*5;7h*ju7G1-8ZiG5l%eWPDs4tJA{YTdsXE9ah9JmPUFWPm-(fZ5BgaV`Eg%A+}3!|&(#L_R@ykj{Olq; z-V~g7CgVZ#>MY?8hTwOo-mv%-#VVY#Gc!49B-L7=$8z#kl0!Wl7fBVZsa$cw&Qa{5 zNI@8TW>zJO`RQERNvDiMo%{EP3+WJ&WtU3!97RQ((wv$s+4;1Un#t$qNX5hHz)qd0 zj*&QX)9JE~ofyfa;wX}lFQ#U4X)8&dC^x862U*tOsbeDp*1*Z*9K^Cx%C(Cv|o1|)s z4qGIr+)$uD(&z>rxr+??63U>Nlc(7bGDZIDWebFW~#c z-|37D{c*{FE;P^=SnZ|^-wB{!kqqc7wC-@?`Z$aFah~UWiG71U&WWJ7caVrqVh*gL zp`-W?0DYV%K_3!Hv>$SyeN@hO3h+30f_93&-+zJC6O`dH=;M3}x*~$8-`{@zAA|MT z4^BA8g3h|I?jycS`SsI;ai8G0jC&*~?ptC;*M&UOaeBP^I2VI*xi?|Y0X<8FUVWUO zLE$fKfCuG?;MK?Z4;1%g=%Zfn^FDptn?dE+1@S=p7epW5uhIXOFu>o+o4!sA`wxAb z??JD4g(;7A1LpH2pK&oha1X%$nwTe$hd$`{sG!}WkNd)^96%x0NdGDo@Yq59wR52a zDk|JKN@zFi;X4$mj76Z2`$vVHqLk3@wHNe3uTha#zjmIK3VPR6bq;!@6`%9Pu@l?pi<6LRQj}Fm#3kU3ohGhB6Y4vEWDVz}P3=~waaL#F**>_B zaJNV8h!0Cnk=){90TSU41plETK;;im_;%zdAWZWTqY6!FIANj2mVrWOl3+WIH58B{ zp(ZQ9po$};5OyrGU0B(Ll^yU^6&MiXPBf=e+2VNfczauO+cCY6(~rt= zvwzk-eP%%Rgn86R;+S{Q#cP1u|MbZ}j9oANBJ`{9n}^?c<-2=t9r|67ZE;ccKQ8U0 zsd;GcC9cVX?xh5ELtoH*4D;~YglP_s$IX#cHl9vhN|MM;X2FW*Ei)5O zWyR@OZ?w}qsvp&l3$wSs&rBrq$%{c1<&^%Wx0aKn&!5Z3chmR8&mJp8ZT8JtkZ-r*UpR5@uNhQ4j!Z5 z#{C^sYogl7Ec&^6m}>JQdgK1fl4`x%JN^0vWBPB#)UD+w`(rcJpQd3LGY$8s*_b)? zT8YZ;pKjO0dyV_MB=s%{1IA4C9YW*8iZOlLdWb@KkwprnVX(AV_CGk!QIutJ`D0d` zIsSd==FlqH8`oC-#`LmrZRrWacf)vn)e4crrd@tqQ<26(zeEa;5^FHP^b&|T^I~61R0pWSV z>x5}S-n*Zt$loG-hwuR1IexBi@m)G6eB(jizWSQlSzk?%d487!$?hYppo+J?5C^E8 z^tZUJA zh2lsu84U!#S``h1UiNnfw3#i@K=>=wMxf=(Ta7^bR83!?BOhpw1X?12a5SKi9_dE| zwQ^tolFaAGPv!n%mz!mC1~zA4a|SkNU~>lk>lsilE#!a5?SS>l@zpJMr9sJ;l^s48 zk!S8x?RZCWKF$UCCckUBp#2}Kr5wvw)XNRIpP`aQyi>2L_U)`pB|x8!Kq&@unwI3R)O{6Ch!#9Zlp#MC zn7`=a!-~iAPpQ-C7=NRb$_wlz%AJ1zk($AZ+vVL9NCEn+8fBiHeO*Gy~wcfCecM`8jU(TOr zCBJ?>93eg^_N-eE4a7Hz_4}et@=o6@4JbeE^)^Jjwq9L)miQf2djy_)Q28SBqP*|0 zU1x~r@dnj+dyNJ|Rn)@q9i$VJ5RyenO1oF$2BKN5{~vsZa=H(V&nP~ZQkE}4j1ahR0P`Z_ushkYh%*#~;0>pa~v6NQ{P z7SAU5%l6oZC+ib;0l{A_y>O;j89RsHlLT70#UKkU4Vj@faY_#%r z%jc5$LMoSa2qv}Vlj%4c2we_NznHf4(VQi8D>+WTyi7j;Vz)mLO9|wyC{B zYzx%MZ#Os#)ry&iEm^ z_|Xn~D` zRaF7v0G6L$JWsGkKK%@FoD0Jqc^eRUJ*mjTJ}S!v5;*6EJ@PvszE==G^njlsd(Pn@ zBM$^>sxx=|iWZls!gC7y6_wZn=aeG)caQ%vwey(pd`Dgh+@tIP?)!JkWsm$5i2nn? z9y0Jnm;IvZ0El>D4;lD%mp$HBz^}P(82W|3Z@KJ|_X6?#1%3DU?fi(}Wp+V+40P*5 z27XWVkNbx_8h8>ND}){BpSbLiZv)*M9`gqK-%uq$7rqCPhvENnZhgqW-;-@)g+0Ej zHFin`dbU)S!T*^WcQ^$9!N$Ct^LZ!zrgJ#dqQq7t@Y?HBgIMQW+E zU;A!Q9pE_u2e1R)aoHo^3n_cV>9&XbcdD>2#E-lxtPIf)^j*tYyhjbpwLn23?D70W zGwdr7YY#e%hU$MoJvjJ1!#N}3!MyPPKp9P<8;9 GJMnK#!s$K$ literal 0 HcmV?d00001 diff --git a/shared/sentry/external/crashpad/snapshot/elf/elf_image_reader_test.cc b/shared/sentry/external/crashpad/snapshot/elf/elf_image_reader_test.cc new file mode 100644 index 000000000..161eab029 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/elf/elf_image_reader_test.cc @@ -0,0 +1,362 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/elf/elf_image_reader.h" + +#include +#include +#include + +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "test/multiprocess_exec.h" +#include "test/process_type.h" +#include "test/scoped_module_handle.h" +#include "test/test_paths.h" +#include "util/file/file_io.h" +#include "util/misc/address_types.h" +#include "util/misc/elf_note_types.h" +#include "util/misc/from_pointer_cast.h" +#include "util/process/process_memory_native.h" + +#if BUILDFLAG(IS_FUCHSIA) +#include + +#include "base/fuchsia/fuchsia_logging.h" + +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) + +#include "test/linux/fake_ptrace_connection.h" +#include "util/linux/auxiliary_vector.h" +#include "util/linux/memory_map.h" + +#else + +#error Port. + +#endif // BUILDFLAG(IS_FUCHSIA) + +extern "C" { +__attribute__((visibility("default"))) void ElfImageReaderTestExportedSymbol() { +} +} // extern "C" + +namespace crashpad { +namespace test { +namespace { + +#if BUILDFLAG(IS_FUCHSIA) + +void LocateExecutable(const ProcessType& process, + ProcessMemory* memory, + VMAddress* elf_address) { + uintptr_t debug_address; + zx_status_t status = process->get_property( + ZX_PROP_PROCESS_DEBUG_ADDR, &debug_address, sizeof(debug_address)); + ASSERT_EQ(status, ZX_OK) + << "zx_object_get_property: ZX_PROP_PROCESS_DEBUG_ADDR"; + // Can be 0 if requested before the loader has loaded anything. + EXPECT_NE(debug_address, 0u); + + constexpr auto k_r_debug_map_offset = offsetof(r_debug, r_map); + uintptr_t map; + ASSERT_TRUE( + memory->Read(debug_address + k_r_debug_map_offset, sizeof(map), &map)) + << "read link_map"; + + constexpr auto k_link_map_addr_offset = offsetof(link_map, l_addr); + uintptr_t base; + ASSERT_TRUE(memory->Read(map + k_link_map_addr_offset, sizeof(base), &base)) + << "read base"; + + *elf_address = base; +} + +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) + +void LocateExecutable(PtraceConnection* connection, + ProcessMemory* memory, + VMAddress* elf_address) { + AuxiliaryVector aux; + ASSERT_TRUE(aux.Initialize(connection)); + + VMAddress phdrs; + ASSERT_TRUE(aux.GetValue(AT_PHDR, &phdrs)); + + MemoryMap memory_map; + ASSERT_TRUE(memory_map.Initialize(connection)); + const MemoryMap::Mapping* phdr_mapping = memory_map.FindMapping(phdrs); + ASSERT_TRUE(phdr_mapping); + auto possible_mappings = memory_map.FindFilePossibleMmapStarts(*phdr_mapping); + ASSERT_EQ(possible_mappings->Count(), 1u); + *elf_address = possible_mappings->Next()->range.Base(); +} + +#endif // BUILDFLAG(IS_FUCHSIA) + +void ExpectSymbol(ElfImageReader* reader, + const std::string& symbol_name, + VMAddress expected_symbol_address) { + VMAddress symbol_address; + VMSize symbol_size; + ASSERT_TRUE( + reader->GetDynamicSymbol(symbol_name, &symbol_address, &symbol_size)); + EXPECT_EQ(symbol_address, expected_symbol_address); + + EXPECT_FALSE( + reader->GetDynamicSymbol("notasymbol", &symbol_address, &symbol_size)); +} + +void ReadThisExecutableInTarget(ProcessType process, + VMAddress exported_symbol_address) { +#if defined(ARCH_CPU_64_BITS) + constexpr bool am_64_bit = true; +#else + constexpr bool am_64_bit = false; +#endif // ARCH_CPU_64_BITS + + VMAddress elf_address; +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) + FakePtraceConnection connection; + ASSERT_TRUE(connection.Initialize(process)); + ProcessMemoryLinux memory(&connection); + LocateExecutable(&connection, &memory, &elf_address); +#elif BUILDFLAG(IS_FUCHSIA) + ProcessMemoryFuchsia memory; + ASSERT_TRUE(memory.Initialize(process)); + LocateExecutable(process, &memory, &elf_address); +#endif + ASSERT_NO_FATAL_FAILURE(); + + ProcessMemoryRange range; + ASSERT_TRUE(range.Initialize(&memory, am_64_bit)); + + ElfImageReader reader; + ASSERT_TRUE(reader.Initialize(range, elf_address)); + + ExpectSymbol( + &reader, "ElfImageReaderTestExportedSymbol", exported_symbol_address); + + ElfImageReader::NoteReader::Result result; + std::string note_name; + std::string note_desc; + ElfImageReader::NoteReader::NoteType note_type; + VMAddress desc_addr; + + std::unique_ptr notes = reader.Notes(10000); + while ((result = notes->NextNote( + ¬e_name, ¬e_type, ¬e_desc, &desc_addr)) == + ElfImageReader::NoteReader::Result::kSuccess) { + } + EXPECT_EQ(result, ElfImageReader::NoteReader::Result::kNoMoreNotes); + + notes = reader.Notes(0); + EXPECT_EQ(notes->NextNote(¬e_name, ¬e_type, ¬e_desc, &desc_addr), + ElfImageReader::NoteReader::Result::kNoMoreNotes); + + // Find the note defined in elf_image_reader_test_note.S. + constexpr uint32_t kCrashpadNoteDesc = 42; + notes = reader.NotesWithNameAndType( + CRASHPAD_ELF_NOTE_NAME, CRASHPAD_ELF_NOTE_TYPE_SNAPSHOT_TEST, 10000); + ASSERT_EQ(notes->NextNote(¬e_name, ¬e_type, ¬e_desc, &desc_addr), + ElfImageReader::NoteReader::Result::kSuccess); + EXPECT_EQ(note_name, CRASHPAD_ELF_NOTE_NAME); + EXPECT_EQ(note_type, + implicit_cast(CRASHPAD_ELF_NOTE_TYPE_SNAPSHOT_TEST)); + EXPECT_EQ(note_desc.size(), sizeof(kCrashpadNoteDesc)); + EXPECT_EQ(*reinterpret_cast(¬e_desc[0]), + kCrashpadNoteDesc); + + EXPECT_EQ(notes->NextNote(¬e_name, ¬e_type, ¬e_desc, &desc_addr), + ElfImageReader::NoteReader::Result::kNoMoreNotes); +} + +void ReadLibcInTarget(ProcessType process, + VMAddress elf_address, + VMAddress getpid_address) { +#if defined(ARCH_CPU_64_BITS) + constexpr bool am_64_bit = true; +#else + constexpr bool am_64_bit = false; +#endif // ARCH_CPU_64_BITS + +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) + FakePtraceConnection connection; + ASSERT_TRUE(connection.Initialize(process)); + ProcessMemoryLinux memory(&connection); +#else + ProcessMemoryNative memory; + ASSERT_TRUE(memory.Initialize(process)); +#endif + + ProcessMemoryRange range; + ASSERT_TRUE(range.Initialize(&memory, am_64_bit)); + + ElfImageReader reader; + ASSERT_TRUE(reader.Initialize(range, elf_address)); + + ExpectSymbol(&reader, "getpid", getpid_address); +} + +TEST(ElfImageReader, MainExecutableSelf) { + ReadThisExecutableInTarget( + GetSelfProcess(), + FromPointerCast(ElfImageReaderTestExportedSymbol)); +} + +CRASHPAD_CHILD_TEST_MAIN(ReadExecutableChild) { + VMAddress exported_symbol_address = + FromPointerCast(ElfImageReaderTestExportedSymbol); + CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput), + &exported_symbol_address, + sizeof(exported_symbol_address)); + CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput)); + return 0; +} + +class ReadExecutableChildTest : public MultiprocessExec { + public: + ReadExecutableChildTest() : MultiprocessExec() {} + + private: + void MultiprocessParent() { + // This read serves two purposes -- on Fuchsia, the loader may have not + // filled in debug address as soon as the child process handle is valid, so + // this causes a wait at least until the main() of the child, at which point + // it will always be valid. Secondarily, the address of the symbol to be + // looked up needs to be communicated. + VMAddress exported_symbol_address; + CheckedReadFileExactly(ReadPipeHandle(), + &exported_symbol_address, + sizeof(exported_symbol_address)); + ReadThisExecutableInTarget(ChildProcess(), exported_symbol_address); + } +}; + +TEST(ElfImageReader, MainExecutableChild) { + ReadExecutableChildTest test; + test.SetChildTestMainFunction("ReadExecutableChild"); + test.Run(); +} + +TEST(ElfImageReader, OneModuleSelf) { + Dl_info info; + ASSERT_TRUE(dladdr(reinterpret_cast(getpid), &info)) << "dladdr:" + << dlerror(); + VMAddress elf_address = FromPointerCast(info.dli_fbase); + ReadLibcInTarget( + GetSelfProcess(), elf_address, FromPointerCast(getpid)); +} + +CRASHPAD_CHILD_TEST_MAIN(ReadLibcChild) { + // Get the address of libc (by using getpid() as a representative member), + // and also the address of getpid() itself, and write them to the parent, so + // it can validate reading this information back out. + Dl_info info; + EXPECT_TRUE(dladdr(reinterpret_cast(getpid), &info)) + << "dladdr:" << dlerror(); + VMAddress elf_address = FromPointerCast(info.dli_fbase); + VMAddress getpid_address = FromPointerCast(getpid); + + CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput), + &elf_address, + sizeof(elf_address)); + CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput), + &getpid_address, + sizeof(getpid_address)); + CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput)); + return 0; +} + +class ReadLibcChildTest : public MultiprocessExec { + public: + ReadLibcChildTest() : MultiprocessExec() {} + ~ReadLibcChildTest() {} + + private: + void MultiprocessParent() { + VMAddress elf_address, getpid_address; + CheckedReadFileExactly(ReadPipeHandle(), &elf_address, sizeof(elf_address)); + CheckedReadFileExactly( + ReadPipeHandle(), &getpid_address, sizeof(getpid_address)); + ReadLibcInTarget(ChildProcess(), elf_address, getpid_address); + } +}; + +TEST(ElfImageReader, OneModuleChild) { + ReadLibcChildTest test; + test.SetChildTestMainFunction("ReadLibcChild"); + test.Run(); +} + +#if BUILDFLAG(IS_FUCHSIA) + +// crashpad_snapshot_test_both_dt_hash_styles is specially built and forced to +// include both .hash and .gnu.hash sections. Linux, Android, and Fuchsia have +// different defaults for which of these sections should be included; this test +// confirms that we get the same count from both sections. +// +// TODO(scottmg): Investigation in https://crrev.com/c/876879 resulted in +// realizing that ld.bfd does not emit a .gnu.hash that is very useful for this +// purpose when there's 0 exported entries in the module. This is not likely to +// be too important, as there's little need to look up non-exported symbols. +// However, it makes this test not work on Linux, where the default build uses +// ld.bfd. On Fuchsia, the only linker in use is lld, and it generates the +// expected .gnu.hash. So, for now, this test is only run on Fuchsia, not Linux. +// +// TODO(scottmg): Separately, the location of the ELF on Android needs some +// work, and then the test could also be enabled there. +TEST(ElfImageReader, DtHashAndDtGnuHashMatch) { + base::FilePath module_path = + TestPaths::BuildArtifact(FILE_PATH_LITERAL("snapshot"), + FILE_PATH_LITERAL("both_dt_hash_styles"), + TestPaths::FileType::kLoadableModule); + // TODO(scottmg): Remove this when upstream Fuchsia bug ZX-1619 is resolved. + // See also explanation in build/run_tests.py for Fuchsia .so files. + module_path = module_path.BaseName(); + ScopedModuleHandle module( + dlopen(module_path.value().c_str(), RTLD_LAZY | RTLD_LOCAL)); + ASSERT_TRUE(module.valid()) << "dlopen " << module_path.value() << ": " + << dlerror(); + +#if defined(ARCH_CPU_64_BITS) + constexpr bool am_64_bit = true; +#else + constexpr bool am_64_bit = false; +#endif // ARCH_CPU_64_BITS + + ProcessMemoryNative memory; + ASSERT_TRUE(memory.Initialize(GetSelfProcess())); + ProcessMemoryRange range; + ASSERT_TRUE(range.Initialize(&memory, am_64_bit)); + + struct link_map* lm = reinterpret_cast(module.get()); + + ElfImageReader reader; + ASSERT_TRUE(reader.Initialize(range, lm->l_addr)); + + VMSize from_dt_hash; + ASSERT_TRUE(reader.GetNumberOfSymbolEntriesFromDtHash(&from_dt_hash)); + + VMSize from_dt_gnu_hash; + ASSERT_TRUE(reader.GetNumberOfSymbolEntriesFromDtGnuHash(&from_dt_gnu_hash)); + + EXPECT_EQ(from_dt_hash, from_dt_gnu_hash); +} + +#endif // BUILDFLAG(IS_FUCHSIA) + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/elf/elf_image_reader_test_note.S b/shared/sentry/external/crashpad/snapshot/elf/elf_image_reader_test_note.S new file mode 100644 index 000000000..b6c17ff32 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/elf/elf_image_reader_test_note.S @@ -0,0 +1,33 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "util/misc/elf_note_types.h" +#include "util/misc/arm64_pac_bti.S" + +#define NOTE_ALIGN 4 + .section .note.crashpad.test,"a",%note + .balign NOTE_ALIGN + .type testnote, %object +testnote: + .long name_end - name // namesz + .long desc_end - desc // descsz + .long CRASHPAD_ELF_NOTE_TYPE_SNAPSHOT_TEST // type +name: + .asciz CRASHPAD_ELF_NOTE_NAME +name_end: + .balign NOTE_ALIGN +desc: + .long 42 +desc_end: + .size testnote, .-testnote diff --git a/shared/sentry/external/crashpad/snapshot/elf/elf_symbol_table_reader.cc b/shared/sentry/external/crashpad/snapshot/elf/elf_symbol_table_reader.cc new file mode 100644 index 000000000..5cdf5ba6f --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/elf/elf_symbol_table_reader.cc @@ -0,0 +1,93 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/elf/elf_symbol_table_reader.h" + +#include + +#include "snapshot/elf/elf_image_reader.h" + +namespace crashpad { + +namespace { + +uint8_t GetBinding(const Elf32_Sym& sym) { + return ELF32_ST_BIND(sym.st_info); +} + +uint8_t GetBinding(const Elf64_Sym& sym) { + return ELF64_ST_BIND(sym.st_info); +} + +uint8_t GetType(const Elf32_Sym& sym) { + return ELF32_ST_TYPE(sym.st_info); +} + +uint8_t GetType(const Elf64_Sym& sym) { + return ELF64_ST_TYPE(sym.st_info); +} + +uint8_t GetVisibility(const Elf32_Sym& sym) { + return ELF32_ST_VISIBILITY(sym.st_other); +} + +uint8_t GetVisibility(const Elf64_Sym& sym) { + return ELF64_ST_VISIBILITY(sym.st_other); +} + +} // namespace + +ElfSymbolTableReader::ElfSymbolTableReader(const ProcessMemoryRange* memory, + ElfImageReader* elf_reader, + VMAddress address, + VMSize num_entries) + : memory_(memory), + elf_reader_(elf_reader), + base_address_(address), + num_entries_(num_entries) {} + +ElfSymbolTableReader::~ElfSymbolTableReader() {} + +bool ElfSymbolTableReader::GetSymbol(const std::string& name, + SymbolInformation* info) { + return memory_->Is64Bit() ? ScanSymbolTable(name, info) + : ScanSymbolTable(name, info); +} + +template +bool ElfSymbolTableReader::ScanSymbolTable(const std::string& name, + SymbolInformation* info_out) { + VMAddress address = base_address_; + SymEnt entry; + std::string string; + size_t i = 0; + while (i < num_entries_ && memory_->Read(address, sizeof(entry), &entry)) { + if (elf_reader_->ReadDynamicStringTableAtOffset(entry.st_name, &string) && + string == name) { + info_out->address = entry.st_value; + info_out->size = entry.st_size; + info_out->shndx = entry.st_shndx; + info_out->binding = GetBinding(entry); + info_out->type = GetType(entry); + info_out->visibility = GetVisibility(entry); + return true; + } + // TODO(scottmg): This should respect DT_SYMENT if present. + address += sizeof(entry); + ++i; + } + return false; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/elf/elf_symbol_table_reader.h b/shared/sentry/external/crashpad/snapshot/elf/elf_symbol_table_reader.h new file mode 100644 index 000000000..89f99e9af --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/elf/elf_symbol_table_reader.h @@ -0,0 +1,90 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_ELF_ELF_SYMBOL_TABLE_READER_H_ +#define CRASHPAD_SNAPSHOT_ELF_ELF_SYMBOL_TABLE_READER_H_ + +#include + +#include + +#include "util/misc/address_types.h" +#include "util/process/process_memory_range.h" + +namespace crashpad { + +class ElfImageReader; + +//! \brief A reader for symbol tables in ELF images mapped into another process. +class ElfSymbolTableReader { + public: + //! \brief Information about a symbol in a module's symbol table. + struct SymbolInformation { + //! \brief The address of the symbol as it exists in the symbol table, not + //! adjusted for any load bias. + VMAddress address; + + //! \brief The size of the symbol. + VMSize size; + + //! \brief The section index that the symbol definition is in relation to. + uint16_t shndx; + + //! \brief Specifies the type of symbol. Possible values include + //! `STT_OBJECT`, `STT_FUNC`, etc. + uint8_t type; + + //! \brief Specifies the default scope at which a symbol takes precedence. + //! Possible values include `STB_LOCAL`, `STB_GLOBAL`, `STB_WEAK`, or + //! OS/processor specific values. + uint8_t binding; + + //! \brief Together with binding, can limit the visibility of a symbol to + //! the module that defines it. Possible values include `STV_DEFAULT`, + //! `STV_INTERNAL`, `STV_HIDDEN`, and `STV_PROTECTED`. + uint8_t visibility; + }; + + // TODO(jperaza): Support using .hash and .gnu.hash sections to improve symbol + // lookup. + ElfSymbolTableReader(const ProcessMemoryRange* memory, + ElfImageReader* elf_reader, + VMAddress address, + VMSize num_entries); + + ElfSymbolTableReader(const ElfSymbolTableReader&) = delete; + ElfSymbolTableReader& operator=(const ElfSymbolTableReader&) = delete; + + ~ElfSymbolTableReader(); + + //! \brief Lookup information about a symbol. + //! + //! \param[in] name The name of the symbol to search for. + //! \param[out] info The symbol information, if found. + //! \return `true` if the symbol is found. + bool GetSymbol(const std::string& name, SymbolInformation* info); + + private: + template + bool ScanSymbolTable(const std::string& name, SymbolInformation* info); + + const ProcessMemoryRange* const memory_; // weak + ElfImageReader* const elf_reader_; // weak + const VMAddress base_address_; + const VMSize num_entries_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_ELF_ELF_SYMBOL_TABLE_READER_H_ diff --git a/shared/sentry/external/crashpad/snapshot/elf/module_snapshot_elf.cc b/shared/sentry/external/crashpad/snapshot/elf/module_snapshot_elf.cc new file mode 100644 index 000000000..037e7cdb7 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/elf/module_snapshot_elf.cc @@ -0,0 +1,253 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/elf/module_snapshot_elf.h" + +#include + +#include + +#include "base/files/file_path.h" +#include "snapshot/crashpad_types/image_annotation_reader.h" +#include "snapshot/memory_snapshot_generic.h" +#include "util/misc/elf_note_types.h" + +namespace crashpad { +namespace internal { + +ModuleSnapshotElf::ModuleSnapshotElf(const std::string& name, + ElfImageReader* elf_reader, + ModuleSnapshot::ModuleType type, + ProcessMemoryRange* process_memory_range, + const ProcessMemory* process_memory) + : ModuleSnapshot(), + name_(name), + elf_reader_(elf_reader), + process_memory_range_(process_memory_range), + process_memory_(process_memory), + crashpad_info_(), + type_(type), + initialized_(), + streams_() {} + +ModuleSnapshotElf::~ModuleSnapshotElf() = default; + +bool ModuleSnapshotElf::Initialize() { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + if (!elf_reader_) { + LOG(ERROR) << "no elf reader"; + return false; + } + + // The data payload is only sizeof(VMAddress) in the note, but add a bit to + // account for the name, header, and padding. + constexpr ssize_t kMaxNoteSize = 256; + std::unique_ptr notes = + elf_reader_->NotesWithNameAndType(CRASHPAD_ELF_NOTE_NAME, + CRASHPAD_ELF_NOTE_TYPE_CRASHPAD_INFO, + kMaxNoteSize); + std::string desc; + VMAddress info_address; + VMAddress desc_address; + if (notes->NextNote(nullptr, nullptr, &desc, &desc_address) == + ElfImageReader::NoteReader::Result::kSuccess) { + VMOffset offset; + if (elf_reader_->Memory()->Is64Bit()) { + offset = *reinterpret_cast(&desc[0]); + } else { + int32_t offset32 = *reinterpret_cast(&desc[0]); + offset = offset32; + } + info_address = desc_address + offset; + + ProcessMemoryRange range; + if (range.Initialize(*elf_reader_->Memory())) { + auto info = std::make_unique(); + if (info->Initialize(&range, info_address)) { + crashpad_info_ = std::move(info); + } + } + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +bool ModuleSnapshotElf::GetCrashpadOptions(CrashpadInfoClientOptions* options) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (!crashpad_info_) { + return false; + } + + options->crashpad_handler_behavior = + crashpad_info_->CrashpadHandlerBehavior(); + options->system_crash_reporter_forwarding = + crashpad_info_->SystemCrashReporterForwarding(); + options->gather_indirectly_referenced_memory = + crashpad_info_->GatherIndirectlyReferencedMemory(); + options->indirectly_referenced_memory_cap = + crashpad_info_->IndirectlyReferencedMemoryCap(); + return true; +} + +std::string ModuleSnapshotElf::Name() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return name_; +} + +uint64_t ModuleSnapshotElf::Address() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return elf_reader_->Address(); +} + +uint64_t ModuleSnapshotElf::Size() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return elf_reader_->Size(); +} + +time_t ModuleSnapshotElf::Timestamp() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return 0; +} + +void ModuleSnapshotElf::FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *version_0 = 0; + *version_1 = 0; + *version_2 = 0; + *version_3 = 0; +} + +void ModuleSnapshotElf::SourceVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *version_0 = 0; + *version_1 = 0; + *version_2 = 0; + *version_3 = 0; +} + +ModuleSnapshot::ModuleType ModuleSnapshotElf::GetModuleType() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return type_; +} + +void ModuleSnapshotElf::UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *age = 0; + + auto build_id = BuildID(); + build_id.insert( + build_id.end(), 16 - std::min(build_id.size(), size_t{16}), '\0'); + uuid->InitializeFromBytes(build_id.data()); + + // TODO(scottmg): https://crashpad.chromium.org/bug/229. These are + // endian-swapped to match FileID::ConvertIdentifierToUUIDString() in + // Breakpad. This is necessary as this identifier is used for symbol lookup. + uuid->data_1 = htobe32(uuid->data_1); + uuid->data_2 = htobe16(uuid->data_2); + uuid->data_3 = htobe16(uuid->data_3); +} + +std::string ModuleSnapshotElf::DebugFileName() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return base::FilePath(Name()).BaseName().value(); +} + +std::vector ModuleSnapshotElf::BuildID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + std::unique_ptr notes = + elf_reader_->NotesWithNameAndType(ELF_NOTE_GNU, NT_GNU_BUILD_ID, 64); + std::string desc; + VMAddress desc_addr; + notes->NextNote(nullptr, nullptr, &desc, &desc_addr); + + std::vector build_id; + build_id.reserve(desc.size()); + std::copy(desc.begin(), desc.end(), std::back_inserter(build_id)); + return build_id; +} + +std::vector ModuleSnapshotElf::AnnotationsVector() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector(); +} + +std::map ModuleSnapshotElf::AnnotationsSimpleMap() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::map annotations; + if (crashpad_info_ && crashpad_info_->SimpleAnnotations()) { + ImageAnnotationReader reader(process_memory_range_); + reader.SimpleMap(crashpad_info_->SimpleAnnotations(), &annotations); + } + return annotations; +} + +std::vector ModuleSnapshotElf::AnnotationObjects() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector annotations; + if (crashpad_info_ && crashpad_info_->AnnotationsList()) { + ImageAnnotationReader reader(process_memory_range_); + reader.AnnotationsList(crashpad_info_->AnnotationsList(), &annotations); + } + return annotations; +} + +std::set> ModuleSnapshotElf::ExtraMemoryRanges() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::set>(); +} + +std::vector +ModuleSnapshotElf::CustomMinidumpStreams() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + streams_.clear(); + + std::vector result; + if (!crashpad_info_) + return result; + + for (uint64_t cur = crashpad_info_->UserDataMinidumpStreamHead(); cur;) { + internal::UserDataMinidumpStreamListEntry list_entry; + if (!process_memory_->Read(cur, sizeof(list_entry), &list_entry)) { + LOG(WARNING) << "could not read user data stream entry from " << name_; + return result; + } + + if (list_entry.size != 0) { + auto memory = std::make_unique(); + memory->Initialize( + process_memory_, list_entry.base_address, list_entry.size); + streams_.push_back(std::make_unique( + list_entry.stream_type, memory.release())); + result.push_back(streams_.back().get()); + } + + cur = list_entry.next; + } + + return result; +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/elf/module_snapshot_elf.h b/shared/sentry/external/crashpad/snapshot/elf/module_snapshot_elf.h new file mode 100644 index 000000000..86741b3e1 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/elf/module_snapshot_elf.h @@ -0,0 +1,109 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_ELF_MODULE_SNAPSHOT_ELF_H_ +#define CRASHPAD_SNAPSHOT_ELF_MODULE_SNAPSHOT_ELF_H_ + +#include +#include + +#include +#include +#include + +#include "client/crashpad_info.h" +#include "snapshot/crashpad_info_client_options.h" +#include "snapshot/crashpad_types/crashpad_info_reader.h" +#include "snapshot/elf/elf_image_reader.h" +#include "snapshot/module_snapshot.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +namespace internal { + +//! \brief A ModuleSnapshot of a code module (binary image) loaded into a +//! running (or crashed) process on a system that uses ELF modules. +class ModuleSnapshotElf final : public ModuleSnapshot { + public: + //! \param[in] name The pathname used to load the module from disk. + //! \param[in] elf_reader An image reader for the module. + //! \param[in] type The module's type. + //! \param[in] process_memory_range A memory reader giving protected access + //! to the target process. + //! \param[in] process_memory A memory reader for the target process which can + //! be used to initialize a MemorySnapshot. + ModuleSnapshotElf(const std::string& name, + ElfImageReader* elf_reader, + ModuleSnapshot::ModuleType type, + ProcessMemoryRange* process_memory_range, + const ProcessMemory* process_memory); + + ModuleSnapshotElf(const ModuleSnapshotElf&) = delete; + ModuleSnapshotElf& operator=(const ModuleSnapshotElf&) = delete; + + ~ModuleSnapshotElf() override; + + //! \brief Initializes the object. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(); + + //! \brief Returns options from the module’s CrashpadInfo structure. + //! + //! \param[out] options Options set in the module’s CrashpadInfo structure. + //! \return `true` if there were options returned. Otherwise `false`. + bool GetCrashpadOptions(CrashpadInfoClientOptions* options); + + // ModuleSnapshot: + + std::string Name() const override; + uint64_t Address() const override; + uint64_t Size() const override; + time_t Timestamp() const override; + void FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const override; + void SourceVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const override; + ModuleType GetModuleType() const override; + void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override; + std::string DebugFileName() const override; + std::vector BuildID() const override; + std::vector AnnotationsVector() const override; + std::map AnnotationsSimpleMap() const override; + std::vector AnnotationObjects() const override; + std::set> ExtraMemoryRanges() const override; + std::vector CustomMinidumpStreams() const override; + + private: + std::string name_; + ElfImageReader* elf_reader_; + ProcessMemoryRange* process_memory_range_; + const ProcessMemory* process_memory_; + std::unique_ptr crashpad_info_; + ModuleType type_; + InitializationStateDcheck initialized_; + // Too const-y: https://crashpad.chromium.org/bug/9. + mutable std::vector> streams_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_ELF_MODULE_SNAPSHOT_ELF_H_ diff --git a/shared/sentry/external/crashpad/snapshot/elf/test_exported_symbols.sym b/shared/sentry/external/crashpad/snapshot/elf/test_exported_symbols.sym new file mode 100644 index 000000000..e0785c6e9 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/elf/test_exported_symbols.sym @@ -0,0 +1,18 @@ +# Copyright 2017 The Crashpad Authors. All rights reserved. +# +# 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. + +# This symbol is used by elf_image_reader_test.cc. +{ + ElfImageReaderTestExportedSymbol; +}; diff --git a/shared/sentry/external/crashpad/snapshot/exception_snapshot.h b/shared/sentry/external/crashpad/snapshot/exception_snapshot.h new file mode 100644 index 000000000..11a941531 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/exception_snapshot.h @@ -0,0 +1,121 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_EXCEPTION_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_EXCEPTION_SNAPSHOT_H_ + +#include + +#include + +#include "snapshot/memory_snapshot.h" + +namespace crashpad { + +struct CPUContext; + +//! \brief An abstract interface to a snapshot representing an exception that a +//! snapshot process sustained and triggered the snapshot being taken. +class ExceptionSnapshot { + public: + virtual ~ExceptionSnapshot() {} + + //! \brief Returns a CPUContext object corresponding to the exception thread’s + //! CPU context at the time of the exception. + //! + //! The caller does not take ownership of this object, it is scoped to the + //! lifetime of the ThreadSnapshot object that it was obtained from. + virtual const CPUContext* Context() const = 0; + + //! \brief Returns the thread identifier of the thread that triggered the + //! exception. + //! + //! This value can be compared to ThreadSnapshot::ThreadID() to associate an + //! ExceptionSnapshot object with the ThreadSnapshot that contains a snapshot + //! of the thread that triggered the exception. + virtual uint64_t ThreadID() const = 0; + + //! \brief Returns the top-level exception code identifying the exception. + //! + //! This is an operating system-specific value. + //! + //! For macOS, this will be an \ref EXC_x "EXC_*" exception type, such as + //! `EXC_BAD_ACCESS`. `EXC_CRASH` will not appear here for exceptions + //! processed as `EXC_CRASH` when generated from another preceding exception: + //! the original exception code will appear instead. The exception type as it + //! was received will appear at index 0 of Codes(). + //! + //! For Windows, this will be an `EXCEPTION_*` exception type, such as + //! `EXCEPTION_ACCESS_VIOLATION`. + virtual uint32_t Exception() const = 0; + + //! \brief Returns the second-level exception code identifying the exception. + //! + //! This is an operating system-specific value. + //! + //! For macOS, this will be the value of the exception code at index 0 as + //! received by a Mach exception handler, except: + //! * For `EXC_CRASH` exceptions generated from another preceding exception, + //! the original exception code will appear here, not the code as received + //! by the Mach exception handler. + //! * For `EXC_RESOURCE` and `EXC_GUARD` exceptions, the high 32 bits of the + //! exception code at index 0 will appear here. + //! + //! In all cases on macOS, the full exception code at index 0 as it was + //! received will appear at index 1 of Codes(). + //! + //! On Windows, this will either be `0` if the exception is continuable, or + //! `EXCEPTION_NONCONTINUABLE` to indicate a noncontinuable exception. + virtual uint32_t ExceptionInfo() const = 0; + + //! \brief Returns the address that triggered the exception. + //! + //! This may be the address that caused a fault on data access, or it may be + //! the instruction pointer that contained an offending instruction. For + //! exceptions where this value cannot be determined, it will be `0`. + //! + //! For macOS, this will be the value of the exception code at index 1 as + //! received by a Mach exception handler. + virtual uint64_t ExceptionAddress() const = 0; + + //! \brief Returns a series of operating system-specific exception codes. + //! + //! The precise interpretation of these codes is specific to the snapshot + //! operating system. These codes may provide a duplicate of information + //! available elsewhere, they may extend information available elsewhere, or + //! they may not be present at all. In this case, an empty vector will be + //! returned. + //! + //! For macOS, this will be a vector containing the original exception type + //! and the values of `code[0]` and `code[1]` as received by a Mach exception + //! handler. + //! + //! For Windows, these are additional arguments (if any) as provided to + //! `RaiseException()`. See the documentation for `ExceptionInformation` in + //! `EXCEPTION_RECORD`. + virtual const std::vector& Codes() const = 0; + + //! \brief Returns a vector of additional memory blocks that should be + //! included in a minidump. + //! + //! \return A vector of MemorySnapshot objects that will be included in the + //! crash dump. The caller does not take ownership of these objects, they + //! are scoped to the lifetime of the ThreadSnapshot object that they + //! were obtained from. + virtual std::vector ExtraMemory() const = 0; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_EXCEPTION_SNAPSHOT_H_ diff --git a/shared/sentry/external/crashpad/snapshot/fuchsia/cpu_context_fuchsia.cc b/shared/sentry/external/crashpad/snapshot/fuchsia/cpu_context_fuchsia.cc new file mode 100644 index 000000000..226bfc672 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/fuchsia/cpu_context_fuchsia.cc @@ -0,0 +1,95 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/fuchsia/cpu_context_fuchsia.h" + +#include + +namespace crashpad { +namespace internal { + +#if defined(ARCH_CPU_X86_64) + +void InitializeCPUContextX86_64_NoFloatingPoint( + const zx_thread_state_general_regs_t& thread_context, + CPUContextX86_64* context) { + memset(context, 0, sizeof(*context)); + context->rax = thread_context.rax; + context->rbx = thread_context.rbx; + context->rcx = thread_context.rcx; + context->rdx = thread_context.rdx; + context->rdi = thread_context.rdi; + context->rsi = thread_context.rsi; + context->rbp = thread_context.rbp; + context->rsp = thread_context.rsp; + context->r8 = thread_context.r8; + context->r9 = thread_context.r9; + context->r10 = thread_context.r10; + context->r11 = thread_context.r11; + context->r12 = thread_context.r12; + context->r13 = thread_context.r13; + context->r14 = thread_context.r14; + context->r15 = thread_context.r15; + context->rip = thread_context.rip; + context->rflags = thread_context.rflags; +} + +#elif defined(ARCH_CPU_ARM64) + +void InitializeCPUContextARM64( + const zx_thread_state_general_regs_t& thread_context, + const zx_thread_state_vector_regs_t& vector_context, + CPUContextARM64* context) { + memset(context, 0, sizeof(*context)); + + // Fuchsia stores the link register (x30) on its own while Crashpad stores it + // with the other general purpose x0-x28 and x29 frame pointer registers. So + // we expect the size and number of elements to be off by one unit. + static_assert(sizeof(context->regs) - sizeof(context->regs[30]) == + sizeof(thread_context.r), + "registers size mismatch"); + static_assert((sizeof(context->regs) - sizeof(context->regs[30])) / + sizeof(context->regs[0]) == + sizeof(thread_context.r) / sizeof(thread_context.r[0]), + "registers number of elements mismatch"); + memcpy(&context->regs, &thread_context.r, sizeof(thread_context.r)); + context->regs[30] = thread_context.lr; + context->sp = thread_context.sp; + context->pc = thread_context.pc; + + // Only the NZCV flags (bits 31 to 28 respectively) of the cpsr register are + // readable and writable by userland on ARM64. + constexpr uint32_t kNZCV = 0xf0000000; + // Fuchsia uses the old "cspr" terminology from armv7 while Crashpad uses the + // new "spsr" terminology for armv8. + context->spsr = thread_context.cpsr & kNZCV; + if (thread_context.cpsr > + std::numeric_limitsspsr)>::max()) { + LOG(WARNING) << "cpsr truncation: we only expect the first 32 bits to be " + "set in the cpsr"; + } + context->spsr = + static_castspsr)>(thread_context.cpsr) & kNZCV; + + context->fpcr = vector_context.fpcr; + context->fpsr = vector_context.fpsr; + static_assert(sizeof(context->fpsimd) == sizeof(vector_context.v), + "registers size mismatch"); + memcpy(&context->fpsimd, &vector_context.v, sizeof(vector_context.v)); +} + +#endif // ARCH_CPU_X86_64 + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/fuchsia/cpu_context_fuchsia.h b/shared/sentry/external/crashpad/snapshot/fuchsia/cpu_context_fuchsia.h new file mode 100644 index 000000000..9227bfc82 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/fuchsia/cpu_context_fuchsia.h @@ -0,0 +1,62 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_FUCHSIA_CPU_CONTEXT_FUCHSIA_H_ +#define CRASHPAD_SNAPSHOT_FUCHSIA_CPU_CONTEXT_FUCHSIA_H_ + +#include + +#include "build/build_config.h" +#include "snapshot/cpu_context.h" +#include "snapshot/fuchsia/process_reader_fuchsia.h" + +namespace crashpad { +namespace internal { + +#if defined(ARCH_CPU_X86_64) || DOXYGEN + +//! \brief Initializes a CPUContextX86_64 structure from native context +//! structures on Fuchsia. +//! +//! Floating point registers are currently initialized to zero. +//! Segment registers are currently initialized to zero. +//! +//! \param[in] thread_context The native thread context. +//! \param[out] context The CPUContextX86_64 structure to initialize. +void InitializeCPUContextX86_64_NoFloatingPoint( + const zx_thread_state_general_regs_t& thread_context, + CPUContextX86_64* context); + +#endif // ARCH_CPU_X86_64 || DOXYGEN + +#if defined(ARCH_CPU_ARM64) || DOXYGEN + +//! \brief Initializes a CPUContextARM64 structure from native context +//! structures on Fuchsia. +//! +//! \param[in] thread_context The native thread context. +//! \param[in] vector_context The native vector context that also contains the +//! floating point registers. +//! \param[out] context The CPUContextARM64 structure to initialize. +void InitializeCPUContextARM64( + const zx_thread_state_general_regs_t& thread_context, + const zx_thread_state_vector_regs_t& vector_context, + CPUContextARM64* context); + +#endif // ARCH_CPU_ARM64 || DOXYGEN + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_FUCHSIA_CPU_CONTEXT_FUCHSIA_H_ diff --git a/shared/sentry/external/crashpad/snapshot/fuchsia/exception_snapshot_fuchsia.cc b/shared/sentry/external/crashpad/snapshot/fuchsia/exception_snapshot_fuchsia.cc new file mode 100644 index 000000000..26077696d --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/fuchsia/exception_snapshot_fuchsia.cc @@ -0,0 +1,141 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/fuchsia/exception_snapshot_fuchsia.h" + +#include "base/numerics/safe_conversions.h" +#include "snapshot/fuchsia/cpu_context_fuchsia.h" +#include "snapshot/fuchsia/process_reader_fuchsia.h" + +namespace crashpad { +namespace internal { + +ExceptionSnapshotFuchsia::ExceptionSnapshotFuchsia() = default; +ExceptionSnapshotFuchsia::~ExceptionSnapshotFuchsia() = default; + +bool ExceptionSnapshotFuchsia::Initialize( + ProcessReaderFuchsia* process_reader, + zx_koid_t thread_id, + const zx_exception_report_t& exception_report) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + exception_ = exception_report.header.type; + thread_id_ = thread_id; + + // TODO(scottmg): Not sure whether these values for exception_info_ are + // helpful or correct. Other values in the structures are stored below into + // Codes() in case they are useful. +#if defined(ARCH_CPU_X86_64) + DCHECK(base::IsValueInRangeForNumericType( + exception_report.context.arch.u.x86_64.err_code)); + exception_info_ = exception_report.context.arch.u.x86_64.err_code; +#elif defined(ARCH_CPU_ARM64) + exception_info_ = exception_report.context.arch.u.arm_64.esr; +#endif + + codes_.push_back(exception_); + codes_.push_back(exception_info_); + +#if defined(ARCH_CPU_X86_64) + codes_.push_back(exception_report.context.arch.u.x86_64.vector); + codes_.push_back(exception_report.context.arch.u.x86_64.cr2); +#elif defined(ARCH_CPU_ARM64) + codes_.push_back(exception_report.context.arch.u.arm_64.far); +#endif + + const auto threads = process_reader->Threads(); + const auto& t = + std::find_if(threads.begin(), + threads.end(), + [thread_id](const ProcessReaderFuchsia::Thread& thread) { + return thread.id == thread_id; + }); + if (t == threads.end()) { + // If no threads have been read, then context_ can't be initalized, and the + // exception snapshot can't be considered initialized_. + return false; + } + +#if defined(ARCH_CPU_X86_64) + context_.architecture = kCPUArchitectureX86_64; + context_.x86_64 = &context_arch_; + // TODO(fxbug.dev/5496): Add float context once saved in |t|. + InitializeCPUContextX86_64_NoFloatingPoint(t->general_registers, + context_.x86_64); +#elif defined(ARCH_CPU_ARM64) + context_.architecture = kCPUArchitectureARM64; + context_.arm64 = &context_arch_; + InitializeCPUContextARM64( + t->general_registers, t->vector_registers, context_.arm64); +#else +#error Port. +#endif + + if (context_.InstructionPointer() != 0 && + (exception_ == ZX_EXCP_UNDEFINED_INSTRUCTION || + exception_ == ZX_EXCP_SW_BREAKPOINT || + exception_ == ZX_EXCP_HW_BREAKPOINT)) { + exception_address_ = context_.InstructionPointer(); + } else { +#if defined(ARCH_CPU_X86_64) + exception_address_ = exception_report.context.arch.u.x86_64.cr2; +#elif defined(ARCH_CPU_ARM64) + exception_address_ = exception_report.context.arch.u.arm_64.far; +#else +#error Port. +#endif + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +const CPUContext* ExceptionSnapshotFuchsia::Context() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &context_; +} + +uint64_t ExceptionSnapshotFuchsia::ThreadID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_id_; +} + +uint32_t ExceptionSnapshotFuchsia::Exception() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_; +} + +uint32_t ExceptionSnapshotFuchsia::ExceptionInfo() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_info_; +} + +uint64_t ExceptionSnapshotFuchsia::ExceptionAddress() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_address_; +} + +const std::vector& ExceptionSnapshotFuchsia::Codes() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return codes_; +} + +std::vector ExceptionSnapshotFuchsia::ExtraMemory() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector(); +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/fuchsia/exception_snapshot_fuchsia.h b/shared/sentry/external/crashpad/snapshot/fuchsia/exception_snapshot_fuchsia.h new file mode 100644 index 000000000..1b0a4e239 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/fuchsia/exception_snapshot_fuchsia.h @@ -0,0 +1,85 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_FUCHSIA_EXCEPTION_SNAPSHOT_FUCHSIA_H_ +#define CRASHPAD_SNAPSHOT_FUCHSIA_EXCEPTION_SNAPSHOT_FUCHSIA_H_ + +#include +#include +#include + +#include "build/build_config.h" +#include "snapshot/cpu_context.h" +#include "snapshot/exception_snapshot.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +class ProcessReaderFuchsia; + +namespace internal { + +//! \brief An ExceptionSnapshot of an exception sustained by a process on a +//! Fuchsia system. +class ExceptionSnapshotFuchsia final : public ExceptionSnapshot { + public: + ExceptionSnapshotFuchsia(); + + ExceptionSnapshotFuchsia(const ExceptionSnapshotFuchsia&) = delete; + ExceptionSnapshotFuchsia& operator=(const ExceptionSnapshotFuchsia&) = delete; + + ~ExceptionSnapshotFuchsia() override; + + //! \brief Initializes the object. + //! + //! \param[in] process_reader A ProcessReaderFuchsia for the process that + //! sustained the exception. + //! \param[in] thread_id The koid of the thread that sustained the exception. + //! \param[in] exception_report The `zx_exception_report_t` retrieved from the + //! thread in the exception state, corresponding to \a thread_id. + //! + //! \return `true` if the exception data was initialized, `false` otherwise + //! with an error logged. + bool Initialize(ProcessReaderFuchsia* process_reader, + zx_koid_t thread_id, + const zx_exception_report_t& exception_report); + + // ExceptionSnapshot: + const CPUContext* Context() const override; + uint64_t ThreadID() const override; + uint32_t Exception() const override; + uint32_t ExceptionInfo() const override; + uint64_t ExceptionAddress() const override; + const std::vector& Codes() const override; + std::vector ExtraMemory() const override; + + private: +#if defined(ARCH_CPU_X86_64) + CPUContextX86_64 context_arch_; +#elif defined(ARCH_CPU_ARM64) + CPUContextARM64 context_arch_; +#endif + CPUContext context_; + std::vector codes_; + zx_koid_t thread_id_; + zx_vaddr_t exception_address_; + uint32_t exception_; + uint32_t exception_info_; + InitializationStateDcheck initialized_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_FUCHSIA_EXCEPTION_SNAPSHOT_FUCHSIA_H_ diff --git a/shared/sentry/external/crashpad/snapshot/fuchsia/memory_map_fuchsia.cc b/shared/sentry/external/crashpad/snapshot/fuchsia/memory_map_fuchsia.cc new file mode 100644 index 000000000..158679296 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/fuchsia/memory_map_fuchsia.cc @@ -0,0 +1,85 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/fuchsia/memory_map_fuchsia.h" + +#include "base/fuchsia/fuchsia_logging.h" +#include "util/numeric/checked_range.h" + +namespace crashpad { + +MemoryMapFuchsia::MemoryMapFuchsia() = default; + +MemoryMapFuchsia::~MemoryMapFuchsia() = default; + +bool MemoryMapFuchsia::Initialize(const zx::process& process) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + // There's no way to know what an appropriate buffer size is before starting. + // Start at a size that should be more than enough for any reasonable process. + map_entries_.resize(4096); + + // Retrieving the maps is racy with new mappings being created, so retry this + // loop up to |tries| times until the number of actual mappings retrieved + // matches those available. + int tries = 5; + for (;;) { + size_t actual; + size_t available; + zx_status_t status = + process.get_info(ZX_INFO_PROCESS_MAPS, + &map_entries_[0], + map_entries_.size() * sizeof(map_entries_[0]), + &actual, + &available); + if (status != ZX_OK) { + ZX_LOG(ERROR, status) << "zx_object_get_info ZX_INFO_PROCESS_MAPS"; + map_entries_.clear(); + return false; + } + if (actual < available && tries-- > 0) { + // Make the buffer slightly larger than |available| to attempt to account + // for the race between here and the next retrieval. + map_entries_.resize(available + 20); + continue; + } + + map_entries_.resize(actual); + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; + } +} + +bool MemoryMapFuchsia::FindMappingForAddress(zx_vaddr_t address, + zx_info_maps_t* map) const { + bool found = false; + zx_info_maps_t result = {}; + for (const auto& m : map_entries_) { + CheckedRange range(m.base, m.size); + if (range.ContainsValue(address)) { + if (!found || m.depth > result.depth) { + result = m; + found = true; + } + } + } + + if (found) { + *map = result; + } + return found; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/fuchsia/memory_map_fuchsia.h b/shared/sentry/external/crashpad/snapshot/fuchsia/memory_map_fuchsia.h new file mode 100644 index 000000000..1c2736a71 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/fuchsia/memory_map_fuchsia.h @@ -0,0 +1,64 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_FUCHSIA_MEMORY_MAP_FUCHSIA_H_ +#define CRASHPAD_SNAPSHOT_FUCHSIA_MEMORY_MAP_FUCHSIA_H_ + +#include +#include + +#include + +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +//! \brief A list of mappings in the address space of a Fuchsia process. +class MemoryMapFuchsia { + public: + MemoryMapFuchsia(); + + MemoryMapFuchsia(const MemoryMapFuchsia&) = delete; + MemoryMapFuchsia& operator=(const MemoryMapFuchsia&) = delete; + + ~MemoryMapFuchsia(); + + //! \brief Initializes this object with information about the mapped memory + //! regions in the given process. + //! + //! \return `true` on success, or `false`, with an error logged. + bool Initialize(const zx::process& process); + + //! \brief Searches through the previously retrieved memory map for the given + //! address. If found, returns the deepest `zx_info_maps_t` mapping that + //! contains \a address. + //! + //! \param[in] address The address to locate. + //! \param[out] map The `zx_info_maps_t` data corresponding to the address. + //! \return `true` if a mapping for \a address was found, in which case \a map + //! will be filled out, otherwise `false` and \a map will be unchanged. + bool FindMappingForAddress(zx_vaddr_t address, zx_info_maps_t* map) const; + + //! \brief Get a vector of `zx_info_maps_t` representing the memory map for + //! this process. + const std::vector& Entries() const { return map_entries_; } + + private: + std::vector map_entries_; + InitializationStateDcheck initialized_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_FUCHSIA_MEMORY_MAP_FUCHSIA_H_ diff --git a/shared/sentry/external/crashpad/snapshot/fuchsia/memory_map_region_snapshot_fuchsia.cc b/shared/sentry/external/crashpad/snapshot/fuchsia/memory_map_region_snapshot_fuchsia.cc new file mode 100644 index 000000000..8d7f9b867 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/fuchsia/memory_map_region_snapshot_fuchsia.cc @@ -0,0 +1,85 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/fuchsia/memory_map_region_snapshot_fuchsia.h" + +#include + +#include "base/check_op.h" + +namespace crashpad { +namespace internal { + +namespace { + +// Maps from bitwise OR of Zircon's flags to enumerated Windows version. +uint32_t MmuFlagsToProtectFlags(zx_vm_option_t flags) { + // These bits are currently the lowest 3 of zx_vm_option_t. They're used to + // index into a mapping array, so make sure that we notice if they change + // values. + static_assert( + ZX_VM_PERM_READ == 1 && ZX_VM_PERM_WRITE == 2 && ZX_VM_PERM_EXECUTE == 4, + "table below will need fixing"); + + // The entries set to zero don't have good corresponding Windows minidump + // names. They also aren't currently supported by the mapping syscall on + // Zircon, so DCHECK that they don't happen in practice. EXECUTE-only also + // cannot currently happen, but as that has a good mapping to the Windows + // value, leave it in place in case it's supported by the syscall in the + // future. + static constexpr uint32_t mapping[] = { + /* --- */ PAGE_NOACCESS, + /* --r */ PAGE_READONLY, + /* -w- */ 0, + /* -wr */ PAGE_READWRITE, + /* x-- */ PAGE_EXECUTE, + /* x-r */ PAGE_EXECUTE_READ, + /* xw- */ 0, + /* xwr */ PAGE_EXECUTE_READWRITE, + }; + + const uint32_t index = + flags & (ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_PERM_EXECUTE); + DCHECK_LT(index, std::size(mapping)); + + const uint32_t protect_flags = mapping[index]; + DCHECK_NE(protect_flags, 0u); + return protect_flags; +} + +} // namespace + +MemoryMapRegionSnapshotFuchsia::MemoryMapRegionSnapshotFuchsia( + const zx_info_maps_t& info_map) + : memory_info_() { + DCHECK_EQ(info_map.type, ZX_INFO_MAPS_TYPE_MAPPING); + + memory_info_.BaseAddress = info_map.base; + memory_info_.AllocationBase = info_map.base; + memory_info_.RegionSize = info_map.size; + memory_info_.State = MEM_COMMIT; + memory_info_.Protect = memory_info_.AllocationProtect = + MmuFlagsToProtectFlags(info_map.u.mapping.mmu_flags); + memory_info_.Type = MEM_MAPPED; +} + +MemoryMapRegionSnapshotFuchsia::~MemoryMapRegionSnapshotFuchsia() {} + +const MINIDUMP_MEMORY_INFO& +MemoryMapRegionSnapshotFuchsia::AsMinidumpMemoryInfo() const { + return memory_info_; +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/fuchsia/memory_map_region_snapshot_fuchsia.h b/shared/sentry/external/crashpad/snapshot/fuchsia/memory_map_region_snapshot_fuchsia.h new file mode 100644 index 000000000..37f7a1a77 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/fuchsia/memory_map_region_snapshot_fuchsia.h @@ -0,0 +1,39 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_FUCHSIA_MEMORY_MAP_REGION_SNAPSHOT_FUCHSIA_H_ +#define CRASHPAD_SNAPSHOT_FUCHSIA_MEMORY_MAP_REGION_SNAPSHOT_FUCHSIA_H_ + +#include "snapshot/memory_map_region_snapshot.h" + +#include + +namespace crashpad { +namespace internal { + +class MemoryMapRegionSnapshotFuchsia : public MemoryMapRegionSnapshot { + public: + explicit MemoryMapRegionSnapshotFuchsia(const zx_info_maps_t& info_map); + ~MemoryMapRegionSnapshotFuchsia() override; + + virtual const MINIDUMP_MEMORY_INFO& AsMinidumpMemoryInfo() const override; + + private: + MINIDUMP_MEMORY_INFO memory_info_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_FUCHSIA_MEMORY_MAP_REGION_SNAPSHOT_FUCHSIA_H_ diff --git a/shared/sentry/external/crashpad/snapshot/fuchsia/process_reader_fuchsia.cc b/shared/sentry/external/crashpad/snapshot/fuchsia/process_reader_fuchsia.cc new file mode 100644 index 000000000..1c037d74b --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/fuchsia/process_reader_fuchsia.cc @@ -0,0 +1,375 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/fuchsia/process_reader_fuchsia.h" + +#include +#include +#include + +#include "base/fuchsia/fuchsia_logging.h" +#include "base/logging.h" +#include "util/fuchsia/koid_utilities.h" + +namespace crashpad { + +namespace { + +// Based on the thread's SP and the process's memory map, attempts to figure out +// the stack regions for the thread. Fuchsia's C ABI specifies +// https://fuchsia.googlesource.com/zircon/+/master/docs/safestack.md so the +// callstack and locals-that-have-their-address-taken are in two different +// stacks. +void GetStackRegions( + const zx_thread_state_general_regs_t& regs, + const MemoryMapFuchsia& memory_map, + std::vector>* stack_regions) { + stack_regions->clear(); + + uint64_t sp; +#if defined(ARCH_CPU_X86_64) + sp = regs.rsp; +#elif defined(ARCH_CPU_ARM64) + sp = regs.sp; +#else +#error Port +#endif + + // TODO(fxbug.dev/74897): make this work for stack overflows, e.g., by looking + // up using the initial stack pointer (sp) when the thread was created. Right + // now, it gets the stack by getting the mapping that contains the current sp. + // But in the case of stack overflows, the current sp is by definition outside + // of the stack so the mapping returned is not the stack and fails the type + // check, at least on arm64. + zx_info_maps_t range_with_sp; + if (!memory_map.FindMappingForAddress(sp, &range_with_sp)) { + LOG(ERROR) << "stack pointer not found in mapping"; + return; + } + + if (range_with_sp.type != ZX_INFO_MAPS_TYPE_MAPPING) { + LOG(ERROR) << "stack range has unexpected type " << range_with_sp.type + << ", stack overflow? Aborting"; + return; + } + + if (range_with_sp.u.mapping.mmu_flags & ZX_VM_PERM_EXECUTE) { + LOG(ERROR) + << "stack range is unexpectedly marked executable, continuing anyway"; + } + + // The stack covers [range_with_sp.base, range_with_sp.base + + // range_with_sp.size). The stack pointer (sp) can be anywhere in that range. + // It starts at the end of the range (range_with_sp.base + range_with_sp.size) + // and goes downwards until range_with_sp.base. Capture the part of the stack + // that is currently used: [sp, range_with_sp.base + range_with_sp.size). + + // Capture up to kExtraCaptureSize additional bytes of stack, but only if + // present in the region that was already found. + constexpr uint64_t kExtraCaptureSize = 128; + const uint64_t start_address = + std::max(sp >= kExtraCaptureSize ? sp - kExtraCaptureSize : sp, + range_with_sp.base); + const size_t region_size = + range_with_sp.size - (start_address - range_with_sp.base); + + // Because most Fuchsia processes use safestack, it is very unlikely that a + // stack this large would be valid. Even if it were, avoid creating + // unreasonably large dumps by artificially limiting the captured amount. + constexpr uint64_t kMaxStackCapture = 1048576u; + LOG_IF(ERROR, region_size > kMaxStackCapture) + << "clamping unexpectedly large stack capture of " << region_size; + const size_t clamped_region_size = std::min(region_size, kMaxStackCapture); + stack_regions->push_back( + CheckedRange(start_address, clamped_region_size)); + + // TODO(scottmg): https://crashpad.chromium.org/bug/196, once the retrievable + // registers include FS and similar for ARM, retrieve the region for the + // unsafe part of the stack too. +} + +} // namespace + +ProcessReaderFuchsia::Module::Module() = default; + +ProcessReaderFuchsia::Module::~Module() = default; + +ProcessReaderFuchsia::Thread::Thread() = default; + +ProcessReaderFuchsia::Thread::~Thread() = default; + +ProcessReaderFuchsia::ProcessReaderFuchsia() = default; + +ProcessReaderFuchsia::~ProcessReaderFuchsia() = default; + +bool ProcessReaderFuchsia::Initialize(const zx::process& process) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + process_ = zx::unowned_process(process); + + process_memory_.reset(new ProcessMemoryFuchsia()); + process_memory_->Initialize(*process_); + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +const std::vector& +ProcessReaderFuchsia::Modules() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (!initialized_modules_) { + InitializeModules(); + } + + return modules_; +} + +const std::vector& +ProcessReaderFuchsia::Threads() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (!initialized_threads_) { + InitializeThreads(); + } + + return threads_; +} + +const MemoryMapFuchsia* ProcessReaderFuchsia::MemoryMap() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (!initialized_memory_map_) { + InitializeMemoryMap(); + } + + return memory_map_.get(); +} + +void ProcessReaderFuchsia::InitializeModules() { + DCHECK(!initialized_modules_); + DCHECK(modules_.empty()); + + initialized_modules_ = true; + + // TODO(scottmg): does some of this, but doesn't + // expose any of the data that's necessary to fill out a Module after it + // retrieves (some of) the data into internal structures. It may be worth + // trying to refactor/upstream some of this into Fuchsia. + + // Starting from the ld.so's _dl_debug_addr, read the link_map structure and + // walk the list to fill out modules_. + + uintptr_t debug_address; + zx_status_t status = process_->get_property( + ZX_PROP_PROCESS_DEBUG_ADDR, &debug_address, sizeof(debug_address)); + if (status != ZX_OK || debug_address == 0) { + LOG(ERROR) << "zx_object_get_property ZX_PROP_PROCESS_DEBUG_ADDR"; + return; + } + + constexpr auto k_r_debug_map_offset = offsetof(r_debug, r_map); + uintptr_t map; + if (!process_memory_->Read( + debug_address + k_r_debug_map_offset, sizeof(map), &map)) { + LOG(ERROR) << "read link_map"; + return; + } + + int i = 0; + constexpr int kMaxDso = 1000; // Stop after an unreasonably large number. + while (map != 0) { + if (++i >= kMaxDso) { + LOG(ERROR) << "possibly circular dso list, terminating"; + return; + } + + constexpr auto k_link_map_addr_offset = offsetof(link_map, l_addr); + zx_vaddr_t base; + if (!process_memory_->Read( + map + k_link_map_addr_offset, sizeof(base), &base)) { + LOG(ERROR) << "Read base"; + // Could theoretically continue here, but realistically if any part of + // link_map fails to read, things are looking bad, so just abort. + break; + } + + constexpr auto k_link_map_next_offset = offsetof(link_map, l_next); + zx_vaddr_t next; + if (!process_memory_->Read( + map + k_link_map_next_offset, sizeof(next), &next)) { + LOG(ERROR) << "Read next"; + break; + } + + constexpr auto k_link_map_name_offset = offsetof(link_map, l_name); + zx_vaddr_t name_address; + if (!process_memory_->Read(map + k_link_map_name_offset, + sizeof(name_address), + &name_address)) { + LOG(ERROR) << "Read name address"; + break; + } + + std::string dsoname; + if (!process_memory_->ReadCString(name_address, &dsoname)) { + // In this case, it could be reasonable to continue on to the next module + // as this data isn't strictly in the link_map. + LOG(ERROR) << "ReadCString name"; + } + + // Debug symbols are indexed by module name x build-id on the crash server. + // The module name in the indexed Breakpad files is set at build time. So + // Crashpad needs to use the same module name at run time for symbol + // resolution to work properly. + // + // TODO(fuchsia/DX-1234): once Crashpad switches to elf-search, the + // following overwrites won't be necessary as only shared libraries will + // have a soname at runtime, just like at build time. + // + // * For shared libraries, the soname is used as module name at build time, + // which is the dsoname here except for libzircon.so (because it is + // injected by the kernel, its load name is "" and Crashpad needs to + // replace it for symbol resolution to work properly). + if (dsoname == "") { + dsoname = "libzircon.so"; + } + // * For executables and loadable modules, the dummy value "<_>" is used as + // module name at build time. This is because executable and loadable + // modules don't have a name on Fuchsia. So we need to use the same dummy + // value at build and run times. + // Most executables have an empty dsoname. Loadable modules (and some rare + // executables) have a non-empty dsoname starting with a specific prefix, + // which Crashpas can use to identify loadable modules and clear the + // dsoname for them. + static constexpr const char kLoadableModuleLoadNamePrefix[] = " reader(new ElfImageReader()); + + std::unique_ptr process_memory_range( + new ProcessMemoryRange()); + // TODO(scottmg): Could this be limited range? + if (process_memory_range->Initialize(process_memory_.get(), true)) { + process_memory_ranges_.push_back(std::move(process_memory_range)); + + if (reader->Initialize(*process_memory_ranges_.back(), base)) { + module.reader = reader.get(); + module_readers_.push_back(std::move(reader)); + modules_.push_back(module); + } + } + + map = next; + } +} + +void ProcessReaderFuchsia::InitializeThreads() { + DCHECK(!initialized_threads_); + DCHECK(threads_.empty()); + + initialized_threads_ = true; + + std::vector thread_koids = + GetChildKoids(*process_, ZX_INFO_PROCESS_THREADS); + std::vector thread_handles = + GetHandlesForThreadKoids(*process_, thread_koids); + DCHECK_EQ(thread_koids.size(), thread_handles.size()); + + for (size_t i = 0; i < thread_handles.size(); ++i) { + Thread thread; + thread.id = thread_koids[i]; + + if (thread_handles[i].is_valid()) { + char name[ZX_MAX_NAME_LEN] = {0}; + zx_status_t status = + thread_handles[i].get_property(ZX_PROP_NAME, &name, sizeof(name)); + if (status != ZX_OK) { + ZX_LOG(WARNING, status) << "zx_object_get_property ZX_PROP_NAME"; + } else { + thread.name.assign(name); + } + + zx_info_thread_t thread_info; + status = thread_handles[i].get_info( + ZX_INFO_THREAD, &thread_info, sizeof(thread_info), nullptr, nullptr); + if (status != ZX_OK) { + ZX_LOG(WARNING, status) << "zx_object_get_info ZX_INFO_THREAD"; + } else { + thread.state = thread_info.state; + } + + zx_thread_state_general_regs_t general_regs; + status = thread_handles[i].read_state( + ZX_THREAD_STATE_GENERAL_REGS, &general_regs, sizeof(general_regs)); + if (status != ZX_OK) { + ZX_LOG(WARNING, status) + << "zx_thread_read_state(ZX_THREAD_STATE_GENERAL_REGS)"; + } else { + thread.general_registers = general_regs; + + const MemoryMapFuchsia* memory_map = MemoryMap(); + if (memory_map) { + // Attempt to retrive stack regions if a memory map was retrieved. In + // particular, this may be null when operating on the current process + // where the memory map will not be able to be retrieved. + GetStackRegions(general_regs, *memory_map, &thread.stack_regions); + } + } + + zx_thread_state_vector_regs_t vector_regs; + status = thread_handles[i].read_state( + ZX_THREAD_STATE_VECTOR_REGS, &vector_regs, sizeof(vector_regs)); + if (status != ZX_OK) { + ZX_LOG(WARNING, status) + << "zx_thread_read_state(ZX_THREAD_STATE_VECTOR_REGS)"; + } else { + thread.vector_registers = vector_regs; + } + } + + threads_.push_back(thread); + } +} + +void ProcessReaderFuchsia::InitializeMemoryMap() { + DCHECK(!initialized_memory_map_); + + initialized_memory_map_ = true; + + memory_map_.reset(new MemoryMapFuchsia); + if (!memory_map_->Initialize(*process_)) { + memory_map_.reset(); + } +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/fuchsia/process_reader_fuchsia.h b/shared/sentry/external/crashpad/snapshot/fuchsia/process_reader_fuchsia.h new file mode 100644 index 000000000..b57f0b9ef --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/fuchsia/process_reader_fuchsia.h @@ -0,0 +1,148 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_FUCHSIA_PROCESS_READER_H_ +#define CRASHPAD_SNAPSHOT_FUCHSIA_PROCESS_READER_H_ + +#include +#include + +#include +#include + +#include "build/build_config.h" +#include "snapshot/elf/elf_image_reader.h" +#include "snapshot/fuchsia/memory_map_fuchsia.h" +#include "snapshot/module_snapshot.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/numeric/checked_range.h" +#include "util/process/process_memory_fuchsia.h" +#include "util/process/process_memory_range.h" + +namespace crashpad { + +//! \brief Accesses information about another process, identified by a Fuchsia +//! process. +class ProcessReaderFuchsia { + public: + //! \brief Contains information about a module loaded into a process. + struct Module { + Module(); + ~Module(); + + //! \brief The `ZX_PROP_NAME` of the module. + std::string name; + + //! \brief An image reader for the module. + //! + //! The lifetime of this ElfImageReader is scoped to the lifetime of the + //! ProcessReaderFuchsia that created it. + //! + //! This field may be `nullptr` if a reader could not be created for the + //! module. + ElfImageReader* reader; + + //! \brief The module's type. + ModuleSnapshot::ModuleType type = ModuleSnapshot::kModuleTypeUnknown; + }; + + //! \brief Contains information about a thread that belongs to a process. + struct Thread { + Thread(); + ~Thread(); + + //! \brief The kernel identifier for the thread. + zx_koid_t id = ZX_KOID_INVALID; + + //! \brief The state of the thread, the `ZX_THREAD_STATE_*` value or `-1` if + //! the value could not be retrieved. + uint32_t state = -1; + + //! \brief The `ZX_PROP_NAME` property of the thread. This may be empty. + std::string name; + + //! \brief The raw architecture-specific `zx_thread_state_general_regs_t` as + //! returned by `zx_thread_read_state()`. + zx_thread_state_general_regs_t general_registers = {}; + + //! \brief The raw architecture-specific `zx_thread_state_vector_regs_t` as + //! returned by `zx_thread_read_state()`. + zx_thread_state_vector_regs_t vector_registers = {}; + + //! \brief The regions representing the stack. The first entry in the vector + //! represents the callstack, and further entries optionally identify + //! other stack data when the thread uses a split stack representation. + std::vector> stack_regions; + }; + + ProcessReaderFuchsia(); + + ProcessReaderFuchsia(const ProcessReaderFuchsia&) = delete; + ProcessReaderFuchsia& operator=(const ProcessReaderFuchsia&) = delete; + + ~ProcessReaderFuchsia(); + + //! \brief Initializes this object. This method must be called before any + //! other. + //! + //! \param[in] process A process handle with permissions to read properties + //! and memory from the target process. + //! + //! \return `true` on success, indicating that this object will respond + //! validly to further method calls. `false` on failure. On failure, no + //! further method calls should be made. + bool Initialize(const zx::process& process); + + //! \return The modules loaded in the process. The first element (at index + //! `0`) corresponds to the main executable. + const std::vector& Modules(); + + //! \return The threads that are in the process. + const std::vector& Threads(); + + //! \brief Return a memory reader for the target process. + const ProcessMemory* Memory() const { return process_memory_.get(); } + + //! \brief Return a memory map for the target process. + const MemoryMapFuchsia* MemoryMap(); + + private: + //! Performs lazy initialization of the \a modules_ vector on behalf of + //! Modules(). + void InitializeModules(); + + //! Performs lazy initialization of the \a threads_ vector on behalf of + //! Threads(). + void InitializeThreads(); + + //! Performs lazy initialization of the \a memory_map_ on behalf of + //! MemoryMap(). + void InitializeMemoryMap(); + + std::vector modules_; + std::vector threads_; + std::vector> module_readers_; + std::vector> process_memory_ranges_; + std::unique_ptr process_memory_; + std::unique_ptr memory_map_; + zx::unowned_process process_; + bool initialized_modules_ = false; + bool initialized_threads_ = false; + bool initialized_memory_map_ = false; + InitializationStateDcheck initialized_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_FUCHSIA_PROCESS_READER_H_ diff --git a/shared/sentry/external/crashpad/snapshot/fuchsia/process_reader_fuchsia_test.cc b/shared/sentry/external/crashpad/snapshot/fuchsia/process_reader_fuchsia_test.cc new file mode 100644 index 000000000..362be5bd4 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/fuchsia/process_reader_fuchsia_test.cc @@ -0,0 +1,200 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/fuchsia/process_reader_fuchsia.h" + +#include +#include +#include +#include +#include + +#include + +#include "gtest/gtest.h" +#include "test/multiprocess_exec.h" +#include "test/test_paths.h" +#include "util/fuchsia/scoped_task_suspend.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(ProcessReaderFuchsia, SelfBasic) { + ProcessReaderFuchsia process_reader; + ASSERT_TRUE(process_reader.Initialize(*zx::process::self())); + + static constexpr char kTestMemory[] = "Some test memory"; + char buffer[std::size(kTestMemory)]; + ASSERT_TRUE(process_reader.Memory()->Read( + reinterpret_cast(kTestMemory), sizeof(kTestMemory), &buffer)); + EXPECT_STREQ(kTestMemory, buffer); + + const auto& modules = process_reader.Modules(); + // The process should have at least one module, the executable, and then some + // shared libraries, no loadable modules. + EXPECT_GT(modules.size(), 0u); + size_t num_executables = 0u; + size_t num_shared_libraries = 0u; + for (const auto& module : modules) { + EXPECT_FALSE(module.name.empty()); + EXPECT_NE(module.type, ModuleSnapshot::kModuleTypeUnknown); + + if (module.type == ModuleSnapshot::kModuleTypeExecutable) { + EXPECT_EQ(module.name, "<_>"); + num_executables++; + } else if (module.type == ModuleSnapshot::kModuleTypeSharedLibrary) { + EXPECT_NE(module.name, "<_>"); + num_shared_libraries++; + } + } + EXPECT_EQ(num_executables, 1u); + EXPECT_EQ(num_shared_libraries, modules.size() - num_executables); + + const auto& threads = process_reader.Threads(); + EXPECT_GT(threads.size(), 0u); + + zx_info_handle_basic_t info; + ASSERT_EQ(zx_object_get_info(zx_thread_self(), + ZX_INFO_HANDLE_BASIC, + &info, + sizeof(info), + nullptr, + nullptr), + ZX_OK); + EXPECT_EQ(threads[0].id, info.koid); + EXPECT_EQ(threads[0].state, ZX_THREAD_STATE_RUNNING); + EXPECT_EQ(threads[0].name, "initial-thread"); +} + +constexpr char kTestMemory[] = "Read me from another process"; + +CRASHPAD_CHILD_TEST_MAIN(ProcessReaderBasicChildTestMain) { + zx_vaddr_t addr = reinterpret_cast(kTestMemory); + CheckedWriteFile( + StdioFileHandle(StdioStream::kStandardOutput), &addr, sizeof(addr)); + CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput)); + return 0; +} + +class BasicChildTest : public MultiprocessExec { + public: + BasicChildTest() : MultiprocessExec() { + SetChildTestMainFunction("ProcessReaderBasicChildTestMain"); + } + + BasicChildTest(const BasicChildTest&) = delete; + BasicChildTest& operator=(const BasicChildTest&) = delete; + + ~BasicChildTest() {} + + private: + void MultiprocessParent() override { + ProcessReaderFuchsia process_reader; + ASSERT_TRUE(process_reader.Initialize(*ChildProcess())); + + zx_vaddr_t addr; + ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), &addr, sizeof(addr))); + + std::string read_string; + ASSERT_TRUE(process_reader.Memory()->ReadCString(addr, &read_string)); + EXPECT_EQ(read_string, kTestMemory); + } +}; + +TEST(ProcessReaderFuchsia, ChildBasic) { + BasicChildTest test; + test.Run(); +} + +void* SignalAndSleep(void* arg) { + zx_port_packet_t packet = {}; + packet.type = ZX_PKT_TYPE_USER; + zx_port_queue(*reinterpret_cast(arg), &packet); + zx_nanosleep(ZX_TIME_INFINITE); + return nullptr; +} + +CRASHPAD_CHILD_TEST_MAIN(ProcessReaderChildThreadsTestMain) { + // Create 5 threads with stack sizes of 4096, 8192, ... + zx_handle_t port; + zx_status_t status = zx_port_create(0, &port); + EXPECT_EQ(status, ZX_OK); + + constexpr size_t kNumThreads = 5; + for (size_t i = 0; i < kNumThreads; ++i) { + pthread_attr_t attr; + EXPECT_EQ(pthread_attr_init(&attr), 0); + EXPECT_EQ(pthread_attr_setstacksize(&attr, (i + 1) * 4096), 0); + pthread_t thread; + EXPECT_EQ(pthread_create(&thread, &attr, &SignalAndSleep, &port), 0); + } + + // Wait until all threads are ready. + for (size_t i = 0; i < kNumThreads; ++i) { + zx_port_packet_t packet; + zx_port_wait(port, ZX_TIME_INFINITE, &packet); + } + + char c = ' '; + CheckedWriteFile( + StdioFileHandle(StdioStream::kStandardOutput), &c, sizeof(c)); + CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput)); + return 0; +} + +class ThreadsChildTest : public MultiprocessExec { + public: + ThreadsChildTest() : MultiprocessExec() { + SetChildTestMainFunction("ProcessReaderChildThreadsTestMain"); + } + + ThreadsChildTest(const ThreadsChildTest&) = delete; + ThreadsChildTest& operator=(const ThreadsChildTest&) = delete; + + ~ThreadsChildTest() {} + + private: + void MultiprocessParent() override { + char c; + ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), &c, 1)); + ASSERT_EQ(c, ' '); + + ScopedTaskSuspend suspend(*ChildProcess()); + + ProcessReaderFuchsia process_reader; + ASSERT_TRUE(process_reader.Initialize(*ChildProcess())); + + const auto& threads = process_reader.Threads(); + EXPECT_EQ(threads.size(), 6u); + + for (size_t i = 1; i < 6; ++i) { + ASSERT_GT(threads[i].stack_regions.size(), 0u); + EXPECT_GT(threads[i].stack_regions[0].size(), 0u); + EXPECT_LE(threads[i].stack_regions[0].size(), i * 4096u); + } + } +}; + +// TODO(scottmg): US-553. ScopedTaskSuspend fails sometimes, with a 50ms +// timeout. Currently unclear how to make that more reliable, so disable the +// test for now as otherwise it flakes. +TEST(ProcessReaderFuchsia, DISABLED_ChildThreads) { + ThreadsChildTest test; + test.Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/fuchsia/process_snapshot_fuchsia.cc b/shared/sentry/external/crashpad/snapshot/fuchsia/process_snapshot_fuchsia.cc new file mode 100644 index 000000000..8ebdae5cc --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/fuchsia/process_snapshot_fuchsia.cc @@ -0,0 +1,243 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/fuchsia/process_snapshot_fuchsia.h" + +#include "base/logging.h" +#include "util/fuchsia/koid_utilities.h" + +namespace crashpad { + +ProcessSnapshotFuchsia::ProcessSnapshotFuchsia() = default; + +ProcessSnapshotFuchsia::~ProcessSnapshotFuchsia() = default; + +bool ProcessSnapshotFuchsia::Initialize(const zx::process& process) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + if (gettimeofday(&snapshot_time_, nullptr) != 0) { + PLOG(ERROR) << "gettimeofday"; + return false; + } + + if (!process_reader_.Initialize(process) || + !memory_range_.Initialize(process_reader_.Memory(), true)) { + return false; + } + + client_id_.InitializeToZero(); + system_.Initialize(&snapshot_time_); + + InitializeThreads(); + InitializeModules(); + + const MemoryMapFuchsia* memory_map = process_reader_.MemoryMap(); + if (memory_map) { + for (const auto& entry : memory_map->Entries()) { + if (entry.type == ZX_INFO_MAPS_TYPE_MAPPING) { + memory_map_.push_back( + std::make_unique(entry)); + } + } + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +bool ProcessSnapshotFuchsia::InitializeException( + zx_koid_t thread_id, + const zx_exception_report_t& report) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + std::unique_ptr exception( + new internal::ExceptionSnapshotFuchsia()); + if (exception->Initialize(&process_reader_, thread_id, report)) { + exception_.swap(exception); + return true; + } + return false; +} + +void ProcessSnapshotFuchsia::GetCrashpadOptions( + CrashpadInfoClientOptions* options) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + CrashpadInfoClientOptions local_options; + + for (const auto& module : modules_) { + CrashpadInfoClientOptions module_options; + module->GetCrashpadOptions(&module_options); + + if (local_options.crashpad_handler_behavior == TriState::kUnset) { + local_options.crashpad_handler_behavior = + module_options.crashpad_handler_behavior; + } + if (local_options.system_crash_reporter_forwarding == TriState::kUnset) { + local_options.system_crash_reporter_forwarding = + module_options.system_crash_reporter_forwarding; + } + if (local_options.gather_indirectly_referenced_memory == TriState::kUnset) { + local_options.gather_indirectly_referenced_memory = + module_options.gather_indirectly_referenced_memory; + local_options.indirectly_referenced_memory_cap = + module_options.indirectly_referenced_memory_cap; + } + + // If non-default values have been found for all options, the loop can end + // early. + if (local_options.crashpad_handler_behavior != TriState::kUnset && + local_options.system_crash_reporter_forwarding != TriState::kUnset && + local_options.gather_indirectly_referenced_memory != TriState::kUnset) { + break; + } + } + + *options = local_options; +} + +crashpad::ProcessID ProcessSnapshotFuchsia::ProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return GetKoidForHandle(*zx::process::self()); +} + +crashpad::ProcessID ProcessSnapshotFuchsia::ParentProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // TODO(scottmg): https://crashpad.chromium.org/bug/196 + return 0; +} + +void ProcessSnapshotFuchsia::SnapshotTime(timeval* snapshot_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *snapshot_time = snapshot_time_; +} + +void ProcessSnapshotFuchsia::ProcessStartTime(timeval* start_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(scottmg): https://crashpad.chromium.org/bug/196. Nothing available. + *start_time = timeval{}; +} + +void ProcessSnapshotFuchsia::ProcessCPUTimes(timeval* user_time, + timeval* system_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(scottmg): https://crashpad.chromium.org/bug/196. Nothing available. + *user_time = timeval{}; + *system_time = timeval{}; +} + +void ProcessSnapshotFuchsia::ReportID(UUID* report_id) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *report_id = report_id_; +} + +void ProcessSnapshotFuchsia::ClientID(UUID* client_id) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *client_id = client_id_; +} + +const std::map& +ProcessSnapshotFuchsia::AnnotationsSimpleMap() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return annotations_simple_map_; +} + +const SystemSnapshot* ProcessSnapshotFuchsia::System() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &system_; +} + +std::vector ProcessSnapshotFuchsia::Threads() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector threads; + for (const auto& thread : threads_) { + threads.push_back(thread.get()); + } + return threads; +} + +std::vector ProcessSnapshotFuchsia::Modules() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector modules; + for (const auto& module : modules_) { + modules.push_back(module.get()); + } + return modules; +} + +std::vector ProcessSnapshotFuchsia::UnloadedModules() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // dlclose() never unloads on Fuchsia. ZX-1728 upstream. + return std::vector(); +} + +const ExceptionSnapshot* ProcessSnapshotFuchsia::Exception() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_.get(); +} + +std::vector ProcessSnapshotFuchsia::MemoryMap() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector memory_map; + for (const auto& item : memory_map_) { + memory_map.push_back(item.get()); + } + return memory_map; +} + +std::vector ProcessSnapshotFuchsia::Handles() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector(); +} + +std::vector ProcessSnapshotFuchsia::ExtraMemory() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector(); +} + +const ProcessMemory* ProcessSnapshotFuchsia::Memory() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return process_reader_.Memory(); +} + +void ProcessSnapshotFuchsia::InitializeThreads() { + const std::vector& process_reader_threads = + process_reader_.Threads(); + for (const ProcessReaderFuchsia::Thread& process_reader_thread : + process_reader_threads) { + auto thread = std::make_unique(); + if (thread->Initialize(&process_reader_, process_reader_thread)) { + threads_.push_back(std::move(thread)); + } + } +} + +void ProcessSnapshotFuchsia::InitializeModules() { + for (const ProcessReaderFuchsia::Module& reader_module : + process_reader_.Modules()) { + auto module = + std::make_unique(reader_module.name, + reader_module.reader, + reader_module.type, + &memory_range_, + process_reader_.Memory()); + if (module->Initialize()) { + modules_.push_back(std::move(module)); + } + } +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/fuchsia/process_snapshot_fuchsia.h b/shared/sentry/external/crashpad/snapshot/fuchsia/process_snapshot_fuchsia.h new file mode 100644 index 000000000..47efaa341 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/fuchsia/process_snapshot_fuchsia.h @@ -0,0 +1,155 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_FUCHSIA_PROCESS_SNAPSHOT_FUCHSIA_H_ +#define CRASHPAD_SNAPSHOT_FUCHSIA_PROCESS_SNAPSHOT_FUCHSIA_H_ + +#include +#include +#include +#include + +#include +#include + +#include "snapshot/crashpad_info_client_options.h" +#include "snapshot/elf/elf_image_reader.h" +#include "snapshot/elf/module_snapshot_elf.h" +#include "snapshot/fuchsia/exception_snapshot_fuchsia.h" +#include "snapshot/fuchsia/memory_map_region_snapshot_fuchsia.h" +#include "snapshot/fuchsia/process_reader_fuchsia.h" +#include "snapshot/fuchsia/system_snapshot_fuchsia.h" +#include "snapshot/fuchsia/thread_snapshot_fuchsia.h" +#include "snapshot/process_snapshot.h" +#include "snapshot/unloaded_module_snapshot.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/process/process_id.h" +#include "util/process/process_memory_range.h" + +namespace crashpad { + +//! \brief A ProcessSnapshot of a running (or crashed) process running on a +//! Fuchsia system. This class is not yet implemented. +class ProcessSnapshotFuchsia : public ProcessSnapshot { + public: + ProcessSnapshotFuchsia(); + + ProcessSnapshotFuchsia(const ProcessSnapshotFuchsia&) = delete; + ProcessSnapshotFuchsia& operator=(const ProcessSnapshotFuchsia&) = delete; + + ~ProcessSnapshotFuchsia() override; + + //! \brief Initializes the object. + //! + //! \param[in] process The process handle to create a snapshot from. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(const zx::process& process); + + //! \brief Initializes the object's exception. + //! + //! This populates the data to be returned by Exception(). The thread + //! identified by \a thread_id must be in an exception. + //! + //! This method must not be called until after a successful call to + //! Initialize(). + //! + //! \param[in] thread_id Koid of the thread which sustained the exception. + //! \param[in] report The `zx_exception_report_t` for the thread which + //! sustained the exception. + //! \return `true` if the exception information could be initialized, `false` + //! otherwise with an appropriate message logged. When this method returns + //! `false`, the ProcessSnapshotFuchsia object’s validity remains + //! unchanged. + bool InitializeException(zx_koid_t thread_id, + const zx_exception_report_t& report); + + //! \brief Returns options from CrashpadInfo structures found in modules in + //! the process. + //! + //! \param[out] options Options set in CrashpadInfo structures in modules in + //! the process. + void GetCrashpadOptions(CrashpadInfoClientOptions* options); + + //! \brief Sets the value to be returned by ReportID(). + //! + //! On Fuchsia, the crash report ID is under the control of the snapshot + //! producer, which may call this method to set the report ID. If this is not + //! done, ReportID() will return an identifier consisting entirely of zeroes. + void SetReportID(const UUID& report_id) { report_id_ = report_id; } + + //! \brief Sets the value to be returned by ClientID(). + //! + //! On Fuchsia, the client ID is under the control of the snapshot producer, + //! which may call this method to set the client ID. If this is not done, + //! ClientID() will return an identifier consisting entirely of zeroes. + void SetClientID(const UUID& client_id) { client_id_ = client_id; } + + //! \brief Sets the value to be returned by AnnotationsSimpleMap(). + //! + //! On Fuchsia, all process annotations are under the control of the snapshot + //! producer, which may call this method to establish these annotations. + //! Contrast this with module annotations, which are under the control of the + //! process being snapshotted. + void SetAnnotationsSimpleMap( + const std::map& annotations_simple_map) { + annotations_simple_map_ = annotations_simple_map; + } + + // ProcessSnapshot: + crashpad::ProcessID ProcessID() const override; + crashpad::ProcessID ParentProcessID() const override; + void SnapshotTime(timeval* snapshot_time) const override; + void ProcessStartTime(timeval* start_time) const override; + void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override; + void ReportID(UUID* report_id) const override; + void ClientID(UUID* client_id) const override; + const std::map& AnnotationsSimpleMap() + const override; + const SystemSnapshot* System() const override; + std::vector Threads() const override; + std::vector Modules() const override; + std::vector UnloadedModules() const override; + const ExceptionSnapshot* Exception() const override; + std::vector MemoryMap() const override; + std::vector Handles() const override; + std::vector ExtraMemory() const override; + const ProcessMemory* Memory() const override; + + private: + // Initializes threads_ on behalf of Initialize(). + void InitializeThreads(); + + // Initializes modules_ on behalf of Initialize(). + void InitializeModules(); + + internal::SystemSnapshotFuchsia system_; + std::vector> threads_; + std::vector> modules_; + std::unique_ptr exception_; + ProcessReaderFuchsia process_reader_; + ProcessMemoryRange memory_range_; + std::map annotations_simple_map_; + std::vector> + memory_map_; + UUID report_id_; + UUID client_id_; + timeval snapshot_time_; + InitializationStateDcheck initialized_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_FUCHSIA_PROCESS_SNAPSHOT_FUCHSIA_H_ diff --git a/shared/sentry/external/crashpad/snapshot/fuchsia/process_snapshot_fuchsia_test.cc b/shared/sentry/external/crashpad/snapshot/fuchsia/process_snapshot_fuchsia_test.cc new file mode 100644 index 000000000..874f15905 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/fuchsia/process_snapshot_fuchsia_test.cc @@ -0,0 +1,240 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/fuchsia/process_snapshot_fuchsia.h" + +#include +#include + +#include + +#include "base/fuchsia/fuchsia_logging.h" +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" +#include "snapshot/fuchsia/memory_map_region_snapshot_fuchsia.h" +#include "test/multiprocess_exec.h" +#include "util/fuchsia/koid_utilities.h" +#include "util/fuchsia/scoped_task_suspend.h" + +namespace crashpad { +namespace test { +namespace { + +constexpr struct { + uint32_t zircon_perm; + size_t pages; + uint32_t minidump_perm; +} kTestMappingPermAndSizes[] = { + // Zircon doesn't currently allow write-only, execute-only, or + // write-execute-only, returning ZX_ERR_INVALID_ARGS on map. + {0, 5, PAGE_NOACCESS}, + {ZX_VM_PERM_READ, 6, PAGE_READONLY}, + // {ZX_VM_PERM_WRITE, 7, PAGE_WRITECOPY}, + // {ZX_VM_PERM_EXECUTE, 8, PAGE_EXECUTE}, + {ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, 9, PAGE_READWRITE}, + {ZX_VM_PERM_READ | ZX_VM_PERM_EXECUTE, 10, PAGE_EXECUTE_READ}, + // {ZX_VM_PERM_WRITE | ZX_VM_PERM_EXECUTE, 11, PAGE_EXECUTE_WRITECOPY}, + {ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_PERM_EXECUTE, + 12, + PAGE_EXECUTE_READWRITE}, +}; + +CRASHPAD_CHILD_TEST_MAIN(AddressSpaceChildTestMain) { + // Create specifically sized mappings/permissions and write the address in + // our address space to the parent so that the reader can check they're read + // correctly. + for (const auto& t : kTestMappingPermAndSizes) { + zx_handle_t vmo = ZX_HANDLE_INVALID; + const size_t size = t.pages * zx_system_get_page_size(); + zx_status_t status = zx_vmo_create(size, 0, &vmo); + ZX_CHECK(status == ZX_OK, status) << "zx_vmo_create"; + status = zx_vmo_replace_as_executable(vmo, ZX_HANDLE_INVALID, &vmo); + ZX_CHECK(status == ZX_OK, status) << "zx_vmo_replace_as_executable"; + uintptr_t mapping_addr = 0; + status = zx_vmar_map( + zx_vmar_root_self(), t.zircon_perm, 0, vmo, 0, size, &mapping_addr); + ZX_CHECK(status == ZX_OK, status) << "zx_vmar_map"; + CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput), + &mapping_addr, + sizeof(mapping_addr)); + } + + CheckedReadFileAtEOF(StdioFileHandle(StdioStream::kStandardInput)); + return 0; +} + +bool HasSingleMatchingMapping( + const std::vector& memory_map, + uintptr_t address, + size_t size, + uint32_t perm) { + const MemoryMapRegionSnapshot* matching = nullptr; + for (const auto* region : memory_map) { + const MINIDUMP_MEMORY_INFO& mmi = region->AsMinidumpMemoryInfo(); + if (mmi.BaseAddress == address) { + if (matching) { + LOG(ERROR) << "multiple mappings matching address"; + return false; + } + matching = region; + } + } + + if (!matching) + return false; + + const MINIDUMP_MEMORY_INFO& matching_mmi = matching->AsMinidumpMemoryInfo(); + return matching_mmi.Protect == perm && matching_mmi.RegionSize == size; +} + +class AddressSpaceTest : public MultiprocessExec { + public: + AddressSpaceTest() : MultiprocessExec() { + SetChildTestMainFunction("AddressSpaceChildTestMain"); + } + + AddressSpaceTest(const AddressSpaceTest&) = delete; + AddressSpaceTest& operator=(const AddressSpaceTest&) = delete; + + ~AddressSpaceTest() {} + + private: + void MultiprocessParent() override { + uintptr_t test_addresses[std::size(kTestMappingPermAndSizes)]; + for (size_t i = 0; i < std::size(test_addresses); ++i) { + ASSERT_TRUE(ReadFileExactly( + ReadPipeHandle(), &test_addresses[i], sizeof(test_addresses[i]))); + } + + ScopedTaskSuspend suspend(*ChildProcess()); + + ProcessSnapshotFuchsia process_snapshot; + ASSERT_TRUE(process_snapshot.Initialize(*ChildProcess())); + + for (size_t i = 0; i < std::size(test_addresses); ++i) { + const auto& t = kTestMappingPermAndSizes[i]; + EXPECT_TRUE(HasSingleMatchingMapping(process_snapshot.MemoryMap(), + test_addresses[i], + t.pages * zx_system_get_page_size(), + t.minidump_perm)) + << base::StringPrintf( + "index %zu, zircon_perm 0x%x, minidump_perm 0x%x", + i, + t.zircon_perm, + t.minidump_perm); + } + } +}; + +TEST(ProcessSnapshotFuchsiaTest, AddressSpaceMapping) { + AddressSpaceTest test; + test.Run(); +} + +CRASHPAD_CHILD_TEST_MAIN(StackPointerIntoInvalidLocation) { + // Map a large block, output the base address of it, and block. The parent + // will artificially set the SP into this large block to confirm that a huge + // stack is not accidentally captured. + zx_handle_t large_vmo; + constexpr uint64_t kSize = 1 << 30u; + zx_status_t status = zx_vmo_create(kSize, 0, &large_vmo); + ZX_CHECK(status == ZX_OK, status) << "zx_vmo_create"; + zx_vaddr_t mapped_addr; + status = zx_vmar_map(zx_vmar_root_self(), + ZX_VM_PERM_READ | ZX_VM_PERM_WRITE, + 0, + large_vmo, + 0, + kSize, + &mapped_addr); + ZX_CHECK(status == ZX_OK, status) << "zx_vmar_map"; + CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput), + &mapped_addr, + sizeof(mapped_addr)); + zx_nanosleep(ZX_TIME_INFINITE); + return 0; +} + +class InvalidStackPointerTest : public MultiprocessExec { + public: + InvalidStackPointerTest() : MultiprocessExec() { + SetChildTestMainFunction("StackPointerIntoInvalidLocation"); + SetExpectedChildTermination(kTerminationNormal, + ZX_TASK_RETCODE_SYSCALL_KILL); + } + + InvalidStackPointerTest(const InvalidStackPointerTest&) = delete; + InvalidStackPointerTest& operator=(const InvalidStackPointerTest&) = delete; + + ~InvalidStackPointerTest() {} + + private: + void MultiprocessParent() override { + uint64_t address_of_large_mapping; + ASSERT_TRUE(ReadFileExactly(ReadPipeHandle(), + &address_of_large_mapping, + sizeof(address_of_large_mapping))); + + ScopedTaskSuspend suspend(*ChildProcess()); + + std::vector threads = GetThreadHandles(*ChildProcess()); + ASSERT_EQ(threads.size(), 1u); + + zx_thread_state_general_regs_t regs; + ASSERT_EQ(threads[0].read_state( + ZX_THREAD_STATE_GENERAL_REGS, ®s, sizeof(regs)), + ZX_OK); + + constexpr uint64_t kOffsetIntoMapping = 1024; +#if defined(ARCH_CPU_X86_64) + regs.rsp = address_of_large_mapping + kOffsetIntoMapping; +#elif defined(ARCH_CPU_ARM64) + regs.sp = address_of_large_mapping + kOffsetIntoMapping; +#else +#error +#endif + + ASSERT_EQ(threads[0].write_state( + ZX_THREAD_STATE_GENERAL_REGS, ®s, sizeof(regs)), + ZX_OK); + + ProcessSnapshotFuchsia process_snapshot; + ASSERT_TRUE(process_snapshot.Initialize(*ChildProcess())); + + ASSERT_EQ(process_snapshot.Threads().size(), 1u); + const MemorySnapshot* stack = process_snapshot.Threads()[0]->Stack(); + ASSERT_TRUE(stack); + // Ensure the stack capture isn't unreasonably large. + EXPECT_LT(stack->Size(), 10 * 1048576u); + + // As we've corrupted the child, don't let it run again. + ASSERT_EQ(ChildProcess()->kill(), ZX_OK); + } +}; + +// This is a test for a specific failure detailed in +// https://bugs.fuchsia.dev/p/fuchsia/issues/detail?id=41212. A test of stack +// behavior that was intentionally overflowing the stack, and so when Crashpad +// received the exception the SP did not point into the actual stack. This +// caused Crashpad to erronously capture the "stack" from the next mapping in +// the address space (which could be very large, cause OOM, etc.). +TEST(ProcessSnapshotFuchsiaTest, InvalidStackPointer) { + InvalidStackPointerTest test; + test.Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/fuchsia/system_snapshot_fuchsia.cc b/shared/sentry/external/crashpad/snapshot/fuchsia/system_snapshot_fuchsia.cc new file mode 100644 index 000000000..36a4e50fc --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/fuchsia/system_snapshot_fuchsia.cc @@ -0,0 +1,213 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/fuchsia/system_snapshot_fuchsia.h" + +#include + +#include "base/check_op.h" +#include "base/fuchsia/fuchsia_logging.h" +#include "base/notreached.h" +#include "base/numerics/safe_conversions.h" +#include "base/strings/stringprintf.h" +#include "snapshot/posix/timezone.h" + +namespace crashpad { +namespace internal { + +SystemSnapshotFuchsia::SystemSnapshotFuchsia() = default; + +SystemSnapshotFuchsia::~SystemSnapshotFuchsia() = default; + +void SystemSnapshotFuchsia::Initialize(const timeval* snapshot_time) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + snapshot_time_ = snapshot_time; + + // This version string mirrors `uname -a` as written by + // garnet/bin/uname/uname.c, however, this information isn't provided by + // uname(). Additionally, uname() seems to hang if the network is in a bad + // state when attempting to retrieve the nodename, so avoid it for now. + std::string kernel_version = zx_system_get_version_string(); + +#if defined(ARCH_CPU_X86_64) + static constexpr const char kArch[] = "x86_64"; +#elif defined(ARCH_CPU_ARM64) + static constexpr const char kArch[] = "aarch64"; +#else + static constexpr const char kArch[] = "unknown"; +#endif + os_version_full_ = base::StringPrintf( + "Zircon prerelease %s %s", kernel_version.c_str(), kArch); + + INITIALIZATION_STATE_SET_VALID(initialized_); +} + +CPUArchitecture SystemSnapshotFuchsia::GetCPUArchitecture() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + +#if defined(ARCH_CPU_X86_64) + return kCPUArchitectureX86_64; +#elif defined(ARCH_CPU_ARM64) + return kCPUArchitectureARM64; +#else +#error Port +#endif +} + +uint32_t SystemSnapshotFuchsia::CPURevision() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if defined(ARCH_CPU_X86_64) + return cpuid_.Revision(); +#else + // TODO(fuchsia/DX-712): Read actual revision. + return 0; +#endif +} + +uint8_t SystemSnapshotFuchsia::CPUCount() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return base::saturated_cast(zx_system_get_num_cpus()); +} + +std::string SystemSnapshotFuchsia::CPUVendor() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if defined(ARCH_CPU_X86_64) + return cpuid_.Vendor(); +#else + // TODO(fuchsia/DX-712): Read actual vendor. + return std::string(); +#endif +} + +void SystemSnapshotFuchsia::CPUFrequency(uint64_t* current_hz, + uint64_t* max_hz) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(scottmg): https://crashpad.chromium.org/bug/196. + *current_hz = 0; + *max_hz = 0; +} + +uint32_t SystemSnapshotFuchsia::CPUX86Signature() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if defined(ARCH_CPU_X86_64) + return cpuid_.Signature(); +#else + NOTREACHED(); + return 0; +#endif +} + +uint64_t SystemSnapshotFuchsia::CPUX86Features() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if defined(ARCH_CPU_X86_64) + return cpuid_.Features(); +#else + NOTREACHED(); + return 0; +#endif +} + +uint64_t SystemSnapshotFuchsia::CPUX86ExtendedFeatures() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if defined(ARCH_CPU_X86_64) + return cpuid_.ExtendedFeatures(); +#else + NOTREACHED(); + return 0; +#endif +} + +uint32_t SystemSnapshotFuchsia::CPUX86Leaf7Features() const { +#if defined(ARCH_CPU_X86_64) + return cpuid_.Leaf7Features(); +#else + NOTREACHED(); + return 0; +#endif +} + +bool SystemSnapshotFuchsia::CPUX86SupportsDAZ() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if defined(ARCH_CPU_X86_64) + return cpuid_.SupportsDAZ(); +#else + NOTREACHED(); + return false; +#endif +} + +SystemSnapshot::OperatingSystem SystemSnapshotFuchsia::GetOperatingSystem() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return kOperatingSystemFuchsia; +} + +bool SystemSnapshotFuchsia::OSServer() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return false; +} + +void SystemSnapshotFuchsia::OSVersion(int* major, + int* minor, + int* bugfix, + std::string* build) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(scottmg): https://crashpad.chromium.org/bug/196. There's no version + // available to be reported yet. + *major = 0; + *minor = 0; + *bugfix = 0; + *build = std::string(); +} + +std::string SystemSnapshotFuchsia::OSVersionFull() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return os_version_full_; +} + +std::string SystemSnapshotFuchsia::MachineDescription() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(scottmg): https://crashpad.chromium.org/bug/196. Not yet available, + // upstream ZX-1775. + return std::string(); +} + +bool SystemSnapshotFuchsia::NXEnabled() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if defined(ARCH_CPU_X86_64) + return cpuid_.NXEnabled(); +#else + // TODO(fuchsia/DX-712): Read actual NX bit value. + return false; +#endif +} + +void SystemSnapshotFuchsia::TimeZone(DaylightSavingTimeStatus* dst_status, + int* standard_offset_seconds, + int* daylight_offset_seconds, + std::string* standard_name, + std::string* daylight_name) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + internal::TimeZone(*snapshot_time_, + dst_status, + standard_offset_seconds, + daylight_offset_seconds, + standard_name, + daylight_name); +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/fuchsia/system_snapshot_fuchsia.h b/shared/sentry/external/crashpad/snapshot/fuchsia/system_snapshot_fuchsia.h new file mode 100644 index 000000000..aadffac91 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/fuchsia/system_snapshot_fuchsia.h @@ -0,0 +1,88 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_FUCHSIA_SYSTEM_SNAPSHOT_FUCHSIA_H_ +#define CRASHPAD_SNAPSHOT_FUCHSIA_SYSTEM_SNAPSHOT_FUCHSIA_H_ + +#include + +#include "build/build_config.h" +#include "snapshot/system_snapshot.h" +#include "util/misc/initialization_state_dcheck.h" + +#if defined(ARCH_CPU_X86_FAMILY) +#include "snapshot/x86/cpuid_reader.h" +#endif + +namespace crashpad { +namespace internal { + +//! \brief A SystemSnapshot of the running system, when the system runs Fuchsia. +class SystemSnapshotFuchsia final : public SystemSnapshot { + public: + SystemSnapshotFuchsia(); + + SystemSnapshotFuchsia(const SystemSnapshotFuchsia&) = delete; + SystemSnapshotFuchsia& operator=(const SystemSnapshotFuchsia&) = delete; + + ~SystemSnapshotFuchsia() override; + + //! \brief Initializes the object. + //! + //! \param[in] snapshot_time The time of the snapshot being taken. + //! + //! This parameter is necessary for TimeZone() to determine whether daylight + //! saving time was in effect at the time the snapshot was taken. Otherwise, + //! it would need to base its determination on the current time, which may be + //! different than the snapshot time for snapshots generated around the + //! daylight saving transition time. + void Initialize(const timeval* snapshot_time); + + // SystemSnapshot: + + CPUArchitecture GetCPUArchitecture() const override; + uint32_t CPURevision() const override; + uint8_t CPUCount() const override; + std::string CPUVendor() const override; + void CPUFrequency(uint64_t* current_hz, uint64_t* max_hz) const override; + uint32_t CPUX86Signature() const override; + uint64_t CPUX86Features() const override; + uint64_t CPUX86ExtendedFeatures() const override; + uint32_t CPUX86Leaf7Features() const override; + bool CPUX86SupportsDAZ() const override; + OperatingSystem GetOperatingSystem() const override; + bool OSServer() const override; + void OSVersion( + int* major, int* minor, int* bugfix, std::string* build) const override; + std::string OSVersionFull() const override; + bool NXEnabled() const override; + std::string MachineDescription() const override; + void TimeZone(DaylightSavingTimeStatus* dst_status, + int* standard_offset_seconds, + int* daylight_offset_seconds, + std::string* standard_name, + std::string* daylight_name) const override; + private: + std::string os_version_full_; + const timeval* snapshot_time_; // weak +#if defined(ARCH_CPU_X86_FAMILY) + CpuidReader cpuid_; +#endif // ARCH_CPU_X86_FAMILY + InitializationStateDcheck initialized_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_FUCHSIA_SYSTEM_SNAPSHOT_FUCHSIA_H_ diff --git a/shared/sentry/external/crashpad/snapshot/fuchsia/thread_snapshot_fuchsia.cc b/shared/sentry/external/crashpad/snapshot/fuchsia/thread_snapshot_fuchsia.cc new file mode 100644 index 000000000..369203ae3 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/fuchsia/thread_snapshot_fuchsia.cc @@ -0,0 +1,106 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/fuchsia/thread_snapshot_fuchsia.h" + +#include "base/check_op.h" +#include "snapshot/fuchsia/cpu_context_fuchsia.h" + +namespace crashpad { +namespace internal { + +ThreadSnapshotFuchsia::ThreadSnapshotFuchsia() + : ThreadSnapshot(), + context_arch_(), + context_(), + stack_(), + thread_id_(ZX_KOID_INVALID), + thread_specific_data_address_(0), + initialized_() {} + +ThreadSnapshotFuchsia::~ThreadSnapshotFuchsia() {} + +bool ThreadSnapshotFuchsia::Initialize( + ProcessReaderFuchsia* process_reader, + const ProcessReaderFuchsia::Thread& thread) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + +#if defined(ARCH_CPU_X86_64) + context_.architecture = kCPUArchitectureX86_64; + context_.x86_64 = &context_arch_; + // TODO(fuchsia/DX-642): Add float context once saved in |thread|. + InitializeCPUContextX86_64_NoFloatingPoint(thread.general_registers, + context_.x86_64); +#elif defined(ARCH_CPU_ARM64) + context_.architecture = kCPUArchitectureARM64; + context_.arm64 = &context_arch_; + InitializeCPUContextARM64( + thread.general_registers, thread.vector_registers, context_.arm64); +#else +#error Port. +#endif + + if (thread.stack_regions.empty()) { + stack_.Initialize(process_reader->Memory(), 0, 0); + } else { + stack_.Initialize(process_reader->Memory(), + thread.stack_regions[0].base(), + thread.stack_regions[0].size()); + // TODO(scottmg): Handle split stack by adding other parts to ExtraMemory(). + } + + thread_id_ = thread.id; + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +const CPUContext* ThreadSnapshotFuchsia::Context() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &context_; +} + +const MemorySnapshot* ThreadSnapshotFuchsia::Stack() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &stack_; +} + +uint64_t ThreadSnapshotFuchsia::ThreadID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_id_; +} + +int ThreadSnapshotFuchsia::SuspendCount() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // There is not (currently) a suspend count for threads on Fuchsia. + return 0; +} + +int ThreadSnapshotFuchsia::Priority() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // There is not (currently) thread priorities on Fuchsia. + return 0; +} + +uint64_t ThreadSnapshotFuchsia::ThreadSpecificDataAddress() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_specific_data_address_; +} + +std::vector ThreadSnapshotFuchsia::ExtraMemory() const { + return std::vector(); +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/fuchsia/thread_snapshot_fuchsia.h b/shared/sentry/external/crashpad/snapshot/fuchsia/thread_snapshot_fuchsia.h new file mode 100644 index 000000000..b91a514d9 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/fuchsia/thread_snapshot_fuchsia.h @@ -0,0 +1,82 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_FUCHSIA_THREAD_SNAPSHOT_FUCHSIA_H_ +#define CRASHPAD_SNAPSHOT_FUCHSIA_THREAD_SNAPSHOT_FUCHSIA_H_ + +#include +#include + +#include "build/build_config.h" +#include "snapshot/cpu_context.h" +#include "snapshot/fuchsia/process_reader_fuchsia.h" +#include "snapshot/memory_snapshot.h" +#include "snapshot/memory_snapshot_generic.h" +#include "snapshot/thread_snapshot.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { +namespace internal { + +//! \brief A ThreadSnapshot of a thread on a Fuchsia system. +class ThreadSnapshotFuchsia final : public ThreadSnapshot { + public: + ThreadSnapshotFuchsia(); + + ThreadSnapshotFuchsia(const ThreadSnapshotFuchsia&) = delete; + ThreadSnapshotFuchsia& operator=(const ThreadSnapshotFuchsia&) = delete; + + ~ThreadSnapshotFuchsia() override; + + //! \brief Initializes the object. + //! + //! \param[in] process_reader A ProcessReaderFuchsia for the process + //! containing the thread. + //! \param[in] thread The thread within the ProcessReaderFuchsia for + //! which the snapshot should be created. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! a message logged. + bool Initialize(ProcessReaderFuchsia* process_reader, + const ProcessReaderFuchsia::Thread& thread); + + // ThreadSnapshot: + + const CPUContext* Context() const override; + const MemorySnapshot* Stack() const override; + uint64_t ThreadID() const override; + int SuspendCount() const override; + int Priority() const override; + uint64_t ThreadSpecificDataAddress() const override; + std::vector ExtraMemory() const override; + + private: +#if defined(ARCH_CPU_X86_64) + CPUContextX86_64 context_arch_; +#elif defined(ARCH_CPU_ARM64) + CPUContextARM64 context_arch_; +#else +#error Port. +#endif + CPUContext context_; + MemorySnapshotGeneric stack_; + zx_koid_t thread_id_; + zx_vaddr_t thread_specific_data_address_; + InitializationStateDcheck initialized_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_FUCHSIA_THREAD_SNAPSHOT_FUCHSIA_H_ diff --git a/shared/sentry/external/crashpad/snapshot/handle_snapshot.cc b/shared/sentry/external/crashpad/snapshot/handle_snapshot.cc new file mode 100644 index 000000000..331b6b356 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/handle_snapshot.cc @@ -0,0 +1,31 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/handle_snapshot.h" + +namespace crashpad { + +HandleSnapshot::HandleSnapshot() + : type_name(), + handle(0), + attributes(0), + granted_access(0), + pointer_count(0), + handle_count(0) { +} + +HandleSnapshot::~HandleSnapshot() { +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/handle_snapshot.h b/shared/sentry/external/crashpad/snapshot/handle_snapshot.h new file mode 100644 index 000000000..eee7e7e45 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/handle_snapshot.h @@ -0,0 +1,56 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_HANDLE_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_HANDLE_SNAPSHOT_H_ + +#include + +#include + +namespace crashpad { + +struct HandleSnapshot { + HandleSnapshot(); + ~HandleSnapshot(); + + //! \brief A UTF-8 string representation of the handle's type. + std::string type_name; + + //! \brief The handle's value. + uint32_t handle; + + //! \brief The attributes for the handle, e.g. `OBJ_INHERIT`, + //! `OBJ_CASE_INSENSITIVE`, etc. + uint32_t attributes; + + //! \brief The ACCESS_MASK for the handle in this process. + //! + //! See + //! https://blogs.msdn.microsoft.com/openspecification/2010/04/01/about-the-access_mask-structure/ + //! for more information. + uint32_t granted_access; + + //! \brief The number of kernel references to the object that this handle + //! refers to. + uint32_t pointer_count; + + //! \brief The number of open handles to the object that this handle refers + //! to. + uint32_t handle_count; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_HANDLE_SNAPSHOT_H_ diff --git a/shared/sentry/external/crashpad/snapshot/hash_types_test.cc b/shared/sentry/external/crashpad/snapshot/hash_types_test.cc new file mode 100644 index 000000000..436323b20 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/hash_types_test.cc @@ -0,0 +1,17 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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. + +int main() { + return 0; +} diff --git a/shared/sentry/external/crashpad/snapshot/ios/exception_snapshot_ios_intermediate_dump.cc b/shared/sentry/external/crashpad/snapshot/ios/exception_snapshot_ios_intermediate_dump.cc new file mode 100644 index 000000000..a45d4cc29 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/ios/exception_snapshot_ios_intermediate_dump.cc @@ -0,0 +1,399 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/ios/exception_snapshot_ios_intermediate_dump.h" + +#include "base/logging.h" +#include "base/mac/mach_logging.h" +#include "snapshot/cpu_context.h" +#include "snapshot/ios/intermediate_dump_reader_util.h" +#include "snapshot/mac/cpu_context_mac.h" +#include "util/ios/ios_intermediate_dump_data.h" +#include "util/ios/ios_intermediate_dump_list.h" +#include "util/ios/ios_intermediate_dump_writer.h" +#include "util/misc/from_pointer_cast.h" + +namespace crashpad { + +namespace internal { + +size_t ThreadStateLengthForFlavor(thread_state_flavor_t flavor) { +#if defined(ARCH_CPU_X86_64) + switch (flavor) { + case x86_THREAD_STATE: + return sizeof(x86_thread_state_t); + case x86_FLOAT_STATE: + return sizeof(x86_float_state_t); + case x86_DEBUG_STATE: + return sizeof(x86_debug_state_t); + case x86_THREAD_STATE64: + return sizeof(x86_thread_state64_t); + case x86_FLOAT_STATE64: + return sizeof(x86_float_state64_t); + case x86_DEBUG_STATE64: + return sizeof(x86_debug_state64_t); + default: + return 0; + } +#elif defined(ARCH_CPU_ARM64) + switch (flavor) { + case ARM_UNIFIED_THREAD_STATE: + return sizeof(arm_unified_thread_state_t); + case ARM_THREAD_STATE64: + return sizeof(arm_thread_state64_t); + case ARM_NEON_STATE64: + return sizeof(arm_neon_state64_t); + case ARM_DEBUG_STATE64: + return sizeof(arm_debug_state64_t); + default: + return 0; + } +#endif +} + +using Key = IntermediateDumpKey; + +ExceptionSnapshotIOSIntermediateDump::ExceptionSnapshotIOSIntermediateDump() + : ExceptionSnapshot(), + context_(), + codes_(), + thread_id_(0), + exception_address_(0), + exception_(0), + exception_info_(0), + initialized_() { +#if defined(ARCH_CPU_X86_64) + context_.architecture = kCPUArchitectureX86_64; + context_.x86_64 = &context_x86_64_; +#elif defined(ARCH_CPU_ARM64) + context_.architecture = kCPUArchitectureARM64; + context_.arm64 = &context_arm64_; +#else +#error Port to your CPU architecture +#endif +} + +ExceptionSnapshotIOSIntermediateDump::~ExceptionSnapshotIOSIntermediateDump() {} + +bool ExceptionSnapshotIOSIntermediateDump::InitializeFromSignal( + const IOSIntermediateDumpMap* exception_data) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + DCHECK(exception_data); + + if (!GetDataValueFromMap(exception_data, Key::kThreadID, &thread_id_)) { + LOG(ERROR) << "Exceptions require a thread id."; + return false; + } + +#if defined(ARCH_CPU_X86_64) + typedef x86_thread_state64_t thread_state_type; + typedef x86_float_state64_t float_state_type; +#elif defined(ARCH_CPU_ARM64) + typedef arm_thread_state64_t thread_state_type; + typedef arm_neon_state64_t float_state_type; +#endif + + thread_state_type thread_state; + float_state_type float_state; + if (GetDataValueFromMap(exception_data, Key::kThreadState, &thread_state) && + GetDataValueFromMap(exception_data, Key::kFloatState, &float_state)) { +#if defined(ARCH_CPU_X86_64) + x86_debug_state64_t empty_debug_state = {}; + InitializeCPUContextX86_64(&context_x86_64_, + THREAD_STATE_NONE, + nullptr, + 0, + &thread_state, + &float_state, + &empty_debug_state); +#elif defined(ARCH_CPU_ARM64) + arm_debug_state64_t empty_debug_state = {}; + InitializeCPUContextARM64(&context_arm64_, + THREAD_STATE_NONE, + nullptr, + 0, + &thread_state, + &float_state, + &empty_debug_state); +#else +#error Port to your CPU architecture +#endif + } + + exception_ = EXC_SOFT_SIGNAL; + GetDataValueFromMap(exception_data, Key::kSignalNumber, &exception_info_); + GetDataValueFromMap(exception_data, Key::kSignalAddress, &exception_address_); + + codes_.push_back(exception_); + codes_.push_back(exception_info_); + uint32_t code; + GetDataValueFromMap(exception_data, Key::kSignalCode, &code); + codes_.push_back(code); + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +bool ExceptionSnapshotIOSIntermediateDump::InitializeFromMachException( + const IOSIntermediateDumpMap* exception_data, + const IOSIntermediateDumpList* thread_list) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + DCHECK(exception_data); + + if (!GetDataValueFromMap(exception_data, Key::kThreadID, &thread_id_)) { + LOG(ERROR) << "Exceptions require a thread id."; + return false; + } + + exception_type_t exception; + if (GetDataValueFromMap(exception_data, Key::kException, &exception)) { + codes_.push_back(exception); + exception_ = exception; + } + + const IOSIntermediateDumpData* code_dump = + GetDataFromMap(exception_data, Key::kCodes); + if (code_dump) { + const std::vector& bytes = code_dump->bytes(); + const mach_exception_data_type_t* code = + reinterpret_cast(bytes.data()); + if (bytes.size() == 0 || !code) { + LOG(ERROR) << "Invalid mach exception code."; + } else { + // TODO: rationalize with the macOS implementation. + mach_msg_type_number_t code_count = + bytes.size() / sizeof(mach_exception_data_type_t); + for (mach_msg_type_number_t code_index = 0; code_index < code_count; + ++code_index) { + codes_.push_back(code[code_index]); + } + exception_info_ = code[0]; + exception_address_ = code[1]; + } + } + + if (thread_list) { + for (const auto& other_thread : *thread_list) { + uint64_t other_thread_id; + if (GetDataValueFromMap( + other_thread.get(), Key::kThreadID, &other_thread_id)) { + if (thread_id_ == other_thread_id) { + LoadContextFromThread(exception_data, other_thread.get()); + break; + } + } + } + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +bool ExceptionSnapshotIOSIntermediateDump::InitializeFromNSException( + const IOSIntermediateDumpMap* exception_data, + const IOSIntermediateDumpList* thread_list) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + DCHECK(exception_data); + + exception_ = kMachExceptionFromNSException; + + if (!GetDataValueFromMap(exception_data, Key::kThreadID, &thread_id_)) { + LOG(ERROR) << "Exceptions require a thread id."; + return false; + } + + if (thread_list) { + for (const auto& other_thread : *thread_list) { + uint64_t other_thread_id; + if (GetDataValueFromMap( + other_thread.get(), Key::kThreadID, &other_thread_id)) { + if (thread_id_ == other_thread_id) { + const IOSIntermediateDumpData* uncaught_exceptions = + other_thread->GetAsData(Key::kThreadUncaughtNSExceptionFrames); + if (uncaught_exceptions) { + LoadContextFromUncaughtNSExceptionFrames(uncaught_exceptions, + other_thread.get()); + } else { + LoadContextFromThread(exception_data, other_thread.get()); + } + break; + } + } + } + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +const CPUContext* ExceptionSnapshotIOSIntermediateDump::Context() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &context_; +} + +uint64_t ExceptionSnapshotIOSIntermediateDump::ThreadID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_id_; +} + +uint32_t ExceptionSnapshotIOSIntermediateDump::Exception() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_; +} + +uint32_t ExceptionSnapshotIOSIntermediateDump::ExceptionInfo() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_info_; +} + +uint64_t ExceptionSnapshotIOSIntermediateDump::ExceptionAddress() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_address_; +} + +const std::vector& ExceptionSnapshotIOSIntermediateDump::Codes() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return codes_; +} + +std::vector +ExceptionSnapshotIOSIntermediateDump::ExtraMemory() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector(); +} + +void ExceptionSnapshotIOSIntermediateDump::LoadContextFromThread( + const IOSIntermediateDumpMap* exception_data, + const IOSIntermediateDumpMap* other_thread) { +#if defined(ARCH_CPU_X86_64) + typedef x86_thread_state64_t thread_state_type; + typedef x86_float_state64_t float_state_type; + typedef x86_debug_state64_t debug_state_type; +#elif defined(ARCH_CPU_ARM64) + typedef arm_thread_state64_t thread_state_type; + typedef arm_neon_state64_t float_state_type; + typedef arm_debug_state64_t debug_state_type; +#endif + + thread_state_type thread_state; + float_state_type float_state; + debug_state_type debug_state; + + thread_state_flavor_t flavor = THREAD_STATE_NONE; + if (GetDataValueFromMap(exception_data, Key::kFlavor, &flavor) && + GetDataValueFromMap(other_thread, Key::kThreadState, &thread_state) && + GetDataValueFromMap(other_thread, Key::kFloatState, &float_state) && + GetDataValueFromMap(other_thread, Key::kDebugState, &debug_state)) { + const IOSIntermediateDumpData* state_dump = + GetDataFromMap(exception_data, Key::kState); + if (state_dump) { + std::vector bytes = state_dump->bytes(); + size_t actual_length = bytes.size(); + size_t expected_length = ThreadStateLengthForFlavor(flavor); + if (actual_length < expected_length) { + // Zero out bytes if actual_length is shorter than expected_length. + bytes.resize(expected_length, 0); + actual_length = bytes.size(); + LOG(WARNING) << "Exception context length " << actual_length + << " shorter than expected length " << expected_length; + } + const ConstThreadState state = + reinterpret_cast(bytes.data()); + // Tolerating actual_length longer than expected_length by setting + // state_count based on expected_length, not bytes.size(). + mach_msg_type_number_t state_count = expected_length / sizeof(uint32_t); +#if defined(ARCH_CPU_X86_64) + InitializeCPUContextX86_64(&context_x86_64_, + flavor, + state, + state_count, + &thread_state, + &float_state, + &debug_state); +#elif defined(ARCH_CPU_ARM64) + InitializeCPUContextARM64(&context_arm64_, + flavor, + state, + state_count, + &thread_state, + &float_state, + &debug_state); +#else +#error Port to your CPU architecture +#endif + } + } + + // Normally, for EXC_BAD_ACCESS exceptions, the exception address is present + // in code[1]. It may or may not be the instruction pointer address (usually + // it’s not). code[1] may carry the exception address for other exception + // types too, but it’s not guaranteed. But for all other exception types, the + // instruction pointer will be the exception address, and in fact will be + // equal to codes[1] when it’s carrying the exception address. In those cases, + // just use the instruction pointer directly. + bool code_1_is_exception_address = exception_ == EXC_BAD_ACCESS; + +#if defined(ARCH_CPU_X86_64) + // For x86 and x86_64 EXC_BAD_ACCESS exceptions, some code[0] values + // indicate that code[1] does not (or may not) carry the exception address: + // EXC_I386_GPFLT (10.9.5 xnu-2422.115.4/osfmk/i386/trap.c user_trap() for + // T_GENERAL_PROTECTION) and the oddball (VM_PROT_READ | VM_PROT_EXECUTE) + // which collides with EXC_I386_BOUNDFLT (10.9.5 + // xnu-2422.115.4/osfmk/i386/fpu.c fpextovrflt()). Other EXC_BAD_ACCESS + // exceptions come through 10.9.5 xnu-2422.115.4/osfmk/i386/trap.c + // user_page_fault_continue() and do contain the exception address in + // code[1]. + if (exception_ == EXC_BAD_ACCESS && + (exception_info_ == EXC_I386_GPFLT || + exception_info_ == (VM_PROT_READ | VM_PROT_EXECUTE))) { + code_1_is_exception_address = false; + } +#endif + + if (!code_1_is_exception_address) { + exception_address_ = context_.InstructionPointer(); + } +} + +void ExceptionSnapshotIOSIntermediateDump:: + LoadContextFromUncaughtNSExceptionFrames( + const IOSIntermediateDumpData* frames_dump, + const IOSIntermediateDumpMap* other_thread) { + const std::vector& bytes = frames_dump->bytes(); + const uint64_t* frames = reinterpret_cast(bytes.data()); + size_t num_frames = bytes.size() / sizeof(uint64_t); + if (num_frames < 2) { + return; + } + +#if defined(ARCH_CPU_X86_64) + context_x86_64_ = {}; + context_x86_64_.rip = frames[0]; // instruction pointer + context_x86_64_.rsp = frames[1]; +#elif defined(ARCH_CPU_ARM64) + context_arm64_ = {}; + context_arm64_.sp = 0; + context_arm64_.pc = frames[0]; + context_arm64_.regs[30] = frames[1]; // link register + context_arm64_.regs[29] = sizeof(uintptr_t); // function pointers +#else +#error Port to your CPU architecture +#endif + + exception_address_ = frames[0]; +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/ios/exception_snapshot_ios_intermediate_dump.h b/shared/sentry/external/crashpad/snapshot/ios/exception_snapshot_ios_intermediate_dump.h new file mode 100644 index 000000000..7e22330bc --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/ios/exception_snapshot_ios_intermediate_dump.h @@ -0,0 +1,115 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_EXCEPTION_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ +#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_EXCEPTION_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ + +#include +#include + +#include + +#include "build/build_config.h" +#include "snapshot/cpu_context.h" +#include "snapshot/exception_snapshot.h" +#include "util/ios/ios_intermediate_dump_map.h" +#include "util/mach/mach_extensions.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +namespace internal { + +//! \brief An ExceptionSnapshot of an exception sustained by a running (or +//! crashed) process on an iOS system. +class ExceptionSnapshotIOSIntermediateDump final : public ExceptionSnapshot { + public: + ExceptionSnapshotIOSIntermediateDump(); + + ExceptionSnapshotIOSIntermediateDump( + const ExceptionSnapshotIOSIntermediateDump&) = delete; + ExceptionSnapshotIOSIntermediateDump& operator=( + const ExceptionSnapshotIOSIntermediateDump&) = delete; + + ~ExceptionSnapshotIOSIntermediateDump() override; + + //! \brief Initialize the snapshot as a signal exception. + //! + //! \param[in] exception_data The intermediate dump map used to initialize + //! this object. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool InitializeFromSignal(const IOSIntermediateDumpMap* exception_data); + + //! \brief Initialize the object as a Mach exception from an intermediate + //! dump. + //! + //! \param[in] exception_data The intermediate dump map used to initialize + //! this object. + //! \param[in] thread_list The intermediate dump map containing list of + //! threads. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool InitializeFromMachException(const IOSIntermediateDumpMap* exception_data, + const IOSIntermediateDumpList* thread_list); + + //! \brief Initialize the object as an NSException from an intermediate dump. + //! + //! \param[in] exception_data The intermediate dump map used to initialize + //! this object. + //! \param[in] thread_list The intermediate dump map containing list of + //! threads. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool InitializeFromNSException(const IOSIntermediateDumpMap* exception_data, + const IOSIntermediateDumpList* thread_list); + + // ExceptionSnapshot: + const CPUContext* Context() const override; + uint64_t ThreadID() const override; + uint32_t Exception() const override; + uint32_t ExceptionInfo() const override; + uint64_t ExceptionAddress() const override; + const std::vector& Codes() const override; + virtual std::vector ExtraMemory() const override; + + private: + void LoadContextFromUncaughtNSExceptionFrames( + const IOSIntermediateDumpData* data, + const IOSIntermediateDumpMap* other_thread); + void LoadContextFromThread(const IOSIntermediateDumpMap* exception_data, + const IOSIntermediateDumpMap* other_thread); +#if defined(ARCH_CPU_X86_64) + CPUContextX86_64 context_x86_64_; +#elif defined(ARCH_CPU_ARM64) + CPUContextARM64 context_arm64_; +#else +#error Port. +#endif // ARCH_CPU_X86_64 + CPUContext context_; + std::vector codes_; + uint64_t thread_id_; + uintptr_t exception_address_; + uint32_t exception_; + uint32_t exception_info_; + InitializationStateDcheck initialized_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_EXCEPTION_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ diff --git a/shared/sentry/external/crashpad/snapshot/ios/intermediate_dump_reader_util.cc b/shared/sentry/external/crashpad/snapshot/ios/intermediate_dump_reader_util.cc new file mode 100644 index 000000000..74c0842ce --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/ios/intermediate_dump_reader_util.cc @@ -0,0 +1,93 @@ +// Copyright 2021 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/ios/intermediate_dump_reader_util.h" + +#include "util/ios/ios_intermediate_dump_data.h" +#include "util/ios/ios_intermediate_dump_map.h" +#include "util/misc/metrics.h" + +namespace crashpad { +namespace internal { + +std::ostream& operator<<(std::ostream& os, const IntermediateDumpKey& t) { + switch (t) { +#define X(Name, Value) \ + case IntermediateDumpKey::Name: \ + os << #Name; \ + break; + INTERMEDIATE_DUMP_KEYS(X) +#undef X + } + return os; +} + +const IOSIntermediateDumpData* GetDataFromMap( + const IOSIntermediateDumpMap* map, + const IntermediateDumpKey& key, + LogMissingDataValueFromMap logging) { + const IOSIntermediateDumpData* data = map->GetAsData(key); + if (!data) { + if (logging != LogMissingDataValueFromMap::kDontLogIfMissing) { + LOG(ERROR) << "Missing expected data for key " << key; + Metrics::MissingIntermediateDumpKey(key); + } + return nullptr; + } + return data; +} + +const IOSIntermediateDumpMap* GetMapFromMap(const IOSIntermediateDumpMap* map, + const IntermediateDumpKey& key) { + const IOSIntermediateDumpMap* map_dump = map->GetAsMap(key); + if (!map_dump) { + LOG(ERROR) << "Missing expected map for key " << key; + Metrics::MissingIntermediateDumpKey(key); + return nullptr; + } + return map_dump; +} + +const IOSIntermediateDumpList* GetListFromMap(const IOSIntermediateDumpMap* map, + const IntermediateDumpKey& key) { + const IOSIntermediateDumpList* list = map->GetAsList(key); + if (!list) { + LOG(ERROR) << "Missing expected list for key " << key; + Metrics::MissingIntermediateDumpKey(key); + return nullptr; + } + return list; +} + +bool GetDataStringFromMap(const IOSIntermediateDumpMap* map, + const IntermediateDumpKey& key, + std::string* value) { + const IOSIntermediateDumpData* data = GetDataFromMap(map, key); + if (!data) { + LOG(ERROR) << "Missing expected string for key " << key; + Metrics::MissingIntermediateDumpKey(key); + return false; + } + + *value = data->GetString(); + return true; +} + +void GetDataValueFromMapErrorInternal(const IntermediateDumpKey& key) { + LOG(ERROR) << "Invalid key size: " << key; + Metrics::InvalidIntermediateDumpKeySize(key); +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/ios/intermediate_dump_reader_util.h b/shared/sentry/external/crashpad/snapshot/ios/intermediate_dump_reader_util.h new file mode 100644 index 000000000..4d33d965a --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/ios/intermediate_dump_reader_util.h @@ -0,0 +1,116 @@ +// Copyright 2021 The Crashpad Authors. All rights reserved. +// +// 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 + +#include "base/logging.h" +#include "util/ios/ios_intermediate_dump_data.h" +#include "util/ios/ios_intermediate_dump_map.h" + +#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_READER_UTILS_H_ +#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_READER_UTILS_H_ + +namespace crashpad { + +namespace internal { + +//! \brief Overload the ostream output operator to make logged keys readable. +std::ostream& operator<<(std::ostream& os, const IntermediateDumpKey& t); + +//! \brief Determine if GetDataFromMap will log and report missing keys. +enum class LogMissingDataValueFromMap : bool { + //! \brief Do not log an error and report to UMA if a key is missing. + kDontLogIfMissing, + + //! \brief Log an error and report to UMA if a key is missing. + kLogIfMissing, +}; + +//! \brief Call GetAsData with error and UMA logging. +//! +//! \param[in] map The map to load from. +//! \param[in] key The key to load from \a map. +//! \param[in] logging This call will log missing keys unless \a logging is +//! LogDataValueFromMap::kDontLogIfMissing +//! +//! \return The IOSIntermediateDumpData pointer or a nullptr; +const IOSIntermediateDumpData* GetDataFromMap( + const IOSIntermediateDumpMap* map, + const IntermediateDumpKey& key, + LogMissingDataValueFromMap logging = + LogMissingDataValueFromMap::kLogIfMissing); + +//! \brief Call GetAsMap with error and UMA logging. +//! +//! \param[in] map The map to load from. +//! \param[in] key The key to load from \a map. +//! +//! \return The IOSIntermediateDumpMap pointer or a nullptr; +const IOSIntermediateDumpMap* GetMapFromMap(const IOSIntermediateDumpMap* map, + const IntermediateDumpKey& key); + +//! \brief Call GetAsList with error and UMA logging. +//! +//! \param[in] map The map to load from. +//! \param[in] key The key to load from \a map. +//! +//! \return The IOSIntermediateDumpList pointer or a nullptr; +const IOSIntermediateDumpList* GetListFromMap(const IOSIntermediateDumpMap* map, + const IntermediateDumpKey& key); + +//! \brief Call GetAsList with error and UMA logging. +//! +//! \param[in] map The map to load from. +//! \param[in] key The key to load from \a map. +//! +//! \return Returns `true` if the string could be loaded, otherwise returns +//! `false` and logs an error. +bool GetDataStringFromMap(const IOSIntermediateDumpMap* map, + const IntermediateDumpKey& key, + std::string* value); + +//! \brief Log key size error and record error with UMA. +void GetDataValueFromMapErrorInternal(const IntermediateDumpKey& key); + +//! \brief Call GetAsData and GetValue with error and UMA logging. +//! +//! \param[in] map The map to load from. +//! \param[in] key The key to load from \a map. +//! \param[out] value The data to populate. +//! \param[in] logging This call will log missing keys unless \a logging is +//! LogDataValueFromMap::kDontLogIfMissing. This call will always log +//! keys with an invalid size. +//! +//! \return On success, returns `true`, otherwise returns `false`. +template +bool GetDataValueFromMap(const IOSIntermediateDumpMap* map, + const IntermediateDumpKey& key, + T* value, + LogMissingDataValueFromMap logging = + LogMissingDataValueFromMap::kLogIfMissing) { + const IOSIntermediateDumpData* data = GetDataFromMap(map, key, logging); + if (!data) { + return false; + } + if (!data->GetValue(value)) { + GetDataValueFromMapErrorInternal(key); + return false; + } + return true; +} + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_READER_UTILS_H_ diff --git a/shared/sentry/external/crashpad/snapshot/ios/memory_snapshot_ios_intermediate_dump.cc b/shared/sentry/external/crashpad/snapshot/ios/memory_snapshot_ios_intermediate_dump.cc new file mode 100644 index 000000000..84275aa91 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/ios/memory_snapshot_ios_intermediate_dump.cc @@ -0,0 +1,88 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/ios/memory_snapshot_ios_intermediate_dump.h" + +namespace crashpad { +namespace internal { + +void MemorySnapshotIOSIntermediateDump::Initialize(vm_address_t address, + vm_address_t data, + vm_size_t size) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + address_ = address; + data_ = data; + size_ = base::checked_cast(size); + INITIALIZATION_STATE_SET_VALID(initialized_); +} + +uint64_t MemorySnapshotIOSIntermediateDump::Address() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return address_; +} + +size_t MemorySnapshotIOSIntermediateDump::Size() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return size_; +} + +bool MemorySnapshotIOSIntermediateDump::Read(Delegate* delegate) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (size_ == 0) { + return delegate->MemorySnapshotDelegateRead(nullptr, size_); + } + + return delegate->MemorySnapshotDelegateRead(reinterpret_cast(data_), + size_); +} + +const MemorySnapshot* MemorySnapshotIOSIntermediateDump::MergeWithOtherSnapshot( + const MemorySnapshot* other) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + auto other_snapshot = + reinterpret_cast(other); + + INITIALIZATION_STATE_DCHECK_VALID(other_snapshot->initialized_); + if (other_snapshot->address_ < address_) { + return other_snapshot->MergeWithOtherSnapshot(this); + } + + CheckedRange merged(0, 0); + if (!LoggingDetermineMergedRange(this, other, &merged)) + return nullptr; + + auto result = std::make_unique(); + result->Initialize(merged.base(), data_, merged.size()); + if (size_ == merged.size()) { + return result.release(); + } + + const uint8_t* data = reinterpret_cast(data_); + const uint8_t* other_data = + reinterpret_cast(other_snapshot->data_); + vm_size_t overlap = merged.size() - other_snapshot->size_; + result->merged_data_.reserve(merged.size()); + result->merged_data_.insert(result->merged_data_.end(), data, data + overlap); + result->merged_data_.insert(result->merged_data_.end(), + other_data, + other_data + other_snapshot->size_); + result->data_ = + reinterpret_cast(result->merged_data_.data()); + DCHECK_EQ(result->merged_data_.size(), merged.size()); + return result.release(); +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/ios/memory_snapshot_ios_intermediate_dump.h b/shared/sentry/external/crashpad/snapshot/ios/memory_snapshot_ios_intermediate_dump.h new file mode 100644 index 000000000..244bbd8be --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/ios/memory_snapshot_ios_intermediate_dump.h @@ -0,0 +1,73 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_MEMORY_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ +#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_MEMORY_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ + +#include + +#include "snapshot/memory_snapshot.h" +#include "util/misc/address_types.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { +namespace internal { + +//! \brief A MemorySnapshot of a memory region. +class MemorySnapshotIOSIntermediateDump final : public MemorySnapshot { + public: + MemorySnapshotIOSIntermediateDump() = default; + + MemorySnapshotIOSIntermediateDump(const MemorySnapshotIOSIntermediateDump&) = + delete; + MemorySnapshotIOSIntermediateDump& operator=( + const MemorySnapshotIOSIntermediateDump&) = delete; + + ~MemorySnapshotIOSIntermediateDump() = default; + + //! \brief Initializes the object. + //! + //! \param[in] address The base address of the memory region to snapshot. + //! \param[in] size The size of the memory region to snapshot. + void Initialize(vm_address_t address, vm_address_t data, vm_size_t size); + + // MemorySnapshot: + uint64_t Address() const override; + size_t Size() const override; + bool Read(Delegate* delegate) const override; + const MemorySnapshot* MergeWithOtherSnapshot( + const MemorySnapshot* other) const override; + + private: + template + friend const MemorySnapshot* MergeWithOtherSnapshotImpl( + const T* self, + const MemorySnapshot* other); + + vm_address_t address_; + vm_address_t data_; + + // Because the iOS snapshot memory region is owned by the intermediate dump, + // it's necessary to copy the merged data into a vector owned by the memory + // snapshot itself. + std::vector merged_data_; + + vm_size_t size_; + InitializationStateDcheck initialized_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_MEMORY_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ diff --git a/shared/sentry/external/crashpad/snapshot/ios/memory_snapshot_ios_intermediate_dump_test.cc b/shared/sentry/external/crashpad/snapshot/ios/memory_snapshot_ios_intermediate_dump_test.cc new file mode 100644 index 000000000..bbe2a88bd --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/ios/memory_snapshot_ios_intermediate_dump_test.cc @@ -0,0 +1,161 @@ +// Copyright 2022 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/ios/memory_snapshot_ios_intermediate_dump.h" + +#include +#include + +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +using internal::MemorySnapshotIOSIntermediateDump; + +const vm_address_t kDefaultAddress = 0x1000; + +class ReadToString : public crashpad::MemorySnapshot::Delegate { + public: + const std::string& result() { return result_; } + + private: + // MemorySnapshot::Delegate: + bool MemorySnapshotDelegateRead(void* data, size_t size) override { + result_ = std::string(reinterpret_cast(data), size); + return true; + } + + std::string result_; +}; + +std::unique_ptr CreateMemorySnapshot( + vm_address_t address, + std::vector& data) { + auto memory = std::make_unique(); + memory->Initialize( + address, reinterpret_cast(data.data()), data.size()); + return memory; +} + +TEST(MemorySnapshotIOSIntermediateDumpTest, MergeSame) { + std::vector data(10, 'a'); + auto memory = CreateMemorySnapshot(kDefaultAddress, data); + std::unique_ptr merged( + memory->MergeWithOtherSnapshot(memory.get())); + EXPECT_EQ(merged->Address(), kDefaultAddress); + EXPECT_EQ(merged->Size(), data.size()); + ReadToString delegate; + merged->Read(&delegate); + EXPECT_EQ(delegate.result(), "aaaaaaaaaa"); +} + +TEST(MemorySnapshotIOSIntermediateDumpTest, MergeNoOverlap) { + std::vector data1(10, 'a'); + auto memory1 = CreateMemorySnapshot(kDefaultAddress, data1); + + std::vector data2(10, 'b'); + auto memory2 = CreateMemorySnapshot(kDefaultAddress + 10, data2); + + std::unique_ptr merged( + memory1->MergeWithOtherSnapshot(memory2.get())); + EXPECT_EQ(merged->Address(), kDefaultAddress); + EXPECT_EQ(merged->Size(), 20u); + ReadToString delegate; + merged->Read(&delegate); + EXPECT_EQ(delegate.result(), "aaaaaaaaaabbbbbbbbbb"); +} + +TEST(MemorySnapshotIOSIntermediateDumpTest, MergePartial) { + std::vector data1(10, 'a'); + auto memory1 = CreateMemorySnapshot(kDefaultAddress, data1); + + std::vector data2(10, 'b'); + auto memory2 = CreateMemorySnapshot(kDefaultAddress + 5, data2); + + std::unique_ptr merged( + memory1->MergeWithOtherSnapshot(memory2.get())); + EXPECT_EQ(merged->Address(), kDefaultAddress); + EXPECT_EQ(merged->Size(), 15u); + ReadToString delegate; + merged->Read(&delegate); + EXPECT_EQ(delegate.result(), "aaaaabbbbbbbbbb"); +} + +TEST(MemorySnapshotIOSIntermediateDumpTest, NoMerge) { + std::vector data1(10, 'a'); + auto memory1 = CreateMemorySnapshot(kDefaultAddress, data1); + + std::vector data2(10, 'b'); + auto memory2 = CreateMemorySnapshot(kDefaultAddress + 20, data2); + + std::unique_ptr merged( + memory1->MergeWithOtherSnapshot(memory2.get())); + EXPECT_EQ(merged.get(), nullptr); +} + +TEST(MemorySnapshotIOSIntermediateDumpTest, EnvelopeBiggerFirst) { + std::vector data1(30, 'a'); + auto memory1 = CreateMemorySnapshot(kDefaultAddress, data1); + + std::vector data2(10, 'b'); + auto memory2 = CreateMemorySnapshot(kDefaultAddress + 15, data2); + + std::unique_ptr merged( + memory1->MergeWithOtherSnapshot(memory2.get())); + EXPECT_EQ(merged->Address(), kDefaultAddress); + EXPECT_EQ(merged->Size(), data1.size()); + + ReadToString delegate; + merged->Read(&delegate); + EXPECT_EQ(delegate.result(), "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); +} + +TEST(MemorySnapshotIOSIntermediateDumpTest, EnvelopeBiggerSecond) { + std::vector data1(10, 'a'); + auto memory1 = CreateMemorySnapshot(kDefaultAddress, data1); + + std::vector data2(20, 'b'); + auto memory2 = CreateMemorySnapshot(kDefaultAddress, data2); + + std::unique_ptr merged( + memory1->MergeWithOtherSnapshot(memory2.get())); + EXPECT_EQ(merged->Address(), kDefaultAddress); + EXPECT_EQ(merged->Size(), data2.size()); + + ReadToString delegate; + merged->Read(&delegate); + EXPECT_EQ(delegate.result(), "bbbbbbbbbbbbbbbbbbbb"); +} + +TEST(MemorySnapshotIOSIntermediateDumpTest, SmallerAddressSecond) { + std::vector data1(10, 'a'); + auto memory1 = CreateMemorySnapshot(kDefaultAddress, data1); + + std::vector data2(20, 'b'); + auto memory2 = CreateMemorySnapshot(kDefaultAddress - 10, data2); + + std::unique_ptr merged( + memory1->MergeWithOtherSnapshot(memory2.get())); + EXPECT_EQ(merged->Address(), kDefaultAddress - 10); + EXPECT_EQ(merged->Size(), data2.size()); + ReadToString delegate; + merged->Read(&delegate); + EXPECT_EQ(delegate.result(), "bbbbbbbbbbbbbbbbbbbb"); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/ios/module_snapshot_ios_intermediate_dump.cc b/shared/sentry/external/crashpad/snapshot/ios/module_snapshot_ios_intermediate_dump.cc new file mode 100644 index 000000000..cdef7697c --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/ios/module_snapshot_ios_intermediate_dump.cc @@ -0,0 +1,266 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/ios/module_snapshot_ios_intermediate_dump.h" + +#include +#include + +#include "base/files/file_path.h" +#include "base/mac/mach_logging.h" +#include "snapshot/ios/intermediate_dump_reader_util.h" +#include "util/ios/ios_intermediate_dump_data.h" +#include "util/ios/ios_intermediate_dump_list.h" +#include "util/misc/from_pointer_cast.h" +#include "util/misc/uuid.h" + +namespace crashpad { +namespace internal { + +using Key = IntermediateDumpKey; + +ModuleSnapshotIOSIntermediateDump::ModuleSnapshotIOSIntermediateDump() + : ModuleSnapshot(), + name_(), + address_(0), + size_(0), + timestamp_(0), + dylib_version_(0), + source_version_(0), + filetype_(0), + initialized_() {} + +ModuleSnapshotIOSIntermediateDump::~ModuleSnapshotIOSIntermediateDump() {} + +bool ModuleSnapshotIOSIntermediateDump::Initialize( + const IOSIntermediateDumpMap* image_data) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + GetDataStringFromMap(image_data, Key::kName, &name_); + GetDataValueFromMap(image_data, Key::kAddress, &address_); + GetDataValueFromMap(image_data, Key::kSize, &size_); + GetDataValueFromMap(image_data, Key::kSourceVersion, &source_version_); + GetDataValueFromMap(image_data, Key::kFileType, &filetype_); + + // These keys are often missing. + GetDataValueFromMap(image_data, + Key::kTimestamp, + ×tamp_, + LogMissingDataValueFromMap::kDontLogIfMissing); + GetDataValueFromMap(image_data, + Key::kDylibCurrentVersion, + &dylib_version_, + LogMissingDataValueFromMap::kDontLogIfMissing); + + const IOSIntermediateDumpData* uuid_dump = + GetDataFromMap(image_data, IntermediateDumpKey::kUUID); + if (uuid_dump) { + const std::vector& bytes = uuid_dump->bytes(); + if (!bytes.data() || bytes.size() != 16) { + LOG(ERROR) << "Invalid module uuid."; + } else { + uuid_.InitializeFromBytes(bytes.data()); + } + } + + const IOSIntermediateDumpList* annotation_list = + image_data->GetAsList(IntermediateDumpKey::kAnnotationObjects); + if (annotation_list) { + for (auto& annotation : *annotation_list) { + std::string name; + if (!GetDataStringFromMap( + annotation.get(), Key::kAnnotationName, &name) || + name.empty() || name.length() > 64) { // Annotation::kNameMaxLength + LOG(ERROR) << "Invalid annotation name length."; + continue; + } + + uint16_t type; + const IOSIntermediateDumpData* type_dump = + annotation->GetAsData(IntermediateDumpKey::kAnnotationType); + const IOSIntermediateDumpData* value_dump = + annotation->GetAsData(IntermediateDumpKey::kAnnotationValue); + if (type_dump && value_dump && type_dump->GetValue(&type)) { + const std::vector& bytes = value_dump->bytes(); + uint64_t length = bytes.size(); + if (!bytes.data() || length > 20480) { // Annotation::kValueMaxSize + LOG(ERROR) << "Invalid annotation value length."; + continue; + } + annotation_objects_.push_back(AnnotationSnapshot(name, type, bytes)); + } + } + } + + const IOSIntermediateDumpList* simple_map_dump = + image_data->GetAsList(IntermediateDumpKey::kAnnotationsSimpleMap); + if (simple_map_dump) { + for (auto& annotation : *simple_map_dump) { + const IOSIntermediateDumpData* name_dump = + annotation->GetAsData(IntermediateDumpKey::kAnnotationName); + const IOSIntermediateDumpData* value_dump = + annotation->GetAsData(IntermediateDumpKey::kAnnotationValue); + if (name_dump && value_dump) { + annotations_simple_map_.insert( + make_pair(name_dump->GetString(), value_dump->GetString())); + } + } + } + + const IOSIntermediateDumpMap* crash_info_dump = + image_data->GetAsMap(IntermediateDumpKey::kAnnotationsCrashInfo); + if (crash_info_dump) { + const IOSIntermediateDumpData* message1_dump = crash_info_dump->GetAsData( + IntermediateDumpKey::kAnnotationsCrashInfoMessage1); + if (message1_dump) { + std::string message1 = message1_dump->GetString(); + if (!message1.empty()) + annotations_vector_.push_back(message1); + } + const IOSIntermediateDumpData* message2_dump = crash_info_dump->GetAsData( + IntermediateDumpKey::kAnnotationsCrashInfoMessage2); + if (message2_dump) { + std::string message2 = message2_dump->GetString(); + if (!message2.empty()) + annotations_vector_.push_back(message2); + } + } + + const IOSIntermediateDumpData* dyld_error_dump = + image_data->GetAsData(IntermediateDumpKey::kAnnotationsDyldErrorString); + if (dyld_error_dump) { + std::string dyld_error_string = dyld_error_dump->GetString(); + if (!dyld_error_string.empty()) + annotations_vector_.push_back(dyld_error_string); + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +std::string ModuleSnapshotIOSIntermediateDump::Name() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return name_; +} + +uint64_t ModuleSnapshotIOSIntermediateDump::Address() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return address_; +} + +uint64_t ModuleSnapshotIOSIntermediateDump::Size() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return size_; +} + +time_t ModuleSnapshotIOSIntermediateDump::Timestamp() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return timestamp_; +} + +void ModuleSnapshotIOSIntermediateDump::FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (filetype_ == MH_DYLIB) { + *version_0 = (dylib_version_ & 0xffff0000) >> 16; + *version_1 = (dylib_version_ & 0x0000ff00) >> 8; + *version_2 = (dylib_version_ & 0x000000ff); + *version_3 = 0; + } else { + *version_0 = 0; + *version_1 = 0; + *version_2 = 0; + *version_3 = 0; + } +} + +void ModuleSnapshotIOSIntermediateDump::SourceVersion( + uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *version_0 = (source_version_ & 0xffff000000000000u) >> 48; + *version_1 = (source_version_ & 0x0000ffff00000000u) >> 32; + *version_2 = (source_version_ & 0x00000000ffff0000u) >> 16; + *version_3 = source_version_ & 0x000000000000ffffu; +} + +ModuleSnapshot::ModuleType ModuleSnapshotIOSIntermediateDump::GetModuleType() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + switch (filetype_) { + case MH_EXECUTE: + return kModuleTypeExecutable; + case MH_DYLIB: + return kModuleTypeSharedLibrary; + case MH_DYLINKER: + return kModuleTypeDynamicLoader; + case MH_BUNDLE: + return kModuleTypeLoadableModule; + default: + return kModuleTypeUnknown; + } +} + +void ModuleSnapshotIOSIntermediateDump::UUIDAndAge(crashpad::UUID* uuid, + uint32_t* age) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *uuid = uuid_; + *age = 0; +} + +std::string ModuleSnapshotIOSIntermediateDump::DebugFileName() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return base::FilePath(Name()).BaseName().value(); +} + +std::vector ModuleSnapshotIOSIntermediateDump::BuildID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector(); +} + +std::vector ModuleSnapshotIOSIntermediateDump::AnnotationsVector() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return annotations_vector_; +} + +std::map +ModuleSnapshotIOSIntermediateDump::AnnotationsSimpleMap() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return annotations_simple_map_; +} + +std::vector +ModuleSnapshotIOSIntermediateDump::AnnotationObjects() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return annotation_objects_; +} + +std::set> +ModuleSnapshotIOSIntermediateDump::ExtraMemoryRanges() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::set>(); +} + +std::vector +ModuleSnapshotIOSIntermediateDump::CustomMinidumpStreams() const { + return std::vector(); +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/ios/module_snapshot_ios_intermediate_dump.h b/shared/sentry/external/crashpad/snapshot/ios/module_snapshot_ios_intermediate_dump.h new file mode 100644 index 000000000..55b7ace88 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/ios/module_snapshot_ios_intermediate_dump.h @@ -0,0 +1,97 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_MODULE_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ +#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_MODULE_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ + +#include +#include +#include + +#include +#include +#include + +#include "snapshot/crashpad_info_client_options.h" +#include "snapshot/module_snapshot.h" +#include "util/ios/ios_intermediate_dump_map.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { +namespace internal { + +//! \brief A ModuleSnapshot of a code module (binary image) loaded into a +//! running (or crashed) process on an iOS system. +class ModuleSnapshotIOSIntermediateDump final : public ModuleSnapshot { + public: + ModuleSnapshotIOSIntermediateDump(); + + ModuleSnapshotIOSIntermediateDump(const ModuleSnapshotIOSIntermediateDump&) = + delete; + ModuleSnapshotIOSIntermediateDump& operator=( + const ModuleSnapshotIOSIntermediateDump&) = delete; + + ~ModuleSnapshotIOSIntermediateDump() override; + + //! \brief Initialize the snapshot + //! + //! \param[in] exception_data The intermediate dump map used to initialize + //! this object. + //! + //! \return `true` if the snapshot could be created. + bool Initialize(const IOSIntermediateDumpMap* image_data); + + // ModuleSnapshot: + std::string Name() const override; + uint64_t Address() const override; + uint64_t Size() const override; + time_t Timestamp() const override; + void FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const override; + void SourceVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const override; + ModuleType GetModuleType() const override; + void UUIDAndAge(UUID* uuid, uint32_t* age) const override; + std::string DebugFileName() const override; + std::vector BuildID() const override; + std::vector AnnotationsVector() const override; + std::map AnnotationsSimpleMap() const override; + std::vector AnnotationObjects() const override; + std::set> ExtraMemoryRanges() const override; + std::vector CustomMinidumpStreams() const override; + + private: + std::string name_; + uint64_t address_; + uint64_t size_; + time_t timestamp_; + uint32_t dylib_version_; + uint64_t source_version_; + uint32_t filetype_; + UUID uuid_; + std::vector annotations_vector_; + std::map annotations_simple_map_; + std::vector annotation_objects_; + + InitializationStateDcheck initialized_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_MODULE_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ diff --git a/shared/sentry/external/crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump.cc b/shared/sentry/external/crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump.cc new file mode 100644 index 000000000..9a63ea1d4 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump.cc @@ -0,0 +1,313 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/ios/process_snapshot_ios_intermediate_dump.h" + +#include + +#include "base/logging.h" +#include "snapshot/ios/intermediate_dump_reader_util.h" +#include "util/ios/ios_intermediate_dump_data.h" +#include "util/ios/ios_intermediate_dump_list.h" +#include "util/ios/ios_intermediate_dump_map.h" + +namespace { + +void MachTimeValueToTimeval(const time_value& mach, timeval* tv) { + tv->tv_sec = mach.seconds; + tv->tv_usec = mach.microseconds; +} + +} // namespace + +namespace crashpad { +namespace internal { + +using Key = internal::IntermediateDumpKey; + +bool ProcessSnapshotIOSIntermediateDump::InitializeWithFilePath( + const base::FilePath& dump_path, + const std::map& annotations) { + IOSIntermediateDumpFilePath dump_interface; + if (!dump_interface.Initialize(dump_path)) + return false; + + return InitializeWithFileInterface(dump_interface, annotations); +} + +bool ProcessSnapshotIOSIntermediateDump::InitializeWithFileInterface( + const IOSIntermediateDumpInterface& dump_interface, + const std::map& annotations) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + annotations_simple_map_ = annotations; + client_id_.InitializeToZero(); + + IOSIntermediateDumpReaderInitializeResult result = + reader_.Initialize(dump_interface); + if (result == IOSIntermediateDumpReaderInitializeResult::kFailure) { + return false; + } else if (result == IOSIntermediateDumpReaderInitializeResult::kIncomplete) { + annotations_simple_map_["crashpad_intermediate_dump_incomplete"] = "yes"; + } + + const IOSIntermediateDumpMap* root_map = reader_.RootMap(); + if (root_map->empty()) + return false; + + uint8_t version; + if (!GetDataValueFromMap(root_map, Key::kVersion, &version) || version != 1) { + LOG(ERROR) << "Root map version mismatch"; + return false; + } + + const internal::IOSIntermediateDumpMap* process_info = + GetMapFromMap(root_map, Key::kProcessInfo); + if (!process_info) { + LOG(ERROR) << "Process snapshot missing required process info map."; + return false; + } + + GetDataValueFromMap(process_info, Key::kPID, &p_pid_); + GetDataValueFromMap(process_info, Key::kParentPID, &e_ppid_); + GetDataValueFromMap(process_info, Key::kStartTime, &p_starttime_); + const IOSIntermediateDumpMap* basic_info = + process_info->GetAsMap(Key::kTaskBasicInfo); + if (basic_info) { + GetDataValueFromMap(basic_info, Key::kUserTime, &basic_info_user_time_); + GetDataValueFromMap(basic_info, Key::kSystemTime, &basic_info_system_time_); + } + + const IOSIntermediateDumpMap* thread_times = + process_info->GetAsMap(Key::kTaskThreadTimes); + if (thread_times) { + GetDataValueFromMap(thread_times, Key::kUserTime, &thread_times_user_time_); + GetDataValueFromMap( + thread_times, Key::kSystemTime, &thread_times_system_time_); + } + + GetDataValueFromMap(process_info, Key::kSnapshotTime, &snapshot_time_); + + const IOSIntermediateDumpList* simple_map_dump = + process_info->GetAsList(IntermediateDumpKey::kAnnotationsSimpleMap); + if (simple_map_dump) { + for (auto& annotation : *simple_map_dump) { + const IOSIntermediateDumpData* name_dump = + annotation->GetAsData(IntermediateDumpKey::kAnnotationName); + const IOSIntermediateDumpData* value_dump = + annotation->GetAsData(IntermediateDumpKey::kAnnotationValue); + if (name_dump && value_dump) { + annotations_simple_map_.insert( + make_pair(name_dump->GetString(), value_dump->GetString())); + } + } + } + + const IOSIntermediateDumpMap* system_info = + GetMapFromMap(root_map, Key::kSystemInfo); + if (!system_info) { + LOG(ERROR) << "Process snapshot missing required system info map."; + return false; + } + system_.Initialize(system_info); + + // Threads + const IOSIntermediateDumpList* thread_list = + GetListFromMap(root_map, Key::kThreads); + if (thread_list) { + for (const auto& value : *thread_list) { + auto thread = + std::make_unique(); + if (thread->Initialize(value.get())) { + threads_.push_back(std::move(thread)); + } + } + } + + const IOSIntermediateDumpList* module_list = + GetListFromMap(root_map, Key::kModules); + if (module_list) { + for (const auto& value : *module_list) { + auto module = + std::make_unique(); + if (module->Initialize(value.get())) { + modules_.push_back(std::move(module)); + } + } + } + + // Exceptions + const IOSIntermediateDumpMap* signal_exception = + root_map->GetAsMap(Key::kSignalException); + const IOSIntermediateDumpMap* mach_exception = + root_map->GetAsMap(Key::kMachException); + const IOSIntermediateDumpMap* ns_exception = + root_map->GetAsMap(Key::kNSException); + if (signal_exception) { + exception_.reset(new internal::ExceptionSnapshotIOSIntermediateDump()); + if (!exception_->InitializeFromSignal(signal_exception)) { + LOG(ERROR) << "Process snapshot could not initialize signal exception."; + return false; + } + } else if (mach_exception) { + exception_.reset(new internal::ExceptionSnapshotIOSIntermediateDump()); + if (!exception_->InitializeFromMachException( + mach_exception, GetListFromMap(root_map, Key::kThreads))) { + LOG(ERROR) << "Process snapshot could not initialize Mach exception."; + return false; + } + } else if (ns_exception) { + exception_.reset(new internal::ExceptionSnapshotIOSIntermediateDump()); + if (!exception_->InitializeFromNSException( + ns_exception, GetListFromMap(root_map, Key::kThreads))) { + LOG(ERROR) << "Process snapshot could not initialize NSException."; + return false; + } + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +void ProcessSnapshotIOSIntermediateDump::SetClientID(const UUID& client_id) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + client_id_ = client_id; +} + +void ProcessSnapshotIOSIntermediateDump::SetReportID(const UUID& report_id) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + report_id_ = report_id; +} + +pid_t ProcessSnapshotIOSIntermediateDump::ProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return p_pid_; +} + +pid_t ProcessSnapshotIOSIntermediateDump::ParentProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return e_ppid_; +} + +void ProcessSnapshotIOSIntermediateDump::SnapshotTime( + timeval* snapshot_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *snapshot_time = snapshot_time_; +} + +void ProcessSnapshotIOSIntermediateDump::ProcessStartTime( + timeval* start_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *start_time = p_starttime_; +} + +void ProcessSnapshotIOSIntermediateDump::ProcessCPUTimes( + timeval* user_time, + timeval* system_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + // Calculate user and system time the same way the kernel does for + // getrusage(). See 10.15.0 xnu-6153.11.26/bsd/kern/kern_resource.c calcru(). + timerclear(user_time); + timerclear(system_time); + + MachTimeValueToTimeval(basic_info_user_time_, user_time); + MachTimeValueToTimeval(basic_info_system_time_, system_time); + + timeval thread_user_time; + MachTimeValueToTimeval(thread_times_user_time_, &thread_user_time); + timeval thread_system_time; + MachTimeValueToTimeval(thread_times_system_time_, &thread_system_time); + + timeradd(user_time, &thread_user_time, user_time); + timeradd(system_time, &thread_system_time, system_time); +} + +void ProcessSnapshotIOSIntermediateDump::ReportID(UUID* report_id) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *report_id = report_id_; +} + +void ProcessSnapshotIOSIntermediateDump::ClientID(UUID* client_id) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *client_id = client_id_; +} + +const std::map& +ProcessSnapshotIOSIntermediateDump::AnnotationsSimpleMap() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return annotations_simple_map_; +} + +const SystemSnapshot* ProcessSnapshotIOSIntermediateDump::System() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &system_; +} + +std::vector ProcessSnapshotIOSIntermediateDump::Threads() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector threads; + for (const auto& thread : threads_) { + threads.push_back(thread.get()); + } + return threads; +} + +std::vector ProcessSnapshotIOSIntermediateDump::Modules() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector modules; + for (const auto& module : modules_) { + modules.push_back(module.get()); + } + return modules; +} + +std::vector +ProcessSnapshotIOSIntermediateDump::UnloadedModules() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector(); +} + +const ExceptionSnapshot* ProcessSnapshotIOSIntermediateDump::Exception() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_.get(); +} + +std::vector +ProcessSnapshotIOSIntermediateDump::MemoryMap() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector(); +} + +std::vector ProcessSnapshotIOSIntermediateDump::Handles() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector(); +} + +std::vector +ProcessSnapshotIOSIntermediateDump::ExtraMemory() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector(); +} + +const ProcessMemory* ProcessSnapshotIOSIntermediateDump::Memory() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return nullptr; +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump.h b/shared/sentry/external/crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump.h new file mode 100644 index 000000000..fb05cdaaa --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump.h @@ -0,0 +1,130 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_PROCESS_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ +#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_PROCESS_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ + +#include + +#include + +#include "base/files/file_path.h" +#include "snapshot/ios/exception_snapshot_ios_intermediate_dump.h" +#include "snapshot/ios/module_snapshot_ios_intermediate_dump.h" +#include "snapshot/ios/process_snapshot_ios_intermediate_dump.h" +#include "snapshot/ios/system_snapshot_ios_intermediate_dump.h" +#include "snapshot/ios/thread_snapshot_ios_intermediate_dump.h" +#include "snapshot/process_snapshot.h" +#include "snapshot/thread_snapshot.h" +#include "snapshot/unloaded_module_snapshot.h" +#include "util/ios/ios_intermediate_dump_reader.h" + +namespace crashpad { +namespace internal { + +//! \brief A ProcessSnapshot of a running (or crashed) process running on a +//! iphoneOS system. +class ProcessSnapshotIOSIntermediateDump final : public ProcessSnapshot { + public: + ProcessSnapshotIOSIntermediateDump() = default; + + ProcessSnapshotIOSIntermediateDump( + const ProcessSnapshotIOSIntermediateDump&) = delete; + ProcessSnapshotIOSIntermediateDump& operator=( + const ProcessSnapshotIOSIntermediateDump&) = delete; + + //! \brief Initializes the object. + //! + //! \param[in] dump_path The intermediate dump to read. + //! \param[in] annotations Process annotations to set in each crash report. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool InitializeWithFilePath( + const base::FilePath& dump_path, + const std::map& annotations); + + //! \brief Initializes the object. + //! + //! \param[in] dump_interface An interface corresponding to an intermediate + //! dump file. + //! \param[in] annotations Process annotations to set in each crash report. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool InitializeWithFileInterface( + const IOSIntermediateDumpInterface& dump_interface, + const std::map& annotations); + + //! On iOS, the client ID is under the control of the snapshot producer, + //! which may call this method to set the client ID. If this is not done, + //! ClientID() will return an identifier consisting entirely of zeroes. + void SetClientID(const UUID& client_id); + + //! \brief Sets the value to be returned by ReportID(). + //! + //! On iOS, the crash report ID is under the control of the snapshot + //! producer, which may call this method to set the report ID. If this is not + //! done, ReportID() will return an identifier consisting entirely of zeroes. + void SetReportID(const UUID& report_id); + + // ProcessSnapshot: + pid_t ProcessID() const override; + pid_t ParentProcessID() const override; + void SnapshotTime(timeval* snapshot_time) const override; + void ProcessStartTime(timeval* start_time) const override; + void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override; + void ReportID(UUID* report_id) const override; + void ClientID(UUID* client_id) const override; + const std::map& AnnotationsSimpleMap() + const override; + const SystemSnapshot* System() const override; + std::vector Threads() const override; + std::vector Modules() const override; + std::vector UnloadedModules() const override; + const ExceptionSnapshot* Exception() const override; + std::vector MemoryMap() const override; + std::vector Handles() const override; + std::vector ExtraMemory() const override; + const ProcessMemory* Memory() const override; + + private: + // Retain the reader for the lifetime of the ProcessSnapshot so large chunks + // of data do not need to be copied around (such as MemorySnapshot + // intermediate dumps). + IOSIntermediateDumpReader reader_; + pid_t p_pid_; + pid_t e_ppid_; + timeval p_starttime_; + time_value_t basic_info_user_time_; + time_value_t basic_info_system_time_; + time_value_t thread_times_user_time_; + time_value_t thread_times_system_time_; + internal::SystemSnapshotIOSIntermediateDump system_; + std::vector> + threads_; + std::vector> + modules_; + std::unique_ptr exception_; + UUID report_id_; + UUID client_id_; + std::map annotations_simple_map_; + timeval snapshot_time_; + InitializationStateDcheck initialized_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_PROCESS_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ diff --git a/shared/sentry/external/crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc b/shared/sentry/external/crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc new file mode 100644 index 000000000..5f56082eb --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/ios/process_snapshot_ios_intermediate_dump_test.cc @@ -0,0 +1,679 @@ +// Copyright 2021 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/ios/process_snapshot_ios_intermediate_dump.h" + +#include + +#include "base/cxx17_backports.h" +#include "base/files/scoped_file.h" +#include "base/posix/eintr_wrapper.h" +#include "build/build_config.h" +#include "client/annotation.h" +#include "gtest/gtest.h" +#include "minidump/minidump_file_writer.h" +#include "test/errors.h" +#include "test/scoped_temp_dir.h" +#include "test/test_paths.h" +#include "util/file/file_io.h" +#include "util/file/filesystem.h" +#include "util/file/string_file.h" +#include "util/misc/uuid.h" + +namespace crashpad { +namespace test { +namespace { + +using Key = internal::IntermediateDumpKey; +using internal::IOSIntermediateDumpWriter; +using internal::ProcessSnapshotIOSIntermediateDump; + +class ReadToString : public crashpad::MemorySnapshot::Delegate { + public: + std::string result; + + bool MemorySnapshotDelegateRead(void* data, size_t size) override { + result = std::string(reinterpret_cast(data), size); + return true; + } +}; + +class ProcessSnapshotIOSIntermediateDumpTest : public testing::Test { + protected: + // testing::Test: + + void SetUp() override { + path_ = temp_dir_.path().Append("dump_file"); + writer_ = std::make_unique(); + EXPECT_TRUE(writer_->Open(path_)); + ASSERT_TRUE(IsRegularFile(path_)); + } + + void TearDown() override { + writer_.reset(); + EXPECT_FALSE(IsRegularFile(path_)); + } + + const auto& path() const { return path_; } + const auto& annotations() const { return annotations_; } + auto writer() const { return writer_.get(); } + + bool DumpSnapshot(const ProcessSnapshotIOSIntermediateDump& snapshot) { + MinidumpFileWriter minidump; + minidump.InitializeFromSnapshot(&snapshot); + StringFile string_file; + return minidump.WriteEverything(&string_file); + } + + void WriteProcessInfo(IOSIntermediateDumpWriter* writer) { + IOSIntermediateDumpWriter::ScopedMap map(writer, Key::kProcessInfo); + pid_t pid = 2; + pid_t parent = 1; + EXPECT_TRUE(writer->AddProperty(Key::kPID, &pid)); + EXPECT_TRUE(writer->AddProperty(Key::kParentPID, &parent)); + timeval start_time = {12, 0}; + EXPECT_TRUE(writer->AddProperty(Key::kStartTime, &start_time)); + + time_value_t user_time = {20, 0}; + time_value_t system_time = {30, 0}; + { + IOSIntermediateDumpWriter::ScopedMap taskInfo(writer, + Key::kTaskBasicInfo); + EXPECT_TRUE(writer->AddProperty(Key::kUserTime, &user_time)); + EXPECT_TRUE(writer->AddProperty(Key::kSystemTime, &system_time)); + } + { + IOSIntermediateDumpWriter::ScopedMap taskThreadTimesMap( + writer, Key::kTaskThreadTimes); + writer->AddProperty(Key::kUserTime, &user_time); + writer->AddProperty(Key::kSystemTime, &system_time); + } + + timeval snapshot_time = {42, 0}; + writer->AddProperty(Key::kSnapshotTime, &snapshot_time); + } + + void WriteSystemInfo(IOSIntermediateDumpWriter* writer) { + IOSIntermediateDumpWriter::ScopedMap map(writer, Key::kSystemInfo); + std::string machine_description = "Gibson"; + EXPECT_TRUE(writer->AddProperty(Key::kMachineDescription, + machine_description.c_str(), + machine_description.length())); + int os_version_major = 1995; + int os_version_minor = 9; + int os_version_bugfix = 15; + EXPECT_TRUE(writer->AddProperty(Key::kOSVersionMajor, &os_version_major)); + EXPECT_TRUE(writer->AddProperty(Key::kOSVersionMinor, &os_version_minor)); + EXPECT_TRUE(writer->AddProperty(Key::kOSVersionBugfix, &os_version_bugfix)); + std::string os_version_build = "Da Vinci"; + writer->AddProperty(Key::kOSVersionBuild, + os_version_build.c_str(), + os_version_build.length()); + + int cpu_count = 1; + EXPECT_TRUE(writer->AddProperty(Key::kCpuCount, &cpu_count)); + std::string cpu_vendor = "RISC"; + EXPECT_TRUE(writer->AddProperty( + Key::kCpuVendor, cpu_vendor.c_str(), cpu_vendor.length())); + + bool has_daylight_saving_time = true; + EXPECT_TRUE(writer->AddProperty(Key::kHasDaylightSavingTime, + &has_daylight_saving_time)); + bool is_daylight_saving_time = true; + EXPECT_TRUE(writer->AddProperty(Key::kIsDaylightSavingTime, + &is_daylight_saving_time)); + int standard_offset_seconds = 7200; + EXPECT_TRUE(writer->AddProperty(Key::kStandardOffsetSeconds, + &standard_offset_seconds)); + int daylight_offset_seconds = 3600; + EXPECT_TRUE(writer->AddProperty(Key::kDaylightOffsetSeconds, + &daylight_offset_seconds)); + std::string standard_name = "Standard"; + EXPECT_TRUE(writer->AddProperty( + Key::kStandardName, standard_name.c_str(), standard_name.length())); + std::string daylight_name = "Daylight"; + EXPECT_TRUE(writer->AddProperty( + Key::kDaylightName, daylight_name.c_str(), daylight_name.length())); + + vm_size_t page_size = getpagesize(); + EXPECT_TRUE(writer->AddProperty(Key::kPageSize, &page_size)); + { + natural_t count = 0; + IOSIntermediateDumpWriter::ScopedMap vmStatMap(writer, Key::kVMStat); + EXPECT_TRUE(writer->AddProperty(Key::kActive, &count)); + EXPECT_TRUE(writer->AddProperty(Key::kInactive, &count)); + EXPECT_TRUE(writer->AddProperty(Key::kWired, &count)); + EXPECT_TRUE(writer->AddProperty(Key::kFree, &count)); + } + } + + void WriteAnnotations(IOSIntermediateDumpWriter* writer) { + constexpr char annotation_name[] = "annotation_name"; + constexpr char annotation_value[] = "annotation_value"; + { + IOSIntermediateDumpWriter::ScopedArray annotationObjectArray( + writer, Key::kAnnotationObjects); + { + IOSIntermediateDumpWriter::ScopedArrayMap annotationMap(writer); + EXPECT_TRUE(writer->AddPropertyBytes( + Key::kAnnotationName, annotation_name, strlen(annotation_name))); + EXPECT_TRUE(writer->AddPropertyBytes( + Key::kAnnotationValue, annotation_value, strlen(annotation_value))); + Annotation::Type type = Annotation::Type::kString; + EXPECT_TRUE(writer->AddProperty(Key::kAnnotationType, &type)); + } + } + { + IOSIntermediateDumpWriter::ScopedArray annotationsSimpleArray( + writer, Key::kAnnotationsSimpleMap); + { + IOSIntermediateDumpWriter::ScopedArrayMap annotationMap(writer); + EXPECT_TRUE(writer->AddPropertyBytes( + Key::kAnnotationName, annotation_name, strlen(annotation_name))); + EXPECT_TRUE(writer->AddPropertyBytes( + Key::kAnnotationValue, annotation_value, strlen(annotation_value))); + } + } + + IOSIntermediateDumpWriter::ScopedMap annotationMap( + writer, Key::kAnnotationsCrashInfo); + { + EXPECT_TRUE(writer->AddPropertyBytes(Key::kAnnotationsCrashInfoMessage1, + annotation_value, + strlen(annotation_value))); + EXPECT_TRUE(writer->AddPropertyBytes(Key::kAnnotationsCrashInfoMessage2, + annotation_value, + strlen(annotation_value))); + } + } + + void WriteModules(IOSIntermediateDumpWriter* writer) { + IOSIntermediateDumpWriter::ScopedArray moduleArray(writer, Key::kModules); + for (uint32_t image_index = 0; image_index < 2; ++image_index) { + IOSIntermediateDumpWriter::ScopedArrayMap modules(writer); + + constexpr char image_file[] = "/path/to/module"; + EXPECT_TRUE( + writer->AddProperty(Key::kName, image_file, strlen(image_file))); + + uint64_t address = 0; + uint64_t vmsize = 1; + uintptr_t imageFileModDate = 2; + uint32_t current_version = 3; + uint32_t filetype = MH_DYLIB; + uint64_t source_version = 5; + static constexpr uint8_t uuid[16] = {0x00, + 0x01, + 0x02, + 0x03, + 0x04, + 0x05, + 0x06, + 0x07, + 0x08, + 0x09, + 0x0a, + 0x0b, + 0x0c, + 0x0d, + 0x0e, + 0x0f}; + EXPECT_TRUE(writer->AddProperty(Key::kAddress, &address)); + EXPECT_TRUE(writer->AddProperty(Key::kSize, &vmsize)); + EXPECT_TRUE(writer->AddProperty(Key::kTimestamp, &imageFileModDate)); + EXPECT_TRUE( + writer->AddProperty(Key::kDylibCurrentVersion, ¤t_version)); + EXPECT_TRUE(writer->AddProperty(Key::kSourceVersion, &source_version)); + EXPECT_TRUE(writer->AddProperty(Key::kUUID, &uuid)); + EXPECT_TRUE(writer->AddProperty(Key::kFileType, &filetype)); + WriteAnnotations(writer); + } + } + + void ExpectModules(const std::vector& modules) { + for (auto module : modules) { + EXPECT_EQ(module->GetModuleType(), + ModuleSnapshot::kModuleTypeSharedLibrary); + EXPECT_STREQ(module->Name().c_str(), "/path/to/module"); + EXPECT_STREQ(module->DebugFileName().c_str(), "module"); + UUID uuid; + uint32_t age; + module->UUIDAndAge(&uuid, &age); + EXPECT_EQ(uuid.ToString(), "00010203-0405-0607-0809-0a0b0c0d0e0f"); + + for (auto annotation : module->AnnotationsVector()) { + EXPECT_STREQ(annotation.c_str(), "annotation_value"); + } + + for (const auto& it : module->AnnotationsSimpleMap()) { + EXPECT_STREQ(it.first.c_str(), "annotation_name"); + EXPECT_STREQ(it.second.c_str(), "annotation_value"); + } + + for (auto annotation_object : module->AnnotationObjects()) { + EXPECT_STREQ(annotation_object.name.c_str(), "annotation_name"); + EXPECT_EQ(annotation_object.type, (short)Annotation::Type::kString); + EXPECT_STREQ(std::string(reinterpret_cast( + annotation_object.value.data()), + annotation_object.value.size()) + .c_str(), + "annotation_value"); + } + } + } + + void WriteMachException(IOSIntermediateDumpWriter* writer, + bool short_context = false) { + IOSIntermediateDumpWriter::ScopedMap machExceptionMap(writer, + Key::kMachException); + exception_type_t exception = 5; + mach_exception_data_type_t code[] = {4, 3}; + mach_msg_type_number_t code_count = 2; + +#if defined(ARCH_CPU_X86_64) + thread_state_flavor_t flavor = x86_THREAD_STATE; + x86_thread_state_t state = {}; + state.tsh.flavor = x86_THREAD_STATE64; + state.tsh.count = x86_THREAD_STATE64_COUNT; + state.uts.ts64.__rip = 0xdeadbeef; + size_t state_length = sizeof(x86_thread_state_t); +#elif defined(ARCH_CPU_ARM64) + thread_state_flavor_t flavor = ARM_UNIFIED_THREAD_STATE; + arm_unified_thread_state_t state = {}; + state.ash.flavor = ARM_THREAD_STATE64; + state.ash.count = ARM_THREAD_STATE64_COUNT; + state.ts_64.__pc = 0xdeadbeef; + size_t state_length = sizeof(arm_unified_thread_state_t); +#endif + EXPECT_TRUE(writer->AddProperty(Key::kException, &exception)); + EXPECT_TRUE(writer->AddProperty(Key::kCodes, code, code_count)); + EXPECT_TRUE(writer->AddProperty(Key::kFlavor, &flavor)); + + if (short_context) { + state_length -= 10; + } + EXPECT_TRUE(writer->AddPropertyBytes( + Key::kState, reinterpret_cast(&state), state_length)); + uint64_t thread_id = 1; + EXPECT_TRUE(writer->AddProperty(Key::kThreadID, &thread_id)); + } + + void WriteThreads(IOSIntermediateDumpWriter* writer) { + vm_address_t stack_region_address = 0; + IOSIntermediateDumpWriter::ScopedArray threadArray(writer, Key::kThreads); + for (uint64_t thread_id = 1; thread_id < 3; thread_id++) { + IOSIntermediateDumpWriter::ScopedArrayMap threadMap(writer); + EXPECT_TRUE(writer->AddProperty(Key::kThreadID, &thread_id)); + + integer_t suspend_count = 666; + integer_t importance = 5; + uint64_t thread_handle = thread_id; + EXPECT_TRUE(writer->AddProperty(Key::kSuspendCount, &suspend_count)); + EXPECT_TRUE(writer->AddProperty(Key::kPriority, &importance)); + EXPECT_TRUE(writer->AddProperty(Key::kThreadDataAddress, &thread_handle)); + +#if defined(ARCH_CPU_X86_64) + x86_thread_state64_t thread_state = {}; + thread_state.__rip = 0xdeadbeef; + x86_float_state64_t float_state = {}; + x86_debug_state64_t debug_state = {}; +#elif defined(ARCH_CPU_ARM64) + arm_thread_state64_t thread_state = {}; + thread_state.__pc = 0xdeadbeef; + arm_neon_state64_t float_state = {}; + arm_debug_state64_t debug_state = {}; +#endif + EXPECT_TRUE(writer->AddProperty(Key::kThreadState, &thread_state)); + EXPECT_TRUE(writer->AddProperty(Key::kFloatState, &float_state)); + EXPECT_TRUE(writer->AddProperty(Key::kDebugState, &debug_state)); + + // Non-overlapping stack_region_address. + stack_region_address += 10; + EXPECT_TRUE( + writer->AddProperty(Key::kStackRegionAddress, &stack_region_address)); + EXPECT_TRUE( + writer->AddPropertyBytes(Key::kStackRegionData, "stack_data", 10)); + { + IOSIntermediateDumpWriter::ScopedArray memoryRegions( + writer, Key::kThreadContextMemoryRegions); + { + IOSIntermediateDumpWriter::ScopedArrayMap memoryRegion(writer); + const vm_address_t memory_region_address = 0; + EXPECT_TRUE(writer->AddProperty( + Key::kThreadContextMemoryRegionAddress, &memory_region_address)); + EXPECT_TRUE(writer->AddPropertyBytes( + Key::kThreadContextMemoryRegionData, "string", 6)); + } + } + } + } + + void ExpectMachException(const ExceptionSnapshot& exception) { + EXPECT_EQ(exception.ThreadID(), 1u); + EXPECT_EQ(exception.Exception(), 5u); + EXPECT_TRUE(exception.Context()->Is64Bit()); + EXPECT_EQ(exception.Context()->InstructionPointer(), 0xdeadbeef); + EXPECT_EQ(exception.ExceptionInfo(), 4u); + EXPECT_EQ(exception.ExceptionAddress(), 0xdeadbeef); + EXPECT_EQ(exception.Codes()[0], 5u); + EXPECT_EQ(exception.Codes()[1], 4u); + EXPECT_EQ(exception.Codes()[2], 3u); + } + + void ExpectThreads(const std::vector& threads) { + uint64_t thread_id = 1; + for (auto thread : threads) { + EXPECT_EQ(thread->ThreadID(), thread_id); + EXPECT_EQ(thread->SuspendCount(), 666); + EXPECT_EQ(thread->Priority(), 5); + EXPECT_EQ(thread->ThreadSpecificDataAddress(), thread_id++); + ReadToString delegate; + for (auto memory : thread->ExtraMemory()) { + memory->Read(&delegate); + EXPECT_STREQ(delegate.result.c_str(), "string"); + } + + thread->Stack()->Read(&delegate); + EXPECT_STREQ(delegate.result.c_str(), "stack_data"); + + EXPECT_TRUE(thread->Context()->Is64Bit()); + EXPECT_EQ(thread->Context()->InstructionPointer(), 0xdeadbeef); + } + } + + void ExpectSystem(const SystemSnapshot& system) { + EXPECT_EQ(system.CPUCount(), 1u); + EXPECT_STREQ(system.CPUVendor().c_str(), "RISC"); + int major; + int minor; + int bugfix; + std::string build; + system.OSVersion(&major, &minor, &bugfix, &build); + EXPECT_EQ(major, 1995); + EXPECT_EQ(minor, 9); + EXPECT_EQ(bugfix, 15); + EXPECT_STREQ(build.c_str(), "Da Vinci"); + EXPECT_STREQ(system.OSVersionFull().c_str(), "1995.9.15 Da Vinci"); + EXPECT_STREQ(system.MachineDescription().c_str(), "Gibson"); + + SystemSnapshot::DaylightSavingTimeStatus dst_status; + int standard_offset_seconds; + int daylight_offset_seconds; + std::string standard_name; + std::string daylight_name; + + system.TimeZone(&dst_status, + &standard_offset_seconds, + &daylight_offset_seconds, + &standard_name, + &daylight_name); + EXPECT_EQ(standard_offset_seconds, 7200); + EXPECT_EQ(daylight_offset_seconds, 3600); + EXPECT_STREQ(standard_name.c_str(), "Standard"); + EXPECT_STREQ(daylight_name.c_str(), "Daylight"); + } + + void ExpectSnapshot(const ProcessSnapshot& snapshot) { + EXPECT_EQ(snapshot.ProcessID(), 2); + EXPECT_EQ(snapshot.ParentProcessID(), 1); + + timeval snapshot_time; + snapshot.SnapshotTime(&snapshot_time); + EXPECT_EQ(snapshot_time.tv_sec, 42); + EXPECT_EQ(snapshot_time.tv_usec, 0); + + timeval start_time; + snapshot.ProcessStartTime(&start_time); + EXPECT_EQ(start_time.tv_sec, 12); + EXPECT_EQ(start_time.tv_usec, 0); + + timeval user_time, system_time; + snapshot.ProcessCPUTimes(&user_time, &system_time); + EXPECT_EQ(user_time.tv_sec, 40); + EXPECT_EQ(user_time.tv_usec, 0); + EXPECT_EQ(system_time.tv_sec, 60); + EXPECT_EQ(system_time.tv_usec, 0); + + ExpectSystem(*snapshot.System()); + ExpectThreads(snapshot.Threads()); + ExpectModules(snapshot.Modules()); + ExpectMachException(*snapshot.Exception()); + } + + private: + std::unique_ptr writer_; + ScopedTempDir temp_dir_; + base::FilePath path_; + std::map annotations_; +}; + +TEST_F(ProcessSnapshotIOSIntermediateDumpTest, InitializeNoFile) { + const base::FilePath file; + ProcessSnapshotIOSIntermediateDump process_snapshot; + EXPECT_FALSE(process_snapshot.InitializeWithFilePath(file, annotations())); + EXPECT_TRUE(LoggingRemoveFile(path())); + EXPECT_FALSE(IsRegularFile(path())); +} + +TEST_F(ProcessSnapshotIOSIntermediateDumpTest, InitializeEmpty) { + ProcessSnapshotIOSIntermediateDump process_snapshot; + EXPECT_FALSE(process_snapshot.InitializeWithFilePath(path(), annotations())); + EXPECT_FALSE(IsRegularFile(path())); +} + +TEST_F(ProcessSnapshotIOSIntermediateDumpTest, InitializeMinimumDump) { + { + IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer()); + uint8_t version = 1; + EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version)); + { IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kSystemInfo); } + { IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kProcessInfo); } + } + ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), annotations())); + EXPECT_FALSE(IsRegularFile(path())); + EXPECT_TRUE(DumpSnapshot(process_snapshot)); +} + +TEST_F(ProcessSnapshotIOSIntermediateDumpTest, MissingSystemDump) { + { + IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer()); + uint8_t version = 1; + EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version)); + { IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kProcessInfo); } + } + ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_FALSE(process_snapshot.InitializeWithFilePath(path(), annotations())); + EXPECT_FALSE(IsRegularFile(path())); +} + +TEST_F(ProcessSnapshotIOSIntermediateDumpTest, MissingProcessDump) { + { + IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer()); + uint8_t version = 1; + EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version)); + { IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kSystemInfo); } + } + ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_FALSE(process_snapshot.InitializeWithFilePath(path(), annotations())); + EXPECT_FALSE(IsRegularFile(path())); +} + +TEST_F(ProcessSnapshotIOSIntermediateDumpTest, EmptySignalDump) { + { + IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer()); + uint8_t version = 1; + EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version)); + WriteSystemInfo(writer()); + WriteProcessInfo(writer()); + { + IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kSignalException); + uint64_t thread_id = 1; + EXPECT_TRUE(writer()->AddProperty(Key::kThreadID, &thread_id)); + } + { + IOSIntermediateDumpWriter::ScopedArray threadArray(writer(), + Key::kThreads); + IOSIntermediateDumpWriter::ScopedArrayMap threadMap(writer()); + uint64_t thread_id = 1; + writer()->AddProperty(Key::kThreadID, &thread_id); + } + } + ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), annotations())); + EXPECT_FALSE(IsRegularFile(path())); + EXPECT_TRUE(DumpSnapshot(process_snapshot)); +} + +TEST_F(ProcessSnapshotIOSIntermediateDumpTest, EmptyMachDump) { + { + IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer()); + uint8_t version = 1; + EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version)); + WriteSystemInfo(writer()); + WriteProcessInfo(writer()); + { + IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kMachException); + uint64_t thread_id = 1; + EXPECT_TRUE(writer()->AddProperty(Key::kThreadID, &thread_id)); + } + { + IOSIntermediateDumpWriter::ScopedArray threadArray(writer(), + Key::kThreads); + IOSIntermediateDumpWriter::ScopedArrayMap threadMap(writer()); + uint64_t thread_id = 1; + writer()->AddProperty(Key::kThreadID, &thread_id); + } + } + ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), annotations())); + EXPECT_FALSE(IsRegularFile(path())); + EXPECT_TRUE(DumpSnapshot(process_snapshot)); +} + +TEST_F(ProcessSnapshotIOSIntermediateDumpTest, EmptyExceptionDump) { + { + IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer()); + uint8_t version = 1; + EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version)); + WriteSystemInfo(writer()); + WriteProcessInfo(writer()); + { + IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kNSException); + uint64_t thread_id = 1; + EXPECT_TRUE(writer()->AddProperty(Key::kThreadID, &thread_id)); + } + { + IOSIntermediateDumpWriter::ScopedArray threadArray(writer(), + Key::kThreads); + IOSIntermediateDumpWriter::ScopedArrayMap threadMap(writer()); + uint64_t thread_id = 1; + writer()->AddProperty(Key::kThreadID, &thread_id); + } + } + ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), annotations())); + EXPECT_FALSE(IsRegularFile(path())); + EXPECT_TRUE(DumpSnapshot(process_snapshot)); +} + +TEST_F(ProcessSnapshotIOSIntermediateDumpTest, EmptyUncaughtNSExceptionDump) { + { + IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer()); + uint8_t version = 1; + EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version)); + WriteSystemInfo(writer()); + WriteProcessInfo(writer()); + { + IOSIntermediateDumpWriter::ScopedMap map(writer(), Key::kNSException); + uint64_t thread_id = 1; + EXPECT_TRUE(writer()->AddProperty(Key::kThreadID, &thread_id)); + } + { + IOSIntermediateDumpWriter::ScopedArray threadArray(writer(), + Key::kThreads); + IOSIntermediateDumpWriter::ScopedArrayMap threadMap(writer()); + uint64_t thread_id = 1; + writer()->AddProperty(Key::kThreadID, &thread_id); + const uint64_t frames[] = {0, 0}; + const size_t num_frames = 2; + writer()->AddProperty( + Key::kThreadUncaughtNSExceptionFrames, frames, num_frames); + } + } + ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), annotations())); + EXPECT_FALSE(IsRegularFile(path())); + EXPECT_TRUE(DumpSnapshot(process_snapshot)); +} + +TEST_F(ProcessSnapshotIOSIntermediateDumpTest, ShortContext) { + { + IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer()); + uint8_t version = 1; + EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version)); + WriteSystemInfo(writer()); + WriteProcessInfo(writer()); + WriteThreads(writer()); + WriteModules(writer()); + WriteMachException(writer(), true /* short_context=true*/); + } + ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), annotations())); + EXPECT_FALSE(IsRegularFile(path())); + EXPECT_TRUE(DumpSnapshot(process_snapshot)); + ExpectSnapshot(process_snapshot); +} + +TEST_F(ProcessSnapshotIOSIntermediateDumpTest, FullReport) { + { + IOSIntermediateDumpWriter::ScopedRootMap rootMap(writer()); + uint8_t version = 1; + EXPECT_TRUE(writer()->AddProperty(Key::kVersion, &version)); + WriteSystemInfo(writer()); + WriteProcessInfo(writer()); + WriteThreads(writer()); + WriteModules(writer()); + WriteMachException(writer()); + } + ProcessSnapshotIOSIntermediateDump process_snapshot; + ASSERT_TRUE(process_snapshot.InitializeWithFilePath(path(), annotations())); + EXPECT_FALSE(IsRegularFile(path())); + EXPECT_TRUE(DumpSnapshot(process_snapshot)); + ExpectSnapshot(process_snapshot); +} + +TEST_F(ProcessSnapshotIOSIntermediateDumpTest, FuzzTestCases) { + base::FilePath fuzz_path = TestPaths::TestDataRoot().Append(FILE_PATH_LITERAL( + "snapshot/ios/testdata/crash-1fa088dda0adb41459d063078a0f384a0bb8eefa")); + crashpad::internal::ProcessSnapshotIOSIntermediateDump process_snapshot; + EXPECT_TRUE(process_snapshot.InitializeWithFilePath(fuzz_path, {})); + EXPECT_TRUE(LoggingRemoveFile(path())); + + auto map = process_snapshot.AnnotationsSimpleMap(); + ASSERT_TRUE(map.find("crashpad_intermediate_dump_incomplete") != map.end()); + EXPECT_EQ(map["crashpad_intermediate_dump_incomplete"], "yes"); + + fuzz_path = TestPaths::TestDataRoot().Append( + FILE_PATH_LITERAL("snapshot/ios/testdata/crash-5726011582644224")); + crashpad::internal::ProcessSnapshotIOSIntermediateDump process_snapshot2; + EXPECT_TRUE(process_snapshot2.InitializeWithFilePath(fuzz_path, {})); + map = process_snapshot2.AnnotationsSimpleMap(); + ASSERT_TRUE(map.find("crashpad_intermediate_dump_incomplete") != map.end()); + EXPECT_EQ(map["crashpad_intermediate_dump_incomplete"], "yes"); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/ios/system_snapshot_ios_intermediate_dump.cc b/shared/sentry/external/crashpad/snapshot/ios/system_snapshot_ios_intermediate_dump.cc new file mode 100644 index 000000000..d10e9c0a0 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/ios/system_snapshot_ios_intermediate_dump.cc @@ -0,0 +1,245 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/ios/system_snapshot_ios_intermediate_dump.h" + +#include +#include +#include +#include +#include + +#include + +#include "base/logging.h" +#include "base/mac/mach_logging.h" +#include "base/strings/stringprintf.h" +#include "build/build_config.h" +#include "snapshot/cpu_context.h" +#include "snapshot/ios/intermediate_dump_reader_util.h" +#include "snapshot/posix/timezone.h" +#include "util/ios/ios_intermediate_dump_data.h" +#include "util/mac/mac_util.h" +#include "util/numeric/in_range_cast.h" + +namespace crashpad { + +namespace internal { + +using Key = IntermediateDumpKey; + +SystemSnapshotIOSIntermediateDump::SystemSnapshotIOSIntermediateDump() + : SystemSnapshot(), + os_version_build_(), + machine_description_(), + os_version_major_(0), + os_version_minor_(0), + os_version_bugfix_(0), + active_(0), + inactive_(0), + wired_(0), + free_(0), + cpu_count_(0), + cpu_vendor_(), + dst_status_(), + standard_offset_seconds_(0), + daylight_offset_seconds_(0), + standard_name_(), + daylight_name_(), + initialized_() {} + +SystemSnapshotIOSIntermediateDump::~SystemSnapshotIOSIntermediateDump() {} + +void SystemSnapshotIOSIntermediateDump::Initialize( + const IOSIntermediateDumpMap* system_data) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + GetDataStringFromMap(system_data, Key::kOSVersionBuild, &os_version_build_); + GetDataStringFromMap( + system_data, Key::kMachineDescription, &machine_description_); + GetDataStringFromMap(system_data, Key::kCpuVendor, &cpu_vendor_); + GetDataStringFromMap(system_data, Key::kStandardName, &standard_name_); + GetDataStringFromMap(system_data, Key::kDaylightName, &daylight_name_); + + GetDataValueFromMap(system_data, Key::kOSVersionMajor, &os_version_major_); + GetDataValueFromMap(system_data, Key::kOSVersionMinor, &os_version_minor_); + GetDataValueFromMap(system_data, Key::kOSVersionBugfix, &os_version_bugfix_); + GetDataValueFromMap(system_data, Key::kCpuCount, &cpu_count_); + + GetDataValueFromMap( + system_data, Key::kStandardOffsetSeconds, &standard_offset_seconds_); + GetDataValueFromMap( + system_data, Key::kDaylightOffsetSeconds, &daylight_offset_seconds_); + + bool has_daylight_saving_time; + GetDataValueFromMap( + system_data, Key::kHasDaylightSavingTime, &has_daylight_saving_time); + bool is_daylight_saving_time; + GetDataValueFromMap( + system_data, Key::kIsDaylightSavingTime, &is_daylight_saving_time); + + if (has_daylight_saving_time) { + dst_status_ = is_daylight_saving_time + ? SystemSnapshot::kObservingDaylightSavingTime + : SystemSnapshot::kObservingStandardTime; + } else { + dst_status_ = SystemSnapshot::kDoesNotObserveDaylightSavingTime; + } + + vm_size_t page_size; + if (GetDataValueFromMap(system_data, Key::kPageSize, &page_size)) { + const IOSIntermediateDumpMap* vm_stat = + GetMapFromMap(system_data, Key::kVMStat); + if (vm_stat) { + GetDataValueFromMap(vm_stat, Key::kActive, &active_); + active_ *= page_size; + + GetDataValueFromMap(vm_stat, Key::kInactive, &inactive_); + inactive_ *= page_size; + + GetDataValueFromMap(vm_stat, Key::kWired, &wired_); + wired_ *= page_size; + + GetDataValueFromMap(vm_stat, Key::kFree, &free_); + free_ *= page_size; + } + } + + INITIALIZATION_STATE_SET_VALID(initialized_); +} + +CPUArchitecture SystemSnapshotIOSIntermediateDump::GetCPUArchitecture() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if defined(ARCH_CPU_X86_64) + return kCPUArchitectureX86_64; +#elif defined(ARCH_CPU_ARM64) + return kCPUArchitectureARM64; +#endif +} + +uint32_t SystemSnapshotIOSIntermediateDump::CPURevision() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(justincohen): sysctlbyname machdep.cpu.* returns -1 on iOS/ARM64, but + // consider recording this for X86_64 only. + return 0; +} + +uint8_t SystemSnapshotIOSIntermediateDump::CPUCount() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return cpu_count_; +} + +std::string SystemSnapshotIOSIntermediateDump::CPUVendor() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return cpu_vendor_; +} + +void SystemSnapshotIOSIntermediateDump::CPUFrequency(uint64_t* current_hz, + uint64_t* max_hz) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(justincohen): sysctlbyname hw.cpufrequency returns -1 on iOS/ARM64, + // but consider recording this for X86_64 only. + *current_hz = 0; + *max_hz = 0; +} + +uint32_t SystemSnapshotIOSIntermediateDump::CPUX86Signature() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(justincohen): Consider recording this for X86_64 only. + return 0; +} + +uint64_t SystemSnapshotIOSIntermediateDump::CPUX86Features() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(justincohen): Consider recording this for X86_64 only. + return 0; +} + +uint64_t SystemSnapshotIOSIntermediateDump::CPUX86ExtendedFeatures() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(justincohen): Consider recording this for X86_64 only. + return 0; +} + +uint32_t SystemSnapshotIOSIntermediateDump::CPUX86Leaf7Features() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(justincohen): Consider recording this for X86_64 only. + return 0; +} + +bool SystemSnapshotIOSIntermediateDump::CPUX86SupportsDAZ() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(justincohen): Consider recording this for X86_64 only. + return false; +} + +SystemSnapshot::OperatingSystem +SystemSnapshotIOSIntermediateDump::GetOperatingSystem() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return kOperatingSystemIOS; +} + +bool SystemSnapshotIOSIntermediateDump::OSServer() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return false; +} + +void SystemSnapshotIOSIntermediateDump::OSVersion(int* major, + int* minor, + int* bugfix, + std::string* build) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *major = os_version_major_; + *minor = os_version_minor_; + *bugfix = os_version_bugfix_; + build->assign(os_version_build_); +} + +std::string SystemSnapshotIOSIntermediateDump::OSVersionFull() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return base::StringPrintf("%d.%d.%d %s", + os_version_major_, + os_version_minor_, + os_version_bugfix_, + os_version_build_.c_str()); +} + +std::string SystemSnapshotIOSIntermediateDump::MachineDescription() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return machine_description_; +} + +bool SystemSnapshotIOSIntermediateDump::NXEnabled() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(justincohen): Consider using kern.nx when available (pre-iOS 13, + // pre-OS X 10.15). Otherwise the bit is always enabled. + return true; +} + +void SystemSnapshotIOSIntermediateDump::TimeZone( + DaylightSavingTimeStatus* dst_status, + int* standard_offset_seconds, + int* daylight_offset_seconds, + std::string* standard_name, + std::string* daylight_name) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *dst_status = dst_status_; + *standard_offset_seconds = standard_offset_seconds_; + *daylight_offset_seconds = daylight_offset_seconds_; + standard_name->assign(standard_name_); + daylight_name->assign(daylight_name_); +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/ios/system_snapshot_ios_intermediate_dump.h b/shared/sentry/external/crashpad/snapshot/ios/system_snapshot_ios_intermediate_dump.h new file mode 100644 index 000000000..9708e7fd1 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/ios/system_snapshot_ios_intermediate_dump.h @@ -0,0 +1,100 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_SYSTEM_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ +#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_SYSTEM_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ + +#include + +#include + +#include "snapshot/system_snapshot.h" +#include "util/ios/ios_intermediate_dump_map.h" +#include "util/ios/ios_system_data_collector.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +namespace internal { + +//! \brief A SystemSnapshot of the running system, when the system runs iOS. +class SystemSnapshotIOSIntermediateDump final : public SystemSnapshot { + public: + SystemSnapshotIOSIntermediateDump(); + + SystemSnapshotIOSIntermediateDump(const SystemSnapshotIOSIntermediateDump&) = + delete; + SystemSnapshotIOSIntermediateDump& operator=( + const SystemSnapshotIOSIntermediateDump&) = delete; + + ~SystemSnapshotIOSIntermediateDump() override; + + //! \brief Initializes the object. + //! + //! \param[in] system_data An intermediate dump map containing various system + //! data points. + //! \return `true` if the snapshot could be created. + void Initialize(const IOSIntermediateDumpMap* system_data); + + // SystemSnapshot: + + CPUArchitecture GetCPUArchitecture() const override; + uint32_t CPURevision() const override; + uint8_t CPUCount() const override; + std::string CPUVendor() const override; + void CPUFrequency(uint64_t* current_hz, uint64_t* max_hz) const override; + uint32_t CPUX86Signature() const override; + uint64_t CPUX86Features() const override; + uint64_t CPUX86ExtendedFeatures() const override; + uint32_t CPUX86Leaf7Features() const override; + bool CPUX86SupportsDAZ() const override; + OperatingSystem GetOperatingSystem() const override; + bool OSServer() const override; + void OSVersion(int* major, + int* minor, + int* bugfix, + std::string* build) const override; + std::string OSVersionFull() const override; + bool NXEnabled() const override; + std::string MachineDescription() const override; + void TimeZone(DaylightSavingTimeStatus* dst_status, + int* standard_offset_seconds, + int* daylight_offset_seconds, + std::string* standard_name, + std::string* daylight_name) const override; + + private: + std::string os_version_build_; + std::string machine_description_; + int os_version_major_; + int os_version_minor_; + int os_version_bugfix_; + uint32_t active_; + uint32_t inactive_; + uint32_t wired_; + uint32_t free_; + int cpu_count_; + std::string cpu_vendor_; + DaylightSavingTimeStatus dst_status_; + int standard_offset_seconds_; + int daylight_offset_seconds_; + std::string standard_name_; + std::string daylight_name_; + InitializationStateDcheck initialized_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_SYSTEM_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ diff --git a/shared/sentry/external/crashpad/snapshot/ios/testdata/crash-1fa088dda0adb41459d063078a0f384a0bb8eefa b/shared/sentry/external/crashpad/snapshot/ios/testdata/crash-1fa088dda0adb41459d063078a0f384a0bb8eefa new file mode 100644 index 0000000000000000000000000000000000000000..6f062a744bc5e3733ee0f98c7df193169a052eab GIT binary patch literal 565 zcmZQ$Wn^GvfB;6u1^lc__ywTcO%APzP$r0CVqC`0x`LkrN$7qQ8w2Zd6roLtObkp+ zj2*(P{lZdErJ4T03c;DVr8$Wu`9%sEnE@I3d8vj*I>wr;lZ08Iiui#v>tr|=B+5Dg zl{-bvE3jFGZpZtTW+Uy@kxc&}A+VXJjpxhlLzSA8Uy?3b#TW=0d|vGqCr2z&>X! t7GDANmOKL_;$hx=F;#cx_j(2f4T!u>+kYTu0aWn7ElCBSZ|WJqssS8Ga0LJW literal 0 HcmV?d00001 diff --git a/shared/sentry/external/crashpad/snapshot/ios/testdata/crash-5726011582644224 b/shared/sentry/external/crashpad/snapshot/ios/testdata/crash-5726011582644224 new file mode 100644 index 0000000000000000000000000000000000000000..c2ebe211bf3d13ec9a6dcdf610300cb497113d65 GIT binary patch literal 71017 zcmeHQ30zdw_rC*-8zLyVq=jSd3oSEGYe2M$I)BG_BM! zGBrypvRrXNb1SW|#9uBcm1UYKYKx}H|GsVJy!$Rcs-M4d=0ETAfp_2C-ka|^_uO;u zJ?GvAav72#?vlw$1Ldm%gZM{_YPy;UCQe4ks3}g8uMG6#lYX2aL-JRhk0*5a@ik{b zGheXH+{SEv+3G;~+Q3G9S>YysT@vLZTSMjy+uQ&VIomJwK|VgRS;6uJ!EO24^l2H2 z41J;9pthMTif;OJoyn*TkLVTIUA`z-`07BMS^k{!ktr%K5kD>rZotUTE-}me7W&#+%-7sn zCZE(=K!bU;+$MgU+*){Rz$1OxpCm7KMpY-*1Tb-OYwNZAceh6z^9a9VR)}WTuTLY? zjZa7Ixo080nok$<`8E_Xa(DAR+`WO1_*p0~)F+hM_V|qnwfKEPexf=d8She8LZ{#TS!S@pv9WISAEsf-Ngz3LHY^l!u>`5_X^KFpW@o9@GQJfcs5_1 z!^w^ZG`m>Yz{?L+K#v))k*4?Ien%pIFNx%yw}0=dpOHht??o*T((x}5WX_~!T!irL zLboS;%U`_<67;W>7leUVI56n_v%-P-0)dte=7G6lr?c${7Y`78PSAFk%|BR*cp{{Ua4AOAiD|18AU@a1#%_GjNGloRgNd^>-t zoJ;46J8w<+&|A2}6!_nnhy(Y-2OK8bsqE7QK8!p;{N35#ll67taB<+Q%q6_50=I9b zeG2sm@j`uxLm~}KekK3=r}SLH{?5vKlq}wp*MzcnK`)pVah8>H_gt9D=2P*%|6=I9 z?DH4fIeEs&L*`B?|8Ub8@4_j|9+grU8RnnHaH)! zyyffL`b1MUe+6G&$WGobZsO#q5brMp@SVXXrz?log8Kv5`nvPDlXMb_btbwx$K}U3 zz7;b3P4}{wg4pjRUw7+viIW$YG;W-Lknp>!dAxR9NMhUBbppSGojE*E4Of4T@XY7` zg705(I`?Wb-(&Ilk?_plyYcY~oLkP8@I$b_5#HDBC+>Zf#QVyG=Q`deq~8ii=Hv$V zRj5rD?sq7J?-K5OeD)dc_XpK-@6`$Uxw~vA|IAm1HuHQU#0&QlKHrNC`1fCF^{ARD zB;W2Y$T)=hv(?BB`1{Cr_zPV;LW3L1EBuAtF(mxJOPZ>ILDQcM&AV~=aF%>0r~sEA z+z`pPISVs`DIb}<-2W_FH9}!=X0t)BQQPz;qcv=d#+0Y+tu~v(5=?j;Wwcqt617EI zgUPJ5gr(!LT)xRtXbs~=b7>j9&3sf?sy^4EwiJWh2_}oyIRFexx0v$v2CX%0q}^!K z7vcaOAojNE3oYEU>wP1&R+HVLab+E8GTFj1imf(nq4PD#7ImR^lF2f`iVIq_Lo8~u zPOq`{&KKfE(ekY>NOH=$F?=N7<&4T|Ek#~cNJHLN*0cdYK6}v(#id#O+fT__|Mjl# z#)pfLSE^7`JW!?}f69}P&(uz~5vVg!5b>ZJti%C`|3cyQn)_H-M0}3tFkF28>GZk! zD<>n)E?fO%eo5!A{w+cqRiHgSo{e^fR%5s5ZN&g>Aqt|6T6G}!TS4J(M@tfIS<~My z0JPuU7k~WhFa4&K?7H=;rG?!gLOTfg{(gaqcDZ};w%uw8Gw5@fzsGxr_s%QE0SIRL z7h?!8kG7Nw3niy40oL{R5wY&Q4dYYtE2H`^K6&Do=9fc)MPOpYhGTg|5fnSz7EDdg zo-{bCyO5ILI0eoeuD=IxBHuDUz?%&w9$z%A|DX+T{(8FIxHex76T?}7%KWHsR=6#k zT<6z2Udo_~r%48_q9Cf^emO)GwU+}#U%qdFI`7kgPcImI?w%>j4)hQsig0EsqC4Fd zQOuy~T)h-eWhatD4^i;U;Tk-tmx;Nwq4L0pw;i#0i$1))I8U?ov>4Mmxs;A61GF%- z5~gaML%csVrF++Ect94Lav#(5j+0WSrZq{OBNXZ=IG{w_eimvH^YV-lA%~R#>o1&d z5H)AxVlib9O7`WreLTzFPL27BdvQCVF{fMfMQWQCD9(u{jlB@7_`}skb%E9b^a>&c z_K0QkdlNmPKSjN$ppf8ONP=mP4VbznZoul*&*g;FBpmsGB*C}z9`WuQv>@bNA>rG_ z-EzBgD5{PkGsOk8gJF5{uVaHF4BLm<7PNh%+d)xdRj5!A&P`EYaT{hY_ncvn%FC>jVR{=g z+W;h`GY-P8itI$dzK%BF9m{GFpqrZDdng#dzC5Kz;IY*M_B}s4eT-?|hE^ij%l4x~ z!A{r-j*)(Qs&=woV={s-=VQQkJ3G2_cY<&Ti6(~-a@@oNQ+|#Jp&~7(<@2xd-n&o(7^&OO1P(>Wk1_D7rt%gv!40v^9Ib!kxnPhCp?=UW z-}NY0zIy+0UzCIE;qwd6B8y-%l9hkX<*(t(v-$D+nf$)vaKOyH4~PEaW#)fZ@|C0P z_Y0XTn0V%@p457#JHkKk!sgw;)SUd2^rUDb43KKhol!7FjKxZ*eTRWpGAtNxt{`M-dS7 z2hZeyKfLXnZ*6yZI~U{$BPWHN#UK@0VTY+$E5N-p)_ku8X z1$CBf2g2CW(;1KL<(*+hLlWMr0O1ZGjQuT~c{+k{ClKxo!d*bPD+pub8D~A+LAVD9 z_XOcyARG$9y+K$B!r>qs0m6|W90kICKzINM4+P;sAUqg^lRDro@ko8#$K$ z@GPs=VhwxDZnf!+8k0_I3}fu+!!#DPRcBV`xdJA;4V(4n+6zMUX2yvdBg=brvsk2z z$VeKQnax$6l9b^lkc0``f=zyhtM$fAokgq8OVDXG6R>RsYYEJumOL_Uc+%*!k;CpB zZa!d@|LEd9FPyT*3@Zs~H&*$*?2KrYAG6{r-WZK*ip+=w3-EIP&$(w5CM9EPJ+2to zp+nEOS0pDV$M;K!=@Xie5FZ^Hl^C8B8lRXP6PnOh*)K7nPgGJ&OmbMB+NNfGGcZI9 zzr@>(c?K=y`mmVjguc;nQGG-E#YD%4Mn%MjhsG!Mi3$zJ32{*|$w`s%5n+0hHJfFv z?8zFt-i9q>tgM>}B_1~0pgru^3N+g}vD4MeZ-;&|6xwz3dLROv0V$)jKyx3cpz4*M^LbI(nn|&89z^CC+IpFK8qg#92H?42W ztqD)H*fiPKM({OL1{e)7B)~5!_y3Tu4070A?3Pr&5P{sBvxW5~2i#1vf3M*95f}zx zF#zqcnyxrKFW~aPs3rTC4{i7~>MMd)p$hJD3qtEO>pA9L&=NLHzzU8vx&o$5>@&n! zabFr?fHja}_ZGzA0dOV^unx*!?2+gj-hAPYBmK_4XV@#kS!{rXt8(I8?I}1Xo3OBF z9RmT_u1JWu*+g1qmr_jL(y>O3FVb#a?9b?v+ip-8pO`0wxVnigC~fqW67 zpk(oSll}=Gb)XD-y-ELDhkpR3*P9gYz3^Y|p8(+GR1dE=>77H=-MmS?KA`B2`haqy z3g_6)>jR1b@%n)N38kR!fgbeQ18NwIhN_s?neVToE_H}=I_?4FopBKMzu%b;w=k!? zIp@~18dBIZbZR`l(P_1C(;!zQ1l}=$^fdp3GNq*CW}m?%USrvZaFeE;84s^&aWJau z26>Al`Ss-SqUod(75b3ANz)G=27C%$zs%5D*p(l^@-P~LpPta4m_hxJ0=|P=E(PEt z^rH+c0$dzD*)*fPZ(`Xd^%0wG=$j(&F~Ya#p2H{H8hkKQol=N*R!B*MfO9brYsW5f z_RfzoTb&cbf+Cqa6|D?Zb$#xOW&5MGWxl^=sWN?iNW9lJqOJ^_cWb;UQpS{TOVt}E z=#2#c`V3brgukq70m14=DXcDCEI_CST$DNT^*amqjEYEEbp2;d%Y;vskkDhp60qLo z6!6t<4LxA>hwN&L?Qv%M3PA3Pgou~^i`;^54h8WtY@&lNlHu_pCJ->2Mz0)}_QN2} z`^T1AwkKWtRD^nv!)K3Z_1ua2Sx-Tooi3D$br=niFMF93HSrWtQ-ck+D9Fp~aD6u; z{+_t5!^7Lm-l$ic*%v)Wggh=ilfDU;$B6b3S{q|81DKtUfp|O8yAZNv6b0{eZbe_z zjf%KD?&O;lO<#-N{LYY@$EIF7bzFos-i)uT301c2V#(+>OJZP($~w`2ZA2oaJ3uMX z0|cP74f)dP42)8sZgOemFw;8JuiMnEvWp?QrV@djLf1|9xGkt=i*}-2%K$6I)%+}} z@6q->lWue+t%2l9$QEK4Ki%5a-{+7<+H$99L zco%0Q)~@4CYD*qvSV>EU#4x9IB#3dCLX1+$@G=DHq3vW*hj*)L28Yj@x_@TF=+c4! z5izP|I5V|hyzbV-06KS;QP1qhomp(wrsUzB{Pg*HY@-QO5_$$m36*?2L5a>34Mo9O z4vI9CeUK8hhDkjGy2UJ;I^;s`kSXoNdsSh$KcQ2?)26rZUg{Zkvl-h>0wTB~A@HSU zQj+Oyin6qqp0~9#UI0(J+=dOf|386sm!0tu(63H0RC}{t40cZO+i+p@V^UW`PtmJ7lHRViZH2U z;riEj(#`p?wV(&4~SWW9pTn37N+v));H)jPGf4 zm)v<5dH8@k?9gwwkPdFNWJnAoBj+yXQ`&`!qQ{GL%U92iX)$g?^q?8@1MXjZ^wC42 zc7Z9`gg$ZeGe=}Ml|s@v{dAKadumy-QYf>?k~ByLMF$PhCITsKqTbFRX|g4yp-qj*eYtyn_F#uf@ z4{>*N8b>gE3I%s12aky1H^HIQ<5@l8rk?9pIql~8wqpzKk;xx-ZV$;8-9(=``--dA zO>vhB{eR3X+ zI}qO3i8i|O7z?rC+C05F(_}K_nkEClVKhWt6-|z@UZ6L zN`M5&c`}B?YlBLGL&Nzs7$C-(m6)?8kPWH+h}#WuZ$v_ps!;vorgI+07@G~a`2iW#6tzd8EBlFn~$RE~SD z<(S)zhg!s5WlD$7a=MUlZ@CVHOiG$~4R#c^JHxY@*08uRHVH!R=t)}M45V;<1x1k$ zhW6S|D6e%MqUw{nYDwcMeOGr;h&UcYUfPh_m0ogdjt7u48$FINF*bf0cFGd6yCeeYjunf=R-ZdF;T*G2dv6>7tNg5=BavnK}|0$&z)JQs8!shkoMlU7o=^FFsQrrEt_jO+*R&VOIdG$0h#H%$E z>N0|oq(L%N+eqiL7b#?@VCKBA4Bz3;*&s6yqSi3x#Th@}e>Aqus-dsXTCw_)WG&@O zdTa1B8^CzARm+kAsP@h{2zyW_srr0PfxU$Dgp#N}m=Z2^vw8O7LgVWZjh`>tv%Tt? ze79H-ArwuYL_5RK5MQ1&sLe_NxHE&G5<1Nw z=kJ|z?@#tlvA-={b@j()#|xe#i7=l&ZSQIB-JH`E)7iqg91shkS1cu=r`dhy)Ti)w zcwQey{oJi-XtB5AiwKv(2DUBKYgx`+FbPg}aNA30&@( z8DgtdkKLK|wqmKRYh@3z8~upl$y`&P#fPD(@xwQo^}jvKI-!;I(mb;W)(RxE)7J%e zyDhAGja`HF^4@V$NX>)Dd9hOzNEO^RC{R%NB*G3dYf`ScrnB zPRzB;y_~;m*wZAcS@d4-klUkbDl9Y^6D-AMY@flNf&+zXr3Bm^6mTmj;D-DFM)1?N z+?!o?ZNLxZL0Q|Lm6f+A!43I=+P8h=0pObPb`h3r>>n(_wqzj5xEm?hI;7b8=0nD{ zwH&`_+@)CMypby!o}4(7SaGgE`Ek_n0MJ_XoPDX`)hzx&F6 zzVgP$QC;V+?eM@$?ee>kO#2=kST{Y;2FO|5eQD~D2SM$~{iJBnDkQqDUz6Q%FR6>~ z-qu|!AGB)5u{rIEn+@nBf>!15*-hWy#O--DPj5A=Z5o}FXKR8;U_YSnteG1;6Ek;y zO?ttV;#tq$6EpmWzQ4V+vOvAMPn>w>4uji^zEb>wy9)&7*gh$+C7nrNH&KAC;C?&- zEU^TTa=Z1zBQHgj&xrlB_phPj9}saZ&MQ8-jag7&Pq=b>#j6l2UKHIRX9U@QZKfcb z!yzkDyiP*@=HG9$>unGyZ+bPg0 zD9{dss=?Rkx03zZ_iyx0r|;J+C|V>LDd4k4X(I)SZ=W;@75{`U8{z5#XN^72H-HU%RxBC-aqh=5@mmUln=utaT< z_HPh1fZwGqFk#*e@lDKpjx`K#hiuaq;z0duuE(CyMlEg)W`o+sZ0;K-jFkRYBo%A` zxM4iAvdV?Bt@^@j;VZIrCM#vlN`VZ-(#kf<*q-J<_9GnIzttg8m6H~8b$Z^*C3CtT zZ9|THu~7?cHIO7Rye_ZFc3mh)}de!Ye^7W<6&%v|(2mF1&q_(VE zb2v44HlcHGt4|-tmV+;^uiQChp6Kg?P?P5Lsll#X-T&$9Z90oqOP|f-d~kk+=;KwW$+z^6_q3xjK2MpS<#d>wQce_xSti@J5adYI5-F0V~(V_2_XZ`MHy% ztayvw6S}E!3|1_7K(jUZI;{aON>HFxEhLqUBa}W-!L?Q(1qf&t9jLljR}_2itbp*m z+^SRle-K(XwLoN{EyRZ{Vs9FgQOd71(@0ROD4?bXu&9ba6$yZuFg1G zxU;$8H4@Y(>HNCeZAGivqS5J#w7nywkeYu^B18a988&k>P7qR`#!v*ze>$Mmwwck> zpWWOgZ0Cw^3Q0&cbVzr)Eu@nR4G}avRiBdE`+iDcQDrj;i#Emw(YiKjSan)CY<|P= z6FY7X3RoCec}0w;19kK?JPm4Nfu1rMS2i$$;Lp<(JQW-tLH@+!H-P8KL$~~%S)}}| zS3=92K zBe<-5IiY?be-59&T@aVwOgszkA@9!L!~f39wGG)ULcH)EvfLIvKTKELZjTgGhWj#j z3H1oiFdwXI44)sSv-R+q9F-rEg{WbL617FqG-*BVN44 zIureTV)yx}BTXh-7-4emb)EhD{LTpIfvvgAOBUEFx*}n__PWmg0iQzN{gB4+0Ch%& zQZ`zK(GdBvmsyoUzRT&z6;F|qP9YrHzytjVTdn@PCwIl#@ZPbP378DReXEJ?_Q8090(1uyf~^4AEL`Y6Hd9S`|g^7XC2> zRu* z>H@8W9Y44{3Mr|u>#MwyPMGr@q8u@xppZhe-`kE$)^8kdM9*3`ZKM3{O-0!vQeeF6 z*zfJ8hmiuyZKK{;P}gB@v}8yOa~kQXa+pGlQf}@5h#TTqy`m29R@Dp+pEY&=%!bjW z1py*rRLhW_e!kCjwyeSKzwq$)Jsj2(he_av(?|TC zbBr?{0I?gC0l=A#fsKQBmuw}wVw!VYWdzHSLWwr?k#@%n%vpG61t@TwaS(dt&s_-_6GD+Oj#ex>B32h! z((ivR;B5b9k&mxvKH4;6R2kttr)so&Gwm+8VnzcGR4*hE3)_YuP}8?@!-q-DBom((P6qagrey1F_Dj z<@OJ8&2!D8FuH?d$hsv?SnSMK^Ao{<|dB`Sjter%9YK37MH{OMw6f&&Q_WT3873O&(+&ZA%vydU(@jQ7!^JxEyHffS8KF@rJZq*pUr*A zeYwt2gh^$6crVC#Tfgx2*s9rM&t%J61nva2zYS z3Z@Ox%v$4+)T9i6w-5#4R&9$V*!wgE?g|oaOCkPJ94XS>v-W50nb;_%(UcJjSECHn SON4ol3 + +namespace { + +std::vector GenerateStackMemoryFromFrames(const uint64_t* frames, + const size_t frame_count) { + std::vector stack_memory; + if (frame_count < 2) { + return stack_memory; + } + size_t pointer_size = sizeof(uintptr_t); + size_t frame_record_size = 2 * pointer_size; + size_t stack_size = frame_record_size * (frame_count - 1) + pointer_size; + stack_memory.resize(stack_size); + uintptr_t sp = stack_size - pointer_size; + uintptr_t fp = 0; + uintptr_t lr = 0; + for (size_t current_frame = frame_count - 1; current_frame > 0; + --current_frame) { + memcpy(&stack_memory[0] + sp, &lr, sizeof(lr)); + sp -= pointer_size; + memcpy(&stack_memory[0] + sp, &fp, sizeof(fp)); + fp = sp; + sp -= pointer_size; + lr = frames[current_frame]; + } + + if (sp != 0) { + LOG(ERROR) << "kExpectedFinalSp should be 0, is " << sp; + } + if (fp != sizeof(uintptr_t)) { + LOG(ERROR) << "kExpectedFinalFp should be sizeof(uintptr_t), is " << fp; + } + if (lr != frames[1]) { + LOG(ERROR) << "lr should be " << frames[1] << ", is " << lr; + } + return stack_memory; +} + +} // namespace +namespace crashpad { +namespace internal { + +using Key = IntermediateDumpKey; + +ThreadSnapshotIOSIntermediateDump::ThreadSnapshotIOSIntermediateDump() + : ThreadSnapshot(), + context_(), + stack_(), + thread_id_(0), + thread_specific_data_address_(0), + suspend_count_(0), + priority_(0), + initialized_() { +#if defined(ARCH_CPU_X86_64) + context_.architecture = kCPUArchitectureX86_64; + context_.x86_64 = &context_x86_64_; +#elif defined(ARCH_CPU_ARM64) + context_.architecture = kCPUArchitectureARM64; + context_.arm64 = &context_arm64_; +#endif +} + +ThreadSnapshotIOSIntermediateDump::~ThreadSnapshotIOSIntermediateDump() {} + +bool ThreadSnapshotIOSIntermediateDump::Initialize( + const IOSIntermediateDumpMap* thread_data) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + GetDataValueFromMap(thread_data, Key::kSuspendCount, &suspend_count_); + GetDataValueFromMap(thread_data, Key::kPriority, &priority_); + GetDataValueFromMap(thread_data, Key::kThreadID, &thread_id_); + GetDataValueFromMap( + thread_data, Key::kThreadDataAddress, &thread_specific_data_address_); + +#if defined(ARCH_CPU_X86_64) + typedef x86_thread_state64_t thread_state_type; + typedef x86_float_state64_t float_state_type; + typedef x86_debug_state64_t debug_state_type; +#elif defined(ARCH_CPU_ARM64) + typedef arm_thread_state64_t thread_state_type; + typedef arm_neon_state64_t float_state_type; + typedef arm_debug_state64_t debug_state_type; +#endif + + thread_state_type thread_state; + float_state_type float_state; + debug_state_type debug_state; + + const IOSIntermediateDumpData* nsexception_frames = + thread_data->GetAsData(Key::kThreadUncaughtNSExceptionFrames); + const IOSIntermediateDumpData* thread_stack_data_dump = + thread_data->GetAsData(Key::kStackRegionData); + if (nsexception_frames && thread_stack_data_dump) { + LOG(ERROR) << "Unexpected thread with kStackRegionData and " + << "kThreadUncaughtNSExceptionFrames, using kStackRegionData"; + } + if (thread_stack_data_dump) { + vm_address_t stack_region_address; + GetDataValueFromMap( + thread_data, Key::kStackRegionAddress, &stack_region_address); + + const std::vector& bytes = thread_stack_data_dump->bytes(); + const vm_address_t stack_region_data = + reinterpret_cast(bytes.data()); + vm_size_t stack_region_size = bytes.size(); + stack_.Initialize( + stack_region_address, stack_region_data, stack_region_size); + } else if (nsexception_frames) { + const std::vector& bytes = nsexception_frames->bytes(); + const uint64_t* frames = reinterpret_cast(bytes.data()); + size_t frame_count = bytes.size() / sizeof(uint64_t); + exception_stack_memory_ = + GenerateStackMemoryFromFrames(frames, frame_count); + vm_address_t stack_memory_addr = + !exception_stack_memory_.empty() + ? reinterpret_cast(&exception_stack_memory_[0]) + : 0; + stack_.Initialize(0, stack_memory_addr, exception_stack_memory_.size()); + } else { + stack_.Initialize(0, 0, 0); + } + + if (GetDataValueFromMap(thread_data, Key::kThreadState, &thread_state) && + GetDataValueFromMap(thread_data, Key::kFloatState, &float_state) && + GetDataValueFromMap(thread_data, Key::kDebugState, &debug_state)) { +#if defined(ARCH_CPU_X86_64) + InitializeCPUContextX86_64(&context_x86_64_, + THREAD_STATE_NONE, + nullptr, + 0, + &thread_state, + &float_state, + &debug_state); +#elif defined(ARCH_CPU_ARM64) + InitializeCPUContextARM64(&context_arm64_, + THREAD_STATE_NONE, + nullptr, + 0, + &thread_state, + &float_state, + &debug_state); + +#else +#error Port to your CPU architecture +#endif + } + const IOSIntermediateDumpList* thread_context_memory_regions = + GetListFromMap(thread_data, Key::kThreadContextMemoryRegions); + if (thread_context_memory_regions) { + for (auto& region : *thread_context_memory_regions) { + vm_address_t address; + const IOSIntermediateDumpData* region_data = + region->GetAsData(Key::kThreadContextMemoryRegionData); + if (!region_data) + continue; + if (GetDataValueFromMap( + region.get(), Key::kThreadContextMemoryRegionAddress, &address)) { + const std::vector& bytes = region_data->bytes(); + vm_size_t data_size = bytes.size(); + if (data_size == 0) + continue; + + const vm_address_t data = + reinterpret_cast(bytes.data()); + + auto memory = + std::make_unique(); + memory->Initialize(address, data, data_size); + extra_memory_.push_back(std::move(memory)); + } + } + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} +const CPUContext* ThreadSnapshotIOSIntermediateDump::Context() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &context_; +} + +const MemorySnapshot* ThreadSnapshotIOSIntermediateDump::Stack() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &stack_; +} + +uint64_t ThreadSnapshotIOSIntermediateDump::ThreadID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_id_; +} + +int ThreadSnapshotIOSIntermediateDump::SuspendCount() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return suspend_count_; +} + +int ThreadSnapshotIOSIntermediateDump::Priority() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return priority_; +} + +uint64_t ThreadSnapshotIOSIntermediateDump::ThreadSpecificDataAddress() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_specific_data_address_; +} + +std::vector +ThreadSnapshotIOSIntermediateDump::ExtraMemory() const { + std::vector extra_memory; + for (const auto& memory : extra_memory_) { + extra_memory.push_back(memory.get()); + } + return extra_memory; +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/ios/thread_snapshot_ios_intermediate_dump.h b/shared/sentry/external/crashpad/snapshot/ios/thread_snapshot_ios_intermediate_dump.h new file mode 100644 index 000000000..cf9ccec02 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/ios/thread_snapshot_ios_intermediate_dump.h @@ -0,0 +1,80 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_THREAD_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ +#define CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_THREAD_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ + +#include "build/build_config.h" +#include "snapshot/cpu_context.h" +#include "snapshot/ios/memory_snapshot_ios_intermediate_dump.h" +#include "snapshot/thread_snapshot.h" +#include "util/ios/ios_intermediate_dump_map.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { +namespace internal { + +//! \brief A ThreadSnapshot of a thread on an iOS system. +class ThreadSnapshotIOSIntermediateDump final : public ThreadSnapshot { + public: + ThreadSnapshotIOSIntermediateDump(); + + ThreadSnapshotIOSIntermediateDump(const ThreadSnapshotIOSIntermediateDump&) = + delete; + ThreadSnapshotIOSIntermediateDump& operator=( + const ThreadSnapshotIOSIntermediateDump&) = delete; + + ~ThreadSnapshotIOSIntermediateDump() override; + + //! \brief Initializes the object. + //! + //! \brief thread_data The intermediate dump map used to initialize this + //! object. + //! + //! \return `true` if the snapshot could be created. + bool Initialize(const IOSIntermediateDumpMap* thread_data); + + // ThreadSnapshot: + const CPUContext* Context() const override; + const MemorySnapshot* Stack() const override; + uint64_t ThreadID() const override; + int SuspendCount() const override; + int Priority() const override; + uint64_t ThreadSpecificDataAddress() const override; + std::vector ExtraMemory() const override; + + private: +#if defined(ARCH_CPU_X86_64) + CPUContextX86_64 context_x86_64_; +#elif defined(ARCH_CPU_ARM64) + CPUContextARM64 context_arm64_; +#else +#error Port. +#endif // ARCH_CPU_X86_64 + CPUContext context_; + std::vector exception_stack_memory_; + MemorySnapshotIOSIntermediateDump stack_; + uint64_t thread_id_; + uint64_t thread_specific_data_address_; + int suspend_count_; + int priority_; + std::vector> + extra_memory_; + InitializationStateDcheck initialized_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_IOS_INTERMEDIATE_DUMP_THREAD_SNAPSHOT_IOS_INTERMEDIATEDUMP_H_ diff --git a/shared/sentry/external/crashpad/snapshot/linux/capture_memory_delegate_linux.cc b/shared/sentry/external/crashpad/snapshot/linux/capture_memory_delegate_linux.cc new file mode 100644 index 000000000..918e572d5 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/linux/capture_memory_delegate_linux.cc @@ -0,0 +1,75 @@ +// Copyright 2021 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/linux/capture_memory_delegate_linux.h" + +#include + +#include "base/numerics/safe_conversions.h" +#include "snapshot/memory_snapshot_generic.h" + +namespace crashpad { +namespace internal { + +CaptureMemoryDelegateLinux::CaptureMemoryDelegateLinux( + ProcessReaderLinux* process_reader, + const ProcessReaderLinux::Thread* thread_opt, + std::vector>* snapshots, + uint32_t* budget_remaining) + : stack_(thread_opt ? thread_opt->stack_region_address : 0, + thread_opt ? thread_opt->stack_region_size : 0), + process_reader_(process_reader), + snapshots_(snapshots), + budget_remaining_(budget_remaining) {} + +bool CaptureMemoryDelegateLinux::Is64Bit() const { + return process_reader_->Is64Bit(); +} + +bool CaptureMemoryDelegateLinux::ReadMemory(uint64_t at, + uint64_t num_bytes, + void* into) const { + return process_reader_->Memory()->Read( + at, base::checked_cast(num_bytes), into); +} + +std::vector> +CaptureMemoryDelegateLinux::GetReadableRanges( + const CheckedRange& range) const { + return process_reader_->GetMemoryMap()->GetReadableRanges(range); +} + +void CaptureMemoryDelegateLinux::AddNewMemorySnapshot( + const CheckedRange& range) { + // Don't bother storing this memory if it points back into the stack. + if (stack_.ContainsRange(range)) + return; + if (range.size() == 0) + return; + if (!budget_remaining_ || *budget_remaining_ == 0) + return; + snapshots_->push_back(std::make_unique()); + internal::MemorySnapshotGeneric* snapshot = snapshots_->back().get(); + snapshot->Initialize(process_reader_->Memory(), range.base(), range.size()); + if (!base::IsValueInRangeForNumericType(range.size())) { + *budget_remaining_ = 0; + } else { + int64_t temp = *budget_remaining_; + temp -= range.size(); + *budget_remaining_ = base::saturated_cast(temp); + } +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/linux/capture_memory_delegate_linux.h b/shared/sentry/external/crashpad/snapshot/linux/capture_memory_delegate_linux.h new file mode 100644 index 000000000..c0250b556 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/linux/capture_memory_delegate_linux.h @@ -0,0 +1,70 @@ +// Copyright 2021 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_LINUX_CAPTURE_MEMORY_DELEGATE_LINUX_H_ +#define CRASHPAD_SNAPSHOT_LINUX_CAPTURE_MEMORY_DELEGATE_LINUX_H_ + +#include "snapshot/capture_memory.h" + +#include + +#include +#include + +#include "snapshot/linux/process_reader_linux.h" +#include "util/numeric/checked_range.h" + +namespace crashpad { +namespace internal { + +class MemorySnapshotGeneric; + +class CaptureMemoryDelegateLinux : public CaptureMemory::Delegate { + public: + //! \brief A MemoryCaptureDelegate for Linux. + //! + //! \param[in] process_reader A ProcessReaderLinux for the target process. + //! \param[in] thread_opt The thread being inspected. Memory ranges + //! overlapping this thread's stack will be ignored on the assumption + //! that they're already captured elsewhere. May be nullptr. + //! \param[in] snapshots A vector of MemorySnapshotGeneric to which the + //! captured memory will be added. + //! \param[in] budget_remaining If non-null, a pointer to the remaining number + //! of bytes to capture. If this is `0`, no further memory will be + //! captured. + CaptureMemoryDelegateLinux( + ProcessReaderLinux* process_reader, + const ProcessReaderLinux::Thread* thread_opt, + std::vector>* snapshots, + uint32_t* budget_remaining); + + // MemoryCaptureDelegate: + bool Is64Bit() const override; + bool ReadMemory(uint64_t at, uint64_t num_bytes, void* into) const override; + std::vector> GetReadableRanges( + const CheckedRange& range) const override; + void AddNewMemorySnapshot( + const CheckedRange& range) override; + + private: + CheckedRange stack_; + ProcessReaderLinux* process_reader_; // weak + std::vector>* snapshots_; // weak + uint32_t* budget_remaining_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_LINUX_CAPTURE_MEMORY_DELEGATE_LINUX_H_ diff --git a/shared/sentry/external/crashpad/snapshot/linux/cpu_context_linux.cc b/shared/sentry/external/crashpad/snapshot/linux/cpu_context_linux.cc new file mode 100644 index 000000000..8464a5a27 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/linux/cpu_context_linux.cc @@ -0,0 +1,272 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/linux/cpu_context_linux.h" + +#include +#include + +#include + +#include "base/logging.h" + +namespace crashpad { +namespace internal { + +#if defined(ARCH_CPU_X86_FAMILY) + +#define SET_GPRS32() \ + do { \ + context->eax = thread_context.eax; \ + context->ebx = thread_context.ebx; \ + context->ecx = thread_context.ecx; \ + context->edx = thread_context.edx; \ + context->edi = thread_context.edi; \ + context->esi = thread_context.esi; \ + context->ebp = thread_context.ebp; \ + context->esp = thread_context.esp; \ + context->eip = thread_context.eip; \ + context->eflags = thread_context.eflags; \ + context->cs = thread_context.xcs; \ + context->ds = thread_context.xds; \ + context->es = thread_context.xes; \ + context->fs = thread_context.xfs; \ + context->gs = thread_context.xgs; \ + context->ss = thread_context.xss; \ + } while (false) + +void InitializeCPUContextX86(const ThreadContext::t32_t& thread_context, + const FloatContext::f32_t& float_context, + CPUContextX86* context) { + SET_GPRS32(); + + static_assert(sizeof(context->fxsave) == sizeof(float_context.fxsave), + "fxsave size mismatch"); + memcpy(&context->fxsave, &float_context.fxsave, sizeof(context->fxsave)); + + // TODO(jperaza): debug registers + context->dr0 = 0; + context->dr1 = 0; + context->dr2 = 0; + context->dr3 = 0; + context->dr4 = 0; + context->dr5 = 0; + context->dr6 = 0; + context->dr7 = 0; +} + +void InitializeCPUContextX86(const SignalThreadContext32& thread_context, + const SignalFloatContext32& float_context, + CPUContextX86* context) { + InitializeCPUContextX86_NoFloatingPoint(thread_context, context); + CPUContextX86::FsaveToFxsave(float_context.fsave, &context->fxsave); +} + +void InitializeCPUContextX86_NoFloatingPoint( + const SignalThreadContext32& thread_context, + CPUContextX86* context) { + SET_GPRS32(); + + memset(&context->fxsave, 0, sizeof(context->fxsave)); + + context->dr0 = 0; + context->dr1 = 0; + context->dr2 = 0; + context->dr3 = 0; + context->dr4 = 0; + context->dr5 = 0; + context->dr6 = 0; + context->dr7 = 0; +} + +#define SET_GPRS64() \ + do { \ + context->rax = thread_context.rax; \ + context->rbx = thread_context.rbx; \ + context->rcx = thread_context.rcx; \ + context->rdx = thread_context.rdx; \ + context->rdi = thread_context.rdi; \ + context->rsi = thread_context.rsi; \ + context->rbp = thread_context.rbp; \ + context->rsp = thread_context.rsp; \ + context->r8 = thread_context.r8; \ + context->r9 = thread_context.r9; \ + context->r10 = thread_context.r10; \ + context->r11 = thread_context.r11; \ + context->r12 = thread_context.r12; \ + context->r13 = thread_context.r13; \ + context->r14 = thread_context.r14; \ + context->r15 = thread_context.r15; \ + context->rip = thread_context.rip; \ + context->rflags = thread_context.eflags; \ + context->cs = thread_context.cs; \ + context->fs = thread_context.fs; \ + context->gs = thread_context.gs; \ + } while (false) + +void InitializeCPUContextX86_64(const ThreadContext::t64_t& thread_context, + const FloatContext::f64_t& float_context, + CPUContextX86_64* context) { + SET_GPRS64(); + + static_assert(sizeof(context->fxsave) == sizeof(float_context.fxsave), + "fxsave size mismatch"); + memcpy(&context->fxsave, &float_context.fxsave, sizeof(context->fxsave)); + + // TODO(jperaza): debug registers. + context->dr0 = 0; + context->dr1 = 0; + context->dr2 = 0; + context->dr3 = 0; + context->dr4 = 0; + context->dr5 = 0; + context->dr6 = 0; + context->dr7 = 0; +} + +void InitializeCPUContextX86_64(const SignalThreadContext64& thread_context, + const SignalFloatContext64& float_context, + CPUContextX86_64* context) { + SET_GPRS64(); + + static_assert( + std::is_same::value, + "signal float context has unexpected type"); + memcpy(&context->fxsave, &float_context, sizeof(context->fxsave)); + + context->dr0 = 0; + context->dr1 = 0; + context->dr2 = 0; + context->dr3 = 0; + context->dr4 = 0; + context->dr5 = 0; + context->dr6 = 0; + context->dr7 = 0; +} + +void InitializeCPUContextX86_64_NoFloatingPoint( + const SignalThreadContext64& thread_context, + CPUContextX86_64* context) { + SET_GPRS64(); + + memset(&context->fxsave, 0, sizeof(context->fxsave)); + + context->dr0 = 0; + context->dr1 = 0; + context->dr2 = 0; + context->dr3 = 0; + context->dr4 = 0; + context->dr5 = 0; + context->dr6 = 0; + context->dr7 = 0; +} + +#elif defined(ARCH_CPU_ARM_FAMILY) + +void InitializeCPUContextARM(const ThreadContext::t32_t& thread_context, + const FloatContext::f32_t& float_context, + CPUContextARM* context) { + static_assert(sizeof(context->regs) == sizeof(thread_context.regs), + "registers size mismatch"); + memcpy(&context->regs, &thread_context.regs, sizeof(context->regs)); + context->fp = thread_context.fp; + context->ip = thread_context.ip; + context->sp = thread_context.sp; + context->lr = thread_context.lr; + context->pc = thread_context.pc; + context->cpsr = thread_context.cpsr; + + static_assert(sizeof(context->vfp_regs) == sizeof(float_context.vfp), + "vfp size mismatch"); + context->have_vfp_regs = float_context.have_vfp; + if (float_context.have_vfp) { + memcpy(&context->vfp_regs, &float_context.vfp, sizeof(context->vfp_regs)); + } + + static_assert(sizeof(context->fpa_regs) == sizeof(float_context.fpregs), + "fpregs size mismatch"); + context->have_fpa_regs = float_context.have_fpregs; + if (float_context.have_fpregs) { + memcpy( + &context->fpa_regs, &float_context.fpregs, sizeof(context->fpa_regs)); + } +} + +void InitializeCPUContextARM_NoFloatingPoint( + const SignalThreadContext32& thread_context, + CPUContextARM* context) { + static_assert(sizeof(context->regs) == sizeof(thread_context.regs), + "registers size mismatch"); + memcpy(&context->regs, &thread_context.regs, sizeof(context->regs)); + context->fp = thread_context.fp; + context->ip = thread_context.ip; + context->sp = thread_context.sp; + context->lr = thread_context.lr; + context->pc = thread_context.pc; + context->cpsr = thread_context.cpsr; + + memset(&context->fpa_regs, 0, sizeof(context->fpa_regs)); + memset(&context->vfp_regs, 0, sizeof(context->vfp_regs)); + context->have_fpa_regs = false; + context->have_vfp_regs = false; +} + +void InitializeCPUContextARM64(const ThreadContext::t64_t& thread_context, + const FloatContext::f64_t& float_context, + CPUContextARM64* context) { + InitializeCPUContextARM64_NoFloatingPoint(thread_context, context); + + static_assert(sizeof(context->fpsimd) == sizeof(float_context.vregs), + "fpsimd context size mismatch"); + memcpy(context->fpsimd, float_context.vregs, sizeof(context->fpsimd)); + context->fpsr = float_context.fpsr; + context->fpcr = float_context.fpcr; +} + +void InitializeCPUContextARM64_NoFloatingPoint( + const ThreadContext::t64_t& thread_context, + CPUContextARM64* context) { + static_assert(sizeof(context->regs) == sizeof(thread_context.regs), + "gpr context size mismtach"); + memcpy(context->regs, thread_context.regs, sizeof(context->regs)); + context->sp = thread_context.sp; + context->pc = thread_context.pc; + // Linux seems to only be putting the SPSR register in its "pstate" field. + // https://elixir.bootlin.com/linux/latest/source/arch/arm64/include/uapi/asm/ptrace.h + if (thread_context.pstate > + std::numeric_limitsspsr)>::max()) { + LOG(WARNING) << "pstate truncation: we only expect the SPSR bits to be set " + "in the pstate"; + } + context->spsr = static_castspsr)>(thread_context.pstate); + + memset(&context->fpsimd, 0, sizeof(context->fpsimd)); + context->fpsr = 0; + context->fpcr = 0; +} + +void InitializeCPUContextARM64_OnlyFPSIMD( + const SignalFPSIMDContext& float_context, + CPUContextARM64* context) { + static_assert(sizeof(context->fpsimd) == sizeof(float_context.vregs), + "fpsimd context size mismatch"); + memcpy(context->fpsimd, float_context.vregs, sizeof(context->fpsimd)); + context->fpsr = float_context.fpsr; + context->fpcr = float_context.fpcr; +} + +#endif // ARCH_CPU_X86_FAMILY + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/linux/cpu_context_linux.h b/shared/sentry/external/crashpad/snapshot/linux/cpu_context_linux.h new file mode 100644 index 000000000..9f46a4897 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/linux/cpu_context_linux.h @@ -0,0 +1,180 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_LINUX_CPU_CONTEXT_LINUX_H_ +#define CRASHPAD_SNAPSHOT_LINUX_CPU_CONTEXT_LINUX_H_ + +#include "build/build_config.h" +#include "snapshot/cpu_context.h" +#include "snapshot/linux/signal_context.h" +#include "util/linux/thread_info.h" + +namespace crashpad { +namespace internal { + +#if defined(ARCH_CPU_X86_FAMILY) || DOXYGEN + +//! \{ +//! \brief Initializes a CPUContextX86 structure from native context structures +//! on Linux. +//! +//! \param[in] thread_context The native thread context. +//! \param[in] float_context The native float context. +//! \param[out] context The CPUContextX86 structure to initialize. +void InitializeCPUContextX86(const ThreadContext::t32_t& thread_context, + const FloatContext::f32_t& float_context, + CPUContextX86* context); + +void InitializeCPUContextX86(const SignalThreadContext32& thread_context, + const SignalFloatContext32& float_context, + CPUContextX86* context); +//! \} + +//! \brief Initializes GPR and debug state in a CPUContextX86 from a native +//! signal context structure on Linux. +//! +//! Floating point state and debug registers are initialized to zero. +//! +//! \param[in] thread_context The native thread context. +//! \param[out] context The CPUContextX86 structure to initialize. +void InitializeCPUContextX86_NoFloatingPoint( + const SignalThreadContext32& thread_context, + CPUContextX86* context); + +//! \{ +//! \brief Initializes a CPUContextX86_64 structure from native context +//! structures on Linux. +//! +//! \param[in] thread_context The native thread context. +//! \param[in] float_context The native float context. +//! \param[out] context The CPUContextX86_64 structure to initialize. +void InitializeCPUContextX86_64(const ThreadContext::t64_t& thread_context, + const FloatContext::f64_t& float_context, + CPUContextX86_64* context); + +void InitializeCPUContextX86_64(const SignalThreadContext64& thread_context, + const SignalFloatContext64& float_context, + CPUContextX86_64* context); +//! \} + +//! \brief Initializes GPR and debug state in a CPUContextX86_64 from a native +//! signal context structure on Linux. +//! +//! Floating point state and debug registers are initialized to zero. +//! +//! \param[in] thread_context The native thread context. +//! \param[out] context The CPUContextX86_64 structure to initialize. +void InitializeCPUContextX86_64_NoFloatingPoint( + const SignalThreadContext64& thread_context, + CPUContextX86_64* context); + +#endif // ARCH_CPU_X86_FAMILY || DOXYGEN + +#if defined(ARCH_CPU_ARM_FAMILY) || DOXYGEN + +//! \brief Initializes a CPUContextARM structure from native context structures +//! on Linux. +//! +//! \param[in] thread_context The native thread context. +//! \param[in] float_context The native float context. +//! \param[out] context The CPUContextARM structure to initialize. +void InitializeCPUContextARM(const ThreadContext::t32_t& thread_context, + const FloatContext::f32_t& float_context, + CPUContextARM* context); + +//! \brief Initializes GPR state in a CPUContextARM from a native signal context +//! structure on Linux. +//! +//! Floating point state is initialized to zero. +//! +//! \param[in] thread_context The native thread context. +//! \param[out] context The CPUContextARM structure to initialize. +void InitializeCPUContextARM_NoFloatingPoint( + const SignalThreadContext32& thread_context, + CPUContextARM* context); + +//! \brief Initializes a CPUContextARM64 structure from native context +//! structures on Linux. +//! +//! \param[in] thread_context The native thread context. +//! \param[in] float_context The native float context. +//! \param[out] context The CPUContextARM64 structure to initialize. +void InitializeCPUContextARM64(const ThreadContext::t64_t& thread_context, + const FloatContext::f64_t& float_context, + CPUContextARM64* context); + +//! \brief Initializes GPR state in a CPUContextARM64 from a native context +//! structure on Linux. +//! +//! Floating point state is initialized to zero. +//! +//! \param[in] thread_context The native thread context. +//! \param[out] context The CPUContextARM64 structure to initialize. +void InitializeCPUContextARM64_NoFloatingPoint( + const ThreadContext::t64_t& thread_context, + CPUContextARM64* context); + +//! \brief Initializes FPSIMD state in a CPUContextARM64 from a native fpsimd +//! signal context structure on Linux. +//! +//! General purpose registers are not initialized. +//! +//! \param[in] float_context The native fpsimd context. +//! \param[out] context The CPUContextARM64 structure to initialize. +void InitializeCPUContextARM64_OnlyFPSIMD( + const SignalFPSIMDContext& float_context, + CPUContextARM64* context); + +#endif // ARCH_CPU_ARM_FAMILY || DOXYGEN + +#if defined(ARCH_CPU_MIPS_FAMILY) || DOXYGEN + +//! \brief Initializes a CPUContextMIPS structure from native context +//! structures on Linux. +//! +//! This function has template specializations for MIPSEL and MIPS64EL +//! architecture contexts, using ContextTraits32 or ContextTraits64 as template +//! parameter, respectively. +//! +//! \param[in] thread_context The native thread context. +//! \param[in] float_context The native float context. +//! \param[out] context The CPUContextMIPS structure to initialize. +template +void InitializeCPUContextMIPS( + const typename Traits::SignalThreadContext& thread_context, + const typename Traits::SignalFloatContext& float_context, + typename Traits::CPUContext* context) { + static_assert(sizeof(context->regs) == sizeof(thread_context.regs), + "registers size mismatch"); + static_assert(sizeof(context->fpregs) == sizeof(float_context.fpregs), + "fp registers size mismatch"); + memcpy(&context->regs, &thread_context.regs, sizeof(context->regs)); + context->mdlo = thread_context.lo; + context->mdhi = thread_context.hi; + context->cp0_epc = thread_context.cp0_epc; + context->cp0_badvaddr = thread_context.cp0_badvaddr; + context->cp0_status = thread_context.cp0_status; + context->cp0_cause = thread_context.cp0_cause; + + memcpy(&context->fpregs, &float_context.fpregs, sizeof(context->fpregs)); + context->fpcsr = float_context.fpcsr; + context->fir = float_context.fpu_id; +} + +#endif // ARCH_CPU_MIPS_FAMILY || DOXYGEN + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_LINUX_CPU_CONTEXT_LINUX_H_ diff --git a/shared/sentry/external/crashpad/snapshot/linux/debug_rendezvous.cc b/shared/sentry/external/crashpad/snapshot/linux/debug_rendezvous.cc new file mode 100644 index 000000000..e0af2b3b1 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/linux/debug_rendezvous.cc @@ -0,0 +1,159 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/linux/debug_rendezvous.h" + +#include + +#include + +#include "base/logging.h" +#include "build/build_config.h" + +#if BUILDFLAG(IS_ANDROID) +#include +#endif + +namespace crashpad { + +namespace { + +struct Traits32 { + using Integer = int32_t; + using Address = uint32_t; +}; + +struct Traits64 { + using Integer = int64_t; + using Address = uint64_t; +}; + +template +struct DebugRendezvousSpecific { + typename Traits::Integer r_version; + typename Traits::Address r_map; + typename Traits::Address r_brk; + typename Traits::Integer r_state; + typename Traits::Address r_ldbase; +}; + +template +struct LinkEntrySpecific { + typename Traits::Address l_addr; + typename Traits::Address l_name; + typename Traits::Address l_ld; + typename Traits::Address l_next; + typename Traits::Address l_prev; +}; + +template +bool ReadLinkEntry(const ProcessMemoryRange& memory, + LinuxVMAddress* address, + DebugRendezvous::LinkEntry* entry_out) { + LinkEntrySpecific entry; + if (!memory.Read(*address, sizeof(entry), &entry)) { + return false; + } + + std::string name; + if (!memory.ReadCStringSizeLimited(entry.l_name, 4096, &name)) { + name.clear(); + } + + entry_out->load_bias = entry.l_addr; + entry_out->dynamic_array = entry.l_ld; + entry_out->name.swap(name); + + *address = entry.l_next; + return true; +} + +} // namespace + +DebugRendezvous::LinkEntry::LinkEntry() + : name(), load_bias(0), dynamic_array(0) {} + +DebugRendezvous::DebugRendezvous() + : modules_(), executable_(), initialized_() {} + +DebugRendezvous::~DebugRendezvous() {} + +bool DebugRendezvous::Initialize(const ProcessMemoryRange& memory, + LinuxVMAddress address) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + if (!(memory.Is64Bit() ? InitializeSpecific(memory, address) + : InitializeSpecific(memory, address))) { + return false; + } + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +const DebugRendezvous::LinkEntry* DebugRendezvous::Executable() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &executable_; +} + +const std::vector& DebugRendezvous::Modules() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return modules_; +} + +template +bool DebugRendezvous::InitializeSpecific(const ProcessMemoryRange& memory, + LinuxVMAddress address) { + DebugRendezvousSpecific debug; + if (!memory.Read(address, sizeof(debug), &debug)) { + return false; + } + if (debug.r_version != 1) { + LOG(ERROR) << "unexpected version " << debug.r_version; + return false; + } + + LinuxVMAddress link_entry_address = debug.r_map; + if (!ReadLinkEntry(memory, &link_entry_address, &executable_)) { + return false; + } + + std::set visited; + while (link_entry_address) { + if (!visited.insert(link_entry_address).second) { + LOG(ERROR) << "cycle at address 0x" << std::hex << link_entry_address; + return false; + } + + LinkEntry entry; + if (!ReadLinkEntry(memory, &link_entry_address, &entry)) { + return false; + } + modules_.push_back(entry); + } + +#if BUILDFLAG(IS_ANDROID) + // Android P (API 28) mistakenly places the vdso in the first entry in the + // link map. + const int android_runtime_api = android_get_device_api_level(); + if (android_runtime_api == 28 && executable_.name == "[vdso]") { + LinkEntry executable = modules_[0]; + modules_[0] = executable_; + executable_ = executable; + } +#endif // BUILDFLAG(IS_ANDROID) + + return true; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/linux/debug_rendezvous.h b/shared/sentry/external/crashpad/snapshot/linux/debug_rendezvous.h new file mode 100644 index 000000000..f92051740 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/linux/debug_rendezvous.h @@ -0,0 +1,89 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_LINUX_DEBUG_RENDEZVOUS_H_ +#define CRASHPAD_SNAPSHOT_LINUX_DEBUG_RENDEZVOUS_H_ + +#include +#include + +#include "util/linux/address_types.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/process/process_memory_range.h" + +namespace crashpad { + +//! \brief Reads an `r_debug` struct defined in `` via +//! ProcessMemoryRange. +class DebugRendezvous { + public: + //! \brief An entry in the dynamic linker's list of loaded objects. + //! + //! All of these values should be checked before use. Whether and how they are + //! populated may vary by dynamic linker. + struct LinkEntry { + LinkEntry(); + + //! \brief A filename identifying the object. + std::string name; + + //! \brief The difference between the preferred load address in the ELF file + //! and the actual loaded address in memory. + VMAddress load_bias; + + //! \brief The address of the dynamic array for this object. + LinuxVMAddress dynamic_array; + }; + + DebugRendezvous(); + + DebugRendezvous(const DebugRendezvous&) = delete; + DebugRendezvous& operator=(const DebugRendezvous&) = delete; + + ~DebugRendezvous(); + + //! \brief Initializes this object by reading an `r_debug` struct from a + //! target process. + //! + //! This method must be called successfully prior to calling any other method + //! in this class. + //! + //! \param[in] memory A memory reader for the remote process. + //! \param[in] address The address of an `r_debug` struct in the remote + //! process. + //! \return `true` on success. `false` on failure with a message logged. + bool Initialize(const ProcessMemoryRange& memory, LinuxVMAddress address); + + //! \brief Returns the LinkEntry for the main executable. + const LinkEntry* Executable() const; + + //! \brief Returns a vector of modules found in the link map. + //! + //! This list excludes the entry for the executable and may include entries + //! for the VDSO and loader. + const std::vector& Modules() const; + + private: + template + bool InitializeSpecific(const ProcessMemoryRange& memory, + LinuxVMAddress address); + + std::vector modules_; + LinkEntry executable_; + InitializationStateDcheck initialized_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_LINUX_DEBUG_RENDEZVOUS_H_ diff --git a/shared/sentry/external/crashpad/snapshot/linux/debug_rendezvous_test.cc b/shared/sentry/external/crashpad/snapshot/linux/debug_rendezvous_test.cc new file mode 100644 index 000000000..9fa6d6311 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/linux/debug_rendezvous_test.cc @@ -0,0 +1,270 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/linux/debug_rendezvous.h" + +#include +#include +#include + +#include + +#include "base/format_macros.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_piece.h" +#include "base/strings/stringprintf.h" +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "snapshot/elf/elf_image_reader.h" +#include "snapshot/linux/test_modules.h" +#include "test/linux/fake_ptrace_connection.h" +#include "test/main_arguments.h" +#include "test/multiprocess.h" +#include "util/linux/address_types.h" +#include "util/linux/auxiliary_vector.h" +#include "util/linux/direct_ptrace_connection.h" +#include "util/linux/memory_map.h" +#include "util/misc/address_sanitizer.h" +#include "util/misc/memory_sanitizer.h" +#include "util/numeric/safe_assignment.h" +#include "util/process/process_memory_linux.h" +#include "util/process/process_memory_range.h" + +#if BUILDFLAG(IS_ANDROID) +#include +#endif + +namespace crashpad { +namespace test { +namespace { + +void ExpectLoadBias(bool is_64_bit, + VMAddress unsigned_bias, + VMOffset signed_bias) { + if (is_64_bit) { + EXPECT_EQ(unsigned_bias, static_cast(signed_bias)); + } else { + uint32_t unsigned_bias32; + ASSERT_TRUE(AssignIfInRange(&unsigned_bias32, unsigned_bias)); + + uint32_t casted_bias32 = static_cast(signed_bias); + EXPECT_EQ(unsigned_bias32, casted_bias32); + } +} + +void TestAgainstTarget(PtraceConnection* connection) { + // Use ElfImageReader on the main executable which can tell us the debug + // address. glibc declares the symbol _r_debug in link.h which we can use to + // get the address, but Android does not. + AuxiliaryVector aux; + ASSERT_TRUE(aux.Initialize(connection)); + + LinuxVMAddress phdrs; + ASSERT_TRUE(aux.GetValue(AT_PHDR, &phdrs)); + + MemoryMap mappings; + ASSERT_TRUE(mappings.Initialize(connection)); + + const MemoryMap::Mapping* phdr_mapping = mappings.FindMapping(phdrs); + ASSERT_TRUE(phdr_mapping); + + auto exe_mappings = mappings.FindFilePossibleMmapStarts(*phdr_mapping); + ASSERT_EQ(exe_mappings->Count(), 1u); + LinuxVMAddress elf_address = exe_mappings->Next()->range.Base(); + + ProcessMemoryLinux memory(connection); + ProcessMemoryRange range; + ASSERT_TRUE(range.Initialize(&memory, connection->Is64Bit())); + + ElfImageReader exe_reader; + ASSERT_TRUE(exe_reader.Initialize(range, elf_address)); + LinuxVMAddress debug_address; + ASSERT_TRUE(exe_reader.GetDebugAddress(&debug_address)); + + VMAddress exe_dynamic_address = 0; + if (exe_reader.GetDynamicArrayAddress(&exe_dynamic_address)) { + CheckedLinuxAddressRange exe_range( + connection->Is64Bit(), exe_reader.Address(), exe_reader.Size()); + EXPECT_TRUE(exe_range.ContainsValue(exe_dynamic_address)); + } + + // start the actual tests + DebugRendezvous debug; + ASSERT_TRUE(debug.Initialize(range, debug_address)); + +#if BUILDFLAG(IS_ANDROID) + const int android_runtime_api = android_get_device_api_level(); + ASSERT_GE(android_runtime_api, 1); + + base::FilePath exe_name(base::FilePath(GetMainArguments()[0]).BaseName()); + EXPECT_NE(debug.Executable()->name.find(exe_name.value()), std::string::npos); + + // Android's loader doesn't set the dynamic array for the executable in the + // link map until Android 10.0 (API 29). + if (android_runtime_api >= 29) { + EXPECT_EQ(debug.Executable()->dynamic_array, exe_dynamic_address); + } else { + EXPECT_EQ(debug.Executable()->dynamic_array, 0u); + } +#else + // glibc's loader implements most of the tested features that Android's was + // missing but has since gained. + const int android_runtime_api = std::numeric_limits::max(); + + // glibc's loader does not set the name for the executable. + EXPECT_TRUE(debug.Executable()->name.empty()); + EXPECT_EQ(debug.Executable()->dynamic_array, exe_dynamic_address); +#endif // BUILDFLAG(IS_ANDROID) + + // Android's loader doesn't set the load bias until Android 4.3 (API 18). + if (android_runtime_api >= 18) { + ExpectLoadBias(connection->Is64Bit(), + debug.Executable()->load_bias, + exe_reader.GetLoadBias()); + } else { + EXPECT_EQ(debug.Executable()->load_bias, 0u); + } + + for (const DebugRendezvous::LinkEntry& module : debug.Modules()) { + SCOPED_TRACE(base::StringPrintf("name %s, load_bias 0x%" PRIx64 + ", dynamic_array 0x%" PRIx64, + module.name.c_str(), + module.load_bias, + module.dynamic_array)); + const bool is_android_loader = (module.name == "/system/bin/linker" || + module.name == "/system/bin/linker64"); + + // Android's loader doesn't set its own dynamic array until Android 4.2 + // (API 17). + if (is_android_loader && android_runtime_api < 17) { + EXPECT_EQ(module.dynamic_array, 0u); + EXPECT_EQ(module.load_bias, 0u); + continue; + } + + ASSERT_TRUE(module.dynamic_array); + const MemoryMap::Mapping* dyn_mapping = + mappings.FindMapping(module.dynamic_array); + ASSERT_TRUE(dyn_mapping); + + auto possible_mappings = mappings.FindFilePossibleMmapStarts(*dyn_mapping); + ASSERT_GE(possible_mappings->Count(), 1u); + + std::unique_ptr module_reader; +#if !BUILDFLAG(IS_ANDROID) + const MemoryMap::Mapping* module_mapping = nullptr; +#endif + const MemoryMap::Mapping* mapping = nullptr; + while ((mapping = possible_mappings->Next())) { + auto parsed_module = std::make_unique(); + VMAddress dynamic_address; + if (parsed_module->Initialize( + range, mapping->range.Base(), possible_mappings->Count() == 0) && + parsed_module->GetDynamicArrayAddress(&dynamic_address) && + dynamic_address == module.dynamic_array) { + module_reader = std::move(parsed_module); +#if !BUILDFLAG(IS_ANDROID) + module_mapping = mapping; +#endif + break; + } + } + ASSERT_TRUE(module_reader.get()); + +#if BUILDFLAG(IS_ANDROID) + EXPECT_FALSE(module.name.empty()); +#else + // glibc's loader doesn't always set the name in the link map for the vdso. + EXPECT_PRED4( + [](const std::string mapping_name, + int device, + int inode, + const std::string& module_name) { + const bool is_vdso_mapping = + device == 0 && inode == 0 && mapping_name == "[vdso]"; +#if defined(ARCH_CPU_X86) + static constexpr char kPrefix[] = "linux-gate.so."; +#else + static constexpr char kPrefix[] = "linux-vdso.so."; +#endif + return is_vdso_mapping == + (module_name.empty() || + module_name.compare(0, strlen(kPrefix), kPrefix) == 0); + }, + module_mapping->name, + module_mapping->device, + module_mapping->inode, + module.name); +#endif // BUILDFLAG(IS_ANDROID) + + // Android's loader stops setting its own load bias after Android 4.4.4 + // (API 20) until Android 6.0 (API 23). + if (is_android_loader && android_runtime_api > 20 && + android_runtime_api < 23) { + EXPECT_EQ(module.load_bias, 0u); + } else { + ExpectLoadBias(connection->Is64Bit(), + module.load_bias, + static_cast(module_reader->GetLoadBias())); + } + + CheckedLinuxAddressRange module_range( + connection->Is64Bit(), module_reader->Address(), module_reader->Size()); + EXPECT_TRUE(module_range.ContainsValue(module.dynamic_array)); + } +} + +TEST(DebugRendezvous, Self) { +#if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) + const std::string module_name = "test_module.so"; + const std::string module_soname = "test_module_soname"; + ScopedModuleHandle empty_test_module( + LoadTestModule(module_name, module_soname)); + ASSERT_TRUE(empty_test_module.valid()); +#endif // !ADDRESS_SANITIZER && !MEMORY_SANITIZER + + FakePtraceConnection connection; + ASSERT_TRUE(connection.Initialize(getpid())); + + TestAgainstTarget(&connection); +} + +class ChildTest : public Multiprocess { + public: + ChildTest() {} + + ChildTest(const ChildTest&) = delete; + ChildTest& operator=(const ChildTest&) = delete; + + ~ChildTest() {} + + private: + void MultiprocessParent() { + DirectPtraceConnection connection; + ASSERT_TRUE(connection.Initialize(ChildPID())); + + TestAgainstTarget(&connection); + } + + void MultiprocessChild() { CheckedReadFileAtEOF(ReadPipeHandle()); } +}; + +TEST(DebugRendezvous, Child) { + ChildTest test; + test.Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/linux/exception_snapshot_linux.cc b/shared/sentry/external/crashpad/snapshot/linux/exception_snapshot_linux.cc new file mode 100644 index 000000000..efc9e5694 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/linux/exception_snapshot_linux.cc @@ -0,0 +1,500 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/linux/exception_snapshot_linux.h" + +#include + +#include "base/logging.h" +#include "snapshot/linux/capture_memory_delegate_linux.h" +#include "snapshot/linux/cpu_context_linux.h" +#include "snapshot/linux/process_reader_linux.h" +#include "snapshot/linux/signal_context.h" +#include "util/linux/traits.h" +#include "util/misc/reinterpret_bytes.h" +#include "util/numeric/safe_assignment.h" +#include "util/posix/signals.h" + +namespace crashpad { +namespace internal { + +ExceptionSnapshotLinux::ExceptionSnapshotLinux() + : ExceptionSnapshot(), + context_union_(), + context_(), + codes_(), + thread_id_(0), + exception_address_(0), + signal_number_(0), + signal_code_(0), + initialized_() {} + +ExceptionSnapshotLinux::~ExceptionSnapshotLinux() {} + +#if defined(ARCH_CPU_X86_FAMILY) + +template <> +bool ExceptionSnapshotLinux::ReadContext( + ProcessReaderLinux* reader, + LinuxVMAddress context_address) { + UContext ucontext; + if (!reader->Memory()->Read(context_address, sizeof(ucontext), &ucontext)) { + LOG(ERROR) << "Couldn't read ucontext"; + return false; + } + + context_.architecture = kCPUArchitectureX86; + context_.x86 = &context_union_.x86; + + if (!ucontext.mcontext.fpptr) { + InitializeCPUContextX86_NoFloatingPoint(ucontext.mcontext.gprs, + context_.x86); + return true; + } + + SignalFloatContext32 fprs; + if (!reader->Memory()->Read(ucontext.mcontext.fpptr, sizeof(fprs), &fprs)) { + LOG(ERROR) << "Couldn't read float context"; + return false; + } + + if (fprs.magic == X86_FXSR_MAGIC) { + InitializeCPUContextX86_NoFloatingPoint(ucontext.mcontext.gprs, + context_.x86); + if (!reader->Memory()->Read( + ucontext.mcontext.fpptr + offsetof(SignalFloatContext32, fxsave), + sizeof(CPUContextX86::Fxsave), + &context_.x86->fxsave)) { + LOG(ERROR) << "Couldn't read fxsave"; + return false; + } + } else if (fprs.magic == 0xffff) { + InitializeCPUContextX86(ucontext.mcontext.gprs, fprs, context_.x86); + } else { + LOG(ERROR) << "unexpected magic 0x" << std::hex << fprs.magic; + return false; + } + + return true; +} + +template <> +bool ExceptionSnapshotLinux::ReadContext( + ProcessReaderLinux* reader, + LinuxVMAddress context_address) { + UContext ucontext; + if (!reader->Memory()->Read(context_address, sizeof(ucontext), &ucontext)) { + LOG(ERROR) << "Couldn't read ucontext"; + return false; + } + + context_.architecture = kCPUArchitectureX86_64; + context_.x86_64 = &context_union_.x86_64; + + if (!ucontext.mcontext.fpptr) { + InitializeCPUContextX86_64_NoFloatingPoint(ucontext.mcontext.gprs, + context_.x86_64); + return true; + } + + SignalFloatContext64 fprs; + if (!reader->Memory()->Read(ucontext.mcontext.fpptr, sizeof(fprs), &fprs)) { + LOG(ERROR) << "Couldn't read float context"; + return false; + } + + InitializeCPUContextX86_64(ucontext.mcontext.gprs, fprs, context_.x86_64); + return true; +} + +#elif defined(ARCH_CPU_ARM_FAMILY) + +template <> +bool ExceptionSnapshotLinux::ReadContext( + ProcessReaderLinux* reader, + LinuxVMAddress context_address) { + context_.architecture = kCPUArchitectureARM; + context_.arm = &context_union_.arm; + + CPUContextARM* dest_context = context_.arm; + const ProcessMemory* memory = reader->Memory(); + + LinuxVMAddress gprs_address = + context_address + offsetof(UContext, mcontext32) + + offsetof(ContextTraits32::MContext32, gprs); + + SignalThreadContext32 thread_context; + if (!memory->Read(gprs_address, sizeof(thread_context), &thread_context)) { + LOG(ERROR) << "Couldn't read gprs"; + return false; + } + InitializeCPUContextARM_NoFloatingPoint(thread_context, dest_context); + + LinuxVMAddress reserved_address = + context_address + offsetof(UContext, reserved); + if ((reserved_address & 7) != 0) { + LOG(ERROR) << "invalid alignment 0x" << std::hex << reserved_address; + return false; + } + + constexpr VMSize kMaxContextSpace = 1024; + + ProcessMemoryRange range; + if (!range.Initialize(memory, false, reserved_address, kMaxContextSpace)) { + return false; + } + + do { + CoprocessorContextHead head; + if (!range.Read(reserved_address, sizeof(head), &head)) { + LOG(ERROR) << "missing context terminator"; + return false; + } + reserved_address += sizeof(head); + + switch (head.magic) { + case VFP_MAGIC: + if (head.size != sizeof(SignalVFPContext) + sizeof(head)) { + LOG(ERROR) << "unexpected vfp context size " << head.size; + return false; + } + static_assert( + sizeof(SignalVFPContext::vfp) == sizeof(dest_context->vfp_regs), + "vfp context size mismatch"); + if (!range.Read(reserved_address + offsetof(SignalVFPContext, vfp), + sizeof(dest_context->vfp_regs), + &dest_context->vfp_regs)) { + LOG(ERROR) << "Couldn't read vfp"; + return false; + } + dest_context->have_vfp_regs = true; + return true; + + case CRUNCH_MAGIC: + case IWMMXT_MAGIC: + case DUMMY_MAGIC: + reserved_address += head.size - sizeof(head); + continue; + + case 0: + return true; + + default: + LOG(ERROR) << "invalid magic number 0x" << std::hex << head.magic; + return false; + } + } while (true); +} + +template <> +bool ExceptionSnapshotLinux::ReadContext( + ProcessReaderLinux* reader, + LinuxVMAddress context_address) { + context_.architecture = kCPUArchitectureARM64; + context_.arm64 = &context_union_.arm64; + + CPUContextARM64* dest_context = context_.arm64; + const ProcessMemory* memory = reader->Memory(); + + LinuxVMAddress gprs_address = + context_address + offsetof(UContext, mcontext64) + + offsetof(ContextTraits64::MContext64, gprs); + + ThreadContext::t64_t thread_context; + if (!memory->Read(gprs_address, sizeof(thread_context), &thread_context)) { + LOG(ERROR) << "Couldn't read gprs"; + return false; + } + InitializeCPUContextARM64_NoFloatingPoint(thread_context, dest_context); + + LinuxVMAddress reserved_address = + context_address + offsetof(UContext, reserved); + if ((reserved_address & 15) != 0) { + LOG(ERROR) << "invalid alignment 0x" << std::hex << reserved_address; + return false; + } + + constexpr VMSize kMaxContextSpace = 4096; + + ProcessMemoryRange range; + if (!range.Initialize(memory, true, reserved_address, kMaxContextSpace)) { + return false; + } + + do { + CoprocessorContextHead head; + if (!range.Read(reserved_address, sizeof(head), &head)) { + LOG(ERROR) << "missing context terminator"; + return false; + } + reserved_address += sizeof(head); + + switch (head.magic) { + case FPSIMD_MAGIC: + if (head.size != sizeof(SignalFPSIMDContext) + sizeof(head)) { + LOG(ERROR) << "unexpected fpsimd context size " << head.size; + return false; + } + SignalFPSIMDContext fpsimd; + if (!range.Read(reserved_address, sizeof(fpsimd), &fpsimd)) { + LOG(ERROR) << "Couldn't read fpsimd " << head.size; + return false; + } + InitializeCPUContextARM64_OnlyFPSIMD(fpsimd, dest_context); + return true; + + case ESR_MAGIC: + case EXTRA_MAGIC: + reserved_address += head.size - sizeof(head); + continue; + + case 0: + LOG(WARNING) << "fpsimd not found"; + return true; + + default: + LOG(ERROR) << "invalid magic number 0x" << std::hex << head.magic; + return false; + } + } while (true); +} + +#elif defined(ARCH_CPU_MIPS_FAMILY) + +template +static bool ReadContext(ProcessReaderLinux* reader, + LinuxVMAddress context_address, + typename Traits::CPUContext* dest_context) { + const ProcessMemory* memory = reader->Memory(); + + LinuxVMAddress gregs_address = context_address + + offsetof(UContext, mcontext) + + offsetof(typename Traits::MContext, gregs); + + typename Traits::SignalThreadContext thread_context; + if (!memory->Read(gregs_address, sizeof(thread_context), &thread_context)) { + LOG(ERROR) << "Couldn't read gregs"; + return false; + } + + LinuxVMAddress fpregs_address = context_address + + offsetof(UContext, mcontext) + + offsetof(typename Traits::MContext, fpregs); + + typename Traits::SignalFloatContext fp_context; + if (!memory->Read(fpregs_address, sizeof(fp_context), &fp_context)) { + LOG(ERROR) << "Couldn't read fpregs"; + return false; + } + + InitializeCPUContextMIPS(thread_context, fp_context, dest_context); + + return true; +} + +template <> +bool ExceptionSnapshotLinux::ReadContext( + ProcessReaderLinux* reader, + LinuxVMAddress context_address) { + context_.architecture = kCPUArchitectureMIPSEL; + context_.mipsel = &context_union_.mipsel; + + return internal::ReadContext( + reader, context_address, context_.mipsel); +} + +template <> +bool ExceptionSnapshotLinux::ReadContext( + ProcessReaderLinux* reader, + LinuxVMAddress context_address) { + context_.architecture = kCPUArchitectureMIPS64EL; + context_.mips64 = &context_union_.mips64; + + return internal::ReadContext( + reader, context_address, context_.mips64); +} + +#endif // ARCH_CPU_X86_FAMILY + +bool ExceptionSnapshotLinux::Initialize( + ProcessReaderLinux* process_reader, + LinuxVMAddress siginfo_address, + LinuxVMAddress context_address, + pid_t thread_id, + uint32_t* gather_indirectly_referenced_memory_cap) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + thread_id_ = thread_id; + const ProcessReaderLinux::Thread* thread = nullptr; + for (const auto& loop_thread : process_reader->Threads()) { + if (thread_id == loop_thread.tid) { + thread = &loop_thread; + break; + } + } + if (!thread) { + // This is allowed until {ProcessReaderLinux::InitializeThreads()} is + // improved to support target threads in the same thread group. + LOG(WARNING) << "thread ID " << thread_id << " not found in process"; + } + + if (process_reader->Is64Bit()) { + if (!ReadContext(process_reader, context_address) || + !ReadSiginfo(process_reader, siginfo_address)) { + return false; + } + } else { + if (!ReadContext(process_reader, context_address) || + !ReadSiginfo(process_reader, siginfo_address)) { + return false; + } + } + + CaptureMemoryDelegateLinux capture_memory_delegate( + process_reader, + thread, + &extra_memory_, + gather_indirectly_referenced_memory_cap); + CaptureMemory::PointedToByContext(context_, &capture_memory_delegate); + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +template +bool ExceptionSnapshotLinux::ReadSiginfo(ProcessReaderLinux* reader, + LinuxVMAddress siginfo_address) { + Siginfo siginfo; + if (!reader->Memory()->Read(siginfo_address, sizeof(siginfo), &siginfo)) { + LOG(ERROR) << "Couldn't read siginfo"; + return false; + } + + signal_number_ = siginfo.signo; + signal_code_ = siginfo.code; + + uint64_t extra_code; +#define PUSH_CODE(value) \ + do { \ + if (!ReinterpretBytes(value, &extra_code)) { \ + LOG(ERROR) << "bad code"; \ + return false; \ + } \ + codes_.push_back(extra_code); \ + } while (false) + + switch (siginfo.signo) { + case SIGILL: + case SIGFPE: + case SIGSEGV: + case SIGBUS: + case SIGTRAP: + exception_address_ = siginfo.address; + break; + + case SIGPOLL: // SIGIO + PUSH_CODE(siginfo.band); + PUSH_CODE(siginfo.fd); + break; + + case SIGSYS: + exception_address_ = siginfo.call_address; + PUSH_CODE(siginfo.syscall); + PUSH_CODE(siginfo.arch); + break; + + case SIGALRM: + case SIGVTALRM: + case SIGPROF: + PUSH_CODE(siginfo.timerid); + PUSH_CODE(siginfo.overrun); + PUSH_CODE(siginfo.sigval.sigval); + break; + + case SIGABRT: + case SIGQUIT: + case SIGXCPU: + case SIGXFSZ: + case SIGHUP: + case SIGINT: + case SIGPIPE: + case SIGTERM: + case SIGUSR1: + case SIGUSR2: +#if defined(SIGEMT) + case SIGEMT: +#endif // SIGEMT +#if defined(SIGPWR) + case SIGPWR: +#endif // SIGPWR +#if defined(SIGSTKFLT) + case SIGSTKFLT: +#endif // SIGSTKFLT + PUSH_CODE(siginfo.pid); + PUSH_CODE(siginfo.uid); + PUSH_CODE(siginfo.sigval.sigval); + break; + + case Signals::kSimulatedSigno: + break; + + default: + LOG(WARNING) << "Unhandled signal " << siginfo.signo; + } + + return true; +} + +const CPUContext* ExceptionSnapshotLinux::Context() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &context_; +} + +uint64_t ExceptionSnapshotLinux::ThreadID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_id_; +} + +uint32_t ExceptionSnapshotLinux::Exception() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return signal_number_; +} + +uint32_t ExceptionSnapshotLinux::ExceptionInfo() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return signal_code_; +} + +uint64_t ExceptionSnapshotLinux::ExceptionAddress() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_address_; +} + +const std::vector& ExceptionSnapshotLinux::Codes() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return codes_; +} + +std::vector ExceptionSnapshotLinux::ExtraMemory() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector result; + result.reserve(extra_memory_.size()); + for (const auto& em : extra_memory_) { + result.push_back(em.get()); + } + return result; +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/linux/exception_snapshot_linux.h b/shared/sentry/external/crashpad/snapshot/linux/exception_snapshot_linux.h new file mode 100644 index 000000000..05f6004e1 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/linux/exception_snapshot_linux.h @@ -0,0 +1,107 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_LINUX_EXCEPTION_SNAPSHOT_LINUX_H_ +#define CRASHPAD_SNAPSHOT_LINUX_EXCEPTION_SNAPSHOT_LINUX_H_ + +#include +#include + +#include + +#include "build/build_config.h" +#include "snapshot/cpu_context.h" +#include "snapshot/exception_snapshot.h" +#include "snapshot/linux/process_reader_linux.h" +#include "snapshot/memory_snapshot.h" +#include "snapshot/memory_snapshot_generic.h" +#include "util/linux/address_types.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { +namespace internal { + +//! \brief An ExceptionSnapshot of an signal received by a running (or crashed) +//! process on a Linux system. +class ExceptionSnapshotLinux final : public ExceptionSnapshot { + public: + ExceptionSnapshotLinux(); + + ExceptionSnapshotLinux(const ExceptionSnapshotLinux&) = delete; + ExceptionSnapshotLinux& operator=(const ExceptionSnapshotLinux&) = delete; + + ~ExceptionSnapshotLinux() override; + + //! \brief Initializes the object. + //! + //! \param[in] process_reader A ProcessReaderLinux for the process that + //! received + //! the signal. + //! \param[in] siginfo_address The address in the target process' address + //! space of the siginfo_t passed to the signal handler. + //! \param[in] context_address The address in the target process' address + //! space of the ucontext_t passed to the signal handler. + //! \param[in] thread_id The thread ID of the thread that received the signal. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(ProcessReaderLinux* process_reader, + LinuxVMAddress siginfo_address, + LinuxVMAddress context_address, + pid_t thread_id, + uint32_t* gather_indirectly_referenced_memory_cap); + + // ExceptionSnapshot: + + const CPUContext* Context() const override; + uint64_t ThreadID() const override; + uint32_t Exception() const override; + uint32_t ExceptionInfo() const override; + uint64_t ExceptionAddress() const override; + const std::vector& Codes() const override; + virtual std::vector ExtraMemory() const override; + + private: + template + bool ReadSiginfo(ProcessReaderLinux* reader, LinuxVMAddress siginfo_address); + + template + bool ReadContext(ProcessReaderLinux* reader, LinuxVMAddress context_address); + + union { +#if defined(ARCH_CPU_X86_FAMILY) + CPUContextX86 x86; + CPUContextX86_64 x86_64; +#elif defined(ARCH_CPU_ARM_FAMILY) + CPUContextARM arm; + CPUContextARM64 arm64; +#elif defined(ARCH_CPU_MIPS_FAMILY) + CPUContextMIPS mipsel; + CPUContextMIPS64 mips64; +#endif + } context_union_; + CPUContext context_; + std::vector codes_; + std::vector> extra_memory_; + uint64_t thread_id_; + uint64_t exception_address_; + uint32_t signal_number_; + uint32_t signal_code_; + InitializationStateDcheck initialized_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_LINUX_EXCEPTION_SNAPSHOT_LINUX_H_ diff --git a/shared/sentry/external/crashpad/snapshot/linux/exception_snapshot_linux_test.cc b/shared/sentry/external/crashpad/snapshot/linux/exception_snapshot_linux_test.cc new file mode 100644 index 000000000..a045f3659 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/linux/exception_snapshot_linux_test.cc @@ -0,0 +1,502 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/linux/exception_snapshot_linux.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include "base/bit_cast.h" +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" +#include "snapshot/cpu_architecture.h" +#include "snapshot/linux/process_reader_linux.h" +#include "snapshot/linux/signal_context.h" +#include "sys/syscall.h" +#include "test/errors.h" +#include "test/linux/fake_ptrace_connection.h" +#include "util/linux/address_types.h" +#include "util/misc/clock.h" +#include "util/misc/from_pointer_cast.h" +#include "util/posix/signals.h" +#include "util/synchronization/semaphore.h" + +namespace crashpad { +namespace test { +namespace { + +pid_t gettid() { + return syscall(SYS_gettid); +} + +#if defined(ARCH_CPU_X86) +struct FxsaveUContext { + ucontext_t ucontext; + CPUContextX86::Fxsave fxsave; +}; +using NativeCPUContext = FxsaveUContext; + +void InitializeContext(NativeCPUContext* context) { + context->ucontext.uc_mcontext.gregs[REG_EAX] = 0xabcd1234; + context->ucontext.uc_mcontext.fpregs = &context->ucontext.__fpregs_mem; + // glibc and bionic use an unsigned long for status, but the kernel treats + // status as two uint16_t, with the upper 16 bits called "magic" which, if set + // to X86_FXSR_MAGIC, indicate that an fxsave follows. + reinterpret_cast(&context->ucontext.__fpregs_mem.status)[1] = + X86_FXSR_MAGIC; + memset(&context->fxsave, 43, sizeof(context->fxsave)); +} + +void ExpectContext(const CPUContext& actual, const NativeCPUContext& expected) { + EXPECT_EQ(actual.architecture, kCPUArchitectureX86); + EXPECT_EQ(actual.x86->eax, + bit_cast(expected.ucontext.uc_mcontext.gregs[REG_EAX])); + for (unsigned int byte_offset = 0; byte_offset < sizeof(actual.x86->fxsave); + ++byte_offset) { + SCOPED_TRACE(base::StringPrintf("byte offset = %u\n", byte_offset)); + EXPECT_EQ(reinterpret_cast(&actual.x86->fxsave)[byte_offset], + reinterpret_cast(&expected.fxsave)[byte_offset]); + } +} +#elif defined(ARCH_CPU_X86_64) +using NativeCPUContext = ucontext_t; + +void InitializeContext(NativeCPUContext* context) { + context->uc_mcontext.gregs[REG_RAX] = 0xabcd1234abcd1234; + context->uc_mcontext.fpregs = &context->__fpregs_mem; + memset(&context->__fpregs_mem, 44, sizeof(context->__fpregs_mem)); +} + +void ExpectContext(const CPUContext& actual, const NativeCPUContext& expected) { + EXPECT_EQ(actual.architecture, kCPUArchitectureX86_64); + EXPECT_EQ(actual.x86_64->rax, + bit_cast(expected.uc_mcontext.gregs[REG_RAX])); + for (unsigned int byte_offset = 0; + byte_offset < sizeof(actual.x86_64->fxsave); + ++byte_offset) { + SCOPED_TRACE(base::StringPrintf("byte offset = %u\n", byte_offset)); + EXPECT_EQ( + reinterpret_cast(&actual.x86_64->fxsave)[byte_offset], + reinterpret_cast(&expected.__fpregs_mem)[byte_offset]); + } +} +#elif defined(ARCH_CPU_ARMEL) +// A native ucontext_t on ARM doesn't have enough regspace (yet) to hold all of +// the different possible coprocessor contexts at once. However, the ABI allows +// it and the native regspace may be expanded in the future. Append some extra +// space so this is testable now. +struct NativeCPUContext { + ucontext_t ucontext; + char extra[1024]; +}; + +struct CrunchContext { + uint32_t mvdx[16][2]; + uint32_t mvax[4][3]; + uint32_t dspsc[2]; +}; + +struct IWMMXTContext { + uint32_t save[38]; +}; + +struct TestCoprocessorContext { + struct { + internal::CoprocessorContextHead head; + CrunchContext context; + } crunch; + struct { + internal::CoprocessorContextHead head; + IWMMXTContext context; + } iwmmxt; + struct { + internal::CoprocessorContextHead head; + IWMMXTContext context; + } dummy; + struct { + internal::CoprocessorContextHead head; + internal::SignalVFPContext context; + } vfp; + internal::CoprocessorContextHead terminator; +}; + +void InitializeContext(NativeCPUContext* context) { + memset(context, 'x', sizeof(*context)); + + for (int index = 0; index < (&context->ucontext.uc_mcontext.fault_address - + &context->ucontext.uc_mcontext.arm_r0); + ++index) { + (&context->ucontext.uc_mcontext.arm_r0)[index] = index; + } + + static_assert( + sizeof(TestCoprocessorContext) <= + sizeof(context->ucontext.uc_regspace) + sizeof(context->extra), + "Insufficient context space"); + auto test_context = + reinterpret_cast(context->ucontext.uc_regspace); + + test_context->crunch.head.magic = CRUNCH_MAGIC; + test_context->crunch.head.size = sizeof(test_context->crunch); + memset( + &test_context->crunch.context, 'c', sizeof(test_context->crunch.context)); + + test_context->iwmmxt.head.magic = IWMMXT_MAGIC; + test_context->iwmmxt.head.size = sizeof(test_context->iwmmxt); + memset( + &test_context->iwmmxt.context, 'i', sizeof(test_context->iwmmxt.context)); + + test_context->dummy.head.magic = DUMMY_MAGIC; + test_context->dummy.head.size = sizeof(test_context->dummy); + memset( + &test_context->dummy.context, 'd', sizeof(test_context->dummy.context)); + + test_context->vfp.head.magic = VFP_MAGIC; + test_context->vfp.head.size = sizeof(test_context->vfp); + memset(&test_context->vfp.context, 'v', sizeof(test_context->vfp.context)); + for (size_t reg = 0; reg < std::size(test_context->vfp.context.vfp.fpregs); + ++reg) { + test_context->vfp.context.vfp.fpregs[reg] = reg; + } + test_context->vfp.context.vfp.fpscr = 42; + + test_context->terminator.magic = 0; + test_context->terminator.size = 0; +} + +void ExpectContext(const CPUContext& actual, const NativeCPUContext& expected) { + EXPECT_EQ(actual.architecture, kCPUArchitectureARM); + + EXPECT_EQ(memcmp(actual.arm->regs, + &expected.ucontext.uc_mcontext.arm_r0, + sizeof(actual.arm->regs)), + 0); + EXPECT_EQ(actual.arm->fp, expected.ucontext.uc_mcontext.arm_fp); + EXPECT_EQ(actual.arm->ip, expected.ucontext.uc_mcontext.arm_ip); + EXPECT_EQ(actual.arm->sp, expected.ucontext.uc_mcontext.arm_sp); + EXPECT_EQ(actual.arm->lr, expected.ucontext.uc_mcontext.arm_lr); + EXPECT_EQ(actual.arm->pc, expected.ucontext.uc_mcontext.arm_pc); + EXPECT_EQ(actual.arm->cpsr, expected.ucontext.uc_mcontext.arm_cpsr); + + EXPECT_FALSE(actual.arm->have_fpa_regs); + + EXPECT_TRUE(actual.arm->have_vfp_regs); + + auto test_context = reinterpret_cast( + expected.ucontext.uc_regspace); + + EXPECT_EQ(memcmp(actual.arm->vfp_regs.vfp, + &test_context->vfp.context.vfp, + sizeof(actual.arm->vfp_regs.vfp)), + 0); +} +#elif defined(ARCH_CPU_ARM64) +using NativeCPUContext = ucontext_t; + +struct TestCoprocessorContext { + esr_context esr; + fpsimd_context fpsimd; + _aarch64_ctx terminator; +}; + +void InitializeContext(NativeCPUContext* context) { + memset(context, 'x', sizeof(*context)); + + for (size_t index = 0; index < std::size(context->uc_mcontext.regs); + ++index) { + context->uc_mcontext.regs[index] = index; + } + context->uc_mcontext.sp = 1; + context->uc_mcontext.pc = 2; + context->uc_mcontext.pstate = 3; + + auto test_context = reinterpret_cast( + context->uc_mcontext.__reserved); + + test_context->esr.head.magic = ESR_MAGIC; + test_context->esr.head.size = sizeof(test_context->esr); + memset(&test_context->esr.esr, 'e', sizeof(test_context->esr.esr)); + + test_context->fpsimd.head.magic = FPSIMD_MAGIC; + test_context->fpsimd.head.size = sizeof(test_context->fpsimd); + test_context->fpsimd.fpsr = 1; + test_context->fpsimd.fpcr = 2; + for (size_t reg = 0; reg < std::size(test_context->fpsimd.vregs); ++reg) { + test_context->fpsimd.vregs[reg] = reg; + } + + test_context->terminator.magic = 0; + test_context->terminator.size = 0; +} + +void ExpectContext(const CPUContext& actual, const NativeCPUContext& expected) { + EXPECT_EQ(actual.architecture, kCPUArchitectureARM64); + + EXPECT_EQ(memcmp(actual.arm64->regs, + expected.uc_mcontext.regs, + sizeof(actual.arm64->regs)), + 0); + EXPECT_EQ(actual.arm64->sp, expected.uc_mcontext.sp); + EXPECT_EQ(actual.arm64->pc, expected.uc_mcontext.pc); + EXPECT_EQ(actual.arm64->spsr, expected.uc_mcontext.pstate); + + auto test_context = reinterpret_cast( + expected.uc_mcontext.__reserved); + + EXPECT_EQ(actual.arm64->fpsr, test_context->fpsimd.fpsr); + EXPECT_EQ(actual.arm64->fpcr, test_context->fpsimd.fpcr); + EXPECT_EQ(memcmp(actual.arm64->fpsimd, + &test_context->fpsimd.vregs, + sizeof(actual.arm64->fpsimd)), + 0); +} +#elif defined(ARCH_CPU_MIPS_FAMILY) +using NativeCPUContext = ucontext_t; + +void InitializeContext(NativeCPUContext* context) { + for (size_t reg = 0; reg < std::size(context->uc_mcontext.gregs); ++reg) { + context->uc_mcontext.gregs[reg] = reg; + } + memset(&context->uc_mcontext.fpregs, 44, sizeof(context->uc_mcontext.fpregs)); +} + +void ExpectContext(const CPUContext& actual, const NativeCPUContext& expected) { +#if defined(ARCH_CPU_MIPSEL) + EXPECT_EQ(actual.architecture, kCPUArchitectureMIPSEL); +#define CPU_ARCH_NAME mipsel +#elif defined(ARCH_CPU_MIPS64EL) + EXPECT_EQ(actual.architecture, kCPUArchitectureMIPS64EL); +#define CPU_ARCH_NAME mips64 +#endif + + for (size_t reg = 0; reg < std::size(expected.uc_mcontext.gregs); ++reg) { + EXPECT_EQ(actual.CPU_ARCH_NAME->regs[reg], expected.uc_mcontext.gregs[reg]); + } + + EXPECT_EQ(memcmp(&actual.CPU_ARCH_NAME->fpregs, + &expected.uc_mcontext.fpregs, + sizeof(actual.CPU_ARCH_NAME->fpregs)), + 0); +#undef CPU_ARCH_NAME +} + +#else +#error Port. +#endif + +TEST(ExceptionSnapshotLinux, SelfBasic) { + FakePtraceConnection connection; + ASSERT_TRUE(connection.Initialize(getpid())); + + ProcessReaderLinux process_reader; + ASSERT_TRUE(process_reader.Initialize(&connection)); + + siginfo_t siginfo; + siginfo.si_signo = SIGSEGV; + siginfo.si_errno = 42; + siginfo.si_code = SEGV_MAPERR; + siginfo.si_addr = reinterpret_cast(0xdeadbeef); + + NativeCPUContext context; + InitializeContext(&context); + + internal::ExceptionSnapshotLinux exception; + ASSERT_TRUE(exception.Initialize(&process_reader, + FromPointerCast(&siginfo), + FromPointerCast(&context), + gettid(), + nullptr)); + EXPECT_EQ(exception.Exception(), static_cast(siginfo.si_signo)); + EXPECT_EQ(exception.ExceptionInfo(), static_cast(siginfo.si_code)); + EXPECT_EQ(exception.ExceptionAddress(), + FromPointerCast(siginfo.si_addr)); + ExpectContext(*exception.Context(), context); +} + +class ScopedSigactionRestore { + public: + ScopedSigactionRestore() : old_action_(), signo_(-1), valid_(false) {} + + ScopedSigactionRestore(const ScopedSigactionRestore&) = delete; + ScopedSigactionRestore& operator=(const ScopedSigactionRestore&) = delete; + + ~ScopedSigactionRestore() { Reset(); } + + bool Reset() { + if (valid_) { + int res = sigaction(signo_, &old_action_, nullptr); + EXPECT_EQ(res, 0) << ErrnoMessage("sigaction"); + if (res != 0) { + return false; + } + } + valid_ = false; + signo_ = -1; + return true; + } + + bool ResetInstallHandler(int signo, Signals::Handler handler) { + if (Reset() && Signals::InstallHandler(signo, handler, 0, &old_action_)) { + signo_ = signo; + valid_ = true; + return true; + } + return false; + } + + private: + struct sigaction old_action_; + int signo_; + bool valid_; +}; + +class RaiseTest { + public: + RaiseTest() = delete; + RaiseTest(const RaiseTest&) = delete; + RaiseTest& operator=(const RaiseTest&) = delete; + + static void Run() { + test_complete_ = false; + + ScopedSigactionRestore sigrestore; + ASSERT_TRUE(sigrestore.ResetInstallHandler(kSigno, HandleRaisedSignal)); + + EXPECT_EQ(raise(kSigno), 0) << ErrnoMessage("raise"); + EXPECT_TRUE(test_complete_); + } + + private: + static void HandleRaisedSignal(int signo, siginfo_t* siginfo, void* context) { + FakePtraceConnection connection; + ASSERT_TRUE(connection.Initialize(getpid())); + + ProcessReaderLinux process_reader; + ASSERT_TRUE(process_reader.Initialize(&connection)); + + internal::ExceptionSnapshotLinux exception; + ASSERT_TRUE(exception.Initialize(&process_reader, + FromPointerCast(siginfo), + FromPointerCast(context), + gettid(), + nullptr)); + + EXPECT_EQ(exception.Exception(), static_cast(kSigno)); + + EXPECT_EQ(exception.Codes().size(), 3u); + EXPECT_EQ(exception.Codes()[0], static_cast(getpid())); + EXPECT_EQ(exception.Codes()[1], getuid()); + // Codes()[2] is not set by kill, but we still expect to get it because some + // interfaces may set it and we don't necessarily know where this signal + // came + // from. + + test_complete_ = true; + } + + static constexpr uint32_t kSigno = SIGUSR1; + static bool test_complete_; +}; +bool RaiseTest::test_complete_ = false; + +TEST(ExceptionSnapshotLinux, Raise) { + RaiseTest::Run(); +} + +class TimerTest { + public: + TimerTest() : event_(), timer_(-1), test_complete_(false) { test_ = this; } + + TimerTest(const TimerTest&) = delete; + TimerTest& operator=(const TimerTest&) = delete; + + ~TimerTest() { test_ = nullptr; } + + void Run() { + ScopedSigactionRestore sigrestore; + ASSERT_TRUE(sigrestore.ResetInstallHandler(kSigno, HandleTimer)); + + event_.sigev_notify = SIGEV_SIGNAL; + event_.sigev_signo = kSigno; + event_.sigev_value.sival_int = 42; + ASSERT_EQ(syscall(SYS_timer_create, CLOCK_MONOTONIC, &event_, &timer_), 0); + + itimerspec spec; + spec.it_interval.tv_sec = 0; + spec.it_interval.tv_nsec = 0; + spec.it_value.tv_sec = 0; + spec.it_value.tv_nsec = 1; + ASSERT_EQ(syscall(SYS_timer_settime, timer_, TIMER_ABSTIME, &spec, nullptr), + 0); + + for (size_t attempt = 0; attempt < 3; ++attempt) { + SleepNanoseconds(1); + if (test_complete_) { + return; + } + } + ADD_FAILURE() << "signal not received"; + } + + private: + static void HandleTimer(int signo, siginfo_t* siginfo, void* context) { + FakePtraceConnection connection; + ASSERT_TRUE(connection.Initialize(getpid())); + + ProcessReaderLinux process_reader; + ASSERT_TRUE(process_reader.Initialize(&connection)); + + internal::ExceptionSnapshotLinux exception; + ASSERT_TRUE(exception.Initialize(&process_reader, + FromPointerCast(siginfo), + FromPointerCast(context), + gettid(), + nullptr)); + + EXPECT_EQ(exception.Exception(), static_cast(kSigno)); + + EXPECT_EQ(exception.Codes().size(), 3u); + EXPECT_EQ(exception.Codes()[0], static_cast(test_->timer_)); + int overruns = syscall(SYS_timer_getoverrun, test_->timer_); + ASSERT_GE(overruns, 0); + EXPECT_EQ(exception.Codes()[1], static_cast(overruns)); + EXPECT_EQ(exception.Codes()[2], + static_cast(test_->event_.sigev_value.sival_int)); + + test_->test_complete_ = true; + } + + sigevent event_; + __kernel_timer_t timer_; + volatile bool test_complete_; + + static constexpr uint32_t kSigno = SIGALRM; + static TimerTest* test_; +}; +TimerTest* TimerTest::test_; + +TEST(ExceptionSnapshotLinux, SelfTimer) { + TimerTest test; + test.Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/linux/process_reader_linux.cc b/shared/sentry/external/crashpad/snapshot/linux/process_reader_linux.cc new file mode 100644 index 000000000..5711f343a --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/linux/process_reader_linux.cc @@ -0,0 +1,543 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/linux/process_reader_linux.h" + +#include +#include +#include +#include +#include +#include + +#include + +#include "base/logging.h" +#include "build/build_config.h" +#include "snapshot/linux/debug_rendezvous.h" +#include "util/linux/auxiliary_vector.h" +#include "util/linux/proc_stat_reader.h" + +#if BUILDFLAG(IS_ANDROID) +#include +#endif + +namespace crashpad { + +namespace { + +bool ShouldMergeStackMappings(const MemoryMap::Mapping& stack_mapping, + const MemoryMap::Mapping& adj_mapping) { + DCHECK(stack_mapping.readable); + return adj_mapping.readable && stack_mapping.device == adj_mapping.device && + stack_mapping.inode == adj_mapping.inode && + (stack_mapping.name == adj_mapping.name || + stack_mapping.name.empty() || adj_mapping.name.empty()); +} + +} // namespace + +ProcessReaderLinux::Thread::Thread() + : thread_info(), + stack_region_address(0), + stack_region_size(0), + tid(-1), + static_priority(-1), + nice_value(-1) {} + +ProcessReaderLinux::Thread::~Thread() {} + +bool ProcessReaderLinux::Thread::InitializePtrace( + PtraceConnection* connection) { + if (!connection->GetThreadInfo(tid, &thread_info)) { + return false; + } + + // TODO(jperaza): Collect scheduling priorities via the broker when they can't + // be collected directly. + have_priorities = false; + + // TODO(jperaza): Starting with Linux 3.14, scheduling policy, static + // priority, and nice value can be collected all in one call with + // sched_getattr(). + int res = sched_getscheduler(tid); + if (res < 0) { + PLOG(WARNING) << "sched_getscheduler"; + return true; + } + sched_policy = res; + + sched_param param; + if (sched_getparam(tid, ¶m) != 0) { + PLOG(WARNING) << "sched_getparam"; + return true; + } + static_priority = param.sched_priority; + + errno = 0; + res = getpriority(PRIO_PROCESS, tid); + if (res == -1 && errno) { + PLOG(WARNING) << "getpriority"; + return true; + } + nice_value = res; + + have_priorities = true; + return true; +} + +void ProcessReaderLinux::Thread::InitializeStack(ProcessReaderLinux* reader) { + LinuxVMAddress stack_pointer; +#if defined(ARCH_CPU_X86_FAMILY) + stack_pointer = reader->Is64Bit() ? thread_info.thread_context.t64.rsp + : thread_info.thread_context.t32.esp; +#elif defined(ARCH_CPU_ARM_FAMILY) + stack_pointer = reader->Is64Bit() ? thread_info.thread_context.t64.sp + : thread_info.thread_context.t32.sp; +#elif defined(ARCH_CPU_MIPS_FAMILY) + stack_pointer = reader->Is64Bit() ? thread_info.thread_context.t64.regs[29] + : thread_info.thread_context.t32.regs[29]; +#else +#error Port. +#endif + InitializeStackFromSP(reader, stack_pointer); +} + +void ProcessReaderLinux::Thread::InitializeStackFromSP( + ProcessReaderLinux* reader, + LinuxVMAddress stack_pointer) { + const MemoryMap* memory_map = reader->GetMemoryMap(); + + // If we can't find the mapping, it's probably a bad stack pointer + const MemoryMap::Mapping* mapping = memory_map->FindMapping(stack_pointer); + if (!mapping) { + LOG(WARNING) << "no stack mapping"; + return; + } + LinuxVMAddress stack_region_start = + reader->Memory()->PointerToAddress(stack_pointer); + + // We've hit what looks like a guard page; skip to the end and check for a + // mapped stack region. + if (!mapping->readable) { + stack_region_start = mapping->range.End(); + mapping = memory_map->FindMapping(stack_region_start); + if (!mapping) { + LOG(WARNING) << "no stack mapping"; + return; + } + } else { +#if defined(ARCH_CPU_X86_FAMILY) + // Adjust start address to include the red zone + if (reader->Is64Bit()) { + constexpr LinuxVMSize kRedZoneSize = 128; + LinuxVMAddress red_zone_base = + stack_region_start - std::min(kRedZoneSize, stack_region_start); + + // Only include the red zone if it is part of a valid mapping + if (red_zone_base >= mapping->range.Base()) { + stack_region_start = red_zone_base; + } else { + const MemoryMap::Mapping* rz_mapping = + memory_map->FindMapping(red_zone_base); + if (rz_mapping && ShouldMergeStackMappings(*mapping, *rz_mapping)) { + stack_region_start = red_zone_base; + } else { + stack_region_start = mapping->range.Base(); + } + } + } +#endif + } + stack_region_address = stack_region_start; + + // If there are more mappings at the end of this one, they may be a + // continuation of the stack. + LinuxVMAddress stack_end = mapping->range.End(); + const MemoryMap::Mapping* next_mapping; + while ((next_mapping = memory_map->FindMapping(stack_end)) && + ShouldMergeStackMappings(*mapping, *next_mapping)) { + stack_end = next_mapping->range.End(); + } + + // The main thread should have an entry in the maps file just for its stack, + // so we'll assume the base of the stack is at the end of the region. Other + // threads' stacks may not have their own entries in the maps file if they + // were user-allocated within a larger mapping, but pthreads places the TLS + // at the high-address end of the stack so we can try using that to shrink + // the stack region. + stack_region_size = stack_end - stack_region_address; + VMAddress tls_address = reader->Memory()->PointerToAddress( + thread_info.thread_specific_data_address); + if (tid != reader->ProcessID() && tls_address > stack_region_address && + tls_address < stack_end) { + stack_region_size = tls_address - stack_region_address; + } +} + +ProcessReaderLinux::Module::Module() + : name(), elf_reader(nullptr), type(ModuleSnapshot::kModuleTypeUnknown) {} + +ProcessReaderLinux::Module::~Module() = default; + +ProcessReaderLinux::ProcessReaderLinux() + : connection_(), + process_info_(), + memory_map_(), + threads_(), + modules_(), + elf_readers_(), + is_64_bit_(false), + initialized_threads_(false), + initialized_modules_(false), + initialized_() {} + +ProcessReaderLinux::~ProcessReaderLinux() {} + +bool ProcessReaderLinux::Initialize(PtraceConnection* connection) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + DCHECK(connection); + connection_ = connection; + + if (!process_info_.InitializeWithPtrace(connection_)) { + return false; + } + + if (!memory_map_.Initialize(connection_)) { + return false; + } + + is_64_bit_ = process_info_.Is64Bit(); + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +bool ProcessReaderLinux::StartTime(timeval* start_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return process_info_.StartTime(start_time); +} + +bool ProcessReaderLinux::CPUTimes(timeval* user_time, + timeval* system_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + timerclear(user_time); + timerclear(system_time); + + timeval local_user_time; + timerclear(&local_user_time); + timeval local_system_time; + timerclear(&local_system_time); + + for (const Thread& thread : threads_) { + ProcStatReader stat; + if (!stat.Initialize(connection_, thread.tid)) { + return false; + } + + timeval thread_user_time; + if (!stat.UserCPUTime(&thread_user_time)) { + return false; + } + + timeval thread_system_time; + if (!stat.SystemCPUTime(&thread_system_time)) { + return false; + } + + timeradd(&local_user_time, &thread_user_time, &local_user_time); + timeradd(&local_system_time, &thread_system_time, &local_system_time); + } + + *user_time = local_user_time; + *system_time = local_system_time; + return true; +} + +const std::vector& ProcessReaderLinux::Threads() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (!initialized_threads_) { + InitializeThreads(); + } + return threads_; +} + +const std::vector& ProcessReaderLinux::Modules() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (!initialized_modules_) { + InitializeModules(); + } + return modules_; +} + +void ProcessReaderLinux::InitializeAbortMessage() { +#if BUILDFLAG(IS_ANDROID) + const MemoryMap::Mapping* mapping = + memory_map_.FindMappingWithName("[anon:abort message]"); + if (!mapping) { + return; + } + + if (is_64_bit_) { + ReadAbortMessage(mapping); + } else { + ReadAbortMessage(mapping); + } +#endif +} + +#if BUILDFLAG(IS_ANDROID) + +// These structure definitions and the magic numbers below were copied from +// bionic/libc/bionic/android_set_abort_message.cpp + +template +struct abort_msg_t { + uint32_t size; + char msg[0]; +}; + +template <> +struct abort_msg_t { + uint64_t size; + char msg[0]; +}; + +template +struct magic_abort_msg_t { + uint64_t magic1; + uint64_t magic2; + abort_msg_t msg; +}; + +template +void ProcessReaderLinux::ReadAbortMessage(const MemoryMap::Mapping* mapping) { + magic_abort_msg_t header; + if (!Memory()->Read( + mapping->range.Base(), sizeof(magic_abort_msg_t), &header)) { + return; + } + + size_t size = header.msg.size - sizeof(magic_abort_msg_t) - 1; + if (header.magic1 != 0xb18e40886ac388f0ULL || + header.magic2 != 0xc6dfba755a1de0b5ULL || + mapping->range.Size() < + offsetof(magic_abort_msg_t, msg.msg) + size) { + return; + } + + abort_message_.resize(size); + if (!Memory()->Read( + mapping->range.Base() + offsetof(magic_abort_msg_t, msg.msg), + size, + &abort_message_[0])) { + abort_message_.clear(); + } +} + +#endif // BUILDFLAG(IS_ANDROID) + +const std::string& ProcessReaderLinux::AbortMessage() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (abort_message_.empty()) { + InitializeAbortMessage(); + } + return abort_message_; +} + +void ProcessReaderLinux::InitializeThreads() { + DCHECK(threads_.empty()); + initialized_threads_ = true; + + pid_t pid = ProcessID(); + if (pid == getpid()) { + // TODO(jperaza): ptrace can't be used on threads in the same thread group. + // Using clone to create a new thread in it's own thread group doesn't work + // because glibc doesn't support threads it didn't create via pthreads. + // Fork a new process to snapshot us and copy the data back? + LOG(ERROR) << "not implemented"; + return; + } + + Thread main_thread; + main_thread.tid = pid; + if (main_thread.InitializePtrace(connection_)) { + main_thread.InitializeStack(this); + threads_.push_back(main_thread); + } else { + LOG(WARNING) << "Couldn't initialize main thread."; + } + + bool main_thread_found = false; + std::vector thread_ids; + bool result = connection_->Threads(&thread_ids); + DCHECK(result); + for (pid_t tid : thread_ids) { + if (tid == pid) { + DCHECK(!main_thread_found); + main_thread_found = true; + continue; + } + + Thread thread; + thread.tid = tid; + if (connection_->Attach(tid) && thread.InitializePtrace(connection_)) { + thread.InitializeStack(this); + threads_.push_back(thread); + } + } + DCHECK(main_thread_found); +} + +void ProcessReaderLinux::InitializeModules() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + initialized_modules_ = true; + + AuxiliaryVector aux; + if (!aux.Initialize(connection_)) { + return; + } + + LinuxVMAddress phdrs; + if (!aux.GetValue(AT_PHDR, &phdrs)) { + return; + } + + ProcessMemoryRange range; + if (!range.Initialize(Memory(), is_64_bit_)) { + return; + } + + // The strategy used for identifying loaded modules depends on ELF files + // conventionally loading their header and program headers into memory. + // Locating the correct module could fail if the headers aren't mapped, are + // mapped at an unexpected location, or if there are other mappings + // constructed to look like the ELF module being searched for. + const MemoryMap::Mapping* exe_mapping = nullptr; + std::unique_ptr exe_reader; + { + const MemoryMap::Mapping* phdr_mapping = memory_map_.FindMapping(phdrs); + if (!phdr_mapping) { + return; + } + + auto possible_mappings = + memory_map_.FindFilePossibleMmapStarts(*phdr_mapping); + const MemoryMap::Mapping* mapping = nullptr; + while ((mapping = possible_mappings->Next())) { + auto parsed_exe = std::make_unique(); + if (parsed_exe->Initialize( + range, + mapping->range.Base(), + /* verbose= */ possible_mappings->Count() == 0) && + parsed_exe->GetProgramHeaderTableAddress() == phdrs) { + exe_mapping = mapping; + exe_reader = std::move(parsed_exe); + break; + } + } + if (!exe_mapping) { + LOG(ERROR) << "no exe mappings 0x" << std::hex + << phdr_mapping->range.Base(); + return; + } + } + + LinuxVMAddress debug_address; + if (!exe_reader->GetDebugAddress(&debug_address)) { + return; + } + + DebugRendezvous debug; + if (!debug.Initialize(range, debug_address)) { + return; + } + + Module exe = {}; + exe.name = !debug.Executable()->name.empty() ? debug.Executable()->name + : exe_mapping->name; + exe.elf_reader = exe_reader.get(); + exe.type = ModuleSnapshot::ModuleType::kModuleTypeExecutable; + + modules_.push_back(exe); + elf_readers_.push_back(std::move(exe_reader)); + + LinuxVMAddress loader_base = 0; + aux.GetValue(AT_BASE, &loader_base); + + for (const DebugRendezvous::LinkEntry& entry : debug.Modules()) { + const MemoryMap::Mapping* module_mapping = nullptr; + std::unique_ptr elf_reader; + { + const MemoryMap::Mapping* dyn_mapping = + memory_map_.FindMapping(entry.dynamic_array); + if (!dyn_mapping) { + continue; + } + +#if BUILDFLAG(IS_ANDROID) + // Beginning at API 21, Bionic provides android_dlopen_ext() which allows + // passing a file descriptor with an existing relro segment to the loader. + // This means that the mapping attributes of dyn_mapping may be unrelated + // to the attributes of the other mappings for the module. In this case, + // search all mappings in reverse order from dyn_mapping until a module is + // parsed whose dynamic address matches the value in the debug link. + static int api_level = android_get_device_api_level(); + auto possible_mappings = + (api_level >= 21 || api_level < 0) + ? memory_map_.ReverseIteratorFrom(*dyn_mapping) + : memory_map_.FindFilePossibleMmapStarts(*dyn_mapping); +#else + auto possible_mappings = + memory_map_.FindFilePossibleMmapStarts(*dyn_mapping); +#endif + const MemoryMap::Mapping* mapping = nullptr; + while ((mapping = possible_mappings->Next())) { + auto parsed_module = std::make_unique(); + VMAddress dynamic_address; + if (parsed_module->Initialize( + range, + mapping->range.Base(), + /* verbose= */ possible_mappings->Count() == 0) && + parsed_module->GetDynamicArrayAddress(&dynamic_address) && + dynamic_address == entry.dynamic_array) { + module_mapping = mapping; + elf_reader = std::move(parsed_module); + break; + } + } + if (!module_mapping) { + LOG(ERROR) << "no module mappings 0x" << std::hex + << dyn_mapping->range.Base(); + continue; + } + } + + Module module = {}; + std::string soname; + if (elf_reader->SoName(&soname) && !soname.empty()) { + module.name = soname; + } else { + module.name = !entry.name.empty() ? entry.name : module_mapping->name; + } + module.elf_reader = elf_reader.get(); + module.type = loader_base && elf_reader->Address() == loader_base + ? ModuleSnapshot::kModuleTypeDynamicLoader + : ModuleSnapshot::kModuleTypeSharedLibrary; + modules_.push_back(module); + elf_readers_.push_back(std::move(elf_reader)); + } +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/linux/process_reader_linux.h b/shared/sentry/external/crashpad/snapshot/linux/process_reader_linux.h new file mode 100644 index 000000000..f44e15f5f --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/linux/process_reader_linux.h @@ -0,0 +1,185 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_LINUX_PROCESS_READER_LINUX_H_ +#define CRASHPAD_SNAPSHOT_LINUX_PROCESS_READER_LINUX_H_ + +#include +#include + +#include +#include +#include + +#include "snapshot/elf/elf_image_reader.h" +#include "snapshot/module_snapshot.h" +#include "util/linux/address_types.h" +#include "util/linux/memory_map.h" +#include "util/linux/ptrace_connection.h" +#include "util/linux/thread_info.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/posix/process_info.h" +#include "util/process/process_memory.h" + +namespace crashpad { + +//! \brief Accesses information about another process, identified by a process +//! ID. +class ProcessReaderLinux { + public: + //! \brief Contains information about a thread that belongs to a process. + struct Thread { + Thread(); + ~Thread(); + + //! \brief Initializes the thread's stack using \a stack_pointer instead of + //! the stack pointer in \a thread_info. + //! + //! This method initializes \a stack_region_address and \a stack_region_size + //! overwriting any values they previously contained. This is useful, for + //! example, if the thread is currently in a signal handler context, which + //! may execute on a different stack than was used before the signal was + //! received. + //! + //! \param[in] reader A process reader for the target process. + //! \param[in] stack_pointer The stack pointer for the stack to initialize. + void InitializeStackFromSP(ProcessReaderLinux* reader, + LinuxVMAddress stack_pointer); + + ThreadInfo thread_info; + LinuxVMAddress stack_region_address; + LinuxVMSize stack_region_size; + pid_t tid; + int sched_policy; + int static_priority; + int nice_value; + + //! \brief `true` if `sched_policy`, `static_priority`, and `nice_value` are + //! all valid. + bool have_priorities; + + private: + friend class ProcessReaderLinux; + + bool InitializePtrace(PtraceConnection* connection); + void InitializeStack(ProcessReaderLinux* reader); + }; + + //! \brief Contains information about a module loaded into a process. + struct Module { + Module(); + ~Module(); + + //! \brief The pathname used to load the module from disk. + std::string name; + + //! \brief An image reader for the module. + //! + //! The lifetime of this ElfImageReader is scoped to the lifetime of the + //! ProcessReaderLinux that created it. + //! + //! This field may be `nullptr` if a reader could not be created for the + //! module. + ElfImageReader* elf_reader; + + //! \brief The module's type. + ModuleSnapshot::ModuleType type; + }; + + ProcessReaderLinux(); + + ProcessReaderLinux(const ProcessReaderLinux&) = delete; + ProcessReaderLinux& operator=(const ProcessReaderLinux&) = delete; + + ~ProcessReaderLinux(); + + //! \brief Initializes this object. + //! + //! This method must be successfully called before calling any other method in + //! this class and may only be called once. + //! + //! \param[in] connection A PtraceConnection to the target process. + //! \return `true` on success. `false` on failure with a message logged. + bool Initialize(PtraceConnection* connection); + + //! \brief Return `true` if the target task is a 64-bit process. + bool Is64Bit() const { return is_64_bit_; } + + //! \brief Return the target process' process ID. + pid_t ProcessID() const { return process_info_.ProcessID(); } + + //! \brief Return the target process' parent process ID. + pid_t ParentProcessID() const { return process_info_.ParentProcessID(); } + + //! \brief Return a memory reader for the target process. + const ProcessMemoryLinux* Memory() const { return connection_->Memory(); } + + //! \brief Return a memory map of the target process. + MemoryMap* GetMemoryMap() { return &memory_map_; } + + //! \brief Determines the target process’ start time. + //! + //! \param[out] start_time The time that the process started. + //! \return `true` on success with \a start_time set. Otherwise `false` with a + //! message logged. + bool StartTime(timeval* start_time) const; + + //! \brief Determines the target process’ execution time. + //! + //! \param[out] user_time The amount of time the process has executed code in + //! user mode. + //! \param[out] system_time The amount of time the process has executed code + //! in system mode. + //! + //! \return `true` on success, `false` on failure, with a warning logged. On + //! failure, \a user_time and \a system_time will be set to represent no + //! time spent executing code in user or system mode. + bool CPUTimes(timeval* user_time, timeval* system_time) const; + + //! \brief Return a vector of threads that are in the task process. If the + //! main thread is able to be identified and traced, it will be placed at + //! index `0`. + const std::vector& Threads(); + + //! \return The modules loaded in the process. The first element (at index + //! `0`) corresponds to the main executable. + const std::vector& Modules(); + + //! \return On Android, the abort message that was passed to + //! android_set_abort_message(). This is only available on Q or later. + const std::string& AbortMessage(); + + private: + void InitializeThreads(); + void InitializeModules(); + void InitializeAbortMessage(); + template + void ReadAbortMessage(const MemoryMap::Mapping* mapping); + + PtraceConnection* connection_; // weak + ProcessInfo process_info_; + MemoryMap memory_map_; + std::vector threads_; + std::vector modules_; + std::string abort_message_; + std::vector> elf_readers_; + bool is_64_bit_; + bool initialized_threads_; + bool initialized_modules_; + InitializationStateDcheck initialized_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_LINUX_PROCESS_READER_LINUX_H_ diff --git a/shared/sentry/external/crashpad/snapshot/linux/process_reader_linux_test.cc b/shared/sentry/external/crashpad/snapshot/linux/process_reader_linux_test.cc new file mode 100644 index 000000000..81b3d6ec6 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/linux/process_reader_linux_test.cc @@ -0,0 +1,669 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/linux/process_reader_linux.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "base/format_macros.h" +#include "base/memory/free_deleter.h" +#include "base/strings/stringprintf.h" +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "snapshot/linux/test_modules.h" +#include "test/errors.h" +#include "test/linux/fake_ptrace_connection.h" +#include "test/linux/get_tls.h" +#include "test/multiprocess.h" +#include "test/scoped_module_handle.h" +#include "test/test_paths.h" +#include "util/file/file_io.h" +#include "util/file/file_writer.h" +#include "util/file/filesystem.h" +#include "util/linux/direct_ptrace_connection.h" +#include "util/misc/address_sanitizer.h" +#include "util/misc/from_pointer_cast.h" +#include "util/misc/memory_sanitizer.h" +#include "util/synchronization/semaphore.h" + +#if BUILDFLAG(IS_ANDROID) +#include +#include +#include "dlfcn_internal.h" + +// Normally this comes from set_abort_message.h, but only at API level 21. +extern "C" void android_set_abort_message(const char* msg) + __attribute__((weak)); +#endif + +namespace crashpad { +namespace test { +namespace { + +pid_t gettid() { + return syscall(SYS_gettid); +} + +TEST(ProcessReaderLinux, SelfBasic) { + FakePtraceConnection connection; + connection.Initialize(getpid()); + + ProcessReaderLinux process_reader; + ASSERT_TRUE(process_reader.Initialize(&connection)); + +#if defined(ARCH_CPU_64_BITS) + EXPECT_TRUE(process_reader.Is64Bit()); +#else + EXPECT_FALSE(process_reader.Is64Bit()); +#endif + + EXPECT_EQ(process_reader.ProcessID(), getpid()); + EXPECT_EQ(process_reader.ParentProcessID(), getppid()); + + static constexpr char kTestMemory[] = "Some test memory"; + char buffer[std::size(kTestMemory)]; + ASSERT_TRUE(process_reader.Memory()->Read( + reinterpret_cast(kTestMemory), + sizeof(kTestMemory), + &buffer)); + EXPECT_STREQ(kTestMemory, buffer); + + EXPECT_EQ("", process_reader.AbortMessage()); +} + +constexpr char kTestMemory[] = "Read me from another process"; + +class BasicChildTest : public Multiprocess { + public: + BasicChildTest() : Multiprocess() {} + + BasicChildTest(const BasicChildTest&) = delete; + BasicChildTest& operator=(const BasicChildTest&) = delete; + + ~BasicChildTest() {} + + private: + void MultiprocessParent() override { + DirectPtraceConnection connection; + ASSERT_TRUE(connection.Initialize(ChildPID())); + + ProcessReaderLinux process_reader; + ASSERT_TRUE(process_reader.Initialize(&connection)); + +#if !defined(ARCH_CPU_64_BITS) + EXPECT_FALSE(process_reader.Is64Bit()); +#else + EXPECT_TRUE(process_reader.Is64Bit()); +#endif + + EXPECT_EQ(process_reader.ParentProcessID(), getpid()); + EXPECT_EQ(process_reader.ProcessID(), ChildPID()); + + std::string read_string; + ASSERT_TRUE(process_reader.Memory()->ReadCString( + reinterpret_cast(kTestMemory), &read_string)); + EXPECT_EQ(read_string, kTestMemory); + } + + void MultiprocessChild() override { CheckedReadFileAtEOF(ReadPipeHandle()); } +}; + +TEST(ProcessReaderLinux, ChildBasic) { + BasicChildTest test; + test.Run(); +} + +class TestThreadPool { + public: + struct ThreadExpectation { + LinuxVMAddress tls = 0; + LinuxVMAddress stack_address = 0; + LinuxVMSize max_stack_size = 0; + int sched_policy = 0; + int static_priority = 0; + int nice_value = 0; + }; + + TestThreadPool() : threads_() {} + + TestThreadPool(const TestThreadPool&) = delete; + TestThreadPool& operator=(const TestThreadPool&) = delete; + + ~TestThreadPool() { + for (const auto& thread : threads_) { + thread->exit_semaphore.Signal(); + } + + for (const auto& thread : threads_) { + EXPECT_EQ(pthread_join(thread->pthread, nullptr), 0) + << ErrnoMessage("pthread_join"); + } + } + + void StartThreads(size_t thread_count, size_t stack_size = 0) { + for (size_t thread_index = 0; thread_index < thread_count; ++thread_index) { + threads_.push_back(std::make_unique()); + Thread* thread = threads_.back().get(); + + pthread_attr_t attr; + ASSERT_EQ(pthread_attr_init(&attr), 0) + << ErrnoMessage("pthread_attr_init"); + + if (stack_size > 0) { + void* stack_ptr; + errno = posix_memalign(&stack_ptr, getpagesize(), stack_size); + ASSERT_EQ(errno, 0) << ErrnoMessage("posix_memalign"); + + thread->stack.reset(reinterpret_cast(stack_ptr)); + + ASSERT_EQ(pthread_attr_setstack(&attr, thread->stack.get(), stack_size), + 0) + << ErrnoMessage("pthread_attr_setstack"); + thread->expectation.max_stack_size = stack_size; + } + + ASSERT_EQ(pthread_attr_setschedpolicy(&attr, SCHED_OTHER), 0) + << ErrnoMessage("pthread_attr_setschedpolicy"); + thread->expectation.sched_policy = SCHED_OTHER; + + sched_param param; + param.sched_priority = 0; + ASSERT_EQ(pthread_attr_setschedparam(&attr, ¶m), 0) + << ErrnoMessage("pthread_attr_setschedparam"); + thread->expectation.static_priority = 0; + + thread->expectation.nice_value = thread_index % 20; + + ASSERT_EQ(pthread_create(&thread->pthread, &attr, ThreadMain, thread), 0) + << ErrnoMessage("pthread_create"); + } + + for (const auto& thread : threads_) { + thread->ready_semaphore.Wait(); + } + } + + pid_t GetThreadExpectation(size_t thread_index, + ThreadExpectation* expectation) { + CHECK_LT(thread_index, threads_.size()); + + const Thread* thread = threads_[thread_index].get(); + *expectation = thread->expectation; + return thread->tid; + } + + private: + struct Thread { + Thread() + : pthread(), + expectation(), + ready_semaphore(0), + exit_semaphore(0), + tid(-1) {} + ~Thread() {} + + pthread_t pthread; + ThreadExpectation expectation; + std::unique_ptr stack; + Semaphore ready_semaphore; + Semaphore exit_semaphore; + pid_t tid; + }; + + static void* ThreadMain(void* argument) { + Thread* thread = static_cast(argument); + + CHECK_EQ(setpriority(PRIO_PROCESS, 0, thread->expectation.nice_value), 0) + << ErrnoMessage("setpriority"); + + thread->expectation.tls = GetTLS(); + thread->expectation.stack_address = + reinterpret_cast(&thread); + thread->tid = gettid(); + + thread->ready_semaphore.Signal(); + thread->exit_semaphore.Wait(); + + CHECK_EQ(pthread_self(), thread->pthread); + + return nullptr; + } + + std::vector> threads_; +}; + +using ThreadMap = std::map; + +void ExpectThreads(const ThreadMap& thread_map, + const std::vector& threads, + PtraceConnection* connection) { + ASSERT_EQ(threads.size(), thread_map.size()); + + MemoryMap memory_map; + ASSERT_TRUE(memory_map.Initialize(connection)); + + for (const auto& thread : threads) { + SCOPED_TRACE( + base::StringPrintf("Thread id %d, tls 0x%" PRIx64 + ", stack addr 0x%" PRIx64 ", stack size 0x%" PRIx64, + thread.tid, + thread.thread_info.thread_specific_data_address, + thread.stack_region_address, + thread.stack_region_size)); + + const auto& iterator = thread_map.find(thread.tid); + ASSERT_NE(iterator, thread_map.end()); + + EXPECT_EQ(thread.thread_info.thread_specific_data_address, + iterator->second.tls); + + ASSERT_TRUE(memory_map.FindMapping(thread.stack_region_address)); + ASSERT_TRUE(memory_map.FindMapping(thread.stack_region_address + + thread.stack_region_size - 1)); + +#if !defined(ADDRESS_SANITIZER) + // AddressSanitizer causes stack variables to be stored separately from the + // call stack. + EXPECT_LE( + thread.stack_region_address, + connection->Memory()->PointerToAddress(iterator->second.stack_address)); + EXPECT_GE( + thread.stack_region_address + thread.stack_region_size, + connection->Memory()->PointerToAddress(iterator->second.stack_address)); +#endif // !defined(ADDRESS_SANITIZER) + + if (iterator->second.max_stack_size) { + EXPECT_LT(thread.stack_region_size, iterator->second.max_stack_size); + } + + EXPECT_EQ(thread.sched_policy, iterator->second.sched_policy); + EXPECT_EQ(thread.static_priority, iterator->second.static_priority); + EXPECT_EQ(thread.nice_value, iterator->second.nice_value); + } +} + +class ChildThreadTest : public Multiprocess { + public: + ChildThreadTest(size_t stack_size = 0) + : Multiprocess(), stack_size_(stack_size) {} + + ChildThreadTest(const ChildThreadTest&) = delete; + ChildThreadTest& operator=(const ChildThreadTest&) = delete; + + ~ChildThreadTest() {} + + private: + void MultiprocessParent() override { + ThreadMap thread_map; + for (size_t thread_index = 0; thread_index < kThreadCount + 1; + ++thread_index) { + pid_t tid; + TestThreadPool::ThreadExpectation expectation; + + CheckedReadFileExactly(ReadPipeHandle(), &tid, sizeof(tid)); + CheckedReadFileExactly( + ReadPipeHandle(), &expectation, sizeof(expectation)); + thread_map[tid] = expectation; + } + + DirectPtraceConnection connection; + ASSERT_TRUE(connection.Initialize(ChildPID())); + + ProcessReaderLinux process_reader; + ASSERT_TRUE(process_reader.Initialize(&connection)); + const std::vector& threads = + process_reader.Threads(); + ExpectThreads(thread_map, threads, &connection); + } + + void MultiprocessChild() override { + TestThreadPool thread_pool; + thread_pool.StartThreads(kThreadCount, stack_size_); + + TestThreadPool::ThreadExpectation expectation; +#if defined(MEMORY_SANITIZER) + // memset() + re-initialization is required to zero padding bytes for MSan. + memset(&expectation, 0, sizeof(expectation)); +#endif // defined(MEMORY_SANITIZER) + expectation = {}; + expectation.tls = GetTLS(); + expectation.stack_address = reinterpret_cast(&thread_pool); + + int res = sched_getscheduler(0); + ASSERT_GE(res, 0) << ErrnoMessage("sched_getscheduler"); + expectation.sched_policy = res; + + sched_param param; + ASSERT_EQ(sched_getparam(0, ¶m), 0) << ErrnoMessage("sched_getparam"); + expectation.static_priority = param.sched_priority; + + errno = 0; + res = getpriority(PRIO_PROCESS, 0); + ASSERT_FALSE(res == -1 && errno) << ErrnoMessage("getpriority"); + expectation.nice_value = res; + + pid_t tid = gettid(); + + CheckedWriteFile(WritePipeHandle(), &tid, sizeof(tid)); + CheckedWriteFile(WritePipeHandle(), &expectation, sizeof(expectation)); + + for (size_t thread_index = 0; thread_index < kThreadCount; ++thread_index) { + tid = thread_pool.GetThreadExpectation(thread_index, &expectation); + CheckedWriteFile(WritePipeHandle(), &tid, sizeof(tid)); + CheckedWriteFile(WritePipeHandle(), &expectation, sizeof(expectation)); + } + + CheckedReadFileAtEOF(ReadPipeHandle()); + } + + static constexpr size_t kThreadCount = 3; + const size_t stack_size_; +}; + +TEST(ProcessReaderLinux, ChildWithThreads) { + ChildThreadTest test; + test.Run(); +} + +TEST(ProcessReaderLinux, ChildThreadsWithSmallUserStacks) { + ChildThreadTest test(PTHREAD_STACK_MIN); + test.Run(); +} + +// Tests a thread with a stack that spans multiple mappings. +class ChildWithSplitStackTest : public Multiprocess { + public: + ChildWithSplitStackTest() : Multiprocess(), page_size_(getpagesize()) {} + + ChildWithSplitStackTest(const ChildWithSplitStackTest&) = delete; + ChildWithSplitStackTest& operator=(const ChildWithSplitStackTest&) = delete; + + ~ChildWithSplitStackTest() {} + + private: + void MultiprocessParent() override { + LinuxVMAddress stack_addr1; + LinuxVMAddress stack_addr2; + LinuxVMAddress stack_addr3; + + CheckedReadFileExactly(ReadPipeHandle(), &stack_addr1, sizeof(stack_addr1)); + CheckedReadFileExactly(ReadPipeHandle(), &stack_addr2, sizeof(stack_addr2)); + CheckedReadFileExactly(ReadPipeHandle(), &stack_addr3, sizeof(stack_addr3)); + + DirectPtraceConnection connection; + ASSERT_TRUE(connection.Initialize(ChildPID())); + + ProcessReaderLinux process_reader; + ASSERT_TRUE(process_reader.Initialize(&connection)); + + const std::vector& threads = + process_reader.Threads(); + ASSERT_EQ(threads.size(), 1u); + + LinuxVMAddress thread_stack_start = threads[0].stack_region_address; + EXPECT_LE(thread_stack_start, stack_addr1); + EXPECT_LE(thread_stack_start, stack_addr2); + EXPECT_LE(thread_stack_start, stack_addr3); + + LinuxVMAddress thread_stack_end = + thread_stack_start + threads[0].stack_region_size; + EXPECT_GE(thread_stack_end, stack_addr1); + EXPECT_GE(thread_stack_end, stack_addr2); + EXPECT_GE(thread_stack_end, stack_addr3); + } + + void MultiprocessChild() override { + const LinuxVMSize stack_size = page_size_ * 4; + GrowStack(stack_size, reinterpret_cast(&stack_size)); + } + + void GrowStack(LinuxVMSize stack_size, LinuxVMAddress bottom_of_stack) { + char stack_contents[4096]; + auto stack_address = reinterpret_cast(&stack_contents); + + if (bottom_of_stack - stack_address < stack_size) { + GrowStack(stack_size, bottom_of_stack); + } else { + // Write-protect a page on our stack to split up the mapping + LinuxVMAddress page_addr = + stack_address - (stack_address % page_size_) + 2 * page_size_; + ASSERT_EQ( + mprotect(reinterpret_cast(page_addr), page_size_, PROT_READ), + 0) + << ErrnoMessage("mprotect"); + + CheckedWriteFile( + WritePipeHandle(), &bottom_of_stack, sizeof(bottom_of_stack)); + CheckedWriteFile(WritePipeHandle(), &page_addr, sizeof(page_addr)); + CheckedWriteFile( + WritePipeHandle(), &stack_address, sizeof(stack_address)); + + // Wait for parent to read us + CheckedReadFileAtEOF(ReadPipeHandle()); + + ASSERT_EQ(mprotect(reinterpret_cast(page_addr), + page_size_, + PROT_READ | PROT_WRITE), + 0) + << ErrnoMessage("mprotect"); + } + } + + const size_t page_size_; +}; + +// AddressSanitizer with use-after-return detection causes stack variables to +// be allocated on the heap. +#if defined(ADDRESS_SANITIZER) +#define MAYBE_ChildWithSplitStack DISABLED_ChildWithSplitStack +#else +#define MAYBE_ChildWithSplitStack ChildWithSplitStack +#endif +TEST(ProcessReaderLinux, MAYBE_ChildWithSplitStack) { + ChildWithSplitStackTest test; + test.Run(); +} + +// Android doesn't provide dl_iterate_phdr on ARM until API 21. +#if !BUILDFLAG(IS_ANDROID) || !defined(ARCH_CPU_ARMEL) || __ANDROID_API__ >= 21 +int ExpectFindModule(dl_phdr_info* info, size_t size, void* data) { + SCOPED_TRACE( + base::StringPrintf("module %s at 0x%" PRIx64 " phdrs 0x%" PRIx64, + info->dlpi_name, + LinuxVMAddress{info->dlpi_addr}, + FromPointerCast(info->dlpi_phdr))); + auto modules = + reinterpret_cast*>(data); + +#if BUILDFLAG(IS_ANDROID) + // Prior to API 27, Bionic includes a null entry for /system/bin/linker. + if (!info->dlpi_name) { + EXPECT_EQ(info->dlpi_addr, 0u); + EXPECT_EQ(info->dlpi_phnum, 0u); + EXPECT_EQ(info->dlpi_phdr, nullptr); + return 0; + } +#endif + + // Bionic doesn't always set both of these addresses for the vdso and + // /system/bin/linker, but it does always set one of them. + VMAddress module_addr = info->dlpi_phdr + ? FromPointerCast(info->dlpi_phdr) + : info->dlpi_addr; + + // TODO(jperaza): This can use a range map when one is available. + bool found = false; + for (const auto& module : *modules) { + if (module.elf_reader && module_addr >= module.elf_reader->Address() && + module_addr < + module.elf_reader->Address() + module.elf_reader->Size()) { + found = true; + break; + } + } + EXPECT_TRUE(found); + return 0; +} +#endif // !BUILDFLAG(IS_ANDROID) || !ARCH_CPU_ARMEL || __ANDROID_API__ >= 21 + +void ExpectModulesFromSelf( + const std::vector& modules) { + for (const auto& module : modules) { + EXPECT_FALSE(module.name.empty()); + EXPECT_NE(module.type, ModuleSnapshot::kModuleTypeUnknown); + } + +// Android doesn't provide dl_iterate_phdr on ARM until API 21. +#if !BUILDFLAG(IS_ANDROID) || !defined(ARCH_CPU_ARMEL) || __ANDROID_API__ >= 21 + EXPECT_EQ( + dl_iterate_phdr( + ExpectFindModule, + reinterpret_cast( + const_cast*>(&modules))), + 0); +#endif // !BUILDFLAG(IS_ANDROID) || !ARCH_CPU_ARMEL || __ANDROID_API__ >= 21 +} + +#if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) +void ExpectTestModule(ProcessReaderLinux* reader, + const std::string& module_name) { + for (const auto& module : reader->Modules()) { + if (module.name.find(module_name) != std::string::npos) { + ASSERT_TRUE(module.elf_reader); + + VMAddress dynamic_addr; + ASSERT_TRUE(module.elf_reader->GetDynamicArrayAddress(&dynamic_addr)); + + auto dynamic_mapping = reader->GetMemoryMap()->FindMapping(dynamic_addr); + auto mappings = + reader->GetMemoryMap()->FindFilePossibleMmapStarts(*dynamic_mapping); + EXPECT_EQ(mappings->Count(), 2u); + return; + } + } + ADD_FAILURE() << "Test module not found"; +} +#endif // !ADDRESS_SANITIZER && !MEMORY_SANITIZER + +TEST(ProcessReaderLinux, SelfModules) { +#if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) + const std::string module_name = "test_module.so"; + const std::string module_soname = "test_module_soname"; + ScopedModuleHandle empty_test_module( + LoadTestModule(module_name, module_soname)); + ASSERT_TRUE(empty_test_module.valid()); +#endif // !ADDRESS_SANITIZER && !MEMORY_SANITIZER + + FakePtraceConnection connection; + connection.Initialize(getpid()); + + ProcessReaderLinux process_reader; + ASSERT_TRUE(process_reader.Initialize(&connection)); + + ExpectModulesFromSelf(process_reader.Modules()); +#if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) + ExpectTestModule(&process_reader, module_soname); +#endif // !ADDRESS_SANITIZER && !MEMORY_SANITIZER +} + +class ChildModuleTest : public Multiprocess { + public: + ChildModuleTest() : Multiprocess(), module_soname_("test_module_soname") {} + + ChildModuleTest(const ChildModuleTest&) = delete; + ChildModuleTest& operator=(const ChildModuleTest&) = delete; + + ~ChildModuleTest() = default; + + private: + void MultiprocessParent() override { + char c; + ASSERT_TRUE(LoggingReadFileExactly(ReadPipeHandle(), &c, sizeof(c))); + + DirectPtraceConnection connection; + ASSERT_TRUE(connection.Initialize(ChildPID())); + + ProcessReaderLinux process_reader; + ASSERT_TRUE(process_reader.Initialize(&connection)); + + ExpectModulesFromSelf(process_reader.Modules()); +#if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) + ExpectTestModule(&process_reader, module_soname_); +#endif // !ADDRESS_SANITIZER && !MEMORY_SANITIZER + } + + void MultiprocessChild() override { +#if !defined(ADDRESS_SANITIZER) && !defined(MEMORY_SANITIZER) + ScopedModuleHandle empty_test_module( + LoadTestModule("test_module.so", module_soname_)); + ASSERT_TRUE(empty_test_module.valid()); +#endif // !ADDRESS_SANITIZER && !MEMORY_SANITIZER + + char c = 0; + ASSERT_TRUE(LoggingWriteFile(WritePipeHandle(), &c, sizeof(c))); + + CheckedReadFileAtEOF(ReadPipeHandle()); + } + + const std::string module_soname_; +}; + +TEST(ProcessReaderLinux, ChildModules) { + ChildModuleTest test; + test.Run(); +} + +#if BUILDFLAG(IS_ANDROID) +const char kTestAbortMessage[] = "test abort message"; + +TEST(ProcessReaderLinux, AbortMessage) { + // This test requires Q. The API level on Q devices will be 28 until the API + // is finalized, so we can't check API level yet. For now, test for the + // presence of a libc symbol which was introduced in Q. + if (!crashpad::internal::Dlsym(RTLD_DEFAULT, + "android_fdsan_close_with_tag")) { + GTEST_SKIP(); + } + + android_set_abort_message(kTestAbortMessage); + + FakePtraceConnection connection; + connection.Initialize(getpid()); + + ProcessReaderLinux process_reader; + ASSERT_TRUE(process_reader.Initialize(&connection)); + + EXPECT_EQ(kTestAbortMessage, process_reader.AbortMessage()); +} +#endif + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/linux/process_snapshot_linux.cc b/shared/sentry/external/crashpad/snapshot/linux/process_snapshot_linux.cc new file mode 100644 index 000000000..cdb69a3ad --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/linux/process_snapshot_linux.cc @@ -0,0 +1,323 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/linux/process_snapshot_linux.h" + +#include + +#include "base/logging.h" +#include "build/build_config.h" +#include "util/linux/exception_information.h" + +namespace crashpad { + +ProcessSnapshotLinux::ProcessSnapshotLinux() = default; + +ProcessSnapshotLinux::~ProcessSnapshotLinux() = default; + +bool ProcessSnapshotLinux::Initialize(PtraceConnection* connection) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + if (gettimeofday(&snapshot_time_, nullptr) != 0) { + PLOG(ERROR) << "gettimeofday"; + return false; + } + + if (!process_reader_.Initialize(connection) || + !memory_range_.Initialize(process_reader_.Memory(), + process_reader_.Is64Bit())) { + return false; + } + + client_id_.InitializeToZero(); + system_.Initialize(&process_reader_, &snapshot_time_); + + InitializeModules(); + GetCrashpadOptionsInternal((&options_)); + InitializeThreads(); + InitializeAnnotations(); + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +pid_t ProcessSnapshotLinux::FindThreadWithStackAddress( + VMAddress stack_address) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + for (const auto& thread : process_reader_.Threads()) { + if (stack_address >= thread.stack_region_address && + stack_address < + thread.stack_region_address + thread.stack_region_size) { + return thread.tid; + } + } + return -1; +} + +bool ProcessSnapshotLinux::InitializeException( + LinuxVMAddress exception_info_address, + pid_t exception_thread_id) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + DCHECK(!exception_); + + ExceptionInformation info; + if (!process_reader_.Memory()->Read( + exception_info_address, sizeof(info), &info)) { + LOG(ERROR) << "Couldn't read exception info"; + return false; + } + + if (exception_thread_id >= 0) { + info.thread_id = exception_thread_id; + } + + uint32_t* budget_remaining_pointer = + options_.gather_indirectly_referenced_memory == TriState::kEnabled + ? &options_.indirectly_referenced_memory_cap + : nullptr; + + exception_.reset(new internal::ExceptionSnapshotLinux()); + if (!exception_->Initialize(&process_reader_, + info.siginfo_address, + info.context_address, + info.thread_id, + budget_remaining_pointer)) { + exception_.reset(); + return false; + } + + // The thread's existing snapshot will have captured the stack for the signal + // handler. Replace it with a thread snapshot which captures the stack for the + // exception context. + for (const auto& reader_thread : process_reader_.Threads()) { + if (reader_thread.tid == info.thread_id) { + ProcessReaderLinux::Thread thread = reader_thread; + thread.InitializeStackFromSP(&process_reader_, + exception_->Context()->StackPointer()); + + auto exc_thread_snapshot = + std::make_unique(); + if (!exc_thread_snapshot->Initialize(&process_reader_, thread, nullptr)) { + return false; + } + +#ifdef CLIENT_STACKTRACES_ENABLED + exc_thread_snapshot->TrimStackTrace( + exception_->Context()->InstructionPointer()); +#endif + + for (auto& thread_snapshot : threads_) { + if (thread_snapshot->ThreadID() == + static_cast(info.thread_id)) { + thread_snapshot.reset(exc_thread_snapshot.release()); + return true; + } + } + + LOG(ERROR) << "thread not found " << info.thread_id; + return false; + } + } + + LOG(ERROR) << "thread not found " << info.thread_id; + return false; +} + +void ProcessSnapshotLinux::GetCrashpadOptions( + CrashpadInfoClientOptions* options) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *options = options_; +} + +void ProcessSnapshotLinux::GetCrashpadOptionsInternal( + CrashpadInfoClientOptions* options) { + CrashpadInfoClientOptions local_options; + + for (const auto& module : modules_) { + CrashpadInfoClientOptions module_options; + if (!module->GetCrashpadOptions(&module_options)) { + continue; + } + + if (local_options.crashpad_handler_behavior == TriState::kUnset) { + local_options.crashpad_handler_behavior = + module_options.crashpad_handler_behavior; + } + if (local_options.system_crash_reporter_forwarding == TriState::kUnset) { + local_options.system_crash_reporter_forwarding = + module_options.system_crash_reporter_forwarding; + } + if (local_options.gather_indirectly_referenced_memory == TriState::kUnset) { + local_options.gather_indirectly_referenced_memory = + module_options.gather_indirectly_referenced_memory; + local_options.indirectly_referenced_memory_cap = + module_options.indirectly_referenced_memory_cap; + } + + // If non-default values have been found for all options, the loop can end + // early. + if (local_options.crashpad_handler_behavior != TriState::kUnset && + local_options.system_crash_reporter_forwarding != TriState::kUnset && + local_options.gather_indirectly_referenced_memory != TriState::kUnset) { + break; + } + } + + *options = local_options; +} + +crashpad::ProcessID ProcessSnapshotLinux::ProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return process_reader_.ProcessID(); +} + +crashpad::ProcessID ProcessSnapshotLinux::ParentProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return process_reader_.ParentProcessID(); +} + +void ProcessSnapshotLinux::SnapshotTime(timeval* snapshot_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *snapshot_time = snapshot_time_; +} + +void ProcessSnapshotLinux::ProcessStartTime(timeval* start_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + process_reader_.StartTime(start_time); +} + +void ProcessSnapshotLinux::ProcessCPUTimes(timeval* user_time, + timeval* system_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + process_reader_.CPUTimes(user_time, system_time); +} + +void ProcessSnapshotLinux::ReportID(UUID* report_id) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *report_id = report_id_; +} + +void ProcessSnapshotLinux::ClientID(UUID* client_id) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *client_id = client_id_; +} + +const std::map& +ProcessSnapshotLinux::AnnotationsSimpleMap() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return annotations_simple_map_; +} + +const SystemSnapshot* ProcessSnapshotLinux::System() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &system_; +} + +std::vector ProcessSnapshotLinux::Threads() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector threads; + for (const auto& thread : threads_) { + threads.push_back(thread.get()); + } + return threads; +} + +std::vector ProcessSnapshotLinux::Modules() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector modules; + for (const auto& module : modules_) { + modules.push_back(module.get()); + } + return modules; +} + +std::vector ProcessSnapshotLinux::UnloadedModules() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(jperaza): Can this be implemented on Linux? + return std::vector(); +} + +const ExceptionSnapshot* ProcessSnapshotLinux::Exception() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_.get(); +} + +std::vector ProcessSnapshotLinux::MemoryMap() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(jperaza): do this. + return std::vector(); +} + +std::vector ProcessSnapshotLinux::Handles() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector(); +} + +std::vector ProcessSnapshotLinux::ExtraMemory() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector(); +} + +const ProcessMemory* ProcessSnapshotLinux::Memory() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return process_reader_.Memory(); +} + +void ProcessSnapshotLinux::InitializeThreads() { + const std::vector& process_reader_threads = + process_reader_.Threads(); + uint32_t* budget_remaining_pointer = + options_.gather_indirectly_referenced_memory == TriState::kEnabled + ? &options_.indirectly_referenced_memory_cap + : nullptr; + + for (const ProcessReaderLinux::Thread& process_reader_thread : + process_reader_threads) { + auto thread = std::make_unique(); + if (thread->Initialize(&process_reader_, + process_reader_thread, + budget_remaining_pointer)) { + threads_.push_back(std::move(thread)); + } + } +} + +void ProcessSnapshotLinux::InitializeModules() { + for (const ProcessReaderLinux::Module& reader_module : + process_reader_.Modules()) { + auto module = + std::make_unique(reader_module.name, + reader_module.elf_reader, + reader_module.type, + &memory_range_, + process_reader_.Memory()); + if (module->Initialize()) { + modules_.push_back(std::move(module)); + } + } +} + +void ProcessSnapshotLinux::InitializeAnnotations() { +#if BUILDFLAG(IS_ANDROID) + const std::string& abort_message = process_reader_.AbortMessage(); + if (!abort_message.empty()) { + annotations_simple_map_["abort_message"] = abort_message; + } +#endif +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/linux/process_snapshot_linux.h b/shared/sentry/external/crashpad/snapshot/linux/process_snapshot_linux.h new file mode 100644 index 000000000..285ee5d09 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/linux/process_snapshot_linux.h @@ -0,0 +1,161 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_LINUX_PROCESS_SNAPSHOT_LINUX_H_ +#define CRASHPAD_SNAPSHOT_LINUX_PROCESS_SNAPSHOT_LINUX_H_ + +#include +#include + +#include +#include +#include +#include + +#include "snapshot/crashpad_info_client_options.h" +#include "snapshot/elf/module_snapshot_elf.h" +#include "snapshot/linux/exception_snapshot_linux.h" +#include "snapshot/linux/process_reader_linux.h" +#include "snapshot/linux/system_snapshot_linux.h" +#include "snapshot/linux/thread_snapshot_linux.h" +#include "snapshot/memory_map_region_snapshot.h" +#include "snapshot/module_snapshot.h" +#include "snapshot/process_snapshot.h" +#include "snapshot/system_snapshot.h" +#include "snapshot/thread_snapshot.h" +#include "snapshot/unloaded_module_snapshot.h" +#include "util/linux/ptrace_connection.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/misc/uuid.h" +#include "util/process/process_id.h" +#include "util/process/process_memory_range.h" + +namespace crashpad { + +//! \brief A ProcessSnapshot of a running (or crashed) process running on a +//! Linux system. +class ProcessSnapshotLinux final : public ProcessSnapshot { + public: + ProcessSnapshotLinux(); + + ProcessSnapshotLinux(const ProcessSnapshotLinux&) = delete; + ProcessSnapshotLinux& operator=(const ProcessSnapshotLinux&) = delete; + + ~ProcessSnapshotLinux() override; + + //! \brief Initializes the object. + //! + //! \param[in] connection A connection to the process to snapshot. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(PtraceConnection* connection); + + //! \brief Finds the thread whose stack contains \a stack_address. + //! + //! \param[in] stack_address A stack address to search for. + //! \return The thread ID of the thread whose stack contains \a stack_address + //! or -1 if no matching thread is found. + pid_t FindThreadWithStackAddress(VMAddress stack_address); + + //! \brief Initializes the object's exception. + //! + //! \param[in] exception_info The address of an ExceptionInformation in the + //! target process' address space. + //! \param[in] exception_thread_id The thread ID to assocaite the thread with. + //! Optional. If -1, the exception thread will be identified by the + //! ExceptionInformation struct which contains the thread ID in the target + //! process' namespace. + bool InitializeException(LinuxVMAddress exception_info, + pid_t exception_thread_id = -1); + + //! \brief Sets the value to be returned by ReportID(). + //! + //! The crash report ID is under the control of the snapshot + //! producer, which may call this method to set the report ID. If this is not + //! done, ReportID() will return an identifier consisting entirely of zeroes. + void SetReportID(const UUID& report_id) { report_id_ = report_id; } + + //! \brief Sets the value to be returned by ClientID(). + //! + //! The client ID is under the control of the snapshot producer, + //! which may call this method to set the client ID. If this is not done, + //! ClientID() will return an identifier consisting entirely of zeroes. + void SetClientID(const UUID& client_id) { client_id_ = client_id; } + + //! \brief Add an annotation to be returned by AnnotationsSimpleMap(). + //! + //! Most process annotations are under the control of the snapshot + //! producer, which may call this method to establish these annotations. + //! On Android Q or later, the process snapshot may add an "abort_message" + //! annotation, which will contain the abort message passed to the + //! android_set_abort_message() function. Contrast this with module + //! annotations, which are under the control of the process being snapshotted. + void AddAnnotation(const std::string& key, const std::string& value) { + annotations_simple_map_[key] = value; + } + + //! \brief Returns options from CrashpadInfo structures found in modules in + //! the process. + //! + //! \param[out] options Options set in CrashpadInfo structures in modules in + //! the process. + void GetCrashpadOptions(CrashpadInfoClientOptions* options); + + // ProcessSnapshot: + + crashpad::ProcessID ProcessID() const override; + crashpad::ProcessID ParentProcessID() const override; + void SnapshotTime(timeval* snapshot_time) const override; + void ProcessStartTime(timeval* start_time) const override; + void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override; + void ReportID(UUID* report_id) const override; + void ClientID(UUID* client_id) const override; + const std::map& AnnotationsSimpleMap() + const override; + const SystemSnapshot* System() const override; + std::vector Threads() const override; + std::vector Modules() const override; + std::vector UnloadedModules() const override; + const ExceptionSnapshot* Exception() const override; + std::vector MemoryMap() const override; + std::vector Handles() const override; + std::vector ExtraMemory() const override; + const ProcessMemory* Memory() const override; + + private: + void InitializeThreads(); + void InitializeModules(); + void InitializeAnnotations(); + + // Initializes options_ on behalf of Initialize(). + void GetCrashpadOptionsInternal(CrashpadInfoClientOptions* options); + + std::map annotations_simple_map_; + timeval snapshot_time_; + UUID report_id_; + UUID client_id_; + std::vector> threads_; + std::vector> modules_; + std::unique_ptr exception_; + internal::SystemSnapshotLinux system_; + ProcessReaderLinux process_reader_; + ProcessMemoryRange memory_range_; + CrashpadInfoClientOptions options_; + InitializationStateDcheck initialized_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_LINUX_PROCESS_SNAPSHOT_LINUX_H_ diff --git a/shared/sentry/external/crashpad/snapshot/linux/signal_context.h b/shared/sentry/external/crashpad/snapshot/linux/signal_context.h new file mode 100644 index 000000000..c004f8f6d --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/linux/signal_context.h @@ -0,0 +1,434 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_SIGNAL_CONTEXT_H_ +#define CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_SIGNAL_CONTEXT_H_ + +#include +#include +#include +#include + +#include +#include + +#include "build/build_config.h" +#include "util/linux/thread_info.h" +#include "util/linux/traits.h" + +namespace crashpad { +namespace internal { + +#pragma pack(push, 1) + +template +union Sigval { + int32_t sigval; + typename Traits::Address pointer; +}; + +template +struct Siginfo { + int32_t signo; +#ifdef ARCH_CPU_MIPS_FAMILY + // Attribute order for signo_t defined in kernel is different for MIPS. + int32_t code; + int32_t err; +#else + int32_t err; + int32_t code; +#endif + typename Traits::UInteger32_64Only padding; + + union { + // SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGTRAP + struct { + typename Traits::Address address; + }; + + // SIGPOLL + struct { + typename Traits::Long band; + int32_t fd; + }; + + // SIGSYS + struct { + typename Traits::Address call_address; + int32_t syscall; + uint32_t arch; + }; + + // Everything else + struct { + union { + struct { + pid_t pid; + uid_t uid; + }; + struct { + int32_t timerid; + int32_t overrun; + }; + }; + + union { + Sigval sigval; + + // SIGCHLD + struct { + int32_t status; + typename Traits::Clock utime; + typename Traits::Clock stime; + }; + }; + }; + }; +}; + +template +struct SignalStack { + typename Traits::Address stack_pointer; + uint32_t flags; + typename Traits::UInteger32_64Only padding; + typename Traits::Size size; +}; + +template +struct Sigset {}; + +template +struct Sigset< + Traits, + typename std::enable_if::value>::type> { + uint64_t val; +}; + +template +struct Sigset< + Traits, + typename std::enable_if::value>::type> { +#if BUILDFLAG(IS_ANDROID) + uint64_t val; +#else + typename Traits::ULong val[16]; +#endif // BUILDFLAG(IS_ANDROID) +}; + +#if defined(ARCH_CPU_X86_FAMILY) + +struct SignalThreadContext32 { + uint32_t xgs; + uint32_t xfs; + uint32_t xes; + uint32_t xds; + uint32_t edi; + uint32_t esi; + uint32_t ebp; + uint32_t esp; + uint32_t ebx; + uint32_t edx; + uint32_t ecx; + uint32_t eax; + uint32_t trapno; + uint32_t err; + uint32_t eip; + uint32_t xcs; + uint32_t eflags; + uint32_t uesp; + uint32_t xss; +}; + +struct SignalThreadContext64 { + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t rdi; + uint64_t rsi; + uint64_t rbp; + uint64_t rbx; + uint64_t rdx; + uint64_t rax; + uint64_t rcx; + uint64_t rsp; + uint64_t rip; + uint64_t eflags; + uint16_t cs; + uint16_t gs; + uint16_t fs; + uint16_t padding; + uint64_t err; + uint64_t trapno; + uint64_t oldmask; + uint64_t cr2; +}; + +struct SignalFloatContext32 { + CPUContextX86::Fsave fsave; + uint16_t status; + uint16_t magic; + CPUContextX86::Fxsave fxsave[0]; +}; + +using SignalFloatContext64 = CPUContextX86_64::Fxsave; + +struct ContextTraits32 : public Traits32 { + using ThreadContext = SignalThreadContext32; + using FloatContext = SignalFloatContext32; +}; + +struct ContextTraits64 : public Traits64 { + using ThreadContext = SignalThreadContext64; + using FloatContext = SignalFloatContext64; +}; + +template +struct MContext { + typename Traits::ThreadContext gprs; + typename Traits::Address fpptr; + typename Traits::ULong_32Only oldmask; + typename Traits::ULong_32Only cr2; + typename Traits::ULong_64Only reserved[8]; +}; + +template +struct UContext { + typename Traits::ULong flags; + typename Traits::Address link; + SignalStack stack; + MContext mcontext; + Sigset sigmask; + char fpregs_mem[0]; +}; + +#elif defined(ARCH_CPU_ARM_FAMILY) + +struct CoprocessorContextHead { + uint32_t magic; + uint32_t size; +}; + +struct SignalFPSIMDContext { + uint32_t fpsr; + uint32_t fpcr; + uint128_struct vregs[32]; +}; + +struct SignalVFPContext { + FloatContext::f32_t::vfp_t vfp; + struct vfp_exc { + uint32_t fpexc; + uint32_t fpinst; + uint32_t fpinst2; + } vfp_exc; + uint32_t padding; +}; + +struct SignalThreadContext32 { + uint32_t regs[11]; + uint32_t fp; + uint32_t ip; + uint32_t sp; + uint32_t lr; + uint32_t pc; + uint32_t cpsr; +}; + +using SignalThreadContext64 = ThreadContext::t64_t; + +struct MContext32Data { + uint32_t trap_no; + uint32_t error_code; + uint32_t oldmask; + SignalThreadContext32 gprs; + uint32_t fault_address; +}; + +struct MContext64Data { + uint64_t fault_address; + SignalThreadContext64 gprs; +}; + +struct ContextTraits32 : public Traits32 { + using MContext32 = MContext32Data; + using MContext64 = Nothing; +}; + +struct ContextTraits64 : public Traits64 { + using MContext32 = Nothing; + using MContext64 = MContext64Data; +}; + +template +struct UContext { + typename Traits::ULong flags; + typename Traits::Address link; + SignalStack stack; + typename Traits::MContext32 mcontext32; + Sigset sigmask; + char padding[128 - sizeof(sigmask)]; + typename Traits::Char_64Only padding2[8]; + typename Traits::MContext64 mcontext64; + typename Traits::Char_64Only padding3[8]; + char reserved[0]; +}; + +#if defined(ARCH_CPU_ARMEL) +static_assert(offsetof(UContext, mcontext32) == + offsetof(ucontext_t, uc_mcontext), + "context offset mismatch"); +static_assert(offsetof(UContext, reserved) == + offsetof(ucontext_t, uc_regspace), + "regspace offset mismatch"); + +#elif defined(ARCH_CPU_ARM64) +static_assert(offsetof(UContext, mcontext64) == + offsetof(ucontext_t, uc_mcontext), + "context offset mismtach"); +static_assert(offsetof(UContext, reserved) == + offsetof(ucontext_t, uc_mcontext) + + offsetof(mcontext_t, __reserved), + "reserved space offset mismtach"); +#endif + +#elif defined(ARCH_CPU_MIPS_FAMILY) + +struct MContext32 { + uint32_t regmask; + uint32_t status; + uint64_t pc; + uint64_t gregs[32]; + struct { + float _fp_fregs; + unsigned int _fp_pad; + } fpregs[32]; + uint32_t fp_owned; + uint32_t fpc_csr; + uint32_t fpc_eir; + uint32_t used_math; + uint32_t dsp; + uint64_t mdhi; + uint64_t mdlo; + uint32_t hi1; + uint32_t lo1; + uint32_t hi2; + uint32_t lo2; + uint32_t hi3; + uint32_t lo3; +}; + +struct MContext64 { + uint64_t gregs[32]; + double fpregs[32]; + uint64_t mdhi; + uint64_t hi1; + uint64_t hi2; + uint64_t hi3; + uint64_t mdlo; + uint64_t lo1; + uint64_t lo2; + uint64_t lo3; + uint64_t pc; + uint32_t fpc_csr; + uint32_t used_math; + uint32_t dsp; + uint32_t __glibc_reserved1; +}; + +struct SignalThreadContext32 { + uint64_t regs[32]; + uint32_t lo; + uint32_t hi; + uint32_t cp0_epc; + uint32_t cp0_badvaddr; + uint32_t cp0_status; + uint32_t cp0_cause; + + SignalThreadContext32() {} + explicit SignalThreadContext32( + const struct ThreadContext::t32_t& thread_context) { + for (size_t reg = 0; reg < 32; ++reg) { + regs[reg] = thread_context.regs[reg]; + } + lo = thread_context.lo; + hi = thread_context.hi; + cp0_epc = thread_context.cp0_epc; + cp0_badvaddr = thread_context.cp0_badvaddr; + cp0_status = thread_context.cp0_status; + cp0_cause = thread_context.cp0_cause; + } +}; + +struct ContextTraits32 : public Traits32 { + using MContext = MContext32; + using SignalThreadContext = SignalThreadContext32; + using SignalFloatContext = FloatContext::f32_t; + using CPUContext = CPUContextMIPS; +}; + +struct ContextTraits64 : public Traits64 { + using MContext = MContext64; + using SignalThreadContext = ThreadContext::t64_t; + using SignalFloatContext = FloatContext::f64_t; + using CPUContext = CPUContextMIPS64; +}; + +template +struct UContext { + typename Traits::ULong flags; + typename Traits::Address link; + SignalStack stack; + typename Traits::ULong_32Only alignment_padding_; + typename Traits::MContext mcontext; + Sigset sigmask; +}; + +#if defined(ARCH_CPU_MIPSEL) +static_assert(offsetof(UContext, mcontext) == + offsetof(ucontext_t, uc_mcontext), + "context offset mismatch"); +static_assert(offsetof(UContext, mcontext.gregs) == + offsetof(ucontext_t, uc_mcontext.gregs), + "context offset mismatch"); +static_assert(offsetof(UContext, mcontext.fpregs) == + offsetof(ucontext_t, uc_mcontext.fpregs), + "context offset mismatch"); + +#elif defined(ARCH_CPU_MIPS64EL) +static_assert(offsetof(UContext, mcontext) == + offsetof(ucontext_t, uc_mcontext), + "context offset mismtach"); +static_assert(offsetof(UContext, mcontext.gregs) == + offsetof(ucontext_t, uc_mcontext.gregs), + "context offset mismatch"); +static_assert(offsetof(UContext, mcontext.fpregs) == + offsetof(ucontext_t, uc_mcontext.fpregs), + "context offset mismatch"); +#endif + +#else +#error Port. +#endif // ARCH_CPU_X86_FAMILY + +#pragma pack(pop) + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_LINUX_SNAPSHOT_SIGNAL_CONTEXT_H_ diff --git a/shared/sentry/external/crashpad/snapshot/linux/system_snapshot_linux.cc b/shared/sentry/external/crashpad/snapshot/linux/system_snapshot_linux.cc new file mode 100644 index 000000000..e77bcafa9 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/linux/system_snapshot_linux.cc @@ -0,0 +1,432 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/linux/system_snapshot_linux.h" + +#include +#include +#include + +#include + +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/notreached.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_piece.h" +#include "base/strings/stringprintf.h" +#include "build/build_config.h" +#include "snapshot/cpu_context.h" +#include "snapshot/posix/timezone.h" +#include "util/file/file_io.h" +#include "util/numeric/in_range_cast.h" +#include "util/string/split_string.h" + +#if BUILDFLAG(IS_ANDROID) +#include +#endif + +namespace crashpad { +namespace internal { + +namespace { + +bool ReadCPUsOnline(uint32_t* first_cpu, uint8_t* cpu_count) { + std::string contents; + if (!LoggingReadEntireFile(base::FilePath("/sys/devices/system/cpu/online"), + &contents)) { + return false; + } + if (contents.back() != '\n') { + LOG(ERROR) << "format error"; + return false; + } + contents.pop_back(); + + unsigned int count = 0; + unsigned int first = 0; + bool have_first = false; + std::vector ranges = SplitString(contents, ','); + for (const auto& range : ranges) { + std::string left, right; + if (SplitStringFirst(range, '-', &left, &right)) { + unsigned int start, end; + if (!StringToUint(base::StringPiece(left), &start) || + !StringToUint(base::StringPiece(right), &end) || end <= start) { + LOG(ERROR) << "format error: " << range; + return false; + } + if (end <= start) { + LOG(ERROR) << "format error"; + return false; + } + count += end - start + 1; + if (!have_first) { + first = start; + have_first = true; + } + } else { + unsigned int cpuno; + if (!StringToUint(base::StringPiece(range), &cpuno)) { + LOG(ERROR) << "format error"; + return false; + } + if (!have_first) { + first = cpuno; + have_first = true; + } + ++count; + } + } + if (!have_first) { + LOG(ERROR) << "no cpus online"; + return false; + } + *cpu_count = InRangeCast(count, std::numeric_limits::max()); + *first_cpu = first; + return true; +} + +bool ReadFreqFile(const std::string& filename, uint64_t* hz) { + std::string contents; + if (!LoggingReadEntireFile(base::FilePath(filename), &contents)) { + return false; + } + if (contents.back() != '\n') { + LOG(ERROR) << "format error"; + return false; + } + contents.pop_back(); + + uint64_t khz; + if (!base::StringToUint64(base::StringPiece(contents), &khz)) { + LOG(ERROR) << "format error"; + return false; + } + + *hz = khz * 1000; + return true; +} + +#if BUILDFLAG(IS_ANDROID) +bool ReadProperty(const char* property, std::string* value) { + char value_buffer[PROP_VALUE_MAX]; + int length = __system_property_get(property, value_buffer); + if (length <= 0) { + LOG(ERROR) << "Couldn't read property " << property; + return false; + } + *value = value_buffer; + return true; +} +#endif // BUILDFLAG(IS_ANDROID) + +} // namespace + +SystemSnapshotLinux::SystemSnapshotLinux() + : SystemSnapshot(), + os_version_full_(), + os_version_build_(), + process_reader_(nullptr), + snapshot_time_(nullptr), +#if defined(ARCH_CPU_X86_FAMILY) + cpuid_(), +#endif // ARCH_CPU_X86_FAMILY + os_version_major_(-1), + os_version_minor_(-1), + os_version_bugfix_(-1), + target_cpu_(0), + cpu_count_(0), + initialized_() { +} + +SystemSnapshotLinux::~SystemSnapshotLinux() {} + +void SystemSnapshotLinux::Initialize(ProcessReaderLinux* process_reader, + const timeval* snapshot_time) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + process_reader_ = process_reader; + snapshot_time_ = snapshot_time; + +#if BUILDFLAG(IS_ANDROID) + std::string build_string; + if (ReadProperty("ro.build.fingerprint", &build_string)) { + os_version_build_ = build_string; + os_version_full_ = build_string; + } +#endif // BUILDFLAG(IS_ANDROID) + + utsname uts; + if (uname(&uts) != 0) { + PLOG(WARNING) << "uname"; + } else { + if (!os_version_full_.empty()) { + os_version_full_.push_back(' '); + } + os_version_full_ += base::StringPrintf( + "%s %s %s %s", uts.sysname, uts.release, uts.version, uts.machine); + } + ReadKernelVersion(uts.release); + + if (!os_version_build_.empty()) { + os_version_build_.push_back(' '); + } + os_version_build_ += uts.version; + os_version_build_.push_back(' '); + os_version_build_ += uts.machine; + + if (!ReadCPUsOnline(&target_cpu_, &cpu_count_)) { + target_cpu_ = 0; + cpu_count_ = 0; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); +} + +CPUArchitecture SystemSnapshotLinux::GetCPUArchitecture() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if defined(ARCH_CPU_X86_FAMILY) + return process_reader_->Is64Bit() ? kCPUArchitectureX86_64 + : kCPUArchitectureX86; +#elif defined(ARCH_CPU_ARM_FAMILY) + return process_reader_->Is64Bit() ? kCPUArchitectureARM64 + : kCPUArchitectureARM; +#elif defined(ARCH_CPU_MIPS_FAMILY) + return process_reader_->Is64Bit() ? kCPUArchitectureMIPS64EL + : kCPUArchitectureMIPSEL; +#else +#error port to your architecture +#endif +} + +uint32_t SystemSnapshotLinux::CPURevision() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if defined(ARCH_CPU_X86_FAMILY) + return cpuid_.Revision(); +#elif defined(ARCH_CPU_ARM_FAMILY) + // TODO(jperaza): do this. https://crashpad.chromium.org/bug/30 + return 0; +#elif defined(ARCH_CPU_MIPS_FAMILY) + // Not implementable on MIPS + return 0; +#else +#error port to your architecture +#endif +} + +uint8_t SystemSnapshotLinux::CPUCount() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return cpu_count_; +} + +std::string SystemSnapshotLinux::CPUVendor() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if defined(ARCH_CPU_X86_FAMILY) + return cpuid_.Vendor(); +#elif defined(ARCH_CPU_ARM_FAMILY) + // TODO(jperaza): do this. https://crashpad.chromium.org/bug/30 + return std::string(); +#elif defined(ARCH_CPU_MIPS_FAMILY) + // Not implementable on MIPS + return std::string(); +#else +#error port to your architecture +#endif +} + +void SystemSnapshotLinux::CPUFrequency(uint64_t* current_hz, + uint64_t* max_hz) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *current_hz = 0; + *max_hz = 0; + + ReadFreqFile(base::StringPrintf( + "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_cur_freq", + target_cpu_), + current_hz); + + ReadFreqFile(base::StringPrintf( + "/sys/devices/system/cpu/cpu%d/cpufreq/scaling_max_freq", + target_cpu_), + max_hz); +} + +uint32_t SystemSnapshotLinux::CPUX86Signature() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if defined(ARCH_CPU_X86_FAMILY) + return cpuid_.Signature(); +#else + NOTREACHED(); + return 0; +#endif +} + +uint64_t SystemSnapshotLinux::CPUX86Features() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if defined(ARCH_CPU_X86_FAMILY) + return cpuid_.Features(); +#else + NOTREACHED(); + return 0; +#endif +} + +uint64_t SystemSnapshotLinux::CPUX86ExtendedFeatures() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if defined(ARCH_CPU_X86_FAMILY) + return cpuid_.ExtendedFeatures(); +#else + NOTREACHED(); + return 0; +#endif +} + +uint32_t SystemSnapshotLinux::CPUX86Leaf7Features() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if defined(ARCH_CPU_X86_FAMILY) + return cpuid_.Leaf7Features(); +#else + NOTREACHED(); + return 0; +#endif +} + +bool SystemSnapshotLinux::CPUX86SupportsDAZ() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if defined(ARCH_CPU_X86_FAMILY) + return cpuid_.SupportsDAZ(); +#else + NOTREACHED(); + return false; +#endif // ARCH_CPU_X86_FMAILY +} + +SystemSnapshot::OperatingSystem SystemSnapshotLinux::GetOperatingSystem() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if BUILDFLAG(IS_ANDROID) + return kOperatingSystemAndroid; +#else + return kOperatingSystemLinux; +#endif // BUILDFLAG(IS_ANDROID) +} + +bool SystemSnapshotLinux::OSServer() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return false; +} + +void SystemSnapshotLinux::OSVersion(int* major, + int* minor, + int* bugfix, + std::string* build) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *major = os_version_major_; + *minor = os_version_minor_; + *bugfix = os_version_bugfix_; + build->assign(os_version_build_); +} + +std::string SystemSnapshotLinux::OSVersionFull() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return os_version_full_; +} + +std::string SystemSnapshotLinux::MachineDescription() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if BUILDFLAG(IS_ANDROID) + std::string description; + std::string prop; + if (ReadProperty("ro.product.model", &prop)) { + description += prop; + } + if (ReadProperty("ro.product.board", &prop)) { + if (!description.empty()) { + description.push_back(' '); + } + description += prop; + } + return description; +#else + return std::string(); +#endif // BUILDFLAG(IS_ANDROID) +} + +bool SystemSnapshotLinux::NXEnabled() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if defined(ARCH_CPU_X86_FAMILY) + return cpuid_.NXEnabled(); +#elif defined(ARCH_CPU_ARM_FAMILY) + // TODO(jperaza): do this. https://crashpad.chromium.org/bug/30 + return false; +#elif defined(ARCH_CPU_MIPS_FAMILY) + // Not implementable on MIPS + return false; +#else +#error Port. +#endif // ARCH_CPU_X86_FAMILY +} + +void SystemSnapshotLinux::TimeZone(DaylightSavingTimeStatus* dst_status, + int* standard_offset_seconds, + int* daylight_offset_seconds, + std::string* standard_name, + std::string* daylight_name) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + internal::TimeZone(*snapshot_time_, + dst_status, + standard_offset_seconds, + daylight_offset_seconds, + standard_name, + daylight_name); +} + +void SystemSnapshotLinux::ReadKernelVersion(const std::string& version_string) { + std::vector versions = SplitString(version_string, '.'); + if (versions.size() < 3) { + LOG(WARNING) << "format error"; + return; + } + + if (!StringToInt(base::StringPiece(versions[0]), &os_version_major_)) { + LOG(WARNING) << "no kernel version"; + return; + } + DCHECK_GE(os_version_major_, 3); + + if (!StringToInt(base::StringPiece(versions[1]), &os_version_minor_)) { + LOG(WARNING) << "no major revision"; + return; + } + DCHECK_GE(os_version_minor_, 0); + + size_t minor_rev_end = versions[2].find_first_not_of("0123456789"); + if (minor_rev_end == std::string::npos) { + minor_rev_end = versions[2].size(); + } + if (!StringToInt(base::StringPiece(versions[2].c_str(), minor_rev_end), + &os_version_bugfix_)) { + LOG(WARNING) << "no minor revision"; + return; + } + DCHECK_GE(os_version_bugfix_, 0); + + if (!os_version_build_.empty()) { + os_version_build_.push_back(' '); + } + os_version_build_ += versions[2].substr(minor_rev_end); +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/linux/system_snapshot_linux.h b/shared/sentry/external/crashpad/snapshot/linux/system_snapshot_linux.h new file mode 100644 index 000000000..e13c9dfb3 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/linux/system_snapshot_linux.h @@ -0,0 +1,114 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_LINUX_SYSTEM_SNAPSHOT_LINUX_H_ +#define CRASHPAD_SNAPSHOT_LINUX_SYSTEM_SNAPSHOT_LINUX_H_ + +#include +#include + +#include + +#include "build/build_config.h" +#include "snapshot/linux/process_reader_linux.h" +#include "snapshot/system_snapshot.h" +#include "util/misc/initialization_state_dcheck.h" + +#if defined(ARCH_CPU_X86_FAMILY) +#include "snapshot/x86/cpuid_reader.h" +#endif // ARCH_CPU_X86_FAMILY + +namespace crashpad { +namespace internal { + +//! \brief A SystemSnapshot of the running system, when the system runs Linux. +class SystemSnapshotLinux final : public SystemSnapshot { + public: + SystemSnapshotLinux(); + + SystemSnapshotLinux(const SystemSnapshotLinux&) = delete; + SystemSnapshotLinux& operator=(const SystemSnapshotLinux&) = delete; + + ~SystemSnapshotLinux() override; + + //! \brief Initializes the object. + //! + //! \param[in] process_reader A reader for the process being snapshotted. + //! \n\n + //! It seems odd that a system snapshot implementation would need a + //! ProcessReaderLinux, but some of the information reported about the + //! system depends on the process it’s being reported for. For example, + //! the architecture returned by GetCPUArchitecture() should be the + //! architecture of the process, which may be different than the native + //! architecture of the system: an x86_64 system can run both x86_64 and + //! 32-bit x86 processes. + //! \param[in] snapshot_time The time of the snapshot being taken. + //! \n\n + //! This parameter is necessary for TimeZone() to determine whether + //! daylight saving time was in effect at the time the snapshot was taken. + //! Otherwise, it would need to base its determination on the current + //! time, which may be different than the snapshot time for snapshots + //! generated around the daylight saving transition time. + void Initialize(ProcessReaderLinux* process_reader, + const timeval* snapshot_time); + + // SystemSnapshot: + + CPUArchitecture GetCPUArchitecture() const override; + uint32_t CPURevision() const override; + uint8_t CPUCount() const override; + std::string CPUVendor() const override; + void CPUFrequency(uint64_t* current_hz, uint64_t* max_hz) const override; + uint32_t CPUX86Signature() const override; + uint64_t CPUX86Features() const override; + uint64_t CPUX86ExtendedFeatures() const override; + uint32_t CPUX86Leaf7Features() const override; + bool CPUX86SupportsDAZ() const override; + OperatingSystem GetOperatingSystem() const override; + bool OSServer() const override; + void OSVersion(int* major, + int* minor, + int* bugfix, + std::string* build) const override; + std::string OSVersionFull() const override; + bool NXEnabled() const override; + std::string MachineDescription() const override; + void TimeZone(DaylightSavingTimeStatus* dst_status, + int* standard_offset_seconds, + int* daylight_offset_seconds, + std::string* standard_name, + std::string* daylight_name) const override; + + private: + void ReadKernelVersion(const std::string& version_string); + + std::string os_version_full_; + std::string os_version_build_; + ProcessReaderLinux* process_reader_; // weak + const timeval* snapshot_time_; // weak +#if defined(ARCH_CPU_X86_FAMILY) + CpuidReader cpuid_; +#endif // ARCH_CPU_X86_FAMILY + int os_version_major_; + int os_version_minor_; + int os_version_bugfix_; + uint32_t target_cpu_; + uint8_t cpu_count_; + InitializationStateDcheck initialized_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_LINUX_SYSTEM_SNAPSHOT_LINUX_H_ diff --git a/shared/sentry/external/crashpad/snapshot/linux/system_snapshot_linux_test.cc b/shared/sentry/external/crashpad/snapshot/linux/system_snapshot_linux_test.cc new file mode 100644 index 000000000..09c219602 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/linux/system_snapshot_linux_test.cc @@ -0,0 +1,93 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/linux/system_snapshot_linux.h" + +#include +#include + +#include + +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "snapshot/linux/process_reader_linux.h" +#include "test/errors.h" +#include "test/linux/fake_ptrace_connection.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(SystemSnapshotLinux, Basic) { + FakePtraceConnection connection; + ASSERT_TRUE(connection.Initialize(getpid())); + + ProcessReaderLinux process_reader; + ASSERT_TRUE(process_reader.Initialize(&connection)); + + timeval snapshot_time; + ASSERT_EQ(gettimeofday(&snapshot_time, nullptr), 0) + << ErrnoMessage("gettimeofday"); + + internal::SystemSnapshotLinux system; + system.Initialize(&process_reader, &snapshot_time); + + EXPECT_GT(system.CPUCount(), 0u); + + uint64_t current_hz, max_hz; + system.CPUFrequency(¤t_hz, &max_hz); + // For short-term loads, modern CPUs can boost single-core frequency beyond + // the advertised base clock. Let's assume this is no more than a factor 2. + EXPECT_GE(max_hz * 2, current_hz); + + int major, minor, bugfix; + std::string build; + system.OSVersion(&major, &minor, &bugfix, &build); + EXPECT_GE(major, 3); + EXPECT_GE(minor, 0); + EXPECT_GE(bugfix, 0); + EXPECT_FALSE(build.empty()); + + EXPECT_FALSE(system.OSVersionFull().empty()); + + // No expectations; just make sure these can be called successfully. + system.CPURevision(); + system.NXEnabled(); + +#if BUILDFLAG(IS_ANDROID) + EXPECT_FALSE(system.MachineDescription().empty()); +#else + system.MachineDescription(); +#endif // BUILDFLAG(IS_ANDROID) + +#if defined(ARCH_CPU_X86_FAMILY) + system.CPUX86Signature(); + system.CPUX86Features(); + system.CPUX86ExtendedFeatures(); + system.CPUX86Leaf7Features(); + + EXPECT_PRED1( + [](std::string vendor) { + return vendor == "GenuineIntel" || vendor == "AuthenticAMD" || + vendor == "HygonGenuine"; + }, + system.CPUVendor()); + + EXPECT_TRUE(system.CPUX86SupportsDAZ()); +#endif // ARCH_CPU_X86_FAMILY +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/linux/test_modules.cc b/shared/sentry/external/crashpad/snapshot/linux/test_modules.cc new file mode 100644 index 000000000..b2450c206 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/linux/test_modules.cc @@ -0,0 +1,262 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/linux/test_modules.h" + +#include + +#include + +#include "base/check_op.h" +#include "base/files/file_path.h" +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "test/test_paths.h" +#include "util/file/filesystem.h" +#include "util/file/file_writer.h" + +namespace crashpad { +namespace test { + +bool WriteTestModule(const base::FilePath& module_path, + const std::string& soname) { +#if defined(ARCH_CPU_64_BITS) + using Ehdr = Elf64_Ehdr; + using Phdr = Elf64_Phdr; + using Shdr = Elf64_Shdr; + using Dyn = Elf64_Dyn; + using Sym = Elf64_Sym; + unsigned char elf_class = ELFCLASS64; +#else + using Ehdr = Elf32_Ehdr; + using Phdr = Elf32_Phdr; + using Shdr = Elf32_Shdr; + using Dyn = Elf32_Dyn; + using Sym = Elf32_Sym; + unsigned char elf_class = ELFCLASS32; +#endif + + struct { + Ehdr ehdr; + struct { + Phdr load1; + Phdr load2; + Phdr dynamic; + } phdr_table; + struct { + Dyn hash; + Dyn strtab; + Dyn symtab; + Dyn strsz; + Dyn syment; + Dyn soname; + Dyn null; + } dynamic_array; + struct { + Elf32_Word nbucket; + Elf32_Word nchain; + Elf32_Word bucket; + Elf32_Word chain; + } hash_table; + char string_table[32]; + struct { + } section_header_string_table; + struct { + Sym und_symbol; + } symbol_table; + struct { + Shdr null; + Shdr dynamic; + Shdr string_table; + Shdr section_header_string_table; + } shdr_table; + } module = {}; + + module.ehdr.e_ident[EI_MAG0] = ELFMAG0; + module.ehdr.e_ident[EI_MAG1] = ELFMAG1; + module.ehdr.e_ident[EI_MAG2] = ELFMAG2; + module.ehdr.e_ident[EI_MAG3] = ELFMAG3; + + module.ehdr.e_ident[EI_CLASS] = elf_class; + +#if defined(ARCH_CPU_LITTLE_ENDIAN) + module.ehdr.e_ident[EI_DATA] = ELFDATA2LSB; +#else + module.ehdr.e_ident[EI_DATA] = ELFDATA2MSB; +#endif // ARCH_CPU_LITTLE_ENDIAN + + module.ehdr.e_ident[EI_VERSION] = EV_CURRENT; + + module.ehdr.e_type = ET_DYN; + +#if defined(ARCH_CPU_X86) + module.ehdr.e_machine = EM_386; +#elif defined(ARCH_CPU_X86_64) + module.ehdr.e_machine = EM_X86_64; +#elif defined(ARCH_CPU_ARMEL) + module.ehdr.e_machine = EM_ARM; +#elif defined(ARCH_CPU_ARM64) + module.ehdr.e_machine = EM_AARCH64; +#elif defined(ARCH_CPU_MIPSEL) || defined(ARCH_CPU_MIPS64EL) + module.ehdr.e_machine = EM_MIPS; +#endif + + module.ehdr.e_version = EV_CURRENT; + module.ehdr.e_ehsize = sizeof(module.ehdr); + + module.ehdr.e_phoff = offsetof(decltype(module), phdr_table); + module.ehdr.e_phnum = sizeof(module.phdr_table) / sizeof(Phdr); + module.ehdr.e_phentsize = sizeof(Phdr); + + module.ehdr.e_shoff = offsetof(decltype(module), shdr_table); + module.ehdr.e_shentsize = sizeof(Shdr); + module.ehdr.e_shnum = sizeof(module.shdr_table) / sizeof(Shdr); + module.ehdr.e_shstrndx = + offsetof(decltype(module.shdr_table), section_header_string_table) / + sizeof(Shdr); + + const size_t page_size = getpagesize(); + auto align = [page_size](uintptr_t addr) { + return (addr + page_size - 1) & ~(page_size - 1); + }; + constexpr size_t segment_size = offsetof(decltype(module), shdr_table); + + // This test module covers cases where: + // 1. Multiple segments are mapped from file offset 0. + // 2. Load bias is negative. + + const uintptr_t load2_vaddr = align(std::numeric_limits::max() - + align(segment_size) - page_size); + const uintptr_t load1_vaddr = load2_vaddr - align(segment_size); + + module.phdr_table.load1.p_type = PT_LOAD; + module.phdr_table.load1.p_offset = 0; + module.phdr_table.load1.p_vaddr = load1_vaddr; + module.phdr_table.load1.p_filesz = segment_size; + module.phdr_table.load1.p_memsz = segment_size; + module.phdr_table.load1.p_flags = PF_R; + module.phdr_table.load1.p_align = page_size; + + module.phdr_table.load2.p_type = PT_LOAD; + module.phdr_table.load2.p_offset = 0; + module.phdr_table.load2.p_vaddr = load2_vaddr; + module.phdr_table.load2.p_filesz = segment_size; + module.phdr_table.load2.p_memsz = segment_size; + module.phdr_table.load2.p_flags = PF_R | PF_W; + module.phdr_table.load2.p_align = page_size; + + module.phdr_table.dynamic.p_type = PT_DYNAMIC; + module.phdr_table.dynamic.p_offset = + offsetof(decltype(module), dynamic_array); + module.phdr_table.dynamic.p_vaddr = + load2_vaddr + module.phdr_table.dynamic.p_offset; + module.phdr_table.dynamic.p_filesz = sizeof(module.dynamic_array); + module.phdr_table.dynamic.p_memsz = sizeof(module.dynamic_array); + module.phdr_table.dynamic.p_flags = PF_R | PF_W; + module.phdr_table.dynamic.p_align = 8; + + module.dynamic_array.hash.d_tag = DT_HASH; + module.dynamic_array.hash.d_un.d_ptr = + load1_vaddr + offsetof(decltype(module), hash_table); + module.dynamic_array.strtab.d_tag = DT_STRTAB; + module.dynamic_array.strtab.d_un.d_ptr = + load1_vaddr + offsetof(decltype(module), string_table); + module.dynamic_array.symtab.d_tag = DT_SYMTAB; + module.dynamic_array.symtab.d_un.d_ptr = + load1_vaddr + offsetof(decltype(module), symbol_table); + module.dynamic_array.strsz.d_tag = DT_STRSZ; + module.dynamic_array.strsz.d_un.d_val = sizeof(module.string_table); + module.dynamic_array.syment.d_tag = DT_SYMENT; + module.dynamic_array.syment.d_un.d_val = sizeof(Sym); + constexpr size_t kSonameOffset = 1; + module.dynamic_array.soname.d_tag = DT_SONAME; + module.dynamic_array.soname.d_un.d_val = kSonameOffset; + + module.dynamic_array.null.d_tag = DT_NULL; + + module.hash_table.nbucket = 1; + module.hash_table.nchain = 1; + module.hash_table.bucket = 0; + module.hash_table.chain = 0; + + if (sizeof(module.string_table) < soname.size() + 2) { + ADD_FAILURE() << "string table too small"; + return false; + } + module.string_table[0] = '\0'; + memcpy(&module.string_table[kSonameOffset], soname.c_str(), soname.size()); + + module.shdr_table.null.sh_type = SHT_NULL; + + module.shdr_table.dynamic.sh_name = 0; + module.shdr_table.dynamic.sh_type = SHT_DYNAMIC; + module.shdr_table.dynamic.sh_flags = SHF_WRITE | SHF_ALLOC; + module.shdr_table.dynamic.sh_addr = module.phdr_table.dynamic.p_vaddr; + module.shdr_table.dynamic.sh_offset = module.phdr_table.dynamic.p_offset; + module.shdr_table.dynamic.sh_size = module.phdr_table.dynamic.p_filesz; + module.shdr_table.dynamic.sh_link = + offsetof(decltype(module.shdr_table), string_table) / sizeof(Shdr); + + module.shdr_table.string_table.sh_name = 0; + module.shdr_table.string_table.sh_type = SHT_STRTAB; + module.shdr_table.string_table.sh_offset = + offsetof(decltype(module), string_table); + module.shdr_table.string_table.sh_size = sizeof(module.string_table); + + module.shdr_table.section_header_string_table.sh_name = 0; + module.shdr_table.section_header_string_table.sh_type = SHT_STRTAB; + module.shdr_table.section_header_string_table.sh_offset = + offsetof(decltype(module), section_header_string_table); + module.shdr_table.section_header_string_table.sh_size = + sizeof(module.section_header_string_table); + + FileWriter writer; + if (!writer.Open(module_path, + FileWriteMode::kCreateOrFail, + FilePermissions::kWorldReadable)) { + ADD_FAILURE(); + return false; + } + + if (!writer.Write(&module, sizeof(module))) { + ADD_FAILURE(); + LoggingRemoveFile(module_path); + return false; + } + + return true; +} + +ScopedModuleHandle LoadTestModule(const std::string& module_name, + const std::string& module_soname) { + base::FilePath module_path( + TestPaths::Executable().DirName().Append(module_name)); + + if (!WriteTestModule(module_path, module_soname)) { + return ScopedModuleHandle(nullptr); + } + EXPECT_TRUE(IsRegularFile(module_path)); + + ScopedModuleHandle handle( + dlopen(module_path.value().c_str(), RTLD_LAZY | RTLD_LOCAL)); + EXPECT_TRUE(handle.valid()) + << "dlopen: " << module_path.value() << " " << dlerror(); + + EXPECT_TRUE(LoggingRemoveFile(module_path)); + + return handle; +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/linux/test_modules.h b/shared/sentry/external/crashpad/snapshot/linux/test_modules.h new file mode 100644 index 000000000..b791480a1 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/linux/test_modules.h @@ -0,0 +1,37 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_LINUX_TEST_MODULES_H_ +#define CRASHPAD_SNAPSHOT_LINUX_TEST_MODULES_H_ + +#include + +#include "test/scoped_module_handle.h" + +namespace crashpad { +namespace test { + +//! \brief Constructs and loads a test module. +//! +//! \param module_name The filename of the mdoule. +//! \param module_soname The SONAME for the module. +//! \return a handle to the loaded module on success. On failure, the handle +//! will be invalid and a message will be logged. +ScopedModuleHandle LoadTestModule(const std::string& module_name, + const std::string& module_soname); + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_LINUX_DEBUG_RENDEZVOUS_H_ diff --git a/shared/sentry/external/crashpad/snapshot/linux/thread_snapshot_linux.cc b/shared/sentry/external/crashpad/snapshot/linux/thread_snapshot_linux.cc new file mode 100644 index 000000000..bfbeac6c3 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/linux/thread_snapshot_linux.cc @@ -0,0 +1,315 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/linux/thread_snapshot_linux.h" + +#include + +#ifdef CLIENT_STACKTRACES_ENABLED +#include +#include +#include +#endif + +#include "base/logging.h" +#include "snapshot/linux/capture_memory_delegate_linux.h" +#include "snapshot/linux/cpu_context_linux.h" +#include "util/misc/reinterpret_bytes.h" + +namespace crashpad { +namespace internal { + +namespace { + +int ComputeThreadPriority(int static_priority, + int sched_policy, + int nice_value) { + // Map Linux scheduling policy, static priority, and nice value into a + // single int value. + // + // The possible policies in order of approximate priority (low to high) are + // SCHED_IDLE + // SCHED_BATCH + // SCHED_OTHER + // SCHED_RR + // SCHED_FIFO + // + // static_priority is not used for OTHER, BATCH, or IDLE and should be 0. + // For FIFO and RR, static_priority should range from 1 to 99 with 99 being + // the highest priority. + // + // nice value ranges from -20 to 19, with -20 being highest priority + + enum class Policy : uint8_t { + kUnknown = 0, + kIdle, + kBatch, + kOther, + kRR, + kFIFO + }; + + struct LinuxPriority { +#if defined(ARCH_CPU_LITTLE_ENDIAN) + // nice values affect how dynamic priorities are updated, which only + // matters for threads with the same static priority. + uint8_t nice_value = 0; + + // The scheduling policy also affects how threads with the same static + // priority are ordered, but has greater impact than nice value. + Policy policy = Policy::kUnknown; + + // The static priority is the most significant in determining overall + // priority. + uint8_t static_priority = 0; + + // Put this in the most significant byte position to prevent negative + // priorities. + uint8_t unused = 0; +#elif defined(ARCH_CPU_BIG_ENDIAN) + uint8_t unused = 0; + uint8_t static_priority = 0; + Policy policy = Policy::kUnknown; + uint8_t nice_value = 0; +#endif // ARCH_CPU_LITTLE_ENDIAN + }; + static_assert(sizeof(LinuxPriority) <= sizeof(int), "priority is too large"); + + LinuxPriority prio; + + // Lower nice values have higher priority, so negate them and add 20 to put + // them in the range 1-40 with 40 being highest priority. + if (nice_value < -20 || nice_value > 19) { + LOG(WARNING) << "invalid nice value " << nice_value; + prio.nice_value = 0; + } else { + prio.nice_value = -1 * nice_value + 20; + } + + switch (sched_policy) { + case SCHED_IDLE: + prio.policy = Policy::kIdle; + break; + case SCHED_BATCH: + prio.policy = Policy::kBatch; + break; + case SCHED_OTHER: + prio.policy = Policy::kOther; + break; + case SCHED_RR: + prio.policy = Policy::kRR; + break; + case SCHED_FIFO: + prio.policy = Policy::kFIFO; + break; + default: + prio.policy = Policy::kUnknown; + LOG(WARNING) << "Unknown scheduling policy " << sched_policy; + } + + if (static_priority < 0 || static_priority > 99) { + LOG(WARNING) << "invalid static priority " << static_priority; + } + prio.static_priority = static_priority; + + int priority; + if (!ReinterpretBytes(prio, &priority)) { + LOG(ERROR) << "Couldn't set priority"; + return -1; + } + return priority; +} + +} // namespace + +ThreadSnapshotLinux::ThreadSnapshotLinux() + : ThreadSnapshot(), + context_union_(), + context_(), + stack_(), + thread_specific_data_address_(0), + thread_id_(-1), + priority_(-1), + initialized_() {} + +ThreadSnapshotLinux::~ThreadSnapshotLinux() {} + +bool ThreadSnapshotLinux::Initialize( + ProcessReaderLinux* process_reader, + const ProcessReaderLinux::Thread& thread, + uint32_t* gather_indirectly_referenced_memory_bytes_remaining) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + +#if defined(ARCH_CPU_X86_FAMILY) + if (process_reader->Is64Bit()) { + context_.architecture = kCPUArchitectureX86_64; + context_.x86_64 = &context_union_.x86_64; + InitializeCPUContextX86_64(thread.thread_info.thread_context.t64, + thread.thread_info.float_context.f64, + context_.x86_64); + } else { + context_.architecture = kCPUArchitectureX86; + context_.x86 = &context_union_.x86; + InitializeCPUContextX86(thread.thread_info.thread_context.t32, + thread.thread_info.float_context.f32, + context_.x86); + } +#elif defined(ARCH_CPU_ARM_FAMILY) + if (process_reader->Is64Bit()) { + context_.architecture = kCPUArchitectureARM64; + context_.arm64 = &context_union_.arm64; + InitializeCPUContextARM64(thread.thread_info.thread_context.t64, + thread.thread_info.float_context.f64, + context_.arm64); + } else { + context_.architecture = kCPUArchitectureARM; + context_.arm = &context_union_.arm; + InitializeCPUContextARM(thread.thread_info.thread_context.t32, + thread.thread_info.float_context.f32, + context_.arm); + } +#elif defined(ARCH_CPU_MIPS_FAMILY) + if (process_reader->Is64Bit()) { + context_.architecture = kCPUArchitectureMIPS64EL; + context_.mips64 = &context_union_.mips64; + InitializeCPUContextMIPS( + thread.thread_info.thread_context.t64, + thread.thread_info.float_context.f64, + context_.mips64); + } else { + context_.architecture = kCPUArchitectureMIPSEL; + context_.mipsel = &context_union_.mipsel; + InitializeCPUContextMIPS( + SignalThreadContext32(thread.thread_info.thread_context.t32), + thread.thread_info.float_context.f32, + context_.mipsel); + } +#else +#error Port. +#endif + + stack_.Initialize(process_reader->Memory(), + thread.stack_region_address, + thread.stack_region_size); + + thread_specific_data_address_ = + thread.thread_info.thread_specific_data_address; + + thread_id_ = thread.tid; + +#ifdef CLIENT_STACKTRACES_ENABLED + void* upt = _UPT_create(thread_id_); + if (upt) { + unw_addr_space_t as = + unw_create_addr_space(&_UPT_accessors, __LITTLE_ENDIAN); + unw_cursor_t cursor; + if (unw_init_remote(&cursor, as, upt) == UNW_ESUCCESS) { + do { + unw_word_t addr; + if (unw_get_reg(&cursor, UNW_REG_IP, &addr) < 0) { + return false; + } + + std::string sym(""); + char buf[1024]; + unw_word_t symbol_offset; + if (unw_get_proc_name(&cursor, buf, sizeof(buf), &symbol_offset) == + UNW_ESUCCESS) { + sym = std::string(buf); + } + + FrameSnapshot frame(addr, sym); + frames_.push_back(frame); + } while (unw_step(&cursor) > 0); + } + + unw_destroy_addr_space(as); + _UPT_destroy(upt); + } +#endif + + priority_ = + thread.have_priorities + ? ComputeThreadPriority( + thread.static_priority, thread.sched_policy, thread.nice_value) + : -1; + + CaptureMemoryDelegateLinux capture_memory_delegate( + process_reader, + &thread, + &pointed_to_memory_, + gather_indirectly_referenced_memory_bytes_remaining); + CaptureMemory::PointedToByContext(context_, &capture_memory_delegate); + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +const CPUContext* ThreadSnapshotLinux::Context() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &context_; +} + +const MemorySnapshot* ThreadSnapshotLinux::Stack() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &stack_; +} + +uint64_t ThreadSnapshotLinux::ThreadID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_id_; +} + +int ThreadSnapshotLinux::SuspendCount() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return 0; +} + +int ThreadSnapshotLinux::Priority() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return priority_; +} + +uint64_t ThreadSnapshotLinux::ThreadSpecificDataAddress() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_specific_data_address_; +} + +std::vector ThreadSnapshotLinux::ExtraMemory() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector result; + result.reserve(pointed_to_memory_.size()); + for (const auto& pointed_to_memory : pointed_to_memory_) { + result.push_back(pointed_to_memory.get()); + } + return result; +} + +#ifdef CLIENT_STACKTRACES_ENABLED +void ThreadSnapshotLinux::TrimStackTrace(uint64_t exception_address) { + auto start_frame = begin(frames_); + for (; start_frame != end(frames_); start_frame++) { + // These two addresses are never equivalent to each other + if (start_frame->InstructionAddr() == exception_address) { + break; + } + } + if (start_frame < end(frames_)) { + frames_.erase(begin(frames_), start_frame); + } +} +#endif + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/linux/thread_snapshot_linux.h b/shared/sentry/external/crashpad/snapshot/linux/thread_snapshot_linux.h new file mode 100644 index 000000000..38f8c7d26 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/linux/thread_snapshot_linux.h @@ -0,0 +1,96 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_LINUX_THREAD_SNAPSHOT_LINUX_H_ +#define CRASHPAD_SNAPSHOT_LINUX_THREAD_SNAPSHOT_LINUX_H_ + +#include + +#include "build/build_config.h" +#include "snapshot/cpu_context.h" +#include "snapshot/linux/process_reader_linux.h" +#include "snapshot/memory_snapshot.h" +#include "snapshot/memory_snapshot_generic.h" +#include "snapshot/thread_snapshot.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { +namespace internal { + +//! \brief A ThreadSnapshot of a thread on a Linux system. +class ThreadSnapshotLinux final : public ThreadSnapshot { + public: + ThreadSnapshotLinux(); + + ThreadSnapshotLinux(const ThreadSnapshotLinux&) = delete; + ThreadSnapshotLinux& operator=(const ThreadSnapshotLinux&) = delete; + + ~ThreadSnapshotLinux() override; + + //! \brief Initializes the object. + //! + //! \param[in] process_reader A ProcessReaderLinux for the process containing + //! the thread. + //! \param[in] thread The thread within the ProcessReaderLinux for + //! which the snapshot should be created. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! a message logged. + bool Initialize( + ProcessReaderLinux* process_reader, + const ProcessReaderLinux::Thread& thread, + uint32_t* gather_indirectly_referenced_memory_bytes_remaining); + + // ThreadSnapshot: + + const CPUContext* Context() const override; + const MemorySnapshot* Stack() const override; + uint64_t ThreadID() const override; + int SuspendCount() const override; + int Priority() const override; + uint64_t ThreadSpecificDataAddress() const override; + std::vector ExtraMemory() const override; + +#ifdef CLIENT_STACKTRACES_ENABLED + void TrimStackTrace(uint64_t exception_address); +#endif + + private: + union { +#if defined(ARCH_CPU_X86_FAMILY) + CPUContextX86 x86; + CPUContextX86_64 x86_64; +#elif defined(ARCH_CPU_ARM_FAMILY) + CPUContextARM arm; + CPUContextARM64 arm64; +#elif defined(ARCH_CPU_MIPS_FAMILY) + CPUContextMIPS mipsel; + CPUContextMIPS64 mips64; +#else +#error Port. +#endif // ARCH_CPU_X86_FAMILY + } context_union_; + CPUContext context_; + MemorySnapshotGeneric stack_; + LinuxVMAddress thread_specific_data_address_; + pid_t thread_id_; + int priority_; + InitializationStateDcheck initialized_; + std::vector> pointed_to_memory_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_LINUX_THREAD_SNAPSHOT_LINUX_H_ diff --git a/shared/sentry/external/crashpad/snapshot/mac/cpu_context_mac.cc b/shared/sentry/external/crashpad/snapshot/mac/cpu_context_mac.cc new file mode 100644 index 000000000..0a30fde42 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/cpu_context_mac.cc @@ -0,0 +1,594 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/mac/cpu_context_mac.h" + +#include +#include + +#include "base/logging.h" +#include "base/notreached.h" + +namespace crashpad { + +#if defined(ARCH_CPU_X86_FAMILY) + +namespace { + +void InitializeCPUContextX86Thread( + CPUContextX86* context, + const x86_thread_state32_t* x86_thread_state32) { + context->eax = x86_thread_state32->__eax; + context->ebx = x86_thread_state32->__ebx; + context->ecx = x86_thread_state32->__ecx; + context->edx = x86_thread_state32->__edx; + context->edi = x86_thread_state32->__edi; + context->esi = x86_thread_state32->__esi; + context->ebp = x86_thread_state32->__ebp; + context->esp = x86_thread_state32->__esp; + context->eip = x86_thread_state32->__eip; + context->eflags = x86_thread_state32->__eflags; + context->cs = x86_thread_state32->__cs; + context->ds = x86_thread_state32->__ds; + context->es = x86_thread_state32->__es; + context->fs = x86_thread_state32->__fs; + context->gs = x86_thread_state32->__gs; + context->ss = x86_thread_state32->__ss; +} + +void InitializeCPUContextX86Float( + CPUContextX86* context, const x86_float_state32_t* x86_float_state32) { + // This relies on both x86_float_state32_t and context->fxsave having + // identical (fxsave) layout. + static_assert(offsetof(x86_float_state32_t, __fpu_reserved1) - + offsetof(x86_float_state32_t, __fpu_fcw) == + sizeof(context->fxsave), + "types must be equivalent"); + + memcpy( + &context->fxsave, &x86_float_state32->__fpu_fcw, sizeof(context->fxsave)); +} + +void InitializeCPUContextX86Debug( + CPUContextX86* context, const x86_debug_state32_t* x86_debug_state32) { + context->dr0 = x86_debug_state32->__dr0; + context->dr1 = x86_debug_state32->__dr1; + context->dr2 = x86_debug_state32->__dr2; + context->dr3 = x86_debug_state32->__dr3; + context->dr4 = x86_debug_state32->__dr4; + context->dr5 = x86_debug_state32->__dr5; + context->dr6 = x86_debug_state32->__dr6; + context->dr7 = x86_debug_state32->__dr7; +} + +// Initializes |context| from the native thread state structure |state|, which +// is interpreted according to |flavor|. |state_count| must be at least the +// expected size for |flavor|. This handles the architecture-specific +// x86_THREAD_STATE32, x86_FLOAT_STATE32, and x86_DEBUG_STATE32 flavors. It also +// handles the universal x86_THREAD_STATE, x86_FLOAT_STATE, and x86_DEBUG_STATE +// flavors provided that the associated structure carries 32-bit data of the +// corresponding state type. |flavor| may be THREAD_STATE_NONE to avoid setting +// any thread state in |context|. This returns the architecture-specific flavor +// value for the thread state that was actually set, or THREAD_STATE_NONE if no +// thread state was set. +thread_state_flavor_t InitializeCPUContextX86Flavor( + CPUContextX86* context, + thread_state_flavor_t flavor, + ConstThreadState state, + mach_msg_type_number_t state_count) { + mach_msg_type_number_t expected_state_count; + switch (flavor) { + case x86_THREAD_STATE: + expected_state_count = x86_THREAD_STATE_COUNT; + break; + case x86_FLOAT_STATE: + expected_state_count = x86_FLOAT_STATE_COUNT; + break; + case x86_DEBUG_STATE: + expected_state_count = x86_DEBUG_STATE_COUNT; + break; + case x86_THREAD_STATE32: + expected_state_count = x86_THREAD_STATE32_COUNT; + break; + case x86_FLOAT_STATE32: + expected_state_count = x86_FLOAT_STATE32_COUNT; + break; + case x86_DEBUG_STATE32: + expected_state_count = x86_DEBUG_STATE32_COUNT; + break; + case THREAD_STATE_NONE: + expected_state_count = 0; + break; + default: + LOG(WARNING) << "unhandled flavor " << flavor; + return THREAD_STATE_NONE; + } + + if (state_count < expected_state_count) { + LOG(WARNING) << "expected state_count " << expected_state_count + << " for flavor " << flavor << ", observed " << state_count; + return THREAD_STATE_NONE; + } + + switch (flavor) { + case x86_THREAD_STATE: { + const x86_thread_state_t* x86_thread_state = + reinterpret_cast(state); + if (x86_thread_state->tsh.flavor != x86_THREAD_STATE32) { + LOG(WARNING) << "expected flavor x86_THREAD_STATE32, observed " + << x86_thread_state->tsh.flavor; + return THREAD_STATE_NONE; + } + return InitializeCPUContextX86Flavor( + context, + x86_thread_state->tsh.flavor, + reinterpret_cast(&x86_thread_state->uts.ts32), + x86_thread_state->tsh.count); + } + + case x86_FLOAT_STATE: { + const x86_float_state_t* x86_float_state = + reinterpret_cast(state); + if (x86_float_state->fsh.flavor != x86_FLOAT_STATE32) { + LOG(WARNING) << "expected flavor x86_FLOAT_STATE32, observed " + << x86_float_state->fsh.flavor; + return THREAD_STATE_NONE; + } + return InitializeCPUContextX86Flavor( + context, + x86_float_state->fsh.flavor, + reinterpret_cast(&x86_float_state->ufs.fs32), + x86_float_state->fsh.count); + } + + case x86_DEBUG_STATE: { + const x86_debug_state_t* x86_debug_state = + reinterpret_cast(state); + if (x86_debug_state->dsh.flavor != x86_DEBUG_STATE32) { + LOG(WARNING) << "expected flavor x86_DEBUG_STATE32, observed " + << x86_debug_state->dsh.flavor; + return THREAD_STATE_NONE; + } + return InitializeCPUContextX86Flavor( + context, + x86_debug_state->dsh.flavor, + reinterpret_cast(&x86_debug_state->uds.ds32), + x86_debug_state->dsh.count); + } + + case x86_THREAD_STATE32: { + const x86_thread_state32_t* x86_thread_state32 = + reinterpret_cast(state); + InitializeCPUContextX86Thread(context, x86_thread_state32); + return flavor; + } + + case x86_FLOAT_STATE32: { + const x86_float_state32_t* x86_float_state32 = + reinterpret_cast(state); + InitializeCPUContextX86Float(context, x86_float_state32); + return flavor; + } + + case x86_DEBUG_STATE32: { + const x86_debug_state32_t* x86_debug_state32 = + reinterpret_cast(state); + InitializeCPUContextX86Debug(context, x86_debug_state32); + return flavor; + } + + case THREAD_STATE_NONE: { + // This may happen without error when called without exception-style + // flavor data, or even from an exception handler when the exception + // behavior is EXCEPTION_DEFAULT. + return flavor; + } + + default: { + NOTREACHED(); + return THREAD_STATE_NONE; + } + } +} + +void InitializeCPUContextX86_64Thread( + CPUContextX86_64* context, const x86_thread_state64_t* x86_thread_state64) { + context->rax = x86_thread_state64->__rax; + context->rbx = x86_thread_state64->__rbx; + context->rcx = x86_thread_state64->__rcx; + context->rdx = x86_thread_state64->__rdx; + context->rdi = x86_thread_state64->__rdi; + context->rsi = x86_thread_state64->__rsi; + context->rbp = x86_thread_state64->__rbp; + context->rsp = x86_thread_state64->__rsp; + context->r8 = x86_thread_state64->__r8; + context->r9 = x86_thread_state64->__r9; + context->r10 = x86_thread_state64->__r10; + context->r11 = x86_thread_state64->__r11; + context->r12 = x86_thread_state64->__r12; + context->r13 = x86_thread_state64->__r13; + context->r14 = x86_thread_state64->__r14; + context->r15 = x86_thread_state64->__r15; + context->rip = x86_thread_state64->__rip; + context->rflags = x86_thread_state64->__rflags; + context->cs = x86_thread_state64->__cs; + context->fs = x86_thread_state64->__fs; + context->gs = x86_thread_state64->__gs; +} + +void InitializeCPUContextX86_64Float( + CPUContextX86_64* context, const x86_float_state64_t* x86_float_state64) { + // This relies on both x86_float_state64_t and context->fxsave having + // identical (fxsave) layout. + static_assert(offsetof(x86_float_state64_t, __fpu_reserved1) - + offsetof(x86_float_state64_t, __fpu_fcw) == + sizeof(context->fxsave), + "types must be equivalent"); + + memcpy(&context->fxsave, + &x86_float_state64->__fpu_fcw, + sizeof(context->fxsave)); +} + +void InitializeCPUContextX86_64Debug( + CPUContextX86_64* context, const x86_debug_state64_t* x86_debug_state64) { + context->dr0 = x86_debug_state64->__dr0; + context->dr1 = x86_debug_state64->__dr1; + context->dr2 = x86_debug_state64->__dr2; + context->dr3 = x86_debug_state64->__dr3; + context->dr4 = x86_debug_state64->__dr4; + context->dr5 = x86_debug_state64->__dr5; + context->dr6 = x86_debug_state64->__dr6; + context->dr7 = x86_debug_state64->__dr7; +} + +// Initializes |context| from the native thread state structure |state|, which +// is interpreted according to |flavor|. |state_count| must be at least the +// expected size for |flavor|. This handles the architecture-specific +// x86_THREAD_STATE64, x86_FLOAT_STATE64, and x86_DEBUG_STATE64 flavors. It also +// handles the universal x86_THREAD_STATE, x86_FLOAT_STATE, and x86_DEBUG_STATE +// flavors provided that the associated structure carries 64-bit data of the +// corresponding state type. |flavor| may be THREAD_STATE_NONE to avoid setting +// any thread state in |context|. This returns the architecture-specific flavor +// value for the thread state that was actually set, or THREAD_STATE_NONE if no +// thread state was set. +thread_state_flavor_t InitializeCPUContextX86_64Flavor( + CPUContextX86_64* context, + thread_state_flavor_t flavor, + ConstThreadState state, + mach_msg_type_number_t state_count) { + mach_msg_type_number_t expected_state_count; + switch (flavor) { + case x86_THREAD_STATE: + expected_state_count = x86_THREAD_STATE_COUNT; + break; + case x86_FLOAT_STATE: + expected_state_count = x86_FLOAT_STATE_COUNT; + break; + case x86_DEBUG_STATE: + expected_state_count = x86_DEBUG_STATE_COUNT; + break; + case x86_THREAD_STATE64: + expected_state_count = x86_THREAD_STATE64_COUNT; + break; + case x86_FLOAT_STATE64: + expected_state_count = x86_FLOAT_STATE64_COUNT; + break; + case x86_DEBUG_STATE64: + expected_state_count = x86_DEBUG_STATE64_COUNT; + break; + case THREAD_STATE_NONE: + expected_state_count = 0; + break; + default: + LOG(WARNING) << "unhandled flavor " << flavor; + return THREAD_STATE_NONE; + } + + if (state_count < expected_state_count) { + LOG(WARNING) << "expected state_count " << expected_state_count + << " for flavor " << flavor << ", observed " << state_count; + return THREAD_STATE_NONE; + } + + switch (flavor) { + case x86_THREAD_STATE: { + const x86_thread_state_t* x86_thread_state = + reinterpret_cast(state); + if (x86_thread_state->tsh.flavor != x86_THREAD_STATE64) { + LOG(WARNING) << "expected flavor x86_THREAD_STATE64, observed " + << x86_thread_state->tsh.flavor; + return THREAD_STATE_NONE; + } + return InitializeCPUContextX86_64Flavor( + context, + x86_thread_state->tsh.flavor, + reinterpret_cast(&x86_thread_state->uts.ts64), + x86_thread_state->tsh.count); + } + + case x86_FLOAT_STATE: { + const x86_float_state_t* x86_float_state = + reinterpret_cast(state); + if (x86_float_state->fsh.flavor != x86_FLOAT_STATE64) { + LOG(WARNING) << "expected flavor x86_FLOAT_STATE64, observed " + << x86_float_state->fsh.flavor; + return THREAD_STATE_NONE; + } + return InitializeCPUContextX86_64Flavor( + context, + x86_float_state->fsh.flavor, + reinterpret_cast(&x86_float_state->ufs.fs64), + x86_float_state->fsh.count); + } + + case x86_DEBUG_STATE: { + const x86_debug_state_t* x86_debug_state = + reinterpret_cast(state); + if (x86_debug_state->dsh.flavor != x86_DEBUG_STATE64) { + LOG(WARNING) << "expected flavor x86_DEBUG_STATE64, observed " + << x86_debug_state->dsh.flavor; + return THREAD_STATE_NONE; + } + return InitializeCPUContextX86_64Flavor( + context, + x86_debug_state->dsh.flavor, + reinterpret_cast(&x86_debug_state->uds.ds64), + x86_debug_state->dsh.count); + } + + case x86_THREAD_STATE64: { + const x86_thread_state64_t* x86_thread_state64 = + reinterpret_cast(state); + InitializeCPUContextX86_64Thread(context, x86_thread_state64); + return flavor; + } + + case x86_FLOAT_STATE64: { + const x86_float_state64_t* x86_float_state64 = + reinterpret_cast(state); + InitializeCPUContextX86_64Float(context, x86_float_state64); + return flavor; + } + + case x86_DEBUG_STATE64: { + const x86_debug_state64_t* x86_debug_state64 = + reinterpret_cast(state); + InitializeCPUContextX86_64Debug(context, x86_debug_state64); + return flavor; + } + + case THREAD_STATE_NONE: { + // This may happen without error when called without exception-style + // flavor data, or even from an exception handler when the exception + // behavior is EXCEPTION_DEFAULT. + return flavor; + } + + default: { + NOTREACHED(); + return THREAD_STATE_NONE; + } + } +} + +} // namespace + +namespace internal { + +void InitializeCPUContextX86(CPUContextX86* context, + thread_state_flavor_t flavor, + ConstThreadState state, + mach_msg_type_number_t state_count, + const x86_thread_state32_t* x86_thread_state32, + const x86_float_state32_t* x86_float_state32, + const x86_debug_state32_t* x86_debug_state32) { + thread_state_flavor_t set_flavor = THREAD_STATE_NONE; + if (flavor != THREAD_STATE_NONE) { + set_flavor = + InitializeCPUContextX86Flavor(context, flavor, state, state_count); + } + + if (set_flavor != x86_THREAD_STATE32) { + InitializeCPUContextX86Thread(context, x86_thread_state32); + } + if (set_flavor != x86_FLOAT_STATE32) { + InitializeCPUContextX86Float(context, x86_float_state32); + } + if (set_flavor != x86_DEBUG_STATE32) { + InitializeCPUContextX86Debug(context, x86_debug_state32); + } +} + +void InitializeCPUContextX86_64(CPUContextX86_64* context, + thread_state_flavor_t flavor, + ConstThreadState state, + mach_msg_type_number_t state_count, + const x86_thread_state64_t* x86_thread_state64, + const x86_float_state64_t* x86_float_state64, + const x86_debug_state64_t* x86_debug_state64) { + thread_state_flavor_t set_flavor = THREAD_STATE_NONE; + if (flavor != THREAD_STATE_NONE) { + set_flavor = + InitializeCPUContextX86_64Flavor(context, flavor, state, state_count); + } + + if (set_flavor != x86_THREAD_STATE64) { + InitializeCPUContextX86_64Thread(context, x86_thread_state64); + } + if (set_flavor != x86_FLOAT_STATE64) { + InitializeCPUContextX86_64Float(context, x86_float_state64); + } + if (set_flavor != x86_DEBUG_STATE64) { + InitializeCPUContextX86_64Debug(context, x86_debug_state64); + } +} + +} // namespace internal + +#elif defined(ARCH_CPU_ARM64) + +namespace { + +void InitializeCPUContextARM64Thread( + CPUContextARM64* context, + const arm_thread_state64_t* arm_thread_state64) { + // The first 29 fields of context->regs is laid out identically to + // arm_thread_state64->__x. + memcpy( + context->regs, arm_thread_state64->__x, sizeof(arm_thread_state64->__x)); + + context->regs[29] = arm_thread_state64_get_fp(*arm_thread_state64); + context->regs[30] = arm_thread_state64_get_lr(*arm_thread_state64); + context->sp = arm_thread_state64_get_sp(*arm_thread_state64); + context->pc = arm_thread_state64_get_pc(*arm_thread_state64); + context->spsr = + static_castspsr)>(arm_thread_state64->__cpsr); +} + +void InitializeCPUContextARM64Neon(CPUContextARM64* context, + const arm_neon_state64_t* arm_neon_state64) { + static_assert(sizeof(context->fpsimd) == sizeof(arm_neon_state64->__v), + "fpsimd context size mismatch"); + memcpy(context->fpsimd, arm_neon_state64->__v, sizeof(arm_neon_state64->__v)); + context->fpsr = arm_neon_state64->__fpsr; + context->fpcr = arm_neon_state64->__fpcr; +} + +void InitializeCPUContextARM64Debug( + CPUContextARM64* context, + const arm_debug_state64_t* arm_debug_state64) { + // TODO(macos_arm64): Create a spot in CPUContextARM64 to keep this. +} + +thread_state_flavor_t InitializeCPUContextARM64Flavor( + CPUContextARM64* context, + thread_state_flavor_t flavor, + ConstThreadState state, + mach_msg_type_number_t state_count) { + mach_msg_type_number_t expected_state_count; + switch (flavor) { + case ARM_UNIFIED_THREAD_STATE: + expected_state_count = ARM_UNIFIED_THREAD_STATE_COUNT; + break; + case ARM_THREAD_STATE64: + expected_state_count = ARM_THREAD_STATE64_COUNT; + break; + case ARM_NEON_STATE64: + expected_state_count = ARM_NEON_STATE64_COUNT; + break; + case ARM_DEBUG_STATE64: + expected_state_count = ARM_DEBUG_STATE64_COUNT; + break; + case THREAD_STATE_NONE: { + // This may happen without error when called without exception-style + // flavor data, or even from an exception handler when the exception + // behavior is EXCEPTION_DEFAULT. + return flavor; + } + default: + LOG(WARNING) << "unhandled flavor " << flavor; + return THREAD_STATE_NONE; + } + + if (state_count < expected_state_count) { + LOG(WARNING) << "expected state_count " << expected_state_count + << " for flavor " << flavor << ", observed " << state_count; + return THREAD_STATE_NONE; + } + + switch (flavor) { + case ARM_UNIFIED_THREAD_STATE: { + const arm_unified_thread_state_t* arm_thread_state = + reinterpret_cast(state); + if (arm_thread_state->ash.flavor != ARM_THREAD_STATE64) { + LOG(WARNING) << "expected flavor ARM_THREAD_STATE64, observed " + << arm_thread_state->ash.flavor; + return THREAD_STATE_NONE; + } + return InitializeCPUContextARM64Flavor( + context, + arm_thread_state->ash.flavor, + reinterpret_cast(&arm_thread_state->ts_64), + arm_thread_state->ash.count); + } + + case ARM_THREAD_STATE64: { + const arm_thread_state64_t* arm_thread_state = + reinterpret_cast(state); + InitializeCPUContextARM64Thread(context, arm_thread_state); + return ARM_THREAD_STATE64; + } + + case ARM_NEON_STATE64: { + const arm_neon_state64_t* arm_neon_state = + reinterpret_cast(state); + InitializeCPUContextARM64Neon(context, arm_neon_state); + return ARM_NEON_STATE64; + } + + case ARM_DEBUG_STATE64: { + const arm_debug_state64_t* arm_debug_state = + reinterpret_cast(state); + InitializeCPUContextARM64Debug(context, arm_debug_state); + return ARM_DEBUG_STATE64; + } + + case THREAD_STATE_NONE: { + // This may happen without error when called without exception-style + // flavor data, or even from an exception handler when the exception + // behavior is EXCEPTION_DEFAULT. + return flavor; + } + + default: { + NOTREACHED(); + return THREAD_STATE_NONE; + } + } +} + +} // namespace + +namespace internal { + +void InitializeCPUContextARM64(CPUContextARM64* context, + thread_state_flavor_t flavor, + ConstThreadState state, + mach_msg_type_number_t state_count, + const arm_thread_state64_t* arm_thread_state64, + const arm_neon_state64_t* arm_neon_state64, + const arm_debug_state64_t* arm_debug_state64) { + thread_state_flavor_t set_flavor = THREAD_STATE_NONE; + if (flavor != THREAD_STATE_NONE) { + set_flavor = + InitializeCPUContextARM64Flavor(context, flavor, state, state_count); + } + + if (set_flavor != ARM_THREAD_STATE64) { + InitializeCPUContextARM64Thread(context, arm_thread_state64); + } + if (set_flavor != ARM_NEON_STATE64) { + InitializeCPUContextARM64Neon(context, arm_neon_state64); + } + if (set_flavor != ARM_DEBUG_STATE64) { + InitializeCPUContextARM64Debug(context, arm_debug_state64); + } +} + +} // namespace internal + +#endif + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/mac/cpu_context_mac.h b/shared/sentry/external/crashpad/snapshot/mac/cpu_context_mac.h new file mode 100644 index 000000000..154dccddb --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/cpu_context_mac.h @@ -0,0 +1,156 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MAC_CPU_CONTEXT_MAC_H_ +#define CRASHPAD_SNAPSHOT_MAC_CPU_CONTEXT_MAC_H_ + +#include + +#include "build/build_config.h" +#include "snapshot/cpu_context.h" +#include "util/mach/mach_extensions.h" + +namespace crashpad { +namespace internal { + +#if defined(ARCH_CPU_X86_FAMILY) || DOXYGEN + +//! \brief Initializes a CPUContextX86 structure from native context structures +//! on macOS. +//! +//! \a flavor, \a state, and \a state_count may be supplied by exception +//! handlers in order for the \a context parameter to be initialized by the +//! thread state received by the exception handler to the extent possible. In +//! that case, whatever thread state specified by these three parameters will +//! supersede \a x86_thread_state32, \a x86_float_state32, or \a +//! x86_debug_state32. If thread state in this format is not available, \a +//! flavor may be set to `THREAD_STATE_NONE`, and all of \a x86_thread_state32, +//! \a x86_float_state32, and \a x86_debug_state32 will be honored. +//! +//! If \a flavor, \a state, and \a state_count are provided but do not contain +//! valid values, a message will be logged and their values will be ignored as +//! though \a flavor were specified as `THREAD_STATE_NONE`. +//! +//! \param[out] context The CPUContextX86 structure to initialize. +//! \param[in] flavor The native thread state flavor of \a state. This may be +//! `x86_THREAD_STATE32`, `x86_FLOAT_STATE32`, `x86_DEBUG_STATE32`, +//! `x86_THREAD_STATE`, `x86_FLOAT_STATE`, or `x86_DEBUG_STATE`. It may also +//! be `THREAD_STATE_NONE` if \a state is not supplied (and is `nullptr`). +//! \param[in] state The native thread state, which may be a casted pointer to +//! `x86_thread_state32_t`, `x86_float_state32_t`, `x86_debug_state32_t`, +//! `x86_thread_state`, `x86_float_state`, or `x86_debug_state`. This +//! parameter may be `nullptr` to not supply this data, in which case \a +//! flavor must be `THREAD_STATE_NONE`. If a “universalâ€Â structure is used, +//! it must carry 32-bit state data of the correct type. +//! \param[in] state_count The number of `natural_t`-sized (`int`-sized) units +//! in \a state. This may be 0 if \a state is `nullptr`. +//! \param[in] x86_thread_state32 The state of the thread’s integer registers. +//! \param[in] x86_float_state32 The state of the thread’s floating-point +//! registers. +//! \param[in] x86_debug_state32 The state of the thread’s debug registers. +void InitializeCPUContextX86(CPUContextX86* context, + thread_state_flavor_t flavor, + ConstThreadState state, + mach_msg_type_number_t state_count, + const x86_thread_state32_t* x86_thread_state32, + const x86_float_state32_t* x86_float_state32, + const x86_debug_state32_t* x86_debug_state32); + +//! \brief Initializes a CPUContextX86_64 structure from native context +//! structures on macOS. +//! +//! \a flavor, \a state, and \a state_count may be supplied by exception +//! handlers in order for the \a context parameter to be initialized by the +//! thread state received by the exception handler to the extent possible. In +//! that case, whatever thread state specified by these three parameters will +//! supersede \a x86_thread_state64, \a x86_float_state64, or \a +//! x86_debug_state64. If thread state in this format is not available, \a +//! flavor may be set to `THREAD_STATE_NONE`, and all of \a x86_thread_state64, +//! \a x86_float_state64, and \a x86_debug_state64 will be honored. +//! +//! If \a flavor, \a state, and \a state_count are provided but do not contain +//! valid values, a message will be logged and their values will be ignored as +//! though \a flavor were specified as `THREAD_STATE_NONE`. +//! +//! \param[out] context The CPUContextX86_64 structure to initialize. +//! \param[in] flavor The native thread state flavor of \a state. This may be +//! `x86_THREAD_STATE64`, `x86_FLOAT_STATE64`, `x86_DEBUG_STATE64`, +//! `x86_THREAD_STATE`, `x86_FLOAT_STATE`, or `x86_DEBUG_STATE`. It may also +//! be `THREAD_STATE_NONE` if \a state is not supplied (and is `nullptr`). +//! \param[in] state The native thread state, which may be a casted pointer to +//! `x86_thread_state64_t`, `x86_float_state64_t`, `x86_debug_state64_t`, +//! `x86_thread_state`, `x86_float_state`, or `x86_debug_state`. This +//! parameter may be `nullptr` to not supply this data, in which case \a +//! flavor must be `THREAD_STATE_NONE`. If a “universalâ€Â structure is used, +//! it must carry 64-bit state data of the correct type. +//! \param[in] state_count The number of `int`-sized units in \a state. This may +//! be 0 if \a state is `nullptr`. +//! \param[in] x86_thread_state64 The state of the thread’s integer registers. +//! \param[in] x86_float_state64 The state of the thread’s floating-point +//! registers. +//! \param[in] x86_debug_state64 The state of the thread’s debug registers. +void InitializeCPUContextX86_64(CPUContextX86_64* context, + thread_state_flavor_t flavor, + ConstThreadState state, + mach_msg_type_number_t state_count, + const x86_thread_state64_t* x86_thread_state64, + const x86_float_state64_t* x86_float_state64, + const x86_debug_state64_t* x86_debug_state64); + +#elif defined(ARCH_CPU_ARM64) || DOXYGEN +//! \brief Initializes a CPUContextARM64 structure from native context +//! structures on macOS or iOS. +//! +//! \a flavor, \a state, and \a state_count may be supplied by exception +//! handlers in order for the \a context parameter to be initialized by the +//! thread state received by the exception handler to the extent possible. In +//! that case, whatever thread state specified by these three parameters will +//! supersede \a arm_thread_state64 or \a arm_neon_state64. If thread state in +//! this format is not available, \a flavor may be set to `THREAD_STATE_NONE`, +//! and all of \a arm_thread_state64 abd \a arm_neon_state64 will be honored. +//! +//! If \a flavor, \a state, and \a state_count are provided but do not contain +//! valid values, a message will be logged and their values will be ignored as +//! though \a flavor were specified as `THREAD_STATE_NONE`. +//! +//! \param[out] context The CPUContextARM64 structure to initialize. +//! \param[in] flavor The native thread state flavor of \a state. This may be +//! `ARM_THREAD_STATE64`, `ARM_THREAD_STATE` or `ARM_NEON_STATE64`. It may +//! also be `THREAD_STATE_NONE` if \a state is not supplied (and is +//! `nullptr`). +//! \param[in] state The native thread state, which may be a casted pointer to +//! `arm_thread_state64_t`, `arm_unified_thread_state` or +//! `arm_neon_state64_t`. This parameter may be `nullptr` to not supply this +//! data, in which case \a flavor must be `THREAD_STATE_NONE`. If a +//! “universalâ€Â structure is used, it must carry 64-bit state data of the +//! correct type. +//! \param[in] state_count The number of `int`-sized units in \a state. This +//! may be 0 if \a state is `nullptr`. +//! \param[in] arm_thread_state64 The state of the thread’s integer registers. +//! \param[in] arm_neon_state64 The state of the thread’s floating-point +//! registers. +//! \param[in] arm_debug_state64 The state of the thread’s debug registers. +void InitializeCPUContextARM64(CPUContextARM64* context, + thread_state_flavor_t flavor, + ConstThreadState state, + mach_msg_type_number_t state_count, + const arm_thread_state64_t* arm_thread_state64, + const arm_neon_state64_t* arm_neon_state64, + const arm_debug_state64_t* arm_debug_state64); +#endif + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MAC_CPU_CONTEXT_MAC_H_ diff --git a/shared/sentry/external/crashpad/snapshot/mac/cpu_context_mac_test.cc b/shared/sentry/external/crashpad/snapshot/mac/cpu_context_mac_test.cc new file mode 100644 index 000000000..c7156da13 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/cpu_context_mac_test.cc @@ -0,0 +1,421 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/mac/cpu_context_mac.h" + +#include + +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +#if defined(ARCH_CPU_X86_FAMILY) + +TEST(CPUContextMac, InitializeContextX86) { + x86_thread_state32_t x86_thread_state32 = {}; + x86_float_state32_t x86_float_state32 = {}; + x86_debug_state32_t x86_debug_state32 = {}; + x86_thread_state32.__eax = 1; + x86_float_state32.__fpu_ftw = 2; + x86_debug_state32.__dr0 = 3; + + // Test the simple case, where everything in the CPUContextX86 argument is set + // directly from the supplied thread, float, and debug state parameters. + { + CPUContextX86 cpu_context_x86 = {}; + internal::InitializeCPUContextX86(&cpu_context_x86, + THREAD_STATE_NONE, + nullptr, + 0, + &x86_thread_state32, + &x86_float_state32, + &x86_debug_state32); + EXPECT_EQ(cpu_context_x86.eax, 1u); + EXPECT_EQ(cpu_context_x86.fxsave.ftw, 2u); + EXPECT_EQ(cpu_context_x86.dr0, 3u); + } + + // Supply context in a CPU-specific “flavor†parameter expected to be used + // instead of the supplied thread, float, or debug state parameters. Do this + // once for each of the three valid flavors. This simulates how + // InitializeCPUContextX86() might be used to initialize the context in an + // exception handler, where the exception handler may have received the + // “flavor†parameter and this context should be used to initialize the + // CPUContextX86. + + { + x86_thread_state32_t alt_x86_thread_state32 = {}; + alt_x86_thread_state32.__eax = 4; + + CPUContextX86 cpu_context_x86 = {}; + internal::InitializeCPUContextX86( + &cpu_context_x86, + x86_THREAD_STATE32, + reinterpret_cast(&alt_x86_thread_state32), + x86_THREAD_STATE32_COUNT, + &x86_thread_state32, + &x86_float_state32, + &x86_debug_state32); + EXPECT_EQ(cpu_context_x86.eax, 4u); + EXPECT_EQ(cpu_context_x86.fxsave.ftw, 2u); + EXPECT_EQ(cpu_context_x86.dr0, 3u); + } + + { + x86_float_state32_t alt_x86_float_state32 = {}; + alt_x86_float_state32.__fpu_ftw = 5; + + CPUContextX86 cpu_context_x86 = {}; + internal::InitializeCPUContextX86( + &cpu_context_x86, + x86_FLOAT_STATE32, + reinterpret_cast(&alt_x86_float_state32), + x86_FLOAT_STATE32_COUNT, + &x86_thread_state32, + &x86_float_state32, + &x86_debug_state32); + EXPECT_EQ(cpu_context_x86.eax, 1u); + EXPECT_EQ(cpu_context_x86.fxsave.ftw, 5u); + EXPECT_EQ(cpu_context_x86.dr0, 3u); + } + + { + x86_debug_state32_t alt_x86_debug_state32 = {}; + alt_x86_debug_state32.__dr0 = 6; + + CPUContextX86 cpu_context_x86 = {}; + internal::InitializeCPUContextX86( + &cpu_context_x86, + x86_DEBUG_STATE32, + reinterpret_cast(&alt_x86_debug_state32), + x86_DEBUG_STATE32_COUNT, + &x86_thread_state32, + &x86_float_state32, + &x86_debug_state32); + EXPECT_EQ(cpu_context_x86.eax, 1u); + EXPECT_EQ(cpu_context_x86.fxsave.ftw, 2u); + EXPECT_EQ(cpu_context_x86.dr0, 6u); + } + + // Supply context in a universal “flavor†parameter expected to be used + // instead of the supplied thread, float, or debug state parameters. The + // universal format allows an exception handler to be registered to receive + // thread, float, or debug state without having to know in advance whether it + // will be receiving the state from a 32-bit or 64-bit process. For + // CPUContextX86, only the 32-bit form is supported. + + { + x86_thread_state x86_thread_state_3264 = {}; + x86_thread_state_3264.tsh.flavor = x86_THREAD_STATE32; + x86_thread_state_3264.tsh.count = x86_THREAD_STATE32_COUNT; + x86_thread_state_3264.uts.ts32.__eax = 7; + + CPUContextX86 cpu_context_x86 = {}; + internal::InitializeCPUContextX86( + &cpu_context_x86, + x86_THREAD_STATE, + reinterpret_cast(&x86_thread_state_3264), + x86_THREAD_STATE_COUNT, + &x86_thread_state32, + &x86_float_state32, + &x86_debug_state32); + EXPECT_EQ(cpu_context_x86.eax, 7u); + EXPECT_EQ(cpu_context_x86.fxsave.ftw, 2u); + EXPECT_EQ(cpu_context_x86.dr0, 3u); + } + + { + x86_float_state x86_float_state_3264 = {}; + x86_float_state_3264.fsh.flavor = x86_FLOAT_STATE32; + x86_float_state_3264.fsh.count = x86_FLOAT_STATE32_COUNT; + x86_float_state_3264.ufs.fs32.__fpu_ftw = 8; + + CPUContextX86 cpu_context_x86 = {}; + internal::InitializeCPUContextX86( + &cpu_context_x86, + x86_FLOAT_STATE, + reinterpret_cast(&x86_float_state_3264), + x86_FLOAT_STATE_COUNT, + &x86_thread_state32, + &x86_float_state32, + &x86_debug_state32); + EXPECT_EQ(cpu_context_x86.eax, 1u); + EXPECT_EQ(cpu_context_x86.fxsave.ftw, 8u); + EXPECT_EQ(cpu_context_x86.dr0, 3u); + } + + { + x86_debug_state x86_debug_state_3264 = {}; + x86_debug_state_3264.dsh.flavor = x86_DEBUG_STATE32; + x86_debug_state_3264.dsh.count = x86_DEBUG_STATE32_COUNT; + x86_debug_state_3264.uds.ds32.__dr0 = 9; + + CPUContextX86 cpu_context_x86 = {}; + internal::InitializeCPUContextX86( + &cpu_context_x86, + x86_DEBUG_STATE, + reinterpret_cast(&x86_debug_state_3264), + x86_DEBUG_STATE_COUNT, + &x86_thread_state32, + &x86_float_state32, + &x86_debug_state32); + EXPECT_EQ(cpu_context_x86.eax, 1u); + EXPECT_EQ(cpu_context_x86.fxsave.ftw, 2u); + EXPECT_EQ(cpu_context_x86.dr0, 9u); + } + + // Supply inappropriate “flavor†contexts to test that + // InitializeCPUContextX86() detects the problem and refuses to use the + // supplied “flavor†context, falling back to the thread, float, and debug + // states. + + { + x86_thread_state64_t x86_thread_state64 = {}; + + CPUContextX86 cpu_context_x86 = {}; + internal::InitializeCPUContextX86( + &cpu_context_x86, + x86_THREAD_STATE64, + reinterpret_cast(&x86_thread_state64), + x86_THREAD_STATE64_COUNT, + &x86_thread_state32, + &x86_float_state32, + &x86_debug_state32); + EXPECT_EQ(cpu_context_x86.eax, 1u); + EXPECT_EQ(cpu_context_x86.fxsave.ftw, 2u); + EXPECT_EQ(cpu_context_x86.dr0, 3u); + } + + { + x86_thread_state x86_thread_state_3264 = {}; + x86_thread_state_3264.tsh.flavor = x86_THREAD_STATE64; + x86_thread_state_3264.tsh.count = x86_THREAD_STATE64_COUNT; + + CPUContextX86 cpu_context_x86 = {}; + internal::InitializeCPUContextX86( + &cpu_context_x86, + x86_THREAD_STATE, + reinterpret_cast(&x86_thread_state_3264), + x86_THREAD_STATE_COUNT, + &x86_thread_state32, + &x86_float_state32, + &x86_debug_state32); + EXPECT_EQ(cpu_context_x86.eax, 1u); + EXPECT_EQ(cpu_context_x86.fxsave.ftw, 2u); + EXPECT_EQ(cpu_context_x86.dr0, 3u); + } +} + +TEST(CPUContextMac, InitializeContextX86_64) { + x86_thread_state64_t x86_thread_state64 = {}; + x86_float_state64_t x86_float_state64 = {}; + x86_debug_state64_t x86_debug_state64 = {}; + x86_thread_state64.__rax = 10; + x86_float_state64.__fpu_ftw = 11; + x86_debug_state64.__dr0 = 12; + + // Test the simple case, where everything in the CPUContextX86_64 argument is + // set directly from the supplied thread, float, and debug state parameters. + { + CPUContextX86_64 cpu_context_x86_64 = {}; + internal::InitializeCPUContextX86_64(&cpu_context_x86_64, + THREAD_STATE_NONE, + nullptr, + 0, + &x86_thread_state64, + &x86_float_state64, + &x86_debug_state64); + EXPECT_EQ(cpu_context_x86_64.rax, 10u); + EXPECT_EQ(cpu_context_x86_64.fxsave.ftw, 11u); + EXPECT_EQ(cpu_context_x86_64.dr0, 12u); + } + + // Supply context in a CPU-specific “flavor†parameter expected to be used + // instead of the supplied thread, float, or debug state parameters. Do this + // once for each of the three valid flavors. This simulates how + // InitializeCPUContextX86_64() might be used to initialize the context in an + // exception handler, where the exception handler may have received the + // “flavor†parameter and this context should be used to initialize the + // CPUContextX86_64. + + { + x86_thread_state64_t alt_x86_thread_state64 = {}; + alt_x86_thread_state64.__rax = 13; + + CPUContextX86_64 cpu_context_x86_64 = {}; + internal::InitializeCPUContextX86_64( + &cpu_context_x86_64, + x86_THREAD_STATE64, + reinterpret_cast(&alt_x86_thread_state64), + x86_THREAD_STATE64_COUNT, + &x86_thread_state64, + &x86_float_state64, + &x86_debug_state64); + EXPECT_EQ(cpu_context_x86_64.rax, 13u); + EXPECT_EQ(cpu_context_x86_64.fxsave.ftw, 11u); + EXPECT_EQ(cpu_context_x86_64.dr0, 12u); + } + + { + x86_float_state64_t alt_x86_float_state64 = {}; + alt_x86_float_state64.__fpu_ftw = 14; + + CPUContextX86_64 cpu_context_x86_64 = {}; + internal::InitializeCPUContextX86_64( + &cpu_context_x86_64, + x86_FLOAT_STATE64, + reinterpret_cast(&alt_x86_float_state64), + x86_FLOAT_STATE64_COUNT, + &x86_thread_state64, + &x86_float_state64, + &x86_debug_state64); + EXPECT_EQ(cpu_context_x86_64.rax, 10u); + EXPECT_EQ(cpu_context_x86_64.fxsave.ftw, 14u); + EXPECT_EQ(cpu_context_x86_64.dr0, 12u); + } + + { + x86_debug_state64_t alt_x86_debug_state64 = {}; + alt_x86_debug_state64.__dr0 = 15; + + CPUContextX86_64 cpu_context_x86_64 = {}; + internal::InitializeCPUContextX86_64( + &cpu_context_x86_64, + x86_DEBUG_STATE64, + reinterpret_cast(&alt_x86_debug_state64), + x86_DEBUG_STATE64_COUNT, + &x86_thread_state64, + &x86_float_state64, + &x86_debug_state64); + EXPECT_EQ(cpu_context_x86_64.rax, 10u); + EXPECT_EQ(cpu_context_x86_64.fxsave.ftw, 11u); + EXPECT_EQ(cpu_context_x86_64.dr0, 15u); + } + + // Supply context in a universal “flavor†parameter expected to be used + // instead of the supplied thread, float, or debug state parameters. The + // universal format allows an exception handler to be registered to receive + // thread, float, or debug state without having to know in advance whether it + // will be receiving the state from a 32-bit or 64-bit process. For + // CPUContextX86_64, only the 64-bit form is supported. + + { + x86_thread_state x86_thread_state_3264 = {}; + x86_thread_state_3264.tsh.flavor = x86_THREAD_STATE64; + x86_thread_state_3264.tsh.count = x86_THREAD_STATE64_COUNT; + x86_thread_state_3264.uts.ts64.__rax = 16; + + CPUContextX86_64 cpu_context_x86_64 = {}; + internal::InitializeCPUContextX86_64( + &cpu_context_x86_64, + x86_THREAD_STATE, + reinterpret_cast(&x86_thread_state_3264), + x86_THREAD_STATE_COUNT, + &x86_thread_state64, + &x86_float_state64, + &x86_debug_state64); + EXPECT_EQ(cpu_context_x86_64.rax, 16u); + EXPECT_EQ(cpu_context_x86_64.fxsave.ftw, 11u); + EXPECT_EQ(cpu_context_x86_64.dr0, 12u); + } + + { + x86_float_state x86_float_state_3264 = {}; + x86_float_state_3264.fsh.flavor = x86_FLOAT_STATE64; + x86_float_state_3264.fsh.count = x86_FLOAT_STATE64_COUNT; + x86_float_state_3264.ufs.fs64.__fpu_ftw = 17; + + CPUContextX86_64 cpu_context_x86_64 = {}; + internal::InitializeCPUContextX86_64( + &cpu_context_x86_64, + x86_FLOAT_STATE, + reinterpret_cast(&x86_float_state_3264), + x86_FLOAT_STATE_COUNT, + &x86_thread_state64, + &x86_float_state64, + &x86_debug_state64); + EXPECT_EQ(cpu_context_x86_64.rax, 10u); + EXPECT_EQ(cpu_context_x86_64.fxsave.ftw, 17u); + EXPECT_EQ(cpu_context_x86_64.dr0, 12u); + } + + { + x86_debug_state x86_debug_state_3264 = {}; + x86_debug_state_3264.dsh.flavor = x86_DEBUG_STATE64; + x86_debug_state_3264.dsh.count = x86_DEBUG_STATE64_COUNT; + x86_debug_state_3264.uds.ds64.__dr0 = 18; + + CPUContextX86_64 cpu_context_x86_64 = {}; + internal::InitializeCPUContextX86_64( + &cpu_context_x86_64, + x86_DEBUG_STATE, + reinterpret_cast(&x86_debug_state_3264), + x86_DEBUG_STATE_COUNT, + &x86_thread_state64, + &x86_float_state64, + &x86_debug_state64); + EXPECT_EQ(cpu_context_x86_64.rax, 10u); + EXPECT_EQ(cpu_context_x86_64.fxsave.ftw, 11u); + EXPECT_EQ(cpu_context_x86_64.dr0, 18u); + } + + // Supply inappropriate “flavor†contexts to test that + // InitializeCPUContextX86() detects the problem and refuses to use the + // supplied “flavor†context, falling back to the thread, float, and debug + // states. + + { + x86_thread_state32_t x86_thread_state32 = {}; + + CPUContextX86_64 cpu_context_x86_64 = {}; + internal::InitializeCPUContextX86_64( + &cpu_context_x86_64, + x86_THREAD_STATE32, + reinterpret_cast(&x86_thread_state32), + x86_THREAD_STATE32_COUNT, + &x86_thread_state64, + &x86_float_state64, + &x86_debug_state64); + EXPECT_EQ(cpu_context_x86_64.rax, 10u); + EXPECT_EQ(cpu_context_x86_64.fxsave.ftw, 11u); + EXPECT_EQ(cpu_context_x86_64.dr0, 12u); + } + + { + x86_thread_state x86_thread_state_3264 = {}; + x86_thread_state_3264.tsh.flavor = x86_THREAD_STATE32; + x86_thread_state_3264.tsh.count = x86_THREAD_STATE32_COUNT; + + CPUContextX86_64 cpu_context_x86_64 = {}; + internal::InitializeCPUContextX86_64( + &cpu_context_x86_64, + x86_THREAD_STATE, + reinterpret_cast(&x86_thread_state_3264), + x86_THREAD_STATE_COUNT, + &x86_thread_state64, + &x86_float_state64, + &x86_debug_state64); + EXPECT_EQ(cpu_context_x86_64.rax, 10u); + EXPECT_EQ(cpu_context_x86_64.fxsave.ftw, 11u); + EXPECT_EQ(cpu_context_x86_64.dr0, 12u); + } +} + +#endif + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/mac/exception_snapshot_mac.cc b/shared/sentry/external/crashpad/snapshot/mac/exception_snapshot_mac.cc new file mode 100644 index 000000000..39d8fb245 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/exception_snapshot_mac.cc @@ -0,0 +1,263 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/mac/exception_snapshot_mac.h" + +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "snapshot/mac/cpu_context_mac.h" +#include "snapshot/mac/process_reader_mac.h" +#include "util/mach/exception_behaviors.h" +#include "util/mach/exception_types.h" +#include "util/mach/symbolic_constants_mach.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { +namespace internal { + +ExceptionSnapshotMac::ExceptionSnapshotMac() + : ExceptionSnapshot(), + context_union_(), + context_(), + codes_(), + thread_id_(0), + exception_address_(0), + exception_(0), + exception_code_0_(0), + initialized_() { +} + +ExceptionSnapshotMac::~ExceptionSnapshotMac() { +} + +bool ExceptionSnapshotMac::Initialize(ProcessReaderMac* process_reader, + exception_behavior_t behavior, + thread_t exception_thread, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t flavor, + ConstThreadState state, + mach_msg_type_number_t state_count) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + codes_.push_back(exception); + for (mach_msg_type_number_t code_index = 0; + code_index < code_count; + ++code_index) { + codes_.push_back(code[code_index]); + } + + exception_ = exception; + mach_exception_code_t exception_code_0 = code[0]; + + if (exception_ == EXC_CRASH) { + exception_ = ExcCrashRecoverOriginalException( + exception_code_0, &exception_code_0, nullptr); + + if (!ExcCrashCouldContainException(exception_)) { + LOG(WARNING) << base::StringPrintf( + "exception %s invalid in EXC_CRASH", + ExceptionToString(exception_, kUseFullName | kUnknownIsNumeric) + .c_str()); + } + } + + // The operations that follow put exception_code_0 (a mach_exception_code_t, + // a typedef for int64_t) into exception_code_0_ (a uint32_t). The range + // checks and bit shifts involved need the same signedness on both sides to + // work properly. + const uint64_t unsigned_exception_code_0 = exception_code_0; + + // ExceptionInfo() returns code[0] as a 32-bit value, but exception_code_0 is + // a 64-bit value. The best treatment for this inconsistency depends on the + // exception type. + if (exception_ == EXC_RESOURCE || exception_ == EXC_GUARD) { + // All 64 bits of code[0] are significant for these exceptions. See + // for EXC_RESOURCE and 10.10 + // xnu-2782.1.97/bsd/kern/kern_guarded.c fd_guard_ast() for EXC_GUARD. + // code[0] is structured similarly for these two exceptions. + // + // EXC_RESOURCE: see . The resource type and “flavor†+ // together define the resource and are in the highest bits. The resource + // limit is in the lowest bits. + // + // EXC_GUARD: see 10.10 xnu-2782.1.97/osfmk/ipc/mach_port.c + // mach_port_guard_exception() and xnu-2782.1.97/bsd/kern/kern_guarded.c + // fd_guard_ast(). The guard type (GUARD_TYPE_MACH_PORT or GUARD_TYPE_FD) + // and “flavor†(from the mach_port_guard_exception_codes or + // guard_exception_codes enums) are in the highest bits. The violating Mach + // port name or file descriptor number is in the lowest bits. + + // If MACH_EXCEPTION_CODES is not set in |behavior|, code[0] will only carry + // 32 significant bits, and the interesting high bits will have been + // truncated. + if (!ExceptionBehaviorHasMachExceptionCodes(behavior)) { + LOG(WARNING) << base::StringPrintf( + "behavior %s invalid for exception %s", + ExceptionBehaviorToString( + behavior, kUseFullName | kUnknownIsNumeric | kUseOr).c_str(), + ExceptionToString(exception_, kUseFullName | kUnknownIsNumeric) + .c_str()); + } + + // Include the more-significant information from the high bits of code[0] in + // the value to be returned by ExceptionInfo(). The full value of codes[0] + // including the less-significant lower bits is still available via Codes(). + exception_code_0_ = unsigned_exception_code_0 >> 32; + } else { + // For other exceptions, code[0]’s values never exceed 32 bits. + if (!base::IsValueInRangeForNumericType( + unsigned_exception_code_0)) { + LOG(WARNING) << base::StringPrintf("exception_code_0 0x%llx out of range", + unsigned_exception_code_0); + } + exception_code_0_ = unsigned_exception_code_0; + } + + const ProcessReaderMac::Thread* thread = nullptr; + for (const ProcessReaderMac::Thread& loop_thread : + process_reader->Threads()) { + if (exception_thread == loop_thread.port) { + thread = &loop_thread; + break; + } + } + + if (!thread) { + LOG(ERROR) << "exception_thread not found in task"; + return false; + } + + thread_id_ = thread->id; + + // Normally, for EXC_BAD_ACCESS exceptions, the exception address is present + // in code[1]. It may or may not be the instruction pointer address (usually + // it’s not). code[1] may carry the exception address for other exception + // types too, but it’s not guaranteed. But for all other exception types, the + // instruction pointer will be the exception address, and in fact will be + // equal to codes[1] when it’s carrying the exception address. In those cases, + // just use the instruction pointer directly. + bool code_1_is_exception_address = exception_ == EXC_BAD_ACCESS; + +#if defined(ARCH_CPU_X86_FAMILY) + if (process_reader->Is64Bit()) { + context_.architecture = kCPUArchitectureX86_64; + context_.x86_64 = &context_union_.x86_64; + InitializeCPUContextX86_64(context_.x86_64, + flavor, + state, + state_count, + &thread->thread_context.t64, + &thread->float_context.f64, + &thread->debug_context.d64); + } else { + context_.architecture = kCPUArchitectureX86; + context_.x86 = &context_union_.x86; + InitializeCPUContextX86(context_.x86, + flavor, + state, + state_count, + &thread->thread_context.t32, + &thread->float_context.f32, + &thread->debug_context.d32); + } + + // For x86 and x86_64 EXC_BAD_ACCESS exceptions, some code[0] values indicate + // that code[1] does not (or may not) carry the exception address: + // EXC_I386_GPFLT (10.9.5 xnu-2422.115.4/osfmk/i386/trap.c user_trap() for + // T_GENERAL_PROTECTION) and the oddball (VM_PROT_READ | VM_PROT_EXECUTE) + // which collides with EXC_I386_BOUNDFLT (10.9.5 + // xnu-2422.115.4/osfmk/i386/fpu.c fpextovrflt()). Other EXC_BAD_ACCESS + // exceptions come through 10.9.5 xnu-2422.115.4/osfmk/i386/trap.c + // user_page_fault_continue() and do contain the exception address in code[1]. + if (exception_ == EXC_BAD_ACCESS && + (exception_code_0_ == EXC_I386_GPFLT || + exception_code_0_ == (VM_PROT_READ | VM_PROT_EXECUTE))) { + code_1_is_exception_address = false; + } +#elif defined(ARCH_CPU_ARM64) + context_.architecture = kCPUArchitectureARM64; + context_.arm64 = &context_union_.arm64; + InitializeCPUContextARM64(context_.arm64, + flavor, + state, + state_count, + &thread->thread_context, + &thread->float_context, + &thread->debug_context); +#else +#error Port to your architecture +#endif + + if (code_1_is_exception_address) { + if (process_reader->Is64Bit() && + !ExceptionBehaviorHasMachExceptionCodes(behavior)) { + // If code[1] is an address from a 64-bit process, the exception must have + // been received with MACH_EXCEPTION_CODES or the address will have been + // truncated. + LOG(WARNING) << base::StringPrintf( + "behavior %s invalid for exception %s code %d in 64-bit process", + ExceptionBehaviorToString( + behavior, kUseFullName | kUnknownIsNumeric | kUseOr).c_str(), + ExceptionToString(exception_, kUseFullName | kUnknownIsNumeric) + .c_str(), + exception_code_0_); + } + exception_address_ = code[1]; + } else { + exception_address_ = context_.InstructionPointer(); + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +const CPUContext* ExceptionSnapshotMac::Context() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &context_; +} + +uint64_t ExceptionSnapshotMac::ThreadID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_id_; +} + +uint32_t ExceptionSnapshotMac::Exception() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_; +} + +uint32_t ExceptionSnapshotMac::ExceptionInfo() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_code_0_; +} + +uint64_t ExceptionSnapshotMac::ExceptionAddress() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_address_; +} + +const std::vector& ExceptionSnapshotMac::Codes() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return codes_; +} + +std::vector ExceptionSnapshotMac::ExtraMemory() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector(); +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/mac/exception_snapshot_mac.h b/shared/sentry/external/crashpad/snapshot/mac/exception_snapshot_mac.h new file mode 100644 index 000000000..ea7150a93 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/exception_snapshot_mac.h @@ -0,0 +1,107 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MAC_EXCEPTION_SNAPSHOT_MAC_H_ +#define CRASHPAD_SNAPSHOT_MAC_EXCEPTION_SNAPSHOT_MAC_H_ + +#include +#include + +#include + +#include "build/build_config.h" +#include "snapshot/cpu_context.h" +#include "snapshot/exception_snapshot.h" +#include "util/mach/mach_extensions.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +class ProcessReaderMac; + +namespace internal { + +//! \brief An ExceptionSnapshot of an exception sustained by a running (or +//! crashed) process on a macOS system. +class ExceptionSnapshotMac final : public ExceptionSnapshot { + public: + ExceptionSnapshotMac(); + + ExceptionSnapshotMac(const ExceptionSnapshotMac&) = delete; + ExceptionSnapshotMac& operator=(const ExceptionSnapshotMac&) = delete; + + ~ExceptionSnapshotMac() override; + + //! \brief Initializes the object. + //! + //! Other than \a process_reader, the parameters may be passed directly + //! through from a Mach exception handler. + //! + //! \param[in] process_reader A ProcessReaderMac for the task that sustained + //! the exception. + //! \param[in] behavior + //! \param[in] exception_thread + //! \param[in] exception + //! \param[in] code + //! \param[in] code_count + //! \param[in,out] flavor + //! \param[in] state + //! \param[in] state_count + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(ProcessReaderMac* process_reader, + exception_behavior_t behavior, + thread_t exception_thread, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t flavor, + ConstThreadState state, + mach_msg_type_number_t state_count); + + // ExceptionSnapshot: + + const CPUContext* Context() const override; + uint64_t ThreadID() const override; + uint32_t Exception() const override; + uint32_t ExceptionInfo() const override; + uint64_t ExceptionAddress() const override; + const std::vector& Codes() const override; + virtual std::vector ExtraMemory() const override; + + private: + union { +#if defined(ARCH_CPU_X86_FAMILY) + CPUContextX86 x86; + CPUContextX86_64 x86_64; +#elif defined(ARCH_CPU_ARM64) + CPUContextARM64 arm64; +#else +#error Port to your CPU architecture +#endif + } context_union_; + CPUContext context_; + std::vector codes_; + uint64_t thread_id_; + uint64_t exception_address_; + exception_type_t exception_; + uint32_t exception_code_0_; + InitializationStateDcheck initialized_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MAC_EXCEPTION_SNAPSHOT_MAC_H_ diff --git a/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_annotations_reader.cc b/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_annotations_reader.cc new file mode 100644 index 000000000..eefd7f447 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_annotations_reader.cc @@ -0,0 +1,238 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/mac/mach_o_image_annotations_reader.h" + +#include +#include +#include + +#include + +#include "base/logging.h" +#include "client/crashpad_info.h" +#include "client/simple_string_dictionary.h" +#include "snapshot/mac/mach_o_image_reader.h" +#include "snapshot/mac/process_reader_mac.h" +#include "snapshot/snapshot_constants.h" +#include "util/stdlib/strnlen.h" + +namespace crashpad { + +MachOImageAnnotationsReader::MachOImageAnnotationsReader( + ProcessReaderMac* process_reader, + const MachOImageReader* image_reader, + const std::string& name) + : name_(name), + process_reader_(process_reader), + image_reader_(image_reader) {} + +std::vector MachOImageAnnotationsReader::Vector() const { + std::vector vector_annotations; + + ReadCrashReporterClientAnnotations(&vector_annotations); + ReadDyldErrorStringAnnotation(&vector_annotations); + + return vector_annotations; +} + +std::map MachOImageAnnotationsReader::SimpleMap() + const { + std::map simple_map_annotations; + + ReadCrashpadSimpleAnnotations(&simple_map_annotations); + + return simple_map_annotations; +} + +std::vector MachOImageAnnotationsReader::AnnotationsList() + const { + std::vector annotations; + + ReadCrashpadAnnotationsList(&annotations); + + return annotations; +} + +void MachOImageAnnotationsReader::ReadCrashReporterClientAnnotations( + std::vector* vector_annotations) const { + mach_vm_address_t crash_info_address; + const process_types::section* crash_info_section = + image_reader_->GetSectionByName( + SEG_DATA, "__crash_info", &crash_info_address); + if (!crash_info_section) { + return; + } + + process_types::crashreporter_annotations_t crash_info; + if (!crash_info.Read(process_reader_, crash_info_address)) { + LOG(WARNING) << "could not read crash info from " << name_; + return; + } + + if (crash_info.version != 4 && crash_info.version != 5) { + LOG(WARNING) << "unexpected crash info version " << crash_info.version + << " in " << name_; + return; + } + + size_t expected_size = + process_types::crashreporter_annotations_t::ExpectedSizeForVersion( + process_reader_, crash_info.version); + if (crash_info_section->size < expected_size) { + LOG(WARNING) << "small crash info section size " << crash_info_section->size + << " < " << expected_size << " for version " + << crash_info.version << " in " << name_; + return; + } + + // This number was totally made up out of nowhere, but it seems prudent to + // enforce some limit. + constexpr size_t kMaxMessageSize = 1024; + if (crash_info.message) { + std::string message; + if (process_reader_->Memory()->ReadCStringSizeLimited( + crash_info.message, kMaxMessageSize, &message)) { + vector_annotations->push_back(message); + } else { + LOG(WARNING) << "could not read crash message in " << name_; + } + } + + if (crash_info.message2) { + std::string message; + if (process_reader_->Memory()->ReadCStringSizeLimited( + crash_info.message2, kMaxMessageSize, &message)) { + vector_annotations->push_back(message); + } else { + LOG(WARNING) << "could not read crash message 2 in " << name_; + } + } +} + +void MachOImageAnnotationsReader::ReadDyldErrorStringAnnotation( + std::vector* vector_annotations) const { + // dyld stores its error string at the external symbol for |const char + // error_string[1024]|. See 10.9.5 dyld-239.4/src/dyld.cpp error_string. + if (image_reader_->FileType() != MH_DYLINKER) { + return; + } + + mach_vm_address_t error_string_address; + if (!image_reader_->LookUpExternalDefinedSymbol("_error_string", + &error_string_address)) { + return; + } + + std::string message; + // 1024 here is distinct from kMaxMessageSize above, because it refers to a + // precisely-sized buffer inside dyld. + if (process_reader_->Memory()->ReadCStringSizeLimited( + error_string_address, 1024, &message)) { + if (!message.empty()) { + vector_annotations->push_back(message); + } + } else { + LOG(WARNING) << "could not read dylinker error string from " << name_; + } +} + +void MachOImageAnnotationsReader::ReadCrashpadSimpleAnnotations( + std::map* simple_map_annotations) const { + process_types::CrashpadInfo crashpad_info; + if (!image_reader_->GetCrashpadInfo(&crashpad_info) || + !crashpad_info.simple_annotations) { + return; + } + + std::vector + simple_annotations(SimpleStringDictionary::num_entries); + if (!process_reader_->Memory()->Read( + crashpad_info.simple_annotations, + simple_annotations.size() * sizeof(simple_annotations[0]), + &simple_annotations[0])) { + LOG(WARNING) << "could not read simple annotations from " << name_; + return; + } + + for (const auto& entry : simple_annotations) { + size_t key_length = strnlen(entry.key, sizeof(entry.key)); + if (key_length) { + std::string key(entry.key, key_length); + std::string value(entry.value, strnlen(entry.value, sizeof(entry.value))); + if (!simple_map_annotations->insert(std::make_pair(key, value)).second) { + LOG(INFO) << "duplicate simple annotation " << key << " in " << name_; + } + } + } +} + +// TODO(https://crbug.com/crashpad/270): Replace implementations of +// ReadCrashpadAnnotationsList and ReadCrashpadSimpleAnnotations with the +// platform-agnostic implementations in ImageAnnotationReader. +void MachOImageAnnotationsReader::ReadCrashpadAnnotationsList( + std::vector* annotations) const { + process_types::CrashpadInfo crashpad_info; + if (!image_reader_->GetCrashpadInfo(&crashpad_info) || + !crashpad_info.annotations_list) { + return; + } + + process_types::AnnotationList annotation_list_object; + if (!annotation_list_object.Read(process_reader_, + crashpad_info.annotations_list)) { + LOG(WARNING) << "could not read annotations list object in " << name_; + return; + } + + process_types::Annotation current = annotation_list_object.head; + for (size_t index = 0; + current.link_node != annotation_list_object.tail_pointer && + index < kMaxNumberOfAnnotations; + ++index) { + if (!current.Read(process_reader_, current.link_node)) { + LOG(WARNING) << "could not read annotation at index " << index << " in " + << name_; + return; + } + + if (current.size == 0) { + continue; + } + + AnnotationSnapshot snapshot; + snapshot.type = current.type; + snapshot.value.resize(current.size); + + if (!process_reader_->Memory()->ReadCStringSizeLimited( + current.name, Annotation::kNameMaxLength, &snapshot.name)) { + LOG(WARNING) << "could not read annotation name at index " << index + << " in " << name_; + continue; + } + + size_t size = + std::min(static_cast(current.size), Annotation::kValueMaxSize); + if (!process_reader_->Memory()->Read( + current.value, size, snapshot.value.data())) { + LOG(WARNING) << "could not read annotation value at index " << index + << " in " << name_; + continue; + } + + annotations->push_back(std::move(snapshot)); + } +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_annotations_reader.h b/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_annotations_reader.h new file mode 100644 index 000000000..7a1a0a1c0 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_annotations_reader.h @@ -0,0 +1,103 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_ANNOTATIONS_READER_H_ +#define CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_ANNOTATIONS_READER_H_ + +#include +#include +#include + +#include "snapshot/annotation_snapshot.h" +#include "snapshot/mac/process_types.h" + +namespace crashpad { + +class MachOImageReader; +class ProcessReaderMac; + +//! \brief A reader for annotations stored in a Mach-O image mapped into another +//! process. +//! +//! These annotations are stored for the benefit of crash reporters, and provide +//! information thought to be potentially useful for crash analysis. This class +//! can decode annotations stored in these formats: +//! - CrashpadInfo. This format is used by Crashpad clients. The “simple +//! annotations†are recovered from any module with a compatible data +//! section, and are included in the annotations returned by SimpleMap(). +//! - `CrashReporterClient.h`’s `crashreporter_annotations_t`. This format is +//! used by Apple code. The `message` and `message2` fields can be recovered +//! from any module with a compatible data section, and are included in the +//! annotations returned by Vector(). +//! - `dyld`’s `error_string`. This format is used exclusively by dyld, +//! typically for fatal errors. This string can be recovered from any +//! `MH_DYLINKER`-type module with this symbol, and is included in the +//! annotations returned by Vector(). +class MachOImageAnnotationsReader { + public: + //! \brief Constructs an object. + //! + //! \param[in] process_reader The reader for the remote process. + //! \param[in] image_reader The MachOImageReader for the Mach-O image file + //! contained within the remote process. + //! \param[in] name The module’s name, a string to be used in logged messages. + //! This string is for diagnostic purposes only, and may be empty. + MachOImageAnnotationsReader(ProcessReaderMac* process_reader, + const MachOImageReader* image_reader, + const std::string& name); + + MachOImageAnnotationsReader(const MachOImageAnnotationsReader&) = delete; + MachOImageAnnotationsReader& operator=(const MachOImageAnnotationsReader&) = + delete; + + ~MachOImageAnnotationsReader() {} + + //! \brief Returns the module’s annotations that are organized as a vector of + //! strings. + std::vector Vector() const; + + //! \brief Returns the module’s annotations that are organized as key-value + //! pairs, where all keys and values are strings. + std::map SimpleMap() const; + + //! \brief Returns the module’s annotations that are organized as a list of + // typed annotation objects. + std::vector AnnotationsList() const; + + private: + // Reades crashreporter_annotations_t::message and + // crashreporter_annotations_t::message2 on behalf of Vector(). + void ReadCrashReporterClientAnnotations( + std::vector* vector_annotations) const; + + // Reads dyld_error_string on behalf of Vector(). + void ReadDyldErrorStringAnnotation( + std::vector* vector_annotations) const; + + // Reads CrashpadInfo::simple_annotations_ on behalf of SimpleMap(). + void ReadCrashpadSimpleAnnotations( + std::map* simple_map_annotations) const; + + // Reads CrashpadInfo::annotations_list_ on behalf of AnnotationsList(). + void ReadCrashpadAnnotationsList( + std::vector* vector_annotations) const; + + std::string name_; + ProcessReaderMac* process_reader_; // weak + const MachOImageReader* image_reader_; // weak +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_ANNOTATIONS_READER_H_ diff --git a/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_annotations_reader_test.cc b/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_annotations_reader_test.cc new file mode 100644 index 000000000..74a21be67 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_annotations_reader_test.cc @@ -0,0 +1,483 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/mac/mach_o_image_annotations_reader.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "base/files/file_path.h" +#include "client/annotation.h" +#include "client/annotation_list.h" +#include "client/crashpad_info.h" +#include "client/simple_string_dictionary.h" +#include "gtest/gtest.h" +#include "snapshot/mac/process_reader_mac.h" +#include "test/errors.h" +#include "test/mac/mach_errors.h" +#include "test/mac/mach_multiprocess.h" +#include "test/test_paths.h" +#include "util/file/file_io.h" +#include "util/mac/mac_util.h" +#include "util/mach/exc_server_variants.h" +#include "util/mach/exception_ports.h" +#include "util/mach/mach_extensions.h" +#include "util/mach/mach_message.h" +#include "util/mach/mach_message_server.h" + +namespace crashpad { +namespace test { +namespace { + +// \return The path to crashpad_snapshot_test_module_crashy_initializer.so +base::FilePath ModuleWithCrashyInitializer() { + return TestPaths::BuildArtifact("snapshot", + "module_crashy_initializer", + TestPaths::FileType::kLoadableModule); +} + +//! \return The path to the crashpad_snapshot_test_no_op executable. +base::FilePath NoOpExecutable() { + return TestPaths::BuildArtifact( + "snapshot", "no_op", TestPaths::FileType::kExecutable); +} + +class TestMachOImageAnnotationsReader final + : public MachMultiprocess, + public UniversalMachExcServer::Interface { + public: + enum TestType { + // Don’t crash, just test the CrashpadInfo interface. + kDontCrash = 0, + + // The child process should crash by calling abort(). The parent verifies + // that the system libraries set the expected annotations. + // + // This test verifies that the message field in crashreporter_annotations_t + // can be recovered. Either 10.10.2 Libc-1044.1.2/stdlib/FreeBSD/abort.c + // abort() or 10.10.2 Libc-1044.10.1/sys/_libc_fork_child.c + // _libc_fork_child() calls CRSetCrashLogMessage() to set the message field. + kCrashAbort, + + // The child process should crash at module initialization time, when dyld + // will have set an annotation matching the path of the module being + // initialized. + // + // This test exists to verify that the message2 field in + // crashreporter_annotations_t can be recovered. 10.10.2 + // dyld-353.2.1/src/ImageLoaderMachO.cpp + // ImageLoaderMachO::doInitialization() calls CRSetCrashLogMessage2() to set + // the message2 field. + kCrashModuleInitialization, + + // The child process should crash by setting DYLD_INSERT_LIBRARIES to + // contain a nonexistent library. The parent verifies that dyld sets the + // expected annotations. + kCrashDyld, + }; + + explicit TestMachOImageAnnotationsReader(TestType test_type) + : MachMultiprocess(), + UniversalMachExcServer::Interface(), + test_type_(test_type) { + switch (test_type_) { + case kDontCrash: + // SetExpectedChildTermination(kTerminationNormal, EXIT_SUCCESS) is the + // default. + break; + + case kCrashAbort: + SetExpectedChildTermination(kTerminationSignal, SIGABRT); + break; + + case kCrashModuleInitialization: + SetExpectedChildTerminationBuiltinTrap(); + break; + + case kCrashDyld: + // Prior to 10.12, dyld fatal errors result in the execution of an + // int3 instruction on x86 and a trap instruction on ARM, both of + // which raise SIGTRAP. 10.9.5 dyld-239.4/src/dyldStartup.s + // _dyld_fatal_error. This changed in 10.12 to use + // abort_with_payload(), which appears as SIGABRT to a waiting parent. + SetExpectedChildTermination( + kTerminationSignal, + MacOSVersionNumber() < 10'12'00 ? SIGTRAP : SIGABRT); + break; + } + } + + TestMachOImageAnnotationsReader(const TestMachOImageAnnotationsReader&) = + delete; + TestMachOImageAnnotationsReader& operator=( + const TestMachOImageAnnotationsReader&) = delete; + + ~TestMachOImageAnnotationsReader() {} + + // UniversalMachExcServer::Interface: + kern_return_t CatchMachException(exception_behavior_t behavior, + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count, + const mach_msg_trailer_t* trailer, + bool* destroy_complex_request) override { + *destroy_complex_request = true; + + if (test_type_ != kCrashDyld) { + // In 10.12.1 and later, the task port will not match ChildTask() in the + // kCrashDyld case, because kCrashDyld uses execl(), which results in a + // new task port being assigned. + EXPECT_EQ(task, ChildTask()); + } + + // The process ID should always compare favorably. + pid_t task_pid; + kern_return_t kr = pid_for_task(task, &task_pid); + EXPECT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "pid_for_task"); + EXPECT_EQ(task_pid, ChildPID()); + + ProcessReaderMac process_reader; + bool rv = process_reader.Initialize(task); + if (!rv) { + ADD_FAILURE(); + } else { + const std::vector& modules = + process_reader.Modules(); + std::vector all_annotations_vector; + for (const ProcessReaderMac::Module& module : modules) { + if (module.reader) { + MachOImageAnnotationsReader module_annotations_reader( + &process_reader, module.reader, module.name); + std::vector module_annotations_vector = + module_annotations_reader.Vector(); + all_annotations_vector.insert(all_annotations_vector.end(), + module_annotations_vector.begin(), + module_annotations_vector.end()); + } else { + EXPECT_TRUE(module.reader); + } + } + + // Mac OS X 10.6 doesn’t have support for CrashReporter annotations + // (CrashReporterClient.h), so don’t look for any special annotations in + // that version. + const int macos_version_number = MacOSVersionNumber(); + if (macos_version_number > 10'07'00) { + EXPECT_GE(all_annotations_vector.size(), 1u); + + std::string expected_annotation; + switch (test_type_) { + case kCrashAbort: + // The child process calls abort(), so the expected annotation + // reflects this, with a string set by 10.7.5 + // Libc-763.13/stdlib/abort-fbsd.c abort(). This string is still + // present in 10.9.5 Libc-997.90.3/stdlib/FreeBSD/abort.c abort(), + // but because abort() tests to see if a message is already set and + // something else in Libc will have set a message, this string is + // not the expectation on 10.9 or higher. Instead, after fork(), the + // child process has a message indicating that a fork() without + // exec() occurred. See 10.9.5 Libc-997.90.3/sys/_libc_fork_child.c + // _libc_fork_child(). + expected_annotation = + macos_version_number <= 10'08'00 + ? "abort() called" + : "crashed on child side of fork pre-exec"; + break; + + case kCrashModuleInitialization: + // This message is set by dyld-353.2.1/src/ImageLoaderMachO.cpp + // ImageLoaderMachO::doInitialization(). + expected_annotation = ModuleWithCrashyInitializer().value(); + break; + + case kCrashDyld: + // This is independent of dyld’s error_string, which is tested + // below. + expected_annotation = "dyld: launch, loading dependent libraries"; + break; + + default: + ADD_FAILURE(); + break; + } + + bool found = false; + for (const std::string& annotation : all_annotations_vector) { + // Look for the expectation as a leading susbtring, because the actual + // string that dyld uses will have the contents of the + // DYLD_INSERT_LIBRARIES environment variable appended to it on OS X + // 10.10. + if (annotation.substr(0, expected_annotation.length()) == + expected_annotation) { + found = true; + break; + } + } + EXPECT_TRUE(found) << expected_annotation; + } + + // dyld exposes its error_string at least as far back as Mac OS X 10.4. + if (test_type_ == kCrashDyld) { + static constexpr char kExpectedAnnotation[] = + "could not load inserted library"; + size_t expected_annotation_length = strlen(kExpectedAnnotation); + bool found = false; + for (const std::string& annotation : all_annotations_vector) { + // Look for the expectation as a leading substring, because the actual + // string will contain the library’s pathname and, on OS X 10.9 and + // later, a reason. + if (annotation.substr(0, expected_annotation_length) == + kExpectedAnnotation) { + found = true; + break; + } + } + + EXPECT_TRUE(found) << kExpectedAnnotation; + } + } + + ExcServerCopyState( + behavior, old_state, old_state_count, new_state, new_state_count); + return ExcServerSuccessfulReturnValue(exception, behavior, false); + } + + private: + // MachMultiprocess: + + void MachMultiprocessParent() override { + ProcessReaderMac process_reader; + ASSERT_TRUE(process_reader.Initialize(ChildTask())); + + // Wait for the child process to indicate that it’s done setting up its + // annotations via the CrashpadInfo interface. + char c; + CheckedReadFileExactly(ReadPipeHandle(), &c, sizeof(c)); + + // Verify the “simple map†and object-based annotations set via the + // CrashpadInfo interface. + const std::vector& modules = + process_reader.Modules(); + std::map all_annotations_simple_map; + std::vector all_annotations; + for (const ProcessReaderMac::Module& module : modules) { + MachOImageAnnotationsReader module_annotations_reader( + &process_reader, module.reader, module.name); + std::map module_annotations_simple_map = + module_annotations_reader.SimpleMap(); + all_annotations_simple_map.insert(module_annotations_simple_map.begin(), + module_annotations_simple_map.end()); + + std::vector annotations = + module_annotations_reader.AnnotationsList(); + all_annotations.insert( + all_annotations.end(), annotations.begin(), annotations.end()); + } + + EXPECT_GE(all_annotations_simple_map.size(), 5u); + EXPECT_EQ(all_annotations_simple_map["#TEST# pad"], "crash"); + EXPECT_EQ(all_annotations_simple_map["#TEST# key"], "value"); + EXPECT_EQ(all_annotations_simple_map["#TEST# x"], "y"); + EXPECT_EQ(all_annotations_simple_map["#TEST# longer"], "shorter"); + EXPECT_EQ(all_annotations_simple_map["#TEST# empty_value"], ""); + + EXPECT_EQ(all_annotations.size(), 3u); + bool saw_same_name_3 = false, saw_same_name_4 = false; + for (const auto& annotation : all_annotations) { + EXPECT_EQ(annotation.type, + static_cast(Annotation::Type::kString)); + std::string value(reinterpret_cast(annotation.value.data()), + annotation.value.size()); + + if (annotation.name == "#TEST# one") { + EXPECT_EQ(value, "moocow"); + } else if (annotation.name == "#TEST# same-name") { + if (value == "same-name 3") { + EXPECT_FALSE(saw_same_name_3); + saw_same_name_3 = true; + } else if (value == "same-name 4") { + EXPECT_FALSE(saw_same_name_4); + saw_same_name_4 = true; + } else { + ADD_FAILURE() << "unexpected annotation value " << value; + } + } else { + ADD_FAILURE() << "unexpected annotation " << annotation.name; + } + } + + // Tell the child process that it’s permitted to crash. + CheckedWriteFile(WritePipeHandle(), &c, sizeof(c)); + + if (test_type_ != kDontCrash) { + // Handle the child’s crash. Further validation will be done in + // CatchMachException(). + UniversalMachExcServer universal_mach_exc_server(this); + + mach_msg_return_t mr = + MachMessageServer::Run(&universal_mach_exc_server, + LocalPort(), + MACH_MSG_OPTION_NONE, + MachMessageServer::kOneShot, + MachMessageServer::kReceiveLargeError, + kMachMessageTimeoutWaitIndefinitely); + EXPECT_EQ(mr, MACH_MSG_SUCCESS) + << MachErrorMessage(mr, "MachMessageServer::Run"); + } + } + + void MachMultiprocessChild() override { + CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo(); + + // This is “leaked†to crashpad_info. + SimpleStringDictionary* simple_annotations = new SimpleStringDictionary(); + simple_annotations->SetKeyValue("#TEST# pad", "break"); + simple_annotations->SetKeyValue("#TEST# key", "value"); + simple_annotations->SetKeyValue("#TEST# pad", "crash"); + simple_annotations->SetKeyValue("#TEST# x", "y"); + simple_annotations->SetKeyValue("#TEST# longer", "shorter"); + simple_annotations->SetKeyValue("#TEST# empty_value", ""); + + crashpad_info->set_simple_annotations(simple_annotations); + + AnnotationList::Register(); // This is “leaked†to crashpad_info. + + static StringAnnotation<32> test_annotation_one{"#TEST# one"}; + static StringAnnotation<32> test_annotation_two{"#TEST# two"}; + static StringAnnotation<32> test_annotation_three{"#TEST# same-name"}; + static StringAnnotation<32> test_annotation_four{"#TEST# same-name"}; + + test_annotation_one.Set("moocow"); + test_annotation_two.Set("this will be cleared"); + test_annotation_three.Set("same-name 3"); + test_annotation_four.Set("same-name 4"); + test_annotation_two.Clear(); + + // Tell the parent that the environment has been set up. + char c = '\0'; + CheckedWriteFile(WritePipeHandle(), &c, sizeof(c)); + + // Wait for the parent to indicate that it’s safe to crash. + CheckedReadFileExactly(ReadPipeHandle(), &c, sizeof(c)); + + // Direct an exception message to the exception server running in the + // parent. + ExceptionPorts exception_ports(ExceptionPorts::kTargetTypeTask, + mach_task_self()); + ASSERT_TRUE(exception_ports.SetExceptionPort( + EXC_MASK_CRASH, RemotePort(), EXCEPTION_DEFAULT, THREAD_STATE_NONE)); + + switch (test_type_) { + case kDontCrash: { + break; + } + + case kCrashAbort: { + abort(); + } + + case kCrashModuleInitialization: { + // Load a module that crashes while executing a module initializer. + void* dl_handle = dlopen(ModuleWithCrashyInitializer().value().c_str(), + RTLD_LAZY | RTLD_LOCAL); + + // This should have crashed in the dlopen(). If dlopen() failed, the + // ASSERT_NE() will show the message. If it succeeded without crashing, + // the FAIL() will fail the test. + ASSERT_NE(dl_handle, nullptr) << dlerror(); + FAIL(); + } + + case kCrashDyld: { + // Set DYLD_INSERT_LIBRARIES to contain a library that does not exist. + // Unable to load it, dyld will abort with a fatal error. + ASSERT_EQ( + setenv( + "DYLD_INSERT_LIBRARIES", "/var/empty/NoDirectory/NoLibrary", 1), + 0) + << ErrnoMessage("setenv"); + + // The actual executable doesn’t matter very much, because dyld won’t + // ever launch it. It just needs to be an executable that uses dyld as + // its LC_LOAD_DYLINKER (all normal executables do). A custom no-op + // executable is provided because DYLD_INSERT_LIBRARIES does not work + // with system executables on OS X 10.11 due to System Integrity + // Protection. + base::FilePath no_op_executable = NoOpExecutable(); + ASSERT_EQ(execl(no_op_executable.value().c_str(), + no_op_executable.BaseName().value().c_str(), + nullptr), + 0) + << ErrnoMessage("execl"); + break; + } + + default: + break; + } + } + + TestType test_type_; +}; + +TEST(MachOImageAnnotationsReader, DontCrash) { + TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader( + TestMachOImageAnnotationsReader::kDontCrash); + test_mach_o_image_annotations_reader.Run(); +} + +TEST(MachOImageAnnotationsReader, CrashAbort) { + TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader( + TestMachOImageAnnotationsReader::kCrashAbort); + test_mach_o_image_annotations_reader.Run(); +} + +#if defined(ADDRESS_SANITIZER) +// https://crbug.com/844396 +#define MAYBE_CrashModuleInitialization DISABLED_CrashModuleInitialization +#else +#define MAYBE_CrashModuleInitialization CrashModuleInitialization +#endif +TEST(MachOImageAnnotationsReader, MAYBE_CrashModuleInitialization) { + TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader( + TestMachOImageAnnotationsReader::kCrashModuleInitialization); + test_mach_o_image_annotations_reader.Run(); +} + +TEST(MachOImageAnnotationsReader, CrashDyld) { + TestMachOImageAnnotationsReader test_mach_o_image_annotations_reader( + TestMachOImageAnnotationsReader::kCrashDyld); + test_mach_o_image_annotations_reader.Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_annotations_reader_test_module_crashy_initializer.cc b/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_annotations_reader_test_module_crashy_initializer.cc new file mode 100644 index 000000000..2ba9bee04 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_annotations_reader_test_module_crashy_initializer.cc @@ -0,0 +1,31 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +namespace crashpad { +namespace test { +namespace { + +class CrashyClass { + public: + CrashyClass() { + __builtin_trap(); + } +}; + +// __attribute__((used)) keeps the dead code stripper away. +__attribute__((used)) CrashyClass g_crashy_object; + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_annotations_reader_test_no_op.cc b/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_annotations_reader_test_no_op.cc new file mode 100644 index 000000000..b8953207b --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_annotations_reader_test_no_op.cc @@ -0,0 +1,19 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 + +int main(int argc, char* argv[]) { + return EXIT_SUCCESS; +} diff --git a/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_reader.cc b/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_reader.cc new file mode 100644 index 000000000..5c5bcb00f --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_reader.cc @@ -0,0 +1,733 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/mac/mach_o_image_reader.h" + +#include +#include +#include + +#include +#include +#include + +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "client/crashpad_info.h" +#include "snapshot/mac/mach_o_image_segment_reader.h" +#include "snapshot/mac/mach_o_image_symbol_table_reader.h" +#include "snapshot/mac/process_reader_mac.h" +#include "util/mac/checked_mach_address_range.h" +#include "util/misc/implicit_cast.h" + +namespace { + +constexpr uint32_t kInvalidSegmentIndex = std::numeric_limits::max(); + +} // namespace + +namespace crashpad { + +MachOImageReader::MachOImageReader() + : segments_(), + segment_map_(), + module_name_(), + module_info_(), + dylinker_name_(), + uuid_(), + address_(0), + size_(0), + slide_(0), + source_version_(0), + symtab_command_(), + dysymtab_command_(), + symbol_table_(), + id_dylib_command_(), + process_reader_(nullptr), + file_type_(0), + initialized_(), + symbol_table_initialized_() { +} + +MachOImageReader::~MachOImageReader() { +} + +bool MachOImageReader::Initialize(ProcessReaderMac* process_reader, + mach_vm_address_t address, + const std::string& name) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + process_reader_ = process_reader; + address_ = address; + module_name_ = name; + + module_info_ = + base::StringPrintf(", module %s, address 0x%llx", name.c_str(), address); + + process_types::mach_header mach_header; + if (!mach_header.Read(process_reader, address)) { + LOG(WARNING) << "could not read mach_header" << module_info_; + return false; + } + + const bool is_64_bit = process_reader->Is64Bit(); + const uint32_t kExpectedMagic = is_64_bit ? MH_MAGIC_64 : MH_MAGIC; + if (mach_header.magic != kExpectedMagic) { + LOG(WARNING) << base::StringPrintf("unexpected mach_header::magic 0x%08x", + mach_header.magic) << module_info_; + return false; + } + + switch (mach_header.filetype) { + case MH_EXECUTE: + case MH_DYLIB: + case MH_DYLINKER: + case MH_BUNDLE: + file_type_ = mach_header.filetype; + break; + default: + LOG(WARNING) << base::StringPrintf( + "unexpected mach_header::filetype 0x%08x", + mach_header.filetype) << module_info_; + return false; + } + + const uint32_t kExpectedSegmentCommand = + is_64_bit ? LC_SEGMENT_64 : LC_SEGMENT; + const uint32_t kUnexpectedSegmentCommand = + is_64_bit ? LC_SEGMENT : LC_SEGMENT_64; + + const struct { + // Which method to call when encountering a load command matching |command|. + bool (MachOImageReader::*function)(mach_vm_address_t, const std::string&); + + // The minimum size that may be allotted to store the load command. + size_t size; + + // The load command to match. + uint32_t command; + + // True if the load command must not appear more than one time. + bool singleton; + } kLoadCommandReaders[] = { + { + &MachOImageReader::ReadSegmentCommand, + process_types::segment_command::ExpectedSize(process_reader), + kExpectedSegmentCommand, + false, + }, + { + &MachOImageReader::ReadSymTabCommand, + process_types::symtab_command::ExpectedSize(process_reader), + LC_SYMTAB, + true, + }, + { + &MachOImageReader::ReadDySymTabCommand, + process_types::symtab_command::ExpectedSize(process_reader), + LC_DYSYMTAB, + true, + }, + { + &MachOImageReader::ReadIdDylibCommand, + process_types::dylib_command::ExpectedSize(process_reader), + LC_ID_DYLIB, + true, + }, + { + &MachOImageReader::ReadDylinkerCommand, + process_types::dylinker_command::ExpectedSize(process_reader), + LC_LOAD_DYLINKER, + true, + }, + { + &MachOImageReader::ReadDylinkerCommand, + process_types::dylinker_command::ExpectedSize(process_reader), + LC_ID_DYLINKER, + true, + }, + { + &MachOImageReader::ReadUUIDCommand, + process_types::uuid_command::ExpectedSize(process_reader), + LC_UUID, + true, + }, + { + &MachOImageReader::ReadSourceVersionCommand, + process_types::source_version_command::ExpectedSize(process_reader), + LC_SOURCE_VERSION, + true, + }, + + // When reading a 64-bit process, no 32-bit segment commands should be + // present, and vice-versa. + { + &MachOImageReader::ReadUnexpectedCommand, + process_types::load_command::ExpectedSize(process_reader), + kUnexpectedSegmentCommand, + false, + }, + }; + + // This vector is parallel to the kLoadCommandReaders array, and tracks + // whether a singleton load command matching the |command| field has been + // found yet. + std::vector singleton_indices(std::size(kLoadCommandReaders), + kInvalidSegmentIndex); + + size_t offset = mach_header.Size(); + const mach_vm_address_t kLoadCommandAddressLimit = + address + offset + mach_header.sizeofcmds; + + for (uint32_t load_command_index = 0; + load_command_index < mach_header.ncmds; + ++load_command_index) { + mach_vm_address_t load_command_address = address + offset; + std::string load_command_info = base::StringPrintf(", load command %u/%u%s", + load_command_index, + mach_header.ncmds, + module_info_.c_str()); + + process_types::load_command load_command; + + // Make sure that the basic load command structure doesn’t overflow the + // space allotted for load commands. + if (load_command_address + load_command.ExpectedSize(process_reader) > + kLoadCommandAddressLimit) { + LOG(WARNING) << base::StringPrintf( + "load_command at 0x%llx exceeds sizeofcmds 0x%x", + load_command_address, + mach_header.sizeofcmds) << load_command_info; + return false; + } + + if (!load_command.Read(process_reader, load_command_address)) { + LOG(WARNING) << "could not read load_command" << load_command_info; + return false; + } + + load_command_info = base::StringPrintf(", load command 0x%x %u/%u%s", + load_command.cmd, + load_command_index, + mach_header.ncmds, + module_info_.c_str()); + + // Now that the load command’s stated size is known, make sure that it + // doesn’t overflow the space allotted for load commands. + if (load_command_address + load_command.cmdsize > + kLoadCommandAddressLimit) { + LOG(WARNING) + << base::StringPrintf( + "load_command at 0x%llx cmdsize 0x%x exceeds sizeofcmds 0x%x", + load_command_address, + load_command.cmdsize, + mach_header.sizeofcmds) << load_command_info; + return false; + } + + for (size_t reader_index = 0; reader_index < std::size(kLoadCommandReaders); + ++reader_index) { + if (load_command.cmd != kLoadCommandReaders[reader_index].command) { + continue; + } + + if (load_command.cmdsize < kLoadCommandReaders[reader_index].size) { + LOG(WARNING) << base::StringPrintf( + "load command cmdsize 0x%x insufficient for 0x%zx", + load_command.cmdsize, + kLoadCommandReaders[reader_index].size) + << load_command_info; + return false; + } + + if (kLoadCommandReaders[reader_index].singleton) { + if (singleton_indices[reader_index] != kInvalidSegmentIndex) { + LOG(WARNING) << "duplicate load command at " + << singleton_indices[reader_index] << load_command_info; + return false; + } + + singleton_indices[reader_index] = load_command_index; + } + + if (!((this)->*(kLoadCommandReaders[reader_index].function))( + load_command_address, load_command_info)) { + return false; + } + + break; + } + + offset += load_command.cmdsize; + } + + // Now that the slide is known, push it into the segments. + for (const auto& segment : segments_) { + segment->SetSlide(slide_); + + // This was already checked for the unslid values while the segments were + // read, but now it’s possible to check the slid values too. The individual + // sections don’t need to be checked because they were verified to be + // contained within their respective segments when the segments were read. + mach_vm_address_t slid_segment_address = segment->Address(); + mach_vm_size_t slid_segment_size = segment->Size(); + CheckedMachAddressRange slid_segment_range( + process_reader_->Is64Bit(), slid_segment_address, slid_segment_size); + if (!slid_segment_range.IsValid()) { + LOG(WARNING) << base::StringPrintf( + "invalid slid segment range 0x%llx + 0x%llx, " + "segment ", + slid_segment_address, + slid_segment_size) << segment->Name() << module_info_; + return false; + } + } + + if (!segment_map_.count(SEG_TEXT)) { + // The __TEXT segment is required. Even a module with no executable code + // will have a __TEXT segment encompassing the Mach-O header and load + // commands. Without a __TEXT segment, |size_| will not have been computed. + LOG(WARNING) << "no " SEG_TEXT " segment" << module_info_; + return false; + } + + if (mach_header.filetype == MH_DYLIB && !id_dylib_command_) { + // This doesn’t render a module unusable, it’s just weird and worth noting. + LOG(INFO) << "no LC_ID_DYLIB" << module_info_; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +const MachOImageSegmentReader* MachOImageReader::GetSegmentByName( + const std::string& segment_name) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + const auto& iterator = segment_map_.find(segment_name); + if (iterator == segment_map_.end()) { + return nullptr; + } + + return segments_[iterator->second].get(); +} + +const process_types::section* MachOImageReader::GetSectionByName( + const std::string& segment_name, + const std::string& section_name, + mach_vm_address_t* address) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + const MachOImageSegmentReader* segment = GetSegmentByName(segment_name); + if (!segment) { + return nullptr; + } + + return segment->GetSectionByName(section_name, address); +} + +const process_types::section* MachOImageReader::GetSectionAtIndex( + size_t index, + const MachOImageSegmentReader** containing_segment, + mach_vm_address_t* address) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + static_assert(NO_SECT == 0, "NO_SECT must be zero"); + if (index == NO_SECT) { + LOG(WARNING) << "section index " << index << " out of range"; + return nullptr; + } + + // Switch to a more comfortable 0-based index. + size_t local_index = index - 1; + + for (const auto& segment : segments_) { + size_t nsects = segment->nsects(); + if (local_index < nsects) { + const process_types::section* section = + segment->GetSectionAtIndex(local_index, address); + + if (containing_segment) { + *containing_segment = segment.get(); + } + + return section; + } + + local_index -= nsects; + } + + LOG(WARNING) << "section index " << index << " out of range"; + return nullptr; +} + +bool MachOImageReader::LookUpExternalDefinedSymbol( + const std::string& name, + mach_vm_address_t* value) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (symbol_table_initialized_.is_uninitialized()) { + InitializeSymbolTable(); + } + + if (!symbol_table_initialized_.is_valid() || !symbol_table_) { + return false; + } + + const MachOImageSymbolTableReader::SymbolInformation* symbol_info = + symbol_table_->LookUpExternalDefinedSymbol(name); + if (!symbol_info) { + return false; + } + + if (symbol_info->section == NO_SECT) { + // This is an absolute (N_ABS) symbol, which requires no further validation + // or processing. + *value = symbol_info->value; + return true; + } + + // This is a symbol defined in a particular section, so make sure that it’s + // valid for that section and fix it up for any “slide†as needed. + + mach_vm_address_t section_address; + const MachOImageSegmentReader* segment; + const process_types::section* section = + GetSectionAtIndex(symbol_info->section, &segment, §ion_address); + if (!section) { + return false; + } + + mach_vm_address_t slid_value = + symbol_info->value + (segment->SegmentSlides() ? slide_ : 0); + + // The __mh_execute_header (_MH_EXECUTE_SYM) symbol is weird. In + // position-independent executables, it shows up in the symbol table as a + // symbol in section 1, although it’s not really in that section. It points to + // the mach_header[_64], which is the beginning of the __TEXT segment, and the + // __text section normally begins after the load commands in the __TEXT + // segment. The range check below will fail for this symbol, because it’s not + // really in the section it claims to be in. See Xcode 5.1 + // ld64-236.3/src/ld/OutputFile.cpp ld::tool::OutputFile::buildSymbolTable(). + // There, ld takes symbols that refer to anything in the mach_header[_64] and + // marks them as being in section 1. Here, section 1 is treated in this same + // special way as long as it’s in the __TEXT segment that begins at the start + // of the image, which is normally the case, and as long as the symbol’s value + // is the base of the image. + // + // This only happens for PIE executables, because __mh_execute_header needs + // to slide. In non-PIE executables, __mh_execute_header is an absolute + // symbol. + CheckedMachAddressRange section_range( + process_reader_->Is64Bit(), section_address, section->size); + if (!section_range.ContainsValue(slid_value) && + !(symbol_info->section == 1 && segment->Name() == SEG_TEXT && + slid_value == Address())) { + std::string section_name_full = + MachOImageSegmentReader::SegmentAndSectionNameString(section->segname, + section->sectname); + LOG(WARNING) << base::StringPrintf( + "symbol %s (0x%llx) outside of section %s (0x%llx + " + "0x%llx)", + name.c_str(), + slid_value, + section_name_full.c_str(), + section_address, + section->size) << module_info_; + return false; + } + + *value = slid_value; + return true; +} + +uint32_t MachOImageReader::DylibVersion() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + DCHECK_EQ(FileType(), implicit_cast(MH_DYLIB)); + + if (id_dylib_command_) { + return id_dylib_command_->dylib_current_version; + } + + // In case this was a weird dylib without an LC_ID_DYLIB command. + return 0; +} + +void MachOImageReader::UUID(crashpad::UUID* uuid) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + memcpy(uuid, &uuid_, sizeof(uuid_)); +} + +bool MachOImageReader::GetCrashpadInfo( + process_types::CrashpadInfo* crashpad_info) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + mach_vm_address_t crashpad_info_address; + const process_types::section* crashpad_info_section = + GetSectionByName(SEG_DATA, "crashpad_info", &crashpad_info_address); + if (!crashpad_info_section) { + return false; + } + + if (crashpad_info_section->size < + crashpad_info->MinimumSize(process_reader_)) { + LOG(WARNING) << "small crashpad info section size " + << crashpad_info_section->size << module_info_; + return false; + } + + // This Read() will zero out anything beyond the structure’s declared size. + if (!crashpad_info->Read(process_reader_, crashpad_info_address)) { + LOG(WARNING) << "could not read crashpad info" << module_info_; + return false; + } + + if (crashpad_info->signature != CrashpadInfo::kSignature || + crashpad_info->version != 1) { + LOG(WARNING) << base::StringPrintf( + "unexpected crashpad info signature 0x%x, version %u%s", + crashpad_info->signature, + crashpad_info->version, + module_info_.c_str()); + return false; + } + + // Don’t require strict equality, to leave wiggle room for sloppy linkers. + if (crashpad_info->size > crashpad_info_section->size) { + LOG(WARNING) << "crashpad info struct size " << crashpad_info->size + << " large for section size " << crashpad_info_section->size + << module_info_; + return false; + } + + if (crashpad_info->size > crashpad_info->ExpectedSize(process_reader_)) { + // This isn’t strictly a problem, because unknown fields will simply be + // ignored, but it may be of diagnostic interest. + LOG(INFO) << "large crashpad info size " << crashpad_info->size + << module_info_; + } + + return true; +} + +template +bool MachOImageReader::ReadLoadCommand(mach_vm_address_t load_command_address, + const std::string& load_command_info, + uint32_t expected_load_command_id, + T* load_command) { + if (!load_command->Read(process_reader_, load_command_address)) { + LOG(WARNING) << "could not read load command" << load_command_info; + return false; + } + + DCHECK_GE(load_command->cmdsize, load_command->Size()); + DCHECK_EQ(load_command->cmd, expected_load_command_id); + return true; +} + +bool MachOImageReader::ReadSegmentCommand( + mach_vm_address_t load_command_address, + const std::string& load_command_info) { + size_t segment_index = segments_.size(); + segments_.push_back(std::make_unique()); + MachOImageSegmentReader* segment = segments_.back().get(); + + if (!segment->Initialize(process_reader_, + load_command_address, + load_command_info, + module_name_, + file_type_)) { + segments_.pop_back(); + return false; + } + + // At this point, the segment itself is considered valid, but if one of the + // next checks fails, it will render the module invalid. If any of the next + // checks fail, this method should return false, but it doesn’t need to bother + // removing the segment from segments_. The segment will be properly released + // when the image is destroyed, and the image won’t be usable because + // initialization won’t have completed. Most importantly, leaving the segment + // in segments_ means that no other structures (such as perhaps segment_map_) + // become inconsistent or require cleanup. + + const std::string segment_name = segment->Name(); + const auto insert_result = + segment_map_.insert(std::make_pair(segment_name, segment_index)); + if (!insert_result.second) { + LOG(WARNING) << base::StringPrintf("duplicate %s segment at %zu and %zu", + segment_name.c_str(), + insert_result.first->second, + segment_index) << load_command_info; + return false; + } + + if (segment_name == SEG_TEXT) { + mach_vm_size_t vmsize = segment->vmsize(); + + if (vmsize == 0) { + LOG(WARNING) << "zero-sized " SEG_TEXT " segment" << load_command_info; + return false; + } + + size_ = vmsize; + + // The slide is computed as the difference between the __TEXT segment’s + // preferred and actual load addresses. This is the same way that dyld + // computes slide. See 10.9.2 dyld-239.4/src/dyldInitialization.cpp + // slideOfMainExecutable(). + slide_ = address_ - segment->vmaddr(); + } + + return true; +} + +bool MachOImageReader::ReadSymTabCommand(mach_vm_address_t load_command_address, + const std::string& load_command_info) { + symtab_command_.reset(new process_types::symtab_command()); + return ReadLoadCommand(load_command_address, + load_command_info, + LC_SYMTAB, + symtab_command_.get()); +} + +bool MachOImageReader::ReadDySymTabCommand( + mach_vm_address_t load_command_address, + const std::string& load_command_info) { + dysymtab_command_.reset(new process_types::dysymtab_command()); + return ReadLoadCommand(load_command_address, + load_command_info, + LC_DYSYMTAB, + dysymtab_command_.get()); +} + +bool MachOImageReader::ReadIdDylibCommand( + mach_vm_address_t load_command_address, + const std::string& load_command_info) { + if (file_type_ != MH_DYLIB) { + LOG(WARNING) << base::StringPrintf( + "LC_ID_DYLIB inappropriate in non-dylib file type 0x%x", + file_type_) << load_command_info; + return false; + } + + DCHECK(!id_dylib_command_); + id_dylib_command_.reset(new process_types::dylib_command()); + return ReadLoadCommand(load_command_address, + load_command_info, + LC_ID_DYLIB, + id_dylib_command_.get()); +} + +bool MachOImageReader::ReadDylinkerCommand( + mach_vm_address_t load_command_address, + const std::string& load_command_info) { + if (file_type_ != MH_EXECUTE && file_type_ != MH_DYLINKER) { + LOG(WARNING) << base::StringPrintf( + "LC_LOAD_DYLINKER/LC_ID_DYLINKER inappropriate in file " + "type 0x%x", + file_type_) << load_command_info; + return false; + } + + const uint32_t kExpectedCommand = + file_type_ == MH_DYLINKER ? LC_ID_DYLINKER : LC_LOAD_DYLINKER; + process_types::dylinker_command dylinker_command; + if (!ReadLoadCommand(load_command_address, + load_command_info, + kExpectedCommand, + &dylinker_command)) { + return false; + } + + if (!process_reader_->Memory()->ReadCStringSizeLimited( + load_command_address + dylinker_command.name, + dylinker_command.cmdsize - dylinker_command.name, + &dylinker_name_)) { + LOG(WARNING) << "could not read dylinker_command name" << load_command_info; + return false; + } + + return true; +} + +bool MachOImageReader::ReadUUIDCommand(mach_vm_address_t load_command_address, + const std::string& load_command_info) { + process_types::uuid_command uuid_command; + if (!ReadLoadCommand( + load_command_address, load_command_info, LC_UUID, &uuid_command)) { + return false; + } + + uuid_.InitializeFromBytes(uuid_command.uuid); + return true; +} + +bool MachOImageReader::ReadSourceVersionCommand( + mach_vm_address_t load_command_address, + const std::string& load_command_info) { + process_types::source_version_command source_version_command; + if (!ReadLoadCommand(load_command_address, + load_command_info, + LC_SOURCE_VERSION, + &source_version_command)) { + return false; + } + + source_version_ = source_version_command.version; + return true; +} + +bool MachOImageReader::ReadUnexpectedCommand( + mach_vm_address_t load_command_address, + const std::string& load_command_info) { + LOG(WARNING) << "unexpected load command" << load_command_info; + return false; +} + +void MachOImageReader::InitializeSymbolTable() const { + DCHECK(symbol_table_initialized_.is_uninitialized()); + symbol_table_initialized_.set_invalid(); + + if (!symtab_command_) { + // It’s technically valid for there to be no LC_SYMTAB, and in that case, + // any symbol lookups should fail. Mark the symbol table as valid, and + // LookUpExternalDefinedSymbol() will understand what it means when this is + // valid but symbol_table_ is not present. + symbol_table_initialized_.set_valid(); + return; + } + + // Find the __LINKEDIT segment. Technically, the symbol table can be in any + // mapped segment, but by convention, it’s in the one named __LINKEDIT. + const MachOImageSegmentReader* linkedit_segment = + GetSegmentByName(SEG_LINKEDIT); + if (!linkedit_segment) { + LOG(WARNING) << "no " SEG_LINKEDIT " segment"; + return; + } + + symbol_table_.reset(new MachOImageSymbolTableReader()); + if (!symbol_table_->Initialize(process_reader_, + symtab_command_.get(), + dysymtab_command_.get(), + linkedit_segment, + module_info_)) { + symbol_table_.reset(); + return; + } + + symbol_table_initialized_.set_valid(); +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_reader.h b/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_reader.h new file mode 100644 index 000000000..0c60bcad2 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_reader.h @@ -0,0 +1,357 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_READER_H_ +#define CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_READER_H_ + +#include +#include +#include + +#include +#include +#include +#include + +#include "snapshot/mac/process_types.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/misc/uuid.h" + +namespace crashpad { + +class MachOImageSegmentReader; +class MachOImageSymbolTableReader; +class ProcessReaderMac; + +//! \brief A reader for Mach-O images mapped into another process. +//! +//! This class is capable of reading both 32-bit (`mach_header`/`MH_MAGIC`) and +//! 64-bit (`mach_header_64`/`MH_MAGIC_64`) images based on the bitness of the +//! remote process. +//! +//! \sa MachOImageAnnotationsReader +class MachOImageReader { + public: + MachOImageReader(); + + MachOImageReader(const MachOImageReader&) = delete; + MachOImageReader& operator=(const MachOImageReader&) = delete; + + ~MachOImageReader(); + + //! \brief Reads the Mach-O image file’s load commands from another process. + //! + //! This method must only be called once on an object. This method must be + //! called successfully before any other method in this class may be called. + //! + //! \param[in] process_reader The reader for the remote process. + //! \param[in] address The address, in the remote process’ address space, + //! where the `mach_header` or `mach_header_64` at the beginning of the + //! image to be read is located. This address can be determined by reading + //! the remote process’ dyld information (see + //! snapshot/mac/process_types/dyld_images.proctype). + //! \param[in] name The module’s name, a string to be used in logged messages. + //! This string is for diagnostic purposes and to relax otherwise strict + //! parsing rules for common modules with known defects. + //! + //! \return `true` if the image was read successfully, including all load + //! commands. `false` otherwise, with an appropriate message logged. + bool Initialize(ProcessReaderMac* process_reader, + mach_vm_address_t address, + const std::string& name); + + //! \brief Returns the Mach-O file type. + //! + //! This value comes from the `filetype` field of the `mach_header` or + //! `mach_header_64`. Common values include `MH_EXECUTE`, `MH_DYLIB`, + //! `MH_DYLINKER`, and `MH_BUNDLE`. + uint32_t FileType() const { return file_type_; } + + //! \brief Returns the Mach-O image’s load address. + //! + //! This is the value passed as \a address to Initialize(). + mach_vm_address_t Address() const { return address_; } + + //! \brief Returns the mapped size of the Mach-O image’s `__TEXT` segment. + //! + //! Note that this is returns only the size of the `__TEXT` segment, not of + //! any other segment. This is because the interface only allows one load + //! address and size to be reported, but Mach-O image files may consist of + //! multiple discontiguous segments. By convention, the `__TEXT` segment is + //! always mapped at the beginning of a Mach-O image file, and it is the most + //! useful for the expected intended purpose of collecting data to obtain + //! stack backtraces. The implementation insists during initialization that + //! the `__TEXT` segment be mapped at the beginning of the file. + //! + //! In practice, discontiguous segments are only found for images that have + //! loaded out of the dyld shared cache, but the `__TEXT` segment’s size is + //! returned for modules that loaded with contiguous segments as well for + //! consistency. + mach_vm_size_t Size() const { return size_; } + + //! \brief Returns the Mach-O image’s “slide,†the difference between its + //! actual load address and its preferred load address. + //! + //! “Slide†is computed by subtracting the `__TEXT` segment’s preferred load + //! address from its actual load address. It will be reported as a positive + //! offset when the actual load address is greater than the preferred load + //! address. The preferred load address is taken to be the segment’s reported + //! `vmaddr` value. + mach_vm_size_t Slide() const { return slide_; } + + //! \brief Obtain segment information by segment name. + //! + //! \param[in] segment_name The name of the segment to search for, for + //! example, `"__TEXT"`. + //! + //! \return A pointer to the segment information if it was found, or `nullptr` + //! if it was not found. The caller does not take ownership; the lifetime + //! of the returned object is scoped to the lifetime of this + //! MachOImageReader object. + const MachOImageSegmentReader* GetSegmentByName( + const std::string& segment_name) const; + + //! \brief Obtain section information by segment and section name. + //! + //! \param[in] segment_name The name of the segment to search for, for + //! example, `"__TEXT"`. + //! \param[in] section_name The name of the section within the segment to + //! search for, for example, `"__text"`. + //! \param[out] address The actual address that the section was loaded at in + //! memory, taking any “slide†into account if the section did not load at + //! its preferred address as stored in the Mach-O image file. This + //! parameter can be `nullptr`. + //! + //! \return A pointer to the section information if it was found, or `nullptr` + //! if it was not found. The caller does not take ownership; the lifetime + //! of the returned object is scoped to the lifetime of this + //! MachOImageReader object. + //! + //! No parameter is provided for the section’s size, because it can be + //! obtained from the returned process_types::section::size field. + //! + //! \note The process_types::section::addr field gives the section’s preferred + //! load address as stored in the Mach-O image file, and is not adjusted + //! for any “slide†that may have occurred when the image was loaded. Use + //! \a address to obtain the section’s actual load address. + const process_types::section* GetSectionByName( + const std::string& segment_name, + const std::string& section_name, + mach_vm_address_t* address) const; + + //! \brief Obtain section information by section index. + //! + //! \param[in] index The index of the section to return, in the order that it + //! appears in the segment load commands. This is a 1-based index, + //! matching the section number values used for `nlist::n_sect`. + //! \param[out] containing_segment The segment that contains the section. + //! This parameter can be `nullptr`. The caller does not take ownership; + //! the lifetime of the returned object is scoped to the lifetime of this + //! MachOImageReader object. + //! \param[out] address The actual address that the section was loaded at in + //! memory, taking any “slide†into account if the section did not load at + //! its preferred address as stored in the Mach-O image file. This + //! parameter can be `nullptr`. + //! + //! \return A pointer to the section information. If \a index is out of range, + //! logs a warning and returns `nullptr`. The caller does not take + //! ownership; the lifetime of the returned object is scoped to the + //! lifetime of this MachOImageReader object. + //! + //! No parameter is provided for the section’s size, because it can be + //! obtained from the returned process_types::section::size field. + //! + //! \note The process_types::section::addr field gives the section’s preferred + //! load address as stored in the Mach-O image file, and is not adjusted + //! for any “slide†that may have occurred when the image was loaded. Use + //! \a address to obtain the section’s actual load address. + //! \note Unlike MachOImageSegmentReader::GetSectionAtIndex(), this method + //! accepts out-of-range values for \a index, and returns `nullptr` + //! instead of aborting execution upon encountering an out-of-range value. + //! This is because a Mach-O image file’s symbol table refers to this + //! per-module section index, and an out-of-range index in that case + //! should be treated as a data error (where the data is beyond this + //! code’s control) and handled non-fatally by reporting the error to the + //! caller. + const process_types::section* GetSectionAtIndex( + size_t index, + const MachOImageSegmentReader** containing_segment, + mach_vm_address_t* address) const; + + //! \brief Looks up a symbol in the image’s symbol table. + //! + //! This method is capable of locating external defined symbols. Specifically, + //! this method can look up symbols that have these charcteristics: + //! - `N_STAB` (debugging) and `N_PEXT` (private external) must not be set. + //! - `N_EXT` (external) must be set. + //! - The type must be `N_ABS` (absolute) or `N_SECT` (defined in section). + //! + //! `N_INDR` (indirect), `N_UNDF` (undefined), and `N_PBUD` (prebound + //! undefined) symbols cannot be located through this mechanism. + //! + //! \param[in] name The name of the symbol to look up, “mangled†or + //! “decorated†appropriately. For example, use `"_main"` to look up the + //! symbol for the C `main()` function, and use `"__Z4Funcv"` to look up + //! the symbol for the C++ `Func()` function. Contrary to `dlsym()`, the + //! leading underscore must not be stripped when using this interface. + //! \param[out] value If the lookup was successful, this will be set to the + //! value of the symbol, adjusted for any “slideâ€Â as needed. The value can + //! be used as an address in the remote process’ address space where the + //! pointee of the symbol exists in memory. + //! + //! \return `true` if the symbol lookup was successful and the symbol was + //! found. `false` otherwise, including error conditions (for which a + //! warning message will be logged), modules without symbol tables, and + //! symbol names not found in the symbol table. + //! + //! \note Symbol values returned via this interface are adjusted for “slide†+ //! as appropriate, in contrast to the underlying implementation, + //! MachOImageSymbolTableReader::LookUpExternalDefinedSymbol(). + //! + //! \warning Symbols that are resolved by running symbol resolvers + //! (`.symbol_resolver`) are not properly handled by this interface. The + //! address of the symbol resolver is returned because that’s what shows + //! up in the symbol table, rather than the effective address of the + //! resolved symbol as used by dyld after running the resolver. The only + //! way to detect this situation would be to read the `LC_DYLD_INFO` or + //! `LC_DYLD_INFO_ONLY` load command if present and looking for the + //! `EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER` flag, but that would just be + //! able to detect symbols with a resolver, it would not be able to + //! resolve them from out-of-process, so it’s not currently done. + bool LookUpExternalDefinedSymbol(const std::string& name, + mach_vm_address_t* value) const; + + //! \brief Returns a Mach-O dylib image’s current version. + //! + //! This information comes from the `dylib_current_version` field of a dylib’s + //! `LC_ID_DYLIB` load command. For dylibs without this load command, `0` will + //! be returned. + //! + //! This method may only be called on Mach-O images for which FileType() + //! returns `MH_DYLIB`. + uint32_t DylibVersion() const; + + //! \brief Returns a Mach-O image’s source version. + //! + //! This information comes from a Mach-O image’s `LC_SOURCE_VERSION` load + //! command. For Mach-O images without this load command, `0` will be + //! returned. + uint64_t SourceVersion() const { return source_version_; } + + //! \brief Returns a Mach-O image’s UUID. + //! + //! This information comes from a Mach-O image’s `LC_UUID` load command. For + //! Mach-O images without this load command, a zeroed-out UUID value will be + //! returned. + // + // UUID is a name in this scope (referring to this method), so the parameter’s + // type needs to be qualified with |crashpad::|. + void UUID(crashpad::UUID* uuid) const; + + //! \brief Returns the dynamic linker’s pathname. + //! + //! The dynamic linker is normally /usr/lib/dyld. + //! + //! For executable images (those with file type `MH_EXECUTE`), this is the + //! name provided in the `LC_LOAD_DYLINKER` load command, if any. For dynamic + //! linker images (those with file type `MH_DYLINKER`), this is the name + //! provided in the `LC_ID_DYLINKER` load command. In other cases, this will + //! be empty. + std::string DylinkerName() const { return dylinker_name_; } + + //! \brief Obtains the module’s CrashpadInfo structure. + //! + //! \return `true` on success, `false` on failure. If the module does not have + //! a `__DATA,crashpad_info` section, this will return `false` without + //! logging any messages. Other failures will result in messages being + //! logged. + bool GetCrashpadInfo(process_types::CrashpadInfo* crashpad_info) const; + + private: + // A generic helper routine for the other Read*Command() methods. + template + bool ReadLoadCommand(mach_vm_address_t load_command_address, + const std::string& load_command_info, + uint32_t expected_load_command_id, + T* load_command); + + // The Read*Command() methods are subroutines called by Initialize(). They are + // responsible for reading a single load command. They may update the member + // fields of their MachOImageReader object. If they can’t make sense of a load + // command, they return false. + bool ReadSegmentCommand(mach_vm_address_t load_command_address, + const std::string& load_command_info); + bool ReadSymTabCommand(mach_vm_address_t load_command_address, + const std::string& load_command_info); + bool ReadDySymTabCommand(mach_vm_address_t load_command_address, + const std::string& load_command_info); + bool ReadIdDylibCommand(mach_vm_address_t load_command_address, + const std::string& load_command_info); + bool ReadDylinkerCommand(mach_vm_address_t load_command_address, + const std::string& load_command_info); + bool ReadUUIDCommand(mach_vm_address_t load_command_address, + const std::string& load_command_info); + bool ReadSourceVersionCommand(mach_vm_address_t load_command_address, + const std::string& load_command_info); + bool ReadUnexpectedCommand(mach_vm_address_t load_command_address, + const std::string& load_command_info); + + // Performs deferred initialization of the symbol table. Because a module’s + // symbol table is often not needed, this is not handled in Initialize(), but + // is done lazily, on-demand as needed. + // + // symbol_table_initialized_ will be transitioned to the appropriate state. If + // initialization completes successfully, this will be the valid state. + // Otherwise, it will be left in the invalid state and a warning message will + // be logged. + // + // Note that if the object contains no symbol table, symbol_table_initialized_ + // will be set to the valid state, but symbol_table_ will be nullptr. + void InitializeSymbolTable() const; + + std::vector> segments_; + std::map segment_map_; + std::string module_name_; + std::string module_info_; + std::string dylinker_name_; + crashpad::UUID uuid_; + mach_vm_address_t address_; + mach_vm_size_t size_; + mach_vm_size_t slide_; + uint64_t source_version_; + std::unique_ptr symtab_command_; + std::unique_ptr dysymtab_command_; + + // symbol_table_ (and symbol_table_initialized_) are mutable in order to + // maintain LookUpExternalDefinedSymbol() as a const interface while allowing + // lazy initialization via InitializeSymbolTable(). This is logical + // const-ness, not physical const-ness. + mutable std::unique_ptr symbol_table_; + + std::unique_ptr id_dylib_command_; + ProcessReaderMac* process_reader_; // weak + uint32_t file_type_; + InitializationStateDcheck initialized_; + + // symbol_table_initialized_ protects symbol_table_: symbol_table_ can only + // be used when symbol_table_initialized_ is valid, although + // symbol_table_initialized_ being valid doesn’t imply that symbol_table_ is + // set. symbol_table_initialized_ will be valid without symbol_table_ being + // set in modules that have no symbol table. + mutable InitializationState symbol_table_initialized_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_READER_H_ diff --git a/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_reader_test.cc b/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_reader_test.cc new file mode 100644 index 000000000..f1f3b1de8 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_reader_test.cc @@ -0,0 +1,649 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/mac/mach_o_image_reader.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "base/strings/stringprintf.h" +#include "build/build_config.h" +#include "client/crashpad_info.h" +#include "gtest/gtest.h" +#include "snapshot/mac/mach_o_image_segment_reader.h" +#include "snapshot/mac/process_reader_mac.h" +#include "snapshot/mac/process_types.h" +#include "test/mac/dyld.h" +#include "util/misc/from_pointer_cast.h" +#include "util/misc/implicit_cast.h" +#include "util/misc/uuid.h" + +// This file is responsible for testing MachOImageReader, +// MachOImageSegmentReader, and MachOImageSymbolTableReader. + +namespace crashpad { +namespace test { +namespace { + +// Native types and constants, in cases where the 32-bit and 64-bit versions +// are different. +#if defined(ARCH_CPU_64_BITS) +using MachHeader = mach_header_64; +constexpr uint32_t kMachMagic = MH_MAGIC_64; +using SegmentCommand = segment_command_64; +constexpr uint32_t kSegmentCommand = LC_SEGMENT_64; +using Section = section_64; +using Nlist = nlist_64; +#else +using MachHeader = mach_header; +constexpr uint32_t kMachMagic = MH_MAGIC; +using SegmentCommand = segment_command; +constexpr uint32_t kSegmentCommand = LC_SEGMENT; +using Section = section; + +// This needs to be called “struct nlist†because “nlist†without the struct +// refers to the nlist() function. +using Nlist = struct nlist; +#endif + +#if defined(ARCH_CPU_X86_64) +constexpr int kCPUType = CPU_TYPE_X86_64; +#elif defined(ARCH_CPU_X86) +constexpr int kCPUType = CPU_TYPE_X86; +#elif defined(ARCH_CPU_ARM64) +constexpr int kCPUType = CPU_TYPE_ARM64; +#endif + +// Verifies that |expect_section| and |actual_section| agree. +void ExpectSection(const Section* expect_section, + const process_types::section* actual_section) { + ASSERT_TRUE(expect_section); + ASSERT_TRUE(actual_section); + + EXPECT_EQ( + MachOImageSegmentReader::SectionNameString(actual_section->sectname), + MachOImageSegmentReader::SectionNameString(expect_section->sectname)); + EXPECT_EQ( + MachOImageSegmentReader::SegmentNameString(actual_section->segname), + MachOImageSegmentReader::SegmentNameString(expect_section->segname)); + EXPECT_EQ(actual_section->addr, expect_section->addr); + EXPECT_EQ(actual_section->size, expect_section->size); + EXPECT_EQ(actual_section->offset, expect_section->offset); + EXPECT_EQ(actual_section->align, expect_section->align); + EXPECT_EQ(actual_section->reloff, expect_section->reloff); + EXPECT_EQ(actual_section->nreloc, expect_section->nreloc); + EXPECT_EQ(actual_section->flags, expect_section->flags); + EXPECT_EQ(actual_section->reserved1, expect_section->reserved1); + EXPECT_EQ(actual_section->reserved2, expect_section->reserved2); +} + +// Verifies that |expect_segment| is a valid Mach-O segment load command for the +// current system by checking its |cmd| field. Then, verifies that the +// information in |actual_segment| matches that in |expect_segment|. The +// |segname|, |vmaddr|, |vmsize|, and |fileoff| fields are examined. Each +// section within the segment is also examined by calling ExpectSection(). +// Access to each section via both MachOImageSegmentReader::GetSectionByName() +// and MachOImageReader::GetSectionByName() is verified, expecting that each +// call produces the same section. Segment and section data addresses are +// verified against data obtained by calling getsegmentdata() and +// getsectiondata(). The segment is checked to make sure that it behaves +// correctly when attempting to look up a nonexistent section by name. +// |section_index| is used to track the last-used section index in an image on +// entry, and is reset to the last-used section index on return after the +// sections are processed. This is used to test that +// MachOImageReader::GetSectionAtIndex() returns the correct result. +void ExpectSegmentCommand(const SegmentCommand* expect_segment, + const MachHeader* expect_image, + const MachOImageSegmentReader* actual_segment, + const MachOImageReader* actual_image, + size_t* section_index) { + ASSERT_TRUE(expect_segment); + ASSERT_TRUE(actual_segment); + + EXPECT_EQ(expect_segment->cmd, kSegmentCommand); + + std::string segment_name = actual_segment->Name(); + EXPECT_EQ( + segment_name, + MachOImageSegmentReader::SegmentNameString(expect_segment->segname)); + EXPECT_EQ(actual_segment->vmaddr(), expect_segment->vmaddr); + EXPECT_EQ(actual_segment->vmsize(), expect_segment->vmsize); + EXPECT_EQ(actual_segment->fileoff(), expect_segment->fileoff); + + if (actual_segment->SegmentSlides()) { + EXPECT_EQ(actual_segment->vmaddr() + actual_image->Slide(), + actual_segment->Address()); + + unsigned long expect_segment_size; + const uint8_t* expect_segment_data = getsegmentdata( + expect_image, segment_name.c_str(), &expect_segment_size); + mach_vm_address_t expect_segment_address = + FromPointerCast(expect_segment_data); + EXPECT_EQ(actual_segment->Address(), expect_segment_address); + EXPECT_EQ(actual_segment->vmsize(), expect_segment_size); + EXPECT_EQ(actual_segment->Size(), actual_segment->vmsize()); + } else { + // getsegmentdata() doesn’t return appropriate data for the __PAGEZERO + // segment because getsegmentdata() always adjusts for slide, but the + // __PAGEZERO segment never slides, it just grows. Skip the getsegmentdata() + // check for that segment according to the same rules that the kernel uses + // to identify __PAGEZERO. See 10.9.4 xnu-2422.110.17/bsd/kern/mach_loader.c + // load_segment(). + EXPECT_EQ(actual_segment->vmaddr(), actual_segment->Address()); + EXPECT_EQ(actual_segment->Size(), + actual_segment->vmsize() + actual_image->Slide()); + } + + ASSERT_EQ(actual_segment->nsects(), expect_segment->nsects); + + // Make sure that the expected load command is big enough for the number of + // sections that it claims to have, and set up a pointer to its first section + // structure. + ASSERT_EQ(expect_segment->cmdsize, + sizeof(*expect_segment) + expect_segment->nsects * sizeof(Section)); + const Section* expect_sections = + reinterpret_cast(&expect_segment[1]); + + for (size_t index = 0; index < actual_segment->nsects(); ++index) { + const Section* expect_section = &expect_sections[index]; + const process_types::section* actual_section = + actual_segment->GetSectionAtIndex(index, nullptr); + ASSERT_NO_FATAL_FAILURE( + ExpectSection(&expect_sections[index], actual_section)); + + // Make sure that the section is accessible by GetSectionByName as well. + std::string section_name = + MachOImageSegmentReader::SectionNameString(expect_section->sectname); + const process_types::section* actual_section_by_name = + actual_segment->GetSectionByName(section_name, nullptr); + EXPECT_EQ(actual_section_by_name, actual_section); + + // Make sure that the section is accessible by the parent MachOImageReader’s + // GetSectionByName. + mach_vm_address_t actual_section_address; + const process_types::section* actual_section_from_image_by_name = + actual_image->GetSectionByName( + segment_name, section_name, &actual_section_address); + EXPECT_EQ(actual_section_from_image_by_name, actual_section); + + if (actual_segment->SegmentSlides()) { + EXPECT_EQ(actual_section->addr + actual_image->Slide(), + actual_section_address); + + unsigned long expect_section_size; + const uint8_t* expect_section_data = getsectiondata(expect_image, + segment_name.c_str(), + section_name.c_str(), + &expect_section_size); + mach_vm_address_t expect_section_address = + FromPointerCast(expect_section_data); + EXPECT_EQ(actual_section_address, expect_section_address); + EXPECT_EQ(actual_section->size, expect_section_size); + } else { + EXPECT_EQ(actual_section->addr, actual_section_address); + } + + // Test the parent MachOImageReader’s GetSectionAtIndex as well. + const MachOImageSegmentReader* containing_segment; + mach_vm_address_t actual_section_address_at_index; + const process_types::section* actual_section_from_image_at_index = + actual_image->GetSectionAtIndex(++(*section_index), + &containing_segment, + &actual_section_address_at_index); + EXPECT_EQ(actual_section_from_image_at_index, actual_section); + EXPECT_EQ(containing_segment, actual_segment); + EXPECT_EQ(actual_section_address_at_index, actual_section_address); + } + + EXPECT_EQ(actual_segment->GetSectionByName("NoSuchSection", nullptr), + nullptr); +} + +// Walks through the load commands of |expect_image|, finding all of the +// expected segment commands. For each expected segment command, calls +// actual_image->GetSegmentByName() to obtain an actual segment command, and +// calls ExpectSegmentCommand() to compare the expected and actual segments. A +// series of by-name lookups is also performed on the segment to ensure that it +// behaves correctly when attempting to look up segment and section names that +// are not present. |test_section_indices| should be true to test +// MachOImageReader::GetSectionAtIndex() using out-of-range section indices. +// This should be tested for at least one module, but it’s very noisy in terms +// of logging output, so this knob is provided to suppress this portion of the +// test when looping over all modules. +void ExpectSegmentCommands(const MachHeader* expect_image, + const MachOImageReader* actual_image, + bool test_section_index_bounds) { + ASSERT_TRUE(expect_image); + ASSERT_TRUE(actual_image); + + // &expect_image[1] points right past the end of the mach_header[_64], to the + // start of the load commands. + const char* commands_base = reinterpret_cast(&expect_image[1]); + uint32_t position = 0; + size_t section_index = 0; + for (uint32_t index = 0; index < expect_image->ncmds; ++index) { + ASSERT_LT(position, expect_image->sizeofcmds); + const load_command* command = + reinterpret_cast(&commands_base[position]); + ASSERT_LE(position + command->cmdsize, expect_image->sizeofcmds); + if (command->cmd == kSegmentCommand) { + ASSERT_GE(command->cmdsize, sizeof(SegmentCommand)); + const SegmentCommand* expect_segment = + reinterpret_cast(command); + std::string segment_name = + MachOImageSegmentReader::SegmentNameString(expect_segment->segname); + const MachOImageSegmentReader* actual_segment = + actual_image->GetSegmentByName(segment_name); + ASSERT_NO_FATAL_FAILURE(ExpectSegmentCommand(expect_segment, + expect_image, + actual_segment, + actual_image, + §ion_index)); + } + position += command->cmdsize; + } + EXPECT_EQ(position, expect_image->sizeofcmds); + + if (test_section_index_bounds) { + // GetSectionAtIndex uses a 1-based index. Make sure that the range is + // correct. + EXPECT_EQ(actual_image->GetSectionAtIndex(0, nullptr, nullptr), nullptr); + EXPECT_EQ( + actual_image->GetSectionAtIndex(section_index + 1, nullptr, nullptr), + nullptr); + } + + // Make sure that by-name lookups for names that don’t exist work properly: + // they should return nullptr. + EXPECT_FALSE(actual_image->GetSegmentByName("NoSuchSegment")); + EXPECT_FALSE(actual_image->GetSectionByName( + "NoSuchSegment", "NoSuchSection", nullptr)); + + // Make sure that there’s a __TEXT segment so that this can do a valid test of + // a section that doesn’t exist within a segment that does. + EXPECT_TRUE(actual_image->GetSegmentByName(SEG_TEXT)); + EXPECT_FALSE( + actual_image->GetSectionByName(SEG_TEXT, "NoSuchSection", nullptr)); + + // Similarly, make sure that a section name that exists in one segment isn’t + // accidentally found during a lookup for that section in a different segment. + // + // If the image has no sections (unexpected), then any section lookup should + // fail, and these initial values of test_segment and test_section are fine + // for the EXPECT_FALSE checks on GetSectionByName() below. + std::string test_segment = SEG_DATA; + std::string test_section = SECT_TEXT; + + const process_types::section* section = + actual_image->GetSectionAtIndex(1, nullptr, nullptr); + if (section) { + // Use the name of the first section in the image as the section that + // shouldn’t appear in a different segment. If the first section is in the + // __TEXT segment (as it is normally), then a section by the same name + // wouldn’t be expected in the __DATA segment. But if the first section is + // in any other segment, then it wouldn’t be expected in the __TEXT segment. + if (MachOImageSegmentReader::SegmentNameString(section->segname) == + SEG_TEXT) { + test_segment = SEG_DATA; + } else { + test_segment = SEG_TEXT; + } + test_section = + MachOImageSegmentReader::SectionNameString(section->sectname); + + // It should be possible to look up the first section by name. + EXPECT_EQ(actual_image->GetSectionByName( + section->segname, section->sectname, nullptr), + section); + } + EXPECT_FALSE( + actual_image->GetSectionByName("NoSuchSegment", test_section, nullptr)); + EXPECT_FALSE( + actual_image->GetSectionByName(test_segment, test_section, nullptr)); + + // The __LINKEDIT segment normally does exist but doesn’t have any sections. + EXPECT_FALSE( + actual_image->GetSectionByName(SEG_LINKEDIT, "NoSuchSection", nullptr)); + EXPECT_FALSE( + actual_image->GetSectionByName(SEG_LINKEDIT, SECT_TEXT, nullptr)); +} + +// In some cases, the expected slide value for an image is unknown, because no +// reasonable API to return it is provided. When this happens, use kSlideUnknown +// to avoid checking the actual slide value against anything. +constexpr mach_vm_size_t kSlideUnknown = + std::numeric_limits::max(); + +// Verifies that |expect_image| is a vaild Mach-O header for the current system +// by checking its |magic| and |cputype| fields. Then, verifies that the +// information in |actual_image| matches that in |expect_image|. The |filetype| +// field is examined, actual_image->Address() is compared to +// |expect_image_address|, and actual_image->Slide() is compared to +// |expect_image_slide|, unless |expect_image_slide| is kSlideUnknown. Various +// other attributes of |actual_image| are sanity-checked depending on the Mach-O +// file type. Finally, ExpectSegmentCommands() is called to verify all that all +// of the segments match; |test_section_index_bounds| is used as an argument to +// that function. +void ExpectMachImage(const MachHeader* expect_image, + mach_vm_address_t expect_image_address, + mach_vm_size_t expect_image_slide, + const MachOImageReader* actual_image, + bool test_section_index_bounds) { + ASSERT_TRUE(expect_image); + ASSERT_TRUE(actual_image); + + EXPECT_EQ(expect_image->magic, kMachMagic); + EXPECT_EQ(expect_image->cputype, kCPUType); + + EXPECT_EQ(actual_image->FileType(), expect_image->filetype); + EXPECT_EQ(actual_image->Address(), expect_image_address); + if (expect_image_slide != kSlideUnknown) { + EXPECT_EQ(actual_image->Slide(), expect_image_slide); + } + + const MachOImageSegmentReader* actual_text_segment = + actual_image->GetSegmentByName(SEG_TEXT); + ASSERT_TRUE(actual_text_segment); + EXPECT_EQ(actual_text_segment->Address(), expect_image_address); + EXPECT_EQ(actual_text_segment->Size(), actual_image->Size()); + EXPECT_EQ(actual_image->Slide(), + expect_image_address - actual_text_segment->vmaddr()); + + uint32_t file_type = actual_image->FileType(); + EXPECT_TRUE(file_type == MH_EXECUTE || file_type == MH_DYLIB || + file_type == MH_DYLINKER || file_type == MH_BUNDLE); + + if (file_type == MH_EXECUTE || file_type == MH_DYLINKER) { + EXPECT_EQ(actual_image->DylinkerName(), "/usr/lib/dyld"); + } + + // For these, just don’t crash or anything. + if (file_type == MH_DYLIB) { + actual_image->DylibVersion(); + } + actual_image->SourceVersion(); + UUID uuid; + actual_image->UUID(&uuid); + + ASSERT_NO_FATAL_FAILURE(ExpectSegmentCommands( + expect_image, actual_image, test_section_index_bounds)); +} + +// Verifies the symbol whose Nlist structure is |entry| and whose name is |name| +// matches the value of a symbol by the same name looked up in |actual_image|. +// MachOImageReader::LookUpExternalDefinedSymbol() is used for this purpose. +// Only external defined symbols are considered, other types of symbols are +// excluded because LookUpExternalDefinedSymbol() only deals with external +// defined symbols. +void ExpectSymbol(const Nlist* entry, + const char* name, + const MachOImageReader* actual_image) { + SCOPED_TRACE(name); + + uint32_t entry_type = entry->n_type & N_TYPE; + if ((entry->n_type & N_STAB) == 0 && (entry->n_type & N_PEXT) == 0 && + (entry_type == N_ABS || entry_type == N_SECT) && + (entry->n_type & N_EXT) == 1) { + mach_vm_address_t actual_address; + ASSERT_TRUE( + actual_image->LookUpExternalDefinedSymbol(name, &actual_address)); + + // Since the nlist interface was used to read the symbol, use it to compute + // the symbol address too. This isn’t perfect, and it should be possible in + // theory to use dlsym() to get the expected address of a symbol. In + // practice, dlsym() is difficult to use when only a MachHeader* is + // available as in this function, as opposed to a void* opaque handle. It is + // possible to get a void* handle by using dladdr() to find the file name + // corresponding to the MachHeader*, and using dlopen() again on that name, + // assuming it hasn’t changed on disk since being loaded. However, even with + // that being done, dlsym() can only deal with symbols whose names begin + // with an underscore (and requires that the leading underscore be trimmed). + // dlsym() will also return different addresses for symbols that are + // resolved via symbol resolver. + mach_vm_address_t expect_address = entry->n_value; + if (entry_type == N_SECT) { + EXPECT_GE(entry->n_sect, 1u); + expect_address += actual_image->Slide(); + } else { + EXPECT_EQ(entry->n_sect, NO_SECT); + } + + EXPECT_EQ(actual_address, expect_address); + } + + // You’d think that it might be a good idea to verify that if the conditions + // above weren’t met, that the symbol didn’t show up in actual_image’s symbol + // table at all. Unfortunately, it’s possible for the same name to show up as + // both an external defined symbol and as something else, so it’s not possible + // to verify this reliably. +} + +// Locates the symbol table in |expect_image| and verifies that all of the +// external defined symbols found there are also present and have the same +// values in |actual_image|. ExpectSymbol() is used to verify the actual symbol. +void ExpectSymbolTable(const MachHeader* expect_image, + const MachOImageReader* actual_image) { + // This intentionally consults only LC_SYMTAB and not LC_DYSYMTAB so that it + // can look at the larger set of all symbols. The actual implementation being + // tested is free to consult LC_DYSYMTAB, but that’s considered an + // optimization. It’s not necessary for the test, and it’s better for the test + // to expose bugs in that optimization rather than duplicate them. + const char* commands_base = reinterpret_cast(&expect_image[1]); + uint32_t position = 0; + const symtab_command* symtab = nullptr; + const SegmentCommand* linkedit = nullptr; + for (uint32_t index = 0; index < expect_image->ncmds; ++index) { + ASSERT_LT(position, expect_image->sizeofcmds); + const load_command* command = + reinterpret_cast(&commands_base[position]); + ASSERT_LE(position + command->cmdsize, expect_image->sizeofcmds); + if (command->cmd == LC_SYMTAB) { + ASSERT_FALSE(symtab); + ASSERT_EQ(command->cmdsize, sizeof(symtab_command)); + symtab = reinterpret_cast(command); + } else if (command->cmd == kSegmentCommand) { + ASSERT_GE(command->cmdsize, sizeof(SegmentCommand)); + const SegmentCommand* segment = + reinterpret_cast(command); + std::string segment_name = + MachOImageSegmentReader::SegmentNameString(segment->segname); + if (segment_name == SEG_LINKEDIT) { + ASSERT_FALSE(linkedit); + linkedit = segment; + } + } + position += command->cmdsize; + } + + if (symtab) { + ASSERT_TRUE(linkedit); + + const char* linkedit_base = + reinterpret_cast(linkedit->vmaddr + actual_image->Slide()); + const Nlist* nlist = reinterpret_cast( + linkedit_base + symtab->symoff - linkedit->fileoff); + const char* strtab = linkedit_base + symtab->stroff - linkedit->fileoff; + + for (uint32_t index = 0; index < symtab->nsyms; ++index) { + const Nlist* entry = nlist + index; + const char* name = strtab + entry->n_un.n_strx; + ASSERT_NO_FATAL_FAILURE(ExpectSymbol(entry, name, actual_image)); + } + } + + mach_vm_address_t ignore; + EXPECT_FALSE(actual_image->LookUpExternalDefinedSymbol("", &ignore)); + EXPECT_FALSE( + actual_image->LookUpExternalDefinedSymbol("NoSuchSymbolName", &ignore)); + EXPECT_FALSE( + actual_image->LookUpExternalDefinedSymbol("_NoSuchSymbolName", &ignore)); +} + +TEST(MachOImageReader, Self_MainExecutable) { + ProcessReaderMac process_reader; + ASSERT_TRUE(process_reader.Initialize(mach_task_self())); + + const MachHeader* mh_execute_header = + reinterpret_cast(dlsym(RTLD_MAIN_ONLY, MH_EXECUTE_SYM)); + ASSERT_NE(mh_execute_header, nullptr); + mach_vm_address_t mh_execute_header_address = + FromPointerCast(mh_execute_header); + + MachOImageReader image_reader; + ASSERT_TRUE(image_reader.Initialize( + &process_reader, mh_execute_header_address, "executable")); + + EXPECT_EQ(image_reader.FileType(), implicit_cast(MH_EXECUTE)); + + // The main executable has image index 0. + intptr_t image_slide = _dyld_get_image_vmaddr_slide(0); + + ASSERT_NO_FATAL_FAILURE(ExpectMachImage(mh_execute_header, + mh_execute_header_address, + image_slide, + &image_reader, + true)); + + // This symbol, __mh_execute_header, is known to exist in all MH_EXECUTE + // Mach-O files. + mach_vm_address_t symbol_address; + ASSERT_TRUE(image_reader.LookUpExternalDefinedSymbol(_MH_EXECUTE_SYM, + &symbol_address)); + EXPECT_EQ(symbol_address, mh_execute_header_address); + + ASSERT_NO_FATAL_FAILURE(ExpectSymbolTable(mh_execute_header, &image_reader)); +} + +TEST(MachOImageReader, Self_DyldImages) { + ProcessReaderMac process_reader; + ASSERT_TRUE(process_reader.Initialize(mach_task_self())); + + uint32_t count = _dyld_image_count(); + ASSERT_GE(count, 1u); + + size_t modules_with_crashpad_info = 0; + + for (uint32_t index = 0; index < count; ++index) { + const char* image_name = _dyld_get_image_name(index); + SCOPED_TRACE(base::StringPrintf("index %u, image %s", index, image_name)); + + // _dyld_get_image_header() is poorly-declared: it’s declared as returning + // const mach_header* in both 32-bit and 64-bit environments, but in the + // 64-bit environment, it should be const mach_header_64*. + const MachHeader* mach_header = + reinterpret_cast(_dyld_get_image_header(index)); + mach_vm_address_t image_address = + FromPointerCast(mach_header); + + MachOImageReader image_reader; + ASSERT_TRUE( + image_reader.Initialize(&process_reader, image_address, image_name)); + + uint32_t file_type = image_reader.FileType(); + if (index == 0) { + EXPECT_EQ(file_type, implicit_cast(MH_EXECUTE)); + } else { + EXPECT_TRUE(file_type == MH_DYLIB || file_type == MH_BUNDLE); + } + + intptr_t image_slide = _dyld_get_image_vmaddr_slide(index); + ASSERT_NO_FATAL_FAILURE(ExpectMachImage( + mach_header, image_address, image_slide, &image_reader, false)); + + ASSERT_NO_FATAL_FAILURE(ExpectSymbolTable(mach_header, &image_reader)); + + process_types::CrashpadInfo crashpad_info; + if (image_reader.GetCrashpadInfo(&crashpad_info)) { + ++modules_with_crashpad_info; + } + } + + EXPECT_GE(modules_with_crashpad_info, 1u); + + // Now that all of the modules have been verified, make sure that dyld itself + // can be read properly too. + const dyld_all_image_infos* dyld_image_infos = DyldGetAllImageInfos(); + ASSERT_GE(dyld_image_infos->version, 1u); + EXPECT_EQ(dyld_image_infos->infoArrayCount, count); + + if (dyld_image_infos->version >= 2) { + SCOPED_TRACE("dyld"); + + // dyld_all_image_infos::dyldImageLoadAddress is poorly-declared too. + const MachHeader* mach_header = reinterpret_cast( + dyld_image_infos->dyldImageLoadAddress); + mach_vm_address_t image_address = + FromPointerCast(mach_header); + + MachOImageReader image_reader; + ASSERT_TRUE( + image_reader.Initialize(&process_reader, image_address, "dyld")); + + EXPECT_EQ(image_reader.FileType(), implicit_cast(MH_DYLINKER)); + + // There’s no good API to get dyld’s slide, so don’t bother checking it. + ASSERT_NO_FATAL_FAILURE(ExpectMachImage( + mach_header, image_address, kSlideUnknown, &image_reader, false)); + + ASSERT_NO_FATAL_FAILURE(ExpectSymbolTable(mach_header, &image_reader)); + } + +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_7 + // If dyld is new enough to record UUIDs, check the UUID of any module that + // it says has one. Note that dyld doesn’t record UUIDs of anything that + // loaded out of the shared cache, but it should at least have a UUID for the + // main executable if it has one. + if (dyld_image_infos->version >= 8 && dyld_image_infos->uuidArray) { + for (uint32_t index = 0; + index < dyld_image_infos->uuidArrayCount; + ++index) { + const dyld_uuid_info* dyld_image = &dyld_image_infos->uuidArray[index]; + SCOPED_TRACE(base::StringPrintf("uuid index %u", index)); + + // dyld_uuid_info::imageLoadAddress is poorly-declared too. + const MachHeader* mach_header = + reinterpret_cast(dyld_image->imageLoadAddress); + mach_vm_address_t image_address = + FromPointerCast(mach_header); + + MachOImageReader image_reader; + ASSERT_TRUE( + image_reader.Initialize(&process_reader, image_address, "uuid")); + + // There’s no good way to get the image’s slide here, although the image + // should have already been checked along with its slide above, in the + // loop through all images. + ExpectMachImage( + mach_header, image_address, kSlideUnknown, &image_reader, false); + + UUID expected_uuid; + expected_uuid.InitializeFromBytes(dyld_image->imageUUID); + UUID actual_uuid; + image_reader.UUID(&actual_uuid); + EXPECT_EQ(actual_uuid, expected_uuid); + } + } +#endif +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_segment_reader.cc b/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_segment_reader.cc new file mode 100644 index 000000000..1d7d429f4 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_segment_reader.cc @@ -0,0 +1,310 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/mac/mach_o_image_segment_reader.h" + +#include +#include +#include + +#include + +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "build/build_config.h" +#include "snapshot/mac/process_reader_mac.h" +#include "util/mac/checked_mach_address_range.h" +#include "util/mac/mac_util.h" +#include "util/stdlib/strnlen.h" + +namespace crashpad { + +namespace { + +std::string SizeLimitedCString(const char* c_string, size_t max_length) { + return std::string(c_string, strnlen(c_string, max_length)); +} + +} // namespace + +bool IsMalformedCLKernelsModule(uint32_t mach_o_file_type, + const std::string& module_name, + bool* has_timestamp) { +#if defined(ARCH_CPU_X86_FAMILY) + if (mach_o_file_type != MH_BUNDLE) { + return false; + } + + if (module_name == "cl_kernels") { + if (__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_10 || + MacOSVersionNumber() >= 10'10'00) { + if (has_timestamp) { + *has_timestamp = false; + } + return true; + } + return false; + } + + static const char kCvmsObjectPathPrefix[] = + "/private/var/db/CVMS/cvmsCodeSignObj"; + if (module_name.compare( + 0, strlen(kCvmsObjectPathPrefix), kCvmsObjectPathPrefix) == 0 && + (__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_14 || + MacOSVersionNumber() >= 10'14'00)) { + if (has_timestamp) { + *has_timestamp = true; + } + return true; + } +#endif // ARCH_CPU_X86_FAMILY + + return false; +} + +MachOImageSegmentReader::MachOImageSegmentReader() + : segment_command_(), + sections_(), + section_map_(), + slide_(0), + initialized_(), + initialized_slide_() { +} + +MachOImageSegmentReader::~MachOImageSegmentReader() { +} + +bool MachOImageSegmentReader::Initialize(ProcessReaderMac* process_reader, + mach_vm_address_t load_command_address, + const std::string& load_command_info, + const std::string& module_name, + uint32_t file_type) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + if (!segment_command_.Read(process_reader, load_command_address)) { + LOG(WARNING) << "could not read segment_command" << load_command_info; + return false; + } + + const uint32_t kExpectedSegmentCommand = + process_reader->Is64Bit() ? LC_SEGMENT_64 : LC_SEGMENT; + DCHECK_EQ(segment_command_.cmd, kExpectedSegmentCommand); + DCHECK_GE(segment_command_.cmdsize, segment_command_.Size()); + const size_t kSectionStructSize = + process_types::section::ExpectedSize(process_reader); + const size_t kRequiredSize = + segment_command_.Size() + segment_command_.nsects * kSectionStructSize; + if (segment_command_.cmdsize < kRequiredSize) { + LOG(WARNING) << base::StringPrintf( + "segment command cmdsize 0x%x insufficient for %u " + "section%s (0x%zx)", + segment_command_.cmdsize, + segment_command_.nsects, + segment_command_.nsects == 1 ? "" : "s", + kRequiredSize) << load_command_info; + return false; + } + + std::string segment_name = NameInternal(); + std::string segment_info = base::StringPrintf( + ", segment %s%s", segment_name.c_str(), load_command_info.c_str()); + + // This checks the unslid segment range. The slid range (as loaded into + // memory) will be checked later by MachOImageReader. + CheckedMachAddressRange segment_range(process_reader->Is64Bit(), + segment_command_.vmaddr, + segment_command_.vmsize); + if (!segment_range.IsValid()) { + LOG(WARNING) << base::StringPrintf("invalid segment range 0x%llx + 0x%llx", + segment_command_.vmaddr, + segment_command_.vmsize) << segment_info; + return false; + } + + sections_.resize(segment_command_.nsects); + if (!sections_.empty() && + !process_types::section::ReadArrayInto( + process_reader, + load_command_address + segment_command_.Size(), + segment_command_.nsects, + §ions_[0])) { + LOG(WARNING) << "could not read sections" << segment_info; + return false; + } + + for (size_t section_index = 0; + section_index < sections_.size(); + ++section_index) { + const process_types::section& section = sections_[section_index]; + std::string section_segment_name = SegmentNameString(section.segname); + std::string section_name = SectionNameString(section.sectname); + std::string section_full_name = + SegmentAndSectionNameString(section.segname, section.sectname); + + std::string section_info = base::StringPrintf(", section %s %zu/%zu%s", + section_full_name.c_str(), + section_index, + sections_.size(), + load_command_info.c_str()); + + // cl_kernels modules (for OpenCL) aren’t ld output, and they’re formatted + // incorrectly on OS X 10.10 and later. Because at least one cl_kernels + // module will commonly be found in a process, and sometimes more will be, + // tolerate this quirk. + // + // https://openradar.appspot.com/20239912 + if (section_segment_name != segment_name && + !(IsMalformedCLKernelsModule(file_type, module_name, nullptr) && + segment_name == SEG_TEXT && + section_segment_name == "__LD" && + section_name == "__compact_unwind" && + (section.flags & S_ATTR_DEBUG))) { + LOG(WARNING) << "section.segname incorrect in segment " << segment_name + << section_info; + return false; + } + + CheckedMachAddressRange section_range( + process_reader->Is64Bit(), section.addr, section.size); + if (!section_range.IsValid()) { + LOG(WARNING) << base::StringPrintf( + "invalid section range 0x%llx + 0x%llx", + section.addr, + section.size) << section_info; + return false; + } + + if (!segment_range.ContainsRange(section_range)) { + LOG(WARNING) << base::StringPrintf( + "section at 0x%llx + 0x%llx outside of segment at " + "0x%llx + 0x%llx", + section.addr, + section.size, + segment_command_.vmaddr, + segment_command_.vmsize) << section_info; + return false; + } + + const auto insert_result = + section_map_.insert(std::make_pair(section_name, section_index)); + if (!insert_result.second) { + LOG(WARNING) << base::StringPrintf("duplicate section name at %zu", + insert_result.first->second) + << section_info; + return false; + } + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +std::string MachOImageSegmentReader::Name() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return NameInternal(); +} + +mach_vm_address_t MachOImageSegmentReader::Address() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + INITIALIZATION_STATE_DCHECK_VALID(initialized_slide_); + return vmaddr() + (SegmentSlides() ? slide_ : 0); +} + +mach_vm_size_t MachOImageSegmentReader::Size() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + INITIALIZATION_STATE_DCHECK_VALID(initialized_slide_); + return vmsize() + (SegmentSlides() ? 0 : slide_); +} + +const process_types::section* MachOImageSegmentReader::GetSectionByName( + const std::string& section_name, + mach_vm_address_t* address) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + const auto& iterator = section_map_.find(section_name); + if (iterator == section_map_.end()) { + return nullptr; + } + + return GetSectionAtIndex(iterator->second, address); +} + +const process_types::section* MachOImageSegmentReader::GetSectionAtIndex( + size_t index, + mach_vm_address_t* address) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + CHECK_LT(index, sections_.size()); + + const process_types::section* section = §ions_[index]; + + if (address) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_slide_); + *address = section->addr + (SegmentSlides() ? slide_ : 0); + } + + return section; +} + +bool MachOImageSegmentReader::SegmentSlides() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + // These are the same rules that the kernel uses to identify __PAGEZERO. See + // 10.9.4 xnu-2422.110.17/bsd/kern/mach_loader.c load_segment(). + return !(segment_command_.vmaddr == 0 && segment_command_.filesize == 0 && + segment_command_.vmsize != 0 && + (segment_command_.initprot & VM_PROT_ALL) == VM_PROT_NONE && + (segment_command_.maxprot & VM_PROT_ALL) == VM_PROT_NONE); +} + +// static +std::string MachOImageSegmentReader::SegmentNameString( + const char* segment_name_c) { + // This is used to interpret the segname field of both the segment_command and + // section structures, so be sure that they’re identical. + static_assert(sizeof(process_types::segment_command::segname) == + sizeof(process_types::section::segname), + "sizes must be equal"); + + return SizeLimitedCString(segment_name_c, + sizeof(process_types::segment_command::segname)); +} + +// static +std::string MachOImageSegmentReader::SectionNameString( + const char* section_name_c) { + return SizeLimitedCString(section_name_c, + sizeof(process_types::section::sectname)); +} + +// static +std::string MachOImageSegmentReader::SegmentAndSectionNameString( + const char* segment_name_c, + const char* section_name_c) { + return base::StringPrintf("%s,%s", + SegmentNameString(segment_name_c).c_str(), + SectionNameString(section_name_c).c_str()); +} + +std::string MachOImageSegmentReader::NameInternal() const { + return SegmentNameString(segment_command_.segname); +} + +void MachOImageSegmentReader::SetSlide(mach_vm_size_t slide) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + INITIALIZATION_STATE_SET_INITIALIZING(initialized_slide_); + slide_ = slide; + INITIALIZATION_STATE_SET_VALID(initialized_slide_); +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_segment_reader.h b/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_segment_reader.h new file mode 100644 index 000000000..d639a5d3a --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_segment_reader.h @@ -0,0 +1,301 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_SEGMENT_READER_H_ +#define CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_SEGMENT_READER_H_ + +#include +#include +#include + +#include +#include +#include + +#include "snapshot/mac/process_types.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +//! \brief Determines whether a module appears to be a malformed OpenCL +//! `cl_kernels` module based on its name and Mach-O file type. +//! +//! `cl_kernels` modules require special handling because they’re malformed on +//! OS X 10.10 and later. A `cl_kernels` module always has Mach-O type +//! `MH_BUNDLE` and is named `"cl_kernels"` until macOS 10.14, and +//! `"/private/var/db/CVMS/cvmsCodeSignObj"` plus 16 random characters on macOS +//! 10.14. +//! +//! Malformed `cl_kernels` modules have a single `__TEXT` segment, but one of +//! the sections within it claims to belong to the `__LD` segment. This mismatch +//! shouldn’t happen. This errant section also has the `S_ATTR_DEBUG` flag set, +//! which shouldn’t happen unless all of the other sections in the segment also +//! have this bit set (they don’t). These odd sections are reminiscent of unwind +//! information stored in `MH_OBJECT` images, although `cl_kernels` images claim +//! to be `MH_BUNDLE`. +//! +//! These `cl_kernels` modules have only been observed on x86, not on arm64. +//! This function always returns `false` on arm64. +//! +//! This function is exposed for testing purposes only. +//! +//! \param[in] mach_o_file_type The Mach-O type of the module being examined. +//! \param[in] module_name The pathname that `dyld` reported having loaded the +//! module from. +//! \param[out] has_timestamp Optional, may be `nullptr`. If provided, and the +//! module is a maformed `cl_kernels` module, this will be set to `true` if +//! the module was loaded from the filesystem (as is the case when loaded +//! from the CVMS directory) and is expected to have a timestamp, and +//! `false` otherwise. Note that even when loaded from the filesystem, these +//! modules are unlinked from the filesystem after loading. +//! +//! \return `true` if the module appears to be a malformed `cl_kernels` module +//! based on the provided information, `false` otherwise. +bool IsMalformedCLKernelsModule(uint32_t mach_o_file_type, + const std::string& module_name, + bool* has_timestamp); + +//! \brief A reader for `LC_SEGMENT` or `LC_SEGMENT_64` load commands in Mach-O +//! images mapped into another process. +//! +//! This class is capable of reading both `LC_SEGMENT` and `LC_SEGMENT_64` based +//! on the bitness of the remote process. +//! +//! A MachOImageSegmentReader will normally be instantiated by a +//! MachOImageReader. +class MachOImageSegmentReader { + public: + MachOImageSegmentReader(); + + MachOImageSegmentReader(const MachOImageSegmentReader&) = delete; + MachOImageSegmentReader& operator=(const MachOImageSegmentReader&) = delete; + + ~MachOImageSegmentReader(); + + //! \brief Reads the segment load command from another process. + //! + //! This method must only be called once on an object. This method must be + //! called successfully before any other method in this class may be called. + //! + //! \param[in] process_reader The reader for the remote process. + //! \param[in] load_command_address The address, in the remote process’ + //! address space, where the `LC_SEGMENT` or `LC_SEGMENT_64` load command + //! to be read is located. This address is determined by a Mach-O image + //! reader, such as MachOImageReader, as it walks Mach-O load commands. + //! \param[in] load_command_info A string to be used in logged messages. This + //! string is for diagnostic purposes only, and may be empty. + //! \param[in] module_name The path used to load the module. This string is + //! used to relax otherwise strict parsing rules for common modules with + //! known defects. + //! \param[in] file_type The module’s Mach-O file type. This is used to relax + //! otherwise strict parsing rules for common modules with known defects. + //! + //! \return `true` if the load command was read successfully. `false` + //! otherwise, with an appropriate message logged. + bool Initialize(ProcessReaderMac* process_reader, + mach_vm_address_t load_command_address, + const std::string& load_command_info, + const std::string& module_name, + uint32_t file_type); + + //! \brief Sets the image’s slide value. + //! + //! This method must only be called once on an object, after Initialize() is + //! called successfully. It must be called before Address(), Size(), + //! GetSectionByName(), or GetSectionAtIndex() can be called. + //! + //! This method is provided because slide is a property of the image that + //! cannot be determined until at least some segments have been read. As such, + //! it is not necessarily known at the time that Initialize() is called. + void SetSlide(mach_vm_size_t slide); + + //! \brief Returns the segment’s name. + //! + //! The segment’s name is taken from the load command’s `segname` field. + //! Common segment names are `"__TEXT"`, `"__DATA"`, and `"__LINKEDIT"`. + //! Symbolic constants for these common names are defined in + //! ``. + std::string Name() const; + + //! \return The segment’s actual load address in memory, adjusted for any + //! “slideâ€. + //! + //! \note For the segment’s preferred load address, not adjusted for slide, + //! use vmaddr(). + mach_vm_address_t Address() const; + + //! \return The segment’s actual size address in memory, adjusted for any + //! growth in the case of a nonsliding segment. + //! + //! \note For the segment’s preferred size, not adjusted for growth, use + //! vmsize(). + mach_vm_address_t Size() const; + + //! \brief The segment’s preferred load address. + //! + //! \return The segment’s preferred load address as stored in the Mach-O file. + //! + //! \note This value is not adjusted for any “slide†that may have occurred + //! when the image was loaded. Use Address() for a value adjusted for + //! slide. + //! + //! \sa MachOImageReader::GetSegmentByName() + mach_vm_address_t vmaddr() const { return segment_command_.vmaddr; } + + //! \brief Returns the segment’s size as mapped into memory. + //! + //! \note For non-sliding segments, this value is not adjusted for any growth + //! that may have occurred when the image was loaded. Use Size() for a + //! value adjusted for growth. + mach_vm_size_t vmsize() const { return segment_command_.vmsize; } + + //! \brief Returns the file offset of the mapped segment in the file from + //! which it was mapped. + //! + //! The file offset is the difference between the beginning of the + //! `mach_header` or `mach_header_64` and the beginning of the segment’s + //! mapped region. For segments that are not mapped from a file (such as + //! `__PAGEZERO` segments), this will be `0`. + mach_vm_size_t fileoff() const { return segment_command_.fileoff; } + + //! \brief Returns the number of sections in the segment. + //! + //! This will return `0` for a segment without any sections, typical for + //! `__PAGEZERO` and `__LINKEDIT` segments. + //! + //! Although the Mach-O file format uses a `uint32_t` for this field, there is + //! an overall limit of 255 sections in an entire Mach-O image file (not just + //! in a single segment) imposed by the symbol table format. Symbols will not + //! be able to reference anything in a section beyond the first 255 in a + //! Mach-O image file. + uint32_t nsects() const { return segment_command_.nsects; } + + //! \brief Obtain section information by section name. + //! + //! \param[in] section_name The name of the section to search for, without the + //! leading segment name. For example, use `"__text"`, not + //! `"__TEXT,__text"` or `"__TEXT.__text"`. + //! \param[out] address The actual address that the section was loaded at in + //! memory, taking any “slide†into account if the section did not load at + //! its preferred address as stored in the Mach-O image file. This + //! parameter can be `nullptr`. + //! + //! \return A pointer to the section information if it was found, or `nullptr` + //! if it was not found. The caller does not take ownership; the lifetime + //! of the returned object is scoped to the lifetime of this + //! MachOImageSegmentReader object. + //! + //! \note The process_types::section::addr field gives the section’s preferred + //! load address as stored in the Mach-O image file, and is not adjusted + //! for any “slide†that may have occurred when the image was loaded. + //! + //! \sa MachOImageReader::GetSectionByName() + const process_types::section* GetSectionByName( + const std::string& section_name, + mach_vm_address_t* address) const; + + //! \brief Obtain section information by section index. + //! + //! \param[in] index The index of the section to return, in the order that it + //! appears in the segment load command. Unlike + //! MachOImageReader::GetSectionAtIndex(), this is a 0-based index. This + //! parameter must be in the range of valid indices aas reported by + //! nsects(). + //! \param[out] address The actual address that the section was loaded at in + //! memory, taking any “slide†into account if the section did not load at + //! its preferred address as stored in the Mach-O image file. This + //! parameter can be `nullptr`. + //! + //! \return A pointer to the section information. If \a index is out of range, + //! execution is aborted. The caller does not take ownership; the + //! lifetime of the returned object is scoped to the lifetime of this + //! MachOImageSegmentReader object. + //! + //! \note The process_types::section::addr field gives the section’s preferred + //! load address as stored in the Mach-O image file, and is not adjusted + //! for any “slide†that may have occurred when the image was loaded. + //! \note Unlike MachOImageReader::GetSectionAtIndex(), this method does not + //! accept out-of-range values for \a index, and aborts execution instead + //! of returning `nullptr` upon encountering an out-of-range value. This + //! is because this method is expected to be used in a loop that can be + //! limited to nsects() iterations, so an out-of-range error can be + //! treated more harshly as a logic error, as opposed to a data error. + //! + //! \sa MachOImageReader::GetSectionAtIndex() + const process_types::section* GetSectionAtIndex( + size_t index, + mach_vm_address_t* address) const; + + //! Returns whether the segment slides. + //! + //! Most segments slide, but the `__PAGEZERO` segment does not, it grows + //! instead. This method identifies non-sliding segments in the same way that + //! the kernel does. + bool SegmentSlides() const; + + //! \brief Returns a segment name string. + //! + //! Segment names may be 16 characters long, and are not necessarily + //! `NUL`-terminated. This function will return a segment name based on up to + //! the first 16 characters found at \a segment_name_c. + static std::string SegmentNameString(const char* segment_name_c); + + //! \brief Returns a section name string. + //! + //! Section names may be 16 characters long, and are not necessarily + //! `NUL`-terminated. This function will return a section name based on up to + //! the first 16 characters found at \a section_name_c. + static std::string SectionNameString(const char* section_name_c); + + //! \brief Returns a segment and section name string. + //! + //! A segment and section name string is composed of a segment name string + //! (see SegmentNameString()) and a section name string (see + //! SectionNameString()) separated by a comma. An example is + //! `"__TEXT,__text"`. + static std::string SegmentAndSectionNameString(const char* segment_name_c, + const char* section_name_c); + + private: + //! \brief The internal implementation of Name(). + //! + //! This is identical to Name() but does not perform the + //! InitializationStateDcheck check. It may be called during initialization + //! provided that the caller only does so after segment_command_ has been + //! read successfully. + std::string NameInternal() const; + + // The segment command data read from the remote process. + process_types::segment_command segment_command_; + + // Section structures read from the remote process in the order that they are + // given in the remote process. + std::vector sections_; + + // Maps section names to indices into the sections_ vector. + std::map section_map_; + + // The image’s slide. Note that the segment’s slide may be 0 and not the value + // of the image’s slide if SegmentSlides() is false. In that case, the + // segment is extended instead of slid, so its size as loaded will be + // increased by this value. + mach_vm_size_t slide_; + + InitializationStateDcheck initialized_; + InitializationStateDcheck initialized_slide_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_SEGMENT_READER_H_ diff --git a/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_segment_reader_test.cc b/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_segment_reader_test.cc new file mode 100644 index 000000000..38e55dfa1 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_segment_reader_test.cc @@ -0,0 +1,188 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/mac/mach_o_image_segment_reader.h" + +#include + +#include + +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +// Most of MachOImageSegmentReader is tested as part of MachOImageReader, which +// depends on MachOImageSegmentReader to provide major portions of its +// functionality. Because MachOImageSegmentReader is difficult to use except by +// a Mach-O load command reader such as MachOImageReader, these portions +// of MachOImageSegmentReader are not tested independently. +// +// The tests here exercise the portions of MachOImageSegmentReader that are +// exposed and independently useful. + +TEST(MachOImageSegmentReader, SegmentNameString) { + // The output value should be a string of up to 16 characters, even if the + // input value is not NUL-terminated within 16 characters. + EXPECT_EQ(MachOImageSegmentReader::SegmentNameString("__TEXT"), "__TEXT"); + EXPECT_EQ(MachOImageSegmentReader::SegmentNameString("__OVER"), "__OVER"); + EXPECT_EQ(MachOImageSegmentReader::SegmentNameString(""), ""); + EXPECT_EQ(MachOImageSegmentReader::SegmentNameString("p"), "p"); + EXPECT_EQ(MachOImageSegmentReader::SegmentNameString("NoUnderChar"), + "NoUnderChar"); + EXPECT_EQ(MachOImageSegmentReader::SegmentNameString("0123456789abcde"), + "0123456789abcde"); + EXPECT_EQ(MachOImageSegmentReader::SegmentNameString("0123456789abcdef"), + "0123456789abcdef"); + EXPECT_EQ(MachOImageSegmentReader::SegmentNameString("gfedcba9876543210"), + "gfedcba987654321"); + EXPECT_EQ(MachOImageSegmentReader::SegmentNameString("hgfedcba9876543210"), + "hgfedcba98765432"); + + // Segment names defined in . All of these should come + // through SegmentNameString() cleanly and without truncation. + static constexpr const char* kSegmentTestData[] = { + SEG_TEXT, + SEG_DATA, + SEG_OBJC, + SEG_ICON, + SEG_LINKEDIT, + SEG_UNIXSTACK, + SEG_IMPORT, + }; + + for (size_t index = 0; index < std::size(kSegmentTestData); ++index) { + EXPECT_EQ( + MachOImageSegmentReader::SegmentNameString(kSegmentTestData[index]), + kSegmentTestData[index]) + << base::StringPrintf("index %zu", index); + } +} + +TEST(MachOImageSegmentReader, SectionNameString) { + // The output value should be a string of up to 16 characters, even if the + // input value is not NUL-terminated within 16 characters. + EXPECT_EQ(MachOImageSegmentReader::SectionNameString("__text"), "__text"); + EXPECT_EQ(MachOImageSegmentReader::SectionNameString("__over"), "__over"); + EXPECT_EQ(MachOImageSegmentReader::SectionNameString(""), ""); + EXPECT_EQ(MachOImageSegmentReader::SectionNameString("p"), "p"); + EXPECT_EQ(MachOImageSegmentReader::SectionNameString("NoUnderChar"), + "NoUnderChar"); + EXPECT_EQ(MachOImageSegmentReader::SectionNameString("0123456789abcde"), + "0123456789abcde"); + EXPECT_EQ(MachOImageSegmentReader::SectionNameString("0123456789abcdef"), + "0123456789abcdef"); + EXPECT_EQ(MachOImageSegmentReader::SectionNameString("gfedcba9876543210"), + "gfedcba987654321"); + EXPECT_EQ(MachOImageSegmentReader::SectionNameString("hgfedcba9876543210"), + "hgfedcba98765432"); + + // Section names defined in . All of these should come + // through SectionNameString() cleanly and without truncation. + static constexpr const char* kSectionTestData[] = { + SECT_TEXT, + SECT_FVMLIB_INIT0, + SECT_FVMLIB_INIT1, + SECT_DATA, + SECT_BSS, + SECT_COMMON, + SECT_OBJC_SYMBOLS, + SECT_OBJC_MODULES, + SECT_OBJC_STRINGS, + SECT_OBJC_REFS, + SECT_ICON_HEADER, + SECT_ICON_TIFF, + }; + + for (size_t index = 0; index < std::size(kSectionTestData); ++index) { + EXPECT_EQ( + MachOImageSegmentReader::SectionNameString(kSectionTestData[index]), + kSectionTestData[index]) + << base::StringPrintf("index %zu", index); + } +} + +TEST(MachOImageSegmentReader, SegmentAndSectionNameString) { + static constexpr struct { + const char* segment; + const char* section; + const char* output; + } kSegmentAndSectionTestData[] = { + {"segment", "section", "segment,section"}, + {"Segment", "Section", "Segment,Section"}, + {"SEGMENT", "SECTION", "SEGMENT,SECTION"}, + {"__TEXT", "__plain", "__TEXT,__plain"}, + {"__TEXT", "poetry", "__TEXT,poetry"}, + {"__TEXT", "Prose", "__TEXT,Prose"}, + {"__PLAIN", "__text", "__PLAIN,__text"}, + {"rich", "__text", "rich,__text"}, + {"segment", "", "segment,"}, + {"", "section", ",section"}, + {"", "", ","}, + {"0123456789abcdef", "section", "0123456789abcdef,section"}, + {"gfedcba9876543210", "section", "gfedcba987654321,section"}, + {"0123456789abcdef", "", "0123456789abcdef,"}, + {"gfedcba9876543210", "", "gfedcba987654321,"}, + {"segment", "0123456789abcdef", "segment,0123456789abcdef"}, + {"segment", "gfedcba9876543210", "segment,gfedcba987654321"}, + {"", "0123456789abcdef", ",0123456789abcdef"}, + {"", "gfedcba9876543210", ",gfedcba987654321"}, + {"0123456789abcdef", + "0123456789abcdef", + "0123456789abcdef,0123456789abcdef"}, + {"gfedcba9876543210", + "gfedcba9876543210", + "gfedcba987654321,gfedcba987654321"}, + + // Sections defined in . All of these should come through + // SegmentAndSectionNameString() cleanly and without truncation. + {SEG_TEXT, SECT_TEXT, "__TEXT,__text"}, + {SEG_TEXT, SECT_FVMLIB_INIT0, "__TEXT,__fvmlib_init0"}, + {SEG_TEXT, SECT_FVMLIB_INIT1, "__TEXT,__fvmlib_init1"}, + {SEG_DATA, SECT_DATA, "__DATA,__data"}, + {SEG_DATA, SECT_BSS, "__DATA,__bss"}, + {SEG_DATA, SECT_COMMON, "__DATA,__common"}, + {SEG_OBJC, SECT_OBJC_SYMBOLS, "__OBJC,__symbol_table"}, + {SEG_OBJC, SECT_OBJC_MODULES, "__OBJC,__module_info"}, + {SEG_OBJC, SECT_OBJC_STRINGS, "__OBJC,__selector_strs"}, + {SEG_OBJC, SECT_OBJC_REFS, "__OBJC,__selector_refs"}, + {SEG_ICON, SECT_ICON_HEADER, "__ICON,__header"}, + {SEG_ICON, SECT_ICON_TIFF, "__ICON,__tiff"}, + + // These segments don’t normally have sections, but the above group tested + // the known segment names for segments that do normally have sections. + // This group does the same for segments that normally don’t. + {SEG_LINKEDIT, "", "__LINKEDIT,"}, + {SEG_UNIXSTACK, "", "__UNIXSTACK,"}, + {SEG_IMPORT, "", "__IMPORT,"}, + }; + + for (size_t index = 0; index < std::size(kSegmentAndSectionTestData); + ++index) { + const auto& test = kSegmentAndSectionTestData[index]; + EXPECT_EQ(MachOImageSegmentReader::SegmentAndSectionNameString( + test.segment, test.section), + test.output) + << base::StringPrintf("index %zu, segment %s, section %s", + index, + test.segment, + test.section); + } +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_symbol_table_reader.cc b/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_symbol_table_reader.cc new file mode 100644 index 000000000..ef5a598fa --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_symbol_table_reader.cc @@ -0,0 +1,297 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/mac/mach_o_image_symbol_table_reader.h" + +#include +#include +#include + +#include +#include + +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "util/mac/checked_mach_address_range.h" +#include "util/process/process_memory_mac.h" + +namespace crashpad { + +namespace internal { + +//! \brief The internal implementation for MachOImageSymbolTableReader. +//! +//! Initialization is broken into more than one function that needs to share +//! data, so member variables are used. However, much of this data is irrelevant +//! after initialization is completed, so rather than doing it in +//! MachOImageSymbolTableReader, it’s handled by this class, which is a “friend†+//! of MachOImageSymbolTableReader. +class MachOImageSymbolTableReaderInitializer { + public: + MachOImageSymbolTableReaderInitializer( + ProcessReaderMac* process_reader, + const MachOImageSegmentReader* linkedit_segment, + const std::string& module_info) + : module_info_(module_info), + linkedit_range_(), + process_reader_(process_reader), + linkedit_segment_(linkedit_segment) { + linkedit_range_.SetRange(process_reader_->Is64Bit(), + linkedit_segment->Address(), + linkedit_segment->Size()); + DCHECK(linkedit_range_.IsValid()); + } + + MachOImageSymbolTableReaderInitializer( + const MachOImageSymbolTableReaderInitializer&) = delete; + MachOImageSymbolTableReaderInitializer& operator=( + const MachOImageSymbolTableReaderInitializer&) = delete; + + ~MachOImageSymbolTableReaderInitializer() {} + + //! \brief Reads the symbol table from another process. + //! + //! \sa MachOImageSymbolTableReader::Initialize() + bool Initialize(const process_types::symtab_command* symtab_command, + const process_types::dysymtab_command* dysymtab_command, + MachOImageSymbolTableReader::SymbolInformationMap* + external_defined_symbols) { + mach_vm_address_t symtab_address = + AddressForLinkEditComponent(symtab_command->symoff); + uint32_t symbol_count = symtab_command->nsyms; + size_t nlist_size = process_types::nlist::ExpectedSize(process_reader_); + mach_vm_size_t symtab_size = symbol_count * nlist_size; + if (!IsInLinkEditSegment(symtab_address, symtab_size, "symtab")) { + return false; + } + + // If a dysymtab is present, use it to filter the symtab for just the + // portion used for extdefsym. If no dysymtab is present, the entire symtab + // will need to be consulted. + uint32_t skip_count = 0; + if (dysymtab_command) { + if (dysymtab_command->iextdefsym >= symtab_command->nsyms || + dysymtab_command->iextdefsym + dysymtab_command->nextdefsym > + symtab_command->nsyms) { + LOG(WARNING) << base::StringPrintf( + "dysymtab extdefsym %u + %u > symtab nsyms %u", + dysymtab_command->iextdefsym, + dysymtab_command->nextdefsym, + symtab_command->nsyms) << module_info_; + return false; + } + + skip_count = dysymtab_command->iextdefsym; + mach_vm_size_t skip_size = skip_count * nlist_size; + symtab_address += skip_size; + symtab_size -= skip_size; + symbol_count = dysymtab_command->nextdefsym; + } + + mach_vm_address_t strtab_address = + AddressForLinkEditComponent(symtab_command->stroff); + mach_vm_size_t strtab_size = symtab_command->strsize; + if (!IsInLinkEditSegment(strtab_address, strtab_size, "strtab")) { + return false; + } + + std::unique_ptr symbols( + new process_types::nlist[symtab_command->nsyms]); + if (!process_types::nlist::ReadArrayInto( + process_reader_, symtab_address, symbol_count, &symbols[0])) { + LOG(WARNING) << "could not read symbol table" << module_info_; + return false; + } + + std::unique_ptr string_table; + for (size_t symbol_index = 0; symbol_index < symbol_count; ++symbol_index) { + const process_types::nlist& symbol = symbols[symbol_index]; + std::string symbol_info = base::StringPrintf(", symbol index %zu%s", + skip_count + symbol_index, + module_info_.c_str()); + bool valid_symbol = true; + if ((symbol.n_type & N_STAB) == 0 && (symbol.n_type & N_PEXT) == 0 && + (symbol.n_type & N_EXT)) { + uint8_t symbol_type = symbol.n_type & N_TYPE; + if (symbol_type == N_ABS || symbol_type == N_SECT) { + if (symbol.n_strx >= strtab_size) { + LOG(WARNING) << base::StringPrintf( + "string at 0x%x out of bounds (0x%llx)", + symbol.n_strx, + strtab_size) << symbol_info; + return false; + } + + if (!string_table) { + string_table = process_reader_->Memory()->ReadMapped( + strtab_address, strtab_size); + if (!string_table) { + LOG(WARNING) << "could not read string table" << module_info_; + return false; + } + } + + std::string name; + if (!string_table->ReadCString(symbol.n_strx, &name)) { + LOG(WARNING) << "could not read string" << symbol_info; + return false; + } + + if (symbol_type == N_ABS && symbol.n_sect != NO_SECT) { + LOG(WARNING) << base::StringPrintf("N_ABS symbol %s in section %u", + name.c_str(), + symbol.n_sect) << symbol_info; + return false; + } + + if (symbol_type == N_SECT && symbol.n_sect == NO_SECT) { + LOG(WARNING) << base::StringPrintf( + "N_SECT symbol %s in section NO_SECT", + name.c_str()) << symbol_info; + return false; + } + + MachOImageSymbolTableReader::SymbolInformation this_symbol_info; + this_symbol_info.value = symbol.n_value; + this_symbol_info.section = symbol.n_sect; + if (!external_defined_symbols->insert( + std::make_pair(name, this_symbol_info)).second) { + LOG(WARNING) << "duplicate symbol " << name << symbol_info; + return false; + } + } else { + // External indirect symbols may be found in the portion of the symbol + // table used for external symbols as opposed to indirect symbols when + // the indirect symbols are also external. These can be produced by + // Xcode 5.1 ld64-236.3/src/ld/LinkEditClassic.hpp + // ld::tool::SymbolTableAtom<>::addGlobal(). Indirect symbols are not + // currently supported by this symbol table reader, so ignore them + // without failing or logging a message when encountering them. See + // https://groups.google.com/a/chromium.org/d/topic/crashpad-dev/k7QkLwO71Zo + valid_symbol = symbol_type == N_INDR; + } + } else { + valid_symbol = false; + } + if (!valid_symbol && dysymtab_command) { + LOG(WARNING) << "non-external symbol with type " << symbol.n_type + << " in extdefsym" << symbol_info; + return false; + } + } + + return true; + } + + private: + //! \brief Computes the address for data in the `__LINKEDIT` segment + //! identified by its file offset in a Mach-O image. + //! + //! \param[in] fileoff The file offset relative to the beginning of an image’s + //! `mach_header` or `mach_header_64` of the data in the `__LINKEDIT` + //! segment. + //! + //! \return The address, in the remote process’ address space, of the + //! requested data. + mach_vm_address_t AddressForLinkEditComponent(uint32_t fileoff) const { + return linkedit_range_.Base() + fileoff - linkedit_segment_->fileoff(); + } + + //! \brief Determines whether an address range is located within the + //! `__LINKEDIT` segment. + //! + //! \param[in] address The base address of the range to check. + //! \param[in] size The size of the range to check. + //! \param[in] tag A string that identifies the range being checked. This is + //! used only for logging. + //! + //! \return `true` if the range identified by \a address + \a size lies + //! entirely within the `__LINKEDIT` segment. `false` if that range is + //! invalid, or if that range is not contained by the `__LINKEDIT` + //! segment, with an appropriate message logged. + bool IsInLinkEditSegment(mach_vm_address_t address, + mach_vm_size_t size, + const char* tag) const { + CheckedMachAddressRange subrange(process_reader_->Is64Bit(), address, size); + if (!subrange.IsValid()) { + LOG(WARNING) << base::StringPrintf("invalid %s range (0x%llx + 0x%llx)", + tag, + address, + size) << module_info_; + return false; + } + + if (!linkedit_range_.ContainsRange(subrange)) { + LOG(WARNING) << base::StringPrintf( + "%s at 0x%llx + 0x%llx outside of " SEG_LINKEDIT + " segment at 0x%llx + 0x%llx", + tag, + address, + size, + linkedit_range_.Base(), + linkedit_range_.Size()) << module_info_; + return false; + } + + return true; + } + + std::string module_info_; + CheckedMachAddressRange linkedit_range_; + ProcessReaderMac* process_reader_; // weak + const MachOImageSegmentReader* linkedit_segment_; // weak +}; + +} // namespace internal + +MachOImageSymbolTableReader::MachOImageSymbolTableReader() + : external_defined_symbols_(), initialized_() { +} + +MachOImageSymbolTableReader::~MachOImageSymbolTableReader() { +} + +bool MachOImageSymbolTableReader::Initialize( + ProcessReaderMac* process_reader, + const process_types::symtab_command* symtab_command, + const process_types::dysymtab_command* dysymtab_command, + const MachOImageSegmentReader* linkedit_segment, + const std::string& module_info) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + internal::MachOImageSymbolTableReaderInitializer initializer(process_reader, + linkedit_segment, + module_info); + if (!initializer.Initialize( + symtab_command, dysymtab_command, &external_defined_symbols_)) { + return false; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +const MachOImageSymbolTableReader::SymbolInformation* +MachOImageSymbolTableReader::LookUpExternalDefinedSymbol( + const std::string& name) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + const auto& iterator = external_defined_symbols_.find(name); + if (iterator == external_defined_symbols_.end()) { + return nullptr; + } + return &iterator->second; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_symbol_table_reader.h b/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_symbol_table_reader.h new file mode 100644 index 000000000..2af485394 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/mach_o_image_symbol_table_reader.h @@ -0,0 +1,134 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_SYMBOL_TABLE_READER_H_ +#define CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_SYMBOL_TABLE_READER_H_ + +#include +#include + +#include +#include + +#include "snapshot/mac/mach_o_image_segment_reader.h" +#include "snapshot/mac/process_reader_mac.h" +#include "snapshot/mac/process_types.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +//! \brief A reader for symbol tables in Mach-O images mapped into another +//! process. +class MachOImageSymbolTableReader { + public: + //! \brief Information about a symbol in a module’s symbol table. + //! + //! This is a more minimal form of the `nlist` (or `nlist_64`) structure, + //! only containing the equivalent of the `n_value` and `n_sect` fields. + struct SymbolInformation { + //! \brief The address of the symbol as it exists in the symbol table, not + //! adjusted for any “slide.†+ mach_vm_address_t value; + + //! \brief The 1-based section index in the module in which the symbol is + //! found. + //! + //! For symbols defined in a section (`N_SECT`), this is the section index + //! that can be passed to MachOImageReader::GetSectionAtIndex(), and \a + //! value will need to be adjusted for segment slide if the containing + //! segment slid when loaded. For absolute symbols (`N_ABS`), this will be + //! `NO_SECT` (`0`), and \a value must not be adjusted for segment slide. + uint8_t section; + }; + + // TODO(mark): Use std::unordered_map or a similar hash-based map? For now, + // std::map is fine because this map only stores external defined symbols, and + // there aren’t expected to be very many of those that performance would + // become a problem. In reality, std::unordered_map does not appear to provide + // a performance advantage. It appears that the memory copies currently done + // by ProcessMemoryMac::Read() have substantially more impact on symbol table + // operations. + // + // This is public so that the type is available to + // MachOImageSymbolTableReaderInitializer. + using SymbolInformationMap = std::map; + + MachOImageSymbolTableReader(); + + MachOImageSymbolTableReader(const MachOImageSymbolTableReader&) = delete; + MachOImageSymbolTableReader& operator=(const MachOImageSymbolTableReader&) = + delete; + + ~MachOImageSymbolTableReader(); + + //! \brief Reads the symbol table from another process. + //! + //! This method must only be called once on an object. This method must be + //! called successfully before any other method in this class may be called. + //! + //! \param[in] process_reader The reader for the remote process. + //! \param[in] symtab_command The `LC_SYMTAB` load command that identifies + //! the symbol table. + //! \param[in] dysymtab_command The `LC_DYSYMTAB` load command that identifies + //! dynamic symbol information within the symbol table. This load command + //! is not present in all modules, and this parameter may be `nullptr` for + //! modules that do not have this information. When present, \a + //! dysymtab_command is an optimization that allows the symbol table + //! reader to only examine symbol table entries known to be relevant for + //! its purposes. + //! \param[in] linkedit_segment The `__LINKEDIT` segment. This segment should + //! contain the data referenced by \a symtab_command and \a + //! dysymtab_command. This may be any segment in the module, but by + //! convention, the name `__LINKEDIT` is used for this purpose. + //! \param[in] module_info A string to be used in logged messages. This string + //! is for diagnostic purposes only, and may be empty. + //! + //! \return `true` if the symbol table was read successfully. `false` + //! otherwise, with an appropriate message logged. + bool Initialize(ProcessReaderMac* process_reader, + const process_types::symtab_command* symtab_command, + const process_types::dysymtab_command* dysymtab_command, + const MachOImageSegmentReader* linkedit_segment, + const std::string& module_info); + + //! \brief Looks up a symbol in the image’s symbol table. + //! + //! The returned information captures the symbol as it exists in the image’s + //! symbol table, not adjusted for any “slide.†+ //! + //! \param[in] name The name of the symbol to look up, “mangled†or + //! “decorated†appropriately. For example, use `"_main"` to look up the + //! symbol for the C `main()` function, and use `"__Z4Funcv"` to look up + //! the symbol for the C++ `Func()` function. + //! + //! \return A SymbolInformation* object with information about the symbol if + //! it was found, or `nullptr` if the symbol was not found or if an error + //! occurred. On error, a warning message will also be logged. The caller + //! does not take ownership; the lifetime of the returned object is scoped + //! to the lifetime of this MachOImageSymbolTableReader object. + //! + //! \note Symbol values returned via this interface are not adjusted for + //! “slide.†For slide-adjusted values, use the higher-level + //! MachOImageReader::LookUpExternalDefinedSymbol() interface. + const SymbolInformation* LookUpExternalDefinedSymbol( + const std::string& name) const; + + private: + SymbolInformationMap external_defined_symbols_; + InitializationStateDcheck initialized_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MAC_MACH_O_IMAGE_SYMBOL_TABLE_READER_H_ diff --git a/shared/sentry/external/crashpad/snapshot/mac/module_snapshot_mac.cc b/shared/sentry/external/crashpad/snapshot/mac/module_snapshot_mac.cc new file mode 100644 index 000000000..906483583 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/module_snapshot_mac.cc @@ -0,0 +1,208 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/mac/module_snapshot_mac.h" + +#include +#include + +#include "base/files/file_path.h" +#include "snapshot/mac/mach_o_image_annotations_reader.h" +#include "snapshot/mac/mach_o_image_reader.h" +#include "util/misc/tri_state.h" +#include "util/misc/uuid.h" +#include "util/stdlib/strnlen.h" + +namespace crashpad { +namespace internal { + +ModuleSnapshotMac::ModuleSnapshotMac() + : ModuleSnapshot(), + name_(), + timestamp_(0), + mach_o_image_reader_(nullptr), + process_reader_(nullptr), + initialized_() {} + +ModuleSnapshotMac::~ModuleSnapshotMac() {} + +bool ModuleSnapshotMac::Initialize( + ProcessReaderMac* process_reader, + const ProcessReaderMac::Module& process_reader_module) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + process_reader_ = process_reader; + name_ = process_reader_module.name; + timestamp_ = process_reader_module.timestamp; + mach_o_image_reader_ = process_reader_module.reader; + if (!mach_o_image_reader_) { + return false; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +void ModuleSnapshotMac::GetCrashpadOptions(CrashpadInfoClientOptions* options) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + process_types::CrashpadInfo crashpad_info; + if (!mach_o_image_reader_->GetCrashpadInfo(&crashpad_info)) { + options->crashpad_handler_behavior = TriState::kUnset; + options->system_crash_reporter_forwarding = TriState::kUnset; + options->gather_indirectly_referenced_memory = TriState::kUnset; + return; + } + + options->crashpad_handler_behavior = + CrashpadInfoClientOptions::TriStateFromCrashpadInfo( + crashpad_info.crashpad_handler_behavior); + + options->system_crash_reporter_forwarding = + CrashpadInfoClientOptions::TriStateFromCrashpadInfo( + crashpad_info.system_crash_reporter_forwarding); + + options->gather_indirectly_referenced_memory = + CrashpadInfoClientOptions::TriStateFromCrashpadInfo( + crashpad_info.gather_indirectly_referenced_memory); + + options->indirectly_referenced_memory_cap = + crashpad_info.indirectly_referenced_memory_cap; +} + +std::string ModuleSnapshotMac::Name() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return name_; +} + +uint64_t ModuleSnapshotMac::Address() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return mach_o_image_reader_->Address(); +} + +uint64_t ModuleSnapshotMac::Size() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return mach_o_image_reader_->Size(); +} + +time_t ModuleSnapshotMac::Timestamp() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return timestamp_; +} + +void ModuleSnapshotMac::FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (mach_o_image_reader_->FileType() == MH_DYLIB) { + uint32_t dylib_version = mach_o_image_reader_->DylibVersion(); + *version_0 = (dylib_version & 0xffff0000) >> 16; + *version_1 = (dylib_version & 0x0000ff00) >> 8; + *version_2 = (dylib_version & 0x000000ff); + *version_3 = 0; + } else { + *version_0 = 0; + *version_1 = 0; + *version_2 = 0; + *version_3 = 0; + } +} + +void ModuleSnapshotMac::SourceVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + // LC_SOURCE_VERSION is supposed to be interpreted as a 5-component version + // number, 24 bits for the first component and 10 for the others, per + // . To preserve the full range of possible version numbers + // without data loss, map it to the 4 16-bit fields mandated by the interface + // here, which was informed by the minidump file format. + uint64_t source_version = mach_o_image_reader_->SourceVersion(); + *version_0 = (source_version & 0xffff000000000000u) >> 48; + *version_1 = (source_version & 0x0000ffff00000000u) >> 32; + *version_2 = (source_version & 0x00000000ffff0000u) >> 16; + *version_3 = source_version & 0x000000000000ffffu; +} + +ModuleSnapshot::ModuleType ModuleSnapshotMac::GetModuleType() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + uint32_t file_type = mach_o_image_reader_->FileType(); + switch (file_type) { + case MH_EXECUTE: + return kModuleTypeExecutable; + case MH_DYLIB: + return kModuleTypeSharedLibrary; + case MH_DYLINKER: + return kModuleTypeDynamicLoader; + case MH_BUNDLE: + return kModuleTypeLoadableModule; + default: + return kModuleTypeUnknown; + } +} + +void ModuleSnapshotMac::UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + mach_o_image_reader_->UUID(uuid); + *age = 0; +} + +std::string ModuleSnapshotMac::DebugFileName() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return base::FilePath(Name()).BaseName().value(); +} + +std::vector ModuleSnapshotMac::BuildID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector(); +} + +std::vector ModuleSnapshotMac::AnnotationsVector() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + MachOImageAnnotationsReader annotations_reader( + process_reader_, mach_o_image_reader_, name_); + return annotations_reader.Vector(); +} + +std::map ModuleSnapshotMac::AnnotationsSimpleMap() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + MachOImageAnnotationsReader annotations_reader( + process_reader_, mach_o_image_reader_, name_); + return annotations_reader.SimpleMap(); +} + +std::vector ModuleSnapshotMac::AnnotationObjects() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + MachOImageAnnotationsReader annotations_reader( + process_reader_, mach_o_image_reader_, name_); + return annotations_reader.AnnotationsList(); +} + +std::set> ModuleSnapshotMac::ExtraMemoryRanges() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::set>(); +} + +std::vector +ModuleSnapshotMac::CustomMinidumpStreams() const { + return std::vector(); +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/mac/module_snapshot_mac.h b/shared/sentry/external/crashpad/snapshot/mac/module_snapshot_mac.h new file mode 100644 index 000000000..ce1e19dab --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/module_snapshot_mac.h @@ -0,0 +1,101 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MAC_MODULE_SNAPSHOT_MAC_H_ +#define CRASHPAD_SNAPSHOT_MAC_MODULE_SNAPSHOT_MAC_H_ + +#include +#include + +#include +#include +#include + +#include "client/crashpad_info.h" +#include "snapshot/crashpad_info_client_options.h" +#include "snapshot/mac/process_reader_mac.h" +#include "snapshot/module_snapshot.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +class MachOImageReader; +struct UUID; + +namespace internal { + +//! \brief A ModuleSnapshot of a code module (binary image) loaded into a +//! running (or crashed) process on a macOS system. +class ModuleSnapshotMac final : public ModuleSnapshot { + public: + ModuleSnapshotMac(); + + ModuleSnapshotMac(const ModuleSnapshotMac&) = delete; + ModuleSnapshotMac& operator=(const ModuleSnapshotMac&) = delete; + + ~ModuleSnapshotMac() override; + + //! \brief Initializes the object. + //! + //! \param[in] process_reader A ProcessReaderMac for the task containing the + //! module. + //! \param[in] process_reader_module The module within the ProcessReaderMac + //! for which the snapshot should be created. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(ProcessReaderMac* process_reader, + const ProcessReaderMac::Module& process_reader_module); + + //! \brief Returns options from the module’s CrashpadInfo structure. + //! + //! \param[out] options Options set in the module’s CrashpadInfo structure. + void GetCrashpadOptions(CrashpadInfoClientOptions* options); + + // ModuleSnapshot: + + std::string Name() const override; + uint64_t Address() const override; + uint64_t Size() const override; + time_t Timestamp() const override; + void FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const override; + void SourceVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const override; + ModuleType GetModuleType() const override; + void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override; + std::string DebugFileName() const override; + std::vector BuildID() const override; + std::vector AnnotationsVector() const override; + std::map AnnotationsSimpleMap() const override; + std::vector AnnotationObjects() const override; + std::set> ExtraMemoryRanges() const override; + std::vector CustomMinidumpStreams() const override; + + private: + std::string name_; + time_t timestamp_; + const MachOImageReader* mach_o_image_reader_; // weak + ProcessReaderMac* process_reader_; // weak + InitializationStateDcheck initialized_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MAC_MODULE_SNAPSHOT_MAC_H_ diff --git a/shared/sentry/external/crashpad/snapshot/mac/process_reader_mac.cc b/shared/sentry/external/crashpad/snapshot/mac/process_reader_mac.cc new file mode 100644 index 000000000..9b2a23562 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/process_reader_mac.cc @@ -0,0 +1,756 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/mac/process_reader_mac.h" + +#include +#include +#include + +#include +#include + +#include "base/logging.h" +#include "base/mac/mach_logging.h" +#include "base/mac/scoped_mach_port.h" +#include "base/mac/scoped_mach_vm.h" +#include "base/strings/stringprintf.h" +#include "snapshot/mac/mach_o_image_reader.h" +#include "snapshot/mac/process_types.h" +#include "util/misc/scoped_forbid_return.h" + +namespace { + +void MachTimeValueToTimeval(const time_value& mach, timeval* tv) { + tv->tv_sec = mach.seconds; + tv->tv_usec = mach.microseconds; +} + +kern_return_t MachVMRegionRecurseDeepest(task_t task, + mach_vm_address_t* address, + mach_vm_size_t* size, + natural_t* depth, + vm_prot_t* protection, + unsigned int* user_tag) { + vm_region_submap_short_info_64 submap_info; + mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64; + while (true) { + kern_return_t kr = mach_vm_region_recurse( + task, + address, + size, + depth, + reinterpret_cast(&submap_info), + &count); + if (kr != KERN_SUCCESS) { + return kr; + } + + if (!submap_info.is_submap) { + *protection = submap_info.protection; + *user_tag = submap_info.user_tag; + return KERN_SUCCESS; + } + + ++*depth; + } +} + +} // namespace + +namespace crashpad { + +ProcessReaderMac::Thread::Thread() + : thread_context(), + float_context(), + debug_context(), + id(0), + stack_region_address(0), + stack_region_size(0), + thread_specific_data_address(0), + port(THREAD_NULL), + suspend_count(0), + priority(0) {} + +ProcessReaderMac::Module::Module() : name(), reader(nullptr), timestamp(0) {} + +ProcessReaderMac::Module::~Module() {} + +ProcessReaderMac::ProcessReaderMac() + : process_info_(), + threads_(), + modules_(), + module_readers_(), + process_memory_(), + task_(TASK_NULL), + initialized_(), +#if defined(CRASHPAD_MAC_32_BIT_SUPPORT) + is_64_bit_(false), +#endif // CRASHPAD_MAC_32_BIT_SUPPORT + initialized_threads_(false), + initialized_modules_(false) { +} + +ProcessReaderMac::~ProcessReaderMac() { + for (const Thread& thread : threads_) { + kern_return_t kr = mach_port_deallocate(mach_task_self(), thread.port); + MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "mach_port_deallocate"; + } +} + +bool ProcessReaderMac::Initialize(task_t task) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + if (!process_info_.InitializeWithTask(task)) { + return false; + } + + if (!process_memory_.Initialize(task)) { + return false; + } + +#if defined(CRASHPAD_MAC_32_BIT_SUPPORT) + is_64_bit_ = process_info_.Is64Bit(); +#else // CRASHPAD_MAC_32_BIT_SUPPORT + DCHECK(process_info_.Is64Bit()); +#endif // CRASHPAD_MAC_32_BIT_SUPPORT + + task_ = task; + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +void ProcessReaderMac::StartTime(timeval* start_time) const { + bool rv = process_info_.StartTime(start_time); + DCHECK(rv); +} + +bool ProcessReaderMac::CPUTimes(timeval* user_time, + timeval* system_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + // Calculate user and system time the same way the kernel does for + // getrusage(). See 10.9.2 xnu-2422.90.20/bsd/kern/kern_resource.c calcru(). + timerclear(user_time); + timerclear(system_time); + + // As of the 10.8 SDK, the preferred routine is MACH_TASK_BASIC_INFO. + // TASK_BASIC_INFO_64 is equivalent and works on earlier systems. + task_basic_info_64 task_basic_info; + mach_msg_type_number_t task_basic_info_count = TASK_BASIC_INFO_64_COUNT; + kern_return_t kr = task_info(task_, + TASK_BASIC_INFO_64, + reinterpret_cast(&task_basic_info), + &task_basic_info_count); + if (kr != KERN_SUCCESS) { + MACH_LOG(WARNING, kr) << "task_info TASK_BASIC_INFO_64"; + return false; + } + + task_thread_times_info_data_t task_thread_times; + mach_msg_type_number_t task_thread_times_count = TASK_THREAD_TIMES_INFO_COUNT; + kr = task_info(task_, + TASK_THREAD_TIMES_INFO, + reinterpret_cast(&task_thread_times), + &task_thread_times_count); + if (kr != KERN_SUCCESS) { + MACH_LOG(WARNING, kr) << "task_info TASK_THREAD_TIMES"; + return false; + } + + MachTimeValueToTimeval(task_basic_info.user_time, user_time); + MachTimeValueToTimeval(task_basic_info.system_time, system_time); + + timeval thread_user_time; + MachTimeValueToTimeval(task_thread_times.user_time, &thread_user_time); + timeval thread_system_time; + MachTimeValueToTimeval(task_thread_times.system_time, &thread_system_time); + + timeradd(user_time, &thread_user_time, user_time); + timeradd(system_time, &thread_system_time, system_time); + + return true; +} + +const std::vector& ProcessReaderMac::Threads() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (!initialized_threads_) { + InitializeThreads(); + } + + return threads_; +} + +const std::vector& ProcessReaderMac::Modules() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (!initialized_modules_) { + InitializeModules(); + } + + return modules_; +} + +mach_vm_address_t ProcessReaderMac::DyldAllImageInfo( + mach_vm_size_t* all_image_info_size) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + task_dyld_info_data_t dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + kern_return_t kr = task_info( + task_, TASK_DYLD_INFO, reinterpret_cast(&dyld_info), &count); + if (kr != KERN_SUCCESS) { + MACH_LOG(WARNING, kr) << "task_info"; + return 0; + } + +// TODO(mark): Deal with statically linked executables which don’t use dyld. +// This may look for the module that matches the executable path in the same +// data set that vmmap uses. + +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_7 + // The task_dyld_info_data_t struct grew in 10.7, adding the format field. + // Don’t check this field if it’s not present, which can happen when either + // the SDK used at compile time or the kernel at run time are too old and + // don’t know about it. + if (count >= TASK_DYLD_INFO_COUNT) { + const integer_t kExpectedFormat = + !Is64Bit() ? TASK_DYLD_ALL_IMAGE_INFO_32 : TASK_DYLD_ALL_IMAGE_INFO_64; + if (dyld_info.all_image_info_format != kExpectedFormat) { + LOG(WARNING) << "unexpected task_dyld_info_data_t::all_image_info_format " + << dyld_info.all_image_info_format; + DCHECK_EQ(dyld_info.all_image_info_format, kExpectedFormat); + return 0; + } + } +#endif + + if (all_image_info_size) { + *all_image_info_size = dyld_info.all_image_info_size; + } + return dyld_info.all_image_info_addr; +} + +void ProcessReaderMac::InitializeThreads() { + DCHECK(!initialized_threads_); + DCHECK(threads_.empty()); + + initialized_threads_ = true; + + thread_act_array_t threads; + mach_msg_type_number_t thread_count = 0; + kern_return_t kr = task_threads(task_, &threads, &thread_count); + if (kr != KERN_SUCCESS) { + MACH_LOG(WARNING, kr) << "task_threads"; + return; + } + + // The send rights in the |threads| array won’t have their send rights managed + // by anything until they’re added to |threads_| by the loop below. Any early + // return (or exception) that happens between here and the completion of the + // loop below will leak thread port send rights. + ScopedForbidReturn threads_need_owners; + + base::mac::ScopedMachVM threads_vm( + reinterpret_cast(threads), + mach_vm_round_page(thread_count * sizeof(*threads))); + + for (size_t index = 0; index < thread_count; ++index) { + Thread thread; + thread.port = threads[index]; + +#if defined(ARCH_CPU_X86_FAMILY) + const thread_state_flavor_t kThreadStateFlavor = + Is64Bit() ? x86_THREAD_STATE64 : x86_THREAD_STATE32; + mach_msg_type_number_t thread_state_count = + Is64Bit() ? x86_THREAD_STATE64_COUNT : x86_THREAD_STATE32_COUNT; + + // TODO(mark): Use the AVX variants instead of the FLOAT variants? + const thread_state_flavor_t kFloatStateFlavor = + Is64Bit() ? x86_FLOAT_STATE64 : x86_FLOAT_STATE32; + mach_msg_type_number_t float_state_count = + Is64Bit() ? x86_FLOAT_STATE64_COUNT : x86_FLOAT_STATE32_COUNT; + + const thread_state_flavor_t kDebugStateFlavor = + Is64Bit() ? x86_DEBUG_STATE64 : x86_DEBUG_STATE32; + mach_msg_type_number_t debug_state_count = + Is64Bit() ? x86_DEBUG_STATE64_COUNT : x86_DEBUG_STATE32_COUNT; +#elif defined(ARCH_CPU_ARM64) + const thread_state_flavor_t kThreadStateFlavor = ARM_THREAD_STATE64; + mach_msg_type_number_t thread_state_count = ARM_THREAD_STATE64_COUNT; + + const thread_state_flavor_t kFloatStateFlavor = ARM_NEON_STATE64; + mach_msg_type_number_t float_state_count = ARM_NEON_STATE64_COUNT; + + const thread_state_flavor_t kDebugStateFlavor = ARM_DEBUG_STATE64; + mach_msg_type_number_t debug_state_count = ARM_DEBUG_STATE64_COUNT; +#endif + + kr = thread_get_state( + thread.port, + kThreadStateFlavor, + reinterpret_cast(&thread.thread_context), + &thread_state_count); + if (kr != KERN_SUCCESS) { + MACH_LOG(ERROR, kr) << "thread_get_state(" << kThreadStateFlavor << ")"; + continue; + } + + kr = thread_get_state( + thread.port, + kFloatStateFlavor, + reinterpret_cast(&thread.float_context), + &float_state_count); + if (kr != KERN_SUCCESS) { + MACH_LOG(ERROR, kr) << "thread_get_state(" << kFloatStateFlavor << ")"; + continue; + } + + kr = thread_get_state( + thread.port, + kDebugStateFlavor, + reinterpret_cast(&thread.debug_context), + &debug_state_count); + if (kr != KERN_SUCCESS) { + MACH_LOG(ERROR, kr) << "thread_get_state(" << kDebugStateFlavor << ")"; + continue; + } + + thread_basic_info basic_info; + mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT; + kr = thread_info(thread.port, + THREAD_BASIC_INFO, + reinterpret_cast(&basic_info), + &count); + if (kr != KERN_SUCCESS) { + MACH_LOG(WARNING, kr) << "thread_info(THREAD_BASIC_INFO)"; + } else { + thread.suspend_count = basic_info.suspend_count; + } + + thread_identifier_info identifier_info; + count = THREAD_IDENTIFIER_INFO_COUNT; + kr = thread_info(thread.port, + THREAD_IDENTIFIER_INFO, + reinterpret_cast(&identifier_info), + &count); + if (kr != KERN_SUCCESS) { + MACH_LOG(WARNING, kr) << "thread_info(THREAD_IDENTIFIER_INFO)"; + } else { + thread.id = identifier_info.thread_id; + + // thread_identifier_info::thread_handle contains the base of the + // thread-specific data area, which on x86 and x86_64 is the thread’s base + // address of the %gs segment. 10.9.2 xnu-2422.90.20/osfmk/kern/thread.c + // thread_info_internal() gets the value from + // machine_thread::cthread_self, which is the same value used to set the + // %gs base in xnu-2422.90.20/osfmk/i386/pcb_native.c + // act_machine_switch_pcb(). + // + // This address is the internal pthread’s _pthread::tsd[], an array of + // void* values that can be indexed by pthread_key_t values. + thread.thread_specific_data_address = identifier_info.thread_handle; + } + + thread_precedence_policy precedence; + count = THREAD_PRECEDENCE_POLICY_COUNT; + boolean_t get_default = FALSE; + kr = thread_policy_get(thread.port, + THREAD_PRECEDENCE_POLICY, + reinterpret_cast(&precedence), + &count, + &get_default); + if (kr != KERN_SUCCESS) { + MACH_LOG(INFO, kr) << "thread_policy_get"; + } else { + thread.priority = precedence.importance; + } + +#if defined(ARCH_CPU_X86_FAMILY) + mach_vm_address_t stack_pointer = Is64Bit() + ? thread.thread_context.t64.__rsp + : thread.thread_context.t32.__esp; +#elif defined(ARCH_CPU_ARM64) + mach_vm_address_t stack_pointer = + arm_thread_state64_get_sp(thread.thread_context); +#endif + + thread.stack_region_address = + CalculateStackRegion(stack_pointer, &thread.stack_region_size); + + threads_.push_back(thread); + } + + threads_need_owners.Disarm(); +} + +void ProcessReaderMac::InitializeModules() { + DCHECK(!initialized_modules_); + DCHECK(modules_.empty()); + + initialized_modules_ = true; + + mach_vm_size_t all_image_info_size; + mach_vm_address_t all_image_info_addr = + DyldAllImageInfo(&all_image_info_size); + + process_types::dyld_all_image_infos all_image_infos; + if (!all_image_infos.Read(this, all_image_info_addr)) { + LOG(WARNING) << "could not read dyld_all_image_infos"; + return; + } + + if (all_image_infos.version < 1) { + LOG(WARNING) << "unexpected dyld_all_image_infos version " + << all_image_infos.version; + return; + } + + size_t expected_size = + process_types::dyld_all_image_infos::ExpectedSizeForVersion( + this, all_image_infos.version); + if (all_image_info_size < expected_size) { + LOG(WARNING) << "small dyld_all_image_infos size " << all_image_info_size + << " < " << expected_size << " for version " + << all_image_infos.version; + return; + } + + // Note that all_image_infos.infoArrayCount may be 0 if a crash occurred while + // dyld was loading the executable. This can happen if a required dynamic + // library was not found. Similarly, all_image_infos.infoArray may be nullptr + // if a crash occurred while dyld was updating it. + // + // TODO(mark): It may be possible to recover from these situations by looking + // through memory mappings for Mach-O images. + // + // Continue along when this situation is detected, because even without any + // images in infoArray, dyldImageLoadAddress may be set, and it may be + // possible to recover some information from dyld. + if (all_image_infos.infoArrayCount == 0) { + LOG(WARNING) << "all_image_infos.infoArrayCount is zero"; + } else if (!all_image_infos.infoArray) { + LOG(WARNING) << "all_image_infos.infoArray is nullptr"; + } + + std::vector image_info_vector( + all_image_infos.infoArrayCount); + if (!process_types::dyld_image_info::ReadArrayInto(this, + all_image_infos.infoArray, + image_info_vector.size(), + &image_info_vector[0])) { + LOG(WARNING) << "could not read dyld_image_info array"; + return; + } + + size_t main_executable_count = 0; + bool found_dyld = false; + modules_.reserve(image_info_vector.size()); + for (const process_types::dyld_image_info& image_info : image_info_vector) { + Module module; + module.timestamp = image_info.imageFileModDate; + + if (!process_memory_.ReadCString(image_info.imageFilePath, &module.name)) { + LOG(WARNING) << "could not read dyld_image_info::imageFilePath"; + // Proceed anyway with an empty module name. + } + + std::unique_ptr reader(new MachOImageReader()); + if (!reader->Initialize(this, image_info.imageLoadAddress, module.name)) { + reader.reset(); + } + + module.reader = reader.get(); + + uint32_t file_type = reader ? reader->FileType() : 0; + + module_readers_.push_back(std::move(reader)); + modules_.push_back(module); + + if (all_image_infos.version >= 2 && all_image_infos.dyldImageLoadAddress && + image_info.imageLoadAddress == all_image_infos.dyldImageLoadAddress) { + found_dyld = true; + LOG(WARNING) << base::StringPrintf( + "found dylinker (%s) in dyld_all_image_infos::infoArray", + module.name.c_str()); + + LOG_IF(WARNING, file_type != MH_DYLINKER) + << base::StringPrintf("dylinker (%s) has unexpected Mach-O type %d", + module.name.c_str(), + file_type); + } + + if (file_type == MH_EXECUTE) { + // On Mac OS X 10.6, the main executable does not normally show up at + // index 0. This is because of how 10.6.8 dyld-132.13/src/dyld.cpp + // notifyGDB(), the function resposible for causing + // dyld_all_image_infos::infoArray to be updated, is called. It is + // registered to be called when all dependents of an image have been + // mapped (dyld_image_state_dependents_mapped), meaning that the main + // executable won’t be added to the list until all of the libraries it + // depends on are, even though dyld begins looking at the main executable + // first. This changed in later versions of dyld, including those present + // in 10.7. 10.9.4 dyld-239.4/src/dyld.cpp updateAllImages() (renamed from + // notifyGDB()) is registered to be called when an image itself has been + // mapped (dyld_image_state_mapped), regardless of the libraries that it + // depends on. + // + // The interface requires that the main executable be first in the list, + // so swap it into the right position. + size_t index = modules_.size() - 1; + if (main_executable_count == 0) { + std::swap(modules_[0], modules_[index]); + } else { + LOG(WARNING) << base::StringPrintf( + "multiple MH_EXECUTE modules (%s, %s)", + modules_[0].name.c_str(), + modules_[index].name.c_str()); + } + ++main_executable_count; + } + } + + LOG_IF(WARNING, main_executable_count == 0) << "no MH_EXECUTE modules"; + + // all_image_infos.infoArray doesn’t include an entry for dyld, but dyld is + // loaded into the process’ address space as a module. Its load address is + // easily known given a sufficiently recent all_image_infos.version, but the + // timestamp and pathname are not given as they are for other modules. + // + // The timestamp is a lost cause, because the kernel doesn’t record the + // timestamp of the dynamic linker at the time it’s loaded in the same way + // that dyld records the timestamps of other modules when they’re loaded. (The + // timestamp for the main executable is also not reported and appears as 0 + // even when accessed via dyld APIs, because it’s loaded by the kernel, not by + // dyld.) + // + // The name can be determined, but it’s not as simple as hardcoding the + // default "/usr/lib/dyld" because an executable could have specified anything + // in its LC_LOAD_DYLINKER command. + if (!found_dyld && all_image_infos.version >= 2 && + all_image_infos.dyldImageLoadAddress) { + Module module; + module.timestamp = 0; + + // Examine the executable’s LC_LOAD_DYLINKER load command to find the path + // used to load dyld. + if (all_image_infos.infoArrayCount >= 1 && main_executable_count >= 1) { + module.name = modules_[0].reader->DylinkerName(); + } + std::string module_name = !module.name.empty() ? module.name : "(dyld)"; + + std::unique_ptr reader(new MachOImageReader()); + if (!reader->Initialize( + this, all_image_infos.dyldImageLoadAddress, module_name)) { + reader.reset(); + } + + module.reader = reader.get(); + + uint32_t file_type = reader ? reader->FileType() : 0; + + LOG_IF(WARNING, file_type != MH_DYLINKER) + << base::StringPrintf("dylinker (%s) has unexpected Mach-O type %d", + module.name.c_str(), + file_type); + + if (module.name.empty() && file_type == MH_DYLINKER) { + // Look inside dyld directly to find its preferred path. + module.name = reader->DylinkerName(); + } + + if (module.name.empty()) { + module.name = "(dyld)"; + } + + // dyld is loaded in the process even if its path can’t be determined. + module_readers_.push_back(std::move(reader)); + modules_.push_back(module); + } +} + +mach_vm_address_t ProcessReaderMac::CalculateStackRegion( + mach_vm_address_t stack_pointer, + mach_vm_size_t* stack_region_size) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + // For pthreads, it may be possible to compute the stack region based on the + // internal _pthread::stackaddr and _pthread::stacksize. The _pthread struct + // for a thread can be located at TSD slot 0, or the known offsets of + // stackaddr and stacksize from the TSD area could be used. + mach_vm_address_t region_base = stack_pointer; + mach_vm_size_t region_size; + natural_t depth = 0; + vm_prot_t protection; + unsigned int user_tag; + kern_return_t kr = MachVMRegionRecurseDeepest( + task_, ®ion_base, ®ion_size, &depth, &protection, &user_tag); + if (kr != KERN_SUCCESS) { + MACH_LOG(INFO, kr) << "mach_vm_region_recurse"; + *stack_region_size = 0; + return 0; + } + + if (region_base > stack_pointer) { + // There’s nothing mapped at the stack pointer’s address. Something may have + // trashed the stack pointer. Note that this shouldn’t happen for a normal + // stack guard region violation because the guard region is mapped but has + // VM_PROT_NONE protection. + *stack_region_size = 0; + return 0; + } + + mach_vm_address_t start_address = stack_pointer; + + if ((protection & VM_PROT_READ) == 0) { + // If the region isn’t readable, the stack pointer probably points to the + // guard region. Don’t include it as part of the stack, and don’t include + // anything at any lower memory address. The code below may still possibly + // find the real stack region at a memory address higher than this region. + start_address = region_base + region_size; + } else { + // If the ABI requires a red zone, adjust the region to include it if + // possible. + LocateRedZone(&start_address, ®ion_base, ®ion_size, user_tag); + + // Regardless of whether the ABI requires a red zone, capture up to + // kExtraCaptureSize additional bytes of stack, but only if present in the + // region that was already found. + constexpr mach_vm_size_t kExtraCaptureSize = 128; + start_address = std::max(start_address >= kExtraCaptureSize + ? start_address - kExtraCaptureSize + : start_address, + region_base); + + // Align start_address to a 16-byte boundary, which can help readers by + // ensuring that data is aligned properly. This could page-align instead, + // but that might be wasteful. + constexpr mach_vm_size_t kDesiredAlignment = 16; + start_address &= ~(kDesiredAlignment - 1); + DCHECK_GE(start_address, region_base); + } + + region_size -= (start_address - region_base); + region_base = start_address; + + mach_vm_size_t total_region_size = region_size; + + // The stack region may have gotten split up into multiple abutting regions. + // Try to coalesce them. This frequently happens for the main thread’s stack + // when setrlimit(RLIMIT_STACK, …) is called. It may also happen if a region + // is split up due to an mprotect() or vm_protect() call. + // + // Stack regions created by the kernel and the pthreads library will be marked + // with the VM_MEMORY_STACK user tag. Scanning for multiple adjacent regions + // with the same tag should find an entire stack region. Checking that the + // protection on individual regions is not VM_PROT_NONE should guarantee that + // this algorithm doesn’t collect map entries belonging to another thread’s + // stack: well-behaved stacks (such as those created by the kernel and the + // pthreads library) have VM_PROT_NONE guard regions at their low-address + // ends. + // + // Other stack regions may not be so well-behaved and thus if user_tag is not + // VM_MEMORY_STACK, the single region that was found is used as-is without + // trying to merge it with other adjacent regions. + if (user_tag == VM_MEMORY_STACK) { + mach_vm_address_t try_address = region_base; + mach_vm_address_t original_try_address; + + while (try_address += region_size, + original_try_address = try_address, + (kr = MachVMRegionRecurseDeepest(task_, + &try_address, + ®ion_size, + &depth, + &protection, + &user_tag) == KERN_SUCCESS) && + try_address == original_try_address && + (protection & VM_PROT_READ) != 0 && + user_tag == VM_MEMORY_STACK) { + total_region_size += region_size; + } + + if (kr != KERN_SUCCESS && kr != KERN_INVALID_ADDRESS) { + // Tolerate KERN_INVALID_ADDRESS because it will be returned when there + // are no more regions in the map at or above the specified |try_address|. + MACH_LOG(INFO, kr) << "mach_vm_region_recurse"; + } + } + + *stack_region_size = total_region_size; + return region_base; +} + +void ProcessReaderMac::LocateRedZone(mach_vm_address_t* const start_address, + mach_vm_address_t* const region_base, + mach_vm_address_t* const region_size, + const unsigned int user_tag) { +#if defined(ARCH_CPU_X86_FAMILY) + if (Is64Bit()) { + // x86_64 has a red zone. See AMD64 ABI 0.99.8, + // https://gitlab.com/x86-psABIs/x86-64-ABI/-/wikis/uploads/01de35b2c8adc7545de52604cc45d942/x86-64-psABI-2021-05-20.pdf#page=23. + // section 3.2.2, “The Stack Frameâ€. + constexpr mach_vm_size_t kRedZoneSize = 128; + mach_vm_address_t red_zone_base = + *start_address >= kRedZoneSize ? *start_address - kRedZoneSize : 0; + bool red_zone_ok = false; + if (red_zone_base >= *region_base) { + // The red zone is within the region already discovered. + red_zone_ok = true; + } else if (red_zone_base < *region_base && user_tag == VM_MEMORY_STACK) { + // Probe to see if there’s a region immediately below the one already + // discovered. + mach_vm_address_t red_zone_region_base = red_zone_base; + mach_vm_size_t red_zone_region_size; + natural_t red_zone_depth = 0; + vm_prot_t red_zone_protection; + unsigned int red_zone_user_tag; + kern_return_t kr = MachVMRegionRecurseDeepest(task_, + &red_zone_region_base, + &red_zone_region_size, + &red_zone_depth, + &red_zone_protection, + &red_zone_user_tag); + if (kr != KERN_SUCCESS) { + MACH_LOG(INFO, kr) << "mach_vm_region_recurse"; + *start_address = *region_base; + } else if (red_zone_region_base + red_zone_region_size == *region_base && + (red_zone_protection & VM_PROT_READ) != 0 && + red_zone_user_tag == user_tag) { + // The region containing the red zone is immediately below the region + // already found, it’s readable (not the guard region), and it has the + // same user tag as the region already found, so merge them. + red_zone_ok = true; + *region_base -= red_zone_region_size; + *region_size += red_zone_region_size; + } + } + + if (red_zone_ok) { + // Begin capturing from the base of the red zone (but not the entire + // region that encompasses the red zone). + *start_address = red_zone_base; + } else { + // The red zone would go lower into another region in memory, but no + // region was found. Memory can only be captured to an address as low as + // the base address of the region already found. + *start_address = *region_base; + } + } +#endif +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/mac/process_reader_mac.h b/shared/sentry/external/crashpad/snapshot/mac/process_reader_mac.h new file mode 100644 index 000000000..5b58a2918 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/process_reader_mac.h @@ -0,0 +1,273 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MAC_PROCESS_READER_MAC_H_ +#define CRASHPAD_SNAPSHOT_MAC_PROCESS_READER_MAC_H_ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "build/build_config.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/posix/process_info.h" +#include "util/process/process_memory_mac.h" + +#if defined(ARCH_CPU_32_BIT) || \ + (!defined(ARCH_CPU_ARM64) && \ + __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_15) +// There’s no 32-bit x86 environment on macOS 10.15 or later, and there’s no +// 32-bit ARM environment for macOS at all. +#define CRASHPAD_MAC_32_BIT_SUPPORT 1 +#endif // ARCH_CPU_32_BIT || (!ARCH_CPU_ARM64 && DT < 10.15) + +namespace crashpad { + +class MachOImageReader; + +//! \brief Accesses information about another process, identified by a Mach +//! task. +class ProcessReaderMac { + public: + //! \brief Contains information about a thread that belongs to a task + //! (process). + struct Thread { +#if defined(ARCH_CPU_X86_FAMILY) + union ThreadContext { + x86_thread_state64_t t64; + x86_thread_state32_t t32; + }; + union FloatContext { + x86_float_state64_t f64; + x86_float_state32_t f32; + }; + union DebugContext { + x86_debug_state64_t d64; + x86_debug_state32_t d32; + }; +#elif defined(ARCH_CPU_ARM64) + using ThreadContext = arm_thread_state64_t; + using FloatContext = arm_neon_state64_t; + using DebugContext = arm_debug_state64_t; +#endif + + Thread(); + ~Thread() {} + + ThreadContext thread_context; + FloatContext float_context; + DebugContext debug_context; + uint64_t id; + mach_vm_address_t stack_region_address; + mach_vm_size_t stack_region_size; + mach_vm_address_t thread_specific_data_address; + thread_t port; + int suspend_count; + int priority; + }; + + //! \brief Contains information about a module loaded into a process. + struct Module { + Module(); + ~Module(); + + //! \brief The pathname used to load the module from disk. + std::string name; + + //! \brief An image reader for the module. + //! + //! The lifetime of this MachOImageReader is scoped to the lifetime of the + //! ProcessReaderMac that created it. + //! + //! This field may be `nullptr` if a reader could not be created for the + //! module. + const MachOImageReader* reader; + + //! \brief The module’s timestamp. + //! + //! This field will be `0` if its value cannot be determined. It can only be + //! determined for images that are loaded by dyld, so it will be `0` for the + //! main executable and for dyld itself. + time_t timestamp; + }; + + ProcessReaderMac(); + + ProcessReaderMac(const ProcessReaderMac&) = delete; + ProcessReaderMac& operator=(const ProcessReaderMac&) = delete; + + ~ProcessReaderMac(); + + //! \brief Initializes this object. This method must be called before any + //! other. + //! + //! \param[in] task A send right to the target task’s task port. This object + //! does not take ownership of the send right. + //! + //! \return `true` on success, indicating that this object will respond + //! validly to further method calls. `false` on failure. On failure, no + //! further method calls should be made. + bool Initialize(task_t task); + + //! \return `true` if the target task is a 64-bit process. +#if defined(CRASHPAD_MAC_32_BIT_SUPPORT) || DOXYGEN + bool Is64Bit() const { return is_64_bit_; } +#else // CRASHPAD_MAC_32_BIT_SUPPORT + bool Is64Bit() const { return true; } +#endif // CRASHPAD_MAC_32_BIT_SUPPORT + + //! \return The target task’s process ID. + pid_t ProcessID() const { return process_info_.ProcessID(); } + + //! \return The target task’s parent process ID. + pid_t ParentProcessID() const { return process_info_.ParentProcessID(); } + + //! \brief Determines the target process’ start time. + //! + //! \param[out] start_time The time that the process started. + void StartTime(timeval* start_time) const; + + //! \brief Determines the target process’ execution time. + //! + //! \param[out] user_time The amount of time the process has executed code in + //! user mode. + //! \param[out] system_time The amount of time the process has executed code + //! in system mode. + //! + //! \return `true` on success, `false` on failure, with a warning logged. On + //! failure, \a user_time and \a system_time will be set to represent no + //! time spent executing code in user or system mode. + bool CPUTimes(timeval* user_time, timeval* system_time) const; + + //! \return Accesses the memory of the target task. + const ProcessMemoryMac* Memory() const { return &process_memory_; } + + //! \return The threads that are in the task (process). The first element (at + //! index `0`) corresponds to the main thread. + const std::vector& Threads(); + + //! \return The modules loaded in the process. The first element (at index + //! `0`) corresponds to the main executable, and the final element + //! corresponds to the dynamic loader, dyld. + const std::vector& Modules(); + + //! \brief Determines the location of the `dyld_all_image_infos` structure in + //! the process’ address space. + //! + //! This function is an internal implementation detail of Modules(), and + //! should not normally be used directly. It is exposed solely for use by test + //! code. + //! + //! \param[out] all_image_info_size The size of the `dyld_all_image_infos` + //! structure. Optional, may be `nullptr` if not required. + //! + //! \return The address of the `dyld_all_image_infos` structure in the + //! process’ address space, with \a all_image_info_size set appropriately. + //! On failure, returns `0` with a message logged. + mach_vm_address_t DyldAllImageInfo(mach_vm_size_t* all_image_info_size); + + task_t task_; // weak + + private: + //! Performs lazy initialization of the \a threads_ vector on behalf of + //! Threads(). + void InitializeThreads(); + + //! Performs lazy initialization of the \a modules_ vector on behalf of + //! Modules(). + void InitializeModules(); + + //! \brief Calculates the base address and size of the region used as a + //! thread’s stack. + //! + //! The region returned by this method may be formed by merging multiple + //! adjacent regions in a process’ memory map if appropriate. The base address + //! of the returned region may be lower than the \a stack_pointer passed in + //! when the ABI mandates a red zone below the stack pointer. + //! + //! \param[in] stack_pointer The stack pointer, referring to the top (lowest + //! address) of a thread’s stack. + //! \param[out] stack_region_size The size of the memory region used as the + //! thread’s stack. + //! + //! \return The base address (lowest address) of the memory region used as the + //! thread’s stack. + mach_vm_address_t CalculateStackRegion(mach_vm_address_t stack_pointer, + mach_vm_size_t* stack_region_size); + + //! \brief Adjusts the region for the red zone, if the ABI requires one. + //! + //! This method performs red zone calculation for CalculateStackRegion(). Its + //! parameters are local variables used within that method, and may be + //! modified as needed. + //! + //! Where a red zone is required, the region of memory captured for a thread’s + //! stack will be extended to include the red zone below the stack pointer, + //! provided that such memory is mapped, readable, and has the correct user + //! tag value. If these conditions cannot be met fully, as much of the red + //! zone will be captured as is possible while meeting these conditions. + //! + //! \param[in,out] start_address The base address of the region to begin + //! capturing stack memory from. On entry, \a start_address is the stack + //! pointer. On return, \a start_address may be decreased to encompass a + //! red zone. + //! \param[in,out] region_base The base address of the region that contains + //! stack memory. This is distinct from \a start_address in that \a + //! region_base will be page-aligned. On entry, \a region_base is the + //! base address of a region that contains \a start_address. On return, + //! if \a start_address is decremented and is outside of the region + //! originally described by \a region_base, \a region_base will also be + //! decremented appropriately. + //! \param[in,out] region_size The size of the region that contains stack + //! memory. This region begins at \a region_base. On return, if \a + //! region_base is decremented, \a region_size will be incremented + //! appropriately. + //! \param[in] user_tag The Mach VM system’s user tag for the region described + //! by the initial values of \a region_base and \a region_size. The red + //! zone will only be allowed to extend out of the region described by + //! these initial values if the user tag is appropriate for stack memory + //! and the expanded region has the same user tag value. + void LocateRedZone(mach_vm_address_t* start_address, + mach_vm_address_t* region_base, + mach_vm_address_t* region_size, + unsigned int user_tag); + + ProcessInfo process_info_; + std::vector threads_; // owns send rights + std::vector modules_; + std::vector> module_readers_; + ProcessMemoryMac process_memory_; + InitializationStateDcheck initialized_; + +#if defined(CRASHPAD_MAC_32_BIT_SUPPORT) + // This shadows a method of process_info_, but it’s accessed so frequently + // that it’s given a first-class field to save a call and a few bit operations + // on each access. + bool is_64_bit_; +#endif // CRASHPAD_MAC_32_BIT_SUPPORT + + bool initialized_threads_; + bool initialized_modules_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MAC_PROCESS_READER_MAC_H_ diff --git a/shared/sentry/external/crashpad/snapshot/mac/process_reader_mac_test.cc b/shared/sentry/external/crashpad/snapshot/mac/process_reader_mac_test.cc new file mode 100644 index 000000000..886895084 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/process_reader_mac_test.cc @@ -0,0 +1,949 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/mac/process_reader_mac.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "base/check_op.h" +#include "base/logging.h" +#include "base/mac/mach_logging.h" +#include "base/posix/eintr_wrapper.h" +#include "base/strings/stringprintf.h" +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "snapshot/mac/mach_o_image_reader.h" +#include "snapshot/mac/mach_o_image_segment_reader.h" +#include "test/errors.h" +#include "test/mac/dyld.h" +#include "test/mac/mach_errors.h" +#include "test/mac/mach_multiprocess.h" +#include "util/file/file_io.h" +#include "util/mac/mac_util.h" +#include "util/mach/mach_extensions.h" +#include "util/misc/from_pointer_cast.h" +#include "util/synchronization/semaphore.h" + +namespace crashpad { +namespace test { +namespace { + +constexpr char kDyldPath[] = "/usr/lib/dyld"; + +TEST(ProcessReaderMac, SelfBasic) { + ProcessReaderMac process_reader; + ASSERT_TRUE(process_reader.Initialize(mach_task_self())); + +#if !defined(ARCH_CPU_64_BITS) + EXPECT_FALSE(process_reader.Is64Bit()); +#else + EXPECT_TRUE(process_reader.Is64Bit()); +#endif + + EXPECT_EQ(process_reader.ProcessID(), getpid()); + EXPECT_EQ(process_reader.ParentProcessID(), getppid()); + + static constexpr char kTestMemory[] = "Some test memory"; + char buffer[std::size(kTestMemory)]; + ASSERT_TRUE(process_reader.Memory()->Read( + FromPointerCast(kTestMemory), + sizeof(kTestMemory), + &buffer)); + EXPECT_STREQ(kTestMemory, buffer); +} + +constexpr char kTestMemory[] = "Read me from another process"; + +class ProcessReaderChild final : public MachMultiprocess { + public: + ProcessReaderChild() : MachMultiprocess() {} + + ProcessReaderChild(const ProcessReaderChild&) = delete; + ProcessReaderChild& operator=(const ProcessReaderChild&) = delete; + + ~ProcessReaderChild() {} + + private: + void MachMultiprocessParent() override { + ProcessReaderMac process_reader; + ASSERT_TRUE(process_reader.Initialize(ChildTask())); + +#if !defined(ARCH_CPU_64_BITS) + EXPECT_FALSE(process_reader.Is64Bit()); +#else + EXPECT_TRUE(process_reader.Is64Bit()); +#endif + + EXPECT_EQ(process_reader.ParentProcessID(), getpid()); + EXPECT_EQ(process_reader.ProcessID(), ChildPID()); + + FileHandle read_handle = ReadPipeHandle(); + + mach_vm_address_t address; + CheckedReadFileExactly(read_handle, &address, sizeof(address)); + + std::string read_string; + ASSERT_TRUE(process_reader.Memory()->ReadCString(address, &read_string)); + EXPECT_EQ(read_string, kTestMemory); + } + + void MachMultiprocessChild() override { + FileHandle write_handle = WritePipeHandle(); + + mach_vm_address_t address = FromPointerCast(kTestMemory); + CheckedWriteFile(write_handle, &address, sizeof(address)); + + // Wait for the parent to signal that it’s OK to exit by closing its end of + // the pipe. + CheckedReadFileAtEOF(ReadPipeHandle()); + } +}; + +TEST(ProcessReaderMac, ChildBasic) { + ProcessReaderChild process_reader_child; + process_reader_child.Run(); +} + +// Returns a thread ID given a pthread_t. This wraps pthread_threadid_np() but +// that function has a cumbersome interface because it returns a success value. +// This function CHECKs success and returns the thread ID directly. +uint64_t PthreadToThreadID(pthread_t pthread) { + uint64_t thread_id; + errno = pthread_threadid_np(pthread, &thread_id); + PCHECK(errno == 0) << "pthread_threadid_np"; + return thread_id; +} + +TEST(ProcessReaderMac, SelfOneThread) { + ProcessReaderMac process_reader; + ASSERT_TRUE(process_reader.Initialize(mach_task_self())); + + const std::vector& threads = + process_reader.Threads(); + + // If other tests ran in this process previously, threads may have been + // created and may still be running. This check must look for at least one + // thread, not exactly one thread. + ASSERT_GE(threads.size(), 1u); + + EXPECT_EQ(threads[0].id, PthreadToThreadID(pthread_self())); + + thread_t thread_self = MachThreadSelf(); + EXPECT_EQ(threads[0].port, thread_self); + + EXPECT_EQ(threads[0].suspend_count, 0); +} + +class TestThreadPool { + public: + struct ThreadExpectation { + mach_vm_address_t stack_address; + int suspend_count; + }; + + TestThreadPool() : thread_infos_() {} + + TestThreadPool(const TestThreadPool&) = delete; + TestThreadPool& operator=(const TestThreadPool&) = delete; + + // Resumes suspended threads, signals each thread’s exit semaphore asking it + // to exit, and joins each thread, blocking until they have all exited. + ~TestThreadPool() { + for (const auto& thread_info : thread_infos_) { + thread_t thread_port = pthread_mach_thread_np(thread_info->pthread); + while (thread_info->suspend_count > 0) { + kern_return_t kr = thread_resume(thread_port); + EXPECT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "thread_resume"); + --thread_info->suspend_count; + } + } + + for (const auto& thread_info : thread_infos_) { + thread_info->exit_semaphore.Signal(); + } + + for (const auto& thread_info : thread_infos_) { + int rv = pthread_join(thread_info->pthread, nullptr); + CHECK_EQ(0, rv); + } + } + + // Starts |thread_count| threads and waits on each thread’s ready semaphore, + // so that when this function returns, all threads have been started and have + // all run to the point that they’ve signalled that they are ready. + void StartThreads(size_t thread_count) { + ASSERT_TRUE(thread_infos_.empty()); + + for (size_t thread_index = 0; thread_index < thread_count; ++thread_index) { + thread_infos_.push_back(std::make_unique()); + ThreadInfo* thread_info = thread_infos_.back().get(); + + int rv = pthread_create( + &thread_info->pthread, nullptr, ThreadMain, thread_info); + ASSERT_EQ(rv, 0); + } + + for (const auto& thread_info : thread_infos_) { + thread_info->ready_semaphore.Wait(); + } + + // If present, suspend the thread at indices 1 through 3 the same number of + // times as their index. This tests reporting of suspend counts. + for (size_t thread_index = 1; + thread_index < thread_infos_.size() && thread_index < 4; + ++thread_index) { + thread_t thread_port = + pthread_mach_thread_np(thread_infos_[thread_index]->pthread); + for (size_t suspend_count = 0; suspend_count < thread_index; + ++suspend_count) { + kern_return_t kr = thread_suspend(thread_port); + EXPECT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "thread_suspend"); + if (kr == KERN_SUCCESS) { + ++thread_infos_[thread_index]->suspend_count; + } + } + } + } + + uint64_t GetThreadInfo(size_t thread_index, ThreadExpectation* expectation) { + CHECK_LT(thread_index, thread_infos_.size()); + + const auto& thread_info = thread_infos_[thread_index]; + expectation->stack_address = thread_info->stack_address; + expectation->suspend_count = thread_info->suspend_count; + + return PthreadToThreadID(thread_info->pthread); + } + + private: + struct ThreadInfo { + ThreadInfo() + : pthread(nullptr), + stack_address(0), + ready_semaphore(0), + exit_semaphore(0), + suspend_count(0) {} + + ~ThreadInfo() {} + + // The thread’s ID, set at the time the thread is created. + pthread_t pthread; + + // An address somewhere within the thread’s stack. The thread sets this in + // its ThreadMain(). + mach_vm_address_t stack_address; + + // The worker thread signals ready_semaphore to indicate that it’s done + // setting up its ThreadInfo structure. The main thread waits on this + // semaphore before using any data that the worker thread is responsible for + // setting. + Semaphore ready_semaphore; + + // The worker thread waits on exit_semaphore to determine when it’s safe to + // exit. The main thread signals exit_semaphore when it no longer needs the + // worker thread. + Semaphore exit_semaphore; + + // The thread’s suspend count. + int suspend_count; + }; + + static void* ThreadMain(void* argument) { + ThreadInfo* thread_info = static_cast(argument); + + thread_info->stack_address = + FromPointerCast(&thread_info); + + thread_info->ready_semaphore.Signal(); + thread_info->exit_semaphore.Wait(); + + // Check this here after everything’s known to be synchronized, otherwise + // there’s a race between the parent thread storing this thread’s pthread_t + // in thread_info_pthread and this thread starting and attempting to access + // it. + CHECK_EQ(pthread_self(), thread_info->pthread); + + return nullptr; + } + + // This is a vector of pointers because the address of a ThreadInfo object is + // passed to each thread’s ThreadMain(), so they cannot move around in memory. + std::vector> thread_infos_; +}; + +using ThreadMap = std::map; + +// Verifies that all of the threads in |threads|, obtained from +// ProcessReaderMac, agree with the expectation in |thread_map|. If +// |tolerate_extra_threads| is true, |threads| is allowed to contain threads +// that are not listed in |thread_map|. This is useful when testing situations +// where code outside of the test’s control (such as system libraries) may start +// threads, or may have started threads prior to a test’s execution. +void ExpectSeveralThreads(ThreadMap* thread_map, + const std::vector& threads, + const bool tolerate_extra_threads) { + if (tolerate_extra_threads) { + ASSERT_GE(threads.size(), thread_map->size()); + } else { + ASSERT_EQ(threads.size(), thread_map->size()); + } + + for (size_t thread_index = 0; thread_index < threads.size(); ++thread_index) { + const ProcessReaderMac::Thread& thread = threads[thread_index]; + mach_vm_address_t thread_stack_region_end = + thread.stack_region_address + thread.stack_region_size; + + const auto& iterator = thread_map->find(thread.id); + if (!tolerate_extra_threads) { + // Make sure that the thread is in the expectation map. + ASSERT_NE(iterator, thread_map->end()); + } + + if (iterator != thread_map->end()) { + EXPECT_GE(iterator->second.stack_address, thread.stack_region_address); + EXPECT_LT(iterator->second.stack_address, thread_stack_region_end); + + EXPECT_EQ(thread.suspend_count, iterator->second.suspend_count); + + // Remove the thread from the expectation map since it’s already been + // found. This makes it easy to check for duplicate thread IDs, and makes + // it easy to check that all expected threads were found. + thread_map->erase(iterator); + } + + // Make sure that this thread’s ID, stack region, and port don’t conflict + // with any other thread’s. Each thread should have a unique value for its + // ID and port, and each should have its own stack that doesn’t touch any + // other thread’s stack. + for (size_t other_thread_index = 0; other_thread_index < threads.size(); + ++other_thread_index) { + if (other_thread_index == thread_index) { + continue; + } + + const ProcessReaderMac::Thread& other_thread = + threads[other_thread_index]; + + EXPECT_NE(other_thread.id, thread.id); + EXPECT_NE(other_thread.port, thread.port); + + mach_vm_address_t other_thread_stack_region_end = + other_thread.stack_region_address + other_thread.stack_region_size; + EXPECT_FALSE(thread.stack_region_address >= + other_thread.stack_region_address && + thread.stack_region_address < other_thread_stack_region_end); + EXPECT_FALSE(thread_stack_region_end > + other_thread.stack_region_address && + thread_stack_region_end <= other_thread_stack_region_end); + } + } + + // Make sure that each expected thread was found. + EXPECT_TRUE(thread_map->empty()); +} + +TEST(ProcessReaderMac, SelfSeveralThreads) { + // Set up the ProcessReaderMac here, before any other threads are running. + // This tests that the threads it returns are lazily initialized as a snapshot + // of the threads at the time of the first call to Threads(), and not at the + // time the ProcessReader was created or initialized. + ProcessReaderMac process_reader; + ASSERT_TRUE(process_reader.Initialize(mach_task_self())); + + TestThreadPool thread_pool; + constexpr size_t kChildThreads = 16; + ASSERT_NO_FATAL_FAILURE(thread_pool.StartThreads(kChildThreads)); + + // Build a map of all expected threads, keyed by each thread’s ID. The values + // are addresses that should lie somewhere within each thread’s stack. + ThreadMap thread_map; + const uint64_t self_thread_id = PthreadToThreadID(pthread_self()); + TestThreadPool::ThreadExpectation expectation; + expectation.stack_address = FromPointerCast(&thread_map); + expectation.suspend_count = 0; + thread_map[self_thread_id] = expectation; + for (size_t thread_index = 0; thread_index < kChildThreads; ++thread_index) { + uint64_t thread_id = thread_pool.GetThreadInfo(thread_index, &expectation); + + // There can’t be any duplicate thread IDs. + EXPECT_EQ(thread_map.count(thread_id), 0u); + + thread_map[thread_id] = expectation; + } + + const std::vector& threads = + process_reader.Threads(); + + // Other tests that have run previously may have resulted in the creation of + // threads that still exist, so pass true for |tolerate_extra_threads|. + ExpectSeveralThreads(&thread_map, threads, true); + + // When testing in-process, verify that when this thread shows up in the + // vector, it has the expected thread port, and that this thread port only + // shows up once. + thread_t thread_self = MachThreadSelf(); + bool found_thread_self = false; + for (const ProcessReaderMac::Thread& thread : threads) { + if (thread.port == thread_self) { + EXPECT_FALSE(found_thread_self); + found_thread_self = true; + EXPECT_EQ(thread.id, self_thread_id); + } + } + EXPECT_TRUE(found_thread_self); +} + +uint64_t GetThreadID() { + thread_identifier_info info; + mach_msg_type_number_t info_count = THREAD_IDENTIFIER_INFO_COUNT; + kern_return_t kr = thread_info(MachThreadSelf(), + THREAD_IDENTIFIER_INFO, + reinterpret_cast(&info), + &info_count); + MACH_CHECK(kr == KERN_SUCCESS, kr) << "thread_info"; + + return info.thread_id; +} + +class ProcessReaderThreadedChild final : public MachMultiprocess { + public: + explicit ProcessReaderThreadedChild(size_t thread_count) + : MachMultiprocess(), thread_count_(thread_count) {} + + ProcessReaderThreadedChild(const ProcessReaderThreadedChild&) = delete; + ProcessReaderThreadedChild& operator=(const ProcessReaderThreadedChild&) = + delete; + + ~ProcessReaderThreadedChild() {} + + private: + void MachMultiprocessParent() override { + ProcessReaderMac process_reader; + ASSERT_TRUE(process_reader.Initialize(ChildTask())); + + FileHandle read_handle = ReadPipeHandle(); + + // Build a map of all expected threads, keyed by each thread’s ID, and with + // addresses that should lie somewhere within each thread’s stack as values. + // These IDs and addresses all come from the child process via the pipe. + ThreadMap thread_map; + for (size_t thread_index = 0; thread_index < thread_count_ + 1; + ++thread_index) { + uint64_t thread_id; + CheckedReadFileExactly(read_handle, &thread_id, sizeof(thread_id)); + + TestThreadPool::ThreadExpectation expectation; + CheckedReadFileExactly(read_handle, + &expectation.stack_address, + sizeof(expectation.stack_address)); + CheckedReadFileExactly(read_handle, + &expectation.suspend_count, + sizeof(expectation.suspend_count)); + + // There can’t be any duplicate thread IDs. + EXPECT_EQ(thread_map.count(thread_id), 0u); + + thread_map[thread_id] = expectation; + } + + const std::vector& threads = + process_reader.Threads(); + + // The child shouldn’t have any threads other than its main thread and the + // ones it created in its pool, so pass false for |tolerate_extra_threads|. + ExpectSeveralThreads(&thread_map, threads, false); + } + + void MachMultiprocessChild() override { + TestThreadPool thread_pool; + ASSERT_NO_FATAL_FAILURE(thread_pool.StartThreads(thread_count_)); + + FileHandle write_handle = WritePipeHandle(); + + // This thread isn’t part of the thread pool, but the parent will be able + // to inspect it. Write an entry for it. + uint64_t thread_id = GetThreadID(); + + CheckedWriteFile(write_handle, &thread_id, sizeof(thread_id)); + + TestThreadPool::ThreadExpectation expectation; + expectation.stack_address = FromPointerCast(&thread_id); + expectation.suspend_count = 0; + + CheckedWriteFile(write_handle, + &expectation.stack_address, + sizeof(expectation.stack_address)); + CheckedWriteFile(write_handle, + &expectation.suspend_count, + sizeof(expectation.suspend_count)); + + // Write an entry for everything in the thread pool. + for (size_t thread_index = 0; thread_index < thread_count_; + ++thread_index) { + thread_id = thread_pool.GetThreadInfo(thread_index, &expectation); + + CheckedWriteFile(write_handle, &thread_id, sizeof(thread_id)); + CheckedWriteFile(write_handle, + &expectation.stack_address, + sizeof(expectation.stack_address)); + CheckedWriteFile(write_handle, + &expectation.suspend_count, + sizeof(expectation.suspend_count)); + } + + // Wait for the parent to signal that it’s OK to exit by closing its end of + // the pipe. + CheckedReadFileAtEOF(ReadPipeHandle()); + } + + size_t thread_count_; +}; + +TEST(ProcessReaderMac, ChildOneThread) { + // The main thread plus zero child threads equals one thread. + constexpr size_t kChildThreads = 0; + ProcessReaderThreadedChild process_reader_threaded_child(kChildThreads); + process_reader_threaded_child.Run(); +} + +TEST(ProcessReaderMac, ChildSeveralThreads) { + constexpr size_t kChildThreads = 64; + ProcessReaderThreadedChild process_reader_threaded_child(kChildThreads); + process_reader_threaded_child.Run(); +} + +template +T GetDyldFunction(const char* symbol) { + static void* dl_handle = []() -> void* { + Dl_info dl_info; + if (!dladdr(reinterpret_cast(dlopen), &dl_info)) { + LOG(ERROR) << "dladdr: failed"; + return nullptr; + } + + void* dl_handle = + dlopen(dl_info.dli_fname, RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD); + DCHECK(dl_handle) << "dlopen: " << dlerror(); + + return dl_handle; + }(); + + if (!dl_handle) { + return nullptr; + } + + return reinterpret_cast(dlsym(dl_handle, symbol)); +} + +void VerifyImageExistenceAndTimestamp(const char* path, time_t timestamp) { + const char* stat_path; + bool timestamp_may_be_0; + +#if __MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_16 + static auto _dyld_shared_cache_contains_path = + GetDyldFunction( + "_dyld_shared_cache_contains_path"); +#endif + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability" + if (_dyld_shared_cache_contains_path && + _dyld_shared_cache_contains_path(path)) { +#pragma clang diagnostic pop + // The timestamp will either match the timestamp of the dyld_shared_cache + // file in use, or be 0. + static const char* dyld_shared_cache_file_path = []() -> const char* { + auto dyld_shared_cache_file_path_f = + GetDyldFunction("dyld_shared_cache_file_path"); + + // dyld_shared_cache_file_path should always be present if + // _dyld_shared_cache_contains_path is. + DCHECK(dyld_shared_cache_file_path_f); + + const char* dyld_shared_cache_file_path = dyld_shared_cache_file_path_f(); + DCHECK(dyld_shared_cache_file_path); + + return dyld_shared_cache_file_path; + }(); + + stat_path = dyld_shared_cache_file_path; + timestamp_may_be_0 = true; + } else { + stat_path = path; + timestamp_may_be_0 = false; + } + + struct stat stat_buf; + int rv = stat(stat_path, &stat_buf); + EXPECT_EQ(rv, 0) << ErrnoMessage("stat"); + if (rv == 0 && (!timestamp_may_be_0 || timestamp != 0)) { + EXPECT_EQ(timestamp, stat_buf.st_mtime); + } +} + +// cl_kernels images (OpenCL kernels) are weird. They’re not ld output and don’t +// exist as files on disk. On OS X 10.10 and 10.11, their Mach-O structure isn’t +// perfect. They show up loaded into many executables, so these quirks should be +// tolerated. +// +// Create an object of this class to ensure that at least one cl_kernels image +// is present in a process, to be able to test that all of the process-reading +// machinery tolerates them. On systems where cl_kernels modules have known +// quirks, the image that an object of this class produces will also have those +// quirks. +// +// https://openradar.appspot.com/20239912 +class ScopedOpenCLNoOpKernel { + public: + ScopedOpenCLNoOpKernel() + : context_(nullptr), + program_(nullptr), + kernel_(nullptr), + success_(false) {} + + ScopedOpenCLNoOpKernel(const ScopedOpenCLNoOpKernel&) = delete; + ScopedOpenCLNoOpKernel& operator=(const ScopedOpenCLNoOpKernel&) = delete; + + ~ScopedOpenCLNoOpKernel() { + if (kernel_) { + cl_int rv = clReleaseKernel(kernel_); + EXPECT_EQ(rv, CL_SUCCESS) << "clReleaseKernel"; + } + + if (program_) { + cl_int rv = clReleaseProgram(program_); + EXPECT_EQ(rv, CL_SUCCESS) << "clReleaseProgram"; + } + + if (context_) { + cl_int rv = clReleaseContext(context_); + EXPECT_EQ(rv, CL_SUCCESS) << "clReleaseContext"; + } + } + + void SetUp() { + cl_platform_id platform_id; + cl_int rv = clGetPlatformIDs(1, &platform_id, nullptr); + ASSERT_EQ(rv, CL_SUCCESS) << "clGetPlatformIDs"; + +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_10 && \ + __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_10 + // cl_device_id is really available in OpenCL.framework back to 10.5, but in + // the 10.10 SDK and later, OpenCL.framework includes , + // which has its own cl_device_id that was introduced in 10.10. That + // triggers erroneous availability warnings. +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunguarded-availability" +#define DISABLED_WUNGUARDED_AVAILABILITY +#endif // SDK >= 10.10 && DT < 10.10 + // Use CL_DEVICE_TYPE_CPU to ensure that the kernel would execute on the + // CPU. This is the only device type that a cl_kernels image will be created + // for. + cl_device_id device_id; +#if defined(DISABLED_WUNGUARDED_AVAILABILITY) +#pragma clang diagnostic pop +#undef DISABLED_WUNGUARDED_AVAILABILITY +#endif // DISABLED_WUNGUARDED_AVAILABILITY + rv = + clGetDeviceIDs(platform_id, CL_DEVICE_TYPE_CPU, 1, &device_id, nullptr); +#if defined(ARCH_CPU_ARM64) + // CL_DEVICE_TYPE_CPU doesn’t seem to work at all on arm64, meaning that + // these weird OpenCL modules probably don’t show up there at all. Keep this + // test even on arm64 in case this ever does start working. + if (rv == CL_INVALID_VALUE) { + return; + } +#endif // ARCH_CPU_ARM64 + ASSERT_EQ(rv, CL_SUCCESS) << "clGetDeviceIDs"; + + context_ = clCreateContext(nullptr, 1, &device_id, nullptr, nullptr, &rv); + ASSERT_EQ(rv, CL_SUCCESS) << "clCreateContext"; + + // The goal of the program in |sources| is to produce a cl_kernels image + // that doesn’t strictly conform to Mach-O expectations. On OS X 10.10, + // cl_kernels modules show up with an __LD,__compact_unwind section, showing + // up in the __TEXT segment. MachOImageSegmentReader would normally reject + // modules for this problem, but a special exception is made when this + // occurs in cl_kernels images. This portion of the test is aimed at making + // sure that this exception works correctly. + // + // A true no-op program doesn’t actually produce unwind data, so there would + // be no errant __LD,__compact_unwind section on 10.10, and the test + // wouldn’t be complete. This simple no-op, which calls a built-in function, + // does produce unwind data provided optimization is disabled. + // "-cl-opt-disable" is given to clBuildProgram() below. + const char* sources[] = { + "__kernel void NoOp(void) {barrier(CLK_LOCAL_MEM_FENCE);}", + }; + const size_t source_lengths[] = { + strlen(sources[0]), + }; + static_assert(std::size(sources) == std::size(source_lengths), + "arrays must be parallel"); + + program_ = clCreateProgramWithSource( + context_, std::size(sources), sources, source_lengths, &rv); + ASSERT_EQ(rv, CL_SUCCESS) << "clCreateProgramWithSource"; + + rv = clBuildProgram( + program_, 1, &device_id, "-cl-opt-disable", nullptr, nullptr); + ASSERT_EQ(rv, CL_SUCCESS) << "clBuildProgram"; + + kernel_ = clCreateKernel(program_, "NoOp", &rv); + ASSERT_EQ(rv, CL_SUCCESS) << "clCreateKernel"; + + success_ = true; + } + + bool success() const { return success_; } + + private: + cl_context context_; + cl_program program_; + cl_kernel kernel_; + bool success_; +}; + +// Although Mac OS X 10.6 has OpenCL and can compile and execute OpenCL code, +// OpenCL kernels that run on the CPU do not result in cl_kernels images +// appearing on that OS version. +bool ExpectCLKernels() { + return __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_7 || + MacOSVersionNumber() >= 10'07'00; +} + +TEST(ProcessReaderMac, SelfModules) { + ScopedOpenCLNoOpKernel ensure_cl_kernels; + ASSERT_NO_FATAL_FAILURE(ensure_cl_kernels.SetUp()); + + ProcessReaderMac process_reader; + ASSERT_TRUE(process_reader.Initialize(mach_task_self())); + + uint32_t dyld_image_count = _dyld_image_count(); + const std::vector& modules = + process_reader.Modules(); + + // There needs to be at least an entry for the main executable, for a dylib, + // and for dyld. + ASSERT_GE(modules.size(), 3u); + + // dyld_image_count doesn’t include an entry for dyld itself, but |modules| + // does. + ASSERT_EQ(modules.size(), dyld_image_count + 1); + + bool found_cl_kernels = false; + for (uint32_t index = 0; index < dyld_image_count; ++index) { + SCOPED_TRACE(base::StringPrintf( + "index %u, name %s", index, modules[index].name.c_str())); + + const char* dyld_image_name = _dyld_get_image_name(index); + EXPECT_EQ(modules[index].name, dyld_image_name); + ASSERT_TRUE(modules[index].reader); + EXPECT_EQ( + modules[index].reader->Address(), + FromPointerCast(_dyld_get_image_header(index))); + + bool expect_timestamp; + if (index == 0) { + // dyld didn’t load the main executable, so it couldn’t record its + // timestamp, and it is reported as 0. + EXPECT_EQ(modules[index].timestamp, 0); + } else if (IsMalformedCLKernelsModule(modules[index].reader->FileType(), + modules[index].name, + &expect_timestamp)) { + // cl_kernels doesn’t exist as a file, but may still have a timestamp. + if (!expect_timestamp) { + EXPECT_EQ(modules[index].timestamp, 0); + } else { + EXPECT_NE(modules[index].timestamp, 0); + } + found_cl_kernels = true; + } else { + // Hope that the module didn’t change on disk. + VerifyImageExistenceAndTimestamp(dyld_image_name, + modules[index].timestamp); + } + } + + EXPECT_EQ(found_cl_kernels, ExpectCLKernels() && ensure_cl_kernels.success()); + + size_t index = modules.size() - 1; + EXPECT_EQ(modules[index].name, kDyldPath); + + // dyld didn’t load itself either, so it couldn’t record its timestamp, and it + // is also reported as 0. + EXPECT_EQ(modules[index].timestamp, 0); + + const dyld_all_image_infos* dyld_image_infos = DyldGetAllImageInfos(); + if (dyld_image_infos->version >= 2) { + ASSERT_TRUE(modules[index].reader); + EXPECT_EQ(modules[index].reader->Address(), + FromPointerCast( + dyld_image_infos->dyldImageLoadAddress)); + } +} + +class ProcessReaderModulesChild final : public MachMultiprocess { + public: + explicit ProcessReaderModulesChild(bool ensure_cl_kernels_success) + : MachMultiprocess(), + ensure_cl_kernels_success_(ensure_cl_kernels_success) {} + + ProcessReaderModulesChild(const ProcessReaderModulesChild&) = delete; + ProcessReaderModulesChild& operator=(const ProcessReaderModulesChild&) = + delete; + + ~ProcessReaderModulesChild() {} + + private: + void MachMultiprocessParent() override { + ProcessReaderMac process_reader; + ASSERT_TRUE(process_reader.Initialize(ChildTask())); + + const std::vector& modules = + process_reader.Modules(); + + // There needs to be at least an entry for the main executable, for a dylib, + // and for dyld. + ASSERT_GE(modules.size(), 3u); + + FileHandle read_handle = ReadPipeHandle(); + + uint32_t expect_modules; + CheckedReadFileExactly( + read_handle, &expect_modules, sizeof(expect_modules)); + + ASSERT_EQ(modules.size(), expect_modules); + + bool found_cl_kernels = false; + for (size_t index = 0; index < modules.size(); ++index) { + SCOPED_TRACE(base::StringPrintf( + "index %zu, name %s", index, modules[index].name.c_str())); + + uint32_t expect_name_length; + CheckedReadFileExactly( + read_handle, &expect_name_length, sizeof(expect_name_length)); + + // The NUL terminator is not read. + std::string expect_name(expect_name_length, '\0'); + CheckedReadFileExactly(read_handle, &expect_name[0], expect_name_length); + EXPECT_EQ(modules[index].name, expect_name); + + mach_vm_address_t expect_address; + CheckedReadFileExactly( + read_handle, &expect_address, sizeof(expect_address)); + ASSERT_TRUE(modules[index].reader); + EXPECT_EQ(modules[index].reader->Address(), expect_address); + + bool expect_timestamp; + if (index == 0 || index == modules.size() - 1) { + // dyld didn’t load the main executable or itself, so it couldn’t record + // these timestamps, and they are reported as 0. + EXPECT_EQ(modules[index].timestamp, 0); + } else if (IsMalformedCLKernelsModule(modules[index].reader->FileType(), + modules[index].name, + &expect_timestamp)) { + // cl_kernels doesn’t exist as a file, but may still have a timestamp. + if (!expect_timestamp) { + EXPECT_EQ(modules[index].timestamp, 0); + } else { + EXPECT_NE(modules[index].timestamp, 0); + } + found_cl_kernels = true; + } else { + // Hope that the module didn’t change on disk. + VerifyImageExistenceAndTimestamp(expect_name.c_str(), + modules[index].timestamp); + } + } + + EXPECT_EQ(found_cl_kernels, + ExpectCLKernels() && ensure_cl_kernels_success_); + } + + void MachMultiprocessChild() override { + FileHandle write_handle = WritePipeHandle(); + + uint32_t dyld_image_count = _dyld_image_count(); + const dyld_all_image_infos* dyld_image_infos = DyldGetAllImageInfos(); + + uint32_t write_image_count = dyld_image_count; + if (dyld_image_infos->version >= 2) { + // dyld_image_count doesn’t include an entry for dyld itself, but one will + // be written. + ++write_image_count; + } + + CheckedWriteFile( + write_handle, &write_image_count, sizeof(write_image_count)); + + for (size_t index = 0; index < write_image_count; ++index) { + const char* dyld_image_name; + mach_vm_address_t dyld_image_address; + + if (index < dyld_image_count) { + dyld_image_name = _dyld_get_image_name(index); + dyld_image_address = + FromPointerCast(_dyld_get_image_header(index)); + } else { + dyld_image_name = kDyldPath; + dyld_image_address = FromPointerCast( + dyld_image_infos->dyldImageLoadAddress); + } + + uint32_t dyld_image_name_length = strlen(dyld_image_name); + CheckedWriteFile(write_handle, + &dyld_image_name_length, + sizeof(dyld_image_name_length)); + + // The NUL terminator is not written. + CheckedWriteFile(write_handle, dyld_image_name, dyld_image_name_length); + + CheckedWriteFile( + write_handle, &dyld_image_address, sizeof(dyld_image_address)); + } + + // Wait for the parent to signal that it’s OK to exit by closing its end of + // the pipe. + CheckedReadFileAtEOF(ReadPipeHandle()); + } + + bool ensure_cl_kernels_success_; +}; + +TEST(ProcessReaderMac, ChildModules) { + ScopedOpenCLNoOpKernel ensure_cl_kernels; + ASSERT_NO_FATAL_FAILURE(ensure_cl_kernels.SetUp()); + + ProcessReaderModulesChild process_reader_modules_child( + ensure_cl_kernels.success()); + process_reader_modules_child.Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/mac/process_snapshot_mac.cc b/shared/sentry/external/crashpad/snapshot/mac/process_snapshot_mac.cc new file mode 100644 index 000000000..2a0884b98 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/process_snapshot_mac.cc @@ -0,0 +1,250 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/mac/process_snapshot_mac.h" + +#include + +#include "base/logging.h" +#include "util/misc/tri_state.h" + +namespace crashpad { + +ProcessSnapshotMac::ProcessSnapshotMac() + : ProcessSnapshot(), + system_(), + threads_(), + modules_(), + exception_(), + process_reader_(), + report_id_(), + client_id_(), + annotations_simple_map_(), + snapshot_time_(), + initialized_() { +} + +ProcessSnapshotMac::~ProcessSnapshotMac() { +} + +bool ProcessSnapshotMac::Initialize(task_t task) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + if (gettimeofday(&snapshot_time_, nullptr) != 0) { + PLOG(ERROR) << "gettimeofday"; + return false; + } + + if (!process_reader_.Initialize(task)) { + return false; + } + + client_id_.InitializeToZero(); + system_.Initialize(&process_reader_, &snapshot_time_); + + InitializeThreads(); + InitializeModules(); + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +bool ProcessSnapshotMac::InitializeException( + exception_behavior_t behavior, + thread_t exception_thread, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t flavor, + ConstThreadState state, + mach_msg_type_number_t state_count) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + DCHECK(!exception_); + + exception_.reset(new internal::ExceptionSnapshotMac()); + if (!exception_->Initialize(&process_reader_, + behavior, + exception_thread, + exception, + code, + code_count, + flavor, + state, + state_count)) { + exception_.reset(); + return false; + } + + return true; +} + +void ProcessSnapshotMac::GetCrashpadOptions( + CrashpadInfoClientOptions* options) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + CrashpadInfoClientOptions local_options; + + for (const auto& module : modules_) { + CrashpadInfoClientOptions module_options; + module->GetCrashpadOptions(&module_options); + + if (local_options.crashpad_handler_behavior == TriState::kUnset) { + local_options.crashpad_handler_behavior = + module_options.crashpad_handler_behavior; + } + if (local_options.system_crash_reporter_forwarding == TriState::kUnset) { + local_options.system_crash_reporter_forwarding = + module_options.system_crash_reporter_forwarding; + } + if (local_options.gather_indirectly_referenced_memory == TriState::kUnset) { + local_options.gather_indirectly_referenced_memory = + module_options.gather_indirectly_referenced_memory; + local_options.indirectly_referenced_memory_cap = + module_options.indirectly_referenced_memory_cap; + } + + // If non-default values have been found for all options, the loop can end + // early. + if (local_options.crashpad_handler_behavior != TriState::kUnset && + local_options.system_crash_reporter_forwarding != TriState::kUnset && + local_options.gather_indirectly_referenced_memory != TriState::kUnset) { + break; + } + } + + *options = local_options; +} + +pid_t ProcessSnapshotMac::ProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return process_reader_.ProcessID(); +} + +pid_t ProcessSnapshotMac::ParentProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return process_reader_.ParentProcessID(); +} + +void ProcessSnapshotMac::SnapshotTime(timeval* snapshot_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *snapshot_time = snapshot_time_; +} + +void ProcessSnapshotMac::ProcessStartTime(timeval* start_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + process_reader_.StartTime(start_time); +} + +void ProcessSnapshotMac::ProcessCPUTimes(timeval* user_time, + timeval* system_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + process_reader_.CPUTimes(user_time, system_time); +} + +void ProcessSnapshotMac::ReportID(UUID* report_id) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *report_id = report_id_; +} + +void ProcessSnapshotMac::ClientID(UUID* client_id) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *client_id = client_id_; +} + +const std::map& +ProcessSnapshotMac::AnnotationsSimpleMap() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return annotations_simple_map_; +} + +const SystemSnapshot* ProcessSnapshotMac::System() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &system_; +} + +std::vector ProcessSnapshotMac::Threads() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector threads; + for (const auto& thread : threads_) { + threads.push_back(thread.get()); + } + return threads; +} + +std::vector ProcessSnapshotMac::Modules() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector modules; + for (const auto& module : modules_) { + modules.push_back(module.get()); + } + return modules; +} + +std::vector ProcessSnapshotMac::UnloadedModules() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector(); +} + +const ExceptionSnapshot* ProcessSnapshotMac::Exception() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_.get(); +} + +std::vector ProcessSnapshotMac::MemoryMap() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector(); +} + +std::vector ProcessSnapshotMac::Handles() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector(); +} + +std::vector ProcessSnapshotMac::ExtraMemory() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector(); +} + +const ProcessMemory* ProcessSnapshotMac::Memory() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return process_reader_.Memory(); +} + +void ProcessSnapshotMac::InitializeThreads() { + const std::vector& process_reader_threads = + process_reader_.Threads(); + for (const ProcessReaderMac::Thread& process_reader_thread : + process_reader_threads) { + auto thread = std::make_unique(); + if (thread->Initialize(&process_reader_, process_reader_thread)) { + threads_.push_back(std::move(thread)); + } + } +} + +void ProcessSnapshotMac::InitializeModules() { + const std::vector& process_reader_modules = + process_reader_.Modules(); + for (const ProcessReaderMac::Module& process_reader_module : + process_reader_modules) { + auto module = std::make_unique(); + if (module->Initialize(&process_reader_, process_reader_module)) { + modules_.push_back(std::move(module)); + } + } +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/mac/process_snapshot_mac.h b/shared/sentry/external/crashpad/snapshot/mac/process_snapshot_mac.h new file mode 100644 index 000000000..59239102c --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/process_snapshot_mac.h @@ -0,0 +1,160 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MAC_PROCESS_SNAPSHOT_MAC_H_ +#define CRASHPAD_SNAPSHOT_MAC_PROCESS_SNAPSHOT_MAC_H_ + +#include +#include +#include + +#include +#include +#include +#include + +#include "client/crashpad_info.h" +#include "snapshot/crashpad_info_client_options.h" +#include "snapshot/exception_snapshot.h" +#include "snapshot/mac/exception_snapshot_mac.h" +#include "snapshot/mac/module_snapshot_mac.h" +#include "snapshot/mac/process_reader_mac.h" +#include "snapshot/mac/system_snapshot_mac.h" +#include "snapshot/mac/thread_snapshot_mac.h" +#include "snapshot/memory_map_region_snapshot.h" +#include "snapshot/module_snapshot.h" +#include "snapshot/process_snapshot.h" +#include "snapshot/system_snapshot.h" +#include "snapshot/thread_snapshot.h" +#include "snapshot/unloaded_module_snapshot.h" +#include "util/mach/mach_extensions.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/misc/uuid.h" + +namespace crashpad { + +//! \brief A ProcessSnapshot of a running (or crashed) process running on a +//! macOS system. +class ProcessSnapshotMac final : public ProcessSnapshot { + public: + ProcessSnapshotMac(); + + ProcessSnapshotMac(const ProcessSnapshotMac&) = delete; + ProcessSnapshotMac& operator=(const ProcessSnapshotMac&) = delete; + + ~ProcessSnapshotMac() override; + + //! \brief Initializes the object. + //! + //! \param[in] task The task to create a snapshot from. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(task_t task); + + //! \brief Initializes the object’s exception. + //! + //! This populates the data to be returned by Exception(). The parameters may + //! be passed directly through from a Mach exception handler. + //! + //! This method must not be called until after a successful call to + //! Initialize(). + //! + //! \return `true` if the exception information could be initialized, `false` + //! otherwise with an appropriate message logged. When this method returns + //! `false`, the ProcessSnapshotMac object’s validity remains unchanged. + bool InitializeException(exception_behavior_t behavior, + thread_t exception_thread, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t flavor, + ConstThreadState state, + mach_msg_type_number_t state_count); + + //! \brief Sets the value to be returned by ReportID(). + //! + //! On macOS, the crash report ID is under the control of the snapshot + //! producer, which may call this method to set the report ID. If this is not + //! done, ReportID() will return an identifier consisting entirely of zeroes. + void SetReportID(const UUID& report_id) { report_id_ = report_id; } + + //! \brief Sets the value to be returned by ClientID(). + //! + //! On macOS, the client ID is under the control of the snapshot producer, + //! which may call this method to set the client ID. If this is not done, + //! ClientID() will return an identifier consisting entirely of zeroes. + void SetClientID(const UUID& client_id) { client_id_ = client_id; } + + //! \brief Sets the value to be returned by AnnotationsSimpleMap(). + //! + //! On macOS, all process annotations are under the control of the snapshot + //! producer, which may call this method to establish these annotations. + //! Contrast this with module annotations, which are under the control of the + //! process being snapshotted. + void SetAnnotationsSimpleMap( + const std::map& annotations_simple_map) { + annotations_simple_map_ = annotations_simple_map; + } + + //! \brief Returns options from CrashpadInfo structures found in modules in + //! the process. + //! + //! \param[out] options Options set in CrashpadInfo structures in modules in + //! the process. + void GetCrashpadOptions(CrashpadInfoClientOptions* options); + + // ProcessSnapshot: + + pid_t ProcessID() const override; + pid_t ParentProcessID() const override; + void SnapshotTime(timeval* snapshot_time) const override; + void ProcessStartTime(timeval* start_time) const override; + void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override; + void ReportID(UUID* report_id) const override; + void ClientID(UUID* client_id) const override; + const std::map& AnnotationsSimpleMap() + const override; + const SystemSnapshot* System() const override; + std::vector Threads() const override; + std::vector Modules() const override; + std::vector UnloadedModules() const override; + const ExceptionSnapshot* Exception() const override; + std::vector MemoryMap() const override; + std::vector Handles() const override; + std::vector ExtraMemory() const override; + const ProcessMemory* Memory() const override; + + private: + // Initializes threads_ on behalf of Initialize(). + void InitializeThreads(); + + // Initializes modules_ on behalf of Initialize(). + void InitializeModules(); + + internal::SystemSnapshotMac system_; + std::vector> threads_; + std::vector> modules_; + std::unique_ptr exception_; + ProcessReaderMac process_reader_; + UUID report_id_; + UUID client_id_; + std::map annotations_simple_map_; + timeval snapshot_time_; + InitializationStateDcheck initialized_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MAC_PROCESS_SNAPSHOT_MAC_H_ diff --git a/shared/sentry/external/crashpad/snapshot/mac/process_types.cc b/shared/sentry/external/crashpad/snapshot/mac/process_types.cc new file mode 100644 index 000000000..c91b697d1 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/process_types.cc @@ -0,0 +1,377 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/mac/process_types.h" + +#include +#include +#include + +#include +#include + +#include "snapshot/mac/process_types/internal.h" +#include "util/process/process_memory_mac.h" + +namespace crashpad { +namespace { + +// Assign() is used by each flavor's ReadInto implementation to copy data from a +// specific struct to a generic struct. For fundamental types, the assignment +// operator suffices. For other types such as arrays, an explicit Assign +// specialization is needed, typically performing a copy. + +template +inline void Assign(DestinationType* destination, const SourceType& source) { + *destination = source; +} + +template +inline void Assign(Type* destination, const Type& source) { + memcpy(destination, &source, sizeof(source)); +} + +template <> +inline void Assign( + process_types::internal::Reserved32_32Only64* destination, + const process_types::internal::Reserved32_32Only32& source) { + // Reserved32_32Only32 carries no data and has no storage in the 64-bit + // structure. +} + +template <> +inline void Assign( + process_types::internal::Reserved32_64Only64* destination, + const process_types::internal::Reserved32_64Only32& source) { + // Reserved32_64Only32 carries no data. + *destination = 0; +} + +template <> +inline void Assign( + process_types::internal::Reserved64_64Only64* destination, + const process_types::internal::Reserved64_64Only32& source) { + // Reserved64_64Only32 carries no data. + *destination = 0; +} + +using UInt32Array4 = uint32_t[4]; +using UInt64Array4 = uint64_t[4]; +template <> +inline void Assign(UInt64Array4* destination, + const UInt32Array4& source) { + for (size_t index = 0; index < std::size(source); ++index) { + (*destination)[index] = source[index]; + } +} + +} // namespace +} // namespace crashpad + +// Implement the generic crashpad::process_types::struct_name ReadInto(), which +// delegates to the templatized ReadIntoInternal(), which reads the specific +// struct type from the remote process and genericizes it. Also implement +// crashpad::process_types::internal::struct_name<> GenericizeInto(), which +// operates on each member in the struct. +#define PROCESS_TYPE_STRUCT_IMPLEMENT 1 + +#define PROCESS_TYPE_STRUCT_BEGIN(struct_name) \ + namespace crashpad { \ + namespace process_types { \ + \ + /* static */ \ + size_t struct_name::ExpectedSize(ProcessReaderMac* process_reader) { \ + if (!process_reader->Is64Bit()) { \ + return internal::struct_name::Size(); \ + } else { \ + return internal::struct_name::Size(); \ + } \ + } \ + \ + /* static */ \ + bool struct_name::ReadInto(ProcessReaderMac* process_reader, \ + mach_vm_address_t address, \ + struct_name* generic) { \ + if (!process_reader->Is64Bit()) { \ + return ReadIntoInternal>( \ + process_reader, address, generic); \ + } else { \ + return ReadIntoInternal>( \ + process_reader, address, generic); \ + } \ + } \ + \ + /* static */ \ + template \ + bool struct_name::ReadIntoInternal(ProcessReaderMac* process_reader, \ + mach_vm_address_t address, \ + struct_name* generic) { \ + T specific; \ + if (!specific.Read(process_reader, address)) { \ + return false; \ + } \ + specific.GenericizeInto(generic, &generic->size_); \ + return true; \ + } \ + \ + namespace internal { \ + \ + template \ + void struct_name::GenericizeInto( \ + process_types::struct_name* generic, \ + size_t* specific_size) { \ + *specific_size = Size(); + +#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...) \ + Assign(&generic->member_name, member_name); + +#define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field) + +#define PROCESS_TYPE_STRUCT_SIZED(struct_name, size_field) + +#define PROCESS_TYPE_STRUCT_END(struct_name) \ + } \ + } /* namespace internal */ \ + } /* namespace process_types */ \ + } /* namespace crashpad */ + +#include "snapshot/mac/process_types/all.proctype" + +#undef PROCESS_TYPE_STRUCT_BEGIN +#undef PROCESS_TYPE_STRUCT_MEMBER +#undef PROCESS_TYPE_STRUCT_VERSIONED +#undef PROCESS_TYPE_STRUCT_SIZED +#undef PROCESS_TYPE_STRUCT_END +#undef PROCESS_TYPE_STRUCT_IMPLEMENT + +// Implement the specific crashpad::process_types::internal::struct_name<> +// ReadInto(). The default implementation simply reads the struct from the +// remote process. This is separated from other method implementations because +// some types may wish to provide custom readers. This can be done by guarding +// such types’ proctype definitions against this macro and providing custom +// implementations in snapshot/mac/process_types/custom.cc. +#define PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO 1 + +#define PROCESS_TYPE_STRUCT_BEGIN(struct_name) \ + namespace crashpad { \ + namespace process_types { \ + namespace internal { \ + \ + /* static */ \ + template \ + bool struct_name::ReadInto(ProcessReaderMac* process_reader, \ + mach_vm_address_t address, \ + struct_name* specific) { \ + return process_reader->Memory()->Read( \ + address, sizeof(*specific), specific); \ + } \ + } /* namespace internal */ \ + } /* namespace process_types */ \ + } /* namespace crashpad */ + +#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...) + +#define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field) + +#define PROCESS_TYPE_STRUCT_SIZED(struct_name, size_field) + +#define PROCESS_TYPE_STRUCT_END(struct_name) + +#include "snapshot/mac/process_types/all.proctype" + +#undef PROCESS_TYPE_STRUCT_BEGIN +#undef PROCESS_TYPE_STRUCT_MEMBER +#undef PROCESS_TYPE_STRUCT_VERSIONED +#undef PROCESS_TYPE_STRUCT_SIZED +#undef PROCESS_TYPE_STRUCT_END +#undef PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO + +// Implement the array operations. These are separated from other method +// implementations because some types are variable-length and are never stored +// as direct-access arrays. It would be incorrect to provide reader +// implementations for such types. Types that wish to suppress array operations +// can do so by guarding their proctype definitions against this macro. +#define PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY 1 + +#define PROCESS_TYPE_STRUCT_BEGIN(struct_name) \ + namespace crashpad { \ + namespace process_types { \ + namespace internal { \ + \ + /* static */ \ + template \ + bool struct_name::ReadArrayInto(ProcessReaderMac* process_reader, \ + mach_vm_address_t address, \ + size_t count, \ + struct_name* specific) { \ + return process_reader->Memory()->Read( \ + address, count * sizeof(struct_name), specific); \ + } \ + \ + } /* namespace internal */ \ + \ + /* static */ \ + bool struct_name::ReadArrayInto(ProcessReaderMac* process_reader, \ + mach_vm_address_t address, \ + size_t count, \ + struct_name* generic) { \ + if (!process_reader->Is64Bit()) { \ + return ReadArrayIntoInternal>( \ + process_reader, address, count, generic); \ + } else { \ + return ReadArrayIntoInternal>( \ + process_reader, address, count, generic); \ + } \ + return true; \ + } \ + \ + /* static */ \ + template \ + bool struct_name::ReadArrayIntoInternal(ProcessReaderMac* process_reader, \ + mach_vm_address_t address, \ + size_t count, \ + struct_name* generic) { \ + std::unique_ptr specific(new T[count]); \ + if (!T::ReadArrayInto(process_reader, address, count, &specific[0])) { \ + return false; \ + } \ + for (size_t index = 0; index < count; ++index) { \ + specific[index].GenericizeInto(&generic[index], &generic[index].size_); \ + } \ + return true; \ + } \ + } /* namespace process_types */ \ + } /* namespace crashpad */ + +#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...) + +#define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field) + +#define PROCESS_TYPE_STRUCT_SIZED(struct_name, size_field) + +#define PROCESS_TYPE_STRUCT_END(struct_name) + +#include "snapshot/mac/process_types/all.proctype" + +#undef PROCESS_TYPE_STRUCT_BEGIN +#undef PROCESS_TYPE_STRUCT_MEMBER +#undef PROCESS_TYPE_STRUCT_VERSIONED +#undef PROCESS_TYPE_STRUCT_SIZED +#undef PROCESS_TYPE_STRUCT_END +#undef PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY + +// Implement the generic crashpad::process_types::struct_name +// ExpectedSizeForVersion(), which delegates to the templatized +// ExpectedSizeForVersion(), which returns the expected size of a versioned +// structure given a version parameter. This is only implemented for structures +// that use PROCESS_TYPE_STRUCT_VERSIONED(), and implementations of the internal +// templatized functions must be provided in +// snapshot/mac/process_types/custom.cc. +#define PROCESS_TYPE_STRUCT_IMPLEMENT_VERSIONED 1 + +#define PROCESS_TYPE_STRUCT_BEGIN(struct_name) + +#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...) + +#define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field) \ + namespace crashpad { \ + namespace process_types { \ + \ + /* static */ \ + size_t struct_name::ExpectedSizeForVersion( \ + ProcessReaderMac* process_reader, \ + decltype(struct_name::version_field) version) { \ + if (!process_reader->Is64Bit()) { \ + return internal::struct_name< \ + internal::Traits32>::ExpectedSizeForVersion(version); \ + } else { \ + return internal::struct_name< \ + internal::Traits64>::ExpectedSizeForVersion(version); \ + } \ + } \ + \ + } /* namespace process_types */ \ + } /* namespace crashpad */ + +#define PROCESS_TYPE_STRUCT_SIZED(struct_name, size_field) + +#define PROCESS_TYPE_STRUCT_END(struct_name) + +#include "snapshot/mac/process_types/all.proctype" + +#undef PROCESS_TYPE_STRUCT_BEGIN +#undef PROCESS_TYPE_STRUCT_MEMBER +#undef PROCESS_TYPE_STRUCT_VERSIONED +#undef PROCESS_TYPE_STRUCT_SIZED +#undef PROCESS_TYPE_STRUCT_END +#undef PROCESS_TYPE_STRUCT_IMPLEMENT_VERSIONED + +// Implement the generic crashpad::process_types::struct_name MinimumSize() and +// its templatized equivalent. The generic version delegates to the templatized +// one, which returns the minimum size of a sized structure. This can be used to +// ensure that enough of a sized structure is available to interpret its size +// field. This is only implemented for structures that use +// PROCESS_TYPE_STRUCT_SIZED(). +#define PROCESS_TYPE_STRUCT_IMPLEMENT_SIZED 1 + +#define PROCESS_TYPE_STRUCT_BEGIN(struct_name) + +#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...) + +#define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field) + +#define PROCESS_TYPE_STRUCT_SIZED(struct_name, size_field) \ + namespace crashpad { \ + namespace process_types { \ + \ + namespace internal { \ + \ + /* static */ \ + template \ + size_t struct_name::MinimumSize() { \ + return offsetof(struct_name, size_field) + \ + sizeof(struct_name::size_field); \ + } \ + \ + /* Explicit instantiations of the above. */ \ + template size_t struct_name::MinimumSize(); \ + template size_t struct_name::MinimumSize(); \ + \ + } /* namespace internal */ \ + \ + /* static */ \ + size_t struct_name::MinimumSize(ProcessReaderMac* process_reader) { \ + if (!process_reader->Is64Bit()) { \ + return internal::struct_name::MinimumSize(); \ + } else { \ + return internal::struct_name::MinimumSize(); \ + } \ + } \ + \ + } /* namespace process_types */ \ + } /* namespace crashpad */ + +#define PROCESS_TYPE_STRUCT_END(struct_name) + +#include "snapshot/mac/process_types/all.proctype" + +#undef PROCESS_TYPE_STRUCT_BEGIN +#undef PROCESS_TYPE_STRUCT_MEMBER +#undef PROCESS_TYPE_STRUCT_VERSIONED +#undef PROCESS_TYPE_STRUCT_SIZED +#undef PROCESS_TYPE_STRUCT_END +#undef PROCESS_TYPE_STRUCT_IMPLEMENT_SIZED diff --git a/shared/sentry/external/crashpad/snapshot/mac/process_types.h b/shared/sentry/external/crashpad/snapshot/mac/process_types.h new file mode 100644 index 000000000..6a5f9c1ce --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/process_types.h @@ -0,0 +1,234 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MAC_PROCESS_TYPES_H_ +#define CRASHPAD_SNAPSHOT_MAC_PROCESS_TYPES_H_ + +#include +#include +#include +#include +#include + +#include "snapshot/mac/process_reader_mac.h" + +namespace crashpad { +namespace process_types { +namespace internal { + +// Some structure definitions have tail padding when built in a 64-bit +// environment, but will have less (or no) tail padding in a 32-bit environment. +// These structure’s apparent sizes will differ between these two environments, +// which is incorrect. Use this “Nothing†type to place an “endâ€Â marker after +// all of the real members of such structures, and use offsetof(type, end) to +// compute their actual sizes. +using Nothing = char[0]; + +// Some structure definitions differ in 32-bit and 64-bit environments by having +// additional “reserved†padding fields present only in the 64-bit environment. +// These Reserved*_*Only* types allow the process_types system to replicate +// these structures more precisely. +using Reserved32_32Only32 = uint32_t; +using Reserved32_32Only64 = Nothing; +using Reserved32_64Only32 = Nothing; +using Reserved32_64Only64 = uint32_t; +using Reserved64_64Only32 = Nothing; +using Reserved64_64Only64 = uint64_t; + +} // namespace internal +} // namespace process_types +} // namespace crashpad + +#include "snapshot/mac/process_types/traits.h" + +// Creates the traits type crashpad::process_types::internal::TraitsGeneric. +DECLARE_PROCESS_TYPE_TRAITS_CLASS(Generic, 64) + +#undef DECLARE_PROCESS_TYPE_TRAITS_CLASS + +// Declare the crashpad::process_types::struct_name structs. These are the +// user-visible generic structs that callers will interact with. They read data +// from 32-bit or 64-bit processes by using the specific internal templatized +// structs below. +#define PROCESS_TYPE_STRUCT_DECLARE 1 + +#define PROCESS_TYPE_STRUCT_BEGIN(struct_name) \ + namespace crashpad { \ + namespace process_types { \ + struct struct_name { \ + public: \ + using Long = internal::TraitsGeneric::Long; \ + using ULong = internal::TraitsGeneric::ULong; \ + using Pointer = internal::TraitsGeneric::Pointer; \ + using IntPtr = internal::TraitsGeneric::IntPtr; \ + using UIntPtr = internal::TraitsGeneric::UIntPtr; \ + using Reserved32_32Only = internal::TraitsGeneric::Reserved32_32Only; \ + using Reserved32_64Only = internal::TraitsGeneric::Reserved32_64Only; \ + using Reserved64_64Only = internal::TraitsGeneric::Reserved64_64Only; \ + using Nothing = internal::TraitsGeneric::Nothing; \ + \ + /* Initializes an object with data read from |process_reader| at \ + * |address|, properly genericized. */ \ + bool Read(ProcessReaderMac* process_reader, mach_vm_address_t address) { \ + return ReadInto(process_reader, address, this); \ + } \ + \ + /* Reads |count| objects from |process_reader| beginning at |address|, and \ + * genericizes the objects. The caller must provide storage for |count| \ + * objects in |generic|. */ \ + static bool ReadArrayInto(ProcessReaderMac* process_reader, \ + mach_vm_address_t address, \ + size_t count, \ + struct_name* generic); \ + \ + /* Returns the size of the object that was read. This is the size of the \ + * storage in the process that the data is read from, and is not the same \ + * as the size of the generic struct. For versioned and sized structures, \ + * the size of the full structure is returned. */ \ + size_t Size() const { return size_; } \ + \ + /* Similar to Size(), but computes the expected size of a structure based \ + * on the process’ bitness. This can be used prior to reading any data \ + * from a process. For versioned and sized structures, \ + * ExpectedSizeForVersion() and MinimumSize() may also be useful. */ \ + static size_t ExpectedSize(ProcessReaderMac* process_reader); + +#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...) \ + member_type member_name __VA_ARGS__; + +#define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field) \ + /* Similar to ExpectedSize(), but computes the expected size of a \ + * structure based on the process’ bitness and a custom value, such as a \ + * structure version number. This can be used prior to reading any data \ + * from a process. */ \ + static size_t ExpectedSizeForVersion( \ + ProcessReaderMac* process_reader, \ + decltype(struct_name::version_field) version); + +#define PROCESS_TYPE_STRUCT_SIZED(struct_name, size_field) \ + /* Similar to ExpectedSize(), but computes the minimum size of a \ + * structure based on the process’ bitness, typically including enough of \ + * a structure to contain its size field. This can be used prior to \ + * reading any data from a process. */ \ + static size_t MinimumSize(ProcessReaderMac* process_reader); + +#define PROCESS_TYPE_STRUCT_END(struct_name) \ + private: \ + /* The static form of Read(). Populates the struct at |generic|. */ \ + static bool ReadInto(ProcessReaderMac* process_reader, \ + mach_vm_address_t address, \ + struct_name* generic); \ + \ + template \ + static bool ReadIntoInternal(ProcessReaderMac* process_reader, \ + mach_vm_address_t address, \ + struct_name* generic); \ + template \ + static bool ReadArrayIntoInternal(ProcessReaderMac* process_reader, \ + mach_vm_address_t address, \ + size_t count, \ + struct_name* generic); \ + size_t size_; \ + } \ + ; \ + } /* namespace process_types */ \ + } /* namespace crashpad */ + +#include "snapshot/mac/process_types/all.proctype" + +#undef PROCESS_TYPE_STRUCT_BEGIN +#undef PROCESS_TYPE_STRUCT_MEMBER +#undef PROCESS_TYPE_STRUCT_VERSIONED +#undef PROCESS_TYPE_STRUCT_SIZED +#undef PROCESS_TYPE_STRUCT_END +#undef PROCESS_TYPE_STRUCT_DECLARE + +// Declare the templatized crashpad::process_types::internal::struct_name<> +// structs. These are the 32-bit and 64-bit specific structs that describe the +// layout of objects in another process. This is repeated instead of being +// shared with the generic declaration above because both the generic and +// templatized specific structs need all of the struct members declared. +// +// GenericizeInto() translates a struct from the representation used in the +// remote process into the generic form. +#define PROCESS_TYPE_STRUCT_DECLARE_INTERNAL 1 + +#define PROCESS_TYPE_STRUCT_BEGIN(struct_name) \ + namespace crashpad { \ + namespace process_types { \ + namespace internal { \ + template \ + struct struct_name { \ + public: \ + using Long = typename Traits::Long; \ + using ULong = typename Traits::ULong; \ + using Pointer = typename Traits::Pointer; \ + using IntPtr = typename Traits::IntPtr; \ + using UIntPtr = typename Traits::UIntPtr; \ + using Reserved32_32Only = typename Traits::Reserved32_32Only; \ + using Reserved32_64Only = typename Traits::Reserved32_64Only; \ + using Reserved64_64Only = typename Traits::Reserved64_64Only; \ + using Nothing = typename Traits::Nothing; \ + \ + /* Read(), ReadArrayInto(), and Size() are as in the generic user-visible \ + * struct above. */ \ + bool Read(ProcessReaderMac* process_reader, mach_vm_address_t address) { \ + return ReadInto(process_reader, address, this); \ + } \ + static bool ReadArrayInto(ProcessReaderMac* process_reader, \ + mach_vm_address_t address, \ + size_t count, \ + struct_name* specific); \ + static size_t Size() { return sizeof(struct_name); } \ + \ + /* Translates a struct from the representation used in the remote process \ + * into the generic form. */ \ + void GenericizeInto(process_types::struct_name* generic, \ + size_t* specific_size); + +#define PROCESS_TYPE_STRUCT_MEMBER(member_type, member_name, ...) \ + member_type member_name __VA_ARGS__; + +#define PROCESS_TYPE_STRUCT_VERSIONED(struct_name, version_field) \ + /* ExpectedSizeForVersion() is as in the generic user-visible struct \ + * above. */ \ + static size_t ExpectedSizeForVersion( \ + decltype(struct_name::version_field) version); + +#define PROCESS_TYPE_STRUCT_SIZED(struct_name, size_field) \ + /* MinimumSize() is as in the generic user-visible struct above. */ \ + static size_t MinimumSize(); + +#define PROCESS_TYPE_STRUCT_END(struct_name) \ + private: \ + /* ReadInto() is as in the generic user-visible struct above. */ \ + static bool ReadInto(ProcessReaderMac* process_reader, \ + mach_vm_address_t address, \ + struct_name* specific); \ + } \ + ; \ + } /* namespace internal */ \ + } /* namespace process_types */ \ + } /* namespace crashpad */ + +#include "snapshot/mac/process_types/all.proctype" + +#undef PROCESS_TYPE_STRUCT_BEGIN +#undef PROCESS_TYPE_STRUCT_MEMBER +#undef PROCESS_TYPE_STRUCT_VERSIONED +#undef PROCESS_TYPE_STRUCT_SIZED +#undef PROCESS_TYPE_STRUCT_END +#undef PROCESS_TYPE_STRUCT_DECLARE_INTERNAL + +#endif // CRASHPAD_SNAPSHOT_MAC_PROCESS_TYPES_H_ diff --git a/shared/sentry/external/crashpad/snapshot/mac/process_types/all.proctype b/shared/sentry/external/crashpad/snapshot/mac/process_types/all.proctype new file mode 100644 index 000000000..d84b41d14 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/process_types/all.proctype @@ -0,0 +1,27 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +// This file is intended to be included multiple times in the same translation +// unit, so #include guards are intentionally absent. +// +// This file is included by snapshot/mac/process_types.h and +// snapshot/mac/process_types.cc to produce process type struct definitions and +// accessors. + +#include "snapshot/mac/process_types/annotation.proctype" +#include "snapshot/mac/process_types/crashpad_info.proctype" +#include "snapshot/mac/process_types/crashreporterclient.proctype" +#include "snapshot/mac/process_types/dyld_images.proctype" +#include "snapshot/mac/process_types/loader.proctype" +#include "snapshot/mac/process_types/nlist.proctype" diff --git a/shared/sentry/external/crashpad/snapshot/mac/process_types/annotation.proctype b/shared/sentry/external/crashpad/snapshot/mac/process_types/annotation.proctype new file mode 100644 index 000000000..5d34fdabd --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/process_types/annotation.proctype @@ -0,0 +1,31 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +PROCESS_TYPE_STRUCT_BEGIN(Annotation) + PROCESS_TYPE_STRUCT_MEMBER(Pointer, link_node) + PROCESS_TYPE_STRUCT_MEMBER(Pointer, name) + PROCESS_TYPE_STRUCT_MEMBER(Pointer, value) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, size) + PROCESS_TYPE_STRUCT_MEMBER(uint16_t, type) +PROCESS_TYPE_STRUCT_END(Annotation) + +#if !defined(PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY) + +PROCESS_TYPE_STRUCT_BEGIN(AnnotationList) + PROCESS_TYPE_STRUCT_MEMBER(Pointer, tail_pointer) + PROCESS_TYPE_STRUCT_MEMBER(crashpad::process_types::Annotation, head) + PROCESS_TYPE_STRUCT_MEMBER(crashpad::process_types::Annotation, tail) +PROCESS_TYPE_STRUCT_END(AnnotationList) + +#endif // !defined(PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY) diff --git a/shared/sentry/external/crashpad/snapshot/mac/process_types/crashpad_info.proctype b/shared/sentry/external/crashpad/snapshot/mac/process_types/crashpad_info.proctype new file mode 100644 index 000000000..7c077ca7a --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/process_types/crashpad_info.proctype @@ -0,0 +1,71 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +// The file corresponds to Crashpad’s client/crashpad_info.h. +// +// This file is intended to be included multiple times in the same translation +// unit, so #include guards are intentionally absent. +// +// This file is included by snapshot/mac/process_types.h and +// snapshot/mac/process_types.cc to produce process type struct definitions and +// accessors. + +// Client Mach-O images will contain a __DATA,crashpad_info section formatted +// according to this structure. +// +// CrashpadInfo is variable-length. Its length dictated by its |size| field +// which is always present. A custom implementation of the flavored +// ReadSpecificInto function that understands how to use this field is provided +// in snapshot/mac/process_types/custom.cc. No implementation of ReadArrayInto +// is provided because CrashpadInfo structs are singletons in a module and are +// never present in arrays, so the functionality is unnecessary. + +#if !defined(PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO) && \ + !defined(PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY) + +PROCESS_TYPE_STRUCT_BEGIN(CrashpadInfo) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, signature) + + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, size) + PROCESS_TYPE_STRUCT_SIZED(CrashpadInfo, size) + + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, version) + + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, indirectly_referenced_memory_cap) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, padding_0) + PROCESS_TYPE_STRUCT_MEMBER(uint8_t, crashpad_handler_behavior) // TriState + + // TriState + PROCESS_TYPE_STRUCT_MEMBER(uint8_t, system_crash_reporter_forwarding) + + // TriState + PROCESS_TYPE_STRUCT_MEMBER(uint8_t, gather_indirectly_referenced_memory) + + PROCESS_TYPE_STRUCT_MEMBER(uint8_t, padding_1) + + // SimpleAddressRangeBag* + PROCESS_TYPE_STRUCT_MEMBER(Pointer, extra_memory_ranges) + + // SimpleStringDictionary* + PROCESS_TYPE_STRUCT_MEMBER(Pointer, simple_annotations) + + // UserDataStreamListEntry* + PROCESS_TYPE_STRUCT_MEMBER(Pointer, user_data_minidump_stream_head) + + // AnnotationList* + PROCESS_TYPE_STRUCT_MEMBER(Pointer, annotations_list) +PROCESS_TYPE_STRUCT_END(CrashpadInfo) + +#endif // ! PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO && + // ! PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY diff --git a/shared/sentry/external/crashpad/snapshot/mac/process_types/crashreporterclient.proctype b/shared/sentry/external/crashpad/snapshot/mac/process_types/crashreporterclient.proctype new file mode 100644 index 000000000..226eaa256 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/process_types/crashreporterclient.proctype @@ -0,0 +1,60 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +// The name of this file was chosen based on +// https://llvm.org/svn/llvm-project/llvm/trunk/lib/Support/PrettyStackTrace.cpp. +// The name of the structure it describes was chosen based on that file as well +// as 10.9.2 cups-372.2/cups/backend/usb-darwin.c. That file also provided the +// names and types of the fields in the structure. +// +// This file is intended to be included multiple times in the same translation +// unit, so #include guards are intentionally absent. +// +// This file is included by snapshot/mac/process_types.h and +// snapshot/mac/process_types.cc to produce process type struct definitions and +// accessors. This file is also used by the iOS in process handler to read both +// messages in client/ios_handler/in_process_intermediate_dump_handler.cc. + +// Client Mach-O images will contain a __DATA,__crash_info section formatted +// according to this structure. +// +// crashreporter_annotations_t is variable-length. Its length dictated by its +// |version| field which is always present. A custom implementation of the +// flavored ReadSpecificInto function that understands how to map this field to +// the structure’s actual size is provided in +// snapshot/mac/process_types/custom.cc. No implementation of ReadArrayInto is +// provided because crashreporter_annotations_t structs are singletons in a +// module and are never present in arrays, so the functionality is unnecessary. + +#if !defined(PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO) && \ + !defined(PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY) + +PROCESS_TYPE_STRUCT_BEGIN(crashreporter_annotations_t) + PROCESS_TYPE_STRUCT_MEMBER(uint64_t, version) // unsigned long + PROCESS_TYPE_STRUCT_VERSIONED(crashreporter_annotations_t, version) + + // Version 4 (OS X 10.7) + PROCESS_TYPE_STRUCT_MEMBER(uint64_t, message) // char* + PROCESS_TYPE_STRUCT_MEMBER(uint64_t, signature_string) // char* + PROCESS_TYPE_STRUCT_MEMBER(uint64_t, backtrace) // char* + PROCESS_TYPE_STRUCT_MEMBER(uint64_t, message2) // char* + PROCESS_TYPE_STRUCT_MEMBER(uint64_t, thread) + PROCESS_TYPE_STRUCT_MEMBER(uint64_t, dialog_mode) // unsigned int + + // Version 5 (OS X 10.11) + PROCESS_TYPE_STRUCT_MEMBER(uint64_t, unknown_0) +PROCESS_TYPE_STRUCT_END(crashreporter_annotations_t) + +#endif // ! PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO && + // ! PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY diff --git a/shared/sentry/external/crashpad/snapshot/mac/process_types/custom.cc b/shared/sentry/external/crashpad/snapshot/mac/process_types/custom.cc new file mode 100644 index 000000000..5c6dec98d --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/process_types/custom.cc @@ -0,0 +1,247 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 +#include +#include + +#include +#include +#include +#include + +#include "base/check_op.h" +#include "base/logging.h" +#include "base/numerics/safe_math.h" +#include "base/strings/stringprintf.h" +#include "snapshot/mac/process_types.h" +#include "snapshot/mac/process_types/internal.h" +#include "util/mac/mac_util.h" +#include "util/process/process_memory_mac.h" + +#if !DOXYGEN + +namespace crashpad { +namespace process_types { +namespace internal { + +namespace { + +template +bool ReadIntoAndZero(const ProcessMemoryMac* process_memory, + mach_vm_address_t address, + mach_vm_size_t size, + T* specific) { + DCHECK_LE(size, sizeof(*specific)); + + if (!process_memory->Read(address, size, specific)) { + return false; + } + + // Zero out the rest of the structure in case anything accesses fields without + // checking the version or size. + const size_t remaining = sizeof(*specific) - size; + if (remaining > 0) { + char* const start = reinterpret_cast(specific) + size; + memset(start, 0, remaining); + } + + return true; +} + +template +bool FieldAddressIfInRange(mach_vm_address_t address, + size_t offset, + mach_vm_address_t* field_address) { + base::CheckedNumeric checked_field_address(address); + checked_field_address += offset; + typename T::Pointer local_field_address; + if (!checked_field_address.AssignIfValid(&local_field_address)) { + LOG(ERROR) << base::StringPrintf( + "address 0x%llx + offset 0x%zx out of range", address, offset); + return false; + } + + *field_address = local_field_address; + return true; +} + +template +bool ReadIntoVersioned(ProcessReaderMac* process_reader, + mach_vm_address_t address, + T* specific) { + mach_vm_address_t field_address; + if (!FieldAddressIfInRange( + address, offsetof(T, version), &field_address)) { + return false; + } + + const ProcessMemoryMac* process_memory = process_reader->Memory(); + decltype(specific->version) version; + if (!process_memory->Read(field_address, sizeof(version), &version)) { + return false; + } + + const size_t size = T::ExpectedSizeForVersion(version); + return ReadIntoAndZero(process_memory, address, size, specific); +} + +template +bool ReadIntoSized(ProcessReaderMac* process_reader, + mach_vm_address_t address, + T* specific) { + mach_vm_address_t field_address; + if (!FieldAddressIfInRange(address, offsetof(T, size), &field_address)) { + return false; + } + + const ProcessMemoryMac* process_memory = process_reader->Memory(); + decltype(specific->size) size; + if (!process_memory->Read(address + offsetof(T, size), sizeof(size), &size)) { + return false; + } + + if (size < T::MinimumSize()) { + LOG(ERROR) << "small size " << size; + return false; + } + + size = std::min(static_cast(size), sizeof(*specific)); + return ReadIntoAndZero(process_memory, address, size, specific); +} + +} // namespace + +// static +template +size_t dyld_all_image_infos::ExpectedSizeForVersion( + decltype(dyld_all_image_infos::version) version) { + static constexpr size_t kSizeForVersion[] = { + offsetof(dyld_all_image_infos, infoArrayCount), // 0 + offsetof(dyld_all_image_infos, libSystemInitialized), // 1 + offsetof(dyld_all_image_infos, jitInfo), // 2 + offsetof(dyld_all_image_infos, dyldVersion), // 3 + offsetof(dyld_all_image_infos, dyldVersion), // 4 + offsetof(dyld_all_image_infos, coreSymbolicationShmPage), // 5 + offsetof(dyld_all_image_infos, systemOrderFlag), // 6 + offsetof(dyld_all_image_infos, uuidArrayCount), // 7 + offsetof(dyld_all_image_infos, dyldAllImageInfosAddress), // 8 + offsetof(dyld_all_image_infos, initialImageCount), // 9 + offsetof(dyld_all_image_infos, errorKind), // 10 + offsetof(dyld_all_image_infos, sharedCacheSlide), // 11 + offsetof(dyld_all_image_infos, sharedCacheUUID), // 12 + offsetof(dyld_all_image_infos, infoArrayChangeTimestamp), // 13 + offsetof(dyld_all_image_infos, end_v14), // 14 + std::numeric_limits::max(), // 15, see below + offsetof(dyld_all_image_infos, end_v16), // 16 + sizeof(dyld_all_image_infos), // 17 + sizeof(dyld_all_image_infos), // 18 + }; + + if (version >= std::size(kSizeForVersion)) { + return kSizeForVersion[std::size(kSizeForVersion) - 1]; + } + + static_assert(std::is_unsigned::value, + "version must be unsigned"); + + if (version == 15) { + // Disambiguate between the two different layouts for version 15. The + // original one introduced in macOS 10.12 had the same size as version 14. + // The revised one in macOS 10.13 grew. It’s safe to assume that the + // dyld_all_image_infos structure came from the same system that’s now + // interpreting it, so use an OS version check. + const int macos_version_number = MacOSVersionNumber(); + if (macos_version_number / 1'00 == 10'12) { + return offsetof(dyld_all_image_infos, end_v14); + } + + DCHECK_GE(macos_version_number, 10'13'00); + DCHECK_LT(macos_version_number, 10'15'00); + return offsetof(dyld_all_image_infos, platform); + } + + size_t size = kSizeForVersion[version]; + DCHECK_NE(size, std::numeric_limits::max()); + + return size; +} + +// static +template +bool dyld_all_image_infos::ReadInto( + ProcessReaderMac* process_reader, + mach_vm_address_t address, + dyld_all_image_infos* specific) { + return ReadIntoVersioned(process_reader, address, specific); +} + +// static +template +size_t crashreporter_annotations_t::ExpectedSizeForVersion( + decltype(crashreporter_annotations_t::version) version) { + if (version >= 5) { + return sizeof(crashreporter_annotations_t); + } + if (version >= 4) { + return offsetof(crashreporter_annotations_t, unknown_0); + } + return offsetof(crashreporter_annotations_t, message); +} + +// static +template +bool crashreporter_annotations_t::ReadInto( + ProcessReaderMac* process_reader, + mach_vm_address_t address, + crashreporter_annotations_t* specific) { + return ReadIntoVersioned(process_reader, address, specific); +} + +// static +template +bool CrashpadInfo::ReadInto(ProcessReaderMac* process_reader, + mach_vm_address_t address, + CrashpadInfo* specific) { + return ReadIntoSized(process_reader, address, specific); +} + +// Explicit template instantiation of the above. +#define PROCESS_TYPE_FLAVOR_TRAITS(lp_bits) \ + template size_t \ + dyld_all_image_infos::ExpectedSizeForVersion( \ + decltype(dyld_all_image_infos::version)); \ + template bool dyld_all_image_infos::ReadInto( \ + ProcessReaderMac*, \ + mach_vm_address_t, \ + dyld_all_image_infos*); \ + template size_t \ + crashreporter_annotations_t::ExpectedSizeForVersion( \ + decltype(crashreporter_annotations_t::version)); \ + template bool crashreporter_annotations_t::ReadInto( \ + ProcessReaderMac*, \ + mach_vm_address_t, \ + crashreporter_annotations_t*); \ + template bool CrashpadInfo::ReadInto( \ + ProcessReaderMac*, mach_vm_address_t, CrashpadInfo*); + +#include "snapshot/mac/process_types/flavors.h" + +#undef PROCESS_TYPE_FLAVOR_TRAITS + +} // namespace internal +} // namespace process_types +} // namespace crashpad + +#endif // !DOXYGEN diff --git a/shared/sentry/external/crashpad/snapshot/mac/process_types/dyld_images.proctype b/shared/sentry/external/crashpad/snapshot/mac/process_types/dyld_images.proctype new file mode 100644 index 000000000..5c57a26f0 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/process_types/dyld_images.proctype @@ -0,0 +1,165 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +// This file corresponds to the system’s . +// +// This file is intended to be included multiple times in the same translation +// unit, so #include guards are intentionally absent. +// +// This file is included by snapshot/mac/process_types.h and +// snapshot/mac/process_types.cc to produce process type struct definitions and +// accessors. + +PROCESS_TYPE_STRUCT_BEGIN(dyld_image_info) + PROCESS_TYPE_STRUCT_MEMBER(Pointer, imageLoadAddress) // const mach_header* + PROCESS_TYPE_STRUCT_MEMBER(Pointer, imageFilePath) // const char* + PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, imageFileModDate) +PROCESS_TYPE_STRUCT_END(dyld_image_info) + +PROCESS_TYPE_STRUCT_BEGIN(dyld_uuid_info) + PROCESS_TYPE_STRUCT_MEMBER(Pointer, imageLoadAddress) // const mach_header* + PROCESS_TYPE_STRUCT_MEMBER(uuid_t, imageUUID) +PROCESS_TYPE_STRUCT_END(dyld_uuid_info) + +// dyld_all_image_infos is variable-length. Its length dictated by its |version| +// field which is always present. A custom implementation of the flavored +// ReadSpecificInto function that understands how to map this field to the +// structure’s actual size is provided in snapshot/mac/process_types/custom.cc. +// No implementation of ReadArrayInto is provided because dyld_all_image_infos +// structs are singletons in a process and are never present in arrays, so the +// functionality is unnecessary. + +#if !defined(PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO) && \ + !defined(PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY) + +PROCESS_TYPE_STRUCT_BEGIN(dyld_all_image_infos) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, version) + PROCESS_TYPE_STRUCT_VERSIONED(dyld_all_image_infos, version) + + // Version 1 (Mac OS X 10.4) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, infoArrayCount) + PROCESS_TYPE_STRUCT_MEMBER(Pointer, infoArray) // const dyld_image_info* + PROCESS_TYPE_STRUCT_MEMBER(Pointer, notification) // function pointer + PROCESS_TYPE_STRUCT_MEMBER(bool, processDetachedFromSharedRegion) + + // Version 2 (Mac OS X 10.6) + PROCESS_TYPE_STRUCT_MEMBER(bool, libSystemInitialized) + + // This field does not appear in the system’s structure definition but is + // necessary to ensure that when building in 32-bit mode, the 64-bit version + // of the process_types structure matches the genuine 64-bit structure. This + // is required because the alignment constraints on 64-bit types are more + // stringent in 64-bit mode. + PROCESS_TYPE_STRUCT_MEMBER(Reserved32_64Only, alignment) + + // const mach_header* + PROCESS_TYPE_STRUCT_MEMBER(Pointer, dyldImageLoadAddress) + + // Version 3 (Mac OS X 10.6) + PROCESS_TYPE_STRUCT_MEMBER(Pointer, jitInfo) // void* + + // Version 5 (Mac OS X 10.6) + PROCESS_TYPE_STRUCT_MEMBER(Pointer, dyldVersion) // const char* + PROCESS_TYPE_STRUCT_MEMBER(Pointer, errorMessage) // const char* + PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, terminationFlags) + + // Version 6 (Mac OS X 10.6) + PROCESS_TYPE_STRUCT_MEMBER(Pointer, coreSymbolicationShmPage) // void* + + // Version 7 (Mac OS X 10.6) + PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, systemOrderFlag) + + // Version 8 (OS X 10.7) + PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, uuidArrayCount) + PROCESS_TYPE_STRUCT_MEMBER(Pointer, uuidArray) // const dyld_uuid_info* + + // Version 9 (OS X 10.7) + // dyld_all_image_infos* + PROCESS_TYPE_STRUCT_MEMBER(Pointer, dyldAllImageInfosAddress) + + // Version 10 (OS X 10.7) + PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, initialImageCount) + + // Version 11 (OS X 10.7) + PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, errorKind) + PROCESS_TYPE_STRUCT_MEMBER(Pointer, errorClientOfDylibPath) // const char* + PROCESS_TYPE_STRUCT_MEMBER(Pointer, errorTargetDylibPath) // const char* + PROCESS_TYPE_STRUCT_MEMBER(Pointer, errorSymbol) // const char* + + // Version 12 (OS X 10.7) + PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, sharedCacheSlide) + + // Version 13 (OS X 10.9) + PROCESS_TYPE_STRUCT_MEMBER(uint8_t, sharedCacheUUID, [16]) + + // Version 15 (macOS 10.12) + // This space is also allocated in version 14 (OS X 10.9) as part of the + // “reserved†member. + PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, sharedCacheBaseAddress) + PROCESS_TYPE_STRUCT_MEMBER(uint64_t, infoArrayChangeTimestamp) + PROCESS_TYPE_STRUCT_MEMBER(Pointer, dyldPath) // const char* + + // These should be considered mach_port_name_t when interacting with them from + // another Mach IPC namespace (process). + PROCESS_TYPE_STRUCT_MEMBER(mach_port_t, notifyPorts, [8]) + + // Version 14 (OS X 10.9) + // As of the 10.12 SDK, this is declared as reserved[9] for 64-bit platforms + // and reserved[4] for 32-bit platforms. It was expanded to reserved[5] for + // 32-bit platforms in the 10.13 SDK to provide proper padding, but because + // the runtimes that use versions 14 and 15 were built with SDKs that did not + // have this extra padding, it’s necessary to treat the element at index 4 on + // 32-bit systems as outside of the version 14 and 15 structure. This is why + // |reserved| is only declared a 4-element array, with a special end_v14 + // member (not present in the native definition) available to indicate the end + // of the native version 14 structure and the 10.12 version 15 structure, + // preceding the padding in the 32-bit structure that would natively be + // addressed at index 4 of |reserved|. Treat reserved_4_32 as only available + // in version 16 of the structure. + // In the 12.0 SDK, 2 of the trailing UIntPtrs on 64-bit and + // 4 of them on 32-bit were replaced by two uint64_ts. On 32-bit, that + // awkwardly straddles end_v14. Since macOS 12.0 is 64-bit only, the proctype + // version of this struct only has these uint64_ts in the 64-bit version. + PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, reserved, [4]) + PROCESS_TYPE_STRUCT_MEMBER(Reserved64_64Only, reserved_4_64) + PROCESS_TYPE_STRUCT_MEMBER(Reserved64_64Only, reserved_5) + PROCESS_TYPE_STRUCT_MEMBER(Reserved64_64Only, reserved_6) + PROCESS_TYPE_STRUCT_MEMBER(Reserved64_64Only, shared_cache_fs_id) + PROCESS_TYPE_STRUCT_MEMBER(Reserved64_64Only, shared_cache_fs_obj_id) + PROCESS_TYPE_STRUCT_MEMBER(Nothing, end_v14) + PROCESS_TYPE_STRUCT_MEMBER(Reserved32_32Only, reserved_4_32) + + // Version 15 (macOS 10.13) + // incorrectly claims that these were introduced at + // version 16. These fields are not present in macOS 10.12, which also + // identifies its structure as version 15. + PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, compact_dyld_image_info_addr) + PROCESS_TYPE_STRUCT_MEMBER(ULong, compact_dyld_image_info_size) // size_t + + // Version 16 (macOS 10.15) + // The native structure is followed by 4 bytes of padding, marked by the + // end_v16 member later, not present in the native version of the structure. + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, platform) // dyld_platform_t + + // Version 17 (macOS 10.16/11.0) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, aotInfoCount) + PROCESS_TYPE_STRUCT_MEMBER(Nothing, end_v16) + PROCESS_TYPE_STRUCT_MEMBER(Pointer, aotInfoArray) // dyld_aot_image_info* + PROCESS_TYPE_STRUCT_MEMBER(uint64_t, aotInfoArrayChangeTimestamp) + PROCESS_TYPE_STRUCT_MEMBER(UIntPtr, aotSharedCacheBaseAddress) + PROCESS_TYPE_STRUCT_MEMBER(uint8_t, aotSharedCacheUUID, [16]) +PROCESS_TYPE_STRUCT_END(dyld_all_image_infos) + +#endif // ! PROCESS_TYPE_STRUCT_IMPLEMENT_INTERNAL_READ_INTO && + // ! PROCESS_TYPE_STRUCT_IMPLEMENT_ARRAY diff --git a/shared/sentry/external/crashpad/snapshot/mac/process_types/flavors.h b/shared/sentry/external/crashpad/snapshot/mac/process_types/flavors.h new file mode 100644 index 000000000..01ab2bd7c --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/process_types/flavors.h @@ -0,0 +1,24 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +// This file is intended to be included multiple times in the same translation +// unit, so #include guards are intentionally absent. +// +// This file is included by snapshot/mac/process_types/internal.h to produce +// process type flavor traits class declarations and by +// snapshot/mac/process_types/custom.cc to provide explicit instantiation of +// flavored implementations. + +PROCESS_TYPE_FLAVOR_TRAITS(32) +PROCESS_TYPE_FLAVOR_TRAITS(64) diff --git a/shared/sentry/external/crashpad/snapshot/mac/process_types/internal.h b/shared/sentry/external/crashpad/snapshot/mac/process_types/internal.h new file mode 100644 index 000000000..fd956574d --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/process_types/internal.h @@ -0,0 +1,36 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MAC_PROCESS_TYPES_INTERNAL_H_ +#define CRASHPAD_SNAPSHOT_MAC_PROCESS_TYPES_INTERNAL_H_ + +#include "snapshot/mac/process_types.h" + +// Declare Traits32 and Traits64, flavor-specific traits classes. These are +// private traits classes not for use outside of process type internals. +// TraitsGeneric is declared in snapshot/mac/process_types.h. + +#include "snapshot/mac/process_types/traits.h" + +#define PROCESS_TYPE_FLAVOR_TRAITS(lp_bits) \ + DECLARE_PROCESS_TYPE_TRAITS_CLASS( \ + lp_bits, lp_bits, __attribute__((aligned(lp_bits / 8)))) + +#include "snapshot/mac/process_types/flavors.h" + +#undef PROCESS_TYPE_FLAVOR_TRAITS + +#undef DECLARE_PROCESS_TYPE_TRAITS_CLASS + +#endif // CRASHPAD_SNAPSHOT_MAC_PROCESS_TYPES_INTERNAL_H_ diff --git a/shared/sentry/external/crashpad/snapshot/mac/process_types/loader.proctype b/shared/sentry/external/crashpad/snapshot/mac/process_types/loader.proctype new file mode 100644 index 000000000..43a9a7f13 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/process_types/loader.proctype @@ -0,0 +1,140 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +// This file corresponds to the system’s . +// +// This file is intended to be included multiple times in the same translation +// unit, so #include guards are intentionally absent. +// +// This file is included by snapshot/mac/process_types.h and +// snapshot/mac/process_types.cc to produce process type struct definitions and +// accessors. + +PROCESS_TYPE_STRUCT_BEGIN(mach_header) // 64-bit: mach_header_64 + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, magic) + + // cputype is really cpu_type_t, a typedef for integer_t, itself a typedef for + // int. It is currently reasonable to assume that int is int32_t. + PROCESS_TYPE_STRUCT_MEMBER(int32_t, cputype) + + // cpusubtype is really cpu_subtype_t, a typedef for integer_t, itself a + // typedef for int. It is currently reasonable to assume that int is int32_t. + PROCESS_TYPE_STRUCT_MEMBER(int32_t, cpusubtype) + + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, filetype) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, ncmds) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, sizeofcmds) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, flags) + PROCESS_TYPE_STRUCT_MEMBER(Reserved32_64Only, reserved) +PROCESS_TYPE_STRUCT_END(mach_header) + +PROCESS_TYPE_STRUCT_BEGIN(load_command) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize) +PROCESS_TYPE_STRUCT_END(load_command) + +PROCESS_TYPE_STRUCT_BEGIN(segment_command) // 64-bit: segment_command_64 + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize) + + // This string is not necessarily NUL-terminated. + PROCESS_TYPE_STRUCT_MEMBER(char, segname, [16]) + + PROCESS_TYPE_STRUCT_MEMBER(ULong, vmaddr) + PROCESS_TYPE_STRUCT_MEMBER(ULong, vmsize) + PROCESS_TYPE_STRUCT_MEMBER(ULong, fileoff) + PROCESS_TYPE_STRUCT_MEMBER(ULong, filesize) + PROCESS_TYPE_STRUCT_MEMBER(vm_prot_t, maxprot) + PROCESS_TYPE_STRUCT_MEMBER(vm_prot_t, initprot) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nsects) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, flags) +PROCESS_TYPE_STRUCT_END(segment_command) + +PROCESS_TYPE_STRUCT_BEGIN(dylib_command) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize) + + // The following come from the dylib struct member of dylib_command. + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, dylib_name) // lc_str + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, dylib_timestamp) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, dylib_current_version) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, dylib_compatibility_version) +PROCESS_TYPE_STRUCT_END(dylib_command) + +PROCESS_TYPE_STRUCT_BEGIN(dylinker_command) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, name) // lc_str +PROCESS_TYPE_STRUCT_END(dylinker_command) + +PROCESS_TYPE_STRUCT_BEGIN(symtab_command) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, symoff) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nsyms) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, stroff) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, strsize) +PROCESS_TYPE_STRUCT_END(symtab_command) + +PROCESS_TYPE_STRUCT_BEGIN(dysymtab_command) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, ilocalsym) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nlocalsym) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, iextdefsym) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nextdefsym) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, iundefsym) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nundefsym) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, tocoff) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, ntoc) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, modtaboff) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nmodtab) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, extrefsymoff) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nextrefsyms) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, indirectsymoff) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nindirectsyms) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, extreloff) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nextrel) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, locreloff) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nlocrel) +PROCESS_TYPE_STRUCT_END(dysymtab_command) + +PROCESS_TYPE_STRUCT_BEGIN(uuid_command) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize) + PROCESS_TYPE_STRUCT_MEMBER(uint8_t, uuid, [16]) +PROCESS_TYPE_STRUCT_END(uuid_command) + +PROCESS_TYPE_STRUCT_BEGIN(source_version_command) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmd) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, cmdsize) + PROCESS_TYPE_STRUCT_MEMBER(uint64_t, version) +PROCESS_TYPE_STRUCT_END(source_version_command) + +PROCESS_TYPE_STRUCT_BEGIN(section) // 64-bit: section_64 + // These strings are not necessarily NUL-terminated. + PROCESS_TYPE_STRUCT_MEMBER(char, sectname, [16]) + PROCESS_TYPE_STRUCT_MEMBER(char, segname, [16]) + + PROCESS_TYPE_STRUCT_MEMBER(ULong, addr) + PROCESS_TYPE_STRUCT_MEMBER(ULong, size) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, offset) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, align) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, reloff) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, nreloc) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, flags) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, reserved1) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, reserved2) + PROCESS_TYPE_STRUCT_MEMBER(Reserved32_64Only, reserved3) +PROCESS_TYPE_STRUCT_END(section) diff --git a/shared/sentry/external/crashpad/snapshot/mac/process_types/nlist.proctype b/shared/sentry/external/crashpad/snapshot/mac/process_types/nlist.proctype new file mode 100644 index 000000000..c89891395 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/process_types/nlist.proctype @@ -0,0 +1,30 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +// This file corresponds to the system’s . +// +// This file is intended to be included multiple times in the same translation +// unit, so #include guards are intentionally absent. +// +// This file is included by snapshot/mac/process_types.h and +// snapshot/mac/process_types.cc to produce process type struct definitions and +// accessors. + +PROCESS_TYPE_STRUCT_BEGIN(nlist) + PROCESS_TYPE_STRUCT_MEMBER(uint32_t, n_strx) // n_un.n_strx + PROCESS_TYPE_STRUCT_MEMBER(uint8_t, n_type) + PROCESS_TYPE_STRUCT_MEMBER(uint8_t, n_sect) + PROCESS_TYPE_STRUCT_MEMBER(uint16_t, n_desc) // 32-bit: int16_t + PROCESS_TYPE_STRUCT_MEMBER(ULong, n_value) +PROCESS_TYPE_STRUCT_END(nlist) diff --git a/shared/sentry/external/crashpad/snapshot/mac/process_types/traits.h b/shared/sentry/external/crashpad/snapshot/mac/process_types/traits.h new file mode 100644 index 000000000..613f4f575 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/process_types/traits.h @@ -0,0 +1,46 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +// This file is intended to be included multiple times in the same translation +// unit, so #include guards are intentionally absent. +// +// This file is included by snapshot/mac/process_types.h and +// snapshot/mac/process_types/internal.h to produce traits class definitions. + +// Things that #include this file should #undef +// DECLARE_PROCESS_TYPE_TRAITS_CLASS before #including this file again and after +// the last #include of this file. +// +// |Reserved*| is used for padding fields that may be zero-length, and |Nothing| +// is always zero-length and is used solely as an anchor to compute offsets. +// __VA_ARGS__, which is intended to set the alignment of the 64-bit types, is +// not used for those type aliases. +#define DECLARE_PROCESS_TYPE_TRAITS_CLASS(traits_name, lp_bits, ...) \ + namespace crashpad { \ + namespace process_types { \ + namespace internal { \ + struct Traits##traits_name { \ + using Long = int##lp_bits##_t __VA_ARGS__; \ + using ULong = uint##lp_bits##_t __VA_ARGS__; \ + using Pointer = uint##lp_bits##_t __VA_ARGS__; \ + using IntPtr = int##lp_bits##_t __VA_ARGS__; \ + using UIntPtr = uint##lp_bits##_t __VA_ARGS__; \ + using Reserved32_32Only = Reserved32_32Only##lp_bits; \ + using Reserved32_64Only = Reserved32_64Only##lp_bits; \ + using Reserved64_64Only = Reserved64_64Only##lp_bits; \ + using Nothing = Nothing; \ + }; \ + } \ + } \ + } diff --git a/shared/sentry/external/crashpad/snapshot/mac/process_types_test.cc b/shared/sentry/external/crashpad/snapshot/mac/process_types_test.cc new file mode 100644 index 000000000..2946dc25c --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/process_types_test.cc @@ -0,0 +1,453 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/mac/process_types.h" + +#include +#include +#include + +#include +#include +#include + +#include "base/strings/stringprintf.h" +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "snapshot/mac/process_types/internal.h" +#include "test/mac/dyld.h" +#include "util/mac/mac_util.h" +#include "util/misc/from_pointer_cast.h" +#include "util/misc/implicit_cast.h" + +namespace crashpad { +namespace test { +namespace { + +#define TEST_STRING(process_reader, self_view, proctype_view, field) \ + do { \ + if (self_view->field) { \ + std::string proctype_string; \ + ASSERT_TRUE(process_reader.Memory()->ReadCString(proctype_view.field, \ + &proctype_string)); \ + EXPECT_EQ(proctype_string, self_view->field); \ + } \ + } while (false) + +TEST(ProcessTypes, DyldImagesSelf) { + // Get the in-process view of dyld_all_image_infos, and check it for sanity. + const dyld_all_image_infos* self_image_infos = DyldGetAllImageInfos(); + const int macos_version_number = MacOSVersionNumber(); + + if (macos_version_number >= 10'15'00) { + EXPECT_GE(self_image_infos->version, 16u); + } else if (macos_version_number >= 10'12'00) { + EXPECT_GE(self_image_infos->version, 15u); + } else if (macos_version_number >= 10'09'00) { + EXPECT_GE(self_image_infos->version, 13u); + } else if (macos_version_number >= 10'07'00) { + EXPECT_GE(self_image_infos->version, 8u); + } else if (macos_version_number >= 10'06'00) { + EXPECT_GE(self_image_infos->version, 2u); + } else { + EXPECT_GE(self_image_infos->version, 1u); + } + + EXPECT_GT(self_image_infos->infoArrayCount, 1u); + if (self_image_infos->version >= 2) { + EXPECT_TRUE(self_image_infos->libSystemInitialized); + } +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_7 + if (self_image_infos->version >= 9) { + EXPECT_EQ(self_image_infos->dyldAllImageInfosAddress, self_image_infos); + } +#endif + + // Get the out-of-process view of dyld_all_image_infos, and work with it + // through the process_types interface. + task_dyld_info_data_t dyld_info; + mach_msg_type_number_t count = TASK_DYLD_INFO_COUNT; + kern_return_t kr = task_info(mach_task_self(), + TASK_DYLD_INFO, + reinterpret_cast(&dyld_info), + &count); + ASSERT_EQ(kr, KERN_SUCCESS); + + EXPECT_EQ(dyld_info.all_image_info_addr, + FromPointerCast(self_image_infos)); + EXPECT_GT(dyld_info.all_image_info_size, 1u); + + // This field is only present in the OS X 10.7 SDK (at build time) and kernel + // (at run time). +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_7 + if (macos_version_number >= 10'07'00) { +#if !defined(ARCH_CPU_64_BITS) + EXPECT_EQ(dyld_info.all_image_info_format, TASK_DYLD_ALL_IMAGE_INFO_32); +#else + EXPECT_EQ(dyld_info.all_image_info_format, TASK_DYLD_ALL_IMAGE_INFO_64); +#endif + } +#endif + + ProcessReaderMac process_reader; + ASSERT_TRUE(process_reader.Initialize(mach_task_self())); + +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_16 + constexpr uint32_t kDyldAllImageInfosVersionInSDK = 17; +#elif __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_15 + constexpr uint32_t kDyldAllImageInfosVersionInSDK = 16; +#elif __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_12 + constexpr uint32_t kDyldAllImageInfosVersionInSDK = 15; +#elif __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_9 + constexpr uint32_t kDyldAllImageInfosVersionInSDK = 14; +#elif __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_7 + constexpr uint32_t kDyldAllImageInfosVersionInSDK = 12; +#elif __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_6 + constexpr uint32_t kDyldAllImageInfosVersionInSDK = 7; +#else + constexpr uint32_t kDyldAllImageInfosVersionInSDK = 1; +#endif + + // Make sure that the size of the structure as declared in the SDK matches the + // size expected for the version of the structure that the SDK describes. + // + // There are two possible layouts for version 15, and the + // ExpectedSizeForVersion() implementation infers the correct one based on the + // run-time OS version, so if the SDK defines the version 15 structure, this + // test can only be performed if the run-time OS natively uses the same format + // structure as the SDK. + bool test_expected_size_for_version_matches_sdk_sizeof; +#if __MAC_OS_X_VERSION_MAX_ALLOWED == __MAC_10_12 + test_expected_size_for_version_matches_sdk_sizeof = + macos_version_number / 1'00 == 10'12; +#elif __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_13 && \ + __MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_15 + test_expected_size_for_version_matches_sdk_sizeof = + macos_version_number >= 10'13'00 && macos_version_number < 10'15'00; +#else + test_expected_size_for_version_matches_sdk_sizeof = true; +#endif + + if (test_expected_size_for_version_matches_sdk_sizeof) { + EXPECT_EQ(process_types::dyld_all_image_infos::ExpectedSizeForVersion( + &process_reader, kDyldAllImageInfosVersionInSDK), + sizeof(dyld_all_image_infos)); + } + + // Make sure that the computed sizes of various versions of this structure are + // correct at different bitnessses. Version 16 and later are unsupported on + // 32-bit systems due to the OS deprecating 32-bit support in macOS 10.15. + constexpr size_t kSpecialCase = std::numeric_limits::max(); + constexpr size_t kUnsupported = std::numeric_limits::max() - 1; + constexpr struct { + uint32_t version; + size_t size_32; + size_t size_64; + } kVersionsAndSizes[] = { + {1, 17, 25}, + {2, 24, 40}, + {3, 28, 48}, + {5, 40, 72}, + {6, 44, 80}, + {7, 48, 88}, + {8, 56, 104}, + {9, 60, 112}, + {10, 64, 120}, + {11, 80, 152}, + {12, 84, 160}, + {13, 104, 184}, + {14, 164, 304}, + {15, kSpecialCase, kSpecialCase}, + {16, kUnsupported, 328}, + {17, kUnsupported, 368}, + }; + for (size_t index = 0; index < std::size(kVersionsAndSizes); ++index) { + uint32_t version = kVersionsAndSizes[index].version; + SCOPED_TRACE(base::StringPrintf("index %zu, version %u", index, version)); + + if (version == 15) { + if (macos_version_number / 1'00 == 10'12) { + EXPECT_EQ(process_types::internal::dyld_all_image_infos< + process_types::internal::Traits32>:: + ExpectedSizeForVersion(version), + 164u); + EXPECT_EQ(process_types::internal::dyld_all_image_infos< + process_types::internal::Traits64>:: + ExpectedSizeForVersion(version), + 304u); + } else if (macos_version_number >= 10'13'00 && + macos_version_number < 10'15'00) { + EXPECT_EQ(process_types::internal::dyld_all_image_infos< + process_types::internal::Traits32>:: + ExpectedSizeForVersion(version), + 176u); + EXPECT_EQ(process_types::internal::dyld_all_image_infos< + process_types::internal::Traits64>:: + ExpectedSizeForVersion(version), + 320u); + } + + continue; + } + + ASSERT_NE(kVersionsAndSizes[index].size_32, kSpecialCase); + ASSERT_NE(kVersionsAndSizes[index].size_64, kSpecialCase); + + if (kVersionsAndSizes[index].size_32 != kUnsupported) { + EXPECT_EQ(process_types::internal::dyld_all_image_infos< + process_types::internal::Traits32>:: + ExpectedSizeForVersion(version), + kVersionsAndSizes[index].size_32); + } + if (kVersionsAndSizes[index].size_64 != kUnsupported) { + EXPECT_EQ(process_types::internal::dyld_all_image_infos< + process_types::internal::Traits64>:: + ExpectedSizeForVersion(version), + kVersionsAndSizes[index].size_64); + } + } + + process_types::dyld_all_image_infos proctype_image_infos; + ASSERT_TRUE(proctype_image_infos.Read(&process_reader, + dyld_info.all_image_info_addr)); + + ASSERT_EQ(proctype_image_infos.version, self_image_infos->version); + + if (proctype_image_infos.version >= 1) { + EXPECT_EQ(proctype_image_infos.infoArrayCount, + self_image_infos->infoArrayCount); + EXPECT_EQ(proctype_image_infos.infoArray, + reinterpret_cast(self_image_infos->infoArray)); + EXPECT_EQ(proctype_image_infos.notification, + reinterpret_cast(self_image_infos->notification)); + EXPECT_EQ(proctype_image_infos.processDetachedFromSharedRegion, + self_image_infos->processDetachedFromSharedRegion); + } + if (proctype_image_infos.version >= 2) { + EXPECT_EQ(proctype_image_infos.libSystemInitialized, + self_image_infos->libSystemInitialized); + EXPECT_EQ( + proctype_image_infos.dyldImageLoadAddress, + reinterpret_cast(self_image_infos->dyldImageLoadAddress)); + } + if (proctype_image_infos.version >= 3) { + EXPECT_EQ(proctype_image_infos.jitInfo, + reinterpret_cast(self_image_infos->jitInfo)); + } + if (proctype_image_infos.version >= 5) { + EXPECT_EQ(proctype_image_infos.dyldVersion, + reinterpret_cast(self_image_infos->dyldVersion)); + EXPECT_EQ(proctype_image_infos.errorMessage, + reinterpret_cast(self_image_infos->errorMessage)); + EXPECT_EQ(proctype_image_infos.terminationFlags, + implicit_cast(self_image_infos->terminationFlags)); + + TEST_STRING( + process_reader, self_image_infos, proctype_image_infos, dyldVersion); + TEST_STRING( + process_reader, self_image_infos, proctype_image_infos, errorMessage); + } + if (proctype_image_infos.version >= 6) { + EXPECT_EQ( + proctype_image_infos.coreSymbolicationShmPage, + reinterpret_cast(self_image_infos->coreSymbolicationShmPage)); + } + if (proctype_image_infos.version >= 7) { + EXPECT_EQ(proctype_image_infos.systemOrderFlag, + implicit_cast(self_image_infos->systemOrderFlag)); + } +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_7 + if (proctype_image_infos.version >= 8) { + EXPECT_EQ(proctype_image_infos.uuidArrayCount, + implicit_cast(self_image_infos->uuidArrayCount)); + } + if (proctype_image_infos.version >= 9) { + EXPECT_EQ( + proctype_image_infos.dyldAllImageInfosAddress, + reinterpret_cast(self_image_infos->dyldAllImageInfosAddress)); + } + if (proctype_image_infos.version >= 10) { + EXPECT_EQ(proctype_image_infos.initialImageCount, + implicit_cast(self_image_infos->initialImageCount)); + } + if (proctype_image_infos.version >= 11) { + EXPECT_EQ(proctype_image_infos.errorKind, + implicit_cast(self_image_infos->errorKind)); + EXPECT_EQ( + proctype_image_infos.errorClientOfDylibPath, + reinterpret_cast(self_image_infos->errorClientOfDylibPath)); + EXPECT_EQ( + proctype_image_infos.errorTargetDylibPath, + reinterpret_cast(self_image_infos->errorTargetDylibPath)); + EXPECT_EQ(proctype_image_infos.errorSymbol, + reinterpret_cast(self_image_infos->errorSymbol)); + + TEST_STRING(process_reader, + self_image_infos, + proctype_image_infos, + errorClientOfDylibPath); + TEST_STRING(process_reader, + self_image_infos, + proctype_image_infos, + errorTargetDylibPath); + TEST_STRING( + process_reader, self_image_infos, proctype_image_infos, errorSymbol); + } + if (proctype_image_infos.version >= 12) { + EXPECT_EQ(proctype_image_infos.sharedCacheSlide, + implicit_cast(self_image_infos->sharedCacheSlide)); + } +#endif +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_9 + if (proctype_image_infos.version >= 13) { + EXPECT_EQ(memcmp(self_image_infos->sharedCacheUUID, + proctype_image_infos.sharedCacheUUID, + sizeof(self_image_infos->sharedCacheUUID)), + 0); + } +#endif +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_12 + if (proctype_image_infos.version >= 15) { + EXPECT_EQ(proctype_image_infos.infoArrayChangeTimestamp, + self_image_infos->infoArrayChangeTimestamp); + EXPECT_EQ(proctype_image_infos.sharedCacheBaseAddress, + self_image_infos->sharedCacheBaseAddress); + EXPECT_EQ(proctype_image_infos.dyldPath, + reinterpret_cast(self_image_infos->dyldPath)); + for (size_t index = 0; index < std::size(self_image_infos->notifyPorts); + ++index) { + EXPECT_EQ(proctype_image_infos.notifyPorts[index], + self_image_infos->notifyPorts[index]) + << "index " << index; + } + + TEST_STRING( + process_reader, self_image_infos, proctype_image_infos, dyldPath); + } +#endif + +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_12 + // As dyld_all_image_infos has evolved over time, new fields were added to the + // reserved region. process_types::dyld_all_image_infos declares a recent + // version of the structure, but an older SDK may declare an older version + // whose |reserved| member appears at a different (smaller) offset than the + // process_types version. It’s difficult to compare the reserved fields in + // these older SDKs, so only do it where the declarations match. + if (proctype_image_infos.version >= 14) { + for (size_t index = 0; index < std::size(proctype_image_infos.reserved); + ++index) { + EXPECT_EQ(proctype_image_infos.reserved[index], + implicit_cast(self_image_infos->reserved[index])) + << "index " << index; + } +#if defined(ARCH_CPU_64_BITS) + EXPECT_EQ(proctype_image_infos.reserved_4_64, + self_image_infos->reserved[4]); + EXPECT_EQ(proctype_image_infos.reserved_5, self_image_infos->reserved[5]); + EXPECT_EQ(proctype_image_infos.reserved_6, self_image_infos->reserved[6]); +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_12_0 + uint64_t shared_cache_fs_id = self_image_infos->sharedCacheFSID; + uint64_t shared_cache_fs_obj_id = self_image_infos->sharedCacheFSObjID; +#else + uint64_t shared_cache_fs_id = self_image_infos->reserved[7]; + uint64_t shared_cache_fs_obj_id = self_image_infos->reserved[8]; +#endif + EXPECT_EQ(proctype_image_infos.shared_cache_fs_id, shared_cache_fs_id); + EXPECT_EQ(proctype_image_infos.shared_cache_fs_obj_id, + shared_cache_fs_obj_id); +#endif + } +#endif + +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_13 + if (proctype_image_infos.version >= 15 && macos_version_number >= 10'13'00) { + EXPECT_EQ(proctype_image_infos.compact_dyld_image_info_addr, + self_image_infos->compact_dyld_image_info_addr); + EXPECT_EQ(proctype_image_infos.compact_dyld_image_info_size, + self_image_infos->compact_dyld_image_info_size); + } +#endif + +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_15 + if (proctype_image_infos.version >= 16) { + EXPECT_EQ(proctype_image_infos.platform, self_image_infos->platform); + } +#endif + + if (proctype_image_infos.version >= 1) { + std::vector proctype_image_info_vector( + proctype_image_infos.infoArrayCount); + ASSERT_TRUE(process_types::dyld_image_info::ReadArrayInto( + &process_reader, + proctype_image_infos.infoArray, + proctype_image_info_vector.size(), + &proctype_image_info_vector[0])); + + for (size_t index = 0; + index < proctype_image_infos.infoArrayCount; + ++index) { + const dyld_image_info* self_image_info = + &self_image_infos->infoArray[index]; + const process_types::dyld_image_info& proctype_image_info = + proctype_image_info_vector[index]; + + EXPECT_EQ(proctype_image_info.imageLoadAddress, + reinterpret_cast(self_image_info->imageLoadAddress)) + << "index " << index; + EXPECT_EQ(proctype_image_info.imageFilePath, + reinterpret_cast(self_image_info->imageFilePath)) + << "index " << index; + EXPECT_EQ(proctype_image_info.imageFileModDate, + implicit_cast(self_image_info->imageFileModDate)) + << "index " << index; + + TEST_STRING( + process_reader, self_image_info, proctype_image_info, imageFilePath); + } + } + +#if __MAC_OS_X_VERSION_MAX_ALLOWED >= __MAC_10_7 + if (proctype_image_infos.version >= 8) { + std::vector proctype_uuid_info_vector( + proctype_image_infos.uuidArrayCount); + ASSERT_TRUE(process_types::dyld_uuid_info::ReadArrayInto( + &process_reader, + proctype_image_infos.uuidArray, + proctype_uuid_info_vector.size(), + &proctype_uuid_info_vector[0])); + + for (size_t index = 0; + index < proctype_image_infos.uuidArrayCount; + ++index) { + const dyld_uuid_info* self_uuid_info = + &self_image_infos->uuidArray[index]; + const process_types::dyld_uuid_info& proctype_uuid_info = + proctype_uuid_info_vector[index]; + + EXPECT_EQ(proctype_uuid_info.imageLoadAddress, + reinterpret_cast(self_uuid_info->imageLoadAddress)) + << "index " << index; + EXPECT_EQ(memcmp(self_uuid_info->imageUUID, + proctype_uuid_info.imageUUID, + sizeof(self_uuid_info->imageUUID)), + 0) + << "index " << index; + } + } +#endif +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/mac/system_snapshot_mac.cc b/shared/sentry/external/crashpad/snapshot/mac/system_snapshot_mac.cc new file mode 100644 index 000000000..7a12e749c --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/system_snapshot_mac.cc @@ -0,0 +1,394 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/mac/system_snapshot_mac.h" + +#include +#include +#include +#include +#include + +#include + +#include "base/logging.h" +#include "base/notreached.h" +#include "base/scoped_clear_last_error.h" +#include "base/strings/stringprintf.h" +#include "build/build_config.h" +#include "snapshot/cpu_context.h" +#include "snapshot/mac/process_reader_mac.h" +#include "snapshot/posix/timezone.h" +#include "util/mac/mac_util.h" +#include "util/mac/sysctl.h" +#include "util/numeric/in_range_cast.h" + +namespace crashpad { + +namespace { + +template +int ReadIntSysctlByName_NoLog(const char* name, T* value) { + size_t value_len = sizeof(*value); + return sysctlbyname(name, value, &value_len, nullptr, 0); +} + +template +T ReadIntSysctlByName(const char* name, T default_value) { + T value; + if (ReadIntSysctlByName_NoLog(name, &value) != 0) { + PLOG(WARNING) << "sysctlbyname " << name; + return default_value; + } + + return value; +} + +template +T CastIntSysctlByName(const char* name, T default_value) { + int int_value = ReadIntSysctlByName(name, default_value); + return InRangeCast(int_value, default_value); +} + +#if defined(ARCH_CPU_X86_FAMILY) +void CallCPUID(uint32_t leaf, + uint32_t* eax, + uint32_t* ebx, + uint32_t* ecx, + uint32_t* edx) { + asm("cpuid" + : "=a"(*eax), "=b"(*ebx), "=c"(*ecx), "=d"(*edx) + : "a"(leaf), "b"(0), "c"(0), "d"(0)); +} +#endif + +} // namespace + +namespace internal { + +SystemSnapshotMac::SystemSnapshotMac() + : SystemSnapshot(), + os_version_full_(), + os_version_build_(), + process_reader_(nullptr), + snapshot_time_(nullptr), + os_version_major_(0), + os_version_minor_(0), + os_version_bugfix_(0), + os_server_(false), + initialized_() { +} + +SystemSnapshotMac::~SystemSnapshotMac() { +} + +void SystemSnapshotMac::Initialize(ProcessReaderMac* process_reader, + const timeval* snapshot_time) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + process_reader_ = process_reader; + snapshot_time_ = snapshot_time; + + // MacOSVersionComponents() logs its own warnings if it can’t figure anything + // out. It’s not fatal if this happens. The default values are reasonable. + std::string os_version_string; + MacOSVersionComponents(&os_version_major_, + &os_version_minor_, + &os_version_bugfix_, + &os_version_build_, + &os_server_, + &os_version_string); + + std::string uname_string; + utsname uts; + if (uname(&uts) != 0) { + PLOG(WARNING) << "uname"; + } else { + uname_string = base::StringPrintf( + "%s %s %s %s", uts.sysname, uts.release, uts.version, uts.machine); + } + + if (!os_version_string.empty()) { + if (!uname_string.empty()) { + os_version_full_ = base::StringPrintf( + "%s; %s", os_version_string.c_str(), uname_string.c_str()); + } else { + os_version_full_ = os_version_string; + } + } else { + os_version_full_ = uname_string; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); +} + +CPUArchitecture SystemSnapshotMac::GetCPUArchitecture() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + +#if defined(ARCH_CPU_X86_FAMILY) + return process_reader_->Is64Bit() ? kCPUArchitectureX86_64 + : kCPUArchitectureX86; +#elif defined(ARCH_CPU_ARM64) + return kCPUArchitectureARM64; +#else +#error port to your architecture +#endif +} + +uint32_t SystemSnapshotMac::CPURevision() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + +#if defined(ARCH_CPU_X86_FAMILY) + // machdep.cpu.family and machdep.cpu.model already take the extended family + // and model IDs into account. See 10.9.2 xnu-2422.90.20/osfmk/i386/cpuid.c + // cpuid_set_generic_info(). + uint16_t family = CastIntSysctlByName("machdep.cpu.family", 0); + uint8_t model = CastIntSysctlByName("machdep.cpu.model", 0); + uint8_t stepping = CastIntSysctlByName("machdep.cpu.stepping", 0); + + return (family << 16) | (model << 8) | stepping; +#elif defined(ARCH_CPU_ARM64) + // TODO(macos_arm64): Verify and test. + return CastIntSysctlByName("hw.cpufamily", 0); +#else +#error port to your architecture +#endif +} + +uint8_t SystemSnapshotMac::CPUCount() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return CastIntSysctlByName("hw.ncpu", 1); +} + +std::string SystemSnapshotMac::CPUVendor() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + +#if defined(ARCH_CPU_X86_FAMILY) + return ReadStringSysctlByName("machdep.cpu.vendor", true); +#elif defined(ARCH_CPU_ARM64) + return ReadStringSysctlByName("machdep.cpu.brand_string", true); +#else +#error port to your architecture +#endif +} + +void SystemSnapshotMac::CPUFrequency( + uint64_t* current_hz, uint64_t* max_hz) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); +#if defined(ARCH_CPU_X86_FAMILY) + *current_hz = ReadIntSysctlByName("hw.cpufrequency", 0); + *max_hz = ReadIntSysctlByName("hw.cpufrequency_max", 0); +#elif defined(ARCH_CPU_ARM64) + // TODO(https://crashpad.chromium.org/bug/352): When production arm64 + // hardware is available, determine whether CPU frequency is visible anywhere + // (likely via a sysctl or via IOKit) and use it if feasible. + *current_hz = 0; + *max_hz = 0; +#else +#error port to your architecture +#endif +} + +uint32_t SystemSnapshotMac::CPUX86Signature() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + +#if defined(ARCH_CPU_X86_FAMILY) + return ReadIntSysctlByName("machdep.cpu.signature", 0); +#else + NOTREACHED(); + return 0; +#endif +} + +uint64_t SystemSnapshotMac::CPUX86Features() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + +#if defined(ARCH_CPU_X86_FAMILY) + return ReadIntSysctlByName("machdep.cpu.feature_bits", 0); +#else + NOTREACHED(); + return 0; +#endif +} + +uint64_t SystemSnapshotMac::CPUX86ExtendedFeatures() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + +#if defined(ARCH_CPU_X86_FAMILY) + return ReadIntSysctlByName("machdep.cpu.extfeature_bits", 0); +#else + NOTREACHED(); + return 0; +#endif +} + +uint32_t SystemSnapshotMac::CPUX86Leaf7Features() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + +#if defined(ARCH_CPU_X86_FAMILY) + // The machdep.cpu.leaf7_feature_bits sysctl isn’t supported prior to OS X + // 10.7, so read this by calling cpuid directly. + // + // machdep.cpu.max_basic could be used to check whether to read the leaf, but + // that sysctl isn’t supported prior to Mac OS X 10.6, so read the maximum + // basic leaf by calling cpuid directly as well. All CPUs that Apple is known + // to have shipped should support a maximum basic leaf value of at least 0xa. + uint32_t eax, ebx, ecx, edx; + CallCPUID(0, &eax, &ebx, &ecx, &edx); + if (eax < 7) { + return 0; + } + + CallCPUID(7, &eax, &ebx, &ecx, &edx); + return ebx; +#else + NOTREACHED(); + return 0; +#endif +} + +bool SystemSnapshotMac::CPUX86SupportsDAZ() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + +#if defined(ARCH_CPU_X86_FAMILY) + // The correct way to check for denormals-as-zeros (DAZ) support is to examine + // mxcsr mask, which can be done with fxsave. See Intel Software Developer’s + // Manual, Volume 1: Basic Architecture (253665-051), 11.6.3 “Checking for the + // DAZ Flag in the MXCSR Registerâ€. Note that since this function tests for + // DAZ support in the CPU, it checks the mxcsr mask. Testing mxcsr would + // indicate whether DAZ is actually enabled, which is a per-thread context + // concern. + // + // All CPUs that Apple is known to have shipped should support DAZ. + + // Test for fxsave support. + uint64_t features = CPUX86Features(); + if (!(features & (UINT64_C(1) << 24))) { + return false; + } + + // Call fxsave. +#if defined(ARCH_CPU_X86) + CPUContextX86::Fxsave fxsave __attribute__((aligned(16))) = {}; +#elif defined(ARCH_CPU_X86_64) + CPUContextX86_64::Fxsave fxsave __attribute__((aligned(16))) = {}; +#endif + static_assert(sizeof(fxsave) == 512, "fxsave size"); + static_assert(offsetof(decltype(fxsave), mxcsr_mask) == 28, + "mxcsr_mask offset"); + asm("fxsave %0" : "=m"(fxsave)); + + // Test the DAZ bit. + return fxsave.mxcsr_mask & (1 << 6); +#else + NOTREACHED(); + return false; +#endif +} + +SystemSnapshot::OperatingSystem SystemSnapshotMac::GetOperatingSystem() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return kOperatingSystemMacOSX; +} + +bool SystemSnapshotMac::OSServer() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return os_server_; +} + +void SystemSnapshotMac::OSVersion(int* major, + int* minor, + int* bugfix, + std::string* build) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *major = os_version_major_; + *minor = os_version_minor_; + *bugfix = os_version_bugfix_; + build->assign(os_version_build_); +} + +std::string SystemSnapshotMac::OSVersionFull() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return os_version_full_; +} + +std::string SystemSnapshotMac::MachineDescription() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + std::string model; + std::string board_id; + MacModelAndBoard(&model, &board_id); + + if (!model.empty()) { + if (!board_id.empty()) { + return base::StringPrintf("%s (%s)", model.c_str(), board_id.c_str()); + } + return model; + } + if (!board_id.empty()) { + return base::StringPrintf("(%s)", board_id.c_str()); + } + return std::string(); +} + +bool SystemSnapshotMac::NXEnabled() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + int value; + if (ReadIntSysctlByName_NoLog("kern.nx", &value) != 0) { + { + // Support for the kern.nx sysctlbyname is compiled out of production + // kernels on macOS 10.14.5 and later, although it’s available in + // development and debug kernels. Compare 10.14.3 + // xnu-4903.241.1/bsd/kern/kern_sysctl.c to 10.15.0 + // xnu-6153.11.26/bsd/kern/kern_sysctl.c (10.14.4 and 10.14.5 xnu source + // are not yet available). In newer production kernels, NX is always + // enabled. See 10.15.0 xnu-6153.11.26/osfmk/x86_64/pmap.c nx_enabled. +#if __MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_14 + const bool nx_always_enabled = true; +#else // DT >= 10.14 + base::ScopedClearLastError reset_errno; + const bool nx_always_enabled = MacOSVersionNumber() >= 10'14'00; +#endif // DT >= 10.14 + if (nx_always_enabled) { + return true; + } + } + + // Even if sysctlbyname should have worked, NX is enabled by default in all + // supported configurations, so return true even while warning. + PLOG(WARNING) << "sysctlbyname kern.nx"; + return true; + } + + return value; +} + +void SystemSnapshotMac::TimeZone(DaylightSavingTimeStatus* dst_status, + int* standard_offset_seconds, + int* daylight_offset_seconds, + std::string* standard_name, + std::string* daylight_name) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + internal::TimeZone(*snapshot_time_, + dst_status, + standard_offset_seconds, + daylight_offset_seconds, + standard_name, + daylight_name); +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/mac/system_snapshot_mac.h b/shared/sentry/external/crashpad/snapshot/mac/system_snapshot_mac.h new file mode 100644 index 000000000..cca6be253 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/system_snapshot_mac.h @@ -0,0 +1,102 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MAC_SYSTEM_SNAPSHOT_MAC_H_ +#define CRASHPAD_SNAPSHOT_MAC_SYSTEM_SNAPSHOT_MAC_H_ + +#include + +#include + +#include "snapshot/system_snapshot.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +class ProcessReaderMac; + +namespace internal { + +//! \brief A SystemSnapshot of the running system, when the system runs macOS. +class SystemSnapshotMac final : public SystemSnapshot { + public: + SystemSnapshotMac(); + + SystemSnapshotMac(const SystemSnapshotMac&) = delete; + SystemSnapshotMac& operator=(const SystemSnapshotMac&) = delete; + + ~SystemSnapshotMac() override; + + //! \brief Initializes the object. + //! + //! \param[in] process_reader A reader for the process being snapshotted. + //! \n\n + //! It seems odd that a system snapshot implementation would need a + //! ProcessReaderMac, but some of the information reported about the + //! system depends on the process it’s being reported for. For example, + //! the architecture returned by GetCPUArchitecture() should be the + //! architecture of the process, which may be different than the native + //! architecture of the system: an x86_64 system can run both x86_64 and + //! 32-bit x86 processes. + //! \param[in] snapshot_time The time of the snapshot being taken. + //! \n\n + //! This parameter is necessary for TimeZone() to determine whether + //! daylight saving time was in effect at the time the snapshot was taken. + //! Otherwise, it would need to base its determination on the current + //! time, which may be different than the snapshot time for snapshots + //! generated around the daylight saving transition time. + void Initialize(ProcessReaderMac* process_reader, + const timeval* snapshot_time); + + // SystemSnapshot: + + CPUArchitecture GetCPUArchitecture() const override; + uint32_t CPURevision() const override; + uint8_t CPUCount() const override; + std::string CPUVendor() const override; + void CPUFrequency(uint64_t* current_hz, uint64_t* max_hz) const override; + uint32_t CPUX86Signature() const override; + uint64_t CPUX86Features() const override; + uint64_t CPUX86ExtendedFeatures() const override; + uint32_t CPUX86Leaf7Features() const override; + bool CPUX86SupportsDAZ() const override; + OperatingSystem GetOperatingSystem() const override; + bool OSServer() const override; + void OSVersion( + int* major, int* minor, int* bugfix, std::string* build) const override; + std::string OSVersionFull() const override; + bool NXEnabled() const override; + std::string MachineDescription() const override; + void TimeZone(DaylightSavingTimeStatus* dst_status, + int* standard_offset_seconds, + int* daylight_offset_seconds, + std::string* standard_name, + std::string* daylight_name) const override; + + private: + std::string os_version_full_; + std::string os_version_build_; + ProcessReaderMac* process_reader_; // weak + const timeval* snapshot_time_; // weak + int os_version_major_; + int os_version_minor_; + int os_version_bugfix_; + bool os_server_; + InitializationStateDcheck initialized_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MAC_SYSTEM_SNAPSHOT_MAC_H_ diff --git a/shared/sentry/external/crashpad/snapshot/mac/system_snapshot_mac_test.cc b/shared/sentry/external/crashpad/snapshot/mac/system_snapshot_mac_test.cc new file mode 100644 index 000000000..6bf32b1fa --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/system_snapshot_mac_test.cc @@ -0,0 +1,145 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/mac/system_snapshot_mac.h" + +#include + +#include + +#include "build/build_config.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "snapshot/mac/process_reader_mac.h" +#include "test/errors.h" +#include "util/mac/mac_util.h" + +namespace crashpad { +namespace test { +namespace { + +// SystemSnapshotMac objects would be cumbersome to construct in each test that +// requires one, because of the repetitive and mechanical work necessary to set +// up a ProcessReaderMac and timeval, along with the checks to verify that these +// operations succeed. This test fixture class handles the initialization work +// so that individual tests don’t have to. +class SystemSnapshotMacTest : public testing::Test { + public: + SystemSnapshotMacTest() + : Test(), + process_reader_(), + snapshot_time_(), + system_snapshot_() { + } + + SystemSnapshotMacTest(const SystemSnapshotMacTest&) = delete; + SystemSnapshotMacTest& operator=(const SystemSnapshotMacTest&) = delete; + + const internal::SystemSnapshotMac& system_snapshot() const { + return system_snapshot_; + } + + // testing::Test: + void SetUp() override { + ASSERT_TRUE(process_reader_.Initialize(mach_task_self())); + ASSERT_EQ(gettimeofday(&snapshot_time_, nullptr), 0) + << ErrnoMessage("gettimeofday"); + system_snapshot_.Initialize(&process_reader_, &snapshot_time_); + } + + private: + ProcessReaderMac process_reader_; + timeval snapshot_time_; + internal::SystemSnapshotMac system_snapshot_; +}; + +TEST_F(SystemSnapshotMacTest, GetCPUArchitecture) { + CPUArchitecture cpu_architecture = system_snapshot().GetCPUArchitecture(); + +#if defined(ARCH_CPU_X86) + EXPECT_EQ(cpu_architecture, kCPUArchitectureX86); +#elif defined(ARCH_CPU_X86_64) + EXPECT_EQ(cpu_architecture, kCPUArchitectureX86_64); +#elif defined(ARCH_CPU_ARM64) + EXPECT_EQ(cpu_architecture, kCPUArchitectureARM64); +#else +#error port to your architecture +#endif +} + +TEST_F(SystemSnapshotMacTest, CPUCount) { + EXPECT_GE(system_snapshot().CPUCount(), 1); +} + +TEST_F(SystemSnapshotMacTest, CPUVendor) { + std::string cpu_vendor = system_snapshot().CPUVendor(); + +#if defined(ARCH_CPU_X86_FAMILY) + // Apple has only shipped Intel x86-family CPUs, but here’s a small nod to the + // “Hackintosh†crowd. + if (cpu_vendor != "GenuineIntel" && cpu_vendor != "AuthenticAMD") { + FAIL() << "cpu_vendor " << cpu_vendor; + } +#elif defined(ARCH_CPU_ARM64) + EXPECT_THAT(cpu_vendor, testing::StartsWith("Apple ")); +#else +#error port to your architecture +#endif +} + +#if defined(ARCH_CPU_X86_FAMILY) + +TEST_F(SystemSnapshotMacTest, CPUX86SupportsDAZ) { + // All x86-family CPUs that Apple is known to have shipped should support DAZ. + EXPECT_TRUE(system_snapshot().CPUX86SupportsDAZ()); +} + +#endif + +TEST_F(SystemSnapshotMacTest, GetOperatingSystem) { + EXPECT_EQ(system_snapshot().GetOperatingSystem(), + SystemSnapshot::kOperatingSystemMacOSX); +} + +TEST_F(SystemSnapshotMacTest, OSVersion) { + int major; + int minor; + int bugfix; + std::string build; + system_snapshot().OSVersion(&major, &minor, &bugfix, &build); + + const int macos_version_number = MacOSVersionNumber(); + EXPECT_EQ(major * 1'00'00 + minor * 1'00 + + (macos_version_number >= 10'13'04 ? bugfix : 0), + macos_version_number); + EXPECT_FALSE(build.empty()); +} + +TEST_F(SystemSnapshotMacTest, OSVersionFull) { + EXPECT_FALSE(system_snapshot().OSVersionFull().empty()); +} + +TEST_F(SystemSnapshotMacTest, MachineDescription) { + EXPECT_FALSE(system_snapshot().MachineDescription().empty()); +} + +TEST_F(SystemSnapshotMacTest, NXEnabled) { + // Assume NX will always be enabled, as it was always enabled by default on + // all supported versions of macOS. + EXPECT_TRUE(system_snapshot().NXEnabled()); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/mac/thread_snapshot_mac.cc b/shared/sentry/external/crashpad/snapshot/mac/thread_snapshot_mac.cc new file mode 100644 index 000000000..7ae5f9fae --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/thread_snapshot_mac.cc @@ -0,0 +1,162 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/mac/thread_snapshot_mac.h" + +#include "base/check_op.h" +#include "snapshot/mac/cpu_context_mac.h" +#include "snapshot/mac/process_reader_mac.h" + +#ifdef CLIENT_STACKTRACES_ENABLED +#include +#endif + +namespace crashpad { +namespace internal { + +ThreadSnapshotMac::ThreadSnapshotMac() + : ThreadSnapshot(), + context_union_(), + context_(), + stack_(), + thread_id_(0), + thread_specific_data_address_(0), + thread_(MACH_PORT_NULL), + suspend_count_(0), + priority_(0), + initialized_() {} + +ThreadSnapshotMac::~ThreadSnapshotMac() {} + +bool ThreadSnapshotMac::Initialize( + ProcessReaderMac* process_reader, + const ProcessReaderMac::Thread& process_reader_thread) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + thread_ = process_reader_thread.port; + thread_id_ = process_reader_thread.id; + suspend_count_ = process_reader_thread.suspend_count; + priority_ = process_reader_thread.priority; + thread_specific_data_address_ = + process_reader_thread.thread_specific_data_address; + + stack_.Initialize(process_reader->Memory(), + process_reader_thread.stack_region_address, + process_reader_thread.stack_region_size); + +#if defined(ARCH_CPU_X86_FAMILY) + if (process_reader->Is64Bit()) { + context_.architecture = kCPUArchitectureX86_64; + context_.x86_64 = &context_union_.x86_64; + InitializeCPUContextX86_64(context_.x86_64, + THREAD_STATE_NONE, + nullptr, + 0, + &process_reader_thread.thread_context.t64, + &process_reader_thread.float_context.f64, + &process_reader_thread.debug_context.d64); + } else { + context_.architecture = kCPUArchitectureX86; + context_.x86 = &context_union_.x86; + InitializeCPUContextX86(context_.x86, + THREAD_STATE_NONE, + nullptr, + 0, + &process_reader_thread.thread_context.t32, + &process_reader_thread.float_context.f32, + &process_reader_thread.debug_context.d32); + } +#elif defined(ARCH_CPU_ARM64) + context_.architecture = kCPUArchitectureARM64; + context_.arm64 = &context_union_.arm64; + InitializeCPUContextARM64(context_.arm64, + THREAD_STATE_NONE, + nullptr, + 0, + &process_reader_thread.thread_context, + &process_reader_thread.float_context, + &process_reader_thread.debug_context); +#else +#error Port to your CPU architecture +#endif + +#ifdef CLIENT_STACKTRACES_ENABLED + unw_addr_space_t as = unw_create_addr_space_for_task(process_reader->task_); + unw_cursor_t cursor; + + if (unw_init_remote_thread(&cursor, as, thread_) == UNW_ESUCCESS) { + do { + unw_word_t addr; + unw_get_reg(&cursor, UNW_REG_IP, &addr); + + std::string sym(""); + char buf[1024]; + unw_word_t symbol_offset; + if (unw_get_proc_name(&cursor, buf, sizeof(buf), &symbol_offset) == + UNW_ESUCCESS) { + if (buf[0] == '_') { + sym = std::string(buf + 1); + } else { + sym = std::string(buf); + } + } + + FrameSnapshot frame(addr, sym); + frames_.push_back(frame); + } while (unw_step(&cursor) > 0); + } + + unw_destroy_addr_space(as); +#endif + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +const CPUContext* ThreadSnapshotMac::Context() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &context_; +} + +const MemorySnapshot* ThreadSnapshotMac::Stack() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &stack_; +} + +uint64_t ThreadSnapshotMac::ThreadID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_id_; +} + +int ThreadSnapshotMac::SuspendCount() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return suspend_count_; +} + +int ThreadSnapshotMac::Priority() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return priority_; +} + +uint64_t ThreadSnapshotMac::ThreadSpecificDataAddress() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_specific_data_address_; +} + +std::vector ThreadSnapshotMac::ExtraMemory() const { + return std::vector(); +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/mac/thread_snapshot_mac.h b/shared/sentry/external/crashpad/snapshot/mac/thread_snapshot_mac.h new file mode 100644 index 000000000..4d9cb3167 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/mac/thread_snapshot_mac.h @@ -0,0 +1,92 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MAC_THREAD_SNAPSHOT_MAC_H_ +#define CRASHPAD_SNAPSHOT_MAC_THREAD_SNAPSHOT_MAC_H_ + +#include +#include + +#include "build/build_config.h" +#include "snapshot/cpu_context.h" +#include "snapshot/mac/process_reader_mac.h" +#include "snapshot/memory_snapshot.h" +#include "snapshot/memory_snapshot_generic.h" +#include "snapshot/thread_snapshot.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +class ProcessReaderMac; + +namespace internal { + +//! \brief A ThreadSnapshot of a thread in a running (or crashed) process on a +//! macOS system. +class ThreadSnapshotMac final : public ThreadSnapshot { + public: + ThreadSnapshotMac(); + + ThreadSnapshotMac(const ThreadSnapshotMac&) = delete; + ThreadSnapshotMac& operator=(const ThreadSnapshotMac&) = delete; + + ~ThreadSnapshotMac() override; + + //! \brief Initializes the object. + //! + //! \param[in] process_reader A ProcessReaderMac for the task containing the + //! thread. + //! \param[in] process_reader_thread The thread within the ProcessReaderMac + //! for which the snapshot should be created. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(ProcessReaderMac* process_reader, + const ProcessReaderMac::Thread& process_reader_thread); + + // ThreadSnapshot: + + const CPUContext* Context() const override; + const MemorySnapshot* Stack() const override; + uint64_t ThreadID() const override; + int SuspendCount() const override; + int Priority() const override; + uint64_t ThreadSpecificDataAddress() const override; + std::vector ExtraMemory() const override; + + private: + union { +#if defined(ARCH_CPU_X86_FAMILY) + CPUContextX86 x86; + CPUContextX86_64 x86_64; +#elif defined(ARCH_CPU_ARM64) + CPUContextARM64 arm64; +#else +#error Port to your CPU architecture +#endif + } context_union_; + CPUContext context_; + MemorySnapshotGeneric stack_; + uint64_t thread_id_; + uint64_t thread_specific_data_address_; + thread_t thread_; + int suspend_count_; + int priority_; + InitializationStateDcheck initialized_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MAC_THREAD_SNAPSHOT_MAC_H_ diff --git a/shared/sentry/external/crashpad/snapshot/memory_map_region_snapshot.h b/shared/sentry/external/crashpad/snapshot/memory_map_region_snapshot.h new file mode 100644 index 000000000..4d271f7ab --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/memory_map_region_snapshot.h @@ -0,0 +1,35 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MEMORY_MAP_REGION_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_MEMORY_MAP_REGION_SNAPSHOT_H_ + +#include +#include + +namespace crashpad { + +//! \brief An abstract interface to a snapshot representing a region of the +//! memory map present in the snapshot process. +class MemoryMapRegionSnapshot { + public: + virtual ~MemoryMapRegionSnapshot() {} + + //! \brief Gets a MINIDUMP_MEMORY_INFO representing the region. + virtual const MINIDUMP_MEMORY_INFO& AsMinidumpMemoryInfo() const = 0; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MEMORY_MAP_REGION_SNAPSHOT_H_ diff --git a/shared/sentry/external/crashpad/snapshot/memory_snapshot.cc b/shared/sentry/external/crashpad/snapshot/memory_snapshot.cc new file mode 100644 index 000000000..c86fa48d1 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/memory_snapshot.cc @@ -0,0 +1,96 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/memory_snapshot.h" + +#include + +#include "base/format_macros.h" +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "util/numeric/checked_range.h" + +namespace crashpad { +namespace { + +bool DetermineMergedRangeImpl(bool log, + const MemorySnapshot* a, + const MemorySnapshot* b, + CheckedRange* merged) { + if (a->Size() == 0) { + LOG_IF(ERROR, log) << base::StringPrintf( + "invalid empty range at 0x%" PRIx64, a->Address()); + return false; + } + + if (b->Size() == 0) { + LOG_IF(ERROR, log) << base::StringPrintf( + "invalid empty range at 0x%" PRIx64, b->Address()); + return false; + } + + CheckedRange range_a(a->Address(), a->Size()); + if (!range_a.IsValid()) { + LOG_IF(ERROR, log) << base::StringPrintf("invalid range at 0x%" PRIx64 + ", size %" PRIuS, + range_a.base(), + range_a.size()); + return false; + } + + CheckedRange range_b(b->Address(), b->Size()); + if (!range_b.IsValid()) { + LOG_IF(ERROR, log) << base::StringPrintf("invalid range at 0x%" PRIx64 + ", size %" PRIuS, + range_b.base(), + range_b.size()); + return false; + } + + if (!range_a.OverlapsRange(range_b) && range_a.end() != range_b.base() && + range_b.end() != range_a.base()) { + LOG_IF(ERROR, log) << base::StringPrintf( + "ranges not overlapping or abutting: (0x%" PRIx64 ", size %" PRIuS + ") and (0x%" PRIx64 ", size %" PRIuS ")", + range_a.base(), + range_a.size(), + range_b.base(), + range_b.size()); + return false; + } + + if (merged) { + uint64_t base = std::min(range_a.base(), range_b.base()); + uint64_t end = std::max(range_a.end(), range_b.end()); + size_t size = static_cast(end - base); + merged->SetRange(base, size); + } + return true; +} + +} // namespace + +bool LoggingDetermineMergedRange(const MemorySnapshot* a, + const MemorySnapshot* b, + CheckedRange* merged) { + return DetermineMergedRangeImpl(true, a, b, merged); +} + +bool DetermineMergedRange(const MemorySnapshot* a, + const MemorySnapshot* b, + CheckedRange* merged) { + return DetermineMergedRangeImpl(false, a, b, merged); +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/memory_snapshot.h b/shared/sentry/external/crashpad/snapshot/memory_snapshot.h new file mode 100644 index 000000000..5dbac5ad2 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/memory_snapshot.h @@ -0,0 +1,116 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MEMORY_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_MEMORY_SNAPSHOT_H_ + +#include +#include + +#include + +#include "util/numeric/checked_range.h" + +namespace crashpad { + +//! \brief An abstract interface to a snapshot representing a region of memory +//! present in a snapshot process. +class MemorySnapshot { + public: + //! \brief An interface that MemorySnapshot clients must implement in order to + //! receive memory snapshot data. + //! + //! This callback-based model frees MemorySnapshot implementations from having + //! to deal with memory region ownership problems. When a memory snapshot’s + //! data is read, it will be passed to a delegate method. + class Delegate { + public: + virtual ~Delegate() {} + + //! \brief Called by MemorySnapshot::Read() to provide data requested by a + //! call to that method. + //! + //! \param[in] data A pointer to the data that was read. The callee does not + //! take ownership of this data. This data is only valid for the + //! duration of the call to this method. This parameter may be `nullptr` + //! if \a size is `0`. + //! \param[in] size The size of the data that was read. + //! + //! \return `true` on success, `false` on failure. MemoryDelegate::Read() + //! will use this as its own return value. + virtual bool MemorySnapshotDelegateRead(void* data, size_t size) = 0; + }; + + virtual ~MemorySnapshot() {} + + //! \brief The base address of the memory snapshot in the snapshot process’ + //! address space. + virtual uint64_t Address() const = 0; + + //! \brief The size of the memory snapshot. + virtual size_t Size() const = 0; + + //! \brief Calls Delegate::MemorySnapshotDelegateRead(), providing it with + //! the memory snapshot’s data. + //! + //! Implementations do not necessarily read the memory snapshot data prior to + //! this method being called. Memory snapshot data may be loaded lazily and + //! may be discarded after being passed to the delegate. This provides clean + //! memory management without burdening a snapshot implementation with the + //! requirement that it track all memory region data simultaneously. + //! + //! \return `false` on failure, otherwise, the return value of + //! Delegate::MemorySnapshotDelegateRead(), which should be `true` on + //! success and `false` on failure. + virtual bool Read(Delegate* delegate) const = 0; + + //! \brief Creates a new MemorySnapshot based on merging this one with \a + //! other. + //! + //! The ranges described by the two snapshots must either overlap or abut, and + //! must be of the same concrete type. + //! + //! \return A newly allocated MemorySnapshot representing the merged range, or + //! `nullptr` with an error logged. + virtual const MemorySnapshot* MergeWithOtherSnapshot( + const MemorySnapshot* other) const = 0; +}; + +//! \brief Given two memory snapshots, checks if they're overlapping or +//! abutting, and if so, returns the result of merging the two ranges. +//! +//! This function is useful to implement +//! MemorySnapshot::MergeWithOtherSnapshot(). +//! +//! \param[in] a The first range. Must have Size() > 0. +//! \param[in] b The second range. Must have Size() > 0. +//! \param[out] merged The resulting merged range. May be `nullptr` if only a +//! characterization of the ranges is desired. +//! +//! \return `true` if the input ranges overlap or abut, with \a merged filled +//! out, otherwise, `false` with an error logged if \a log is `true`. +bool LoggingDetermineMergedRange(const MemorySnapshot* a, + const MemorySnapshot* b, + CheckedRange* merged); + +//! \brief The same as LoggingDetermineMergedRange but with no errors logged. +//! +//! \sa LoggingDetermineMergedRange +bool DetermineMergedRange(const MemorySnapshot* a, + const MemorySnapshot* b, + CheckedRange* merged); + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MEMORY_SNAPSHOT_H_ diff --git a/shared/sentry/external/crashpad/snapshot/memory_snapshot_generic.h b/shared/sentry/external/crashpad/snapshot/memory_snapshot_generic.h new file mode 100644 index 000000000..b09e7a960 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/memory_snapshot_generic.h @@ -0,0 +1,121 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MEMORY_SNAPSHOT_GENERIC_H_ +#define CRASHPAD_SNAPSHOT_MEMORY_SNAPSHOT_GENERIC_H_ + +#include +#include + +#include "base/logging.h" +#include "base/numerics/safe_math.h" +#include "snapshot/memory_snapshot.h" +#include "util/misc/address_types.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/process/process_memory.h" + +namespace crashpad { +namespace internal { + +//! \brief A MemorySnapshot of a memory region in a process on the running +//! system. Works on multiple platforms by using a platform-specific +//! ProcessMemory object. +class MemorySnapshotGeneric final : public MemorySnapshot { + public: + MemorySnapshotGeneric() = default; + + MemorySnapshotGeneric(const MemorySnapshotGeneric&) = delete; + MemorySnapshotGeneric& operator=(const MemorySnapshotGeneric&) = delete; + + ~MemorySnapshotGeneric() = default; + + //! \brief Initializes the object. + //! + //! Memory is read lazily. No attempt is made to read the memory snapshot data + //! until Read() is called, and the memory snapshot data is discared when + //! Read() returns. + //! + //! \param[in] process_memory A reader for the process being snapshotted. + //! \param[in] address The base address of the memory region to snapshot, in + //! the snapshot process’ address space. + //! \param[in] size The size of the memory region to snapshot. + void Initialize(const ProcessMemory* process_memory, + VMAddress address, + VMSize size) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + process_memory_ = process_memory; + address_ = address; + size_ = base::checked_cast(size); + INITIALIZATION_STATE_SET_VALID(initialized_); + } + + // MemorySnapshot: + + uint64_t Address() const override { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return address_; + } + + size_t Size() const override { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return size_; + } + + bool Read(Delegate* delegate) const override { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (size_ == 0) { + return delegate->MemorySnapshotDelegateRead(nullptr, size_); + } + + std::unique_ptr buffer(new uint8_t[size_]); + if (!process_memory_->Read(address_, size_, buffer.get())) { + return false; + } + return delegate->MemorySnapshotDelegateRead(buffer.get(), size_); + } + + const MemorySnapshot* MergeWithOtherSnapshot( + const MemorySnapshot* other) const override { + const MemorySnapshotGeneric* other_as_memory_snapshot_concrete = + reinterpret_cast(other); + if (process_memory_ != other_as_memory_snapshot_concrete->process_memory_) { + LOG(ERROR) << "different process_memory_ for snapshots"; + return nullptr; + } + CheckedRange merged(0, 0); + if (!LoggingDetermineMergedRange(this, other, &merged)) + return nullptr; + + auto result = std::make_unique(); + result->Initialize(process_memory_, merged.base(), merged.size()); + return result.release(); + } + + private: + template + friend const MemorySnapshot* MergeWithOtherSnapshotImpl( + const T* self, + const MemorySnapshot* other); + + const ProcessMemory* process_memory_; // weak + VMAddress address_; + size_t size_; + InitializationStateDcheck initialized_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_GENERIC_MEMORY_SNAPSHOT_GENERIC_H_ diff --git a/shared/sentry/external/crashpad/snapshot/memory_snapshot_test.cc b/shared/sentry/external/crashpad/snapshot/memory_snapshot_test.cc new file mode 100644 index 000000000..14ba78ee9 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/memory_snapshot_test.cc @@ -0,0 +1,151 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/memory_snapshot.h" + +#include "gtest/gtest.h" +#include "snapshot/test/test_memory_snapshot.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(DetermineMergedRange, NonOverlapping) { + TestMemorySnapshot a; + TestMemorySnapshot b; + a.SetAddress(0); + a.SetSize(100); + b.SetAddress(200); + b.SetSize(50); + CheckedRange range(0, 0); + EXPECT_FALSE(DetermineMergedRange(&a, &b, &range)); + EXPECT_FALSE(DetermineMergedRange(&b, &a, &range)); + + a.SetSize(199); + EXPECT_FALSE(DetermineMergedRange(&a, &b, &range)); +} + +TEST(DetermineMergedRange, Empty) { + TestMemorySnapshot a; + TestMemorySnapshot b; + a.SetAddress(100); + a.SetSize(0); + b.SetAddress(200); + b.SetSize(20); + + CheckedRange range(0, 0); + // Empty are invalid. + EXPECT_FALSE(DetermineMergedRange(&a, &b, &range)); + EXPECT_FALSE(DetermineMergedRange(&b, &a, &range)); + EXPECT_FALSE(DetermineMergedRange(&a, &a, &range)); +} + +TEST(DetermineMergedRange, Abutting) { + TestMemorySnapshot a; + TestMemorySnapshot b; + + a.SetAddress(0); + a.SetSize(10); + b.SetAddress(10); + b.SetSize(20); + + CheckedRange range(0, 0); + EXPECT_TRUE(DetermineMergedRange(&a, &b, &range)); + EXPECT_EQ(0u, range.base()); + EXPECT_EQ(30u, range.size()); + + EXPECT_TRUE(DetermineMergedRange(&b, &a, &range)); + EXPECT_EQ(0u, range.base()); + EXPECT_EQ(30u, range.size()); +} + +TEST(DetermineMergedRange, TypicalOverlapping) { + TestMemorySnapshot a; + TestMemorySnapshot b; + + a.SetAddress(10); + a.SetSize(100); + b.SetAddress(50); + b.SetSize(100); + + CheckedRange range(0, 0); + EXPECT_TRUE(DetermineMergedRange(&a, &b, &range)); + EXPECT_EQ(10u, range.base()); + EXPECT_EQ(140u, range.size()); + + EXPECT_TRUE(DetermineMergedRange(&b, &a, &range)); + EXPECT_EQ(10u, range.base()); + EXPECT_EQ(140u, range.size()); +} + +TEST(DetermineMergedRange, OneFullyInsideAnother) { + TestMemorySnapshot a; + TestMemorySnapshot b; + + a.SetAddress(20); + a.SetSize(100); + b.SetAddress(5); + b.SetSize(200); + + CheckedRange range(0, 0); + EXPECT_TRUE(DetermineMergedRange(&a, &b, &range)); + EXPECT_EQ(5u, range.base()); + EXPECT_EQ(200u, range.size()); + + EXPECT_TRUE(DetermineMergedRange(&b, &a, &range)); + EXPECT_EQ(5u, range.base()); + EXPECT_EQ(200u, range.size()); +} + +TEST(DetermineMergedRange, SameStart) { + TestMemorySnapshot a; + TestMemorySnapshot b; + + a.SetAddress(5); + a.SetSize(100); + b.SetAddress(5); + b.SetSize(50); + + CheckedRange range(0, 0); + EXPECT_TRUE(DetermineMergedRange(&a, &b, &range)); + EXPECT_EQ(5u, range.base()); + EXPECT_EQ(100u, range.size()); + + EXPECT_TRUE(DetermineMergedRange(&b, &a, &range)); + EXPECT_EQ(5u, range.base()); + EXPECT_EQ(100u, range.size()); +} + +TEST(DetermineMergedRange, SameEnd) { + TestMemorySnapshot a; + TestMemorySnapshot b; + + a.SetAddress(5); + a.SetSize(100); + b.SetAddress(70); + b.SetSize(35); + + CheckedRange range(0, 0); + EXPECT_TRUE(DetermineMergedRange(&a, &b, &range)); + EXPECT_EQ(5u, range.base()); + EXPECT_EQ(100u, range.size()); + + EXPECT_TRUE(DetermineMergedRange(&b, &a, &range)); + EXPECT_EQ(5u, range.base()); + EXPECT_EQ(100u, range.size()); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/minidump/exception_snapshot_minidump.cc b/shared/sentry/external/crashpad/snapshot/minidump/exception_snapshot_minidump.cc new file mode 100644 index 000000000..afd328287 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/minidump/exception_snapshot_minidump.cc @@ -0,0 +1,111 @@ +// Copyright 2019 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/minidump/exception_snapshot_minidump.h" + +#include "snapshot/minidump/minidump_string_reader.h" + +namespace crashpad { +namespace internal { + +ExceptionSnapshotMinidump::ExceptionSnapshotMinidump() + : ExceptionSnapshot(), + minidump_exception_stream_(), + context_(), + exception_information_(), + initialized_() {} + +ExceptionSnapshotMinidump::~ExceptionSnapshotMinidump() {} + +bool ExceptionSnapshotMinidump::Initialize(FileReaderInterface* file_reader, + CPUArchitecture arch, + RVA minidump_exception_stream_rva) { + DCHECK(initialized_.is_uninitialized()); + initialized_.set_invalid(); + + std::vector minidump_context; + + if (!file_reader->SeekSet(minidump_exception_stream_rva)) { + return false; + } + + if (!file_reader->ReadExactly(&minidump_exception_stream_, + sizeof(minidump_exception_stream_))) { + return false; + } + + const size_t num_parameters = + minidump_exception_stream_.ExceptionRecord.NumberParameters; + for (size_t i = 0; i < num_parameters; ++i) { + exception_information_.push_back( + minidump_exception_stream_.ExceptionRecord.ExceptionInformation[i]); + } + + if (!file_reader->SeekSet(minidump_exception_stream_.ThreadContext.Rva)) { + return false; + } + + minidump_context.resize(minidump_exception_stream_.ThreadContext.DataSize); + + if (!file_reader->ReadExactly(minidump_context.data(), + minidump_context.size())) { + return false; + } + + if (!context_.Initialize(arch, minidump_context)) { + return false; + } + + initialized_.set_valid(); + return true; +} + +const CPUContext* ExceptionSnapshotMinidump::Context() const { + DCHECK(initialized_.is_valid()); + return context_.Get(); +} + +uint64_t ExceptionSnapshotMinidump::ThreadID() const { + DCHECK(initialized_.is_valid()); + return minidump_exception_stream_.ThreadId; +} + +uint32_t ExceptionSnapshotMinidump::Exception() const { + DCHECK(initialized_.is_valid()); + return minidump_exception_stream_.ExceptionRecord.ExceptionCode; +} + +uint32_t ExceptionSnapshotMinidump::ExceptionInfo() const { + DCHECK(initialized_.is_valid()); + return minidump_exception_stream_.ExceptionRecord.ExceptionFlags; +} + +uint64_t ExceptionSnapshotMinidump::ExceptionAddress() const { + DCHECK(initialized_.is_valid()); + return minidump_exception_stream_.ExceptionRecord.ExceptionAddress; +} + +const std::vector& ExceptionSnapshotMinidump::Codes() const { + DCHECK(initialized_.is_valid()); + return exception_information_; +} + +std::vector ExceptionSnapshotMinidump::ExtraMemory() + const { + DCHECK(initialized_.is_valid()); + return std::vector(); +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/minidump/exception_snapshot_minidump.h b/shared/sentry/external/crashpad/snapshot/minidump/exception_snapshot_minidump.h new file mode 100644 index 000000000..917c237f1 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/minidump/exception_snapshot_minidump.h @@ -0,0 +1,79 @@ +// Copyright 2019 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_EXCEPTION_SNAPSHOT_MINIDUMP_H_ +#define CRASHPAD_SNAPSHOT_MINIDUMP_EXCEPTION_SNAPSHOT_MINIDUMP_H_ + +#include +#include + +#include "build/build_config.h" +#include "snapshot/cpu_context.h" +#include "snapshot/exception_snapshot.h" +#include "snapshot/minidump/minidump_context_converter.h" +#include "util/file/file_reader.h" +#include "util/misc/initialization_state.h" + +namespace crashpad { +namespace internal { + +//! \brief An ExceptionSnapshot based on a minidump file. +class ExceptionSnapshotMinidump final : public ExceptionSnapshot { + public: + ExceptionSnapshotMinidump(); + + ExceptionSnapshotMinidump(const ExceptionSnapshotMinidump&) = delete; + ExceptionSnapshotMinidump& operator=(const ExceptionSnapshotMinidump&) = + delete; + + ~ExceptionSnapshotMinidump() override; + + //! \brief Initializes the object. + //! + //! \param[in] file_reader A file reader corresponding to a minidump file. + //! The file reader must support seeking. + //! \param[in] arch The CPU architecture of this snapshot. + //! \param[in] minidump_exception_stream_rva The file offset in \a file_reader + //! at which the MINIDUMP_EXCEPTION_STREAM structure is located. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(FileReaderInterface* file_reader, + CPUArchitecture arch, + RVA minidump_exception_stream_rva); + + // ExceptionSnapshot: + const CPUContext* Context() const override; + uint64_t ThreadID() const override; + uint32_t Exception() const override; + uint32_t ExceptionInfo() const override; + uint64_t ExceptionAddress() const override; + const std::vector& Codes() const override; + std::vector ExtraMemory() const override; + + // Allow callers to explicitly check whether this exception snapshot has been + // initialized. + bool IsValid() const { return initialized_.is_valid(); } + + private: + MINIDUMP_EXCEPTION_STREAM minidump_exception_stream_; + MinidumpContextConverter context_; + std::vector exception_information_; + InitializationState initialized_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MINIDUMP_EXCEPTION_SNAPSHOT_MINIDUMP_H_ diff --git a/shared/sentry/external/crashpad/snapshot/minidump/memory_snapshot_minidump.cc b/shared/sentry/external/crashpad/snapshot/minidump/memory_snapshot_minidump.cc new file mode 100644 index 000000000..2c6c899a4 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/minidump/memory_snapshot_minidump.cc @@ -0,0 +1,111 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/minidump/memory_snapshot_minidump.h" + +#include + +#include "base/numerics/safe_math.h" + +namespace crashpad { +namespace internal { + +MemorySnapshotMinidump::MemorySnapshotMinidump() + : MemorySnapshot(), + address_(0), + data_(), + initialized_() {} + +MemorySnapshotMinidump::~MemorySnapshotMinidump() {} + +bool MemorySnapshotMinidump::Initialize(FileReaderInterface* file_reader, + RVA location) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + MINIDUMP_MEMORY_DESCRIPTOR descriptor; + + if (!file_reader->SeekSet(location)) { + return false; + } + + if (!file_reader->ReadExactly(&descriptor, sizeof(descriptor))) { + return false; + } + + address_ = descriptor.StartOfMemoryRange; + data_.resize(descriptor.Memory.DataSize); + + if (!file_reader->SeekSet(descriptor.Memory.Rva)) { + return false; + } + + if (!file_reader->ReadExactly(data_.data(), data_.size())) { + return false; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +uint64_t MemorySnapshotMinidump::Address() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return address_; +} + +size_t MemorySnapshotMinidump::Size() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return data_.size(); +} + +bool MemorySnapshotMinidump::Read(Delegate* delegate) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return delegate->MemorySnapshotDelegateRead( + const_cast(data_.data()), data_.size()); +} + +const MemorySnapshot* MemorySnapshotMinidump::MergeWithOtherSnapshot( + const MemorySnapshot* other) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + // TODO: Verify type of other + auto other_cast = reinterpret_cast(other); + + INITIALIZATION_STATE_DCHECK_VALID(other_cast->initialized_); + + if (other_cast->address_ < address_) { + return other_cast->MergeWithOtherSnapshot(this); + } + + CheckedRange merged(0, 0); + if (!LoggingDetermineMergedRange(this, other, &merged)) { + return nullptr; + } + + auto result = std::make_unique(); + result->address_ = merged.base(); + result->data_ = data_; + + if (result->data_.size() == merged.size()) { + return result.release(); + } + + result->data_.resize( + base::checked_cast(other_cast->address_ - address_)); + result->data_.insert(result->data_.end(), other_cast->data_.begin(), + other_cast->data_.end()); + return result.release(); +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/minidump/memory_snapshot_minidump.h b/shared/sentry/external/crashpad/snapshot/minidump/memory_snapshot_minidump.h new file mode 100644 index 000000000..0ab05b2ca --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/minidump/memory_snapshot_minidump.h @@ -0,0 +1,64 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_MEMORY_SNAPSHOT_MINIDUMP_H_ +#define CRASHPAD_SNAPSHOT_MINIDUMP_MEMORY_SNAPSHOT_MINIDUMP_H_ + +#include +#include + +#include + +#include "snapshot/memory_snapshot.h" +#include "util/file/file_reader.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { +namespace internal { +class MemorySnapshotMinidump : public MemorySnapshot { + public: + MemorySnapshotMinidump(); + + MemorySnapshotMinidump(const MemorySnapshotMinidump&) = delete; + MemorySnapshotMinidump& operator=(const MemorySnapshotMinidump&) = delete; + + ~MemorySnapshotMinidump() override; + + //! \brief Initializes the object. + //! + //! \param[in] file_reader A file reader corresponding to a minidump file. + //! The file reader must support seeking. + //! \param[in] location The location within the file where we will find a + //! MINIDUMP_MEMORY_DESCRIPTOR from which to initialize this object. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(FileReaderInterface* file_reader, RVA location); + + uint64_t Address() const override; + size_t Size() const override; + bool Read(Delegate* delegate) const override; + const MemorySnapshot* MergeWithOtherSnapshot( + const MemorySnapshot* other) const override; + + private: + uint64_t address_; + std::vector data_; + InitializationStateDcheck initialized_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MINIDUMP_MEMORY_SNAPSHOT_MINIDUMP_H_ diff --git a/shared/sentry/external/crashpad/snapshot/minidump/minidump_annotation_reader.cc b/shared/sentry/external/crashpad/snapshot/minidump/minidump_annotation_reader.cc new file mode 100644 index 000000000..b1e4f9852 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/minidump/minidump_annotation_reader.cc @@ -0,0 +1,119 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/minidump/minidump_annotation_reader.h" + +#include + +#include "base/logging.h" +#include "minidump/minidump_extensions.h" +#include "snapshot/minidump/minidump_string_reader.h" + +namespace crashpad { +namespace internal { + +namespace { + +bool ReadMinidumpByteArray(FileReaderInterface* file_reader, + RVA rva, + std::vector* data) { + if (rva == 0) { + data->clear(); + return true; + } + + if (!file_reader->SeekSet(rva)) { + return false; + } + + uint32_t length; + if (!file_reader->ReadExactly(&length, sizeof(length))) { + return false; + } + + std::vector local_data(length); + if (!file_reader->ReadExactly(local_data.data(), length)) { + return false; + } + + data->swap(local_data); + return true; +} + +} // namespace + +bool ReadMinidumpAnnotationList(FileReaderInterface* file_reader, + const MINIDUMP_LOCATION_DESCRIPTOR& location, + std::vector* list) { + if (location.Rva == 0) { + list->clear(); + return true; + } + + if (location.DataSize < sizeof(MinidumpAnnotationList)) { + LOG(ERROR) << "annotation list size mismatch"; + return false; + } + + if (!file_reader->SeekSet(location.Rva)) { + return false; + } + + uint32_t count; + if (!file_reader->ReadExactly(&count, sizeof(count))) { + return false; + } + + if (location.DataSize != + sizeof(MinidumpAnnotationList) + count * sizeof(MinidumpAnnotation)) { + LOG(ERROR) << "annotation object size mismatch"; + return false; + } + + std::vector minidump_annotations(count); + if (!file_reader->ReadExactly(minidump_annotations.data(), + count * sizeof(MinidumpAnnotation))) { + return false; + } + + std::vector annotations; + annotations.reserve(count); + + for (size_t i = 0; i < count; ++i) { + const MinidumpAnnotation* minidump_annotation = &minidump_annotations[i]; + + AnnotationSnapshot annotation; + // The client-exposed size of this field is 16-bit, but the minidump field + // is 32-bit for padding. Take just the lower part. + annotation.type = static_cast(minidump_annotation->type); + + if (!ReadMinidumpUTF8String( + file_reader, minidump_annotation->name, &annotation.name)) { + return false; + } + + if (!ReadMinidumpByteArray( + file_reader, minidump_annotation->value, &annotation.value)) { + return false; + } + + annotations.push_back(std::move(annotation)); + } + + list->swap(annotations); + return true; +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/minidump/minidump_annotation_reader.h b/shared/sentry/external/crashpad/snapshot/minidump/minidump_annotation_reader.h new file mode 100644 index 000000000..a5bafb5bf --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/minidump/minidump_annotation_reader.h @@ -0,0 +1,41 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef SNAPSHOT_MINIDUMP_MINIDUMP_ANNOTATION_READER_H_ +#define SNAPSHOT_MINIDUMP_MINIDUMP_ANNOTATION_READER_H_ + +#include +#include + +#include + +#include "snapshot/annotation_snapshot.h" +#include "util/file/file_reader.h" + +namespace crashpad { +namespace internal { + +//! \brief Reads a MinidumpAnnotationList from a minidump file at \a location +//! in \a file_reader, and returns it in \a list. +//! +//! \return `true` on success, with \a list set by replacing its contents. +//! `false` on failure, with a message logged. +bool ReadMinidumpAnnotationList(FileReaderInterface* file_reader, + const MINIDUMP_LOCATION_DESCRIPTOR& location, + std::vector* list); + +} // namespace internal +} // namespace crashpad + +#endif // SNAPSHOT_MINIDUMP_MINIDUMP_ANNOTATION_READER_H_ diff --git a/shared/sentry/external/crashpad/snapshot/minidump/minidump_context_converter.cc b/shared/sentry/external/crashpad/snapshot/minidump/minidump_context_converter.cc new file mode 100644 index 000000000..5e94ca2cb --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/minidump/minidump_context_converter.cc @@ -0,0 +1,279 @@ +// Copyright 2019 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/minidump/minidump_context_converter.h" + +#include + +#include + +#include "base/logging.h" +#include "minidump/minidump_context.h" + +namespace crashpad { +namespace internal { + +MinidumpContextConverter::MinidumpContextConverter() : initialized_() { + context_.architecture = CPUArchitecture::kCPUArchitectureUnknown; +} + +bool MinidumpContextConverter::Initialize( + CPUArchitecture arch, + const std::vector& minidump_context) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + if (minidump_context.size() == 0) { + // Thread has no context. + context_.architecture = CPUArchitecture::kCPUArchitectureUnknown; + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; + } + + context_.architecture = arch; + + if (context_.architecture == CPUArchitecture::kCPUArchitectureX86) { + context_memory_.resize(sizeof(CPUContextX86)); + context_.x86 = reinterpret_cast(context_memory_.data()); + const MinidumpContextX86* src = + reinterpret_cast(minidump_context.data()); + if (minidump_context.size() < sizeof(MinidumpContextX86)) { + return false; + } + + if (!(src->context_flags & kMinidumpContextX86)) { + return false; + } + + if (src->context_flags & kMinidumpContextX86Extended) { + context_.x86->fxsave = src->fxsave; + } else if (src->context_flags & kMinidumpContextX86FloatingPoint) { + CPUContextX86::FsaveToFxsave(src->fsave, &context_.x86->fxsave); + } + + context_.x86->eax = src->eax; + context_.x86->ebx = src->ebx; + context_.x86->ecx = src->ecx; + context_.x86->edx = src->edx; + context_.x86->edi = src->edi; + context_.x86->esi = src->esi; + context_.x86->ebp = src->ebp; + context_.x86->esp = src->esp; + context_.x86->eip = src->eip; + context_.x86->eflags = src->eflags; + context_.x86->cs = static_cast(src->cs); + context_.x86->ds = static_cast(src->ds); + context_.x86->es = static_cast(src->es); + context_.x86->fs = static_cast(src->fs); + context_.x86->gs = static_cast(src->gs); + context_.x86->ss = static_cast(src->ss); + context_.x86->dr0 = src->dr0; + context_.x86->dr1 = src->dr1; + context_.x86->dr2 = src->dr2; + context_.x86->dr3 = src->dr3; + context_.x86->dr6 = src->dr6; + context_.x86->dr7 = src->dr7; + + // Minidump passes no value for dr4/5. Our output context has space for + // them. According to spec they're obsolete, but when present read as + // aliases for dr6/7, so we'll do this. + context_.x86->dr4 = src->dr6; + context_.x86->dr5 = src->dr7; + } else if (context_.architecture == CPUArchitecture::kCPUArchitectureX86_64) { + context_memory_.resize(sizeof(CPUContextX86_64)); + context_.x86_64 = + reinterpret_cast(context_memory_.data()); + const MinidumpContextAMD64* src = + reinterpret_cast(minidump_context.data()); + if (minidump_context.size() < sizeof(MinidumpContextAMD64)) { + return false; + } + + if (!(src->context_flags & kMinidumpContextAMD64)) { + return false; + } + + context_.x86_64->fxsave = src->fxsave; + context_.x86_64->cs = src->cs; + context_.x86_64->fs = src->fs; + context_.x86_64->gs = src->gs; + context_.x86_64->rflags = src->eflags; + context_.x86_64->dr0 = src->dr0; + context_.x86_64->dr1 = src->dr1; + context_.x86_64->dr2 = src->dr2; + context_.x86_64->dr3 = src->dr3; + context_.x86_64->dr6 = src->dr6; + context_.x86_64->dr7 = src->dr7; + context_.x86_64->rax = src->rax; + context_.x86_64->rcx = src->rcx; + context_.x86_64->rdx = src->rdx; + context_.x86_64->rbx = src->rbx; + context_.x86_64->rsp = src->rsp; + context_.x86_64->rbp = src->rbp; + context_.x86_64->rsi = src->rsi; + context_.x86_64->rdi = src->rdi; + context_.x86_64->r8 = src->r8; + context_.x86_64->r9 = src->r9; + context_.x86_64->r10 = src->r10; + context_.x86_64->r11 = src->r11; + context_.x86_64->r12 = src->r12; + context_.x86_64->r13 = src->r13; + context_.x86_64->r14 = src->r14; + context_.x86_64->r15 = src->r15; + context_.x86_64->rip = src->rip; + + // See comments on x86 above. + context_.x86_64->dr4 = src->dr6; + context_.x86_64->dr5 = src->dr7; + } else if (context_.architecture == CPUArchitecture::kCPUArchitectureARM) { + context_memory_.resize(sizeof(CPUContextARM)); + context_.arm = reinterpret_cast(context_memory_.data()); + const MinidumpContextARM* src = + reinterpret_cast(minidump_context.data()); + if (minidump_context.size() < sizeof(MinidumpContextARM)) { + return false; + } + + if (!(src->context_flags & kMinidumpContextARM)) { + return false; + } + + for (size_t i = 0; i < std::size(src->regs); i++) { + context_.arm->regs[i] = src->regs[i]; + } + + context_.arm->fp = src->fp; + context_.arm->ip = src->ip; + context_.arm->sp = src->sp; + context_.arm->lr = src->lr; + context_.arm->pc = src->pc; + context_.arm->cpsr = src->cpsr; + context_.arm->vfp_regs.fpscr = src->fpscr; + + for (size_t i = 0; i < std::size(src->vfp); i++) { + context_.arm->vfp_regs.vfp[i] = src->vfp[i]; + } + + context_.arm->have_fpa_regs = false; + context_.arm->have_vfp_regs = + !!(src->context_flags & kMinidumpContextARMVFP); + } else if (context_.architecture == CPUArchitecture::kCPUArchitectureARM64) { + context_memory_.resize(sizeof(CPUContextARM64)); + context_.arm64 = reinterpret_cast(context_memory_.data()); + const MinidumpContextARM64* src = + reinterpret_cast(minidump_context.data()); + if (minidump_context.size() < sizeof(MinidumpContextARM64)) { + return false; + } + + if (!(src->context_flags & kMinidumpContextARM64)) { + return false; + } + + for (size_t i = 0; i < std::size(src->regs); i++) { + context_.arm64->regs[i] = src->regs[i]; + } + + context_.arm64->regs[29] = src->fp; + context_.arm64->regs[30] = src->lr; + + for (size_t i = 0; i < std::size(src->fpsimd); i++) { + context_.arm64->fpsimd[i] = src->fpsimd[i]; + } + + context_.arm64->sp = src->sp; + context_.arm64->pc = src->pc; + context_.arm64->fpcr = src->fpcr; + context_.arm64->fpsr = src->fpsr; + context_.arm64->spsr = src->cpsr; + } else if (context_.architecture == CPUArchitecture::kCPUArchitectureMIPSEL) { + context_memory_.resize(sizeof(CPUContextMIPS)); + context_.mipsel = reinterpret_cast(context_memory_.data()); + const MinidumpContextMIPS* src = + reinterpret_cast(minidump_context.data()); + if (minidump_context.size() < sizeof(MinidumpContextMIPS)) { + return false; + } + + if (!(src->context_flags & kMinidumpContextMIPS)) { + return false; + } + + for (size_t i = 0; i < std::size(src->regs); i++) { + context_.mipsel->regs[i] = src->regs[i]; + } + + context_.mipsel->mdhi = static_cast(src->mdhi); + context_.mipsel->mdlo = static_cast(src->mdlo); + context_.mipsel->dsp_control = src->dsp_control; + + for (size_t i = 0; i < std::size(src->hi); i++) { + context_.mipsel->hi[i] = src->hi[i]; + context_.mipsel->lo[i] = src->lo[i]; + } + + context_.mipsel->cp0_epc = static_cast(src->epc); + context_.mipsel->cp0_badvaddr = static_cast(src->badvaddr); + context_.mipsel->cp0_status = src->status; + context_.mipsel->cp0_cause = src->cause; + context_.mipsel->fpcsr = src->fpcsr; + context_.mipsel->fir = src->fir; + + memcpy(&context_.mipsel->fpregs, &src->fpregs, sizeof(src->fpregs)); + } else if (context_.architecture == + CPUArchitecture::kCPUArchitectureMIPS64EL) { + context_memory_.resize(sizeof(CPUContextMIPS64)); + context_.mips64 = + reinterpret_cast(context_memory_.data()); + const MinidumpContextMIPS64* src = + reinterpret_cast(minidump_context.data()); + if (minidump_context.size() < sizeof(MinidumpContextMIPS64)) { + return false; + } + + if (!(src->context_flags & kMinidumpContextMIPS64)) { + return false; + } + + for (size_t i = 0; i < std::size(src->regs); i++) { + context_.mips64->regs[i] = src->regs[i]; + } + + context_.mips64->mdhi = src->mdhi; + context_.mips64->mdlo = src->mdlo; + context_.mips64->dsp_control = src->dsp_control; + + for (size_t i = 0; i < std::size(src->hi); i++) { + context_.mips64->hi[i] = src->hi[i]; + context_.mips64->lo[i] = src->lo[i]; + } + + context_.mips64->cp0_epc = src->epc; + context_.mips64->cp0_badvaddr = src->badvaddr; + context_.mips64->cp0_status = src->status; + context_.mips64->cp0_cause = src->cause; + context_.mips64->fpcsr = src->fpcsr; + context_.mips64->fir = src->fir; + + memcpy(&context_.mips64->fpregs, &src->fpregs, sizeof(src->fpregs)); + } else { + // Architecture is listed as "unknown". + DLOG(ERROR) << "Unknown architecture"; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/minidump/minidump_context_converter.h b/shared/sentry/external/crashpad/snapshot/minidump/minidump_context_converter.h new file mode 100644 index 000000000..48e36c1f0 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/minidump/minidump_context_converter.h @@ -0,0 +1,47 @@ +// Copyright 2019 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_CONTEXT_CONVERTER_H_ +#define CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_CONTEXT_CONVERTER_H_ + +#include + +#include "snapshot/cpu_context.h" +#include "util/misc/initialization_state.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { +namespace internal { + +class MinidumpContextConverter { + public: + MinidumpContextConverter(); + + bool Initialize(CPUArchitecture arch, + const std::vector& minidump_context); + const CPUContext* Get() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &context_; + } + + private: + CPUContext context_; + std::vector context_memory_; + InitializationStateDcheck initialized_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_CONTEXT_CONVERTER_H_ diff --git a/shared/sentry/external/crashpad/snapshot/minidump/minidump_simple_string_dictionary_reader.cc b/shared/sentry/external/crashpad/snapshot/minidump/minidump_simple_string_dictionary_reader.cc new file mode 100644 index 000000000..7e263f9e8 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/minidump/minidump_simple_string_dictionary_reader.cc @@ -0,0 +1,88 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/minidump/minidump_simple_string_dictionary_reader.h" + +#include + +#include +#include + +#include "base/logging.h" +#include "minidump/minidump_extensions.h" +#include "snapshot/minidump/minidump_string_reader.h" + +namespace crashpad { +namespace internal { + +bool ReadMinidumpSimpleStringDictionary( + FileReaderInterface* file_reader, + const MINIDUMP_LOCATION_DESCRIPTOR& location, + std::map* dictionary) { + if (location.Rva == 0) { + dictionary->clear(); + return true; + } + + if (location.DataSize < sizeof(MinidumpSimpleStringDictionary)) { + LOG(ERROR) << "simple_string_dictionary size mismatch"; + return false; + } + + if (!file_reader->SeekSet(location.Rva)) { + return false; + } + + uint32_t entry_count; + if (!file_reader->ReadExactly(&entry_count, sizeof(entry_count))) { + return false; + } + + if (location.DataSize != + sizeof(MinidumpSimpleStringDictionary) + + entry_count * sizeof(MinidumpSimpleStringDictionaryEntry)) { + LOG(ERROR) << "simple_string_dictionary size mismatch"; + return false; + } + + std::vector entries(entry_count); + if (!file_reader->ReadExactly(&entries[0], + entry_count * sizeof(entries[0]))) { + return false; + } + + std::map local_dictionary; + for (const MinidumpSimpleStringDictionaryEntry& entry : entries) { + std::string key; + if (!ReadMinidumpUTF8String(file_reader, entry.key, &key)) { + return false; + } + + std::string value; + if (!ReadMinidumpUTF8String(file_reader, entry.value, &value)) { + return false; + } + + if (!local_dictionary.insert(std::make_pair(key, value)).second) { + LOG(ERROR) << "duplicate key " << key; + return false; + } + } + + dictionary->swap(local_dictionary); + return true; +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/minidump/minidump_simple_string_dictionary_reader.h b/shared/sentry/external/crashpad/snapshot/minidump/minidump_simple_string_dictionary_reader.h new file mode 100644 index 000000000..633ea0f13 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/minidump/minidump_simple_string_dictionary_reader.h @@ -0,0 +1,42 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_SIMPLE_STRING_DICTIONARY_READER_H_ +#define CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_SIMPLE_STRING_DICTIONARY_READER_H_ + +#include +#include + +#include +#include + +#include "util/file/file_reader.h" + +namespace crashpad { +namespace internal { + +//! \brief Reads a MinidumpSimpleStringDictionary from a minidump file \a +//! location in \a file_reader, and returns it in \a dictionary. +//! +//! \return `true` on success, with \a dictionary set by replacing its contents. +//! `false` on failure, with a message logged. +bool ReadMinidumpSimpleStringDictionary( + FileReaderInterface* file_reader, + const MINIDUMP_LOCATION_DESCRIPTOR& location, + std::map* dictionary); + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_SIMPLE_STRING_DICTIONARY_READER_H_ diff --git a/shared/sentry/external/crashpad/snapshot/minidump/minidump_stream.h b/shared/sentry/external/crashpad/snapshot/minidump/minidump_stream.h new file mode 100644 index 000000000..20f1dda4e --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/minidump/minidump_stream.h @@ -0,0 +1,44 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STREAM_H_ +#define CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STREAM_H_ + +#include + +#include + + +namespace crashpad { + +//! \brief Stores a minidump stream along with its stream ID. +class MinidumpStream { + public: + MinidumpStream(uint32_t stream_type, std::vector data) + : stream_type_(stream_type), data_(data) {} + + MinidumpStream(const MinidumpStream&) = delete; + MinidumpStream& operator=(const MinidumpStream&) = delete; + + uint32_t stream_type() const { return stream_type_; } + const std::vector& data() const { return data_; } + + private: + uint32_t stream_type_; + std::vector data_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STREAM_H_ diff --git a/shared/sentry/external/crashpad/snapshot/minidump/minidump_string_list_reader.cc b/shared/sentry/external/crashpad/snapshot/minidump/minidump_string_list_reader.cc new file mode 100644 index 000000000..3b9bd6b5a --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/minidump/minidump_string_list_reader.cc @@ -0,0 +1,74 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/minidump/minidump_string_list_reader.h" + +#include + +#include "base/logging.h" +#include "minidump/minidump_extensions.h" +#include "snapshot/minidump/minidump_string_reader.h" + +namespace crashpad { +namespace internal { + +bool ReadMinidumpStringList(FileReaderInterface* file_reader, + const MINIDUMP_LOCATION_DESCRIPTOR& location, + std::vector* list) { + if (location.Rva == 0) { + list->clear(); + return true; + } + + if (location.DataSize < sizeof(MinidumpRVAList)) { + LOG(ERROR) << "string_list size mismatch"; + return false; + } + + if (!file_reader->SeekSet(location.Rva)) { + return false; + } + + uint32_t entry_count; + if (!file_reader->ReadExactly(&entry_count, sizeof(entry_count))) { + return false; + } + + if (location.DataSize != + sizeof(MinidumpRVAList) + entry_count * sizeof(RVA)) { + LOG(ERROR) << "string_list size mismatch"; + return false; + } + + std::vector rvas(entry_count); + if (!file_reader->ReadExactly(&rvas[0], entry_count * sizeof(rvas[0]))) { + return false; + } + + std::vector local_list; + for (RVA rva : rvas) { + std::string element; + if (!ReadMinidumpUTF8String(file_reader, rva, &element)) { + return false; + } + + local_list.push_back(element); + } + + list->swap(local_list); + return true; +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/minidump/minidump_string_list_reader.h b/shared/sentry/external/crashpad/snapshot/minidump/minidump_string_list_reader.h new file mode 100644 index 000000000..2521605a1 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/minidump/minidump_string_list_reader.h @@ -0,0 +1,42 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STRING_LIST_READER_H_ +#define CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STRING_LIST_READER_H_ + +#include +#include + +#include +#include + +#include "util/file/file_reader.h" + +namespace crashpad { +namespace internal { + +//! \brief Reads a list of MinidumpUTF8String objects in a MinidumpRVAList from +//! a minidump file \a location in \a file_reader, and returns it in \a +//! list. +//! +//! \return `true` on success, with \a list set by replacing its contents. +//! `false` on failure, with a message logged. +bool ReadMinidumpStringList(FileReaderInterface* file_reader, + const MINIDUMP_LOCATION_DESCRIPTOR& location, + std::vector* list); + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STRING_LIST_READER_H_ diff --git a/shared/sentry/external/crashpad/snapshot/minidump/minidump_string_reader.cc b/shared/sentry/external/crashpad/snapshot/minidump/minidump_string_reader.cc new file mode 100644 index 000000000..0e9527c20 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/minidump/minidump_string_reader.cc @@ -0,0 +1,83 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/minidump/minidump_string_reader.h" + +#include + +#include "base/strings/utf_string_conversions.h" +#include "minidump/minidump_extensions.h" + +namespace crashpad { +namespace internal { + +namespace { + +template +bool ReadMinidumpString(FileReaderInterface* file_reader, + RVA rva, + StringType* string) { + if (rva == 0) { + string->clear(); + return true; + } + + if (!file_reader->SeekSet(rva)) { + return false; + } + + uint32_t string_size; + if (!file_reader->ReadExactly(&string_size, sizeof(string_size))) { + return false; + } + + StringType local_string(string_size / sizeof((*string)[0]), '\0'); + if (!file_reader->ReadExactly(&local_string[0], string_size)) { + return false; + } + + string->swap(local_string); + return true; +} + +} // namespace + +bool ReadMinidumpUTF8String(FileReaderInterface* file_reader, + RVA rva, + std::string* string) { + return ReadMinidumpString(file_reader, rva, string); +} + +bool ReadMinidumpUTF16String(FileReaderInterface* file_reader, + RVA rva, + std::u16string* string) { + return ReadMinidumpString(file_reader, rva, string); +} + +bool ReadMinidumpUTF16String(FileReaderInterface* file_reader, + RVA rva, + std::string* string) { + std::u16string string_raw; + + if (!ReadMinidumpString(file_reader, rva, &string_raw)) { + return false; + } + + base::UTF16ToUTF8(string_raw.data(), string_raw.size(), string); + + return true; +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/minidump/minidump_string_reader.h b/shared/sentry/external/crashpad/snapshot/minidump/minidump_string_reader.h new file mode 100644 index 000000000..370fa8c19 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/minidump/minidump_string_reader.h @@ -0,0 +1,58 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STRING_READER_H_ +#define CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STRING_READER_H_ + +#include +#include + +#include + +#include "util/file/file_reader.h" + +namespace crashpad { +namespace internal { + +//! \brief Reads a MinidumpUTF8String from a minidump file at offset \a rva in +//! \a file_reader, and returns it in \a string. +//! +//! \return `true` on success, with \a string set. `false` on failure, with a +//! message logged. +bool ReadMinidumpUTF8String(FileReaderInterface* file_reader, + RVA rva, + std::string* string); + +//! \brief Reads a MinidumpUTF16String from a minidump file at offset \a rva in +//! \a file_reader, and returns it in \a string. +//! +//! \return `true` on success, with \a string set. `false` on failure, with a +//! message logged. +bool ReadMinidumpUTF16String(FileReaderInterface* file_reader, + RVA rva, + std::u16string* string); + +//! \brief Reads a MinidumpUTF16String from a minidump file at offset \a rva in +//! \a file_reader, and returns it in \a string. +//! +//! \return `true` on success, with \a string set. `false` on failure, with a +//! message logged. +bool ReadMinidumpUTF16String(FileReaderInterface* file_reader, + RVA rva, + std::string* string); + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MINIDUMP_MINIDUMP_STRING_READER_H_ diff --git a/shared/sentry/external/crashpad/snapshot/minidump/module_snapshot_minidump.cc b/shared/sentry/external/crashpad/snapshot/minidump/module_snapshot_minidump.cc new file mode 100644 index 000000000..7dad87a66 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/minidump/module_snapshot_minidump.cc @@ -0,0 +1,296 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/minidump/module_snapshot_minidump.h" + +#include +#include + +#include "base/logging.h" +#include "base/notreached.h" +#include "minidump/minidump_extensions.h" +#include "snapshot/minidump/minidump_annotation_reader.h" +#include "snapshot/minidump/minidump_simple_string_dictionary_reader.h" +#include "snapshot/minidump/minidump_string_list_reader.h" +#include "snapshot/minidump/minidump_string_reader.h" +#include "util/misc/pdb_structures.h" + +namespace crashpad { +namespace internal { + +ModuleSnapshotMinidump::ModuleSnapshotMinidump() + : ModuleSnapshot(), + minidump_module_(), + annotations_vector_(), + annotations_simple_map_(), + annotation_objects_(), + uuid_(), + build_id_(), + name_(), + debug_file_name_(), + age_(0), + initialized_() {} + +ModuleSnapshotMinidump::~ModuleSnapshotMinidump() {} + +bool ModuleSnapshotMinidump::Initialize( + FileReaderInterface* file_reader, + RVA minidump_module_rva, + const MINIDUMP_LOCATION_DESCRIPTOR* + minidump_module_crashpad_info_location) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + if (!file_reader->SeekSet(minidump_module_rva)) { + return false; + } + + if (!file_reader->ReadExactly(&minidump_module_, sizeof(minidump_module_))) { + return false; + } + + if (!InitializeModuleCrashpadInfo(file_reader, + minidump_module_crashpad_info_location)) { + return false; + } + + ReadMinidumpUTF16String(file_reader, minidump_module_.ModuleNameRva, &name_); + + if (minidump_module_.CvRecord.Rva != 0 && + !InitializeModuleCodeView(file_reader)) { + return false; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +bool ModuleSnapshotMinidump::InitializeModuleCodeView( + FileReaderInterface* file_reader) { + uint32_t signature; + + DCHECK_NE(minidump_module_.CvRecord.Rva, 0u); + + if (minidump_module_.CvRecord.DataSize < sizeof(signature)) { + LOG(ERROR) << "CodeView record in module too small to contain signature"; + return false; + } + + if (!file_reader->SeekSet(minidump_module_.CvRecord.Rva)) { + return false; + } + + std::vector cv_record; + cv_record.resize(minidump_module_.CvRecord.DataSize); + + if (!file_reader->ReadExactly(cv_record.data(), cv_record.size())) { + return false; + } + + signature = *reinterpret_cast(cv_record.data()); + + if (signature == CodeViewRecordPDB70::kSignature) { + if (cv_record.size() < offsetof(CodeViewRecordPDB70, pdb_name) + 1) { + LOG(ERROR) << "CodeView record in module marked as PDB70 but too small"; + return false; + } + + auto cv_record_pdb70 = + reinterpret_cast(cv_record.data()); + + age_ = cv_record_pdb70->age; + uuid_ = cv_record_pdb70->uuid; + + if (cv_record.back() != '\0') { + LOG(ERROR) << "CodeView record marked as PDB70 missing NUL-terminator in " + "pdb_name"; + return false; + } + + std::copy(cv_record.begin() + offsetof(CodeViewRecordPDB70, pdb_name), + cv_record.end() - 1, + std::back_inserter(debug_file_name_)); + return true; + } + + if (signature == CodeViewRecordBuildID::kSignature) { + std::copy(cv_record.begin() + offsetof(CodeViewRecordBuildID, build_id), + cv_record.end(), + std::back_inserter(build_id_)); + return true; + } + + LOG(ERROR) << "Bad CodeView signature in module"; + return false; +} + +std::string ModuleSnapshotMinidump::Name() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return name_; +} + +uint64_t ModuleSnapshotMinidump::Address() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return minidump_module_.BaseOfImage; +} + +uint64_t ModuleSnapshotMinidump::Size() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return minidump_module_.SizeOfImage; +} + +time_t ModuleSnapshotMinidump::Timestamp() const { + return minidump_module_.TimeDateStamp; +} + +void ModuleSnapshotMinidump::FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + uint32_t version_01 = minidump_module_.VersionInfo.dwFileVersionMS; + uint32_t version_23 = minidump_module_.VersionInfo.dwFileVersionLS; + *version_0 = static_cast(version_01 >> 16); + *version_1 = static_cast(version_01 & 0xFFFF); + *version_2 = static_cast(version_23 >> 16); + *version_3 = static_cast(version_23 & 0xFFFF); +} + +void ModuleSnapshotMinidump::SourceVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + uint32_t version_01 = minidump_module_.VersionInfo.dwProductVersionMS; + uint32_t version_23 = minidump_module_.VersionInfo.dwProductVersionLS; + *version_0 = static_cast(version_01 >> 16); + *version_1 = static_cast(version_01 & 0xFFFF); + *version_2 = static_cast(version_23 >> 16); + *version_3 = static_cast(version_23 & 0xFFFF); +} + +ModuleSnapshot::ModuleType ModuleSnapshotMinidump::GetModuleType() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + switch (minidump_module_.VersionInfo.dwFileType) { + case VFT_APP: + return kModuleTypeExecutable; + case VFT_DLL: + return kModuleTypeSharedLibrary; + } + return kModuleTypeUnknown; +} + +void ModuleSnapshotMinidump::UUIDAndAge(crashpad::UUID* uuid, + uint32_t* age) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *uuid = uuid_; + *age = age_; +} + +std::string ModuleSnapshotMinidump::DebugFileName() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return debug_file_name_; +} + +std::vector ModuleSnapshotMinidump::BuildID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return build_id_; +} + +std::vector ModuleSnapshotMinidump::AnnotationsVector() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return annotations_vector_; +} + +std::map +ModuleSnapshotMinidump::AnnotationsSimpleMap() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return annotations_simple_map_; +} + +std::vector ModuleSnapshotMinidump::AnnotationObjects() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return annotation_objects_; +} + +std::set> ModuleSnapshotMinidump::ExtraMemoryRanges() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return std::set>(); +} + +std::vector +ModuleSnapshotMinidump::CustomMinidumpStreams() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return std::vector(); +} + +bool ModuleSnapshotMinidump::InitializeModuleCrashpadInfo( + FileReaderInterface* file_reader, + const MINIDUMP_LOCATION_DESCRIPTOR* + minidump_module_crashpad_info_location) { + if (!minidump_module_crashpad_info_location || + minidump_module_crashpad_info_location->Rva == 0) { + return true; + } + + MinidumpModuleCrashpadInfo minidump_module_crashpad_info; + if (minidump_module_crashpad_info_location->DataSize < + sizeof(minidump_module_crashpad_info)) { + LOG(ERROR) << "minidump_module_crashpad_info size mismatch"; + return false; + } + + if (!file_reader->SeekSet(minidump_module_crashpad_info_location->Rva)) { + return false; + } + + if (!file_reader->ReadExactly(&minidump_module_crashpad_info, + sizeof(minidump_module_crashpad_info))) { + return false; + } + + if (minidump_module_crashpad_info.version != + MinidumpModuleCrashpadInfo::kVersion) { + LOG(ERROR) << "minidump_module_crashpad_info version mismatch"; + return false; + } + + if (!ReadMinidumpStringList(file_reader, + minidump_module_crashpad_info.list_annotations, + &annotations_vector_)) { + return false; + } + + if (!ReadMinidumpSimpleStringDictionary( + file_reader, + minidump_module_crashpad_info.simple_annotations, + &annotations_simple_map_)) { + return false; + } + + if (!ReadMinidumpAnnotationList( + file_reader, + minidump_module_crashpad_info.annotation_objects, + &annotation_objects_)) { + return false; + } + + return true; +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/minidump/module_snapshot_minidump.h b/shared/sentry/external/crashpad/snapshot/minidump/module_snapshot_minidump.h new file mode 100644 index 000000000..f00e566b8 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/minidump/module_snapshot_minidump.h @@ -0,0 +1,114 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_MODULE_SNAPSHOT_MINIDUMP_H_ +#define CRASHPAD_SNAPSHOT_MINIDUMP_MODULE_SNAPSHOT_MINIDUMP_H_ + +#include +#include +#include +#include + +#include +#include +#include + +#include "snapshot/annotation_snapshot.h" +#include "snapshot/module_snapshot.h" +#include "util/file/file_reader.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { +namespace internal { + +//! \brief A ModuleSnapshot based on a module in a minidump file. +class ModuleSnapshotMinidump final : public ModuleSnapshot { + public: + ModuleSnapshotMinidump(); + + ModuleSnapshotMinidump(const ModuleSnapshotMinidump&) = delete; + ModuleSnapshotMinidump& operator=(const ModuleSnapshotMinidump&) = delete; + + ~ModuleSnapshotMinidump() override; + + //! \brief Initializes the object. + //! + //! \param[in] file_reader A file reader corresponding to a minidump file. + //! The file reader must support seeking. + //! \param[in] minidump_module_rva The file offset in \a file_reader at which + //! the module’s MINIDUMP_MODULE structure is located. + //! \param[in] minidump_crashpad_module_info_location The location in \a + //! file_reader at which the module’s corresponding + //! MinidumpModuleCrashpadInfo structure is located. If no such + //! corresponding structure is available for a module, this may be + //! `nullptr`. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(FileReaderInterface* file_reader, + RVA minidump_module_rva, + const MINIDUMP_LOCATION_DESCRIPTOR* + minidump_crashpad_module_info_location); + + // ModuleSnapshot: + + std::string Name() const override; + uint64_t Address() const override; + uint64_t Size() const override; + time_t Timestamp() const override; + void FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const override; + void SourceVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const override; + ModuleType GetModuleType() const override; + void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override; + std::string DebugFileName() const override; + std::vector BuildID() const override; + std::vector AnnotationsVector() const override; + std::map AnnotationsSimpleMap() const override; + std::vector AnnotationObjects() const override; + std::set> ExtraMemoryRanges() const override; + std::vector CustomMinidumpStreams() const override; + + private: + // Initializes data carried in a MinidumpModuleCrashpadInfo structure on + // behalf of Initialize(). + bool InitializeModuleCrashpadInfo(FileReaderInterface* file_reader, + const MINIDUMP_LOCATION_DESCRIPTOR* + minidump_module_crashpad_info_location); + + // Initializes data from the CodeView record, which usually points toward + // debug symbols. + bool InitializeModuleCodeView(FileReaderInterface* file_reader); + + MINIDUMP_MODULE minidump_module_; + std::vector annotations_vector_; + std::map annotations_simple_map_; + std::vector annotation_objects_; + UUID uuid_; + std::vector build_id_; + std::string name_; + std::string debug_file_name_; + uint32_t age_; + InitializationStateDcheck initialized_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MINIDUMP_MODULE_SNAPSHOT_MINIDUMP_H_ diff --git a/shared/sentry/external/crashpad/snapshot/minidump/process_snapshot_minidump.cc b/shared/sentry/external/crashpad/snapshot/minidump/process_snapshot_minidump.cc new file mode 100644 index 000000000..8c870ee00 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/minidump/process_snapshot_minidump.cc @@ -0,0 +1,662 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/minidump/process_snapshot_minidump.h" + +#include + +#include "base/logging.h" +#include "base/notreached.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" +#include "minidump/minidump_extensions.h" +#include "snapshot/memory_map_region_snapshot.h" +#include "snapshot/minidump/minidump_simple_string_dictionary_reader.h" +#include "util/file/file_io.h" + +namespace crashpad { + +namespace internal { + +class MemoryMapRegionSnapshotMinidump : public MemoryMapRegionSnapshot { + public: + MemoryMapRegionSnapshotMinidump(MINIDUMP_MEMORY_INFO info) : info_(info) {} + ~MemoryMapRegionSnapshotMinidump() override = default; + + const MINIDUMP_MEMORY_INFO& AsMinidumpMemoryInfo() const override { + return info_; + } + + private: + MINIDUMP_MEMORY_INFO info_; +}; + +} // namespace internal + +ProcessSnapshotMinidump::ProcessSnapshotMinidump() + : ProcessSnapshot(), + header_(), + stream_directory_(), + stream_map_(), + modules_(), + threads_(), + unloaded_modules_(), + mem_regions_(), + mem_regions_exposed_(), + custom_streams_(), + crashpad_info_(), + system_snapshot_(), + exception_snapshot_(), + arch_(CPUArchitecture::kCPUArchitectureUnknown), + annotations_simple_map_(), + file_reader_(nullptr), + process_id_(kInvalidProcessID), + create_time_(0), + user_time_(0), + kernel_time_(0), + initialized_() {} + +ProcessSnapshotMinidump::~ProcessSnapshotMinidump() {} + +bool ProcessSnapshotMinidump::Initialize(FileReaderInterface* file_reader) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + file_reader_ = file_reader; + + if (!file_reader_->SeekSet(0)) { + return false; + } + + if (!file_reader_->ReadExactly(&header_, sizeof(header_))) { + return false; + } + + if (header_.Signature != MINIDUMP_SIGNATURE) { + LOG(ERROR) << "minidump signature mismatch"; + return false; + } + + if (header_.Version != MINIDUMP_VERSION) { + LOG(ERROR) << "minidump version mismatch"; + return false; + } + + if (!file_reader->SeekSet(header_.StreamDirectoryRva)) { + return false; + } + + stream_directory_.resize(header_.NumberOfStreams); + if (!stream_directory_.empty() && + !file_reader_->ReadExactly( + &stream_directory_[0], + header_.NumberOfStreams * sizeof(stream_directory_[0]))) { + return false; + } + + for (const MINIDUMP_DIRECTORY& directory : stream_directory_) { + const MinidumpStreamType stream_type = + static_cast(directory.StreamType); + if (stream_map_.find(stream_type) != stream_map_.end()) { + LOG(ERROR) << "duplicate streams for type " << directory.StreamType; + return false; + } + + stream_map_[stream_type] = &directory.Location; + } + + if (!InitializeCrashpadInfo() || !InitializeMiscInfo() || + !InitializeModules() || !InitializeSystemSnapshot() || + !InitializeMemoryInfo() || !InitializeExtraMemory() || + !InitializeThreads() || !InitializeCustomMinidumpStreams() || + !InitializeExceptionSnapshot()) { + return false; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +crashpad::ProcessID ProcessSnapshotMinidump::ProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return process_id_; +} + +crashpad::ProcessID ProcessSnapshotMinidump::ParentProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return 0; +} + +void ProcessSnapshotMinidump::SnapshotTime(timeval* snapshot_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + snapshot_time->tv_sec = header_.TimeDateStamp; + snapshot_time->tv_usec = 0; +} + +void ProcessSnapshotMinidump::ProcessStartTime(timeval* start_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + start_time->tv_sec = create_time_; + start_time->tv_usec = 0; +} + +void ProcessSnapshotMinidump::ProcessCPUTimes(timeval* user_time, + timeval* system_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + user_time->tv_sec = user_time_; + user_time->tv_usec = 0; + system_time->tv_sec = kernel_time_; + system_time->tv_usec = 0; +} + +void ProcessSnapshotMinidump::ReportID(UUID* report_id) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *report_id = crashpad_info_.report_id; +} + +void ProcessSnapshotMinidump::ClientID(UUID* client_id) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *client_id = crashpad_info_.client_id; +} + +const std::map& +ProcessSnapshotMinidump::AnnotationsSimpleMap() const { + // TODO(mark): This method should not be const, although the interface + // currently imposes this requirement. Making it non-const would allow + // annotations_simple_map_ to be lazily constructed: InitializeCrashpadInfo() + // could be called here, and from other locations that require it, rather than + // calling it from Initialize(). + // https://crashpad.chromium.org/bug/9 + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return annotations_simple_map_; +} + +const SystemSnapshot* ProcessSnapshotMinidump::System() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &system_snapshot_; +} + +std::vector ProcessSnapshotMinidump::Threads() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector threads; + for (const auto& thread : threads_) { + threads.push_back(thread.get()); + } + return threads; +} + +std::vector ProcessSnapshotMinidump::Modules() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector modules; + for (const auto& module : modules_) { + modules.push_back(module.get()); + } + return modules; +} + +std::vector ProcessSnapshotMinidump::UnloadedModules() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return unloaded_modules_; +} + +const ExceptionSnapshot* ProcessSnapshotMinidump::Exception() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (exception_snapshot_.IsValid()) { + return &exception_snapshot_; + } + // Allow caller to know whether the minidump contained an exception stream. + return nullptr; +} + +std::vector ProcessSnapshotMinidump::MemoryMap() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return mem_regions_exposed_; +} + +std::vector ProcessSnapshotMinidump::Handles() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return std::vector(); +} + +std::vector ProcessSnapshotMinidump::ExtraMemory() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector chunks; + for (const auto& chunk : extra_memory_) { + chunks.push_back(chunk.get()); + } + return chunks; +} + +const ProcessMemory* ProcessSnapshotMinidump::Memory() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return nullptr; +} + +std::vector +ProcessSnapshotMinidump::CustomMinidumpStreams() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + std::vector result; + result.reserve(custom_streams_.size()); + for (const auto& custom_stream : custom_streams_) { + result.push_back(custom_stream.get()); + } + return result; +} + +bool ProcessSnapshotMinidump::InitializeCrashpadInfo() { + const auto& stream_it = stream_map_.find(kMinidumpStreamTypeCrashpadInfo); + if (stream_it == stream_map_.end()) { + return true; + } + + if (stream_it->second->DataSize < sizeof(crashpad_info_)) { + LOG(ERROR) << "crashpad_info size mismatch"; + return false; + } + + if (!file_reader_->SeekSet(stream_it->second->Rva)) { + return false; + } + + if (!file_reader_->ReadExactly(&crashpad_info_, sizeof(crashpad_info_))) { + return false; + } + + if (crashpad_info_.version != MinidumpCrashpadInfo::kVersion) { + LOG(ERROR) << "crashpad_info version mismatch"; + return false; + } + + return internal::ReadMinidumpSimpleStringDictionary( + file_reader_, + crashpad_info_.simple_annotations, + &annotations_simple_map_); +} + +bool ProcessSnapshotMinidump::InitializeMiscInfo() { + const auto& stream_it = stream_map_.find(kMinidumpStreamTypeMiscInfo); + if (stream_it == stream_map_.end()) { + return true; + } + + if (!file_reader_->SeekSet(stream_it->second->Rva)) { + return false; + } + + const size_t size = stream_it->second->DataSize; + if (size != sizeof(MINIDUMP_MISC_INFO_5) && + size != sizeof(MINIDUMP_MISC_INFO_4) && + size != sizeof(MINIDUMP_MISC_INFO_3) && + size != sizeof(MINIDUMP_MISC_INFO_2) && + size != sizeof(MINIDUMP_MISC_INFO)) { + LOG(ERROR) << "misc_info size mismatch"; + return false; + } + + MINIDUMP_MISC_INFO_5 info; + if (!file_reader_->ReadExactly(&info, size)) { + return false; + } + + switch (stream_it->second->DataSize) { + case sizeof(MINIDUMP_MISC_INFO_5): + case sizeof(MINIDUMP_MISC_INFO_4): +#if defined(WCHAR_T_IS_UTF16) + full_version_ = base::WideToUTF8(info.BuildString); +#else + full_version_ = base::UTF16ToUTF8(info.BuildString); +#endif + full_version_ = full_version_.substr(0, full_version_.find(';')); + [[fallthrough]]; + case sizeof(MINIDUMP_MISC_INFO_3): + case sizeof(MINIDUMP_MISC_INFO_2): + case sizeof(MINIDUMP_MISC_INFO): + // TODO(jperaza): Save the remaining misc info. + // https://crashpad.chromium.org/bug/10 + process_id_ = info.ProcessId; + create_time_ = info.ProcessCreateTime; + user_time_ = info.ProcessUserTime; + kernel_time_ = info.ProcessKernelTime; + } + + return true; +} + +bool ProcessSnapshotMinidump::InitializeModules() { + const auto& stream_it = stream_map_.find(kMinidumpStreamTypeModuleList); + if (stream_it == stream_map_.end()) { + return true; + } + + std::map module_crashpad_info_links; + if (!InitializeModulesCrashpadInfo(&module_crashpad_info_links)) { + return false; + } + + if (stream_it->second->DataSize < sizeof(MINIDUMP_MODULE_LIST)) { + LOG(ERROR) << "module_list size mismatch"; + return false; + } + + if (!file_reader_->SeekSet(stream_it->second->Rva)) { + return false; + } + + uint32_t module_count; + if (!file_reader_->ReadExactly(&module_count, sizeof(module_count))) { + return false; + } + + if (sizeof(MINIDUMP_MODULE_LIST) + module_count * sizeof(MINIDUMP_MODULE) != + stream_it->second->DataSize) { + LOG(ERROR) << "module_list size mismatch"; + return false; + } + + for (uint32_t module_index = 0; module_index < module_count; ++module_index) { + const RVA module_rva = stream_it->second->Rva + sizeof(module_count) + + module_index * sizeof(MINIDUMP_MODULE); + + const auto& module_crashpad_info_it = + module_crashpad_info_links.find(module_index); + const MINIDUMP_LOCATION_DESCRIPTOR* module_crashpad_info_location = + module_crashpad_info_it != module_crashpad_info_links.end() + ? &module_crashpad_info_it->second + : nullptr; + + auto module = std::make_unique(); + if (!module->Initialize( + file_reader_, module_rva, module_crashpad_info_location)) { + return false; + } + + modules_.push_back(std::move(module)); + } + + return true; +} + +bool ProcessSnapshotMinidump::InitializeModulesCrashpadInfo( + std::map* + module_crashpad_info_links) { + module_crashpad_info_links->clear(); + + if (crashpad_info_.version != MinidumpCrashpadInfo::kVersion) { + return false; + } + + if (crashpad_info_.module_list.Rva == 0) { + return true; + } + + if (crashpad_info_.module_list.DataSize < + sizeof(MinidumpModuleCrashpadInfoList)) { + LOG(ERROR) << "module_crashpad_info_list size mismatch"; + return false; + } + + if (!file_reader_->SeekSet(crashpad_info_.module_list.Rva)) { + return false; + } + + uint32_t crashpad_module_count; + if (!file_reader_->ReadExactly(&crashpad_module_count, + sizeof(crashpad_module_count))) { + return false; + } + + if (crashpad_info_.module_list.DataSize != + sizeof(MinidumpModuleCrashpadInfoList) + + crashpad_module_count * sizeof(MinidumpModuleCrashpadInfoLink)) { + LOG(ERROR) << "module_crashpad_info_list size mismatch"; + return false; + } + + std::unique_ptr minidump_links( + new MinidumpModuleCrashpadInfoLink[crashpad_module_count]); + if (!file_reader_->ReadExactly( + &minidump_links[0], + crashpad_module_count * sizeof(MinidumpModuleCrashpadInfoLink))) { + return false; + } + + for (uint32_t crashpad_module_index = 0; + crashpad_module_index < crashpad_module_count; + ++crashpad_module_index) { + const MinidumpModuleCrashpadInfoLink& minidump_link = + minidump_links[crashpad_module_index]; + if (!module_crashpad_info_links + ->insert(std::make_pair(minidump_link.minidump_module_list_index, + minidump_link.location)) + .second) { + LOG(WARNING) + << "duplicate module_crashpad_info_list minidump_module_list_index " + << minidump_link.minidump_module_list_index; + return false; + } + } + + return true; +} + +bool ProcessSnapshotMinidump::InitializeMemoryInfo() { + const auto& stream_it = stream_map_.find(kMinidumpStreamTypeMemoryInfoList); + if (stream_it == stream_map_.end()) { + return true; + } + + if (stream_it->second->DataSize < sizeof(MINIDUMP_MEMORY_INFO_LIST)) { + LOG(ERROR) << "memory_info_list size mismatch"; + return false; + } + + if (!file_reader_->SeekSet(stream_it->second->Rva)) { + return false; + } + + MINIDUMP_MEMORY_INFO_LIST list; + + if (!file_reader_->ReadExactly(&list, sizeof(list))) { + return false; + } + + if (list.SizeOfHeader != sizeof(list)) { + return false; + } + + if (list.SizeOfEntry != sizeof(MINIDUMP_MEMORY_INFO)) { + return false; + } + + if (sizeof(MINIDUMP_MEMORY_INFO_LIST) + + list.NumberOfEntries * list.SizeOfEntry != + stream_it->second->DataSize) { + LOG(ERROR) << "memory_info_list size mismatch"; + return false; + } + + for (uint32_t i = 0; i < list.NumberOfEntries; i++) { + MINIDUMP_MEMORY_INFO info; + + if (!file_reader_->ReadExactly(&info, sizeof(info))) { + return false; + } + + mem_regions_.emplace_back( + std::make_unique(info)); + mem_regions_exposed_.emplace_back(mem_regions_.back().get()); + } + + return true; +} + +bool ProcessSnapshotMinidump::InitializeExtraMemory() { + const auto& stream_it = stream_map_.find(kMinidumpStreamTypeMemoryList); + if (stream_it == stream_map_.end()) { + return true; + } + + if (stream_it->second->DataSize < sizeof(MINIDUMP_MEMORY_LIST)) { + LOG(ERROR) << "memory_list size mismatch"; + return false; + } + + if (!file_reader_->SeekSet(stream_it->second->Rva)) { + return false; + } + + // MSVC won't let us stack-allocate a MINIDUMP_MEMORY_LIST because of its + // trailing zero-element array. Luckily we're only interested in its other + // field anyway: a uint32_t indicating the number of memory descriptors that + // follow. + static_assert( + sizeof(MINIDUMP_MEMORY_LIST) == 4, + "MINIDUMP_MEMORY_LIST's only actual field should be an uint32_t"); + uint32_t num_ranges; + if (!file_reader_->ReadExactly(&num_ranges, sizeof(num_ranges))) { + return false; + } + + // We have to manually keep track of the locations of the entries in the + // contiguous list of MINIDUMP_MEMORY_DESCRIPTORs, because the Initialize() + // function jumps around the file to find the contents of each snapshot. + FileOffset location = file_reader_->SeekGet(); + for (uint32_t i = 0; i < num_ranges; i++) { + extra_memory_.emplace_back( + std::make_unique()); + if (!extra_memory_.back()->Initialize(file_reader_, + static_cast(location))) { + return false; + } + location += sizeof(MINIDUMP_MEMORY_DESCRIPTOR); + } + + return true; +} + +bool ProcessSnapshotMinidump::InitializeThreads() { + const auto& stream_it = stream_map_.find(kMinidumpStreamTypeThreadList); + if (stream_it == stream_map_.end()) { + return true; + } + + if (stream_it->second->DataSize < sizeof(MINIDUMP_THREAD_LIST)) { + LOG(ERROR) << "thread_list size mismatch"; + return false; + } + + if (!file_reader_->SeekSet(stream_it->second->Rva)) { + return false; + } + + uint32_t thread_count; + if (!file_reader_->ReadExactly(&thread_count, sizeof(thread_count))) { + return false; + } + + if (sizeof(MINIDUMP_THREAD_LIST) + thread_count * sizeof(MINIDUMP_THREAD) != + stream_it->second->DataSize) { + LOG(ERROR) << "thread_list size mismatch"; + return false; + } + + for (uint32_t thread_index = 0; thread_index < thread_count; ++thread_index) { + const RVA thread_rva = stream_it->second->Rva + sizeof(thread_count) + + thread_index * sizeof(MINIDUMP_THREAD); + + auto thread = std::make_unique(); + if (!thread->Initialize(file_reader_, thread_rva, arch_)) { + return false; + } + + threads_.push_back(std::move(thread)); + } + + return true; +} + +bool ProcessSnapshotMinidump::InitializeSystemSnapshot() { + const auto& stream_it = stream_map_.find(kMinidumpStreamTypeSystemInfo); + if (stream_it == stream_map_.end()) { + return true; + } + + if (stream_it->second->DataSize < sizeof(MINIDUMP_SYSTEM_INFO)) { + LOG(ERROR) << "system info size mismatch"; + return false; + } + + if (!system_snapshot_.Initialize( + file_reader_, stream_it->second->Rva, full_version_)) { + return false; + } + + arch_ = system_snapshot_.GetCPUArchitecture(); + return true; +} + +bool ProcessSnapshotMinidump::InitializeCustomMinidumpStreams() { + for (size_t i = 0; i < stream_directory_.size(); i++) { + const auto& stream = stream_directory_[i]; + + // Filter out reserved minidump and crashpad streams. + const uint32_t stream_type = stream.StreamType; + if (stream_type <= + MinidumpStreamType::kMinidumpStreamTypeLastReservedStream || + (stream_type >= MinidumpStreamType::kMinidumpStreamTypeCrashpadInfo && + stream_type <= MinidumpStreamType:: + kMinidumpStreamTypeCrashpadLastReservedStream)) { + continue; + } + + std::vector data(stream.Location.DataSize); + if (!file_reader_->SeekSet(stream.Location.Rva) || + !file_reader_->ReadExactly(data.data(), data.size())) { + LOG(ERROR) << "Failed to read stream with ID 0x" << std::hex + << stream_type << std::dec << " at index " << i; + return false; + } + + custom_streams_.push_back( + std::make_unique(stream_type, std::move(data))); + } + + return true; +} + +bool ProcessSnapshotMinidump::InitializeExceptionSnapshot() { + const auto& stream_it = stream_map_.find(kMinidumpStreamTypeException); + if (stream_it == stream_map_.end()) { + return true; + } + + if (stream_it->second->DataSize < sizeof(MINIDUMP_EXCEPTION_STREAM)) { + LOG(ERROR) << "system info size mismatch"; + return false; + } + + if (!exception_snapshot_.Initialize( + file_reader_, arch_, stream_it->second->Rva)) { + return false; + } + + return true; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/minidump/process_snapshot_minidump.h b/shared/sentry/external/crashpad/snapshot/minidump/process_snapshot_minidump.h new file mode 100644 index 000000000..18fbc7c36 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/minidump/process_snapshot_minidump.h @@ -0,0 +1,172 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_PROCESS_SNAPSHOT_MINIDUMP_H_ +#define CRASHPAD_SNAPSHOT_MINIDUMP_PROCESS_SNAPSHOT_MINIDUMP_H_ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "minidump/minidump_extensions.h" +#include "snapshot/exception_snapshot.h" +#include "snapshot/memory_snapshot.h" +#include "snapshot/minidump/exception_snapshot_minidump.h" +#include "snapshot/minidump/minidump_stream.h" +#include "snapshot/minidump/module_snapshot_minidump.h" +#include "snapshot/minidump/system_snapshot_minidump.h" +#include "snapshot/minidump/thread_snapshot_minidump.h" +#include "snapshot/module_snapshot.h" +#include "snapshot/process_snapshot.h" +#include "snapshot/system_snapshot.h" +#include "snapshot/thread_snapshot.h" +#include "snapshot/unloaded_module_snapshot.h" +#include "util/file/file_reader.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/misc/uuid.h" +#include "util/process/process_id.h" + +namespace crashpad { + +namespace internal { +class MemoryMapRegionSnapshotMinidump; +} // namespace internal + +//! \brief A ProcessSnapshot based on a minidump file. +class ProcessSnapshotMinidump final : public ProcessSnapshot { + public: + ProcessSnapshotMinidump(); + + ProcessSnapshotMinidump(const ProcessSnapshotMinidump&) = delete; + ProcessSnapshotMinidump& operator=(const ProcessSnapshotMinidump&) = delete; + + ~ProcessSnapshotMinidump() override; + + //! \brief Initializes the object. + //! + //! \param[in] file_reader A file reader corresponding to a minidump file. + //! The file reader must support seeking. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(FileReaderInterface* file_reader); + + // ProcessSnapshot: + + crashpad::ProcessID ProcessID() const override; + crashpad::ProcessID ParentProcessID() const override; + void SnapshotTime(timeval* snapshot_time) const override; + void ProcessStartTime(timeval* start_time) const override; + void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override; + void ReportID(UUID* report_id) const override; + void ClientID(UUID* client_id) const override; + const std::map& AnnotationsSimpleMap() + const override; + const SystemSnapshot* System() const override; + std::vector Threads() const override; + std::vector Modules() const override; + std::vector UnloadedModules() const override; + const ExceptionSnapshot* Exception() const override; + std::vector MemoryMap() const override; + std::vector Handles() const override; + std::vector ExtraMemory() const override; + const ProcessMemory* Memory() const override; + + //! \brief Returns a list of custom minidump streams. This routine is the + //! equivalent of ModuleSnapshot::CustomMinidumpStreams(), except that in + //! a minidump it is impossible to associate a custom stream to a specific + //! module. + //! + //! \return The caller does not take ownership of the returned objects, they + //! are scoped to the lifetime of the ProcessSnapshotMinidump object that + //! they were obtained from. + std::vector CustomMinidumpStreams() const; + + private: + // Initializes data carried in a MinidumpCrashpadInfo stream on behalf of + // Initialize(). + bool InitializeCrashpadInfo(); + + // Initializes data carried in a MINIDUMP_MODULE_LIST stream on behalf of + // Initialize(). + bool InitializeModules(); + + // Initializes data carried in a MINIDUMP_THREAD_LIST stream on behalf of + // Initialize(). + bool InitializeThreads(); + + // Initializes data carried in a MINIDUMP_MEMORY_INFO_LIST stream on behalf of + // Initialize(). + bool InitializeMemoryInfo(); + + // Initializes data carried in a MINIDUMP_MEMORY_LIST stream on behalf of + // Initialize(). + bool InitializeExtraMemory(); + + // Initializes data carried in a MINIDUMP_SYSTEM_INFO stream on behalf of + // Initialize(). + bool InitializeSystemSnapshot(); + + // Initializes data carried in a MinidumpModuleCrashpadInfoList structure on + // behalf of InitializeModules(). This makes use of MinidumpCrashpadInfo as + // well, so it must be called after InitializeCrashpadInfo(). + bool InitializeModulesCrashpadInfo( + std::map* + module_crashpad_info_links); + + // Initializes data carried in a MINIDUMP_MISC_INFO structure on behalf of + // Initialize(). + bool InitializeMiscInfo(); + + // Initializes custom minidump streams. + bool InitializeCustomMinidumpStreams(); + + // Initializes data carried in a MINIDUMP_EXCEPTION_STREAM stream on behalf of + // Initialize(). + bool InitializeExceptionSnapshot(); + + MINIDUMP_HEADER header_; + std::vector stream_directory_; + std::map stream_map_; + std::vector> modules_; + std::vector> threads_; + std::vector unloaded_modules_; + std::vector> + mem_regions_; + std::vector mem_regions_exposed_; + std::vector> extra_memory_; + std::vector> custom_streams_; + MinidumpCrashpadInfo crashpad_info_; + internal::SystemSnapshotMinidump system_snapshot_; + internal::ExceptionSnapshotMinidump exception_snapshot_; + CPUArchitecture arch_; + std::map annotations_simple_map_; + std::string full_version_; + FileReaderInterface* file_reader_; // weak + crashpad::ProcessID process_id_; + uint32_t create_time_; + uint32_t user_time_; + uint32_t kernel_time_; + InitializationStateDcheck initialized_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MINIDUMP_PROCESS_SNAPSHOT_MINIDUMP_H_ diff --git a/shared/sentry/external/crashpad/snapshot/minidump/process_snapshot_minidump_test.cc b/shared/sentry/external/crashpad/snapshot/minidump/process_snapshot_minidump_test.cc new file mode 100644 index 000000000..ded561bf1 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/minidump/process_snapshot_minidump_test.cc @@ -0,0 +1,1438 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/minidump/process_snapshot_minidump.h" + +#include +#include +#include + +#include +#include +#include + +#include "base/numerics/safe_math.h" +#include "base/strings/utf_string_conversions.h" +#include "gtest/gtest.h" +#include "minidump/minidump_context.h" +#include "snapshot/memory_map_region_snapshot.h" +#include "snapshot/minidump/minidump_annotation_reader.h" +#include "snapshot/module_snapshot.h" +#include "util/file/string_file.h" +#include "util/misc/pdb_structures.h" + +namespace crashpad { +namespace test { +namespace { + +class ReadToVector : public crashpad::MemorySnapshot::Delegate { + public: + std::vector result; + + bool MemorySnapshotDelegateRead(void* data, size_t size) override { + result.resize(size); + memcpy(result.data(), data, size); + return true; + } +}; + +MinidumpContextARM64 GetArm64MinidumpContext() { + MinidumpContextARM64 minidump_context; + + minidump_context.context_flags = kMinidumpContextARM64Full; + + minidump_context.cpsr = 0; + + for (int i = 0; i < 29; i++) { + minidump_context.regs[i] = i + 1; + } + + minidump_context.fp = 30; + minidump_context.lr = 31; + minidump_context.sp = 32; + minidump_context.pc = 33; + + for (int i = 0; i < 32; i++) { + minidump_context.fpsimd[i].lo = i * 2 + 34; + minidump_context.fpsimd[i].hi = i * 2 + 35; + } + + minidump_context.fpcr = 98; + minidump_context.fpsr = 99; + + for (int i = 0; i < 8; i++) { + minidump_context.bcr[i] = i * 2 + 100; + minidump_context.bvr[i] = i * 2 + 101; + } + + for (int i = 0; i < 2; i++) { + minidump_context.wcr[i] = i * 2 + 115; + minidump_context.wvr[i] = i * 2 + 116; + } + + return minidump_context; +} + +TEST(ProcessSnapshotMinidump, EmptyFile) { + StringFile string_file; + ProcessSnapshotMinidump process_snapshot; + + EXPECT_FALSE(process_snapshot.Initialize(&string_file)); +} + +TEST(ProcessSnapshotMinidump, InvalidSignatureAndVersion) { + StringFile string_file; + + MINIDUMP_HEADER header = {}; + + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + ProcessSnapshotMinidump process_snapshot; + EXPECT_FALSE(process_snapshot.Initialize(&string_file)); +} + +TEST(ProcessSnapshotMinidump, Empty) { + StringFile string_file; + + MINIDUMP_HEADER header = {}; + header.Signature = MINIDUMP_SIGNATURE; + header.Version = MINIDUMP_VERSION; + + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + ProcessSnapshotMinidump process_snapshot; + EXPECT_TRUE(process_snapshot.Initialize(&string_file)); + + UUID client_id; + process_snapshot.ClientID(&client_id); + EXPECT_EQ(client_id, UUID()); + + EXPECT_TRUE(process_snapshot.AnnotationsSimpleMap().empty()); +} + +// Writes |string| to |writer| as a MinidumpUTF8String, and returns the file +// offset of the beginning of the string. +RVA WriteString(FileWriterInterface* writer, const std::string& string) { + RVA rva = static_cast(writer->SeekGet()); + + uint32_t string_size = static_cast(string.size()); + EXPECT_TRUE(writer->Write(&string_size, sizeof(string_size))); + + // Include the trailing NUL character. + EXPECT_TRUE(writer->Write(string.c_str(), string.size() + 1)); + + return rva; +} + +// Writes |dictionary| to |writer| as a MinidumpSimpleStringDictionary, and +// populates |location| with a location descriptor identifying what was written. +void WriteMinidumpSimpleStringDictionary( + MINIDUMP_LOCATION_DESCRIPTOR* location, + FileWriterInterface* writer, + const std::map& dictionary) { + std::vector entries; + for (const auto& it : dictionary) { + MinidumpSimpleStringDictionaryEntry entry; + entry.key = WriteString(writer, it.first); + entry.value = WriteString(writer, it.second); + entries.push_back(entry); + } + + location->Rva = static_cast(writer->SeekGet()); + + const uint32_t simple_string_dictionary_entries = + static_cast(entries.size()); + EXPECT_TRUE(writer->Write(&simple_string_dictionary_entries, + sizeof(simple_string_dictionary_entries))); + for (const MinidumpSimpleStringDictionaryEntry& entry : entries) { + EXPECT_TRUE(writer->Write(&entry, sizeof(entry))); + } + + location->DataSize = static_cast( + sizeof(simple_string_dictionary_entries) + + entries.size() * sizeof(MinidumpSimpleStringDictionaryEntry)); +} + +// Writes |strings| to |writer| as a MinidumpRVAList referencing +// MinidumpUTF8String objects, and populates |location| with a location +// descriptor identifying what was written. +void WriteMinidumpStringList(MINIDUMP_LOCATION_DESCRIPTOR* location, + FileWriterInterface* writer, + const std::vector& strings) { + std::vector rvas; + for (const std::string& string : strings) { + rvas.push_back(WriteString(writer, string)); + } + + location->Rva = static_cast(writer->SeekGet()); + + const uint32_t string_list_entries = static_cast(rvas.size()); + EXPECT_TRUE(writer->Write(&string_list_entries, sizeof(string_list_entries))); + for (RVA rva : rvas) { + EXPECT_TRUE(writer->Write(&rva, sizeof(rva))); + } + + location->DataSize = static_cast(sizeof(string_list_entries) + + rvas.size() * sizeof(RVA)); +} + +// Writes |data| to |writer| as a MinidumpByteArray, and returns the file offset +// from the beginning of the string. +RVA WriteByteArray(FileWriterInterface* writer, + const std::vector data) { + auto rva = static_cast(writer->SeekGet()); + + auto length = static_cast(data.size()); + EXPECT_TRUE(writer->Write(&length, sizeof(length))); + EXPECT_TRUE(writer->Write(data.data(), length)); + + return rva; +} + +// Writes |annotations| to |writer| as a MinidumpAnnotationList, and populates +// |location| with a location descriptor identifying what was written. +void WriteMinidumpAnnotationList( + MINIDUMP_LOCATION_DESCRIPTOR* location, + FileWriterInterface* writer, + const std::vector& annotations) { + std::vector minidump_annotations; + for (const auto& it : annotations) { + MinidumpAnnotation annotation; + annotation.name = WriteString(writer, it.name); + annotation.type = it.type; + annotation.reserved = 0; + annotation.value = WriteByteArray(writer, it.value); + minidump_annotations.push_back(annotation); + } + + location->Rva = static_cast(writer->SeekGet()); + + auto count = static_cast(minidump_annotations.size()); + EXPECT_TRUE(writer->Write(&count, sizeof(count))); + + for (const auto& it : minidump_annotations) { + EXPECT_TRUE(writer->Write(&it, sizeof(MinidumpAnnotation))); + } + + location->DataSize = + sizeof(MinidumpAnnotationList) + count * sizeof(MinidumpAnnotation); +} + +TEST(ProcessSnapshotMinidump, ClientID) { + StringFile string_file; + + MINIDUMP_HEADER header = {}; + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + UUID client_id; + ASSERT_TRUE( + client_id.InitializeFromString("0001f4a9-d00d-5155-0a55-c0ffeec0ffee")); + + MinidumpCrashpadInfo crashpad_info = {}; + crashpad_info.version = MinidumpCrashpadInfo::kVersion; + crashpad_info.client_id = client_id; + + MINIDUMP_DIRECTORY crashpad_info_directory = {}; + crashpad_info_directory.StreamType = kMinidumpStreamTypeCrashpadInfo; + crashpad_info_directory.Location.Rva = + static_cast(string_file.SeekGet()); + EXPECT_TRUE(string_file.Write(&crashpad_info, sizeof(crashpad_info))); + crashpad_info_directory.Location.DataSize = sizeof(crashpad_info); + + header.StreamDirectoryRva = static_cast(string_file.SeekGet()); + EXPECT_TRUE(string_file.Write(&crashpad_info_directory, + sizeof(crashpad_info_directory))); + + header.Signature = MINIDUMP_SIGNATURE; + header.Version = MINIDUMP_VERSION; + header.NumberOfStreams = 1; + EXPECT_TRUE(string_file.SeekSet(0)); + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + ProcessSnapshotMinidump process_snapshot; + EXPECT_TRUE(process_snapshot.Initialize(&string_file)); + + UUID actual_client_id; + process_snapshot.ClientID(&actual_client_id); + EXPECT_EQ(actual_client_id, client_id); + + EXPECT_TRUE(process_snapshot.AnnotationsSimpleMap().empty()); +} + +TEST(ProcessSnapshotMinidump, AnnotationsSimpleMap) { + StringFile string_file; + + MINIDUMP_HEADER header = {}; + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + MinidumpCrashpadInfo crashpad_info = {}; + crashpad_info.version = MinidumpCrashpadInfo::kVersion; + + std::map dictionary; + dictionary["the first key"] = "THE FIRST VALUE EVER!"; + dictionary["2key"] = "a lowly second value"; + WriteMinidumpSimpleStringDictionary( + &crashpad_info.simple_annotations, &string_file, dictionary); + + MINIDUMP_DIRECTORY crashpad_info_directory = {}; + crashpad_info_directory.StreamType = kMinidumpStreamTypeCrashpadInfo; + crashpad_info_directory.Location.Rva = + static_cast(string_file.SeekGet()); + EXPECT_TRUE(string_file.Write(&crashpad_info, sizeof(crashpad_info))); + crashpad_info_directory.Location.DataSize = sizeof(crashpad_info); + + header.StreamDirectoryRva = static_cast(string_file.SeekGet()); + EXPECT_TRUE(string_file.Write(&crashpad_info_directory, + sizeof(crashpad_info_directory))); + + header.Signature = MINIDUMP_SIGNATURE; + header.Version = MINIDUMP_VERSION; + header.NumberOfStreams = 1; + EXPECT_TRUE(string_file.SeekSet(0)); + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + ProcessSnapshotMinidump process_snapshot; + EXPECT_TRUE(process_snapshot.Initialize(&string_file)); + + UUID client_id; + process_snapshot.ClientID(&client_id); + EXPECT_EQ(client_id, UUID()); + + const auto annotations_simple_map = process_snapshot.AnnotationsSimpleMap(); + EXPECT_EQ(annotations_simple_map, dictionary); +} + +TEST(ProcessSnapshotMinidump, AnnotationObjects) { + StringFile string_file; + + MINIDUMP_HEADER header{}; + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + std::vector annotations; + annotations.emplace_back( + AnnotationSnapshot("name 1", 0xBBBB, {'t', 'e', '\0', 's', 't', '\0'})); + annotations.emplace_back( + AnnotationSnapshot("name 2", 0xABBA, {0xF0, 0x9F, 0x92, 0x83})); + + MINIDUMP_LOCATION_DESCRIPTOR location; + WriteMinidumpAnnotationList(&location, &string_file, annotations); + + std::vector read_annotations; + EXPECT_TRUE(internal::ReadMinidumpAnnotationList( + &string_file, location, &read_annotations)); + + EXPECT_EQ(read_annotations, annotations); +} + +TEST(ProcessSnapshotMinidump, Modules) { + StringFile string_file; + + MINIDUMP_HEADER header = {}; + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + MINIDUMP_MODULE minidump_module = {}; + constexpr uint32_t minidump_module_count = 4; + RVA name_rvas[minidump_module_count]; + std::string names[minidump_module_count] = { + "libtacotruck", + "libevidencebased", + "libgeorgism", + "librealistutopia", + }; + constexpr char debug_name[] = "debugme.pdb"; + + minidump_module.BaseOfImage = 0xbadf00d; + minidump_module.SizeOfImage = 9001; + minidump_module.TimeDateStamp = 1970; + minidump_module.VersionInfo.dwFileVersionMS = 0xAABBCCDD; + minidump_module.VersionInfo.dwFileVersionLS = 0xEEFF4242; + minidump_module.VersionInfo.dwProductVersionMS = 0xAAAABBBB; + minidump_module.VersionInfo.dwProductVersionLS = 0xCCCCDDDD; + minidump_module.VersionInfo.dwFileType = VFT_APP; + + for (uint32_t i = 0; i < minidump_module_count; i++) { + name_rvas[i] = static_cast(string_file.SeekGet()); + auto name16 = base::UTF8ToUTF16(names[i]); + uint32_t size = + base::checked_cast(sizeof(name16[0]) * name16.size()); + EXPECT_TRUE(string_file.Write(&size, sizeof(size))); + EXPECT_TRUE(string_file.Write(&name16[0], size)); + } + + CodeViewRecordPDB70 pdb70_cv; + pdb70_cv.signature = CodeViewRecordPDB70::kSignature; + pdb70_cv.age = 7; + pdb70_cv.uuid.InitializeFromString("00112233-4455-6677-8899-aabbccddeeff"); + + auto pdb70_loc = static_cast(string_file.SeekGet()); + auto pdb70_size = offsetof(CodeViewRecordPDB70, pdb_name); + + EXPECT_TRUE(string_file.Write(&pdb70_cv, pdb70_size)); + + size_t nul_terminated_length = strlen(debug_name) + 1; + EXPECT_TRUE(string_file.Write(debug_name, nul_terminated_length)); + pdb70_size += nul_terminated_length; + + CodeViewRecordBuildID build_id_cv; + build_id_cv.signature = CodeViewRecordBuildID::kSignature; + + auto build_id_cv_loc = static_cast(string_file.SeekGet()); + + EXPECT_TRUE(string_file.Write(&build_id_cv, + offsetof(CodeViewRecordBuildID, build_id))); + EXPECT_TRUE(string_file.Write("atestbuildidbecausewhynot", 25)); + + auto build_id_cv_size = + static_cast(string_file.SeekGet() - build_id_cv_loc); + + MINIDUMP_DIRECTORY minidump_module_list_directory = {}; + minidump_module_list_directory.StreamType = kMinidumpStreamTypeModuleList; + minidump_module_list_directory.Location.DataSize = + sizeof(MINIDUMP_MODULE_LIST) + + minidump_module_count * sizeof(MINIDUMP_MODULE); + minidump_module_list_directory.Location.Rva = + static_cast(string_file.SeekGet()); + + EXPECT_TRUE( + string_file.Write(&minidump_module_count, sizeof(minidump_module_count))); + for (uint32_t minidump_module_index = 0; + minidump_module_index < minidump_module_count; + ++minidump_module_index) { + if (minidump_module_index % 2) { + minidump_module.CvRecord.Rva = pdb70_loc; + minidump_module.CvRecord.DataSize = static_cast(pdb70_size); + } else { + minidump_module.CvRecord.Rva = build_id_cv_loc; + minidump_module.CvRecord.DataSize = + static_cast(build_id_cv_size); + } + + minidump_module.ModuleNameRva = name_rvas[minidump_module_index]; + EXPECT_TRUE(string_file.Write(&minidump_module, sizeof(minidump_module))); + minidump_module.TimeDateStamp++; + } + + MinidumpModuleCrashpadInfo crashpad_module_0 = {}; + crashpad_module_0.version = MinidumpModuleCrashpadInfo::kVersion; + std::map dictionary_0; + dictionary_0["ptype"] = "browser"; + dictionary_0["pid"] = "12345"; + WriteMinidumpSimpleStringDictionary( + &crashpad_module_0.simple_annotations, &string_file, dictionary_0); + + MinidumpModuleCrashpadInfoLink crashpad_module_0_link = {}; + crashpad_module_0_link.minidump_module_list_index = 0; + crashpad_module_0_link.location.DataSize = sizeof(crashpad_module_0); + crashpad_module_0_link.location.Rva = static_cast(string_file.SeekGet()); + EXPECT_TRUE(string_file.Write(&crashpad_module_0, sizeof(crashpad_module_0))); + + MinidumpModuleCrashpadInfo crashpad_module_2 = {}; + crashpad_module_2.version = MinidumpModuleCrashpadInfo::kVersion; + std::map dictionary_2; + dictionary_2["fakemodule"] = "yes"; + WriteMinidumpSimpleStringDictionary( + &crashpad_module_2.simple_annotations, &string_file, dictionary_2); + + std::vector list_annotations_2; + list_annotations_2.push_back("first string"); + list_annotations_2.push_back("last string"); + WriteMinidumpStringList( + &crashpad_module_2.list_annotations, &string_file, list_annotations_2); + + MinidumpModuleCrashpadInfoLink crashpad_module_2_link = {}; + crashpad_module_2_link.minidump_module_list_index = 2; + crashpad_module_2_link.location.DataSize = sizeof(crashpad_module_2); + crashpad_module_2_link.location.Rva = static_cast(string_file.SeekGet()); + EXPECT_TRUE(string_file.Write(&crashpad_module_2, sizeof(crashpad_module_2))); + + MinidumpModuleCrashpadInfo crashpad_module_4 = {}; + crashpad_module_4.version = MinidumpModuleCrashpadInfo::kVersion; + std::vector annotations_4{ + {"first one", 0xBADE, {'a', 'b', 'c'}}, + {"2", 0xEDD1, {0x11, 0x22, 0x33}}, + {"threeeeee", 0xDADA, {'f'}}, + }; + WriteMinidumpAnnotationList( + &crashpad_module_4.annotation_objects, &string_file, annotations_4); + + MinidumpModuleCrashpadInfoLink crashpad_module_4_link = {}; + crashpad_module_4_link.minidump_module_list_index = 3; + crashpad_module_4_link.location.DataSize = sizeof(crashpad_module_4); + crashpad_module_4_link.location.Rva = static_cast(string_file.SeekGet()); + EXPECT_TRUE(string_file.Write(&crashpad_module_4, sizeof(crashpad_module_4))); + + MinidumpCrashpadInfo crashpad_info = {}; + crashpad_info.version = MinidumpCrashpadInfo::kVersion; + + uint32_t crashpad_module_count = 3; + + crashpad_info.module_list.DataSize = + sizeof(MinidumpModuleCrashpadInfoList) + + crashpad_module_count * sizeof(MinidumpModuleCrashpadInfoLink); + crashpad_info.module_list.Rva = static_cast(string_file.SeekGet()); + + EXPECT_TRUE( + string_file.Write(&crashpad_module_count, sizeof(crashpad_module_count))); + EXPECT_TRUE(string_file.Write(&crashpad_module_0_link, + sizeof(crashpad_module_0_link))); + EXPECT_TRUE(string_file.Write(&crashpad_module_2_link, + sizeof(crashpad_module_2_link))); + EXPECT_TRUE(string_file.Write(&crashpad_module_4_link, + sizeof(crashpad_module_4_link))); + + MINIDUMP_DIRECTORY crashpad_info_directory = {}; + crashpad_info_directory.StreamType = kMinidumpStreamTypeCrashpadInfo; + crashpad_info_directory.Location.DataSize = sizeof(crashpad_info); + crashpad_info_directory.Location.Rva = + static_cast(string_file.SeekGet()); + EXPECT_TRUE(string_file.Write(&crashpad_info, sizeof(crashpad_info))); + + header.StreamDirectoryRva = static_cast(string_file.SeekGet()); + EXPECT_TRUE(string_file.Write(&minidump_module_list_directory, + sizeof(minidump_module_list_directory))); + EXPECT_TRUE(string_file.Write(&crashpad_info_directory, + sizeof(crashpad_info_directory))); + + header.Signature = MINIDUMP_SIGNATURE; + header.Version = MINIDUMP_VERSION; + header.NumberOfStreams = 2; + EXPECT_TRUE(string_file.SeekSet(0)); + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + ProcessSnapshotMinidump process_snapshot; + EXPECT_TRUE(process_snapshot.Initialize(&string_file)); + + std::vector modules = process_snapshot.Modules(); + ASSERT_EQ(modules.size(), minidump_module_count); + + for (uint32_t i = 0; i < minidump_module_count; i++) { + EXPECT_EQ(modules[i]->Name(), names[i]); + EXPECT_EQ(modules[i]->Address(), 0xbadf00dU); + EXPECT_EQ(modules[i]->Size(), 9001U); + EXPECT_EQ(modules[i]->Timestamp(), static_cast(1970U + i)); + + uint16_t v0; + uint16_t v1; + uint16_t v2; + uint16_t v3; + + modules[i]->FileVersion(&v0, &v1, &v2, &v3); + EXPECT_EQ(v0, 0xAABBU); + EXPECT_EQ(v1, 0xCCDDU); + EXPECT_EQ(v2, 0xEEFFU); + EXPECT_EQ(v3, 0x4242U); + + modules[i]->SourceVersion(&v0, &v1, &v2, &v3); + EXPECT_EQ(v0, 0xAAAAU); + EXPECT_EQ(v1, 0xBBBBU); + EXPECT_EQ(v2, 0xCCCCU); + EXPECT_EQ(v3, 0xDDDDU); + + EXPECT_EQ(modules[i]->GetModuleType(), + ModuleSnapshot::kModuleTypeExecutable); + + if (i % 2) { + uint32_t age; + UUID uuid; + modules[i]->UUIDAndAge(&uuid, &age); + + EXPECT_EQ(uuid.ToString(), "00112233-4455-6677-8899-aabbccddeeff"); + EXPECT_EQ(age, 7U); + EXPECT_EQ(modules[i]->DebugFileName(), debug_name); + } else { + auto build_id = modules[i]->BuildID(); + std::string build_id_text(build_id.data(), + build_id.data() + build_id.size()); + EXPECT_EQ(build_id_text, "atestbuildidbecausewhynot"); + } + } + + auto annotations_simple_map = modules[0]->AnnotationsSimpleMap(); + EXPECT_EQ(annotations_simple_map, dictionary_0); + + auto annotations_vector = modules[0]->AnnotationsVector(); + EXPECT_TRUE(annotations_vector.empty()); + + annotations_simple_map = modules[1]->AnnotationsSimpleMap(); + EXPECT_TRUE(annotations_simple_map.empty()); + + annotations_vector = modules[1]->AnnotationsVector(); + EXPECT_TRUE(annotations_vector.empty()); + + annotations_simple_map = modules[2]->AnnotationsSimpleMap(); + EXPECT_EQ(annotations_simple_map, dictionary_2); + + annotations_vector = modules[2]->AnnotationsVector(); + EXPECT_EQ(annotations_vector, list_annotations_2); + + auto annotation_objects = modules[3]->AnnotationObjects(); + EXPECT_EQ(annotation_objects, annotations_4); +} + +TEST(ProcessSnapshotMinidump, ProcessID) { + StringFile string_file; + + MINIDUMP_HEADER header = {}; + ASSERT_TRUE(string_file.Write(&header, sizeof(header))); + + static const crashpad::ProcessID kTestProcessId = 42; + MINIDUMP_MISC_INFO misc_info = {}; + misc_info.SizeOfInfo = sizeof(misc_info); + misc_info.Flags1 = MINIDUMP_MISC1_PROCESS_ID; + misc_info.ProcessId = kTestProcessId; + + MINIDUMP_DIRECTORY misc_directory = {}; + misc_directory.StreamType = kMinidumpStreamTypeMiscInfo; + misc_directory.Location.DataSize = sizeof(misc_info); + misc_directory.Location.Rva = static_cast(string_file.SeekGet()); + ASSERT_TRUE(string_file.Write(&misc_info, sizeof(misc_info))); + + header.StreamDirectoryRva = static_cast(string_file.SeekGet()); + ASSERT_TRUE(string_file.Write(&misc_directory, sizeof(misc_directory))); + + header.Signature = MINIDUMP_SIGNATURE; + header.Version = MINIDUMP_VERSION; + header.NumberOfStreams = 1; + ASSERT_TRUE(string_file.SeekSet(0)); + ASSERT_TRUE(string_file.Write(&header, sizeof(header))); + + ProcessSnapshotMinidump process_snapshot; + ASSERT_TRUE(process_snapshot.Initialize(&string_file)); + EXPECT_EQ(process_snapshot.ProcessID(), kTestProcessId); +} + +TEST(ProcessSnapshotMinidump, SnapshotTime) { + StringFile string_file; + + MINIDUMP_HEADER header = {}; + header.Signature = MINIDUMP_SIGNATURE; + header.Version = MINIDUMP_VERSION; + header.TimeDateStamp = 42; + ASSERT_TRUE(string_file.Write(&header, sizeof(header))); + + ProcessSnapshotMinidump process_snapshot; + ASSERT_TRUE(process_snapshot.Initialize(&string_file)); + + timeval snapshot_time; + process_snapshot.SnapshotTime(&snapshot_time); + EXPECT_EQ(snapshot_time.tv_sec, 42); + EXPECT_EQ(snapshot_time.tv_usec, 0); +} + +TEST(ProcessSnapshotMinidump, MiscTimes) { + StringFile string_file; + + MINIDUMP_HEADER header = {}; + ASSERT_TRUE(string_file.Write(&header, sizeof(header))); + + MINIDUMP_MISC_INFO misc_info = {}; + misc_info.SizeOfInfo = sizeof(misc_info); + misc_info.Flags1 = MINIDUMP_MISC1_PROCESS_TIMES; + misc_info.ProcessCreateTime = 42; + misc_info.ProcessUserTime = 43; + misc_info.ProcessKernelTime = 44; + + MINIDUMP_DIRECTORY misc_directory = {}; + misc_directory.StreamType = kMinidumpStreamTypeMiscInfo; + misc_directory.Location.DataSize = sizeof(misc_info); + misc_directory.Location.Rva = static_cast(string_file.SeekGet()); + ASSERT_TRUE(string_file.Write(&misc_info, sizeof(misc_info))); + + header.StreamDirectoryRva = static_cast(string_file.SeekGet()); + ASSERT_TRUE(string_file.Write(&misc_directory, sizeof(misc_directory))); + + header.Signature = MINIDUMP_SIGNATURE; + header.Version = MINIDUMP_VERSION; + header.NumberOfStreams = 1; + ASSERT_TRUE(string_file.SeekSet(0)); + ASSERT_TRUE(string_file.Write(&header, sizeof(header))); + + ProcessSnapshotMinidump process_snapshot; + ASSERT_TRUE(process_snapshot.Initialize(&string_file)); + + timeval start_time, user_time, kernel_time; + process_snapshot.ProcessStartTime(&start_time); + process_snapshot.ProcessCPUTimes(&user_time, &kernel_time); + EXPECT_EQ(static_cast(start_time.tv_sec), + misc_info.ProcessCreateTime); + EXPECT_EQ(start_time.tv_usec, 0); + EXPECT_EQ(static_cast(user_time.tv_sec), misc_info.ProcessUserTime); + EXPECT_EQ(user_time.tv_usec, 0); + EXPECT_EQ(static_cast(kernel_time.tv_sec), + misc_info.ProcessKernelTime); + EXPECT_EQ(kernel_time.tv_usec, 0); +} + +TEST(ProcessSnapshotMinidump, Threads) { + StringFile string_file; + + MINIDUMP_HEADER header = {}; + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + MINIDUMP_THREAD minidump_thread = {}; + uint32_t minidump_thread_count = 4; + + minidump_thread.ThreadId = 42; + minidump_thread.Teb = 24; + + MINIDUMP_DIRECTORY minidump_thread_list_directory = {}; + minidump_thread_list_directory.StreamType = kMinidumpStreamTypeThreadList; + minidump_thread_list_directory.Location.DataSize = + sizeof(MINIDUMP_THREAD_LIST) + + minidump_thread_count * sizeof(MINIDUMP_THREAD); + minidump_thread_list_directory.Location.Rva = + static_cast(string_file.SeekGet()); + + // Fields in MINIDUMP_THREAD_LIST. + EXPECT_TRUE( + string_file.Write(&minidump_thread_count, sizeof(minidump_thread_count))); + for (uint32_t minidump_thread_index = 0; + minidump_thread_index < minidump_thread_count; + ++minidump_thread_index) { + EXPECT_TRUE(string_file.Write(&minidump_thread, sizeof(minidump_thread))); + minidump_thread.ThreadId++; + } + + header.StreamDirectoryRva = static_cast(string_file.SeekGet()); + EXPECT_TRUE(string_file.Write(&minidump_thread_list_directory, + sizeof(minidump_thread_list_directory))); + + header.Signature = MINIDUMP_SIGNATURE; + header.Version = MINIDUMP_VERSION; + header.NumberOfStreams = 1; + EXPECT_TRUE(string_file.SeekSet(0)); + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + ProcessSnapshotMinidump process_snapshot; + EXPECT_TRUE(process_snapshot.Initialize(&string_file)); + + std::vector threads = process_snapshot.Threads(); + ASSERT_EQ(threads.size(), minidump_thread_count); + + uint32_t thread_id = 42; + for (const auto& thread : threads) { + EXPECT_EQ(thread->ThreadID(), thread_id); + EXPECT_EQ(thread->ThreadSpecificDataAddress(), 24UL); + thread_id++; + } +} + +TEST(ProcessSnapshotMinidump, System) { + const char* cpu_info = "GenuineIntel"; + const uint32_t* cpu_info_bytes = reinterpret_cast(cpu_info); + StringFile string_file; + + MINIDUMP_HEADER header = {}; + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + MINIDUMP_SYSTEM_INFO minidump_system_info = {}; + + minidump_system_info.ProcessorArchitecture = kMinidumpCPUArchitectureX86; + minidump_system_info.ProcessorLevel = 3; + minidump_system_info.ProcessorRevision = 3; + minidump_system_info.NumberOfProcessors = 8; + minidump_system_info.ProductType = kMinidumpOSTypeServer; + minidump_system_info.PlatformId = kMinidumpOSFuchsia; + minidump_system_info.MajorVersion = 3; + minidump_system_info.MinorVersion = 4; + minidump_system_info.BuildNumber = 56; + minidump_system_info.CSDVersionRva = WriteString(&string_file, "Snazzle"); + minidump_system_info.Cpu.X86CpuInfo.VendorId[0] = cpu_info_bytes[0]; + minidump_system_info.Cpu.X86CpuInfo.VendorId[1] = cpu_info_bytes[1]; + minidump_system_info.Cpu.X86CpuInfo.VendorId[2] = cpu_info_bytes[2]; + + MINIDUMP_MISC_INFO_5 minidump_misc_info = {}; + std::u16string build_string; + ASSERT_TRUE(base::UTF8ToUTF16( + "MyOSVersion; MyMachineDescription", 33, &build_string)); + std::copy(build_string.begin(), build_string.end(), + minidump_misc_info.BuildString); + + MINIDUMP_DIRECTORY minidump_system_info_directory = {}; + minidump_system_info_directory.StreamType = kMinidumpStreamTypeSystemInfo; + minidump_system_info_directory.Location.DataSize = + sizeof(MINIDUMP_SYSTEM_INFO); + minidump_system_info_directory.Location.Rva = + static_cast(string_file.SeekGet()); + + ASSERT_TRUE( + string_file.Write(&minidump_system_info, sizeof(minidump_system_info))); + + MINIDUMP_DIRECTORY minidump_misc_info_directory = {}; + minidump_misc_info_directory.StreamType = kMinidumpStreamTypeMiscInfo; + minidump_misc_info_directory.Location.DataSize = sizeof(MINIDUMP_MISC_INFO_5); + minidump_misc_info_directory.Location.Rva = + static_cast(string_file.SeekGet()); + + ASSERT_TRUE( + string_file.Write(&minidump_misc_info, sizeof(minidump_misc_info))); + + header.StreamDirectoryRva = static_cast(string_file.SeekGet()); + ASSERT_TRUE(string_file.Write(&minidump_system_info_directory, + sizeof(minidump_system_info_directory))); + ASSERT_TRUE(string_file.Write(&minidump_misc_info_directory, + sizeof(minidump_misc_info_directory))); + + header.Signature = MINIDUMP_SIGNATURE; + header.Version = MINIDUMP_VERSION; + header.NumberOfStreams = 2; + EXPECT_TRUE(string_file.SeekSet(0)); + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + ProcessSnapshotMinidump process_snapshot; + EXPECT_TRUE(process_snapshot.Initialize(&string_file)); + + const SystemSnapshot* s = process_snapshot.System(); + + EXPECT_EQ(s->GetCPUArchitecture(), kCPUArchitectureX86); + EXPECT_EQ(s->CPURevision(), 3UL); + EXPECT_EQ(s->CPUVendor(), "GenuineIntel"); + EXPECT_EQ(s->GetOperatingSystem(), + SystemSnapshot::OperatingSystem::kOperatingSystemFuchsia); + EXPECT_EQ(s->OSVersionFull(), "MyOSVersion"); + + int major, minor, bugfix; + std::string build; + s->OSVersion(&major, &minor, &bugfix, &build); + + EXPECT_EQ(major, 3); + EXPECT_EQ(minor, 4); + EXPECT_EQ(bugfix, 56); + EXPECT_EQ(build, "Snazzle"); +} + +TEST(ProcessSnapshotMinidump, ThreadContextARM64) { + StringFile string_file; + + MINIDUMP_HEADER header = {}; + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + MINIDUMP_SYSTEM_INFO minidump_system_info = {}; + + minidump_system_info.ProcessorArchitecture = kMinidumpCPUArchitectureARM64; + minidump_system_info.ProductType = kMinidumpOSTypeServer; + minidump_system_info.PlatformId = kMinidumpOSFuchsia; + minidump_system_info.CSDVersionRva = WriteString(&string_file, ""); + + MINIDUMP_DIRECTORY minidump_system_info_directory = {}; + minidump_system_info_directory.StreamType = kMinidumpStreamTypeSystemInfo; + minidump_system_info_directory.Location.DataSize = + sizeof(MINIDUMP_SYSTEM_INFO); + minidump_system_info_directory.Location.Rva = + static_cast(string_file.SeekGet()); + + ASSERT_TRUE( + string_file.Write(&minidump_system_info, sizeof(minidump_system_info))); + + MINIDUMP_THREAD minidump_thread = {}; + uint32_t minidump_thread_count = 1; + + minidump_thread.ThreadId = 42; + minidump_thread.Teb = 24; + + MinidumpContextARM64 minidump_context = GetArm64MinidumpContext(); + + minidump_thread.ThreadContext.DataSize = sizeof(minidump_context); + minidump_thread.ThreadContext.Rva = static_cast(string_file.SeekGet()); + + EXPECT_TRUE(string_file.Write(&minidump_context, sizeof(minidump_context))); + + MINIDUMP_DIRECTORY minidump_thread_list_directory = {}; + minidump_thread_list_directory.StreamType = kMinidumpStreamTypeThreadList; + minidump_thread_list_directory.Location.DataSize = + sizeof(MINIDUMP_THREAD_LIST) + + minidump_thread_count * sizeof(MINIDUMP_THREAD); + minidump_thread_list_directory.Location.Rva = + static_cast(string_file.SeekGet()); + + // Fields in MINIDUMP_THREAD_LIST. + EXPECT_TRUE( + string_file.Write(&minidump_thread_count, sizeof(minidump_thread_count))); + EXPECT_TRUE(string_file.Write(&minidump_thread, sizeof(minidump_thread))); + + header.StreamDirectoryRva = static_cast(string_file.SeekGet()); + ASSERT_TRUE(string_file.Write(&minidump_system_info_directory, + sizeof(minidump_system_info_directory))); + ASSERT_TRUE(string_file.Write(&minidump_thread_list_directory, + sizeof(minidump_thread_list_directory))); + + header.Signature = MINIDUMP_SIGNATURE; + header.Version = MINIDUMP_VERSION; + header.NumberOfStreams = 2; + EXPECT_TRUE(string_file.SeekSet(0)); + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + ProcessSnapshotMinidump process_snapshot; + EXPECT_TRUE(process_snapshot.Initialize(&string_file)); + + std::vector threads = process_snapshot.Threads(); + ASSERT_EQ(threads.size(), minidump_thread_count); + + const CPUContext* ctx_generic = threads[0]->Context(); + + ASSERT_EQ(ctx_generic->architecture, CPUArchitecture::kCPUArchitectureARM64); + + const CPUContextARM64* ctx = ctx_generic->arm64; + + EXPECT_EQ(ctx->spsr, 0UL); + + for (unsigned int i = 0; i < 31; i++) { + EXPECT_EQ(ctx->regs[i], i + 1); + } + + EXPECT_EQ(ctx->sp, 32UL); + EXPECT_EQ(ctx->pc, 33UL); + EXPECT_EQ(ctx->fpcr, 98UL); + EXPECT_EQ(ctx->fpsr, 99UL); + + for (unsigned int i = 0; i < 32; i++) { + EXPECT_EQ(ctx->fpsimd[i].lo, i * 2 + 34); + EXPECT_EQ(ctx->fpsimd[i].hi, i * 2 + 35); + } +} + +TEST(ProcessSnapshotMinidump, ThreadContextX86_64) { + StringFile string_file; + + MINIDUMP_HEADER header = {}; + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + MINIDUMP_SYSTEM_INFO minidump_system_info = {}; + + minidump_system_info.ProcessorArchitecture = kMinidumpCPUArchitectureAMD64; + minidump_system_info.ProductType = kMinidumpOSTypeServer; + minidump_system_info.PlatformId = kMinidumpOSFuchsia; + minidump_system_info.CSDVersionRva = WriteString(&string_file, ""); + + MINIDUMP_DIRECTORY minidump_system_info_directory = {}; + minidump_system_info_directory.StreamType = kMinidumpStreamTypeSystemInfo; + minidump_system_info_directory.Location.DataSize = + sizeof(MINIDUMP_SYSTEM_INFO); + minidump_system_info_directory.Location.Rva = + static_cast(string_file.SeekGet()); + + ASSERT_TRUE( + string_file.Write(&minidump_system_info, sizeof(minidump_system_info))); + + MINIDUMP_THREAD minidump_thread = {}; + uint32_t minidump_thread_count = 1; + + minidump_thread.ThreadId = 42; + minidump_thread.Teb = 24; + + MinidumpContextAMD64 minidump_context; + + minidump_context.context_flags = kMinidumpContextAMD64Full; + + minidump_context.mx_csr = 0; + minidump_context.cs = 1; + minidump_context.ds = 2; + minidump_context.es = 3; + minidump_context.fs = 4; + minidump_context.gs = 5; + minidump_context.ss = 6; + minidump_context.eflags = 7; + minidump_context.dr0 = 8; + minidump_context.dr1 = 9; + minidump_context.dr2 = 10; + minidump_context.dr3 = 11; + minidump_context.dr6 = 12; + minidump_context.dr7 = 13; + minidump_context.rax = 14; + minidump_context.rcx = 15; + minidump_context.rdx = 16; + minidump_context.rbx = 17; + minidump_context.rsp = 18; + minidump_context.rbp = 19; + minidump_context.rsi = 20; + minidump_context.rdi = 21; + minidump_context.r8 = 22; + minidump_context.r9 = 23; + minidump_context.r10 = 24; + minidump_context.r11 = 25; + minidump_context.r12 = 26; + minidump_context.r13 = 27; + minidump_context.r14 = 28; + minidump_context.r15 = 29; + minidump_context.rip = 30; + minidump_context.vector_control = 31; + minidump_context.debug_control = 32; + minidump_context.last_branch_to_rip = 33; + minidump_context.last_branch_from_rip = 34; + minidump_context.last_exception_to_rip = 35; + minidump_context.last_exception_from_rip = 36; + minidump_context.fxsave.fcw = 37; + minidump_context.fxsave.fsw = 38; + minidump_context.fxsave.ftw = 39; + minidump_context.fxsave.reserved_1 = 40; + minidump_context.fxsave.fop = 41; + minidump_context.fxsave.fpu_ip_64 = 42; + minidump_context.fxsave.fpu_dp_64 = 43; + + for (size_t i = 0; i < std::size(minidump_context.vector_register); i++) { + minidump_context.vector_register[i].lo = i * 2 + 44; + minidump_context.vector_register[i].hi = i * 2 + 45; + } + + for (uint8_t i = 0; i < std::size(minidump_context.fxsave.reserved_4); i++) { + minidump_context.fxsave.reserved_4[i] = i * 2 + 115; + minidump_context.fxsave.available[i] = i * 2 + 116; + } + + for (size_t i = 0; i < std::size(minidump_context.fxsave.st_mm); i++) { + for (uint8_t j = 0; + j < std::size(minidump_context.fxsave.st_mm[0].mm_value); + j++) { + minidump_context.fxsave.st_mm[i].mm_value[j] = j + 1; + minidump_context.fxsave.st_mm[i].mm_reserved[j] = j + 1; + } + } + + for (size_t i = 0; i < std::size(minidump_context.fxsave.xmm); i++) { + for (uint8_t j = 0; j < std::size(minidump_context.fxsave.xmm[0]); j++) { + minidump_context.fxsave.xmm[i][j] = j + 1; + } + } + + minidump_thread.ThreadContext.DataSize = sizeof(minidump_context); + minidump_thread.ThreadContext.Rva = static_cast(string_file.SeekGet()); + + EXPECT_TRUE(string_file.Write(&minidump_context, sizeof(minidump_context))); + + MINIDUMP_DIRECTORY minidump_thread_list_directory = {}; + minidump_thread_list_directory.StreamType = kMinidumpStreamTypeThreadList; + minidump_thread_list_directory.Location.DataSize = + sizeof(MINIDUMP_THREAD_LIST) + + minidump_thread_count * sizeof(MINIDUMP_THREAD); + minidump_thread_list_directory.Location.Rva = + static_cast(string_file.SeekGet()); + + // Fields in MINIDUMP_THREAD_LIST. + EXPECT_TRUE( + string_file.Write(&minidump_thread_count, sizeof(minidump_thread_count))); + EXPECT_TRUE(string_file.Write(&minidump_thread, sizeof(minidump_thread))); + + header.StreamDirectoryRva = static_cast(string_file.SeekGet()); + ASSERT_TRUE(string_file.Write(&minidump_system_info_directory, + sizeof(minidump_system_info_directory))); + ASSERT_TRUE(string_file.Write(&minidump_thread_list_directory, + sizeof(minidump_thread_list_directory))); + + header.Signature = MINIDUMP_SIGNATURE; + header.Version = MINIDUMP_VERSION; + header.NumberOfStreams = 2; + EXPECT_TRUE(string_file.SeekSet(0)); + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + ProcessSnapshotMinidump process_snapshot; + EXPECT_TRUE(process_snapshot.Initialize(&string_file)); + + std::vector threads = process_snapshot.Threads(); + ASSERT_EQ(threads.size(), minidump_thread_count); + + const CPUContext* ctx_generic = threads[0]->Context(); + + ASSERT_EQ(ctx_generic->architecture, CPUArchitecture::kCPUArchitectureX86_64); + + const CPUContextX86_64* ctx = ctx_generic->x86_64; + EXPECT_EQ(ctx->cs, 1); + EXPECT_EQ(ctx->fs, 4); + EXPECT_EQ(ctx->gs, 5); + EXPECT_EQ(ctx->rflags, 7UL); + EXPECT_EQ(ctx->dr0, 8UL); + EXPECT_EQ(ctx->dr1, 9U); + EXPECT_EQ(ctx->dr2, 10U); + EXPECT_EQ(ctx->dr3, 11U); + EXPECT_EQ(ctx->dr4, 12U); + EXPECT_EQ(ctx->dr5, 13U); + EXPECT_EQ(ctx->dr6, 12U); + EXPECT_EQ(ctx->dr7, 13U); + EXPECT_EQ(ctx->rax, 14U); + EXPECT_EQ(ctx->rcx, 15U); + EXPECT_EQ(ctx->rdx, 16U); + EXPECT_EQ(ctx->rbx, 17U); + EXPECT_EQ(ctx->rsp, 18U); + EXPECT_EQ(ctx->rbp, 19U); + EXPECT_EQ(ctx->rsi, 20U); + EXPECT_EQ(ctx->rdi, 21U); + EXPECT_EQ(ctx->r8, 22U); + EXPECT_EQ(ctx->r9, 23U); + EXPECT_EQ(ctx->r10, 24U); + EXPECT_EQ(ctx->r11, 25U); + EXPECT_EQ(ctx->r12, 26U); + EXPECT_EQ(ctx->r13, 27U); + EXPECT_EQ(ctx->r14, 28U); + EXPECT_EQ(ctx->r15, 29U); + EXPECT_EQ(ctx->rip, 30U); + EXPECT_EQ(ctx->fxsave.fcw, 37U); + EXPECT_EQ(ctx->fxsave.fsw, 38U); + EXPECT_EQ(ctx->fxsave.ftw, 39U); + EXPECT_EQ(ctx->fxsave.reserved_1, 40U); + EXPECT_EQ(ctx->fxsave.fop, 41U); + EXPECT_EQ(ctx->fxsave.fpu_ip_64, 42U); + EXPECT_EQ(ctx->fxsave.fpu_dp_64, 43U); + + for (uint8_t i = 0; i < std::size(ctx->fxsave.reserved_4); i++) { + EXPECT_EQ(ctx->fxsave.reserved_4[i], i * 2 + 115); + EXPECT_EQ(ctx->fxsave.available[i], i * 2 + 116); + } + + for (size_t i = 0; i < std::size(ctx->fxsave.st_mm); i++) { + for (uint8_t j = 0; j < std::size(ctx->fxsave.st_mm[0].mm_value); j++) { + EXPECT_EQ(ctx->fxsave.st_mm[i].mm_value[j], j + 1); + EXPECT_EQ(ctx->fxsave.st_mm[i].mm_reserved[j], j + 1); + } + } + + for (size_t i = 0; i < std::size(ctx->fxsave.xmm); i++) { + for (uint8_t j = 0; j < std::size(ctx->fxsave.xmm[0]); j++) { + EXPECT_EQ(ctx->fxsave.xmm[i][j], j + 1); + } + } +} + +TEST(ProcessSnapshotMinidump, MemoryMap) { + StringFile string_file; + + MINIDUMP_HEADER header = {}; + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + MINIDUMP_MEMORY_INFO minidump_memory_info_1 = {}; + MINIDUMP_MEMORY_INFO minidump_memory_info_2 = {}; + uint32_t minidump_memory_info_count = 2; + + minidump_memory_info_1.BaseAddress = 1; + minidump_memory_info_1.AllocationBase = 2; + minidump_memory_info_1.AllocationProtect = 3; + minidump_memory_info_1.RegionSize = 4; + minidump_memory_info_1.State = 5; + minidump_memory_info_1.Protect = 6; + minidump_memory_info_1.Type = 6; + + minidump_memory_info_2.BaseAddress = 7; + minidump_memory_info_2.AllocationBase = 8; + minidump_memory_info_2.AllocationProtect = 9; + minidump_memory_info_2.RegionSize = 10; + minidump_memory_info_2.State = 11; + minidump_memory_info_2.Protect = 12; + minidump_memory_info_2.Type = 13; + + MINIDUMP_MEMORY_INFO_LIST minidump_memory_info_list = {}; + + minidump_memory_info_list.SizeOfHeader = sizeof(minidump_memory_info_list); + minidump_memory_info_list.SizeOfEntry = sizeof(MINIDUMP_MEMORY_INFO); + minidump_memory_info_list.NumberOfEntries = minidump_memory_info_count; + + MINIDUMP_DIRECTORY minidump_memory_info_list_directory = {}; + minidump_memory_info_list_directory.StreamType = + kMinidumpStreamTypeMemoryInfoList; + minidump_memory_info_list_directory.Location.DataSize = + sizeof(minidump_memory_info_list) + + minidump_memory_info_count * sizeof(MINIDUMP_MEMORY_INFO); + minidump_memory_info_list_directory.Location.Rva = + static_cast(string_file.SeekGet()); + + EXPECT_TRUE(string_file.Write(&minidump_memory_info_list, + sizeof(minidump_memory_info_list))); + EXPECT_TRUE(string_file.Write(&minidump_memory_info_1, + sizeof(minidump_memory_info_1))); + EXPECT_TRUE(string_file.Write(&minidump_memory_info_2, + sizeof(minidump_memory_info_2))); + + header.StreamDirectoryRva = static_cast(string_file.SeekGet()); + EXPECT_TRUE(string_file.Write(&minidump_memory_info_list_directory, + sizeof(minidump_memory_info_list_directory))); + + header.Signature = MINIDUMP_SIGNATURE; + header.Version = MINIDUMP_VERSION; + header.NumberOfStreams = 1; + EXPECT_TRUE(string_file.SeekSet(0)); + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + ProcessSnapshotMinidump process_snapshot; + EXPECT_TRUE(process_snapshot.Initialize(&string_file)); + + std::vector map = + process_snapshot.MemoryMap(); + ASSERT_EQ(map.size(), minidump_memory_info_count); + EXPECT_EQ(memcmp(&map[0]->AsMinidumpMemoryInfo(), + &minidump_memory_info_1, + sizeof(minidump_memory_info_1)), + 0); + EXPECT_EQ(memcmp(&map[1]->AsMinidumpMemoryInfo(), + &minidump_memory_info_2, + sizeof(minidump_memory_info_2)), + 0); +} + +TEST(ProcessSnapshotMinidump, Stacks) { + StringFile string_file; + + MINIDUMP_HEADER header = {}; + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + MINIDUMP_THREAD minidump_thread = {}; + uint32_t minidump_thread_count = 1; + + minidump_thread.ThreadId = 42; + minidump_thread.Stack.StartOfMemoryRange = 0xbeefd00d; + + std::vector minidump_stack = {'1', + '2', + '3', + '4', + '5', + '6', + '7', + '8', + '9', + 'a', + 'b', + 'c', + 'd', + 'e', + 'f'}; + + minidump_thread.Stack.Memory.DataSize = + base::checked_cast(minidump_stack.size()); + minidump_thread.Stack.Memory.Rva = static_cast(string_file.SeekGet()); + + EXPECT_TRUE(string_file.Write(minidump_stack.data(), minidump_stack.size())); + + MINIDUMP_DIRECTORY minidump_thread_list_directory = {}; + minidump_thread_list_directory.StreamType = kMinidumpStreamTypeThreadList; + minidump_thread_list_directory.Location.DataSize = + sizeof(MINIDUMP_THREAD_LIST) + + minidump_thread_count * sizeof(MINIDUMP_THREAD); + minidump_thread_list_directory.Location.Rva = + static_cast(string_file.SeekGet()); + + // Fields in MINIDUMP_THREAD_LIST. + EXPECT_TRUE( + string_file.Write(&minidump_thread_count, sizeof(minidump_thread_count))); + EXPECT_TRUE(string_file.Write(&minidump_thread, sizeof(minidump_thread))); + + header.StreamDirectoryRva = static_cast(string_file.SeekGet()); + ASSERT_TRUE(string_file.Write(&minidump_thread_list_directory, + sizeof(minidump_thread_list_directory))); + + header.Signature = MINIDUMP_SIGNATURE; + header.Version = MINIDUMP_VERSION; + header.NumberOfStreams = 1; + EXPECT_TRUE(string_file.SeekSet(0)); + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + ProcessSnapshotMinidump process_snapshot; + EXPECT_TRUE(process_snapshot.Initialize(&string_file)); + + std::vector threads = process_snapshot.Threads(); + ASSERT_EQ(threads.size(), minidump_thread_count); + + ReadToVector delegate; + threads[0]->Stack()->Read(&delegate); + + EXPECT_EQ(delegate.result, minidump_stack); +} + +TEST(ProcessSnapshotMinidump, CustomMinidumpStreams) { + StringFile string_file; + + MINIDUMP_HEADER header = {}; + ASSERT_TRUE(string_file.Write(&header, sizeof(header))); + + static const char kStreamReservedData[] = "A string"; + static const char kStreamUnreservedData[] = "Another string"; + // In the minidump reserved range + constexpr MinidumpStreamType kStreamTypeReserved1 = + (MinidumpStreamType)0x1111; + // In the crashpad reserved range + constexpr MinidumpStreamType kStreamTypeReserved2 = + (MinidumpStreamType)0x43501111; + constexpr MinidumpStreamType kStreamTypeUnreserved = + (MinidumpStreamType)0xffffffff; + + MINIDUMP_DIRECTORY misc_directory = {}; + RVA reserved1_offset = static_cast(string_file.SeekGet()); + ASSERT_TRUE( + string_file.Write(kStreamReservedData, sizeof(kStreamReservedData))); + RVA reserved2_offset = static_cast(string_file.SeekGet()); + ASSERT_TRUE( + string_file.Write(kStreamReservedData, sizeof(kStreamReservedData))); + RVA unreserved_offset = static_cast(string_file.SeekGet()); + ASSERT_TRUE( + string_file.Write(kStreamUnreservedData, sizeof(kStreamUnreservedData))); + + header.StreamDirectoryRva = static_cast(string_file.SeekGet()); + misc_directory.StreamType = kStreamTypeReserved1; + misc_directory.Location.DataSize = sizeof(kStreamReservedData); + misc_directory.Location.Rva = reserved1_offset; + ASSERT_TRUE(string_file.Write(&misc_directory, sizeof(misc_directory))); + + misc_directory.StreamType = kStreamTypeReserved2; + misc_directory.Location.DataSize = sizeof(kStreamReservedData); + misc_directory.Location.Rva = reserved2_offset; + ASSERT_TRUE(string_file.Write(&misc_directory, sizeof(misc_directory))); + + misc_directory.StreamType = kStreamTypeUnreserved; + misc_directory.Location.DataSize = sizeof(kStreamUnreservedData); + misc_directory.Location.Rva = unreserved_offset; + ASSERT_TRUE(string_file.Write(&misc_directory, sizeof(misc_directory))); + + header.Signature = MINIDUMP_SIGNATURE; + header.Version = MINIDUMP_VERSION; + header.NumberOfStreams = 3; + ASSERT_TRUE(string_file.SeekSet(0)); + ASSERT_TRUE(string_file.Write(&header, sizeof(header))); + + ProcessSnapshotMinidump process_snapshot; + ASSERT_TRUE(process_snapshot.Initialize(&string_file)); + + auto custom_streams = process_snapshot.CustomMinidumpStreams(); + ASSERT_EQ(custom_streams.size(), 1U); + EXPECT_EQ(custom_streams[0]->stream_type(), (uint32_t)kStreamTypeUnreserved); + + auto stream_data = custom_streams[0]->data(); + EXPECT_EQ(stream_data.size(), sizeof(kStreamUnreservedData)); + EXPECT_STREQ((char*)&stream_data.front(), kStreamUnreservedData); +} + +TEST(ProcessSnapshotMinidump, Exception) { + StringFile string_file; + + MINIDUMP_HEADER header = {}; + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + uint32_t exception_signo = + static_cast(-1); // crashpad::Signals::kSimulatedSigno + + MINIDUMP_EXCEPTION minidump_exception = {}; + minidump_exception.ExceptionCode = exception_signo; + minidump_exception.ExceptionFlags = 2; + minidump_exception.ExceptionRecord = 4; + minidump_exception.ExceptionAddress = 0xdeedb00f; + minidump_exception.NumberParameters = 2; + minidump_exception.ExceptionInformation[0] = 51; + minidump_exception.ExceptionInformation[1] = 62; + + MINIDUMP_SYSTEM_INFO minidump_system_info = {}; + + minidump_system_info.ProcessorArchitecture = kMinidumpCPUArchitectureARM64; + minidump_system_info.ProductType = kMinidumpOSTypeServer; + minidump_system_info.PlatformId = kMinidumpOSFuchsia; + minidump_system_info.CSDVersionRva = WriteString(&string_file, ""); + + MINIDUMP_DIRECTORY minidump_system_info_directory = {}; + minidump_system_info_directory.StreamType = kMinidumpStreamTypeSystemInfo; + minidump_system_info_directory.Location.DataSize = + sizeof(MINIDUMP_SYSTEM_INFO); + minidump_system_info_directory.Location.Rva = + static_cast(string_file.SeekGet()); + + ASSERT_TRUE( + string_file.Write(&minidump_system_info, sizeof(minidump_system_info))); + + MINIDUMP_EXCEPTION_STREAM minidump_exception_stream = {}; + minidump_exception_stream.ThreadId = 5; + minidump_exception_stream.ExceptionRecord = minidump_exception; + + MinidumpContextARM64 minidump_context = GetArm64MinidumpContext(); + + minidump_exception_stream.ThreadContext.DataSize = sizeof(minidump_context); + minidump_exception_stream.ThreadContext.Rva = + static_cast(string_file.SeekGet()); + + ASSERT_TRUE(string_file.Write(&minidump_context, sizeof(minidump_context))); + + MINIDUMP_DIRECTORY minidump_exception_directory = {}; + minidump_exception_directory.StreamType = kMinidumpStreamTypeException; + minidump_exception_directory.Location.DataSize = + sizeof(MINIDUMP_EXCEPTION_STREAM); + minidump_exception_directory.Location.Rva = + static_cast(string_file.SeekGet()); + + ASSERT_TRUE(string_file.Write(&minidump_exception_stream, + sizeof(minidump_exception_stream))); + + header.StreamDirectoryRva = static_cast(string_file.SeekGet()); + ASSERT_TRUE(string_file.Write(&minidump_exception_directory, + sizeof(minidump_exception_directory))); + ASSERT_TRUE(string_file.Write(&minidump_system_info_directory, + sizeof(minidump_system_info_directory))); + + header.Signature = MINIDUMP_SIGNATURE; + header.Version = MINIDUMP_VERSION; + header.NumberOfStreams = 2; + EXPECT_TRUE(string_file.SeekSet(0)); + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + ProcessSnapshotMinidump process_snapshot; + EXPECT_TRUE(process_snapshot.Initialize(&string_file)); + + const ExceptionSnapshot* s = process_snapshot.Exception(); + + EXPECT_EQ(s->ThreadID(), 5UL); + EXPECT_EQ(s->Exception(), exception_signo); + EXPECT_EQ(s->ExceptionInfo(), 2U); + EXPECT_EQ(s->ExceptionAddress(), 0xdeedb00f); + + const std::vector codes = s->Codes(); + EXPECT_EQ(codes.size(), 2UL); + EXPECT_EQ(codes[0], 51UL); + EXPECT_EQ(codes[1], 62UL); + + const CPUContext* ctx_generic = s->Context(); + + ASSERT_EQ(ctx_generic->architecture, CPUArchitecture::kCPUArchitectureARM64); + + const CPUContextARM64* ctx = ctx_generic->arm64; + + EXPECT_EQ(ctx->spsr, 0UL); + + for (unsigned int i = 0; i < 31; i++) { + EXPECT_EQ(ctx->regs[i], i + 1); + } + + EXPECT_EQ(ctx->sp, 32UL); + EXPECT_EQ(ctx->pc, 33UL); + EXPECT_EQ(ctx->fpcr, 98UL); + EXPECT_EQ(ctx->fpsr, 99UL); + + for (unsigned int i = 0; i < 32; i++) { + EXPECT_EQ(ctx->fpsimd[i].lo, i * 2 + 34); + EXPECT_EQ(ctx->fpsimd[i].hi, i * 2 + 35); + } +} + +TEST(ProcessSnapshotMinidump, NoExceptionInMinidump) { + StringFile string_file; + + MINIDUMP_HEADER header = {}; + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + header.Signature = MINIDUMP_SIGNATURE; + header.Version = MINIDUMP_VERSION; + header.NumberOfStreams = 0; + EXPECT_TRUE(string_file.SeekSet(0)); + EXPECT_TRUE(string_file.Write(&header, sizeof(header))); + + ProcessSnapshotMinidump process_snapshot; + EXPECT_TRUE(process_snapshot.Initialize(&string_file)); + + const ExceptionSnapshot* s = process_snapshot.Exception(); + EXPECT_EQ(s, nullptr); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/minidump/system_snapshot_minidump.cc b/shared/sentry/external/crashpad/snapshot/minidump/system_snapshot_minidump.cc new file mode 100644 index 000000000..e4b1b032d --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/minidump/system_snapshot_minidump.cc @@ -0,0 +1,199 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/minidump/system_snapshot_minidump.h" + +#include "base/notreached.h" +#include "snapshot/minidump/minidump_string_reader.h" + +namespace crashpad { +namespace internal { + +SystemSnapshotMinidump::SystemSnapshotMinidump() + : SystemSnapshot(), minidump_system_info_(), initialized_() {} + +SystemSnapshotMinidump::~SystemSnapshotMinidump() {} + +bool SystemSnapshotMinidump::Initialize(FileReaderInterface* file_reader, + RVA minidump_system_info_rva, + const std::string& version) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + full_version_ = version; + + if (!file_reader->SeekSet(minidump_system_info_rva)) { + return false; + } + + if (!file_reader->ReadExactly(&minidump_system_info_, + sizeof(minidump_system_info_))) { + return false; + } + + if (!ReadMinidumpUTF8String(file_reader, + minidump_system_info_.CSDVersionRva, + &minidump_build_name_)) { + return false; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +CPUArchitecture SystemSnapshotMinidump::GetCPUArchitecture() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + switch (minidump_system_info_.ProcessorArchitecture) { + case kMinidumpCPUArchitectureAMD64: + return kCPUArchitectureX86_64; + case kMinidumpCPUArchitectureX86: + case kMinidumpCPUArchitectureX86Win64: + return kCPUArchitectureX86; + case kMinidumpCPUArchitectureARM: + case kMinidumpCPUArchitectureARM32Win64: + return kCPUArchitectureARM; + case kMinidumpCPUArchitectureARM64: + case kMinidumpCPUArchitectureARM64Breakpad: + return kCPUArchitectureARM64; + case kMinidumpCPUArchitectureMIPS: + return kCPUArchitectureMIPSEL; + // No word on how MIPS64 is signalled + + default: + return CPUArchitecture::kCPUArchitectureUnknown; + } +} + +uint32_t SystemSnapshotMinidump::CPURevision() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return minidump_system_info_.ProcessorRevision; +} + +uint8_t SystemSnapshotMinidump::CPUCount() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return minidump_system_info_.NumberOfProcessors; +} + +std::string SystemSnapshotMinidump::CPUVendor() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (GetCPUArchitecture() == kCPUArchitectureX86) { + const char* ptr = reinterpret_cast( + minidump_system_info_.Cpu.X86CpuInfo.VendorId); + return std::string(ptr, ptr + (3 * sizeof(uint32_t))); + } else { + return std::string(); + } +} + +void SystemSnapshotMinidump::CPUFrequency(uint64_t* current_hz, + uint64_t* max_hz) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 +} + +uint32_t SystemSnapshotMinidump::CPUX86Signature() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return 0; +} + +uint64_t SystemSnapshotMinidump::CPUX86Features() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return 0; +} + +uint64_t SystemSnapshotMinidump::CPUX86ExtendedFeatures() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return 0; +} + +uint32_t SystemSnapshotMinidump::CPUX86Leaf7Features() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return 0; +} + +bool SystemSnapshotMinidump::CPUX86SupportsDAZ() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return false; +} + +SystemSnapshot::OperatingSystem SystemSnapshotMinidump::GetOperatingSystem() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + switch (minidump_system_info_.PlatformId) { + case kMinidumpOSMacOSX: + return OperatingSystem::kOperatingSystemMacOSX; + case kMinidumpOSWin32s: + case kMinidumpOSWin32Windows: + case kMinidumpOSWin32NT: + return OperatingSystem::kOperatingSystemWindows; + case kMinidumpOSLinux: + return OperatingSystem::kOperatingSystemLinux; + case kMinidumpOSAndroid: + return OperatingSystem::kOperatingSystemAndroid; + case kMinidumpOSFuchsia: + return OperatingSystem::kOperatingSystemFuchsia; + default: + return OperatingSystem::kOperatingSystemUnknown; + } +} + +bool SystemSnapshotMinidump::OSServer() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return minidump_system_info_.ProductType == kMinidumpOSTypeServer; +} + +void SystemSnapshotMinidump::OSVersion(int* major, + int* minor, + int* bugfix, + std::string* build) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *major = minidump_system_info_.MajorVersion; + *minor = minidump_system_info_.MinorVersion; + *bugfix = minidump_system_info_.BuildNumber; + *build = minidump_build_name_; +} + +std::string SystemSnapshotMinidump::OSVersionFull() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return full_version_; +} + +std::string SystemSnapshotMinidump::MachineDescription() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return std::string(); +} + +bool SystemSnapshotMinidump::NXEnabled() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 + return false; +} + +void SystemSnapshotMinidump::TimeZone(DaylightSavingTimeStatus* dst_status, + int* standard_offset_seconds, + int* daylight_offset_seconds, + std::string* standard_name, + std::string* daylight_name) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + NOTREACHED(); // https://crashpad.chromium.org/bug/10 +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/minidump/system_snapshot_minidump.h b/shared/sentry/external/crashpad/snapshot/minidump/system_snapshot_minidump.h new file mode 100644 index 000000000..8a7ad1f1f --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/minidump/system_snapshot_minidump.h @@ -0,0 +1,88 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_SYSTEM_SNAPSHOT_MINIDUMP_H_ +#define CRASHPAD_SNAPSHOT_MINIDUMP_SYSTEM_SNAPSHOT_MINIDUMP_H_ + +#include + +#include "minidump/minidump_extensions.h" +#include "snapshot/system_snapshot.h" +#include "util/file/file_reader.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { +namespace internal { + +//! \brief A SystemSnapshot based on a minidump file. +class SystemSnapshotMinidump : public SystemSnapshot { + public: + SystemSnapshotMinidump(); + + SystemSnapshotMinidump(const SystemSnapshotMinidump&) = delete; + SystemSnapshotMinidump& operator=(const SystemSnapshotMinidump&) = delete; + + ~SystemSnapshotMinidump() override; + + //! \brief Initializes the object. + //! + //! \param[in] file_reader A file reader corresponding to a minidump file. + //! The file reader must support seeking. + //! \param[in] minidump_system_info_rva The file offset in \a file_reader at + //! which the thread’s MINIDUMP_SYSTEM_INFO structure is located. + //! \param[in] version The OS version taken from the build string in + //! MINIDUMP_MISC_INFO_4. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(FileReaderInterface* file_reader, + RVA minidump_system_info_rva, + const std::string& version); + + CPUArchitecture GetCPUArchitecture() const override; + uint32_t CPURevision() const override; + uint8_t CPUCount() const override; + std::string CPUVendor() const override; + void CPUFrequency(uint64_t* current_hz, uint64_t* max_hz) const override; + uint32_t CPUX86Signature() const override; + uint64_t CPUX86Features() const override; + uint64_t CPUX86ExtendedFeatures() const override; + uint32_t CPUX86Leaf7Features() const override; + bool CPUX86SupportsDAZ() const override; + OperatingSystem GetOperatingSystem() const override; + bool OSServer() const override; + void OSVersion(int* major, + int* minor, + int* bugfix, + std::string* build) const override; + std::string OSVersionFull() const override; + std::string MachineDescription() const override; + bool NXEnabled() const override; + void TimeZone(DaylightSavingTimeStatus* dst_status, + int* standard_offset_seconds, + int* daylight_offset_seconds, + std::string* standard_name, + std::string* daylight_name) const override; + + private: + MINIDUMP_SYSTEM_INFO minidump_system_info_; + std::string minidump_build_name_; + std::string full_version_; + InitializationStateDcheck initialized_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MINIDUMP_SYSTEM_SNAPSHOT_MINIDUMP_H_ diff --git a/shared/sentry/external/crashpad/snapshot/minidump/thread_snapshot_minidump.cc b/shared/sentry/external/crashpad/snapshot/minidump/thread_snapshot_minidump.cc new file mode 100644 index 000000000..f8f3673f4 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/minidump/thread_snapshot_minidump.cc @@ -0,0 +1,114 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/minidump/thread_snapshot_minidump.h" + +#include +#include + +#include "base/cxx17_backports.h" +#include "minidump/minidump_context.h" + +namespace crashpad { +namespace internal { + +ThreadSnapshotMinidump::ThreadSnapshotMinidump() + : ThreadSnapshot(), + minidump_thread_(), + context_(), + stack_(), + initialized_() {} + +ThreadSnapshotMinidump::~ThreadSnapshotMinidump() {} + +bool ThreadSnapshotMinidump::Initialize(FileReaderInterface* file_reader, + RVA minidump_thread_rva, + CPUArchitecture arch) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + std::vector minidump_context; + + if (!file_reader->SeekSet(minidump_thread_rva)) { + return false; + } + + if (!file_reader->ReadExactly(&minidump_thread_, sizeof(minidump_thread_))) { + return false; + } + + if (!file_reader->SeekSet(minidump_thread_.ThreadContext.Rva)) { + return false; + } + + minidump_context.resize(minidump_thread_.ThreadContext.DataSize); + + if (!file_reader->ReadExactly(minidump_context.data(), + minidump_context.size())) { + return false; + } + + if (!context_.Initialize(arch, minidump_context)) { + return false; + } + + RVA stack_info_location = + minidump_thread_rva + offsetof(MINIDUMP_THREAD, Stack); + + if (!stack_.Initialize(file_reader, stack_info_location)) { + return false; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +uint64_t ThreadSnapshotMinidump::ThreadID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return minidump_thread_.ThreadId; +} + +int ThreadSnapshotMinidump::SuspendCount() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return minidump_thread_.SuspendCount; +} + +uint64_t ThreadSnapshotMinidump::ThreadSpecificDataAddress() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return minidump_thread_.Teb; +} + +int ThreadSnapshotMinidump::Priority() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return minidump_thread_.Priority; +} + +const CPUContext* ThreadSnapshotMinidump::Context() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return context_.Get(); +} + +const MemorySnapshot* ThreadSnapshotMinidump::Stack() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &stack_; +} + +std::vector ThreadSnapshotMinidump::ExtraMemory() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // This doesn't correspond to anything minidump can give us, with the + // exception of the BackingStore field in the MINIDUMP_THREAD_EX structure, + // which is only valid for IA-64. + return std::vector(); +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/minidump/thread_snapshot_minidump.h b/shared/sentry/external/crashpad/snapshot/minidump/thread_snapshot_minidump.h new file mode 100644 index 000000000..7efb18d71 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/minidump/thread_snapshot_minidump.h @@ -0,0 +1,82 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MINIDUMP_THREAD_SNAPSHOT_MINIDUMP_H_ +#define CRASHPAD_SNAPSHOT_MINIDUMP_THREAD_SNAPSHOT_MINIDUMP_H_ + +#include + +#include "minidump/minidump_extensions.h" +#include "snapshot/cpu_context.h" +#include "snapshot/minidump/memory_snapshot_minidump.h" +#include "snapshot/minidump/minidump_context_converter.h" +#include "snapshot/thread_snapshot.h" +#include "util/file/file_reader.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { +namespace internal { + +//! \brief A ThreadSnapshot based on a thread in a minidump file. +class ThreadSnapshotMinidump : public ThreadSnapshot { + public: + ThreadSnapshotMinidump(); + + ThreadSnapshotMinidump(const ThreadSnapshotMinidump&) = delete; + ThreadSnapshotMinidump& operator=(const ThreadSnapshotMinidump&) = delete; + + ~ThreadSnapshotMinidump() override; + + //! \brief Initializes the object. + //! + //! \param[in] file_reader A file reader corresponding to a minidump file. + //! The file reader must support seeking. + //! \param[in] minidump_thread_rva The file offset in \a file_reader at which + //! the thread’s MINIDUMP_THREAD structure is located. + //! \param[in] arch The architecture of the system this thread is running on. + //! Used to decode CPU Context. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(FileReaderInterface* file_reader, + RVA minidump_thread_rva, + CPUArchitecture arch); + + const CPUContext* Context() const override; + const MemorySnapshot* Stack() const override; + uint64_t ThreadID() const override; + int SuspendCount() const override; + int Priority() const override; + uint64_t ThreadSpecificDataAddress() const override; + std::vector ExtraMemory() const override; + + private: + //! \brief Initializes the CPU Context + //! + //! \param[in] minidump_context the raw bytes of the context data from the + //! minidump file. + //! + //! \return `true` if the context could be decoded without error. + bool InitializeContext(const std::vector& minidump_context); + + MINIDUMP_THREAD minidump_thread_; + MinidumpContextConverter context_; + MemorySnapshotMinidump stack_; + InitializationStateDcheck initialized_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MINIDUMP_THREAD_SNAPSHOT_MINIDUMP_H_ diff --git a/shared/sentry/external/crashpad/snapshot/module_snapshot.h b/shared/sentry/external/crashpad/snapshot/module_snapshot.h new file mode 100644 index 000000000..5f205cee7 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/module_snapshot.h @@ -0,0 +1,247 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_MODULE_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_MODULE_SNAPSHOT_H_ + +#include +#include + +#include +#include +#include +#include +#include + +#include "snapshot/annotation_snapshot.h" +#include "snapshot/memory_snapshot.h" +#include "util/misc/uuid.h" +#include "util/numeric/checked_range.h" + +namespace crashpad { + +class MemorySnapshot; + +//! \brief Information describing a custom user data stream in a minidump. +class UserMinidumpStream { + public: + //! \brief Constructs a UserMinidumpStream, takes ownership of \a memory. + UserMinidumpStream(uint32_t stream_type, MemorySnapshot* memory) + : memory_(memory), stream_type_(stream_type) {} + + UserMinidumpStream(const UserMinidumpStream&) = delete; + UserMinidumpStream& operator=(const UserMinidumpStream&) = delete; + + const MemorySnapshot* memory() const { return memory_.get(); } + uint32_t stream_type() const { return stream_type_; } + + private: + //! \brief The memory representing the custom minidump stream. + std::unique_ptr memory_; + + //! \brief The stream type that the minidump stream will be tagged with. + uint32_t stream_type_; +}; + +//! \brief An abstract interface to a snapshot representing a code module +//! (binary image) loaded into a snapshot process. +class ModuleSnapshot { + public: + virtual ~ModuleSnapshot() {} + + //! \brief A module’s type. + enum ModuleType { + //! \brief The module’s type is unknown. + kModuleTypeUnknown = 0, + + //! \brief The module is a main executable. + kModuleTypeExecutable, + + //! \brief The module is a shared library. + //! + //! \sa kModuleTypeLoadableModule + kModuleTypeSharedLibrary, + + //! \brief The module is a loadable module. + //! + //! On some platforms, loadable modules are distinguished from shared + //! libraries. On these platforms, a shared library is a module that another + //! module links against directly, and a loadable module is not. Loadable + //! modules tend to be binary plug-ins. + kModuleTypeLoadableModule, + + //! \brief The module is a dynamic loader. + //! + //! This is the module responsible for loading other modules. This is + //! normally `dyld` for macOS and `ld.so` for Linux and other systems using + //! ELF. + kModuleTypeDynamicLoader, + }; + + //! \brief Returns the module’s pathname. + virtual std::string Name() const = 0; + + //! \brief Returns the base address that the module is loaded at in the + //! snapshot process. + virtual uint64_t Address() const = 0; + + //! \brief Returns the size that the module occupies in the snapshot process’ + //! address space, starting at its base address. + //! + //! For macOS snapshots, this method only reports the size of the `__TEXT` + //! segment, because segments may not be loaded contiguously. + virtual uint64_t Size() const = 0; + + //! \brief Returns the module’s timestamp, if known. + //! + //! The timestamp is typically the modification time of the file that provided + //! the module in `time_t` format, seconds since the POSIX epoch. If the + //! module’s timestamp is unknown, this method returns `0`. + virtual time_t Timestamp() const = 0; + + //! \brief Returns the module’s file version in the \a version_* parameters. + //! + //! If no file version can be determined, the \a version_* parameters are set + //! to `0`. + //! + //! For macOS snapshots, this is taken from the module’s `LC_ID_DYLIB` load + //! command for shared libraries, and is `0` for other module types. + virtual void FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const = 0; + + //! \brief Returns the module’s source version in the \a version_* parameters. + //! + //! If no source version can be determined, the \a version_* parameters are + //! set to `0`. + //! + //! For macOS snapshots, this is taken from the module’s `LC_SOURCE_VERSION` + //! load command. + virtual void SourceVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const = 0; + + //! \brief Returns the module’s type. + virtual ModuleType GetModuleType() const = 0; + + //! \brief Returns the module’s UUID in the \a uuid parameter, and the age of + //! that UUID in \a age. + //! + //! A snapshot module’s UUID is taken directly from the module itself. If the + //! module does not have a UUID, the \a uuid parameter will be zeroed out. + //! + //! \a age is the number of times the UUID has been reused. This occurs on + //! Windows with incremental linking. On other platforms \a age will always be + //! `0`. + //! + //! \sa BuildID() + //! \sa DebugFileName() + virtual void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const = 0; + + //! \brief Returns the module’s debug file info name. + //! + //! On Windows, this references the PDB file, which contains symbol + //! information held separately from the module itself. On other platforms, + //! this is normally the basename of the module, because the debug info file’s + //! name is not relevant even in split-debug scenarios. + //! + //! \sa UUIDAndAge() + virtual std::string DebugFileName() const = 0; + + //! \brief Returns the module’s build ID. + //! + //! On ELF platforms, the build ID is a variable-length byte stream that + //! identifies a library uniquely, and is usually used to look up its debug + //! symbols when stored separately. This will return an empty vector if it is + //! unsupported. + //! + //! BuildID() and UUIDAndAge() are never available in the same place. When + //! UUIDAndAge() is unavailable, it will be filled out with the contents of + //! BuildID() (either 0-padded or truncated) and age will be zero. + //! + //! \sa UUIDAndAge() + virtual std::vector BuildID() const = 0; + + //! \brief Returns string annotations recorded in the module. + //! + //! This method retrieves annotations recorded in a module. These annotations + //! are intended for diagnostic use, including crash analysis. A module may + //! contain multiple annotations, so they are returned in a vector. + //! + //! For macOS snapshots, these annotations are found by interpreting the + //! module’s `__DATA,__crash_info` section as `crashreporter_annotations_t`. + //! System libraries using the crash reporter client interface may reference + //! annotations in this structure. Additional annotations messages may be + //! found in other locations, which may be module-specific. The dynamic linker + //! (`dyld`) can provide an annotation at its `_error_string` symbol. + //! + //! The annotations returned by this method do not duplicate those returned by + //! AnnotationsSimpleMap() or AnnotationObjects(). + virtual std::vector AnnotationsVector() const = 0; + + //! \brief Returns key-value string annotations recorded in the module. + //! + //! This method retrieves annotations recorded in a module. These annotations + //! are intended for diagnostic use, including crash analysis. “Simple + //! annotations†are structured as a sequence of key-value pairs, where all + //! keys and values are strings. These are referred to in Chrome as “crash + //! keys.†+ //! + //! For macOS snapshots, these annotations are found by interpreting the + //! `__DATA,crashpad_info` section as `CrashpadInfo`. Clients can use the + //! Crashpad client interface to store annotations in this structure. Most + //! annotations under the client’s direct control will be retrievable by this + //! method. For clients such as Chrome, this includes the process type. + //! + //! The annotations returned by this method do not duplicate those returned by + //! AnnotationsVector() or AnnotationObjects(). Additional annotations related + //! to the process, system, or snapshot producer may be obtained by calling + //! ProcessSnapshot::AnnotationsSimpleMap(). + virtual std::map AnnotationsSimpleMap() const = 0; + + //! \brief Returns the typed annotation objects recorded in the module. + //! + //! This method retrieves annotations recorded in a module. These annotations + //! are intended for diagnostic use, including crash analysis. Annotation + //! objects are strongly-typed name-value pairs. The names are not unique. + //! + //! For macOS snapshots, these annotations are found by interpreting the + //! `__DATA,crashpad_info` section as `CrashpadInfo`. Clients can use the + //! Crashpad client interface to store annotations in this structure. Most + //! annotations under the client’s direct control will be retrievable by this + //! method. For clients such as Chrome, this includes the process type. + //! + //! The annotations returned by this method do not duplicate those returned by + //! AnnotationsVector() or AnnotationsSimpleMap(). + virtual std::vector AnnotationObjects() const = 0; + + //! \brief Returns a set of extra memory ranges specified in the module as + //! being desirable to include in the crash dump. + virtual std::set> ExtraMemoryRanges() const = 0; + + //! \brief Returns a list of custom minidump stream specified in the module to + //! be included in the crash dump. + //! + //! \return The caller does not take ownership of the returned objects, they + //! are scoped to the lifetime of the ModuleSnapshot object that they were + //! obtained from. + virtual std::vector CustomMinidumpStreams() + const = 0; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_MODULE_SNAPSHOT_H_ diff --git a/shared/sentry/external/crashpad/snapshot/posix/timezone.cc b/shared/sentry/external/crashpad/snapshot/posix/timezone.cc new file mode 100644 index 000000000..3591157e8 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/posix/timezone.cc @@ -0,0 +1,120 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/posix/timezone.h" + +#include +#include + +#include + +#include "base/check.h" +#include "base/logging.h" +#include "build/build_config.h" + +namespace crashpad { +namespace internal { + +void TimeZone(const timeval& snapshot_time, + SystemSnapshot::DaylightSavingTimeStatus* dst_status, + int* standard_offset_seconds, + int* daylight_offset_seconds, + std::string* standard_name, + std::string* daylight_name) { + tzset(); + + tm local; + PCHECK(localtime_r(&snapshot_time.tv_sec, &local)) << "localtime_r"; + + *standard_name = tzname[0]; + + bool found_transition = false; + long probe_gmtoff = local.tm_gmtoff; +#if BUILDFLAG(IS_ANDROID) + // Some versions of the timezone database on Android have incorrect + // information (e.g. Asia/Kolkata and Pacific/Honolulu). These timezones set + // daylight to a non-zero value and return incorrect, >= 0 values for tm_isdst + // in the probes below. If tzname[1] is set to a bogus value, assume the + // timezone does not actually use daylight saving time. + if (daylight && strncmp(tzname[1], "_TZif", 5) != 0) { +#else + if (daylight) { +#endif + // Scan forward and backward, one month at a time, looking for an instance + // when the observance of daylight saving time is different than it is in + // |local|. It’s possible that no such instance will be found even with + // |daylight| set. This can happen in locations where daylight saving time + // was once observed or is expected to be observed in the future, but where + // no transitions to or from daylight saving time occurred or will occur + // within a year of the current date. Arizona, which last observed daylight + // saving time in 1967, is an example. + static constexpr int kMonthDeltas[] = + {0, 1, -1, 2, -2, 3, -3, 4, -4, 5, -5, 6, -6, + 7, -7, 8, -8, 9, -9, 10, -10, 11, -11, 12, -12}; + for (size_t index = 0; index < std::size(kMonthDeltas) && !found_transition; + ++index) { + // Look at a day of each month at local noon. Set tm_isdst to -1 to avoid + // giving mktime() any hints about whether to consider daylight saving + // time in effect. mktime() accepts values of tm_mon that are outside of + // its normal range and behaves as expected: if tm_mon is -1, it + // references December of the preceding year, and if it is 12, it + // references January of the following year. + tm probe_tm = {}; + probe_tm.tm_hour = 12; + probe_tm.tm_mday = std::min(local.tm_mday, 28); + probe_tm.tm_mon = local.tm_mon + kMonthDeltas[index]; + probe_tm.tm_year = local.tm_year; + probe_tm.tm_isdst = -1; + if (mktime(&probe_tm) == -1) { + PLOG(WARNING) << "mktime"; + continue; + } + if (probe_tm.tm_isdst < 0 || local.tm_isdst < 0) { + LOG(WARNING) << "dst status not available"; + continue; + } + if (probe_tm.tm_isdst != local.tm_isdst) { + found_transition = true; + probe_gmtoff = probe_tm.tm_gmtoff; + } + } + } + + if (found_transition) { + *daylight_name = tzname[1]; + if (!local.tm_isdst) { + *dst_status = SystemSnapshot::kObservingStandardTime; + *standard_offset_seconds = local.tm_gmtoff; + *daylight_offset_seconds = probe_gmtoff; + } else { + *dst_status = SystemSnapshot::kObservingDaylightSavingTime; + *standard_offset_seconds = probe_gmtoff; + *daylight_offset_seconds = local.tm_gmtoff; + } + } else { + *daylight_name = tzname[0]; + *dst_status = SystemSnapshot::kDoesNotObserveDaylightSavingTime; +#if BUILDFLAG(IS_ANDROID) + // timezone is more reliably set correctly on Android. + *standard_offset_seconds = -timezone; + *daylight_offset_seconds = -timezone; +#else + *standard_offset_seconds = local.tm_gmtoff; + *daylight_offset_seconds = local.tm_gmtoff; +#endif // BUILDFLAG(IS_ANDROID) + } +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/posix/timezone.h b/shared/sentry/external/crashpad/snapshot/posix/timezone.h new file mode 100644 index 000000000..bd404a354 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/posix/timezone.h @@ -0,0 +1,55 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_POSIX_TIMEZONE_H_ +#define CRASHPAD_SNAPSHOT_POSIX_TIMEZONE_H_ + +#include + +#include + +#include "snapshot/system_snapshot.h" + +namespace crashpad { +namespace internal { + +//! \brief Returns time zone information from the snapshot system, based on +//! its locale configuration and \a snapshot_time. +//! +//! \param[in] snapshot_time The time to use collect daylight saving time status +//! for, given in time since Epoch. +//! \param[out] dst_status Whether the location observes daylight saving time, +//! and if so, whether it or standard time is currently being observed. +//! \param[out] standard_offset_seconds The number of seconds that the +//! location’s time zone is east (ahead) of UTC during standard time. +//! \param[out] daylight_offset_seconds The number of seconds that the +//! location’s time zone is east (ahead) of UTC during daylight saving. +//! time. +//! \param[out] standard_name The name of the time zone while standard time is +//! being observed. +//! \param[out] daylight_name The name of the time zone while daylight saving +//! time is being observed. +//! +//! \sa SystemSnapshot::TimeZone +void TimeZone(const timeval& snapshot_time, + SystemSnapshot::DaylightSavingTimeStatus* dst_status, + int* standard_offset_seconds, + int* daylight_offset_seconds, + std::string* standard_name, + std::string* daylight_name); + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_POSIX_TIMEZONE_H_ diff --git a/shared/sentry/external/crashpad/snapshot/posix/timezone_test.cc b/shared/sentry/external/crashpad/snapshot/posix/timezone_test.cc new file mode 100644 index 000000000..990a063eb --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/posix/timezone_test.cc @@ -0,0 +1,195 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/posix/timezone.h" + +#include +#include +#include + +#include +#include + +#include "base/strings/stringprintf.h" +#include "gtest/gtest.h" +#include "test/errors.h" + +namespace crashpad { +namespace test { +namespace { + +class ScopedSetTZ { + public: + ScopedSetTZ(const std::string& tz) { + const char* old_tz = getenv(kTZ); + old_tz_set_ = old_tz; + if (old_tz_set_) { + old_tz_.assign(old_tz); + } + + EXPECT_EQ(setenv(kTZ, tz.c_str(), 1), 0) << ErrnoMessage("setenv"); + tzset(); + } + + ScopedSetTZ(const ScopedSetTZ&) = delete; + ScopedSetTZ& operator=(const ScopedSetTZ&) = delete; + + ~ScopedSetTZ() { + if (old_tz_set_) { + EXPECT_EQ(setenv(kTZ, old_tz_.c_str(), 1), 0) << ErrnoMessage("setenv"); + } else { + EXPECT_EQ(unsetenv(kTZ), 0) << ErrnoMessage("unsetenv"); + } + tzset(); + } + + private: + std::string old_tz_; + bool old_tz_set_; + + static constexpr char kTZ[] = "TZ"; +}; + +constexpr char ScopedSetTZ::kTZ[]; + +TEST(TimeZone, Basic) { + SystemSnapshot::DaylightSavingTimeStatus dst_status; + int standard_offset_seconds; + int daylight_offset_seconds; + std::string standard_name; + std::string daylight_name; + + timeval snapshot_time; + ASSERT_EQ(gettimeofday(&snapshot_time, nullptr), 0); + + internal::TimeZone(snapshot_time, + &dst_status, + &standard_offset_seconds, + &daylight_offset_seconds, + &standard_name, + &daylight_name); + + // |standard_offset_seconds| gives seconds east of UTC, and |timezone| gives + // seconds west of UTC. + EXPECT_EQ(standard_offset_seconds, -timezone); + + // In contemporary usage, most time zones have an integer hour offset from + // UTC, although several are at a half-hour offset, and two are at 15-minute + // offsets. Throughout history, other variations existed. See + // https://www.timeanddate.com/time/time-zones-interesting.html. + EXPECT_EQ(standard_offset_seconds % (15 * 60), 0) + << "standard_offset_seconds " << standard_offset_seconds; + + if (dst_status == SystemSnapshot::kDoesNotObserveDaylightSavingTime) { + EXPECT_EQ(daylight_offset_seconds, standard_offset_seconds); + EXPECT_EQ(daylight_name, standard_name); + } else { + EXPECT_EQ(daylight_offset_seconds % (15 * 60), 0) + << "daylight_offset_seconds " << daylight_offset_seconds; + + // In contemporary usage, dst_delta_seconds will almost always be one hour, + // except for Lord Howe Island, Australia, which uses a 30-minute delta. + // Throughout history, other variations existed. See + // https://www.timeanddate.com/time/dst/. + int dst_delta_seconds = daylight_offset_seconds - standard_offset_seconds; + if (dst_delta_seconds != 60 * 60 && dst_delta_seconds != 30 * 60) { + FAIL() << "dst_delta_seconds " << dst_delta_seconds; + } + + EXPECT_NE(standard_name, daylight_name); + } + + // Test a variety of time zones. Some of these observe daylight saving time, + // some don’t. Some used to but no longer do. Some have uncommon UTC offsets. + // standard_name and daylight_name can be nullptr where no name exists to + // verify, as may happen when some versions of the timezone database carry + // invented names and others do not. + static constexpr struct { + const char* tz; + bool observes_dst; + float standard_offset_hours; + float daylight_offset_hours; + const char* standard_name; + const char* daylight_name; + } kTestTimeZones[] = { + {"America/Anchorage", true, -9, -8, "AKST", "AKDT"}, + {"America/Chicago", true, -6, -5, "CST", "CDT"}, + {"America/Denver", true, -7, -6, "MST", "MDT"}, + {"America/Halifax", true, -4, -3, "AST", "ADT"}, + {"America/Los_Angeles", true, -8, -7, "PST", "PDT"}, + {"America/New_York", true, -5, -4, "EST", "EDT"}, + {"America/Phoenix", false, -7, -7, "MST", "MST"}, + {"Asia/Karachi", false, 5, 5, "PKT", "PKT"}, + {"Asia/Kolkata", false, 5.5, 5.5, "IST", "IST"}, + {"Asia/Shanghai", false, 8, 8, "CST", "CST"}, + {"Asia/Tokyo", false, 9, 9, "JST", "JST"}, + + // Australian timezone names have an optional "A" prefix, which is + // present for glibc and macOS, but missing on Android. + {"Australia/Adelaide", true, 9.5, 10.5, nullptr, nullptr}, + {"Australia/Brisbane", false, 10, 10, nullptr, nullptr}, + {"Australia/Darwin", false, 9.5, 9.5, nullptr, nullptr}, + {"Australia/Eucla", false, 8.75, 8.75, nullptr, nullptr}, + {"Australia/Lord_Howe", true, 10.5, 11, nullptr, nullptr}, + {"Australia/Perth", false, 8, 8, nullptr, nullptr}, + {"Australia/Sydney", true, 10, 11, nullptr, nullptr}, + + {"Europe/Bucharest", true, 2, 3, "EET", "EEST"}, + {"Europe/London", true, 0, 1, "GMT", "BST"}, + {"Europe/Paris", true, 1, 2, "CET", "CEST"}, + {"Europe/Reykjavik", false, 0, 0, nullptr, nullptr}, + {"Pacific/Auckland", true, 12, 13, "NZST", "NZDT"}, + {"Pacific/Honolulu", false, -10, -10, "HST", "HST"}, + {"UTC", false, 0, 0, "UTC", "UTC"}, + }; + + for (size_t index = 0; index < std::size(kTestTimeZones); ++index) { + const auto& test_time_zone = kTestTimeZones[index]; + const char* tz = test_time_zone.tz; + SCOPED_TRACE(base::StringPrintf("index %zu, tz %s", index, tz)); + + { + ScopedSetTZ set_tz(tz); + internal::TimeZone(snapshot_time, + &dst_status, + &standard_offset_seconds, + &daylight_offset_seconds, + &standard_name, + &daylight_name); + } + + EXPECT_PRED2( + [](SystemSnapshot::DaylightSavingTimeStatus dst, bool observes) { + return (dst != SystemSnapshot::kDoesNotObserveDaylightSavingTime) == + observes; + }, + dst_status, + test_time_zone.observes_dst); + + EXPECT_EQ(standard_offset_seconds, + test_time_zone.standard_offset_hours * 60 * 60); + EXPECT_EQ(daylight_offset_seconds, + test_time_zone.daylight_offset_hours * 60 * 60); + if (test_time_zone.standard_name) { + EXPECT_EQ(standard_name, test_time_zone.standard_name); + } + if (test_time_zone.daylight_name) { + EXPECT_EQ(daylight_name, test_time_zone.daylight_name); + } + } +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/process_snapshot.h b/shared/sentry/external/crashpad/snapshot/process_snapshot.h new file mode 100644 index 000000000..08d9f2bc0 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/process_snapshot.h @@ -0,0 +1,210 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_PROCESS_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_PROCESS_SNAPSHOT_H_ + +#include +#include + +#include +#include +#include + +#include "snapshot/handle_snapshot.h" +#include "util/misc/uuid.h" +#include "util/process/process_id.h" + +namespace crashpad { + +class ExceptionSnapshot; +class MemoryMapRegionSnapshot; +class MemorySnapshot; +class ModuleSnapshot; +class ProcessMemory; +class SystemSnapshot; +class ThreadSnapshot; +class UnloadedModuleSnapshot; + +//! \brief An abstract interface to a snapshot representing the state of a +//! process. +//! +//! This is the top-level object in a family of Snapshot objects, because it +//! gives access to a SystemSnapshot, vectors of ModuleSnapshot and +//! ThreadSnapshot objects, and possibly an ExceptionSnapshot. In turn, +//! ThreadSnapshot and ExceptionSnapshot objects both give access to CPUContext +//! objects, and ThreadSnapshot objects also give access to MemorySnapshot +//! objects corresponding to thread stacks. +class ProcessSnapshot { + public: + virtual ~ProcessSnapshot() {} + + //! \brief Returns the snapshot process’ process ID. + virtual crashpad::ProcessID ProcessID() const = 0; + + //! \brief Returns the snapshot process’ parent process’ process ID. + virtual crashpad::ProcessID ParentProcessID() const = 0; + + //! \brief Returns the time that the snapshot was taken in \a snapshot_time. + //! + //! \param[out] snapshot_time The time that the snapshot was taken. This is + //! distinct from the time that a ProcessSnapshot object was created or + //! initialized, although it may be that time for ProcessSnapshot objects + //! representing live or recently-crashed process state. + virtual void SnapshotTime(timeval* snapshot_time) const = 0; + + //! \brief Returns the time that the snapshot process was started in \a + //! start_time. + //! + //! Normally, process uptime in wall clock time can be computed as + //! SnapshotTime() − ProcessStartTime(), but this cannot be guaranteed in + //! cases where the real-time clock has been set during the snapshot process’ + //! lifetime. + //! + //! \param[out] start_time The time that the process was started. + virtual void ProcessStartTime(timeval* start_time) const = 0; + + //! \brief Returns the snapshot process’ CPU usage times in \a user_time and + //! \a system_time. + //! + //! \param[out] user_time The time that the process has spent executing in + //! user mode. + //! \param[out] system_time The time that the process has spent executing in + //! system (kernel) mode. + virtual void ProcessCPUTimes(timeval* user_time, + timeval* system_time) const = 0; + + //! \brief Returns a %UUID identifying the event that the snapshot describes. + //! + //! This provides a stable identifier for a crash even as the report is + //! converted to different formats, provided that all formats support storing + //! a crash report ID. When a report is originally created, a report ID should + //! be assigned. From that point on, any operations involving the same report + //! should preserve the same report ID. + //! + //! If no identifier is available, this field will contain zeroes. + virtual void ReportID(UUID* client_id) const = 0; + + //! \brief Returns a %UUID identifying the client that the snapshot + //! represents. + //! + //! Client identification is within the scope of the application, but it is + //! expected that the identifier will be unique for an instance of Crashpad + //! monitoring an application or set of applications for a user. The + //! identifier shall remain stable over time. + //! + //! If no identifier is available, this field will contain zeroes. + virtual void ClientID(UUID* client_id) const = 0; + + //! \brief Returns key-value string annotations recorded for the process, + //! system, or snapshot producer. + //! + //! This method retrieves annotations recorded for a process. These + //! annotations are intended for diagnostic use, including crash analysis. + //! “Simple annotations†are structured as a sequence of key-value pairs, + //! where all keys and values are strings. These are referred to in Chrome as + //! “crash keys.†+ //! + //! Annotations stored here may reflect the process, system, or snapshot + //! producer. Most annotations not under the client’s direct control will be + //! retrievable by this method. For clients such as Chrome, this includes the + //! product name and version. + //! + //! Additional per-module annotations may be obtained by calling + //! ModuleSnapshot::AnnotationsSimpleMap(). + // + // This interface currently returns a const& because all implementations store + // the data within their objects in this format, and are therefore able to + // provide this access without requiring a copy. Future implementations may + // obtain data on demand or store data in a different format, at which point + // the cost of maintaining this data in ProcessSnapshot subclass objects will + // need to be taken into account, and this interface possibly revised. + virtual const std::map& AnnotationsSimpleMap() + const = 0; + + //! \brief Returns a SystemSnapshot reflecting the characteristics of the + //! system that ran the snapshot process at the time of the snapshot. + //! + //! \return A SystemSnapshot object. The caller does not take ownership of + //! this object, it is scoped to the lifetime of the ProcessSnapshot + //! object that it was obtained from. + virtual const SystemSnapshot* System() const = 0; + + //! \brief Returns ModuleSnapshot objects reflecting the code modules (binary + //! images) loaded into the snapshot process at the time of the snapshot. + //! + //! \return A vector of ModuleSnapshot objects. The caller does not take + //! ownership of these objects, they are scoped to the lifetime of the + //! ProcessSnapshot object that they were obtained from. + virtual std::vector Modules() const = 0; + + //! \brief Returns UnloadedModuleSnapshot objects reflecting the code modules + //! the were recorded as unloaded at the time of the snapshot. + //! + //! \return A vector of UnloadedModuleSnapshot objects. + virtual std::vector UnloadedModules() const = 0; + + //! \brief Returns ThreadSnapshot objects reflecting the threads (lightweight + //! processes) existing in the snapshot process at the time of the + //! snapshot. + //! + //! \return A vector of ThreadSnapshot objects. The caller does not take + //! ownership of these objects, they are scoped to the lifetime of the + //! ProcessSnapshot object that they were obtained from. + virtual std::vector Threads() const = 0; + + //! \brief Returns an ExceptionSnapshot reflecting the exception that the + //! snapshot process sustained to trigger the snapshot being taken. + //! + //! \return An ExceptionSnapshot object. The caller does not take ownership of + //! this object, it is scoped to the lifetime of the ProcessSnapshot + //! object that it was obtained from. If the snapshot is not a result of + //! an exception, returns `nullptr`. + virtual const ExceptionSnapshot* Exception() const = 0; + + //! \brief Returns MemoryMapRegionSnapshot objects reflecting the regions + //! of the memory map in the snapshot process at the time of the snapshot. + //! + //! \return A vector of MemoryMapRegionSnapshot objects. The caller does not + //! take ownership of these objects, they are scoped to the lifetime of + //! the ProcessSnapshot object that they were obtained from. + virtual std::vector MemoryMap() const = 0; + + //! \brief Returns HandleSnapshot objects reflecting the open handles in the + //! snapshot process at the time of the snapshot. + //! + //! \return A vector of HandleSnapshot objects. + virtual std::vector Handles() const = 0; + + //! \brief Returns a vector of additional memory blocks that should be + //! included in a minidump. + //! + //! \return An vector of MemorySnapshot objects that will be included in the + //! crash dump. The caller does not take ownership of these objects, they + //! are scoped to the lifetime of the ProcessSnapshot object that they + //! were obtained from. + virtual std::vector ExtraMemory() const = 0; + + //! \brief Returns a ProcessMemory object that allows accessing the process' + //! memory directly. + //! + //! \return A ProcessMemory object. The caller does not take ownership of this + //! object, it is scoped to the lifetime of the ProcessSnapshot object + //! that it was obtained from. + virtual const ProcessMemory* Memory() const = 0; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_PROCESS_SNAPSHOT_H_ diff --git a/shared/sentry/external/crashpad/snapshot/sanitized/memory_snapshot_sanitized.cc b/shared/sentry/external/crashpad/snapshot/sanitized/memory_snapshot_sanitized.cc new file mode 100644 index 000000000..a25349180 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/sanitized/memory_snapshot_sanitized.cc @@ -0,0 +1,108 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/sanitized/memory_snapshot_sanitized.h" + +#include + +namespace crashpad { +namespace internal { + +namespace { + +class MemorySanitizer : public MemorySnapshot::Delegate { + public: + MemorySanitizer(MemorySnapshot::Delegate* delegate, + RangeSet* ranges, + VMAddress address, + bool is_64_bit) + : delegate_(delegate), + ranges_(ranges), + address_(address), + is_64_bit_(is_64_bit) {} + + MemorySanitizer(const MemorySanitizer&) = delete; + MemorySanitizer& operator=(const MemorySanitizer&) = delete; + + ~MemorySanitizer() = default; + + bool MemorySnapshotDelegateRead(void* data, size_t size) override { + if (is_64_bit_) { + Sanitize(data, size); + } else { + Sanitize(data, size); + } + return delegate_->MemorySnapshotDelegateRead(data, size); + } + + private: + template + void Sanitize(void* data, size_t size) { + const Pointer defaced = + static_cast(MemorySnapshotSanitized::kDefaced); + + // Sanitize up to a word-aligned address. + const size_t aligned_offset = + ((address_ + sizeof(Pointer) - 1) & ~(sizeof(Pointer) - 1)) - address_; + memcpy(data, &defaced, aligned_offset); + + // Sanitize words that aren't small and don't look like pointers. + size_t word_count = (size - aligned_offset) / sizeof(Pointer); + auto words = + reinterpret_cast(static_cast(data) + aligned_offset); + for (size_t index = 0; index < word_count; ++index) { + if (words[index] > MemorySnapshotSanitized::kSmallWordMax && + !ranges_->Contains(words[index])) { + words[index] = defaced; + } + } + + // Sanitize trailing bytes beyond the word-sized items. + const size_t sanitized_bytes = + aligned_offset + word_count * sizeof(Pointer); + memcpy(static_cast(data) + sanitized_bytes, + &defaced, + size - sanitized_bytes); + } + + MemorySnapshot::Delegate* delegate_; + RangeSet* ranges_; + VMAddress address_; + bool is_64_bit_; +}; + +} // namespace + +MemorySnapshotSanitized::MemorySnapshotSanitized(const MemorySnapshot* snapshot, + RangeSet* ranges, + bool is_64_bit) + : snapshot_(snapshot), ranges_(ranges), is_64_bit_(is_64_bit) {} + +MemorySnapshotSanitized::~MemorySnapshotSanitized() = default; + +uint64_t MemorySnapshotSanitized::Address() const { + return snapshot_->Address(); +} + +size_t MemorySnapshotSanitized::Size() const { + return snapshot_->Size(); +} + +bool MemorySnapshotSanitized::Read(Delegate* delegate) const { + MemorySanitizer sanitizer(delegate, ranges_, Address(), is_64_bit_); + return snapshot_->Read(&sanitizer); +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/sanitized/memory_snapshot_sanitized.h b/shared/sentry/external/crashpad/snapshot/sanitized/memory_snapshot_sanitized.h new file mode 100644 index 000000000..30885f5f8 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/sanitized/memory_snapshot_sanitized.h @@ -0,0 +1,75 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_SANITIZED_MEMORY_SNAPSHOT_SANITIZED_H_ +#define CRASHPAD_SNAPSHOT_SANITIZED_MEMORY_SNAPSHOT_SANITIZED_H_ + +#include + +#include "snapshot/memory_snapshot.h" +#include "util/misc/range_set.h" + +namespace crashpad { +namespace internal { + +//! \brief A MemorySnapshot which wraps and filters sensitive information from +//! another MemorySnapshot. +//! +//! This class redacts all data from the wrapped MemorySnapshot unless: +//! 1. The data is pointer aligned and points into an allowed address range. +//! 2. The data is pointer aligned and is a small integer. +class MemorySnapshotSanitized final : public MemorySnapshot { + public: + //! \brief Redacted data is replaced with this value, casted to the + //! appropriate size for a pointer in the target process. + static constexpr uint64_t kDefaced = 0x0defaced0defaced; + + //! \brief Pointer-aligned data smaller than this value is not redacted. + static constexpr uint64_t kSmallWordMax = 4096; + + //! \brief Constructs this object. + //! + //! \param[in] snapshot The MemorySnapshot to sanitize. + //! \param[in] ranges A set of allowed address ranges. + //! \param[in] is_64_bit `true` if this memory is for a 64-bit process. + MemorySnapshotSanitized(const MemorySnapshot* snapshot, + RangeSet* ranges, + bool is_64_bit); + + MemorySnapshotSanitized(const MemorySnapshotSanitized&) = delete; + MemorySnapshotSanitized& operator=(const MemorySnapshotSanitized&) = delete; + + ~MemorySnapshotSanitized() override; + + // MemorySnapshot: + + uint64_t Address() const override; + size_t Size() const override; + bool Read(Delegate* delegate) const override; + + const MemorySnapshot* MergeWithOtherSnapshot( + const MemorySnapshot* other) const override { + return nullptr; + } + + private: + const MemorySnapshot* snapshot_; + RangeSet* ranges_; + bool is_64_bit_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_SANITIZED_MEMORY_SNAPSHOT_SANITIZED_H_ diff --git a/shared/sentry/external/crashpad/snapshot/sanitized/module_snapshot_sanitized.cc b/shared/sentry/external/crashpad/snapshot/sanitized/module_snapshot_sanitized.cc new file mode 100644 index 000000000..1789fa278 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/sanitized/module_snapshot_sanitized.cc @@ -0,0 +1,136 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/sanitized/module_snapshot_sanitized.h" + +namespace crashpad { +namespace internal { + +namespace { + +bool KeyIsAllowed(const std::string& name, + const std::vector& allowed_keys) { + for (const auto& key : allowed_keys) { + if (name == key) { + return true; + } + } + return false; +} + +} // namespace + +ModuleSnapshotSanitized::ModuleSnapshotSanitized( + const ModuleSnapshot* snapshot, + const std::vector* allowed_annotations) + : snapshot_(snapshot), allowed_annotations_(allowed_annotations) {} + +ModuleSnapshotSanitized::~ModuleSnapshotSanitized() = default; + +std::string ModuleSnapshotSanitized::Name() const { + return snapshot_->Name(); +} + +uint64_t ModuleSnapshotSanitized::Address() const { + return snapshot_->Address(); +} + +uint64_t ModuleSnapshotSanitized::Size() const { + return snapshot_->Size(); +} + +time_t ModuleSnapshotSanitized::Timestamp() const { + return snapshot_->Timestamp(); +} + +void ModuleSnapshotSanitized::FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + snapshot_->FileVersion(version_0, version_1, version_2, version_3); +} + +void ModuleSnapshotSanitized::SourceVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + snapshot_->SourceVersion(version_0, version_1, version_2, version_3); +} + +ModuleSnapshot::ModuleType ModuleSnapshotSanitized::GetModuleType() const { + return snapshot_->GetModuleType(); +} + +void ModuleSnapshotSanitized::UUIDAndAge(crashpad::UUID* uuid, + uint32_t* age) const { + snapshot_->UUIDAndAge(uuid, age); +} + +std::string ModuleSnapshotSanitized::DebugFileName() const { + return snapshot_->DebugFileName(); +} + +std::vector ModuleSnapshotSanitized::BuildID() const { + return snapshot_->BuildID(); +} + +std::vector ModuleSnapshotSanitized::AnnotationsVector() const { + // TODO(jperaza): If/when AnnotationsVector() begins to be used, determine + // whether and how the content should be sanitized. + DCHECK(snapshot_->AnnotationsVector().empty()); + return std::vector(); +} + +std::map +ModuleSnapshotSanitized::AnnotationsSimpleMap() const { + std::map annotations = + snapshot_->AnnotationsSimpleMap(); + if (allowed_annotations_) { + for (auto kv = annotations.begin(); kv != annotations.end(); ++kv) { + if (!KeyIsAllowed(kv->first, *allowed_annotations_)) { + annotations.erase(kv); + } + } + } + return annotations; +} + +std::vector ModuleSnapshotSanitized::AnnotationObjects() + const { + std::vector annotations = snapshot_->AnnotationObjects(); + if (allowed_annotations_) { + std::vector allowed; + for (const auto& anno : annotations) { + if (KeyIsAllowed(anno.name, *allowed_annotations_)) { + allowed.push_back(anno); + } + } + annotations.swap(allowed); + } + return annotations; +} + +std::set> ModuleSnapshotSanitized::ExtraMemoryRanges() + const { + DCHECK(snapshot_->ExtraMemoryRanges().empty()); + return std::set>(); +} + +std::vector +ModuleSnapshotSanitized::CustomMinidumpStreams() const { + return snapshot_->CustomMinidumpStreams(); +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/sanitized/module_snapshot_sanitized.h b/shared/sentry/external/crashpad/snapshot/sanitized/module_snapshot_sanitized.h new file mode 100644 index 000000000..f8937f373 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/sanitized/module_snapshot_sanitized.h @@ -0,0 +1,76 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_SANITIZED_MODULE_SNAPSHOT_SANITIZED_H_ +#define CRASHPAD_SNAPSHOT_SANITIZED_MODULE_SNAPSHOT_SANITIZED_H_ + +#include +#include + +#include "snapshot/module_snapshot.h" + +namespace crashpad { +namespace internal { + +//! \brief A ModuleSnapshot which wraps and filters sensitive information from +//! another ModuleSnapshot. +class ModuleSnapshotSanitized final : public ModuleSnapshot { + public: + //! \brief Constructs this object. + //! + //! \param[in] snapshot The ModuleSnapshot to sanitize. + //! \param[in] allowed_annotations A list of annotation names to allow to be + //! returned by AnnotationsSimpleMap() or AnnotationObjects(). If + //! `nullptr`, all annotations will be returned. + ModuleSnapshotSanitized(const ModuleSnapshot* snapshot, + const std::vector* allowed_annotations); + + ModuleSnapshotSanitized(const ModuleSnapshotSanitized&) = delete; + ModuleSnapshotSanitized& operator=(const ModuleSnapshotSanitized&) = delete; + + ~ModuleSnapshotSanitized() override; + + // ModuleSnapshot: + + std::string Name() const override; + uint64_t Address() const override; + uint64_t Size() const override; + time_t Timestamp() const override; + void FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const override; + void SourceVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const override; + ModuleType GetModuleType() const override; + void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override; + std::string DebugFileName() const override; + std::vector BuildID() const override; + std::vector AnnotationsVector() const override; + std::map AnnotationsSimpleMap() const override; + std::vector AnnotationObjects() const override; + std::set> ExtraMemoryRanges() const override; + std::vector CustomMinidumpStreams() const override; + + private: + const ModuleSnapshot* snapshot_; + const std::vector* allowed_annotations_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_SANITIZED_MODULE_SNAPSHOT_SANITIZED_H_ diff --git a/shared/sentry/external/crashpad/snapshot/sanitized/process_snapshot_sanitized.cc b/shared/sentry/external/crashpad/snapshot/sanitized/process_snapshot_sanitized.cc new file mode 100644 index 000000000..21cf5a628 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/sanitized/process_snapshot_sanitized.cc @@ -0,0 +1,274 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/sanitized/process_snapshot_sanitized.h" + +#include + +#include "snapshot/cpu_context.h" +#include "util/numeric/safe_assignment.h" + +namespace crashpad { + +namespace { + +class StackReferencesAddressRange : public MemorySnapshot::Delegate { + public: + // Returns true if stack contains a pointer aligned word in the range [low, + // high). The search starts at the first pointer aligned address greater than + // stack_pointer. + bool CheckStack(const MemorySnapshot* stack, + VMAddress stack_pointer, + VMAddress low, + VMAddress high, + bool is_64_bit) { + stack_ = stack; + stack_pointer_ = stack_pointer; + low_ = low; + high_ = high; + is_64_bit_ = is_64_bit; + return stack_->Read(this); + } + + // MemorySnapshot::Delegate + bool MemorySnapshotDelegateRead(void* data, size_t size) { + return is_64_bit_ ? ScanStackForPointers(data, size) + : ScanStackForPointers(data, size); + } + + private: + template + bool ScanStackForPointers(void* data, size_t size) { + size_t sp_offset; + if (!AssignIfInRange(&sp_offset, stack_pointer_ - stack_->Address())) { + return false; + } + const size_t aligned_sp_offset = + (sp_offset + sizeof(Pointer) - 1) & ~(sizeof(Pointer) - 1); + + auto words = reinterpret_cast(static_cast(data) + + aligned_sp_offset); + size_t word_count = (size - aligned_sp_offset) / sizeof(Pointer); + for (size_t index = 0; index < word_count; ++index) { + if (words[index] >= low_ && words[index] < high_) { + return true; + } + } + + return false; + } + + VMAddress stack_pointer_; + VMAddress low_; + VMAddress high_; + const MemorySnapshot* stack_; + bool is_64_bit_; +}; + +} // namespace + +ProcessSnapshotSanitized::ProcessSnapshotSanitized() = default; + +ProcessSnapshotSanitized::~ProcessSnapshotSanitized() = default; + +bool ProcessSnapshotSanitized::Initialize( + const ProcessSnapshot* snapshot, + std::unique_ptr> allowed_annotations, + std::unique_ptr>> + allowed_memory_ranges, + VMAddress target_module_address, + bool sanitize_stacks) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + snapshot_ = snapshot; + allowed_annotations_ = std::move(allowed_annotations); + sanitize_stacks_ = sanitize_stacks; + + if (target_module_address) { + const ExceptionSnapshot* exception = snapshot_->Exception(); + if (!exception) { + return false; + } + + const ThreadSnapshot* exc_thread = nullptr; + for (const auto thread : snapshot_->Threads()) { + if (thread->ThreadID() == exception->ThreadID()) { + exc_thread = thread; + break; + } + } + if (!exc_thread) { + return false; + } + + const ModuleSnapshot* target_module = nullptr; + for (const auto module : snapshot_->Modules()) { + if (target_module_address >= module->Address() && + target_module_address < module->Address() + module->Size()) { + target_module = module; + break; + } + } + if (!target_module) { + return false; + } + + // Only allow the snapshot if the program counter or some address on the + // stack points into the target module. + VMAddress pc = exception->Context()->InstructionPointer(); + VMAddress module_address_low = target_module->Address(); + VMAddress module_address_high = module_address_low + target_module->Size(); + if ((pc < module_address_low || pc >= module_address_high) && + !StackReferencesAddressRange().CheckStack( + exc_thread->Stack(), + exception->Context()->StackPointer(), + module_address_low, + module_address_high, + exception->Context()->Is64Bit())) { + return false; + } + } + + if (allowed_annotations_) { + for (const auto module : snapshot_->Modules()) { + modules_.emplace_back(std::make_unique( + module, allowed_annotations_.get())); + } + } + + if (sanitize_stacks_) { + for (const auto module : snapshot_->Modules()) { + address_ranges_.Insert(module->Address(), module->Size()); + } + + for (const auto thread : snapshot_->Threads()) { + address_ranges_.Insert(thread->Stack()->Address(), + thread->Stack()->Size()); + threads_.emplace_back(std::make_unique( + thread, &address_ranges_)); + } + } + + process_memory_.Initialize(snapshot_->Memory(), allowed_memory_ranges.get()); + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +crashpad::ProcessID ProcessSnapshotSanitized::ProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return snapshot_->ProcessID(); +} + +crashpad::ProcessID ProcessSnapshotSanitized::ParentProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return snapshot_->ParentProcessID(); +} + +void ProcessSnapshotSanitized::SnapshotTime(timeval* snapshot_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + snapshot_->SnapshotTime(snapshot_time); +} + +void ProcessSnapshotSanitized::ProcessStartTime(timeval* start_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + snapshot_->ProcessStartTime(start_time); +} + +void ProcessSnapshotSanitized::ProcessCPUTimes(timeval* user_time, + timeval* system_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + snapshot_->ProcessCPUTimes(user_time, system_time); +} + +void ProcessSnapshotSanitized::ReportID(UUID* report_id) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + snapshot_->ReportID(report_id); +} + +void ProcessSnapshotSanitized::ClientID(UUID* client_id) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + snapshot_->ClientID(client_id); +} + +const std::map& +ProcessSnapshotSanitized::AnnotationsSimpleMap() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return snapshot_->AnnotationsSimpleMap(); +} + +const SystemSnapshot* ProcessSnapshotSanitized::System() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return snapshot_->System(); +} + +std::vector ProcessSnapshotSanitized::Threads() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (!sanitize_stacks_) { + return snapshot_->Threads(); + } + + std::vector threads; + for (const auto& thread : threads_) { + threads.push_back(thread.get()); + } + return threads; +} + +std::vector ProcessSnapshotSanitized::Modules() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (!allowed_annotations_) { + return snapshot_->Modules(); + } + + std::vector modules; + for (const auto& module : modules_) { + modules.push_back(module.get()); + } + return modules; +} + +std::vector ProcessSnapshotSanitized::UnloadedModules() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return snapshot_->UnloadedModules(); +} + +const ExceptionSnapshot* ProcessSnapshotSanitized::Exception() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return snapshot_->Exception(); +} + +std::vector +ProcessSnapshotSanitized::MemoryMap() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return snapshot_->MemoryMap(); +} + +std::vector ProcessSnapshotSanitized::Handles() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return snapshot_->Handles(); +} + +std::vector ProcessSnapshotSanitized::ExtraMemory() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return snapshot_->ExtraMemory(); +} + +const ProcessMemory* ProcessSnapshotSanitized::Memory() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &process_memory_; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/sanitized/process_snapshot_sanitized.h b/shared/sentry/external/crashpad/snapshot/sanitized/process_snapshot_sanitized.h new file mode 100644 index 000000000..86c7c4e4e --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/sanitized/process_snapshot_sanitized.h @@ -0,0 +1,115 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_SANITIZED_PROCESS_SNAPSHOT_SANITIZED_H_ +#define CRASHPAD_SNAPSHOT_SANITIZED_PROCESS_SNAPSHOT_SANITIZED_H_ + +#include +#include +#include + +#include "snapshot/exception_snapshot.h" +#include "snapshot/process_snapshot.h" +#include "snapshot/sanitized/module_snapshot_sanitized.h" +#include "snapshot/sanitized/thread_snapshot_sanitized.h" +#include "snapshot/thread_snapshot.h" +#include "snapshot/unloaded_module_snapshot.h" +#include "util/misc/address_types.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/misc/range_set.h" +#include "util/process/process_id.h" +#include "util/process/process_memory_sanitized.h" + +namespace crashpad { + +//! \brief A ProcessSnapshot which wraps and filters sensitive information from +//! another ProcessSnapshot. +class ProcessSnapshotSanitized final : public ProcessSnapshot { + public: + ProcessSnapshotSanitized(); + + ProcessSnapshotSanitized(const ProcessSnapshotSanitized&) = delete; + ProcessSnapshotSanitized& operator=(const ProcessSnapshotSanitized&) = delete; + + ~ProcessSnapshotSanitized() override; + + //! \brief Initializes this object. + //! + //! This method must be successfully called before calling any other method on + //! this object. + //! + //! \param[in] snapshot The ProcessSnapshot to sanitize. + //! \param[in] allowed_annotations A list of annotations names to allow to + //! be returned by AnnotationsSimpleMap() or from this object's module + //! snapshots. If `nullptr`, all annotations will be returned. + //! \param[in] allowed_memory_ranges A list of memory ranges to allow to be + //! accessible via Memory(), or `nullptr` to allow all ranges. + //! \param[in] target_module_address An address in the target process' + //! address space within the bounds of a module to target. If the + //! crashing thread's context and stack do not contain any pointers into + //! this module's address range, this method will return `false`. If this + //! value is 0, this method will not check the context or stack for + //! references to any particular module. + //! \param[in] sanitize_stacks If `true`, the MemorySnapshots for each + //! thread's stack will be filtered using an + //! internal::StackSnapshotSanitized. + //! \return `false` if \a snapshot does not meet sanitization requirements and + //! should be filtered entirely. Otherwise `true`. + bool Initialize( + const ProcessSnapshot* snapshot, + std::unique_ptr> allowed_annotations, + std::unique_ptr>> + allowed_memory_ranges, + VMAddress target_module_address, + bool sanitize_stacks); + + // ProcessSnapshot: + + crashpad::ProcessID ProcessID() const override; + crashpad::ProcessID ParentProcessID() const override; + void SnapshotTime(timeval* snapshot_time) const override; + void ProcessStartTime(timeval* start_time) const override; + void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override; + void ReportID(UUID* report_id) const override; + void ClientID(UUID* client_id) const override; + const std::map& AnnotationsSimpleMap() + const override; + const SystemSnapshot* System() const override; + std::vector Threads() const override; + std::vector Modules() const override; + std::vector UnloadedModules() const override; + const ExceptionSnapshot* Exception() const override; + std::vector MemoryMap() const override; + std::vector Handles() const override; + std::vector ExtraMemory() const override; + const ProcessMemory* Memory() const override; + + private: + // Only used when allowed_annotations_ != nullptr. + std::vector> modules_; + + // Only used when sanitize_stacks_ == true. + std::vector> threads_; + + RangeSet address_ranges_; + const ProcessSnapshot* snapshot_; + ProcessMemorySanitized process_memory_; + std::unique_ptr> allowed_annotations_; + bool sanitize_stacks_; + InitializationStateDcheck initialized_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_SANITIZED_PROCESS_SNAPSHOT_SANITIZED_H_ diff --git a/shared/sentry/external/crashpad/snapshot/sanitized/process_snapshot_sanitized_test.cc b/shared/sentry/external/crashpad/snapshot/sanitized/process_snapshot_sanitized_test.cc new file mode 100644 index 000000000..306f2df26 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/sanitized/process_snapshot_sanitized_test.cc @@ -0,0 +1,314 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/sanitized/process_snapshot_sanitized.h" + +#include + +#include + +#include "base/notreached.h" +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "test/multiprocess_exec.h" +#include "util/file/file_io.h" +#include "util/misc/address_sanitizer.h" +#include "util/numeric/safe_assignment.h" + +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) +#include + +#include "snapshot/linux/process_snapshot_linux.h" +#include "util/linux/direct_ptrace_connection.h" +#include "util/linux/exception_information.h" +#include "util/posix/signals.h" +#endif + +namespace crashpad { +namespace test { +namespace { + +class ExceptionGenerator { + public: + ExceptionGenerator(const ExceptionGenerator&) = delete; + ExceptionGenerator& operator=(const ExceptionGenerator&) = delete; + + static ExceptionGenerator* Get() { + static ExceptionGenerator* instance = new ExceptionGenerator(); + return instance; + } + + bool Initialize(FileHandle in, FileHandle out) { + in_ = in; + out_ = out; + return Signals::InstallCrashHandlers(HandleCrash, 0, nullptr); + } + + private: + ExceptionGenerator() = default; + ~ExceptionGenerator() = delete; + + static void HandleCrash(int signo, siginfo_t* siginfo, void* context) { + auto state = Get(); + + ExceptionInformation info = {}; + info.siginfo_address = FromPointerCast(siginfo); + info.context_address = FromPointerCast(context); + info.thread_id = syscall(SYS_gettid); + + auto info_addr = FromPointerCast(&info); + ASSERT_TRUE(LoggingWriteFile(state->out_, &info_addr, sizeof(info_addr))); + + CheckedReadFileAtEOF(state->in_); + Signals::RestoreHandlerAndReraiseSignalOnReturn(siginfo, nullptr); + } + + FileHandle in_; + FileHandle out_; +}; + +constexpr char kAllowedAnnotationName[] = "name_of_allowed_anno"; +constexpr char kAllowedAnnotationValue[] = "some_value"; +constexpr char kNonAllowedAnnotationName[] = "non_allowed_anno"; +constexpr char kNonAllowedAnnotationValue[] = "private_annotation"; +constexpr char kSensitiveStackData[] = "sensitive_stack_data"; + +struct ChildTestAddresses { + VMAddress string_address; + VMAddress module_address; + VMAddress non_module_address; + VMAddress code_pointer_address; + VMAddress code_pointer_value; +}; + +void ChildTestFunction() { + FileHandle in = StdioFileHandle(StdioStream::kStandardInput); + FileHandle out = StdioFileHandle(StdioStream::kStandardOutput); + + static StringAnnotation<32> allowed_annotation(kAllowedAnnotationName); + allowed_annotation.Set(kAllowedAnnotationValue); + + static StringAnnotation<32> non_allowed_annotation(kNonAllowedAnnotationName); + non_allowed_annotation.Set(kNonAllowedAnnotationValue); + + char string_data[std::size(kSensitiveStackData)]; + strcpy(string_data, kSensitiveStackData); + + void (*code_pointer)(void) = ChildTestFunction; + + ChildTestAddresses addrs = {}; + addrs.string_address = FromPointerCast(string_data); + addrs.module_address = FromPointerCast(ChildTestFunction); + addrs.non_module_address = FromPointerCast(&addrs); + addrs.code_pointer_address = FromPointerCast(&code_pointer); + addrs.code_pointer_value = FromPointerCast(code_pointer); + ASSERT_TRUE(LoggingWriteFile(out, &addrs, sizeof(addrs))); + + auto gen = ExceptionGenerator::Get(); + ASSERT_TRUE(gen->Initialize(in, out)); + + __builtin_trap(); +} + +CRASHPAD_CHILD_TEST_MAIN(ChildToBeSanitized) { + ChildTestFunction(); + NOTREACHED(); + return EXIT_SUCCESS; +} + +void ExpectAnnotations(ProcessSnapshot* snapshot, bool sanitized) { + bool found_allowed = false; + bool found_non_allowed = false; + for (auto module : snapshot->Modules()) { + for (const auto& anno : module->AnnotationObjects()) { + if (anno.name == kAllowedAnnotationName) { + found_allowed = true; + } else if (anno.name == kNonAllowedAnnotationName) { + found_non_allowed = true; + } + } + } + + EXPECT_TRUE(found_allowed); + if (sanitized) { + EXPECT_FALSE(found_non_allowed); + } else { + EXPECT_TRUE(found_non_allowed); + } +} + +void ExpectProcessMemory(ProcessSnapshot* snapshot, + VMAddress allowed_byte, + bool sanitized) { + auto memory = snapshot->Memory(); + + char out; + EXPECT_TRUE(memory->Read(allowed_byte, 1, &out)); + + bool disallowed_read = memory->Read(allowed_byte + 1, 1, &out); + if (sanitized) { + EXPECT_FALSE(disallowed_read); + } else { + EXPECT_TRUE(disallowed_read); + } +} + +class StackSanitizationChecker : public MemorySnapshot::Delegate { + public: + StackSanitizationChecker() = default; + ~StackSanitizationChecker() = default; + + void CheckStack(const MemorySnapshot* stack, + const ChildTestAddresses& addrs, + bool is_64_bit, + bool sanitized) { + stack_ = stack; + addrs_ = addrs; + is_64_bit_ = is_64_bit; + sanitized_ = sanitized; + EXPECT_TRUE(stack_->Read(this)); + } + + // MemorySnapshot::Delegate + bool MemorySnapshotDelegateRead(void* data, size_t size) override { + // AddressSanitizer with use-after-return detection causes stack variables + // to be allocated on the heap. +#if !defined(ADDRESS_SANITIZER) + size_t pointer_offset; + if (!AssignIfInRange(&pointer_offset, + addrs_.code_pointer_address - stack_->Address())) { + ADD_FAILURE(); + return false; + } + + const auto data_c = static_cast(data); + VMAddress pointer_value; + if (is_64_bit_) { + pointer_value = *reinterpret_cast(data_c + pointer_offset); + } else { + pointer_value = *reinterpret_cast(data_c + pointer_offset); + } + EXPECT_EQ(pointer_value, addrs_.code_pointer_value); + + size_t string_offset; + if (!AssignIfInRange(&string_offset, + addrs_.string_address - stack_->Address())) { + ADD_FAILURE(); + return false; + } + + auto string = data_c + string_offset; + if (sanitized_) { + EXPECT_STRNE(string, kSensitiveStackData); + } else { + EXPECT_STREQ(string, kSensitiveStackData); + } +#endif // !ADDRESS_SANITIZER + return true; + } + + private: + const MemorySnapshot* stack_; + ChildTestAddresses addrs_; + bool is_64_bit_; + bool sanitized_; +}; + +void ExpectStackData(ProcessSnapshot* snapshot, + const ChildTestAddresses& addrs, + bool sanitized) { + const ThreadSnapshot* crasher = nullptr; + for (const auto thread : snapshot->Threads()) { + if (thread->ThreadID() == snapshot->Exception()->ThreadID()) { + crasher = thread; + break; + } + } + ASSERT_TRUE(crasher); + + const MemorySnapshot* stack = crasher->Stack(); + StackSanitizationChecker().CheckStack( + stack, addrs, crasher->Context()->Is64Bit(), sanitized); +} + +class SanitizeTest : public MultiprocessExec { + public: + SanitizeTest() : MultiprocessExec() { + SetChildTestMainFunction("ChildToBeSanitized"); + SetExpectedChildTerminationBuiltinTrap(); + } + + SanitizeTest(const SanitizeTest&) = delete; + SanitizeTest& operator=(const SanitizeTest&) = delete; + + ~SanitizeTest() = default; + + private: + void MultiprocessParent() { + ChildTestAddresses addrs = {}; + ASSERT_TRUE( + LoggingReadFileExactly(ReadPipeHandle(), &addrs, sizeof(addrs))); + + VMAddress exception_info_addr; + ASSERT_TRUE(LoggingReadFileExactly( + ReadPipeHandle(), &exception_info_addr, sizeof(exception_info_addr))); + + DirectPtraceConnection connection; + ASSERT_TRUE(connection.Initialize(ChildProcess())); + + ProcessSnapshotLinux snapshot; + ASSERT_TRUE(snapshot.Initialize(&connection)); + ASSERT_TRUE(snapshot.InitializeException(exception_info_addr)); + + ExpectAnnotations(&snapshot, /* sanitized= */ false); + ExpectStackData(&snapshot, addrs, /* sanitized= */ false); + ExpectProcessMemory(&snapshot, + addrs.string_address, + /* sanitized= */ false); + + auto allowed_annotations = std::make_unique>(); + allowed_annotations->push_back(kAllowedAnnotationName); + + auto allowed_memory_ranges = + std::make_unique>>(); + allowed_memory_ranges->push_back( + std::make_pair(addrs.string_address, addrs.string_address + 1)); + + ProcessSnapshotSanitized sanitized; + ASSERT_TRUE(sanitized.Initialize(&snapshot, + std::move(allowed_annotations), + std::move(allowed_memory_ranges), + addrs.module_address, + true)); + + ExpectAnnotations(&sanitized, /* sanitized= */ true); + ExpectStackData(&sanitized, addrs, /* sanitized= */ true); + ExpectProcessMemory(&sanitized, + addrs.string_address, + /* sanitized= */ true); + + ProcessSnapshotSanitized screened_snapshot; + EXPECT_FALSE(screened_snapshot.Initialize( + &snapshot, nullptr, nullptr, addrs.non_module_address, false)); + } +}; + +TEST(ProcessSnapshotSanitized, Sanitize) { + SanitizeTest test; + test.Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/sanitized/sanitization_information.cc b/shared/sentry/external/crashpad/snapshot/sanitized/sanitization_information.cc new file mode 100644 index 000000000..1b2b85393 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/sanitized/sanitization_information.cc @@ -0,0 +1,116 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/sanitized/sanitization_information.h" + +#include + +#include "base/logging.h" +#include "client/annotation.h" + +namespace crashpad { + +namespace { + +template +bool ReadAllowedAnnotations(const ProcessMemoryRange& memory, + VMAddress list_address, + std::vector* allowed_annotations) { + if (!list_address) { + return true; + } + + std::vector local_allowed_annotations; + Pointer name_address; + while (memory.Read(list_address, sizeof(name_address), &name_address)) { + if (!name_address) { + allowed_annotations->swap(local_allowed_annotations); + return true; + } + + std::string name; + if (!memory.ReadCStringSizeLimited( + name_address, Annotation::kNameMaxLength, &name)) { + return false; + } + local_allowed_annotations.push_back(name); + list_address += sizeof(Pointer); + } + + return false; +} + +} // namespace + +bool ReadAllowedAnnotations(const ProcessMemoryRange& memory, + VMAddress list_address, + std::vector* allowed_annotations) { + return memory.Is64Bit() ? ReadAllowedAnnotations( + memory, list_address, allowed_annotations) + : ReadAllowedAnnotations( + memory, list_address, allowed_annotations); +} + +bool ReadAllowedMemoryRanges( + const ProcessMemoryRange& memory, + VMAddress list_address, + std::vector>* allowed_memory_ranges) { + allowed_memory_ranges->clear(); + if (!list_address) { + return true; + } + + SanitizationAllowedMemoryRanges list; + if (!memory.Read(list_address, sizeof(list), &list)) { + LOG(ERROR) << "Failed to read allowed memory ranges"; + return false; + } + + if (!list.size) { + return true; + } + + // An upper bound of entries that we never expect to see more than. + constexpr size_t kMaxListSize = 256; + if (list.size > kMaxListSize) { + LOG(ERROR) << "list exceeded maximum, size=" << list.size; + return false; + } + + std::vector ranges(list.size); + if (!memory.Read(list.entries, sizeof(ranges[0]) * list.size, + ranges.data())) { + LOG(ERROR) << "Failed to read allowed memory ranges"; + return false; + } + + const VMAddress vm_max = memory.Is64Bit() + ? std::numeric_limits::max() + : std::numeric_limits::max(); + std::vector> local_allowed_memory_ranges; + for (size_t i = 0; i < list.size; ++i) { + if (ranges[i].base > vm_max || ranges[i].length > vm_max - ranges[i].base) { + LOG(ERROR) << "Invalid range: base=" << ranges[i].base + << " length=" << ranges[i].length; + return false; + } + local_allowed_memory_ranges.emplace_back(ranges[i].base, + ranges[i].base + ranges[i].length); + } + + allowed_memory_ranges->swap(local_allowed_memory_ranges); + return true; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/sanitized/sanitization_information.h b/shared/sentry/external/crashpad/snapshot/sanitized/sanitization_information.h new file mode 100644 index 000000000..d5afe0d57 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/sanitized/sanitization_information.h @@ -0,0 +1,101 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_SANITIZED_SANITIZATION_INFORMATION_H_ +#define CRASHPAD_SNAPSHOT_SANITIZED_SANITIZATION_INFORMATION_H_ + +#include + +#include +#include +#include + +#include "util/misc/address_types.h" +#include "util/process/process_memory_range.h" + +namespace crashpad { + +#pragma pack(push, 1) + +//! \brief Struture containing information about how snapshots should be +//! sanitized. +//! +//! \see ProcessSnapshotSanitized +struct SanitizationInformation { + //! \brief The address in the client process' address space of a nullptr + //! terminated array of NUL-terminated strings. The string values are the + //! names of allowed annotations. This value is 0 if all annotations are + //! allowed. + VMAddress allowed_annotations_address; + + //! \brief An address in the client process' address space within a module to + //! target. When a target module is used, crash dumps are discarded unless + //! the crashing thread's program counter or pointer-aligned values on the + //! crashing thread's stack point into the target module. This value is 0 + //! if there is no target module. + VMAddress target_module_address; + + //! \brief The address in the client process' address space of a + //! \a SanitizationAllowedMemoryRanges, a list of address ranges allowed + //! to be accessed by ProcessMemorySanitized. This value is 0 if no memory + //! is allowed to be read using ProcessMemorySanitized. + VMAddress allowed_memory_ranges_address; + + //! \brief Non-zero if stacks should be sanitized for possible PII. + uint8_t sanitize_stacks; +}; + +//! \brief Describes a list of allowed memory ranges. +struct SanitizationAllowedMemoryRanges { + //! \brief Describes a range of memory. + struct Range { + VMAddress base; + VMSize length; + }; + + //! \brief Address of an array of |size| elements of type Range. + VMAddress entries; + VMSize size; +}; + +#pragma pack(pop) + +//! \brief Reads a list of allowed annotations from another process. +//! +//! \param[in] memory A memory reader for the target process. +//! \param[in] list_address The address in the target process' address space of +//! a nullptr terminated array of NUL-terminated strings. +//! \param[out] allowed_annotations The list read, valid only if this function +//! returns `true`. +//! \return `true` on success, `false` on failure with a message logged. +bool ReadAllowedAnnotations(const ProcessMemoryRange& memory, + VMAddress list_address, + std::vector* allowed_annotations); + +//! \brief Reads a list of allowed memory ranges from another process. +//! +//! \param[in] memory A memory reader for the target process. +//! \param[in] list_address The address in the target process' address space of +//! a nullptr terminated array of NUL-terminated strings. +//! \param[out] allowed_memory_ranges A list of allowed memory regions, valid +//! only if this function returns `true`. +//! \return `true` on success, `false` on failure with a message logged. +bool ReadAllowedMemoryRanges( + const ProcessMemoryRange& memory, + VMAddress list_address, + std::vector>* allowed_memory_ranges); + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_SANITIZED_SANITIZATION_INFORMATION_H_ diff --git a/shared/sentry/external/crashpad/snapshot/sanitized/sanitization_information_test.cc b/shared/sentry/external/crashpad/snapshot/sanitized/sanitization_information_test.cc new file mode 100644 index 000000000..65b01f085 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/sanitized/sanitization_information_test.cc @@ -0,0 +1,78 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/sanitized/sanitization_information.h" + +#include + +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "util/misc/from_pointer_cast.h" +#include "util/process/process_memory_linux.h" + +#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) +#include "test/linux/fake_ptrace_connection.h" +#endif + +namespace crashpad { +namespace test { +namespace { + +class AllowedAnnotationsTest : public testing::Test { + public: + void SetUp() override { + ASSERT_TRUE(connection_.Initialize(getpid())); + +#if defined(ARCH_CPU_64_BITS) + ASSERT_TRUE(range_.Initialize(connection_.Memory(), true)); +#else + ASSERT_TRUE(range_.Initialize(connection_.Memory(), false)); +#endif + } + + protected: + bool DoReadAllowedAnnotations(const char* const* address) { + return ReadAllowedAnnotations( + range_, FromPointerCast(address), &allowed_annotations_); + } + + FakePtraceConnection connection_; + ProcessMemoryRange range_; + std::vector allowed_annotations_; +}; + +const char* const kEmptyAllowedAnnotations[] = {nullptr}; + +TEST_F(AllowedAnnotationsTest, EmptyAllowedAnnotations) { + ASSERT_TRUE(DoReadAllowedAnnotations(kEmptyAllowedAnnotations)); + EXPECT_EQ(allowed_annotations_, std::vector()); +} + +const char* const kNonEmptyAllowedAnnotations[] = {"string1", + "another_string", + "", + nullptr}; + +TEST_F(AllowedAnnotationsTest, NonEmptyAllowedAnnotations) { + ASSERT_TRUE(DoReadAllowedAnnotations(kNonEmptyAllowedAnnotations)); + ASSERT_EQ(allowed_annotations_.size(), + std::size(kNonEmptyAllowedAnnotations) - 1); + for (size_t index = 0; index < allowed_annotations_.size(); ++index) { + EXPECT_EQ(allowed_annotations_[index], kNonEmptyAllowedAnnotations[index]); + } +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/sanitized/thread_snapshot_sanitized.cc b/shared/sentry/external/crashpad/snapshot/sanitized/thread_snapshot_sanitized.cc new file mode 100644 index 000000000..186776eae --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/sanitized/thread_snapshot_sanitized.cc @@ -0,0 +1,63 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/sanitized/thread_snapshot_sanitized.h" + +#include "snapshot/cpu_context.h" + +namespace crashpad { +namespace internal { + +ThreadSnapshotSanitized::ThreadSnapshotSanitized(const ThreadSnapshot* snapshot, + RangeSet* ranges) + : ThreadSnapshot(), + snapshot_(snapshot), + stack_(snapshot_->Stack(), ranges, snapshot_->Context()->Is64Bit()) {} + +ThreadSnapshotSanitized::~ThreadSnapshotSanitized() = default; + +const CPUContext* ThreadSnapshotSanitized::Context() const { + return snapshot_->Context(); +} + +const MemorySnapshot* ThreadSnapshotSanitized::Stack() const { + return &stack_; +} + +uint64_t ThreadSnapshotSanitized::ThreadID() const { + return snapshot_->ThreadID(); +} + +int ThreadSnapshotSanitized::SuspendCount() const { + return snapshot_->SuspendCount(); +} + +int ThreadSnapshotSanitized::Priority() const { + return snapshot_->Priority(); +} + +uint64_t ThreadSnapshotSanitized::ThreadSpecificDataAddress() const { + return snapshot_->ThreadSpecificDataAddress(); +} + +std::vector ThreadSnapshotSanitized::ExtraMemory() + const { + // TODO(jperaza): If/when ExtraMemory() is used, decide whether and how it + // should be sanitized. + DCHECK(snapshot_->ExtraMemory().empty()); + return std::vector(); +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/sanitized/thread_snapshot_sanitized.h b/shared/sentry/external/crashpad/snapshot/sanitized/thread_snapshot_sanitized.h new file mode 100644 index 000000000..dca0a0e91 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/sanitized/thread_snapshot_sanitized.h @@ -0,0 +1,60 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_SANITIZED_THREAD_SNAPSHOT_SANITIZED_H_ +#define CRASHPAD_SNAPSHOT_SANITIZED_THREAD_SNAPSHOT_SANITIZED_H_ + +#include "snapshot/thread_snapshot.h" + +#include "snapshot/sanitized/memory_snapshot_sanitized.h" +#include "util/misc/range_set.h" + +namespace crashpad { +namespace internal { + +//! \brief A ThreadSnapshot which wraps and filters sensitive information from +//! another ThreadSnapshot. +class ThreadSnapshotSanitized final : public ThreadSnapshot { + public: + //! \brief Constructs this object. + //! + //! \param[in] snapshot The ThreadSnapshot to sanitize. + //! \param[in] ranges A set of address ranges with which to sanitize this + //! thread's stacks. \see internal::MemorySnapshotSanitized. + ThreadSnapshotSanitized(const ThreadSnapshot* snapshot, RangeSet* ranges); + + ThreadSnapshotSanitized(const ThreadSnapshotSanitized&) = delete; + ThreadSnapshotSanitized& operator=(const ThreadSnapshotSanitized&) = delete; + + ~ThreadSnapshotSanitized() override; + + // ThreadSnapshot: + + const CPUContext* Context() const override; + const MemorySnapshot* Stack() const override; + uint64_t ThreadID() const override; + int SuspendCount() const override; + int Priority() const override; + uint64_t ThreadSpecificDataAddress() const override; + std::vector ExtraMemory() const override; + + private: + const ThreadSnapshot* snapshot_; + MemorySnapshotSanitized stack_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_SANITIZED_THREAD_SNAPSHOT_SANITIZED_H_ diff --git a/shared/sentry/external/crashpad/snapshot/snapshot_constants.h b/shared/sentry/external/crashpad/snapshot/snapshot_constants.h new file mode 100644 index 000000000..2bbddda38 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/snapshot_constants.h @@ -0,0 +1,28 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef SNAPSHOT_SNAPSHOT_CONSTANTS_H_ +#define SNAPSHOT_SNAPSHOT_CONSTANTS_H_ + +namespace crashpad { + +//! \brief The maximum number of crashpad::Annotations that will be read from +//! a client process. +//! +//! \note This maximum was chosen arbitrarily and may change in the future. +constexpr size_t kMaxNumberOfAnnotations = 200; + +} // namespace crashpad + +#endif // SNAPSHOT_SNAPSHOT_CONSTANTS_H_ diff --git a/shared/sentry/external/crashpad/snapshot/system_snapshot.h b/shared/sentry/external/crashpad/snapshot/system_snapshot.h new file mode 100644 index 000000000..d78cc235e --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/system_snapshot.h @@ -0,0 +1,282 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_SYSTEM_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_SYSTEM_SNAPSHOT_H_ + +#include +#include + +#include + +#include "snapshot/cpu_architecture.h" + +namespace crashpad { + +//! \brief An abstract interface to a snapshot representing the state of a +//! system, comprising an operating system, CPU architecture, and various +//! other characteristics. +class SystemSnapshot { + public: + virtual ~SystemSnapshot() {} + + //! \brief A system’s operating system family. + enum OperatingSystem { + //! \brief The snapshot system’s operating system is unknown. + kOperatingSystemUnknown = 0, + + //! \brief macOS. + kOperatingSystemMacOSX, + + //! \brief Windows. + kOperatingSystemWindows, + + //! \brief Linux. + kOperatingSystemLinux, + + //! \brief Android. + kOperatingSystemAndroid, + + //! \brief Fuchsia. + kOperatingSystemFuchsia, + + //! \brief iOS. + kOperatingSystemIOS, + }; + + //! \brief A system’s daylight saving time status. + //! + //! The daylight saving time status is taken partially from the system’s + //! locale configuration. This determines whether daylight saving time is + //! ever observed on the system. If it is, the snapshot’s time + //! (ProcessSnapshot::SnapshotTime()) is used to determine whether the system + //! was observing daylight saving time at the time of the snapshot. + enum DaylightSavingTimeStatus { + //! \brief Daylight saving time is never observed on the snapshot system. + kDoesNotObserveDaylightSavingTime = 0, + + //! \brief Daylight saving time is observed on the snapshot system when in + //! effect, but standard time was in effect at the time of the snapshot. + kObservingStandardTime, + + //! \brief Daylight saving time is observed on the snapshot system when in + //! effect, and daylight saving time was in effect at the time of the + //! snapshot. + kObservingDaylightSavingTime, + }; + + //! \brief Returns the snapshot system’s CPU architecture. + //! + //! In some cases, a system may be able to run processes of multiple specific + //! architecture types. For example, systems based on 64-bit architectures + //! such as x86_64 are often able to run 32-bit code of another architecture + //! in the same family, such as 32-bit x86. On these systems, this method will + //! return the architecture of the process that the snapshot is associated + //! with, provided that the SystemSnapshot object was obtained from + //! ProcessSnapshot::System(). This renders one aspect of this method’s return + //! value a process attribute rather than a system attribute, but it’s defined + //! here rather than in ProcessSnapshot because the CPU architecture is a + //! better conceptual fit for the system abstraction alongside these other + //! related methods. + virtual CPUArchitecture GetCPUArchitecture() const = 0; + + //! \brief Returns the snapshot system’s CPU revision. + //! + //! For x86-family CPUs (including x86_64 and 32-bit x86), this is the CPU + //! family, model, and stepping ID values from `cpuid 1` `eax`. The family and + //! model values are adjusted to take the extended family and model IDs into + //! account. These values are encoded in this method’s return value with the + //! family in the high high 16 bits, the model in the next 8 bits, and the + //! stepping in the low 8 bits. + //! + //! \return A CPU architecture-specific value identifying the CPU revision. + virtual uint32_t CPURevision() const = 0; + + //! \brief Returns the total number of CPUs present in the snapshot system. + virtual uint8_t CPUCount() const = 0; + + //! \brief Returns the vendor of the snapshot system’s CPUs. + //! + //! For x86-family CPUs (including x86_64 and 32-bit x86), this is the CPU + //! vendor identification string as encoded in `cpuid 0` `ebx`, `edx`, and + //! `ecx`. + //! + //! \return A string identifying the vendor of the snapshot system’s CPUs. + virtual std::string CPUVendor() const = 0; + + //! \brief Returns frequency information about the snapshot system’s CPUs in + //! \a current_hz and \a max_hz. + //! + //! \param[out] current_hz The snapshot system’s CPU clock frequency in Hz at + //! the time of the snapshot. + //! \param[out] max_hz The snapshot system’s maximum possible CPU clock + //! frequency. + virtual void CPUFrequency(uint64_t* current_hz, uint64_t* max_hz) const = 0; + + //! \brief Returns an x86-family snapshot system’s CPU signature. + //! + //! This is the family, model, and stepping ID values as encoded in `cpuid 1` + //! `eax`. + //! + //! This method must only be called when GetCPUArchitecture() indicates an + //! x86-family CPU architecture (#kCPUArchitectureX86 or + //! #kCPUArchitectureX86_64). + //! + //! \return An x86 family-specific value identifying the CPU signature. + virtual uint32_t CPUX86Signature() const = 0; + + //! \brief Returns an x86-family snapshot system’s CPU features. + //! + //! This is the feature information as encoded in `cpuid 1` `edx` and `ecx`. + //! `edx` is placed in the low half of the return value, and `ecx` is placed + //! in the high half. + //! + //! This method must only be called when GetCPUArchitecture() indicates an + //! x86-family CPU architecture (#kCPUArchitectureX86 or + //! #kCPUArchitectureX86_64). + //! + //! \return An x86 family-specific value identifying CPU features. + //! + //! \sa CPUX86ExtendedFeatures() + //! \sa CPUX86Leaf7Features() + virtual uint64_t CPUX86Features() const = 0; + + //! \brief Returns an x86-family snapshot system’s extended CPU features. + //! + //! This is the extended feature information as encoded in `cpuid 0x80000001` + //! `edx` and `ecx`. `edx` is placed in the low half of the return value, and + //! `ecx` is placed in the high half. + //! + //! This method must only be called when GetCPUArchitecture() indicates an + //! x86-family CPU architecture (#kCPUArchitectureX86 or + //! #kCPUArchitectureX86_64). + //! + //! \return An x86 family-specific value identifying extended CPU features. + //! + //! \sa CPUX86Features() + //! \sa CPUX86Leaf7Features() + virtual uint64_t CPUX86ExtendedFeatures() const = 0; + + //! \brief Returns an x86-family snapshot system’s “leaf 7†CPU features. + //! + //! This is the “leaf 7†feature information as encoded in `cpuid 7` `ebx`. If + //! `cpuid 7` is not supported by the snapshot CPU, this returns `0`. + //! + //! This method must only be called when GetCPUArchitecture() indicates an + //! x86-family CPU architecture (#kCPUArchitectureX86 or + //! #kCPUArchitectureX86_64). + //! + //! \return An x86 family-specific value identifying “leaf 7†CPU features. + //! + //! \sa CPUX86Features() + //! \sa CPUX86ExtendedFeatures() + virtual uint32_t CPUX86Leaf7Features() const = 0; + + //! \brief Returns an x86-family snapshot system’s CPU’s support for the SSE + //! DAZ (“denormals are zerosâ€) mode. + //! + //! This determines whether the CPU supports DAZ mode at all, not whether this + //! mode is enabled for any particular thread. DAZ mode support is detected by + //! examining the DAZ bit in the `mxcsr_mask` field of the floating-point + //! context saved by `fxsave`. + //! + //! This method must only be called when GetCPUArchitecture() indicates an + //! x86-family CPU architecture (#kCPUArchitectureX86 or + //! #kCPUArchitectureX86_64). + //! + //! \return `true` if the snapshot system’s CPUs support the SSE DAZ mode, + //! `false` if they do not. + virtual bool CPUX86SupportsDAZ() const = 0; + + //! \brief Returns the snapshot system’s operating system family. + virtual OperatingSystem GetOperatingSystem() const = 0; + + //! \brief Returns whether the snapshot system runs a server variant of its + //! operating system. + virtual bool OSServer() const = 0; + + //! \brief Returns the snapshot system’s operating system version information + //! in \a major, \a minor, \a bugfix, and \a build. + //! + //! \param[out] major The snapshot system’s operating system’s first (major) + //! version number component. This would be `10` for macOS 10.12.1, and + //! `6` for Windows 7 (NT 6.1) SP1 version 6.1.7601. + //! \param[out] minor The snapshot system’s operating system’s second (minor) + //! version number component. This would be `12` for macOS 10.12.1, and + //! `1` for Windows 7 (NT 6.1) SP1 version 6.1.7601. + //! \param[out] bugfix The snapshot system’s operating system’s third (bugfix) + //! version number component. This would be `1` for macOS 10.12.1, and + //! `7601` for Windows 7 (NT 6.1) SP1 version 6.1.7601. + //! \param[out] build A string further identifying an operating system + //! version. For macOS 10.12.1, this would be `"16B2657"`. For Windows, + //! this would be `"Service Pack 1"` if that service pack was installed. + //! On Android, the `ro.build.fingerprint` system property would be + //! appended. For Linux and other Unix-like systems, this would be the + //! kernel version from `uname -srvm`, possibly with additional + //! information appended. + virtual void OSVersion(int* major, + int* minor, + int* bugfix, + std::string* build) const = 0; + + //! \brief Returns the snapshot system’s full operating system version + //! information in string format. + //! + //! For macOS, the string contains values from the operating system and + //! kernel. A macOS 10.12.1 system snapshot would be identified as `"Mac OS + //! X 10.12.1 (16B2657); Darwin 16.1.0 Darwin Kernel Version 16.1.0: Wed Oct + //! 19 20:31:56 PDT 2016; root:xnu-3789.21.4~4/RELEASE_X86_64 x86_64"`. + virtual std::string OSVersionFull() const = 0; + + //! \brief Returns a description of the snapshot system’s hardware in string + //! format. + //! + //! For macOS, the string contains the Mac model and board ID. A mid-2014 15" + //! MacBook Pro would be identified as `"MacBookPro11,3 + //! (Mac-2BD1B31983FE1663)"`. + virtual std::string MachineDescription() const = 0; + + //! \brief Returns the status of the NX (no-execute, or XD, execute-disable) + //! feature on the snapshot system. + //! + //! This refers to a feature that allows mapped readable pages to be marked + //! as non-executable. + //! + //! \return `true` if the snapshot system supports NX and it is enabled. + virtual bool NXEnabled() const = 0; + + //! \brief Returns time zone information from the snapshot system, based on + //! its locale configuration and real-time clock. + //! + //! \param[out] dst_status Whether the location observes daylight saving time, + //! and if so, whether it or standard time is currently being observed. + //! \param[out] standard_offset_seconds The number of seconds that the + //! location’s time zone is east (ahead) of UTC during standard time. + //! \param[out] daylight_offset_seconds The number of seconds that the + //! location’s time zone is east (ahead) of UTC during daylight saving. + //! time. + //! \param[out] standard_name The name of the time zone while standard time is + //! being observed. + //! \param[out] daylight_name The name of the time zone while daylight saving + //! time is being observed. + virtual void TimeZone(DaylightSavingTimeStatus* dst_status, + int* standard_offset_seconds, + int* daylight_offset_seconds, + std::string* standard_name, + std::string* daylight_name) const = 0; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_SYSTEM_SNAPSHOT_H_ diff --git a/shared/sentry/external/crashpad/snapshot/test/test_cpu_context.cc b/shared/sentry/external/crashpad/snapshot/test/test_cpu_context.cc new file mode 100644 index 000000000..75d9c5a1f --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/test/test_cpu_context.cc @@ -0,0 +1,296 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/test/test_cpu_context.h" + +#include +#include + +#include + +namespace crashpad { +namespace test { + +namespace { + +// This is templatized because the CPUContextX86::Fxsave and +// CPUContextX86_64::Fxsave are nearly identical but have different sizes for +// the members |xmm|, |reserved_4|, and |available|. +template +void InitializeCPUContextFxsave(FxsaveType* fxsave, uint32_t* seed) { + uint32_t value = *seed; + + fxsave->fcw = static_cast(value++); + fxsave->fsw = static_cast(value++); + fxsave->ftw = static_cast(value++); + fxsave->reserved_1 = static_cast(value++); + fxsave->fop = static_cast(value++); + fxsave->fpu_ip = value++; + fxsave->fpu_cs = static_cast(value++); + fxsave->reserved_2 = static_cast(value++); + fxsave->fpu_dp = value++; + fxsave->fpu_ds = static_cast(value++); + fxsave->reserved_3 = static_cast(value++); + fxsave->mxcsr = value++; + fxsave->mxcsr_mask = value++; + for (size_t st_mm_index = 0; st_mm_index < std::size(fxsave->st_mm); + ++st_mm_index) { + for (size_t byte = 0; byte < std::size(fxsave->st_mm[st_mm_index].st); + ++byte) { + fxsave->st_mm[st_mm_index].st[byte] = static_cast(value++); + } + for (size_t byte = 0; + byte < std::size(fxsave->st_mm[st_mm_index].st_reserved); + ++byte) { + fxsave->st_mm[st_mm_index].st_reserved[byte] = + static_cast(value); + } + } + for (size_t xmm_index = 0; xmm_index < std::size(fxsave->xmm); ++xmm_index) { + for (size_t byte = 0; byte < std::size(fxsave->xmm[xmm_index]); ++byte) { + fxsave->xmm[xmm_index][byte] = static_cast(value++); + } + } + for (size_t byte = 0; byte < std::size(fxsave->reserved_4); ++byte) { + fxsave->reserved_4[byte] = static_cast(value++); + } + for (size_t byte = 0; byte < std::size(fxsave->available); ++byte) { + fxsave->available[byte] = static_cast(value++); + } + + *seed = value; +} + +} // namespace + +void InitializeCPUContextX86Fxsave(CPUContextX86::Fxsave* fxsave, + uint32_t* seed) { + return InitializeCPUContextFxsave(fxsave, seed); +} + +void InitializeCPUContextX86_64Fxsave(CPUContextX86_64::Fxsave* fxsave, + uint32_t* seed) { + return InitializeCPUContextFxsave(fxsave, seed); +} + +void InitializeCPUContextX86(CPUContext* context, uint32_t seed) { + context->architecture = kCPUArchitectureX86; + + if (seed == 0) { + memset(context->x86, 0, sizeof(*context->x86)); + return; + } + + uint32_t value = seed; + + context->x86->eax = value++; + context->x86->ebx = value++; + context->x86->ecx = value++; + context->x86->edx = value++; + context->x86->edi = value++; + context->x86->esi = value++; + context->x86->ebp = value++; + context->x86->esp = value++; + context->x86->eip = value++; + context->x86->eflags = value++; + context->x86->cs = static_cast(value++); + context->x86->ds = static_cast(value++); + context->x86->es = static_cast(value++); + context->x86->fs = static_cast(value++); + context->x86->gs = static_cast(value++); + context->x86->ss = static_cast(value++); + InitializeCPUContextX86Fxsave(&context->x86->fxsave, &value); + context->x86->dr0 = value++; + context->x86->dr1 = value++; + context->x86->dr2 = value++; + context->x86->dr3 = value++; + context->x86->dr4 = value++; + context->x86->dr5 = value++; + context->x86->dr6 = value++; + context->x86->dr7 = value++; +} + +void InitializeCPUContextX86_64(CPUContext* context, uint32_t seed) { + context->architecture = kCPUArchitectureX86_64; + + if (seed == 0) { + memset(context->x86_64, 0, sizeof(*context->x86_64)); + return; + } + + uint32_t value = seed; + + context->x86_64->rax = value++; + context->x86_64->rbx = value++; + context->x86_64->rcx = value++; + context->x86_64->rdx = value++; + context->x86_64->rdi = value++; + context->x86_64->rsi = value++; + context->x86_64->rbp = value++; + context->x86_64->rsp = value++; + context->x86_64->r8 = value++; + context->x86_64->r9 = value++; + context->x86_64->r10 = value++; + context->x86_64->r11 = value++; + context->x86_64->r12 = value++; + context->x86_64->r13 = value++; + context->x86_64->r14 = value++; + context->x86_64->r15 = value++; + context->x86_64->rip = value++; + context->x86_64->rflags = value++; + context->x86_64->cs = static_cast(value++); + context->x86_64->fs = static_cast(value++); + context->x86_64->gs = static_cast(value++); + InitializeCPUContextX86_64Fxsave(&context->x86_64->fxsave, &value); + context->x86_64->dr0 = value++; + context->x86_64->dr1 = value++; + context->x86_64->dr2 = value++; + context->x86_64->dr3 = value++; + context->x86_64->dr4 = value++; + context->x86_64->dr5 = value++; + context->x86_64->dr6 = value++; + context->x86_64->dr7 = value++; +} + +void InitializeCPUContextARM(CPUContext* context, uint32_t seed) { + context->architecture = kCPUArchitectureARM; + CPUContextARM* arm = context->arm; + + if (seed == 0) { + memset(arm, 0, sizeof(*arm)); + return; + } + + uint32_t value = seed; + + for (size_t index = 0; index < std::size(arm->regs); ++index) { + arm->regs[index] = value++; + } + arm->fp = value++; + arm->ip = value++; + arm->ip = value++; + arm->sp = value++; + arm->lr = value++; + arm->pc = value++; + arm->cpsr = value++; + + for (size_t index = 0; index < std::size(arm->vfp_regs.vfp); ++index) { + arm->vfp_regs.vfp[index] = value++; + } + arm->vfp_regs.fpscr = value++; + + arm->have_fpa_regs = false; + arm->have_vfp_regs = true; +} + +void InitializeCPUContextARM64(CPUContext* context, uint32_t seed) { + context->architecture = kCPUArchitectureARM64; + CPUContextARM64* arm64 = context->arm64; + + if (seed == 0) { + memset(arm64, 0, sizeof(*arm64)); + return; + } + + uint32_t value = seed; + + for (size_t index = 0; index < std::size(arm64->regs); ++index) { + arm64->regs[index] = value++; + } + arm64->sp = value++; + arm64->pc = value++; + arm64->spsr = value++; + + for (size_t index = 0; index < std::size(arm64->fpsimd); ++index) { + arm64->fpsimd[index].lo = value++; + arm64->fpsimd[index].hi = value++; + } + arm64->fpsr = value++; + arm64->fpcr = value++; +} + +void InitializeCPUContextMIPS(CPUContext* context, uint32_t seed) { + context->architecture = kCPUArchitectureMIPSEL; + CPUContextMIPS* mipsel = context->mipsel; + + if (seed == 0) { + memset(mipsel, 0, sizeof(*mipsel)); + return; + } + + uint32_t value = seed; + + for (size_t index = 0; index < std::size(mipsel->regs); ++index) { + mipsel->regs[index] = value++; + } + + mipsel->mdlo = value++; + mipsel->mdhi = value++; + mipsel->cp0_epc = value++; + mipsel->cp0_badvaddr = value++; + mipsel->cp0_status = value++; + mipsel->cp0_cause = value++; + + for (size_t index = 0; index < std::size(mipsel->fpregs.fregs); ++index) { + mipsel->fpregs.fregs[index]._fp_fregs = static_cast(value++); + } + + mipsel->fpcsr = value++; + mipsel->fir = value++; + + for (size_t index = 0; index < 3; ++index) { + mipsel->hi[index] = value++; + mipsel->lo[index] = value++; + } + mipsel->dsp_control = value++; +} + +void InitializeCPUContextMIPS64(CPUContext* context, uint32_t seed) { + context->architecture = kCPUArchitectureMIPS64EL; + CPUContextMIPS64* mips64 = context->mips64; + + if (seed == 0) { + memset(mips64, 0, sizeof(*mips64)); + return; + } + + uint64_t value = seed; + + for (size_t index = 0; index < std::size(mips64->regs); ++index) { + mips64->regs[index] = value++; + } + + mips64->mdlo = value++; + mips64->mdhi = value++; + mips64->cp0_epc = value++; + mips64->cp0_badvaddr = value++; + mips64->cp0_status = value++; + mips64->cp0_cause = value++; + + for (size_t index = 0; index < std::size(mips64->fpregs.dregs); ++index) { + mips64->fpregs.dregs[index] = static_cast(value++); + } + + mips64->fpcsr = value++; + mips64->fir = value++; + + for (size_t index = 0; index < 3; ++index) { + mips64->hi[index] = value++; + mips64->lo[index] = value++; + } + mips64->dsp_control = value++; +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/test/test_cpu_context.h b/shared/sentry/external/crashpad/snapshot/test/test_cpu_context.h new file mode 100644 index 000000000..ca1b6b7b2 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/test/test_cpu_context.h @@ -0,0 +1,71 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_TEST_TEST_CPU_CONTEXT_H_ +#define CRASHPAD_SNAPSHOT_TEST_TEST_CPU_CONTEXT_H_ + +#include + +#include "snapshot/cpu_context.h" + +namespace crashpad { +namespace test { + +//! \brief Initializes an `fxsave` context substructure for testing. +//! +//! \param[out] fxsave The structure to initialize. +//! \param[in,out] seed The seed value. Initializing two `fxsave` structures of +//! the same type with identical seed values should produce identical +//! structures. Initialization with a different seed value should produce +//! a different `fxsave` structure. If \a seed is `0`, \a fxsave is zeroed +//! out entirely. If \a seed is nonzero, \a fxsave will be populated +//! entirely with nonzero values. \a seed will be updated by this function +//! to allow the caller to perform subsequent initialization of the context +//! structure containing \a fxsave. +//! +//! \{ +void InitializeCPUContextX86Fxsave(CPUContextX86::Fxsave* fxsave, + uint32_t* seed); +void InitializeCPUContextX86_64Fxsave(CPUContextX86_64::Fxsave* fxsave, + uint32_t* seed); +//! \} + +//! \brief Initializes a context structure for testing. +//! +//! Initialization is compatible with the initialization used by minidump +//! context test initialization functions such as InitializeMinidumpContextX86() +//! and InitializeMinidumpContextAMD64() for identical \a seed values. +//! +//! \param[out] context The structure to initialize. +//! \param[in] seed The seed value. Initializing two context structures of the +//! same type with identical seed values should produce identical context +//! structures. Initialization with a different seed value should produce +//! a different context structure. If \a seed is `0`, \a context is zeroed +//! out entirely except for the CPUContext::architecture field, which will +//! identify the context type. If \a seed is nonzero, \a context will be +//! populated entirely with nonzero values. +//! +//! \{ +void InitializeCPUContextX86(CPUContext* context, uint32_t seed); +void InitializeCPUContextX86_64(CPUContext* context, uint32_t seed); +void InitializeCPUContextARM(CPUContext* context, uint32_t seed); +void InitializeCPUContextARM64(CPUContext* context, uint32_t seed); +void InitializeCPUContextMIPS(CPUContext* context, uint32_t seed); +void InitializeCPUContextMIPS64(CPUContext* context, uint32_t seed); +//! \} + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_TEST_TEST_CPU_CONTEXT_H_ diff --git a/shared/sentry/external/crashpad/snapshot/test/test_exception_snapshot.cc b/shared/sentry/external/crashpad/snapshot/test/test_exception_snapshot.cc new file mode 100644 index 000000000..84340671a --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/test/test_exception_snapshot.cc @@ -0,0 +1,67 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/test/test_exception_snapshot.h" + +namespace crashpad { +namespace test { + +TestExceptionSnapshot::TestExceptionSnapshot() + : context_union_(), + context_(), + thread_id_(0), + exception_(0), + exception_info_(0), + exception_address_(0), + codes_() { + context_.x86 = &context_union_.x86; +} + +TestExceptionSnapshot::~TestExceptionSnapshot() { +} + +const CPUContext* TestExceptionSnapshot::Context() const { + return &context_; +} + +uint64_t TestExceptionSnapshot::ThreadID() const { + return thread_id_; +} + +uint32_t TestExceptionSnapshot::Exception() const { + return exception_; +} + +uint32_t TestExceptionSnapshot::ExceptionInfo() const { + return exception_info_; +} + +uint64_t TestExceptionSnapshot::ExceptionAddress() const { + return exception_address_; +} + +const std::vector& TestExceptionSnapshot::Codes() const { + return codes_; +} + +std::vector TestExceptionSnapshot::ExtraMemory() const { + std::vector extra_memory; + for (const auto& em : extra_memory_) { + extra_memory.push_back(em.get()); + } + return extra_memory; +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/test/test_exception_snapshot.h b/shared/sentry/external/crashpad/snapshot/test/test_exception_snapshot.h new file mode 100644 index 000000000..0f6b7c072 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/test/test_exception_snapshot.h @@ -0,0 +1,96 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_TEST_TEST_EXCEPTION_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_TEST_TEST_EXCEPTION_SNAPSHOT_H_ + +#include + +#include +#include +#include + +#include "snapshot/cpu_context.h" +#include "snapshot/exception_snapshot.h" + +namespace crashpad { +namespace test { + +//! \brief A test ExceptionSnapshot that can carry arbitrary data for testing +//! purposes. +class TestExceptionSnapshot final : public ExceptionSnapshot { + public: + TestExceptionSnapshot(); + + TestExceptionSnapshot(const TestExceptionSnapshot&) = delete; + TestExceptionSnapshot& operator=(const TestExceptionSnapshot&) = delete; + + ~TestExceptionSnapshot(); + + //! \brief Obtains a pointer to the underlying mutable CPUContext structure. + //! + //! This method is intended to be used by callers to populate the CPUContext + //! structure. + //! + //! \return The same pointer that Context() does, while treating the data as + //! mutable. + //! + //! \attention This returns a non-`const` pointer to this object’s private + //! data so that a caller can populate the context structure directly. + //! This is done because providing setter interfaces to each field in the + //! context structure would be unwieldy and cumbersome. Care must be taken + //! to populate the context structure correctly. + CPUContext* MutableContext() { return &context_; } + + void SetThreadID(uint64_t thread_id) { thread_id_ = thread_id; } + void SetException(uint32_t exception) { exception_ = exception; } + void SetExceptionInfo(uint32_t exception_information) { + exception_info_ = exception_information; + } + void SetExceptionAddress(uint64_t exception_address) { + exception_address_ = exception_address; + } + void SetCodes(const std::vector& codes) { codes_ = codes; } + void AddExtraMemory(std::unique_ptr extra_memory) { + extra_memory_.push_back(std::move(extra_memory)); + } + + // ExceptionSnapshot: + + const CPUContext* Context() const override; + uint64_t ThreadID() const override; + uint32_t Exception() const override; + uint32_t ExceptionInfo() const override; + uint64_t ExceptionAddress() const override; + const std::vector& Codes() const override; + std::vector ExtraMemory() const override; + + private: + union { + CPUContextX86 x86; + CPUContextX86_64 x86_64; + } context_union_; + CPUContext context_; + uint64_t thread_id_; + uint32_t exception_; + uint32_t exception_info_; + uint64_t exception_address_; + std::vector codes_; + std::vector> extra_memory_; +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_TEST_TEST_EXCEPTION_SNAPSHOT_H_ diff --git a/shared/sentry/external/crashpad/snapshot/test/test_memory_map_region_snapshot.cc b/shared/sentry/external/crashpad/snapshot/test/test_memory_map_region_snapshot.cc new file mode 100644 index 000000000..53500c647 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/test/test_memory_map_region_snapshot.cc @@ -0,0 +1,37 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/test/test_memory_map_region_snapshot.h" + +namespace crashpad { +namespace test { + +TestMemoryMapRegionSnapshot::TestMemoryMapRegionSnapshot() : memory_info_() { +} + +TestMemoryMapRegionSnapshot::~TestMemoryMapRegionSnapshot() { +} + +void TestMemoryMapRegionSnapshot::SetMindumpMemoryInfo( + const MINIDUMP_MEMORY_INFO& mmi) { + memory_info_ = mmi; +} + +const MINIDUMP_MEMORY_INFO& TestMemoryMapRegionSnapshot::AsMinidumpMemoryInfo() + const { + return memory_info_; +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/test/test_memory_map_region_snapshot.h b/shared/sentry/external/crashpad/snapshot/test/test_memory_map_region_snapshot.h new file mode 100644 index 000000000..c4e1ca752 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/test/test_memory_map_region_snapshot.h @@ -0,0 +1,49 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_TEST_TEST_MEMORY_MAP_REGION_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_TEST_TEST_MEMORY_MAP_REGION_SNAPSHOT_H_ + +#include + +#include "snapshot/memory_map_region_snapshot.h" + +namespace crashpad { +namespace test { + +//! \brief A test MemoryMapRegionSnapshot that can carry arbitrary data for +//! testing purposes. +class TestMemoryMapRegionSnapshot final : public MemoryMapRegionSnapshot { + public: + TestMemoryMapRegionSnapshot(); + + TestMemoryMapRegionSnapshot(const TestMemoryMapRegionSnapshot&) = delete; + TestMemoryMapRegionSnapshot& operator=(const TestMemoryMapRegionSnapshot&) = + delete; + + ~TestMemoryMapRegionSnapshot() override; + + void SetMindumpMemoryInfo(const MINIDUMP_MEMORY_INFO& mmi); + + // MemoryMapRegionSnapshot: + const MINIDUMP_MEMORY_INFO& AsMinidumpMemoryInfo() const override; + + private: + MINIDUMP_MEMORY_INFO memory_info_; +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_TEST_TEST_MEMORY_MAP_REGION_SNAPSHOT_H_ diff --git a/shared/sentry/external/crashpad/snapshot/test/test_memory_snapshot.cc b/shared/sentry/external/crashpad/snapshot/test/test_memory_snapshot.cc new file mode 100644 index 000000000..95aba4066 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/test/test_memory_snapshot.cc @@ -0,0 +1,65 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/test/test_memory_snapshot.h" + +#include +#include + +namespace crashpad { +namespace test { + +TestMemorySnapshot::TestMemorySnapshot() + : address_(0), size_(0), value_('\0'), should_fail_(false) { +} + +TestMemorySnapshot::~TestMemorySnapshot() { +} + +uint64_t TestMemorySnapshot::Address() const { + return address_; +} + +size_t TestMemorySnapshot::Size() const { + return size_; +} + +bool TestMemorySnapshot::Read(Delegate* delegate) const { + if (should_fail_) { + return false; + } + + if (size_ == 0) { + return delegate->MemorySnapshotDelegateRead(nullptr, size_); + } + + std::string buffer(size_, value_); + return delegate->MemorySnapshotDelegateRead(&buffer[0], size_); +} + +const MemorySnapshot* TestMemorySnapshot::MergeWithOtherSnapshot( + const MemorySnapshot* other) const { + CheckedRange merged(0, 0); + if (!DetermineMergedRange(this, other, &merged)) + return nullptr; + + std::unique_ptr result(new TestMemorySnapshot()); + result->SetAddress(merged.base()); + result->SetSize(merged.size()); + result->SetValue(value_); + return result.release(); +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/test/test_memory_snapshot.h b/shared/sentry/external/crashpad/snapshot/test/test_memory_snapshot.h new file mode 100644 index 000000000..a9e8fb1a2 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/test/test_memory_snapshot.h @@ -0,0 +1,66 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_TEST_TEST_MEMORY_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_TEST_TEST_MEMORY_SNAPSHOT_H_ + +#include +#include + +#include "snapshot/memory_snapshot.h" + +namespace crashpad { +namespace test { + +//! \brief A test MemorySnapshot that can carry arbitrary data for testing +//! purposes. +class TestMemorySnapshot final : public MemorySnapshot { + public: + TestMemorySnapshot(); + + TestMemorySnapshot(const TestMemorySnapshot&) = delete; + TestMemorySnapshot& operator=(const TestMemorySnapshot&) = delete; + + ~TestMemorySnapshot(); + + void SetAddress(uint64_t address) { address_ = address; } + void SetSize(size_t size) { size_ = size; } + + //! \brief Sets the value to fill the test memory region with. + //! + //! \param[in] value The value to be written to \a delegate when Read() is + //! called. This value will be repeated Size() times. + void SetValue(char value) { value_ = value; } + + void SetShouldFailRead(bool should_fail) { should_fail_ = true; } + + // MemorySnapshot: + + uint64_t Address() const override; + size_t Size() const override; + bool Read(Delegate* delegate) const override; + const MemorySnapshot* MergeWithOtherSnapshot( + const MemorySnapshot* other) const override; + + private: + uint64_t address_; + size_t size_; + char value_; + bool should_fail_; +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_TEST_TEST_MEMORY_SNAPSHOT_H_ diff --git a/shared/sentry/external/crashpad/snapshot/test/test_module_snapshot.cc b/shared/sentry/external/crashpad/snapshot/test/test_module_snapshot.cc new file mode 100644 index 000000000..ad0c2dec1 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/test/test_module_snapshot.cc @@ -0,0 +1,113 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/test/test_module_snapshot.h" + +namespace crashpad { +namespace test { + +TestModuleSnapshot::TestModuleSnapshot() + : name_(), + address_(0), + size_(0), + timestamp_(0), + file_version_(), + source_version_(), + module_type_(kModuleTypeUnknown), + age_(0), + uuid_(), + debug_file_name_(), + annotations_vector_(), + annotations_simple_map_(), + extra_memory_ranges_() {} + +TestModuleSnapshot::~TestModuleSnapshot() {} + +std::string TestModuleSnapshot::Name() const { + return name_; +} + +uint64_t TestModuleSnapshot::Address() const { + return address_; +} + +uint64_t TestModuleSnapshot::Size() const { + return size_; +} + +time_t TestModuleSnapshot::Timestamp() const { + return timestamp_; +} + +void TestModuleSnapshot::FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + *version_0 = file_version_[0]; + *version_1 = file_version_[1]; + *version_2 = file_version_[2]; + *version_3 = file_version_[3]; +} + +void TestModuleSnapshot::SourceVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + *version_0 = source_version_[0]; + *version_1 = source_version_[1]; + *version_2 = source_version_[2]; + *version_3 = source_version_[3]; +} + +ModuleSnapshot::ModuleType TestModuleSnapshot::GetModuleType() const { + return module_type_; +} + +void TestModuleSnapshot::UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const { + *uuid = uuid_; + *age = age_; +} + +std::string TestModuleSnapshot::DebugFileName() const { + return debug_file_name_; +} + +std::vector TestModuleSnapshot::BuildID() const { + return build_id_; +} + +std::vector TestModuleSnapshot::AnnotationsVector() const { + return annotations_vector_; +} + +std::map TestModuleSnapshot::AnnotationsSimpleMap() + const { + return annotations_simple_map_; +} + +std::vector TestModuleSnapshot::AnnotationObjects() const { + return annotation_objects_; +} + +std::set> TestModuleSnapshot::ExtraMemoryRanges() const { + return extra_memory_ranges_; +} + +std::vector +TestModuleSnapshot::CustomMinidumpStreams() const { + return std::vector(); +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/test/test_module_snapshot.h b/shared/sentry/external/crashpad/snapshot/test/test_module_snapshot.h new file mode 100644 index 000000000..19e129900 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/test/test_module_snapshot.h @@ -0,0 +1,138 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_TEST_TEST_MODULE_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_TEST_TEST_MODULE_SNAPSHOT_H_ + +#include +#include + +#include +#include +#include + +#include "snapshot/module_snapshot.h" + +namespace crashpad { +namespace test { + +//! \brief A test ModuleSnapshot that can carry arbitrary data for testing +//! purposes. +class TestModuleSnapshot final : public ModuleSnapshot { + public: + TestModuleSnapshot(); + + TestModuleSnapshot(const TestModuleSnapshot&) = delete; + TestModuleSnapshot& operator=(const TestModuleSnapshot&) = delete; + + ~TestModuleSnapshot() override; + + void SetName(const std::string& name) { name_ = name; } + void SetAddressAndSize(uint64_t address, uint64_t size) { + address_ = address; + size_ = size; + } + void SetTimestamp(time_t timestamp) { timestamp_ = timestamp; } + void SetFileVersion(uint16_t file_version_0, + uint16_t file_version_1, + uint16_t file_version_2, + uint16_t file_version_3) { + file_version_[0] = file_version_0; + file_version_[1] = file_version_1; + file_version_[2] = file_version_2; + file_version_[3] = file_version_3; + } + void SetSourceVersion(uint16_t source_version_0, + uint16_t source_version_1, + uint16_t source_version_2, + uint16_t source_version_3) { + source_version_[0] = source_version_0; + source_version_[1] = source_version_1; + source_version_[2] = source_version_2; + source_version_[3] = source_version_3; + } + void SetModuleType(ModuleType module_type) { module_type_ = module_type; } + void SetUUIDAndAge(const crashpad::UUID& uuid, uint32_t age) { + uuid_ = uuid; + age_ = age; + } + void SetBuildID(const std::vector& build_id) { + build_id_ = build_id; + } + void SetDebugFileName(const std::string& debug_file_name) { + debug_file_name_ = debug_file_name; + } + void SetAnnotationsVector( + const std::vector& annotations_vector) { + annotations_vector_ = annotations_vector; + } + void SetAnnotationsSimpleMap( + const std::map& annotations_simple_map) { + annotations_simple_map_ = annotations_simple_map; + } + void SetAnnotationObjects( + const std::vector& annotations) { + annotation_objects_ = annotations; + } + void SetExtraMemoryRanges( + const std::set>& extra_memory_ranges) { + extra_memory_ranges_ = extra_memory_ranges; + } + + // ModuleSnapshot: + + std::string Name() const override; + uint64_t Address() const override; + uint64_t Size() const override; + time_t Timestamp() const override; + void FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const override; + void SourceVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const override; + ModuleType GetModuleType() const override; + void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override; + std::string DebugFileName() const override; + std::vector BuildID() const override; + std::vector AnnotationsVector() const override; + std::map AnnotationsSimpleMap() const override; + std::vector AnnotationObjects() const override; + std::set> ExtraMemoryRanges() const override; + std::vector CustomMinidumpStreams() const override; + + private: + std::string name_; + uint64_t address_; + uint64_t size_; + time_t timestamp_; + uint16_t file_version_[4]; + uint16_t source_version_[4]; + ModuleType module_type_; + uint32_t age_; + crashpad::UUID uuid_; + std::vector build_id_; + std::string debug_file_name_; + std::vector annotations_vector_; + std::map annotations_simple_map_; + std::vector annotation_objects_; + std::set> extra_memory_ranges_; +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_TEST_TEST_MODULE_SNAPSHOT_H_ diff --git a/shared/sentry/external/crashpad/snapshot/test/test_process_snapshot.cc b/shared/sentry/external/crashpad/snapshot/test/test_process_snapshot.cc new file mode 100644 index 000000000..42a049a5c --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/test/test_process_snapshot.cc @@ -0,0 +1,135 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/exception_snapshot.h" +#include "snapshot/system_snapshot.h" +#include "snapshot/test/test_process_snapshot.h" + +namespace crashpad { +namespace test { + +TestProcessSnapshot::TestProcessSnapshot() + : process_id_(0), + parent_process_id_(0), + snapshot_time_(), + process_start_time_(), + process_cpu_user_time_(), + process_cpu_system_time_(), + report_id_(), + client_id_(), + annotations_simple_map_(), + system_(), + threads_(), + modules_(), + exception_(), + memory_map_(), + handles_(), + extra_memory_(), + process_memory_() { +} + +TestProcessSnapshot::~TestProcessSnapshot() { +} + +crashpad::ProcessID TestProcessSnapshot::ProcessID() const { + return process_id_; +} + +crashpad::ProcessID TestProcessSnapshot::ParentProcessID() const { + return parent_process_id_; +} + +void TestProcessSnapshot::SnapshotTime(timeval* snapshot_time) const { + *snapshot_time = snapshot_time_; +} + +void TestProcessSnapshot::ProcessStartTime(timeval* start_time) const { + *start_time = process_start_time_; +} + +void TestProcessSnapshot::ProcessCPUTimes(timeval* user_time, + timeval* system_time) const { + *user_time = process_cpu_user_time_; + *system_time = process_cpu_system_time_; +} + +void TestProcessSnapshot::ReportID(UUID* report_id) const { + *report_id = report_id_; +} + +void TestProcessSnapshot::ClientID(UUID* client_id) const { + *client_id = client_id_; +} + +const std::map& +TestProcessSnapshot::AnnotationsSimpleMap() const { + return annotations_simple_map_; +} + +const SystemSnapshot* TestProcessSnapshot::System() const { + return system_.get(); +} + +std::vector TestProcessSnapshot::Threads() const { + std::vector threads; + for (const auto& thread : threads_) { + threads.push_back(thread.get()); + } + return threads; +} + +std::vector TestProcessSnapshot::Modules() const { + std::vector modules; + for (const auto& module : modules_) { + modules.push_back(module.get()); + } + return modules; +} + +std::vector TestProcessSnapshot::UnloadedModules() + const { + return unloaded_modules_; +} + +const ExceptionSnapshot* TestProcessSnapshot::Exception() const { + return exception_.get(); +} + +std::vector TestProcessSnapshot::MemoryMap() + const { + std::vector memory_map; + for (const auto& item : memory_map_) { + memory_map.push_back(item.get()); + } + return memory_map; +} + +std::vector TestProcessSnapshot::Handles() const { + return handles_; +} + +std::vector TestProcessSnapshot::ExtraMemory() const { + std::vector extra_memory; + for (const auto& em : extra_memory_) { + extra_memory.push_back(em.get()); + } + return extra_memory; +} + +const ProcessMemory* TestProcessSnapshot::Memory() const { + return process_memory_.get(); +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/test/test_process_snapshot.h b/shared/sentry/external/crashpad/snapshot/test/test_process_snapshot.h new file mode 100644 index 000000000..16873fee1 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/test/test_process_snapshot.h @@ -0,0 +1,198 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_TEST_TEST_PROCESS_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_TEST_TEST_PROCESS_SNAPSHOT_H_ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "snapshot/exception_snapshot.h" +#include "snapshot/memory_map_region_snapshot.h" +#include "snapshot/memory_snapshot.h" +#include "snapshot/module_snapshot.h" +#include "snapshot/process_snapshot.h" +#include "snapshot/system_snapshot.h" +#include "snapshot/thread_snapshot.h" +#include "snapshot/unloaded_module_snapshot.h" +#include "util/misc/uuid.h" +#include "util/process/process_id.h" +#include "util/process/process_memory.h" + +namespace crashpad { +namespace test { + +//! \brief A test ProcessSnapshot that can carry arbitrary data for testing +//! purposes. +class TestProcessSnapshot final : public ProcessSnapshot { + public: + TestProcessSnapshot(); + + TestProcessSnapshot(const TestProcessSnapshot&) = delete; + TestProcessSnapshot& operator=(const TestProcessSnapshot&) = delete; + + ~TestProcessSnapshot() override; + + void SetProcessID(crashpad::ProcessID process_id) { + process_id_ = process_id; + } + void SetParentProcessID(crashpad::ProcessID parent_process_id) { + parent_process_id_ = parent_process_id; + } + void SetSnapshotTime(const timeval& snapshot_time) { + snapshot_time_ = snapshot_time; + } + void SetProcessStartTime(const timeval& start_time) { + process_start_time_ = start_time; + } + void SetProcessCPUTimes(const timeval& user_time, + const timeval& system_time) { + process_cpu_user_time_ = user_time; + process_cpu_system_time_ = system_time; + } + void SetReportID(const UUID& report_id) { report_id_ = report_id; } + void SetClientID(const UUID& client_id) { client_id_ = client_id; } + void SetAnnotationsSimpleMap( + const std::map& annotations_simple_map) { + annotations_simple_map_ = annotations_simple_map; + } + + //! \brief Sets the system snapshot to be returned by System(). + //! + //! \param[in] system The system snapshot that System() will return. The + //! TestProcessSnapshot object takes ownership of \a system. + void SetSystem(std::unique_ptr system) { + system_ = std::move(system); + } + + //! \brief Adds a thread snapshot to be returned by Threads(). + //! + //! \param[in] thread The thread snapshot that will be included in Threads(). + //! The TestProcessSnapshot object takes ownership of \a thread. + void AddThread(std::unique_ptr thread) { + threads_.push_back(std::move(thread)); + } + + //! \brief Adds a module snapshot to be returned by Modules(). + //! + //! \param[in] module The module snapshot that will be included in Modules(). + //! The TestProcessSnapshot object takes ownership of \a module. + void AddModule(std::unique_ptr module) { + modules_.push_back(std::move(module)); + } + + //! \brief Adds an unloaded module snapshot to be returned by + //! UnloadedModules(). + //! + //! \param[in] unloaded_module The unloaded module snapshot that will be + //! included in UnloadedModules(). + void AddModule(const UnloadedModuleSnapshot& unloaded_module) { + unloaded_modules_.push_back(unloaded_module); + } + + //! \brief Sets the exception snapshot to be returned by Exception(). + //! + //! \param[in] exception The exception snapshot that Exception() will return. + //! The TestProcessSnapshot object takes ownership of \a exception. + void SetException(std::unique_ptr exception) { + exception_ = std::move(exception); + } + + //! \brief Adds a memory map region snapshot to be returned by MemoryMap(). + //! + //! \param[in] region The memory map region snapshot that will be included in + //! MemoryMap(). The TestProcessSnapshot object takes ownership of \a + //! region. + void AddMemoryMapRegion(std::unique_ptr region) { + memory_map_.push_back(std::move(region)); + } + + //! \brief Adds a handle snapshot to be returned by Handles(). + //! + //! \param[in] handle The handle snapshot that will be included in Handles(). + void AddHandle(const HandleSnapshot& handle) { + handles_.push_back(handle); + } + + //! \brief Add a memory snapshot to be returned by ExtraMemory(). + //! + //! \param[in] extra_memory The memory snapshot that will be included in + //! ExtraMemory(). The TestProcessSnapshot object takes ownership of \a + //! extra_memory. + void AddExtraMemory(std::unique_ptr extra_memory) { + extra_memory_.push_back(std::move(extra_memory)); + } + + //! \brief Add a process memory object to be returned by Memory(). + //! + //! \param[in] process_memory The memory object that will be returned by + //! Memory(). The TestProcessSnapshot object takes ownership of \a + //! extra_memory. + void SetProcessMemory(std::unique_ptr process_memory) { + process_memory_ = std::move(process_memory); + } + + // ProcessSnapshot: + + crashpad::ProcessID ProcessID() const override; + crashpad::ProcessID ParentProcessID() const override; + void SnapshotTime(timeval* snapshot_time) const override; + void ProcessStartTime(timeval* start_time) const override; + void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override; + void ReportID(UUID* report_id) const override; + void ClientID(UUID* client_id) const override; + const std::map& AnnotationsSimpleMap() + const override; + const SystemSnapshot* System() const override; + std::vector Threads() const override; + std::vector Modules() const override; + std::vector UnloadedModules() const override; + const ExceptionSnapshot* Exception() const override; + std::vector MemoryMap() const override; + std::vector Handles() const override; + std::vector ExtraMemory() const override; + const ProcessMemory* Memory() const override; + + private: + crashpad::ProcessID process_id_; + crashpad::ProcessID parent_process_id_; + timeval snapshot_time_; + timeval process_start_time_; + timeval process_cpu_user_time_; + timeval process_cpu_system_time_; + UUID report_id_; + UUID client_id_; + std::map annotations_simple_map_; + std::unique_ptr system_; + std::vector> threads_; + std::vector> modules_; + std::vector unloaded_modules_; + std::unique_ptr exception_; + std::vector> memory_map_; + std::vector handles_; + std::vector> extra_memory_; + std::unique_ptr process_memory_; +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_TEST_TEST_PROCESS_SNAPSHOT_H_ diff --git a/shared/sentry/external/crashpad/snapshot/test/test_system_snapshot.cc b/shared/sentry/external/crashpad/snapshot/test/test_system_snapshot.cc new file mode 100644 index 000000000..0f3605cf0 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/test/test_system_snapshot.cc @@ -0,0 +1,134 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/test/test_system_snapshot.h" + +namespace crashpad { +namespace test { + +TestSystemSnapshot::TestSystemSnapshot() + : cpu_architecture_(kCPUArchitectureUnknown), + cpu_revision_(0), + cpu_count_(0), + cpu_vendor_(), + cpu_frequency_current_hz_(0), + cpu_frequency_max_hz_(0), + cpu_x86_signature_(0), + cpu_x86_features_(0), + cpu_x86_extended_features_(0), + cpu_x86_leaf_7_features_(0), + cpu_x86_supports_daz_(false), + operating_system_(kOperatingSystemUnknown), + os_server_(false), + os_version_major_(0), + os_version_minor_(0), + os_version_bugfix_(0), + os_version_build_(), + os_version_full_(), + nx_enabled_(false), + machine_description_(), + time_zone_dst_status_(kDoesNotObserveDaylightSavingTime), + time_zone_standard_offset_seconds_(0), + time_zone_daylight_offset_seconds_(0), + time_zone_standard_name_(), + time_zone_daylight_name_() { +} + +TestSystemSnapshot::~TestSystemSnapshot() { +} + +CPUArchitecture TestSystemSnapshot::GetCPUArchitecture() const { + return cpu_architecture_; +} + +uint32_t TestSystemSnapshot::CPURevision() const { + return cpu_revision_; +} + +uint8_t TestSystemSnapshot::CPUCount() const { + return cpu_count_; +} + +std::string TestSystemSnapshot::CPUVendor() const { + return cpu_vendor_; +} + +void TestSystemSnapshot::CPUFrequency(uint64_t* current_hz, + uint64_t* max_hz) const { + *current_hz = cpu_frequency_current_hz_; + *max_hz = cpu_frequency_max_hz_; +} + +uint32_t TestSystemSnapshot::CPUX86Signature() const { + return cpu_x86_signature_; +} + +uint64_t TestSystemSnapshot::CPUX86Features() const { + return cpu_x86_features_; +} + +uint64_t TestSystemSnapshot::CPUX86ExtendedFeatures() const { + return cpu_x86_extended_features_; +} + +uint32_t TestSystemSnapshot::CPUX86Leaf7Features() const { + return cpu_x86_leaf_7_features_; +} + +bool TestSystemSnapshot::CPUX86SupportsDAZ() const { + return cpu_x86_supports_daz_; +} + +SystemSnapshot::OperatingSystem TestSystemSnapshot::GetOperatingSystem() const { + return operating_system_; +} + +bool TestSystemSnapshot::OSServer() const { + return os_server_; +} + +void TestSystemSnapshot::OSVersion( + int* major, int* minor, int* bugfix, std::string* build) const { + *major = os_version_major_; + *minor = os_version_minor_; + *bugfix = os_version_bugfix_; + *build = os_version_build_; +} + +std::string TestSystemSnapshot::OSVersionFull() const { + return os_version_full_; +} + +bool TestSystemSnapshot::NXEnabled() const { + return nx_enabled_; +} + +std::string TestSystemSnapshot::MachineDescription() const { + return machine_description_; +} + +void TestSystemSnapshot::TimeZone(DaylightSavingTimeStatus* dst_status, + int* standard_offset_seconds, + int* daylight_offset_seconds, + std::string* standard_name, + std::string* daylight_name) const { + *dst_status = time_zone_dst_status_; + *standard_offset_seconds = time_zone_standard_offset_seconds_; + *daylight_offset_seconds = time_zone_daylight_offset_seconds_; + *standard_name = time_zone_standard_name_; + *daylight_name = time_zone_daylight_name_; +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/test/test_system_snapshot.h b/shared/sentry/external/crashpad/snapshot/test/test_system_snapshot.h new file mode 100644 index 000000000..612626566 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/test/test_system_snapshot.h @@ -0,0 +1,149 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_TEST_TEST_SYSTEM_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_TEST_TEST_SYSTEM_SNAPSHOT_H_ + +#include + +#include + +#include "snapshot/system_snapshot.h" + +namespace crashpad { +namespace test { + +//! \brief A test SystemSnapshot that can carry arbitrary data for testing +//! purposes. +class TestSystemSnapshot final : public SystemSnapshot { + public: + TestSystemSnapshot(); + + TestSystemSnapshot(const TestSystemSnapshot&) = delete; + TestSystemSnapshot& operator=(const TestSystemSnapshot&) = delete; + + ~TestSystemSnapshot() override; + + void SetCPUArchitecture(CPUArchitecture cpu_architecture) { + cpu_architecture_ = cpu_architecture; + } + void SetCPURevision(uint32_t cpu_revision) { cpu_revision_ = cpu_revision; } + void SetCPUCount(uint8_t cpu_count) { cpu_count_ = cpu_count; } + void SetCPUVendor(const std::string& cpu_vendor) { cpu_vendor_ = cpu_vendor; } + void SetCPUFrequency(uint64_t current_hz, uint64_t max_hz) { + cpu_frequency_current_hz_ = current_hz; + cpu_frequency_max_hz_ = max_hz; + } + void SetCPUX86Signature(uint32_t cpu_x86_signature) { + cpu_x86_signature_ = cpu_x86_signature; + } + void SetCPUX86Features(uint64_t cpu_x86_features) { + cpu_x86_features_ = cpu_x86_features; + } + void SetCPUX86ExtendedFeatures(uint64_t cpu_x86_extended_features) { + cpu_x86_extended_features_ = cpu_x86_extended_features; + } + void SetCPUX86Leaf7Features(uint32_t cpu_x86_leaf_7_features) { + cpu_x86_leaf_7_features_ = cpu_x86_leaf_7_features; + } + void SetCPUX86SupportsDAZ(bool cpu_x86_supports_daz) { + cpu_x86_supports_daz_ = cpu_x86_supports_daz; + } + void SetOperatingSystem(OperatingSystem operating_system) { + operating_system_ = operating_system; + } + void SetOSServer(bool os_server) { os_server_ = os_server; } + void SetOSVersion( + int major, int minor, int bugfix, const std::string& build) { + os_version_major_ = major; + os_version_minor_ = minor; + os_version_bugfix_ = bugfix; + os_version_build_ = build; + } + void SetOSVersionFull(const std::string& os_version_full) { + os_version_full_ = os_version_full; + } + void SetNXEnabled(bool nx_enabled) { nx_enabled_ = nx_enabled; } + void SetMachineDescription(const std::string& machine_description) { + machine_description_ = machine_description; + } + void SetTimeZone(DaylightSavingTimeStatus dst_status, + int standard_offset_seconds, + int daylight_offset_seconds, + const std::string& standard_name, + const std::string& daylight_name) { + time_zone_dst_status_ = dst_status; + time_zone_standard_offset_seconds_ = standard_offset_seconds; + time_zone_daylight_offset_seconds_ = daylight_offset_seconds; + time_zone_standard_name_ = standard_name; + time_zone_daylight_name_ = daylight_name; + } + + // SystemSnapshot: + + CPUArchitecture GetCPUArchitecture() const override; + uint32_t CPURevision() const override; + uint8_t CPUCount() const override; + std::string CPUVendor() const override; + void CPUFrequency(uint64_t* current_hz, uint64_t* max_hz) const override; + uint32_t CPUX86Signature() const override; + uint64_t CPUX86Features() const override; + uint64_t CPUX86ExtendedFeatures() const override; + uint32_t CPUX86Leaf7Features() const override; + bool CPUX86SupportsDAZ() const override; + OperatingSystem GetOperatingSystem() const override; + bool OSServer() const override; + void OSVersion( + int* major, int* minor, int* bugfix, std::string* build) const override; + std::string OSVersionFull() const override; + bool NXEnabled() const override; + std::string MachineDescription() const override; + void TimeZone(DaylightSavingTimeStatus* dst_status, + int* standard_offset_seconds, + int* daylight_offset_seconds, + std::string* standard_name, + std::string* daylight_name) const override; + + private: + CPUArchitecture cpu_architecture_; + uint32_t cpu_revision_; + uint8_t cpu_count_; + std::string cpu_vendor_; + uint64_t cpu_frequency_current_hz_; + uint64_t cpu_frequency_max_hz_; + uint32_t cpu_x86_signature_; + uint64_t cpu_x86_features_; + uint64_t cpu_x86_extended_features_; + uint32_t cpu_x86_leaf_7_features_; + bool cpu_x86_supports_daz_; + OperatingSystem operating_system_; + bool os_server_; + int os_version_major_; + int os_version_minor_; + int os_version_bugfix_; + std::string os_version_build_; + std::string os_version_full_; + bool nx_enabled_; + std::string machine_description_; + DaylightSavingTimeStatus time_zone_dst_status_; + int time_zone_standard_offset_seconds_; + int time_zone_daylight_offset_seconds_; + std::string time_zone_standard_name_; + std::string time_zone_daylight_name_; +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_TEST_TEST_SYSTEM_SNAPSHOT_H_ diff --git a/shared/sentry/external/crashpad/snapshot/test/test_thread_snapshot.cc b/shared/sentry/external/crashpad/snapshot/test/test_thread_snapshot.cc new file mode 100644 index 000000000..ed6d9fdab --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/test/test_thread_snapshot.cc @@ -0,0 +1,67 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/test/test_thread_snapshot.h" + +namespace crashpad { +namespace test { + +TestThreadSnapshot::TestThreadSnapshot() + : context_union_(), + context_(), + stack_(), + thread_id_(0), + suspend_count_(0), + priority_(0), + thread_specific_data_address_(0) { + context_.x86 = &context_union_.x86; +} + +TestThreadSnapshot::~TestThreadSnapshot() { +} + +const CPUContext* TestThreadSnapshot::Context() const { + return &context_; +} + +const MemorySnapshot* TestThreadSnapshot::Stack() const { + return stack_.get(); +} + +uint64_t TestThreadSnapshot::ThreadID() const { + return thread_id_; +} + +int TestThreadSnapshot::SuspendCount() const { + return suspend_count_; +} + +int TestThreadSnapshot::Priority() const { + return priority_; +} + +uint64_t TestThreadSnapshot::ThreadSpecificDataAddress() const { + return thread_specific_data_address_; +} + +std::vector TestThreadSnapshot::ExtraMemory() const { + std::vector extra_memory; + for (const auto& em : extra_memory_) { + extra_memory.push_back(em.get()); + } + return extra_memory; +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/test/test_thread_snapshot.h b/shared/sentry/external/crashpad/snapshot/test/test_thread_snapshot.h new file mode 100644 index 000000000..f865bdfea --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/test/test_thread_snapshot.h @@ -0,0 +1,108 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_TEST_TEST_THREAD_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_TEST_TEST_THREAD_SNAPSHOT_H_ + +#include + +#include +#include +#include + +#include "snapshot/cpu_context.h" +#include "snapshot/memory_snapshot.h" +#include "snapshot/thread_snapshot.h" + +namespace crashpad { +namespace test { + +//! \brief A test ThreadSnapshot that can carry arbitrary data for testing +//! purposes. +class TestThreadSnapshot final : public ThreadSnapshot { + public: + TestThreadSnapshot(); + + TestThreadSnapshot(const TestThreadSnapshot&) = delete; + TestThreadSnapshot& operator=(const TestThreadSnapshot&) = delete; + + ~TestThreadSnapshot(); + + //! \brief Obtains a pointer to the underlying mutable CPUContext structure. + //! + //! This method is intended to be used by callers to populate the CPUContext + //! structure. + //! + //! \return The same pointer that Context() does, while treating the data as + //! mutable. + //! + //! \attention This returns a non-`const` pointer to this object’s private + //! data so that a caller can populate the context structure directly. + //! This is done because providing setter interfaces to each field in the + //! context structure would be unwieldy and cumbersome. Care must be taken + //! to populate the context structure correctly. + CPUContext* MutableContext() { return &context_; } + + //! \brief Sets the memory region to be returned by Stack(). + //! + //! \param[in] stack The memory region that Stack() will return. The + //! TestThreadSnapshot object takes ownership of \a stack. + void SetStack(std::unique_ptr stack) { + stack_ = std::move(stack); + } + + void SetThreadID(uint64_t thread_id) { thread_id_ = thread_id; } + void SetSuspendCount(int suspend_count) { suspend_count_ = suspend_count; } + void SetPriority(int priority) { priority_ = priority; } + void SetThreadSpecificDataAddress(uint64_t thread_specific_data_address) { + thread_specific_data_address_ = thread_specific_data_address; + } + + //! \brief Add a memory snapshot to be returned by ExtraMemory(). + //! + //! \param[in] extra_memory The memory snapshot that will be included in + //! ExtraMemory(). The TestThreadSnapshot object takes ownership of \a + //! extra_memory. + void AddExtraMemory(std::unique_ptr extra_memory) { + extra_memory_.push_back(std::move(extra_memory)); + } + + // ThreadSnapshot: + + const CPUContext* Context() const override; + const MemorySnapshot* Stack() const override; + uint64_t ThreadID() const override; + int SuspendCount() const override; + int Priority() const override; + uint64_t ThreadSpecificDataAddress() const override; + std::vector ExtraMemory() const override; + + private: + union { + CPUContextX86 x86; + CPUContextX86_64 x86_64; + } context_union_; + CPUContext context_; + std::unique_ptr stack_; + uint64_t thread_id_; + int suspend_count_; + int priority_; + uint64_t thread_specific_data_address_; + std::vector> extra_memory_; +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_TEST_TEST_THREAD_SNAPSHOT_H_ diff --git a/shared/sentry/external/crashpad/snapshot/thread_snapshot.h b/shared/sentry/external/crashpad/snapshot/thread_snapshot.h new file mode 100644 index 000000000..08604a883 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/thread_snapshot.h @@ -0,0 +1,109 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_THREAD_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_THREAD_SNAPSHOT_H_ + +#include + +#ifdef CLIENT_STACKTRACES_ENABLED +#include +#endif +#include + +namespace crashpad { + +struct CPUContext; +class MemorySnapshot; + +#ifdef CLIENT_STACKTRACES_ENABLED +class FrameSnapshot { + public: + FrameSnapshot(uint64_t instruction_addr, std::string symbol) + : instruction_addr_(instruction_addr), symbol_(symbol) {} + + uint64_t InstructionAddr() const { return instruction_addr_; }; + const std::string& Symbol() const { return symbol_; }; + + private: + uint64_t instruction_addr_; + std::string symbol_; +}; +#endif + +//! \brief An abstract interface to a snapshot representing a thread +//! (lightweight process) present in a snapshot process. +class ThreadSnapshot { + public: +#ifdef CLIENT_STACKTRACES_ENABLED + ThreadSnapshot() : frames_() {} +#endif + + virtual ~ThreadSnapshot() {} + + //! \brief Returns a CPUContext object corresponding to the thread’s CPU + //! context. + //! + //! The caller does not take ownership of this object, it is scoped to the + //! lifetime of the ThreadSnapshot object that it was obtained from. + virtual const CPUContext* Context() const = 0; + + //! \brief Returns a MemorySnapshot object corresponding to the memory region + //! that contains the thread’s stack, or `nullptr` if no stack region is + //! available. + //! + //! The caller does not take ownership of this object, it is scoped to the + //! lifetime of the ThreadSnapshot object that it was obtained from. + virtual const MemorySnapshot* Stack() const = 0; + + //! \brief Returns the thread’s identifier. + //! + //! %Thread identifiers are at least unique within a process, and may be + //! unique system-wide. + virtual uint64_t ThreadID() const = 0; + + //! \brief Returns the thread’s suspend count. + //! + //! A suspend count of `0` denotes a schedulable (not suspended) thread. + virtual int SuspendCount() const = 0; + + //! \brief Returns the thread’s priority. + //! + //! Threads with higher priorities will have higher priority values. + virtual int Priority() const = 0; + + //! \brief Returns the base address of a region used to store thread-specific + //! data. + virtual uint64_t ThreadSpecificDataAddress() const = 0; + + //! \brief Returns a vector of additional memory blocks that should be + //! included in a minidump. + //! + //! \return A vector of MemorySnapshot objects that will be included in the + //! crash dump. The caller does not take ownership of these objects, they + //! are scoped to the lifetime of the ThreadSnapshot object that they + //! were obtained from. + virtual std::vector ExtraMemory() const = 0; + +#ifdef CLIENT_STACKTRACES_ENABLED + const std::vector& StackTrace() const { return frames_; } + + protected: + std::vector frames_; +#endif +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_THREAD_SNAPSHOT_H_ diff --git a/shared/sentry/external/crashpad/snapshot/unloaded_module_snapshot.cc b/shared/sentry/external/crashpad/snapshot/unloaded_module_snapshot.cc new file mode 100644 index 000000000..45f606a68 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/unloaded_module_snapshot.cc @@ -0,0 +1,33 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/unloaded_module_snapshot.h" + +namespace crashpad { + +UnloadedModuleSnapshot::UnloadedModuleSnapshot(uint64_t address, + uint64_t size, + uint32_t checksum, + uint32_t timestamp, + const std::string& name) + : name_(name), + address_(address), + size_(size), + checksum_(checksum), + timestamp_(timestamp) {} + +UnloadedModuleSnapshot::~UnloadedModuleSnapshot() { +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/unloaded_module_snapshot.h b/shared/sentry/external/crashpad/snapshot/unloaded_module_snapshot.h new file mode 100644 index 000000000..03e100e1d --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/unloaded_module_snapshot.h @@ -0,0 +1,61 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_UNLOADED_MODULE_SNAPSHOT_H_ +#define CRASHPAD_SNAPSHOT_UNLOADED_MODULE_SNAPSHOT_H_ + +#include + +#include + +namespace crashpad { + +//! \brief Information about an unloaded module that was previously loaded into +//! a snapshot process. +class UnloadedModuleSnapshot { + public: + UnloadedModuleSnapshot(uint64_t address, + uint64_t size, + uint32_t checksum, + uint32_t timestamp, + const std::string& name); + ~UnloadedModuleSnapshot(); + + //! \brief The base address of the module in the target processes' address + //! space. + uint64_t Address() const { return address_; } + + //! \brief The size of the module. + uint64_t Size() const { return size_; } + + //! \brief The checksum of the image. + uint32_t Checksum() const { return checksum_; } + + //! \brief The time and date stamp in `time_t` format. + uint32_t Timestamp() const { return timestamp_; } + + //! \brief The name of the module. + std::string Name() const { return name_; } + + private: + std::string name_; + uint64_t address_; + uint64_t size_; + uint32_t checksum_; + uint32_t timestamp_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_UNLOADED_MODULE_SNAPSHOT_H_ diff --git a/shared/sentry/external/crashpad/snapshot/win/capture_memory_delegate_win.cc b/shared/sentry/external/crashpad/snapshot/win/capture_memory_delegate_win.cc new file mode 100644 index 000000000..43067caa6 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/capture_memory_delegate_win.cc @@ -0,0 +1,73 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/win/capture_memory_delegate_win.h" + +#include + +#include "base/numerics/safe_conversions.h" +#include "snapshot/memory_snapshot_generic.h" + +namespace crashpad { +namespace internal { + +CaptureMemoryDelegateWin::CaptureMemoryDelegateWin( + ProcessReaderWin* process_reader, + const ProcessReaderWin::Thread& thread, + std::vector>* snapshots, + uint32_t* budget_remaining) + : stack_(thread.stack_region_address, thread.stack_region_size), + process_reader_(process_reader), + snapshots_(snapshots), + budget_remaining_(budget_remaining) {} + +bool CaptureMemoryDelegateWin::Is64Bit() const { + return process_reader_->Is64Bit(); +} + +bool CaptureMemoryDelegateWin::ReadMemory(uint64_t at, + uint64_t num_bytes, + void* into) const { + return process_reader_->Memory()->Read( + at, base::checked_cast(num_bytes), into); +} + +std::vector> CaptureMemoryDelegateWin::GetReadableRanges( + const CheckedRange& range) const { + return process_reader_->GetProcessInfo().GetReadableRanges(range); +} + +void CaptureMemoryDelegateWin::AddNewMemorySnapshot( + const CheckedRange& range) { + // Don't bother storing this memory if it points back into the stack. + if (stack_.ContainsRange(range)) + return; + if (range.size() == 0) + return; + if (!budget_remaining_ || *budget_remaining_ == 0) + return; + snapshots_->push_back(std::make_unique()); + internal::MemorySnapshotGeneric* snapshot = snapshots_->back().get(); + snapshot->Initialize(process_reader_->Memory(), range.base(), range.size()); + if (!base::IsValueInRangeForNumericType(range.size())) { + *budget_remaining_ = 0; + } else { + int64_t temp = *budget_remaining_; + temp -= range.size(); + *budget_remaining_ = base::saturated_cast(temp); + } +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/win/capture_memory_delegate_win.h b/shared/sentry/external/crashpad/snapshot/win/capture_memory_delegate_win.h new file mode 100644 index 000000000..85c3db97a --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/capture_memory_delegate_win.h @@ -0,0 +1,70 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_WIN_CAPTURE_MEMORY_DELEGATE_WIN_H_ +#define CRASHPAD_SNAPSHOT_WIN_CAPTURE_MEMORY_DELEGATE_WIN_H_ + +#include "snapshot/capture_memory.h" + +#include + +#include +#include + +#include "snapshot/win/process_reader_win.h" +#include "util/numeric/checked_range.h" + +namespace crashpad { +namespace internal { + +class MemorySnapshotGeneric; + +class CaptureMemoryDelegateWin : public CaptureMemory::Delegate { + public: + //! \brief A MemoryCaptureDelegate for Windows. + //! + //! \param[in] process_reader A ProcessReaderWin for the target process. + //! \param[in] thread The thread being inspected. Memory ranges overlapping + //! this thread's stack will be ignored on the assumption that they're + //! already captured elsewhere. + //! \param[in] snapshots A vector of MemorySnapshotGeneric to which the + //! captured memory will be added. + //! \param[in] budget_remaining If non-null, a pointer to the remaining number + //! of bytes to capture. If this is `0`, no further memory will be + //! captured. + CaptureMemoryDelegateWin( + ProcessReaderWin* process_reader, + const ProcessReaderWin::Thread& thread, + std::vector>* snapshots, + uint32_t* budget_remaining); + + // MemoryCaptureDelegate: + bool Is64Bit() const override; + bool ReadMemory(uint64_t at, uint64_t num_bytes, void* into) const override; + std::vector> GetReadableRanges( + const CheckedRange& range) const override; + void AddNewMemorySnapshot( + const CheckedRange& range) override; + + private: + CheckedRange stack_; + ProcessReaderWin* process_reader_; // weak + std::vector>* snapshots_; // weak + uint32_t* budget_remaining_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_WIN_CAPTURE_MEMORY_DELEGATE_WIN_H_ diff --git a/shared/sentry/external/crashpad/snapshot/win/cpu_context_win.cc b/shared/sentry/external/crashpad/snapshot/win/cpu_context_win.cc new file mode 100644 index 000000000..db2e80362 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/cpu_context_win.cc @@ -0,0 +1,233 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/win/cpu_context_win.h" + +#include +#include +#include + +#include "base/logging.h" +#include "snapshot/cpu_context.h" + +namespace crashpad { + +namespace { + +// Validation for casts used with CPUContextX86::FsaveToFxsave(). +static_assert(sizeof(CPUContextX86::Fsave) == + offsetof(WOW64_FLOATING_SAVE_AREA, Cr0NpxState), + "WoW64 fsave types must be equivalent"); +#if defined(ARCH_CPU_X86) +static_assert(sizeof(CPUContextX86::Fsave) == + offsetof(FLOATING_SAVE_AREA, Spare0), + "fsave types must be equivalent"); +#endif // ARCH_CPU_X86 + +template +bool HasContextPart(const T& context, uint32_t bits) { + return (context.ContextFlags & bits) == bits; +} + +template +void CommonInitializeX86Context(const T& context, CPUContextX86* out) { + // This function assumes that the WOW64_CONTEXT_* and x86 CONTEXT_* values + // for ContextFlags are identical. This can be tested when targeting 32-bit + // x86. +#if defined(ARCH_CPU_X86) + static_assert(sizeof(CONTEXT) == sizeof(WOW64_CONTEXT), + "type mismatch: CONTEXT"); +#define ASSERT_WOW64_EQUIVALENT(x) \ + do { \ + static_assert(x == WOW64_##x, "value mismatch: " #x); \ + } while (false) + ASSERT_WOW64_EQUIVALENT(CONTEXT_i386); + ASSERT_WOW64_EQUIVALENT(CONTEXT_i486); + ASSERT_WOW64_EQUIVALENT(CONTEXT_CONTROL); + ASSERT_WOW64_EQUIVALENT(CONTEXT_INTEGER); + ASSERT_WOW64_EQUIVALENT(CONTEXT_SEGMENTS); + ASSERT_WOW64_EQUIVALENT(CONTEXT_FLOATING_POINT); + ASSERT_WOW64_EQUIVALENT(CONTEXT_DEBUG_REGISTERS); + ASSERT_WOW64_EQUIVALENT(CONTEXT_EXTENDED_REGISTERS); + ASSERT_WOW64_EQUIVALENT(CONTEXT_FULL); + ASSERT_WOW64_EQUIVALENT(CONTEXT_ALL); + ASSERT_WOW64_EQUIVALENT(CONTEXT_XSTATE); +#undef ASSERT_WOW64_EQUIVALENT +#endif // ARCH_CPU_X86 + + memset(out, 0, sizeof(*out)); + + LOG_IF(ERROR, !HasContextPart(context, WOW64_CONTEXT_i386)) + << "non-x86 context"; + + if (HasContextPart(context, WOW64_CONTEXT_CONTROL)) { + out->ebp = context.Ebp; + out->eip = context.Eip; + out->cs = static_cast(context.SegCs); + out->eflags = context.EFlags; + out->esp = context.Esp; + out->ss = static_cast(context.SegSs); + } + + if (HasContextPart(context, WOW64_CONTEXT_INTEGER)) { + out->eax = context.Eax; + out->ebx = context.Ebx; + out->ecx = context.Ecx; + out->edx = context.Edx; + out->edi = context.Edi; + out->esi = context.Esi; + } + + if (HasContextPart(context, WOW64_CONTEXT_SEGMENTS)) { + out->ds = static_cast(context.SegDs); + out->es = static_cast(context.SegEs); + out->fs = static_cast(context.SegFs); + out->gs = static_cast(context.SegGs); + } + + if (HasContextPart(context, WOW64_CONTEXT_DEBUG_REGISTERS)) { + out->dr0 = context.Dr0; + out->dr1 = context.Dr1; + out->dr2 = context.Dr2; + out->dr3 = context.Dr3; + + // DR4 and DR5 are obsolete synonyms for DR6 and DR7, see + // https://en.wikipedia.org/wiki/X86_debug_register. + out->dr4 = context.Dr6; + out->dr5 = context.Dr7; + + out->dr6 = context.Dr6; + out->dr7 = context.Dr7; + } + + if (HasContextPart(context, WOW64_CONTEXT_EXTENDED_REGISTERS)) { + static_assert(sizeof(out->fxsave) == sizeof(context.ExtendedRegisters), + "fxsave types must be equivalent"); + memcpy(&out->fxsave, &context.ExtendedRegisters, sizeof(out->fxsave)); + } else if (HasContextPart(context, WOW64_CONTEXT_FLOATING_POINT)) { + // The static_assert that validates this cast can’t be here because it + // relies on field names that vary based on the template parameter. + CPUContextX86::FsaveToFxsave( + *reinterpret_cast(&context.FloatSave), + &out->fxsave); + } +} + +} // namespace + +#if defined(ARCH_CPU_X86) + +void InitializeX86Context(const CONTEXT& context, CPUContextX86* out) { + CommonInitializeX86Context(context, out); +} + +#elif defined(ARCH_CPU_X86_64) + +void InitializeX86Context(const WOW64_CONTEXT& context, CPUContextX86* out) { + CommonInitializeX86Context(context, out); +} + +void InitializeX64Context(const CONTEXT& context, CPUContextX86_64* out) { + memset(out, 0, sizeof(*out)); + + LOG_IF(ERROR, !HasContextPart(context, CONTEXT_AMD64)) << "non-x64 context"; + + if (HasContextPart(context, CONTEXT_CONTROL)) { + out->cs = context.SegCs; + out->rflags = context.EFlags; + out->rip = context.Rip; + out->rsp = context.Rsp; + // SegSs ignored. + } + + if (HasContextPart(context, CONTEXT_INTEGER)) { + out->rax = context.Rax; + out->rbx = context.Rbx; + out->rcx = context.Rcx; + out->rdx = context.Rdx; + out->rdi = context.Rdi; + out->rsi = context.Rsi; + out->rbp = context.Rbp; + out->r8 = context.R8; + out->r9 = context.R9; + out->r10 = context.R10; + out->r11 = context.R11; + out->r12 = context.R12; + out->r13 = context.R13; + out->r14 = context.R14; + out->r15 = context.R15; + } + + if (HasContextPart(context, CONTEXT_SEGMENTS)) { + out->fs = context.SegFs; + out->gs = context.SegGs; + // SegDs ignored. + // SegEs ignored. + } + + if (HasContextPart(context, CONTEXT_DEBUG_REGISTERS)) { + out->dr0 = context.Dr0; + out->dr1 = context.Dr1; + out->dr2 = context.Dr2; + out->dr3 = context.Dr3; + + // DR4 and DR5 are obsolete synonyms for DR6 and DR7, see + // https://en.wikipedia.org/wiki/X86_debug_register. + out->dr4 = context.Dr6; + out->dr5 = context.Dr7; + + out->dr6 = context.Dr6; + out->dr7 = context.Dr7; + } + + if (HasContextPart(context, CONTEXT_FLOATING_POINT)) { + static_assert(sizeof(out->fxsave) == sizeof(context.FltSave), + "types must be equivalent"); + memcpy(&out->fxsave, &context.FltSave, sizeof(out->fxsave)); + } +} + +#elif defined(ARCH_CPU_ARM64) + +void InitializeARM64Context(const CONTEXT& context, CPUContextARM64* out) { + memset(out, 0, sizeof(*out)); + + LOG_IF(ERROR, !HasContextPart(context, CONTEXT_ARM64)) << "non-arm64 context"; + + if (HasContextPart(context, CONTEXT_CONTROL)) { + out->spsr = context.Cpsr; + out->pc = context.Pc; + out->regs[30] = context.Lr; + out->sp = context.Sp; + out->regs[29] = context.Fp; + } + + if (HasContextPart(context, CONTEXT_INTEGER)) { + memcpy(&out->regs[0], &context.X0, 18 * sizeof(context.X0)); + // Don't copy x18 which is reserved as platform register. + memcpy(&out->regs[19], &context.X19, 10 * sizeof(context.X0)); + } + + if (HasContextPart(context, CONTEXT_FLOATING_POINT)) { + static_assert(sizeof(out->fpsimd) == sizeof(context.V), + "types must be equivalent"); + memcpy(&out->fpsimd, &context.V, sizeof(out->fpsimd)); + } +} + +#else +#error Unsupported Windows Arch +#endif // ARCH_CPU_X86 + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/win/cpu_context_win.h b/shared/sentry/external/crashpad/snapshot/win/cpu_context_win.h new file mode 100644 index 000000000..9718f49cb --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/cpu_context_win.h @@ -0,0 +1,58 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_WIN_CPU_CONTEXT_WIN_H_ +#define CRASHPAD_SNAPSHOT_WIN_CPU_CONTEXT_WIN_H_ + +#include + +#include "build/build_config.h" + +namespace crashpad { + +struct CPUContextX86; +struct CPUContextX86_64; +struct CPUContextARM64; + +#if defined(ARCH_CPU_X86) || DOXYGEN + +//! \brief Initializes a CPUContextX86 structure from a native context structure +//! on Windows. +void InitializeX86Context(const CONTEXT& context, CPUContextX86* out); + +#endif // ARCH_CPU_X86 + +#if defined(ARCH_CPU_X86_64) || DOXYGEN + +//! \brief Initializes a CPUContextX86 structure from a native context structure +//! on Windows. +void InitializeX86Context(const WOW64_CONTEXT& context, CPUContextX86* out); + +//! \brief Initializes a CPUContextX86_64 structure from a native context +//! structure on Windows. +void InitializeX64Context(const CONTEXT& context, CPUContextX86_64* out); + +#endif // ARCH_CPU_X86_64 + +#if defined(ARCH_CPU_ARM64) || DOXYGEN + +//! \brief Initializes a CPUContextARM64 structure from a native context +//! structure on Windows. +void InitializeARM64Context(const CONTEXT& context, CPUContextARM64* out); + +#endif // ARCH_CPU_ARM64 + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_WIN_CPU_CONTEXT_WIN_H_ diff --git a/shared/sentry/external/crashpad/snapshot/win/cpu_context_win_test.cc b/shared/sentry/external/crashpad/snapshot/win/cpu_context_win_test.cc new file mode 100644 index 000000000..ab8b8b313 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/cpu_context_win_test.cc @@ -0,0 +1,149 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/win/cpu_context_win.h" + +#include + +#include + +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "snapshot/cpu_context.h" +#include "test/hex_string.h" + +namespace crashpad { +namespace test { +namespace { + +template +void TestInitializeX86Context() { + T context = {0}; + context.ContextFlags = WOW64_CONTEXT_INTEGER | + WOW64_CONTEXT_DEBUG_REGISTERS | + WOW64_CONTEXT_EXTENDED_REGISTERS; + context.Eax = 1; + context.Dr0 = 3; + context.ExtendedRegisters[4] = 2; // FTW + + // Test the simple case, where everything in the CPUContextX86 argument is set + // directly from the supplied thread, float, and debug state parameters. + { + CPUContextX86 cpu_context_x86 = {}; + InitializeX86Context(context, &cpu_context_x86); + EXPECT_EQ(cpu_context_x86.eax, 1u); + EXPECT_EQ(cpu_context_x86.fxsave.ftw, 2u); + EXPECT_EQ(cpu_context_x86.dr0, 3u); + } +} + +template +void TestInitializeX86Context_FsaveWithoutFxsave() { + T context = {0}; + context.ContextFlags = WOW64_CONTEXT_INTEGER | + WOW64_CONTEXT_FLOATING_POINT | + WOW64_CONTEXT_DEBUG_REGISTERS; + context.Eax = 1; + + // In fields that are wider than they need to be, set the high bits to ensure + // that they’re masked off appropriately in the output. + context.FloatSave.ControlWord = 0xffff027f; + context.FloatSave.StatusWord = 0xffff0004; + context.FloatSave.TagWord = 0xffffa9ff; + context.FloatSave.ErrorOffset = 0x01234567; + context.FloatSave.ErrorSelector = 0x0bad0003; + context.FloatSave.DataOffset = 0x89abcdef; + context.FloatSave.DataSelector = 0xffff0007; + context.FloatSave.RegisterArea[77] = 0x80; + context.FloatSave.RegisterArea[78] = 0xff; + context.FloatSave.RegisterArea[79] = 0x7f; + + context.Dr0 = 3; + + { + CPUContextX86 cpu_context_x86 = {}; + InitializeX86Context(context, &cpu_context_x86); + + EXPECT_EQ(cpu_context_x86.eax, 1u); + + EXPECT_EQ(cpu_context_x86.fxsave.fcw, 0x027f); + EXPECT_EQ(cpu_context_x86.fxsave.fsw, 0x0004); + EXPECT_EQ(cpu_context_x86.fxsave.ftw, 0x00f0); + EXPECT_EQ(cpu_context_x86.fxsave.fop, 0x0bad); + EXPECT_EQ(cpu_context_x86.fxsave.fpu_ip, 0x01234567u); + EXPECT_EQ(cpu_context_x86.fxsave.fpu_cs, 0x0003); + EXPECT_EQ(cpu_context_x86.fxsave.fpu_dp, 0x89abcdefu); + EXPECT_EQ(cpu_context_x86.fxsave.fpu_ds, 0x0007); + for (size_t st_mm = 0; st_mm < 7; ++st_mm) { + EXPECT_EQ( + BytesToHexString(cpu_context_x86.fxsave.st_mm[st_mm].st, + std::size(cpu_context_x86.fxsave.st_mm[st_mm].st)), + std::string(std::size(cpu_context_x86.fxsave.st_mm[st_mm].st) * 2, + '0')) + << "st_mm " << st_mm; + } + EXPECT_EQ(BytesToHexString(cpu_context_x86.fxsave.st_mm[7].st, + std::size(cpu_context_x86.fxsave.st_mm[7].st)), + "0000000000000080ff7f"); + + EXPECT_EQ(cpu_context_x86.dr0, 3u); + } +} + +#if defined(ARCH_CPU_X86_FAMILY) + +#if defined(ARCH_CPU_X86_64) + +TEST(CPUContextWin, InitializeX64Context) { + CONTEXT context = {0}; + context.Rax = 10; + context.FltSave.TagWord = 11; + context.Dr0 = 12; + context.ContextFlags = + CONTEXT_INTEGER | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS; + + // Test the simple case, where everything in the CPUContextX86_64 argument is + // set directly from the supplied thread, float, and debug state parameters. + { + CPUContextX86_64 cpu_context_x86_64 = {}; + InitializeX64Context(context, &cpu_context_x86_64); + EXPECT_EQ(cpu_context_x86_64.rax, 10u); + EXPECT_EQ(cpu_context_x86_64.fxsave.ftw, 11u); + EXPECT_EQ(cpu_context_x86_64.dr0, 12u); + } +} + +#endif // ARCH_CPU_X86_64 + +TEST(CPUContextWin, InitializeX86Context) { +#if defined(ARCH_CPU_X86) + TestInitializeX86Context(); +#else // ARCH_CPU_X86 + TestInitializeX86Context(); +#endif // ARCH_CPU_X86 +} + +TEST(CPUContextWin, InitializeX86Context_FsaveWithoutFxsave) { +#if defined(ARCH_CPU_X86) + TestInitializeX86Context_FsaveWithoutFxsave(); +#else // ARCH_CPU_X86 + TestInitializeX86Context_FsaveWithoutFxsave(); +#endif // ARCH_CPU_X86 +} + +#endif // ARCH_CPU_X86_FAMILY + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/win/crashpad_snapshot_test_annotations.cc b/shared/sentry/external/crashpad/snapshot/win/crashpad_snapshot_test_annotations.cc new file mode 100644 index 000000000..d46c3d725 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/crashpad_snapshot_test_annotations.cc @@ -0,0 +1,69 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 + +#include "base/check.h" +#include "client/annotation.h" +#include "client/annotation_list.h" +#include "client/crashpad_info.h" +#include "util/file/file_io.h" + +int wmain(int argc, wchar_t* argv[]) { + crashpad::CrashpadInfo* crashpad_info = + crashpad::CrashpadInfo::GetCrashpadInfo(); + + // This is "leaked" to crashpad_info. + crashpad::SimpleStringDictionary* simple_annotations = + new crashpad::SimpleStringDictionary(); + simple_annotations->SetKeyValue("#TEST# pad", "break"); + simple_annotations->SetKeyValue("#TEST# key", "value"); + simple_annotations->SetKeyValue("#TEST# pad", "crash"); + simple_annotations->SetKeyValue("#TEST# x", "y"); + simple_annotations->SetKeyValue("#TEST# longer", "shorter"); + simple_annotations->SetKeyValue("#TEST# empty_value", ""); + + crashpad_info->set_simple_annotations(simple_annotations); + + // Set the annotation objects. + crashpad::AnnotationList::Register(); + + static crashpad::StringAnnotation<32> annotation_one("#TEST# one"); + static crashpad::StringAnnotation<32> annotation_two("#TEST# two"); + static crashpad::StringAnnotation<32> annotation_three("#TEST# same-name"); + static crashpad::StringAnnotation<32> annotation_four("#TEST# same-name"); + + annotation_one.Set("moocow"); + annotation_two.Set("this will be cleared"); + annotation_three.Set("same-name 3"); + annotation_four.Set("same-name 4"); + annotation_two.Clear(); + + // Tell the parent that the environment has been set up. + HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); + PCHECK(out != INVALID_HANDLE_VALUE) << "GetStdHandle"; + char c = ' '; + crashpad::CheckedWriteFile(out, &c, sizeof(c)); + + HANDLE in = GetStdHandle(STD_INPUT_HANDLE); + PCHECK(in != INVALID_HANDLE_VALUE) << "GetStdHandle"; + crashpad::CheckedReadFileExactly(in, &c, sizeof(c)); + CHECK(c == 'd' || c == ' '); + + // If 'd' we crash with a debug break, otherwise exit normally. + if (c == 'd') + __debugbreak(); + + return 0; +} diff --git a/shared/sentry/external/crashpad/snapshot/win/crashpad_snapshot_test_crashing_child.cc b/shared/sentry/external/crashpad/snapshot/win/crashpad_snapshot_test_crashing_child.cc new file mode 100644 index 000000000..724bd5724 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/crashpad_snapshot_test_crashing_child.cc @@ -0,0 +1,53 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 +#include + +#include "base/check_op.h" +#include "client/crashpad_client.h" +#include "util/misc/capture_context.h" +#include "util/win/address_types.h" +#include "util/win/context_wrappers.h" + +int wmain(int argc, wchar_t* argv[]) { + CHECK_EQ(argc, 2); + + crashpad::CrashpadClient client; + CHECK(client.SetHandlerIPCPipe(argv[1])); + + HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); + PCHECK(out != INVALID_HANDLE_VALUE) << "GetStdHandle"; + + CONTEXT context; + crashpad::CaptureContext(&context); + crashpad::WinVMAddress break_address = + reinterpret_cast( + crashpad::ProgramCounterFromCONTEXT(&context)); + + // This does not used CheckedWriteFile() because at high optimization + // settings, a lot of logging code can be inlined, causing there to be a large + // number of instructions between where the IP is captured and the actual + // __debugbreak(). Instead call Windows' WriteFile() to minimize the amount of + // code here. Because the next line is going to crash in any case, there's + // minimal difference in behavior aside from an indication of what broke when + // the other end experiences a ReadFile() error. + DWORD bytes_written; + WriteFile( + out, &break_address, sizeof(break_address), &bytes_written, nullptr); + + __debugbreak(); + + return 0; +} diff --git a/shared/sentry/external/crashpad/snapshot/win/crashpad_snapshot_test_dump_without_crashing.cc b/shared/sentry/external/crashpad/snapshot/win/crashpad_snapshot_test_dump_without_crashing.cc new file mode 100644 index 000000000..eeeddf53f --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/crashpad_snapshot_test_dump_without_crashing.cc @@ -0,0 +1,54 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 +#include + +#include "base/check_op.h" +#include "client/crashpad_client.h" +#include "client/simulate_crash.h" +#include "util/misc/capture_context.h" +#include "util/win/address_types.h" +#include "util/win/context_wrappers.h" + +int wmain(int argc, wchar_t* argv[]) { + CHECK_EQ(argc, 2); + + crashpad::CrashpadClient client; + CHECK(client.SetHandlerIPCPipe(argv[1])); + + HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); + PCHECK(out != INVALID_HANDLE_VALUE) << "GetStdHandle"; + + CONTEXT context; + crashpad::CaptureContext(&context); + crashpad::WinVMAddress break_address = + reinterpret_cast( + crashpad::ProgramCounterFromCONTEXT(&context)); + + // This does not used CheckedWriteFile() because at high optimization + // settings, a lot of logging code can be inlined, causing there to be a large + // number of instructions between where the IP is captured and the actual + // __debugbreak(). Instead call Windows' WriteFile() to minimize the amount of + // code here. Because the next line is going to crash in any case, there's + // minimal difference in behavior aside from an indication of what broke when + // the other end experiences a ReadFile() error. + DWORD bytes_written; + WriteFile( + out, &break_address, sizeof(break_address), &bytes_written, nullptr); + + CRASHPAD_SIMULATE_CRASH(); + + return 0; +} diff --git a/shared/sentry/external/crashpad/snapshot/win/crashpad_snapshot_test_extra_memory_ranges.cc b/shared/sentry/external/crashpad/snapshot/win/crashpad_snapshot_test_extra_memory_ranges.cc new file mode 100644 index 000000000..6a1841035 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/crashpad_snapshot_test_extra_memory_ranges.cc @@ -0,0 +1,54 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// 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 + +#include "base/check.h" +#include "client/crashpad_info.h" +#include "util/file/file_io.h" + + +int wmain(int argc, wchar_t* argv[]) { + using namespace crashpad; + + CrashpadInfo* crashpad_info = CrashpadInfo::GetCrashpadInfo(); + + // This is "leaked" to crashpad_info. + SimpleAddressRangeBag* extra_ranges = new SimpleAddressRangeBag(); + extra_ranges->Insert(CheckedRange(0, 1)); + extra_ranges->Insert(CheckedRange(1, 0)); + extra_ranges->Insert(CheckedRange(0x1000000000ULL, 0x1000)); + extra_ranges->Insert(CheckedRange(0x2000, 0x2000000000ULL)); + extra_ranges->Insert(CheckedRange(1234, 5678)); + extra_ranges->Insert(CheckedRange(1234, 5678)); + extra_ranges->Insert(CheckedRange(1234, 5678)); + crashpad_info->set_extra_memory_ranges(extra_ranges); + + // Tell the parent that the environment has been set up. + HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); + PCHECK(out != INVALID_HANDLE_VALUE) << "GetStdHandle"; + char c = ' '; + CheckedWriteFile(out, &c, sizeof(c)); + + HANDLE in = GetStdHandle(STD_INPUT_HANDLE); + PCHECK(in != INVALID_HANDLE_VALUE) << "GetStdHandle"; + CheckedReadFileExactly(in, &c, sizeof(c)); + CHECK(c == 'd' || c == ' '); + + // If 'd' we crash with a debug break, otherwise exit normally. + if (c == 'd') + __debugbreak(); + + return 0; +} diff --git a/shared/sentry/external/crashpad/snapshot/win/crashpad_snapshot_test_image_reader.cc b/shared/sentry/external/crashpad/snapshot/win/crashpad_snapshot_test_image_reader.cc new file mode 100644 index 000000000..65c778318 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/crashpad_snapshot_test_image_reader.cc @@ -0,0 +1,90 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 + +#include + +#include "base/logging.h" +#include "client/crashpad_info.h" +#include "util/file/file_io.h" +#include "util/synchronization/semaphore.h" +#include "util/win/scoped_handle.h" + +namespace { + +DWORD WINAPI LotsOfReferencesThreadProc(void* param) { + crashpad::Semaphore* semaphore = + reinterpret_cast(param); + + // Allocate a bunch of pointers to things on the stack. + int* pointers[1000]; + for (size_t i = 0; i < std::size(pointers); ++i) { + pointers[i] = new int[2048]; + } + + semaphore->Signal(); + Sleep(INFINITE); + return 0; +} + +} // namespace + +int wmain(int argc, wchar_t* argv[]) { + CHECK_EQ(argc, 2); + + crashpad::ScopedKernelHANDLE done(CreateEvent(nullptr, true, false, argv[1])); + PCHECK(done.is_valid()) << "CreateEvent"; + + PCHECK(LoadLibrary(L"crashpad_snapshot_test_image_reader_module.dll")) + << "LoadLibrary"; + + // Create threads with lots of stack pointers to memory. This is used to + // verify the cap on pointed-to memory. + crashpad::Semaphore semaphore(0); + crashpad::ScopedKernelHANDLE threads[100]; + for (size_t i = 0; i < std::size(threads); ++i) { + threads[i].reset(CreateThread(nullptr, + 0, + &LotsOfReferencesThreadProc, + reinterpret_cast(&semaphore), + 0, + nullptr)); + if (!threads[i].is_valid()) { + PLOG(ERROR) << "CreateThread"; + return 1; + } + } + + for (size_t i = 0; i < std::size(threads); ++i) { + semaphore.Wait(); + } + + crashpad::CrashpadInfo* crashpad_info = + crashpad::CrashpadInfo::GetCrashpadInfo(); + crashpad_info->set_gather_indirectly_referenced_memory( + crashpad::TriState::kEnabled, 100000); + + // Tell the parent process we're ready to proceed. + HANDLE out = GetStdHandle(STD_OUTPUT_HANDLE); + PCHECK(out != INVALID_HANDLE_VALUE) << "GetStdHandle"; + char c = ' '; + crashpad::CheckedWriteFile(out, &c, sizeof(c)); + + // Parent process says we can exit. + PCHECK(WaitForSingleObject(done.get(), INFINITE) == WAIT_OBJECT_0) + << "WaitForSingleObject"; + + return 0; +} diff --git a/shared/sentry/external/crashpad/snapshot/win/crashpad_snapshot_test_image_reader_module.cc b/shared/sentry/external/crashpad/snapshot/win/crashpad_snapshot_test_image_reader_module.cc new file mode 100644 index 000000000..6e81de957 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/crashpad_snapshot_test_image_reader_module.cc @@ -0,0 +1,19 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 + +BOOL WINAPI DllMain(HINSTANCE hinstance, DWORD reason, LPVOID reserved) { + return TRUE; +} diff --git a/shared/sentry/external/crashpad/snapshot/win/end_to_end_test.py b/shared/sentry/external/crashpad/snapshot/win/end_to_end_test.py new file mode 100755 index 000000000..9d319078d --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/end_to_end_test.py @@ -0,0 +1,488 @@ +#!/usr/bin/env python3 + +# Copyright 2015 The Crashpad Authors. All rights reserved. +# +# 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 os +import platform +import pywintypes +import random +import re +import struct +import subprocess +import sys +import tempfile +import time +import win32con +import win32pipe +import winerror + +g_temp_dirs = [] +g_had_failures = False + + +def MakeTempDir(): + global g_temp_dirs + new_dir = tempfile.mkdtemp() + g_temp_dirs.append(new_dir) + return new_dir + + +def CleanUpTempDirs(): + global g_temp_dirs + for d in g_temp_dirs: + subprocess.call(['rmdir', '/s', '/q', d], shell=True) + + +def FindInstalledWindowsApplication(app_path): + search_paths = [ + os.getenv('PROGRAMFILES(X86)'), + os.getenv('PROGRAMFILES'), + os.getenv('PROGRAMW6432'), + os.getenv('LOCALAPPDATA') + ] + search_paths += os.getenv('PATH', '').split(os.pathsep) + + for search_path in search_paths: + if not search_path: + continue + path = os.path.join(search_path, app_path) + if os.path.isfile(path): + return path + + return None + + +def GetCdbPath(): + """Search in some reasonable places to find cdb.exe. Searches x64 before x86 + and newer versions before older versions. + """ + possible_paths = ( + os.path.join('Windows Kits', '10', 'Debuggers', 'x64'), + os.path.join('Windows Kits', '10', 'Debuggers', 'x86'), + os.path.join('Windows Kits', '8.1', 'Debuggers', 'x64'), + os.path.join('Windows Kits', '8.1', 'Debuggers', 'x86'), + os.path.join('Windows Kits', '8.0', 'Debuggers', 'x64'), + os.path.join('Windows Kits', '8.0', 'Debuggers', 'x86'), + 'Debugging Tools For Windows (x64)', + 'Debugging Tools For Windows (x86)', + 'Debugging Tools For Windows', + ) + for possible_path in possible_paths: + app_path = os.path.join(possible_path, 'cdb.exe') + app_path = FindInstalledWindowsApplication(app_path) + if app_path: + return app_path + return None + + +def NamedPipeExistsAndReady(pipe_name): + """Returns False if pipe_name does not exist. If pipe_name does exist, + blocks until the pipe is ready to service clients, and then returns True. + + This is used as a drop-in replacement for os.path.exists() and os.access() + to test for the pipe's existence. Both of those calls tickle the pipe in a + way that appears to the server to be a client connecting, triggering error + messages when no data is received. + + Although this function only needs to test pipe existence (waiting for + CreateNamedPipe()), it actually winds up testing pipe readiness (waiting for + ConnectNamedPipe()). This is unnecessary but harmless. + """ + try: + win32pipe.WaitNamedPipe(pipe_name, win32pipe.NMPWAIT_WAIT_FOREVER) + except pywintypes.error as e: + if e.winerror == winerror.ERROR_FILE_NOT_FOUND: + return False + raise + return True + + +def GetDumpFromProgram(out_dir, pipe_name, executable_name, expect_exit_code, + *args): + """Initialize a crash database, and run |executable_name| connecting to a + crash handler. If pipe_name is set, crashpad_handler will be started first. + If pipe_name is empty, the executable is responsible for starting + crashpad_handler. *args will be passed after other arguments to + executable_name. If the child process does not exit with |expect_exit_code|, + an exception will be raised. Returns the path to the minidump generated by + crashpad_handler for further testing. + """ + test_database = MakeTempDir() + handler = None + + try: + subprocess.check_call([ + os.path.join(out_dir, 'crashpad_database_util.exe'), '--create', + '--database=' + test_database + ]) + + if pipe_name is not None: + handler = subprocess.Popen([ + os.path.join(out_dir, 'crashpad_handler.com'), + '--pipe-name=' + pipe_name, '--database=' + test_database + ]) + + # Wait until the server is ready. + printed = False + while not NamedPipeExistsAndReady(pipe_name): + if not printed: + print('Waiting for crashpad_handler to be ready...') + printed = True + time.sleep(0.001) + + command = [os.path.join(out_dir, executable_name), pipe_name + ] + list(args) + else: + command = ([ + os.path.join(out_dir, executable_name), + os.path.join(out_dir, 'crashpad_handler.com'), test_database + ] + list(args)) + print('Running %s' % os.path.basename(command[0])) + exit_code = subprocess.call(command) + + # Some win32con codes are negative signed integers, whereas all exit + # codes are unsigned integers. Convert from signed to unsigned. + if expect_exit_code < 0: + expect_exit_code = struct.unpack('I', + struct.pack('i', + expect_exit_code))[0] + + if exit_code != expect_exit_code: + raise subprocess.CalledProcessError(exit_code, executable_name) + + out = subprocess.check_output([ + os.path.join(out_dir, 'crashpad_database_util.exe'), + '--database=' + test_database, + '--show-pending-reports', + '--show-all-report-info', + ], + text=True) + for line in out.splitlines(): + if line.strip().startswith('Path:'): + return line.partition(':')[2].strip() + finally: + if handler: + handler.kill() + + +def GetDumpFromCrashyProgram(out_dir, pipe_name): + return GetDumpFromProgram(out_dir, pipe_name, 'crashy_program.exe', + win32con.EXCEPTION_ACCESS_VIOLATION) + + +def GetDumpFromOtherProgram(out_dir, pipe_name, *args): + return GetDumpFromProgram(out_dir, pipe_name, 'crash_other_program.exe', 0, + *args) + + +def GetDumpFromSignal(out_dir, pipe_name, *args): + STATUS_FATAL_APP_EXIT = 0x40000015 # Not known by win32con. + return GetDumpFromProgram(out_dir, pipe_name, 'crashy_signal.exe', + STATUS_FATAL_APP_EXIT, *args) + + +def GetDumpFromSelfDestroyingProgram(out_dir, pipe_name): + return GetDumpFromProgram(out_dir, pipe_name, 'self_destroying_program.exe', + win32con.EXCEPTION_BREAKPOINT) + + +def GetDumpFromZ7Program(out_dir, pipe_name): + return GetDumpFromProgram(out_dir, pipe_name, 'crashy_z7_loader.exe', + win32con.EXCEPTION_ACCESS_VIOLATION) + + +class CdbRun(object): + """Run cdb.exe passing it a cdb command and capturing the output. + `Check()` searches for regex patterns in sequence allowing verification of + expected output. + """ + + def __init__(self, cdb_path, dump_path, command): + # Run a command line that loads the dump, runs the specified cdb + # command, and then quits, and capturing stdout. + self.out = subprocess.check_output( + [cdb_path, '-z', dump_path, '-c', command + ';q'], text=True) + + def Check(self, pattern, message, re_flags=0): + match_obj = re.search(pattern, self.out, re_flags) + if match_obj: + # Matched. Consume up to end of match. + self.out = self.out[match_obj.end(0):] + print('ok - %s' % message) + sys.stdout.flush() + else: + print('-' * 80, file=sys.stderr) + print('FAILED - %s' % message, file=sys.stderr) + print('-' * 80, file=sys.stderr) + print('did not match:\n %s' % pattern, file=sys.stderr) + print('-' * 80, file=sys.stderr) + print('remaining output was:\n %s' % self.out, file=sys.stderr) + print('-' * 80, file=sys.stderr) + sys.stderr.flush() + global g_had_failures + g_had_failures = True + + def Find(self, pattern, re_flags=0): + match_obj = re.search(pattern, self.out, re_flags) + if match_obj: + # Matched. Consume up to end of match. + self.out = self.out[match_obj.end(0):] + return match_obj + return None + + +def RunTests(cdb_path, dump_path, start_handler_dump_path, destroyed_dump_path, + z7_dump_path, other_program_path, other_program_no_exception_path, + sigabrt_main_path, sigabrt_background_path, pipe_name): + """Runs various tests in sequence. Runs a new cdb instance on the dump for + each block of tests to reduce the chances that output from one command is + confused for output from another. + """ + out = CdbRun(cdb_path, dump_path, '.ecxr') + out.Check('This dump file has an exception of interest stored in it', + 'captured exception') + + # When SomeCrashyFunction is inlined, cdb doesn't demangle its namespace as + # "`anonymous namespace'" and instead gives the decorated form. + out.Check( + 'crashy_program!crashpad::(`anonymous namespace\'|\?A0x[0-9a-f]+)::' + 'SomeCrashyFunction', 'exception at correct location') + + out = CdbRun(cdb_path, start_handler_dump_path, '.ecxr') + out.Check('This dump file has an exception of interest stored in it', + 'captured exception (using StartHandler())') + out.Check( + 'crashy_program!crashpad::(`anonymous namespace\'|\?A0x[0-9a-f]+)::' + 'SomeCrashyFunction', + 'exception at correct location (using StartHandler())') + + out = CdbRun(cdb_path, dump_path, '!peb') + out.Check(r'PEB at', 'found the PEB') + out.Check(r'Ldr\.InMemoryOrderModuleList:.*\d+ \. \d+', + 'PEB_LDR_DATA saved') + out.Check(r'Base TimeStamp Module', + 'module list present') + pipe_name_escaped = pipe_name.replace('\\', '\\\\') + out.Check(r'CommandLine: *\'.*crashy_program\.exe *' + pipe_name_escaped, + 'some PEB data is correct') + out.Check(r'SystemRoot=C:\\Windows', 'some of environment captured', + re.IGNORECASE) + + out = CdbRun(cdb_path, dump_path, '?? @$peb->ProcessParameters') + out.Check(r' ImagePathName *: _UNICODE_STRING ".*\\crashy_program\.exe"', + 'PEB->ProcessParameters.ImagePathName string captured') + out.Check( + ' DesktopInfo *: ' + '_UNICODE_STRING "(?!--- memory read error at address ).*"', + 'PEB->ProcessParameters.DesktopInfo string captured') + + out = CdbRun(cdb_path, dump_path, '!teb') + out.Check(r'TEB at', 'found the TEB') + out.Check(r'ExceptionList:\s+[0-9a-fA-F]+', 'some valid teb data') + out.Check(r'LastErrorValue:\s+2', 'correct LastErrorValue') + + out = CdbRun(cdb_path, dump_path, '!gle') + out.Check( + 'LastErrorValue: \(Win32\) 0x2 \(2\) - The system cannot find the ' + 'file specified.', '!gle gets last error') + out.Check( + 'LastStatusValue: \(NTSTATUS\) 0xc000000f - {File Not Found} The ' + 'file %hs does not exist.', '!gle gets last ntstatus') + + if False: + # TODO(scottmg): Re-enable when we grab ntdll!RtlCriticalSectionList. + out = CdbRun(cdb_path, dump_path, '!locks') + out.Check( + r'CritSec crashy_program!crashpad::`anonymous namespace\'::' + r'g_test_critical_section', 'lock was captured') + if platform.win32_ver()[0] != '7': + # We can't allocate CRITICAL_SECTIONs with .DebugInfo on Win 7. + out.Check(r'\*\*\* Locked', + 'lock debug info was captured, and is locked') + + out = CdbRun(cdb_path, dump_path, '!handle') + out.Check(r'\d+ Handles', 'captured handles') + out.Check(r'Event\s+\d+', 'capture some event handles') + out.Check(r'File\s+\d+', 'capture some file handles') + + out = CdbRun(cdb_path, dump_path, 'lm') + out.Check(r'Unloaded modules:', 'captured some unloaded modules') + out.Check(r'lz32\.dll', 'found expected unloaded module lz32') + out.Check(r'wmerror\.dll', 'found expected unloaded module wmerror') + + out = CdbRun(cdb_path, destroyed_dump_path, '.ecxr;!peb;k 2') + out.Check(r'Ldr\.InMemoryOrderModuleList:.*\d+ \. \d+', + 'PEB_LDR_DATA saved') + out.Check(r'ntdll\.dll', 'ntdll present', re.IGNORECASE) + + # Check that there is no stack trace in the self-destroyed process. Confirm + # that the top is where we expect it (that's based only on IP), but + # subsequent stack entries will not be available. This confirms that we have + # a mostly valid dump, but that the stack was omitted. + out.Check( + r'self_destroying_program!crashpad::`anonymous namespace\'::' + r'FreeOwnStackAndBreak.*\nquit:', + 'at correct location, no additional stack entries') + + # Dump memory pointed to be EDI on the background suspended thread. We don't + # know the index of the thread because the system may have started other + # threads, so first do a run to extract the thread index that's suspended, + # and then another run to dump the data pointed to by EDI for that thread. + out = CdbRun(cdb_path, dump_path, '.ecxr;~') + match_obj = out.Find(r'(\d+)\s+Id: [0-9a-f.]+ Suspend: 1 Teb:') + if match_obj: + thread = match_obj.group(1) + out = CdbRun(cdb_path, dump_path, '.ecxr;~' + thread + 's;db /c14 edi') + out.Check(r'63 62 61 60 5f 5e 5d 5c-5b 5a 59 58 57 56 55 54 53 52 51 50', + 'data pointed to by registers captured') + + # Move up one stack frame after jumping to the exception, and examine + # memory. + out = CdbRun(cdb_path, dump_path, + '.ecxr; .f+; dd /c100 poi(offset_pointer)-20') + out.Check( + r'80000078 00000079 8000007a 0000007b 8000007c 0000007d 8000007e ' + r'0000007f 80000080 00000081 80000082 00000083 80000084 00000085 ' + r'80000086 00000087 80000088 00000089 8000008a 0000008b 8000008c ' + r'0000008d 8000008e 0000008f 80000090 00000091 80000092 00000093 ' + r'80000094 00000095 80000096 00000097', + 'data pointed to by stack captured') + + # Attempt to retrieve the value of g_extra_memory_pointer (by name), and + # then examine the memory at which it points. Both should have been saved. + out = CdbRun( + cdb_path, dump_path, + 'dd poi(crashy_program!crashpad::g_extra_memory_pointer)+0x1f30 ' + 'L8') + out.Check(r'0000655e 0000656b 00006578 00006585', + 'extra memory range captured') + + out = CdbRun(cdb_path, dump_path, '.dumpdebug') + out.Check(r'type \?\?\? \(333333\), size 00001000', 'first user stream') + out.Check(r'type \?\?\? \(222222\), size 00000080', 'second user stream') + + if z7_dump_path: + out = CdbRun(cdb_path, z7_dump_path, '.ecxr;lm') + out.Check('This dump file has an exception of interest stored in it', + 'captured exception in z7 module') + # Older versions of cdb display relative to exports for /Z7 modules, + # newer ones just display the offset. + out.Check(r'z7_test(!CrashMe\+0xe|\+0x100e):', + 'exception in z7 at correct location') + out.Check(r'z7_test C \(codeview symbols\) z7_test\.dll', + 'expected non-pdb symbol format') + + out = CdbRun(cdb_path, other_program_path, '.ecxr;k;~') + out.Check('Unknown exception - code deadbea7', + 'other program dump exception code') + out.Check('!Sleep', 'other program reasonable location') + out.Check("hanging_program!`anonymous namespace'::Thread1", + 'other program dump right thread') + count = 0 + while True: + match_obj = out.Find(r'Id.*Suspend: (\d+) ') + if match_obj: + if match_obj.group(1) != '0': + out.Check(r'FAILED', 'all suspend counts should be 0') + else: + count += 1 + else: + break + assert count > 2 + + out = CdbRun(cdb_path, other_program_no_exception_path, '.ecxr;k') + out.Check('Unknown exception - code 0cca11ed', + 'other program with no exception given') + out.Check('!RaiseException', 'other program in RaiseException()') + + out = CdbRun(cdb_path, sigabrt_main_path, '.ecxr') + out.Check('code 40000015', 'got sigabrt signal') + out.Check('::HandleAbortSignal', ' stack in expected location') + + out = CdbRun(cdb_path, sigabrt_background_path, '.ecxr') + out.Check('code 40000015', 'got sigabrt signal from background thread') + + +def main(args): + try: + if len(args) != 1: + print('must supply binary dir', file=sys.stderr) + return 1 + + cdb_path = GetCdbPath() + if not cdb_path: + print('could not find cdb', file=sys.stderr) + return 1 + + # Make sure we can download Windows symbols. + if not os.environ.get('_NT_SYMBOL_PATH'): + symbol_dir = MakeTempDir() + protocol = 'https' if platform.win32_ver()[0] != 'XP' else 'http' + os.environ['_NT_SYMBOL_PATH'] = ( + 'SRV*' + symbol_dir + '*' + protocol + + '://msdl.microsoft.com/download/symbols') + + pipe_name = r'\\.\pipe\end-to-end_%s_%s' % (os.getpid(), + str(random.getrandbits(64))) + + crashy_dump_path = GetDumpFromCrashyProgram(args[0], pipe_name) + if not crashy_dump_path: + return 1 + + start_handler_dump_path = GetDumpFromCrashyProgram(args[0], None) + if not start_handler_dump_path: + return 1 + + destroyed_dump_path = GetDumpFromSelfDestroyingProgram( + args[0], pipe_name) + if not destroyed_dump_path: + return 1 + + z7_dump_path = None + if not args[0].endswith('_x64'): + z7_dump_path = GetDumpFromZ7Program(args[0], pipe_name) + if not z7_dump_path: + return 1 + + other_program_path = GetDumpFromOtherProgram(args[0], pipe_name) + if not other_program_path: + return 1 + + other_program_no_exception_path = GetDumpFromOtherProgram( + args[0], pipe_name, 'noexception') + if not other_program_no_exception_path: + return 1 + + sigabrt_main_path = GetDumpFromSignal(args[0], pipe_name, 'main') + if not sigabrt_main_path: + return 1 + + sigabrt_background_path = GetDumpFromSignal(args[0], pipe_name, + 'background') + if not sigabrt_background_path: + return 1 + + RunTests(cdb_path, crashy_dump_path, start_handler_dump_path, + destroyed_dump_path, z7_dump_path, other_program_path, + other_program_no_exception_path, sigabrt_main_path, + sigabrt_background_path, pipe_name) + + return 1 if g_had_failures else 0 + finally: + CleanUpTempDirs() + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/shared/sentry/external/crashpad/snapshot/win/exception_snapshot_win.cc b/shared/sentry/external/crashpad/snapshot/win/exception_snapshot_win.cc new file mode 100644 index 000000000..29cf165d1 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/exception_snapshot_win.cc @@ -0,0 +1,276 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/win/exception_snapshot_win.h" + +#include "base/logging.h" +#include "snapshot/capture_memory.h" +#include "snapshot/memory_snapshot.h" +#include "snapshot/memory_snapshot_generic.h" +#include "snapshot/win/capture_memory_delegate_win.h" +#include "snapshot/win/cpu_context_win.h" +#include "snapshot/win/process_reader_win.h" +#include "util/win/exception_codes.h" +#include "util/win/nt_internals.h" + +namespace crashpad { +namespace internal { + +namespace { + +#if defined(ARCH_CPU_X86_FAMILY) +#if defined(ARCH_CPU_32_BITS) +using Context32 = CONTEXT; +#elif defined(ARCH_CPU_64_BITS) +using Context32 = WOW64_CONTEXT; +#endif + +void NativeContextToCPUContext32(const Context32& context_record, + CPUContext* context, + CPUContextUnion* context_union) { + context->architecture = kCPUArchitectureX86; + context->x86 = &context_union->x86; + InitializeX86Context(context_record, context->x86); +} +#endif // ARCH_CPU_X86_FAMILY + +#if defined(ARCH_CPU_64_BITS) +void NativeContextToCPUContext64(const CONTEXT& context_record, + CPUContext* context, + CPUContextUnion* context_union) { +#if defined(ARCH_CPU_X86_64) + context->architecture = kCPUArchitectureX86_64; + context->x86_64 = &context_union->x86_64; + InitializeX64Context(context_record, context->x86_64); +#elif defined(ARCH_CPU_ARM64) + context->architecture = kCPUArchitectureARM64; + context->arm64 = &context_union->arm64; + InitializeARM64Context(context_record, context->arm64); +#else +#error Unsupported Windows 64-bit Arch +#endif +} +#endif + +} // namespace + +ExceptionSnapshotWin::ExceptionSnapshotWin() + : ExceptionSnapshot(), + context_union_(), + context_(), + codes_(), + extra_memory_(), + thread_id_(0), + exception_address_(0), + exception_flags_(0), + exception_code_(0), + initialized_() { +} + +ExceptionSnapshotWin::~ExceptionSnapshotWin() { +} + +bool ExceptionSnapshotWin::Initialize( + ProcessReaderWin* process_reader, + DWORD thread_id, + WinVMAddress exception_pointers_address, + uint32_t* gather_indirectly_referenced_memory_cap) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + const ProcessReaderWin::Thread* thread = nullptr; + for (const auto& loop_thread : process_reader->Threads()) { + if (thread_id == loop_thread.id) { + thread = &loop_thread; + break; + } + } + + if (!thread) { + LOG(ERROR) << "thread ID " << thread_id << " not found in process"; + return false; + } else { + thread_id_ = thread_id; + } + +#if defined(ARCH_CPU_32_BITS) + const bool is_64_bit = false; +#elif defined(ARCH_CPU_64_BITS) + const bool is_64_bit = process_reader->Is64Bit(); + if (is_64_bit) { + if (!InitializeFromExceptionPointers( + process_reader, + exception_pointers_address, + thread_id, + &NativeContextToCPUContext64)) { + return false; + } + } +#endif + +#if !defined(ARCH_CPU_ARM64) + if (!is_64_bit) { + if (!InitializeFromExceptionPointers( + process_reader, + exception_pointers_address, + thread_id, + &NativeContextToCPUContext32)) { + return false; + } + } +#endif + + CaptureMemoryDelegateWin capture_memory_delegate( + process_reader, + *thread, + &extra_memory_, + gather_indirectly_referenced_memory_cap); + CaptureMemory::PointedToByContext(context_, &capture_memory_delegate); + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +const CPUContext* ExceptionSnapshotWin::Context() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &context_; +} + +uint64_t ExceptionSnapshotWin::ThreadID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_id_; +} + +uint32_t ExceptionSnapshotWin::Exception() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_code_; +} + +uint32_t ExceptionSnapshotWin::ExceptionInfo() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_flags_; +} + +uint64_t ExceptionSnapshotWin::ExceptionAddress() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return exception_address_; +} + +const std::vector& ExceptionSnapshotWin::Codes() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return codes_; +} + +std::vector ExceptionSnapshotWin::ExtraMemory() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector result; + result.reserve(extra_memory_.size()); + for (const auto& em : extra_memory_) { + result.push_back(em.get()); + } + return result; +} + +template +bool ExceptionSnapshotWin::InitializeFromExceptionPointers( + ProcessReaderWin* process_reader, + WinVMAddress exception_pointers_address, + DWORD exception_thread_id, + void (*native_to_cpu_context)(const ContextType& context_record, + CPUContext* context, + CPUContextUnion* context_union)) { + ExceptionPointersType exception_pointers; + if (!process_reader->Memory()->Read(exception_pointers_address, + sizeof(exception_pointers), + &exception_pointers)) { + LOG(ERROR) << "EXCEPTION_POINTERS read failed"; + return false; + } + if (!exception_pointers.ExceptionRecord) { + LOG(ERROR) << "null ExceptionRecord"; + return false; + } + + ExceptionRecordType first_record; + if (!process_reader->Memory()->Read( + static_cast(exception_pointers.ExceptionRecord), + sizeof(first_record), + &first_record)) { + LOG(ERROR) << "ExceptionRecord"; + return false; + } + + const bool triggered_by_client = + first_record.ExceptionCode == ExceptionCodes::kTriggeredExceptionCode && + first_record.NumberParameters == 2; + if (triggered_by_client) + process_reader->DecrementThreadSuspendCounts(exception_thread_id); + + if (triggered_by_client && first_record.ExceptionInformation[0] != 0) { + // This special exception code indicates that the target was crashed by + // another client calling CrashpadClient::DumpAndCrashTargetProcess(). In + // this case the parameters are a thread id and an exception code which we + // use to fabricate a new exception record. + using ArgumentType = decltype(first_record.ExceptionInformation[0]); + const ArgumentType blame_thread_id = first_record.ExceptionInformation[0]; + exception_code_ = static_cast(first_record.ExceptionInformation[1]); + exception_flags_ = EXCEPTION_NONCONTINUABLE; + for (const auto& thread : process_reader->Threads()) { + if (thread.id == blame_thread_id) { + thread_id_ = blame_thread_id; + native_to_cpu_context( + *reinterpret_cast(&thread.context), + &context_, + &context_union_); + exception_address_ = context_.InstructionPointer(); + break; + } + } + + if (exception_address_ == 0) { + LOG(WARNING) << "thread " << blame_thread_id << " not found"; + return false; + } + } else { + // Normal case. + exception_code_ = first_record.ExceptionCode; + exception_flags_ = first_record.ExceptionFlags; + exception_address_ = first_record.ExceptionAddress; + for (DWORD i = 0; i < first_record.NumberParameters; ++i) + codes_.push_back(first_record.ExceptionInformation[i]); + if (first_record.ExceptionRecord) { + // https://crashpad.chromium.org/bug/43 + LOG(WARNING) << "dropping chained ExceptionRecord"; + } + + ContextType context_record; + if (!process_reader->Memory()->Read( + static_cast(exception_pointers.ContextRecord), + sizeof(context_record), + &context_record)) { + LOG(ERROR) << "ContextRecord"; + return false; + } + + native_to_cpu_context(context_record, &context_, &context_union_); + } + + return true; +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/win/exception_snapshot_win.h b/shared/sentry/external/crashpad/snapshot/win/exception_snapshot_win.h new file mode 100644 index 000000000..b8fa73530 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/exception_snapshot_win.h @@ -0,0 +1,114 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_WIN_EXCEPTION_SNAPSHOT_WIN_H_ +#define CRASHPAD_SNAPSHOT_WIN_EXCEPTION_SNAPSHOT_WIN_H_ + +#include +#include + +#include +#include + +#include "build/build_config.h" +#include "snapshot/cpu_context.h" +#include "snapshot/exception_snapshot.h" +#include "snapshot/win/thread_snapshot_win.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/win/address_types.h" +#include "util/win/process_structs.h" + +namespace crashpad { + +class ProcessReaderWin; + +namespace internal { + +class MemorySnapshotGeneric; + +union CPUContextUnion { +#if defined(ARCH_CPU_X86_FAMILY) + CPUContextX86 x86; + CPUContextX86_64 x86_64; +#elif defined(ARCH_CPU_ARM64) + CPUContextARM64 arm64; +#endif +}; + +class ExceptionSnapshotWin final : public ExceptionSnapshot { + public: + ExceptionSnapshotWin(); + + ExceptionSnapshotWin(const ExceptionSnapshotWin&) = delete; + ExceptionSnapshotWin& operator=(const ExceptionSnapshotWin&) = delete; + + ~ExceptionSnapshotWin() override; + + //! \brief Initializes the object. + //! + //! \param[in] process_reader A ProcessReaderWin for the process that + //! sustained the exception. + //! \param[in] thread_id The thread ID in which the exception occurred. + //! \param[in] exception_pointers The address of an `EXCEPTION_POINTERS` + //! record in the target process, passed through from the exception + //! handler. + //! + //! \note If the exception was triggered by + //! CrashpadClient::DumpAndCrashTargetProcess(), this has the side-effect + //! of correcting the thread suspend counts for \a process_reader. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(ProcessReaderWin* process_reader, + DWORD thread_id, + WinVMAddress exception_pointers, + uint32_t* gather_indirectly_referenced_memory_cap); + + // ExceptionSnapshot: + + const CPUContext* Context() const override; + uint64_t ThreadID() const override; + uint32_t Exception() const override; + uint32_t ExceptionInfo() const override; + uint64_t ExceptionAddress() const override; + const std::vector& Codes() const override; + std::vector ExtraMemory() const override; + + private: + template + bool InitializeFromExceptionPointers( + ProcessReaderWin* process_reader, + WinVMAddress exception_pointers_address, + DWORD exception_thread_id, + void (*native_to_cpu_context)(const ContextType& context_record, + CPUContext* context, + CPUContextUnion* context_union)); + + CPUContextUnion context_union_; + CPUContext context_; + std::vector codes_; + std::vector> extra_memory_; + uint64_t thread_id_; + uint64_t exception_address_; + uint32_t exception_flags_; + DWORD exception_code_; + InitializationStateDcheck initialized_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_WIN_EXCEPTION_SNAPSHOT_WIN_H_ diff --git a/shared/sentry/external/crashpad/snapshot/win/exception_snapshot_win_test.cc b/shared/sentry/external/crashpad/snapshot/win/exception_snapshot_win_test.cc new file mode 100644 index 000000000..91ff48fa1 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/exception_snapshot_win_test.cc @@ -0,0 +1,320 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/win/exception_snapshot_win.h" + +#include + +#include "base/files/file_path.h" +#include "base/strings/utf_string_conversions.h" +#include "gtest/gtest.h" +#include "snapshot/win/process_snapshot_win.h" +#include "test/errors.h" +#include "test/test_paths.h" +#include "test/win/child_launcher.h" +#include "util/file/file_io.h" +#include "util/thread/thread.h" +#include "util/win/exception_handler_server.h" +#include "util/win/registration_protocol_win.h" +#include "util/win/scoped_handle.h" +#include "util/win/scoped_process_suspend.h" + +namespace crashpad { +namespace test { +namespace { + +// Runs the ExceptionHandlerServer on a background thread. +class RunServerThread : public Thread { + public: + // Instantiates a thread which will invoke server->Run(delegate); + RunServerThread(ExceptionHandlerServer* server, + ExceptionHandlerServer::Delegate* delegate) + : server_(server), delegate_(delegate) {} + + RunServerThread(const RunServerThread&) = delete; + RunServerThread& operator=(const RunServerThread&) = delete; + + ~RunServerThread() override {} + + private: + // Thread: + void ThreadMain() override { server_->Run(delegate_); } + + ExceptionHandlerServer* server_; + ExceptionHandlerServer::Delegate* delegate_; +}; + +// During destruction, ensures that the server is stopped and the background +// thread joined. +class ScopedStopServerAndJoinThread { + public: + ScopedStopServerAndJoinThread(ExceptionHandlerServer* server, Thread* thread) + : server_(server), thread_(thread) {} + + ScopedStopServerAndJoinThread(const ScopedStopServerAndJoinThread&) = delete; + ScopedStopServerAndJoinThread& operator=( + const ScopedStopServerAndJoinThread&) = delete; + + ~ScopedStopServerAndJoinThread() { + server_->Stop(); + thread_->Join(); + } + + private: + ExceptionHandlerServer* server_; + Thread* thread_; +}; + +class CrashingDelegate : public ExceptionHandlerServer::Delegate { + public: + CrashingDelegate(HANDLE server_ready, HANDLE completed_test_event) + : server_ready_(server_ready), + completed_test_event_(completed_test_event), + break_near_(0) {} + + CrashingDelegate(const CrashingDelegate&) = delete; + CrashingDelegate& operator=(const CrashingDelegate&) = delete; + + ~CrashingDelegate() {} + + void set_break_near(WinVMAddress break_near) { break_near_ = break_near; } + + void ExceptionHandlerServerStarted() override { SetEvent(server_ready_); } + + unsigned int ExceptionHandlerServerException( + HANDLE process, + WinVMAddress exception_information_address, + WinVMAddress debug_critical_section_address) override { + ScopedProcessSuspend suspend(process); + ProcessSnapshotWin snapshot; + snapshot.Initialize(process, + ProcessSuspensionState::kSuspended, + exception_information_address, + debug_critical_section_address); + + // Confirm the exception record was read correctly. + EXPECT_NE(snapshot.Exception()->ThreadID(), 0u); + EXPECT_EQ(EXCEPTION_BREAKPOINT, snapshot.Exception()->Exception()); + + // Verify the exception happened at the expected location with a bit of + // slop space to allow for reading the current PC before the exception + // happens. See TestCrashingChild(). +#if !defined(NDEBUG) + // Debug build is likely not optimized and contains more instructions. + constexpr uint64_t kAllowedOffset = 200; +#else + constexpr uint64_t kAllowedOffset = 100; +#endif + EXPECT_GT(snapshot.Exception()->ExceptionAddress(), break_near_); + EXPECT_LT(snapshot.Exception()->ExceptionAddress(), + break_near_ + kAllowedOffset); + + SetEvent(completed_test_event_); + + return snapshot.Exception()->Exception(); + } + + private: + HANDLE server_ready_; // weak + HANDLE completed_test_event_; // weak + WinVMAddress break_near_; +}; + +void TestCrashingChild(TestPaths::Architecture architecture) { + // Set up the registration server on a background thread. + ScopedKernelHANDLE server_ready(CreateEvent(nullptr, false, false, nullptr)); + ASSERT_TRUE(server_ready.is_valid()) << ErrorMessage("CreateEvent"); + ScopedKernelHANDLE completed(CreateEvent(nullptr, false, false, nullptr)); + ASSERT_TRUE(completed.is_valid()) << ErrorMessage("CreateEvent"); + CrashingDelegate delegate(server_ready.get(), completed.get()); + + ExceptionHandlerServer exception_handler_server(true); + std::wstring pipe_name(L"\\\\.\\pipe\\test_name"); + exception_handler_server.SetPipeName(pipe_name); + RunServerThread server_thread(&exception_handler_server, &delegate); + server_thread.Start(); + ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread( + &exception_handler_server, &server_thread); + + EXPECT_EQ(WaitForSingleObject(server_ready.get(), INFINITE), WAIT_OBJECT_0) + << ErrorMessage("WaitForSingleObject"); + + // Spawn a child process, passing it the pipe name to connect to. + base::FilePath child_test_executable = + TestPaths::BuildArtifact(L"snapshot", + L"crashing_child", + TestPaths::FileType::kExecutable, + architecture); + ChildLauncher child(child_test_executable, pipe_name); + ASSERT_NO_FATAL_FAILURE(child.Start()); + + // The child tells us (approximately) where it will crash. + WinVMAddress break_near_address; + LoggingReadFileExactly(child.stdout_read_handle(), + &break_near_address, + sizeof(break_near_address)); + delegate.set_break_near(break_near_address); + + // Wait for the child to crash and the exception information to be validated. + EXPECT_EQ(WaitForSingleObject(completed.get(), INFINITE), WAIT_OBJECT_0) + << ErrorMessage("WaitForSingleObject"); + + EXPECT_EQ(child.WaitForExit(), EXCEPTION_BREAKPOINT); +} + +#if defined(ADDRESS_SANITIZER) +// https://crbug.com/845011 +#define MAYBE_ChildCrash DISABLED_ChildCrash +#else +#define MAYBE_ChildCrash ChildCrash +#endif +TEST(ExceptionSnapshotWinTest, MAYBE_ChildCrash) { + TestCrashingChild(TestPaths::Architecture::kDefault); +} + +#if defined(ARCH_CPU_64_BITS) +TEST(ExceptionSnapshotWinTest, ChildCrashWOW64) { + if (!TestPaths::Has32BitBuildArtifacts()) { + GTEST_SKIP(); + } + + TestCrashingChild(TestPaths::Architecture::k32Bit); +} +#endif // ARCH_CPU_64_BITS + +class SimulateDelegate : public ExceptionHandlerServer::Delegate { + public: + SimulateDelegate(HANDLE server_ready, HANDLE completed_test_event) + : server_ready_(server_ready), + completed_test_event_(completed_test_event), + dump_near_(0) {} + + SimulateDelegate(const SimulateDelegate&) = delete; + SimulateDelegate& operator=(const SimulateDelegate&) = delete; + + ~SimulateDelegate() {} + + void set_dump_near(WinVMAddress dump_near) { dump_near_ = dump_near; } + + void ExceptionHandlerServerStarted() override { SetEvent(server_ready_); } + + unsigned int ExceptionHandlerServerException( + HANDLE process, + WinVMAddress exception_information_address, + WinVMAddress debug_critical_section_address) override { + ScopedProcessSuspend suspend(process); + ProcessSnapshotWin snapshot; + snapshot.Initialize(process, + ProcessSuspensionState::kSuspended, + exception_information_address, + debug_critical_section_address); + EXPECT_TRUE(snapshot.Exception()); + EXPECT_EQ(snapshot.Exception()->Exception(), 0x517a7edu); + + // Verify the dump was captured at the expected location with some slop + // space. +#if defined(ADDRESS_SANITIZER) + // ASan instrumentation inserts more instructions between the expected + // location and what's reported. https://crbug.com/845011. + constexpr uint64_t kAllowedOffset = 500; +#elif !defined(NDEBUG) + // Debug build is likely not optimized and contains more instructions. + constexpr uint64_t kAllowedOffset = 200; +#else + constexpr uint64_t kAllowedOffset = 100; +#endif + EXPECT_GT(snapshot.Exception()->Context()->InstructionPointer(), + dump_near_); + EXPECT_LT(snapshot.Exception()->Context()->InstructionPointer(), + dump_near_ + kAllowedOffset); + + EXPECT_EQ(snapshot.Exception()->ExceptionAddress(), + snapshot.Exception()->Context()->InstructionPointer()); + + SetEvent(completed_test_event_); + + return 0; + } + + private: + HANDLE server_ready_; // weak + HANDLE completed_test_event_; // weak + WinVMAddress dump_near_; +}; + +void TestDumpWithoutCrashingChild(TestPaths::Architecture architecture) { + // Set up the registration server on a background thread. + ScopedKernelHANDLE server_ready(CreateEvent(nullptr, false, false, nullptr)); + ASSERT_TRUE(server_ready.is_valid()) << ErrorMessage("CreateEvent"); + ScopedKernelHANDLE completed(CreateEvent(nullptr, false, false, nullptr)); + ASSERT_TRUE(completed.is_valid()) << ErrorMessage("CreateEvent"); + SimulateDelegate delegate(server_ready.get(), completed.get()); + + ExceptionHandlerServer exception_handler_server(true); + std::wstring pipe_name(L"\\\\.\\pipe\\test_name"); + exception_handler_server.SetPipeName(pipe_name); + RunServerThread server_thread(&exception_handler_server, &delegate); + server_thread.Start(); + ScopedStopServerAndJoinThread scoped_stop_server_and_join_thread( + &exception_handler_server, &server_thread); + + EXPECT_EQ(WaitForSingleObject(server_ready.get(), INFINITE), WAIT_OBJECT_0) + << ErrorMessage("WaitForSingleObject"); + + // Spawn a child process, passing it the pipe name to connect to. + base::FilePath child_test_executable = + TestPaths::BuildArtifact(L"snapshot", + L"dump_without_crashing", + TestPaths::FileType::kExecutable, + architecture); + ChildLauncher child(child_test_executable, pipe_name); + ASSERT_NO_FATAL_FAILURE(child.Start()); + + // The child tells us (approximately) where it will capture a dump. + WinVMAddress dump_near_address; + LoggingReadFileExactly(child.stdout_read_handle(), + &dump_near_address, + sizeof(dump_near_address)); + delegate.set_dump_near(dump_near_address); + + // Wait for the child to crash and the exception information to be validated. + EXPECT_EQ(WaitForSingleObject(completed.get(), INFINITE), WAIT_OBJECT_0) + << ErrorMessage("WaitForSingleObject"); + + EXPECT_EQ(child.WaitForExit(), 0u); +} + +#if defined(ADDRESS_SANITIZER) +// https://crbug.com/845011 +#define MAYBE_ChildDumpWithoutCrashing DISABLED_ChildDumpWithoutCrashing +#else +#define MAYBE_ChildDumpWithoutCrashing ChildDumpWithoutCrashing +#endif +TEST(SimulateCrash, MAYBE_ChildDumpWithoutCrashing) { + TestDumpWithoutCrashingChild(TestPaths::Architecture::kDefault); +} + +#if defined(ARCH_CPU_64_BITS) +TEST(SimulateCrash, ChildDumpWithoutCrashingWOW64) { + if (!TestPaths::Has32BitBuildArtifacts()) { + GTEST_SKIP(); + } + + TestDumpWithoutCrashingChild(TestPaths::Architecture::k32Bit); +} +#endif // ARCH_CPU_64_BITS + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/win/extra_memory_ranges_test.cc b/shared/sentry/external/crashpad/snapshot/win/extra_memory_ranges_test.cc new file mode 100644 index 000000000..e0c17a209 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/extra_memory_ranges_test.cc @@ -0,0 +1,129 @@ +// Copyright 2016 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/win/module_snapshot_win.h" + +#include +#include + +#include + +#include "base/files/file_path.h" +#include "build/build_config.h" +#include "client/crashpad_info.h" +#include "client/simple_address_range_bag.h" +#include "gtest/gtest.h" +#include "snapshot/win/process_snapshot_win.h" +#include "test/test_paths.h" +#include "test/win/child_launcher.h" +#include "util/file/file_io.h" +#include "util/win/process_info.h" + +namespace crashpad { +namespace test { +namespace { + +enum TestType { + // Don't crash, just test the CrashpadInfo interface. + kDontCrash = 0, + + // The child process should crash by __debugbreak(). + kCrashDebugBreak, +}; + +void TestExtraMemoryRanges(TestType type, + TestPaths::Architecture architecture) { + // Spawn a child process, passing it the pipe name to connect to. + base::FilePath child_test_executable = + TestPaths::BuildArtifact(L"snapshot", + L"extra_memory_ranges", + TestPaths::FileType::kExecutable, + architecture); + ChildLauncher child(child_test_executable, L""); + ASSERT_NO_FATAL_FAILURE(child.Start()); + + // Wait for the child process to indicate that it's done setting up its + // annotations via the CrashpadInfo interface. + char c; + CheckedReadFileExactly(child.stdout_read_handle(), &c, sizeof(c)); + + ProcessSnapshotWin snapshot; + ASSERT_TRUE(snapshot.Initialize( + child.process_handle(), ProcessSuspensionState::kRunning, 0, 0)); + + // Verify the extra memory ranges set via the CrashpadInfo interface. + std::set> all_ranges; + for (const auto* module : snapshot.Modules()) { + for (const auto& range : module->ExtraMemoryRanges()) + all_ranges.insert(range); + } + + EXPECT_EQ(all_ranges.size(), 5u); + EXPECT_NE(all_ranges.find(CheckedRange(0, 1)), all_ranges.end()); + EXPECT_NE(all_ranges.find(CheckedRange(1, 0)), all_ranges.end()); + EXPECT_NE(all_ranges.find(CheckedRange(1234, 5678)), + all_ranges.end()); + EXPECT_NE(all_ranges.find(CheckedRange(0x1000000000ULL, 0x1000)), + all_ranges.end()); + EXPECT_NE(all_ranges.find(CheckedRange(0x2000, 0x2000000000ULL)), + all_ranges.end()); + + // Tell the child process to continue. + DWORD expected_exit_code; + switch (type) { + case kDontCrash: + c = ' '; + expected_exit_code = 0; + break; + case kCrashDebugBreak: + c = 'd'; + expected_exit_code = STATUS_BREAKPOINT; + break; + default: + FAIL(); + } + CheckedWriteFile(child.stdin_write_handle(), &c, sizeof(c)); + + EXPECT_EQ(child.WaitForExit(), expected_exit_code); +} + +TEST(ExtraMemoryRanges, DontCrash) { + TestExtraMemoryRanges(kDontCrash, TestPaths::Architecture::kDefault); +} + +TEST(ExtraMemoryRanges, CrashDebugBreak) { + TestExtraMemoryRanges(kCrashDebugBreak, TestPaths::Architecture::kDefault); +} + +#if defined(ARCH_CPU_64_BITS) +TEST(ExtraMemoryRanges, DontCrashWOW64) { + if (!TestPaths::Has32BitBuildArtifacts()) { + GTEST_SKIP(); + } + + TestExtraMemoryRanges(kDontCrash, TestPaths::Architecture::k32Bit); +} + +TEST(ExtraMemoryRanges, CrashDebugBreakWOW64) { + if (!TestPaths::Has32BitBuildArtifacts()) { + GTEST_SKIP(); + } + + TestExtraMemoryRanges(kCrashDebugBreak, TestPaths::Architecture::k32Bit); +} +#endif // ARCH_CPU_64_BITS + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/win/memory_map_region_snapshot_win.cc b/shared/sentry/external/crashpad/snapshot/win/memory_map_region_snapshot_win.cc new file mode 100644 index 000000000..c64254c68 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/memory_map_region_snapshot_win.cc @@ -0,0 +1,41 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/win/memory_map_region_snapshot_win.h" + +namespace crashpad { +namespace internal { + +MemoryMapRegionSnapshotWin::MemoryMapRegionSnapshotWin( + const MEMORY_BASIC_INFORMATION64& mbi) + : memory_info_() { + memory_info_.BaseAddress = mbi.BaseAddress; + memory_info_.AllocationBase = mbi.AllocationBase; + memory_info_.AllocationProtect = mbi.AllocationProtect; + memory_info_.RegionSize = mbi.RegionSize; + memory_info_.State = mbi.State; + memory_info_.Protect = mbi.Protect; + memory_info_.Type = mbi.Type; +} + +MemoryMapRegionSnapshotWin::~MemoryMapRegionSnapshotWin() { +} + +const MINIDUMP_MEMORY_INFO& MemoryMapRegionSnapshotWin::AsMinidumpMemoryInfo() + const { + return memory_info_; +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/win/memory_map_region_snapshot_win.h b/shared/sentry/external/crashpad/snapshot/win/memory_map_region_snapshot_win.h new file mode 100644 index 000000000..725c12565 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/memory_map_region_snapshot_win.h @@ -0,0 +1,37 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_WIN_MEMORY_MAP_REGION_SNAPSHOT_WIN_H_ +#define CRASHPAD_SNAPSHOT_WIN_MEMORY_MAP_REGION_SNAPSHOT_WIN_H_ + +#include "snapshot/memory_map_region_snapshot.h" + +namespace crashpad { +namespace internal { + +class MemoryMapRegionSnapshotWin : public MemoryMapRegionSnapshot { + public: + explicit MemoryMapRegionSnapshotWin(const MEMORY_BASIC_INFORMATION64& mbi); + ~MemoryMapRegionSnapshotWin() override; + + virtual const MINIDUMP_MEMORY_INFO& AsMinidumpMemoryInfo() const override; + + private: + MINIDUMP_MEMORY_INFO memory_info_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_WIN_MEMORY_MAP_REGION_SNAPSHOT_WIN_H_ diff --git a/shared/sentry/external/crashpad/snapshot/win/module_snapshot_win.cc b/shared/sentry/external/crashpad/snapshot/win/module_snapshot_win.cc new file mode 100644 index 000000000..fe16bb91d --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/module_snapshot_win.cc @@ -0,0 +1,343 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/win/module_snapshot_win.h" + +#include + +#include "base/strings/utf_string_conversions.h" +#include "client/crashpad_info.h" +#include "client/simple_address_range_bag.h" +#include "snapshot/memory_snapshot_generic.h" +#include "snapshot/win/pe_image_annotations_reader.h" +#include "snapshot/win/pe_image_reader.h" +#include "util/misc/tri_state.h" +#include "util/misc/uuid.h" + +namespace crashpad { +namespace internal { + +ModuleSnapshotWin::ModuleSnapshotWin() + : ModuleSnapshot(), + name_(), + pdb_name_(), + uuid_(), + memory_range_(), + streams_(), + vs_fixed_file_info_(), + initialized_vs_fixed_file_info_(), + process_reader_(nullptr), + pe_image_reader_(), + crashpad_info_(), + timestamp_(0), + age_(0), + initialized_() {} + +ModuleSnapshotWin::~ModuleSnapshotWin() {} + +bool ModuleSnapshotWin::Initialize( + ProcessReaderWin* process_reader, + const ProcessInfo::Module& process_reader_module) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + process_reader_ = process_reader; + name_ = process_reader_module.name; + timestamp_ = process_reader_module.timestamp; + pe_image_reader_.reset(new PEImageReader()); + if (!pe_image_reader_->Initialize(process_reader_, + process_reader_module.dll_base, + process_reader_module.size, + base::WideToUTF8(name_))) { + return false; + } + + DWORD age_dword; + if (pe_image_reader_->DebugDirectoryInformation( + &uuid_, &age_dword, &pdb_name_)) { + static_assert(sizeof(DWORD) == sizeof(uint32_t), "unexpected age size"); + age_ = age_dword; + } else { + // If we fully supported all old debugging formats, we would want to extract + // and emit a different type of CodeView record here (as old Microsoft tools + // would do). As we don't expect to ever encounter a module that wouldn't be + // using .PDB that we actually have symbols for, we simply set a plausible + // name here, but this will never correspond to symbols that we have. + pdb_name_ = base::WideToUTF8(name_); + } + + if (!memory_range_.Initialize(process_reader_->Memory(), + process_reader_->Is64Bit())) { + return false; + } + + WinVMAddress crashpad_info_address; + WinVMSize crashpad_info_size; + if (pe_image_reader_->GetCrashpadInfoSection(&crashpad_info_address, + &crashpad_info_size)) { + ProcessMemoryRange info_range; + info_range.Initialize(memory_range_); + info_range.RestrictRange(crashpad_info_address, + crashpad_info_address + crashpad_info_size); + + auto info = std::make_unique(); + if (info->Initialize(&info_range, crashpad_info_address)) { + crashpad_info_ = std::move(info); + } + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +void ModuleSnapshotWin::GetCrashpadOptions(CrashpadInfoClientOptions* options) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (process_reader_->Is64Bit()) + GetCrashpadOptionsInternal(options); + else + GetCrashpadOptionsInternal(options); +} + +std::string ModuleSnapshotWin::Name() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return base::WideToUTF8(name_); +} + +uint64_t ModuleSnapshotWin::Address() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return pe_image_reader_->Address(); +} + +uint64_t ModuleSnapshotWin::Size() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return pe_image_reader_->Size(); +} + +time_t ModuleSnapshotWin::Timestamp() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return timestamp_; +} + +void ModuleSnapshotWin::FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + const VS_FIXEDFILEINFO* ffi = VSFixedFileInfo(); + if (ffi) { + *version_0 = ffi->dwFileVersionMS >> 16; + *version_1 = ffi->dwFileVersionMS & 0xffff; + *version_2 = ffi->dwFileVersionLS >> 16; + *version_3 = ffi->dwFileVersionLS & 0xffff; + } else { + *version_0 = 0; + *version_1 = 0; + *version_2 = 0; + *version_3 = 0; + } +} + +void ModuleSnapshotWin::SourceVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + const VS_FIXEDFILEINFO* ffi = VSFixedFileInfo(); + if (ffi) { + *version_0 = ffi->dwProductVersionMS >> 16; + *version_1 = ffi->dwProductVersionMS & 0xffff; + *version_2 = ffi->dwProductVersionLS >> 16; + *version_3 = ffi->dwProductVersionLS & 0xffff; + } else { + *version_0 = 0; + *version_1 = 0; + *version_2 = 0; + *version_3 = 0; + } +} + +ModuleSnapshot::ModuleType ModuleSnapshotWin::GetModuleType() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + const VS_FIXEDFILEINFO* ffi = VSFixedFileInfo(); + if (ffi) { + switch (ffi->dwFileType) { + case VFT_APP: + return ModuleSnapshot::kModuleTypeExecutable; + case VFT_DLL: + return ModuleSnapshot::kModuleTypeSharedLibrary; + case VFT_DRV: + case VFT_VXD: + return ModuleSnapshot::kModuleTypeLoadableModule; + } + } + return ModuleSnapshot::kModuleTypeUnknown; +} + +void ModuleSnapshotWin::UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *uuid = uuid_; + *age = age_; +} + +std::string ModuleSnapshotWin::DebugFileName() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return pdb_name_; +} + +std::vector ModuleSnapshotWin::BuildID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return std::vector(); +} + +std::vector ModuleSnapshotWin::AnnotationsVector() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // These correspond to system-logged things on Mac. We don't currently track + // any of these on Windows, but could in the future. See + // https://crashpad.chromium.org/bug/38. + return std::vector(); +} + +std::map ModuleSnapshotWin::AnnotationsSimpleMap() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + PEImageAnnotationsReader annotations_reader( + process_reader_, pe_image_reader_.get(), name_); + return annotations_reader.SimpleMap(); +} + +std::vector ModuleSnapshotWin::AnnotationObjects() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + PEImageAnnotationsReader annotations_reader( + process_reader_, pe_image_reader_.get(), name_); + return annotations_reader.AnnotationsList(); +} + +std::set> ModuleSnapshotWin::ExtraMemoryRanges() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::set> ranges; + if (process_reader_->Is64Bit()) + GetCrashpadExtraMemoryRanges(&ranges); + else + GetCrashpadExtraMemoryRanges(&ranges); + return ranges; +} + +std::vector +ModuleSnapshotWin::CustomMinidumpStreams() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + streams_.clear(); + if (process_reader_->Is64Bit()) { + GetCrashpadUserMinidumpStreams( + &streams_); + } else { + GetCrashpadUserMinidumpStreams( + &streams_); + } + + std::vector result; + for (const auto& stream : streams_) { + result.push_back(stream.get()); + } + return result; +} + +template +void ModuleSnapshotWin::GetCrashpadOptionsInternal( + CrashpadInfoClientOptions* options) { + if (!crashpad_info_) { + options->crashpad_handler_behavior = TriState::kUnset; + options->system_crash_reporter_forwarding = TriState::kUnset; + options->gather_indirectly_referenced_memory = TriState::kUnset; + options->indirectly_referenced_memory_cap = 0; + return; + } + + options->crashpad_handler_behavior = + crashpad_info_->CrashpadHandlerBehavior(); + options->system_crash_reporter_forwarding = + crashpad_info_->SystemCrashReporterForwarding(); + options->gather_indirectly_referenced_memory = + crashpad_info_->GatherIndirectlyReferencedMemory(); + options->indirectly_referenced_memory_cap = + crashpad_info_->IndirectlyReferencedMemoryCap(); +} + +const VS_FIXEDFILEINFO* ModuleSnapshotWin::VSFixedFileInfo() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (initialized_vs_fixed_file_info_.is_uninitialized()) { + initialized_vs_fixed_file_info_.set_invalid(); + if (pe_image_reader_->VSFixedFileInfo(&vs_fixed_file_info_)) { + initialized_vs_fixed_file_info_.set_valid(); + } + } + + return initialized_vs_fixed_file_info_.is_valid() ? &vs_fixed_file_info_ + : nullptr; +} + +template +void ModuleSnapshotWin::GetCrashpadExtraMemoryRanges( + std::set>* ranges) const { + if (!crashpad_info_ || !crashpad_info_->ExtraMemoryRanges()) + return; + + std::vector simple_ranges( + SimpleAddressRangeBag::num_entries); + if (!process_reader_->Memory()->Read( + crashpad_info_->ExtraMemoryRanges(), + simple_ranges.size() * sizeof(simple_ranges[0]), + &simple_ranges[0])) { + LOG(WARNING) << "could not read simple address_ranges from " + << base::WideToUTF8(name_); + return; + } + + for (const auto& entry : simple_ranges) { + if (entry.base != 0 || entry.size != 0) { + // Deduplication here is fine. + ranges->insert(CheckedRange(entry.base, entry.size)); + } + } +} + +template +void ModuleSnapshotWin::GetCrashpadUserMinidumpStreams( + std::vector>* streams) const { + if (!crashpad_info_) + return; + + for (uint64_t cur = crashpad_info_->UserDataMinidumpStreamHead(); cur;) { + internal::UserDataMinidumpStreamListEntry list_entry; + if (!process_reader_->Memory()->Read( + cur, sizeof(list_entry), &list_entry)) { + LOG(WARNING) << "could not read user data stream entry from " + << base::WideToUTF8(name_); + return; + } + + if (list_entry.size != 0) { + std::unique_ptr memory( + new internal::MemorySnapshotGeneric()); + memory->Initialize( + process_reader_->Memory(), list_entry.base_address, list_entry.size); + streams->push_back(std::make_unique( + list_entry.stream_type, memory.release())); + } + + cur = list_entry.next; + } +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/win/module_snapshot_win.h b/shared/sentry/external/crashpad/snapshot/win/module_snapshot_win.h new file mode 100644 index 000000000..5b375f431 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/module_snapshot_win.h @@ -0,0 +1,135 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_WIN_MODULE_SNAPSHOT_WIN_H_ +#define CRASHPAD_SNAPSHOT_WIN_MODULE_SNAPSHOT_WIN_H_ + +#include +#include + +#include +#include +#include +#include + +#include "snapshot/crashpad_info_client_options.h" +#include "snapshot/crashpad_types/crashpad_info_reader.h" +#include "snapshot/module_snapshot.h" +#include "snapshot/win/process_reader_win.h" +#include "util/misc/initialization_state.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/win/process_info.h" + +namespace crashpad { + +class PEImageReader; +struct UUID; + +namespace internal { + +//! \brief A ModuleSnapshot of a code module (binary image) loaded into a +//! running (or crashed) process on a Windows system. +class ModuleSnapshotWin final : public ModuleSnapshot { + public: + ModuleSnapshotWin(); + + ModuleSnapshotWin(const ModuleSnapshotWin&) = delete; + ModuleSnapshotWin& operator=(const ModuleSnapshotWin&) = delete; + + ~ModuleSnapshotWin() override; + + //! \brief Initializes the object. + //! + //! \param[in] process_reader A ProcessReaderWin for the process containing + //! the module. + //! \param[in] process_reader_module The module within the ProcessReaderWin + //! for which the snapshot should be created. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize(ProcessReaderWin* process_reader, + const ProcessInfo::Module& process_reader_module); + + //! \brief Returns options from the module's CrashpadInfo structure. + //! + //! \param[out] options Options set in the module's CrashpadInfo structure. + void GetCrashpadOptions(CrashpadInfoClientOptions* options); + + //! \brief Returns the PEImageReader used to read this module. Only valid + //! after Initialize() is called. + const PEImageReader& pe_image_reader() const { return *pe_image_reader_; } + + // ModuleSnapshot: + + std::string Name() const override; + uint64_t Address() const override; + uint64_t Size() const override; + time_t Timestamp() const override; + void FileVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const override; + void SourceVersion(uint16_t* version_0, + uint16_t* version_1, + uint16_t* version_2, + uint16_t* version_3) const override; + ModuleType GetModuleType() const override; + void UUIDAndAge(crashpad::UUID* uuid, uint32_t* age) const override; + std::string DebugFileName() const override; + std::vector BuildID() const override; + std::vector AnnotationsVector() const override; + std::map AnnotationsSimpleMap() const override; + std::vector AnnotationObjects() const override; + std::set> ExtraMemoryRanges() const override; + std::vector CustomMinidumpStreams() const override; + + private: + template + void GetCrashpadOptionsInternal(CrashpadInfoClientOptions* options); + + template + void GetCrashpadExtraMemoryRanges( + std::set>* ranges) const; + + template + void GetCrashpadUserMinidumpStreams( + std::vector>* streams) const; + + // Initializes vs_fixed_file_info_ if it has not yet been initialized, and + // returns a pointer to it. Returns nullptr on failure, with a message logged + // on the first call. + const VS_FIXEDFILEINFO* VSFixedFileInfo() const; + + std::wstring name_; + std::string pdb_name_; + UUID uuid_; + ProcessMemoryRange memory_range_; + // Too const-y: https://crashpad.chromium.org/bug/9. + mutable std::vector> streams_; + // VSFixedFileInfo() is logically const, but updates these members on the + // call. See https://crashpad.chromium.org/bug/9. + mutable VS_FIXEDFILEINFO vs_fixed_file_info_; + mutable InitializationState initialized_vs_fixed_file_info_; + ProcessReaderWin* process_reader_; // weak + std::unique_ptr pe_image_reader_; + std::unique_ptr crashpad_info_; + time_t timestamp_; + uint32_t age_; + InitializationStateDcheck initialized_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_WIN_MODULE_SNAPSHOT_WIN_H_ diff --git a/shared/sentry/external/crashpad/snapshot/win/module_snapshot_win_test.cc b/shared/sentry/external/crashpad/snapshot/win/module_snapshot_win_test.cc new file mode 100644 index 000000000..d9786cfd1 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/module_snapshot_win_test.cc @@ -0,0 +1,171 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/win/module_snapshot_win.h" + +#include +#include + +#include +#include +#include + +#include "base/files/file_path.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" +#include "client/crashpad_info.h" +#include "client/simple_string_dictionary.h" +#include "gtest/gtest.h" +#include "snapshot/annotation_snapshot.h" +#include "snapshot/win/pe_image_reader.h" +#include "snapshot/win/process_reader_win.h" +#include "test/test_paths.h" +#include "test/win/child_launcher.h" +#include "util/file/file_io.h" +#include "util/win/process_info.h" + +namespace crashpad { +namespace test { +namespace { + +enum TestType { + // Don't crash, just test the CrashpadInfo interface. + kDontCrash = 0, + + // The child process should crash by __debugbreak(). + kCrashDebugBreak, +}; + +void TestAnnotationsOnCrash(TestType type, + TestPaths::Architecture architecture) { + // Spawn a child process, passing it the pipe name to connect to. + base::FilePath child_test_executable = + TestPaths::BuildArtifact(L"snapshot", + L"annotations", + TestPaths::FileType::kExecutable, + architecture); + ChildLauncher child(child_test_executable, L""); + ASSERT_NO_FATAL_FAILURE(child.Start()); + + // Wait for the child process to indicate that it's done setting up its + // annotations via the CrashpadInfo interface. + char c; + CheckedReadFileExactly(child.stdout_read_handle(), &c, sizeof(c)); + + ProcessReaderWin process_reader; + ASSERT_TRUE(process_reader.Initialize(child.process_handle(), + ProcessSuspensionState::kRunning)); + + // Read all the kinds of annotations referenced from the CrashpadInfo + // structure. + const std::vector& modules = process_reader.Modules(); + std::map all_annotations_simple_map; + std::vector all_annotation_objects; + for (const ProcessInfo::Module& module : modules) { + internal::ModuleSnapshotWin module_snapshot; + module_snapshot.Initialize(&process_reader, module); + + std::map module_annotations_simple_map = + module_snapshot.AnnotationsSimpleMap(); + all_annotations_simple_map.insert(module_annotations_simple_map.begin(), + module_annotations_simple_map.end()); + + auto module_annotations_list = module_snapshot.AnnotationObjects(); + all_annotation_objects.insert(all_annotation_objects.end(), + module_annotations_list.begin(), + module_annotations_list.end()); + } + + // Verify the "simple map" annotations. + EXPECT_GE(all_annotations_simple_map.size(), 5u); + EXPECT_EQ(all_annotations_simple_map["#TEST# pad"], "crash"); + EXPECT_EQ(all_annotations_simple_map["#TEST# key"], "value"); + EXPECT_EQ(all_annotations_simple_map["#TEST# x"], "y"); + EXPECT_EQ(all_annotations_simple_map["#TEST# longer"], "shorter"); + EXPECT_EQ(all_annotations_simple_map["#TEST# empty_value"], ""); + + // Verify the typed annotation objects. + EXPECT_EQ(all_annotation_objects.size(), 3u); + bool saw_same_name_3 = false, saw_same_name_4 = false; + for (const auto& annotation : all_annotation_objects) { + EXPECT_EQ(annotation.type, + static_cast(Annotation::Type::kString)); + std::string value(reinterpret_cast(annotation.value.data()), + annotation.value.size()); + + if (annotation.name == "#TEST# one") { + EXPECT_EQ(value, "moocow"); + } else if (annotation.name == "#TEST# same-name") { + if (value == "same-name 3") { + EXPECT_FALSE(saw_same_name_3); + saw_same_name_3 = true; + } else if (value == "same-name 4") { + EXPECT_FALSE(saw_same_name_4); + saw_same_name_4 = true; + } else { + ADD_FAILURE() << "unexpected annotation value " << value; + } + } else { + ADD_FAILURE() << "unexpected annotation " << annotation.name; + } + } + + // Tell the child process to continue. + DWORD expected_exit_code; + switch (type) { + case kDontCrash: + c = ' '; + expected_exit_code = 0; + break; + case kCrashDebugBreak: + c = 'd'; + expected_exit_code = STATUS_BREAKPOINT; + break; + default: + FAIL(); + } + CheckedWriteFile(child.stdin_write_handle(), &c, sizeof(c)); + + EXPECT_EQ(child.WaitForExit(), expected_exit_code); +} + +TEST(ModuleSnapshotWinTest, DontCrash) { + TestAnnotationsOnCrash(kDontCrash, TestPaths::Architecture::kDefault); +} + +TEST(ModuleSnapshotWinTest, CrashDebugBreak) { + TestAnnotationsOnCrash(kCrashDebugBreak, TestPaths::Architecture::kDefault); +} + +#if defined(ARCH_CPU_64_BITS) +TEST(ModuleSnapshotWinTest, DontCrashWOW64) { + if (!TestPaths::Has32BitBuildArtifacts()) { + GTEST_SKIP(); + } + + TestAnnotationsOnCrash(kDontCrash, TestPaths::Architecture::k32Bit); +} + +TEST(ModuleSnapshotWinTest, CrashDebugBreakWOW64) { + if (!TestPaths::Has32BitBuildArtifacts()) { + GTEST_SKIP(); + } + + TestAnnotationsOnCrash(kCrashDebugBreak, TestPaths::Architecture::k32Bit); +} +#endif // ARCH_CPU_64_BITS + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/win/pe_image_annotations_reader.cc b/shared/sentry/external/crashpad/snapshot/win/pe_image_annotations_reader.cc new file mode 100644 index 000000000..9130e6d4f --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/pe_image_annotations_reader.cc @@ -0,0 +1,184 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/win/pe_image_annotations_reader.h" + +#include +#include + +#include + +#include "base/logging.h" +#include "base/strings/utf_string_conversions.h" +#include "client/annotation.h" +#include "client/simple_string_dictionary.h" +#include "snapshot/snapshot_constants.h" +#include "snapshot/win/pe_image_reader.h" +#include "snapshot/win/process_reader_win.h" +#include "util/win/process_structs.h" + +namespace crashpad { + +namespace process_types { + +template +struct Annotation { + typename Traits::Pointer link_node; + typename Traits::Pointer name; + typename Traits::Pointer value; + uint32_t size; + uint16_t type; +}; + +template +struct AnnotationList { + typename Traits::Pointer tail_pointer; + Annotation head; + Annotation tail; +}; + +} // namespace process_types + +PEImageAnnotationsReader::PEImageAnnotationsReader( + ProcessReaderWin* process_reader, + const PEImageReader* pe_image_reader, + const std::wstring& name) + : name_(name), + process_reader_(process_reader), + pe_image_reader_(pe_image_reader) { +} + +std::map PEImageAnnotationsReader::SimpleMap() const { + std::map simple_map_annotations; + if (process_reader_->Is64Bit()) { + ReadCrashpadSimpleAnnotations( + &simple_map_annotations); + } else { + ReadCrashpadSimpleAnnotations( + &simple_map_annotations); + } + return simple_map_annotations; +} + +std::vector PEImageAnnotationsReader::AnnotationsList() + const { + std::vector annotations; + if (process_reader_->Is64Bit()) { + ReadCrashpadAnnotationsList( + &annotations); + } else { + ReadCrashpadAnnotationsList( + &annotations); + } + return annotations; +} + +template +void PEImageAnnotationsReader::ReadCrashpadSimpleAnnotations( + std::map* simple_map_annotations) const { + process_types::CrashpadInfo crashpad_info; + if (!pe_image_reader_->GetCrashpadInfo(&crashpad_info) || + !crashpad_info.simple_annotations) { + return; + } + + std::vector + simple_annotations(SimpleStringDictionary::num_entries); + if (!process_reader_->Memory()->Read( + crashpad_info.simple_annotations, + simple_annotations.size() * sizeof(simple_annotations[0]), + &simple_annotations[0])) { + LOG(WARNING) << "could not read simple annotations from " + << base::WideToUTF8(name_); + return; + } + + for (const auto& entry : simple_annotations) { + size_t key_length = strnlen(entry.key, sizeof(entry.key)); + if (key_length) { + std::string key(entry.key, key_length); + std::string value(entry.value, strnlen(entry.value, sizeof(entry.value))); + if (!simple_map_annotations->insert(std::make_pair(key, value)).second) { + LOG(INFO) << "duplicate simple annotation " << key << " in " + << base::WideToUTF8(name_); + } + } + } +} + +// TODO(rsesek): When there is a platform-agnostic remote memory reader +// interface available, use it so that the implementation is not duplicated +// in the MachOImageAnnotationsReader. +template +void PEImageAnnotationsReader::ReadCrashpadAnnotationsList( + std::vector* vector_annotations) const { + process_types::CrashpadInfo crashpad_info; + if (!pe_image_reader_->GetCrashpadInfo(&crashpad_info) || + !crashpad_info.annotations_list) { + return; + } + + process_types::AnnotationList annotation_list_object; + if (!process_reader_->Memory()->Read(crashpad_info.annotations_list, + sizeof(annotation_list_object), + &annotation_list_object)) { + LOG(WARNING) << "could not read annotations list object in " + << base::WideToUTF8(name_); + return; + } + + process_types::Annotation current = annotation_list_object.head; + for (size_t index = 0; + current.link_node != annotation_list_object.tail_pointer && + index < kMaxNumberOfAnnotations; + ++index) { + if (!process_reader_->Memory()->Read( + current.link_node, sizeof(current), ¤t)) { + LOG(WARNING) << "could not read annotation at index " << index << " in " + << base::WideToUTF8(name_); + return; + } + + if (current.size == 0) { + continue; + } + + AnnotationSnapshot snapshot; + snapshot.type = current.type; + + char name[Annotation::kNameMaxLength]; + if (!process_reader_->Memory()->Read(current.name, std::size(name), name)) { + LOG(WARNING) << "could not read annotation name at index " << index + << " in " << base::WideToUTF8(name_); + continue; + } + + size_t name_length = strnlen(name, Annotation::kNameMaxLength); + snapshot.name = std::string(name, name_length); + + size_t value_length = + std::min(static_cast(current.size), Annotation::kValueMaxSize); + snapshot.value.resize(value_length); + if (!process_reader_->Memory()->Read( + current.value, value_length, snapshot.value.data())) { + LOG(WARNING) << "could not read annotation value at index " << index + << " in " << base::WideToUTF8(name_); + continue; + } + + vector_annotations->push_back(std::move(snapshot)); + } +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/win/pe_image_annotations_reader.h b/shared/sentry/external/crashpad/snapshot/win/pe_image_annotations_reader.h new file mode 100644 index 000000000..97b1e8fea --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/pe_image_annotations_reader.h @@ -0,0 +1,83 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_ANNOTATIONS_READER_H_ +#define CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_ANNOTATIONS_READER_H_ + +#include +#include +#include + +#include "snapshot/annotation_snapshot.h" + +namespace crashpad { + +class PEImageReader; +class ProcessReaderWin; + +//! \brief A reader of annotations stored in a PE image mapped into another +//! process. +//! +//! These annotations are stored for the benefit of crash reporters, and provide +//! information thought to be potentially useful for crash analysis. +//! +//! Currently, this class can decode information stored only in the CrashpadInfo +//! structure. This format is used by Crashpad clients. The "simple annotations" +//! are recovered from any module with a compatible data section, and are +//! included in the annotations returned by SimpleMap(). +class PEImageAnnotationsReader { + public: + //! \brief Constructs the object. + //! + //! \param[in] process_reader The reader for the remote process. + //! \param[in] pe_image_reader The PEImageReader for the PE image file + //! contained within the remote process. + //! \param[in] name The module's name, a string to be used in logged messages. + //! This string is for diagnostic purposes only, and may be empty. + PEImageAnnotationsReader(ProcessReaderWin* process_reader, + const PEImageReader* pe_image_reader, + const std::wstring& name); + + PEImageAnnotationsReader(const PEImageAnnotationsReader&) = delete; + PEImageAnnotationsReader& operator=(const PEImageAnnotationsReader&) = delete; + + ~PEImageAnnotationsReader() {} + + //! \brief Returns the module's annotations that are organized as key-value + //! pairs, where all keys and values are strings. + std::map SimpleMap() const; + + //! \brief Returns the module's annotations that are organized as a list of + //! typed annotation objects. + std::vector AnnotationsList() const; + + private: + // Reads CrashpadInfo::simple_annotations_ on behalf of SimpleMap(). + template + void ReadCrashpadSimpleAnnotations( + std::map* simple_map_annotations) const; + + // Reads CrashpadInfo::annotations_list_ on behalf of AnnotationsList(). + template + void ReadCrashpadAnnotationsList( + std::vector* vector_annotations) const; + + std::wstring name_; + ProcessReaderWin* process_reader_; // weak + const PEImageReader* pe_image_reader_; // weak +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_ANNOTATIONS_READER_H_ diff --git a/shared/sentry/external/crashpad/snapshot/win/pe_image_reader.cc b/shared/sentry/external/crashpad/snapshot/win/pe_image_reader.cc new file mode 100644 index 000000000..2a7b15109 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/pe_image_reader.cc @@ -0,0 +1,447 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/win/pe_image_reader.h" + +#include +#include + +#include +#include +#include + +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "client/crashpad_info.h" +#include "snapshot/win/pe_image_resource_reader.h" +#include "util/misc/from_pointer_cast.h" +#include "util/misc/pdb_structures.h" +#include "util/win/process_structs.h" + +namespace crashpad { + +namespace { + +// Map from Traits to an IMAGE_NT_HEADERSxx. +template +struct NtHeadersForTraits; + +template <> +struct NtHeadersForTraits { + using type = IMAGE_NT_HEADERS32; +}; + +template <> +struct NtHeadersForTraits { + using type = IMAGE_NT_HEADERS64; +}; + +} // namespace + +PEImageReader::PEImageReader() + : module_subrange_reader_(), + initialized_() { +} + +PEImageReader::~PEImageReader() { +} + +bool PEImageReader::Initialize(ProcessReaderWin* process_reader, + WinVMAddress address, + WinVMSize size, + const std::string& module_name) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + if (!module_subrange_reader_.Initialize( + process_reader, address, size, module_name)) { + return false; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +bool PEImageReader::GetCrashpadInfoSection(WinVMAddress* address, + WinVMSize* size) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (module_subrange_reader_.Is64Bit()) { + return GetCrashpadInfoSectionInternal( + address, size); + } else { + return GetCrashpadInfoSectionInternal( + address, size); + } +} + +template +bool PEImageReader::GetCrashpadInfo( + process_types::CrashpadInfo* crashpad_info) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + IMAGE_SECTION_HEADER section; + if (!GetSectionByName::type>("CPADinfo", + §ion)) { + return false; + } + + if (section.Misc.VirtualSize < + offsetof(process_types::CrashpadInfo, size) + + sizeof(crashpad_info->size)) { + LOG(WARNING) << "small crashpad info section size " + << section.Misc.VirtualSize << ", " + << module_subrange_reader_.name(); + return false; + } + + const WinVMAddress crashpad_info_address = Address() + section.VirtualAddress; + const WinVMSize crashpad_info_size = + std::min(static_cast(sizeof(*crashpad_info)), + static_cast(section.Misc.VirtualSize)); + if (!module_subrange_reader_.ReadMemory( + crashpad_info_address, crashpad_info_size, crashpad_info)) { + LOG(WARNING) << "could not read crashpad info from " + << module_subrange_reader_.name(); + return false; + } + + if (crashpad_info->size < sizeof(*crashpad_info)) { + // Zero out anything beyond the structure’s declared size. + memset(reinterpret_cast(crashpad_info) + crashpad_info->size, + 0, + sizeof(*crashpad_info) - crashpad_info->size); + } + + if (crashpad_info->signature != CrashpadInfo::kSignature || + crashpad_info->version != 1) { + LOG(WARNING) << base::StringPrintf( + "unexpected crashpad info signature 0x%x, version %u in %s", + crashpad_info->signature, + crashpad_info->version, + module_subrange_reader_.name().c_str()); + return false; + } + + // Don’t require strict equality, to leave wiggle room for sloppy linkers. + if (crashpad_info->size > section.Misc.VirtualSize) { + LOG(WARNING) << "crashpad info struct size " << crashpad_info->size + << " large for section size " << section.Misc.VirtualSize + << " in " << module_subrange_reader_.name(); + return false; + } + + if (crashpad_info->size > sizeof(*crashpad_info)) { + // This isn’t strictly a problem, because unknown fields will simply be + // ignored, but it may be of diagnostic interest. + LOG(INFO) << "large crashpad info size " << crashpad_info->size << ", " + << module_subrange_reader_.name(); + } + + return true; +} + +bool PEImageReader::DebugDirectoryInformation(UUID* uuid, + DWORD* age, + std::string* pdbname) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + IMAGE_DATA_DIRECTORY data_directory; + if (!ImageDataDirectoryEntry(IMAGE_DIRECTORY_ENTRY_DEBUG, &data_directory)) + return false; + + IMAGE_DEBUG_DIRECTORY debug_directory; + if (data_directory.Size % sizeof(debug_directory) != 0) + return false; + for (size_t offset = 0; offset < data_directory.Size; + offset += sizeof(debug_directory)) { + if (!module_subrange_reader_.ReadMemory( + Address() + data_directory.VirtualAddress + offset, + sizeof(debug_directory), + &debug_directory)) { + LOG(WARNING) << "could not read data directory from " + << module_subrange_reader_.name(); + return false; + } + + if (debug_directory.Type != IMAGE_DEBUG_TYPE_CODEVIEW) + continue; + + if (debug_directory.AddressOfRawData) { + if (debug_directory.SizeOfData < sizeof(CodeViewRecordPDB70)) { + LOG(WARNING) << "CodeView debug entry of unexpected size in " + << module_subrange_reader_.name(); + continue; + } + + std::unique_ptr data(new char[debug_directory.SizeOfData]); + if (!module_subrange_reader_.ReadMemory( + Address() + debug_directory.AddressOfRawData, + debug_directory.SizeOfData, + data.get())) { + LOG(WARNING) << "could not read debug directory from " + << module_subrange_reader_.name(); + return false; + } + + if (*reinterpret_cast(data.get()) != + CodeViewRecordPDB70::kSignature) { + LOG(WARNING) << "encountered non-7.0 CodeView debug record in " + << module_subrange_reader_.name(); + continue; + } + + CodeViewRecordPDB70* codeview = + reinterpret_cast(data.get()); + *uuid = codeview->uuid; + *age = codeview->age; + // This is a NUL-terminated string encoded in the codepage of the system + // where the binary was linked. We have no idea what that was, so we just + // assume ASCII. + *pdbname = std::string(reinterpret_cast(&codeview->pdb_name[0])); + return true; + } else if (debug_directory.PointerToRawData) { + // This occurs for non-PDB based debug information. We simply ignore these + // as we don't expect to encounter modules that will be in this format + // for which we'll actually have symbols. See + // https://crashpad.chromium.org/bug/47. + } + } + + return false; +} + +bool PEImageReader::VSFixedFileInfo( + VS_FIXEDFILEINFO* vs_fixed_file_info) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + IMAGE_DATA_DIRECTORY data_directory; + if (!ImageDataDirectoryEntry(IMAGE_DIRECTORY_ENTRY_RESOURCE, + &data_directory)) { + return false; + } + + PEImageResourceReader resource_reader; + if (!resource_reader.Initialize(module_subrange_reader_, data_directory)) { + return false; + } + + WinVMAddress address; + WinVMSize size; + if (!resource_reader.FindResourceByID( + FromPointerCast(VS_FILE_INFO), // RT_VERSION + VS_VERSION_INFO, + MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), + &address, + &size, + nullptr)) { + return false; + } + + // This structure is not declared anywhere in the SDK, but is documented at + // https://msdn.microsoft.com/library/ms647001.aspx. + struct VS_VERSIONINFO { + WORD wLength; + WORD wValueLength; + WORD wType; + + // The structure documentation on MSDN doesn’t show the [16], but it does + // say that it’s supposed to be L"VS_VERSION_INFO", which is is in fact a + // 16-character string (including its NUL terminator). + WCHAR szKey[16]; + + WORD Padding1; + VS_FIXEDFILEINFO Value; + + // Don’t include Children or the Padding2 that precedes it, because they may + // not be present. + // WORD Padding2; + // WORD Children; + }; + VS_VERSIONINFO version_info; + + if (size < sizeof(version_info)) { + LOG(WARNING) << "version info size " << size + << " too small for structure of size " << sizeof(version_info) + << " in " << module_subrange_reader_.name(); + return false; + } + + if (!module_subrange_reader_.ReadMemory( + address, sizeof(version_info), &version_info)) { + LOG(WARNING) << "could not read version info from " + << module_subrange_reader_.name(); + return false; + } + + if (version_info.wLength < sizeof(version_info) || + version_info.wValueLength != sizeof(version_info.Value) || + version_info.wType != 0 || + wcsncmp(version_info.szKey, + L"VS_VERSION_INFO", + std::size(version_info.szKey)) != 0) { + LOG(WARNING) << "unexpected VS_VERSIONINFO in " + << module_subrange_reader_.name(); + return false; + } + + if (version_info.Value.dwSignature != VS_FFI_SIGNATURE || + version_info.Value.dwStrucVersion != VS_FFI_STRUCVERSION) { + LOG(WARNING) << "unexpected VS_FIXEDFILEINFO in " + << module_subrange_reader_.name(); + return false; + } + + *vs_fixed_file_info = version_info.Value; + vs_fixed_file_info->dwFileFlags &= vs_fixed_file_info->dwFileFlagsMask; + return true; +} + +template +bool PEImageReader::GetCrashpadInfoSectionInternal(WinVMAddress* address, + WinVMSize* size) const { + IMAGE_SECTION_HEADER section; + if (!GetSectionByName::type>("CPADinfo", + §ion)) { + return false; + } + + process_types::CrashpadInfo crashpad_info; + if (section.Misc.VirtualSize < + offsetof(process_types::CrashpadInfo, size) + + sizeof(crashpad_info.size)) { + LOG(WARNING) << "small crashpad info section size " + << section.Misc.VirtualSize << ", " + << module_subrange_reader_.name(); + return false; + } + + *address = Address() + section.VirtualAddress; + *size = std::min(sizeof(crashpad_info), section.Misc.VirtualSize); + + return true; +} + +template +bool PEImageReader::ReadNtHeaders(NtHeadersType* nt_headers, + WinVMAddress* nt_headers_address) const { + IMAGE_DOS_HEADER dos_header; + if (!module_subrange_reader_.ReadMemory( + Address(), sizeof(IMAGE_DOS_HEADER), &dos_header)) { + LOG(WARNING) << "could not read dos header from " + << module_subrange_reader_.name(); + return false; + } + + if (dos_header.e_magic != IMAGE_DOS_SIGNATURE) { + LOG(WARNING) << "invalid e_magic in dos header of " + << module_subrange_reader_.name(); + return false; + } + + WinVMAddress local_nt_headers_address = Address() + dos_header.e_lfanew; + if (!module_subrange_reader_.ReadMemory( + local_nt_headers_address, sizeof(NtHeadersType), nt_headers)) { + LOG(WARNING) << "could not read nt headers from " + << module_subrange_reader_.name(); + return false; + } + + if (nt_headers->Signature != IMAGE_NT_SIGNATURE) { + LOG(WARNING) << "invalid signature in nt headers of " + << module_subrange_reader_.name(); + return false; + } + + if (nt_headers_address) + *nt_headers_address = local_nt_headers_address; + + return true; +} + +template +bool PEImageReader::GetSectionByName(const std::string& name, + IMAGE_SECTION_HEADER* section) const { + if (name.size() > sizeof(section->Name)) { + LOG(WARNING) << "supplied section name too long " << name; + return false; + } + + NtHeadersType nt_headers; + WinVMAddress nt_headers_address; + if (!ReadNtHeaders(&nt_headers, &nt_headers_address)) + return false; + + WinVMAddress first_section_address = + nt_headers_address + offsetof(NtHeadersType, OptionalHeader) + + nt_headers.FileHeader.SizeOfOptionalHeader; + for (DWORD i = 0; i < nt_headers.FileHeader.NumberOfSections; ++i) { + WinVMAddress section_address = + first_section_address + sizeof(IMAGE_SECTION_HEADER) * i; + if (!module_subrange_reader_.ReadMemory( + section_address, sizeof(IMAGE_SECTION_HEADER), section)) { + LOG(WARNING) << "could not read section " << i << " from " + << module_subrange_reader_.name(); + return false; + } + if (strncmp(reinterpret_cast(section->Name), + name.c_str(), + sizeof(section->Name)) == 0) { + return true; + } + } + + return false; +} + +bool PEImageReader::ImageDataDirectoryEntry(size_t index, + IMAGE_DATA_DIRECTORY* entry) const { + bool rv; + if (module_subrange_reader_.Is64Bit()) { + rv = ImageDataDirectoryEntryT(index, entry); + } else { + rv = ImageDataDirectoryEntryT(index, entry); + } + + return rv && entry->VirtualAddress != 0 && entry->Size != 0; +} + +template +bool PEImageReader::ImageDataDirectoryEntryT( + size_t index, + IMAGE_DATA_DIRECTORY* entry) const { + NtHeadersType nt_headers; + if (!ReadNtHeaders(&nt_headers, nullptr)) { + return false; + } + + if (nt_headers.FileHeader.SizeOfOptionalHeader < + offsetof(decltype(nt_headers.OptionalHeader), DataDirectory[index]) + + sizeof(nt_headers.OptionalHeader.DataDirectory[index]) || + nt_headers.OptionalHeader.NumberOfRvaAndSizes <= index) { + return false; + } + + *entry = nt_headers.OptionalHeader.DataDirectory[index]; + return true; +} + +// Explicit instantiations with the only 2 valid template arguments to avoid +// putting the body of the function in the header. +template bool PEImageReader::GetCrashpadInfo( + process_types::CrashpadInfo* + crashpad_info) const; +template bool PEImageReader::GetCrashpadInfo( + process_types::CrashpadInfo* + crashpad_info) const; + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/win/pe_image_reader.h b/shared/sentry/external/crashpad/snapshot/win/pe_image_reader.h new file mode 100644 index 000000000..a2bd28ecc --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/pe_image_reader.h @@ -0,0 +1,204 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_READER_H_ +#define CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_READER_H_ + +#include +#include +#include + +#include + +#include "snapshot/win/process_subrange_reader.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/misc/uuid.h" +#include "util/win/address_types.h" +#include "util/win/process_structs.h" + +namespace crashpad { + +class ProcessReaderWin; + +namespace process_types { + +template +struct CrashpadInfo { + uint32_t signature; + uint32_t size; + uint32_t version; + uint32_t indirectly_referenced_memory_cap; + uint32_t padding_0; + uint8_t crashpad_handler_behavior; // TriState. + uint8_t system_crash_reporter_forwarding; // TriState. + uint8_t gather_indirectly_referenced_memory; // TriState. + uint8_t padding_1; + typename Traits::Pointer extra_address_ranges; + typename Traits::Pointer simple_annotations; + typename Traits::Pointer user_data_minidump_stream_head; + typename Traits::Pointer annotations_list; +}; + +} // namespace process_types + +//! \brief A reader for PE images mapped into another process. +//! +//! This class is capable of reading both 32-bit and 64-bit images based on the +//! bitness of the remote process. +//! +//! \sa PEImageAnnotationsReader +//! \sa PEImageResourceReader +class PEImageReader { + public: + PEImageReader(); + + PEImageReader(const PEImageReader&) = delete; + PEImageReader& operator=(const PEImageReader&) = delete; + + ~PEImageReader(); + + //! \brief Initializes the reader. + //! + //! This method must be called only once on an object. This method must be + //! called successfully before any other method in this class may be called. + //! + //! \param[in] process_reader The reader for the remote process. + //! \param[in] address The address, in the remote process' address space, + //! where the `IMAGE_DOS_HEADER` is located. + //! \param[in] size The size of the image. + //! \param[in] module_name The module's name, a string to be used in logged + //! messages. This string is for diagnostic purposes. + //! + //! \return `true` if the image was read successfully, `false` otherwise, with + //! an appropriate message logged. + bool Initialize(ProcessReaderWin* process_reader, + WinVMAddress address, + WinVMSize size, + const std::string& module_name); + + //! \brief Returns the image's load address. + //! + //! This is the value passed as \a address to Initialize(). + WinVMAddress Address() const { return module_subrange_reader_.Base(); } + + //! \brief Returns the image's size. + //! + //! This is the value passed as \a size to Initialize(). + WinVMSize Size() const { return module_subrange_reader_.Size(); } + + //! \brief Obtains the module's CrashpadInfo structure address and size. + //! + //! \param[out] address The CrashpadInfo structure address. + //! \param[out] size The CrashpadInfo structure size. + //! + //! \return `true` on success, `false` on failure. If the module does not have + //! a `CPADinfo` section, this will return `false` without logging any + //! messages. Other failures will result in messages being logged. + bool GetCrashpadInfoSection(WinVMAddress* address, WinVMSize* size) const; + + //! \brief Obtains the module's CrashpadInfo structure. + //! + //! \return `true` on success, `false` on failure. If the module does not have + //! a `CPADinfo` section, this will return `false` without logging any + //! messages. Other failures will result in messages being logged. + template + bool GetCrashpadInfo( + process_types::CrashpadInfo* crashpad_info) const; + + //! \brief Obtains information from the module's debug directory, if any. + //! + //! \param[out] uuid The unique identifier of the executable/PDB. + //! \param[out] age The age field for the pdb (the number of times it's been + //! relinked). + //! \param[out] pdbname Name of the pdb file. + //! + //! \return `true` on success, with the parameters set appropriately. `false` + //! on failure. This method may return `false` without logging anything in + //! the case of a module that does not contain relevant debugging + //! information but is otherwise properly structured. + bool DebugDirectoryInformation(UUID* uuid, + DWORD* age, + std::string* pdbname) const; + + //! \brief Obtains the module’s `VS_FIXEDFILEINFO`, containing its version and + //! type information. + //! + //! The data obtained from this method should be equivalent to what could be + //! obtained by calling GetModuleVersionAndType(). Avoiding that function + //! ensures that the data in the module loaded into the remote process will be + //! used as-is, without the risks associated with loading the module into the + //! reading process. + //! + //! \param[out] vs_fixed_file_info The VS_FIXEDFILEINFO on success. + //! VS_FIXEDFILEINFO::dwFileFlags will have been masked with + //! VS_FIXEDFILEINFO::dwFileFlagsMask already. + //! + //! \return `true` on success. `false` if the module does not contain this + //! information, without logging any messages. `false` on failure, with + //! a message logged. + bool VSFixedFileInfo(VS_FIXEDFILEINFO* vs_fixed_file_info) const; + + private: + //! \brief Performs the internal logic for GetCrashpadInfoSection(). + //! + //! \sa GetCrashpadInfoSection + template + bool GetCrashpadInfoSectionInternal(WinVMAddress* address, + WinVMSize* size) const; + + //! \brief Reads the `IMAGE_NT_HEADERS` from the beginning of the image. + //! + //! \param[out] nt_headers The contents of the templated NtHeadersType + //! structure read from the remote process. + //! \param[out] nt_headers_address The address of the templated NtHeadersType + //! structure in the remote process’ address space. If this information is + //! not needed, this parameter may be `nullptr`. + //! + //! \return `true` on success, with \a nt_headers and optionally \a + //! nt_headers_address set appropriately. `false` on failure, with a + //! message logged. + template + bool ReadNtHeaders(NtHeadersType* nt_headers, + WinVMAddress* nt_headers_address) const; + + //! \brief Finds a given section by name in the image. + template + bool GetSectionByName(const std::string& name, + IMAGE_SECTION_HEADER* section) const; + + //! \brief Finds the `IMAGE_DATA_DIRECTORY` in + //! `IMAGE_OPTIONAL_HEADER::DataDirectory` at the specified \a index. + //! + //! \param[in] index An `IMAGE_DIRECTORY_ENTRY_*` constant specifying the + //! data to be returned. + //! \param[out] entry The `IMAGE_DATA_DIRECTORY` found within the module. + //! + //! \return `true` on success, with \a entry set appropriately. `false` if the + //! module does not contain the specified information, without logging a + //! message. `false` on failure, with a message logged. + bool ImageDataDirectoryEntry(size_t index, IMAGE_DATA_DIRECTORY* entry) const; + + //! \brief A templatized helper for ImageDataDirectoryEntry() to account for + //! differences in \a NtHeadersType. + template + bool ImageDataDirectoryEntryT(size_t index, + IMAGE_DATA_DIRECTORY* entry) const; + + ProcessSubrangeReader module_subrange_reader_; + InitializationStateDcheck initialized_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_READER_H_ diff --git a/shared/sentry/external/crashpad/snapshot/win/pe_image_reader_test.cc b/shared/sentry/external/crashpad/snapshot/win/pe_image_reader_test.cc new file mode 100644 index 000000000..e364ed732 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/pe_image_reader_test.cc @@ -0,0 +1,193 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/win/pe_image_reader.h" + +#ifndef PSAPI_VERSION +#define PSAPI_VERSION 2 +#endif +#include + +#include "base/files/file_path.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "gtest/gtest.h" +#include "snapshot/win/process_reader_win.h" +#include "test/errors.h" +#include "test/scoped_module_handle.h" +#include "test/test_paths.h" +#include "util/misc/from_pointer_cast.h" +#include "util/win/get_module_information.h" +#include "util/win/module_version.h" +#include "util/win/process_info.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(PEImageReader, DebugDirectory) { + base::FilePath module_path = + TestPaths::BuildArtifact(L"snapshot", + L"image_reader_module", + TestPaths::FileType::kLoadableModule); + ScopedModuleHandle module_handle(LoadLibrary(module_path.value().c_str())); + ASSERT_TRUE(module_handle.valid()) << ErrorMessage("LoadLibrary"); + + PEImageReader pe_image_reader; + ProcessReaderWin process_reader; + ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(), + ProcessSuspensionState::kRunning)); + + MODULEINFO module_info; + ASSERT_TRUE(CrashpadGetModuleInformation(GetCurrentProcess(), + module_handle.get(), + &module_info, + sizeof(module_info))) + << ErrorMessage("GetModuleInformation"); + EXPECT_EQ(module_info.lpBaseOfDll, module_handle.get()); + + base::FilePath module_basename = module_path.BaseName(); + ASSERT_TRUE(pe_image_reader.Initialize( + &process_reader, + FromPointerCast(module_handle.get()), + module_info.SizeOfImage, + base::WideToUTF8(module_basename.value()))); + + UUID uuid; + DWORD age; + std::string pdbname; + ASSERT_TRUE(pe_image_reader.DebugDirectoryInformation(&uuid, &age, &pdbname)); + std::string module_name = + base::WideToUTF8(module_basename.RemoveFinalExtension().value()); + EXPECT_NE(pdbname.find(module_name), std::string::npos); + const std::string suffix(".pdb"); + EXPECT_EQ( + pdbname.compare(pdbname.size() - suffix.size(), suffix.size(), suffix), + 0); +} + +void TestVSFixedFileInfo(ProcessReaderWin* process_reader, + const ProcessInfo::Module& module, + bool known_dll) { + PEImageReader pe_image_reader; + ASSERT_TRUE(pe_image_reader.Initialize(process_reader, + module.dll_base, + module.size, + base::WideToUTF8(module.name))); + + VS_FIXEDFILEINFO observed; + const bool observed_rv = pe_image_reader.VSFixedFileInfo(&observed); + ASSERT_TRUE(observed_rv || !known_dll); + + if (observed_rv) { + EXPECT_EQ(observed.dwSignature, static_cast(VS_FFI_SIGNATURE)); + EXPECT_EQ(observed.dwStrucVersion, static_cast(VS_FFI_STRUCVERSION)); + EXPECT_EQ(observed.dwFileFlags & ~observed.dwFileFlagsMask, 0u); + if (known_dll) { + EXPECT_EQ(observed.dwFileOS, static_cast(VOS_NT_WINDOWS32)); + EXPECT_EQ(observed.dwFileType, static_cast(VFT_DLL)); + } else { + EXPECT_NE(observed.dwFileOS & VOS_NT_WINDOWS32, 0u); + + // VFT_DRV/VFT2_DRV_NETWORK is for nsi.dll, “network service interface.†+ // It’s not normally loaded, but has been observed to be loaded in some + // cases. + EXPECT_TRUE(observed.dwFileType == VFT_APP || + observed.dwFileType == VFT_DLL || + (observed.dwFileType == VFT_DRV && + observed.dwFileSubtype == VFT2_DRV_NETWORK)) + << base::StringPrintf("type 0x%lx, subtype 0x%lx", + observed.dwFileType, + observed.dwFileSubtype); + } + } + + base::FilePath module_path(module.name); + + const DWORD version = GetVersion(); + const int major_version = LOBYTE(LOWORD(version)); + const int minor_version = HIBYTE(LOWORD(version)); + if (major_version > 6 || (major_version == 6 && minor_version >= 2)) { + // Windows 8 or later. + // + // Use BaseName() to ensure that GetModuleVersionAndType() finds the + // already-loaded module with the specified name. Otherwise, dwFileVersionMS + // may not match. This appears to be related to the changes made in Windows + // 8.1 to GetVersion() and GetVersionEx() for non-manifested applications + module_path = module_path.BaseName(); + } + + VS_FIXEDFILEINFO expected; + const bool expected_rv = GetModuleVersionAndType(module_path, &expected); + ASSERT_TRUE(expected_rv || !known_dll); + + EXPECT_EQ(observed_rv, expected_rv); + + if (observed_rv && expected_rv) { + EXPECT_EQ(observed.dwSignature, expected.dwSignature); + EXPECT_EQ(observed.dwStrucVersion, expected.dwStrucVersion); + EXPECT_EQ(observed.dwFileVersionMS, expected.dwFileVersionMS); + EXPECT_EQ(observed.dwFileVersionLS, expected.dwFileVersionLS); + EXPECT_EQ(observed.dwProductVersionMS, expected.dwProductVersionMS); + EXPECT_EQ(observed.dwProductVersionLS, expected.dwProductVersionLS); + EXPECT_EQ(observed.dwFileFlagsMask, expected.dwFileFlagsMask); + EXPECT_EQ(observed.dwFileFlags, expected.dwFileFlags); + EXPECT_EQ(observed.dwFileOS, expected.dwFileOS); + EXPECT_EQ(observed.dwFileType, expected.dwFileType); + EXPECT_EQ(observed.dwFileSubtype, expected.dwFileSubtype); + EXPECT_EQ(observed.dwFileDateMS, expected.dwFileDateMS); + EXPECT_EQ(observed.dwFileDateLS, expected.dwFileDateLS); + } +} + +TEST(PEImageReader, VSFixedFileInfo_OneModule) { + ProcessReaderWin process_reader; + ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(), + ProcessSuspensionState::kRunning)); + + static constexpr wchar_t kModuleName[] = L"kernel32.dll"; + const HMODULE module_handle = GetModuleHandle(kModuleName); + ASSERT_TRUE(module_handle) << ErrorMessage("GetModuleHandle"); + + MODULEINFO module_info; + ASSERT_TRUE(CrashpadGetModuleInformation( + GetCurrentProcess(), module_handle, &module_info, sizeof(module_info))) + << ErrorMessage("GetModuleInformation"); + EXPECT_EQ(module_info.lpBaseOfDll, module_handle); + + ProcessInfo::Module module; + module.name = kModuleName; + module.dll_base = FromPointerCast(module_info.lpBaseOfDll); + module.size = module_info.SizeOfImage; + + TestVSFixedFileInfo(&process_reader, module, true); +} + +TEST(PEImageReader, VSFixedFileInfo_AllModules) { + ProcessReaderWin process_reader; + ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(), + ProcessSuspensionState::kRunning)); + + const std::vector& modules = process_reader.Modules(); + EXPECT_GT(modules.size(), 2u); + + for (const auto& module : modules) { + SCOPED_TRACE(base::WideToUTF8(module.name)); + TestVSFixedFileInfo(&process_reader, module, false); + } +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/win/pe_image_resource_reader.cc b/shared/sentry/external/crashpad/snapshot/win/pe_image_resource_reader.cc new file mode 100644 index 000000000..f37394523 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/pe_image_resource_reader.cc @@ -0,0 +1,279 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/win/pe_image_resource_reader.h" + +#include +#include + +#include "base/logging.h" + +namespace { + +void AddLanguageAndNeutralSublanguage(std::vector* languages, + uint16_t language) { + languages->push_back(language); + if (SUBLANGID(language) != SUBLANG_NEUTRAL) { + languages->push_back(MAKELANGID(PRIMARYLANGID(language), SUBLANG_NEUTRAL)); + } +} + +} // namespace + +namespace crashpad { + +PEImageResourceReader::PEImageResourceReader() + : resources_subrange_reader_(), + module_base_(0), + initialized_() { +} + +PEImageResourceReader::~PEImageResourceReader() {} + +bool PEImageResourceReader::Initialize( + const ProcessSubrangeReader& module_subrange_reader, + const IMAGE_DATA_DIRECTORY& resources_directory_entry) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + module_base_ = module_subrange_reader.Base(); + + if (!resources_subrange_reader_.InitializeSubrange( + module_subrange_reader, + module_base_ + resources_directory_entry.VirtualAddress, + resources_directory_entry.Size, + "resources")) { + return false; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +bool PEImageResourceReader::FindResourceByID(uint16_t type, + uint16_t name, + uint16_t language, + WinVMAddress* address, + WinVMSize* size, + uint32_t* code_page) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + // The root resource directory is at the beginning of the resources area + // within the module. + const uint32_t name_directory_offset = + GetEntryFromResourceDirectoryByID(0, type, true); + if (!name_directory_offset) { + return false; + } + + const uint32_t language_directory_offset = + GetEntryFromResourceDirectoryByID(name_directory_offset, name, true); + if (!language_directory_offset) { + return false; + } + + // The definition of IMAGE_RESOURCE_DIRECTORY_ENTRY in has a comment + // saying that its offsets are relative to “the resource directory of the data + // associated with this directory entryâ€. That could be interpreted to mean + // that language_directory_offset is relative to name_directory_offset, since + // the language directory entry is found within the name directory. This is + // not correct. All resource offsets are relative to the resources area within + // the module. + const uint32_t data_offset = GetEntryFromResourceDirectoryByLanguage( + language_directory_offset, language); + if (!data_offset) { + return false; + } + + IMAGE_RESOURCE_DATA_ENTRY data_entry; + if (!resources_subrange_reader_.ReadMemory( + resources_subrange_reader_.Base() + data_offset, + sizeof(data_entry), + &data_entry)) { + LOG(WARNING) << "could not read resource data entry from " + << resources_subrange_reader_.name(); + return false; + } + + // The definition of IMAGE_RESOURCE_DATA_ENTRY in has a comment + // saying that OffsetToData is relative to the beginning of the resource data. + // This is not correct. It’s module-relative. + *address = module_base_ + data_entry.OffsetToData; + *size = data_entry.Size; + if (code_page) { + *code_page = data_entry.CodePage; + } + + return true; +} + +uint32_t PEImageResourceReader::GetEntryFromResourceDirectoryByID( + uint32_t language_directory_offset, + uint16_t id, + bool want_subdirectory) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + std::vector entries_by_id; + if (!ReadResourceDirectory( + language_directory_offset, nullptr, nullptr, &entries_by_id)) { + return 0; + } + + const auto entry_it = + std::find_if(entries_by_id.begin(), + entries_by_id.end(), + [id](const IMAGE_RESOURCE_DIRECTORY_ENTRY& entry) { + return !entry.NameIsString && entry.Id == id; + }); + if (entry_it != entries_by_id.end()) { + if ((entry_it->DataIsDirectory != 0) != want_subdirectory) { + LOG(WARNING) << "expected " << (want_subdirectory ? "" : "non-") + << "directory for entry id " << id << " in " + << resources_subrange_reader_.name(); + return 0; + } + + return entry_it->DataIsDirectory ? entry_it->OffsetToDirectory + : entry_it->OffsetToData; + } + + return 0; +} + +uint32_t PEImageResourceReader::GetEntryFromResourceDirectoryByLanguage( + uint32_t resource_directory_offset, + uint16_t language) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + std::vector entries_by_language; + if (!ReadResourceDirectory( + resource_directory_offset, nullptr, nullptr, &entries_by_language)) { + return 0; + } + + if (entries_by_language.empty()) { + return 0; + } + + // https://msdn.microsoft.com/library/cc194810.aspx + // + // TODO(mark): It seems like FindResourceEx() might do something more complex. + // It would be best to mimic its behavior. + std::vector try_languages; + if (PRIMARYLANGID(language) != LANG_NEUTRAL) { + AddLanguageAndNeutralSublanguage(&try_languages, language); + } else { + if (SUBLANGID(language) != SUBLANG_SYS_DEFAULT) { + AddLanguageAndNeutralSublanguage(&try_languages, + LANGIDFROMLCID(GetThreadLocale())); + AddLanguageAndNeutralSublanguage(&try_languages, + LANGIDFROMLCID(GetUserDefaultLCID())); + } + if (SUBLANGID(language) != SUBLANG_DEFAULT) { + AddLanguageAndNeutralSublanguage(&try_languages, + LANGIDFROMLCID(GetSystemDefaultLCID())); + } + } + + try_languages.push_back(MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL)); + try_languages.push_back(MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT)); + + for (const auto try_language : try_languages) { + const auto entry_it = std::find_if( + entries_by_language.begin(), + entries_by_language.end(), + [try_language](const IMAGE_RESOURCE_DIRECTORY_ENTRY& entry) { + return !entry.NameIsString && entry.Id == try_language; + }); + if (entry_it != entries_by_language.end()) { + if (entry_it->DataIsDirectory) { + LOG(WARNING) << "expected non-directory for entry language " + << try_language << " in " + << resources_subrange_reader_.name(); + return 0; + } + + return entry_it->OffsetToData; + } + } + + // Fall back to the first entry in the list. + const auto& entry = entries_by_language.front(); + if (entry.DataIsDirectory) { + LOG(WARNING) << "expected non-directory for entry in " + << resources_subrange_reader_.name(); + return 0; + } + + return entry.OffsetToData; +} + +bool PEImageResourceReader::ReadResourceDirectory( + uint32_t resource_directory_offset, + IMAGE_RESOURCE_DIRECTORY* resource_directory, + std::vector* named_entries, + std::vector* id_entries) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + // resource_directory is optional, but it’s still needed locally even if the + // caller isn’t interested in it. + std::unique_ptr local_resource_directory; + if (!resource_directory) { + local_resource_directory.reset(new IMAGE_RESOURCE_DIRECTORY); + resource_directory = local_resource_directory.get(); + } + + const WinVMAddress address = + resources_subrange_reader_.Base() + resource_directory_offset; + + if (!resources_subrange_reader_.ReadMemory( + address, sizeof(*resource_directory), resource_directory)) { + LOG(WARNING) << "could not read resource directory from " + << resources_subrange_reader_.name(); + return false; + } + + if (named_entries) { + named_entries->clear(); + named_entries->resize(resource_directory->NumberOfNamedEntries); + if (!named_entries->empty() && + !resources_subrange_reader_.ReadMemory( + address + sizeof(*resource_directory), + named_entries->size() * sizeof((*named_entries)[0]), + &(*named_entries)[0])) { + LOG(WARNING) << "could not read resource directory named entries from " + << resources_subrange_reader_.name(); + return false; + } + } + + if (id_entries) { + id_entries->clear(); + id_entries->resize(resource_directory->NumberOfIdEntries); + if (!id_entries->empty() && + !resources_subrange_reader_.ReadMemory( + address + sizeof(*resource_directory) + + resource_directory->NumberOfNamedEntries * + sizeof(IMAGE_RESOURCE_DIRECTORY_ENTRY), + id_entries->size() * sizeof((*id_entries)[0]), + &(*id_entries)[0])) { + LOG(WARNING) << "could not read resource directory ID entries from " + << resources_subrange_reader_.name(); + return false; + } + } + + return true; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/win/pe_image_resource_reader.h b/shared/sentry/external/crashpad/snapshot/win/pe_image_resource_reader.h new file mode 100644 index 000000000..a8468db7c --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/pe_image_resource_reader.h @@ -0,0 +1,180 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_RESOURCE_READER_H_ +#define CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_RESOURCE_READER_H_ + +#include +#include + +#include + +#include "snapshot/win/process_subrange_reader.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/win/address_types.h" + +namespace crashpad { + +//! \brief A reader for resources stored in PE images mapped into another +//! process. +//! +//! \sa PEImageReader +class PEImageResourceReader { + public: + PEImageResourceReader(); + + PEImageResourceReader(const PEImageResourceReader&) = delete; + PEImageResourceReader& operator=(const PEImageResourceReader&) = delete; + + ~PEImageResourceReader(); + + //! \brief Initializes the resource reader. + //! + //! \param[in] module_subrange_reader The reader for the module. + //! \param[in] resources_directory_entry The module’s `IMAGE_DATA_DIRECTORY` + //! for its resources area. This is taken from the module’s + //! `IMAGE_OPTIONAL_HEADER::DataDirectory` at index + //! `IMAGE_DIRECTORY_ENTRY_RESOURCE`. + //! + //! \return `true` on success, `false` on failure with a message logged. + bool Initialize(const ProcessSubrangeReader& module_subrange_reader, + const IMAGE_DATA_DIRECTORY& resources_directory_entry); + + //! \brief Locates a resource in a module by its ID. + //! + //! This method is similar to `FindResourceEx()`, but it operates on modules + //! loaded in a remote process’ address space. It is not necessary to + //! `LoadLibrary()` a module into a process in order to use this method. + //! + //! No support is provided at present for locating resources by \a type or \a + //! name using strings as opposed to integer identifiers. + //! + //! Languages are scanned in the order determined by + //! GetEntryFromResourceDirectoryByLanguage(). + //! + //! \param[in] type The integer identifier of the resource type, as in the + //! `lpType` parameter of `FindResourceEx()`. + //! \param[in] name The integer identifier of the resource, as in the `lpName` + //! parameter of `FindResourceEx()`. + //! \param[in] language The language of the resource, as in the `wLanguage` + //! parameter of `FindResourceEx()`. + //! \param[out] address The address, in the remote process’ address space, of + //! the resource data. + //! \param[out] size The size of the resource data. + //! \param[out] code_page The code page used to encode textual resource data. + //! This parameter is optional. + //! + //! \return `true` on success, with the out parameters set appropriately. + //! `false` if the resource was not found, without logging any messages. + //! `false` on failure, with a message logged. + bool FindResourceByID(uint16_t type, + uint16_t name, + uint16_t language, + WinVMAddress* address, + WinVMSize* size, + uint32_t* code_page) const; + + private: + //! \brief Locates a resource directory entry within a resource directory by + //! integer ID. + //! + //! \param[in] resource_directory_offset The offset, in the module’s resources + //! area, of the resource directory to search. + //! \param[in] id The integer identifier of the resource to search for. + //! \param[in] want_subdirectory `true` if the resource directory entry is + //! expected to be a resource directory itself, `false` otherwise. + //! + //! \return The offset, in the module’s resources area, of the entry that was + //! found. On failure, `0`. `0` is technically a valid offset, but it + //! corresponds to the root resource directory, which should never be the + //! offset of another resource directory entry. If \a id was not found, + //! `0` will be returned without logging anything. For other failures, a + //! message will be logged. + uint32_t GetEntryFromResourceDirectoryByID(uint32_t resource_directory_offset, + uint16_t id, + bool want_subdirectory) const; + + //! \brief Locates a resource directory entry within a resource directory by + //! language. + //! + //! This method is similar to GetEntryFromResourceDirectoryByID() with \a + //! want_subdirectory set to `false`. Attempts are made to locate the resource + //! by using these languages: + //!
      + //!
    • If \a language is `LANG_NEUTRAL`:
    • + //!
        + //!
      • Unless `SUBLANG_SYS_DEFAULT` is specified, the language of the + //! thread’s locale, with its normal sublanguage and with + //! `SUBLANG_NEUTRAL`.
      • + //!
      • Unless `SUBLANG_SYS_DEFAULT` is specified, the language of the + //! user’s default locale, with its normal sublanguage and with + //! `SUBLANG_NEUTRAL`.
      • + //!
      • Unless `SUBLANG_DEFAULT` is specified, the language of the + //! system’s default locale, with its normal sublanguage and with + //! `SUBLANG_NEUTRAL`.
      • + //!
      + //!
    • If \a language is not `LANG_NEUTRAL`:
    • + //!
        + //!
      • \a language
      • + //!
      • \a language, with `SUBLANG_NEUTRAL`
      • + //!
      + //!
    • `LANG_NEUTRAL` with `SUBLANG_NEUTRAL`
    • + //!
    • `LANG_ENGLISH` with `SUBLANG_DEFAULT`
    • + //!
    • If none of the above match, the first language found
    • + //!
    + //! + //! If only a specific language is desired without any fallbacks, call + //! GetEntryFromResourceDirectoryByID() with the language directory’s offset + //! instead, passing the desired language in the \a id parameter, and `false` + //! for \a want_subdirectory. + //! + //! \param[in] language_directory_offset The offset, in the module’s resources + //! area, of the resource directory to search. + //! \param[in] language The language of the resource to search for. + //! + //! \return The return value is as in GetEntryFromResourceDirectoryByID(). + uint32_t GetEntryFromResourceDirectoryByLanguage( + uint32_t language_directory_offset, + uint16_t language) const; + + //! \brief Reads a resource directory. + //! + //! \param[in] resource_directory_offset The offset, in the module’s resources + //! area, of the resource directory to read. + //! \param[out] resource_directory The `IMAGE_RESOURCE_DIRECTORY` structure. + //! This parameter is optional. + //! \param[out] named_entries A vector of \a + //! resource_directory->NumberOfNamedEntries + //! `IMAGE_RESOURCE_DIRECTORY_ENTRY` items that follow the resource + //! directory. This parameter is optional. + //! \param[out] id_entries A vector of \a + //! resource_directory->NumberOfIdEntries `IMAGE_RESOURCE_DIRECTORY_ENTRY` + //! items that follow the named entries. This parameter is optional. + //! + //! \return `true` on success, with the out parameters set appropriately. + //! `false` on failure with a message logged. + bool ReadResourceDirectory( + uint32_t resource_directory_offset, + IMAGE_RESOURCE_DIRECTORY* resource_directory, + std::vector* named_entries, + std::vector* id_entries) const; + + ProcessSubrangeReader resources_subrange_reader_; + WinVMAddress module_base_; + InitializationStateDcheck initialized_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_WIN_PE_IMAGE_RESOURCE_READER_H_ diff --git a/shared/sentry/external/crashpad/snapshot/win/process_reader_win.cc b/shared/sentry/external/crashpad/snapshot/win/process_reader_win.cc new file mode 100644 index 000000000..ff4a23615 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/process_reader_win.cc @@ -0,0 +1,484 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/win/process_reader_win.h" + +#ifdef CLIENT_STACKTRACES_ENABLED +#include +#endif +#include +#include + +#include + +#include "base/numerics/safe_conversions.h" +#include "util/misc/capture_context.h" +#include "util/misc/time.h" +#include "util/win/nt_internals.h" +#include "util/win/ntstatus_logging.h" +#include "util/win/process_structs.h" +#include "util/win/scoped_handle.h" + +namespace crashpad { + +namespace { + +// Gets a pointer to the process information structure after a given one, or +// null when iteration is complete, assuming they've been retrieved in a block +// via NtQuerySystemInformation(). +template +process_types::SYSTEM_PROCESS_INFORMATION* NextProcess( + process_types::SYSTEM_PROCESS_INFORMATION* process) { + ULONG offset = process->NextEntryOffset; + if (offset == 0) + return nullptr; + return reinterpret_cast*>( + reinterpret_cast(process) + offset); +} + +//! \brief Retrieves the SYSTEM_PROCESS_INFORMATION for a given process. +//! +//! The returned pointer points into the memory block stored by \a buffer. +//! Ownership of \a buffer is transferred to the caller. +//! +//! \return Pointer to the process' data, or nullptr if it was not found or on +//! error. On error, a message will be logged. +template +process_types::SYSTEM_PROCESS_INFORMATION* GetProcessInformation( + HANDLE process_handle, + std::unique_ptr* buffer) { + ULONG buffer_size = 16384; + ULONG actual_size; + buffer->reset(new uint8_t[buffer_size]); + NTSTATUS status; + // This must be in retry loop, as we're racing with process creation on the + // system to find a buffer large enough to hold all process information. + for (int tries = 0; tries < 20; ++tries) { + status = crashpad::NtQuerySystemInformation( + SystemProcessInformation, + reinterpret_cast(buffer->get()), + buffer_size, + &actual_size); + if (status == STATUS_BUFFER_TOO_SMALL || + status == STATUS_INFO_LENGTH_MISMATCH) { + DCHECK_GT(actual_size, buffer_size); + + // Add a little extra to try to avoid an additional loop iteration. We're + // racing with system-wide process creation between here and the next call + // to NtQuerySystemInformation(). + buffer_size = actual_size + 4096; + + // Free the old buffer before attempting to allocate a new one. + buffer->reset(); + + buffer->reset(new uint8_t[buffer_size]); + } else { + break; + } + } + + if (!NT_SUCCESS(status)) { + NTSTATUS_LOG(ERROR, status) << "NtQuerySystemInformation"; + return nullptr; + } + + DCHECK_LE(actual_size, buffer_size); + + process_types::SYSTEM_PROCESS_INFORMATION* process = + reinterpret_cast*>( + buffer->get()); + DWORD process_id = GetProcessId(process_handle); + for (;;) { + if (process->UniqueProcessId == process_id) + return process; + process = NextProcess(process); + if (!process) + break; + } + + LOG(ERROR) << "process " << process_id << " not found"; + return nullptr; +} + +template +HANDLE OpenThread( + const process_types::SYSTEM_THREAD_INFORMATION& thread_info) { + HANDLE handle; + ACCESS_MASK query_access = + THREAD_GET_CONTEXT | THREAD_SUSPEND_RESUME | THREAD_QUERY_INFORMATION; + OBJECT_ATTRIBUTES object_attributes; + InitializeObjectAttributes(&object_attributes, nullptr, 0, nullptr, nullptr); + NTSTATUS status = crashpad::NtOpenThread( + &handle, query_access, &object_attributes, &thread_info.ClientId); + if (!NT_SUCCESS(status)) { + NTSTATUS_LOG(ERROR, status) << "NtOpenThread"; + return nullptr; + } + return handle; +} + +#ifdef CLIENT_STACKTRACES_ENABLED +void DoStackWalk(ProcessReaderWin::Thread* thread, + HANDLE process, + HANDLE thread_handle, + bool is_64_reading_32) { + if (is_64_reading_32) { + // TODO: we dont support it right away, maybe in the future + return; + } + + STACKFRAME64 stack_frame; + memset(&stack_frame, 0, sizeof(stack_frame)); + + stack_frame.AddrPC.Mode = AddrModeFlat; + stack_frame.AddrFrame.Mode = AddrModeFlat; + stack_frame.AddrStack.Mode = AddrModeFlat; + + int machine_type = IMAGE_FILE_MACHINE_I386; + LPVOID ctx = NULL; +#if defined(ARCH_CPU_X86) + const CONTEXT* ctx_ = &thread->context.native; + stack_frame.AddrPC.Offset = ctx_->Eip; + stack_frame.AddrFrame.Offset = ctx_->Ebp; + stack_frame.AddrStack.Offset = ctx_->Esp; + ctx = (LPVOID)ctx_; +#elif defined(ARCH_CPU_X86_64) + // if (!is_64_reading_32) { + machine_type = IMAGE_FILE_MACHINE_AMD64; + + const CONTEXT* ctx_ = &thread->context.native; + stack_frame.AddrPC.Offset = ctx_->Rip; + stack_frame.AddrFrame.Offset = ctx_->Rbp; + stack_frame.AddrStack.Offset = ctx_->Rsp; + ctx = (LPVOID)ctx_; + // } else { + // const WOW64_CONTEXT* ctx_ = &thread->context.wow64; + // stack_frame.AddrPC.Offset = ctx_->Eip; + // stack_frame.AddrFrame.Offset = ctx_->Ebp; + // stack_frame.AddrStack.Offset = ctx_->Esp; + // ctx = (LPVOID)ctx_; + // } + +// TODO: we dont support this right away, maybe in the future +//#elif defined(ARCH_CPU_ARM64) +// machine_type = IMAGE_FILE_MACHINE_ARM64; +#else +#error Unsupported Windows Arch +#endif // ARCH_CPU_X86 + + char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME]; + PSYMBOL_INFO pSymbol = (PSYMBOL_INFO)buffer; + + pSymbol->SizeOfStruct = sizeof(SYMBOL_INFO); + pSymbol->MaxNameLen = MAX_SYM_NAME; + + while (StackWalk64(machine_type, + process, + thread_handle, + &stack_frame, + ctx, + NULL, + SymFunctionTableAccess64, + SymGetModuleBase64, + NULL)) { + uint64_t addr = stack_frame.AddrPC.Offset; + std::string sym(""); + if (SymFromAddr(process, addr, 0, pSymbol)) { + sym = std::string(pSymbol->Name); + } + FrameSnapshot frame(addr, sym); + thread->frames.push_back(frame); + } +} +#endif + +// It's necessary to suspend the thread to grab CONTEXT. SuspendThread has a +// side-effect of returning the SuspendCount of the thread on success, so we +// fill out these two pieces of semi-unrelated data in the same function. +template +bool FillThreadContextAndSuspendCount(HANDLE process, + HANDLE thread_handle, + ProcessReaderWin::Thread* thread, + ProcessSuspensionState suspension_state, + bool is_64_reading_32) { +#ifndef CLIENT_STACKTRACES_ENABLED + (void)process; +#endif + + // Don't suspend the thread if it's this thread. This is really only for test + // binaries, as we won't be walking ourselves, in general. + bool is_current_thread = + thread->id == + reinterpret_cast*>(NtCurrentTeb()) + ->ClientId.UniqueThread; + + if (is_current_thread) { + DCHECK(suspension_state == ProcessSuspensionState::kRunning); + thread->suspend_count = 0; + DCHECK(!is_64_reading_32); + CaptureContext(&thread->context.native); + +#ifdef CLIENT_STACKTRACES_ENABLED + DoStackWalk(thread, process, thread_handle, is_64_reading_32); +#endif + } else { + DWORD previous_suspend_count = SuspendThread(thread_handle); + if (previous_suspend_count == static_cast(-1)) { + PLOG(ERROR) << "SuspendThread"; + return false; + } + if (previous_suspend_count <= 0 && + suspension_state == ProcessSuspensionState::kSuspended) { + LOG(WARNING) << "Thread " << thread->id + << " should be suspended, but previous_suspend_count is " + << previous_suspend_count; + thread->suspend_count = 0; + } else { + thread->suspend_count = + previous_suspend_count - + (suspension_state == ProcessSuspensionState::kSuspended ? 1 : 0); + } + + memset(&thread->context, 0, sizeof(thread->context)); +#if defined(ARCH_CPU_32_BITS) + const bool is_native = true; +#elif defined(ARCH_CPU_64_BITS) + const bool is_native = !is_64_reading_32; + if (is_64_reading_32) { + thread->context.wow64.ContextFlags = CONTEXT_ALL; + if (!Wow64GetThreadContext(thread_handle, &thread->context.wow64)) { + PLOG(ERROR) << "Wow64GetThreadContext"; + return false; + } + } +#endif + if (is_native) { + thread->context.native.ContextFlags = CONTEXT_ALL; + if (!GetThreadContext(thread_handle, &thread->context.native)) { + PLOG(ERROR) << "GetThreadContext"; + return false; + } + } + +#ifdef CLIENT_STACKTRACES_ENABLED + DoStackWalk(thread, process, thread_handle, is_64_reading_32); +#endif + + if (!ResumeThread(thread_handle)) { + PLOG(ERROR) << "ResumeThread"; + return false; + } + } + + return true; +} + +} // namespace + +ProcessReaderWin::Thread::Thread() + : context(), + id(0), + teb_address(0), + teb_size(0), + stack_region_address(0), + stack_region_size(0), + suspend_count(0), + priority_class(0), + priority(0) {} + +ProcessReaderWin::ProcessReaderWin() + : process_(INVALID_HANDLE_VALUE), + process_info_(), + process_memory_(), + threads_(), + modules_(), + suspension_state_(), + initialized_threads_(false), + initialized_() {} + +ProcessReaderWin::~ProcessReaderWin() {} + +bool ProcessReaderWin::Initialize(HANDLE process, + ProcessSuspensionState suspension_state) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + process_ = process; + suspension_state_ = suspension_state; + if (!process_info_.Initialize(process)) + return false; + if (!process_memory_.Initialize(process)) + return false; + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +bool ProcessReaderWin::StartTime(timeval* start_time) const { + FILETIME creation, exit, kernel, user; + if (!GetProcessTimes(process_, &creation, &exit, &kernel, &user)) { + PLOG(ERROR) << "GetProcessTimes"; + return false; + } + *start_time = FiletimeToTimevalEpoch(creation); + return true; +} + +bool ProcessReaderWin::CPUTimes(timeval* user_time, + timeval* system_time) const { + FILETIME creation, exit, kernel, user; + if (!GetProcessTimes(process_, &creation, &exit, &kernel, &user)) { + PLOG(ERROR) << "GetProcessTimes"; + return false; + } + *user_time = FiletimeToTimevalInterval(user); + *system_time = FiletimeToTimevalInterval(kernel); + return true; +} + +const std::vector& ProcessReaderWin::Threads() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (initialized_threads_) + return threads_; + + initialized_threads_ = true; + +#if defined(ARCH_CPU_64_BITS) + ReadThreadData(process_info_.IsWow64()); +#else + ReadThreadData(false); +#endif + + return threads_; +} + +const std::vector& ProcessReaderWin::Modules() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + if (!process_info_.Modules(&modules_)) { + LOG(ERROR) << "couldn't retrieve modules"; + } + + return modules_; +} + +const ProcessInfo& ProcessReaderWin::GetProcessInfo() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return process_info_; +} + +void ProcessReaderWin::DecrementThreadSuspendCounts(uint64_t except_thread_id) { + Threads(); + for (auto& thread : threads_) { + if (thread.id != except_thread_id) { + DCHECK_GT(thread.suspend_count, 0u); + --thread.suspend_count; + } + } +} + +template +void ProcessReaderWin::ReadThreadData(bool is_64_reading_32) { + DCHECK(threads_.empty()); + + std::unique_ptr buffer; + process_types::SYSTEM_PROCESS_INFORMATION* process_information = + GetProcessInformation(process_, &buffer); + if (!process_information) + return; + +#ifdef CLIENT_STACKTRACES_ENABLED + DWORD options = SymGetOptions(); + SymSetOptions(options | SYMOPT_UNDNAME); + SymInitialize(process_, NULL, TRUE); +#endif + + for (unsigned long i = 0; i < process_information->NumberOfThreads; ++i) { + const process_types::SYSTEM_THREAD_INFORMATION& thread_info = + process_information->Threads[i]; + ProcessReaderWin::Thread thread; + thread.id = thread_info.ClientId.UniqueThread; + + ScopedKernelHANDLE thread_handle(OpenThread(thread_info)); + if (!thread_handle.is_valid()) + continue; + + if (!FillThreadContextAndSuspendCount(process_, + thread_handle.get(), + &thread, + suspension_state_, + is_64_reading_32)) { + continue; + } + + // TODO(scottmg): I believe we could reverse engineer the PriorityClass from + // the Priority, BasePriority, and + // https://msdn.microsoft.com/library/ms685100.aspx. MinidumpThreadWriter + // doesn't handle it yet in any case, so investigate both of those at the + // same time if it's useful. + thread.priority_class = NORMAL_PRIORITY_CLASS; + + thread.priority = thread_info.Priority; + + process_types::THREAD_BASIC_INFORMATION thread_basic_info; + NTSTATUS status = crashpad::NtQueryInformationThread( + thread_handle.get(), + static_cast(ThreadBasicInformation), + &thread_basic_info, + sizeof(thread_basic_info), + nullptr); + if (!NT_SUCCESS(status)) { + NTSTATUS_LOG(ERROR, status) << "NtQueryInformationThread"; + continue; + } + + // Read the TIB (Thread Information Block) which is the first element of the + // TEB, for its stack fields. + process_types::NT_TIB tib; + thread.teb_address = thread_basic_info.TebBaseAddress; + thread.teb_size = sizeof(process_types::TEB); + if (process_memory_.Read(thread.teb_address, sizeof(tib), &tib)) { + WinVMAddress base = 0; + WinVMAddress limit = 0; + // If we're reading a WOW64 process, then the TIB we just retrieved is the + // x64 one. The first word of the x64 TIB points at the x86 TIB. See + // https://msdn.microsoft.com/library/dn424783.aspx. + if (is_64_reading_32) { + process_types::NT_TIB tib32; + thread.teb_address = tib.Wow64Teb; + thread.teb_size = + sizeof(process_types::TEB); + if (process_memory_.Read(thread.teb_address, sizeof(tib32), &tib32)) { + base = tib32.StackBase; + limit = tib32.StackLimit; + } + } else { + base = tib.StackBase; + limit = tib.StackLimit; + } + + // Note, "backwards" because of direction of stack growth. + thread.stack_region_address = limit; + if (limit > base) { + LOG(ERROR) << "invalid stack range: " << base << " - " << limit; + thread.stack_region_size = 0; + } else { + thread.stack_region_size = base - limit; + } + } + threads_.push_back(thread); + } +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/win/process_reader_win.h b/shared/sentry/external/crashpad/snapshot/win/process_reader_win.h new file mode 100644 index 000000000..314dca7e4 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/process_reader_win.h @@ -0,0 +1,150 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_WIN_PROCESS_READER_WIN_H_ +#define CRASHPAD_SNAPSHOT_WIN_PROCESS_READER_WIN_H_ + +#include +#include + +#include + +#include "build/build_config.h" +#include "snapshot/thread_snapshot.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/process/process_memory_win.h" +#include "util/win/address_types.h" +#include "util/win/process_info.h" + +namespace crashpad { + +//! \brief State of process being read by ProcessReaderWin. +enum class ProcessSuspensionState : bool { + //! \brief The process has not been suspended. + kRunning, + + //! \brief The process is suspended. + kSuspended, +}; + +//! \brief Accesses information about another process, identified by a `HANDLE`. +class ProcessReaderWin { + public: + //! \brief Contains information about a thread that belongs to a process. + struct Thread { + Thread(); + ~Thread() {} + + union { + CONTEXT native; +#if defined(ARCH_CPU_64_BITS) + WOW64_CONTEXT wow64; +#endif + } context; + uint64_t id; + WinVMAddress teb_address; + WinVMSize teb_size; + WinVMAddress stack_region_address; + WinVMSize stack_region_size; + uint32_t suspend_count; + uint32_t priority_class; + uint32_t priority; + +#ifdef CLIENT_STACKTRACES_ENABLED + std::vector frames; +#endif + }; + + ProcessReaderWin(); + + ProcessReaderWin(const ProcessReaderWin&) = delete; + ProcessReaderWin& operator=(const ProcessReaderWin&) = delete; + + ~ProcessReaderWin(); + + //! \brief Initializes this object. This method must be called before any + //! other. + //! + //! \param[in] process Process handle, must have `PROCESS_QUERY_INFORMATION`, + //! `PROCESS_VM_READ`, and `PROCESS_DUP_HANDLE` access. + //! \param[in] suspension_state Whether \a process has already been suspended + //! by the caller. Typically, this will be + //! ProcessSuspensionState::kSuspended, except for testing uses and where + //! the reader is reading itself. + //! + //! \return `true` on success, indicating that this object will respond + //! validly to further method calls. `false` on failure. On failure, no + //! further method calls should be made. + //! + //! \sa ScopedProcessSuspend + bool Initialize(HANDLE process, ProcessSuspensionState suspension_state); + + //! \return `true` if the target task is a 64-bit process. + bool Is64Bit() const { return process_info_.Is64Bit(); } + + //! \brief Return a memory reader for the target process. + const ProcessMemoryWin* Memory() const { return &process_memory_; } + + //! \brief Determines the target process' start time. + //! + //! \param[out] start_time The time that the process started. + //! + //! \return `true` on success, `false` on failure, with a warning logged. + bool StartTime(timeval* start_time) const; + + //! \brief Determines the target process' execution time. + //! + //! \param[out] user_time The amount of time the process has executed code in + //! user mode. + //! \param[out] system_time The amount of time the process has executed code + //! in kernel mode. + //! + //! \return `true` on success, `false` on failure, with a warning logged. + bool CPUTimes(timeval* user_time, timeval* system_time) const; + + //! \return The threads that are in the process. The first element (at index + //! `0`) corresponds to the main thread. + const std::vector& Threads(); + + //! \return The modules loaded in the process. The first element (at index + //! `0`) corresponds to the main executable. + const std::vector& Modules(); + + //! \return A ProcessInfo object for the process being read. + const ProcessInfo& GetProcessInfo() const; + + //! \brief Decrements the thread suspend counts for all thread ids other than + //! \a except_thread_id. + //! + //! Used to adjust the thread suspend count to correspond to the actual values + //! for the process before Crashpad got involved. + void DecrementThreadSuspendCounts(uint64_t except_thread_id); + + private: + template + void ReadThreadData(bool is_64_reading_32); + + HANDLE process_; + ProcessInfo process_info_; + ProcessMemoryWin process_memory_; + std::vector threads_; + std::vector modules_; + ProcessSuspensionState suspension_state_; + bool initialized_threads_; + InitializationStateDcheck initialized_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_WIN_PROCESS_READER_WIN_H_ diff --git a/shared/sentry/external/crashpad/snapshot/win/process_reader_win_test.cc b/shared/sentry/external/crashpad/snapshot/win/process_reader_win_test.cc new file mode 100644 index 000000000..709e56b21 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/process_reader_win_test.cc @@ -0,0 +1,210 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/win/process_reader_win.h" + +#include +#include + +#include + +#include "gtest/gtest.h" +#include "test/win/win_multiprocess.h" +#include "util/misc/from_pointer_cast.h" +#include "util/synchronization/semaphore.h" +#include "util/thread/thread.h" +#include "util/win/context_wrappers.h" +#include "util/win/scoped_process_suspend.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(ProcessReaderWin, SelfBasic) { + ProcessReaderWin process_reader; + ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(), + ProcessSuspensionState::kRunning)); + +#if !defined(ARCH_CPU_64_BITS) + EXPECT_FALSE(process_reader.Is64Bit()); +#else + EXPECT_TRUE(process_reader.Is64Bit()); +#endif + + EXPECT_EQ(process_reader.GetProcessInfo().ProcessID(), GetCurrentProcessId()); + + static constexpr char kTestMemory[] = "Some test memory"; + char buffer[std::size(kTestMemory)]; + ASSERT_TRUE(process_reader.Memory()->Read( + reinterpret_cast(kTestMemory), sizeof(kTestMemory), &buffer)); + EXPECT_STREQ(kTestMemory, buffer); +} + +constexpr char kTestMemory[] = "Read me from another process"; + +class ProcessReaderChild final : public WinMultiprocess { + public: + ProcessReaderChild() : WinMultiprocess() {} + + ProcessReaderChild(const ProcessReaderChild&) = delete; + ProcessReaderChild& operator=(const ProcessReaderChild&) = delete; + + ~ProcessReaderChild() {} + + private: + void WinMultiprocessParent() override { + ProcessReaderWin process_reader; + ASSERT_TRUE(process_reader.Initialize(ChildProcess(), + ProcessSuspensionState::kRunning)); + +#if !defined(ARCH_CPU_64_BITS) + EXPECT_FALSE(process_reader.Is64Bit()); +#else + EXPECT_TRUE(process_reader.Is64Bit()); +#endif + + WinVMAddress address; + CheckedReadFileExactly(ReadPipeHandle(), &address, sizeof(address)); + + char buffer[sizeof(kTestMemory)]; + ASSERT_TRUE( + process_reader.Memory()->Read(address, sizeof(kTestMemory), &buffer)); + EXPECT_EQ(strcmp(kTestMemory, buffer), 0); + } + + void WinMultiprocessChild() override { + WinVMAddress address = FromPointerCast(kTestMemory); + CheckedWriteFile(WritePipeHandle(), &address, sizeof(address)); + + // Wait for the parent to signal that it's OK to exit by closing its end of + // the pipe. + CheckedReadFileAtEOF(ReadPipeHandle()); + } +}; + +TEST(ProcessReaderWin, ChildBasic) { + WinMultiprocess::Run(); +} + +TEST(ProcessReaderWin, SelfOneThread) { + ProcessReaderWin process_reader; + ASSERT_TRUE(process_reader.Initialize(GetCurrentProcess(), + ProcessSuspensionState::kRunning)); + + const std::vector& threads = + process_reader.Threads(); + + // If other tests ran in this process previously, threads may have been + // created and may still be running. This check must look for at least one + // thread, not exactly one thread. + ASSERT_GE(threads.size(), 1u); + + EXPECT_EQ(threads[0].id, GetCurrentThreadId()); + EXPECT_NE(ProgramCounterFromCONTEXT(&threads[0].context.native), nullptr); + EXPECT_EQ(threads[0].suspend_count, 0u); +} + +class ProcessReaderChildThreadSuspendCount final : public WinMultiprocess { + public: + ProcessReaderChildThreadSuspendCount() : WinMultiprocess() {} + + ProcessReaderChildThreadSuspendCount( + const ProcessReaderChildThreadSuspendCount&) = delete; + ProcessReaderChildThreadSuspendCount& operator=( + const ProcessReaderChildThreadSuspendCount&) = delete; + + ~ProcessReaderChildThreadSuspendCount() {} + + private: + enum : unsigned int { kCreatedThreads = 3 }; + + class SleepingThread : public Thread { + public: + SleepingThread() : done_(nullptr) {} + + void SetHandle(Semaphore* done) { + done_= done; + } + + void ThreadMain() override { + done_->Wait(); + } + + private: + Semaphore* done_; + }; + + void WinMultiprocessParent() override { + char c; + CheckedReadFileExactly(ReadPipeHandle(), &c, sizeof(c)); + ASSERT_EQ(c, ' '); + + { + ProcessReaderWin process_reader; + ASSERT_TRUE(process_reader.Initialize(ChildProcess(), + ProcessSuspensionState::kRunning)); + + const auto& threads = process_reader.Threads(); + ASSERT_GE(threads.size(), kCreatedThreads + 1); + for (const auto& thread : threads) + EXPECT_EQ(thread.suspend_count, 0u); + } + + { + ScopedProcessSuspend suspend(ChildProcess()); + + ProcessReaderWin process_reader; + ASSERT_TRUE(process_reader.Initialize( + ChildProcess(), ProcessSuspensionState::kSuspended)); + + // Confirm that thread counts are adjusted correctly for the process being + // suspended. + const auto& threads = process_reader.Threads(); + ASSERT_GE(threads.size(), kCreatedThreads + 1); + for (const auto& thread : threads) + EXPECT_EQ(thread.suspend_count, 0u); + } + } + + void WinMultiprocessChild() override { + // Create three dummy threads so we can confirm we read successfully read + // more than just the main thread. + SleepingThread threads[kCreatedThreads]; + Semaphore done(0); + for (auto& thread : threads) + thread.SetHandle(&done); + for (auto& thread : threads) + thread.Start(); + + char c = ' '; + CheckedWriteFile(WritePipeHandle(), &c, sizeof(c)); + + // Wait for the parent to signal that it's OK to exit by closing its end of + // the pipe. + CheckedReadFileAtEOF(ReadPipeHandle()); + + for (size_t i = 0; i < std::size(threads); ++i) + done.Signal(); + for (auto& thread : threads) + thread.Join(); + } +}; + +TEST(ProcessReaderWin, ChildThreadSuspendCounts) { + WinMultiprocess::Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/win/process_snapshot_win.cc b/shared/sentry/external/crashpad/snapshot/win/process_snapshot_win.cc new file mode 100644 index 000000000..f1a20b5d7 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/process_snapshot_win.cc @@ -0,0 +1,570 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/win/process_snapshot_win.h" + +#include +#include + +#include +#include +#include + +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" +#include "util/misc/from_pointer_cast.h" +#include "util/misc/time.h" +#include "util/win/nt_internals.h" +#include "util/win/registration_protocol_win.h" + +namespace crashpad { + +ProcessSnapshotWin::ProcessSnapshotWin() + : ProcessSnapshot(), + system_(), + threads_(), + modules_(), + exception_(), + memory_map_(), + process_reader_(), + report_id_(), + client_id_(), + annotations_simple_map_(), + snapshot_time_(), + options_(), + initialized_() { +} + +ProcessSnapshotWin::~ProcessSnapshotWin() { +} + +bool ProcessSnapshotWin::Initialize( + HANDLE process, + ProcessSuspensionState suspension_state, + WinVMAddress exception_information_address, + WinVMAddress debug_critical_section_address) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + GetTimeOfDay(&snapshot_time_); + + if (!process_reader_.Initialize(process, suspension_state)) + return false; + + client_id_.InitializeToZero(); + system_.Initialize(&process_reader_); + + if (process_reader_.Is64Bit()) { + InitializePebData( + debug_critical_section_address); + } else { + InitializePebData( + debug_critical_section_address); + } + + InitializeModules(); + InitializeUnloadedModules(); + + GetCrashpadOptionsInternal(&options_); + uint32_t* budget_remaining_pointer = + options_.gather_indirectly_referenced_memory == TriState::kEnabled + ? &options_.indirectly_referenced_memory_cap + : nullptr; + + if (exception_information_address != 0) { + ExceptionInformation exception_information = {}; + if (!process_reader_.Memory()->Read(exception_information_address, + sizeof(exception_information), + &exception_information)) { + LOG(WARNING) << "ReadMemory ExceptionInformation failed"; + return false; + } + + exception_.reset(new internal::ExceptionSnapshotWin()); + if (!exception_->Initialize(&process_reader_, + exception_information.thread_id, + exception_information.exception_pointers, + budget_remaining_pointer)) { + exception_.reset(); + return false; + } + } + + InitializeThreads(budget_remaining_pointer); + + for (const MEMORY_BASIC_INFORMATION64& mbi : + process_reader_.GetProcessInfo().MemoryInfo()) { + memory_map_.push_back( + std::make_unique(mbi)); + } + + for (const auto& module : modules_) { + for (const auto& range : module->ExtraMemoryRanges()) { + AddMemorySnapshot(range.base(), range.size(), &extra_memory_); + } + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +void ProcessSnapshotWin::GetCrashpadOptions( + CrashpadInfoClientOptions* options) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *options = options_; +} + +crashpad::ProcessID ProcessSnapshotWin::ProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return process_reader_.GetProcessInfo().ProcessID(); +} + +crashpad::ProcessID ProcessSnapshotWin::ParentProcessID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return process_reader_.GetProcessInfo().ParentProcessID(); +} + +void ProcessSnapshotWin::SnapshotTime(timeval* snapshot_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *snapshot_time = snapshot_time_; +} + +void ProcessSnapshotWin::ProcessStartTime(timeval* start_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + process_reader_.StartTime(start_time); +} + +void ProcessSnapshotWin::ProcessCPUTimes(timeval* user_time, + timeval* system_time) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + process_reader_.CPUTimes(user_time, system_time); +} + +void ProcessSnapshotWin::ReportID(UUID* report_id) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *report_id = report_id_; +} + +void ProcessSnapshotWin::ClientID(UUID* client_id) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *client_id = client_id_; +} + +const std::map& +ProcessSnapshotWin::AnnotationsSimpleMap() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return annotations_simple_map_; +} + +const SystemSnapshot* ProcessSnapshotWin::System() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &system_; +} + +std::vector ProcessSnapshotWin::Threads() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector threads; + for (const auto& thread : threads_) { + threads.push_back(thread.get()); + } + return threads; +} + +std::vector ProcessSnapshotWin::Modules() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector modules; + for (const auto& module : modules_) { + modules.push_back(module.get()); + } + return modules; +} + +std::vector ProcessSnapshotWin::UnloadedModules() + const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return unloaded_modules_; +} + +const ExceptionSnapshot* ProcessSnapshotWin::Exception() const { + return exception_.get(); +} + +std::vector ProcessSnapshotWin::MemoryMap() + const { + std::vector memory_map; + for (const auto& item : memory_map_) { + memory_map.push_back(item.get()); + } + return memory_map; +} + +std::vector ProcessSnapshotWin::Handles() const { + std::vector result; + for (const auto& handle : process_reader_.GetProcessInfo().Handles()) { + HandleSnapshot snapshot; + // This is probably not strictly correct, but these are not localized so we + // expect them all to be in ASCII range anyway. This will need to be more + // carefully done if the object name is added. + snapshot.type_name = base::WideToUTF8(handle.type_name); + snapshot.handle = handle.handle; + snapshot.attributes = handle.attributes; + snapshot.granted_access = handle.granted_access; + snapshot.pointer_count = handle.pointer_count; + snapshot.handle_count = handle.handle_count; + result.push_back(snapshot); + } + return result; +} + +std::vector ProcessSnapshotWin::ExtraMemory() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector extra_memory; + for (const auto& em : extra_memory_) { + extra_memory.push_back(em.get()); + } + return extra_memory; +} + +const ProcessMemory* ProcessSnapshotWin::Memory() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return process_reader_.Memory(); +} + +void ProcessSnapshotWin::InitializeThreads(uint32_t* budget_remaining_pointer) { + const std::vector& process_reader_threads = + process_reader_.Threads(); + for (const ProcessReaderWin::Thread& process_reader_thread : + process_reader_threads) { + auto thread = std::make_unique(); + if (thread->Initialize(&process_reader_, + process_reader_thread, + budget_remaining_pointer)) { + threads_.push_back(std::move(thread)); + } + } +} + +void ProcessSnapshotWin::InitializeModules() { + const std::vector& process_reader_modules = + process_reader_.Modules(); + for (const ProcessInfo::Module& process_reader_module : + process_reader_modules) { + auto module = std::make_unique(); + if (module->Initialize(&process_reader_, process_reader_module)) { + modules_.push_back(std::move(module)); + } + } +} + +void ProcessSnapshotWin::InitializeUnloadedModules() { + // As documented by https://msdn.microsoft.com/library/cc678403.aspx, we can + // retrieve the location for our unload events, and use that address in the + // target process. Unfortunately, this of course only works for 64-reading-64 + // and 32-reading-32, so at the moment, we simply do not retrieve unloaded + // modules for 64-reading-32. See https://crashpad.chromium.org/bug/89. + +#if defined(ARCH_CPU_X86_64) || defined(ARCH_CPU_ARM64) + if (!process_reader_.Is64Bit()) { + LOG(ERROR) + << "reading unloaded modules across bitness not currently supported"; + return; + } + using Traits = process_types::internal::Traits64; +#elif defined(ARCH_CPU_X86) + using Traits = process_types::internal::Traits32; +#else +#error port +#endif + + ULONG* element_size; + ULONG* element_count; + void* event_trace_address; + RtlGetUnloadEventTraceEx(&element_size, &element_count, &event_trace_address); + + if (*element_size < sizeof(RTL_UNLOAD_EVENT_TRACE)) { + LOG(ERROR) << "unexpected unloaded module list element size"; + return; + } + + const WinVMAddress address_in_target_process = + FromPointerCast(event_trace_address); + + Traits::Pointer pointer_to_array; + if (!process_reader_.Memory()->Read(address_in_target_process, + sizeof(pointer_to_array), + &pointer_to_array)) { + LOG(ERROR) << "failed to read target address"; + return; + } + + // No unloaded modules. + if (pointer_to_array == 0) + return; + + const size_t data_size = *element_size * *element_count; + std::vector data(data_size); + if (!process_reader_.Memory()->Read(pointer_to_array, data_size, &data[0])) { + LOG(ERROR) << "failed to read unloaded module data"; + return; + } + + for (ULONG i = 0; i < *element_count; ++i) { + const uint8_t* base_address = &data[i * *element_size]; + const auto& uet = + *reinterpret_cast*>(base_address); + if (uet.ImageName[0] != 0) { + unloaded_modules_.push_back(UnloadedModuleSnapshot( + uet.BaseAddress, + uet.SizeOfImage, + uet.CheckSum, + uet.TimeDateStamp, + base::WideToUTF8(base::WStringPiece( + uet.ImageName, + wcsnlen(uet.ImageName, std::size(uet.ImageName)))))); + } + } +} + +void ProcessSnapshotWin::GetCrashpadOptionsInternal( + CrashpadInfoClientOptions* options) { + CrashpadInfoClientOptions local_options; + + for (const auto& module : modules_) { + CrashpadInfoClientOptions module_options; + module->GetCrashpadOptions(&module_options); + + if (local_options.crashpad_handler_behavior == TriState::kUnset) { + local_options.crashpad_handler_behavior = + module_options.crashpad_handler_behavior; + } + if (local_options.system_crash_reporter_forwarding == TriState::kUnset) { + local_options.system_crash_reporter_forwarding = + module_options.system_crash_reporter_forwarding; + } + if (local_options.gather_indirectly_referenced_memory == TriState::kUnset) { + local_options.gather_indirectly_referenced_memory = + module_options.gather_indirectly_referenced_memory; + local_options.indirectly_referenced_memory_cap = + module_options.indirectly_referenced_memory_cap; + } + + // If non-default values have been found for all options, the loop can end + // early. + if (local_options.crashpad_handler_behavior != TriState::kUnset && + local_options.system_crash_reporter_forwarding != TriState::kUnset && + local_options.gather_indirectly_referenced_memory != TriState::kUnset) { + break; + } + } + + *options = local_options; +} + +template +void ProcessSnapshotWin::InitializePebData( + WinVMAddress debug_critical_section_address) { + WinVMAddress peb_address; + WinVMSize peb_size; + process_reader_.GetProcessInfo().Peb(&peb_address, &peb_size); + AddMemorySnapshot(peb_address, peb_size, &extra_memory_); + + process_types::PEB peb_data; + if (!process_reader_.Memory()->Read( + peb_address, base::checked_cast(peb_size), &peb_data)) { + LOG(ERROR) << "ReadMemory PEB"; + return; + } + + process_types::PEB_LDR_DATA peb_ldr_data; + AddMemorySnapshot(peb_data.Ldr, sizeof(peb_ldr_data), &extra_memory_); + if (!process_reader_.Memory()->Read( + peb_data.Ldr, sizeof(peb_ldr_data), &peb_ldr_data)) { + LOG(ERROR) << "ReadMemory PEB_LDR_DATA"; + } else { + // Walk the LDR structure to retrieve its pointed-to data. + AddMemorySnapshotForLdrLIST_ENTRY( + peb_ldr_data.InLoadOrderModuleList, + offsetof(process_types::LDR_DATA_TABLE_ENTRY, InLoadOrderLinks), + &extra_memory_); + AddMemorySnapshotForLdrLIST_ENTRY( + peb_ldr_data.InMemoryOrderModuleList, + offsetof(process_types::LDR_DATA_TABLE_ENTRY, + InMemoryOrderLinks), + &extra_memory_); + AddMemorySnapshotForLdrLIST_ENTRY( + peb_ldr_data.InInitializationOrderModuleList, + offsetof(process_types::LDR_DATA_TABLE_ENTRY, + InInitializationOrderLinks), + &extra_memory_); + } + + process_types::RTL_USER_PROCESS_PARAMETERS process_parameters; + if (!process_reader_.Memory()->Read(peb_data.ProcessParameters, + sizeof(process_parameters), + &process_parameters)) { + LOG(ERROR) << "ReadMemory RTL_USER_PROCESS_PARAMETERS"; + return; + } + AddMemorySnapshot( + peb_data.ProcessParameters, sizeof(process_parameters), &extra_memory_); + + AddMemorySnapshotForUNICODE_STRING( + process_parameters.CurrentDirectory.DosPath, &extra_memory_); + AddMemorySnapshotForUNICODE_STRING(process_parameters.DllPath, + &extra_memory_); + AddMemorySnapshotForUNICODE_STRING(process_parameters.ImagePathName, + &extra_memory_); + AddMemorySnapshotForUNICODE_STRING(process_parameters.CommandLine, + &extra_memory_); + AddMemorySnapshotForUNICODE_STRING(process_parameters.WindowTitle, + &extra_memory_); + AddMemorySnapshotForUNICODE_STRING(process_parameters.DesktopInfo, + &extra_memory_); + AddMemorySnapshotForUNICODE_STRING(process_parameters.ShellInfo, + &extra_memory_); + AddMemorySnapshotForUNICODE_STRING(process_parameters.RuntimeData, + &extra_memory_); + AddMemorySnapshot( + process_parameters.Environment, + DetermineSizeOfEnvironmentBlock(process_parameters.Environment), + &extra_memory_); + + // Walk the loader lock which is directly referenced by the PEB. + ReadLock(peb_data.LoaderLock, &extra_memory_); + + // TODO(scottmg): Use debug_critical_section_address to walk the list of + // locks (see history of this file for walking code). In some configurations + // this can walk many thousands of locks, so we may want to get some + // annotation from the client for which locks to grab. Unfortunately, without + // walking the list, the !locks command in windbg won't work because it + // requires the lock pointed to by ntdll!RtlCriticalSectionList, which we + // won't have captured. +} + +void ProcessSnapshotWin::AddMemorySnapshot( + WinVMAddress address, + WinVMSize size, + std::vector>* into) { + if (size == 0) + return; + + if (!process_reader_.GetProcessInfo().LoggingRangeIsFullyReadable( + CheckedRange(address, size))) { + return; + } + + // If we have already added this exact range, don't add it again. This is + // useful for the LDR module lists which are a set of doubly-linked lists, all + // pointing to the same module name strings. + // TODO(scottmg): A more general version of this, handling overlapping, + // contained, etc. https://crashpad.chromium.org/bug/61. + for (const auto& memory_snapshot : *into) { + if (memory_snapshot->Address() == address && + memory_snapshot->Size() == size) { + return; + } + } + + into->push_back(std::make_unique()); + into->back()->Initialize(process_reader_.Memory(), address, size); +} + +template +void ProcessSnapshotWin::AddMemorySnapshotForUNICODE_STRING( + const process_types::UNICODE_STRING& us, + std::vector>* into) { + AddMemorySnapshot(us.Buffer, us.Length, into); +} + +template +void ProcessSnapshotWin::AddMemorySnapshotForLdrLIST_ENTRY( + const process_types::LIST_ENTRY& le, + size_t offset_of_member, + std::vector>* into) { + // Walk the doubly-linked list of entries, adding the list memory itself, as + // well as pointed-to strings. + typename Traits::Pointer last = le.Blink; + process_types::LDR_DATA_TABLE_ENTRY entry; + typename Traits::Pointer cur = le.Flink; + for (;;) { + // |cur| is the pointer to LIST_ENTRY embedded in the LDR_DATA_TABLE_ENTRY. + // So we need to offset back to the beginning of the structure. + if (!process_reader_.Memory()->Read( + cur - offset_of_member, sizeof(entry), &entry)) { + return; + } + AddMemorySnapshot(cur - offset_of_member, sizeof(entry), into); + AddMemorySnapshotForUNICODE_STRING(entry.FullDllName, into); + AddMemorySnapshotForUNICODE_STRING(entry.BaseDllName, into); + + process_types::LIST_ENTRY* links = + reinterpret_cast*>( + reinterpret_cast(&entry) + offset_of_member); + cur = links->Flink; + if (cur == last) + break; + } +} + +WinVMSize ProcessSnapshotWin::DetermineSizeOfEnvironmentBlock( + WinVMAddress start_of_environment_block) { + // https://blogs.msdn.microsoft.com/oldnewthing/20100203-00/?p=15083: On newer + // OSs there's no stated limit, but in practice grabbing 32k characters should + // be more than enough. + std::wstring env_block; + env_block.resize(32768); + size_t bytes_read = process_reader_.Memory()->ReadAvailableMemory( + start_of_environment_block, + env_block.size() * sizeof(env_block[0]), + &env_block[0]); + env_block.resize( + static_cast(bytes_read / sizeof(env_block[0]))); + static constexpr wchar_t terminator[] = {0, 0}; + size_t at = env_block.find(std::wstring(terminator, std::size(terminator))); + if (at != std::wstring::npos) + env_block.resize(at + std::size(terminator)); + + return env_block.size() * sizeof(env_block[0]); +} + +template +void ProcessSnapshotWin::ReadLock( + WinVMAddress start, + std::vector>* into) { + // We're walking the RTL_CRITICAL_SECTION_DEBUG ProcessLocksList, but starting + // from an actual RTL_CRITICAL_SECTION, so start by getting to the first + // RTL_CRITICAL_SECTION_DEBUG. + + process_types::RTL_CRITICAL_SECTION critical_section; + if (!process_reader_.Memory()->Read( + start, sizeof(critical_section), &critical_section)) { + LOG(ERROR) << "failed to read RTL_CRITICAL_SECTION"; + return; + } + + AddMemorySnapshot( + start, sizeof(process_types::RTL_CRITICAL_SECTION), into); + + constexpr decltype(critical_section.DebugInfo) kInvalid = + static_cast(-1); + if (critical_section.DebugInfo == kInvalid) + return; + + AddMemorySnapshot(critical_section.DebugInfo, + sizeof(process_types::RTL_CRITICAL_SECTION_DEBUG), + into); +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/win/process_snapshot_win.h b/shared/sentry/external/crashpad/snapshot/win/process_snapshot_win.h new file mode 100644 index 000000000..002107078 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/process_snapshot_win.h @@ -0,0 +1,201 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_WIN_PROCESS_SNAPSHOT_WIN_H_ +#define CRASHPAD_SNAPSHOT_WIN_PROCESS_SNAPSHOT_WIN_H_ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "client/crashpad_info.h" +#include "snapshot/crashpad_info_client_options.h" +#include "snapshot/exception_snapshot.h" +#include "snapshot/memory_map_region_snapshot.h" +#include "snapshot/memory_snapshot.h" +#include "snapshot/memory_snapshot_generic.h" +#include "snapshot/module_snapshot.h" +#include "snapshot/process_snapshot.h" +#include "snapshot/system_snapshot.h" +#include "snapshot/thread_snapshot.h" +#include "snapshot/unloaded_module_snapshot.h" +#include "snapshot/win/exception_snapshot_win.h" +#include "snapshot/win/memory_map_region_snapshot_win.h" +#include "snapshot/win/module_snapshot_win.h" +#include "snapshot/win/system_snapshot_win.h" +#include "snapshot/win/thread_snapshot_win.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/misc/uuid.h" +#include "util/process/process_id.h" +#include "util/win/address_types.h" +#include "util/win/process_structs.h" + +namespace crashpad { + +//! \brief A ProcessSnapshot of a running (or crashed) process running on a +//! Windows system. +class ProcessSnapshotWin final : public ProcessSnapshot { + public: + ProcessSnapshotWin(); + + ProcessSnapshotWin(const ProcessSnapshotWin&) = delete; + ProcessSnapshotWin& operator=(const ProcessSnapshotWin&) = delete; + + ~ProcessSnapshotWin() override; + + //! \brief Initializes the object. + //! + //! \param[in] process The handle to create a snapshot from. + //! \param[in] suspension_state Whether \a process has been suspended by the + //! caller. + //! \param[in] exception_information_address The address in the client + //! process's address space of an ExceptionInformation structure. May be + //! `0`, in which case no exception data will be recorded. + //! \param[in] debug_critical_section_address The address in the target + //! process's address space of a `CRITICAL_SECTION` allocated with valid + //! `.DebugInfo`. Used as a starting point to walk the process's locks. + //! May be `0`. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + //! + //! \sa ScopedProcessSuspend + bool Initialize(HANDLE process, + ProcessSuspensionState suspension_state, + WinVMAddress exception_information_address, + WinVMAddress debug_critical_section_address); + + //! \brief Sets the value to be returned by ReportID(). + //! + //! The crash report ID is under the control of the snapshot producer, which + //! may call this method to set the report ID. If this is not done, ReportID() + //! will return an identifier consisting entirely of zeroes. + void SetReportID(const UUID& report_id) { report_id_ = report_id; } + + //! \brief Sets the value to be returned by ClientID(). + //! + //! The client ID is under the control of the snapshot producer, which may + //! call this method to set the client ID. If this is not done, ClientID() + //! will return an identifier consisting entirely of zeroes. + void SetClientID(const UUID& client_id) { client_id_ = client_id; } + + //! \brief Sets the value to be returned by AnnotationsSimpleMap(). + //! + //! All process annotations are under the control of the snapshot producer, + //! which may call this method to establish these annotations. Contrast this + //! with module annotations, which are under the control of the process being + //! snapshotted. + void SetAnnotationsSimpleMap( + const std::map& annotations_simple_map) { + annotations_simple_map_ = annotations_simple_map; + } + + //! \brief Returns options from CrashpadInfo structures found in modules in + //! the process. + //! + //! \param[out] options Options set in CrashpadInfo structures in modules in + //! the process. + void GetCrashpadOptions(CrashpadInfoClientOptions* options); + + // ProcessSnapshot: + + crashpad::ProcessID ProcessID() const override; + crashpad::ProcessID ParentProcessID() const override; + void SnapshotTime(timeval* snapshot_time) const override; + void ProcessStartTime(timeval* start_time) const override; + void ProcessCPUTimes(timeval* user_time, timeval* system_time) const override; + void ReportID(UUID* report_id) const override; + void ClientID(UUID* client_id) const override; + const std::map& AnnotationsSimpleMap() + const override; + const SystemSnapshot* System() const override; + std::vector Threads() const override; + std::vector Modules() const override; + std::vector UnloadedModules() const override; + const ExceptionSnapshot* Exception() const override; + std::vector MemoryMap() const override; + std::vector Handles() const override; + std::vector ExtraMemory() const override; + const ProcessMemory* Memory() const override; + + private: + // Initializes threads_ on behalf of Initialize(). + void InitializeThreads(uint32_t* indirectly_referenced_memory_cap); + + // Initializes modules_ on behalf of Initialize(). + void InitializeModules(); + + // Initializes unloaded_modules_ on behalf of Initialize(). + void InitializeUnloadedModules(); + + // Initializes options_ on behalf of Initialize(). + void GetCrashpadOptionsInternal(CrashpadInfoClientOptions* options); + + // Initializes various memory blocks reachable from the PEB on behalf of + // Initialize(). + template + void InitializePebData(WinVMAddress debug_critical_section_address); + + void AddMemorySnapshot( + WinVMAddress address, + WinVMSize size, + std::vector>* into); + + template + void AddMemorySnapshotForUNICODE_STRING( + const process_types::UNICODE_STRING& us, + std::vector>* into); + + template + void AddMemorySnapshotForLdrLIST_ENTRY( + const process_types::LIST_ENTRY& le, + size_t offset_of_member, + std::vector>* into); + + WinVMSize DetermineSizeOfEnvironmentBlock( + WinVMAddress start_of_environment_block); + + // Starting from the address of a CRITICAL_SECTION, add a lock and, if valid, + // its .DebugInfo field to the snapshot. + template + void ReadLock( + WinVMAddress start, + std::vector>* into); + + internal::SystemSnapshotWin system_; + std::vector> extra_memory_; + std::vector> threads_; + std::vector> modules_; + std::vector unloaded_modules_; + std::unique_ptr exception_; + std::vector> + memory_map_; + ProcessReaderWin process_reader_; + UUID report_id_; + UUID client_id_; + std::map annotations_simple_map_; + timeval snapshot_time_; + CrashpadInfoClientOptions options_; + InitializationStateDcheck initialized_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_WIN_PROCESS_SNAPSHOT_WIN_H_ diff --git a/shared/sentry/external/crashpad/snapshot/win/process_snapshot_win_test.cc b/shared/sentry/external/crashpad/snapshot/win/process_snapshot_win_test.cc new file mode 100644 index 000000000..e1bfebbee --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/process_snapshot_win_test.cc @@ -0,0 +1,131 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/win/process_snapshot_win.h" + +#include "base/files/file_path.h" +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "snapshot/win/pe_image_reader.h" +#include "snapshot/win/process_reader_win.h" +#include "test/errors.h" +#include "test/test_paths.h" +#include "test/win/child_launcher.h" +#include "util/file/file_io.h" +#include "util/win/scoped_handle.h" +#include "util/win/scoped_process_suspend.h" +#include "util/win/scoped_set_event.h" + +namespace crashpad { +namespace test { +namespace { + +void TestImageReaderChild(const TestPaths::Architecture architecture) { + UUID done_uuid; + done_uuid.InitializeWithNew(); + ScopedKernelHANDLE done( + CreateEvent(nullptr, true, false, done_uuid.ToWString().c_str())); + ASSERT_TRUE(done.is_valid()) << ErrorMessage("CreateEvent"); + + base::FilePath child_test_executable = + TestPaths::BuildArtifact(L"snapshot", + L"image_reader", + TestPaths::FileType::kExecutable, + architecture); + ChildLauncher child(child_test_executable, done_uuid.ToWString()); + ASSERT_NO_FATAL_FAILURE(child.Start()); + + ScopedSetEvent set_done(done.get()); + + char c; + ASSERT_TRUE( + LoggingReadFileExactly(child.stdout_read_handle(), &c, sizeof(c))); + ASSERT_EQ(c, ' '); + + { + ScopedProcessSuspend suspend(child.process_handle()); + + ProcessSnapshotWin process_snapshot; + ASSERT_TRUE(process_snapshot.Initialize( + child.process_handle(), ProcessSuspensionState::kSuspended, 0, 0)); + + ASSERT_GE(process_snapshot.Modules().size(), 2u); + + UUID uuid; + DWORD age; + std::string pdbname; + const std::string suffix(".pdb"); + + // Check the main .exe to see that we can retrieve its sections. + auto module = reinterpret_cast( + process_snapshot.Modules()[0]); + ASSERT_TRUE(module->pe_image_reader().DebugDirectoryInformation( + &uuid, &age, &pdbname)); + EXPECT_NE(pdbname.find("crashpad_snapshot_test_image_reader"), + std::string::npos); + EXPECT_EQ( + pdbname.compare(pdbname.size() - suffix.size(), suffix.size(), suffix), + 0); + + // Check the dll it loads too. + module = reinterpret_cast( + process_snapshot.Modules().back()); + ASSERT_TRUE(module->pe_image_reader().DebugDirectoryInformation( + &uuid, &age, &pdbname)); + EXPECT_NE(pdbname.find("crashpad_snapshot_test_image_reader_module"), + std::string::npos); + EXPECT_EQ( + pdbname.compare(pdbname.size() - suffix.size(), suffix.size(), suffix), + 0); + + // Sum the size of the extra memory in all the threads and confirm it's near + // the limit that the child process set in its CrashpadInfo. + EXPECT_GE(process_snapshot.Threads().size(), 100u); + + size_t extra_memory_total = 0; + for (const auto* thread : process_snapshot.Threads()) { + for (const auto* extra_memory : thread->ExtraMemory()) { + extra_memory_total += extra_memory->Size(); + } + } + + // Confirm that less than 1M of extra data was gathered. The cap is set to + // only 100K, but there are other "extra memory" regions that aren't + // included in the cap. (Completely uncapped it would be > 10M.) + EXPECT_LT(extra_memory_total, 1000000u); + } + + // Tell the child it can terminate. + EXPECT_TRUE(set_done.Set()); + + EXPECT_EQ(child.WaitForExit(), 0u); +} + +TEST(ProcessSnapshotTest, CrashpadInfoChild) { + TestImageReaderChild(TestPaths::Architecture::kDefault); +} + +#if defined(ARCH_CPU_64_BITS) +TEST(ProcessSnapshotTest, CrashpadInfoChildWOW64) { + if (!TestPaths::Has32BitBuildArtifacts()) { + GTEST_SKIP(); + } + + TestImageReaderChild(TestPaths::Architecture::k32Bit); +} +#endif + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/win/process_subrange_reader.cc b/shared/sentry/external/crashpad/snapshot/win/process_subrange_reader.cc new file mode 100644 index 000000000..124c5d023 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/process_subrange_reader.cc @@ -0,0 +1,106 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/win/process_subrange_reader.h" + +#include "base/logging.h" +#include "base/numerics/safe_conversions.h" +#include "snapshot/win/process_reader_win.h" + +namespace crashpad { + +ProcessSubrangeReader::ProcessSubrangeReader() + : name_(), + range_(), + process_reader_(nullptr) { +} + +ProcessSubrangeReader::~ProcessSubrangeReader() { +} + +bool ProcessSubrangeReader::Initialize(ProcessReaderWin* process_reader, + WinVMAddress base, + WinVMSize size, + const std::string& name) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + if (!InitializeInternal(process_reader, base, size, name)) { + return false; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +bool ProcessSubrangeReader::InitializeSubrange( + const ProcessSubrangeReader& that, + WinVMAddress base, + WinVMSize size, + const std::string& sub_name) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + INITIALIZATION_STATE_DCHECK_VALID(that.initialized_); + + if (!InitializeInternal( + that.process_reader_, base, size, that.name_ + " " + sub_name)) { + return false; + } + + if (!that.range_.ContainsRange(range_)) { + LOG(WARNING) << "range " << range_.AsString() << " outside of range " + << that.range_.AsString() << " for " << name_; + return false; + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +bool ProcessSubrangeReader::ReadMemory(WinVMAddress address, + WinVMSize size, + void* into) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + CheckedWinAddressRange read_range(process_reader_->Is64Bit(), address, size); + if (!read_range.IsValid()) { + LOG(WARNING) << "invalid read range " << read_range.AsString(); + return false; + } + + if (!range_.ContainsRange(read_range)) { + LOG(WARNING) << "attempt to read outside of " << name_ << " range " + << range_.AsString() << " at range " << read_range.AsString(); + return false; + } + + return process_reader_->Memory()->Read( + address, base::checked_cast(size), into); +} + +bool ProcessSubrangeReader::InitializeInternal(ProcessReaderWin* process_reader, + WinVMAddress base, + WinVMSize size, + const std::string& name) { + range_.SetRange(process_reader->Is64Bit(), base, size); + if (!range_.IsValid()) { + LOG(WARNING) << "invalid range " << range_.AsString() << " for " << name; + return false; + } + + name_ = name; + process_reader_ = process_reader; + + return true; +} + +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/win/process_subrange_reader.h b/shared/sentry/external/crashpad/snapshot/win/process_subrange_reader.h new file mode 100644 index 000000000..bcdff5e21 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/process_subrange_reader.h @@ -0,0 +1,115 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_WIN_PROCESS_SUBRANGE_READER_WIN_H_ +#define CRASHPAD_SNAPSHOT_WIN_PROCESS_SUBRANGE_READER_WIN_H_ + +#include + +#include "util/misc/initialization_state_dcheck.h" +#include "util/win/address_types.h" +#include "util/win/checked_win_address_range.h" + +namespace crashpad { + +class ProcessReaderWin; + +//! \brief A wrapper for ProcessReaderWin that only allows a specific subrange +//! to be read from. +//! +//! This class is useful to restrict reads to a specific address range, such as +//! the address range occupied by a loaded module, or a specific section within +//! a module. +class ProcessSubrangeReader { + public: + ProcessSubrangeReader(); + + ProcessSubrangeReader(const ProcessSubrangeReader&) = delete; + ProcessSubrangeReader& operator=(const ProcessSubrangeReader&) = delete; + + ~ProcessSubrangeReader(); + + //! \brief Initializes the object. + //! + //! \param[in] process_reader A reader for a remote process. + //! \param[in] base The base address for the range that reads should be + //! restricted to. + //! \param[in] size The size of the range that reads should be restricted to. + //! \param[in] name The range’s name, a string to be used in logged messages. + //! This string is for diagnostic purposes. + //! + //! \return `true` on success, `false` on failure with a message logged. The + //! other methods in this class must not be called unless this method or + //! InitializeSubrange() has returned true. + bool Initialize(ProcessReaderWin* process_reader, + WinVMAddress base, + WinVMSize size, + const std::string& name); + + //! \brief Initializes the object to a subrange of an existing + //! ProcessSubrangeReader. + //! + //! The subrange identified by \a base and \a size must be contained within + //! the subrange in \a that. + //! + //! \param[in] that The existing ProcessSubrangeReader to base the new object + //! on. + //! \param[in] base The base address for the range that reads should be + //! restricted to. + //! \param[in] size The size of the range that reads should be restricted to. + //! \param[in] sub_name A description of the subrange, which will be appended + //! to the \a name in \a that and used in logged messages. This string is + //! for diagnostic purposes. + //! + //! \return `true` on success, `false` on failure with a message logged. The + //! other methods in this class must not be called unless this method or + //! Initialize() has returned true. + bool InitializeSubrange(const ProcessSubrangeReader& that, + WinVMAddress base, + WinVMSize size, + const std::string& sub_name); + + bool Is64Bit() const { return range_.Is64Bit(); } + WinVMAddress Base() const { return range_.Base(); } + WinVMAddress Size() const { return range_.Size(); } + const std::string& name() const { return name_; } + + //! \brief Reads memory from the remote process. + //! + //! The range specified by \a address and \a size must be contained within + //! the range that this object is permitted to read. + //! + //! \param[in] address The address to read from. + //! \param[in] size The size of data to read, in bytes. + //! \param[out] into The buffer to read data into. + //! + //! \return `true` on success, `false` on failure with a message logged. + bool ReadMemory(WinVMAddress address, WinVMSize size, void* into) const; + + private: + // Common helper for Initialize() and InitializeSubrange(). + bool InitializeInternal(ProcessReaderWin* process_reader, + WinVMAddress base, + WinVMSize size, + const std::string& name); + + std::string name_; + CheckedWinAddressRange range_; + ProcessReaderWin* process_reader_; // weak + InitializationStateDcheck initialized_; +}; + +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_WIN_PROCESS_SUBRANGE_READER_WIN_H_ diff --git a/shared/sentry/external/crashpad/snapshot/win/system_snapshot_win.cc b/shared/sentry/external/crashpad/snapshot/win/system_snapshot_win.cc new file mode 100644 index 000000000..a19253f23 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/system_snapshot_win.cc @@ -0,0 +1,505 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/win/system_snapshot_win.h" + +#include +#include +#include +#include + +// Must be after windows.h. +#include + +#include +#include +#include + +#include "base/check_op.h" +#include "base/logging.h" +#include "base/notreached.h" +#include "base/numerics/safe_conversions.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" +#include "util/stdlib/string_number_conversion.h" +#include "util/win/module_version.h" +#include "util/win/scoped_registry_key.h" + +namespace crashpad { + +namespace { + +//! \brief Gets a string representation for a VS_FIXEDFILEINFO.dwFileFlags +//! value. +std::string GetStringForFileFlags(uint32_t file_flags) { + std::string result; + DCHECK_EQ(file_flags & VS_FF_INFOINFERRED, 0u); + if (file_flags & VS_FF_DEBUG) + result += "Debug,"; + if (file_flags & VS_FF_PATCHED) + result += "Patched,"; + if (file_flags & VS_FF_PRERELEASE) + result += "Prerelease,"; + if (file_flags & VS_FF_PRIVATEBUILD) + result += "Private,"; + if (file_flags & VS_FF_SPECIALBUILD) + result += "Special,"; + if (!result.empty()) + return result.substr(0, result.size() - 1); // Remove trailing comma. + return result; +} + +//! \brief Gets a string representation for a VS_FIXEDFILEINFO.dwFileOS value. +std::string GetStringForFileOS(uint32_t file_os) { + // There are a variety of ancient things this could theoretically be. In + // practice, we're always going to get VOS_NT_WINDOWS32 here. + if ((file_os & VOS_NT_WINDOWS32) == VOS_NT_WINDOWS32) + return "Windows NT"; + else + return "Unknown"; +} + +//! \brief Reads a DWORD from the registry and returns it as an int. +bool ReadRegistryDWORD(HKEY key, const wchar_t* name, int* out_value) { + DWORD type; + DWORD local_value; + DWORD size = sizeof(local_value); + if (RegQueryValueExW(key, + name, + nullptr, + &type, + reinterpret_cast(&local_value), + &size) == ERROR_SUCCESS && + type == REG_DWORD) { + *out_value = static_cast(local_value); + return true; + } + return false; +} + +//! \brief Reads a string from the registry and returns it as an int. +bool ReadRegistryDWORDFromSZ(HKEY key, const char* name, int* out_value) { + char string_value[11]; + DWORD type; + // Leave space for a terminating zero. + DWORD size = sizeof(string_value) - sizeof(string_value[0]); + // Use the 'A' version of this function so that we can use + // StringToNumber. + if (RegQueryValueExA(key, + name, + nullptr, + &type, + reinterpret_cast(&string_value), + &size) == ERROR_SUCCESS && + type == REG_SZ) { + // Make sure the string is null-terminated. + string_value[size / sizeof(string_value[0])] = '\0'; + unsigned local_value; + if (StringToNumber(string_value, &local_value)) { + *out_value = local_value; + return true; + } + } + return false; +} + +} // namespace + +namespace internal { + +SystemSnapshotWin::SystemSnapshotWin() + : SystemSnapshot(), + os_version_full_(), + os_version_build_(), + process_reader_(nullptr), + os_version_major_(0), + os_version_minor_(0), + os_version_bugfix_(0), + os_server_(false), + initialized_() {} + +SystemSnapshotWin::~SystemSnapshotWin() {} + +void SystemSnapshotWin::Initialize(ProcessReaderWin* process_reader) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + process_reader_ = process_reader; + + // We use both IsWindowsServer() (which uses VerifyVersionInfo() internally) + // and GetModuleVersionAndType() (which uses VerQueryValue() internally). + // VerifyVersionInfo() is not trustworthy after Windows 8 (depending on the + // application manifest) so its data is used only to fill the os_server_ + // field, and the rest comes from the version information stamped on + // kernel32.dll and from the registry. + os_server_ = IsWindowsServer(); + + // kernel32.dll used to be a good way to get a non-lying version number, but + // kernel32.dll has been refactored into multiple DLLs so it sometimes does + // not get updated when a new version of Windows ships, especially on + // Windows 11. Additionally, pairs of releases such as 19041/19042 + // (20H1/20H2) actually have identical code and have their differences + // enabled by a configuration setting. Therefore the recommended way to get + // OS version information on recent versions of Windows is to read it from the + // registry. If any of the version-number components are missing from the + // registry (on Windows 7, for instance) then kernel32.dll is used as a + // fallback. + bool version_data_found = false; + int os_version_build = 0; + HKEY key; + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, + L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", + 0, + KEY_QUERY_VALUE, + &key) == ERROR_SUCCESS) { + ScopedRegistryKey scoped_key(key); + + // Read the four components of the version from the registry. + // UBR apparently stands for Update Build Revision and it goes up every + // month when patches are installed. The full version is stored in the + // registry as: + // CurrentMajorVersionNumber.CurrentMinorVersionNumber.CurrentBuildNumber.UBR + if (ReadRegistryDWORD( + key, L"CurrentMajorVersionNumber", &os_version_major_) && + ReadRegistryDWORD( + key, L"CurrentMinorVersionNumber", &os_version_minor_) && + ReadRegistryDWORDFromSZ( + key, "CurrentBuildNumber", &os_version_bugfix_) && + ReadRegistryDWORD(key, L"UBR", &os_version_build)) { + // Since we found all four components in the registry we don't need + // to read them from kernel32.dll. + version_data_found = true; + } + } + + static constexpr wchar_t kSystemDll[] = L"kernel32.dll"; + VS_FIXEDFILEINFO ffi; + if (GetModuleVersionAndType(base::FilePath(kSystemDll), &ffi)) { + std::string flags_string = GetStringForFileFlags(ffi.dwFileFlags); + std::string os_name = GetStringForFileOS(ffi.dwFileOS); + if (!version_data_found) { + os_version_major_ = ffi.dwFileVersionMS >> 16; + os_version_minor_ = ffi.dwFileVersionMS & 0xffff; + os_version_bugfix_ = ffi.dwFileVersionLS >> 16; + os_version_build = static_cast(ffi.dwFileVersionLS & 0xffff); + } + + os_version_build_ = base::StringPrintf("%u", os_version_build); + + os_version_full_ = base::StringPrintf( + "%s %u.%u.%u.%s%s", + os_name.c_str(), + os_version_major_, + os_version_minor_, + os_version_bugfix_, + os_version_build_.c_str(), + flags_string.empty() + ? "" + : (std::string(" (") + flags_string + ")").c_str()); + } + + INITIALIZATION_STATE_SET_VALID(initialized_); +} + +CPUArchitecture SystemSnapshotWin::GetCPUArchitecture() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + +#if defined(ARCH_CPU_X86_FAMILY) + return process_reader_->Is64Bit() ? kCPUArchitectureX86_64 + : kCPUArchitectureX86; +#elif defined(ARCH_CPU_ARM64) + return kCPUArchitectureARM64; +#else +#error Unsupported Windows Arch +#endif +} + +uint32_t SystemSnapshotWin::CPURevision() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + +#if defined(ARCH_CPU_X86_FAMILY) + uint32_t raw = CPUX86Signature(); + uint8_t stepping = raw & 0xf; + uint8_t model = (raw & 0xf0) >> 4; + uint8_t family = (raw & 0xf00) >> 8; + uint8_t extended_model = static_cast((raw & 0xf0000) >> 16); + uint16_t extended_family = (raw & 0xff00000) >> 20; + + // For families before 15, extended_family are simply reserved bits. + if (family < 15) + extended_family = 0; + // extended_model is only used for families 6 and 15. + if (family != 6 && family != 15) + extended_model = 0; + + uint16_t adjusted_family = family + extended_family; + uint8_t adjusted_model = model + (extended_model << 4); + return (adjusted_family << 16) | (adjusted_model << 8) | stepping; +#elif defined(ARCH_CPU_ARM64) + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + + return system_info.wProcessorRevision; +#else +#error Unsupported Windows Arch +#endif +} + +uint8_t SystemSnapshotWin::CPUCount() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + SYSTEM_INFO system_info; + GetSystemInfo(&system_info); + if (!base::IsValueInRangeForNumericType( + system_info.dwNumberOfProcessors)) { + LOG(WARNING) << "dwNumberOfProcessors exceeds uint8_t storage"; + } + return base::saturated_cast(system_info.dwNumberOfProcessors); +} + +std::string SystemSnapshotWin::CPUVendor() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + +#if defined(ARCH_CPU_X86_FAMILY) + int cpu_info[4]; + __cpuid(cpu_info, 0); + char vendor[12]; + *reinterpret_cast(vendor) = cpu_info[1]; + *reinterpret_cast(vendor + 4) = cpu_info[3]; + *reinterpret_cast(vendor + 8) = cpu_info[2]; + return std::string(vendor, sizeof(vendor)); +#elif defined(ARCH_CPU_ARM64) + HKEY key; + + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, + L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0", + 0, + KEY_QUERY_VALUE, + &key) != ERROR_SUCCESS) { + return std::string(); + } + + crashpad::ScopedRegistryKey scoped_key(key); + DWORD type; + char16_t vendor_identifier[1024]; + DWORD vendor_identifier_size = sizeof(vendor_identifier); + + if (RegQueryValueEx(key, + L"VendorIdentifier", + nullptr, + &type, + reinterpret_cast(vendor_identifier), + &vendor_identifier_size) != ERROR_SUCCESS || + type != REG_SZ) { + return std::string(); + } + + std::string return_value; + DCHECK_EQ(vendor_identifier_size % sizeof(char16_t), 0u); + base::UTF16ToUTF8(vendor_identifier, + vendor_identifier_size / sizeof(char16_t), + &return_value); + + return return_value.c_str(); +#else +#error Unsupported Windows Arch +#endif +} + +void SystemSnapshotWin::CPUFrequency(uint64_t* current_hz, + uint64_t* max_hz) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + + int num_cpus = CPUCount(); + DCHECK_GT(num_cpus, 0); + std::vector info(num_cpus); + if (CallNtPowerInformation(ProcessorInformation, + nullptr, + 0, + &info[0], + sizeof(PROCESSOR_POWER_INFORMATION) * num_cpus) != + 0) { + *current_hz = 0; + *max_hz = 0; + return; + } + constexpr uint64_t kMhzToHz = static_cast(1E6); + *current_hz = std::max_element(info.begin(), + info.end(), + [](const PROCESSOR_POWER_INFORMATION& a, + const PROCESSOR_POWER_INFORMATION& b) { + return a.CurrentMhz < b.CurrentMhz; + })->CurrentMhz * + kMhzToHz; + *max_hz = std::max_element(info.begin(), + info.end(), + [](const PROCESSOR_POWER_INFORMATION& a, + const PROCESSOR_POWER_INFORMATION& b) { + return a.MaxMhz < b.MaxMhz; + })->MaxMhz * + kMhzToHz; +} + +uint32_t SystemSnapshotWin::CPUX86Signature() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + +#if defined(ARCH_CPU_X86_FAMILY) + int cpu_info[4]; + // We will never run on any processors that don't support at least function 1. + __cpuid(cpu_info, 1); + return cpu_info[0]; +#else + NOTREACHED(); + return 0; +#endif +} + +uint64_t SystemSnapshotWin::CPUX86Features() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + +#if defined(ARCH_CPU_X86_FAMILY) + int cpu_info[4]; + // We will never run on any processors that don't support at least function 1. + __cpuid(cpu_info, 1); + return (static_cast(cpu_info[2]) << 32) | + static_cast(cpu_info[3]); +#else + NOTREACHED(); + return 0; +#endif +} + +uint64_t SystemSnapshotWin::CPUX86ExtendedFeatures() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + +#if defined(ARCH_CPU_X86_FAMILY) + int cpu_info[4]; + // We will never run on any processors that don't support at least extended + // function 1. + __cpuid(cpu_info, 0x80000001); + return (static_cast(cpu_info[2]) << 32) | + static_cast(cpu_info[3]); +#else + NOTREACHED(); + return 0; +#endif +} + +uint32_t SystemSnapshotWin::CPUX86Leaf7Features() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + +#if defined(ARCH_CPU_X86_FAMILY) + int cpu_info[4]; + + // Make sure leaf 7 can be called. + __cpuid(cpu_info, 0); + if (cpu_info[0] < 7) + return 0; + + __cpuidex(cpu_info, 7, 0); + return cpu_info[1]; +#else + NOTREACHED(); + return 0; +#endif +} + +bool SystemSnapshotWin::CPUX86SupportsDAZ() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + +#if defined(ARCH_CPU_X86_FAMILY) + // The correct way to check for denormals-as-zeros (DAZ) support is to examine + // mxcsr mask, which can be done with fxsave. See Intel Software Developer's + // Manual, Volume 1: Basic Architecture (253665-051), 11.6.3 "Checking for the + // DAZ Flag in the MXCSR Register". Note that since this function tests for + // DAZ support in the CPU, it checks the mxcsr mask. Testing mxcsr would + // indicate whether DAZ is actually enabled, which is a per-thread context + // concern. + + // Test for fxsave support. + uint64_t features = CPUX86Features(); + if (!(features & (UINT64_C(1) << 24))) { + return false; + } + + // Call fxsave. + __declspec(align(16)) uint32_t extended_registers[128]; + _fxsave(&extended_registers); + uint32_t mxcsr_mask = extended_registers[7]; + + // Test the DAZ bit. + return (mxcsr_mask & (1 << 6)) != 0; +#else + NOTREACHED(); + return 0; +#endif +} + +SystemSnapshot::OperatingSystem SystemSnapshotWin::GetOperatingSystem() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return kOperatingSystemWindows; +} + +bool SystemSnapshotWin::OSServer() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return os_server_; +} + +void SystemSnapshotWin::OSVersion(int* major, + int* minor, + int* bugfix, + std::string* build) const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + *major = os_version_major_; + *minor = os_version_minor_; + *bugfix = os_version_bugfix_; + build->assign(os_version_build_); +} + +std::string SystemSnapshotWin::OSVersionFull() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return os_version_full_; +} + +std::string SystemSnapshotWin::MachineDescription() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + // TODO(scottmg): Not sure if there's anything sensible to put here. + return std::string(); +} + +bool SystemSnapshotWin::NXEnabled() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return !!IsProcessorFeaturePresent(PF_NX_ENABLED); +} + +void SystemSnapshotWin::TimeZone(DaylightSavingTimeStatus* dst_status, + int* standard_offset_seconds, + int* daylight_offset_seconds, + std::string* standard_name, + std::string* daylight_name) const { + // This returns the current time zone status rather than the status at the + // time of the snapshot. This differs from the Mac implementation. + TIME_ZONE_INFORMATION time_zone_information; + *dst_status = static_cast( + GetTimeZoneInformation(&time_zone_information)); + *standard_offset_seconds = + (time_zone_information.Bias + time_zone_information.StandardBias) * -60; + *daylight_offset_seconds = + (time_zone_information.Bias + time_zone_information.DaylightBias) * -60; + *standard_name = base::WideToUTF8(time_zone_information.StandardName); + *daylight_name = base::WideToUTF8(time_zone_information.DaylightName); +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/win/system_snapshot_win.h b/shared/sentry/external/crashpad/snapshot/win/system_snapshot_win.h new file mode 100644 index 000000000..d10c25f23 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/system_snapshot_win.h @@ -0,0 +1,97 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_WIN_SYSTEM_SNAPSHOT_WIN_H_ +#define CRASHPAD_SNAPSHOT_WIN_SYSTEM_SNAPSHOT_WIN_H_ + +#include +#include + +#include + +#include "snapshot/system_snapshot.h" +#include "snapshot/win/process_reader_win.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +class ProcessReaderWin; + +namespace internal { + +//! \brief A SystemSnapshot of the running system, when the system runs Windows. +class SystemSnapshotWin final : public SystemSnapshot { + public: + SystemSnapshotWin(); + + SystemSnapshotWin(const SystemSnapshotWin&) = delete; + SystemSnapshotWin& operator=(const SystemSnapshotWin&) = delete; + + ~SystemSnapshotWin() override; + + //! \brief Initializes the object. + //! + //! \param[in] process_reader A reader for the process being snapshotted. + //! + //! It seems odd that a system snapshot implementation would need a + //! ProcessReaderWin, but some of the information reported about the + //! system depends on the process it's being reported for. For example, + //! the architecture returned by GetCPUArchitecture() should be the + //! architecture of the process, which may be different than the native + //! architecture of the system: an x86_64 system can run both x86_64 and + //! 32-bit x86 processes. + void Initialize(ProcessReaderWin* process_reader); + + // SystemSnapshot: + + CPUArchitecture GetCPUArchitecture() const override; + uint32_t CPURevision() const override; + uint8_t CPUCount() const override; + std::string CPUVendor() const override; + void CPUFrequency(uint64_t* current_hz, uint64_t* max_hz) const override; + uint32_t CPUX86Signature() const override; + uint64_t CPUX86Features() const override; + uint64_t CPUX86ExtendedFeatures() const override; + uint32_t CPUX86Leaf7Features() const override; + bool CPUX86SupportsDAZ() const override; + OperatingSystem GetOperatingSystem() const override; + bool OSServer() const override; + void OSVersion(int* major, + int* minor, + int* bugfix, + std::string* build) const override; + std::string OSVersionFull() const override; + bool NXEnabled() const override; + std::string MachineDescription() const override; + void TimeZone(DaylightSavingTimeStatus* dst_status, + int* standard_offset_seconds, + int* daylight_offset_seconds, + std::string* standard_name, + std::string* daylight_name) const override; + + private: + std::string os_version_full_; + std::string os_version_build_; + ProcessReaderWin* process_reader_; // weak + int os_version_major_; + int os_version_minor_; + int os_version_bugfix_; + bool os_server_; + InitializationStateDcheck initialized_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_WIN_SYSTEM_SNAPSHOT_WIN_H_ diff --git a/shared/sentry/external/crashpad/snapshot/win/system_snapshot_win_test.cc b/shared/sentry/external/crashpad/snapshot/win/system_snapshot_win_test.cc new file mode 100644 index 000000000..c8716b33d --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/system_snapshot_win_test.cc @@ -0,0 +1,171 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/win/system_snapshot_win.h" + +#include +#include + +#include + +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "snapshot/win/process_reader_win.h" + +namespace crashpad { +namespace test { +namespace { + +class SystemSnapshotWinTest : public testing::Test { + public: + SystemSnapshotWinTest() + : Test(), + process_reader_(), + system_snapshot_() { + } + + SystemSnapshotWinTest(const SystemSnapshotWinTest&) = delete; + SystemSnapshotWinTest& operator=(const SystemSnapshotWinTest&) = delete; + + const internal::SystemSnapshotWin& system_snapshot() const { + return system_snapshot_; + } + + // testing::Test: + void SetUp() override { + ASSERT_TRUE(process_reader_.Initialize(GetCurrentProcess(), + ProcessSuspensionState::kRunning)); + system_snapshot_.Initialize(&process_reader_); + } + + private: + ProcessReaderWin process_reader_; + internal::SystemSnapshotWin system_snapshot_; +}; + +TEST_F(SystemSnapshotWinTest, GetCPUArchitecture) { + CPUArchitecture cpu_architecture = system_snapshot().GetCPUArchitecture(); + +#if defined(ARCH_CPU_X86) + EXPECT_EQ(cpu_architecture, kCPUArchitectureX86); +#elif defined(ARCH_CPU_X86_64) + EXPECT_EQ(cpu_architecture, kCPUArchitectureX86_64); +#elif defined(ARCH_CPU_ARM64) + EXPECT_EQ(cpu_architecture, kCPUArchitectureARM64); +#else +#error Unsupported Windows Arch +#endif +} + +TEST_F(SystemSnapshotWinTest, CPUCount) { + EXPECT_GE(system_snapshot().CPUCount(), 1); +} + +TEST_F(SystemSnapshotWinTest, CPUVendor) { + std::string cpu_vendor = system_snapshot().CPUVendor(); +#if defined(ARCH_CPU_X86_FAMILY) + EXPECT_TRUE(cpu_vendor == "GenuineIntel" || cpu_vendor == "AuthenticAMD"); +#elif defined(ARCH_CPU_ARM64) + EXPECT_FALSE(cpu_vendor.empty()); +#else +#error Unsupported Windows Arch +#endif +} + +#if defined(ARCH_CPU_X86_FAMILY) +TEST_F(SystemSnapshotWinTest, CPUX86SupportsDAZ) { + // Most SSE2+ machines support Denormals-Are-Zero. This may fail if run on + // older machines. + EXPECT_TRUE(system_snapshot().CPUX86SupportsDAZ()); +} +#endif + +TEST_F(SystemSnapshotWinTest, GetOperatingSystem) { + EXPECT_EQ(system_snapshot().GetOperatingSystem(), + SystemSnapshot::kOperatingSystemWindows); +} + +TEST_F(SystemSnapshotWinTest, OSVersion) { + int major; + int minor; + int bugfix; + std::string build; + system_snapshot().OSVersion(&major, &minor, &bugfix, &build); + + EXPECT_GE(major, 5); + if (major == 5) + EXPECT_GE(minor, 1); + if (major == 6) + EXPECT_TRUE(minor >= 0 && minor <= 3); +} + +TEST_F(SystemSnapshotWinTest, OSVersionFull) { + EXPECT_FALSE(system_snapshot().OSVersionFull().empty()); +} + +TEST_F(SystemSnapshotWinTest, MachineDescription) { + EXPECT_TRUE(system_snapshot().MachineDescription().empty()); +} + +TEST_F(SystemSnapshotWinTest, TimeZone) { + SystemSnapshot::DaylightSavingTimeStatus dst_status; + int standard_offset_seconds; + int daylight_offset_seconds; + std::string standard_name; + std::string daylight_name; + + system_snapshot().TimeZone(&dst_status, + &standard_offset_seconds, + &daylight_offset_seconds, + &standard_name, + &daylight_name); + + // |standard_offset_seconds| gives seconds east of UTC, and |timezone| gives + // seconds west of UTC. + long timezone = 0; + _get_timezone(&timezone); + EXPECT_EQ(standard_offset_seconds, -timezone); + + // In contemporary usage, most time zones have an integer hour offset from + // UTC, although several are at a half-hour offset, and two are at 15-minute + // offsets. Throughout history, other variations existed. See + // https://www.timeanddate.com/time/time-zones-interesting.html. + EXPECT_EQ(standard_offset_seconds % (15 * 60), 0) + << "standard_offset_seconds " << standard_offset_seconds; + + // dst_status of kDoesNotObserveDaylightSavingTime can mean only that the + // adjustment is not automatic, as opposed to daylight/standard differences + // not existing at all. So it cannot be asserted that the two offsets are the + // same in that case. + + EXPECT_EQ(daylight_offset_seconds % (15 * 60), 0) + << "daylight_offset_seconds " << daylight_offset_seconds; + + // In contemporary usage, dst_delta_seconds will almost always be one hour, + // except for Lord Howe Island, Australia, which uses a 30-minute delta. + // Throughout history, other variations existed. See + // https://www.timeanddate.com/time/dst/. + int dst_delta_seconds = daylight_offset_seconds - standard_offset_seconds; + if (dst_delta_seconds != 60 * 60 && dst_delta_seconds != 30 * 60) { + FAIL() << "dst_delta_seconds " << dst_delta_seconds; + } + + if (dst_status != SystemSnapshot::kDoesNotObserveDaylightSavingTime) { + EXPECT_NE(standard_name, daylight_name); + } +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/win/thread_snapshot_win.cc b/shared/sentry/external/crashpad/snapshot/win/thread_snapshot_win.cc new file mode 100644 index 000000000..b891b05b9 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/thread_snapshot_win.cc @@ -0,0 +1,147 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/win/thread_snapshot_win.h" + +#include +#include + +#include "base/check_op.h" +#include "snapshot/capture_memory.h" +#include "snapshot/win/capture_memory_delegate_win.h" +#include "snapshot/win/cpu_context_win.h" +#include "snapshot/win/process_reader_win.h" + +namespace crashpad { +namespace internal { + +ThreadSnapshotWin::ThreadSnapshotWin() + : ThreadSnapshot(), + context_(), + stack_(), + teb_(), + thread_(), + initialized_() {} + +ThreadSnapshotWin::~ThreadSnapshotWin() {} + +bool ThreadSnapshotWin::Initialize( + ProcessReaderWin* process_reader, + const ProcessReaderWin::Thread& process_reader_thread, + uint32_t* gather_indirectly_referenced_memory_bytes_remaining) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + thread_ = process_reader_thread; + if (process_reader->GetProcessInfo().LoggingRangeIsFullyReadable( + CheckedRange(thread_.stack_region_address, + thread_.stack_region_size))) { + stack_.Initialize(process_reader->Memory(), + thread_.stack_region_address, + thread_.stack_region_size); + } else { + stack_.Initialize(process_reader->Memory(), 0, 0); + } + + if (process_reader->GetProcessInfo().LoggingRangeIsFullyReadable( + CheckedRange(thread_.teb_address, + thread_.teb_size))) { + teb_.Initialize( + process_reader->Memory(), thread_.teb_address, thread_.teb_size); + } else { + teb_.Initialize(process_reader->Memory(), 0, 0); + } + +#if defined(ARCH_CPU_X86) + context_.architecture = kCPUArchitectureX86; + context_.x86 = &context_union_.x86; + InitializeX86Context(process_reader_thread.context.native, context_.x86); +#elif defined(ARCH_CPU_X86_64) + if (process_reader->Is64Bit()) { + context_.architecture = kCPUArchitectureX86_64; + context_.x86_64 = &context_union_.x86_64; + InitializeX64Context(process_reader_thread.context.native, context_.x86_64); + } else { + context_.architecture = kCPUArchitectureX86; + context_.x86 = &context_union_.x86; + InitializeX86Context(process_reader_thread.context.wow64, context_.x86); + } +#elif defined(ARCH_CPU_ARM64) + context_.architecture = kCPUArchitectureARM64; + context_.arm64 = &context_union_.arm64; + InitializeARM64Context(process_reader_thread.context.native, context_.arm64); +#else +#error Unsupported Windows Arch +#endif // ARCH_CPU_X86 + +#ifdef CLIENT_STACKTRACES_ENABLED + frames_ = thread_.frames; +#endif + + CaptureMemoryDelegateWin capture_memory_delegate( + process_reader, + thread_, + &pointed_to_memory_, + gather_indirectly_referenced_memory_bytes_remaining); + CaptureMemory::PointedToByContext(context_, &capture_memory_delegate); + if (gather_indirectly_referenced_memory_bytes_remaining) { + CaptureMemory::PointedToByMemoryRange(stack_, &capture_memory_delegate); + } + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +const CPUContext* ThreadSnapshotWin::Context() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &context_; +} + +const MemorySnapshot* ThreadSnapshotWin::Stack() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return &stack_; +} + +uint64_t ThreadSnapshotWin::ThreadID() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_.id; +} + +int ThreadSnapshotWin::SuspendCount() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_.suspend_count; +} + +int ThreadSnapshotWin::Priority() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_.priority; +} + +uint64_t ThreadSnapshotWin::ThreadSpecificDataAddress() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return thread_.teb_address; +} + +std::vector ThreadSnapshotWin::ExtraMemory() const { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + std::vector result; + result.reserve(1 + pointed_to_memory_.size()); + result.push_back(&teb_); + for (const auto& pointed_to_memory : pointed_to_memory_) { + result.push_back(pointed_to_memory.get()); + } + return result; +} + +} // namespace internal +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/snapshot/win/thread_snapshot_win.h b/shared/sentry/external/crashpad/snapshot/win/thread_snapshot_win.h new file mode 100644 index 000000000..b9fafaa23 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/win/thread_snapshot_win.h @@ -0,0 +1,98 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_WIN_THREAD_SNAPSHOT_WIN_H_ +#define CRASHPAD_SNAPSHOT_WIN_THREAD_SNAPSHOT_WIN_H_ + +#include + +#include +#include + +#include "build/build_config.h" +#include "snapshot/cpu_context.h" +#include "snapshot/memory_snapshot.h" +#include "snapshot/memory_snapshot_generic.h" +#include "snapshot/thread_snapshot.h" +#include "snapshot/win/process_reader_win.h" +#include "util/misc/initialization_state_dcheck.h" + +namespace crashpad { + +class ProcessReaderWin; + +namespace internal { + +//! \brief A ThreadSnapshot of a thread in a running (or crashed) process on a +//! Windows system. +class ThreadSnapshotWin final : public ThreadSnapshot { + public: + ThreadSnapshotWin(); + + ThreadSnapshotWin(const ThreadSnapshotWin&) = delete; + ThreadSnapshotWin& operator=(const ThreadSnapshotWin&) = delete; + + ~ThreadSnapshotWin() override; + + //! \brief Initializes the object. + //! + //! \param[in] process_reader A ProcessReaderWin for the process containing + //! the thread. + //! \param[in] process_reader_thread The thread within the ProcessReaderWin + //! for which the snapshot should be created. + //! \param[in,out] gather_indirectly_referenced_memory_bytes_remaining If + //! non-null, add extra memory regions to the snapshot pointed to by the + //! thread's stack. The size of the regions added is subtracted from the + //! count, and when it's `0`, no more regions will be added. + //! + //! \return `true` if the snapshot could be created, `false` otherwise with + //! an appropriate message logged. + bool Initialize( + ProcessReaderWin* process_reader, + const ProcessReaderWin::Thread& process_reader_thread, + uint32_t* gather_indirectly_referenced_memory_bytes_remaining); + + // ThreadSnapshot: + + const CPUContext* Context() const override; + const MemorySnapshot* Stack() const override; + uint64_t ThreadID() const override; + int SuspendCount() const override; + int Priority() const override; + uint64_t ThreadSpecificDataAddress() const override; + std::vector ExtraMemory() const override; + + private: + union { +#if defined(ARCH_CPU_X86_FAMILY) + CPUContextX86 x86; + CPUContextX86_64 x86_64; +#elif defined(ARCH_CPU_ARM64) + CPUContextARM64 arm64; +#else +#error Unsupported Windows Arch +#endif + } context_union_; + CPUContext context_; + MemorySnapshotGeneric stack_; + MemorySnapshotGeneric teb_; + ProcessReaderWin::Thread thread_; + InitializationStateDcheck initialized_; + std::vector> pointed_to_memory_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // CRASHPAD_SNAPSHOT_WIN_THREAD_SNAPSHOT_WIN_H_ diff --git a/shared/sentry/external/crashpad/snapshot/x86/cpuid_reader.cc b/shared/sentry/external/crashpad/snapshot/x86/cpuid_reader.cc new file mode 100644 index 000000000..c1bd837c4 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/x86/cpuid_reader.cc @@ -0,0 +1,138 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "snapshot/x86/cpuid_reader.h" + +#include + +#include "build/build_config.h" +#include "snapshot/cpu_context.h" + +#if BUILDFLAG(IS_WIN) +#include +#include +#endif // BUILDFLAG(IS_WIN) + +#if defined(ARCH_CPU_X86_FAMILY) + +namespace crashpad { +namespace internal { + +CpuidReader::CpuidReader() + : features_(0), + extended_features_(0), + vendor_(), + max_leaf_(0), + signature_(0) { + uint32_t cpuinfo[4]; + Cpuid(cpuinfo, 0); + max_leaf_ = cpuinfo[0]; + vendor_.append(reinterpret_cast(&cpuinfo[1]), 4); + vendor_.append(reinterpret_cast(&cpuinfo[3]), 4); + vendor_.append(reinterpret_cast(&cpuinfo[2]), 4); + + Cpuid(cpuinfo, 1); + signature_ = cpuinfo[0]; + features_ = (static_cast(cpuinfo[2]) << 32) | + static_cast(cpuinfo[3]); + + Cpuid(cpuinfo, 0x80000001); + extended_features_ = (static_cast(cpuinfo[2]) << 32) | + static_cast(cpuinfo[3]); +} + +CpuidReader::~CpuidReader() {} + +uint32_t CpuidReader::Revision() const { + uint8_t stepping = signature_ & 0xf; + uint8_t model = (signature_ & 0xf0) >> 4; + uint8_t family = (signature_ & 0xf00) >> 8; + uint8_t extended_model = static_cast((signature_ & 0xf0000) >> 16); + uint16_t extended_family = (signature_ & 0xff00000) >> 20; + + // For families before 15, extended_family are simply reserved bits. + if (family < 15) + extended_family = 0; + // extended_model is only used for families 6 and 15. + if (family != 6 && family != 15) + extended_model = 0; + + uint16_t adjusted_family = family + extended_family; + uint8_t adjusted_model = model + (extended_model << 4); + return (adjusted_family << 16) | (adjusted_model << 8) | stepping; +} + +uint32_t CpuidReader::Leaf7Features() const { + if (max_leaf_ < 7) { + return 0; + } + uint32_t cpuinfo[4]; + Cpuid(cpuinfo, 7); + return cpuinfo[1]; +} + +bool CpuidReader::SupportsDAZ() const { + // The correct way to check for denormals-as-zeros (DAZ) support is to examine + // mxcsr mask, which can be done with fxsave. See Intel Software Developer’s + // Manual, Volume 1: Basic Architecture (253665-051), 11.6.3 “Checking for the + // DAZ Flag in the MXCSR Registerâ€. Note that since this function tests for + // DAZ support in the CPU, it checks the mxcsr mask. Testing mxcsr would + // indicate whether DAZ is actually enabled, which is a per-thread context + // concern. + + // Test for fxsave support. + if (!(features_ & (UINT64_C(1) << 24))) { + return false; + } + +#if defined(ARCH_CPU_X86) + using Fxsave = CPUContextX86::Fxsave; +#elif defined(ARCH_CPU_X86_64) + using Fxsave = CPUContextX86_64::Fxsave; +#endif + +#if BUILDFLAG(IS_WIN) + __declspec(align(16)) Fxsave fxsave = {}; +#else + Fxsave fxsave __attribute__((aligned(16))) = {}; +#endif + + static_assert(sizeof(fxsave) == 512, "fxsave size"); + static_assert(offsetof(decltype(fxsave), mxcsr_mask) == 28, + "mxcsr_mask offset"); + +#if BUILDFLAG(IS_WIN) + _fxsave(&fxsave); +#else + asm("fxsave %0" : "=m"(fxsave)); +#endif + + // Test the DAZ bit. + return (fxsave.mxcsr_mask & (1 << 6)) != 0; +} + +void CpuidReader::Cpuid(uint32_t cpuinfo[4], uint32_t leaf) const { +#if BUILDFLAG(IS_WIN) + __cpuid(reinterpret_cast(cpuinfo), leaf); +#else + asm("cpuid" + : "=a"(cpuinfo[0]), "=b"(cpuinfo[1]), "=c"(cpuinfo[2]), "=d"(cpuinfo[3]) + : "a"(leaf), "b"(0), "c"(0), "d"(0)); +#endif // BUILDFLAG(IS_WIN) +} + +} // namespace internal +} // namespace crashpad + +#endif // ARCH_CPU_X86_FAMILY diff --git a/shared/sentry/external/crashpad/snapshot/x86/cpuid_reader.h b/shared/sentry/external/crashpad/snapshot/x86/cpuid_reader.h new file mode 100644 index 000000000..39a1ca843 --- /dev/null +++ b/shared/sentry/external/crashpad/snapshot/x86/cpuid_reader.h @@ -0,0 +1,74 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_SNAPSHOT_X86_CPUID_READER_H_ +#define CRASHPAD_SNAPSHOT_X86_CPUID_READER_H_ + +#include + +#include + +#include "build/build_config.h" + +#if defined(ARCH_CPU_X86_FAMILY) + +namespace crashpad { +namespace internal { + +//! \brief Reads x86-family CPU information by calling `cpuid`. +class CpuidReader { + public: + CpuidReader(); + ~CpuidReader(); + + //! \see SystemSnapshot::CPURevision + uint32_t Revision() const; + + //! \see SystemSnapshot::CPUVendor + std::string Vendor() const { return vendor_; } + + //! \see SystemSnapshot::CPUX86Signature + uint32_t Signature() const { return signature_; } + + //! \see SystemSnapshot::CPUX86Features + uint64_t Features() const { return features_; } + + //! \see SystemSnapshot::CPUX86ExtendedFeatures + uint64_t ExtendedFeatures() const { return extended_features_; } + + //! \see SystemSnapshot::CPUX86Leaf7Features + uint32_t Leaf7Features() const; + + //! \see SystemSnapshot::NXEnabled + bool NXEnabled() const { return (ExtendedFeatures() & (1 << 20)) != 0; } + + //! \see SystemSnapshot::CPUX86SupportsDAZ + bool SupportsDAZ() const; + + private: + void Cpuid(uint32_t cpuinfo[4], uint32_t leaf) const; + + uint64_t features_; + uint64_t extended_features_; + std::string vendor_; + uint32_t max_leaf_; + uint32_t signature_; +}; + +} // namespace internal +} // namespace crashpad + +#endif // ARCH_CPU_X86_FAMILY + +#endif // CRASHPAD_SNAPSHOT_X86_CPUID_READER_H_ diff --git a/shared/sentry/external/crashpad/test/BUILD.gn b/shared/sentry/external/crashpad/test/BUILD.gn new file mode 100644 index 000000000..dfc84d162 --- /dev/null +++ b/shared/sentry/external/crashpad/test/BUILD.gn @@ -0,0 +1,263 @@ +# Copyright 2017 The Crashpad Authors. All rights reserved. +# +# 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("../build/crashpad_buildconfig.gni") + +static_library("test") { + testonly = true + + sources = [ + "errors.cc", + "errors.h", + "file.cc", + "file.h", + "filesystem.cc", + "filesystem.h", + "gtest_death.h", + "hex_string.cc", + "hex_string.h", + "main_arguments.cc", + "main_arguments.h", + "multiprocess.h", + "multiprocess_exec.cc", + "multiprocess_exec.h", + "process_type.cc", + "process_type.h", + "scoped_guarded_page.h", + "scoped_module_handle.cc", + "scoped_module_handle.h", + "scoped_temp_dir.cc", + "scoped_temp_dir.h", + "test_paths.cc", + "test_paths.h", + ] + + if (crashpad_is_posix || crashpad_is_fuchsia) { + sources += [ + "scoped_guarded_page_posix.cc", + "scoped_temp_dir_posix.cc", + ] + + if (!crashpad_is_fuchsia && !crashpad_is_ios) { + sources += [ + "multiprocess_exec_posix.cc", + "multiprocess_posix.cc", + ] + } + } + + if (crashpad_is_mac || crashpad_is_ios) { + sources += [ + "mac/mach_errors.cc", + "mac/mach_errors.h", + ] + } + + if (crashpad_is_mac) { + sources += [ + "mac/dyld.cc", + "mac/dyld.h", + "mac/exception_swallower.cc", + "mac/exception_swallower.h", + "mac/mach_multiprocess.cc", + "mac/mach_multiprocess.h", + ] + } + + if (crashpad_is_ios) { + sources -= [ + "multiprocess.h", + "multiprocess_exec.cc", + "multiprocess_exec.h", + ] + } + + if (crashpad_is_linux || crashpad_is_android) { + sources += [ + "linux/fake_ptrace_connection.cc", + "linux/fake_ptrace_connection.h", + "linux/get_tls.cc", + "linux/get_tls.h", + ] + } + + if (crashpad_is_win) { + sources += [ + "multiprocess_exec_win.cc", + "scoped_guarded_page_win.cc", + "scoped_temp_dir_win.cc", + "win/child_launcher.cc", + "win/child_launcher.h", + "win/win_child_process.cc", + "win/win_child_process.h", + "win/win_multiprocess.cc", + "win/win_multiprocess.h", + "win/win_multiprocess_with_temp_dir.cc", + "win/win_multiprocess_with_temp_dir.h", + ] + } + + if (crashpad_is_fuchsia) { + sources += [ "multiprocess_exec_fuchsia.cc" ] + } + + public_configs = [ "..:crashpad_config" ] + + configs += [ + "../build:crashpad_is_in_chromium", + "../build:crashpad_is_in_fuchsia", + ] + + data = [ "test_paths_test_data_root.txt" ] + + deps = [ + "../compat", + "../third_party/googletest:googletest", + "$mini_chromium_source_parent:base", + "../util", + ] + + if (crashpad_is_mac) { + libs = [ "bsm" ] + deps += [ + "../handler", + "../snapshot", + ] + } + + if (crashpad_is_ios) { + deps += [ ":test_bundle_data" ] + } + + if (crashpad_is_win) { + libs = [ "shell32.lib" ] + } + + if (crashpad_is_fuchsia) { + public_deps = [ "../third_party/fuchsia" ] + if (crashpad_is_in_fuchsia) { + deps += [ "//sdk/lib/fdio" ] + } + } +} + +if (crashpad_is_ios) { + bundle_data("test_bundle_data") { + testonly = true + + sources = [ "test_paths_test_data_root.txt" ] + + outputs = [ "{{bundle_resources_dir}}/crashpad_test_data/" + + "{{source_root_relative_dir}}/{{source_file_part}}" ] + } +} + +source_set("test_test") { + testonly = true + + sources = [ + "hex_string_test.cc", + "main_arguments_test.cc", + "multiprocess_exec_test.cc", + "scoped_guarded_page_test.cc", + "scoped_temp_dir_test.cc", + "test_paths_test.cc", + ] + + # TODO(crbug.com/812974): Remove !crashpad_is_fuchsia when Fuchsia is no + # longer treated as a posix platform. + if (crashpad_is_posix && !crashpad_is_fuchsia && !crashpad_is_ios) { + sources += [ "multiprocess_posix_test.cc" ] + } + + if (crashpad_is_mac) { + sources += [ "mac/mach_multiprocess_test.cc" ] + } + + if (crashpad_is_ios) { + sources -= [ + "multiprocess_exec_test.cc", + "scoped_guarded_page_test.cc", + ] + } + + if (crashpad_is_win) { + sources += [ + "win/win_child_process_test.cc", + "win/win_multiprocess_test.cc", + ] + } + + deps = [ + ":test", + "../compat", + "../third_party/googletest:googlemock", + "../third_party/googletest:googletest", + "$mini_chromium_source_parent:base", + "../util", + ] + + data_deps = [ ":crashpad_test_test_multiprocess_exec_test_child" ] + + if (crashpad_is_ios) { + data_deps -= [ ":crashpad_test_test_multiprocess_exec_test_child" ] + } +} + +if (!crashpad_is_ios) { + crashpad_executable("crashpad_test_test_multiprocess_exec_test_child") { + sources = [ "multiprocess_exec_test_child.cc" ] + + deps = [ "$mini_chromium_source_parent:base" ] + } +} + +static_library("googlemock_main") { + testonly = true + sources = [ "gtest_main.cc" ] + configs += [ "../build:crashpad_is_in_chromium" ] + defines = [ "CRASHPAD_TEST_LAUNCHER_GOOGLEMOCK" ] + deps = [ + ":test", + "../third_party/googletest:googlemock", + "../third_party/googletest:googletest", + "$mini_chromium_source_parent:base", + "$mini_chromium_source_parent:base_test_support", + ] + if (crashpad_is_android) { + deps += [ "../util" ] + } + if (crashpad_is_ios) { + deps += [ "ios:google_test_setup" ] + } +} + +static_library("googletest_main") { + testonly = true + sources = [ "gtest_main.cc" ] + configs += [ "../build:crashpad_is_in_chromium" ] + defines = [ "CRASHPAD_TEST_LAUNCHER_GOOGLETEST" ] + deps = [ + ":test", + "../third_party/googletest:googletest", + "$mini_chromium_source_parent:base", + "$mini_chromium_source_parent:base_test_support", + ] + if (crashpad_is_android) { + deps += [ "../util" ] + } + if (crashpad_is_ios) { + deps += [ "ios:google_test_setup" ] + } +} diff --git a/shared/sentry/external/crashpad/test/errors.cc b/shared/sentry/external/crashpad/test/errors.cc new file mode 100644 index 000000000..1f14db28c --- /dev/null +++ b/shared/sentry/external/crashpad/test/errors.cc @@ -0,0 +1,63 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "test/errors.h" + +#include + +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "build/build_config.h" + +#if BUILDFLAG(IS_POSIX) +#include "base/posix/safe_strerror.h" +#elif BUILDFLAG(IS_WIN) +#include +#include +#endif + +namespace crashpad { +namespace test { + +std::string ErrnoMessage(int err, const std::string& base) { +#if BUILDFLAG(IS_POSIX) + std::string err_as_string = base::safe_strerror(errno); + const char* err_string = err_as_string.c_str(); +#elif BUILDFLAG(IS_WIN) + char err_string[256]; + strerror_s(err_string, errno); +#endif + return base::StringPrintf("%s%s%s (%d)", + base.c_str(), + base.empty() ? "" : ": ", + err_string, + err); +} + +std::string ErrnoMessage(const std::string& base) { + return ErrnoMessage(errno, base); +} + +#if BUILDFLAG(IS_WIN) +std::string ErrorMessage(const std::string& base) { + return base::StringPrintf( + "%s%s%s", + base.c_str(), + base.empty() ? "" : ": ", + logging::SystemErrorCodeToString(GetLastError()).c_str()); +} +#endif + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/errors.h b/shared/sentry/external/crashpad/test/errors.h new file mode 100644 index 000000000..5be04f1b9 --- /dev/null +++ b/shared/sentry/external/crashpad/test/errors.h @@ -0,0 +1,82 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_ERRORS_H_ +#define CRASHPAD_TEST_ERRORS_H_ + +#include + +#include "build/build_config.h" + +namespace crashpad { +namespace test { + +// These functions format messages in a similar way to the PLOG and PCHECK +// family of logging macros in base/logging.h. They exist to interoperate with +// Google Test assertions, which don’t interoperate with logging but can be +// streamed to. +// +// Where non-test code could do: +// PCHECK(rv == 0) << "close"; +// Google Test-based test code can do: +// EXPECT_EQ(rv, 0) << ErrnoMessage("close"); + +//! \brief Formats an error message using an `errno` value. +//! +//! The returned string will combine the \a base string, if supplied, with a +//! textual and numeric description of the error. +//! +//! The message is formatted using `strerror()`. \a err may be `0` or outside of +//! the range of known error codes, and the message returned will contain the +//! string that `strerror()` uses in these cases. +//! +//! \param[in] err The error code, usable as an `errno` value. +//! \param[in] base A string to prepend to the error description. +//! +//! \return A string of the format `"Operation not permitted (1)"` if \a err has +//! the value `EPERM` on a system where this is defined to be `1`. If \a +//! base is not empty, it will be prepended to this string, separated by a +//! colon. +std::string ErrnoMessage(int err, const std::string& base = std::string()); + +//! \brief Formats an error message using `errno`. +//! +//! The returned string will combine the \a base string, if supplied, with a +//! textual and numeric description of the error. +//! +//! The message is formatted using `strerror()`. `errno` may be `0` or outside +//! of the range of known error codes, and the message returned will contain the +//! string that `strerror()` uses in these cases. +//! +//! \param[in] base A string to prepend to the error description. +//! +//! \return A string of the format `"Operation not permitted (1)"` if `errno` +//! has the value `EPERM` on a system where this is defined to be `1`. If +//! \a base is not empty, it will be prepended to this string, separated by +//! a colon. +std::string ErrnoMessage(const std::string& base = std::string()); + +#if BUILDFLAG(IS_WIN) || DOXYGEN +//! \brief Formats an error message using `GetLastError()`. +//! +//! The returned string will combine the \a base string, if supplied, with a +//! textual and numeric description of the error. The format is the same as the +//! `PLOG()` formatting in base. +std::string ErrorMessage(const std::string& base = std::string()); +#endif + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_ERRORS_H_ diff --git a/shared/sentry/external/crashpad/test/file.cc b/shared/sentry/external/crashpad/test/file.cc new file mode 100644 index 000000000..bcfb987ec --- /dev/null +++ b/shared/sentry/external/crashpad/test/file.cc @@ -0,0 +1,91 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "test/file.h" + +#include +#include + +#include "base/logging.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "test/errors.h" + +#if BUILDFLAG(IS_WIN) +#include +#endif + +namespace crashpad { +namespace test { + +bool FileExists(const base::FilePath& path) { +#if BUILDFLAG(IS_POSIX) + struct stat st; + int rv = lstat(path.value().c_str(), &st); + static constexpr char stat_function[] = "lstat"; +#elif BUILDFLAG(IS_WIN) + struct _stat st; + int rv = _wstat(path.value().c_str(), &st); + static constexpr char stat_function[] = "_wstat"; +#else +#error "Not implemented" +#endif + if (rv < 0) { + EXPECT_EQ(errno, ENOENT) << ErrnoMessage(stat_function) << " " + << path.value(); + return false; + } + return true; +} + +bool RemoveFileIfExists(const base::FilePath& path) { +#if BUILDFLAG(IS_POSIX) + if (unlink(path.value().c_str()) != 0 && errno != ENOENT) { + PLOG(ERROR) << "unlink " << path.value(); + return false; + } +#elif BUILDFLAG(IS_WIN) + if (!DeleteFile(path.value().c_str()) && + GetLastError() != ERROR_FILE_NOT_FOUND) { + PLOG(ERROR) << "DeleteFile " << base::WideToUTF8(path.value()); + return false; + } +#else +#error "Not implemented" +#endif + return true; +} + +FileOffset FileSize(const base::FilePath& path) { +#if BUILDFLAG(IS_POSIX) + struct stat st; + int rv = lstat(path.value().c_str(), &st); + static constexpr char stat_function[] = "lstat"; +#elif BUILDFLAG(IS_WIN) + struct _stati64 st; + int rv = _wstati64(path.value().c_str(), &st); + static constexpr char stat_function[] = "_wstati64"; +#else +#error "Not implemented" +#endif + if (rv < 0) { + ADD_FAILURE() << ErrnoMessage(stat_function) << " " << path.value(); + return -1; + } + return st.st_size; +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/file.h b/shared/sentry/external/crashpad/test/file.h new file mode 100644 index 000000000..451588a4d --- /dev/null +++ b/shared/sentry/external/crashpad/test/file.h @@ -0,0 +1,51 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_FILE_H_ +#define CRASHPAD_TEST_FILE_H_ + +#include "base/files/file_path.h" +#include "util/file/file_io.h" + +namespace crashpad { +namespace test { + +//! \brief Determines whether a file exists. +//! +//! \param[in] path The path to check for existence. +//! +//! \return `true` if \a path exists. `false` if it does not exist. If an error +//! other than “file not found†occurs when searching for \a path, returns +//! `false` with a Google Test failure added. +bool FileExists(const base::FilePath& path); + +//! \brief Removes a file if it exists, logging a message on failure. +//! +//! \param[in] path The path to the file to remove. +//! \return `true` on success. `false` on failure with a message logged. +bool RemoveFileIfExists(const base::FilePath& path); + +//! \brief Determines the size of a file. +//! +//! \param[in] path The path of the file to check. The file must exist. +//! +//! \return The size of the file at \a path. If the file does not exist, or an +//! error occurs when attempting to determine its size, returns `-1` with a +//! Google Test failure added. +FileOffset FileSize(const base::FilePath& path); + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_FILE_H_ diff --git a/shared/sentry/external/crashpad/test/filesystem.cc b/shared/sentry/external/crashpad/test/filesystem.cc new file mode 100644 index 000000000..a43421206 --- /dev/null +++ b/shared/sentry/external/crashpad/test/filesystem.cc @@ -0,0 +1,218 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "test/filesystem.h" + +#include +#include +#include +#include +#include + +#include "base/logging.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "test/errors.h" +#include "test/scoped_temp_dir.h" +#include "util/file/file_io.h" +#include "util/file/filesystem.h" +#include "util/misc/time.h" + +#if BUILDFLAG(IS_POSIX) +#include + +#include "base/posix/eintr_wrapper.h" +#elif BUILDFLAG(IS_WIN) +#include +#endif + +namespace crashpad { +namespace test { + +namespace { + +#if BUILDFLAG(IS_WIN) + +// Detects the flags necessary to create symbolic links and stores them in +// |flags| if non-nullptr, and returns true on success. If symbolic links can’t +// be created, returns false. +bool SymbolicLinkFlags(DWORD* flags) { + static DWORD symbolic_link_flags = []() { + ScopedTempDir temp_dir_; + + base::FilePath target_path = temp_dir_.path().Append(L"target"); + base::FilePath symlink_path = temp_dir_.path().Append(L"symlink"); + if (::CreateSymbolicLink( + symlink_path.value().c_str(), target_path.value().c_str(), 0)) { + return 0; + } + + DWORD error = GetLastError(); + if (error == ERROR_PRIVILEGE_NOT_HELD) { + if (::CreateSymbolicLink(symlink_path.value().c_str(), + target_path.value().c_str(), + SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE)) { + return SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE; + } + + // This may fail with ERROR_INVALID_PARAMETER if the OS is too old to + // understand SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE, so keep + // ERROR_PRIVILEGE_NOT_HELD for |error|. + } + + // Don’t use ErrorMessage() here because the second CreateSymbolicLink() may + // have scrambled it. Use the saved |error| value instead. + EXPECT_EQ(error, static_cast(ERROR_PRIVILEGE_NOT_HELD)) + << "CreateSymbolicLink: " << logging::SystemErrorCodeToString(error); + return -1; + }(); + + if (symbolic_link_flags == static_cast(-1)) { + return false; + } + + if (flags) { + *flags = symbolic_link_flags; + } + + return true; +} + +#endif // BUILDFLAG(IS_WIN) + +} // namespace + +bool CreateFile(const base::FilePath& file) { + ScopedFileHandle fd(LoggingOpenFileForWrite( + file, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly)); + EXPECT_TRUE(fd.is_valid()); + return fd.is_valid(); +} + +bool PathExists(const base::FilePath& path) { +#if BUILDFLAG(IS_POSIX) + struct stat st; + if (lstat(path.value().c_str(), &st) != 0) { + EXPECT_EQ(errno, ENOENT) << ErrnoMessage("lstat ") << path.value(); + return false; + } + return true; +#elif BUILDFLAG(IS_WIN) + if (GetFileAttributes(path.value().c_str()) == INVALID_FILE_ATTRIBUTES) { + EXPECT_EQ(GetLastError(), static_cast(ERROR_FILE_NOT_FOUND)) + << ErrorMessage("GetFileAttributes ") << base::WideToUTF8(path.value()); + return false; + } + return true; +#endif +} + +bool SetFileModificationTime(const base::FilePath& path, + const timespec& mtime) { +#if BUILDFLAG(IS_APPLE) + // utimensat() isn't available on macOS until 10.13, so lutimes() is used + // instead. + struct stat st; + if (lstat(path.value().c_str(), &st) != 0) { + PLOG(ERROR) << "lstat " << path.value(); + return false; + } + timeval times[2]; + EXPECT_TRUE(TimespecToTimeval(st.st_atimespec, ×[0])); + EXPECT_TRUE(TimespecToTimeval(mtime, ×[1])); + if (lutimes(path.value().c_str(), times) != 0) { + PLOG(ERROR) << "lutimes " << path.value(); + return false; + } + return true; +#elif BUILDFLAG(IS_POSIX) + timespec times[2]; + times[0].tv_sec = 0; + times[0].tv_nsec = UTIME_OMIT; + times[1] = mtime; + if (utimensat(AT_FDCWD, path.value().c_str(), times, AT_SYMLINK_NOFOLLOW) != + 0) { + PLOG(ERROR) << "utimensat " << path.value(); + return false; + } + return true; +#elif BUILDFLAG(IS_WIN) + DWORD flags = FILE_FLAG_OPEN_REPARSE_POINT; + if (IsDirectory(path, true)) { + // required for directory handles + flags |= FILE_FLAG_BACKUP_SEMANTICS; + } + + ScopedFileHandle handle(::CreateFile(path.value().c_str(), + GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE, + nullptr, + OPEN_EXISTING, + flags, + nullptr)); + if (!handle.is_valid()) { + PLOG(ERROR) << "CreateFile " << base::WideToUTF8(path.value()); + return false; + } + + FILETIME filetime = TimespecToFiletimeEpoch(mtime); + if (!SetFileTime(handle.get(), nullptr, nullptr, &filetime)) { + PLOG(ERROR) << "SetFileTime " << base::WideToUTF8(path.value()); + return false; + } + return true; +#endif // BUILDFLAG(IS_APPLE) +} + +#if !BUILDFLAG(IS_FUCHSIA) + +bool CanCreateSymbolicLinks() { +#if BUILDFLAG(IS_POSIX) + return true; +#elif BUILDFLAG(IS_WIN) + return SymbolicLinkFlags(nullptr); +#endif // BUILDFLAG(IS_POSIX) +} + +bool CreateSymbolicLink(const base::FilePath& target_path, + const base::FilePath& symlink_path) { +#if BUILDFLAG(IS_POSIX) + int rv = HANDLE_EINTR( + symlink(target_path.value().c_str(), symlink_path.value().c_str())); + if (rv != 0) { + PLOG(ERROR) << "symlink"; + return false; + } + return true; +#elif BUILDFLAG(IS_WIN) + DWORD symbolic_link_flags = 0; + SymbolicLinkFlags(&symbolic_link_flags); + if (!::CreateSymbolicLink( + symlink_path.value().c_str(), + target_path.value().c_str(), + symbolic_link_flags | + (IsDirectory(target_path, true) ? SYMBOLIC_LINK_FLAG_DIRECTORY + : 0))) { + PLOG(ERROR) << "CreateSymbolicLink"; + return false; + } + return true; +#endif // BUILDFLAG(IS_POSIX) +} + +#endif // !BUILDFLAG(IS_FUCHSIA) + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/filesystem.h b/shared/sentry/external/crashpad/test/filesystem.h new file mode 100644 index 000000000..683213689 --- /dev/null +++ b/shared/sentry/external/crashpad/test/filesystem.h @@ -0,0 +1,75 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_FILESYSTEM_H_ +#define CRASHPAD_TEST_FILESYSTEM_H_ + +#include "base/files/file_path.h" + +#include + +#include "build/build_config.h" + +namespace crashpad { +namespace test { + +//! \brief Creates an empty file at path \a filepath. +bool CreateFile(const base::FilePath& filepath); + +//! \brief Returns `true` if a filesystem node exists at path \a path. +bool PathExists(const base::FilePath& path); + +//! \brief Sets the modification time for a file, directory, or symbolic link. +//! +//! \param[in] path The path to the file to set the modification time for. +//! \param[in] mtime The new modification time for the file. +//! \return `true` on success. Otherwise `false` with a message logged. +bool SetFileModificationTime(const base::FilePath& path, const timespec& mtime); + +#if !BUILDFLAG(IS_FUCHSIA) || DOXYGEN +// There are no symbolic links on Fuchsia. Don’t bother declaring or defining +// symbolic link-related functions at all, because it’s an error to even pretend +// that symbolic links might be available on Fuchsia. + +//! \brief Determines whether it should be possible to create symbolic links. +//! +//! It is always possible to create symbolic links on POSIX. +//! +//! On Windows, it is only possible to create symbolic links when running as an +//! administrator, or as a non-administrator when running Windows 10 build 15063 +//! (1703, Creators Update) or later, provided that developer mode is enabled +//! and `SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE` is used. This function +//! tests the creation of a symbolic link and returns true on success, and false +//! on failure. If the symbolic link could not be created for a reason other +//! than the expected lack of privilege, a message is logged. +//! +//! Additional background:
    Symlinks +//! in Windows 10! +bool CanCreateSymbolicLinks(); + +//! \brief Creates a new symbolic link. +//! +//! \param[in] target_path The target for the link. +//! \param[in] symlink_path The name for the new link. +//! \return `true` on success. Otherwise `false` with a message logged. +bool CreateSymbolicLink(const base::FilePath& target_path, + const base::FilePath& symlink_path); + +#endif // !BUILDFLAG(IS_FUCHSIA) || DOXYGEN + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_FILESYSTEM_H_ diff --git a/shared/sentry/external/crashpad/test/fuchsia_crashpad_tests.cmx b/shared/sentry/external/crashpad/test/fuchsia_crashpad_tests.cmx new file mode 100644 index 000000000..0a8c137f3 --- /dev/null +++ b/shared/sentry/external/crashpad/test/fuchsia_crashpad_tests.cmx @@ -0,0 +1,27 @@ +{ + "facets": { + "fuchsia.test": { + "injected-services": { + "fuchsia.net.name.Lookup": "fuchsia-pkg://fuchsia.com/crashpad-test#meta/dns-resolver.cmx", + "fuchsia.posix.socket.Provider": "fuchsia-pkg://fuchsia.com/crashpad-test#meta/netstack.cmx" + } + } + }, + "include": [ + "syslog/client.shard.cmx" + ], + "program": { + "binary": "bin/crashpad_tests" + }, + "sandbox": { + "features": [ + "deprecated-ambient-replace-as-executable", + "isolated-temp" + ], + "services": [ + "fuchsia.net.name.Lookup", + "fuchsia.posix.socket.Provider", + "fuchsia.process.Launcher" + ] + } +} diff --git a/shared/sentry/external/crashpad/test/gtest_death.h b/shared/sentry/external/crashpad/test/gtest_death.h new file mode 100644 index 000000000..8142c3d13 --- /dev/null +++ b/shared/sentry/external/crashpad/test/gtest_death.h @@ -0,0 +1,133 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_GTEST_DEATH_H_ +#define CRASHPAD_TEST_GTEST_DEATH_H_ + +#include "base/logging.h" +#include "build/build_config.h" +#include "gtest/gtest.h" + +#if BUILDFLAG(IS_APPLE) +#include "test/mac/exception_swallower.h" +#endif + +//! \file + +#if BUILDFLAG(IS_MAC) || DOXYGEN + +//! \brief Wraps the Google Test `ASSERT_DEATH_IF_SUPPORTED()` macro to make +//! assertions about death caused by crashes. +//! +//! On macOS, this macro prevents the system’s crash reporter from handling +//! crashes that occur in \a statement. Crashes are normally visible to the +//! system’s crash reporter, but it is undesirable for intentional +//! ASSERT_DEATH_CRASH() crashes to be handled by any crash reporter. +//! +//! `ASSERT_DEATH_IF_SUPPORTED()` is used instead of `ASSERT_DEATH()` to support +//! platforms where death tests are not implemented by Google Test (e.g. +//! Fuchsia). On platforms where death tests are not implemented, a warning will +//! be logged and the remainder of the test body skipped. +//! +//! \sa ASSERT_DEATH_CHECK() +//! \sa EXPECT_DEATH_CRASH() +#define ASSERT_DEATH_CRASH(statement, regex) \ + do { \ + crashpad::test::ExceptionSwallower exception_swallower; \ + ASSERT_DEATH_IF_SUPPORTED( \ + crashpad::test::ExceptionSwallower::SwallowExceptions(); \ + { statement; }, regex); \ + } while (false) + +//! \brief Wraps the Google Test `EXPECT_DEATH_IF_SUPPORTED()` macro to make +//! assertions about death caused by crashes. +//! +//! On macOS, this macro prevents the system’s crash reporter from handling +//! crashes that occur in \a statement. Crashes are normally visible to the +//! system’s crash reporter, but it is undesirable for intentional +//! EXPECT_DEATH_CRASH() crashes to be handled by any crash reporter. +//! +//! `EXPECT_DEATH_IF_SUPPORTED()` is used instead of `EXPECT_DEATH()` to support +//! platforms where death tests are not implemented by Google Test (e.g. +//! Fuchsia). On platforms where death tests are not implemented, a warning will +//! be logged and the remainder of the test body skipped. +//! +//! \sa EXPECT_DEATH_CHECK() +//! \sa ASSERT_DEATH_CRASH() +#define EXPECT_DEATH_CRASH(statement, regex) \ + do { \ + crashpad::test::ExceptionSwallower exception_swallower; \ + EXPECT_DEATH(crashpad::test::ExceptionSwallower::SwallowExceptions(); \ + { statement; }, \ + regex); \ + } while (false) + +#else // BUILDFLAG(IS_MAC) + +#define ASSERT_DEATH_CRASH(statement, regex) \ + ASSERT_DEATH_IF_SUPPORTED(statement, regex) +#define EXPECT_DEATH_CRASH(statement, regex) \ + EXPECT_DEATH_IF_SUPPORTED(statement, regex) + +#endif // BUILDFLAG(IS_MAC) + +#if !(!defined(MINI_CHROMIUM_BASE_LOGGING_H_) && \ + defined(OFFICIAL_BUILD) && \ + defined(NDEBUG)) || \ + DOXYGEN + +//! \brief Wraps the ASSERT_DEATH_CRASH() macro to make assertions about death +//! caused by `CHECK()` failures. +//! +//! In an in-Chromium build in the official configuration, `CHECK()` does not +//! print its condition or streamed messages. In that case, this macro uses an +//! empty \a regex pattern when calling ASSERT_DEATH_CRASH() to avoid looking +//! for any particular output on the standard error stream. In other build +//! configurations, the \a regex pattern is left intact. +//! +//! On macOS, `CHECK()` failures normally show up as crashes to the system’s +//! crash reporter, but it is undesirable for intentional ASSERT_DEATH_CHECK() +//! crashes to be handled by any crash reporter, so this is implemented in +//! terms of ASSERT_DEATH_CRASH() instead of `ASSERT_DEATH()`. +//! +//! \sa EXPECT_DEATH_CHECK() +#define ASSERT_DEATH_CHECK(statement, regex) \ + ASSERT_DEATH_CRASH(statement, regex) + +//! \brief Wraps the EXPECT_DEATH_CRASH() macro to make assertions about death +//! caused by `CHECK()` failures. +//! +//! In an in-Chromium build in the official configuration, `CHECK()` does not +//! print its condition or streamed messages. In that case, this macro uses an +//! empty \a regex pattern when calling EXPECT_DEATH_CRASH() to avoid looking +//! for any particular output on the standard error stream. In other build +//! configurations, the \a regex pattern is left intact. +//! +//! On macOS, `CHECK()` failures normally show up as crashes to the system’s +//! crash reporter, but it is undesirable for intentional EXPECT_DEATH_CHECK() +//! crashes to be handled by any crash reporter, so this is implemented in +//! terms of EXPECT_DEATH_CRASH() instead of `EXPECT_DEATH()`. +//! +//! \sa ASSERT_DEATH_CHECK() +#define EXPECT_DEATH_CHECK(statement, regex) \ + EXPECT_DEATH_CRASH(statement, regex) + +#else // !(!MINI_CHROMIUM_BASE_LOGGING_H_ && OFFICIAL_BUILD && NDEBUG) + +#define ASSERT_DEATH_CHECK(statement, regex) ASSERT_DEATH_CRASH(statement, "") +#define EXPECT_DEATH_CHECK(statement, regex) EXPECT_DEATH_CRASH(statement, "") + +#endif // !(!MINI_CHROMIUM_BASE_LOGGING_H_ && OFFICIAL_BUILD && NDEBUG) + +#endif // CRASHPAD_TEST_GTEST_DEATH_H_ diff --git a/shared/sentry/external/crashpad/test/gtest_main.cc b/shared/sentry/external/crashpad/test/gtest_main.cc new file mode 100644 index 000000000..04c539b77 --- /dev/null +++ b/shared/sentry/external/crashpad/test/gtest_main.cc @@ -0,0 +1,126 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "base/logging.h" +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "test/main_arguments.h" +#include "test/multiprocess_exec.h" + +#if defined(CRASHPAD_TEST_LAUNCHER_GOOGLEMOCK) +#include "gmock/gmock.h" +#endif // CRASHPAD_TEST_LAUNCHER_GOOGLEMOCK + +#if BUILDFLAG(IS_ANDROID) +#include "util/linux/initial_signal_dispositions.h" +#endif // BUILDFLAG(IS_ANDROID) + +#if BUILDFLAG(IS_IOS) +#include "test/ios/google_test_setup.h" +#endif + +#if BUILDFLAG(IS_WIN) +#include "test/win/win_child_process.h" +#endif // BUILDFLAG(IS_WIN) + +#if defined(CRASHPAD_IS_IN_CHROMIUM) +#include "base/bind.h" +#include "base/test/launcher/unit_test_launcher.h" +#include "base/test/test_suite.h" +#endif // CRASHPAD_IS_IN_CHROMIUM + +namespace { + +#if !BUILDFLAG(IS_IOS) +bool GetChildTestFunctionName(std::string* child_func_name) { + constexpr size_t arg_length = + sizeof(crashpad::test::internal::kChildTestFunction) - 1; + for (const auto& it : crashpad::test::GetMainArguments()) { + if (it.compare( + 0, arg_length, crashpad::test::internal::kChildTestFunction) == 0) { + *child_func_name = it.substr(arg_length); + return true; + } + } + return false; +} +#endif // !BUILDFLAG(IS_IOS) + +} // namespace + +int main(int argc, char* argv[]) { +#if BUILDFLAG(IS_ANDROID) + crashpad::InitializeSignalDispositions(); +#endif // BUILDFLAG(IS_ANDROID) + + crashpad::test::InitializeMainArguments(argc, argv); + +#if !BUILDFLAG(IS_IOS) + std::string child_func_name; + if (GetChildTestFunctionName(&child_func_name)) { + return crashpad::test::internal::CheckedInvokeMultiprocessChild( + child_func_name); + } +#endif // !BUILDFLAG(IS_IOS) + +#if defined(CRASHPAD_IS_IN_CHROMIUM) + +#if BUILDFLAG(IS_WIN) + // Chromium’s test launcher interferes with WinMultiprocess-based tests. Allow + // their child processes to be launched by the standard Google Test-based test + // runner. + const bool use_chromium_test_launcher = + !crashpad::test::WinChildProcess::IsChildProcess(); +#elif BUILDFLAG(IS_ANDROID) + constexpr bool use_chromium_test_launcher = false; +#else // BUILDFLAG(IS_WIN) + constexpr bool use_chromium_test_launcher = true; +#endif // BUILDFLAG(IS_WIN) + + if (use_chromium_test_launcher) { + // This supports --test-launcher-summary-output, which writes a JSON file + // containing test details needed by Swarming. + base::TestSuite test_suite(argc, argv); + return base::LaunchUnitTests( + argc, + argv, + base::BindOnce(&base::TestSuite::Run, base::Unretained(&test_suite))); + } + +#endif // CRASHPAD_IS_IN_CHROMIUM + + // base::TestSuite initializes logging when using Chromium's test launcher. + logging::LoggingSettings settings; + settings.logging_dest = + logging::LOG_TO_STDERR | logging::LOG_TO_SYSTEM_DEBUG_LOG; + logging::InitLogging(settings); + +#if defined(CRASHPAD_TEST_LAUNCHER_GOOGLEMOCK) + testing::InitGoogleMock(&argc, argv); +#elif defined(CRASHPAD_TEST_LAUNCHER_GOOGLETEST) + testing::InitGoogleTest(&argc, argv); +#else // CRASHPAD_TEST_LAUNCHER_GOOGLEMOCK +#error #define CRASHPAD_TEST_LAUNCHER_GOOGLETEST or \ + CRASHPAD_TEST_LAUNCHER_GOOGLEMOCK +#endif // CRASHPAD_TEST_LAUNCHER_GOOGLEMOCK + +#if BUILDFLAG(IS_IOS) + // iOS needs to run tests within the context of an app, so call a helper that + // invokes UIApplicationMain(). The application delegate will call + // RUN_ALL_TESTS() and exit before returning control to this function. + crashpad::test::IOSLaunchApplicationAndRunTests(argc, argv); +#else + return RUN_ALL_TESTS(); +#endif +} diff --git a/shared/sentry/external/crashpad/test/hex_string.cc b/shared/sentry/external/crashpad/test/hex_string.cc new file mode 100644 index 000000000..bb9b5f3a8 --- /dev/null +++ b/shared/sentry/external/crashpad/test/hex_string.cc @@ -0,0 +1,35 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "test/hex_string.h" + +#include "base/strings/stringprintf.h" + +namespace crashpad { +namespace test { + +std::string BytesToHexString(const void* bytes, size_t length) { + const unsigned char* bytes_c = reinterpret_cast(bytes); + + std::string hex_string; + hex_string.reserve(length * 2); + for (size_t index = 0; index < length; ++index) { + hex_string.append(base::StringPrintf("%02x", bytes_c[index])); + } + + return hex_string; +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/hex_string.h b/shared/sentry/external/crashpad/test/hex_string.h new file mode 100644 index 000000000..be0143b7b --- /dev/null +++ b/shared/sentry/external/crashpad/test/hex_string.h @@ -0,0 +1,40 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_HEX_STRING_H_ +#define CRASHPAD_TEST_HEX_STRING_H_ + +#include + +#include + +namespace crashpad { +namespace test { + +//! \brief Returns a hexadecimal string corresponding to \a bytes and \a length. +//! +//! Example usage: +//! \code +//! uint8_t expected[10]; +//! uint8_t observed[10]; +//! // … +//! EXPECT_EQ(BytesToHexString(observed, std::size(observed)), +//! BytesToHexString(expected, std::size(expected))); +//! \endcode +std::string BytesToHexString(const void* bytes, size_t length); + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_HEX_STRING_H_ diff --git a/shared/sentry/external/crashpad/test/hex_string_test.cc b/shared/sentry/external/crashpad/test/hex_string_test.cc new file mode 100644 index 000000000..13aa311a3 --- /dev/null +++ b/shared/sentry/external/crashpad/test/hex_string_test.cc @@ -0,0 +1,35 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "test/hex_string.h" + +#include + +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(HexString, HexString) { + EXPECT_EQ(BytesToHexString(nullptr, 0), ""); + + static constexpr char kBytes[] = "Abc123xyz \x0a\x7f\xf0\x9f\x92\xa9_"; + EXPECT_EQ(BytesToHexString(kBytes, std::size(kBytes)), + "41626331323378797a200a7ff09f92a95f00"); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/ios/BUILD.gn b/shared/sentry/external/crashpad/test/ios/BUILD.gn new file mode 100644 index 000000000..d17e540cb --- /dev/null +++ b/shared/sentry/external/crashpad/test/ios/BUILD.gn @@ -0,0 +1,83 @@ +# Copyright 2020 The Crashpad Authors. All rights reserved. +# +# 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("../../build/crashpad_buildconfig.gni") + +if (crashpad_is_in_chromium) { + import("//build/config/ios/ios_test_runner_xcuitest.gni") + import("//build/config/ios/rules.gni") +} else if (crashpad_is_standalone) { + import("//third_party/mini_chromium/mini_chromium/build/ios/rules.gni") +} + +group("all_tests") { + testonly = true + deps = [ + ":ios_crash_xcuitests_module", + "host:ios_crash_xcuitests", + ] +} + +source_set("google_test_runner_shared_headers") { + testonly = true + sources = [ "cptest_google_test_runner_delegate.h" ] +} + +source_set("google_test_runner") { + testonly = true + sources = [ "cptest_google_test_runner.mm" ] + configs += [ "../..:crashpad_config" ] + deps = [ + "../$mini_chromium_source_parent:base", + "../../build:ios_enable_arc", + "../../build:ios_xctest", + "../../test/ios:google_test_runner_shared_headers", + ] + frameworks = [ "UIKit.framework" ] +} + +source_set("google_test_setup") { + testonly = true + sources = [ + "google_test_setup.h", + "google_test_setup.mm", + ] + configs += [ "../..:crashpad_config" ] + deps = [ + ":google_test_runner_shared_headers", + "../$mini_chromium_source_parent:base", + "../../build:ios_enable_arc", + "../../third_party/googletest:googletest", + ] + frameworks = [ "UIKit.framework" ] +} + +source_set("xcuitests") { + testonly = true + sources = [ "crash_type_xctest.mm" ] + configs += [ "../..:crashpad_config" ] + deps = [ + "../../build:ios_enable_arc", + "../../build:ios_xctest", + "../../test/ios/host:app_shared_sources", + "../../third_party/edo", + "../../util", + ] +} + +ios_test_runner_xcuitest("ios_crash_xcuitests_module") { + xcode_test_application_name = "ios_crash_xcuitests" + deps = [ ":xcuitests" ] + data_deps = [ "host:ios_crash_xcuitests" ] +} diff --git a/shared/sentry/external/crashpad/test/ios/cptest_google_test_runner.mm b/shared/sentry/external/crashpad/test/ios/cptest_google_test_runner.mm new file mode 100644 index 000000000..9ba795e5e --- /dev/null +++ b/shared/sentry/external/crashpad/test/ios/cptest_google_test_runner.mm @@ -0,0 +1,41 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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 +#import + +#include "base/check.h" +#import "test/ios/cptest_google_test_runner_delegate.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +@interface CPTestGoogleTestRunner : XCTestCase +@end + +@implementation CPTestGoogleTestRunner + +- (void)testRunGoogleTests { + id appDelegate = UIApplication.sharedApplication.delegate; + DCHECK([appDelegate + conformsToProtocol:@protocol(CPTestGoogleTestRunnerDelegate)]); + + id runnerDelegate = + static_cast>(appDelegate); + DCHECK(runnerDelegate.supportsRunningGoogleTestsWithXCTest); + XCTAssertTrue([runnerDelegate runGoogleTests] == 0); +} + +@end diff --git a/shared/sentry/external/crashpad/test/ios/cptest_google_test_runner_delegate.h b/shared/sentry/external/crashpad/test/ios/cptest_google_test_runner_delegate.h new file mode 100644 index 000000000..8be183db8 --- /dev/null +++ b/shared/sentry/external/crashpad/test/ios/cptest_google_test_runner_delegate.h @@ -0,0 +1,30 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_IOS_CPTEST_GOOGLE_TEST_RUNNER_DELEGATE_ +#define CRASHPAD_TEST_IOS_CPTEST_GOOGLE_TEST_RUNNER_DELEGATE_ + +@protocol CPTestGoogleTestRunnerDelegate + +// Returns YES if this delegate supports running Google Test tests via a call to +// |runGoogleTests|. +@property(nonatomic, readonly, assign) + BOOL supportsRunningGoogleTestsWithXCTest; + +// Runs Google Test tests and returns the final exit code. +- (int)runGoogleTests; + +@end + +#endif // CRASHPAD_TEST_IOS_CPTEST_GOOGLE_TEST_RUNNER_DELEGATE_H_ diff --git a/shared/sentry/external/crashpad/test/ios/crash_type_xctest.mm b/shared/sentry/external/crashpad/test/ios/crash_type_xctest.mm new file mode 100644 index 000000000..27b4e1f7f --- /dev/null +++ b/shared/sentry/external/crashpad/test/ios/crash_type_xctest.mm @@ -0,0 +1,384 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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 +#include + +#import "Service/Sources/EDOClientService.h" +#include "build/build_config.h" +#import "test/ios/host/cptest_shared_object.h" +#include "util/mach/exception_types.h" +#include "util/mach/mach_extensions.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +@interface CPTestTestCase : XCTestCase { + XCUIApplication* app_; + CPTestSharedObject* rootObject_; +} +@end + +@implementation CPTestTestCase + ++ (void)setUp { + [CPTestTestCase swizzleHandleCrashUnderSymbol]; + [CPTestTestCase swizleMayTerminateOutOfBandWithoutCrashReport]; + + // Override EDO default error handler. Without this, the default EDO error + // handler will throw an error and fail the test. + EDOSetClientErrorHandler(^(NSError* error){ + // Do nothing. + }); +} + +// Swizzle away the -[XCUIApplicationImpl handleCrashUnderSymbol:] callback. +// Without this, any time the host app is intentionally crashed, the test is +// immediately failed. ++ (void)swizzleHandleCrashUnderSymbol { + SEL originalSelector = NSSelectorFromString(@"handleCrashUnderSymbol:"); + SEL swizzledSelector = @selector(handleCrashUnderSymbol:); + Method originalMethod = class_getInstanceMethod( + objc_getClass("XCUIApplicationImpl"), originalSelector); + Method swizzledMethod = + class_getInstanceMethod([self class], swizzledSelector); + method_exchangeImplementations(originalMethod, swizzledMethod); +} + +// Swizzle away the time consuming 'Checking for crash reports corresponding to' +// from -[XCUIApplicationProcess swizleMayTerminateOutOfBandWithoutCrashReport] +// that is unnecessary for these tests. ++ (void)swizleMayTerminateOutOfBandWithoutCrashReport { + SEL originalSelector = + NSSelectorFromString(@"mayTerminateOutOfBandWithoutCrashReport"); + SEL swizzledSelector = @selector(mayTerminateOutOfBandWithoutCrashReport); + Method originalMethod = class_getInstanceMethod( + objc_getClass("XCUIApplicationProcess"), originalSelector); + Method swizzledMethod = + class_getInstanceMethod([self class], swizzledSelector); + method_exchangeImplementations(originalMethod, swizzledMethod); +} + +// This gets called after tearDown, so there's no straightforward way to +// test that this is called. However, not swizzling this out will cause every +// crashing test to fail. +- (void)handleCrashUnderSymbol:(id)arg1 { +} + +- (BOOL)mayTerminateOutOfBandWithoutCrashReport { + return YES; +} + +- (void)setUp { + app_ = [[XCUIApplication alloc] init]; + [app_ launch]; + rootObject_ = [EDOClientService rootObjectWithPort:12345]; + [rootObject_ clearPendingReports]; + XCTAssertEqual([rootObject_ pendingReportCount], 0); + XCTAssertTrue(app_.state == XCUIApplicationStateRunningForeground); +} + +- (void)verifyCrashReportException:(uint32_t)exception { + // Confirm the app is not running. + XCTAssertTrue([app_ waitForState:XCUIApplicationStateNotRunning timeout:15]); + XCTAssertTrue(app_.state == XCUIApplicationStateNotRunning); + + // Restart app to get the report signal. + [app_ launch]; + XCTAssertTrue(app_.state == XCUIApplicationStateRunningForeground); + rootObject_ = [EDOClientService rootObjectWithPort:12345]; + XCTAssertEqual([rootObject_ pendingReportCount], 1); + NSNumber* report_exception; + XCTAssertTrue([rootObject_ pendingReportException:&report_exception]); + XCTAssertEqual(report_exception.unsignedIntValue, exception); + + NSString* rawLogContents = [rootObject_ rawLogContents]; + XCTAssertFalse([rawLogContents containsString:@"allocator used in handler."]); +} + +- (void)testEDO { + NSString* result = [rootObject_ testEDO]; + XCTAssertEqualObjects(result, @"crashpad"); +} + +- (void)testKillAbort { + [rootObject_ crashKillAbort]; + [self verifyCrashReportException:EXC_SOFT_SIGNAL]; + NSNumber* report_exception; + XCTAssertTrue([rootObject_ pendingReportExceptionInfo:&report_exception]); + XCTAssertEqual(report_exception.intValue, SIGABRT); +} + +- (void)testTrap { + [rootObject_ crashTrap]; +#if defined(ARCH_CPU_X86_64) + [self verifyCrashReportException:EXC_BAD_INSTRUCTION]; +#elif defined(ARCH_CPU_ARM64) + [self verifyCrashReportException:EXC_BREAKPOINT]; +#else +#error Port to your CPU architecture +#endif +} + +- (void)testAbort { + [rootObject_ crashAbort]; + [self verifyCrashReportException:EXC_SOFT_SIGNAL]; + NSNumber* report_exception; + XCTAssertTrue([rootObject_ pendingReportExceptionInfo:&report_exception]); + XCTAssertEqual(report_exception.intValue, SIGABRT); +} + +- (void)testBadAccess { + [rootObject_ crashBadAccess]; + [self verifyCrashReportException:EXC_BAD_ACCESS]; +} + +- (void)testException { + [rootObject_ crashException]; + [self verifyCrashReportException:EXC_SOFT_SIGNAL]; + NSNumber* report_exception; + XCTAssertTrue([rootObject_ pendingReportExceptionInfo:&report_exception]); + XCTAssertEqual(report_exception.intValue, SIGABRT); +} + +- (void)testNSException { + [rootObject_ crashNSException]; + [self verifyCrashReportException:crashpad::kMachExceptionFromNSException]; + NSDictionary* dict = [rootObject_ getAnnotations]; + NSString* userInfo = + [dict[@"objects"][0] valueForKeyPath:@"exceptionUserInfo"]; + XCTAssertTrue([userInfo containsString:@"Error Object=* old_args = app_.launchArguments; + app_.launchArguments = @[ @"--alternate-client-annotations" ]; + [self verifyCrashReportException:EXC_SOFT_SIGNAL]; + NSNumber* report_exception; + XCTAssertTrue([rootObject_ pendingReportExceptionInfo:&report_exception]); + XCTAssertEqual(report_exception.intValue, SIGABRT); + + app_.launchArguments = old_args; + + // Confirm the initial crash took the standard annotations. + NSDictionary* dict = [rootObject_ getProcessAnnotations]; + XCTAssertTrue([dict[@"crashpad"] isEqualToString:@"yes"]); + XCTAssertTrue([dict[@"plat"] isEqualToString:@"iOS"]); + XCTAssertTrue([dict[@"prod"] isEqualToString:@"xcuitest"]); + XCTAssertTrue([dict[@"ver"] isEqualToString:@"1"]); + + // Confirm passing alternate client annotation args works. + [rootObject_ clearPendingReports]; + [rootObject_ crashKillAbort]; + [self verifyCrashReportException:EXC_SOFT_SIGNAL]; + XCTAssertTrue([rootObject_ pendingReportExceptionInfo:&report_exception]); + XCTAssertEqual(report_exception.intValue, SIGABRT); + + dict = [rootObject_ getProcessAnnotations]; + XCTAssertTrue([dict[@"crashpad"] isEqualToString:@"no"]); + XCTAssertTrue([dict[@"plat"] isEqualToString:@"macOS"]); + XCTAssertTrue([dict[@"prod"] isEqualToString:@"some_app"]); + XCTAssertTrue([dict[@"ver"] isEqualToString:@"42"]); +} + +#if TARGET_OS_SIMULATOR +- (void)testCrashWithCrashInfoMessage { + if (@available(iOS 15.0, *)) { + // Figure out how to test this on iOS15. + return; + } + [rootObject_ crashWithCrashInfoMessage]; + [self verifyCrashReportException:EXC_BAD_ACCESS]; + NSDictionary* dict = [rootObject_ getAnnotations]; + NSString* dyldMessage = dict[@"vector"][0]; + XCTAssertTrue([dyldMessage isEqualToString:@"dyld: in dlsym()"]); +} +#endif + +// TODO(justincohen): Codesign crashy_initializer.so so it can run on devices. +#if TARGET_OS_SIMULATOR +- (void)testCrashWithDyldErrorString { + if (@available(iOS 15.0, *)) { + // iOS 15 uses dyld4, which doesn't use CRSetCrashLogMessage2 + return; + } + [rootObject_ crashWithDyldErrorString]; + [self verifyCrashReportException:EXC_BAD_INSTRUCTION]; + NSArray* vector = [rootObject_ getAnnotations][@"vector"]; + // This message is set by dyld-353.2.1/src/ImageLoaderMachO.cpp + // ImageLoaderMachO::doInitialization(). + NSString* module = @"crashpad_snapshot_test_module_crashy_initializer.so"; + XCTAssertTrue([vector[0] hasSuffix:module]); +} +#endif + +- (void)testCrashWithAnnotations { + [rootObject_ crashWithAnnotations]; + [self verifyCrashReportException:EXC_SOFT_SIGNAL]; + NSNumber* report_exception; + XCTAssertTrue([rootObject_ pendingReportExceptionInfo:&report_exception]); + XCTAssertEqual(report_exception.intValue, SIGABRT); + + NSDictionary* dict = [rootObject_ getAnnotations]; + NSDictionary* simpleMap = dict[@"simplemap"]; + XCTAssertTrue([simpleMap[@"#TEST# empty_value"] isEqualToString:@""]); + XCTAssertTrue([simpleMap[@"#TEST# key"] isEqualToString:@"value"]); + XCTAssertTrue([simpleMap[@"#TEST# longer"] isEqualToString:@"shorter"]); + XCTAssertTrue([simpleMap[@"#TEST# pad"] isEqualToString:@"crash"]); + XCTAssertTrue([simpleMap[@"#TEST# x"] isEqualToString:@"y"]); + + XCTAssertTrue([[dict[@"objects"][0] valueForKeyPath:@"#TEST# same-name"] + isEqualToString:@"same-name 4"]); + XCTAssertTrue([[dict[@"objects"][1] valueForKeyPath:@"#TEST# same-name"] + isEqualToString:@"same-name 3"]); + XCTAssertTrue([[dict[@"objects"][2] valueForKeyPath:@"#TEST# one"] + isEqualToString:@"moocow"]); +} + +- (void)testDumpWithoutCrash { + [rootObject_ generateDumpWithoutCrash:10 threads:3]; + + // The app should not crash + XCTAssertTrue(app_.state == XCUIApplicationStateRunningForeground); + + XCTAssertEqual([rootObject_ pendingReportCount], 30); +} + +- (void)testSimultaneousCrash { + [rootObject_ crashConcurrentSignalAndMach]; + + // Confirm the app is not running. + XCTAssertTrue([app_ waitForState:XCUIApplicationStateNotRunning timeout:15]); + XCTAssertTrue(app_.state == XCUIApplicationStateNotRunning); + + [app_ launch]; + XCTAssertTrue(app_.state == XCUIApplicationStateRunningForeground); + rootObject_ = [EDOClientService rootObjectWithPort:12345]; + XCTAssertEqual([rootObject_ pendingReportCount], 1); +} + +- (void)testCrashInHandlerReentrant { + XCTAssertTrue(app_.state == XCUIApplicationStateRunningForeground); + rootObject_ = [EDOClientService rootObjectWithPort:12345]; + + [rootObject_ crashInHandlerReentrant]; + + // Confirm the app is not running. + XCTAssertTrue([app_ waitForState:XCUIApplicationStateNotRunning timeout:15]); + XCTAssertTrue(app_.state == XCUIApplicationStateNotRunning); + + [app_ launch]; + XCTAssertTrue(app_.state == XCUIApplicationStateRunningForeground); + rootObject_ = [EDOClientService rootObjectWithPort:12345]; + + XCTAssertEqual([rootObject_ pendingReportCount], 0); + + NSString* rawLogContents = [rootObject_ rawLogContents]; + NSString* errmsg = @"Cannot DumpExceptionFromSignal without writer"; + XCTAssertTrue([rawLogContents containsString:errmsg]); +} + +- (void)testFailureWhenHandlerAllocates { + XCTAssertTrue(app_.state == XCUIApplicationStateRunningForeground); + rootObject_ = [EDOClientService rootObjectWithPort:12345]; + + [rootObject_ allocateWithForbiddenAllocators]; + + // Confirm the app is not running. + XCTAssertTrue([app_ waitForState:XCUIApplicationStateNotRunning timeout:15]); + XCTAssertTrue(app_.state == XCUIApplicationStateNotRunning); + + [app_ launch]; + XCTAssertTrue(app_.state == XCUIApplicationStateRunningForeground); + rootObject_ = [EDOClientService rootObjectWithPort:12345]; + + XCTAssertEqual([rootObject_ pendingReportCount], 0); + + NSString* rawLogContents = [rootObject_ rawLogContents]; + XCTAssertTrue([rawLogContents containsString:@"allocator used in handler."]); +} + +@end diff --git a/shared/sentry/external/crashpad/test/ios/google_test_setup.h b/shared/sentry/external/crashpad/test/ios/google_test_setup.h new file mode 100644 index 000000000..98bb94273 --- /dev/null +++ b/shared/sentry/external/crashpad/test/ios/google_test_setup.h @@ -0,0 +1,33 @@ +// Copyright 2019 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_IOS_GOOGLE_TEST_SETUP_ +#define CRASHPAD_TEST_IOS_GOOGLE_TEST_SETUP_ + +namespace crashpad { +namespace test { + +//! \brief Runs all registered tests in the context of a UIKit application. +//! +//! Invokes UIApplicationMain() to launch the iOS application and runs all +//! registered tests after the application finishes +//! launching. UIApplicationMain() brings up the main runloop and never returns, +//! so therefore this function never returns either. It invokes _exit() to +//! terminate the application after tests have completed. +void IOSLaunchApplicationAndRunTests(int argc, char* argv[]); + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_IOS_GOOGLE_TEST_SETUP_ diff --git a/shared/sentry/external/crashpad/test/ios/google_test_setup.mm b/shared/sentry/external/crashpad/test/ios/google_test_setup.mm new file mode 100644 index 000000000..f49597e1e --- /dev/null +++ b/shared/sentry/external/crashpad/test/ios/google_test_setup.mm @@ -0,0 +1,134 @@ +// Copyright 2019 The Crashpad Authors. All rights reserved. +// +// 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 "test/ios/google_test_setup.h" + +#import + +#include "base/check.h" +#include "gtest/gtest.h" +#include "test/ios/cptest_google_test_runner_delegate.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +@interface UIApplication (Testing) +- (void)_terminateWithStatus:(int)status; +@end + +namespace { + +// The iOS watchdog timer will kill an app that doesn't spin the main event loop +// often enough. This uses a Google Test TestEventListener to spin the current +// loop after each test finishes. However, if any individual test takes too +// long, it is still possible that the app will get killed. +class IOSRunLoopListener : public testing::EmptyTestEventListener { + public: + virtual void OnTestEnd(const testing::TestInfo& test_info) { + @autoreleasepool { + // At the end of the test, spin the default loop for a moment. + NSDate* stop_date = [NSDate dateWithTimeIntervalSinceNow:0.001]; + [[NSRunLoop currentRunLoop] runUntilDate:stop_date]; + } + } +}; + +void RegisterTestEndListener() { + testing::TestEventListeners& listeners = + testing::UnitTest::GetInstance()->listeners(); + listeners.Append(new IOSRunLoopListener); +} + +} // namespace + +@interface CPTestUnitTestApplicationDelegate + : NSObject +@property(nonatomic, readwrite, strong) UIWindow* window; +- (void)runTests; +@end + +@implementation CPTestUnitTestApplicationDelegate + +- (BOOL)application:(UIApplication*)application + didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { + self.window = [[UIWindow alloc] initWithFrame:UIScreen.mainScreen.bounds]; + self.window.backgroundColor = UIColor.whiteColor; + [self.window makeKeyAndVisible]; + + UIViewController* controller = [[UIViewController alloc] init]; + [self.window setRootViewController:controller]; + + // Add a label with the app name. + UILabel* label = [[UILabel alloc] initWithFrame:controller.view.bounds]; + label.text = [[NSProcessInfo processInfo] processName]; + label.textAlignment = NSTextAlignmentCenter; + label.textColor = UIColor.blackColor; + [controller.view addSubview:label]; + + // Queue up the test run. + if (![self supportsRunningGoogleTestsWithXCTest]) { + // When running in XCTest mode, XCTest will invoke |runGoogleTest| directly. + // Otherwise, schedule a call to |runTests|. + [self performSelector:@selector(runTests) withObject:nil afterDelay:0.1]; + } + + return YES; +} + +- (BOOL)supportsRunningGoogleTestsWithXCTest { + return getenv("XCTestConfigurationFilePath") != nullptr; +} + +- (int)runGoogleTests { + RegisterTestEndListener(); + int exitStatus = RUN_ALL_TESTS(); + return exitStatus; +} + +- (void)runTests { + DCHECK(![self supportsRunningGoogleTestsWithXCTest]); + + int exitStatus = [self runGoogleTests]; + + // If a test app is too fast, it will exit before Instruments has has a + // a chance to initialize and no test results will be seen. + // TODO(crbug.com/137010): Figure out how much time is actually needed, and + // sleep only to make sure that much time has elapsed since launch. + [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]]; + self.window = nil; + + // Use the hidden selector to try and cleanly take down the app (otherwise + // things can think the app crashed even on a zero exit status). + UIApplication* application = [UIApplication sharedApplication]; + [application _terminateWithStatus:exitStatus]; + + exit(exitStatus); +} + +@end + +namespace crashpad { +namespace test { + +void IOSLaunchApplicationAndRunTests(int argc, char* argv[]) { + @autoreleasepool { + int exit_status = UIApplicationMain( + argc, argv, nil, @"CPTestUnitTestApplicationDelegate"); + exit(exit_status); + } +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/ios/host/BUILD.gn b/shared/sentry/external/crashpad/test/ios/host/BUILD.gn new file mode 100644 index 000000000..a5e04ee79 --- /dev/null +++ b/shared/sentry/external/crashpad/test/ios/host/BUILD.gn @@ -0,0 +1,74 @@ +# Copyright 2020 The Crashpad Authors. All rights reserved. +# +# 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("../../../build/crashpad_buildconfig.gni") + +if (crashpad_is_in_chromium) { + import("//build/config/ios/rules.gni") +} else if (crashpad_is_standalone) { + import("//third_party/mini_chromium/mini_chromium/build/ios/rules.gni") +} + +source_set("app_shared_sources") { + testonly = true + sources = [ "cptest_shared_object.h" ] + configs += [ "../../..:crashpad_config" ] + deps = [ "../../../build:ios_enable_arc" ] + frameworks = [ "UIKit.framework" ] +} + +static_library("app_host_sources") { + testonly = true + sources = [ + "cptest_application_delegate.h", + "cptest_application_delegate.mm", + "cptest_crash_view_controller.h", + "cptest_crash_view_controller.mm", + "handler_forbidden_allocators.cc", + "handler_forbidden_allocators.h", + "main.mm", + ] + configs += [ "../../..:crashpad_config" ] + deps = [ + ":app_shared_sources", + "../../../build:ios_enable_arc", + "../../../client", + "../../../snapshot", + "../../../test", + "../../../third_party/edo", + ] + frameworks = [ + "Foundation.framework", + "UIKit.framework", + ] +} + +# TODO(justincohen): Codesign crashy_initializer.so so it can run on devices. +bundle_data("crashy_module_bundle") { + testonly = true + sources = + [ "$root_out_dir/crashpad_snapshot_test_module_crashy_initializer.so" ] + outputs = [ "{{bundle_contents_dir}}/crashpad_snapshot_test_module_crashy_initializer.so" ] + public_deps = + [ "../../../snapshot:crashpad_snapshot_test_module_crashy_initializer" ] +} + +ios_app_bundle("ios_crash_xcuitests") { + info_plist = "Info.plist" + testonly = true + deps = [ + ":app_host_sources", + ":crashy_module_bundle", + ] +} diff --git a/shared/sentry/external/crashpad/test/ios/host/Info.plist b/shared/sentry/external/crashpad/test/ios/host/Info.plist new file mode 100644 index 000000000..4c62e9695 --- /dev/null +++ b/shared/sentry/external/crashpad/test/ios/host/Info.plist @@ -0,0 +1,112 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + ${PRODUCT_NAME} + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${IOS_BUNDLE_ID_PREFIX}.googletest.${EXECUTABLE_NAME:rfc1034identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1.0 + LSRequiresIPhoneOS + + UILaunchImages + + + UILaunchImageMinimumOSVersion + 9.0 + UILaunchImageName + Default + UILaunchImageSize + {320, 480} + + + UILaunchImageMinimumOSVersion + 9.0 + UILaunchImageName + Default + UILaunchImageSize + {320, 568} + + + UILaunchImageMinimumOSVersion + 9.0 + UILaunchImageName + Default + UILaunchImageSize + {375, 667} + + + UILaunchImageMinimumOSVersion + 9.0 + UILaunchImageName + Default + UILaunchImageSize + {414, 736} + + + UILaunchImageMinimumOSVersion + 9.0 + UILaunchImageName + Default + UILaunchImageSize + {375, 812} + + + UILaunchImageMinimumOSVersion + 9.0 + UILaunchImageName + Default + UILaunchImageSize + {414, 896} + + + UILaunchImages~ipad + + + UILaunchImageMinimumOSVersion + 9.0 + UILaunchImageName + Default + UILaunchImageSize + {768, 1024} + + + UILaunchImageMinimumOSVersion + 9.0 + UILaunchImageName + Default + UILaunchImageSize + {1024, 1366} + + + UILaunchImageMinimumOSVersion + 9.0 + UILaunchImageName + Default + UILaunchImageSize + {832, 1114} + + + UISupportedInterfaceOrientation + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/shared/sentry/external/crashpad/test/ios/host/cptest_application_delegate.h b/shared/sentry/external/crashpad/test/ios/host/cptest_application_delegate.h new file mode 100644 index 000000000..ca835f12a --- /dev/null +++ b/shared/sentry/external/crashpad/test/ios/host/cptest_application_delegate.h @@ -0,0 +1,23 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_IOS_HOST_CPTEST_APPLICATION_DELEGATE_H_ +#define CRASHPAD_TEST_IOS_HOST_CPTEST_APPLICATION_DELEGATE_H_ + +#import + +@interface CPTestApplicationDelegate : UIResponder +@end + +#endif // CRASHPAD_TEST_IOS_HOST_CPTEST_APPLICATION_DELEGATE_H_ diff --git a/shared/sentry/external/crashpad/test/ios/host/cptest_application_delegate.mm b/shared/sentry/external/crashpad/test/ios/host/cptest_application_delegate.mm new file mode 100644 index 000000000..503d2febf --- /dev/null +++ b/shared/sentry/external/crashpad/test/ios/host/cptest_application_delegate.mm @@ -0,0 +1,498 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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 "test/ios/host/cptest_application_delegate.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#import "Service/Sources/EDOHostNamingService.h" +#import "Service/Sources/EDOHostService.h" +#import "Service/Sources/NSObject+EDOValueObject.h" +#include "base/logging.h" +#include "base/strings/sys_string_conversions.h" +#include "client/annotation.h" +#include "client/annotation_list.h" +#include "client/crash_report_database.h" +#include "client/crashpad_client.h" +#include "client/crashpad_info.h" +#include "client/simple_string_dictionary.h" +#include "client/simulate_crash.h" +#include "snapshot/minidump/process_snapshot_minidump.h" +#include "test/file.h" +#import "test/ios/host/cptest_crash_view_controller.h" +#import "test/ios/host/cptest_shared_object.h" +#import "test/ios/host/handler_forbidden_allocators.h" +#include "util/file/filesystem.h" +#include "util/ios/raw_logging.h" +#include "util/thread/thread.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +using OperationStatus = crashpad::CrashReportDatabase::OperationStatus; +using Report = crashpad::CrashReportDatabase::Report; + +namespace { + +base::FilePath GetDatabaseDir() { + base::FilePath database_dir([NSFileManager.defaultManager + URLsForDirectory:NSDocumentDirectory + inDomains:NSUserDomainMask] + .lastObject.path.UTF8String); + return database_dir.Append("crashpad"); +} + +base::FilePath GetRawLogOutputFile() { + base::FilePath document_directory([NSFileManager.defaultManager + URLsForDirectory:NSDocumentDirectory + inDomains:NSUserDomainMask] + .lastObject.path.UTF8String); + return document_directory.Append("raw_log_output.txt"); +} + +std::unique_ptr GetDatabase() { + base::FilePath database_dir = GetDatabaseDir(); + std::unique_ptr database = + crashpad::CrashReportDatabase::Initialize(database_dir); + return database; +} + +OperationStatus GetPendingReports(std::vector* pending_reports) { + std::unique_ptr database(GetDatabase()); + return database->GetPendingReports(pending_reports); +} + +std::unique_ptr +GetProcessSnapshotMinidumpFromSinglePending() { + std::vector pending_reports; + OperationStatus status = GetPendingReports(&pending_reports); + if (status != crashpad::CrashReportDatabase::kNoError || + pending_reports.size() != 1) { + return nullptr; + } + + auto reader = std::make_unique(); + auto process_snapshot = std::make_unique(); + if (!reader->Open(pending_reports[0].file_path) || + !process_snapshot->Initialize(reader.get())) { + return nullptr; + } + return process_snapshot; +} + +UIWindow* GetAnyWindow() { +#if defined(__IPHONE_15_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_15_0 + if (@available(iOS 15.0, *)) { + UIWindowScene* scene = reinterpret_cast( + [UIApplication sharedApplication].connectedScenes.anyObject); + return scene.keyWindow; + } +#endif + return [UIApplication sharedApplication].windows[0]; +} + +[[clang::optnone]] void recurse(int counter) { + // Fill up the stack faster. + int arr[1024]; + arr[0] = counter; + if (counter > INT_MAX) + return; + recurse(++counter); +} + +} // namespace + +@interface CPTestApplicationDelegate () +- (void)processIntermediateDumps; +@property(copy, nonatomic) NSString* raw_log_output; +@end + +@implementation CPTestApplicationDelegate { + crashpad::CrashpadClient client_; + crashpad::ScopedFileHandle raw_logging_file_; +} + +@synthesize window = _window; + +- (BOOL)application:(UIApplication*)application + didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { + base::FilePath raw_log_file_path = GetRawLogOutputFile(); + NSString* path = + [NSString stringWithUTF8String:raw_log_file_path.value().c_str()]; + self.raw_log_output = + [[NSString alloc] initWithContentsOfFile:path + encoding:NSUTF8StringEncoding + error:NULL]; + raw_logging_file_.reset( + LoggingOpenFileForWrite(raw_log_file_path, + crashpad::FileWriteMode::kTruncateOrCreate, + crashpad::FilePermissions::kOwnerOnly)); + crashpad::internal::SetFileHandleForTesting(raw_logging_file_.get()); + + // Start up crashpad. + std::map annotations = { + {"prod", "xcuitest"}, {"ver", "1"}, {"plat", "iOS"}, {"crashpad", "yes"}}; + NSArray* arguments = [[NSProcessInfo processInfo] arguments]; + if ([arguments containsObject:@"--alternate-client-annotations"]) { + annotations = {{"prod", "some_app"}, + {"ver", "42"}, + {"plat", "macOS"}, + {"crashpad", "no"}}; + } + if (client_.StartCrashpadInProcessHandler( + GetDatabaseDir(), "", annotations)) { + client_.ProcessIntermediateDumps(); + } + + self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]]; + [self.window makeKeyAndVisible]; + self.window.backgroundColor = UIColor.greenColor; + + CPTestCrashViewController* controller = + [[CPTestCrashViewController alloc] init]; + self.window.rootViewController = controller; + + // Start up EDO. + [EDOHostService serviceWithPort:12345 + rootObject:[[CPTestSharedObject alloc] init] + queue:dispatch_get_main_queue()]; + + return YES; +} + +- (void)processIntermediateDumps { + client_.ProcessIntermediateDumps(); +} + +@end + +@implementation CPTestSharedObject + +- (NSString*)testEDO { + return @"crashpad"; +} + +- (void)processIntermediateDumps { + CPTestApplicationDelegate* delegate = + (CPTestApplicationDelegate*)UIApplication.sharedApplication.delegate; + [delegate processIntermediateDumps]; +} + +- (void)clearPendingReports { + std::unique_ptr database(GetDatabase()); + std::vector pending_reports; + database->GetPendingReports(&pending_reports); + for (auto report : pending_reports) { + database->DeleteReport(report.uuid); + } +} + +- (int)pendingReportCount { + std::vector pending_reports; + OperationStatus status = GetPendingReports(&pending_reports); + if (status != crashpad::CrashReportDatabase::kNoError) { + return -1; + } + return pending_reports.size(); +} + +- (bool)pendingReportException:(NSNumber**)exception { + auto process_snapshot = GetProcessSnapshotMinidumpFromSinglePending(); + if (!process_snapshot || !process_snapshot->Exception()->Exception()) + return false; + *exception = [NSNumber + numberWithUnsignedInt:process_snapshot->Exception()->Exception()]; + return true; +} + +- (bool)pendingReportExceptionInfo:(NSNumber**)exception_info { + auto process_snapshot = GetProcessSnapshotMinidumpFromSinglePending(); + if (!process_snapshot || !process_snapshot->Exception()->ExceptionInfo()) + return false; + + *exception_info = [NSNumber + numberWithUnsignedInt:process_snapshot->Exception()->ExceptionInfo()]; + return true; +} + +- (NSDictionary*)getAnnotations { + auto process_snapshot = GetProcessSnapshotMinidumpFromSinglePending(); + if (!process_snapshot) + return @{}; + + NSDictionary* dict = @{ + @"simplemap" : [@{} mutableCopy], + @"vector" : [@[] mutableCopy], + @"objects" : [@[] mutableCopy] + }; + for (const auto* module : process_snapshot->Modules()) { + for (const auto& kv : module->AnnotationsSimpleMap()) { + [dict[@"simplemap"] setValue:@(kv.second.c_str()) + forKey:@(kv.first.c_str())]; + } + for (const std::string& annotation : module->AnnotationsVector()) { + [dict[@"vector"] addObject:@(annotation.c_str())]; + } + for (const auto& annotation : module->AnnotationObjects()) { + if (annotation.type != + static_cast(crashpad::Annotation::Type::kString)) { + continue; + } + std::string value(reinterpret_cast(annotation.value.data()), + annotation.value.size()); + [dict[@"objects"] + addObject:@{@(annotation.name.c_str()) : @(value.c_str())}]; + } + } + return [dict passByValue]; +} + +- (NSDictionary*)getProcessAnnotations { + auto process_snapshot = GetProcessSnapshotMinidumpFromSinglePending(); + if (!process_snapshot) + return @{}; + + NSDictionary* dict = [@{} mutableCopy]; + for (const auto& kv : process_snapshot->AnnotationsSimpleMap()) { + [dict setValue:@(kv.second.c_str()) forKey:@(kv.first.c_str())]; + } + + return [dict passByValue]; +} + +// Use [[clang::optnone]] here to get consistent exception codes, otherwise the +// exception can change depending on optimization level. +- (void)crashBadAccess [[clang::optnone]] { + strcpy(nullptr, "bla"); +} + +- (void)crashKillAbort { + crashpad::test::ReplaceAllocatorsWithHandlerForbidden(); + kill(getpid(), SIGABRT); +} + +- (void)crashTrap { + crashpad::test::ReplaceAllocatorsWithHandlerForbidden(); + __builtin_trap(); +} + +- (void)crashAbort { + crashpad::test::ReplaceAllocatorsWithHandlerForbidden(); + abort(); +} + +- (void)crashException { + std::vector empty_vector = {}; + empty_vector.at(42); +} + +- (void)crashNSException { + // EDO has its own sinkhole which will suppress this attempt at an NSException + // crash, so dispatch this out of the sinkhole. + dispatch_async(dispatch_get_main_queue(), ^{ + NSError* error = [NSError errorWithDomain:@"com.crashpad.xcuitests" + code:200 + userInfo:@{@"Error Object" : self}]; + + [[NSException exceptionWithName:NSInternalInconsistencyException + reason:@"Intentionally throwing error." + userInfo:@{NSUnderlyingErrorKey : error}] raise]; + }); +} + +- (void)crashUnhandledNSException { + std::thread t([self]() { + @autoreleasepool { + @try { + NSError* error = [NSError errorWithDomain:@"com.crashpad.xcuitests" + code:200 + userInfo:@{@"Error Object" : self}]; + + [[NSException exceptionWithName:NSInternalInconsistencyException + reason:@"Intentionally throwing error." + userInfo:@{NSUnderlyingErrorKey : error}] raise]; + } @catch (id reason_exception) { + // Intentionally use throw here to intentionally make a sinkhole that + // will be missed by ObjcPreprocessor. + objc_exception_throw(reason_exception); + } + } + }); + t.join(); +} + +- (void)crashUnrecognizedSelectorAfterDelay { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wundeclared-selector" + [self performSelector:@selector(does_not_exist) withObject:nil afterDelay:1]; +#pragma clang diagnostic pop +} + +- (void)catchNSException { + @try { + NSArray* empty_array = @[]; + [empty_array objectAtIndex:42]; + } @catch (NSException* exception) { + } @finally { + } +} + +- (void)crashCoreAutoLayoutSinkhole { + // EDO has its own sinkhole which will suppress this attempt at an NSException + // crash, so dispatch this out of the sinkhole. + dispatch_async(dispatch_get_main_queue(), ^{ + UIView* unattachedView = [[UIView alloc] init]; + UIWindow* window = GetAnyWindow(); + [NSLayoutConstraint activateConstraints:@[ + [window.rootViewController.view.bottomAnchor + constraintEqualToAnchor:unattachedView.bottomAnchor], + ]]; + }); +} + +- (void)crashRecursion { + recurse(0); +} + +- (void)crashWithCrashInfoMessage { + dlsym(nullptr, nullptr); +} + +- (void)crashWithDyldErrorString { + std::string crashy_initializer = + base::SysNSStringToUTF8([[NSBundle mainBundle] + pathForResource:@"crashpad_snapshot_test_module_crashy_initializer" + ofType:@"so"]); + dlopen(crashy_initializer.c_str(), RTLD_LAZY | RTLD_LOCAL); +} + +- (void)crashWithAnnotations { + // This is “leaked†to crashpad_info. + crashpad::SimpleStringDictionary* simple_annotations = + new crashpad::SimpleStringDictionary(); + simple_annotations->SetKeyValue("#TEST# pad", "break"); + simple_annotations->SetKeyValue("#TEST# key", "value"); + simple_annotations->SetKeyValue("#TEST# pad", "crash"); + simple_annotations->SetKeyValue("#TEST# x", "y"); + simple_annotations->SetKeyValue("#TEST# longer", "shorter"); + simple_annotations->SetKeyValue("#TEST# empty_value", ""); + + crashpad::CrashpadInfo* crashpad_info = + crashpad::CrashpadInfo::GetCrashpadInfo(); + + crashpad_info->set_simple_annotations(simple_annotations); + + crashpad::AnnotationList::Register(); // This is “leaked†to crashpad_info. + + static crashpad::StringAnnotation<32> test_annotation_one{"#TEST# one"}; + static crashpad::StringAnnotation<32> test_annotation_two{"#TEST# two"}; + static crashpad::StringAnnotation<32> test_annotation_three{ + "#TEST# same-name"}; + static crashpad::StringAnnotation<32> test_annotation_four{ + "#TEST# same-name"}; + + test_annotation_one.Set("moocow"); + test_annotation_two.Set("this will be cleared"); + test_annotation_three.Set("same-name 3"); + test_annotation_four.Set("same-name 4"); + test_annotation_two.Clear(); + abort(); +} + +class RaceThread : public crashpad::Thread { + public: + explicit RaceThread() : Thread() {} + + void SetCount(int count) { count_ = count; } + + private: + void ThreadMain() override { + for (int i = 0; i < count_; ++i) { + CRASHPAD_SIMULATE_CRASH(); + } + } + + int count_; +}; + +- (void)generateDumpWithoutCrash:(int)dump_count threads:(int)threads { + std::vector race_threads(threads); + for (RaceThread& race_thread : race_threads) { + race_thread.SetCount(dump_count); + race_thread.Start(); + } + + for (RaceThread& race_thread : race_threads) { + race_thread.Join(); + } +} + +class CrashThread : public crashpad::Thread { + public: + explicit CrashThread(bool signal) : Thread(), signal_(signal) {} + + private: + void ThreadMain() override { + sleep(1); + if (signal_) { + abort(); + } else { + __builtin_trap(); + } + } + bool signal_; +}; + +- (void)crashConcurrentSignalAndMach { + CrashThread signal_thread(true); + CrashThread mach_thread(false); + signal_thread.Start(); + mach_thread.Start(); + signal_thread.Join(); + mach_thread.Join(); +} + +- (void)crashInHandlerReentrant { + crashpad::CrashpadClient client_; + client_.SetMachExceptionCallbackForTesting(abort); + + // Trigger a Mach exception. + [self crashTrap]; +} + +- (void)allocateWithForbiddenAllocators { + crashpad::test::ReplaceAllocatorsWithHandlerForbidden(); + (void)malloc(10); +} + +- (NSString*)rawLogContents { + CPTestApplicationDelegate* delegate = + (CPTestApplicationDelegate*)UIApplication.sharedApplication.delegate; + return delegate.raw_log_output; +} + +@end diff --git a/shared/sentry/external/crashpad/test/ios/host/cptest_crash_view_controller.h b/shared/sentry/external/crashpad/test/ios/host/cptest_crash_view_controller.h new file mode 100644 index 000000000..2a41e129e --- /dev/null +++ b/shared/sentry/external/crashpad/test/ios/host/cptest_crash_view_controller.h @@ -0,0 +1,23 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_IOS_HOST_CPTEST_CRASH_VIEW_CONTROLLER_H_ +#define CRASHPAD_TEST_IOS_HOST_CPTEST_CRASH_VIEW_CONTROLLER_H_ + +#import + +@interface CPTestCrashViewController : UIViewController +@end + +#endif // CRASHPAD_TEST_IOS_HOST_CPTEST_CRASH_VIEW_CONTROLLER_H_ diff --git a/shared/sentry/external/crashpad/test/ios/host/cptest_crash_view_controller.mm b/shared/sentry/external/crashpad/test/ios/host/cptest_crash_view_controller.mm new file mode 100644 index 000000000..af69845ca --- /dev/null +++ b/shared/sentry/external/crashpad/test/ios/host/cptest_crash_view_controller.mm @@ -0,0 +1,64 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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 "test/ios/host/cptest_crash_view_controller.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +@implementation CPTestCrashViewController + +- (void)loadView { + self.view = [[UIView alloc] init]; + + UIStackView* buttonStack = [[UIStackView alloc] init]; + buttonStack.axis = UILayoutConstraintAxisVertical; + buttonStack.spacing = 6; + + UIButton* button = [UIButton new]; + [button setTitle:@"UIGestureEnvironmentException" + forState:UIControlStateNormal]; + UITapGestureRecognizer* tapGesture = [[UITapGestureRecognizer alloc] + initWithTarget:self + action:@selector(throwUIGestureEnvironmentException)]; + [button addGestureRecognizer:tapGesture]; + [button setTranslatesAutoresizingMaskIntoConstraints:NO]; + + [buttonStack addArrangedSubview:button]; + + [self.view addSubview:buttonStack]; + + [buttonStack setTranslatesAutoresizingMaskIntoConstraints:NO]; + + [NSLayoutConstraint activateConstraints:@[ + [buttonStack.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor], + [buttonStack.topAnchor constraintEqualToAnchor:self.view.topAnchor], + [buttonStack.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor], + [buttonStack.trailingAnchor + constraintEqualToAnchor:self.view.trailingAnchor], + ]]; +} + +- (void)throwUIGestureEnvironmentException { + NSArray* empty_array = @[]; + [empty_array objectAtIndex:42]; +} + +- (void)viewDidLoad { + [super viewDidLoad]; + self.view.backgroundColor = UIColor.redColor; +} + +@end diff --git a/shared/sentry/external/crashpad/test/ios/host/cptest_shared_object.h b/shared/sentry/external/crashpad/test/ios/host/cptest_shared_object.h new file mode 100644 index 000000000..47ee7ad35 --- /dev/null +++ b/shared/sentry/external/crashpad/test/ios/host/cptest_shared_object.h @@ -0,0 +1,120 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_IOS_HOST_SHARED_OBJECT_H_ +#define CRASHPAD_TEST_IOS_HOST_SHARED_OBJECT_H_ + +#import + +@interface CPTestSharedObject : NSObject + +// Returns the string "crashpad" for testing EDO. +- (NSString*)testEDO; + +// Tell Crashpad to process intermediate dumps. +- (void)processIntermediateDumps; + +// Clear pending reports from Crashpad database. +- (void)clearPendingReports; + +// Returns the number of pending reports, or -1 if there's an error getting +// report. +- (int)pendingReportCount; + +// Returns true if there's a single pending report and sets the exception code +// in the out |exception| parameter. Returns false if there's a different number +// of pending reports. +- (bool)pendingReportException:(NSNumber**)exception; + +// Returns true if there's a single pending report and sets the second-level +// exception code in the out |exception_info| parameter. Returns false if +// there's a different number of pending reports. +- (bool)pendingReportExceptionInfo:(NSNumber**)exception_info; + +// Return an NSDictionary with a dictionary named "simplemap", an array named +// "vector" and an array named "objects", representing the combination of all +// modules AnnotationsSimpleMap, AnnotationsVector and AnnotationObjects +// (strings only) respectively. +- (NSDictionary*)getAnnotations; + +// Return an NSDictionary representing the ProcessSnapshotMinidump +// AnnotationsSimpleMap. +- (NSDictionary*)getProcessAnnotations; + +// Triggers an EXC_BAD_ACCESS exception and crash. +- (void)crashBadAccess; + +// Triggers a crash with a call to kill(SIGABRT). This crash runs with +// ReplaceAllocatorsWithHandlerForbidden. +- (void)crashKillAbort; + +// Trigger a crash with a __builtin_trap. This crash runs with +// ReplaceAllocatorsWithHandlerForbidden. +- (void)crashTrap; + +// Trigger a crash with an abort(). This crash runs with +// ReplaceAllocatorsWithHandlerForbidden. +- (void)crashAbort; + +// Trigger a crash with an uncaught exception. +- (void)crashException; + +// Trigger a crash with an uncaught NSException. +- (void)crashNSException; + +// Trigger a crash with an uncaught and unhandled NSException. +- (void)crashUnhandledNSException; + +// Trigger an unrecognized selector after delay. +- (void)crashUnrecognizedSelectorAfterDelay; + +// Trigger a caught NSException, this will not crash +- (void)catchNSException; + +// Trigger an NSException with sinkholes in CoreAutoLayout. +- (void)crashCoreAutoLayoutSinkhole; + +// Trigger a crash with an infinite recursion. +- (void)crashRecursion; + +// Trigger a crash dlsym that contains a crash_info message. +- (void)crashWithCrashInfoMessage; + +// Trigger an error that will to the dyld error string `_error_string` +- (void)crashWithDyldErrorString; + +// Trigger a crash after writing various annotations. +- (void)crashWithAnnotations; + +// Triggers a DumpWithoutCrash |dump_count| times in each of |threads| threads. +- (void)generateDumpWithoutCrash:(int)dump_count threads:(int)threads; + +// Triggers a simulataneous Mach exception and signal in different threads. +- (void)crashConcurrentSignalAndMach; + +// Triggers a SIGABRT signal while handling an NSException to test reentrant +// exceptions. +- (void)crashInHandlerReentrant; + +// Runs with ReplaceAllocatorsWithHandlerForbidden and allocates memory, testing +// that the handler forbidden allocator works. +- (void)allocateWithForbiddenAllocators; + +// Return the contents of the RawLog output from the previous run of the host +// application. +- (NSString*)rawLogContents; + +@end + +#endif // CRASHPAD_TEST_IOS_HOST_SHARED_OBJECT_H_ diff --git a/shared/sentry/external/crashpad/test/ios/host/handler_forbidden_allocators.cc b/shared/sentry/external/crashpad/test/ios/host/handler_forbidden_allocators.cc new file mode 100644 index 000000000..dbb499a70 --- /dev/null +++ b/shared/sentry/external/crashpad/test/ios/host/handler_forbidden_allocators.cc @@ -0,0 +1,295 @@ +// Copyright 2022 The Crashpad Authors. All rights reserved. +// +// 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 "test/ios/host/handler_forbidden_allocators.h" + +#include +#include +#include +#include + +#include "base/mac/mach_logging.h" +#include "client/crashpad_client.h" +#include "util/ios/raw_logging.h" + +namespace crashpad { +namespace test { + +namespace { + +uint64_t g_main_thread = 0; +uint64_t g_mach_exception_thread = 0; +malloc_zone_t g_old_zone; + +bool is_handler_thread() { + uint64_t thread_self; + pthread_threadid_np(pthread_self(), &thread_self); + return (thread_self == g_main_thread || + thread_self == g_mach_exception_thread); +} + +void* handler_forbidden_malloc(struct _malloc_zone_t* zone, size_t size) { + if (is_handler_thread()) { + CRASHPAD_RAW_LOG("handler_forbidden_malloc allocator used in handler."); + exit(EXIT_FAILURE); + } + return g_old_zone.malloc(zone, size); +} + +void* handler_forbidden_calloc(struct _malloc_zone_t* zone, + size_t num_items, + size_t size) { + if (is_handler_thread()) { + CRASHPAD_RAW_LOG("handler_forbidden_calloc allocator used in handler."); + exit(EXIT_FAILURE); + } + return g_old_zone.calloc(zone, num_items, size); +} + +void* handler_forbidden_valloc(struct _malloc_zone_t* zone, size_t size) { + if (is_handler_thread()) { + CRASHPAD_RAW_LOG("handler_forbidden_valloc allocator used in handler."); + exit(EXIT_FAILURE); + } + return g_old_zone.valloc(zone, size); +} + +void handler_forbidden_free(struct _malloc_zone_t* zone, void* ptr) { + if (is_handler_thread()) { + CRASHPAD_RAW_LOG("handler_forbidden_free allocator used in handler."); + exit(EXIT_FAILURE); + } + g_old_zone.free(zone, ptr); +} + +void* handler_forbidden_realloc(struct _malloc_zone_t* zone, + void* ptr, + size_t size) { + if (is_handler_thread()) { + CRASHPAD_RAW_LOG("handler_forbidden_realloc allocator used in handler."); + exit(EXIT_FAILURE); + } + return g_old_zone.realloc(zone, ptr, size); +} + +void handler_forbidden_destroy(struct _malloc_zone_t* zone) { + if (is_handler_thread()) { + CRASHPAD_RAW_LOG("handler_forbidden_destroy allocator used in handler."); + exit(EXIT_FAILURE); + } + g_old_zone.destroy(zone); +} + +void* handler_forbidden_memalign(struct _malloc_zone_t* zone, + size_t alignment, + size_t size) { + if (is_handler_thread()) { + CRASHPAD_RAW_LOG("handler_forbidden_memalign allocator used in handler."); + exit(EXIT_FAILURE); + } + return g_old_zone.memalign(zone, alignment, size); +} + +unsigned handler_forbidden_batch_malloc(struct _malloc_zone_t* zone, + size_t size, + void** results, + unsigned num_requested) { + if (is_handler_thread()) { + CRASHPAD_RAW_LOG( + "handler_forbidden_batch_malloc allocator used in handler."); + exit(EXIT_FAILURE); + } + return g_old_zone.batch_malloc(zone, size, results, num_requested); +} + +void handler_forbidden_batch_free(struct _malloc_zone_t* zone, + void** to_be_freed, + unsigned num_to_be_freed) { + if (is_handler_thread()) { + CRASHPAD_RAW_LOG("handler_forbidden_batch_free allocator used in handler."); + exit(EXIT_FAILURE); + } + g_old_zone.batch_free(zone, to_be_freed, num_to_be_freed); +} + +void handler_forbidden_free_definite_size(struct _malloc_zone_t* zone, + void* ptr, + size_t size) { + if (is_handler_thread()) { + CRASHPAD_RAW_LOG( + "handler_forbidden_free_definite_size allocator used in handler."); + exit(EXIT_FAILURE); + } + g_old_zone.free_definite_size(zone, ptr, size); +} + +size_t handler_forbidden_pressure_relief(struct _malloc_zone_t* zone, + size_t goal) { + if (is_handler_thread()) { + CRASHPAD_RAW_LOG( + "handler_forbidden_pressure_relief allocator used in handler."); + exit(EXIT_FAILURE); + } + return g_old_zone.pressure_relief(zone, goal); +} + +boolean_t handler_forbidden_claimed_address(struct _malloc_zone_t* zone, + void* ptr) { + if (is_handler_thread()) { + CRASHPAD_RAW_LOG( + "handler_forbidden_claimed_address allocator used in handler."); + exit(EXIT_FAILURE); + } + return g_old_zone.claimed_address(zone, ptr); +} + +size_t handler_forbidden_size(struct _malloc_zone_t* zone, const void* ptr) { + if (is_handler_thread()) { + CRASHPAD_RAW_LOG("handler_forbidden_size allocator used in handler."); + exit(EXIT_FAILURE); + } + return g_old_zone.size(zone, ptr); +} + +bool DeprotectMallocZone(malloc_zone_t* default_zone, + vm_address_t* reprotection_start, + vm_size_t* reprotection_length, + vm_prot_t* reprotection_value) { + mach_port_t unused; + *reprotection_start = reinterpret_cast(default_zone); + struct vm_region_basic_info_64 info; + mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64; + kern_return_t result = vm_region_64(mach_task_self(), + reprotection_start, + reprotection_length, + VM_REGION_BASIC_INFO_64, + reinterpret_cast(&info), + &count, + &unused); + if (result != KERN_SUCCESS) { + MACH_LOG(ERROR, result) << "vm_region_64"; + return false; + } + + // The kernel always returns a null object for VM_REGION_BASIC_INFO_64, but + // balance it with a deallocate in case this ever changes. See + // the VM_REGION_BASIC_INFO_64 case in vm_map_region() in 10.15's + // https://opensource.apple.com/source/xnu/xnu-6153.11.26/osfmk/vm/vm_map.c . + mach_port_deallocate(mach_task_self(), unused); + + if (!(info.max_protection & VM_PROT_WRITE)) { + LOG(ERROR) << "Invalid max_protection " << info.max_protection; + return false; + } + + // Does the region fully enclose the zone pointers? Possibly unwarranted + // simplification used: using the size of a full version 10 malloc zone rather + // than the actual smaller size if the passed-in zone is not version 10. + DCHECK_LE(*reprotection_start, reinterpret_cast(default_zone)); + vm_size_t zone_offset = reinterpret_cast(default_zone) - + reinterpret_cast(*reprotection_start); + DCHECK_LE(zone_offset + sizeof(malloc_zone_t), *reprotection_length); + + if (info.protection & VM_PROT_WRITE) { + // No change needed; the zone is already writable. + *reprotection_start = 0; + *reprotection_length = 0; + *reprotection_value = VM_PROT_NONE; + } else { + *reprotection_value = info.protection; + result = vm_protect(mach_task_self(), + *reprotection_start, + *reprotection_length, + false, + info.protection | VM_PROT_WRITE); + if (result != KERN_SUCCESS) { + MACH_LOG(ERROR, result) << "vm_protect"; + return false; + } + } + return true; +} + +void ReplaceZoneFunctions(malloc_zone_t* zone, const malloc_zone_t* functions) { + // Remove protection. + vm_address_t reprotection_start = 0; + vm_size_t reprotection_length = 0; + vm_prot_t reprotection_value = VM_PROT_NONE; + bool success = DeprotectMallocZone( + zone, &reprotection_start, &reprotection_length, &reprotection_value); + if (!success) { + return; + } + + zone->size = functions->size; + zone->malloc = functions->malloc; + zone->calloc = functions->calloc; + zone->valloc = functions->valloc; + zone->free = functions->free; + zone->realloc = functions->realloc; + zone->destroy = functions->destroy; + zone->batch_malloc = functions->batch_malloc; + zone->batch_free = functions->batch_free; + zone->introspect = functions->introspect; + zone->memalign = functions->memalign; + zone->free_definite_size = functions->free_definite_size; + zone->pressure_relief = functions->pressure_relief; + zone->claimed_address = functions->claimed_address; + + // Restore protection if it was active. + if (reprotection_start) { + kern_return_t result = vm_protect(mach_task_self(), + reprotection_start, + reprotection_length, + false, + reprotection_value); + if (result != KERN_SUCCESS) { + MACH_LOG(ERROR, result) << "vm_protect"; + return; + } + } +} + +} // namespace + +void ReplaceAllocatorsWithHandlerForbidden() { + pthread_threadid_np(pthread_self(), &g_main_thread); + + CrashpadClient crashpad_client; + g_mach_exception_thread = crashpad_client.GetThreadIdForTesting(); + + malloc_zone_t* default_zone = malloc_default_zone(); + memcpy(&g_old_zone, default_zone, sizeof(g_old_zone)); + malloc_zone_t new_functions = {}; + new_functions.size = handler_forbidden_size; + new_functions.malloc = handler_forbidden_malloc; + new_functions.calloc = handler_forbidden_calloc; + new_functions.valloc = handler_forbidden_valloc; + new_functions.free = handler_forbidden_free; + new_functions.realloc = handler_forbidden_realloc; + new_functions.destroy = handler_forbidden_destroy; + new_functions.batch_malloc = handler_forbidden_batch_malloc; + new_functions.batch_free = handler_forbidden_batch_free; + new_functions.memalign = handler_forbidden_memalign; + new_functions.free_definite_size = handler_forbidden_free_definite_size; + new_functions.pressure_relief = handler_forbidden_pressure_relief; + new_functions.claimed_address = handler_forbidden_claimed_address; + ReplaceZoneFunctions(default_zone, &new_functions); + + malloc_zone_t* purgeable_zone = malloc_default_purgeable_zone(); + ReplaceZoneFunctions(purgeable_zone, &new_functions); +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/ios/host/handler_forbidden_allocators.h b/shared/sentry/external/crashpad/test/ios/host/handler_forbidden_allocators.h new file mode 100644 index 000000000..c32a092de --- /dev/null +++ b/shared/sentry/external/crashpad/test/ios/host/handler_forbidden_allocators.h @@ -0,0 +1,31 @@ +// Copyright 2022 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_IOS_HANDLER_FORBIDDEN_ALLOCATIONS_H_ +#define CRASHPAD_TEST_IOS_HANDLER_FORBIDDEN_ALLOCATIONS_H_ + +namespace crashpad { +namespace test { + +// Override malloc_default_zone and malloc_default_purgeable_zone with functions +// that immediately exit if called from the same thread that this helper is +// called from or from the Crashpad Mach exception handler thread indicated by +// GetThreadIdForTesting. This is used to ensure the allocator is not used by +// the Crashpad InProcessHandler. +void ReplaceAllocatorsWithHandlerForbidden(); + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_IOS_HANDLER_FORBIDDEN_ALLOCATIONS_H_ diff --git a/shared/sentry/external/crashpad/test/ios/host/main.mm b/shared/sentry/external/crashpad/test/ios/host/main.mm new file mode 100644 index 000000000..a0ae5c469 --- /dev/null +++ b/shared/sentry/external/crashpad/test/ios/host/main.mm @@ -0,0 +1,30 @@ +// Copyright 2020 The Crashpad Authors. All rights reserved. +// +// 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 + +#import "test/ios/host/cptest_application_delegate.h" + +#if !defined(__has_feature) || !__has_feature(objc_arc) +#error "This file requires ARC support." +#endif + +int main(int argc, char* argv[]) { + NSString* appDelegateClassName; + @autoreleasepool { + // Setup code that might create autoreleased objects goes here. + appDelegateClassName = NSStringFromClass([CPTestApplicationDelegate class]); + } + return UIApplicationMain(argc, argv, nil, appDelegateClassName); +} diff --git a/shared/sentry/external/crashpad/test/linux/fake_ptrace_connection.cc b/shared/sentry/external/crashpad/test/linux/fake_ptrace_connection.cc new file mode 100644 index 000000000..33a8f08b6 --- /dev/null +++ b/shared/sentry/external/crashpad/test/linux/fake_ptrace_connection.cc @@ -0,0 +1,105 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "test/linux/fake_ptrace_connection.h" + +#include + +#include "base/notreached.h" +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "util/file/file_io.h" + +namespace crashpad { +namespace test { + +FakePtraceConnection::FakePtraceConnection() + : PtraceConnection(), + attachments_(), + pid_(-1), + is_64_bit_(false), + initialized_() {} + +FakePtraceConnection::~FakePtraceConnection() {} + +bool FakePtraceConnection::Initialize(pid_t pid) { + INITIALIZATION_STATE_SET_INITIALIZING(initialized_); + + if (!Attach(pid)) { + return false; + } + pid_ = pid; + +#if defined(ARCH_CPU_64_BITS) + is_64_bit_ = true; +#else + is_64_bit_ = false; +#endif + + INITIALIZATION_STATE_SET_VALID(initialized_); + return true; +} + +pid_t FakePtraceConnection::GetProcessID() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return pid_; +} + +bool FakePtraceConnection::Attach(pid_t tid) { + bool inserted = attachments_.insert(tid).second; + EXPECT_TRUE(inserted); + return inserted; +} + +bool FakePtraceConnection::Is64Bit() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return is_64_bit_; +} + +bool FakePtraceConnection::GetThreadInfo(pid_t tid, ThreadInfo* info) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + bool attached = attachments_.find(tid) != attachments_.end(); + EXPECT_TRUE(attached); + return attached; +} + +bool FakePtraceConnection::ReadFileContents(const base::FilePath& path, + std::string* contents) { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + return LoggingReadEntireFile(path, contents); +} + +ProcessMemoryLinux* FakePtraceConnection::Memory() { + INITIALIZATION_STATE_DCHECK_VALID(initialized_); + if (!memory_) { + memory_ = std::make_unique(this); + } + return memory_.get(); +} + +bool FakePtraceConnection::Threads(std::vector* threads) { + // TODO(jperaza): Implement this if/when it's needed. + NOTREACHED(); + return false; +} + +ssize_t FakePtraceConnection::ReadUpTo(VMAddress address, + size_t size, + void* buffer) { + NOTREACHED(); + return false; +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/linux/fake_ptrace_connection.h b/shared/sentry/external/crashpad/test/linux/fake_ptrace_connection.h new file mode 100644 index 000000000..f6ba7de3e --- /dev/null +++ b/shared/sentry/external/crashpad/test/linux/fake_ptrace_connection.h @@ -0,0 +1,85 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_LINUX_FAKE_PTRACE_CONNECTION_H_ +#define CRASHPAD_TEST_LINUX_FAKE_PTRACE_CONNECTION_H_ + +#include + +#include +#include + +#include "util/linux/ptrace_connection.h" +#include "util/misc/initialization_state_dcheck.h" +#include "util/process/process_memory_linux.h" + +namespace crashpad { +namespace test { + +//! \brief Stands-in where real PtraceConnections aren't available. +//! +//! This class performs basic EXPECTs that it is used correctly, but does not +//! execute any real `ptrace` calls or attachments. +class FakePtraceConnection : public PtraceConnection { + public: + FakePtraceConnection(); + + FakePtraceConnection(const FakePtraceConnection&) = delete; + FakePtraceConnection& operator=(const FakePtraceConnection&) = delete; + + ~FakePtraceConnection(); + + //! \brief Initializes this connection for the process whose process ID is + //! \a pid. + //! + //! \param[in] pid The process ID of the process to connect to. + //! \return `true` on success. `false` on failure with a message logged. + bool Initialize(pid_t pid); + + // PtraceConnection: + + pid_t GetProcessID() override; + bool Attach(pid_t tid) override; + + //! \brief Returns `true` if the current process is 64-bit. + bool Is64Bit() override; + + //! \brief Does not modify \a info. + bool GetThreadInfo(pid_t tid, ThreadInfo* info) override; + + bool ReadFileContents(const base::FilePath& path, + std::string* contents) override; + + //! \brief Attempts to create a ProcessMemory when called, calling + //! ADD_FAILURE() and returning `nullptr` on failure. + ProcessMemoryLinux* Memory() override; + + //! \todo Not yet implemented. + bool Threads(std::vector* threads) override; + + //! \\todo Not yet implemented. + ssize_t ReadUpTo(VMAddress address, size_t size, void* buffer) override; + + private: + std::set attachments_; + std::unique_ptr memory_; + pid_t pid_; + bool is_64_bit_; + InitializationStateDcheck initialized_; +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_LINUX_FAKE_PTRACE_CONNECTION_H_ diff --git a/shared/sentry/external/crashpad/test/linux/get_tls.cc b/shared/sentry/external/crashpad/test/linux/get_tls.cc new file mode 100644 index 000000000..452724dea --- /dev/null +++ b/shared/sentry/external/crashpad/test/linux/get_tls.cc @@ -0,0 +1,59 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "test/linux/get_tls.h" + +#include "build/build_config.h" +#include "util/misc/from_pointer_cast.h" + +namespace crashpad { +namespace test { + +LinuxVMAddress GetTLS() { + LinuxVMAddress tls; +#if defined(ARCH_CPU_ARMEL) + // 0xffff0fe0 is the address of the kernel user helper __kuser_get_tls(). + auto kuser_get_tls = reinterpret_cast(0xffff0fe0); + tls = FromPointerCast(kuser_get_tls()); +#elif defined(ARCH_CPU_ARM64) + // Linux/aarch64 places the tls address in system register tpidr_el0. + asm("mrs %0, tpidr_el0" : "=r"(tls)); +#elif defined(ARCH_CPU_X86) + uint32_t tls_32; + asm("movl %%gs:0x0, %0" : "=r"(tls_32)); + tls = tls_32; +#elif defined(ARCH_CPU_X86_64) + asm("movq %%fs:0x0, %0" : "=r"(tls)); +#elif defined(ARCH_CPU_MIPSEL) + uint32_t tls_32; + asm("rdhwr $3,$29\n\t" + "move %0,$3\n\t" + : "=r"(tls_32) + : + : "$3"); + tls = tls_32; +#elif defined(ARCH_CPU_MIPS64EL) + asm("rdhwr $3,$29\n\t" + "move %0,$3\n\t" + : "=r"(tls) + : + : "$3"); +#else +#error Port. +#endif // ARCH_CPU_ARMEL + return tls; +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/linux/get_tls.h b/shared/sentry/external/crashpad/test/linux/get_tls.h new file mode 100644 index 000000000..5d59144e0 --- /dev/null +++ b/shared/sentry/external/crashpad/test/linux/get_tls.h @@ -0,0 +1,29 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_LINUX_GET_TLS_H_ +#define CRASHPAD_TEST_LINUX_GET_TLS_H_ + +#include "util/linux/address_types.h" + +namespace crashpad { +namespace test { + +//! \brief Return the thread-local storage address for the current thread. +LinuxVMAddress GetTLS(); + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_LINUX_GET_TLS_H_ diff --git a/shared/sentry/external/crashpad/test/mac/dyld.cc b/shared/sentry/external/crashpad/test/mac/dyld.cc new file mode 100644 index 000000000..ba1cb56f5 --- /dev/null +++ b/shared/sentry/external/crashpad/test/mac/dyld.cc @@ -0,0 +1,99 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "test/mac/dyld.h" + +#include +#include +#include +#include +#include + +#include "base/logging.h" +#include "snapshot/mac/process_reader_mac.h" +#include "test/scoped_module_handle.h" +#include "util/numeric/safe_assignment.h" + +#if __MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_13 +extern "C" { + +// A non-public dyld API, declared in 10.12.4 +// dyld-433.5/include/mach-o/dyld_priv.h. The code still exists in 10.13, but +// its symbol is no longer public, so it can’t be used there. +const dyld_all_image_infos* _dyld_get_all_image_infos() + __attribute__((weak_import)); + +} // extern "C" +#endif + +namespace crashpad { +namespace test { + +const dyld_all_image_infos* DyldGetAllImageInfos() { +#if __MAC_OS_X_VERSION_MAX_ALLOWED < __MAC_10_13 + // When building with the pre-10.13 SDK, the weak_import declaration above is + // available and a symbol will be present in the SDK to link against. If the + // old interface is also available at run time (running on pre-10.13), use it. + if (_dyld_get_all_image_infos) { + return _dyld_get_all_image_infos(); + } +#elif __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_13 + // When building with the 10.13 SDK or later, but able to run on pre-10.13, + // look for _dyld_get_all_image_infos in the same module that provides + // _dyld_image_count. There’s no symbol in the SDK to link against, so this is + // a little more involved than the pre-10.13 SDK case above. + Dl_info dli; + if (!dladdr(reinterpret_cast(_dyld_image_count), &dli)) { + LOG(WARNING) << "dladdr: failed"; + } else { + ScopedModuleHandle module( + dlopen(dli.dli_fname, RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD)); + if (!module.valid()) { + LOG(WARNING) << "dlopen: " << dlerror(); + } else { + using DyldGetAllImageInfosType = const dyld_all_image_infos*(*)(); + const auto _dyld_get_all_image_infos = + module.LookUpSymbol( + "_dyld_get_all_image_infos"); + if (_dyld_get_all_image_infos) { + return _dyld_get_all_image_infos(); + } + } + } +#endif + + // On 10.13 and later, do it the hard way. + ProcessReaderMac process_reader; + if (!process_reader.Initialize(mach_task_self())) { + return nullptr; + } + + mach_vm_address_t all_image_info_addr_m = + process_reader.DyldAllImageInfo(nullptr); + if (!all_image_info_addr_m) { + return nullptr; + } + + uintptr_t all_image_info_addr_u; + if (!AssignIfInRange(&all_image_info_addr_u, all_image_info_addr_m)) { + LOG(ERROR) << "all_image_info_addr_m " << all_image_info_addr_m + << " out of range"; + return nullptr; + } + + return reinterpret_cast(all_image_info_addr_u); +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/mac/dyld.h b/shared/sentry/external/crashpad/test/mac/dyld.h new file mode 100644 index 000000000..0a688f3e1 --- /dev/null +++ b/shared/sentry/external/crashpad/test/mac/dyld.h @@ -0,0 +1,33 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_MAC_DYLD_H_ +#define CRASHPAD_TEST_MAC_DYLD_H_ + +#include + +namespace crashpad { +namespace test { + +//! \brief Calls or emulates the `_dyld_get_all_image_infos()` private/internal +//! function. +//! +//! \return A pointer to this process’ dyld_all_image_infos structure, or +//! `nullptr` on failure with a message logged. +const dyld_all_image_infos* DyldGetAllImageInfos(); + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_MAC_DYLD_H_ diff --git a/shared/sentry/external/crashpad/test/mac/exception_swallower.cc b/shared/sentry/external/crashpad/test/mac/exception_swallower.cc new file mode 100644 index 000000000..3098643bd --- /dev/null +++ b/shared/sentry/external/crashpad/test/mac/exception_swallower.cc @@ -0,0 +1,195 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "test/mac/exception_swallower.h" + +#include +#include +#include + +#include + +#include "base/check_op.h" +#include "base/mac/scoped_mach_port.h" +#include "base/strings/stringprintf.h" +#include "handler/mac/exception_handler_server.h" +#include "util/mach/bootstrap.h" +#include "util/mach/exc_server_variants.h" +#include "util/mach/exception_ports.h" +#include "util/mach/mach_extensions.h" +#include "util/misc/random_string.h" +#include "util/thread/thread.h" + +namespace crashpad { +namespace test { + +namespace { + +constexpr char kServiceEnvironmentVariable[] = + "CRASHPAD_EXCEPTION_SWALLOWER_SERVICE"; + +ExceptionSwallower* g_exception_swallower; + +// Like getenv(), but fails a CHECK() if the underlying function fails. It’s not +// considered a failure for |name| to be unset in the environment. In that case, +// nullptr is returned. +const char* CheckedGetenv(const char* name) { + errno = 0; + const char* value; + PCHECK((value = getenv(name)) || errno == 0) << "getenv"; + return value; +} + +} // namespace + +class ExceptionSwallower::ExceptionSwallowerThread + : public Thread, + public UniversalMachExcServer::Interface { + public: + explicit ExceptionSwallowerThread( + base::mac::ScopedMachReceiveRight receive_right) + : Thread(), + UniversalMachExcServer::Interface(), + exception_handler_server_(std::move(receive_right), true), + pid_(getpid()) { + Start(); + } + + ExceptionSwallowerThread(const ExceptionSwallowerThread&) = delete; + ExceptionSwallowerThread& operator=(const ExceptionSwallowerThread&) = delete; + + ~ExceptionSwallowerThread() override {} + + void Stop() { exception_handler_server_.Stop(); } + + // Returns the process ID that the thread is running in. This is used to + // detect misuses that place the exception swallower server thread and code + // that wants its exceptions swallowed in the same process. + pid_t ProcessID() const { return pid_; } + + private: + // Thread: + + void ThreadMain() override { exception_handler_server_.Run(this); } + + // UniversalMachExcServer::Interface: + + kern_return_t CatchMachException(exception_behavior_t behavior, + exception_handler_t exception_port, + thread_t thread, + task_t task, + exception_type_t exception, + const mach_exception_data_type_t* code, + mach_msg_type_number_t code_count, + thread_state_flavor_t* flavor, + ConstThreadState old_state, + mach_msg_type_number_t old_state_count, + thread_state_t new_state, + mach_msg_type_number_t* new_state_count, + const mach_msg_trailer_t* trailer, + bool* destroy_complex_request) override { + *destroy_complex_request = true; + + // Swallow. + + ExcServerCopyState( + behavior, old_state, old_state_count, new_state, new_state_count); + return ExcServerSuccessfulReturnValue(exception, behavior, false); + } + + ExceptionHandlerServer exception_handler_server_; + pid_t pid_; +}; + +ExceptionSwallower::ExceptionSwallower() : exception_swallower_thread_() { + CHECK(!g_exception_swallower); + g_exception_swallower = this; + + if (CheckedGetenv(kServiceEnvironmentVariable)) { + // The environment variable is already set, so just proceed with the + // existing service. This normally happens when the Google Test “threadsafe†+ // death test style is chosen, because the test child process will + // re-execute code already run in the test parent process. See + // https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md#death-test-styles. + return; + } + + std::string service_name = + base::StringPrintf("org.chromium.crashpad.test.exception_swallower.%d.%s", + getpid(), + RandomString().c_str()); + base::mac::ScopedMachReceiveRight receive_right( + BootstrapCheckIn(service_name)); + CHECK(receive_right.is_valid()); + + exception_swallower_thread_.reset( + new ExceptionSwallowerThread(std::move(receive_right))); + + PCHECK(setenv(kServiceEnvironmentVariable, service_name.c_str(), 1) == 0) + << "setenv"; +} + +ExceptionSwallower::~ExceptionSwallower() { + PCHECK(unsetenv(kServiceEnvironmentVariable) == 0) << "unsetenv"; + + exception_swallower_thread_->Stop(); + exception_swallower_thread_->Join(); + + CHECK_EQ(g_exception_swallower, this); + g_exception_swallower = nullptr; +} + +// static +void ExceptionSwallower::SwallowExceptions() { + // The exception swallower thread can’t be in this process, because the + // EXC_CRASH or EXC_CORPSE_NOTIFY exceptions that it needs to swallow will be + // delivered after a crash has occurred and none of its threads will be + // scheduled to run. + CHECK(!g_exception_swallower || + !g_exception_swallower->exception_swallower_thread_ || + g_exception_swallower->exception_swallower_thread_->ProcessID() != + getpid()); + + const char* service_name = CheckedGetenv(kServiceEnvironmentVariable); + CHECK(service_name); + + base::mac::ScopedMachSendRight exception_swallower_port( + BootstrapLookUp(service_name)); + CHECK(exception_swallower_port.is_valid()); + + ExceptionPorts task_exception_ports(ExceptionPorts::kTargetTypeTask, + TASK_NULL); + + // The mask is similar to the one used by CrashpadClient::UseHandler(), but + // EXC_CORPSE_NOTIFY is added. This is done for the benefit of tests that + // crash intentionally with their own custom exception port set for EXC_CRASH. + // In that case, depending on the actions taken by the EXC_CRASH handler, the + // exception may be transformed by the kernel into an EXC_CORPSE_NOTIFY, which + // would be sent to an EXC_CORPSE_NOTIFY handler, normally the system’s crash + // reporter at the task or host level. See 10.13.0 + // xnu-4570.1.46/bsd/kern/kern_exit.c proc_prepareexit(). Swallowing + // EXC_CORPSE_NOTIFY at the task level prevents such exceptions from reaching + // the system’s crash reporter. + CHECK(task_exception_ports.SetExceptionPort( + (EXC_MASK_CRASH | + EXC_MASK_RESOURCE | + EXC_MASK_GUARD | + EXC_MASK_CORPSE_NOTIFY) & ExcMaskValid(), + exception_swallower_port.get(), + EXCEPTION_DEFAULT, + THREAD_STATE_NONE)); +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/mac/exception_swallower.h b/shared/sentry/external/crashpad/test/mac/exception_swallower.h new file mode 100644 index 000000000..ac1456207 --- /dev/null +++ b/shared/sentry/external/crashpad/test/mac/exception_swallower.h @@ -0,0 +1,87 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_MAC_EXCEPTION_SWALLOWER_H_ +#define CRASHPAD_TEST_MAC_EXCEPTION_SWALLOWER_H_ + +#include + + +namespace crashpad { +namespace test { + +//! \brief Swallows `EXC_CRASH` and `EXC_CORPSE_NOTIFY` exceptions in test child +//! processes. +//! +//! This class is intended to be used by test code that crashes intentionally. +//! +//! On macOS, the system’s crash reporter normally saves crash reports for all +//! crashes in test code, by virtue of being set as the `EXC_CRASH` or +//! `EXC_CORPSE_NOTIFY` handler. This litters the user’s +//! `~/Library/Logs/DiagnosticReports` directory and can be time-consuming. +//! Reports generated for code that crashes intentionally have no value, and +//! many Crashpad tests do crash intentionally. +//! +//! Instantiate an ExceptionSwallower object in a parent test process (a process +//! where `TEST()`, `TEST_F()`, and `TEST_P()` execute) to create an exception +//! swallower server running on a dedicated thread. A service mapping for this +//! server will be published with the bootstrap server and made available in the +//! `CRASHPAD_EXCEPTION_SWALLOWER_SERVICE` environment variable. In a child +//! process, call SwallowExceptions() to look up this service and set it as the +//! `EXC_CRASH` and `EXC_CORPSE_NOTIFY` handler. When these exceptions are +//! raised in the child process, they’ll be handled by the exception swallower +//! server, which performs no action but reports that exceptions were +//! successfully handled so that the system’s crash reporter, ReportCrash, will +//! not be invoked. +//! +//! At most one ExceptionSwallower may be instantiated in a process at a time. +//! If `CRASHPAD_EXCEPTION_SWALLOWER_SERVICE` is already set, ExceptionSwallower +//! leaves it in place and takes no additional action. +//! +//! Crashpad’s ASSERT_DEATH_CRASH(), EXPECT_DEATH_CRASH(), ASSERT_DEATH_CHECK(), +//! and EXPECT_DEATH_CHECK() macros make use of this class on macOS, as does the +//! Multiprocess test interface. +class ExceptionSwallower { + public: + ExceptionSwallower(); + + ExceptionSwallower(const ExceptionSwallower&) = delete; + ExceptionSwallower& operator=(const ExceptionSwallower&) = delete; + + ~ExceptionSwallower(); + + //! \brief In a test child process, arranges to swallow `EXC_CRASH` and + //! `EXC_CORPSE_NOTIFY` exceptions. + //! + //! This must be called in a test child process. It must not be called from a + //! parent test process directly. Parent test processes are those that execute + //! `TEST()`, `TEST_F()`, and `TEST_P()`. Test child processes execute + //! ASSERT_DEATH_CRASH(), EXPECT_DEATH_CRASH(), ASSERT_DEATH_CHECK(), + //! EXPECT_DEATH_CHECK(), and Multiprocess::RunChild(). + //! + //! It is an error to call this in a test child process without having first + //! instantiated an ExceptionSwallower object in a parent test project. It is + //! also an error to call this in a parent test process. + static void SwallowExceptions(); + + private: + class ExceptionSwallowerThread; + + std::unique_ptr exception_swallower_thread_; +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_MAC_EXCEPTION_SWALLOWER_H_ diff --git a/shared/sentry/external/crashpad/test/mac/mach_errors.cc b/shared/sentry/external/crashpad/test/mac/mach_errors.cc new file mode 100644 index 000000000..db8ed6661 --- /dev/null +++ b/shared/sentry/external/crashpad/test/mac/mach_errors.cc @@ -0,0 +1,52 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "test/mac/mach_errors.h" + +#include "base/strings/stringprintf.h" + +namespace { + +std::string FormatBase(const std::string& base) { + if (base.empty()) { + return std::string(); + } + + return base::StringPrintf("%s: ", base.c_str()); +} + +std::string FormatMachErrorNumber(mach_error_t mach_err) { + // For the os/kern subsystem, give the error number in decimal as in + // . Otherwise, give it in hexadecimal to make it easier + // to visualize the various bits. See . + if (mach_err >= 0 && mach_err < KERN_RETURN_MAX) { + return base::StringPrintf(" (%d)", mach_err); + } + return base::StringPrintf(" (0x%08x)", mach_err); +} + +} // namespace + +namespace crashpad { +namespace test { + +std::string MachErrorMessage(mach_error_t mach_err, const std::string& base) { + return base::StringPrintf("%s%s%s", + FormatBase(base).c_str(), + mach_error_string(mach_err), + FormatMachErrorNumber(mach_err).c_str()); +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/mac/mach_errors.h b/shared/sentry/external/crashpad/test/mac/mach_errors.h new file mode 100644 index 000000000..19217d114 --- /dev/null +++ b/shared/sentry/external/crashpad/test/mac/mach_errors.h @@ -0,0 +1,53 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_MAC_MACH_ERRORS_H_ +#define CRASHPAD_TEST_MAC_MACH_ERRORS_H_ + +#include + +#include + +namespace crashpad { +namespace test { + +// This function formats messages in a similar way to the Mach error logging +// macros in base/mac/mach_logging.h. It exists to interoperate with Google Test +// assertions, which don’t interoperate with logging but can be streamed to. +// +// Where non-test code could do: +// MACH_CHECK(kr == KERN_SUCCESS, kr) << "vm_deallocate"; +// Google Test-based test code can do: +// EXPECT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "vm_deallocate"); + +//! \brief Formats a Mach error message. +//! +//! The returned string will combine the \a base string, if supplied, with a +//! textual and numeric description of the error. +//! +//! \param[in] mach_err The Mach error code, which may be a `kern_return_t` or +//! related type. +//! \param[in] base A string to prepend to the error description. +//! +//! \return A string of the format `"(os/kern) invalid address (1)"` if \a +//! mach_err has the value `KERN_INVALID_ADDRESS` on a system where this is +//! defined to be 1. If \a base is not empty, it will be prepended to this +//! string, separated by a colon. +std::string MachErrorMessage(mach_error_t mach_err, + const std::string& base = std::string()); + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_MAC_MACH_ERRORS_H_ diff --git a/shared/sentry/external/crashpad/test/mac/mach_multiprocess.cc b/shared/sentry/external/crashpad/test/mac/mach_multiprocess.cc new file mode 100644 index 000000000..5f1b3aca2 --- /dev/null +++ b/shared/sentry/external/crashpad/test/mac/mach_multiprocess.cc @@ -0,0 +1,271 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "test/mac/mach_multiprocess.h" + +#include +#include + +#include +#include +#include + +#include "base/auto_reset.h" +#include "base/mac/scoped_mach_port.h" +#include "gtest/gtest.h" +#include "test/errors.h" +#include "test/mac/mach_errors.h" +#include "util/file/file_io.h" +#include "util/mach/bootstrap.h" +#include "util/mach/mach_extensions.h" +#include "util/mach/mach_message.h" +#include "util/misc/implicit_cast.h" +#include "util/misc/random_string.h" +#include "util/misc/scoped_forbid_return.h" + +namespace { + +// The “hello†message contains a send right to the child process’ task port. +struct SendHelloMessage : public mach_msg_base_t { + mach_msg_port_descriptor_t port_descriptor; +}; + +struct ReceiveHelloMessage : public SendHelloMessage { + union { + mach_msg_trailer_t trailer; + mach_msg_audit_trailer_t audit_trailer; + }; +}; + +} // namespace + +namespace crashpad { +namespace test { + +namespace internal { + +struct MachMultiprocessInfo { + MachMultiprocessInfo() + : service_name(), + local_port(MACH_PORT_NULL), + remote_port(MACH_PORT_NULL), + child_task(TASK_NULL) { + } + + std::string service_name; + base::mac::ScopedMachReceiveRight local_port; + base::mac::ScopedMachSendRight remote_port; + base::mac::ScopedMachSendRight child_task; // valid only in parent +}; + +} // namespace internal + +MachMultiprocess::MachMultiprocess() : Multiprocess(), info_(nullptr) { +} + +void MachMultiprocess::Run() { + ASSERT_EQ(info_, nullptr); + std::unique_ptr info( + new internal::MachMultiprocessInfo); + base::AutoReset reset_info(&info_, + info.get()); + + return Multiprocess::Run(); +} + +MachMultiprocess::~MachMultiprocess() { +} + +void MachMultiprocess::PreFork() { + ASSERT_NO_FATAL_FAILURE(Multiprocess::PreFork()); + + // Set up the parent port and register it with the bootstrap server before + // forking, so that it’s guaranteed to be there when the child attempts to + // look it up. + info_->service_name = "org.chromium.crashpad.test.mach_multiprocess."; + info_->service_name.append(RandomString()); + + info_->local_port = BootstrapCheckIn(info_->service_name); + ASSERT_TRUE(info_->local_port.is_valid()); +} + +mach_port_t MachMultiprocess::LocalPort() const { + EXPECT_TRUE(info_->local_port.is_valid()); + return info_->local_port.get(); +} + +mach_port_t MachMultiprocess::RemotePort() const { + EXPECT_TRUE(info_->remote_port.is_valid()); + return info_->remote_port.get(); +} + +task_t MachMultiprocess::ChildTask() const { + EXPECT_TRUE(info_->child_task.is_valid()); + return info_->child_task.get(); +} + +void MachMultiprocess::MultiprocessParent() { + ReceiveHelloMessage message = {}; + + kern_return_t kr = mach_msg(&message.header, + MACH_RCV_MSG | kMachMessageReceiveAuditTrailer, + 0, + sizeof(message), + info_->local_port.get(), + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + ASSERT_EQ(kr, MACH_MSG_SUCCESS) << MachErrorMessage(kr, "mach_msg"); + + // Comb through the entire message, checking every field against its expected + // value. + EXPECT_EQ(message.header.msgh_bits, + MACH_MSGH_BITS(MACH_MSG_TYPE_MOVE_SEND, MACH_MSG_TYPE_MOVE_SEND) | + MACH_MSGH_BITS_COMPLEX); + ASSERT_EQ(message.header.msgh_size, sizeof(SendHelloMessage)); + EXPECT_EQ(message.header.msgh_local_port, info_->local_port); + ASSERT_EQ(message.body.msgh_descriptor_count, 1u); + EXPECT_EQ(message.port_descriptor.disposition, + implicit_cast(MACH_MSG_TYPE_MOVE_SEND)); + ASSERT_EQ( + message.port_descriptor.type, + implicit_cast(MACH_MSG_PORT_DESCRIPTOR)); + ASSERT_EQ(message.audit_trailer.msgh_trailer_type, + implicit_cast(MACH_MSG_TRAILER_FORMAT_0)); + ASSERT_EQ(message.audit_trailer.msgh_trailer_size, + sizeof(message.audit_trailer)); + EXPECT_EQ(message.audit_trailer.msgh_seqno, 0u); + + // Check the audit trailer’s values for sanity. This is a little bit of + // overkill, but because the service was registered with the bootstrap server + // and other processes will be able to look it up and send messages to it, + // these checks disambiguate genuine failures later on in the test from those + // that would occur if an errant process sends a message to this service. +#if __MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_8 + uid_t audit_auid; + uid_t audit_euid; + gid_t audit_egid; + uid_t audit_ruid; + gid_t audit_rgid; + pid_t audit_pid; + au_asid_t audit_asid; + audit_token_to_au32(message.audit_trailer.msgh_audit, + &audit_auid, + &audit_euid, + &audit_egid, + &audit_ruid, + &audit_rgid, + &audit_pid, + &audit_asid, + nullptr); +#else + uid_t audit_auid = audit_token_to_auid(message.audit_trailer.msgh_audit); + uid_t audit_euid = audit_token_to_euid(message.audit_trailer.msgh_audit); + gid_t audit_egid = audit_token_to_egid(message.audit_trailer.msgh_audit); + uid_t audit_ruid = audit_token_to_ruid(message.audit_trailer.msgh_audit); + gid_t audit_rgid = audit_token_to_rgid(message.audit_trailer.msgh_audit); + pid_t audit_pid = audit_token_to_pid(message.audit_trailer.msgh_audit); + au_asid_t audit_asid = audit_token_to_asid(message.audit_trailer.msgh_audit); +#endif + EXPECT_EQ(audit_euid, geteuid()); + EXPECT_EQ(audit_egid, getegid()); + EXPECT_EQ(audit_ruid, getuid()); + EXPECT_EQ(audit_rgid, getgid()); + ASSERT_EQ(audit_pid, ChildPID()); + + ASSERT_EQ(AuditPIDFromMachMessageTrailer(&message.trailer), ChildPID()); + + auditinfo_addr_t audit_info; + int rv = getaudit_addr(&audit_info, sizeof(audit_info)); + ASSERT_EQ(rv, 0) << ErrnoMessage("getaudit_addr"); + EXPECT_EQ(audit_auid, audit_info.ai_auid); + EXPECT_EQ(audit_asid, audit_info.ai_asid); + + // Retrieve the remote port from the message header, and the child’s task port + // from the message body. + info_->remote_port.reset(message.header.msgh_remote_port); + info_->child_task.reset(message.port_descriptor.name); + + // Verify that the child’s task port is what it purports to be. + int mach_pid; + kr = pid_for_task(info_->child_task.get(), &mach_pid); + ASSERT_EQ(kr, KERN_SUCCESS) << MachErrorMessage(kr, "pid_for_task"); + ASSERT_EQ(mach_pid, ChildPID()); + + MachMultiprocessParent(); + + info_->remote_port.reset(); + info_->local_port.reset(); +} + +void MachMultiprocess::MultiprocessChild() { + ScopedForbidReturn forbid_return; + + // local_port is not valid in the forked child process. + std::ignore = info_->local_port.release(); + + info_->local_port.reset(NewMachPort(MACH_PORT_RIGHT_RECEIVE)); + ASSERT_NE(info_->local_port, kMachPortNull); + + // The remote port can be obtained from the bootstrap server. + info_->remote_port = BootstrapLookUp(info_->service_name); + ASSERT_NE(info_->remote_port, kMachPortNull); + + // The “hello†message will provide the parent with its remote port, a send + // right to the child task’s local port receive right. It will also carry a + // send right to the child task’s task port. + SendHelloMessage message = {}; + message.header.msgh_bits = + MACH_MSGH_BITS(MACH_MSG_TYPE_COPY_SEND, MACH_MSG_TYPE_MAKE_SEND) | + MACH_MSGH_BITS_COMPLEX; + message.header.msgh_size = sizeof(message); + message.header.msgh_remote_port = info_->remote_port.get(); + message.header.msgh_local_port = info_->local_port.get(); + message.body.msgh_descriptor_count = 1; + message.port_descriptor.name = mach_task_self(); + message.port_descriptor.disposition = MACH_MSG_TYPE_COPY_SEND; + message.port_descriptor.type = MACH_MSG_PORT_DESCRIPTOR; + + kern_return_t kr = mach_msg(&message.header, + MACH_SEND_MSG, + message.header.msgh_size, + 0, + MACH_PORT_NULL, + MACH_MSG_TIMEOUT_NONE, + MACH_PORT_NULL); + ASSERT_EQ(kr, MACH_MSG_SUCCESS) << MachErrorMessage(kr, "mach_msg"); + + MachMultiprocessChild(); + + info_->remote_port.reset(); + info_->local_port.reset(); + + // Close the write pipe now, for cases where the parent is waiting on it to + // be closed as an indication that the child has finished. + CloseWritePipe(); + + // Wait for the parent process to close its end of the pipe. The child process + // needs to remain alive until then because the parent process will attempt to + // verify it using the task port it has access to via ChildTask(). + CheckedReadFileAtEOF(ReadPipeHandle()); + + if (testing::Test::HasFailure()) { + // Trigger the ScopedForbidReturn destructor. + return; + } + + forbid_return.Disarm(); +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/mac/mach_multiprocess.h b/shared/sentry/external/crashpad/test/mac/mach_multiprocess.h new file mode 100644 index 000000000..b3b0bb1b6 --- /dev/null +++ b/shared/sentry/external/crashpad/test/mac/mach_multiprocess.h @@ -0,0 +1,120 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_MAC_MACH_MULTIPROCESS_H_ +#define CRASHPAD_TEST_MAC_MACH_MULTIPROCESS_H_ + +#include +#include + +#include "test/multiprocess.h" + +namespace crashpad { +namespace test { + +namespace internal { +struct MachMultiprocessInfo; +} // namespace internal + +//! \brief Manages a Mach-aware multiprocess test. +//! +//! This is similar to the base Multiprocess test, but adds Mach features. The +//! parent process has access to the child process’ task port. The parent and +//! child processes are able to communicate via Mach IPC: each process has a +//! receive right to its “local port†and a send right to a “remote portâ€, and +//! messages sent to the remote port in one process can be received on the local +//! port in the partner process. +//! +//! Subclasses are expected to implement the parent and child by overriding the +//! appropriate methods. +class MachMultiprocess : public Multiprocess { + public: + MachMultiprocess(); + + MachMultiprocess(const MachMultiprocess&) = delete; + MachMultiprocess& operator=(const MachMultiprocess&) = delete; + + void Run(); + + protected: + ~MachMultiprocess(); + + // Multiprocess: + void PreFork() override; + + //! \brief Returns a receive right for the local port. + //! + //! This method may be called by either the parent or the child process. It + //! returns a receive right, with a corresponding send right held in the + //! opposing process. + mach_port_t LocalPort() const; + + //! \brief Returns a send right for the remote port. + //! + //! This method may be called by either the parent or the child process. It + //! returns a send right, with the corresponding receive right held in the + //! opposing process. + mach_port_t RemotePort() const; + + //! \brief Returns a send right for the child’s task port. + //! + //! This method may only be called by the parent process. + task_t ChildTask() const; + + private: + // Multiprocess: + + //! \brief Runs the parent side of the test. + //! + //! This method establishes the parent’s environment and calls + //! MachMultiprocessParent(). + //! + //! Subclasses must override MachMultiprocessParent() instead of this method. + void MultiprocessParent() final; + + //! \brief Runs the child side of the test. + //! + //! This method establishes the child’s environment and calls + //! MachMultiprocessChild(). If any failure (via fatal or nonfatal Google Test + //! assertion) is detected, the child will exit with a failure status. + //! + //! Subclasses must override MachMultiprocessChild() instead of this method. + void MultiprocessChild() final; + + //! \brief The subclass-provided parent routine. + //! + //! Test failures should be reported via Google Test: `EXPECT_*()`, + //! `ASSERT_*()`, `FAIL()`, etc. + //! + //! This method must not use a `wait()`-family system call to wait for the + //! child process to exit, as this is handled by the superclass. + //! + //! Subclasses must implement this method to define how the parent operates. + virtual void MachMultiprocessParent() = 0; + + //! \brief The subclass-provided child routine. + //! + //! Test failures should be reported via Google Test: `EXPECT_*()`, + //! `ASSERT_*()`, `FAIL()`, etc. + //! + //! Subclasses must implement this method to define how the child operates. + virtual void MachMultiprocessChild() = 0; + + internal::MachMultiprocessInfo* info_; +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_MAC_MACH_MULTIPROCESS_H_ diff --git a/shared/sentry/external/crashpad/test/mac/mach_multiprocess_test.cc b/shared/sentry/external/crashpad/test/mac/mach_multiprocess_test.cc new file mode 100644 index 000000000..455249bde --- /dev/null +++ b/shared/sentry/external/crashpad/test/mac/mach_multiprocess_test.cc @@ -0,0 +1,49 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "test/mac/mach_multiprocess.h" + +#include + +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +class TestMachMultiprocess final : public MachMultiprocess { + public: + TestMachMultiprocess() : MachMultiprocess() {} + + TestMachMultiprocess(const TestMachMultiprocess&) = delete; + TestMachMultiprocess& operator=(const TestMachMultiprocess&) = delete; + + ~TestMachMultiprocess() {} + + private: + // MachMultiprocess will have already exercised the Mach ports for IPC and the + // child task port. + void MachMultiprocessParent() override {} + + void MachMultiprocessChild() override {} +}; + +TEST(MachMultiprocess, MachMultiprocess) { + TestMachMultiprocess mach_multiprocess; + mach_multiprocess.Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/main_arguments.cc b/shared/sentry/external/crashpad/test/main_arguments.cc new file mode 100644 index 000000000..95221b2e8 --- /dev/null +++ b/shared/sentry/external/crashpad/test/main_arguments.cc @@ -0,0 +1,38 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "test/main_arguments.h" + +#include "base/check.h" + +namespace crashpad { +namespace test { + +const std::vector* g_arguments; + +void InitializeMainArguments(int argc, char* argv[]) { + CHECK(!g_arguments); + CHECK(argc); + CHECK(argv); + + g_arguments = new const std::vector(argv, argv + argc); +} + +const std::vector& GetMainArguments() { + CHECK(g_arguments); + return *g_arguments; +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/main_arguments.h b/shared/sentry/external/crashpad/test/main_arguments.h new file mode 100644 index 000000000..d6c4d5c82 --- /dev/null +++ b/shared/sentry/external/crashpad/test/main_arguments.h @@ -0,0 +1,49 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_MAIN_ARGUMENTS_H_ +#define CRASHPAD_TEST_MAIN_ARGUMENTS_H_ + +#include +#include + +namespace crashpad { +namespace test { + +//! \brief Saves the arguments to `main()` for later use. +//! +//! Call this function from a test program’s `main()` function so that tests +//! that require access to these variables can retrieve them from +//! GetMainArguments(). +//! +//! The contents of \a argv, limited to \a argc elements, will be copied, so +//! that subsequent modifications to these variables by `main()` will not affect +//! the state returned by GetMainArguments(). +//! +//! This function must be called exactly once during the lifetime of a test +//! program. +void InitializeMainArguments(int argc, char* argv[]); + +//! \brief Retrieves pointers to the arguments to `main()`. +//! +//! Tests that need to access the original values of a test program’s `main()` +//! function’s parameters at process creation can use this function to retrieve +//! them, provided that `main()` called InitializeMainArguments() before making +//! any changes to its arguments. +const std::vector& GetMainArguments(); + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_MAIN_ARGUMENTS_H_ diff --git a/shared/sentry/external/crashpad/test/main_arguments_test.cc b/shared/sentry/external/crashpad/test/main_arguments_test.cc new file mode 100644 index 000000000..85d15984d --- /dev/null +++ b/shared/sentry/external/crashpad/test/main_arguments_test.cc @@ -0,0 +1,34 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "test/main_arguments.h" + +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(MainArguments, GetMainArguments) { + // Make sure that InitializeMainArguments() has been called and that + // GetMainArguments() provides reasonable values. + const std::vector& arguments = GetMainArguments(); + + ASSERT_FALSE(arguments.empty()); + EXPECT_FALSE(arguments[0].empty()); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/multiprocess.h b/shared/sentry/external/crashpad/test/multiprocess.h new file mode 100644 index 000000000..dd96a0fe1 --- /dev/null +++ b/shared/sentry/external/crashpad/test/multiprocess.h @@ -0,0 +1,232 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_MULTIPROCESS_H_ +#define CRASHPAD_TEST_MULTIPROCESS_H_ + +#include +#include + +#include "build/build_config.h" +#include "util/file/file_io.h" + +namespace crashpad { +namespace test { + +namespace internal { +struct MultiprocessInfo; +} // namespace internal + +#if BUILDFLAG(IS_FUCHSIA) +using ReturnCodeType = int64_t; +#else +using ReturnCodeType = int; +#endif + +//! \brief Manages a multiprocess test. +//! +//! These tests are `fork()`-based. The parent and child processes are able to +//! communicate via a pair of POSIX pipes. +//! +//! Subclasses are expected to implement the parent and child by overriding the +//! appropriate methods. +//! +//! On Windows and Fuchsia, this class is only an internal implementation +//! detail of MultiprocessExec and all tests must use that class. +class Multiprocess { + public: + //! \brief The termination type for a child process. + enum TerminationReason : bool { + //! \brief The child terminated normally. + //! + //! A normal return happens when a test returns from RunChild(), or for + //! tests that `exec()`, returns from `main()`. This also happens for tests + //! that call `exit()` or `_exit()`. + kTerminationNormal = false, + +#if !BUILDFLAG(IS_FUCHSIA) // There are no signals on Fuchsia. + //! \brief The child terminated by signal. + //! + //! Signal termination happens as a result of a crash, a call to `abort()`, + //! assertion failure (including Google Test assertions), etc. + kTerminationSignal, +#endif // !BUILDFLAG(IS_FUCHSIA) + }; + + Multiprocess(); + + Multiprocess(const Multiprocess&) = delete; + Multiprocess& operator=(const Multiprocess&) = delete; + + //! \brief Runs the test. + //! + //! This method establishes the proper testing environment by calling + //! PreFork(), then calls `fork()`. In the parent process, it calls + //! RunParent(), and in the child process, it calls RunChild(). + //! + //! This method uses Google Test assertions to validate the testing + //! environment. If the testing environment cannot be set up properly, it is + //! possible that MultiprocessParent() or MultiprocessChild() will not be + //! called. In the parent process, this method also waits for the child + //! process to exit after MultiprocessParent() returns, and verifies that it + //! exited in accordance with the expectations set by + //! SetExpectedChildTermination(). + void Run(); + + //! \brief Sets the expected termination reason and code. + //! + //! The default expected termination reason is + //! TerminationReason::kTerminationNormal, and the default expected + //! termination code is `EXIT_SUCCESS` (`0`). + //! + //! This method does not need to be called if the default termination + //! expectation is appropriate, but if this method is called, it must be + //! called before Run(). + //! + //! \param[in] reason Whether to expect the child to terminate normally or + //! as a result of a signal. + //! \param[in] code If \a reason is TerminationReason::kTerminationNormal, + //! this is the expected exit status of the child. If \a reason is + //! TerminationReason::kTerminationSignal, this is the signal that is + //! expected to kill the child. On Linux platforms, SIG_DFL will be + //! installed for \a code in the child process. + void SetExpectedChildTermination(TerminationReason reason, + ReturnCodeType code); + +#if !BUILDFLAG(IS_WIN) + //! \brief Sets termination reason and code appropriately for a child that + //! terminates via `__builtin_trap()`. + void SetExpectedChildTerminationBuiltinTrap(); +#endif // !BUILDFLAG(IS_WIN) + + protected: + ~Multiprocess(); + + //! \brief Establishes the proper testing environment prior to forking. + //! + //! Subclasses that solely implement a test should not need to override this + //! method. Subclasses that do not implement tests but instead implement + //! additional testing features on top of this class may override this method + //! provided that they call the superclass’ implementation first as follows: + //! + //! \code + //! void PreFork() override { + //! ASSERT_NO_FATAL_FAILURE(Multiprocess::PreFork()); + //! + //! // Place subclass-specific pre-fork code here. + //! } + //! \endcode + //! + //! Subclass implementations may signal failure by raising their own fatal + //! Google Test assertions. + virtual void PreFork() +#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_FUCHSIA) + = 0 +#endif // BUILDFLAG(IS_WIN) || BUILDFLAG(IS_FUCHSIA) + ; + +#if !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_FUCHSIA) + //! \brief Returns the child process’ process ID. + //! + //! This method may only be called by the parent process. + pid_t ChildPID() const; +#endif // !BUILDFLAG(IS_WIN) && !BUILDFLAG(IS_FUCHSIA) + + //! \brief Returns the read pipe’s file handle. + //! + //! This method may be called by either the parent or the child process. + //! Anything written to the write pipe in the partner process will appear + //! on this file handle in this process. + //! + //! It is an error to call this after CloseReadPipe() has been called. + //! + //! \return The read pipe’s file handle. + FileHandle ReadPipeHandle() const; + + //! \brief Returns the write pipe’s file handle. + //! + //! This method may be called by either the parent or the child process. + //! Anything written to this file handle in this process will appear on + //! the read pipe in the partner process. + //! + //! It is an error to call this after CloseWritePipe() has been called. + //! + //! \return The write pipe’s file handle. + FileHandle WritePipeHandle() const; + + //! \brief Closes the read pipe. + //! + //! This method may be called by either the parent or the child process. An + //! attempt to write to the write pipe in the partner process will fail with + //! `EPIPE` or `SIGPIPE`. ReadPipeHandle() must not be called after this. + void CloseReadPipe(); + + //! \brief Closes the write pipe. + //! + //! This method may be called by either the parent or the child process. An + //! attempt to read from the read pipe in the partner process will indicate + //! end-of-file. WritePipeHandle() must not be called after this. + void CloseWritePipe(); + + void set_info(internal::MultiprocessInfo* info) { info_ = info; } + internal::MultiprocessInfo* info() { return info_; } + + private: + //! \brief Runs the parent side of the test. + //! + //! This method establishes the parent’s environment and calls + //! MultiprocessParent(). + void RunParent(); + + //! \brief Runs the child side of the test. + //! + //! This method establishes the child’s environment, calls + //! MultiprocessChild(), and exits cleanly by calling `_exit(0)`. However, if + //! any failure (via fatal or nonfatal Google Test assertion) is detected, the + //! child will exit with a failure status. + void RunChild(); + + //! \brief The subclass-provided parent routine. + //! + //! Test failures should be reported via Google Test: `EXPECT_*()`, + //! `ASSERT_*()`, `FAIL()`, etc. + //! + //! This method must not use a `wait()`-family system call to wait for the + //! child process to exit, as this is handled by this class. + //! + //! Subclasses must implement this method to define how the parent operates. + virtual void MultiprocessParent() = 0; + + //! \brief The subclass-provided child routine. + //! + //! Test failures should be reported via Google Test: `EXPECT_*()`, + //! `ASSERT_*()`, `FAIL()`, etc. + //! + //! Subclasses must implement this method to define how the child operates. + //! Subclasses may exit with a failure status by using `LOG(FATAL)`, + //! `abort()`, or similar. They may exit cleanly by returning from this method + //! or by calling `_exit(0)`. Under no circumstances may `exit()` be called + //! by the child without having the child process `exec()`. Use + //! MultiprocessExec if the child should call `exec()`. + virtual void MultiprocessChild() = 0; + + internal::MultiprocessInfo* info_; + ReturnCodeType code_; + TerminationReason reason_; +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_MULTIPROCESS_H_ diff --git a/shared/sentry/external/crashpad/test/multiprocess_exec.cc b/shared/sentry/external/crashpad/test/multiprocess_exec.cc new file mode 100644 index 000000000..805090d0a --- /dev/null +++ b/shared/sentry/external/crashpad/test/multiprocess_exec.cc @@ -0,0 +1,75 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "test/multiprocess_exec.h" + +#include + +#include "base/check.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" +#include "test/main_arguments.h" +#include "test/test_paths.h" +#include "util/stdlib/map_insert.h" + +namespace crashpad { +namespace test { + +namespace internal { + +namespace { + +std::map* GetMultiprocessFunctionMap() { + static auto* map = new std::map(); + return map; +} + +} // namespace + +AppendMultiprocessTest::AppendMultiprocessTest(const std::string& test_name, + int (*main_function_pointer)()) { + CHECK(MapInsertOrReplace( + GetMultiprocessFunctionMap(), test_name, main_function_pointer, nullptr)) + << test_name << " already registered"; +} + +int CheckedInvokeMultiprocessChild(const std::string& test_name) { + const auto* functions = internal::GetMultiprocessFunctionMap(); + auto it = functions->find(test_name); + CHECK(it != functions->end()) + << "child main " << test_name << " not registered"; + return (*it->second)(); +} + +} // namespace internal + +void MultiprocessExec::SetChildTestMainFunction( + const std::string& function_name) { + std::vector rest(GetMainArguments().begin() + 1, + GetMainArguments().end()); + rest.push_back(internal::kChildTestFunction + function_name); + +#if BUILDFLAG(IS_WIN) + // Instead of using argv[0] on Windows, use the actual binary name. This is + // necessary because if originally the test isn't run with ".exe" on the + // command line, then argv[0] also won't include ".exe". This argument is used + // as the lpApplicationName argument to CreateProcess(), and so will fail. + SetChildCommand(TestPaths::Executable(), &rest); +#else + SetChildCommand(base::FilePath(GetMainArguments()[0]), &rest); +#endif +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/multiprocess_exec.h b/shared/sentry/external/crashpad/test/multiprocess_exec.h new file mode 100644 index 000000000..3be9b7a25 --- /dev/null +++ b/shared/sentry/external/crashpad/test/multiprocess_exec.h @@ -0,0 +1,154 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_MULTIPROCESS_EXEC_H_ +#define CRASHPAD_TEST_MULTIPROCESS_EXEC_H_ + +#include +#include + +#include "base/files/file_path.h" +#include "build/build_config.h" +#include "test/multiprocess.h" +#include "test/process_type.h" + +//! \file + +namespace crashpad { +namespace test { + +namespace internal { + +//! \brief Command line argument used to indicate that a child test function +//! should be run. +constexpr char kChildTestFunction[] = "--child-test-function="; + + +//! \brief Helper class used by CRASHPAD_CHILD_TEST_MAIN() to insert a child +//! function into the global mapping. +class AppendMultiprocessTest { + public: + AppendMultiprocessTest(const std::string& test_name, + int (*main_function_pointer)()); +}; + +//! \brief Used to run a child test function by name, registered by +//! CRASHPAD_CHILD_TEST_MAIN(). +//! +//! \return The exit code of the child process after running the function named +//! by \a test_name. Aborts with a CHECK() if \a test_name wasn't +//! registered. +int CheckedInvokeMultiprocessChild(const std::string& test_name); + +} // namespace internal + +//! \brief Registers a function that can be invoked as a child process by +//! MultiprocessExec. +//! +//! Used as: +//! +//! \code +//! CRASHPAD_CHILD_TEST_MAIN(MyChildTestBody) { +//! ... child body ... +//! } +//! \endcode +//! +//! In the main (parent) test body, this function can be run in a child process +//! via MultiprocessExec::SetChildTestMainFunction(). +#define CRASHPAD_CHILD_TEST_MAIN(test_main) \ + int test_main(); \ + namespace { \ + ::crashpad::test::internal::AppendMultiprocessTest \ + AddMultiprocessTest##_##test_main(#test_main, (test_main)); \ + } /* namespace */ \ + int test_main() + +//! \brief Manages an `exec()`-based multiprocess test. +//! +//! These tests are based on `fork()` and `exec()`. The parent process is able +//! to communicate with the child in the same manner as a base-class +//! Multiprocess parent. The read and write pipes appear in the child process on +//! stdin and stdout, respectively. +//! +//! Subclasses are expected to implement the parent in the same was as a +//! base-class Multiprocess parent. The child must be implemented in an +//! executable to be set by SetChildCommand(). +class MultiprocessExec : public Multiprocess { + public: + MultiprocessExec(); + + MultiprocessExec(const MultiprocessExec&) = delete; + MultiprocessExec& operator=(const MultiprocessExec&) = delete; + + //! \brief Sets the command to `exec()` in the child. + //! + //! This method must be called before the test can be Run(). + //! + //! This method is useful when a custom executable is required for the child + //! binary, however, SetChildTestMainFunction() should generally be preferred. + //! + //! \param[in] command The executable’s pathname. + //! \param[in] arguments The command-line arguments to pass to the child + //! process in its `argv[]` vector. This vector must begin at `argv[1]`, + //! as \a command is implicitly used as `argv[0]`. This argument may be + //! `nullptr` if no command-line arguments are to be passed. + //! + //! \sa SetChildTestMainFunction + void SetChildCommand(const base::FilePath& command, + const std::vector* arguments); + + //! \brief Calls SetChildCommand() to run a child test main function + //! registered with CRASHPAD_CHILD_TEST_MAIN(). + //! + //! This uses the same launch mechanism as SetChildCommand(), but coordinates + //! with test/gtest_main.cc to allow for simple registration of a child + //! processes' entry point via the helper macro, rather than needing to create + //! a separate build target. + //! + //! \param[in] function_name The name of the function as passed to + //! CRASHPAD_CHILD_TEST_MAIN(). + void SetChildTestMainFunction(const std::string& function_name); + + //! \brief Returns a ProcessType representing the child process. + //! + //! This method is only valid during the body of MultiprocessParent(). + //! + //! \return A platform-specific type representing the child process. This + //! method can fail on macOS because access to a child's task port + //! requires the task_for_pid entitlement. + ProcessType ChildProcess(); + + protected: + ~MultiprocessExec(); + + // Multiprocess: + void PreFork() override; + + private: + // Multiprocess: + void MultiprocessChild() override; + + base::FilePath command_; + std::vector arguments_; +#if BUILDFLAG(IS_POSIX) + std::vector argv_; +#elif BUILDFLAG(IS_WIN) + std::wstring command_line_; +#endif // BUILDFLAG(IS_POSIX) +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_MULTIPROCESS_EXEC_H_ diff --git a/shared/sentry/external/crashpad/test/multiprocess_exec_fuchsia.cc b/shared/sentry/external/crashpad/test/multiprocess_exec_fuchsia.cc new file mode 100644 index 000000000..a1e2acf50 --- /dev/null +++ b/shared/sentry/external/crashpad/test/multiprocess_exec_fuchsia.cc @@ -0,0 +1,203 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "test/multiprocess_exec.h" + +#include +#include +#include +#include +#include + +#include "base/files/scoped_file.h" +#include "base/fuchsia/fuchsia_logging.h" +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { + +namespace { + +void AddPipe(fdio_spawn_action_t* action, int target_fd, int* fd_out) { + zx_handle_t handle = ZX_HANDLE_INVALID; + zx_status_t status = fdio_pipe_half(fd_out, &handle); + ZX_CHECK(status == ZX_OK, status) << "fdio_pipe_half"; + action->action = FDIO_SPAWN_ACTION_ADD_HANDLE; + action->h.id = PA_HND(PA_FD, target_fd); + action->h.handle = handle; +} + +} // namespace + +namespace internal { + +struct MultiprocessInfo { + MultiprocessInfo() {} + base::ScopedFD stdin_write; + base::ScopedFD stdout_read; + zx::process child; +}; + +} // namespace internal + +Multiprocess::Multiprocess() + : info_(nullptr), code_(EXIT_SUCCESS), reason_(kTerminationNormal) {} + +void Multiprocess::Run() { + // Set up and spawn the child process. + ASSERT_NO_FATAL_FAILURE(PreFork()); + RunChild(); + + // And then run the parent actions in this process. + RunParent(); + + // Wait until the child exits. + zx_signals_t signals; + ASSERT_EQ( + info_->child.wait_one(ZX_TASK_TERMINATED, zx::time::infinite(), &signals), + ZX_OK); + ASSERT_EQ(signals, ZX_TASK_TERMINATED); + + // Get the child's exit code. + zx_info_process_t proc_info; + zx_status_t status = info_->child.get_info( + ZX_INFO_PROCESS, &proc_info, sizeof(proc_info), nullptr, nullptr); + if (status != ZX_OK) { + ZX_LOG(ERROR, status) << "zx_object_get_info"; + ADD_FAILURE() << "Unable to get exit code of child"; + } else { + if (code_ != proc_info.return_code) { + ADD_FAILURE() << "Child exited with code " << proc_info.return_code + << ", expected exit with code " << code_; + } + } +} + +void Multiprocess::SetExpectedChildTermination(TerminationReason reason, + ReturnCodeType code) { + EXPECT_EQ(info_, nullptr) + << "SetExpectedChildTermination() must be called before Run()"; + reason_ = reason; + code_ = code; +} + +void Multiprocess::SetExpectedChildTerminationBuiltinTrap() { + constexpr ReturnCodeType kExpectedReturnCode = ZX_TASK_RETCODE_EXCEPTION_KILL; + SetExpectedChildTermination(kTerminationNormal, kExpectedReturnCode); +} + +Multiprocess::~Multiprocess() { + delete info_; +} + +FileHandle Multiprocess::ReadPipeHandle() const { + return info_->stdout_read.get(); +} + +FileHandle Multiprocess::WritePipeHandle() const { + return info_->stdin_write.get(); +} + +void Multiprocess::CloseReadPipe() { + info_->stdout_read.reset(); +} + +void Multiprocess::CloseWritePipe() { + info_->stdin_write.reset(); +} + +void Multiprocess::RunParent() { + MultiprocessParent(); + + info_->stdout_read.reset(); + info_->stdin_write.reset(); +} + +void Multiprocess::RunChild() { + MultiprocessChild(); +} + +MultiprocessExec::MultiprocessExec() + : Multiprocess(), command_(), arguments_(), argv_() {} + +void MultiprocessExec::SetChildCommand( + const base::FilePath& command, + const std::vector* arguments) { + command_ = command; + if (arguments) { + arguments_ = *arguments; + } else { + arguments_.clear(); + } +} + +MultiprocessExec::~MultiprocessExec() {} + +void MultiprocessExec::PreFork() { + ASSERT_FALSE(command_.empty()); + + ASSERT_TRUE(argv_.empty()); + + argv_.push_back(command_.value().c_str()); + for (const std::string& argument : arguments_) { + argv_.push_back(argument.c_str()); + } + argv_.push_back(nullptr); + + ASSERT_EQ(info(), nullptr); + set_info(new internal::MultiprocessInfo()); +} + +void MultiprocessExec::MultiprocessChild() { + constexpr size_t kActionCount = 3; + fdio_spawn_action_t actions[kActionCount]; + + int stdin_parent_side = -1; + AddPipe(&actions[0], STDIN_FILENO, &stdin_parent_side); + info()->stdin_write.reset(stdin_parent_side); + + int stdout_parent_side = -1; + AddPipe(&actions[1], STDOUT_FILENO, &stdout_parent_side); + info()->stdout_read.reset(stdout_parent_side); + + actions[2].action = FDIO_SPAWN_ACTION_CLONE_FD; + actions[2].fd.local_fd = STDERR_FILENO; + actions[2].fd.target_fd = STDERR_FILENO; + + // Pass the filesystem namespace, parent environment, and default job to the + // child, but don't include any other file handles, preferring to set them + // up explicitly below. + uint32_t flags = FDIO_SPAWN_CLONE_ALL & ~FDIO_SPAWN_CLONE_STDIO; + + char error_message[FDIO_SPAWN_ERR_MSG_MAX_LENGTH]; + zx::process child; + zx_status_t status = fdio_spawn_etc(ZX_HANDLE_INVALID, + flags, + command_.value().c_str(), + argv_.data(), + nullptr, + kActionCount, + actions, + child.reset_and_get_address(), + error_message); + ZX_CHECK(status == ZX_OK, status) << "fdio_spawn_etc: " << error_message; + info()->child = std::move(child); +} + +ProcessType MultiprocessExec::ChildProcess() { + return zx::unowned_process(info()->child); +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/multiprocess_exec_posix.cc b/shared/sentry/external/crashpad/test/multiprocess_exec_posix.cc new file mode 100644 index 000000000..fa87f0888 --- /dev/null +++ b/shared/sentry/external/crashpad/test/multiprocess_exec_posix.cc @@ -0,0 +1,165 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "test/multiprocess_exec.h" + +#include +#include +#include +#include + +#include "base/posix/eintr_wrapper.h" +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "test/errors.h" +#include "util/misc/scoped_forbid_return.h" +#include "util/posix/close_multiple.h" + +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) +#include +#endif + +#if BUILDFLAG(IS_APPLE) +#include "util/mach/task_for_pid.h" +#endif + +namespace crashpad { +namespace test { + +MultiprocessExec::MultiprocessExec() + : Multiprocess(), + command_(), + arguments_(), + argv_() { +} + +void MultiprocessExec::SetChildCommand( + const base::FilePath& command, + const std::vector* arguments) { + command_ = command; + if (arguments) { + arguments_ = *arguments; + } else { + arguments_.clear(); + } +} + +MultiprocessExec::~MultiprocessExec() { +} + +void MultiprocessExec::PreFork() { + ASSERT_NO_FATAL_FAILURE(Multiprocess::PreFork()); + + ASSERT_FALSE(command_.empty()); + + // Build up the argv vector. This is done in PreFork() instead of + // MultiprocessChild() because although the result is only needed in the child + // process, building it is a hazardous operation in that process. + ASSERT_TRUE(argv_.empty()); + + argv_.push_back(command_.value().c_str()); + for (const std::string& argument : arguments_) { + argv_.push_back(argument.c_str()); + } + argv_.push_back(nullptr); +} + +void MultiprocessExec::MultiprocessChild() { + // Make sure that stdin, stdout, and stderr are FDs 0, 1, and 2, respectively. + // All FDs above this will be closed. + static_assert(STDIN_FILENO == 0, "stdin must be fd 0"); + static_assert(STDOUT_FILENO == 1, "stdout must be fd 1"); + static_assert(STDERR_FILENO == 2, "stderr must be fd 2"); + + // Move the read pipe to stdin. + FileHandle read_handle = ReadPipeHandle(); + ASSERT_NE(read_handle, STDIN_FILENO); + ASSERT_NE(read_handle, STDOUT_FILENO); + ASSERT_EQ(fileno(stdin), STDIN_FILENO); + + int rv; + +#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) + __fpurge(stdin); +#else + rv = fpurge(stdin); + ASSERT_EQ(rv, 0) << ErrnoMessage("fpurge"); +#endif + + rv = HANDLE_EINTR(dup2(read_handle, STDIN_FILENO)); + ASSERT_EQ(rv, STDIN_FILENO) << ErrnoMessage("dup2"); + + // Move the write pipe to stdout. + FileHandle write_handle = WritePipeHandle(); + ASSERT_NE(write_handle, STDIN_FILENO); + ASSERT_NE(write_handle, STDOUT_FILENO); + ASSERT_EQ(fileno(stdout), STDOUT_FILENO); + + // Make a copy of the original stdout file descriptor so that in case there’s + // an execv() failure, the original stdout can be restored so that Google Test + // messages directed to stdout go to the right place. Mark it as + // close-on-exec, so that the child won’t see it after a successful exec(), + // but it will still be available in this process after an unsuccessful + // exec(). + int dup_orig_stdout_fd = dup(STDOUT_FILENO); + ASSERT_GE(dup_orig_stdout_fd, 0) << ErrnoMessage("dup"); + + rv = fcntl(dup_orig_stdout_fd, F_SETFD, FD_CLOEXEC); + ASSERT_NE(rv, -1) << ErrnoMessage("fcntl"); + + rv = HANDLE_EINTR(fflush(stdout)); + ASSERT_EQ(rv, 0) << ErrnoMessage("fflush"); + + rv = HANDLE_EINTR(dup2(write_handle, STDOUT_FILENO)); + ASSERT_EQ(rv, STDOUT_FILENO) << ErrnoMessage("dup2"); + + CloseMultipleNowOrOnExec(STDERR_FILENO + 1, dup_orig_stdout_fd); + + // Start the new program, replacing this one. execv() has a weird declaration + // where its argv argument is declared as char* const*. In reality, the + // implementation behaves as if the argument were const char* const*, and this + // behavior is required by the standard. See + // http://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html + // (search for “constantâ€). + execv(argv_[0], const_cast(&argv_[0])); + + // This should not normally be reached. Getting here means that execv() + // failed. + + // Be sure not to return until FAIL() is reached. + ScopedForbidReturn forbid_return; + + // Put the original stdout back. Close the copy of the write pipe FD that’s + // currently on stdout first, so that in case the dup2() that restores the + // original stdout fails, stdout isn’t left attached to the pipe when the + // FAIL() statement executes. + HANDLE_EINTR(fflush(stdout)); + IGNORE_EINTR(close(STDOUT_FILENO)); + HANDLE_EINTR(dup2(dup_orig_stdout_fd, STDOUT_FILENO)); + IGNORE_EINTR(close(dup_orig_stdout_fd)); + + forbid_return.Disarm(); + FAIL() << ErrnoMessage("execv") << ": " << argv_[0]; +} + +ProcessType MultiprocessExec::ChildProcess() { +#if BUILDFLAG(IS_APPLE) + return TaskForPID(ChildPID()); +#else + return ChildPID(); +#endif +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/multiprocess_exec_test.cc b/shared/sentry/external/crashpad/test/multiprocess_exec_test.cc new file mode 100644 index 000000000..8d8f1e201 --- /dev/null +++ b/shared/sentry/external/crashpad/test/multiprocess_exec_test.cc @@ -0,0 +1,137 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "test/multiprocess_exec.h" + +#include "base/logging.h" +#include "base/strings/utf_string_conversions.h" +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "test/test_paths.h" +#include "util/file/file_io.h" + +namespace crashpad { +namespace test { +namespace { + +class TestMultiprocessExec final : public MultiprocessExec { + public: + TestMultiprocessExec() : MultiprocessExec() {} + + TestMultiprocessExec(const TestMultiprocessExec&) = delete; + TestMultiprocessExec& operator=(const TestMultiprocessExec&) = delete; + + ~TestMultiprocessExec() {} + + private: + void MultiprocessParent() override { + // Use Logging*File() instead of Checked*File() so that the test can fail + // gracefully with a Google Test assertion if the child does not execute + // properly. + + char c = 'z'; + ASSERT_TRUE(LoggingWriteFile(WritePipeHandle(), &c, 1)); + + ASSERT_TRUE(LoggingReadFileExactly(ReadPipeHandle(), &c, 1)); + EXPECT_EQ(c, 'Z'); + } +}; + +TEST(MultiprocessExec, MultiprocessExec) { + TestMultiprocessExec multiprocess_exec; + base::FilePath child_test_executable = TestPaths::BuildArtifact( + FILE_PATH_LITERAL("test"), + FILE_PATH_LITERAL("multiprocess_exec_test_child"), + TestPaths::FileType::kExecutable); + multiprocess_exec.SetChildCommand(child_test_executable, nullptr); + multiprocess_exec.Run(); +} + + +CRASHPAD_CHILD_TEST_MAIN(SimpleMultiprocess) { + char c; + CheckedReadFileExactly(StdioFileHandle(StdioStream::kStandardInput), &c, 1); + LOG_IF(FATAL, c != 'z'); + + c = 'Z'; + CheckedWriteFile(StdioFileHandle(StdioStream::kStandardOutput), &c, 1); + return 0; +} + +TEST(MultiprocessExec, MultiprocessExecSimpleChild) { + TestMultiprocessExec exec; + exec.SetChildTestMainFunction("SimpleMultiprocess"); + exec.Run(); +} + +CRASHPAD_CHILD_TEST_MAIN(SimpleMultiprocessReturnsNonZero) { + return 123; +} + +class TestMultiprocessExecEmpty final : public MultiprocessExec { + public: + TestMultiprocessExecEmpty() = default; + + TestMultiprocessExecEmpty(const TestMultiprocessExecEmpty&) = delete; + TestMultiprocessExecEmpty& operator=(const TestMultiprocessExecEmpty&) = + delete; + + ~TestMultiprocessExecEmpty() = default; + + private: + void MultiprocessParent() override {} +}; + +TEST(MultiprocessExec, MultiprocessExecSimpleChildReturnsNonZero) { + TestMultiprocessExecEmpty exec; + exec.SetChildTestMainFunction("SimpleMultiprocessReturnsNonZero"); + exec.SetExpectedChildTermination( + Multiprocess::TerminationReason::kTerminationNormal, 123); + exec.Run(); +} + +#if !BUILDFLAG(IS_WIN) + +CRASHPAD_CHILD_TEST_MAIN(BuiltinTrapChild) { + __builtin_trap(); + return EXIT_SUCCESS; +} + +class TestBuiltinTrapTermination final : public MultiprocessExec { + public: + TestBuiltinTrapTermination() { + SetChildTestMainFunction("BuiltinTrapChild"); + SetExpectedChildTerminationBuiltinTrap(); + } + + TestBuiltinTrapTermination(const TestBuiltinTrapTermination&) = delete; + TestBuiltinTrapTermination& operator=(const TestBuiltinTrapTermination&) = + delete; + + ~TestBuiltinTrapTermination() = default; + + private: + void MultiprocessParent() override {} +}; + +TEST(MultiprocessExec, BuiltinTrapTermination) { + TestBuiltinTrapTermination test; + test.Run(); +} + +#endif // !BUILDFLAG(IS_WIN) + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/multiprocess_exec_test_child.cc b/shared/sentry/external/crashpad/test/multiprocess_exec_test_child.cc new file mode 100644 index 000000000..f74b58560 --- /dev/null +++ b/shared/sentry/external/crashpad/test/multiprocess_exec_test_child.cc @@ -0,0 +1,95 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 +#include +#include +#include + +#include + +#include "base/logging.h" +#include "build/build_config.h" + +#if BUILDFLAG(IS_POSIX) +#if !BUILDFLAG(IS_FUCHSIA) +#include +#endif // !BUILDFLAG(IS_FUCHSIA) +#include +#elif BUILDFLAG(IS_WIN) +#include +#endif + +int main(int argc, char* argv[]) { +#if BUILDFLAG(IS_POSIX) + +#if BUILDFLAG(IS_FUCHSIA) + // getrlimit() is not implemented on Fuchsia. By construction, the child only + // receieves specific fds that it's given, but check low values as mild + // verification. + int last_fd = 1024; +#else + rlimit rlimit_nofile; + if (getrlimit(RLIMIT_NOFILE, &rlimit_nofile) != 0) { + LOG(FATAL) << "getrlimit"; + } + int last_fd = static_cast(rlimit_nofile.rlim_cur); +#endif // BUILDFLAG(IS_FUCHSIA) + + // Make sure that there’s nothing open at any FD higher than 3. All FDs other + // than stdin, stdout, and stderr should have been closed prior to or at + // exec(). + for (int fd = STDERR_FILENO + 1; fd < last_fd; ++fd) { + if (close(fd) == 0 || errno != EBADF) { + LOG(FATAL) << "close"; + } + } + + // Read a byte from stdin, expecting it to be a specific value. + char c; + ssize_t rv = read(STDIN_FILENO, &c, 1); + if (rv != 1 || c != 'z') { + LOG(FATAL) << "read"; + } + + // Write a byte to stdout. + c = 'Z'; + rv = write(STDOUT_FILENO, &c, 1); + if (rv != 1) { + LOG(FATAL) << "write"; + } +#elif BUILDFLAG(IS_WIN) + // TODO(scottmg): Verify that only the handles we expect to be open, are. + + // Read a byte from stdin, expecting it to be a specific value. + char c; + DWORD bytes_read; + HANDLE stdin_handle = GetStdHandle(STD_INPUT_HANDLE); + if (!ReadFile(stdin_handle, &c, 1, &bytes_read, nullptr) || + bytes_read != 1 || c != 'z') { + LOG(FATAL) << "ReadFile"; + } + + // Write a byte to stdout. + c = 'Z'; + DWORD bytes_written; + if (!WriteFile( + GetStdHandle(STD_OUTPUT_HANDLE), &c, 1, &bytes_written, nullptr) || + bytes_written != 1) { + LOG(FATAL) << "WriteFile"; + } +#endif // BUILDFLAG(IS_POSIX) + + return 0; +} diff --git a/shared/sentry/external/crashpad/test/multiprocess_exec_win.cc b/shared/sentry/external/crashpad/test/multiprocess_exec_win.cc new file mode 100644 index 000000000..08c4c17a1 --- /dev/null +++ b/shared/sentry/external/crashpad/test/multiprocess_exec_win.cc @@ -0,0 +1,178 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "test/multiprocess_exec.h" + +#include + +#include "base/check.h" +#include "base/strings/utf_string_conversions.h" +#include "gtest/gtest.h" +#include "util/win/command_line.h" + +namespace crashpad { +namespace test { + +namespace internal { + +struct MultiprocessInfo { + MultiprocessInfo() {} + ScopedFileHANDLE pipe_c2p_read; + ScopedFileHANDLE pipe_c2p_write; + ScopedFileHANDLE pipe_p2c_read; + ScopedFileHANDLE pipe_p2c_write; + PROCESS_INFORMATION process_info; +}; + +} // namespace internal + +Multiprocess::Multiprocess() + : info_(nullptr), + code_(EXIT_SUCCESS), + reason_(kTerminationNormal) { +} + +void Multiprocess::Run() { + // Set up and spawn the child process. + ASSERT_NO_FATAL_FAILURE(PreFork()); + RunChild(); + + // And then run the parent actions in this process. + RunParent(); + + // Reap the child. + WaitForSingleObject(info_->process_info.hProcess, INFINITE); + CloseHandle(info_->process_info.hThread); + CloseHandle(info_->process_info.hProcess); +} + +void Multiprocess::SetExpectedChildTermination(TerminationReason reason, + ReturnCodeType code) { + EXPECT_EQ(info_, nullptr) + << "SetExpectedChildTermination() must be called before Run()"; + reason_ = reason; + code_ = code; +} + +Multiprocess::~Multiprocess() { + delete info_; +} + +FileHandle Multiprocess::ReadPipeHandle() const { + // This is the parent case, it's stdin in the child. + return info_->pipe_c2p_read.get(); +} + +FileHandle Multiprocess::WritePipeHandle() const { + // This is the parent case, it's stdout in the child. + return info_->pipe_p2c_write.get(); +} + +void Multiprocess::CloseReadPipe() { + info_->pipe_c2p_read.reset(); +} + +void Multiprocess::CloseWritePipe() { + info_->pipe_p2c_write.reset(); +} + +void Multiprocess::RunParent() { + MultiprocessParent(); + + info_->pipe_c2p_read.reset(); + info_->pipe_p2c_write.reset(); +} + +void Multiprocess::RunChild() { + MultiprocessChild(); + + info_->pipe_c2p_write.reset(); + info_->pipe_p2c_read.reset(); +} + +MultiprocessExec::MultiprocessExec() + : Multiprocess(), command_(), arguments_(), command_line_() { +} + +void MultiprocessExec::SetChildCommand( + const base::FilePath& command, + const std::vector* arguments) { + command_ = command; + if (arguments) { + arguments_ = *arguments; + } else { + arguments_.clear(); + } +} + +MultiprocessExec::~MultiprocessExec() { +} + +void MultiprocessExec::PreFork() { + ASSERT_FALSE(command_.empty()); + + command_line_.clear(); + AppendCommandLineArgument(command_.value(), &command_line_); + for (size_t i = 0; i < arguments_.size(); ++i) { + AppendCommandLineArgument(base::UTF8ToWide(arguments_[i]), &command_line_); + } + + // Make pipes for child-to-parent and parent-to-child communication. Mark them + // as inheritable via the SECURITY_ATTRIBUTES, but use SetHandleInformation to + // ensure that the parent sides are not inherited. + ASSERT_EQ(info(), nullptr); + set_info(new internal::MultiprocessInfo()); + + SECURITY_ATTRIBUTES security_attributes = {0}; + security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); + security_attributes.bInheritHandle = TRUE; + + HANDLE c2p_read, c2p_write; + PCHECK(CreatePipe(&c2p_read, &c2p_write, &security_attributes, 0)); + PCHECK(SetHandleInformation(c2p_read, HANDLE_FLAG_INHERIT, 0)); + info()->pipe_c2p_read.reset(c2p_read); + info()->pipe_c2p_write.reset(c2p_write); + + HANDLE p2c_read, p2c_write; + PCHECK(CreatePipe(&p2c_read, &p2c_write, &security_attributes, 0)); + PCHECK(SetHandleInformation(p2c_write, HANDLE_FLAG_INHERIT, 0)); + info()->pipe_p2c_read.reset(p2c_read); + info()->pipe_p2c_write.reset(p2c_write); +} + +void MultiprocessExec::MultiprocessChild() { + STARTUPINFO startup_info = {0}; + startup_info.cb = sizeof(startup_info); + startup_info.hStdInput = info()->pipe_p2c_read.get(); + startup_info.hStdOutput = info()->pipe_c2p_write.get(); + startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); + startup_info.dwFlags = STARTF_USESTDHANDLES; + PCHECK(CreateProcess(command_.value().c_str(), + &command_line_[0], // This cannot be constant, per MSDN. + nullptr, + nullptr, + TRUE, + 0, + nullptr, + nullptr, + &startup_info, + &info()->process_info)); +} + +ProcessType MultiprocessExec::ChildProcess() { + return info()->process_info.hProcess; +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/multiprocess_posix.cc b/shared/sentry/external/crashpad/test/multiprocess_posix.cc new file mode 100644 index 000000000..f1b138f6c --- /dev/null +++ b/shared/sentry/external/crashpad/test/multiprocess_posix.cc @@ -0,0 +1,259 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "test/multiprocess.h" + +#include +#include +#include +#include + +#include +#include + +#include "base/auto_reset.h" +#include "base/check_op.h" +#include "base/files/scoped_file.h" +#include "base/posix/eintr_wrapper.h" +#include "base/strings/stringprintf.h" +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "test/errors.h" +#include "util/misc/scoped_forbid_return.h" +#include "util/posix/signals.h" + +#if BUILDFLAG(IS_APPLE) +#include "test/mac/exception_swallower.h" +#endif + +namespace crashpad { +namespace test { + +namespace internal { + +struct MultiprocessInfo { + MultiprocessInfo() + : pipe_c2p_read(-1), + pipe_c2p_write(-1), + pipe_p2c_read(-1), + pipe_p2c_write(-1), + child_pid(0) {} + + base::ScopedFD pipe_c2p_read; // child to parent + base::ScopedFD pipe_c2p_write; // child to parent + base::ScopedFD pipe_p2c_read; // parent to child + base::ScopedFD pipe_p2c_write; // parent to child + pid_t child_pid; // valid only in parent +}; + +} // namespace internal + +Multiprocess::Multiprocess() + : info_(nullptr), + code_(EXIT_SUCCESS), + reason_(kTerminationNormal) { +} + +void Multiprocess::Run() { + ASSERT_EQ(info_, nullptr); + std::unique_ptr info( + new internal::MultiprocessInfo); + base::AutoReset reset_info(&info_, info.get()); + + ASSERT_NO_FATAL_FAILURE(PreFork()); + +#if BUILDFLAG(IS_APPLE) + // If the child is expected to crash, set up an exception swallower to swallow + // the exception instead of allowing it to be seen by the system’s crash + // reporter. + std::unique_ptr exception_swallower; + if (reason_ == kTerminationSignal && Signals::IsCrashSignal(code_)) { + exception_swallower.reset(new ExceptionSwallower()); + } +#endif // BUILDFLAG(IS_APPLE) + + pid_t pid = fork(); + ASSERT_GE(pid, 0) << ErrnoMessage("fork"); + + if (pid > 0) { + info_->child_pid = pid; + + RunParent(); + + // Waiting for the child happens here instead of in RunParent() because even + // if RunParent() returns early due to a Google Test fatal assertion + // failure, the child should still be reaped. + + // This will make the parent hang up on the child as much as would be + // visible from the child’s perspective. The child’s side of the pipe will + // be broken, the child’s remote port will become a dead name, and an + // attempt by the child to look up the service will fail. If this weren’t + // done, the child might hang while waiting for a parent that has already + // triggered a fatal assertion failure to do something. + info.reset(); + info_ = nullptr; + + int status; + pid_t wait_pid = HANDLE_EINTR(waitpid(pid, &status, 0)); + ASSERT_EQ(wait_pid, pid) << ErrnoMessage("waitpid"); + + TerminationReason reason; + int code; + std::string message; + if (WIFEXITED(status)) { + reason = kTerminationNormal; + code = WEXITSTATUS(status); + message = base::StringPrintf("Child exited with code %d", code); + } else if (WIFSIGNALED(status)) { + reason = kTerminationSignal; + code = WTERMSIG(status); + message = + base::StringPrintf("Child terminated by signal %d (%s)%s", + code, + strsignal(code), + WCOREDUMP(status) ? " (core dumped)" : ""); + } else { + FAIL() << base::StringPrintf("Unknown termination reason 0x%x", status); + } + + if (reason_ == kTerminationNormal) { + message += base::StringPrintf(", expected exit with code %d", code_); + } else if (reason_ == kTerminationSignal) { + message += base::StringPrintf(", expected termination by signal %d (%s)", + code_, + strsignal(code_)); + } + + if (reason != reason_ || code != code_) { + ADD_FAILURE() << message; + } + } else { +#if BUILDFLAG(IS_APPLE) + if (exception_swallower.get()) { + ExceptionSwallower::SwallowExceptions(); + } +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) + if (reason_ == kTerminationSignal && Signals::IsCrashSignal(code_)) { + Signals::InstallDefaultHandler(code_); + } +#endif // BUILDFLAG(IS_APPLE) + + RunChild(); + } +} + +void Multiprocess::SetExpectedChildTermination(TerminationReason reason, + ReturnCodeType code) { + EXPECT_EQ(info_, nullptr) + << "SetExpectedChildTermination() must be called before Run()"; + reason_ = reason; + code_ = code; +} + +void Multiprocess::SetExpectedChildTerminationBuiltinTrap() { +#if defined(ARCH_CPU_ARM64) || defined(ARCH_CPU_MIPS_FAMILY) + SetExpectedChildTermination(kTerminationSignal, SIGTRAP); +#else + SetExpectedChildTermination(kTerminationSignal, SIGILL); +#endif +} + +Multiprocess::~Multiprocess() { +} + +void Multiprocess::PreFork() { + int pipe_fds_c2p[2]; + int rv = pipe(pipe_fds_c2p); + ASSERT_EQ(rv, 0) << ErrnoMessage("pipe"); + + info_->pipe_c2p_read.reset(pipe_fds_c2p[0]); + info_->pipe_c2p_write.reset(pipe_fds_c2p[1]); + + int pipe_fds_p2c[2]; + rv = pipe(pipe_fds_p2c); + ASSERT_EQ(rv, 0) << ErrnoMessage("pipe"); + + info_->pipe_p2c_read.reset(pipe_fds_p2c[0]); + info_->pipe_p2c_write.reset(pipe_fds_p2c[1]); +} + +pid_t Multiprocess::ChildPID() const { + EXPECT_NE(info_->child_pid, 0); + return info_->child_pid; +} + +FileHandle Multiprocess::ReadPipeHandle() const { + int fd = info_->child_pid ? info_->pipe_c2p_read.get() + : info_->pipe_p2c_read.get(); + CHECK_NE(fd, -1); + return fd; +} + +FileHandle Multiprocess::WritePipeHandle() const { + int fd = info_->child_pid ? info_->pipe_p2c_write.get() + : info_->pipe_c2p_write.get(); + CHECK_NE(fd, -1); + return fd; +} + +void Multiprocess::CloseReadPipe() { + if (info_->child_pid) { + info_->pipe_c2p_read.reset(); + } else { + info_->pipe_p2c_read.reset(); + } +} + +void Multiprocess::CloseWritePipe() { + if (info_->child_pid) { + info_->pipe_p2c_write.reset(); + } else { + info_->pipe_c2p_write.reset(); + } +} + +void Multiprocess::RunParent() { + // The parent uses the read end of c2p and the write end of p2c. + info_->pipe_c2p_write.reset(); + info_->pipe_p2c_read.reset(); + + MultiprocessParent(); + + info_->pipe_c2p_read.reset(); + info_->pipe_p2c_write.reset(); +} + +void Multiprocess::RunChild() { + ScopedForbidReturn forbid_return; + + // The child uses the write end of c2p and the read end of p2c. + info_->pipe_c2p_read.reset(); + info_->pipe_p2c_write.reset(); + + MultiprocessChild(); + + info_->pipe_c2p_write.reset(); + info_->pipe_p2c_read.reset(); + + if (testing::Test::HasFailure()) { + // Trigger the ScopedForbidReturn destructor. + return; + } + + // In a forked child, exit() is unsafe. Use _exit() instead. + _exit(EXIT_SUCCESS); +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/multiprocess_posix_test.cc b/shared/sentry/external/crashpad/test/multiprocess_posix_test.cc new file mode 100644 index 000000000..dd5f63f79 --- /dev/null +++ b/shared/sentry/external/crashpad/test/multiprocess_posix_test.cc @@ -0,0 +1,295 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "test/multiprocess.h" + +#include +#include +#include + +#include "gtest/gtest.h" +#include "test/gtest_death.h" +#include "util/file/file_io.h" + +namespace crashpad { +namespace test { +namespace { + +class TestMultiprocess final : public Multiprocess { + public: + TestMultiprocess() : Multiprocess() {} + + TestMultiprocess(const TestMultiprocess&) = delete; + TestMultiprocess& operator=(const TestMultiprocess&) = delete; + + ~TestMultiprocess() {} + + private: + // Multiprocess: + + void MultiprocessParent() override { + FileHandle read_handle = ReadPipeHandle(); + char c; + CheckedReadFileExactly(read_handle, &c, 1); + EXPECT_EQ(c, 'M'); + + pid_t pid; + CheckedReadFileExactly(read_handle, &pid, sizeof(pid)); + EXPECT_EQ(ChildPID(), pid); + + c = 'm'; + CheckedWriteFile(WritePipeHandle(), &c, 1); + + // The child will close its end of the pipe and exit. Make sure that the + // parent sees EOF. + CheckedReadFileAtEOF(read_handle); + } + + void MultiprocessChild() override { + FileHandle write_handle = WritePipeHandle(); + + char c = 'M'; + CheckedWriteFile(write_handle, &c, 1); + + pid_t pid = getpid(); + CheckedWriteFile(write_handle, &pid, sizeof(pid)); + + CheckedReadFileExactly(ReadPipeHandle(), &c, 1); + EXPECT_EQ(c, 'm'); + } +}; + +TEST(Multiprocess, Multiprocess) { + TestMultiprocess multiprocess; + multiprocess.Run(); +} + +class TestMultiprocessUnclean final : public Multiprocess { + public: + enum TerminationType { + kExitSuccess = 0, + kExitFailure, + kExit2, + kAbort, + }; + + explicit TestMultiprocessUnclean(TerminationType type) + : Multiprocess(), + type_(type) { + if (type_ == kAbort) { + SetExpectedChildTermination(kTerminationSignal, SIGABRT); + } else { + SetExpectedChildTermination(kTerminationNormal, ExitCode()); + } + } + + TestMultiprocessUnclean(const TestMultiprocessUnclean&) = delete; + TestMultiprocessUnclean& operator=(const TestMultiprocessUnclean&) = delete; + + ~TestMultiprocessUnclean() {} + + private: + int ExitCode() const { + return type_; + } + + // Multiprocess: + + void MultiprocessParent() override { + } + + void MultiprocessChild() override { + if (type_ == kAbort) { + abort(); + } else { + _exit(ExitCode()); + } + } + + TerminationType type_; +}; + +TEST(Multiprocess, SuccessfulExit) { + TestMultiprocessUnclean multiprocess(TestMultiprocessUnclean::kExitSuccess); + multiprocess.Run(); +} + +TEST(Multiprocess, UnsuccessfulExit) { + TestMultiprocessUnclean multiprocess(TestMultiprocessUnclean::kExitFailure); + multiprocess.Run(); +} + +TEST(Multiprocess, Exit2) { + TestMultiprocessUnclean multiprocess(TestMultiprocessUnclean::kExit2); + multiprocess.Run(); +} + +TEST(Multiprocess, AbortSignal) { + TestMultiprocessUnclean multiprocess(TestMultiprocessUnclean::kAbort); + multiprocess.Run(); +} + +class TestMultiprocessClosePipe final : public Multiprocess { + public: + enum WhoCloses { + kParentCloses = 0, + kChildCloses, + }; + enum WhatCloses { + kReadCloses = 0, + kWriteCloses, + kReadAndWriteClose, + }; + + TestMultiprocessClosePipe(WhoCloses who_closes, WhatCloses what_closes) + : Multiprocess(), + who_closes_(who_closes), + what_closes_(what_closes) { + // Fails under "threadsafe" mode on macOS 10.11. + testing::GTEST_FLAG(death_test_style) = "fast"; + } + + TestMultiprocessClosePipe(const TestMultiprocessClosePipe&) = delete; + TestMultiprocessClosePipe& operator=(const TestMultiprocessClosePipe&) = + delete; + + ~TestMultiprocessClosePipe() {} + + private: + void VerifyInitial() { + ASSERT_NE(ReadPipeHandle(), -1); + ASSERT_NE(WritePipeHandle(), -1); + } + + // Verifies that the partner process did what it was supposed to do. This must + // only be called when who_closes_ names the partner process, not this + // process. + // + // If the partner was supposed to close its write pipe, the read pipe will be + // checked to ensure that it shows end-of-file. + // + // If the partner was supposed to close its read pipe, the write pipe will be + // checked to ensure that a checked write causes death. This can only be done + // if the partner also provides some type of signal when it has closed its + // read pipe, which is done in the form of it closing its write pipe, causing + // the read pipe in this process to show end-of-file. + void VerifyPartner() { + if (what_closes_ == kWriteCloses) { + CheckedReadFileAtEOF(ReadPipeHandle()); + } else if (what_closes_ == kReadAndWriteClose) { + CheckedReadFileAtEOF(ReadPipeHandle()); + char c = '\0'; + + // This will raise SIGPIPE. If fatal (the normal case), that will cause + // process termination. If SIGPIPE is being handled somewhere, the write + // will still fail and set errno to EPIPE, and CheckedWriteFile() will + // abort execution. Regardless of how SIGPIPE is handled, the process will + // be terminated. Because the actual termination mechanism is not known, + // no regex can be specified. + EXPECT_DEATH_CHECK(CheckedWriteFile(WritePipeHandle(), &c, 1), ""); + } + } + + void Close() { + switch (what_closes_) { + case kReadCloses: + CloseReadPipe(); + EXPECT_NE(WritePipeHandle(), -1); + EXPECT_DEATH_CHECK(ReadPipeHandle(), "fd"); + break; + case kWriteCloses: + CloseWritePipe(); + EXPECT_NE(ReadPipeHandle(), -1); + EXPECT_DEATH_CHECK(WritePipeHandle(), "fd"); + break; + case kReadAndWriteClose: + CloseReadPipe(); + CloseWritePipe(); + EXPECT_DEATH_CHECK(ReadPipeHandle(), "fd"); + EXPECT_DEATH_CHECK(WritePipeHandle(), "fd"); + break; + } + } + + // Multiprocess: + + void MultiprocessParent() override { + ASSERT_NO_FATAL_FAILURE(VerifyInitial()); + + if (who_closes_ == kParentCloses) { + Close(); + } else { + VerifyPartner(); + } + } + + void MultiprocessChild() override { + ASSERT_NO_FATAL_FAILURE(VerifyInitial()); + + if (who_closes_ == kChildCloses) { + Close(); + } else { + VerifyPartner(); + } + } + + WhoCloses who_closes_; + WhatCloses what_closes_; +}; + +TEST(MultiprocessDeathTest, ParentClosesReadPipe) { + TestMultiprocessClosePipe multiprocess( + TestMultiprocessClosePipe::kParentCloses, + TestMultiprocessClosePipe::kReadCloses); + multiprocess.Run(); +} + +TEST(MultiprocessDeathTest, ParentClosesWritePipe) { + TestMultiprocessClosePipe multiprocess( + TestMultiprocessClosePipe::kParentCloses, + TestMultiprocessClosePipe::kWriteCloses); + multiprocess.Run(); +} + +TEST(MultiprocessDeathTest, ParentClosesReadAndWritePipe) { + TestMultiprocessClosePipe multiprocess( + TestMultiprocessClosePipe::kParentCloses, + TestMultiprocessClosePipe::kReadAndWriteClose); + multiprocess.Run(); +} + +TEST(MultiprocessDeathTest, ChildClosesReadPipe) { + TestMultiprocessClosePipe multiprocess( + TestMultiprocessClosePipe::kChildCloses, + TestMultiprocessClosePipe::kReadCloses); + multiprocess.Run(); +} + +TEST(MultiprocessDeathTest, ChildClosesWritePipe) { + TestMultiprocessClosePipe multiprocess( + TestMultiprocessClosePipe::kChildCloses, + TestMultiprocessClosePipe::kWriteCloses); + multiprocess.Run(); +} + +TEST(MultiprocessDeathTest, ChildClosesReadAndWritePipe) { + TestMultiprocessClosePipe multiprocess( + TestMultiprocessClosePipe::kChildCloses, + TestMultiprocessClosePipe::kReadAndWriteClose); + multiprocess.Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/process_type.cc b/shared/sentry/external/crashpad/test/process_type.cc new file mode 100644 index 000000000..7c6f88fa9 --- /dev/null +++ b/shared/sentry/external/crashpad/test/process_type.cc @@ -0,0 +1,41 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "test/process_type.h" + +#include "build/build_config.h" + +#if BUILDFLAG(IS_FUCHSIA) +#include +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) +#include +#endif + +namespace crashpad { +namespace test { + +ProcessType GetSelfProcess() { +#if BUILDFLAG(IS_FUCHSIA) + return zx::process::self(); +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) + return getpid(); +#elif BUILDFLAG(IS_WIN) + return GetCurrentProcess(); +#elif BUILDFLAG(IS_APPLE) + return mach_task_self(); +#endif +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/process_type.h b/shared/sentry/external/crashpad/test/process_type.h new file mode 100644 index 000000000..a1c727cb0 --- /dev/null +++ b/shared/sentry/external/crashpad/test/process_type.h @@ -0,0 +1,53 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_PROCESS_TYPE_H_ +#define CRASHPAD_TEST_PROCESS_TYPE_H_ + +#include "build/build_config.h" + +#if BUILDFLAG(IS_FUCHSIA) +#include +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_ANDROID) +#include +#elif BUILDFLAG(IS_WIN) +#include +#elif BUILDFLAG(IS_APPLE) +#include +#endif + +namespace crashpad { +namespace test { + +#if BUILDFLAG(IS_FUCHSIA) +using ProcessType = zx::unowned_process; +#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || \ + BUILDFLAG(IS_ANDROID) || DOXYGEN +//! \brief Alias for platform-specific type to represent a process. +using ProcessType = pid_t; +#elif BUILDFLAG(IS_WIN) +using ProcessType = HANDLE; +#elif BUILDFLAG(IS_APPLE) +using ProcessType = task_t; +#else +#error Port. +#endif + +//! \brief Get a ProcessType representing the current process. +ProcessType GetSelfProcess(); + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_PROCESS_TYPE_H_ diff --git a/shared/sentry/external/crashpad/test/scoped_guarded_page.h b/shared/sentry/external/crashpad/test/scoped_guarded_page.h new file mode 100644 index 000000000..f7513d4f7 --- /dev/null +++ b/shared/sentry/external/crashpad/test/scoped_guarded_page.h @@ -0,0 +1,51 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_SCOPED_GUARDED_PAGE_ +#define CRASHPAD_TEST_SCOPED_GUARDED_PAGE_ + + +namespace crashpad { +namespace test { + +//! \brief A RAII object that allocates a read-write page with an inacessible +//! page following it. +//! +//! Upon construction, a mapping will be created. Failure to create the mapping +//! is fatal. On destruction, the mapping is freed. +//! +//! This object should not be used in multi-threded contexts, the POSIX +//! implementation can not be made thread-safe. +class ScopedGuardedPage { + public: + ScopedGuardedPage(); + + ScopedGuardedPage(const ScopedGuardedPage&) = delete; + ScopedGuardedPage& operator=(const ScopedGuardedPage&) = delete; + + ~ScopedGuardedPage(); + + //! \brief Returns the address of the read-write page. + //! + //! \return The address of the read-write page. + void* Pointer() const { return ptr_; } + + private: + void* ptr_; +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_SCOPED_GUARDED_PAGE_ diff --git a/shared/sentry/external/crashpad/test/scoped_guarded_page_posix.cc b/shared/sentry/external/crashpad/test/scoped_guarded_page_posix.cc new file mode 100644 index 000000000..bb191d78e --- /dev/null +++ b/shared/sentry/external/crashpad/test/scoped_guarded_page_posix.cc @@ -0,0 +1,47 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "test/scoped_guarded_page.h" + +#include + +#include "base/check.h" +#include "base/memory/page_size.h" + +namespace crashpad { +namespace test { + +ScopedGuardedPage::ScopedGuardedPage() { + ptr_ = mmap(nullptr, + base::GetPageSize() * 2, + PROT_READ | PROT_WRITE, + MAP_PRIVATE | MAP_ANONYMOUS, + -1, + 0); + PCHECK(ptr_ != MAP_FAILED) << "mmap"; + + // Simply mprotect()ing the guard page PROT_NONE does not make it inaccessible + // using ptrace() or /proc/$pid/mem so we munmap() the following page instead. + // Unfortunately, this means that the guarded page is not thread safe from + // other threads mapping a single page into the empty region. + char* second_page = static_cast(ptr_) + base::GetPageSize(); + PCHECK(munmap(second_page, base::GetPageSize()) >= 0) << "munmap"; +} + +ScopedGuardedPage::~ScopedGuardedPage() { + PCHECK(munmap(ptr_, base::GetPageSize()) >= 0) << "munmap"; +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/scoped_guarded_page_test.cc b/shared/sentry/external/crashpad/test/scoped_guarded_page_test.cc new file mode 100644 index 000000000..291a12b4b --- /dev/null +++ b/shared/sentry/external/crashpad/test/scoped_guarded_page_test.cc @@ -0,0 +1,38 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "test/scoped_guarded_page.h" + +#include "base/memory/page_size.h" +#include "gtest/gtest.h" +#include "test/gtest_death.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(ScopedGuardedPage, BasicFunctionality) { + ::testing::FLAGS_gtest_death_test_style = "threadsafe"; + + ScopedGuardedPage page; + char* address = (char*)page.Pointer(); + EXPECT_NE(address, nullptr); + address[0] = 0; + address[base::GetPageSize() - 1] = 0; + EXPECT_DEATH_CRASH({ address[base::GetPageSize()] = 0; }, ""); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/scoped_guarded_page_win.cc b/shared/sentry/external/crashpad/test/scoped_guarded_page_win.cc new file mode 100644 index 000000000..a87691e97 --- /dev/null +++ b/shared/sentry/external/crashpad/test/scoped_guarded_page_win.cc @@ -0,0 +1,39 @@ +// Copyright 2018 The Crashpad Authors. All rights reserved. +// +// 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 "test/scoped_guarded_page.h" + +#include + +#include "base/check.h" +#include "base/memory/page_size.h" + +namespace crashpad { +namespace test { + +ScopedGuardedPage::ScopedGuardedPage() { + const size_t page_size = base::GetPageSize(); + ptr_ = VirtualAlloc(nullptr, page_size * 2, MEM_RESERVE, PAGE_NOACCESS); + PCHECK(ptr_ != nullptr) << "VirtualAlloc"; + + PCHECK(VirtualAlloc(ptr_, page_size, MEM_COMMIT, PAGE_READWRITE) != nullptr) + << "VirtualAlloc"; +} + +ScopedGuardedPage::~ScopedGuardedPage() { + PCHECK(VirtualFree(ptr_, 0, MEM_RELEASE)) << "VirtualFree"; +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/scoped_module_handle.cc b/shared/sentry/external/crashpad/test/scoped_module_handle.cc new file mode 100644 index 000000000..ff8e0bd9c --- /dev/null +++ b/shared/sentry/external/crashpad/test/scoped_module_handle.cc @@ -0,0 +1,52 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "test/scoped_module_handle.h" + +#include "base/logging.h" +#include "build/build_config.h" + +namespace crashpad { +namespace test { + +// static +void ScopedModuleHandle::Impl::Close(ModuleHandle handle) { +#if BUILDFLAG(IS_POSIX) + if (dlclose(handle) != 0) { + LOG(ERROR) << "dlclose: " << dlerror(); + } +#elif BUILDFLAG(IS_WIN) + if (!FreeLibrary(handle)) { + PLOG(ERROR) << "FreeLibrary"; + } +#else +#error Port +#endif +} + +ScopedModuleHandle::ScopedModuleHandle(ModuleHandle handle) : handle_(handle) {} + +ScopedModuleHandle::ScopedModuleHandle(ScopedModuleHandle&& other) + : handle_(other.handle_) { + other.handle_ = nullptr; +} + +ScopedModuleHandle::~ScopedModuleHandle() { + if (valid()) { + Impl::Close(handle_); + } +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/scoped_module_handle.h b/shared/sentry/external/crashpad/test/scoped_module_handle.h new file mode 100644 index 000000000..1a8ed1bf9 --- /dev/null +++ b/shared/sentry/external/crashpad/test/scoped_module_handle.h @@ -0,0 +1,87 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_SCOPED_MODULE_HANDLE_H_ +#define CRASHPAD_TEST_SCOPED_MODULE_HANDLE_H_ + +#include "build/build_config.h" + +#if BUILDFLAG(IS_POSIX) +#include +#elif BUILDFLAG(IS_WIN) +#include +#endif + +namespace crashpad { +namespace test { + +//! \brief Maintains ownership of a loadable module handle, releasing it as +//! appropriate on destruction. +class ScopedModuleHandle { + private: + class Impl { + public: + Impl() = delete; + Impl(const Impl&) = delete; + Impl& operator=(const Impl&) = delete; + +#if BUILDFLAG(IS_POSIX) + using ModuleHandle = void*; + + static void* LookUpSymbol(ModuleHandle handle, const char* symbol_name) { + return dlsym(handle, symbol_name); + } +#elif BUILDFLAG(IS_WIN) + using ModuleHandle = HMODULE; + + static void* LookUpSymbol(ModuleHandle handle, const char* symbol_name) { + return reinterpret_cast(GetProcAddress(handle, symbol_name)); + } +#endif + + static void Close(ModuleHandle handle); + }; + + public: + using ModuleHandle = Impl::ModuleHandle; + + explicit ScopedModuleHandle(ModuleHandle handle); + ScopedModuleHandle(ScopedModuleHandle&& handle); + + ScopedModuleHandle(const ScopedModuleHandle&) = delete; + ScopedModuleHandle& operator=(const ScopedModuleHandle&) = delete; + + ~ScopedModuleHandle(); + + //! \return The module handle being managed. + ModuleHandle get() const { return handle_; } + + //! \return `true` if this object manages a valid loadable module handle. + bool valid() const { return handle_ != nullptr; } + + //! \return The value of the symbol named by \a symbol_name, or `nullptr` on + //! failure. + template + T LookUpSymbol(const char* symbol_name) const { + return reinterpret_cast(Impl::LookUpSymbol(handle_, symbol_name)); + } + + private: + ModuleHandle handle_; +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_SCOPED_MODULE_HANDLE_H_ diff --git a/shared/sentry/external/crashpad/test/scoped_temp_dir.cc b/shared/sentry/external/crashpad/test/scoped_temp_dir.cc new file mode 100644 index 000000000..7bc870316 --- /dev/null +++ b/shared/sentry/external/crashpad/test/scoped_temp_dir.cc @@ -0,0 +1,53 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "test/scoped_temp_dir.h" + +#include "gtest/gtest.h" +#include "util/file/directory_reader.h" +#include "util/file/file_io.h" +#include "util/file/filesystem.h" + +namespace crashpad { +namespace test { + +ScopedTempDir::ScopedTempDir() : path_(CreateTemporaryDirectory()) { +} + +ScopedTempDir::~ScopedTempDir() { + RecursivelyDeleteTemporaryDirectory(path()); +} + +// static +void ScopedTempDir::RecursivelyDeleteTemporaryDirectory( + const base::FilePath& path) { + DirectoryReader reader; + ASSERT_TRUE(reader.Open(path)); + DirectoryReader::Result result; + base::FilePath entry; + while ((result = reader.NextFile(&entry)) == + DirectoryReader::Result::kSuccess) { + const base::FilePath entry_path(path.Append(entry)); + if (IsDirectory(entry_path, false)) { + RecursivelyDeleteTemporaryDirectory(entry_path); + } else { + EXPECT_TRUE(LoggingRemoveFile(entry_path)); + } + } + EXPECT_EQ(result, DirectoryReader::Result::kNoMoreFiles); + EXPECT_TRUE(LoggingRemoveDirectory(path)); +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/scoped_temp_dir.h b/shared/sentry/external/crashpad/test/scoped_temp_dir.h new file mode 100644 index 000000000..a13319025 --- /dev/null +++ b/shared/sentry/external/crashpad/test/scoped_temp_dir.h @@ -0,0 +1,65 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_SCOPED_TEMP_DIR_ +#define CRASHPAD_TEST_SCOPED_TEMP_DIR_ + +#include "base/files/file_path.h" + +namespace crashpad { +namespace test { + +//! \brief A RAII object that creates a temporary directory for testing. +//! +//! Upon construction, a temporary directory will be created. Failure to create +//! the directory is fatal. On destruction, the directory and all its contents +//! will be removed. +class ScopedTempDir { + public: + ScopedTempDir(); + + ScopedTempDir(const ScopedTempDir&) = delete; + ScopedTempDir& operator=(const ScopedTempDir&) = delete; + + ~ScopedTempDir(); + + //! \brief Returns the path of the temporary directory. + //! + //! \return The temporary directory path. + const base::FilePath& path() const { return path_; } + + //! \brief Moves the temporary directory to a new temporary location. + void Rename(); + + private: + //! \brief Creates the temporary directory and asserts success of the + //! operation. + static base::FilePath CreateTemporaryDirectory(); + + //! \brief Removes all files and subdirectories at the given \a path, + //! including the \a path itself. + //! + //! Failures are recorded by Google Test expectations. + //! + //! \param[in] path The path to delete, along with its contents. This must + //! reference a directory. + static void RecursivelyDeleteTemporaryDirectory(const base::FilePath& path); + + base::FilePath path_; +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_SCOPED_TEMP_DIR_ diff --git a/shared/sentry/external/crashpad/test/scoped_temp_dir_posix.cc b/shared/sentry/external/crashpad/test/scoped_temp_dir_posix.cc new file mode 100644 index 000000000..848ab5692 --- /dev/null +++ b/shared/sentry/external/crashpad/test/scoped_temp_dir_posix.cc @@ -0,0 +1,62 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "test/scoped_temp_dir.h" + +#include +#include +#include +#include + +#include + +#include "base/check.h" +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "test/errors.h" + +namespace crashpad { +namespace test { + +void ScopedTempDir::Rename() { + base::FilePath move_to = CreateTemporaryDirectory(); + PCHECK(rename(path_.value().c_str(), move_to.value().c_str()) == 0); + path_ = move_to; +} + +// static +base::FilePath ScopedTempDir::CreateTemporaryDirectory() { + char* tmpdir = getenv("TMPDIR"); + std::string dir; + if (tmpdir && tmpdir[0] != '\0') { + dir.assign(tmpdir); + } else { +#if BUILDFLAG(IS_ANDROID) + dir.assign("/data/local/tmp"); +#else + dir.assign("/tmp"); +#endif + } + + if (dir[dir.size() - 1] != '/') { + dir.append(1, '/'); + } + dir.append("org.chromium.crashpad.test.XXXXXX"); + + PCHECK(mkdtemp(&dir[0])) << "mkdtemp " << dir; + return base::FilePath(dir); +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/scoped_temp_dir_test.cc b/shared/sentry/external/crashpad/test/scoped_temp_dir_test.cc new file mode 100644 index 000000000..3b5a31642 --- /dev/null +++ b/shared/sentry/external/crashpad/test/scoped_temp_dir_test.cc @@ -0,0 +1,124 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "test/scoped_temp_dir.h" + +#include +#include + +#include "base/posix/eintr_wrapper.h" +#include "build/build_config.h" +#include "gtest/gtest.h" +#include "test/errors.h" +#include "test/file.h" + +#if BUILDFLAG(IS_POSIX) +#include +#elif BUILDFLAG(IS_WIN) +#include +#include +#endif // BUILDFLAG(IS_POSIX) + +namespace crashpad { +namespace test { +namespace { + +void CreateFile(const base::FilePath& path) { +#if BUILDFLAG(IS_POSIX) + int fd = HANDLE_EINTR(creat(path.value().c_str(), 0644)); + ASSERT_GE(fd, 0) << ErrnoMessage("creat") << " " << path.value(); + + // gcc refuses to compile ASSERT_EQ(IGNORE_EINTR(close(fd)), 0). + int close_rv = IGNORE_EINTR(close(fd)); + ASSERT_EQ(close_rv, 0) << ErrnoMessage("close") << " " << path.value(); +#elif BUILDFLAG(IS_WIN) + int fd = _wcreat(path.value().c_str(), _S_IREAD | _S_IWRITE); + ASSERT_GE(fd, 0) << ErrnoMessage("_wcreat") << " " << path.value(); + ASSERT_EQ(_close(fd), 0) << ErrnoMessage("_close") << " " << path.value(); +#else +#error "Not implemented" +#endif + EXPECT_TRUE(FileExists(path)); +} + +void CreateDirectory(const base::FilePath& path) { +#if BUILDFLAG(IS_POSIX) + ASSERT_EQ(mkdir(path.value().c_str(), 0755), 0) << ErrnoMessage("mkdir") + << " " << path.value(); +#elif BUILDFLAG(IS_WIN) + ASSERT_EQ(_wmkdir(path.value().c_str()), 0) << ErrnoMessage("_wmkdir") << " " + << path.value(); +#else +#error "Not implemented" +#endif + ASSERT_TRUE(FileExists(path)); +} + +TEST(ScopedTempDir, Empty) { + base::FilePath path; + { + ScopedTempDir dir; + path = dir.path(); + EXPECT_TRUE(FileExists(path)); + } + EXPECT_FALSE(FileExists(path)); +} + +TEST(ScopedTempDir, WithTwoFiles) { + base::FilePath parent, file1, file2; + + { + ScopedTempDir dir; + parent = dir.path(); + ASSERT_TRUE(FileExists(parent)); + + file1 = parent.Append(FILE_PATH_LITERAL("test1")); + CreateFile(file1); + + file2 = parent.Append(FILE_PATH_LITERAL("test 2")); + CreateFile(file2); + } + + EXPECT_FALSE(FileExists(file1)); + EXPECT_FALSE(FileExists(file2)); + EXPECT_FALSE(FileExists(parent)); +} + +TEST(ScopedTempDir, WithRecursiveDirectory) { + base::FilePath parent, file1, child_dir, file2; + + { + ScopedTempDir dir; + parent = dir.path(); + ASSERT_TRUE(FileExists(parent)); + + file1 = parent.Append(FILE_PATH_LITERAL(".first-level file")); + CreateFile(file1); + + child_dir = parent.Append(FILE_PATH_LITERAL("subdir")); + CreateDirectory(child_dir); + + file2 = child_dir.Append(FILE_PATH_LITERAL("second level file")); + CreateFile(file2); + } + + EXPECT_FALSE(FileExists(file1)); + EXPECT_FALSE(FileExists(file2)); + EXPECT_FALSE(FileExists(child_dir)); + EXPECT_FALSE(FileExists(parent)); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/scoped_temp_dir_win.cc b/shared/sentry/external/crashpad/test/scoped_temp_dir_win.cc new file mode 100644 index 000000000..10063dc7e --- /dev/null +++ b/shared/sentry/external/crashpad/test/scoped_temp_dir_win.cc @@ -0,0 +1,78 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "test/scoped_temp_dir.h" + +#include +#include + +#include + +#include "base/check.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "gtest/gtest.h" +#include "util/misc/random_string.h" + +namespace crashpad { +namespace test { + +namespace { + +base::FilePath GenerateCandidateName() { + wchar_t temp_path[MAX_PATH + 1]; + DWORD path_len = GetTempPath(MAX_PATH, temp_path); + PCHECK(path_len != 0) << "GetTempPath"; + base::FilePath system_temp_dir(temp_path); + std::wstring new_dir_name = base::UTF8ToWide(base::StringPrintf( + "crashpad.test.%lu.%s", GetCurrentProcessId(), RandomString().c_str())); + return system_temp_dir.Append(new_dir_name); +} + +constexpr int kRetries = 50; + +} // namespace + +void ScopedTempDir::Rename() { + for (int count = 0; count < kRetries; ++count) { + // Try to move to a new temporary directory with a randomly generated name. + // If the one we try exists, retry with a new name until we reach some + // limit. + base::FilePath target_path = GenerateCandidateName(); + if (MoveFileEx(path_.value().c_str(), target_path.value().c_str(), 0)) { + path_ = target_path; + return; + } + } + + CHECK(false) << "Couldn't move to a new unique temp dir"; +} + +// static +base::FilePath ScopedTempDir::CreateTemporaryDirectory() { + for (int count = 0; count < kRetries; ++count) { + // Try to create a new temporary directory with random generated name. If + // the one we generate exists, keep trying another path name until we reach + // some limit. + base::FilePath path_to_create = GenerateCandidateName(); + if (CreateDirectory(path_to_create.value().c_str(), nullptr)) + return path_to_create; + } + + CHECK(false) << "Couldn't create a new unique temp dir"; + return base::FilePath(); +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/test_paths.cc b/shared/sentry/external/crashpad/test/test_paths.cc new file mode 100644 index 000000000..c55aa647b --- /dev/null +++ b/shared/sentry/external/crashpad/test/test_paths.cc @@ -0,0 +1,245 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "test/test_paths.h" + +#include +#include + +#include "base/logging.h" +#include "build/build_config.h" +#include "util/misc/paths.h" + +namespace crashpad { +namespace test { + +namespace { + +bool IsTestDataRoot(const base::FilePath& candidate) { + const base::FilePath marker_path = + candidate.Append(FILE_PATH_LITERAL("test")) + .Append(FILE_PATH_LITERAL("test_paths_test_data_root.txt")); + +#if !BUILDFLAG(IS_WIN) + struct stat stat_buf; + int rv = stat(marker_path.value().c_str(), &stat_buf); +#else + struct _stat stat_buf; + int rv = _wstat(marker_path.value().c_str(), &stat_buf); +#endif + + return rv == 0; +} + +base::FilePath TestDataRootInternal() { +#if BUILDFLAG(IS_FUCHSIA) + base::FilePath asset_path("/pkg/data"); + if (!IsTestDataRoot(asset_path)) { + LOG(WARNING) << "test data root seems invalid, continuing anyway"; + } + return asset_path; +#else // BUILDFLAG(IS_FUCHSIA) +#if !BUILDFLAG(IS_WIN) + const char* environment_value = getenv("CRASHPAD_TEST_DATA_ROOT"); +#else // BUILDFLAG(IS_WIN) + const wchar_t* environment_value = _wgetenv(L"CRASHPAD_TEST_DATA_ROOT"); +#endif + + if (environment_value) { + // It was specified explicitly, so use it even if it seems incorrect. + if (!IsTestDataRoot(base::FilePath(environment_value))) { + LOG(WARNING) << "CRASHPAD_TEST_DATA_ROOT seems invalid, honoring anyway"; + } + + return base::FilePath(environment_value); + } + + base::FilePath executable_path; + if (Paths::Executable(&executable_path)) { +#if BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID) + // On Android and iOS, test data is in a crashpad_test_data directory + // adjacent to the main executable. On iOS, this refers to the main + // executable file inside the .app bundle, so crashpad_test_data is also + // inside the bundle. + base::FilePath candidate = executable_path.DirName() + .Append("crashpad_test_data"); +#else // BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDRID) + // In a standalone build, the test executable is usually at + // out/{Debug,Release} relative to the Crashpad root. + base::FilePath candidate = + base::FilePath(executable_path.DirName() + .Append(base::FilePath::kParentDirectory) + .Append(base::FilePath::kParentDirectory)); +#endif // BUILDFLAG(IS_IOS) || BUILDFLAG(IS_ANDROID) + if (IsTestDataRoot(candidate)) { + return candidate; + } + + // In an in-Chromium build, the test executable is usually at + // out/{Debug,Release} relative to the Chromium root, and the Crashpad root + // is at third_party/crashpad/crashpad relative to the Chromium root. + candidate = candidate.Append(FILE_PATH_LITERAL("third_party")) + .Append(FILE_PATH_LITERAL("crashpad")) + .Append(FILE_PATH_LITERAL("crashpad")); + if (IsTestDataRoot(candidate)) { + return candidate; + } + } + + // If nothing else worked, use the current directory, issuing a warning if it + // doesn’t seem right. + if (!IsTestDataRoot(base::FilePath(base::FilePath::kCurrentDirectory))) { + LOG(WARNING) << "could not locate a valid test data root"; + } + + return base::FilePath(base::FilePath::kCurrentDirectory); +#endif // BUILDFLAG(IS_FUCHSIA) +} + +#if BUILDFLAG(IS_WIN) && defined(ARCH_CPU_64_BITS) + +// Returns the pathname of a directory containing 32-bit test build output. +// +// It would be better for this to be named 32BitOutputDirectory(), but that’s +// not a legal name. +base::FilePath Output32BitDirectory() { + const wchar_t* environment_value = _wgetenv(L"CRASHPAD_TEST_32_BIT_OUTPUT"); + if (!environment_value) { + return base::FilePath(); + } + + return base::FilePath(environment_value); +} + +#endif // BUILDFLAG(IS_WIN) && defined(ARCH_CPU_64_BITS) + +} // namespace + +// static +base::FilePath TestPaths::Executable() { + base::FilePath executable_path; + CHECK(Paths::Executable(&executable_path)); +#if defined(CRASHPAD_IS_IN_FUCHSIA) + executable_path = base::FilePath("/pkg/bin/app"); +#endif + return executable_path; +} + +// static +base::FilePath TestPaths::ExpectedExecutableBasename( + const base::FilePath::StringType& name) { +#if BUILDFLAG(IS_FUCHSIA) + // Apps in Fuchsia packages are always named "app". + return base::FilePath("app"); +#else // BUILDFLAG(IS_FUCHSIA) +#if defined(CRASHPAD_IS_IN_CHROMIUM) + base::FilePath::StringType executable_name( + FILE_PATH_LITERAL("crashpad_tests")); +#else // CRASHPAD_IS_IN_CHROMIUM + base::FilePath::StringType executable_name(name); +#endif // CRASHPAD_IS_IN_CHROMIUM + +#if BUILDFLAG(IS_WIN) + executable_name += FILE_PATH_LITERAL(".exe"); +#endif // BUILDFLAG(IS_WIN) + + return base::FilePath(executable_name); +#endif // BUILDFLAG(IS_FUCHSIA) +} + +// static +base::FilePath TestPaths::TestDataRoot() { + static base::FilePath* test_data_root = + new base::FilePath(TestDataRootInternal()); + return *test_data_root; +} + +// static +base::FilePath TestPaths::BuildArtifact( + const base::FilePath::StringType& module, + const base::FilePath::StringType& artifact, + FileType file_type, + Architecture architecture) { + base::FilePath directory; + switch (architecture) { + case Architecture::kDefault: + directory = Executable().DirName(); + break; + +#if BUILDFLAG(IS_WIN) && defined(ARCH_CPU_64_BITS) + case Architecture::k32Bit: + directory = Output32BitDirectory(); + CHECK(!directory.empty()); + break; +#endif // BUILDFLAG(IS_WIN) && ARCH_CPU_64_BITS + } + + base::FilePath::StringType test_name = + FILE_PATH_LITERAL("crashpad_") + module + FILE_PATH_LITERAL("_test"); +#if !defined(CRASHPAD_IS_IN_CHROMIUM) && !BUILDFLAG(IS_FUCHSIA) + CHECK(Executable().BaseName().RemoveFinalExtension().value() == test_name); +#endif // !CRASHPAD_IS_IN_CHROMIUM + + base::FilePath::StringType extension; + switch (file_type) { + case FileType::kNone: + break; + + case FileType::kExecutable: +#if BUILDFLAG(IS_WIN) + extension = FILE_PATH_LITERAL(".exe"); +#elif BUILDFLAG(IS_FUCHSIA) + directory = base::FilePath(FILE_PATH_LITERAL("/pkg/bin")); +#endif // BUILDFLAG(IS_WIN) + break; + + case FileType::kLoadableModule: +#if BUILDFLAG(IS_WIN) + extension = FILE_PATH_LITERAL(".dll"); +#else // BUILDFLAG(IS_WIN) + extension = FILE_PATH_LITERAL(".so"); +#endif // BUILDFLAG(IS_WIN) + +#if BUILDFLAG(IS_FUCHSIA) + // TODO(scottmg): .so files are currently deployed into /boot/lib, where + // they'll be found (without a path) by the loader. Application packaging + // infrastructure is in progress, so this will likely change again in the + // future. + directory = base::FilePath(); +#endif + break; + + case FileType::kCertificate: +#if defined(CRASHPAD_IS_IN_FUCHSIA) + directory = base::FilePath(FILE_PATH_LITERAL("/pkg/data")); +#endif + extension = FILE_PATH_LITERAL(".pem"); + break; + } + + return directory.Append(test_name + FILE_PATH_LITERAL("_") + artifact + + extension); +} + +#if BUILDFLAG(IS_WIN) && defined(ARCH_CPU_64_BITS) + +// static +bool TestPaths::Has32BitBuildArtifacts() { + return !Output32BitDirectory().empty(); +} + +#endif // BUILDFLAG(IS_WIN) && defined(ARCH_CPU_64_BITS) + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/test_paths.h b/shared/sentry/external/crashpad/test/test_paths.h new file mode 100644 index 000000000..0fc021e13 --- /dev/null +++ b/shared/sentry/external/crashpad/test/test_paths.h @@ -0,0 +1,151 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_TEST_PATHS_H_ +#define CRASHPAD_TEST_TEST_PATHS_H_ + +#include "base/files/file_path.h" +#include "build/build_config.h" + +namespace crashpad { +namespace test { + +//! \brief Functions to obtain paths from within tests. +class TestPaths { + public: + //! \brief The type of file requested of BuildArtifact(). + //! + //! This is used to establish the file extension used by the returned path. + enum class FileType { + //! \brief No file extension is requested. + kNone = 0, + + //! \brief `.exe` will be used on Windows, and no file extension will be + //! used on other platforms. + kExecutable, + + //! \brief `.dll` will be used on Windows, and `.so` will be used on other + //! platforms. + kLoadableModule, + + //! \brief `.pem` used for all platforms. + kCertificate, + }; + + //! \brief The architecture of the file requested of BuildArtifact(). + enum class Architecture { + //! \brief The default architecture is requested. This is usually the same + //! architecture as the running process. + kDefault = 0, + +#if (BUILDFLAG(IS_WIN) && defined(ARCH_CPU_64_BITS)) || DOXYGEN + //! \brief The 32-bit variant is requested. + //! + //! On Windows, when running 64-bit code, the 32-bit variant can be + //! requested. Before doing so, Has32BitBuildArtifacts() must be called and + //! must return `true`. Otherwise, execution will be aborted. + k32Bit, +#endif // BUILDFLAG(IS_WIN) && ARCH_CPU_64_BITS + }; + + TestPaths() = delete; + TestPaths(const TestPaths&) = delete; + TestPaths& operator=(const TestPaths&) = delete; + + //! \brief Returns the pathname of the currently-running test executable. + //! + //! On failure, aborts execution. + static base::FilePath Executable(); + + //! \brief Returns the expected basename of the currently-running test + //! executable. + //! + //! In Crashpad’s standalone build, this returns \a name, with the system’s + //! extension for executables (`.exe`) appended if appropriate. + //! + //! When building in Chromium, \a name is ignored, and the name of the + //! monolithic test executable (`crashpad_tests`) is returned, with the + //! system’s extension for executables appended if appropriate. + //! + //! Only use this function to determine test expectations. + //! + //! Do not use this function to obtain the name of the currently running test + //! executable, use Executable() instead. Do not use this function to locate + //! other build artifacts, use BuildArtifact() instead. + static base::FilePath ExpectedExecutableBasename( + const base::FilePath::StringType& name); + + //! \brief Returns the pathname of the test data root. + //! + //! If the `CRASHPAD_TEST_DATA_ROOT` environment variable is set, its value + //! will be returned. Otherwise, this function will attempt to locate the test + //! data root relative to the executable path. If this fails, it will fall + //! back to returning the current working directory. + //! + //! At present, the test data root is normally the root of the Crashpad source + //! tree, although this may not be the case indefinitely. This function may + //! only be used to locate test data, not for arbitrary access to source + //! files. + static base::FilePath TestDataRoot(); + + //! \brief Returns the pathname of a build artifact. + //! + //! \param[in] module The name of the Crashpad module associated with the + //! artifact, such as `"util"` or `"snapshot"`. \a module must correspond + //! to the module of the calling code, or execution will be aborted. + //! \param[in] artifact The name of the specific artifact. + //! \param[in] file_type The artifact’s type, used to establish the returned + //! path’s extension. + //! \param[in] architecture The artifact’s architecture. + //! + //! \return The computed pathname to the build artifact. + //! + //! For example, the following snippet will return a path to + //! `crashpad_snapshot_test_module.so` or `crashpad_snapshot_test_module.dll` + //! (depending on platform) in the same directory as the currently running + //! executable: + //! + //! \code + //! base::FilePath path = TestPaths::BuildArtifact( + //! FILE_PATH_LITERAL("snapshot"), + //! FILE_PATH_LITERAL("module"), + //! TestPaths::FileType::kLoadableModule); + //! \endcode + static base::FilePath BuildArtifact( + const base::FilePath::StringType& module, + const base::FilePath::StringType& artifact, + FileType file_type, + Architecture architecture = Architecture::kDefault); + +#if (BUILDFLAG(IS_WIN) && defined(ARCH_CPU_64_BITS)) || DOXYGEN + //! \return `true` if 32-bit build artifacts are available. + //! + //! Tests that require the use of 32-bit build output should call this + //! function to determine whether that output is available. This function is + //! only provided to aid 64-bit test code in locating 32-bit output. Only if + //! this function indicates that 32-bit output is available, 64-bit test code + //! may call BuildArtifact() with Architecture::k32Bit to obtain a path to the + //! 32-bit output. + //! + //! 32-bit test code may assume the existence of 32-bit build output, which + //! can be found its own directory, and located by calling BuildArtifact() + //! with Architecture::kDefault. + static bool Has32BitBuildArtifacts(); +#endif // BUILDFLAG(IS_WIN) && ARCH_CPU_64_BITS +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_TEST_PATHS_H_ diff --git a/shared/sentry/external/crashpad/test/test_paths_test.cc b/shared/sentry/external/crashpad/test/test_paths_test.cc new file mode 100644 index 000000000..9d43b594f --- /dev/null +++ b/shared/sentry/external/crashpad/test/test_paths_test.cc @@ -0,0 +1,35 @@ +// Copyright 2014 The Crashpad Authors. All rights reserved. +// +// 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 "test/test_paths.h" + +#include "base/files/file_path.h" +#include "gtest/gtest.h" +#include "util/file/file_io.h" + +namespace crashpad { +namespace test { +namespace { + +TEST(TestPaths, TestDataRoot) { + base::FilePath test_data_root = TestPaths::TestDataRoot(); + ScopedFileHandle file(LoggingOpenFileForRead( + test_data_root.Append(FILE_PATH_LITERAL("test")) + .Append(FILE_PATH_LITERAL("test_paths_test_data_root.txt")))); + EXPECT_TRUE(file.is_valid()); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/test_paths_test_data_root.txt b/shared/sentry/external/crashpad/test/test_paths_test_data_root.txt new file mode 100644 index 000000000..6ac07806b --- /dev/null +++ b/shared/sentry/external/crashpad/test/test_paths_test_data_root.txt @@ -0,0 +1,16 @@ +# Copyright 2015 The Crashpad Authors. All rights reserved. +# +# 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. + +This file is used by TestPaths::TestDataRoot() to locate the test data root. It +is present at a known path from the test data root. diff --git a/shared/sentry/external/crashpad/test/win/child_launcher.cc b/shared/sentry/external/crashpad/test/win/child_launcher.cc new file mode 100644 index 000000000..abd2e3d94 --- /dev/null +++ b/shared/sentry/external/crashpad/test/win/child_launcher.cc @@ -0,0 +1,109 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "test/win/child_launcher.h" + +#include "gtest/gtest.h" +#include "test/errors.h" +#include "util/win/command_line.h" + +namespace crashpad { +namespace test { + +ChildLauncher::ChildLauncher(const base::FilePath& executable, + const std::wstring& command_line) + : executable_(executable), + command_line_(command_line), + process_handle_(), + main_thread_handle_(), + stdout_read_handle_(), + stdin_write_handle_() {} + +ChildLauncher::~ChildLauncher() { + if (process_handle_.is_valid()) + WaitForExit(); +} + +void ChildLauncher::Start() { + ASSERT_FALSE(process_handle_.is_valid()); + ASSERT_FALSE(main_thread_handle_.is_valid()); + ASSERT_FALSE(stdout_read_handle_.is_valid()); + + // Create pipes for the stdin/stdout of the child. + SECURITY_ATTRIBUTES security_attributes = {0}; + security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); + security_attributes.bInheritHandle = true; + + HANDLE stdout_read; + HANDLE stdout_write; + ASSERT_TRUE(CreatePipe(&stdout_read, &stdout_write, &security_attributes, 0)) + << ErrorMessage("CreatePipe"); + stdout_read_handle_.reset(stdout_read); + ScopedFileHANDLE write_handle(stdout_write); + ASSERT_TRUE( + SetHandleInformation(stdout_read_handle_.get(), HANDLE_FLAG_INHERIT, 0)) + << ErrorMessage("SetHandleInformation"); + + HANDLE stdin_read; + HANDLE stdin_write; + ASSERT_TRUE(CreatePipe(&stdin_read, &stdin_write, &security_attributes, 0)) + << ErrorMessage("CreatePipe"); + stdin_write_handle_.reset(stdin_write); + ScopedFileHANDLE read_handle(stdin_read); + ASSERT_TRUE( + SetHandleInformation(stdin_write_handle_.get(), HANDLE_FLAG_INHERIT, 0)) + << ErrorMessage("SetHandleInformation"); + + STARTUPINFO startup_info = {0}; + startup_info.cb = sizeof(startup_info); + startup_info.hStdInput = read_handle.get(); + startup_info.hStdOutput = write_handle.get(); + startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); + EXPECT_NE(startup_info.hStdError, INVALID_HANDLE_VALUE) + << ErrorMessage("GetStdHandle"); + startup_info.dwFlags = STARTF_USESTDHANDLES; + PROCESS_INFORMATION process_information; + std::wstring command_line; + AppendCommandLineArgument(executable_.value(), &command_line); + command_line += L" "; + command_line += command_line_; + ASSERT_TRUE(CreateProcess(executable_.value().c_str(), + &command_line[0], + nullptr, + nullptr, + true, + 0, + nullptr, + nullptr, + &startup_info, + &process_information)) + << ErrorMessage("CreateProcess"); + // Take ownership of the two process handles returned. + main_thread_handle_.reset(process_information.hThread); + process_handle_.reset(process_information.hProcess); +} + +DWORD ChildLauncher::WaitForExit() { + EXPECT_TRUE(process_handle_.is_valid()); + EXPECT_EQ(WaitForSingleObject(process_handle_.get(), INFINITE), WAIT_OBJECT_0) + << ErrorMessage("WaitForSingleObject"); + DWORD exit_code = 0; + EXPECT_TRUE(GetExitCodeProcess(process_handle_.get(), &exit_code)) + << ErrorMessage("GetExitCodeProcess"); + process_handle_.reset(); + return exit_code; +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/win/child_launcher.h b/shared/sentry/external/crashpad/test/win/child_launcher.h new file mode 100644 index 000000000..273933e06 --- /dev/null +++ b/shared/sentry/external/crashpad/test/win/child_launcher.h @@ -0,0 +1,77 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_WIN_CHILD_LAUNCHER_H_ +#define CRASHPAD_TEST_WIN_CHILD_LAUNCHER_H_ + +#include + +#include + +#include "base/files/file_path.h" +#include "util/win/scoped_handle.h" + +namespace crashpad { +namespace test { + +//! \brief Creates a child process for testing. Uses Google Test `ASSERT_*` to +//! indicate failure. The child's output is passed through a pipe and is +//! available via stdout_read_handle(), and the child's input is attached to +//! a second pipe available via stdin_write_handle(). +class ChildLauncher { + public: + //! \brief Creates the object. \a executable will be escaped and prepended to + //! \a command_line to build the command line of the child. + ChildLauncher(const base::FilePath& executable, + const std::wstring& command_line); + + ~ChildLauncher(); + + //! \brief Starts the child process, after which the handle functions below + //! will be valid. + //! + //! Errors are signaled via Google Test assertions. This method may be invoked + //! via `ASSERT_NO_FATAL_FAILURE()` to assert that it succeeds. + void Start(); + + //! \brief Waits for the child process to exit. + //! + //! \return The process exit code. + DWORD WaitForExit(); + + //! \brief The child process's `HANDLE`. + HANDLE process_handle() const { return process_handle_.get(); } + + //! \brief The child process's main thread's `HANDLE`. + HANDLE main_thread_handle() const { return main_thread_handle_.get(); } + + //! \brief The read end of a pipe attached to the child's stdout. + HANDLE stdout_read_handle() const { return stdout_read_handle_.get(); } + + //! \brief The write end of a pipe attached to the child's stdin. + HANDLE stdin_write_handle() const { return stdin_write_handle_.get(); } + + private: + base::FilePath executable_; + std::wstring command_line_; + ScopedKernelHANDLE process_handle_; + ScopedKernelHANDLE main_thread_handle_; + ScopedFileHANDLE stdout_read_handle_; + ScopedFileHANDLE stdin_write_handle_; +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_WIN_CHILD_LAUNCHER_H_ diff --git a/shared/sentry/external/crashpad/test/win/win_child_process.cc b/shared/sentry/external/crashpad/test/win/win_child_process.cc new file mode 100644 index 000000000..a203d9d40 --- /dev/null +++ b/shared/sentry/external/crashpad/test/win/win_child_process.cc @@ -0,0 +1,239 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "test/win/win_child_process.h" + +#include +#include + +#include +#include + +#include "base/logging.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" +#include "gtest/gtest.h" +#include "test/test_paths.h" +#include "util/stdlib/string_number_conversion.h" +#include "util/string/split_string.h" +#include "util/win/handle.h" +#include "util/win/scoped_local_alloc.h" + +namespace crashpad { +namespace test { + +namespace { + +constexpr char kIsMultiprocessChild[] = "--is-multiprocess-child"; + +bool GetSwitch(const char* switch_name, std::string* value) { + int num_args; + wchar_t** args = CommandLineToArgvW(GetCommandLine(), &num_args); + ScopedLocalAlloc scoped_args(args); // Take ownership. + if (!args) { + PLOG(FATAL) << "CommandLineToArgvW"; + return false; + } + + std::string switch_name_with_equals(switch_name); + switch_name_with_equals += "="; + for (int i = 1; i < num_args; ++i) { + const wchar_t* arg = args[i]; + std::string arg_as_utf8 = base::WideToUTF8(arg); + if (arg_as_utf8.compare( + 0, switch_name_with_equals.size(), switch_name_with_equals) == 0) { + if (value) + *value = arg_as_utf8.substr(switch_name_with_equals.size()); + return true; + } + } + + return false; +} + +ScopedKernelHANDLE LaunchCommandLine(wchar_t* command_line) { + STARTUPINFO startup_info = {0}; + startup_info.cb = sizeof(startup_info); + startup_info.hStdInput = GetStdHandle(STD_INPUT_HANDLE); + startup_info.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); + startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE); + startup_info.dwFlags = STARTF_USESTDHANDLES; + PROCESS_INFORMATION process_info; + if (!CreateProcess(TestPaths::Executable().value().c_str(), + &command_line[0], // This cannot be constant, per MSDN. + nullptr, + nullptr, + true, // Inherit handles. + 0, + nullptr, + nullptr, + &startup_info, + &process_info)) { + PLOG(ERROR) << "CreateProcess"; + return ScopedKernelHANDLE(); + } + if (!CloseHandle(process_info.hThread)) { + PLOG(ERROR) << "CloseHandle"; + if (!CloseHandle(process_info.hProcess)) + PLOG(ERROR) << "CloseHandle"; + return ScopedKernelHANDLE(); + } + return ScopedKernelHANDLE(process_info.hProcess); +} + +bool UnsetHandleInheritance(HANDLE handle) { + if (!SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0)) { + PLOG(ERROR) << "SetHandleInformation"; + ADD_FAILURE() << "SetHandleInformation"; + return false; + } + return true; +} + +bool CreateInheritablePipe(ScopedFileHANDLE* read_handle, + bool read_inheritable, + ScopedFileHANDLE* write_handle, + bool write_inheritable) { + // Mark both sides as inheritable via the SECURITY_ATTRIBUTES and use + // SetHandleInformation as necessary to restrict inheritance of either side. + SECURITY_ATTRIBUTES security_attributes = {0}; + security_attributes.nLength = sizeof(SECURITY_ATTRIBUTES); + security_attributes.bInheritHandle = true; + + HANDLE read, write; + BOOL result = CreatePipe(&read, &write, &security_attributes, 0); + if (!result) { + PLOG(ERROR) << "CreatePipe"; + ADD_FAILURE() << "CreatePipe failed"; + return false; + } + ScopedFileHANDLE temp_read(read); + ScopedFileHANDLE temp_write(write); + + if (!read_inheritable && !UnsetHandleInheritance(temp_read.get())) + return false; + if (!write_inheritable && !UnsetHandleInheritance(temp_write.get())) + return false; + + *read_handle = std::move(temp_read); + *write_handle = std::move(temp_write); + + return true; +} + +} // namespace + +WinChildProcess::WinChildProcess() { + std::string switch_value; + CHECK(GetSwitch(kIsMultiprocessChild, &switch_value)); + + // Set up the handles we inherited from the parent. These are inherited from + // the parent and so are open and have the same value as in the parent. The + // values are passed to the child on the command line. + std::string left, right; + CHECK(SplitStringFirst(switch_value, '|', &left, &right)); + + // left and right were formatted as 0x%x, so they need to be converted as + // unsigned ints. + unsigned int write, read; + CHECK(StringToNumber(left, &write)); + CHECK(StringToNumber(right, &read)); + + pipe_write_.reset(IntToHandle(write)); + pipe_read_.reset(IntToHandle(read)); + + // Notify the parent that it's OK to proceed. We only need to wait to get to + // the process entry point, but this is the easiest place we can notify. + char c = ' '; + CheckedWriteFile(WritePipeHandle(), &c, sizeof(c)); +} + +// static +bool WinChildProcess::IsChildProcess() { + return GetSwitch(kIsMultiprocessChild, nullptr); +} + +// static +std::unique_ptr WinChildProcess::Launch() { + // Make pipes for child-to-parent and parent-to-child communication. + std::unique_ptr handles_for_parent(new Handles); + ScopedFileHANDLE read_for_child; + ScopedFileHANDLE write_for_child; + + if (!CreateInheritablePipe( + &handles_for_parent->read, false, &write_for_child, true)) { + return std::unique_ptr(); + } + + if (!CreateInheritablePipe( + &read_for_child, true, &handles_for_parent->write, false)) { + return std::unique_ptr(); + } + + // Build a command line for the child process that tells it only to run the + // current test, and to pass down the values of the pipe handles. Use + // --gtest_also_run_disabled_tests because the test may be DISABLED_, but if + // it managed to run in the parent, disabled tests must be running. + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance()->current_test_info(); + std::wstring command_line = + TestPaths::Executable().value() + + base::UTF8ToWide(base::StringPrintf( + " --gtest_filter=%s.%s %s=0x%x|0x%x --gtest_also_run_disabled_tests", + test_info->test_case_name(), + test_info->name(), + kIsMultiprocessChild, + HandleToInt(write_for_child.get()), + HandleToInt(read_for_child.get()))); + + // Command-line buffer cannot be constant, per CreateProcess signature. + handles_for_parent->process = LaunchCommandLine(&command_line[0]); + if (!handles_for_parent->process.is_valid()) + return std::unique_ptr(); + + // Block until the child process has launched. CreateProcess() returns + // immediately, and test code expects process initialization to have + // completed so it can, for example, read the process memory. + char c; + if (!LoggingReadFileExactly(handles_for_parent->read.get(), &c, sizeof(c))) { + ADD_FAILURE() << "LoggedReadFile"; + return std::unique_ptr(); + } + + if (c != ' ') { + ADD_FAILURE() << "invalid data read from child"; + return std::unique_ptr(); + } + + return handles_for_parent; +} + +FileHandle WinChildProcess::ReadPipeHandle() const { + return pipe_read_.get(); +} + +FileHandle WinChildProcess::WritePipeHandle() const { + return pipe_write_.get(); +} + +void WinChildProcess::CloseReadPipe() { + pipe_read_.reset(); +} + +void WinChildProcess::CloseWritePipe() { + pipe_write_.reset(); +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/win/win_child_process.h b/shared/sentry/external/crashpad/test/win/win_child_process.h new file mode 100644 index 000000000..a93f3ca82 --- /dev/null +++ b/shared/sentry/external/crashpad/test/win/win_child_process.h @@ -0,0 +1,120 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_WIN_WIN_CHILD_PROCESS_H_ +#define CRASHPAD_TEST_WIN_WIN_CHILD_PROCESS_H_ + +#include + +#include "util/file/file_io.h" +#include "util/win/scoped_handle.h" + +namespace crashpad { +namespace test { + +//! \brief Facilitates the launching of child processes from unit tests. +class WinChildProcess { + public: + //! \brief Groups handles used to communicate with, observe, and manage a + //! child process. + struct Handles { + //! \brief A handle to read from an anonymous pipe shared with the child + //! process. + ScopedFileHANDLE read; + //! \brief A handle to write to an anonymous pipe shared with the child + //! process. + ScopedFileHANDLE write; + //! \brief A handle to the child process. + ScopedKernelHANDLE process; + }; + + WinChildProcess(); + + WinChildProcess(const WinChildProcess&) = delete; + WinChildProcess& operator=(const WinChildProcess&) = delete; + + virtual ~WinChildProcess() {} + + //! \brief Returns true if the current process is a child process. + static bool IsChildProcess(); + + //! \brief Runs the child process defined by T if the current process is a + //! child process; does not return in that case. Otherwise, returns. + template + static void EntryPoint() { + if (IsChildProcess()) { + // The static_cast here will cause a compiler failure if T is not a + // subclass of WinChildProcess. It's the constructor of WinChildProcess + // that performs the pipe handshake with the parent process (without which + // we would have a hang). + T child_process; + int result = static_cast(&child_process)->Run(); + exit(result); + } + } + + //! \brief Launches a child process and returns the Handles for that process. + //! The process is guaranteed to be executing by the time this method + //! returns. Returns `nullptr` and logs a Google Test failure in case of + //! failure. + static std::unique_ptr Launch(); + + protected: + //! \brief Returns a handle to read from an anonymous pipe shared with the + //! parent process. + //! + //! It is an error to call this after CloseReadPipe() has been called. + //! + //! \return The read pipe's file handle. + FileHandle ReadPipeHandle() const; + + //! \brief Returns a handle to write to an anonymous pipe shared with the + //! parent process. + //! + //! It is an error to call this after CloseWritePipe() has been called. + //! + //! \return The write pipe's file handle. + FileHandle WritePipeHandle() const; + + //! \brief Closes the read pipe. + //! + //! ReadPipeHandle() must not be called after this. + void CloseReadPipe(); + + //! \brief Closes the write pipe. + //! + //! An attempt to read from the read pipe in the parent process will indicate + //! end-of-file. WritePipeHandle() must not be called after this. + void CloseWritePipe(); + + private: + //! \brief The subclass-provided child routine. + //! + //! Subclasses must implement this method to define how the child operates. + //! Subclasses may exit with a failure status by using `LOG(FATAL)`, + //! `abort()`, or similar. They may also exit by returning their exit code + //! from this method. It is up to the client to observe and interpret the + //! child's exit code. + //! + //! \return The child process exit code. + virtual int Run() = 0; + + ScopedFileHANDLE pipe_read_; + ScopedFileHANDLE pipe_write_; +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_WIN_WIN_CHILD_PROCESS_H_ diff --git a/shared/sentry/external/crashpad/test/win/win_child_process_test.cc b/shared/sentry/external/crashpad/test/win/win_child_process_test.cc new file mode 100644 index 000000000..45b2dce5b --- /dev/null +++ b/shared/sentry/external/crashpad/test/win/win_child_process_test.cc @@ -0,0 +1,87 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "test/win/win_child_process.h" + +#include +#include + +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +int ReadInt(HANDLE handle) { + int value = 0; + DWORD bytes_read = 0; + EXPECT_TRUE(::ReadFile(handle, &value, sizeof(value), &bytes_read, nullptr)); + EXPECT_EQ(bytes_read, sizeof(value)); + return value; +} + +void WriteInt(HANDLE handle, int value) { + DWORD bytes_written = 0; + EXPECT_TRUE( + ::WriteFile(handle, &value, sizeof(value), &bytes_written, nullptr)); + EXPECT_EQ(bytes_written, sizeof(value)); +} + +class TestWinChildProcess final : public WinChildProcess { + public: + TestWinChildProcess() : WinChildProcess() {} + + TestWinChildProcess(const TestWinChildProcess&) = delete; + TestWinChildProcess& operator=(const TestWinChildProcess&) = delete; + + ~TestWinChildProcess() {} + + private: + // WinChildProcess will have already exercised the pipes. + int Run() override { + int value = ReadInt(ReadPipeHandle()); + WriteInt(WritePipeHandle(), value); + return testing::Test::HasFailure() ? EXIT_FAILURE : EXIT_SUCCESS; + } +}; + +TEST(WinChildProcessTest, WinChildProcess) { + WinChildProcess::EntryPoint(); + + std::unique_ptr handles = WinChildProcess::Launch(); + WriteInt(handles->write.get(), 1); + ASSERT_EQ(ReadInt(handles->read.get()), 1); +} + +TEST(WinChildProcessTest, MultipleChildren) { + WinChildProcess::EntryPoint(); + + std::unique_ptr handles_1 = + WinChildProcess::Launch(); + std::unique_ptr handles_2 = + WinChildProcess::Launch(); + std::unique_ptr handles_3 = + WinChildProcess::Launch(); + + WriteInt(handles_1->write.get(), 1); + WriteInt(handles_2->write.get(), 2); + WriteInt(handles_3->write.get(), 3); + ASSERT_EQ(ReadInt(handles_3->read.get()), 3); + ASSERT_EQ(ReadInt(handles_2->read.get()), 2); + ASSERT_EQ(ReadInt(handles_1->read.get()), 1); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/win/win_multiprocess.cc b/shared/sentry/external/crashpad/test/win/win_multiprocess.cc new file mode 100644 index 000000000..645effc01 --- /dev/null +++ b/shared/sentry/external/crashpad/test/win/win_multiprocess.cc @@ -0,0 +1,78 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "test/win/win_multiprocess.h" + +#include + +#include "base/check.h" +#include "base/scoped_generic.h" +#include "base/strings/utf_string_conversions.h" +#include "util/stdlib/string_number_conversion.h" +#include "util/string/split_string.h" + +namespace crashpad { +namespace test { + +WinMultiprocess::WinMultiprocess() + : exit_code_(EXIT_SUCCESS), + child_handles_(nullptr), + child_process_helper_(nullptr) {} + +WinMultiprocess::~WinMultiprocess() { +} + +void WinMultiprocess::SetExpectedChildExitCode(unsigned int exit_code) { + exit_code_ = exit_code; +} + +FileHandle WinMultiprocess::ReadPipeHandle() const { + if (child_handles_) + return child_handles_->read.get(); + CHECK(child_process_helper_); + return child_process_helper_->ReadPipeHandleForwarder(); +} + +FileHandle WinMultiprocess::WritePipeHandle() const { + if (child_handles_) + return child_handles_->write.get(); + CHECK(child_process_helper_); + return child_process_helper_->WritePipeHandleForwarder(); +} + +void WinMultiprocess::CloseReadPipe() { + if (child_handles_) { + child_handles_->read.reset(); + } else { + CHECK(child_process_helper_); + child_process_helper_->CloseReadPipeForwarder(); + } +} + +void WinMultiprocess::CloseWritePipe() { + if (child_handles_) { + child_handles_->write.reset(); + } else { + CHECK(child_process_helper_); + child_process_helper_->CloseWritePipeForwarder(); + } +} + +HANDLE WinMultiprocess::ChildProcess() const { + CHECK(child_handles_); + return child_handles_->process.get(); +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/win/win_multiprocess.h b/shared/sentry/external/crashpad/test/win/win_multiprocess.h new file mode 100644 index 000000000..91bee7e2c --- /dev/null +++ b/shared/sentry/external/crashpad/test/win/win_multiprocess.h @@ -0,0 +1,210 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_WIN_WIN_MULTIPROCESS_H_ +#define CRASHPAD_TEST_WIN_WIN_MULTIPROCESS_H_ + +#include + +#include "gtest/gtest.h" +#include "test/win/win_child_process.h" +#include "util/file/file_io.h" +#include "util/win/scoped_handle.h" + +namespace crashpad { +namespace test { + +//! \brief Manages a multiprocess test on Windows. +class WinMultiprocess { + public: + WinMultiprocess(); + + WinMultiprocess(const WinMultiprocess&) = delete; + WinMultiprocess& operator=(const WinMultiprocess&) = delete; + + //! \brief Runs the test. + //! + //! This method establishes the testing environment by respawning the process + //! as a child with additional flags. + //! + //! In the parent process, WinMultiprocessParent() is run, and in the child + //! WinMultiprocessChild(). + template + static void Run() { + ASSERT_NO_FATAL_FAILURE( + WinChildProcess::EntryPoint>()); + // If WinChildProcess::EntryPoint returns, we are in the parent process. + T parent_process; + WinMultiprocess* parent_multiprocess = &parent_process; + + parent_multiprocess->WinMultiprocessParentBeforeChild(); + + std::unique_ptr child_handles = + WinChildProcess::Launch(); + ASSERT_TRUE(child_handles.get()); + parent_process.child_handles_ = child_handles.get(); + parent_multiprocess->WinMultiprocessParent(); + + // Close our side of the handles now that we're done. The child can + // use this to know when it's safe to complete. + child_handles->read.reset(); + child_handles->write.reset(); + + // Wait for the child to complete. + ASSERT_EQ(WaitForSingleObject(child_handles->process.get(), INFINITE), + WAIT_OBJECT_0); + + DWORD exit_code; + ASSERT_TRUE(GetExitCodeProcess(child_handles->process.get(), &exit_code)); + ASSERT_EQ(exit_code, parent_process.exit_code_); + + parent_multiprocess->WinMultiprocessParentAfterChild( + child_handles->process.get()); + } + + protected: + virtual ~WinMultiprocess(); + + //! \brief Sets the expected exit code of the child process. + //! + //! The default expected termination code is `EXIT_SUCCESS` (`0`). + //! + //! \param[in] exit_code The expected exit status of the child. + void SetExpectedChildExitCode(unsigned int exit_code); + + //! \brief Returns the read pipe's file handle. + //! + //! This method may be called by either the parent or the child process. + //! Anything written to the write pipe in the partner process will appear + //! on this file handle in this process. + //! + //! It is an error to call this after CloseReadPipe() has been called. + //! + //! \return The read pipe's file handle. + FileHandle ReadPipeHandle() const; + + //! \brief Returns the write pipe's file handle. + //! + //! This method may be called by either the parent or the child process. + //! Anything written to this file handle in this process will appear on + //! the read pipe in the partner process. + //! + //! It is an error to call this after CloseWritePipe() has been called. + //! + //! \return The write pipe's file handle. + FileHandle WritePipeHandle() const; + + //! \brief Closes the read pipe. + //! + //! This method may be called by either the parent or the child process. + //! ReadPipeHandle() must not be called after this. + void CloseReadPipe(); + + //! \brief Closes the write pipe. + //! + //! This method may be called by either the parent or the child process. An + //! attempt to read from the read pipe in the partner process will indicate + //! end-of-file. WritePipeHandle() must not be called after this. + void CloseWritePipe(); + + //! \brief Returns a handle to the child process. + //! + //! This method may only be called by the parent process. + HANDLE ChildProcess() const; + + private: + // Implements an adapter to provide WinMultiprocess with access to the + // anonymous pipe handles from WinChildProcess. + class ChildProcessHelperBase : public WinChildProcess { + public: + ChildProcessHelperBase() {} + + ChildProcessHelperBase(const ChildProcessHelperBase&) = delete; + ChildProcessHelperBase& operator=(const ChildProcessHelperBase&) = delete; + + ~ChildProcessHelperBase() override {} + + void CloseWritePipeForwarder() { CloseWritePipe(); } + void CloseReadPipeForwarder() { CloseReadPipe(); } + FileHandle ReadPipeHandleForwarder() const { return ReadPipeHandle(); } + FileHandle WritePipeHandleForwarder() const { return WritePipeHandle(); } + }; + + // Forwards WinChildProcess::Run to T::WinMultiprocessChild. + template + class ChildProcessHelper : public ChildProcessHelperBase { + public: + ChildProcessHelper() {} + + ChildProcessHelper(const ChildProcessHelper&) = delete; + ChildProcessHelper& operator=(const ChildProcessHelper&) = delete; + + ~ChildProcessHelper() override {} + + private: + int Run() override { + T child_process; + child_process.child_process_helper_ = this; + static_cast(&child_process)->WinMultiprocessChild(); + if (testing::Test::HasFailure()) + return 255; + return EXIT_SUCCESS; + } + }; + + //! \brief The subclass-provided parent routine. + //! + //! Test failures should be reported via Google Test: `EXPECT_*()`, + //! `ASSERT_*()`, `FAIL()`, etc. + //! + //! This method need not use `WaitForSingleObject()`-family call to wait for + //! the child process to exit, as this is handled by this class. + //! + //! Subclasses must implement this method to define how the parent operates. + virtual void WinMultiprocessParent() = 0; + + //! \brief The optional routine run in parent before the child is spawned. + //! + //! Subclasses may implement this method to prepare the environment for + //! the child process. + virtual void WinMultiprocessParentBeforeChild() {} + + //! \brief The optional routine run in parent after the child exits. + //! + //! Subclasses may implement this method to clean up the environment after + //! the child process has exited. + //! + //! \param[in] child A handle to the exited child process. + virtual void WinMultiprocessParentAfterChild(HANDLE child) {} + + //! \brief The subclass-provided child routine. + //! + //! Test failures should be reported via Google Test: `EXPECT_*()`, + //! `ASSERT_*()`, `FAIL()`, etc. + //! + //! Subclasses must implement this method to define how the child operates. + //! Subclasses may exit with a failure status by using `LOG(FATAL)`, + //! `abort()`, or similar. They may exit cleanly by returning from this + //! method. + virtual void WinMultiprocessChild() = 0; + + unsigned int exit_code_; + WinChildProcess::Handles* child_handles_; + ChildProcessHelperBase* child_process_helper_; +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_WIN_WIN_MULTIPROCESS_H_ diff --git a/shared/sentry/external/crashpad/test/win/win_multiprocess_test.cc b/shared/sentry/external/crashpad/test/win/win_multiprocess_test.cc new file mode 100644 index 000000000..60eb9d8d0 --- /dev/null +++ b/shared/sentry/external/crashpad/test/win/win_multiprocess_test.cc @@ -0,0 +1,90 @@ +// Copyright 2015 The Crashpad Authors. All rights reserved. +// +// 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 "test/win/win_multiprocess.h" + +#include "gtest/gtest.h" + +namespace crashpad { +namespace test { +namespace { + +template +class TestWinMultiprocess final : public WinMultiprocess { + public: + TestWinMultiprocess() {} + + TestWinMultiprocess(const TestWinMultiprocess&) = delete; + TestWinMultiprocess& operator=(const TestWinMultiprocess&) = delete; + + private: + // WinMultiprocess will have already exercised the pipes. + void WinMultiprocessParent() override { SetExpectedChildExitCode(ExitCode); } + + void WinMultiprocessChild() override { + exit(ExitCode); + } +}; + +class TestWinMultiprocessChildAsserts final : public WinMultiprocess { + public: + TestWinMultiprocessChildAsserts() {} + + TestWinMultiprocessChildAsserts(const TestWinMultiprocessChildAsserts&) = + delete; + TestWinMultiprocessChildAsserts& operator=( + const TestWinMultiprocessChildAsserts&) = delete; + + private: + void WinMultiprocessParent() override { SetExpectedChildExitCode(255); } + void WinMultiprocessChild() override { + ASSERT_FALSE(true); + } +}; + +class TestWinMultiprocessChildExpects final : public WinMultiprocess { + public: + TestWinMultiprocessChildExpects() {} + + TestWinMultiprocessChildExpects(const TestWinMultiprocessChildExpects&) = + delete; + TestWinMultiprocessChildExpects& operator=( + const TestWinMultiprocessChildExpects&) = delete; + + private: + void WinMultiprocessParent() override { SetExpectedChildExitCode(255); } + void WinMultiprocessChild() override { + EXPECT_FALSE(true); + } +}; + +TEST(WinMultiprocess, WinMultiprocess) { + WinMultiprocess::Run>(); +} + +TEST(WinMultiprocess, WinMultiprocessNonSuccessExitCode) { + WinMultiprocess::Run>(); +} + +TEST(WinMultiprocessChildFails, ChildExpectFailure) { + WinMultiprocess::Run(); +} + +TEST(WinMultiprocessChildFails, ChildAssertFailure) { + WinMultiprocess::Run(); +} + +} // namespace +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/win/win_multiprocess_with_temp_dir.cc b/shared/sentry/external/crashpad/test/win/win_multiprocess_with_temp_dir.cc new file mode 100644 index 000000000..1dfc1b13f --- /dev/null +++ b/shared/sentry/external/crashpad/test/win/win_multiprocess_with_temp_dir.cc @@ -0,0 +1,189 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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 "test/win/win_multiprocess_with_temp_dir.h" + +#include + +#include "base/logging.h" +#include "test/errors.h" +#include "util/process/process_id.h" +#include "util/win/process_info.h" + +namespace crashpad { +namespace test { + +namespace { + +constexpr wchar_t kTempDirEnvName[] = L"CRASHPAD_TEST_TEMP_DIR"; + +// Returns the process IDs of all processes that have |parent_pid| as +// parent process ID. +std::vector GetPotentialChildProcessesOf(ProcessID parent_pid) { + ScopedFileHANDLE snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)); + if (!snapshot.is_valid()) { + ADD_FAILURE() << ErrorMessage("CreateToolhelp32Snapshot"); + return std::vector(); + } + + PROCESSENTRY32 entry = {sizeof(entry)}; + if (!Process32First(snapshot.get(), &entry)) { + ADD_FAILURE() << ErrorMessage("Process32First"); + return std::vector(); + } + + std::vector child_pids; + do { + if (entry.th32ParentProcessID == parent_pid) + child_pids.push_back(entry.th32ProcessID); + } while (Process32Next(snapshot.get(), &entry)); + + return child_pids; +} + +ULARGE_INTEGER GetProcessCreationTime(HANDLE process) { + ULARGE_INTEGER ret = {}; + FILETIME creation_time; + FILETIME dummy; + if (GetProcessTimes(process, &creation_time, &dummy, &dummy, &dummy)) { + ret.LowPart = creation_time.dwLowDateTime; + ret.HighPart = creation_time.dwHighDateTime; + } else { + ADD_FAILURE() << ErrorMessage("GetProcessTimes"); + } + + return ret; +} + +// Waits for the processes directly created by |parent| - and specifically +// not their offspring. For this to work without race, |parent| has to be +// suspended or have exited. +void WaitForAllChildProcessesOf(HANDLE parent) { + ProcessID parent_pid = GetProcessId(parent); + std::vector child_pids = GetPotentialChildProcessesOf(parent_pid); + + ULARGE_INTEGER parent_creationtime = GetProcessCreationTime(parent); + for (ProcessID child_pid : child_pids) { + // Try and open the process. This may fail for reasons such as: + // 1. The process isn't |parent|'s child process, but rather a + // higher-privilege sub-process of an earlier process that had + // |parent|'s PID. + // 2. The process no longer exists, e.g. it exited after enumeration. + ScopedKernelHANDLE child_process( + OpenProcess(PROCESS_VM_READ | PROCESS_QUERY_INFORMATION | SYNCHRONIZE, + false, + child_pid)); + if (!child_process.is_valid()) + continue; + + // Check that the child now has the right parent PID, as its PID may have + // been reused after the enumeration above. + ProcessInfo child_info; + if (!child_info.Initialize(child_process.get())) { + // This can happen if child_process has exited after the handle is opened. + LOG(ERROR) << "ProcessInfo::Initialize, pid: " << child_pid; + continue; + } + + if (parent_pid != child_info.ParentProcessID()) { + // The child's process ID was reused after enumeration. + continue; + } + + // We successfully opened |child_process| and it has |parent|'s PID for + // parent process ID. However, this could still be a sub-process of another + // process that earlier had |parent|'s PID. To make sure, check that + // |child_process| was created after |parent_process|. + ULARGE_INTEGER process_creationtime = + GetProcessCreationTime(child_process.get()); + if (process_creationtime.QuadPart < parent_creationtime.QuadPart) + continue; + + DWORD err = WaitForSingleObject(child_process.get(), INFINITE); + if (err == WAIT_FAILED) { + ADD_FAILURE() << ErrorMessage("WaitForSingleObject"); + } else if (err != WAIT_OBJECT_0) { + ADD_FAILURE() << "WaitForSingleObject returned " << err; + } + } +} + +} // namespace + +WinMultiprocessWithTempDir::WinMultiprocessWithTempDir() + : WinMultiprocess(), temp_dir_env_(kTempDirEnvName) {} + +void WinMultiprocessWithTempDir::WinMultiprocessParentBeforeChild() { + temp_dir_ = std::make_unique(); + temp_dir_env_.SetValue(temp_dir_->path().value().c_str()); +} + +void WinMultiprocessWithTempDir::WinMultiprocessParentAfterChild(HANDLE child) { + WaitForAllChildProcessesOf(child); + temp_dir_.reset(); +} + +base::FilePath WinMultiprocessWithTempDir::GetTempDirPath() const { + return base::FilePath(temp_dir_env_.GetValue()); +} + +WinMultiprocessWithTempDir::ScopedEnvironmentVariable:: + ScopedEnvironmentVariable(const wchar_t* name) + : name_(name) { + original_value_ = GetValueImpl(&was_defined_); +} + +WinMultiprocessWithTempDir::ScopedEnvironmentVariable:: + ~ScopedEnvironmentVariable() { + if (was_defined_) + SetValue(original_value_.data()); + else + SetValue(nullptr); +} + +std::wstring WinMultiprocessWithTempDir::ScopedEnvironmentVariable::GetValue() + const { + bool dummy; + return GetValueImpl(&dummy); +} + +std::wstring +WinMultiprocessWithTempDir::ScopedEnvironmentVariable::GetValueImpl( + bool* is_defined) const { + // The length returned is inclusive of the terminating zero, except + // if the variable doesn't exist, in which case the return value is zero. + DWORD len = GetEnvironmentVariable(name_, nullptr, 0); + if (len == 0) { + *is_defined = false; + return L""; + } + + *is_defined = true; + + std::wstring ret; + ret.resize(len); + // The length returned on success is exclusive of the terminating zero. + len = GetEnvironmentVariable(name_, &ret[0], len); + ret.resize(len); + + return ret; +} + +void WinMultiprocessWithTempDir::ScopedEnvironmentVariable::SetValue( + const wchar_t* new_value) const { + SetEnvironmentVariable(name_, new_value); +} + +} // namespace test +} // namespace crashpad diff --git a/shared/sentry/external/crashpad/test/win/win_multiprocess_with_temp_dir.h b/shared/sentry/external/crashpad/test/win/win_multiprocess_with_temp_dir.h new file mode 100644 index 000000000..5e47c22a0 --- /dev/null +++ b/shared/sentry/external/crashpad/test/win/win_multiprocess_with_temp_dir.h @@ -0,0 +1,85 @@ +// Copyright 2017 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_TEST_WIN_WIN_MULTIPROCESS_WITH_TEMPDIR_H_ +#define CRASHPAD_TEST_WIN_WIN_MULTIPROCESS_WITH_TEMPDIR_H_ + +#include +#include + +#include +#include + +#include "base/files/file_path.h" +#include "test/scoped_temp_dir.h" +#include "test/win/win_multiprocess.h" + +namespace crashpad { +namespace test { + +//! \brief Manages a multiprocess test on Windows with a parent-created +//! temporary directory. +//! +//! This class creates a temp directory in the parent process for the use of +//! the subprocess and its children. To ensure a raceless rundown, it waits on +//! the child process and any processes directly created by the child before +//! deleting the temporary directory. +class WinMultiprocessWithTempDir : public WinMultiprocess { + public: + WinMultiprocessWithTempDir(); + + WinMultiprocessWithTempDir(const WinMultiprocessWithTempDir&) = delete; + WinMultiprocessWithTempDir& operator=(const WinMultiprocessWithTempDir&) = + delete; + + protected: + void WinMultiprocessParentBeforeChild() override; + void WinMultiprocessParentAfterChild(HANDLE child) override; + + //! \brief Returns the path of the temp directory. + base::FilePath GetTempDirPath() const; + + private: + class ScopedEnvironmentVariable { + public: + explicit ScopedEnvironmentVariable(const wchar_t* name); + + ScopedEnvironmentVariable(const ScopedEnvironmentVariable&) = delete; + ScopedEnvironmentVariable& operator=(const ScopedEnvironmentVariable&) = + delete; + + ~ScopedEnvironmentVariable(); + + std::wstring GetValue() const; + + // Sets this environment variable to |new_value|. If |new_value| is nullptr + // this environment variable will be undefined. + void SetValue(const wchar_t* new_value) const; + + private: + std::wstring GetValueImpl(bool* is_defined) const; + + std::wstring original_value_; + const wchar_t* name_; + bool was_defined_; + }; + + std::unique_ptr temp_dir_; + ScopedEnvironmentVariable temp_dir_env_; +}; + +} // namespace test +} // namespace crashpad + +#endif // CRASHPAD_TEST_WIN_WIN_MULTIPROCESS_WITH_TEMPDIR_H_ diff --git a/shared/sentry/external/crashpad/third_party/cpp-httplib/BUILD.gn b/shared/sentry/external/crashpad/third_party/cpp-httplib/BUILD.gn new file mode 100644 index 000000000..8db916acf --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/cpp-httplib/BUILD.gn @@ -0,0 +1,20 @@ +# Copyright 2018 The Crashpad Authors. All rights reserved. +# +# 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. + +source_set("cpp-httplib") { + testonly = true + include_dirs = [ "cpp-httplib" ] + sources = [ "cpp-httplib/httplib.h" ] + deps = [ "../zlib" ] +} diff --git a/shared/sentry/external/crashpad/third_party/cpp-httplib/README.crashpad b/shared/sentry/external/crashpad/third_party/cpp-httplib/README.crashpad new file mode 100644 index 000000000..cfb476e0a --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/cpp-httplib/README.crashpad @@ -0,0 +1,15 @@ +Name: cpp-httplib +Short Name: cpp-httplib +URL: https://github.com/yhirose/cpp-httplib +Revision: 5b3187e2f9e77c672063d49a1167bbb563da023e +License: MIT +License File: cpp-httplib/LICENSE +Security Critical: no (test only) + +Description: +A C++11 header-only HTTP library. + +Local Modifications: +- Exclude test/ and example/ subdirs. +- Patch httplib.h to use #include "third_party/zlib/zlib_crashpad.h" instead of + . diff --git a/shared/sentry/external/crashpad/third_party/cpp-httplib/cpp-httplib/LICENSE b/shared/sentry/external/crashpad/third_party/cpp-httplib/cpp-httplib/LICENSE new file mode 100644 index 000000000..3e5ed359a --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/cpp-httplib/cpp-httplib/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2017 yhirose + +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. + diff --git a/shared/sentry/external/crashpad/third_party/cpp-httplib/cpp-httplib/README.md b/shared/sentry/external/crashpad/third_party/cpp-httplib/cpp-httplib/README.md new file mode 100644 index 000000000..84685b4a9 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/cpp-httplib/cpp-httplib/README.md @@ -0,0 +1,209 @@ +cpp-httplib +=========== + +A C++11 header-only HTTP library. + +It's extremely easy to setup. Just include **httplib.h** file in your code! + +Inspired by [Sinatra](http://www.sinatrarb.com/) and [express](https://github.com/visionmedia/express). + +Server Example +-------------- + +```c++ +#include + +int main(void) +{ + using namespace httplib; + + Server svr; + + svr.Get("/hi", [](const Request& req, Response& res) { + res.set_content("Hello World!", "text/plain"); + }); + + svr.Get(R"(/numbers/(\d+))", [&](const Request& req, Response& res) { + auto numbers = req.matches[1]; + res.set_content(numbers, "text/plain"); + }); + + svr.listen("localhost", 1234); +} +``` + +`Post`, `Put`, `Delete` and `Options` methods are also supported. + +### Method Chain + +```cpp +svr.Get("/get", [](const auto& req, auto& res) { + res.set_content("get", "text/plain"); + }) + .Post("/post", [](const auto& req, auto& res) { + res.set_content(req.body(), "text/plain"); + }) + .listen("localhost", 1234); +``` + +### Static File Server + +```cpp +svr.set_base_dir("./www"); +``` + +### Logging + +```cpp +svr.set_logger([](const auto& req, const auto& res) { + your_logger(req, res); +}); +``` + +### Error Handler + +```cpp +svr.set_error_handler([](const auto& req, auto& res) { + const char* fmt = "

    Error Status: %d

    "; + char buf[BUFSIZ]; + snprintf(buf, sizeof(buf), fmt, res.status); + res.set_content(buf, "text/html"); +}); +``` + +### 'multipart/form-data' POST data + +```cpp +svr.Post("/multipart", [&](const auto& req, auto& res) { + auto size = req.files.size(); + auto ret = req.has_file("name1")); + const auto& file = req.get_file_value("name1"); + // file.filename; + // file.content_type; + auto body = req.body.substr(file.offset, file.length)); +}) +``` + +Client Example +-------------- + +### GET + +```c++ +#include +#include + +int main(void) +{ + httplib::Client cli("localhost", 1234); + + auto res = cli.Get("/hi"); + if (res && res->status == 200) { + std::cout << res->body << std::endl; + } +} +``` + +### POST + +```c++ +res = cli.Post("/post", "text", "text/plain"); +res = cli.Post("/person", "name=john1¬e=coder", "application/x-www-form-urlencoded"); +``` + +### POST with parameters + +```c++ +httplib::Params params; +params["name"] = "john"; +params["note"] = "coder"; +auto res = cli.Post("/post", params); +``` + +### PUT + +```c++ +res = cli.Put("/resource/foo", "text", "text/plain"); +``` + +### DELETE + +```c++ +res = cli.Delete("/resource/foo"); +``` + +### OPTIONS + +```c++ +res = cli.Options("*"); +res = cli.Options("/resource/foo"); +``` + +### Connection Timeout + +```c++ +httplib::Client cli("localhost", 8080, 5); // timeouts in 5 seconds +``` +### With Progress Callback + +```cpp +httplib::Client client(url, port); + +// prints: 0 / 000 bytes => 50% complete +std::shared_ptr res = + cli.Get("/", [](uint64_t len, uint64_t total) { + printf("%lld / %lld bytes => %d%% complete\n", + len, total, + (int)((len/total)*100)); + } +); +``` + +![progress](https://user-images.githubusercontent.com/236374/33138910-495c4ecc-cf86-11e7-8693-2fc6d09615c4.gif) + +This feature was contributed by [underscorediscovery](https://github.com/yhirose/cpp-httplib/pull/23). + +### Range + +```cpp +httplib::Client cli("httpbin.org", 80); + +// 'Range: bytes=1-10' +httplib::Headers headers = { httplib::make_range_header(1, 10) }; + +auto res = cli.Get("/range/32", headers); +// res->status should be 206. +// res->body should be "bcdefghijk". +``` + +OpenSSL Support +--------------- + +SSL support is available with `CPPHTTPLIB_OPENSSL_SUPPORT`. `libssl` and `libcrypto` should be linked. + +```c++ +#define CPPHTTPLIB_OPENSSL_SUPPORT + +SSLServer svr("./cert.pem", "./key.pem"); + +SSLClient cli("localhost", 8080); +``` + +Zlib Support +------------ + +'gzip' compression is available with `CPPHTTPLIB_ZLIB_SUPPORT`. + +The server applies gzip compression to the following MIME type contents: + + * all text types + * image/svg+xml + * application/javascript + * application/json + * application/xml + * application/xhtml+xml + +License +------- + +MIT license (© 2018 Yuji Hirose) diff --git a/shared/sentry/external/crashpad/third_party/cpp-httplib/cpp-httplib/httplib.h b/shared/sentry/external/crashpad/third_party/cpp-httplib/cpp-httplib/httplib.h new file mode 100644 index 000000000..dadab1d8e --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/cpp-httplib/cpp-httplib/httplib.h @@ -0,0 +1,2335 @@ +// +// httplib.h +// +// Copyright (c) 2017 Yuji Hirose. All rights reserved. +// MIT License +// + +#ifndef _CPPHTTPLIB_HTTPLIB_H_ +#define _CPPHTTPLIB_HTTPLIB_H_ + +#ifdef _WIN32 +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif +#ifndef _CRT_NONSTDC_NO_DEPRECATE +#define _CRT_NONSTDC_NO_DEPRECATE +#endif + +#if defined(_MSC_VER) && _MSC_VER < 1900 +#define snprintf _snprintf_s +#endif + +#ifndef S_ISREG +#define S_ISREG(m) (((m)&S_IFREG)==S_IFREG) +#endif +#ifndef S_ISDIR +#define S_ISDIR(m) (((m)&S_IFDIR)==S_IFDIR) +#endif + +#include +#include +#include + +#undef min +#undef max + +#ifndef strcasecmp +#define strcasecmp _stricmp +#endif + +typedef SOCKET socket_t; +#else +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef int socket_t; +#define INVALID_SOCKET (-1) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +#include +#endif + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT +#include "third_party/zlib/zlib_crashpad.h" +#endif + +/* + * Configuration + */ +#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5 +#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND 0 + +namespace httplib +{ + +namespace detail { + +struct ci { + bool operator() (const std::string & s1, const std::string & s2) const { + return std::lexicographical_compare( + s1.begin(), s1.end(), + s2.begin(), s2.end(), + [](char c1, char c2) { + return ::tolower(c1) < ::tolower(c2); + }); + } +}; + +} // namespace detail + +enum class HttpVersion { v1_0 = 0, v1_1 }; + +typedef std::multimap Headers; + +template +std::pair make_range_header(uint64_t value, Args... args); + +typedef std::multimap Params; +typedef std::smatch Match; +typedef std::function Progress; + +struct MultipartFile { + std::string filename; + std::string content_type; + size_t offset = 0; + size_t length = 0; +}; +typedef std::multimap MultipartFiles; + +struct Request { + std::string version; + std::string method; + std::string target; + std::string path; + Headers headers; + std::string body; + Params params; + MultipartFiles files; + Match matches; + + Progress progress; + + bool has_header(const char* key) const; + std::string get_header_value(const char* key) const; + void set_header(const char* key, const char* val); + + bool has_param(const char* key) const; + std::string get_param_value(const char* key) const; + + bool has_file(const char* key) const; + MultipartFile get_file_value(const char* key) const; +}; + +struct Response { + std::string version; + int status; + Headers headers; + std::string body; + + bool has_header(const char* key) const; + std::string get_header_value(const char* key) const; + void set_header(const char* key, const char* val); + + void set_redirect(const char* uri); + void set_content(const char* s, size_t n, const char* content_type); + void set_content(const std::string& s, const char* content_type); + + Response() : status(-1) {} +}; + +class Stream { +public: + virtual ~Stream() {} + virtual int read(char* ptr, size_t size) = 0; + virtual int write(const char* ptr, size_t size1) = 0; + virtual int write(const char* ptr) = 0; + virtual std::string get_remote_addr() = 0; + + template + void write_format(const char* fmt, const Args& ...args); +}; + +class SocketStream : public Stream { +public: + SocketStream(socket_t sock); + virtual ~SocketStream(); + + virtual int read(char* ptr, size_t size); + virtual int write(const char* ptr, size_t size); + virtual int write(const char* ptr); + virtual std::string get_remote_addr(); + +private: + socket_t sock_; +}; + +class Server { +public: + typedef std::function Handler; + typedef std::function Logger; + + Server(); + + virtual ~Server(); + + virtual bool is_valid() const; + + Server& Get(const char* pattern, Handler handler); + Server& Post(const char* pattern, Handler handler); + + Server& Put(const char* pattern, Handler handler); + Server& Delete(const char* pattern, Handler handler); + Server& Options(const char* pattern, Handler handler); + + bool set_base_dir(const char* path); + + void set_error_handler(Handler handler); + void set_logger(Logger logger); + + void set_keep_alive_max_count(size_t count); + + int bind_to_any_port(const char* host, int socket_flags = 0); + bool listen_after_bind(); + + bool listen(const char* host, int port, int socket_flags = 0); + + bool is_running() const; + void stop(); + +protected: + bool process_request(Stream& strm, bool last_connection, bool& connection_close); + + size_t keep_alive_max_count_; + +private: + typedef std::vector> Handlers; + + socket_t create_server_socket(const char* host, int port, int socket_flags) const; + int bind_internal(const char* host, int port, int socket_flags); + bool listen_internal(); + + bool routing(Request& req, Response& res); + bool handle_file_request(Request& req, Response& res); + bool dispatch_request(Request& req, Response& res, Handlers& handlers); + + bool parse_request_line(const char* s, Request& req); + void write_response(Stream& strm, bool last_connection, const Request& req, Response& res); + + virtual bool read_and_close_socket(socket_t sock); + + bool is_running_; + socket_t svr_sock_; + std::string base_dir_; + Handlers get_handlers_; + Handlers post_handlers_; + Handlers put_handlers_; + Handlers delete_handlers_; + Handlers options_handlers_; + Handler error_handler_; + Logger logger_; + + // TODO: Use thread pool... + std::mutex running_threads_mutex_; + int running_threads_; +}; + +class Client { +public: + Client( + const char* host, + int port = 80, + size_t timeout_sec = 300); + + virtual ~Client(); + + virtual bool is_valid() const; + + std::shared_ptr Get(const char* path, Progress progress = nullptr); + std::shared_ptr Get(const char* path, const Headers& headers, Progress progress = nullptr); + + std::shared_ptr Head(const char* path); + std::shared_ptr Head(const char* path, const Headers& headers); + + std::shared_ptr Post(const char* path, const std::string& body, const char* content_type); + std::shared_ptr Post(const char* path, const Headers& headers, const std::string& body, const char* content_type); + + std::shared_ptr Post(const char* path, const Params& params); + std::shared_ptr Post(const char* path, const Headers& headers, const Params& params); + + std::shared_ptr Put(const char* path, const std::string& body, const char* content_type); + std::shared_ptr Put(const char* path, const Headers& headers, const std::string& body, const char* content_type); + + std::shared_ptr Delete(const char* path); + std::shared_ptr Delete(const char* path, const Headers& headers); + + std::shared_ptr Options(const char* path); + std::shared_ptr Options(const char* path, const Headers& headers); + + bool send(Request& req, Response& res); + +protected: + bool process_request(Stream& strm, Request& req, Response& res, bool& connection_close); + + const std::string host_; + const int port_; + size_t timeout_sec_; + const std::string host_and_port_; + +private: + socket_t create_client_socket() const; + bool read_response_line(Stream& strm, Response& res); + void write_request(Stream& strm, Request& req); + + virtual bool read_and_close_socket(socket_t sock, Request& req, Response& res); +}; + +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +class SSLSocketStream : public Stream { +public: + SSLSocketStream(socket_t sock, SSL* ssl); + virtual ~SSLSocketStream(); + + virtual int read(char* ptr, size_t size); + virtual int write(const char* ptr, size_t size); + virtual int write(const char* ptr); + virtual std::string get_remote_addr(); + +private: + socket_t sock_; + SSL* ssl_; +}; + +class SSLServer : public Server { +public: + SSLServer( + const char* cert_path, const char* private_key_path); + + virtual ~SSLServer(); + + virtual bool is_valid() const; + +private: + virtual bool read_and_close_socket(socket_t sock); + + SSL_CTX* ctx_; + std::mutex ctx_mutex_; +}; + +class SSLClient : public Client { +public: + SSLClient( + const char* host, + int port = 80, + size_t timeout_sec = 300); + + virtual ~SSLClient(); + + virtual bool is_valid() const; + +private: + virtual bool read_and_close_socket(socket_t sock, Request& req, Response& res); + + SSL_CTX* ctx_; + std::mutex ctx_mutex_; +}; +#endif + +/* + * Implementation + */ +namespace detail { + +template +void split(const char* b, const char* e, char d, Fn fn) +{ + int i = 0; + int beg = 0; + + while (e ? (b + i != e) : (b[i] != '\0')) { + if (b[i] == d) { + fn(&b[beg], &b[i]); + beg = i + 1; + } + i++; + } + + if (i) { + fn(&b[beg], &b[i]); + } +} + +// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer` +// to store data. The call can set memory on stack for performance. +class stream_line_reader { +public: + stream_line_reader(Stream& strm, char* fixed_buffer, size_t fixed_buffer_size) + : strm_(strm) + , fixed_buffer_(fixed_buffer) + , fixed_buffer_size_(fixed_buffer_size) { + } + + const char* ptr() const { + if (glowable_buffer_.empty()) { + return fixed_buffer_; + } else { + return glowable_buffer_.data(); + } + } + + bool getline() { + fixed_buffer_used_size_ = 0; + glowable_buffer_.clear(); + + for (size_t i = 0; ; i++) { + char byte; + auto n = strm_.read(&byte, 1); + + if (n < 0) { + return false; + } else if (n == 0) { + if (i == 0) { + return false; + } else { + break; + } + } + + append(byte); + + if (byte == '\n') { + break; + } + } + + return true; + } + +private: + void append(char c) { + if (fixed_buffer_used_size_ < fixed_buffer_size_ - 1) { + fixed_buffer_[fixed_buffer_used_size_++] = c; + fixed_buffer_[fixed_buffer_used_size_] = '\0'; + } else { + if (glowable_buffer_.empty()) { + assert(fixed_buffer_[fixed_buffer_used_size_] == '\0'); + glowable_buffer_.assign(fixed_buffer_, fixed_buffer_used_size_); + } + glowable_buffer_ += c; + } + } + + Stream& strm_; + char* fixed_buffer_; + const size_t fixed_buffer_size_; + size_t fixed_buffer_used_size_; + std::string glowable_buffer_; +}; + +inline int close_socket(socket_t sock) +{ +#ifdef _WIN32 + return closesocket(sock); +#else + return close(sock); +#endif +} + +inline int select_read(socket_t sock, size_t sec, size_t usec) +{ + fd_set fds; + FD_ZERO(&fds); + FD_SET(sock, &fds); + + timeval tv; + tv.tv_sec = sec; + tv.tv_usec = usec; + + return select(sock + 1, &fds, NULL, NULL, &tv); +} + +inline bool wait_until_socket_is_ready(socket_t sock, size_t sec, size_t usec) +{ + fd_set fdsr; + FD_ZERO(&fdsr); + FD_SET(sock, &fdsr); + + auto fdsw = fdsr; + auto fdse = fdsr; + + timeval tv; + tv.tv_sec = sec; + tv.tv_usec = usec; + + if (select(sock + 1, &fdsr, &fdsw, &fdse, &tv) < 0) { + return false; + } else if (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw)) { + int error = 0; + socklen_t len = sizeof(error); + if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char*)&error, &len) < 0 || error) { + return false; + } + } else { + return false; + } + + return true; +} + +template +inline bool read_and_close_socket(socket_t sock, size_t keep_alive_max_count, T callback) +{ + bool ret = false; + + if (keep_alive_max_count > 0) { + auto count = keep_alive_max_count; + while (count > 0 && + detail::select_read(sock, + CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND, + CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0) { + SocketStream strm(sock); + auto last_connection = count == 1; + auto connection_close = false; + + ret = callback(strm, last_connection, connection_close); + if (!ret || connection_close) { + break; + } + + count--; + } + } else { + SocketStream strm(sock); + auto dummy_connection_close = false; + ret = callback(strm, true, dummy_connection_close); + } + + close_socket(sock); + return ret; +} + +inline int shutdown_socket(socket_t sock) +{ +#ifdef _WIN32 + return shutdown(sock, SD_BOTH); +#else + return shutdown(sock, SHUT_RDWR); +#endif +} + +template +socket_t create_socket(const char* host, int port, Fn fn, int socket_flags = 0) +{ +#ifdef _WIN32 +#define SO_SYNCHRONOUS_NONALERT 0x20 +#define SO_OPENTYPE 0x7008 + + int opt = SO_SYNCHRONOUS_NONALERT; + setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char*)&opt, sizeof(opt)); +#endif + + // Get address info + struct addrinfo hints; + struct addrinfo *result; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = socket_flags; + hints.ai_protocol = 0; + + auto service = std::to_string(port); + + if (getaddrinfo(host, service.c_str(), &hints, &result)) { + return INVALID_SOCKET; + } + + for (auto rp = result; rp; rp = rp->ai_next) { + // Create a socket + auto sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); + if (sock == INVALID_SOCKET) { + continue; + } + + // Make 'reuse address' option available + int yes = 1; + setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes)); + + // bind or connect + if (fn(sock, *rp)) { + freeaddrinfo(result); + return sock; + } + + close_socket(sock); + } + + freeaddrinfo(result); + return INVALID_SOCKET; +} + +inline void set_nonblocking(socket_t sock, bool nonblocking) +{ +#ifdef _WIN32 + auto flags = nonblocking ? 1UL : 0UL; + ioctlsocket(sock, FIONBIO, &flags); +#else + auto flags = fcntl(sock, F_GETFL, 0); + fcntl(sock, F_SETFL, nonblocking ? (flags | O_NONBLOCK) : (flags & (~O_NONBLOCK))); +#endif +} + +inline bool is_connection_error() +{ +#ifdef _WIN32 + return WSAGetLastError() != WSAEWOULDBLOCK; +#else + return errno != EINPROGRESS; +#endif +} + +inline std::string get_remote_addr(socket_t sock) { + struct sockaddr_storage addr; + socklen_t len = sizeof(addr); + + if (!getpeername(sock, (struct sockaddr*)&addr, &len)) { + char ipstr[NI_MAXHOST]; + + if (!getnameinfo((struct sockaddr*)&addr, len, + ipstr, sizeof(ipstr), nullptr, 0, NI_NUMERICHOST)) { + return ipstr; + } + } + + return std::string(); +} + +inline bool is_file(const std::string& path) +{ + struct stat st; + return stat(path.c_str(), &st) >= 0 && S_ISREG(st.st_mode); +} + +inline bool is_dir(const std::string& path) +{ + struct stat st; + return stat(path.c_str(), &st) >= 0 && S_ISDIR(st.st_mode); +} + +inline bool is_valid_path(const std::string& path) { + size_t level = 0; + size_t i = 0; + + // Skip slash + while (i < path.size() && path[i] == '/') { + i++; + } + + while (i < path.size()) { + // Read component + auto beg = i; + while (i < path.size() && path[i] != '/') { + i++; + } + + auto len = i - beg; + assert(len > 0); + + if (!path.compare(beg, len, ".")) { + ; + } else if (!path.compare(beg, len, "..")) { + if (level == 0) { + return false; + } + level--; + } else { + level++; + } + + // Skip slash + while (i < path.size() && path[i] == '/') { + i++; + } + } + + return true; +} + +inline void read_file(const std::string& path, std::string& out) +{ + std::ifstream fs(path, std::ios_base::binary); + fs.seekg(0, std::ios_base::end); + auto size = fs.tellg(); + fs.seekg(0); + out.resize(static_cast(size)); + fs.read(&out[0], size); +} + +inline std::string file_extension(const std::string& path) +{ + std::smatch m; + auto pat = std::regex("\\.([a-zA-Z0-9]+)$"); + if (std::regex_search(path, m, pat)) { + return m[1].str(); + } + return std::string(); +} + +inline const char* find_content_type(const std::string& path) +{ + auto ext = file_extension(path); + if (ext == "txt") { + return "text/plain"; + } else if (ext == "html") { + return "text/html"; + } else if (ext == "css") { + return "text/css"; + } else if (ext == "jpeg" || ext == "jpg") { + return "image/jpg"; + } else if (ext == "png") { + return "image/png"; + } else if (ext == "gif") { + return "image/gif"; + } else if (ext == "svg") { + return "image/svg+xml"; + } else if (ext == "ico") { + return "image/x-icon"; + } else if (ext == "json") { + return "application/json"; + } else if (ext == "pdf") { + return "application/pdf"; + } else if (ext == "js") { + return "application/javascript"; + } else if (ext == "xml") { + return "application/xml"; + } else if (ext == "xhtml") { + return "application/xhtml+xml"; + } + return nullptr; +} + +inline const char* status_message(int status) +{ + switch (status) { + case 200: return "OK"; + case 400: return "Bad Request"; + case 404: return "Not Found"; + case 415: return "Unsupported Media Type"; + default: + case 500: return "Internal Server Error"; + } +} + +inline const char* get_header_value(const Headers& headers, const char* key, const char* def) +{ + auto it = headers.find(key); + if (it != headers.end()) { + return it->second.c_str(); + } + return def; +} + +inline int get_header_value_int(const Headers& headers, const char* key, int def) +{ + auto it = headers.find(key); + if (it != headers.end()) { + return std::stoi(it->second); + } + return def; +} + +inline bool read_headers(Stream& strm, Headers& headers) +{ + static std::regex re(R"((.+?):\s*(.+?)\s*\r\n)"); + + const auto bufsiz = 2048; + char buf[bufsiz]; + + stream_line_reader reader(strm, buf, bufsiz); + + for (;;) { + if (!reader.getline()) { + return false; + } + if (!strcmp(reader.ptr(), "\r\n")) { + break; + } + std::cmatch m; + if (std::regex_match(reader.ptr(), m, re)) { + auto key = std::string(m[1]); + auto val = std::string(m[2]); + headers.emplace(key, val); + } + } + + return true; +} + +inline bool read_content_with_length(Stream& strm, std::string& out, size_t len, Progress progress) +{ + out.assign(len, 0); + size_t r = 0; + while (r < len){ + auto n = strm.read(&out[r], len - r); + if (n <= 0) { + return false; + } + + r += n; + + if (progress) { + progress(r, len); + } + } + + return true; +} + +inline bool read_content_without_length(Stream& strm, std::string& out) +{ + for (;;) { + char byte; + auto n = strm.read(&byte, 1); + if (n < 0) { + return false; + } else if (n == 0) { + return true; + } + out += byte; + } + + return true; +} + +inline bool read_content_chunked(Stream& strm, std::string& out) +{ + const auto bufsiz = 16; + char buf[bufsiz]; + + stream_line_reader reader(strm, buf, bufsiz); + + if (!reader.getline()) { + return false; + } + + auto chunk_len = std::stoi(reader.ptr(), 0, 16); + + while (chunk_len > 0){ + std::string chunk; + if (!read_content_with_length(strm, chunk, chunk_len, nullptr)) { + return false; + } + + if (!reader.getline()) { + return false; + } + + if (strcmp(reader.ptr(), "\r\n")) { + break; + } + + out += chunk; + + if (!reader.getline()) { + return false; + } + + chunk_len = std::stoi(reader.ptr(), 0, 16); + } + + if (chunk_len == 0) { + // Reader terminator after chunks + if (!reader.getline() || strcmp(reader.ptr(), "\r\n")) + return false; + } + + return true; +} + +template +bool read_content(Stream& strm, T& x, Progress progress = Progress()) +{ + auto len = get_header_value_int(x.headers, "Content-Length", 0); + + if (len) { + return read_content_with_length(strm, x.body, len, progress); + } else { + const auto& encoding = get_header_value(x.headers, "Transfer-Encoding", ""); + + if (!strcasecmp(encoding, "chunked")) { + return read_content_chunked(strm, x.body); + } else { + return read_content_without_length(strm, x.body); + } + } + + return true; +} + +template +inline void write_headers(Stream& strm, const T& info) +{ + for (const auto& x: info.headers) { + strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str()); + } + strm.write("\r\n"); +} + +inline std::string encode_url(const std::string& s) +{ + std::string result; + + for (auto i = 0; s[i]; i++) { + switch (s[i]) { + case ' ': result += "+"; break; + case '\'': result += "%27"; break; + case ',': result += "%2C"; break; + case ':': result += "%3A"; break; + case ';': result += "%3B"; break; + default: + if (s[i] < 0) { + result += '%'; + char hex[4]; + size_t len = snprintf(hex, sizeof(hex) - 1, "%02X", (unsigned char)s[i]); + assert(len == 2); + result.append(hex, len); + } else { + result += s[i]; + } + break; + } + } + + return result; +} + +inline bool is_hex(char c, int& v) +{ + if (0x20 <= c && isdigit(c)) { + v = c - '0'; + return true; + } else if ('A' <= c && c <= 'F') { + v = c - 'A' + 10; + return true; + } else if ('a' <= c && c <= 'f') { + v = c - 'a' + 10; + return true; + } + return false; +} + +inline bool from_hex_to_i(const std::string& s, int i, int cnt, int& val) +{ + val = 0; + for (; cnt; i++, cnt--) { + if (!s[i]) { + return false; + } + int v = 0; + if (is_hex(s[i], v)) { + val = val * 16 + v; + } else { + return false; + } + } + return true; +} + +inline size_t to_utf8(int code, char* buff) +{ + if (code < 0x0080) { + buff[0] = (code & 0x7F); + return 1; + } else if (code < 0x0800) { + buff[0] = (0xC0 | ((code >> 6) & 0x1F)); + buff[1] = (0x80 | (code & 0x3F)); + return 2; + } else if (code < 0xD800) { + buff[0] = (0xE0 | ((code >> 12) & 0xF)); + buff[1] = (0x80 | ((code >> 6) & 0x3F)); + buff[2] = (0x80 | (code & 0x3F)); + return 3; + } else if (code < 0xE000) { // D800 - DFFF is invalid... + return 0; + } else if (code < 0x10000) { + buff[0] = (0xE0 | ((code >> 12) & 0xF)); + buff[1] = (0x80 | ((code >> 6) & 0x3F)); + buff[2] = (0x80 | (code & 0x3F)); + return 3; + } else if (code < 0x110000) { + buff[0] = (0xF0 | ((code >> 18) & 0x7)); + buff[1] = (0x80 | ((code >> 12) & 0x3F)); + buff[2] = (0x80 | ((code >> 6) & 0x3F)); + buff[3] = (0x80 | (code & 0x3F)); + return 4; + } + + // NOTREACHED + return 0; +} + +inline std::string decode_url(const std::string& s) +{ + std::string result; + + for (int i = 0; s[i]; i++) { + if (s[i] == '%') { + if (s[i + 1] && s[i + 1] == 'u') { + int val = 0; + if (from_hex_to_i(s, i + 2, 4, val)) { + // 4 digits Unicode codes + char buff[4]; + size_t len = to_utf8(val, buff); + if (len > 0) { + result.append(buff, len); + } + i += 5; // 'u0000' + } else { + result += s[i]; + } + } else { + int val = 0; + if (from_hex_to_i(s, i + 1, 2, val)) { + // 2 digits hex codes + result += val; + i += 2; // '00' + } else { + result += s[i]; + } + } + } else if (s[i] == '+') { + result += ' '; + } else { + result += s[i]; + } + } + + return result; +} + +inline void parse_query_text(const std::string& s, Params& params) +{ + split(&s[0], &s[s.size()], '&', [&](const char* b, const char* e) { + std::string key; + std::string val; + split(b, e, '=', [&](const char* b, const char* e) { + if (key.empty()) { + key.assign(b, e); + } else { + val.assign(b, e); + } + }); + params.emplace(key, decode_url(val)); + }); +} + +inline bool parse_multipart_boundary(const std::string& content_type, std::string& boundary) +{ + auto pos = content_type.find("boundary="); + if (pos == std::string::npos) { + return false; + } + + boundary = content_type.substr(pos + 9); + return true; +} + +inline bool parse_multipart_formdata( + const std::string& boundary, const std::string& body, MultipartFiles& files) +{ + static std::string dash = "--"; + static std::string crlf = "\r\n"; + + static std::regex re_content_type( + "Content-Type: (.*?)", std::regex_constants::icase); + + static std::regex re_content_disposition( + "Content-Disposition: form-data; name=\"(.*?)\"(?:; filename=\"(.*?)\")?", + std::regex_constants::icase); + + auto dash_boundary = dash + boundary; + + auto pos = body.find(dash_boundary); + if (pos != 0) { + return false; + } + + pos += dash_boundary.size(); + + auto next_pos = body.find(crlf, pos); + if (next_pos == std::string::npos) { + return false; + } + + pos = next_pos + crlf.size(); + + while (pos < body.size()) { + next_pos = body.find(crlf, pos); + if (next_pos == std::string::npos) { + return false; + } + + std::string name; + MultipartFile file; + + auto header = body.substr(pos, (next_pos - pos)); + + while (pos != next_pos) { + std::smatch m; + if (std::regex_match(header, m, re_content_type)) { + file.content_type = m[1]; + } else if (std::regex_match(header, m, re_content_disposition)) { + name = m[1]; + file.filename = m[2]; + } + + pos = next_pos + crlf.size(); + + next_pos = body.find(crlf, pos); + if (next_pos == std::string::npos) { + return false; + } + + header = body.substr(pos, (next_pos - pos)); + } + + pos = next_pos + crlf.size(); + + next_pos = body.find(crlf + dash_boundary, pos); + + if (next_pos == std::string::npos) { + return false; + } + + file.offset = pos; + file.length = next_pos - pos; + + pos = next_pos + crlf.size() + dash_boundary.size(); + + next_pos = body.find(crlf, pos); + if (next_pos == std::string::npos) { + return false; + } + + files.emplace(name, file); + + pos = next_pos + crlf.size(); + } + + return true; +} + +inline std::string to_lower(const char* beg, const char* end) +{ + std::string out; + auto it = beg; + while (it != end) { + out += ::tolower(*it); + it++; + } + return out; +} + +inline void make_range_header_core(std::string&) {} + +template +inline void make_range_header_core(std::string& field, uint64_t value) +{ + if (!field.empty()) { + field += ", "; + } + field += std::to_string(value) + "-"; +} + +template +inline void make_range_header_core(std::string& field, uint64_t value1, uint64_t value2, Args... args) +{ + if (!field.empty()) { + field += ", "; + } + field += std::to_string(value1) + "-" + std::to_string(value2); + make_range_header_core(field, args...); +} + +#ifdef CPPHTTPLIB_ZLIB_SUPPORT +inline bool can_compress(const std::string& content_type) { + return !content_type.find("text/") || + content_type == "image/svg+xml" || + content_type == "application/javascript" || + content_type == "application/json" || + content_type == "application/xml" || + content_type == "application/xhtml+xml"; +} + +inline void compress(std::string& content) +{ + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + + auto ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8, Z_DEFAULT_STRATEGY); + if (ret != Z_OK) { + return; + } + + strm.avail_in = content.size(); + strm.next_in = (Bytef *)content.data(); + + std::string compressed; + + const auto bufsiz = 16384; + char buff[bufsiz]; + do { + strm.avail_out = bufsiz; + strm.next_out = (Bytef *)buff; + deflate(&strm, Z_FINISH); + compressed.append(buff, bufsiz - strm.avail_out); + } while (strm.avail_out == 0); + + content.swap(compressed); + + deflateEnd(&strm); +} + +inline void decompress(std::string& content) +{ + z_stream strm; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + + // 15 is the value of wbits, which should be at the maximum possible value to ensure + // that any gzip stream can be decoded. The offset of 16 specifies that the stream + // to decompress will be formatted with a gzip wrapper. + auto ret = inflateInit2(&strm, 16 + 15); + if (ret != Z_OK) { + return; + } + + strm.avail_in = content.size(); + strm.next_in = (Bytef *)content.data(); + + std::string decompressed; + + const auto bufsiz = 16384; + char buff[bufsiz]; + do { + strm.avail_out = bufsiz; + strm.next_out = (Bytef *)buff; + inflate(&strm, Z_NO_FLUSH); + decompressed.append(buff, bufsiz - strm.avail_out); + } while (strm.avail_out == 0); + + content.swap(decompressed); + + inflateEnd(&strm); +} +#endif + +#ifdef _WIN32 +class WSInit { +public: + WSInit() { + WSADATA wsaData; + WSAStartup(0x0002, &wsaData); + } + + ~WSInit() { + WSACleanup(); + } +}; + +static WSInit wsinit_; +#endif + +} // namespace detail + +// Header utilities +template +inline std::pair make_range_header(uint64_t value, Args... args) +{ + std::string field; + detail::make_range_header_core(field, value, args...); + field.insert(0, "bytes="); + return std::make_pair("Range", field); +} + +// Request implementation +inline bool Request::has_header(const char* key) const +{ + return headers.find(key) != headers.end(); +} + +inline std::string Request::get_header_value(const char* key) const +{ + return detail::get_header_value(headers, key, ""); +} + +inline void Request::set_header(const char* key, const char* val) +{ + headers.emplace(key, val); +} + +inline bool Request::has_param(const char* key) const +{ + return params.find(key) != params.end(); +} + +inline std::string Request::get_param_value(const char* key) const +{ + auto it = params.find(key); + if (it != params.end()) { + return it->second; + } + return std::string(); +} + +inline bool Request::has_file(const char* key) const +{ + return files.find(key) != files.end(); +} + +inline MultipartFile Request::get_file_value(const char* key) const +{ + auto it = files.find(key); + if (it != files.end()) { + return it->second; + } + return MultipartFile(); +} + +// Response implementation +inline bool Response::has_header(const char* key) const +{ + return headers.find(key) != headers.end(); +} + +inline std::string Response::get_header_value(const char* key) const +{ + return detail::get_header_value(headers, key, ""); +} + +inline void Response::set_header(const char* key, const char* val) +{ + headers.emplace(key, val); +} + +inline void Response::set_redirect(const char* url) +{ + set_header("Location", url); + status = 302; +} + +inline void Response::set_content(const char* s, size_t n, const char* content_type) +{ + body.assign(s, n); + set_header("Content-Type", content_type); +} + +inline void Response::set_content(const std::string& s, const char* content_type) +{ + body = s; + set_header("Content-Type", content_type); +} + +// Rstream implementation +template +inline void Stream::write_format(const char* fmt, const Args& ...args) +{ + const auto bufsiz = 2048; + char buf[bufsiz]; + +#if defined(_MSC_VER) && _MSC_VER < 1900 + auto n = _snprintf_s(buf, bufsiz, bufsiz - 1, fmt, args...); +#else + auto n = snprintf(buf, bufsiz - 1, fmt, args...); +#endif + if (n > 0) { + if (n >= bufsiz - 1) { + std::vector glowable_buf(bufsiz); + + while (n >= static_cast(glowable_buf.size() - 1)) { + glowable_buf.resize(glowable_buf.size() * 2); +#if defined(_MSC_VER) && _MSC_VER < 1900 + n = _snprintf_s(&glowable_buf[0], glowable_buf.size(), glowable_buf.size() - 1, fmt, args...); +#else + n = snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...); +#endif + } + write(&glowable_buf[0], n); + } else { + write(buf, n); + } + } +} + +// Socket stream implementation +inline SocketStream::SocketStream(socket_t sock): sock_(sock) +{ +} + +inline SocketStream::~SocketStream() +{ +} + +inline int SocketStream::read(char* ptr, size_t size) +{ + return recv(sock_, ptr, size, 0); +} + +inline int SocketStream::write(const char* ptr, size_t size) +{ + return send(sock_, ptr, size, 0); +} + +inline int SocketStream::write(const char* ptr) +{ + return write(ptr, strlen(ptr)); +} + +inline std::string SocketStream::get_remote_addr() { + return detail::get_remote_addr(sock_); +} + +// HTTP server implementation +inline Server::Server() + : keep_alive_max_count_(5) + , is_running_(false) + , svr_sock_(INVALID_SOCKET) + , running_threads_(0) +{ +#ifndef _WIN32 + signal(SIGPIPE, SIG_IGN); +#endif +} + +inline Server::~Server() +{ +} + +inline Server& Server::Get(const char* pattern, Handler handler) +{ + get_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server& Server::Post(const char* pattern, Handler handler) +{ + post_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server& Server::Put(const char* pattern, Handler handler) +{ + put_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server& Server::Delete(const char* pattern, Handler handler) +{ + delete_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline Server& Server::Options(const char* pattern, Handler handler) +{ + options_handlers_.push_back(std::make_pair(std::regex(pattern), handler)); + return *this; +} + +inline bool Server::set_base_dir(const char* path) +{ + if (detail::is_dir(path)) { + base_dir_ = path; + return true; + } + return false; +} + +inline void Server::set_error_handler(Handler handler) +{ + error_handler_ = handler; +} + +inline void Server::set_logger(Logger logger) +{ + logger_ = logger; +} + +inline void Server::set_keep_alive_max_count(size_t count) +{ + keep_alive_max_count_ = count; +} + +inline int Server::bind_to_any_port(const char* host, int socket_flags) +{ + return bind_internal(host, 0, socket_flags); +} + +inline bool Server::listen_after_bind() { + return listen_internal(); +} + +inline bool Server::listen(const char* host, int port, int socket_flags) +{ + if (bind_internal(host, port, socket_flags) < 0) + return false; + return listen_internal(); +} + +inline bool Server::is_running() const +{ + return is_running_; +} + +inline void Server::stop() +{ + if (is_running_) { + assert(svr_sock_ != INVALID_SOCKET); + detail::shutdown_socket(svr_sock_); + detail::close_socket(svr_sock_); + svr_sock_ = INVALID_SOCKET; + } +} + +inline bool Server::parse_request_line(const char* s, Request& req) +{ + static std::regex re("(GET|HEAD|POST|PUT|DELETE|OPTIONS) (([^?]+)(?:\\?(.+?))?) (HTTP/1\\.[01])\r\n"); + + std::cmatch m; + if (std::regex_match(s, m, re)) { + req.version = std::string(m[4]); + req.method = std::string(m[1]); + req.target = std::string(m[2]); + req.path = detail::decode_url(m[3]); + + // Parse query text + auto len = std::distance(m[4].first, m[4].second); + if (len > 0) { + detail::parse_query_text(m[4], req.params); + } + + return true; + } + + return false; +} + +inline void Server::write_response(Stream& strm, bool last_connection, const Request& req, Response& res) +{ + assert(res.status != -1); + + if (400 <= res.status && error_handler_) { + error_handler_(req, res); + } + + // Response line + strm.write_format("HTTP/1.1 %d %s\r\n", + res.status, + detail::status_message(res.status)); + + // Headers + if (last_connection || + req.version == "HTTP/1.0" || + req.get_header_value("Connection") == "close") { + res.set_header("Connection", "close"); + } + + if (!res.body.empty()) { +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + // TODO: 'Accpet-Encoding' has gzip, not gzip;q=0 + const auto& encodings = req.get_header_value("Accept-Encoding"); + if (encodings.find("gzip") != std::string::npos && + detail::can_compress(res.get_header_value("Content-Type"))) { + detail::compress(res.body); + res.set_header("Content-Encoding", "gzip"); + } +#endif + + if (!res.has_header("Content-Type")) { + res.set_header("Content-Type", "text/plain"); + } + + auto length = std::to_string(res.body.size()); + res.set_header("Content-Length", length.c_str()); + } + + detail::write_headers(strm, res); + + // Body + if (!res.body.empty() && req.method != "HEAD") { + strm.write(res.body.c_str(), res.body.size()); + } + + // Log + if (logger_) { + logger_(req, res); + } +} + +inline bool Server::handle_file_request(Request& req, Response& res) +{ + if (!base_dir_.empty() && detail::is_valid_path(req.path)) { + std::string path = base_dir_ + req.path; + + if (!path.empty() && path.back() == '/') { + path += "index.html"; + } + + if (detail::is_file(path)) { + detail::read_file(path, res.body); + auto type = detail::find_content_type(path); + if (type) { + res.set_header("Content-Type", type); + } + res.status = 200; + return true; + } + } + + return false; +} + +inline socket_t Server::create_server_socket(const char* host, int port, int socket_flags) const +{ + return detail::create_socket(host, port, + [](socket_t sock, struct addrinfo& ai) -> bool { + if (::bind(sock, ai.ai_addr, ai.ai_addrlen)) { + return false; + } + if (::listen(sock, 5)) { // Listen through 5 channels + return false; + } + return true; + }, socket_flags); +} + +inline int Server::bind_internal(const char* host, int port, int socket_flags) +{ + if (!is_valid()) { + return -1; + } + + svr_sock_ = create_server_socket(host, port, socket_flags); + if (svr_sock_ == INVALID_SOCKET) { + return -1; + } + + if (port == 0) { + struct sockaddr_storage address; + socklen_t len = sizeof(address); + if (getsockname(svr_sock_, reinterpret_cast(&address), &len) == -1) { + return -1; + } + if (address.ss_family == AF_INET) { + return ntohs(reinterpret_cast(&address)->sin_port); + } else if (address.ss_family == AF_INET6) { + return ntohs(reinterpret_cast(&address)->sin6_port); + } else { + return -1; + } + } else { + return port; + } +} + +inline bool Server::listen_internal() +{ + auto ret = true; + + is_running_ = true; + + for (;;) { + auto val = detail::select_read(svr_sock_, 0, 100000); + + if (val == 0) { // Timeout + if (svr_sock_ == INVALID_SOCKET) { + // The server socket was closed by 'stop' method. + break; + } + continue; + } + + socket_t sock = accept(svr_sock_, NULL, NULL); + + if (sock == INVALID_SOCKET) { + if (svr_sock_ != INVALID_SOCKET) { + detail::close_socket(svr_sock_); + ret = false; + } else { + ; // The server socket was closed by user. + } + break; + } + + // TODO: Use thread pool... + std::thread([=]() { + { + std::lock_guard guard(running_threads_mutex_); + running_threads_++; + } + + read_and_close_socket(sock); + + { + std::lock_guard guard(running_threads_mutex_); + running_threads_--; + } + }).detach(); + } + + // TODO: Use thread pool... + for (;;) { + std::this_thread::sleep_for(std::chrono::milliseconds(10)); + std::lock_guard guard(running_threads_mutex_); + if (!running_threads_) { + break; + } + } + + is_running_ = false; + + return ret; +} + +inline bool Server::routing(Request& req, Response& res) +{ + if (req.method == "GET" && handle_file_request(req, res)) { + return true; + } + + if (req.method == "GET" || req.method == "HEAD") { + return dispatch_request(req, res, get_handlers_); + } else if (req.method == "POST") { + return dispatch_request(req, res, post_handlers_); + } else if (req.method == "PUT") { + return dispatch_request(req, res, put_handlers_); + } else if (req.method == "DELETE") { + return dispatch_request(req, res, delete_handlers_); + } else if (req.method == "OPTIONS") { + return dispatch_request(req, res, options_handlers_); + } + return false; +} + +inline bool Server::dispatch_request(Request& req, Response& res, Handlers& handlers) +{ + for (const auto& x: handlers) { + const auto& pattern = x.first; + const auto& handler = x.second; + + if (std::regex_match(req.path, req.matches, pattern)) { + handler(req, res); + return true; + } + } + return false; +} + +inline bool Server::process_request(Stream& strm, bool last_connection, bool& connection_close) +{ + const auto bufsiz = 2048; + char buf[bufsiz]; + + detail::stream_line_reader reader(strm, buf, bufsiz); + + // Connection has been closed on client + if (!reader.getline()) { + return false; + } + + Request req; + Response res; + + res.version = "HTTP/1.1"; + + // Request line and headers + if (!parse_request_line(reader.ptr(), req) || !detail::read_headers(strm, req.headers)) { + res.status = 400; + write_response(strm, last_connection, req, res); + return true; + } + + auto ret = true; + if (req.get_header_value("Connection") == "close") { + // ret = false; + connection_close = true; + } + + req.set_header("REMOTE_ADDR", strm.get_remote_addr().c_str()); + + // Body + if (req.method == "POST" || req.method == "PUT") { + if (!detail::read_content(strm, req)) { + res.status = 400; + write_response(strm, last_connection, req, res); + return ret; + } + + const auto& content_type = req.get_header_value("Content-Type"); + + if (req.get_header_value("Content-Encoding") == "gzip") { +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + detail::decompress(req.body); +#else + res.status = 415; + write_response(strm, last_connection, req, res); + return ret; +#endif + } + + if (!content_type.find("application/x-www-form-urlencoded")) { + detail::parse_query_text(req.body, req.params); + } else if(!content_type.find("multipart/form-data")) { + std::string boundary; + if (!detail::parse_multipart_boundary(content_type, boundary) || + !detail::parse_multipart_formdata(boundary, req.body, req.files)) { + res.status = 400; + write_response(strm, last_connection, req, res); + return ret; + } + } + } + + if (routing(req, res)) { + if (res.status == -1) { + res.status = 200; + } + } else { + res.status = 404; + } + + write_response(strm, last_connection, req, res); + return ret; +} + +inline bool Server::is_valid() const +{ + return true; +} + +inline bool Server::read_and_close_socket(socket_t sock) +{ + return detail::read_and_close_socket( + sock, + keep_alive_max_count_, + [this](Stream& strm, bool last_connection, bool& connection_close) { + return process_request(strm, last_connection, connection_close); + }); +} + +// HTTP client implementation +inline Client::Client( + const char* host, int port, size_t timeout_sec) + : host_(host) + , port_(port) + , timeout_sec_(timeout_sec) + , host_and_port_(host_ + ":" + std::to_string(port_)) +{ +} + +inline Client::~Client() +{ +} + +inline bool Client::is_valid() const +{ + return true; +} + +inline socket_t Client::create_client_socket() const +{ + return detail::create_socket(host_.c_str(), port_, + [=](socket_t sock, struct addrinfo& ai) -> bool { + detail::set_nonblocking(sock, true); + + auto ret = connect(sock, ai.ai_addr, ai.ai_addrlen); + if (ret < 0) { + if (detail::is_connection_error() || + !detail::wait_until_socket_is_ready(sock, timeout_sec_, 0)) { + detail::close_socket(sock); + return false; + } + } + + detail::set_nonblocking(sock, false); + return true; + }); +} + +inline bool Client::read_response_line(Stream& strm, Response& res) +{ + const auto bufsiz = 2048; + char buf[bufsiz]; + + detail::stream_line_reader reader(strm, buf, bufsiz); + + if (!reader.getline()) { + return false; + } + + const static std::regex re("(HTTP/1\\.[01]) (\\d+?) .+\r\n"); + + std::cmatch m; + if (std::regex_match(reader.ptr(), m, re)) { + res.version = std::string(m[1]); + res.status = std::stoi(std::string(m[2])); + } + + return true; +} + +inline bool Client::send(Request& req, Response& res) +{ + if (req.path.empty()) { + return false; + } + + auto sock = create_client_socket(); + if (sock == INVALID_SOCKET) { + return false; + } + + return read_and_close_socket(sock, req, res); +} + +inline void Client::write_request(Stream& strm, Request& req) +{ + auto path = detail::encode_url(req.path); + + // Request line + strm.write_format("%s %s HTTP/1.1\r\n", + req.method.c_str(), + path.c_str()); + + // Headers + req.set_header("Host", host_and_port_.c_str()); + + if (!req.has_header("Accept")) { + req.set_header("Accept", "*/*"); + } + + if (!req.has_header("User-Agent")) { + req.set_header("User-Agent", "cpp-httplib/0.2"); + } + + // TODO: Support KeepAlive connection + // if (!req.has_header("Connection")) { + req.set_header("Connection", "close"); + // } + + if (!req.body.empty()) { + if (!req.has_header("Content-Type")) { + req.set_header("Content-Type", "text/plain"); + } + + auto length = std::to_string(req.body.size()); + req.set_header("Content-Length", length.c_str()); + } + + detail::write_headers(strm, req); + + // Body + if (!req.body.empty()) { + if (req.get_header_value("Content-Type") == "application/x-www-form-urlencoded") { + auto str = detail::encode_url(req.body); + strm.write(str.c_str(), str.size()); + } else { + strm.write(req.body.c_str(), req.body.size()); + } + } +} + +inline bool Client::process_request(Stream& strm, Request& req, Response& res, bool& connection_close) +{ + // Send request + write_request(strm, req); + + // Receive response and headers + if (!read_response_line(strm, res) || !detail::read_headers(strm, res.headers)) { + return false; + } + + if (res.get_header_value("Connection") == "close" || res.version == "HTTP/1.0") { + connection_close = true; + } + + // Body + if (req.method != "HEAD") { + if (!detail::read_content(strm, res, req.progress)) { + return false; + } + + if (res.get_header_value("Content-Encoding") == "gzip") { +#ifdef CPPHTTPLIB_ZLIB_SUPPORT + detail::decompress(res.body); +#else + return false; +#endif + } + } + + return true; +} + +inline bool Client::read_and_close_socket(socket_t sock, Request& req, Response& res) +{ + return detail::read_and_close_socket( + sock, + 0, + [&](Stream& strm, bool /*last_connection*/, bool& connection_close) { + return process_request(strm, req, res, connection_close); + }); +} + +inline std::shared_ptr Client::Get(const char* path, Progress progress) +{ + return Get(path, Headers(), progress); +} + +inline std::shared_ptr Client::Get(const char* path, const Headers& headers, Progress progress) +{ + Request req; + req.method = "GET"; + req.path = path; + req.headers = headers; + req.progress = progress; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; +} + +inline std::shared_ptr Client::Head(const char* path) +{ + return Head(path, Headers()); +} + +inline std::shared_ptr Client::Head(const char* path, const Headers& headers) +{ + Request req; + req.method = "HEAD"; + req.headers = headers; + req.path = path; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; +} + +inline std::shared_ptr Client::Post( + const char* path, const std::string& body, const char* content_type) +{ + return Post(path, Headers(), body, content_type); +} + +inline std::shared_ptr Client::Post( + const char* path, const Headers& headers, const std::string& body, const char* content_type) +{ + Request req; + req.method = "POST"; + req.headers = headers; + req.path = path; + + req.headers.emplace("Content-Type", content_type); + req.body = body; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; +} + +inline std::shared_ptr Client::Post(const char* path, const Params& params) +{ + return Post(path, Headers(), params); +} + +inline std::shared_ptr Client::Post(const char* path, const Headers& headers, const Params& params) +{ + std::string query; + for (auto it = params.begin(); it != params.end(); ++it) { + if (it != params.begin()) { + query += "&"; + } + query += it->first; + query += "="; + query += it->second; + } + + return Post(path, headers, query, "application/x-www-form-urlencoded"); +} + +inline std::shared_ptr Client::Put( + const char* path, const std::string& body, const char* content_type) +{ + return Put(path, Headers(), body, content_type); +} + +inline std::shared_ptr Client::Put( + const char* path, const Headers& headers, const std::string& body, const char* content_type) +{ + Request req; + req.method = "PUT"; + req.headers = headers; + req.path = path; + + req.headers.emplace("Content-Type", content_type); + req.body = body; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; +} + +inline std::shared_ptr Client::Delete(const char* path) +{ + return Delete(path, Headers()); +} + +inline std::shared_ptr Client::Delete(const char* path, const Headers& headers) +{ + Request req; + req.method = "DELETE"; + req.path = path; + req.headers = headers; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; +} + +inline std::shared_ptr Client::Options(const char* path) +{ + return Options(path, Headers()); +} + +inline std::shared_ptr Client::Options(const char* path, const Headers& headers) +{ + Request req; + req.method = "OPTIONS"; + req.path = path; + req.headers = headers; + + auto res = std::make_shared(); + + return send(req, *res) ? res : nullptr; +} + +/* + * SSL Implementation + */ +#ifdef CPPHTTPLIB_OPENSSL_SUPPORT +namespace detail { + +template +inline bool read_and_close_socket_ssl( + socket_t sock, size_t keep_alive_max_count, + // TODO: OpenSSL 1.0.2 occasionally crashes... + // The upcoming 1.1.0 is going to be thread safe. + SSL_CTX* ctx, std::mutex& ctx_mutex, + U SSL_connect_or_accept, V setup, + T callback) +{ + SSL* ssl = nullptr; + { + std::lock_guard guard(ctx_mutex); + + ssl = SSL_new(ctx); + if (!ssl) { + return false; + } + } + + auto bio = BIO_new_socket(sock, BIO_NOCLOSE); + SSL_set_bio(ssl, bio, bio); + + setup(ssl); + + SSL_connect_or_accept(ssl); + + bool ret = false; + + if (keep_alive_max_count > 0) { + auto count = keep_alive_max_count; + while (count > 0 && + detail::select_read(sock, + CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND, + CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0) { + SSLSocketStream strm(sock, ssl); + auto last_connection = count == 1; + auto connection_close = false; + + ret = callback(strm, last_connection, connection_close); + if (!ret || connection_close) { + break; + } + + count--; + } + } else { + SSLSocketStream strm(sock, ssl); + auto dummy_connection_close = false; + ret = callback(strm, true, dummy_connection_close); + } + + SSL_shutdown(ssl); + + { + std::lock_guard guard(ctx_mutex); + SSL_free(ssl); + } + + close_socket(sock); + + return ret; +} + +class SSLInit { +public: + SSLInit() { + SSL_load_error_strings(); + SSL_library_init(); + } +}; + +static SSLInit sslinit_; + +} // namespace detail + +// SSL socket stream implementation +inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL* ssl) + : sock_(sock), ssl_(ssl) +{ +} + +inline SSLSocketStream::~SSLSocketStream() +{ +} + +inline int SSLSocketStream::read(char* ptr, size_t size) +{ + return SSL_read(ssl_, ptr, size); +} + +inline int SSLSocketStream::write(const char* ptr, size_t size) +{ + return SSL_write(ssl_, ptr, size); +} + +inline int SSLSocketStream::write(const char* ptr) +{ + return write(ptr, strlen(ptr)); +} + +inline std::string SSLSocketStream::get_remote_addr() { + return detail::get_remote_addr(sock_); +} + +// SSL HTTP server implementation +inline SSLServer::SSLServer(const char* cert_path, const char* private_key_path) +{ + ctx_ = SSL_CTX_new(SSLv23_server_method()); + + if (ctx_) { + SSL_CTX_set_options(ctx_, + SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION); + + // auto ecdh = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + // SSL_CTX_set_tmp_ecdh(ctx_, ecdh); + // EC_KEY_free(ecdh); + + if (SSL_CTX_use_certificate_file(ctx_, cert_path, SSL_FILETYPE_PEM) != 1 || + SSL_CTX_use_PrivateKey_file(ctx_, private_key_path, SSL_FILETYPE_PEM) != 1) { + SSL_CTX_free(ctx_); + ctx_ = nullptr; + } + } +} + +inline SSLServer::~SSLServer() +{ + if (ctx_) { + SSL_CTX_free(ctx_); + } +} + +inline bool SSLServer::is_valid() const +{ + return ctx_; +} + +inline bool SSLServer::read_and_close_socket(socket_t sock) +{ + return detail::read_and_close_socket_ssl( + sock, + keep_alive_max_count_, + ctx_, ctx_mutex_, + SSL_accept, + [](SSL* /*ssl*/) {}, + [this](Stream& strm, bool last_connection, bool& connection_close) { + return process_request(strm, last_connection, connection_close); + }); +} + +// SSL HTTP client implementation +inline SSLClient::SSLClient(const char* host, int port, size_t timeout_sec) + : Client(host, port, timeout_sec) +{ + ctx_ = SSL_CTX_new(SSLv23_client_method()); +} + +inline SSLClient::~SSLClient() +{ + if (ctx_) { + SSL_CTX_free(ctx_); + } +} + +inline bool SSLClient::is_valid() const +{ + return ctx_; +} + +inline bool SSLClient::read_and_close_socket(socket_t sock, Request& req, Response& res) +{ + return is_valid() && detail::read_and_close_socket_ssl( + sock, 0, + ctx_, ctx_mutex_, + SSL_connect, + [&](SSL* ssl) { + SSL_set_tlsext_host_name(ssl, host_.c_str()); + }, + [&](Stream& strm, bool /*last_connection*/, bool& connection_close) { + return process_request(strm, req, res, connection_close); + }); +} +#endif + +} // namespace httplib + +#endif + +// vim: et ts=4 sw=4 cin cino={1s ff=unix diff --git a/shared/sentry/external/crashpad/third_party/edo/BUILD.gn b/shared/sentry/external/crashpad/third_party/edo/BUILD.gn new file mode 100644 index 000000000..ea6e9d38b --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/edo/BUILD.gn @@ -0,0 +1,144 @@ +# Copyright 2018 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("../../build/crashpad_buildconfig.gni") + +if (crashpad_is_in_chromium) { + group("edo") { + testonly = true + public_deps = [ "//ios/third_party/edo" ] + } +} else { + config("config") { + include_dirs = [ "../../third_party/edo/edo" ] + } + + source_set("edo") { + testonly = true + + sources = [ + "edo/Channel/Sources/EDOBlockingQueue.h", + "edo/Channel/Sources/EDOBlockingQueue.m", + "edo/Channel/Sources/EDOChannel.h", + "edo/Channel/Sources/EDOChannelErrors.h", + "edo/Channel/Sources/EDOChannelErrors.m", + "edo/Channel/Sources/EDOChannelForwarder.h", + "edo/Channel/Sources/EDOChannelForwarder.m", + "edo/Channel/Sources/EDOChannelMultiplexer.h", + "edo/Channel/Sources/EDOChannelMultiplexer.m", + "edo/Channel/Sources/EDOChannelPool.h", + "edo/Channel/Sources/EDOChannelPool.m", + "edo/Channel/Sources/EDOChannelUtil.h", + "edo/Channel/Sources/EDOChannelUtil.m", + "edo/Channel/Sources/EDOHostPort.h", + "edo/Channel/Sources/EDOHostPort.m", + "edo/Channel/Sources/EDOListenSocket.h", + "edo/Channel/Sources/EDOListenSocket.m", + "edo/Channel/Sources/EDOSocket.h", + "edo/Channel/Sources/EDOSocket.m", + "edo/Channel/Sources/EDOSocketChannel.h", + "edo/Channel/Sources/EDOSocketChannel.m", + "edo/Channel/Sources/EDOSocketPort.h", + "edo/Channel/Sources/EDOSocketPort.m", + "edo/Device/Sources/EDODeviceChannel.h", + "edo/Device/Sources/EDODeviceChannel.m", + "edo/Device/Sources/EDODeviceConnector.h", + "edo/Device/Sources/EDODeviceConnector.m", + "edo/Device/Sources/EDODeviceDetector.h", + "edo/Device/Sources/EDODeviceDetector.m", + "edo/Device/Sources/EDOUSBMuxUtil.h", + "edo/Device/Sources/EDOUSBMuxUtil.m", + "edo/DeviceForwarder/Sources/EDODeviceForwardersManager.h", + "edo/DeviceForwarder/Sources/EDODeviceForwardersManager.m", + "edo/Measure/Sources/EDONumericMeasure.h", + "edo/Measure/Sources/EDONumericMeasure.m", + "edo/Service/Sources/EDOBlockObject.h", + "edo/Service/Sources/EDOBlockObject.m", + "edo/Service/Sources/EDOClassMessage.h", + "edo/Service/Sources/EDOClassMessage.m", + "edo/Service/Sources/EDOClientService+Private.h", + "edo/Service/Sources/EDOClientService.h", + "edo/Service/Sources/EDOClientService.m", + "edo/Service/Sources/EDOClientServiceStatsCollector.h", + "edo/Service/Sources/EDOClientServiceStatsCollector.m", + "edo/Service/Sources/EDODeallocationTracker.h", + "edo/Service/Sources/EDODeallocationTracker.m", + "edo/Service/Sources/EDOExecutor.h", + "edo/Service/Sources/EDOExecutor.m", + "edo/Service/Sources/EDOExecutorMessage.h", + "edo/Service/Sources/EDOExecutorMessage.m", + "edo/Service/Sources/EDOHostNamingService+Private.h", + "edo/Service/Sources/EDOHostNamingService.h", + "edo/Service/Sources/EDOHostNamingService.m", + "edo/Service/Sources/EDOHostService+Handlers.h", + "edo/Service/Sources/EDOHostService+Handlers.m", + "edo/Service/Sources/EDOHostService+Private.h", + "edo/Service/Sources/EDOHostService.h", + "edo/Service/Sources/EDOHostService.m", + "edo/Service/Sources/EDOInvocationMessage.h", + "edo/Service/Sources/EDOInvocationMessage.m", + "edo/Service/Sources/EDOMessage.h", + "edo/Service/Sources/EDOMessage.m", + "edo/Service/Sources/EDOMethodSignatureMessage.h", + "edo/Service/Sources/EDOMethodSignatureMessage.m", + "edo/Service/Sources/EDOObject+EDOParameter.m", + "edo/Service/Sources/EDOObject+Invocation.m", + "edo/Service/Sources/EDOObject+Private.h", + "edo/Service/Sources/EDOObject.h", + "edo/Service/Sources/EDOObject.m", + "edo/Service/Sources/EDOObjectAliveMessage.h", + "edo/Service/Sources/EDOObjectAliveMessage.m", + "edo/Service/Sources/EDOObjectMessage.h", + "edo/Service/Sources/EDOObjectMessage.m", + "edo/Service/Sources/EDOObjectReleaseMessage.h", + "edo/Service/Sources/EDOObjectReleaseMessage.m", + "edo/Service/Sources/EDOParameter.h", + "edo/Service/Sources/EDOParameter.m", + "edo/Service/Sources/EDOProtocolObject.h", + "edo/Service/Sources/EDOProtocolObject.m", + "edo/Service/Sources/EDORemoteException.h", + "edo/Service/Sources/EDORemoteException.m", + "edo/Service/Sources/EDORemoteVariable.h", + "edo/Service/Sources/EDORemoteVariable.m", + "edo/Service/Sources/EDORuntimeUtils.h", + "edo/Service/Sources/EDORuntimeUtils.m", + "edo/Service/Sources/EDOServiceError.h", + "edo/Service/Sources/EDOServiceError.m", + "edo/Service/Sources/EDOServiceException.h", + "edo/Service/Sources/EDOServiceException.m", + "edo/Service/Sources/EDOServicePort.h", + "edo/Service/Sources/EDOServicePort.m", + "edo/Service/Sources/EDOServiceRequest.h", + "edo/Service/Sources/EDOServiceRequest.m", + "edo/Service/Sources/EDOTimingFunctions.h", + "edo/Service/Sources/EDOTimingFunctions.m", + "edo/Service/Sources/EDOValueObject+EDOParameter.m", + "edo/Service/Sources/EDOValueObject.h", + "edo/Service/Sources/EDOValueObject.m", + "edo/Service/Sources/EDOValueType.m", + "edo/Service/Sources/EDOWeakObject.h", + "edo/Service/Sources/EDOWeakObject.m", + "edo/Service/Sources/NSBlock+EDOInvocation.m", + "edo/Service/Sources/NSKeyedArchiver+EDOAdditions.h", + "edo/Service/Sources/NSKeyedArchiver+EDOAdditions.m", + "edo/Service/Sources/NSKeyedUnarchiver+EDOAdditions.h", + "edo/Service/Sources/NSKeyedUnarchiver+EDOAdditions.m", + "edo/Service/Sources/NSObject+EDOBlockedType.h", + "edo/Service/Sources/NSObject+EDOBlockedType.m", + "edo/Service/Sources/NSObject+EDOParameter.h", + "edo/Service/Sources/NSObject+EDOParameter.m", + "edo/Service/Sources/NSObject+EDOValue.h", + "edo/Service/Sources/NSObject+EDOValue.m", + "edo/Service/Sources/NSObject+EDOValueObject.h", + "edo/Service/Sources/NSObject+EDOValueObject.m", + "edo/Service/Sources/NSObject+EDOWeakObject.h", + "edo/Service/Sources/NSObject+EDOWeakObject.m", + "edo/Service/Sources/NSProxy+EDOParameter.h", + "edo/Service/Sources/NSProxy+EDOParameter.m", + ] + + public_configs = [ ":config" ] + deps = [ "../../build:ios_enable_arc" ] + } +} diff --git a/shared/sentry/external/crashpad/third_party/edo/README.crashpad b/shared/sentry/external/crashpad/third_party/edo/README.crashpad new file mode 100644 index 000000000..fb8315825 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/edo/README.crashpad @@ -0,0 +1,10 @@ +Name: eDistantObject +Short Name: EDO +URL: https://github.com/google/eDistantObject +Revision: See DEPS +License: Apache 2.0 +License File: edo/LICENSE +Security Critical: no + +Description: +iOS remote method invocations (distant object) over Inter-process communication layer. diff --git a/shared/sentry/external/crashpad/third_party/fuchsia/BUILD.gn b/shared/sentry/external/crashpad/third_party/fuchsia/BUILD.gn new file mode 100644 index 000000000..b1ed5e4b8 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/fuchsia/BUILD.gn @@ -0,0 +1,38 @@ +# Copyright 2018 The Crashpad Authors. All rights reserved. +# +# 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("../../build/crashpad_buildconfig.gni") + +if (crashpad_is_in_fuchsia) { + group("fuchsia") { + public_deps = [ + "//sdk/lib/fdio", + "//zircon/public/lib/zx", + ] + } +} else if (crashpad_is_in_chromium) { + group("fuchsia") { + public_deps = [ + "//third_party/fuchsia-sdk/sdk/pkg/fdio", + "//third_party/fuchsia-sdk/sdk/pkg/zx", + ] + } +} else { + group("fuchsia") { + public_deps = [ + "//third_party/fuchsia/sdk/$host_os-amd64/pkg/fdio", + "//third_party/fuchsia/sdk/$host_os-amd64/pkg/zx", + ] + } +} diff --git a/shared/sentry/external/crashpad/third_party/fuchsia/README.crashpad b/shared/sentry/external/crashpad/third_party/fuchsia/README.crashpad new file mode 100644 index 000000000..8bf0a9148 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/fuchsia/README.crashpad @@ -0,0 +1,3 @@ +This directory is a placeholder for Fuchsia tools that will be downloaded by +CIPD (https://github.com/luci/luci-go/tree/master/cipd). The CIPD update happens +as part of gclient runhooks. diff --git a/shared/sentry/external/crashpad/third_party/fuchsia/runner.py b/shared/sentry/external/crashpad/third_party/fuchsia/runner.py new file mode 100755 index 000000000..229d8a42b --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/fuchsia/runner.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python + +# Copyright 2018 The Crashpad Authors. All rights reserved. +# +# 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 os +import sys + +os.execv(sys.argv[1], sys.argv[1:]) diff --git a/shared/sentry/external/crashpad/third_party/getopt/BUILD.gn b/shared/sentry/external/crashpad/third_party/getopt/BUILD.gn new file mode 100644 index 000000000..da27624f6 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/getopt/BUILD.gn @@ -0,0 +1,20 @@ +# Copyright 2014 The Crashpad Authors. All rights reserved. +# +# 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. + +static_library("getopt") { + sources = [ + "getopt.cc", + "getopt.h", + ] +} diff --git a/shared/sentry/external/crashpad/third_party/getopt/CMakeLists.txt b/shared/sentry/external/crashpad/third_party/getopt/CMakeLists.txt new file mode 100644 index 000000000..24175ce59 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/getopt/CMakeLists.txt @@ -0,0 +1,18 @@ +if(MSVC) + add_library(crashpad_getopt STATIC + getopt.cc + getopt.h + ) + target_include_directories(crashpad_getopt PUBLIC + $ + ) + target_link_libraries(crashpad_getopt PRIVATE + $ + ) + crashpad_install_target(crashpad_getopt) +else() + add_library(crashpad_getopt INTERFACE) +endif() + +set_property(TARGET crashpad_getopt PROPERTY EXPORT_NAME getopt) +add_library(crashpad::getopt ALIAS crashpad_getopt) diff --git a/shared/sentry/external/crashpad/third_party/getopt/LICENSE b/shared/sentry/external/crashpad/third_party/getopt/LICENSE new file mode 100644 index 000000000..4444b1201 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/getopt/LICENSE @@ -0,0 +1,5 @@ +Copyright (C) 1997 Gregory Pietsch + +[These files] are hereby placed in the public domain without restrictions. Just +give the author credit, don't claim you wrote it or prevent anyone else from +using it. diff --git a/shared/sentry/external/crashpad/third_party/getopt/README.crashpad b/shared/sentry/external/crashpad/third_party/getopt/README.crashpad new file mode 100644 index 000000000..b7ff95436 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/getopt/README.crashpad @@ -0,0 +1,20 @@ +Name: Gregory Pietsch getopt +Short Name: getopt +URL: https://sourceware.org/ml/newlib/2005/msg00758.html +License: Public domain +License File: LICENSE +Security Critical: no + +Description: +A public domain implementation of getopt. + +Local Modifications: + - Minor compilation fixes applied for Windows. + - NO_ARG, REQUIRED_ARG, and OPTIONAL_ARG were renamed to the more traditional + no_argument, required_argument, and optional_argument for source + compatibility with BSD and glibc getopt_long(). + - Add copy of copyright (Public domain) to the top of both files for Chromium's + checklicenses step. + - Compiled as .cc, and wrapped in namespace crashpad. + - memcmp() -> strncmp() in getopt.cc to make ASan happier about some string + manipulation. diff --git a/shared/sentry/external/crashpad/third_party/getopt/getopt.cc b/shared/sentry/external/crashpad/third_party/getopt/getopt.cc new file mode 100644 index 000000000..7dfd888ce --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/getopt/getopt.cc @@ -0,0 +1,423 @@ +/* +Copyright (C) 1997 Gregory Pietsch + +[These files] are hereby placed in the public domain without restrictions. Just +give the author credit, don't claim you wrote it or prevent anyone else from +using it. +*/ + +/**************************************************************************** + +getopt.c - Read command line options + +AUTHOR: Gregory Pietsch +CREATED Fri Jan 10 21:13:05 1997 + +DESCRIPTION: + +The getopt() function parses the command line arguments. Its arguments argc +and argv are the argument count and array as passed to the main() function +on program invocation. The argument optstring is a list of available option +characters. If such a character is followed by a colon (`:'), the option +takes an argument, which is placed in optarg. If such a character is +followed by two colons, the option takes an optional argument, which is +placed in optarg. If the option does not take an argument, optarg is NULL. + +The external variable optind is the index of the next array element of argv +to be processed; it communicates from one call to the next which element to +process. + +The getopt_long() function works like getopt() except that it also accepts +long options started by two dashes `--'. If these take values, it is either +in the form + +--arg=value + + or + +--arg value + +It takes the additional arguments longopts which is a pointer to the first +element of an array of type GETOPT_LONG_OPTION_T. The last element of the +array has to be filled with NULL for the name field. + +The longind pointer points to the index of the current long option relative +to longopts if it is non-NULL. + +The getopt() function returns the option character if the option was found +successfully, `:' if there was a missing parameter for one of the options, +`?' for an unknown option character, and EOF for the end of the option list. + +The getopt_long() function's return value is described in the header file. + +The function getopt_long_only() is identical to getopt_long(), except that a +plus sign `+' can introduce long options as well as `--'. + +The following describes how to deal with options that follow non-option +argv-elements. + +If the caller did not specify anything, the default is REQUIRE_ORDER if the +environment variable POSIXLY_CORRECT is defined, PERMUTE otherwise. + +REQUIRE_ORDER means don't recognize them as options; stop option processing +when the first non-option is seen. This is what Unix does. This mode of +operation is selected by either setting the environment variable +POSIXLY_CORRECT, or using `+' as the first character of the optstring +parameter. + +PERMUTE is the default. We permute the contents of ARGV as we scan, so that +eventually all the non-options are at the end. This allows options to be +given in any order, even with programs that were not written to expect this. + +RETURN_IN_ORDER is an option available to programs that were written to +expect options and other argv-elements in any order and that care about the +ordering of the two. We describe each non-option argv-element as if it were +the argument of an option with character code 1. Using `-' as the first +character of the optstring parameter selects this mode of operation. + +The special argument `--' forces an end of option-scanning regardless of the +value of ordering. In the case of RETURN_IN_ORDER, only `--' can cause +getopt() and friends to return EOF with optind != argc. + +COPYRIGHT NOTICE AND DISCLAIMER: + +Copyright (C) 1997 Gregory Pietsch + +This file and the accompanying getopt.h header file are hereby placed in the +public domain without restrictions. Just give the author credit, don't +claim you wrote it or prevent anyone else from using it. + +Gregory Pietsch's current e-mail address: +gpietsch@comcast.net +****************************************************************************/ + +/* include files */ +#include +#include +#include +#ifndef GETOPT_H +#include "getopt.h" +#endif + +namespace crashpad { + +/* macros */ + +/* types */ +typedef enum GETOPT_ORDERING_T +{ + PERMUTE, + RETURN_IN_ORDER, + REQUIRE_ORDER +} GETOPT_ORDERING_T; + +/* globally-defined variables */ +char *optarg = NULL; +int optind = 0; +int opterr = 1; +int optopt = '?'; + +/* functions */ + +/* reverse_argv_elements: reverses num elements starting at argv */ +static void +reverse_argv_elements (char **argv, int num) +{ + int i; + char *tmp; + + for (i = 0; i < (num >> 1); i++) + { + tmp = argv[i]; + argv[i] = argv[num - i - 1]; + argv[num - i - 1] = tmp; + } +} + +/* permute: swap two blocks of argv-elements given their lengths */ +static void +permute (char **argv, int len1, int len2) +{ + reverse_argv_elements (argv, len1); + reverse_argv_elements (argv, len1 + len2); + reverse_argv_elements (argv, len2); +} + +/* is_option: is this argv-element an option or the end of the option list? */ +static int +is_option (char *argv_element, int only) +{ + return ((argv_element == NULL) + || (argv_element[0] == '-') || (only && argv_element[0] == '+')); +} + +/* getopt_internal: the function that does all the dirty work */ +static int +getopt_internal (int argc, char **argv, char *shortopts, + GETOPT_LONG_OPTION_T * longopts, int *longind, int only) +{ + GETOPT_ORDERING_T ordering = PERMUTE; + static size_t optwhere = 0; + size_t permute_from = 0; + int num_nonopts = 0; + int optindex = 0; + size_t match_chars = 0; + char *possible_arg = NULL; + int longopt_match = -1; + int has_arg = -1; + char *cp = NULL; + int arg_next = 0; + + /* first, deal with silly parameters and easy stuff */ + if (argc == 0 || argv == NULL || (shortopts == NULL && longopts == NULL)) + return (optopt = '?'); + if (optind >= argc || argv[optind] == NULL) + return EOF; + if (strcmp (argv[optind], "--") == 0) + { + optind++; + return EOF; + } + /* if this is our first time through */ + if (optind == 0) { + optind = 1; + optwhere = 1; + } + + /* define ordering */ + if (shortopts != NULL && (*shortopts == '-' || *shortopts == '+')) + { + ordering = (*shortopts == '-') ? RETURN_IN_ORDER : REQUIRE_ORDER; + shortopts++; + } + else + ordering = (getenv ("POSIXLY_CORRECT") != NULL) ? REQUIRE_ORDER : PERMUTE; + + /* + * based on ordering, find our next option, if we're at the beginning of + * one + */ + if (optwhere == 1) + { + switch (ordering) + { + case PERMUTE: + permute_from = optind; + num_nonopts = 0; + while (!is_option (argv[optind], only)) + { + optind++; + num_nonopts++; + } + if (argv[optind] == NULL) + { + /* no more options */ + optind = (int)permute_from; + return EOF; + } + else if (strcmp (argv[optind], "--") == 0) + { + /* no more options, but have to get `--' out of the way */ + permute (argv + permute_from, num_nonopts, 1); + optind = (int)(permute_from + 1); + return EOF; + } + break; + case RETURN_IN_ORDER: + if (!is_option (argv[optind], only)) + { + optarg = argv[optind++]; + return (optopt = 1); + } + break; + case REQUIRE_ORDER: + if (!is_option (argv[optind], only)) + return EOF; + break; + } + } + /* we've got an option, so parse it */ + + /* first, is it a long option? */ + if (longopts != NULL + && (strncmp (argv[optind], "--", 2) == 0 + || (only && argv[optind][0] == '+')) && optwhere == 1) + { + /* handle long options */ + if (strncmp (argv[optind], "--", 2) == 0) + optwhere = 2; + longopt_match = -1; + possible_arg = strchr (argv[optind] + optwhere, '='); + if (possible_arg == NULL) + { + /* no =, so next argv might be arg */ + match_chars = strlen (argv[optind]); + possible_arg = argv[optind] + match_chars; + match_chars = match_chars - optwhere; + } + else + match_chars = (possible_arg - argv[optind]) - optwhere; + for (optindex = 0; longopts[optindex].name != NULL; optindex++) + { + if (strncmp (argv[optind] + optwhere, + longopts[optindex].name, match_chars) == 0) + { + /* do we have an exact match? */ + if (match_chars == strlen (longopts[optindex].name)) + { + longopt_match = optindex; + break; + } + /* do any characters match? */ + else + { + if (longopt_match < 0) + longopt_match = optindex; + else + { + /* we have ambiguous options */ + if (opterr) + fprintf (stderr, "%s: option `%s' is ambiguous " + "(could be `--%s' or `--%s')\n", + argv[0], + argv[optind], + longopts[longopt_match].name, + longopts[optindex].name); + return (optopt = '?'); + } + } + } + } + if (longopt_match >= 0) + has_arg = longopts[longopt_match].has_arg; + } + /* if we didn't find a long option, is it a short option? */ + if (longopt_match < 0 && shortopts != NULL) + { + cp = strchr (shortopts, argv[optind][optwhere]); + if (cp == NULL) + { + /* couldn't find option in shortopts */ + if (opterr) + fprintf (stderr, + "%s: invalid option -- `-%c'\n", + argv[0], argv[optind][optwhere]); + optwhere++; + if (argv[optind][optwhere] == '\0') + { + optind++; + optwhere = 1; + } + return (optopt = '?'); + } + has_arg = ((cp[1] == ':') ? ((cp[2] == ':') ? optional_argument + : required_argument) + : no_argument); + possible_arg = argv[optind] + optwhere + 1; + optopt = *cp; + } + /* get argument and reset optwhere */ + arg_next = 0; + switch (has_arg) + { + case optional_argument: + if (*possible_arg == '=') + possible_arg++; + if (*possible_arg != '\0') + { + optarg = possible_arg; + optwhere = 1; + } + else + optarg = NULL; + break; + case required_argument: + if (*possible_arg == '=') + possible_arg++; + if (*possible_arg != '\0') + { + optarg = possible_arg; + optwhere = 1; + } + else if (optind + 1 >= argc) + { + if (opterr) + { + fprintf (stderr, "%s: argument required for option `", argv[0]); + if (longopt_match >= 0) + fprintf (stderr, "--%s'\n", longopts[longopt_match].name); + else + fprintf (stderr, "-%c'\n", *cp); + } + optind++; + return (optopt = ':'); + } + else + { + optarg = argv[optind + 1]; + arg_next = 1; + optwhere = 1; + } + break; + case no_argument: + if (longopt_match < 0) + { + optwhere++; + if (argv[optind][optwhere] == '\0') + optwhere = 1; + } + else + optwhere = 1; + optarg = NULL; + break; + } + + /* do we have to permute or otherwise modify optind? */ + if (ordering == PERMUTE && optwhere == 1 && num_nonopts != 0) + { + permute (argv + permute_from, num_nonopts, 1 + arg_next); + optind = (int)(permute_from + 1 + arg_next); + } + else if (optwhere == 1) + optind = optind + 1 + arg_next; + + /* finally return */ + if (longopt_match >= 0) + { + if (longind != NULL) + *longind = longopt_match; + if (longopts[longopt_match].flag != NULL) + { + *(longopts[longopt_match].flag) = longopts[longopt_match].val; + return 0; + } + else + return longopts[longopt_match].val; + } + else + return optopt; +} + +int +getopt (int argc, char **argv, char *optstring) +{ + return getopt_internal (argc, argv, optstring, NULL, NULL, 0); +} + +int +getopt_long (int argc, char **argv, const char *shortopts, + const GETOPT_LONG_OPTION_T * longopts, int *longind) +{ + return getopt_internal (argc, argv, (char*)shortopts, (GETOPT_LONG_OPTION_T*)longopts, longind, 0); +} + +int +getopt_long_only (int argc, char **argv, const char *shortopts, + const GETOPT_LONG_OPTION_T * longopts, int *longind) +{ + return getopt_internal (argc, argv, (char*)shortopts, (GETOPT_LONG_OPTION_T*)longopts, longind, 1); +} + +} // namespace crashpad + +/* end of file GETOPT.C */ diff --git a/shared/sentry/external/crashpad/third_party/getopt/getopt.h b/shared/sentry/external/crashpad/third_party/getopt/getopt.h new file mode 100644 index 000000000..27ab0dd4e --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/getopt/getopt.h @@ -0,0 +1,63 @@ +/* +Copyright (C) 1997 Gregory Pietsch + +[These files] are hereby placed in the public domain without restrictions. Just +give the author credit, don't claim you wrote it or prevent anyone else from +using it. +*/ + +#ifndef GETOPT_H +#define GETOPT_H + +/* include files needed by this include file */ + +/* macros defined by this include file */ +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +/* types defined by this include file */ + +namespace crashpad { + +/* GETOPT_LONG_OPTION_T: The type of long option */ +typedef struct GETOPT_LONG_OPTION_T +{ + const char *name; /* the name of the long option */ + int has_arg; /* one of the above macros */ + int *flag; /* determines if getopt_long() returns a + * value for a long option; if it is + * non-NULL, 0 is returned as a function + * value and the value of val is stored in + * the area pointed to by flag. Otherwise, + * val is returned. */ + int val; /* determines the value to return if flag is + * NULL. */ +} GETOPT_LONG_OPTION_T; + +typedef GETOPT_LONG_OPTION_T option; + +/* externally-defined variables */ +extern char *optarg; +extern int optind; +extern int opterr; +extern int optopt; + +/* function prototypes */ +int getopt(int argc, char** argv, char* optstring); +int getopt_long(int argc, + char** argv, + const char* shortopts, + const GETOPT_LONG_OPTION_T* longopts, + int* longind); +int getopt_long_only(int argc, + char** argv, + const char* shortopts, + const GETOPT_LONG_OPTION_T* longopts, + int* longind); + +} // namespace crashpad + +#endif /* GETOPT_H */ + +/* END OF FILE getopt.h */ diff --git a/shared/sentry/external/crashpad/third_party/glibc/BUILD.gn b/shared/sentry/external/crashpad/third_party/glibc/BUILD.gn new file mode 100644 index 000000000..3f2b08bc0 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/glibc/BUILD.gn @@ -0,0 +1,17 @@ +# Copyright 2019 The Crashpad Authors. All rights reserved. +# +# 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. + +source_set("glibc") { + sources = [ "elf/elf.h" ] +} diff --git a/shared/sentry/external/crashpad/third_party/glibc/COPYING.LIB b/shared/sentry/external/crashpad/third_party/glibc/COPYING.LIB new file mode 100644 index 000000000..4362b4915 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/glibc/COPYING.LIB @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! diff --git a/shared/sentry/external/crashpad/third_party/glibc/README.crashpad b/shared/sentry/external/crashpad/third_party/glibc/README.crashpad new file mode 100644 index 000000000..b550ad98a --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/glibc/README.crashpad @@ -0,0 +1,16 @@ +Name: GNU C Library +Short Name: glibc +URL: https://www.gnu.org/software/libc/ +URL: https://sourceware.org/git/?p=glibc.git +Version: 2.29 +License: GNU LGPL 2.1 +License File: COPYING.LIB +Security Critical: no + +Description: +glibc is the GNU Project’s implementation of the C standard library. + +Local Modifications: + - Only elf/elf.h is included. Its #include of has been removed, + and it uses of __BEGIN_DECLS and __END_DECLS have been replaced with inline + versions in the manner that misc/sys/cdefs.h defines those macros. diff --git a/shared/sentry/external/crashpad/third_party/glibc/elf/elf.h b/shared/sentry/external/crashpad/third_party/glibc/elf/elf.h new file mode 100644 index 000000000..331536b49 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/glibc/elf/elf.h @@ -0,0 +1,4003 @@ +/* This file defines standard ELF types, structures, and macros. + Copyright (C) 1995-2019 Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ + +#ifndef _ELF_H +#define _ELF_H 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* Standard ELF types. */ + +#include + +/* Type for a 16-bit quantity. */ +typedef uint16_t Elf32_Half; +typedef uint16_t Elf64_Half; + +/* Types for signed and unsigned 32-bit quantities. */ +typedef uint32_t Elf32_Word; +typedef int32_t Elf32_Sword; +typedef uint32_t Elf64_Word; +typedef int32_t Elf64_Sword; + +/* Types for signed and unsigned 64-bit quantities. */ +typedef uint64_t Elf32_Xword; +typedef int64_t Elf32_Sxword; +typedef uint64_t Elf64_Xword; +typedef int64_t Elf64_Sxword; + +/* Type of addresses. */ +typedef uint32_t Elf32_Addr; +typedef uint64_t Elf64_Addr; + +/* Type of file offsets. */ +typedef uint32_t Elf32_Off; +typedef uint64_t Elf64_Off; + +/* Type for section indices, which are 16-bit quantities. */ +typedef uint16_t Elf32_Section; +typedef uint16_t Elf64_Section; + +/* Type for version symbol information. */ +typedef Elf32_Half Elf32_Versym; +typedef Elf64_Half Elf64_Versym; + + +/* The ELF file header. This appears at the start of every ELF file. */ + +#define EI_NIDENT (16) + +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf32_Half e_type; /* Object file type */ + Elf32_Half e_machine; /* Architecture */ + Elf32_Word e_version; /* Object file version */ + Elf32_Addr e_entry; /* Entry point virtual address */ + Elf32_Off e_phoff; /* Program header table file offset */ + Elf32_Off e_shoff; /* Section header table file offset */ + Elf32_Word e_flags; /* Processor-specific flags */ + Elf32_Half e_ehsize; /* ELF header size in bytes */ + Elf32_Half e_phentsize; /* Program header table entry size */ + Elf32_Half e_phnum; /* Program header table entry count */ + Elf32_Half e_shentsize; /* Section header table entry size */ + Elf32_Half e_shnum; /* Section header table entry count */ + Elf32_Half e_shstrndx; /* Section header string table index */ +} Elf32_Ehdr; + +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf64_Half e_type; /* Object file type */ + Elf64_Half e_machine; /* Architecture */ + Elf64_Word e_version; /* Object file version */ + Elf64_Addr e_entry; /* Entry point virtual address */ + Elf64_Off e_phoff; /* Program header table file offset */ + Elf64_Off e_shoff; /* Section header table file offset */ + Elf64_Word e_flags; /* Processor-specific flags */ + Elf64_Half e_ehsize; /* ELF header size in bytes */ + Elf64_Half e_phentsize; /* Program header table entry size */ + Elf64_Half e_phnum; /* Program header table entry count */ + Elf64_Half e_shentsize; /* Section header table entry size */ + Elf64_Half e_shnum; /* Section header table entry count */ + Elf64_Half e_shstrndx; /* Section header string table index */ +} Elf64_Ehdr; + +/* Fields in the e_ident array. The EI_* macros are indices into the + array. The macros under each EI_* macro are the values the byte + may have. */ + +#define EI_MAG0 0 /* File identification byte 0 index */ +#define ELFMAG0 0x7f /* Magic number byte 0 */ + +#define EI_MAG1 1 /* File identification byte 1 index */ +#define ELFMAG1 'E' /* Magic number byte 1 */ + +#define EI_MAG2 2 /* File identification byte 2 index */ +#define ELFMAG2 'L' /* Magic number byte 2 */ + +#define EI_MAG3 3 /* File identification byte 3 index */ +#define ELFMAG3 'F' /* Magic number byte 3 */ + +/* Conglomeration of the identification bytes, for easy testing as a word. */ +#define ELFMAG "\177ELF" +#define SELFMAG 4 + +#define EI_CLASS 4 /* File class byte index */ +#define ELFCLASSNONE 0 /* Invalid class */ +#define ELFCLASS32 1 /* 32-bit objects */ +#define ELFCLASS64 2 /* 64-bit objects */ +#define ELFCLASSNUM 3 + +#define EI_DATA 5 /* Data encoding byte index */ +#define ELFDATANONE 0 /* Invalid data encoding */ +#define ELFDATA2LSB 1 /* 2's complement, little endian */ +#define ELFDATA2MSB 2 /* 2's complement, big endian */ +#define ELFDATANUM 3 + +#define EI_VERSION 6 /* File version byte index */ + /* Value must be EV_CURRENT */ + +#define EI_OSABI 7 /* OS ABI identification */ +#define ELFOSABI_NONE 0 /* UNIX System V ABI */ +#define ELFOSABI_SYSV 0 /* Alias. */ +#define ELFOSABI_HPUX 1 /* HP-UX */ +#define ELFOSABI_NETBSD 2 /* NetBSD. */ +#define ELFOSABI_GNU 3 /* Object uses GNU ELF extensions. */ +#define ELFOSABI_LINUX ELFOSABI_GNU /* Compatibility alias. */ +#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ +#define ELFOSABI_AIX 7 /* IBM AIX. */ +#define ELFOSABI_IRIX 8 /* SGI Irix. */ +#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ +#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ +#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ +#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ +#define ELFOSABI_ARM_AEABI 64 /* ARM EABI */ +#define ELFOSABI_ARM 97 /* ARM */ +#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ + +#define EI_ABIVERSION 8 /* ABI version */ + +#define EI_PAD 9 /* Byte index of padding bytes */ + +/* Legal values for e_type (object file type). */ + +#define ET_NONE 0 /* No file type */ +#define ET_REL 1 /* Relocatable file */ +#define ET_EXEC 2 /* Executable file */ +#define ET_DYN 3 /* Shared object file */ +#define ET_CORE 4 /* Core file */ +#define ET_NUM 5 /* Number of defined types */ +#define ET_LOOS 0xfe00 /* OS-specific range start */ +#define ET_HIOS 0xfeff /* OS-specific range end */ +#define ET_LOPROC 0xff00 /* Processor-specific range start */ +#define ET_HIPROC 0xffff /* Processor-specific range end */ + +/* Legal values for e_machine (architecture). */ + +#define EM_NONE 0 /* No machine */ +#define EM_M32 1 /* AT&T WE 32100 */ +#define EM_SPARC 2 /* SUN SPARC */ +#define EM_386 3 /* Intel 80386 */ +#define EM_68K 4 /* Motorola m68k family */ +#define EM_88K 5 /* Motorola m88k family */ +#define EM_IAMCU 6 /* Intel MCU */ +#define EM_860 7 /* Intel 80860 */ +#define EM_MIPS 8 /* MIPS R3000 big-endian */ +#define EM_S370 9 /* IBM System/370 */ +#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ + /* reserved 11-14 */ +#define EM_PARISC 15 /* HPPA */ + /* reserved 16 */ +#define EM_VPP500 17 /* Fujitsu VPP500 */ +#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ +#define EM_960 19 /* Intel 80960 */ +#define EM_PPC 20 /* PowerPC */ +#define EM_PPC64 21 /* PowerPC 64-bit */ +#define EM_S390 22 /* IBM S390 */ +#define EM_SPU 23 /* IBM SPU/SPC */ + /* reserved 24-35 */ +#define EM_V800 36 /* NEC V800 series */ +#define EM_FR20 37 /* Fujitsu FR20 */ +#define EM_RH32 38 /* TRW RH-32 */ +#define EM_RCE 39 /* Motorola RCE */ +#define EM_ARM 40 /* ARM */ +#define EM_FAKE_ALPHA 41 /* Digital Alpha */ +#define EM_SH 42 /* Hitachi SH */ +#define EM_SPARCV9 43 /* SPARC v9 64-bit */ +#define EM_TRICORE 44 /* Siemens Tricore */ +#define EM_ARC 45 /* Argonaut RISC Core */ +#define EM_H8_300 46 /* Hitachi H8/300 */ +#define EM_H8_300H 47 /* Hitachi H8/300H */ +#define EM_H8S 48 /* Hitachi H8S */ +#define EM_H8_500 49 /* Hitachi H8/500 */ +#define EM_IA_64 50 /* Intel Merced */ +#define EM_MIPS_X 51 /* Stanford MIPS-X */ +#define EM_COLDFIRE 52 /* Motorola Coldfire */ +#define EM_68HC12 53 /* Motorola M68HC12 */ +#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator */ +#define EM_PCP 55 /* Siemens PCP */ +#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ +#define EM_NDR1 57 /* Denso NDR1 microprocessor */ +#define EM_STARCORE 58 /* Motorola Start*Core processor */ +#define EM_ME16 59 /* Toyota ME16 processor */ +#define EM_ST100 60 /* STMicroelectronic ST100 processor */ +#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam */ +#define EM_X86_64 62 /* AMD x86-64 architecture */ +#define EM_PDSP 63 /* Sony DSP Processor */ +#define EM_PDP10 64 /* Digital PDP-10 */ +#define EM_PDP11 65 /* Digital PDP-11 */ +#define EM_FX66 66 /* Siemens FX66 microcontroller */ +#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ +#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ +#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ +#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ +#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ +#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ +#define EM_SVX 73 /* Silicon Graphics SVx */ +#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ +#define EM_VAX 75 /* Digital VAX */ +#define EM_CRIS 76 /* Axis Communications 32-bit emb.proc */ +#define EM_JAVELIN 77 /* Infineon Technologies 32-bit emb.proc */ +#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ +#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ +#define EM_MMIX 80 /* Donald Knuth's educational 64-bit proc */ +#define EM_HUANY 81 /* Harvard University machine-independent object files */ +#define EM_PRISM 82 /* SiTera Prism */ +#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ +#define EM_FR30 84 /* Fujitsu FR30 */ +#define EM_D10V 85 /* Mitsubishi D10V */ +#define EM_D30V 86 /* Mitsubishi D30V */ +#define EM_V850 87 /* NEC v850 */ +#define EM_M32R 88 /* Mitsubishi M32R */ +#define EM_MN10300 89 /* Matsushita MN10300 */ +#define EM_MN10200 90 /* Matsushita MN10200 */ +#define EM_PJ 91 /* picoJava */ +#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ +#define EM_ARC_COMPACT 93 /* ARC International ARCompact */ +#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ +#define EM_VIDEOCORE 95 /* Alphamosaic VideoCore */ +#define EM_TMM_GPP 96 /* Thompson Multimedia General Purpose Proc */ +#define EM_NS32K 97 /* National Semi. 32000 */ +#define EM_TPC 98 /* Tenor Network TPC */ +#define EM_SNP1K 99 /* Trebia SNP 1000 */ +#define EM_ST200 100 /* STMicroelectronics ST200 */ +#define EM_IP2K 101 /* Ubicom IP2xxx */ +#define EM_MAX 102 /* MAX processor */ +#define EM_CR 103 /* National Semi. CompactRISC */ +#define EM_F2MC16 104 /* Fujitsu F2MC16 */ +#define EM_MSP430 105 /* Texas Instruments msp430 */ +#define EM_BLACKFIN 106 /* Analog Devices Blackfin DSP */ +#define EM_SE_C33 107 /* Seiko Epson S1C33 family */ +#define EM_SEP 108 /* Sharp embedded microprocessor */ +#define EM_ARCA 109 /* Arca RISC */ +#define EM_UNICORE 110 /* PKU-Unity & MPRC Peking Uni. mc series */ +#define EM_EXCESS 111 /* eXcess configurable cpu */ +#define EM_DXP 112 /* Icera Semi. Deep Execution Processor */ +#define EM_ALTERA_NIOS2 113 /* Altera Nios II */ +#define EM_CRX 114 /* National Semi. CompactRISC CRX */ +#define EM_XGATE 115 /* Motorola XGATE */ +#define EM_C166 116 /* Infineon C16x/XC16x */ +#define EM_M16C 117 /* Renesas M16C */ +#define EM_DSPIC30F 118 /* Microchip Technology dsPIC30F */ +#define EM_CE 119 /* Freescale Communication Engine RISC */ +#define EM_M32C 120 /* Renesas M32C */ + /* reserved 121-130 */ +#define EM_TSK3000 131 /* Altium TSK3000 */ +#define EM_RS08 132 /* Freescale RS08 */ +#define EM_SHARC 133 /* Analog Devices SHARC family */ +#define EM_ECOG2 134 /* Cyan Technology eCOG2 */ +#define EM_SCORE7 135 /* Sunplus S+core7 RISC */ +#define EM_DSP24 136 /* New Japan Radio (NJR) 24-bit DSP */ +#define EM_VIDEOCORE3 137 /* Broadcom VideoCore III */ +#define EM_LATTICEMICO32 138 /* RISC for Lattice FPGA */ +#define EM_SE_C17 139 /* Seiko Epson C17 */ +#define EM_TI_C6000 140 /* Texas Instruments TMS320C6000 DSP */ +#define EM_TI_C2000 141 /* Texas Instruments TMS320C2000 DSP */ +#define EM_TI_C5500 142 /* Texas Instruments TMS320C55x DSP */ +#define EM_TI_ARP32 143 /* Texas Instruments App. Specific RISC */ +#define EM_TI_PRU 144 /* Texas Instruments Prog. Realtime Unit */ + /* reserved 145-159 */ +#define EM_MMDSP_PLUS 160 /* STMicroelectronics 64bit VLIW DSP */ +#define EM_CYPRESS_M8C 161 /* Cypress M8C */ +#define EM_R32C 162 /* Renesas R32C */ +#define EM_TRIMEDIA 163 /* NXP Semi. TriMedia */ +#define EM_QDSP6 164 /* QUALCOMM DSP6 */ +#define EM_8051 165 /* Intel 8051 and variants */ +#define EM_STXP7X 166 /* STMicroelectronics STxP7x */ +#define EM_NDS32 167 /* Andes Tech. compact code emb. RISC */ +#define EM_ECOG1X 168 /* Cyan Technology eCOG1X */ +#define EM_MAXQ30 169 /* Dallas Semi. MAXQ30 mc */ +#define EM_XIMO16 170 /* New Japan Radio (NJR) 16-bit DSP */ +#define EM_MANIK 171 /* M2000 Reconfigurable RISC */ +#define EM_CRAYNV2 172 /* Cray NV2 vector architecture */ +#define EM_RX 173 /* Renesas RX */ +#define EM_METAG 174 /* Imagination Tech. META */ +#define EM_MCST_ELBRUS 175 /* MCST Elbrus */ +#define EM_ECOG16 176 /* Cyan Technology eCOG16 */ +#define EM_CR16 177 /* National Semi. CompactRISC CR16 */ +#define EM_ETPU 178 /* Freescale Extended Time Processing Unit */ +#define EM_SLE9X 179 /* Infineon Tech. SLE9X */ +#define EM_L10M 180 /* Intel L10M */ +#define EM_K10M 181 /* Intel K10M */ + /* reserved 182 */ +#define EM_AARCH64 183 /* ARM AARCH64 */ + /* reserved 184 */ +#define EM_AVR32 185 /* Amtel 32-bit microprocessor */ +#define EM_STM8 186 /* STMicroelectronics STM8 */ +#define EM_TILE64 187 /* Tileta TILE64 */ +#define EM_TILEPRO 188 /* Tilera TILEPro */ +#define EM_MICROBLAZE 189 /* Xilinx MicroBlaze */ +#define EM_CUDA 190 /* NVIDIA CUDA */ +#define EM_TILEGX 191 /* Tilera TILE-Gx */ +#define EM_CLOUDSHIELD 192 /* CloudShield */ +#define EM_COREA_1ST 193 /* KIPO-KAIST Core-A 1st gen. */ +#define EM_COREA_2ND 194 /* KIPO-KAIST Core-A 2nd gen. */ +#define EM_ARC_COMPACT2 195 /* Synopsys ARCompact V2 */ +#define EM_OPEN8 196 /* Open8 RISC */ +#define EM_RL78 197 /* Renesas RL78 */ +#define EM_VIDEOCORE5 198 /* Broadcom VideoCore V */ +#define EM_78KOR 199 /* Renesas 78KOR */ +#define EM_56800EX 200 /* Freescale 56800EX DSC */ +#define EM_BA1 201 /* Beyond BA1 */ +#define EM_BA2 202 /* Beyond BA2 */ +#define EM_XCORE 203 /* XMOS xCORE */ +#define EM_MCHP_PIC 204 /* Microchip 8-bit PIC(r) */ + /* reserved 205-209 */ +#define EM_KM32 210 /* KM211 KM32 */ +#define EM_KMX32 211 /* KM211 KMX32 */ +#define EM_EMX16 212 /* KM211 KMX16 */ +#define EM_EMX8 213 /* KM211 KMX8 */ +#define EM_KVARC 214 /* KM211 KVARC */ +#define EM_CDP 215 /* Paneve CDP */ +#define EM_COGE 216 /* Cognitive Smart Memory Processor */ +#define EM_COOL 217 /* Bluechip CoolEngine */ +#define EM_NORC 218 /* Nanoradio Optimized RISC */ +#define EM_CSR_KALIMBA 219 /* CSR Kalimba */ +#define EM_Z80 220 /* Zilog Z80 */ +#define EM_VISIUM 221 /* Controls and Data Services VISIUMcore */ +#define EM_FT32 222 /* FTDI Chip FT32 */ +#define EM_MOXIE 223 /* Moxie processor */ +#define EM_AMDGPU 224 /* AMD GPU */ + /* reserved 225-242 */ +#define EM_RISCV 243 /* RISC-V */ + +#define EM_BPF 247 /* Linux BPF -- in-kernel virtual machine */ +#define EM_CSKY 252 /* C_SKY */ + +#define EM_NUM 253 + +/* Old spellings/synonyms. */ + +#define EM_ARC_A5 EM_ARC_COMPACT + +/* If it is necessary to assign new unofficial EM_* values, please + pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the + chances of collision with official or non-GNU unofficial values. */ + +#define EM_ALPHA 0x9026 + +/* Legal values for e_version (version). */ + +#define EV_NONE 0 /* Invalid ELF version */ +#define EV_CURRENT 1 /* Current version */ +#define EV_NUM 2 + +/* Section header. */ + +typedef struct +{ + Elf32_Word sh_name; /* Section name (string tbl index) */ + Elf32_Word sh_type; /* Section type */ + Elf32_Word sh_flags; /* Section flags */ + Elf32_Addr sh_addr; /* Section virtual addr at execution */ + Elf32_Off sh_offset; /* Section file offset */ + Elf32_Word sh_size; /* Section size in bytes */ + Elf32_Word sh_link; /* Link to another section */ + Elf32_Word sh_info; /* Additional section information */ + Elf32_Word sh_addralign; /* Section alignment */ + Elf32_Word sh_entsize; /* Entry size if section holds table */ +} Elf32_Shdr; + +typedef struct +{ + Elf64_Word sh_name; /* Section name (string tbl index) */ + Elf64_Word sh_type; /* Section type */ + Elf64_Xword sh_flags; /* Section flags */ + Elf64_Addr sh_addr; /* Section virtual addr at execution */ + Elf64_Off sh_offset; /* Section file offset */ + Elf64_Xword sh_size; /* Section size in bytes */ + Elf64_Word sh_link; /* Link to another section */ + Elf64_Word sh_info; /* Additional section information */ + Elf64_Xword sh_addralign; /* Section alignment */ + Elf64_Xword sh_entsize; /* Entry size if section holds table */ +} Elf64_Shdr; + +/* Special section indices. */ + +#define SHN_UNDEF 0 /* Undefined section */ +#define SHN_LORESERVE 0xff00 /* Start of reserved indices */ +#define SHN_LOPROC 0xff00 /* Start of processor-specific */ +#define SHN_BEFORE 0xff00 /* Order section before all others + (Solaris). */ +#define SHN_AFTER 0xff01 /* Order section after all others + (Solaris). */ +#define SHN_HIPROC 0xff1f /* End of processor-specific */ +#define SHN_LOOS 0xff20 /* Start of OS-specific */ +#define SHN_HIOS 0xff3f /* End of OS-specific */ +#define SHN_ABS 0xfff1 /* Associated symbol is absolute */ +#define SHN_COMMON 0xfff2 /* Associated symbol is common */ +#define SHN_XINDEX 0xffff /* Index is in extra table. */ +#define SHN_HIRESERVE 0xffff /* End of reserved indices */ + +/* Legal values for sh_type (section type). */ + +#define SHT_NULL 0 /* Section header table entry unused */ +#define SHT_PROGBITS 1 /* Program data */ +#define SHT_SYMTAB 2 /* Symbol table */ +#define SHT_STRTAB 3 /* String table */ +#define SHT_RELA 4 /* Relocation entries with addends */ +#define SHT_HASH 5 /* Symbol hash table */ +#define SHT_DYNAMIC 6 /* Dynamic linking information */ +#define SHT_NOTE 7 /* Notes */ +#define SHT_NOBITS 8 /* Program space with no data (bss) */ +#define SHT_REL 9 /* Relocation entries, no addends */ +#define SHT_SHLIB 10 /* Reserved */ +#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ +#define SHT_INIT_ARRAY 14 /* Array of constructors */ +#define SHT_FINI_ARRAY 15 /* Array of destructors */ +#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ +#define SHT_GROUP 17 /* Section group */ +#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ +#define SHT_NUM 19 /* Number of defined types. */ +#define SHT_LOOS 0x60000000 /* Start OS-specific. */ +#define SHT_GNU_ATTRIBUTES 0x6ffffff5 /* Object attributes. */ +#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */ +#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ +#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ +#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ +#define SHT_SUNW_move 0x6ffffffa +#define SHT_SUNW_COMDAT 0x6ffffffb +#define SHT_SUNW_syminfo 0x6ffffffc +#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ +#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ +#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ +#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ +#define SHT_HIOS 0x6fffffff /* End OS-specific type */ +#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ +#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ +#define SHT_LOUSER 0x80000000 /* Start of application-specific */ +#define SHT_HIUSER 0x8fffffff /* End of application-specific */ + +/* Legal values for sh_flags (section flags). */ + +#define SHF_WRITE (1 << 0) /* Writable */ +#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ +#define SHF_EXECINSTR (1 << 2) /* Executable */ +#define SHF_MERGE (1 << 4) /* Might be merged */ +#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ +#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */ +#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */ +#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling + required */ +#define SHF_GROUP (1 << 9) /* Section is member of a group. */ +#define SHF_TLS (1 << 10) /* Section hold thread-local data. */ +#define SHF_COMPRESSED (1 << 11) /* Section with compressed data. */ +#define SHF_MASKOS 0x0ff00000 /* OS-specific. */ +#define SHF_MASKPROC 0xf0000000 /* Processor-specific */ +#define SHF_ORDERED (1 << 30) /* Special ordering requirement + (Solaris). */ +#define SHF_EXCLUDE (1U << 31) /* Section is excluded unless + referenced or allocated (Solaris).*/ + +/* Section compression header. Used when SHF_COMPRESSED is set. */ + +typedef struct +{ + Elf32_Word ch_type; /* Compression format. */ + Elf32_Word ch_size; /* Uncompressed data size. */ + Elf32_Word ch_addralign; /* Uncompressed data alignment. */ +} Elf32_Chdr; + +typedef struct +{ + Elf64_Word ch_type; /* Compression format. */ + Elf64_Word ch_reserved; + Elf64_Xword ch_size; /* Uncompressed data size. */ + Elf64_Xword ch_addralign; /* Uncompressed data alignment. */ +} Elf64_Chdr; + +/* Legal values for ch_type (compression algorithm). */ +#define ELFCOMPRESS_ZLIB 1 /* ZLIB/DEFLATE algorithm. */ +#define ELFCOMPRESS_LOOS 0x60000000 /* Start of OS-specific. */ +#define ELFCOMPRESS_HIOS 0x6fffffff /* End of OS-specific. */ +#define ELFCOMPRESS_LOPROC 0x70000000 /* Start of processor-specific. */ +#define ELFCOMPRESS_HIPROC 0x7fffffff /* End of processor-specific. */ + +/* Section group handling. */ +#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */ + +/* Symbol table entry. */ + +typedef struct +{ + Elf32_Word st_name; /* Symbol name (string tbl index) */ + Elf32_Addr st_value; /* Symbol value */ + Elf32_Word st_size; /* Symbol size */ + unsigned char st_info; /* Symbol type and binding */ + unsigned char st_other; /* Symbol visibility */ + Elf32_Section st_shndx; /* Section index */ +} Elf32_Sym; + +typedef struct +{ + Elf64_Word st_name; /* Symbol name (string tbl index) */ + unsigned char st_info; /* Symbol type and binding */ + unsigned char st_other; /* Symbol visibility */ + Elf64_Section st_shndx; /* Section index */ + Elf64_Addr st_value; /* Symbol value */ + Elf64_Xword st_size; /* Symbol size */ +} Elf64_Sym; + +/* The syminfo section if available contains additional information about + every dynamic symbol. */ + +typedef struct +{ + Elf32_Half si_boundto; /* Direct bindings, symbol bound to */ + Elf32_Half si_flags; /* Per symbol flags */ +} Elf32_Syminfo; + +typedef struct +{ + Elf64_Half si_boundto; /* Direct bindings, symbol bound to */ + Elf64_Half si_flags; /* Per symbol flags */ +} Elf64_Syminfo; + +/* Possible values for si_boundto. */ +#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ +#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ +#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ + +/* Possible bitmasks for si_flags. */ +#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ +#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ +#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ +#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy + loaded */ +/* Syminfo version values. */ +#define SYMINFO_NONE 0 +#define SYMINFO_CURRENT 1 +#define SYMINFO_NUM 2 + + +/* How to extract and insert information held in the st_info field. */ + +#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) +#define ELF32_ST_TYPE(val) ((val) & 0xf) +#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) + +/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ +#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) +#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) +#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) + +/* Legal values for ST_BIND subfield of st_info (symbol binding). */ + +#define STB_LOCAL 0 /* Local symbol */ +#define STB_GLOBAL 1 /* Global symbol */ +#define STB_WEAK 2 /* Weak symbol */ +#define STB_NUM 3 /* Number of defined types. */ +#define STB_LOOS 10 /* Start of OS-specific */ +#define STB_GNU_UNIQUE 10 /* Unique symbol. */ +#define STB_HIOS 12 /* End of OS-specific */ +#define STB_LOPROC 13 /* Start of processor-specific */ +#define STB_HIPROC 15 /* End of processor-specific */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_NOTYPE 0 /* Symbol type is unspecified */ +#define STT_OBJECT 1 /* Symbol is a data object */ +#define STT_FUNC 2 /* Symbol is a code object */ +#define STT_SECTION 3 /* Symbol associated with a section */ +#define STT_FILE 4 /* Symbol's name is file name */ +#define STT_COMMON 5 /* Symbol is a common data object */ +#define STT_TLS 6 /* Symbol is thread-local data object*/ +#define STT_NUM 7 /* Number of defined types. */ +#define STT_LOOS 10 /* Start of OS-specific */ +#define STT_GNU_IFUNC 10 /* Symbol is indirect code object */ +#define STT_HIOS 12 /* End of OS-specific */ +#define STT_LOPROC 13 /* Start of processor-specific */ +#define STT_HIPROC 15 /* End of processor-specific */ + + +/* Symbol table indices are found in the hash buckets and chain table + of a symbol hash table section. This special index value indicates + the end of a chain, meaning no further symbols are found in that bucket. */ + +#define STN_UNDEF 0 /* End of a chain. */ + + +/* How to extract and insert information held in the st_other field. */ + +#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) + +/* For ELF64 the definitions are the same. */ +#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) + +/* Symbol visibility specification encoded in the st_other field. */ +#define STV_DEFAULT 0 /* Default symbol visibility rules */ +#define STV_INTERNAL 1 /* Processor specific hidden class */ +#define STV_HIDDEN 2 /* Sym unavailable in other modules */ +#define STV_PROTECTED 3 /* Not preemptible, not exported */ + + +/* Relocation table entry without addend (in section of type SHT_REL). */ + +typedef struct +{ + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ +} Elf32_Rel; + +/* I have seen two different definitions of the Elf64_Rel and + Elf64_Rela structures, so we'll leave them out until Novell (or + whoever) gets their act together. */ +/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ + +typedef struct +{ + Elf64_Addr r_offset; /* Address */ + Elf64_Xword r_info; /* Relocation type and symbol index */ +} Elf64_Rel; + +/* Relocation table entry with addend (in section of type SHT_RELA). */ + +typedef struct +{ + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ + Elf32_Sword r_addend; /* Addend */ +} Elf32_Rela; + +typedef struct +{ + Elf64_Addr r_offset; /* Address */ + Elf64_Xword r_info; /* Relocation type and symbol index */ + Elf64_Sxword r_addend; /* Addend */ +} Elf64_Rela; + +/* How to extract and insert information held in the r_info field. */ + +#define ELF32_R_SYM(val) ((val) >> 8) +#define ELF32_R_TYPE(val) ((val) & 0xff) +#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) + +#define ELF64_R_SYM(i) ((i) >> 32) +#define ELF64_R_TYPE(i) ((i) & 0xffffffff) +#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) + +/* Program segment header. */ + +typedef struct +{ + Elf32_Word p_type; /* Segment type */ + Elf32_Off p_offset; /* Segment file offset */ + Elf32_Addr p_vaddr; /* Segment virtual address */ + Elf32_Addr p_paddr; /* Segment physical address */ + Elf32_Word p_filesz; /* Segment size in file */ + Elf32_Word p_memsz; /* Segment size in memory */ + Elf32_Word p_flags; /* Segment flags */ + Elf32_Word p_align; /* Segment alignment */ +} Elf32_Phdr; + +typedef struct +{ + Elf64_Word p_type; /* Segment type */ + Elf64_Word p_flags; /* Segment flags */ + Elf64_Off p_offset; /* Segment file offset */ + Elf64_Addr p_vaddr; /* Segment virtual address */ + Elf64_Addr p_paddr; /* Segment physical address */ + Elf64_Xword p_filesz; /* Segment size in file */ + Elf64_Xword p_memsz; /* Segment size in memory */ + Elf64_Xword p_align; /* Segment alignment */ +} Elf64_Phdr; + +/* Special value for e_phnum. This indicates that the real number of + program headers is too large to fit into e_phnum. Instead the real + value is in the field sh_info of section 0. */ + +#define PN_XNUM 0xffff + +/* Legal values for p_type (segment type). */ + +#define PT_NULL 0 /* Program header table entry unused */ +#define PT_LOAD 1 /* Loadable program segment */ +#define PT_DYNAMIC 2 /* Dynamic linking information */ +#define PT_INTERP 3 /* Program interpreter */ +#define PT_NOTE 4 /* Auxiliary information */ +#define PT_SHLIB 5 /* Reserved */ +#define PT_PHDR 6 /* Entry for header table itself */ +#define PT_TLS 7 /* Thread-local storage segment */ +#define PT_NUM 8 /* Number of defined types */ +#define PT_LOOS 0x60000000 /* Start of OS-specific */ +#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ +#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ +#define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */ +#define PT_LOSUNW 0x6ffffffa +#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ +#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ +#define PT_HISUNW 0x6fffffff +#define PT_HIOS 0x6fffffff /* End of OS-specific */ +#define PT_LOPROC 0x70000000 /* Start of processor-specific */ +#define PT_HIPROC 0x7fffffff /* End of processor-specific */ + +/* Legal values for p_flags (segment flags). */ + +#define PF_X (1 << 0) /* Segment is executable */ +#define PF_W (1 << 1) /* Segment is writable */ +#define PF_R (1 << 2) /* Segment is readable */ +#define PF_MASKOS 0x0ff00000 /* OS-specific */ +#define PF_MASKPROC 0xf0000000 /* Processor-specific */ + +/* Legal values for note segment descriptor types for core files. */ + +#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ +#define NT_PRFPREG 2 /* Contains copy of fpregset + struct. */ +#define NT_FPREGSET 2 /* Contains copy of fpregset struct */ +#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ +#define NT_PRXREG 4 /* Contains copy of prxregset struct */ +#define NT_TASKSTRUCT 4 /* Contains copy of task structure */ +#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ +#define NT_AUXV 6 /* Contains copy of auxv array */ +#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ +#define NT_ASRS 8 /* Contains copy of asrset struct */ +#define NT_PSTATUS 10 /* Contains copy of pstatus struct */ +#define NT_PSINFO 13 /* Contains copy of psinfo struct */ +#define NT_PRCRED 14 /* Contains copy of prcred struct */ +#define NT_UTSNAME 15 /* Contains copy of utsname struct */ +#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ +#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ +#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct */ +#define NT_SIGINFO 0x53494749 /* Contains copy of siginfo_t, + size might increase */ +#define NT_FILE 0x46494c45 /* Contains information about mapped + files */ +#define NT_PRXFPREG 0x46e62b7f /* Contains copy of user_fxsr_struct */ +#define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */ +#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */ +#define NT_PPC_VSX 0x102 /* PowerPC VSX registers */ +#define NT_PPC_TAR 0x103 /* Target Address Register */ +#define NT_PPC_PPR 0x104 /* Program Priority Register */ +#define NT_PPC_DSCR 0x105 /* Data Stream Control Register */ +#define NT_PPC_EBB 0x106 /* Event Based Branch Registers */ +#define NT_PPC_PMU 0x107 /* Performance Monitor Registers */ +#define NT_PPC_TM_CGPR 0x108 /* TM checkpointed GPR Registers */ +#define NT_PPC_TM_CFPR 0x109 /* TM checkpointed FPR Registers */ +#define NT_PPC_TM_CVMX 0x10a /* TM checkpointed VMX Registers */ +#define NT_PPC_TM_CVSX 0x10b /* TM checkpointed VSX Registers */ +#define NT_PPC_TM_SPR 0x10c /* TM Special Purpose Registers */ +#define NT_PPC_TM_CTAR 0x10d /* TM checkpointed Target Address + Register */ +#define NT_PPC_TM_CPPR 0x10e /* TM checkpointed Program Priority + Register */ +#define NT_PPC_TM_CDSCR 0x10f /* TM checkpointed Data Stream Control + Register */ +#define NT_PPC_PKEY 0x110 /* Memory Protection Keys + registers. */ +#define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */ +#define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */ +#define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */ +#define NT_S390_HIGH_GPRS 0x300 /* s390 upper register halves */ +#define NT_S390_TIMER 0x301 /* s390 timer register */ +#define NT_S390_TODCMP 0x302 /* s390 TOD clock comparator register */ +#define NT_S390_TODPREG 0x303 /* s390 TOD programmable register */ +#define NT_S390_CTRS 0x304 /* s390 control registers */ +#define NT_S390_PREFIX 0x305 /* s390 prefix register */ +#define NT_S390_LAST_BREAK 0x306 /* s390 breaking event address */ +#define NT_S390_SYSTEM_CALL 0x307 /* s390 system call restart data */ +#define NT_S390_TDB 0x308 /* s390 transaction diagnostic block */ +#define NT_S390_VXRS_LOW 0x309 /* s390 vector registers 0-15 + upper half. */ +#define NT_S390_VXRS_HIGH 0x30a /* s390 vector registers 16-31. */ +#define NT_S390_GS_CB 0x30b /* s390 guarded storage registers. */ +#define NT_S390_GS_BC 0x30c /* s390 guarded storage + broadcast control block. */ +#define NT_S390_RI_CB 0x30d /* s390 runtime instrumentation. */ +#define NT_ARM_VFP 0x400 /* ARM VFP/NEON registers */ +#define NT_ARM_TLS 0x401 /* ARM TLS register */ +#define NT_ARM_HW_BREAK 0x402 /* ARM hardware breakpoint registers */ +#define NT_ARM_HW_WATCH 0x403 /* ARM hardware watchpoint registers */ +#define NT_ARM_SYSTEM_CALL 0x404 /* ARM system call number */ +#define NT_ARM_SVE 0x405 /* ARM Scalable Vector Extension + registers */ +#define NT_VMCOREDD 0x700 /* Vmcore Device Dump Note. */ +#define NT_MIPS_DSP 0x800 /* MIPS DSP ASE registers. */ +#define NT_MIPS_FP_MODE 0x801 /* MIPS floating-point mode. */ + +/* Legal values for the note segment descriptor types for object files. */ + +#define NT_VERSION 1 /* Contains a version string. */ + + +/* Dynamic section entry. */ + +typedef struct +{ + Elf32_Sword d_tag; /* Dynamic entry type */ + union + { + Elf32_Word d_val; /* Integer value */ + Elf32_Addr d_ptr; /* Address value */ + } d_un; +} Elf32_Dyn; + +typedef struct +{ + Elf64_Sxword d_tag; /* Dynamic entry type */ + union + { + Elf64_Xword d_val; /* Integer value */ + Elf64_Addr d_ptr; /* Address value */ + } d_un; +} Elf64_Dyn; + +/* Legal values for d_tag (dynamic entry type). */ + +#define DT_NULL 0 /* Marks end of dynamic section */ +#define DT_NEEDED 1 /* Name of needed library */ +#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ +#define DT_PLTGOT 3 /* Processor defined value */ +#define DT_HASH 4 /* Address of symbol hash table */ +#define DT_STRTAB 5 /* Address of string table */ +#define DT_SYMTAB 6 /* Address of symbol table */ +#define DT_RELA 7 /* Address of Rela relocs */ +#define DT_RELASZ 8 /* Total size of Rela relocs */ +#define DT_RELAENT 9 /* Size of one Rela reloc */ +#define DT_STRSZ 10 /* Size of string table */ +#define DT_SYMENT 11 /* Size of one symbol table entry */ +#define DT_INIT 12 /* Address of init function */ +#define DT_FINI 13 /* Address of termination function */ +#define DT_SONAME 14 /* Name of shared object */ +#define DT_RPATH 15 /* Library search path (deprecated) */ +#define DT_SYMBOLIC 16 /* Start symbol search here */ +#define DT_REL 17 /* Address of Rel relocs */ +#define DT_RELSZ 18 /* Total size of Rel relocs */ +#define DT_RELENT 19 /* Size of one Rel reloc */ +#define DT_PLTREL 20 /* Type of reloc in PLT */ +#define DT_DEBUG 21 /* For debugging; unspecified */ +#define DT_TEXTREL 22 /* Reloc might modify .text */ +#define DT_JMPREL 23 /* Address of PLT relocs */ +#define DT_BIND_NOW 24 /* Process relocations of object */ +#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ +#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ +#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ +#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ +#define DT_RUNPATH 29 /* Library search path */ +#define DT_FLAGS 30 /* Flags for the object being loaded */ +#define DT_ENCODING 32 /* Start of encoded range */ +#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ +#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ +#define DT_SYMTAB_SHNDX 34 /* Address of SYMTAB_SHNDX section */ +#define DT_NUM 35 /* Number used */ +#define DT_LOOS 0x6000000d /* Start of OS-specific */ +#define DT_HIOS 0x6ffff000 /* End of OS-specific */ +#define DT_LOPROC 0x70000000 /* Start of processor-specific */ +#define DT_HIPROC 0x7fffffff /* End of processor-specific */ +#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */ + +/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the + Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's + approach. */ +#define DT_VALRNGLO 0x6ffffd00 +#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */ +#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */ +#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */ +#define DT_CHECKSUM 0x6ffffdf8 +#define DT_PLTPADSZ 0x6ffffdf9 +#define DT_MOVEENT 0x6ffffdfa +#define DT_MOVESZ 0x6ffffdfb +#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */ +#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting + the following DT_* entry. */ +#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ +#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ +#define DT_VALRNGHI 0x6ffffdff +#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */ +#define DT_VALNUM 12 + +/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the + Dyn.d_un.d_ptr field of the Elf*_Dyn structure. + + If any adjustment is made to the ELF object after it has been + built these entries will need to be adjusted. */ +#define DT_ADDRRNGLO 0x6ffffe00 +#define DT_GNU_HASH 0x6ffffef5 /* GNU-style hash table. */ +#define DT_TLSDESC_PLT 0x6ffffef6 +#define DT_TLSDESC_GOT 0x6ffffef7 +#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */ +#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */ +#define DT_CONFIG 0x6ffffefa /* Configuration information. */ +#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */ +#define DT_AUDIT 0x6ffffefc /* Object auditing. */ +#define DT_PLTPAD 0x6ffffefd /* PLT padding. */ +#define DT_MOVETAB 0x6ffffefe /* Move table. */ +#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ +#define DT_ADDRRNGHI 0x6ffffeff +#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ +#define DT_ADDRNUM 11 + +/* The versioning entry types. The next are defined as part of the + GNU extension. */ +#define DT_VERSYM 0x6ffffff0 + +#define DT_RELACOUNT 0x6ffffff9 +#define DT_RELCOUNT 0x6ffffffa + +/* These were chosen by Sun. */ +#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */ +#define DT_VERDEF 0x6ffffffc /* Address of version definition + table */ +#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */ +#define DT_VERNEED 0x6ffffffe /* Address of table with needed + versions */ +#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */ +#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ +#define DT_VERSIONTAGNUM 16 + +/* Sun added these machine-independent extensions in the "processor-specific" + range. Be compatible. */ +#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */ +#define DT_FILTER 0x7fffffff /* Shared object to get values from */ +#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) +#define DT_EXTRANUM 3 + +/* Values of `d_un.d_val' in the DT_FLAGS entry. */ +#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */ +#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */ +#define DF_TEXTREL 0x00000004 /* Object contains text relocations */ +#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */ +#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */ + +/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 + entry in the dynamic section. */ +#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ +#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ +#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */ +#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ +#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/ +#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/ +#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */ +#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */ +#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */ +#define DF_1_TRANS 0x00000200 +#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */ +#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */ +#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */ +#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/ +#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */ +#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */ +#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */ +#define DF_1_NODIRECT 0x00020000 /* Object has no-direct binding. */ +#define DF_1_IGNMULDEF 0x00040000 +#define DF_1_NOKSYMS 0x00080000 +#define DF_1_NOHDR 0x00100000 +#define DF_1_EDITED 0x00200000 /* Object is modified after built. */ +#define DF_1_NORELOC 0x00400000 +#define DF_1_SYMINTPOSE 0x00800000 /* Object has individual interposers. */ +#define DF_1_GLOBAUDIT 0x01000000 /* Global auditing required. */ +#define DF_1_SINGLETON 0x02000000 /* Singleton symbols are used. */ +#define DF_1_STUB 0x04000000 +#define DF_1_PIE 0x08000000 + +/* Flags for the feature selection in DT_FEATURE_1. */ +#define DTF_1_PARINIT 0x00000001 +#define DTF_1_CONFEXP 0x00000002 + +/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */ +#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */ +#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not + generally available. */ + +/* Version definition sections. */ + +typedef struct +{ + Elf32_Half vd_version; /* Version revision */ + Elf32_Half vd_flags; /* Version information */ + Elf32_Half vd_ndx; /* Version Index */ + Elf32_Half vd_cnt; /* Number of associated aux entries */ + Elf32_Word vd_hash; /* Version name hash value */ + Elf32_Word vd_aux; /* Offset in bytes to verdaux array */ + Elf32_Word vd_next; /* Offset in bytes to next verdef + entry */ +} Elf32_Verdef; + +typedef struct +{ + Elf64_Half vd_version; /* Version revision */ + Elf64_Half vd_flags; /* Version information */ + Elf64_Half vd_ndx; /* Version Index */ + Elf64_Half vd_cnt; /* Number of associated aux entries */ + Elf64_Word vd_hash; /* Version name hash value */ + Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ + Elf64_Word vd_next; /* Offset in bytes to next verdef + entry */ +} Elf64_Verdef; + + +/* Legal values for vd_version (version revision). */ +#define VER_DEF_NONE 0 /* No version */ +#define VER_DEF_CURRENT 1 /* Current version */ +#define VER_DEF_NUM 2 /* Given version number */ + +/* Legal values for vd_flags (version information flags). */ +#define VER_FLG_BASE 0x1 /* Version definition of file itself */ +#define VER_FLG_WEAK 0x2 /* Weak version identifier */ + +/* Versym symbol index values. */ +#define VER_NDX_LOCAL 0 /* Symbol is local. */ +#define VER_NDX_GLOBAL 1 /* Symbol is global. */ +#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */ +#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */ + +/* Auxialiary version information. */ + +typedef struct +{ + Elf32_Word vda_name; /* Version or dependency names */ + Elf32_Word vda_next; /* Offset in bytes to next verdaux + entry */ +} Elf32_Verdaux; + +typedef struct +{ + Elf64_Word vda_name; /* Version or dependency names */ + Elf64_Word vda_next; /* Offset in bytes to next verdaux + entry */ +} Elf64_Verdaux; + + +/* Version dependency section. */ + +typedef struct +{ + Elf32_Half vn_version; /* Version of structure */ + Elf32_Half vn_cnt; /* Number of associated aux entries */ + Elf32_Word vn_file; /* Offset of filename for this + dependency */ + Elf32_Word vn_aux; /* Offset in bytes to vernaux array */ + Elf32_Word vn_next; /* Offset in bytes to next verneed + entry */ +} Elf32_Verneed; + +typedef struct +{ + Elf64_Half vn_version; /* Version of structure */ + Elf64_Half vn_cnt; /* Number of associated aux entries */ + Elf64_Word vn_file; /* Offset of filename for this + dependency */ + Elf64_Word vn_aux; /* Offset in bytes to vernaux array */ + Elf64_Word vn_next; /* Offset in bytes to next verneed + entry */ +} Elf64_Verneed; + + +/* Legal values for vn_version (version revision). */ +#define VER_NEED_NONE 0 /* No version */ +#define VER_NEED_CURRENT 1 /* Current version */ +#define VER_NEED_NUM 2 /* Given version number */ + +/* Auxiliary needed version information. */ + +typedef struct +{ + Elf32_Word vna_hash; /* Hash value of dependency name */ + Elf32_Half vna_flags; /* Dependency specific information */ + Elf32_Half vna_other; /* Unused */ + Elf32_Word vna_name; /* Dependency name string offset */ + Elf32_Word vna_next; /* Offset in bytes to next vernaux + entry */ +} Elf32_Vernaux; + +typedef struct +{ + Elf64_Word vna_hash; /* Hash value of dependency name */ + Elf64_Half vna_flags; /* Dependency specific information */ + Elf64_Half vna_other; /* Unused */ + Elf64_Word vna_name; /* Dependency name string offset */ + Elf64_Word vna_next; /* Offset in bytes to next vernaux + entry */ +} Elf64_Vernaux; + + +/* Legal values for vna_flags. */ +#define VER_FLG_WEAK 0x2 /* Weak version identifier */ + + +/* Auxiliary vector. */ + +/* This vector is normally only used by the program interpreter. The + usual definition in an ABI supplement uses the name auxv_t. The + vector is not usually defined in a standard file, but it + can't hurt. We rename it to avoid conflicts. The sizes of these + types are an arrangement between the exec server and the program + interpreter, so we don't fully specify them here. */ + +typedef struct +{ + uint32_t a_type; /* Entry type */ + union + { + uint32_t a_val; /* Integer value */ + /* We use to have pointer elements added here. We cannot do that, + though, since it does not work when using 32-bit definitions + on 64-bit platforms and vice versa. */ + } a_un; +} Elf32_auxv_t; + +typedef struct +{ + uint64_t a_type; /* Entry type */ + union + { + uint64_t a_val; /* Integer value */ + /* We use to have pointer elements added here. We cannot do that, + though, since it does not work when using 32-bit definitions + on 64-bit platforms and vice versa. */ + } a_un; +} Elf64_auxv_t; + +/* Legal values for a_type (entry type). */ + +#define AT_NULL 0 /* End of vector */ +#define AT_IGNORE 1 /* Entry should be ignored */ +#define AT_EXECFD 2 /* File descriptor of program */ +#define AT_PHDR 3 /* Program headers for program */ +#define AT_PHENT 4 /* Size of program header entry */ +#define AT_PHNUM 5 /* Number of program headers */ +#define AT_PAGESZ 6 /* System page size */ +#define AT_BASE 7 /* Base address of interpreter */ +#define AT_FLAGS 8 /* Flags */ +#define AT_ENTRY 9 /* Entry point of program */ +#define AT_NOTELF 10 /* Program is not ELF */ +#define AT_UID 11 /* Real uid */ +#define AT_EUID 12 /* Effective uid */ +#define AT_GID 13 /* Real gid */ +#define AT_EGID 14 /* Effective gid */ +#define AT_CLKTCK 17 /* Frequency of times() */ + +/* Some more special a_type values describing the hardware. */ +#define AT_PLATFORM 15 /* String identifying platform. */ +#define AT_HWCAP 16 /* Machine-dependent hints about + processor capabilities. */ + +/* This entry gives some information about the FPU initialization + performed by the kernel. */ +#define AT_FPUCW 18 /* Used FPU control word. */ + +/* Cache block sizes. */ +#define AT_DCACHEBSIZE 19 /* Data cache block size. */ +#define AT_ICACHEBSIZE 20 /* Instruction cache block size. */ +#define AT_UCACHEBSIZE 21 /* Unified cache block size. */ + +/* A special ignored value for PPC, used by the kernel to control the + interpretation of the AUXV. Must be > 16. */ +#define AT_IGNOREPPC 22 /* Entry should be ignored. */ + +#define AT_SECURE 23 /* Boolean, was exec setuid-like? */ + +#define AT_BASE_PLATFORM 24 /* String identifying real platforms.*/ + +#define AT_RANDOM 25 /* Address of 16 random bytes. */ + +#define AT_HWCAP2 26 /* More machine-dependent hints about + processor capabilities. */ + +#define AT_EXECFN 31 /* Filename of executable. */ + +/* Pointer to the global system page used for system calls and other + nice things. */ +#define AT_SYSINFO 32 +#define AT_SYSINFO_EHDR 33 + +/* Shapes of the caches. Bits 0-3 contains associativity; bits 4-7 contains + log2 of line size; mask those to get cache size. */ +#define AT_L1I_CACHESHAPE 34 +#define AT_L1D_CACHESHAPE 35 +#define AT_L2_CACHESHAPE 36 +#define AT_L3_CACHESHAPE 37 + +/* Shapes of the caches, with more room to describe them. + *GEOMETRY are comprised of cache line size in bytes in the bottom 16 bits + and the cache associativity in the next 16 bits. */ +#define AT_L1I_CACHESIZE 40 +#define AT_L1I_CACHEGEOMETRY 41 +#define AT_L1D_CACHESIZE 42 +#define AT_L1D_CACHEGEOMETRY 43 +#define AT_L2_CACHESIZE 44 +#define AT_L2_CACHEGEOMETRY 45 +#define AT_L3_CACHESIZE 46 +#define AT_L3_CACHEGEOMETRY 47 + +#define AT_MINSIGSTKSZ 51 /* Stack needed for signal delivery + (AArch64). */ + +/* Note section contents. Each entry in the note section begins with + a header of a fixed form. */ + +typedef struct +{ + Elf32_Word n_namesz; /* Length of the note's name. */ + Elf32_Word n_descsz; /* Length of the note's descriptor. */ + Elf32_Word n_type; /* Type of the note. */ +} Elf32_Nhdr; + +typedef struct +{ + Elf64_Word n_namesz; /* Length of the note's name. */ + Elf64_Word n_descsz; /* Length of the note's descriptor. */ + Elf64_Word n_type; /* Type of the note. */ +} Elf64_Nhdr; + +/* Known names of notes. */ + +/* Solaris entries in the note section have this name. */ +#define ELF_NOTE_SOLARIS "SUNW Solaris" + +/* Note entries for GNU systems have this name. */ +#define ELF_NOTE_GNU "GNU" + + +/* Defined types of notes for Solaris. */ + +/* Value of descriptor (one word) is desired pagesize for the binary. */ +#define ELF_NOTE_PAGESIZE_HINT 1 + + +/* Defined note types for GNU systems. */ + +/* ABI information. The descriptor consists of words: + word 0: OS descriptor + word 1: major version of the ABI + word 2: minor version of the ABI + word 3: subminor version of the ABI +*/ +#define NT_GNU_ABI_TAG 1 +#define ELF_NOTE_ABI NT_GNU_ABI_TAG /* Old name. */ + +/* Known OSes. These values can appear in word 0 of an + NT_GNU_ABI_TAG note section entry. */ +#define ELF_NOTE_OS_LINUX 0 +#define ELF_NOTE_OS_GNU 1 +#define ELF_NOTE_OS_SOLARIS2 2 +#define ELF_NOTE_OS_FREEBSD 3 + +/* Synthetic hwcap information. The descriptor begins with two words: + word 0: number of entries + word 1: bitmask of enabled entries + Then follow variable-length entries, one byte followed by a + '\0'-terminated hwcap name string. The byte gives the bit + number to test if enabled, (1U << bit) & bitmask. */ +#define NT_GNU_HWCAP 2 + +/* Build ID bits as generated by ld --build-id. + The descriptor consists of any nonzero number of bytes. */ +#define NT_GNU_BUILD_ID 3 + +/* Version note generated by GNU gold containing a version string. */ +#define NT_GNU_GOLD_VERSION 4 + +/* Program property. */ +#define NT_GNU_PROPERTY_TYPE_0 5 + +/* Note section name of program property. */ +#define NOTE_GNU_PROPERTY_SECTION_NAME ".note.gnu.property" + +/* Values used in GNU .note.gnu.property notes (NT_GNU_PROPERTY_TYPE_0). */ + +/* Stack size. */ +#define GNU_PROPERTY_STACK_SIZE 1 +/* No copy relocation on protected data symbol. */ +#define GNU_PROPERTY_NO_COPY_ON_PROTECTED 2 + +/* Processor-specific semantics, lo */ +#define GNU_PROPERTY_LOPROC 0xc0000000 +/* Processor-specific semantics, hi */ +#define GNU_PROPERTY_HIPROC 0xdfffffff +/* Application-specific semantics, lo */ +#define GNU_PROPERTY_LOUSER 0xe0000000 +/* Application-specific semantics, hi */ +#define GNU_PROPERTY_HIUSER 0xffffffff + +/* The x86 instruction sets indicated by the corresponding bits are + used in program. Their support in the hardware is optional. */ +#define GNU_PROPERTY_X86_ISA_1_USED 0xc0000000 +/* The x86 instruction sets indicated by the corresponding bits are + used in program and they must be supported by the hardware. */ +#define GNU_PROPERTY_X86_ISA_1_NEEDED 0xc0000001 +/* X86 processor-specific features used in program. */ +#define GNU_PROPERTY_X86_FEATURE_1_AND 0xc0000002 + +#define GNU_PROPERTY_X86_ISA_1_486 (1U << 0) +#define GNU_PROPERTY_X86_ISA_1_586 (1U << 1) +#define GNU_PROPERTY_X86_ISA_1_686 (1U << 2) +#define GNU_PROPERTY_X86_ISA_1_SSE (1U << 3) +#define GNU_PROPERTY_X86_ISA_1_SSE2 (1U << 4) +#define GNU_PROPERTY_X86_ISA_1_SSE3 (1U << 5) +#define GNU_PROPERTY_X86_ISA_1_SSSE3 (1U << 6) +#define GNU_PROPERTY_X86_ISA_1_SSE4_1 (1U << 7) +#define GNU_PROPERTY_X86_ISA_1_SSE4_2 (1U << 8) +#define GNU_PROPERTY_X86_ISA_1_AVX (1U << 9) +#define GNU_PROPERTY_X86_ISA_1_AVX2 (1U << 10) +#define GNU_PROPERTY_X86_ISA_1_AVX512F (1U << 11) +#define GNU_PROPERTY_X86_ISA_1_AVX512CD (1U << 12) +#define GNU_PROPERTY_X86_ISA_1_AVX512ER (1U << 13) +#define GNU_PROPERTY_X86_ISA_1_AVX512PF (1U << 14) +#define GNU_PROPERTY_X86_ISA_1_AVX512VL (1U << 15) +#define GNU_PROPERTY_X86_ISA_1_AVX512DQ (1U << 16) +#define GNU_PROPERTY_X86_ISA_1_AVX512BW (1U << 17) + +/* This indicates that all executable sections are compatible with + IBT. */ +#define GNU_PROPERTY_X86_FEATURE_1_IBT (1U << 0) +/* This indicates that all executable sections are compatible with + SHSTK. */ +#define GNU_PROPERTY_X86_FEATURE_1_SHSTK (1U << 1) + +/* Move records. */ +typedef struct +{ + Elf32_Xword m_value; /* Symbol value. */ + Elf32_Word m_info; /* Size and index. */ + Elf32_Word m_poffset; /* Symbol offset. */ + Elf32_Half m_repeat; /* Repeat count. */ + Elf32_Half m_stride; /* Stride info. */ +} Elf32_Move; + +typedef struct +{ + Elf64_Xword m_value; /* Symbol value. */ + Elf64_Xword m_info; /* Size and index. */ + Elf64_Xword m_poffset; /* Symbol offset. */ + Elf64_Half m_repeat; /* Repeat count. */ + Elf64_Half m_stride; /* Stride info. */ +} Elf64_Move; + +/* Macro to construct move records. */ +#define ELF32_M_SYM(info) ((info) >> 8) +#define ELF32_M_SIZE(info) ((unsigned char) (info)) +#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) + +#define ELF64_M_SYM(info) ELF32_M_SYM (info) +#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) +#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) + + +/* Motorola 68k specific definitions. */ + +/* Values for Elf32_Ehdr.e_flags. */ +#define EF_CPU32 0x00810000 + +/* m68k relocs. */ + +#define R_68K_NONE 0 /* No reloc */ +#define R_68K_32 1 /* Direct 32 bit */ +#define R_68K_16 2 /* Direct 16 bit */ +#define R_68K_8 3 /* Direct 8 bit */ +#define R_68K_PC32 4 /* PC relative 32 bit */ +#define R_68K_PC16 5 /* PC relative 16 bit */ +#define R_68K_PC8 6 /* PC relative 8 bit */ +#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */ +#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */ +#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */ +#define R_68K_GOT32O 10 /* 32 bit GOT offset */ +#define R_68K_GOT16O 11 /* 16 bit GOT offset */ +#define R_68K_GOT8O 12 /* 8 bit GOT offset */ +#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */ +#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */ +#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */ +#define R_68K_PLT32O 16 /* 32 bit PLT offset */ +#define R_68K_PLT16O 17 /* 16 bit PLT offset */ +#define R_68K_PLT8O 18 /* 8 bit PLT offset */ +#define R_68K_COPY 19 /* Copy symbol at runtime */ +#define R_68K_GLOB_DAT 20 /* Create GOT entry */ +#define R_68K_JMP_SLOT 21 /* Create PLT entry */ +#define R_68K_RELATIVE 22 /* Adjust by program base */ +#define R_68K_TLS_GD32 25 /* 32 bit GOT offset for GD */ +#define R_68K_TLS_GD16 26 /* 16 bit GOT offset for GD */ +#define R_68K_TLS_GD8 27 /* 8 bit GOT offset for GD */ +#define R_68K_TLS_LDM32 28 /* 32 bit GOT offset for LDM */ +#define R_68K_TLS_LDM16 29 /* 16 bit GOT offset for LDM */ +#define R_68K_TLS_LDM8 30 /* 8 bit GOT offset for LDM */ +#define R_68K_TLS_LDO32 31 /* 32 bit module-relative offset */ +#define R_68K_TLS_LDO16 32 /* 16 bit module-relative offset */ +#define R_68K_TLS_LDO8 33 /* 8 bit module-relative offset */ +#define R_68K_TLS_IE32 34 /* 32 bit GOT offset for IE */ +#define R_68K_TLS_IE16 35 /* 16 bit GOT offset for IE */ +#define R_68K_TLS_IE8 36 /* 8 bit GOT offset for IE */ +#define R_68K_TLS_LE32 37 /* 32 bit offset relative to + static TLS block */ +#define R_68K_TLS_LE16 38 /* 16 bit offset relative to + static TLS block */ +#define R_68K_TLS_LE8 39 /* 8 bit offset relative to + static TLS block */ +#define R_68K_TLS_DTPMOD32 40 /* 32 bit module number */ +#define R_68K_TLS_DTPREL32 41 /* 32 bit module-relative offset */ +#define R_68K_TLS_TPREL32 42 /* 32 bit TP-relative offset */ +/* Keep this the last entry. */ +#define R_68K_NUM 43 + +/* Intel 80386 specific definitions. */ + +/* i386 relocs. */ + +#define R_386_NONE 0 /* No reloc */ +#define R_386_32 1 /* Direct 32 bit */ +#define R_386_PC32 2 /* PC relative 32 bit */ +#define R_386_GOT32 3 /* 32 bit GOT entry */ +#define R_386_PLT32 4 /* 32 bit PLT address */ +#define R_386_COPY 5 /* Copy symbol at runtime */ +#define R_386_GLOB_DAT 6 /* Create GOT entry */ +#define R_386_JMP_SLOT 7 /* Create PLT entry */ +#define R_386_RELATIVE 8 /* Adjust by program base */ +#define R_386_GOTOFF 9 /* 32 bit offset to GOT */ +#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */ +#define R_386_32PLT 11 +#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */ +#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS + block offset */ +#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block + offset */ +#define R_386_TLS_LE 17 /* Offset relative to static TLS + block */ +#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of + general dynamic thread local data */ +#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of + local dynamic thread local data + in LE code */ +#define R_386_16 20 +#define R_386_PC16 21 +#define R_386_8 22 +#define R_386_PC8 23 +#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic + thread local data */ +#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */ +#define R_386_TLS_GD_CALL 26 /* Relocation for call to + __tls_get_addr() */ +#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */ +#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic + thread local data in LE code */ +#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */ +#define R_386_TLS_LDM_CALL 30 /* Relocation for call to + __tls_get_addr() in LDM code */ +#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */ +#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */ +#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS + block offset */ +#define R_386_TLS_LE_32 34 /* Negated offset relative to static + TLS block */ +#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */ +#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */ +#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */ +#define R_386_SIZE32 38 /* 32-bit symbol size */ +#define R_386_TLS_GOTDESC 39 /* GOT offset for TLS descriptor. */ +#define R_386_TLS_DESC_CALL 40 /* Marker of call through TLS + descriptor for + relaxation. */ +#define R_386_TLS_DESC 41 /* TLS descriptor containing + pointer to code and to + argument, returning the TLS + offset for the symbol. */ +#define R_386_IRELATIVE 42 /* Adjust indirectly by program base */ +#define R_386_GOT32X 43 /* Load from 32 bit GOT entry, + relaxable. */ +/* Keep this the last entry. */ +#define R_386_NUM 44 + +/* SUN SPARC specific definitions. */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_SPARC_REGISTER 13 /* Global register reserved to app. */ + +/* Values for Elf64_Ehdr.e_flags. */ + +#define EF_SPARCV9_MM 3 +#define EF_SPARCV9_TSO 0 +#define EF_SPARCV9_PSO 1 +#define EF_SPARCV9_RMO 2 +#define EF_SPARC_LEDATA 0x800000 /* little endian data */ +#define EF_SPARC_EXT_MASK 0xFFFF00 +#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */ +#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */ +#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */ +#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */ + +/* SPARC relocs. */ + +#define R_SPARC_NONE 0 /* No reloc */ +#define R_SPARC_8 1 /* Direct 8 bit */ +#define R_SPARC_16 2 /* Direct 16 bit */ +#define R_SPARC_32 3 /* Direct 32 bit */ +#define R_SPARC_DISP8 4 /* PC relative 8 bit */ +#define R_SPARC_DISP16 5 /* PC relative 16 bit */ +#define R_SPARC_DISP32 6 /* PC relative 32 bit */ +#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */ +#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */ +#define R_SPARC_HI22 9 /* High 22 bit */ +#define R_SPARC_22 10 /* Direct 22 bit */ +#define R_SPARC_13 11 /* Direct 13 bit */ +#define R_SPARC_LO10 12 /* Truncated 10 bit */ +#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */ +#define R_SPARC_GOT13 14 /* 13 bit GOT entry */ +#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */ +#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */ +#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */ +#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */ +#define R_SPARC_COPY 19 /* Copy symbol at runtime */ +#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */ +#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */ +#define R_SPARC_RELATIVE 22 /* Adjust by program base */ +#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */ + +/* Additional Sparc64 relocs. */ + +#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */ +#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */ +#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */ +#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */ +#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */ +#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */ +#define R_SPARC_10 30 /* Direct 10 bit */ +#define R_SPARC_11 31 /* Direct 11 bit */ +#define R_SPARC_64 32 /* Direct 64 bit */ +#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */ +#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */ +#define R_SPARC_HM10 35 /* High middle 10 bits of ... */ +#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */ +#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */ +#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */ +#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */ +#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */ +#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */ +#define R_SPARC_GLOB_JMP 42 /* was part of v9 ABI but was removed */ +#define R_SPARC_7 43 /* Direct 7 bit */ +#define R_SPARC_5 44 /* Direct 5 bit */ +#define R_SPARC_6 45 /* Direct 6 bit */ +#define R_SPARC_DISP64 46 /* PC relative 64 bit */ +#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */ +#define R_SPARC_HIX22 48 /* High 22 bit complemented */ +#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */ +#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */ +#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */ +#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */ +#define R_SPARC_REGISTER 53 /* Global register usage */ +#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */ +#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */ +#define R_SPARC_TLS_GD_HI22 56 +#define R_SPARC_TLS_GD_LO10 57 +#define R_SPARC_TLS_GD_ADD 58 +#define R_SPARC_TLS_GD_CALL 59 +#define R_SPARC_TLS_LDM_HI22 60 +#define R_SPARC_TLS_LDM_LO10 61 +#define R_SPARC_TLS_LDM_ADD 62 +#define R_SPARC_TLS_LDM_CALL 63 +#define R_SPARC_TLS_LDO_HIX22 64 +#define R_SPARC_TLS_LDO_LOX10 65 +#define R_SPARC_TLS_LDO_ADD 66 +#define R_SPARC_TLS_IE_HI22 67 +#define R_SPARC_TLS_IE_LO10 68 +#define R_SPARC_TLS_IE_LD 69 +#define R_SPARC_TLS_IE_LDX 70 +#define R_SPARC_TLS_IE_ADD 71 +#define R_SPARC_TLS_LE_HIX22 72 +#define R_SPARC_TLS_LE_LOX10 73 +#define R_SPARC_TLS_DTPMOD32 74 +#define R_SPARC_TLS_DTPMOD64 75 +#define R_SPARC_TLS_DTPOFF32 76 +#define R_SPARC_TLS_DTPOFF64 77 +#define R_SPARC_TLS_TPOFF32 78 +#define R_SPARC_TLS_TPOFF64 79 +#define R_SPARC_GOTDATA_HIX22 80 +#define R_SPARC_GOTDATA_LOX10 81 +#define R_SPARC_GOTDATA_OP_HIX22 82 +#define R_SPARC_GOTDATA_OP_LOX10 83 +#define R_SPARC_GOTDATA_OP 84 +#define R_SPARC_H34 85 +#define R_SPARC_SIZE32 86 +#define R_SPARC_SIZE64 87 +#define R_SPARC_WDISP10 88 +#define R_SPARC_JMP_IREL 248 +#define R_SPARC_IRELATIVE 249 +#define R_SPARC_GNU_VTINHERIT 250 +#define R_SPARC_GNU_VTENTRY 251 +#define R_SPARC_REV32 252 +/* Keep this the last entry. */ +#define R_SPARC_NUM 253 + +/* For Sparc64, legal values for d_tag of Elf64_Dyn. */ + +#define DT_SPARC_REGISTER 0x70000001 +#define DT_SPARC_NUM 2 + +/* MIPS R3000 specific definitions. */ + +/* Legal values for e_flags field of Elf32_Ehdr. */ + +#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used. */ +#define EF_MIPS_PIC 2 /* Contains PIC code. */ +#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence. */ +#define EF_MIPS_XGOT 8 +#define EF_MIPS_64BIT_WHIRL 16 +#define EF_MIPS_ABI2 32 +#define EF_MIPS_ABI_ON32 64 +#define EF_MIPS_FP64 512 /* Uses FP64 (12 callee-saved). */ +#define EF_MIPS_NAN2008 1024 /* Uses IEEE 754-2008 NaN encoding. */ +#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level. */ + +/* Legal values for MIPS architecture level. */ + +#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ +#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ +#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ +#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ +#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ +#define EF_MIPS_ARCH_32 0x50000000 /* MIPS32 code. */ +#define EF_MIPS_ARCH_64 0x60000000 /* MIPS64 code. */ +#define EF_MIPS_ARCH_32R2 0x70000000 /* MIPS32r2 code. */ +#define EF_MIPS_ARCH_64R2 0x80000000 /* MIPS64r2 code. */ + +/* The following are unofficial names and should not be used. */ + +#define E_MIPS_ARCH_1 EF_MIPS_ARCH_1 +#define E_MIPS_ARCH_2 EF_MIPS_ARCH_2 +#define E_MIPS_ARCH_3 EF_MIPS_ARCH_3 +#define E_MIPS_ARCH_4 EF_MIPS_ARCH_4 +#define E_MIPS_ARCH_5 EF_MIPS_ARCH_5 +#define E_MIPS_ARCH_32 EF_MIPS_ARCH_32 +#define E_MIPS_ARCH_64 EF_MIPS_ARCH_64 + +/* Special section indices. */ + +#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols. */ +#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */ +#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */ +#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols. */ +#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols. */ + +/* Legal values for sh_type field of Elf32_Shdr. */ + +#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link. */ +#define SHT_MIPS_MSYM 0x70000001 +#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols. */ +#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes. */ +#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */ +#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging info. */ +#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information. */ +#define SHT_MIPS_PACKAGE 0x70000007 +#define SHT_MIPS_PACKSYM 0x70000008 +#define SHT_MIPS_RELD 0x70000009 +#define SHT_MIPS_IFACE 0x7000000b +#define SHT_MIPS_CONTENT 0x7000000c +#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */ +#define SHT_MIPS_SHDR 0x70000010 +#define SHT_MIPS_FDESC 0x70000011 +#define SHT_MIPS_EXTSYM 0x70000012 +#define SHT_MIPS_DENSE 0x70000013 +#define SHT_MIPS_PDESC 0x70000014 +#define SHT_MIPS_LOCSYM 0x70000015 +#define SHT_MIPS_AUXSYM 0x70000016 +#define SHT_MIPS_OPTSYM 0x70000017 +#define SHT_MIPS_LOCSTR 0x70000018 +#define SHT_MIPS_LINE 0x70000019 +#define SHT_MIPS_RFDESC 0x7000001a +#define SHT_MIPS_DELTASYM 0x7000001b +#define SHT_MIPS_DELTAINST 0x7000001c +#define SHT_MIPS_DELTACLASS 0x7000001d +#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */ +#define SHT_MIPS_DELTADECL 0x7000001f +#define SHT_MIPS_SYMBOL_LIB 0x70000020 +#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */ +#define SHT_MIPS_TRANSLATE 0x70000022 +#define SHT_MIPS_PIXIE 0x70000023 +#define SHT_MIPS_XLATE 0x70000024 +#define SHT_MIPS_XLATE_DEBUG 0x70000025 +#define SHT_MIPS_WHIRL 0x70000026 +#define SHT_MIPS_EH_REGION 0x70000027 +#define SHT_MIPS_XLATE_OLD 0x70000028 +#define SHT_MIPS_PDR_EXCEPTION 0x70000029 + +/* Legal values for sh_flags field of Elf32_Shdr. */ + +#define SHF_MIPS_GPREL 0x10000000 /* Must be in global data area. */ +#define SHF_MIPS_MERGE 0x20000000 +#define SHF_MIPS_ADDR 0x40000000 +#define SHF_MIPS_STRINGS 0x80000000 +#define SHF_MIPS_NOSTRIP 0x08000000 +#define SHF_MIPS_LOCAL 0x04000000 +#define SHF_MIPS_NAMES 0x02000000 +#define SHF_MIPS_NODUPE 0x01000000 + + +/* Symbol tables. */ + +/* MIPS specific values for `st_other'. */ +#define STO_MIPS_DEFAULT 0x0 +#define STO_MIPS_INTERNAL 0x1 +#define STO_MIPS_HIDDEN 0x2 +#define STO_MIPS_PROTECTED 0x3 +#define STO_MIPS_PLT 0x8 +#define STO_MIPS_SC_ALIGN_UNUSED 0xff + +/* MIPS specific values for `st_info'. */ +#define STB_MIPS_SPLIT_COMMON 13 + +/* Entries found in sections of type SHT_MIPS_GPTAB. */ + +typedef union +{ + struct + { + Elf32_Word gt_current_g_value; /* -G value used for compilation. */ + Elf32_Word gt_unused; /* Not used. */ + } gt_header; /* First entry in section. */ + struct + { + Elf32_Word gt_g_value; /* If this value were used for -G. */ + Elf32_Word gt_bytes; /* This many bytes would be used. */ + } gt_entry; /* Subsequent entries in section. */ +} Elf32_gptab; + +/* Entry found in sections of type SHT_MIPS_REGINFO. */ + +typedef struct +{ + Elf32_Word ri_gprmask; /* General registers used. */ + Elf32_Word ri_cprmask[4]; /* Coprocessor registers used. */ + Elf32_Sword ri_gp_value; /* $gp register value. */ +} Elf32_RegInfo; + +/* Entries found in sections of type SHT_MIPS_OPTIONS. */ + +typedef struct +{ + unsigned char kind; /* Determines interpretation of the + variable part of descriptor. */ + unsigned char size; /* Size of descriptor, including header. */ + Elf32_Section section; /* Section header index of section affected, + 0 for global options. */ + Elf32_Word info; /* Kind-specific information. */ +} Elf_Options; + +/* Values for `kind' field in Elf_Options. */ + +#define ODK_NULL 0 /* Undefined. */ +#define ODK_REGINFO 1 /* Register usage information. */ +#define ODK_EXCEPTIONS 2 /* Exception processing options. */ +#define ODK_PAD 3 /* Section padding options. */ +#define ODK_HWPATCH 4 /* Hardware workarounds performed */ +#define ODK_FILL 5 /* record the fill value used by the linker. */ +#define ODK_TAGS 6 /* reserve space for desktop tools to write. */ +#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */ +#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */ + +/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */ + +#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */ +#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */ +#define OEX_PAGE0 0x10000 /* page zero must be mapped. */ +#define OEX_SMM 0x20000 /* Force sequential memory mode? */ +#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */ +#define OEX_PRECISEFP OEX_FPDBUG +#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */ + +#define OEX_FPU_INVAL 0x10 +#define OEX_FPU_DIV0 0x08 +#define OEX_FPU_OFLO 0x04 +#define OEX_FPU_UFLO 0x02 +#define OEX_FPU_INEX 0x01 + +/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */ + +#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */ +#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */ +#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */ +#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */ + +#define OPAD_PREFIX 0x1 +#define OPAD_POSTFIX 0x2 +#define OPAD_SYMBOL 0x4 + +/* Entry found in `.options' section. */ + +typedef struct +{ + Elf32_Word hwp_flags1; /* Extra flags. */ + Elf32_Word hwp_flags2; /* Extra flags. */ +} Elf_Options_Hw; + +/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */ + +#define OHWA0_R4KEOP_CHECKED 0x00000001 +#define OHWA1_R4KEOP_CLEAN 0x00000002 + +/* MIPS relocs. */ + +#define R_MIPS_NONE 0 /* No reloc */ +#define R_MIPS_16 1 /* Direct 16 bit */ +#define R_MIPS_32 2 /* Direct 32 bit */ +#define R_MIPS_REL32 3 /* PC relative 32 bit */ +#define R_MIPS_26 4 /* Direct 26 bit shifted */ +#define R_MIPS_HI16 5 /* High 16 bit */ +#define R_MIPS_LO16 6 /* Low 16 bit */ +#define R_MIPS_GPREL16 7 /* GP relative 16 bit */ +#define R_MIPS_LITERAL 8 /* 16 bit literal entry */ +#define R_MIPS_GOT16 9 /* 16 bit GOT entry */ +#define R_MIPS_PC16 10 /* PC relative 16 bit */ +#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ +#define R_MIPS_GPREL32 12 /* GP relative 32 bit */ + +#define R_MIPS_SHIFT5 16 +#define R_MIPS_SHIFT6 17 +#define R_MIPS_64 18 +#define R_MIPS_GOT_DISP 19 +#define R_MIPS_GOT_PAGE 20 +#define R_MIPS_GOT_OFST 21 +#define R_MIPS_GOT_HI16 22 +#define R_MIPS_GOT_LO16 23 +#define R_MIPS_SUB 24 +#define R_MIPS_INSERT_A 25 +#define R_MIPS_INSERT_B 26 +#define R_MIPS_DELETE 27 +#define R_MIPS_HIGHER 28 +#define R_MIPS_HIGHEST 29 +#define R_MIPS_CALL_HI16 30 +#define R_MIPS_CALL_LO16 31 +#define R_MIPS_SCN_DISP 32 +#define R_MIPS_REL16 33 +#define R_MIPS_ADD_IMMEDIATE 34 +#define R_MIPS_PJUMP 35 +#define R_MIPS_RELGOT 36 +#define R_MIPS_JALR 37 +#define R_MIPS_TLS_DTPMOD32 38 /* Module number 32 bit */ +#define R_MIPS_TLS_DTPREL32 39 /* Module-relative offset 32 bit */ +#define R_MIPS_TLS_DTPMOD64 40 /* Module number 64 bit */ +#define R_MIPS_TLS_DTPREL64 41 /* Module-relative offset 64 bit */ +#define R_MIPS_TLS_GD 42 /* 16 bit GOT offset for GD */ +#define R_MIPS_TLS_LDM 43 /* 16 bit GOT offset for LDM */ +#define R_MIPS_TLS_DTPREL_HI16 44 /* Module-relative offset, high 16 bits */ +#define R_MIPS_TLS_DTPREL_LO16 45 /* Module-relative offset, low 16 bits */ +#define R_MIPS_TLS_GOTTPREL 46 /* 16 bit GOT offset for IE */ +#define R_MIPS_TLS_TPREL32 47 /* TP-relative offset, 32 bit */ +#define R_MIPS_TLS_TPREL64 48 /* TP-relative offset, 64 bit */ +#define R_MIPS_TLS_TPREL_HI16 49 /* TP-relative offset, high 16 bits */ +#define R_MIPS_TLS_TPREL_LO16 50 /* TP-relative offset, low 16 bits */ +#define R_MIPS_GLOB_DAT 51 +#define R_MIPS_COPY 126 +#define R_MIPS_JUMP_SLOT 127 +/* Keep this the last entry. */ +#define R_MIPS_NUM 128 + +/* Legal values for p_type field of Elf32_Phdr. */ + +#define PT_MIPS_REGINFO 0x70000000 /* Register usage information. */ +#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */ +#define PT_MIPS_OPTIONS 0x70000002 +#define PT_MIPS_ABIFLAGS 0x70000003 /* FP mode requirement. */ + +/* Special program header types. */ + +#define PF_MIPS_LOCAL 0x10000000 + +/* Legal values for d_tag field of Elf32_Dyn. */ + +#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */ +#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */ +#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */ +#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */ +#define DT_MIPS_FLAGS 0x70000005 /* Flags */ +#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */ +#define DT_MIPS_MSYM 0x70000007 +#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */ +#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */ +#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */ +#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */ +#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */ +#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */ +#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */ +#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */ +#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */ +#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */ +#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */ +#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in + DT_MIPS_DELTA_CLASS. */ +#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */ +#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in + DT_MIPS_DELTA_INSTANCE. */ +#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */ +#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in + DT_MIPS_DELTA_RELOC. */ +#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta + relocations refer to. */ +#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in + DT_MIPS_DELTA_SYM. */ +#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the + class declaration. */ +#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in + DT_MIPS_DELTA_CLASSSYM. */ +#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */ +#define DT_MIPS_PIXIE_INIT 0x70000023 +#define DT_MIPS_SYMBOL_LIB 0x70000024 +#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 +#define DT_MIPS_LOCAL_GOTIDX 0x70000026 +#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 +#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 +#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */ +#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */ +#define DT_MIPS_DYNSTR_ALIGN 0x7000002b +#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */ +#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve + function stored in GOT. */ +#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added + by rld on dlopen() calls. */ +#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */ +#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */ +#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */ +/* The address of .got.plt in an executable using the new non-PIC ABI. */ +#define DT_MIPS_PLTGOT 0x70000032 +/* The base of the PLT in an executable using the new non-PIC ABI if that + PLT is writable. For a non-writable PLT, this is omitted or has a zero + value. */ +#define DT_MIPS_RWPLT 0x70000034 +/* An alternative description of the classic MIPS RLD_MAP that is usable + in a PIE as it stores a relative offset from the address of the tag + rather than an absolute address. */ +#define DT_MIPS_RLD_MAP_REL 0x70000035 +#define DT_MIPS_NUM 0x36 + +/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ + +#define RHF_NONE 0 /* No flags */ +#define RHF_QUICKSTART (1 << 0) /* Use quickstart */ +#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */ +#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */ +#define RHF_NO_MOVE (1 << 3) +#define RHF_SGI_ONLY (1 << 4) +#define RHF_GUARANTEE_INIT (1 << 5) +#define RHF_DELTA_C_PLUS_PLUS (1 << 6) +#define RHF_GUARANTEE_START_INIT (1 << 7) +#define RHF_PIXIE (1 << 8) +#define RHF_DEFAULT_DELAY_LOAD (1 << 9) +#define RHF_REQUICKSTART (1 << 10) +#define RHF_REQUICKSTARTED (1 << 11) +#define RHF_CORD (1 << 12) +#define RHF_NO_UNRES_UNDEF (1 << 13) +#define RHF_RLD_ORDER_SAFE (1 << 14) + +/* Entries found in sections of type SHT_MIPS_LIBLIST. */ + +typedef struct +{ + Elf32_Word l_name; /* Name (string table index) */ + Elf32_Word l_time_stamp; /* Timestamp */ + Elf32_Word l_checksum; /* Checksum */ + Elf32_Word l_version; /* Interface version */ + Elf32_Word l_flags; /* Flags */ +} Elf32_Lib; + +typedef struct +{ + Elf64_Word l_name; /* Name (string table index) */ + Elf64_Word l_time_stamp; /* Timestamp */ + Elf64_Word l_checksum; /* Checksum */ + Elf64_Word l_version; /* Interface version */ + Elf64_Word l_flags; /* Flags */ +} Elf64_Lib; + + +/* Legal values for l_flags. */ + +#define LL_NONE 0 +#define LL_EXACT_MATCH (1 << 0) /* Require exact match */ +#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */ +#define LL_REQUIRE_MINOR (1 << 2) +#define LL_EXPORTS (1 << 3) +#define LL_DELAY_LOAD (1 << 4) +#define LL_DELTA (1 << 5) + +/* Entries found in sections of type SHT_MIPS_CONFLICT. */ + +typedef Elf32_Addr Elf32_Conflict; + +typedef struct +{ + /* Version of flags structure. */ + Elf32_Half version; + /* The level of the ISA: 1-5, 32, 64. */ + unsigned char isa_level; + /* The revision of ISA: 0 for MIPS V and below, 1-n otherwise. */ + unsigned char isa_rev; + /* The size of general purpose registers. */ + unsigned char gpr_size; + /* The size of co-processor 1 registers. */ + unsigned char cpr1_size; + /* The size of co-processor 2 registers. */ + unsigned char cpr2_size; + /* The floating-point ABI. */ + unsigned char fp_abi; + /* Processor-specific extension. */ + Elf32_Word isa_ext; + /* Mask of ASEs used. */ + Elf32_Word ases; + /* Mask of general flags. */ + Elf32_Word flags1; + Elf32_Word flags2; +} Elf_MIPS_ABIFlags_v0; + +/* Values for the register size bytes of an abi flags structure. */ + +#define MIPS_AFL_REG_NONE 0x00 /* No registers. */ +#define MIPS_AFL_REG_32 0x01 /* 32-bit registers. */ +#define MIPS_AFL_REG_64 0x02 /* 64-bit registers. */ +#define MIPS_AFL_REG_128 0x03 /* 128-bit registers. */ + +/* Masks for the ases word of an ABI flags structure. */ + +#define MIPS_AFL_ASE_DSP 0x00000001 /* DSP ASE. */ +#define MIPS_AFL_ASE_DSPR2 0x00000002 /* DSP R2 ASE. */ +#define MIPS_AFL_ASE_EVA 0x00000004 /* Enhanced VA Scheme. */ +#define MIPS_AFL_ASE_MCU 0x00000008 /* MCU (MicroController) ASE. */ +#define MIPS_AFL_ASE_MDMX 0x00000010 /* MDMX ASE. */ +#define MIPS_AFL_ASE_MIPS3D 0x00000020 /* MIPS-3D ASE. */ +#define MIPS_AFL_ASE_MT 0x00000040 /* MT ASE. */ +#define MIPS_AFL_ASE_SMARTMIPS 0x00000080 /* SmartMIPS ASE. */ +#define MIPS_AFL_ASE_VIRT 0x00000100 /* VZ ASE. */ +#define MIPS_AFL_ASE_MSA 0x00000200 /* MSA ASE. */ +#define MIPS_AFL_ASE_MIPS16 0x00000400 /* MIPS16 ASE. */ +#define MIPS_AFL_ASE_MICROMIPS 0x00000800 /* MICROMIPS ASE. */ +#define MIPS_AFL_ASE_XPA 0x00001000 /* XPA ASE. */ +#define MIPS_AFL_ASE_MASK 0x00001fff /* All ASEs. */ + +/* Values for the isa_ext word of an ABI flags structure. */ + +#define MIPS_AFL_EXT_XLR 1 /* RMI Xlr instruction. */ +#define MIPS_AFL_EXT_OCTEON2 2 /* Cavium Networks Octeon2. */ +#define MIPS_AFL_EXT_OCTEONP 3 /* Cavium Networks OcteonP. */ +#define MIPS_AFL_EXT_LOONGSON_3A 4 /* Loongson 3A. */ +#define MIPS_AFL_EXT_OCTEON 5 /* Cavium Networks Octeon. */ +#define MIPS_AFL_EXT_5900 6 /* MIPS R5900 instruction. */ +#define MIPS_AFL_EXT_4650 7 /* MIPS R4650 instruction. */ +#define MIPS_AFL_EXT_4010 8 /* LSI R4010 instruction. */ +#define MIPS_AFL_EXT_4100 9 /* NEC VR4100 instruction. */ +#define MIPS_AFL_EXT_3900 10 /* Toshiba R3900 instruction. */ +#define MIPS_AFL_EXT_10000 11 /* MIPS R10000 instruction. */ +#define MIPS_AFL_EXT_SB1 12 /* Broadcom SB-1 instruction. */ +#define MIPS_AFL_EXT_4111 13 /* NEC VR4111/VR4181 instruction. */ +#define MIPS_AFL_EXT_4120 14 /* NEC VR4120 instruction. */ +#define MIPS_AFL_EXT_5400 15 /* NEC VR5400 instruction. */ +#define MIPS_AFL_EXT_5500 16 /* NEC VR5500 instruction. */ +#define MIPS_AFL_EXT_LOONGSON_2E 17 /* ST Microelectronics Loongson 2E. */ +#define MIPS_AFL_EXT_LOONGSON_2F 18 /* ST Microelectronics Loongson 2F. */ + +/* Masks for the flags1 word of an ABI flags structure. */ +#define MIPS_AFL_FLAGS1_ODDSPREG 1 /* Uses odd single-precision registers. */ + +/* Object attribute values. */ +enum +{ + /* Not tagged or not using any ABIs affected by the differences. */ + Val_GNU_MIPS_ABI_FP_ANY = 0, + /* Using hard-float -mdouble-float. */ + Val_GNU_MIPS_ABI_FP_DOUBLE = 1, + /* Using hard-float -msingle-float. */ + Val_GNU_MIPS_ABI_FP_SINGLE = 2, + /* Using soft-float. */ + Val_GNU_MIPS_ABI_FP_SOFT = 3, + /* Using -mips32r2 -mfp64. */ + Val_GNU_MIPS_ABI_FP_OLD_64 = 4, + /* Using -mfpxx. */ + Val_GNU_MIPS_ABI_FP_XX = 5, + /* Using -mips32r2 -mfp64. */ + Val_GNU_MIPS_ABI_FP_64 = 6, + /* Using -mips32r2 -mfp64 -mno-odd-spreg. */ + Val_GNU_MIPS_ABI_FP_64A = 7, + /* Maximum allocated FP ABI value. */ + Val_GNU_MIPS_ABI_FP_MAX = 7 +}; + +/* HPPA specific definitions. */ + +/* Legal values for e_flags field of Elf32_Ehdr. */ + +#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ +#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ +#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ +#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ +#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch + prediction. */ +#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ +#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ + +/* Defined values for `e_flags & EF_PARISC_ARCH' are: */ + +#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ +#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ +#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ + +/* Additional section indeces. */ + +#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared + symbols in ANSI C. */ +#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ + +/* Legal values for sh_type field of Elf32_Shdr. */ + +#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ +#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ +#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ + +/* Legal values for sh_flags field of Elf32_Shdr. */ + +#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ +#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ +#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ + +#define STT_HP_OPAQUE (STT_LOOS + 0x1) +#define STT_HP_STUB (STT_LOOS + 0x2) + +/* HPPA relocs. */ + +#define R_PARISC_NONE 0 /* No reloc. */ +#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ +#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ +#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ +#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */ +#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ +#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ +#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ +#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ +#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ +#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ +#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ +#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ +#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ +#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ +#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ +#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ +#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ +#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ +#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ +#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ +#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ +#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ +#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ +#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ +#define R_PARISC_FPTR64 64 /* 64 bits function address. */ +#define R_PARISC_PLABEL32 65 /* 32 bits function address. */ +#define R_PARISC_PLABEL21L 66 /* Left 21 bits of fdesc address. */ +#define R_PARISC_PLABEL14R 70 /* Right 14 bits of fdesc address. */ +#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ +#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ +#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ +#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ +#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ +#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ +#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ +#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ +#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ +#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ +#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ +#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ +#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ +#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ +#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ +#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ +#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ +#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ +#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ +#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ +#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ +#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ +#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ +#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ +#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ +#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ +#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ +#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ +#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ +#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ +#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ +#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ +#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LORESERVE 128 +#define R_PARISC_COPY 128 /* Copy relocation. */ +#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ +#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ +#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ +#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ +#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ +#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ +#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ +#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ +#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ +#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ +#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_GNU_VTENTRY 232 +#define R_PARISC_GNU_VTINHERIT 233 +#define R_PARISC_TLS_GD21L 234 /* GD 21-bit left. */ +#define R_PARISC_TLS_GD14R 235 /* GD 14-bit right. */ +#define R_PARISC_TLS_GDCALL 236 /* GD call to __t_g_a. */ +#define R_PARISC_TLS_LDM21L 237 /* LD module 21-bit left. */ +#define R_PARISC_TLS_LDM14R 238 /* LD module 14-bit right. */ +#define R_PARISC_TLS_LDMCALL 239 /* LD module call to __t_g_a. */ +#define R_PARISC_TLS_LDO21L 240 /* LD offset 21-bit left. */ +#define R_PARISC_TLS_LDO14R 241 /* LD offset 14-bit right. */ +#define R_PARISC_TLS_DTPMOD32 242 /* DTP module 32-bit. */ +#define R_PARISC_TLS_DTPMOD64 243 /* DTP module 64-bit. */ +#define R_PARISC_TLS_DTPOFF32 244 /* DTP offset 32-bit. */ +#define R_PARISC_TLS_DTPOFF64 245 /* DTP offset 32-bit. */ +#define R_PARISC_TLS_LE21L R_PARISC_TPREL21L +#define R_PARISC_TLS_LE14R R_PARISC_TPREL14R +#define R_PARISC_TLS_IE21L R_PARISC_LTOFF_TP21L +#define R_PARISC_TLS_IE14R R_PARISC_LTOFF_TP14R +#define R_PARISC_TLS_TPREL32 R_PARISC_TPREL32 +#define R_PARISC_TLS_TPREL64 R_PARISC_TPREL64 +#define R_PARISC_HIRESERVE 255 + +/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ + +#define PT_HP_TLS (PT_LOOS + 0x0) +#define PT_HP_CORE_NONE (PT_LOOS + 0x1) +#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) +#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) +#define PT_HP_CORE_COMM (PT_LOOS + 0x4) +#define PT_HP_CORE_PROC (PT_LOOS + 0x5) +#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) +#define PT_HP_CORE_STACK (PT_LOOS + 0x7) +#define PT_HP_CORE_SHM (PT_LOOS + 0x8) +#define PT_HP_CORE_MMF (PT_LOOS + 0x9) +#define PT_HP_PARALLEL (PT_LOOS + 0x10) +#define PT_HP_FASTBIND (PT_LOOS + 0x11) +#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) +#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) +#define PT_HP_STACK (PT_LOOS + 0x14) + +#define PT_PARISC_ARCHEXT 0x70000000 +#define PT_PARISC_UNWIND 0x70000001 + +/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ + +#define PF_PARISC_SBP 0x08000000 + +#define PF_HP_PAGE_SIZE 0x00100000 +#define PF_HP_FAR_SHARED 0x00200000 +#define PF_HP_NEAR_SHARED 0x00400000 +#define PF_HP_CODE 0x01000000 +#define PF_HP_MODIFY 0x02000000 +#define PF_HP_LAZYSWAP 0x04000000 +#define PF_HP_SBP 0x08000000 + + +/* Alpha specific definitions. */ + +/* Legal values for e_flags field of Elf64_Ehdr. */ + +#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */ +#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */ + +/* Legal values for sh_type field of Elf64_Shdr. */ + +/* These two are primerily concerned with ECOFF debugging info. */ +#define SHT_ALPHA_DEBUG 0x70000001 +#define SHT_ALPHA_REGINFO 0x70000002 + +/* Legal values for sh_flags field of Elf64_Shdr. */ + +#define SHF_ALPHA_GPREL 0x10000000 + +/* Legal values for st_other field of Elf64_Sym. */ +#define STO_ALPHA_NOPV 0x80 /* No PV required. */ +#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */ + +/* Alpha relocs. */ + +#define R_ALPHA_NONE 0 /* No reloc */ +#define R_ALPHA_REFLONG 1 /* Direct 32 bit */ +#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ +#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ +#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ +#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ +#define R_ALPHA_GPDISP 6 /* Add displacement to GP */ +#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ +#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ +#define R_ALPHA_SREL16 9 /* PC relative 16 bit */ +#define R_ALPHA_SREL32 10 /* PC relative 32 bit */ +#define R_ALPHA_SREL64 11 /* PC relative 64 bit */ +#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */ +#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */ +#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */ +#define R_ALPHA_COPY 24 /* Copy symbol at runtime */ +#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ +#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ +#define R_ALPHA_RELATIVE 27 /* Adjust by program base */ +#define R_ALPHA_TLS_GD_HI 28 +#define R_ALPHA_TLSGD 29 +#define R_ALPHA_TLS_LDM 30 +#define R_ALPHA_DTPMOD64 31 +#define R_ALPHA_GOTDTPREL 32 +#define R_ALPHA_DTPREL64 33 +#define R_ALPHA_DTPRELHI 34 +#define R_ALPHA_DTPRELLO 35 +#define R_ALPHA_DTPREL16 36 +#define R_ALPHA_GOTTPREL 37 +#define R_ALPHA_TPREL64 38 +#define R_ALPHA_TPRELHI 39 +#define R_ALPHA_TPRELLO 40 +#define R_ALPHA_TPREL16 41 +/* Keep this the last entry. */ +#define R_ALPHA_NUM 46 + +/* Magic values of the LITUSE relocation addend. */ +#define LITUSE_ALPHA_ADDR 0 +#define LITUSE_ALPHA_BASE 1 +#define LITUSE_ALPHA_BYTOFF 2 +#define LITUSE_ALPHA_JSR 3 +#define LITUSE_ALPHA_TLS_GD 4 +#define LITUSE_ALPHA_TLS_LDM 5 + +/* Legal values for d_tag of Elf64_Dyn. */ +#define DT_ALPHA_PLTRO (DT_LOPROC + 0) +#define DT_ALPHA_NUM 1 + +/* PowerPC specific declarations */ + +/* Values for Elf32/64_Ehdr.e_flags. */ +#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ + +/* Cygnus local bits below */ +#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ +#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib + flag */ + +/* PowerPC relocations defined by the ABIs */ +#define R_PPC_NONE 0 +#define R_PPC_ADDR32 1 /* 32bit absolute address */ +#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ +#define R_PPC_ADDR16 3 /* 16bit absolute address */ +#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ +#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ +#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ +#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ +#define R_PPC_ADDR14_BRTAKEN 8 +#define R_PPC_ADDR14_BRNTAKEN 9 +#define R_PPC_REL24 10 /* PC relative 26 bit */ +#define R_PPC_REL14 11 /* PC relative 16 bit */ +#define R_PPC_REL14_BRTAKEN 12 +#define R_PPC_REL14_BRNTAKEN 13 +#define R_PPC_GOT16 14 +#define R_PPC_GOT16_LO 15 +#define R_PPC_GOT16_HI 16 +#define R_PPC_GOT16_HA 17 +#define R_PPC_PLTREL24 18 +#define R_PPC_COPY 19 +#define R_PPC_GLOB_DAT 20 +#define R_PPC_JMP_SLOT 21 +#define R_PPC_RELATIVE 22 +#define R_PPC_LOCAL24PC 23 +#define R_PPC_UADDR32 24 +#define R_PPC_UADDR16 25 +#define R_PPC_REL32 26 +#define R_PPC_PLT32 27 +#define R_PPC_PLTREL32 28 +#define R_PPC_PLT16_LO 29 +#define R_PPC_PLT16_HI 30 +#define R_PPC_PLT16_HA 31 +#define R_PPC_SDAREL16 32 +#define R_PPC_SECTOFF 33 +#define R_PPC_SECTOFF_LO 34 +#define R_PPC_SECTOFF_HI 35 +#define R_PPC_SECTOFF_HA 36 + +/* PowerPC relocations defined for the TLS access ABI. */ +#define R_PPC_TLS 67 /* none (sym+add)@tls */ +#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */ +#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */ +#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ +#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ +#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ +#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */ +#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */ +#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ +#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ +#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ +#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */ +#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ +#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ +#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ +#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ +#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ +#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ +#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ +#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ +#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */ +#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */ +#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ +#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ +#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */ +#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */ +#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */ +#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */ +#define R_PPC_TLSGD 95 /* none (sym+add)@tlsgd */ +#define R_PPC_TLSLD 96 /* none (sym+add)@tlsld */ + +/* The remaining relocs are from the Embedded ELF ABI, and are not + in the SVR4 ELF ABI. */ +#define R_PPC_EMB_NADDR32 101 +#define R_PPC_EMB_NADDR16 102 +#define R_PPC_EMB_NADDR16_LO 103 +#define R_PPC_EMB_NADDR16_HI 104 +#define R_PPC_EMB_NADDR16_HA 105 +#define R_PPC_EMB_SDAI16 106 +#define R_PPC_EMB_SDA2I16 107 +#define R_PPC_EMB_SDA2REL 108 +#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ +#define R_PPC_EMB_MRKREF 110 +#define R_PPC_EMB_RELSEC16 111 +#define R_PPC_EMB_RELST_LO 112 +#define R_PPC_EMB_RELST_HI 113 +#define R_PPC_EMB_RELST_HA 114 +#define R_PPC_EMB_BIT_FLD 115 +#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ + +/* Diab tool relocations. */ +#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ +#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ +#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ +#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ +#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ +#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ + +/* GNU extension to support local ifunc. */ +#define R_PPC_IRELATIVE 248 + +/* GNU relocs used in PIC code sequences. */ +#define R_PPC_REL16 249 /* half16 (sym+add-.) */ +#define R_PPC_REL16_LO 250 /* half16 (sym+add-.)@l */ +#define R_PPC_REL16_HI 251 /* half16 (sym+add-.)@h */ +#define R_PPC_REL16_HA 252 /* half16 (sym+add-.)@ha */ + +/* This is a phony reloc to handle any old fashioned TOC16 references + that may still be in object files. */ +#define R_PPC_TOC16 255 + +/* PowerPC specific values for the Dyn d_tag field. */ +#define DT_PPC_GOT (DT_LOPROC + 0) +#define DT_PPC_OPT (DT_LOPROC + 1) +#define DT_PPC_NUM 2 + +/* PowerPC specific values for the DT_PPC_OPT Dyn entry. */ +#define PPC_OPT_TLS 1 + +/* PowerPC64 relocations defined by the ABIs */ +#define R_PPC64_NONE R_PPC_NONE +#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */ +#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */ +#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */ +#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */ +#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */ +#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */ +#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */ +#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN +#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN +#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */ +#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */ +#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN +#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN +#define R_PPC64_GOT16 R_PPC_GOT16 +#define R_PPC64_GOT16_LO R_PPC_GOT16_LO +#define R_PPC64_GOT16_HI R_PPC_GOT16_HI +#define R_PPC64_GOT16_HA R_PPC_GOT16_HA + +#define R_PPC64_COPY R_PPC_COPY +#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT +#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT +#define R_PPC64_RELATIVE R_PPC_RELATIVE + +#define R_PPC64_UADDR32 R_PPC_UADDR32 +#define R_PPC64_UADDR16 R_PPC_UADDR16 +#define R_PPC64_REL32 R_PPC_REL32 +#define R_PPC64_PLT32 R_PPC_PLT32 +#define R_PPC64_PLTREL32 R_PPC_PLTREL32 +#define R_PPC64_PLT16_LO R_PPC_PLT16_LO +#define R_PPC64_PLT16_HI R_PPC_PLT16_HI +#define R_PPC64_PLT16_HA R_PPC_PLT16_HA + +#define R_PPC64_SECTOFF R_PPC_SECTOFF +#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO +#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI +#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA +#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */ +#define R_PPC64_ADDR64 38 /* doubleword64 S + A */ +#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */ +#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */ +#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */ +#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */ +#define R_PPC64_UADDR64 43 /* doubleword64 S + A */ +#define R_PPC64_REL64 44 /* doubleword64 S + A - P */ +#define R_PPC64_PLT64 45 /* doubleword64 L + A */ +#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */ +#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */ +#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */ +#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */ +#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */ +#define R_PPC64_TOC 51 /* doubleword64 .TOC */ +#define R_PPC64_PLTGOT16 52 /* half16* M + A */ +#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */ +#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */ +#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */ + +#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */ +#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */ +#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */ +#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */ +#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */ +#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */ +#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */ +#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */ +#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */ +#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */ +#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */ + +/* PowerPC64 relocations defined for the TLS access ABI. */ +#define R_PPC64_TLS 67 /* none (sym+add)@tls */ +#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */ +#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */ +#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ +#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ +#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ +#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */ +#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */ +#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ +#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ +#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ +#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */ +#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ +#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ +#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ +#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ +#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ +#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ +#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ +#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ +#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */ +#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */ +#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ +#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ +#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */ +#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */ +#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */ +#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */ +#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */ +#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */ +#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */ +#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */ +#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */ +#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */ +#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */ +#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */ +#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */ +#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */ +#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */ +#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */ +#define R_PPC64_TLSGD 107 /* none (sym+add)@tlsgd */ +#define R_PPC64_TLSLD 108 /* none (sym+add)@tlsld */ +#define R_PPC64_TOCSAVE 109 /* none */ + +/* Added when HA and HI relocs were changed to report overflows. */ +#define R_PPC64_ADDR16_HIGH 110 +#define R_PPC64_ADDR16_HIGHA 111 +#define R_PPC64_TPREL16_HIGH 112 +#define R_PPC64_TPREL16_HIGHA 113 +#define R_PPC64_DTPREL16_HIGH 114 +#define R_PPC64_DTPREL16_HIGHA 115 + +/* GNU extension to support local ifunc. */ +#define R_PPC64_JMP_IREL 247 +#define R_PPC64_IRELATIVE 248 +#define R_PPC64_REL16 249 /* half16 (sym+add-.) */ +#define R_PPC64_REL16_LO 250 /* half16 (sym+add-.)@l */ +#define R_PPC64_REL16_HI 251 /* half16 (sym+add-.)@h */ +#define R_PPC64_REL16_HA 252 /* half16 (sym+add-.)@ha */ + +/* e_flags bits specifying ABI. + 1 for original function descriptor using ABI, + 2 for revised ABI without function descriptors, + 0 for unspecified or not using any features affected by the differences. */ +#define EF_PPC64_ABI 3 + +/* PowerPC64 specific values for the Dyn d_tag field. */ +#define DT_PPC64_GLINK (DT_LOPROC + 0) +#define DT_PPC64_OPD (DT_LOPROC + 1) +#define DT_PPC64_OPDSZ (DT_LOPROC + 2) +#define DT_PPC64_OPT (DT_LOPROC + 3) +#define DT_PPC64_NUM 4 + +/* PowerPC64 specific bits in the DT_PPC64_OPT Dyn entry. */ +#define PPC64_OPT_TLS 1 +#define PPC64_OPT_MULTI_TOC 2 +#define PPC64_OPT_LOCALENTRY 4 + +/* PowerPC64 specific values for the Elf64_Sym st_other field. */ +#define STO_PPC64_LOCAL_BIT 5 +#define STO_PPC64_LOCAL_MASK (7 << STO_PPC64_LOCAL_BIT) +#define PPC64_LOCAL_ENTRY_OFFSET(other) \ + (((1 << (((other) & STO_PPC64_LOCAL_MASK) >> STO_PPC64_LOCAL_BIT)) >> 2) << 2) + + +/* ARM specific declarations */ + +/* Processor specific flags for the ELF header e_flags field. */ +#define EF_ARM_RELEXEC 0x01 +#define EF_ARM_HASENTRY 0x02 +#define EF_ARM_INTERWORK 0x04 +#define EF_ARM_APCS_26 0x08 +#define EF_ARM_APCS_FLOAT 0x10 +#define EF_ARM_PIC 0x20 +#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */ +#define EF_ARM_NEW_ABI 0x80 +#define EF_ARM_OLD_ABI 0x100 +#define EF_ARM_SOFT_FLOAT 0x200 +#define EF_ARM_VFP_FLOAT 0x400 +#define EF_ARM_MAVERICK_FLOAT 0x800 + +#define EF_ARM_ABI_FLOAT_SOFT 0x200 /* NB conflicts with EF_ARM_SOFT_FLOAT */ +#define EF_ARM_ABI_FLOAT_HARD 0x400 /* NB conflicts with EF_ARM_VFP_FLOAT */ + + +/* Other constants defined in the ARM ELF spec. version B-01. */ +/* NB. These conflict with values defined above. */ +#define EF_ARM_SYMSARESORTED 0x04 +#define EF_ARM_DYNSYMSUSESEGIDX 0x08 +#define EF_ARM_MAPSYMSFIRST 0x10 +#define EF_ARM_EABIMASK 0XFF000000 + +/* Constants defined in AAELF. */ +#define EF_ARM_BE8 0x00800000 +#define EF_ARM_LE8 0x00400000 + +#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) +#define EF_ARM_EABI_UNKNOWN 0x00000000 +#define EF_ARM_EABI_VER1 0x01000000 +#define EF_ARM_EABI_VER2 0x02000000 +#define EF_ARM_EABI_VER3 0x03000000 +#define EF_ARM_EABI_VER4 0x04000000 +#define EF_ARM_EABI_VER5 0x05000000 + +/* Additional symbol types for Thumb. */ +#define STT_ARM_TFUNC STT_LOPROC /* A Thumb function. */ +#define STT_ARM_16BIT STT_HIPROC /* A Thumb label. */ + +/* ARM-specific values for sh_flags */ +#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ +#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined + in the input to a link step. */ + +/* ARM-specific program header flags */ +#define PF_ARM_SB 0x10000000 /* Segment contains the location + addressed by the static base. */ +#define PF_ARM_PI 0x20000000 /* Position-independent segment. */ +#define PF_ARM_ABS 0x40000000 /* Absolute segment. */ + +/* Processor specific values for the Phdr p_type field. */ +#define PT_ARM_EXIDX (PT_LOPROC + 1) /* ARM unwind segment. */ + +/* Processor specific values for the Shdr sh_type field. */ +#define SHT_ARM_EXIDX (SHT_LOPROC + 1) /* ARM unwind section. */ +#define SHT_ARM_PREEMPTMAP (SHT_LOPROC + 2) /* Preemption details. */ +#define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) /* ARM attributes section. */ + + +/* AArch64 relocs. */ + +#define R_AARCH64_NONE 0 /* No relocation. */ + +/* ILP32 AArch64 relocs. */ +#define R_AARCH64_P32_ABS32 1 /* Direct 32 bit. */ +#define R_AARCH64_P32_COPY 180 /* Copy symbol at runtime. */ +#define R_AARCH64_P32_GLOB_DAT 181 /* Create GOT entry. */ +#define R_AARCH64_P32_JUMP_SLOT 182 /* Create PLT entry. */ +#define R_AARCH64_P32_RELATIVE 183 /* Adjust by program base. */ +#define R_AARCH64_P32_TLS_DTPMOD 184 /* Module number, 32 bit. */ +#define R_AARCH64_P32_TLS_DTPREL 185 /* Module-relative offset, 32 bit. */ +#define R_AARCH64_P32_TLS_TPREL 186 /* TP-relative offset, 32 bit. */ +#define R_AARCH64_P32_TLSDESC 187 /* TLS Descriptor. */ +#define R_AARCH64_P32_IRELATIVE 188 /* STT_GNU_IFUNC relocation. */ + +/* LP64 AArch64 relocs. */ +#define R_AARCH64_ABS64 257 /* Direct 64 bit. */ +#define R_AARCH64_ABS32 258 /* Direct 32 bit. */ +#define R_AARCH64_ABS16 259 /* Direct 16-bit. */ +#define R_AARCH64_PREL64 260 /* PC-relative 64-bit. */ +#define R_AARCH64_PREL32 261 /* PC-relative 32-bit. */ +#define R_AARCH64_PREL16 262 /* PC-relative 16-bit. */ +#define R_AARCH64_MOVW_UABS_G0 263 /* Dir. MOVZ imm. from bits 15:0. */ +#define R_AARCH64_MOVW_UABS_G0_NC 264 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_UABS_G1 265 /* Dir. MOVZ imm. from bits 31:16. */ +#define R_AARCH64_MOVW_UABS_G1_NC 266 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_UABS_G2 267 /* Dir. MOVZ imm. from bits 47:32. */ +#define R_AARCH64_MOVW_UABS_G2_NC 268 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_UABS_G3 269 /* Dir. MOV{K,Z} imm. from 63:48. */ +#define R_AARCH64_MOVW_SABS_G0 270 /* Dir. MOV{N,Z} imm. from 15:0. */ +#define R_AARCH64_MOVW_SABS_G1 271 /* Dir. MOV{N,Z} imm. from 31:16. */ +#define R_AARCH64_MOVW_SABS_G2 272 /* Dir. MOV{N,Z} imm. from 47:32. */ +#define R_AARCH64_LD_PREL_LO19 273 /* PC-rel. LD imm. from bits 20:2. */ +#define R_AARCH64_ADR_PREL_LO21 274 /* PC-rel. ADR imm. from bits 20:0. */ +#define R_AARCH64_ADR_PREL_PG_HI21 275 /* Page-rel. ADRP imm. from 32:12. */ +#define R_AARCH64_ADR_PREL_PG_HI21_NC 276 /* Likewise; no overflow check. */ +#define R_AARCH64_ADD_ABS_LO12_NC 277 /* Dir. ADD imm. from bits 11:0. */ +#define R_AARCH64_LDST8_ABS_LO12_NC 278 /* Likewise for LD/ST; no check. */ +#define R_AARCH64_TSTBR14 279 /* PC-rel. TBZ/TBNZ imm. from 15:2. */ +#define R_AARCH64_CONDBR19 280 /* PC-rel. cond. br. imm. from 20:2. */ +#define R_AARCH64_JUMP26 282 /* PC-rel. B imm. from bits 27:2. */ +#define R_AARCH64_CALL26 283 /* Likewise for CALL. */ +#define R_AARCH64_LDST16_ABS_LO12_NC 284 /* Dir. ADD imm. from bits 11:1. */ +#define R_AARCH64_LDST32_ABS_LO12_NC 285 /* Likewise for bits 11:2. */ +#define R_AARCH64_LDST64_ABS_LO12_NC 286 /* Likewise for bits 11:3. */ +#define R_AARCH64_MOVW_PREL_G0 287 /* PC-rel. MOV{N,Z} imm. from 15:0. */ +#define R_AARCH64_MOVW_PREL_G0_NC 288 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_PREL_G1 289 /* PC-rel. MOV{N,Z} imm. from 31:16. */ +#define R_AARCH64_MOVW_PREL_G1_NC 290 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_PREL_G2 291 /* PC-rel. MOV{N,Z} imm. from 47:32. */ +#define R_AARCH64_MOVW_PREL_G2_NC 292 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_PREL_G3 293 /* PC-rel. MOV{N,Z} imm. from 63:48. */ +#define R_AARCH64_LDST128_ABS_LO12_NC 299 /* Dir. ADD imm. from bits 11:4. */ +#define R_AARCH64_MOVW_GOTOFF_G0 300 /* GOT-rel. off. MOV{N,Z} imm. 15:0. */ +#define R_AARCH64_MOVW_GOTOFF_G0_NC 301 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_GOTOFF_G1 302 /* GOT-rel. o. MOV{N,Z} imm. 31:16. */ +#define R_AARCH64_MOVW_GOTOFF_G1_NC 303 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_GOTOFF_G2 304 /* GOT-rel. o. MOV{N,Z} imm. 47:32. */ +#define R_AARCH64_MOVW_GOTOFF_G2_NC 305 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_GOTOFF_G3 306 /* GOT-rel. o. MOV{N,Z} imm. 63:48. */ +#define R_AARCH64_GOTREL64 307 /* GOT-relative 64-bit. */ +#define R_AARCH64_GOTREL32 308 /* GOT-relative 32-bit. */ +#define R_AARCH64_GOT_LD_PREL19 309 /* PC-rel. GOT off. load imm. 20:2. */ +#define R_AARCH64_LD64_GOTOFF_LO15 310 /* GOT-rel. off. LD/ST imm. 14:3. */ +#define R_AARCH64_ADR_GOT_PAGE 311 /* P-page-rel. GOT off. ADRP 32:12. */ +#define R_AARCH64_LD64_GOT_LO12_NC 312 /* Dir. GOT off. LD/ST imm. 11:3. */ +#define R_AARCH64_LD64_GOTPAGE_LO15 313 /* GOT-page-rel. GOT off. LD/ST 14:3 */ +#define R_AARCH64_TLSGD_ADR_PREL21 512 /* PC-relative ADR imm. 20:0. */ +#define R_AARCH64_TLSGD_ADR_PAGE21 513 /* page-rel. ADRP imm. 32:12. */ +#define R_AARCH64_TLSGD_ADD_LO12_NC 514 /* direct ADD imm. from 11:0. */ +#define R_AARCH64_TLSGD_MOVW_G1 515 /* GOT-rel. MOV{N,Z} 31:16. */ +#define R_AARCH64_TLSGD_MOVW_G0_NC 516 /* GOT-rel. MOVK imm. 15:0. */ +#define R_AARCH64_TLSLD_ADR_PREL21 517 /* Like 512; local dynamic model. */ +#define R_AARCH64_TLSLD_ADR_PAGE21 518 /* Like 513; local dynamic model. */ +#define R_AARCH64_TLSLD_ADD_LO12_NC 519 /* Like 514; local dynamic model. */ +#define R_AARCH64_TLSLD_MOVW_G1 520 /* Like 515; local dynamic model. */ +#define R_AARCH64_TLSLD_MOVW_G0_NC 521 /* Like 516; local dynamic model. */ +#define R_AARCH64_TLSLD_LD_PREL19 522 /* TLS PC-rel. load imm. 20:2. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G2 523 /* TLS DTP-rel. MOV{N,Z} 47:32. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G1 524 /* TLS DTP-rel. MOV{N,Z} 31:16. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC 525 /* Likewise; MOVK; no check. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G0 526 /* TLS DTP-rel. MOV{N,Z} 15:0. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC 527 /* Likewise; MOVK; no check. */ +#define R_AARCH64_TLSLD_ADD_DTPREL_HI12 528 /* DTP-rel. ADD imm. from 23:12. */ +#define R_AARCH64_TLSLD_ADD_DTPREL_LO12 529 /* DTP-rel. ADD imm. from 11:0. */ +#define R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC 530 /* Likewise; no ovfl. check. */ +#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12 531 /* DTP-rel. LD/ST imm. 11:0. */ +#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC 532 /* Likewise; no check. */ +#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12 533 /* DTP-rel. LD/ST imm. 11:1. */ +#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC 534 /* Likewise; no check. */ +#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12 535 /* DTP-rel. LD/ST imm. 11:2. */ +#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC 536 /* Likewise; no check. */ +#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12 537 /* DTP-rel. LD/ST imm. 11:3. */ +#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC 538 /* Likewise; no check. */ +#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G1 539 /* GOT-rel. MOV{N,Z} 31:16. */ +#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC 540 /* GOT-rel. MOVK 15:0. */ +#define R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 541 /* Page-rel. ADRP 32:12. */ +#define R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC 542 /* Direct LD off. 11:3. */ +#define R_AARCH64_TLSIE_LD_GOTTPREL_PREL19 543 /* PC-rel. load imm. 20:2. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G2 544 /* TLS TP-rel. MOV{N,Z} 47:32. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G1 545 /* TLS TP-rel. MOV{N,Z} 31:16. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G1_NC 546 /* Likewise; MOVK; no check. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G0 547 /* TLS TP-rel. MOV{N,Z} 15:0. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G0_NC 548 /* Likewise; MOVK; no check. */ +#define R_AARCH64_TLSLE_ADD_TPREL_HI12 549 /* TP-rel. ADD imm. 23:12. */ +#define R_AARCH64_TLSLE_ADD_TPREL_LO12 550 /* TP-rel. ADD imm. 11:0. */ +#define R_AARCH64_TLSLE_ADD_TPREL_LO12_NC 551 /* Likewise; no ovfl. check. */ +#define R_AARCH64_TLSLE_LDST8_TPREL_LO12 552 /* TP-rel. LD/ST off. 11:0. */ +#define R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC 553 /* Likewise; no ovfl. check. */ +#define R_AARCH64_TLSLE_LDST16_TPREL_LO12 554 /* TP-rel. LD/ST off. 11:1. */ +#define R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC 555 /* Likewise; no check. */ +#define R_AARCH64_TLSLE_LDST32_TPREL_LO12 556 /* TP-rel. LD/ST off. 11:2. */ +#define R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC 557 /* Likewise; no check. */ +#define R_AARCH64_TLSLE_LDST64_TPREL_LO12 558 /* TP-rel. LD/ST off. 11:3. */ +#define R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC 559 /* Likewise; no check. */ +#define R_AARCH64_TLSDESC_LD_PREL19 560 /* PC-rel. load immediate 20:2. */ +#define R_AARCH64_TLSDESC_ADR_PREL21 561 /* PC-rel. ADR immediate 20:0. */ +#define R_AARCH64_TLSDESC_ADR_PAGE21 562 /* Page-rel. ADRP imm. 32:12. */ +#define R_AARCH64_TLSDESC_LD64_LO12 563 /* Direct LD off. from 11:3. */ +#define R_AARCH64_TLSDESC_ADD_LO12 564 /* Direct ADD imm. from 11:0. */ +#define R_AARCH64_TLSDESC_OFF_G1 565 /* GOT-rel. MOV{N,Z} imm. 31:16. */ +#define R_AARCH64_TLSDESC_OFF_G0_NC 566 /* GOT-rel. MOVK imm. 15:0; no ck. */ +#define R_AARCH64_TLSDESC_LDR 567 /* Relax LDR. */ +#define R_AARCH64_TLSDESC_ADD 568 /* Relax ADD. */ +#define R_AARCH64_TLSDESC_CALL 569 /* Relax BLR. */ +#define R_AARCH64_TLSLE_LDST128_TPREL_LO12 570 /* TP-rel. LD/ST off. 11:4. */ +#define R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC 571 /* Likewise; no check. */ +#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12 572 /* DTP-rel. LD/ST imm. 11:4. */ +#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC 573 /* Likewise; no check. */ +#define R_AARCH64_COPY 1024 /* Copy symbol at runtime. */ +#define R_AARCH64_GLOB_DAT 1025 /* Create GOT entry. */ +#define R_AARCH64_JUMP_SLOT 1026 /* Create PLT entry. */ +#define R_AARCH64_RELATIVE 1027 /* Adjust by program base. */ +#define R_AARCH64_TLS_DTPMOD 1028 /* Module number, 64 bit. */ +#define R_AARCH64_TLS_DTPREL 1029 /* Module-relative offset, 64 bit. */ +#define R_AARCH64_TLS_TPREL 1030 /* TP-relative offset, 64 bit. */ +#define R_AARCH64_TLSDESC 1031 /* TLS Descriptor. */ +#define R_AARCH64_IRELATIVE 1032 /* STT_GNU_IFUNC relocation. */ + +/* ARM relocs. */ + +#define R_ARM_NONE 0 /* No reloc */ +#define R_ARM_PC24 1 /* Deprecated PC relative 26 + bit branch. */ +#define R_ARM_ABS32 2 /* Direct 32 bit */ +#define R_ARM_REL32 3 /* PC relative 32 bit */ +#define R_ARM_PC13 4 +#define R_ARM_ABS16 5 /* Direct 16 bit */ +#define R_ARM_ABS12 6 /* Direct 12 bit */ +#define R_ARM_THM_ABS5 7 /* Direct & 0x7C (LDR, STR). */ +#define R_ARM_ABS8 8 /* Direct 8 bit */ +#define R_ARM_SBREL32 9 +#define R_ARM_THM_PC22 10 /* PC relative 24 bit (Thumb32 BL). */ +#define R_ARM_THM_PC8 11 /* PC relative & 0x3FC + (Thumb16 LDR, ADD, ADR). */ +#define R_ARM_AMP_VCALL9 12 +#define R_ARM_SWI24 13 /* Obsolete static relocation. */ +#define R_ARM_TLS_DESC 13 /* Dynamic relocation. */ +#define R_ARM_THM_SWI8 14 /* Reserved. */ +#define R_ARM_XPC25 15 /* Reserved. */ +#define R_ARM_THM_XPC22 16 /* Reserved. */ +#define R_ARM_TLS_DTPMOD32 17 /* ID of module containing symbol */ +#define R_ARM_TLS_DTPOFF32 18 /* Offset in TLS block */ +#define R_ARM_TLS_TPOFF32 19 /* Offset in static TLS block */ +#define R_ARM_COPY 20 /* Copy symbol at runtime */ +#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ +#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ +#define R_ARM_RELATIVE 23 /* Adjust by program base */ +#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ +#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ +#define R_ARM_GOT32 26 /* 32 bit GOT entry */ +#define R_ARM_PLT32 27 /* Deprecated, 32 bit PLT address. */ +#define R_ARM_CALL 28 /* PC relative 24 bit (BL, BLX). */ +#define R_ARM_JUMP24 29 /* PC relative 24 bit + (B, BL). */ +#define R_ARM_THM_JUMP24 30 /* PC relative 24 bit (Thumb32 B.W). */ +#define R_ARM_BASE_ABS 31 /* Adjust by program base. */ +#define R_ARM_ALU_PCREL_7_0 32 /* Obsolete. */ +#define R_ARM_ALU_PCREL_15_8 33 /* Obsolete. */ +#define R_ARM_ALU_PCREL_23_15 34 /* Obsolete. */ +#define R_ARM_LDR_SBREL_11_0 35 /* Deprecated, prog. base relative. */ +#define R_ARM_ALU_SBREL_19_12 36 /* Deprecated, prog. base relative. */ +#define R_ARM_ALU_SBREL_27_20 37 /* Deprecated, prog. base relative. */ +#define R_ARM_TARGET1 38 +#define R_ARM_SBREL31 39 /* Program base relative. */ +#define R_ARM_V4BX 40 +#define R_ARM_TARGET2 41 +#define R_ARM_PREL31 42 /* 32 bit PC relative. */ +#define R_ARM_MOVW_ABS_NC 43 /* Direct 16-bit (MOVW). */ +#define R_ARM_MOVT_ABS 44 /* Direct high 16-bit (MOVT). */ +#define R_ARM_MOVW_PREL_NC 45 /* PC relative 16-bit (MOVW). */ +#define R_ARM_MOVT_PREL 46 /* PC relative (MOVT). */ +#define R_ARM_THM_MOVW_ABS_NC 47 /* Direct 16 bit (Thumb32 MOVW). */ +#define R_ARM_THM_MOVT_ABS 48 /* Direct high 16 bit + (Thumb32 MOVT). */ +#define R_ARM_THM_MOVW_PREL_NC 49 /* PC relative 16 bit + (Thumb32 MOVW). */ +#define R_ARM_THM_MOVT_PREL 50 /* PC relative high 16 bit + (Thumb32 MOVT). */ +#define R_ARM_THM_JUMP19 51 /* PC relative 20 bit + (Thumb32 B.W). */ +#define R_ARM_THM_JUMP6 52 /* PC relative X & 0x7E + (Thumb16 CBZ, CBNZ). */ +#define R_ARM_THM_ALU_PREL_11_0 53 /* PC relative 12 bit + (Thumb32 ADR.W). */ +#define R_ARM_THM_PC12 54 /* PC relative 12 bit + (Thumb32 LDR{D,SB,H,SH}). */ +#define R_ARM_ABS32_NOI 55 /* Direct 32-bit. */ +#define R_ARM_REL32_NOI 56 /* PC relative 32-bit. */ +#define R_ARM_ALU_PC_G0_NC 57 /* PC relative (ADD, SUB). */ +#define R_ARM_ALU_PC_G0 58 /* PC relative (ADD, SUB). */ +#define R_ARM_ALU_PC_G1_NC 59 /* PC relative (ADD, SUB). */ +#define R_ARM_ALU_PC_G1 60 /* PC relative (ADD, SUB). */ +#define R_ARM_ALU_PC_G2 61 /* PC relative (ADD, SUB). */ +#define R_ARM_LDR_PC_G1 62 /* PC relative (LDR,STR,LDRB,STRB). */ +#define R_ARM_LDR_PC_G2 63 /* PC relative (LDR,STR,LDRB,STRB). */ +#define R_ARM_LDRS_PC_G0 64 /* PC relative (STR{D,H}, + LDR{D,SB,H,SH}). */ +#define R_ARM_LDRS_PC_G1 65 /* PC relative (STR{D,H}, + LDR{D,SB,H,SH}). */ +#define R_ARM_LDRS_PC_G2 66 /* PC relative (STR{D,H}, + LDR{D,SB,H,SH}). */ +#define R_ARM_LDC_PC_G0 67 /* PC relative (LDC, STC). */ +#define R_ARM_LDC_PC_G1 68 /* PC relative (LDC, STC). */ +#define R_ARM_LDC_PC_G2 69 /* PC relative (LDC, STC). */ +#define R_ARM_ALU_SB_G0_NC 70 /* Program base relative (ADD,SUB). */ +#define R_ARM_ALU_SB_G0 71 /* Program base relative (ADD,SUB). */ +#define R_ARM_ALU_SB_G1_NC 72 /* Program base relative (ADD,SUB). */ +#define R_ARM_ALU_SB_G1 73 /* Program base relative (ADD,SUB). */ +#define R_ARM_ALU_SB_G2 74 /* Program base relative (ADD,SUB). */ +#define R_ARM_LDR_SB_G0 75 /* Program base relative (LDR, + STR, LDRB, STRB). */ +#define R_ARM_LDR_SB_G1 76 /* Program base relative + (LDR, STR, LDRB, STRB). */ +#define R_ARM_LDR_SB_G2 77 /* Program base relative + (LDR, STR, LDRB, STRB). */ +#define R_ARM_LDRS_SB_G0 78 /* Program base relative + (LDR, STR, LDRB, STRB). */ +#define R_ARM_LDRS_SB_G1 79 /* Program base relative + (LDR, STR, LDRB, STRB). */ +#define R_ARM_LDRS_SB_G2 80 /* Program base relative + (LDR, STR, LDRB, STRB). */ +#define R_ARM_LDC_SB_G0 81 /* Program base relative (LDC,STC). */ +#define R_ARM_LDC_SB_G1 82 /* Program base relative (LDC,STC). */ +#define R_ARM_LDC_SB_G2 83 /* Program base relative (LDC,STC). */ +#define R_ARM_MOVW_BREL_NC 84 /* Program base relative 16 + bit (MOVW). */ +#define R_ARM_MOVT_BREL 85 /* Program base relative high + 16 bit (MOVT). */ +#define R_ARM_MOVW_BREL 86 /* Program base relative 16 + bit (MOVW). */ +#define R_ARM_THM_MOVW_BREL_NC 87 /* Program base relative 16 + bit (Thumb32 MOVW). */ +#define R_ARM_THM_MOVT_BREL 88 /* Program base relative high + 16 bit (Thumb32 MOVT). */ +#define R_ARM_THM_MOVW_BREL 89 /* Program base relative 16 + bit (Thumb32 MOVW). */ +#define R_ARM_TLS_GOTDESC 90 +#define R_ARM_TLS_CALL 91 +#define R_ARM_TLS_DESCSEQ 92 /* TLS relaxation. */ +#define R_ARM_THM_TLS_CALL 93 +#define R_ARM_PLT32_ABS 94 +#define R_ARM_GOT_ABS 95 /* GOT entry. */ +#define R_ARM_GOT_PREL 96 /* PC relative GOT entry. */ +#define R_ARM_GOT_BREL12 97 /* GOT entry relative to GOT + origin (LDR). */ +#define R_ARM_GOTOFF12 98 /* 12 bit, GOT entry relative + to GOT origin (LDR, STR). */ +#define R_ARM_GOTRELAX 99 +#define R_ARM_GNU_VTENTRY 100 +#define R_ARM_GNU_VTINHERIT 101 +#define R_ARM_THM_PC11 102 /* PC relative & 0xFFE (Thumb16 B). */ +#define R_ARM_THM_PC9 103 /* PC relative & 0x1FE + (Thumb16 B/B). */ +#define R_ARM_TLS_GD32 104 /* PC-rel 32 bit for global dynamic + thread local data */ +#define R_ARM_TLS_LDM32 105 /* PC-rel 32 bit for local dynamic + thread local data */ +#define R_ARM_TLS_LDO32 106 /* 32 bit offset relative to TLS + block */ +#define R_ARM_TLS_IE32 107 /* PC-rel 32 bit for GOT entry of + static TLS block offset */ +#define R_ARM_TLS_LE32 108 /* 32 bit offset relative to static + TLS block */ +#define R_ARM_TLS_LDO12 109 /* 12 bit relative to TLS + block (LDR, STR). */ +#define R_ARM_TLS_LE12 110 /* 12 bit relative to static + TLS block (LDR, STR). */ +#define R_ARM_TLS_IE12GP 111 /* 12 bit GOT entry relative + to GOT origin (LDR). */ +#define R_ARM_ME_TOO 128 /* Obsolete. */ +#define R_ARM_THM_TLS_DESCSEQ 129 +#define R_ARM_THM_TLS_DESCSEQ16 129 +#define R_ARM_THM_TLS_DESCSEQ32 130 +#define R_ARM_THM_GOT_BREL12 131 /* GOT entry relative to GOT + origin, 12 bit (Thumb32 LDR). */ +#define R_ARM_IRELATIVE 160 +#define R_ARM_RXPC25 249 +#define R_ARM_RSBREL32 250 +#define R_ARM_THM_RPC22 251 +#define R_ARM_RREL32 252 +#define R_ARM_RABS22 253 +#define R_ARM_RPC24 254 +#define R_ARM_RBASE 255 +/* Keep this the last entry. */ +#define R_ARM_NUM 256 + +/* csky */ +#define R_CKCORE_NONE 0 /* no reloc */ +#define R_CKCORE_ADDR32 1 /* direct 32 bit (S + A) */ +#define R_CKCORE_PCRELIMM8BY4 2 /* disp ((S + A - P) >> 2) & 0xff */ +#define R_CKCORE_PCRELIMM11BY2 3 /* disp ((S + A - P) >> 1) & 0x7ff */ +#define R_CKCORE_PCREL32 5 /* 32-bit rel (S + A - P) */ +#define R_CKCORE_PCRELJSR_IMM11BY2 6 /* disp ((S + A - P) >>1) & 0x7ff */ +#define R_CKCORE_RELATIVE 9 /* 32 bit adjust program base(B + A)*/ +#define R_CKCORE_COPY 10 /* 32 bit adjust by program base */ +#define R_CKCORE_GLOB_DAT 11 /* off between got and sym (S) */ +#define R_CKCORE_JUMP_SLOT 12 /* PLT entry (S) */ +#define R_CKCORE_GOTOFF 13 /* offset to GOT (S + A - GOT) */ +#define R_CKCORE_GOTPC 14 /* PC offset to GOT (GOT + A - P) */ +#define R_CKCORE_GOT32 15 /* 32 bit GOT entry (G) */ +#define R_CKCORE_PLT32 16 /* 32 bit PLT entry (G) */ +#define R_CKCORE_ADDRGOT 17 /* GOT entry in GLOB_DAT (GOT + G) */ +#define R_CKCORE_ADDRPLT 18 /* PLT entry in GLOB_DAT (GOT + G) */ +#define R_CKCORE_PCREL_IMM26BY2 19 /* ((S + A - P) >> 1) & 0x3ffffff */ +#define R_CKCORE_PCREL_IMM16BY2 20 /* disp ((S + A - P) >> 1) & 0xffff */ +#define R_CKCORE_PCREL_IMM16BY4 21 /* disp ((S + A - P) >> 2) & 0xffff */ +#define R_CKCORE_PCREL_IMM10BY2 22 /* disp ((S + A - P) >> 1) & 0x3ff */ +#define R_CKCORE_PCREL_IMM10BY4 23 /* disp ((S + A - P) >> 2) & 0x3ff */ +#define R_CKCORE_ADDR_HI16 24 /* high & low 16 bit ADDR */ + /* ((S + A) >> 16) & 0xffff */ +#define R_CKCORE_ADDR_LO16 25 /* (S + A) & 0xffff */ +#define R_CKCORE_GOTPC_HI16 26 /* high & low 16 bit GOTPC */ + /* ((GOT + A - P) >> 16) & 0xffff */ +#define R_CKCORE_GOTPC_LO16 27 /* (GOT + A - P) & 0xffff */ +#define R_CKCORE_GOTOFF_HI16 28 /* high & low 16 bit GOTOFF */ + /* ((S + A - GOT) >> 16) & 0xffff */ +#define R_CKCORE_GOTOFF_LO16 29 /* (S + A - GOT) & 0xffff */ +#define R_CKCORE_GOT12 30 /* 12 bit disp GOT entry (G) */ +#define R_CKCORE_GOT_HI16 31 /* high & low 16 bit GOT */ + /* (G >> 16) & 0xffff */ +#define R_CKCORE_GOT_LO16 32 /* (G & 0xffff) */ +#define R_CKCORE_PLT12 33 /* 12 bit disp PLT entry (G) */ +#define R_CKCORE_PLT_HI16 34 /* high & low 16 bit PLT */ + /* (G >> 16) & 0xffff */ +#define R_CKCORE_PLT_LO16 35 /* G & 0xffff */ +#define R_CKCORE_ADDRGOT_HI16 36 /* high & low 16 bit ADDRGOT */ + /* (GOT + G * 4) & 0xffff */ +#define R_CKCORE_ADDRGOT_LO16 37 /* (GOT + G * 4) & 0xffff */ +#define R_CKCORE_ADDRPLT_HI16 38 /* high & low 16 bit ADDRPLT */ + /* ((GOT + G * 4) >> 16) & 0xFFFF */ +#define R_CKCORE_ADDRPLT_LO16 39 /* (GOT+G*4) & 0xffff */ +#define R_CKCORE_PCREL_JSR_IMM26BY2 40 /* disp ((S+A-P) >>1) & x3ffffff */ +#define R_CKCORE_TOFFSET_LO16 41 /* (S+A-BTEXT) & 0xffff */ +#define R_CKCORE_DOFFSET_LO16 42 /* (S+A-BTEXT) & 0xffff */ +#define R_CKCORE_PCREL_IMM18BY2 43 /* disp ((S+A-P) >>1) & 0x3ffff */ +#define R_CKCORE_DOFFSET_IMM18 44 /* disp (S+A-BDATA) & 0x3ffff */ +#define R_CKCORE_DOFFSET_IMM18BY2 45 /* disp ((S+A-BDATA)>>1) & 0x3ffff */ +#define R_CKCORE_DOFFSET_IMM18BY4 46 /* disp ((S+A-BDATA)>>2) & 0x3ffff */ +#define R_CKCORE_GOT_IMM18BY4 48 /* disp (G >> 2) */ +#define R_CKCORE_PLT_IMM18BY4 49 /* disp (G >> 2) */ +#define R_CKCORE_PCREL_IMM7BY4 50 /* disp ((S+A-P) >>2) & 0x7f */ +#define R_CKCORE_TLS_LE32 51 /* 32 bit offset to TLS block */ +#define R_CKCORE_TLS_IE32 52 +#define R_CKCORE_TLS_GD32 53 +#define R_CKCORE_TLS_LDM32 54 +#define R_CKCORE_TLS_LDO32 55 +#define R_CKCORE_TLS_DTPMOD32 56 +#define R_CKCORE_TLS_DTPOFF32 57 +#define R_CKCORE_TLS_TPOFF32 58 + +/* IA-64 specific declarations. */ + +/* Processor specific flags for the Ehdr e_flags field. */ +#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ +#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ +#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ + +/* Processor specific values for the Phdr p_type field. */ +#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ +#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ +#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12) +#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13) +#define PT_IA_64_HP_STACK (PT_LOOS + 0x14) + +/* Processor specific flags for the Phdr p_flags field. */ +#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ + +/* Processor specific values for the Shdr sh_type field. */ +#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ +#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ + +/* Processor specific flags for the Shdr sh_flags field. */ +#define SHF_IA_64_SHORT 0x10000000 /* section near gp */ +#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ + +/* Processor specific values for the Dyn d_tag field. */ +#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) +#define DT_IA_64_NUM 1 + +/* IA-64 relocations. */ +#define R_IA64_NONE 0x00 /* none */ +#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ +#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ +#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ +#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ +#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ +#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ +#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ +#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ +#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ +#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ +#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ +#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ +#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ +#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ +#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ +#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ +#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ +#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ +#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ +#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ +#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ +#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ +#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ +#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ +#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ +#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ +#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ +#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ +#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ +#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ +#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ +#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ +#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ +#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ +#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ +#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ +#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ +#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ +#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ +#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ +#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ +#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ +#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ +#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ +#define R_IA64_REL32MSB 0x6c /* data 4 + REL */ +#define R_IA64_REL32LSB 0x6d /* data 4 + REL */ +#define R_IA64_REL64MSB 0x6e /* data 8 + REL */ +#define R_IA64_REL64LSB 0x6f /* data 8 + REL */ +#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ +#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ +#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ +#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ +#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ +#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ +#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ +#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ +#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ +#define R_IA64_COPY 0x84 /* copy relocation */ +#define R_IA64_SUB 0x85 /* Addend and symbol difference */ +#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ +#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ +#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ +#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ +#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ +#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ +#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ +#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ +#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ +#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ +#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ +#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ +#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ +#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ +#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ +#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ +#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ + +/* SH specific declarations */ + +/* Processor specific flags for the ELF header e_flags field. */ +#define EF_SH_MACH_MASK 0x1f +#define EF_SH_UNKNOWN 0x0 +#define EF_SH1 0x1 +#define EF_SH2 0x2 +#define EF_SH3 0x3 +#define EF_SH_DSP 0x4 +#define EF_SH3_DSP 0x5 +#define EF_SH4AL_DSP 0x6 +#define EF_SH3E 0x8 +#define EF_SH4 0x9 +#define EF_SH2E 0xb +#define EF_SH4A 0xc +#define EF_SH2A 0xd +#define EF_SH4_NOFPU 0x10 +#define EF_SH4A_NOFPU 0x11 +#define EF_SH4_NOMMU_NOFPU 0x12 +#define EF_SH2A_NOFPU 0x13 +#define EF_SH3_NOMMU 0x14 +#define EF_SH2A_SH4_NOFPU 0x15 +#define EF_SH2A_SH3_NOFPU 0x16 +#define EF_SH2A_SH4 0x17 +#define EF_SH2A_SH3E 0x18 + +/* SH relocs. */ +#define R_SH_NONE 0 +#define R_SH_DIR32 1 +#define R_SH_REL32 2 +#define R_SH_DIR8WPN 3 +#define R_SH_IND12W 4 +#define R_SH_DIR8WPL 5 +#define R_SH_DIR8WPZ 6 +#define R_SH_DIR8BP 7 +#define R_SH_DIR8W 8 +#define R_SH_DIR8L 9 +#define R_SH_SWITCH16 25 +#define R_SH_SWITCH32 26 +#define R_SH_USES 27 +#define R_SH_COUNT 28 +#define R_SH_ALIGN 29 +#define R_SH_CODE 30 +#define R_SH_DATA 31 +#define R_SH_LABEL 32 +#define R_SH_SWITCH8 33 +#define R_SH_GNU_VTINHERIT 34 +#define R_SH_GNU_VTENTRY 35 +#define R_SH_TLS_GD_32 144 +#define R_SH_TLS_LD_32 145 +#define R_SH_TLS_LDO_32 146 +#define R_SH_TLS_IE_32 147 +#define R_SH_TLS_LE_32 148 +#define R_SH_TLS_DTPMOD32 149 +#define R_SH_TLS_DTPOFF32 150 +#define R_SH_TLS_TPOFF32 151 +#define R_SH_GOT32 160 +#define R_SH_PLT32 161 +#define R_SH_COPY 162 +#define R_SH_GLOB_DAT 163 +#define R_SH_JMP_SLOT 164 +#define R_SH_RELATIVE 165 +#define R_SH_GOTOFF 166 +#define R_SH_GOTPC 167 +/* Keep this the last entry. */ +#define R_SH_NUM 256 + +/* S/390 specific definitions. */ + +/* Valid values for the e_flags field. */ + +#define EF_S390_HIGH_GPRS 0x00000001 /* High GPRs kernel facility needed. */ + +/* Additional s390 relocs */ + +#define R_390_NONE 0 /* No reloc. */ +#define R_390_8 1 /* Direct 8 bit. */ +#define R_390_12 2 /* Direct 12 bit. */ +#define R_390_16 3 /* Direct 16 bit. */ +#define R_390_32 4 /* Direct 32 bit. */ +#define R_390_PC32 5 /* PC relative 32 bit. */ +#define R_390_GOT12 6 /* 12 bit GOT offset. */ +#define R_390_GOT32 7 /* 32 bit GOT offset. */ +#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ +#define R_390_COPY 9 /* Copy symbol at runtime. */ +#define R_390_GLOB_DAT 10 /* Create GOT entry. */ +#define R_390_JMP_SLOT 11 /* Create PLT entry. */ +#define R_390_RELATIVE 12 /* Adjust by program base. */ +#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */ +#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */ +#define R_390_GOT16 15 /* 16 bit GOT offset. */ +#define R_390_PC16 16 /* PC relative 16 bit. */ +#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ +#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ +#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ +#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ +#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ +#define R_390_64 22 /* Direct 64 bit. */ +#define R_390_PC64 23 /* PC relative 64 bit. */ +#define R_390_GOT64 24 /* 64 bit GOT offset. */ +#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ +#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ +#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */ +#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */ +#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */ +#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */ +#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */ +#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */ +#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */ +#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */ +#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */ +#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */ +#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */ +#define R_390_TLS_GDCALL 38 /* Tag for function call in general + dynamic TLS code. */ +#define R_390_TLS_LDCALL 39 /* Tag for function call in local + dynamic TLS code. */ +#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic + thread local data. */ +#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic + thread local data. */ +#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic + thread local data in LE code. */ +#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic + thread local data in LE code. */ +#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to + static TLS block. */ +#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to + static TLS block. */ +#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS + block. */ +#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS + block. */ +#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */ +#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */ +#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS + block. */ +#define R_390_20 57 /* Direct 20 bit. */ +#define R_390_GOT20 58 /* 20 bit GOT offset. */ +#define R_390_GOTPLT20 59 /* 20 bit offset to jump slot. */ +#define R_390_TLS_GOTIE20 60 /* 20 bit GOT offset for static TLS + block offset. */ +#define R_390_IRELATIVE 61 /* STT_GNU_IFUNC relocation. */ +/* Keep this the last entry. */ +#define R_390_NUM 62 + + +/* CRIS relocations. */ +#define R_CRIS_NONE 0 +#define R_CRIS_8 1 +#define R_CRIS_16 2 +#define R_CRIS_32 3 +#define R_CRIS_8_PCREL 4 +#define R_CRIS_16_PCREL 5 +#define R_CRIS_32_PCREL 6 +#define R_CRIS_GNU_VTINHERIT 7 +#define R_CRIS_GNU_VTENTRY 8 +#define R_CRIS_COPY 9 +#define R_CRIS_GLOB_DAT 10 +#define R_CRIS_JUMP_SLOT 11 +#define R_CRIS_RELATIVE 12 +#define R_CRIS_16_GOT 13 +#define R_CRIS_32_GOT 14 +#define R_CRIS_16_GOTPLT 15 +#define R_CRIS_32_GOTPLT 16 +#define R_CRIS_32_GOTREL 17 +#define R_CRIS_32_PLT_GOTREL 18 +#define R_CRIS_32_PLT_PCREL 19 + +#define R_CRIS_NUM 20 + + +/* AMD x86-64 relocations. */ +#define R_X86_64_NONE 0 /* No reloc */ +#define R_X86_64_64 1 /* Direct 64 bit */ +#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ +#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ +#define R_X86_64_PLT32 4 /* 32 bit PLT address */ +#define R_X86_64_COPY 5 /* Copy symbol at runtime */ +#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ +#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ +#define R_X86_64_RELATIVE 8 /* Adjust by program base */ +#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative + offset to GOT */ +#define R_X86_64_32 10 /* Direct 32 bit zero extended */ +#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ +#define R_X86_64_16 12 /* Direct 16 bit zero extended */ +#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ +#define R_X86_64_8 14 /* Direct 8 bit sign extended */ +#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ +#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ +#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */ +#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */ +#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset + to two GOT entries for GD symbol */ +#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset + to two GOT entries for LD symbol */ +#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ +#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset + to GOT entry for IE symbol */ +#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */ +#define R_X86_64_PC64 24 /* PC relative 64 bit */ +#define R_X86_64_GOTOFF64 25 /* 64 bit offset to GOT */ +#define R_X86_64_GOTPC32 26 /* 32 bit signed pc relative + offset to GOT */ +#define R_X86_64_GOT64 27 /* 64-bit GOT entry offset */ +#define R_X86_64_GOTPCREL64 28 /* 64-bit PC relative offset + to GOT entry */ +#define R_X86_64_GOTPC64 29 /* 64-bit PC relative offset to GOT */ +#define R_X86_64_GOTPLT64 30 /* like GOT64, says PLT entry needed */ +#define R_X86_64_PLTOFF64 31 /* 64-bit GOT relative offset + to PLT entry */ +#define R_X86_64_SIZE32 32 /* Size of symbol plus 32-bit addend */ +#define R_X86_64_SIZE64 33 /* Size of symbol plus 64-bit addend */ +#define R_X86_64_GOTPC32_TLSDESC 34 /* GOT offset for TLS descriptor. */ +#define R_X86_64_TLSDESC_CALL 35 /* Marker for call through TLS + descriptor. */ +#define R_X86_64_TLSDESC 36 /* TLS descriptor. */ +#define R_X86_64_IRELATIVE 37 /* Adjust indirectly by program base */ +#define R_X86_64_RELATIVE64 38 /* 64-bit adjust by program base */ + /* 39 Reserved was R_X86_64_PC32_BND */ + /* 40 Reserved was R_X86_64_PLT32_BND */ +#define R_X86_64_GOTPCRELX 41 /* Load from 32 bit signed pc relative + offset to GOT entry without REX + prefix, relaxable. */ +#define R_X86_64_REX_GOTPCRELX 42 /* Load from 32 bit signed pc relative + offset to GOT entry with REX prefix, + relaxable. */ +#define R_X86_64_NUM 43 + +/* x86-64 sh_type values. */ +#define SHT_X86_64_UNWIND 0x70000001 /* Unwind information. */ + + +/* AM33 relocations. */ +#define R_MN10300_NONE 0 /* No reloc. */ +#define R_MN10300_32 1 /* Direct 32 bit. */ +#define R_MN10300_16 2 /* Direct 16 bit. */ +#define R_MN10300_8 3 /* Direct 8 bit. */ +#define R_MN10300_PCREL32 4 /* PC-relative 32-bit. */ +#define R_MN10300_PCREL16 5 /* PC-relative 16-bit signed. */ +#define R_MN10300_PCREL8 6 /* PC-relative 8-bit signed. */ +#define R_MN10300_GNU_VTINHERIT 7 /* Ancient C++ vtable garbage... */ +#define R_MN10300_GNU_VTENTRY 8 /* ... collection annotation. */ +#define R_MN10300_24 9 /* Direct 24 bit. */ +#define R_MN10300_GOTPC32 10 /* 32-bit PCrel offset to GOT. */ +#define R_MN10300_GOTPC16 11 /* 16-bit PCrel offset to GOT. */ +#define R_MN10300_GOTOFF32 12 /* 32-bit offset from GOT. */ +#define R_MN10300_GOTOFF24 13 /* 24-bit offset from GOT. */ +#define R_MN10300_GOTOFF16 14 /* 16-bit offset from GOT. */ +#define R_MN10300_PLT32 15 /* 32-bit PCrel to PLT entry. */ +#define R_MN10300_PLT16 16 /* 16-bit PCrel to PLT entry. */ +#define R_MN10300_GOT32 17 /* 32-bit offset to GOT entry. */ +#define R_MN10300_GOT24 18 /* 24-bit offset to GOT entry. */ +#define R_MN10300_GOT16 19 /* 16-bit offset to GOT entry. */ +#define R_MN10300_COPY 20 /* Copy symbol at runtime. */ +#define R_MN10300_GLOB_DAT 21 /* Create GOT entry. */ +#define R_MN10300_JMP_SLOT 22 /* Create PLT entry. */ +#define R_MN10300_RELATIVE 23 /* Adjust by program base. */ +#define R_MN10300_TLS_GD 24 /* 32-bit offset for global dynamic. */ +#define R_MN10300_TLS_LD 25 /* 32-bit offset for local dynamic. */ +#define R_MN10300_TLS_LDO 26 /* Module-relative offset. */ +#define R_MN10300_TLS_GOTIE 27 /* GOT offset for static TLS block + offset. */ +#define R_MN10300_TLS_IE 28 /* GOT address for static TLS block + offset. */ +#define R_MN10300_TLS_LE 29 /* Offset relative to static TLS + block. */ +#define R_MN10300_TLS_DTPMOD 30 /* ID of module containing symbol. */ +#define R_MN10300_TLS_DTPOFF 31 /* Offset in module TLS block. */ +#define R_MN10300_TLS_TPOFF 32 /* Offset in static TLS block. */ +#define R_MN10300_SYM_DIFF 33 /* Adjustment for next reloc as needed + by linker relaxation. */ +#define R_MN10300_ALIGN 34 /* Alignment requirement for linker + relaxation. */ +#define R_MN10300_NUM 35 + + +/* M32R relocs. */ +#define R_M32R_NONE 0 /* No reloc. */ +#define R_M32R_16 1 /* Direct 16 bit. */ +#define R_M32R_32 2 /* Direct 32 bit. */ +#define R_M32R_24 3 /* Direct 24 bit. */ +#define R_M32R_10_PCREL 4 /* PC relative 10 bit shifted. */ +#define R_M32R_18_PCREL 5 /* PC relative 18 bit shifted. */ +#define R_M32R_26_PCREL 6 /* PC relative 26 bit shifted. */ +#define R_M32R_HI16_ULO 7 /* High 16 bit with unsigned low. */ +#define R_M32R_HI16_SLO 8 /* High 16 bit with signed low. */ +#define R_M32R_LO16 9 /* Low 16 bit. */ +#define R_M32R_SDA16 10 /* 16 bit offset in SDA. */ +#define R_M32R_GNU_VTINHERIT 11 +#define R_M32R_GNU_VTENTRY 12 +/* M32R relocs use SHT_RELA. */ +#define R_M32R_16_RELA 33 /* Direct 16 bit. */ +#define R_M32R_32_RELA 34 /* Direct 32 bit. */ +#define R_M32R_24_RELA 35 /* Direct 24 bit. */ +#define R_M32R_10_PCREL_RELA 36 /* PC relative 10 bit shifted. */ +#define R_M32R_18_PCREL_RELA 37 /* PC relative 18 bit shifted. */ +#define R_M32R_26_PCREL_RELA 38 /* PC relative 26 bit shifted. */ +#define R_M32R_HI16_ULO_RELA 39 /* High 16 bit with unsigned low */ +#define R_M32R_HI16_SLO_RELA 40 /* High 16 bit with signed low */ +#define R_M32R_LO16_RELA 41 /* Low 16 bit */ +#define R_M32R_SDA16_RELA 42 /* 16 bit offset in SDA */ +#define R_M32R_RELA_GNU_VTINHERIT 43 +#define R_M32R_RELA_GNU_VTENTRY 44 +#define R_M32R_REL32 45 /* PC relative 32 bit. */ + +#define R_M32R_GOT24 48 /* 24 bit GOT entry */ +#define R_M32R_26_PLTREL 49 /* 26 bit PC relative to PLT shifted */ +#define R_M32R_COPY 50 /* Copy symbol at runtime */ +#define R_M32R_GLOB_DAT 51 /* Create GOT entry */ +#define R_M32R_JMP_SLOT 52 /* Create PLT entry */ +#define R_M32R_RELATIVE 53 /* Adjust by program base */ +#define R_M32R_GOTOFF 54 /* 24 bit offset to GOT */ +#define R_M32R_GOTPC24 55 /* 24 bit PC relative offset to GOT */ +#define R_M32R_GOT16_HI_ULO 56 /* High 16 bit GOT entry with unsigned + low */ +#define R_M32R_GOT16_HI_SLO 57 /* High 16 bit GOT entry with signed + low */ +#define R_M32R_GOT16_LO 58 /* Low 16 bit GOT entry */ +#define R_M32R_GOTPC_HI_ULO 59 /* High 16 bit PC relative offset to + GOT with unsigned low */ +#define R_M32R_GOTPC_HI_SLO 60 /* High 16 bit PC relative offset to + GOT with signed low */ +#define R_M32R_GOTPC_LO 61 /* Low 16 bit PC relative offset to + GOT */ +#define R_M32R_GOTOFF_HI_ULO 62 /* High 16 bit offset to GOT + with unsigned low */ +#define R_M32R_GOTOFF_HI_SLO 63 /* High 16 bit offset to GOT + with signed low */ +#define R_M32R_GOTOFF_LO 64 /* Low 16 bit offset to GOT */ +#define R_M32R_NUM 256 /* Keep this the last entry. */ + +/* MicroBlaze relocations */ +#define R_MICROBLAZE_NONE 0 /* No reloc. */ +#define R_MICROBLAZE_32 1 /* Direct 32 bit. */ +#define R_MICROBLAZE_32_PCREL 2 /* PC relative 32 bit. */ +#define R_MICROBLAZE_64_PCREL 3 /* PC relative 64 bit. */ +#define R_MICROBLAZE_32_PCREL_LO 4 /* Low 16 bits of PCREL32. */ +#define R_MICROBLAZE_64 5 /* Direct 64 bit. */ +#define R_MICROBLAZE_32_LO 6 /* Low 16 bit. */ +#define R_MICROBLAZE_SRO32 7 /* Read-only small data area. */ +#define R_MICROBLAZE_SRW32 8 /* Read-write small data area. */ +#define R_MICROBLAZE_64_NONE 9 /* No reloc. */ +#define R_MICROBLAZE_32_SYM_OP_SYM 10 /* Symbol Op Symbol relocation. */ +#define R_MICROBLAZE_GNU_VTINHERIT 11 /* GNU C++ vtable hierarchy. */ +#define R_MICROBLAZE_GNU_VTENTRY 12 /* GNU C++ vtable member usage. */ +#define R_MICROBLAZE_GOTPC_64 13 /* PC-relative GOT offset. */ +#define R_MICROBLAZE_GOT_64 14 /* GOT entry offset. */ +#define R_MICROBLAZE_PLT_64 15 /* PLT offset (PC-relative). */ +#define R_MICROBLAZE_REL 16 /* Adjust by program base. */ +#define R_MICROBLAZE_JUMP_SLOT 17 /* Create PLT entry. */ +#define R_MICROBLAZE_GLOB_DAT 18 /* Create GOT entry. */ +#define R_MICROBLAZE_GOTOFF_64 19 /* 64 bit offset to GOT. */ +#define R_MICROBLAZE_GOTOFF_32 20 /* 32 bit offset to GOT. */ +#define R_MICROBLAZE_COPY 21 /* Runtime copy. */ +#define R_MICROBLAZE_TLS 22 /* TLS Reloc. */ +#define R_MICROBLAZE_TLSGD 23 /* TLS General Dynamic. */ +#define R_MICROBLAZE_TLSLD 24 /* TLS Local Dynamic. */ +#define R_MICROBLAZE_TLSDTPMOD32 25 /* TLS Module ID. */ +#define R_MICROBLAZE_TLSDTPREL32 26 /* TLS Offset Within TLS Block. */ +#define R_MICROBLAZE_TLSDTPREL64 27 /* TLS Offset Within TLS Block. */ +#define R_MICROBLAZE_TLSGOTTPREL32 28 /* TLS Offset From Thread Pointer. */ +#define R_MICROBLAZE_TLSTPREL32 29 /* TLS Offset From Thread Pointer. */ + +/* Legal values for d_tag (dynamic entry type). */ +#define DT_NIOS2_GP 0x70000002 /* Address of _gp. */ + +/* Nios II relocations. */ +#define R_NIOS2_NONE 0 /* No reloc. */ +#define R_NIOS2_S16 1 /* Direct signed 16 bit. */ +#define R_NIOS2_U16 2 /* Direct unsigned 16 bit. */ +#define R_NIOS2_PCREL16 3 /* PC relative 16 bit. */ +#define R_NIOS2_CALL26 4 /* Direct call. */ +#define R_NIOS2_IMM5 5 /* 5 bit constant expression. */ +#define R_NIOS2_CACHE_OPX 6 /* 5 bit expression, shift 22. */ +#define R_NIOS2_IMM6 7 /* 6 bit constant expression. */ +#define R_NIOS2_IMM8 8 /* 8 bit constant expression. */ +#define R_NIOS2_HI16 9 /* High 16 bit. */ +#define R_NIOS2_LO16 10 /* Low 16 bit. */ +#define R_NIOS2_HIADJ16 11 /* High 16 bit, adjusted. */ +#define R_NIOS2_BFD_RELOC_32 12 /* 32 bit symbol value + addend. */ +#define R_NIOS2_BFD_RELOC_16 13 /* 16 bit symbol value + addend. */ +#define R_NIOS2_BFD_RELOC_8 14 /* 8 bit symbol value + addend. */ +#define R_NIOS2_GPREL 15 /* 16 bit GP pointer offset. */ +#define R_NIOS2_GNU_VTINHERIT 16 /* GNU C++ vtable hierarchy. */ +#define R_NIOS2_GNU_VTENTRY 17 /* GNU C++ vtable member usage. */ +#define R_NIOS2_UJMP 18 /* Unconditional branch. */ +#define R_NIOS2_CJMP 19 /* Conditional branch. */ +#define R_NIOS2_CALLR 20 /* Indirect call through register. */ +#define R_NIOS2_ALIGN 21 /* Alignment requirement for + linker relaxation. */ +#define R_NIOS2_GOT16 22 /* 16 bit GOT entry. */ +#define R_NIOS2_CALL16 23 /* 16 bit GOT entry for function. */ +#define R_NIOS2_GOTOFF_LO 24 /* %lo of offset to GOT pointer. */ +#define R_NIOS2_GOTOFF_HA 25 /* %hiadj of offset to GOT pointer. */ +#define R_NIOS2_PCREL_LO 26 /* %lo of PC relative offset. */ +#define R_NIOS2_PCREL_HA 27 /* %hiadj of PC relative offset. */ +#define R_NIOS2_TLS_GD16 28 /* 16 bit GOT offset for TLS GD. */ +#define R_NIOS2_TLS_LDM16 29 /* 16 bit GOT offset for TLS LDM. */ +#define R_NIOS2_TLS_LDO16 30 /* 16 bit module relative offset. */ +#define R_NIOS2_TLS_IE16 31 /* 16 bit GOT offset for TLS IE. */ +#define R_NIOS2_TLS_LE16 32 /* 16 bit LE TP-relative offset. */ +#define R_NIOS2_TLS_DTPMOD 33 /* Module number. */ +#define R_NIOS2_TLS_DTPREL 34 /* Module-relative offset. */ +#define R_NIOS2_TLS_TPREL 35 /* TP-relative offset. */ +#define R_NIOS2_COPY 36 /* Copy symbol at runtime. */ +#define R_NIOS2_GLOB_DAT 37 /* Create GOT entry. */ +#define R_NIOS2_JUMP_SLOT 38 /* Create PLT entry. */ +#define R_NIOS2_RELATIVE 39 /* Adjust by program base. */ +#define R_NIOS2_GOTOFF 40 /* 16 bit offset to GOT pointer. */ +#define R_NIOS2_CALL26_NOAT 41 /* Direct call in .noat section. */ +#define R_NIOS2_GOT_LO 42 /* %lo() of GOT entry. */ +#define R_NIOS2_GOT_HA 43 /* %hiadj() of GOT entry. */ +#define R_NIOS2_CALL_LO 44 /* %lo() of function GOT entry. */ +#define R_NIOS2_CALL_HA 45 /* %hiadj() of function GOT entry. */ + +/* TILEPro relocations. */ +#define R_TILEPRO_NONE 0 /* No reloc */ +#define R_TILEPRO_32 1 /* Direct 32 bit */ +#define R_TILEPRO_16 2 /* Direct 16 bit */ +#define R_TILEPRO_8 3 /* Direct 8 bit */ +#define R_TILEPRO_32_PCREL 4 /* PC relative 32 bit */ +#define R_TILEPRO_16_PCREL 5 /* PC relative 16 bit */ +#define R_TILEPRO_8_PCREL 6 /* PC relative 8 bit */ +#define R_TILEPRO_LO16 7 /* Low 16 bit */ +#define R_TILEPRO_HI16 8 /* High 16 bit */ +#define R_TILEPRO_HA16 9 /* High 16 bit, adjusted */ +#define R_TILEPRO_COPY 10 /* Copy relocation */ +#define R_TILEPRO_GLOB_DAT 11 /* Create GOT entry */ +#define R_TILEPRO_JMP_SLOT 12 /* Create PLT entry */ +#define R_TILEPRO_RELATIVE 13 /* Adjust by program base */ +#define R_TILEPRO_BROFF_X1 14 /* X1 pipe branch offset */ +#define R_TILEPRO_JOFFLONG_X1 15 /* X1 pipe jump offset */ +#define R_TILEPRO_JOFFLONG_X1_PLT 16 /* X1 pipe jump offset to PLT */ +#define R_TILEPRO_IMM8_X0 17 /* X0 pipe 8-bit */ +#define R_TILEPRO_IMM8_Y0 18 /* Y0 pipe 8-bit */ +#define R_TILEPRO_IMM8_X1 19 /* X1 pipe 8-bit */ +#define R_TILEPRO_IMM8_Y1 20 /* Y1 pipe 8-bit */ +#define R_TILEPRO_MT_IMM15_X1 21 /* X1 pipe mtspr */ +#define R_TILEPRO_MF_IMM15_X1 22 /* X1 pipe mfspr */ +#define R_TILEPRO_IMM16_X0 23 /* X0 pipe 16-bit */ +#define R_TILEPRO_IMM16_X1 24 /* X1 pipe 16-bit */ +#define R_TILEPRO_IMM16_X0_LO 25 /* X0 pipe low 16-bit */ +#define R_TILEPRO_IMM16_X1_LO 26 /* X1 pipe low 16-bit */ +#define R_TILEPRO_IMM16_X0_HI 27 /* X0 pipe high 16-bit */ +#define R_TILEPRO_IMM16_X1_HI 28 /* X1 pipe high 16-bit */ +#define R_TILEPRO_IMM16_X0_HA 29 /* X0 pipe high 16-bit, adjusted */ +#define R_TILEPRO_IMM16_X1_HA 30 /* X1 pipe high 16-bit, adjusted */ +#define R_TILEPRO_IMM16_X0_PCREL 31 /* X0 pipe PC relative 16 bit */ +#define R_TILEPRO_IMM16_X1_PCREL 32 /* X1 pipe PC relative 16 bit */ +#define R_TILEPRO_IMM16_X0_LO_PCREL 33 /* X0 pipe PC relative low 16 bit */ +#define R_TILEPRO_IMM16_X1_LO_PCREL 34 /* X1 pipe PC relative low 16 bit */ +#define R_TILEPRO_IMM16_X0_HI_PCREL 35 /* X0 pipe PC relative high 16 bit */ +#define R_TILEPRO_IMM16_X1_HI_PCREL 36 /* X1 pipe PC relative high 16 bit */ +#define R_TILEPRO_IMM16_X0_HA_PCREL 37 /* X0 pipe PC relative ha() 16 bit */ +#define R_TILEPRO_IMM16_X1_HA_PCREL 38 /* X1 pipe PC relative ha() 16 bit */ +#define R_TILEPRO_IMM16_X0_GOT 39 /* X0 pipe 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X1_GOT 40 /* X1 pipe 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X0_GOT_LO 41 /* X0 pipe low 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X1_GOT_LO 42 /* X1 pipe low 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X0_GOT_HI 43 /* X0 pipe high 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X1_GOT_HI 44 /* X1 pipe high 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X0_GOT_HA 45 /* X0 pipe ha() 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X1_GOT_HA 46 /* X1 pipe ha() 16-bit GOT offset */ +#define R_TILEPRO_MMSTART_X0 47 /* X0 pipe mm "start" */ +#define R_TILEPRO_MMEND_X0 48 /* X0 pipe mm "end" */ +#define R_TILEPRO_MMSTART_X1 49 /* X1 pipe mm "start" */ +#define R_TILEPRO_MMEND_X1 50 /* X1 pipe mm "end" */ +#define R_TILEPRO_SHAMT_X0 51 /* X0 pipe shift amount */ +#define R_TILEPRO_SHAMT_X1 52 /* X1 pipe shift amount */ +#define R_TILEPRO_SHAMT_Y0 53 /* Y0 pipe shift amount */ +#define R_TILEPRO_SHAMT_Y1 54 /* Y1 pipe shift amount */ +#define R_TILEPRO_DEST_IMM8_X1 55 /* X1 pipe destination 8-bit */ +/* Relocs 56-59 are currently not defined. */ +#define R_TILEPRO_TLS_GD_CALL 60 /* "jal" for TLS GD */ +#define R_TILEPRO_IMM8_X0_TLS_GD_ADD 61 /* X0 pipe "addi" for TLS GD */ +#define R_TILEPRO_IMM8_X1_TLS_GD_ADD 62 /* X1 pipe "addi" for TLS GD */ +#define R_TILEPRO_IMM8_Y0_TLS_GD_ADD 63 /* Y0 pipe "addi" for TLS GD */ +#define R_TILEPRO_IMM8_Y1_TLS_GD_ADD 64 /* Y1 pipe "addi" for TLS GD */ +#define R_TILEPRO_TLS_IE_LOAD 65 /* "lw_tls" for TLS IE */ +#define R_TILEPRO_IMM16_X0_TLS_GD 66 /* X0 pipe 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X1_TLS_GD 67 /* X1 pipe 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X0_TLS_GD_LO 68 /* X0 pipe low 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X1_TLS_GD_LO 69 /* X1 pipe low 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X0_TLS_GD_HI 70 /* X0 pipe high 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X1_TLS_GD_HI 71 /* X1 pipe high 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X0_TLS_GD_HA 72 /* X0 pipe ha() 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X1_TLS_GD_HA 73 /* X1 pipe ha() 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X0_TLS_IE 74 /* X0 pipe 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X1_TLS_IE 75 /* X1 pipe 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X0_TLS_IE_LO 76 /* X0 pipe low 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X1_TLS_IE_LO 77 /* X1 pipe low 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X0_TLS_IE_HI 78 /* X0 pipe high 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X1_TLS_IE_HI 79 /* X1 pipe high 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X0_TLS_IE_HA 80 /* X0 pipe ha() 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X1_TLS_IE_HA 81 /* X1 pipe ha() 16-bit TLS IE offset */ +#define R_TILEPRO_TLS_DTPMOD32 82 /* ID of module containing symbol */ +#define R_TILEPRO_TLS_DTPOFF32 83 /* Offset in TLS block */ +#define R_TILEPRO_TLS_TPOFF32 84 /* Offset in static TLS block */ +#define R_TILEPRO_IMM16_X0_TLS_LE 85 /* X0 pipe 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X1_TLS_LE 86 /* X1 pipe 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X0_TLS_LE_LO 87 /* X0 pipe low 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X1_TLS_LE_LO 88 /* X1 pipe low 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X0_TLS_LE_HI 89 /* X0 pipe high 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X1_TLS_LE_HI 90 /* X1 pipe high 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X0_TLS_LE_HA 91 /* X0 pipe ha() 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X1_TLS_LE_HA 92 /* X1 pipe ha() 16-bit TLS LE offset */ + +#define R_TILEPRO_GNU_VTINHERIT 128 /* GNU C++ vtable hierarchy */ +#define R_TILEPRO_GNU_VTENTRY 129 /* GNU C++ vtable member usage */ + +#define R_TILEPRO_NUM 130 + + +/* TILE-Gx relocations. */ +#define R_TILEGX_NONE 0 /* No reloc */ +#define R_TILEGX_64 1 /* Direct 64 bit */ +#define R_TILEGX_32 2 /* Direct 32 bit */ +#define R_TILEGX_16 3 /* Direct 16 bit */ +#define R_TILEGX_8 4 /* Direct 8 bit */ +#define R_TILEGX_64_PCREL 5 /* PC relative 64 bit */ +#define R_TILEGX_32_PCREL 6 /* PC relative 32 bit */ +#define R_TILEGX_16_PCREL 7 /* PC relative 16 bit */ +#define R_TILEGX_8_PCREL 8 /* PC relative 8 bit */ +#define R_TILEGX_HW0 9 /* hword 0 16-bit */ +#define R_TILEGX_HW1 10 /* hword 1 16-bit */ +#define R_TILEGX_HW2 11 /* hword 2 16-bit */ +#define R_TILEGX_HW3 12 /* hword 3 16-bit */ +#define R_TILEGX_HW0_LAST 13 /* last hword 0 16-bit */ +#define R_TILEGX_HW1_LAST 14 /* last hword 1 16-bit */ +#define R_TILEGX_HW2_LAST 15 /* last hword 2 16-bit */ +#define R_TILEGX_COPY 16 /* Copy relocation */ +#define R_TILEGX_GLOB_DAT 17 /* Create GOT entry */ +#define R_TILEGX_JMP_SLOT 18 /* Create PLT entry */ +#define R_TILEGX_RELATIVE 19 /* Adjust by program base */ +#define R_TILEGX_BROFF_X1 20 /* X1 pipe branch offset */ +#define R_TILEGX_JUMPOFF_X1 21 /* X1 pipe jump offset */ +#define R_TILEGX_JUMPOFF_X1_PLT 22 /* X1 pipe jump offset to PLT */ +#define R_TILEGX_IMM8_X0 23 /* X0 pipe 8-bit */ +#define R_TILEGX_IMM8_Y0 24 /* Y0 pipe 8-bit */ +#define R_TILEGX_IMM8_X1 25 /* X1 pipe 8-bit */ +#define R_TILEGX_IMM8_Y1 26 /* Y1 pipe 8-bit */ +#define R_TILEGX_DEST_IMM8_X1 27 /* X1 pipe destination 8-bit */ +#define R_TILEGX_MT_IMM14_X1 28 /* X1 pipe mtspr */ +#define R_TILEGX_MF_IMM14_X1 29 /* X1 pipe mfspr */ +#define R_TILEGX_MMSTART_X0 30 /* X0 pipe mm "start" */ +#define R_TILEGX_MMEND_X0 31 /* X0 pipe mm "end" */ +#define R_TILEGX_SHAMT_X0 32 /* X0 pipe shift amount */ +#define R_TILEGX_SHAMT_X1 33 /* X1 pipe shift amount */ +#define R_TILEGX_SHAMT_Y0 34 /* Y0 pipe shift amount */ +#define R_TILEGX_SHAMT_Y1 35 /* Y1 pipe shift amount */ +#define R_TILEGX_IMM16_X0_HW0 36 /* X0 pipe hword 0 */ +#define R_TILEGX_IMM16_X1_HW0 37 /* X1 pipe hword 0 */ +#define R_TILEGX_IMM16_X0_HW1 38 /* X0 pipe hword 1 */ +#define R_TILEGX_IMM16_X1_HW1 39 /* X1 pipe hword 1 */ +#define R_TILEGX_IMM16_X0_HW2 40 /* X0 pipe hword 2 */ +#define R_TILEGX_IMM16_X1_HW2 41 /* X1 pipe hword 2 */ +#define R_TILEGX_IMM16_X0_HW3 42 /* X0 pipe hword 3 */ +#define R_TILEGX_IMM16_X1_HW3 43 /* X1 pipe hword 3 */ +#define R_TILEGX_IMM16_X0_HW0_LAST 44 /* X0 pipe last hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_LAST 45 /* X1 pipe last hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_LAST 46 /* X0 pipe last hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_LAST 47 /* X1 pipe last hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_LAST 48 /* X0 pipe last hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_LAST 49 /* X1 pipe last hword 2 */ +#define R_TILEGX_IMM16_X0_HW0_PCREL 50 /* X0 pipe PC relative hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_PCREL 51 /* X1 pipe PC relative hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_PCREL 52 /* X0 pipe PC relative hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_PCREL 53 /* X1 pipe PC relative hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_PCREL 54 /* X0 pipe PC relative hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_PCREL 55 /* X1 pipe PC relative hword 2 */ +#define R_TILEGX_IMM16_X0_HW3_PCREL 56 /* X0 pipe PC relative hword 3 */ +#define R_TILEGX_IMM16_X1_HW3_PCREL 57 /* X1 pipe PC relative hword 3 */ +#define R_TILEGX_IMM16_X0_HW0_LAST_PCREL 58 /* X0 pipe PC-rel last hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_LAST_PCREL 59 /* X1 pipe PC-rel last hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_LAST_PCREL 60 /* X0 pipe PC-rel last hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_LAST_PCREL 61 /* X1 pipe PC-rel last hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_LAST_PCREL 62 /* X0 pipe PC-rel last hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_LAST_PCREL 63 /* X1 pipe PC-rel last hword 2 */ +#define R_TILEGX_IMM16_X0_HW0_GOT 64 /* X0 pipe hword 0 GOT offset */ +#define R_TILEGX_IMM16_X1_HW0_GOT 65 /* X1 pipe hword 0 GOT offset */ +#define R_TILEGX_IMM16_X0_HW0_PLT_PCREL 66 /* X0 pipe PC-rel PLT hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_PLT_PCREL 67 /* X1 pipe PC-rel PLT hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_PLT_PCREL 68 /* X0 pipe PC-rel PLT hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_PLT_PCREL 69 /* X1 pipe PC-rel PLT hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_PLT_PCREL 70 /* X0 pipe PC-rel PLT hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_PLT_PCREL 71 /* X1 pipe PC-rel PLT hword 2 */ +#define R_TILEGX_IMM16_X0_HW0_LAST_GOT 72 /* X0 pipe last hword 0 GOT offset */ +#define R_TILEGX_IMM16_X1_HW0_LAST_GOT 73 /* X1 pipe last hword 0 GOT offset */ +#define R_TILEGX_IMM16_X0_HW1_LAST_GOT 74 /* X0 pipe last hword 1 GOT offset */ +#define R_TILEGX_IMM16_X1_HW1_LAST_GOT 75 /* X1 pipe last hword 1 GOT offset */ +#define R_TILEGX_IMM16_X0_HW3_PLT_PCREL 76 /* X0 pipe PC-rel PLT hword 3 */ +#define R_TILEGX_IMM16_X1_HW3_PLT_PCREL 77 /* X1 pipe PC-rel PLT hword 3 */ +#define R_TILEGX_IMM16_X0_HW0_TLS_GD 78 /* X0 pipe hword 0 TLS GD offset */ +#define R_TILEGX_IMM16_X1_HW0_TLS_GD 79 /* X1 pipe hword 0 TLS GD offset */ +#define R_TILEGX_IMM16_X0_HW0_TLS_LE 80 /* X0 pipe hword 0 TLS LE offset */ +#define R_TILEGX_IMM16_X1_HW0_TLS_LE 81 /* X1 pipe hword 0 TLS LE offset */ +#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_LE 82 /* X0 pipe last hword 0 LE off */ +#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_LE 83 /* X1 pipe last hword 0 LE off */ +#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_LE 84 /* X0 pipe last hword 1 LE off */ +#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_LE 85 /* X1 pipe last hword 1 LE off */ +#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD 86 /* X0 pipe last hword 0 GD off */ +#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD 87 /* X1 pipe last hword 0 GD off */ +#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD 88 /* X0 pipe last hword 1 GD off */ +#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD 89 /* X1 pipe last hword 1 GD off */ +/* Relocs 90-91 are currently not defined. */ +#define R_TILEGX_IMM16_X0_HW0_TLS_IE 92 /* X0 pipe hword 0 TLS IE offset */ +#define R_TILEGX_IMM16_X1_HW0_TLS_IE 93 /* X1 pipe hword 0 TLS IE offset */ +#define R_TILEGX_IMM16_X0_HW0_LAST_PLT_PCREL 94 /* X0 pipe PC-rel PLT last hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_LAST_PLT_PCREL 95 /* X1 pipe PC-rel PLT last hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_LAST_PLT_PCREL 96 /* X0 pipe PC-rel PLT last hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_LAST_PLT_PCREL 97 /* X1 pipe PC-rel PLT last hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_LAST_PLT_PCREL 98 /* X0 pipe PC-rel PLT last hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_LAST_PLT_PCREL 99 /* X1 pipe PC-rel PLT last hword 2 */ +#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE 100 /* X0 pipe last hword 0 IE off */ +#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE 101 /* X1 pipe last hword 0 IE off */ +#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE 102 /* X0 pipe last hword 1 IE off */ +#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE 103 /* X1 pipe last hword 1 IE off */ +/* Relocs 104-105 are currently not defined. */ +#define R_TILEGX_TLS_DTPMOD64 106 /* 64-bit ID of symbol's module */ +#define R_TILEGX_TLS_DTPOFF64 107 /* 64-bit offset in TLS block */ +#define R_TILEGX_TLS_TPOFF64 108 /* 64-bit offset in static TLS block */ +#define R_TILEGX_TLS_DTPMOD32 109 /* 32-bit ID of symbol's module */ +#define R_TILEGX_TLS_DTPOFF32 110 /* 32-bit offset in TLS block */ +#define R_TILEGX_TLS_TPOFF32 111 /* 32-bit offset in static TLS block */ +#define R_TILEGX_TLS_GD_CALL 112 /* "jal" for TLS GD */ +#define R_TILEGX_IMM8_X0_TLS_GD_ADD 113 /* X0 pipe "addi" for TLS GD */ +#define R_TILEGX_IMM8_X1_TLS_GD_ADD 114 /* X1 pipe "addi" for TLS GD */ +#define R_TILEGX_IMM8_Y0_TLS_GD_ADD 115 /* Y0 pipe "addi" for TLS GD */ +#define R_TILEGX_IMM8_Y1_TLS_GD_ADD 116 /* Y1 pipe "addi" for TLS GD */ +#define R_TILEGX_TLS_IE_LOAD 117 /* "ld_tls" for TLS IE */ +#define R_TILEGX_IMM8_X0_TLS_ADD 118 /* X0 pipe "addi" for TLS GD/IE */ +#define R_TILEGX_IMM8_X1_TLS_ADD 119 /* X1 pipe "addi" for TLS GD/IE */ +#define R_TILEGX_IMM8_Y0_TLS_ADD 120 /* Y0 pipe "addi" for TLS GD/IE */ +#define R_TILEGX_IMM8_Y1_TLS_ADD 121 /* Y1 pipe "addi" for TLS GD/IE */ + +#define R_TILEGX_GNU_VTINHERIT 128 /* GNU C++ vtable hierarchy */ +#define R_TILEGX_GNU_VTENTRY 129 /* GNU C++ vtable member usage */ + +#define R_TILEGX_NUM 130 + +/* RISC-V ELF Flags */ +#define EF_RISCV_RVC 0x0001 +#define EF_RISCV_FLOAT_ABI 0x0006 +#define EF_RISCV_FLOAT_ABI_SOFT 0x0000 +#define EF_RISCV_FLOAT_ABI_SINGLE 0x0002 +#define EF_RISCV_FLOAT_ABI_DOUBLE 0x0004 +#define EF_RISCV_FLOAT_ABI_QUAD 0x0006 + +/* RISC-V relocations. */ +#define R_RISCV_NONE 0 +#define R_RISCV_32 1 +#define R_RISCV_64 2 +#define R_RISCV_RELATIVE 3 +#define R_RISCV_COPY 4 +#define R_RISCV_JUMP_SLOT 5 +#define R_RISCV_TLS_DTPMOD32 6 +#define R_RISCV_TLS_DTPMOD64 7 +#define R_RISCV_TLS_DTPREL32 8 +#define R_RISCV_TLS_DTPREL64 9 +#define R_RISCV_TLS_TPREL32 10 +#define R_RISCV_TLS_TPREL64 11 +#define R_RISCV_BRANCH 16 +#define R_RISCV_JAL 17 +#define R_RISCV_CALL 18 +#define R_RISCV_CALL_PLT 19 +#define R_RISCV_GOT_HI20 20 +#define R_RISCV_TLS_GOT_HI20 21 +#define R_RISCV_TLS_GD_HI20 22 +#define R_RISCV_PCREL_HI20 23 +#define R_RISCV_PCREL_LO12_I 24 +#define R_RISCV_PCREL_LO12_S 25 +#define R_RISCV_HI20 26 +#define R_RISCV_LO12_I 27 +#define R_RISCV_LO12_S 28 +#define R_RISCV_TPREL_HI20 29 +#define R_RISCV_TPREL_LO12_I 30 +#define R_RISCV_TPREL_LO12_S 31 +#define R_RISCV_TPREL_ADD 32 +#define R_RISCV_ADD8 33 +#define R_RISCV_ADD16 34 +#define R_RISCV_ADD32 35 +#define R_RISCV_ADD64 36 +#define R_RISCV_SUB8 37 +#define R_RISCV_SUB16 38 +#define R_RISCV_SUB32 39 +#define R_RISCV_SUB64 40 +#define R_RISCV_GNU_VTINHERIT 41 +#define R_RISCV_GNU_VTENTRY 42 +#define R_RISCV_ALIGN 43 +#define R_RISCV_RVC_BRANCH 44 +#define R_RISCV_RVC_JUMP 45 +#define R_RISCV_RVC_LUI 46 +#define R_RISCV_GPREL_I 47 +#define R_RISCV_GPREL_S 48 +#define R_RISCV_TPREL_I 49 +#define R_RISCV_TPREL_S 50 +#define R_RISCV_RELAX 51 +#define R_RISCV_SUB6 52 +#define R_RISCV_SET6 53 +#define R_RISCV_SET8 54 +#define R_RISCV_SET16 55 +#define R_RISCV_SET32 56 +#define R_RISCV_32_PCREL 57 + +#define R_RISCV_NUM 58 + +/* BPF specific declarations. */ + +#define R_BPF_NONE 0 /* No reloc */ +#define R_BPF_64_64 1 +#define R_BPF_64_32 10 + +/* Imagination Meta specific relocations. */ + +#define R_METAG_HIADDR16 0 +#define R_METAG_LOADDR16 1 +#define R_METAG_ADDR32 2 /* 32bit absolute address */ +#define R_METAG_NONE 3 /* No reloc */ +#define R_METAG_RELBRANCH 4 +#define R_METAG_GETSETOFF 5 + +/* Backward compatability */ +#define R_METAG_REG32OP1 6 +#define R_METAG_REG32OP2 7 +#define R_METAG_REG32OP3 8 +#define R_METAG_REG16OP1 9 +#define R_METAG_REG16OP2 10 +#define R_METAG_REG16OP3 11 +#define R_METAG_REG32OP4 12 + +#define R_METAG_HIOG 13 +#define R_METAG_LOOG 14 + +#define R_METAG_REL8 15 +#define R_METAG_REL16 16 + +/* GNU */ +#define R_METAG_GNU_VTINHERIT 30 +#define R_METAG_GNU_VTENTRY 31 + +/* PIC relocations */ +#define R_METAG_HI16_GOTOFF 32 +#define R_METAG_LO16_GOTOFF 33 +#define R_METAG_GETSET_GOTOFF 34 +#define R_METAG_GETSET_GOT 35 +#define R_METAG_HI16_GOTPC 36 +#define R_METAG_LO16_GOTPC 37 +#define R_METAG_HI16_PLT 38 +#define R_METAG_LO16_PLT 39 +#define R_METAG_RELBRANCH_PLT 40 +#define R_METAG_GOTOFF 41 +#define R_METAG_PLT 42 +#define R_METAG_COPY 43 +#define R_METAG_JMP_SLOT 44 +#define R_METAG_RELATIVE 45 +#define R_METAG_GLOB_DAT 46 + +/* TLS relocations */ +#define R_METAG_TLS_GD 47 +#define R_METAG_TLS_LDM 48 +#define R_METAG_TLS_LDO_HI16 49 +#define R_METAG_TLS_LDO_LO16 50 +#define R_METAG_TLS_LDO 51 +#define R_METAG_TLS_IE 52 +#define R_METAG_TLS_IENONPIC 53 +#define R_METAG_TLS_IENONPIC_HI16 54 +#define R_METAG_TLS_IENONPIC_LO16 55 +#define R_METAG_TLS_TPOFF 56 +#define R_METAG_TLS_DTPMOD 57 +#define R_METAG_TLS_DTPOFF 58 +#define R_METAG_TLS_LE 59 +#define R_METAG_TLS_LE_HI16 60 +#define R_METAG_TLS_LE_LO16 61 + +/* NDS32 relocations. */ +#define R_NDS32_NONE 0 +#define R_NDS32_32_RELA 20 +#define R_NDS32_COPY 39 +#define R_NDS32_GLOB_DAT 40 +#define R_NDS32_JMP_SLOT 41 +#define R_NDS32_RELATIVE 42 +#define R_NDS32_TLS_TPOFF 102 +#define R_NDS32_TLS_DESC 119 + +#ifdef __cplusplus +} +#endif + +#endif /* elf.h */ diff --git a/shared/sentry/external/crashpad/third_party/googletest/BUILD.gn b/shared/sentry/external/crashpad/third_party/googletest/BUILD.gn new file mode 100644 index 000000000..2cfb50892 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/googletest/BUILD.gn @@ -0,0 +1,375 @@ +# Copyright 2017 The Crashpad Authors. All rights reserved. +# +# 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("../../build/crashpad_buildconfig.gni") +import("../../build/test.gni") + +if (crashpad_is_in_chromium) { + group("googletest") { + testonly = true + public_deps = [ "//testing/gtest" ] + } + group("googlemock") { + testonly = true + public_deps = [ "//testing/gmock" ] + } +} else if (crashpad_is_in_dart || crashpad_is_in_fuchsia) { + group("googletest") { + testonly = true + public_deps = [ "//third_party/googletest:gtest" ] + } + group("googlemock") { + testonly = true + public_deps = [ "//third_party/googletest:gmock" ] + } +} else if (crashpad_is_standalone || crashpad_is_external) { + if (crashpad_is_standalone) { + googletest_dir = "googletest" + mini_chromium_dir = "//third_party/mini_chromium/mini_chromium" + } else if (crashpad_is_external) { + googletest_dir = "../../../../googletest" + mini_chromium_dir = "//../../mini_chromium/mini_chromium" + } + + config("googletest_private_config") { + visibility = [ ":*" ] + include_dirs = [ "$googletest_dir/googletest" ] + defines = [ "GUNIT_NO_GOOGLE3=1" ] + } + + config("googletest_public_config") { + include_dirs = [ "$googletest_dir/googletest/include" ] + } + + static_library("googletest") { + testonly = true + sources = [ + "$googletest_dir/googletest/include/gtest/gtest-assertion-result.h", + "$googletest_dir/googletest/include/gtest/gtest-death-test.h", + "$googletest_dir/googletest/include/gtest/gtest-matchers.h", + "$googletest_dir/googletest/include/gtest/gtest-message.h", + "$googletest_dir/googletest/include/gtest/gtest-param-test.h", + "$googletest_dir/googletest/include/gtest/gtest-printers.h", + "$googletest_dir/googletest/include/gtest/gtest-spi.h", + "$googletest_dir/googletest/include/gtest/gtest-test-part.h", + "$googletest_dir/googletest/include/gtest/gtest-typed-test.h", + "$googletest_dir/googletest/include/gtest/gtest.h", + "$googletest_dir/googletest/include/gtest/gtest_pred_impl.h", + "$googletest_dir/googletest/include/gtest/gtest_prod.h", + "$googletest_dir/googletest/include/gtest/internal/custom/gtest-port.h", + "$googletest_dir/googletest/include/gtest/internal/custom/gtest-printers.h", + "$googletest_dir/googletest/include/gtest/internal/custom/gtest.h", + "$googletest_dir/googletest/include/gtest/internal/gtest-death-test-internal.h", + "$googletest_dir/googletest/include/gtest/internal/gtest-filepath.h", + "$googletest_dir/googletest/include/gtest/internal/gtest-internal.h", + "$googletest_dir/googletest/include/gtest/internal/gtest-param-util.h", + "$googletest_dir/googletest/include/gtest/internal/gtest-port-arch.h", + "$googletest_dir/googletest/include/gtest/internal/gtest-port.h", + "$googletest_dir/googletest/include/gtest/internal/gtest-string.h", + "$googletest_dir/googletest/include/gtest/internal/gtest-type-util.h", + "$googletest_dir/googletest/src/gtest-all.cc", + "$googletest_dir/googletest/src/gtest-assertion-result.cc", + "$googletest_dir/googletest/src/gtest-death-test.cc", + "$googletest_dir/googletest/src/gtest-filepath.cc", + "$googletest_dir/googletest/src/gtest-internal-inl.h", + "$googletest_dir/googletest/src/gtest-matchers.cc", + "$googletest_dir/googletest/src/gtest-port.cc", + "$googletest_dir/googletest/src/gtest-printers.cc", + "$googletest_dir/googletest/src/gtest-test-part.cc", + "$googletest_dir/googletest/src/gtest-typed-test.cc", + "$googletest_dir/googletest/src/gtest.cc", + ] + sources -= [ "$googletest_dir/googletest/src/gtest-all.cc" ] + public_configs = [ ":googletest_public_config" ] + configs -= [ "$mini_chromium_dir/build/config:Wexit_time_destructors" ] + configs += [ ":googletest_private_config" ] + if (crashpad_is_fuchsia) { + deps = [ "../fuchsia" ] + } + } + + static_library("googletest_main") { + # Tests outside of this file should use ../../test:googletest_main instead. + visibility = [ ":*" ] + + testonly = true + sources = [ "$googletest_dir/googletest/src/gtest_main.cc" ] + deps = [ ":googletest" ] + } + + test("gtest_all_test") { + sources = [ + "$googletest_dir/googletest/test/googletest-death-test-test.cc", + "$googletest_dir/googletest/test/googletest-filepath-test.cc", + "$googletest_dir/googletest/test/googletest-message-test.cc", + "$googletest_dir/googletest/test/googletest-options-test.cc", + "$googletest_dir/googletest/test/googletest-port-test.cc", + "$googletest_dir/googletest/test/googletest-test-part-test.cc", + "$googletest_dir/googletest/test/gtest-typed-test2_test.cc", + "$googletest_dir/googletest/test/gtest-typed-test_test.cc", + "$googletest_dir/googletest/test/gtest-typed-test_test.h", + "$googletest_dir/googletest/test/gtest_main_unittest.cc", + "$googletest_dir/googletest/test/gtest_pred_impl_unittest.cc", + "$googletest_dir/googletest/test/gtest_prod_test.cc", + "$googletest_dir/googletest/test/gtest_skip_test.cc", + "$googletest_dir/googletest/test/gtest_unittest.cc", + "$googletest_dir/googletest/test/production.cc", + "$googletest_dir/googletest/test/production.h", + ] + + if (!crashpad_is_win) { + # TODO: Fix error C2015: too many characters in constant. As this error + # cannot be suppressed, removing the test on Windows. See + # https://chromium-review.googlesource.com/c/crashpad/crashpad/+/2855854/2 + # for details. + sources += + [ "$googletest_dir/googletest/test/googletest-printers-test.cc" ] + } + configs -= [ "$mini_chromium_dir/build/config:Wexit_time_destructors" ] + configs += [ ":googletest_private_config" ] + deps = [ + ":googletest", + ":googletest_main", + ] + + if (crashpad_is_win) { + cflags = [ "/wd4702" ] # unreachable code + } + } + + test("gtest_environment_test") { + sources = [ "$googletest_dir/googletest/test/gtest_environment_test.cc" ] + configs += [ ":googletest_private_config" ] + deps = [ ":googletest" ] + } + + test("gtest_listener_test") { + sources = [ "$googletest_dir/googletest/test/googletest-listener-test.cc" ] + deps = [ ":googletest" ] + } + + test("gtest_macro_stack_footprint_test") { + sources = [ "$googletest_dir/googletest/test/gtest_test_macro_stack_footprint_test.cc" ] + deps = [ ":googletest" ] + } + + test("gtest_no_test") { + sources = [ "$googletest_dir/googletest/test/gtest_no_test_unittest.cc" ] + deps = [ ":googletest" ] + } + + test("gtest_param_test") { + sources = [ + "$googletest_dir/googletest/test/googletest-param-test-test.cc", + "$googletest_dir/googletest/test/googletest-param-test-test.h", + "$googletest_dir/googletest/test/googletest-param-test2-test.cc", + ] + configs -= [ "$mini_chromium_dir/build/config:Wexit_time_destructors" ] + configs += [ ":googletest_private_config" ] + deps = [ ":googletest" ] + } + + test("gtest_premature_exit_test") { + sources = [ "$googletest_dir/googletest/test/gtest_premature_exit_test.cc" ] + deps = [ ":googletest" ] + } + + test("gtest_repeat_test") { + sources = [ "$googletest_dir/googletest/test/gtest_repeat_test.cc" ] + configs += [ ":googletest_private_config" ] + deps = [ ":googletest" ] + } + + test("gtest_skip_in_environment_setup_test") { + sources = [ + "$googletest_dir/googletest/test/gtest_skip_in_environment_setup_test.cc", + ] + deps = [ ":googletest" ] + } + + test("gtest_sole_header_test") { + sources = [ "$googletest_dir/googletest/test/gtest_sole_header_test.cc" ] + deps = [ + ":googletest", + ":googletest_main", + ] + } + + test("gtest_stress_test") { + sources = [ "$googletest_dir/googletest/test/gtest_stress_test.cc" ] + configs += [ ":googletest_private_config" ] + deps = [ ":googletest" ] + } + + test("gtest_unittest_api_test") { + sources = [ "$googletest_dir/googletest/test/gtest-unittest-api_test.cc" ] + deps = [ ":googletest" ] + } + + group("googletest_all_tests") { + testonly = true + deps = [ + ":gtest_all_test", + ":gtest_environment_test", + ":gtest_listener_test", + ":gtest_macro_stack_footprint_test", + ":gtest_no_test", + ":gtest_param_test", + ":gtest_premature_exit_test", + ":gtest_repeat_test", + ":gtest_skip_in_environment_setup_test", + ":gtest_sole_header_test", + ":gtest_stress_test", + ":gtest_unittest_api_test", + ] + } + + config("googlemock_private_config") { + visibility = [ ":*" ] + include_dirs = [ "$googletest_dir/googlemock" ] + } + + config("googlemock_public_config") { + include_dirs = [ "$googletest_dir/googlemock/include" ] + } + + static_library("googlemock") { + testonly = true + sources = [ + "$googletest_dir/googlemock/include/gmock/gmock-actions.h", + "$googletest_dir/googlemock/include/gmock/gmock-cardinalities.h", + "$googletest_dir/googlemock/include/gmock/gmock-function-mocker.h", + "$googletest_dir/googlemock/include/gmock/gmock-matchers.h", + "$googletest_dir/googlemock/include/gmock/gmock-more-actions.h", + "$googletest_dir/googlemock/include/gmock/gmock-more-matchers.h", + "$googletest_dir/googlemock/include/gmock/gmock-nice-strict.h", + "$googletest_dir/googlemock/include/gmock/gmock-spec-builders.h", + "$googletest_dir/googlemock/include/gmock/gmock.h", + "$googletest_dir/googlemock/include/gmock/internal/custom/gmock-generated-actions.h", + "$googletest_dir/googlemock/include/gmock/internal/custom/gmock-matchers.h", + "$googletest_dir/googlemock/include/gmock/internal/custom/gmock-port.h", + "$googletest_dir/googlemock/include/gmock/internal/gmock-internal-utils.h", + "$googletest_dir/googlemock/include/gmock/internal/gmock-port.h", + "$googletest_dir/googlemock/include/gmock/internal/gmock-pp.h", + "$googletest_dir/googlemock/src/gmock-all.cc", + "$googletest_dir/googlemock/src/gmock-cardinalities.cc", + "$googletest_dir/googlemock/src/gmock-internal-utils.cc", + "$googletest_dir/googlemock/src/gmock-matchers.cc", + "$googletest_dir/googlemock/src/gmock-spec-builders.cc", + "$googletest_dir/googlemock/src/gmock.cc", + ] + sources -= [ "$googletest_dir/googlemock/src/gmock-all.cc" ] + public_configs = [ ":googlemock_public_config" ] + configs -= [ "$mini_chromium_dir/build/config:Wexit_time_destructors" ] + configs += [ ":googlemock_private_config" ] + deps = [ ":googletest" ] + } + + static_library("googlemock_main") { + # Tests outside of this file should use ../../test:googlemock_main instead. + visibility = [ ":*" ] + testonly = true + sources = [ "$googletest_dir/googlemock/src/gmock_main.cc" ] + deps = [ + ":googlemock", + ":googletest", + ] + } + + test("gmock_all_test") { + sources = [ + "$googletest_dir/googlemock/test/gmock-actions_test.cc", + "$googletest_dir/googlemock/test/gmock-cardinalities_test.cc", + "$googletest_dir/googlemock/test/gmock-function-mocker_test.cc", + "$googletest_dir/googlemock/test/gmock-internal-utils_test.cc", + "$googletest_dir/googlemock/test/gmock-matchers-arithmetic_test.cc", + "$googletest_dir/googlemock/test/gmock-matchers-comparisons_test.cc", + "$googletest_dir/googlemock/test/gmock-matchers-containers_test.cc", + "$googletest_dir/googlemock/test/gmock-matchers-misc_test.cc", + "$googletest_dir/googlemock/test/gmock-matchers_test.h", + "$googletest_dir/googlemock/test/gmock-more-actions_test.cc", + "$googletest_dir/googlemock/test/gmock-nice-strict_test.cc", + "$googletest_dir/googlemock/test/gmock-port_test.cc", + "$googletest_dir/googlemock/test/gmock-pp-string_test.cc", + "$googletest_dir/googlemock/test/gmock-pp_test.cc", + "$googletest_dir/googlemock/test/gmock-spec-builders_test.cc", + "$googletest_dir/googlemock/test/gmock_test.cc", + ] + configs += [ + ":googlemock_private_config", + ":googletest_private_config", + ] + deps = [ + ":googlemock", + ":googlemock_main", + ":googletest", + ] + + if (crashpad_is_clang) { + cflags_cc = [ + # googletest/googlemock/test/gmock-function-mocker_test.cc does not + # always use the override modifier with MOCK_METHOD. + "-Wno-inconsistent-missing-override", + + # For googlemock/test/gmock-matchers-misc_test.cc comparison of + # integers of different signs. + "-Wno-sign-compare", + ] + } + + if (crashpad_is_win) { + cflags = [ + # TODO: Correct SDK in vc\tools\msvc\14.14.26428\include\functional + "/wd4789", # VAR of size N bytes will be overrun + + # For googlemock/test/gmock-matchers-misc_test.cc comparison of + # integers of different signs. + "/wd4018", + ] + } + } + + test("gmock_link_test") { + sources = [ + "$googletest_dir/googlemock/test/gmock_link2_test.cc", + "$googletest_dir/googlemock/test/gmock_link_test.cc", + "$googletest_dir/googlemock/test/gmock_link_test.h", + ] + configs += [ ":googlemock_private_config" ] + deps = [ + ":googlemock", + ":googlemock_main", + ":googletest", + ] + } + + test("gmock_stress_test") { + sources = [ "$googletest_dir/googlemock/test/gmock_stress_test.cc" ] + configs -= [ "$mini_chromium_dir/build/config:Wexit_time_destructors" ] + configs += [ ":googlemock_private_config" ] + deps = [ + ":googlemock", + ":googletest", + ] + } + + group("googlemock_all_tests") { + testonly = true + deps = [ + ":gmock_all_test", + ":gmock_link_test", + ":gmock_stress_test", + ] + } +} diff --git a/shared/sentry/external/crashpad/third_party/googletest/README.crashpad b/shared/sentry/external/crashpad/third_party/googletest/README.crashpad new file mode 100644 index 000000000..9a6ca6c6b --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/googletest/README.crashpad @@ -0,0 +1,15 @@ +Name: Google Test +Short Name: googletest +URL: https://github.com/google/googletest/ +Revision: See DEPS +License: BSD 3-clause +License File: googletest/googletest/LICENSE +Security Critical: no + +Description: +Google Test (Google C++ Testing Framework) is Google’s framework for writing C++ +tests on a variety of platforms. It includes Google Mock, an extension for +writing and using C++ mock classes. + +Local Modifications: +None diff --git a/shared/sentry/external/crashpad/third_party/gyp/README.crashpad b/shared/sentry/external/crashpad/third_party/gyp/README.crashpad new file mode 100644 index 000000000..18eb16491 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/gyp/README.crashpad @@ -0,0 +1,13 @@ +Name: GYP (Generate Your Projects) +Short Name: gyp +URL: https://gyp.gsrc.io/ +Revision: See DEPS +License: BSD 3-clause +License File: gyp/LICENSE +Security Critical: no + +Description: +GYP is used to generate build files. + +Local Modifications: +None diff --git a/shared/sentry/external/crashpad/third_party/linux/README.crashpad b/shared/sentry/external/crashpad/third_party/linux/README.crashpad new file mode 100644 index 000000000..8bf0a9148 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/linux/README.crashpad @@ -0,0 +1,3 @@ +This directory is a placeholder for Fuchsia tools that will be downloaded by +CIPD (https://github.com/luci/luci-go/tree/master/cipd). The CIPD update happens +as part of gclient runhooks. diff --git a/shared/sentry/external/crashpad/third_party/lss/BUILD.gn b/shared/sentry/external/crashpad/third_party/lss/BUILD.gn new file mode 100644 index 000000000..f10482552 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/lss/BUILD.gn @@ -0,0 +1,31 @@ +# Copyright 2019 The Crashpad Authors. All rights reserved. +# +# 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("../../build/crashpad_buildconfig.gni") + +config("lss_config") { + if (crashpad_is_in_chromium) { + defines = [ "CRASHPAD_LSS_SOURCE_EXTERNAL" ] + } else if (crashpad_is_in_fuchsia) { + defines = [ "CRASHPAD_LSS_SOURCE_FUCHSIA" ] + } else { + defines = [ "CRASHPAD_LSS_SOURCE_EMBEDDED" ] + } +} + +source_set("lss") { + public_configs = [ ":lss_config" ] + + sources = [ "lss.h" ] +} diff --git a/shared/sentry/external/crashpad/third_party/lss/README.crashpad b/shared/sentry/external/crashpad/third_party/lss/README.crashpad new file mode 100644 index 000000000..d1ac9913e --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/lss/README.crashpad @@ -0,0 +1,16 @@ +Name: linux-syscall-support +Short Name: lss +URL: https://chromium.googlesource.com/linux-syscall-support/ +Revision: See DEPS +License: BSD 3-clause +License File: lss/linux_syscall_support.h +Security Critical: yes + +Description: +Every so often, projects need to directly embed Linux system calls instead of +calling the implementations in the system runtime library. This project +provides a header file that can be included into your application whenever you +need to make direct system calls. + +Local Modifications: +None. diff --git a/shared/sentry/external/crashpad/third_party/lss/lss.h b/shared/sentry/external/crashpad/third_party/lss/lss.h new file mode 100644 index 000000000..0bdd381c6 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/lss/lss.h @@ -0,0 +1,28 @@ +// Copyright 2019 The Crashpad Authors. All rights reserved. +// +// 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. + +#ifndef CRASHPAD_THIRD_PARTY_LSS_LSS_H_ +#define CRASHPAD_THIRD_PARTY_LSS_LSS_H_ + +#if defined(CRASHPAD_LSS_SOURCE_EXTERNAL) +#include "third_party/lss/linux_syscall_support.h" +#elif defined(CRASHPAD_LSS_SOURCE_EMBEDDED) +#include "third_party/lss/lss/linux_syscall_support.h" +#elif defined(CRASHPAD_LSS_SOURCE_FUCHSIA) +#include "../../../../third_party/linux-syscall-support/linux_syscall_support.h" +#else +#error Unknown lss source +#endif + +#endif // CRASHPAD_THIRD_PARTY_LSS_LSS_H_ diff --git a/shared/sentry/external/crashpad/third_party/lss/lss/.gitignore b/shared/sentry/external/crashpad/third_party/lss/lss/.gitignore new file mode 100644 index 000000000..4032eb05c --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/lss/lss/.gitignore @@ -0,0 +1,3 @@ +*.o + +core diff --git a/shared/sentry/external/crashpad/third_party/lss/lss/DIR_METADATA b/shared/sentry/external/crashpad/third_party/lss/lss/DIR_METADATA new file mode 100644 index 000000000..eccfd35fa --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/lss/lss/DIR_METADATA @@ -0,0 +1,12 @@ +# Metadata information for this directory. +# +# For more information on DIR_METADATA files, see: +# https://source.chromium.org/chromium/infra/infra/+/HEAD:go/src/infra/tools/dirmd/README.md +# +# For the schema of this file, see Metadata message: +# https://source.chromium.org/chromium/infra/infra/+/HEAD:go/src/infra/tools/dirmd/proto/dir_metadata.proto + +os: LINUX +monorail { + project: "linux-syscall-support" +} diff --git a/shared/sentry/external/crashpad/third_party/lss/lss/LICENSE b/shared/sentry/external/crashpad/third_party/lss/lss/LICENSE new file mode 100644 index 000000000..58ab3fba9 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/lss/lss/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2005-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. diff --git a/shared/sentry/external/crashpad/third_party/lss/lss/OWNERS b/shared/sentry/external/crashpad/third_party/lss/lss/OWNERS new file mode 100644 index 000000000..29b02fa11 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/lss/lss/OWNERS @@ -0,0 +1,2 @@ +mseaborn@chromium.org +vapier@chromium.org diff --git a/shared/sentry/external/crashpad/third_party/lss/lss/README.md b/shared/sentry/external/crashpad/third_party/lss/lss/README.md new file mode 100644 index 000000000..b0cd12977 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/lss/lss/README.md @@ -0,0 +1,137 @@ +# Linux Syscall Support (LSS) + +Every so often, projects need to directly embed Linux system calls instead of +calling the implementations in the system runtime library. + +This project provides a header file that can be included into your application +whenever you need to make direct system calls. + +The goal is to provide an API that generally mirrors the standard C library +while still making direct syscalls. We try to hide some of the differences +between arches when reasonably feasible. e.g. Newer architectures no longer +provide an `open` syscall, but do provide `openat`. We will still expose a +`sys_open` helper by default that calls into `openat` instead. + +We explicitly do not expose the raw syscall ABI including all of its historical +warts to the user. We want people to be able to easily make a syscall, not have +to worry that on some arches size args are swapped or they are shifted. + +Please be sure to review the Caveats section below however. + +## How to include linux\_syscall\_support.h in your project + +You can either copy the file into your project, or preferably, you can set up +Git submodules to automatically pull from our source repository. + +## Supported targets + +The following architectures/ABIs have been tested (at some point) and should +generally work. If you don't see your combo listed here, please double check +the header itself as this list might be out of date. + +* x86 32-bit (i.e. i386, i486, i586, i686, Intel, AMD, etc...) +* [x86_64 64-bit](https://en.wikipedia.org/wiki/X86-64) (i.e. x86-64, amd64, etc...) +* [x32 32-bit](https://sites.google.com/site/x32abi/) +* [ARM 32-bit](https://en.wikipedia.org/wiki/ARM_architecture) OABI +* [ARM 32-bit](https://en.wikipedia.org/wiki/ARM_architecture) EABI (i.e. armv6, armv7, etc...) +* AARCH64 64-bit (i.e. arm64, armv8, etc...) +* PowerPC 32-bit (i.e. ppc, ppc32, etc...) +* MIPS 32-bit o32 ABI +* MIPS 32-bit n32 ABI +* MIPS 64-bit n64 ABI + +## API + +By default, you can just add a `sys_` prefix to any function you want to call. +So if you want to call `open(...)`, use `sys_open(...)` instead. + +### Knobs + +The linux\_syscall\_support.h header provides many knobs for you to control +the exported API. These are all documented in the top of the header in a big +comment block, so refer to that instead. + +## Caveats + +### ABI differences + +Some functions that the standard C library exposes use a different ABI than +what the Linux kernel uses. Care must be taken when making syscalls directly +that you use the right structure and flags. e.g. Most C libraries define a +`struct stat` (commonly in `sys/stat.h` or `bits/stat.h`) that is different +from the `struct stat` the kernel uses (commonly in `asm/stat.h`). If you use +the wrong structure layout, then you can see errors like memory corruption or +weird/shifted values. If you plan on making syscalls directly, you should +focus on headers that are available under the `linux/` and `asm/` namespaces. + +Note: LSS provides structs for most of these cases. For `sys_stat()`, it +provides `struct kernel_stat` for you to use. + +### Transparent backwards compatibility with older kernels + +While some C libraries (notably, glibc) take care to fallback to older syscalls +when running on older kernels, there is no such support in LSS. If you plan on +trying to run on older kernels, you will need to handle errors yourself (e.g. +`ENOSYS` when using a too new syscall). + +Remember that this can happen with new flag bits too. e.g. The `O_CLOEXEC` +flag was added to many syscalls, but if you try to run use it on older kernels, +it will fail with `EINVAL`. In that case, you must handle the fallback logic +yourself. + +### Variable arguments (varargs) + +We do not support vararg type functions. e.g. While the standard `open()` +function can accept 2 or 3 arguments (with the mode field being optional), +the `sys_open()` function always requires 3 arguments. + +## Bug reports & feature requests + +If you wish to report a problem or request a feature, please file them in our +[bug tracker](https://bugs.chromium.org/p/linux-syscall-support/issues/). + +Please do not post patches to the tracker. Instead, see below for how to send +patches to us directly. + +While we welcome feature requests, please keep in mind that it is unlikely that +anyone will find time to implement them for you. Sending patches is strongly +preferred and will often move things much faster. + +## Projects that use LSS + +* [Chromium](https://www.chromium.org/) +* [Breakpad](https://chromium.googlesource.com/breakpad/breakpad) +* [Native Client](https://developer.chrome.com/native-client), in nacl\_bootstrap.c + +## How to get an LSS change committed + +### Review + +You get your change reviewed, you can upload it to +[Gerrit](https://chromium-review.googlesource.com/q/project:linux-syscall-support+status:open) +using `git cl upload` from +[Chromium's depot-tools](https://commondatastorage.googleapis.com/chrome-infra-docs/flat/depot_tools/docs/html/depot_tools_tutorial.html). + +### Testing + +Tests are found in the [tests/](./tests/) subdirectory. It does not (yet) offer +100% coverage, but should grow over time. + +New commits that update/change/add syscall wrappers should include tests for +them too. Consult the [test documentation](./tests/README.md) for more details. + +To run, just run `make` inside the tests directory. It will compile & execute +the tests locally. + +There is some limited cross-compile coverage available if you run `make cross`. +It only compiles things (does not execute at all). + +### Rolling into Chromium + +If you commit a change to LSS, please also commit a Chromium change to update +`lss_revision` in +[Chromium's DEPS](https://chromium.googlesource.com/chromium/src/+/HEAD/DEPS) +file. + +This ensures that the LSS change gets tested, so that people who commit later +LSS changes don't run into problems with updating `lss_revision`. diff --git a/shared/sentry/external/crashpad/third_party/lss/lss/codereview.settings b/shared/sentry/external/crashpad/third_party/lss/lss/codereview.settings new file mode 100644 index 000000000..b5082e8d3 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/lss/lss/codereview.settings @@ -0,0 +1,5 @@ +# This file is used by git cl to get repository specific information. +CC_LIST: chromium-reviews@chromium.org,mseaborn@chromium.org +CODE_REVIEW_SERVER: codereview.chromium.org +GERRIT_HOST: True +VIEW_VC: https://chromium.googlesource.com/linux-syscall-support/+/ diff --git a/shared/sentry/external/crashpad/third_party/lss/lss/linux_syscall_support.h b/shared/sentry/external/crashpad/third_party/lss/lss/linux_syscall_support.h new file mode 100644 index 000000000..d3791cd4e --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/lss/lss/linux_syscall_support.h @@ -0,0 +1,4867 @@ +/* Copyright (c) 2005-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: Markus Gutschke + */ + +/* This file includes Linux-specific support functions common to the + * coredumper and the thread lister; primarily, this is a collection + * of direct system calls, and a couple of symbols missing from + * standard header files. + * There are a few options that the including file can set to control + * the behavior of this file: + * + * SYS_CPLUSPLUS: + * The entire header file will normally be wrapped in 'extern "C" { }", + * making it suitable for compilation as both C and C++ source. If you + * do not want to do this, you can set the SYS_CPLUSPLUS macro to inhibit + * the wrapping. N.B. doing so will suppress inclusion of all prerequisite + * system header files, too. It is the caller's responsibility to provide + * the necessary definitions. + * + * SYS_ERRNO: + * All system calls will update "errno" unless overriden by setting the + * SYS_ERRNO macro prior to including this file. SYS_ERRNO should be + * an l-value. + * + * SYS_INLINE: + * New symbols will be defined "static inline", unless overridden by + * the SYS_INLINE macro. + * + * SYS_LINUX_SYSCALL_SUPPORT_H + * This macro is used to avoid multiple inclusions of this header file. + * If you need to include this file more than once, make sure to + * unset SYS_LINUX_SYSCALL_SUPPORT_H before each inclusion. + * + * SYS_PREFIX: + * New system calls will have a prefix of "sys_" unless overridden by + * the SYS_PREFIX macro. Valid values for this macro are [0..9] which + * results in prefixes "sys[0..9]_". It is also possible to set this + * macro to -1, which avoids all prefixes. + * + * SYS_SYSCALL_ENTRYPOINT: + * Some applications (such as sandboxes that filter system calls), need + * to be able to run custom-code each time a system call is made. If this + * macro is defined, it expands to the name of a "common" symbol. If + * this symbol is assigned a non-NULL pointer value, it is used as the + * address of the system call entrypoint. + * A pointer to this symbol can be obtained by calling + * get_syscall_entrypoint() + * + * This file defines a few internal symbols that all start with "LSS_". + * Do not access these symbols from outside this file. They are not part + * of the supported API. + */ +#ifndef SYS_LINUX_SYSCALL_SUPPORT_H +#define SYS_LINUX_SYSCALL_SUPPORT_H + +/* We currently only support x86-32, x86-64, ARM, MIPS, PPC, s390 and s390x + * on Linux. + * Porting to other related platforms should not be difficult. + */ +#if (defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) || \ + defined(__mips__) || defined(__PPC__) || defined(__ARM_EABI__) || \ + defined(__aarch64__) || defined(__s390__) || defined(__e2k__)) \ + && (defined(__linux) || defined(__ANDROID__)) + +#ifndef SYS_CPLUSPLUS +#ifdef __cplusplus +/* Some system header files in older versions of gcc neglect to properly + * handle being included from C++. As it appears to be harmless to have + * multiple nested 'extern "C"' blocks, just add another one here. + */ +extern "C" { +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __mips__ +/* Include definitions of the ABI currently in use. */ +#ifdef __ANDROID__ +/* Android doesn't have sgidefs.h, but does have asm/sgidefs.h, + * which has the definitions we need. + */ +#include +#else +#include +#endif +#endif +#endif + +/* Some libcs, for example Android NDK and musl, #define these + * macros as aliases to their non-64 counterparts. To avoid naming + * conflict, remove them. + * + * These are restored by the corresponding #pragma pop_macro near + * the end of this file. + */ +#pragma push_macro("stat64") +#pragma push_macro("fstat64") +#pragma push_macro("lstat64") +#pragma push_macro("pread64") +#pragma push_macro("pwrite64") +#pragma push_macro("getdents64") +#undef stat64 +#undef fstat64 +#undef lstat64 +#undef pread64 +#undef pwrite64 +#undef getdents64 + +#if defined(__ANDROID__) && defined(__x86_64__) +// A number of x86_64 syscalls are blocked by seccomp on recent Android; +// undefine them so that modern alternatives will be used instead where +// possible. +// The alternative syscalls have been sanity checked against linux-3.4+; +// older versions might not work. +# undef __NR_getdents +# undef __NR_dup2 +# undef __NR_fork +# undef __NR_getpgrp +# undef __NR_open +# undef __NR_poll +# undef __NR_readlink +# undef __NR_stat +# undef __NR_unlink +# undef __NR_pipe +#endif + +#if defined(__ANDROID__) +// waitpid is blocked by seccomp on all architectures on recent Android. +# undef __NR_waitpid +#endif + +/* As glibc often provides subtly incompatible data structures (and implicit + * wrapper functions that convert them), we provide our own kernel data + * structures for use by the system calls. + * These structures have been developed by using Linux 2.6.23 headers for + * reference. Note though, we do not care about exact API compatibility + * with the kernel, and in fact the kernel often does not have a single + * API that works across architectures. Instead, we try to mimic the glibc + * API where reasonable, and only guarantee ABI compatibility with the + * kernel headers. + * Most notably, here are a few changes that were made to the structures + * defined by kernel headers: + * + * - we only define structures, but not symbolic names for kernel data + * types. For the latter, we directly use the native C datatype + * (i.e. "unsigned" instead of "mode_t"). + * - in a few cases, it is possible to define identical structures for + * both 32bit (e.g. i386) and 64bit (e.g. x86-64) platforms by + * standardizing on the 64bit version of the data types. In particular, + * this means that we use "unsigned" where the 32bit headers say + * "unsigned long". + * - overall, we try to minimize the number of cases where we need to + * conditionally define different structures. + * - the "struct kernel_sigaction" class of structures have been + * modified to more closely mimic glibc's API by introducing an + * anonymous union for the function pointer. + * - a small number of field names had to have an underscore appended to + * them, because glibc defines a global macro by the same name. + */ + +/* include/linux/dirent.h */ +struct kernel_dirent64 { + unsigned long long d_ino; + long long d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[256]; +}; + +/* include/linux/dirent.h */ +#if !defined(__NR_getdents) +// when getdents is not available, getdents64 is used for both. +#define kernel_dirent kernel_dirent64 +#else +struct kernel_dirent { + long d_ino; + long d_off; + unsigned short d_reclen; + char d_name[256]; +}; +#endif + +/* include/linux/uio.h */ +struct kernel_iovec { + void *iov_base; + unsigned long iov_len; +}; + +/* include/linux/socket.h */ +struct kernel_msghdr { + void *msg_name; + int msg_namelen; + struct kernel_iovec*msg_iov; + unsigned long msg_iovlen; + void *msg_control; + unsigned long msg_controllen; + unsigned msg_flags; +}; + +/* include/asm-generic/poll.h */ +struct kernel_pollfd { + int fd; + short events; + short revents; +}; + +/* include/linux/resource.h */ +struct kernel_rlimit { + unsigned long rlim_cur; + unsigned long rlim_max; +}; + +/* include/linux/time.h */ +struct kernel_timespec { + long tv_sec; + long tv_nsec; +}; + +/* include/linux/time.h */ +struct kernel_timeval { + long tv_sec; + long tv_usec; +}; + +/* include/linux/resource.h */ +struct kernel_rusage { + struct kernel_timeval ru_utime; + struct kernel_timeval ru_stime; + long ru_maxrss; + long ru_ixrss; + long ru_idrss; + long ru_isrss; + long ru_minflt; + long ru_majflt; + long ru_nswap; + long ru_inblock; + long ru_oublock; + long ru_msgsnd; + long ru_msgrcv; + long ru_nsignals; + long ru_nvcsw; + long ru_nivcsw; +}; + +#if defined(__i386__) || defined(__ARM_EABI__) || defined(__ARM_ARCH_3__) \ + || defined(__PPC__) || (defined(__s390__) && !defined(__s390x__)) \ + || defined(__e2k__) + +/* include/asm-{arm,i386,mips,ppc}/signal.h */ +struct kernel_old_sigaction { + union { + void (*sa_handler_)(int); + void (*sa_sigaction_)(int, siginfo_t *, void *); + }; + unsigned long sa_mask; + unsigned long sa_flags; + void (*sa_restorer)(void); +} __attribute__((packed,aligned(4))); +#elif (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) + #define kernel_old_sigaction kernel_sigaction +#elif defined(__aarch64__) + // No kernel_old_sigaction defined for arm64. +#endif + +/* Some kernel functions (e.g. sigaction() in 2.6.23) require that the + * exactly match the size of the signal set, even though the API was + * intended to be extensible. We define our own KERNEL_NSIG to deal with + * this. + * Please note that glibc provides signals [1.._NSIG-1], whereas the + * kernel (and this header) provides the range [1..KERNEL_NSIG]. The + * actual number of signals is obviously the same, but the constants + * differ by one. + */ +#ifdef __mips__ +#define KERNEL_NSIG 128 +#else +#define KERNEL_NSIG 64 +#endif + +/* include/asm-{arm,aarch64,i386,mips,x86_64}/signal.h */ +struct kernel_sigset_t { + unsigned long sig[(KERNEL_NSIG + 8*sizeof(unsigned long) - 1)/ + (8*sizeof(unsigned long))]; +}; + +/* include/asm-{arm,i386,mips,x86_64,ppc}/signal.h */ +struct kernel_sigaction { +#ifdef __mips__ + unsigned long sa_flags; + union { + void (*sa_handler_)(int); + void (*sa_sigaction_)(int, siginfo_t *, void *); + }; + struct kernel_sigset_t sa_mask; +#else + union { + void (*sa_handler_)(int); + void (*sa_sigaction_)(int, siginfo_t *, void *); + }; + unsigned long sa_flags; + void (*sa_restorer)(void); + struct kernel_sigset_t sa_mask; +#endif +}; + +/* include/linux/socket.h */ +struct kernel_sockaddr { + unsigned short sa_family; + char sa_data[14]; +}; + +/* include/asm-{arm,aarch64,i386,mips,ppc,s390}/stat.h */ +#ifdef __mips__ +#if _MIPS_SIM == _MIPS_SIM_ABI64 +struct kernel_stat { +#else +struct kernel_stat64 { +#endif + unsigned st_dev; + unsigned __pad0[3]; + unsigned long long st_ino; + unsigned st_mode; + unsigned st_nlink; + unsigned st_uid; + unsigned st_gid; + unsigned st_rdev; + unsigned __pad1[3]; + long long st_size; + unsigned st_atime_; + unsigned st_atime_nsec_; + unsigned st_mtime_; + unsigned st_mtime_nsec_; + unsigned st_ctime_; + unsigned st_ctime_nsec_; + unsigned st_blksize; + unsigned __pad2; + unsigned long long st_blocks; +}; +#elif defined __PPC__ +struct kernel_stat64 { + unsigned long long st_dev; + unsigned long long st_ino; + unsigned st_mode; + unsigned st_nlink; + unsigned st_uid; + unsigned st_gid; + unsigned long long st_rdev; + unsigned short int __pad2; + long long st_size; + long st_blksize; + long long st_blocks; + long st_atime_; + unsigned long st_atime_nsec_; + long st_mtime_; + unsigned long st_mtime_nsec_; + long st_ctime_; + unsigned long st_ctime_nsec_; + unsigned long __unused4; + unsigned long __unused5; +}; +#elif defined(__e2k__) +struct kernel_stat64 { + unsigned long long st_dev; + unsigned long long st_ino; + unsigned int st_mode; + unsigned int st_nlink; + unsigned int st_uid; + unsigned int st_gid; + unsigned long long st_rdev; + long long st_size; + int st_blksize; + int __pad2; + unsigned long long st_blocks; + int st_atime_; + unsigned int st_atime_nsec_; + int st_mtime_; + unsigned int st_mtime_nsec_; + int st_ctime_; + unsigned int st_ctime_nsec_; + unsigned int __unused4; + unsigned int __unused5; +}; +#else +struct kernel_stat64 { + unsigned long long st_dev; + unsigned char __pad0[4]; + unsigned __st_ino; + unsigned st_mode; + unsigned st_nlink; + unsigned st_uid; + unsigned st_gid; + unsigned long long st_rdev; + unsigned char __pad3[4]; + long long st_size; + unsigned st_blksize; + unsigned long long st_blocks; + unsigned st_atime_; + unsigned st_atime_nsec_; + unsigned st_mtime_; + unsigned st_mtime_nsec_; + unsigned st_ctime_; + unsigned st_ctime_nsec_; + unsigned long long st_ino; +}; +#endif + +/* include/asm-{arm,aarch64,i386,mips,x86_64,ppc,s390}/stat.h */ +#if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) +struct kernel_stat { + /* The kernel headers suggest that st_dev and st_rdev should be 32bit + * quantities encoding 12bit major and 20bit minor numbers in an interleaved + * format. In reality, we do not see useful data in the top bits. So, + * we'll leave the padding in here, until we find a better solution. + */ + unsigned short st_dev; + short pad1; + unsigned st_ino; + unsigned short st_mode; + unsigned short st_nlink; + unsigned short st_uid; + unsigned short st_gid; + unsigned short st_rdev; + short pad2; + unsigned st_size; + unsigned st_blksize; + unsigned st_blocks; + unsigned st_atime_; + unsigned st_atime_nsec_; + unsigned st_mtime_; + unsigned st_mtime_nsec_; + unsigned st_ctime_; + unsigned st_ctime_nsec_; + unsigned __unused4; + unsigned __unused5; +}; +#elif defined(__x86_64__) +struct kernel_stat { + uint64_t st_dev; + uint64_t st_ino; + uint64_t st_nlink; + unsigned st_mode; + unsigned st_uid; + unsigned st_gid; + unsigned __pad0; + uint64_t st_rdev; + int64_t st_size; + int64_t st_blksize; + int64_t st_blocks; + uint64_t st_atime_; + uint64_t st_atime_nsec_; + uint64_t st_mtime_; + uint64_t st_mtime_nsec_; + uint64_t st_ctime_; + uint64_t st_ctime_nsec_; + int64_t __unused4[3]; +}; +#elif defined(__PPC__) +struct kernel_stat { + unsigned st_dev; + unsigned long st_ino; // ino_t + unsigned long st_mode; // mode_t + unsigned short st_nlink; // nlink_t + unsigned st_uid; // uid_t + unsigned st_gid; // gid_t + unsigned st_rdev; + long st_size; // off_t + unsigned long st_blksize; + unsigned long st_blocks; + unsigned long st_atime_; + unsigned long st_atime_nsec_; + unsigned long st_mtime_; + unsigned long st_mtime_nsec_; + unsigned long st_ctime_; + unsigned long st_ctime_nsec_; + unsigned long __unused4; + unsigned long __unused5; +}; +#elif (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI64) +struct kernel_stat { + unsigned st_dev; + int st_pad1[3]; + unsigned st_ino; + unsigned st_mode; + unsigned st_nlink; + unsigned st_uid; + unsigned st_gid; + unsigned st_rdev; + int st_pad2[2]; + long st_size; + int st_pad3; + long st_atime_; + long st_atime_nsec_; + long st_mtime_; + long st_mtime_nsec_; + long st_ctime_; + long st_ctime_nsec_; + int st_blksize; + int st_blocks; + int st_pad4[14]; +}; +#elif defined(__aarch64__) +struct kernel_stat { + unsigned long st_dev; + unsigned long st_ino; + unsigned int st_mode; + unsigned int st_nlink; + unsigned int st_uid; + unsigned int st_gid; + unsigned long st_rdev; + unsigned long __pad1; + long st_size; + int st_blksize; + int __pad2; + long st_blocks; + long st_atime_; + unsigned long st_atime_nsec_; + long st_mtime_; + unsigned long st_mtime_nsec_; + long st_ctime_; + unsigned long st_ctime_nsec_; + unsigned int __unused4; + unsigned int __unused5; +}; +#elif defined(__s390x__) +struct kernel_stat { + unsigned long st_dev; + unsigned long st_ino; + unsigned long st_nlink; + unsigned int st_mode; + unsigned int st_uid; + unsigned int st_gid; + unsigned int __pad1; + unsigned long st_rdev; + unsigned long st_size; + unsigned long st_atime_; + unsigned long st_atime_nsec_; + unsigned long st_mtime_; + unsigned long st_mtime_nsec_; + unsigned long st_ctime_; + unsigned long st_ctime_nsec_; + unsigned long st_blksize; + long st_blocks; + unsigned long __unused[3]; +}; +#elif defined(__s390__) +struct kernel_stat { + unsigned short st_dev; + unsigned short __pad1; + unsigned long st_ino; + unsigned short st_mode; + unsigned short st_nlink; + unsigned short st_uid; + unsigned short st_gid; + unsigned short st_rdev; + unsigned short __pad2; + unsigned long st_size; + unsigned long st_blksize; + unsigned long st_blocks; + unsigned long st_atime_; + unsigned long st_atime_nsec_; + unsigned long st_mtime_; + unsigned long st_mtime_nsec_; + unsigned long st_ctime_; + unsigned long st_ctime_nsec_; + unsigned long __unused4; + unsigned long __unused5; +}; +#elif defined(__e2k__) +struct kernel_stat { + unsigned long st_dev; + unsigned long st_ino; + unsigned int st_mode; + unsigned long st_nlink; + unsigned int st_uid; + unsigned int st_gid; + unsigned long st_rdev; + unsigned long st_size; + unsigned long st_blksize; + unsigned long st_blocks; + unsigned long st_atime_; + unsigned long st_atime_nsec_; + unsigned long st_mtime_; + unsigned long st_mtime_nsec_; + unsigned long st_ctime_; + unsigned long st_ctime_nsec_; +}; +#endif + +/* include/asm-{arm,aarch64,i386,mips,x86_64,ppc,s390}/statfs.h */ +#ifdef __mips__ +#if _MIPS_SIM != _MIPS_SIM_ABI64 +struct kernel_statfs64 { + unsigned long f_type; + unsigned long f_bsize; + unsigned long f_frsize; + unsigned long __pad; + unsigned long long f_blocks; + unsigned long long f_bfree; + unsigned long long f_files; + unsigned long long f_ffree; + unsigned long long f_bavail; + struct { int val[2]; } f_fsid; + unsigned long f_namelen; + unsigned long f_spare[6]; +}; +#endif +#elif defined(__s390__) +/* See also arch/s390/include/asm/compat.h */ +struct kernel_statfs64 { + unsigned int f_type; + unsigned int f_bsize; + unsigned long long f_blocks; + unsigned long long f_bfree; + unsigned long long f_bavail; + unsigned long long f_files; + unsigned long long f_ffree; + struct { int val[2]; } f_fsid; + unsigned int f_namelen; + unsigned int f_frsize; + unsigned int f_flags; + unsigned int f_spare[4]; +}; +#elif !defined(__x86_64__) +struct kernel_statfs64 { + unsigned long f_type; + unsigned long f_bsize; + unsigned long long f_blocks; + unsigned long long f_bfree; + unsigned long long f_bavail; + unsigned long long f_files; + unsigned long long f_ffree; + struct { int val[2]; } f_fsid; + unsigned long f_namelen; + unsigned long f_frsize; + unsigned long f_spare[5]; +}; +#endif + +/* include/asm-{arm,i386,mips,x86_64,ppc,generic,s390}/statfs.h */ +#ifdef __mips__ +struct kernel_statfs { + long f_type; + long f_bsize; + long f_frsize; + long f_blocks; + long f_bfree; + long f_files; + long f_ffree; + long f_bavail; + struct { int val[2]; } f_fsid; + long f_namelen; + long f_spare[6]; +}; +#elif defined(__x86_64__) +struct kernel_statfs { + /* x86_64 actually defines all these fields as signed, whereas all other */ + /* platforms define them as unsigned. Leaving them at unsigned should not */ + /* cause any problems. Make sure these are 64-bit even on x32. */ + uint64_t f_type; + uint64_t f_bsize; + uint64_t f_blocks; + uint64_t f_bfree; + uint64_t f_bavail; + uint64_t f_files; + uint64_t f_ffree; + struct { int val[2]; } f_fsid; + uint64_t f_namelen; + uint64_t f_frsize; + uint64_t f_spare[5]; +}; +#elif defined(__s390__) +struct kernel_statfs { + unsigned int f_type; + unsigned int f_bsize; + unsigned long f_blocks; + unsigned long f_bfree; + unsigned long f_bavail; + unsigned long f_files; + unsigned long f_ffree; + struct { int val[2]; } f_fsid; + unsigned int f_namelen; + unsigned int f_frsize; + unsigned int f_flags; + unsigned int f_spare[4]; +}; +#else +struct kernel_statfs { + unsigned long f_type; + unsigned long f_bsize; + unsigned long f_blocks; + unsigned long f_bfree; + unsigned long f_bavail; + unsigned long f_files; + unsigned long f_ffree; + struct { int val[2]; } f_fsid; + unsigned long f_namelen; + unsigned long f_frsize; + unsigned long f_spare[5]; +}; +#endif + + +/* Definitions missing from the standard header files */ +#ifndef O_DIRECTORY +#if defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) || defined(__aarch64__) +#define O_DIRECTORY 0040000 +#else +#define O_DIRECTORY 0200000 +#endif +#endif +#ifndef NT_PRXFPREG +#define NT_PRXFPREG 0x46e62b7f +#endif +#ifndef PTRACE_GETFPXREGS +#define PTRACE_GETFPXREGS ((enum __ptrace_request)18) +#endif +#ifndef PR_GET_DUMPABLE +#define PR_GET_DUMPABLE 3 +#endif +#ifndef PR_SET_DUMPABLE +#define PR_SET_DUMPABLE 4 +#endif +#ifndef PR_GET_SECCOMP +#define PR_GET_SECCOMP 21 +#endif +#ifndef PR_SET_SECCOMP +#define PR_SET_SECCOMP 22 +#endif +#ifndef AT_FDCWD +#define AT_FDCWD (-100) +#endif +#ifndef AT_SYMLINK_NOFOLLOW +#define AT_SYMLINK_NOFOLLOW 0x100 +#endif +#ifndef AT_REMOVEDIR +#define AT_REMOVEDIR 0x200 +#endif +#ifndef MREMAP_FIXED +#define MREMAP_FIXED 2 +#endif +#ifndef SA_RESTORER +#define SA_RESTORER 0x04000000 +#endif +#ifndef CPUCLOCK_PROF +#define CPUCLOCK_PROF 0 +#endif +#ifndef CPUCLOCK_VIRT +#define CPUCLOCK_VIRT 1 +#endif +#ifndef CPUCLOCK_SCHED +#define CPUCLOCK_SCHED 2 +#endif +#ifndef CPUCLOCK_PERTHREAD_MASK +#define CPUCLOCK_PERTHREAD_MASK 4 +#endif +#ifndef MAKE_PROCESS_CPUCLOCK +#define MAKE_PROCESS_CPUCLOCK(pid, clock) \ + ((int)(~(unsigned)(pid) << 3) | (int)(clock)) +#endif +#ifndef MAKE_THREAD_CPUCLOCK +#define MAKE_THREAD_CPUCLOCK(tid, clock) \ + ((int)(~(unsigned)(tid) << 3) | \ + (int)((clock) | CPUCLOCK_PERTHREAD_MASK)) +#endif + +#ifndef FUTEX_WAIT +#define FUTEX_WAIT 0 +#endif +#ifndef FUTEX_WAKE +#define FUTEX_WAKE 1 +#endif +#ifndef FUTEX_FD +#define FUTEX_FD 2 +#endif +#ifndef FUTEX_REQUEUE +#define FUTEX_REQUEUE 3 +#endif +#ifndef FUTEX_CMP_REQUEUE +#define FUTEX_CMP_REQUEUE 4 +#endif +#ifndef FUTEX_WAKE_OP +#define FUTEX_WAKE_OP 5 +#endif +#ifndef FUTEX_LOCK_PI +#define FUTEX_LOCK_PI 6 +#endif +#ifndef FUTEX_UNLOCK_PI +#define FUTEX_UNLOCK_PI 7 +#endif +#ifndef FUTEX_TRYLOCK_PI +#define FUTEX_TRYLOCK_PI 8 +#endif +#ifndef FUTEX_PRIVATE_FLAG +#define FUTEX_PRIVATE_FLAG 128 +#endif +#ifndef FUTEX_CMD_MASK +#define FUTEX_CMD_MASK ~FUTEX_PRIVATE_FLAG +#endif +#ifndef FUTEX_WAIT_PRIVATE +#define FUTEX_WAIT_PRIVATE (FUTEX_WAIT | FUTEX_PRIVATE_FLAG) +#endif +#ifndef FUTEX_WAKE_PRIVATE +#define FUTEX_WAKE_PRIVATE (FUTEX_WAKE | FUTEX_PRIVATE_FLAG) +#endif +#ifndef FUTEX_REQUEUE_PRIVATE +#define FUTEX_REQUEUE_PRIVATE (FUTEX_REQUEUE | FUTEX_PRIVATE_FLAG) +#endif +#ifndef FUTEX_CMP_REQUEUE_PRIVATE +#define FUTEX_CMP_REQUEUE_PRIVATE (FUTEX_CMP_REQUEUE | FUTEX_PRIVATE_FLAG) +#endif +#ifndef FUTEX_WAKE_OP_PRIVATE +#define FUTEX_WAKE_OP_PRIVATE (FUTEX_WAKE_OP | FUTEX_PRIVATE_FLAG) +#endif +#ifndef FUTEX_LOCK_PI_PRIVATE +#define FUTEX_LOCK_PI_PRIVATE (FUTEX_LOCK_PI | FUTEX_PRIVATE_FLAG) +#endif +#ifndef FUTEX_UNLOCK_PI_PRIVATE +#define FUTEX_UNLOCK_PI_PRIVATE (FUTEX_UNLOCK_PI | FUTEX_PRIVATE_FLAG) +#endif +#ifndef FUTEX_TRYLOCK_PI_PRIVATE +#define FUTEX_TRYLOCK_PI_PRIVATE (FUTEX_TRYLOCK_PI | FUTEX_PRIVATE_FLAG) +#endif + + +#if defined(__x86_64__) +#ifndef ARCH_SET_GS +#define ARCH_SET_GS 0x1001 +#endif +#ifndef ARCH_GET_GS +#define ARCH_GET_GS 0x1004 +#endif +#endif + +#if defined(__i386__) +#ifndef __NR_quotactl +#define __NR_quotactl 131 +#endif +#ifndef __NR_setresuid +#define __NR_setresuid 164 +#define __NR_getresuid 165 +#define __NR_setresgid 170 +#define __NR_getresgid 171 +#endif +#ifndef __NR_rt_sigaction +#define __NR_rt_sigreturn 173 +#define __NR_rt_sigaction 174 +#define __NR_rt_sigprocmask 175 +#define __NR_rt_sigpending 176 +#define __NR_rt_sigsuspend 179 +#endif +#ifndef __NR_pread64 +#define __NR_pread64 180 +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 181 +#endif +#ifndef __NR_ugetrlimit +#define __NR_ugetrlimit 191 +#endif +#ifndef __NR_stat64 +#define __NR_stat64 195 +#endif +#ifndef __NR_fstat64 +#define __NR_fstat64 197 +#endif +#ifndef __NR_setresuid32 +#define __NR_setresuid32 208 +#define __NR_getresuid32 209 +#define __NR_setresgid32 210 +#define __NR_getresgid32 211 +#endif +#ifndef __NR_setfsuid32 +#define __NR_setfsuid32 215 +#define __NR_setfsgid32 216 +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 220 +#endif +#ifndef __NR_gettid +#define __NR_gettid 224 +#endif +#ifndef __NR_readahead +#define __NR_readahead 225 +#endif +#ifndef __NR_setxattr +#define __NR_setxattr 226 +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr 227 +#endif +#ifndef __NR_getxattr +#define __NR_getxattr 229 +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr 230 +#endif +#ifndef __NR_listxattr +#define __NR_listxattr 232 +#endif +#ifndef __NR_llistxattr +#define __NR_llistxattr 233 +#endif +#ifndef __NR_tkill +#define __NR_tkill 238 +#endif +#ifndef __NR_futex +#define __NR_futex 240 +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity 241 +#define __NR_sched_getaffinity 242 +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address 258 +#endif +#ifndef __NR_clock_gettime +#define __NR_clock_gettime 265 +#endif +#ifndef __NR_clock_getres +#define __NR_clock_getres 266 +#endif +#ifndef __NR_statfs64 +#define __NR_statfs64 268 +#endif +#ifndef __NR_fstatfs64 +#define __NR_fstatfs64 269 +#endif +#ifndef __NR_fadvise64_64 +#define __NR_fadvise64_64 272 +#endif +#ifndef __NR_ioprio_set +#define __NR_ioprio_set 289 +#endif +#ifndef __NR_ioprio_get +#define __NR_ioprio_get 290 +#endif +#ifndef __NR_openat +#define __NR_openat 295 +#endif +#ifndef __NR_fstatat64 +#define __NR_fstatat64 300 +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat 301 +#endif +#ifndef __NR_move_pages +#define __NR_move_pages 317 +#endif +#ifndef __NR_getcpu +#define __NR_getcpu 318 +#endif +#ifndef __NR_fallocate +#define __NR_fallocate 324 +#endif +#ifndef __NR_getrandom +#define __NR_getrandom 355 +#endif +/* End of i386 definitions */ +#elif defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) +#ifndef __NR_setresuid +#define __NR_setresuid (__NR_SYSCALL_BASE + 164) +#define __NR_getresuid (__NR_SYSCALL_BASE + 165) +#define __NR_setresgid (__NR_SYSCALL_BASE + 170) +#define __NR_getresgid (__NR_SYSCALL_BASE + 171) +#endif +#ifndef __NR_rt_sigaction +#define __NR_rt_sigreturn (__NR_SYSCALL_BASE + 173) +#define __NR_rt_sigaction (__NR_SYSCALL_BASE + 174) +#define __NR_rt_sigprocmask (__NR_SYSCALL_BASE + 175) +#define __NR_rt_sigpending (__NR_SYSCALL_BASE + 176) +#define __NR_rt_sigsuspend (__NR_SYSCALL_BASE + 179) +#endif +#ifndef __NR_pread64 +#define __NR_pread64 (__NR_SYSCALL_BASE + 180) +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 (__NR_SYSCALL_BASE + 181) +#endif +#ifndef __NR_ugetrlimit +#define __NR_ugetrlimit (__NR_SYSCALL_BASE + 191) +#endif +#ifndef __NR_stat64 +#define __NR_stat64 (__NR_SYSCALL_BASE + 195) +#endif +#ifndef __NR_fstat64 +#define __NR_fstat64 (__NR_SYSCALL_BASE + 197) +#endif +#ifndef __NR_setresuid32 +#define __NR_setresuid32 (__NR_SYSCALL_BASE + 208) +#define __NR_getresuid32 (__NR_SYSCALL_BASE + 209) +#define __NR_setresgid32 (__NR_SYSCALL_BASE + 210) +#define __NR_getresgid32 (__NR_SYSCALL_BASE + 211) +#endif +#ifndef __NR_setfsuid32 +#define __NR_setfsuid32 (__NR_SYSCALL_BASE + 215) +#define __NR_setfsgid32 (__NR_SYSCALL_BASE + 216) +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 (__NR_SYSCALL_BASE + 217) +#endif +#ifndef __NR_gettid +#define __NR_gettid (__NR_SYSCALL_BASE + 224) +#endif +#ifndef __NR_readahead +#define __NR_readahead (__NR_SYSCALL_BASE + 225) +#endif +#ifndef __NR_setxattr +#define __NR_setxattr (__NR_SYSCALL_BASE + 226) +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr (__NR_SYSCALL_BASE + 227) +#endif +#ifndef __NR_getxattr +#define __NR_getxattr (__NR_SYSCALL_BASE + 229) +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr (__NR_SYSCALL_BASE + 230) +#endif +#ifndef __NR_listxattr +#define __NR_listxattr (__NR_SYSCALL_BASE + 232) +#endif +#ifndef __NR_llistxattr +#define __NR_llistxattr (__NR_SYSCALL_BASE + 233) +#endif +#ifndef __NR_tkill +#define __NR_tkill (__NR_SYSCALL_BASE + 238) +#endif +#ifndef __NR_futex +#define __NR_futex (__NR_SYSCALL_BASE + 240) +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity (__NR_SYSCALL_BASE + 241) +#define __NR_sched_getaffinity (__NR_SYSCALL_BASE + 242) +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address (__NR_SYSCALL_BASE + 256) +#endif +#ifndef __NR_clock_gettime +#define __NR_clock_gettime (__NR_SYSCALL_BASE + 263) +#endif +#ifndef __NR_clock_getres +#define __NR_clock_getres (__NR_SYSCALL_BASE + 264) +#endif +#ifndef __NR_statfs64 +#define __NR_statfs64 (__NR_SYSCALL_BASE + 266) +#endif +#ifndef __NR_fstatfs64 +#define __NR_fstatfs64 (__NR_SYSCALL_BASE + 267) +#endif +#ifndef __NR_ioprio_set +#define __NR_ioprio_set (__NR_SYSCALL_BASE + 314) +#endif +#ifndef __NR_ioprio_get +#define __NR_ioprio_get (__NR_SYSCALL_BASE + 315) +#endif +#ifndef __NR_fstatat64 +#define __NR_fstatat64 (__NR_SYSCALL_BASE + 327) +#endif +#ifndef __NR_move_pages +#define __NR_move_pages (__NR_SYSCALL_BASE + 344) +#endif +#ifndef __NR_getcpu +#define __NR_getcpu (__NR_SYSCALL_BASE + 345) +#endif +#ifndef __NR_getrandom +#define __NR_getrandom (__NR_SYSCALL_BASE + 384) +#endif +/* End of ARM 3/EABI definitions */ +#elif defined(__aarch64__) +#ifndef __NR_setxattr +#define __NR_setxattr 5 +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr 6 +#endif +#ifndef __NR_getxattr +#define __NR_getxattr 8 +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr 9 +#endif +#ifndef __NR_listxattr +#define __NR_listxattr 11 +#endif +#ifndef __NR_llistxattr +#define __NR_llistxattr 12 +#endif +#ifndef __NR_ioprio_set +#define __NR_ioprio_set 30 +#endif +#ifndef __NR_ioprio_get +#define __NR_ioprio_get 31 +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat 35 +#endif +#ifndef __NR_fallocate +#define __NR_fallocate 47 +#endif +#ifndef __NR_openat +#define __NR_openat 56 +#endif +#ifndef __NR_quotactl +#define __NR_quotactl 60 +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 61 +#endif +#ifndef __NR_getdents +// when getdents is not available, getdents64 is used for both. +#define __NR_getdents __NR_getdents64 +#endif +#ifndef __NR_pread64 +#define __NR_pread64 67 +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 68 +#endif +#ifndef __NR_ppoll +#define __NR_ppoll 73 +#endif +#ifndef __NR_readlinkat +#define __NR_readlinkat 78 +#endif +#ifndef __NR_newfstatat +#define __NR_newfstatat 79 +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address 96 +#endif +#ifndef __NR_futex +#define __NR_futex 98 +#endif +#ifndef __NR_clock_gettime +#define __NR_clock_gettime 113 +#endif +#ifndef __NR_clock_getres +#define __NR_clock_getres 114 +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity 122 +#define __NR_sched_getaffinity 123 +#endif +#ifndef __NR_tkill +#define __NR_tkill 130 +#endif +#ifndef __NR_setresuid +#define __NR_setresuid 147 +#define __NR_getresuid 148 +#define __NR_setresgid 149 +#define __NR_getresgid 150 +#endif +#ifndef __NR_gettid +#define __NR_gettid 178 +#endif +#ifndef __NR_readahead +#define __NR_readahead 213 +#endif +#ifndef __NR_fadvise64 +#define __NR_fadvise64 223 +#endif +#ifndef __NR_move_pages +#define __NR_move_pages 239 +#endif +#ifndef __NR_getrandom +#define __NR_getrandom 278 +#endif +/* End of aarch64 definitions */ +#elif defined(__x86_64__) +#ifndef __NR_pread64 +#define __NR_pread64 17 +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 18 +#endif +#ifndef __NR_setresuid +#define __NR_setresuid 117 +#define __NR_getresuid 118 +#define __NR_setresgid 119 +#define __NR_getresgid 120 +#endif +#ifndef __NR_quotactl +#define __NR_quotactl 179 +#endif +#ifndef __NR_gettid +#define __NR_gettid 186 +#endif +#ifndef __NR_readahead +#define __NR_readahead 187 +#endif +#ifndef __NR_setxattr +#define __NR_setxattr 188 +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr 189 +#endif +#ifndef __NR_getxattr +#define __NR_getxattr 191 +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr 192 +#endif +#ifndef __NR_listxattr +#define __NR_listxattr 194 +#endif +#ifndef __NR_llistxattr +#define __NR_llistxattr 195 +#endif +#ifndef __NR_tkill +#define __NR_tkill 200 +#endif +#ifndef __NR_futex +#define __NR_futex 202 +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity 203 +#define __NR_sched_getaffinity 204 +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 217 +#endif +#ifndef __NR_getdents +// when getdents is not available, getdents64 is used for both. +#define __NR_getdents __NR_getdents64 +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address 218 +#endif +#ifndef __NR_fadvise64 +#define __NR_fadvise64 221 +#endif +#ifndef __NR_clock_gettime +#define __NR_clock_gettime 228 +#endif +#ifndef __NR_clock_getres +#define __NR_clock_getres 229 +#endif +#ifndef __NR_ioprio_set +#define __NR_ioprio_set 251 +#endif +#ifndef __NR_ioprio_get +#define __NR_ioprio_get 252 +#endif +#ifndef __NR_openat +#define __NR_openat 257 +#endif +#ifndef __NR_newfstatat +#define __NR_newfstatat 262 +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat 263 +#endif +#ifndef __NR_move_pages +#define __NR_move_pages 279 +#endif +#ifndef __NR_fallocate +#define __NR_fallocate 285 +#endif +#ifndef __NR_getrandom +#define __NR_getrandom 318 +#endif +/* End of x86-64 definitions */ +#elif defined(__mips__) +#if _MIPS_SIM == _MIPS_SIM_ABI32 +#ifndef __NR_setresuid +#define __NR_setresuid (__NR_Linux + 185) +#define __NR_getresuid (__NR_Linux + 186) +#define __NR_setresgid (__NR_Linux + 190) +#define __NR_getresgid (__NR_Linux + 191) +#endif +#ifndef __NR_rt_sigaction +#define __NR_rt_sigreturn (__NR_Linux + 193) +#define __NR_rt_sigaction (__NR_Linux + 194) +#define __NR_rt_sigprocmask (__NR_Linux + 195) +#define __NR_rt_sigpending (__NR_Linux + 196) +#define __NR_rt_sigsuspend (__NR_Linux + 199) +#endif +#ifndef __NR_pread64 +#define __NR_pread64 (__NR_Linux + 200) +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 (__NR_Linux + 201) +#endif +#ifndef __NR_stat64 +#define __NR_stat64 (__NR_Linux + 213) +#endif +#ifndef __NR_fstat64 +#define __NR_fstat64 (__NR_Linux + 215) +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 (__NR_Linux + 219) +#endif +#ifndef __NR_gettid +#define __NR_gettid (__NR_Linux + 222) +#endif +#ifndef __NR_readahead +#define __NR_readahead (__NR_Linux + 223) +#endif +#ifndef __NR_setxattr +#define __NR_setxattr (__NR_Linux + 224) +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr (__NR_Linux + 225) +#endif +#ifndef __NR_getxattr +#define __NR_getxattr (__NR_Linux + 227) +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr (__NR_Linux + 228) +#endif +#ifndef __NR_listxattr +#define __NR_listxattr (__NR_Linux + 230) +#endif +#ifndef __NR_llistxattr +#define __NR_llistxattr (__NR_Linux + 231) +#endif +#ifndef __NR_tkill +#define __NR_tkill (__NR_Linux + 236) +#endif +#ifndef __NR_futex +#define __NR_futex (__NR_Linux + 238) +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity (__NR_Linux + 239) +#define __NR_sched_getaffinity (__NR_Linux + 240) +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address (__NR_Linux + 252) +#endif +#ifndef __NR_statfs64 +#define __NR_statfs64 (__NR_Linux + 255) +#endif +#ifndef __NR_fstatfs64 +#define __NR_fstatfs64 (__NR_Linux + 256) +#endif +#ifndef __NR_clock_gettime +#define __NR_clock_gettime (__NR_Linux + 263) +#endif +#ifndef __NR_clock_getres +#define __NR_clock_getres (__NR_Linux + 264) +#endif +#ifndef __NR_openat +#define __NR_openat (__NR_Linux + 288) +#endif +#ifndef __NR_fstatat +#define __NR_fstatat (__NR_Linux + 293) +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat (__NR_Linux + 294) +#endif +#ifndef __NR_move_pages +#define __NR_move_pages (__NR_Linux + 308) +#endif +#ifndef __NR_getcpu +#define __NR_getcpu (__NR_Linux + 312) +#endif +#ifndef __NR_ioprio_set +#define __NR_ioprio_set (__NR_Linux + 314) +#endif +#ifndef __NR_ioprio_get +#define __NR_ioprio_get (__NR_Linux + 315) +#endif +#ifndef __NR_getrandom +#define __NR_getrandom (__NR_Linux + 353) +#endif +/* End of MIPS (old 32bit API) definitions */ +#elif _MIPS_SIM == _MIPS_SIM_ABI64 +#ifndef __NR_pread64 +#define __NR_pread64 (__NR_Linux + 16) +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 (__NR_Linux + 17) +#endif +#ifndef __NR_setresuid +#define __NR_setresuid (__NR_Linux + 115) +#define __NR_getresuid (__NR_Linux + 116) +#define __NR_setresgid (__NR_Linux + 117) +#define __NR_getresgid (__NR_Linux + 118) +#endif +#ifndef __NR_gettid +#define __NR_gettid (__NR_Linux + 178) +#endif +#ifndef __NR_readahead +#define __NR_readahead (__NR_Linux + 179) +#endif +#ifndef __NR_setxattr +#define __NR_setxattr (__NR_Linux + 180) +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr (__NR_Linux + 181) +#endif +#ifndef __NR_getxattr +#define __NR_getxattr (__NR_Linux + 183) +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr (__NR_Linux + 184) +#endif +#ifndef __NR_listxattr +#define __NR_listxattr (__NR_Linux + 186) +#endif +#ifndef __NR_llistxattr +#define __NR_llistxattr (__NR_Linux + 187) +#endif +#ifndef __NR_tkill +#define __NR_tkill (__NR_Linux + 192) +#endif +#ifndef __NR_futex +#define __NR_futex (__NR_Linux + 194) +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity (__NR_Linux + 195) +#define __NR_sched_getaffinity (__NR_Linux + 196) +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address (__NR_Linux + 212) +#endif +#ifndef __NR_clock_gettime +#define __NR_clock_gettime (__NR_Linux + 222) +#endif +#ifndef __NR_clock_getres +#define __NR_clock_getres (__NR_Linux + 223) +#endif +#ifndef __NR_openat +#define __NR_openat (__NR_Linux + 247) +#endif +#ifndef __NR_fstatat +#define __NR_fstatat (__NR_Linux + 252) +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat (__NR_Linux + 253) +#endif +#ifndef __NR_move_pages +#define __NR_move_pages (__NR_Linux + 267) +#endif +#ifndef __NR_getcpu +#define __NR_getcpu (__NR_Linux + 271) +#endif +#ifndef __NR_ioprio_set +#define __NR_ioprio_set (__NR_Linux + 273) +#endif +#ifndef __NR_ioprio_get +#define __NR_ioprio_get (__NR_Linux + 274) +#endif +#ifndef __NR_getrandom +#define __NR_getrandom (__NR_Linux + 313) +#endif +/* End of MIPS (64bit API) definitions */ +#else +#ifndef __NR_setresuid +#define __NR_setresuid (__NR_Linux + 115) +#define __NR_getresuid (__NR_Linux + 116) +#define __NR_setresgid (__NR_Linux + 117) +#define __NR_getresgid (__NR_Linux + 118) +#endif +#ifndef __NR_gettid +#define __NR_gettid (__NR_Linux + 178) +#endif +#ifndef __NR_readahead +#define __NR_readahead (__NR_Linux + 179) +#endif +#ifndef __NR_setxattr +#define __NR_setxattr (__NR_Linux + 180) +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr (__NR_Linux + 181) +#endif +#ifndef __NR_getxattr +#define __NR_getxattr (__NR_Linux + 183) +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr (__NR_Linux + 184) +#endif +#ifndef __NR_listxattr +#define __NR_listxattr (__NR_Linux + 186) +#endif +#ifndef __NR_llistxattr +#define __NR_llistxattr (__NR_Linux + 187) +#endif +#ifndef __NR_tkill +#define __NR_tkill (__NR_Linux + 192) +#endif +#ifndef __NR_futex +#define __NR_futex (__NR_Linux + 194) +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity (__NR_Linux + 195) +#define __NR_sched_getaffinity (__NR_Linux + 196) +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address (__NR_Linux + 213) +#endif +#ifndef __NR_statfs64 +#define __NR_statfs64 (__NR_Linux + 217) +#endif +#ifndef __NR_fstatfs64 +#define __NR_fstatfs64 (__NR_Linux + 218) +#endif +#ifndef __NR_clock_gettime +#define __NR_clock_gettime (__NR_Linux + 226) +#endif +#ifndef __NR_clock_getres +#define __NR_clock_getres (__NR_Linux + 227) +#endif +#ifndef __NR_openat +#define __NR_openat (__NR_Linux + 251) +#endif +#ifndef __NR_fstatat +#define __NR_fstatat (__NR_Linux + 256) +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat (__NR_Linux + 257) +#endif +#ifndef __NR_move_pages +#define __NR_move_pages (__NR_Linux + 271) +#endif +#ifndef __NR_getcpu +#define __NR_getcpu (__NR_Linux + 275) +#endif +#ifndef __NR_ioprio_set +#define __NR_ioprio_set (__NR_Linux + 277) +#endif +#ifndef __NR_ioprio_get +#define __NR_ioprio_get (__NR_Linux + 278) +#endif +/* End of MIPS (new 32bit API) definitions */ +#endif +/* End of MIPS definitions */ +#elif defined(__PPC__) +#ifndef __NR_setfsuid +#define __NR_setfsuid 138 +#define __NR_setfsgid 139 +#endif +#ifndef __NR_setresuid +#define __NR_setresuid 164 +#define __NR_getresuid 165 +#define __NR_setresgid 169 +#define __NR_getresgid 170 +#endif +#ifndef __NR_rt_sigaction +#define __NR_rt_sigreturn 172 +#define __NR_rt_sigaction 173 +#define __NR_rt_sigprocmask 174 +#define __NR_rt_sigpending 175 +#define __NR_rt_sigsuspend 178 +#endif +#ifndef __NR_pread64 +#define __NR_pread64 179 +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 180 +#endif +#ifndef __NR_ugetrlimit +#define __NR_ugetrlimit 190 +#endif +#ifndef __NR_readahead +#define __NR_readahead 191 +#endif +#ifndef __NR_stat64 +#define __NR_stat64 195 +#endif +#ifndef __NR_fstat64 +#define __NR_fstat64 197 +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 202 +#endif +#ifndef __NR_gettid +#define __NR_gettid 207 +#endif +#ifndef __NR_tkill +#define __NR_tkill 208 +#endif +#ifndef __NR_setxattr +#define __NR_setxattr 209 +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr 210 +#endif +#ifndef __NR_getxattr +#define __NR_getxattr 212 +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr 213 +#endif +#ifndef __NR_listxattr +#define __NR_listxattr 215 +#endif +#ifndef __NR_llistxattr +#define __NR_llistxattr 216 +#endif +#ifndef __NR_futex +#define __NR_futex 221 +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity 222 +#define __NR_sched_getaffinity 223 +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address 232 +#endif +#ifndef __NR_clock_gettime +#define __NR_clock_gettime 246 +#endif +#ifndef __NR_clock_getres +#define __NR_clock_getres 247 +#endif +#ifndef __NR_statfs64 +#define __NR_statfs64 252 +#endif +#ifndef __NR_fstatfs64 +#define __NR_fstatfs64 253 +#endif +#ifndef __NR_fadvise64_64 +#define __NR_fadvise64_64 254 +#endif +#ifndef __NR_ioprio_set +#define __NR_ioprio_set 273 +#endif +#ifndef __NR_ioprio_get +#define __NR_ioprio_get 274 +#endif +#ifndef __NR_openat +#define __NR_openat 286 +#endif +#ifndef __NR_fstatat64 +#define __NR_fstatat64 291 +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat 292 +#endif +#ifndef __NR_move_pages +#define __NR_move_pages 301 +#endif +#ifndef __NR_getcpu +#define __NR_getcpu 302 +#endif +/* End of powerpc defininitions */ +#elif defined(__s390__) +#ifndef __NR_quotactl +#define __NR_quotactl 131 +#endif +#ifndef __NR_rt_sigreturn +#define __NR_rt_sigreturn 173 +#endif +#ifndef __NR_rt_sigaction +#define __NR_rt_sigaction 174 +#endif +#ifndef __NR_rt_sigprocmask +#define __NR_rt_sigprocmask 175 +#endif +#ifndef __NR_rt_sigpending +#define __NR_rt_sigpending 176 +#endif +#ifndef __NR_rt_sigsuspend +#define __NR_rt_sigsuspend 179 +#endif +#ifndef __NR_pread64 +#define __NR_pread64 180 +#endif +#ifndef __NR_pwrite64 +#define __NR_pwrite64 181 +#endif +#ifndef __NR_getdents64 +#define __NR_getdents64 220 +#endif +#ifndef __NR_readahead +#define __NR_readahead 222 +#endif +#ifndef __NR_setxattr +#define __NR_setxattr 224 +#endif +#ifndef __NR_lsetxattr +#define __NR_lsetxattr 225 +#endif +#ifndef __NR_getxattr +#define __NR_getxattr 227 +#endif +#ifndef __NR_lgetxattr +#define __NR_lgetxattr 228 +#endif +#ifndef __NR_listxattr +#define __NR_listxattr 230 +#endif +#ifndef __NR_llistxattr +#define __NR_llistxattr 231 +#endif +#ifndef __NR_gettid +#define __NR_gettid 236 +#endif +#ifndef __NR_tkill +#define __NR_tkill 237 +#endif +#ifndef __NR_futex +#define __NR_futex 238 +#endif +#ifndef __NR_sched_setaffinity +#define __NR_sched_setaffinity 239 +#endif +#ifndef __NR_sched_getaffinity +#define __NR_sched_getaffinity 240 +#endif +#ifndef __NR_set_tid_address +#define __NR_set_tid_address 252 +#endif +#ifndef __NR_clock_gettime +#define __NR_clock_gettime 260 +#endif +#ifndef __NR_clock_getres +#define __NR_clock_getres 261 +#endif +#ifndef __NR_statfs64 +#define __NR_statfs64 265 +#endif +#ifndef __NR_fstatfs64 +#define __NR_fstatfs64 266 +#endif +#ifndef __NR_ioprio_set +#define __NR_ioprio_set 282 +#endif +#ifndef __NR_ioprio_get +#define __NR_ioprio_get 283 +#endif +#ifndef __NR_openat +#define __NR_openat 288 +#endif +#ifndef __NR_unlinkat +#define __NR_unlinkat 294 +#endif +#ifndef __NR_move_pages +#define __NR_move_pages 310 +#endif +#ifndef __NR_getcpu +#define __NR_getcpu 311 +#endif +#ifndef __NR_fallocate +#define __NR_fallocate 314 +#endif +/* Some syscalls are named/numbered differently between s390 and s390x. */ +#ifdef __s390x__ +# ifndef __NR_getrlimit +# define __NR_getrlimit 191 +# endif +# ifndef __NR_setresuid +# define __NR_setresuid 208 +# endif +# ifndef __NR_getresuid +# define __NR_getresuid 209 +# endif +# ifndef __NR_setresgid +# define __NR_setresgid 210 +# endif +# ifndef __NR_getresgid +# define __NR_getresgid 211 +# endif +# ifndef __NR_setfsuid +# define __NR_setfsuid 215 +# endif +# ifndef __NR_setfsgid +# define __NR_setfsgid 216 +# endif +# ifndef __NR_fadvise64 +# define __NR_fadvise64 253 +# endif +# ifndef __NR_newfstatat +# define __NR_newfstatat 293 +# endif +#else /* __s390x__ */ +# ifndef __NR_getrlimit +# define __NR_getrlimit 76 +# endif +# ifndef __NR_setfsuid +# define __NR_setfsuid 138 +# endif +# ifndef __NR_setfsgid +# define __NR_setfsgid 139 +# endif +# ifndef __NR_setresuid +# define __NR_setresuid 164 +# endif +# ifndef __NR_getresuid +# define __NR_getresuid 165 +# endif +# ifndef __NR_setresgid +# define __NR_setresgid 170 +# endif +# ifndef __NR_getresgid +# define __NR_getresgid 171 +# endif +# ifndef __NR_ugetrlimit +# define __NR_ugetrlimit 191 +# endif +# ifndef __NR_mmap2 +# define __NR_mmap2 192 +# endif +# ifndef __NR_setresuid32 +# define __NR_setresuid32 208 +# endif +# ifndef __NR_getresuid32 +# define __NR_getresuid32 209 +# endif +# ifndef __NR_setresgid32 +# define __NR_setresgid32 210 +# endif +# ifndef __NR_getresgid32 +# define __NR_getresgid32 211 +# endif +# ifndef __NR_setfsuid32 +# define __NR_setfsuid32 215 +# endif +# ifndef __NR_setfsgid32 +# define __NR_setfsgid32 216 +# endif +# ifndef __NR_fadvise64_64 +# define __NR_fadvise64_64 264 +# endif +# ifndef __NR_fstatat64 +# define __NR_fstatat64 293 +# endif +#endif /* __s390__ */ +/* End of s390/s390x definitions */ +#endif + + +/* After forking, we must make sure to only call system calls. */ +#if defined(__BOUNDED_POINTERS__) + #error "Need to port invocations of syscalls for bounded ptrs" +#else + /* The core dumper and the thread lister get executed after threads + * have been suspended. As a consequence, we cannot call any functions + * that acquire locks. Unfortunately, libc wraps most system calls + * (e.g. in order to implement pthread_atfork, and to make calls + * cancellable), which means we cannot call these functions. Instead, + * we have to call syscall() directly. + */ + #undef LSS_ERRNO + #ifdef SYS_ERRNO + /* Allow the including file to override the location of errno. This can + * be useful when using clone() with the CLONE_VM option. + */ + #define LSS_ERRNO SYS_ERRNO + #else + #define LSS_ERRNO errno + #endif + + #undef LSS_INLINE + #ifdef SYS_INLINE + #define LSS_INLINE SYS_INLINE + #else + #define LSS_INLINE static inline + #endif + + /* Allow the including file to override the prefix used for all new + * system calls. By default, it will be set to "sys_". + */ + #undef LSS_NAME + #ifndef SYS_PREFIX + #define LSS_NAME(name) sys_##name + #elif defined(SYS_PREFIX) && SYS_PREFIX < 0 + #define LSS_NAME(name) name + #elif defined(SYS_PREFIX) && SYS_PREFIX == 0 + #define LSS_NAME(name) sys0_##name + #elif defined(SYS_PREFIX) && SYS_PREFIX == 1 + #define LSS_NAME(name) sys1_##name + #elif defined(SYS_PREFIX) && SYS_PREFIX == 2 + #define LSS_NAME(name) sys2_##name + #elif defined(SYS_PREFIX) && SYS_PREFIX == 3 + #define LSS_NAME(name) sys3_##name + #elif defined(SYS_PREFIX) && SYS_PREFIX == 4 + #define LSS_NAME(name) sys4_##name + #elif defined(SYS_PREFIX) && SYS_PREFIX == 5 + #define LSS_NAME(name) sys5_##name + #elif defined(SYS_PREFIX) && SYS_PREFIX == 6 + #define LSS_NAME(name) sys6_##name + #elif defined(SYS_PREFIX) && SYS_PREFIX == 7 + #define LSS_NAME(name) sys7_##name + #elif defined(SYS_PREFIX) && SYS_PREFIX == 8 + #define LSS_NAME(name) sys8_##name + #elif defined(SYS_PREFIX) && SYS_PREFIX == 9 + #define LSS_NAME(name) sys9_##name + #endif + + #undef LSS_RETURN + #if defined(__i386__) || defined(__x86_64__) || defined(__ARM_ARCH_3__) \ + || defined(__ARM_EABI__) || defined(__aarch64__) || defined(__s390__) \ + || defined(__e2k__) + /* Failing system calls return a negative result in the range of + * -1..-4095. These are "errno" values with the sign inverted. + */ + #define LSS_RETURN(type, res) \ + do { \ + if ((unsigned long)(res) >= (unsigned long)(-4095)) { \ + LSS_ERRNO = -(res); \ + res = -1; \ + } \ + return (type) (res); \ + } while (0) + #elif defined(__mips__) + /* On MIPS, failing system calls return -1, and set errno in a + * separate CPU register. + */ + #define LSS_RETURN(type, res, err) \ + do { \ + if (err) { \ + unsigned long __errnovalue = (res); \ + LSS_ERRNO = __errnovalue; \ + res = -1; \ + } \ + return (type) (res); \ + } while (0) + #elif defined(__PPC__) + /* On PPC, failing system calls return -1, and set errno in a + * separate CPU register. See linux/unistd.h. + */ + #define LSS_RETURN(type, res, err) \ + do { \ + if (err & 0x10000000 ) { \ + LSS_ERRNO = (res); \ + res = -1; \ + } \ + return (type) (res); \ + } while (0) + #endif + #if defined(__i386__) + /* In PIC mode (e.g. when building shared libraries), gcc for i386 + * reserves ebx. Unfortunately, most distribution ship with implementations + * of _syscallX() which clobber ebx. + * Also, most definitions of _syscallX() neglect to mark "memory" as being + * clobbered. This causes problems with compilers, that do a better job + * at optimizing across __asm__ calls. + * So, we just have to redefine all of the _syscallX() macros. + */ + #undef LSS_ENTRYPOINT + #ifdef SYS_SYSCALL_ENTRYPOINT + static inline void (**LSS_NAME(get_syscall_entrypoint)(void))(void) { + void (**entrypoint)(void); + asm volatile(".bss\n" + ".align 8\n" + ".globl " SYS_SYSCALL_ENTRYPOINT "\n" + ".common " SYS_SYSCALL_ENTRYPOINT ",8,8\n" + ".previous\n" + /* This logically does 'lea "SYS_SYSCALL_ENTRYPOINT", %0' */ + "call 0f\n" + "0:pop %0\n" + "add $_GLOBAL_OFFSET_TABLE_+[.-0b], %0\n" + "mov " SYS_SYSCALL_ENTRYPOINT "@GOT(%0), %0\n" + : "=r"(entrypoint)); + return entrypoint; + } + + #define LSS_ENTRYPOINT ".bss\n" \ + ".align 8\n" \ + ".globl " SYS_SYSCALL_ENTRYPOINT "\n" \ + ".common " SYS_SYSCALL_ENTRYPOINT ",8,8\n" \ + ".previous\n" \ + /* Check the SYS_SYSCALL_ENTRYPOINT vector */ \ + "push %%eax\n" \ + "call 10000f\n" \ + "10000:pop %%eax\n" \ + "add $_GLOBAL_OFFSET_TABLE_+[.-10000b], %%eax\n" \ + "mov " SYS_SYSCALL_ENTRYPOINT \ + "@GOT(%%eax), %%eax\n" \ + "mov 0(%%eax), %%eax\n" \ + "test %%eax, %%eax\n" \ + "jz 10002f\n" \ + "push %%eax\n" \ + "call 10001f\n" \ + "10001:pop %%eax\n" \ + "add $(10003f-10001b), %%eax\n" \ + "xchg 4(%%esp), %%eax\n" \ + "ret\n" \ + "10002:pop %%eax\n" \ + "int $0x80\n" \ + "10003:\n" + #else + #define LSS_ENTRYPOINT "int $0x80\n" + #endif + #undef LSS_BODY + #define LSS_BODY(type,args...) \ + long __res; \ + __asm__ __volatile__("push %%ebx\n" \ + "movl %2,%%ebx\n" \ + LSS_ENTRYPOINT \ + "pop %%ebx" \ + args \ + : "memory"); \ + LSS_RETURN(type,__res) + #undef _syscall0 + #define _syscall0(type,name) \ + type LSS_NAME(name)(void) { \ + long __res; \ + __asm__ volatile(LSS_ENTRYPOINT \ + : "=a" (__res) \ + : "0" (__NR_##name) \ + : "memory"); \ + LSS_RETURN(type,__res); \ + } + #undef _syscall1 + #define _syscall1(type,name,type1,arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_BODY(type, \ + : "=a" (__res) \ + : "0" (__NR_##name), "ri" ((long)(arg1))); \ + } + #undef _syscall2 + #define _syscall2(type,name,type1,arg1,type2,arg2) \ + type LSS_NAME(name)(type1 arg1,type2 arg2) { \ + LSS_BODY(type, \ + : "=a" (__res) \ + : "0" (__NR_##name),"ri" ((long)(arg1)), "c" ((long)(arg2))); \ + } + #undef _syscall3 + #define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \ + type LSS_NAME(name)(type1 arg1,type2 arg2,type3 arg3) { \ + LSS_BODY(type, \ + : "=a" (__res) \ + : "0" (__NR_##name), "ri" ((long)(arg1)), "c" ((long)(arg2)), \ + "d" ((long)(arg3))); \ + } + #undef _syscall4 + #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_BODY(type, \ + : "=a" (__res) \ + : "0" (__NR_##name), "ri" ((long)(arg1)), "c" ((long)(arg2)), \ + "d" ((long)(arg3)),"S" ((long)(arg4))); \ + } + #undef _syscall5 + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + long __res; \ + __asm__ __volatile__("push %%ebx\n" \ + "movl %2,%%ebx\n" \ + "movl %1,%%eax\n" \ + LSS_ENTRYPOINT \ + "pop %%ebx" \ + : "=a" (__res) \ + : "i" (__NR_##name), "ri" ((long)(arg1)), \ + "c" ((long)(arg2)), "d" ((long)(arg3)), \ + "S" ((long)(arg4)), "D" ((long)(arg5)) \ + : "memory"); \ + LSS_RETURN(type,__res); \ + } + #undef _syscall6 + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + long __res; \ + struct { long __a1; long __a6; } __s = { (long)arg1, (long) arg6 }; \ + __asm__ __volatile__("push %%ebp\n" \ + "push %%ebx\n" \ + "movl 4(%2),%%ebp\n" \ + "movl 0(%2), %%ebx\n" \ + "movl %1,%%eax\n" \ + LSS_ENTRYPOINT \ + "pop %%ebx\n" \ + "pop %%ebp" \ + : "=a" (__res) \ + : "i" (__NR_##name), "0" ((long)(&__s)), \ + "c" ((long)(arg2)), "d" ((long)(arg3)), \ + "S" ((long)(arg4)), "D" ((long)(arg5)) \ + : "memory"); \ + LSS_RETURN(type,__res); \ + } + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + long __res; + __asm__ __volatile__(/* if (fn == NULL) + * return -EINVAL; + */ + "movl %3,%%ecx\n" + "jecxz 1f\n" + + /* if (child_stack == NULL) + * return -EINVAL; + */ + "movl %4,%%ecx\n" + "jecxz 1f\n" + + /* Set up alignment of the child stack: + * child_stack = (child_stack & ~0xF) - 20; + */ + "andl $-16,%%ecx\n" + "subl $20,%%ecx\n" + + /* Push "arg" and "fn" onto the stack that will be + * used by the child. + */ + "movl %6,%%eax\n" + "movl %%eax,4(%%ecx)\n" + "movl %3,%%eax\n" + "movl %%eax,(%%ecx)\n" + + /* %eax = syscall(%eax = __NR_clone, + * %ebx = flags, + * %ecx = child_stack, + * %edx = parent_tidptr, + * %esi = newtls, + * %edi = child_tidptr) + * Also, make sure that %ebx gets preserved as it is + * used in PIC mode. + */ + "movl %8,%%esi\n" + "movl %7,%%edx\n" + "movl %5,%%eax\n" + "movl %9,%%edi\n" + "pushl %%ebx\n" + "movl %%eax,%%ebx\n" + "movl %2,%%eax\n" + LSS_ENTRYPOINT + + /* In the parent: restore %ebx + * In the child: move "fn" into %ebx + */ + "popl %%ebx\n" + + /* if (%eax != 0) + * return %eax; + */ + "test %%eax,%%eax\n" + "jnz 1f\n" + + /* In the child, now. Terminate frame pointer chain. + */ + "movl $0,%%ebp\n" + + /* Call "fn". "arg" is already on the stack. + */ + "call *%%ebx\n" + + /* Call _exit(%ebx). Unfortunately older versions + * of gcc restrict the number of arguments that can + * be passed to asm(). So, we need to hard-code the + * system call number. + */ + "movl %%eax,%%ebx\n" + "movl $1,%%eax\n" + LSS_ENTRYPOINT + + /* Return to parent. + */ + "1:\n" + : "=a" (__res) + : "0"(-EINVAL), "i"(__NR_clone), + "m"(fn), "m"(child_stack), "m"(flags), "m"(arg), + "m"(parent_tidptr), "m"(newtls), "m"(child_tidptr) + : "memory", "ecx", "edx", "esi", "edi"); + LSS_RETURN(int, __res); + } + + LSS_INLINE _syscall1(int, set_thread_area, void *, u) + LSS_INLINE _syscall1(int, get_thread_area, void *, u) + + LSS_INLINE void (*LSS_NAME(restore_rt)(void))(void) { + /* On i386, the kernel does not know how to return from a signal + * handler. Instead, it relies on user space to provide a + * restorer function that calls the {rt_,}sigreturn() system call. + * Unfortunately, we cannot just reference the glibc version of this + * function, as glibc goes out of its way to make it inaccessible. + */ + void (*res)(void); + __asm__ __volatile__("call 2f\n" + "0:.align 16\n" + "1:movl %1,%%eax\n" + LSS_ENTRYPOINT + "2:popl %0\n" + "addl $(1b-0b),%0\n" + : "=a" (res) + : "i" (__NR_rt_sigreturn)); + return res; + } + LSS_INLINE void (*LSS_NAME(restore)(void))(void) { + /* On i386, the kernel does not know how to return from a signal + * handler. Instead, it relies on user space to provide a + * restorer function that calls the {rt_,}sigreturn() system call. + * Unfortunately, we cannot just reference the glibc version of this + * function, as glibc goes out of its way to make it inaccessible. + */ + void (*res)(void); + __asm__ __volatile__("call 2f\n" + "0:.align 16\n" + "1:pop %%eax\n" + "movl %1,%%eax\n" + LSS_ENTRYPOINT + "2:popl %0\n" + "addl $(1b-0b),%0\n" + : "=a" (res) + : "i" (__NR_sigreturn)); + return res; + } + #elif defined(__x86_64__) + /* There are no known problems with any of the _syscallX() macros + * currently shipping for x86_64, but we still need to be able to define + * our own version so that we can override the location of the errno + * location (e.g. when using the clone() system call with the CLONE_VM + * option). + */ + #undef LSS_ENTRYPOINT + #ifdef SYS_SYSCALL_ENTRYPOINT + static inline void (**LSS_NAME(get_syscall_entrypoint)(void))(void) { + void (**entrypoint)(void); + asm volatile(".bss\n" + ".align 8\n" + ".globl " SYS_SYSCALL_ENTRYPOINT "\n" + ".common " SYS_SYSCALL_ENTRYPOINT ",8,8\n" + ".previous\n" + "mov " SYS_SYSCALL_ENTRYPOINT "@GOTPCREL(%%rip), %0\n" + : "=r"(entrypoint)); + return entrypoint; + } + + #define LSS_ENTRYPOINT \ + ".bss\n" \ + ".align 8\n" \ + ".globl " SYS_SYSCALL_ENTRYPOINT "\n" \ + ".common " SYS_SYSCALL_ENTRYPOINT ",8,8\n" \ + ".previous\n" \ + "mov " SYS_SYSCALL_ENTRYPOINT "@GOTPCREL(%%rip), %%rcx\n" \ + "mov 0(%%rcx), %%rcx\n" \ + "test %%rcx, %%rcx\n" \ + "jz 10001f\n" \ + "call *%%rcx\n" \ + "jmp 10002f\n" \ + "10001:syscall\n" \ + "10002:\n" + + #else + #define LSS_ENTRYPOINT "syscall\n" + #endif + + /* The x32 ABI has 32 bit longs, but the syscall interface is 64 bit. + * We need to explicitly cast to an unsigned 64 bit type to avoid implicit + * sign extension. We can't cast pointers directly because those are + * 32 bits, and gcc will dump ugly warnings about casting from a pointer + * to an integer of a different size. + */ + #undef LSS_SYSCALL_ARG + #define LSS_SYSCALL_ARG(a) ((uint64_t)(uintptr_t)(a)) + #undef _LSS_RETURN + #define _LSS_RETURN(type, res, cast) \ + do { \ + if ((uint64_t)(res) >= (uint64_t)(-4095)) { \ + LSS_ERRNO = -(res); \ + res = -1; \ + } \ + return (type)(cast)(res); \ + } while (0) + #undef LSS_RETURN + #define LSS_RETURN(type, res) _LSS_RETURN(type, res, uintptr_t) + + #undef _LSS_BODY + #define _LSS_BODY(nr, type, name, cast, ...) \ + long long __res; \ + __asm__ __volatile__(LSS_BODY_ASM##nr LSS_ENTRYPOINT \ + : "=a" (__res) \ + : "0" (__NR_##name) LSS_BODY_ARG##nr(__VA_ARGS__) \ + : LSS_BODY_CLOBBER##nr "r11", "rcx", "memory"); \ + _LSS_RETURN(type, __res, cast) + #undef LSS_BODY + #define LSS_BODY(nr, type, name, args...) \ + _LSS_BODY(nr, type, name, uintptr_t, ## args) + + #undef LSS_BODY_ASM0 + #undef LSS_BODY_ASM1 + #undef LSS_BODY_ASM2 + #undef LSS_BODY_ASM3 + #undef LSS_BODY_ASM4 + #undef LSS_BODY_ASM5 + #undef LSS_BODY_ASM6 + #define LSS_BODY_ASM0 + #define LSS_BODY_ASM1 LSS_BODY_ASM0 + #define LSS_BODY_ASM2 LSS_BODY_ASM1 + #define LSS_BODY_ASM3 LSS_BODY_ASM2 + #define LSS_BODY_ASM4 LSS_BODY_ASM3 "movq %5,%%r10;" + #define LSS_BODY_ASM5 LSS_BODY_ASM4 "movq %6,%%r8;" + #define LSS_BODY_ASM6 LSS_BODY_ASM5 "movq %7,%%r9;" + + #undef LSS_BODY_CLOBBER0 + #undef LSS_BODY_CLOBBER1 + #undef LSS_BODY_CLOBBER2 + #undef LSS_BODY_CLOBBER3 + #undef LSS_BODY_CLOBBER4 + #undef LSS_BODY_CLOBBER5 + #undef LSS_BODY_CLOBBER6 + #define LSS_BODY_CLOBBER0 + #define LSS_BODY_CLOBBER1 LSS_BODY_CLOBBER0 + #define LSS_BODY_CLOBBER2 LSS_BODY_CLOBBER1 + #define LSS_BODY_CLOBBER3 LSS_BODY_CLOBBER2 + #define LSS_BODY_CLOBBER4 LSS_BODY_CLOBBER3 "r10", + #define LSS_BODY_CLOBBER5 LSS_BODY_CLOBBER4 "r8", + #define LSS_BODY_CLOBBER6 LSS_BODY_CLOBBER5 "r9", + + #undef LSS_BODY_ARG0 + #undef LSS_BODY_ARG1 + #undef LSS_BODY_ARG2 + #undef LSS_BODY_ARG3 + #undef LSS_BODY_ARG4 + #undef LSS_BODY_ARG5 + #undef LSS_BODY_ARG6 + #define LSS_BODY_ARG0() + #define LSS_BODY_ARG1(arg1) \ + LSS_BODY_ARG0(), "D" (arg1) + #define LSS_BODY_ARG2(arg1, arg2) \ + LSS_BODY_ARG1(arg1), "S" (arg2) + #define LSS_BODY_ARG3(arg1, arg2, arg3) \ + LSS_BODY_ARG2(arg1, arg2), "d" (arg3) + #define LSS_BODY_ARG4(arg1, arg2, arg3, arg4) \ + LSS_BODY_ARG3(arg1, arg2, arg3), "r" (arg4) + #define LSS_BODY_ARG5(arg1, arg2, arg3, arg4, arg5) \ + LSS_BODY_ARG4(arg1, arg2, arg3, arg4), "r" (arg5) + #define LSS_BODY_ARG6(arg1, arg2, arg3, arg4, arg5, arg6) \ + LSS_BODY_ARG5(arg1, arg2, arg3, arg4, arg5), "r" (arg6) + + #undef _syscall0 + #define _syscall0(type,name) \ + type LSS_NAME(name)(void) { \ + LSS_BODY(0, type, name); \ + } + #undef _syscall1 + #define _syscall1(type,name,type1,arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_BODY(1, type, name, LSS_SYSCALL_ARG(arg1)); \ + } + #undef _syscall2 + #define _syscall2(type,name,type1,arg1,type2,arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + LSS_BODY(2, type, name, LSS_SYSCALL_ARG(arg1), LSS_SYSCALL_ARG(arg2));\ + } + #undef _syscall3 + #define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + LSS_BODY(3, type, name, LSS_SYSCALL_ARG(arg1), LSS_SYSCALL_ARG(arg2), \ + LSS_SYSCALL_ARG(arg3)); \ + } + #undef _syscall4 + #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_BODY(4, type, name, LSS_SYSCALL_ARG(arg1), LSS_SYSCALL_ARG(arg2), \ + LSS_SYSCALL_ARG(arg3), LSS_SYSCALL_ARG(arg4));\ + } + #undef _syscall5 + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + LSS_BODY(5, type, name, LSS_SYSCALL_ARG(arg1), LSS_SYSCALL_ARG(arg2), \ + LSS_SYSCALL_ARG(arg3), LSS_SYSCALL_ARG(arg4), \ + LSS_SYSCALL_ARG(arg5)); \ + } + #undef _syscall6 + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + LSS_BODY(6, type, name, LSS_SYSCALL_ARG(arg1), LSS_SYSCALL_ARG(arg2), \ + LSS_SYSCALL_ARG(arg3), LSS_SYSCALL_ARG(arg4), \ + LSS_SYSCALL_ARG(arg5), LSS_SYSCALL_ARG(arg6));\ + } + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + long long __res; + { + __asm__ __volatile__(/* if (fn == NULL) + * return -EINVAL; + */ + "testq %4,%4\n" + "jz 1f\n" + + /* if (child_stack == NULL) + * return -EINVAL; + */ + "testq %5,%5\n" + "jz 1f\n" + + /* childstack -= 2*sizeof(void *); + */ + "subq $16,%5\n" + + /* Push "arg" and "fn" onto the stack that will be + * used by the child. + */ + "movq %7,8(%5)\n" + "movq %4,0(%5)\n" + + /* %rax = syscall(%rax = __NR_clone, + * %rdi = flags, + * %rsi = child_stack, + * %rdx = parent_tidptr, + * %r8 = new_tls, + * %r10 = child_tidptr) + */ + "movq %2,%%rax\n" + "movq %9,%%r8\n" + "movq %10,%%r10\n" + LSS_ENTRYPOINT + + /* if (%rax != 0) + * return; + */ + "testq %%rax,%%rax\n" + "jnz 1f\n" + + /* In the child. Terminate frame pointer chain. + */ + "xorq %%rbp,%%rbp\n" + + /* Call "fn(arg)". + */ + "popq %%rax\n" + "popq %%rdi\n" + "call *%%rax\n" + + /* Call _exit(%ebx). + */ + "movq %%rax,%%rdi\n" + "movq %3,%%rax\n" + LSS_ENTRYPOINT + + /* Return to parent. + */ + "1:\n" + : "=a" (__res) + : "0"(-EINVAL), "i"(__NR_clone), "i"(__NR_exit), + "r"(LSS_SYSCALL_ARG(fn)), + "S"(LSS_SYSCALL_ARG(child_stack)), + "D"(LSS_SYSCALL_ARG(flags)), + "r"(LSS_SYSCALL_ARG(arg)), + "d"(LSS_SYSCALL_ARG(parent_tidptr)), + "r"(LSS_SYSCALL_ARG(newtls)), + "r"(LSS_SYSCALL_ARG(child_tidptr)) + : "memory", "r8", "r10", "r11", "rcx"); + } + LSS_RETURN(int, __res); + } + LSS_INLINE _syscall2(int, arch_prctl, int, c, void *, a) + + LSS_INLINE void (*LSS_NAME(restore_rt)(void))(void) { + /* On x86-64, the kernel does not know how to return from + * a signal handler. Instead, it relies on user space to provide a + * restorer function that calls the rt_sigreturn() system call. + * Unfortunately, we cannot just reference the glibc version of this + * function, as glibc goes out of its way to make it inaccessible. + */ + long long res; + __asm__ __volatile__("jmp 2f\n" + ".align 16\n" + "1:movq %1,%%rax\n" + LSS_ENTRYPOINT + "2:leaq 1b(%%rip),%0\n" + : "=r" (res) + : "i" (__NR_rt_sigreturn)); + return (void (*)(void))(uintptr_t)res; + } + #elif defined(__ARM_ARCH_3__) + /* Most definitions of _syscallX() neglect to mark "memory" as being + * clobbered. This causes problems with compilers, that do a better job + * at optimizing across __asm__ calls. + * So, we just have to redefine all of the _syscallX() macros. + */ + #undef LSS_REG + #define LSS_REG(r,a) register long __r##r __asm__("r"#r) = (long)a + #undef LSS_BODY + #define LSS_BODY(type,name,args...) \ + register long __res_r0 __asm__("r0"); \ + long __res; \ + __asm__ __volatile__ (__syscall(name) \ + : "=r"(__res_r0) : args : "lr", "memory"); \ + __res = __res_r0; \ + LSS_RETURN(type, __res) + #undef _syscall0 + #define _syscall0(type, name) \ + type LSS_NAME(name)(void) { \ + LSS_BODY(type, name); \ + } + #undef _syscall1 + #define _syscall1(type, name, type1, arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_REG(0, arg1); LSS_BODY(type, name, "r"(__r0)); \ + } + #undef _syscall2 + #define _syscall2(type, name, type1, arg1, type2, arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1)); \ + } + #undef _syscall3 + #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2)); \ + } + #undef _syscall4 + #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3)); \ + } + #undef _syscall5 + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); LSS_REG(4, arg5); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \ + "r"(__r4)); \ + } + #undef _syscall6 + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); LSS_REG(4, arg5); LSS_REG(5, arg6); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \ + "r"(__r4), "r"(__r5)); \ + } + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + long __res; + { + register int __flags __asm__("r0") = flags; + register void *__stack __asm__("r1") = child_stack; + register void *__ptid __asm__("r2") = parent_tidptr; + register void *__tls __asm__("r3") = newtls; + register int *__ctid __asm__("r4") = child_tidptr; + __asm__ __volatile__(/* if (fn == NULL || child_stack == NULL) + * return -EINVAL; + */ + "cmp %2,#0\n" + "cmpne %3,#0\n" + "moveq %0,%1\n" + "beq 1f\n" + + /* Push "arg" and "fn" onto the stack that will be + * used by the child. + */ + "str %5,[%3,#-4]!\n" + "str %2,[%3,#-4]!\n" + + /* %r0 = syscall(%r0 = flags, + * %r1 = child_stack, + * %r2 = parent_tidptr, + * %r3 = newtls, + * %r4 = child_tidptr) + */ + __syscall(clone)"\n" + + /* if (%r0 != 0) + * return %r0; + */ + "movs %0,r0\n" + "bne 1f\n" + + /* In the child, now. Call "fn(arg)". + */ + "ldr r0,[sp, #4]\n" + "mov lr,pc\n" + "ldr pc,[sp]\n" + + /* Call _exit(%r0). + */ + __syscall(exit)"\n" + "1:\n" + : "=r" (__res) + : "i"(-EINVAL), + "r"(fn), "r"(__stack), "r"(__flags), "r"(arg), + "r"(__ptid), "r"(__tls), "r"(__ctid) + : "cc", "lr", "memory"); + } + LSS_RETURN(int, __res); + } + #elif defined(__ARM_EABI__) + /* Most definitions of _syscallX() neglect to mark "memory" as being + * clobbered. This causes problems with compilers, that do a better job + * at optimizing across __asm__ calls. + * So, we just have to redefine all fo the _syscallX() macros. + */ + #undef LSS_REG + #define LSS_REG(r,a) register long __r##r __asm__("r"#r) = (long)a + #undef LSS_BODY + #define LSS_BODY(type,name,args...) \ + register long __res_r0 __asm__("r0"); \ + long __res; \ + __asm__ __volatile__ ("push {r7}\n" \ + "mov r7, %1\n" \ + "swi 0x0\n" \ + "pop {r7}\n" \ + : "=r"(__res_r0) \ + : "i"(__NR_##name) , ## args \ + : "lr", "memory"); \ + __res = __res_r0; \ + LSS_RETURN(type, __res) + #undef _syscall0 + #define _syscall0(type, name) \ + type LSS_NAME(name)(void) { \ + LSS_BODY(type, name); \ + } + #undef _syscall1 + #define _syscall1(type, name, type1, arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_REG(0, arg1); LSS_BODY(type, name, "r"(__r0)); \ + } + #undef _syscall2 + #define _syscall2(type, name, type1, arg1, type2, arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1)); \ + } + #undef _syscall3 + #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2)); \ + } + #undef _syscall4 + #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3)); \ + } + #undef _syscall5 + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); LSS_REG(4, arg5); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \ + "r"(__r4)); \ + } + #undef _syscall6 + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); LSS_REG(4, arg5); LSS_REG(5, arg6); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \ + "r"(__r4), "r"(__r5)); \ + } + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + long __res; + if (fn == NULL || child_stack == NULL) { + __res = -EINVAL; + LSS_RETURN(int, __res); + } + + /* Push "arg" and "fn" onto the stack that will be + * used by the child. + */ + { + uintptr_t* cstack = (uintptr_t*)child_stack - 2; + cstack[0] = (uintptr_t)fn; + cstack[1] = (uintptr_t)arg; + child_stack = cstack; + } + { + register int __flags __asm__("r0") = flags; + register void *__stack __asm__("r1") = child_stack; + register void *__ptid __asm__("r2") = parent_tidptr; + register void *__tls __asm__("r3") = newtls; + register int *__ctid __asm__("r4") = child_tidptr; + __asm__ __volatile__( +#ifdef __thumb2__ + "push {r7}\n" +#endif + /* %r0 = syscall(%r0 = flags, + * %r1 = child_stack, + * %r2 = parent_tidptr, + * %r3 = newtls, + * %r4 = child_tidptr) + */ + "mov r7, %6\n" + "swi 0x0\n" + + /* if (%r0 != 0) + * return %r0; + */ + "cmp r0, #0\n" + "bne 1f\n" + + /* In the child, now. Call "fn(arg)". + */ + "ldr r0,[sp, #4]\n" + + "ldr lr,[sp]\n" + "blx lr\n" + + /* Call _exit(%r0). + */ + "mov r7, %7\n" + "swi 0x0\n" + /* Unreachable */ + "bkpt #0\n" + "1:\n" +#ifdef __thumb2__ + "pop {r7}\n" +#endif + "movs %0,r0\n" + : "=r"(__res) + : "r"(__stack), "r"(__flags), "r"(__ptid), "r"(__tls), "r"(__ctid), + "i"(__NR_clone), "i"(__NR_exit) + : "cc", "lr", "memory" +#ifndef __thumb2__ + , "r7" +#endif + ); + } + LSS_RETURN(int, __res); + } + #elif defined(__aarch64__) + /* Most definitions of _syscallX() neglect to mark "memory" as being + * clobbered. This causes problems with compilers, that do a better job + * at optimizing across __asm__ calls. + * So, we just have to redefine all of the _syscallX() macros. + */ + #undef LSS_REG + #define LSS_REG(r,a) register int64_t __r##r __asm__("x"#r) = (int64_t)a + #undef LSS_BODY + #define LSS_BODY(type,name,args...) \ + register int64_t __res_x0 __asm__("x0"); \ + int64_t __res; \ + __asm__ __volatile__ ("mov x8, %1\n" \ + "svc 0x0\n" \ + : "=r"(__res_x0) \ + : "i"(__NR_##name) , ## args \ + : "x8", "memory"); \ + __res = __res_x0; \ + LSS_RETURN(type, __res) + #undef _syscall0 + #define _syscall0(type, name) \ + type LSS_NAME(name)(void) { \ + LSS_BODY(type, name); \ + } + #undef _syscall1 + #define _syscall1(type, name, type1, arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_REG(0, arg1); LSS_BODY(type, name, "r"(__r0)); \ + } + #undef _syscall2 + #define _syscall2(type, name, type1, arg1, type2, arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1)); \ + } + #undef _syscall3 + #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2)); \ + } + #undef _syscall4 + #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3)); \ + } + #undef _syscall5 + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); LSS_REG(4, arg5); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \ + "r"(__r4)); \ + } + #undef _syscall6 + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + LSS_REG(0, arg1); LSS_REG(1, arg2); LSS_REG(2, arg3); \ + LSS_REG(3, arg4); LSS_REG(4, arg5); LSS_REG(5, arg6); \ + LSS_BODY(type, name, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), \ + "r"(__r4), "r"(__r5)); \ + } + + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + int64_t __res; + { + register uint64_t __flags __asm__("x0") = flags; + register void *__stack __asm__("x1") = child_stack; + register void *__ptid __asm__("x2") = parent_tidptr; + register void *__tls __asm__("x3") = newtls; + register int *__ctid __asm__("x4") = child_tidptr; + __asm__ __volatile__(/* Push "arg" and "fn" onto the stack that will be + * used by the child. + */ + "stp %1, %4, [%2, #-16]!\n" + + /* %x0 = syscall(%x0 = flags, + * %x1 = child_stack, + * %x2 = parent_tidptr, + * %x3 = newtls, + * %x4 = child_tidptr) + */ + "mov x8, %8\n" + "svc 0x0\n" + + /* if (%r0 != 0) + * return %r0; + */ + "mov %0, x0\n" + "cbnz x0, 1f\n" + + /* In the child, now. Call "fn(arg)". + */ + "ldp x1, x0, [sp], #16\n" + "blr x1\n" + + /* Call _exit(%r0). + */ + "mov x8, %9\n" + "svc 0x0\n" + "1:\n" + : "=r" (__res) + : "r"(fn), "r"(__stack), "r"(__flags), "r"(arg), + "r"(__ptid), "r"(__tls), "r"(__ctid), + "i"(__NR_clone), "i"(__NR_exit) + : "cc", "x8", "memory"); + } + LSS_RETURN(int, __res); + } + #elif defined(__mips__) + #undef LSS_REG + #define LSS_REG(r,a) register unsigned long __r##r __asm__("$"#r) = \ + (unsigned long)(a) + #undef LSS_BODY + #undef LSS_SYSCALL_CLOBBERS + #if _MIPS_SIM == _MIPS_SIM_ABI32 + #define LSS_SYSCALL_CLOBBERS "$1", "$3", "$8", "$9", "$10", \ + "$11", "$12", "$13", "$14", "$15", \ + "$24", "$25", "hi", "lo", "memory" + #else + #define LSS_SYSCALL_CLOBBERS "$1", "$3", "$10", "$11", "$12", \ + "$13", "$14", "$15", "$24", "$25", \ + "hi", "lo", "memory" + #endif + #define LSS_BODY(type,name,r7,...) \ + register unsigned long __v0 __asm__("$2") = __NR_##name; \ + __asm__ __volatile__ ("syscall\n" \ + : "=r"(__v0), r7 (__r7) \ + : "0"(__v0), ##__VA_ARGS__ \ + : LSS_SYSCALL_CLOBBERS); \ + LSS_RETURN(type, __v0, __r7) + #undef _syscall0 + #define _syscall0(type, name) \ + type LSS_NAME(name)(void) { \ + register unsigned long __r7 __asm__("$7"); \ + LSS_BODY(type, name, "=r"); \ + } + #undef _syscall1 + #define _syscall1(type, name, type1, arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + register unsigned long __r7 __asm__("$7"); \ + LSS_REG(4, arg1); LSS_BODY(type, name, "=r", "r"(__r4)); \ + } + #undef _syscall2 + #define _syscall2(type, name, type1, arg1, type2, arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + register unsigned long __r7 __asm__("$7"); \ + LSS_REG(4, arg1); LSS_REG(5, arg2); \ + LSS_BODY(type, name, "=r", "r"(__r4), "r"(__r5)); \ + } + #undef _syscall3 + #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + register unsigned long __r7 __asm__("$7"); \ + LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ + LSS_BODY(type, name, "=r", "r"(__r4), "r"(__r5), "r"(__r6)); \ + } + #undef _syscall4 + #define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ + LSS_REG(7, arg4); \ + LSS_BODY(type, name, "+r", "r"(__r4), "r"(__r5), "r"(__r6)); \ + } + #undef _syscall5 + #if _MIPS_SIM == _MIPS_SIM_ABI32 + /* The old 32bit MIPS system call API passes the fifth and sixth argument + * on the stack, whereas the new APIs use registers "r8" and "r9". + */ + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ + LSS_REG(7, arg4); \ + register unsigned long __v0 __asm__("$2") = __NR_##name; \ + __asm__ __volatile__ (".set noreorder\n" \ + "subu $29, 32\n" \ + "sw %5, 16($29)\n" \ + "syscall\n" \ + "addiu $29, 32\n" \ + ".set reorder\n" \ + : "+r"(__v0), "+r" (__r7) \ + : "r"(__r4), "r"(__r5), \ + "r"(__r6), "r" ((unsigned long)arg5) \ + : "$8", "$9", "$10", "$11", "$12", \ + "$13", "$14", "$15", "$24", "$25", \ + "memory"); \ + LSS_RETURN(type, __v0, __r7); \ + } + #else + #define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ + LSS_REG(7, arg4); LSS_REG(8, arg5); \ + LSS_BODY(type, name, "+r", "r"(__r4), "r"(__r5), "r"(__r6), \ + "r"(__r8)); \ + } + #endif + #undef _syscall6 + #if _MIPS_SIM == _MIPS_SIM_ABI32 + /* The old 32bit MIPS system call API passes the fifth and sixth argument + * on the stack, whereas the new APIs use registers "r8" and "r9". + */ + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ + LSS_REG(7, arg4); \ + register unsigned long __v0 __asm__("$2") = __NR_##name; \ + __asm__ __volatile__ (".set noreorder\n" \ + "subu $29, 32\n" \ + "sw %5, 16($29)\n" \ + "sw %6, 20($29)\n" \ + "syscall\n" \ + "addiu $29, 32\n" \ + ".set reorder\n" \ + : "+r"(__v0), "+r" (__r7) \ + : "r"(__r4), "r"(__r5), \ + "r"(__r6), "r" ((unsigned long)arg5), \ + "r" ((unsigned long)arg6) \ + : "$8", "$9", "$10", "$11", "$12", \ + "$13", "$14", "$15", "$24", "$25", \ + "memory"); \ + LSS_RETURN(type, __v0, __r7); \ + } + #else + #define _syscall6(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \ + type5,arg5,type6,arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5,type6 arg6) { \ + LSS_REG(4, arg1); LSS_REG(5, arg2); LSS_REG(6, arg3); \ + LSS_REG(7, arg4); LSS_REG(8, arg5); LSS_REG(9, arg6); \ + LSS_BODY(type, name, "+r", "r"(__r4), "r"(__r5), "r"(__r6), \ + "r"(__r8), "r"(__r9)); \ + } + #endif + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + register unsigned long __v0 __asm__("$2") = -EINVAL; + register unsigned long __r7 __asm__("$7") = (unsigned long)newtls; + { + register int __flags __asm__("$4") = flags; + register void *__stack __asm__("$5") = child_stack; + register void *__ptid __asm__("$6") = parent_tidptr; + register int *__ctid __asm__("$8") = child_tidptr; + __asm__ __volatile__( + #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32 + "subu $29,24\n" + #elif _MIPS_SIM == _MIPS_SIM_NABI32 + "sub $29,16\n" + #else + "dsubu $29,16\n" + #endif + + /* if (fn == NULL || child_stack == NULL) + * return -EINVAL; + */ + "beqz %4,1f\n" + "beqz %5,1f\n" + + /* Push "arg" and "fn" onto the stack that will be + * used by the child. + */ + #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32 + "subu %5,32\n" + "sw %4,0(%5)\n" + "sw %7,4(%5)\n" + #elif _MIPS_SIM == _MIPS_SIM_NABI32 + "sub %5,32\n" + "sw %4,0(%5)\n" + "sw %7,8(%5)\n" + #else + "dsubu %5,32\n" + "sd %4,0(%5)\n" + "sd %7,8(%5)\n" + #endif + + /* $7 = syscall($4 = flags, + * $5 = child_stack, + * $6 = parent_tidptr, + * $7 = newtls, + * $8 = child_tidptr) + */ + "li $2,%2\n" + "syscall\n" + + /* if ($7 != 0) + * return $2; + */ + "bnez $7,1f\n" + "bnez $2,1f\n" + + /* In the child, now. Call "fn(arg)". + */ + #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32 + "lw $25,0($29)\n" + "lw $4,4($29)\n" + #elif _MIPS_SIM == _MIPS_SIM_NABI32 + "lw $25,0($29)\n" + "lw $4,8($29)\n" + #else + "ld $25,0($29)\n" + "ld $4,8($29)\n" + #endif + "jalr $25\n" + + /* Call _exit($2) + */ + "move $4,$2\n" + "li $2,%3\n" + "syscall\n" + + "1:\n" + #if _MIPS_SIM == _MIPS_SIM_ABI32 && _MIPS_SZPTR == 32 + "addu $29, 24\n" + #elif _MIPS_SIM == _MIPS_SIM_NABI32 + "add $29, 16\n" + #else + "daddu $29,16\n" + #endif + : "+r" (__v0), "+r" (__r7) + : "i"(__NR_clone), "i"(__NR_exit), "r"(fn), + "r"(__stack), "r"(__flags), "r"(arg), + "r"(__ptid), "r"(__ctid) + : "$9", "$10", "$11", "$12", "$13", "$14", "$15", + "$24", "$25", "memory"); + } + LSS_RETURN(int, __v0, __r7); + } + #elif defined (__PPC__) + #undef LSS_LOADARGS_0 + #define LSS_LOADARGS_0(name, dummy...) \ + __sc_0 = __NR_##name + #undef LSS_LOADARGS_1 + #define LSS_LOADARGS_1(name, arg1) \ + LSS_LOADARGS_0(name); \ + __sc_3 = (unsigned long) (arg1) + #undef LSS_LOADARGS_2 + #define LSS_LOADARGS_2(name, arg1, arg2) \ + LSS_LOADARGS_1(name, arg1); \ + __sc_4 = (unsigned long) (arg2) + #undef LSS_LOADARGS_3 + #define LSS_LOADARGS_3(name, arg1, arg2, arg3) \ + LSS_LOADARGS_2(name, arg1, arg2); \ + __sc_5 = (unsigned long) (arg3) + #undef LSS_LOADARGS_4 + #define LSS_LOADARGS_4(name, arg1, arg2, arg3, arg4) \ + LSS_LOADARGS_3(name, arg1, arg2, arg3); \ + __sc_6 = (unsigned long) (arg4) + #undef LSS_LOADARGS_5 + #define LSS_LOADARGS_5(name, arg1, arg2, arg3, arg4, arg5) \ + LSS_LOADARGS_4(name, arg1, arg2, arg3, arg4); \ + __sc_7 = (unsigned long) (arg5) + #undef LSS_LOADARGS_6 + #define LSS_LOADARGS_6(name, arg1, arg2, arg3, arg4, arg5, arg6) \ + LSS_LOADARGS_5(name, arg1, arg2, arg3, arg4, arg5); \ + __sc_8 = (unsigned long) (arg6) + #undef LSS_ASMINPUT_0 + #define LSS_ASMINPUT_0 "0" (__sc_0) + #undef LSS_ASMINPUT_1 + #define LSS_ASMINPUT_1 LSS_ASMINPUT_0, "1" (__sc_3) + #undef LSS_ASMINPUT_2 + #define LSS_ASMINPUT_2 LSS_ASMINPUT_1, "2" (__sc_4) + #undef LSS_ASMINPUT_3 + #define LSS_ASMINPUT_3 LSS_ASMINPUT_2, "3" (__sc_5) + #undef LSS_ASMINPUT_4 + #define LSS_ASMINPUT_4 LSS_ASMINPUT_3, "4" (__sc_6) + #undef LSS_ASMINPUT_5 + #define LSS_ASMINPUT_5 LSS_ASMINPUT_4, "5" (__sc_7) + #undef LSS_ASMINPUT_6 + #define LSS_ASMINPUT_6 LSS_ASMINPUT_5, "6" (__sc_8) + #undef LSS_BODY + #define LSS_BODY(nr, type, name, args...) \ + long __sc_ret, __sc_err; \ + { \ + register unsigned long __sc_0 __asm__ ("r0"); \ + register unsigned long __sc_3 __asm__ ("r3"); \ + register unsigned long __sc_4 __asm__ ("r4"); \ + register unsigned long __sc_5 __asm__ ("r5"); \ + register unsigned long __sc_6 __asm__ ("r6"); \ + register unsigned long __sc_7 __asm__ ("r7"); \ + register unsigned long __sc_8 __asm__ ("r8"); \ + \ + LSS_LOADARGS_##nr(name, args); \ + __asm__ __volatile__ \ + ("sc\n\t" \ + "mfcr %0" \ + : "=&r" (__sc_0), \ + "=&r" (__sc_3), "=&r" (__sc_4), \ + "=&r" (__sc_5), "=&r" (__sc_6), \ + "=&r" (__sc_7), "=&r" (__sc_8) \ + : LSS_ASMINPUT_##nr \ + : "cr0", "ctr", "memory", \ + "r9", "r10", "r11", "r12"); \ + __sc_ret = __sc_3; \ + __sc_err = __sc_0; \ + } \ + LSS_RETURN(type, __sc_ret, __sc_err) + #undef _syscall0 + #define _syscall0(type, name) \ + type LSS_NAME(name)(void) { \ + LSS_BODY(0, type, name); \ + } + #undef _syscall1 + #define _syscall1(type, name, type1, arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_BODY(1, type, name, arg1); \ + } + #undef _syscall2 + #define _syscall2(type, name, type1, arg1, type2, arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + LSS_BODY(2, type, name, arg1, arg2); \ + } + #undef _syscall3 + #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + LSS_BODY(3, type, name, arg1, arg2, arg3); \ + } + #undef _syscall4 + #define _syscall4(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_BODY(4, type, name, arg1, arg2, arg3, arg4); \ + } + #undef _syscall5 + #define _syscall5(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + LSS_BODY(5, type, name, arg1, arg2, arg3, arg4, arg5); \ + } + #undef _syscall6 + #define _syscall6(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5, type6, arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + LSS_BODY(6, type, name, arg1, arg2, arg3, arg4, arg5, arg6); \ + } + /* clone function adapted from glibc 2.3.6 clone.S */ + /* TODO(csilvers): consider wrapping some args up in a struct, like we + * do for i386's _syscall6, so we can compile successfully on gcc 2.95 + */ + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + long __ret, __err; + { + register int (*__fn)(void *) __asm__ ("r8") = fn; + register void *__cstack __asm__ ("r4") = child_stack; + register int __flags __asm__ ("r3") = flags; + register void * __arg __asm__ ("r9") = arg; + register int * __ptidptr __asm__ ("r5") = parent_tidptr; + register void * __newtls __asm__ ("r6") = newtls; + register int * __ctidptr __asm__ ("r7") = child_tidptr; + __asm__ __volatile__( + /* check for fn == NULL + * and child_stack == NULL + */ + "cmpwi cr0, %6, 0\n\t" + "cmpwi cr1, %7, 0\n\t" + "cror cr0*4+eq, cr1*4+eq, cr0*4+eq\n\t" + "beq- cr0, 1f\n\t" + + /* set up stack frame for child */ + "clrrwi %7, %7, 4\n\t" + "li 0, 0\n\t" + "stwu 0, -16(%7)\n\t" + + /* fn, arg, child_stack are saved across the syscall: r28-30 */ + "mr 28, %6\n\t" + "mr 29, %7\n\t" + "mr 27, %9\n\t" + + /* syscall */ + "li 0, %4\n\t" + /* flags already in r3 + * child_stack already in r4 + * ptidptr already in r5 + * newtls already in r6 + * ctidptr already in r7 + */ + "sc\n\t" + + /* Test if syscall was successful */ + "cmpwi cr1, 3, 0\n\t" + "crandc cr1*4+eq, cr1*4+eq, cr0*4+so\n\t" + "bne- cr1, 1f\n\t" + + /* Do the function call */ + "mtctr 28\n\t" + "mr 3, 27\n\t" + "bctrl\n\t" + + /* Call _exit(r3) */ + "li 0, %5\n\t" + "sc\n\t" + + /* Return to parent */ + "1:\n" + "mfcr %1\n\t" + "mr %0, 3\n\t" + : "=r" (__ret), "=r" (__err) + : "0" (-1), "1" (EINVAL), + "i" (__NR_clone), "i" (__NR_exit), + "r" (__fn), "r" (__cstack), "r" (__flags), + "r" (__arg), "r" (__ptidptr), "r" (__newtls), + "r" (__ctidptr) + : "cr0", "cr1", "memory", "ctr", + "r0", "r29", "r27", "r28"); + } + LSS_RETURN(int, __ret, __err); + } + #elif defined(__s390__) + #undef LSS_REG + #define LSS_REG(r, a) register unsigned long __r##r __asm__("r"#r) = (unsigned long) a + #undef LSS_BODY + #define LSS_BODY(type, name, args...) \ + register unsigned long __nr __asm__("r1") \ + = (unsigned long)(__NR_##name); \ + register long __res_r2 __asm__("r2"); \ + long __res; \ + __asm__ __volatile__ \ + ("svc 0\n\t" \ + : "=d"(__res_r2) \ + : "d"(__nr), ## args \ + : "memory"); \ + __res = __res_r2; \ + LSS_RETURN(type, __res) + #undef _syscall0 + #define _syscall0(type, name) \ + type LSS_NAME(name)(void) { \ + LSS_BODY(type, name); \ + } + #undef _syscall1 + #define _syscall1(type, name, type1, arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_REG(2, arg1); \ + LSS_BODY(type, name, "0"(__r2)); \ + } + #undef _syscall2 + #define _syscall2(type, name, type1, arg1, type2, arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + LSS_REG(2, arg1); LSS_REG(3, arg2); \ + LSS_BODY(type, name, "0"(__r2), "d"(__r3)); \ + } + #undef _syscall3 + #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + LSS_REG(2, arg1); LSS_REG(3, arg2); LSS_REG(4, arg3); \ + LSS_BODY(type, name, "0"(__r2), "d"(__r3), "d"(__r4)); \ + } + #undef _syscall4 + #define _syscall4(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, \ + type4 arg4) { \ + LSS_REG(2, arg1); LSS_REG(3, arg2); LSS_REG(4, arg3); \ + LSS_REG(5, arg4); \ + LSS_BODY(type, name, "0"(__r2), "d"(__r3), "d"(__r4), \ + "d"(__r5)); \ + } + #undef _syscall5 + #define _syscall5(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, \ + type4 arg4, type5 arg5) { \ + LSS_REG(2, arg1); LSS_REG(3, arg2); LSS_REG(4, arg3); \ + LSS_REG(5, arg4); LSS_REG(6, arg5); \ + LSS_BODY(type, name, "0"(__r2), "d"(__r3), "d"(__r4), \ + "d"(__r5), "d"(__r6)); \ + } + #undef _syscall6 + #define _syscall6(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5, type6, arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, \ + type4 arg4, type5 arg5, type6 arg6) { \ + LSS_REG(2, arg1); LSS_REG(3, arg2); LSS_REG(4, arg3); \ + LSS_REG(5, arg4); LSS_REG(6, arg5); LSS_REG(7, arg6); \ + LSS_BODY(type, name, "0"(__r2), "d"(__r3), "d"(__r4), \ + "d"(__r5), "d"(__r6), "d"(__r7)); \ + } + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + long __ret; + { + register int (*__fn)(void *) __asm__ ("r1") = fn; + register void *__cstack __asm__ ("r2") = child_stack; + register int __flags __asm__ ("r3") = flags; + register void *__arg __asm__ ("r0") = arg; + register int *__ptidptr __asm__ ("r4") = parent_tidptr; + register void *__newtls __asm__ ("r6") = newtls; + register int *__ctidptr __asm__ ("r5") = child_tidptr; + __asm__ __volatile__ ( + #ifndef __s390x__ + /* arg already in r0 */ + "ltr %4, %4\n\t" /* check fn, which is already in r1 */ + "jz 1f\n\t" /* NULL function pointer, return -EINVAL */ + "ltr %5, %5\n\t" /* check child_stack, which is already in r2 */ + "jz 1f\n\t" /* NULL stack pointer, return -EINVAL */ + /* flags already in r3 */ + /* parent_tidptr already in r4 */ + /* child_tidptr already in r5 */ + /* newtls already in r6 */ + "svc %2\n\t" /* invoke clone syscall */ + "ltr %0,%%r2\n\t" /* load return code into __ret and test */ + "jnz 1f\n\t" /* return to parent if non-zero */ + /* start child thread */ + "lr %%r2, %7\n\t" /* set first parameter to void *arg */ + "ahi %%r15, -96\n\t" /* make room on the stack for the save area */ + "xc 0(4,%%r15), 0(%%r15)\n\t" + "basr %%r14, %4\n\t" /* jump to fn */ + "svc %3\n" /* invoke exit syscall */ + "1:\n" + #else + /* arg already in r0 */ + "ltgr %4, %4\n\t" /* check fn, which is already in r1 */ + "jz 1f\n\t" /* NULL function pointer, return -EINVAL */ + "ltgr %5, %5\n\t" /* check child_stack, which is already in r2 */ + "jz 1f\n\t" /* NULL stack pointer, return -EINVAL */ + /* flags already in r3 */ + /* parent_tidptr already in r4 */ + /* child_tidptr already in r5 */ + /* newtls already in r6 */ + "svc %2\n\t" /* invoke clone syscall */ + "ltgr %0, %%r2\n\t" /* load return code into __ret and test */ + "jnz 1f\n\t" /* return to parent if non-zero */ + /* start child thread */ + "lgr %%r2, %7\n\t" /* set first parameter to void *arg */ + "aghi %%r15, -160\n\t" /* make room on the stack for the save area */ + "xc 0(8,%%r15), 0(%%r15)\n\t" + "basr %%r14, %4\n\t" /* jump to fn */ + "svc %3\n" /* invoke exit syscall */ + "1:\n" + #endif + : "=r" (__ret) + : "0" (-EINVAL), "i" (__NR_clone), "i" (__NR_exit), + "d" (__fn), "d" (__cstack), "d" (__flags), "d" (__arg), + "d" (__ptidptr), "d" (__newtls), "d" (__ctidptr) + : "cc", "r14", "memory" + ); + } + LSS_RETURN(int, __ret); + } + #elif defined(__e2k__) + + #undef _LSS_BODY + #define _LSS_BODY(nr, type, name, ...) \ + register unsigned long long __res; \ + __asm__ __volatile__ \ + ( \ + "{\n\t" \ + " sdisp %%ctpr1, 0x3\n\t" \ + " addd, s 0x0, %[sys_num], %%b[0]\n\t" \ + LSS_BODY_ASM##nr \ + "}\n\t" \ + "{\n\t" \ + " call %%ctpr1, wbs = %#\n\t" \ + "}\n\t" \ + "{\n\t" \ + " addd, s 0x0, %%b[0], %[res]\n\t" \ + "}\n\t" \ + : [res] "=r" (__res) \ + : \ + LSS_BODY_ARG##nr(__VA_ARGS__) \ + [sys_num] "ri" (__NR_##name) \ + : "ctpr1", "ctpr2", "ctpr3", \ + "b[0]", "b[1]", "b[2]", "b[3]", \ + "b[4]", "b[5]", "b[6]", "b[7]" \ + ); \ + LSS_RETURN(type, __res); + + #undef LSS_BODY + #define LSS_BODY(nr, type, name, args...) \ + _LSS_BODY(nr, type, name, ## args) + + #undef LSS_BODY_ASM0 + #undef LSS_BODY_ASM1 + #undef LSS_BODY_ASM2 + #undef LSS_BODY_ASM3 + #undef LSS_BODY_ASM4 + #undef LSS_BODY_ASM5 + #undef LSS_BODY_ASM6 + + #define LSS_BODY_ASM0 + #define LSS_BODY_ASM1 LSS_BODY_ASM0 \ + " addd, s 0x0, %[arg1], %%b[1]\n\t" + #define LSS_BODY_ASM2 LSS_BODY_ASM1 \ + " addd, s 0x0, %[arg2], %%b[2]\n\t" + #define LSS_BODY_ASM3 LSS_BODY_ASM2 \ + " addd, s 0x0, %[arg3], %%b[3]\n\t" + #define LSS_BODY_ASM4 LSS_BODY_ASM3 \ + " addd, s 0x0, %[arg4], %%b[4]\n\t" + #define LSS_BODY_ASM5 LSS_BODY_ASM4 \ + " addd, s 0x0, %[arg5], %%b[5]\n\t" + #define LSS_BODY_ASM6 LSS_BODY_ASM5 \ + "}\n\t" \ + "{\n\t" \ + " addd, s 0x0, %[arg6], %%b[6]\n\t" + + #undef LSS_SYSCALL_ARG + #define LSS_SYSCALL_ARG(a) ((unsigned long long)(uintptr_t)(a)) + + #undef LSS_BODY_ARG0 + #undef LSS_BODY_ARG1 + #undef LSS_BODY_ARG2 + #undef LSS_BODY_ARG3 + #undef LSS_BODY_ARG4 + #undef LSS_BODY_ARG5 + #undef LSS_BODY_ARG6 + + #define LSS_BODY_ARG0() + #define LSS_BODY_ARG1(_arg1) \ + [arg1] "ri" LSS_SYSCALL_ARG(_arg1), + #define LSS_BODY_ARG2(_arg1, _arg2) \ + LSS_BODY_ARG1(_arg1) \ + [arg2] "ri" LSS_SYSCALL_ARG(_arg2), + #define LSS_BODY_ARG3(_arg1, _arg2, _arg3) \ + LSS_BODY_ARG2(_arg1, _arg2) \ + [arg3] "ri" LSS_SYSCALL_ARG(_arg3), + #define LSS_BODY_ARG4(_arg1, _arg2, _arg3, _arg4) \ + LSS_BODY_ARG3(_arg1, _arg2, _arg3) \ + [arg4] "ri" LSS_SYSCALL_ARG(_arg4), + #define LSS_BODY_ARG5(_arg1, _arg2, _arg3, _arg4, _arg5) \ + LSS_BODY_ARG4(_arg1, _arg2, _arg3, _arg4) \ + [arg5] "ri" LSS_SYSCALL_ARG(_arg5), + #define LSS_BODY_ARG6(_arg1, _arg2, _arg3, _arg4, _arg5, _arg6) \ + LSS_BODY_ARG5(_arg1, _arg2, _arg3, _arg4, _arg5) \ + [arg6] "ri" LSS_SYSCALL_ARG(_arg6), + + #undef _syscall0 + #define _syscall0(type, name) \ + type LSS_NAME(name)(void) { \ + LSS_BODY(0, type, name); \ + } + + #undef _syscall1 + #define _syscall1(type, name, type1, arg1) \ + type LSS_NAME(name)(type1 arg1) { \ + LSS_BODY(1, type, name, arg1) \ + } + + #undef _syscall2 + #define _syscall2(type, name, type1, arg1, type2, arg2) \ + type LSS_NAME(name)(type1 arg1, type2 arg2) { \ + LSS_BODY(2, type, name, arg1, arg2) \ + } + + #undef _syscall3 + #define _syscall3(type, name, type1, arg1, type2, arg2, type3, arg3) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3) { \ + LSS_BODY(3, type, name, arg1, arg2, arg3) \ + } + + #undef _syscall4 + #define _syscall4(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4) { \ + LSS_BODY(4, type, name, arg1, arg2, arg3, arg4) \ + } + + #undef _syscall5 + #define _syscall5(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5) { \ + LSS_BODY(5, type, name, arg1, arg2, arg3, arg4, arg5) \ + } + + #undef _syscall6 + #define _syscall6(type, name, type1, arg1, type2, arg2, type3, arg3, \ + type4, arg4, type5, arg5, type6, arg6) \ + type LSS_NAME(name)(type1 arg1, type2 arg2, type3 arg3, type4 arg4, \ + type5 arg5, type6 arg6) { \ + LSS_BODY(6, type, name, arg1, arg2, arg3, arg4, arg5, arg6) \ + } + + LSS_INLINE int LSS_NAME(clone)(int (*fn)(void *), void *child_stack, + int flags, void *arg, int *parent_tidptr, + void *newtls, int *child_tidptr) { + unsigned long long __res; + + __asm__ __volatile__ ( + "{\n\t" + " addd,s 0x0, %[nr_clone], %%b[0]\n\t" + " addd,s 0x0, %[flags], %%db[1]\n\t" + " addd,s 0x0, %[child_stack], %%db[2]\n\t" + " addd,s 0x0, %[parent_tidptr], %%db[3]\n\t" + " addd,s 0x0, %[child_tidptr], %%db[4]\n\t" + " addd,s 0x0, %[newtls], %%db[5]\n\t" + "}\n\t" + /* if (fn == NULL) + * return -EINVAL; + */ + + "{\n\t" + " disp %%ctpr1, .L1\n\t" + "}\n\t" + "{\n\t" + " cmpesb,s 0x0, %[fn], %%pred0\n\t" + "}\n\t" + "{\n\t" + " ct %%ctpr1 ? %%pred0\n\t" + "}\n\t" + + /* if (child_stack == NULL) + * return -EINVAL; + */ + "{\n\t" + " cmpesb,s 0x0, %%db[2], %%pred0\n\t" + "}\n\t" + "{\n\t" + " ct %%ctpr1 ? %%pred0\n\t" + "}\n\t" + + /* b[0] = syscall(%b[0] = __NR_clone, + * %db[1] = flags, + * %db[2] = child_stack, + * %db[3] = parent_tidptr, + * %db[4] = child_tidptr, + * %db[5] = newtls) + */ + "{\n\t" + " sdisp %%ctpr1, 0x3\n\t" + "}\n\t" + "{\n\t" + " call %%ctpr1, wbs = %#\n\t" + "}\n\t" + + /* if (%[b0] != 0) + * return %b[0]; + */ + "{\n\t" + " disp %%ctpr1, .L2\n\t" + " cmpesb,s 0x0, %%b[0], %%pred0\n\t" + "}\n\t" + "{\n\t" + " ct %%ctpr1 ? ~%%pred0\n\t" + "}\n\t" + /* In the child, now. Call "fn(arg)". + */ + + "{\n\t" + " movtd,s %[fn], %%ctpr1\n\t" + "}\n\t" + "{\n\t" + " addd,s 0x0, %[arg], %%db[0]\n\t" + "}\n\t" + "{\n\t" + " call %%ctpr1, wbs = %#\n\t" + "}\n\t" + /* Call _exit(%b[0]). + */ + + "{\n\t" + " sdisp %%ctpr1, 0x3\n\t" + " addd,s 0x0, %%b[0], %%b[1]\n\t" + "}\n\t" + "{\n\t" + " addd,s 0x0, %[nr_exit], %%b[0]\n\t" + "}\n\t" + "{\n\t" + " call %%ctpr1, wbs = %#\n\t" + "}\n\t" + "{\n\t" + " disp %%ctpr1, .L2\n\t" + " adds,s 0x0, 0x0, %%b[0]\n\t" + "}\n\t" + "{\n\t" + " ct %%ctpr1\n\t" + "}\n\t" + ".L1:\n\t" + "{\n\t" + " addd,s 0x0, %[einval], %%b[0]\n\t" + "}\n\t" + ".L2:\n\t" + "{\n\t" + " addd,s 0x0, %%b[0], %[res]\n\t" + "}\n\t" + : [res] "=r" LSS_SYSCALL_ARG(__res) + : [nr_clone] "ri" LSS_SYSCALL_ARG(__NR_clone) + [arg] "ri" LSS_SYSCALL_ARG(arg) + [nr_exit] "ri" LSS_SYSCALL_ARG(__NR_exit) + [flags] "ri" LSS_SYSCALL_ARG(flags) + [child_stack] "ri" LSS_SYSCALL_ARG(child_stack) + [parent_tidptr] "ri" + LSS_SYSCALL_ARG(parent_tidptr) + [newtls] "ri" LSS_SYSCALL_ARG(newtls) + [child_tidptr] "ri" + LSS_SYSCALL_ARG(child_tidptr) + [fn] "ri" LSS_SYSCALL_ARG(fn) + [einval] "ri" LSS_SYSCALL_ARG(-EINVAL) + : "ctpr1", "b[0]", "b[1]", "b[2]", "b[3]", + "b[4]", "b[5]", "pred0"); + LSS_RETURN(int, __res); + } + + #endif + #define __NR__exit __NR_exit + #define __NR__gettid __NR_gettid + #define __NR__mremap __NR_mremap + LSS_INLINE _syscall1(void *, brk, void *, e) + LSS_INLINE _syscall1(int, chdir, const char *,p) + LSS_INLINE _syscall1(int, close, int, f) + LSS_INLINE _syscall2(int, clock_getres, int, c, + struct kernel_timespec*, t) + LSS_INLINE _syscall2(int, clock_gettime, int, c, + struct kernel_timespec*, t) + LSS_INLINE _syscall1(int, dup, int, f) + #if defined(__NR_dup2) + // dup2 is polyfilled below when not available. + LSS_INLINE _syscall2(int, dup2, int, s, + int, d) + #endif + #if defined(__NR_dup3) + LSS_INLINE _syscall3(int, dup3, int, s, int, d, int, f) + #endif + LSS_INLINE _syscall3(int, execve, const char*, f, + const char*const*,a,const char*const*, e) + LSS_INLINE _syscall1(int, _exit, int, e) + LSS_INLINE _syscall1(int, exit_group, int, e) + LSS_INLINE _syscall3(int, fcntl, int, f, + int, c, long, a) + #if defined(__NR_fork) + // fork is polyfilled below when not available. + LSS_INLINE _syscall0(pid_t, fork) + #endif + LSS_INLINE _syscall2(int, fstat, int, f, + struct kernel_stat*, b) + LSS_INLINE _syscall2(int, fstatfs, int, f, + struct kernel_statfs*, b) + #if defined(__x86_64__) + /* Need to make sure off_t isn't truncated to 32-bits under x32. */ + LSS_INLINE int LSS_NAME(ftruncate)(int f, off_t l) { + LSS_BODY(2, int, ftruncate, LSS_SYSCALL_ARG(f), (uint64_t)(l)); + } + #else + LSS_INLINE _syscall2(int, ftruncate, int, f, + off_t, l) + #endif + LSS_INLINE _syscall6(int, futex, int*, u, + int, o, int, v, + struct kernel_timespec*, t, + int*, u2, int, v2) + LSS_INLINE _syscall3(int, getdents, int, f, + struct kernel_dirent*, d, int, c) + LSS_INLINE _syscall3(int, getdents64, int, f, + struct kernel_dirent64*, d, int, c) + LSS_INLINE _syscall0(gid_t, getegid) + LSS_INLINE _syscall0(uid_t, geteuid) + #if defined(__NR_getpgrp) + LSS_INLINE _syscall0(pid_t, getpgrp) + #endif + LSS_INLINE _syscall0(pid_t, getpid) + LSS_INLINE _syscall0(pid_t, getppid) + LSS_INLINE _syscall2(int, getpriority, int, a, + int, b) + LSS_INLINE _syscall3(int, getresgid, gid_t *, r, + gid_t *, e, gid_t *, s) + LSS_INLINE _syscall3(int, getresuid, uid_t *, r, + uid_t *, e, uid_t *, s) +#if !defined(__ARM_EABI__) + LSS_INLINE _syscall2(int, getrlimit, int, r, + struct kernel_rlimit*, l) +#endif + LSS_INLINE _syscall1(pid_t, getsid, pid_t, p) + LSS_INLINE _syscall0(pid_t, _gettid) + LSS_INLINE _syscall2(pid_t, gettimeofday, struct kernel_timeval*, t, + void*, tz) + LSS_INLINE _syscall5(int, setxattr, const char *,p, + const char *, n, const void *,v, + size_t, s, int, f) + LSS_INLINE _syscall5(int, lsetxattr, const char *,p, + const char *, n, const void *,v, + size_t, s, int, f) + LSS_INLINE _syscall4(ssize_t, getxattr, const char *,p, + const char *, n, void *, v, size_t, s) + LSS_INLINE _syscall4(ssize_t, lgetxattr, const char *,p, + const char *, n, void *, v, size_t, s) + LSS_INLINE _syscall3(ssize_t, listxattr, const char *,p, + char *, l, size_t, s) + LSS_INLINE _syscall3(ssize_t, llistxattr, const char *,p, + char *, l, size_t, s) + LSS_INLINE _syscall3(int, ioctl, int, d, + int, r, void *, a) + LSS_INLINE _syscall2(int, ioprio_get, int, which, + int, who) + LSS_INLINE _syscall3(int, ioprio_set, int, which, + int, who, int, ioprio) + LSS_INLINE _syscall2(int, kill, pid_t, p, + int, s) + #if defined(__x86_64__) + /* Need to make sure off_t isn't truncated to 32-bits under x32. */ + LSS_INLINE off_t LSS_NAME(lseek)(int f, off_t o, int w) { + _LSS_BODY(3, off_t, lseek, off_t, LSS_SYSCALL_ARG(f), (uint64_t)(o), + LSS_SYSCALL_ARG(w)); + } + #else + LSS_INLINE _syscall3(off_t, lseek, int, f, + off_t, o, int, w) + #endif + LSS_INLINE _syscall2(int, munmap, void*, s, + size_t, l) + LSS_INLINE _syscall6(long, move_pages, pid_t, p, + unsigned long, n, void **,g, int *, d, + int *, s, int, f) + LSS_INLINE _syscall3(int, mprotect, const void *,a, + size_t, l, int, p) + LSS_INLINE _syscall5(void*, _mremap, void*, o, + size_t, os, size_t, ns, + unsigned long, f, void *, a) + #if defined(__NR_open) + // open is polyfilled below when not available. + LSS_INLINE _syscall3(int, open, const char*, p, + int, f, int, m) + #endif + #if defined(__NR_poll) + // poll is polyfilled below when not available. + LSS_INLINE _syscall3(int, poll, struct kernel_pollfd*, u, + unsigned int, n, int, t) + #endif + #if defined(__NR_ppoll) + LSS_INLINE _syscall5(int, ppoll, struct kernel_pollfd *, u, + unsigned int, n, const struct kernel_timespec *, t, + const struct kernel_sigset_t *, sigmask, size_t, s) + #endif + LSS_INLINE _syscall5(int, prctl, int, option, + unsigned long, arg2, + unsigned long, arg3, + unsigned long, arg4, + unsigned long, arg5) + LSS_INLINE _syscall4(long, ptrace, int, r, + pid_t, p, void *, a, void *, d) + #if defined(__NR_quotactl) + // Defined on x86_64 / i386 only + LSS_INLINE _syscall4(int, quotactl, int, cmd, const char *, special, + int, id, caddr_t, addr) + #endif + LSS_INLINE _syscall3(ssize_t, read, int, f, + void *, b, size_t, c) + #if defined(__NR_readlink) + // readlink is polyfilled below when not available. + LSS_INLINE _syscall3(int, readlink, const char*, p, + char*, b, size_t, s) + #endif + #if defined(__NR_readlinkat) + LSS_INLINE _syscall4(int, readlinkat, int, d, const char *, p, char *, b, + size_t, s) + #endif + LSS_INLINE _syscall4(int, rt_sigaction, int, s, + const struct kernel_sigaction*, a, + struct kernel_sigaction*, o, size_t, c) + LSS_INLINE _syscall2(int, rt_sigpending, struct kernel_sigset_t *, s, + size_t, c) + LSS_INLINE _syscall4(int, rt_sigprocmask, int, h, + const struct kernel_sigset_t*, s, + struct kernel_sigset_t*, o, size_t, c) + LSS_INLINE _syscall2(int, rt_sigsuspend, + const struct kernel_sigset_t*, s, size_t, c) + LSS_INLINE _syscall4(int, rt_sigtimedwait, const struct kernel_sigset_t*, s, + siginfo_t*, i, const struct timespec*, t, size_t, c) + LSS_INLINE _syscall3(int, sched_getaffinity,pid_t, p, + unsigned int, l, unsigned long *, m) + LSS_INLINE _syscall3(int, sched_setaffinity,pid_t, p, + unsigned int, l, unsigned long *, m) + LSS_INLINE _syscall0(int, sched_yield) + LSS_INLINE _syscall1(long, set_tid_address, int *, t) + LSS_INLINE _syscall1(int, setfsgid, gid_t, g) + LSS_INLINE _syscall1(int, setfsuid, uid_t, u) + LSS_INLINE _syscall1(int, setuid, uid_t, u) + LSS_INLINE _syscall1(int, setgid, gid_t, g) + LSS_INLINE _syscall2(int, setpgid, pid_t, p, + pid_t, g) + LSS_INLINE _syscall3(int, setpriority, int, a, + int, b, int, p) + LSS_INLINE _syscall3(int, setresgid, gid_t, r, + gid_t, e, gid_t, s) + LSS_INLINE _syscall3(int, setresuid, uid_t, r, + uid_t, e, uid_t, s) + LSS_INLINE _syscall2(int, setrlimit, int, r, + const struct kernel_rlimit*, l) + LSS_INLINE _syscall0(pid_t, setsid) + LSS_INLINE _syscall2(int, sigaltstack, const stack_t*, s, + const stack_t*, o) + #if defined(__NR_sigreturn) + LSS_INLINE _syscall1(int, sigreturn, unsigned long, u) + #endif + #if defined(__NR_stat) + // stat and lstat are polyfilled below when not available. + LSS_INLINE _syscall2(int, stat, const char*, f, + struct kernel_stat*, b) + #endif + #if defined(__NR_lstat) + LSS_INLINE _syscall2(int, lstat, const char*, f, + struct kernel_stat*, b) + #endif + LSS_INLINE _syscall2(int, statfs, const char*, f, + struct kernel_statfs*, b) + LSS_INLINE _syscall3(int, tgkill, pid_t, p, + pid_t, t, int, s) + LSS_INLINE _syscall2(int, tkill, pid_t, p, + int, s) + #if defined(__NR_unlink) + // unlink is polyfilled below when not available. + LSS_INLINE _syscall1(int, unlink, const char*, f) + #endif + LSS_INLINE _syscall3(ssize_t, write, int, f, + const void *, b, size_t, c) + LSS_INLINE _syscall3(ssize_t, writev, int, f, + const struct kernel_iovec*, v, size_t, c) + #if defined(__NR_getcpu) + LSS_INLINE _syscall3(long, getcpu, unsigned *, cpu, + unsigned *, node, void *, unused) + #endif + #if defined(__x86_64__) || defined(__e2k__) || \ + (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI32) + LSS_INLINE _syscall3(int, recvmsg, int, s, + struct kernel_msghdr*, m, int, f) + LSS_INLINE _syscall3(int, sendmsg, int, s, + const struct kernel_msghdr*, m, int, f) + LSS_INLINE _syscall6(int, sendto, int, s, + const void*, m, size_t, l, + int, f, + const struct kernel_sockaddr*, a, int, t) + LSS_INLINE _syscall2(int, shutdown, int, s, + int, h) + LSS_INLINE _syscall3(int, socket, int, d, + int, t, int, p) + LSS_INLINE _syscall4(int, socketpair, int, d, + int, t, int, p, int*, s) + #endif + #if defined(__NR_fadvise64) + #if defined(__x86_64__) + /* Need to make sure loff_t isn't truncated to 32-bits under x32. */ + LSS_INLINE int LSS_NAME(fadvise64)(int fd, loff_t offset, loff_t len, + int advice) { + LSS_BODY(4, int, fadvise64, LSS_SYSCALL_ARG(fd), (uint64_t)(offset), + (uint64_t)(len), LSS_SYSCALL_ARG(advice)); + } + #else + LSS_INLINE _syscall4(int, fadvise64, + int, fd, loff_t, offset, loff_t, len, int, advice) + #endif + #elif defined(__i386__) + #define __NR__fadvise64_64 __NR_fadvise64_64 + LSS_INLINE _syscall6(int, _fadvise64_64, int, fd, + unsigned, offset_lo, unsigned, offset_hi, + unsigned, len_lo, unsigned, len_hi, + int, advice) + + LSS_INLINE int LSS_NAME(fadvise64)(int fd, loff_t offset, + loff_t len, int advice) { + return LSS_NAME(_fadvise64_64)(fd, + (unsigned)offset, (unsigned)(offset >>32), + (unsigned)len, (unsigned)(len >> 32), + advice); + } + + #elif defined(__s390__) && !defined(__s390x__) + #define __NR__fadvise64_64 __NR_fadvise64_64 + struct kernel_fadvise64_64_args { + int fd; + long long offset; + long long len; + int advice; + }; + + LSS_INLINE _syscall1(int, _fadvise64_64, + struct kernel_fadvise64_64_args *args) + + LSS_INLINE int LSS_NAME(fadvise64)(int fd, loff_t offset, + loff_t len, int advice) { + struct kernel_fadvise64_64_args args = { fd, offset, len, advice }; + return LSS_NAME(_fadvise64_64)(&args); + } + #endif + #if defined(__NR_fallocate) + #if defined(__x86_64__) + /* Need to make sure loff_t isn't truncated to 32-bits under x32. */ + LSS_INLINE int LSS_NAME(fallocate)(int f, int mode, loff_t offset, + loff_t len) { + LSS_BODY(4, int, fallocate, LSS_SYSCALL_ARG(f), LSS_SYSCALL_ARG(mode), + (uint64_t)(offset), (uint64_t)(len)); + } + #elif (defined(__i386__) || (defined(__s390__) && !defined(__s390x__)) \ + || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) \ + || (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) \ + || defined(__PPC__)) + #define __NR__fallocate __NR_fallocate + LSS_INLINE _syscall6(int, _fallocate, int, fd, + int, mode, + unsigned, offset_lo, unsigned, offset_hi, + unsigned, len_lo, unsigned, len_hi) + + LSS_INLINE int LSS_NAME(fallocate)(int fd, int mode, + loff_t offset, loff_t len) { + union { loff_t off; unsigned w[2]; } o = { offset }, l = { len }; + return LSS_NAME(_fallocate)(fd, mode, o.w[0], o.w[1], l.w[0], l.w[1]); + } + #else + LSS_INLINE _syscall4(int, fallocate, + int, f, int, mode, loff_t, offset, loff_t, len) + #endif + #endif + #if defined(__NR_getrandom) + LSS_INLINE _syscall3(ssize_t, getrandom, void*, buffer, size_t, length, + unsigned int, flags) + #endif + #if defined(__NR_newfstatat) + LSS_INLINE _syscall4(int, newfstatat, int, d, + const char *, p, + struct kernel_stat*, b, int, f) + #endif + #if defined(__x86_64__) || defined(__s390x__) + LSS_INLINE int LSS_NAME(getresgid32)(gid_t *rgid, + gid_t *egid, + gid_t *sgid) { + return LSS_NAME(getresgid)(rgid, egid, sgid); + } + + LSS_INLINE int LSS_NAME(getresuid32)(uid_t *ruid, + uid_t *euid, + uid_t *suid) { + return LSS_NAME(getresuid)(ruid, euid, suid); + } + + LSS_INLINE int LSS_NAME(setfsgid32)(gid_t gid) { + return LSS_NAME(setfsgid)(gid); + } + + LSS_INLINE int LSS_NAME(setfsuid32)(uid_t uid) { + return LSS_NAME(setfsuid)(uid); + } + + LSS_INLINE int LSS_NAME(setresgid32)(gid_t rgid, gid_t egid, gid_t sgid) { + return LSS_NAME(setresgid)(rgid, egid, sgid); + } + + LSS_INLINE int LSS_NAME(setresuid32)(uid_t ruid, uid_t euid, uid_t suid) { + return LSS_NAME(setresuid)(ruid, euid, suid); + } + + LSS_INLINE int LSS_NAME(sigaction)(int signum, + const struct kernel_sigaction *act, + struct kernel_sigaction *oldact) { + #if defined(__x86_64__) + /* On x86_64, the kernel requires us to always set our own + * SA_RESTORER in order to be able to return from a signal handler. + * This function must have a "magic" signature that the "gdb" + * (and maybe the kernel?) can recognize. + */ + if (act != NULL && !(act->sa_flags & SA_RESTORER)) { + struct kernel_sigaction a = *act; + a.sa_flags |= SA_RESTORER; + a.sa_restorer = LSS_NAME(restore_rt)(); + return LSS_NAME(rt_sigaction)(signum, &a, oldact, + (KERNEL_NSIG+7)/8); + } else + #endif + return LSS_NAME(rt_sigaction)(signum, act, oldact, + (KERNEL_NSIG+7)/8); + } + + LSS_INLINE int LSS_NAME(sigpending)(struct kernel_sigset_t *set) { + return LSS_NAME(rt_sigpending)(set, (KERNEL_NSIG+7)/8); + } + + LSS_INLINE int LSS_NAME(sigsuspend)(const struct kernel_sigset_t *set) { + return LSS_NAME(rt_sigsuspend)(set, (KERNEL_NSIG+7)/8); + } + #endif + #if defined(__NR_rt_sigprocmask) + LSS_INLINE int LSS_NAME(sigprocmask)(int how, + const struct kernel_sigset_t *set, + struct kernel_sigset_t *oldset) { + return LSS_NAME(rt_sigprocmask)(how, set, oldset, (KERNEL_NSIG+7)/8); + } + #endif + #if defined(__NR_rt_sigtimedwait) + LSS_INLINE int LSS_NAME(sigtimedwait)(const struct kernel_sigset_t *set, + siginfo_t *info, + const struct timespec *timeout) { + return LSS_NAME(rt_sigtimedwait)(set, info, timeout, (KERNEL_NSIG+7)/8); + } + #endif + #if defined(__NR_wait4) + LSS_INLINE _syscall4(pid_t, wait4, pid_t, p, + int*, s, int, o, + struct kernel_rusage*, r) + #endif + #if defined(__NR_openat) + LSS_INLINE _syscall4(int, openat, int, d, const char *, p, int, f, int, m) + #endif + #if defined(__NR_unlinkat) + LSS_INLINE _syscall3(int, unlinkat, int, d, const char *, p, int, f) + #endif + #if defined(__i386__) || defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) || \ + (defined(__s390__) && !defined(__s390x__)) + #define __NR__getresgid32 __NR_getresgid32 + #define __NR__getresuid32 __NR_getresuid32 + #define __NR__setfsgid32 __NR_setfsgid32 + #define __NR__setfsuid32 __NR_setfsuid32 + #define __NR__setresgid32 __NR_setresgid32 + #define __NR__setresuid32 __NR_setresuid32 +#if defined(__ARM_EABI__) + LSS_INLINE _syscall2(int, ugetrlimit, int, r, + struct kernel_rlimit*, l) +#endif + LSS_INLINE _syscall3(int, _getresgid32, gid_t *, r, + gid_t *, e, gid_t *, s) + LSS_INLINE _syscall3(int, _getresuid32, uid_t *, r, + uid_t *, e, uid_t *, s) + LSS_INLINE _syscall1(int, _setfsgid32, gid_t, f) + LSS_INLINE _syscall1(int, _setfsuid32, uid_t, f) + LSS_INLINE _syscall3(int, _setresgid32, gid_t, r, + gid_t, e, gid_t, s) + LSS_INLINE _syscall3(int, _setresuid32, uid_t, r, + uid_t, e, uid_t, s) + + LSS_INLINE int LSS_NAME(getresgid32)(gid_t *rgid, + gid_t *egid, + gid_t *sgid) { + int rc; + if ((rc = LSS_NAME(_getresgid32)(rgid, egid, sgid)) < 0 && + LSS_ERRNO == ENOSYS) { + if ((rgid == NULL) || (egid == NULL) || (sgid == NULL)) { + return EFAULT; + } + // Clear the high bits first, since getresgid only sets 16 bits + *rgid = *egid = *sgid = 0; + rc = LSS_NAME(getresgid)(rgid, egid, sgid); + } + return rc; + } + + LSS_INLINE int LSS_NAME(getresuid32)(uid_t *ruid, + uid_t *euid, + uid_t *suid) { + int rc; + if ((rc = LSS_NAME(_getresuid32)(ruid, euid, suid)) < 0 && + LSS_ERRNO == ENOSYS) { + if ((ruid == NULL) || (euid == NULL) || (suid == NULL)) { + return EFAULT; + } + // Clear the high bits first, since getresuid only sets 16 bits + *ruid = *euid = *suid = 0; + rc = LSS_NAME(getresuid)(ruid, euid, suid); + } + return rc; + } + + LSS_INLINE int LSS_NAME(setfsgid32)(gid_t gid) { + int rc; + if ((rc = LSS_NAME(_setfsgid32)(gid)) < 0 && + LSS_ERRNO == ENOSYS) { + if ((unsigned int)gid & ~0xFFFFu) { + rc = EINVAL; + } else { + rc = LSS_NAME(setfsgid)(gid); + } + } + return rc; + } + + LSS_INLINE int LSS_NAME(setfsuid32)(uid_t uid) { + int rc; + if ((rc = LSS_NAME(_setfsuid32)(uid)) < 0 && + LSS_ERRNO == ENOSYS) { + if ((unsigned int)uid & ~0xFFFFu) { + rc = EINVAL; + } else { + rc = LSS_NAME(setfsuid)(uid); + } + } + return rc; + } + + LSS_INLINE int LSS_NAME(setresgid32)(gid_t rgid, gid_t egid, gid_t sgid) { + int rc; + if ((rc = LSS_NAME(_setresgid32)(rgid, egid, sgid)) < 0 && + LSS_ERRNO == ENOSYS) { + if ((unsigned int)rgid & ~0xFFFFu || + (unsigned int)egid & ~0xFFFFu || + (unsigned int)sgid & ~0xFFFFu) { + rc = EINVAL; + } else { + rc = LSS_NAME(setresgid)(rgid, egid, sgid); + } + } + return rc; + } + + LSS_INLINE int LSS_NAME(setresuid32)(uid_t ruid, uid_t euid, uid_t suid) { + int rc; + if ((rc = LSS_NAME(_setresuid32)(ruid, euid, suid)) < 0 && + LSS_ERRNO == ENOSYS) { + if ((unsigned int)ruid & ~0xFFFFu || + (unsigned int)euid & ~0xFFFFu || + (unsigned int)suid & ~0xFFFFu) { + rc = EINVAL; + } else { + rc = LSS_NAME(setresuid)(ruid, euid, suid); + } + } + return rc; + } + #endif + LSS_INLINE int LSS_NAME(sigemptyset)(struct kernel_sigset_t *set) { + memset(&set->sig, 0, sizeof(set->sig)); + return 0; + } + + LSS_INLINE int LSS_NAME(sigfillset)(struct kernel_sigset_t *set) { + memset(&set->sig, -1, sizeof(set->sig)); + return 0; + } + + LSS_INLINE int LSS_NAME(sigaddset)(struct kernel_sigset_t *set, + int signum) { + if (signum < 1 || signum > (int)(8*sizeof(set->sig))) { + LSS_ERRNO = EINVAL; + return -1; + } else { + set->sig[(signum - 1)/(8*sizeof(set->sig[0]))] + |= 1UL << ((signum - 1) % (8*sizeof(set->sig[0]))); + return 0; + } + } + + LSS_INLINE int LSS_NAME(sigdelset)(struct kernel_sigset_t *set, + int signum) { + if (signum < 1 || signum > (int)(8*sizeof(set->sig))) { + LSS_ERRNO = EINVAL; + return -1; + } else { + set->sig[(signum - 1)/(8*sizeof(set->sig[0]))] + &= ~(1UL << ((signum - 1) % (8*sizeof(set->sig[0])))); + return 0; + } + } + + LSS_INLINE int LSS_NAME(sigismember)(struct kernel_sigset_t *set, + int signum) { + if (signum < 1 || signum > (int)(8*sizeof(set->sig))) { + LSS_ERRNO = EINVAL; + return -1; + } else { + return !!(set->sig[(signum - 1)/(8*sizeof(set->sig[0]))] & + (1UL << ((signum - 1) % (8*sizeof(set->sig[0]))))); + } + } + #if defined(__i386__) || \ + defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) || \ + (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || \ + defined(__PPC__) || \ + (defined(__s390__) && !defined(__s390x__)) || defined(__e2k__) + #define __NR__sigaction __NR_sigaction + #define __NR__sigpending __NR_sigpending + #define __NR__sigsuspend __NR_sigsuspend + #define __NR__socketcall __NR_socketcall + LSS_INLINE _syscall2(int, fstat64, int, f, + struct kernel_stat64 *, b) + LSS_INLINE _syscall5(int, _llseek, uint, fd, + unsigned long, hi, unsigned long, lo, + loff_t *, res, uint, wh) +#if defined(__s390__) && !defined(__s390x__) + /* On s390, mmap2() arguments are passed in memory. */ + LSS_INLINE void* LSS_NAME(_mmap2)(void *s, size_t l, int p, int f, int d, + off_t o) { + unsigned long buf[6] = { (unsigned long) s, (unsigned long) l, + (unsigned long) p, (unsigned long) f, + (unsigned long) d, (unsigned long) o }; + LSS_REG(2, buf); + LSS_BODY(void*, mmap2, "0"(__r2)); + } +#else + #define __NR__mmap2 __NR_mmap2 + LSS_INLINE _syscall6(void*, _mmap2, void*, s, + size_t, l, int, p, + int, f, int, d, + off_t, o) +#endif + LSS_INLINE _syscall3(int, _sigaction, int, s, + const struct kernel_old_sigaction*, a, + struct kernel_old_sigaction*, o) + LSS_INLINE _syscall1(int, _sigpending, unsigned long*, s) + #ifdef __PPC__ + LSS_INLINE _syscall1(int, _sigsuspend, unsigned long, s) + #else + LSS_INLINE _syscall3(int, _sigsuspend, const void*, a, + int, b, + unsigned long, s) + #endif + LSS_INLINE _syscall2(int, stat64, const char *, p, + struct kernel_stat64 *, b) + + LSS_INLINE int LSS_NAME(sigaction)(int signum, + const struct kernel_sigaction *act, + struct kernel_sigaction *oldact) { + int old_errno = LSS_ERRNO; + int rc; + struct kernel_sigaction a; + if (act != NULL) { + a = *act; + #ifdef __i386__ + /* On i386, the kernel requires us to always set our own + * SA_RESTORER when using realtime signals. Otherwise, it does not + * know how to return from a signal handler. This function must have + * a "magic" signature that the "gdb" (and maybe the kernel?) can + * recognize. + * Apparently, a SA_RESTORER is implicitly set by the kernel, when + * using non-realtime signals. + * + * TODO: Test whether ARM needs a restorer + */ + if (!(a.sa_flags & SA_RESTORER)) { + a.sa_flags |= SA_RESTORER; + a.sa_restorer = (a.sa_flags & SA_SIGINFO) + ? LSS_NAME(restore_rt)() : LSS_NAME(restore)(); + } + #endif + } + rc = LSS_NAME(rt_sigaction)(signum, act ? &a : act, oldact, + (KERNEL_NSIG+7)/8); + if (rc < 0 && LSS_ERRNO == ENOSYS) { + struct kernel_old_sigaction oa, ooa, *ptr_a = &oa, *ptr_oa = &ooa; + if (!act) { + ptr_a = NULL; + } else { + oa.sa_handler_ = act->sa_handler_; + memcpy(&oa.sa_mask, &act->sa_mask, sizeof(oa.sa_mask)); + #ifndef __mips__ + oa.sa_restorer = act->sa_restorer; + #endif + oa.sa_flags = act->sa_flags; + } + if (!oldact) { + ptr_oa = NULL; + } + LSS_ERRNO = old_errno; + rc = LSS_NAME(_sigaction)(signum, ptr_a, ptr_oa); + if (rc == 0 && oldact) { + if (act) { + memcpy(oldact, act, sizeof(*act)); + } else { + memset(oldact, 0, sizeof(*oldact)); + } + oldact->sa_handler_ = ptr_oa->sa_handler_; + oldact->sa_flags = ptr_oa->sa_flags; + memcpy(&oldact->sa_mask, &ptr_oa->sa_mask, sizeof(ptr_oa->sa_mask)); + #ifndef __mips__ + oldact->sa_restorer = ptr_oa->sa_restorer; + #endif + } + } + return rc; + } + + LSS_INLINE int LSS_NAME(sigpending)(struct kernel_sigset_t *set) { + int old_errno = LSS_ERRNO; + int rc = LSS_NAME(rt_sigpending)(set, (KERNEL_NSIG+7)/8); + if (rc < 0 && LSS_ERRNO == ENOSYS) { + LSS_ERRNO = old_errno; + LSS_NAME(sigemptyset)(set); + rc = LSS_NAME(_sigpending)(&set->sig[0]); + } + return rc; + } + + LSS_INLINE int LSS_NAME(sigsuspend)(const struct kernel_sigset_t *set) { + int olderrno = LSS_ERRNO; + int rc = LSS_NAME(rt_sigsuspend)(set, (KERNEL_NSIG+7)/8); + if (rc < 0 && LSS_ERRNO == ENOSYS) { + LSS_ERRNO = olderrno; + rc = LSS_NAME(_sigsuspend)( + #ifndef __PPC__ + set, 0, + #endif + set->sig[0]); + } + return rc; + } + #endif + #if defined(__i386__) || \ + defined(__ARM_ARCH_3__) || defined(__ARM_EABI__) || \ + (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || \ + defined(__PPC__) || \ + (defined(__s390__) && !defined(__s390x__)) + /* On these architectures, implement mmap() with mmap2(). */ + LSS_INLINE void* LSS_NAME(mmap)(void *s, size_t l, int p, int f, int d, + int64_t o) { + if (o % 4096) { + LSS_ERRNO = EINVAL; + return (void *) -1; + } + return LSS_NAME(_mmap2)(s, l, p, f, d, (o / 4096)); + } + #elif defined(__s390x__) + /* On s390x, mmap() arguments are passed in memory. */ + LSS_INLINE void* LSS_NAME(mmap)(void *s, size_t l, int p, int f, int d, + int64_t o) { + unsigned long buf[6] = { (unsigned long) s, (unsigned long) l, + (unsigned long) p, (unsigned long) f, + (unsigned long) d, (unsigned long) o }; + LSS_REG(2, buf); + LSS_BODY(void*, mmap, "0"(__r2)); + } + #elif defined(__x86_64__) + /* Need to make sure __off64_t isn't truncated to 32-bits under x32. */ + LSS_INLINE void* LSS_NAME(mmap)(void *s, size_t l, int p, int f, int d, + int64_t o) { + LSS_BODY(6, void*, mmap, LSS_SYSCALL_ARG(s), LSS_SYSCALL_ARG(l), + LSS_SYSCALL_ARG(p), LSS_SYSCALL_ARG(f), + LSS_SYSCALL_ARG(d), (uint64_t)(o)); + } + #else + /* Remaining 64-bit architectures. */ + LSS_INLINE _syscall6(void*, mmap, void*, addr, size_t, length, int, prot, + int, flags, int, fd, int64_t, offset) + #endif + #if defined(__PPC__) + #undef LSS_SC_LOADARGS_0 + #define LSS_SC_LOADARGS_0(dummy...) + #undef LSS_SC_LOADARGS_1 + #define LSS_SC_LOADARGS_1(arg1) \ + __sc_4 = (unsigned long) (arg1) + #undef LSS_SC_LOADARGS_2 + #define LSS_SC_LOADARGS_2(arg1, arg2) \ + LSS_SC_LOADARGS_1(arg1); \ + __sc_5 = (unsigned long) (arg2) + #undef LSS_SC_LOADARGS_3 + #define LSS_SC_LOADARGS_3(arg1, arg2, arg3) \ + LSS_SC_LOADARGS_2(arg1, arg2); \ + __sc_6 = (unsigned long) (arg3) + #undef LSS_SC_LOADARGS_4 + #define LSS_SC_LOADARGS_4(arg1, arg2, arg3, arg4) \ + LSS_SC_LOADARGS_3(arg1, arg2, arg3); \ + __sc_7 = (unsigned long) (arg4) + #undef LSS_SC_LOADARGS_5 + #define LSS_SC_LOADARGS_5(arg1, arg2, arg3, arg4, arg5) \ + LSS_SC_LOADARGS_4(arg1, arg2, arg3, arg4); \ + __sc_8 = (unsigned long) (arg5) + #undef LSS_SC_BODY + #define LSS_SC_BODY(nr, type, opt, args...) \ + long __sc_ret, __sc_err; \ + { \ + register unsigned long __sc_0 __asm__ ("r0") = __NR_socketcall; \ + register unsigned long __sc_3 __asm__ ("r3") = opt; \ + register unsigned long __sc_4 __asm__ ("r4"); \ + register unsigned long __sc_5 __asm__ ("r5"); \ + register unsigned long __sc_6 __asm__ ("r6"); \ + register unsigned long __sc_7 __asm__ ("r7"); \ + register unsigned long __sc_8 __asm__ ("r8"); \ + LSS_SC_LOADARGS_##nr(args); \ + __asm__ __volatile__ \ + ("stwu 1, -48(1)\n\t" \ + "stw 4, 20(1)\n\t" \ + "stw 5, 24(1)\n\t" \ + "stw 6, 28(1)\n\t" \ + "stw 7, 32(1)\n\t" \ + "stw 8, 36(1)\n\t" \ + "addi 4, 1, 20\n\t" \ + "sc\n\t" \ + "mfcr %0" \ + : "=&r" (__sc_0), \ + "=&r" (__sc_3), "=&r" (__sc_4), \ + "=&r" (__sc_5), "=&r" (__sc_6), \ + "=&r" (__sc_7), "=&r" (__sc_8) \ + : LSS_ASMINPUT_##nr \ + : "cr0", "ctr", "memory"); \ + __sc_ret = __sc_3; \ + __sc_err = __sc_0; \ + } \ + LSS_RETURN(type, __sc_ret, __sc_err) + + LSS_INLINE ssize_t LSS_NAME(recvmsg)(int s,struct kernel_msghdr *msg, + int flags){ + LSS_SC_BODY(3, ssize_t, 17, s, msg, flags); + } + + LSS_INLINE ssize_t LSS_NAME(sendmsg)(int s, + const struct kernel_msghdr *msg, + int flags) { + LSS_SC_BODY(3, ssize_t, 16, s, msg, flags); + } + + // TODO(csilvers): why is this ifdef'ed out? +#if 0 + LSS_INLINE ssize_t LSS_NAME(sendto)(int s, const void *buf, size_t len, + int flags, + const struct kernel_sockaddr *to, + unsigned int tolen) { + LSS_BODY(6, ssize_t, 11, s, buf, len, flags, to, tolen); + } +#endif + + LSS_INLINE int LSS_NAME(shutdown)(int s, int how) { + LSS_SC_BODY(2, int, 13, s, how); + } + + LSS_INLINE int LSS_NAME(socket)(int domain, int type, int protocol) { + LSS_SC_BODY(3, int, 1, domain, type, protocol); + } + + LSS_INLINE int LSS_NAME(socketpair)(int d, int type, int protocol, + int sv[2]) { + LSS_SC_BODY(4, int, 8, d, type, protocol, sv); + } + #endif + #if defined(__ARM_EABI__) || defined (__aarch64__) + LSS_INLINE _syscall3(ssize_t, recvmsg, int, s, struct kernel_msghdr*, msg, + int, flags) + LSS_INLINE _syscall3(ssize_t, sendmsg, int, s, const struct kernel_msghdr*, + msg, int, flags) + LSS_INLINE _syscall6(ssize_t, sendto, int, s, const void*, buf, size_t,len, + int, flags, const struct kernel_sockaddr*, to, + unsigned int, tolen) + LSS_INLINE _syscall2(int, shutdown, int, s, int, how) + LSS_INLINE _syscall3(int, socket, int, domain, int, type, int, protocol) + LSS_INLINE _syscall4(int, socketpair, int, d, int, type, int, protocol, + int*, sv) + #endif + #if defined(__i386__) || defined(__ARM_ARCH_3__) || \ + (defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI32) || \ + defined(__s390__) + #define __NR__socketcall __NR_socketcall + LSS_INLINE _syscall2(int, _socketcall, int, c, + va_list, a) + LSS_INLINE int LSS_NAME(socketcall)(int op, ...) { + int rc; + va_list ap; + va_start(ap, op); + rc = LSS_NAME(_socketcall)(op, ap); + va_end(ap); + return rc; + } + + LSS_INLINE ssize_t LSS_NAME(recvmsg)(int s,struct kernel_msghdr *msg, + int flags){ + return (ssize_t)LSS_NAME(socketcall)(17, s, msg, flags); + } + + LSS_INLINE ssize_t LSS_NAME(sendmsg)(int s, + const struct kernel_msghdr *msg, + int flags) { + return (ssize_t)LSS_NAME(socketcall)(16, s, msg, flags); + } + + LSS_INLINE ssize_t LSS_NAME(sendto)(int s, const void *buf, size_t len, + int flags, + const struct kernel_sockaddr *to, + unsigned int tolen) { + return (ssize_t)LSS_NAME(socketcall)(11, s, buf, len, flags, to, tolen); + } + + LSS_INLINE int LSS_NAME(shutdown)(int s, int how) { + return LSS_NAME(socketcall)(13, s, how); + } + + LSS_INLINE int LSS_NAME(socket)(int domain, int type, int protocol) { + return LSS_NAME(socketcall)(1, domain, type, protocol); + } + + LSS_INLINE int LSS_NAME(socketpair)(int d, int type, int protocol, + int sv[2]) { + return LSS_NAME(socketcall)(8, d, type, protocol, sv); + } + #endif + #if defined(__NR_fstatat64) + LSS_INLINE _syscall4(int, fstatat64, int, d, + const char *, p, + struct kernel_stat64 *, b, int, f) + #endif + #if defined(__NR_waitpid) + // waitpid is polyfilled below when not available. + LSS_INLINE _syscall3(pid_t, waitpid, pid_t, p, + int*, s, int, o) + #endif + #if defined(__mips__) + /* sys_pipe() on MIPS has non-standard calling conventions, as it returns + * both file handles through CPU registers. + */ + LSS_INLINE int LSS_NAME(pipe)(int *p) { + register unsigned long __v0 __asm__("$2") = __NR_pipe; + register unsigned long __v1 __asm__("$3"); + register unsigned long __r7 __asm__("$7"); + __asm__ __volatile__ ("syscall\n" + : "=r"(__v0), "=r"(__v1), "=r" (__r7) + : "0"(__v0) + : "$8", "$9", "$10", "$11", "$12", + "$13", "$14", "$15", "$24", "$25", "memory"); + if (__r7) { + unsigned long __errnovalue = __v0; + LSS_ERRNO = __errnovalue; + return -1; + } else { + p[0] = __v0; + p[1] = __v1; + return 0; + } + } + #elif defined(__NR_pipe) + // pipe is polyfilled below when not available. + LSS_INLINE _syscall1(int, pipe, int *, p) + #endif + #if defined(__NR_pipe2) + LSS_INLINE _syscall2(int, pipe2, int *, pipefd, int, flags) + #endif + /* TODO(csilvers): see if ppc can/should support this as well */ + #if defined(__i386__) || defined(__ARM_ARCH_3__) || \ + defined(__ARM_EABI__) || \ + (defined(__mips__) && _MIPS_SIM != _MIPS_SIM_ABI64) || \ + (defined(__s390__) && !defined(__s390x__)) + #define __NR__statfs64 __NR_statfs64 + #define __NR__fstatfs64 __NR_fstatfs64 + LSS_INLINE _syscall3(int, _statfs64, const char*, p, + size_t, s,struct kernel_statfs64*, b) + LSS_INLINE _syscall3(int, _fstatfs64, int, f, + size_t, s,struct kernel_statfs64*, b) + LSS_INLINE int LSS_NAME(statfs64)(const char *p, + struct kernel_statfs64 *b) { + return LSS_NAME(_statfs64)(p, sizeof(*b), b); + } + LSS_INLINE int LSS_NAME(fstatfs64)(int f,struct kernel_statfs64 *b) { + return LSS_NAME(_fstatfs64)(f, sizeof(*b), b); + } + #endif + + LSS_INLINE int LSS_NAME(execv)(const char *path, const char *const argv[]) { + extern char **environ; + return LSS_NAME(execve)(path, argv, (const char *const *)environ); + } + + LSS_INLINE pid_t LSS_NAME(gettid)(void) { + pid_t tid = LSS_NAME(_gettid)(); + if (tid != -1) { + return tid; + } + return LSS_NAME(getpid)(); + } + + LSS_INLINE void *LSS_NAME(mremap)(void *old_address, size_t old_size, + size_t new_size, int flags, ...) { + va_list ap; + void *new_address, *rc; + va_start(ap, flags); + new_address = va_arg(ap, void *); + rc = LSS_NAME(_mremap)(old_address, old_size, new_size, + flags, new_address); + va_end(ap); + return rc; + } + + LSS_INLINE int LSS_NAME(ptrace_detach)(pid_t pid) { + /* PTRACE_DETACH can sometimes forget to wake up the tracee and it + * then sends job control signals to the real parent, rather than to + * the tracer. We reduce the risk of this happening by starting a + * whole new time slice, and then quickly sending a SIGCONT signal + * right after detaching from the tracee. + * + * We use tkill to ensure that we only issue a wakeup for the thread being + * detached. Large multi threaded apps can take a long time in the kernel + * processing SIGCONT. + */ + int rc, err; + LSS_NAME(sched_yield)(); + rc = LSS_NAME(ptrace)(PTRACE_DETACH, pid, (void *)0, (void *)0); + err = LSS_ERRNO; + LSS_NAME(tkill)(pid, SIGCONT); + /* Old systems don't have tkill */ + if (LSS_ERRNO == ENOSYS) + LSS_NAME(kill)(pid, SIGCONT); + LSS_ERRNO = err; + return rc; + } + + LSS_INLINE int LSS_NAME(raise)(int sig) { + return LSS_NAME(kill)(LSS_NAME(getpid)(), sig); + } + + LSS_INLINE int LSS_NAME(setpgrp)(void) { + return LSS_NAME(setpgid)(0, 0); + } + + #if defined(__x86_64__) + /* Need to make sure loff_t isn't truncated to 32-bits under x32. */ + LSS_INLINE ssize_t LSS_NAME(pread64)(int f, void *b, size_t c, loff_t o) { + LSS_BODY(4, ssize_t, pread64, LSS_SYSCALL_ARG(f), LSS_SYSCALL_ARG(b), + LSS_SYSCALL_ARG(c), (uint64_t)(o)); + } + + LSS_INLINE ssize_t LSS_NAME(pwrite64)(int f, const void *b, size_t c, + loff_t o) { + LSS_BODY(4, ssize_t, pwrite64, LSS_SYSCALL_ARG(f), LSS_SYSCALL_ARG(b), + LSS_SYSCALL_ARG(c), (uint64_t)(o)); + } + + LSS_INLINE int LSS_NAME(readahead)(int f, loff_t o, unsigned c) { + LSS_BODY(3, int, readahead, LSS_SYSCALL_ARG(f), (uint64_t)(o), + LSS_SYSCALL_ARG(c)); + } + #elif defined(__mips__) && _MIPS_SIM == _MIPS_SIM_ABI64 + LSS_INLINE _syscall4(ssize_t, pread64, int, f, + void *, b, size_t, c, + loff_t, o) + LSS_INLINE _syscall4(ssize_t, pwrite64, int, f, + const void *, b, size_t, c, + loff_t, o) + LSS_INLINE _syscall3(int, readahead, int, f, + loff_t, o, unsigned, c) + #else + #define __NR__pread64 __NR_pread64 + #define __NR__pwrite64 __NR_pwrite64 + #define __NR__readahead __NR_readahead + #if defined(__ARM_EABI__) || defined(__mips__) + /* On ARM and MIPS, a 64-bit parameter has to be in an even-odd register + * pair. Hence these calls ignore their fourth argument (r3) so that their + * fifth and sixth make such a pair (r4,r5). + */ + #define LSS_LLARG_PAD 0, + LSS_INLINE _syscall6(ssize_t, _pread64, int, f, + void *, b, size_t, c, + unsigned, skip, unsigned, o1, unsigned, o2) + LSS_INLINE _syscall6(ssize_t, _pwrite64, int, f, + const void *, b, size_t, c, + unsigned, skip, unsigned, o1, unsigned, o2) + LSS_INLINE _syscall5(int, _readahead, int, f, + unsigned, skip, + unsigned, o1, unsigned, o2, size_t, c) + #else + #define LSS_LLARG_PAD + LSS_INLINE _syscall5(ssize_t, _pread64, int, f, + void *, b, size_t, c, unsigned, o1, + unsigned, o2) + LSS_INLINE _syscall5(ssize_t, _pwrite64, int, f, + const void *, b, size_t, c, unsigned, o1, + long, o2) + LSS_INLINE _syscall4(int, _readahead, int, f, + unsigned, o1, unsigned, o2, size_t, c) + #endif + /* We force 64bit-wide parameters onto the stack, then access each + * 32-bit component individually. This guarantees that we build the + * correct parameters independent of the native byte-order of the + * underlying architecture. + */ + LSS_INLINE ssize_t LSS_NAME(pread64)(int fd, void *buf, size_t count, + loff_t off) { + union { loff_t off; unsigned arg[2]; } o = { off }; + return LSS_NAME(_pread64)(fd, buf, count, + LSS_LLARG_PAD o.arg[0], o.arg[1]); + } + LSS_INLINE ssize_t LSS_NAME(pwrite64)(int fd, const void *buf, + size_t count, loff_t off) { + union { loff_t off; unsigned arg[2]; } o = { off }; + return LSS_NAME(_pwrite64)(fd, buf, count, + LSS_LLARG_PAD o.arg[0], o.arg[1]); + } + LSS_INLINE int LSS_NAME(readahead)(int fd, loff_t off, int len) { + union { loff_t off; unsigned arg[2]; } o = { off }; + return LSS_NAME(_readahead)(fd, LSS_LLARG_PAD o.arg[0], o.arg[1], len); + } + #endif +#endif + +/* + * Polyfills for deprecated syscalls. + */ + +#if !defined(__NR_dup2) + LSS_INLINE int LSS_NAME(dup2)(int s, int d) { + return LSS_NAME(dup3)(s, d, 0); + } +#endif + +#if !defined(__NR_open) + LSS_INLINE int LSS_NAME(open)(const char *pathname, int flags, int mode) { + return LSS_NAME(openat)(AT_FDCWD, pathname, flags, mode); + } +#endif + +#if !defined(__NR_unlink) + LSS_INLINE int LSS_NAME(unlink)(const char *pathname) { + return LSS_NAME(unlinkat)(AT_FDCWD, pathname, 0); + } +#endif + +#if !defined(__NR_readlink) + LSS_INLINE int LSS_NAME(readlink)(const char *pathname, char *buffer, + size_t size) { + return LSS_NAME(readlinkat)(AT_FDCWD, pathname, buffer, size); + } +#endif + +#if !defined(__NR_pipe) + LSS_INLINE int LSS_NAME(pipe)(int *pipefd) { + return LSS_NAME(pipe2)(pipefd, 0); + } +#endif + +#if !defined(__NR_poll) + LSS_INLINE int LSS_NAME(poll)(struct kernel_pollfd *fds, unsigned int nfds, + int timeout) { + struct kernel_timespec timeout_ts; + struct kernel_timespec *timeout_ts_p = NULL; + + if (timeout >= 0) { + timeout_ts.tv_sec = timeout / 1000; + timeout_ts.tv_nsec = (timeout % 1000) * 1000000; + timeout_ts_p = &timeout_ts; + } + return LSS_NAME(ppoll)(fds, nfds, timeout_ts_p, NULL, 0); + } +#endif + +#if !defined(__NR_stat) + LSS_INLINE int LSS_NAME(stat)(const char *pathname, + struct kernel_stat *buf) { + return LSS_NAME(newfstatat)(AT_FDCWD, pathname, buf, 0); + } +#endif +#if !defined(__NR_lstat) + LSS_INLINE int LSS_NAME(lstat)(const char *pathname, + struct kernel_stat *buf) { + return LSS_NAME(newfstatat)(AT_FDCWD, pathname, buf, AT_SYMLINK_NOFOLLOW); + } +#endif + +#if !defined(__NR_waitpid) + LSS_INLINE pid_t LSS_NAME(waitpid)(pid_t pid, int *status, int options) { + return LSS_NAME(wait4)(pid, status, options, 0); + } +#endif + +#if !defined(__NR_fork) +// TODO: define this in an arch-independant way instead of inlining the clone +// syscall body. + +# if defined(__aarch64__) + LSS_INLINE pid_t LSS_NAME(fork)(void) { + // No fork syscall on aarch64 - implement by means of the clone syscall. + // Note that this does not reset glibc's cached view of the PID/TID, so + // some glibc interfaces might go wrong in the forked subprocess. + int flags = SIGCHLD; + void *child_stack = NULL; + void *parent_tidptr = NULL; + void *newtls = NULL; + void *child_tidptr = NULL; + + LSS_REG(0, flags); + LSS_REG(1, child_stack); + LSS_REG(2, parent_tidptr); + LSS_REG(3, newtls); + LSS_REG(4, child_tidptr); + LSS_BODY(pid_t, clone, "r"(__r0), "r"(__r1), "r"(__r2), "r"(__r3), + "r"(__r4)); + } +# elif defined(__x86_64__) + LSS_INLINE pid_t LSS_NAME(fork)(void) { + // Android disallows the fork syscall on x86_64 - implement by means of the + // clone syscall as above for aarch64. + int flags = SIGCHLD; + void *child_stack = NULL; + void *parent_tidptr = NULL; + void *newtls = NULL; + void *child_tidptr = NULL; + + LSS_BODY(5, pid_t, clone, LSS_SYSCALL_ARG(flags), + LSS_SYSCALL_ARG(child_stack), LSS_SYSCALL_ARG(parent_tidptr), + LSS_SYSCALL_ARG(newtls), LSS_SYSCALL_ARG(child_tidptr)); + } +# else +# error missing fork polyfill for this architecture +# endif +#endif + +/* These restore the original values of these macros saved by the + * corresponding #pragma push_macro near the top of this file. */ +#pragma pop_macro("stat64") +#pragma pop_macro("fstat64") +#pragma pop_macro("lstat64") +#pragma pop_macro("pread64") +#pragma pop_macro("pwrite64") +#pragma pop_macro("getdents64") + +#if defined(__cplusplus) && !defined(SYS_CPLUSPLUS) +} +#endif + +#endif +#endif diff --git a/shared/sentry/external/crashpad/third_party/lss/lss/tests/.gitignore b/shared/sentry/external/crashpad/third_party/lss/lss/tests/.gitignore new file mode 100644 index 000000000..89c5d3c93 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/lss/lss/tests/.gitignore @@ -0,0 +1,4 @@ +/*_test + +# Some tests create temp files. +/tempfile.* diff --git a/shared/sentry/external/crashpad/third_party/lss/lss/tests/Makefile b/shared/sentry/external/crashpad/third_party/lss/lss/tests/Makefile new file mode 100644 index 000000000..a6f1ec6a5 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/lss/lss/tests/Makefile @@ -0,0 +1,140 @@ +# Copyright 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. + +top_srcdir ?= .. + +DEF_FLAGS = -g -pipe +DEF_WFLAGS = -Wall +CFLAGS ?= $(DEF_FLAGS) +CXXFLAGS ?= $(DEF_FLAGS) +CFLAGS += $(DEF_WFLAGS) -Wstrict-prototypes +CXXFLAGS += $(DEF_WFLAGS) +CPPFLAGS += -I$(top_srcdir) +# We use static linking here so that if people run through qemu/etc... by hand, +# it's a lot easier to run/debug. Same for strace output. +LDFLAGS += -static + +TESTS = \ + fallocate \ + getrandom \ + lstat \ + sigaction \ + sigtimedwait \ + stat \ + unlink \ + +all: check + +%_test: %.c test_skel.h $(top_srcdir)/linux_syscall_support.h + $(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $< + +%_test: %.cc test_skel.h $(top_srcdir)/linux_syscall_support.h + $(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) -o $@ $< + +%_run: %_test + @t=$(@:_run=_test); \ + echo "./$$t"; \ + env -i ./$$t; \ + exit_status=$$?; \ + if [ $$exit_status = 77 ]; then \ + echo "SKIP: $$t"; \ + elif [ $$exit_status != 0 ]; then \ + echo "FAIL: $$t"; \ + env -i strace -f -v ./$$t; \ + echo "TRY: gdb -q -ex r -ex bt ./$$t"; \ + exit 1; \ + fi + +ALL_TEST_TARGETS = $(TESTS:=_test) +compile_tests: $(ALL_TEST_TARGETS) + +ALL_RUN_TARGETS = $(TESTS:=_run) +check: $(ALL_RUN_TARGETS) + +# The "tempfile" targets are the names we use with temp files. +# Clean them out in case some tests crashed in the middle. +clean: + rm -f *~ *.o tempfile.* a.out core $(ALL_TEST_TARGETS) + +.SUFFIXES: +.PHONY: all check clean compile_tests +.SECONDARY: $(ALL_TEST_TARGETS) + +# Try to cross-compile the tests for all our supported arches. We test with +# both gcc and clang. We don't support execution (yet?), but just compiling +# & linking helps catch common bugs. +.PHONY: cross compile_cross +cross_compile: + @echo "Running: $(MAKE) $@ CC='$(CC)' CXX='$(CXX)'"; \ + if (echo '#include ' | $(CC) -x c -c -o /dev/null -) 2>/dev/null; then \ + $(MAKE) -s clean; \ + $(MAKE) -k --no-print-directory compile_tests; \ + else \ + echo "Skipping $(CC) test: not installed"; \ + fi; \ + echo + +# The names here are a best effort. Not easy to probe for. +cross: + @for cc in \ + "x86_64-pc-linux-gnu-gcc" \ + "i686-pc-linux-gnu-gcc" \ + "x86_64-pc-linux-gnu-gcc -mx32" \ + "armv7a-unknown-linux-gnueabi-gcc -marm -mhard-float" \ + "armv7a-unknown-linux-gnueabi-gcc -mthumb -mhard-float" \ + "powerpc-unknown-linux-gnu-gcc" \ + "aarch64-unknown-linux-gnu-gcc" \ + "mips64-unknown-linux-gnu-gcc -mabi=64" \ + "mips64-unknown-linux-gnu-gcc -mabi=32" \ + "mips64-unknown-linux-gnu-gcc -mabi=n32" \ + "s390-ibm-linux-gnu-gcc" \ + "s390x-ibm-linux-gnu-gcc" \ + ; do \ + cxx=`echo "$$cc" | sed 's:-gcc:-g++:'`; \ + $(MAKE) --no-print-directory CC="$$cc" CXX="$$cxx" cross_compile; \ + \ + sysroot=`$$cc --print-sysroot 2>/dev/null`; \ + gccdir=`$$cc -print-file-name=libgcc.a 2>/dev/null`; \ + gccdir=`dirname "$$gccdir"`; \ + : Skip building for clang for mips/o32 and s390/31-bit until it works.; \ + case $$cc in \ + mips64*-mabi=32) continue;; \ + s390-*) continue;; \ + esac; \ + set -- $$cc; \ + tuple=$${1%-gcc}; \ + shift; \ + cc="clang -target $$tuple $$*"; \ + : Assume the build system is x86_64 based, so ignore the sysroot.; \ + case $$tuple in \ + x86_64*) ;; \ + *) cc="$$cc --sysroot $$sysroot -B$$gccdir -L$$gccdir";; \ + esac; \ + cxx=`echo "$$cc" | sed 's:^clang:clang++:'`; \ + $(MAKE) --no-print-directory CC="$$cc" CXX="$$cxx" cross_compile; \ + done diff --git a/shared/sentry/external/crashpad/third_party/lss/lss/tests/README.md b/shared/sentry/external/crashpad/third_party/lss/lss/tests/README.md new file mode 100644 index 000000000..45af3c37e --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/lss/lss/tests/README.md @@ -0,0 +1,52 @@ +# LSS Tests + +## Source Layout + +The general layout of the tests: +* [test_skel.h]: Test helpers for common checks/etc... +* xxx.c: Unittest for the xxx syscall (e.g. `open.c`). +* [Makefile]: New tests should be registered in the `TESTS` variable. + +## Test Guidelines + +The unittest itself generally follows the conventions: +* Written in C (unless a very specific language behavior is needed). +* You should only need to `#include "test_skel.h"`. For new system headers, try + to add them here rather than copying to exact unittest (if possible). + It might slow compilation down slightly, but makes the code easier to manage. + Make sure it is included first. +* Use `assert()` on everything to check return values. +* Use `sys_xxx()` to access the syscall via LSS (compared to `xxx()` which tends + to come from the C library). +* If you need a tempfile, use `tempfile.XXXXXX` for templates with helpers like + `mkstemp`. Try to clean them up when you're done with them. + These will be created in the cwd, but that's fine. +* Don't worry about trying to verify the kernel/C library API and various edge + cases. The goal of LSS is to make sure that we pass args along correctly to + the syscall only. +* Make sure to leave comments in the test so it's clear what behavior you're + trying to verify (and how). + +Feel free to extend [test_skel.h] with more helpers if they're useful to more +than one test. + +If you're looking for a simple example, start with [unlink.c](./unlink.c). +You should be able to copy this over and replace the content of `main()`. + +## Running The Tests + +Simply run `make`. This will compile & execute all the tests on your local +system. A standard `make clean` will clean up all the objects. + +If you need to debug something, then the programs are simply named `xxx_test` +and can easily be thrown into `gdb ./xxx_test`. + +We have rudimentary cross-compile testing via gcc and clang. Try running +`make cross` -- for any toolchains you don't have available, it should skip +things automatically. This only verifies the compilation & linking stages +though. + +The cross-compilers can be created using . + +[Makefile]: ./Makefile +[test_skel.h]: ./test_skel.h diff --git a/shared/sentry/external/crashpad/third_party/lss/lss/tests/fallocate.c b/shared/sentry/external/crashpad/third_party/lss/lss/tests/fallocate.c new file mode 100644 index 000000000..f6589070f --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/lss/lss/tests/fallocate.c @@ -0,0 +1,69 @@ +/* Copyright 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 "test_skel.h" + +int main(int argc, char *argv[]) { + int fd = 0, mode = 0; + loff_t offset = 0, len = 0; + + // Bad file descriptor. + fd = -1; + assert(sys_fallocate(fd, mode, offset, len) == -1); + assert(errno == EBADF); + + char filename[] = "tempfile.XXXXXX"; + fd = mkstemp(filename); + assert(fd >= 0); + + // Invalid len. + assert(sys_fallocate(fd, mode, offset, len) == -1); + assert(errno == EINVAL); + + // Small offset and length succeeds. + len = 4096; + assert(sys_fallocate(fd, mode, offset, len) == 0); + + // Large offset succeeds and isn't truncated. + offset = 1llu + UINT32_MAX; + assert(sys_fallocate(fd, mode , offset, len) == 0); + +#if defined(__NR_fstat64) + struct kernel_stat64 st; + assert(sys_fstat64(fd, &st) == 0); +#else + struct kernel_stat st; + assert(sys_fstat(fd, &st) == 0); +#endif + assert(st.st_size == offset + len); + + sys_unlink(filename); + + return 0; +} diff --git a/shared/sentry/external/crashpad/third_party/lss/lss/tests/getrandom.c b/shared/sentry/external/crashpad/third_party/lss/lss/tests/getrandom.c new file mode 100644 index 000000000..eb1f16228 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/lss/lss/tests/getrandom.c @@ -0,0 +1,59 @@ +/* Copyright 2020, 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 "test_skel.h" + +#define BUFFER_SIZE 256 + +int main(int argc, char *argv[]) { + char buffer[BUFFER_SIZE]; + // Zero it out so we can check later that it's at least not all 0s. + memset(buffer, 0, BUFFER_SIZE); + bool buffer_contains_all_zeros = true; + + // Don't bother passing any flags. (If we're using lss, we might not have the + // right header files with the flags defined anyway, and we'd have to copy + // this in here too, and risk getting out of sync in yet another way.) + const ssize_t r = sys_getrandom(buffer, BUFFER_SIZE, 0); + + // Make sure it either worked, or that it's just not supported. + assert(r == BUFFER_SIZE || errno == ENOSYS); + + if (r == BUFFER_SIZE) { + // If all the bytes are 0, it didn't really work. + for (size_t i = 0; i < BUFFER_SIZE; ++i) { + if (buffer[i] != 0) { + buffer_contains_all_zeros = false; + } + } + assert(!buffer_contains_all_zeros); + } + + return 0; +} diff --git a/shared/sentry/external/crashpad/third_party/lss/lss/tests/lstat.c b/shared/sentry/external/crashpad/third_party/lss/lss/tests/lstat.c new file mode 100644 index 000000000..33848a981 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/lss/lss/tests/lstat.c @@ -0,0 +1,97 @@ +/* Copyright 2021, 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 "test_skel.h" + +int main(int argc, char *argv[]) { + int exit_status = 0; + + // Get two unique paths to play with. + char foo[] = "tempfile.XXXXXX"; + char bar[] = "tempfile.XXXXXX"; + int fd_foo = mkstemp(foo); + int fd_bar = mkstemp(bar); + assert(fd_foo != -1); + assert(fd_bar != -1); + + // Then delete foo. + assert(sys_unlink(foo) == 0); + + // Now make foo a symlink to bar. + assert(symlink(bar, foo) == 0); + + // Make sure sys_stat() and sys_lstat() implementation return different + // information. + + // We need to check our stat syscalls for EOVERFLOW, as sometimes the integer + // types used in the stat structures are too small to fit the actual value. + // E.g. on some systems st_ino is 32-bit, but some filesystems have 64-bit + // inodes. + + struct kernel_stat lstat_info; + int rc = sys_lstat(foo, &lstat_info); + if (rc < 0 && errno == EOVERFLOW) { + // Bail out since we had an overflow in the stat structure. + exit_status = SKIP_TEST_EXIT_STATUS; + goto cleanup; + } + assert(rc == 0); + + struct kernel_stat stat_info; + rc = sys_stat(foo, &stat_info); + if (rc < 0 && errno == EOVERFLOW) { + // Bail out since we had an overflow in the stat structure. + exit_status = SKIP_TEST_EXIT_STATUS; + goto cleanup; + } + assert(rc == 0); + + struct kernel_stat bar_stat_info; + rc = sys_stat(bar, &bar_stat_info); + if (rc < 0 && errno == EOVERFLOW) { + // Bail out since we had an overflow in the stat structure. + exit_status = SKIP_TEST_EXIT_STATUS; + goto cleanup; + } + assert(rc == 0); + + // lstat should produce information about a symlink. + assert((lstat_info.st_mode & S_IFMT) == S_IFLNK); + + // stat-ing foo and bar should produce the same inode. + assert(stat_info.st_ino == bar_stat_info.st_ino); + + // lstat-ing foo should give a different inode than stat-ing foo. + assert(stat_info.st_ino != lstat_info.st_ino); + +cleanup: + sys_unlink(foo); + sys_unlink(bar); + return exit_status; +} diff --git a/shared/sentry/external/crashpad/third_party/lss/lss/tests/sigaction.c b/shared/sentry/external/crashpad/third_party/lss/lss/tests/sigaction.c new file mode 100644 index 000000000..f247aca7c --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/lss/lss/tests/sigaction.c @@ -0,0 +1,54 @@ +/* Copyright 2020, 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 "test_skel.h" + +void test_handler(int sig) {} + +int main(int argc, char *argv[]) { + const size_t kSigsetSize = sizeof(struct kernel_sigset_t); + struct kernel_sigaction action = {}; + // Invalid signal returns EINVAL. + assert(sys_rt_sigaction(SIGKILL, &action, NULL, kSigsetSize) == -1); + assert(errno == EINVAL); + + // Set an action. + action.sa_handler_ = test_handler; + action.sa_flags = SA_SIGINFO; + assert(sys_sigemptyset(&action.sa_mask) == 0); + assert(sys_sigaddset(&action.sa_mask, SIGPIPE) == 0); + assert(sys_rt_sigaction(SIGSEGV, &action, NULL, kSigsetSize) == 0); + + // Retrieve the action. + struct kernel_sigaction old_action = {}; + assert(sys_rt_sigaction(SIGSEGV, NULL, &old_action, kSigsetSize) == 0); + assert(memcmp(&action, &old_action, sizeof(action)) == 0); + + return 0; +} diff --git a/shared/sentry/external/crashpad/third_party/lss/lss/tests/sigtimedwait.c b/shared/sentry/external/crashpad/third_party/lss/lss/tests/sigtimedwait.c new file mode 100644 index 000000000..ee2f740fe --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/lss/lss/tests/sigtimedwait.c @@ -0,0 +1,58 @@ +/* Copyright 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 "test_skel.h" + +int main(int argc, char *argv[]) { + + struct kernel_sigset_t sigset = {}; + siginfo_t siginfo = {}; + struct timespec timeout = {}; + + // Invalid timeouts. + timeout.tv_sec = -1; + assert(sys_sigtimedwait(&sigset, &siginfo, &timeout) == -1); + assert(errno == EINVAL); + + // Expired timeouts. + timeout.tv_sec = 0; + assert(sys_sigtimedwait(&sigset, &siginfo, &timeout) == -1); + assert(errno == EAGAIN); + + // Success. + const int kTestSignal = SIGCONT; + assert(sys_sigemptyset(&sigset) == 0); + assert(sys_sigaddset(&sigset, kTestSignal) == 0); + assert(sys_sigprocmask(SIG_BLOCK, &sigset, NULL) == 0); + assert(raise(kTestSignal) == 0); + assert(sys_sigtimedwait(&sigset, &siginfo, &timeout) == kTestSignal); + assert(siginfo.si_signo == kTestSignal); + + return 0; +} diff --git a/shared/sentry/external/crashpad/third_party/lss/lss/tests/stat.c b/shared/sentry/external/crashpad/third_party/lss/lss/tests/stat.c new file mode 100644 index 000000000..48cac620c --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/lss/lss/tests/stat.c @@ -0,0 +1,67 @@ +/* Copyright 2021, 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 "test_skel.h" + +int main(int argc, char *argv[]) { + int exit_status = 0; + + // Get two unique paths to play with. + char foo[] = "tempfile.XXXXXX"; + int fd_foo = mkstemp(foo); + assert(fd_foo != -1); + + // Make sure it exists. + assert(access(foo, F_OK) == 0); + + // Make sure sys_stat() and a libc stat() implementation return the same + // information. + struct stat libc_stat; + assert(stat(foo, &libc_stat) == 0); + + struct kernel_stat raw_stat; + // We need to check our stat syscall for EOVERFLOW, as sometimes the integer + // types used in the stat structures are too small to fit the actual value. + // E.g. on some systems st_ino is 32-bit, but some filesystems have 64-bit + // inodes. + int rc = sys_stat(foo, &raw_stat); + if (rc < 0 && errno == EOVERFLOW) { + // Bail out since we had an overflow in the stat structure. + exit_status = SKIP_TEST_EXIT_STATUS; + goto cleanup; + } + assert(rc == 0); + + assert(libc_stat.st_ino == raw_stat.st_ino); + + +cleanup: + sys_unlink(foo); + return exit_status; +} diff --git a/shared/sentry/external/crashpad/third_party/lss/lss/tests/test_skel.h b/shared/sentry/external/crashpad/third_party/lss/lss/tests/test_skel.h new file mode 100644 index 000000000..238c48d1d --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/lss/lss/tests/test_skel.h @@ -0,0 +1,74 @@ +/* Copyright 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. + */ + +/* + * Make sure it's defined before including anything else. A number of syscalls + * are GNU extensions and rely on being exported by glibc. + */ +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +/* + * Make sure the assert checks aren't removed as all the unittests are based + * on them. + */ +#undef NDEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "linux_syscall_support.h" + +#define SKIP_TEST_EXIT_STATUS 77 + +void assert_buffers_eq_len(const void *buf1, const void *buf2, size_t len) { + const uint8_t *u8_1 = (const uint8_t *)buf1; + const uint8_t *u8_2 = (const uint8_t *)buf2; + size_t i; + + for (i = 0; i < len; ++i) { + if (u8_1[i] != u8_2[i]) + printf("offset %zu: %02x != %02x\n", i, u8_1[i], u8_2[i]); + } +} +#define assert_buffers_eq(obj1, obj2) assert_buffers_eq_len(obj1, obj2, sizeof(*obj1)) diff --git a/shared/sentry/external/crashpad/third_party/lss/lss/tests/unlink.c b/shared/sentry/external/crashpad/third_party/lss/lss/tests/unlink.c new file mode 100644 index 000000000..70c8bc9a5 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/lss/lss/tests/unlink.c @@ -0,0 +1,48 @@ +/* Copyright 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. + */ + +#include "test_skel.h" + +int main(int argc, char *argv[]) { + // Get a unique path to play with. + char foo[] = "tempfile.XXXXXX"; + int fd = mkstemp(foo); + assert(fd != -1); + + // Make sure it exists. + assert(access(foo, F_OK) == 0); + + // Then delete it. + assert(sys_unlink(foo) == 0); + + // Make sure it's gone. + assert(access(foo, F_OK) != 0); + + return 0; +} diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/BUILD.gn b/shared/sentry/external/crashpad/third_party/mini_chromium/BUILD.gn new file mode 100644 index 000000000..ccd70c3d5 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/BUILD.gn @@ -0,0 +1,66 @@ +# Copyright 2017 The Crashpad Authors. All rights reserved. +# +# 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("../../build/crashpad_buildconfig.gni") + +group("base") { + if (crashpad_is_in_chromium) { + public_deps = [ "//base" ] + } else if (crashpad_is_standalone) { + public_deps = [ "mini_chromium/base" ] + } else if (crashpad_is_in_fuchsia) { + public_deps = [ "//third_party/mini_chromium/base" ] + } else if (crashpad_is_external) { + public_deps = [ "../../../../mini_chromium/mini_chromium/base" ] + } else if (crashpad_is_in_dart) { + public_deps = [ "//third_party/mini_chromium/mini_chromium/base" ] + } +} + +group("base_test_support") { + testonly = true + + if (crashpad_is_in_chromium) { + public_deps = [ "//base/test:test_support" ] + } +} + +group("build") { + if (crashpad_is_in_chromium) { + # Chromium has no build target. + } else if (crashpad_is_standalone) { + public_deps = [ "mini_chromium/build" ] + } else if (crashpad_is_in_fuchsia) { + public_deps = [ "//third_party/mini_chromium/build" ] + } else if (crashpad_is_external) { + public_deps = [ "../../../../mini_chromium/mini_chromium/build" ] + } else if (crashpad_is_in_dart) { + public_deps = [ "//third_party/mini_chromium/mini_chromium/build" ] + } +} + +group("chromeos_buildflags") { + if (crashpad_is_in_chromium) { + public_deps = [ "//build:chromeos_buildflags" ] + } else if (crashpad_is_standalone) { + public_deps = [ "mini_chromium/build:chromeos_buildflags" ] + } else if (crashpad_is_in_fuchsia) { + public_deps = [ "//third_party/mini_chromium/build:chromeos_buildflags" ] + } else if (crashpad_is_external) { + public_deps = [ "../../../../mini_chromium/mini_chromium/build:chromeos_buildflags" ] + } else if (crashpad_is_in_dart) { + public_deps = [ "//third_party/mini_chromium/mini_chromium/build:chromeos_buildflags" ] + } +} + diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/CMakeLists.txt b/shared/sentry/external/crashpad/third_party/mini_chromium/CMakeLists.txt new file mode 100644 index 000000000..4577c1d12 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/CMakeLists.txt @@ -0,0 +1,212 @@ +add_library(mini_chromium STATIC) +function(mc_append_sources) + list(TRANSFORM ARGN PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/mini_chromium/base/") + target_sources(mini_chromium PRIVATE ${ARGN}) +endfunction() + +target_sources(mini_chromium PRIVATE build/chromeos_buildflags.h) +mc_append_sources( + ../build/build_config.h + atomicops.h + atomicops_internals_atomicword_compat.h + atomicops_internals_portable.h + auto_reset.h + bit_cast.h + check.h + check_op.h + compiler_specific.h + cxx17_backports.h + debug/alias.cc + debug/alias.h + files/file_path.cc + files/file_path.h + files/file_util.h + files/scoped_file.cc + files/scoped_file.h + format_macros.h + logging.cc + logging.h + memory/free_deleter.h + memory/page_size.h + memory/scoped_policy.h + metrics/histogram_functions.h + metrics/histogram_macros.h + metrics/persistent_histogram_allocator.h + notreached.h + numerics/checked_math.h + numerics/checked_math_impl.h + numerics/clamped_math.h + numerics/clamped_math_impl.h + numerics/safe_conversions.h + numerics/safe_conversions_arm_impl.h + numerics/safe_conversions_impl.h + numerics/safe_math.h + numerics/safe_math_arm_impl.h + numerics/safe_math_clang_gcc_impl.h + numerics/safe_math_shared_impl.h + process/memory.cc + process/memory.h + rand_util.cc + rand_util.h + scoped_clear_last_error.h + scoped_generic.h + strings/string_number_conversions.cc + strings/string_number_conversions.h + strings/string_piece.h + strings/string_util.h + strings/stringprintf.cc + strings/stringprintf.h + strings/sys_string_conversions.h + strings/utf_string_conversion_utils.h + strings/utf_string_conversions.cc + strings/utf_string_conversions.h + synchronization/condition_variable.h + synchronization/lock.cc + synchronization/lock.h + synchronization/lock_impl.h + sys_byteorder.h + template_util.h + third_party/icu/icu_utf.cc + third_party/icu/icu_utf.h + threading/thread_local_storage.cc + threading/thread_local_storage.h +) + +if(NOT MINGW) + mc_append_sources( + strings/utf_string_conversion_utils.cc + ) +else() + mc_append_sources( + ../../utf_string_conversion_utils.mingw.cc + ) +endif() + +if(APPLE AND NOT IOS) + mc_append_sources( + mac/close_nocancel.cc + mac/foundation_util.h + mac/foundation_util.mm + mac/mach_logging.cc + mac/mach_logging.h + mac/scoped_cftyperef.h + mac/scoped_ioobject.h + mac/scoped_launch_data.h + mac/scoped_mach_port.cc + mac/scoped_mach_port.h + mac/scoped_mach_vm.cc + mac/scoped_mach_vm.h + mac/scoped_nsautorelease_pool.h + mac/scoped_nsautorelease_pool.mm + mac/scoped_nsobject.h + mac/scoped_typeref.h + strings/sys_string_conversions_mac.mm + ) +elseif(IOS) + mc_append_sources( + mac/foundation_util.h + mac/foundation_util.mm + mac/mach_logging.cc + mac/mach_logging.h + mac/scoped_cftyperef.h + mac/scoped_mach_port.cc + mac/scoped_mach_port.h + mac/scoped_mach_vm.cc + mac/scoped_mach_vm.h + mac/scoped_nsautorelease_pool.h + mac/scoped_nsautorelease_pool.mm + mac/scoped_nsobject.h + mac/scoped_typeref.h + strings/sys_string_conversions_mac.mm + ) +endif() + +if(WIN32) + mc_append_sources( + memory/page_size_win.cc + scoped_clear_last_error_win.cc + strings/string_util_win.cc + strings/string_util_win.h + synchronization/lock_impl_win.cc + threading/thread_local_storage_win.cc + ) +else() + mc_append_sources( + files/file_util_posix.cc + memory/page_size_posix.cc + posix/eintr_wrapper.h + posix/safe_strerror.cc + posix/safe_strerror.h + strings/string_util_posix.h + synchronization/condition_variable_posix.cc + synchronization/lock_impl_posix.cc + threading/thread_local_storage_posix.cc + ) +endif() + +if(APPLE AND NOT IOS) + target_link_libraries(mini_chromium PUBLIC + "-framework ApplicationServices" + "-framework CoreFoundation" + "-framework Foundation" + "-framework IOKit" + "-framework Security" + ) +elseif(IOS) + target_link_libraries(mini_chromium PUBLIC + "-framework CoreFoundation" + "-framework CoreGraphics" + "-framework CoreText" + "-framework Foundation" + "-framework Security" + ) +endif() + +if(LINUX) + target_link_libraries(mini_chromium PRIVATE pthread) +endif() + +target_include_directories(mini_chromium PUBLIC + "$" + $ +) +target_include_directories(mini_chromium PUBLIC + "$" +) +target_link_libraries(mini_chromium + PRIVATE + $ +) + +if(WIN32) + target_link_libraries(mini_chromium PRIVATE advapi32 kernel32) + if(MSVC) + target_compile_options(mini_chromium PRIVATE "/wd4201" "/wd4996") + target_compile_definitions(mini_chromium PRIVATE + NOMINMAX + UNICODE + WIN32_LEAN_AND_MEAN + _CRT_SECURE_NO_WARNINGS + _HAS_EXCEPTIONS=0 + _UNICODE + ) + elseif(MINGW) + target_compile_options(mini_chromium PRIVATE + "-municode" + "-Wno-format" + "-Wno-unknown-pragmas" + ) + endif() +endif() + +add_library(crashpad::mini_chromium ALIAS mini_chromium) + +crashpad_install_target(mini_chromium) +crashpad_install_dev(DIRECTORY mini_chromium + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/crashpad" + FILES_MATCHING PATTERN "*.h" +) +crashpad_install_dev(DIRECTORY build + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/crashpad/mini_chromium" + FILES_MATCHING PATTERN "*.h" +) diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/README.crashpad b/shared/sentry/external/crashpad/third_party/mini_chromium/README.crashpad new file mode 100644 index 000000000..37d507bf2 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/README.crashpad @@ -0,0 +1,17 @@ +Name: mini_chromium +Short Name: mini_chromium +URL: https://chromium.googlesource.com/chromium/mini_chromium/ +Revision: See DEPS +License: BSD 3-clause +License File: mini_chromium/LICENSE +Security Critical: yes + +Description: +mini_chromium is a small collection of useful low-level (“baseâ€) routines from +the Chromium open-source project at https://www.chromium.org/Home. Chromium is +large, sprawling, full of dependencies, and a web browser. mini_chromium is +small, self-contained, and a library. mini_chromium is especially useful as a +dependency of other code that wishes to use Chromium’s base routines. + +Local Modifications: +None diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/build/chromeos_buildflags.h b/shared/sentry/external/crashpad/third_party/mini_chromium/build/chromeos_buildflags.h new file mode 100644 index 000000000..72714a24a --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/build/chromeos_buildflags.h @@ -0,0 +1,13 @@ +// This header should be generated by `build/write_buildflag_header.py`, +// but we rather hardcode it to simplify CMake scripts, as we do not +// support building on chromeos anyway. + +#ifndef MINI_CHROMIUM_BUILD_CHROMEOS_BUILDFLAGS_H_ +#define MINI_CHROMIUM_BUILD_CHROMEOS_BUILDFLAGS_H_ + +#include "build/buildflag.h" + +#define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_CHROMEOS_LACROS() (0) +#define MINI_CHROMIUM_INTERNAL_BUILDFLAG_VALUE_IS_CHROMEOS_ASH() (0) + +#endif // MINI_CHROMIUM_BUILD_CHROMEOS_BUILDFLAGS_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/.gitattributes b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/.gitattributes new file mode 100644 index 000000000..c9c89d500 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/.gitattributes @@ -0,0 +1,23 @@ +# Copyright 2019 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +*.c text eol=lf +*.cc text eol=lf +*.gn text eol=lf +*.gni text eol=lf +*.gyp text eol=lf +*.gypi text eol=lf +*.h text eol=lf +*.m text eol=lf +*.md text eol=lf +*.mm text eol=lf +*.plist text eol=lf +*.py text eol=lf +.clang-format text eol=lf +.gitattributes text eol=lf +.gitignore text eol=lf +/AUTHORS text eol=lf +/LICENSE text eol=lf +/codereview.settings text eol=lf +README.chromium text eol=lf diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/.gitignore b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/.gitignore new file mode 100644 index 000000000..7bd9b295c --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/.gitignore @@ -0,0 +1,17 @@ +# Copyright 2012 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +*.Makefile +*.ninja +*.pyc +*.target.mk +*.xcodeproj +*~ +.*.sw? +.DS_Store +.gdb_history +.gdbinit +/Makefile +/out +/xcodebuild diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/.style.yapf b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/.style.yapf new file mode 100644 index 000000000..c9970ee73 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/.style.yapf @@ -0,0 +1,7 @@ +# Copyright 2022 The Crashpad Authors. All rights reserved. +# +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +[style] +based_on_style = google diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/AUTHORS b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/AUTHORS new file mode 100644 index 000000000..37d59f20c --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/AUTHORS @@ -0,0 +1,5 @@ +# Copyright 2012 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +# See https://chromium.googlesource.com/chromium/chromium/+/trunk/AUTHORS diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/BUILD.gn b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/BUILD.gn new file mode 100644 index 000000000..226ec832d --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/BUILD.gn @@ -0,0 +1,7 @@ +# Copyright 2016 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +group("mini_chromium") { + deps = [ "//base" ] +} diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/LICENSE b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/LICENSE new file mode 100644 index 000000000..36980134e --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/LICENSE @@ -0,0 +1,27 @@ +// Copyright 2006-2008 The Chromium Authors. 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. diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/README.md b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/README.md new file mode 100644 index 000000000..563c1a9f4 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/README.md @@ -0,0 +1,38 @@ + + +# mini_chromium + +This is mini_chromium, a small collection of useful low-level (“baseâ€) routines +from the [Chromium open-source project](https://dev.chromium.org/Home). Chromium +is large, sprawling, full of dependencies, and a web browser. mini_chromium is +small, self-contained, and a library. mini_chromium is especially useful as a +dependency of other code that wishes to use Chromium’s base routines. By using +mini_chromium, other projects’ code can function in a standalone environment +outside of Chromium without having to treat all of Chromium as a dependency. +When building as part of Chromium, those projects’ code can use Chromium’s own +(non-mini_chromium) base implementation. + +Code provided in mini_chromium provides the same interface as the equivalent +code in Chromium. + +While it’s a goal of mini_chromium to maintain interface compatibility with +Chromium’s base library for the interfaces it does implement, there’s no +requirement that it use the same implementations as Chromium’s base library. +Many of the implementations used in mini_chromium are identical to Chromium’s, +but many others have been modified to eliminate dependencies that are not +desired in mini_chromium, and a few are completely distinct from Chromium’s +altogether. Additionally, when mini_chromium provides an interface in the form +of a file or class present in Chromium, it’s not bound to provide all functions, +methods, or types that the Chromium equivalent does. The differences noted above +notwithstanding, the interfaces exposed by mini_chromium’s base are and must +remain a strict subset of Chromium’s. + +[Crashpad](https://crashpad.chromium.org/) is the chief consumer of +mini_chromium. + +Mark Mentovai
    +mark@chromium.org diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/BUILD.gn b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/BUILD.gn new file mode 100644 index 000000000..bd3663841 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/BUILD.gn @@ -0,0 +1,170 @@ +# Copyright 2016 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +import("../build/platform.gni") + +static_library("base") { + sources = [ + "atomicops.h", + "atomicops_internals_atomicword_compat.h", + "atomicops_internals_portable.h", + "auto_reset.h", + "bit_cast.h", + "check.h", + "check_op.h", + "compiler_specific.h", + "cxx17_backports.h", + "debug/alias.cc", + "debug/alias.h", + "files/file_path.cc", + "files/file_path.h", + "files/file_util.h", + "files/scoped_file.cc", + "files/scoped_file.h", + "format_macros.h", + "logging.cc", + "logging.h", + "memory/free_deleter.h", + "memory/page_size.h", + "memory/scoped_policy.h", + "metrics/histogram_functions.h", + "metrics/histogram_macros.h", + "metrics/persistent_histogram_allocator.h", + "notreached.h", + "numerics/checked_math.h", + "numerics/checked_math_impl.h", + "numerics/clamped_math.h", + "numerics/clamped_math_impl.h", + "numerics/safe_conversions.h", + "numerics/safe_conversions_arm_impl.h", + "numerics/safe_conversions_impl.h", + "numerics/safe_math.h", + "numerics/safe_math_arm_impl.h", + "numerics/safe_math_clang_gcc_impl.h", + "numerics/safe_math_shared_impl.h", + "process/memory.cc", + "process/memory.h", + "rand_util.cc", + "rand_util.h", + "scoped_clear_last_error.h", + "scoped_generic.h", + "strings/string_number_conversions.cc", + "strings/string_number_conversions.h", + "strings/string_piece.h", + "strings/string_util.h", + "strings/stringprintf.cc", + "strings/stringprintf.h", + "strings/sys_string_conversions.h", + "strings/utf_string_conversion_utils.cc", + "strings/utf_string_conversion_utils.h", + "strings/utf_string_conversions.cc", + "strings/utf_string_conversions.h", + "synchronization/condition_variable.h", + "synchronization/lock.cc", + "synchronization/lock.h", + "synchronization/lock_impl.h", + "sys_byteorder.h", + "template_util.h", + "third_party/icu/icu_utf.cc", + "third_party/icu/icu_utf.h", + "threading/thread_local_storage.cc", + "threading/thread_local_storage.h", + ] + + if (mini_chromium_is_posix || mini_chromium_is_fuchsia) { + sources += [ + "files/file_util_posix.cc", + "memory/page_size_posix.cc", + "posix/eintr_wrapper.h", + "posix/safe_strerror.cc", + "posix/safe_strerror.h", + "strings/string_util_posix.h", + "synchronization/condition_variable_posix.cc", + "synchronization/lock_impl_posix.cc", + "threading/thread_local_storage_posix.cc", + ] + } + + if (mini_chromium_is_mac) { + sources += [ + "mac/close_nocancel.cc", + "mac/foundation_util.h", + "mac/foundation_util.mm", + "mac/mach_logging.cc", + "mac/mach_logging.h", + "mac/scoped_cftyperef.h", + "mac/scoped_ioobject.h", + "mac/scoped_launch_data.h", + "mac/scoped_mach_port.cc", + "mac/scoped_mach_port.h", + "mac/scoped_mach_vm.cc", + "mac/scoped_mach_vm.h", + "mac/scoped_nsautorelease_pool.h", + "mac/scoped_nsautorelease_pool.mm", + "mac/scoped_nsobject.h", + "mac/scoped_typeref.h", + "strings/sys_string_conversions_mac.mm", + ] + frameworks = [ + "ApplicationServices.framework", + "CoreFoundation.framework", + "Foundation.framework", + "IOKit.framework", + "Security.framework", + ] + } else if (mini_chromium_is_ios) { + sources += [ + "mac/foundation_util.h", + "mac/foundation_util.mm", + "mac/mach_logging.cc", + "mac/mach_logging.h", + "mac/scoped_cftyperef.h", + "mac/scoped_mach_port.cc", + "mac/scoped_mach_port.h", + "mac/scoped_mach_vm.cc", + "mac/scoped_mach_vm.h", + "mac/scoped_nsautorelease_pool.h", + "mac/scoped_nsautorelease_pool.mm", + "mac/scoped_nsobject.h", + "mac/scoped_typeref.h", + "strings/sys_string_conversions_mac.mm", + ] + frameworks = [ + "CoreFoundation.framework", + "CoreGraphics.framework", + "CoreText.framework", + "Foundation.framework", + "Security.framework", + ] + } else if (mini_chromium_is_win) { + sources += [ + "memory/page_size_win.cc", + "scoped_clear_last_error_win.cc", + "strings/string_util_win.cc", + "strings/string_util_win.h", + "synchronization/lock_impl_win.cc", + "threading/thread_local_storage_win.cc", + ] + libs = [ "advapi32.lib" ] + } else if (mini_chromium_is_fuchsia) { + sources += [ + "fuchsia/fuchsia_logging.cc", + "fuchsia/fuchsia_logging.h", + ] + + if (defined(is_fuchsia_tree) && is_fuchsia_tree) { + deps = [ "//zircon/system/ulib/syslog" ] + } else { + deps = [ "//third_party/fuchsia/sdk/$host_os-amd64/pkg/syslog" ] + } + } + + public_configs = [ "../build:mini_chromium_config" ] + + public_deps = [ "../build" ] + + if (mini_chromium_is_android) { + libs = [ "log" ] + } +} diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/atomicops.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/atomicops.h new file mode 100644 index 000000000..b6058e550 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/atomicops.h @@ -0,0 +1,178 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// For atomic operations on reference counts, see atomic_refcount.h. +// For atomic operations on sequence numbers, see atomic_sequence_num.h. + +// The routines exported by this module are subtle. If you use them, even if +// you get the code right, it will depend on careful reasoning about atomicity +// and memory ordering; it will be less readable, and harder to maintain. If +// you plan to use these routines, you should have a good reason, such as solid +// evidence that performance would otherwise suffer, or there being no +// alternative. You should assume only properties explicitly guaranteed by the +// specifications in this file. You are almost certainly _not_ writing code +// just for the x86; if you assume x86 semantics, x86 hardware bugs and +// implementations on other archtectures will cause your code to break. If you +// do not know what you are doing, avoid these routines, and use a Mutex. +// +// It is incorrect to make direct assignments to/from an atomic variable. +// You should use one of the Load or Store routines. The NoBarrier +// versions are provided when no barriers are needed: +// NoBarrier_Store() +// NoBarrier_Load() +// Although there are currently no compiler enforcement, you are encouraged +// to use these. +// + +#ifndef MINI_CHROMIUM_BASE_ATOMICOPS_H_ +#define MINI_CHROMIUM_BASE_ATOMICOPS_H_ + +#include + +// Small C++ header which defines implementation specific macros used to +// identify the STL implementation. +// - libc++: captures __config for _LIBCPP_VERSION +// - libstdc++: captures bits/c++config.h for __GLIBCXX__ +#include + +#include "build/build_config.h" + +#if BUILDFLAG(IS_WIN) && defined(ARCH_CPU_64_BITS) +// windows.h #defines this (only on x64). This causes problems because the +// public API also uses MemoryBarrier at the public name for this fence. So, on +// X64, undef it, and call its documented +// (http://msdn.microsoft.com/en-us/library/windows/desktop/ms684208.aspx) +// implementation directly. +#undef MemoryBarrier +#endif + +namespace base { +namespace subtle { + +typedef int32_t Atomic32; +#ifdef ARCH_CPU_64_BITS +// We need to be able to go between Atomic64 and AtomicWord implicitly. This +// means Atomic64 and AtomicWord should be the same type on 64-bit. +#if defined(__ILP32__) +typedef int64_t Atomic64; +#else +typedef intptr_t Atomic64; +#endif +#endif + +// Use AtomicWord for a machine-sized pointer. It will use the Atomic32 or +// Atomic64 routines below, depending on your architecture. +typedef intptr_t AtomicWord; + +// Atomically execute: +// result = *ptr; +// if (*ptr == old_value) +// *ptr = new_value; +// return result; +// +// I.e., replace "*ptr" with "new_value" if "*ptr" used to be "old_value". +// Always return the old value of "*ptr" +// +// This routine implies no memory barriers. +Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value); + +// Atomically store new_value into *ptr, returning the previous value held in +// *ptr. This routine implies no memory barriers. +Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, Atomic32 new_value); + +// Atomically increment *ptr by "increment". Returns the new value of +// *ptr with the increment applied. This routine implies no memory barriers. +Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, Atomic32 increment); + +Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment); + +// These following lower-level operations are typically useful only to people +// implementing higher-level synchronization operations like spinlocks, +// mutexes, and condition-variables. They combine CompareAndSwap(), a load, or +// a store with appropriate memory-ordering instructions. "Acquire" operations +// ensure that no later memory access can be reordered ahead of the operation. +// "Release" operations ensure that no previous memory access can be reordered +// after the operation. "Barrier" operations have both "Acquire" and "Release" +// semantics. A MemoryBarrier() has "Barrier" semantics, but does no memory +// access. +Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value); +Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value); + +void MemoryBarrier(); +void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value); +void Acquire_Store(volatile Atomic32* ptr, Atomic32 value); +void Release_Store(volatile Atomic32* ptr, Atomic32 value); + +Atomic32 NoBarrier_Load(volatile const Atomic32* ptr); +Atomic32 Acquire_Load(volatile const Atomic32* ptr); +Atomic32 Release_Load(volatile const Atomic32* ptr); + +// 64-bit atomic operations (only available on 64-bit processors). +#ifdef ARCH_CPU_64_BITS +Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value); +Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, Atomic64 new_value); +Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, Atomic64 increment); +Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, Atomic64 increment); + +Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value); +Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value); +void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value); +void Acquire_Store(volatile Atomic64* ptr, Atomic64 value); +void Release_Store(volatile Atomic64* ptr, Atomic64 value); +Atomic64 NoBarrier_Load(volatile const Atomic64* ptr); +Atomic64 Acquire_Load(volatile const Atomic64* ptr); +Atomic64 Release_Load(volatile const Atomic64* ptr); +#endif // ARCH_CPU_64_BITS + +} // namespace subtle +} // namespace base + +// The following x86 CPU features are used in atomicops_internals_x86_gcc.h, but +// this file is duplicated inside of Chrome: protobuf and tcmalloc rely on the +// struct being present at link time. Some parts of Chrome can currently use the +// portable interface whereas others still use GCC one. The include guards are +// the same as in atomicops_internals_x86_gcc.cc. +#if defined(__i386__) || defined(__x86_64__) +// This struct is not part of the public API of this module; clients may not +// use it. (However, it's exported via BASE_EXPORT because clients implicitly +// do use it at link time by inlining these functions.) +// Features of this x86. Values may not be correct before main() is run, +// but are set conservatively. +struct AtomicOps_x86CPUFeatureStruct { + bool has_amd_lock_mb_bug; // Processor has AMD memory-barrier bug; do lfence + // after acquire compare-and-swap. + // The following fields are unused by Chrome's base implementation but are + // still used by copies of the same code in other parts of the code base. This + // causes an ODR violation, and the other code is likely reading invalid + // memory. + // TODO(jfb) Delete these fields once the rest of the Chrome code base doesn't + // depend on them. + bool has_sse2; // Processor has SSE2. + bool has_cmpxchg16b; // Processor supports cmpxchg16b instruction. +}; +extern struct AtomicOps_x86CPUFeatureStruct AtomicOps_Internalx86CPUFeatures; +#endif + +#include "base/atomicops_internals_portable.h" + +// On some platforms we need additional declarations to make +// AtomicWord compatible with our other Atomic* types. +#if BUILDFLAG(IS_APPLE) +#include "base/atomicops_internals_atomicword_compat.h" +#endif + +#endif // MINI_CHROMIUM_BASE_ATOMICOPS_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/atomicops_internals_atomicword_compat.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/atomicops_internals_atomicword_compat.h new file mode 100644 index 000000000..d5b8caaf6 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/atomicops_internals_atomicword_compat.h @@ -0,0 +1,100 @@ +// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file is an internal atomic implementation, use base/atomicops.h instead. + +#ifndef MINI_CHROMIUM_BASE_ATOMICOPS_INTERNALS_ATOMICWORD_COMPAT_H_ +#define MINI_CHROMIUM_BASE_ATOMICOPS_INTERNALS_ATOMICWORD_COMPAT_H_ + +// AtomicWord is a synonym for intptr_t, and Atomic32 is a synonym for int32, +// which in turn means int. On some LP32 platforms, intptr_t is an int, but +// on others, it's a long. When AtomicWord and Atomic32 are based on different +// fundamental types, their pointers are incompatible. +// +// This file defines function overloads to allow both AtomicWord and Atomic32 +// data to be used with this interface. +// +// On LP64 platforms, AtomicWord and Atomic64 are both always long, +// so this problem doesn't occur. + +#if !defined(ARCH_CPU_64_BITS) + +namespace base { +namespace subtle { + +inline AtomicWord NoBarrier_CompareAndSwap(volatile AtomicWord* ptr, + AtomicWord old_value, + AtomicWord new_value) { + return NoBarrier_CompareAndSwap( + reinterpret_cast(ptr), old_value, new_value); +} + +inline AtomicWord NoBarrier_AtomicExchange(volatile AtomicWord* ptr, + AtomicWord new_value) { + return NoBarrier_AtomicExchange( + reinterpret_cast(ptr), new_value); +} + +inline AtomicWord NoBarrier_AtomicIncrement(volatile AtomicWord* ptr, + AtomicWord increment) { + return NoBarrier_AtomicIncrement( + reinterpret_cast(ptr), increment); +} + +inline AtomicWord Barrier_AtomicIncrement(volatile AtomicWord* ptr, + AtomicWord increment) { + return Barrier_AtomicIncrement( + reinterpret_cast(ptr), increment); +} + +inline AtomicWord Acquire_CompareAndSwap(volatile AtomicWord* ptr, + AtomicWord old_value, + AtomicWord new_value) { + return base::subtle::Acquire_CompareAndSwap( + reinterpret_cast(ptr), old_value, new_value); +} + +inline AtomicWord Release_CompareAndSwap(volatile AtomicWord* ptr, + AtomicWord old_value, + AtomicWord new_value) { + return base::subtle::Release_CompareAndSwap( + reinterpret_cast(ptr), old_value, new_value); +} + +inline void NoBarrier_Store(volatile AtomicWord *ptr, AtomicWord value) { + NoBarrier_Store( + reinterpret_cast(ptr), value); +} + +inline void Acquire_Store(volatile AtomicWord* ptr, AtomicWord value) { + return base::subtle::Acquire_Store( + reinterpret_cast(ptr), value); +} + +inline void Release_Store(volatile AtomicWord* ptr, AtomicWord value) { + return base::subtle::Release_Store( + reinterpret_cast(ptr), value); +} + +inline AtomicWord NoBarrier_Load(volatile const AtomicWord *ptr) { + return NoBarrier_Load( + reinterpret_cast(ptr)); +} + +inline AtomicWord Acquire_Load(volatile const AtomicWord* ptr) { + return base::subtle::Acquire_Load( + reinterpret_cast(ptr)); +} + +inline AtomicWord Release_Load(volatile const AtomicWord* ptr) { + return base::subtle::Release_Load( + reinterpret_cast(ptr)); +} + +} // namespace base::subtle +} // namespace base + +#endif // !defined(ARCH_CPU_64_BITS) + +#endif // MINI_CHROMIUM_BASE_ATOMICOPS_INTERNALS_ATOMICWORD_COMPAT_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/atomicops_internals_portable.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/atomicops_internals_portable.h new file mode 100644 index 000000000..db610523c --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/atomicops_internals_portable.h @@ -0,0 +1,227 @@ +// Copyright (c) 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// This file is an internal atomic implementation, use atomicops.h instead. +// +// This implementation uses C++11 atomics' member functions. The code base is +// currently written assuming atomicity revolves around accesses instead of +// C++11's memory locations. The burden is on the programmer to ensure that all +// memory locations accessed atomically are never accessed non-atomically (tsan +// should help with this). +// +// TODO(jfb) Modify the atomicops.h API and user code to declare atomic +// locations as truly atomic. See the static_assert below. +// +// Of note in this implementation: +// * All NoBarrier variants are implemented as relaxed. +// * All Barrier variants are implemented as sequentially-consistent. +// * Compare exchange's failure ordering is always the same as the success one +// (except for release, which fails as relaxed): using a weaker ordering is +// only valid under certain uses of compare exchange. +// * Acquire store doesn't exist in the C11 memory model, it is instead +// implemented as a relaxed store followed by a sequentially consistent +// fence. +// * Release load doesn't exist in the C11 memory model, it is instead +// implemented as sequentially consistent fence followed by a relaxed load. +// * Atomic increment is expected to return the post-incremented value, whereas +// C11 fetch add returns the previous value. The implementation therefore +// needs to increment twice (which the compiler should be able to detect and +// optimize). + +#ifndef MINI_CHROMIUM_BASE_ATOMICOPS_INTERNALS_PORTABLE_H_ +#define MINI_CHROMIUM_BASE_ATOMICOPS_INTERNALS_PORTABLE_H_ + +#include + +namespace base { +namespace subtle { + +// This implementation is transitional and maintains the original API for +// atomicops.h. This requires casting memory locations to the atomic types, and +// assumes that the API and the C++11 implementation are layout-compatible, +// which isn't true for all implementations or hardware platforms. The static +// assertion should detect this issue, were it to fire then this header +// shouldn't be used. +// +// TODO(jfb) If this header manages to stay committed then the API should be +// modified, and all call sites updated. +typedef volatile std::atomic* AtomicLocation32; +static_assert(sizeof(*(AtomicLocation32) nullptr) == sizeof(Atomic32), + "incompatible 32-bit atomic layout"); + +inline void MemoryBarrier() { +#if defined(__GLIBCXX__) + // Work around libstdc++ bug 51038 where atomic_thread_fence was declared but + // not defined, leading to the linker complaining about undefined references. + __atomic_thread_fence(std::memory_order_seq_cst); +#else + std::atomic_thread_fence(std::memory_order_seq_cst); +#endif +} + +inline Atomic32 NoBarrier_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + ((AtomicLocation32)ptr) + ->compare_exchange_strong(old_value, + new_value, + std::memory_order_relaxed, + std::memory_order_relaxed); + return old_value; +} + +inline Atomic32 NoBarrier_AtomicExchange(volatile Atomic32* ptr, + Atomic32 new_value) { + return ((AtomicLocation32)ptr) + ->exchange(new_value, std::memory_order_relaxed); +} + +inline Atomic32 NoBarrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { + return increment + + ((AtomicLocation32)ptr) + ->fetch_add(increment, std::memory_order_relaxed); +} + +inline Atomic32 Barrier_AtomicIncrement(volatile Atomic32* ptr, + Atomic32 increment) { + return increment + ((AtomicLocation32)ptr)->fetch_add(increment); +} + +inline Atomic32 Acquire_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + ((AtomicLocation32)ptr) + ->compare_exchange_strong(old_value, + new_value, + std::memory_order_acquire, + std::memory_order_acquire); + return old_value; +} + +inline Atomic32 Release_CompareAndSwap(volatile Atomic32* ptr, + Atomic32 old_value, + Atomic32 new_value) { + ((AtomicLocation32)ptr) + ->compare_exchange_strong(old_value, + new_value, + std::memory_order_release, + std::memory_order_relaxed); + return old_value; +} + +inline void NoBarrier_Store(volatile Atomic32* ptr, Atomic32 value) { + ((AtomicLocation32)ptr)->store(value, std::memory_order_relaxed); +} + +inline void Acquire_Store(volatile Atomic32* ptr, Atomic32 value) { + ((AtomicLocation32)ptr)->store(value, std::memory_order_relaxed); + MemoryBarrier(); +} + +inline void Release_Store(volatile Atomic32* ptr, Atomic32 value) { + ((AtomicLocation32)ptr)->store(value, std::memory_order_release); +} + +inline Atomic32 NoBarrier_Load(volatile const Atomic32* ptr) { + return ((AtomicLocation32)ptr)->load(std::memory_order_relaxed); +} + +inline Atomic32 Acquire_Load(volatile const Atomic32* ptr) { + return ((AtomicLocation32)ptr)->load(std::memory_order_acquire); +} + +inline Atomic32 Release_Load(volatile const Atomic32* ptr) { + MemoryBarrier(); + return ((AtomicLocation32)ptr)->load(std::memory_order_relaxed); +} + +#if defined(ARCH_CPU_64_BITS) + +typedef volatile std::atomic* AtomicLocation64; +static_assert(sizeof(*(AtomicLocation64) nullptr) == sizeof(Atomic64), + "incompatible 64-bit atomic layout"); + +inline Atomic64 NoBarrier_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + ((AtomicLocation64)ptr) + ->compare_exchange_strong(old_value, + new_value, + std::memory_order_relaxed, + std::memory_order_relaxed); + return old_value; +} + +inline Atomic64 NoBarrier_AtomicExchange(volatile Atomic64* ptr, + Atomic64 new_value) { + return ((AtomicLocation64)ptr) + ->exchange(new_value, std::memory_order_relaxed); +} + +inline Atomic64 NoBarrier_AtomicIncrement(volatile Atomic64* ptr, + Atomic64 increment) { + return increment + + ((AtomicLocation64)ptr) + ->fetch_add(increment, std::memory_order_relaxed); +} + +inline Atomic64 Barrier_AtomicIncrement(volatile Atomic64* ptr, + Atomic64 increment) { + return increment + ((AtomicLocation64)ptr)->fetch_add(increment); +} + +inline Atomic64 Acquire_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + ((AtomicLocation64)ptr) + ->compare_exchange_strong(old_value, + new_value, + std::memory_order_acquire, + std::memory_order_acquire); + return old_value; +} + +inline Atomic64 Release_CompareAndSwap(volatile Atomic64* ptr, + Atomic64 old_value, + Atomic64 new_value) { + ((AtomicLocation64)ptr) + ->compare_exchange_strong(old_value, + new_value, + std::memory_order_release, + std::memory_order_relaxed); + return old_value; +} + +inline void NoBarrier_Store(volatile Atomic64* ptr, Atomic64 value) { + ((AtomicLocation64)ptr)->store(value, std::memory_order_relaxed); +} + +inline void Acquire_Store(volatile Atomic64* ptr, Atomic64 value) { + ((AtomicLocation64)ptr)->store(value, std::memory_order_relaxed); + MemoryBarrier(); +} + +inline void Release_Store(volatile Atomic64* ptr, Atomic64 value) { + ((AtomicLocation64)ptr)->store(value, std::memory_order_release); +} + +inline Atomic64 NoBarrier_Load(volatile const Atomic64* ptr) { + return ((AtomicLocation64)ptr)->load(std::memory_order_relaxed); +} + +inline Atomic64 Acquire_Load(volatile const Atomic64* ptr) { + return ((AtomicLocation64)ptr)->load(std::memory_order_acquire); +} + +inline Atomic64 Release_Load(volatile const Atomic64* ptr) { + MemoryBarrier(); + return ((AtomicLocation64)ptr)->load(std::memory_order_relaxed); +} + +#endif // defined(ARCH_CPU_64_BITS) +} +} // namespace base::subtle + +#endif // MINI_CHROMIUM_BASE_ATOMICOPS_INTERNALS_PORTABLE_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/auto_reset.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/auto_reset.h new file mode 100644 index 000000000..88ebbddd8 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/auto_reset.h @@ -0,0 +1,31 @@ +// Copyright 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_AUTO_RESET_H_ +#define MINI_CHROMIUM_BASE_AUTO_RESET_H_ + +namespace base { + +template +class AutoReset { + public: + AutoReset(T* scoped_variable, T new_value) + : scoped_variable_(scoped_variable), + original_value_(*scoped_variable) { + *scoped_variable_ = new_value; + } + + AutoReset(const AutoReset&) = delete; + AutoReset& operator=(const AutoReset&) = delete; + + ~AutoReset() { *scoped_variable_ = original_value_; } + + private: + T* scoped_variable_; + T original_value_; +}; + +} // namespace base + +#endif // MINI_CHROMIUM_BASE_AUTO_RESET_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/bit_cast.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/bit_cast.h new file mode 100644 index 000000000..a93ba63ef --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/bit_cast.h @@ -0,0 +1,98 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_BIT_CAST_H_ +#define MINI_CHROMIUM_BASE_BIT_CAST_H_ + +#include +#include + +#include "base/compiler_specific.h" +#include "build/build_config.h" + +// bit_cast is a template function that implements the equivalent +// of "*reinterpret_cast(&source)". We need this in very low-level +// functions like the protobuf library and fast math support. +// +// float f = 3.14159265358979; +// int i = bit_cast(f); +// // i = 0x40490fdb +// +// The classical address-casting method is: +// +// // WRONG +// float f = 3.14159265358979; // WRONG +// int i = * reinterpret_cast(&f); // WRONG +// +// The address-casting method actually produces undefined behavior according to +// the ISO C++98 specification, section 3.10 ("basic.lval"), paragraph 15. +// (This did not substantially change in C++11.) Roughly, this section says: if +// an object in memory has one type, and a program accesses it with a different +// type, then the result is undefined behavior for most values of "different +// type". +// +// This is true for any cast syntax, either *(int*)&f or +// *reinterpret_cast(&f). And it is particularly true for conversions +// between integral lvalues and floating-point lvalues. +// +// The purpose of this paragraph is to allow optimizing compilers to assume that +// expressions with different types refer to different memory. Compilers are +// known to take advantage of this. So a non-conforming program quietly +// produces wildly incorrect output. +// +// The problem is not the use of reinterpret_cast. The problem is type punning: +// holding an object in memory of one type and reading its bits back using a +// different type. +// +// The C++ standard is more subtle and complex than this, but that is the basic +// idea. +// +// Anyways ... +// +// bit_cast<> calls memcpy() which is blessed by the standard, especially by the +// example in section 3.9 . Also, of course, bit_cast<> wraps up the nasty +// logic in one place. +// +// Fortunately memcpy() is very fast. In optimized mode, compilers replace +// calls to memcpy() with inline object code when the size argument is a +// compile-time constant. On a 32-bit system, memcpy(d,s,4) compiles to one +// load and one store, and memcpy(d,s,8) compiles to two loads and two stores. + +template +inline Dest bit_cast(const Source& source) { + static_assert(sizeof(Dest) == sizeof(Source), + "bit_cast requires source and destination to be the same size"); + +#if (__GNUC__ > 5 || (__GNUC__ == 5 && __GNUC_MINOR__ >= 1) || \ + defined(_LIBCPP_VERSION)) + // GCC 5.1 contains the first libstdc++ with is_trivially_copyable. + // Assume libc++ Just Works: is_trivially_copyable added on May 13th 2011. + static_assert(std::is_trivially_copyable::value, + "non-trivially-copyable bit_cast is undefined"); + static_assert(std::is_trivially_copyable::value, + "non-trivially-copyable bit_cast is undefined"); +#elif HAS_FEATURE(is_trivially_copyable) + // The compiler supports an equivalent intrinsic. + static_assert(__is_trivially_copyable(Dest), + "non-trivially-copyable bit_cast is undefined"); + static_assert(__is_trivially_copyable(Source), + "non-trivially-copyable bit_cast is undefined"); +#elif COMPILER_GCC + // Fallback to compiler intrinsic on GCC and clang (which pretends to be + // GCC). This isn't quite the same as is_trivially_copyable but it'll do for + // our purpose. + static_assert(__has_trivial_copy(Dest), + "non-trivially-copyable bit_cast is undefined"); + static_assert(__has_trivial_copy(Source), + "non-trivially-copyable bit_cast is undefined"); +#else + // Do nothing, let the bots handle it. +#endif + + Dest dest; + memcpy(&dest, &source, sizeof(dest)); + return dest; +} + +#endif // MINI_CHROMIUM_BASE_BIT_CAST_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/check.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/check.h new file mode 100644 index 000000000..90a2c0dc5 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/check.h @@ -0,0 +1,26 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_CHECK_H_ +#define MINI_CHROMIUM_BASE_CHECK_H_ + +#include "base/logging.h" + +#define CHECK(condition) \ + LAZY_STREAM(LOG_STREAM(FATAL), !(condition)) \ + << "Check failed: " # condition << ". " + +#define PCHECK(condition) \ + LAZY_STREAM(PLOG_STREAM(FATAL), !(condition)) \ + << "Check failed: " # condition << ". " + +#define DCHECK(condition) \ + LAZY_STREAM(LOG_STREAM(FATAL), DCHECK_IS_ON() ? !(condition) : false) \ + << "Check failed: " # condition << ". " + +#define DPCHECK(condition) \ + LAZY_STREAM(PLOG_STREAM(FATAL), DCHECK_IS_ON() ? !(condition) : false) \ + << "Check failed: " # condition << ". " + +#endif // MINI_CHROMIUM_BASE_CHECK_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/check_op.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/check_op.h new file mode 100644 index 000000000..0f8d85b07 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/check_op.h @@ -0,0 +1,80 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_CHECK_OP_H_ +#define MINI_CHROMIUM_BASE_CHECK_OP_H_ + +#include "base/logging.h" + +namespace logging { + +template +std::string* MakeCheckOpString(const t1& v1, const t2& v2, const char* names) { + std::ostringstream ss; + ss << names << " (" << v1 << " vs. " << v2 << ")"; + std::string* msg = new std::string(ss.str()); + return msg; +} + +#define DEFINE_CHECK_OP_IMPL(name, op) \ + template \ + inline std::string* Check ## name ## Impl(const t1& v1, const t2& v2, \ + const char* names) { \ + if (v1 op v2) { \ + return NULL; \ + } else { \ + return MakeCheckOpString(v1, v2, names); \ + } \ + } \ + inline std::string* Check ## name ## Impl(int v1, int v2, \ + const char* names) { \ + if (v1 op v2) { \ + return NULL; \ + } else { \ + return MakeCheckOpString(v1, v2, names); \ + } \ + } + +DEFINE_CHECK_OP_IMPL(EQ, ==) +DEFINE_CHECK_OP_IMPL(NE, !=) +DEFINE_CHECK_OP_IMPL(LE, <=) +DEFINE_CHECK_OP_IMPL(LT, <) +DEFINE_CHECK_OP_IMPL(GE, >=) +DEFINE_CHECK_OP_IMPL(GT, >) + +#undef DEFINE_CHECK_OP_IMPL + +} // namespace logging + +#define CHECK_OP(name, op, val1, val2) \ + if (std::string* _result = \ + logging::Check ## name ## Impl((val1), (val2), \ + # val1 " " # op " " # val2)) \ + logging::LogMessage(FUNCTION_SIGNATURE, __FILE__, __LINE__, \ + _result).stream() + +#define CHECK_EQ(val1, val2) CHECK_OP(EQ, ==, val1, val2) +#define CHECK_NE(val1, val2) CHECK_OP(NE, !=, val1, val2) +#define CHECK_LE(val1, val2) CHECK_OP(LE, <=, val1, val2) +#define CHECK_LT(val1, val2) CHECK_OP(LT, <, val1, val2) +#define CHECK_GE(val1, val2) CHECK_OP(GE, >=, val1, val2) +#define CHECK_GT(val1, val2) CHECK_OP(GT, >, val1, val2) + + +#define DCHECK_OP(name, op, val1, val2) \ + if (DCHECK_IS_ON()) \ + if (std::string* _result = \ + logging::Check ## name ## Impl((val1), (val2), \ + # val1 " " # op " " # val2)) \ + logging::LogMessage(FUNCTION_SIGNATURE, __FILE__, __LINE__, \ + _result).stream() + +#define DCHECK_EQ(val1, val2) DCHECK_OP(EQ, ==, val1, val2) +#define DCHECK_NE(val1, val2) DCHECK_OP(NE, !=, val1, val2) +#define DCHECK_LE(val1, val2) DCHECK_OP(LE, <=, val1, val2) +#define DCHECK_LT(val1, val2) DCHECK_OP(LT, <, val1, val2) +#define DCHECK_GE(val1, val2) DCHECK_OP(GE, >=, val1, val2) +#define DCHECK_GT(val1, val2) DCHECK_OP(GT, >, val1, val2) + +#endif // MINI_CHROMIUM_BASE_CHECK_OP_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/compiler_specific.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/compiler_specific.h new file mode 100644 index 000000000..b1c83c953 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/compiler_specific.h @@ -0,0 +1,53 @@ +// Copyright 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_COMPILER_SPECIFIC_H_ +#define MINI_CHROMIUM_BASE_COMPILER_SPECIFIC_H_ + +#include "build/build_config.h" + +// Specify memory alignment for structs, classes, etc. +// Use like: +// class ALIGNAS(16) MyClass { ... } +// ALIGNAS(16) int array[4]; +#if defined(COMPILER_MSVC) +#define ALIGNAS(byte_alignment) __declspec(align(byte_alignment)) +#elif defined(COMPILER_GCC) +#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment))) +#endif + +#if defined(COMPILER_MSVC) +#define PRINTF_FORMAT(format_param, dots_param) +#else +#define PRINTF_FORMAT(format_param, dots_param) \ + __attribute__((format(printf, format_param, dots_param))) +#endif + +// Sanitizers annotations. +#if defined(__has_attribute) +#if __has_attribute(no_sanitize) +#define NO_SANITIZE(what) __attribute__((no_sanitize(what))) +#endif +#endif +#if !defined(NO_SANITIZE) +#define NO_SANITIZE(what) +#endif + +// DISABLE_CFI_ICALL -- Disable Control Flow Integrity indirect call checks. +#if BUILDFLAG(IS_WIN) +// Windows also needs __declspec(guard(nocf)). +#define DISABLE_CFI_ICALL NO_SANITIZE("cfi-icall") __declspec(guard(nocf)) +#else +#define DISABLE_CFI_ICALL NO_SANITIZE("cfi-icall") +#endif + +// Compiler feature-detection. +// clang.llvm.org/docs/LanguageExtensions.html#has-feature-and-has-extension +#if defined(__has_feature) +#define HAS_FEATURE(FEATURE) __has_feature(FEATURE) +#else +#define HAS_FEATURE(FEATURE) 0 +#endif + +#endif // MINI_CHROMIUM_BASE_COMPILER_SPECIFIC_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/cxx17_backports.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/cxx17_backports.h new file mode 100644 index 000000000..479a21951 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/cxx17_backports.h @@ -0,0 +1,19 @@ +// Copyright 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_CXX17_BACKPORTS_H_ +#define MINI_CHROMIUM_BASE_CXX17_BACKPORTS_H_ + +#include + +namespace base { + +template +constexpr size_t size(const T (&array)[N]) noexcept { + return N; +} + +} // namespace base + +#endif // MINI_CHROMIUM_BASE_CXX17_BACKPORTS_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/debug/alias.cc b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/debug/alias.cc new file mode 100644 index 000000000..f672d2bba --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/debug/alias.cc @@ -0,0 +1,24 @@ +// Copyright 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/debug/alias.h" + +#include "build/build_config.h" + +namespace base { +namespace debug { + +#if defined(COMPILER_MSVC) +#pragma optimize("", off) +#endif + +void Alias(const void* var) { +} + +#if defined(COMPILER_MSVC) +#pragma optimize("", on) +#endif + +} // namespace debug +} // namespace base diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/debug/alias.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/debug/alias.h new file mode 100644 index 000000000..08d833a48 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/debug/alias.h @@ -0,0 +1,19 @@ +// Copyright 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_DEBUG_ALIAS_H_ +#define MINI_CHROMIUM_BASE_DEBUG_ALIAS_H_ + +namespace base { +namespace debug { + +// Make the optimizer think that var is aliased. This is to prevent it from +// optimizing out variables that that would not otherwise be live at the point +// of a potential crash. +void Alias(const void* var); + +} // namespace debug +} // namespace base + +#endif // MINI_CHROMIUM_BASE_DEBUG_ALIAS_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/files/file_path.cc b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/files/file_path.cc new file mode 100644 index 000000000..517ebce5c --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/files/file_path.cc @@ -0,0 +1,292 @@ +// Copyright 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/files/file_path.h" + +#include + +#include "base/cxx17_backports.h" +#include "base/logging.h" + +namespace base { + +#if defined(FILE_PATH_USES_WIN_SEPARATORS) +const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("\\/"); +#else // FILE_PATH_USES_WIN_SEPARATORS +const FilePath::CharType FilePath::kSeparators[] = FILE_PATH_LITERAL("/"); +#endif // FILE_PATH_USES_WIN_SEPARATORS + +const FilePath::CharType FilePath::kCurrentDirectory[] = FILE_PATH_LITERAL("."); +const FilePath::CharType FilePath::kParentDirectory[] = FILE_PATH_LITERAL(".."); +const FilePath::CharType FilePath::kExtensionSeparator = FILE_PATH_LITERAL('.'); + +typedef FilePath::StringType StringType; + +namespace { + +const FilePath::CharType kStringTerminator = FILE_PATH_LITERAL('\0'); + +// If this FilePath contains a drive letter specification, returns the +// position of the last character of the drive letter specification, +// otherwise returns npos. This can only be true on Windows, when a pathname +// begins with a letter followed by a colon. On other platforms, this always +// returns npos. +StringType::size_type FindDriveLetter(const StringType& path) { +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + // This is dependent on an ASCII-based character set, but that's a + // reasonable assumption. iswalpha can be too inclusive here. + if (path.length() >= 2 && path[1] == L':' && + ((path[0] >= L'A' && path[0] <= L'Z') || + (path[0] >= L'a' && path[0] <= L'z'))) { + return 1; + } +#endif // FILE_PATH_USES_DRIVE_LETTERS + return StringType::npos; +} + +#if defined(FILE_PATH_USES_DRIVE_LETTERS) +bool EqualDriveLetterCaseInsensitive(const StringType& a, + const StringType& b) { + size_t a_letter_pos = FindDriveLetter(a); + size_t b_letter_pos = FindDriveLetter(b); + + if (a_letter_pos == StringType::npos || b_letter_pos == StringType::npos) + return a == b; + + if (::tolower(a[0]) != ::tolower(b[0])) + return false; + + StringType a_rest(a.substr(a_letter_pos + 1)); + StringType b_rest(b.substr(b_letter_pos + 1)); + return a_rest == b_rest; +} +#endif // defined(FILE_PATH_USES_DRIVE_LETTERS) + +bool IsPathAbsolute(const StringType& path) { +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + StringType::size_type letter = FindDriveLetter(path); + if (letter != StringType::npos) { + // Look for a separator right after the drive specification. + return path.length() > letter + 1 && + FilePath::IsSeparator(path[letter + 1]); + } + // Look for a pair of leading separators. + return path.length() > 1 && + FilePath::IsSeparator(path[0]) && FilePath::IsSeparator(path[1]); +#else // FILE_PATH_USES_DRIVE_LETTERS + // Look for a separator in the first position. + return path.length() > 0 && FilePath::IsSeparator(path[0]); +#endif // FILE_PATH_USES_DRIVE_LETTERS +} + +} // namespace + +FilePath::FilePath() { +} + +FilePath::FilePath(const FilePath& that) : path_(that.path_) { +} + +FilePath::FilePath(const StringType& path) : path_(path) { + StringType::size_type nul_pos = path_.find(kStringTerminator); + if (nul_pos != StringType::npos) + path_.erase(nul_pos, StringType::npos); +} + +FilePath::~FilePath() { +} + +FilePath& FilePath::operator=(const FilePath& that) { + path_ = that.path_; + return *this; +} + +bool FilePath::operator==(const FilePath& that) const { +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + return EqualDriveLetterCaseInsensitive(this->path_, that.path_); +#else // defined(FILE_PATH_USES_DRIVE_LETTERS) + return path_ == that.path_; +#endif // defined(FILE_PATH_USES_DRIVE_LETTERS) +} + +bool FilePath::operator!=(const FilePath& that) const { +#if defined(FILE_PATH_USES_DRIVE_LETTERS) + return !EqualDriveLetterCaseInsensitive(this->path_, that.path_); +#else // defined(FILE_PATH_USES_DRIVE_LETTERS) + return path_ != that.path_; +#endif // defined(FILE_PATH_USES_DRIVE_LETTERS) +} + +// static +bool FilePath::IsSeparator(CharType character) { + for (size_t i = 0; i < size(FilePath::kSeparators) - 1; ++i) { + if (character == kSeparators[i]) { + return true; + } + } + + return false; +} + +// libgen's dirname and basename aren't guaranteed to be thread-safe and aren't +// guaranteed to not modify their input strings, and in fact are implemented +// differently in this regard on different platforms. Don't use them, but +// adhere to their behavior. +FilePath FilePath::DirName() const { + FilePath new_path(path_); + new_path.StripTrailingSeparatorsInternal(); + + // The drive letter, if any, always needs to remain in the output. If there + // is no drive letter, as will always be the case on platforms which do not + // support drive letters, letter will be npos, or -1, so the comparisons and + // resizes below using letter will still be valid. + StringType::size_type letter = FindDriveLetter(new_path.path_); + + StringType::size_type last_separator = + new_path.path_.find_last_of(kSeparators, StringType::npos, + size(kSeparators) - 1); + if (last_separator == StringType::npos) { + // path_ is in the current directory. + new_path.path_.resize(letter + 1); + } else if (last_separator == letter + 1) { + // path_ is in the root directory. + new_path.path_.resize(letter + 2); + } else if (last_separator == letter + 2 && + IsSeparator(new_path.path_[letter + 1])) { + // path_ is in "//" (possibly with a drive letter); leave the double + // separator intact indicating alternate root. + new_path.path_.resize(letter + 3); + } else if (last_separator != 0) { + // path_ is somewhere else, trim the basename. + new_path.path_.resize(last_separator); + } + + new_path.StripTrailingSeparatorsInternal(); + if (!new_path.path_.length()) + new_path.path_ = kCurrentDirectory; + + return new_path; +} + +FilePath FilePath::BaseName() const { + FilePath new_path(path_); + new_path.StripTrailingSeparatorsInternal(); + + // The drive letter, if any, is always stripped. + StringType::size_type letter = FindDriveLetter(new_path.path_); + if (letter != StringType::npos) { + new_path.path_.erase(0, letter + 1); + } + + // Keep everything after the final separator, but if the pathname is only + // one character and it's a separator, leave it alone. + StringType::size_type last_separator = + new_path.path_.find_last_of(kSeparators, StringType::npos, + size(kSeparators) - 1); + if (last_separator != StringType::npos && + last_separator < new_path.path_.length() - 1) { + new_path.path_.erase(0, last_separator + 1); + } + + return new_path; +} + +StringType FilePath::FinalExtension() const { + StringType base(BaseName().value()); + // Special case "." and ".." + if (base == FilePath::kCurrentDirectory || base == FilePath::kParentDirectory) + return StringType(); + const StringType::size_type dot = base.rfind(FilePath::kExtensionSeparator); + if (dot == StringType::npos) + return StringType(); + + return base.substr(dot, StringType::npos); +} + +FilePath FilePath::RemoveFinalExtension() const { + StringType extension = FinalExtension(); + if (FinalExtension().empty()) + return *this; + return FilePath(path_.substr(0, path_.size() - extension.size())); +} + +FilePath FilePath::Append(const StringType& component) const { + const StringType* appended = &component; + StringType without_nuls; + + StringType::size_type nul_pos = component.find(kStringTerminator); + if (nul_pos != StringType::npos) { + without_nuls = component.substr(0, nul_pos); + appended = &without_nuls; + } + + DCHECK(!IsPathAbsolute(*appended)); + + if (path_.compare(kCurrentDirectory) == 0) { + // Append normally doesn't do any normalization, but as a special case, + // when appending to kCurrentDirectory, just return a new path for the + // component argument. Appending component to kCurrentDirectory would + // serve no purpose other than needlessly lengthening the path, and + // it's likely in practice to wind up with FilePath objects containing + // only kCurrentDirectory when calling DirName on a single relative path + // component. + return FilePath(*appended); + } + + FilePath new_path(path_); + new_path.StripTrailingSeparatorsInternal(); + + // Don't append a separator if the path is empty (indicating the current + // directory) or if the path component is empty (indicating nothing to + // append). + if (appended->length() > 0 && new_path.path_.length() > 0) { + // Don't append a separator if the path still ends with a trailing + // separator after stripping (indicating the root directory). + if (!IsSeparator(new_path.path_[new_path.path_.length() - 1])) { + // Don't append a separator if the path is just a drive letter. + if (FindDriveLetter(new_path.path_) + 1 != new_path.path_.length()) { + new_path.path_.append(1, kSeparators[0]); + } + } + } + + new_path.path_.append(*appended); + return new_path; +} + +FilePath FilePath::Append(const FilePath& component) const { + return Append(component.value()); +} + +bool FilePath::IsAbsolute() const { + return IsPathAbsolute(path_); +} + +void FilePath::StripTrailingSeparatorsInternal() { + // If there is no drive letter, start will be 1, which will prevent stripping + // the leading separator if there is only one separator. If there is a drive + // letter, start will be set appropriately to prevent stripping the first + // separator following the drive letter, if a separator immediately follows + // the drive letter. + StringType::size_type start = FindDriveLetter(path_) + 2; + + StringType::size_type last_stripped = StringType::npos; + for (StringType::size_type pos = path_.length(); + pos > start && IsSeparator(path_[pos - 1]); + --pos) { + // If the string only has two separators and they're at the beginning, + // don't strip them, unless the string began with more than two separators. + if (pos != start + 1 || last_stripped == start + 2 || + !IsSeparator(path_[start - 1])) { + path_.resize(pos - 1); + last_stripped = pos; + } + } +} + +} // namespace base + +void PrintTo(const base::FilePath& path, std::ostream* out) { + *out << path.value().c_str(); +} diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/files/file_path.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/files/file_path.h new file mode 100644 index 000000000..216c396e8 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/files/file_path.h @@ -0,0 +1,243 @@ +// Copyright 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// FilePath is a container for pathnames stored in a platform's native string +// type, providing containers for manipulation in according with the +// platform's conventions for pathnames. It supports the following path +// types: +// +// POSIX Windows +// --------------- ---------------------------------- +// Fundamental type char[] wchar_t[] +// Encoding unspecified* UTF-16 +// Separator / \, tolerant of / +// Drive letters no case-insensitive A-Z followed by : +// Alternate root // (surprise!) \\, for UNC paths +// +// * The encoding need not be specified on POSIX systems, although some +// POSIX-compliant systems do specify an encoding. Mac OS X uses UTF-8. +// Chrome OS also uses UTF-8. +// Linux does not specify an encoding, but in practice, the locale's +// character set may be used. +// +// For more arcane bits of path trivia, see below. +// +// FilePath objects are intended to be used anywhere paths are. An +// application may pass FilePath objects around internally, masking the +// underlying differences between systems, only differing in implementation +// where interfacing directly with the system. For example, a single +// OpenFile(const FilePath &) function may be made available, allowing all +// callers to operate without regard to the underlying implementation. On +// POSIX-like platforms, OpenFile might wrap fopen, and on Windows, it might +// wrap _wfopen_s, perhaps both by calling file_path.value().c_str(). This +// allows each platform to pass pathnames around without requiring conversions +// between encodings, which has an impact on performance, but more imporantly, +// has an impact on correctness on platforms that do not have well-defined +// encodings for pathnames. +// +// Several methods are available to perform common operations on a FilePath +// object, such as determining the parent directory (DirName), isolating the +// final path component (BaseName), and appending a relative pathname string +// to an existing FilePath object (Append). These methods are highly +// recommended over attempting to split and concatenate strings directly. +// These methods are based purely on string manipulation and knowledge of +// platform-specific pathname conventions, and do not consult the filesystem +// at all, making them safe to use without fear of blocking on I/O operations. +// These methods do not function as mutators but instead return distinct +// instances of FilePath objects, and are therefore safe to use on const +// objects. The objects themselves are safe to share between threads. +// +// To aid in initialization of FilePath objects from string literals, a +// FILE_PATH_LITERAL macro is provided, which accounts for the difference +// between char[]-based pathnames on POSIX systems and wchar_t[]-based +// pathnames on Windows. +// +// Paths can't contain NULs as a precaution agaist premature truncation. +// +// Because a FilePath object should not be instantiated at the global scope, +// instead, use a FilePath::CharType[] and initialize it with +// FILE_PATH_LITERAL. At runtime, a FilePath object can be created from the +// character array. Example: +// +// | const FilePath::CharType kLogFileName[] = FILE_PATH_LITERAL("log.txt"); +// | +// | void Function() { +// | FilePath log_file_path(kLogFileName); +// | [...] +// | } +// +// WARNING: FilePaths should ALWAYS be displayed with LTR directionality, even +// when the UI language is RTL. This means you always need to pass filepaths +// through base::i18n::WrapPathWithLTRFormatting() before displaying it in the +// RTL UI. +// +// This is a very common source of bugs, please try to keep this in mind. +// +// ARCANE BITS OF PATH TRIVIA +// +// - A double leading slash is actually part of the POSIX standard. Systems +// are allowed to treat // as an alternate root, as Windows does for UNC +// (network share) paths. Most POSIX systems don't do anything special +// with two leading slashes, but FilePath handles this case properly +// in case it ever comes across such a system. FilePath needs this support +// for Windows UNC paths, anyway. +// References: +// The Open Group Base Specifications Issue 7, sections 3.266 ("Pathname") +// and 4.12 ("Pathname Resolution"), available at: +// http://www.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap03.html#tag_03_266 +// http://www.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_12 +// +// - Windows treats c:\\ the same way it treats \\. This was intended to +// allow older applications that require drive letters to support UNC paths +// like \\server\share\path, by permitting c:\\server\share\path as an +// equivalent. Since the OS treats these paths specially, FilePath needs +// to do the same. Since Windows can use either / or \ as the separator, +// FilePath treats c://, c:\\, //, and \\ all equivalently. +// Reference: +// The Old New Thing, "Why is a drive letter permitted in front of UNC +// paths (sometimes)?", available at: +// http://blogs.msdn.com/oldnewthing/archive/2005/11/22/495740.aspx + +#ifndef MINI_CHROMIUM_BASE_FILES_FILE_PATH_H_ +#define MINI_CHROMIUM_BASE_FILES_FILE_PATH_H_ + +#include + +#include +#include + +#include "build/build_config.h" + +// Windows-style drive letter support and pathname separator characters can be +// enabled and disabled independently, to aid testing. These #defines are +// here so that the same setting can be used in both the implementation and +// in the unit test. +#if BUILDFLAG(IS_WIN) +#define FILE_PATH_USES_DRIVE_LETTERS +#define FILE_PATH_USES_WIN_SEPARATORS +#endif // BUILDFLAG(IS_WIN) + +namespace base { + +// An abstraction to isolate users from the differences between native +// pathnames on different platforms. +class FilePath { + public: +#if BUILDFLAG(IS_POSIX) + // On most platforms, native pathnames are char arrays, and the encoding + // may or may not be specified. On Mac OS X, native pathnames are encoded + // in UTF-8. + typedef std::string StringType; +#elif BUILDFLAG(IS_WIN) + // On Windows, for Unicode-aware applications, native pathnames are wchar_t + // arrays encoded in UTF-16. + typedef std::wstring StringType; +#endif // BUILDFLAG(IS_WIN) + + typedef StringType::value_type CharType; + + // Null-terminated array of separators used to separate components in + // hierarchical paths. Each character in this array is a valid separator, + // but kSeparators[0] is treated as the canonical separator and will be used + // when composing pathnames. + static const CharType kSeparators[]; + + // A special path component meaning "this directory." + static const CharType kCurrentDirectory[]; + + // A special path component meaning "the parent directory." + static const CharType kParentDirectory[]; + + // The character used to identify a file extension. + static const CharType kExtensionSeparator; + + FilePath(); + FilePath(const FilePath& that); + explicit FilePath(const StringType& path); + ~FilePath(); + FilePath& operator=(const FilePath& that); + + bool operator==(const FilePath& that) const; + + bool operator!=(const FilePath& that) const; + + // Required for some STL containers and operations + bool operator<(const FilePath& that) const { + return path_ < that.path_; + } + + const StringType& value() const { return path_; } + + bool empty() const { return path_.empty(); } + + void clear() { path_.clear(); } + + // Returns true if |character| is in kSeparators. + static bool IsSeparator(CharType character); + + // Returns a FilePath corresponding to the directory containing the path + // named by this object, stripping away the file component. If this object + // only contains one component, returns a FilePath identifying + // kCurrentDirectory. If this object already refers to the root directory, + // returns a FilePath identifying the root directory. + [[nodiscard]] FilePath DirName() const; + + // Returns a FilePath corresponding to the last path component of this + // object, either a file or a directory. If this object already refers to + // the root directory, returns a FilePath identifying the root directory; + // this is the only situation in which BaseName will return an absolute path. + [[nodiscard]] FilePath BaseName() const; + + // Returns the path's file extension. This does not have a special case for + // common double extensions, so FinalExtension() of "foo.tar.gz" is simply + // ".gz". If there is no extension, "" will be returned. + [[nodiscard]] StringType FinalExtension() const; + + // Returns a FilePath with FinalExtension() removed. + [[nodiscard]] FilePath RemoveFinalExtension() const; + + // Returns a FilePath by appending a separator and the supplied path + // component to this object's path. Append takes care to avoid adding + // excessive separators if this object's path already ends with a separator. + // If this object's path is kCurrentDirectory, a new FilePath corresponding + // only to |component| is returned. |component| must be a relative path; + // it is an error to pass an absolute path. + [[nodiscard]] FilePath Append(const StringType& component) const; + [[nodiscard]] FilePath Append(const FilePath& component) const; + + // Returns true if this FilePath contains an absolute path. On Windows, an + // absolute path begins with either a drive letter specification followed by + // a separator character, or with two separator characters. On POSIX + // platforms, an absolute path begins with a separator character. + bool IsAbsolute() const; + + private: + // Remove trailing separators from this object. If the path is absolute, it + // will never be stripped any more than to refer to the absolute root + // directory, so "////" will become "/", not "". A leading pair of + // separators is never stripped, to support alternate roots. This is used to + // support UNC paths on Windows. + void StripTrailingSeparatorsInternal(); + + StringType path_; +}; + +} // namespace base + +// This is required by googletest to print a readable output on test failures. +extern void PrintTo(const base::FilePath& path, std::ostream* out); + +// Macros for string literal initialization of FilePath::CharType[], and for +// using a FilePath::CharType[] in a printf-style format string. +#if BUILDFLAG(IS_POSIX) +#define FILE_PATH_LITERAL(x) x +#define PRFilePath "s" +#define PRFilePathLiteral "%s" +#elif BUILDFLAG(IS_WIN) +#define FILE_PATH_LITERAL(x) L ## x +#define PRFilePath "ls" +#define PRFilePathLiteral L"%ls" +#endif // BUILDFLAG(IS_WIN) + +#endif // MINI_CHROMIUM_BASE_FILES_FILE_PATH_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/files/file_util.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/files/file_util.h new file mode 100644 index 000000000..95906af3d --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/files/file_util.h @@ -0,0 +1,22 @@ +// Copyright 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_FILES_FILE_UTIL_H_ +#define MINI_CHROMIUM_BASE_FILES_FILE_UTIL_H_ + +#include "build/build_config.h" + +#if BUILDFLAG(IS_POSIX) + +#include + +namespace base { + +bool ReadFromFD(int fd, char* buffer, size_t bytes); + +} // namespace base + +#endif // BUILDFLAG(IS_POSIX) + +#endif // MINI_CHROMIUM_BASE_FILES_FILE_UTIL_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/files/file_util_posix.cc b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/files/file_util_posix.cc new file mode 100644 index 000000000..6d3c096c8 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/files/file_util_posix.cc @@ -0,0 +1,26 @@ +// Copyright 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/files/file_util.h" + +#include + +#include "base/posix/eintr_wrapper.h" + +namespace base { + +bool ReadFromFD(int fd, char* buffer, size_t bytes) { + size_t total_read = 0; + while (total_read < bytes) { + ssize_t bytes_read = + HANDLE_EINTR(read(fd, buffer + total_read, bytes - total_read)); + if (bytes_read <= 0) { + break; + } + total_read += bytes_read; + } + return total_read == bytes; +} + +} // namespace base diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/files/scoped_file.cc b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/files/scoped_file.cc new file mode 100644 index 000000000..215407d9a --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/files/scoped_file.cc @@ -0,0 +1,34 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/files/scoped_file.h" + +#include + +#include "base/logging.h" + +#if BUILDFLAG(IS_POSIX) +#include +#include "base/posix/eintr_wrapper.h" +#endif + +namespace base { +namespace internal { + +#if BUILDFLAG(IS_POSIX) +void ScopedFDCloseTraits::Free(int fd) { + PCHECK(IGNORE_EINTR(close(fd)) == 0); +} +#endif // BUILDFLAG(IS_POSIX) + +void ScopedFILECloser::operator()(FILE* file) const { + if (file) { + if (fclose(file) < 0) { + PLOG(ERROR) << "fclose"; + } + } +} + +} // namespace internal +} // namespace base diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/files/scoped_file.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/files/scoped_file.h new file mode 100644 index 000000000..a0cc882bf --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/files/scoped_file.h @@ -0,0 +1,41 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_FILES_SCOPED_FILE_H_ +#define MINI_CHROMIUM_BASE_FILES_SCOPED_FILE_H_ + +#include + +#include + +#include "base/scoped_generic.h" +#include "build/build_config.h" + +namespace base { + +namespace internal { + +#if BUILDFLAG(IS_POSIX) +struct ScopedFDCloseTraits { + static int InvalidValue() { + return -1; + } + static void Free(int fd); +}; +#endif // BUILDFLAG(IS_POSIX) + +struct ScopedFILECloser { + void operator()(FILE* file) const; +}; + +} // namespace internal + +#if BUILDFLAG(IS_POSIX) +typedef ScopedGeneric ScopedFD; +#endif // BUILDFLAG(IS_POSIX) +typedef std::unique_ptr ScopedFILE; + +} // namespace base + +#endif // MINI_CHROMIUM_BASE_FILES_SCOPED_FILE_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/format_macros.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/format_macros.h new file mode 100644 index 000000000..30459f7c7 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/format_macros.h @@ -0,0 +1,101 @@ +// Copyright (c) 2009 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef BASE_FORMAT_MACROS_H_ +#define BASE_FORMAT_MACROS_H_ + +// This file defines the format macros for some integer types. + +// To print a 64-bit value in a portable way: +// int64_t value; +// printf("xyz:%" PRId64, value); +// The "d" in the macro corresponds to %d; you can also use PRIu64 etc. +// +// For wide strings, prepend "Wide" to the macro: +// int64_t value; +// StringPrintf(L"xyz: %" WidePRId64, value); +// +// To print a size_t value in a portable way: +// size_t size; +// printf("xyz: %" PRIuS, size); +// The "u" in the macro corresponds to %u, and S is for "size". + +#include "build/build_config.h" + +#if BUILDFLAG(IS_POSIX) + +#if (defined(_INTTYPES_H) || defined(_INTTYPES_H_)) && !defined(PRId64) +#error "inttypes.h has already been included before this header file, but " +#error "without __STDC_FORMAT_MACROS defined." +#endif + +#if !defined(__STDC_FORMAT_MACROS) +#define __STDC_FORMAT_MACROS +#endif + +#include + +// GCC will concatenate wide and narrow strings correctly, so nothing needs to +// be done here. +#define WidePRId64 PRId64 +#define WidePRIu64 PRIu64 +#define WidePRIx64 PRIx64 + +#if !defined(PRIuS) +#define PRIuS "zu" +#endif + +// The size of NSInteger and NSUInteger varies between 32-bit and 64-bit +// architectures and Apple does not provides standard format macros and +// recommends casting. This has many drawbacks, so instead define macros +// for formatting those types. +#if BUILDFLAG(IS_APPLE) +#if defined(ARCH_CPU_64_BITS) +#if !defined(PRIdNS) +#define PRIdNS "ld" +#endif +#if !defined(PRIuNS) +#define PRIuNS "lu" +#endif +#if !defined(PRIxNS) +#define PRIxNS "lx" +#endif +#else // defined(ARCH_CPU_64_BITS) +#if !defined(PRIdNS) +#define PRIdNS "d" +#endif +#if !defined(PRIuNS) +#define PRIuNS "u" +#endif +#if !defined(PRIxNS) +#define PRIxNS "x" +#endif +#endif +#endif // BUILDFLAG(IS_APPLE) + +#else // BUILDFLAG(IS_WIN) + +#if !defined(PRId64) +#define PRId64 "I64d" +#endif + +#if !defined(PRIu64) +#define PRIu64 "I64u" +#endif + +#if !defined(PRIx64) +#define PRIx64 "I64x" +#endif + +#define WidePRId64 L"I64d" +#define WidePRIu64 L"I64u" +#define WidePRIx64 L"I64x" + +#if !defined(PRIuS) +#define PRIuS "Iu" +#endif + +#endif + +#endif // BASE_FORMAT_MACROS_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/fuchsia/fuchsia_logging.cc b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/fuchsia/fuchsia_logging.cc new file mode 100644 index 000000000..de30a3043 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/fuchsia/fuchsia_logging.cc @@ -0,0 +1,27 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/fuchsia/fuchsia_logging.h" + +#include + +#include + +namespace logging { + +ZxLogMessage::ZxLogMessage(const char* function, + const char* file_path, + int line, + LogSeverity severity, + zx_status_t zx_err) + : LogMessage(function, file_path, line, severity), zx_err_(zx_err) {} + +ZxLogMessage::~ZxLogMessage() { + // zx_status_t error values are negative, so log the numeric version as + // decimal rather than hex. This is also useful to match zircon/errors.h for + // grepping. + stream() << ": " << zx_status_get_string(zx_err_) << " (" << zx_err_ << ")"; +} + +} // namespace logging diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/fuchsia/fuchsia_logging.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/fuchsia/fuchsia_logging.h new file mode 100644 index 000000000..50b4ed70e --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/fuchsia/fuchsia_logging.h @@ -0,0 +1,60 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_FUCHSIA_FUCHSIA_LOGGING_H_ +#define MINI_CHROMIUM_BASE_FUCHSIA_FUCHSIA_LOGGING_H_ + +#include + +#include "base/logging.h" + +// Use the ZX_LOG family of macros along with a zx_status_t containing a Zircon +// error. The error value will be decoded so that logged messages explain the +// error. + +namespace logging { + +class ZxLogMessage : public logging::LogMessage { + public: + ZxLogMessage(const char* function, + const char* file_path, + int line, + LogSeverity severity, + zx_status_t zx_err); + + ZxLogMessage(const ZxLogMessage&) = delete; + ZxLogMessage& operator=(const ZxLogMessage&) = delete; + + ~ZxLogMessage(); + + private: + zx_status_t zx_err_; +}; + +} // namespace logging + +#define ZX_LOG_STREAM(severity, zx_err) \ + COMPACT_GOOGLE_LOG_EX_##severity(ZxLogMessage, zx_err).stream() + +#define ZX_LOG(severity, zx_err) \ + LAZY_STREAM(ZX_LOG_STREAM(severity, zx_err), LOG_IS_ON(severity)) +#define ZX_LOG_IF(severity, condition, zx_err) \ + LAZY_STREAM(ZX_LOG_STREAM(severity, zx_err), \ + LOG_IS_ON(severity) && (condition)) + +#define ZX_CHECK(condition, zx_err) \ + LAZY_STREAM(ZX_LOG_STREAM(FATAL, zx_err), !(condition)) \ + << "Check failed: " #condition << ". " + +#define ZX_DLOG(severity, zx_err) \ + LAZY_STREAM(ZX_LOG_STREAM(severity, zx_err), DLOG_IS_ON(severity)) +#define ZX_DLOG_IF(severity, condition, zx_err) \ + LAZY_STREAM(ZX_LOG_STREAM(severity, zx_err), \ + DLOG_IS_ON(severity) && (condition)) + +#define ZX_DCHECK(condition, zx_err) \ + LAZY_STREAM(ZX_LOG_STREAM(FATAL, zx_err), DCHECK_IS_ON() && !(condition)) \ + << "Check failed: " #condition << ". " + +#endif // MINI_CHROMIUM_BASE_FUCHSIA_FUCHSIA_LOGGING_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/logging.cc b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/logging.cc new file mode 100644 index 000000000..38c947feb --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/logging.cc @@ -0,0 +1,535 @@ +// Copyright 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/logging.h" + +#include +#include + +#include +#include + +#if BUILDFLAG(IS_POSIX) +#include +#include +#include +#include +#include "base/posix/safe_strerror.h" +#endif // BUILDFLAG(IS_POSIX) + +#if BUILDFLAG(IS_APPLE) +// In macOS 10.12 and iOS 10.0 and later ASL (Apple System Log) was deprecated +// in favor of OS_LOG (Unified Logging). +#include +#if BUILDFLAG(IS_IOS) +#if !defined(__IPHONE_10_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_10_0 +#define USE_ASL +#endif +#else // !BUILDFLAG(IS_IOS) +#if !defined(MAC_OS_X_VERSION_10_12) || \ + MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12 +#define USE_ASL +#endif +#endif // BUILDFLAG(IS_IOS) + +#if defined(USE_ASL) +#include +#else +#include +#endif // USE_ASL + +#include +#include + +#elif BUILDFLAG(IS_LINUX) +#include +#include +#elif BUILDFLAG(IS_WIN) +#include +#include +#elif BUILDFLAG(IS_ANDROID) +#include +#elif BUILDFLAG(IS_FUCHSIA) +#include +#endif + +#include "base/cxx17_backports.h" +#include "base/strings/string_util.h" +#include "base/strings/stringprintf.h" +#include "base/strings/utf_string_conversions.h" + +namespace logging { + +namespace { + +const char* const log_severity_names[] = { + "INFO", + "WARNING", + "ERROR", + "ERROR_REPORT", + "FATAL" +}; + +LogMessageHandlerFunction g_log_message_handler = nullptr; + +LoggingDestination g_logging_destination = LOG_DEFAULT; + +} // namespace + +bool InitLogging(const LoggingSettings& settings) { + DCHECK_EQ(settings.logging_dest & LOG_TO_FILE, 0u); + + g_logging_destination = settings.logging_dest; + return true; +} + +void SetLogMessageHandler(LogMessageHandlerFunction log_message_handler) { + g_log_message_handler = log_message_handler; +} + +LogMessageHandlerFunction GetLogMessageHandler() { + return g_log_message_handler; +} + +#if BUILDFLAG(IS_WIN) +std::string SystemErrorCodeToString(unsigned long error_code) { + wchar_t msgbuf[256]; + DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_MAX_WIDTH_MASK; + DWORD len = FormatMessage(flags, + nullptr, + error_code, + 0, + msgbuf, + static_cast(base::size(msgbuf)), + nullptr); + if (len) { + // Most system messages end in a period and a space. Remove the space if + // it’s there, because the following StringPrintf() includes one. + if (len >= 1 && msgbuf[len - 1] == ' ') { + msgbuf[len - 1] = '\0'; + } + return base::StringPrintf("%s (%u)", + base::WideToUTF8(msgbuf).c_str(), error_code); + } + return base::StringPrintf("Error %u while retrieving error %u", + GetLastError(), + error_code); +} +#endif // BUILDFLAG(IS_WIN) + +LogMessage::LogMessage(const char* function, + const char* file_path, + int line, + LogSeverity severity) + : stream_(), + file_path_(file_path), + message_start_(0), + line_(line), + severity_(severity) { + Init(function); +} + +LogMessage::LogMessage(const char* function, + const char* file_path, + int line, + std::string* result) + : stream_(), + file_path_(file_path), + message_start_(0), + line_(line), + severity_(LOG_FATAL) { + Init(function); + stream_ << "Check failed: " << *result << ". "; + delete result; +} + +LogMessage::~LogMessage() { + stream_ << std::endl; + std::string str_newline(stream_.str()); + + if (g_log_message_handler && + g_log_message_handler( + severity_, file_path_, line_, message_start_, str_newline)) { + return; + } + + if ((g_logging_destination & LOG_TO_STDERR)) { + fprintf(stderr, "%s", str_newline.c_str()); + fflush(stderr); + } + + if ((g_logging_destination & LOG_TO_SYSTEM_DEBUG_LOG) != 0) { +#if BUILDFLAG(IS_APPLE) + const bool log_to_system = []() { + struct stat stderr_stat; + if (fstat(fileno(stderr), &stderr_stat) == -1) { + return true; + } + if (!S_ISCHR(stderr_stat.st_mode)) { + return false; + } + + struct stat dev_null_stat; + if (stat(_PATH_DEVNULL, &dev_null_stat) == -1) { + return true; + } + + return !S_ISCHR(dev_null_stat.st_mode) || + stderr_stat.st_rdev == dev_null_stat.st_rdev; + }(); + + if (log_to_system) { + CFBundleRef main_bundle = CFBundleGetMainBundle(); + CFStringRef main_bundle_id_cf = + main_bundle ? CFBundleGetIdentifier(main_bundle) : nullptr; + + std::string main_bundle_id_buf; + const char* main_bundle_id = nullptr; + + if (main_bundle_id_cf) { + main_bundle_id = + CFStringGetCStringPtr(main_bundle_id_cf, kCFStringEncodingUTF8); + if (!main_bundle_id) { + // 1024 is from 10.10.5 CF-1153.18/CFBundle.c __CFBundleMainID__ (at + // the point of use, not declaration). + main_bundle_id_buf.resize(1024); + if (!CFStringGetCString(main_bundle_id_cf, + &main_bundle_id_buf[0], + main_bundle_id_buf.size(), + kCFStringEncodingUTF8)) { + main_bundle_id_buf.clear(); + } else { + main_bundle_id = &main_bundle_id_buf[0]; + } + } + } + +#if defined(USE_ASL) + // Use ASL when this might run on pre-10.12 systems. Unified Logging + // (os_log) was introduced in 10.12. + + const class ASLClient { + public: + explicit ASLClient(const char* asl_facility) + : client_(asl_open(nullptr, asl_facility, ASL_OPT_NO_DELAY)) {} + + ASLClient(const ASLClient&) = delete; + ASLClient& operator=(const ASLClient&) = delete; + + ~ASLClient() { asl_close(client_); } + + aslclient get() const { return client_; } + + private: + aslclient client_; + } asl_client(main_bundle_id ? main_bundle_id : "com.apple.console"); + + const class ASLMessage { + public: + ASLMessage() : message_(asl_new(ASL_TYPE_MSG)) {} + + ASLMessage(const ASLMessage&) = delete; + ASLMessage& operator=(const ASLMessage&) = delete; + + ~ASLMessage() { asl_free(message_); } + + aslmsg get() const { return message_; } + + private: + aslmsg message_; + } asl_message; + + // By default, messages are only readable by the admin group. Explicitly + // make them readable by the user generating the messages. + char euid_string[12]; + snprintf(euid_string, base::size(euid_string), "%d", geteuid()); + asl_set(asl_message.get(), ASL_KEY_READ_UID, euid_string); + + // Map Chrome log severities to ASL log levels. + const char* const asl_level_string = [](LogSeverity severity) { +#define ASL_LEVEL_STR(level) ASL_LEVEL_STR_X(level) +#define ASL_LEVEL_STR_X(level) #level + switch (severity) { + case LOG_INFO: + return ASL_LEVEL_STR(ASL_LEVEL_INFO); + case LOG_WARNING: + return ASL_LEVEL_STR(ASL_LEVEL_WARNING); + case LOG_ERROR: + return ASL_LEVEL_STR(ASL_LEVEL_ERR); + case LOG_FATAL: + return ASL_LEVEL_STR(ASL_LEVEL_CRIT); + default: + return severity < 0 ? ASL_LEVEL_STR(ASL_LEVEL_DEBUG) + : ASL_LEVEL_STR(ASL_LEVEL_NOTICE); + } +#undef ASL_LEVEL_STR +#undef ASL_LEVEL_STR_X + }(severity_); + asl_set(asl_message.get(), ASL_KEY_LEVEL, asl_level_string); + + asl_set(asl_message.get(), ASL_KEY_MSG, str_newline.c_str()); + + asl_send(asl_client.get(), asl_message.get()); +#else + // Use Unified Logging (os_log) when this will only run on 10.12 and + // later. ASL is deprecated in 10.12. + + const class OSLog { + public: + explicit OSLog(const char* subsystem) + : os_log_(subsystem ? os_log_create(subsystem, "chromium_logging") + : OS_LOG_DEFAULT) {} + + OSLog(const OSLog&) = delete; + OSLog& operator=(const OSLog&) = delete; + + ~OSLog() { + if (os_log_ != OS_LOG_DEFAULT) { + os_release(os_log_); + } + } + + os_log_t get() const { return os_log_; } + + private: + os_log_t os_log_; + } log(main_bundle_id); + + const os_log_type_t os_log_type = [](LogSeverity severity) { + switch (severity) { + case LOG_INFO: + return OS_LOG_TYPE_INFO; + case LOG_WARNING: + return OS_LOG_TYPE_DEFAULT; + case LOG_ERROR: + return OS_LOG_TYPE_ERROR; + case LOG_FATAL: + return OS_LOG_TYPE_FAULT; + default: + return severity < 0 ? OS_LOG_TYPE_DEBUG : OS_LOG_TYPE_DEFAULT; + } + }(severity_); + + os_log_with_type( + log.get(), os_log_type, "%{public}s", str_newline.c_str()); +#endif + } +#elif BUILDFLAG(IS_WIN) + OutputDebugString(base::UTF8ToWide(str_newline).c_str()); +#elif BUILDFLAG(IS_ANDROID) + android_LogPriority priority = + (severity_ < 0) ? ANDROID_LOG_VERBOSE : ANDROID_LOG_UNKNOWN; + switch (severity_) { + case LOG_INFO: + priority = ANDROID_LOG_INFO; + break; + case LOG_WARNING: + priority = ANDROID_LOG_WARN; + break; + case LOG_ERROR: + priority = ANDROID_LOG_ERROR; + break; + case LOG_FATAL: + priority = ANDROID_LOG_FATAL; + break; + } + // The Android system may truncate the string if it's too long. + __android_log_write(priority, "chromium", str_newline.c_str()); +#elif BUILDFLAG(IS_FUCHSIA) + fx_log_severity_t fx_severity; + switch (severity_) { + case LOG_INFO: + fx_severity = FX_LOG_INFO; + break; + case LOG_WARNING: + fx_severity = FX_LOG_WARNING; + break; + case LOG_ERROR: + fx_severity = FX_LOG_ERROR; + break; + case LOG_FATAL: + fx_severity = FX_LOG_FATAL; + break; + default: + fx_severity = FX_LOG_INFO; + break; + } + // Temporarily remove the trailing newline from |str_newline|'s C-string + // representation, since fx_logger will add a newline of its own. + str_newline.pop_back(); + // Ideally the tag would be the same as the caller, but this is not + // supported right now. + fx_logger_log_with_source(fx_log_get_logger(), + fx_severity, + /*tag=*/nullptr, + file_path_, + line_, + str_newline.c_str() + message_start_); + str_newline.push_back('\n'); +#endif // BUILDFLAG(IS_*) + } + + if (severity_ == LOG_FATAL) { +#if defined(COMPILER_MSVC) + __debugbreak(); +#if defined(ARCH_CPU_X86_FAMILY) + __ud2(); +#elif defined(ARCH_CPU_ARM64) + __hlt(0); +#else +#error Unsupported Windows Arch +#endif +#elif defined(ARCH_CPU_X86_FAMILY) + asm("int3; ud2;"); +#elif defined(ARCH_CPU_ARMEL) + asm("bkpt #0; udf #0;"); +#elif defined(ARCH_CPU_ARM64) + asm("brk #0; hlt #0;"); +#else + __builtin_trap(); +#endif + } +} + +void LogMessage::Init(const char* function) { + std::string file_name(file_path_); +#if BUILDFLAG(IS_WIN) + size_t last_slash = file_name.find_last_of("\\/"); +#else + size_t last_slash = file_name.find_last_of('/'); +#endif + if (last_slash != std::string::npos) { + file_name.assign(file_name.substr(last_slash + 1)); + } + +#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_FUCHSIA) + pid_t pid = getpid(); +#elif BUILDFLAG(IS_WIN) + DWORD pid = GetCurrentProcessId(); +#endif + +#if BUILDFLAG(IS_APPLE) + uint64_t thread; + pthread_threadid_np(pthread_self(), &thread); +#elif BUILDFLAG(IS_ANDROID) + pid_t thread = gettid(); +#elif BUILDFLAG(IS_LINUX) + pid_t thread = static_cast(syscall(__NR_gettid)); +#elif BUILDFLAG(IS_WIN) + DWORD thread = GetCurrentThreadId(); +#endif + + // On Fuchsia, the platform is responsible for adding the process id and + // thread id, not the process itself. +#if !BUILDFLAG(IS_FUCHSIA) + stream_ << '[' + << pid + << ':' + << thread + << ':' + << std::setfill('0'); +#endif + + // On Fuchsia, the platform is responsible for adding the log timestamp, + // not the process itself. +#if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_FUCHSIA) + timeval tv; + gettimeofday(&tv, nullptr); + tm local_time; + localtime_r(&tv.tv_sec, &local_time); + stream_ << std::setw(4) << local_time.tm_year + 1900 + << std::setw(2) << local_time.tm_mon + 1 + << std::setw(2) << local_time.tm_mday + << ',' + << std::setw(2) << local_time.tm_hour + << std::setw(2) << local_time.tm_min + << std::setw(2) << local_time.tm_sec + << '.' + << std::setw(6) << tv.tv_usec + << ':'; +#elif BUILDFLAG(IS_WIN) + SYSTEMTIME local_time; + GetLocalTime(&local_time); + stream_ << std::setw(4) << local_time.wYear + << std::setw(2) << local_time.wMonth + << std::setw(2) << local_time.wDay + << ',' + << std::setw(2) << local_time.wHour + << std::setw(2) << local_time.wMinute + << std::setw(2) << local_time.wSecond + << '.' + << std::setw(3) << local_time.wMilliseconds + << ':'; +#endif + + // On Fuchsia, ~LogMessage() will add the severity, filename and line + // number when LOG_TO_SYSTEM_DEBUG_LOG is enabled, but not on + // LOG_TO_STDERR so if LOG_TO_STDERR is enabled, print them here with + // potentially repetition if LOG_TO_SYSTEM_DEBUG_LOG is also enabled. +#if BUILDFLAG(IS_FUCHSIA) + if ((g_logging_destination & LOG_TO_STDERR)) { +#endif + if (severity_ >= 0) { + stream_ << log_severity_names[severity_]; + } else { + stream_ << "VERBOSE" << -severity_; + } + + stream_ << ' ' + << file_name + << ':' + << line_ + << "] "; +#if BUILDFLAG(IS_FUCHSIA) + } +#endif + + message_start_ = stream_.str().size(); +} + +#if BUILDFLAG(IS_WIN) + +unsigned long GetLastSystemErrorCode() { + return GetLastError(); +} + +Win32ErrorLogMessage::Win32ErrorLogMessage(const char* function, + const char* file_path, + int line, + LogSeverity severity, + unsigned long err) + : LogMessage(function, file_path, line, severity), err_(err) { +} + +Win32ErrorLogMessage::~Win32ErrorLogMessage() { + stream() << ": " << SystemErrorCodeToString(err_); +} + +#elif BUILDFLAG(IS_POSIX) + +ErrnoLogMessage::ErrnoLogMessage(const char* function, + const char* file_path, + int line, + LogSeverity severity, + int err) + : LogMessage(function, file_path, line, severity), + err_(err) { +} + +ErrnoLogMessage::~ErrnoLogMessage() { + stream() << ": " + << base::safe_strerror(err_) + << " (" + << err_ + << ")"; +} + +#endif // BUILDFLAG(IS_POSIX) + +} // namespace logging + +std::ostream& std::operator<<(std::ostream& out, const std::u16string& str) { + return out << base::UTF16ToUTF8(str); +} diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/logging.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/logging.h new file mode 100644 index 000000000..a12353578 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/logging.h @@ -0,0 +1,317 @@ +// Copyright 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_LOGGING_H_ +#define MINI_CHROMIUM_BASE_LOGGING_H_ + +#include +#include + +#include +#include +#include + +#include "base/check.h" +#include "base/check_op.h" +#include "base/notreached.h" +#include "build/build_config.h" + +namespace logging { + +// A bitmask of potential logging destinations. +using LoggingDestination = uint32_t; + +// Specifies where logs will be written. Multiple destinations can be specified +// with bitwise OR. +// Unless destination is LOG_NONE, all logs with severity ERROR and above will +// be written to stderr in addition to the specified destination. +enum : LoggingDestination { + LOG_NONE = 0, + LOG_TO_FILE = 1 << 0, + LOG_TO_SYSTEM_DEBUG_LOG = 1 << 1, + LOG_TO_STDERR = 1 << 2, + + LOG_TO_ALL = LOG_TO_FILE | LOG_TO_SYSTEM_DEBUG_LOG | LOG_TO_STDERR, + +#if BUILDFLAG(IS_WIN) + LOG_DEFAULT = LOG_TO_FILE, +#elif BUILDFLAG(IS_FUCHSIA) + LOG_DEFAULT = LOG_TO_SYSTEM_DEBUG_LOG, +#elif BUILDFLAG(IS_POSIX) + LOG_DEFAULT = LOG_TO_SYSTEM_DEBUG_LOG | LOG_TO_STDERR, +#endif +}; + +struct LoggingSettings { + LoggingDestination logging_dest = LOG_DEFAULT; +}; + +// Sets the logging destination. +// +// TODO(jperaza): LOG_TO_FILE is not yet supported. +bool InitLogging(const LoggingSettings& settings); + +typedef int LogSeverity; +const LogSeverity LOG_VERBOSE = -1; +const LogSeverity LOG_INFO = 0; +const LogSeverity LOG_WARNING = 1; +const LogSeverity LOG_ERROR = 2; +const LogSeverity LOG_ERROR_REPORT = 3; +const LogSeverity LOG_FATAL = 4; +const LogSeverity LOG_NUM_SEVERITIES = 5; + +#if defined(NDEBUG) +const LogSeverity LOG_DFATAL = LOG_ERROR; +#else +const LogSeverity LOG_DFATAL = LOG_FATAL; +#endif + +typedef bool (*LogMessageHandlerFunction)(LogSeverity severity, + const char* file_poath, + int line, + size_t message_start, + const std::string& string); + +void SetLogMessageHandler(LogMessageHandlerFunction log_message_handler); +LogMessageHandlerFunction GetLogMessageHandler(); + +static inline int GetMinLogLevel() { + return LOG_INFO; +} + +static inline int GetVlogLevel(const char*) { + return std::numeric_limits::max(); +} + +#if BUILDFLAG(IS_WIN) +// This is just ::GetLastError, but out-of-line to avoid including windows.h in +// such a widely used place. +unsigned long GetLastSystemErrorCode(); +std::string SystemErrorCodeToString(unsigned long error_code); +#elif BUILDFLAG(IS_POSIX) +static inline int GetLastSystemErrorCode() { + return errno; +} +#endif + +class LogMessage { + public: + LogMessage(const char* function, + const char* file_path, + int line, + LogSeverity severity); + LogMessage(const char* function, + const char* file_path, + int line, + std::string* result); + + LogMessage(const LogMessage&) = delete; + LogMessage& operator=(const LogMessage&) = delete; + + ~LogMessage(); + + std::ostream& stream() { return stream_; } + + private: + void Init(const char* function); + + std::ostringstream stream_; + const char* file_path_; + size_t message_start_; + const int line_; + LogSeverity severity_; +}; + +class LogMessageVoidify { + public: + LogMessageVoidify() {} + + void operator&(const std::ostream&) const {} +}; + +#if BUILDFLAG(IS_WIN) +class Win32ErrorLogMessage : public LogMessage { + public: + Win32ErrorLogMessage(const char* function, + const char* file_path, + int line, + LogSeverity severity, + unsigned long err); + + Win32ErrorLogMessage(const Win32ErrorLogMessage&) = delete; + Win32ErrorLogMessage& operator=(const Win32ErrorLogMessage&) = delete; + + ~Win32ErrorLogMessage(); + + private: + unsigned long err_; +}; +#elif BUILDFLAG(IS_POSIX) +class ErrnoLogMessage : public LogMessage { + public: + ErrnoLogMessage(const char* function, + const char* file_path, + int line, + LogSeverity severity, + int err); + + ErrnoLogMessage(const ErrnoLogMessage&) = delete; + ErrnoLogMessage& operator=(const ErrnoLogMessage&) = delete; + + ~ErrnoLogMessage(); + + private: + int err_; +}; +#endif + +} // namespace logging + +#if defined(COMPILER_MSVC) +#define FUNCTION_SIGNATURE __FUNCSIG__ +#else +#define FUNCTION_SIGNATURE __PRETTY_FUNCTION__ +#endif + +#define COMPACT_GOOGLE_LOG_EX_INFO(ClassName, ...) \ + logging::ClassName(FUNCTION_SIGNATURE, __FILE__, __LINE__, \ + logging::LOG_INFO, ## __VA_ARGS__) +#define COMPACT_GOOGLE_LOG_EX_WARNING(ClassName, ...) \ + logging::ClassName(FUNCTION_SIGNATURE, __FILE__, __LINE__, \ + logging::LOG_WARNING, ## __VA_ARGS__) +#define COMPACT_GOOGLE_LOG_EX_ERROR(ClassName, ...) \ + logging::ClassName(FUNCTION_SIGNATURE, __FILE__, __LINE__, \ + logging::LOG_ERROR, ## __VA_ARGS__) +#define COMPACT_GOOGLE_LOG_EX_ERROR_REPORT(ClassName, ...) \ + logging::ClassName(FUNCTION_SIGNATURE, __FILE__, __LINE__, \ + logging::LOG_ERROR_REPORT, ## __VA_ARGS__) +#define COMPACT_GOOGLE_LOG_EX_FATAL(ClassName, ...) \ + logging::ClassName(FUNCTION_SIGNATURE, __FILE__, __LINE__, \ + logging::LOG_FATAL, ## __VA_ARGS__) +#define COMPACT_GOOGLE_LOG_EX_DFATAL(ClassName, ...) \ + logging::ClassName(FUNCTION_SIGNATURE, __FILE__, __LINE__, \ + logging::LOG_DFATAL, ## __VA_ARGS__) + +#define COMPACT_GOOGLE_LOG_INFO \ + COMPACT_GOOGLE_LOG_EX_INFO(LogMessage) +#define COMPACT_GOOGLE_LOG_WARNING \ + COMPACT_GOOGLE_LOG_EX_WARNING(LogMessage) +#define COMPACT_GOOGLE_LOG_ERROR \ + COMPACT_GOOGLE_LOG_EX_ERROR(LogMessage) +#define COMPACT_GOOGLE_LOG_ERROR_REPORT \ + COMPACT_GOOGLE_LOG_EX_ERROR_REPORT(LogMessage) +#define COMPACT_GOOGLE_LOG_FATAL \ + COMPACT_GOOGLE_LOG_EX_FATAL(LogMessage) +#define COMPACT_GOOGLE_LOG_DFATAL \ + COMPACT_GOOGLE_LOG_EX_DFATAL(LogMessage) + +#if BUILDFLAG(IS_WIN) + +// wingdi.h defines ERROR 0. We don't want to include windows.h here, and we +// want to allow "LOG(ERROR)", which will expand to LOG_0. + +// This will not cause a warning if the RHS text is identical to that in +// wingdi.h (which it is). +#define ERROR 0 + +#define COMPACT_GOOGLE_LOG_EX_0(ClassName, ...) \ + COMPACT_GOOGLE_LOG_EX_ERROR(ClassName , ##__VA_ARGS__) +#define COMPACT_GOOGLE_LOG_0 COMPACT_GOOGLE_LOG_ERROR +namespace logging { +const LogSeverity LOG_0 = LOG_ERROR; +} // namespace logging + +#endif // BUILDFLAG(IS_WIN) + +#define LAZY_STREAM(stream, condition) \ + !(condition) ? (void) 0 : ::logging::LogMessageVoidify() & (stream) + +#define LOG_IS_ON(severity) \ + ((::logging::LOG_ ## severity) >= ::logging::GetMinLogLevel()) +#define VLOG_IS_ON(verbose_level) \ + ((verbose_level) <= ::logging::GetVlogLevel(__FILE__)) + +#define LOG_STREAM(severity) COMPACT_GOOGLE_LOG_ ## severity.stream() +#define VLOG_STREAM(verbose_level) \ + logging::LogMessage(FUNCTION_SIGNATURE, __FILE__, __LINE__, \ + -verbose_level).stream() + +#if BUILDFLAG(IS_WIN) +#define PLOG_STREAM(severity) COMPACT_GOOGLE_LOG_EX_ ## severity( \ + Win32ErrorLogMessage, ::logging::GetLastSystemErrorCode()).stream() +#define VPLOG_STREAM(verbose_level) \ + logging::Win32ErrorLogMessage(FUNCTION_SIGNATURE, __FILE__, __LINE__, \ + -verbose_level, \ + ::logging::GetLastSystemErrorCode()).stream() +#elif BUILDFLAG(IS_POSIX) +#define PLOG_STREAM(severity) COMPACT_GOOGLE_LOG_EX_ ## severity( \ + ErrnoLogMessage, ::logging::GetLastSystemErrorCode()).stream() +#define VPLOG_STREAM(verbose_level) \ + logging::ErrnoLogMessage(FUNCTION_SIGNATURE, __FILE__, __LINE__, \ + -verbose_level, \ + ::logging::GetLastSystemErrorCode()).stream() +#endif + +#define LOG(severity) LAZY_STREAM(LOG_STREAM(severity), LOG_IS_ON(severity)) +#define LOG_IF(severity, condition) \ + LAZY_STREAM(LOG_STREAM(severity), LOG_IS_ON(severity) && (condition)) +#define LOG_ASSERT(condition) \ + LOG_IF(FATAL, !(condition)) << "Assertion failed: " # condition ". " + +#define VLOG(verbose_level) \ + LAZY_STREAM(VLOG_STREAM(verbose_level), VLOG_IS_ON(verbose_level)) +#define VLOG_IF(verbose_level, condition) \ + LAZY_STREAM(VLOG_STREAM(verbose_level), \ + VLOG_IS_ON(verbose_level) && (condition)) + +#define PLOG(severity) LAZY_STREAM(PLOG_STREAM(severity), LOG_IS_ON(severity)) +#define PLOG_IF(severity, condition) \ + LAZY_STREAM(PLOG_STREAM(severity), LOG_IS_ON(severity) && (condition)) + +#define VPLOG(verbose_level) \ + LAZY_STREAM(VPLOG_STREAM(verbose_level), VLOG_IS_ON(verbose_level)) +#define VPLOG_IF(verbose_level, condition) \ + LAZY_STREAM(VPLOG_STREAM(verbose_level), \ + VLOG_IS_ON(verbose_level) && (condition)) + +#if defined(NDEBUG) +#define DLOG_IS_ON(severity) 0 +#define DVLOG_IS_ON(verbose_level) 0 +#define DCHECK_IS_ON() 0 +#else +#define DLOG_IS_ON(severity) LOG_IS_ON(severity) +#define DVLOG_IS_ON(verbose_level) VLOG_IS_ON(verbose_level) +#define DCHECK_IS_ON() 1 +#endif + +#define DLOG(severity) LAZY_STREAM(LOG_STREAM(severity), DLOG_IS_ON(severity)) +#define DLOG_IF(severity, condition) \ + LAZY_STREAM(LOG_STREAM(severity), DLOG_IS_ON(severity) && (condition)) +#define DLOG_ASSERT(condition) \ + DLOG_IF(FATAL, !(condition)) << "Assertion failed: " # condition ". " + +#define DVLOG(verbose_level) \ + LAZY_STREAM(VLOG_STREAM(verbose_level), DVLOG_IS_ON(verbose_level)) +#define DVLOG_IF(verbose_level, condition) \ + LAZY_STREAM(VLOG_STREAM(verbose_level), \ + DVLOG_IS_ON(verbose_level) && (condition)) + +#define DPLOG(severity) LAZY_STREAM(PLOG_STREAM(severity), DLOG_IS_ON(severity)) +#define DPLOG_IF(severity, condition) \ + LAZY_STREAM(PLOG_STREAM(severity), DLOG_IS_ON(severity) && (condition)) + +#define DVPLOG(verbose_level) \ + LAZY_STREAM(VPLOG_STREAM(verbose_level), DVLOG_IS_ON(verbose_level)) +#define DVPLOG_IF(verbose_level, condition) \ + LAZY_STREAM(VPLOG_STREAM(verbose_level), \ + DVLOG_IS_ON(verbose_level) && (condition)) + +#undef assert +#define assert(condition) DLOG_ASSERT(condition) + +namespace std { +ostream& operator<<(ostream& out, const u16string& str); +} // namespace std + +#endif // MINI_CHROMIUM_BASE_LOGGING_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/close_nocancel.cc b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/close_nocancel.cc new file mode 100644 index 000000000..8971e731c --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/close_nocancel.cc @@ -0,0 +1,70 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +// http://crbug.com/269623 +// http://openradar.appspot.com/14999594 +// +// When the default version of close used on Mac OS X fails with EINTR, the +// file descriptor is not in a deterministic state. It may have been closed, +// or it may not have been. This makes it impossible to gracefully recover +// from the error. If the close is retried after the FD has been closed, the +// subsequent close can report EBADF, or worse, it can close an unrelated FD +// opened by another thread. If the close is not retried after the FD has been +// left open, the FD is leaked. Neither of these are good options. +// +// Mac OS X provides an alternate version of close, close$NOCANCEL. This +// version will never fail with EINTR before the FD is actually closed. With +// this version, it is thus safe to call close without checking for EINTR (as +// the HANDLE_EINTR macro does) and not risk leaking the FD. In fact, mixing +// this verison of close with HANDLE_EINTR is hazardous. +// +// The $NOCANCEL variants of various system calls are activated by compiling +// with __DARWIN_NON_CANCELABLE, which prevents them from being pthread +// cancellation points. Rather than taking such a heavy-handed approach, this +// file implements an alternative: to use the $NOCANCEL variant of close (thus +// preventing it from being a pthread cancellation point) without affecting +// any other system calls. +// +// This file operates by providing a close function with the non-$NOCANCEL +// symbol name expected for the compilation environment as set by +// and (the DARWIN_ALIAS_C macro). That name is set by an asm +// label on the declaration of the close function, so the definition of that +// function receives that name. The function calls the $NOCANCEL variant, which +// is resolved from libsyscall. By linking with this version of close prior to +// the libsyscall version, close's implementation is overridden. + +#include +#include + +// If the non-cancelable variants of all system calls have already been +// chosen, do nothing. +#if !__DARWIN_NON_CANCELABLE + +extern "C" { + +#if !__DARWIN_ONLY_UNIX_CONFORMANCE + +// When there's a choice between UNIX2003 and pre-UNIX2003. There's no +// close$NOCANCEL symbol in this case, so use close$NOCANCEL$UNIX2003 as the +// implementation. It does the same thing that close$NOCANCEL would do. +#define close_implementation close$NOCANCEL$UNIX2003 + +#else // __DARWIN_ONLY_UNIX_CONFORMANCE + +// When only UNIX2003 is supported: +#define close_implementation close$NOCANCEL + +#endif + +int close_implementation(int fd); + +int close(int fd) { + return close_implementation(fd); +} + +#undef close_implementation + +} // extern "C" + +#endif // !__DARWIN_NON_CANCELABLE diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/foundation_util.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/foundation_util.h new file mode 100644 index 000000000..ee9deafbd --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/foundation_util.h @@ -0,0 +1,188 @@ +// Copyright 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_MAC_FOUNDATION_UTIL_H_ +#define MINI_CHROMIUM_BASE_MAC_FOUNDATION_UTIL_H_ + +#include "base/logging.h" +#include "build/build_config.h" + +#if BUILDFLAG(IS_IOS) +#include +#else +#include +#endif + +#if defined(__OBJC__) +#import +#else // defined(__OBJC__) +#include +#endif // defined(__OBJC__) + +#if !defined(__OBJC__) +#define OBJC_CPP_CLASS_DECL(x) class x; +#else // defined(__OBJC__) +#define OBJC_CPP_CLASS_DECL(x) +#endif // defined(__OBJC__) + +// Convert toll-free bridged CFTypes to NSTypes and vice-versa. This does not +// autorelease |cf_val|. This is useful for the case where there is a CFType in +// a call that expects an NSType and the compiler is complaining about const +// casting problems. +// The calls are used like this: +// NSString *foo = CFToNSCast(CFSTR("Hello")); +// CFStringRef foo2 = NSToCFCast(@"Hello"); +// The macro magic below is to enforce safe casting. It could possibly have +// been done using template function specialization, but template function +// specialization doesn't always work intuitively, +// (http://www.gotw.ca/publications/mill17.htm) so the trusty combination +// of macros and function overloading is used instead. + +#define CF_TO_NS_CAST_DECL(TypeCF, TypeNS) \ +OBJC_CPP_CLASS_DECL(TypeNS) \ +\ +namespace base { \ +namespace mac { \ +TypeNS* CFToNSCast(TypeCF##Ref cf_val); \ +TypeCF##Ref NSToCFCast(TypeNS* ns_val); \ +} \ +} +#define CF_TO_NS_MUTABLE_CAST_DECL(name) \ +CF_TO_NS_CAST_DECL(CF##name, NS##name) \ +OBJC_CPP_CLASS_DECL(NSMutable##name) \ +\ +namespace base { \ +namespace mac { \ +NSMutable##name* CFToNSCast(CFMutable##name##Ref cf_val); \ +CFMutable##name##Ref NSToCFCast(NSMutable##name* ns_val); \ +} \ +} + +// List of toll-free bridged types taken from: +// http://www.cocoadev.com/index.pl?TollFreeBridged + +CF_TO_NS_MUTABLE_CAST_DECL(Array) +CF_TO_NS_MUTABLE_CAST_DECL(AttributedString) +CF_TO_NS_CAST_DECL(CFCalendar, NSCalendar) +CF_TO_NS_MUTABLE_CAST_DECL(CharacterSet) +CF_TO_NS_MUTABLE_CAST_DECL(Data) +CF_TO_NS_CAST_DECL(CFDate, NSDate) +CF_TO_NS_MUTABLE_CAST_DECL(Dictionary) +CF_TO_NS_CAST_DECL(CFError, NSError) +CF_TO_NS_CAST_DECL(CFLocale, NSLocale) +CF_TO_NS_CAST_DECL(CFNumber, NSNumber) +CF_TO_NS_CAST_DECL(CFRunLoopTimer, NSTimer) +CF_TO_NS_CAST_DECL(CFTimeZone, NSTimeZone) +CF_TO_NS_MUTABLE_CAST_DECL(Set) +CF_TO_NS_CAST_DECL(CFReadStream, NSInputStream) +CF_TO_NS_CAST_DECL(CFWriteStream, NSOutputStream) +CF_TO_NS_MUTABLE_CAST_DECL(String) +CF_TO_NS_CAST_DECL(CFURL, NSURL) + +#undef CF_TO_NS_CAST_DECL +#undef CF_TO_NS_MUTABLE_CAST_DECL +#undef OBJC_CPP_CLASS_DECL + +namespace base { +namespace mac { + +// CFCast<>() and CFCastStrict<>() cast a basic CFTypeRef to a more +// specific CoreFoundation type. The compatibility of the passed +// object is found by comparing its opaque type against the +// requested type identifier. If the supplied object is not +// compatible with the requested return type, CFCast<>() returns +// NULL and CFCastStrict<>() will DCHECK. Providing a NULL pointer +// to either variant results in NULL being returned without +// triggering any DCHECK. +// +// Example usage: +// CFNumberRef some_number = base::mac::CFCast( +// CFArrayGetValueAtIndex(array, index)); +// +// CFTypeRef hello = CFSTR("hello world"); +// CFStringRef some_string = base::mac::CFCastStrict(hello); + +template +T CFCast(const CFTypeRef& cf_val); + +template +T CFCastStrict(const CFTypeRef& cf_val); + +#define CF_CAST_DECL(TypeCF) \ +template<> TypeCF##Ref \ +CFCast(const CFTypeRef& cf_val); \ + \ +template<> TypeCF##Ref \ +CFCastStrict(const CFTypeRef& cf_val) + +CF_CAST_DECL(CFArray); +CF_CAST_DECL(CFBag); +CF_CAST_DECL(CFBoolean); +CF_CAST_DECL(CFData); +CF_CAST_DECL(CFDate); +CF_CAST_DECL(CFDictionary); +CF_CAST_DECL(CFNull); +CF_CAST_DECL(CFNumber); +CF_CAST_DECL(CFSet); +CF_CAST_DECL(CFString); +CF_CAST_DECL(CFURL); +CF_CAST_DECL(CFUUID); + +CF_CAST_DECL(CGColor); + +CF_CAST_DECL(CTFont); +CF_CAST_DECL(CTRun); + +#if !BUILDFLAG(IS_IOS) +CF_CAST_DECL(SecACL); +CF_CAST_DECL(SecTrustedApplication); +#endif + +#undef CF_CAST_DECL + +#if defined(__OBJC__) + +// ObjCCast<>() and ObjCCastStrict<>() cast a basic id to a more +// specific (NSObject-derived) type. The compatibility of the passed +// object is found by checking if it's a kind of the requested type +// identifier. If the supplied object is not compatible with the +// requested return type, ObjCCast<>() returns nil and +// ObjCCastStrict<>() will DCHECK. Providing a nil pointer to either +// variant results in nil being returned without triggering any DCHECK. +// +// The strict variant is useful when retrieving a value from a +// collection which only has values of a specific type, e.g. an +// NSArray of NSStrings. The non-strict variant is useful when +// retrieving values from data that you can't fully control. For +// example, a plist read from disk may be beyond your exclusive +// control, so you'd only want to check that the values you retrieve +// from it are of the expected types, but not crash if they're not. +// +// Example usage: +// NSString* version = base::mac::ObjCCast( +// [bundle objectForInfoDictionaryKey:@"CFBundleShortVersionString"]); +// +// NSString* str = base::mac::ObjCCastStrict( +// [ns_arr_of_ns_strs objectAtIndex:0]); +template +T* ObjCCast(id objc_val) { + if ([objc_val isKindOfClass:[T class]]) { + return reinterpret_cast(objc_val); + } + return nil; +} + +template +T* ObjCCastStrict(id objc_val) { + T* rv = ObjCCast(objc_val); + DCHECK(objc_val == nil || rv); + return rv; +} + +#endif // defined(__OBJC__) + +} // namespace mac +} // namespace base + +#endif // MINI_CHROMIUM_BASE_MAC_FOUNDATION_UTIL_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/foundation_util.mm b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/foundation_util.mm new file mode 100644 index 000000000..1e86235ef --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/foundation_util.mm @@ -0,0 +1,116 @@ +// Copyright 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/mac/foundation_util.h" + +#if !BUILDFLAG(IS_IOS) +extern "C" { +CFTypeID SecACLGetTypeID(); +CFTypeID SecTrustedApplicationGetTypeID(); +} // extern "C" +#endif + +namespace base { +namespace mac { + +// Definitions for the corresponding CF_TO_NS_CAST_DECL macros in +// foundation_util.h. +#define CF_TO_NS_CAST_DEFN(TypeCF, TypeNS) \ +\ +TypeNS* CFToNSCast(TypeCF##Ref cf_val) { \ + DCHECK(!cf_val || TypeCF##GetTypeID() == CFGetTypeID(cf_val)); \ + TypeNS* ns_val = \ + const_cast(reinterpret_cast(cf_val)); \ + return ns_val; \ +} \ +\ +TypeCF##Ref NSToCFCast(TypeNS* ns_val) { \ + TypeCF##Ref cf_val = reinterpret_cast(ns_val); \ + DCHECK(!cf_val || TypeCF##GetTypeID() == CFGetTypeID(cf_val)); \ + return cf_val; \ +} + +#define CF_TO_NS_MUTABLE_CAST_DEFN(name) \ +CF_TO_NS_CAST_DEFN(CF##name, NS##name) \ +\ +NSMutable##name* CFToNSCast(CFMutable##name##Ref cf_val) { \ + DCHECK(!cf_val || CF##name##GetTypeID() == CFGetTypeID(cf_val)); \ + NSMutable##name* ns_val = reinterpret_cast(cf_val); \ + return ns_val; \ +} \ +\ +CFMutable##name##Ref NSToCFCast(NSMutable##name* ns_val) { \ + CFMutable##name##Ref cf_val = \ + reinterpret_cast(ns_val); \ + DCHECK(!cf_val || CF##name##GetTypeID() == CFGetTypeID(cf_val)); \ + return cf_val; \ +} + +CF_TO_NS_MUTABLE_CAST_DEFN(Array) +CF_TO_NS_MUTABLE_CAST_DEFN(AttributedString) +CF_TO_NS_CAST_DEFN(CFCalendar, NSCalendar) +CF_TO_NS_MUTABLE_CAST_DEFN(CharacterSet) +CF_TO_NS_MUTABLE_CAST_DEFN(Data) +CF_TO_NS_CAST_DEFN(CFDate, NSDate) +CF_TO_NS_MUTABLE_CAST_DEFN(Dictionary) +CF_TO_NS_CAST_DEFN(CFError, NSError) +CF_TO_NS_CAST_DEFN(CFLocale, NSLocale) +CF_TO_NS_CAST_DEFN(CFNumber, NSNumber) +CF_TO_NS_CAST_DEFN(CFRunLoopTimer, NSTimer) +CF_TO_NS_CAST_DEFN(CFTimeZone, NSTimeZone) +CF_TO_NS_MUTABLE_CAST_DEFN(Set) +CF_TO_NS_CAST_DEFN(CFReadStream, NSInputStream) +CF_TO_NS_CAST_DEFN(CFWriteStream, NSOutputStream) +CF_TO_NS_MUTABLE_CAST_DEFN(String) +CF_TO_NS_CAST_DEFN(CFURL, NSURL) + +#undef CF_TO_NS_CAST_DEFN +#undef CF_TO_NS_MUTABLE_CAST_DEFN + +#define CF_CAST_DEFN(TypeCF) \ +template<> TypeCF##Ref \ +CFCast(const CFTypeRef& cf_val) { \ + if (cf_val == NULL) { \ + return NULL; \ + } \ + if (CFGetTypeID(cf_val) == TypeCF##GetTypeID()) { \ + return (TypeCF##Ref)(cf_val); \ + } \ + return NULL; \ +} \ +\ +template<> TypeCF##Ref \ +CFCastStrict(const CFTypeRef& cf_val) { \ + TypeCF##Ref rv = CFCast(cf_val); \ + DCHECK(cf_val == NULL || rv); \ + return rv; \ +} + +CF_CAST_DEFN(CFArray) +CF_CAST_DEFN(CFBag) +CF_CAST_DEFN(CFBoolean) +CF_CAST_DEFN(CFData) +CF_CAST_DEFN(CFDate) +CF_CAST_DEFN(CFDictionary) +CF_CAST_DEFN(CFNull) +CF_CAST_DEFN(CFNumber) +CF_CAST_DEFN(CFSet) +CF_CAST_DEFN(CFString) +CF_CAST_DEFN(CFURL) +CF_CAST_DEFN(CFUUID) + +CF_CAST_DEFN(CGColor) + +CF_CAST_DEFN(CTFont) +CF_CAST_DEFN(CTRun) + +#if !BUILDFLAG(IS_IOS) +CF_CAST_DEFN(SecACL) +CF_CAST_DEFN(SecTrustedApplication) +#endif + +#undef CF_CAST_DEFN + +} // namespace mac +} // namespace base diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/mach_logging.cc b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/mach_logging.cc new file mode 100644 index 000000000..d3008e63f --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/mach_logging.cc @@ -0,0 +1,89 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/mac/mach_logging.h" + +#include +#include + +#include "base/strings/stringprintf.h" + +#if !BUILDFLAG(IS_IOS) +#include +#endif // !BUILDFLAG(IS_IOS) + +namespace { + +std::string FormatMachErrorNumber(mach_error_t mach_err) { + // For the os/kern subsystem, give the error number in decimal as in + // . Otherwise, give it in hexadecimal to make it easier + // to visualize the various bits. See . + if (mach_err >= 0 && mach_err < KERN_RETURN_MAX) { + return base::StringPrintf(" (%d)", mach_err); + } + return base::StringPrintf(" (0x%08x)", mach_err); +} + +} // namespace + +namespace logging { + +MachLogMessage::MachLogMessage(const char* function, + const char* file_path, + int line, + LogSeverity severity, + mach_error_t mach_err) + : LogMessage(function, file_path, line, severity), + mach_err_(mach_err) { +} + +MachLogMessage::~MachLogMessage() { + stream() << ": " + << mach_error_string(mach_err_) + << FormatMachErrorNumber(mach_err_); +} + +#if !BUILDFLAG(IS_IOS) + +BootstrapLogMessage::BootstrapLogMessage(const char* function, + const char* file_path, + int line, + LogSeverity severity, + kern_return_t bootstrap_err) + : LogMessage(function, file_path, line, severity), + bootstrap_err_(bootstrap_err) { +} + +BootstrapLogMessage::~BootstrapLogMessage() { + stream() << ": " + << bootstrap_strerror(bootstrap_err_); + + switch (bootstrap_err_) { + case BOOTSTRAP_SUCCESS: + case BOOTSTRAP_NOT_PRIVILEGED: + case BOOTSTRAP_NAME_IN_USE: + case BOOTSTRAP_UNKNOWN_SERVICE: + case BOOTSTRAP_SERVICE_ACTIVE: + case BOOTSTRAP_BAD_COUNT: + case BOOTSTRAP_NO_MEMORY: + case BOOTSTRAP_NO_CHILDREN: { + // Show known bootstrap errors in decimal because that's how they're + // defined in . + stream() << " (" << bootstrap_err_ << ")"; + break; + } + + default: { + // bootstrap_strerror passes unknown errors to mach_error_string, so + // format them as they would be if they were handled by + // MachErrorMessage. + stream() << FormatMachErrorNumber(bootstrap_err_); + break; + } + } +} + +#endif // !BUILDFLAG(IS_IOS) + +} // namespace logging diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/mach_logging.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/mach_logging.h new file mode 100644 index 000000000..94322c496 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/mach_logging.h @@ -0,0 +1,163 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_MAC_MACH_LOGGING_H_ +#define MINI_CHROMIUM_BASE_MAC_MACH_LOGGING_H_ + +#include + +#include "base/logging.h" +#include "build/build_config.h" + +// Use the MACH_LOG family of macros along with a mach_error_t (kern_return_t) +// containing a Mach error. The error value will be decoded so that logged +// messages explain the error. +// +// Use the BOOTSTRAP_LOG family of macros specifically for errors that occur +// while interoperating with the bootstrap subsystem. These errors will first +// be looked up as bootstrap error messages. If no match is found, they will +// be treated as generic Mach errors, as in MACH_LOG. +// +// Examples: +// +// kern_return_t kr = mach_timebase_info(&info); +// if (kr != KERN_SUCCESS) { +// MACH_LOG(ERROR, kr) << "mach_timebase_info"; +// } +// +// kr = vm_deallocate(task, address, size); +// MACH_DCHECK(kr == KERN_SUCCESS, kr) << "vm_deallocate"; + +namespace logging { + +class MachLogMessage : public logging::LogMessage { + public: + MachLogMessage(const char* function, + const char* file_path, + int line, + LogSeverity severity, + mach_error_t mach_err); + + MachLogMessage(const MachLogMessage&) = delete; + MachLogMessage& operator=(const MachLogMessage&) = delete; + + ~MachLogMessage(); + + private: + mach_error_t mach_err_; +}; + +} // namespace logging + +#define MACH_LOG_STREAM(severity, mach_err) \ + COMPACT_GOOGLE_LOG_EX_ ## severity(MachLogMessage, mach_err).stream() +#define MACH_VLOG_STREAM(verbose_level, mach_err) \ + logging::MachLogMessage(__PRETTY_FUNCTION__, __FILE__, __LINE__, \ + -verbose_level, mach_err).stream() + +#define MACH_LOG(severity, mach_err) \ + LAZY_STREAM(MACH_LOG_STREAM(severity, mach_err), LOG_IS_ON(severity)) +#define MACH_LOG_IF(severity, condition, mach_err) \ + LAZY_STREAM(MACH_LOG_STREAM(severity, mach_err), \ + LOG_IS_ON(severity) && (condition)) + +#define MACH_VLOG(verbose_level, mach_err) \ + LAZY_STREAM(MACH_VLOG_STREAM(verbose_level, mach_err), \ + VLOG_IS_ON(verbose_level)) +#define MACH_VLOG_IF(verbose_level, condition, mach_err) \ + LAZY_STREAM(MACH_VLOG_STREAM(verbose_level, mach_err), \ + VLOG_IS_ON(verbose_level) && (condition)) + +#define MACH_CHECK(condition, mach_err) \ + LAZY_STREAM(MACH_LOG_STREAM(FATAL, mach_err), !(condition)) \ + << "Check failed: " # condition << ". " + +#define MACH_DLOG(severity, mach_err) \ + LAZY_STREAM(MACH_LOG_STREAM(severity, mach_err), DLOG_IS_ON(severity)) +#define MACH_DLOG_IF(severity, condition, mach_err) \ + LAZY_STREAM(MACH_LOG_STREAM(severity, mach_err), \ + DLOG_IS_ON(severity) && (condition)) + +#define MACH_DVLOG(verbose_level, mach_err) \ + LAZY_STREAM(MACH_VLOG_STREAM(verbose_level, mach_err), \ + DVLOG_IS_ON(verbose_level)) +#define MACH_DVLOG_IF(verbose_level, condition, mach_err) \ + LAZY_STREAM(MACH_VLOG_STREAM(verbose_level, mach_err), \ + DVLOG_IS_ON(verbose_level) && (condition)) + +#define MACH_DCHECK(condition, mach_err) \ + LAZY_STREAM(MACH_LOG_STREAM(FATAL, mach_err), \ + DCHECK_IS_ON && !(condition)) \ + << "Check failed: " # condition << ". " + +#if !BUILDFLAG(IS_IOS) + +namespace logging { + +class BootstrapLogMessage : public logging::LogMessage { + public: + BootstrapLogMessage(const char* function, + const char* file_path, + int line, + LogSeverity severity, + kern_return_t bootstrap_err); + + BootstrapLogMessage(const BootstrapLogMessage&) = delete; + BootstrapLogMessage& operator=(const BootstrapLogMessage&) = delete; + + ~BootstrapLogMessage(); + + private: + kern_return_t bootstrap_err_; +}; + +} // namespace logging + +#define BOOTSTRAP_LOG_STREAM(severity, bootstrap_err) \ + COMPACT_GOOGLE_LOG_EX_ ## severity(BootstrapLogMessage, \ + bootstrap_err).stream() +#define BOOTSTRAP_VLOG_STREAM(verbose_level, bootstrap_err) \ + logging::BootstrapLogMessage(__PRETTY_FUNCTION__, __FILE__, __LINE__, \ + -verbose_level, bootstrap_err).stream() + +#define BOOTSTRAP_LOG(severity, bootstrap_err) \ + LAZY_STREAM(BOOTSTRAP_LOG_STREAM(severity, \ + bootstrap_err), LOG_IS_ON(severity)) +#define BOOTSTRAP_LOG_IF(severity, condition, bootstrap_err) \ + LAZY_STREAM(BOOTSTRAP_LOG_STREAM(severity, bootstrap_err), \ + LOG_IS_ON(severity) && (condition)) + +#define BOOTSTRAP_VLOG(verbose_level, bootstrap_err) \ + LAZY_STREAM(BOOTSTRAP_VLOG_STREAM(verbose_level, bootstrap_err), \ + VLOG_IS_ON(verbose_level)) +#define BOOTSTRAP_VLOG_IF(verbose_level, condition, bootstrap_err) \ + LAZY_STREAM(BOOTSTRAP_VLOG_STREAM(verbose_level, bootstrap_err), \ + VLOG_IS_ON(verbose_level) && (condition)) + +#define BOOTSTRAP_CHECK(condition, bootstrap_err) \ + LAZY_STREAM(BOOTSTRAP_LOG_STREAM(FATAL, bootstrap_err), !(condition)) \ + << "Check failed: " # condition << ". " + +#define BOOTSTRAP_DLOG(severity, bootstrap_err) \ + LAZY_STREAM(BOOTSTRAP_LOG_STREAM(severity, bootstrap_err), \ + DLOG_IS_ON(severity)) +#define BOOTSTRAP_DLOG_IF(severity, condition, bootstrap_err) \ + LAZY_STREAM(BOOTSTRAP_LOG_STREAM(severity, bootstrap_err), \ + DLOG_IS_ON(severity) && (condition)) + +#define BOOTSTRAP_DVLOG(verbose_level, bootstrap_err) \ + LAZY_STREAM(BOOTSTRAP_VLOG_STREAM(verbose_level, bootstrap_err), \ + DVLOG_IS_ON(verbose_level)) +#define BOOTSTRAP_DVLOG_IF(verbose_level, condition, bootstrap_err) \ + LAZY_STREAM(BOOTSTRAP_VLOG_STREAM(verbose_level, bootstrap_err), \ + DVLOG_IS_ON(verbose_level) && (condition)) + +#define BOOTSTRAP_DCHECK(condition, bootstrap_err) \ + LAZY_STREAM(BOOTSTRAP_LOG_STREAM(FATAL, bootstrap_err), \ + DCHECK_IS_ON && !(condition)) \ + << "Check failed: " # condition << ". " + +#endif // !BUILDFLAG(IS_IOS) + +#endif // MINI_CHROMIUM_BASE_MAC_MACH_LOGGING_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_cftyperef.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_cftyperef.h new file mode 100644 index 000000000..7ea09219f --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_cftyperef.h @@ -0,0 +1,34 @@ +// Copyright 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_MAC_SCOPED_CFTYPEREF_H_ +#define MINI_CHROMIUM_BASE_MAC_SCOPED_CFTYPEREF_H_ + +#include + +#include "base/mac/scoped_typeref.h" + +namespace base { + +namespace internal { + +template +struct ScopedCFTypeRefTraits { + static CFT InvalidValue() { return nullptr; } + static CFT Retain(CFT object) { + CFRetain(object); + return object; + } + static void Release(CFT object) { CFRelease(object); } +}; + +} // namespace internal + +template +using ScopedCFTypeRef = + ScopedTypeRef>; + +} // namespace base + +#endif // MINI_CHROMIUM_BASE_MAC_SCOPED_CFTYPEREF_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_ioobject.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_ioobject.h new file mode 100644 index 000000000..e653110a9 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_ioobject.h @@ -0,0 +1,35 @@ +// Copyright 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_MAC_SCOPED_IOOBJECT_H_ +#define MINI_CHROMIUM_BASE_MAC_SCOPED_IOOBJECT_H_ + +#include + +#include "base/mac/scoped_typeref.h" + +namespace base { +namespace mac { + +namespace internal { + +template +struct ScopedIOObjectTraits { + static IOT InvalidValue() { return IO_OBJECT_NULL; } + static IOT Retain(IOT iot) { + IOObjectRetain(iot); + return iot; + } + static void Release(IOT iot) { IOObjectRelease(iot); } +}; + +} // namespce internal + +template +using ScopedIOObject = ScopedTypeRef>; + +} // namespace mac +} // namespace base + +#endif // MINI_CHROMIUM_BASE_MAC_SCOPED_IOOBJECT_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_launch_data.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_launch_data.h new file mode 100644 index 000000000..8f8e759fc --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_launch_data.h @@ -0,0 +1,36 @@ +// Copyright 2011 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_MAC_SCOPED_LAUNCH_DATA_H_ +#define MINI_CHROMIUM_BASE_MAC_SCOPED_LAUNCH_DATA_H_ + +#include + +#include "base/scoped_generic.h" + +namespace base { +namespace mac { + +namespace internal { + +struct ScopedLaunchDataTraits { + static launch_data_t InvalidValue() { return nullptr; } + + static void Free(launch_data_t ldt) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + launch_data_free(ldt); +#pragma clang diagnostic pop + } +}; + +} // namespace internal + +using ScopedLaunchData = + ScopedGeneric; + +} // namespace mac +} // namespace base + +#endif // MINI_CHROMIUM_BASE_MAC_SCOPED_LAUNCH_DATA_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_mach_port.cc b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_mach_port.cc new file mode 100644 index 000000000..4a501da05 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_mach_port.cc @@ -0,0 +1,32 @@ +// Copyright 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/mac/scoped_mach_port.h" + +#include "base/mac/mach_logging.h" + +namespace base { +namespace mac { +namespace internal { + +void SendRightTraits::Free(mach_port_t port) { + kern_return_t kr = mach_port_deallocate(mach_task_self(), port); + MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "mach_port_deallocate"; +} + +void ReceiveRightTraits::Free(mach_port_t port) { + kern_return_t kr = + mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_RECEIVE, -1); + MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "mach_port_mod_refs"; +} + +void PortSetTraits::Free(mach_port_t port) { + kern_return_t kr = + mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_PORT_SET, -1); + MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr) << "mach_port_mod_refs"; +} + +} // namespace internal +} // namespace mac +} // namespace base diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_mach_port.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_mach_port.h new file mode 100644 index 000000000..f7275edb6 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_mach_port.h @@ -0,0 +1,49 @@ +// Copyright 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_MAC_SCOPED_MACH_PORT_H_ +#define MINI_CHROMIUM_BASE_MAC_SCOPED_MACH_PORT_H_ + +#include + +#include "base/scoped_generic.h" + +namespace base { +namespace mac { + +namespace internal { + +struct SendRightTraits { + static mach_port_t InvalidValue() { + return MACH_PORT_NULL; + } + static void Free(mach_port_t port); +}; + +struct ReceiveRightTraits { + static mach_port_t InvalidValue() { + return MACH_PORT_NULL; + } + static void Free(mach_port_t port); +}; + +struct PortSetTraits { + static mach_port_t InvalidValue() { + return MACH_PORT_NULL; + } + static void Free(mach_port_t port); +}; + +} // namespace internal + +using ScopedMachSendRight = + ScopedGeneric; +using ScopedMachReceiveRight = + ScopedGeneric; +using ScopedMachPortSet = ScopedGeneric; + +} // namespace mac +} // namespace base + +#endif // MINI_CHROMIUM_BASE_MAC_SCOPED_MACH_PORT_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_mach_vm.cc b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_mach_vm.cc new file mode 100644 index 000000000..1c28d9c7c --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_mach_vm.cc @@ -0,0 +1,33 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/mac/scoped_mach_vm.h" + +namespace base { +namespace mac { + +void ScopedMachVM::reset(vm_address_t address, vm_size_t size) { + DCHECK(address % PAGE_SIZE == 0); + DCHECK(size % PAGE_SIZE == 0); + + if (size_) { + if (address_ < address) { + vm_deallocate(mach_task_self(), + address_, + std::min(size_, address - address_)); + } + if (address_ + size_ > address + size) { + vm_address_t deallocate_start = std::max(address_, address + size); + vm_deallocate(mach_task_self(), + deallocate_start, + address_ + size_ - deallocate_start); + } + } + + address_ = address; + size_ = size; +} + +} // namespace mac +} // namespace base diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_mach_vm.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_mach_vm.h new file mode 100644 index 000000000..8464e162c --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_mach_vm.h @@ -0,0 +1,92 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_MAC_SCOPED_MACH_VM_H_ +#define MINI_CHROMIUM_BASE_MAC_SCOPED_MACH_VM_H_ + +#include + +#include + +#include "base/logging.h" + +// Use ScopedMachVM to supervise ownership of pages in the current process +// through the Mach VM subsystem. Pages allocated with vm_allocate can be +// released when exiting a scope with ScopedMachVM. +// +// The Mach VM subsystem operates on a page-by-page basis, and a single VM +// allocation managed by a ScopedMachVM object may span multiple pages. As far +// as Mach is concerned, allocated pages may be deallocated individually. This +// is in contrast to higher-level allocators such as malloc, where the base +// address of an allocation implies the size of an allocated block. +// Consequently, it is not sufficient to just pass the base address of an +// allocation to ScopedMachVM, it also needs to know the size of the +// allocation. To avoid any confusion, both the base address and size must +// be page-aligned. +// +// When dealing with Mach VM, base addresses will naturally be page-aligned, +// but user-specified sizes may not be. If there's a concern that a size is +// not page-aligned, use the mach_vm_round_page macro to correct it. +// +// Example: +// +// vm_address_t address = 0; +// vm_size_t size = 12345; // This requested size is not page-aligned. +// kern_return_t kr = +// vm_allocate(mach_task_self(), &address, size, VM_FLAGS_ANYWHERE); +// if (kr != KERN_SUCCESS) { +// return false; +// } +// ScopedMachVM vm_owner(address, mach_vm_round_page(size)); + +namespace base { +namespace mac { + +class ScopedMachVM { + public: + explicit ScopedMachVM(vm_address_t address = 0, vm_size_t size = 0) + : address_(address), + size_(size) { + DCHECK(address % PAGE_SIZE == 0); + DCHECK(size % PAGE_SIZE == 0); + } + + ScopedMachVM(const ScopedMachVM&) = delete; + ScopedMachVM& operator=(const ScopedMachVM&) = delete; + + ~ScopedMachVM() { + if (size_) { + vm_deallocate(mach_task_self(), address_, size_); + } + } + + void reset(vm_address_t address = 0, vm_size_t size = 0); + + vm_address_t address() const { + return address_; + } + + vm_size_t size() const { + return size_; + } + + void swap(ScopedMachVM& that) { + std::swap(address_, that.address_); + std::swap(size_, that.size_); + } + + void release() { + address_ = 0; + size_ = 0; + } + + private: + vm_address_t address_; + vm_size_t size_; +}; + +} // namespace mac +} // namespace base + +#endif // MINI_CHROMIUM_BASE_MAC_SCOPED_MACH_VM_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_nsautorelease_pool.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_nsautorelease_pool.h new file mode 100644 index 000000000..634c2f7de --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_nsautorelease_pool.h @@ -0,0 +1,55 @@ +// Copyright 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_MAC_SCOPED_NSAUTORELEASE_POOL_H_ +#define MINI_CHROMIUM_BASE_MAC_SCOPED_NSAUTORELEASE_POOL_H_ + +#include "build/build_config.h" + +#if BUILDFLAG(IS_APPLE) +#if defined(__OBJC__) +@class NSAutoreleasePool; +#else // __OBJC__ +class NSAutoreleasePool; +#endif // __OBJC__ +#endif // BUILDFLAG(IS_APPLE) + +namespace base { +namespace mac { + +// On the Mac, ScopedNSAutoreleasePool allocates an NSAutoreleasePool when +// instantiated and sends it a -drain message when destroyed. This allows an +// autorelease pool to be maintained in ordinary C++ code without bringing in +// any direct Objective-C dependency. +// +// On other platforms, ScopedNSAutoreleasePool is an empty object with no +// effects. This allows it to be used directly in cross-platform code without +// ugly #ifdefs. +class ScopedNSAutoreleasePool { + public: +#if !BUILDFLAG(IS_APPLE) + ScopedNSAutoreleasePool() {} + void Recycle() { } +#else // BUILDFLAG(IS_APPLE) + ScopedNSAutoreleasePool(); + + ScopedNSAutoreleasePool(const ScopedNSAutoreleasePool&) = delete; + ScopedNSAutoreleasePool& operator=(const ScopedNSAutoreleasePool&) = delete; + + ~ScopedNSAutoreleasePool(); + + // Clear out the pool in case its position on the stack causes it to be + // alive for long periods of time (such as the entire length of the app). + // Only use then when you're certain the items currently in the pool are + // no longer needed. + void Recycle(); + private: + NSAutoreleasePool* autorelease_pool_; +#endif // BUILDFLAG(IS_APPLE) +}; + +} // namespace mac +} // namespace base + +#endif // MINI_CHROMIUM_BASE_MAC_SCOPED_NSAUTORELEASE_POOL_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_nsautorelease_pool.mm b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_nsautorelease_pool.mm new file mode 100644 index 000000000..e8ad82cc0 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_nsautorelease_pool.mm @@ -0,0 +1,32 @@ +// Copyright 2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/mac/scoped_nsautorelease_pool.h" + +#import + +#include "base/logging.h" + +namespace base { +namespace mac { + +ScopedNSAutoreleasePool::ScopedNSAutoreleasePool() + : autorelease_pool_([[NSAutoreleasePool alloc] init]) { + DCHECK(autorelease_pool_); +} + +ScopedNSAutoreleasePool::~ScopedNSAutoreleasePool() { + [autorelease_pool_ drain]; +} + +// Cycle the internal pool, allowing everything there to get cleaned up and +// start anew. +void ScopedNSAutoreleasePool::Recycle() { + [autorelease_pool_ drain]; + autorelease_pool_ = [[NSAutoreleasePool alloc] init]; + DCHECK(autorelease_pool_); +} + +} // namespace mac +} // namespace base diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_nsobject.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_nsobject.h new file mode 100644 index 000000000..2e157a4a2 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_nsobject.h @@ -0,0 +1,70 @@ +// Copyright 2006-2008 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_MAC_SCOPED_NSOBJECT_H_ +#define MINI_CHROMIUM_BASE_MAC_SCOPED_NSOBJECT_H_ + +#import + +#include + +#include "base/compiler_specific.h" +#include "base/mac/scoped_typeref.h" + +namespace base { + +namespace internal { + +template +struct ScopedNSProtocolTraits { + static NST InvalidValue() { return nil; } + static NST Retain(NST nst) { return [nst retain]; } + static void Release(NST nst) { [nst release]; } +}; + +} // namespace internal + +template +class scoped_nsprotocol + : public ScopedTypeRef> { + public: + using ScopedTypeRef>::ScopedTypeRef; + + NST autorelease() { return [this->release() autorelease]; } +}; + +template +void swap(scoped_nsprotocol& p1, scoped_nsprotocol& p2) { + p1.swap(p2); +} + +template +bool operator==(C p1, const scoped_nsprotocol& p2) { + return p1 == p2.get(); +} + +template +bool operator!=(C p1, const scoped_nsprotocol& p2) { + return p1 != p2.get(); +} + +template +class scoped_nsobject : public scoped_nsprotocol { + public: + using scoped_nsprotocol::scoped_nsprotocol; + + static_assert(std::is_same::value == false, + "Use ScopedNSAutoreleasePool instead"); +}; + +template<> +class scoped_nsobject : public scoped_nsprotocol { + public: + using scoped_nsprotocol::scoped_nsprotocol; +}; + +} // namespace base + +#endif // MINI_CHROMIUM_BASE_MAC_SCOPED_NSOBJECT_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_typeref.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_typeref.h new file mode 100644 index 000000000..196451839 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/mac/scoped_typeref.h @@ -0,0 +1,85 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_MAC_SCOPED_TYPEREF_H_ +#define MINI_CHROMIUM_BASE_MAC_SCOPED_TYPEREF_H_ + +#include "base/logging.h" +#include "base/memory/scoped_policy.h" + +namespace base { + +template +struct ScopedTypeRefTraits; + +template > +class ScopedTypeRef { + public: + typedef T element_type; + + ScopedTypeRef( + T object = Traits::InvalidValue(), + base::scoped_policy::OwnershipPolicy policy = base::scoped_policy::ASSUME) + : object_(object) { + if (object_ && policy == base::scoped_policy::RETAIN) + object_ = Traits::Retain(object_); + } + + ScopedTypeRef(const ScopedTypeRef& that) : object_(that.object_) { + if (object_) + object_ = Traits::Retain(object_); + } + + ~ScopedTypeRef() { + if (object_) + Traits::Release(object_); + } + + ScopedTypeRef& operator=(const ScopedTypeRef& that) { + reset(that.get(), base::scoped_policy::RETAIN); + return *this; + } + + [[nodiscard]] T* InitializeInto() { + DCHECK(!object_); + return &object_; + } + + void reset(T object = Traits::InvalidValue(), + base::scoped_policy::OwnershipPolicy policy = + base::scoped_policy::ASSUME) { + if (object && policy == base::scoped_policy::RETAIN) + object = Traits::Retain(object); + if (object_) + Traits::Release(object_); + object_ = object; + } + + bool operator==(T that) const { return object_ == that; } + + bool operator!=(T that) const { return object_ != that; } + + operator T() const { return object_; } + + T get() const { return object_; } + + void swap(ScopedTypeRef& that) { + T temp = that.object_; + that.object_ = object_; + object_ = temp; + } + + [[nodiscard]] T release() { + T temp = object_; + object_ = Traits::InvalidValue(); + return temp; + } + + private: + T object_; +}; + +} // namespace base + +#endif // MINI_CHROMIUM_BASE_MAC_SCOPED_TYPEREF_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/memory/free_deleter.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/memory/free_deleter.h new file mode 100644 index 000000000..ade7ed59a --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/memory/free_deleter.h @@ -0,0 +1,25 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_MEMORY_FREE_DELETER_H_ +#define MINI_CHROMIUM_BASE_MEMORY_FREE_DELETER_H_ + +#include + +namespace base { + +// Function object which invokes 'free' on its parameter, which must be +// a pointer. Can be used to store malloc-allocated pointers in std::unique_ptr: +// +// std::unique_ptr foo_ptr( +// static_cast(malloc(sizeof(int)))); +struct FreeDeleter { + inline void operator()(void* ptr) const { + free(ptr); + } +}; + +} // namespace base + +#endif // MINI_CHROMIUM_BASE_MEMORY_FREE_DELETER_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/memory/page_size.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/memory/page_size.h new file mode 100644 index 000000000..dedf33141 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/memory/page_size.h @@ -0,0 +1,16 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_PROCESS_PROCESS_METRICS_H_ +#define MINI_CHROMIUM_BASE_PROCESS_PROCESS_METRICS_H_ + +#include + +namespace base { + +size_t GetPageSize(); + +} // namespace base + +#endif // MINI_CHROMIUM_BASE_PROCESS_PROCESS_METRICS_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/memory/page_size_posix.cc b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/memory/page_size_posix.cc new file mode 100644 index 000000000..6be176618 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/memory/page_size_posix.cc @@ -0,0 +1,15 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/memory/page_size.h" + +#include + +namespace base { + +size_t GetPageSize() { + return getpagesize(); +} + +} // namespace base diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/memory/page_size_win.cc b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/memory/page_size_win.cc new file mode 100644 index 000000000..9efd67676 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/memory/page_size_win.cc @@ -0,0 +1,13 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/memory/page_size.h" + +namespace base { + +size_t GetPageSize() { + return 4 * 1024; +} + +} // namespace base diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/memory/scoped_policy.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/memory/scoped_policy.h new file mode 100644 index 000000000..e86a638ab --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/memory/scoped_policy.h @@ -0,0 +1,25 @@ +// Copyright 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_MEMORY_SCOPED_POLICY_H_ +#define MINI_CHROMIUM_BASE_MEMORY_SCOPED_POLICY_H_ + +namespace base { +namespace scoped_policy { + +// Defines the ownership policy for a scoped object. +enum OwnershipPolicy { + // The scoped object takes ownership of an object by taking over an existing + // ownership claim. + ASSUME, + + // The scoped object will retain the the object and any initial ownership is + // not changed. + RETAIN +}; + +} // namespace scoped_policy +} // namespace base + +#endif // MINI_CHROMIUM_BASE_MEMORY_SCOPED_POLICY_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/metrics/histogram_functions.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/metrics/histogram_functions.h new file mode 100644 index 000000000..a96cb9dc8 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/metrics/histogram_functions.h @@ -0,0 +1,19 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_METRICS_HISTOGRAM_FUNCTIONS_H_ +#define MINI_CHROMIUM_BASE_METRICS_HISTOGRAM_FUNCTIONS_H_ + +#include + +// These are no-op stub versions of a subset of the functions from Chromium's +// base/metrics/histogram_functions.h. This allows us to instrument the Crashpad +// code as necessary, while not affecting out-of-Chromium builds. +namespace base { + +void UmaHistogramSparse(const std::string& name, int sample) {} + +} // namespace base + +#endif // MINI_CHROMIUM_BASE_METRICS_HISTOGRAM_FUNCTIONS_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/metrics/histogram_macros.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/metrics/histogram_macros.h new file mode 100644 index 000000000..2c5634058 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/metrics/histogram_macros.h @@ -0,0 +1,70 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_METRICS_HISTOGRAM_MACROS_H_ +#define MINI_CHROMIUM_BASE_METRICS_HISTOGRAM_MACROS_H_ + +// These are no-op stub versions of a subset of the macros from Chromium's +// base/metrics/histogram_macros.h. This allows us to instrument the Crashpad +// code as necessary, while not affecting out-of-Chromium builds. + +#define UMA_HISTOGRAM_UNUSED(x) (void)(x) + +#define UMA_HISTOGRAM_TIMES(name, sample) \ + UMA_HISTOGRAM_UNUSED(name), UMA_HISTOGRAM_UNUSED(sample) +#define UMA_HISTOGRAM_MEDIUM_TIMES(name, sample) \ + UMA_HISTOGRAM_UNUSED(name), UMA_HISTOGRAM_UNUSED(sample) +#define UMA_HISTOGRAM_LONG_TIMES(name, sample) \ + UMA_HISTOGRAM_UNUSED(name), UMA_HISTOGRAM_UNUSED(sample) +#define UMA_HISTOGRAM_LONG_TIMES_100(name, sample) \ + UMA_HISTOGRAM_UNUSED(name), UMA_HISTOGRAM_UNUSED(sample) +#define UMA_HISTOGRAM_CUSTOM_TIMES(name, sample, min, max, bucket_count) \ + UMA_HISTOGRAM_UNUSED(name), \ + UMA_HISTOGRAM_UNUSED(sample), \ + UMA_HISTOGRAM_UNUSED(min), \ + UMA_HISTOGRAM_UNUSED(max), \ + UMA_HISTOGRAM_UNUSED(bucket_count) + +#define UMA_HISTOGRAM_COUNTS(name, sample) \ + UMA_HISTOGRAM_UNUSED(name), UMA_HISTOGRAM_UNUSED(sample) +#define UMA_HISTOGRAM_COUNTS_100(name, sample) \ + UMA_HISTOGRAM_UNUSED(name), UMA_HISTOGRAM_UNUSED(sample) +#define UMA_HISTOGRAM_COUNTS_1000(name, sample) \ + UMA_HISTOGRAM_UNUSED(name), UMA_HISTOGRAM_UNUSED(sample) +#define UMA_HISTOGRAM_COUNTS_10000(name, sample) \ + UMA_HISTOGRAM_UNUSED(name), UMA_HISTOGRAM_UNUSED(sample) +#define UMA_HISTOGRAM_CUSTOM_COUNTS(name, sample, min, max, bucket_count) \ + UMA_HISTOGRAM_UNUSED(name), \ + UMA_HISTOGRAM_UNUSED(sample), \ + UMA_HISTOGRAM_UNUSED(min), \ + UMA_HISTOGRAM_UNUSED(max), \ + UMA_HISTOGRAM_UNUSED(bucket_count) + +#define UMA_HISTOGRAM_MEMORY_KB(name, sample) \ + UMA_HISTOGRAM_UNUSED(name), UMA_HISTOGRAM_UNUSED(sample) +#define UMA_HISTOGRAM_MEMORY_MB(name, sample) \ + UMA_HISTOGRAM_UNUSED(name), UMA_HISTOGRAM_UNUSED(sample) +#define UMA_HISTOGRAM_MEMORY_LARGE_MB(name, sample) \ + UMA_HISTOGRAM_UNUSED(name), UMA_HISTOGRAM_UNUSED(sample) + +#define UMA_HISTOGRAM_PERCENTAGE(name, under_one_hundred) \ + UMA_HISTOGRAM_UNUSED(name), UMA_HISTOGRAM_UNUSED(under_one_hundred) + +#define UMA_HISTOGRAM_BOOLEAN(name, sample) \ + UMA_HISTOGRAM_UNUSED(name), UMA_HISTOGRAM_UNUSED(sample) + +#define UMA_HISTOGRAM_ENUMERATION(name, sample, boundary_value) \ + UMA_HISTOGRAM_UNUSED(name), \ + UMA_HISTOGRAM_UNUSED(sample), \ + UMA_HISTOGRAM_UNUSED(boundary_value) +#define UMA_STABILITY_HISTOGRAM_ENUMERATION(name, sample, boundary_value) \ + UMA_HISTOGRAM_UNUSED(name), \ + UMA_HISTOGRAM_UNUSED(sample), \ + UMA_HISTOGRAM_UNUSED(boundary_value) +#define UMA_HISTOGRAM_CUSTOM_ENUMERATION(name, sample, custom_ranges) \ + UMA_HISTOGRAM_UNUSED(name), \ + UMA_HISTOGRAM_UNUSED(sample), \ + UMA_HISTOGRAM_UNUSED(custom_ranges) + +#endif // MINI_CHROMIUM_BASE_METRICS_HISTOGRAM_MACROS_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/metrics/persistent_histogram_allocator.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/metrics/persistent_histogram_allocator.h new file mode 100644 index 000000000..2da582b58 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/metrics/persistent_histogram_allocator.h @@ -0,0 +1,40 @@ +// Copyright 2016 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_METRICS_PERSISTENT_HISTOGRAM_ALLOCATOR_H_ +#define MINI_CHROMIUM_BASE_METRICS_PERSISTENT_HISTOGRAM_ALLOCATOR_H_ + +#include +#include + +#include "base/files/file_path.h" +#include "base/strings/string_piece.h" + +// This file is a non-functional stub of the Chromium base interface to allow +// Crashpad to set up and tear down histogram storage when built against +// Chromium. When Crashpad is built standalone these stubs are used which +// silently do nothing. +namespace base { + +class GlobalHistogramAllocator { + public: + GlobalHistogramAllocator(const GlobalHistogramAllocator&) = delete; + GlobalHistogramAllocator& operator=(const GlobalHistogramAllocator&) = delete; + + static bool CreateWithActiveFileInDir(const base::FilePath&, + size_t, + uint64_t, + base::StringPiece sp) { + return false; + } + + void CreateTrackingHistograms(base::StringPiece) {} + void DeletePersistentLocation() {} + + static GlobalHistogramAllocator* Get() { return nullptr; } +}; + +} // namespace base + +#endif // MINI_CHROMIUM_BASE_METRICS_PERSISTENT_HISTOGRAM_ALLOCATOR_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/notreached.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/notreached.h new file mode 100644 index 000000000..3a3aa8c4b --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/notreached.h @@ -0,0 +1,12 @@ +// Copyright 2020 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_NOTREACHED_H_ +#define MINI_CHROMIUM_BASE_NOTREACHED_H_ + +#include "base/check.h" + +#define NOTREACHED() DCHECK(false) + +#endif // MINI_CHROMIUM_BASE_NOTREACHED_H_ diff --git a/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/numerics/checked_math.h b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/numerics/checked_math.h new file mode 100644 index 000000000..9eefced22 --- /dev/null +++ b/shared/sentry/external/crashpad/third_party/mini_chromium/mini_chromium/base/numerics/checked_math.h @@ -0,0 +1,387 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MINI_CHROMIUM_BASE_NUMERICS_CHECKED_MATH_H_ +#define MINI_CHROMIUM_BASE_NUMERICS_CHECKED_MATH_H_ + +#include + +#include +#include + +#include "base/numerics/checked_math_impl.h" + +namespace base { +namespace internal { + +template +class CheckedNumeric { + static_assert(std::is_arithmetic::value, + "CheckedNumeric: T must be a numeric type."); + + public: + using type = T; + + constexpr CheckedNumeric() = default; + + // Copy constructor. + template + constexpr CheckedNumeric(const CheckedNumeric& rhs) + : state_(rhs.state_.value(), rhs.IsValid()) {} + + template + friend class CheckedNumeric; + + // This is not an explicit constructor because we implicitly upgrade regular + // numerics to CheckedNumerics to make them easier to use. + template + constexpr CheckedNumeric(Src value) // NOLINT(runtime/explicit) + : state_(value) { + static_assert(std::is_arithmetic::value, "Argument must be numeric."); + } + + // This is not an explicit constructor because we want a seamless conversion + // from StrictNumeric types. + template + constexpr CheckedNumeric( + StrictNumeric value) // NOLINT(runtime/explicit) + : state_(static_cast(value)) {} + + // IsValid() - The public API to test if a CheckedNumeric is currently valid. + // A range checked destination type can be supplied using the Dst template + // parameter. + template + constexpr bool IsValid() const { + return state_.is_valid() && + IsValueInRangeForNumericType(state_.value()); + } + + // AssignIfValid(Dst) - Assigns the underlying value if it is currently valid + // and is within the range supported by the destination type. Returns true if + // successful and false otherwise. + template + [[nodiscard]] constexpr bool AssignIfValid(Dst* result) const { + return BASE_NUMERICS_LIKELY(IsValid()) + ? ((*result = static_cast(state_.value())), true) + : false; + } + + // ValueOrDie() - The primary accessor for the underlying value. If the + // current state is not valid it will CHECK and crash. + // A range checked destination type can be supplied using the Dst template + // parameter, which will trigger a CHECK if the value is not in bounds for + // the destination. + // The CHECK behavior can be overridden by supplying a handler as a + // template parameter, for test code, etc. However, the handler cannot access + // the underlying value, and it is not available through other means. + template + constexpr StrictNumeric ValueOrDie() const { + return BASE_NUMERICS_LIKELY(IsValid()) + ? static_cast(state_.value()) + : CheckHandler::template HandleFailure(); + } + + // ValueOrDefault(T default_value) - A convenience method that returns the + // current value if the state is valid, and the supplied default_value for + // any other state. + // A range checked destination type can be supplied using the Dst template + // parameter. WARNING: This function may fail to compile or CHECK at runtime + // if the supplied default_value is not within range of the destination type. + template + constexpr StrictNumeric ValueOrDefault(const Src default_value) const { + return BASE_NUMERICS_LIKELY(IsValid()) + ? static_cast(state_.value()) + : checked_cast(default_value); + } + + // Returns a checked numeric of the specified type, cast from the current + // CheckedNumeric. If the current state is invalid or the destination cannot + // represent the result then the returned CheckedNumeric will be invalid. + template + constexpr CheckedNumeric::type> Cast() const { + return *this; + } + + // This friend method is available solely for providing more detailed logging + // in the the tests. Do not implement it in production code, because the + // underlying values may change at any time. + template + friend U GetNumericValueForTest(const CheckedNumeric& src); + + // Prototypes for the supported arithmetic operator overloads. + template + constexpr CheckedNumeric& operator+=(const Src rhs); + template + constexpr CheckedNumeric& operator-=(const Src rhs); + template + constexpr CheckedNumeric& operator*=(const Src rhs); + template + constexpr CheckedNumeric& operator/=(const Src rhs); + template + constexpr CheckedNumeric& operator%=(const Src rhs); + template + constexpr CheckedNumeric& operator<<=(const Src rhs); + template + constexpr CheckedNumeric& operator>>=(const Src rhs); + template + constexpr CheckedNumeric& operator&=(const Src rhs); + template + constexpr CheckedNumeric& operator|=(const Src rhs); + template + constexpr CheckedNumeric& operator^=(const Src rhs); + + constexpr CheckedNumeric operator-() const { + // The negation of two's complement int min is int min, so we simply + // check for that in the constexpr case. + // We use an optimized code path for a known run-time variable. + return MustTreatAsConstexpr(state_.value()) || !std::is_signed::value || + std::is_floating_point::value + ? CheckedNumeric( + NegateWrapper(state_.value()), + IsValid() && (!std::is_signed::value || + std::is_floating_point::value || + NegateWrapper(state_.value()) != + std::numeric_limits::lowest())) + : FastRuntimeNegate(); + } + + constexpr CheckedNumeric operator~() const { + return CheckedNumeric( + InvertWrapper(state_.value()), IsValid()); + } + + constexpr CheckedNumeric Abs() const { + return !IsValueNegative(state_.value()) ? *this : -*this; + } + + template + constexpr CheckedNumeric::type> Max( + const U rhs) const { + using R = typename UnderlyingType::type; + using result_type = typename MathWrapper::type; + // TODO(jschuh): This can be converted to the MathOp version and remain + // constexpr once we have C++14 support. + return CheckedNumeric( + static_cast( + IsGreater::Test(state_.value(), Wrapper::value(rhs)) + ? state_.value() + : Wrapper::value(rhs)), + state_.is_valid() && Wrapper::is_valid(rhs)); + } + + template + constexpr CheckedNumeric::type> Min( + const U rhs) const { + using R = typename UnderlyingType::type; + using result_type = typename MathWrapper::type; + // TODO(jschuh): This can be converted to the MathOp version and remain + // constexpr once we have C++14 support. + return CheckedNumeric( + static_cast( + IsLess::Test(state_.value(), Wrapper::value(rhs)) + ? state_.value() + : Wrapper::value(rhs)), + state_.is_valid() && Wrapper::is_valid(rhs)); + } + + // This function is available only for integral types. It returns an unsigned + // integer of the same width as the source type, containing the absolute value + // of the source, and properly handling signed min. + constexpr CheckedNumeric::type> + UnsignedAbs() const { + return CheckedNumeric::type>( + SafeUnsignedAbs(state_.value()), state_.is_valid()); + } + + constexpr CheckedNumeric& operator++() { + *this += 1; + return *this; + } + + constexpr CheckedNumeric operator++(int) { + CheckedNumeric value = *this; + *this += 1; + return value; + } + + constexpr CheckedNumeric& operator--() { + *this -= 1; + return *this; + } + + constexpr CheckedNumeric operator--(int) { + CheckedNumeric value = *this; + *this -= 1; + return value; + } + + // These perform the actual math operations on the CheckedNumerics. + // Binary arithmetic operations. + template